aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/.gitignore5
-rw-r--r--lib/Makefile4
-rw-r--r--lib/appmon/doc/src/appmon.xml2
-rw-r--r--lib/appmon/doc/src/notes.xml31
-rw-r--r--lib/appmon/src/appmon.erl15
-rw-r--r--lib/appmon/src/appmon_info.erl21
-rw-r--r--lib/appmon/src/appmon_place.erl14
-rw-r--r--lib/appmon/src/appmon_web.erl8
-rw-r--r--lib/appmon/vsn.mk20
-rw-r--r--lib/asn1/c_src/asn1_erl_driver.c3
-rw-r--r--lib/asn1/doc/src/asn1_ug.xml113
-rw-r--r--lib/asn1/doc/src/notes.xml126
-rw-r--r--lib/asn1/src/asn1.appup.src108
-rw-r--r--lib/asn1/src/asn1_records.hrl13
-rw-r--r--lib/asn1/src/asn1ct.erl122
-rw-r--r--lib/asn1/src/asn1ct_check.erl411
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber.erl41
-rw-r--r--lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl31
-rw-r--r--lib/asn1/src/asn1ct_constructed_per.erl373
-rw-r--r--lib/asn1/src/asn1ct_gen.erl32
-rw-r--r--lib/asn1/src/asn1ct_gen_ber.erl13
-rw-r--r--lib/asn1/src/asn1ct_gen_ber_bin_v2.erl14
-rw-r--r--lib/asn1/src/asn1ct_gen_per.erl23
-rw-r--r--lib/asn1/src/asn1ct_gen_per_rt2ct.erl23
-rw-r--r--lib/asn1/src/asn1ct_parser2.erl527
-rw-r--r--lib/asn1/src/asn1ct_tok.erl184
-rw-r--r--lib/asn1/src/asn1rt_ber_bin.erl7
-rw-r--r--lib/asn1/src/asn1rt_driver_handler.erl7
-rw-r--r--lib/asn1/src/asn1rt_uper_bin.erl29
-rw-r--r--lib/asn1/test/External.hrl2
-rw-r--r--lib/asn1/test/Makefile31
-rw-r--r--lib/asn1/test/asn1.cover2
-rw-r--r--lib/asn1/test/asn1.spec4
-rw-r--r--lib/asn1/test/asn1_SUITE.erl2489
-rw-r--r--lib/asn1/test/asn1_SUITE.erl.src26
-rw-r--r--lib/asn1/test/asn1_SUITE_data/AA2.asn1dbbin5224 -> 5910 bytes
-rw-r--r--lib/asn1/test/asn1_SUITE_data/CAP.asn141
-rw-r--r--lib/asn1/test/asn1_SUITE_data/EUTRA-RRC-Definitions.asn5795
-rw-r--r--lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn82
-rw-r--r--lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/a_SeqIn.erl2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/b_SeqIn.erl2
-rw-r--r--lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl132
-rw-r--r--lib/asn1/test/asn1_SUITE_data/test_records.erl2
-rw-r--r--lib/asn1/test/asn1_app_test.erl33
-rw-r--r--lib/asn1/test/asn1_appup_test.erl32
-rw-r--r--lib/asn1/test/asn1_bin_SUITE.erl2382
-rw-r--r--lib/asn1/test/asn1_bin_v2_SUITE.erl2474
-rw-r--r--lib/asn1/test/asn1_common_SUITE.erl.src2
-rw-r--r--lib/asn1/test/test_compile_options.erl2
-rw-r--r--lib/asn1/vsn.mk320
-rw-r--r--lib/common_test/AUTHORS1
-rw-r--r--lib/common_test/Makefile10
-rw-r--r--lib/common_test/doc/src/Makefile10
-rw-r--r--lib/common_test/doc/src/common_test_app.xml20
-rw-r--r--lib/common_test/doc/src/config_file_chapter.xml291
-rw-r--r--lib/common_test/doc/src/cover_chapter.xml6
-rw-r--r--lib/common_test/doc/src/ct_hooks.xml556
-rw-r--r--lib/common_test/doc/src/ct_hooks_chapter.xml401
-rw-r--r--lib/common_test/doc/src/ct_master_chapter.xml62
-rw-r--r--lib/common_test/doc/src/ct_run.xml179
-rw-r--r--lib/common_test/doc/src/event_handler_chapter.xml37
-rw-r--r--lib/common_test/doc/src/install_chapter.xml139
-rw-r--r--lib/common_test/doc/src/notes.xml298
-rw-r--r--lib/common_test/doc/src/part.xml3
-rw-r--r--lib/common_test/doc/src/ref_man.xml10
-rw-r--r--lib/common_test/doc/src/run_test.xml140
-rw-r--r--lib/common_test/doc/src/run_test_chapter.xml273
-rw-r--r--lib/common_test/doc/src/test_structure_chapter.xml12
-rw-r--r--lib/common_test/doc/src/write_test_chapter.xml45
-rw-r--r--lib/common_test/priv/Makefile.in22
-rw-r--r--lib/common_test/priv/tile1.jpgbin0 -> 34734 bytes
-rw-r--r--lib/common_test/src/Makefile18
-rw-r--r--lib/common_test/src/common_test.app.src17
-rw-r--r--lib/common_test/src/ct.erl159
-rw-r--r--lib/common_test/src/ct_config.erl804
-rw-r--r--lib/common_test/src/ct_config_plain.erl109
-rw-r--r--lib/common_test/src/ct_config_xml.erl118
-rw-r--r--lib/common_test/src/ct_framework.erl530
-rw-r--r--lib/common_test/src/ct_gen_conn.erl14
-rw-r--r--lib/common_test/src/ct_hooks.erl307
-rw-r--r--lib/common_test/src/ct_hooks_lock.erl132
-rw-r--r--lib/common_test/src/ct_logs.erl214
-rw-r--r--lib/common_test/src/ct_master.erl157
-rw-r--r--lib/common_test/src/ct_master_logs.erl14
-rw-r--r--lib/common_test/src/ct_repeat.erl12
-rw-r--r--lib/common_test/src/ct_run.erl1986
-rw-r--r--lib/common_test/src/ct_slave.erl439
-rw-r--r--lib/common_test/src/ct_snmp.erl22
-rw-r--r--lib/common_test/src/ct_telnet_client.erl61
-rw-r--r--lib/common_test/src/ct_testspec.erl484
-rw-r--r--lib/common_test/src/ct_util.erl718
-rw-r--r--lib/common_test/src/ct_util.hrl12
-rw-r--r--lib/common_test/src/vts.erl24
-rw-r--r--lib/common_test/test/Makefile22
-rw-r--r--lib/common_test/test/common_test.spec2
-rw-r--r--lib/common_test/test/ct_config_SUITE.erl275
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/config.txt31
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/config.xml27
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/test/config_driver.erl46
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl145
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/test/config_server.erl93
-rw-r--r--lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl123
-rw-r--r--lib/common_test/test/ct_error_SUITE.erl284
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_10_SUITE.erl123
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_11_SUITE.erl134
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl88
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl47
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl46
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl3
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl194
-rw-r--r--lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl138
-rw-r--r--lib/common_test/test/ct_event_handler_SUITE.erl28
-rw-r--r--lib/common_test/test/ct_event_handler_SUITE_data/eh_A.erl23
-rw-r--r--lib/common_test/test/ct_groups_test_1_SUITE.erl131
-rw-r--r--lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl2
-rw-r--r--lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl10
-rw-r--r--lib/common_test/test/ct_groups_test_1_SUITE_data/groups_2/test/groups_22_SUITE.erl6
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE.erl169
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE_data/cfgs/groups_2.1.cfg1
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE_data/groups_1/repeat_1_SUITE.erl105
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_21_SUITE.erl281
-rw-r--r--lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl314
-rw-r--r--lib/common_test/test/ct_hooks_SUITE.erl1021
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl34
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl34
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl47
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl64
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl47
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl50
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl56
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl56
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl47
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl47
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl110
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl50
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl50
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl56
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl278
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl72
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl72
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl32
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl33
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl38
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl74
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl75
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl72
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl73
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl83
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl71
-rw-r--r--lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl82
-rw-r--r--lib/common_test/test/ct_master_SUITE.erl223
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/config.txt2
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/config.xml4
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/include/test.hrl (renamed from lib/ssl/examples/certs/ebin/.gitignore)0
-rw-r--r--lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl58
-rw-r--r--lib/common_test/test/ct_misc_1_SUITE.erl228
-rw-r--r--lib/common_test/test/ct_misc_1_SUITE_data/beam_1_SUITE.erl134
-rw-r--r--lib/common_test/test/ct_misc_1_SUITE_data/beam_2_SUITE.erl134
-rw-r--r--lib/common_test/test/ct_repeat_1_SUITE.erl1539
-rw-r--r--lib/common_test/test/ct_repeat_1_SUITE_data/repeat_1_SUITE.erl373
-rw-r--r--lib/common_test/test/ct_sequence_1_SUITE.erl311
-rw-r--r--lib/common_test/test/ct_sequence_1_SUITE_data/subgroups_1_SUITE.erl108
-rw-r--r--lib/common_test/test/ct_skip_SUITE.erl41
-rw-r--r--lib/common_test/test/ct_smoke_test_SUITE.erl74
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE.erl40
-rw-r--r--lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl8
-rw-r--r--lib/common_test/test/ct_test_support.erl237
-rw-r--r--lib/common_test/test/ct_test_support_eh.erl27
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE.erl1380
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl281
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl344
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl281
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl314
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl146
-rw-r--r--lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl146
-rw-r--r--lib/common_test/test/ct_userconfig_callback.erl32
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/doc/src/compile.xml131
-rw-r--r--lib/compiler/doc/src/notes.xml204
-rw-r--r--lib/compiler/doc/src/part_notes_history.xml2
-rw-r--r--lib/compiler/src/Makefile6
-rw-r--r--lib/compiler/src/beam_asm.erl3
-rw-r--r--lib/compiler/src/beam_block.erl36
-rw-r--r--lib/compiler/src/beam_bool.erl21
-rw-r--r--lib/compiler/src/beam_dead.erl58
-rw-r--r--lib/compiler/src/beam_dict.erl45
-rw-r--r--lib/compiler/src/beam_disasm.erl15
-rw-r--r--lib/compiler/src/beam_peep.erl58
-rw-r--r--lib/compiler/src/beam_type.erl4
-rw-r--r--lib/compiler/src/beam_utils.erl52
-rw-r--r--lib/compiler/src/beam_validator.erl7
-rw-r--r--lib/compiler/src/cerl.erl19
-rw-r--r--lib/compiler/src/cerl_inline.erl7
-rw-r--r--lib/compiler/src/cerl_trees.erl16
-rw-r--r--lib/compiler/src/compile.erl356
-rw-r--r--lib/compiler/src/core_lint.erl25
-rw-r--r--lib/compiler/src/erl_bifs.erl12
-rw-r--r--lib/compiler/src/genop.tab3
-rw-r--r--lib/compiler/src/rec_env.erl14
-rw-r--r--lib/compiler/src/sys_core_fold.erl237
-rw-r--r--lib/compiler/src/sys_pre_expand.erl28
-rw-r--r--lib/compiler/src/v3_codegen.erl8
-rw-r--r--lib/compiler/src/v3_core.erl194
-rw-r--r--lib/compiler/src/v3_kernel.erl1
-rw-r--r--lib/compiler/src/v3_kernel_pp.erl55
-rw-r--r--lib/compiler/src/v3_life.erl2
-rw-r--r--lib/compiler/test/Makefile4
-rw-r--r--lib/compiler/test/andor_SUITE.erl55
-rw-r--r--lib/compiler/test/apply_SUITE.erl31
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl55
-rw-r--r--lib/compiler/test/bs_bincomp_SUITE.erl67
-rw-r--r--lib/compiler/test/bs_bit_binaries_SUITE.erl36
-rw-r--r--lib/compiler/test/bs_construct_SUITE.erl41
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl80
-rw-r--r--lib/compiler/test/bs_utf_SUITE.erl35
-rw-r--r--lib/compiler/test/compilation_SUITE.erl124
-rw-r--r--lib/compiler/test/compilation_SUITE_data/string_table.erl8
-rw-r--r--lib/compiler/test/compile_SUITE.erl118
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-basic1.mk1
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-basic2.mk1
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-missing.mk1
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-target1.mk1
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple-target2.mk1
-rw-r--r--lib/compiler/test/compile_SUITE_data/simple.erl6
-rw-r--r--lib/compiler/test/compiler.cover4
-rw-r--r--lib/compiler/test/compiler.dynspec10
-rw-r--r--lib/compiler/test/compiler.spec2
-rw-r--r--lib/compiler/test/core_SUITE.erl34
-rw-r--r--lib/compiler/test/core_SUITE_data/.gitignore1
-rw-r--r--lib/compiler/test/core_fold_SUITE.erl33
-rw-r--r--lib/compiler/test/error_SUITE.erl194
-rw-r--r--lib/compiler/test/float_SUITE.erl57
-rw-r--r--lib/compiler/test/fun_SUITE.erl31
-rw-r--r--lib/compiler/test/guard_SUITE.erl199
-rw-r--r--lib/compiler/test/inline_SUITE.erl33
-rw-r--r--lib/compiler/test/lc_SUITE.erl36
-rw-r--r--lib/compiler/test/match_SUITE.erl48
-rw-r--r--lib/compiler/test/misc_SUITE.erl96
-rw-r--r--lib/compiler/test/num_bif_SUITE.erl37
-rw-r--r--lib/compiler/test/parteval_SUITE.erl28
-rw-r--r--lib/compiler/test/pmod_SUITE.erl32
-rw-r--r--lib/compiler/test/receive_SUITE.erl56
-rw-r--r--lib/compiler/test/record_SUITE.erl37
-rw-r--r--lib/compiler/test/test_lib.erl26
-rw-r--r--lib/compiler/test/trycatch_SUITE.erl39
-rw-r--r--lib/compiler/test/warnings_SUITE.erl48
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml2
-rw-r--r--lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml2
-rw-r--r--lib/cosEvent/doc/src/ch_contents.xml2
-rw-r--r--lib/cosEvent/doc/src/ch_introduction.xml2
-rw-r--r--lib/cosEvent/doc/src/cosEventApp.xml2
-rw-r--r--lib/cosEvent/doc/src/notes.xml26
-rw-r--r--lib/cosEvent/src/cosEventApp.erl44
-rw-r--r--lib/cosEvent/test/Makefile5
-rw-r--r--lib/cosEvent/test/cosEvent.cover2
-rw-r--r--lib/cosEvent/test/cosEvent.spec20
-rw-r--r--lib/cosEvent/test/event_channel_SUITE.erl42
-rw-r--r--lib/cosEvent/test/event_test_PullC_impl.erl2
-rw-r--r--lib/cosEvent/test/event_test_PullS_impl.erl2
-rw-r--r--lib/cosEvent/test/event_test_PushC_impl.erl2
-rw-r--r--lib/cosEvent/test/event_test_PushS_impl.erl2
-rw-r--r--lib/cosEvent/test/generated_SUITE.erl53
-rw-r--r--lib/cosEvent/vsn.mk14
-rw-r--r--lib/cosEventDomain/doc/src/CosEventDomainAdmin.xml2
-rw-r--r--lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml2
-rw-r--r--lib/cosEventDomain/doc/src/cosEventDomainApp.xml2
-rw-r--r--lib/cosEventDomain/doc/src/notes.xml20
-rw-r--r--lib/cosEventDomain/src/CosEventDomainAdmin_EventDomain_impl.erl24
-rw-r--r--lib/cosEventDomain/src/cosEventDomainApp.erl13
-rw-r--r--lib/cosEventDomain/test/Makefile5
-rw-r--r--lib/cosEventDomain/test/cosEventDomain.cover2
-rw-r--r--lib/cosEventDomain/test/cosEventDomain.spec20
-rw-r--r--lib/cosEventDomain/test/event_domain_SUITE.erl39
-rw-r--r--lib/cosEventDomain/test/generated_SUITE.erl51
-rw-r--r--lib/cosEventDomain/vsn.mk14
-rw-r--r--lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml2
-rw-r--r--lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml2
-rw-r--r--lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml2
-rw-r--r--lib/cosFileTransfer/test/Makefile133
-rw-r--r--lib/cosFileTransfer/test/cosFileTransfer.cover2
-rw-r--r--lib/cosFileTransfer/test/cosFileTransfer.spec1
-rw-r--r--lib/cosFileTransfer/test/fileTransfer_SUITE.erl972
-rw-r--r--lib/cosFileTransfer/vsn.mk15
-rw-r--r--lib/cosNotification/doc/src/CosNotification.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyComm_NotifyPublish.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyFilter_FilterAdmin.xml2
-rw-r--r--lib/cosNotification/doc/src/CosNotifyFilter_FilterFactory.xml2
-rw-r--r--lib/cosNotification/doc/src/notes.xml86
-rw-r--r--lib/cosNotification/src/CosNotification_Common.erl46
-rw-r--r--lib/cosNotification/src/cosNotification_Filter.erl32
-rw-r--r--lib/cosNotification/test/Makefile5
-rw-r--r--lib/cosNotification/test/cosNotification.cover2
-rw-r--r--lib/cosNotification/test/cosNotification.spec20
-rw-r--r--lib/cosNotification/test/eventDB_SUITE.erl44
-rw-r--r--lib/cosNotification/test/generated_SUITE.erl156
-rw-r--r--lib/cosNotification/test/grammar_SUITE.erl43
-rw-r--r--lib/cosNotification/test/notification_SUITE.erl46
-rw-r--r--lib/cosNotification/test/notify_test_impl.erl2
-rw-r--r--lib/cosNotification/vsn.mk18
-rw-r--r--lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml2
-rw-r--r--lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml2
-rw-r--r--lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml2
-rw-r--r--lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml2
-rw-r--r--lib/cosProperty/doc/src/notes.xml24
-rw-r--r--lib/cosProperty/src/CosPropertyService_PropertySetDefFactory_impl.erl14
-rw-r--r--lib/cosProperty/src/CosPropertyService_PropertySetFactory_impl.erl14
-rw-r--r--lib/cosProperty/test/Makefile5
-rw-r--r--lib/cosProperty/test/cosProperty.cover2
-rw-r--r--lib/cosProperty/test/cosProperty.spec21
-rw-r--r--lib/cosProperty/test/generated_SUITE.erl70
-rw-r--r--lib/cosProperty/test/property_SUITE.erl43
-rw-r--r--lib/cosProperty/vsn.mk13
-rw-r--r--lib/cosTime/doc/src/CosTime_TIO.xml2
-rw-r--r--lib/cosTime/doc/src/CosTime_TimeService.xml2
-rw-r--r--lib/cosTime/doc/src/CosTime_UTO.xml2
-rw-r--r--lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml2
-rw-r--r--lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml2
-rw-r--r--lib/cosTime/doc/src/cosTime.xml2
-rw-r--r--lib/cosTime/doc/src/notes.xml18
-rw-r--r--lib/cosTime/src/cosTime.erl60
-rw-r--r--lib/cosTime/test/Makefile5
-rw-r--r--lib/cosTime/test/cosTime.cover2
-rw-r--r--lib/cosTime/test/cosTime.spec20
-rw-r--r--lib/cosTime/test/generated_SUITE.erl38
-rw-r--r--lib/cosTime/test/time_SUITE.erl39
-rw-r--r--lib/cosTime/vsn.mk13
-rw-r--r--lib/cosTransactions/doc/src/CosTransactions_Control.xml2
-rw-r--r--lib/cosTransactions/doc/src/CosTransactions_Synchronization.xml2
-rw-r--r--lib/cosTransactions/doc/src/CosTransactions_Terminator.xml2
-rw-r--r--lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml2
-rw-r--r--lib/cosTransactions/doc/src/cosTransactions.xml2
-rw-r--r--lib/cosTransactions/test/Makefile5
-rw-r--r--lib/cosTransactions/test/cosTransactions.cover2
-rw-r--r--lib/cosTransactions/test/cosTransactions.spec20
-rw-r--r--lib/cosTransactions/test/etrap_test_lib.erl2
-rw-r--r--lib/cosTransactions/test/etrap_test_lib.hrl2
-rw-r--r--lib/cosTransactions/test/generated_SUITE.erl64
-rw-r--r--lib/cosTransactions/test/transactions_SUITE.erl37
-rw-r--r--lib/cosTransactions/vsn.mk13
-rw-r--r--lib/crypto/c_src/Makefile.in24
-rw-r--r--lib/crypto/c_src/crypto.c146
-rw-r--r--lib/crypto/doc/src/crypto.xml55
-rw-r--r--lib/crypto/doc/src/crypto_app.xml2
-rw-r--r--lib/crypto/doc/src/notes.xml117
-rw-r--r--lib/crypto/doc/src/release_notes.xml2
-rw-r--r--lib/crypto/src/crypto.app.src12
-rw-r--r--lib/crypto/src/crypto.erl157
-rw-r--r--lib/crypto/test/Makefile4
-rw-r--r--lib/crypto/test/blowfish_SUITE.erl35
-rw-r--r--lib/crypto/test/crypto.cover2
-rw-r--r--lib/crypto/test/crypto.spec3
-rw-r--r--lib/crypto/test/crypto_SUITE.erl193
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/doc/src/notes.xml69
-rw-r--r--lib/debugger/src/dbg_debugged.erl4
-rw-r--r--lib/debugger/src/dbg_icmd.erl31
-rw-r--r--lib/debugger/src/dbg_ieval.erl55
-rw-r--r--lib/debugger/src/dbg_iload.erl59
-rw-r--r--lib/debugger/src/dbg_iserver.erl52
-rw-r--r--lib/debugger/src/dbg_ui_break_win.erl9
-rw-r--r--lib/debugger/src/dbg_ui_filedialog_win.erl15
-rw-r--r--lib/debugger/src/dbg_ui_mon.erl21
-rw-r--r--lib/debugger/src/dbg_ui_mon_win.erl30
-rw-r--r--lib/debugger/src/dbg_ui_trace_win.erl237
-rw-r--r--lib/debugger/src/dbg_ui_view.erl19
-rw-r--r--lib/debugger/src/dbg_ui_win.erl23
-rw-r--r--lib/debugger/src/dbg_ui_winman.erl14
-rw-r--r--lib/debugger/src/dbg_wx_break_win.erl8
-rw-r--r--lib/debugger/src/dbg_wx_interpret.erl12
-rw-r--r--lib/debugger/src/dbg_wx_mon.erl19
-rw-r--r--lib/debugger/src/dbg_wx_mon_win.erl11
-rw-r--r--lib/debugger/src/dbg_wx_trace.erl52
-rwxr-xr-xlib/debugger/src/dbg_wx_trace_win.erl34
-rw-r--r--lib/debugger/src/dbg_wx_view.erl50
-rwxr-xr-xlib/debugger/src/dbg_wx_winman.erl14
-rw-r--r--lib/debugger/src/i.erl10
-rw-r--r--lib/debugger/src/int.erl12
-rw-r--r--lib/debugger/test/Makefile106
-rw-r--r--lib/debugger/test/andor_SUITE.erl320
-rw-r--r--lib/debugger/test/bs_bincomp_SUITE.erl126
-rw-r--r--lib/debugger/test/bs_construct_SUITE.erl446
-rw-r--r--lib/debugger/test/bs_match_bin_SUITE.erl128
-rw-r--r--lib/debugger/test/bs_match_int_SUITE.erl244
-rw-r--r--lib/debugger/test/bs_match_misc_SUITE.erl166
-rw-r--r--lib/debugger/test/bs_match_tail_SUITE.erl120
-rw-r--r--lib/debugger/test/bs_utf_SUITE.erl305
-rw-r--r--lib/debugger/test/bug_SUITE.erl95
-rw-r--r--lib/debugger/test/bug_SUITE_data/Makefile.src25
-rw-r--r--lib/debugger/test/bug_SUITE_data/otp2163.erl60
-rw-r--r--lib/debugger/test/bug_SUITE_data/otp4845.erl52
-rw-r--r--lib/debugger/test/cleanup.erl56
-rw-r--r--lib/debugger/test/dbg_ui_SUITE.erl269
-rw-r--r--lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/lists1.erl469
-rw-r--r--lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/ordsets1.erl188
-rw-r--r--lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/test.erl157
-rw-r--r--lib/debugger/test/debugger.cover2
-rw-r--r--lib/debugger/test/debugger.spec1
-rw-r--r--lib/debugger/test/debugger_SUITE.erl144
-rw-r--r--lib/debugger/test/debugger_test.erl154
-rw-r--r--lib/debugger/test/debugger_testdoc.fm5bin0 -> 28672 bytes
-rw-r--r--lib/debugger/test/erl_eval_SUITE.erl1406
-rw-r--r--lib/debugger/test/exception_SUITE.erl270
-rw-r--r--lib/debugger/test/fun_SUITE.erl246
-rw-r--r--lib/debugger/test/guard_SUITE.erl1492
-rw-r--r--lib/debugger/test/int_SUITE.erl291
-rw-r--r--lib/debugger/test/int_SUITE_data/Makefile.src39
-rw-r--r--lib/debugger/test/int_SUITE_data/guards.erl108
-rw-r--r--lib/debugger/test/int_SUITE_data/lists1.erl469
-rw-r--r--lib/debugger/test/int_SUITE_data/my_lists.erl5681
-rw-r--r--lib/debugger/test/int_SUITE_data/ordsets1.erl191
-rw-r--r--lib/debugger/test/int_SUITE_data/test.erl152
-rw-r--r--lib/debugger/test/int_SUITE_data/test1.erl34
-rw-r--r--lib/debugger/test/int_break_SUITE.erl112
-rw-r--r--lib/debugger/test/int_break_SUITE_data/Makefile.src25
-rw-r--r--lib/debugger/test/int_break_SUITE_data/ordsets1.erl188
-rw-r--r--lib/debugger/test/int_eval_SUITE.erl291
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/Makefile.src26
-rw-r--r--lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl245
-rw-r--r--lib/debugger/test/lc_SUITE.erl88
-rw-r--r--lib/debugger/test/record_SUITE.erl266
-rw-r--r--lib/debugger/test/test_lib.erl29
-rw-r--r--lib/debugger/test/trycatch_SUITE.erl810
-rw-r--r--lib/debugger/vsn.mk2
-rw-r--r--lib/dialyzer/RELEASE_NOTES48
-rw-r--r--lib/dialyzer/doc/manual.txt85
-rw-r--r--lib/dialyzer/doc/src/dialyzer.xml219
-rwxr-xr-xlib/dialyzer/doc/src/notes.xml170
-rw-r--r--lib/dialyzer/src/dialyzer.erl99
-rw-r--r--lib/dialyzer/src/dialyzer.hrl21
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl123
-rw-r--r--lib/dialyzer/src/dialyzer_behaviours.erl76
-rw-r--r--lib/dialyzer/src/dialyzer_callgraph.erl2
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl187
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl153
-rw-r--r--lib/dialyzer/src/dialyzer_codeserver.erl78
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl129
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl713
-rw-r--r--lib/dialyzer/src/dialyzer_gui.erl16
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl47
-rw-r--r--lib/dialyzer/src/dialyzer_plt.erl210
-rw-r--r--lib/dialyzer/src/dialyzer_races.erl102
-rw-r--r--lib/dialyzer/src/dialyzer_succ_typings.erl30
-rw-r--r--lib/dialyzer/src/dialyzer_typesig.erl420
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl101
-rw-r--r--lib/dialyzer/test/Makefile75
-rw-r--r--lib/dialyzer/test/README44
-rw-r--r--lib/dialyzer/test/callgraph_tests_SUITE.erl52
-rw-r--r--lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions3
-rw-r--r--lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl16
-rw-r--r--lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl16
-rw-r--r--lib/dialyzer/test/dialyzer.spec5
-rw-r--r--lib/dialyzer/test/dialyzer_common.erl377
-rw-r--r--lib/dialyzer/test/dialyzer_test_constants.hrl1
-rw-r--r--lib/dialyzer/test/file_utils.erl155
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE.erl184
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/array3
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/crash7
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/dict15
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/ets3
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/gb_sets (renamed from lib/xmerl/src/xmerl_dtd.erl)0
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop15
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/int3
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque2
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph0
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue7
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque2
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/queue11
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/rec6
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/timer4
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/union5
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/results/wings11
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl15
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl55
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl83
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl17
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl23
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl172
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl33
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl11
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl26
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl25
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl31
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl51
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl23
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl35
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl9
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl17
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl13
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl19
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl21
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl66
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl22
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl30
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl20
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl19
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl16
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl205
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl375
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl243
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl91
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl127
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl299
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl15
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl37
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl68
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl69
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl39
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl250
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl14
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl14
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl14
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl14
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl14
-rw-r--r--lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl14
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE.erl54
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries3
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository1
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root1
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl43
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl42
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/results/compiler35
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl358
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl601
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl617
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl232
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl196
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl964
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl137
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl477
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl117
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl240
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl12
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl551
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl1022
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl4169
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl409
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl2762
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl801
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl1109
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl509
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl515
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl4911
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl111
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl430
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl495
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl486
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl611
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl425
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl212
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl1026
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl1755
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl1320
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl1568
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl77
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl444
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl448
-rw-r--r--lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl25
-rw-r--r--lib/dialyzer/test/options2_tests_SUITE.erl52
-rw-r--r--lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/options2_tests_SUITE_data/results/kernel0
-rw-r--r--lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl1999
-rw-r--r--lib/dialyzer/test/plt_tests_SUITE.erl21
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE.erl64
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/results/asn1106
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/results/inets59
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/results/mnesia34
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Makefile151
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Restrictions.txt55
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.app.src20
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.appup.src166
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_db.erl162
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_records.hrl96
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct.erl1904
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_check.erl5567
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber.erl1468
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl1357
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_per.erl1235
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen.erl1664
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber.erl1525
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl1568
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per.erl1190
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl1811
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_name.erl225
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser.yrl1175
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser2.erl2764
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_pretty_format.erl199
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_tok.erl351
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_value.erl330
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt.erl69
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin.erl2310
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl1869
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_check.erl333
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_driver_handler.erl108
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per.erl1609
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin.erl2182
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl2102
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_v1.erl1843
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_history.sgml100
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_latest.sgml100
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/Makefile178
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl1582
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.erl260
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.hrl127
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http_lib.erl745
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_handler.erl724
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_manager.erl542
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.erl596
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.hrl77
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor.erl176
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor_sup.erl118
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_conf.erl688
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_example.erl134
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_manager.erl1030
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_misc_sup.erl116
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_parse.erl348
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_request_handler.erl995
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_response.erl437
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_socket.erl381
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_sup.erl203
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_util.erl777
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.erl94
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.hrl65
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.app.src56
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.appup.src135
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.config2
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets_sup.erl158
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/jnets_httpd.hrl138
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_actions.erl92
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_alias.erl175
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.erl750
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.hrl27
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_dets.erl222
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_mnesia.erl276
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_plain.erl344
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_server.erl424
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_browser.erl214
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_cgi.erl694
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_dir.erl266
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_disk_log.erl405
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_esi.erl490
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_get.erl179
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_head.erl89
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_htaccess.erl1150
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_include.erl726
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_log.erl250
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_range.erl397
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_responsecontrol.erl337
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security.erl307
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security_server.erl728
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_trace.erl69
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/uri.erl349
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/Makefile137
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.app.src52
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.appup.src6
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.erl2191
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.hrl118
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_backup.erl195
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl1169
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint.erl1284
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint_sup.erl39
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_controller.erl2012
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_dumper.erl1092
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_event.erl263
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag.erl1201
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_hash.erl118
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_old_hash.erl127
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_index.erl380
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_kernel_sup.erl62
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_late_loader.erl95
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_lib.erl1278
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_loader.erl805
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_locker.erl1022
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_log.erl1019
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_monitor.erl776
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl1175
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_registry.erl277
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_schema.erl2899
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_hook.erl271
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_sup.erl39
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sp.erl39
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_subscr.erl492
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sup.erl137
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_text.erl191
-rw-r--r--lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_tm.erl2173
-rw-r--r--lib/dialyzer/test/race_tests_SUITE.erl799
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args30
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args72
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args82
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow33
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow43
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow55
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double14
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double24
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_new0
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_param5
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/extract_translations5
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_one_write_two2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_two_write_one2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_one2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_two2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow43
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_no_race0
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_race2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions13
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_nested2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_pathsens2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_twice3
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_nested2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_pathsens2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_twice3
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_nested2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_pathsens2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_pathsens2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_rec2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_pathsens2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_rec2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_nested2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_twice3
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_no_race0
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_race2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module72
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module82
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param_inter_module2
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function72
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function82
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_try_catch3
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars10
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars102
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars110
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars122
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars132
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars142
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars152
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars162
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars172
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars180
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars190
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars200
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars210
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars222
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars42
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars52
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars62
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars72
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars82
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars92
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args1.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args2.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args3.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args4.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args5.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args6.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args7.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args8.erl16
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow1.erl20
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow2.erl26
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow3.erl31
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow4.erl31
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow5.erl34
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race1.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race2.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race3.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race4.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race5.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race6.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double1.erl28
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double2.erl28
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs1.erl18
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs2.erl18
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_new.erl15
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_param.erl26
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/extract_translations.erl294
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race1.erl33
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race2.erl37
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_one_write_two.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_two_write_one.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double1.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double2.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double3.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double4.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_one.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_two.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow1.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow2.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow3.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow4.erl29
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow5.erl12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow6.erl12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_no_race.erl24
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_race.erl35
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_nested.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_pathsens.erl32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_twice.erl30
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_nested.erl20
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_pathsens.erl29
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_twice.erl27
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_nested.erl21
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_pathsens.erl29
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions4.erl32
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions5.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions6.erl29
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules1.erl16
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules2.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules1_pathsens.erl26
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules2_pathsens.erl12
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules1_rec.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules2_rec.erl8
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules3.erl8
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules4.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules3_pathsens.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules4_pathsens.erl13
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules3_rec.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules4_rec.erl8
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules5.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules6.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules1_nested.erl14
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules2_nested.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules3_nested.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules1_twice.erl21
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules2_twice.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_no_race.erl13
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_race.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module1.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module2.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module3.erl16
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module4.erl14
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module5.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module6.erl14
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module7.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module8.erl13
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module10.erl16
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module9.erl11
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module11.erl27
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module12.erl14
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module13.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module14.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module15.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module16.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param.erl16
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module1.erl9
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module2.erl13
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function1.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function2.erl24
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function3.erl27
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function4.erl27
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function5.erl21
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function6.erl24
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function7.erl19
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function8.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_try_catch.erl25
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars1.erl17
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars10.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars11.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars12.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars13.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars14.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars15.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars16.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars17.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars18.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars19.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars2.erl18
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars20.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars21.erl23
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars22.erl27
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars3.erl18
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars4.erl18
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars5.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars6.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars7.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars8.erl22
-rw-r--r--lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars9.erl22
-rwxr-xr-xlib/dialyzer/test/remake9
-rw-r--r--lib/dialyzer/test/small_tests_SUITE.erl483
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/dialyzer_options1
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/andalso_test0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/app_call3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/appmon_place0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/areq2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/atom_call3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/atom_widen3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/bs_fail_constr9
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/bs_utf80
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/cerl_hipeify4
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/comm_layer2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/compare14
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/confusing_warning2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/contract13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/contract22
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/contract33
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/contract52
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/eqeq2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/ets_select0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/exhaust_case3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/failing_guard14
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/flatten2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/fun_app7
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/fun_ref_match2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/gencall4
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/gs_make0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/inf_loop24
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/letrec10
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/list_match2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/lzip0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/make_tuple3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/minus_minus0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/mod_info0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/my_filter0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/my_sofs3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/no_match4
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun20
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/non_existing2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/not_guard_crash0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/or_bug0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug20
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/overloaded13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/port_info_test6
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/process_info_test0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/pubsub0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/receive12
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/record_construct7
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/record_pat2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/record_send_test2
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/record_test3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types10
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types20
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types30
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types40
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types50
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types60
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types70
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/toth0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/trec7
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/try10
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/tuple15
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/unsafe_beamcode_bug0
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/unused_cases4
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/unused_clauses3
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/results/zero_tuple5
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/app_call.erl17
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/appmon_place.erl71
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/areq.erl12
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/atom_call.erl14
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/atom_guard.erl9
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/atom_widen.erl24
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/bs_fail_constr.erl16
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/bs_utf8.erl27
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/cerl_hipeify.erl684
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_acceptor.erl120
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_connection.erl206
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.erl83
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.hrl30
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_logger.erl143
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port.erl240
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port_sup.erl90
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/compare1.erl21
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/confusing_warning.erl22
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/contract2.erl18
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/contract3.erl34
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/contract5.erl15
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/disj_norm_form.erl23
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/eqeq.erl16
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/ets_select.erl12
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/exhaust_case.erl24
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/failing_guard1.erl16
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/flatten.erl18
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/fun_app.erl42
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_match.erl21
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_record.erl17
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/gencall.erl12
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/gs_make.erl261
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/inf_loop2.erl23
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/letrec1.erl13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/list_match.erl20
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/lzip.erl8
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/make_tuple.erl5
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/minus_minus.erl8
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/mod_info.erl5
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/my_filter.erl17
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/my_sofs.erl83
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/no_match.erl9
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun.erl20
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun2.erl20
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/non_existing.erl13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/not_guard_crash.erl49
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/or_bug.erl24
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug.erl17
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug2.erl23
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/overloaded1.erl31
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/port_info_test.erl34
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/process_info_test.erl21
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_api.erl99
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_publish.erl50
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/receive1.erl17
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/record_construct.erl22
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/record_pat.erl19
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/record_send_test.erl33
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/record_test.erl24
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types1.erl10
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types2.erl12
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types3.erl15
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types4.erl13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types5.erl13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types6.erl17
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types7.erl13
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/refine_bug1.erl11
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/toth.erl99
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/trec.erl37
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/try1.erl27
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/tuple1.erl29
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/unsafe_beamcode_bug.erl15
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/unused_cases.erl41
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/unused_clauses.erl18
-rw-r--r--lib/dialyzer/test/small_tests_SUITE_data/src/zero_tuple.erl13
-rw-r--r--lib/dialyzer/test/user_tests_SUITE.erl78
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/dialyzer_options2
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/results/broken_dialyzer0
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/results/gcpFlowControl2
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/results/qlc_error0
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/results/spvcOrig193
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/results/wsp_pdu25
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/broken_dialyzer.erl130
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/gcp.hrl166
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/gcpFlowControl.erl397
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/qlc_error.erl15
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/spvcOrig.erl3523
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/wdp.hrl97
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/wsp.hrl242
-rw-r--r--lib/dialyzer/test/user_tests_SUITE_data/src/wsp_pdu.erl5423
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/docbuilder/doc/src/docb_gen.xml2
-rw-r--r--lib/docbuilder/doc/src/docb_transform.xml2
-rw-r--r--lib/docbuilder/doc/src/docb_xml_check.xml2
-rw-r--r--lib/docbuilder/doc/src/docbuilder_app.xml2
-rw-r--r--lib/docbuilder/doc/src/notes.xml31
-rw-r--r--lib/docbuilder/src/docb_edoc_xml_cb.erl91
-rw-r--r--lib/docbuilder/src/docb_html.erl27
-rw-r--r--lib/docbuilder/src/docb_html_util.erl17
-rw-r--r--lib/docbuilder/src/docb_main.erl56
-rw-r--r--lib/docbuilder/src/docb_pretty_format.erl4
-rw-r--r--lib/docbuilder/src/docb_tr_application2html.erl12
-rw-r--r--lib/docbuilder/src/docb_tr_cite2html.erl22
-rw-r--r--lib/docbuilder/src/docb_tr_index2html.erl4
-rw-r--r--lib/docbuilder/src/docb_tr_part2html.erl15
-rw-r--r--lib/docbuilder/src/docb_tr_term2html.erl22
-rw-r--r--lib/docbuilder/src/docb_transform.erl4
-rw-r--r--lib/docbuilder/src/docb_util.erl8
-rw-r--r--lib/docbuilder/src/docb_xmerl_tree_cb.erl4
-rw-r--r--lib/docbuilder/test/Makefile4
-rw-r--r--lib/docbuilder/test/docb.cover2
-rw-r--r--lib/docbuilder/test/docb_SUITE.erl25
-rw-r--r--lib/docbuilder/vsn.mk15
-rw-r--r--lib/edoc/doc/overview.edoc103
-rw-r--r--lib/edoc/doc/src/Makefile2
-rw-r--r--lib/edoc/doc/src/notes.xml85
-rw-r--r--lib/edoc/doc/src/ref_man.xml2
-rw-r--r--lib/edoc/src/Makefile3
-rw-r--r--lib/edoc/src/edoc.app.src1
-rw-r--r--lib/edoc/src/edoc.erl36
-rw-r--r--lib/edoc/src/edoc.hrl11
-rw-r--r--lib/edoc/src/edoc_data.erl16
-rw-r--r--lib/edoc/src/edoc_doclet.erl4
-rw-r--r--lib/edoc/src/edoc_extract.erl154
-rw-r--r--lib/edoc/src/edoc_layout.erl395
-rw-r--r--lib/edoc/src/edoc_lib.erl60
-rw-r--r--lib/edoc/src/edoc_macros.erl10
-rw-r--r--lib/edoc/src/edoc_parser.yrl108
-rw-r--r--lib/edoc/src/edoc_refs.erl4
-rw-r--r--lib/edoc/src/edoc_report.erl2
-rw-r--r--lib/edoc/src/edoc_run.erl2
-rw-r--r--lib/edoc/src/edoc_scanner.erl22
-rw-r--r--lib/edoc/src/edoc_specs.erl603
-rw-r--r--lib/edoc/src/edoc_tags.erl146
-rw-r--r--lib/edoc/src/edoc_types.erl105
-rw-r--r--lib/edoc/src/edoc_types.hrl45
-rw-r--r--lib/edoc/src/edoc_wiki.erl5
-rw-r--r--lib/edoc/test/Makefile4
-rw-r--r--lib/edoc/test/edoc.cover2
-rw-r--r--lib/edoc/test/edoc.spec2
-rw-r--r--lib/edoc/test/edoc_SUITE.erl25
-rw-r--r--lib/edoc/vsn.mk2
-rw-r--r--lib/erl_docgen/Makefile19
-rw-r--r--lib/erl_docgen/doc/src/notes.xml81
-rw-r--r--lib/erl_docgen/ebin/.gitignore0
-rw-r--r--lib/erl_docgen/priv/bin/specs_gen.escript129
-rwxr-xr-xlib/erl_docgen/priv/bin/xref_mod_app.escript105
-rw-r--r--lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd7
-rw-r--r--lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd2
-rw-r--r--lib/erl_docgen/priv/dtd_man_entities/xhtml-lat1.ent128
-rw-r--r--lib/erl_docgen/priv/xsl/db_eix.xsl2
-rw-r--r--lib/erl_docgen/priv/xsl/db_html.xsl638
-rw-r--r--lib/erl_docgen/priv/xsl/db_man.xsl349
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl506
-rw-r--r--lib/erl_docgen/src/Makefile96
-rw-r--r--lib/erl_docgen/src/erl_docgen.app.src12
-rw-r--r--lib/erl_docgen/src/erl_docgen.appup.src1
-rw-r--r--lib/erl_docgen/src/otp_specs.erl701
-rw-r--r--lib/erl_docgen/vsn.mk22
-rw-r--r--lib/erl_interface/configure.in8
-rw-r--r--lib/erl_interface/doc/src/ei.xml4
-rw-r--r--lib/erl_interface/doc/src/ei_connect.xml35
-rw-r--r--lib/erl_interface/doc/src/erl_connect.xml29
-rw-r--r--lib/erl_interface/doc/src/notes.xml200
-rw-r--r--lib/erl_interface/include/ei.h42
-rw-r--r--lib/erl_interface/src/connect/ei_connect.c46
-rw-r--r--lib/erl_interface/src/connect/ei_connect_int.h11
-rw-r--r--lib/erl_interface/src/connect/ei_resolve.c6
-rw-r--r--lib/erl_interface/src/connect/eirecv.c20
-rw-r--r--lib/erl_interface/src/connect/send.c5
-rw-r--r--lib/erl_interface/src/connect/send_exit.c5
-rw-r--r--lib/erl_interface/src/connect/send_reg.c5
-rw-r--r--lib/erl_interface/src/decode/decode_atom.c4
-rw-r--r--lib/erl_interface/src/decode/decode_big.c4
-rw-r--r--lib/erl_interface/src/decode/decode_double.c30
-rw-r--r--lib/erl_interface/src/decode/decode_pid.c4
-rw-r--r--lib/erl_interface/src/decode/decode_port.c4
-rw-r--r--lib/erl_interface/src/decode/decode_ref.c5
-rw-r--r--lib/erl_interface/src/decode/decode_skip.c11
-rw-r--r--lib/erl_interface/src/encode/encode_double.c20
-rw-r--r--lib/erl_interface/src/epmd/ei_epmd.h11
-rw-r--r--lib/erl_interface/src/epmd/epmd_port.c77
-rw-r--r--lib/erl_interface/src/epmd/epmd_publish.c74
-rw-r--r--lib/erl_interface/src/epmd/epmd_unpublish.c7
-rw-r--r--lib/erl_interface/src/legacy/decode_term.c11
-rw-r--r--lib/erl_interface/src/legacy/erl_connect.c9
-rw-r--r--lib/erl_interface/src/legacy/erl_format.c4
-rw-r--r--lib/erl_interface/src/legacy/erl_marshal.c174
-rw-r--r--lib/erl_interface/src/legacy/erl_timeout.c4
-rw-r--r--lib/erl_interface/src/legacy/global_register.c14
-rw-r--r--lib/erl_interface/src/legacy/global_unregister.c14
-rw-r--r--lib/erl_interface/src/misc/ei_decode_term.c29
-rw-r--r--lib/erl_interface/src/misc/ei_format.c24
-rw-r--r--lib/erl_interface/src/misc/ei_portio.c7
-rw-r--r--lib/erl_interface/src/misc/ei_printterm.c14
-rw-r--r--lib/erl_interface/src/misc/get_type.c17
-rw-r--r--lib/erl_interface/src/misc/putget.h38
-rw-r--r--lib/erl_interface/src/misc/show_msg.c20
-rw-r--r--lib/erl_interface/src/prog/erl_call.c170
-rw-r--r--lib/erl_interface/src/registry/reg_dump.c6
-rw-r--r--lib/erl_interface/src/registry/reg_restore.c4
-rw-r--r--lib/erl_interface/test/Makefile13
-rw-r--r--lib/erl_interface/test/ei_accept_SUITE.erl75
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE.erl78
-rw-r--r--lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c25
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE.erl101
-rw-r--r--lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c69
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE.erl31
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE.erl68
-rw-r--r--lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c14
-rw-r--r--lib/erl_interface/test/ei_format_SUITE.erl44
-rw-r--r--lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c4
-rw-r--r--lib/erl_interface/test/ei_print_SUITE.erl29
-rw-r--r--lib/erl_interface/test/ei_tmo_SUITE.erl45
-rw-r--r--lib/erl_interface/test/erl_connect_SUITE.erl32
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE.erl72
-rw-r--r--lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c120
-rw-r--r--lib/erl_interface/test/erl_ext_SUITE.erl45
-rw-r--r--lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c14
-rw-r--r--lib/erl_interface/test/erl_format_SUITE.erl28
-rw-r--r--lib/erl_interface/test/erl_global_SUITE.erl142
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/Makefile.first21
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/Makefile.src41
-rw-r--r--lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c263
-rw-r--r--lib/erl_interface/test/erl_interface.cover2
-rw-r--r--lib/erl_interface/test/erl_interface.spec3
-rw-r--r--lib/erl_interface/test/erl_match_SUITE.erl31
-rw-r--r--lib/erl_interface/test/port_call_SUITE.erl30
-rw-r--r--lib/erl_interface/test/runner.erl2
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/et/doc/src/et.xml1
-rw-r--r--lib/et/doc/src/et_collector.xml1
-rw-r--r--lib/et/doc/src/et_selector.xml1
-rw-r--r--lib/et/doc/src/et_tutorial.xmlsrc3
-rw-r--r--lib/et/doc/src/et_viewer.xml1
-rw-r--r--lib/et/doc/src/notes.xml41
-rw-r--r--lib/et/src/et_selector.erl4
-rw-r--r--lib/et/src/et_wx_contents_viewer.erl2
-rw-r--r--lib/et/src/et_wx_viewer.erl13
-rw-r--r--lib/et/test/Makefile9
-rw-r--r--lib/et/test/et.cover2
-rw-r--r--lib/et/test/et.spec3
-rw-r--r--lib/et/test/et_test_lib.erl8
-rw-r--r--lib/et/test/et_wx_SUITE.erl29
-rw-r--r--lib/et/vsn.mk25
-rw-r--r--lib/eunit/doc/overview.edoc2
-rw-r--r--lib/eunit/doc/src/Makefile2
-rw-r--r--lib/eunit/doc/src/book.xml2
-rw-r--r--lib/eunit/doc/src/notes.xml17
-rw-r--r--lib/eunit/doc/src/part.xml2
-rw-r--r--lib/eunit/doc/src/part_notes.xml2
-rw-r--r--lib/eunit/doc/src/ref_man.xml2
-rw-r--r--lib/eunit/src/eunit.erl2
-rw-r--r--lib/eunit/src/eunit_surefire.erl2
-rw-r--r--lib/eunit/test/Makefile2
-rw-r--r--lib/eunit/test/eunit.cover4
-rw-r--r--lib/eunit/test/eunit.dynspec6
-rw-r--r--lib/eunit/test/eunit.spec3
-rw-r--r--lib/eunit/test/eunit_SUITE.erl27
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/gs/contribs/bonk/bonk.erl26
-rw-r--r--lib/gs/contribs/othello/othello_adt.erl36
-rw-r--r--lib/gs/doc/src/gs.xml14
-rw-r--r--lib/gs/doc/src/gs_chapter1.xml14
-rw-r--r--lib/gs/doc/src/notes.xml45
-rw-r--r--lib/gs/src/tool_utils.erl23
-rw-r--r--lib/gs/vsn.mk2
-rw-r--r--lib/hipe/cerl/cerl_closurean.erl14
-rw-r--r--lib/hipe/cerl/cerl_messagean.erl16
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl663
-rw-r--r--lib/hipe/cerl/erl_types.erl760
-rw-r--r--lib/hipe/doc/src/notes.xml212
-rw-r--r--lib/hipe/doc/src/ref_man.xml2
-rw-r--r--lib/hipe/flow/hipe_dominators.erl12
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl22
-rw-r--r--lib/hipe/icode/hipe_icode_callgraph.erl8
-rw-r--r--lib/hipe/icode/hipe_icode_exceptions.erl12
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl13
-rw-r--r--lib/hipe/icode/hipe_icode_range.erl366
-rw-r--r--lib/hipe/icode/hipe_icode_type.erl20
-rw-r--r--lib/hipe/main/hipe.erl31
-rw-r--r--lib/hipe/main/hipe_main.erl12
-rw-r--r--lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl54
-rw-r--r--lib/hipe/rtl/hipe_rtl.erl7
-rw-r--r--lib/hipe/rtl/hipe_rtl_arith.inc9
-rw-r--r--lib/hipe/rtl/hipe_rtl_primops.erl24
-rw-r--r--lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl45
-rw-r--r--lib/hipe/rtl/hipe_tagscheme.erl6
-rw-r--r--lib/hipe/tools/hipe_tool.erl40
-rw-r--r--lib/hipe/util/hipe_digraph.erl12
-rw-r--r--lib/hipe/vsn.mk2
-rw-r--r--lib/hipe/x86/hipe_x86_spill_restore.erl40
-rw-r--r--lib/ic/doc/src/Makefile16
-rw-r--r--lib/ic/doc/src/notes.xml42
-rw-r--r--lib/ic/src/ic_forms.erl6
-rw-r--r--lib/ic/src/ic_pragma.erl12
-rw-r--r--lib/ic/src/ic_symtab.erl4
-rw-r--r--lib/ic/src/icforms.hrl3
-rw-r--r--lib/ic/src/icparse.yrl22
-rw-r--r--lib/ic/src/ictype.erl41
-rw-r--r--lib/ic/test/Makefile277
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE.erl332
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src145
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/c_client.c1760
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/c_erl_test.idl174
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/erl_server.erl28
-rw-r--r--lib/ic/test/c_client_erl_server_SUITE_data/m_i_impl.erl161
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE.erl332
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src146
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c1763
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/c_erl_test.idl173
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/erl_server.erl28
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/m_i_impl.erl161
-rw-r--r--lib/ic/test/c_client_erl_server_proto_SUITE_data/my.c50
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE.erl334
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src146
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c1763
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_erl_test.idl173
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/erl_server.erl28
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/m_i_impl.erl161
-rw-r--r--lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/my.c51
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE.erl366
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src150
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/c_server.c299
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c610
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/erl_c_test.idl174
-rw-r--r--lib/ic/test/erl_client_c_server_SUITE_data/erl_client.erl331
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE.erl366
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src150
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c299
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c610
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_c_test.idl174
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_client.erl331
-rw-r--r--lib/ic/test/erl_client_c_server_proto_SUITE_data/my.c34
-rw-r--r--lib/ic/test/ic.cover2
-rw-r--r--lib/ic/test/ic.spec1
-rw-r--r--lib/ic/test/ic.spec.vxworks2
-rw-r--r--lib/ic/test/ic_SUITE.erl980
-rw-r--r--lib/ic/test/ic_SUITE_data/Corba.idl1013
-rw-r--r--lib/ic/test/ic_SUITE_data/Coss.idl1537
-rw-r--r--lib/ic/test/ic_SUITE_data/attr.idl29
-rw-r--r--lib/ic/test/ic_SUITE_data/c_err1.idl63
-rw-r--r--lib/ic/test/ic_SUITE_data/c_err2.idl30
-rw-r--r--lib/ic/test/ic_SUITE_data/c_err3.idl28
-rw-r--r--lib/ic/test/ic_SUITE_data/c_norm.idl163
-rw-r--r--lib/ic/test/ic_SUITE_data/enum.idl32
-rw-r--r--lib/ic/test/ic_SUITE_data/forward.idl34
-rw-r--r--lib/ic/test/ic_SUITE_data/include.idl30
-rw-r--r--lib/ic/test/ic_SUITE_data/include2.idl26
-rw-r--r--lib/ic/test/ic_SUITE_data/include3.idl25
-rw-r--r--lib/ic/test/ic_SUITE_data/inherit.idl68
-rw-r--r--lib/ic/test/ic_SUITE_data/inherit_err.idl71
-rw-r--r--lib/ic/test/ic_SUITE_data/inherit_warn.idl64
-rw-r--r--lib/ic/test/ic_SUITE_data/mult_ids.idl92
-rw-r--r--lib/ic/test/ic_SUITE_data/nasty.idl60
-rw-r--r--lib/ic/test/ic_SUITE_data/one.idl29
-rw-r--r--lib/ic/test/ic_SUITE_data/one_followed.idl54
-rw-r--r--lib/ic/test/ic_SUITE_data/one_out.idl28
-rw-r--r--lib/ic/test/ic_SUITE_data/one_raises.idl32
-rw-r--r--lib/ic/test/ic_SUITE_data/one_void.idl30
-rw-r--r--lib/ic/test/ic_SUITE_data/raises_reg.idl52
-rw-r--r--lib/ic/test/ic_SUITE_data/struct.idl53
-rw-r--r--lib/ic/test/ic_SUITE_data/syntax1.idl28
-rw-r--r--lib/ic/test/ic_SUITE_data/syntax2.idl27
-rw-r--r--lib/ic/test/ic_SUITE_data/syntax3.idl20
-rw-r--r--lib/ic/test/ic_SUITE_data/syntax4.idl23
-rw-r--r--lib/ic/test/ic_SUITE_data/syntax5.idl22
-rw-r--r--lib/ic/test/ic_SUITE_data/syntax6.idl20
-rw-r--r--lib/ic/test/ic_SUITE_data/type.idl190
-rw-r--r--lib/ic/test/ic_SUITE_data/typeid.idl28
-rw-r--r--lib/ic/test/ic_SUITE_data/u_case_mult.idl54
-rw-r--r--lib/ic/test/ic_SUITE_data/u_default.idl51
-rw-r--r--lib/ic/test/ic_SUITE_data/u_mult.idl61
-rw-r--r--lib/ic/test/ic_SUITE_data/u_norm.idl63
-rw-r--r--lib/ic/test/ic_SUITE_data/u_type.idl82
-rw-r--r--lib/ic/test/ic_SUITE_data/undef_id.idl63
-rw-r--r--lib/ic/test/ic_be_SUITE.erl89
-rw-r--r--lib/ic/test/ic_be_SUITE_data/plain.idl33
-rw-r--r--lib/ic/test/ic_pp_SUITE.erl622
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/arg.idl38
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/cascade.idl29
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/comment.idl72
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/concat.idl60
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/define.idl41
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/if.idl32
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/if_zero.idl31
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/improp_nest_constr.idl30
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/inc.idl68
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/included1.idl35
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/included2.idl41
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/includer.idl45
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/line.idl45
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/misc.idl44
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/nopara.idl35
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/predef.idl33
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/predef_time.idl24
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/self_ref.idl26
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/separate.idl37
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/swallow_sc.idl37
-rw-r--r--lib/ic/test/ic_pp_SUITE_data/unintended_grp.idl29
-rw-r--r--lib/ic/test/ic_pragma_SUITE.erl306
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m0.idl77
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m1.idl75
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m2.idl40
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m3.idl38
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m4.idl64
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m5.idl28
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m6.idl38
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/reg_m7.idl62
-rw-r--r--lib/ic/test/ic_pragma_SUITE_data/uggly.idl204
-rw-r--r--lib/ic/test/ic_register_SUITE.erl437
-rw-r--r--lib/ic/test/ic_register_SUITE_data/reg_m10.idl37
-rw-r--r--lib/ic/test/ic_register_SUITE_data/reg_m11.idl32
-rw-r--r--lib/ic/test/ic_register_SUITE_data/reg_m12.idl40
-rw-r--r--lib/ic/test/ic_register_SUITE_data/reg_m8.idl32
-rw-r--r--lib/ic/test/ic_register_SUITE_data/reg_m9.idl32
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE.erl340
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/JavaClient.java759
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src88
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/java_erl_test.idl68
-rw-r--r--lib/ic/test/java_client_erl_server_SUITE_data/m_i_impl.erl169
-rw-r--r--lib/ic/vsn.mk19
-rw-r--r--lib/inets/doc/src/ftp.xml2
-rw-r--r--lib/inets/doc/src/http_client.xml39
-rw-r--r--lib/inets/doc/src/http_server.xml17
-rw-r--r--lib/inets/doc/src/httpc.xml140
-rw-r--r--lib/inets/doc/src/httpd.xml76
-rw-r--r--lib/inets/doc/src/inets.xml22
-rw-r--r--lib/inets/doc/src/mod_auth.xml5
-rw-r--r--lib/inets/doc/src/mod_esi.xml3
-rw-r--r--lib/inets/doc/src/notes.xml240
-rw-r--r--lib/inets/examples/Makefile194
-rw-r--r--lib/inets/examples/httpd_load_test/Makefile123
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt.config.skel20
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt.erl74
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt.sh.skel44
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_client.erl370
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_ctrl.erl1530
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_logger.erl138
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_logger.hrl33
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_random_html.erl59
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_server.erl163
-rw-r--r--lib/inets/examples/httpd_load_test/hdlt_slave.erl291
l---------lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem1
l---------lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem1
-rw-r--r--lib/inets/examples/httpd_load_test/modules.mk44
-rw-r--r--lib/inets/examples/server_root/Makefile209
-rw-r--r--lib/inets/examples/subdirs.mk3
-rw-r--r--lib/inets/include/httpd.hrl41
-rw-r--r--lib/inets/include/mod_auth.hrl33
-rw-r--r--lib/inets/src/ftp/Makefile28
-rw-r--r--lib/inets/src/ftp/ftp.erl58
-rw-r--r--lib/inets/src/ftp/ftp_internal.hrl13
-rw-r--r--lib/inets/src/http_client/Makefile27
-rw-r--r--lib/inets/src/http_client/http.erl35
-rw-r--r--lib/inets/src/http_client/httpc.erl88
-rw-r--r--lib/inets/src/http_client/httpc_cookie.erl12
-rw-r--r--lib/inets/src/http_client/httpc_handler.erl297
-rw-r--r--lib/inets/src/http_client/httpc_internal.hrl20
-rw-r--r--lib/inets/src/http_client/httpc_manager.erl61
-rw-r--r--lib/inets/src/http_client/httpc_request.erl15
-rw-r--r--lib/inets/src/http_client/httpc_response.erl4
-rw-r--r--lib/inets/src/http_lib/Makefile30
-rw-r--r--lib/inets/src/http_lib/http_chunk.erl10
-rw-r--r--lib/inets/src/http_lib/http_internal.hrl27
-rw-r--r--lib/inets/src/http_lib/http_transport.erl228
-rw-r--r--lib/inets/src/http_lib/http_uri.erl (renamed from lib/inets/src/http_client/http_uri.erl)50
-rw-r--r--lib/inets/src/http_lib/http_util.erl106
-rw-r--r--lib/inets/src/http_server/Makefile27
-rw-r--r--lib/inets/src/http_server/httpd.erl284
-rw-r--r--lib/inets/src/http_server/httpd.hrl77
-rw-r--r--lib/inets/src/http_server/httpd_acceptor.erl17
-rw-r--r--lib/inets/src/http_server/httpd_cgi.erl13
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl100
-rw-r--r--lib/inets/src/http_server/httpd_esi.erl13
-rw-r--r--lib/inets/src/http_server/httpd_file.erl12
-rw-r--r--lib/inets/src/http_server/httpd_instance_sup.erl6
-rw-r--r--lib/inets/src/http_server/httpd_internal.hrl56
-rw-r--r--lib/inets/src/http_server/httpd_manager.erl38
-rw-r--r--lib/inets/src/http_server/httpd_request.erl70
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl104
-rw-r--r--lib/inets/src/http_server/httpd_script_env.erl3
-rw-r--r--lib/inets/src/http_server/httpd_sup.erl12
-rw-r--r--lib/inets/src/http_server/httpd_util.erl76
-rw-r--r--lib/inets/src/http_server/mod_actions.erl3
-rw-r--r--lib/inets/src/http_server/mod_alias.erl74
-rw-r--r--lib/inets/src/http_server/mod_auth.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth.hrl26
-rw-r--r--lib/inets/src/http_server/mod_auth_dets.erl3
-rw-r--r--lib/inets/src/http_server/mod_auth_plain.erl4
-rw-r--r--lib/inets/src/http_server/mod_auth_server.erl3
-rw-r--r--lib/inets/src/http_server/mod_cgi.erl1
-rw-r--r--lib/inets/src/http_server/mod_dir.erl8
-rw-r--r--lib/inets/src/http_server/mod_disk_log.erl4
-rw-r--r--lib/inets/src/http_server/mod_esi.erl44
-rw-r--r--lib/inets/src/http_server/mod_get.erl4
-rw-r--r--lib/inets/src/http_server/mod_head.erl2
-rw-r--r--lib/inets/src/http_server/mod_htaccess.erl3
-rw-r--r--lib/inets/src/http_server/mod_include.erl7
-rw-r--r--lib/inets/src/http_server/mod_log.erl3
-rw-r--r--lib/inets/src/http_server/mod_range.erl4
-rw-r--r--lib/inets/src/http_server/mod_responsecontrol.erl3
-rw-r--r--lib/inets/src/http_server/mod_security.erl3
-rw-r--r--lib/inets/src/http_server/mod_security_server.erl3
-rw-r--r--lib/inets/src/http_server/mod_trace.erl2
-rw-r--r--lib/inets/src/inets_app/Makefile23
-rw-r--r--lib/inets/src/inets_app/inets.app.src1
-rw-r--r--lib/inets/src/inets_app/inets.appup.src52
-rw-r--r--lib/inets/src/inets_app/inets.erl20
-rw-r--r--lib/inets/src/inets_app/inets.mk45
-rw-r--r--lib/inets/src/inets_app/inets_service.erl12
-rw-r--r--lib/inets/src/tftp/Makefile22
-rw-r--r--lib/inets/test/Makefile46
-rw-r--r--lib/inets/test/ftp_SUITE.erl105
-rw-r--r--lib/inets/test/ftp_format_SUITE.erl51
-rw-r--r--lib/inets/test/ftp_freebsd_x86_test.erl27
-rw-r--r--lib/inets/test/ftp_linux_ppc_test.erl27
-rw-r--r--lib/inets/test/ftp_linux_x86_test.erl36
-rw-r--r--lib/inets/test/ftp_macosx_ppc_test.erl27
-rw-r--r--lib/inets/test/ftp_macosx_x86_test.erl31
-rw-r--r--lib/inets/test/ftp_netbsd_x86_test.erl27
-rw-r--r--lib/inets/test/ftp_openbsd_x86_test.erl27
-rw-r--r--lib/inets/test/ftp_solaris10_sparc_test.erl27
-rw-r--r--lib/inets/test/ftp_solaris10_x86_test.erl27
-rw-r--r--lib/inets/test/ftp_solaris8_sparc_test.erl27
-rw-r--r--lib/inets/test/ftp_solaris9_sparc_test.erl27
-rw-r--r--lib/inets/test/ftp_suite_lib.erl124
-rw-r--r--lib/inets/test/ftp_ticket_test.erl20
-rw-r--r--lib/inets/test/ftp_windows_2003_server_test.erl27
-rw-r--r--lib/inets/test/ftp_windows_xp_test.erl27
-rw-r--r--lib/inets/test/http_format_SUITE.erl81
-rw-r--r--lib/inets/test/httpc_SUITE.erl726
-rw-r--r--lib/inets/test/httpc_cookie_SUITE.erl45
-rw-r--r--lib/inets/test/httpd_1_1.erl2
-rw-r--r--lib/inets/test/httpd_SUITE.erl1736
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/Makefile209
-rw-r--r--lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf2
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl55
-rw-r--r--lib/inets/test/httpd_block.erl101
-rw-r--r--lib/inets/test/httpd_load.erl2
-rw-r--r--lib/inets/test/httpd_mod.erl136
-rw-r--r--lib/inets/test/httpd_poll.erl66
-rw-r--r--lib/inets/test/httpd_test_data/server_root/Makefile209
-rw-r--r--lib/inets/test/httpd_test_data/server_root/conf/httpd.conf2
-rw-r--r--lib/inets/test/httpd_test_lib.erl43
-rw-r--r--lib/inets/test/httpd_time_test.erl65
-rw-r--r--lib/inets/test/inets.cover2
-rw-r--r--lib/inets/test/inets.spec3
-rw-r--r--lib/inets/test/inets_SUITE.erl43
-rw-r--r--lib/inets/test/inets_app_test.erl39
-rw-r--r--lib/inets/test/inets_appup_test.erl41
-rw-r--r--lib/inets/test/inets_sup_SUITE.erl33
-rw-r--r--lib/inets/test/inets_test_lib.erl221
-rw-r--r--lib/inets/test/inets_test_lib.hrl15
-rw-r--r--lib/inets/test/tftp_SUITE.erl38
-rw-r--r--lib/inets/test/tftp_test_lib.erl6
-rw-r--r--lib/inets/test/tftp_test_lib.hrl2
-rw-r--r--lib/inets/vsn.mk90
-rw-r--r--lib/inviso/doc/src/inviso_as_lib.xml2
-rw-r--r--lib/inviso/doc/src/inviso_lfm.xml2
-rw-r--r--lib/inviso/doc/src/inviso_lfm_tpfreader.xml2
-rw-r--r--lib/inviso/doc/src/inviso_rt.xml2
-rw-r--r--lib/inviso/doc/src/notes.xml20
-rw-r--r--lib/inviso/src/inviso.erl78
-rw-r--r--lib/inviso/src/inviso_c.erl50
-rw-r--r--lib/inviso/src/inviso_lfm.erl14
-rw-r--r--lib/inviso/src/inviso_lfm_tpfreader.erl12
-rw-r--r--lib/inviso/src/inviso_tool.erl6579
-rw-r--r--lib/inviso/src/inviso_tool_lib.erl18
-rw-r--r--lib/inviso/src/inviso_tool_sh.erl3480
-rw-r--r--lib/inviso/test/Makefile4
-rw-r--r--lib/inviso/test/inviso.cover2
-rw-r--r--lib/inviso/test/inviso.spec2
-rw-r--r--lib/inviso/test/inviso_tool_SUITE.erl95
-rw-r--r--lib/inviso/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/notes.xml48
-rw-r--r--lib/jinterface/java_src/Makefile18
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java161
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java7
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java3
-rw-r--r--lib/jinterface/java_src/pom.xml.src106
-rw-r--r--lib/jinterface/test/Makefile85
-rw-r--r--lib/jinterface/test/jinterface.cover2
-rw-r--r--lib/jinterface/test/jinterface.spec20
-rw-r--r--lib/jinterface/test/jinterface_SUITE.erl781
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/BooleanAtom.java46
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/GetNames.java68
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Makefile.src62
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java201
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/MboxPing.java50
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java229
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/NodePing.java83
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java168
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/Nodename.java54
-rw-r--r--lib/jinterface/test/jinterface_SUITE_data/RegisterAndWhereis.java79
-rw-r--r--lib/jinterface/test/jitu.erl156
-rw-r--r--lib/jinterface/test/nc_SUITE.erl740
-rw-r--r--lib/jinterface/test/nc_SUITE_data/Makefile.src53
-rw-r--r--lib/jinterface/test/nc_SUITE_data/connection_server.java96
-rw-r--r--lib/jinterface/test/nc_SUITE_data/echo_server.java261
-rw-r--r--lib/jinterface/vsn.mk20
-rw-r--r--lib/kernel/doc/src/code.xml4
-rw-r--r--lib/kernel/doc/src/disk_log.xml2
-rw-r--r--lib/kernel/doc/src/erl_ddll.xml38
-rw-r--r--lib/kernel/doc/src/error_handler.xml2
-rw-r--r--lib/kernel/doc/src/file.xml135
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml2
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml6
-rw-r--r--lib/kernel/doc/src/inet.xml65
-rw-r--r--lib/kernel/doc/src/notes.xml334
-rw-r--r--lib/kernel/doc/src/part_notes_history.xml2
-rw-r--r--lib/kernel/doc/src/rpc.xml2
-rw-r--r--lib/kernel/doc/src/user.xml2
-rw-r--r--lib/kernel/internal_doc/distribution_handshake.txt40
-rw-r--r--lib/kernel/src/application.erl6
-rw-r--r--lib/kernel/src/auth.erl6
-rw-r--r--lib/kernel/src/code.erl149
-rw-r--r--lib/kernel/src/disk_log_1.erl8
-rw-r--r--lib/kernel/src/dist_util.erl15
-rw-r--r--lib/kernel/src/erl_ddll.erl33
-rw-r--r--lib/kernel/src/erl_epmd.erl86
-rw-r--r--lib/kernel/src/erl_epmd.hrl17
-rw-r--r--lib/kernel/src/error_handler.erl11
-rw-r--r--lib/kernel/src/file.erl111
-rw-r--r--lib/kernel/src/file_io_server.erl20
-rw-r--r--lib/kernel/src/gen_sctp.erl38
-rw-r--r--lib/kernel/src/gen_tcp.erl36
-rw-r--r--lib/kernel/src/gen_udp.erl34
-rw-r--r--lib/kernel/src/global.erl30
-rw-r--r--lib/kernel/src/group.erl48
-rw-r--r--lib/kernel/src/inet.erl14
-rw-r--r--lib/kernel/src/inet6_sctp.erl23
-rw-r--r--lib/kernel/src/inet6_tcp_dist.erl6
-rw-r--r--lib/kernel/src/inet_config.erl2
-rw-r--r--lib/kernel/src/inet_dns.erl44
-rw-r--r--lib/kernel/src/inet_int.hrl3
-rw-r--r--lib/kernel/src/inet_parse.erl2
-rw-r--r--lib/kernel/src/inet_res.erl11
-rw-r--r--lib/kernel/src/inet_sctp.erl21
-rw-r--r--lib/kernel/src/kernel.erl9
-rw-r--r--lib/kernel/src/net_kernel.erl234
-rw-r--r--lib/kernel/src/os.erl64
-rw-r--r--lib/kernel/src/pg2.erl4
-rw-r--r--lib/kernel/src/ram_file.erl48
-rw-r--r--lib/kernel/test/Makefile5
-rw-r--r--lib/kernel/test/appinc1.erl2
-rw-r--r--lib/kernel/test/appinc1x.erl2
-rw-r--r--lib/kernel/test/appinc2.erl2
-rw-r--r--lib/kernel/test/appinc2A.erl2
-rw-r--r--lib/kernel/test/appinc2B.erl2
-rw-r--r--lib/kernel/test/appinc2top.erl2
-rw-r--r--lib/kernel/test/application_SUITE.erl53
-rw-r--r--lib/kernel/test/bif_SUITE.erl50
-rw-r--r--lib/kernel/test/ch.erl2
-rw-r--r--lib/kernel/test/ch_sup.erl2
-rw-r--r--lib/kernel/test/cleanup.erl17
-rw-r--r--lib/kernel/test/code_SUITE.erl210
-rw-r--r--lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl9
-rw-r--r--lib/kernel/test/code_a_test.erl2
-rw-r--r--lib/kernel/test/code_b_test.erl2
-rw-r--r--lib/kernel/test/disk_log_SUITE.erl127
-rw-r--r--lib/kernel/test/erl_boot_server_SUITE.erl26
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl81
-rw-r--r--lib/kernel/test/erl_distribution_wb_SUITE.erl33
-rw-r--r--lib/kernel/test/erl_prim_loader_SUITE.erl50
-rw-r--r--lib/kernel/test/error_logger_SUITE.erl32
-rw-r--r--lib/kernel/test/error_logger_warn_SUITE.erl36
-rw-r--r--lib/kernel/test/file_SUITE.erl322
-rw-r--r--lib/kernel/test/file_name_SUITE.erl1756
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl284
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl101
-rw-r--r--lib/kernel/test/gen_tcp_echo_SUITE.erl36
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl210
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl115
-rw-r--r--lib/kernel/test/global_SUITE.erl98
-rw-r--r--lib/kernel/test/global_group_SUITE.erl66
-rw-r--r--lib/kernel/test/heart_SUITE.erl35
-rw-r--r--lib/kernel/test/inet_SUITE.erl279
-rw-r--r--lib/kernel/test/inet_res_SUITE.erl53
-rw-r--r--lib/kernel/test/inet_sockopt_SUITE.erl35
-rw-r--r--lib/kernel/test/init_SUITE.erl108
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl30
-rw-r--r--lib/kernel/test/kernel.cover3
-rw-r--r--lib/kernel/test/kernel.dynspec57
-rw-r--r--lib/kernel/test/kernel.spec4
-rw-r--r--lib/kernel/test/kernel.spec.wxworks63
-rw-r--r--lib/kernel/test/kernel_SUITE.erl33
-rw-r--r--lib/kernel/test/kernel_config_SUITE.erl36
-rw-r--r--lib/kernel/test/myApp.erl2
-rw-r--r--lib/kernel/test/os_SUITE.erl56
-rw-r--r--lib/kernel/test/pdict_SUITE.erl31
-rw-r--r--lib/kernel/test/pg2_SUITE.erl164
-rw-r--r--lib/kernel/test/prim_file_SUITE.erl225
-rw-r--r--lib/kernel/test/ram_file_SUITE.erl33
-rw-r--r--lib/kernel/test/rpc_SUITE.erl35
-rw-r--r--lib/kernel/test/seq_trace_SUITE.erl39
-rw-r--r--lib/kernel/test/topApp.erl2
-rw-r--r--lib/kernel/test/topApp2.erl2
-rw-r--r--lib/kernel/test/topApp3.erl2
-rw-r--r--lib/kernel/test/wrap_log_reader_SUITE.erl52
-rw-r--r--lib/kernel/test/zlib_SUITE.erl81
-rw-r--r--lib/kernel/vsn.mk21
-rw-r--r--lib/megaco/Makefile34
-rw-r--r--lib/megaco/doc/src/Makefile18
-rw-r--r--lib/megaco/doc/src/files.mk14
-rw-r--r--lib/megaco/doc/src/megaco.xml20
-rw-r--r--lib/megaco/doc/src/megaco_flex_scanner.xml4
-rw-r--r--lib/megaco/doc/src/megaco_performance.xml122
-rw-r--r--lib/megaco/doc/src/megaco_user.xml2
-rw-r--r--lib/megaco/doc/src/mstone1-s8flex.log234
-rw-r--r--lib/megaco/doc/src/mstone1.gifbin30004 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/mstone1.jpgbin17845 -> 16746 bytes
-rw-r--r--lib/megaco/doc/src/mstone1.pngbin53099 -> 0 bytes
-rw-r--r--lib/megaco/doc/src/mstone1.ps1959
-rw-r--r--lib/megaco/doc/src/notes.xml593
-rw-r--r--lib/megaco/doc/src/notes_history.xml413
-rw-r--r--lib/megaco/examples/meas/megaco_codec_meas.erl4
-rw-r--r--lib/megaco/examples/meas/megaco_codec_mstone_lib.erl4
-rw-r--r--lib/megaco/src/app/megaco.app.src3
-rw-r--r--lib/megaco/src/app/megaco.appup.src96
-rw-r--r--lib/megaco/src/app/megaco_internal.hrl26
-rw-r--r--lib/megaco/src/binary/megaco_binary_encoder_lib.erl31
-rw-r--r--lib/megaco/src/binary/megaco_binary_transformer_prev3a.erl10
-rw-r--r--lib/megaco/src/binary/megaco_binary_transformer_prev3b.erl10
-rw-r--r--lib/megaco/src/binary/megaco_binary_transformer_prev3c.erl10
-rw-r--r--lib/megaco/src/binary/megaco_binary_transformer_v3.erl10
-rw-r--r--lib/megaco/src/engine/depend.mk4
-rw-r--r--lib/megaco/src/engine/megaco_config.erl182
-rw-r--r--lib/megaco/src/engine/megaco_config_misc.erl113
-rw-r--r--lib/megaco/src/engine/megaco_filter.erl33
-rw-r--r--lib/megaco/src/engine/megaco_messenger.erl239
-rw-r--r--lib/megaco/src/engine/megaco_monitor.erl33
-rw-r--r--lib/megaco/src/engine/megaco_sdp.erl8
-rw-r--r--lib/megaco/src/engine/megaco_timer.erl14
-rw-r--r--lib/megaco/src/engine/modules.mk3
-rw-r--r--lib/megaco/src/flex/megaco_flex_scanner.erl18
-rw-r--r--lib/megaco/src/text/megaco_text_gen_prev3a.hrl6
-rw-r--r--lib/megaco/src/text/megaco_text_gen_prev3b.hrl6
-rw-r--r--lib/megaco/src/text/megaco_text_gen_prev3c.hrl6
-rw-r--r--lib/megaco/src/text/megaco_text_gen_v1.hrl6
-rw-r--r--lib/megaco/src/text/megaco_text_gen_v2.hrl6
-rw-r--r--lib/megaco/src/text/megaco_text_gen_v3.hrl6
-rw-r--r--lib/megaco/test/Makefile2
-rw-r--r--lib/megaco/test/megaco.cover4
-rw-r--r--lib/megaco/test/megaco.spec7
-rw-r--r--lib/megaco/test/megaco_SUITE.erl137
-rw-r--r--lib/megaco/test/megaco_actions_test.erl33
-rw-r--r--lib/megaco/test/megaco_app_test.erl48
-rw-r--r--lib/megaco/test/megaco_appup_mg.erl2
-rw-r--r--lib/megaco/test/megaco_appup_mgc.erl2
-rw-r--r--lib/megaco/test/megaco_appup_test.erl37
-rw-r--r--lib/megaco/test/megaco_binary_term_id_test.erl39
-rw-r--r--lib/megaco/test/megaco_call_flow_test.erl38
-rw-r--r--lib/megaco/test/megaco_codec_flex_lib.erl2
-rw-r--r--lib/megaco/test/megaco_codec_mini_test.erl82
-rw-r--r--lib/megaco/test/megaco_codec_prev3a_test.erl469
-rw-r--r--lib/megaco/test/megaco_codec_prev3b_test.erl481
-rw-r--r--lib/megaco/test/megaco_codec_prev3c_test.erl485
-rw-r--r--lib/megaco/test/megaco_codec_test.erl39
-rw-r--r--lib/megaco/test/megaco_codec_test_lib.erl2
-rw-r--r--lib/megaco/test/megaco_codec_v1_test.erl508
-rw-r--r--lib/megaco/test/megaco_codec_v2_test.erl484
-rw-r--r--lib/megaco/test/megaco_codec_v3_test.erl478
-rw-r--r--lib/megaco/test/megaco_config_test.erl38
-rw-r--r--lib/megaco/test/megaco_digit_map_test.erl67
-rw-r--r--lib/megaco/test/megaco_examples_test.erl25
-rw-r--r--lib/megaco/test/megaco_flex_test.erl45
-rw-r--r--lib/megaco/test/megaco_load_test.erl35
-rw-r--r--lib/megaco/test/megaco_mess_otp8212_test.erl2
-rw-r--r--lib/megaco/test/megaco_mess_test.erl129
-rw-r--r--lib/megaco/test/megaco_mess_user_test.erl2
-rw-r--r--lib/megaco/test/megaco_mib_test.erl26
-rw-r--r--lib/megaco/test/megaco_mreq_test.erl26
-rw-r--r--lib/megaco/test/megaco_pending_limit_test.erl60
-rw-r--r--lib/megaco/test/megaco_profile.erl2
-rw-r--r--lib/megaco/test/megaco_sdp_test.erl32
-rw-r--r--lib/megaco/test/megaco_segment_test.erl70
-rw-r--r--lib/megaco/test/megaco_tc_controller.erl2
-rw-r--r--lib/megaco/test/megaco_tcp_test.erl62
-rw-r--r--lib/megaco/test/megaco_test_deliver.erl2
-rw-r--r--lib/megaco/test/megaco_test_generator.erl6
-rw-r--r--lib/megaco/test/megaco_test_generator_lib.erl2
-rw-r--r--lib/megaco/test/megaco_test_generic_transport.erl2
-rw-r--r--lib/megaco/test/megaco_test_lib.erl244
-rw-r--r--lib/megaco/test/megaco_test_megaco_generator.erl4
-rw-r--r--lib/megaco/test/megaco_test_mg.erl2
-rw-r--r--lib/megaco/test/megaco_test_mgc.erl2
-rw-r--r--lib/megaco/test/megaco_test_msg_prev3a_lib.erl6
-rw-r--r--lib/megaco/test/megaco_test_msg_prev3b_lib.erl6
-rw-r--r--lib/megaco/test/megaco_test_msg_prev3c_lib.erl6
-rw-r--r--lib/megaco/test/megaco_test_msg_v1_lib.erl6
-rw-r--r--lib/megaco/test/megaco_test_msg_v2_lib.erl6
-rw-r--r--lib/megaco/test/megaco_test_msg_v3_lib.erl6
-rw-r--r--lib/megaco/test/megaco_test_tcp_generator.erl4
-rw-r--r--lib/megaco/test/megaco_timer_test.erl65
-rw-r--r--lib/megaco/test/megaco_trans_test.erl107
-rw-r--r--lib/megaco/test/megaco_udp_test.erl53
-rw-r--r--lib/megaco/vsn.mk134
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap2.xmlsrc14
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap3.xml8
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap4.xmlsrc6
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap5.xmlsrc18
-rw-r--r--lib/mnesia/doc/src/mnesia.xml28
-rw-r--r--lib/mnesia/doc/src/mnesia_frag_hash.xml2
-rw-r--r--lib/mnesia/doc/src/mnesia_registry.xml2
-rw-r--r--lib/mnesia/doc/src/notes.xml96
-rw-r--r--lib/mnesia/doc/src/part_notes_history.xml2
-rw-r--r--lib/mnesia/examples/mnesia_meter.erl12
-rw-r--r--lib/mnesia/src/mnesia.appup.src80
-rw-r--r--lib/mnesia/src/mnesia.erl9
-rw-r--r--lib/mnesia/src/mnesia_bup.erl7
-rw-r--r--lib/mnesia/src/mnesia_controller.erl64
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl4
-rw-r--r--lib/mnesia/src/mnesia_frag.erl28
-rw-r--r--lib/mnesia/src/mnesia_index.erl2
-rw-r--r--lib/mnesia/src/mnesia_lib.erl42
-rw-r--r--lib/mnesia/src/mnesia_locker.erl5
-rw-r--r--lib/mnesia/src/mnesia_log.erl4
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl2
-rw-r--r--lib/mnesia/src/mnesia_recover.erl42
-rw-r--r--lib/mnesia/src/mnesia_registry.erl4
-rw-r--r--lib/mnesia/src/mnesia_schema.erl40
-rw-r--r--lib/mnesia/src/mnesia_snmp_hook.erl2
-rw-r--r--lib/mnesia/src/mnesia_subscr.erl2
-rw-r--r--lib/mnesia/src/mnesia_text.erl4
-rw-r--r--lib/mnesia/src/mnesia_tm.erl4
-rw-r--r--lib/mnesia/test/Makefile118
-rw-r--r--lib/mnesia/test/README107
-rw-r--r--lib/mnesia/test/mnesia.cover2
-rw-r--r--lib/mnesia/test/mnesia.spec76
-rw-r--r--lib/mnesia/test/mnesia.spec.vxworks362
-rw-r--r--lib/mnesia/test/mnesia_SUITE.erl162
-rw-r--r--lib/mnesia/test/mnesia_atomicity_test.erl782
-rw-r--r--lib/mnesia/test/mnesia_config_backup.erl105
-rw-r--r--lib/mnesia/test/mnesia_config_event.erl74
-rw-r--r--lib/mnesia/test/mnesia_config_test.erl1420
-rw-r--r--lib/mnesia/test/mnesia_consistency_test.erl1512
-rw-r--r--lib/mnesia/test/mnesia_cost.erl222
-rw-r--r--lib/mnesia/test/mnesia_dbn_meters.erl242
-rw-r--r--lib/mnesia/test/mnesia_dirty_access_test.erl880
-rw-r--r--lib/mnesia/test/mnesia_durability_test.erl1447
-rw-r--r--lib/mnesia/test/mnesia_evil_backup.erl738
-rw-r--r--lib/mnesia/test/mnesia_evil_coverage_test.erl2347
-rw-r--r--lib/mnesia/test/mnesia_examples_test.erl152
-rw-r--r--lib/mnesia/test/mnesia_frag_test.erl859
-rw-r--r--lib/mnesia/test/mnesia_inconsistent_database_test.erl74
-rw-r--r--lib/mnesia/test/mnesia_install_test.erl331
-rw-r--r--lib/mnesia/test/mnesia_isolation_test.erl2355
-rw-r--r--lib/mnesia/test/mnesia_measure_test.erl152
-rw-r--r--lib/mnesia/test/mnesia_meter.erl465
-rw-r--r--lib/mnesia/test/mnesia_nice_coverage_test.erl233
-rw-r--r--lib/mnesia/test/mnesia_qlc_test.erl477
-rw-r--r--lib/mnesia/test/mnesia_recovery_test.erl1638
-rw-r--r--lib/mnesia/test/mnesia_registry_test.erl142
-rw-r--r--lib/mnesia/test/mnesia_schema_recovery_test.erl777
-rw-r--r--lib/mnesia/test/mnesia_test_lib.erl1058
-rw-r--r--lib/mnesia/test/mnesia_test_lib.hrl132
-rw-r--r--lib/mnesia/test/mnesia_tpcb.erl1268
-rw-r--r--lib/mnesia/test/mnesia_trans_access_test.erl1233
-rwxr-xr-xlib/mnesia/test/mt60
-rw-r--r--lib/mnesia/test/mt.erl262
-rw-r--r--lib/mnesia/vsn.mk17
-rw-r--r--lib/observer/doc/src/crashdump.xml6
-rw-r--r--lib/observer/doc/src/crashdump_help.html4
-rw-r--r--lib/observer/doc/src/crashdump_ug.xml44
-rw-r--r--lib/observer/doc/src/etop.xml43
-rw-r--r--lib/observer/doc/src/notes.xml72
-rw-r--r--lib/observer/doc/src/notes_history.xml2
-rw-r--r--lib/observer/doc/src/observer_app.xml2
-rw-r--r--lib/observer/doc/src/part_notes_history.xml2
-rw-r--r--lib/observer/doc/src/ttb.xml2
-rwxr-xr-xlib/observer/priv/bin/cdv4
-rw-r--r--lib/observer/priv/bin/cdv.bat2
-rw-r--r--lib/observer/src/Makefile9
-rw-r--r--lib/observer/src/crashdump_viewer.erl1111
-rw-r--r--lib/observer/src/crashdump_viewer.hrl185
-rw-r--r--lib/observer/src/crashdump_viewer_html.erl601
-rw-r--r--lib/observer/test/Makefile4
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl86
-rw-r--r--lib/observer/test/etop_SUITE.erl32
-rw-r--r--lib/observer/test/observer.cover6
-rw-r--r--lib/observer/test/observer.spec3
-rw-r--r--lib/observer/test/observer_SUITE.erl27
-rw-r--r--lib/observer/test/ttb_SUITE.erl39
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/c_src/odbcserver.c104
-rw-r--r--lib/odbc/c_src/odbcserver.h1
-rw-r--r--lib/odbc/configure.in9
-rw-r--r--lib/odbc/doc/src/databases.xml2
-rw-r--r--lib/odbc/doc/src/notes.xml41
-rw-r--r--lib/odbc/src/odbc.appup.src9
-rw-r--r--lib/odbc/src/odbc.erl6
-rw-r--r--lib/odbc/test/Makefile114
-rw-r--r--lib/odbc/test/README86
-rw-r--r--lib/odbc/test/odbc.cover2
-rw-r--r--lib/odbc/test/odbc.dynspec31
-rw-r--r--lib/odbc/test/odbc.spec25
-rw-r--r--lib/odbc/test/odbc.spec.win5
-rw-r--r--lib/odbc/test/odbc_connect_SUITE.erl823
-rw-r--r--lib/odbc/test/odbc_data_type_SUITE.erl1499
-rw-r--r--lib/odbc/test/odbc_query_SUITE.erl1453
-rw-r--r--lib/odbc/test/odbc_start_SUITE.erl153
-rw-r--r--lib/odbc/test/odbc_test.hrl37
-rw-r--r--lib/odbc/test/odbc_test_lib.erl77
-rw-r--r--lib/odbc/test/oracle.erl246
-rw-r--r--lib/odbc/test/postgres.erl294
-rw-r--r--lib/odbc/test/sqlserver.erl298
-rw-r--r--lib/odbc/vsn.mk24
-rw-r--r--lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl13
-rw-r--r--lib/orber/doc/src/CosNaming_BindingIterator.xml2
-rw-r--r--lib/orber/doc/src/CosNaming_NamingContextExt.xml2
-rw-r--r--lib/orber/doc/src/Module_Interface.xml2
-rw-r--r--lib/orber/doc/src/any.xml2
-rw-r--r--lib/orber/doc/src/ch_idl_to_erlang_mapping.xml37
-rw-r--r--lib/orber/doc/src/corba_object.xml2
-rw-r--r--lib/orber/doc/src/fixed.xml2
-rw-r--r--lib/orber/doc/src/intro_part.xml2
-rw-r--r--lib/orber/doc/src/notes.xml102
-rw-r--r--lib/orber/doc/src/orber_acl.xml2
-rw-r--r--lib/orber/doc/src/orber_tc.xml2
-rw-r--r--lib/orber/doc/src/tools_debugging_part.xml2
-rw-r--r--lib/orber/include/ifr_types.hrl6
-rw-r--r--lib/orber/src/cdr_decode.erl78
-rw-r--r--lib/orber/src/cdr_encode.erl30
-rw-r--r--lib/orber/src/corba.erl6
-rw-r--r--lib/orber/src/orber.app.src2
-rw-r--r--lib/orber/src/orber.erl18
-rw-r--r--lib/orber/src/orber_socket.erl28
-rw-r--r--lib/orber/src/orber_tb.erl4
-rw-r--r--lib/orber/test/Makefile13
-rw-r--r--lib/orber/test/cdrcoding_10_SUITE.erl45
-rw-r--r--lib/orber/test/cdrcoding_11_SUITE.erl45
-rw-r--r--lib/orber/test/cdrcoding_12_SUITE.erl45
-rw-r--r--lib/orber/test/cdrlib_SUITE.erl35
-rw-r--r--lib/orber/test/corba_SUITE.erl47
-rw-r--r--lib/orber/test/csiv2_SUITE.erl85
-rw-r--r--lib/orber/test/data_types_SUITE.erl31
-rw-r--r--lib/orber/test/generated_SUITE.erl50
-rw-r--r--lib/orber/test/iiop_module_do_test_impl.erl2
-rw-r--r--lib/orber/test/iiop_module_test_impl.erl2
-rw-r--r--lib/orber/test/iiop_test_impl.erl2
-rw-r--r--lib/orber/test/interceptors_SUITE.erl30
-rw-r--r--lib/orber/test/iop_ior_10_SUITE.erl30
-rw-r--r--lib/orber/test/iop_ior_11_SUITE.erl30
-rw-r--r--lib/orber/test/iop_ior_12_SUITE.erl30
-rw-r--r--lib/orber/test/lname_SUITE.erl30
-rw-r--r--lib/orber/test/multi_ORB_SUITE.erl324
-rw-r--r--lib/orber/test/naming_context_SUITE.erl35
-rw-r--r--lib/orber/test/orber.cover2
-rw-r--r--lib/orber/test/orber.spec20
-rw-r--r--lib/orber/test/orber_SUITE.erl37
-rw-r--r--lib/orber/test/orber_acl_SUITE.erl27
-rw-r--r--lib/orber/test/orber_firewall_ipv4_in_SUITE.erl40
-rw-r--r--lib/orber/test/orber_firewall_ipv4_out_SUITE.erl35
-rw-r--r--lib/orber/test/orber_firewall_ipv6_in_SUITE.erl41
-rw-r--r--lib/orber/test/orber_firewall_ipv6_out_SUITE.erl35
-rw-r--r--lib/orber/test/orber_nat_SUITE.erl255
-rw-r--r--lib/orber/test/orber_test_lib.erl60
-rw-r--r--lib/orber/test/orber_test_server.idl25
-rw-r--r--lib/orber/test/orber_test_server_impl.erl15
-rw-r--r--lib/orber/test/orber_test_timeout_server_impl.erl2
-rw-r--r--lib/orber/test/orber_web_SUITE.erl36
-rw-r--r--lib/orber/test/tc_SUITE.erl45
-rw-r--r--lib/orber/vsn.mk20
-rw-r--r--lib/os_mon/test/Makefile92
-rw-r--r--lib/os_mon/test/cpu_sup_SUITE.erl296
-rw-r--r--lib/os_mon/test/disksup_SUITE.erl440
-rw-r--r--lib/os_mon/test/memsup_SUITE.erl793
-rw-r--r--lib/os_mon/test/os_mon.cover2
-rw-r--r--lib/os_mon/test/os_mon.spec1
-rw-r--r--lib/os_mon/test/os_mon_SUITE.erl108
-rw-r--r--lib/os_mon/test/os_mon_conf.erl28
-rw-r--r--lib/os_mon/test/os_mon_mib_SUITE.erl742
-rw-r--r--lib/os_mon/test/os_sup_SUITE.erl200
-rw-r--r--lib/parsetools/doc/src/notes.xml52
-rw-r--r--lib/parsetools/include/yeccpre.hrl26
-rw-r--r--lib/parsetools/src/leex.erl53
-rw-r--r--lib/parsetools/src/yecc.erl18
-rw-r--r--lib/parsetools/src/yeccparser.erl215
-rw-r--r--lib/parsetools/test/Makefile6
-rw-r--r--lib/parsetools/test/leex_SUITE.erl44
-rw-r--r--lib/parsetools/test/parsetools.cover2
-rw-r--r--lib/parsetools/test/parsetools.spec2
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl91
-rw-r--r--lib/parsetools/vsn.mk2
-rw-r--r--lib/percept/doc/src/book.xml2
-rw-r--r--lib/percept/doc/src/egd_ug.xmlsrc2
-rw-r--r--lib/percept/doc/src/notes.xml17
-rw-r--r--lib/percept/doc/src/part.xml2
-rwxr-xr-xlib/percept/doc/src/part_notes.xml2
-rw-r--r--lib/percept/doc/src/percept_ug.xmlsrc2
-rw-r--r--lib/percept/doc/src/ref_man.xml2
-rw-r--r--lib/percept/src/egd.erl3
-rw-r--r--lib/percept/src/percept.erl32
-rw-r--r--lib/percept/src/percept_db.erl49
-rw-r--r--lib/percept/test/Makefile6
-rw-r--r--lib/percept/test/egd_SUITE.erl32
-rw-r--r--lib/percept/test/percept.cover2
-rw-r--r--lib/percept/test/percept.spec3
-rw-r--r--lib/percept/test/percept_SUITE.erl30
-rw-r--r--lib/percept/test/percept_db_SUITE.erl76
-rw-r--r--lib/percept/vsn.mk2
-rw-r--r--lib/pman/doc/src/pman.xml2
-rw-r--r--lib/public_key/asn1/OTP-PKIX.asn113
-rw-r--r--lib/public_key/doc/src/book.xml2
-rw-r--r--lib/public_key/doc/src/cert_records.xml39
-rw-r--r--lib/public_key/doc/src/introduction.xml2
-rw-r--r--lib/public_key/doc/src/notes.xml157
-rw-r--r--lib/public_key/doc/src/part.xml2
-rw-r--r--lib/public_key/doc/src/part_notes.xml2
-rw-r--r--lib/public_key/doc/src/public_key.xml577
-rw-r--r--lib/public_key/doc/src/public_key_records.xml2
-rw-r--r--lib/public_key/doc/src/ref_man.xml2
-rw-r--r--lib/public_key/include/public_key.hrl29
-rw-r--r--lib/public_key/src/Makefile6
-rw-r--r--lib/public_key/src/pubkey_cert.erl576
-rw-r--r--lib/public_key/src/pubkey_cert_records.erl484
-rw-r--r--lib/public_key/src/pubkey_crypto.erl160
-rw-r--r--lib/public_key/src/pubkey_pem.erl255
-rw-r--r--lib/public_key/src/pubkey_ssh.erl431
-rw-r--r--lib/public_key/src/public_key.app.src10
-rw-r--r--lib/public_key/src/public_key.appup.src32
-rw-r--r--lib/public_key/src/public_key.erl830
-rw-r--r--lib/public_key/test/Makefile8
-rw-r--r--lib/public_key/test/erl_make_certs.erl421
-rw-r--r--lib/public_key/test/pkits_SUITE.erl56
-rw-r--r--lib/public_key/test/public_key.cover4
-rw-r--r--lib/public_key/test/public_key.spec3
-rw-r--r--lib/public_key/test/public_key_SUITE.erl736
-rw-r--r--lib/public_key/test/public_key_SUITE_data/auth_keys3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/dsa_pub.pem12
-rw-r--r--lib/public_key/test/public_key_SUITE_data/known_hosts3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub1
-rw-r--r--lib/public_key/test/public_key_SUITE_data/rsa_pub.pem4
-rw-r--r--lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem4
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys3
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts2
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub13
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub12
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub7
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub13
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub8
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub9
-rw-r--r--lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub9
-rw-r--r--lib/public_key/vsn.mk12
-rwxr-xr-x[-rw-r--r--]lib/reltool/bin/reltool.escript0
-rw-r--r--lib/reltool/doc/src/notes.xml91
-rw-r--r--lib/reltool/doc/src/reltool.xml78
-rw-r--r--lib/reltool/doc/src/reltool_examples.xml2
-rw-r--r--lib/reltool/doc/src/reltool_usage.xml72
-rw-r--r--lib/reltool/src/reltool.app.src4
-rw-r--r--lib/reltool/src/reltool.erl45
-rw-r--r--lib/reltool/src/reltool.hrl100
-rw-r--r--lib/reltool/src/reltool_mod_win.erl10
-rw-r--r--lib/reltool/src/reltool_server.erl113
-rw-r--r--lib/reltool/src/reltool_target.erl27
-rw-r--r--lib/reltool/test/Makefile9
-rw-r--r--lib/reltool/test/reltool.cover2
-rw-r--r--lib/reltool/test/reltool.spec3
-rw-r--r--lib/reltool/test/reltool_app_SUITE.erl47
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl70
-rw-r--r--lib/reltool/test/reltool_test_lib.erl8
-rw-r--r--lib/reltool/test/reltool_wx_SUITE.erl29
-rw-r--r--lib/reltool/test/rtt.erl2
-rw-r--r--lib/reltool/vsn.mk28
-rw-r--r--lib/runtime_tools/c_src/trace_file_drv.c4
-rw-r--r--lib/runtime_tools/doc/src/notes.xml53
-rw-r--r--lib/runtime_tools/doc/src/notes_history.xml2
-rw-r--r--lib/runtime_tools/doc/src/part_notes_history.xml2
-rw-r--r--lib/runtime_tools/doc/src/runtime_tools_app.xml2
-rw-r--r--lib/runtime_tools/src/inviso_autostart.erl11
-rw-r--r--lib/runtime_tools/src/inviso_autostart_server.erl12
-rw-r--r--lib/runtime_tools/src/inviso_rt.erl70
-rw-r--r--lib/runtime_tools/test/Makefile65
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl922
-rw-r--r--lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl87
-rw-r--r--lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl50
-rw-r--r--lib/runtime_tools/test/erts_alloc_config_SUITE.erl226
-rw-r--r--lib/runtime_tools/test/inviso_SUITE.erl2831
-rw-r--r--lib/runtime_tools/test/inviso_testmodule1_foo.erl9
-rw-r--r--lib/runtime_tools/test/runtime_tools.cover3
-rw-r--r--lib/runtime_tools/test/runtime_tools.spec1
-rw-r--r--lib/runtime_tools/test/runtime_tools_SUITE.erl69
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/doc/src/alarm_handler.xml2
-rw-r--r--lib/sasl/doc/src/appup.xml12
-rw-r--r--lib/sasl/doc/src/notes.xml33
-rw-r--r--lib/sasl/doc/src/part_notes_history.xml2
-rw-r--r--lib/sasl/doc/src/rel.xml2
-rw-r--r--lib/sasl/doc/src/relup.xml2
-rw-r--r--lib/sasl/doc/src/script.xml2
-rw-r--r--lib/sasl/doc/src/systools.xml18
-rw-r--r--lib/sasl/src/release_handler.erl42
-rw-r--r--lib/sasl/src/release_handler_1.erl44
-rw-r--r--lib/sasl/src/systools_make.erl83
-rw-r--r--lib/sasl/src/systools_rc.erl28
-rw-r--r--lib/sasl/src/systools_relup.erl15
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/doc/man1/.gitignore0
-rw-r--r--lib/snmp/doc/src/Makefile34
-rw-r--r--lib/snmp/doc/src/depend.mk3
-rw-r--r--lib/snmp/doc/src/files.mk21
-rw-r--r--lib/snmp/doc/src/make.dep4
-rw-r--r--lib/snmp/doc/src/notes.xml235
-rw-r--r--lib/snmp/doc/src/ref_man.xml5
-rw-r--r--lib/snmp/doc/src/snmp_agent_config_files.xml11
-rw-r--r--lib/snmp/doc/src/snmp_app.xml2
-rw-r--r--lib/snmp/doc/src/snmp_config.xml46
-rw-r--r--lib/snmp/doc/src/snmp_view_based_acm_mib.xml74
-rw-r--r--lib/snmp/doc/src/snmpa.xml75
-rw-r--r--lib/snmp/doc/src/snmpa_error.xml7
-rw-r--r--lib/snmp/doc/src/snmpa_mpd.xml42
-rw-r--r--lib/snmp/doc/src/snmpc.xml45
-rw-r--r--lib/snmp/doc/src/snmpc_cmd.xml191
-rw-r--r--lib/snmp/examples/ex2/snmp_ex2_manager.erl4
-rw-r--r--lib/snmp/examples/ex2/snmp_ex2_simple_standard_test.erl4
-rw-r--r--lib/snmp/include/snmp_types.hrl4
-rw-r--r--lib/snmp/mibs/Makefile.in26
-rw-r--r--lib/snmp/src/agent/snmp_community_mib.erl8
-rw-r--r--lib/snmp/src/agent/snmp_framework_mib.erl16
-rw-r--r--lib/snmp/src/agent/snmp_generic.erl4
-rw-r--r--lib/snmp/src/agent/snmp_notification_mib.erl4
-rw-r--r--lib/snmp/src/agent/snmp_standard_mib.erl279
-rw-r--r--lib/snmp/src/agent/snmp_target_mib.erl17
-rw-r--r--lib/snmp/src/agent/snmp_user_based_sm_mib.erl64
-rw-r--r--lib/snmp/src/agent/snmp_view_based_acm_mib.erl238
-rw-r--r--lib/snmp/src/agent/snmpa.erl205
-rw-r--r--lib/snmp/src/agent/snmpa_agent.erl230
-rw-r--r--lib/snmp/src/agent/snmpa_conf.erl4
-rw-r--r--lib/snmp/src/agent/snmpa_internal.hrl12
-rw-r--r--lib/snmp/src/agent/snmpa_mib.erl12
-rw-r--r--lib/snmp/src/agent/snmpa_mib_lib.erl59
-rw-r--r--lib/snmp/src/agent/snmpa_mpd.erl226
-rw-r--r--lib/snmp/src/agent/snmpa_net_if.erl11
-rw-r--r--lib/snmp/src/agent/snmpa_target_cache.erl4
-rw-r--r--lib/snmp/src/agent/snmpa_trap.erl95
-rw-r--r--lib/snmp/src/agent/snmpa_usm.erl78
-rw-r--r--lib/snmp/src/agent/snmpa_vacm.erl9
-rw-r--r--lib/snmp/src/app/snmp.appup.src212
-rw-r--r--lib/snmp/src/compile/Makefile16
-rw-r--r--lib/snmp/src/compile/depend.mk5
-rw-r--r--lib/snmp/src/compile/modules.mk5
-rw-r--r--lib/snmp/src/compile/snmpc.erl138
-rw-r--r--lib/snmp/src/compile/snmpc.hrl63
-rw-r--r--lib/snmp/src/compile/snmpc.src381
-rw-r--r--lib/snmp/src/compile/snmpc_lib.erl7
-rw-r--r--lib/snmp/src/compile/snmpc_mib_gram.yrl330
-rw-r--r--lib/snmp/src/compile/snmpc_mib_to_hrl.erl5
-rw-r--r--lib/snmp/src/compile/snmpc_tok.erl9
-rw-r--r--lib/snmp/src/manager/snmpm.erl4
-rw-r--r--lib/snmp/src/manager/snmpm_conf.erl4
-rw-r--r--lib/snmp/src/manager/snmpm_config.erl2
-rw-r--r--lib/snmp/src/manager/snmpm_mpd.erl24
-rw-r--r--lib/snmp/src/manager/snmpm_server.erl26
-rw-r--r--lib/snmp/src/manager/snmpm_usm.erl6
-rw-r--r--lib/snmp/src/misc/snmp_conf.erl4
-rw-r--r--lib/snmp/src/misc/snmp_config.erl2
-rw-r--r--lib/snmp/src/misc/snmp_usm.erl2
-rw-r--r--lib/snmp/test/Makefile2
-rw-r--r--lib/snmp/test/klas3.erl2
-rw-r--r--lib/snmp/test/modules.mk4
-rw-r--r--lib/snmp/test/sa.erl2
-rw-r--r--lib/snmp/test/snmp.cover4
-rw-r--r--lib/snmp/test/snmp.spec2
-rw-r--r--lib/snmp/test/snmp_SUITE.erl171
-rw-r--r--lib/snmp/test/snmp_agent_bl_test.erl4
-rw-r--r--lib/snmp/test/snmp_agent_mibs_test.erl59
-rw-r--r--lib/snmp/test/snmp_agent_ms_test.erl478
-rw-r--r--lib/snmp/test/snmp_agent_mt_test.erl478
-rw-r--r--lib/snmp/test/snmp_agent_nfilter_test.erl14
-rw-r--r--lib/snmp/test/snmp_agent_test.erl705
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl12
-rw-r--r--lib/snmp/test/snmp_agent_v1_test.erl4
-rw-r--r--lib/snmp/test/snmp_agent_v2_test.erl478
-rw-r--r--lib/snmp/test/snmp_agent_v3_test.erl478
-rw-r--r--lib/snmp/test/snmp_app_test.erl108
-rw-r--r--lib/snmp/test/snmp_appup_mgr.erl2
-rw-r--r--lib/snmp/test/snmp_appup_test.erl40
-rw-r--r--lib/snmp/test/snmp_compiler_test.erl128
-rw-r--r--lib/snmp/test/snmp_conf_test.erl46
-rw-r--r--lib/snmp/test/snmp_log_test.erl69
-rw-r--r--lib/snmp/test/snmp_manager_config_test.erl208
-rw-r--r--lib/snmp/test/snmp_manager_test.erl239
-rw-r--r--lib/snmp/test/snmp_manager_user.erl2
-rwxr-xr-xlib/snmp/test/snmp_manager_user_old.erl2
-rw-r--r--lib/snmp/test/snmp_manager_user_test.erl128
-rw-r--r--lib/snmp/test/snmp_manager_user_test_lib.erl2
-rw-r--r--lib/snmp/test/snmp_note_store_test.erl30
-rw-r--r--lib/snmp/test/snmp_pdus_test.erl33
-rw-r--r--lib/snmp/test/snmp_test_data/AC-TEST-MIB.mib131
-rw-r--r--lib/snmp/test/snmp_test_data/MC-TEST-MIB.mib173
-rw-r--r--lib/snmp/test/snmp_test_lib.erl22
-rw-r--r--lib/snmp/test/snmp_test_manager.erl2
-rw-r--r--lib/snmp/test/snmp_test_mgr_misc.erl4
-rw-r--r--lib/snmp/test/snmp_test_server.erl8
-rw-r--r--lib/snmp/test/snmp_test_suite.erl4
-rw-r--r--lib/snmp/test/test1.erl2
-rw-r--r--lib/snmp/test/test2.erl2
-rw-r--r--lib/snmp/test/test_config/.gitignore19
-rw-r--r--lib/snmp/test/test_config/Makefile199
-rw-r--r--lib/snmp/test/test_config/agent/agent.conf.src19
-rw-r--r--lib/snmp/test/test_config/agent/community.conf.src15
-rw-r--r--lib/snmp/test/test_config/agent/context.conf.src14
-rw-r--r--lib/snmp/test/test_config/agent/notify.conf.src13
-rw-r--r--lib/snmp/test/test_config/agent/standard.conf.src21
-rw-r--r--lib/snmp/test/test_config/agent/target_addr.conf.src21
-rw-r--r--lib/snmp/test/test_config/agent/target_params.conf.src11
-rw-r--r--lib/snmp/test/test_config/agent/usm.conf.src17
-rw-r--r--lib/snmp/test/test_config/agent/vacm.conf.src27
-rw-r--r--lib/snmp/test/test_config/manager/manager.conf.src16
-rw-r--r--lib/snmp/test/test_config/manager/usm.conf.src9
-rw-r--r--lib/snmp/test/test_config/modules.mk41
-rw-r--r--lib/snmp/test/test_config/snmp_test_config.erl32
-rw-r--r--lib/snmp/test/test_config/sys-agent.config.src43
-rw-r--r--lib/snmp/test/test_config/sys-manager.config.src35
-rw-r--r--lib/snmp/test/test_config/sys.config.src68
-rw-r--r--lib/snmp/vsn.mk160
-rw-r--r--lib/ssh/doc/src/notes.xml129
-rw-r--r--lib/ssh/doc/src/ssh.xml32
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml2
-rw-r--r--lib/ssh/src/ssh.appup.src30
-rw-r--r--lib/ssh/src/ssh.erl49
-rw-r--r--lib/ssh/src/ssh_acceptor.erl10
-rw-r--r--lib/ssh/src/ssh_channel.erl14
-rw-r--r--lib/ssh/src/ssh_cli.erl34
-rwxr-xr-xlib/ssh/src/ssh_connect.hrl3
-rw-r--r--lib/ssh/src/ssh_connection.erl20
-rw-r--r--lib/ssh/src/ssh_connection_controler.erl8
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl23
-rw-r--r--lib/ssh/src/ssh_connection_manager.erl30
-rwxr-xr-xlib/ssh/src/ssh_file.erl42
-rwxr-xr-xlib/ssh/src/ssh_rsa.erl3
-rw-r--r--lib/ssh/src/ssh_sftpd.erl9
-rw-r--r--lib/ssh/src/ssh_system_sup.erl20
-rw-r--r--lib/ssh/src/ssh_transport.erl4
-rw-r--r--lib/ssh/vsn.mk79
-rw-r--r--lib/ssl/Makefile28
-rw-r--r--lib/ssl/doc/src/Makefile4
-rw-r--r--lib/ssl/doc/src/book.xml5
-rw-r--r--lib/ssl/doc/src/create_certs.xml148
-rw-r--r--lib/ssl/doc/src/insidecover.xml14
-rw-r--r--lib/ssl/doc/src/licenses.xml156
-rw-r--r--lib/ssl/doc/src/make.dep30
-rw-r--r--lib/ssl/doc/src/new_ssl.xml685
-rw-r--r--lib/ssl/doc/src/notes.xml353
-rw-r--r--lib/ssl/doc/src/old_ssl.xml709
-rw-r--r--lib/ssl/doc/src/refman.xml9
-rw-r--r--lib/ssl/doc/src/ssl.xml1061
-rw-r--r--lib/ssl/doc/src/ssl_app.xml159
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml16
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml435
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml158
-rw-r--r--lib/ssl/doc/src/usersguide.xml22
-rw-r--r--lib/ssl/doc/src/using_ssl.xml204
-rw-r--r--lib/ssl/examples/certs/Makefile41
-rw-r--r--lib/ssl/examples/certs/Makefile.in80
-rw-r--r--lib/ssl/examples/certs/etc/client/cacerts.pem34
-rw-r--r--lib/ssl/examples/certs/etc/client/cert.pem17
-rw-r--r--lib/ssl/examples/certs/etc/client/key.pem16
-rw-r--r--lib/ssl/examples/certs/etc/erlangCA/cert.pem17
-rw-r--r--lib/ssl/examples/certs/etc/otpCA/cert.pem17
-rw-r--r--lib/ssl/examples/certs/etc/server/cacerts.pem34
-rw-r--r--lib/ssl/examples/certs/etc/server/cert.pem17
-rw-r--r--lib/ssl/examples/certs/etc/server/key.pem16
-rw-r--r--lib/ssl/examples/certs/rnd/RANDbin512 -> 0 bytes
-rw-r--r--lib/ssl/examples/certs/src/make_certs.erl297
-rw-r--r--lib/ssl/examples/src/Makefile2
-rw-r--r--lib/ssl/pkix/Makefile121
-rw-r--r--lib/ssl/pkix/OTP-PKIX.asn1config2
-rw-r--r--lib/ssl/pkix/OTP-PKIX.set.asn6
-rwxr-xr-xlib/ssl/pkix/PKCS-1.asn154
-rw-r--r--lib/ssl/pkix/PKIX1Algorithms88.asn1274
-rw-r--r--lib/ssl/pkix/PKIX1Algorithms88.hrl94
-rw-r--r--lib/ssl/pkix/PKIX1Explicit88.asn1619
-rw-r--r--lib/ssl/pkix/PKIX1Explicit88.hrl163
-rw-r--r--lib/ssl/pkix/PKIX1Implicit88.asn1349
-rw-r--r--lib/ssl/pkix/PKIX1Implicit88.hrl93
-rw-r--r--lib/ssl/pkix/PKIXAttributeCertificate.asn1189
-rw-r--r--lib/ssl/pkix/PKIXAttributeCertificate.hrl64
-rw-r--r--lib/ssl/pkix/README49
-rw-r--r--lib/ssl/pkix/SSL-PKIX.asn1704
-rw-r--r--lib/ssl/pkix/mk_ssl_pkix_oid.erl94
-rw-r--r--lib/ssl/pkix/prebuild.skip5
-rw-r--r--lib/ssl/src/Makefile25
-rw-r--r--lib/ssl/src/inet_ssl_dist.erl29
-rw-r--r--lib/ssl/src/ssl.app.src9
-rw-r--r--lib/ssl/src/ssl.appup.src29
-rw-r--r--lib/ssl/src/ssl.erl513
-rw-r--r--lib/ssl/src/ssl_alert.erl25
-rw-r--r--lib/ssl/src/ssl_app.erl14
-rw-r--r--lib/ssl/src/ssl_base64.erl129
-rw-r--r--lib/ssl/src/ssl_certificate.erl179
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl132
-rw-r--r--lib/ssl/src/ssl_cipher.erl684
-rw-r--r--lib/ssl/src/ssl_cipher.hrl67
-rw-r--r--lib/ssl/src/ssl_connection.erl1084
-rw-r--r--lib/ssl/src/ssl_handshake.erl718
-rw-r--r--lib/ssl/src/ssl_handshake.hrl9
-rw-r--r--lib/ssl/src/ssl_internal.hrl34
-rw-r--r--lib/ssl/src/ssl_manager.erl222
-rw-r--r--lib/ssl/src/ssl_pem.erl147
-rw-r--r--lib/ssl/src/ssl_pkix.erl307
-rw-r--r--lib/ssl/src/ssl_pkix.hrl81
-rw-r--r--lib/ssl/src/ssl_record.erl299
-rw-r--r--lib/ssl/src/ssl_session.erl62
-rw-r--r--lib/ssl/src/ssl_session_cache.erl61
-rw-r--r--lib/ssl/src/ssl_session_cache_api.erl12
-rw-r--r--lib/ssl/src/ssl_ssl3.erl117
-rw-r--r--lib/ssl/src/ssl_sup.erl40
-rw-r--r--lib/ssl/src/ssl_tls1.erl104
-rw-r--r--lib/ssl/test/Makefile23
-rw-r--r--lib/ssl/test/erl_make_certs.erl421
-rw-r--r--lib/ssl/test/make_certs.erl18
-rw-r--r--lib/ssl/test/old_ssl_active_SUITE.erl82
-rw-r--r--lib/ssl/test/old_ssl_active_once_SUITE.erl92
-rw-r--r--lib/ssl/test/old_ssl_dist_SUITE.erl84
-rw-r--r--lib/ssl/test/old_ssl_misc_SUITE.erl76
-rw-r--r--lib/ssl/test/old_ssl_passive_SUITE.erl90
-rw-r--r--lib/ssl/test/old_ssl_peer_cert_SUITE.erl79
-rw-r--r--lib/ssl/test/old_ssl_protocol_SUITE.erl76
-rw-r--r--lib/ssl/test/old_ssl_verify_SUITE.erl76
-rw-r--r--lib/ssl/test/old_transport_accept_SUITE.erl56
-rw-r--r--lib/ssl/test/ssl.cover36
-rw-r--r--lib/ssl/test/ssl.spec2
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl1641
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl473
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl70
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl317
-rw-r--r--lib/ssl/test/ssl_test_MACHINE.erl27
-rw-r--r--lib/ssl/test/ssl_test_lib.erl235
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl449
-rw-r--r--lib/ssl/vsn.mk57
-rw-r--r--lib/stdlib/doc/src/Makefile11
-rw-r--r--lib/stdlib/doc/src/beam_lib.xml1
-rw-r--r--lib/stdlib/doc/src/binary.xml729
-rw-r--r--lib/stdlib/doc/src/calendar.xml34
-rw-r--r--lib/stdlib/doc/src/dets.xml8
-rw-r--r--lib/stdlib/doc/src/dict.xml10
-rw-r--r--lib/stdlib/doc/src/erl_expand_records.xml2
-rw-r--r--lib/stdlib/doc/src/erl_id_trans.xml5
-rw-r--r--lib/stdlib/doc/src/erl_internal.xml2
-rw-r--r--lib/stdlib/doc/src/erl_lint.xml4
-rw-r--r--lib/stdlib/doc/src/erl_parse.xml2
-rw-r--r--lib/stdlib/doc/src/erl_pp.xml2
-rw-r--r--lib/stdlib/doc/src/ets.xml178
-rw-r--r--lib/stdlib/doc/src/filelib.xml35
-rw-r--r--lib/stdlib/doc/src/filename.xml15
-rw-r--r--lib/stdlib/doc/src/gen_event.xml64
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml64
-rw-r--r--lib/stdlib/doc/src/gen_server.xml61
-rw-r--r--lib/stdlib/doc/src/io.xml61
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml4
-rw-r--r--lib/stdlib/doc/src/lists.xml8
-rw-r--r--lib/stdlib/doc/src/log_mf_h.xml2
-rw-r--r--lib/stdlib/doc/src/math.xml2
-rw-r--r--lib/stdlib/doc/src/notes.xml642
-rw-r--r--lib/stdlib/doc/src/orddict.xml10
-rw-r--r--lib/stdlib/doc/src/part_notes_history.xml2
-rw-r--r--lib/stdlib/doc/src/pg.xml2
-rw-r--r--lib/stdlib/doc/src/re.xml145
-rw-r--r--lib/stdlib/doc/src/ref_man.xml7
-rw-r--r--lib/stdlib/doc/src/shell_default.xml2
-rw-r--r--lib/stdlib/doc/src/sofs.xml2
-rw-r--r--lib/stdlib/doc/src/supervisor.xml2
-rw-r--r--lib/stdlib/doc/src/supervisor_bridge.xml2
-rw-r--r--lib/stdlib/doc/src/sys.xml8
-rw-r--r--lib/stdlib/doc/src/timer.xml27
-rw-r--r--lib/stdlib/doc/src/unicode.xml4
-rw-r--r--lib/stdlib/doc/src/unicode_usage.xml44
-rw-r--r--lib/stdlib/doc/src/zip.xml20
-rw-r--r--lib/stdlib/src/Makefile1
-rw-r--r--lib/stdlib/src/base64.erl113
-rw-r--r--lib/stdlib/src/beam_lib.erl7
-rw-r--r--lib/stdlib/src/binary.erl177
-rw-r--r--lib/stdlib/src/c.erl76
-rw-r--r--lib/stdlib/src/calendar.erl55
-rw-r--r--lib/stdlib/src/dets.erl122
-rw-r--r--lib/stdlib/src/dets.hrl4
-rw-r--r--lib/stdlib/src/dets_v8.erl2
-rw-r--r--lib/stdlib/src/dets_v9.erl5
-rw-r--r--lib/stdlib/src/digraph.erl12
-rw-r--r--lib/stdlib/src/edlin.erl5
-rw-r--r--lib/stdlib/src/epp.erl96
-rw-r--r--lib/stdlib/src/erl_compile.erl12
-rw-r--r--lib/stdlib/src/erl_expand_records.erl137
-rw-r--r--lib/stdlib/src/erl_internal.erl159
-rw-r--r--lib/stdlib/src/erl_lint.erl478
-rw-r--r--lib/stdlib/src/erl_parse.yrl59
-rw-r--r--lib/stdlib/src/erl_posix_msg.erl285
-rw-r--r--lib/stdlib/src/erl_pp.erl45
-rw-r--r--lib/stdlib/src/erl_scan.erl47
-rw-r--r--lib/stdlib/src/escript.erl110
-rw-r--r--lib/stdlib/src/ets.erl54
-rw-r--r--lib/stdlib/src/eval_bits.erl4
-rw-r--r--lib/stdlib/src/file_sorter.erl11
-rw-r--r--lib/stdlib/src/filelib.erl62
-rw-r--r--lib/stdlib/src/filename.erl434
-rw-r--r--lib/stdlib/src/gb_sets.erl4
-rw-r--r--lib/stdlib/src/gen_event.erl29
-rw-r--r--lib/stdlib/src/gen_fsm.erl38
-rw-r--r--lib/stdlib/src/gen_server.erl38
-rw-r--r--lib/stdlib/src/io.erl49
-rw-r--r--lib/stdlib/src/io_lib.erl2
-rw-r--r--lib/stdlib/src/io_lib_format.erl44
-rw-r--r--lib/stdlib/src/io_lib_fread.erl12
-rw-r--r--lib/stdlib/src/lists.erl71
-rw-r--r--lib/stdlib/src/log_mf_h.erl14
-rw-r--r--lib/stdlib/src/ms_transform.erl195
-rw-r--r--lib/stdlib/src/orddict.erl4
-rw-r--r--lib/stdlib/src/ordsets.erl20
-rw-r--r--lib/stdlib/src/otp_internal.erl16
-rw-r--r--lib/stdlib/src/proc_lib.erl12
-rw-r--r--lib/stdlib/src/proplists.erl12
-rw-r--r--lib/stdlib/src/qlc.erl2
-rw-r--r--lib/stdlib/src/re.erl63
-rw-r--r--lib/stdlib/src/stdlib.app.src11
-rw-r--r--lib/stdlib/src/string.erl4
-rw-r--r--lib/stdlib/src/supervisor.erl306
-rw-r--r--lib/stdlib/src/timer.erl16
-rw-r--r--lib/stdlib/src/unicode.erl13
-rw-r--r--lib/stdlib/test/Makefile6
-rw-r--r--lib/stdlib/test/array_SUITE.erl57
-rw-r--r--lib/stdlib/test/base64_SUITE.erl118
-rw-r--r--lib/stdlib/test/beam_lib_SUITE.erl47
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl1363
-rw-r--r--lib/stdlib/test/binref.erl588
-rw-r--r--lib/stdlib/test/c_SUITE.erl28
-rw-r--r--lib/stdlib/test/calendar_SUITE.erl52
-rw-r--r--lib/stdlib/test/dets_SUITE.erl208
-rw-r--r--lib/stdlib/test/dict_SUITE.erl32
-rw-r--r--lib/stdlib/test/dict_test_lib.erl2
-rw-r--r--lib/stdlib/test/digraph_SUITE.erl36
-rw-r--r--lib/stdlib/test/digraph_utils_SUITE.erl30
-rw-r--r--lib/stdlib/test/dummy1_h.erl15
-rw-r--r--lib/stdlib/test/dummy_h.erl2
-rw-r--r--lib/stdlib/test/edlin_expand_SUITE.erl37
-rw-r--r--lib/stdlib/test/epp_SUITE.erl147
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl63
-rw-r--r--lib/stdlib/test/erl_eval_helper.erl2
-rw-r--r--lib/stdlib/test/erl_expand_records_SUITE.erl39
-rw-r--r--lib/stdlib/test/erl_internal_SUITE.erl32
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl349
-rw-r--r--lib/stdlib/test/erl_pp_SUITE.erl120
-rw-r--r--lib/stdlib/test/erl_scan_SUITE.erl46
-rw-r--r--lib/stdlib/test/error_logger_forwarder.erl2
-rw-r--r--lib/stdlib/test/escript_SUITE.erl57
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/arg_overflow5
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/linebuf_overflow5
-rw-r--r--lib/stdlib/test/ets_SUITE.erl1344
-rw-r--r--lib/stdlib/test/ets_tough_SUITE.erl32
-rw-r--r--lib/stdlib/test/file_sorter_SUITE.erl49
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl41
-rw-r--r--lib/stdlib/test/filename_SUITE.erl341
-rw-r--r--lib/stdlib/test/fixtable_SUITE.erl37
-rw-r--r--lib/stdlib/test/format_SUITE.erl33
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl93
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl104
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl135
-rw-r--r--lib/stdlib/test/id_transform_SUITE.erl30
-rw-r--r--lib/stdlib/test/io_SUITE.erl121
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl49
-rw-r--r--lib/stdlib/test/lists_SUITE.erl139
-rw-r--r--lib/stdlib/test/log_mf_h_SUITE.erl28
-rw-r--r--lib/stdlib/test/ms_transform_SUITE.erl139
-rw-r--r--lib/stdlib/test/naughty_child.erl2
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl35
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl112
-rw-r--r--lib/stdlib/test/queue_SUITE.erl33
-rw-r--r--lib/stdlib/test/random_SUITE.erl33
-rw-r--r--lib/stdlib/test/random_iolist.erl2
-rw-r--r--lib/stdlib/test/random_unicode_list.erl2
-rw-r--r--lib/stdlib/test/re_SUITE.erl37
-rw-r--r--lib/stdlib/test/select_SUITE.erl36
-rw-r--r--lib/stdlib/test/sets_SUITE.erl36
-rw-r--r--lib/stdlib/test/sets_test_lib.erl2
-rw-r--r--lib/stdlib/test/shell_SUITE.erl88
-rw-r--r--lib/stdlib/test/slave_SUITE.erl27
-rw-r--r--lib/stdlib/test/sofs_SUITE.erl75
-rw-r--r--lib/stdlib/test/stdlib.cover25
-rw-r--r--lib/stdlib/test/stdlib.spec5
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl37
-rw-r--r--lib/stdlib/test/string_SUITE.erl45
-rw-r--r--lib/stdlib/test/supervisor_1.erl2
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl277
-rw-r--r--lib/stdlib/test/supervisor_bridge_SUITE.erl29
-rw-r--r--lib/stdlib/test/sys_SUITE.erl29
-rw-r--r--lib/stdlib/test/tar_SUITE.erl36
-rw-r--r--lib/stdlib/test/timer_SUITE.erl27
-rw-r--r--lib/stdlib/test/timer_simple_SUITE.erl69
-rw-r--r--lib/stdlib/test/unicode_SUITE.erl35
-rw-r--r--lib/stdlib/test/win32reg_SUITE.erl28
-rw-r--r--lib/stdlib/test/y2k_SUITE.erl44
-rw-r--r--lib/stdlib/test/zip_SUITE.erl37
-rw-r--r--lib/stdlib/vsn.mk22
-rw-r--r--lib/syntax_tools/doc/src/notes.xml29
-rw-r--r--lib/syntax_tools/src/epp_dodger.erl2
-rw-r--r--lib/syntax_tools/src/erl_comment_scan.erl1
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl16
-rw-r--r--lib/syntax_tools/src/erl_recomment.erl16
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl5
-rw-r--r--lib/syntax_tools/src/erl_syntax_lib.erl37
-rw-r--r--lib/syntax_tools/src/erl_tidy.erl15
-rw-r--r--lib/syntax_tools/src/igor.erl52
-rw-r--r--lib/syntax_tools/src/prettypr.erl2
-rw-r--r--lib/syntax_tools/test/Makefile4
-rw-r--r--lib/syntax_tools/test/syntax_tools.cover2
-rw-r--r--lib/syntax_tools/test/syntax_tools.dynspec5
-rw-r--r--lib/syntax_tools/test/syntax_tools.spec2
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl25
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/test_server/doc/src/notes.xml157
-rw-r--r--lib/test_server/doc/src/test_server.xml18
-rw-r--r--lib/test_server/doc/src/test_server_ctrl.xml27
-rw-r--r--lib/test_server/doc/src/ts.xml2
-rw-r--r--lib/test_server/src/Makefile11
-rw-r--r--lib/test_server/src/test_server.erl664
-rw-r--r--lib/test_server/src/test_server_ctrl.erl1299
-rw-r--r--lib/test_server/src/test_server_node.erl4
-rw-r--r--lib/test_server/src/test_server_sup.erl19
-rw-r--r--lib/test_server/src/ts.config83
-rw-r--r--lib/test_server/src/ts.erl38
-rw-r--r--lib/test_server/src/ts.unix.config2
-rw-r--r--lib/test_server/src/ts.vxworks.config19
-rw-r--r--lib/test_server/src/ts.win32.config15
-rw-r--r--lib/test_server/src/ts_erl_config.erl39
-rw-r--r--lib/test_server/src/ts_install.erl29
-rw-r--r--lib/test_server/src/ts_install_cth.erl286
-rw-r--r--lib/test_server/src/ts_lib.erl22
-rw-r--r--lib/test_server/src/ts_reports.erl4
-rw-r--r--lib/test_server/src/ts_run.erl459
-rw-r--r--lib/test_server/test/Makefile13
-rw-r--r--lib/test_server/test/test_server.cover34
-rw-r--r--lib/test_server/test/test_server.spec3
-rw-r--r--lib/test_server/test/test_server_SUITE.erl656
-rw-r--r--lib/test_server/test/test_server_SUITE_data/Makefile.src2
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl554
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_SUITE_data/dummy_file (renamed from lib/test_server/test/test_server_SUITE_data/dummy_file)0
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_conf01_SUITE.erl (renamed from lib/test_server/test/test_server_conf01_SUITE.erl)2
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_conf02_SUITE.erl (renamed from lib/test_server/test/test_server_conf02_SUITE.erl)2
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl (renamed from lib/test_server/test/test_server_parallel01_SUITE.erl)2
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_shuffle01_SUITE.erl (renamed from lib/test_server/test/test_server_shuffle01_SUITE.erl)2
-rw-r--r--lib/test_server/test/test_server_SUITE_data/test_server_skip_SUITE.erl (renamed from lib/test_server/test/test_server_skip_SUITE.erl)2
-rw-r--r--lib/test_server/test/test_server_line_SUITE.erl21
-rw-r--r--lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl2
-rw-r--r--lib/test_server/test/test_server_test_lib.erl191
-rw-r--r--lib/test_server/test/test_server_test_lib.hrl23
-rw-r--r--lib/test_server/vsn.mk2
-rw-r--r--lib/toolbar/doc/src/toolbar.xml2
-rw-r--r--lib/tools/c_src/Makefile.in5
-rw-r--r--lib/tools/c_src/erl_memory.c36
-rw-r--r--lib/tools/doc/src/cover.xml31
-rw-r--r--lib/tools/doc/src/cover_chapter.xml9
-rw-r--r--lib/tools/doc/src/cprof.xml2
-rw-r--r--lib/tools/doc/src/eprof.xml55
-rw-r--r--lib/tools/doc/src/erlang_mode.xml4
-rw-r--r--lib/tools/doc/src/erlang_mode_chapter.xml4
-rw-r--r--lib/tools/doc/src/make.xml2
-rw-r--r--lib/tools/doc/src/notes.xml155
-rw-r--r--lib/tools/doc/src/part_notes_history.xml2
-rw-r--r--lib/tools/doc/src/tags.xml2
-rw-r--r--lib/tools/emacs/Makefile1
-rw-r--r--lib/tools/emacs/README9
-rw-r--r--lib/tools/emacs/erlang-eunit.el355
-rw-r--r--lib/tools/emacs/erlang-flymake.el102
-rw-r--r--lib/tools/emacs/erlang-start.el5
-rw-r--r--lib/tools/emacs/erlang.el276
-rw-r--r--lib/tools/emacs/test.erl.indented79
-rw-r--r--lib/tools/emacs/test.erl.orig83
-rw-r--r--lib/tools/src/cover.erl587
-rw-r--r--lib/tools/src/eprof.erl757
-rw-r--r--lib/tools/src/xref_base.erl176
-rw-r--r--lib/tools/src/xref_compiler.erl135
-rw-r--r--lib/tools/src/xref_reader.erl52
-rw-r--r--lib/tools/src/xref_utils.erl2
-rw-r--r--lib/tools/test/Makefile10
-rw-r--r--lib/tools/test/cover_SUITE.erl63
-rw-r--r--lib/tools/test/cprof_SUITE.erl37
-rw-r--r--lib/tools/test/emem_SUITE.erl40
-rw-r--r--lib/tools/test/eprof_SUITE.erl119
-rw-r--r--lib/tools/test/eprof_SUITE_data/eprof_test.erl9
-rw-r--r--lib/tools/test/fprof_SUITE.erl38
-rw-r--r--[l---------]lib/tools/test/ignore_cores.erl159
-rw-r--r--lib/tools/test/instrument_SUITE.erl31
-rw-r--r--lib/tools/test/lcnt_SUITE.erl21
-rw-r--r--lib/tools/test/make_SUITE.erl33
-rw-r--r--lib/tools/test/tools.cover2
-rw-r--r--lib/tools/test/tools.spec2
-rw-r--r--lib/tools/test/tools_SUITE.erl33
-rw-r--r--lib/tools/test/xref_SUITE.erl392
-rw-r--r--lib/tools/test/xref_SUITE_data/read/read.erl12
-rw-r--r--lib/tools/vsn.mk20
-rw-r--r--lib/tv/doc/src/notes.xml38
-rw-r--r--lib/tv/doc/src/tv.xml2
-rw-r--r--lib/tv/src/tv_io_lib.erl9
-rw-r--r--lib/tv/src/tv_io_lib_format.erl17
-rw-r--r--lib/tv/src/tv_pb.erl37
-rw-r--r--lib/tv/src/tv_pg_gridfcns.erl73
-rw-r--r--lib/tv/vsn.mk21
-rw-r--r--lib/typer/RELEASE_NOTES22
-rw-r--r--lib/typer/src/Makefile25
-rw-r--r--lib/typer/src/typer.app.src7
-rw-r--r--lib/typer/src/typer.erl1006
-rw-r--r--lib/typer/src/typer.hrl64
-rw-r--r--lib/typer/src/typer_annotator.erl384
-rw-r--r--lib/typer/src/typer_info.erl162
-rw-r--r--lib/typer/src/typer_map.erl47
-rw-r--r--lib/typer/src/typer_options.erl191
-rw-r--r--lib/typer/src/typer_preprocess.erl154
-rw-r--r--lib/typer/vsn.mk2
-rw-r--r--lib/webtool/doc/src/notes.xml17
-rw-r--r--lib/webtool/doc/src/notes_history.xml2
-rw-r--r--lib/webtool/doc/src/part_notes_history.xml2
-rw-r--r--lib/webtool/doc/src/start_webtool.xml10
-rw-r--r--lib/webtool/doc/src/webtool.xml2
-rw-r--r--lib/webtool/src/webtool.erl18
-rw-r--r--lib/webtool/vsn.mk2
-rw-r--r--lib/wx/.gitignore2
-rw-r--r--lib/wx/Makefile4
-rw-r--r--lib/wx/api_gen/Makefile13
-rw-r--r--lib/wx/api_gen/gen_util.erl12
-rw-r--r--lib/wx/api_gen/gl_gen.erl26
-rw-r--r--lib/wx/api_gen/gl_gen_c.erl185
-rw-r--r--lib/wx/api_gen/gl_gen_erl.erl158
-rw-r--r--lib/wx/api_gen/glapi.conf203
-rw-r--r--lib/wx/api_gen/wx_gen.erl2
-rw-r--r--lib/wx/api_gen/wx_gen.hrl6
-rw-r--r--lib/wx/api_gen/wx_gen_cpp.erl12
-rw-r--r--lib/wx/api_gen/wx_gen_erl.erl17
-rw-r--r--lib/wx/api_gen/wxapi.conf56
-rw-r--r--lib/wx/c_src/Makefile.in72
-rw-r--r--lib/wx/c_src/egl_impl.cpp306
-rw-r--r--lib/wx/c_src/egl_impl.h149
-rw-r--r--lib/wx/c_src/gen/gl_fdefs.h419
-rw-r--r--lib/wx/c_src/gen/gl_finit.h213
-rw-r--r--lib/wx/c_src/gen/gl_funcs.cpp2991
-rw-r--r--lib/wx/c_src/gen/wxe_derived_dest.h2
-rw-r--r--lib/wx/c_src/gen/wxe_events.cpp46
-rw-r--r--lib/wx/c_src/gen/wxe_funcs.cpp51
-rw-r--r--lib/wx/c_src/gen/wxe_macros.h2883
-rw-r--r--lib/wx/c_src/wxe_driver.c6
-rw-r--r--lib/wx/c_src/wxe_driver.h5
-rw-r--r--lib/wx/c_src/wxe_gl.cpp361
-rw-r--r--lib/wx/c_src/wxe_gl.h119
-rw-r--r--lib/wx/c_src/wxe_impl.cpp46
-rw-r--r--lib/wx/c_src/wxe_ps_init.c2
-rw-r--r--lib/wx/c_src/wxe_return.cpp3
-rwxr-xr-xlib/wx/configure.in53
-rw-r--r--lib/wx/doc/src/notes.xml90
-rw-r--r--lib/wx/include/gl.hrl578
-rw-r--r--lib/wx/src/Makefile1
-rw-r--r--lib/wx/src/gen/gl.erl3932
-rw-r--r--lib/wx/src/gen/gl_debug.hrl697
-rw-r--r--lib/wx/src/gen/glu.erl129
-rw-r--r--lib/wx/src/gen/wxGLCanvas.erl6
-rw-r--r--lib/wx/src/gen/wxSystemSettings.erl79
-rw-r--r--lib/wx/src/gen/wxTreeCtrl.erl10
-rw-r--r--lib/wx/src/gen/wxe_debug.hrl2883
-rw-r--r--lib/wx/src/gen/wxe_funcs.hrl2883
-rw-r--r--lib/wx/src/wx.erl10
-rw-r--r--lib/wx/src/wx_object.erl71
-rw-r--r--lib/wx/src/wxe.hrl8
-rw-r--r--lib/wx/src/wxe_master.erl142
-rw-r--r--lib/wx/src/wxe_util.erl49
-rw-r--r--lib/wx/test/Makefile6
-rw-r--r--lib/wx/test/wx.cover2
-rw-r--r--lib/wx/test/wx.spec3
-rw-r--r--lib/wx/test/wx_app_SUITE.erl36
-rw-r--r--lib/wx/test/wx_basic_SUITE.erl34
-rw-r--r--lib/wx/test/wx_class_SUITE.erl55
-rw-r--r--lib/wx/test/wx_event_SUITE.erl36
-rw-r--r--lib/wx/test/wx_opengl_SUITE.erl54
-rw-r--r--lib/wx/test/wx_test_lib.erl10
-rw-r--r--lib/wx/test/wx_xtra_SUITE.erl33
-rw-r--r--lib/wx/vsn.mk8
-rw-r--r--lib/xmerl/doc/src/notes.xml90
-rw-r--r--lib/xmerl/doc/src/notes_history.xml2
-rw-r--r--lib/xmerl/doc/src/xmerl_sax_parser.xml2
-rw-r--r--lib/xmerl/src/xmerl_lib.erl9
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_base.erlsrc41
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc4
-rw-r--r--lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc4
-rw-r--r--lib/xmerl/src/xmerl_scan.erl35
-rw-r--r--lib/xmerl/src/xmerl_uri.erl8
-rw-r--r--lib/xmerl/src/xmerl_xpath.erl4
-rw-r--r--lib/xmerl/src/xmerl_xsd.erl22
-rw-r--r--lib/xmerl/vsn.mk130
2712 files changed, 331815 insertions, 59073 deletions
diff --git a/lib/.gitignore b/lib/.gitignore
index a33916b3bf..56b1ed2b84 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -128,6 +128,7 @@
/megaco/src/text/megaco_text_parser_v1.erl
/megaco/src/text/megaco_text_parser_v2.erl
/megaco/src/text/megaco_text_parser_v3.erl
+/megaco/doc/html/mstone1.jpg
# mnesia
@@ -530,6 +531,10 @@
/percept/doc/src/percept_profile.xml
/percept/doc/src/percept_ug.xml
+# snmp
+
+snmp/doc/intex.html
+
# syntax_tools
/syntax_tools/doc/src/chapter.xml
diff --git a/lib/Makefile b/lib/Makefile
index f5ffc6f166..5faf0c8714 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -51,7 +51,7 @@ else
# --------------
#
ERTS_SUB_DIRECTORIES = stdlib sasl kernel compiler
- OTHER_SUB_DIRECTORIES = tools test_server
+ OTHER_SUB_DIRECTORIES = tools test_server common_test runtime_tools
ifdef BUILD_ALL
ifeq ($(findstring win32,$(TARGET)),win32) # BUILD_ALL on win32
OTHER_SUB_DIRECTORIES += \
diff --git a/lib/appmon/doc/src/appmon.xml b/lib/appmon/doc/src/appmon.xml
index 1acb1eb6fa..ae6147a387 100644
--- a/lib/appmon/doc/src/appmon.xml
+++ b/lib/appmon/doc/src/appmon.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/appmon/doc/src/notes.xml b/lib/appmon/doc/src/notes.xml
index 219b5671a4..ace163bcad 100644
--- a/lib/appmon/doc/src/notes.xml
+++ b/lib/appmon/doc/src/notes.xml
@@ -30,6 +30,37 @@
</header>
<p>This document describes the changes made to the Appmon application.</p>
+<section><title>Appmon 2.1.13</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ An obsolete compiler directive for native code generation
+ was removed from a source file.</p>
+ <p>
+ Own Id: OTP-8839</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Appmon 2.1.12</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warnings due to new autoimported BIFs removed</p>
+ <p>
+ Own Id: OTP-8674 Aux Id: OTP-8579 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Appmon 2.1.11</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/appmon/src/appmon.erl b/lib/appmon/src/appmon.erl
index 6f5d2824d2..2b982cddf0 100644
--- a/lib/appmon/src/appmon.erl
+++ b/lib/appmon/src/appmon.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
-module(appmon).
-behaviour(gen_server).
@@ -838,7 +838,7 @@ draw_apps(GUI, [App | Apps], X, Lx0, N, GSObjs) ->
%% Some necessary data
{_Pid, AppName, _Descr} = App,
Text = atom_to_list(AppName),
- Width = max(8*length(Text)+10, ?wBTN),
+ Width = erlang:max(8*length(Text)+10, ?wBTN),
%% Connect the application to the node label with a line
%% Lx0 = leftmost X coordinate (above previous application button)
@@ -1009,9 +1009,6 @@ bcast(MNodes, Msg) ->
end,
MNodes).
-max(X, Y) when X>Y -> X;
-max(_, Y) -> Y.
-
%% parse_nodes(MNodes) -> NodeApps
%% MNodes -> [#mnode{}]
%% NodeApps -> [{Node, Status, Apps}]
diff --git a/lib/appmon/src/appmon_info.erl b/lib/appmon/src/appmon_info.erl
index 4e36d3a13f..332140f69d 100644
--- a/lib/appmon/src/appmon_info.erl
+++ b/lib/appmon/src/appmon_info.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%----------------------------------------------------------------------
@@ -807,24 +807,21 @@ load(Opts) ->
case get_opt(load_scale, Opts) of
linear ->
- min(trunc(load_range()*(Td/Tot+Q/6)),
+ erlang:min(trunc(load_range()*(Td/Tot+Q/6)),
load_range());
prog ->
- min(trunc(load_range()*prog(Td/Tot+Q/6)),
+ erlang:min(trunc(load_range()*prog(Td/Tot+Q/6)),
load_range())
end;
queue ->
case get_opt(load_scale, Opts) of
linear ->
- min(trunc(load_range()*Q/6), load_range());
+ erlang:min(trunc(load_range()*Q/6), load_range());
prog ->
- min(trunc(load_range()*prog(Q/6)), load_range())
+ erlang:min(trunc(load_range()*prog(Q/6)), load_range())
end
end.
-min(X,Y) when X<Y -> X;
-min(_,Y)->Y.
-
%%
%% T shall be within 0 and 0.9 for this to work correctly
diff --git a/lib/appmon/src/appmon_place.erl b/lib/appmon/src/appmon_place.erl
index 5a6ae6aa48..fe1e909d7c 100644
--- a/lib/appmon/src/appmon_place.erl
+++ b/lib/appmon/src/appmon_place.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%------------------------------------------------------------
%%
@@ -155,10 +155,8 @@ move2(DG, V, LastX, DeltaX) ->
ChLX = foldl(fun(C, LX) -> move2(DG, C, LX, DeltaX) end,
tll(LastX),
appmon_dg:get(out, DG, V)),
- [max(NewX+appmon_dg:get(w, DG, V), hdd(LastX)) | ChLX].
+ [erlang:max(NewX+appmon_dg:get(w, DG, V), hdd(LastX)) | ChLX].
-max(A, B) when A>B -> A;
-max(_, B) -> B.
%%------------------------------------------------------------
%%
diff --git a/lib/appmon/src/appmon_web.erl b/lib/appmon/src/appmon_web.erl
index e8a8422a80..fb7144246c 100644
--- a/lib/appmon/src/appmon_web.erl
+++ b/lib/appmon/src/appmon_web.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,12 +38,6 @@
%% webtool
-export([configData/0]).
-
-%% The following directive caters for (significantly) faster native
-%% code compilation of one function in this file by the HiPE compiler
-%% on register-poor architectures like the x86.
--compile([{hipe,[{regalloc,graph_color}]}]).
-
-behaviour(gen_server).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/appmon/vsn.mk b/lib/appmon/vsn.mk
index 78b95e5688..fa17345daf 100644
--- a/lib/appmon/vsn.mk
+++ b/lib/appmon/vsn.mk
@@ -1,19 +1 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-
-APPMON_VSN = 2.1.11
+APPMON_VSN = 2.1.13
diff --git a/lib/asn1/c_src/asn1_erl_driver.c b/lib/asn1/c_src/asn1_erl_driver.c
index fd284e5800..9dd3a0fd7d 100644
--- a/lib/asn1/c_src/asn1_erl_driver.c
+++ b/lib/asn1/c_src/asn1_erl_driver.c
@@ -1407,7 +1407,6 @@ int decode_partial(ErlDrvBinary **drv_binary,unsigned char *in_buf, int in_buf_l
int msg_index_val;
int *msg_index, *tag_index, tmp_index;
int tag_seq_length;
- char tag_code; /* one of ASN1_SKIPPED, ASN1_OPTIONAL, ASN1_CHOOSEN */
int wanted_tag, next_tag;
int buf_end_index = in_buf_len;
int ret = 0, length, old_index;
@@ -1600,7 +1599,7 @@ int get_value(char *out_buf,
{
int len, lenoflen, indef=0, skip_len;
int ret=0;
- int start_index, out_index = 0;
+ int start_index;
/* printf("get_value 1\n\r"); */
if (in_buf[*msg_index] < 0x80){ /* short definite length */
diff --git a/lib/asn1/doc/src/asn1_ug.xml b/lib/asn1/doc/src/asn1_ug.xml
index f2cd073ec8..12d986308f 100644
--- a/lib/asn1/doc/src/asn1_ug.xml
+++ b/lib/asn1/doc/src/asn1_ug.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -22,7 +22,7 @@
</legalnotice>
<title>Asn1</title>
- <prepared>ETX/DN/SP Kenneth. Lundin</prepared>
+ <prepared>Kenneth Lundin</prepared>
<docno></docno>
<date>1999-03-25</date>
<rev>D</rev>
@@ -41,17 +41,28 @@
decode functions to be used by Erlang programs sending and
receiving ASN.1 specified data.</item>
<item>Run-time functions used by the generated code.</item>
- <item>Encoding rules supported are <em>BER</em>, the
- specialized BER version <em>DER</em> and the basic form of
- aligned and unaligned variants of <em>PER</em>.</item>
+ <item>The supported encoding rules are:
+ <list>
+ <item>
+ Basic Encoding Rules (<em>BER</em>)
+ </item>
+ <item>
+ Distinguished Encoding Rules (<em>DER</em>), a specialized form of BER that is used in security-conscious applications.
+ </item>
+ <item>
+ Packed Encoding Rules (<em>PER</em>) both the aligned and unaligned variant.
+ </item>
+ </list>
+ </item>
</list>
</section>
<section>
<title>Overview</title>
- <p>ASN.1 (Abstract Syntax Notation 1) defines the abstract
- syntax of information. The purpose of ASN.1 is to have
- a platform independent language to express types using a
+ <p>ASN.1 (Abstract Syntax Notation 1) is a formal language for describing data structures to be exchanged between distributed computer systems.
+ The purpose of ASN.1 is to have
+ a platform and programming language independent notation to express
+ types using a
standardized set of rules for the transformation of values of
a defined type, into a stream of bytes. This stream of bytes
can then be sent on a communication channel set up by the
@@ -102,20 +113,16 @@
[<cite id="DUBUISSON"></cite>], free to download at
<url href="http://www.oss.com/asn1/dubuisson.html">http://www.oss.com/asn1/dubuisson.html </url>.
</p>
- <p>Knowledge of Erlang programming is also essential and reading the book
- <em>Concurrent Programming in ERLANG</em>,
- [<cite id="erlbook2"></cite>], is recommended. Part 1 of this is available on the web in
- <url href="http://www.erlang.org/download/erlang-book-part1.pdf">PDF</url> format.
- </p>
</section>
<section>
<title>Capability</title>
<p>This application covers all features of ASN.1 up to the 1997
- edition of the specification. In the 2002 edition some new
- extensions came up of which there are support only for some of
- them. ECN (Cncoding Control Notation) and XML notation are still
- unsupported. Though, the other features of 2002 edition are
+ edition of the specification. In the 2002 edition of ASN.1 a number of
+ new features where introduced of which some are supported while
+ others are not. For example the
+ ECN (Encoding Control Notation) and XML notation are still
+ unsupported. Though, the other features of the 2002 edition are
fully or partly supported as shown below:</p>
<list type="bulleted">
<item>
@@ -308,7 +315,7 @@ erlc -o ../asnfiles -I ../asnfiles -I /usr/local/standards/asn1 Person.asn
<p>Choice of encoding rules, if omitted <c>ber</c> is the
default. The <c>ber_bin</c> and <c>per_bin</c> options
allows for optimizations and are therefore recommended
- instaed of the <c>ber</c> and <c>per</c> options.</p>
+ instead of the <c>ber</c> and <c>per</c> options.</p>
</item>
<tag><c>-o OutDirectory</c></tag>
<item>
@@ -629,7 +636,7 @@ asn1ct:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
<c>driver</c> options does not affect the encode or decode
result, just the time spent in run-time. When <c>ber_bin</c> and
<c>driver</c> or <c>per_bin, optimize</c> and <c>driver</c> is
- combined the C-code driver is used in choosen parts of encode /
+ combined the C-code driver is used in chosen parts of encode /
decode procedure.
</p>
<table>
@@ -749,11 +756,11 @@ asn1rt:decode('H323-MESSAGES','SomeChoiceType',Bytes). </pre>
you may want to continue running the old asn1 run-time
functionality.</item>
<item>Performance issues: If you have an asn1 system with a lot
- of cross references you may gain in performance. Meassurements
+ of cross references you may gain in performance. Measurements
must be done for each case.</item>
</list>
<p>You may choose either the plain multi file compilation that just
- merges the choosen asn1 specs or the <c>{inline,OutputModule}</c>
+ merges the chosen asn1 specs or the <c>{inline,OutputModule}</c>
that also includes the used asn1 run-time functionality.</p>
<p>For both cases you need to specify which asn1 specs you will
compile in a module that must have the extension
@@ -919,7 +926,7 @@ T5 ::= INTEGER (MIN&lt;..-99)
T6 ::= INTEGER {red(0),blue(1),white(2)}
</pre>
<p>The Erlang representation of an ASN.1 INTEGER is an integer or
- an atom if a so called \011<c>Named NumberList</c> (see T6 above)
+ an atom if a so called <c>Named Number List</c> (see T6 above)
is specified.</p>
<p>Below is an example of Erlang code which assigns values for the
above types: </p>
@@ -934,7 +941,7 @@ T6value3 = white
ASN.1 defined types. This style of value can be passed directly
to the encoder for transformation into a series of bytes.</p>
<p>The decoder will return an atom if the value corresponds to a
- symbol in the Named NumberList.</p>
+ symbol in the Named Number List.</p>
</section>
<section>
@@ -978,8 +985,9 @@ N1 = 'NULL',
<p>The enumerated type can be used, when the value we wish to
describe, may only take one of a set of predefined values.</p>
<pre>
-DaysOfTheWeek ::= ENUMERATED { sunday(1),monday(2),tuesday(3),
-\011wednesday(4),thursday(5),friday(6),saturday(7) }
+DaysOfTheWeek ::= ENUMERATED {
+ sunday(1),monday(2),tuesday(3),
+ wednesday(4),thursday(5),friday(6),saturday(7) }
</pre>
<p>For example to assign a weekday value in Erlang use the same atom
as in the <c>Enumerations</c> of the type definition:</p>
@@ -1273,11 +1281,14 @@ Pdu ::= SEQUENCE {
<p>Values can be assigned in Erlang as shown below:</p>
<pre>
MyPdu = #'Pdu'{a=22,b=77.99,c={0,1,2,3,4},d='NULL'}. </pre>
- <p>It is also possible to specify the value for each component in
- a SEQUENCE or a SET as <c>{ComponentName,Value}</c>. It is not
- recommended and is not supported if the flags <c>per_bin</c> or
- <c>ber_bin</c> and <c>optimize</c> were used when the module was
- compiled.</p>
+<note>
+ <p>
+ In very early versions of the asn1 compiler it was also possible to
+ specify the values of the components in
+ a SEQUENCE or a SET as a list of tuples <c>{ComponentName,Value}</c>.
+ This is no longer supported.
+ </p>
+</note>
<p>The decode functions will return a record as result when decoding
a <c>SEQUENCE</c> or a <c>SET</c>.
<marker id="DEFAULT"></marker>
@@ -1293,13 +1304,13 @@ MyPdu = #'Pdu'{a=22,b=77.99,c={0,1,2,3,4},d='NULL'}. </pre>
<p>For instance, if the following types exists in a file "File.asn":</p>
<pre>
Seq1 ::= SEQUENCE {
-\011a INTEGER DEFAULT 1,
-\011b Seq2 DEFAULT {aa TRUE, bb 15}
+ a INTEGER DEFAULT 1,
+ b Seq2 DEFAULT {aa TRUE, bb 15}
}
Seq2 ::= SEQUENCE {
-\011aa BOOLEAN,
-\011bb INTEGER
+ aa BOOLEAN,
+ bb INTEGER
}
</pre>
<p>Some values and the corresponding encoding in an Erlang terminal
@@ -1331,7 +1342,7 @@ ok
<marker id="DEFAULT DER"></marker>
</p>
<p>But, the DER encoding format has stronger requirements regarding
- default\011values both for SET and SEQUENCE. A more elaborate and time
+ default values both for SET and SEQUENCE. A more elaborate and time
expensive check of default values will take place. The following is
an example with the same types and values as above but with der
encoding format.</p>
@@ -1409,7 +1420,7 @@ Bad ::= SET {i INTEGER,
values is the same for SET as for SEQUENCE, and is supported by
the compiler, <seealso marker="#DEFAULT DER">see above</seealso>.</p>
<p>Moreover, in DER the elements of a SET will be sorted. If a
- component is an untagged choice the sorting have to take place
+ component is an un-tagged choice the sorting have to take place
in run-time. This fact emphasizes the following recommendation
if DER encoding format is used.</p>
<p>The concept of SET is an unusual
@@ -1425,7 +1436,7 @@ Bad ::= SET {i INTEGER,
</section>
<section>
- <title>Notes about Extendability for SEQUENCE and SET</title>
+ <title>Notes about Extend-ability for SEQUENCE and SET</title>
<p>When a SEQUENCE or SET contains an extension marker and
extension components like this:</p>
<pre>
@@ -1498,9 +1509,9 @@ C2 ::= CHOICE {
<section>
<title>Extendable CHOICE</title>
<p>When a CHOICE contains an extension marker and the decoder detects
- an unknown alternative of the CHIOCE the value is represented as:</p>
+ an unknown alternative of the CHOICE the value is represented as:</p>
<pre>
-\011 {asn1_ExtAlt, BytesForOpenType}
+{asn1_ExtAlt, BytesForOpenType}
</pre>
<p>Where <c>BytesForOpenType</c> is a list of bytes constituting the
encoding of the "unknown" CHOICE alternative. </p>
@@ -1630,15 +1641,15 @@ V = #'Emb'{a=["qqqq",[1,2,255]],
the record name is extended with an underscore and the component
name. If the embedded structure is deeper with SEQUENCE, SET or
CHOICE types in the line, each component-/alternative-name will
- be added to the recordname.</p>
+ be added to the record-name.</p>
<p>For example:</p>
<pre>
Seq ::= SEQUENCE{
- a\011CHOICE{
-\011b SEQUENCE {
-\011 c INTEGER
-\011 }
-\011}
+ a CHOICE{
+ b SEQUENCE {
+ c INTEGER
+ }
+ }
} </pre>
<p>will result in the following record:</p>
<pre>
@@ -1650,10 +1661,10 @@ Seq ::= SEQUENCE{
<pre>
Seq ::= SEQUENCE {
a SEQUENCE OF SEQUENCE {
-\011 b
+ b
}
c SET OF SEQUENCE {
-\011 d
+ d
}
} </pre>
<p>This results in the records:</p>
@@ -1802,16 +1813,16 @@ GENERAL-PROCEDURES GENERAL-PROCEDURE ::= {
<pre>
StartMessage ::= SEQUENCE {
msgId GENERAL-PROCEDURE.&amp;id ({GENERAL-PROCEDURES}),
- content GENERAL-PROCEDURE.&amp;Message\011({GENERAL-PROCEDURES}{@msgId}),
+ content GENERAL-PROCEDURE.&amp;Message ({GENERAL-PROCEDURES}{@msgId}),
} </pre>
<p>In the type <c>StartMessage</c> the constraint following the
<c>content</c> field tells that in a value of type
<c>StartMessage</c> the value in the <c>content</c> field must
- come from the same object that is choosen by the <c>msgId</c>
+ come from the same object that is chosen by the <c>msgId</c>
field.</p>
<p>So, the value <c>#'StartMessage'{msgId="home",content="Any Printable String"}</c> is legal to encode as a StartMessage
value, while the value <c>#'StartMessage'{msgId="remote", content="Some String"}</c> is illegal since the constraint
- in StartMessage tells that when you have choosen a value from a
+ in StartMessage tells that when you have chosen a value from a
specific object in the object set GENERAL-PROCEDURES in the
msgId field you have to choose a value from that same object in
the content field too. In this second case it should have been
@@ -1831,7 +1842,7 @@ StartMessage ::= SEQUENCE {
information object sets.
A part of a definition can be supplied as a parameter. For
instance, if a Type is used in a definition with certain
- purpose, one want the typename to express the intention. This
+ purpose, one want the type-name to express the intention. This
can be done with parameterization.</p>
<p>When many types (or an other ASN.1 entity) only differs in some
minor cases, but the structure of the types are similar, only
diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml
index 5eadb0c90c..77769afcd4 100644
--- a/lib/asn1/doc/src/notes.xml
+++ b/lib/asn1/doc/src/notes.xml
@@ -31,6 +31,132 @@
<p>This document describes the changes made to the asn1 application.</p>
+<section><title>Asn1 1.6.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ asn1ct: Make formatting of errors and warnings consistent</p>
+ <p>
+ Consistently format warning and error reports. Warning
+ and error options from erlc now also work in asnc1ct.
+ (thanks to Tuncer Ayaz)</p>
+ <p>
+ Own Id: OTP-9062</p>
+ </item>
+ <item>
+ <p>
+ Shut off some dialyzer warnings</p>
+ <p>
+ Own Id: OTP-9063</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Crash in asn1ct_check, componentrelation_leadingattr
+ fixed. (Thanks to Stephane Pamelard for finding the bug)</p>
+ <p>
+ Own Id: OTP-9092</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Asn1 1.6.15</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The encoding of ExtensionAdditionGroup (for PER and UPER)
+ is corrected.</p>
+ <p>
+ Own Id: OTP-8866 Aux Id: OTP-8797, SEQ-11557 </p>
+ </item>
+ <item>
+ <p>
+ A race condition when several processes in parallel start
+ to do encode/decode using the driver could cause an error
+ log regarding crashing port owner process. This race is
+ now eliminated.</p>
+ <p>
+ Own Id: OTP-8948 Aux Id: seq11733 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Asn1 1.6.14.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Extension Addition Groups are now supported by the parser
+ and in all backends.</p>
+ <p>
+ Own Id: OTP-8598 Aux Id: seq-11557 </p>
+ </item>
+ <item>
+ <p>
+ Extension Addition Groups are now supported in nested
+ types within a SEQUENCE and CHOICE as well (missed that
+ in previous fix)</p>
+ <p>
+ Own Id: OTP-8797 Aux Id: seq-11557 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Bug in UNALIGNED PER regarding encoding and decoding of
+ constrained numbers with a valuerange > 1024. (Thanks to
+ Vincent de Phily)</p>
+ <p>
+ Own Id: OTP-8779</p>
+ </item>
+ <item>
+ <p>
+ Minor corrections in the User Guide.</p>
+ <p>
+ Own Id: OTP-8829</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Asn1 1.6.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ By default, the ASN.1 compiler is now silent in the
+ absence of warnings or errors. The new '<c>verbose</c>'
+ option or the '<c>-v</c>' option for <c>erlc</c> can be
+ given to show extra information (for instance, about the
+ files that are generated). (Thanks to Tuncer Ayaz.)</p>
+ <p>
+ Own Id: OTP-8565</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Asn1 1.6.13</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/asn1/src/asn1.appup.src b/lib/asn1/src/asn1.appup.src
index 753d308684..2d11eddfbf 100644
--- a/lib/asn1/src/asn1.appup.src
+++ b/lib/asn1/src/asn1.appup.src
@@ -1,111 +1,9 @@
{"%VSN%",
- [
- {"1.6.8",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- },
- {"1.6.8.1",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- },
- {"1.6.9",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- },
- {"1.6.10",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- }
+% This version does not change anything of the runtime modules
+% Only changes in compile time modules and thus no need for upgrade on target
+[
],
[
- {"1.6.8",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- },
- {"1.6.8.1",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- },
- {"1.6.9",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- },
- {"1.6.10",
- [
- {load_module, asn1rt, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
- {load_module, asn1rt_uper_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
- {load_module, asn1rt_ber_bin_v2, soft_purge, soft_purge, []},
- {load_module, asn1rt_check, soft_purge, soft_purge, []},
- {load_module, asn1rt_driver_handler, soft_purge, soft_purge, []},
- {apply, {asn1rt_driver_handler,unload_driver,[]}}
- ]
- }
]}.
diff --git a/lib/asn1/src/asn1_records.hrl b/lib/asn1/src/asn1_records.hrl
index 8a428b744c..59a9acb7e7 100644
--- a/lib/asn1/src/asn1_records.hrl
+++ b/lib/asn1/src/asn1_records.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -36,7 +36,8 @@
-record(module,{pos,name,defid,tagdefault='EXPLICIT',exports={exports,[]},imports={imports,[]}, extensiondefault=empty,typeorval}).
--record('SEQUENCE',{pname=false,tablecinf=false,components=[]}).
+-record('ExtensionAdditionGroup',{number}).
+-record('SEQUENCE',{pname=false,tablecinf=false,extaddgroup,components=[]}).
-record('SET',{pname=false,sorted=false,tablecinf=false,components=[]}).
-record('ComponentType',{pos,name,typespec,prop,tags,textual_order}).
-record('ObjectClassFieldType',{classname,class,fieldname,type}).
diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl
index ae681a4a78..a167d27f82 100644
--- a/lib/asn1/src/asn1ct.erl
+++ b/lib/asn1/src/asn1ct.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -39,7 +39,7 @@
add_tobe_refed_func/1,add_generated_refed_func/1,
maybe_rename_function/3,latest_sindex/0,current_sindex/0,
set_current_sindex/1,next_sindex/0,maybe_saved_sindex/2,
- parse_and_save/2,report_verbose/3]).
+ parse_and_save/2,verbose/3,warning/3,error/3]).
-include("asn1_records.hrl").
-include_lib("stdlib/include/erl_compile.hrl").
@@ -103,8 +103,8 @@ compile(File,Options) when is_list(Options) ->
compile1(File,Options) when is_list(Options) ->
- report_verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File],Options),
- report_verbose("Compiler Options: ~p~n",[Options],Options),
+ verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File],Options),
+ verbose("Compiler Options: ~p~n",[Options],Options),
Ext = filename:extension(File),
Base = filename:basename(File,Ext),
OutFile = outfile(Base,"",Options),
@@ -149,17 +149,17 @@ compile1(File,Options) when is_list(Options) ->
inline(true,Name,Module,Options) ->
RTmodule = get_runtime_mod(Options),
IgorOptions = igorify_options(remove_asn_flags(Options)),
- IgorName = filename:rootname(filename:basename(Name)),
+ IgorName = list_to_atom(filename:rootname(filename:basename(Name))),
% io:format("*****~nName: ~p~nModules: ~p~nIgorOptions: ~p~n*****~n",
% [IgorName,Modules++RTmodule,IgorOptions]),
- report_verbose("Inlining modules: ~p in ~p~n",[[Module]++RTmodule,IgorName],Options),
+ verbose("Inlining modules: ~p in ~p~n",[[Module]++RTmodule,IgorName],Options),
case catch igor:merge(IgorName,[Module]++RTmodule,[{preprocess,true},{stubs,false},{backups,false}]++IgorOptions) of
{'EXIT',{undef,Reason}} -> %% module igor first in R10B
- io:format("Module igor in syntax_tools must be available:~n~p~n",
- [Reason]),
+ error("Module igor in syntax_tools must be available:~n~p~n",
+ [Reason],Options),
{error,'no_compilation'};
{'EXIT',Reason} ->
- io:format("Merge by igor module failed due to ~p~n",[Reason]),
+ error("Merge by igor module failed due to ~p~n",[Reason],Options),
{error,'no_compilation'};
_ ->
%% io:format("compiling output module: ~p~n",[generated_file(Name,IgorOptions)]),
@@ -173,8 +173,8 @@ inline(_,_,_,_) ->
compile_set(SetBase,Files,Options)
when is_list(hd(Files)),is_list(Options) ->
%% case when there are several input files in a list
- report_verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files],Options),
- report_verbose("Compiler Options: ~p~n",[Options],Options),
+ verbose("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files],Options),
+ verbose("Compiler Options: ~p~n",[Options],Options),
OutFile = outfile(SetBase,"",Options),
DbFile = outfile(SetBase,"asn1db",Options),
Includes = [I || {i,I} <- Options],
@@ -728,7 +728,7 @@ parse_set(ScanRes,Options) ->
scan(File,Options) ->
case asn1ct_tok:file(File) of
{error,Reason} ->
- io:format("~p~n",[Reason]),
+ error("~p~n",[Reason],Options),
{false,{error,Reason}};
Tokens ->
case lists:member(ss,Options) of
@@ -753,16 +753,17 @@ parse({true,Tokens},File,Options) ->
if
is_integer(Line) ->
BaseName = filename:basename(File),
- io:format("syntax error at line ~p in module ~s:~n",
- [Line,BaseName]);
+ error("syntax error at line ~p in module ~s:~n",
+ [Line,BaseName],Options);
true ->
- io:format("syntax error in module ~p:~n",[File])
+ error("syntax error in module ~p:~n",
+ [File],Options)
end,
print_error_message(Message),
{false,{error,Message}};
{error,{Line,_Mod,[Message,Token]}} ->
- io:format("syntax error: ~p ~p at line ~p~n",
- [Message,Token,Line]),
+ error("syntax error: ~p ~p at line ~p~n",
+ [Message,Token,Line],Options),
{false,{error,{Line,[Message,Token]}}};
{ok,M} ->
case lists:member(sp,Options) of
@@ -772,7 +773,7 @@ parse({true,Tokens},File,Options) ->
{true,M}
end;
OtherError ->
- io:format("~p~n",[OtherError])
+ error("~p~n",[OtherError],Options)
end;
parse({false,Tokens},_,_) ->
{false,Tokens}.
@@ -802,7 +803,7 @@ check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) ->
NewM = Module#module{typeorval=NewTypeOrVal},
asn1_db:dbput(NewM#module.name,'MODULE',NewM),
asn1_db:dbsave(DbFile,M#module.name),
- report_verbose("--~p--~n",[{generated,DbFile}],Options),
+ verbose("--~p--~n",[{generated,DbFile}],Options),
{true,{M,NewM,GenTypeOrVal}}
end
end;
@@ -823,11 +824,11 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
% io:format("Options: ~p~n",[Options]),
case catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options) of
{error, enoent} -> ok;
- {error, Reason} -> io:format("WARNING: Error in configuration"
- "file: ~n~p~n",[Reason]);
- {'EXIT',Reason} -> io:format("WARNING: Internal error when "
- "analyzing configuration"
- "file: ~n~p~n",[Reason]);
+ {error, Reason} -> warning("Error in configuration "
+ "file: ~n~p~n",[Reason],Options);
+ {'EXIT',Reason} -> warning("Internal error when "
+ "analyzing configuration "
+ "file: ~n~p~n",[Reason],Options);
_ -> ok
end,
@@ -835,7 +836,7 @@ generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
case (catch asn1ct_gen:pgen(OutFile,EncodingRule,
M#module.name,GenTOrV,Options)) of
{'EXIT',Reason2} ->
- io:format("ERROR: ~p~n",[Reason2]),
+ error("~p~n",[Reason2],Options),
{error,Reason2};
_ ->
ok
@@ -878,7 +879,8 @@ parse_and_save(Module,S) ->
_ -> ok
end;
Err ->
- io:format("Warning: could not do a consistency check of the ~p file: no asn1 source file was found.~n",[lists:concat([Module,".asn1db"])]),
+ warning("could not do a consistency check of the ~p file: no asn1 source file was found.~n",
+ [lists:concat([Module,".asn1db"])],Options),
{error,{asn1,input_file_error,Err}}
end.
parse_and_save1(S,File,Options,Includes) ->
@@ -1183,6 +1185,7 @@ is_inline(Options) ->
_ ->
lists:keymember(inline,1,Options)
end.
+
inline_output(Options,Default) ->
case [X||{inline,X}<-Options] of
[OutputName] ->
@@ -1207,7 +1210,7 @@ compile_py(File,OutFile,Options) ->
compile(File, _OutFile, Options) ->
case catch compile(File, make_erl_options(Options)) of
Exit = {'EXIT',_Reason} ->
- io:format("~p~n~s~n",[Exit,"error"]),
+ error("~p~n~s~n",[Exit,"error"],Options),
error;
{error,_Reason} ->
%% case occurs due to error in asn1ct_parser2,asn1ct_check
@@ -1223,7 +1226,7 @@ compile(File, _OutFile, Options) ->
io:format("~p~n",[ScanRes]),
ok;
Unknown ->
- io:format("~p~n~s~n",[Unknown,"error"]),
+ error("~p~n~s~n",[Unknown,"error"],Options),
error
end.
@@ -1237,7 +1240,7 @@ make_erl_options(Opts) ->
Includes = Opts#options.includes,
Defines = Opts#options.defines,
Outdir = Opts#options.outdir,
-%% Warning = Opts#options.warning,
+ Warning = Opts#options.warning,
Verbose = Opts#options.verbose,
Specific = Opts#options.specific,
Optimize = Opts#options.optimize,
@@ -1249,10 +1252,10 @@ make_erl_options(Opts) ->
true -> [verbose];
false -> []
end ++
-%%% case Warning of
-%%% 0 -> [];
-%%% _ -> [report_warnings]
-%%% end ++
+ case Warning of
+ 0 -> [];
+ _ -> [warnings]
+ end ++
[] ++
case Optimize of
1 -> [optimize];
@@ -1276,7 +1279,7 @@ make_erl_options(Opts) ->
uper_bin -> [uper_bin]
end,
- Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}|
+ Options++[errors, {cwd, Cwd}, {outdir, Outdir}|
lists:map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
pretty2(Module,AbsFile) ->
@@ -1674,7 +1677,7 @@ create_pdec_inc_command(ModName,
% [concat_sequential(lists:reverse(Comms),
% [LastComm,CompAcc])|Acc]
case lists:reverse(TagCommand) of
- [Atom|Comms]�when is_atom(Atom) ->
+ [Atom|Comms] when is_atom(Atom) ->
[concat_sequential(lists:reverse(Comms),
[Atom,CompAcc])|Acc];
[[Command2,Tag2]|Comms] ->
@@ -2518,13 +2521,48 @@ type_check(#'Externaltypereference'{}) ->
make_suffix(_) ->
"".
-report_verbose(Format, Args, S) ->
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Report functions.
+%%
+%% Errors messages are controlled with the 'errors' compiler option
+%% Warning messages are controlled with the 'warnings' compiler option
+%% Verbose messages are controlled with the 'verbose' compiler option
+
+error(Format, Args, S) ->
+ case is_error(S) of
+ true ->
+ io:format("Error: " ++ Format, Args);
+ false ->
+ ok
+ end.
+
+warning(Format, Args, S) ->
+ case is_warning(S) of
+ true ->
+ io:format("Warning: " ++ Format, Args);
+ false ->
+ ok
+ end.
+
+verbose(Format, Args, S) ->
case is_verbose(S) of
- true ->
- io:format(Format, Args);
- false ->
- ok
+ true ->
+ io:format(Format, Args);
+ false ->
+ ok
end.
-is_verbose(S) ->
- lists:member(verbose, S).
+is_error(S) when is_record(S, state) ->
+ is_error(S#state.options);
+is_error(O) ->
+ lists:member(errors, O) orelse is_verbose(O).
+
+is_warning(S) when is_record(S, state) ->
+ is_warning(S#state.options);
+is_warning(O) ->
+ lists:member(warnings, O) orelse is_verbose(O).
+
+is_verbose(S) when is_record(S, state) ->
+ is_verbose(S#state.options);
+is_verbose(O) ->
+ lists:member(verbose, O).
diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl
index 54a5c7e727..efd731f052 100644
--- a/lib/asn1/src/asn1ct_check.erl
+++ b/lib/asn1/src/asn1ct_check.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,8 @@
%% Main Module for ASN.1 compile time functions
%-compile(export_all).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([check/2,storeindb/2]).
%-define(debug,1).
-include("asn1_records.hrl").
@@ -2027,8 +2029,9 @@ get_objectset_def2(_S,Set,CField) when is_list(Set) ->
set=Set}};
get_objectset_def2(_S,T = #typedef{typespec=#'ObjectSet'{}},_CField) ->
T;
-get_objectset_def2(_S,T,_CField) ->
- io:format("Warning get_objectset_def2: uncontrolled object set structure:~n~p~n",[T]).
+get_objectset_def2(S,T,_CField) ->
+ asn1ct:warning("get_objectset_def2: uncontrolled object set structure:~n~p~n",
+ [T],S).
type_name(S,#type{def=Def}) ->
CurrMod = S#state.mname,
@@ -2685,7 +2688,7 @@ normalize_value(S,Type,{'DEFAULT',Value},NameList) ->
{'REAL',_,_} ->
normalize_real(Value);
{'ENUMERATED',CType,_} ->
- normalize_enumerated(Value,CType);
+ normalize_enumerated(S,Value,CType);
{'CHOICE',CType,NewNameList} ->
normalize_choice(S,Value,CType,NewNameList);
{'SEQUENCE',CType,NewNameList} ->
@@ -2701,7 +2704,8 @@ normalize_value(S,Type,{'DEFAULT',Value},NameList) ->
{'ASN1_OPEN_TYPE',{typefield,_TF},NL} -> %an open type
normalize_objectclassfieldvalue(S,Value,NL);
Err ->
- io:format("WARNING: could not check default value ~p~nType:~n~p~nNameList:~n~p~n",[Value,Type,Err]),
+ asn1ct:warning("could not check default value ~p~nType:~n~p~nNameList:~n~p~n",
+ [Value,Type,Err],S),
Value
end;
normalize_value(S,Type,Val,NameList) ->
@@ -2786,23 +2790,23 @@ normalize_bitstring(S,Value,Type)->
end,
case catch lists:map(F,RecList) of
{error,Reason} ->
- io:format("WARNING: default value not "
+ asn1ct:warning("default value not "
"compatible with type definition ~p~n",
- [Reason]),
+ [Reason],S),
Value;
NewList ->
NewList
end;
_ ->
- io:format("WARNING: default value not "
+ asn1ct:warning("default value not "
"compatible with type definition ~p~n",
- [RecList]),
+ [RecList],S),
Value
end;
{Name,String} when is_atom(Name) ->
normalize_bitstring(S,String,Type);
Other ->
- io:format("WARNING: illegal default value ~p~n",[Other]),
+ asn1ct:warning("illegal default value ~p~n",[Other],S),
Value
end.
@@ -2841,12 +2845,13 @@ normalize_octetstring(S,Value,CType) ->
%% check if list elements are valid octet values
lists:map(fun([])-> ok;
(H)when H > 255->
- io:format("WARNING: not legal octet value ~p in OCTET STRING, ~p~n",[H,List]);
+ asn1ct:warning("not legal octet value ~p in OCTET STRING, ~p~n",
+ [H,List],S);
(_)-> ok
end, List),
List;
Other ->
- io:format("WARNING: unknown default value ~p~n",[Other]),
+ asn1ct:warning("unknown default value ~p~n",[Other],S),
Value
end.
@@ -2893,23 +2898,23 @@ normalize_objectdescriptor(Value) ->
normalize_real(Value) ->
Value.
-normalize_enumerated(#'Externalvaluereference'{value=V},CType)
+normalize_enumerated(S,#'Externalvaluereference'{value=V},CType)
when is_list(CType) ->
- normalize_enumerated2(V,CType);
-normalize_enumerated(Value,CType) when is_atom(Value),is_list(CType) ->
- normalize_enumerated2(Value,CType);
-normalize_enumerated({Name,EnumV},CType) when is_atom(Name) ->
- normalize_enumerated(EnumV,CType);
-normalize_enumerated(Value,{CType1,CType2}) when is_list(CType1), is_list(CType2)->
- normalize_enumerated(Value,CType1++CType2);
-normalize_enumerated(V,CType) ->
- io:format("WARNING: Enumerated unknown type ~p~n",[CType]),
+ normalize_enumerated2(S,V,CType);
+normalize_enumerated(S,Value,CType) when is_atom(Value),is_list(CType) ->
+ normalize_enumerated2(S,Value,CType);
+normalize_enumerated(S,{Name,EnumV},CType) when is_atom(Name) ->
+ normalize_enumerated(S,EnumV,CType);
+normalize_enumerated(S,Value,{CType1,CType2}) when is_list(CType1), is_list(CType2)->
+ normalize_enumerated(S,Value,CType1++CType2);
+normalize_enumerated(S,V,CType) ->
+ asn1ct:warning("Enumerated unknown type ~p~n",[CType],S),
V.
-normalize_enumerated2(V,Enum) ->
+normalize_enumerated2(S,V,Enum) ->
case lists:keysearch(V,1,Enum) of
{value,{Val,_}} -> Val;
_ ->
- io:format("WARNING: Enumerated value is not correct ~p~n",[V]),
+ asn1ct:warning("Enumerated value is not correct ~p~n",[V],S),
V
end.
@@ -2920,8 +2925,7 @@ normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when is_atom(C) ->
{C,normalize_value(S,CT,{'DEFAULT',V},
[Name|NameList])};
Other ->
- io:format("WARNING: Wrong format of type/value ~p/~p~n",
- [Other,V]),
+ asn1ct:warning("Wrong format of type/value ~p/~p~n",[Other,V],S),
{C,V}
end;
normalize_choice(S,{'DEFAULT',ValueList},CType,NameList) when is_list(ValueList) ->
@@ -3097,8 +3101,7 @@ normalize_s_of(SorS,S,Value,Type,NameList) when is_list(Value) ->
List when is_list(List) ->
List;
_ ->
- io:format("WARNING: ~p could not handle value ~p~n",
- [SorS,Value]),
+ asn1ct:warning("~p could not handle value ~p~n",[SorS,Value],S),
Value
end;
normalize_s_of(SorS,S,Value,Type,NameList)
@@ -3150,15 +3153,13 @@ get_normalized_value(S,Val,Type,Func,AddArg) ->
V2 = sort_val_if_set(AddArg,V,Type),
call_Func(update_state(S,ExtM),V2,Type,Func,AddArg);
{error,_} ->
- io:format("WARNING: default value not "
- "comparable ~p~n",[Val]),
+ asn1ct:warning("default value not comparable ~p~n",[Val],S),
Val;
{ExtM,NewVal} ->
V2 = sort_val_if_set(AddArg,NewVal,Type),
call_Func(update_state(S,ExtM),V2,Type,Func,AddArg);
_ ->
- io:format("WARNING: default value not "
- "comparable ~p~n",[Val]),
+ asn1ct:warning("default value not comparable ~p~n",[Val],S),
Val
end.
@@ -3227,7 +3228,7 @@ check_ptype(_S,_PTDef,Ts) when is_record(Ts,objectclass) ->
% check_type(S,Type,ObjSpec={{objectclassname,_},_}) ->
-% check_class(S,ObjSpec);
+ % check_class(S,ObjSpec);
check_type(_S,Type,Ts) when is_record(Type,typedef),
(Type#typedef.checked==true) ->
Ts;
@@ -3357,6 +3358,7 @@ check_type(S=#state{recordtopname=TopName},Type,Ts) when is_record(Ts,type) ->
merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
'REAL' ->
check_real(S,Constr),
+
TempNewDef#newt{tag=merge_tags(Tag,?TAG_PRIMITIVE(?N_REAL))};
{'BIT STRING',NamedNumberList} ->
NewL = check_bitstring(S,NamedNumberList,Constr),
@@ -4106,7 +4108,7 @@ resolve_namednumber(S,#typedef{typespec=Type},Name) ->
case Type#type.def of
{'ENUMERATED',NameList} ->
NamedNumberList=check_enumerated(S,NameList,Type#type.constraint),
- N = normalize_enumerated(Name,NamedNumberList),
+ N = normalize_enumerated(S,Name,NamedNumberList),
{value,{_,V}} = lists:keysearch(N,1,NamedNumberList),
V;
{'INTEGER',NameList} ->
@@ -5522,24 +5524,9 @@ check_sequence(S,Type,Comps) ->
Components2 = maybe_automatic_tags(S,Components),
%% check the table constraints from here. The outermost type
%% is Type, the innermost is Comps (the list of components)
- NewComps =
- case check_each_component(S,Type,Components2) of
- NewComponents when is_list(NewComponents) ->
- check_unique_sequence_tags(S,NewComponents),
- NewComponents;
- Ret = {NewComponents,NewEcomps} ->
- TagComps = NewComponents ++
- [Comp#'ComponentType'{prop='OPTIONAL'}|| Comp <- NewEcomps],
- %% extension components are like optionals when it comes to tagging
- check_unique_sequence_tags(S,TagComps),
- Ret;
- Ret = {Root1,NewE,Root2} ->
- TagComps = Root1 ++ [Comp#'ComponentType'{prop='OPTIONAL'}|| Comp <- NewE]++Root2,
- %% This is not correct handling if Extension
- %% contains ExtensionAdditionGroups
- check_unique_sequence_tags(S,TagComps),
- Ret
- end,
+ NewComps = check_each_component2(S,Type,Components2),
+ check_unique_sequence_tags(S,NewComps),
+
%% CRelInf is the "leading attribute" information
%% necessary for code generating of the look up in the
%% object set table,
@@ -5553,12 +5540,45 @@ check_sequence(S,Type,Comps) ->
%% the involved class removed, as the class of the object
%% set.
CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps2),
-
- {CRelInf,CompListWithTblInf};
+ %% If encoding rule is in the PER family the Root Components
+ %% after the second extension mark should be encoded before
+ %% all extensions i.e together with the first Root components
+
+ NewComps3 = textual_order(CompListWithTblInf),
+ CompListTuple =
+ complist_as_tuple(is_erule_per(S#state.erule),NewComps3),
+ {CRelInf,CompListTuple};
Dupl ->
- throw({error,{asn1,{duplicate_components,Dupl}}})
+ throw({error,{asn1,{duplicate_components,Dupl}}})
end.
+complist_as_tuple(Per,CompList) ->
+ complist_as_tuple(Per,CompList,[],[],[],root).
+
+complist_as_tuple(Per,[#'EXTENSIONMARK'{}|T],Acc,Ext,Acc2,root) ->
+ complist_as_tuple(Per,T,Acc,Ext,Acc2,ext);
+complist_as_tuple(Per,[#'EXTENSIONMARK'{}|T],Acc,Ext,Acc2,ext) ->
+ complist_as_tuple(Per,T,Acc,Ext,Acc2,root2);
+complist_as_tuple(_Per,[#'EXTENSIONMARK'{}|_T],_Acc,_Ext,_Acc2,root2) ->
+ throw({error,{asn1,{too_many_extension_marks}}});
+complist_as_tuple(Per,[C|T],Acc,Ext,Acc2,root) ->
+ complist_as_tuple(Per,T,[C|Acc],Ext,Acc2,root);
+complist_as_tuple(Per,[C|T],Acc,Ext,Acc2,ext) ->
+ complist_as_tuple(Per,T,Acc,[C|Ext],Acc2,ext);
+complist_as_tuple(Per,[C|T],Acc,Ext,Acc2,root2) ->
+ complist_as_tuple(Per,T,Acc,Ext,[C|Acc2],root2);
+complist_as_tuple(_Per,[],Acc,_Ext,_Acc2,root) ->
+ lists:reverse(Acc);
+complist_as_tuple(_Per,[],Acc,Ext,_Acc2,ext) ->
+ {lists:reverse(Acc),lists:reverse(Ext)};
+%%complist_as_tuple(_Per = true,[],Acc,Ext,Acc2,root2) ->
+%% {lists:reverse(Acc)++lists:reverse(Acc2),lists:reverse(Ext)};
+complist_as_tuple(_Per,[],Acc,Ext,Acc2,root2) ->
+ {lists:reverse(Acc),lists:reverse(Ext),lists:reverse(Acc2)}.
+
+is_erule_per(Erule) ->
+ lists:member(Erule,[per,per_bin,uper_bin]).
+
expand_components(S, [{'COMPONENTS OF',Type}|T]) ->
CompList = expand_components2(S,get_referenced_type(S,Type#type.def)),
expand_components(S,CompList) ++ expand_components(S,T);
@@ -5601,13 +5621,26 @@ take_only_rootset([#'EXTENSIONMARK'{}|_T])->
take_only_rootset([H|T]) ->
[H|take_only_rootset(T)].
-check_unique_sequence_tags(S,[#'ComponentType'{prop=mandatory}|Rest]) ->
- check_unique_sequence_tags(S,Rest);
-check_unique_sequence_tags(S,[C|Rest]) when is_record(C,'ComponentType') ->
+check_unique_sequence_tags(S,CompList) ->
+ TagComps = case complist_as_tuple(false,CompList) of
+ {R1,Ext,R2} ->
+ R1 ++ [C#'ComponentType'{prop='OPTIONAL'}||
+ C = #'ComponentType'{} <- Ext]++R2;
+ {R1,Ext} ->
+ R1 ++ [C#'ComponentType'{prop='OPTIONAL'}||
+ C = #'ComponentType'{} <- Ext];
+ _ ->
+ CompList
+ end,
+ check_unique_sequence_tags0(S,TagComps).
+
+check_unique_sequence_tags0(S,[#'ComponentType'{prop=mandatory}|Rest]) ->
+ check_unique_sequence_tags0(S,Rest);
+check_unique_sequence_tags0(S,[C=#'ComponentType'{}|Rest]) ->
check_unique_sequence_tags1(S,Rest,[C]);% optional or default
-check_unique_sequence_tags(S,[_ExtensionMarker|Rest]) ->
- check_unique_sequence_tags(S,Rest);
-check_unique_sequence_tags(_S,[]) ->
+check_unique_sequence_tags0(S,[_ExtensionMarker|Rest]) ->
+ check_unique_sequence_tags0(S,Rest);
+check_unique_sequence_tags0(_S,[]) ->
true.
check_unique_sequence_tags1(S,[C|Rest],Acc) when is_record(C,'ComponentType') ->
@@ -5676,9 +5709,9 @@ sort_components(der,S=#state{tname=TypeName},Components) ->
end,
case {untagged_choice(S,CompsList),Ext} of
{false,noext} ->
- {true,sort_components1(TypeName,CompsList,[],[],[],[])};
+ {true,sort_components1(S,TypeName,CompsList,[],[],[],[])};
{false,_} ->
- {true,{sort_components1(TypeName,CompsList,[],[],[],[]), []}};
+ {true,{sort_components1(S,TypeName,CompsList,[],[],[],[]), []}};
{true,noext} ->
%% sort in run-time
{dynamic,R1};
@@ -5690,57 +5723,57 @@ sort_components(per,S=#state{tname=TypeName},Components) ->
Root = tag_untagged_choice(S,R1++R2),
case Ext of
noext ->
- {true,sort_components1(TypeName,Root,[],[],[],[])};
+ {true,sort_components1(S,TypeName,Root,[],[],[],[])};
_ ->
- {true,{sort_components1(TypeName,Root,[],[],[],[]),
+ {true,{sort_components1(S,TypeName,Root,[],[],[],[]),
Ext}}
end.
-sort_components1(TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs],
+sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs],
UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
- sort_components1(TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc);
-sort_components1(TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs],
+ sort_components1(S,TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc);
+sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs],
UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
- sort_components1(TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc);
-sort_components1(TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs],
+ sort_components1(S,TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc);
+sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs],
UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
- sort_components1(TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc);
-sort_components1(TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs],
+ sort_components1(S,TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc);
+sort_components1(S,TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs],
UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
- sort_components1(TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]);
-sort_components1(TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(S,TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]);
+sort_components1(S,TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
I = #'ComponentType'.tags,
- ascending_order_check(TypeName,sort_universal_type(UnivAcc)) ++
- ascending_order_check(TypeName,lists:keysort(I,ApplAcc)) ++
- ascending_order_check(TypeName,lists:keysort(I,ContAcc)) ++
- ascending_order_check(TypeName,lists:keysort(I,PrivAcc)).
+ ascending_order_check(S,TypeName,sort_universal_type(UnivAcc)) ++
+ ascending_order_check(S,TypeName,lists:keysort(I,ApplAcc)) ++
+ ascending_order_check(S,TypeName,lists:keysort(I,ContAcc)) ++
+ ascending_order_check(S,TypeName,lists:keysort(I,PrivAcc)).
-ascending_order_check(TypeName,Components) ->
- ascending_order_check1(TypeName,Components),
+ascending_order_check(S,TypeName,Components) ->
+ ascending_order_check1(S,TypeName,Components),
Components.
-ascending_order_check1(TypeName,
+ascending_order_check1(S,TypeName,
[C1 = #'ComponentType'{tags=[{_,T}|_]},
C2 = #'ComponentType'{tags=[{_,T}|_]}|Rest]) ->
- io:format("WARNING: Indistinct tag ~p in SET ~p, components ~p and ~p~n",
- [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name]),
- ascending_order_check1(TypeName,[C2|Rest]);
-ascending_order_check1(TypeName,
+ asn1ct:warning("Indistinct tag ~p in SET ~p, components ~p and ~p~n",
+ [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name],S),
+ ascending_order_check1(S,TypeName,[C2|Rest]);
+ascending_order_check1(S,TypeName,
[C1 = #'ComponentType'{tags=[{'UNIVERSAL',T1}|_]},
C2 = #'ComponentType'{tags=[{'UNIVERSAL',T2}|_]}|Rest]) ->
case (decode_type(T1) == decode_type(T2)) of
true ->
- io:format("WARNING: Indistinct tags ~p and ~p in"
+ asn1ct:warning("Indistinct tags ~p and ~p in"
" SET ~p, components ~p and ~p~n",
[T1,T2,TypeName,C1#'ComponentType'.name,
- C2#'ComponentType'.name]),
- ascending_order_check1(TypeName,[C2|Rest]);
+ C2#'ComponentType'.name],S),
+ ascending_order_check1(S,TypeName,[C2|Rest]);
_ ->
- ascending_order_check1(TypeName,[C2|Rest])
+ ascending_order_check1(S,TypeName,[C2|Rest])
end;
-ascending_order_check1(N,[_|Rest]) ->
- ascending_order_check1(N,Rest);
-ascending_order_check1(_,[]) ->
+ascending_order_check1(S,N,[_|Rest]) ->
+ ascending_order_check1(S,N,Rest);
+ascending_order_check1(_,_,[]) ->
ok.
sort_universal_type(Components) ->
@@ -5807,8 +5840,10 @@ get_least_tag(TagList) ->
%% adds the textual order to the components to keep right order of
%% components in the asn1-value.
textual_order(Cs) ->
- Fun = fun(C,Index) ->
- {C#'ComponentType'{textual_order=Index},Index+1}
+ Fun = fun(C=#'ComponentType'{},Index) ->
+ {C#'ComponentType'{textual_order=Index},Index+1};
+ (Other,Index) ->
+ {Other,Index}
end,
{NewCs,_} = textual_order(Cs,Fun,1),
NewCs.
@@ -5879,7 +5914,6 @@ check_selectiontype2(S,Name,TypeDef) ->
error({type,Msg,S})
end.
-
check_restrictedstring(_S,_Def,_Constr) ->
ok.
@@ -5899,16 +5933,16 @@ check_choice(S,Type,Components) when is_list(Components) ->
[] ->
%% sort_canonical(Components),
Components2 = maybe_automatic_tags(S,Components),
- %NewComps =
- case check_each_alternative(S,Type,Components2) of
- {NewComponents,NewEcomps} ->
- check_unique_tags(S,NewComponents ++ NewEcomps),
- {NewComponents,NewEcomps};
- NewComponents ->
- check_unique_tags(S,NewComponents),
- NewComponents
- end;
-
+ NewComps = check_each_alternative2(S,Type,Components2),
+ %% ExtensionAdditionGroup markers i.e '[[' ']]' are not
+ %% significant for encoding/decoding a choice
+ %% therefore we remove them here
+ NewComps2 = lists:filter(fun(#'ExtensionAdditionGroup'{}) -> false;
+ ('ExtensionAdditionGroupEnd') -> false;
+ (_) -> true
+ end,NewComps),
+ check_unique_tags(S,NewComps2),
+ complist_as_tuple(is_erule_per(S#state.erule),NewComps2);
Dupl ->
throw({error,{asn1,{duplicate_choice_alternatives,Dupl}}})
end;
@@ -6036,12 +6070,17 @@ check_unique2([_|T],Pos,Acc) ->
check_unique2([],_,Acc) ->
lists:reverse(Acc).
-check_each_component(S,Type,Components) ->
- check_each_component(S,Type,Components,[],[],[],root1).
-check_each_component(S = #state{abscomppath=Path,recordtopname=TopName},Type,
- [C|Ct],Acc,Extacc,Acc2,Ext) when is_record(C,'ComponentType') ->
- #'ComponentType'{name=Cname,typespec=Ts,prop=Prop} = C,
+%% Replaces check_each_component and does the same work except that
+%% it keeps the complist as a flat list and does not create a tuple with root and
+%% extensions separated
+check_each_component2(S,Type,Components) ->
+ check_each_component2(S,Type,Components,[]).
+
+check_each_component2(S = #state{abscomppath=Path,recordtopname=TopName},
+ Type,
+ [C = #'ComponentType'{name=Cname,typespec=Ts,prop=Prop}|Ct],
+ Acc) ->
NewAbsCPath =
case Ts#type.def of
#'Externaltypereference'{} -> [];
@@ -6058,75 +6097,48 @@ check_each_component(S = #state{abscomppath=Path,recordtopname=TopName},Type,
DefaultValue -> {'DEFAULT',DefaultValue}
end,
NewC = C#'ComponentType'{typespec=CheckedTs,prop=NewProp,tags=NewTags},
- case Ext of
- root1 ->
- check_each_component(S,Type,Ct,[NewC|Acc],Extacc,Acc2,Ext);
- ext ->
- check_each_component(S,Type,Ct,Acc,[NewC|Extacc],Acc2,Ext);
- root2 ->
- check_each_component(S,Type,Ct,Acc,Extacc,[NewC|Acc2],Ext)
- end;
-check_each_component(S,Type,[_|Ct],Acc,Extacc,Acc2,root1) -> % skip 'EXTENSIONMARK'
- check_each_component(S,Type,Ct,Acc,Extacc,Acc2,ext);
-check_each_component(S,Type,[_|Ct],Acc,Extacc,Acc2,ext) -> % skip 'EXTENSIONMARK'
- check_each_component(S,Type,Ct,Acc,Extacc,Acc2,root2);
-check_each_component(_S,_,[_C|_Ct],_,_,_,root2) -> % 'EXTENSIONMARK'
- throw({error,{asn1,{too_many_extension_marks}}});
-check_each_component(_S,_,[],Acc,Extacc,_,ext) ->
- {lists:reverse(Acc),lists:reverse(Extacc)};
-check_each_component(_S,_,[],Acc1,ExtAcc,Acc2,root2) ->
- {lists:reverse(Acc1),lists:reverse(ExtAcc),lists:reverse(Acc2)};
-check_each_component(_S,_,[],Acc,_,_,root1) ->
+ check_each_component2(S,Type,Ct,[NewC|Acc]);
+
+check_each_component2(S,Type,[OtherMarker|Ct],Acc) ->
+ %% let 'EXTENSIONMARK' and 'ExtensionAdditionGroup' markers pass through as is
+ check_each_component2(S,Type,Ct,[OtherMarker|Acc]);
+check_each_component2(_S,_,[],Acc) ->
lists:reverse(Acc).
-%% check_each_alternative(S,Type,{Rlist,ExtList}) ->
+
+%% check_each_alternative2(S,Type,{Rlist,ExtList}) ->
%% {check_each_alternative(S,Type,Rlist),
%% check_each_alternative(S,Type,ExtList)};
-check_each_alternative(S,Type,[C|Ct]) ->
- check_each_alternative(S,Type,[C|Ct],[],[],noext).
+check_each_alternative2(S,Type,[C|Ct]) ->
+ check_each_alternative2(S,Type,[C|Ct],[]).
-check_each_alternative(S=#state{abscomppath=Path,recordtopname=TopName},Type,[C|Ct],
- Acc,Extacc,Ext) when is_record(C,'ComponentType') ->
- #'ComponentType'{name=Cname,typespec=Ts,prop=_Prop} = C,
+check_each_alternative2(S=#state{abscomppath=Path,recordtopname=TopName},
+ Type,
+ [C = #'ComponentType'{name=Cname,typespec=Ts}|Ct],
+ Acc) ->
NewAbsCPath =
case Ts#type.def of
#'Externaltypereference'{} -> [];
_ -> [Cname|Path]
end,
- NewState =
- S#state{abscomppath=NewAbsCPath,recordtopname=[Cname|TopName]},
- CheckedTs = check_type(NewState,Type,Ts),
+ CheckedTs = check_type(S#state{abscomppath=NewAbsCPath,
+ recordtopname=[Cname|TopName]},Type,Ts),
NewTags = get_taglist(S,CheckedTs),
+
NewC = C#'ComponentType'{typespec=CheckedTs,tags=NewTags},
- case Ext of
- noext ->
- check_each_alternative(S,Type,Ct,[NewC|Acc],Extacc,Ext);
- ext ->
- check_each_alternative(S,Type,Ct,Acc,[NewC|Extacc],Ext)
- end;
+ check_each_alternative2(S,Type,Ct,[NewC|Acc]);
-check_each_alternative(S,Type,[_|Ct],Acc,Extacc,noext) -> % skip 'EXTENSIONMARK'
- check_each_alternative(S,Type,Ct,Acc,Extacc,ext);
-check_each_alternative(_S,_,[_C|_Ct],_,_,ext) -> % skip 'EXTENSIONMARK'
- throw({error,{asn1,{too_many_extension_marks}}});
-check_each_alternative(_S,_,[],Acc,Extacc,ext) ->
- {lists:reverse(Acc),lists:reverse(Extacc)};
-check_each_alternative(_S,_,[],Acc,_,noext) ->
+check_each_alternative2(S,Type,[OtherMarker|Ct],Acc) ->
+ %% let 'EXTENSIONMARK' and 'ExtensionAdditionGroup' markers pass through as is
+ check_each_alternative2(S,Type,Ct,[OtherMarker|Acc]);
+check_each_alternative2(_S,_,[],Acc) ->
lists:reverse(Acc).
+
%% componentrelation_leadingattr/2 searches the structure for table
%% constraints, if any is found componentrelation_leadingattr/5 is
%% called.
componentrelation_leadingattr(S,CompList) ->
- Cs =
- case CompList of
- {Comp1, EComps, Comp2} ->
- Comp1++EComps++Comp2;
- {Components,EComponents} when is_list(Components) ->
- Components ++ EComponents;
- CompList when is_list(CompList) ->
- CompList
- end,
%% get_simple_table_if_used/2 should find out whether there are any
%% component relation constraints in the entire tree of Cs1 that
@@ -6135,12 +6147,22 @@ componentrelation_leadingattr(S,CompList) ->
%% componentrelation_leadingattr/6. The step when the leading
%% attribute and the syntax tree is modified to support the code
%% generating.
- case get_simple_table_if_used(S,Cs) of
+ case get_simple_table_if_used(S,CompList) of
[] -> {false,CompList};
- STList ->
- componentrelation_leadingattr(S,Cs,Cs,STList,[],[])
+ _ ->
+ componentrelation_leadingattr(S,CompList,CompList,[],[])
end.
+
+%%FIXME expand_ExtAddGroups([C#'ExtensionAdditionGroup'{components=ExtAdds}|T],
+%% CurrPos,PosAcc,CompAcc) ->
+%% expand_ExtAddGroups(T,CurrPos+ L = lenght(ExtAdds),[{CurrPos,L}|PosAcc],ExtAdds++CompAcc);
+%% expand_ExtAddGroups([C|T],CurrPos,PosAcc,CompAcc) ->
+%% expand_ExtAddGroups(T,CurrPos+ 1,PosAcc,[C|CompAcc]);
+%% expand_ExtAddGroups([],_CurrPos,PosAcc,CompAcc) ->
+%% {lists:reverse(PosAcc),lists:reverse(CompAcc)}.
+
+
%% componentrelation_leadingattr/6 when all components are searched
%% the new modified components are returned together with the "leading
%% attribute" information, which later is stored in the tablecinf
@@ -6150,11 +6172,11 @@ componentrelation_leadingattr(S,CompList) ->
%% is used in code generating phase too, to recognice the proper
%% components for "open type" encoding and to propagate the result of
%% the object set lookup when needed.
-componentrelation_leadingattr(_,[],_CompList,_,[],NewCompList) ->
+componentrelation_leadingattr(_,[],_CompList,[],NewCompList) ->
{false,lists:reverse(NewCompList)};
-componentrelation_leadingattr(_,[],_CompList,_,LeadingAttr,NewCompList) ->
+componentrelation_leadingattr(_,[],_CompList,LeadingAttr,NewCompList) ->
{lists:last(LeadingAttr),lists:reverse(NewCompList)}; %send all info in Ts later
-componentrelation_leadingattr(S,[C|Cs],CompList,STList,Acc,CompAcc) ->
+componentrelation_leadingattr(S,[C= #'ComponentType'{}|Cs],CompList,Acc,CompAcc) ->
{LAAcc,NewC} =
case catch componentrelation1(S,C#'ComponentType'.typespec,
[C#'ComponentType'.name]) of
@@ -6205,8 +6227,11 @@ componentrelation_leadingattr(S,[C|Cs],CompList,STList,Acc,CompAcc) ->
%% no constraint was found
{[],C}
end,
- componentrelation_leadingattr(S,Cs,CompList,STList,LAAcc++Acc,
- [NewC|CompAcc]).
+ componentrelation_leadingattr(S,Cs,CompList,LAAcc++Acc,
+ [NewC|CompAcc]);
+componentrelation_leadingattr(S,[NotComponentType|Cs],CompList,LeadingAttr,NewCompList) ->
+ componentrelation_leadingattr(S,Cs,CompList,LeadingAttr,[NotComponentType|NewCompList]).
+
object_set_mod_name(_S,ObjSet) when is_atom(ObjSet) ->
ObjSet;
@@ -6228,11 +6253,9 @@ object_set_mod_name(S,#'Externaltypereference'{module=M,type=T}) ->
%% generation of the look up functionality in the object set table are
%% returned.
get_simple_table_if_used(S,Cs) ->
- CNames = lists:map(fun(#'ComponentType'{name=Name}) -> Name;
- (_) -> [] %% in case of extension marks
- end,
- Cs),
- RefedSimpleTable=any_component_relation(S,Cs,CNames,[],[]),
+ CNames = [Name||#'ComponentType'{name=Name}<-Cs],
+ JustComponents = [C || C = #'ComponentType'{}<-Cs],
+ RefedSimpleTable=any_component_relation(S,JustComponents,CNames,[],[]),
get_simple_table_info(S,Cs,remove_doubles(RefedSimpleTable)).
remove_doubles(L) ->
@@ -6336,9 +6359,7 @@ simple_table_info(S,Type,_) ->
%% beginning of the search. CNames holds the names of all components
%% of the start level, this info is used if an outermost at-notation
%% is found to check the validity of the at-list.
-any_component_relation(S,[C|Cs],CNames,NamePath,Acc) ->
- CName = C#'ComponentType'.name,
- Type = C#'ComponentType'.typespec,
+any_component_relation(S,[#'ComponentType'{name=CName,typespec=Type}|Cs],CNames,NamePath,Acc) ->
CRelPath =
case constraint_member(componentrelation,Type#type.constraint) of
%% [{componentrelation,_,AtNotation}] ->
@@ -6358,9 +6379,9 @@ any_component_relation(S,[C|Cs],CNames,NamePath,Acc) ->
case {Type#type.inlined,
asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def))} of
{no,{constructed,bif}} ->
+
{InnerCs,NewNamePath} =
case get_components(Type#type.def) of
- {IC1,_IC2} -> {IC1 ++ IC1,[CName|NamePath]};
T when is_record(T,type) -> {T,NamePath};
IC -> {IC,[CName|NamePath]}
end,
@@ -6384,16 +6405,18 @@ any_component_relation(S,Type,CNames,NamePath,Acc) when is_record(Type,type) ->
case {Type#type.inlined,
asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def))} of
{no,{constructed,bif}} ->
- InnerCs =
- case get_components(Type#type.def) of
- {IC1,_IC2} -> IC1 ++ IC1;
- IC -> IC
- end,
+ InnerCs = get_components(Type#type.def),
any_component_relation(S,InnerCs,CNames,NamePath,[]);
_ ->
[]
end,
InnerAcc ++ CRelPath ++ Acc;
+%% Just skip the markers for ExtensionAdditionGroup start and end
+%% in this function
+any_component_relation(S,[#'ExtensionAdditionGroup'{}|Cs],CNames,NamePath,Acc) ->
+ any_component_relation(S,Cs,CNames,NamePath,Acc);
+any_component_relation(S,['ExtensionAdditionGroupEnd'|Cs],CNames,NamePath,Acc) ->
+ any_component_relation(S,Cs,CNames,NamePath,Acc);
any_component_relation(_,[],_,_,Acc) ->
Acc.
@@ -6456,11 +6479,11 @@ get_components(Def) ->
get_components(any,Def).
get_components(_,#'SEQUENCE'{components=Cs}) ->
- Cs;
+ tuple2complist(Cs);
get_components(_,#'SET'{components=Cs}) ->
- Cs;
+ tuple2complist(Cs);
get_components(_,{'CHOICE',Cs}) ->
- Cs;
+ tuple2complist(Cs);
%do not step in inlined structures
get_components(any,{'SEQUENCE OF',T = #type{def=_Def,inlined=no}}) ->
% get_components(any,Def);
@@ -6471,6 +6494,13 @@ get_components(any,{'SET OF',T = #type{def=_Def,inlined=no}}) ->
get_components(_,_) ->
[].
+tuple2complist({R,E}) ->
+ R ++ E;
+tuple2complist({R1,E,R2}) ->
+ R1 ++ E ++ R2;
+tuple2complist(List) when is_list(List) ->
+ List.
+
get_choice_components(_S,{'CHOICE',Components}) when is_list(Components)->
Components;
get_choice_components(_S,{'CHOICE',{C1,C2}}) when is_list(C1),is_list(C2) ->
@@ -6731,8 +6761,7 @@ get_tableconstraint_info(S,Type,CheckedTs) ->
get_tableconstraint_info(_S,_Type,[],Acc) ->
lists:reverse(Acc);
-get_tableconstraint_info(S,Type,[C|Cs],Acc) ->
- CheckedTs = C#'ComponentType'.typespec,
+get_tableconstraint_info(S,Type,[C=#'ComponentType'{typespec=CheckedTs}|Cs],Acc) ->
AccComp =
case CheckedTs#type.def of
%% ObjectClassFieldType
@@ -6768,7 +6797,9 @@ get_tableconstraint_info(S,Type,[C|Cs],Acc) ->
_ ->
C
end,
- get_tableconstraint_info(S,Type,Cs,[AccComp|Acc]).
+ get_tableconstraint_info(S,Type,Cs,[AccComp|Acc]);
+get_tableconstraint_info(S,Type,[C|Cs],Acc) ->
+ get_tableconstraint_info(S,Type,Cs,[C|Acc]).
get_referenced_fieldname([{_,FirstFieldname}]) ->
{FirstFieldname,[]};
@@ -6850,7 +6881,9 @@ get_taglist(S,Type) when is_record(Type,type) ->
[asn1ct_gen:def_to_tag(Tag)]
end;
get_taglist(S,{'CHOICE',{Rc,Ec}}) ->
- get_taglist(S,{'CHOICE',Rc ++ Ec});
+ get_taglist1(S,Rc ++ Ec);
+get_taglist(S,{'CHOICE',{R1,E,R2}}) ->
+ get_taglist1(S,R1 ++ E ++ R2);
get_taglist(S,{'CHOICE',Components}) ->
get_taglist1(S,Components);
%% ObjectClassFieldType OTP-4390
diff --git a/lib/asn1/src/asn1ct_constructed_ber.erl b/lib/asn1/src/asn1ct_constructed_ber.erl
index 51a241ffbd..77b78dcac7 100644
--- a/lib/asn1/src/asn1ct_constructed_ber.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -71,13 +71,15 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
ok
end,
- {SeqOrSet,TableConsInfo,CompList} =
+ {SeqOrSet,TableConsInfo,CompList0} =
case D#type.def of
#'SEQUENCE'{tablecinf=TCI,components=CL} ->
{'SEQUENCE',TCI,CL};
#'SET'{tablecinf=TCI,components=CL} ->
{'SET',TCI,CL}
end,
+ %% filter away extensionAdditiongroup markers
+ CompList = filter_complist(CompList0),
Ext = extensible(CompList),
CompList1 = case CompList of
{Rl1,El,Rl2} -> Rl1 ++ El ++ Rl2;
@@ -189,7 +191,11 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:start(),
asn1ct_name:new(tag),
- #'SEQUENCE'{tablecinf=TableConsInfo,components=CList} = D#type.def,
+ #'SEQUENCE'{tablecinf=TableConsInfo,components=CList0} = D#type.def,
+
+ %% filter away extensionAdditiongroup markers
+ CList = filter_complist(CList0),
+
Ext = extensible(CList),
{CompList,CompList2} = case CList of
{Rl1,El,Rl2} -> {Rl1 ++ El ++ Rl2,CList};
@@ -369,7 +375,10 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:clear(),
asn1ct_name:new(term),
asn1ct_name:new(tag),
- #'SET'{components=TCompList} = D#type.def,
+ #'SET'{components=TCompList0} = D#type.def,
+
+ %% filter away extensionAdditiongroup markers
+ TCompList = filter_complist(TCompList0),
Ext = extensible(TCompList),
ToOptional = fun(mandatory) ->
'OPTIONAL';
@@ -1473,6 +1482,22 @@ extensible({RootList,ExtList}) ->
{ext,length(RootList)+1,length(ExtList)};
extensible({_Rl1,_ExtL,_Rl2}) ->
extensible.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% filter away ExtensionAdditionGroup start and end marks since these
+%% have no significance for the BER encoding
+%%
+filter_complist(CompList) when is_list(CompList) ->
+ lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
+ false;
+ ('ExtensionAdditionGroupEnd') ->
+ false;
+ (_) ->
+ true
+ end, CompList);
+filter_complist({Root,Ext}) ->
+ {Root,filter_complist(Ext)};
+filter_complist({Root1,Ext,Root2}) ->
+ {Root1,filter_complist(Ext),Root2}.
print_attribute_comment(InnerType,Pos,Prop) ->
CommentLine = "%%-------------------------------------------------",
diff --git a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
index a55ac9db8e..e3be914af4 100644
--- a/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_constructed_ber_bin_v2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -75,13 +75,15 @@ gen_encode_sequence(Erules,Typename,D) when is_record(D,type) ->
"Val"
end,
- {SeqOrSet,TableConsInfo,CompList} =
+ {SeqOrSet,TableConsInfo,CompList0} =
case D#type.def of
#'SEQUENCE'{tablecinf=TCI,components=CL} ->
{'SEQUENCE',TCI,CL};
#'SET'{tablecinf=TCI,components=CL} ->
{'SET',TCI,CL}
end,
+ %% filter away extensionAdditiongroup markers
+ CompList = filter_complist(CompList0),
Ext = extensible(CompList),
CompList1 = case CompList of
{Rl1,El,Rl2} -> Rl1 ++ El ++ Rl2;
@@ -183,7 +185,10 @@ gen_decode_sequence(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:start(),
asn1ct_name:clear(),
asn1ct_name:new(tag),
- #'SEQUENCE'{tablecinf=TableConsInfo,components=CList} = D#type.def,
+ #'SEQUENCE'{tablecinf=TableConsInfo,components=CList0} = D#type.def,
+
+ %% filter away extensionAdditiongroup markers
+ CList = filter_complist(CList0),
Ext = extensible(CList),
{CompList,CompList2} =
case CList of
@@ -345,7 +350,9 @@ gen_decode_set(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:clear(),
%% asn1ct_name:new(term),
asn1ct_name:new(tag),
- #'SET'{tablecinf=TableConsInfo,components=TCompList} = D#type.def,
+ #'SET'{tablecinf=TableConsInfo,components=TCompList0} = D#type.def,
+ %% filter away extensionAdditiongroup markers
+ TCompList = filter_complist(TCompList0),
Ext = extensible(TCompList),
ToOptional = fun(mandatory) ->
'OPTIONAL';
@@ -1426,6 +1433,22 @@ extensible({RootList,ExtList}) ->
{ext,length(RootList)+1,length(ExtList)};
extensible({_Rl1,_Ext,_Rl2}) ->
extensible.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% filter away ExtensionAdditionGroup start and end marks since these
+%% have no significance for the BER encoding
+%%
+filter_complist(CompList) when is_list(CompList) ->
+ lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
+ false;
+ ('ExtensionAdditionGroupEnd') ->
+ false;
+ (_) ->
+ true
+ end, CompList);
+filter_complist({Root,Ext}) ->
+ {Root,filter_complist(Ext)};
+filter_complist({Root1,Ext,Root2}) ->
+ {Root1,filter_complist(Ext),Root2}.
print_attribute_comment(InnerType,Pos,Cname,Prop) ->
diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl
index 2a1c0ebc6b..d6f23aca06 100644
--- a/lib/asn1/src/asn1ct_constructed_per.erl
+++ b/lib/asn1/src/asn1ct_constructed_per.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -46,18 +46,29 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
asn1ct_name:start(),
asn1ct_name:new(term),
asn1ct_name:new(bytes),
- {CompList,TableConsInfo} =
+ {ExtAddGroup,TmpCompList,TableConsInfo} =
case D#type.def of
- #'SEQUENCE'{tablecinf=TCI,components=CL} ->
- {CL,TCI};
+ #'SEQUENCE'{tablecinf=TCI,components=CL,extaddgroup=ExtAddGroup0} ->
+ {ExtAddGroup0,CL,TCI};
#'SET'{tablecinf=TCI,components=CL} ->
- {CL,TCI}
+ {undefined,CL,TCI}
end,
+
+ CompList = case ExtAddGroup of
+ undefined ->
+ TmpCompList;
+ _ when is_integer(ExtAddGroup) ->
+ %% This is a fake SEQUENCE representing an ExtensionAdditionGroup
+ %% Reset the textual order so we get the right
+ %% index of the components
+ [Comp#'ComponentType'{textual_order=undefined}||
+ Comp<-TmpCompList]
+ end,
case Typename of
['EXTERNAL'] ->
- emit({{var,asn1ct_name:next(val)},
+ emit({{next,val},
" = asn1rt_check:transform_to_EXTERNAL1990(",
- {var,asn1ct_name:curr(val)},"),",nl}),
+ {curr,val},"),",nl}),
asn1ct_name:new(val);
_ ->
ok
@@ -66,23 +77,40 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) ->
{[],EmptyCL} when EmptyCL == {[],[],[]};EmptyCL == {[],[]};EmptyCL == [] ->
emit(["%%Variable setting just to eliminate ",
"compiler warning for unused vars!",nl,
- "_Val = ",{var,asn1ct_name:curr(val)},",",nl]);
+ "_Val = ",{curr,val},",",nl]);
{[],_} ->
- emit([{var,asn1ct_name:next(val)}," = ?RT_PER:list_to_record("]),
+ emit([{next,val}," = ?RT_PER:list_to_record("]),
emit(["'",asn1ct_gen:list2rname(Typename),"'"]),
- emit([", ",{var,asn1ct_name:curr(val)},"),",nl]);
+ emit([", ",{curr,val},"),",nl]);
_ ->
Fixoptcall = ",Opt} = ?RT_PER:fixoptionals(",
- emit({"{",{var,asn1ct_name:next(val)},Fixoptcall,
+ emit({"{",{next,val},Fixoptcall,
{asis,Optionals},",",length(Optionals),
- ",",{var,asn1ct_name:curr(val)},"),",nl})
+ ",",{curr,val},"),",nl})
end,
asn1ct_name:new(val),
- Ext = extensible(CompList),
+ Ext = extensible_enc(CompList),
case Ext of
{ext,_,NumExt} when NumExt > 0 ->
- emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},
- ", ",{curr,val},"),",nl]);
+ case extgroup_pos_and_length(CompList) of
+ {extgrouppos,ExtGroupPos,ExtGroupLen} ->
+ Elements = make_elements(ExtGroupPos+1,
+ "Val1",lists:seq(1,ExtGroupLen)),
+ emit([
+ {next,val}," = case [X || X <- [",Elements,
+ "],X =/= asn1_NOVALUE] of",nl,
+ "[] -> ",{curr,val},";",nl,
+ "_ -> setelement(",{asis,ExtGroupPos+1},",",
+ {curr,val},",",
+ "{extaddgroup,", Elements,"})",nl,
+ "end,",nl]),
+ asn1ct_name:new(val);
+ _ -> % no extensionAdditionGroup
+ ok
+ end,
+ asn1ct_name:new(tmpval),
+ emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},",",
+ {curr,val},"),",nl]);
_ -> true
end,
EncObj =
@@ -188,9 +216,10 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
#'SEQUENCE'{tablecinf=TCI,components=CL} ->
{add_textual_order(CL),TCI};
#'SET'{tablecinf=TCI,components=CL} ->
- {add_textual_order(CL),TCI}
+%% {add_textual_order(CL),TCI}
+ {CL,TCI} % the textual order is already taken care of
end,
- Ext = extensible(CompList),
+ Ext = extensible_dec(CompList),
MaybeComma1 = case Ext of
{ext,_Pos,_NumExt} ->
gen_dec_extension_value("Bytes"),
@@ -243,8 +272,9 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
{false,false,false}
end
end,
+ NewCompList = wrap_compList(CompList),
{AccTerm,AccBytes} =
- gen_dec_components_call(Erules,Typename,CompList,MaybeComma2,DecObjInf,Ext,length(Optionals)),
+ gen_dec_components_call(Erules,Typename,NewCompList,MaybeComma2,DecObjInf,Ext,length(Optionals)),
case asn1ct_name:all(term) of
[] -> emit(MaybeComma2); % no components at all
_ -> emit({com,nl})
@@ -284,30 +314,26 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) ->
emit(" {ASN11994Format,");
_ ->
emit(["{{'",RecordName,"'"]),
- mkvlist(textual_order(CompList,asn1ct_name:all(term))),
+ %% CompList is used here because we don't want
+ %% ExtensionAdditionGroups to be wrapped in SEQUENCES when
+ %% we are ordering the fields according to textual order
+ mkvlist(textual_order(to_encoding_order(CompList),asn1ct_name:all(term))),
emit("},")
end,
- emit({{var,asn1ct_name:curr(bytes)},"}"}),
+ emit({{curr,bytes},"}"}),
emit({".",nl,nl}).
textual_order([#'ComponentType'{textual_order=undefined}|_],TermList) ->
TermList;
textual_order(CompList,TermList) when is_list(CompList) ->
- TermTuple = list_to_tuple(TermList), %% ['Term1','Term2',...'TermN']
- %% OrderList is ordered by canonical order of tags
- TmpTuple = TermTuple,
- OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList],
- Fun = fun(X,{Tpl,Ix}) ->
-
- {setelement(X,Tpl,element(Ix,TermTuple)),Ix+1}
- end,
- {Ret,_} = lists:foldl(Fun,{TmpTuple,1},OrderList),
-%% io:format("TermTuple: ~p~nOrderList: ~p~nRet: ~p~n",[TermTuple,OrderList,tuple_to_list(Ret)]),
- tuple_to_list(Ret);
+ OrderList = [Ix||#'ComponentType'{textual_order=Ix} <- CompList],
+ [Term||{_,Term}<-
+ lists:sort(lists:zip(OrderList,
+ lists:sublist(TermList,length(OrderList))))];
+ %% sublist is just because Termlist can sometimes be longer than
+ %% OrderList, which it really shouldn't
textual_order({Root,Ext},TermList) ->
- textual_order(Root ++ Ext,TermList);
-textual_order({Root1,Ext,Root2},TermList) ->
- textual_order(Root1 ++ Ext ++ Root2, TermList).
+ textual_order(Root ++ Ext,TermList).
to_textual_order({Root,Ext}) ->
{to_textual_order(Root),Ext};
@@ -379,7 +405,7 @@ emit_opt_or_mand_check(Val,Term) ->
gen_encode_choice(Erule,Typename,D) when is_record(D,type) ->
{'CHOICE',CompList} = D#type.def,
emit({"[",nl}),
- Ext = extensible(CompList),
+ Ext = extensible_enc(CompList),
gen_enc_choice(Erule,Typename,CompList,Ext),
emit({nl,"].",nl}).
@@ -388,7 +414,7 @@ gen_decode_choice(Erules,Typename,D) when is_record(D,type) ->
asn1ct_name:clear(),
asn1ct_name:new(bytes),
{'CHOICE',CompList} = D#type.def,
- Ext = extensible(CompList),
+ Ext = extensible_enc(CompList),
gen_dec_choice(Erules,Typename,CompList,Ext),
emit({".",nl}).
@@ -558,12 +584,53 @@ mkvlist2([H|T]) ->
mkvlist2([]) ->
true.
-extensible(CompList) when is_list(CompList) ->
+
+extensible_dec(CompList) when is_list(CompList) ->
noext;
-extensible({RootList,ExtList}) ->
- {ext,length(RootList)+1,length(ExtList)};
-extensible({Rl1,Ext,_Rl2}) ->
- {ext,length(Rl1)+1,length(Ext)}.
+extensible_dec({RootList,ExtList}) ->
+ {ext,length(RootList)+1,ext_length(ExtList)};
+extensible_dec({Rl1,Ext,Rl2}) ->
+ {ext,length(Rl1)+length(Rl2)+1,ext_length(Ext)}.
+
+extensible_enc(CompList) when is_list(CompList) ->
+ noext;
+extensible_enc({RootList,ExtList}) ->
+ {ext,length(RootList)+1,ext_length(ExtList)};
+extensible_enc({Rl1,Ext,_Rl2}) ->
+ {ext,length(Rl1)+1,ext_length(Ext)}.
+
+ext_length(ExtList) -> ext_length(ExtList,normal,0).
+ext_length([{'ExtensionAdditionGroup',_Num}|T],_,Acc)->
+ ext_length(T,group,Acc);
+ext_length(['ExtensionAdditionGroupEnd'|T],group,Acc) ->
+ ext_length(T,normal,Acc+1);
+ext_length([#'ComponentType'{}|T],State=group,Acc) ->
+ ext_length(T,State,Acc);
+ext_length([#'ComponentType'{}|T],State=normal,Acc) ->
+ ext_length(T,State,Acc+1);
+ext_length([],_,Acc) ->
+ Acc.
+
+extgroup_pos_and_length(CompList) when is_list(CompList) ->
+ noextgroup;
+extgroup_pos_and_length({RootList,ExtList}) ->
+ extgrouppos(ExtList,length(RootList)+1);
+extgroup_pos_and_length({Rl1,Ext,_Rl2}) ->
+ extgrouppos(Ext,length(Rl1)+1).
+
+extgrouppos([{'ExtensionAdditionGroup',_Num}|T],Pos) ->
+ extgrouppos(T,Pos,0);
+extgrouppos([_|T],Pos) ->
+ extgrouppos(T,Pos+1);
+extgrouppos([],_) ->
+ noextgroup.
+
+extgrouppos(['ExtensionAdditionGroupEnd'|_T],Pos,Len) ->
+ {extgrouppos,Pos,Len};
+extgrouppos([_|T],Pos,Len) ->
+ extgrouppos(T,Pos,Len+1).
+
+
gen_dec_extension_value(_) ->
emit({"{Ext,",{next,bytes},"} = ?RT_PER:getext(",{curr,bytes},")"}),
@@ -574,7 +641,11 @@ gen_dec_extension_value(_) ->
%% there are optional components, start with 2 because first element
%% is the record name
-optionals({L1,_Ext,L2}) -> optionals(L1++L2,[],2);
+optionals({L1,Ext,L2}) ->
+ Opt1 = optionals(L1,[],2),
+ ExtComps = length([C||C = #'ComponentType'{}<-Ext]),
+ Opt2 = optionals(L2,[],2+length(L1)+ExtComps),
+ Opt1 ++ Opt2;
optionals({L,_Ext}) -> optionals(L,[],2);
optionals(L) -> optionals(L,[],2).
@@ -617,6 +688,13 @@ get_optionality_pos(TextPos,OptTable) ->
no_num
end.
+to_encoding_order(Cs) when is_list(Cs) ->
+ Cs;
+to_encoding_order(Cs = {_Root,_Ext}) ->
+ Cs;
+to_encoding_order({R1,Ext,R2}) ->
+ {R1++R2,Ext}.
+
add_textual_order(Cs) when is_list(Cs) ->
{NewCs,_} = add_textual_order1(Cs,1),
NewCs;
@@ -629,26 +707,20 @@ add_textual_order({R1,Ext,R2}) ->
{NewExt,Num2} = add_textual_order1(Ext,Num1),
{NewR2,_} = add_textual_order1(R2,Num2),
{NewR1,NewExt,NewR2}.
-add_textual_order1(Cs=[#'ComponentType'{textual_order=Int}|_],I)
- when is_integer(Int) ->
- {Cs,I};
+%%add_textual_order1(Cs=[#'ComponentType'{textual_order=Int}|_],I)
+%% when is_integer(Int) ->
+%% {Cs,I};
add_textual_order1(Cs,NumIn) ->
- lists:mapfoldl(fun(C,Num) ->
+ lists:mapfoldl(fun(C=#'ComponentType'{},Num) ->
{C#'ComponentType'{textual_order=Num},
- Num+1}
+ Num+1};
+ (OtherMarker,Num) ->
+ {OtherMarker,Num}
end,
NumIn,Cs).
gen_enc_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DynamicEnc,Ext) ->
- Rpos = gen_enc_components_call1(Erule,TopType,Root1,1,MaybeComma,DynamicEnc,noext),
- case Ext of
- {ext,_,ExtNum} when ExtNum > 0 ->
- emit([nl,
- ",Extensions",nl]);
- _ -> true
- end,
- Rpos2 = gen_enc_components_call1(Erule,TopType,ExtList,Rpos,MaybeComma,DynamicEnc,Ext),
- gen_enc_components_call1(Erule,TopType,Root2,Rpos2,MaybeComma,DynamicEnc,noext);
+ gen_enc_components_call(Erule,TopType,{Root1++Root2,ExtList},MaybeComma,DynamicEnc,Ext);
gen_enc_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,DynamicEnc,Ext) ->
%% The type has extensionmarker
Rpos = gen_enc_components_call1(Erule,TopType,CompList,1,MaybeComma,DynamicEnc,noext),
@@ -659,7 +731,8 @@ gen_enc_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,DynamicEnc,E
_ -> true
end,
%handle extensions
- gen_enc_components_call1(Erule,TopType,ExtList,Rpos,MaybeComma,DynamicEnc,Ext);
+ NewExtList = wrap_extensionAdditionGroups(ExtList),
+ gen_enc_components_call1(Erule,TopType,NewExtList,Rpos,MaybeComma,DynamicEnc,Ext);
gen_enc_components_call(Erule,TopType, CompList, MaybeComma, DynamicEnc, Ext) ->
%% The type has no extensionmarker
gen_enc_components_call1(Erule,TopType,CompList,1,MaybeComma,DynamicEnc,Ext).
@@ -706,7 +779,7 @@ gen_enc_components_call1(_Erule,_TopType,[],Pos,_,_,_) ->
Pos.
gen_enc_component_default(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext,DefaultVal) ->
- Element = make_element(Pos+1,"Val1",Cname),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
emit({"case ",Element," of",nl}),
% emit({"asn1_DEFAULT -> [];",nl}),
emit({"DFLT when DFLT == asn1_DEFAULT; DFLT == ",{asis,DefaultVal}," -> [];",nl}),
@@ -719,8 +792,27 @@ gen_enc_component_default(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext,DefaultVal
NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
gen_enc_line(Erule,TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
emit({nl,"end"}).
+
+gen_enc_component_optional(Erule,TopType,Cname,
+ Type=#type{def=#'SEQUENCE'{
+ extaddgroup=Number,
+ components=_ExtGroupCompList}},
+ Pos,DynamicEnc,Ext) when is_integer(Number) ->
+
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ emit({"case ",Element," of",nl}),
+
+ emit({"asn1_NOVALUE -> [];",nl}),
+ asn1ct_name:new(tmpval),
+ emit({{curr,tmpval}," ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ gen_enc_line(Erule,TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
+ emit({nl,"end"});
gen_enc_component_optional(Erule,TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
- Element = make_element(Pos+1,"Val1",Cname),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
emit({"case ",Element," of",nl}),
emit({"asn1_NOVALUE -> [];",nl}),
@@ -834,29 +926,7 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) ->
_ -> true
end.
gen_dec_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DecInfObj,Ext,NumberOfOptionals) ->
- %% The type has extensionmarker
- OptTable = create_optionality_table(Root1 ++ Root2),
- {Rpos,AccTerm,AccBytes} =
- gen_dec_components_call1(Erule,TopType, Root1, 1, OptTable,
- MaybeComma,DecInfObj, noext,[],[],
- NumberOfOptionals),
- emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
- emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
- asn1ct_name:new(bytes),
- {Epos,AccTermE,AccBytesE} =
- gen_dec_components_call1(Erule,TopType,ExtList,Rpos, OptTable, "",
- DecInfObj,Ext,[],[],NumberOfOptionals),
- case ExtList of
- [] -> true;
- _ -> emit([",",nl])
- end,
- emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",",
- length(ExtList)+1,",Extensions),",nl]),
- asn1ct_name:new(bytes),
- {_RPos2,AccTerm2,AccBytes2} =
- gen_dec_components_call1(Erule,TopType,Root2,Epos,OptTable,
- "",DecInfObj,noext,[],[],NumberOfOptionals),
- {AccTerm++AccTermE++AccTerm2,AccBytes++AccBytesE++AccBytes2};
+ gen_dec_components_call(Erule,TopType,{Root1++Root2,ExtList},MaybeComma,DecInfObj,Ext,NumberOfOptionals);
gen_dec_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,
DecInfObj,Ext,NumberOfOptionals) ->
%% The type has extensionmarker
@@ -868,8 +938,9 @@ gen_dec_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,
emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
asn1ct_name:new(bytes),
+ NewExtList = wrap_extensionAdditionGroups(ExtList),
{_Epos,AccTermE,AccBytesE} =
- gen_dec_components_call1(Erule,TopType,ExtList,Rpos, OptTable,
+ gen_dec_components_call1(Erule,TopType,NewExtList,Rpos, OptTable,
"",DecInfObj,Ext,[],[],NumberOfOptionals),
case ExtList of
[] -> true;
@@ -942,8 +1013,18 @@ gen_dec_components_call1(Erule,TopType,
asn1ct_name:new(tmpterm),
emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
_ ->
- asn1ct_name:new(term),
- emit({"{",{curr,term},",",{next,bytes},"} = "})
+ case Type of
+ #type{def=#'SEQUENCE'{
+ extaddgroup=Number1,
+ components=ExtGroupCompList1}} when is_integer(Number1)->
+ emit({"{{_,"}),
+ emit_extaddgroupTerms(term,ExtGroupCompList1),
+ emit({"}"});
+ _ ->
+ asn1ct_name:new(term),
+ emit({"{",{curr,term}})
+ end,
+ emit({",",{next,bytes},"} = "})
end,
case {Ext,Prop,is_optimized(Erule)} of
@@ -967,11 +1048,24 @@ gen_dec_components_call1(Erule,TopType,
{noext,mandatory} -> true; % generate nothing
{noext,_} ->
emit([";",nl,"0 ->"]),
- gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext),
+ emit(["{"]),
+ gen_dec_component_no_val(Ext,Prop),
+ emit({",",{curr,bytes},"}",nl}),
emit([nl,"end"]);
_ ->
emit([";",nl,"_ ->",nl]),
- gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext),
+ emit(["{"]),
+ case Type of
+ #type{def=#'SEQUENCE'{
+ extaddgroup=Number2,
+ components=ExtGroupCompList2}} when is_integer(Number2)->
+ emit({"{extAddGroup,"}),
+ gen_dec_extaddGroup_no_val(Ext,ExtGroupCompList2),
+ emit({"}"});
+ _ ->
+ gen_dec_component_no_val(Ext,Prop)
+ end,
+ emit({",",{curr,bytes},"}",nl}),
emit([nl,"end"])
end,
asn1ct_name:new(bytes),
@@ -988,13 +1082,22 @@ gen_dec_components_call1(Erule,TopType,
gen_dec_components_call1(_,_TopType,[],Pos,_OptTable,_,_,_,AccTerm,AccBytes,_NumberOfOptionals) ->
{Pos,AccTerm,AccBytes}.
-
-gen_dec_component_no_val(_,_,_,{'DEFAULT',DefVal},_,_) ->
- emit(["{",{asis,DefVal},",",{curr,bytes},"}",nl]);
-gen_dec_component_no_val(_,_,_,'OPTIONAL',_,_) ->
- emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl});
-gen_dec_component_no_val(_,_,_,mandatory,_,{ext,_,_}) ->
- emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl}).
+gen_dec_extaddGroup_no_val(Ext,[#'ComponentType'{prop=Prop}])->
+ gen_dec_component_no_val(Ext,Prop),
+ ok;
+gen_dec_extaddGroup_no_val(Ext,[#'ComponentType'{prop=Prop}|Rest])->
+ gen_dec_component_no_val(Ext,Prop),
+ emit({","}),
+ gen_dec_extaddGroup_no_val(Ext,Rest);
+gen_dec_extaddGroup_no_val(_, []) ->
+ ok.
+
+gen_dec_component_no_val(_,{'DEFAULT',DefVal}) ->
+ emit([{asis,DefVal}]);
+gen_dec_component_no_val(_,'OPTIONAL') ->
+ emit({"asn1_NOVALUE"});
+gen_dec_component_no_val({ext,_,_},mandatory) ->
+ emit({"asn1_NOVALUE"}).
gen_dec_line(Erule,TopType,Cname,Type,Pos,DecInfObj,Ext,Prop) ->
@@ -1192,6 +1295,14 @@ gen_enc_choice_tag({C1,C2},_,_) ->
N2 = get_name_list(C2),
emit(["?RT_PER:set_choice(element(1,Val),",
{asis,{N1,N2}},", ",{asis,{length(N1),length(N2)}},")"]);
+
+gen_enc_choice_tag({C1,C2,C3},_,_) ->
+ N1 = get_name_list(C1),
+ N2 = get_name_list(C2),
+ N3 = get_name_list(C3),
+ Root = N1 ++ N3,
+ emit(["?RT_PER:set_choice(element(1,Val),",
+ {asis,{Root,N2}},", ",{asis,{length(Root),length(N2)}},")"]);
gen_enc_choice_tag(C,_,_) ->
N = get_name_list(C),
emit(["?RT_PER:set_choice(element(1,Val),",
@@ -1208,6 +1319,8 @@ get_name_list([], Acc) ->
gen_enc_choice2(Erule,TopType, {L1,L2}, Ext) ->
gen_enc_choice2(Erule,TopType, L1 ++ L2, 0, Ext);
+gen_enc_choice2(Erule,TopType, {L1,L2,L3}, Ext) ->
+ gen_enc_choice2(Erule,TopType, L1 ++ L3 ++ L2, 0, Ext);
gen_enc_choice2(Erule,TopType, L, Ext) ->
gen_enc_choice2(Erule,TopType, L, 0, Ext).
@@ -1279,6 +1392,9 @@ gen_dec_choice1(Erule,TopType,CompList,noext) ->
gen_dec_choice1(Erule,TopType,{RootList,ExtList},Ext) ->
NewList = RootList ++ ExtList,
gen_dec_choice1(Erule,TopType, NewList, Ext);
+gen_dec_choice1(Erule,TopType,{RootList,ExtList,RootList2},Ext) ->
+ NewList = RootList ++ RootList2 ++ ExtList,
+ gen_dec_choice1(Erule,TopType, NewList, Ext);
gen_dec_choice1(Erule,TopType,CompList,{ext,ExtPos,ExtNum}) ->
emit({"{Choice,",{curr,bytes},
"} = ?RT_PER:getchoice(",{prev,bytes},",",
@@ -1347,6 +1463,18 @@ gen_encode_prim_wrapper(CtgenMod,Erule,Cont,DoTag,Value) ->
CtgenMod:gen_encode_prim(Erule,Cont,DoTag,Value).
% erase(component_type).
+make_elements(I,Val,ExtCnames) ->
+ make_elements(I,Val,ExtCnames,[]).
+
+make_elements(I,Val,[ExtCname],Acc)-> % the last one, no comma needed
+ Element = make_element(I,Val,ExtCname),
+ make_elements(I+1,Val,[],[Element|Acc]);
+make_elements(I,Val,[ExtCname|Rest],Acc)->
+ Element = make_element(I,Val,ExtCname),
+ make_elements(I+1,Val,Rest,[", ",Element|Acc]);
+make_elements(_I,_,[],Acc) ->
+ lists:reverse(Acc).
+
make_element(I,Val,Cname) ->
case tuple_notation_allowed() of
true ->
@@ -1355,6 +1483,55 @@ make_element(I,Val,Cname) ->
io_lib:format("element(~w,~s)",[I,Val])
end.
+emit_extaddgroupTerms(VarSeries,[_]) ->
+ asn1ct_name:new(VarSeries),
+ emit({curr,VarSeries}),
+ ok;
+emit_extaddgroupTerms(VarSeries,[_|Rest]) ->
+ asn1ct_name:new(VarSeries),
+ emit({{curr,VarSeries},","}),
+ emit_extaddgroupTerms(VarSeries,Rest);
+emit_extaddgroupTerms(_,[]) ->
+ ok.
+wrap_compList({Root1,Ext,Root2}) ->
+ {Root1,wrap_extensionAdditionGroups(Ext),Root2};
+wrap_compList({Root1,Ext}) ->
+ {Root1,wrap_extensionAdditionGroups(Ext)};
+wrap_compList(CompList) ->
+ CompList.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Will convert all componentTypes following 'ExtensionAdditionGroup'
+%% up to the matching 'ExtensionAdditionGroupEnd' into one componentType
+%% of type SEQUENCE with the componentTypes as components
+%%
+wrap_extensionAdditionGroups(ExtCompList) ->
+ wrap_extensionAdditionGroups(ExtCompList,[],0).
+
+wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest],Acc,0) ->
+ {ExtGroupCompList=
+ [#'ComponentType'{textual_order=TextPos}|_],
+ ['ExtensionAdditionGroupEnd'|Rest2]} =
+ lists:splitwith(fun(#'ComponentType'{}) -> true;
+ (_) -> false
+ end,
+ Rest),
+ wrap_extensionAdditionGroups(Rest2,
+ [#'ComponentType'{
+ name='ExtAddGroup', % FIXME: handles ony one ExtAddGroup
+ typespec=#type{def=#'SEQUENCE'{
+ extaddgroup=1,% FIXME: handles only one
+ components=ExtGroupCompList}},
+ textual_order = TextPos,
+ prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1);
+wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T],Acc,ExtAddGroupDiff) when is_integer(Tord) ->
+ wrap_extensionAdditionGroups(T,[H#'ComponentType'{
+ textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff);
+wrap_extensionAdditionGroups([H|T],Acc,ExtAddGroupDiff) ->
+ wrap_extensionAdditionGroups(T,[H|Acc],ExtAddGroupDiff);
+wrap_extensionAdditionGroups([],Acc,_) ->
+ lists:reverse(Acc).
+
+
tuple_notation_allowed() ->
Options = get(encoding_options),
not (lists:member(optimize,Options) orelse lists:member(uper_bin,Options)).
diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl
index eab5fb4a2a..e49829d82f 100644
--- a/lib/asn1/src/asn1ct_gen.erl
+++ b/lib/asn1/src/asn1ct_gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -87,7 +87,7 @@ pgen_module(OutFile,Erules,Module,
% gen_vars(asn1_db:mod_to_vars(Module)),
% gen_tag_table(AllTypes),
file:close(Fid),
- asn1ct:report_verbose("--~p--~n",[{generated,ErlFile}],Options).
+ asn1ct:verbose("--~p--~n",[{generated,ErlFile}],Options).
pgen_typeorval(Erules,Module,N2nConvEnums,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
@@ -536,14 +536,19 @@ gen_part_decode_funcs({primitive,bif},_TypeName,
gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}).
+
gen_types(Erules,Tname,{RootL1,ExtList,RootL2})
when is_list(RootL1), is_list(RootL2) ->
gen_types(Erules,Tname,RootL1),
- gen_types(Erules,Tname,ExtList),
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ gen_types(Erules,Tname,Rtmod:extaddgroup2sequence(ExtList)),
gen_types(Erules,Tname,RootL2);
gen_types(Erules,Tname,{RootList,ExtList}) when is_list(RootList) ->
gen_types(Erules,Tname,RootList),
- gen_types(Erules,Tname,ExtList);
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ gen_types(Erules,Tname,Rtmod:extaddgroup2sequence(ExtList));
gen_types(Erules,Tname,[{'EXTENSIONMARK',_,_}|Rest]) ->
gen_types(Erules,Tname,Rest);
gen_types(Erules,Tname,[ComponentType|Rest]) ->
@@ -1335,9 +1340,9 @@ pgen_hrl(Erules,Module,TypeOrVal,Options,_Indent) ->
Y ->
Fid = get(gen_file_out),
file:close(Fid),
- asn1ct:report_verbose("--~p--~n",
- [{generated,lists:concat([get(outfile),".hrl"])}],
- Options),
+ asn1ct:verbose("--~p--~n",
+ [{generated,lists:concat([get(outfile),".hrl"])}],
+ Options),
Y
end.
@@ -1543,19 +1548,18 @@ gen_record2(Name,SeqOrSet,Comps) ->
gen_record2(_Name,_SeqOrSet,[],_Com,_Extension) ->
true;
-gen_record2(Name,SeqOrSet,[{'EXTENSIONMARK',_,_}|T],Com,Extension) ->
- gen_record2(Name,SeqOrSet,T,Com,Extension);
-gen_record2(_Name,_SeqOrSet,[H],Com,Extension) ->
- #'ComponentType'{name=Cname} = H,
+gen_record2(_Name,_SeqOrSet,[H = #'ComponentType'{name=Cname}],Com,Extension) ->
emit(Com),
emit({asis,Cname}),
gen_record_default(H, Extension);
-gen_record2(Name,SeqOrSet,[H|T],Com, Extension) ->
- #'ComponentType'{name=Cname} = H,
+gen_record2(Name,SeqOrSet,[H = #'ComponentType'{name=Cname}|T],Com, Extension) ->
emit(Com),
emit({asis,Cname}),
gen_record_default(H, Extension),
- gen_record2(Name,SeqOrSet,T,", ", Extension).
+ gen_record2(Name,SeqOrSet,T,", ", Extension);
+gen_record2(Name,SeqOrSet,[_|T],Com,Extension) ->
+ %% skip EXTENSIONMARK, ExtensionAdditionGroup and other markers
+ gen_record2(Name,SeqOrSet,T,Com,Extension).
gen_record_default(#'ComponentType'{prop='OPTIONAL'}, _)->
emit(" = asn1_NOVALUE");
diff --git a/lib/asn1/src/asn1ct_gen_ber.erl b/lib/asn1/src/asn1ct_gen_ber.erl
index d70586c75c..491ebcb8fd 100644
--- a/lib/asn1/src/asn1ct_gen_ber.erl
+++ b/lib/asn1/src/asn1ct_gen_ber.erl
@@ -33,6 +33,7 @@
-export([gen_objectset_code/2, gen_obj_code/3]).
-export([re_wrap_erule/1]).
-export([unused_var/2]).
+-export([extaddgroup2sequence/1]).
-import(asn1ct_gen, [emit/1,demit/1]).
@@ -1734,3 +1735,15 @@ get_object_field(Name,ObjectFields) ->
{value,Field} -> Field;
false -> false
end.
+
+%% For BER the ExtensionAdditionGroup notation has no impact on the encoding/decoding
+%% and therefore we only filter away the ExtensionAdditionGroup start and end markers
+%%
+extaddgroup2sequence(ExtList) when is_list(ExtList) ->
+ lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
+ false;
+ ('ExtensionAdditionGroupEnd') ->
+ false;
+ (_) ->
+ true
+ end, ExtList).
diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
index a146e92d64..9ec458e351 100644
--- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
+++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl
@@ -33,6 +33,7 @@
-export([gen_objectset_code/2, gen_obj_code/3]).
-export([encode_tag_val/3]).
-export([gen_inc_decode/2,gen_decode_selected/3]).
+-export([extaddgroup2sequence/1]).
-import(asn1ct_gen, [emit/1,demit/1]).
@@ -1826,8 +1827,15 @@ mk_object_val(Val, Ack, Len) ->
add_func(F={_Func,_Arity}) ->
ets:insert(asn1_functab,{F}).
-
-
-
+%% For BER the ExtensionAdditionGroup notation has no impact on the encoding/decoding
+%% and therefore we only filter away the ExtensionAdditionGroup start and end markers
+extaddgroup2sequence(ExtList) when is_list(ExtList) ->
+ lists:filter(fun(#'ExtensionAdditionGroup'{}) ->
+ false;
+ ('ExtensionAdditionGroupEnd') ->
+ false;
+ (_) ->
+ true
+ end, ExtList).
diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl
index 23fb392d60..8313cf1b60 100644
--- a/lib/asn1/src/asn1ct_gen_per.erl
+++ b/lib/asn1/src/asn1ct_gen_per.erl
@@ -31,6 +31,7 @@
-export([gen_encode/2, gen_encode/3]).
-export([is_already_generated/2,more_genfields/1,get_class_fields/1,
get_object_field/2]).
+-export([extaddgroup2sequence/1]).
-import(asn1ct_gen, [emit/1,demit/1]).
@@ -1393,3 +1394,25 @@ get_object_field(Name,ObjectFields) ->
false -> false
end.
+
+%% For PER the ExtensionAdditionGroup notation has significance for the encoding and decoding
+%% the components within the ExtensionAdditionGroup is treated in a similar way as if they
+%% have been specified within a SEQUENCE, therefore we construct a fake sequence type here
+%% so that we can generate code for it
+extaddgroup2sequence(ExtList) ->
+ extaddgroup2sequence(ExtList,[]).
+
+extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],Acc) ->
+ Number = case Number0 of undefined -> 1; _ -> Number0 end,
+ {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} =
+ lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T),
+ extaddgroup2sequence(T2,[#'ComponentType'{
+ name='ExtAddGroup',
+ typespec=#type{def=#'SEQUENCE'{
+ extaddgroup=Number,
+ components=ExtGroupComps}},
+ prop='OPTIONAL'}|Acc]);
+extaddgroup2sequence([C|T],Acc) ->
+ extaddgroup2sequence(T,[C|Acc]);
+extaddgroup2sequence([],Acc) ->
+ lists:reverse(Acc).
diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
index e1feb42a59..4f4fcfafc3 100644
--- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
+++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl
@@ -29,6 +29,7 @@
-export([gen_obj_code/3,gen_objectset_code/2]).
-export([gen_decode/2, gen_decode/3]).
-export([gen_encode/2, gen_encode/3]).
+-export([extaddgroup2sequence/1]).
-import(asn1ct_gen, [emit/1,demit/1]).
-import(asn1ct_gen_per, [is_already_generated/2,more_genfields/1,
@@ -1796,3 +1797,25 @@ dec_enumerated_cases([Name|Rest],Tmpremain,No) ->
dec_enumerated_cases(Rest,Tmpremain,No+1);
dec_enumerated_cases([],_,_) ->
"".
+
+%% For PER the ExtensionAdditionGroup notation has significance for the encoding and decoding
+%% the components within the ExtensionAdditionGroup is treated in a similar way as if they
+%% have been specified within a SEQUENCE, therefore we construct a fake sequence type here
+%% so that we can generate code for it
+extaddgroup2sequence(ExtList) ->
+ extaddgroup2sequence(ExtList,[]).
+
+extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],Acc) ->
+ Number = case Number0 of undefined -> 1; _ -> Number0 end,
+ {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} =
+ lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T),
+ extaddgroup2sequence(T2,[#'ComponentType'{
+ name='ExtAddGroup',
+ typespec=#type{def=#'SEQUENCE'{
+ extaddgroup=Number,
+ components=ExtGroupComps}},
+ prop='OPTIONAL'}|Acc]);
+extaddgroup2sequence([C|T],Acc) ->
+ extaddgroup2sequence(T,[C|Acc]);
+extaddgroup2sequence([],Acc) ->
+ lists:reverse(Acc).
diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl
index 46d7ad6fdb..224a535e87 100644
--- a/lib/asn1/src/asn1ct_parser2.erl
+++ b/lib/asn1/src/asn1ct_parser2.erl
@@ -417,10 +417,22 @@ parse_BuiltinType([{'CHARACTER',_},{'STRING',_}|Rest]) ->
{#type{def='CHARACTER STRING'},Rest};
parse_BuiltinType([{'CHOICE',_},{'{',_}|Rest]) ->
- {AlternativeTypeLists,Rest2} = parse_AlternativeTypeLists(Rest,get(extensiondefault)),
+ {AlternativeTypeLists,Rest2} = parse_AlternativeTypeLists(Rest),
+ AlternativeTypeLists1 =
+ lists:filter(fun(#'ExtensionAdditionGroup'{}) -> false;
+ ('ExtensionAdditionGroupEnd') -> false;
+ (_) -> true
+ end,AlternativeTypeLists),
case Rest2 of
[{'}',_}|Rest3] ->
- {#type{def={'CHOICE',AlternativeTypeLists}},Rest3};
+ AlternativeTypeLists2 =
+ case {[Ext||Ext = #'EXTENSIONMARK'{} <- AlternativeTypeLists1],
+ get(extensiondefault)} of
+ {[],'IMPLIED'} -> AlternativeTypeLists1 ++ [#'EXTENSIONMARK'{}];
+ _ -> AlternativeTypeLists1
+ end,
+
+ {#type{def={'CHOICE',AlternativeTypeLists2}},Rest3};
_ ->
throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
[got,get_token(hd(Rest2)),expected,'}']}})
@@ -479,16 +491,19 @@ parse_BuiltinType([{'REAL',_}|Rest]) ->
{#type{def='REAL'},Rest};
parse_BuiltinType([{'RELATIVE-OID',_}|Rest]) ->
{#type{def='RELATIVE-OID'},Rest};
-parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
- {#type{def=#'SEQUENCE'{components=[{'EXTENSIONMARK',Line,undefined}]}},
+parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'}',_}|Rest]) ->
+ {#type{def=#'SEQUENCE'{components=[]}},
Rest};
+parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
+ {#type{def=#'SEQUENCE'{components=[#'EXTENSIONMARK'{pos = Line}]}},Rest};
parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'!',_}|Rest]) ->
{ExceptionIdentification,Rest2} = parse_ExceptionIdentification(Rest),
case Rest2 of
[{'}',_}|Rest3] ->
- {#type{def=#'SEQUENCE'{components=[{'EXTENSIONMARK',
- Line,
- ExceptionIdentification}]}},
+ {#type{def=#'SEQUENCE'{
+ components=[#'EXTENSIONMARK'{
+ pos = Line,
+ val = ExceptionIdentification}]}},
Rest3};
_ ->
{ComponentTypeLists,Rest3}=
@@ -537,13 +552,14 @@ parse_BuiltinType([{'SEQUENCE',_},{'OF',_}|Rest]) ->
parse_BuiltinType([{'SET',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
- {#type{def=#'SET'{components=[{'EXTENSIONMARK',Line,undefined}]}},Rest};
+ {#type{def=#'SET'{components=[#'EXTENSIONMARK'{pos = Line}]}},Rest};
parse_BuiltinType([{'SET',_},{'{',_},{'...',Line},{'!',_}|Rest]) ->
{ExceptionIdentification,Rest2} = parse_ExceptionIdentification(Rest),
case Rest2 of
[{'}',_}|Rest3] ->
{#type{def=#'SET'{components=
- [{'EXTENSIONMARK',Line,ExceptionIdentification}]}},
+ [#'EXTENSIONMARK'{pos = Line,
+ val = ExceptionIdentification}]}},
Rest3};
_ ->
throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
@@ -2323,101 +2339,197 @@ to_set(V) when is_list(V) ->
to_set(V) ->
ordsets:from_list([V]).
+parse_AlternativeTypeLists(Tokens) ->
+ parse_AlternativeTypeLists(Tokens,[]).
-parse_AlternativeTypeLists(Tokens,ExtensionDefault) ->
- {AltTypeList,Rest1} = parse_AlternativeTypeList(Tokens),
- {ExtensionAndException,Rest2} =
- case Rest1 of
- [{',',_},{'...',L1},{'!',_}|Rest12] ->
- {_,Rest13} = parse_ExceptionIdentification(Rest12),
- %% Exception info is currently thrown away
- {[#'EXTENSIONMARK'{pos=L1}],Rest13};
- [{',',_},{'...',L1}|Rest12] ->
- {[#'EXTENSIONMARK'{pos=L1}],Rest12};
- _ ->
- {[],Rest1}
- end,
- {AltTypeList2,Rest5} =
- case ExtensionAndException of
- [] ->
- {AltTypeList,Rest2};
- _ ->
- {ExtensionAddition,Rest3} =
- case Rest2 of
- [{',',_}|Rest23] ->
- parse_ExtensionAdditionAlternativeList(Rest23);
- _ ->
- {[],Rest2}
- end,
- {OptionalExtensionMarker,Rest4} =
- case Rest3 of
- [{',',_},{'...',L3}|Rest31] ->
- {[#'EXTENSIONMARK'{pos=L3}],Rest31};
- _ ->
- {[],Rest3}
- end,
- {AltTypeList ++ ExtensionAndException ++
- ExtensionAddition ++ OptionalExtensionMarker, Rest4}
- end,
- AltTypeList3 =
- case [X || X=#'EXTENSIONMARK'{} <- AltTypeList2] of
- [] when ExtensionDefault == 'IMPLIED' ->
- AltTypeList2 ++ [#'EXTENSIONMARK'{}];
- _ ->
- AltTypeList2
- end,
- {AltTypeList3,Rest5}.
-
+parse_AlternativeTypeLists(Tokens = [{identifier,_,_}|_Rest0],Clist) ->
+ {CompList,Rest1} = parse_AlternativeTypeList(Tokens,[]),
+ parse_AlternativeTypeLists(Rest1,Clist++CompList);
+parse_AlternativeTypeLists([{'...',L1},{'!',_}|Rest02],Clist0) ->
+ {_,Rest03} = parse_ExceptionIdentification(Rest02),
+ %% Exception info is currently thrown away
+ parse_AlternativeTypeLists2(Rest03,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+parse_AlternativeTypeLists([{',',L1},{'...',_},{'!',_}|Rest02],Clist0) when Clist0 =/= []->
+ {_,Rest03} = parse_ExceptionIdentification(Rest02),
+ %% Exception info is currently thrown away
+ parse_AlternativeTypeLists2(Rest03,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+
+parse_AlternativeTypeLists([{',',_},{'...',L1}|Rest02],Clist0) when Clist0 =/= []->
+ parse_AlternativeTypeLists2(Rest02,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+parse_AlternativeTypeLists([{'...',L1}|Rest02],Clist0) ->
+ parse_AlternativeTypeLists2(Rest02,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+parse_AlternativeTypeLists(Tokens = [{'}',_L1}|_Rest02],Clist0) ->
+ {Clist0,Tokens}.
+
+parse_AlternativeTypeLists2(Tokens,Clist) ->
+ {ExtAdd,Rest} = parse_ExtensionAdditionAlternatives(Tokens,Clist),
+ {Clist2,Rest2} = parse_OptionalExtensionMarker(Rest,lists:flatten(ExtAdd)),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ {CompList,Rest4} = parse_AlternativeTypeList(Rest3,[]),
+ {Clist2 ++ CompList,Rest4};
+ _ ->
+ {Clist2,Rest2}
+ end.
-parse_AlternativeTypeList(Tokens) ->
- parse_AlternativeTypeList(Tokens,[]).
-parse_AlternativeTypeList(Tokens,Acc) ->
- {NamedType,Rest} = parse_NamedType(Tokens),
+
+parse_AlternativeTypeList([{',',_},Id = {identifier,_,_}|Rest],Acc) when Acc =/= [] ->
+ {AlternativeType,Rest2} = parse_NamedType([Id|Rest]),
+ parse_AlternativeTypeList(Rest2,[AlternativeType|Acc]);
+parse_AlternativeTypeList(Tokens = [{'}',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AlternativeTypeList(Tokens = [{']',_},{']',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AlternativeTypeList(Tokens = [{',',_},{'...',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AlternativeTypeList(Tokens,[]) ->
+ {AlternativeType,Rest} = parse_NamedType(Tokens),
+ parse_AlternativeTypeList(Rest,[AlternativeType]);
+parse_AlternativeTypeList(Tokens,_) ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Tokens)),get_token(hd(tl(Tokens)))],
+ expected,['}',', identifier']]}}).
+
+parse_ExtensionAdditionAlternatives(Tokens =[{',',_}|_],Clist) ->
+ {ExtAddList,Rest2} = parse_ExtensionAdditionAlternativesList(Tokens,[]),
+ {Clist++lists:flatten(ExtAddList),Rest2};
+parse_ExtensionAdditionAlternatives(Tokens,Clist) ->
+ %% Empty
+ {Clist,Tokens}.
+
+parse_ExtensionAdditionAlternativesList([{',',_},Id = {identifier,_,_}|Rest],Acc) ->
+ {AlternativeType,Rest2} = parse_NamedType([Id|Rest]),
+ parse_ExtensionAdditionAlternativesList(Rest2,[AlternativeType|Acc]);
+parse_ExtensionAdditionAlternativesList([{',',_},C1 = {'[',_},C2 = {'[',_}|Rest],Acc) ->
+ {ExtAddGroup,Rest2} = parse_ExtensionAdditionAlternativesGroup([C1,C2|Rest],[]),
+ parse_ExtensionAdditionAlternativesList(Rest2,[ExtAddGroup|Acc]);
+parse_ExtensionAdditionAlternativesList(Tokens = [{'}',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ExtensionAdditionAlternativesList(Tokens = [{',',_},{'...',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ExtensionAdditionAlternativesList(Tokens,_) ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Tokens)),get_token(hd(tl(Tokens)))],
+ expected,['}',', identifier']]}}).
+
+
+parse_ExtensionAdditionAlternativesGroup([ {'[',_},{'[',_},_VsnNr = {number,_,Num},{':',_}|Rest],[]) ->
+ parse_ExtensionAdditionAlternativesGroup2(Rest,Num);
+parse_ExtensionAdditionAlternativesGroup([ {'[',_},{'[',_}|Rest],[]) ->
+ parse_ExtensionAdditionAlternativesGroup2(Rest,undefined);
+parse_ExtensionAdditionAlternativesGroup(Tokens,_) ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Tokens)),get_token(hd(tl(Tokens)))],
+ expected,['[[']]}}).
+
+
+parse_ExtensionAdditionAlternativesGroup2(Tokens,Num) ->
+ {CompTypeList,Rest} = parse_AlternativeTypeList(Tokens,[]),
case Rest of
- [{',',_},Id = {identifier,_,_}|Rest2] ->
- parse_AlternativeTypeList([Id|Rest2],[NamedType|Acc]);
+ [{']',_},{']',_}|Rest2] ->
+ {[{'ExtensionAdditionGroup',Num}|CompTypeList] ++
+ ['ExtensionAdditionGroupEnd'],Rest2};
_ ->
- {lists:reverse([NamedType|Acc]),Rest}
- end.
-
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,[']]']]}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% parse_AlternativeTypeLists(Tokens,ExtensionDefault) ->
+%% {AltTypeList,Rest1} = parse_AlternativeTypeList(Tokens),
+%% {ExtensionAndException,Rest2} =
+%% case Rest1 of
+%% [{',',_},{'...',L1},{'!',_}|Rest12] ->
+%% {_,Rest13} = parse_ExceptionIdentification(Rest12),
+%% %% Exception info is currently thrown away
+%% {[#'EXTENSIONMARK'{pos=L1}],Rest13};
+%% [{',',_},{'...',L1}|Rest12] ->
+%% {[#'EXTENSIONMARK'{pos=L1}],Rest12};
+%% _ ->
+%% {[],Rest1}
+%% end,
+%% {AltTypeList2,Rest5} =
+%% case ExtensionAndException of
+%% [] ->
+%% {AltTypeList,Rest2};
+%% _ ->
+%% {ExtensionAddition,Rest3} =
+%% case Rest2 of
+%% [{',',_}|Rest23] ->
+%% parse_ExtensionAdditionAlternativeList(Rest23);
+%% _ ->
+%% {[],Rest2}
+%% end,
+%% {OptionalExtensionMarker,Rest4} =
+%% case Rest3 of
+%% [{',',_},{'...',L3}|Rest31] ->
+%% {[#'EXTENSIONMARK'{pos=L3}],Rest31};
+%% _ ->
+%% {[],Rest3}
+%% end,
+%% {AltTypeList ++ ExtensionAndException ++
+%% ExtensionAddition ++ OptionalExtensionMarker, Rest4}
+%% end,
+%% AltTypeList3 =
+%% case [X || X=#'EXTENSIONMARK'{} <- AltTypeList2] of
+%% [] when ExtensionDefault == 'IMPLIED' ->
+%% AltTypeList2 ++ [#'EXTENSIONMARK'{}];
+%% _ ->
+%% AltTypeList2
+%% end,
+%% {AltTypeList3,Rest5}.
-parse_ExtensionAdditionAlternativeList(Tokens) ->
- parse_ExtensionAdditionAlternativeList(Tokens,[]).
+%% parse_AlternativeTypeList(Tokens) ->
+%% parse_AlternativeTypeList(Tokens,[]).
+
+%% parse_AlternativeTypeList(Tokens,Acc) ->
+%% {NamedType,Rest} = parse_NamedType(Tokens),
+%% case Rest of
+%% [{',',_},Id = {identifier,_,_}|Rest2] ->
+%% parse_AlternativeTypeList([Id|Rest2],[NamedType|Acc]);
+%% _ ->
+%% {lists:reverse([NamedType|Acc]),Rest}
+%% end.
+
-parse_ExtensionAdditionAlternativeList(Tokens,Acc) ->
- {Element,Rest0} =
- case Tokens of
- [{identifier,_,_}|_Rest] ->
- parse_NamedType(Tokens);
- [{'[',_},{'[',_}|_] ->
- parse_ExtensionAdditionAlternatives(Tokens)
- end,
- case Rest0 of
- [{',',_}|Rest01] ->
- parse_ExtensionAdditionAlternativeList(Rest01,[Element|Acc]);
- _ ->
- {lists:reverse([Element|Acc]),Rest0}
- end.
-parse_ExtensionAdditionAlternatives([{'[',_},{'[',_}|Rest]) ->
- parse_ExtensionAdditionAlternatives(Rest,[]);
-parse_ExtensionAdditionAlternatives(Tokens) ->
- throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
- [got,get_token(hd(Tokens)),expected,'[[']}}).
+%% parse_ExtensionAdditionAlternativeList(Tokens) ->
+%% parse_ExtensionAdditionAlternativeList(Tokens,[]).
+
+%% parse_ExtensionAdditionAlternativeList([{'[[',_}|Rest],Acc) ->
+%% parse_ExtensionAdditionAlternativeList(Rest,Acc);
+%% parse_ExtensionAdditionAlternativeList(Tokens = [{identifier,_,_}|_Rest],Acc) ->
+%% {Element,Rest0} = parse_NamedType(Tokens);
+%% case Rest0 of
+%% [{',',_}|Rest01] ->
+%% parse_ExtensionAdditionAlternativeList(Rest01,[Element|Acc]);
+%% _ ->
+%% {lists:reverse([Element|Acc]),Rest0}
+%% end.
-parse_ExtensionAdditionAlternatives([Id = {identifier,_,_}|Rest],Acc) ->
- {NamedType, Rest2} = parse_NamedType([Id|Rest]),
- case Rest2 of
- [{',',_}|Rest21] ->
- parse_ExtensionAdditionAlternatives(Rest21,[NamedType|Acc]);
- [{']',_},{']',_}|Rest21] ->
- {lists:reverse(Acc),Rest21};
- _ ->
- throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
- [got,get_token(hd(Rest2)),expected,[',',']]']]}})
- end.
+%% parse_ExtensionAdditionAlternatives([{'[[',_}|Rest]) ->
+%% parse_ExtensionAdditionAlternatives(Rest,[]);
+%% parse_ExtensionAdditionAlternatives(Tokens) ->
+%% throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+%% [got,get_token(hd(Tokens)),expected,'[[']}}).
+
+%% parse_ExtensionAdditionAlternatives([Id = {identifier,_,_}|Rest],Acc) ->
+%% {NamedType, Rest2} = parse_NamedType([Id|Rest]),
+%% case Rest2 of
+%% [{',',_}|Rest21] ->
+%% parse_ExtensionAdditionAlternatives(Rest21,[NamedType|Acc]);
+%% [{']]',_}|Rest21] ->
+%% {lists:reverse(Acc),Rest21};
+%% _ ->
+%% throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+%% [got,get_token(hd(Rest2)),expected,[',',']]']]}})
+%% end.
parse_NamedType([{identifier,L1,Idname}|Rest]) ->
{Type,Rest2} = parse_Type(Rest),
@@ -2428,144 +2540,123 @@ parse_NamedType(Tokens) ->
parse_ComponentTypeLists(Tokens) ->
-% Resulting tuple {ComponentTypeList,Rest1} is returned
- case Tokens of
- [{identifier,_,_}|_Rest0] ->
- {Clist,Rest01} = parse_ComponentTypeList(Tokens),
- case Rest01 of
- [{',',_}|Rest02] ->
- parse_ComponentTypeLists(Rest02,Clist); % 5 - 13
- _ ->
- {Clist,Rest01}
- end;
- [{'COMPONENTS',_},{'OF',_}|_Rest] ->
- {Clist,Rest01} = parse_ComponentTypeList(Tokens),
- case Rest01 of
- [{',',_}|Rest02] ->
- parse_ComponentTypeLists(Rest02,Clist);
- _ ->
- {Clist,Rest01}
- end;
+ parse_ComponentTypeLists(Tokens,[]).
+
+parse_ComponentTypeLists(Tokens = [{identifier,_,_}|_Rest0],Clist) ->
+ {CompList,Rest1} = parse_ComponentTypeList(Tokens,[]),
+ parse_ComponentTypeLists(Rest1,Clist++CompList);
+parse_ComponentTypeLists(Tokens = [{'COMPONENTS',_},{'OF',_}|_Rest],Clist) ->
+ {CompList,Rest1} = parse_ComponentTypeList(Tokens,[]),
+ parse_ComponentTypeLists(Rest1,Clist++CompList);
+parse_ComponentTypeLists([{'...',L1},{'!',_}|Rest02],Clist0) ->
+ {_,Rest03} = parse_ExceptionIdentification(Rest02),
+ %% Exception info is currently thrown away
+ parse_ComponentTypeLists2(Rest03,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists([{',',L1},{'...',_},{'!',_}|Rest02],Clist0) when Clist0 =/= []->
+ {_,Rest03} = parse_ExceptionIdentification(Rest02),
+ %% Exception info is currently thrown away
+ parse_ComponentTypeLists2(Rest03,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+
+ parse_ComponentTypeLists([{',',_},{'...',L1}|Rest02],Clist0) when Clist0 =/= []->
+ parse_ComponentTypeLists2(Rest02,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists([{'...',L1}|Rest02],Clist0) ->
+ parse_ComponentTypeLists2(Rest02,Clist0++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists(Tokens = [{'}',_L1}|_Rest02],Clist0) ->
+ {Clist0,Tokens}.
+
+parse_ComponentTypeLists2(Tokens,Clist) ->
+ {ExtAdd,Rest} = parse_ExtensionAdditions(Tokens,Clist),
+ {Clist2,Rest2} = parse_OptionalExtensionMarker(Rest,lists:flatten(ExtAdd)),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ {CompList,Rest4} = parse_ComponentTypeList(Rest3,[]),
+ {Clist2 ++ CompList,Rest4};
_ ->
- parse_ComponentTypeLists(Tokens,[])
+ {Clist2,Rest2}
end.
-parse_ComponentTypeLists([{'...',L1},{'!',_}|Rest],Clist1) ->
- {_,Rest2} = parse_ExceptionIdentification(Rest),
- %% Exception info is currently thrown away
- parse_ComponentTypeLists2(Rest2,Clist1++[#'EXTENSIONMARK'{pos=L1}]);
-parse_ComponentTypeLists([{'...',L1}|Rest],Clist1) -> %% first Extensionmark
- parse_ComponentTypeLists2(Rest,Clist1++[#'EXTENSIONMARK'{pos=L1}]);
-parse_ComponentTypeLists(Tokens,Clist1) ->
- {Clist1,Tokens}.
-
-
-parse_ComponentTypeLists2(Tokens,Clist1) ->
- {ExtensionAddition,Rest2} =
- case Tokens of
- [{',',_}|Rest1] ->
- parse_ExtensionAdditionList(Rest1);
- _ ->
- {[],Tokens}
- end,
- {OptionalExtensionMarker,Rest3} =
- case Rest2 of
- [{',',_},{'...',L2}|Rest21] ->
- {[#'EXTENSIONMARK'{pos=L2}],Rest21};
- _ ->
- {[],Rest2}
- end,
- {RootComponentTypeList,Rest4} =
- case Rest3 of
- [{',',_}|Rest31] ->
- parse_ComponentTypeList(Rest31);
- _ ->
- {[],Rest3}
- end,
- {Clist1 ++ ExtensionAddition ++ OptionalExtensionMarker ++ RootComponentTypeList, Rest4}.
-
+parse_OptionalExtensionMarker([{',',_},{'...',L1}|Rest],Clist)->
+ {Clist++[#'EXTENSIONMARK'{pos=L1}],Rest};
+parse_OptionalExtensionMarker(Tokens,Clist) ->
+ {Clist,Tokens}.
-parse_ComponentTypeList(Tokens) ->
- parse_ComponentTypeList(Tokens,[]).
-parse_ComponentTypeList(Tokens,Acc) ->
+parse_ComponentTypeList([{',',_},Id = {identifier,_,_}|Rest],Acc) when Acc =/= [] ->
+ {ComponentType,Rest2} = parse_ComponentType([Id|Rest]),
+ parse_ComponentTypeList(Rest2,[ComponentType|Acc]);
+parse_ComponentTypeList([{',',_},C1={'COMPONENTS',_},C2={'OF',_}|Rest],Acc) when Acc =/= [] ->
+ {ComponentType,Rest2} = parse_ComponentType([C1,C2|Rest]),
+ parse_ComponentTypeList(Rest2,[ComponentType|Acc]);
+parse_ComponentTypeList(Tokens = [{'}',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ComponentTypeList(Tokens = [{']',_},{']',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ComponentTypeList(Tokens = [{',',_},{'...',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ComponentTypeList(Tokens,[]) ->
{ComponentType,Rest} = parse_ComponentType(Tokens),
+ parse_ComponentTypeList(Rest,[ComponentType]);
+parse_ComponentTypeList(Tokens,_) ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Tokens)),get_token(hd(tl(Tokens)))],
+ expected,['}',', identifier']]}}).
+
+parse_ExtensionAdditions(Tokens=[{',',_}|_],Clist) ->
+ {ExtAddList,Rest2} = parse_ExtensionAdditionList(Tokens,[]),
+ {Clist++ExtAddList,Rest2};
+parse_ExtensionAdditions(Tokens,Clist) ->
+ %% Empty
+ {Clist,Tokens}.
+
+parse_ExtensionAdditionList([{',',_},Id = {identifier,_,_}|Rest],Acc) ->
+ {ComponentType,Rest2} = parse_ComponentType([Id|Rest]),
+ parse_ExtensionAdditionList(Rest2,[ComponentType|Acc]);
+parse_ExtensionAdditionList([{',',_},C1={'COMPONENTS',_},C2={'OF',_}|Rest],Acc) ->
+ {ComponentType,Rest2} = parse_ComponentType([C1,C2|Rest]),
+ parse_ExtensionAdditionList(Rest2,[ComponentType|Acc]);
+parse_ExtensionAdditionList([{',',_},C1 = {'[',_},C2 = {'[',_}|Rest],Acc) ->
+ {ExtAddGroup,Rest2} = parse_ExtensionAdditionGroup([C1,C2|Rest],[]),
+ parse_ExtensionAdditionList(Rest2,[ExtAddGroup|Acc]);
+parse_ExtensionAdditionList(Tokens = [{'}',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ExtensionAdditionList(Tokens = [{',',_},{'...',_}|_],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_ExtensionAdditionList(Tokens,_) ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Tokens)),get_token(hd(tl(Tokens)))],
+ expected,['}',', identifier']]}}).
+
+
+parse_ExtensionAdditionGroup([ {'[',_},{'[',_},_VsnNr = {number,_,Num},{':',_}|Rest],[]) ->
+ parse_ExtensionAdditionGroup2(Rest,Num);
+parse_ExtensionAdditionGroup([ {'[',_},{'[',_}|Rest],[]) ->
+ parse_ExtensionAdditionGroup2(Rest,undefined);
+parse_ExtensionAdditionGroup(Tokens,_) ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Tokens)),get_token(hd(tl(Tokens)))],
+ expected,['[[']]}}).
+
+
+parse_ExtensionAdditionGroup2(Tokens,Num) ->
+ {CompTypeList,Rest} = parse_ComponentTypeList(Tokens,[]),
case Rest of
- [{',',_},Id = {identifier,_,_}|Rest2] ->
- parse_ComponentTypeList([Id|Rest2],[ComponentType|Acc]);
- [{',',_},C1={'COMPONENTS',_},C2={'OF',_}|Rest2] ->
- parse_ComponentTypeList([C1,C2|Rest2],[ComponentType|Acc]);
-% _ ->
-% {lists:reverse([ComponentType|Acc]),Rest}
- [{'}',_}|_] ->
- {lists:reverse([ComponentType|Acc]),Rest};
-% [{',',_},{'...',_},{'}',_}|_] ->
-% {lists:reverse([ComponentType|Acc]),Rest};
- [{',',_},{'...',_}|_] ->%% here comes the dubble ellipse
- {lists:reverse([ComponentType|Acc]),Rest};
+ [{']',_},{']',_}|Rest2] ->
+ {[{'ExtensionAdditionGroup',Num}|CompTypeList] ++
+ ['ExtensionAdditionGroupEnd'],Rest2};
_ ->
- throw({asn1_error,
- {get_line(hd(Tokens)),get(asn1_module),
- [got,[get_token(hd(Rest)),get_token(hd(tl(Rest)))],
- expected,['}',', identifier']]}})
- end.
-
-
-parse_ExtensionAdditionList(Tokens) ->
- parse_ExtensionAdditionList(Tokens,[]).
-
-parse_ExtensionAdditionList(Tokens,Acc) ->
- {Element,Rest0} =
- case Tokens of
- [{identifier,_,_}|_Rest] ->
- parse_ComponentType(Tokens);
- [{'[',_},{'[',_}|_] ->
- parse_ExtensionAdditions(Tokens);
- [{'...',L1}|_Rest] ->
- {#'EXTENSIONMARK'{pos=L1},Tokens};
- _ ->
- throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
- [got,get_token(hd(Tokens)),expected,
- [identifier,'[[']]}})
- end,
- case Rest0 of
- [{',',_}|Rest01] ->
- parse_ExtensionAdditionList(Rest01,[Element|Acc]);
- [{'...',_}|Rest01] ->
- {lists:reverse([Element|Acc]),Rest01};
- _ ->
- {lists:reverse([Element|Acc]),Rest0}
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,[']]']]}})
end.
-parse_ExtensionAdditions([{'[',_},{'[',_}|Rest]) ->
- parse_ExtensionAdditions(Rest,[]);
-parse_ExtensionAdditions(Tokens) ->
- throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
- [got,get_token(hd(Tokens)),expected,'[[']}}).
-
-parse_ExtensionAdditions([_VsnNr = {number,_,_},{':',_}|Rest],Acc) ->
- %% ignor version number for now
- parse_ExtensionAdditions(Rest,Acc);
-parse_ExtensionAdditions([Id = {identifier,_,_}|Rest],Acc) ->
- {ComponentType, Rest2} = parse_ComponentType([Id|Rest]),
- case Rest2 of
- [{',',_}|Rest21] ->
- parse_ExtensionAdditions(Rest21,[ComponentType|Acc]);
- [{']',_},{']',_}|Rest21] ->
- {lists:reverse(Acc),Rest21};
- _ ->
- throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
- [got,get_token(hd(Rest2)),expected,[',',']]']]}})
- end;
-parse_ExtensionAdditions(Tokens,_) ->
- throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
- [got,get_token(hd(Tokens)),expected,identifier]}}).
parse_ComponentType([{'COMPONENTS',_},{'OF',_}|Rest]) ->
{Type,Rest2} = parse_Type(Rest),
{{'COMPONENTS OF',Type},Rest2};
parse_ComponentType(Tokens) ->
- {NamedType,Rest} = parse_NamedType(Tokens),
+ Result = {NamedType,Rest} = parse_NamedType(Tokens),
case Rest of
[{'OPTIONAL',_}|Rest2] ->
{NamedType#'ComponentType'{prop='OPTIONAL'},Rest2};
@@ -2573,7 +2664,7 @@ parse_ComponentType(Tokens) ->
{Value,Rest21} = parse_Value(Rest2),
{NamedType#'ComponentType'{prop={'DEFAULT',Value}},Rest21};
_ ->
- {NamedType,Rest}
+ Result
end.
diff --git a/lib/asn1/src/asn1ct_tok.erl b/lib/asn1/src/asn1ct_tok.erl
index f0ce31bdb2..85199c65ec 100644
--- a/lib/asn1/src/asn1ct_tok.erl
+++ b/lib/asn1/src/asn1ct_tok.erl
@@ -21,7 +21,7 @@
%% Tokenize ASN.1 code (input to parser generated with yecc)
--export([get_name/2,tokenise/2, file/1]).
+-export([get_name/2,tokenise/4, file/1]).
file(File) ->
@@ -29,12 +29,9 @@ file(File) ->
{error, Reason} ->
{error,{File,file:format_error(Reason)}};
{ok,Stream} ->
- process0(Stream)
+ process(Stream,0,[])
end.
-process0(Stream) ->
- process(Stream,0,[]).
-
process(Stream,Lno,R) ->
process(io:get_line(Stream, ''), Stream,Lno+1,R).
@@ -45,131 +42,128 @@ process(eof, Stream,Lno,R) ->
process(L, Stream,Lno,R) when is_list(L) ->
%%io:format('read:~s',[L]),
- case catch tokenise(L,Lno) of
+ case catch tokenise(Stream,L,Lno,[]) of
{'ERR',Reason} ->
io:format("Tokeniser error on line: ~w ~w~n",[Lno,Reason]),
exit(0);
- {multiline_comment,NestingLevel} ->
- {RestL,Lno2} = process_skip_multiline_comment(Stream,Lno,NestingLevel),
- process(RestL,Stream,Lno2,R);
- T ->
+ {NewLno,T} ->
%%io:format('toks:~w~n',[T]),
- process(Stream,Lno,[T|R])
+ process(Stream,NewLno,[T|R])
end.
-process_skip_multiline_comment(Stream,Lno,NestingLevel) ->
- process_skip_multiline_comment(io:get_line(Stream, ''),
- Stream, Lno + 1, NestingLevel).
-process_skip_multiline_comment(eof,_Stream,Lno,_NestingLevel) ->
- io:format("Tokeniser error on line: ~w, premature end of multiline comment~n",[Lno]),
- exit(0);
-process_skip_multiline_comment(Line,Stream,Lno,NestingLevel) ->
- case catch skip_multiline_comment(Line,NestingLevel) of
- {multiline_comment,NestingLevel2} ->
- process_skip_multiline_comment(Stream,Lno,NestingLevel2);
- T ->
- {T,Lno}
- end.
-
-tokenise([H|T],Lno) when $a =< H , H =< $z ->
+tokenise(Stream,[H|T],Lno,R) when $a =< H , H =< $z ->
{X, T1} = get_name(T, [H]),
- [{identifier,Lno, list_to_atom(X)}|tokenise(T1,Lno)];
+ tokenise(Stream,T1,Lno,[{identifier,Lno, list_to_atom(X)}|R]);
-tokenise([$&,H|T],Lno) when $A =< H , H =< $Z ->
+tokenise(Stream,[$&,H|T],Lno,R) when $A =< H , H =< $Z ->
{Y, T1} = get_name(T, [H]),
X = list_to_atom(Y),
- [{typefieldreference, Lno, X} | tokenise(T1, Lno)];
+ tokenise(Stream,T1,Lno,[{typefieldreference, Lno, X} | R]);
-tokenise([$&,H|T],Lno) when $a =< H , H =< $z ->
+tokenise(Stream,[$&,H|T],Lno,R) when $a =< H , H =< $z ->
{Y, T1} = get_name(T, [H]),
X = list_to_atom(Y),
- [{valuefieldreference, Lno, X} | tokenise(T1, Lno)];
+ tokenise(Stream,T1,Lno,[{valuefieldreference, Lno, X} | R]);
-tokenise([H|T],Lno) when $A =< H , H =< $Z ->
+tokenise(Stream,[H|T],Lno,R) when $A =< H , H =< $Z ->
{Y, T1} = get_name(T, [H]),
X = list_to_atom(Y),
case reserved_word(X) of
true ->
- [{X,Lno}|tokenise(T1,Lno)];
+ tokenise(Stream,T1,Lno,[{X,Lno}|R]);
false ->
- [{typereference,Lno,X}|tokenise(T1,Lno)];
+ tokenise(Stream,T1,Lno,[{typereference,Lno,X}|R]);
rstrtype ->
- [{restrictedcharacterstringtype,Lno,X}|tokenise(T1,Lno)]
+ tokenise(Stream,T1,Lno,[{restrictedcharacterstringtype,Lno,X}|R])
end;
-tokenise([$-,H|T],Lno) when $0 =< H , H =< $9 ->
+tokenise(Stream,[$-,H|T],Lno,R) when $0 =< H , H =< $9 ->
{X, T1} = get_number(T, [H]),
- [{number,Lno,-1 * list_to_integer(X)}|tokenise(T1,Lno)];
+ tokenise(Stream,T1,Lno,[{number,Lno,-1 * list_to_integer(X)}|R]);
-tokenise([H|T],Lno) when $0 =< H , H =< $9 ->
+tokenise(Stream,[H|T],Lno,R) when $0 =< H , H =< $9 ->
{X, T1} = get_number(T, [H]),
- [{number,Lno,list_to_integer(X)}|tokenise(T1,Lno)];
+ tokenise(Stream,T1,Lno,[{number,Lno,list_to_integer(X)}|R]);
-tokenise([$-,$-|T],Lno) ->
- tokenise(skip_comment(T),Lno);
+tokenise(Stream,[$-,$-|T],Lno,R) ->
+ tokenise(Stream,skip_comment(T),Lno,R);
-tokenise([$/,$*|T],Lno) ->
- tokenise(skip_multiline_comment(T,0),Lno);
+tokenise(Stream,[$/,$*|T],Lno,R) ->
+ {NewLno,T1} = skip_multiline_comment(Stream,T,Lno,0),
+ tokenise(Stream,T1,NewLno,R);
-tokenise([$:,$:,$=|T],Lno) ->
- [{'::=',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$:,$:,$=|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'::=',Lno}|R]);
-tokenise([$'|T],Lno) ->
+tokenise(Stream,[$'|T],Lno,R) ->
case catch collect_quoted(T,Lno,[]) of
{'ERR',_} ->
throw({'ERR','bad_quote'});
{Thing, T1} ->
- [Thing|tokenise(T1,Lno)]
+ tokenise(Stream,T1,Lno,[Thing|R])
end;
-tokenise([$"|T],Lno) ->
- collect_string(T,Lno);
+tokenise(Stream,[$"|T],Lno,R) ->
+ {Str,T1} = collect_string(T,Lno),
+ tokenise(Stream,T1,Lno,[Str|R]);
+
+tokenise(Stream,[${|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'{',Lno}|R]);
+
+tokenise(Stream,[$}|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'}',Lno}|R]);
-tokenise([${|T],Lno) ->
- [{'{',Lno}|tokenise(T,Lno)];
+%% tokenise(Stream,[$],$]|T],Lno,R) ->
+%% tokenise(Stream,T,Lno,[{']]',Lno}|R]);
-tokenise([$}|T],Lno) ->
- [{'}',Lno}|tokenise(T,Lno)];
+%% Even though x.680 specify '[[' and ']]' as lexical items
+%% it does not work to have them as such since the single [ and ] can
+%% be used beside each other in the SYNTAX OF in x.681
+%% the solution chosen here , i.e. to have them as separate lexical items
+%% will not detect the cases where there is white space between them
+%% which would be an error in the use in ExtensionAdditionGroups
-tokenise([$]|T],Lno) ->
- [{']',Lno}|tokenise(T,Lno)];
+%% tokenise(Stream,[$[,$[|T],Lno,R) ->
+%% tokenise(Stream,T,Lno,[{'[[',Lno}|R]);
-tokenise([$[|T],Lno) ->
- [{'[',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$]|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{']',Lno}|R]);
-tokenise([$,|T],Lno) ->
- [{',',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$[|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'[',Lno}|R]);
-tokenise([$(|T],Lno) ->
- [{'(',Lno}|tokenise(T,Lno)];
-tokenise([$)|T],Lno) ->
- [{')',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$,|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{',',Lno}|R]);
-tokenise([$.,$.,$.|T],Lno) ->
- [{'...',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$(|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'(',Lno}|R]);
+tokenise(Stream,[$)|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{')',Lno}|R]);
-tokenise([$.,$.|T],Lno) ->
- [{'..',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$.,$.,$.|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'...',Lno}|R]);
-tokenise([$.|T],Lno) ->
- [{'.',Lno}|tokenise(T,Lno)];
-tokenise([$^|T],Lno) ->
- [{'^',Lno}|tokenise(T,Lno)];
-tokenise([$!|T],Lno) ->
- [{'!',Lno}|tokenise(T,Lno)];
-tokenise([$||T],Lno) ->
- [{'|',Lno}|tokenise(T,Lno)];
+tokenise(Stream,[$.,$.|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'..',Lno}|R]);
+tokenise(Stream,[$.|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'.',Lno}|R]);
+tokenise(Stream,[$^|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'^',Lno}|R]);
+tokenise(Stream,[$!|T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'!',Lno}|R]);
+tokenise(Stream,[$||T],Lno,R) ->
+ tokenise(Stream,T,Lno,[{'|',Lno}|R]);
-tokenise([H|T],Lno) ->
+tokenise(Stream,[H|T],Lno,R) ->
case white_space(H) of
true ->
- tokenise(T,Lno);
+ tokenise(Stream,T,Lno,R);
false ->
- [{list_to_atom([H]),Lno}|tokenise(T,Lno)]
+ tokenise(Stream,T,Lno,[{list_to_atom([H]),Lno}|R])
end;
-tokenise([],_) ->
- [].
+tokenise(_Stream,[],Lno,R) ->
+ {Lno,lists:reverse(R)}.
collect_string(L,Lno) ->
@@ -181,7 +175,7 @@ collect_string([],_,_) ->
collect_string([H|T],Lno,Str) ->
case H of
$" ->
- [{cstring,1,lists:reverse(Str)}|tokenise(T,Lno)];
+ {{cstring,1,lists:reverse(Str)},T};
Ch ->
collect_string(T,Lno,[Ch|Str])
end.
@@ -252,17 +246,23 @@ skip_comment([_|T]) ->
skip_comment(T).
-skip_multiline_comment([],L) ->
- throw({multiline_comment,L});
-skip_multiline_comment([$*,$/|T],0) ->
- T;
-skip_multiline_comment([$*,$/|T],Level) ->
- skip_multiline_comment(T,Level - 1);
-skip_multiline_comment([$/,$*|T],Level) ->
- skip_multiline_comment(T,Level + 1);
-skip_multiline_comment([_|T],Level) ->
- skip_multiline_comment(T,Level).
-
+skip_multiline_comment(Stream,[],Lno,Level) ->
+ case io:get_line(Stream,'') of
+ eof ->
+ io:format("Tokeniser error on line: ~w~n"
+ "premature end of multiline comment~n",[Lno]),
+ exit(0);
+ Line ->
+ skip_multiline_comment(Stream,Line,Lno+1,Level)
+ end;
+skip_multiline_comment(_Stream,[$*,$/|T],Lno,0) ->
+ {Lno,T};
+skip_multiline_comment(Stream,[$*,$/|T],Lno,Level) ->
+ skip_multiline_comment(Stream,T,Lno,Level - 1);
+skip_multiline_comment(Stream,[$/,$*|T],Lno,Level) ->
+ skip_multiline_comment(Stream,T,Lno,Level + 1);
+skip_multiline_comment(Stream,[_|T],Lno,Level) ->
+ skip_multiline_comment(Stream,T,Lno,Level).
collect_quoted([$',$B|T],Lno, L) ->
case check_bin(L) of
diff --git a/lib/asn1/src/asn1rt_ber_bin.erl b/lib/asn1/src/asn1rt_ber_bin.erl
index ab04d981b0..22f9f2ecfd 100644
--- a/lib/asn1/src/asn1rt_ber_bin.erl
+++ b/lib/asn1/src/asn1rt_ber_bin.erl
@@ -2192,12 +2192,12 @@ decode_tag_and_length(Buffer) ->
%% Check if valid tag
%%
%% check_if_valid_tag(Tag, List_of_valid_tags, OptOrMand) -> name of the tag
-%%===============================================================================
+%%============================================================================
check_if_valid_tag(<<0,0,_/binary>>,_,_) ->
asn1_EOC;
check_if_valid_tag(<<>>, _, OptOrMand) ->
- check_if_valid_tag2(false,[],[],OptOrMand);
+ check_if_valid_tag2_error([], OptOrMand);
check_if_valid_tag(Bytes, ListOfTags, OptOrMand) when is_binary(Bytes) ->
{Tag, _, _} = decode_tag(Bytes),
check_if_valid_tag(Tag, ListOfTags, OptOrMand);
@@ -2217,7 +2217,6 @@ check_if_valid_tag(Tag, ListOfTags, OptOrMand) ->
check_if_valid_tag2(_Class_TagNo, [], Tag, MandOrOpt) ->
check_if_valid_tag2_error(Tag,MandOrOpt);
-
check_if_valid_tag2(Class_TagNo, [{TagName,TagList}|T], Tag, OptOrMand) ->
case check_if_valid_tag_loop(Class_TagNo, TagList) of
true ->
@@ -2226,7 +2225,7 @@ check_if_valid_tag2(Class_TagNo, [{TagName,TagList}|T], Tag, OptOrMand) ->
check_if_valid_tag2(Class_TagNo, T, Tag, OptOrMand)
end.
--spec(check_if_valid_tag2_error/2 :: (term(),atom()) -> no_return()).
+-spec check_if_valid_tag2_error(term(), atom()) -> no_return().
check_if_valid_tag2_error(Tag,mandatory) ->
exit({error,{asn1,{invalid_tag,Tag}}});
diff --git a/lib/asn1/src/asn1rt_driver_handler.erl b/lib/asn1/src/asn1rt_driver_handler.erl
index c95b243ae0..146d0043f9 100644
--- a/lib/asn1/src/asn1rt_driver_handler.erl
+++ b/lib/asn1/src/asn1rt_driver_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -71,7 +71,10 @@ load_driver(Reason) ->
end.
init(FromPid,FromRef) ->
- register(asn1_driver_owner,self()),
+ case catch register(asn1_driver_owner,self()) of
+ true -> true;
+ _Other -> exit(normal)
+ end,
Dir = filename:join([code:priv_dir(asn1),"lib"]),
case catch erl_ddll:load_driver(Dir,asn1_erl_drv) of
ok ->
diff --git a/lib/asn1/src/asn1rt_uper_bin.erl b/lib/asn1/src/asn1rt_uper_bin.erl
index a964b835ae..abe178a69e 100644
--- a/lib/asn1/src/asn1rt_uper_bin.erl
+++ b/lib/asn1/src/asn1rt_uper_bin.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1611,25 +1611,8 @@ complete_NFP(InList) when is_bitstring(InList) ->
%% 10.5.6 NOTE: If "range" satisfies the inequality 2^m < "range" =<
%% 2^(m+1) then the number of bits = m + 1
-num_bits(1) -> 0;
-num_bits(2) -> 1;
-num_bits(R) when R =< 4 ->
- 2;
-num_bits(R) when R =< 8 ->
- 3;
-num_bits(R) when R =< 16 ->
- 4;
-num_bits(R) when R =< 32 ->
- 5;
-num_bits(R) when R =< 64 ->
- 6;
-num_bits(R) when R =< 128 ->
- 7;
-num_bits(R) when R =< 256 ->
- 8;
-num_bits(R) when R =< 512 ->
- 9;
-num_bits(R) when R =< 1024 ->
- 10;
-num_bits(R) ->
- 1+num_bits(R bsr 1).
+
+num_bits(N) ->
+ num_bits(N,1,0).
+num_bits(N,T,B) when N=<T->B;
+num_bits(N,T,B) ->num_bits(N,T bsl 1, B+1).
diff --git a/lib/asn1/test/External.hrl b/lib/asn1/test/External.hrl
index 8818fac488..14a3a059e6 100644
--- a/lib/asn1/test/External.hrl
+++ b/lib/asn1/test/External.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile
index 5014eb902b..7ecd544d4b 100644
--- a/lib/asn1/test/Makefile
+++ b/lib/asn1/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -189,27 +189,16 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) asn1.spec $(EMAKEFILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
- @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+ $(INSTALL_DIR) $(RELSYSDIR)/asn1_SUITE_data
$(INSTALL_DIR) $(RELSYSDIR)/asn1_bin_SUITE_data
- @(cd asn1_SUITE_data; tar cf - *) | (cd $(RELSYSDIR)/asn1_bin_SUITE_data; tar xf -)
$(INSTALL_DIR) $(RELSYSDIR)/asn1_bin_v2_SUITE_data
- @(cd asn1_SUITE_data; tar cf - *) | (cd $(RELSYSDIR)/asn1_bin_v2_SUITE_data; tar xf -)
-
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) asn1.spec asn1.cover $(INSTALL_PROGS) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
+ cd asn1_SUITE_data; tar cfh $(RELSYSDIR)/asn1_SUITE_data.tar *
+ cd $(RELSYSDIR)/asn1_SUITE_data; tar xf $(RELSYSDIR)/asn1_SUITE_data.tar
+ cd $(RELSYSDIR)/asn1_bin_SUITE_data; tar xf $(RELSYSDIR)/asn1_SUITE_data.tar
+ cd $(RELSYSDIR)/asn1_bin_v2_SUITE_data; tar xf $(RELSYSDIR)/asn1_SUITE_data.tar
+ rm $(RELSYSDIR)/asn1_SUITE_data.tar
release_docs_spec:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/asn1/test/asn1.cover b/lib/asn1/test/asn1.cover
new file mode 100644
index 0000000000..589a8b7e3d
--- /dev/null
+++ b/lib/asn1/test/asn1.cover
@@ -0,0 +1,2 @@
+{incl_app,asn1,details}.
+
diff --git a/lib/asn1/test/asn1.spec b/lib/asn1/test/asn1.spec
index 6d9ae924fa..ae96de3a58 100644
--- a/lib/asn1/test/asn1.spec
+++ b/lib/asn1/test/asn1.spec
@@ -1,3 +1 @@
-{topcase, {dir, "../asn1_test"}}.
-
-
+{suites,"../asn1_test",all}.
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
new file mode 100644
index 0000000000..d050d8c84b
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -0,0 +1,2489 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%% Purpose : Test suite for the ASN.1 application
+
+-module(asn1_SUITE).
+-define(PER,'per').
+-define(BER,'ber').
+-define(ber_driver(Erule,Func),
+ case Erule of
+ ber_bin_v2 ->
+ Func;
+ _ -> ok
+ end).
+-define(per_optimize(Erule),
+ case Erule of
+ ber_bin_v2 ->[optimize];
+ _ -> []
+ end).
+-define(per_bit_opt(FuncCall),
+ case ?BER of
+ ber_bin_v2 -> FuncCall;
+% _ -> {skip,"only for bit optimized per_bin"}
+ _ -> ok
+ end).
+-define(uper_bin(FuncCall),
+ case ?PER of
+ per -> FuncCall;
+ _ -> ok
+ end).
+
+-compile(export_all).
+%%-export([Function/Arity, ...]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+%% records used by test-case default
+-record('Def1',{ bool0,
+ bool1 = asn1_DEFAULT,
+ bool2 = asn1_DEFAULT,
+ bool3 = asn1_DEFAULT}).
+
+%-record('Def2',{
+%bool10, bool11 = asn1_DEFAULT, bool12 = asn1_DEFAULT, bool13}).
+
+%-record('Def3',{
+%bool30 = asn1_DEFAULT, bool31 = asn1_DEFAULT, bool32 = asn1_DEFAULT, bool33 = asn1_DEFAULT}).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, compile}, parse, default_per, default_ber,
+ default_per_opt, per, {group, ber}, testPrim,
+ testPrimStrings, testPrimExternal, testChoPrim,
+ testChoExtension, testChoExternal, testChoOptional,
+ testChoOptionalImplicitTag, testChoRecursive,
+ testChoTypeRefCho, testChoTypeRefPrim,
+ testChoTypeRefSeq, testChoTypeRefSet, testDef, testOpt,
+ testSeqDefault, testSeqExtension, testSeqExternal,
+ testSeqOptional, testSeqPrim, testSeqTag,
+ testSeqTypeRefCho, testSeqTypeRefPrim,
+ testSeqTypeRefSeq, testSeqTypeRefSet, testSeqOf,
+ testSeqOfIndefinite, testSeqOfCho, testSeqOfExternal,
+ testSetDefault, testSetExtension,
+ testExtensionAdditionGroup, testSetExternal,
+ testSeqOfTag, testSetOptional, testSetPrim, testSetTag,
+ testSetTypeRefCho, testSetTypeRefPrim,
+ testSetTypeRefSeq, testSetTypeRefSet, testSetOf,
+ testSetOfCho, testSetOfExternal, testSetOfTag,
+ testEnumExt, value_test, testSeq2738, constructed,
+ ber_decode_error, h323test, testSeqIndefinite,
+ testSetIndefinite, testChoiceIndefinite,
+ per_GeneralString, per_open_type, testInfObjectClass,
+ testParameterizedInfObj, testMergeCompile, testobj,
+ testDeepTConstr, testConstraints, testInvokeMod,
+ testExport, testImport, testCompactBitString,
+ testMegaco, testParamBasic, testMvrasn6,
+ testContextSwitchingTypes, testTypeValueNotation,
+ testOpenTypeImplicitTag, duplicate_tags, rtUI, testROSE,
+ testINSTANCE_OF, testTCAP, testDER, specialized_decodes,
+ special_decode_performance, test_driver_load,
+ test_ParamTypeInfObj, test_WS_ParamClass,
+ test_Defed_ObjectIdentifier, testSelectionType,
+ testSSLspecs, testNortel, test_undecoded_rest,
+ test_inline, testTcapsystem, testNBAPsystem,
+ test_compile_options, testDoubleEllipses,
+ test_modified_x420, testX420, test_x691, ticket_6143,
+ testExtensionAdditionGroup] ++ common() ++ particular().
+
+groups() ->
+ [{option_tests, [],
+ [test_compile_options, ticket_6143]},
+ {infobj, [],
+ [testInfObjectClass, testParameterizedInfObj,
+ testMergeCompile, testobj, testDeepTConstr]},
+ {performance, [],
+ [testTimer_ber, testTimer_ber_opt_driver, testTimer_per,
+ testTimer_per_opt, testTimer_uper_bin]},
+ {bugs, [],
+ [test_ParamTypeInfObj, test_WS_ParamClass,
+ test_Defed_ObjectIdentifier]},
+ {compile, [],
+ [c_syntax, c_string_per, c_string_ber,
+ c_implicit_before_choice]},
+ {ber, [],
+ [ber_choiceinseq, ber_optional, ber_optional_keyed_list,
+ ber_other]},
+ {app_test, [], [{asn1_app_test, all}]},
+ {appup_test, [], [{asn1_appup_test, all}]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%all(suite) -> [test_inline,testNBAPsystem,test_compile_options,ticket_6143].
+
+
+init_per_testcase(Func,Config) ->
+ %%?line test_server:format("Func: ~p~n",[Func]),
+ ?line {ok, _} = file:read_file_info(filename:join([?config(priv_dir,Config)])),
+ ?line code:add_patha(?config(priv_dir,Config)),
+ Dog=
+ case Func of
+ testX420 ->
+ test_server:timetrap({minutes,60}); % 60 minutes
+ _ ->
+ test_server:timetrap({minutes,30}) % 60 minutes
+ end,
+%% Dog=test_server:timetrap(1800000), % 30 minutes
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Func,Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog).
+
+
+testPrim(suite) -> [];
+testPrim(Config) ->
+ ?line testPrim:compile(Config,?BER,[]),
+ ?line testPrim_cases(?BER),
+ ?line ?ber_driver(?BER,testPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrim_cases(?BER)),
+ ?line testPrim:compile(Config,?PER,[]),
+ ?line testPrim_cases(?PER),
+ ?line ?per_bit_opt(testPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrim_cases(?PER)),
+ ?line ?uper_bin(testPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrim_cases(uper_bin)),
+ ?line testPrim:compile(Config,?PER,[optimize]),
+ ?line testPrim_cases(?PER).
+
+testPrim_cases(Rules) ->
+ ?line testPrim:bool(Rules),
+ ?line testPrim:int(Rules),
+ ?line testPrim:enum(Rules),
+ ?line testPrim:obj_id(Rules),
+ ?line testPrim:rel_oid(Rules),
+ ?line testPrim:null(Rules),
+ ?line testPrim:real(Rules).
+
+
+testCompactBitString(suite) -> [];
+testCompactBitString(Config) ->
+
+ ?line testCompactBitString:compile(Config,?BER,[compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?BER),
+
+ ?line ?ber_driver(?BER,testCompactBitString:compile(Config,?BER,[compact_bit_string,driver])),
+ ?line ?ber_driver(?BER,testCompactBitString:compact_bit_string(?BER)),
+
+ ?line testCompactBitString:compile(Config,?PER,[compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?PER),
+ ?line testCompactBitString:bit_string_unnamed(?PER),
+
+ ?line ?per_bit_opt(testCompactBitString:compile(Config,?PER,
+ [compact_bit_string,optimize])),
+ ?line ?per_bit_opt(testCompactBitString:compact_bit_string(?PER)),
+ ?line ?per_bit_opt(testCompactBitString:bit_string_unnamed(?PER)),
+ ?line ?per_bit_opt(testCompactBitString:ticket_7734(?PER)),
+
+ ?line ?uper_bin(testCompactBitString:compile(Config,uper_bin,
+ [compact_bit_string])),
+ ?line ?uper_bin(testCompactBitString:compact_bit_string(uper_bin)),
+ ?line ?uper_bin(testCompactBitString:bit_string_unnamed(uper_bin)),
+
+ ?line testCompactBitString:compile(Config,?PER,[optimize,compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?PER),
+ ?line testCompactBitString:bit_string_unnamed(?PER),
+
+ ?line testCompactBitString:otp_4869(?PER).
+
+
+testPrimStrings(suite) -> [];
+testPrimStrings(Config) ->
+
+ ?line testPrimStrings:compile(Config,?BER,[]),
+ ?line testPrimStrings_cases(?BER),
+ ?line testPrimStrings:more_strings(?BER), %% these are not implemented in per yet
+ ?line ?ber_driver(?BER,testPrimStrings:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimStrings_cases(?BER)),
+ ?line ?ber_driver(?BER,testPrimStrings:more_strings(?BER)),
+
+ ?line testPrimStrings:compile(Config,?PER,[]),
+ ?line testPrimStrings_cases(?PER),
+
+ ?line ?per_bit_opt(testPrimStrings:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimStrings_cases(?PER)),
+
+ ?line ?uper_bin(testPrimStrings:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimStrings_cases(uper_bin)),
+
+ ?line testPrimStrings:compile(Config,?PER,[optimize]),
+ ?line testPrimStrings_cases(?PER).
+
+testPrimStrings_cases(Rules) ->
+ ?line testPrimStrings:bit_string(Rules),
+ ?line testPrimStrings:bit_string_unnamed(Rules),
+ ?line testPrimStrings:octet_string(Rules),
+ ?line testPrimStrings:numeric_string(Rules),
+ ?line testPrimStrings:other_strings(Rules),
+ ?line testPrimStrings:universal_string(Rules),
+ ?line testPrimStrings:bmp_string(Rules),
+ ?line testPrimStrings:times(Rules),
+ ?line testPrimStrings:utf8_string(Rules).
+
+
+
+testPrimExternal(suite) -> [];
+testPrimExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testPrimExternal:compile(Config,?BER,[]),
+ ?line testPrimExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testPrimExternal:compile(Config,?PER,[]),
+ ?line testPrimExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testPrimExternal:compile(Config,?PER,[optimize]),
+ ?line testPrimExternal_cases(?PER).
+
+testPrimExternal_cases(Rules) ->
+ ?line testPrimExternal:external(Rules).
+
+
+
+
+testChoPrim(suite) -> [];
+testChoPrim(Config) ->
+
+ ?line testChoPrim:compile(Config,?BER,[]),
+ ?line testChoPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoPrim_cases(?BER)),
+
+ ?line testChoPrim:compile(Config,?PER,[]),
+ ?line testChoPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testChoPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoPrim_cases(?PER)),
+
+ ?line ?uper_bin(testChoPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoPrim_cases(uper_bin)),
+
+ ?line testChoPrim:compile(Config,?PER,[optimize]),
+ ?line testChoPrim_cases(?PER).
+
+testChoPrim_cases(Rules) ->
+ ?line testChoPrim:bool(Rules),
+ ?line testChoPrim:int(Rules).
+
+
+
+testChoExtension(suite) -> [];
+testChoExtension(Config) ->
+
+ ?line testChoExtension:compile(Config,?BER,[]),
+ ?line testChoExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExtension_cases(?BER)),
+
+ ?line testChoExtension:compile(Config,?PER,[]),
+ ?line testChoExtension_cases(?PER),
+
+ ?line ?per_bit_opt(testChoExtension:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExtension_cases(?PER)),
+
+ ?line ?uper_bin(testChoExtension:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExtension_cases(uper_bin)),
+
+ ?line testChoExtension:compile(Config,?PER,[optimize]),
+ ?line testChoExtension_cases(?PER).
+
+testChoExtension_cases(Rules) ->
+ ?line testChoExtension:extension(Rules).
+
+
+
+testChoExternal(suite) -> [];
+testChoExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testChoExternal:compile(Config,?BER,[]),
+ ?line testChoExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testChoExternal:compile(Config,?PER,[]),
+ ?line testChoExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testChoExternal:compile(Config,?PER,[optimize]),
+ ?line testChoExternal_cases(?PER).
+
+
+testChoExternal_cases(Rules) ->
+ ?line testChoExternal:external(Rules).
+
+
+
+testChoOptional(suite) -> [];
+testChoOptional(Config) ->
+
+ ?line testChoOptional:compile(Config,?BER,[]),
+ ?line testChoOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoOptional_cases(?BER)),
+
+ ?line testChoOptional:compile(Config,?PER,[]),
+ ?line testChoOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testChoOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoOptional_cases(?PER)),
+
+ ?line ?uper_bin(testChoOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoOptional_cases(uper_bin)),
+
+ ?line testChoOptional:compile(Config,?PER,[optimize]),
+ ?line testChoOptional_cases(?PER).
+
+testChoOptional_cases(Rules) ->
+ ?line testChoOptional:optional(Rules).
+
+testChoOptionalImplicitTag(suite) -> [];
+testChoOptionalImplicitTag(Config) ->
+ %% Only meaningful for ?BER
+ ?line testChoOptionalImplicitTag:compile(Config,?BER),
+ ?line testChoOptionalImplicitTag:optional(?BER).
+
+
+testChoRecursive(suite) -> [];
+testChoRecursive(Config) ->
+
+ ?line testChoRecursive:compile(Config,?BER,[]),
+ ?line testChoRecursive_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoRecursive:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoRecursive_cases(?BER)),
+
+ ?line testChoRecursive:compile(Config,?PER,[]),
+ ?line testChoRecursive_cases(?PER),
+
+ ?line ?per_bit_opt(testChoRecursive:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoRecursive_cases(?PER)),
+
+ ?line ?uper_bin(testChoRecursive:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoRecursive_cases(uper_bin)),
+
+ ?line testChoRecursive:compile(Config,?PER,[optimize]),
+ ?line testChoRecursive_cases(?PER).
+
+testChoRecursive_cases(Rules) ->
+ ?line testChoRecursive:recursive(Rules).
+
+
+
+testChoTypeRefCho(suite) -> [];
+testChoTypeRefCho(Config) ->
+
+ ?line testChoTypeRefCho:compile(Config,?BER,[]),
+ ?line testChoTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefCho_cases(?BER)),
+
+ ?line testChoTypeRefCho:compile(Config,?PER,[]),
+ ?line testChoTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefCho_cases(uper_bin)),
+
+ ?line testChoTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefCho_cases(?PER).
+
+testChoTypeRefCho_cases(Rules) ->
+ ?line testChoTypeRefCho:choice(Rules).
+
+
+
+testChoTypeRefPrim(suite) -> [];
+testChoTypeRefPrim(Config) ->
+
+ ?line testChoTypeRefPrim:compile(Config,?BER,[]),
+ ?line testChoTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefPrim_cases(?BER)),
+
+ ?line testChoTypeRefPrim:compile(Config,?PER,[]),
+ ?line testChoTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefPrim_cases(uper_bin)),
+
+ ?line testChoTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefPrim_cases(?PER).
+
+testChoTypeRefPrim_cases(Rules) ->
+ ?line testChoTypeRefPrim:prim(Rules).
+
+
+
+testChoTypeRefSeq(suite) -> [];
+testChoTypeRefSeq(Config) ->
+
+ ?line testChoTypeRefSeq:compile(Config,?BER,[]),
+ ?line testChoTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefSeq_cases(?BER)),
+
+ ?line testChoTypeRefSeq:compile(Config,?PER,[]),
+ ?line testChoTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefSeq_cases(uper_bin)),
+
+ ?line testChoTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefSeq_cases(?PER).
+
+testChoTypeRefSeq_cases(Rules) ->
+ ?line testChoTypeRefSeq:seq(Rules).
+
+
+
+testChoTypeRefSet(suite) -> [];
+testChoTypeRefSet(Config) ->
+
+ ?line testChoTypeRefSet:compile(Config,?BER,[]),
+ ?line testChoTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefSet_cases(?BER)),
+
+ ?line testChoTypeRefSet:compile(Config,?PER,[]),
+ ?line testChoTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefSet_cases(uper_bin)),
+
+ ?line testChoTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefSet_cases(?PER).
+
+testChoTypeRefSet_cases(Rules) ->
+ ?line testChoTypeRefSet:set(Rules).
+
+
+
+testDef(suite) -> [];
+testDef(Config) ->
+
+ ?line testDef:compile(Config,?BER,[]),
+ ?line testDef_cases(?BER),
+
+ ?line ?ber_driver(?BER,testDef:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDef_cases(?BER)),
+
+ ?line testDef:compile(Config,?PER,[]),
+ ?line testDef_cases(?PER),
+
+ ?line ?per_bit_opt(testDef:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDef_cases(?PER)),
+
+ ?line ?uper_bin(testDef:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDef_cases(uper_bin)),
+
+ ?line testDef:compile(Config,?PER,[optimize]),
+ ?line testDef_cases(?PER).
+
+testDef_cases(Rules) ->
+ ?line testDef:main(Rules).
+
+
+
+testOpt(suite) -> [];
+testOpt(Config) ->
+
+ ?line testOpt:compile(Config,?BER),
+ ?line testOpt_cases(?BER),
+
+ ?line testOpt:compile(Config,?PER),
+ ?line testOpt_cases(?PER).
+
+testOpt_cases(Rules) ->
+ ?line testOpt:main(Rules).
+
+
+testEnumExt(suite) -> [];
+testEnumExt(Config) ->
+
+ ?line testEnumExt:compile(Config,?BER,[]),
+ ?line testEnumExt:main(?BER),
+
+ ?line ?ber_driver(?BER,testEnumExt:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testEnumExt:main(?BER)),
+
+ ?line testEnumExt:compile(Config,?PER,[]),
+ ?line testEnumExt:main(?PER),
+
+ ?line ?per_bit_opt(testEnumExt:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testEnumExt:main(?PER)),
+
+ ?line ?uper_bin(testEnumExt:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testEnumExt:main(uper_bin)),
+
+ ?line testEnumExt:compile(Config,?PER,[optimize]),
+ ?line testEnumExt:main(?PER).
+
+testSeqDefault(doc) -> ["Test of OTP-2523 ENUMERATED with extensionmark."];
+testSeqDefault(suite) -> [];
+testSeqDefault(Config) ->
+
+ ?line testSeqDefault:compile(Config,?BER,[]),
+ ?line testSeqDefault_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqDefault:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqDefault_cases(?BER)),
+
+ ?line testSeqDefault:compile(Config,?PER,[]),
+ ?line testSeqDefault_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqDefault:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqDefault_cases(?PER)),
+
+ ?line ?uper_bin(testSeqDefault:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqDefault_cases(uper_bin)),
+
+ ?line testSeqDefault:compile(Config,?PER,[optimize]),
+ ?line testSeqDefault_cases(?PER).
+
+testSeqDefault_cases(Rules) ->
+ ?line testSeqDefault:main(Rules).
+
+
+
+testSeqExtension(suite) -> [];
+testSeqExtension(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqExtension:compile(Config,?BER,[]),
+ ?line testSeqExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExtension_cases(?BER)).
+
+testSeqExtension_cases(Rules) ->
+ ?line testSeqExtension:main(Rules).
+
+
+
+testSeqExternal(suite) -> [];
+testSeqExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqExternal:compile(Config,?BER,[]),
+ ?line testSeqExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExternal_cases(?BER)).
+
+testSeqExternal_cases(Rules) ->
+ ?line testSeqExternal:main(Rules).
+
+
+testSeqOptional(suite) -> [];
+testSeqOptional(Config) ->
+
+ ?line testSeqOptional:compile(Config,?BER,[]),
+ ?line testSeqOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOptional_cases(?BER)),
+
+ ?line testSeqOptional:compile(Config,?PER,[]),
+ ?line testSeqOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOptional_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOptional_cases(uper_bin)),
+
+ ?line testSeqOptional:compile(Config,?PER,[optimize]),
+ ?line testSeqOptional_cases(?PER).
+
+testSeqOptional_cases(Rules) ->
+ ?line testSeqOptional:main(Rules).
+
+
+
+testSeqPrim(suite) -> [];
+testSeqPrim(Config) ->
+
+ ?line testSeqPrim:compile(Config,?BER,[]),
+ ?line testSeqPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqPrim_cases(?BER)),
+
+ ?line testSeqPrim:compile(Config,?PER,[]),
+ ?line testSeqPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSeqPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqPrim_cases(uper_bin)),
+
+ ?line testSeqPrim:compile(Config,?PER,[optimize]),
+ ?line testSeqPrim_cases(?PER).
+
+testSeqPrim_cases(Rules) ->
+ ?line testSeqPrim:main(Rules).
+
+
+testSeq2738(doc) -> ["Test of OTP-2738 Detect corrupt optional component."];
+testSeq2738(suite) -> [];
+testSeq2738(Config) ->
+
+ ?line testSeq2738:compile(Config,?BER,[]),
+ ?line testSeq2738_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeq2738:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeq2738_cases(?BER)),
+
+ ?line testSeq2738:compile(Config,?PER,[]),
+ ?line testSeq2738_cases(?PER),
+
+ ?line ?per_bit_opt(testSeq2738:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeq2738_cases(?PER)),
+
+ ?line ?uper_bin(testSeq2738:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeq2738_cases(uper_bin)),
+
+ ?line testSeq2738:compile(Config,?PER,[optimize]),
+ ?line testSeq2738_cases(?PER).
+
+testSeq2738_cases(Rules) ->
+ ?line testSeq2738:main(Rules).
+
+
+testSeqTag(suite) -> [];
+testSeqTag(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqTag:compile(Config,?BER,[]),
+ ?line testSeqTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqTag:compile(Config,?PER,[]),
+ ?line testSeqTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqTag:compile(Config,?PER,[optimize]),
+ ?line testSeqTag_cases(?PER).
+
+testSeqTag_cases(Rules) ->
+ ?line testSeqTag:main(Rules).
+
+
+
+
+testSeqTypeRefCho(suite) -> [];
+testSeqTypeRefCho(Config) ->
+
+ ?line testSeqTypeRefCho:compile(Config,?BER,[]),
+ ?line testSeqTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefCho_cases(?BER)),
+
+ ?line testSeqTypeRefCho:compile(Config,?PER,[]),
+ ?line testSeqTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefCho_cases(uper_bin)),
+
+ ?line testSeqTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefCho_cases(?PER).
+
+testSeqTypeRefCho_cases(Rules) ->
+ ?line testSeqTypeRefCho:main(Rules).
+
+
+
+testSeqTypeRefPrim(suite) -> [];
+testSeqTypeRefPrim(Config) ->
+
+ ?line testSeqTypeRefPrim:compile(Config,?BER,[]),
+ ?line testSeqTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefPrim_cases(?BER)),
+
+ ?line testSeqTypeRefPrim:compile(Config,?PER,[]),
+ ?line testSeqTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefPrim_cases(uper_bin)),
+
+ ?line testSeqTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefPrim_cases(?PER).
+
+testSeqTypeRefPrim_cases(Rules) ->
+ ?line testSeqTypeRefPrim:main(Rules).
+
+
+
+testSeqTypeRefSeq(suite) -> [];
+testSeqTypeRefSeq(Config) ->
+
+ ?line testSeqTypeRefSeq:compile(Config,?BER,[]),
+ ?line testSeqTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefSeq_cases(?BER)),
+
+ ?line testSeqTypeRefSeq:compile(Config,?PER,[]),
+ ?line testSeqTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefSeq_cases(uper_bin)),
+
+ ?line testSeqTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefSeq_cases(?PER).
+
+testSeqTypeRefSeq_cases(Rules) ->
+ ?line testSeqTypeRefSeq:main(Rules).
+
+
+
+testSeqTypeRefSet(suite) -> [];
+testSeqTypeRefSet(Config) ->
+
+ ?line testSeqTypeRefSet:compile(Config,?BER,[]),
+ ?line testSeqTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefSet_cases(?BER)),
+
+ ?line testSeqTypeRefSet:compile(Config,?PER,[]),
+ ?line testSeqTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefSet_cases(uper_bin)),
+
+ ?line testSeqTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefSet_cases(?PER).
+
+testSeqTypeRefSet_cases(Rules) ->
+ ?line testSeqTypeRefSet:main(Rules).
+
+
+
+
+testSeqOf(suite) -> [];
+testSeqOf(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOf:compile(Config,?BER,[]),
+ ?line testSeqOf_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOf:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOf_cases(?BER)),
+
+ ?line testSeqOf:compile(Config,?PER,[]),
+ ?line testSeqOf_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOf:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOf_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOf:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOf_cases(uper_bin)),
+
+ ?line testSeqOf:compile(Config,?PER,[optimize]),
+ ?line testSeqOf_cases(?PER).
+
+testSeqOf_cases(Rules) ->
+ ?line testSeqOf:main(Rules).
+
+
+
+
+testSeqOfCho(suite) -> [];
+testSeqOfCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOfCho:compile(Config,?BER,[]),
+ ?line testSeqOfCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOfCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfCho_cases(?BER)),
+
+ ?line testSeqOfCho:compile(Config,?PER,[]),
+ ?line testSeqOfCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOfCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfCho_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOfCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfCho_cases(uper_bin)),
+
+ ?line testSeqOfCho:compile(Config,?PER,[optimize]),
+ ?line testSeqOfCho_cases(?PER).
+
+testSeqOfIndefinite(suite) -> [];
+testSeqOfIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOfIndefinite:compile(Config,?BER,[]),
+ ?line testSeqOfIndefinite:main(),
+
+ ?line ?ber_driver(?BER,testSeqOfIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfIndefinite:main()).
+
+testSeqOfCho_cases(Rules) ->
+ ?line testSeqOfCho:main(Rules).
+
+
+testSeqOfExternal(suite) -> [];
+testSeqOfExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqOfExternal:compile(Config,?BER,[]),
+ ?line testSeqOfExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqOfExternal:compile(Config,?PER,[]),
+ ?line testSeqOfExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfExternal_cases(?PER).
+
+testSeqOfExternal_cases(Rules) ->
+ ?line testSeqOfExternal:main(Rules).
+
+
+
+testSeqOfTag(suite) -> [];
+testSeqOfTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqOfTag:compile(Config,?BER,[]),
+ ?line testSeqOfTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqOfTag:compile(Config,?PER,[]),
+ ?line testSeqOfTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfTag:compile(Config,?PER,[optimize]),
+ ?line testSeqOfTag_cases(?PER).
+
+testSeqOfTag_cases(Rules) ->
+ ?line testSeqOfTag:main(Rules).
+
+
+
+
+testSetDefault(suite) -> [];
+testSetDefault(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetDefault:compile(Config,?BER,[]),
+ ?line testSetDefault_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetDefault:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetDefault_cases(?BER)),
+
+ ?line testSetDefault:compile(Config,?PER,[]),
+ ?line testSetDefault_cases(?PER),
+
+ ?line ?per_bit_opt(testSetDefault:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetDefault_cases(?PER)),
+
+ ?line ?uper_bin(testSetDefault:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetDefault_cases(uper_bin)),
+
+ ?line testSetDefault:compile(Config,?PER,[optimize]),
+ ?line testSetDefault_cases(?PER).
+
+testSetDefault_cases(Rules) ->
+ ?line testSetDefault:main(Rules).
+
+
+testParamBasic(suite) -> [];
+testParamBasic(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testParamBasic:compile(Config,?BER,[]),
+ ?line testParamBasic_cases(?BER),
+
+ ?line ?ber_driver(?BER,testParamBasic:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testParamBasic_cases(?BER)),
+
+ ?line testParamBasic:compile(Config,?PER,[]),
+ ?line testParamBasic_cases(?PER),
+
+ ?line ?per_bit_opt(testParamBasic:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testParamBasic_cases(?PER)),
+
+ ?line ?uper_bin(testParamBasic:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testParamBasic_cases(uper_bin)),
+
+ ?line testParamBasic:compile(Config,?PER,[optimize]),
+ ?line testParamBasic_cases(?PER).
+
+
+testParamBasic_cases(Rules) ->
+ ?line testParamBasic:main(Rules).
+
+testSetExtension(suite) -> [];
+testSetExtension(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetExtension:compile(Config,?BER,[]),
+ ?line testSetExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExtension_cases(?BER)).
+
+testSetExtension_cases(Rules) ->
+ ?line testSetExtension:main(Rules).
+
+
+testSetExternal(suite) -> [];
+testSetExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetExternal:compile(Config,?BER,[]),
+ ?line testSetExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExternal_cases(?BER)).
+
+testSetExternal_cases(Rules) ->
+ ?line testSetExternal:main(Rules).
+
+
+testSetOptional(suite) -> [];
+testSetOptional(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOptional:compile(Config,?BER,[]),
+ ?line testSetOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOptional_cases(?BER)),
+
+ ?line testSetOptional:compile(Config,?PER,[]),
+ ?line testSetOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOptional_cases(?PER)),
+
+ ?line ?uper_bin(testSetOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOptional_cases(uper_bin)),
+
+ ?line testSetOptional:compile(Config,?PER,[optimize]),
+ ?line testSetOptional_cases(?PER).
+
+testSetOptional_cases(Rules) ->
+ ?line ok = testSetOptional:ticket_7533(Rules),
+ ?line ok = testSetOptional:main(Rules).
+
+
+
+
+testSetPrim(suite) -> [];
+testSetPrim(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetPrim:compile(Config,?BER,[]),
+ ?line testSetPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetPrim_cases(?BER)),
+
+ ?line testSetPrim:compile(Config,?PER,[]),
+ ?line testSetPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSetPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSetPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetPrim_cases(uper_bin)),
+
+ ?line testSetPrim:compile(Config,?PER,[optimize]),
+ ?line testSetPrim_cases(?PER).
+
+testSetPrim_cases(Rules) ->
+ ?line testSetPrim:main(Rules).
+
+
+
+testSetTag(suite) -> [];
+testSetTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetTag:compile(Config,?BER,[]),
+ ?line testSetTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetTag:compile(Config,?PER,[]),
+ ?line testSetTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetTag:compile(Config,?PER,[optimize]),
+ ?line testSetTag_cases(?PER).
+
+testSetTag_cases(Rules) ->
+ ?line testSetTag:main(Rules).
+
+
+
+testSetTypeRefCho(suite) -> [];
+testSetTypeRefCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefCho:compile(Config,?BER,[]),
+ ?line testSetTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefCho_cases(?BER)),
+
+ ?line testSetTypeRefCho:compile(Config,?PER,[]),
+ ?line testSetTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefCho_cases(uper_bin)),
+
+ ?line testSetTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefCho_cases(?PER).
+
+testSetTypeRefCho_cases(Rules) ->
+ ?line testSetTypeRefCho:main(Rules).
+
+
+
+testSetTypeRefPrim(suite) -> [];
+testSetTypeRefPrim(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefPrim:compile(Config,?BER,[]),
+ ?line testSetTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefPrim_cases(?BER)),
+
+ ?line testSetTypeRefPrim:compile(Config,?PER,[]),
+ ?line testSetTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefPrim_cases(uper_bin)),
+
+ ?line testSetTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefPrim_cases(?PER).
+
+testSetTypeRefPrim_cases(Rules) ->
+ ?line testSetTypeRefPrim:main(Rules).
+
+
+
+testSetTypeRefSeq(suite) -> [];
+testSetTypeRefSeq(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefSeq:compile(Config,?BER,[]),
+ ?line testSetTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefSeq_cases(?BER)),
+
+ ?line testSetTypeRefSeq:compile(Config,?PER,[]),
+ ?line testSetTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefSeq_cases(uper_bin)),
+
+ ?line testSetTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefSeq_cases(?PER).
+
+testSetTypeRefSeq_cases(Rules) ->
+ ?line testSetTypeRefSeq:main(Rules).
+
+
+
+testSetTypeRefSet(suite) -> [];
+testSetTypeRefSet(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefSet:compile(Config,?BER,[]),
+ ?line testSetTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefSet_cases(?BER)),
+
+ ?line testSetTypeRefSet:compile(Config,?PER,[]),
+ ?line testSetTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefSet_cases(uper_bin)),
+
+ ?line testSetTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefSet_cases(?PER).
+
+testSetTypeRefSet_cases(Rules) ->
+ ?line testSetTypeRefSet:main(Rules).
+
+
+
+testSetOf(suite) -> [];
+testSetOf(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOf:compile(Config,?BER,[]),
+ ?line testSetOf_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOf:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOf_cases(?BER)),
+
+ ?line testSetOf:compile(Config,?PER,[]),
+ ?line testSetOf_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOf:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOf_cases(?PER)),
+
+ ?line ?uper_bin(testSetOf:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOf_cases(uper_bin)),
+
+ ?line testSetOf:compile(Config,?PER,[optimize]),
+ ?line testSetOf_cases(?PER).
+
+testSetOf_cases(Rules) ->
+ ?line testSetOf:main(Rules).
+
+
+
+testSetOfCho(suite) -> [];
+testSetOfCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOfCho:compile(Config,?BER,[]),
+ ?line testSetOfCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOfCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfCho_cases(?BER)),
+
+ ?line testSetOfCho:compile(Config,?PER,[]),
+ ?line testSetOfCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOfCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfCho_cases(?PER)),
+
+ ?line ?uper_bin(testSetOfCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfCho_cases(uper_bin)),
+
+ ?line testSetOfCho:compile(Config,?PER,[optimize]),
+ ?line testSetOfCho_cases(?PER).
+
+testSetOfCho_cases(Rules) ->
+ ?line testSetOfCho:main(Rules).
+
+
+testSetOfExternal(suite) -> [];
+testSetOfExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetOfExternal:compile(Config,?BER,[]),
+ ?line testSetOfExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetOfExternal:compile(Config,?PER,[]),
+ ?line testSetOfExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfExternal_cases(?PER).
+
+testSetOfExternal_cases(Rules) ->
+ ?line testSetOfExternal:main(Rules).
+
+
+
+
+testSetOfTag(suite) -> [];
+testSetOfTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetOfTag:compile(Config,?BER,[]),
+ ?line testSetOfTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetOfTag:compile(Config,?PER,[]),
+ ?line testSetOfTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfTag:compile(Config,?PER,[optimize]),
+ ?line testSetOfTag_cases(?PER).
+
+testSetOfTag_cases(Rules) ->
+ ?line testSetOfTag:main(Rules).
+
+
+c_syntax(suite) -> [];
+c_syntax(Config) ->
+ ?line DataDir% ?line testExternal:compile(Config,?PER),
+% ?line testPrimExternal:compile(Config,?PER),
+% ?line testPrimExternal_cases(?PER).
+ = ?config(data_dir,Config),
+ ?line _TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line {error,_R1} = asn1ct:compile(filename:join(DataDir,"Syntax")),
+ ?line {error,_R2} = asn1ct:compile(filename:join(DataDir,"BadTypeEnding")),
+ ?line {error,_R3} = asn1ct:compile(filename:join(DataDir,
+ "BadValueAssignment1")),
+ ?line {error,_R4} = asn1ct:compile(filename:join(DataDir,
+ "BadValueAssignment2")),
+ ?line {error,_R5} = asn1ct:compile(filename:join(DataDir,
+ "BadValueSet")),
+ ?line {error,_R6} = asn1ct:compile(filename:join(DataDir,
+ "ChoiceBadExtension")),
+ ?line {error,_R7} = asn1ct:compile(filename:join(DataDir,
+ "EnumerationBadExtension")),
+ ?line {error,_R8} = asn1ct:compile(filename:join(DataDir,
+ "Example")),
+ ?line {error,_R9} = asn1ct:compile(filename:join(DataDir,
+ "Export1")),
+ ?line {error,_R10} = asn1ct:compile(filename:join(DataDir,
+ "MissingEnd")),
+ ?line {error,_R11} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComma")),
+ ?line {error,_R12} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComponentName")),
+ ?line {error,_R13} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComponentType")),
+ ?line {error,_R14} = asn1ct:compile(filename:join(DataDir,
+ "SeqBadComma")).
+
+
+c_string_per(suite) -> [];
+c_string_per(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"String"),[?PER,{outdir,TempDir}]).
+
+c_string_ber(suite) -> [];
+c_string_ber(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"String"),[?BER,{outdir,TempDir}]).
+
+
+c_implicit_before_choice(suite) -> [];
+c_implicit_before_choice(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line {error,_R2} = asn1ct:compile(filename:join(DataDir,"CCSNARG3"),[?BER,{outdir,TempDir}]).
+
+parse(suite) -> [];
+parse(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ M1 = test_modules(),
+% M2 = parse_modules(),
+ ?line ok = parse1(M1,DataDir,OutDir).
+
+parse1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[abs,{outdir,OutDir}]),
+ parse1(T,DataDir,OutDir);
+parse1([],_,_) ->
+ ok.
+
+per(suite) -> [];
+per(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = per1(per_modules(),DataDir,OutDir),
+ ?line ?per_bit_opt(per1_bit_opt(per_modules(),DataDir,OutDir)),
+ ?line ok = per1_opt(per_modules(),DataDir,OutDir).
+
+
+per1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1(T,DataDir,OutDir);
+per1([],_,_) ->
+ ok.
+
+per1_bit_opt([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,optimize,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1_bit_opt(T,DataDir,OutDir);
+per1_bit_opt([],_,_) ->
+ ok.
+
+per1_opt([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,optimized,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1_opt(T,DataDir,OutDir);
+per1_opt([],_,_) ->
+ ok.
+
+
+ber_choiceinseq(suite) ->[];
+ber_choiceinseq(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"ChoiceInSeq"),[?BER,{outdir,OutDir}]).
+
+ber_optional(suite) ->[];
+ber_optional(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"SOpttest"),[?BER,{outdir,OutDir}]),
+ ?line V = {'S',{'A',10,asn1_NOVALUE,asn1_NOVALUE},
+ {'B',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ {'C',asn1_NOVALUE,111,asn1_NOVALUE}},
+ ?line {ok,B} = asn1_wrapper:encode('SOpttest','S',V),
+ ?line Bytes = lists:flatten(B),
+ ?line V2 = asn1_wrapper:decode('SOpttest','S',Bytes),
+ ?line ok = eq(V,element(2,V2)).
+
+ber_optional_keyed_list(suite) ->[];
+ber_optional_keyed_list(Config) ->
+ case ?BER of
+ ber_bin_v2 -> ok;
+ _ ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"SOpttest"),
+ [?BER,keyed_list,{outdir,OutDir}]),
+ ?line Vrecord = {'S',{'A',10,asn1_NOVALUE,asn1_NOVALUE},
+ {'B',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ {'C',asn1_NOVALUE,111,asn1_NOVALUE}},
+ ?line V = [ {a,[{scriptKey,10}]},
+ {b,[]},
+ {c,[{callingPartysCategory,111}]} ],
+ ?line {ok,B} = asn1_wrapper:encode('SOpttest','S',V),
+ ?line Bytes = lists:flatten(B),
+ ?line V2 = asn1_wrapper:decode('SOpttest','S',Bytes),
+ ?line ok = eq(Vrecord,element(2,V2))
+ end.
+
+
+eq(V,V) ->
+ ok.
+
+
+ber_other(suite) ->[];
+ber_other(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = ber1(ber_modules(),DataDir,OutDir).
+
+
+ber1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?BER,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ ber1(T,DataDir,OutDir);
+ber1([],_,_) ->
+ ok.
+
+default_per(suite) ->[];
+default_per(Config) ->
+ default1(?PER,Config,[]).
+
+default_per_opt(suite) -> [];
+default_per_opt(Config) ->
+ ?per_bit_opt(default1(?PER,Config,[optimize])),
+ default1(?PER,Config,[optimize]).
+
+default_ber(suite) ->[];
+default_ber(Config) ->
+ default1(?BER,Config,[]).
+
+default1(Rule,Config,Options) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "Def",[Rule,{outdir,OutDir}]++Options),
+ ?line {ok,Bytes1} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true,
+ bool1 = true,
+ bool2 = true,
+ bool3 = true}),
+ ?line {ok,{'Def1',true,true,true,true}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes1)),
+
+ ?line {ok,Bytes2} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true}),
+ ?line {ok,{'Def1',true,false,false,false}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes2)),
+
+ ?line {ok,Bytes3} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true,bool2=false}),
+ ?line {ok,{'Def1',true,false,false,false}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes3)).
+
+
+value_test(suite) ->[];
+value_test(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "ObjIdValues",[?BER,{outdir,OutDir}]),
+ ?line {ok,_} = asn1_wrapper:encode('ObjIdValues','ObjIdType','ObjIdValues':'mobileDomainId'()),
+ ?line ok = asn1ct:compile(DataDir ++ "ObjIdValues",[?PER,{outdir,OutDir}]),
+ ?line {ok,_} = asn1_wrapper:encode('ObjIdValues','ObjIdType','ObjIdValues':'mobileDomainId'()),
+ ?line ok = test_bad_values:tests(Config),
+ ok.
+
+
+constructed(suite) ->
+ [];
+constructed(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "Constructed",[?BER,{outdir,OutDir}]),
+ ?line {ok,B} = asn1_wrapper:encode('Constructed','S',{'S',false}),
+ ?line [40,3,1,1,0] = lists:flatten(B),
+ ?line {ok,B1} = asn1_wrapper:encode('Constructed','S2',{'S2',false}),
+ ?line [40,5,48,3,1,1,0] = lists:flatten(B1),
+ ?line {ok,B2} = asn1_wrapper:encode('Constructed','I',10),
+ ?line [136,1,10] = lists:flatten(B2),
+ ok.
+
+ber_decode_error(suite) -> [];
+ber_decode_error(Config) ->
+ ?line ok = ber_decode_error:compile(Config,?BER,[]),
+ ?line ok = ber_decode_error:run([]),
+
+ ?line ok = ?ber_driver(?BER,ber_decode_error:compile(Config,?BER,[driver])),
+ ?line ok = ?ber_driver(?BER,ber_decode_error:run([driver])),
+ ok.
+
+h323test(suite) ->
+ [];
+h323test(Config) ->
+ ?line ok = h323test:compile(Config,?PER,[]),
+ ?line ok = h323test:run(?PER),
+ ?line ?per_bit_opt(h323test:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(h323test:run(?PER)),
+ ?line ?uper_bin(h323test:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(h323test:run(uper_bin)),
+ ?line ok = h323test:compile(Config,?PER,[optimize]),
+ ?line ok = h323test:run(?PER),
+ ok.
+
+per_GeneralString(suite) ->
+ [];
+per_GeneralString(Config) ->
+ case erlang:module_loaded('MULTIMEDIA-SYSTEM-CONTROL') of
+ true ->
+ ok;
+ false ->
+ h323test:compile(Config,?PER,[])
+ end,
+ UI = [109,64,1,57],
+ ?line {ok,_V} = asn1_wrapper:decode('MULTIMEDIA-SYSTEM-CONTROL',
+ 'MultimediaSystemControlMessage',UI).
+
+per_open_type(suite) ->
+ [];
+per_open_type(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line ok = asn1ct:compile(DataDir ++ "OpenType",[?PER,{outdir,OutDir}]),
+ Stype = {'Stype',10,true},
+ ?line {ok,Bytes} = asn1_wrapper:encode('OpenType','Ot',Stype),
+ ?line {ok,Stype} = asn1_wrapper:decode('OpenType','Ot',Bytes),
+
+ ?line ?per_bit_opt(ok = asn1ct:compile(DataDir ++ "OpenType",
+ [?PER,optimize,{outdir,OutDir}])),
+ ?line ?per_bit_opt({ok,Bytes}=asn1_wrapper:encode('OpenType','Ot',Stype)),
+ ?line ?per_bit_opt({ok,Stype}=asn1_wrapper:decode('OpenType','Ot',Bytes)),
+
+ ?line ?uper_bin(ok = asn1ct:compile(DataDir ++ "OpenType",
+ [uper_bin,{outdir,OutDir}])),
+ ?line ?uper_bin({ok,Bytes}=asn1_wrapper:encode('OpenType','Ot',Stype)),
+ ?line ?uper_bin({ok,Stype}=asn1_wrapper:decode('OpenType','Ot',Bytes)),
+
+ ?line ok = asn1ct:compile(DataDir ++ "OpenType",
+ [?PER,optimize,{outdir,OutDir}]),
+ ?line {ok,Bytes} = asn1_wrapper:encode('OpenType','Ot',Stype),
+ ?line {ok,Stype} = asn1_wrapper:decode('OpenType','Ot',Bytes).
+
+testConstraints(suite) ->
+ [];
+testConstraints(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testConstraints:compile(Config,?BER,[]),
+ ?line testConstraints:int_constraints(?BER),
+
+ ?line ?ber_driver(?BER,testConstraints:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testConstraints:int_constraints(?BER)),
+
+ ?line testConstraints:compile(Config,?PER,[]),
+ ?line testConstraints:int_constraints(?PER),
+ ?line testConstraints:refed_NNL_name(?PER),
+
+ ?line ?per_bit_opt(testConstraints:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testConstraints:int_constraints(?PER)),
+ ?line ?per_bit_opt(testConstraints:refed_NNL_name(?PER)),
+
+ ?line ?uper_bin(testConstraints:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testConstraints:int_constraints(uper_bin)),
+ ?line ?uper_bin(testConstraints:refed_NNL_name(uper_bin)),
+
+ ?line testConstraints:compile(Config,?PER,[optimize]),
+ ?line testConstraints:int_constraints(?PER),
+ ?line testConstraints:refed_NNL_name(?PER).
+
+testSeqIndefinite(suite) -> [];
+testSeqIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqIndefinite:compile(Config,?BER,[]),
+ ?line testSeqIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testSeqIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqIndefinite:main(?BER)).
+
+testSetIndefinite(suite) -> [];
+testSetIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetIndefinite:compile(Config,?BER,[]),
+ ?line testSetIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testSetIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetIndefinite:main(?BER)).
+
+testChoiceIndefinite(suite) -> [];
+testChoiceIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testChoiceIndefinite:compile(Config,?BER,[]),
+ ?line testChoiceIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testChoiceIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoiceIndefinite:main(?BER)).
+
+testInfObjectClass(suite) ->
+ [];
+testInfObjectClass(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testInfObjectClass:compile(Config,?PER,[]),
+ ?line testInfObjectClass:main(?PER),
+ ?line testInfObj:compile(Config,?PER,[]),
+ ?line testInfObj:main(?PER),
+
+ ?line ?per_bit_opt(testInfObjectClass:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testInfObjectClass:main(?PER)),
+ ?line ?per_bit_opt(testInfObj:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testInfObj:main(?PER)),
+
+ ?line ?uper_bin(testInfObjectClass:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testInfObjectClass:main(uper_bin)),
+ ?line ?uper_bin(testInfObj:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testInfObj:main(uper_bin)),
+
+ ?line testInfObjectClass:compile(Config,?PER,[optimize]),
+ ?line testInfObjectClass:main(?PER),
+ ?line testInfObj:compile(Config,?PER,[optimize]),
+ ?line testInfObj:main(?PER),
+
+ ?line testInfObjectClass:compile(Config,?BER,[]),
+ ?line testInfObjectClass:main(?BER),
+ ?line testInfObj:compile(Config,?BER,[]),
+ ?line testInfObj:main(?BER),
+
+ ?line ?ber_driver(?BER,testInfObjectClass:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testInfObjectClass:main(?BER)),
+ ?line ?ber_driver(?BER,testInfObj:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testInfObj:main(?BER)),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?PER,[]),
+
+ ?line ?per_bit_opt(testInfObj:compile_RANAPfiles(Config,?PER,[optimize])),
+
+ ?line ?uper_bin(testInfObj:compile_RANAPfiles(Config,uper_bin,[])),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?PER,[optimize]),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?BER,[]).
+
+testParameterizedInfObj(suite) ->
+ [];
+testParameterizedInfObj(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testParameterizedInfObj:compile(Config,?PER,[]),
+ ?line testParameterizedInfObj:main(?PER),
+
+ ?line ?per_bit_opt(testParameterizedInfObj:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testParameterizedInfObj:main(?PER)),
+
+ ?line ?uper_bin(testParameterizedInfObj:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testParameterizedInfObj:main(uper_bin)),
+
+ ?line testParameterizedInfObj:compile(Config,?PER,[optimize]),
+ ?line testParameterizedInfObj:main(?PER),
+
+ ?line testParameterizedInfObj:compile(Config,?BER,[]),
+ ?line testParameterizedInfObj:main(?BER),
+
+ ?line ?ber_driver(?BER,testParameterizedInfObj:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testParameterizedInfObj:main(?BER)).
+
+testMergeCompile(suite) ->
+ [];
+testMergeCompile(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testMergeCompile:compile(Config,?PER,[]),
+ ?line testMergeCompile:main(?PER),
+ ?line testMergeCompile:mvrasn(?PER),
+
+ ?line ?per_bit_opt(testMergeCompile:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testMergeCompile:main(?PER)),
+ ?line ?per_bit_opt(testMergeCompile:mvrasn(?PER)),
+
+ ?line ?uper_bin(testMergeCompile:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testMergeCompile:main(uper_bin)),
+ ?line ?uper_bin(testMergeCompile:mvrasn(uper_bin)),
+
+ ?line testMergeCompile:compile(Config,?BER,[]),
+ ?line testMergeCompile:main(?BER),
+ ?line testMergeCompile:mvrasn(?BER),
+
+ ?line ?ber_driver(?BER,testMergeCompile:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testMergeCompile:main(?BER)),
+ ?line ?ber_driver(?BER,testMergeCompile:mvrasn(?BER)).
+
+testobj(suite) ->
+ [];
+testobj(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line ok = testRANAP:compile(Config,?PER,[]),
+ ?line ok = testRANAP:testobj(?PER),
+ ?line ok = testParameterizedInfObj:ranap(?PER),
+
+ ?line ?per_bit_opt(ok = testRANAP:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(ok = testRANAP:testobj(?PER)),
+ ?line ?per_bit_opt(ok = testParameterizedInfObj:ranap(?PER)),
+
+ ?line ?uper_bin(ok = testRANAP:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(ok = testRANAP:testobj(uper_bin)),
+ ?line ?uper_bin(ok = testParameterizedInfObj:ranap(uper_bin)),
+
+ ?line ok = testRANAP:compile(Config,?PER,[optimize]),
+ ?line ok = testRANAP:testobj(?PER),
+ ?line ok = testParameterizedInfObj:ranap(?PER),
+
+ ?line ok = testRANAP:compile(Config,?BER,[]),
+ ?line ok = testRANAP:testobj(?BER),
+ ?line ok = testParameterizedInfObj:ranap(?BER),
+
+ ?line ?ber_driver(?BER,testRANAP:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testRANAP:testobj(?BER)),
+ ?line ?ber_driver(?BER,testParameterizedInfObj:ranap(?BER)).
+
+
+testDeepTConstr(suite) ->
+ [];
+testDeepTConstr(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testDeepTConstr:compile(Config,?PER,[]),
+ ?line testDeepTConstr:main(?PER),
+
+ ?line ?per_bit_opt(testDeepTConstr:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDeepTConstr:main(?PER)),
+
+ ?line ?uper_bin(testDeepTConstr:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDeepTConstr:main(uper_bin)),
+
+ ?line testDeepTConstr:compile(Config,?PER,[optimize]),
+ ?line testDeepTConstr:main(?PER),
+
+ ?line testDeepTConstr:compile(Config,?BER,[]),
+ ?line testDeepTConstr:main(?BER),
+
+ ?line ?ber_driver(?BER,testDeepTConstr:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDeepTConstr:main(?BER)).
+
+testInvokeMod(suite) ->
+ [];
+testInvokeMod(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"PrimStrings"),[{outdir,OutDir}]),
+ ?line {ok,_Result1} = 'PrimStrings':encode('Bs1',[1,0,1,0]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"PrimStrings"),[?PER,{outdir,OutDir}]),
+ ?line {ok,_Result2} = 'PrimStrings':encode('Bs1',[1,0,1,0]).
+
+testExport(suite) ->
+ [];
+testExport(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line {error,{asn1,_Reason}} = asn1ct:compile(filename:join(DataDir,"IllegalExport"),[{outdir,OutDir}]).
+
+testImport(suite) ->
+ [];
+testImport(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line _OutDir = ?config(priv_dir,Config),
+ ?line {error,_} = asn1ct:compile(filename:join(DataDir,"ImportsFrom"),[?BER]),
+ ok.
+
+testMegaco(suite) ->
+ [];
+testMegaco(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ io:format("Config: ~p~n",[Config]),
+ ?line {ok,ModuleName1,ModuleName2} = testMegaco:compile(Config,?BER,[]),
+ ?line ok = testMegaco:main(ModuleName1,Config),
+ ?line ok = testMegaco:main(ModuleName2,Config),
+
+ case ?BER of
+ ber_bin_v2 ->
+ ?line {ok,ModuleName3,ModuleName4} = testMegaco:compile(Config,?BER,[driver]),
+ ?line ok = testMegaco:main(ModuleName3,Config),
+ ?line ok = testMegaco:main(ModuleName4,Config);
+ _-> ok
+ end,
+
+ ?line {ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,?PER,[]),
+ ?line ok = testMegaco:main(ModuleName5,Config),
+ ?line ok = testMegaco:main(ModuleName6,Config),
+
+ ?line ?per_bit_opt({ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(ok = testMegaco:main(ModuleName5,Config)),
+ ?line ?per_bit_opt(ok = testMegaco:main(ModuleName6,Config)),
+
+ ?line ?uper_bin({ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(ok = testMegaco:main(ModuleName5,Config)),
+ ?line ?uper_bin(ok = testMegaco:main(ModuleName6,Config)),
+
+ ?line {ok,ModuleName7,ModuleName8} = testMegaco:compile(Config,?PER,[optimize]),
+ ?line ok = testMegaco:main(ModuleName7,Config),
+ ?line ok = testMegaco:main(ModuleName8,Config).
+
+
+testMvrasn6(suite) -> [];
+testMvrasn6(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testMvrasn6:compile(Config,?BER),
+ ?line testMvrasn6:main().
+
+testContextSwitchingTypes(suite) -> [];
+testContextSwitchingTypes(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testContextSwitchingTypes:compile(Config,?BER,[]),
+ ?line testContextSwitchingTypes:test(),
+
+ ?line ?ber_driver(?BER,testContextSwitchingTypes:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testContextSwitchingTypes:test()),
+
+ ?line testContextSwitchingTypes:compile(Config,?PER,[]),
+ ?line testContextSwitchingTypes:test(),
+
+ ?line ?per_bit_opt(testContextSwitchingTypes:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testContextSwitchingTypes:test()),
+
+ ?line ?uper_bin(testContextSwitchingTypes:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testContextSwitchingTypes:test()),
+
+ ?line testContextSwitchingTypes:compile(Config,?PER,[optimize]),
+ ?line testContextSwitchingTypes:test().
+
+testTypeValueNotation(suite) -> [];
+testTypeValueNotation(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ case ?BER of
+ Ber when Ber == ber; Ber == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?BER,[]),
+ ?line testTypeValueNotation:main(?BER,dummy);
+ _ ->
+ ok
+ end,
+
+ ?line ?ber_driver(?BER,testTypeValueNotation:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testTypeValueNotation:main(?BER,optimize)),
+
+ case ?BER of
+ Ber2 when Ber2 == ber; Ber2 == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?PER,[]),
+ ?line testTypeValueNotation:main(?PER,dummy);
+ _ ->
+ ok
+ end,
+
+ ?line ?per_bit_opt(testTypeValueNotation:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testTypeValueNotation:main(?PER,optimize)),
+
+ ?line ?uper_bin(testTypeValueNotation:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testTypeValueNotation:main(uper_bin,optimize)),
+ case ?BER of
+ Ber3 when Ber3 == ber; Ber3 == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?PER,[optimize]),
+ ?line testTypeValueNotation:main(?PER,optimize);
+ _ ->
+ ok
+ end.
+
+testOpenTypeImplicitTag(suite) -> [];
+testOpenTypeImplicitTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?BER,[]),
+ ?line testOpenTypeImplicitTag:main(?BER),
+
+ ?line ?ber_driver(?BER,testOpenTypeImplicitTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testOpenTypeImplicitTag:main(?BER)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?PER,[]),
+ ?line testOpenTypeImplicitTag:main(?PER),
+
+ ?line ?per_bit_opt(testOpenTypeImplicitTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testOpenTypeImplicitTag:main(?PER)),
+
+ ?line ?uper_bin(testOpenTypeImplicitTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testOpenTypeImplicitTag:main(uper_bin)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?PER,[optimize]),
+ ?line testOpenTypeImplicitTag:main(?PER).
+
+duplicate_tags(suite) -> [];
+duplicate_tags(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ {error,{asn1,[{error,{type,_,_,'SeqOpt1Imp',{asn1,{duplicates_of_the_tags,_}}}}]}} =
+ asn1ct:compile(filename:join(DataDir,"SeqOptional2"),[abs]),
+ ok.
+
+rtUI(suite) -> [];
+rtUI(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Prim"),[?BER]),
+ ?line {ok,_} = asn1rt:info('Prim'),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Prim"),[?PER]),
+ ?line {ok,_} = asn1rt:info('Prim'),
+
+ ?line ok = asn1rt:load_driver(),
+ ?line ok = asn1rt:load_driver(),
+ ?line ok = asn1rt:unload_driver().
+
+testROSE(suite) -> [];
+testROSE(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testROSE:compile(Config,?BER,[]),
+
+ ?line testROSE:compile(Config,?PER,[]),
+ ?line ?per_bit_opt(testROSE:compile(Config,?PER,[optimize])),
+ ?line ?uper_bin(testROSE:compile(Config,uper_bin,[])),
+ ?line testROSE:compile(Config,?PER,[optimize]).
+
+testINSTANCE_OF(suite) -> [];
+testINSTANCE_OF(Config) ->
+ ?line testINSTANCE_OF:compile(Config,?BER,[]),
+ ?line testINSTANCE_OF:main(?BER),
+
+ ?line ?ber_driver(?BER,testINSTANCE_OF:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testINSTANCE_OF:main(?BER)),
+
+ ?line testINSTANCE_OF:compile(Config,?PER,[]),
+ ?line testINSTANCE_OF:main(?PER),
+
+ ?line ?per_bit_opt(testINSTANCE_OF:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testINSTANCE_OF:main(?PER)),
+
+ ?line ?uper_bin(testINSTANCE_OF:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testINSTANCE_OF:main(uper_bin)),
+
+ ?line testINSTANCE_OF:compile(Config,?PER,[optimize]),
+ ?line testINSTANCE_OF:main(?PER).
+
+testTCAP(suite) -> [];
+testTCAP(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testTCAP:compile(Config,?BER,[]),
+ ?line testTCAP:test(?BER,Config),
+
+ ?line ?ber_driver(?BER,testTCAP:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testTCAP:test(?BER,Config)),
+
+ ?line ?ber_driver(?BER,testTCAP:compile_asn1config(Config,?BER,[asn1config])),
+ ?line ?ber_driver(?BER,testTCAP:test_asn1config()).
+
+testDER(suite) ->[];
+testDER(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testDER:compile(Config,?BER,[]),
+ ?line testDER:test(),
+
+ ?line ?ber_driver(?BER,testDER:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDER:test()),
+
+ ?line testParamBasic:compile_der(Config,?BER),
+ ?line testParamBasic_cases(der),
+
+
+ ?line testSeqSetDefaultVal:compile(Config,?BER),
+ ?line testSeqSetDefaultVal_cases(?BER).
+
+testSeqSetDefaultVal_cases(?BER) ->
+ ?line testSeqSetDefaultVal:main(?BER).
+
+
+specialized_decodes(suite) -> [];
+specialized_decodes(Config) ->
+ ?line test_partial_incomplete_decode:compile(Config,?BER,[optimize]),
+ ?line test_partial_incomplete_decode:test(?BER,Config),
+ ?line test_selective_decode:test(?BER,Config).
+
+special_decode_performance(suite) ->[];
+special_decode_performance(Config) ->
+ ?line ?ber_driver(?BER,test_special_decode_performance:compile(Config,?BER)),
+ ?line ?ber_driver(?BER,test_special_decode_performance:go(all)).
+
+
+test_driver_load(suite) -> [];
+test_driver_load(Config) ->
+ ?line test_driver_load:compile(Config,?PER),
+ ?line test_driver_load:test(?PER,5).
+
+test_ParamTypeInfObj(suite) -> [];
+test_ParamTypeInfObj(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"IN-CS-1-Datatypes"),[ber_bin]).
+
+test_WS_ParamClass(suite) -> [];
+test_WS_ParamClass(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"InformationFramework"),
+ [ber_bin]).
+
+test_Defed_ObjectIdentifier(suite) -> [];
+test_Defed_ObjectIdentifier(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"UsefulDefinitions"),
+ [ber_bin]).
+
+testSelectionType(suite) -> [];
+testSelectionType(Config) ->
+
+ ?line ok = testSelectionTypes:compile(Config,?BER,[]),
+ ?line {ok,_} = testSelectionTypes:test(),
+
+ ?line ok = testSelectionTypes:compile(Config,?PER,[]),
+ ?line {ok,_} = testSelectionTypes:test().
+
+testSSLspecs(suite) -> [];
+testSSLspecs(Config) ->
+
+ ?line ok = testSSLspecs:compile(Config,?BER,
+ [optimize,compact_bit_string,der]),
+ ?line testSSLspecs:run(?BER),
+
+ case code:which(asn1ct) of
+ cover_compiled ->
+ ok;
+ _ ->
+ ?line ok = testSSLspecs:compile_inline(Config,?BER),
+ ?line ok = testSSLspecs:run_inline(?BER)
+ end.
+
+testNortel(suite) -> [];
+testNortel(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[?BER]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?BER,optimize]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?BER,optimize,driver]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[?PER]),
+ ?line ?per_bit_opt(ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?PER,optimize])),
+ ?line ?uper_bin(ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[uper_bin])),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?PER,optimize]).
+test_undecoded_rest(suite) -> [];
+test_undecoded_rest(Config) ->
+
+ ?line ok = test_undecoded_rest:compile(Config,?BER,[]),
+ ?line ok = test_undecoded_rest:test([]),
+
+ ?line ok = test_undecoded_rest:compile(Config,?BER,[undec_rest]),
+ ?line ok = test_undecoded_rest:test(undec_rest),
+
+ ?line ok = test_undecoded_rest:compile(Config,?PER,[]),
+ ?line ok = test_undecoded_rest:test([]),
+
+ ?line ?per_bit_opt(ok = test_undecoded_rest:compile(Config,?PER,[optimize,undec_rest])),
+ ?line ?per_bit_opt(ok = test_undecoded_rest:test(undec_rest)),
+
+ ?line ?uper_bin(ok = test_undecoded_rest:compile(Config,uper_bin,[undec_rest])),
+ ?line ?uper_bin(ok = test_undecoded_rest:test(undec_rest)),
+
+ ?line ok = test_undecoded_rest:compile(Config,?PER,[undec_rest]),
+ ?line ok = test_undecoded_rest:test(undec_rest).
+
+test_inline(suite) -> [];
+test_inline(Config) ->
+ case code:which(asn1ct) of
+ cover_compiled ->
+ {skip,"Not runnable when cover compiled"};
+ _ ->
+ ?line ok=test_inline:compile(Config,?BER,[]),
+ ?line test_inline:main(?BER),
+ ?line test_inline:inline1(Config,?BER,[]),
+ ?line test_inline:performance2()
+ end.
+
+%test_inline_prf(suite) -> [];
+%test_inline_prf(Config) ->
+% ?line test_inline:performance(Config).
+
+testTcapsystem(suite) -> [];
+testTcapsystem(Config) ->
+ ?line ok=testTcapsystem:compile(Config,?BER,[]).
+
+testNBAPsystem(suite) -> [];
+testNBAPsystem(Config) ->
+ ?line ok=testNBAPsystem:compile(Config,?PER,?per_optimize(?BER)),
+ ?line ok=testNBAPsystem:test(?PER,Config).
+
+test_compile_options(suite) -> [];
+test_compile_options(Config) ->
+ case code:which(asn1ct) of
+ cover_compiled ->
+ {skip,"Not runnable when cover compiled"};
+ _ ->
+ ?line ok = test_compile_options:wrong_path(Config),
+ ?line ok = test_compile_options:path(Config),
+ ?line ok = test_compile_options:noobj(Config),
+ ?line ok = test_compile_options:record_name_prefix(Config),
+ ?line ok = test_compile_options:verbose(Config)
+ end.
+testDoubleEllipses(suite) -> [];
+testDoubleEllipses(Config) ->
+ ?line testDoubleEllipses:compile(Config,?BER,[]),
+ ?line testDoubleEllipses:main(?BER),
+ ?line ?ber_driver(?BER,testDoubleEllipses:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDoubleEllipses:main(?BER)),
+ ?line ?per_bit_opt(testDoubleEllipses:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDoubleEllipses:main(?PER)),
+ ?line ?uper_bin(testDoubleEllipses:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDoubleEllipses:main(uper_bin)),
+ ?line testDoubleEllipses:compile(Config,?PER,?per_optimize(?BER)),
+ ?line testDoubleEllipses:main(?PER).
+
+test_modified_x420(suite) -> [];
+test_modified_x420(Config) ->
+ ?line test_modified_x420:compile(Config),
+ ?line test_modified_x420:test_io(Config).
+
+testX420(suite) -> [];
+testX420(Config) ->
+ ?line testX420:compile(?BER,[der],Config),
+ ?line ok = testX420:ticket7759(?BER,Config),
+ ?line testX420:compile(?PER,[],Config).
+
+test_x691(suite) -> [];
+test_x691(Config) ->
+ case ?PER of
+ per ->
+ ?line ok = test_x691:compile(Config,uper_bin,[]),
+ ?line true = test_x691:cases(uper_bin,unaligned),
+ ?line ok = test_x691:compile(Config,?PER,[]),
+ ?line true = test_x691:cases(?PER,aligned),
+%% ?line ok = asn1_test_lib:ticket_7678(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7708(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7763(Config);
+ _ ->
+ ?line ok = test_x691:compile(Config,?PER,?per_optimize(?BER)),
+ ?line true = test_x691:cases(?PER,aligned)
+ end.
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[compact_bit_string]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[optimize]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[optimize,compact_bit_string]).
+
+
+ticket_6143(suite) -> [];
+ticket_6143(Config) ->
+ ?line ok = test_compile_options:ticket_6143(Config).
+
+testExtensionAdditionGroup(suite) -> [];
+testExtensionAdditionGroup(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line PrivDir = ?config(priv_dir,Config),
+ ?line Path = code:get_path(),
+ ?line code:add_patha(PrivDir),
+ DoIt = fun(Erule) ->
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Extension-Addition-Group"),[Erule,{outdir,PrivDir}]),
+ ?line {ok,_M} = compile:file(filename:join(DataDir,"extensionAdditionGroup"),[{i,PrivDir},{outdir,PrivDir},debug_info]),
+ ?line ok = extensionAdditionGroup:run(Erule)
+ end,
+ ?line [DoIt(Rule)|| Rule <- [per_bin,uper_bin,ber_bin]],
+ ?line code:set_path(Path).
+
+
+
+% parse_modules() ->
+% ["ImportsFrom"].
+
+per_modules() ->
+ [X || X <- test_modules()].
+ber_modules() ->
+ [X || X <- test_modules(),
+ X =/= "CommonDataTypes",
+ X =/= "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath",
+ X =/= "H323-MESSAGES",
+ X =/= "H235-SECURITY-MESSAGES",
+ X =/= "MULTIMEDIA-SYSTEM-CONTROL"].
+test_modules() ->
+ _Modules = [
+ "BitStr",
+ "CommonDataTypes",
+ "Constraints",
+ "ContextSwitchingTypes",
+ "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath",
+ "Enum",
+ "From",
+ "H235-SECURITY-MESSAGES",
+ "H323-MESSAGES",
+ %%"MULTIMEDIA-SYSTEM-CONTROL", recursive type , problem for asn1ct:value
+ "Import",
+ "Int",
+ "MAP-commonDataTypes",
+% ambigous tags "MAP-insertSubscriberData-def",
+ "Null",
+ "Octetstr",
+ "One",
+ "P-Record",
+ "P",
+% "PDUs",
+ "Person",
+ "PrimStrings",
+ "Real",
+ "XSeq",
+ "XSeqOf",
+ "XSet",
+ "XSetOf",
+ "String",
+ "SwCDR",
+% "Syntax",
+ "Time"
+% ANY "Tst",
+% "Two",
+% errors that should be detected "UndefType"
+] ++
+ [
+ "SeqSetLib", % must be compiled before Seq and Set
+ "Seq",
+ "Set",
+ "SetOf",
+ "SeqOf",
+ "Prim",
+ "Cho",
+ "Def",
+ "Opt",
+ "ELDAPv3",
+ "LDAP"
+ ].
+
+
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+common() ->
+[{group, app_test}, {group, appup_test}, testTimer_ber,
+ testTimer_ber_bin, testTimer_ber_bin_opt,
+ testTimer_ber_bin_opt_driver, testTimer_per,
+ testTimer_per_bin, testTimer_per_bin_opt,
+ testTimer_uper_bin, testComment, testName2Number].
+
+
+
+testTimer_ber(suite) -> [];
+testTimer_ber(Config) ->
+ ?line testTimer:compile(Config,ber,[]),
+ ?line testTimer:go(Config,ber).
+
+testTimer_ber_bin(suite) -> [];
+testTimer_ber_bin(Config) ->
+ ?line testTimer:compile(Config,ber_bin,[]),
+ ?line testTimer:go(Config,ber_bin).
+
+testTimer_ber_bin_opt(suite) -> [];
+testTimer_ber_bin_opt(Config) ->
+ ?line testTimer:compile(Config,ber_bin,[optimize]),
+ ?line testTimer:go(Config,ber_bin).
+
+testTimer_ber_bin_opt_driver(suite) -> [];
+testTimer_ber_bin_opt_driver(Config) ->
+ ?line testTimer:compile(Config,ber_bin,[optimize,driver]),
+ ?line testTimer:go(Config,ber_bin).
+
+testTimer_per(suite) -> [];
+testTimer_per(Config) ->
+ ?line testTimer:compile(Config,per,[]),
+ ?line testTimer:go(Config,per).
+
+testTimer_per_bin(suite) -> [];
+testTimer_per_bin(Config) ->
+ ?line testTimer:compile(Config,per_bin,[]),
+ ?line testTimer:go(Config,per_bin).
+
+testTimer_per_bin_opt(suite) -> [];
+testTimer_per_bin_opt(Config) ->
+ ?line testTimer:compile(Config,per_bin,[optimize]),
+ ?line testTimer:go(Config,per_bin).
+
+
+testTimer_uper_bin(suite) -> [];
+testTimer_uper_bin(Config) ->
+ ?line ok=testTimer:compile(Config,uper_bin,[]),
+ ?line {comment,_} = testTimer:go(Config,uper_bin).
+
+%% Test of multiple-line comment, OTP-8043
+testComment(suite) -> [];
+testComment(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+
+ ?line ok = asn1ct:compile(DataDir ++ "Comment",[{outdir,OutDir}]),
+
+ ?line {ok,Enc} = asn1_wrapper:encode('Comment','Seq',{'Seq',12,true}),
+ ?line {ok,{'Seq',12,true}} = asn1_wrapper:decode('Comment','Seq',Enc),
+ ok.
+
+testName2Number(suite) -> [];
+testName2Number(Config) ->
+ DataDir = ?config(data_dir,Config),
+ OutDir = ?config(priv_dir,Config),
+ N2NOptions = [{n2n,Type}|| Type <-
+ ['CauseMisc','CauseProtocol',
+ %% 'CauseNetwork',
+ 'CauseRadioNetwork',
+ 'CauseTransport','CauseNas']],
+ ?line ok = asn1ct:compile(DataDir ++ "S1AP-IEs",[{outdir,OutDir}]++N2NOptions),
+ ?line true = code:add_patha(OutDir),
+
+ ?line 0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'),
+ ?line 'unknown-PLMN' = 'S1AP-IEs':num2name_CauseMisc(5),
+ ok.
+
+
+particular() ->
+ [ticket_7407].
+
+ticket_7407(suite) -> [];
+ticket_7407(Config) ->
+ ?line ok = asn1_test_lib:ticket_7407_compile(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7407_code(true),
+
+ ?line ok = asn1_test_lib:ticket_7407_compile(Config,[no_final_padding]),
+ ?line ok = asn1_test_lib:ticket_7407_code(false).
diff --git a/lib/asn1/test/asn1_SUITE.erl.src b/lib/asn1/test/asn1_SUITE.erl.src
index ca73d259af..7201365ea3 100644
--- a/lib/asn1/test/asn1_SUITE.erl.src
+++ b/lib/asn1/test/asn1_SUITE.erl.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -74,6 +74,7 @@ all(suite) -> [compile,parse,default_per,default_ber,default_per_opt,per,
testSeqTypeRefPrim, testSeqTypeRefSeq, testSeqTypeRefSet,
testSeqOf, testSeqOfIndefinite, testSeqOfCho,
testSeqOfExternal, testSetDefault, testSetExtension,
+ testExtensionAdditionGroup,
testSetExternal, testSeqOfTag, testSetOptional, testSetPrim,
testSetTag, testSetTypeRefCho, testSetTypeRefPrim,
testSetTypeRefSeq, testSetTypeRefSet, testSetOf, testSetOfCho,
@@ -95,7 +96,7 @@ all(suite) -> [compile,parse,default_per,default_ber,default_per_opt,per,
testSSLspecs, testNortel,test_undecoded_rest,
test_inline, testTcapsystem, testNBAPsystem,
test_compile_options,testDoubleEllipses, test_modified_x420,
- testX420, test_x691,ticket_6143
+ testX420, test_x691,ticket_6143, testExtensionAdditionGroup
] ++ common() ++ particular().
%all(suite) -> [test_inline,testNBAPsystem,test_compile_options,ticket_6143].
@@ -1036,7 +1037,6 @@ testSetExtension_cases(Rules) ->
?line testSetExtension:main(Rules).
-
testSetExternal(suite) -> [];
testSetExternal(Config) ->
?line true = code:add_patha(?config(priv_dir,Config)),
@@ -2292,6 +2292,25 @@ ticket_6143(suite) -> [];
ticket_6143(Config) ->
?line ok = test_compile_options:ticket_6143(Config).
+testExtensionAdditionGroup(suite) -> [];
+testExtensionAdditionGroup(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line PrivDir = ?config(priv_dir,Config),
+ ?line Path = code:get_path(),
+ ?line code:add_patha(PrivDir),
+ DoIt = fun(Erule) ->
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Extension-Addition-Group"),Erule ++ [{outdir,PrivDir}]),
+ ?line {ok,_M} = compile:file(filename:join(DataDir,"extensionAdditionGroup"),[{i,PrivDir},{outdir,PrivDir},debug_info]),
+ ?line ok = extensionAdditionGroup:run(Erule),
+ ?line ok = extensionAdditionGroup:run2(Erule),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"EUTRA-RRC-Definitions"),Erule ++ [{record_name_prefix,"RRC-"},{outdir,PrivDir}]),
+ ?line ok = extensionAdditionGroup:run3(Erule)
+ end,
+ ?line [DoIt(Rule)|| Rule <- [[per_bin],[per_bin,optimize],[uper_bin]]],
+ %% FIXME problems with automatic tags [ber_bin],[ber_bin,optimize]
+ ?line code:set_path(Path).
+
+
% parse_modules() ->
% ["ImportsFrom"].
@@ -2308,6 +2327,7 @@ ber_modules() ->
test_modules() ->
_Modules = [
"BitStr",
+ "CAP",
"CommonDataTypes",
"Constraints",
"ContextSwitchingTypes",
diff --git a/lib/asn1/test/asn1_SUITE_data/AA2.asn1db b/lib/asn1/test/asn1_SUITE_data/AA2.asn1db
index 3bf2c1b89d..163dbb032d 100644
--- a/lib/asn1/test/asn1_SUITE_data/AA2.asn1db
+++ b/lib/asn1/test/asn1_SUITE_data/AA2.asn1db
Binary files differ
diff --git a/lib/asn1/test/asn1_SUITE_data/CAP.asn1 b/lib/asn1/test/asn1_SUITE_data/CAP.asn1
new file mode 100644
index 0000000000..69d8486d3b
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/CAP.asn1
@@ -0,0 +1,41 @@
+CAP {ccitt(0) identified-organization(4) etsi(0) mobileDomain(0) umts-network(1) modules(3) cap-datatypes(52) version3(2)}
+
+DEFINITIONS IMPLICIT TAGS ::=
+
+BEGIN
+
+EXTENSION ::= CLASS {
+ &ExtensionType,
+ &criticality CriticalityType DEFAULT ignore,
+ &id Code
+ }
+WITH SYNTAX {
+ EXTENSION-SYNTAX &ExtensionType
+ CRITICALITY &criticality
+ IDENTIFIED BY &id
+ }
+
+ExtensionField ::= SEQUENCE {
+ type EXTENSION.&id ({SupportedExtensions }),
+ criticality CriticalityType DEFAULT ignore,
+ value [1] EXTENSION.&ExtensionType ({SupportedExtensions }{@type}),
+ ...}
+
+SupportedExtensions EXTENSION ::= {firstExtension, ...}
+
+firstExtension EXTENSION ::= {
+ EXTENSION-SYNTAX NULL
+ CRITICALITY ignore
+ IDENTIFIED BY global : {itu-t(0) identified-organization(4) organisation(0) gsm(1)
+ capextension(2)}}
+
+CriticalityType ::= ENUMERATED {
+ ignore (0),
+ abort (1)
+ }
+
+Code ::= CHOICE {local INTEGER,
+ global OBJECT IDENTIFIER}
+
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/EUTRA-RRC-Definitions.asn b/lib/asn1/test/asn1_SUITE_data/EUTRA-RRC-Definitions.asn
index a451874ef0..3b811dafe6 100644
--- a/lib/asn1/test/asn1_SUITE_data/EUTRA-RRC-Definitions.asn
+++ b/lib/asn1/test/asn1_SUITE_data/EUTRA-RRC-Definitions.asn
@@ -1,2640 +1,3155 @@
--- 3GPP TS 36.331 V8.8.0 (2009-12)
--- $Id$
---
-EUTRA-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::=
-
-BEGIN
-
-
-BCCH-BCH-Message ::= SEQUENCE {
- message BCCH-BCH-MessageType
-}
-
-BCCH-BCH-MessageType ::= MasterInformationBlock
-
-
-BCCH-DL-SCH-Message ::= SEQUENCE {
- message BCCH-DL-SCH-MessageType
-}
-
-BCCH-DL-SCH-MessageType ::= CHOICE {
- c1 CHOICE {
- systemInformation SystemInformation,
- systemInformationBlockType1 SystemInformationBlockType1
- },
- messageClassExtension SEQUENCE {}
-}
-
-
-PCCH-Message ::= SEQUENCE {
- message PCCH-MessageType
-}
-
-PCCH-MessageType ::= CHOICE {
- c1 CHOICE {
- paging Paging
- },
- messageClassExtension SEQUENCE {}
-}
-
-
-DL-CCCH-Message ::= SEQUENCE {
- message DL-CCCH-MessageType
-}
-
-DL-CCCH-MessageType ::= CHOICE {
- c1 CHOICE {
- rrcConnectionReestablishment RRCConnectionReestablishment,
- rrcConnectionReestablishmentReject RRCConnectionReestablishmentReject,
- rrcConnectionReject RRCConnectionReject,
- rrcConnectionSetup RRCConnectionSetup
- },
- messageClassExtension SEQUENCE {}
-}
-
-
-DL-DCCH-Message ::= SEQUENCE {
- message DL-DCCH-MessageType
-}
-
-DL-DCCH-MessageType ::= CHOICE {
- c1 CHOICE {
- csfbParametersResponseCDMA2000 CSFBParametersResponseCDMA2000,
- dlInformationTransfer DLInformationTransfer,
- handoverFromEUTRAPreparationRequest HandoverFromEUTRAPreparationRequest,
- mobilityFromEUTRACommand MobilityFromEUTRACommand,
- rrcConnectionReconfiguration RRCConnectionReconfiguration,
- rrcConnectionRelease RRCConnectionRelease,
- securityModeCommand SecurityModeCommand,
- ueCapabilityEnquiry UECapabilityEnquiry,
- counterCheck CounterCheck,
- spare7 NULL,
- spare6 NULL, spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- messageClassExtension SEQUENCE {}
-}
-
-
-UL-CCCH-Message ::= SEQUENCE {
- message UL-CCCH-MessageType
-}
-
-UL-CCCH-MessageType ::= CHOICE {
- c1 CHOICE {
- rrcConnectionReestablishmentRequest RRCConnectionReestablishmentRequest,
- rrcConnectionRequest RRCConnectionRequest
- },
- messageClassExtension SEQUENCE {}
-}
-
-
-UL-DCCH-Message ::= SEQUENCE {
- message UL-DCCH-MessageType
-}
-
-UL-DCCH-MessageType ::= CHOICE {
- c1 CHOICE {
- csfbParametersRequestCDMA2000 CSFBParametersRequestCDMA2000,
- measurementReport MeasurementReport,
- rrcConnectionReconfigurationComplete RRCConnectionReconfigurationComplete,
- rrcConnectionReestablishmentComplete RRCConnectionReestablishmentComplete,
- rrcConnectionSetupComplete RRCConnectionSetupComplete,
- securityModeComplete SecurityModeComplete,
- securityModeFailure SecurityModeFailure,
- ueCapabilityInformation UECapabilityInformation,
- ulHandoverPreparationTransfer ULHandoverPreparationTransfer,
- ulInformationTransfer ULInformationTransfer,
- counterCheckResponse CounterCheckResponse,
- spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- messageClassExtension SEQUENCE {}
-}
-
-
-CounterCheck ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE {
- counterCheck-r8 CounterCheck-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-CounterCheck-r8-IEs ::= SEQUENCE {
- drb-CountMSB-InfoList DRB-CountMSB-InfoList,
- nonCriticalExtension SEQUENCE {} OPTIONAL --Need OP
-}
-
-DRB-CountMSB-InfoList::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-CountMSB-Info
-
-DRB-CountMSB-Info ::= SEQUENCE {
- drb-Identity DRB-Identity,
- countMSB-Uplink INTEGER(0..33554431),
- countMSB-Downlink INTEGER(0..33554431)
-}
-
-
-CounterCheckResponse ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- counterCheckResponse-r8 CounterCheckResponse-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-CounterCheckResponse-r8-IEs ::= SEQUENCE {
- drb-CountInfoList DRB-CountInfoList,
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-DRB-CountInfoList ::= SEQUENCE (SIZE (0..maxDRB)) OF DRB-CountInfo
-
-DRB-CountInfo ::= SEQUENCE {
- drb-Identity DRB-Identity,
- count-Uplink INTEGER(0..4294967295),
- count-Downlink INTEGER(0..4294967295)
-}
-
-
-CSFBParametersRequestCDMA2000 ::= SEQUENCE {
- criticalExtensions CHOICE {
- csfbParametersRequestCDMA2000-r8 CSFBParametersRequestCDMA2000-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-CSFBParametersRequestCDMA2000-r8-IEs ::= SEQUENCE {
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-CSFBParametersResponseCDMA2000 ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- csfbParametersResponseCDMA2000-r8 CSFBParametersResponseCDMA2000-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-CSFBParametersResponseCDMA2000-r8-IEs ::= SEQUENCE {
- rand RAND-CDMA2000,
- mobilityParameters MobilityParametersCDMA2000,
- nonCriticalExtension SEQUENCE {} OPTIONAL --Need OP
-}
-
-
-DLInformationTransfer ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE {
- dlInformationTransfer-r8 DLInformationTransfer-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-DLInformationTransfer-r8-IEs ::= SEQUENCE {
- dedicatedInfoType CHOICE {
- dedicatedInfoNAS DedicatedInfoNAS,
- dedicatedInfoCDMA2000-1XRTT DedicatedInfoCDMA2000,
- dedicatedInfoCDMA2000-HRPD DedicatedInfoCDMA2000
- },
- nonCriticalExtension SEQUENCE {} OPTIONAL --Need OP
-}
-
-
-HandoverFromEUTRAPreparationRequest ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE {
- handoverFromEUTRAPreparationRequest-r8
- HandoverFromEUTRAPreparationRequest-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-HandoverFromEUTRAPreparationRequest-r8-IEs ::= SEQUENCE {
- cdma2000-Type CDMA2000-Type,
- rand RAND-CDMA2000 OPTIONAL, -- Cond cdma2000-Type
- mobilityParameters MobilityParametersCDMA2000 OPTIONAL, -- Cond cdma2000-Type
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-
-MasterInformationBlock ::= SEQUENCE {
- dl-Bandwidth ENUMERATED {
- n6, n15, n25, n50, n75, n100},
- phich-Config PHICH-Config,
- systemFrameNumber BIT STRING (SIZE (8)),
- spare BIT STRING (SIZE (10))
-}
-
-
-
-MeasurementReport ::= SEQUENCE {
- criticalExtensions CHOICE {
- c1 CHOICE{
- measurementReport-r8 MeasurementReport-r8-IEs,
- spare7 NULL,
- spare6 NULL, spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-MeasurementReport-r8-IEs ::= SEQUENCE {
- measResults MeasResults,
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-MobilityFromEUTRACommand ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE{
- mobilityFromEUTRACommand-r8 MobilityFromEUTRACommand-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-MobilityFromEUTRACommand-r8-IEs ::= SEQUENCE {
- cs-FallbackIndicator BOOLEAN,
- purpose CHOICE{
- handover Handover,
- cellChangeOrder CellChangeOrder
- },
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-Handover ::= SEQUENCE {
- targetRAT-Type ENUMERATED {
- utra, geran, cdma2000-1XRTT, cdma2000-HRPD,
- spare4, spare3, spare2, spare1, ...},
- targetRAT-MessageContainer OCTET STRING,
- nas-SecurityParamFromEUTRA OCTET STRING (SIZE (1)) OPTIONAL, -- Cond UTRAGERAN
- systemInformation SI-OrPSI-GERAN OPTIONAL -- Cond PSHO
-}
-
-CellChangeOrder ::= SEQUENCE {
- t304 ENUMERATED {
- ms100, ms200, ms500, ms1000,
- ms2000, ms4000, ms8000, spare1},
- targetRAT-Type CHOICE {
- geran SEQUENCE {
- physCellId PhysCellIdGERAN,
- carrierFreq CarrierFreqGERAN,
- networkControlOrder BIT STRING (SIZE (2)) OPTIONAL, -- Need OP
- systemInformation SI-OrPSI-GERAN OPTIONAL -- Need OP
- },
- ...
- }
-}
-
-SI-OrPSI-GERAN ::= CHOICE {
- si SystemInfoListGERAN,
- psi SystemInfoListGERAN
-}
-
-SystemInfoListGERAN ::= SEQUENCE (SIZE (1..maxGERAN-SI)) OF
- OCTET STRING (SIZE (1..23))
-
-
-Paging ::= SEQUENCE {
- pagingRecordList PagingRecordList OPTIONAL, -- Need ON
- systemInfoModification ENUMERATED {true} OPTIONAL, -- Need ON
- etws-Indication ENUMERATED {true} OPTIONAL, -- Need ON
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-PagingRecordList ::= SEQUENCE (SIZE (1..maxPageRec)) OF PagingRecord
-
-PagingRecord ::= SEQUENCE {
- ue-Identity PagingUE-Identity,
- cn-Domain ENUMERATED {ps, cs},
- ...
-}
-
-PagingUE-Identity ::= CHOICE {
- s-TMSI S-TMSI,
- imsi IMSI,
- ...
-}
-
-IMSI ::= SEQUENCE (SIZE (6..21)) OF IMSI-Digit
-
-IMSI-Digit::= INTEGER (0..9)
-
-
-RRCConnectionReconfiguration ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE{
- rrcConnectionReconfiguration-r8 RRCConnectionReconfiguration-r8-IEs,
- spare7 NULL,
- spare6 NULL, spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReconfiguration-r8-IEs ::= SEQUENCE {
- measConfig MeasConfig OPTIONAL, -- Need ON
- mobilityControlInfo MobilityControlInfo OPTIONAL, -- Cond HO
- dedicatedInfoNASList SEQUENCE (SIZE(1..maxDRB)) OF
- DedicatedInfoNAS OPTIONAL, -- Cond nonHO
- radioResourceConfigDedicated RadioResourceConfigDedicated OPTIONAL, -- Cond HO-toEUTRA
- securityConfigHO SecurityConfigHO OPTIONAL, -- Cond HO
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-SecurityConfigHO ::= SEQUENCE {
- handoverType CHOICE {
- intraLTE SEQUENCE {
- securityAlgorithmConfig SecurityAlgorithmConfig OPTIONAL, -- Need OP
- keyChangeIndicator BOOLEAN,
- nextHopChainingCount NextHopChainingCount
- },
- interRAT SEQUENCE {
- securityAlgorithmConfig SecurityAlgorithmConfig,
- nas-SecurityParamToEUTRA OCTET STRING (SIZE(6))
- }
- },
- ...
-}
-
-
-RRCConnectionReconfigurationComplete ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- rrcConnectionReconfigurationComplete-r8
- RRCConnectionReconfigurationComplete-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReconfigurationComplete-r8-IEs ::= SEQUENCE {
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-RRCConnectionReestablishment ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE{
- rrcConnectionReestablishment-r8 RRCConnectionReestablishment-r8-IEs,
- spare7 NULL,
- spare6 NULL, spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReestablishment-r8-IEs ::= SEQUENCE {
- radioResourceConfigDedicated RadioResourceConfigDedicated,
- nextHopChainingCount NextHopChainingCount,
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-
-RRCConnectionReestablishmentComplete ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- rrcConnectionReestablishmentComplete-r8
- RRCConnectionReestablishmentComplete-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReestablishmentComplete-r8-IEs ::= SEQUENCE {
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-RRCConnectionReestablishmentReject ::= SEQUENCE {
- criticalExtensions CHOICE {
- rrcConnectionReestablishmentReject-r8
- RRCConnectionReestablishmentReject-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReestablishmentReject-r8-IEs ::= SEQUENCE {
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-
-RRCConnectionReestablishmentRequest ::= SEQUENCE {
- criticalExtensions CHOICE {
- rrcConnectionReestablishmentRequest-r8
- RRCConnectionReestablishmentRequest-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReestablishmentRequest-r8-IEs ::= SEQUENCE {
- ue-Identity ReestabUE-Identity,
- reestablishmentCause ReestablishmentCause,
- spare BIT STRING (SIZE (2))
-}
-
-ReestabUE-Identity ::= SEQUENCE {
- c-RNTI C-RNTI,
- physCellId PhysCellId,
- shortMAC-I ShortMAC-I
-}
-
-ReestablishmentCause ::= ENUMERATED {
- reconfigurationFailure, handoverFailure,
- otherFailure, spare1}
-
-
-RRCConnectionReject ::= SEQUENCE {
- criticalExtensions CHOICE {
- c1 CHOICE {
- rrcConnectionReject-r8 RRCConnectionReject-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionReject-r8-IEs ::= SEQUENCE {
- waitTime INTEGER (1..16),
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-
-RRCConnectionRelease ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE {
- rrcConnectionRelease-r8 RRCConnectionRelease-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionRelease-r8-IEs ::= SEQUENCE {
- releaseCause ReleaseCause,
- redirectedCarrierInfo RedirectedCarrierInfo OPTIONAL, -- Need ON
- idleModeMobilityControlInfo IdleModeMobilityControlInfo OPTIONAL, -- Need OP
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-ReleaseCause ::= ENUMERATED {loadBalancingTAUrequired,
- other,spare2, spare1 }
-
-RedirectedCarrierInfo ::= CHOICE {
- eutra ARFCN-ValueEUTRA,
- geran CarrierFreqsGERAN,
- utra-FDD ARFCN-ValueUTRA,
- utra-TDD ARFCN-ValueUTRA,
- cdma2000-HRPD CarrierFreqCDMA2000,
- cdma2000-1xRTT CarrierFreqCDMA2000,
- ...
-}
-
-IdleModeMobilityControlInfo ::= SEQUENCE {
- freqPriorityListEUTRA FreqPriorityListEUTRA OPTIONAL, -- Need ON
- freqPriorityListGERAN FreqsPriorityListGERAN OPTIONAL, -- Need ON
- freqPriorityListUTRA-FDD FreqPriorityListUTRA-FDD OPTIONAL, -- Need ON
- freqPriorityListUTRA-TDD FreqPriorityListUTRA-TDD OPTIONAL, -- Need ON
- bandClassPriorityListHRPD BandClassPriorityListHRPD OPTIONAL, -- Need ON
- bandClassPriorityList1XRTT BandClassPriorityList1XRTT OPTIONAL, -- Need ON
- t320 ENUMERATED {
- min5, min10, min20, min30, min60, min120, min180,
- spare1} OPTIONAL, -- Need OR
- ...
-}
-
-FreqPriorityListEUTRA ::= SEQUENCE (SIZE (1..maxFreq)) OF FreqPriorityEUTRA
-
-FreqPriorityEUTRA ::= SEQUENCE {
- carrierFreq ARFCN-ValueEUTRA,
- cellReselectionPriority CellReselectionPriority
-}
-
-FreqsPriorityListGERAN ::= SEQUENCE (SIZE (1..maxGNFG)) OF FreqsPriorityGERAN
-
-FreqsPriorityGERAN ::= SEQUENCE {
- carrierFreqs CarrierFreqsGERAN,
- cellReselectionPriority CellReselectionPriority
-}
-
-FreqPriorityListUTRA-FDD ::= SEQUENCE (SIZE (1..maxUTRA-FDD-Carrier)) OF FreqPriorityUTRA-FDD
-
-FreqPriorityUTRA-FDD ::= SEQUENCE {
- carrierFreq ARFCN-ValueUTRA,
- cellReselectionPriority CellReselectionPriority
-}
-
-FreqPriorityListUTRA-TDD ::= SEQUENCE (SIZE (1..maxUTRA-TDD-Carrier)) OF FreqPriorityUTRA-TDD
-
-FreqPriorityUTRA-TDD ::= SEQUENCE {
- carrierFreq ARFCN-ValueUTRA,
- cellReselectionPriority CellReselectionPriority
-}
-
-BandClassPriorityListHRPD ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandClassPriorityHRPD
-
-BandClassPriorityHRPD ::= SEQUENCE {
- bandClass BandclassCDMA2000,
- cellReselectionPriority CellReselectionPriority
-}
-
-BandClassPriorityList1XRTT ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandClassPriority1XRTT
-
-BandClassPriority1XRTT ::= SEQUENCE {
- bandClass BandclassCDMA2000,
- cellReselectionPriority CellReselectionPriority
-}
-
-RRCConnectionRequest ::= SEQUENCE {
- criticalExtensions CHOICE {
- rrcConnectionRequest-r8 RRCConnectionRequest-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionRequest-r8-IEs ::= SEQUENCE {
- ue-Identity InitialUE-Identity,
- establishmentCause EstablishmentCause,
- spare BIT STRING (SIZE (1))
-}
-
-InitialUE-Identity ::= CHOICE {
- s-TMSI S-TMSI,
- randomValue BIT STRING (SIZE (40))
-}
-
-EstablishmentCause ::= ENUMERATED {
- emergency, highPriorityAccess, mt-Access, mo-Signalling,
- mo-Data, spare3, spare2, spare1}
-
-
-RRCConnectionSetup ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE {
- rrcConnectionSetup-r8 RRCConnectionSetup-r8-IEs,
- spare7 NULL,
- spare6 NULL, spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionSetup-r8-IEs ::= SEQUENCE {
- radioResourceConfigDedicated RadioResourceConfigDedicated,
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-
-RRCConnectionSetupComplete ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE{
- rrcConnectionSetupComplete-r8 RRCConnectionSetupComplete-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-RRCConnectionSetupComplete-r8-IEs ::= SEQUENCE {
- selectedPLMN-Identity INTEGER (1..6),
- registeredMME RegisteredMME OPTIONAL,
- dedicatedInfoNAS DedicatedInfoNAS,
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-RegisteredMME ::= SEQUENCE {
- plmn-Identity PLMN-Identity OPTIONAL,
- mmegi BIT STRING (SIZE (16)),
- mmec MMEC
-}
-
-
-SecurityModeCommand ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE{
- securityModeCommand-r8 SecurityModeCommand-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-SecurityModeCommand-r8-IEs ::= SEQUENCE {
- securityConfigSMC SecurityConfigSMC,
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-SecurityConfigSMC ::= SEQUENCE {
- securityAlgorithmConfig SecurityAlgorithmConfig,
- ...
-}
-
-
-SecurityModeComplete ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- securityModeComplete-r8 SecurityModeComplete-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-SecurityModeComplete-r8-IEs ::= SEQUENCE {
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-SecurityModeFailure ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- securityModeFailure-r8 SecurityModeFailure-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-SecurityModeFailure-r8-IEs ::= SEQUENCE {
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-SystemInformation ::= SEQUENCE {
- criticalExtensions CHOICE {
- systemInformation-r8 SystemInformation-r8-IEs,
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-SystemInformation-r8-IEs ::= SEQUENCE {
- sib-TypeAndInfo SEQUENCE (SIZE (1..maxSIB)) OF CHOICE {
- sib2 SystemInformationBlockType2,
- sib3 SystemInformationBlockType3,
- sib4 SystemInformationBlockType4,
- sib5 SystemInformationBlockType5,
- sib6 SystemInformationBlockType6,
- sib7 SystemInformationBlockType7,
- sib8 SystemInformationBlockType8,
- sib9 SystemInformationBlockType9,
- sib10 SystemInformationBlockType10,
- sib11 SystemInformationBlockType11,
- ...
- },
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-
-SystemInformationBlockType1 ::= SEQUENCE {
- cellAccessRelatedInfo SEQUENCE {
- plmn-IdentityList PLMN-IdentityList,
- trackingAreaCode TrackingAreaCode,
- cellIdentity CellIdentity,
- cellBarred ENUMERATED {barred, notBarred},
- intraFreqReselection ENUMERATED {allowed, notAllowed},
- csg-Indication BOOLEAN,
- csg-Identity BIT STRING (SIZE (27)) OPTIONAL -- Need OR
- },
- cellSelectionInfo SEQUENCE {
- q-RxLevMin Q-RxLevMin,
- q-RxLevMinOffset INTEGER (1..8) OPTIONAL -- Need OP
- },
- p-Max P-Max OPTIONAL, -- Need OP
- freqBandIndicator INTEGER (1..64),
- schedulingInfoList SchedulingInfoList,
- tdd-Config TDD-Config OPTIONAL, -- Cond TDD
- si-WindowLength ENUMERATED {
- ms1, ms2, ms5, ms10, ms15, ms20,
- ms40},
- systemInfoValueTag INTEGER (0..31),
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-PLMN-IdentityList ::= SEQUENCE (SIZE (1..6)) OF PLMN-IdentityInfo
-
-PLMN-IdentityInfo ::= SEQUENCE {
- plmn-Identity PLMN-Identity,
- cellReservedForOperatorUse ENUMERATED {reserved, notReserved}
-}
-
-SchedulingInfoList ::= SEQUENCE (SIZE (1..maxSI-Message)) OF SchedulingInfo
-
-SchedulingInfo ::= SEQUENCE {
- si-Periodicity ENUMERATED {
- rf8, rf16, rf32, rf64, rf128, rf256, rf512},
- sib-MappingInfo SIB-MappingInfo
-}
-
-SIB-MappingInfo ::= SEQUENCE (SIZE (0..maxSIB-1)) OF SIB-Type
-
-SIB-Type ::= ENUMERATED {
- sibType3, sibType4, sibType5, sibType6,
- sibType7, sibType8, sibType9, sibType10,
- sibType11, spare7, spare6, spare5,
- spare4, spare3, spare2, spare1, ...}
-
-
-UECapabilityEnquiry ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE {
- ueCapabilityEnquiry-r8 UECapabilityEnquiry-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-UECapabilityEnquiry-r8-IEs ::= SEQUENCE {
- ue-CapabilityRequest UE-CapabilityRequest,
- nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
-}
-
-UE-CapabilityRequest ::= SEQUENCE (SIZE (1..maxRAT-Capabilities)) OF RAT-Type
-
-
-UECapabilityInformation ::= SEQUENCE {
- rrc-TransactionIdentifier RRC-TransactionIdentifier,
- criticalExtensions CHOICE {
- c1 CHOICE{
- ueCapabilityInformation-r8 UECapabilityInformation-r8-IEs,
- spare7 NULL,
- spare6 NULL, spare5 NULL, spare4 NULL,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-UECapabilityInformation-r8-IEs ::= SEQUENCE {
- ue-CapabilityRAT-ContainerList UE-CapabilityRAT-ContainerList,
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-ULHandoverPreparationTransfer ::= SEQUENCE {
- criticalExtensions CHOICE {
- c1 CHOICE {
- ulHandoverPreparationTransfer-r8 ULHandoverPreparationTransfer-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-ULHandoverPreparationTransfer-r8-IEs ::= SEQUENCE {
- cdma2000-Type CDMA2000-Type,
- meid BIT STRING (SIZE (56)) OPTIONAL,
- dedicatedInfo DedicatedInfoCDMA2000,
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-ULInformationTransfer ::= SEQUENCE {
- criticalExtensions CHOICE {
- c1 CHOICE {
- ulInformationTransfer-r8 ULInformationTransfer-r8-IEs,
- spare3 NULL, spare2 NULL, spare1 NULL
- },
- criticalExtensionsFuture SEQUENCE {}
- }
-}
-
-ULInformationTransfer-r8-IEs ::= SEQUENCE {
- dedicatedInfoType CHOICE {
- dedicatedInfoNAS DedicatedInfoNAS,
- dedicatedInfoCDMA2000-1XRTT DedicatedInfoCDMA2000,
- dedicatedInfoCDMA2000-HRPD DedicatedInfoCDMA2000
- },
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-
-SystemInformationBlockType2 ::= SEQUENCE {
- ac-BarringInfo SEQUENCE {
- ac-BarringForEmergency BOOLEAN,
- ac-BarringForMO-Signalling AC-BarringConfig OPTIONAL, -- Need OP
- ac-BarringForMO-Data AC-BarringConfig OPTIONAL -- Need OP
- } OPTIONAL, -- Need OP
- radioResourceConfigCommon RadioResourceConfigCommonSIB,
- ue-TimersAndConstants UE-TimersAndConstants,
- freqInfo SEQUENCE {
- ul-CarrierFreq ARFCN-ValueEUTRA OPTIONAL, -- Need OP
- ul-Bandwidth ENUMERATED {n6, n15, n25, n50, n75, n100}
- OPTIONAL, -- Need OP
- additionalSpectrumEmission AdditionalSpectrumEmission
- },
- mbsfn-SubframeConfigList MBSFN-SubframeConfigList OPTIONAL, -- Need OR
- timeAlignmentTimerCommon TimeAlignmentTimer,
- ...
-}
-
-AC-BarringConfig ::= SEQUENCE {
- ac-BarringFactor ENUMERATED {
- p00, p05, p10, p15, p20, p25, p30, p40,
- p50, p60, p70, p75, p80, p85, p90, p95},
- ac-BarringTime ENUMERATED {s4, s8, s16, s32, s64, s128, s256, s512},
- ac-BarringForSpecialAC BIT STRING (SIZE(5))
-}
-
-MBSFN-SubframeConfigList ::= SEQUENCE (SIZE (1..maxMBSFN-Allocations)) OF MBSFN-SubframeConfig
-
-MBSFN-SubframeConfig ::= SEQUENCE {
- radioframeAllocationPeriod ENUMERATED {n1, n2, n4, n8, n16, n32},
- radioframeAllocationOffset INTEGER (0..7),
- subframeAllocation CHOICE {
- oneFrame BIT STRING (SIZE(6)),
- fourFrames BIT STRING (SIZE(24))
- }
-}
-
-SystemInformationBlockType3 ::= SEQUENCE {
- cellReselectionInfoCommon SEQUENCE {
- q-Hyst ENUMERATED {
- dB0, dB1, dB2, dB3, dB4, dB5, dB6, dB8, dB10,
- dB12, dB14, dB16, dB18, dB20, dB22, dB24},
- speedStateReselectionPars SEQUENCE {
- mobilityStateParameters MobilityStateParameters,
- q-HystSF SEQUENCE {
- sf-Medium ENUMERATED {
- dB-6, dB-4, dB-2, dB0},
- sf-High ENUMERATED {
- dB-6, dB-4, dB-2, dB0}
- }
- } OPTIONAL -- Need OP
- },
- cellReselectionServingFreqInfo SEQUENCE {
- s-NonIntraSearch ReselectionThreshold OPTIONAL, -- Need OP
- threshServingLow ReselectionThreshold,
- cellReselectionPriority CellReselectionPriority
- },
- intraFreqCellReselectionInfo SEQUENCE {
- q-RxLevMin Q-RxLevMin,
- p-Max P-Max OPTIONAL, -- Need OP
- s-IntraSearch ReselectionThreshold OPTIONAL, -- Need OP
- allowedMeasBandwidth AllowedMeasBandwidth OPTIONAL, -- Need OP
- presenceAntennaPort1 PresenceAntennaPort1,
- neighCellConfig NeighCellConfig,
- t-ReselectionEUTRA T-Reselection,
- t-ReselectionEUTRA-SF SpeedStateScaleFactors OPTIONAL -- Need OP
- },
- ...
-}
-
-
-SystemInformationBlockType4 ::= SEQUENCE {
- intraFreqNeighCellList IntraFreqNeighCellList OPTIONAL, -- Need OR
- intraFreqBlackCellList IntraFreqBlackCellList OPTIONAL, -- Need OR
- csg-PhysCellIdRange PhysCellIdRange OPTIONAL, -- Cond CSG
- ...
-}
-
-IntraFreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellIntra)) OF IntraFreqNeighCellInfo
-
-IntraFreqNeighCellInfo ::= SEQUENCE {
- physCellId PhysCellId,
- q-OffsetCell Q-OffsetRange,
- ...
-}
-
-IntraFreqBlackCellList ::= SEQUENCE (SIZE (1..maxCellBlack)) OF PhysCellIdRange
-
-
-SystemInformationBlockType5 ::= SEQUENCE {
- interFreqCarrierFreqList InterFreqCarrierFreqList,
- ...
-}
-
-InterFreqCarrierFreqList ::= SEQUENCE (SIZE (1..maxFreq)) OF InterFreqCarrierFreqInfo
-
-InterFreqCarrierFreqInfo ::= SEQUENCE {
- dl-CarrierFreq ARFCN-ValueEUTRA,
- q-RxLevMin Q-RxLevMin,
- p-Max P-Max OPTIONAL, -- Need OP
- t-ReselectionEUTRA T-Reselection,
- t-ReselectionEUTRA-SF SpeedStateScaleFactors OPTIONAL, -- Need OP
- threshX-High ReselectionThreshold,
- threshX-Low ReselectionThreshold,
- allowedMeasBandwidth AllowedMeasBandwidth,
- presenceAntennaPort1 PresenceAntennaPort1,
- cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
- neighCellConfig NeighCellConfig,
- q-OffsetFreq Q-OffsetRange DEFAULT dB0,
- interFreqNeighCellList InterFreqNeighCellList OPTIONAL, -- Need OR
- interFreqBlackCellList InterFreqBlackCellList OPTIONAL, -- Need OR
- ...
-}
-
-InterFreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellInter)) OF InterFreqNeighCellInfo
-
-InterFreqNeighCellInfo ::= SEQUENCE {
- physCellId PhysCellId,
- q-OffsetCell Q-OffsetRange
-}
-
-InterFreqBlackCellList ::= SEQUENCE (SIZE (1..maxCellBlack)) OF PhysCellIdRange
-
-
-SystemInformationBlockType6 ::= SEQUENCE {
- carrierFreqListUTRA-FDD CarrierFreqListUTRA-FDD OPTIONAL, -- Need OR
- carrierFreqListUTRA-TDD CarrierFreqListUTRA-TDD OPTIONAL, -- Need OR
- t-ReselectionUTRA T-Reselection,
- t-ReselectionUTRA-SF SpeedStateScaleFactors OPTIONAL, -- Need OP
- ...
-}
-
-CarrierFreqListUTRA-FDD ::= SEQUENCE (SIZE (1..maxUTRA-FDD-Carrier)) OF CarrierFreqUTRA-FDD
-
-CarrierFreqUTRA-FDD ::= SEQUENCE {
- carrierFreq ARFCN-ValueUTRA,
- cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
- threshX-High ReselectionThreshold,
- threshX-Low ReselectionThreshold,
- q-RxLevMin INTEGER (-60..-13),
- p-MaxUTRA INTEGER (-50..33),
- q-QualMin INTEGER (-24..0),
- ...
-}
-
-CarrierFreqListUTRA-TDD ::= SEQUENCE (SIZE (1..maxUTRA-TDD-Carrier)) OF CarrierFreqUTRA-TDD
-
-CarrierFreqUTRA-TDD ::= SEQUENCE {
- carrierFreq ARFCN-ValueUTRA,
- cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
- threshX-High ReselectionThreshold,
- threshX-Low ReselectionThreshold,
- q-RxLevMin INTEGER (-60..-13),
- p-MaxUTRA INTEGER (-50..33),
- ...
-}
-
-
-SystemInformationBlockType7 ::= SEQUENCE {
- t-ReselectionGERAN T-Reselection,
- t-ReselectionGERAN-SF SpeedStateScaleFactors OPTIONAL, -- Need OR
- carrierFreqsInfoList CarrierFreqsInfoListGERAN OPTIONAL, -- Need OR
- ...
-}
-
-CarrierFreqsInfoListGERAN ::= SEQUENCE (SIZE (1..maxGNFG)) OF CarrierFreqsInfoGERAN
-
-CarrierFreqsInfoGERAN ::= SEQUENCE {
- carrierFreqs CarrierFreqsGERAN,
- commonInfo SEQUENCE {
- cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
- ncc-Permitted BIT STRING (SIZE (8)),
- q-RxLevMin INTEGER (0..45),
- p-MaxGERAN INTEGER (0..39) OPTIONAL, -- Need OP
- threshX-High ReselectionThreshold,
- threshX-Low ReselectionThreshold
- },
- ...
-}
-
-
-SystemInformationBlockType8 ::= SEQUENCE {
- systemTimeInfo SystemTimeInfoCDMA2000 OPTIONAL, -- Need OR
- searchWindowSize INTEGER (0..15) OPTIONAL, -- Need OR
- parametersHRPD SEQUENCE {
- preRegistrationInfoHRPD PreRegistrationInfoHRPD,
- cellReselectionParametersHRPD CellReselectionParametersCDMA2000 OPTIONAL -- Need OR
- } OPTIONAL, -- Need OR
- parameters1XRTT SEQUENCE {
- csfb-RegistrationParam1XRTT CSFB-RegistrationParam1XRTT OPTIONAL, -- Need OP
- longCodeState1XRTT BIT STRING (SIZE (42)) OPTIONAL, -- Need OR
- cellReselectionParameters1XRTT CellReselectionParametersCDMA2000 OPTIONAL -- Need OR
- } OPTIONAL, -- Need OR
- ...
-}
-
-CellReselectionParametersCDMA2000 ::= SEQUENCE {
- bandClassList BandClassListCDMA2000,
- neighCellList NeighCellListCDMA2000,
- t-ReselectionCDMA2000 T-Reselection,
- t-ReselectionCDMA2000-SF SpeedStateScaleFactors OPTIONAL -- Need OP
-}
-NeighCellListCDMA2000 ::= SEQUENCE (SIZE (1..16)) OF NeighCellCDMA2000
-
-NeighCellCDMA2000 ::= SEQUENCE {
- bandClass BandclassCDMA2000,
- neighCellsPerFreqList NeighCellsPerBandclassListCDMA2000
-}
-
-NeighCellsPerBandclassListCDMA2000 ::= SEQUENCE (SIZE (1..16)) OF NeighCellsPerBandclassCDMA2000
-
-NeighCellsPerBandclassCDMA2000 ::= SEQUENCE {
- arfcn ARFCN-ValueCDMA2000,
- physCellIdList PhysCellIdListCDMA2000
-}
-
-PhysCellIdListCDMA2000 ::= SEQUENCE (SIZE (1..16)) OF PhysCellIdCDMA2000
-
-BandClassListCDMA2000 ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandClassInfoCDMA2000
-
-BandClassInfoCDMA2000 ::= SEQUENCE {
- bandClass BandclassCDMA2000,
- cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
- threshX-High INTEGER (0..63),
- threshX-Low INTEGER (0..63),
- ...
-}
-
-
-SystemInformationBlockType9 ::= SEQUENCE {
- hnb-Name OCTET STRING (SIZE(1..48)) OPTIONAL, -- Need OR
- ...
-}
-
-
-SystemInformationBlockType10 ::= SEQUENCE {
- messageIdentifier BIT STRING (SIZE (16)),
- serialNumber BIT STRING (SIZE (16)),
- warningType OCTET STRING (SIZE (2)),
- warningSecurityInfo OCTET STRING (SIZE (50)) OPTIONAL, -- Need OP
- ...
-}
-
-
-SystemInformationBlockType11 ::= SEQUENCE {
- messageIdentifier BIT STRING (SIZE (16)),
- serialNumber BIT STRING (SIZE (16)),
- warningMessageSegmentType ENUMERATED {notLastSegment, lastSegment},
- warningMessageSegmentNumber INTEGER (0..63),
- warningMessageSegment OCTET STRING,
- dataCodingScheme OCTET STRING (SIZE (1)) OPTIONAL, -- Cond Segment1
- ...
-}
-
-
-AntennaInfoCommon ::= SEQUENCE {
- antennaPortsCount ENUMERATED {an1, an2, an4, spare1}
-}
-
-AntennaInfoDedicated ::= SEQUENCE {
- transmissionMode ENUMERATED {
- tm1, tm2, tm3, tm4, tm5, tm6,
- tm7, spare1},
- codebookSubsetRestriction CHOICE {
- n2TxAntenna-tm3 BIT STRING (SIZE (2)),
- n4TxAntenna-tm3 BIT STRING (SIZE (4)),
- n2TxAntenna-tm4 BIT STRING (SIZE (6)),
- n4TxAntenna-tm4 BIT STRING (SIZE (64)),
- n2TxAntenna-tm5 BIT STRING (SIZE (4)),
- n4TxAntenna-tm5 BIT STRING (SIZE (16)),
- n2TxAntenna-tm6 BIT STRING (SIZE (4)),
- n4TxAntenna-tm6 BIT STRING (SIZE (16))
- } OPTIONAL, -- Cond TM
- ue-TransmitAntennaSelection CHOICE{
- release NULL,
- setup ENUMERATED {closedLoop, openLoop}
- }
-}
-
-
-CQI-ReportConfig ::= SEQUENCE {
- cqi-ReportModeAperiodic ENUMERATED {
- rm12, rm20, rm22, rm30, rm31,
- spare3, spare2, spare1} OPTIONAL, -- Need OR
- nomPDSCH-RS-EPRE-Offset INTEGER (-1..6),
- cqi-ReportPeriodic CQI-ReportPeriodic OPTIONAL -- Need ON
-}
-
-CQI-ReportPeriodic ::= CHOICE {
- release NULL,
- setup SEQUENCE {
- cqi-PUCCH-ResourceIndex INTEGER (0.. 1185),
- cqi-pmi-ConfigIndex INTEGER (0..1023),
- cqi-FormatIndicatorPeriodic CHOICE {
- widebandCQI NULL,
- subbandCQI SEQUENCE {
- k INTEGER (1..4)
- }
- },
- ri-ConfigIndex INTEGER (0..1023) OPTIONAL, -- Need OR
- simultaneousAckNackAndCQI BOOLEAN
- }
-}
-
-
-DRB-Identity ::= INTEGER (1..32)
-
-
-LogicalChannelConfig ::= SEQUENCE {
- ul-SpecificParameters SEQUENCE {
- priority INTEGER (1..16),
- prioritisedBitRate ENUMERATED {
- kBps0, kBps8, kBps16, kBps32, kBps64, kBps128,
- kBps256, infinity, spare8, spare7, spare6,
- spare5, spare4, spare3, spare2, spare1},
- bucketSizeDuration ENUMERATED {
- ms50, ms100, ms150, ms300, ms500, ms1000, spare2,
- spare1},
- logicalChannelGroup INTEGER (0..3) OPTIONAL -- Need OR
- } OPTIONAL, -- Cond UL
- ...
-}
-
-
-MAC-MainConfig ::= SEQUENCE {
- ul-SCH-Config SEQUENCE {
- maxHARQ-Tx ENUMERATED {
- n1, n2, n3, n4, n5, n6, n7, n8,
- n10, n12, n16, n20, n24, n28,
- spare2, spare1} OPTIONAL, -- Need ON
- periodicBSR-Timer ENUMERATED {
- sf5, sf10, sf16, sf20, sf32, sf40, sf64, sf80,
- sf128, sf160, sf320, sf640, sf1280, sf2560,
- infinity, spare1} OPTIONAL, -- Need ON
- retxBSR-Timer ENUMERATED {
- sf320, sf640, sf1280, sf2560, sf5120,
- sf10240, spare2, spare1},
- ttiBundling BOOLEAN
- } OPTIONAL, -- Need ON
- drx-Config DRX-Config OPTIONAL, -- Need ON
- timeAlignmentTimerDedicated TimeAlignmentTimer,
- phr-Config CHOICE {
- release NULL,
- setup SEQUENCE {
- periodicPHR-Timer ENUMERATED {sf10, sf20, sf50, sf100, sf200,
- sf500, sf1000, infinity},
- prohibitPHR-Timer ENUMERATED {sf0, sf10, sf20, sf50, sf100,
- sf200, sf500, sf1000},
- dl-PathlossChange ENUMERATED {dB1, dB3, dB6, infinity}
- }
- } OPTIONAL, -- Need ON
- ...
-}
-
-DRX-Config ::= CHOICE {
- release NULL,
- setup SEQUENCE {
- onDurationTimer ENUMERATED {
- psf1, psf2, psf3, psf4, psf5, psf6,
- psf8, psf10, psf20, psf30, psf40,
- psf50, psf60, psf80, psf100,
- psf200},
- drx-InactivityTimer ENUMERATED {
- psf1, psf2, psf3, psf4, psf5, psf6,
- psf8, psf10, psf20, psf30, psf40,
- psf50, psf60, psf80, psf100,
- psf200, psf300, psf500, psf750,
- psf1280, psf1920, psf2560, spare10,
- spare9, spare8, spare7, spare6,
- spare5, spare4, spare3, spare2,
- spare1},
- drx-RetransmissionTimer ENUMERATED {
- psf1, psf2, psf4, psf6, psf8, psf16,
- psf24, psf33},
- longDRX-CycleStartOffset CHOICE {
- sf10 INTEGER(0..9),
- sf20 INTEGER(0..19),
- sf32 INTEGER(0..31),
- sf40 INTEGER(0..39),
- sf64 INTEGER(0..63),
- sf80 INTEGER(0..79),
- sf128 INTEGER(0..127),
- sf160 INTEGER(0..159),
- sf256 INTEGER(0..255),
- sf320 INTEGER(0..319),
- sf512 INTEGER(0..511),
- sf640 INTEGER(0..639),
- sf1024 INTEGER(0..1023),
- sf1280 INTEGER(0..1279),
- sf2048 INTEGER(0..2047),
- sf2560 INTEGER(0..2559)
- },
- shortDRX SEQUENCE {
- shortDRX-Cycle ENUMERATED {
- sf2, sf5, sf8, sf10, sf16, sf20,
- sf32, sf40, sf64, sf80, sf128, sf160,
- sf256, sf320, sf512, sf640},
- drxShortCycleTimer INTEGER (1..16)
- } OPTIONAL -- Need OR
- }
-}
-
-
-PDCP-Config ::= SEQUENCE {
- discardTimer ENUMERATED {
- ms50, ms100, ms150, ms300, ms500,
- ms750, ms1500, infinity
- } OPTIONAL, -- Cond Setup
- rlc-AM SEQUENCE {
- statusReportRequired BOOLEAN
- } OPTIONAL, -- Cond Rlc-AM
- rlc-UM SEQUENCE {
- pdcp-SN-Size ENUMERATED {len7bits, len12bits}
- } OPTIONAL, -- Cond Rlc-UM
- headerCompression CHOICE {
- notUsed NULL,
- rohc SEQUENCE {
- maxCID INTEGER (1..16383) DEFAULT 15,
- profiles SEQUENCE {
- profile0x0001 BOOLEAN,
- profile0x0002 BOOLEAN,
- profile0x0003 BOOLEAN,
- profile0x0004 BOOLEAN,
- profile0x0006 BOOLEAN,
- profile0x0101 BOOLEAN,
- profile0x0102 BOOLEAN,
- profile0x0103 BOOLEAN,
- profile0x0104 BOOLEAN
- },
- ...
- }
- },
- ...
-}
-
-
-PDSCH-ConfigCommon::= SEQUENCE {
- referenceSignalPower INTEGER (-60..50),
- p-b INTEGER (0..3)
-}
-
-PDSCH-ConfigDedicated::= SEQUENCE {
- p-a ENUMERATED {
- dB-6, dB-4dot77, dB-3, dB-1dot77,
- dB0, dB1, dB2, dB3 }
-}
-
-
-PHICH-Config ::= SEQUENCE {
- phich-Duration ENUMERATED {normal, extended},
- phich-Resource ENUMERATED {oneSixth, half, one, two}
-}
-
-
-PhysicalConfigDedicated ::= SEQUENCE {
- pdsch-ConfigDedicated PDSCH-ConfigDedicated OPTIONAL, -- Need ON
- pucch-ConfigDedicated PUCCH-ConfigDedicated OPTIONAL, -- Need ON
- pusch-ConfigDedicated PUSCH-ConfigDedicated OPTIONAL, -- Need ON
- uplinkPowerControlDedicated UplinkPowerControlDedicated OPTIONAL, -- Need ON
- tpc-PDCCH-ConfigPUCCH TPC-PDCCH-Config OPTIONAL, -- Need ON
- tpc-PDCCH-ConfigPUSCH TPC-PDCCH-Config OPTIONAL, -- Need ON
- cqi-ReportConfig CQI-ReportConfig OPTIONAL, -- Need ON
- soundingRS-UL-ConfigDedicated SoundingRS-UL-ConfigDedicated OPTIONAL, -- Need ON
- antennaInfo CHOICE {
- explicitValue AntennaInfoDedicated,
- defaultValue NULL
- } OPTIONAL, -- Need ON
- schedulingRequestConfig SchedulingRequestConfig OPTIONAL, -- Need ON
- ...
-}
-
-
-P-Max ::= INTEGER (-30..33)
-
-
-PRACH-ConfigSIB ::= SEQUENCE {
- rootSequenceIndex INTEGER (0..837),
- prach-ConfigInfo PRACH-ConfigInfo
-}
-
-PRACH-Config ::= SEQUENCE {
- rootSequenceIndex INTEGER (0..837),
- prach-ConfigInfo PRACH-ConfigInfo OPTIONAL -- Need ON
-}
-
-PRACH-ConfigInfo ::= SEQUENCE {
- prach-ConfigIndex INTEGER (0..63),
- highSpeedFlag BOOLEAN,
- zeroCorrelationZoneConfig INTEGER (0..15),
- prach-FreqOffset INTEGER (0..94)
-}
-
-
-PresenceAntennaPort1 ::= BOOLEAN
-
-
-PUCCH-ConfigCommon ::= SEQUENCE {
- deltaPUCCH-Shift ENUMERATED {ds1, ds2, ds3},
- nRB-CQI INTEGER (0..98),
- nCS-AN INTEGER (0..7),
- n1PUCCH-AN INTEGER (0..2047)
-}
-
-PUCCH-ConfigDedicated ::= SEQUENCE {
- ackNackRepetition CHOICE{
- release NULL,
- setup SEQUENCE {
- repetitionFactor ENUMERATED { n2, n4, n6, spare1},
- n1PUCCH-AN-Rep INTEGER (0..2047)
- }
- },
- tdd-AckNackFeedbackMode ENUMERATED {bundling, multiplexing} OPTIONAL -- Cond TDD
-}
-
-
-PUSCH-ConfigCommon ::= SEQUENCE {
- pusch-ConfigBasic SEQUENCE {
- n-SB INTEGER (1..4),
- hoppingMode ENUMERATED {interSubFrame, intraAndInterSubFrame},
- pusch-HoppingOffset INTEGER (0..98),
- enable64QAM BOOLEAN
- },
- ul-ReferenceSignalsPUSCH UL-ReferenceSignalsPUSCH
-}
-
-PUSCH-ConfigDedicated ::= SEQUENCE {
- betaOffset-ACK-Index INTEGER (0..15),
- betaOffset-RI-Index INTEGER (0..15),
- betaOffset-CQI-Index INTEGER (0..15)
-}
-
-UL-ReferenceSignalsPUSCH ::= SEQUENCE {
- groupHoppingEnabled BOOLEAN,
- groupAssignmentPUSCH INTEGER (0..29),
- sequenceHoppingEnabled BOOLEAN,
- cyclicShift INTEGER (0..7)
-}
-
-
-RACH-ConfigCommon ::= SEQUENCE {
- preambleInfo SEQUENCE {
- numberOfRA-Preambles ENUMERATED {
- n4, n8, n12, n16 ,n20, n24, n28,
- n32, n36, n40, n44, n48, n52, n56,
- n60, n64},
- preamblesGroupAConfig SEQUENCE {
- sizeOfRA-PreamblesGroupA ENUMERATED {
- n4, n8, n12, n16 ,n20, n24, n28,
- n32, n36, n40, n44, n48, n52, n56,
- n60},
- messageSizeGroupA ENUMERATED {b56, b144, b208, b256},
- messagePowerOffsetGroupB ENUMERATED {
- minusinfinity, dB0, dB5, dB8, dB10, dB12,
- dB15, dB18},
- ...
- } OPTIONAL -- Need OP
- },
- powerRampingParameters SEQUENCE {
- powerRampingStep ENUMERATED {dB0, dB2,dB4, dB6},
- preambleInitialReceivedTargetPower ENUMERATED {
- dBm-120, dBm-118, dBm-116, dBm-114, dBm-112,
- dBm-110, dBm-108, dBm-106, dBm-104, dBm-102,
- dBm-100, dBm-98, dBm-96, dBm-94,
- dBm-92, dBm-90}
- },
- ra-SupervisionInfo SEQUENCE {
- preambleTransMax ENUMERATED {
- n3, n4, n5, n6, n7, n8, n10, n20, n50,
- n100, n200},
- ra-ResponseWindowSize ENUMERATED {
- sf2, sf3, sf4, sf5, sf6, sf7,
- sf8, sf10},
- mac-ContentionResolutionTimer ENUMERATED {
- sf8, sf16, sf24, sf32, sf40, sf48,
- sf56, sf64}
- },
- maxHARQ-Msg3Tx INTEGER (1..8),
- ...
-}
-
-
-RACH-ConfigDedicated ::= SEQUENCE {
- ra-PreambleIndex INTEGER (0..63),
- ra-PRACH-MaskIndex INTEGER (0..15)
-}
-
-
-RadioResourceConfigCommonSIB ::= SEQUENCE {
- rach-ConfigCommon RACH-ConfigCommon,
- bcch-Config BCCH-Config,
- pcch-Config PCCH-Config,
- prach-Config PRACH-ConfigSIB,
- pdsch-ConfigCommon PDSCH-ConfigCommon,
- pusch-ConfigCommon PUSCH-ConfigCommon,
- pucch-ConfigCommon PUCCH-ConfigCommon,
- soundingRS-UL-ConfigCommon SoundingRS-UL-ConfigCommon,
- uplinkPowerControlCommon UplinkPowerControlCommon,
- ul-CyclicPrefixLength UL-CyclicPrefixLength,
- ...
-}
-
-RadioResourceConfigCommon ::= SEQUENCE {
- rach-ConfigCommon RACH-ConfigCommon OPTIONAL, -- Need ON
- prach-Config PRACH-Config,
- pdsch-ConfigCommon PDSCH-ConfigCommon OPTIONAL, -- Need ON
- pusch-ConfigCommon PUSCH-ConfigCommon,
- phich-Config PHICH-Config OPTIONAL, -- Need ON
- pucch-ConfigCommon PUCCH-ConfigCommon OPTIONAL, -- Need ON
- soundingRS-UL-ConfigCommon SoundingRS-UL-ConfigCommon OPTIONAL, -- Need ON
- uplinkPowerControlCommon UplinkPowerControlCommon OPTIONAL, -- Need ON
- antennaInfoCommon AntennaInfoCommon OPTIONAL, -- Need ON
- p-Max P-Max OPTIONAL, -- Need OP
- tdd-Config TDD-Config OPTIONAL, -- Cond TDD
- ul-CyclicPrefixLength UL-CyclicPrefixLength,
- ...
-}
-
-BCCH-Config ::= SEQUENCE {
- modificationPeriodCoeff ENUMERATED {n2, n4, n8, n16}
-}
-
-PCCH-Config ::= SEQUENCE {
- defaultPagingCycle ENUMERATED {
- rf32, rf64, rf128, rf256},
- nB ENUMERATED {
- fourT, twoT, oneT, halfT, quarterT, oneEighthT,
- oneSixteenthT, oneThirtySecondT}
-}
-
-UL-CyclicPrefixLength ::= ENUMERATED {len1, len2}
-
-
-RadioResourceConfigDedicated ::= SEQUENCE {
- srb-ToAddModList SRB-ToAddModList OPTIONAL, -- Cond HO-Conn
- drb-ToAddModList DRB-ToAddModList OPTIONAL, -- Cond HO-toEUTRA
- drb-ToReleaseList DRB-ToReleaseList OPTIONAL, -- Need ON
- mac-MainConfig CHOICE {
- explicitValue MAC-MainConfig,
- defaultValue NULL
- } OPTIONAL, -- Cond HO-toEUTRA2
- sps-Config SPS-Config OPTIONAL, -- Need ON
- physicalConfigDedicated PhysicalConfigDedicated OPTIONAL, -- Need ON
- ...
-}
-
-SRB-ToAddModList ::= SEQUENCE (SIZE (1..2)) OF SRB-ToAddMod
-
-SRB-ToAddMod ::= SEQUENCE {
- srb-Identity INTEGER (1..2),
- rlc-Config CHOICE {
- explicitValue RLC-Config,
- defaultValue NULL
- } OPTIONAL, -- Cond Setup
- logicalChannelConfig CHOICE {
- explicitValue LogicalChannelConfig,
- defaultValue NULL
- } OPTIONAL, -- Cond Setup
- ...
-}
-
-DRB-ToAddModList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-ToAddMod
-
-DRB-ToAddMod ::= SEQUENCE {
- eps-BearerIdentity INTEGER (0..15) OPTIONAL, -- Cond DRB-Setup
- drb-Identity DRB-Identity,
- pdcp-Config PDCP-Config OPTIONAL, -- Cond PDCP
- rlc-Config RLC-Config OPTIONAL, -- Cond Setup
- logicalChannelIdentity INTEGER (3..10) OPTIONAL, -- Cond DRB-Setup
- logicalChannelConfig LogicalChannelConfig OPTIONAL, -- Cond Setup
- ...
-}
-
-DRB-ToReleaseList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-Identity
-
-
-RLC-Config ::= CHOICE {
- am SEQUENCE {
- ul-AM-RLC UL-AM-RLC,
- dl-AM-RLC DL-AM-RLC
- },
- um-Bi-Directional SEQUENCE {
- ul-UM-RLC UL-UM-RLC,
- dl-UM-RLC DL-UM-RLC
- },
- um-Uni-Directional-UL SEQUENCE {
- ul-UM-RLC UL-UM-RLC
- },
- um-Uni-Directional-DL SEQUENCE {
- dl-UM-RLC DL-UM-RLC
- },
- ...
-}
-
-UL-AM-RLC ::= SEQUENCE {
- t-PollRetransmit T-PollRetransmit,
- pollPDU PollPDU,
- pollByte PollByte,
- maxRetxThreshold ENUMERATED {
- t1, t2, t3, t4, t6, t8, t16, t32}
-}
-
-DL-AM-RLC ::= SEQUENCE {
- t-Reordering T-Reordering,
- t-StatusProhibit T-StatusProhibit
-}
-
-UL-UM-RLC ::= SEQUENCE {
- sn-FieldLength SN-FieldLength
-}
-
-DL-UM-RLC ::= SEQUENCE {
- sn-FieldLength SN-FieldLength,
- t-Reordering T-Reordering
-}
-
-SN-FieldLength ::= ENUMERATED {size5, size10}
-
-T-PollRetransmit ::= ENUMERATED {
- ms5, ms10, ms15, ms20, ms25, ms30, ms35,
- ms40, ms45, ms50, ms55, ms60, ms65, ms70,
- ms75, ms80, ms85, ms90, ms95, ms100, ms105,
- ms110, ms115, ms120, ms125, ms130, ms135,
- ms140, ms145, ms150, ms155, ms160, ms165,
- ms170, ms175, ms180, ms185, ms190, ms195,
- ms200, ms205, ms210, ms215, ms220, ms225,
- ms230, ms235, ms240, ms245, ms250, ms300,
- ms350, ms400, ms450, ms500, spare9, spare8,
- spare7, spare6, spare5, spare4, spare3,
- spare2, spare1}
-
-PollPDU ::= ENUMERATED {
- p4, p8, p16, p32, p64, p128, p256, pInfinity}
-
-PollByte ::= ENUMERATED {
- kB25, kB50, kB75, kB100, kB125, kB250, kB375,
- kB500, kB750, kB1000, kB1250, kB1500, kB2000,
- kB3000, kBinfinity, spare1}
-
-T-Reordering ::= ENUMERATED {
- ms0, ms5, ms10, ms15, ms20, ms25, ms30, ms35,
- ms40, ms45, ms50, ms55, ms60, ms65, ms70,
- ms75, ms80, ms85, ms90, ms95, ms100, ms110,
- ms120, ms130, ms140, ms150, ms160, ms170,
- ms180, ms190, ms200, spare1}
-
-T-StatusProhibit ::= ENUMERATED {
- ms0, ms5, ms10, ms15, ms20, ms25, ms30, ms35,
- ms40, ms45, ms50, ms55, ms60, ms65, ms70,
- ms75, ms80, ms85, ms90, ms95, ms100, ms105,
- ms110, ms115, ms120, ms125, ms130, ms135,
- ms140, ms145, ms150, ms155, ms160, ms165,
- ms170, ms175, ms180, ms185, ms190, ms195,
- ms200, ms205, ms210, ms215, ms220, ms225,
- ms230, ms235, ms240, ms245, ms250, ms300,
- ms350, ms400, ms450, ms500, spare8, spare7,
- spare6, spare5, spare4, spare3, spare2,
- spare1}
-
-
-SchedulingRequestConfig ::= CHOICE {
- release NULL,
- setup SEQUENCE {
- sr-PUCCH-ResourceIndex INTEGER (0..2047),
- sr-ConfigIndex INTEGER (0..155),
- dsr-TransMax ENUMERATED {
- n4, n8, n16, n32, n64, spare3, spare2, spare1}
- }
-}
-
-
-SoundingRS-UL-ConfigCommon ::= CHOICE {
- release NULL,
- setup SEQUENCE {
- srs-BandwidthConfig ENUMERATED {bw0, bw1, bw2, bw3, bw4, bw5, bw6, bw7},
- srs-SubframeConfig ENUMERATED {
- sc0, sc1, sc2, sc3, sc4, sc5, sc6, sc7,
- sc8, sc9, sc10, sc11, sc12, sc13, sc14, sc15},
- ackNackSRS-SimultaneousTransmission BOOLEAN,
- srs-MaxUpPts ENUMERATED {true} OPTIONAL -- Cond TDD
- }
-}
-
-SoundingRS-UL-ConfigDedicated ::= CHOICE{
- release NULL,
- setup SEQUENCE {
- srs-Bandwidth ENUMERATED {bw0, bw1, bw2, bw3},
- srs-HoppingBandwidth ENUMERATED {hbw0, hbw1, hbw2, hbw3},
- freqDomainPosition INTEGER (0..23),
- duration BOOLEAN,
- srs-ConfigIndex INTEGER (0..1023),
- transmissionComb INTEGER (0..1),
- cyclicShift ENUMERATED {cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7}
- }
-}
-
-
-
-SPS-Config ::= SEQUENCE {
- semiPersistSchedC-RNTI C-RNTI OPTIONAL, -- Need OR
- sps-ConfigDL SPS-ConfigDL OPTIONAL, -- Need ON
- sps-ConfigUL SPS-ConfigUL OPTIONAL -- Need ON
-}
-
-SPS-ConfigDL ::= CHOICE{
- release NULL,
- setup SEQUENCE {
- semiPersistSchedIntervalDL ENUMERATED {
- sf10, sf20, sf32, sf40, sf64, sf80,
- sf128, sf160, sf320, sf640, spare6,
- spare5, spare4, spare3, spare2,
- spare1},
- numberOfConfSPS-Processes INTEGER (1..8),
- n1-PUCCH-AN-PersistentList N1-PUCCH-AN-PersistentList,
- ...
- }
-}
-
-SPS-ConfigUL ::= CHOICE {
- release NULL,
- setup SEQUENCE {
- semiPersistSchedIntervalUL ENUMERATED {
- sf10, sf20, sf32, sf40, sf64, sf80,
- sf128, sf160, sf320, sf640, spare6,
- spare5, spare4, spare3, spare2,
- spare1},
- implicitReleaseAfter ENUMERATED {e2, e3, e4, e8},
- p0-Persistent SEQUENCE {
- p0-NominalPUSCH-Persistent INTEGER (-126..24),
- p0-UE-PUSCH-Persistent INTEGER (-8..7)
- } OPTIONAL, -- Need OP
- twoIntervalsConfig ENUMERATED {true} OPTIONAL, -- Cond TDD
- ...
- }
-}
-
-N1-PUCCH-AN-PersistentList ::= SEQUENCE (SIZE (1..4)) OF INTEGER (0..2047)
-
-
-TDD-Config ::= SEQUENCE {
- subframeAssignment ENUMERATED {
- sa0, sa1, sa2, sa3, sa4, sa5, sa6},
- specialSubframePatterns ENUMERATED {
- ssp0, ssp1, ssp2, ssp3, ssp4,ssp5, ssp6, ssp7,
- ssp8}
-}
-
-
-TimeAlignmentTimer ::= ENUMERATED {
- sf500, sf750, sf1280, sf1920, sf2560, sf5120,
- sf10240, infinity}
-
-TPC-PDCCH-Config::= CHOICE {
- release NULL,
- setup SEQUENCE {
- tpc-RNTI BIT STRING (SIZE (16)),
- tpc-Index TPC-Index
- }
-}
-
-TPC-Index ::= CHOICE {
- indexOfFormat3 INTEGER (1..15),
- indexOfFormat3A INTEGER (1..31)
-}
-
-
-UplinkPowerControlCommon ::= SEQUENCE {
- p0-NominalPUSCH INTEGER (-126..24),
- alpha ENUMERATED {al0, al04, al05, al06, al07, al08, al09, al1},
- p0-NominalPUCCH INTEGER (-127..-96),
- deltaFList-PUCCH DeltaFList-PUCCH,
- deltaPreambleMsg3 INTEGER (-1..6)
-}
-
-UplinkPowerControlDedicated ::= SEQUENCE {
- p0-UE-PUSCH INTEGER (-8..7),
- deltaMCS-Enabled ENUMERATED {en0, en1},
- accumulationEnabled BOOLEAN,
- p0-UE-PUCCH INTEGER (-8..7),
- pSRS-Offset INTEGER (0..15),
- filterCoefficient FilterCoefficient DEFAULT fc4
-}
-
-DeltaFList-PUCCH ::= SEQUENCE {
- deltaF-PUCCH-Format1 ENUMERATED {deltaF-2, deltaF0, deltaF2},
- deltaF-PUCCH-Format1b ENUMERATED {deltaF1, deltaF3, deltaF5},
- deltaF-PUCCH-Format2 ENUMERATED {deltaF-2, deltaF0, deltaF1, deltaF2},
- deltaF-PUCCH-Format2a ENUMERATED {deltaF-2, deltaF0, deltaF2},
- deltaF-PUCCH-Format2b ENUMERATED {deltaF-2, deltaF0, deltaF2}
-}
-
-
-NextHopChainingCount ::= INTEGER (0..7)
-
-
-SecurityAlgorithmConfig ::= SEQUENCE {
- cipheringAlgorithm ENUMERATED {
- eea0, eea1, eea2, spare5, spare4, spare3,
- spare2, spare1, ...},
- integrityProtAlgorithm ENUMERATED {
- reserved, eia1, eia2, spare5, spare4, spare3,
- spare2, spare1, ...}
-}
-
-
-ShortMAC-I ::= BIT STRING (SIZE (16))
-
-
-AdditionalSpectrumEmission ::= INTEGER (1..32)
-
-
-ARFCN-ValueCDMA2000 ::= INTEGER (0..2047)
-
-
-ARFCN-ValueEUTRA ::= INTEGER (0..maxEARFCN)
-
-
-ARFCN-ValueGERAN ::= INTEGER (0..1023)
-
-
-ARFCN-ValueUTRA ::= INTEGER (0..16383)
-
-
-BandclassCDMA2000 ::= ENUMERATED {
- bc0, bc1, bc2, bc3, bc4, bc5, bc6, bc7, bc8,
- bc9, bc10, bc11, bc12, bc13, bc14, bc15, bc16,
- bc17, spare14, spare13, spare12, spare11, spare10,
- spare9, spare8, spare7, spare6, spare5, spare4,
- spare3, spare2, spare1, ...}
-
-
-BandIndicatorGERAN ::= ENUMERATED {dcs1800, pcs1900}
-
-
-CarrierFreqCDMA2000 ::= SEQUENCE {
- bandClass BandclassCDMA2000,
- arfcn ARFCN-ValueCDMA2000
-}
-
-
-CarrierFreqGERAN ::= SEQUENCE {
- arfcn ARFCN-ValueGERAN,
- bandIndicator BandIndicatorGERAN
-}
-
-
-CarrierFreqsGERAN ::= SEQUENCE {
- startingARFCN ARFCN-ValueGERAN,
- bandIndicator BandIndicatorGERAN,
- followingARFCNs CHOICE {
- explicitListOfARFCNs ExplicitListOfARFCNs,
- equallySpacedARFCNs SEQUENCE {
- arfcn-Spacing INTEGER (1..8),
- numberOfFollowingARFCNs INTEGER (0..31)
- },
- variableBitMapOfARFCNs OCTET STRING (SIZE (1..16))
- }
-}
-
-ExplicitListOfARFCNs ::= SEQUENCE (SIZE (0..31)) OF ARFCN-ValueGERAN
-
-
-CDMA2000-Type ::= ENUMERATED {type1XRTT, typeHRPD}
-
-
-CellIdentity ::= BIT STRING (SIZE (28))
-
-
-CellIndexList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellIndex
-
-CellIndex ::= INTEGER (1..maxCellMeas)
-
-
-CellReselectionPriority ::= INTEGER (0..7)
-
-
-CSFB-RegistrationParam1XRTT ::= SEQUENCE {
- sid BIT STRING (SIZE (15)),
- nid BIT STRING (SIZE (16)),
- multipleSID BOOLEAN,
- multipleNID BOOLEAN,
- homeReg BOOLEAN,
- foreignSIDReg BOOLEAN,
- foreignNIDReg BOOLEAN,
- parameterReg BOOLEAN,
- powerUpReg BOOLEAN,
- registrationPeriod BIT STRING (SIZE (7)),
- registrationZone BIT STRING (SIZE (12)),
- totalZone BIT STRING (SIZE (3)),
- zoneTimer BIT STRING (SIZE (3))
-}
-
-
-CellGlobalIdEUTRA ::= SEQUENCE {
- plmn-Identity PLMN-Identity,
- cellIdentity CellIdentity
-}
-
-
-CellGlobalIdUTRA ::= SEQUENCE {
- plmn-Identity PLMN-Identity,
- cellIdentity BIT STRING (SIZE (28))
-}
-
-
-CellGlobalIdGERAN ::= SEQUENCE {
- plmn-Identity PLMN-Identity,
- locationAreaCode BIT STRING (SIZE (16)),
- cellIdentity BIT STRING (SIZE (16))
-}
-
-
-CellGlobalIdCDMA2000 ::= CHOICE {
- cellGlobalId1XRTT BIT STRING (SIZE (47)),
- cellGlobalIdHRPD BIT STRING (SIZE (128))
-}
-
-
-MobilityControlInfo ::= SEQUENCE {
- targetPhysCellId PhysCellId,
- carrierFreq CarrierFreqEUTRA OPTIONAL, -- Cond HO-toEUTRA
- carrierBandwidth CarrierBandwidthEUTRA OPTIONAL, -- Cond HO-toEUTRA
- additionalSpectrumEmission AdditionalSpectrumEmission OPTIONAL, -- Cond HO-toEUTRA
- t304 ENUMERATED {
- ms50, ms100, ms150, ms200, ms500, ms1000,
- ms2000, spare1},
- newUE-Identity C-RNTI,
- radioResourceConfigCommon RadioResourceConfigCommon,
- rach-ConfigDedicated RACH-ConfigDedicated OPTIONAL, -- Need OP
- ...
-}
-
-CarrierBandwidthEUTRA ::= SEQUENCE {
- dl-Bandwidth ENUMERATED {
- n6, n15, n25, n50, n75, n100, spare10,
- spare9, spare8, spare7, spare6, spare5,
- spare4, spare3, spare2, spare1},
- ul-Bandwidth ENUMERATED {
- n6, n15, n25, n50, n75, n100, spare10,
- spare9, spare8, spare7, spare6, spare5,
- spare4, spare3, spare2, spare1} OPTIONAL -- Need OP
-}
-
-CarrierFreqEUTRA ::= SEQUENCE {
- dl-CarrierFreq ARFCN-ValueEUTRA,
- ul-CarrierFreq ARFCN-ValueEUTRA OPTIONAL -- Cond FDD
-}
-
-
-MobilityParametersCDMA2000 ::= OCTET STRING
-
-
-MobilityStateParameters ::= SEQUENCE {
- t-Evaluation ENUMERATED {
- s30, s60, s120, s180, s240, spare3, spare2, spare1},
- t-HystNormal ENUMERATED {
- s30, s60, s120, s180, s240, spare3, spare2, spare1},
- n-CellChangeMedium INTEGER (1..16),
- n-CellChangeHigh INTEGER (1..16)
-}
-
-
-PhysCellId ::= INTEGER (0..503)
-
-
-PhysCellIdRange ::= SEQUENCE {
- start PhysCellId,
- range ENUMERATED {
- n4, n8, n12, n16, n24, n32, n48, n64, n84,
- n96, n128, n168, n252, n504, spare2,
- spare1} OPTIONAL -- Need OP
-}
-
-
-PhysCellIdCDMA2000 ::= INTEGER (0..maxPNOffset)
-
-
-PhysCellIdGERAN ::= SEQUENCE {
- networkColourCode BIT STRING (SIZE (3)),
- baseStationColourCode BIT STRING (SIZE (3))
-}
-
-
-PhysCellIdUTRA-FDD ::= INTEGER (0..511)
-
-
-PhysCellIdUTRA-TDD ::= INTEGER (0..127)
-
-
-PLMN-Identity ::= SEQUENCE {
- mcc MCC OPTIONAL, -- Cond MCC
- mnc MNC
-}
-
-MCC ::= SEQUENCE (SIZE (3)) OF
- MCC-MNC-Digit
-
-MNC ::= SEQUENCE (SIZE (2..3)) OF
- MCC-MNC-Digit
-
-MCC-MNC-Digit ::= INTEGER (0..9)
-
-
-
-PreRegistrationInfoHRPD ::= SEQUENCE {
- preRegistrationAllowed BOOLEAN,
- preRegistrationZoneId PreRegistrationZoneIdHRPD OPTIONAL, -- cond PreRegAllowed
- secondaryPreRegistrationZoneIdList SecondaryPreRegistrationZoneIdListHRPD OPTIONAL -- Need OR
-}
-
-SecondaryPreRegistrationZoneIdListHRPD ::= SEQUENCE (SIZE (1..2)) OF PreRegistrationZoneIdHRPD
-
-PreRegistrationZoneIdHRPD ::= INTEGER (0..255)
-
-
-Q-RxLevMin ::= INTEGER (-70..-22)
-
-
-Q-OffsetRange ::= ENUMERATED {
- dB-24, dB-22, dB-20, dB-18, dB-16, dB-14,
- dB-12, dB-10, dB-8, dB-6, dB-5, dB-4, dB-3,
- dB-2, dB-1, dB0, dB1, dB2, dB3, dB4, dB5,
- dB6, dB8, dB10, dB12, dB14, dB16, dB18,
- dB20, dB22, dB24}
-
-
-Q-OffsetRangeInterRAT ::= INTEGER (-15..15)
-
-
-ReselectionThreshold ::= INTEGER (0..31)
-
-
-SpeedStateScaleFactors ::= SEQUENCE {
- sf-Medium ENUMERATED {oDot25, oDot5, oDot75, lDot0},
- sf-High ENUMERATED {oDot25, oDot5, oDot75, lDot0}
-}
-
-SystemTimeInfoCDMA2000 ::= SEQUENCE {
- cdma-EUTRA-Synchronisation BOOLEAN,
- cdma-SystemTime CHOICE {
- synchronousSystemTime BIT STRING (SIZE (39)),
- asynchronousSystemTime BIT STRING (SIZE (49))
- }
-}
-
-
-TrackingAreaCode ::= BIT STRING (SIZE (16))
-
-
-T-Reselection ::= INTEGER (0..7)
-
-
-AllowedMeasBandwidth ::= ENUMERATED {mbw6, mbw15, mbw25, mbw50, mbw75, mbw100}
-
-
-Hysteresis ::= INTEGER (0..30)
-
-
-MeasConfig ::= SEQUENCE {
- -- Measurement objects
- measObjectToRemoveList MeasObjectToRemoveList OPTIONAL, -- Need ON
- measObjectToAddModList MeasObjectToAddModList OPTIONAL, -- Need ON
- -- Reporting configurations
- reportConfigToRemoveList ReportConfigToRemoveList OPTIONAL, -- Need ON
- reportConfigToAddModList ReportConfigToAddModList OPTIONAL, -- Need ON
- -- Measurement identities
- measIdToRemoveList MeasIdToRemoveList OPTIONAL, -- Need ON
- measIdToAddModList MeasIdToAddModList OPTIONAL, -- Need ON
- -- Other parameters
- quantityConfig QuantityConfig OPTIONAL, -- Need ON
- measGapConfig MeasGapConfig OPTIONAL, -- Need ON
- s-Measure RSRP-Range OPTIONAL, -- Need ON
- preRegistrationInfoHRPD PreRegistrationInfoHRPD OPTIONAL, -- Need OP
- speedStatePars CHOICE {
- release NULL,
- setup SEQUENCE {
- mobilityStateParameters MobilityStateParameters,
- timeToTrigger-SF SpeedStateScaleFactors
- }
- } OPTIONAL, -- Need ON
- ...
-}
-
-MeasIdToRemoveList ::= SEQUENCE (SIZE (1..maxMeasId)) OF MeasId
-
-MeasObjectToRemoveList ::= SEQUENCE (SIZE (1..maxObjectId)) OF MeasObjectId
-
-ReportConfigToRemoveList ::= SEQUENCE (SIZE (1..maxReportConfigId)) OF ReportConfigId
-
-
-MeasGapConfig ::= CHOICE {
- release NULL,
- setup SEQUENCE {
- gapOffset CHOICE {
- gp0 INTEGER (0..39),
- gp1 INTEGER (0..79),
- ...
- }
- }
-}
-
-
-MeasId ::= INTEGER (1..maxMeasId)
-
-
-MeasIdToAddModList ::= SEQUENCE (SIZE (1..maxMeasId)) OF MeasIdToAddMod
-
-MeasIdToAddMod ::= SEQUENCE {
- measId MeasId,
- measObjectId MeasObjectId,
- reportConfigId ReportConfigId
-}
-
-
-MeasObjectCDMA2000 ::= SEQUENCE {
- cdma2000-Type CDMA2000-Type,
- carrierFreq CarrierFreqCDMA2000,
- searchWindowSize INTEGER (0..15) OPTIONAL, -- Need ON
- offsetFreq Q-OffsetRangeInterRAT DEFAULT 0,
- cellsToRemoveList CellIndexList OPTIONAL, -- Need ON
- cellsToAddModList CellsToAddModListCDMA2000 OPTIONAL, -- Need ON
- cellForWhichToReportCGI PhysCellIdCDMA2000 OPTIONAL, -- Need ON
- ...
-}
-
-CellsToAddModListCDMA2000 ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddModCDMA2000
-
-CellsToAddModCDMA2000 ::= SEQUENCE {
- cellIndex INTEGER (1..maxCellMeas),
- physCellId PhysCellIdCDMA2000
-}
-
-
-MeasObjectEUTRA ::= SEQUENCE {
- carrierFreq ARFCN-ValueEUTRA,
- allowedMeasBandwidth AllowedMeasBandwidth,
- presenceAntennaPort1 PresenceAntennaPort1,
- neighCellConfig NeighCellConfig,
- offsetFreq Q-OffsetRange DEFAULT dB0,
- -- Neighbour cell list
- cellsToRemoveList CellIndexList OPTIONAL, -- Need ON
- cellsToAddModList CellsToAddModList OPTIONAL, -- Need ON
- -- Black list
- blackCellsToRemoveList CellIndexList OPTIONAL, -- Need ON
- blackCellsToAddModList BlackCellsToAddModList OPTIONAL, -- Need ON
- cellForWhichToReportCGI PhysCellId OPTIONAL, -- Need ON
- ...
-}
-
-CellsToAddModList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddMod
-
-CellsToAddMod ::= SEQUENCE {
- cellIndex INTEGER (1..maxCellMeas),
- physCellId PhysCellId,
- cellIndividualOffset Q-OffsetRange
-}
-
-BlackCellsToAddModList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF BlackCellsToAddMod
-
-BlackCellsToAddMod ::= SEQUENCE {
- cellIndex INTEGER (1..maxCellMeas),
- physCellIdRange PhysCellIdRange
-}
-
-
-MeasObjectGERAN ::= SEQUENCE {
- carrierFreqs CarrierFreqsGERAN,
- offsetFreq Q-OffsetRangeInterRAT DEFAULT 0,
- ncc-Permitted BIT STRING(SIZE (8)) DEFAULT '11111111'B,
- cellForWhichToReportCGI PhysCellIdGERAN OPTIONAL, -- Need ON
- ...
-}
-
-
-MeasObjectId ::= INTEGER (1..maxObjectId)
-
-
-MeasObjectToAddModList ::= SEQUENCE (SIZE (1..maxObjectId)) OF MeasObjectToAddMod
-
-MeasObjectToAddMod ::= SEQUENCE {
- measObjectId MeasObjectId,
- measObject CHOICE {
- measObjectEUTRA MeasObjectEUTRA,
- measObjectUTRA MeasObjectUTRA,
- measObjectGERAN MeasObjectGERAN,
- measObjectCDMA2000 MeasObjectCDMA2000,
- ...
- }
-}
-
-
-MeasObjectUTRA ::= SEQUENCE {
- carrierFreq ARFCN-ValueUTRA,
- offsetFreq Q-OffsetRangeInterRAT DEFAULT 0,
- cellsToRemoveList CellIndexList OPTIONAL, -- Need ON
- cellsToAddModList CHOICE {
- cellsToAddModListUTRA-FDD CellsToAddModListUTRA-FDD,
- cellsToAddModListUTRA-TDD CellsToAddModListUTRA-TDD
- } OPTIONAL, -- Need ON
- cellForWhichToReportCGI CHOICE {
- utra-FDD PhysCellIdUTRA-FDD,
- utra-TDD PhysCellIdUTRA-TDD
- } OPTIONAL, -- Need ON
- ...
-}
-
-CellsToAddModListUTRA-FDD ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddModUTRA-FDD
-
-CellsToAddModUTRA-FDD ::= SEQUENCE {
- cellIndex INTEGER (1..maxCellMeas),
- physCellId PhysCellIdUTRA-FDD
-}
-
-CellsToAddModListUTRA-TDD ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddModUTRA-TDD
-
-CellsToAddModUTRA-TDD ::= SEQUENCE {
- cellIndex INTEGER (1..maxCellMeas),
- physCellId PhysCellIdUTRA-TDD
-}
-
-
-MeasResults ::= SEQUENCE {
- measId MeasId,
- measResultServCell SEQUENCE {
- rsrpResult RSRP-Range,
- rsrqResult RSRQ-Range
- },
- measResultNeighCells CHOICE {
- measResultListEUTRA MeasResultListEUTRA,
- measResultListUTRA MeasResultListUTRA,
- measResultListGERAN MeasResultListGERAN,
- measResultsCDMA2000 MeasResultsCDMA2000,
- ...
- } OPTIONAL,
- ...
-}
-
-MeasResultListEUTRA ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultEUTRA
-
-MeasResultEUTRA ::= SEQUENCE {
- physCellId PhysCellId,
- cgi-Info SEQUENCE {
- cellGlobalId CellGlobalIdEUTRA,
- trackingAreaCode TrackingAreaCode,
- plmn-IdentityList PLMN-IdentityList2 OPTIONAL
- } OPTIONAL,
- measResult SEQUENCE {
- rsrpResult RSRP-Range OPTIONAL,
- rsrqResult RSRQ-Range OPTIONAL,
- ...
- }
-}
-
-MeasResultListUTRA ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultUTRA
-
-MeasResultUTRA ::= SEQUENCE {
- physCellId CHOICE {
- fdd PhysCellIdUTRA-FDD,
- tdd PhysCellIdUTRA-TDD
- },
- cgi-Info SEQUENCE {
- cellGlobalId CellGlobalIdUTRA,
- locationAreaCode BIT STRING (SIZE (16)) OPTIONAL,
- routingAreaCode BIT STRING (SIZE (8)) OPTIONAL,
- plmn-IdentityList PLMN-IdentityList2 OPTIONAL
- } OPTIONAL,
- measResult SEQUENCE {
- utra-RSCP INTEGER (-5..91) OPTIONAL,
- utra-EcN0 INTEGER (0..49) OPTIONAL,
- ...
- }
-}
-
-MeasResultListGERAN ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultGERAN
-
-MeasResultGERAN ::= SEQUENCE {
- carrierFreq CarrierFreqGERAN,
- physCellId PhysCellIdGERAN,
- cgi-Info SEQUENCE {
- cellGlobalId CellGlobalIdGERAN,
- routingAreaCode BIT STRING (SIZE (8)) OPTIONAL
- } OPTIONAL,
- measResult SEQUENCE {
- rssi INTEGER (0..63),
- ...
- }
-}
-
-MeasResultsCDMA2000 ::= SEQUENCE {
- preRegistrationStatusHRPD BOOLEAN,
- measResultListCDMA2000 MeasResultListCDMA2000
-}
-
-MeasResultListCDMA2000 ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultCDMA2000
-
-MeasResultCDMA2000 ::= SEQUENCE {
- physCellId PhysCellIdCDMA2000,
- cgi-Info CellGlobalIdCDMA2000 OPTIONAL,
- measResult SEQUENCE {
- pilotPnPhase INTEGER (0..32767) OPTIONAL,
- pilotStrength INTEGER (0..63),
- ...
- }
-}
-
-PLMN-IdentityList2 ::= SEQUENCE (SIZE (1..5)) OF PLMN-Identity
-
-
-QuantityConfig ::= SEQUENCE {
- quantityConfigEUTRA QuantityConfigEUTRA OPTIONAL, -- Need ON
- quantityConfigUTRA QuantityConfigUTRA OPTIONAL, -- Need ON
- quantityConfigGERAN QuantityConfigGERAN OPTIONAL, -- Need ON
- quantityConfigCDMA2000 QuantityConfigCDMA2000 OPTIONAL, -- Need ON
- ...
-}
-
-QuantityConfigEUTRA ::= SEQUENCE {
- filterCoefficientRSRP FilterCoefficient DEFAULT fc4,
- filterCoefficientRSRQ FilterCoefficient DEFAULT fc4
-}
-
-QuantityConfigUTRA ::= SEQUENCE {
- measQuantityUTRA-FDD ENUMERATED {cpich-RSCP, cpich-EcN0},
- measQuantityUTRA-TDD ENUMERATED {pccpch-RSCP},
- filterCoefficient FilterCoefficient DEFAULT fc4
-}
-
-QuantityConfigGERAN ::= SEQUENCE {
- measQuantityGERAN ENUMERATED {rssi},
- filterCoefficient FilterCoefficient DEFAULT fc2
-}
-
-QuantityConfigCDMA2000 ::= SEQUENCE {
- measQuantityCDMA2000 ENUMERATED {pilotStrength, pilotPnPhaseAndPilotStrength}
-}
-
-
-ReportConfigEUTRA ::= SEQUENCE {
- triggerType CHOICE {
- event SEQUENCE {
- eventId CHOICE {
- eventA1 SEQUENCE {
- a1-Threshold ThresholdEUTRA
- },
- eventA2 SEQUENCE {
- a2-Threshold ThresholdEUTRA
- },
- eventA3 SEQUENCE {
- a3-Offset INTEGER (-30..30),
- reportOnLeave BOOLEAN
- },
- eventA4 SEQUENCE {
- a4-Threshold ThresholdEUTRA
- },
- eventA5 SEQUENCE {
- a5-Threshold1 ThresholdEUTRA,
- a5-Threshold2 ThresholdEUTRA
- },
- ...
- },
- hysteresis Hysteresis,
- timeToTrigger TimeToTrigger
- },
- periodical SEQUENCE {
- purpose ENUMERATED {
- reportStrongestCells, reportCGI}
- }
- },
- triggerQuantity ENUMERATED {rsrp, rsrq},
- reportQuantity ENUMERATED {sameAsTriggerQuantity, both},
- maxReportCells INTEGER (1..maxCellReport),
- reportInterval ReportInterval,
- reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity},
- ...
-}
-
-ThresholdEUTRA ::= CHOICE{
- threshold-RSRP RSRP-Range,
- threshold-RSRQ RSRQ-Range
-}
-
-
-ReportConfigId ::= INTEGER (1..maxReportConfigId)
-
-
-ReportConfigInterRAT ::= SEQUENCE {
- triggerType CHOICE {
- event SEQUENCE {
- eventId CHOICE {
- eventB1 SEQUENCE {
- b1-Threshold CHOICE {
- b1-ThresholdUTRA ThresholdUTRA,
- b1-ThresholdGERAN ThresholdGERAN,
- b1-ThresholdCDMA2000 ThresholdCDMA2000
- }
- },
- eventB2 SEQUENCE {
- b2-Threshold1 ThresholdEUTRA,
- b2-Threshold2 CHOICE {
- b2-Threshold2UTRA ThresholdUTRA,
- b2-Threshold2GERAN ThresholdGERAN,
- b2-Threshold2CDMA2000 ThresholdCDMA2000
- }
- },
- ...
- },
- hysteresis Hysteresis,
- timeToTrigger TimeToTrigger
- },
- periodical SEQUENCE {
- purpose ENUMERATED {
- reportStrongestCells,
- reportStrongestCellsForSON,
- reportCGI}
- }
- },
- maxReportCells INTEGER (1..maxCellReport),
- reportInterval ReportInterval,
- reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity},
- ...
-}
-
-ThresholdUTRA ::= CHOICE{
- utra-RSCP INTEGER (-5..91),
- utra-EcN0 INTEGER (0..49)
-}
-
-ThresholdGERAN ::= INTEGER (0..63)
-
-ThresholdCDMA2000 ::= INTEGER (0..63)
-
-
-ReportConfigToAddModList ::= SEQUENCE (SIZE (1..maxReportConfigId)) OF ReportConfigToAddMod
-
-ReportConfigToAddMod ::= SEQUENCE {
- reportConfigId ReportConfigId,
- reportConfig CHOICE {
- reportConfigEUTRA ReportConfigEUTRA,
- reportConfigInterRAT ReportConfigInterRAT
- }
-}
-
-
-
-ReportInterval ::= ENUMERATED {
- ms120, ms240, ms480, ms640, ms1024, ms2048, ms5120, ms10240,
- min1, min6, min12, min30, min60, spare3, spare2, spare1}
-
-
-RSRP-Range ::= INTEGER(0..97)
-
-
-RSRQ-Range ::= INTEGER(0..34)
-
-
-TimeToTrigger ::= ENUMERATED {
- ms0, ms40, ms64, ms80, ms100, ms128, ms160, ms256,
- ms320, ms480, ms512, ms640, ms1024, ms1280, ms2560,
- ms5120}
-
-
-C-RNTI ::= BIT STRING (SIZE (16))
-
-
-DedicatedInfoCDMA2000 ::= OCTET STRING
-
-
-DedicatedInfoNAS ::= OCTET STRING
-
-
-FilterCoefficient ::= ENUMERATED {
- fc0, fc1, fc2, fc3, fc4, fc5,
- fc6, fc7, fc8, fc9, fc11, fc13,
- fc15, fc17, fc19, spare1, ...}
-
-
-MMEC ::= BIT STRING (SIZE (8))
-
-
-NeighCellConfig ::= BIT STRING (SIZE (2))
-
-
-RAND-CDMA2000 ::= BIT STRING (SIZE (32))
-
-
-RAT-Type ::= ENUMERATED {
- eutra, utra, geran-cs, geran-ps, cdma2000-1XRTT,
- spare3, spare2, spare1, ...}
-
-
-RRC-TransactionIdentifier ::= INTEGER (0..3)
-
-
-S-TMSI ::= SEQUENCE {
- mmec MMEC,
- m-TMSI BIT STRING (SIZE (32))
-}
-
-
-UE-CapabilityRAT-ContainerList ::=SEQUENCE (SIZE (0..maxRAT-Capabilities)) OF UE-CapabilityRAT-Container
-
-UE-CapabilityRAT-Container ::= SEQUENCE {
- rat-Type RAT-Type,
- ueCapabilityRAT-Container OCTET STRING
-}
-
-
-UE-EUTRA-Capability ::= SEQUENCE {
- accessStratumRelease AccessStratumRelease,
- ue-Category INTEGER (1..5),
- pdcp-Parameters PDCP-Parameters,
- phyLayerParameters PhyLayerParameters,
- rf-Parameters RF-Parameters,
- measParameters MeasParameters,
- featureGroupIndicators BIT STRING (SIZE (32)) OPTIONAL,
- interRAT-Parameters SEQUENCE {
- utraFDD IRAT-ParametersUTRA-FDD OPTIONAL,
- utraTDD128 IRAT-ParametersUTRA-TDD128 OPTIONAL,
- utraTDD384 IRAT-ParametersUTRA-TDD384 OPTIONAL,
- utraTDD768 IRAT-ParametersUTRA-TDD768 OPTIONAL,
- geran IRAT-ParametersGERAN OPTIONAL,
- cdma2000-HRPD IRAT-ParametersCDMA2000-HRPD OPTIONAL,
- cdma2000-1xRTT IRAT-ParametersCDMA2000-1XRTT OPTIONAL
- },
- nonCriticalExtension SEQUENCE {} OPTIONAL
-}
-
-AccessStratumRelease ::= ENUMERATED {
- rel8, spare7, spare6, spare5, spare4, spare3,
- spare2, spare1, ...}
-
-PDCP-Parameters ::= SEQUENCE {
- supportedROHC-Profiles SEQUENCE {
- profile0x0001 BOOLEAN,
- profile0x0002 BOOLEAN,
- profile0x0003 BOOLEAN,
- profile0x0004 BOOLEAN,
- profile0x0006 BOOLEAN,
- profile0x0101 BOOLEAN,
- profile0x0102 BOOLEAN,
- profile0x0103 BOOLEAN,
- profile0x0104 BOOLEAN
- },
- maxNumberROHC-ContextSessions ENUMERATED {
- cs2, cs4, cs8, cs12, cs16, cs24, cs32,
- cs48, cs64, cs128, cs256, cs512, cs1024,
- cs16384, spare2, spare1} DEFAULT cs16,
- ...
-}
-
-PhyLayerParameters ::= SEQUENCE {
- ue-TxAntennaSelectionSupported BOOLEAN,
- ue-SpecificRefSigsSupported BOOLEAN
-}
-
-RF-Parameters ::= SEQUENCE {
- supportedBandListEUTRA SupportedBandListEUTRA
-}
-
-SupportedBandListEUTRA ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandEUTRA
-
-SupportedBandEUTRA ::= SEQUENCE {
- bandEUTRA INTEGER (1..64),
- halfDuplex BOOLEAN
-}
-
-MeasParameters ::= SEQUENCE {
- bandListEUTRA BandListEUTRA
-}
-
-BandListEUTRA ::= SEQUENCE (SIZE (1..maxBands)) OF BandInfoEUTRA
-
-BandInfoEUTRA ::= SEQUENCE {
- interFreqBandList InterFreqBandList,
- interRAT-BandList InterRAT-BandList OPTIONAL
-}
-
-InterFreqBandList ::= SEQUENCE (SIZE (1..maxBands)) OF InterFreqBandInfo
-
-InterFreqBandInfo ::= SEQUENCE {
- interFreqNeedForGaps BOOLEAN
-}
-
-InterRAT-BandList ::= SEQUENCE (SIZE (1..maxBands)) OF InterRAT-BandInfo
-
-InterRAT-BandInfo ::= SEQUENCE {
- interRAT-NeedForGaps BOOLEAN
-}
-
-IRAT-ParametersUTRA-FDD ::= SEQUENCE {
- supportedBandListUTRA-FDD SupportedBandListUTRA-FDD
-}
-
-SupportedBandListUTRA-FDD ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-FDD
-
-SupportedBandUTRA-FDD ::= ENUMERATED {
- bandI, bandII, bandIII, bandIV, bandV, bandVI,
- bandVII, bandVIII, bandIX, bandX, bandXI,
- bandXII, bandXIII, bandXIV, bandXV, bandXVI, ...}
-
-IRAT-ParametersUTRA-TDD128 ::= SEQUENCE {
- supportedBandListUTRA-TDD128 SupportedBandListUTRA-TDD128
-}
-
-SupportedBandListUTRA-TDD128 ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-TDD128
-
-SupportedBandUTRA-TDD128 ::= ENUMERATED {
- a, b, c, d, e, f, g, h, i, j, k, l, m, n,
- o, p, ...}
-
-IRAT-ParametersUTRA-TDD384 ::= SEQUENCE {
- supportedBandListUTRA-TDD384 SupportedBandListUTRA-TDD384
-}
-
-SupportedBandListUTRA-TDD384 ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-TDD384
-
-SupportedBandUTRA-TDD384 ::= ENUMERATED {
- a, b, c, d, e, f, g, h, i, j, k, l, m, n,
- o, p, ...}
-
-IRAT-ParametersUTRA-TDD768 ::= SEQUENCE {
- supportedBandListUTRA-TDD768 SupportedBandListUTRA-TDD768
-}
-
-SupportedBandListUTRA-TDD768 ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-TDD768
-
-SupportedBandUTRA-TDD768 ::= ENUMERATED {
- a, b, c, d, e, f, g, h, i, j, k, l, m, n,
- o, p, ...}
-
-IRAT-ParametersGERAN ::= SEQUENCE {
- supportedBandListGERAN SupportedBandListGERAN,
- interRAT-PS-HO-ToGERAN BOOLEAN
-}
-
-SupportedBandListGERAN ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandGERAN
-
-SupportedBandGERAN ::= ENUMERATED {
- gsm450, gsm480, gsm710, gsm750, gsm810, gsm850,
- gsm900P, gsm900E, gsm900R, gsm1800, gsm1900,
- spare5, spare4, spare3, spare2, spare1, ...}
-
-IRAT-ParametersCDMA2000-HRPD ::= SEQUENCE {
- supportedBandListHRPD SupportedBandListHRPD,
- tx-ConfigHRPD ENUMERATED {single, dual},
- rx-ConfigHRPD ENUMERATED {single, dual}
-}
-
-SupportedBandListHRPD ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandclassCDMA2000
-
-IRAT-ParametersCDMA2000-1XRTT ::= SEQUENCE {
- supportedBandList1XRTT SupportedBandList1XRTT,
- tx-Config1XRTT ENUMERATED {single, dual},
- rx-Config1XRTT ENUMERATED {single, dual}
-}
-
-SupportedBandList1XRTT ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandclassCDMA2000
-
-
-UE-TimersAndConstants ::= SEQUENCE {
- t300 ENUMERATED {
- ms100, ms200, ms300, ms400, ms600, ms1000, ms1500,
- ms2000},
- t301 ENUMERATED {
- ms100, ms200, ms300, ms400, ms600, ms1000, ms1500,
- ms2000},
- t310 ENUMERATED {
- ms0, ms50, ms100, ms200, ms500, ms1000, ms2000},
- n310 ENUMERATED {
- n1, n2, n3, n4, n6, n8, n10, n20},
- t311 ENUMERATED {
- ms1000, ms3000, ms5000, ms10000, ms15000,
- ms20000, ms30000},
- n311 ENUMERATED {
- n1, n2, n3, n4, n5, n6, n8, n10},
- ...
-}
-
-
-maxBands INTEGER ::= 64 -- Maximum number of bands listed in EUTRA UE caps
-maxCDMA-BandClass INTEGER ::= 32 -- Maximum value of the CDMA band classes
-maxCellBlack INTEGER ::= 16 -- Maximum number of blacklisted cells
- -- listed in SIB type 4 and 5
-maxCellInter INTEGER ::= 16 -- Maximum number of neighbouring inter-frequency
- -- cells listed in SIB type 5
-maxCellIntra INTEGER ::= 16 -- Maximum number of neighbouring intra-frequency
- -- cells listed in SIB type 4
-maxCellMeas INTEGER ::= 32 -- Maximum number of entries in each of the neighbour
- -- cell lists in a measurement object
-maxCellReport INTEGER ::= 8 -- Maximum number of reported cells
-maxDRB INTEGER ::= 11 -- Maximum number of Data Radio Bearers
-maxEARFCN INTEGER ::= 65535 -- Maximum value of EUTRA carrier fequency
-maxFreq INTEGER ::= 8 -- Maximum number of EUTRA carrier frequencies
-maxGERAN-SI INTEGER ::= 10 -- Maximum number of GERAN SI blocks that can be
- -- provided as part of NACC information
-maxGNFG INTEGER ::= 16 -- Maximum number of GERAN neighbour freq groups
-maxMBSFN-Allocations INTEGER ::= 8 -- Maximum number of MBSFN frame allocations with
- -- different offset
-maxMCS-1 INTEGER ::= 16 -- Maximum number of PUCCH formats (MCS)
-maxMeasId INTEGER ::= 32
-maxObjectId INTEGER ::= 32
-maxPageRec INTEGER ::= 16 --
-maxPNOffset INTEGER ::= 511 -- Maximum number of CDMA2000 PNOffsets
-maxRAT-Capabilities INTEGER ::= 8 -- Maximum number of interworking RATs (incl EUTRA)
-maxReportConfigId INTEGER ::= 32
-maxSIB INTEGER ::= 32 -- Maximum number of SIBs
-maxSIB-1 INTEGER ::= 31
-maxSI-Message INTEGER ::= 32 -- Maximum number of SI messages
-maxUTRA-FDD-Carrier INTEGER ::= 16 -- Maximum number of UTRA FDD carrier frequencies
-maxUTRA-TDD-Carrier INTEGER ::= 16 -- Maximum number of UTRA TDD carrier frequencies
-
-
-END
+-- 3GPP TS 36.331 v9.2.0
+-- http://cdmweb.ericsson.se/TeamCenter/controller/ViewDocs?DocumentName=51%2F15519-10%2FFCP1039669%2F11&Revision=A
+
+EUTRA-RRC-Definitions DEFINITIONS AUTOMATIC TAGS ::=
+
+BEGIN
+
+
+BCCH-BCH-Message ::= SEQUENCE {
+ message BCCH-BCH-MessageType
+}
+
+BCCH-BCH-MessageType ::= MasterInformationBlock
+
+
+BCCH-DL-SCH-Message ::= SEQUENCE {
+ message BCCH-DL-SCH-MessageType
+}
+
+BCCH-DL-SCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ systemInformation SystemInformation,
+ systemInformationBlockType1 SystemInformationBlockType1
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+MCCH-Message ::= SEQUENCE {
+ message MCCH-MessageType
+}
+
+MCCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ mbsfnAreaConfiguration-r9 MBSFNAreaConfiguration-r9
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+PCCH-Message ::= SEQUENCE {
+ message PCCH-MessageType
+}
+
+PCCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ paging Paging
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+DL-CCCH-Message ::= SEQUENCE {
+ message DL-CCCH-MessageType
+}
+
+DL-CCCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ rrcConnectionReestablishment RRCConnectionReestablishment,
+ rrcConnectionReestablishmentReject RRCConnectionReestablishmentReject,
+ rrcConnectionReject RRCConnectionReject,
+ rrcConnectionSetup RRCConnectionSetup
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+DL-DCCH-Message ::= SEQUENCE {
+ message DL-DCCH-MessageType
+}
+
+DL-DCCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ csfbParametersResponseCDMA2000 CSFBParametersResponseCDMA2000,
+ dlInformationTransfer DLInformationTransfer,
+ handoverFromEUTRAPreparationRequest HandoverFromEUTRAPreparationRequest,
+ mobilityFromEUTRACommand MobilityFromEUTRACommand,
+ rrcConnectionReconfiguration RRCConnectionReconfiguration,
+ rrcConnectionRelease RRCConnectionRelease,
+ securityModeCommand SecurityModeCommand,
+ ueCapabilityEnquiry UECapabilityEnquiry,
+ counterCheck CounterCheck,
+ ueInformationRequest-r9 UEInformationRequest-r9,
+ spare6 NULL, spare5 NULL, spare4 NULL,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+UL-CCCH-Message ::= SEQUENCE {
+ message UL-CCCH-MessageType
+}
+
+UL-CCCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ rrcConnectionReestablishmentRequest RRCConnectionReestablishmentRequest,
+ rrcConnectionRequest RRCConnectionRequest
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+UL-DCCH-Message ::= SEQUENCE {
+ message UL-DCCH-MessageType
+}
+
+UL-DCCH-MessageType ::= CHOICE {
+ c1 CHOICE {
+ csfbParametersRequestCDMA2000 CSFBParametersRequestCDMA2000,
+ measurementReport MeasurementReport,
+ rrcConnectionReconfigurationComplete RRCConnectionReconfigurationComplete,
+ rrcConnectionReestablishmentComplete RRCConnectionReestablishmentComplete,
+ rrcConnectionSetupComplete RRCConnectionSetupComplete,
+ securityModeComplete SecurityModeComplete,
+ securityModeFailure SecurityModeFailure,
+ ueCapabilityInformation UECapabilityInformation,
+ulHandoverPreparationTransfer ULHandoverPreparationTransfer,
+ ulInformationTransfer ULInformationTransfer,
+ counterCheckResponse CounterCheckResponse,
+ ueInformationResponse-r9 UEInformationResponse-r9,
+ proximityIndication-r9 ProximityIndication-r9,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ messageClassExtension SEQUENCE {}
+}
+
+
+CounterCheck ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ counterCheck-r8 CounterCheck-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+CounterCheck-r8-IEs ::= SEQUENCE {
+ drb-CountMSB-InfoList DRB-CountMSB-InfoList,
+ nonCriticalExtension SEQUENCE {} OPTIONAL --Need OP
+}
+
+DRB-CountMSB-InfoList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-CountMSB-Info
+
+DRB-CountMSB-Info ::= SEQUENCE {
+ drb-Identity DRB-Identity,
+ countMSB-Uplink INTEGER(0..33554431),
+ countMSB-Downlink INTEGER(0..33554431)
+}
+
+
+CounterCheckResponse ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ counterCheckResponse-r8 CounterCheckResponse-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+CounterCheckResponse-r8-IEs ::= SEQUENCE {
+ drb-CountInfoList DRB-CountInfoList,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+DRB-CountInfoList ::= SEQUENCE (SIZE (0..maxDRB)) OF DRB-CountInfo
+
+DRB-CountInfo ::= SEQUENCE {
+ drb-Identity DRB-Identity,
+ count-Uplink INTEGER(0..4294967295),
+ count-Downlink INTEGER(0..4294967295)
+}
+
+
+CSFBParametersRequestCDMA2000 ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ csfbParametersRequestCDMA2000-r8 CSFBParametersRequestCDMA2000-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+CSFBParametersRequestCDMA2000-r8-IEs ::= SEQUENCE {
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+CSFBParametersResponseCDMA2000 ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ csfbParametersResponseCDMA2000-r8 CSFBParametersResponseCDMA2000-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+CSFBParametersResponseCDMA2000-r8-IEs ::= SEQUENCE {
+ rand RAND-CDMA2000,
+ mobilityParameters MobilityParametersCDMA2000,
+ nonCriticalExtension SEQUENCE {} OPTIONAL --Need OP
+}
+
+
+DLInformationTransfer ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ dlInformationTransfer-r8 DLInformationTransfer-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+DLInformationTransfer-r8-IEs ::= SEQUENCE {
+ dedicatedInfoType CHOICE {
+ dedicatedInfoNAS DedicatedInfoNAS,
+ dedicatedInfoCDMA2000-1XRTT DedicatedInfoCDMA2000,
+ dedicatedInfoCDMA2000-HRPD DedicatedInfoCDMA2000
+ },
+ nonCriticalExtension SEQUENCE {} OPTIONAL --Need OP
+}
+
+
+HandoverFromEUTRAPreparationRequest ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ handoverFromEUTRAPreparationRequest-r8
+ HandoverFromEUTRAPreparationRequest-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+HandoverFromEUTRAPreparationRequest-r8-IEs ::= SEQUENCE {
+ cdma2000-Type CDMA2000-Type,
+ rand RAND-CDMA2000 OPTIONAL, -- Cond cdma2000-Type
+ mobilityParameters MobilityParametersCDMA2000 OPTIONAL, -- Cond cdma2000-Type
+ nonCriticalExtension HandoverFromEUTRAPreparationRequest-v890-IEs OPTIONAL
+}
+
+HandoverFromEUTRAPreparationRequest-v890-IEs ::= SEQUENCE {
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ nonCriticalExtension HandoverFromEUTRAPreparationRequest-v920-IEs OPTIONAL
+}
+
+HandoverFromEUTRAPreparationRequest-v920-IEs ::= SEQUENCE {
+ concurrPrepCDMA2000-HRPD-r9 BOOLEAN OPTIONAL, -- Cond PSHO
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+MasterInformationBlock ::= SEQUENCE {
+ dl-Bandwidth ENUMERATED {
+ n6, n15, n25, n50, n75, n100},
+ phich-Config PHICH-Config,
+ systemFrameNumber BIT STRING (SIZE (8)),
+ spare BIT STRING (SIZE (10))
+}
+
+
+
+MBSFNAreaConfiguration-r9 ::= SEQUENCE {
+ commonSF-Alloc-r9 CommonSF-AllocPatternList-r9,
+ commonSF-AllocPeriod-r9 ENUMERATED {
+ rf4, rf8, rf16, rf32, rf64, rf128, rf256},
+ pmch-InfoList-r9 PMCH-InfoList-r9,
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+CommonSF-AllocPatternList-r9 ::= SEQUENCE (SIZE (1..maxMBSFN-Allocations)) OF MBSFN-SubframeConfig
+
+
+MeasurementReport ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ measurementReport-r8 MeasurementReport-r8-IEs,
+ spare7 NULL,
+ spare6 NULL, spare5 NULL, spare4 NULL,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+MeasurementReport-r8-IEs ::= SEQUENCE {
+ measResults MeasResults,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+MobilityFromEUTRACommand ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ mobilityFromEUTRACommand-r8 MobilityFromEUTRACommand-r8-IEs,
+ mobilityFromEUTRACommand-r9 MobilityFromEUTRACommand-r9-IEs,
+ spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+MobilityFromEUTRACommand-r8-IEs ::= SEQUENCE {
+ cs-FallbackIndicator BOOLEAN,
+ purpose CHOICE{
+ handover Handover,
+ cellChangeOrder CellChangeOrder
+ },
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+MobilityFromEUTRACommand-r9-IEs ::= SEQUENCE {
+ cs-FallbackIndicator BOOLEAN,
+ purpose CHOICE{
+ handover Handover,
+ cellChangeOrder CellChangeOrder,
+ e-CSFB-r9 E-CSFB-r9,
+ ...
+ },
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+Handover ::= SEQUENCE {
+ targetRAT-Type ENUMERATED {
+ utra, geran, cdma2000-1XRTT, cdma2000-HRPD,
+ spare4, spare3, spare2, spare1, ...},
+ targetRAT-MessageContainer OCTET STRING,
+ nas-SecurityParamFromEUTRA OCTET STRING (SIZE (1)) OPTIONAL, -- Cond UTRAGERAN
+ systemInformation SI-OrPSI-GERAN OPTIONAL -- Cond PSHO
+}
+
+CellChangeOrder ::= SEQUENCE {
+ t304 ENUMERATED {
+ ms100, ms200, ms500, ms1000,
+ ms2000, ms4000, ms8000, spare1},
+ targetRAT-Type CHOICE {
+ geran SEQUENCE {
+ physCellId PhysCellIdGERAN,
+ carrierFreq CarrierFreqGERAN,
+ networkControlOrder BIT STRING (SIZE (2)) OPTIONAL, -- Need OP
+ systemInformation SI-OrPSI-GERAN OPTIONAL -- Need OP
+ },
+ ...
+ }
+}
+
+SI-OrPSI-GERAN ::= CHOICE {
+ si SystemInfoListGERAN,
+ psi SystemInfoListGERAN
+}
+
+E-CSFB-r9 ::= SEQUENCE {
+ messageContCDMA2000-1XRTT-r9 OCTET STRING OPTIONAL, -- Need ON
+ mobilityCDMA2000-HRPD-r9 ENUMERATED {
+ handover, redirection
+ } OPTIONAL, -- Need OP
+ messageContCDMA2000-HRPD-r9 OCTET STRING OPTIONAL, -- Cond concHO
+ redirectCarrierCDMA2000-HRPD-r9 CarrierFreqCDMA2000 OPTIONAL -- Cond concRedir
+}
+
+
+Paging ::= SEQUENCE {
+ pagingRecordList PagingRecordList OPTIONAL, -- Need ON
+ systemInfoModification ENUMERATED {true} OPTIONAL, -- Need ON
+ etws-Indication ENUMERATED {true} OPTIONAL, -- Need ON
+ nonCriticalExtension Paging-v890-IEs OPTIONAL
+}
+
+Paging-v890-IEs ::= SEQUENCE {
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ nonCriticalExtension Paging-v920-IEs OPTIONAL
+}
+
+Paging-v920-IEs ::= SEQUENCE {
+ cmas-Indication-r9 ENUMERATED {true} OPTIONAL, -- Need ON
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+PagingRecordList ::= SEQUENCE (SIZE (1..maxPageRec)) OF PagingRecord
+
+PagingRecord ::= SEQUENCE {
+ ue-Identity PagingUE-Identity,
+ cn-Domain ENUMERATED {ps, cs},
+ ...
+}
+
+PagingUE-Identity ::= CHOICE {
+ s-TMSI S-TMSI,
+ imsi IMSI,
+ ...
+}
+
+IMSI ::= SEQUENCE (SIZE (6..21)) OF IMSI-Digit
+
+IMSI-Digit ::= INTEGER (0..9)
+
+
+ProximityIndication-r9 ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ proximityIndication-r9 ProximityIndication-r9-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+ProximityIndication-r9-IEs ::= SEQUENCE {
+ type-r9 ENUMERATED {entering, leaving},
+ carrierFreq-r9 CHOICE {
+ eutra-r9 ARFCN-ValueEUTRA,
+ utra-r9 ARFCN-ValueUTRA,
+ ...
+ },
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+RRCConnectionReconfiguration ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ rrcConnectionReconfiguration-r8 RRCConnectionReconfiguration-r8-IEs,
+ spare7 NULL,
+ spare6 NULL, spare5 NULL, spare4 NULL,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReconfiguration-r8-IEs ::= SEQUENCE {
+ measConfig MeasConfig OPTIONAL, -- Need ON
+ mobilityControlInfo MobilityControlInfo OPTIONAL, -- Cond HO
+ dedicatedInfoNASList SEQUENCE (SIZE(1..maxDRB)) OF
+ DedicatedInfoNAS OPTIONAL, -- Cond nonHO
+ radioResourceConfigDedicated RadioResourceConfigDedicated OPTIONAL, -- Cond HO-toEUTRA
+ securityConfigHO SecurityConfigHO OPTIONAL, -- Cond HO
+ nonCriticalExtension RRCConnectionReconfiguration-v890-IEs OPTIONAL
+}
+
+RRCConnectionReconfiguration-v890-IEs ::= SEQUENCE {
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ nonCriticalExtension RRCConnectionReconfiguration-v920-IEs OPTIONAL
+}
+
+RRCConnectionReconfiguration-v920-IEs ::= SEQUENCE {
+ otherConfig-r9 OtherConfig-r9 OPTIONAL, -- Need ON
+ fullConfig-r9 ENUMERATED {true} OPTIONAL, -- Cond HO-Reestab
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+SecurityConfigHO ::= SEQUENCE {
+ handoverType CHOICE {
+ intraLTE SEQUENCE {
+ securityAlgorithmConfig SecurityAlgorithmConfig OPTIONAL, -- Cond fullConfig
+ keyChangeIndicator BOOLEAN,
+ nextHopChainingCount NextHopChainingCount
+ },
+ interRAT SEQUENCE {
+ securityAlgorithmConfig SecurityAlgorithmConfig,
+ nas-SecurityParamToEUTRA OCTET STRING (SIZE(6))
+ }
+ },
+ ...
+}
+
+
+
+RRCConnectionReconfigurationComplete ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ rrcConnectionReconfigurationComplete-r8
+ RRCConnectionReconfigurationComplete-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReconfigurationComplete-r8-IEs ::= SEQUENCE {
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+RRCConnectionReestablishment ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ rrcConnectionReestablishment-r8 RRCConnectionReestablishment-r8-IEs,
+ spare7 NULL,
+ spare6 NULL, spare5 NULL, spare4 NULL,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReestablishment-r8-IEs ::= SEQUENCE {
+ radioResourceConfigDedicated RadioResourceConfigDedicated,
+ nextHopChainingCount NextHopChainingCount,
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+RRCConnectionReestablishmentComplete ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ rrcConnectionReestablishmentComplete-r8
+ RRCConnectionReestablishmentComplete-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReestablishmentComplete-r8-IEs ::= SEQUENCE {
+ nonCriticalExtension RRCConnectionReestablishmentComplete-v920-IEs OPTIONAL
+}
+
+RRCConnectionReestablishmentComplete-v920-IEs ::= SEQUENCE {
+ rlf-InfoAvailable-r9 ENUMERATED {true} OPTIONAL,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+RRCConnectionReestablishmentReject ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ rrcConnectionReestablishmentReject-r8
+ RRCConnectionReestablishmentReject-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReestablishmentReject-r8-IEs ::= SEQUENCE {
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+RRCConnectionReestablishmentRequest ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ rrcConnectionReestablishmentRequest-r8
+ RRCConnectionReestablishmentRequest-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReestablishmentRequest-r8-IEs ::= SEQUENCE {
+ ue-Identity ReestabUE-Identity,
+ reestablishmentCause ReestablishmentCause,
+ spare BIT STRING (SIZE (2))
+}
+
+ReestabUE-Identity ::= SEQUENCE {
+ c-RNTI C-RNTI,
+ physCellId PhysCellId,
+ shortMAC-I ShortMAC-I
+}
+
+ReestablishmentCause ::= ENUMERATED {
+ reconfigurationFailure, handoverFailure,
+ otherFailure, spare1}
+
+
+RRCConnectionReject ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ rrcConnectionReject-r8 RRCConnectionReject-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionReject-r8-IEs ::= SEQUENCE {
+ waitTime INTEGER (1..16),
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+RRCConnectionRelease ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ rrcConnectionRelease-r8 RRCConnectionRelease-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionRelease-r8-IEs ::= SEQUENCE {
+ releaseCause ReleaseCause,
+ redirectedCarrierInfo RedirectedCarrierInfo OPTIONAL, -- Need ON
+ idleModeMobilityControlInfo IdleModeMobilityControlInfo OPTIONAL, -- Need OP
+ nonCriticalExtension RRCConnectionRelease-v890-IEs OPTIONAL
+}
+
+RRCConnectionRelease-v890-IEs ::= SEQUENCE {
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ nonCriticalExtension RRCConnectionRelease-v920-IEs OPTIONAL
+}
+
+RRCConnectionRelease-v920-IEs ::= SEQUENCE {
+ cellInfoList-r9 CHOICE {
+ geran-r9 CellInfoListGERAN-r9,
+ utra-FDD-r9 CellInfoListUTRA-FDD-r9,
+ utra-TDD-r9 CellInfoListUTRA-TDD-r9,
+ ...
+ } OPTIONAL, -- Cond Redirection
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+ReleaseCause ::= ENUMERATED {loadBalancingTAUrequired,
+ other,spare2,spare1}
+
+RedirectedCarrierInfo ::= CHOICE {
+ eutra ARFCN-ValueEUTRA,
+ geran CarrierFreqsGERAN,
+ utra-FDD ARFCN-ValueUTRA,
+ utra-TDD ARFCN-ValueUTRA,
+ cdma2000-HRPD CarrierFreqCDMA2000,
+ cdma2000-1xRTT CarrierFreqCDMA2000,
+ ...
+}
+
+IdleModeMobilityControlInfo ::= SEQUENCE {
+ freqPriorityListEUTRA FreqPriorityListEUTRA OPTIONAL, -- Need ON
+ freqPriorityListGERAN FreqsPriorityListGERAN OPTIONAL, -- Need ON
+ freqPriorityListUTRA-FDD FreqPriorityListUTRA-FDD OPTIONAL, -- Need ON
+ freqPriorityListUTRA-TDD FreqPriorityListUTRA-TDD OPTIONAL, -- Need ON
+ bandClassPriorityListHRPD BandClassPriorityListHRPD OPTIONAL, -- Need ON
+ bandClassPriorityList1XRTT BandClassPriorityList1XRTT OPTIONAL, -- Need ON
+ t320 ENUMERATED {
+ min5, min10, min20, min30, min60, min120, min180,
+ spare1} OPTIONAL, -- Need OR
+ ...
+}
+
+FreqPriorityListEUTRA ::= SEQUENCE (SIZE (1..maxFreq)) OF FreqPriorityEUTRA
+
+FreqPriorityEUTRA ::= SEQUENCE {
+ carrierFreq ARFCN-ValueEUTRA,
+ cellReselectionPriority CellReselectionPriority
+}
+
+FreqsPriorityListGERAN ::= SEQUENCE (SIZE (1..maxGNFG)) OF FreqsPriorityGERAN
+
+FreqsPriorityGERAN ::= SEQUENCE {
+ carrierFreqs CarrierFreqsGERAN,
+ cellReselectionPriority CellReselectionPriority
+}
+
+FreqPriorityListUTRA-FDD ::= SEQUENCE (SIZE (1..maxUTRA-FDD-Carrier)) OF FreqPriorityUTRA-FDD
+
+FreqPriorityUTRA-FDD ::= SEQUENCE {
+ carrierFreq ARFCN-ValueUTRA,
+ cellReselectionPriority CellReselectionPriority
+}
+
+FreqPriorityListUTRA-TDD ::= SEQUENCE (SIZE (1..maxUTRA-TDD-Carrier)) OF FreqPriorityUTRA-TDD
+
+FreqPriorityUTRA-TDD ::= SEQUENCE {
+ carrierFreq ARFCN-ValueUTRA,
+ cellReselectionPriority CellReselectionPriority
+}
+
+BandClassPriorityListHRPD ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandClassPriorityHRPD
+
+BandClassPriorityHRPD ::= SEQUENCE {
+ bandClass BandclassCDMA2000,
+ cellReselectionPriority CellReselectionPriority
+}
+
+BandClassPriorityList1XRTT ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandClassPriority1XRTT
+
+BandClassPriority1XRTT ::= SEQUENCE {
+ bandClass BandclassCDMA2000,
+ cellReselectionPriority CellReselectionPriority
+}
+
+CellInfoListGERAN-r9 ::= SEQUENCE (SIZE (1..maxCellInfo-GERAN-r9 )) OF CellInfoGERAN-r9
+
+CellInfoGERAN-r9 ::= SEQUENCE {
+ physCellId-r9 PhysCellIdGERAN,
+ carrierFreq-r9 CarrierFreqGERAN,
+ systemInformation-r9 SystemInfoListGERAN
+}
+
+CellInfoListUTRA-FDD-r9 ::= SEQUENCE (SIZE (1..maxUTRA-CellInfo-r9)) OF CellInfoUTRA-FDD-r9
+
+CellInfoUTRA-FDD-r9 ::= SEQUENCE {
+ physCellId-r9 PhysCellIdUTRA-FDD,
+ utra-BCCH-Container-r9 OCTET STRING
+}
+
+CellInfoListUTRA-TDD-r9 ::= SEQUENCE (SIZE (1..maxUTRA-CellInfo-r9)) OF CellInfoUTRA-TDD-r9
+
+CellInfoUTRA-TDD-r9 ::= SEQUENCE {
+ physCellId-r9 PhysCellIdUTRA-TDD,
+ utra-BCCH-Container-r9 OCTET STRING
+}
+
+
+RRCConnectionRequest ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ rrcConnectionRequest-r8 RRCConnectionRequest-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionRequest-r8-IEs ::= SEQUENCE {
+ ue-Identity InitialUE-Identity,
+ establishmentCause EstablishmentCause,
+ spare BIT STRING (SIZE (1))
+}
+
+InitialUE-Identity ::= CHOICE {
+ s-TMSI S-TMSI,
+ randomValue BIT STRING (SIZE (40))
+}
+
+EstablishmentCause ::= ENUMERATED {
+ emergency, highPriorityAccess, mt-Access, mo-Signalling,
+ mo-Data, spare3, spare2, spare1}
+
+
+RRCConnectionSetup ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ rrcConnectionSetup-r8 RRCConnectionSetup-r8-IEs,
+ spare7 NULL,
+ spare6 NULL, spare5 NULL, spare4 NULL,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionSetup-r8-IEs ::= SEQUENCE {
+ radioResourceConfigDedicated RadioResourceConfigDedicated,
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+RRCConnectionSetupComplete ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ rrcConnectionSetupComplete-r8 RRCConnectionSetupComplete-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+RRCConnectionSetupComplete-r8-IEs ::= SEQUENCE {
+ selectedPLMN-Identity INTEGER (1..6),
+ registeredMME RegisteredMME OPTIONAL,
+ dedicatedInfoNAS DedicatedInfoNAS,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+RegisteredMME ::= SEQUENCE {
+ plmn-Identity PLMN-Identity OPTIONAL,
+ mmegi BIT STRING (SIZE (16)),
+ mmec MMEC
+}
+
+
+SecurityModeCommand ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ securityModeCommand-r8 SecurityModeCommand-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+SecurityModeCommand-r8-IEs ::= SEQUENCE {
+ securityConfigSMC SecurityConfigSMC,
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+SecurityConfigSMC ::= SEQUENCE {
+ securityAlgorithmConfig SecurityAlgorithmConfig,
+ ...
+}
+
+
+SecurityModeComplete ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ securityModeComplete-r8 SecurityModeComplete-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+SecurityModeComplete-r8-IEs ::= SEQUENCE {
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+SecurityModeFailure ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ securityModeFailure-r8 SecurityModeFailure-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+SecurityModeFailure-r8-IEs ::= SEQUENCE {
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+SystemInformation ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ systemInformation-r8 SystemInformation-r8-IEs,
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+SystemInformation-r8-IEs ::= SEQUENCE {
+ sib-TypeAndInfo SEQUENCE (SIZE (1..maxSIB)) OF CHOICE {
+ sib2 SystemInformationBlockType2,
+ sib3 SystemInformationBlockType3,
+ sib4 SystemInformationBlockType4,
+ sib5 SystemInformationBlockType5,
+ sib6 SystemInformationBlockType6,
+ sib7 SystemInformationBlockType7,
+ sib8 SystemInformationBlockType8,
+ sib9 SystemInformationBlockType9,
+ sib10 SystemInformationBlockType10,
+ sib11 SystemInformationBlockType11,
+ ...,
+ sib12-v920 SystemInformationBlockType12-r9,
+ sib13-v920 SystemInformationBlockType13-r9
+ },
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+SystemInformationBlockType1 ::= SEQUENCE {
+ cellAccessRelatedInfo SEQUENCE {
+ plmn-IdentityList PLMN-IdentityList,
+ trackingAreaCode TrackingAreaCode,
+ cellIdentity CellIdentity,
+ cellBarred ENUMERATED {barred, notBarred},
+ intraFreqReselection ENUMERATED {allowed, notAllowed},
+ csg-Indication BOOLEAN,
+ csg-Identity CSG-Identity OPTIONAL -- Need OR
+ },
+ cellSelectionInfo SEQUENCE {
+ q-RxLevMin Q-RxLevMin,
+ q-RxLevMinOffset INTEGER (1..8) OPTIONAL -- Need OP
+ },
+ p-Max P-Max OPTIONAL, -- Need OP
+ freqBandIndicator INTEGER (1..64),
+ schedulingInfoList SchedulingInfoList,
+ tdd-Config TDD-Config OPTIONAL, -- Cond TDD
+ si-WindowLength ENUMERATED {
+ ms1, ms2, ms5, ms10, ms15, ms20,
+ ms40},
+ systemInfoValueTag INTEGER (0..31),
+ nonCriticalExtension SystemInformationBlockType1-v890-IEs OPTIONAL
+}
+
+SystemInformationBlockType1-v890-IEs::= SEQUENCE {
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ nonCriticalExtension SystemInformationBlockType1-v920-IEs OPTIONAL
+}
+
+SystemInformationBlockType1-v920-IEs ::= SEQUENCE {
+ ims-EmergencySupport-r9 ENUMERATED {true} OPTIONAL, -- Need OR
+ cellSelectionInfo-v920 CellSelectionInfo-v920 OPTIONAL, -- Need OP
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+PLMN-IdentityList ::= SEQUENCE (SIZE (1..6)) OF PLMN-IdentityInfo
+
+PLMN-IdentityInfo ::= SEQUENCE {
+ plmn-Identity PLMN-Identity,
+ cellReservedForOperatorUse ENUMERATED {reserved, notReserved}
+}
+
+SchedulingInfoList ::= SEQUENCE (SIZE (1..maxSI-Message)) OF SchedulingInfo
+
+SchedulingInfo ::= SEQUENCE {
+ si-Periodicity ENUMERATED {
+ rf8, rf16, rf32, rf64, rf128, rf256, rf512},
+ sib-MappingInfo SIB-MappingInfo
+}
+
+SIB-MappingInfo ::= SEQUENCE (SIZE (0..maxSIB-1)) OF SIB-Type
+
+SIB-Type ::= ENUMERATED {
+ sibType3, sibType4, sibType5, sibType6,
+ sibType7, sibType8, sibType9, sibType10,
+ sibType11, sibType12-v920, sibType13-v920, spare5,
+ spare4, spare3, spare2, spare1, ...}
+
+CellSelectionInfo-v920 ::= SEQUENCE {
+ q-QualMin-r9 Q-QualMin-r9,
+ q-QualMinOffset-r9 INTEGER (1..8) OPTIONAL -- Need OP
+}
+
+
+UECapabilityEnquiry ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ ueCapabilityEnquiry-r8 UECapabilityEnquiry-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+UECapabilityEnquiry-r8-IEs ::= SEQUENCE {
+ ue-CapabilityRequest UE-CapabilityRequest,
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+UE-CapabilityRequest ::= SEQUENCE (SIZE (1..maxRAT-Capabilities)) OF RAT-Type
+
+
+UECapabilityInformation ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE{
+ ueCapabilityInformation-r8 UECapabilityInformation-r8-IEs,
+ spare7 NULL,
+ spare6 NULL, spare5 NULL, spare4 NULL,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+UECapabilityInformation-r8-IEs ::= SEQUENCE {
+ ue-CapabilityRAT-ContainerList UE-CapabilityRAT-ContainerList,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+UEInformationRequest-r9 ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ ueInformationRequest-r9 UEInformationRequest-r9-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+UEInformationRequest-r9-IEs ::= SEQUENCE {
+ rach-ReportReq-r9 BOOLEAN,
+ rlf-ReportReq-r9 BOOLEAN,
+ nonCriticalExtension SEQUENCE {} OPTIONAL -- Need OP
+}
+
+
+UEInformationResponse-r9 ::= SEQUENCE {
+ rrc-TransactionIdentifier RRC-TransactionIdentifier,
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ ueInformationResponse-r9 UEInformationResponse-r9-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+UEInformationResponse-r9-IEs ::= SEQUENCE {
+ rach-Report-r9 SEQUENCE {
+ numberOfPreamblesSent-r9 INTEGER (1..200),
+ contentionDetected-r9 BOOLEAN
+ } OPTIONAL,
+ rlfReport-r9 RLF-Report-r9 OPTIONAL,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+RLF-Report-r9 ::= SEQUENCE {
+ measResultLastServCell SEQUENCE {
+ rsrpResult RSRP-Range,
+ rsrqResult RSRQ-Range OPTIONAL
+ },
+ measResultNeighCells SEQUENCE {
+ measResultListEUTRA MeasResultList2EUTRA OPTIONAL,
+ measResultListUTRA MeasResultList2UTRA OPTIONAL,
+ measResultListGERAN MeasResultListGERAN OPTIONAL,
+ measResultsCDMA2000 MeasResultList2CDMA2000 OPTIONAL
+ } OPTIONAL,
+ ...
+}
+
+MeasResultList2EUTRA ::= SEQUENCE (SIZE (1..maxFreq)) OF SEQUENCE {
+ carrierFreq ARFCN-ValueEUTRA,
+ measResultList MeasResultListEUTRA
+}
+
+MeasResultList2UTRA ::= SEQUENCE (SIZE (1..maxCellReport)) OF SEQUENCE {
+ carrierFreq ARFCN-ValueUTRA,
+ measResultList MeasResultListUTRA
+}
+
+MeasResultList2CDMA2000 ::= SEQUENCE (SIZE (1..maxCellReport)) OF SEQUENCE {
+ carrierFreq CarrierFreqCDMA2000,
+ measResultList MeasResultsCDMA2000
+}
+
+
+ULHandoverPreparationTransfer ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ ulHandoverPreparationTransfer-r8 ULHandoverPreparationTransfer-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+ULHandoverPreparationTransfer-r8-IEs ::= SEQUENCE {
+ cdma2000-Type CDMA2000-Type,
+ meid BIT STRING (SIZE (56)) OPTIONAL,
+ dedicatedInfo DedicatedInfoCDMA2000,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+ULInformationTransfer ::= SEQUENCE {
+ criticalExtensions CHOICE {
+ c1 CHOICE {
+ ulInformationTransfer-r8 ULInformationTransfer-r8-IEs,
+ spare3 NULL, spare2 NULL, spare1 NULL
+ },
+ criticalExtensionsFuture SEQUENCE {}
+ }
+}
+
+ULInformationTransfer-r8-IEs ::= SEQUENCE {
+ dedicatedInfoType CHOICE {
+ dedicatedInfoNAS DedicatedInfoNAS,
+ dedicatedInfoCDMA2000-1XRTT DedicatedInfoCDMA2000,
+ dedicatedInfoCDMA2000-HRPD DedicatedInfoCDMA2000
+ },
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+
+SystemInformationBlockType2 ::= SEQUENCE {
+ ac-BarringInfo SEQUENCE {
+ ac-BarringForEmergency BOOLEAN,
+ ac-BarringForMO-Signalling AC-BarringConfig OPTIONAL, -- Need OP
+ ac-BarringForMO-Data AC-BarringConfig OPTIONAL -- Need OP
+ } OPTIONAL, -- Need OP
+ radioResourceConfigCommon RadioResourceConfigCommonSIB,
+ ue-TimersAndConstants UE-TimersAndConstants,
+ freqInfo SEQUENCE {
+ ul-CarrierFreq ARFCN-ValueEUTRA OPTIONAL, -- Need OP
+ ul-Bandwidth ENUMERATED {n6, n15, n25, n50, n75, n100}
+ OPTIONAL, -- Need OP
+ additionalSpectrumEmission AdditionalSpectrumEmission
+ },
+ mbsfn-SubframeConfigList MBSFN-SubframeConfigList OPTIONAL, -- Need OR
+ timeAlignmentTimerCommon TimeAlignmentTimer,
+ ...,
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ [[ ssac-BarringForMMTEL-Voice-r9 AC-BarringConfig OPTIONAL, -- Need OP
+ ssac-BarringForMMTEL-Video-r9 AC-BarringConfig OPTIONAL -- Need OP
+ ]]
+}
+
+AC-BarringConfig ::= SEQUENCE {
+ ac-BarringFactor ENUMERATED {
+ p00, p05, p10, p15, p20, p25, p30, p40,
+ p50, p60, p70, p75, p80, p85, p90, p95},
+ ac-BarringTime ENUMERATED {s4, s8, s16, s32, s64, s128, s256, s512},
+ ac-BarringForSpecialAC BIT STRING (SIZE(5))
+}
+
+MBSFN-SubframeConfigList ::= SEQUENCE (SIZE (1..maxMBSFN-Allocations)) OF MBSFN-SubframeConfig
+
+
+SystemInformationBlockType3 ::= SEQUENCE {
+ cellReselectionInfoCommon SEQUENCE {
+ q-Hyst ENUMERATED {
+ dB0, dB1, dB2, dB3, dB4, dB5, dB6, dB8, dB10,
+ dB12, dB14, dB16, dB18, dB20, dB22, dB24},
+ speedStateReselectionPars SEQUENCE {
+ mobilityStateParameters MobilityStateParameters,
+ q-HystSF SEQUENCE {
+ sf-Medium ENUMERATED {
+ dB-6, dB-4, dB-2, dB0},
+ sf-High ENUMERATED {
+ dB-6, dB-4, dB-2, dB0}
+ }
+ } OPTIONAL -- Need OP
+ },
+ cellReselectionServingFreqInfo SEQUENCE {
+ s-NonIntraSearch ReselectionThreshold OPTIONAL, -- Need OP
+ threshServingLow ReselectionThreshold,
+ cellReselectionPriority CellReselectionPriority
+ },
+ intraFreqCellReselectionInfo SEQUENCE {
+ q-RxLevMin Q-RxLevMin,
+ p-Max P-Max OPTIONAL, -- Need OP
+ s-IntraSearch ReselectionThreshold OPTIONAL, -- Need OP
+ allowedMeasBandwidth AllowedMeasBandwidth OPTIONAL, -- Need OP
+ presenceAntennaPort1 PresenceAntennaPort1,
+ neighCellConfig NeighCellConfig,
+ t-ReselectionEUTRA T-Reselection,
+ t-ReselectionEUTRA-SF SpeedStateScaleFactors OPTIONAL -- Need OP
+ },
+ ...,
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ [[ s-IntraSearch-v920 SEQUENCE {
+ s-IntraSearchP-r9 ReselectionThreshold,
+ s-IntraSearchQ-r9 ReselectionThresholdQ-r9
+ } OPTIONAL, -- Need OP
+ s-NonIntraSearch-v920 SEQUENCE {
+ s-NonIntraSearchP-r9 ReselectionThreshold,
+ s-NonIntraSearchQ-r9 ReselectionThresholdQ-r9
+ } OPTIONAL, -- Need OP
+ q-QualMin-r9 Q-QualMin-r9 OPTIONAL, -- Need OP
+ threshServingLowQ-r9 ReselectionThresholdQ-r9 OPTIONAL -- Need OP
+ ]]
+}
+
+
+SystemInformationBlockType4 ::= SEQUENCE {
+ intraFreqNeighCellList IntraFreqNeighCellList OPTIONAL, -- Need OR
+ intraFreqBlackCellList IntraFreqBlackCellList OPTIONAL, -- Need OR
+ csg-PhysCellIdRange PhysCellIdRange OPTIONAL, -- Cond CSG
+ ...
+}
+
+IntraFreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellIntra)) OF IntraFreqNeighCellInfo
+
+IntraFreqNeighCellInfo ::= SEQUENCE {
+ physCellId PhysCellId,
+ q-OffsetCell Q-OffsetRange,
+ ...
+}
+
+IntraFreqBlackCellList ::= SEQUENCE (SIZE (1..maxCellBlack)) OF PhysCellIdRange
+
+
+SystemInformationBlockType5 ::= SEQUENCE {
+ interFreqCarrierFreqList InterFreqCarrierFreqList,
+ ...,
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL -- Need OP
+}
+
+InterFreqCarrierFreqList ::= SEQUENCE (SIZE (1..maxFreq)) OF InterFreqCarrierFreqInfo
+
+InterFreqCarrierFreqInfo ::= SEQUENCE {
+ dl-CarrierFreq ARFCN-ValueEUTRA,
+ q-RxLevMin Q-RxLevMin,
+ p-Max P-Max OPTIONAL, -- Need OP
+ t-ReselectionEUTRA T-Reselection,
+ t-ReselectionEUTRA-SF SpeedStateScaleFactors OPTIONAL, -- Need OP
+ threshX-High ReselectionThreshold,
+ threshX-Low ReselectionThreshold,
+ allowedMeasBandwidth AllowedMeasBandwidth,
+ presenceAntennaPort1 PresenceAntennaPort1,
+ cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
+ neighCellConfig NeighCellConfig,
+ q-OffsetFreq Q-OffsetRange DEFAULT dB0,
+ interFreqNeighCellList InterFreqNeighCellList OPTIONAL, -- Need OR
+ interFreqBlackCellList InterFreqBlackCellList OPTIONAL, -- Need OR
+ ...,
+ [[ q-QualMin-r9 Q-QualMin-r9 OPTIONAL, -- Need OP
+ threshX-Q-r9 SEQUENCE {
+ threshX-HighQ-r9 ReselectionThresholdQ-r9,
+ threshX-LowQ-r9 ReselectionThresholdQ-r9
+ } OPTIONAL -- Cond RSRQ
+ ]]
+}
+
+InterFreqNeighCellList ::= SEQUENCE (SIZE (1..maxCellInter)) OF InterFreqNeighCellInfo
+
+InterFreqNeighCellInfo ::= SEQUENCE {
+ physCellId PhysCellId,
+ q-OffsetCell Q-OffsetRange
+}
+
+InterFreqBlackCellList ::= SEQUENCE (SIZE (1..maxCellBlack)) OF PhysCellIdRange
+
+
+SystemInformationBlockType6 ::= SEQUENCE {
+ carrierFreqListUTRA-FDD CarrierFreqListUTRA-FDD OPTIONAL, -- Need OR
+ carrierFreqListUTRA-TDD CarrierFreqListUTRA-TDD OPTIONAL, -- Need OR
+ t-ReselectionUTRA T-Reselection,
+ t-ReselectionUTRA-SF SpeedStateScaleFactors OPTIONAL, -- Need OP
+ ...,
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL -- Need OP
+}
+
+CarrierFreqListUTRA-FDD ::= SEQUENCE (SIZE (1..maxUTRA-FDD-Carrier)) OF CarrierFreqUTRA-FDD
+
+CarrierFreqUTRA-FDD ::= SEQUENCE {
+ carrierFreq ARFCN-ValueUTRA,
+ cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
+ threshX-High ReselectionThreshold,
+ threshX-Low ReselectionThreshold,
+ q-RxLevMin INTEGER (-60..-13),
+ p-MaxUTRA INTEGER (-50..33),
+ q-QualMin INTEGER (-24..0),
+ ...,
+ [[ threshX-Q-r9 SEQUENCE {
+ threshX-HighQ-r9 ReselectionThresholdQ-r9,
+ threshX-LowQ-r9 ReselectionThresholdQ-r9
+ } OPTIONAL -- Cond RSRQ
+ ]]
+}
+
+CarrierFreqListUTRA-TDD ::= SEQUENCE (SIZE (1..maxUTRA-TDD-Carrier)) OF CarrierFreqUTRA-TDD
+
+CarrierFreqUTRA-TDD ::= SEQUENCE {
+ carrierFreq ARFCN-ValueUTRA,
+ cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
+ threshX-High ReselectionThreshold,
+ threshX-Low ReselectionThreshold,
+ q-RxLevMin INTEGER (-60..-13),
+ p-MaxUTRA INTEGER (-50..33),
+ ...
+}
+
+
+SystemInformationBlockType7 ::= SEQUENCE {
+ t-ReselectionGERAN T-Reselection,
+ t-ReselectionGERAN-SF SpeedStateScaleFactors OPTIONAL, -- Need OR
+ carrierFreqsInfoList CarrierFreqsInfoListGERAN OPTIONAL, -- Need OR
+ ...
+}
+
+CarrierFreqsInfoListGERAN ::= SEQUENCE (SIZE (1..maxGNFG)) OF CarrierFreqsInfoGERAN
+
+CarrierFreqsInfoGERAN ::= SEQUENCE {
+ carrierFreqs CarrierFreqsGERAN,
+ commonInfo SEQUENCE {
+ cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
+ ncc-Permitted BIT STRING (SIZE (8)),
+ q-RxLevMin INTEGER (0..45),
+ p-MaxGERAN INTEGER (0..39) OPTIONAL, -- Need OP
+ threshX-High ReselectionThreshold,
+ threshX-Low ReselectionThreshold
+ },
+ ...
+}
+
+
+SystemInformationBlockType8 ::= SEQUENCE {
+ systemTimeInfo SystemTimeInfoCDMA2000 OPTIONAL, -- Need OR
+ searchWindowSize INTEGER (0..15) OPTIONAL, -- Need OR
+ parametersHRPD SEQUENCE {
+ preRegistrationInfoHRPD PreRegistrationInfoHRPD,
+ cellReselectionParametersHRPD CellReselectionParametersCDMA2000 OPTIONAL -- Need OR
+ } OPTIONAL, -- Need OR
+ parameters1XRTT SEQUENCE {
+ csfb-RegistrationParam1XRTT CSFB-RegistrationParam1XRTT OPTIONAL, -- Need OP
+ longCodeState1XRTT BIT STRING (SIZE (42)) OPTIONAL, -- Need OR
+ cellReselectionParameters1XRTT CellReselectionParametersCDMA2000 OPTIONAL -- Need OR
+ } OPTIONAL, -- Need OR
+ ...,
+ lateR8NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ [[ csfb-SupportForDualRxUEs-r9 BOOLEAN OPTIONAL, -- Need OR
+ cellReselectionParametersHRPD-v920 CellReselectionParametersCDMA2000-v920 OPTIONAL, -- Cond NCL-HRPD
+ cellReselectionParameters1XRTT-v920 CellReselectionParametersCDMA2000-v920 OPTIONAL, -- Cond NCL-1XRTT
+ csfb-RegistrationParam1XRTT-v920 CSFB-RegistrationParam1XRTT-v920 OPTIONAL, -- Cond REG-1XRTT
+ ac-BarringConfig1XRTT-r9 AC-BarringConfig1XRTT-r9 OPTIONAL -- Cond REG-1XRTT
+ ]]
+}
+
+CellReselectionParametersCDMA2000 ::= SEQUENCE {
+ bandClassList BandClassListCDMA2000,
+ neighCellList NeighCellListCDMA2000,
+ t-ReselectionCDMA2000 T-Reselection,
+ t-ReselectionCDMA2000-SF SpeedStateScaleFactors OPTIONAL -- Need OP
+}
+
+CellReselectionParametersCDMA2000-v920 ::= SEQUENCE {
+ neighCellList-v920 NeighCellListCDMA2000-v920
+}
+
+NeighCellListCDMA2000 ::= SEQUENCE (SIZE (1..16)) OF NeighCellCDMA2000
+
+NeighCellCDMA2000 ::= SEQUENCE {
+ bandClass BandclassCDMA2000,
+ neighCellsPerFreqList NeighCellsPerBandclassListCDMA2000
+}
+
+NeighCellsPerBandclassListCDMA2000 ::= SEQUENCE (SIZE (1..16)) OF NeighCellsPerBandclassCDMA2000
+
+NeighCellsPerBandclassCDMA2000 ::= SEQUENCE {
+ arfcn ARFCN-ValueCDMA2000,
+ physCellIdList PhysCellIdListCDMA2000
+}
+
+NeighCellListCDMA2000-v920 ::= SEQUENCE (SIZE (1..16)) OF NeighCellCDMA2000-v920
+
+NeighCellCDMA2000-v920 ::= SEQUENCE {
+ neighCellsPerFreqList-v920 NeighCellsPerBandclassListCDMA2000-v920
+}
+
+NeighCellsPerBandclassListCDMA2000-v920 ::= SEQUENCE (SIZE (1..16)) OF NeighCellsPerBandclassCDMA2000-v920
+
+NeighCellsPerBandclassCDMA2000-v920 ::= SEQUENCE {
+ physCellIdList-v920 PhysCellIdListCDMA2000-v920
+}
+
+PhysCellIdListCDMA2000 ::= SEQUENCE (SIZE (1..16)) OF PhysCellIdCDMA2000
+
+PhysCellIdListCDMA2000-v920 ::= SEQUENCE (SIZE (0..24)) OF PhysCellIdCDMA2000
+
+BandClassListCDMA2000 ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandClassInfoCDMA2000
+
+BandClassInfoCDMA2000 ::= SEQUENCE {
+ bandClass BandclassCDMA2000,
+ cellReselectionPriority CellReselectionPriority OPTIONAL, -- Need OP
+ threshX-High INTEGER (0..63),
+ threshX-Low INTEGER (0..63),
+ ...
+}
+
+AC-BarringConfig1XRTT-r9 ::= SEQUENCE {
+ ac-Barring0to9-r9 INTEGER (0..63),
+ ac-Barring10-r9 INTEGER (0..7),
+ ac-Barring11-r9 INTEGER (0..7),
+ ac-Barring12-r9 INTEGER (0..7),
+ ac-Barring13-r9 INTEGER (0..7),
+ ac-Barring14-r9 INTEGER (0..7),
+ ac-Barring15-r9 INTEGER (0..7),
+ ac-BarringMsg-r9 INTEGER (0..7),
+ ac-BarringReg-r9 INTEGER (0..7),
+ ac-BarringEmg-r9 INTEGER (0..7)
+}
+
+
+SystemInformationBlockType9 ::= SEQUENCE {
+ hnb-Name OCTET STRING (SIZE(1..48)) OPTIONAL, -- Need OR
+ ...
+}
+
+
+SystemInformationBlockType10 ::= SEQUENCE {
+ messageIdentifier BIT STRING (SIZE (16)),
+ serialNumber BIT STRING (SIZE (16)),
+ warningType OCTET STRING (SIZE (2)),
+ warningSecurityInfo OCTET STRING (SIZE (50)) OPTIONAL, -- Need OP
+ ...
+}
+
+
+SystemInformationBlockType11 ::= SEQUENCE {
+ messageIdentifier BIT STRING (SIZE (16)),
+ serialNumber BIT STRING (SIZE (16)),
+ warningMessageSegmentType ENUMERATED {notLastSegment, lastSegment},
+ warningMessageSegmentNumber INTEGER (0..63),
+ warningMessageSegment OCTET STRING,
+ dataCodingScheme OCTET STRING (SIZE (1)) OPTIONAL, -- Cond Segment1
+ ...
+}
+
+
+SystemInformationBlockType12-r9 ::= SEQUENCE {
+ messageIdentifier-r9 BIT STRING (SIZE (16)),
+ serialNumber-r9 BIT STRING (SIZE (16)),
+ warningMessageSegmentType-r9 ENUMERATED {notLastSegment, lastSegment},
+ warningMessageSegmentNumber-r9 INTEGER (0..63),
+ warningMessageSegment-r9 OCTET STRING,
+ dataCodingScheme-r9 OCTET STRING (SIZE (1)) OPTIONAL, -- Cond Segment1
+ lateR9NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ ...
+}
+
+
+SystemInformationBlockType13-r9 ::= SEQUENCE {
+ mbsfn-AreaInfoList-r9 MBSFN-AreaInfoList-r9,
+ notificationConfig-r9 MBMS-NotificationConfig-r9,
+ lateR9NonCriticalExtension OCTET STRING OPTIONAL, -- Need OP
+ ...
+}
+
+
+AntennaInfoCommon ::= SEQUENCE {
+ antennaPortsCount ENUMERATED {an1, an2, an4, spare1}
+}
+
+AntennaInfoDedicated ::= SEQUENCE {
+ transmissionMode ENUMERATED {
+ tm1, tm2, tm3, tm4, tm5, tm6,
+ tm7, tm8-v920},
+ codebookSubsetRestriction CHOICE {
+ n2TxAntenna-tm3 BIT STRING (SIZE (2)),
+ n4TxAntenna-tm3 BIT STRING (SIZE (4)),
+ n2TxAntenna-tm4 BIT STRING (SIZE (6)),
+ n4TxAntenna-tm4 BIT STRING (SIZE (64)),
+ n2TxAntenna-tm5 BIT STRING (SIZE (4)),
+ n4TxAntenna-tm5 BIT STRING (SIZE (16)),
+ n2TxAntenna-tm6 BIT STRING (SIZE (4)),
+ n4TxAntenna-tm6 BIT STRING (SIZE (16))
+ } OPTIONAL, -- Cond TM
+ ue-TransmitAntennaSelection CHOICE{
+ release NULL,
+ setup ENUMERATED {closedLoop, openLoop}
+ }
+}
+
+AntennaInfoDedicated-v920 ::= SEQUENCE {
+ codebookSubsetRestriction-v920 CHOICE {
+ n2TxAntenna-tm8-r9 BIT STRING (SIZE (6)),
+ n4TxAntenna-tm8-r9 BIT STRING (SIZE (32))
+ } OPTIONAL -- Cond TM8
+}
+
+
+CQI-ReportConfig ::= SEQUENCE {
+ cqi-ReportModeAperiodic ENUMERATED {
+ rm12, rm20, rm22, rm30, rm31,
+ spare3, spare2, spare1} OPTIONAL, -- Need OR
+ nomPDSCH-RS-EPRE-Offset INTEGER (-1..6),
+ cqi-ReportPeriodic CQI-ReportPeriodic OPTIONAL -- Need ON
+}
+
+CQI-ReportConfig-v920 ::= SEQUENCE {
+ cqi-Mask-r9 ENUMERATED {setup} OPTIONAL, -- Cond cqi-Setup
+ pmi-RI-Report-r9 ENUMERATED {setup} OPTIONAL -- Cond PMIRI
+}
+
+CQI-ReportPeriodic ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ cqi-PUCCH-ResourceIndex INTEGER (0.. 1185),
+ cqi-pmi-ConfigIndex INTEGER (0..1023),
+ cqi-FormatIndicatorPeriodic CHOICE {
+ widebandCQI NULL,
+ subbandCQI SEQUENCE {
+ k INTEGER (1..4)
+ }
+ },
+ ri-ConfigIndex INTEGER (0..1023) OPTIONAL, -- Need OR
+ simultaneousAckNackAndCQI BOOLEAN
+ }
+}
+
+
+DRB-Identity ::= INTEGER (1..32)
+
+
+LogicalChannelConfig ::= SEQUENCE {
+ ul-SpecificParameters SEQUENCE {
+ priority INTEGER (1..16),
+ prioritisedBitRate ENUMERATED {
+ kBps0, kBps8, kBps16, kBps32, kBps64, kBps128,
+ kBps256, infinity, spare8, spare7, spare6,
+ spare5, spare4, spare3, spare2, spare1},
+ bucketSizeDuration ENUMERATED {
+ ms50, ms100, ms150, ms300, ms500, ms1000, spare2,
+ spare1},
+ logicalChannelGroup INTEGER (0..3) OPTIONAL -- Need OR
+ } OPTIONAL, -- Cond UL
+ ...,
+ [[ logicalChannelSR-Mask-r9 ENUMERATED {setup} OPTIONAL -- Cond SRmask
+ ]]
+}
+
+
+MAC-MainConfig ::= SEQUENCE {
+ ul-SCH-Config SEQUENCE {
+ maxHARQ-Tx ENUMERATED {
+ n1, n2, n3, n4, n5, n6, n7, n8,
+ n10, n12, n16, n20, n24, n28,
+ spare2, spare1} OPTIONAL, -- Need ON
+ periodicBSR-Timer ENUMERATED {
+ sf5, sf10, sf16, sf20, sf32, sf40, sf64, sf80,
+ sf128, sf160, sf320, sf640, sf1280, sf2560,
+ infinity, spare1} OPTIONAL, -- Need ON
+ retxBSR-Timer ENUMERATED {
+ sf320, sf640, sf1280, sf2560, sf5120,
+ sf10240, spare2, spare1},
+ ttiBundling BOOLEAN
+ } OPTIONAL, -- Need ON
+ drx-Config DRX-Config OPTIONAL, -- Need ON
+ timeAlignmentTimerDedicated TimeAlignmentTimer,
+ phr-Config CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ periodicPHR-Timer ENUMERATED {sf10, sf20, sf50, sf100, sf200,
+ sf500, sf1000, infinity},
+ prohibitPHR-Timer ENUMERATED {sf0, sf10, sf20, sf50, sf100,
+ sf200, sf500, sf1000},
+ dl-PathlossChange ENUMERATED {dB1, dB3, dB6, infinity}
+ }
+ } OPTIONAL, -- Need ON
+ ...,
+ [[ sr-ProhibitTimer-r9 INTEGER (0..7) OPTIONAL -- Need ON
+ ]]
+}
+
+DRX-Config ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ onDurationTimer ENUMERATED {
+ psf1, psf2, psf3, psf4, psf5, psf6,
+ psf8, psf10, psf20, psf30, psf40,
+ psf50, psf60, psf80, psf100,
+ psf200},
+ drx-InactivityTimer ENUMERATED {
+ psf1, psf2, psf3, psf4, psf5, psf6,
+ psf8, psf10, psf20, psf30, psf40,
+ psf50, psf60, psf80, psf100,
+ psf200, psf300, psf500, psf750,
+ psf1280, psf1920, psf2560, spare10,
+ spare9, spare8, spare7, spare6,
+ spare5, spare4, spare3, spare2,
+ spare1},
+ drx-RetransmissionTimer ENUMERATED {
+ psf1, psf2, psf4, psf6, psf8, psf16,
+ psf24, psf33},
+ longDRX-CycleStartOffset CHOICE {
+ sf10 INTEGER(0..9),
+ sf20 INTEGER(0..19),
+ sf32 INTEGER(0..31),
+ sf40 INTEGER(0..39),
+ sf64 INTEGER(0..63),
+ sf80 INTEGER(0..79),
+ sf128 INTEGER(0..127),
+ sf160 INTEGER(0..159),
+ sf256 INTEGER(0..255),
+ sf320 INTEGER(0..319),
+ sf512 INTEGER(0..511),
+ sf640 INTEGER(0..639),
+ sf1024 INTEGER(0..1023),
+ sf1280 INTEGER(0..1279),
+ sf2048 INTEGER(0..2047),
+ sf2560 INTEGER(0..2559)
+ },
+ shortDRX SEQUENCE {
+ shortDRX-Cycle ENUMERATED {
+ sf2, sf5, sf8, sf10, sf16, sf20,
+ sf32, sf40, sf64, sf80, sf128, sf160,
+ sf256, sf320, sf512, sf640},
+ drxShortCycleTimer INTEGER (1..16)
+ } OPTIONAL -- Need OR
+ }
+}
+
+
+PDCP-Config ::= SEQUENCE {
+ discardTimer ENUMERATED {
+ ms50, ms100, ms150, ms300, ms500,
+ ms750, ms1500, infinity
+ } OPTIONAL, -- Cond Setup
+ rlc-AM SEQUENCE {
+ statusReportRequired BOOLEAN
+ } OPTIONAL, -- Cond Rlc-AM
+ rlc-UM SEQUENCE {
+ pdcp-SN-Size ENUMERATED {len7bits, len12bits}
+ } OPTIONAL, -- Cond Rlc-UM
+ headerCompression CHOICE {
+ notUsed NULL,
+ rohc SEQUENCE {
+ maxCID INTEGER (1..16383) DEFAULT 15,
+ profiles SEQUENCE {
+ profile0x0001 BOOLEAN,
+ profile0x0002 BOOLEAN,
+ profile0x0003 BOOLEAN,
+ profile0x0004 BOOLEAN,
+ profile0x0006 BOOLEAN,
+ profile0x0101 BOOLEAN,
+ profile0x0102 BOOLEAN,
+ profile0x0103 BOOLEAN,
+ profile0x0104 BOOLEAN
+ },
+ ...
+ }
+ },
+ ...
+}
+
+
+PDSCH-ConfigCommon ::= SEQUENCE {
+ referenceSignalPower INTEGER (-60..50),
+ p-b INTEGER (0..3)
+}
+
+PDSCH-ConfigDedicated::= SEQUENCE {
+ p-a ENUMERATED {
+ dB-6, dB-4dot77, dB-3, dB-1dot77,
+ dB0, dB1, dB2, dB3}
+}
+
+
+PHICH-Config ::= SEQUENCE {
+ phich-Duration ENUMERATED {normal, extended},
+ phich-Resource ENUMERATED {oneSixth, half, one, two}
+}
+
+
+PhysicalConfigDedicated ::= SEQUENCE {
+ pdsch-ConfigDedicated PDSCH-ConfigDedicated OPTIONAL, -- Need ON
+ pucch-ConfigDedicated PUCCH-ConfigDedicated OPTIONAL, -- Need ON
+ pusch-ConfigDedicated PUSCH-ConfigDedicated OPTIONAL, -- Need ON
+ uplinkPowerControlDedicated UplinkPowerControlDedicated OPTIONAL, -- Need ON
+ tpc-PDCCH-ConfigPUCCH TPC-PDCCH-Config OPTIONAL, -- Need ON
+ tpc-PDCCH-ConfigPUSCH TPC-PDCCH-Config OPTIONAL, -- Need ON
+ cqi-ReportConfig CQI-ReportConfig OPTIONAL, -- Need ON
+ soundingRS-UL-ConfigDedicated SoundingRS-UL-ConfigDedicated OPTIONAL, -- Need ON
+ antennaInfo CHOICE {
+ explicitValue AntennaInfoDedicated,
+ defaultValue NULL
+ } OPTIONAL, -- Need ON
+ schedulingRequestConfig SchedulingRequestConfig OPTIONAL, -- Need ON
+ ...,
+ [[ cqi-ReportConfig-v920 CQI-ReportConfig-v920 OPTIONAL, -- Need ON
+ antennaInfo-v920 AntennaInfoDedicated-v920 OPTIONAL -- Need ON
+ ]]
+}
+
+
+P-Max ::= INTEGER (-30..33)
+
+
+PRACH-ConfigSIB ::= SEQUENCE {
+ rootSequenceIndex INTEGER (0..837),
+ prach-ConfigInfo PRACH-ConfigInfo
+}
+
+PRACH-Config ::= SEQUENCE {
+ rootSequenceIndex INTEGER (0..837),
+ prach-ConfigInfo PRACH-ConfigInfo OPTIONAL -- Need ON
+}
+
+PRACH-ConfigInfo ::= SEQUENCE {
+ prach-ConfigIndex INTEGER (0..63),
+ highSpeedFlag BOOLEAN,
+ zeroCorrelationZoneConfig INTEGER (0..15),
+ prach-FreqOffset INTEGER (0..94)
+}
+
+
+PresenceAntennaPort1 ::= BOOLEAN
+
+
+PUCCH-ConfigCommon ::= SEQUENCE {
+ deltaPUCCH-Shift ENUMERATED {ds1, ds2, ds3},
+ nRB-CQI INTEGER (0..98),
+ nCS-AN INTEGER (0..7),
+ n1PUCCH-AN INTEGER (0..2047)
+}
+
+PUCCH-ConfigDedicated ::= SEQUENCE {
+ ackNackRepetition CHOICE{
+ release NULL,
+ setup SEQUENCE {
+ repetitionFactor ENUMERATED {n2, n4, n6, spare1},
+ n1PUCCH-AN-Rep INTEGER (0..2047)
+ }
+ },
+ tdd-AckNackFeedbackMode ENUMERATED {bundling, multiplexing} OPTIONAL -- Cond TDD
+}
+
+
+PUSCH-ConfigCommon ::= SEQUENCE {
+ pusch-ConfigBasic SEQUENCE {
+ n-SB INTEGER (1..4),
+ hoppingMode ENUMERATED {interSubFrame, intraAndInterSubFrame},
+ pusch-HoppingOffset INTEGER (0..98),
+ enable64QAM BOOLEAN
+ },
+ ul-ReferenceSignalsPUSCH UL-ReferenceSignalsPUSCH
+}
+
+PUSCH-ConfigDedicated ::= SEQUENCE {
+ betaOffset-ACK-Index INTEGER (0..15),
+ betaOffset-RI-Index INTEGER (0..15),
+ betaOffset-CQI-Index INTEGER (0..15)
+}
+
+UL-ReferenceSignalsPUSCH ::= SEQUENCE {
+ groupHoppingEnabled BOOLEAN,
+ groupAssignmentPUSCH INTEGER (0..29),
+ sequenceHoppingEnabled BOOLEAN,
+ cyclicShift INTEGER (0..7)
+}
+
+
+RACH-ConfigCommon ::= SEQUENCE {
+ preambleInfo SEQUENCE {
+ numberOfRA-Preambles ENUMERATED {
+ n4, n8, n12, n16 ,n20, n24, n28,
+ n32, n36, n40, n44, n48, n52, n56,
+ n60, n64},
+ preamblesGroupAConfig SEQUENCE {
+ sizeOfRA-PreamblesGroupA ENUMERATED {
+ n4, n8, n12, n16 ,n20, n24, n28,
+ n32, n36, n40, n44, n48, n52, n56,
+ n60},
+ messageSizeGroupA ENUMERATED {b56, b144, b208, b256},
+ messagePowerOffsetGroupB ENUMERATED {
+ minusinfinity, dB0, dB5, dB8, dB10, dB12,
+ dB15, dB18},
+ ...
+ } OPTIONAL -- Need OP
+ },
+ powerRampingParameters SEQUENCE {
+ powerRampingStep ENUMERATED {dB0, dB2,dB4, dB6},
+ preambleInitialReceivedTargetPower ENUMERATED {
+ dBm-120, dBm-118, dBm-116, dBm-114, dBm-112,
+ dBm-110, dBm-108, dBm-106, dBm-104, dBm-102,
+ dBm-100, dBm-98, dBm-96, dBm-94,
+ dBm-92, dBm-90}
+ },
+ ra-SupervisionInfo SEQUENCE {
+ preambleTransMax ENUMERATED {
+ n3, n4, n5, n6, n7, n8, n10, n20, n50,
+ n100, n200},
+ ra-ResponseWindowSize ENUMERATED {
+ sf2, sf3, sf4, sf5, sf6, sf7,
+ sf8, sf10},
+ mac-ContentionResolutionTimer ENUMERATED {
+ sf8, sf16, sf24, sf32, sf40, sf48,
+ sf56, sf64}
+ },
+ maxHARQ-Msg3Tx INTEGER (1..8),
+ ...
+}
+
+
+RACH-ConfigDedicated ::= SEQUENCE {
+ ra-PreambleIndex INTEGER (0..63),
+ ra-PRACH-MaskIndex INTEGER (0..15)
+}
+
+
+RadioResourceConfigCommonSIB ::= SEQUENCE {
+ rach-ConfigCommon RACH-ConfigCommon,
+ bcch-Config BCCH-Config,
+ pcch-Config PCCH-Config,
+ prach-Config PRACH-ConfigSIB,
+ pdsch-ConfigCommon PDSCH-ConfigCommon,
+ pusch-ConfigCommon PUSCH-ConfigCommon,
+ pucch-ConfigCommon PUCCH-ConfigCommon,
+ soundingRS-UL-ConfigCommon SoundingRS-UL-ConfigCommon,
+ uplinkPowerControlCommon UplinkPowerControlCommon,
+ ul-CyclicPrefixLength UL-CyclicPrefixLength,
+ ...
+}
+
+RadioResourceConfigCommon ::= SEQUENCE {
+ rach-ConfigCommon RACH-ConfigCommon OPTIONAL, -- Need ON
+ prach-Config PRACH-Config,
+ pdsch-ConfigCommon PDSCH-ConfigCommon OPTIONAL, -- Need ON
+ pusch-ConfigCommon PUSCH-ConfigCommon,
+ phich-Config PHICH-Config OPTIONAL, -- Need ON
+ pucch-ConfigCommon PUCCH-ConfigCommon OPTIONAL, -- Need ON
+ soundingRS-UL-ConfigCommon SoundingRS-UL-ConfigCommon OPTIONAL, -- Need ON
+ uplinkPowerControlCommon UplinkPowerControlCommon OPTIONAL, -- Need ON
+ antennaInfoCommon AntennaInfoCommon OPTIONAL, -- Need ON
+ p-Max P-Max OPTIONAL, -- Need OP
+ tdd-Config TDD-Config OPTIONAL, -- Cond TDD
+ ul-CyclicPrefixLength UL-CyclicPrefixLength,
+ ...
+}
+
+BCCH-Config ::= SEQUENCE {
+ modificationPeriodCoeff ENUMERATED {n2, n4, n8, n16}
+}
+
+PCCH-Config ::= SEQUENCE {
+ defaultPagingCycle ENUMERATED {
+ rf32, rf64, rf128, rf256},
+ nB ENUMERATED {
+ fourT, twoT, oneT, halfT, quarterT, oneEighthT,
+ oneSixteenthT, oneThirtySecondT}
+}
+
+UL-CyclicPrefixLength ::= ENUMERATED {len1, len2}
+
+
+RadioResourceConfigDedicated ::= SEQUENCE {
+ srb-ToAddModList SRB-ToAddModList OPTIONAL, -- Cond HO-Conn
+ drb-ToAddModList DRB-ToAddModList OPTIONAL, -- Cond HO-toEUTRA
+ drb-ToReleaseList DRB-ToReleaseList OPTIONAL, -- Need ON
+ mac-MainConfig CHOICE {
+ explicitValue MAC-MainConfig,
+ defaultValue NULL
+ } OPTIONAL, -- Cond HO-toEUTRA2
+ sps-Config SPS-Config OPTIONAL, -- Need ON
+ physicalConfigDedicated PhysicalConfigDedicated OPTIONAL, -- Need ON
+ ...,
+ [[ rlf-TimersAndConstants-r9 RLF-TimersAndConstants-r9 OPTIONAL -- Need ON
+ ]]
+}
+
+SRB-ToAddModList ::= SEQUENCE (SIZE (1..2)) OF SRB-ToAddMod
+
+SRB-ToAddMod ::= SEQUENCE {
+ srb-Identity INTEGER (1..2),
+ rlc-Config CHOICE {
+ explicitValue RLC-Config,
+ defaultValue NULL
+ } OPTIONAL, -- Cond Setup
+ logicalChannelConfig CHOICE {
+ explicitValue LogicalChannelConfig,
+ defaultValue NULL
+ } OPTIONAL, -- Cond Setup
+ ...
+}
+
+DRB-ToAddModList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-ToAddMod
+
+DRB-ToAddMod ::= SEQUENCE {
+ eps-BearerIdentity INTEGER (0..15) OPTIONAL, -- Cond DRB-Setup
+ drb-Identity DRB-Identity,
+ pdcp-Config PDCP-Config OPTIONAL, -- Cond PDCP
+ rlc-Config RLC-Config OPTIONAL, -- Cond Setup
+ logicalChannelIdentity INTEGER (3..10) OPTIONAL, -- Cond DRB-Setup
+ logicalChannelConfig LogicalChannelConfig OPTIONAL, -- Cond Setup
+ ...
+}
+
+DRB-ToReleaseList ::= SEQUENCE (SIZE (1..maxDRB)) OF DRB-Identity
+
+
+RLC-Config ::= CHOICE {
+ am SEQUENCE {
+ ul-AM-RLC UL-AM-RLC,
+ dl-AM-RLC DL-AM-RLC
+ },
+ um-Bi-Directional SEQUENCE {
+ ul-UM-RLC UL-UM-RLC,
+ dl-UM-RLC DL-UM-RLC
+ },
+ um-Uni-Directional-UL SEQUENCE {
+ ul-UM-RLC UL-UM-RLC
+ },
+ um-Uni-Directional-DL SEQUENCE {
+ dl-UM-RLC DL-UM-RLC
+ },
+ ...
+}
+
+UL-AM-RLC ::= SEQUENCE {
+ t-PollRetransmit T-PollRetransmit,
+ pollPDU PollPDU,
+ pollByte PollByte,
+ maxRetxThreshold ENUMERATED {
+ t1, t2, t3, t4, t6, t8, t16, t32}
+}
+
+DL-AM-RLC ::= SEQUENCE {
+ t-Reordering T-Reordering,
+ t-StatusProhibit T-StatusProhibit
+}
+
+UL-UM-RLC ::= SEQUENCE {
+ sn-FieldLength SN-FieldLength
+}
+
+DL-UM-RLC ::= SEQUENCE {
+ sn-FieldLength SN-FieldLength,
+ t-Reordering T-Reordering
+}
+
+SN-FieldLength ::= ENUMERATED {size5, size10}
+
+T-PollRetransmit ::= ENUMERATED {
+ ms5, ms10, ms15, ms20, ms25, ms30, ms35,
+ ms40, ms45, ms50, ms55, ms60, ms65, ms70,
+ ms75, ms80, ms85, ms90, ms95, ms100, ms105,
+ ms110, ms115, ms120, ms125, ms130, ms135,
+ ms140, ms145, ms150, ms155, ms160, ms165,
+ ms170, ms175, ms180, ms185, ms190, ms195,
+ ms200, ms205, ms210, ms215, ms220, ms225,
+ ms230, ms235, ms240, ms245, ms250, ms300,
+ ms350, ms400, ms450, ms500, spare9, spare8,
+ spare7, spare6, spare5, spare4, spare3,
+ spare2, spare1}
+
+PollPDU ::= ENUMERATED {
+ p4, p8, p16, p32, p64, p128, p256, pInfinity}
+
+PollByte ::= ENUMERATED {
+ kB25, kB50, kB75, kB100, kB125, kB250, kB375,
+ kB500, kB750, kB1000, kB1250, kB1500, kB2000,
+ kB3000, kBinfinity, spare1}
+
+T-Reordering ::= ENUMERATED {
+ ms0, ms5, ms10, ms15, ms20, ms25, ms30, ms35,
+ ms40, ms45, ms50, ms55, ms60, ms65, ms70,
+ ms75, ms80, ms85, ms90, ms95, ms100, ms110,
+ ms120, ms130, ms140, ms150, ms160, ms170,
+ ms180, ms190, ms200, spare1}
+
+T-StatusProhibit ::= ENUMERATED {
+ ms0, ms5, ms10, ms15, ms20, ms25, ms30, ms35,
+ ms40, ms45, ms50, ms55, ms60, ms65, ms70,
+ ms75, ms80, ms85, ms90, ms95, ms100, ms105,
+ ms110, ms115, ms120, ms125, ms130, ms135,
+ ms140, ms145, ms150, ms155, ms160, ms165,
+ ms170, ms175, ms180, ms185, ms190, ms195,
+ ms200, ms205, ms210, ms215, ms220, ms225,
+ ms230, ms235, ms240, ms245, ms250, ms300,
+ ms350, ms400, ms450, ms500, spare8, spare7,
+ spare6, spare5, spare4, spare3, spare2,
+ spare1}
+
+
+RLF-TimersAndConstants-r9 ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ t301-r9 ENUMERATED {
+ ms100, ms200, ms300, ms400, ms600, ms1000, ms1500,
+ ms2000},
+ t310-r9 ENUMERATED {
+ ms0, ms50, ms100, ms200, ms500, ms1000, ms2000},
+ n310-r9 ENUMERATED {
+ n1, n2, n3, n4, n6, n8, n10, n20},
+ t311-r9 ENUMERATED {
+ ms1000, ms3000, ms5000, ms10000, ms15000,
+ ms20000, ms30000},
+ n311-r9 ENUMERATED {
+ n1, n2, n3, n4, n5, n6, n8, n10},
+ ...
+ }
+}
+
+
+SchedulingRequestConfig ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ sr-PUCCH-ResourceIndex INTEGER (0..2047),
+ sr-ConfigIndex INTEGER (0..157),
+ dsr-TransMax ENUMERATED {
+ n4, n8, n16, n32, n64, spare3, spare2, spare1}
+ }
+}
+
+
+SoundingRS-UL-ConfigCommon ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ srs-BandwidthConfig ENUMERATED {bw0, bw1, bw2, bw3, bw4, bw5, bw6, bw7},
+ srs-SubframeConfig ENUMERATED {
+ sc0, sc1, sc2, sc3, sc4, sc5, sc6, sc7,
+ sc8, sc9, sc10, sc11, sc12, sc13, sc14, sc15},
+ ackNackSRS-SimultaneousTransmission BOOLEAN,
+ srs-MaxUpPts ENUMERATED {true} OPTIONAL -- Cond TDD
+ }
+}
+
+SoundingRS-UL-ConfigDedicated ::= CHOICE{
+ release NULL,
+ setup SEQUENCE {
+ srs-Bandwidth ENUMERATED {bw0, bw1, bw2, bw3},
+ srs-HoppingBandwidth ENUMERATED {hbw0, hbw1, hbw2, hbw3},
+ freqDomainPosition INTEGER (0..23),
+ duration BOOLEAN,
+ srs-ConfigIndex INTEGER (0..1023),
+ transmissionComb INTEGER (0..1),
+ cyclicShift ENUMERATED {cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7}
+ }
+}
+
+
+
+SPS-Config ::= SEQUENCE {
+ semiPersistSchedC-RNTI C-RNTI OPTIONAL, -- Need OR
+ sps-ConfigDL SPS-ConfigDL OPTIONAL, -- Need ON
+ sps-ConfigUL SPS-ConfigUL OPTIONAL -- Need ON
+}
+
+SPS-ConfigDL ::= CHOICE{
+ release NULL,
+ setup SEQUENCE {
+ semiPersistSchedIntervalDL ENUMERATED {
+ sf10, sf20, sf32, sf40, sf64, sf80,
+ sf128, sf160, sf320, sf640, spare6,
+ spare5, spare4, spare3, spare2,
+ spare1},
+ numberOfConfSPS-Processes INTEGER (1..8),
+ n1-PUCCH-AN-PersistentList N1-PUCCH-AN-PersistentList,
+ ...
+ }
+}
+
+SPS-ConfigUL ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ semiPersistSchedIntervalUL ENUMERATED {
+ sf10, sf20, sf32, sf40, sf64, sf80,
+ sf128, sf160, sf320, sf640, spare6,
+ spare5, spare4, spare3, spare2,
+ spare1},
+ implicitReleaseAfter ENUMERATED {e2, e3, e4, e8},
+ p0-Persistent SEQUENCE {
+ p0-NominalPUSCH-Persistent INTEGER (-126..24),
+ p0-UE-PUSCH-Persistent INTEGER (-8..7)
+ } OPTIONAL, -- Need OP
+ twoIntervalsConfig ENUMERATED {true} OPTIONAL, -- Cond TDD
+ ...
+ }
+}
+
+N1-PUCCH-AN-PersistentList ::= SEQUENCE (SIZE (1..4)) OF INTEGER (0..2047)
+
+
+TDD-Config ::= SEQUENCE {
+ subframeAssignment ENUMERATED {
+ sa0, sa1, sa2, sa3, sa4, sa5, sa6},
+ specialSubframePatterns ENUMERATED {
+ ssp0, ssp1, ssp2, ssp3, ssp4,ssp5, ssp6, ssp7,
+ ssp8}
+}
+
+
+TimeAlignmentTimer ::= ENUMERATED {
+ sf500, sf750, sf1280, sf1920, sf2560, sf5120,
+ sf10240, infinity}
+
+TPC-PDCCH-Config ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ tpc-RNTI BIT STRING (SIZE (16)),
+ tpc-Index TPC-Index
+ }
+}
+
+TPC-Index ::= CHOICE {
+ indexOfFormat3 INTEGER (1..15),
+ indexOfFormat3A INTEGER (1..31)
+}
+
+
+UplinkPowerControlCommon ::= SEQUENCE {
+ p0-NominalPUSCH INTEGER (-126..24),
+ alpha ENUMERATED {al0, al04, al05, al06, al07, al08, al09, al1},
+ p0-NominalPUCCH INTEGER (-127..-96),
+ deltaFList-PUCCH DeltaFList-PUCCH,
+ deltaPreambleMsg3 INTEGER (-1..6)
+}
+
+UplinkPowerControlDedicated ::= SEQUENCE {
+ p0-UE-PUSCH INTEGER (-8..7),
+ deltaMCS-Enabled ENUMERATED {en0, en1},
+ accumulationEnabled BOOLEAN,
+ p0-UE-PUCCH INTEGER (-8..7),
+ pSRS-Offset INTEGER (0..15),
+ filterCoefficient FilterCoefficient DEFAULT fc4
+}
+
+DeltaFList-PUCCH ::= SEQUENCE {
+ deltaF-PUCCH-Format1 ENUMERATED {deltaF-2, deltaF0, deltaF2},
+ deltaF-PUCCH-Format1b ENUMERATED {deltaF1, deltaF3, deltaF5},
+ deltaF-PUCCH-Format2 ENUMERATED {deltaF-2, deltaF0, deltaF1, deltaF2},
+ deltaF-PUCCH-Format2a ENUMERATED {deltaF-2, deltaF0, deltaF2},
+ deltaF-PUCCH-Format2b ENUMERATED {deltaF-2, deltaF0, deltaF2}
+}
+
+
+NextHopChainingCount ::= INTEGER (0..7)
+
+
+SecurityAlgorithmConfig ::= SEQUENCE {
+ cipheringAlgorithm ENUMERATED {
+ eea0, eea1, eea2, spare5, spare4, spare3,
+ spare2, spare1, ...},
+ integrityProtAlgorithm ENUMERATED {
+ eia0-v920, eia1, eia2, spare5, spare4, spare3,
+ spare2, spare1, ...}
+}
+
+
+ShortMAC-I ::= BIT STRING (SIZE (16))
+
+
+AdditionalSpectrumEmission ::= INTEGER (1..32)
+
+
+ARFCN-ValueCDMA2000 ::= INTEGER (0..2047)
+
+
+ARFCN-ValueEUTRA ::= INTEGER (0..maxEARFCN)
+
+
+ARFCN-ValueGERAN ::= INTEGER (0..1023)
+
+
+ARFCN-ValueUTRA ::= INTEGER (0..16383)
+
+
+BandclassCDMA2000 ::= ENUMERATED {
+ bc0, bc1, bc2, bc3, bc4, bc5, bc6, bc7, bc8,
+ bc9, bc10, bc11, bc12, bc13, bc14, bc15, bc16,
+ bc17, spare14, spare13, spare12, spare11, spare10,
+ spare9, spare8, spare7, spare6, spare5, spare4,
+ spare3, spare2, spare1, ...}
+
+
+BandIndicatorGERAN ::= ENUMERATED {dcs1800, pcs1900}
+
+
+CarrierFreqCDMA2000 ::= SEQUENCE {
+ bandClass BandclassCDMA2000,
+ arfcn ARFCN-ValueCDMA2000
+}
+
+
+CarrierFreqGERAN ::= SEQUENCE {
+ arfcn ARFCN-ValueGERAN,
+ bandIndicator BandIndicatorGERAN
+}
+
+
+CarrierFreqsGERAN ::= SEQUENCE {
+ startingARFCN ARFCN-ValueGERAN,
+ bandIndicator BandIndicatorGERAN,
+ followingARFCNs CHOICE {
+ explicitListOfARFCNs ExplicitListOfARFCNs,
+ equallySpacedARFCNs SEQUENCE {
+ arfcn-Spacing INTEGER (1..8),
+ numberOfFollowingARFCNs INTEGER (0..31)
+ },
+ variableBitMapOfARFCNs OCTET STRING (SIZE (1..16))
+ }
+}
+
+ExplicitListOfARFCNs ::= SEQUENCE (SIZE (0..31)) OF ARFCN-ValueGERAN
+
+
+CDMA2000-Type ::= ENUMERATED {type1XRTT, typeHRPD}
+
+
+CellIdentity ::= BIT STRING (SIZE (28))
+
+
+CellIndexList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellIndex
+
+CellIndex ::= INTEGER (1..maxCellMeas)
+
+
+CellReselectionPriority ::= INTEGER (0..7)
+
+
+CSFB-RegistrationParam1XRTT ::= SEQUENCE {
+ sid BIT STRING (SIZE (15)),
+ nid BIT STRING (SIZE (16)),
+ multipleSID BOOLEAN,
+ multipleNID BOOLEAN,
+ homeReg BOOLEAN,
+ foreignSIDReg BOOLEAN,
+ foreignNIDReg BOOLEAN,
+ parameterReg BOOLEAN,
+ powerUpReg BOOLEAN,
+ registrationPeriod BIT STRING (SIZE (7)),
+ registrationZone BIT STRING (SIZE (12)),
+ totalZone BIT STRING (SIZE (3)),
+ zoneTimer BIT STRING (SIZE (3))
+}
+
+CSFB-RegistrationParam1XRTT-v920 ::= SEQUENCE {
+ powerDownReg-r9 ENUMERATED {true}
+}
+
+
+CellGlobalIdEUTRA ::= SEQUENCE {
+ plmn-Identity PLMN-Identity,
+ cellIdentity CellIdentity
+}
+
+
+CellGlobalIdUTRA ::= SEQUENCE {
+ plmn-Identity PLMN-Identity,
+ cellIdentity BIT STRING (SIZE (28))
+}
+
+
+CellGlobalIdGERAN ::= SEQUENCE {
+ plmn-Identity PLMN-Identity,
+ locationAreaCode BIT STRING (SIZE (16)),
+ cellIdentity BIT STRING (SIZE (16))
+}
+
+
+CellGlobalIdCDMA2000 ::= CHOICE {
+ cellGlobalId1XRTT BIT STRING (SIZE (47)),
+ cellGlobalIdHRPD BIT STRING (SIZE (128))
+}
+
+
+CSG-Identity ::= BIT STRING (SIZE (27))
+
+
+MobilityControlInfo ::= SEQUENCE {
+ targetPhysCellId PhysCellId,
+ carrierFreq CarrierFreqEUTRA OPTIONAL, -- Cond HO-toEUTRA
+ carrierBandwidth CarrierBandwidthEUTRA OPTIONAL, -- Cond HO-toEUTRA
+ additionalSpectrumEmission AdditionalSpectrumEmission OPTIONAL, -- Cond HO-toEUTRA
+ t304 ENUMERATED {
+ ms50, ms100, ms150, ms200, ms500, ms1000,
+ ms2000, spare1},
+ newUE-Identity C-RNTI,
+ radioResourceConfigCommon RadioResourceConfigCommon,
+ rach-ConfigDedicated RACH-ConfigDedicated OPTIONAL, -- Need OP
+ ...
+}
+
+CarrierBandwidthEUTRA ::= SEQUENCE {
+ dl-Bandwidth ENUMERATED {
+ n6, n15, n25, n50, n75, n100, spare10,
+ spare9, spare8, spare7, spare6, spare5,
+ spare4, spare3, spare2, spare1},
+ ul-Bandwidth ENUMERATED {
+ n6, n15, n25, n50, n75, n100, spare10,
+ spare9, spare8, spare7, spare6, spare5,
+ spare4, spare3, spare2, spare1} OPTIONAL -- Need OP
+}
+
+CarrierFreqEUTRA ::= SEQUENCE {
+ dl-CarrierFreq ARFCN-ValueEUTRA,
+ ul-CarrierFreq ARFCN-ValueEUTRA OPTIONAL -- Cond FDD
+}
+
+
+MobilityParametersCDMA2000 ::= OCTET STRING
+
+
+MobilityStateParameters ::= SEQUENCE {
+ t-Evaluation ENUMERATED {
+ s30, s60, s120, s180, s240, spare3, spare2, spare1},
+ t-HystNormal ENUMERATED {
+ s30, s60, s120, s180, s240, spare3, spare2, spare1},
+ n-CellChangeMedium INTEGER (1..16),
+ n-CellChangeHigh INTEGER (1..16)
+}
+
+
+PhysCellId ::= INTEGER (0..503)
+
+
+PhysCellIdRange ::= SEQUENCE {
+ start PhysCellId,
+ range ENUMERATED {
+ n4, n8, n12, n16, n24, n32, n48, n64, n84,
+ n96, n128, n168, n252, n504, spare2,
+ spare1} OPTIONAL -- Need OP
+}
+
+
+PhysCellIdCDMA2000 ::= INTEGER (0..maxPNOffset)
+
+
+PhysCellIdGERAN ::= SEQUENCE {
+ networkColourCode BIT STRING (SIZE (3)),
+ baseStationColourCode BIT STRING (SIZE (3))
+}
+
+
+PhysCellIdUTRA-FDD ::= INTEGER (0..511)
+
+
+PhysCellIdUTRA-TDD ::= INTEGER (0..127)
+
+
+PLMN-Identity ::= SEQUENCE {
+ mcc MCC OPTIONAL, -- Cond MCC
+ mnc MNC
+}
+
+MCC ::= SEQUENCE (SIZE (3)) OF
+ MCC-MNC-Digit
+
+MNC ::= SEQUENCE (SIZE (2..3)) OF
+ MCC-MNC-Digit
+
+MCC-MNC-Digit ::= INTEGER (0..9)
+
+
+
+PreRegistrationInfoHRPD ::= SEQUENCE {
+ preRegistrationAllowed BOOLEAN,
+ preRegistrationZoneId PreRegistrationZoneIdHRPD OPTIONAL, -- cond PreRegAllowed
+ secondaryPreRegistrationZoneIdList SecondaryPreRegistrationZoneIdListHRPD OPTIONAL -- Need OR
+}
+
+SecondaryPreRegistrationZoneIdListHRPD ::= SEQUENCE (SIZE (1..2)) OF PreRegistrationZoneIdHRPD
+
+PreRegistrationZoneIdHRPD ::= INTEGER (0..255)
+
+
+Q-QualMin-r9 ::= INTEGER (-34..-3)
+
+
+Q-RxLevMin ::= INTEGER (-70..-22)
+
+
+Q-OffsetRange ::= ENUMERATED {
+ dB-24, dB-22, dB-20, dB-18, dB-16, dB-14,
+ dB-12, dB-10, dB-8, dB-6, dB-5, dB-4, dB-3,
+ dB-2, dB-1, dB0, dB1, dB2, dB3, dB4, dB5,
+ dB6, dB8, dB10, dB12, dB14, dB16, dB18,
+ dB20, dB22, dB24}
+
+
+Q-OffsetRangeInterRAT ::= INTEGER (-15..15)
+
+
+ReselectionThreshold ::= INTEGER (0..31)
+
+
+ReselectionThresholdQ-r9 ::= INTEGER (0..31)
+
+
+SpeedStateScaleFactors ::= SEQUENCE {
+ sf-Medium ENUMERATED {oDot25, oDot5, oDot75, lDot0},
+ sf-High ENUMERATED {oDot25, oDot5, oDot75, lDot0}
+}
+
+SystemInfoListGERAN ::= SEQUENCE (SIZE (1..maxGERAN-SI)) OF
+ OCTET STRING (SIZE (1..23))
+
+
+SystemTimeInfoCDMA2000 ::= SEQUENCE {
+ cdma-EUTRA-Synchronisation BOOLEAN,
+ cdma-SystemTime CHOICE {
+ synchronousSystemTime BIT STRING (SIZE (39)),
+ asynchronousSystemTime BIT STRING (SIZE (49))
+ }
+}
+
+
+TrackingAreaCode ::= BIT STRING (SIZE (16))
+
+
+T-Reselection ::= INTEGER (0..7)
+
+
+AllowedMeasBandwidth ::= ENUMERATED {mbw6, mbw15, mbw25, mbw50, mbw75, mbw100}
+
+
+Hysteresis ::= INTEGER (0..30)
+
+
+MeasConfig ::= SEQUENCE {
+ -- Measurement objects
+ measObjectToRemoveList MeasObjectToRemoveList OPTIONAL, -- Need ON
+ measObjectToAddModList MeasObjectToAddModList OPTIONAL, -- Need ON
+ -- Reporting configurations
+ reportConfigToRemoveList ReportConfigToRemoveList OPTIONAL, -- Need ON
+ reportConfigToAddModList ReportConfigToAddModList OPTIONAL, -- Need ON
+ -- Measurement identities
+ measIdToRemoveList MeasIdToRemoveList OPTIONAL, -- Need ON
+ measIdToAddModList MeasIdToAddModList OPTIONAL, -- Need ON
+ -- Other parameters
+ quantityConfig QuantityConfig OPTIONAL, -- Need ON
+ measGapConfig MeasGapConfig OPTIONAL, -- Need ON
+ s-Measure RSRP-Range OPTIONAL, -- Need ON
+ preRegistrationInfoHRPD PreRegistrationInfoHRPD OPTIONAL, -- Need OP
+ speedStatePars CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ mobilityStateParameters MobilityStateParameters,
+ timeToTrigger-SF SpeedStateScaleFactors
+ }
+ } OPTIONAL, -- Need ON
+ ...
+}
+
+MeasIdToRemoveList ::= SEQUENCE (SIZE (1..maxMeasId)) OF MeasId
+
+MeasObjectToRemoveList ::= SEQUENCE (SIZE (1..maxObjectId)) OF MeasObjectId
+
+ReportConfigToRemoveList ::= SEQUENCE (SIZE (1..maxReportConfigId)) OF ReportConfigId
+
+
+MeasGapConfig ::= CHOICE {
+ release NULL,
+ setup SEQUENCE {
+ gapOffset CHOICE {
+ gp0 INTEGER (0..39),
+ gp1 INTEGER (0..79),
+ ...
+ }
+ }
+}
+
+
+MeasId ::= INTEGER (1..maxMeasId)
+
+
+MeasIdToAddModList ::= SEQUENCE (SIZE (1..maxMeasId)) OF MeasIdToAddMod
+
+MeasIdToAddMod ::= SEQUENCE {
+ measId MeasId,
+ measObjectId MeasObjectId,
+ reportConfigId ReportConfigId
+}
+
+
+MeasObjectCDMA2000 ::= SEQUENCE {
+ cdma2000-Type CDMA2000-Type,
+ carrierFreq CarrierFreqCDMA2000,
+ searchWindowSize INTEGER (0..15) OPTIONAL, -- Need ON
+ offsetFreq Q-OffsetRangeInterRAT DEFAULT 0,
+ cellsToRemoveList CellIndexList OPTIONAL, -- Need ON
+ cellsToAddModList CellsToAddModListCDMA2000 OPTIONAL, -- Need ON
+ cellForWhichToReportCGI PhysCellIdCDMA2000 OPTIONAL, -- Need ON
+ ...
+}
+
+CellsToAddModListCDMA2000 ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddModCDMA2000
+
+CellsToAddModCDMA2000 ::= SEQUENCE {
+ cellIndex INTEGER (1..maxCellMeas),
+ physCellId PhysCellIdCDMA2000
+}
+
+
+MeasObjectEUTRA ::= SEQUENCE {
+ carrierFreq ARFCN-ValueEUTRA,
+ allowedMeasBandwidth AllowedMeasBandwidth,
+ presenceAntennaPort1 PresenceAntennaPort1,
+ neighCellConfig NeighCellConfig,
+ offsetFreq Q-OffsetRange DEFAULT dB0,
+ -- Neighbour cell list
+ cellsToRemoveList CellIndexList OPTIONAL, -- Need ON
+ cellsToAddModList CellsToAddModList OPTIONAL, -- Need ON
+ -- Black list
+ blackCellsToRemoveList CellIndexList OPTIONAL, -- Need ON
+ blackCellsToAddModList BlackCellsToAddModList OPTIONAL, -- Need ON
+ cellForWhichToReportCGI PhysCellId OPTIONAL, -- Need ON
+ ...
+}
+
+CellsToAddModList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddMod
+
+CellsToAddMod ::= SEQUENCE {
+ cellIndex INTEGER (1..maxCellMeas),
+ physCellId PhysCellId,
+ cellIndividualOffset Q-OffsetRange
+}
+
+BlackCellsToAddModList ::= SEQUENCE (SIZE (1..maxCellMeas)) OF BlackCellsToAddMod
+
+BlackCellsToAddMod ::= SEQUENCE {
+ cellIndex INTEGER (1..maxCellMeas),
+ physCellIdRange PhysCellIdRange
+}
+
+
+MeasObjectGERAN ::= SEQUENCE {
+ carrierFreqs CarrierFreqsGERAN,
+ offsetFreq Q-OffsetRangeInterRAT DEFAULT 0,
+ ncc-Permitted BIT STRING(SIZE (8)) DEFAULT '11111111'B,
+ cellForWhichToReportCGI PhysCellIdGERAN OPTIONAL, -- Need ON
+ ...
+}
+
+
+MeasObjectId ::= INTEGER (1..maxObjectId)
+
+
+MeasObjectToAddModList ::= SEQUENCE (SIZE (1..maxObjectId)) OF MeasObjectToAddMod
+
+MeasObjectToAddMod ::= SEQUENCE {
+ measObjectId MeasObjectId,
+ measObject CHOICE {
+ measObjectEUTRA MeasObjectEUTRA,
+ measObjectUTRA MeasObjectUTRA,
+ measObjectGERAN MeasObjectGERAN,
+ measObjectCDMA2000 MeasObjectCDMA2000,
+ ...
+ }
+}
+
+
+MeasObjectUTRA ::= SEQUENCE {
+ carrierFreq ARFCN-ValueUTRA,
+ offsetFreq Q-OffsetRangeInterRAT DEFAULT 0,
+ cellsToRemoveList CellIndexList OPTIONAL, -- Need ON
+ cellsToAddModList CHOICE {
+ cellsToAddModListUTRA-FDD CellsToAddModListUTRA-FDD,
+ cellsToAddModListUTRA-TDD CellsToAddModListUTRA-TDD
+ } OPTIONAL, -- Need ON
+ cellForWhichToReportCGI CHOICE {
+ utra-FDD PhysCellIdUTRA-FDD,
+ utra-TDD PhysCellIdUTRA-TDD
+ } OPTIONAL, -- Need ON
+ ...
+}
+
+CellsToAddModListUTRA-FDD ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddModUTRA-FDD
+
+CellsToAddModUTRA-FDD ::= SEQUENCE {
+ cellIndex INTEGER (1..maxCellMeas),
+ physCellId PhysCellIdUTRA-FDD
+}
+
+CellsToAddModListUTRA-TDD ::= SEQUENCE (SIZE (1..maxCellMeas)) OF CellsToAddModUTRA-TDD
+
+CellsToAddModUTRA-TDD ::= SEQUENCE {
+ cellIndex INTEGER (1..maxCellMeas),
+ physCellId PhysCellIdUTRA-TDD
+}
+
+
+MeasResults ::= SEQUENCE {
+ measId MeasId,
+ measResultServCell SEQUENCE {
+ rsrpResult RSRP-Range,
+ rsrqResult RSRQ-Range
+ },
+ measResultNeighCells CHOICE {
+ measResultListEUTRA MeasResultListEUTRA,
+ measResultListUTRA MeasResultListUTRA,
+ measResultListGERAN MeasResultListGERAN,
+ measResultsCDMA2000 MeasResultsCDMA2000,
+ ...
+ } OPTIONAL,
+ ...,
+ [[ measResultForECID-r9 MeasResultForECID-r9 OPTIONAL
+ ]]
+}
+
+MeasResultListEUTRA ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultEUTRA
+
+MeasResultEUTRA ::= SEQUENCE {
+ physCellId PhysCellId,
+ cgi-Info SEQUENCE {
+ cellGlobalId CellGlobalIdEUTRA,
+ trackingAreaCode TrackingAreaCode,
+ plmn-IdentityList PLMN-IdentityList2 OPTIONAL
+ } OPTIONAL,
+ measResult SEQUENCE {
+ rsrpResult RSRP-Range OPTIONAL,
+ rsrqResult RSRQ-Range OPTIONAL,
+ ...,
+ [[ additionalSI-Info-r9 AdditionalSI-Info-r9 OPTIONAL
+ ]]
+ }
+}
+
+MeasResultListUTRA ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultUTRA
+
+MeasResultUTRA ::= SEQUENCE {
+ physCellId CHOICE {
+ fdd PhysCellIdUTRA-FDD,
+ tdd PhysCellIdUTRA-TDD
+ },
+ cgi-Info SEQUENCE {
+ cellGlobalId CellGlobalIdUTRA,
+ locationAreaCode BIT STRING (SIZE (16)) OPTIONAL,
+ routingAreaCode BIT STRING (SIZE (8)) OPTIONAL,
+ plmn-IdentityList PLMN-IdentityList2 OPTIONAL
+ } OPTIONAL,
+ measResult SEQUENCE {
+ utra-RSCP INTEGER (-5..91) OPTIONAL,
+ utra-EcN0 INTEGER (0..49) OPTIONAL,
+ ...,
+ [[ additionalSI-Info-r9 AdditionalSI-Info-r9 OPTIONAL
+ ]]
+ }
+}
+
+MeasResultListGERAN ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultGERAN
+
+MeasResultGERAN ::= SEQUENCE {
+ carrierFreq CarrierFreqGERAN,
+ physCellId PhysCellIdGERAN,
+ cgi-Info SEQUENCE {
+ cellGlobalId CellGlobalIdGERAN,
+ routingAreaCode BIT STRING (SIZE (8)) OPTIONAL
+ } OPTIONAL,
+ measResult SEQUENCE {
+ rssi INTEGER (0..63),
+ ...
+ }
+}
+
+MeasResultsCDMA2000 ::= SEQUENCE {
+ preRegistrationStatusHRPD BOOLEAN,
+ measResultListCDMA2000 MeasResultListCDMA2000
+}
+
+MeasResultListCDMA2000 ::= SEQUENCE (SIZE (1..maxCellReport)) OF MeasResultCDMA2000
+
+MeasResultCDMA2000 ::= SEQUENCE {
+ physCellId PhysCellIdCDMA2000,
+ cgi-Info CellGlobalIdCDMA2000 OPTIONAL,
+ measResult SEQUENCE {
+ pilotPnPhase INTEGER (0..32767) OPTIONAL,
+ pilotStrength INTEGER (0..63),
+ ...
+ }
+}
+
+MeasResultForECID-r9 ::= SEQUENCE {
+ ue-RxTxTimeDiffResult-r9 INTEGER (0..4095),
+ currentSFN-r9 BIT STRING (SIZE (10))
+}
+
+PLMN-IdentityList2 ::= SEQUENCE (SIZE (1..5)) OF PLMN-Identity
+
+AdditionalSI-Info-r9 ::= SEQUENCE {
+ csg-MemberStatus-r9 ENUMERATED {member} OPTIONAL,
+ csg-Identity-r9 CSG-Identity OPTIONAL
+}
+
+
+QuantityConfig ::= SEQUENCE {
+ quantityConfigEUTRA QuantityConfigEUTRA OPTIONAL, -- Need ON
+ quantityConfigUTRA QuantityConfigUTRA OPTIONAL, -- Need ON
+ quantityConfigGERAN QuantityConfigGERAN OPTIONAL, -- Need ON
+ quantityConfigCDMA2000 QuantityConfigCDMA2000 OPTIONAL, -- Need ON
+ ...
+}
+
+QuantityConfigEUTRA ::= SEQUENCE {
+ filterCoefficientRSRP FilterCoefficient DEFAULT fc4,
+ filterCoefficientRSRQ FilterCoefficient DEFAULT fc4
+}
+
+QuantityConfigUTRA ::= SEQUENCE {
+ measQuantityUTRA-FDD ENUMERATED {cpich-RSCP, cpich-EcN0},
+ measQuantityUTRA-TDD ENUMERATED {pccpch-RSCP},
+ filterCoefficient FilterCoefficient DEFAULT fc4
+}
+
+QuantityConfigGERAN ::= SEQUENCE {
+ measQuantityGERAN ENUMERATED {rssi},
+ filterCoefficient FilterCoefficient DEFAULT fc2
+}
+
+QuantityConfigCDMA2000 ::= SEQUENCE {
+ measQuantityCDMA2000 ENUMERATED {pilotStrength, pilotPnPhaseAndPilotStrength}
+}
+
+
+ReportConfigEUTRA ::= SEQUENCE {
+ triggerType CHOICE {
+ event SEQUENCE {
+ eventId CHOICE {
+ eventA1 SEQUENCE {
+ a1-Threshold ThresholdEUTRA
+ },
+ eventA2 SEQUENCE {
+ a2-Threshold ThresholdEUTRA
+ },
+ eventA3 SEQUENCE {
+ a3-Offset INTEGER (-30..30),
+ reportOnLeave BOOLEAN
+ },
+ eventA4 SEQUENCE {
+ a4-Threshold ThresholdEUTRA
+ },
+ eventA5 SEQUENCE {
+ a5-Threshold1 ThresholdEUTRA,
+ a5-Threshold2 ThresholdEUTRA
+ },
+ ...
+ },
+ hysteresis Hysteresis,
+ timeToTrigger TimeToTrigger
+ },
+ periodical SEQUENCE {
+ purpose ENUMERATED {
+ reportStrongestCells, reportCGI}
+ }
+ },
+ triggerQuantity ENUMERATED {rsrp, rsrq},
+ reportQuantity ENUMERATED {sameAsTriggerQuantity, both},
+ maxReportCells INTEGER (1..maxCellReport),
+ reportInterval ReportInterval,
+ reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity},
+ ...,
+ [[ si-RequestForHO-r9 ENUMERATED {setup} OPTIONAL, -- Cond reportCGI
+ ue-RxTxTimeDiffPeriodical-r9 ENUMERATED {setup} OPTIONAL -- Need OR
+ ]]
+}
+
+ThresholdEUTRA ::= CHOICE{
+ threshold-RSRP RSRP-Range,
+ threshold-RSRQ RSRQ-Range
+}
+
+
+ReportConfigId ::= INTEGER (1..maxReportConfigId)
+
+
+ReportConfigInterRAT ::= SEQUENCE {
+ triggerType CHOICE {
+ event SEQUENCE {
+ eventId CHOICE {
+ eventB1 SEQUENCE {
+ b1-Threshold CHOICE {
+ b1-ThresholdUTRA ThresholdUTRA,
+ b1-ThresholdGERAN ThresholdGERAN,
+ b1-ThresholdCDMA2000 ThresholdCDMA2000
+ }
+ },
+ eventB2 SEQUENCE {
+ b2-Threshold1 ThresholdEUTRA,
+ b2-Threshold2 CHOICE {
+ b2-Threshold2UTRA ThresholdUTRA,
+ b2-Threshold2GERAN ThresholdGERAN,
+ b2-Threshold2CDMA2000 ThresholdCDMA2000
+ }
+ },
+ ...
+ },
+ hysteresis Hysteresis,
+ timeToTrigger TimeToTrigger
+ },
+ periodical SEQUENCE {
+ purpose ENUMERATED {
+ reportStrongestCells,
+ reportStrongestCellsForSON,
+ reportCGI}
+ }
+ },
+ maxReportCells INTEGER (1..maxCellReport),
+ reportInterval ReportInterval,
+ reportAmount ENUMERATED {r1, r2, r4, r8, r16, r32, r64, infinity},
+ ...,
+ [[ si-RequestForHO-r9 ENUMERATED {setup} OPTIONAL -- Cond reportCGI
+ ]]
+}
+
+ThresholdUTRA ::= CHOICE{
+ utra-RSCP INTEGER (-5..91),
+ utra-EcN0 INTEGER (0..49)
+}
+
+ThresholdGERAN ::= INTEGER (0..63)
+
+ThresholdCDMA2000 ::= INTEGER (0..63)
+
+
+ReportConfigToAddModList ::= SEQUENCE (SIZE (1..maxReportConfigId)) OF ReportConfigToAddMod
+
+ReportConfigToAddMod ::= SEQUENCE {
+ reportConfigId ReportConfigId,
+ reportConfig CHOICE {
+ reportConfigEUTRA ReportConfigEUTRA,
+ reportConfigInterRAT ReportConfigInterRAT
+ }
+}
+
+
+
+ReportInterval ::= ENUMERATED {
+ ms120, ms240, ms480, ms640, ms1024, ms2048, ms5120, ms10240,
+ min1, min6, min12, min30, min60, spare3, spare2, spare1}
+
+
+RSRP-Range ::= INTEGER(0..97)
+
+
+RSRQ-Range ::= INTEGER(0..34)
+
+
+TimeToTrigger ::= ENUMERATED {
+ ms0, ms40, ms64, ms80, ms100, ms128, ms160, ms256,
+ ms320, ms480, ms512, ms640, ms1024, ms1280, ms2560,
+ ms5120}
+
+
+C-RNTI ::= BIT STRING (SIZE (16))
+
+
+DedicatedInfoCDMA2000 ::= OCTET STRING
+
+
+DedicatedInfoNAS ::= OCTET STRING
+
+
+FilterCoefficient ::= ENUMERATED {
+ fc0, fc1, fc2, fc3, fc4, fc5,
+ fc6, fc7, fc8, fc9, fc11, fc13,
+ fc15, fc17, fc19, spare1, ...}
+
+
+MMEC ::= BIT STRING (SIZE (8))
+
+
+NeighCellConfig ::= BIT STRING (SIZE (2))
+
+
+OtherConfig-r9 ::= SEQUENCE {
+ reportProximityConfig-r9 ReportProximityConfig-r9 OPTIONAL, -- Need ON
+ ...
+}
+
+ReportProximityConfig-r9 ::= SEQUENCE {
+ proximityIndicationEUTRA-r9 ENUMERATED {enabled} OPTIONAL, -- Need OR
+ proximityIndicationUTRA-r9 ENUMERATED {enabled} OPTIONAL -- Need OR
+}
+
+
+RAND-CDMA2000 ::= BIT STRING (SIZE (32))
+
+
+RAT-Type ::= ENUMERATED {
+ eutra, utra, geran-cs, geran-ps, cdma2000-1XRTT,
+ spare3, spare2, spare1, ...}
+
+
+RRC-TransactionIdentifier ::= INTEGER (0..3)
+
+
+S-TMSI ::= SEQUENCE {
+ mmec MMEC,
+ m-TMSI BIT STRING (SIZE (32))
+}
+
+
+UE-CapabilityRAT-ContainerList ::=SEQUENCE (SIZE (0..maxRAT-Capabilities)) OF UE-CapabilityRAT-Container
+
+UE-CapabilityRAT-Container ::= SEQUENCE {
+ rat-Type RAT-Type,
+ ueCapabilityRAT-Container OCTET STRING
+}
+
+
+UE-EUTRA-Capability ::= SEQUENCE {
+ accessStratumRelease AccessStratumRelease,
+ ue-Category INTEGER (1..5),
+ pdcp-Parameters PDCP-Parameters,
+ phyLayerParameters PhyLayerParameters,
+ rf-Parameters RF-Parameters,
+ measParameters MeasParameters,
+ featureGroupIndicators BIT STRING (SIZE (32)) OPTIONAL,
+ interRAT-Parameters SEQUENCE {
+ utraFDD IRAT-ParametersUTRA-FDD OPTIONAL,
+ utraTDD128 IRAT-ParametersUTRA-TDD128 OPTIONAL,
+ utraTDD384 IRAT-ParametersUTRA-TDD384 OPTIONAL,
+ utraTDD768 IRAT-ParametersUTRA-TDD768 OPTIONAL,
+ geran IRAT-ParametersGERAN OPTIONAL,
+ cdma2000-HRPD IRAT-ParametersCDMA2000-HRPD OPTIONAL,
+ cdma2000-1xRTT IRAT-ParametersCDMA2000-1XRTT OPTIONAL
+ },
+ nonCriticalExtension UE-EUTRA-Capability-v920-IEs OPTIONAL
+}
+
+UE-EUTRA-Capability-v920-IEs ::= SEQUENCE {
+ phyLayerParameters-v920 PhyLayerParameters-v920,
+ interRAT-ParametersGERAN-v920 IRAT-ParametersGERAN-v920,
+ interRAT-ParametersUTRA-v920 IRAT-ParametersUTRA-v920 OPTIONAL,
+ interRAT-Parameters-v920 IRAT-ParametersCDMA2000-1XRTT-v920 OPTIONAL,
+ deviceType-r9 ENUMERATED {noBenFromBatConsumpOpt} OPTIONAL,
+ csg-ProximityIndicationParameters-r9 CSG-ProximityIndicationParameters-r9,
+ neighCellSI-AcquisitionParameters-r9 NeighCellSI-AcquisitionParameters-r9,
+ son-Parameters-r9 SON-Parameters-r9,
+ nonCriticalExtension SEQUENCE {} OPTIONAL
+}
+
+AccessStratumRelease ::= ENUMERATED {
+ rel8, rel9, spare6, spare5, spare4, spare3,
+ spare2, spare1, ...}
+
+PDCP-Parameters ::= SEQUENCE {
+ supportedROHC-Profiles SEQUENCE {
+ profile0x0001 BOOLEAN,
+ profile0x0002 BOOLEAN,
+ profile0x0003 BOOLEAN,
+ profile0x0004 BOOLEAN,
+ profile0x0006 BOOLEAN,
+ profile0x0101 BOOLEAN,
+ profile0x0102 BOOLEAN,
+ profile0x0103 BOOLEAN,
+ profile0x0104 BOOLEAN
+ },
+ maxNumberROHC-ContextSessions ENUMERATED {
+ cs2, cs4, cs8, cs12, cs16, cs24, cs32,
+ cs48, cs64, cs128, cs256, cs512, cs1024,
+ cs16384, spare2, spare1} DEFAULT cs16,
+ ...
+}
+
+PhyLayerParameters ::= SEQUENCE {
+ ue-TxAntennaSelectionSupported BOOLEAN,
+ ue-SpecificRefSigsSupported BOOLEAN
+}
+
+PhyLayerParameters-v920 ::= SEQUENCE {
+ enhancedDualLayerFDD-Supported-r9 BOOLEAN,
+ enhancedDualLayerTDD-Supported-r9 BOOLEAN
+}
+
+RF-Parameters ::= SEQUENCE {
+ supportedBandListEUTRA SupportedBandListEUTRA
+}
+
+SupportedBandListEUTRA ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandEUTRA
+
+SupportedBandEUTRA ::= SEQUENCE {
+ bandEUTRA INTEGER (1..64),
+ halfDuplex BOOLEAN
+}
+
+MeasParameters ::= SEQUENCE {
+ bandListEUTRA BandListEUTRA
+}
+
+BandListEUTRA ::= SEQUENCE (SIZE (1..maxBands)) OF BandInfoEUTRA
+
+BandInfoEUTRA ::= SEQUENCE {
+ interFreqBandList InterFreqBandList,
+ interRAT-BandList InterRAT-BandList OPTIONAL
+}
+
+InterFreqBandList ::= SEQUENCE (SIZE (1..maxBands)) OF InterFreqBandInfo
+
+InterFreqBandInfo ::= SEQUENCE {
+ interFreqNeedForGaps BOOLEAN
+}
+
+InterRAT-BandList ::= SEQUENCE (SIZE (1..maxBands)) OF InterRAT-BandInfo
+
+InterRAT-BandInfo ::= SEQUENCE {
+ interRAT-NeedForGaps BOOLEAN
+}
+
+IRAT-ParametersUTRA-FDD ::= SEQUENCE {
+ supportedBandListUTRA-FDD SupportedBandListUTRA-FDD
+}
+
+IRAT-ParametersUTRA-v920 ::= SEQUENCE {
+ e-Redirection-r9 ENUMERATED {supported}
+}
+
+SupportedBandListUTRA-FDD ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-FDD
+
+SupportedBandUTRA-FDD ::= ENUMERATED {
+ bandI, bandII, bandIII, bandIV, bandV, bandVI,
+ bandVII, bandVIII, bandIX, bandX, bandXI,
+ bandXII, bandXIII, bandXIV, bandXV, bandXVI, ...}
+
+IRAT-ParametersUTRA-TDD128 ::= SEQUENCE {
+ supportedBandListUTRA-TDD128 SupportedBandListUTRA-TDD128
+}
+
+SupportedBandListUTRA-TDD128 ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-TDD128
+
+SupportedBandUTRA-TDD128 ::= ENUMERATED {
+ a, b, c, d, e, f, g, h, i, j, k, l, m, n,
+ o, p, ...}
+
+IRAT-ParametersUTRA-TDD384 ::= SEQUENCE {
+ supportedBandListUTRA-TDD384 SupportedBandListUTRA-TDD384
+}
+
+SupportedBandListUTRA-TDD384 ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-TDD384
+
+SupportedBandUTRA-TDD384 ::= ENUMERATED {
+ a, b, c, d, e, f, g, h, i, j, k, l, m, n,
+ o, p, ...}
+
+IRAT-ParametersUTRA-TDD768 ::= SEQUENCE {
+ supportedBandListUTRA-TDD768 SupportedBandListUTRA-TDD768
+}
+
+SupportedBandListUTRA-TDD768 ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandUTRA-TDD768
+
+SupportedBandUTRA-TDD768 ::= ENUMERATED {
+ a, b, c, d, e, f, g, h, i, j, k, l, m, n,
+ o, p, ...}
+
+IRAT-ParametersGERAN ::= SEQUENCE {
+ supportedBandListGERAN SupportedBandListGERAN,
+ interRAT-PS-HO-ToGERAN BOOLEAN
+}
+
+IRAT-ParametersGERAN-v920 ::= SEQUENCE {
+ dtm-r9 ENUMERATED {supported} OPTIONAL,
+ e-RedirectionGERAN-r9 ENUMERATED {supported} OPTIONAL
+}
+
+SupportedBandListGERAN ::= SEQUENCE (SIZE (1..maxBands)) OF SupportedBandGERAN
+
+SupportedBandGERAN ::= ENUMERATED {
+ gsm450, gsm480, gsm710, gsm750, gsm810, gsm850,
+ gsm900P, gsm900E, gsm900R, gsm1800, gsm1900,
+ spare5, spare4, spare3, spare2, spare1, ...}
+
+IRAT-ParametersCDMA2000-HRPD ::= SEQUENCE {
+ supportedBandListHRPD SupportedBandListHRPD,
+ tx-ConfigHRPD ENUMERATED {single, dual},
+ rx-ConfigHRPD ENUMERATED {single, dual}
+}
+
+SupportedBandListHRPD ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandclassCDMA2000
+
+IRAT-ParametersCDMA2000-1XRTT ::= SEQUENCE {
+ supportedBandList1XRTT SupportedBandList1XRTT,
+ tx-Config1XRTT ENUMERATED {single, dual},
+ rx-Config1XRTT ENUMERATED {single, dual}
+}
+
+IRAT-ParametersCDMA2000-1XRTT-v920 ::= SEQUENCE {
+ e-CSFB-r9 ENUMERATED {supported},
+ e-CSFB-ConcPS-Mob-r9 ENUMERATED {notSupported, supported}
+}
+
+SupportedBandList1XRTT ::= SEQUENCE (SIZE (1..maxCDMA-BandClass)) OF BandclassCDMA2000
+
+CSG-ProximityIndicationParameters-r9 ::= SEQUENCE {
+ intraFreqProximityIndicationSupported-r9 BOOLEAN,
+ interFreqProximityIndicationSupported-r9 BOOLEAN,
+ utran-ProximityIndicationSupported-r9 BOOLEAN
+}
+
+NeighCellSI-AcquisitionParameters-r9 ::= SEQUENCE {
+ intraFreqSI-AcquisitionForHO-Supported-r9 BOOLEAN,
+ interFreqSI-AcquisitionForHO-Supported-r9 BOOLEAN,
+ utran-SI-AcquisitionForHO-Supported-r9 BOOLEAN
+}
+
+SON-Parameters-r9 ::= SEQUENCE {
+ rach-ReportSupported-r9 BOOLEAN
+}
+
+
+UE-TimersAndConstants ::= SEQUENCE {
+ t300 ENUMERATED {
+ ms100, ms200, ms300, ms400, ms600, ms1000, ms1500,
+ ms2000},
+ t301 ENUMERATED {
+ ms100, ms200, ms300, ms400, ms600, ms1000, ms1500,
+ ms2000},
+ t310 ENUMERATED {
+ ms0, ms50, ms100, ms200, ms500, ms1000, ms2000},
+ n310 ENUMERATED {
+ n1, n2, n3, n4, n6, n8, n10, n20},
+ t311 ENUMERATED {
+ ms1000, ms3000, ms5000, ms10000, ms15000,
+ ms20000, ms30000},
+ n311 ENUMERATED {
+ n1, n2, n3, n4, n5, n6, n8, n10},
+ ...
+}
+
+
+MBMS-NotificationConfig-r9 ::= SEQUENCE {
+ notificationRepetitionCoeff-r9 ENUMERATED {n2, n4},
+ notificationOffset-r9 INTEGER (0..10),
+ notificationSF-Index-r9 INTEGER (1..6)
+}
+
+
+MBSFN-AreaInfoList-r9 ::= SEQUENCE (SIZE(1..maxMBSFN-Area)) OF MBSFN-AreaInfo-r9
+
+MBSFN-AreaInfo-r9 ::= SEQUENCE {
+ mbsfn-AreaId-r9 INTEGER (0..255),
+ non-MBSFNregionLength ENUMERATED {s1, s2},
+ notificationIndicator-r9 INTEGER (0..7),
+ mcch-Config-r9 SEQUENCE {
+ mcch-RepetitionPeriod-r9 ENUMERATED {rf32, rf64, rf128, rf256},
+ mcch-Offset-r9 INTEGER (0..10),
+ mcch-ModificationPeriod-r9 ENUMERATED {rf512, rf1024},
+ sf-AllocInfo-r9 BIT STRING (SIZE(6)),
+ signallingMCS-r9 ENUMERATED {n2, n7, n13, n19}
+ },
+ ...
+}
+
+
+MBSFN-SubframeConfig ::= SEQUENCE {
+ radioframeAllocationPeriod ENUMERATED {n1, n2, n4, n8, n16, n32},
+ radioframeAllocationOffset INTEGER (0..7),
+ subframeAllocation CHOICE {
+ oneFrame BIT STRING (SIZE(6)),
+ fourFrames BIT STRING (SIZE(24))
+ }
+}
+
+PMCH-InfoList-r9 ::= SEQUENCE (SIZE (0..maxPMCH-PerMBSFN)) OF PMCH-Info-r9
+
+PMCH-Info-r9 ::= SEQUENCE {
+ pmch-Config-r9 PMCH-Config-r9,
+ mbms-SessionInfoList-r9 MBMS-SessionInfoList-r9,
+ ...
+}
+
+MBMS-SessionInfoList-r9 ::= SEQUENCE (SIZE (0..maxSessionPerPMCH)) OF MBMS-SessionInfo-r9
+
+MBMS-SessionInfo-r9 ::= SEQUENCE {
+ tmgi-r9 TMGI-r9,
+ sessionId-r9 OCTET STRING (SIZE (1)) OPTIONAL, -- Need OR
+ logicalChannelIdentity-r9 INTEGER (0..maxSessionPerPMCH-1),
+ ...
+}
+
+PMCH-Config-r9 ::= SEQUENCE {
+ sf-AllocEnd-r9 INTEGER (0..1535),
+ dataMCS-r9 INTEGER (0..28),
+ mch-SchedulingPeriod-r9 ENUMERATED {
+ rf8, rf16, rf32, rf64, rf128, rf256, rf512, rf1024},
+ ...
+}
+
+TMGI-r9 ::= SEQUENCE {
+ plmn-Id-r9 CHOICE {
+ plmn-Index-r9 INTEGER (1..6),
+ explicitValue-r9 PLMN-Identity
+ },
+ serviceId-r9 OCTET STRING (SIZE (3))
+}
+
+
+maxBands INTEGER ::= 64 -- Maximum number of bands listed in EUTRA UE caps
+maxCDMA-BandClass INTEGER ::= 32 -- Maximum value of the CDMA band classes
+maxCellBlack INTEGER ::= 16 -- Maximum number of blacklisted cells
+ -- listed in SIB type 4 and 5
+maxCellInter INTEGER ::= 16 -- Maximum number of neighbouring inter-frequency
+ -- cells listed in SIB type 5
+maxCellIntra INTEGER ::= 16 -- Maximum number of neighbouring intra-frequency
+ -- cells listed in SIB type 4
+maxCellMeas INTEGER ::= 32 -- Maximum number of entries in each of the neighbour
+ -- cell lists in a measurement object
+maxCellReport INTEGER ::= 8 -- Maximum number of reported cells
+maxDRB INTEGER ::= 11 -- Maximum number of Data Radio Bearers
+maxEARFCN INTEGER ::= 65535 -- Maximum value of EUTRA carrier fequency
+maxFreq INTEGER ::= 8 -- Maximum number of EUTRA carrier frequencies
+maxCellInfo-GERAN-r9 INTEGER ::= 32 -- Maximum number of GERAN cells for which system in-
+ -- formation can be provided as redirection assistance
+maxGERAN-SI INTEGER ::= 10 -- Maximum number of GERAN SI blocks that can be
+ -- provided as part of NACC information
+maxGNFG INTEGER ::= 16 -- Maximum number of GERAN neighbour freq groups
+maxMBSFN-Allocations INTEGER ::= 8 -- Maximum number of MBSFN frame allocations with
+ -- different offset
+maxMBSFN-Area INTEGER ::= 8
+maxSessionPerPMCH INTEGER ::= 29
+maxSessionPerPMCH-1 INTEGER ::= 28
+maxPMCH-PerMBSFN INTEGER ::= 15
+maxMeasId INTEGER ::= 32
+maxObjectId INTEGER ::= 32
+maxPageRec INTEGER ::= 16 --
+maxPNOffset INTEGER ::= 511 -- Maximum number of CDMA2000 PNOffsets
+maxRAT-Capabilities INTEGER ::= 8 -- Maximum number of interworking RATs (incl EUTRA)
+maxReportConfigId INTEGER ::= 32
+maxSIB INTEGER ::= 32 -- Maximum number of SIBs
+maxSIB-1 INTEGER ::= 31
+maxSI-Message INTEGER ::= 32 -- Maximum number of SI messages
+maxUTRA-FDD-Carrier INTEGER ::= 16 -- Maximum number of UTRA FDD carrier frequencies
+maxUTRA-TDD-Carrier INTEGER ::= 16 -- Maximum number of UTRA TDD carrier frequencies
+maxUTRA-CellInfo-r9 INTEGER ::= 16 -- Maximum number of cells for which system information
+ -- can be provided as redirection assistance
+
+
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
new file mode 100644
index 0000000000..fc244c30a2
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn
@@ -0,0 +1,82 @@
+--
+-- %CopyrightBegin%
+--
+-- Copyright Ericsson AB 2001-2010. All Rights Reserved.
+--
+-- The contents of this file are subject to the Erlang Public License,
+-- Version 1.1, (the "License"); you may not use this file except in
+-- compliance with the License. You should have received a copy of the
+-- Erlang Public License along with this software. If not, it can be
+-- retrieved online at http://www.erlang.org/.
+--
+-- Software distributed under the License is distributed on an "AS IS"
+-- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+-- the License for the specific language governing rights and limitations
+-- under the License.
+--
+-- %CopyrightEnd%
+
+Extension-Addition-Group DEFINITIONS AUTOMATIC TAGS ::=
+
+BEGIN
+
+-- fetched from ITU-T Rec. X691 (07/2002)
+
+Ax ::= SEQUENCE {
+ a INTEGER (250..253),
+ b BOOLEAN,
+ c CHOICE {
+ d INTEGER,
+ ...,
+
+ [[
+ e BOOLEAN,
+ f IA5String
+ ]],
+ ...
+ },
+ ...,
+ [[
+ g NumericString (SIZE(3)),
+ h BOOLEAN OPTIONAL
+ ]]
+,
+ ...,
+ i BMPString OPTIONAL,
+ j PrintableString OPTIONAL
+
+}
+
+-- valAx Ax ::= { a 253, b TRUE, c e: TRUE, g "123", h TRUE }
+
+Ax2 ::= SEQUENCE {
+ a INTEGER (250..253),
+ b BOOLEAN,
+ ...,
+ ug NumericString
+
+}
+
+-- The value { a 253, b TRUE, c e: TRUE, g "123", h TRUE }
+-- is encoded in PER as
+-- Hexadecimal view
+-- 9E000180 010291A4
+
+-- is encoded in Unaligned PER as
+-- 9E000600 040A4690
+
+Ax3 ::= SEQUENCE {
+ a INTEGER (250..253),
+ b BOOLEAN,
+ s SEQUENCE {
+ sa INTEGER,
+ sb BOOLEAN,
+ ...,
+ [[
+ sextaddgroup INTEGER OPTIONAL
+ ]]
+ }
+}
+
+-- { a 253, b TRUE, s {sa 17, sb TRUE, sextaddgroup 11}}
+END
diff --git a/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl b/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl
index cc9a483f49..06eba8b6eb 100644
--- a/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl
+++ b/lib/asn1/test/asn1_SUITE_data/TCAPPackage_msg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/test/asn1_SUITE_data/a_SeqIn.erl b/lib/asn1/test/asn1_SUITE_data/a_SeqIn.erl
index a447524358..c6db3fd016 100644
--- a/lib/asn1/test/asn1_SUITE_data/a_SeqIn.erl
+++ b/lib/asn1/test/asn1_SUITE_data/a_SeqIn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/test/asn1_SUITE_data/b_SeqIn.erl b/lib/asn1/test/asn1_SUITE_data/b_SeqIn.erl
index a416322b8c..3fa124c278 100644
--- a/lib/asn1/test/asn1_SUITE_data/b_SeqIn.erl
+++ b/lib/asn1/test/asn1_SUITE_data/b_SeqIn.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
new file mode 100644
index 0000000000..5fcec23756
--- /dev/null
+++ b/lib/asn1/test/asn1_SUITE_data/extensionAdditionGroup.erl
@@ -0,0 +1,132 @@
+%%%-------------------------------------------------------------------
+%%% File : extensionAdditionGroup.erl
+%%% Author : Kenneth Lundin
+%%% Description :
+%%%
+%%% Created : 18 May 2010 by kenneth
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%%-------------------------------------------------------------------
+-module(extensionAdditionGroup).
+-include("Extension-Addition-Group.hrl").
+
+
+-compile(export_all).
+
+run(Erule) ->
+ Val = #'Ax'{a=253, b = true, c= {e,true}, g="123", h = true},
+ io:format("~p:~p~n",[Erule,Val]),
+ {ok,List}= asn1rt:encode('Extension-Addition-Group','Ax',Val),
+ Enc = iolist_to_binary(List),
+ io:format("~p~n",[Enc]),
+ {ok,Val2} = asn1rt:decode('Extension-Addition-Group','Ax',Enc),
+ io:format("~p~n",[Val2]),
+ case Val2 of
+ Val -> ok;
+ _ -> exit({expected,Val, got, Val2})
+ end.
+
+run2(Erule) ->
+ Val = #'Ax3'{a=253, b = true, s = #'Ax3_s'{sa = 11, sb = true, sextaddgroup = 17}},
+ io:format("~p:~p~n",[Erule,Val]),
+ {ok,List}= asn1rt:encode('Extension-Addition-Group','Ax3',Val),
+ Enc = iolist_to_binary(List),
+ io:format("~p~n",[Enc]),
+ {ok,Val2} = asn1rt:decode('Extension-Addition-Group','Ax3',Enc),
+ io:format("~p~n",[Val2]),
+ case Val2 of
+ Val -> ok;
+ _ -> exit({expected,Val, got, Val2})
+ end.
+
+run3(Erule) ->
+ Val =
+{'RRC-DL-DCCH-Message',
+ {c1,
+ {rrcConnectionReconfiguration,
+ {'RRC-RRCConnectionReconfiguration',0,
+ {c1,
+ {'rrcConnectionReconfiguration-r8',
+ {'RRC-RRCConnectionReconfiguration-r8-IEs',
+ {'RRC-MeasConfig',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
+ asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE,
+ asn1_NOVALUE,asn1_NOVALUE},
+ asn1_NOVALUE,
+ [[80,66,0,5,10,0,5,0,24,11,7,84,54,33,0,1,1,0,0,0,1,39,5,66,127,0,0,1],
+ []],
+ {'RRC-RadioResourceConfigDedicated',
+ [{'RRC-SRB-ToAddMod',1,
+ {explicitValue,
+ {am,
+ {'RRC-RLC-Config_am',
+ {'RRC-UL-AM-RLC',ms45,pInfinity,kBinfinity,t4},
+ {'RRC-DL-AM-RLC',ms35,ms0}}}},
+ {explicitValue,
+ {'RRC-LogicalChannelConfig',
+ {'RRC-LogicalChannelConfig_ul-SpecificParameters',3,infinity,
+ ms50,0},
+ asn1_NOVALUE}}}],
+ [{'RRC-DRB-ToAddMod',3,3,
+ {'RRC-PDCP-Config',infinity,
+ {'RRC-PDCP-Config_rlc-AM',false},
+ asn1_NOVALUE,
+ {notUsed,'NULL'}},
+ {am,
+ {'RRC-RLC-Config_am',
+ {'RRC-UL-AM-RLC',ms70,p256,kBinfinity,t4},
+ {'RRC-DL-AM-RLC',ms35,ms40}}},
+ 3,
+ {'RRC-LogicalChannelConfig',
+ {'RRC-LogicalChannelConfig_ul-SpecificParameters',5,infinity,ms50,
+ 1},
+ asn1_NOVALUE}},
+ {'RRC-DRB-ToAddMod',4,4,
+ {'RRC-PDCP-Config',infinity,
+ {'RRC-PDCP-Config_rlc-AM',false},
+ asn1_NOVALUE,
+ {notUsed,'NULL'}},
+ {am,
+ {'RRC-RLC-Config_am',
+ {'RRC-UL-AM-RLC',ms70,p256,kBinfinity,t4},
+ {'RRC-DL-AM-RLC',ms35,ms40}}},
+ 4,
+ {'RRC-LogicalChannelConfig',
+ {'RRC-LogicalChannelConfig_ul-SpecificParameters',5,infinity,ms50,
+ 1},
+ asn1_NOVALUE}}],
+ asn1_NOVALUE,
+ {explicitValue,
+ {'RRC-MAC-MainConfig',
+ {'RRC-MAC-MainConfig_ul-SCH-Config',n4,sf10,sf10240,false},
+ asn1_NOVALUE,sf500,
+ {setup,{'RRC-MAC-MainConfig_phr-Config_setup',sf200,sf200,dB3}},
+ asn1_NOVALUE}},
+ asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ asn1_NOVALUE,asn1_NOVALUE}}}}}}},
+ io:format("~p:~p~n",[Erule,Val]),
+ {ok,List}= asn1rt:encode('EUTRA-RRC-Definitions','DL-DCCH-Message',Val),
+ Enc = iolist_to_binary(List),
+ io:format("Result from encode:~n~p~n",[Enc]),
+ {ok,Val2} = asn1rt:decode('EUTRA-RRC-Definitions','DL-DCCH-Message',Enc),
+ io:format("Result from decode:~n~p~n",[Val2]),
+ case Val2 of
+ Val -> ok;
+ _ -> exit({expected,Val, got, Val2})
+ end.
+
diff --git a/lib/asn1/test/asn1_SUITE_data/test_records.erl b/lib/asn1/test/asn1_SUITE_data/test_records.erl
index b2c9797fdc..1fdfbb40df 100644
--- a/lib/asn1/test/asn1_SUITE_data/test_records.erl
+++ b/lib/asn1/test/asn1_SUITE_data/test_records.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/test/asn1_app_test.erl b/lib/asn1/test/asn1_app_test.erl
index 23a7e691e7..c3797f08b2 100644
--- a/lib/asn1/test/asn1_app_test.erl
+++ b/lib/asn1/test/asn1_app_test.erl
@@ -26,21 +26,24 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- fields,
- modules,
- exportall,
- app_depend
- ],
- {req, [], {conf, app_init, Cases, app_fin}}.
+all() ->
+ [fields, modules, exportall, app_depend].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-app_init(suite) -> [];
-app_init(doc) -> [];
-app_init(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
case is_app(asn1) of
{ok, AppFile} ->
io:format("AppFile: ~n~p~n", [AppFile]),
@@ -60,9 +63,9 @@ is_app(App) ->
end.
-app_fin(suite) -> [];
-app_fin(doc) -> [];
-app_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
diff --git a/lib/asn1/test/asn1_appup_test.erl b/lib/asn1/test/asn1_appup_test.erl
index 81701328a6..a2c1423eda 100644
--- a/lib/asn1/test/asn1_appup_test.erl
+++ b/lib/asn1/test/asn1_appup_test.erl
@@ -21,23 +21,29 @@
%% Purpose: Verify the application specifics of the asn1 application
%%----------------------------------------------------------------------
-module(asn1_appup_test).
-
+-compile({no_auto_import,[error/1]}).
-compile(export_all).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- appup
- ],
- {req, [], {conf, appup_init, Cases, appup_fin}}.
+all() ->
+ [appup].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup_init(suite) -> [];
-appup_init(doc) -> [];
-appup_init(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
AppFile = file_name(asn1, ".app"),
AppupFile = file_name(asn1, ".appup"),
[{app_file, AppFile}, {appup_file, AppupFile}|Config].
@@ -48,9 +54,9 @@ file_name(App, Ext) ->
filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-appup_fin(suite) -> [];
-appup_fin(doc) -> [];
-appup_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
diff --git a/lib/asn1/test/asn1_bin_SUITE.erl b/lib/asn1/test/asn1_bin_SUITE.erl
new file mode 100644
index 0000000000..a924aee0db
--- /dev/null
+++ b/lib/asn1/test/asn1_bin_SUITE.erl
@@ -0,0 +1,2382 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%% Purpose : Test suite for the ASN.1 application
+
+-module(asn1_bin_SUITE).
+-define(PER,'per_bin').
+-define(BER,'ber_bin').
+-define(ber_driver(Erule,Func),
+ case Erule of
+ ber_bin_v2 ->
+ Func;
+ _ -> ok
+ end).
+-define(per_optimize(Erule),
+ case Erule of
+ ber_bin_v2 ->[optimize];
+ _ -> []
+ end).
+-define(per_bit_opt(FuncCall),
+ case ?BER of
+ ber_bin_v2 -> FuncCall;
+% _ -> {skip,"only for bit optimized per_bin"}
+ _ -> ok
+ end).
+-define(uper_bin(FuncCall),
+ case ?PER of
+ per -> FuncCall;
+ _ -> ok
+ end).
+
+-compile(export_all).
+%%-export([Function/Arity, ...]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+%% records used by test-case default
+-record('Def1',{bool0, bool1 = asn1_DEFAULT,
+ bool2 = asn1_DEFAULT,
+ bool3 = asn1_DEFAULT}).
+
+%-record('Def2',{
+%bool10, bool11 = asn1_DEFAULT, bool12 = asn1_DEFAULT, bool13}).
+
+%-record('Def3',{
+%bool30 = asn1_DEFAULT, bool31 = asn1_DEFAULT, bool32 = asn1_DEFAULT, bool33 = asn1_DEFAULT}).
+
+
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, compile}, parse, default_per, default_ber,
+ default_per_opt, per, {group, ber}, testPrim,
+ testPrimStrings, testPrimExternal, testChoPrim,
+ testChoExtension, testChoExternal, testChoOptional,
+ testChoOptionalImplicitTag, testChoRecursive,
+ testChoTypeRefCho, testChoTypeRefPrim,
+ testChoTypeRefSeq, testChoTypeRefSet, testDef, testOpt,
+ testSeqDefault, testSeqExtension, testSeqExternal,
+ testSeqOptional, testSeqPrim, testSeqTag,
+ testSeqTypeRefCho, testSeqTypeRefPrim,
+ testSeqTypeRefSeq, testSeqTypeRefSet, testSeqOf,
+ testSeqOfIndefinite, testSeqOfCho, testSeqOfExternal,
+ testSetDefault, testSetExtension,
+ testExtensionAdditionGroup, testSetExternal,
+ testSeqOfTag, testSetOptional, testSetPrim, testSetTag,
+ testSetTypeRefCho, testSetTypeRefPrim,
+ testSetTypeRefSeq, testSetTypeRefSet, testSetOf,
+ testSetOfCho, testSetOfExternal, testSetOfTag,
+ testEnumExt, value_test, testSeq2738, constructed,
+ ber_decode_error, h323test, testSeqIndefinite,
+ testSetIndefinite, testChoiceIndefinite,
+ per_GeneralString, per_open_type, testInfObjectClass,
+ testParameterizedInfObj, testMergeCompile, testobj,
+ testDeepTConstr, testConstraints, testInvokeMod,
+ testExport, testImport, testCompactBitString,
+ testMegaco, testParamBasic, testMvrasn6,
+ testContextSwitchingTypes, testTypeValueNotation,
+ testOpenTypeImplicitTag, duplicate_tags, rtUI, testROSE,
+ testINSTANCE_OF, testTCAP, testDER, specialized_decodes,
+ special_decode_performance, test_driver_load,
+ test_ParamTypeInfObj, test_WS_ParamClass,
+ test_Defed_ObjectIdentifier, testSelectionType,
+ testSSLspecs, testNortel, test_undecoded_rest,
+ test_inline, testTcapsystem, testNBAPsystem,
+ test_compile_options, testDoubleEllipses,
+ test_modified_x420, testX420, test_x691, ticket_6143,
+ testExtensionAdditionGroup] ++ common() ++ particular().
+
+groups() ->
+ [{option_tests, [],
+ [test_compile_options, ticket_6143]},
+ {infobj, [],
+ [testInfObjectClass, testParameterizedInfObj,
+ testMergeCompile, testobj, testDeepTConstr]},
+ {performance, [],
+ [testTimer_ber, testTimer_ber_opt_driver, testTimer_per,
+ testTimer_per_opt, testTimer_uper_bin]},
+ {bugs, [],
+ [test_ParamTypeInfObj, test_WS_ParamClass,
+ test_Defed_ObjectIdentifier]},
+ {compile, [],
+ [c_syntax, c_string_per, c_string_ber,
+ c_implicit_before_choice]},
+ {ber, [],
+ [ber_choiceinseq, ber_optional, ber_optional_keyed_list,
+ ber_other]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%all(suite) -> [test_inline,testNBAPsystem,test_compile_options,ticket_6143].
+
+init_per_testcase(Func,Config) ->
+ %%?line test_server:format("Func: ~p~n",[Func]),
+ ?line {ok, _} = file:read_file_info(filename:join([?config(priv_dir,Config)])),
+ ?line code:add_patha(?config(priv_dir,Config)),
+ Dog=
+ case Func of
+ testX420 ->
+ test_server:timetrap({minutes,60}); % 60 minutes
+ _ ->
+ test_server:timetrap({minutes,30}) % 60 minutes
+ end,
+%% Dog=test_server:timetrap(1800000), % 30 minutes
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Func,Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog).
+
+
+testPrim(suite) -> [];
+testPrim(Config) ->
+ ?line testPrim:compile(Config,?BER,[]),
+ ?line testPrim_cases(?BER),
+ ?line ?ber_driver(?BER,testPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrim_cases(?BER)),
+ ?line testPrim:compile(Config,?PER,[]),
+ ?line testPrim_cases(?PER),
+ ?line ?per_bit_opt(testPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrim_cases(?PER)),
+ ?line ?uper_bin(testPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrim_cases(uper_bin)),
+ ?line testPrim:compile(Config,?PER,[optimize]),
+ ?line testPrim_cases(?PER).
+
+testPrim_cases(Rules) ->
+ ?line testPrim:bool(Rules),
+ ?line testPrim:int(Rules),
+ ?line testPrim:enum(Rules),
+ ?line testPrim:obj_id(Rules),
+ ?line testPrim:rel_oid(Rules),
+ ?line testPrim:null(Rules),
+ ?line testPrim:real(Rules).
+
+
+testCompactBitString(suite) -> [];
+testCompactBitString(Config) ->
+
+ ?line testCompactBitString:compile(Config,?BER,[compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?BER),
+
+ ?line ?ber_driver(?BER,testCompactBitString:compile(Config,?BER,[compact_bit_string,driver])),
+ ?line ?ber_driver(?BER,testCompactBitString:compact_bit_string(?BER)),
+
+ ?line testCompactBitString:compile(Config,?PER,[compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?PER),
+ ?line testCompactBitString:bit_string_unnamed(?PER),
+
+ ?line ?per_bit_opt(testCompactBitString:compile(Config,?PER,
+ [compact_bit_string,optimize])),
+ ?line ?per_bit_opt(testCompactBitString:compact_bit_string(?PER)),
+ ?line ?per_bit_opt(testCompactBitString:bit_string_unnamed(?PER)),
+ ?line ?per_bit_opt(testCompactBitString:ticket_7734(?PER)),
+
+ ?line ?uper_bin(testCompactBitString:compile(Config,uper_bin,
+ [compact_bit_string])),
+ ?line ?uper_bin(testCompactBitString:compact_bit_string(uper_bin)),
+ ?line ?uper_bin(testCompactBitString:bit_string_unnamed(uper_bin)),
+
+ ?line testCompactBitString:compile(Config,?PER,[optimize,compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?PER),
+ ?line testCompactBitString:bit_string_unnamed(?PER),
+
+ ?line testCompactBitString:otp_4869(?PER).
+
+
+testPrimStrings(suite) -> [];
+testPrimStrings(Config) ->
+
+ ?line testPrimStrings:compile(Config,?BER,[]),
+ ?line testPrimStrings_cases(?BER),
+ ?line testPrimStrings:more_strings(?BER), %% these are not implemented in per yet
+ ?line ?ber_driver(?BER,testPrimStrings:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimStrings_cases(?BER)),
+ ?line ?ber_driver(?BER,testPrimStrings:more_strings(?BER)),
+
+ ?line testPrimStrings:compile(Config,?PER,[]),
+ ?line testPrimStrings_cases(?PER),
+
+ ?line ?per_bit_opt(testPrimStrings:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimStrings_cases(?PER)),
+
+ ?line ?uper_bin(testPrimStrings:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimStrings_cases(uper_bin)),
+
+ ?line testPrimStrings:compile(Config,?PER,[optimize]),
+ ?line testPrimStrings_cases(?PER).
+
+testPrimStrings_cases(Rules) ->
+ ?line testPrimStrings:bit_string(Rules),
+ ?line testPrimStrings:bit_string_unnamed(Rules),
+ ?line testPrimStrings:octet_string(Rules),
+ ?line testPrimStrings:numeric_string(Rules),
+ ?line testPrimStrings:other_strings(Rules),
+ ?line testPrimStrings:universal_string(Rules),
+ ?line testPrimStrings:bmp_string(Rules),
+ ?line testPrimStrings:times(Rules),
+ ?line testPrimStrings:utf8_string(Rules).
+
+
+
+testPrimExternal(suite) -> [];
+testPrimExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testPrimExternal:compile(Config,?BER,[]),
+ ?line testPrimExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testPrimExternal:compile(Config,?PER,[]),
+ ?line testPrimExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testPrimExternal:compile(Config,?PER,[optimize]),
+ ?line testPrimExternal_cases(?PER).
+
+testPrimExternal_cases(Rules) ->
+ ?line testPrimExternal:external(Rules).
+
+
+
+
+testChoPrim(suite) -> [];
+testChoPrim(Config) ->
+
+ ?line testChoPrim:compile(Config,?BER,[]),
+ ?line testChoPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoPrim_cases(?BER)),
+
+ ?line testChoPrim:compile(Config,?PER,[]),
+ ?line testChoPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testChoPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoPrim_cases(?PER)),
+
+ ?line ?uper_bin(testChoPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoPrim_cases(uper_bin)),
+
+ ?line testChoPrim:compile(Config,?PER,[optimize]),
+ ?line testChoPrim_cases(?PER).
+
+testChoPrim_cases(Rules) ->
+ ?line testChoPrim:bool(Rules),
+ ?line testChoPrim:int(Rules).
+
+
+
+testChoExtension(suite) -> [];
+testChoExtension(Config) ->
+
+ ?line testChoExtension:compile(Config,?BER,[]),
+ ?line testChoExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExtension_cases(?BER)),
+
+ ?line testChoExtension:compile(Config,?PER,[]),
+ ?line testChoExtension_cases(?PER),
+
+ ?line ?per_bit_opt(testChoExtension:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExtension_cases(?PER)),
+
+ ?line ?uper_bin(testChoExtension:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExtension_cases(uper_bin)),
+
+ ?line testChoExtension:compile(Config,?PER,[optimize]),
+ ?line testChoExtension_cases(?PER).
+
+testChoExtension_cases(Rules) ->
+ ?line testChoExtension:extension(Rules).
+
+
+
+testChoExternal(suite) -> [];
+testChoExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testChoExternal:compile(Config,?BER,[]),
+ ?line testChoExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testChoExternal:compile(Config,?PER,[]),
+ ?line testChoExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testChoExternal:compile(Config,?PER,[optimize]),
+ ?line testChoExternal_cases(?PER).
+
+
+testChoExternal_cases(Rules) ->
+ ?line testChoExternal:external(Rules).
+
+
+
+testChoOptional(suite) -> [];
+testChoOptional(Config) ->
+
+ ?line testChoOptional:compile(Config,?BER,[]),
+ ?line testChoOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoOptional_cases(?BER)),
+
+ ?line testChoOptional:compile(Config,?PER,[]),
+ ?line testChoOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testChoOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoOptional_cases(?PER)),
+
+ ?line ?uper_bin(testChoOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoOptional_cases(uper_bin)),
+
+ ?line testChoOptional:compile(Config,?PER,[optimize]),
+ ?line testChoOptional_cases(?PER).
+
+testChoOptional_cases(Rules) ->
+ ?line testChoOptional:optional(Rules).
+
+testChoOptionalImplicitTag(suite) -> [];
+testChoOptionalImplicitTag(Config) ->
+ %% Only meaningful for ?BER
+ ?line testChoOptionalImplicitTag:compile(Config,?BER),
+ ?line testChoOptionalImplicitTag:optional(?BER).
+
+
+testChoRecursive(suite) -> [];
+testChoRecursive(Config) ->
+
+ ?line testChoRecursive:compile(Config,?BER,[]),
+ ?line testChoRecursive_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoRecursive:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoRecursive_cases(?BER)),
+
+ ?line testChoRecursive:compile(Config,?PER,[]),
+ ?line testChoRecursive_cases(?PER),
+
+ ?line ?per_bit_opt(testChoRecursive:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoRecursive_cases(?PER)),
+
+ ?line ?uper_bin(testChoRecursive:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoRecursive_cases(uper_bin)),
+
+ ?line testChoRecursive:compile(Config,?PER,[optimize]),
+ ?line testChoRecursive_cases(?PER).
+
+testChoRecursive_cases(Rules) ->
+ ?line testChoRecursive:recursive(Rules).
+
+
+
+testChoTypeRefCho(suite) -> [];
+testChoTypeRefCho(Config) ->
+
+ ?line testChoTypeRefCho:compile(Config,?BER,[]),
+ ?line testChoTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefCho_cases(?BER)),
+
+ ?line testChoTypeRefCho:compile(Config,?PER,[]),
+ ?line testChoTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefCho_cases(uper_bin)),
+
+ ?line testChoTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefCho_cases(?PER).
+
+testChoTypeRefCho_cases(Rules) ->
+ ?line testChoTypeRefCho:choice(Rules).
+
+
+
+testChoTypeRefPrim(suite) -> [];
+testChoTypeRefPrim(Config) ->
+
+ ?line testChoTypeRefPrim:compile(Config,?BER,[]),
+ ?line testChoTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefPrim_cases(?BER)),
+
+ ?line testChoTypeRefPrim:compile(Config,?PER,[]),
+ ?line testChoTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefPrim_cases(uper_bin)),
+
+ ?line testChoTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefPrim_cases(?PER).
+
+testChoTypeRefPrim_cases(Rules) ->
+ ?line testChoTypeRefPrim:prim(Rules).
+
+
+
+testChoTypeRefSeq(suite) -> [];
+testChoTypeRefSeq(Config) ->
+
+ ?line testChoTypeRefSeq:compile(Config,?BER,[]),
+ ?line testChoTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefSeq_cases(?BER)),
+
+ ?line testChoTypeRefSeq:compile(Config,?PER,[]),
+ ?line testChoTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefSeq_cases(uper_bin)),
+
+ ?line testChoTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefSeq_cases(?PER).
+
+testChoTypeRefSeq_cases(Rules) ->
+ ?line testChoTypeRefSeq:seq(Rules).
+
+
+
+testChoTypeRefSet(suite) -> [];
+testChoTypeRefSet(Config) ->
+
+ ?line testChoTypeRefSet:compile(Config,?BER,[]),
+ ?line testChoTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefSet_cases(?BER)),
+
+ ?line testChoTypeRefSet:compile(Config,?PER,[]),
+ ?line testChoTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefSet_cases(uper_bin)),
+
+ ?line testChoTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefSet_cases(?PER).
+
+testChoTypeRefSet_cases(Rules) ->
+ ?line testChoTypeRefSet:set(Rules).
+
+
+
+testDef(suite) -> [];
+testDef(Config) ->
+
+ ?line testDef:compile(Config,?BER,[]),
+ ?line testDef_cases(?BER),
+
+ ?line ?ber_driver(?BER,testDef:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDef_cases(?BER)),
+
+ ?line testDef:compile(Config,?PER,[]),
+ ?line testDef_cases(?PER),
+
+ ?line ?per_bit_opt(testDef:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDef_cases(?PER)),
+
+ ?line ?uper_bin(testDef:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDef_cases(uper_bin)),
+
+ ?line testDef:compile(Config,?PER,[optimize]),
+ ?line testDef_cases(?PER).
+
+testDef_cases(Rules) ->
+ ?line testDef:main(Rules).
+
+
+
+testOpt(suite) -> [];
+testOpt(Config) ->
+
+ ?line testOpt:compile(Config,?BER),
+ ?line testOpt_cases(?BER),
+
+ ?line testOpt:compile(Config,?PER),
+ ?line testOpt_cases(?PER).
+
+testOpt_cases(Rules) ->
+ ?line testOpt:main(Rules).
+
+
+testEnumExt(suite) -> [];
+testEnumExt(Config) ->
+
+ ?line testEnumExt:compile(Config,?BER,[]),
+ ?line testEnumExt:main(?BER),
+
+ ?line ?ber_driver(?BER,testEnumExt:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testEnumExt:main(?BER)),
+
+ ?line testEnumExt:compile(Config,?PER,[]),
+ ?line testEnumExt:main(?PER),
+
+ ?line ?per_bit_opt(testEnumExt:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testEnumExt:main(?PER)),
+
+ ?line ?uper_bin(testEnumExt:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testEnumExt:main(uper_bin)),
+
+ ?line testEnumExt:compile(Config,?PER,[optimize]),
+ ?line testEnumExt:main(?PER).
+
+testSeqDefault(doc) -> ["Test of OTP-2523 ENUMERATED with extensionmark."];
+testSeqDefault(suite) -> [];
+testSeqDefault(Config) ->
+
+ ?line testSeqDefault:compile(Config,?BER,[]),
+ ?line testSeqDefault_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqDefault:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqDefault_cases(?BER)),
+
+ ?line testSeqDefault:compile(Config,?PER,[]),
+ ?line testSeqDefault_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqDefault:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqDefault_cases(?PER)),
+
+ ?line ?uper_bin(testSeqDefault:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqDefault_cases(uper_bin)),
+
+ ?line testSeqDefault:compile(Config,?PER,[optimize]),
+ ?line testSeqDefault_cases(?PER).
+
+testSeqDefault_cases(Rules) ->
+ ?line testSeqDefault:main(Rules).
+
+
+
+testSeqExtension(suite) -> [];
+testSeqExtension(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqExtension:compile(Config,?BER,[]),
+ ?line testSeqExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExtension_cases(?BER)).
+
+testSeqExtension_cases(Rules) ->
+ ?line testSeqExtension:main(Rules).
+
+
+
+testSeqExternal(suite) -> [];
+testSeqExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqExternal:compile(Config,?BER,[]),
+ ?line testSeqExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExternal_cases(?BER)).
+
+testSeqExternal_cases(Rules) ->
+ ?line testSeqExternal:main(Rules).
+
+
+testSeqOptional(suite) -> [];
+testSeqOptional(Config) ->
+
+ ?line testSeqOptional:compile(Config,?BER,[]),
+ ?line testSeqOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOptional_cases(?BER)),
+
+ ?line testSeqOptional:compile(Config,?PER,[]),
+ ?line testSeqOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOptional_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOptional_cases(uper_bin)),
+
+ ?line testSeqOptional:compile(Config,?PER,[optimize]),
+ ?line testSeqOptional_cases(?PER).
+
+testSeqOptional_cases(Rules) ->
+ ?line testSeqOptional:main(Rules).
+
+
+
+testSeqPrim(suite) -> [];
+testSeqPrim(Config) ->
+
+ ?line testSeqPrim:compile(Config,?BER,[]),
+ ?line testSeqPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqPrim_cases(?BER)),
+
+ ?line testSeqPrim:compile(Config,?PER,[]),
+ ?line testSeqPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSeqPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqPrim_cases(uper_bin)),
+
+ ?line testSeqPrim:compile(Config,?PER,[optimize]),
+ ?line testSeqPrim_cases(?PER).
+
+testSeqPrim_cases(Rules) ->
+ ?line testSeqPrim:main(Rules).
+
+
+testSeq2738(doc) -> ["Test of OTP-2738 Detect corrupt optional component."];
+testSeq2738(suite) -> [];
+testSeq2738(Config) ->
+
+ ?line testSeq2738:compile(Config,?BER,[]),
+ ?line testSeq2738_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeq2738:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeq2738_cases(?BER)),
+
+ ?line testSeq2738:compile(Config,?PER,[]),
+ ?line testSeq2738_cases(?PER),
+
+ ?line ?per_bit_opt(testSeq2738:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeq2738_cases(?PER)),
+
+ ?line ?uper_bin(testSeq2738:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeq2738_cases(uper_bin)),
+
+ ?line testSeq2738:compile(Config,?PER,[optimize]),
+ ?line testSeq2738_cases(?PER).
+
+testSeq2738_cases(Rules) ->
+ ?line testSeq2738:main(Rules).
+
+
+testSeqTag(suite) -> [];
+testSeqTag(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqTag:compile(Config,?BER,[]),
+ ?line testSeqTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqTag:compile(Config,?PER,[]),
+ ?line testSeqTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqTag:compile(Config,?PER,[optimize]),
+ ?line testSeqTag_cases(?PER).
+
+testSeqTag_cases(Rules) ->
+ ?line testSeqTag:main(Rules).
+
+
+
+
+testSeqTypeRefCho(suite) -> [];
+testSeqTypeRefCho(Config) ->
+
+ ?line testSeqTypeRefCho:compile(Config,?BER,[]),
+ ?line testSeqTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefCho_cases(?BER)),
+
+ ?line testSeqTypeRefCho:compile(Config,?PER,[]),
+ ?line testSeqTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefCho_cases(uper_bin)),
+
+ ?line testSeqTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefCho_cases(?PER).
+
+testSeqTypeRefCho_cases(Rules) ->
+ ?line testSeqTypeRefCho:main(Rules).
+
+
+
+testSeqTypeRefPrim(suite) -> [];
+testSeqTypeRefPrim(Config) ->
+
+ ?line testSeqTypeRefPrim:compile(Config,?BER,[]),
+ ?line testSeqTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefPrim_cases(?BER)),
+
+ ?line testSeqTypeRefPrim:compile(Config,?PER,[]),
+ ?line testSeqTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefPrim_cases(uper_bin)),
+
+ ?line testSeqTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefPrim_cases(?PER).
+
+testSeqTypeRefPrim_cases(Rules) ->
+ ?line testSeqTypeRefPrim:main(Rules).
+
+
+
+testSeqTypeRefSeq(suite) -> [];
+testSeqTypeRefSeq(Config) ->
+
+ ?line testSeqTypeRefSeq:compile(Config,?BER,[]),
+ ?line testSeqTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefSeq_cases(?BER)),
+
+ ?line testSeqTypeRefSeq:compile(Config,?PER,[]),
+ ?line testSeqTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefSeq_cases(uper_bin)),
+
+ ?line testSeqTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefSeq_cases(?PER).
+
+testSeqTypeRefSeq_cases(Rules) ->
+ ?line testSeqTypeRefSeq:main(Rules).
+
+
+
+testSeqTypeRefSet(suite) -> [];
+testSeqTypeRefSet(Config) ->
+
+ ?line testSeqTypeRefSet:compile(Config,?BER,[]),
+ ?line testSeqTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefSet_cases(?BER)),
+
+ ?line testSeqTypeRefSet:compile(Config,?PER,[]),
+ ?line testSeqTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefSet_cases(uper_bin)),
+
+ ?line testSeqTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefSet_cases(?PER).
+
+testSeqTypeRefSet_cases(Rules) ->
+ ?line testSeqTypeRefSet:main(Rules).
+
+
+
+
+testSeqOf(suite) -> [];
+testSeqOf(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOf:compile(Config,?BER,[]),
+ ?line testSeqOf_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOf:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOf_cases(?BER)),
+
+ ?line testSeqOf:compile(Config,?PER,[]),
+ ?line testSeqOf_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOf:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOf_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOf:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOf_cases(uper_bin)),
+
+ ?line testSeqOf:compile(Config,?PER,[optimize]),
+ ?line testSeqOf_cases(?PER).
+
+testSeqOf_cases(Rules) ->
+ ?line testSeqOf:main(Rules).
+
+
+
+
+testSeqOfCho(suite) -> [];
+testSeqOfCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOfCho:compile(Config,?BER,[]),
+ ?line testSeqOfCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOfCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfCho_cases(?BER)),
+
+ ?line testSeqOfCho:compile(Config,?PER,[]),
+ ?line testSeqOfCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOfCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfCho_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOfCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfCho_cases(uper_bin)),
+
+ ?line testSeqOfCho:compile(Config,?PER,[optimize]),
+ ?line testSeqOfCho_cases(?PER).
+
+testSeqOfIndefinite(suite) -> [];
+testSeqOfIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOfIndefinite:compile(Config,?BER,[]),
+ ?line testSeqOfIndefinite:main(),
+
+ ?line ?ber_driver(?BER,testSeqOfIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfIndefinite:main()).
+
+testSeqOfCho_cases(Rules) ->
+ ?line testSeqOfCho:main(Rules).
+
+
+testSeqOfExternal(suite) -> [];
+testSeqOfExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqOfExternal:compile(Config,?BER,[]),
+ ?line testSeqOfExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqOfExternal:compile(Config,?PER,[]),
+ ?line testSeqOfExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfExternal_cases(?PER).
+
+testSeqOfExternal_cases(Rules) ->
+ ?line testSeqOfExternal:main(Rules).
+
+
+
+testSeqOfTag(suite) -> [];
+testSeqOfTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqOfTag:compile(Config,?BER,[]),
+ ?line testSeqOfTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqOfTag:compile(Config,?PER,[]),
+ ?line testSeqOfTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfTag:compile(Config,?PER,[optimize]),
+ ?line testSeqOfTag_cases(?PER).
+
+testSeqOfTag_cases(Rules) ->
+ ?line testSeqOfTag:main(Rules).
+
+
+
+
+testSetDefault(suite) -> [];
+testSetDefault(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetDefault:compile(Config,?BER,[]),
+ ?line testSetDefault_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetDefault:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetDefault_cases(?BER)),
+
+ ?line testSetDefault:compile(Config,?PER,[]),
+ ?line testSetDefault_cases(?PER),
+
+ ?line ?per_bit_opt(testSetDefault:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetDefault_cases(?PER)),
+
+ ?line ?uper_bin(testSetDefault:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetDefault_cases(uper_bin)),
+
+ ?line testSetDefault:compile(Config,?PER,[optimize]),
+ ?line testSetDefault_cases(?PER).
+
+testSetDefault_cases(Rules) ->
+ ?line testSetDefault:main(Rules).
+
+
+testParamBasic(suite) -> [];
+testParamBasic(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testParamBasic:compile(Config,?BER,[]),
+ ?line testParamBasic_cases(?BER),
+
+ ?line ?ber_driver(?BER,testParamBasic:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testParamBasic_cases(?BER)),
+
+ ?line testParamBasic:compile(Config,?PER,[]),
+ ?line testParamBasic_cases(?PER),
+
+ ?line ?per_bit_opt(testParamBasic:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testParamBasic_cases(?PER)),
+
+ ?line ?uper_bin(testParamBasic:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testParamBasic_cases(uper_bin)),
+
+ ?line testParamBasic:compile(Config,?PER,[optimize]),
+ ?line testParamBasic_cases(?PER).
+
+
+testParamBasic_cases(Rules) ->
+ ?line testParamBasic:main(Rules).
+
+testSetExtension(suite) -> [];
+testSetExtension(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetExtension:compile(Config,?BER,[]),
+ ?line testSetExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExtension_cases(?BER)).
+
+testSetExtension_cases(Rules) ->
+ ?line testSetExtension:main(Rules).
+
+
+testSetExternal(suite) -> [];
+testSetExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetExternal:compile(Config,?BER,[]),
+ ?line testSetExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExternal_cases(?BER)).
+
+testSetExternal_cases(Rules) ->
+ ?line testSetExternal:main(Rules).
+
+
+testSetOptional(suite) -> [];
+testSetOptional(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOptional:compile(Config,?BER,[]),
+ ?line testSetOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOptional_cases(?BER)),
+
+ ?line testSetOptional:compile(Config,?PER,[]),
+ ?line testSetOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOptional_cases(?PER)),
+
+ ?line ?uper_bin(testSetOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOptional_cases(uper_bin)),
+
+ ?line testSetOptional:compile(Config,?PER,[optimize]),
+ ?line testSetOptional_cases(?PER).
+
+testSetOptional_cases(Rules) ->
+ ?line ok = testSetOptional:ticket_7533(Rules),
+ ?line ok = testSetOptional:main(Rules).
+
+
+
+
+testSetPrim(suite) -> [];
+testSetPrim(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetPrim:compile(Config,?BER,[]),
+ ?line testSetPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetPrim_cases(?BER)),
+
+ ?line testSetPrim:compile(Config,?PER,[]),
+ ?line testSetPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSetPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSetPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetPrim_cases(uper_bin)),
+
+ ?line testSetPrim:compile(Config,?PER,[optimize]),
+ ?line testSetPrim_cases(?PER).
+
+testSetPrim_cases(Rules) ->
+ ?line testSetPrim:main(Rules).
+
+
+
+testSetTag(suite) -> [];
+testSetTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetTag:compile(Config,?BER,[]),
+ ?line testSetTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetTag:compile(Config,?PER,[]),
+ ?line testSetTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetTag:compile(Config,?PER,[optimize]),
+ ?line testSetTag_cases(?PER).
+
+testSetTag_cases(Rules) ->
+ ?line testSetTag:main(Rules).
+
+
+
+testSetTypeRefCho(suite) -> [];
+testSetTypeRefCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefCho:compile(Config,?BER,[]),
+ ?line testSetTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefCho_cases(?BER)),
+
+ ?line testSetTypeRefCho:compile(Config,?PER,[]),
+ ?line testSetTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefCho_cases(uper_bin)),
+
+ ?line testSetTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefCho_cases(?PER).
+
+testSetTypeRefCho_cases(Rules) ->
+ ?line testSetTypeRefCho:main(Rules).
+
+
+
+testSetTypeRefPrim(suite) -> [];
+testSetTypeRefPrim(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefPrim:compile(Config,?BER,[]),
+ ?line testSetTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefPrim_cases(?BER)),
+
+ ?line testSetTypeRefPrim:compile(Config,?PER,[]),
+ ?line testSetTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefPrim_cases(uper_bin)),
+
+ ?line testSetTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefPrim_cases(?PER).
+
+testSetTypeRefPrim_cases(Rules) ->
+ ?line testSetTypeRefPrim:main(Rules).
+
+
+
+testSetTypeRefSeq(suite) -> [];
+testSetTypeRefSeq(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefSeq:compile(Config,?BER,[]),
+ ?line testSetTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefSeq_cases(?BER)),
+
+ ?line testSetTypeRefSeq:compile(Config,?PER,[]),
+ ?line testSetTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefSeq_cases(uper_bin)),
+
+ ?line testSetTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefSeq_cases(?PER).
+
+testSetTypeRefSeq_cases(Rules) ->
+ ?line testSetTypeRefSeq:main(Rules).
+
+
+
+testSetTypeRefSet(suite) -> [];
+testSetTypeRefSet(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefSet:compile(Config,?BER,[]),
+ ?line testSetTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefSet_cases(?BER)),
+
+ ?line testSetTypeRefSet:compile(Config,?PER,[]),
+ ?line testSetTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefSet_cases(uper_bin)),
+
+ ?line testSetTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefSet_cases(?PER).
+
+testSetTypeRefSet_cases(Rules) ->
+ ?line testSetTypeRefSet:main(Rules).
+
+
+
+testSetOf(suite) -> [];
+testSetOf(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOf:compile(Config,?BER,[]),
+ ?line testSetOf_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOf:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOf_cases(?BER)),
+
+ ?line testSetOf:compile(Config,?PER,[]),
+ ?line testSetOf_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOf:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOf_cases(?PER)),
+
+ ?line ?uper_bin(testSetOf:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOf_cases(uper_bin)),
+
+ ?line testSetOf:compile(Config,?PER,[optimize]),
+ ?line testSetOf_cases(?PER).
+
+testSetOf_cases(Rules) ->
+ ?line testSetOf:main(Rules).
+
+
+
+testSetOfCho(suite) -> [];
+testSetOfCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOfCho:compile(Config,?BER,[]),
+ ?line testSetOfCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOfCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfCho_cases(?BER)),
+
+ ?line testSetOfCho:compile(Config,?PER,[]),
+ ?line testSetOfCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOfCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfCho_cases(?PER)),
+
+ ?line ?uper_bin(testSetOfCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfCho_cases(uper_bin)),
+
+ ?line testSetOfCho:compile(Config,?PER,[optimize]),
+ ?line testSetOfCho_cases(?PER).
+
+testSetOfCho_cases(Rules) ->
+ ?line testSetOfCho:main(Rules).
+
+
+testSetOfExternal(suite) -> [];
+testSetOfExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetOfExternal:compile(Config,?BER,[]),
+ ?line testSetOfExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetOfExternal:compile(Config,?PER,[]),
+ ?line testSetOfExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfExternal_cases(?PER).
+
+testSetOfExternal_cases(Rules) ->
+ ?line testSetOfExternal:main(Rules).
+
+
+
+
+testSetOfTag(suite) -> [];
+testSetOfTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetOfTag:compile(Config,?BER,[]),
+ ?line testSetOfTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetOfTag:compile(Config,?PER,[]),
+ ?line testSetOfTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfTag:compile(Config,?PER,[optimize]),
+ ?line testSetOfTag_cases(?PER).
+
+testSetOfTag_cases(Rules) ->
+ ?line testSetOfTag:main(Rules).
+
+
+c_syntax(suite) -> [];
+c_syntax(Config) ->
+ ?line DataDir% ?line testExternal:compile(Config,?PER),
+% ?line testPrimExternal:compile(Config,?PER),
+% ?line testPrimExternal_cases(?PER).
+ = ?config(data_dir,Config),
+ ?line _TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line {error,_R1} = asn1ct:compile(filename:join(DataDir,"Syntax")),
+ ?line {error,_R2} = asn1ct:compile(filename:join(DataDir,"BadTypeEnding")),
+ ?line {error,_R3} = asn1ct:compile(filename:join(DataDir,
+ "BadValueAssignment1")),
+ ?line {error,_R4} = asn1ct:compile(filename:join(DataDir,
+ "BadValueAssignment2")),
+ ?line {error,_R5} = asn1ct:compile(filename:join(DataDir,
+ "BadValueSet")),
+ ?line {error,_R6} = asn1ct:compile(filename:join(DataDir,
+ "ChoiceBadExtension")),
+ ?line {error,_R7} = asn1ct:compile(filename:join(DataDir,
+ "EnumerationBadExtension")),
+ ?line {error,_R8} = asn1ct:compile(filename:join(DataDir,
+ "Example")),
+ ?line {error,_R9} = asn1ct:compile(filename:join(DataDir,
+ "Export1")),
+ ?line {error,_R10} = asn1ct:compile(filename:join(DataDir,
+ "MissingEnd")),
+ ?line {error,_R11} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComma")),
+ ?line {error,_R12} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComponentName")),
+ ?line {error,_R13} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComponentType")),
+ ?line {error,_R14} = asn1ct:compile(filename:join(DataDir,
+ "SeqBadComma")).
+
+
+c_string_per(suite) -> [];
+c_string_per(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"String"),[?PER,{outdir,TempDir}]).
+
+c_string_ber(suite) -> [];
+c_string_ber(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"String"),[?BER,{outdir,TempDir}]).
+
+
+c_implicit_before_choice(suite) -> [];
+c_implicit_before_choice(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line {error,_R2} = asn1ct:compile(filename:join(DataDir,"CCSNARG3"),[?BER,{outdir,TempDir}]).
+
+parse(suite) -> [];
+parse(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ M1 = test_modules(),
+% M2 = parse_modules(),
+ ?line ok = parse1(M1,DataDir,OutDir).
+
+parse1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[abs,{outdir,OutDir}]),
+ parse1(T,DataDir,OutDir);
+parse1([],_,_) ->
+ ok.
+
+per(suite) -> [];
+per(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = per1(per_modules(),DataDir,OutDir),
+ ?line ?per_bit_opt(per1_bit_opt(per_modules(),DataDir,OutDir)),
+ ?line ok = per1_opt(per_modules(),DataDir,OutDir).
+
+
+per1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1(T,DataDir,OutDir);
+per1([],_,_) ->
+ ok.
+
+per1_bit_opt([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,optimize,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1_bit_opt(T,DataDir,OutDir);
+per1_bit_opt([],_,_) ->
+ ok.
+
+per1_opt([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,optimized,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1_opt(T,DataDir,OutDir);
+per1_opt([],_,_) ->
+ ok.
+
+
+ber_choiceinseq(suite) ->[];
+ber_choiceinseq(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"ChoiceInSeq"),[?BER,{outdir,OutDir}]).
+
+ber_optional(suite) ->[];
+ber_optional(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"SOpttest"),[?BER,{outdir,OutDir}]),
+ ?line V = {'S',{'A',10,asn1_NOVALUE,asn1_NOVALUE},
+ {'B',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ {'C',asn1_NOVALUE,111,asn1_NOVALUE}},
+ ?line {ok,B} = asn1_wrapper:encode('SOpttest','S',V),
+ ?line Bytes = lists:flatten(B),
+ ?line V2 = asn1_wrapper:decode('SOpttest','S',Bytes),
+ ?line ok = eq(V,element(2,V2)).
+
+ber_optional_keyed_list(suite) ->[];
+ber_optional_keyed_list(Config) ->
+ case ?BER of
+ ber_bin_v2 -> ok;
+ _ ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"SOpttest"),
+ [?BER,keyed_list,{outdir,OutDir}]),
+ ?line Vrecord = {'S',{'A',10,asn1_NOVALUE,asn1_NOVALUE},
+ {'B',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ {'C',asn1_NOVALUE,111,asn1_NOVALUE}},
+ ?line V = [ {a,[{scriptKey,10}]},
+ {b,[]},
+ {c,[{callingPartysCategory,111}]} ],
+ ?line {ok,B} = asn1_wrapper:encode('SOpttest','S',V),
+ ?line Bytes = lists:flatten(B),
+ ?line V2 = asn1_wrapper:decode('SOpttest','S',Bytes),
+ ?line ok = eq(Vrecord,element(2,V2))
+ end.
+
+
+eq(V,V) ->
+ ok.
+
+
+ber_other(suite) ->[];
+ber_other(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = ber1(ber_modules(),DataDir,OutDir).
+
+
+ber1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?BER,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ ber1(T,DataDir,OutDir);
+ber1([],_,_) ->
+ ok.
+
+default_per(suite) ->[];
+default_per(Config) ->
+ default1(?PER,Config,[]).
+
+default_per_opt(suite) -> [];
+default_per_opt(Config) ->
+ ?per_bit_opt(default1(?PER,Config,[optimize])),
+ default1(?PER,Config,[optimize]).
+
+default_ber(suite) ->[];
+default_ber(Config) ->
+ default1(?BER,Config,[]).
+
+default1(Rule,Config,Options) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "Def",[Rule,{outdir,OutDir}]++Options),
+ ?line {ok,Bytes1} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true,
+ bool1 = true,
+ bool2 = true,
+ bool3 = true}),
+ ?line {ok,{'Def1',true,true,true,true}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes1)),
+
+ ?line {ok,Bytes2} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true}),
+ ?line {ok,{'Def1',true,false,false,false}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes2)),
+
+ ?line {ok,Bytes3} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true,bool2=false}),
+ ?line {ok,{'Def1',true,false,false,false}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes3)).
+
+
+value_test(suite) ->[];
+value_test(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "ObjIdValues",[?BER,{outdir,OutDir}]),
+ ?line {ok,_} = asn1_wrapper:encode('ObjIdValues','ObjIdType','ObjIdValues':'mobileDomainId'()),
+ ?line ok = asn1ct:compile(DataDir ++ "ObjIdValues",[?PER,{outdir,OutDir}]),
+ ?line {ok,_} = asn1_wrapper:encode('ObjIdValues','ObjIdType','ObjIdValues':'mobileDomainId'()),
+ ?line ok = test_bad_values:tests(Config),
+ ok.
+
+
+constructed(suite) ->
+ [];
+constructed(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "Constructed",[?BER,{outdir,OutDir}]),
+ ?line {ok,B} = asn1_wrapper:encode('Constructed','S',{'S',false}),
+ ?line [40,3,1,1,0] = lists:flatten(B),
+ ?line {ok,B1} = asn1_wrapper:encode('Constructed','S2',{'S2',false}),
+ ?line [40,5,48,3,1,1,0] = lists:flatten(B1),
+ ?line {ok,B2} = asn1_wrapper:encode('Constructed','I',10),
+ ?line [136,1,10] = lists:flatten(B2),
+ ok.
+
+ber_decode_error(suite) -> [];
+ber_decode_error(Config) ->
+ ?line ok = ber_decode_error:compile(Config,?BER,[]),
+ ?line ok = ber_decode_error:run([]),
+
+ ?line ok = ?ber_driver(?BER,ber_decode_error:compile(Config,?BER,[driver])),
+ ?line ok = ?ber_driver(?BER,ber_decode_error:run([driver])),
+ ok.
+
+h323test(suite) ->
+ [];
+h323test(Config) ->
+ ?line ok = h323test:compile(Config,?PER,[]),
+ ?line ok = h323test:run(?PER),
+ ?line ?per_bit_opt(h323test:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(h323test:run(?PER)),
+ ?line ?uper_bin(h323test:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(h323test:run(uper_bin)),
+ ?line ok = h323test:compile(Config,?PER,[optimize]),
+ ?line ok = h323test:run(?PER),
+ ok.
+
+per_GeneralString(suite) ->
+ [];
+per_GeneralString(Config) ->
+ case erlang:module_loaded('MULTIMEDIA-SYSTEM-CONTROL') of
+ true ->
+ ok;
+ false ->
+ h323test:compile(Config,?PER,[])
+ end,
+ UI = [109,64,1,57],
+ ?line {ok,_V} = asn1_wrapper:decode('MULTIMEDIA-SYSTEM-CONTROL',
+ 'MultimediaSystemControlMessage',UI).
+
+per_open_type(suite) ->
+ [];
+per_open_type(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line ok = asn1ct:compile(DataDir ++ "OpenType",[?PER,{outdir,OutDir}]),
+ Stype = {'Stype',10,true},
+ ?line {ok,Bytes} = asn1_wrapper:encode('OpenType','Ot',Stype),
+ ?line {ok,Stype} = asn1_wrapper:decode('OpenType','Ot',Bytes),
+
+ ?line ?per_bit_opt(ok = asn1ct:compile(DataDir ++ "OpenType",
+ [?PER,optimize,{outdir,OutDir}])),
+ ?line ?per_bit_opt({ok,Bytes}=asn1_wrapper:encode('OpenType','Ot',Stype)),
+ ?line ?per_bit_opt({ok,Stype}=asn1_wrapper:decode('OpenType','Ot',Bytes)),
+
+ ?line ?uper_bin(ok = asn1ct:compile(DataDir ++ "OpenType",
+ [uper_bin,{outdir,OutDir}])),
+ ?line ?uper_bin({ok,Bytes}=asn1_wrapper:encode('OpenType','Ot',Stype)),
+ ?line ?uper_bin({ok,Stype}=asn1_wrapper:decode('OpenType','Ot',Bytes)),
+
+ ?line ok = asn1ct:compile(DataDir ++ "OpenType",
+ [?PER,optimize,{outdir,OutDir}]),
+ ?line {ok,Bytes} = asn1_wrapper:encode('OpenType','Ot',Stype),
+ ?line {ok,Stype} = asn1_wrapper:decode('OpenType','Ot',Bytes).
+
+testConstraints(suite) ->
+ [];
+testConstraints(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testConstraints:compile(Config,?BER,[]),
+ ?line testConstraints:int_constraints(?BER),
+
+ ?line ?ber_driver(?BER,testConstraints:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testConstraints:int_constraints(?BER)),
+
+ ?line testConstraints:compile(Config,?PER,[]),
+ ?line testConstraints:int_constraints(?PER),
+ ?line testConstraints:refed_NNL_name(?PER),
+
+ ?line ?per_bit_opt(testConstraints:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testConstraints:int_constraints(?PER)),
+ ?line ?per_bit_opt(testConstraints:refed_NNL_name(?PER)),
+
+ ?line ?uper_bin(testConstraints:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testConstraints:int_constraints(uper_bin)),
+ ?line ?uper_bin(testConstraints:refed_NNL_name(uper_bin)),
+
+ ?line testConstraints:compile(Config,?PER,[optimize]),
+ ?line testConstraints:int_constraints(?PER),
+ ?line testConstraints:refed_NNL_name(?PER).
+
+testSeqIndefinite(suite) -> [];
+testSeqIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqIndefinite:compile(Config,?BER,[]),
+ ?line testSeqIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testSeqIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqIndefinite:main(?BER)).
+
+testSetIndefinite(suite) -> [];
+testSetIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetIndefinite:compile(Config,?BER,[]),
+ ?line testSetIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testSetIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetIndefinite:main(?BER)).
+
+testChoiceIndefinite(suite) -> [];
+testChoiceIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testChoiceIndefinite:compile(Config,?BER,[]),
+ ?line testChoiceIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testChoiceIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoiceIndefinite:main(?BER)).
+
+testInfObjectClass(suite) ->
+ [];
+testInfObjectClass(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testInfObjectClass:compile(Config,?PER,[]),
+ ?line testInfObjectClass:main(?PER),
+ ?line testInfObj:compile(Config,?PER,[]),
+ ?line testInfObj:main(?PER),
+
+ ?line ?per_bit_opt(testInfObjectClass:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testInfObjectClass:main(?PER)),
+ ?line ?per_bit_opt(testInfObj:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testInfObj:main(?PER)),
+
+ ?line ?uper_bin(testInfObjectClass:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testInfObjectClass:main(uper_bin)),
+ ?line ?uper_bin(testInfObj:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testInfObj:main(uper_bin)),
+
+ ?line testInfObjectClass:compile(Config,?PER,[optimize]),
+ ?line testInfObjectClass:main(?PER),
+ ?line testInfObj:compile(Config,?PER,[optimize]),
+ ?line testInfObj:main(?PER),
+
+ ?line testInfObjectClass:compile(Config,?BER,[]),
+ ?line testInfObjectClass:main(?BER),
+ ?line testInfObj:compile(Config,?BER,[]),
+ ?line testInfObj:main(?BER),
+
+ ?line ?ber_driver(?BER,testInfObjectClass:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testInfObjectClass:main(?BER)),
+ ?line ?ber_driver(?BER,testInfObj:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testInfObj:main(?BER)),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?PER,[]),
+
+ ?line ?per_bit_opt(testInfObj:compile_RANAPfiles(Config,?PER,[optimize])),
+
+ ?line ?uper_bin(testInfObj:compile_RANAPfiles(Config,uper_bin,[])),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?PER,[optimize]),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?BER,[]).
+
+testParameterizedInfObj(suite) ->
+ [];
+testParameterizedInfObj(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testParameterizedInfObj:compile(Config,?PER,[]),
+ ?line testParameterizedInfObj:main(?PER),
+
+ ?line ?per_bit_opt(testParameterizedInfObj:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testParameterizedInfObj:main(?PER)),
+
+ ?line ?uper_bin(testParameterizedInfObj:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testParameterizedInfObj:main(uper_bin)),
+
+ ?line testParameterizedInfObj:compile(Config,?PER,[optimize]),
+ ?line testParameterizedInfObj:main(?PER),
+
+ ?line testParameterizedInfObj:compile(Config,?BER,[]),
+ ?line testParameterizedInfObj:main(?BER),
+
+ ?line ?ber_driver(?BER,testParameterizedInfObj:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testParameterizedInfObj:main(?BER)).
+
+testMergeCompile(suite) ->
+ [];
+testMergeCompile(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testMergeCompile:compile(Config,?PER,[]),
+ ?line testMergeCompile:main(?PER),
+ ?line testMergeCompile:mvrasn(?PER),
+
+ ?line ?per_bit_opt(testMergeCompile:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testMergeCompile:main(?PER)),
+ ?line ?per_bit_opt(testMergeCompile:mvrasn(?PER)),
+
+ ?line ?uper_bin(testMergeCompile:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testMergeCompile:main(uper_bin)),
+ ?line ?uper_bin(testMergeCompile:mvrasn(uper_bin)),
+
+ ?line testMergeCompile:compile(Config,?BER,[]),
+ ?line testMergeCompile:main(?BER),
+ ?line testMergeCompile:mvrasn(?BER),
+
+ ?line ?ber_driver(?BER,testMergeCompile:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testMergeCompile:main(?BER)),
+ ?line ?ber_driver(?BER,testMergeCompile:mvrasn(?BER)).
+
+testobj(suite) ->
+ [];
+testobj(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line ok = testRANAP:compile(Config,?PER,[]),
+ ?line ok = testRANAP:testobj(?PER),
+ ?line ok = testParameterizedInfObj:ranap(?PER),
+
+ ?line ?per_bit_opt(ok = testRANAP:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(ok = testRANAP:testobj(?PER)),
+ ?line ?per_bit_opt(ok = testParameterizedInfObj:ranap(?PER)),
+
+ ?line ?uper_bin(ok = testRANAP:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(ok = testRANAP:testobj(uper_bin)),
+ ?line ?uper_bin(ok = testParameterizedInfObj:ranap(uper_bin)),
+
+ ?line ok = testRANAP:compile(Config,?PER,[optimize]),
+ ?line ok = testRANAP:testobj(?PER),
+ ?line ok = testParameterizedInfObj:ranap(?PER),
+
+ ?line ok = testRANAP:compile(Config,?BER,[]),
+ ?line ok = testRANAP:testobj(?BER),
+ ?line ok = testParameterizedInfObj:ranap(?BER),
+
+ ?line ?ber_driver(?BER,testRANAP:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testRANAP:testobj(?BER)),
+ ?line ?ber_driver(?BER,testParameterizedInfObj:ranap(?BER)).
+
+
+testDeepTConstr(suite) ->
+ [];
+testDeepTConstr(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testDeepTConstr:compile(Config,?PER,[]),
+ ?line testDeepTConstr:main(?PER),
+
+ ?line ?per_bit_opt(testDeepTConstr:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDeepTConstr:main(?PER)),
+
+ ?line ?uper_bin(testDeepTConstr:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDeepTConstr:main(uper_bin)),
+
+ ?line testDeepTConstr:compile(Config,?PER,[optimize]),
+ ?line testDeepTConstr:main(?PER),
+
+ ?line testDeepTConstr:compile(Config,?BER,[]),
+ ?line testDeepTConstr:main(?BER),
+
+ ?line ?ber_driver(?BER,testDeepTConstr:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDeepTConstr:main(?BER)).
+
+testInvokeMod(suite) ->
+ [];
+testInvokeMod(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"PrimStrings"),[{outdir,OutDir}]),
+ ?line {ok,_Result1} = 'PrimStrings':encode('Bs1',[1,0,1,0]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"PrimStrings"),[?PER,{outdir,OutDir}]),
+ ?line {ok,_Result2} = 'PrimStrings':encode('Bs1',[1,0,1,0]).
+
+testExport(suite) ->
+ [];
+testExport(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line {error,{asn1,_Reason}} = asn1ct:compile(filename:join(DataDir,"IllegalExport"),[{outdir,OutDir}]).
+
+testImport(suite) ->
+ [];
+testImport(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line _OutDir = ?config(priv_dir,Config),
+ ?line {error,_} = asn1ct:compile(filename:join(DataDir,"ImportsFrom"),[?BER]),
+ ok.
+
+testMegaco(suite) ->
+ [];
+testMegaco(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ io:format("Config: ~p~n",[Config]),
+ ?line {ok,ModuleName1,ModuleName2} = testMegaco:compile(Config,?BER,[]),
+ ?line ok = testMegaco:main(ModuleName1,Config),
+ ?line ok = testMegaco:main(ModuleName2,Config),
+
+ case ?BER of
+ ber_bin_v2 ->
+ ?line {ok,ModuleName3,ModuleName4} = testMegaco:compile(Config,?BER,[driver]),
+ ?line ok = testMegaco:main(ModuleName3,Config),
+ ?line ok = testMegaco:main(ModuleName4,Config);
+ _-> ok
+ end,
+
+ ?line {ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,?PER,[]),
+ ?line ok = testMegaco:main(ModuleName5,Config),
+ ?line ok = testMegaco:main(ModuleName6,Config),
+
+ ?line ?per_bit_opt({ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(ok = testMegaco:main(ModuleName5,Config)),
+ ?line ?per_bit_opt(ok = testMegaco:main(ModuleName6,Config)),
+
+ ?line ?uper_bin({ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(ok = testMegaco:main(ModuleName5,Config)),
+ ?line ?uper_bin(ok = testMegaco:main(ModuleName6,Config)),
+
+ ?line {ok,ModuleName7,ModuleName8} = testMegaco:compile(Config,?PER,[optimize]),
+ ?line ok = testMegaco:main(ModuleName7,Config),
+ ?line ok = testMegaco:main(ModuleName8,Config).
+
+
+testMvrasn6(suite) -> [];
+testMvrasn6(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testMvrasn6:compile(Config,?BER),
+ ?line testMvrasn6:main().
+
+testContextSwitchingTypes(suite) -> [];
+testContextSwitchingTypes(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testContextSwitchingTypes:compile(Config,?BER,[]),
+ ?line testContextSwitchingTypes:test(),
+
+ ?line ?ber_driver(?BER,testContextSwitchingTypes:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testContextSwitchingTypes:test()),
+
+ ?line testContextSwitchingTypes:compile(Config,?PER,[]),
+ ?line testContextSwitchingTypes:test(),
+
+ ?line ?per_bit_opt(testContextSwitchingTypes:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testContextSwitchingTypes:test()),
+
+ ?line ?uper_bin(testContextSwitchingTypes:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testContextSwitchingTypes:test()),
+
+ ?line testContextSwitchingTypes:compile(Config,?PER,[optimize]),
+ ?line testContextSwitchingTypes:test().
+
+testTypeValueNotation(suite) -> [];
+testTypeValueNotation(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ case ?BER of
+ Ber when Ber == ber; Ber == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?BER,[]),
+ ?line testTypeValueNotation:main(?BER,dummy);
+ _ ->
+ ok
+ end,
+
+ ?line ?ber_driver(?BER,testTypeValueNotation:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testTypeValueNotation:main(?BER,optimize)),
+
+ case ?BER of
+ Ber2 when Ber2 == ber; Ber2 == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?PER,[]),
+ ?line testTypeValueNotation:main(?PER,dummy);
+ _ ->
+ ok
+ end,
+
+ ?line ?per_bit_opt(testTypeValueNotation:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testTypeValueNotation:main(?PER,optimize)),
+
+ ?line ?uper_bin(testTypeValueNotation:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testTypeValueNotation:main(uper_bin,optimize)),
+ case ?BER of
+ Ber3 when Ber3 == ber; Ber3 == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?PER,[optimize]),
+ ?line testTypeValueNotation:main(?PER,optimize);
+ _ ->
+ ok
+ end.
+
+testOpenTypeImplicitTag(suite) -> [];
+testOpenTypeImplicitTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?BER,[]),
+ ?line testOpenTypeImplicitTag:main(?BER),
+
+ ?line ?ber_driver(?BER,testOpenTypeImplicitTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testOpenTypeImplicitTag:main(?BER)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?PER,[]),
+ ?line testOpenTypeImplicitTag:main(?PER),
+
+ ?line ?per_bit_opt(testOpenTypeImplicitTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testOpenTypeImplicitTag:main(?PER)),
+
+ ?line ?uper_bin(testOpenTypeImplicitTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testOpenTypeImplicitTag:main(uper_bin)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?PER,[optimize]),
+ ?line testOpenTypeImplicitTag:main(?PER).
+
+duplicate_tags(suite) -> [];
+duplicate_tags(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ {error,{asn1,[{error,{type,_,_,'SeqOpt1Imp',{asn1,{duplicates_of_the_tags,_}}}}]}} =
+ asn1ct:compile(filename:join(DataDir,"SeqOptional2"),[abs]),
+ ok.
+
+rtUI(suite) -> [];
+rtUI(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Prim"),[?BER]),
+ ?line {ok,_} = asn1rt:info('Prim'),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Prim"),[?PER]),
+ ?line {ok,_} = asn1rt:info('Prim'),
+
+ ?line ok = asn1rt:load_driver(),
+ ?line ok = asn1rt:load_driver(),
+ ?line ok = asn1rt:unload_driver().
+
+testROSE(suite) -> [];
+testROSE(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testROSE:compile(Config,?BER,[]),
+
+ ?line testROSE:compile(Config,?PER,[]),
+ ?line ?per_bit_opt(testROSE:compile(Config,?PER,[optimize])),
+ ?line ?uper_bin(testROSE:compile(Config,uper_bin,[])),
+ ?line testROSE:compile(Config,?PER,[optimize]).
+
+testINSTANCE_OF(suite) -> [];
+testINSTANCE_OF(Config) ->
+ ?line testINSTANCE_OF:compile(Config,?BER,[]),
+ ?line testINSTANCE_OF:main(?BER),
+
+ ?line ?ber_driver(?BER,testINSTANCE_OF:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testINSTANCE_OF:main(?BER)),
+
+ ?line testINSTANCE_OF:compile(Config,?PER,[]),
+ ?line testINSTANCE_OF:main(?PER),
+
+ ?line ?per_bit_opt(testINSTANCE_OF:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testINSTANCE_OF:main(?PER)),
+
+ ?line ?uper_bin(testINSTANCE_OF:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testINSTANCE_OF:main(uper_bin)),
+
+ ?line testINSTANCE_OF:compile(Config,?PER,[optimize]),
+ ?line testINSTANCE_OF:main(?PER).
+
+testTCAP(suite) -> [];
+testTCAP(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testTCAP:compile(Config,?BER,[]),
+ ?line testTCAP:test(?BER,Config),
+
+ ?line ?ber_driver(?BER,testTCAP:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testTCAP:test(?BER,Config)),
+
+ ?line ?ber_driver(?BER,testTCAP:compile_asn1config(Config,?BER,[asn1config])),
+ ?line ?ber_driver(?BER,testTCAP:test_asn1config()).
+
+testDER(suite) ->[];
+testDER(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testDER:compile(Config,?BER,[]),
+ ?line testDER:test(),
+
+ ?line ?ber_driver(?BER,testDER:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDER:test()),
+
+ ?line testParamBasic:compile_der(Config,?BER),
+ ?line testParamBasic_cases(der),
+
+
+ ?line testSeqSetDefaultVal:compile(Config,?BER),
+ ?line testSeqSetDefaultVal_cases(?BER).
+
+testSeqSetDefaultVal_cases(?BER) ->
+ ?line testSeqSetDefaultVal:main(?BER).
+
+
+specialized_decodes(suite) -> [];
+specialized_decodes(Config) ->
+ ?line test_partial_incomplete_decode:compile(Config,?BER,[optimize]),
+ ?line test_partial_incomplete_decode:test(?BER,Config),
+ ?line test_selective_decode:test(?BER,Config).
+
+special_decode_performance(suite) ->[];
+special_decode_performance(Config) ->
+ ?line ?ber_driver(?BER,test_special_decode_performance:compile(Config,?BER)),
+ ?line ?ber_driver(?BER,test_special_decode_performance:go(all)).
+
+
+test_driver_load(suite) -> [];
+test_driver_load(Config) ->
+ ?line test_driver_load:compile(Config,?PER),
+ ?line test_driver_load:test(?PER,5).
+
+test_ParamTypeInfObj(suite) -> [];
+test_ParamTypeInfObj(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"IN-CS-1-Datatypes"),[ber_bin]).
+
+test_WS_ParamClass(suite) -> [];
+test_WS_ParamClass(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"InformationFramework"),
+ [ber_bin]).
+
+test_Defed_ObjectIdentifier(suite) -> [];
+test_Defed_ObjectIdentifier(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"UsefulDefinitions"),
+ [ber_bin]).
+
+testSelectionType(suite) -> [];
+testSelectionType(Config) ->
+
+ ?line ok = testSelectionTypes:compile(Config,?BER,[]),
+ ?line {ok,_} = testSelectionTypes:test(),
+
+ ?line ok = testSelectionTypes:compile(Config,?PER,[]),
+ ?line {ok,_} = testSelectionTypes:test().
+
+testSSLspecs(suite) -> [];
+testSSLspecs(Config) ->
+
+ ?line ok = testSSLspecs:compile(Config,?BER,
+ [optimize,compact_bit_string,der]),
+ ?line testSSLspecs:run(?BER),
+
+ case code:which(asn1ct) of
+ cover_compiled ->
+ ok;
+ _ ->
+ ?line ok = testSSLspecs:compile_inline(Config,?BER),
+ ?line ok = testSSLspecs:run_inline(?BER)
+ end.
+
+testNortel(suite) -> [];
+testNortel(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[?BER]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?BER,optimize]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?BER,optimize,driver]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[?PER]),
+ ?line ?per_bit_opt(ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?PER,optimize])),
+ ?line ?uper_bin(ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[uper_bin])),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?PER,optimize]).
+test_undecoded_rest(suite) -> [];
+test_undecoded_rest(Config) ->
+
+ ?line ok = test_undecoded_rest:compile(Config,?BER,[]),
+ ?line ok = test_undecoded_rest:test([]),
+
+ ?line ok = test_undecoded_rest:compile(Config,?BER,[undec_rest]),
+ ?line ok = test_undecoded_rest:test(undec_rest),
+
+ ?line ok = test_undecoded_rest:compile(Config,?PER,[]),
+ ?line ok = test_undecoded_rest:test([]),
+
+ ?line ?per_bit_opt(ok = test_undecoded_rest:compile(Config,?PER,[optimize,undec_rest])),
+ ?line ?per_bit_opt(ok = test_undecoded_rest:test(undec_rest)),
+
+ ?line ?uper_bin(ok = test_undecoded_rest:compile(Config,uper_bin,[undec_rest])),
+ ?line ?uper_bin(ok = test_undecoded_rest:test(undec_rest)),
+
+ ?line ok = test_undecoded_rest:compile(Config,?PER,[undec_rest]),
+ ?line ok = test_undecoded_rest:test(undec_rest).
+
+test_inline(suite) -> [];
+test_inline(Config) ->
+ case code:which(asn1ct) of
+ cover_compiled ->
+ {skip,"Not runnable when cover compiled"};
+ _ ->
+ ?line ok=test_inline:compile(Config,?BER,[]),
+ ?line test_inline:main(?BER),
+ ?line test_inline:inline1(Config,?BER,[]),
+ ?line test_inline:performance2()
+ end.
+
+%test_inline_prf(suite) -> [];
+%test_inline_prf(Config) ->
+% ?line test_inline:performance(Config).
+
+testTcapsystem(suite) -> [];
+testTcapsystem(Config) ->
+ ?line ok=testTcapsystem:compile(Config,?BER,[]).
+
+testNBAPsystem(suite) -> [];
+testNBAPsystem(Config) ->
+ ?line ok=testNBAPsystem:compile(Config,?PER,?per_optimize(?BER)),
+ ?line ok=testNBAPsystem:test(?PER,Config).
+
+test_compile_options(suite) -> [];
+test_compile_options(Config) ->
+ case code:which(asn1ct) of
+ cover_compiled ->
+ {skip,"Not runnable when cover compiled"};
+ _ ->
+ ?line ok = test_compile_options:wrong_path(Config),
+ ?line ok = test_compile_options:path(Config),
+ ?line ok = test_compile_options:noobj(Config),
+ ?line ok = test_compile_options:record_name_prefix(Config),
+ ?line ok = test_compile_options:verbose(Config)
+ end.
+testDoubleEllipses(suite) -> [];
+testDoubleEllipses(Config) ->
+ ?line testDoubleEllipses:compile(Config,?BER,[]),
+ ?line testDoubleEllipses:main(?BER),
+ ?line ?ber_driver(?BER,testDoubleEllipses:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDoubleEllipses:main(?BER)),
+ ?line ?per_bit_opt(testDoubleEllipses:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDoubleEllipses:main(?PER)),
+ ?line ?uper_bin(testDoubleEllipses:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDoubleEllipses:main(uper_bin)),
+ ?line testDoubleEllipses:compile(Config,?PER,?per_optimize(?BER)),
+ ?line testDoubleEllipses:main(?PER).
+
+test_modified_x420(suite) -> [];
+test_modified_x420(Config) ->
+ ?line test_modified_x420:compile(Config),
+ ?line test_modified_x420:test_io(Config).
+
+testX420(suite) -> [];
+testX420(Config) ->
+ ?line testX420:compile(?BER,[der],Config),
+ ?line ok = testX420:ticket7759(?BER,Config),
+ ?line testX420:compile(?PER,[],Config).
+
+test_x691(suite) -> [];
+test_x691(Config) ->
+ case ?PER of
+ per ->
+ ?line ok = test_x691:compile(Config,uper_bin,[]),
+ ?line true = test_x691:cases(uper_bin,unaligned),
+ ?line ok = test_x691:compile(Config,?PER,[]),
+ ?line true = test_x691:cases(?PER,aligned),
+%% ?line ok = asn1_test_lib:ticket_7678(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7708(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7763(Config);
+ _ ->
+ ?line ok = test_x691:compile(Config,?PER,?per_optimize(?BER)),
+ ?line true = test_x691:cases(?PER,aligned)
+ end.
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[compact_bit_string]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[optimize]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[optimize,compact_bit_string]).
+
+
+ticket_6143(suite) -> [];
+ticket_6143(Config) ->
+ ?line ok = test_compile_options:ticket_6143(Config).
+
+testExtensionAdditionGroup(suite) -> [];
+testExtensionAdditionGroup(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line PrivDir = ?config(priv_dir,Config),
+ ?line Path = code:get_path(),
+ ?line code:add_patha(PrivDir),
+ DoIt = fun(Erule) ->
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Extension-Addition-Group"),[Erule,{outdir,PrivDir}]),
+ ?line {ok,_M} = compile:file(filename:join(DataDir,"extensionAdditionGroup"),[{i,PrivDir},{outdir,PrivDir},debug_info]),
+ ?line ok = extensionAdditionGroup:run(Erule)
+ end,
+ ?line [DoIt(Rule)|| Rule <- [per_bin,uper_bin,ber_bin]],
+ ?line code:set_path(Path).
+
+
+
+% parse_modules() ->
+% ["ImportsFrom"].
+
+per_modules() ->
+ [X || X <- test_modules()].
+ber_modules() ->
+ [X || X <- test_modules(),
+ X =/= "CommonDataTypes",
+ X =/= "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath",
+ X =/= "H323-MESSAGES",
+ X =/= "H235-SECURITY-MESSAGES",
+ X =/= "MULTIMEDIA-SYSTEM-CONTROL"].
+test_modules() ->
+ _Modules = [
+ "BitStr",
+ "CommonDataTypes",
+ "Constraints",
+ "ContextSwitchingTypes",
+ "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath",
+ "Enum",
+ "From",
+ "H235-SECURITY-MESSAGES",
+ "H323-MESSAGES",
+ %%"MULTIMEDIA-SYSTEM-CONTROL", recursive type , problem for asn1ct:value
+ "Import",
+ "Int",
+ "MAP-commonDataTypes",
+% ambigous tags "MAP-insertSubscriberData-def",
+ "Null",
+ "Octetstr",
+ "One",
+ "P-Record",
+ "P",
+% "PDUs",
+ "Person",
+ "PrimStrings",
+ "Real",
+ "XSeq",
+ "XSeqOf",
+ "XSet",
+ "XSetOf",
+ "String",
+ "SwCDR",
+% "Syntax",
+ "Time"
+% ANY "Tst",
+% "Two",
+% errors that should be detected "UndefType"
+] ++
+ [
+ "SeqSetLib", % must be compiled before Seq and Set
+ "Seq",
+ "Set",
+ "SetOf",
+ "SeqOf",
+ "Prim",
+ "Cho",
+ "Def",
+ "Opt",
+ "ELDAPv3",
+ "LDAP"
+ ].
+
+
+common() ->
+[].
+
+particular() ->
+[].
diff --git a/lib/asn1/test/asn1_bin_v2_SUITE.erl b/lib/asn1/test/asn1_bin_v2_SUITE.erl
new file mode 100644
index 0000000000..2273ca9918
--- /dev/null
+++ b/lib/asn1/test/asn1_bin_v2_SUITE.erl
@@ -0,0 +1,2474 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%% Purpose : Test suite for the ASN.1 application
+
+-module(asn1_bin_v2_SUITE).
+-define(PER,'per_bin').
+-define(BER,'ber_bin_v2').
+-define(ber_driver(Erule,Func),
+ case Erule of
+ ber_bin_v2 ->
+ Func;
+ _ -> ok
+ end).
+-define(per_optimize(Erule),
+ case Erule of
+ ber_bin_v2 ->[optimize];
+ _ -> []
+ end).
+-define(per_bit_opt(FuncCall),
+ case ?BER of
+ ber_bin_v2 -> FuncCall;
+% _ -> {skip,"only for bit optimized per_bin"}
+ _ -> ok
+ end).
+-define(uper_bin(FuncCall),
+ case ?PER of
+ per -> FuncCall;
+ _ -> ok
+ end).
+
+-compile(export_all).
+%%-export([Function/Arity, ...]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+%% records used by test-case default
+-record('Def1',{
+bool0, bool1 = asn1_DEFAULT, bool2 = asn1_DEFAULT, bool3 = asn1_DEFAULT}).
+
+%-record('Def2',{
+%bool10, bool11 = asn1_DEFAULT, bool12 = asn1_DEFAULT, bool13}).
+
+%-record('Def3',{
+%bool30 = asn1_DEFAULT, bool31 = asn1_DEFAULT, bool32 = asn1_DEFAULT, bool33 = asn1_DEFAULT}).
+
+
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, compile}, parse, default_per, default_ber,
+ default_per_opt, per, {group, ber}, testPrim,
+ testPrimStrings, testPrimExternal, testChoPrim,
+ testChoExtension, testChoExternal, testChoOptional,
+ testChoOptionalImplicitTag, testChoRecursive,
+ testChoTypeRefCho, testChoTypeRefPrim,
+ testChoTypeRefSeq, testChoTypeRefSet, testDef, testOpt,
+ testSeqDefault, testSeqExtension, testSeqExternal,
+ testSeqOptional, testSeqPrim, testSeqTag,
+ testSeqTypeRefCho, testSeqTypeRefPrim,
+ testSeqTypeRefSeq, testSeqTypeRefSet, testSeqOf,
+ testSeqOfIndefinite, testSeqOfCho, testSeqOfExternal,
+ testSetDefault, testSetExtension,
+ testExtensionAdditionGroup, testSetExternal,
+ testSeqOfTag, testSetOptional, testSetPrim, testSetTag,
+ testSetTypeRefCho, testSetTypeRefPrim,
+ testSetTypeRefSeq, testSetTypeRefSet, testSetOf,
+ testSetOfCho, testSetOfExternal, testSetOfTag,
+ testEnumExt, value_test, testSeq2738, constructed,
+ ber_decode_error, h323test, testSeqIndefinite,
+ testSetIndefinite, testChoiceIndefinite,
+ per_GeneralString, per_open_type, testInfObjectClass,
+ testParameterizedInfObj, testMergeCompile, testobj,
+ testDeepTConstr, testConstraints, testInvokeMod,
+ testExport, testImport, testCompactBitString,
+ testMegaco, testParamBasic, testMvrasn6,
+ testContextSwitchingTypes, testTypeValueNotation,
+ testOpenTypeImplicitTag, duplicate_tags, rtUI, testROSE,
+ testINSTANCE_OF, testTCAP, testDER, specialized_decodes,
+ special_decode_performance, test_driver_load,
+ test_ParamTypeInfObj, test_WS_ParamClass,
+ test_Defed_ObjectIdentifier, testSelectionType,
+ testSSLspecs, testNortel, test_undecoded_rest,
+ test_inline, testTcapsystem, testNBAPsystem,
+ test_compile_options, testDoubleEllipses,
+ test_modified_x420, testX420, test_x691, ticket_6143,
+ testExtensionAdditionGroup] ++ common() ++ particular().
+
+groups() ->
+ [{option_tests, [],
+ [test_compile_options, ticket_6143]},
+ {infobj, [],
+ [testInfObjectClass, testParameterizedInfObj,
+ testMergeCompile, testobj, testDeepTConstr]},
+ {performance, [],
+ [testTimer_ber, testTimer_ber_opt_driver, testTimer_per,
+ testTimer_per_opt, testTimer_uper_bin]},
+ {bugs, [],
+ [test_ParamTypeInfObj, test_WS_ParamClass,
+ test_Defed_ObjectIdentifier]},
+ {compile, [],
+ [c_syntax, c_string_per, c_string_ber,
+ c_implicit_before_choice]},
+ {ber, [],
+ [ber_choiceinseq, ber_optional, ber_optional_keyed_list,
+ ber_other]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%all(suite) -> [test_inline,testNBAPsystem,test_compile_options,ticket_6143].
+
+init_per_testcase(Func,Config) ->
+ %%?line test_server:format("Func: ~p~n",[Func]),
+ ?line {ok, _} = file:read_file_info(filename:join([?config(priv_dir,Config)])),
+ ?line code:add_patha(?config(priv_dir,Config)),
+ Dog=
+ case Func of
+ testX420 ->
+ test_server:timetrap({minutes,60}); % 60 minutes
+ _ ->
+ test_server:timetrap({minutes,30}) % 60 minutes
+ end,
+ %% Dog=test_server:timetrap(1800000), % 30 minutes
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Func,Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog).
+
+
+testPrim(suite) -> [];
+testPrim(Config) ->
+ ?line testPrim:compile(Config,?BER,[]),
+ ?line testPrim_cases(?BER),
+ ?line ?ber_driver(?BER,testPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrim_cases(?BER)),
+ ?line testPrim:compile(Config,?PER,[]),
+ ?line testPrim_cases(?PER),
+ ?line ?per_bit_opt(testPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrim_cases(?PER)),
+ ?line ?uper_bin(testPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrim_cases(uper_bin)),
+ ?line testPrim:compile(Config,?PER,[optimize]),
+ ?line testPrim_cases(?PER).
+
+testPrim_cases(Rules) ->
+ ?line testPrim:bool(Rules),
+ ?line testPrim:int(Rules),
+ ?line testPrim:enum(Rules),
+ ?line testPrim:obj_id(Rules),
+ ?line testPrim:rel_oid(Rules),
+ ?line testPrim:null(Rules),
+ ?line testPrim:real(Rules).
+
+
+testCompactBitString(suite) -> [];
+testCompactBitString(Config) ->
+
+ ?line testCompactBitString:compile(Config,?BER,[compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?BER),
+
+ ?line ?ber_driver(?BER,testCompactBitString:compile(Config,?BER,[compact_bit_string,driver])),
+ ?line ?ber_driver(?BER,testCompactBitString:compact_bit_string(?BER)),
+
+ ?line testCompactBitString:compile(Config,?PER,[compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?PER),
+ ?line testCompactBitString:bit_string_unnamed(?PER),
+
+ ?line ?per_bit_opt(testCompactBitString:compile(Config,?PER,
+ [compact_bit_string,optimize])),
+ ?line ?per_bit_opt(testCompactBitString:compact_bit_string(?PER)),
+ ?line ?per_bit_opt(testCompactBitString:bit_string_unnamed(?PER)),
+ ?line ?per_bit_opt(testCompactBitString:ticket_7734(?PER)),
+
+ ?line ?uper_bin(testCompactBitString:compile(Config,uper_bin,
+ [compact_bit_string])),
+ ?line ?uper_bin(testCompactBitString:compact_bit_string(uper_bin)),
+ ?line ?uper_bin(testCompactBitString:bit_string_unnamed(uper_bin)),
+
+ ?line testCompactBitString:compile(Config,?PER,[optimize,compact_bit_string]),
+ ?line testCompactBitString:compact_bit_string(?PER),
+ ?line testCompactBitString:bit_string_unnamed(?PER),
+
+ ?line testCompactBitString:otp_4869(?PER).
+
+
+testPrimStrings(suite) -> [];
+testPrimStrings(Config) ->
+
+ ?line testPrimStrings:compile(Config,?BER,[]),
+ ?line testPrimStrings_cases(?BER),
+ ?line testPrimStrings:more_strings(?BER), %% these are not implemented in per yet
+ ?line ?ber_driver(?BER,testPrimStrings:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimStrings_cases(?BER)),
+ ?line ?ber_driver(?BER,testPrimStrings:more_strings(?BER)),
+
+ ?line testPrimStrings:compile(Config,?PER,[]),
+ ?line testPrimStrings_cases(?PER),
+
+ ?line ?per_bit_opt(testPrimStrings:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimStrings_cases(?PER)),
+
+ ?line ?uper_bin(testPrimStrings:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimStrings_cases(uper_bin)),
+
+ ?line testPrimStrings:compile(Config,?PER,[optimize]),
+ ?line testPrimStrings_cases(?PER).
+
+testPrimStrings_cases(Rules) ->
+ ?line testPrimStrings:bit_string(Rules),
+ ?line testPrimStrings:bit_string_unnamed(Rules),
+ ?line testPrimStrings:octet_string(Rules),
+ ?line testPrimStrings:numeric_string(Rules),
+ ?line testPrimStrings:other_strings(Rules),
+ ?line testPrimStrings:universal_string(Rules),
+ ?line testPrimStrings:bmp_string(Rules),
+ ?line testPrimStrings:times(Rules),
+ ?line testPrimStrings:utf8_string(Rules).
+
+
+
+testPrimExternal(suite) -> [];
+testPrimExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testPrimExternal:compile(Config,?BER,[]),
+ ?line testPrimExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testPrimExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testPrimExternal:compile(Config,?PER,[]),
+ ?line testPrimExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testPrimExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testPrimExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testPrimExternal:compile(Config,?PER,[optimize]),
+ ?line testPrimExternal_cases(?PER).
+
+testPrimExternal_cases(Rules) ->
+ ?line testPrimExternal:external(Rules).
+
+
+
+
+testChoPrim(suite) -> [];
+testChoPrim(Config) ->
+
+ ?line testChoPrim:compile(Config,?BER,[]),
+ ?line testChoPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoPrim_cases(?BER)),
+
+ ?line testChoPrim:compile(Config,?PER,[]),
+ ?line testChoPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testChoPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoPrim_cases(?PER)),
+
+ ?line ?uper_bin(testChoPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoPrim_cases(uper_bin)),
+
+ ?line testChoPrim:compile(Config,?PER,[optimize]),
+ ?line testChoPrim_cases(?PER).
+
+testChoPrim_cases(Rules) ->
+ ?line testChoPrim:bool(Rules),
+ ?line testChoPrim:int(Rules).
+
+
+
+testChoExtension(suite) -> [];
+testChoExtension(Config) ->
+
+ ?line testChoExtension:compile(Config,?BER,[]),
+ ?line testChoExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExtension_cases(?BER)),
+
+ ?line testChoExtension:compile(Config,?PER,[]),
+ ?line testChoExtension_cases(?PER),
+
+ ?line ?per_bit_opt(testChoExtension:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExtension_cases(?PER)),
+
+ ?line ?uper_bin(testChoExtension:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExtension_cases(uper_bin)),
+
+ ?line testChoExtension:compile(Config,?PER,[optimize]),
+ ?line testChoExtension_cases(?PER).
+
+testChoExtension_cases(Rules) ->
+ ?line testChoExtension:extension(Rules).
+
+
+
+testChoExternal(suite) -> [];
+testChoExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testChoExternal:compile(Config,?BER,[]),
+ ?line testChoExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testChoExternal:compile(Config,?PER,[]),
+ ?line testChoExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testChoExternal:compile(Config,?PER,[optimize]),
+ ?line testChoExternal_cases(?PER).
+
+
+testChoExternal_cases(Rules) ->
+ ?line testChoExternal:external(Rules).
+
+
+
+testChoOptional(suite) -> [];
+testChoOptional(Config) ->
+
+ ?line testChoOptional:compile(Config,?BER,[]),
+ ?line testChoOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoOptional_cases(?BER)),
+
+ ?line testChoOptional:compile(Config,?PER,[]),
+ ?line testChoOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testChoOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoOptional_cases(?PER)),
+
+ ?line ?uper_bin(testChoOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoOptional_cases(uper_bin)),
+
+ ?line testChoOptional:compile(Config,?PER,[optimize]),
+ ?line testChoOptional_cases(?PER).
+
+testChoOptional_cases(Rules) ->
+ ?line testChoOptional:optional(Rules).
+
+testChoOptionalImplicitTag(suite) -> [];
+testChoOptionalImplicitTag(Config) ->
+ %% Only meaningful for ?BER
+ ?line testChoOptionalImplicitTag:compile(Config,?BER),
+ ?line testChoOptionalImplicitTag:optional(?BER).
+
+
+testChoRecursive(suite) -> [];
+testChoRecursive(Config) ->
+
+ ?line testChoRecursive:compile(Config,?BER,[]),
+ ?line testChoRecursive_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoRecursive:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoRecursive_cases(?BER)),
+
+ ?line testChoRecursive:compile(Config,?PER,[]),
+ ?line testChoRecursive_cases(?PER),
+
+ ?line ?per_bit_opt(testChoRecursive:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoRecursive_cases(?PER)),
+
+ ?line ?uper_bin(testChoRecursive:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoRecursive_cases(uper_bin)),
+
+ ?line testChoRecursive:compile(Config,?PER,[optimize]),
+ ?line testChoRecursive_cases(?PER).
+
+testChoRecursive_cases(Rules) ->
+ ?line testChoRecursive:recursive(Rules).
+
+
+
+testChoTypeRefCho(suite) -> [];
+testChoTypeRefCho(Config) ->
+
+ ?line testChoTypeRefCho:compile(Config,?BER,[]),
+ ?line testChoTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefCho_cases(?BER)),
+
+ ?line testChoTypeRefCho:compile(Config,?PER,[]),
+ ?line testChoTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefCho_cases(uper_bin)),
+
+ ?line testChoTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefCho_cases(?PER).
+
+testChoTypeRefCho_cases(Rules) ->
+ ?line testChoTypeRefCho:choice(Rules).
+
+
+
+testChoTypeRefPrim(suite) -> [];
+testChoTypeRefPrim(Config) ->
+
+ ?line testChoTypeRefPrim:compile(Config,?BER,[]),
+ ?line testChoTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefPrim_cases(?BER)),
+
+ ?line testChoTypeRefPrim:compile(Config,?PER,[]),
+ ?line testChoTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefPrim_cases(uper_bin)),
+
+ ?line testChoTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefPrim_cases(?PER).
+
+testChoTypeRefPrim_cases(Rules) ->
+ ?line testChoTypeRefPrim:prim(Rules).
+
+
+
+testChoTypeRefSeq(suite) -> [];
+testChoTypeRefSeq(Config) ->
+
+ ?line testChoTypeRefSeq:compile(Config,?BER,[]),
+ ?line testChoTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefSeq_cases(?BER)),
+
+ ?line testChoTypeRefSeq:compile(Config,?PER,[]),
+ ?line testChoTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefSeq_cases(uper_bin)),
+
+ ?line testChoTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefSeq_cases(?PER).
+
+testChoTypeRefSeq_cases(Rules) ->
+ ?line testChoTypeRefSeq:seq(Rules).
+
+
+
+testChoTypeRefSet(suite) -> [];
+testChoTypeRefSet(Config) ->
+
+ ?line testChoTypeRefSet:compile(Config,?BER,[]),
+ ?line testChoTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testChoTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoTypeRefSet_cases(?BER)),
+
+ ?line testChoTypeRefSet:compile(Config,?PER,[]),
+ ?line testChoTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testChoTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testChoTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testChoTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testChoTypeRefSet_cases(uper_bin)),
+
+ ?line testChoTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testChoTypeRefSet_cases(?PER).
+
+testChoTypeRefSet_cases(Rules) ->
+ ?line testChoTypeRefSet:set(Rules).
+
+
+
+testDef(suite) -> [];
+testDef(Config) ->
+
+ ?line testDef:compile(Config,?BER,[]),
+ ?line testDef_cases(?BER),
+
+ ?line ?ber_driver(?BER,testDef:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDef_cases(?BER)),
+
+ ?line testDef:compile(Config,?PER,[]),
+ ?line testDef_cases(?PER),
+
+ ?line ?per_bit_opt(testDef:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDef_cases(?PER)),
+
+ ?line ?uper_bin(testDef:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDef_cases(uper_bin)),
+
+ ?line testDef:compile(Config,?PER,[optimize]),
+ ?line testDef_cases(?PER).
+
+testDef_cases(Rules) ->
+ ?line testDef:main(Rules).
+
+
+
+testOpt(suite) -> [];
+testOpt(Config) ->
+
+ ?line testOpt:compile(Config,?BER),
+ ?line testOpt_cases(?BER),
+
+ ?line testOpt:compile(Config,?PER),
+ ?line testOpt_cases(?PER).
+
+testOpt_cases(Rules) ->
+ ?line testOpt:main(Rules).
+
+
+testEnumExt(suite) -> [];
+testEnumExt(Config) ->
+
+ ?line testEnumExt:compile(Config,?BER,[]),
+ ?line testEnumExt:main(?BER),
+
+ ?line ?ber_driver(?BER,testEnumExt:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testEnumExt:main(?BER)),
+
+ ?line testEnumExt:compile(Config,?PER,[]),
+ ?line testEnumExt:main(?PER),
+
+ ?line ?per_bit_opt(testEnumExt:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testEnumExt:main(?PER)),
+
+ ?line ?uper_bin(testEnumExt:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testEnumExt:main(uper_bin)),
+
+ ?line testEnumExt:compile(Config,?PER,[optimize]),
+ ?line testEnumExt:main(?PER).
+
+testSeqDefault(doc) -> ["Test of OTP-2523 ENUMERATED with extensionmark."];
+testSeqDefault(suite) -> [];
+testSeqDefault(Config) ->
+
+ ?line testSeqDefault:compile(Config,?BER,[]),
+ ?line testSeqDefault_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqDefault:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqDefault_cases(?BER)),
+
+ ?line testSeqDefault:compile(Config,?PER,[]),
+ ?line testSeqDefault_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqDefault:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqDefault_cases(?PER)),
+
+ ?line ?uper_bin(testSeqDefault:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqDefault_cases(uper_bin)),
+
+ ?line testSeqDefault:compile(Config,?PER,[optimize]),
+ ?line testSeqDefault_cases(?PER).
+
+testSeqDefault_cases(Rules) ->
+ ?line testSeqDefault:main(Rules).
+
+
+
+testSeqExtension(suite) -> [];
+testSeqExtension(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqExtension:compile(Config,?BER,[]),
+ ?line testSeqExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExtension_cases(?BER)).
+
+testSeqExtension_cases(Rules) ->
+ ?line testSeqExtension:main(Rules).
+
+
+
+testSeqExternal(suite) -> [];
+testSeqExternal(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqExternal:compile(Config,?BER,[]),
+ ?line testSeqExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqExternal_cases(?BER)).
+
+testSeqExternal_cases(Rules) ->
+ ?line testSeqExternal:main(Rules).
+
+
+testSeqOptional(suite) -> [];
+testSeqOptional(Config) ->
+
+ ?line testSeqOptional:compile(Config,?BER,[]),
+ ?line testSeqOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOptional_cases(?BER)),
+
+ ?line testSeqOptional:compile(Config,?PER,[]),
+ ?line testSeqOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOptional_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOptional_cases(uper_bin)),
+
+ ?line testSeqOptional:compile(Config,?PER,[optimize]),
+ ?line testSeqOptional_cases(?PER).
+
+testSeqOptional_cases(Rules) ->
+ ?line testSeqOptional:main(Rules).
+
+
+
+testSeqPrim(suite) -> [];
+testSeqPrim(Config) ->
+
+ ?line testSeqPrim:compile(Config,?BER,[]),
+ ?line testSeqPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqPrim_cases(?BER)),
+
+ ?line testSeqPrim:compile(Config,?PER,[]),
+ ?line testSeqPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSeqPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqPrim_cases(uper_bin)),
+
+ ?line testSeqPrim:compile(Config,?PER,[optimize]),
+ ?line testSeqPrim_cases(?PER).
+
+testSeqPrim_cases(Rules) ->
+ ?line testSeqPrim:main(Rules).
+
+
+testSeq2738(doc) -> ["Test of OTP-2738 Detect corrupt optional component."];
+testSeq2738(suite) -> [];
+testSeq2738(Config) ->
+
+ ?line testSeq2738:compile(Config,?BER,[]),
+ ?line testSeq2738_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeq2738:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeq2738_cases(?BER)),
+
+ ?line testSeq2738:compile(Config,?PER,[]),
+ ?line testSeq2738_cases(?PER),
+
+ ?line ?per_bit_opt(testSeq2738:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeq2738_cases(?PER)),
+
+ ?line ?uper_bin(testSeq2738:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeq2738_cases(uper_bin)),
+
+ ?line testSeq2738:compile(Config,?PER,[optimize]),
+ ?line testSeq2738_cases(?PER).
+
+testSeq2738_cases(Rules) ->
+ ?line testSeq2738:main(Rules).
+
+
+testSeqTag(suite) -> [];
+testSeqTag(Config) ->
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqTag:compile(Config,?BER,[]),
+ ?line testSeqTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqTag:compile(Config,?PER,[]),
+ ?line testSeqTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqTag:compile(Config,?PER,[optimize]),
+ ?line testSeqTag_cases(?PER).
+
+testSeqTag_cases(Rules) ->
+ ?line testSeqTag:main(Rules).
+
+
+
+
+testSeqTypeRefCho(suite) -> [];
+testSeqTypeRefCho(Config) ->
+
+ ?line testSeqTypeRefCho:compile(Config,?BER,[]),
+ ?line testSeqTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefCho_cases(?BER)),
+
+ ?line testSeqTypeRefCho:compile(Config,?PER,[]),
+ ?line testSeqTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefCho_cases(uper_bin)),
+
+ ?line testSeqTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefCho_cases(?PER).
+
+testSeqTypeRefCho_cases(Rules) ->
+ ?line testSeqTypeRefCho:main(Rules).
+
+
+
+testSeqTypeRefPrim(suite) -> [];
+testSeqTypeRefPrim(Config) ->
+
+ ?line testSeqTypeRefPrim:compile(Config,?BER,[]),
+ ?line testSeqTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefPrim_cases(?BER)),
+
+ ?line testSeqTypeRefPrim:compile(Config,?PER,[]),
+ ?line testSeqTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefPrim_cases(uper_bin)),
+
+ ?line testSeqTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefPrim_cases(?PER).
+
+testSeqTypeRefPrim_cases(Rules) ->
+ ?line testSeqTypeRefPrim:main(Rules).
+
+
+
+testSeqTypeRefSeq(suite) -> [];
+testSeqTypeRefSeq(Config) ->
+
+ ?line testSeqTypeRefSeq:compile(Config,?BER,[]),
+ ?line testSeqTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefSeq_cases(?BER)),
+
+ ?line testSeqTypeRefSeq:compile(Config,?PER,[]),
+ ?line testSeqTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefSeq_cases(uper_bin)),
+
+ ?line testSeqTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefSeq_cases(?PER).
+
+testSeqTypeRefSeq_cases(Rules) ->
+ ?line testSeqTypeRefSeq:main(Rules).
+
+
+
+testSeqTypeRefSet(suite) -> [];
+testSeqTypeRefSet(Config) ->
+
+ ?line testSeqTypeRefSet:compile(Config,?BER,[]),
+ ?line testSeqTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqTypeRefSet_cases(?BER)),
+
+ ?line testSeqTypeRefSet:compile(Config,?PER,[]),
+ ?line testSeqTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testSeqTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqTypeRefSet_cases(uper_bin)),
+
+ ?line testSeqTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testSeqTypeRefSet_cases(?PER).
+
+testSeqTypeRefSet_cases(Rules) ->
+ ?line testSeqTypeRefSet:main(Rules).
+
+
+
+
+testSeqOf(suite) -> [];
+testSeqOf(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOf:compile(Config,?BER,[]),
+ ?line testSeqOf_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOf:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOf_cases(?BER)),
+
+ ?line testSeqOf:compile(Config,?PER,[]),
+ ?line testSeqOf_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOf:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOf_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOf:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOf_cases(uper_bin)),
+
+ ?line testSeqOf:compile(Config,?PER,[optimize]),
+ ?line testSeqOf_cases(?PER).
+
+testSeqOf_cases(Rules) ->
+ ?line testSeqOf:main(Rules).
+
+
+
+
+testSeqOfCho(suite) -> [];
+testSeqOfCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOfCho:compile(Config,?BER,[]),
+ ?line testSeqOfCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSeqOfCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfCho_cases(?BER)),
+
+ ?line testSeqOfCho:compile(Config,?PER,[]),
+ ?line testSeqOfCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSeqOfCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfCho_cases(?PER)),
+
+ ?line ?uper_bin(testSeqOfCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfCho_cases(uper_bin)),
+
+ ?line testSeqOfCho:compile(Config,?PER,[optimize]),
+ ?line testSeqOfCho_cases(?PER).
+
+testSeqOfIndefinite(suite) -> [];
+testSeqOfIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqOfIndefinite:compile(Config,?BER,[]),
+ ?line testSeqOfIndefinite:main(),
+
+ ?line ?ber_driver(?BER,testSeqOfIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfIndefinite:main()).
+
+testSeqOfCho_cases(Rules) ->
+ ?line testSeqOfCho:main(Rules).
+
+
+testSeqOfExternal(suite) -> [];
+testSeqOfExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqOfExternal:compile(Config,?BER,[]),
+ ?line testSeqOfExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqOfExternal:compile(Config,?PER,[]),
+ ?line testSeqOfExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfExternal_cases(?PER).
+
+testSeqOfExternal_cases(Rules) ->
+ ?line testSeqOfExternal:main(Rules).
+
+
+
+testSeqOfTag(suite) -> [];
+testSeqOfTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSeqOfTag:compile(Config,?BER,[]),
+ ?line testSeqOfTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqOfTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSeqOfTag:compile(Config,?PER,[]),
+ ?line testSeqOfTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSeqOfTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSeqOfTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSeqOfTag:compile(Config,?PER,[optimize]),
+ ?line testSeqOfTag_cases(?PER).
+
+testSeqOfTag_cases(Rules) ->
+ ?line testSeqOfTag:main(Rules).
+
+
+
+
+testSetDefault(suite) -> [];
+testSetDefault(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetDefault:compile(Config,?BER,[]),
+ ?line testSetDefault_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetDefault:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetDefault_cases(?BER)),
+
+ ?line testSetDefault:compile(Config,?PER,[]),
+ ?line testSetDefault_cases(?PER),
+
+ ?line ?per_bit_opt(testSetDefault:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetDefault_cases(?PER)),
+
+ ?line ?uper_bin(testSetDefault:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetDefault_cases(uper_bin)),
+
+ ?line testSetDefault:compile(Config,?PER,[optimize]),
+ ?line testSetDefault_cases(?PER).
+
+testSetDefault_cases(Rules) ->
+ ?line testSetDefault:main(Rules).
+
+
+testParamBasic(suite) -> [];
+testParamBasic(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testParamBasic:compile(Config,?BER,[]),
+ ?line testParamBasic_cases(?BER),
+
+ ?line ?ber_driver(?BER,testParamBasic:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testParamBasic_cases(?BER)),
+
+ ?line testParamBasic:compile(Config,?PER,[]),
+ ?line testParamBasic_cases(?PER),
+
+ ?line ?per_bit_opt(testParamBasic:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testParamBasic_cases(?PER)),
+
+ ?line ?uper_bin(testParamBasic:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testParamBasic_cases(uper_bin)),
+
+ ?line testParamBasic:compile(Config,?PER,[optimize]),
+ ?line testParamBasic_cases(?PER).
+
+
+testParamBasic_cases(Rules) ->
+ ?line testParamBasic:main(Rules).
+
+testSetExtension(suite) -> [];
+testSetExtension(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetExtension:compile(Config,?BER,[]),
+ ?line testSetExtension_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExtension:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExtension_cases(?BER)).
+
+testSetExtension_cases(Rules) ->
+ ?line testSetExtension:main(Rules).
+
+
+testSetExternal(suite) -> [];
+testSetExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetExternal:compile(Config,?BER,[]),
+ ?line testSetExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetExternal_cases(?BER)).
+
+testSetExternal_cases(Rules) ->
+ ?line testSetExternal:main(Rules).
+
+
+testSetOptional(suite) -> [];
+testSetOptional(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOptional:compile(Config,?BER,[]),
+ ?line testSetOptional_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOptional:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOptional_cases(?BER)),
+
+ ?line testSetOptional:compile(Config,?PER,[]),
+ ?line testSetOptional_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOptional:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOptional_cases(?PER)),
+
+ ?line ?uper_bin(testSetOptional:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOptional_cases(uper_bin)),
+
+ ?line testSetOptional:compile(Config,?PER,[optimize]),
+ ?line testSetOptional_cases(?PER).
+
+testSetOptional_cases(Rules) ->
+ ?line ok = testSetOptional:ticket_7533(Rules),
+ ?line ok = testSetOptional:main(Rules).
+
+
+
+
+testSetPrim(suite) -> [];
+testSetPrim(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetPrim:compile(Config,?BER,[]),
+ ?line testSetPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetPrim_cases(?BER)),
+
+ ?line testSetPrim:compile(Config,?PER,[]),
+ ?line testSetPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSetPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSetPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetPrim_cases(uper_bin)),
+
+ ?line testSetPrim:compile(Config,?PER,[optimize]),
+ ?line testSetPrim_cases(?PER).
+
+testSetPrim_cases(Rules) ->
+ ?line testSetPrim:main(Rules).
+
+
+
+testSetTag(suite) -> [];
+testSetTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetTag:compile(Config,?BER,[]),
+ ?line testSetTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetTag:compile(Config,?PER,[]),
+ ?line testSetTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetTag:compile(Config,?PER,[optimize]),
+ ?line testSetTag_cases(?PER).
+
+testSetTag_cases(Rules) ->
+ ?line testSetTag:main(Rules).
+
+
+
+testSetTypeRefCho(suite) -> [];
+testSetTypeRefCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefCho:compile(Config,?BER,[]),
+ ?line testSetTypeRefCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefCho_cases(?BER)),
+
+ ?line testSetTypeRefCho:compile(Config,?PER,[]),
+ ?line testSetTypeRefCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefCho_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefCho_cases(uper_bin)),
+
+ ?line testSetTypeRefCho:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefCho_cases(?PER).
+
+testSetTypeRefCho_cases(Rules) ->
+ ?line testSetTypeRefCho:main(Rules).
+
+
+
+testSetTypeRefPrim(suite) -> [];
+testSetTypeRefPrim(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefPrim:compile(Config,?BER,[]),
+ ?line testSetTypeRefPrim_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefPrim:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefPrim_cases(?BER)),
+
+ ?line testSetTypeRefPrim:compile(Config,?PER,[]),
+ ?line testSetTypeRefPrim_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefPrim:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefPrim_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefPrim:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefPrim_cases(uper_bin)),
+
+ ?line testSetTypeRefPrim:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefPrim_cases(?PER).
+
+testSetTypeRefPrim_cases(Rules) ->
+ ?line testSetTypeRefPrim:main(Rules).
+
+
+
+testSetTypeRefSeq(suite) -> [];
+testSetTypeRefSeq(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefSeq:compile(Config,?BER,[]),
+ ?line testSetTypeRefSeq_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefSeq:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefSeq_cases(?BER)),
+
+ ?line testSetTypeRefSeq:compile(Config,?PER,[]),
+ ?line testSetTypeRefSeq_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefSeq:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefSeq_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefSeq:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefSeq_cases(uper_bin)),
+
+ ?line testSetTypeRefSeq:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefSeq_cases(?PER).
+
+testSetTypeRefSeq_cases(Rules) ->
+ ?line testSetTypeRefSeq:main(Rules).
+
+
+
+testSetTypeRefSet(suite) -> [];
+testSetTypeRefSet(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetTypeRefSet:compile(Config,?BER,[]),
+ ?line testSetTypeRefSet_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetTypeRefSet:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetTypeRefSet_cases(?BER)),
+
+ ?line testSetTypeRefSet:compile(Config,?PER,[]),
+ ?line testSetTypeRefSet_cases(?PER),
+
+ ?line ?per_bit_opt(testSetTypeRefSet:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetTypeRefSet_cases(?PER)),
+
+ ?line ?uper_bin(testSetTypeRefSet:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetTypeRefSet_cases(uper_bin)),
+
+ ?line testSetTypeRefSet:compile(Config,?PER,[optimize]),
+ ?line testSetTypeRefSet_cases(?PER).
+
+testSetTypeRefSet_cases(Rules) ->
+ ?line testSetTypeRefSet:main(Rules).
+
+
+
+testSetOf(suite) -> [];
+testSetOf(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOf:compile(Config,?BER,[]),
+ ?line testSetOf_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOf:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOf_cases(?BER)),
+
+ ?line testSetOf:compile(Config,?PER,[]),
+ ?line testSetOf_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOf:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOf_cases(?PER)),
+
+ ?line ?uper_bin(testSetOf:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOf_cases(uper_bin)),
+
+ ?line testSetOf:compile(Config,?PER,[optimize]),
+ ?line testSetOf_cases(?PER).
+
+testSetOf_cases(Rules) ->
+ ?line testSetOf:main(Rules).
+
+
+
+testSetOfCho(suite) -> [];
+testSetOfCho(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetOfCho:compile(Config,?BER,[]),
+ ?line testSetOfCho_cases(?BER),
+
+ ?line ?ber_driver(?BER,testSetOfCho:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfCho_cases(?BER)),
+
+ ?line testSetOfCho:compile(Config,?PER,[]),
+ ?line testSetOfCho_cases(?PER),
+
+ ?line ?per_bit_opt(testSetOfCho:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfCho_cases(?PER)),
+
+ ?line ?uper_bin(testSetOfCho:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfCho_cases(uper_bin)),
+
+ ?line testSetOfCho:compile(Config,?PER,[optimize]),
+ ?line testSetOfCho_cases(?PER).
+
+testSetOfCho_cases(Rules) ->
+ ?line testSetOfCho:main(Rules).
+
+
+testSetOfExternal(suite) -> [];
+testSetOfExternal(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetOfExternal:compile(Config,?BER,[]),
+ ?line testSetOfExternal_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfExternal_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetOfExternal:compile(Config,?PER,[]),
+ ?line testSetOfExternal_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfExternal_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfExternal_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfExternal_cases(?PER).
+
+testSetOfExternal_cases(Rules) ->
+ ?line testSetOfExternal:main(Rules).
+
+
+
+
+testSetOfTag(suite) -> [];
+testSetOfTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testExternal:compile(Config,?BER,[]),
+ ?line testSetOfTag:compile(Config,?BER,[]),
+ ?line testSetOfTag_cases(?BER),
+
+ ?line ?ber_driver(?BER,testExternal:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetOfTag_cases(?BER)),
+
+ ?line testExternal:compile(Config,?PER,[]),
+ ?line testSetOfTag:compile(Config,?PER,[]),
+ ?line testSetOfTag_cases(?PER),
+
+ ?line ?per_bit_opt(testExternal:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testSetOfTag_cases(?PER)),
+
+ ?line ?uper_bin(testExternal:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testSetOfTag_cases(uper_bin)),
+
+ ?line testExternal:compile(Config,?PER,[optimize]),
+ ?line testSetOfTag:compile(Config,?PER,[optimize]),
+ ?line testSetOfTag_cases(?PER).
+
+testSetOfTag_cases(Rules) ->
+ ?line testSetOfTag:main(Rules).
+
+
+c_syntax(suite) -> [];
+c_syntax(Config) ->
+ ?line DataDir% ?line testExternal:compile(Config,?PER),
+% ?line testPrimExternal:compile(Config,?PER),
+% ?line testPrimExternal_cases(?PER).
+ = ?config(data_dir,Config),
+ ?line _TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line {error,_R1} = asn1ct:compile(filename:join(DataDir,"Syntax")),
+ ?line {error,_R2} = asn1ct:compile(filename:join(DataDir,"BadTypeEnding")),
+ ?line {error,_R3} = asn1ct:compile(filename:join(DataDir,
+ "BadValueAssignment1")),
+ ?line {error,_R4} = asn1ct:compile(filename:join(DataDir,
+ "BadValueAssignment2")),
+ ?line {error,_R5} = asn1ct:compile(filename:join(DataDir,
+ "BadValueSet")),
+ ?line {error,_R6} = asn1ct:compile(filename:join(DataDir,
+ "ChoiceBadExtension")),
+ ?line {error,_R7} = asn1ct:compile(filename:join(DataDir,
+ "EnumerationBadExtension")),
+ ?line {error,_R8} = asn1ct:compile(filename:join(DataDir,
+ "Example")),
+ ?line {error,_R9} = asn1ct:compile(filename:join(DataDir,
+ "Export1")),
+ ?line {error,_R10} = asn1ct:compile(filename:join(DataDir,
+ "MissingEnd")),
+ ?line {error,_R11} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComma")),
+ ?line {error,_R12} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComponentName")),
+ ?line {error,_R13} = asn1ct:compile(filename:join(DataDir,
+ "SequenceBadComponentType")),
+ ?line {error,_R14} = asn1ct:compile(filename:join(DataDir,
+ "SeqBadComma")).
+
+
+c_string_per(suite) -> [];
+c_string_per(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"String"),[?PER,{outdir,TempDir}]).
+
+c_string_ber(suite) -> [];
+c_string_ber(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"String"),[?BER,{outdir,TempDir}]).
+
+
+c_implicit_before_choice(suite) -> [];
+c_implicit_before_choice(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line TempDir = ?config(priv_dir,Config),
+ ?line {error,_R2} = asn1ct:compile(filename:join(DataDir,"CCSNARG3"),[?BER,{outdir,TempDir}]).
+
+parse(suite) -> [];
+parse(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ M1 = test_modules(),
+% M2 = parse_modules(),
+ ?line ok = parse1(M1,DataDir,OutDir).
+
+parse1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[abs,{outdir,OutDir}]),
+ parse1(T,DataDir,OutDir);
+parse1([],_,_) ->
+ ok.
+
+per(suite) -> [];
+per(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = per1(per_modules(),DataDir,OutDir),
+ ?line ?per_bit_opt(per1_bit_opt(per_modules(),DataDir,OutDir)),
+ ?line ok = per1_opt(per_modules(),DataDir,OutDir).
+
+
+per1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1(T,DataDir,OutDir);
+per1([],_,_) ->
+ ok.
+
+per1_bit_opt([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,optimize,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1_bit_opt(T,DataDir,OutDir);
+per1_bit_opt([],_,_) ->
+ ok.
+
+per1_opt([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?PER,optimized,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ per1_opt(T,DataDir,OutDir);
+per1_opt([],_,_) ->
+ ok.
+
+
+ber_choiceinseq(suite) ->[];
+ber_choiceinseq(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"ChoiceInSeq"),[?BER,{outdir,OutDir}]).
+
+ber_optional(suite) ->[];
+ber_optional(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"SOpttest"),[?BER,{outdir,OutDir}]),
+ ?line V = {'S',{'A',10,asn1_NOVALUE,asn1_NOVALUE},
+ {'B',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ {'C',asn1_NOVALUE,111,asn1_NOVALUE}},
+ ?line {ok,B} = asn1_wrapper:encode('SOpttest','S',V),
+ ?line Bytes = lists:flatten(B),
+ ?line V2 = asn1_wrapper:decode('SOpttest','S',Bytes),
+ ?line ok = eq(V,element(2,V2)).
+
+ber_optional_keyed_list(suite) ->[];
+ber_optional_keyed_list(Config) ->
+ case ?BER of
+ ber_bin_v2 -> ok;
+ _ ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"SOpttest"),
+ [?BER,keyed_list,{outdir,OutDir}]),
+ ?line Vrecord = {'S',{'A',10,asn1_NOVALUE,asn1_NOVALUE},
+ {'B',asn1_NOVALUE,asn1_NOVALUE,asn1_NOVALUE},
+ {'C',asn1_NOVALUE,111,asn1_NOVALUE}},
+ ?line V = [ {a,[{scriptKey,10}]},
+ {b,[]},
+ {c,[{callingPartysCategory,111}]} ],
+ ?line {ok,B} = asn1_wrapper:encode('SOpttest','S',V),
+ ?line Bytes = lists:flatten(B),
+ ?line V2 = asn1_wrapper:decode('SOpttest','S',Bytes),
+ ?line ok = eq(Vrecord,element(2,V2))
+ end.
+
+
+eq(V,V) ->
+ ok.
+
+
+ber_other(suite) ->[];
+ber_other(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = ber1(ber_modules(),DataDir,OutDir).
+
+
+ber1([M|T],DataDir,OutDir) ->
+ ?line ok = asn1ct:compile(DataDir ++ M,[?BER,{outdir,OutDir}]),
+ ?line ok = asn1ct:test(list_to_atom(M)),
+ ber1(T,DataDir,OutDir);
+ber1([],_,_) ->
+ ok.
+
+default_per(suite) ->[];
+default_per(Config) ->
+ default1(?PER,Config,[]).
+
+default_per_opt(suite) -> [];
+default_per_opt(Config) ->
+ ?per_bit_opt(default1(?PER,Config,[optimize])),
+ default1(?PER,Config,[optimize]).
+
+default_ber(suite) ->[];
+default_ber(Config) ->
+ default1(?BER,Config,[]).
+
+default1(Rule,Config,Options) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "Def",[Rule,{outdir,OutDir}]++Options),
+ ?line {ok,Bytes1} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true,
+ bool1 = true,
+ bool2 = true,
+ bool3 = true}),
+ ?line {ok,{'Def1',true,true,true,true}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes1)),
+
+ ?line {ok,Bytes2} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true}),
+ ?line {ok,{'Def1',true,false,false,false}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes2)),
+
+ ?line {ok,Bytes3} = asn1_wrapper:encode('Def','Def1',#'Def1'{bool0 = true,bool2=false}),
+ ?line {ok,{'Def1',true,false,false,false}} = asn1_wrapper:decode('Def','Def1',lists:flatten(Bytes3)).
+
+
+value_test(suite) ->[];
+value_test(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "ObjIdValues",[?BER,{outdir,OutDir}]),
+ ?line {ok,_} = asn1_wrapper:encode('ObjIdValues','ObjIdType','ObjIdValues':'mobileDomainId'()),
+ ?line ok = asn1ct:compile(DataDir ++ "ObjIdValues",[?PER,{outdir,OutDir}]),
+ ?line {ok,_} = asn1_wrapper:encode('ObjIdValues','ObjIdType','ObjIdValues':'mobileDomainId'()),
+ ?line ok = test_bad_values:tests(Config),
+ ok.
+
+
+constructed(suite) ->
+ [];
+constructed(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ ?line ok = asn1ct:compile(DataDir ++ "Constructed",[?BER,{outdir,OutDir}]),
+ ?line {ok,B} = asn1_wrapper:encode('Constructed','S',{'S',false}),
+ ?line [40,3,1,1,0] = lists:flatten(B),
+ ?line {ok,B1} = asn1_wrapper:encode('Constructed','S2',{'S2',false}),
+ ?line [40,5,48,3,1,1,0] = lists:flatten(B1),
+ ?line {ok,B2} = asn1_wrapper:encode('Constructed','I',10),
+ ?line [136,1,10] = lists:flatten(B2),
+ ok.
+
+ber_decode_error(suite) -> [];
+ber_decode_error(Config) ->
+ ?line ok = ber_decode_error:compile(Config,?BER,[]),
+ ?line ok = ber_decode_error:run([]),
+
+ ?line ok = ?ber_driver(?BER,ber_decode_error:compile(Config,?BER,[driver])),
+ ?line ok = ?ber_driver(?BER,ber_decode_error:run([driver])),
+ ok.
+
+h323test(suite) ->
+ [];
+h323test(Config) ->
+ ?line ok = h323test:compile(Config,?PER,[]),
+ ?line ok = h323test:run(?PER),
+ ?line ?per_bit_opt(h323test:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(h323test:run(?PER)),
+ ?line ?uper_bin(h323test:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(h323test:run(uper_bin)),
+ ?line ok = h323test:compile(Config,?PER,[optimize]),
+ ?line ok = h323test:run(?PER),
+ ok.
+
+per_GeneralString(suite) ->
+ [];
+per_GeneralString(Config) ->
+ case erlang:module_loaded('MULTIMEDIA-SYSTEM-CONTROL') of
+ true ->
+ ok;
+ false ->
+ h323test:compile(Config,?PER,[])
+ end,
+ UI = [109,64,1,57],
+ ?line {ok,_V} = asn1_wrapper:decode('MULTIMEDIA-SYSTEM-CONTROL',
+ 'MultimediaSystemControlMessage',UI).
+
+per_open_type(suite) ->
+ [];
+per_open_type(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line ok = asn1ct:compile(DataDir ++ "OpenType",[?PER,{outdir,OutDir}]),
+ Stype = {'Stype',10,true},
+ ?line {ok,Bytes} = asn1_wrapper:encode('OpenType','Ot',Stype),
+ ?line {ok,Stype} = asn1_wrapper:decode('OpenType','Ot',Bytes),
+
+ ?line ?per_bit_opt(ok = asn1ct:compile(DataDir ++ "OpenType",
+ [?PER,optimize,{outdir,OutDir}])),
+ ?line ?per_bit_opt({ok,Bytes}=asn1_wrapper:encode('OpenType','Ot',Stype)),
+ ?line ?per_bit_opt({ok,Stype}=asn1_wrapper:decode('OpenType','Ot',Bytes)),
+
+ ?line ?uper_bin(ok = asn1ct:compile(DataDir ++ "OpenType",
+ [uper_bin,{outdir,OutDir}])),
+ ?line ?uper_bin({ok,Bytes}=asn1_wrapper:encode('OpenType','Ot',Stype)),
+ ?line ?uper_bin({ok,Stype}=asn1_wrapper:decode('OpenType','Ot',Bytes)),
+
+ ?line ok = asn1ct:compile(DataDir ++ "OpenType",
+ [?PER,optimize,{outdir,OutDir}]),
+ ?line {ok,Bytes} = asn1_wrapper:encode('OpenType','Ot',Stype),
+ ?line {ok,Stype} = asn1_wrapper:decode('OpenType','Ot',Bytes).
+
+testConstraints(suite) ->
+ [];
+testConstraints(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testConstraints:compile(Config,?BER,[]),
+ ?line testConstraints:int_constraints(?BER),
+
+ ?line ?ber_driver(?BER,testConstraints:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testConstraints:int_constraints(?BER)),
+
+ ?line testConstraints:compile(Config,?PER,[]),
+ ?line testConstraints:int_constraints(?PER),
+ ?line testConstraints:refed_NNL_name(?PER),
+
+ ?line ?per_bit_opt(testConstraints:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testConstraints:int_constraints(?PER)),
+ ?line ?per_bit_opt(testConstraints:refed_NNL_name(?PER)),
+
+ ?line ?uper_bin(testConstraints:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testConstraints:int_constraints(uper_bin)),
+ ?line ?uper_bin(testConstraints:refed_NNL_name(uper_bin)),
+
+ ?line testConstraints:compile(Config,?PER,[optimize]),
+ ?line testConstraints:int_constraints(?PER),
+ ?line testConstraints:refed_NNL_name(?PER).
+
+testSeqIndefinite(suite) -> [];
+testSeqIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSeqIndefinite:compile(Config,?BER,[]),
+ ?line testSeqIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testSeqIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSeqIndefinite:main(?BER)).
+
+testSetIndefinite(suite) -> [];
+testSetIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testSetIndefinite:compile(Config,?BER,[]),
+ ?line testSetIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testSetIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testSetIndefinite:main(?BER)).
+
+testChoiceIndefinite(suite) -> [];
+testChoiceIndefinite(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testChoiceIndefinite:compile(Config,?BER,[]),
+ ?line testChoiceIndefinite:main(?BER),
+
+ ?line ?ber_driver(?BER,testChoiceIndefinite:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testChoiceIndefinite:main(?BER)).
+
+testInfObjectClass(suite) ->
+ [];
+testInfObjectClass(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testInfObjectClass:compile(Config,?PER,[]),
+ ?line testInfObjectClass:main(?PER),
+ ?line testInfObj:compile(Config,?PER,[]),
+ ?line testInfObj:main(?PER),
+
+ ?line ?per_bit_opt(testInfObjectClass:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testInfObjectClass:main(?PER)),
+ ?line ?per_bit_opt(testInfObj:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testInfObj:main(?PER)),
+
+ ?line ?uper_bin(testInfObjectClass:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testInfObjectClass:main(uper_bin)),
+ ?line ?uper_bin(testInfObj:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testInfObj:main(uper_bin)),
+
+ ?line testInfObjectClass:compile(Config,?PER,[optimize]),
+ ?line testInfObjectClass:main(?PER),
+ ?line testInfObj:compile(Config,?PER,[optimize]),
+ ?line testInfObj:main(?PER),
+
+ ?line testInfObjectClass:compile(Config,?BER,[]),
+ ?line testInfObjectClass:main(?BER),
+ ?line testInfObj:compile(Config,?BER,[]),
+ ?line testInfObj:main(?BER),
+
+ ?line ?ber_driver(?BER,testInfObjectClass:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testInfObjectClass:main(?BER)),
+ ?line ?ber_driver(?BER,testInfObj:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testInfObj:main(?BER)),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?PER,[]),
+
+ ?line ?per_bit_opt(testInfObj:compile_RANAPfiles(Config,?PER,[optimize])),
+
+ ?line ?uper_bin(testInfObj:compile_RANAPfiles(Config,uper_bin,[])),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?PER,[optimize]),
+
+ ?line testInfObj:compile_RANAPfiles(Config,?BER,[]).
+
+testParameterizedInfObj(suite) ->
+ [];
+testParameterizedInfObj(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testParameterizedInfObj:compile(Config,?PER,[]),
+ ?line testParameterizedInfObj:main(?PER),
+
+ ?line ?per_bit_opt(testParameterizedInfObj:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testParameterizedInfObj:main(?PER)),
+
+ ?line ?uper_bin(testParameterizedInfObj:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testParameterizedInfObj:main(uper_bin)),
+
+ ?line testParameterizedInfObj:compile(Config,?PER,[optimize]),
+ ?line testParameterizedInfObj:main(?PER),
+
+ ?line testParameterizedInfObj:compile(Config,?BER,[]),
+ ?line testParameterizedInfObj:main(?BER),
+
+ ?line ?ber_driver(?BER,testParameterizedInfObj:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testParameterizedInfObj:main(?BER)).
+
+testMergeCompile(suite) ->
+ [];
+testMergeCompile(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testMergeCompile:compile(Config,?PER,[]),
+ ?line testMergeCompile:main(?PER),
+ ?line testMergeCompile:mvrasn(?PER),
+
+ ?line ?per_bit_opt(testMergeCompile:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testMergeCompile:main(?PER)),
+ ?line ?per_bit_opt(testMergeCompile:mvrasn(?PER)),
+
+ ?line ?uper_bin(testMergeCompile:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testMergeCompile:main(uper_bin)),
+ ?line ?uper_bin(testMergeCompile:mvrasn(uper_bin)),
+
+ ?line testMergeCompile:compile(Config,?BER,[]),
+ ?line testMergeCompile:main(?BER),
+ ?line testMergeCompile:mvrasn(?BER),
+
+ ?line ?ber_driver(?BER,testMergeCompile:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testMergeCompile:main(?BER)),
+ ?line ?ber_driver(?BER,testMergeCompile:mvrasn(?BER)).
+
+testobj(suite) ->
+ [];
+testobj(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line ok = testRANAP:compile(Config,?PER,[]),
+ ?line ok = testRANAP:testobj(?PER),
+ ?line ok = testParameterizedInfObj:ranap(?PER),
+
+ ?line ?per_bit_opt(ok = testRANAP:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(ok = testRANAP:testobj(?PER)),
+ ?line ?per_bit_opt(ok = testParameterizedInfObj:ranap(?PER)),
+
+ ?line ?uper_bin(ok = testRANAP:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(ok = testRANAP:testobj(uper_bin)),
+ ?line ?uper_bin(ok = testParameterizedInfObj:ranap(uper_bin)),
+
+ ?line ok = testRANAP:compile(Config,?PER,[optimize]),
+ ?line ok = testRANAP:testobj(?PER),
+ ?line ok = testParameterizedInfObj:ranap(?PER),
+
+ ?line ok = testRANAP:compile(Config,?BER,[]),
+ ?line ok = testRANAP:testobj(?BER),
+ ?line ok = testParameterizedInfObj:ranap(?BER),
+
+ ?line ?ber_driver(?BER,testRANAP:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testRANAP:testobj(?BER)),
+ ?line ?ber_driver(?BER,testParameterizedInfObj:ranap(?BER)).
+
+
+testDeepTConstr(suite) ->
+ [];
+testDeepTConstr(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testDeepTConstr:compile(Config,?PER,[]),
+ ?line testDeepTConstr:main(?PER),
+
+ ?line ?per_bit_opt(testDeepTConstr:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDeepTConstr:main(?PER)),
+
+ ?line ?uper_bin(testDeepTConstr:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDeepTConstr:main(uper_bin)),
+
+ ?line testDeepTConstr:compile(Config,?PER,[optimize]),
+ ?line testDeepTConstr:main(?PER),
+
+ ?line testDeepTConstr:compile(Config,?BER,[]),
+ ?line testDeepTConstr:main(?BER),
+
+ ?line ?ber_driver(?BER,testDeepTConstr:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDeepTConstr:main(?BER)).
+
+testInvokeMod(suite) ->
+ [];
+testInvokeMod(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"PrimStrings"),[{outdir,OutDir}]),
+ ?line {ok,_Result1} = 'PrimStrings':encode('Bs1',[1,0,1,0]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"PrimStrings"),[?PER,{outdir,OutDir}]),
+ ?line {ok,_Result2} = 'PrimStrings':encode('Bs1',[1,0,1,0]).
+
+testExport(suite) ->
+ [];
+testExport(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line {error,{asn1,_Reason}} = asn1ct:compile(filename:join(DataDir,"IllegalExport"),[{outdir,OutDir}]).
+
+testImport(suite) ->
+ [];
+testImport(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line _OutDir = ?config(priv_dir,Config),
+ ?line {error,_} = asn1ct:compile(filename:join(DataDir,"ImportsFrom"),[?BER]),
+ ok.
+
+testMegaco(suite) ->
+ [];
+testMegaco(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+ io:format("Config: ~p~n",[Config]),
+ ?line {ok,ModuleName1,ModuleName2} = testMegaco:compile(Config,?BER,[]),
+ ?line ok = testMegaco:main(ModuleName1,Config),
+ ?line ok = testMegaco:main(ModuleName2,Config),
+
+ case ?BER of
+ ber_bin_v2 ->
+ ?line {ok,ModuleName3,ModuleName4} = testMegaco:compile(Config,?BER,[driver]),
+ ?line ok = testMegaco:main(ModuleName3,Config),
+ ?line ok = testMegaco:main(ModuleName4,Config);
+ _-> ok
+ end,
+
+ ?line {ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,?PER,[]),
+ ?line ok = testMegaco:main(ModuleName5,Config),
+ ?line ok = testMegaco:main(ModuleName6,Config),
+
+ ?line ?per_bit_opt({ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(ok = testMegaco:main(ModuleName5,Config)),
+ ?line ?per_bit_opt(ok = testMegaco:main(ModuleName6,Config)),
+
+ ?line ?uper_bin({ok,ModuleName5,ModuleName6} = testMegaco:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(ok = testMegaco:main(ModuleName5,Config)),
+ ?line ?uper_bin(ok = testMegaco:main(ModuleName6,Config)),
+
+ ?line {ok,ModuleName7,ModuleName8} = testMegaco:compile(Config,?PER,[optimize]),
+ ?line ok = testMegaco:main(ModuleName7,Config),
+ ?line ok = testMegaco:main(ModuleName8,Config).
+
+
+testMvrasn6(suite) -> [];
+testMvrasn6(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testMvrasn6:compile(Config,?BER),
+ ?line testMvrasn6:main().
+
+testContextSwitchingTypes(suite) -> [];
+testContextSwitchingTypes(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testContextSwitchingTypes:compile(Config,?BER,[]),
+ ?line testContextSwitchingTypes:test(),
+
+ ?line ?ber_driver(?BER,testContextSwitchingTypes:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testContextSwitchingTypes:test()),
+
+ ?line testContextSwitchingTypes:compile(Config,?PER,[]),
+ ?line testContextSwitchingTypes:test(),
+
+ ?line ?per_bit_opt(testContextSwitchingTypes:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testContextSwitchingTypes:test()),
+
+ ?line ?uper_bin(testContextSwitchingTypes:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testContextSwitchingTypes:test()),
+
+ ?line testContextSwitchingTypes:compile(Config,?PER,[optimize]),
+ ?line testContextSwitchingTypes:test().
+
+testTypeValueNotation(suite) -> [];
+testTypeValueNotation(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ case ?BER of
+ Ber when Ber == ber; Ber == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?BER,[]),
+ ?line testTypeValueNotation:main(?BER,dummy);
+ _ ->
+ ok
+ end,
+
+ ?line ?ber_driver(?BER,testTypeValueNotation:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testTypeValueNotation:main(?BER,optimize)),
+
+ case ?BER of
+ Ber2 when Ber2 == ber; Ber2 == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?PER,[]),
+ ?line testTypeValueNotation:main(?PER,dummy);
+ _ ->
+ ok
+ end,
+
+ ?line ?per_bit_opt(testTypeValueNotation:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testTypeValueNotation:main(?PER,optimize)),
+
+ ?line ?uper_bin(testTypeValueNotation:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testTypeValueNotation:main(uper_bin,optimize)),
+ case ?BER of
+ Ber3 when Ber3 == ber; Ber3 == ber_bin ->
+ ?line testTypeValueNotation:compile(Config,?PER,[optimize]),
+ ?line testTypeValueNotation:main(?PER,optimize);
+ _ ->
+ ok
+ end.
+
+testOpenTypeImplicitTag(suite) -> [];
+testOpenTypeImplicitTag(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?BER,[]),
+ ?line testOpenTypeImplicitTag:main(?BER),
+
+ ?line ?ber_driver(?BER,testOpenTypeImplicitTag:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testOpenTypeImplicitTag:main(?BER)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?PER,[]),
+ ?line testOpenTypeImplicitTag:main(?PER),
+
+ ?line ?per_bit_opt(testOpenTypeImplicitTag:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testOpenTypeImplicitTag:main(?PER)),
+
+ ?line ?uper_bin(testOpenTypeImplicitTag:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testOpenTypeImplicitTag:main(uper_bin)),
+
+ ?line testOpenTypeImplicitTag:compile(Config,?PER,[optimize]),
+ ?line testOpenTypeImplicitTag:main(?PER).
+
+duplicate_tags(suite) -> [];
+duplicate_tags(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ {error,{asn1,[{error,{type,_,_,'SeqOpt1Imp',{asn1,{duplicates_of_the_tags,_}}}}]}} =
+ asn1ct:compile(filename:join(DataDir,"SeqOptional2"),[abs]),
+ ok.
+
+rtUI(suite) -> [];
+rtUI(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Prim"),[?BER]),
+ ?line {ok,_} = asn1rt:info('Prim'),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Prim"),[?PER]),
+ ?line {ok,_} = asn1rt:info('Prim'),
+
+ ?line ok = asn1rt:load_driver(),
+ ?line ok = asn1rt:load_driver(),
+ ?line ok = asn1rt:unload_driver().
+
+testROSE(suite) -> [];
+testROSE(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testROSE:compile(Config,?BER,[]),
+
+ ?line testROSE:compile(Config,?PER,[]),
+ ?line ?per_bit_opt(testROSE:compile(Config,?PER,[optimize])),
+ ?line ?uper_bin(testROSE:compile(Config,uper_bin,[])),
+ ?line testROSE:compile(Config,?PER,[optimize]).
+
+testINSTANCE_OF(suite) -> [];
+testINSTANCE_OF(Config) ->
+ ?line testINSTANCE_OF:compile(Config,?BER,[]),
+ ?line testINSTANCE_OF:main(?BER),
+
+ ?line ?ber_driver(?BER,testINSTANCE_OF:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testINSTANCE_OF:main(?BER)),
+
+ ?line testINSTANCE_OF:compile(Config,?PER,[]),
+ ?line testINSTANCE_OF:main(?PER),
+
+ ?line ?per_bit_opt(testINSTANCE_OF:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testINSTANCE_OF:main(?PER)),
+
+ ?line ?uper_bin(testINSTANCE_OF:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testINSTANCE_OF:main(uper_bin)),
+
+ ?line testINSTANCE_OF:compile(Config,?PER,[optimize]),
+ ?line testINSTANCE_OF:main(?PER).
+
+testTCAP(suite) -> [];
+testTCAP(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testTCAP:compile(Config,?BER,[]),
+ ?line testTCAP:test(?BER,Config),
+
+ ?line ?ber_driver(?BER,testTCAP:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testTCAP:test(?BER,Config)),
+
+ ?line ?ber_driver(?BER,testTCAP:compile_asn1config(Config,?BER,[asn1config])),
+ ?line ?ber_driver(?BER,testTCAP:test_asn1config()).
+
+testDER(suite) ->[];
+testDER(Config) ->
+ ?line true = code:add_patha(?config(priv_dir,Config)),
+
+ ?line testDER:compile(Config,?BER,[]),
+ ?line testDER:test(),
+
+ ?line ?ber_driver(?BER,testDER:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDER:test()),
+
+ ?line testParamBasic:compile_der(Config,?BER),
+ ?line testParamBasic_cases(der),
+
+
+ ?line testSeqSetDefaultVal:compile(Config,?BER),
+ ?line testSeqSetDefaultVal_cases(?BER).
+
+testSeqSetDefaultVal_cases(?BER) ->
+ ?line testSeqSetDefaultVal:main(?BER).
+
+
+specialized_decodes(suite) -> [];
+specialized_decodes(Config) ->
+ ?line test_partial_incomplete_decode:compile(Config,?BER,[optimize]),
+ ?line test_partial_incomplete_decode:test(?BER,Config),
+ ?line test_selective_decode:test(?BER,Config).
+
+special_decode_performance(suite) ->[];
+special_decode_performance(Config) ->
+ ?line ?ber_driver(?BER,test_special_decode_performance:compile(Config,?BER)),
+ ?line ?ber_driver(?BER,test_special_decode_performance:go(all)).
+
+
+test_driver_load(suite) -> [];
+test_driver_load(Config) ->
+ ?line test_driver_load:compile(Config,?PER),
+ ?line test_driver_load:test(?PER,5).
+
+test_ParamTypeInfObj(suite) -> [];
+test_ParamTypeInfObj(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"IN-CS-1-Datatypes"),[ber_bin]).
+
+test_WS_ParamClass(suite) -> [];
+test_WS_ParamClass(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"InformationFramework"),
+ [ber_bin]).
+
+test_Defed_ObjectIdentifier(suite) -> [];
+test_Defed_ObjectIdentifier(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"UsefulDefinitions"),
+ [ber_bin]).
+
+testSelectionType(suite) -> [];
+testSelectionType(Config) ->
+
+ ?line ok = testSelectionTypes:compile(Config,?BER,[]),
+ ?line {ok,_} = testSelectionTypes:test(),
+
+ ?line ok = testSelectionTypes:compile(Config,?PER,[]),
+ ?line {ok,_} = testSelectionTypes:test().
+
+testSSLspecs(suite) -> [];
+testSSLspecs(Config) ->
+
+ ?line ok = testSSLspecs:compile(Config,?BER,
+ [optimize,compact_bit_string,der]),
+ ?line testSSLspecs:run(?BER),
+
+ case code:which(asn1ct) of
+ cover_compiled ->
+ ok;
+ _ ->
+ ?line ok = testSSLspecs:compile_inline(Config,?BER),
+ ?line ok = testSSLspecs:run_inline(?BER)
+ end.
+
+testNortel(suite) -> [];
+testNortel(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[?BER]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?BER,optimize]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?BER,optimize,driver]),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[?PER]),
+ ?line ?per_bit_opt(ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?PER,optimize])),
+ ?line ?uper_bin(ok = asn1ct:compile(filename:join(DataDir,"Nortel"),[uper_bin])),
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Nortel"),
+ [?PER,optimize]).
+test_undecoded_rest(suite) -> [];
+test_undecoded_rest(Config) ->
+
+ ?line ok = test_undecoded_rest:compile(Config,?BER,[]),
+ ?line ok = test_undecoded_rest:test([]),
+
+ ?line ok = test_undecoded_rest:compile(Config,?BER,[undec_rest]),
+ ?line ok = test_undecoded_rest:test(undec_rest),
+
+ ?line ok = test_undecoded_rest:compile(Config,?PER,[]),
+ ?line ok = test_undecoded_rest:test([]),
+
+ ?line ?per_bit_opt(ok = test_undecoded_rest:compile(Config,?PER,[optimize,undec_rest])),
+ ?line ?per_bit_opt(ok = test_undecoded_rest:test(undec_rest)),
+
+ ?line ?uper_bin(ok = test_undecoded_rest:compile(Config,uper_bin,[undec_rest])),
+ ?line ?uper_bin(ok = test_undecoded_rest:test(undec_rest)),
+
+ ?line ok = test_undecoded_rest:compile(Config,?PER,[undec_rest]),
+ ?line ok = test_undecoded_rest:test(undec_rest).
+
+test_inline(suite) -> [];
+test_inline(Config) ->
+ case code:which(asn1ct) of
+ cover_compiled ->
+ {skip,"Not runnable when cover compiled"};
+ _ ->
+ ?line ok=test_inline:compile(Config,?BER,[]),
+ ?line test_inline:main(?BER),
+ ?line test_inline:inline1(Config,?BER,[]),
+ ?line test_inline:performance2()
+ end.
+
+%test_inline_prf(suite) -> [];
+%test_inline_prf(Config) ->
+% ?line test_inline:performance(Config).
+
+testTcapsystem(suite) -> [];
+testTcapsystem(Config) ->
+ ?line ok=testTcapsystem:compile(Config,?BER,[]).
+
+testNBAPsystem(suite) -> [];
+testNBAPsystem(Config) ->
+ ?line ok=testNBAPsystem:compile(Config,?PER,?per_optimize(?BER)),
+ ?line ok=testNBAPsystem:test(?PER,Config).
+
+test_compile_options(suite) -> [];
+test_compile_options(Config) ->
+ case code:which(asn1ct) of
+ cover_compiled ->
+ {skip,"Not runnable when cover compiled"};
+ _ ->
+ ?line ok = test_compile_options:wrong_path(Config),
+ ?line ok = test_compile_options:path(Config),
+ ?line ok = test_compile_options:noobj(Config),
+ ?line ok = test_compile_options:record_name_prefix(Config),
+ ?line ok = test_compile_options:verbose(Config)
+ end.
+testDoubleEllipses(suite) -> [];
+testDoubleEllipses(Config) ->
+ ?line testDoubleEllipses:compile(Config,?BER,[]),
+ ?line testDoubleEllipses:main(?BER),
+ ?line ?ber_driver(?BER,testDoubleEllipses:compile(Config,?BER,[driver])),
+ ?line ?ber_driver(?BER,testDoubleEllipses:main(?BER)),
+ ?line ?per_bit_opt(testDoubleEllipses:compile(Config,?PER,[optimize])),
+ ?line ?per_bit_opt(testDoubleEllipses:main(?PER)),
+ ?line ?uper_bin(testDoubleEllipses:compile(Config,uper_bin,[])),
+ ?line ?uper_bin(testDoubleEllipses:main(uper_bin)),
+ ?line testDoubleEllipses:compile(Config,?PER,?per_optimize(?BER)),
+ ?line testDoubleEllipses:main(?PER).
+
+test_modified_x420(suite) -> [];
+test_modified_x420(Config) ->
+ ?line test_modified_x420:compile(Config),
+ ?line test_modified_x420:test_io(Config).
+
+testX420(suite) -> [];
+testX420(Config) ->
+ ?line testX420:compile(?BER,[der],Config),
+ ?line ok = testX420:ticket7759(?BER,Config),
+ ?line testX420:compile(?PER,[],Config).
+
+test_x691(suite) -> [];
+test_x691(Config) ->
+ case ?PER of
+ per ->
+ ?line ok = test_x691:compile(Config,uper_bin,[]),
+ ?line true = test_x691:cases(uper_bin,unaligned),
+ ?line ok = test_x691:compile(Config,?PER,[]),
+ ?line true = test_x691:cases(?PER,aligned),
+%% ?line ok = asn1_test_lib:ticket_7678(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7708(Config,[]),
+ ?line ok = asn1_test_lib:ticket_7763(Config);
+ _ ->
+ ?line ok = test_x691:compile(Config,?PER,?per_optimize(?BER)),
+ ?line true = test_x691:cases(?PER,aligned)
+ end.
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[compact_bit_string]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[optimize]),
+%% ?line ok = asn1_test_lib:ticket_7876(Config,?PER,[optimize,compact_bit_string]).
+
+
+ticket_6143(suite) -> [];
+ticket_6143(Config) ->
+ ?line ok = test_compile_options:ticket_6143(Config).
+
+testExtensionAdditionGroup(suite) -> [];
+testExtensionAdditionGroup(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line PrivDir = ?config(priv_dir,Config),
+ ?line Path = code:get_path(),
+ ?line code:add_patha(PrivDir),
+ DoIt = fun(Erule) ->
+ ?line ok = asn1ct:compile(filename:join(DataDir,"Extension-Addition-Group"),[Erule,{outdir,PrivDir}]),
+ ?line {ok,_M} = compile:file(filename:join(DataDir,"extensionAdditionGroup"),[{i,PrivDir},{outdir,PrivDir},debug_info]),
+ ?line ok = extensionAdditionGroup:run(Erule)
+ end,
+ ?line [DoIt(Rule)|| Rule <- [per_bin,uper_bin,ber_bin]],
+ ?line code:set_path(Path).
+
+
+
+% parse_modules() ->
+% ["ImportsFrom"].
+
+per_modules() ->
+ [X || X <- test_modules()].
+ber_modules() ->
+ [X || X <- test_modules(),
+ X =/= "CommonDataTypes",
+ X =/= "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath",
+ X =/= "H323-MESSAGES",
+ X =/= "H235-SECURITY-MESSAGES",
+ X =/= "MULTIMEDIA-SYSTEM-CONTROL"].
+test_modules() ->
+ _Modules = [
+ "BitStr",
+ "CommonDataTypes",
+ "Constraints",
+ "ContextSwitchingTypes",
+ "DS-EquipmentUser-CommonFunctionOrig-TransmissionPath",
+ "Enum",
+ "From",
+ "H235-SECURITY-MESSAGES",
+ "H323-MESSAGES",
+ %%"MULTIMEDIA-SYSTEM-CONTROL", recursive type , problem for asn1ct:value
+ "Import",
+ "Int",
+ "MAP-commonDataTypes",
+% ambigous tags "MAP-insertSubscriberData-def",
+ "Null",
+ "Octetstr",
+ "One",
+ "P-Record",
+ "P",
+% "PDUs",
+ "Person",
+ "PrimStrings",
+ "Real",
+ "XSeq",
+ "XSeqOf",
+ "XSet",
+ "XSetOf",
+ "String",
+ "SwCDR",
+% "Syntax",
+ "Time"
+% ANY "Tst",
+% "Two",
+% errors that should be detected "UndefType"
+] ++
+ [
+ "SeqSetLib", % must be compiled before Seq and Set
+ "Seq",
+ "Set",
+ "SetOf",
+ "SeqOf",
+ "Prim",
+ "Cho",
+ "Def",
+ "Opt",
+ "ELDAPv3",
+ "LDAP"
+ ].
+
+
+common() ->
+[].
+
+particular() ->
+[smp, ticket7904].
+
+
+smp(suite) -> [];
+smp(Config) ->
+ case erlang:system_info(smp_support) of
+ true ->
+ NumOfProcs = erlang:system_info(schedulers),
+ io:format("smp starting ~p workers\n",[NumOfProcs]),
+
+ ?line Msg = {initiatingMessage, testNBAPsystem:cell_setup_req_msg()},
+ ?line ok = testNBAPsystem:compile(Config,per_bin,[optimize]),
+
+ Parent = self(),
+
+ ?line ok = asn1rt:load_driver(),
+
+ smp2(Parent,NumOfProcs,Msg,2),
+
+ N = 10000,
+
+ ?line {Time1,ok} = timer:tc(?MODULE,smp2,[Parent,NumOfProcs,Msg, N]),
+ ?line {Time1S,ok} = timer:tc(?MODULE,sequential,[NumOfProcs * N,Msg]),
+
+ ?line ok = testNBAPsystem:compile(Config,ber_bin,[optimize,driver]),
+ ?line {Time2,ok} = timer:tc(?MODULE,smp2,[Parent,NumOfProcs,Msg, N]),
+
+ ?line {Time2S,ok} = timer:tc(?MODULE,sequential,[NumOfProcs * N,Msg]),
+
+ {comment,lists:flatten(io_lib:format("Encode/decode time parallell with ~p cores: ~p [microsecs]~nEncode/decode time sequential: ~p [microsecs]",[NumOfProcs,Time1+Time2,Time1S+Time2S]))};
+ false ->
+ {skipped,"No smp support"}
+ end.
+
+smp2(Parent,NumOfProcs,Msg, N) ->
+ Pids = [spawn_link(fun() -> worker(Msg,Parent, N) end)
+ || _ <- lists:seq(1,NumOfProcs)],
+ ?line ok = wait_pids(Pids).
+
+worker(Msg, Parent, N) ->
+ %% io:format("smp worker ~p with ~p worker loops.~n",[self(), N]),
+ worker_loop(N, Msg),
+ Parent ! self().
+
+worker_loop(0, _Msg) ->
+ ok;
+worker_loop(N, Msg) ->
+ ?line {ok,B}=asn1_wrapper:encode('NBAP-PDU-Discriptions',
+ 'NBAP-PDU',
+ Msg),
+ ?line {ok,_Msg}=asn1_wrapper:decode('NBAP-PDU-Discriptions',
+ 'NBAP-PDU',
+ B),
+ worker_loop(N - 1, Msg).
+
+
+wait_pids([]) ->
+ ok;
+wait_pids(Pids) ->
+ receive
+ Pid when is_pid(Pid) ->
+ ?line true = lists:member(Pid,Pids),
+ Others = lists:delete(Pid,Pids),
+ io:format("wait_pid got ~p, still waiting for ~p\n",[Pid,Others]),
+ wait_pids(Others);
+ Err ->
+ io:format("Err: ~p~n",[Err]),
+ ?line exit(Err)
+ end.
+
+sequential(N,Msg) ->
+ %%io:format("sequential encode/decode with N = ~p~n",[N]),
+ worker_loop(N,Msg).
+
+-record('InitiatingMessage',{procedureCode,criticality,value}).
+-record('Iu-ReleaseCommand',{first,second}).
+
+ticket7904(suite) -> [];
+ticket7904(Config) ->
+ ?line DataDir = ?config(data_dir,Config),
+ ?line OutDir = ?config(priv_dir,Config),
+
+ ?line ok = asn1ct:compile(DataDir ++
+ "RANAPextract1",[per_bin,optimize,{outdir,OutDir}]),
+
+ Val1 = #'InitiatingMessage'{procedureCode=1,
+ criticality=ignore,
+ value=#'Iu-ReleaseCommand'{
+ first=13,
+ second=true}},
+
+ ?line {ok,_} = 'RANAPextract1':encode('InitiatingMessage', Val1),
+ asn1rt:unload_driver(),
+ ?line {ok,_} = 'RANAPextract1':encode('InitiatingMessage', Val1).
diff --git a/lib/asn1/test/asn1_common_SUITE.erl.src b/lib/asn1/test/asn1_common_SUITE.erl.src
index 99a4f90738..2fa2a09f1f 100644
--- a/lib/asn1/test/asn1_common_SUITE.erl.src
+++ b/lib/asn1/test/asn1_common_SUITE.erl.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl
index 83f38c5e6d..5e027cdedb 100644
--- a/lib/asn1/test/test_compile_options.erl
+++ b/lib/asn1/test/test_compile_options.erl
@@ -132,7 +132,7 @@ verbose(Config) when is_list(Config) ->
?line ok = asn1ct:compile(Asn1File, [{i,DataDir},{outdir,OutDir},noobj,verbose]),
?line test_server:capture_stop(),
?line [Line0|_] = test_server:capture_get(),
- ?line lists:prefix("Erlang ASN.1 version", Line0),
+ ?line true = lists:prefix("Erlang ASN.1 version", Line0),
%% Test non-verbose compile
?line test_server:capture_start(),
diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk
index 32151a0cac..7b52e18805 100644
--- a/lib/asn1/vsn.mk
+++ b/lib/asn1/vsn.mk
@@ -1,318 +1,2 @@
-#next version number to use is 1.6.14 | 1.7 | 2.0
-ASN1_VSN = 1.6.13
-
-TICKETS = OTP-8463
-
-TICKETS_1.6.13 = \
- OTP-8463
-
-TICKETS_1.6.12 = \
- OTP-8256
-
-TICKETS_1.6.11 = \
- OTP-8136 \
- OTP-8047 \
- OTP-8046 \
- OTP-8043 \
- OTP-7972
-
-TICKETS_1.6.10 = \
- OTP-7954 \
- OTP-7953
-
-TICKETS_1.6.10 = \
- OTP-7954 \
- OTP-7953
-
-TICKETS_1.6.9 = \
- OTP-7909 \
- OTP-7904
-
-TICKETS_1.6.8.1 = \
- OTP-7900 \
- OTP-7910
-
-TICKETS_1.6.8 = \
- OTP-7876
-
-TICKETS_1.6.7 = \
- OTP-7801 \
- OTP-7806
-
-TICKETS_1.6.6 = \
- OTP-7759 \
- OTP-7763
-
-TICKETS_1.6.5 = \
- OTP-7734
-
-TICKETS_1.6.4 = \
- OTP-7708
-
-TICKETS_1.6.3 = \
- OTP-7681 \
- OTP-7678
-
-TICKETS_1.6.2 = \
- OTP-7608
-
-TICKETS_1.6.1 = \
- OTP-7602 \
- OTP-7533 \
- OTP-7476 \
- OTP-7334 \
- OTP-7332 \
- OTP-7322 \
- OTP-7306 \
- OTP-7299 \
- OTP-7295 \
- OTP-7204 \
- OTP-7174 \
- OTP-7166
-
-TICKETS_1.6 = \
- OTP-7407 \
- OTP-7403 \
- OTP-7400 \
- OTP-7375 \
- OTP-7374 \
- OTP-7335
-
-TICKETS_1.5.2 = \
- OTP-7263 \
- OTP-7264 \
- OTP-7268 \
- OTP-7269 \
- OTP-7273
-
-TICKETS_1.5.1 = \
- OTP-7149 \
- OTP-7151 \
- OTP-7154 \
- OTP-7155 \
- OTP-7169 \
- OTP-7171 \
- OTP-7193 \
- OTP-7199
-
-TICKETS_1.5 = \
- OTP-6835 \
- OTP-6882
-
-TICKETS_1.4.7 = \
- OTP-6828
-
-TICKETS_1.4.6 = \
- OTP-5067 \
- OTP-6763 \
- OTP-6769 \
- OTP-6770 \
- OTP-6786
-
-TICKETS_1.4.5 = \
- OTP-6493 \
- OTP-6601 \
- OTP-6695 \
- OTP-6698 \
- OTP-6702 \
- OTP-6707 \
- OTP-6717
-
-TICKETS_1.4.4.14 = \
- OTP-6462 \
- OTP-6506
-
-TICKETS_1.4.4.13 = \
- OTP-6405
-
-TICKETS_1.4.4.12 = \
- OTP-6314
-
-TICKETS_1.4.4.11 = \
- OTP-6143
-
-TICKETS_1.4.4.10 = \
- OTP-6111 \
- OTP-5932
-
-TICKETS_1.4.4.9 = \
- OTP-5783 \
- OTP-5788 \
- OTP-5812 \
- OTP-5831 \
- OPT-5832
-
-TICKETS_1.4.4.8 = \
- OTP-5687 \
- OTP-5688 \
- OTP-5689 \
- OTP-5701 \
- OTP-5710
-
-TICKETS_1.4.4.7 = \
- OTP-5477 \
- OTP-5509 \
- OTP-5511 \
- OTP-5602 \
- OTP-5616
-
-TICKETS_1.4.4.6 = \
- OTP-5457 \
- OTP-5466
-
-TICKETS_1.4.4.5 = \
- OTP-5302 \
- OTP-5378
-
-TICKETS_1.4.4.4 = \
- OTP-5240 \
- OTP-5243
-
-TICKETS_1.4.4.3 = \
- OTP-5103 \
- OTP-5104
-
-TICKETS_1.4.4.2 = \
- OTP-5022
-
-TICKETS_1.4.4.1 = \
- OTP-4970
-
-TICKETS_1.4.4 = \
- OTP-4893 \
- OTP-4894 \
- OTP-4895 \
- OTP-4917 \
- OTP-4918 \
- OTP-4919 \
- OTP-4944 \
- OTP-4953 \
- OTP-4955 \
- OTP-4957 \
- OTP-4965
-
-TICKETS_1.4.3.1 = \
- OTP-4866 \
- OTP-4869 \
- OTP-4872
-
-TICKETS_1.4.3 = \
- OTP-4832 \
- OTP-4833 \
- OTP-4835 \
- OTP-4856
-
-TICKETS_1.4.2.1 = \
- OTP-4773 \
- OTP-4791 \
- OTP-4792 \
- OTP-4797 \
- OTP-4798 \
- OTP-4799 \
- OTP-4809
-
-# OTP R9C
-TICKETS_1.4.2 = \
- OTP-4693 \
- OTP-4744
-
-TICKETS_1.4.1.1 = \
- OTP-4663 \
- OTP-4665 \
- OTP-4666
-
-TICKETS_1.4.1 = \
- OTP-4559 \
- OTP-4560 \
- OTP-4590 \
- OTP-4591 \
- OTP-4592 \
- OTP-4631 \
- OTP-4633
-
-TICKETS_1.4 = \
- OTP-3304
-
-TICKETS_1.3.3.1 = \
- OTP-4353 \
- OTP-4354 \
- OTP-4390 \
- OTP-4395
-
-TICKETS_1.3.3 = \
- OTP-4381 \
- OTP-4358 \
- OTP-4355 \
- OTP-4275 \
- OTP-4248 \
- OTP-4247 \
- OTP-4242 \
- OTP-4235 \
- OTP-4234 \
- OTP-4232 \
- OTP-4200 \
- OTP-4161 \
- OTP-4129
-
-TICKETS_1.3.2 = \
- OTP-4094 \
- OTP-4103 \
- OTP-3980 \
- OTP-4073
-
-TICKETS_1.3.1.1 = \
- OTP-4037 \
- OTP-4057 \
- OTP-4058
-
-TICKETS_1.3.1 = \
- OTP-4025 \
- OTP-4026
-
-TICKETS_1.3 = \
- OTP-3463 \
- OTP-3659 \
- OTP-3978 \
- OTP-3979 \
- OTP-3981 \
- OTP-3982 \
- OTP-3983 \
- OTP-3985 \
- OTP-3988 \
- OTP-3984 \
- OTP-3994
-
-TICKETS_1.2.9.6 = \
- OTP-3830
-
-TICKETS_1.2.9.5 = \
- OTP-3713 \
- OTP-3796 \
- OTP-3811
-
-TICKETS_1.2.9.3 = \
- OTP-3700 \
- OTP-3701
-
-TICKETS_1.2.9.2 = \
- OTP-xxxx
-
-TICKETS_1.2.9.1 = \
- OTP-xxxx
-
-TICKETS_1.2.9 = \
- OTP-3569 \
- OTP-3573
-
-TICKETS_1.2.8 = \
- OTP-3496
-
-TICKETS_1.2.7 = \
- OTP-3395
-
-TICKETS_1.2.6 = \
- OTP-3352
-
-TICKETS_1.2.5 = \
- OTP-3341
-
+#next version number to use is 1.6.15 | 1.7 | 2.0
+ASN1_VSN = 1.6.16
diff --git a/lib/common_test/AUTHORS b/lib/common_test/AUTHORS
index 152ec1868f..ff443e89e8 100644
--- a/lib/common_test/AUTHORS
+++ b/lib/common_test/AUTHORS
@@ -5,3 +5,4 @@ Siri Hansen
Kenneth Lundin
Ingela Anderton
Niclas Eklund
+Andrey Pampukha
diff --git a/lib/common_test/Makefile b/lib/common_test/Makefile
index ebca4523ab..aecec1a50d 100644
--- a/lib/common_test/Makefile
+++ b/lib/common_test/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2003-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile
index a2c014418d..3ea6ae65d5 100644
--- a/lib/common_test/doc/src/Makefile
+++ b/lib/common_test/doc/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2010. All Rights Reserved.
+# Copyright Ericsson AB 2003-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -45,13 +45,14 @@ CT_MODULES = \
ct_ssh \
ct_rpc \
ct_snmp \
- unix_telnet
+ unix_telnet \
+ ct_slave
CT_XML_FILES = $(CT_MODULES:=.xml)
XML_APPLICATION_FILES = ref_man.xml
-XML_REF1_FILES = run_test.xml
-XML_REF3_FILES = $(CT_XML_FILES)
+XML_REF1_FILES = ct_run.xml
+XML_REF3_FILES = $(CT_XML_FILES) ct_hooks.xml
XML_REF6_FILES = common_test_app.xml
XML_PART_FILES = part.xml
@@ -70,6 +71,7 @@ XML_CHAPTER_FILES = \
cover_chapter.xml \
ct_master_chapter.xml \
event_handler_chapter.xml \
+ ct_hooks_chapter.xml \
dependencies_chapter.xml \
notes.xml \
notes_history.xml
diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml
index 7b52883f8a..1ee73b890b 100644
--- a/lib/common_test/doc/src/common_test_app.xml
+++ b/lib/common_test/doc/src/common_test_app.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Common Test</title>
@@ -131,7 +131,8 @@
<type>
<v> Info = {timetrap,Time} | {require,Required} |
{require,Name,Required} | {userdata,UserData} |
- {silent_connections,Conns} | {stylesheet,CSSFile}</v>
+ {silent_connections,Conns} | {stylesheet,CSSFile} |
+ {ct_hooks, CTHs}</v>
<v> Time = MilliSec | {seconds,integer()} | {minutes,integer()}
| {hours,integer()}</v>
<v> MilliSec = integer()</v>
@@ -143,6 +144,9 @@
<v> UserData = term()</v>
<v> Conns = [atom()]</v>
<v> CSSFile = string()</v>
+ <v> CTHs = [CTHModule | {CTHModule, CTHInitArgs}]</v>
+ <v> CTHModule = atom()</v>
+ <v> CTHInitArgs = term()</v>
</type>
<desc>
@@ -170,6 +174,10 @@
<p>With <c>userdata</c>, it is possible for the user to
specify arbitrary test suite related information which can be
read by calling <c>ct:userdata/2</c>.</p>
+
+ <p>The <c>ct_hooks</c> tag specifies which
+ <seealso marker="ct_hooks_chapter">Common Test Hooks</seealso>
+ are to be run together with this suite.</p>
<p>Other tuples than the ones defined will simply be ignored.</p>
@@ -337,7 +345,7 @@
</func>
<func>
- <name>Module:testcase() -> [Info] </name>
+ <name>Module:Testcase() -> [Info] </name>
<fsummary>Test case info function. </fsummary>
<type>
<v> Info = {timetrap,Time} | {require,Required} |
@@ -396,7 +404,7 @@
<func>
- <name>Module:testcase(Config) -> void() | {skip,Reason} | {comment,Comment} | {save_config,SaveConfig} | {skip_and_save,Reason,SaveConfig} | exit() </name>
+ <name>Module:Testcase(Config) -> void() | {skip,Reason} | {comment,Comment} | {save_config,SaveConfig} | {skip_and_save,Reason,SaveConfig} | exit() </name>
<fsummary>A test case</fsummary>
<type>
<v> Config = SaveConfig = [{Key,Value}]</v>
diff --git a/lib/common_test/doc/src/config_file_chapter.xml b/lib/common_test/doc/src/config_file_chapter.xml
index a22a5270c1..59151a73ec 100644
--- a/lib/common_test/doc/src/config_file_chapter.xml
+++ b/lib/common_test/doc/src/config_file_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,15 +13,15 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
- <title>Config Files</title>
+ <title>External Configuration Data</title>
<prepared>Siri Hansen, Peter Andersson</prepared>
<docno></docno>
<date></date>
@@ -32,16 +32,19 @@
<section>
<title>General</title>
- <p>The Common Test framework uses configuration files to
- describe data related to a test and/or an SUT (System Under Test).
- The configuration data makes it possible to change properties without
- changing the test program itself. Configuration data can for example be:</p>
+ <p>To avoid hard coding data values related to the test and/or SUT (System
+ Under Test) in the test suites, the data may instead be specified by means
+ of configuration files or strings that Common Test reads before
+ the start of a test run. External configuration data makes it possible to
+ change test properties without having to modify the actual test suites
+ using the data. Examples of configuration data:</p>
<list>
<item>Addresses to the test plant or other instruments</item>
- <item>Filenames for files needed by the test</item>
- <item>Program names for programs that shall be run by the test</item>
- <item>Any other variable that is needed by the test</item>
+ <item>User login information</item>
+ <item>Names of files needed by the test</item>
+ <item>Names of programs that should be executed during the test</item>
+ <item>Any other variable needed by the test</item>
</list>
</section>
@@ -51,12 +54,12 @@
<p>A configuration file can contain any number of elements of the type:</p>
<pre>
- {Key,Value}.</pre>
+ {CfgVarName,Value}.</pre>
<p>where</p>
<pre>
- Key = atom()
- Value = term() | [{Key,Value}]</pre>
+ CfgVarName = atom()
+ Value = term() | [{CfgVarName,Value}]</pre>
</section>
@@ -65,8 +68,8 @@
<marker id="require_config_data"></marker>
<p>In a test suite, one must <em>require</em> that a configuration
- variable exists before attempting to read the associated
- value in a test case.</p>
+ variable (<c>CfgVarName</c> in the definition above) exists before
+ attempting to read the associated value in a test case or config function.</p>
<p><c>require</c> is an assert statement that can be part of the <seealso
marker="write_test_chapter#suite">test suite info function</seealso> or
@@ -83,13 +86,13 @@
<p>A <c>require</c> statement in the test suite info- or test case
info-list should look like this:
- <c>{require,Required}</c> or <c>{require,Name,Required}</c>. The
- arguments <c>Name</c> and <c>Required</c> are the same as the
+ <c>{require,CfgVarName}</c> or <c>{require,AliasName,CfgVarName}</c>.
+ The arguments <c>AliasName</c> and <c>CfgVarName</c> are the same as the
arguments to <c>ct:require/[1,2]</c> which are described in the
reference manual for <seealso marker="ct">ct</seealso>.
- <c>Name</c> becomes an alias for the configuration variable
- <c>Required</c>, and can be used as reference to the configuration
- data value. The configuration variable may be associated with an
+ <c>AliasName</c> becomes an alias for the configuration variable,
+ and can be used as reference to the configuration data value.
+ The configuration variable may be associated with an
arbitrary number of alias names, but each name must be unique within
the same test suite. There are two main uses for alias names:</p>
<list>
@@ -180,7 +183,126 @@
</section>
<section>
- <title>Examples</title>
+ <title>User specific configuration data formats</title>
+
+ <p>It is possible for the user to specify configuration data on a
+ different format than key-value tuples in a text file, as described
+ so far. The data can e.g. be read from arbitrary files, fetched from
+ the web over http, or requested from a user specific process.
+ To support this, Common Test provides a callback module plugin
+ mechanism to handle configuration data.</p>
+
+ <section>
+ <title>Default callback modules for handling configuration data</title>
+ <p>The Common Test application includes default callback modules
+ for handling configuration data specified in standard config files
+ (see above) and in xml files:</p>
+ <list>
+ <item>
+ <c>ct_config_plain</c> - for reading configuration files with
+ key-value tuples (standard format). This handler will be used to
+ parse configuration files if no user callback is specified.
+ </item>
+ <item>
+ <c>ct_config_xml</c> - for reading configuration data from XML
+ files.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Using XML configuration files</title>
+ <p>This is an example of an XML configuration file:</p>
+ <pre><![CDATA[
+<config>
+ <ftp_host>
+ <ftp>"targethost"</ftp>
+ <username>"tester"</username>
+ <password>"letmein"</password>
+ </ftp_host>
+ <lm_directory>"/test/loadmodules"</lm_directory>
+</config>]]></pre>
+
+ <p>This configuration file, once read, will produce the same configuration
+ variables as the following text file:</p>
+ <pre>
+{ftp_host, [{ftp,"targethost"},
+ {username,"tester"},
+ {password,"letmein"}]}.
+
+{lm_directory, "/test/loadmodules"}.</pre>
+ </section>
+
+ <section>
+ <title>How to implement a user specific handler</title>
+
+ <p>The user specific handler can be written to handle special
+ configuration file formats. The parameter can be either file
+ name(s) or configuration string(s) (the empty list is valid).</p>
+
+ <p>The callback module implementing the handler is responsible for
+ checking correctness of configuration strings.</p>
+
+ <p>To perform validation of the configuration strings, the callback module
+ should have the following function exported:</p>
+
+ <p><c>Callback:check_parameter/1</c></p>
+ <p>The input argument will be passed from Common Test, as defined in the test
+ specification or given as an option to <c>ct_run</c> or <c>ct:run_test</c>.</p>
+
+ <p>The return value should be any of the following values indicating if given
+ configuration parameter is valid:</p>
+ <list>
+ <item>
+ <c>{ok, {file, FileName}}</c> - parameter is a file name and
+ the file exists,
+ </item>
+ <item>
+ <c>{ok, {config, ConfigString}}</c> - parameter is a config string
+ and it is correct,
+ </item>
+ <item>
+ <c>{error, {nofile, FileName}}</c> - there is no file with the given
+ name in the current directory,
+ </item>
+ <item>
+ <c>{error, {wrong_config, ConfigString}}</c> - the configuration string
+ is wrong.
+ </item>
+ </list>
+
+ <p>To perform reading of configuration data - initially before the tests
+ start, or as a result of data being reloaded during test execution -
+ the following function should be exported from the callback module:</p>
+
+ <p><c>Callback:read_config/1</c></p>
+
+ <p>The input argument is the same as for the <c>check_parameter/1</c> function.</p>
+ <p>The return value should be either:</p>
+
+ <list>
+ <item>
+ <c>{ok, Config}</c> - if the configuration variables are read successfully,
+ </item>
+ <item>
+ <c>{error, Error, ErrorDetails}</c> - if the callback module fails to
+ proceed with the given configuration parameters.
+ </item>
+ </list>
+ <p><c>Config</c> is the proper Erlang key-value list, with possible
+ key-value sublists as values, like for the configuration file
+ example above:</p>
+
+ <pre>
+ [{ftp_host, [{ftp, "targethost"}, {username, "tester"}, {password, "letmein"}]},
+ {lm_directory, "/test/loadmodules"}]</pre>
+
+ </section>
+
+ </section>
+
+ <section>
+ <title>Examples of configuration data handling</title>
<p>A config file for using the FTP client to access files on a remote
host could look like this:</p>
@@ -188,28 +310,33 @@
<pre>
{ftp_host, [{ftp,"targethost"},
{username,"tester"},
- {password,"letmein"}]}.
+ {password,"letmein"}]}.
{lm_directory, "/test/loadmodules"}.</pre>
- <p>Example of how to assert that the configuration data is available and
+
+ <p>The XML version shown in the chapter above can also be used, but it should be
+ explicitly specified that the <c>ct_config_xml</c> callback module is to be
+ used by Common Test.</p>
+
+ <p>Example of how to assert that the configuration data is available and
use it for an FTP session:</p>
<pre>
init_per_testcase(ftptest, Config) ->
{ok,_} = ct_ftp:open(ftp),
- Config.
+ Config.
end_per_testcase(ftptest, _Config) ->
ct_ftp:close(ftp).
ftptest() ->
[{require,ftp,ftp_host},
- {require,lm_directory}].
+ {require,lm_directory}].
ftptest(Config) ->
- Remote = filename:join(ct:get_config(lm_directory), "loadmodX"),
+ Remote = filename:join(ct:get_config(lm_directory), "loadmodX"),
Local = filename:join(?config(priv_dir,Config), "loadmodule"),
ok = ct_ftp:recv(ftp, Remote, Local),
- ...</pre>
+ ...</pre>
<p>An example of how the above functions could be rewritten
if necessary to open multiple connections to the FTP server:</p>
@@ -217,7 +344,7 @@
init_per_testcase(ftptest, Config) ->
{ok,Handle1} = ct_ftp:open(ftp_host),
{ok,Handle2} = ct_ftp:open(ftp_host),
- [{ftp_handles,[Handle1,Handle2]} | Config].
+ [{ftp_handles,[Handle1,Handle2]} | Config].
end_per_testcase(ftptest, Config) ->
lists:foreach(fun(Handle) -> ct_ftp:close(Handle) end,
@@ -225,17 +352,117 @@
ftptest() ->
[{require,ftp_host},
- {require,lm_directory}].
+ {require,lm_directory}].
ftptest(Config) ->
- Remote = filename:join(ct:get_config(lm_directory), "loadmodX"),
+ Remote = filename:join(ct:get_config(lm_directory), "loadmodX"),
Local = filename:join(?config(priv_dir,Config), "loadmodule"),
[Handle | MoreHandles] = ?config(ftp_handles,Config),
ok = ct_ftp:recv(Handle, Remote, Local),
- ...</pre>
+ ...</pre>
</section>
+ <section>
+ <title>Example of user specific configuration handler</title>
+ <p>A simple configuration handling driver which will ask an external server for
+ configuration data can be implemented this way:</p>
+ <pre>
+-module(config_driver).
+-export([read_config/1, check_parameter/1]).
+
+read_config(ServerName)->
+ ServerModule = list_to_atom(ServerName),
+ ServerModule:start(),
+ ServerModule:get_config().
+
+check_parameter(ServerName)->
+ ServerModule = list_to_atom(ServerName),
+ case code:is_loaded(ServerModule) of
+ {file, _}->
+ {ok, {config, ServerName}};
+ false->
+ case code:load_file(ServerModule) of
+ {module, ServerModule}->
+ {ok, {config, ServerName}};
+ {error, nofile}->
+ {error, {wrong_config, "File not found: " ++ ServerName ++ ".beam"}}
+ end
+ end.</pre>
+
+ <p>The configuration string for this driver may be "config_server", if the
+ config_server.erl module below is compiled and exists in the code path
+ during test execution:</p>
+ <pre>
+-module(config_server).
+-export([start/0, stop/0, init/1, get_config/0, loop/0]).
+
+-define(REGISTERED_NAME, ct_test_config_server).
+
+start()->
+ case whereis(?REGISTERED_NAME) of
+ undefined->
+ spawn(?MODULE, init, [?REGISTERED_NAME]),
+ wait();
+ _Pid->
+ ok
+ end,
+ ?REGISTERED_NAME.
+
+init(Name)->
+ register(Name, self()),
+ loop().
+
+get_config()->
+ call(self(), get_config).
+
+stop()->
+ call(self(), stop).
+
+call(Client, Request)->
+ case whereis(?REGISTERED_NAME) of
+ undefined->
+ {error, not_started, Request};
+ Pid->
+ Pid ! {Client, Request},
+ receive
+ Reply->
+ {ok, Reply}
+ after 4000->
+ {error, timeout, Request}
+ end
+ end.
+
+loop()->
+ receive
+ {Pid, stop}->
+ Pid ! ok;
+ {Pid, get_config}->
+ {D,T} = erlang:localtime(),
+ Pid !
+ [{localtime, [{date, D}, {time, T}]},
+ {node, erlang:node()},
+ {now, erlang:now()},
+ {config_server_pid, self()},
+ {config_server_vsn, ?vsn}],
+ ?MODULE:loop()
+ end.
+
+wait()->
+ case whereis(?REGISTERED_NAME) of
+ undefined->
+ wait();
+ _Pid->
+ ok
+ end.</pre>
+
+ <p>In this example, the handler also provides the ability to dynamically reload
+ configuration variables. If <c>ct:reload_config(localtime)</c> is called from
+ the test case function, all variables loaded with <c>config_driver:read_config/1</c>
+ will be updated with their latest values, and the new value for variable
+ <c>localtime</c> will be returned.</p>
+ </section>
+
</chapter>
diff --git a/lib/common_test/doc/src/cover_chapter.xml b/lib/common_test/doc/src/cover_chapter.xml
index 6e4f59ef73..b7162cb542 100644
--- a/lib/common_test/doc/src/cover_chapter.xml
+++ b/lib/common_test/doc/src/cover_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -94,10 +94,10 @@
<p>To activate the code coverage support, you simply specify the
name of the cover specification file as you start Common Test.
- This you do either by using the <c>-cover</c> flag with <c>run_test</c>.
+ This you do either by using the <c>-cover</c> flag with <c>ct_run</c>.
Example:</p>
- <p><c>$ run_test -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec</c></p>
+ <p><c>$ ct_run -dir $TESTOBJS/db -cover $TESTOBJS/db/config/db.coverspec</c></p>
<p>You may also pass the cover specification file name in a
call to <c>ct:run_test/1</c>, by adding a <c>{cover,CoverSpec}</c>
diff --git a/lib/common_test/doc/src/ct_hooks.xml b/lib/common_test/doc/src/ct_hooks.xml
new file mode 100644
index 0000000000..7d5c9f4750
--- /dev/null
+++ b/lib/common_test/doc/src/ct_hooks.xml
@@ -0,0 +1,556 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2010</year><year>2011</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Common Test Hooks</title>
+ <prepared>Lukas Larsson</prepared>
+ <responsible>Lukas Larsson</responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2010-12-02</date>
+ <rev>PA1</rev>
+ <file>ct_hooks.sgml</file>
+ </header>
+ <module>ct_hooks</module>
+ <modulesummary>A callback interface on top of Common Test</modulesummary>
+
+ <description>
+
+ <warning><p>This feature is in alpha release right now. This means that the
+ interface may change in the future and that there may be bugs. We
+ encourage you to use this feature, but be prepared
+ that there might be bugs and that the interface might change
+ inbetween releases.</p></warning>
+
+ <p>The <em>Common Test Hook</em> (henceforth called CTH) framework allows
+ extensions of the default behaviour of Common Test by means of callbacks
+ before and after all test suite calls. It is meant for advanced users of
+ Common Test which want to abstract out behaviour which is common to
+ multiple test suites. </p>
+
+ <p>In brief, Common Test Hooks allows you to:</p>
+
+ <list>
+ <item>Manipulate the runtime config before each suite
+ configuration call</item>
+ <item>Manipulate the return of all suite configuration calls and in
+ extension the result of the test themselves.</item>
+ </list>
+
+ <p>The following sections describe the mandatory and optional CTH
+ functions Common Test will call during test execution. For more details
+ see <seealso marker="ct_hooks_chapter">Common Test Hooks</seealso> in
+ the User's Guide.</p>
+
+ <p>For information about how to add a CTH to your suite see
+ <seealso marker="ct_hooks_chapter#installing">Installing a CTH
+ </seealso> in the User's Guide.</p>
+
+ <note><p>See the
+ <seealso marker="ct_hooks_chapter#example">Example CTH</seealso>
+ in the User's Guide for a minimal example of a CTH. </p></note>
+
+ </description>
+
+ <section>
+ <title>CALLBACK FUNCTIONS</title>
+ <p>The following functions define the callback interface
+ for a Common Test Hook.</p>
+ </section>
+
+ <funcs>
+ <func>
+ <name>Module:init(Id, Opts) -&gt; State</name>
+ <fsummary>Initiates the Common Test Hook</fsummary>
+ <type>
+ <v>Id = reference() | term()</v>
+ <v>Opts = term()</v>
+ <v>State = term()</v>
+ </type>
+
+ <desc>
+ <p> MANDATORY </p>
+
+ <p>Always called before any other callback function.
+ Use this to initiate any common state.
+ It should return a state for this CTH.</p>
+
+ <p><c>Id</c> is the return value of
+ <seealso marker="#Module:id-1">id/1</seealso>, or a <c>reference</c>
+ (created using
+ <seealso marker="erts:erlang#make_ref-0">make_ref/0</seealso>)
+ if <seealso marker="#Module:id-1">id/1</seealso> is not implemented.
+ </p>
+
+ <p>For details about when init is called see
+ <seealso marker="ct_hooks_chapter#scope">scope</seealso>
+ in the User's Guide.</p>
+
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:pre_init_per_suite(SuiteName, Config, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called before init_per_suite</fsummary>
+ <type>
+ <v>SuiteName = atom()</v>
+ <v>Config = NewConfig = [{Key,Value}]</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {Return, NewCTHState}</v>
+ <v>Return = NewConfig | SkipOrFail</v>
+ <v>SkipOrFail = {fail, Reason} | {skip, Reason}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called before
+ <seealso marker="common_test#Module:init_per_suite-1">
+ init_per_suite</seealso> if it exists.
+ It typically contains initialization/logging which needs to be done
+ before init_per_suite is called.
+ If <c>{skip,Reason}</c> or <c>{fail,Reason}</c> is returned,
+ init_per_suite and all test cases of the suite will be skipped and
+ Reason printed in the overview log of the suite.</p>
+
+ <p><c>SuiteName</c> is the name of the suite to be run.</p>
+
+ <p><c>Config</c> is the original config list of the test suite.</p>
+
+ <p><c>CTHState</c> is the current internal state of the CTH.</p>
+
+ <p><c>Return</c> is the result of the init_per_suite function.
+ If it is <c>{skip,Reason}</c> or <c>{fail,Reason}</c>
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite
+ </seealso> will never be called, instead the initiation is considered
+ to be skipped/failed respectively. If a <c>NewConfig</c> list
+ is returned, <seealso marker="common_test#Module:init_per_suite-1">
+ init_per_suite</seealso> will be called with that <c>NewConfig</c> list.
+ See <seealso marker="ct_hooks_chapter#pre">
+ Pre Hooks</seealso> in the User's Guide for more details.</p>
+
+
+ <p>Note that this function is only called if the CTH has been added
+ before init_per_suite is run, see
+ <seealso marker="ct_hooks_chapter#scope">CTH Scoping</seealso>
+ in the User's Guide for details.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:post_init_per_suite(SuiteName, Config, Return, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called after init_per_suite</fsummary>
+ <type>
+ <v>SuiteName = atom()</v>
+ <v>Config = [{Key,Value}]</v>
+ <v>Return = NewReturn = Config | SkipOrFail | term()</v>
+ <v>SkipOrFail = {fail, Reason} | {skip, Reason} | term()</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewReturn, NewCTHState}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:init_per_suite-1">
+ init_per_suite</seealso> if it exists. It typically contains extra
+ checks to make sure that all the correct dependencies have
+ been started correctly.</p>
+
+ <p><c>Return</c> is what
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite
+ </seealso> returned, i.e. {fail,Reason}, {skip,Reason}, a <c>Config</c>
+ list or a term describing how
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite
+ </seealso> failed.</p>
+
+ <p><c>NewReturn</c> is the possibly modified return value of
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite
+ </seealso>. It is here possible to recover from a failure in
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite
+ </seealso> by returning the <c>ConfigList</c> with the <c>tc_status</c>
+ element removed. See <seealso marker="ct_hooks_chapter#post">
+ Post Hooks</seealso> in the User's Guide for more details.</p>
+
+ <p><c>CTHState</c> is the current internal state of the CTH.</p>
+
+ <p>Note that this function is only called if the CTH has been added
+ before or in init_per_suite, see
+ <seealso marker="ct_hooks_chapter#scope">CTH Scoping</seealso>
+ in the User's Guide for details.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:pre_init_per_group(GroupName, Config, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called before init_per_group</fsummary>
+ <type>
+ <v>GroupName = atom()</v>
+ <v>Config = NewConfig = [{Key,Value}]</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called before
+ <seealso marker="common_test#Module:init_per_group-2">
+ init_per_group</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:pre_init_per_suite-3">
+ pre_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:init_per_group-2">
+ init_per_group</seealso> instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:post_init_per_group(GroupName, Config, Return, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called after init_per_group</fsummary>
+ <type>
+ <v>GroupName = atom()</v>
+ <v>Config = [{Key,Value}]</v>
+ <v>Return = NewReturn = Config | SkipOrFail | term()</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewReturn, NewCTHState}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:init_per_group-2">
+ init_per_group</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:post_init_per_suite-4">
+ post_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:init_per_group-2">
+ init_per_group</seealso> instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:pre_init_per_testcase(TestcaseName, Config, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called before init_per_testcase</fsummary>
+ <type>
+ <v>TestcaseName = atom()</v>
+ <v>Config = NewConfig = [{Key,Value}]</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called before
+ <seealso marker="common_test#Module:init_per_testcase-2">
+ init_per_testcase</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:pre_init_per_suite-3">
+ pre_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:init_per_testcase-2">
+ init_per_testcase</seealso> function instead.</p>
+
+ <p>Note that it is not possible to add CTH's here right now,
+ that feature might be added later,
+ but it would right now break backwards compatability.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:post_end_per_testcase(TestcaseName, Config, Return, CTHState)
+ -&gt; Result</name>
+ <fsummary>Called after end_per_testcase</fsummary>
+ <type>
+ <v>TestcaseName = atom()</v>
+ <v>Config = [{Key,Value}]</v>
+ <v>Return = NewReturn = Config | SkipOrFail | term()</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewReturn, NewCTHState}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:end_per_testcase-2">
+ end_per_testcase</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:post_init_per_suite-4">
+ post_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:end_per_testcase-2">
+ end_per_testcase</seealso> function instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:pre_end_per_group(GroupName, Config, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called before end_per_group</fsummary>
+ <type>
+ <v>GroupName = atom()</v>
+ <v>Config = NewConfig = [{Key,Value}]</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called before
+ <seealso marker="common_test#Module:end_per_group-2">
+ end_per_group</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:pre_init_per_suite-3">
+ pre_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:end_per_group-2">
+ end_per_group</seealso> function instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:post_end_per_group(GroupName, Config, Return, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called after end_per_group</fsummary>
+ <type>
+ <v>GroupName = atom()</v>
+ <v>Config = [{Key,Value}]</v>
+ <v>Return = NewReturn = Config | SkipOrFail | term()</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewReturn, NewCTHState}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:end_per_group-2">
+ end_per_group</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:post_init_per_suite-4">
+ post_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:end_per_group-2">
+ end_per_group</seealso> function instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:pre_end_per_suite(SuiteName, Config, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called before end_per_suite</fsummary>
+ <type>
+ <v>SuiteName = atom()</v>
+ <v>Config = NewConfig = [{Key,Value}]</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewConfig | SkipOrFail, NewCTHState}</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called before
+ <seealso marker="common_test#Module:end_per_suite-1">
+ end_per_suite</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:pre_init_per_suite-3">
+ pre_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:end_per_suite-1">
+ end_per_suite</seealso> function instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:post_end_per_suite(SuiteName, Config, Return, CTHState) -&gt;
+ Result</name>
+ <fsummary>Called after end_per_suite</fsummary>
+ <type>
+ <v>SuiteName = atom()</v>
+ <v>Config = [{Key,Value}]</v>
+ <v>Return = NewReturn = Config | SkipOrFail | term()</v>
+ <v>SkipOrFail = {fail,Reason} | {skip, Reason}</v>
+ <v>CTHState = NewCTHState = term()</v>
+ <v>Result = {NewReturn, NewCTHState}</v>
+ <v>Key = atom()</v>
+ <v>Value = term()</v>
+ <v>Reason = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called after
+ <seealso marker="common_test#Module:end_per_suite-1">
+ end_per_suite</seealso> if it exists. It behaves the same way as
+ <seealso marker="ct_hooks#Module:post_init_per_suite-4">
+ post_init_per_suite</seealso>, but for the
+ <seealso marker="common_test#Module:end_per_suite-1">
+ end_per_suite</seealso> function instead.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:on_tc_fail(TestcaseName, Reason, CTHState) -&gt;
+ NewCTHState</name>
+ <fsummary>Called after the CTH scope ends</fsummary>
+ <type>
+ <v>TestcaseName = init_per_suite | end_per_suite |
+ init_per_group | end_per_group | atom()</v>
+ <v>Reason = term()</v>
+ <v>CTHState = NewCTHState = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called whenever a testcase fails.
+ It is called after the post function has been called for
+ the testcase which failed. i.e.
+ if init_per_suite fails this function is called after
+ <seealso marker="#Module:post_init_per_suite-4">
+ post_init_per_suite</seealso>, and if a testcase fails it is called
+ after <seealso marker="#Module:post_end_per_testcase-4">
+ post_end_per_testcase</seealso>.</p>
+
+ <p>The data which comes with the Reason follows the same format as the
+ <seealso marker="event_handler_chapter#failreason">FailReason
+ </seealso> in the <seealso marker="event_handler_chapter#tc_done">tc_done</seealso> event.
+ See <seealso marker="event_handler_chapter#events">Event Handling
+ </seealso> in the User's Guide for details.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:on_tc_skip(TestcaseName, Reason, CTHState) -&gt;
+ NewCTHState</name>
+ <fsummary>Called after the CTH scope ends</fsummary>
+ <type>
+ <v>TestcaseName = end_per_suite | init_per_group |
+ end_per_group | atom()</v>
+ <v>Reason = {tc_auto_skip | tc_user_skip, term()}</v>
+ <v>CTHState = NewCTHState = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called whenever a testcase is skipped.
+ It is called after the post function has been called for the
+ testcase which was skipped.
+ i.e. if init_per_group is skipped this function is called after
+ <seealso marker="#Module:post_init_per_suite-4">post_init_per_group
+ </seealso>, and if a testcase is skipped it is called after
+ <seealso marker="#Module:post_end_per_testcase-4">post_end_per_testcase
+ </seealso>.</p>
+
+ <p>The data which comes with the Reason follows the same format as
+ <seealso marker="event_handler_chapter#tc_auto_skip">tc_auto_skip
+ </seealso> and <seealso marker="event_handler_chapter#tc_user_skip">
+ tc_user_skip</seealso> events.
+ See <seealso marker="event_handler_chapter#events">Event Handling
+ </seealso> in the User's Guide for details.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:terminate(CTHState)</name>
+ <fsummary>Called after the CTH scope ends</fsummary>
+ <type>
+ <v>CTHState = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>This function is called at the end of a CTH's
+ <seealso marker="ct_hooks_chapter#scope">scope</seealso>.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>Module:id(Opts) -&gt; Id</name>
+ <fsummary>Called before the init function of a CTH</fsummary>
+ <type>
+ <v>Opts = term()</v>
+ <v>Id = term()</v>
+ </type>
+
+ <desc>
+ <p> OPTIONAL </p>
+
+ <p>The <c>Id</c> is used to uniquely identify a CTH instance,
+ if two CTH's return the same <c>Id</c> the second CTH is ignored
+ and subsequent calls to the CTH will only be made to the first
+ instance. For more information see
+ <seealso marker="ct_hooks_chapter#installing">Installing a CTH
+ </seealso> in the User's Guide.
+ </p>
+
+ <p>This function should NOT have any side effects as it might
+ be called multiple times by Common Test.</p>
+
+ <p>If not implemented the CTH will act as if this function returned a
+ call to <c>make_ref/0</c>.</p>
+ </desc>
+ </func>
+
+ </funcs>
+
+</erlref>
+
+
diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml
new file mode 100644
index 0000000000..fc5ab48e1b
--- /dev/null
+++ b/lib/common_test/doc/src/ct_hooks_chapter.xml
@@ -0,0 +1,401 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2011</year><year>2011</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Common Test Hooks</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>ct_hooks_chapter.xml</file>
+ </header>
+
+ <marker id="general"></marker>
+ <section>
+ <title>General</title>
+ <warning><p>This feature is in alpha release right now. This means that the
+ interface may change in the future and that there may be bugs. We
+ encourage you to use this feature, but be prepared
+ that there might be bugs and that the interface might change
+ inbetween releases.</p></warning>
+ <p>
+ The <em>Common Test Hook</em> (henceforth called CTH) framework allows
+ extensions of the default behaviour of Common Test by means of hooks
+ before and after all test suite calls. CTHs allow advanced Common Test
+ users to abstract out behaviour which is common to multiple test suites
+ without littering all test suites with library calls. Some example
+ usages are: logging, starting and monitoring external systems,
+ building C files needed by the tests and much more!</p>
+
+ <p>In brief, Common Test Hooks allows you to:</p>
+
+ <list>
+ <item>Manipulate the runtime config before each suite
+ configuration call</item>
+ <item>Manipulate the return of all suite configuration calls and in
+ extension the result of the test themselves.</item>
+ </list>
+
+ <p>The following sections describe how to use CTHs, when they are run
+ and how to manipulate your test results in a CTH</p>
+
+ <warning><p>When executing within a CTH all timetraps are shutoff. So
+ if your CTH never returns, the entire test run will be stalled!</p>
+ </warning>
+
+ </section>
+
+ <marker id="installing"></marker>
+ <section>
+ <title>Installing a CTH</title>
+ <p>There are multiple ways to install a CTH in your test run. You can do it
+ for all tests in a run, for specific test suites and for specific groups
+ within a test suite. If you want a CTH to be present in all test suites
+ within your test run there are three different ways to accomplish that.
+ </p>
+
+ <list>
+ <item>Add <c>-ct_hooks</c> as an argument to
+ <seealso marker="run_test_chapter#ct_run">ct_run</seealso>.
+ To add multiple CTHs using this method append them to each other
+ using the keyword <c>and</c>, i.e.
+ <c>ct_run -ct_hooks cth1 [{debug,true}] and cth2 ...</c>.</item>
+ <item>Add the <c>ct_hooks</c> tag to your
+ <seealso marker="run_test_chapter#test_specifications">
+ Test Specification</seealso></item>
+ <item>Add the <c>ct_hooks</c> tag to your call to
+ <seealso marker="ct#run_test-1">ct:run_test/1</seealso></item>
+ </list>
+
+ <p>You can also add CTHs within a test suite. This is done by returning
+ <c>{ct_hooks,[CTH]}</c> in the config list from
+ <seealso marker="common_test#Module:suite-0">suite/0</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">
+ init_per_suite/1</seealso> or
+ <seealso marker="common_test#Module:init_per_group-2">
+ init_per_group/2</seealso>. <c>CTH</c> in this case can be either
+ only the module name of the CTH or a tuple with the module name and the
+ initial arguments to the CTH. Eg:
+ <c>{ct_hooks,[my_cth_module]}</c> or
+ <c>{ct_hooks,[{my_cth_module,[{debug,true}]}]}</c></p>
+
+ <section>
+ <title>Overriding CTHs</title>
+ <p>By default each installation of a CTH will cause a new instance of it
+ to be activated. This can cause problems if you want to be able to
+ override CTHs in test specifications while still having them in the
+ suite info function. The
+ <seealso marker="ct_hooks#Module:id-1">id/1</seealso>
+ callback exists to address this problem. By returning the same
+ <c>id</c> in both places, Common Test knows that this CTH
+ has already been installed and will not try to install it again.</p>
+ </section>
+
+ </section>
+
+ <marker id="scope"/>
+ <section>
+ <title>CTH Scope</title>
+ <p>Once the CTH is installed into a certain test run it will be there until
+ its scope is expired. The scope of a CTH depends on when it is
+ installed.
+ The <seealso marker="ct_hooks#Module:init-2">init/2</seealso> is
+ called at the beginning of the scope and the
+ <seealso marker="ct_hooks#Module:terminate-1">terminate/1
+ </seealso> function is called when the scope ends.</p>
+ <table>
+ <row>
+ <cell><em>CTH Installed in</em></cell>
+ <cell><em>CTH scope begins before</em></cell>
+ <cell><em>CTH scope ends after</em></cell>
+ </row>
+ <row>
+ <cell><seealso marker="run_test_chapter#ct_run">ct_run</seealso></cell>
+ <cell>the first test suite is to be run.</cell>
+ <cell>the last test suite has been run.</cell>
+ </row>
+ <row>
+ <cell><seealso marker="ct#run_test-1">ct:run_test</seealso></cell>
+ <cell>the first test suite is to be run.</cell>
+ <cell>the last test suite has been run.</cell>
+ </row>
+ <row>
+ <cell><seealso marker="run_test_chapter#test_specifications">
+ Test Specification</seealso></cell>
+ <cell>the first test suite is to be run.</cell>
+ <cell>the last test suite has been run.</cell>
+ </row>
+ <row>
+ <cell><seealso marker="common_test#Module:suite-0">suite/0
+ </seealso></cell>
+ <cell><seealso marker="ct_hooks#Module:pre_init_per_suite-3">
+ pre_init_per_suite/3</seealso> is called.</cell>
+ <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4">
+ post_end_per_suite/4</seealso> has been called for that test suite.</cell>
+ </row>
+ <row>
+ <cell><seealso marker="common_test#Module:init_per_suite-1">
+ init_per_suite/1</seealso></cell>
+ <cell><seealso marker="ct_hooks#Module:post_init_per_suite-4">
+ post_init_per_suite/4</seealso> is called.</cell>
+ <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4">
+ post_end_per_suite/4</seealso> has been called for that test suite.</cell>
+ </row>
+ <row>
+ <cell><seealso marker="common_test#Module:init_per_group-2">
+ init_per_group/2</seealso></cell>
+ <cell><seealso marker="ct_hooks#Module:post_init_per_group-4">
+ post_init_per_group/4</seealso> is called.</cell>
+ <cell><seealso marker="ct_hooks#Module:post_end_per_suite-4">
+ post_end_per_group/4</seealso> has been called for that group.</cell>
+ </row>
+ <tcaption>Scope of a CTH</tcaption>
+ </table>
+
+ <section>
+ <title>CTH Processes and Tables</title>
+ <p>CTHs are run with the same process scoping as normal test suites
+ i.e. a different process will execute the init_per_suite hooks then the
+ init_per_group or per_testcase hooks. So if you want to spawn a
+ process in the CTH you cannot link with the CTH process as it will exit
+ after the post hook ends. Also if you for some reason need an ETS
+ table with your CTH, you will have to spawn a process which handles
+ it.</p>
+ </section>
+
+ </section>
+
+ <marker id="manipulating"/>
+ <section>
+ <title>Manipulating tests</title>
+ <p>It is through CTHs possible to manipulate the results of tests and
+ configuration functions. The main purpose of doing this with CTHs is to
+ allow common patterns to be abstracted out from test test suites and applied to
+ multiple test suites without duplicating any code. All of the callback
+ functions for a CTH follow a common interface, this interface is
+ described below.</p>
+
+ <p>It is only possible to hook into test function which exists in the test
+ suite. So in order for a CTH to hook in before
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>,
+ the <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>
+ function must exist in the test suite.</p>
+
+ <marker id="pre"/>
+ <section>
+ <title>Pre Hooks</title>
+ <p>
+ It is possible in a CTH to hook in behaviour before
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_group</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_testcase</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">end_per_group</seealso> and
+ <seealso marker="common_test#Module:init_per_suite-1">end_per_suite</seealso>.
+ This is done in the CTH functions called pre_&lt;name of function&gt;.
+ All of these functions take the same three arguments: <c>Name</c>,
+ <c>Config</c> and <c>CTHState</c>. The return value of the CTH function
+ is always a combination of an result for the suite/group/test and an
+ updated <c>CTHState</c>. If you want the test suite to continue on
+ executing you should return the config list which you want the test to
+ use as the result. If you for some reason want to skip/fail the test,
+ return a tuple with <c>skip</c> or <c>fail</c> and a reason as the
+ result. Example:
+ </p>
+ <code>pre_init_per_suite(SuiteName, Config, CTHState) -&gt;
+ case db:connect() of
+ {error,_Reason} -&gt;
+ {{fail, "Could not connect to DB"}, CTHState};
+ {ok, Handle} -&gt;
+ {[{db_handle, Handle} | Config], CTHState#state{ handle = Handle }}
+ end.</code>
+
+ </section>
+
+ <marker id="post"/>
+ <section>
+ <title>Post Hooks</title>
+ <p>It is also possible in a CTH to hook in behaviour after
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">init_per_group</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">end_per_testcase</seealso>,
+ <seealso marker="common_test#Module:init_per_suite-1">end_per_group</seealso> and
+ <seealso marker="common_test#Module:init_per_suite-1">end_per_suite</seealso>.
+ This is done in the CTH functions called post_&lt;name of function&gt;.
+ All of these function take the same four arguments: <c>Name</c>,
+ <c>Config</c>, <c>Return</c> and <c>CTHState</c>. <c>Config</c> in this
+ case is the same <c>Config</c> as the testcase is called with.
+ <c>Return</c> is the value returned by the testcase. If the testcase
+ failed by crashing, <c>Return</c> will be
+ <c>{'EXIT',{{Error,Reason},Stacktrace}}</c>.</p>
+
+ <p>The return value of the CTH function is always a combination of an
+ result for the suite/group/test and an updated <c>CTHState</c>. If
+ you want the callback to not affect the outcome of the test you should
+ return the <c>Return</c> data as it is given to the CTH. You can also
+ modify the result of the test. By returning the <c>Config</c> list
+ with the <c>tc_status</c> element removed you can recover from a test
+ failure. As in all the pre hooks, it is also possible to fail/skip
+ the test case in the post hook. Example: </p>
+
+ <code>post_end_per_testcase(_TC, Config, {'EXIT',{_,_}}, CTHState) -&gt;
+ case db:check_consistency() of
+ true ->
+ %% DB is good, pass the test.
+ {proplists:delete(tc_status, Config), CTHState};
+ false ->
+ %% DB is not good, mark as skipped instead of failing
+ {{skip, "DB is inconsisten!"}, CTHState}
+ end;
+post_end_per_testcase(_TC, Config, Return, CTHState) -&gt;
+ %% Do nothing if tc does not crash.
+ {Return, CTHState}.</code>
+
+ <note>Recovering from a testcase failure using CTHs should only be done as
+ a last resort. If used wrongly it could become very difficult to
+ determine which tests pass or fail in a test run</note>
+
+ </section>
+
+ <marker id="skip_n_fail"/>
+ <section>
+ <title>Skip and Fail hooks</title>
+ <p>
+ After any post hook has been executed for all installed CTHs,
+ <seealso marker="ct_hooks#Module:on_tc_fail-3">on_tc_fail</seealso>
+ or <seealso marker="ct_hooks#Module:on_tc_fail-3">on_tc_skip</seealso>
+ might be called if the testcase failed or was skipped
+ respectively. You cannot affect the outcome of the tests any further at
+ this point.
+ </p>
+ </section>
+
+ </section>
+
+ <marker id="example"/>
+ <section>
+ <title>Example CTH</title>
+ <p>The CTH below will log information about a test run into a format
+ parseable by <seealso marker="kernel:file#consult-1">file:consult/1</seealso>.
+ </p>
+ <code>%%% @doc Common Test Example Common Test Hook module.
+-module(example_cth).
+
+%% Callbacks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-record(state, { file_handle, total, suite_total, ts, tcs, data }).
+
+%% @doc Return a unique id for this CTH.
+id(Opts) ->
+ proplists:get_value(filename, Opts, "/tmp/file.log").
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state.
+init(Id, Opts) ->
+ {ok,D} = file:open(Id,[write]),
+ #state{ file_handle = D, total = 0, data = [] }.
+
+%% @doc Called before init_per_suite is called.
+pre_init_per_suite(Suite,Config,State) ->
+ {Config, State#state{ suite_total = 0, tcs = [] }}.
+
+%% @doc Called after init_per_suite.
+post_init_per_suite(Suite,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before end_per_suite.
+pre_end_per_suite(Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after end_per_suite.
+post_end_per_suite(Suite,Config,Return,State) ->
+ Data = {suites, Suite, State#state.suite_total, lists:reverse(State#state.tcs)},
+ {Return, State#state{ data = [Data | State#state.data] ,
+ total = State#state.total + State#state.suite_total } }.
+
+%% @doc Called before each init_per_group.
+pre_init_per_group(Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+post_init_per_group(Group,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called after each end_per_group.
+pre_end_per_group(Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each end_per_group.
+post_end_per_group(Group,Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each test case.
+pre_init_per_testcase(TC,Config,State) ->
+ {Config, State#state{ ts = now(), total = State#state.suite_total + 1 } }.
+
+%% @doc Called after each test case.
+post_end_per_testcase(TC,Config,Return,State) ->
+ TCInfo = {testcase, TC, Return, timer:now_diff(now(), State#state.ts)},
+ {Return, State#state{ ts = undefined, tcs = [TCInfo | State#state.tcs] } }.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_testcase if the suite, group or test case failed.
+on_tc_fail(TC, Reason, State) ->
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing.
+on_tc_skip(TC, Reason, State) ->
+ State.
+
+%% @doc Called when the scope of the CTH is done
+terminate(State) ->
+ io:format(State#state.file_handle, "~p.~n",
+ [{test_run, State#state.total, State#state.data}]),
+ file:close(State#state.file_handle),
+ ok.</code>
+ </section>
+
+</chapter>
+
+
+
+
diff --git a/lib/common_test/doc/src/ct_master_chapter.xml b/lib/common_test/doc/src/ct_master_chapter.xml
index 79288cfe4c..f4f0ecad62 100644
--- a/lib/common_test/doc/src/ct_master_chapter.xml
+++ b/lib/common_test/doc/src/ct_master_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Using Common Test for Large Scale Testing</title>
@@ -30,6 +30,7 @@
</header>
<section>
+ <marker id="general"></marker>
<title>General</title>
<p>Large scale automated testing requires running multiple independent
test sessions in parallel. This is accomplished by running
@@ -102,9 +103,10 @@
<p><c>ct_master:abort()</c> (stop all) or <c>ct_master:abort(Nodes)</c></p>
<p>For detailed information about the <c>ct_master</c> API, please see the
- manual page for this module.</p>
+ <seealso marker="ct_master">manual page</seealso> for this module.</p>
</section>
<section>
+ <marker id="test_specifications"></marker>
<title>Test Specifications</title>
<p>The test specifications used as input to CT Master are fully compatible with the
specifications used as input to the regular CT server. The syntax is described in the
@@ -186,7 +188,7 @@
<seealso marker="run_test_chapter#test_specifications">Running Test Suites</seealso>
chapter). The result is that any test specified to run on a node with the same
name as the Common Test node in question (typically <c>ct@somehost</c> if started
- with the <c>run_test</c> script), will be performed. Tests without explicit
+ with the <c>ct_run</c> program), will be performed. Tests without explicit
node association will always be performed too of course!</p>
<note><p>It is recommended that absolute paths are used for log directories,
@@ -194,6 +196,56 @@
current working directory settings are not important.</p></note>
</section>
+ <section>
+ <title>Automatic startup of test target nodes</title>
+ <marker id="ct_slave"></marker>
+ <p>Is is possible to automatically start, and perform initial actions, on
+ test target nodes by using the test specification term <c>init</c>.</p>
+ <p>Currently, two sub-terms are supported, <c>node_start</c> and <c>eval</c>.</p>
+ <p>Example:</p>
+ <pre>
+ {node, node1, node1@host1}.
+ {node, node2, node1@host2}.
+ {node, node3, node2@host2}.
+ {node, node4, node1@host3}.
+ {init, node1, [{node_start, [{callback_module, my_slave_callback}]}]}.
+ {init, [node2, node3], {node_start, [{username, "ct_user"}, {password, "ct_password"}]}}.
+ {init, node4, {eval, {module, function, []}}}.</pre>
+
+ <p>This test specification declares that <c>node1@host1</c> is to be started using
+ the user callback function <c>callback_module:my_slave_callback/0</c>, and nodes
+ <c>node1@host2</c> and <c>node2@host2</c> will be started with the default callback
+ module <c>ct_slave</c>. The given user name and password is used to log into remote
+ host <c>host2</c>. Also, the function <c>module:function/0</c> will be evaluated on
+ <c>node1@host3</c>, and the result of this call will be printed to the log.</p>
+
+ <p>The default <seealso marker="ct_slave">ct_slave</seealso> callback module,
+ which is part of the Common Test application, has the following features:
+ <list>
+ <item>Starting Erlang target nodes on local or remote hosts
+ (ssh is used for communication).
+ </item>
+ <item>Ability to start an Erlang emulator with additional flags
+ (any flags supported by <c>erl</c> are supported).
+ </item>
+ <item>Supervision of a node being started by means of internal callback
+ functions. Used to prevent hanging nodes. (Configurable).
+ </item>
+ <item>Monitoring of the master node by the slaves. A slave node may be
+ stopped in case the master node terminates. (Configurable).
+ </item>
+ <item>Execution of user functions after a slave node is started.
+ Functions can be given as a list of {Module, Function, Arguments} tuples.
+ </item>
+ </list>
+ </p>
+ <p>Note that it is possible to specify an <c>eval</c> term for the node as well
+ as <c>startup_functions</c> in the <c>node_start</c> options list. In this
+ case first the node will be started, then the <c>startup_functions</c> are
+ executed, and finally functions specified with <c>eval</c> are called.
+ </p>
+ </section>
+
</chapter>
diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml
new file mode 100644
index 0000000000..1ab563d74f
--- /dev/null
+++ b/lib/common_test/doc/src/ct_run.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE comref SYSTEM "comref.dtd">
+
+<comref>
+ <header>
+ <copyright>
+ <year>2007</year><year>2010</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>The ct_run program</title>
+ <prepared>Peter Andersson</prepared>
+ <responsible>Peter Andersson</responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2010-04-01</date>
+ <rev>PA2</rev>
+ <file>ct_run.xml</file>
+ </header>
+ <com>ct_run</com>
+ <comsummary>Program used for starting Common Test from the
+ OS command line.
+ </comsummary>
+
+ <description>
+ <p>The <c>ct_run</c> program is automatically installed with Erlang/OTP
+ and Common Test (please see the Installation chapter in the Common
+ Test User's Guide for more information). The program accepts a number
+ of different start flags. Some flags trigger <c>ct_run</c>
+ to start the Common Test application and pass on data to it. Some
+ flags start an Erlang node prepared for running Common Test in a
+ particular mode.</p>
+
+ <p>There is an interface function that corresponds to this program,
+ called <c>ct:run_test/1</c>, for starting Common Test from the Erlang
+ shell (or an Erlang program). Please see the <c>ct</c> man page for
+ details.</p>
+
+ <p><c>ct_run</c> also accepts Erlang emulator flags. These are used
+ when <c>ct_run</c> calls <c>erl</c> to start the Erlang node
+ (making it possible to e.g. add directories to the code server path,
+ change the cookie on the node, start additional applications, etc).</p>
+
+ <p>With the optional flag:</p>
+ <pre>-erl_args</pre>
+ <p>it's possible to divide the options on the <c>ct_run</c> command line into
+ two groups, one that Common Test should process (those preceding <c>-erl_args</c>),
+ and one it should completely ignore and pass on directly to the emulator
+ (those following <c>-erl_args</c>). Options preceding <c>-erl_args</c> that Common Test
+ doesn't recognize, also get passed on to the emulator untouched.
+ By means of <c>-erl_args</c> the user may specify flags with the same name, but
+ with different destinations, on the <c>ct_run</c> command line.</p>
+ <p>If <c>-pa</c> or <c>-pz</c> flags are specified in the Common Test group of options
+ (preceding <c>-erl_args</c>), relative directories will be converted to
+ absolute and re-inserted into the code path by Common Test (to avoid
+ problems loading user modules when Common Test changes working directory
+ during test runs). Common Test will however ignore <c>-pa</c> and <c>-pz</c> flags
+ following <c>-erl_args</c> on the command line. These directories are added
+ to the code path normally (i.e. on specified form)</p>
+
+ <p>If <c>ct_run</c> is called with option:</p>
+ <pre>-help</pre>
+ <p>it prints all valid start flags to stdout.</p>
+ </description>
+
+ <marker id="ct_run"></marker>
+
+ <section>
+ <title>Run tests from command line</title>
+ <pre>
+ ct_run [-dir TestDir1 TestDir2 .. TestDirN] |
+ [-suite Suite1 Suite2 .. SuiteN
+ [[-group Group1 Group2 .. GroupN] [-case Case1 Case2 .. CaseN]]]
+ [-step [config | keep_inactive]]
+ [-config ConfigFile1 ConfigFile2 .. ConfigFileN]
+ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2
+ ConfigString2 and .. and CallbackModuleN ConfigStringN]
+ [-decrypt_key Key] | [-decrypt_file KeyFile]
+ [-label Label]
+ [-logdir LogDir]
+ [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
+ [-stylesheet CSSFile]
+ [-cover CoverCfgFile]
+ [-event_handler EvHandler1 EvHandler2 .. EvHandlerN] |
+ [-event_handler_init EvHandler1 InitArg1 and
+ EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
+ [-include InclDir1 InclDir2 .. InclDirN]
+ [-no_auto_compile]
+ [-muliply_timetraps Multiplier]
+ [-scale_timetraps]
+ [-repeat N [-force_stop]] |
+ [-duration HHMMSS [-force_stop]] |
+ [-until [YYMoMoDD]HHMMSS [-force_stop]]
+ [-basic_html]</pre>
+ </section>
+ <section>
+ <title>Run tests using test specification</title>
+ <pre>
+ ct_run -spec TestSpec1 TestSpec2 .. TestSpecN
+ [-config ConfigFile1 ConfigFile2 .. ConfigFileN]
+ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2
+ ConfigString2 and .. and CallbackModuleN ConfigStringN]
+ [-decrypt_key Key] | [-decrypt_file KeyFile]
+ [-label Label]
+ [-logdir LogDir]
+ [-allow_user_terms]
+ [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
+ [-stylesheet CSSFile]
+ [-cover CoverCfgFile]
+ [-event_handler EvHandler1 EvHandler2 .. EvHandlerN] |
+ [-event_handler_init EvHandler1 InitArg1 and
+ EvHandler2 InitArg2 and .. EvHandlerN InitArgN]
+ [-include InclDir1 InclDir2 .. InclDirN]
+ [-no_auto_compile]
+ [-muliply_timetraps Multiplier]
+ [-scale_timetraps]
+ [-repeat N [-force_stop]] |
+ [-duration HHMMSS [-force_stop]] |
+ [-until [YYMoMoDD]HHMMSS [-force_stop]]
+ [-basic_html]</pre>
+ </section>
+ <section>
+ <title>Run tests in web based GUI</title>
+ <pre>
+ ct_run -vts [-browser Browser]
+ [-dir TestDir1 TestDir2 .. TestDirN] |
+ [-suite Suite [[-group Group] [-case Case]]]
+ [-config ConfigFile1 ConfigFile2 .. ConfigFileN]
+ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2
+ ConfigString2 and .. and CallbackModuleN ConfigStringN]
+ [-decrypt_key Key] | [-decrypt_file KeyFile]
+ [-include InclDir1 InclDir2 .. InclDirN]
+ [-no_auto_compile]
+ [-muliply_timetraps Multiplier]
+ [-scale_timetraps]
+ [-basic_html]</pre>
+ </section>
+ <section>
+ <title>Refresh the HTML index files</title>
+ <pre>
+ ct_run -refresh_logs [-logdir LogDir] [-basic_html]</pre>
+ </section>
+ <section>
+ <title>Run CT in interactive mode</title>
+ <pre>
+ ct_run -shell
+ [-config ConfigFile1 ConfigFile2 ... ConfigFileN]
+ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2
+ ConfigString2 and .. and CallbackModuleN ConfigStringN]
+ [-decrypt_key Key] | [-decrypt_file KeyFile]</pre>
+ </section>
+ <section>
+ <title>Start a Common Test Master node</title>
+ <pre>
+ ct_run -ctmaster</pre>
+ </section>
+
+ <section>
+ <title>See also</title>
+ <p>Please read the <seealso marker="run_test_chapter">Running Test Suites</seealso>
+ chapter in the Common Test User's Guide for information about the meaning of the
+ different start flags.</p>
+ </section>
+
+</comref>
diff --git a/lib/common_test/doc/src/event_handler_chapter.xml b/lib/common_test/doc/src/event_handler_chapter.xml
index a550810850..b41b233ce6 100644
--- a/lib/common_test/doc/src/event_handler_chapter.xml
+++ b/lib/common_test/doc/src/event_handler_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Event Handling</title>
@@ -61,36 +61,35 @@
itself.</p>
</section>
<section>
+ <marker id="usage"></marker>
<title>Usage</title>
<p>Event handlers may be installed by means of an <c>event_handler</c>
- start flag (<c>run_test</c>) or option (<c>ct:run_test/1</c>), where the
+ start flag (<c>ct_run</c>) or option (<c>ct:run_test/1</c>), where the
argument specifies the names of one or more event handler modules.
Example:</p>
- <p><c>$ run_test -suite test/my_SUITE -event_handler handlers/my_evh1
+ <p><c>$ ct_run -suite test/my_SUITE -event_handler handlers/my_evh1
handlers/my_evh2 -pa $PWD/handlers</c></p>
+ <p>Use the <c><![CDATA[ct_run -event_handler_init]]></c> option instead of
+ <c><![CDATA[-event_handler]]></c> to pass start arguments to the event handler
+ init function.</p>
<p>All event handler modules must have gen_event behaviour. Note also that
these modules must be precompiled, and that their locations must be
added explicitly to the Erlang code server search path (like in the
example).</p>
- <p>It is not possible to specify start arguments to the event handlers when
- using the <c>run_test</c> script. You may however pass along start arguments
- if you use the <c>ct:run_test/1</c> function. An event_handler tuple in the argument
- <c>Opts</c> has the following definition (see also <c>ct:run_test/1</c> in the
- reference manual):</p>
+ <p>An event_handler tuple in the argument <c>Opts</c> has the following
+ definition (see also <c>ct:run_test/1</c> in the reference manual):</p>
<pre>
{event_handler,EventHandlers}
EventHandlers = EH | [EH]
EH = atom() | {atom(),InitArgs} | {[atom()],InitArgs}
- InitArgs = [term()]
- </pre>
+ InitArgs = [term()]</pre>
<p>Example:</p>
<pre>
- 1> ct:run_test([{suite,"test/my_SUITE"},{event_handler,[my_evh1,{my_evh2,[node()]}]}]).
- </pre>
+ 1> ct:run_test([{suite,"test/my_SUITE"},{event_handler,[my_evh1,{my_evh2,[node()]}]}]).</pre>
<p>This will install two event handlers for the <c>my_SUITE</c> test. Event handler
<c>my_evh1</c> is started with <c>[]</c> as argument to the init function. Event handler
<c>my_evh2</c> is started with the name of the current node in the init argument list.</p>
@@ -122,6 +121,7 @@
node the event has originated from (only relevant for CT Master event handlers).
<c>data</c> is specific for the particular event.</p>
+ <marker id="events"></marker>
<p><em>General events:</em></p>
<list>
@@ -174,6 +174,7 @@
are also given.
</p></item>
+ <marker id="tc_done"/>
<item><c>#event{name = tc_done, data = {Suite,FuncOrGroup,Result}}</c>
<p><c>Suite = atom()</c>, name of the suite.</p>
<p><c>FuncOrGroup = Func | {Conf,GroupName,GroupProperties}</c></p>
@@ -183,12 +184,14 @@
(unknown if init- or end function times out).</p>
<p><c>GroupProperties = list()</c>, list of execution properties for the group.</p>
<p><c>Result = ok | {skipped,SkipReason} | {failed,FailReason}</c>, the result.</p>
+ <marker id="skipreason"/>
<p><c>SkipReason = {require_failed,RequireInfo} |
{require_failed_in_suite0,RequireInfo} |
{failed,{Suite,init_per_testcase,FailInfo}} |
UserTerm</c>,
the reason why the case has been skipped.</p>
- <p><c>FailReason = {error,FailInfo} |
+ <marker id="failreason"/>
+ <p><c>FailReason = {error,FailInfo} |
{error,{RunTimeError,StackTrace}} |
{timetrap_timeout,integer()} |
{failed,{Suite,end_per_testcase,FailInfo}}</c>, reason for failure.</p>
@@ -211,6 +214,7 @@
<c>end_per_testcase</c> for the case failed.
</p></item>
+ <marker id="tc_auto_skip"></marker>
<item><c>#event{name = tc_auto_skip, data = {Suite,Func,Reason}}</c>
<p><c>Suite = atom()</c>, the name of the suite.</p>
<p><c>Func = atom()</c>, the name of the test case or configuration function.</p>
@@ -236,7 +240,8 @@
skipped because of <c>init_per_testcase</c> failing, since that information is carried with
the <c>tc_done</c> event.
</p></item>
-
+
+ <marker id="tc_user_skip"></marker>
<item><c>#event{name = tc_user_skip, data = {Suite,TestCase,Comment}}</c>
<p><c>Suite = atom()</c>, name of the suite.</p>
<p><c>TestCase = atom()</c>, name of the test case.</p>
diff --git a/lib/common_test/doc/src/install_chapter.xml b/lib/common_test/doc/src/install_chapter.xml
index e1ff5abf6a..89c497962d 100644
--- a/lib/common_test/doc/src/install_chapter.xml
+++ b/lib/common_test/doc/src/install_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2007</year><year>2009</year>
+ <year>2007</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Installation</title>
@@ -33,82 +33,77 @@
<marker id="general"></marker>
<title>General information</title>
- <p>The two main interfaces for running tests with Common Test
- are an executable Bourne shell script named <c>run_test</c> and an
- erlang module named <c>ct</c>. The shell script will work on Unix/Linux
- (and Linux-like environments such as Cygwin on Windows) and the
- <c>ct</c> interface functions can be called from the Erlang shell
- (or from any Erlang function) on any supported platform.</p>
-
- <p>The Common Test application is installed with the Erlang/OTP
- system and no explicit installation is required to start using
- Common Test by means of the interface functions in the <c>ct</c>
- module. If you wish to use <c>run_test</c>, however, this script
- needs to be generated first, according to the instructions below.</p>
- </section>
+ <p>The two main interfaces for running tests with Common Test
+ are an executable program named ct_run and an
+ erlang module named <c>ct</c>. The ct_run program
+ is compiled for the underlying operating system (e.g. Unix/Linux
+ or Windows) during the build of the Erlang/OTP system, and is
+ installed automatically with other executable programs in
+ the top level <c>bin</c> directory of Erlang/OTP.
+ The <c>ct</c> interface functions can be called from the Erlang shell,
+ or from any Erlang function, on any supported platform.</p>
+
+ <p>A legacy Bourne shell script - named run_test - exists,
+ which may be manually generated and installed. This script may be used
+ instead of the ct_run program mentioned above, e.g. if the user
+ wishes to modify or customize the Common Test start flags in a simpler
+ way than making changes to the ct_run C program.</p>
- <section>
- <title>Unix/Linux</title>
-
- <p>Go to the <c><![CDATA[common_test-<vsn>]]></c> directory, located
- among the other OTP applications (under the OTP lib directory). Here you
- execute the <c>install.sh</c> script with argument <c>local</c>:</p>
-
- <p><c>
- $ ./install.sh local
- </c></p>
+ <p>The Common Test application is installed with the Erlang/OTP
+ system and no additional installation step is required to start using
+ Common Test by means of the ct_run executable program, and/or the interface
+ functions in the <c>ct</c> module. If you wish to use the legacy Bourne
+ shell script version run_test, however, this script needs to be
+ generated first, according to the instructions below.</p>
+
+ <p><note>Before reading on, please note that since Common Test version
+ 1.5, the run_test shell script is no longer required for starting
+ tests with Common Test from the OS command line. The ct_run
+ program (descibed above) is the new recommended command line interface
+ for Common Test. The shell script exists mainly for legacy reasons and
+ may not be updated in future releases of Common Test. It may even be removed.
+ </note></p>
+
+ <p>Optional step to generate a shell script for starting Common Test:</p>
+ <p>To generate the run_test shell script, navigate to the
+ <c><![CDATA[common_test-<vsn>]]></c> directory, located among the other
+ OTP applications (under the OTP lib directory). Here execute the
+ <c>install.sh</c> script with argument <c>local</c>:</p>
+
+ <p><c>
+ $ ./install.sh local
+ </c></p>
- <p>This generates the executable <c>run_test</c> script in the
- <c><![CDATA[common_test-<vsn>/priv/bin]]></c> directory. The script
- will include absolute paths to the Common Test and Test Server
- application directories, so it's possible to copy or move the script to
- a different location on the file system, if desired, without having to
- update it. It's of course possible to leave the script under the
- <c>priv/bin</c> directory and update the PATH variable accordingly (or
- create a link or alias to it).</p>
-
- <p>If you, for any reason, have copied Common Test and Test Server
- to a different location than the default OTP lib directory, you can
- generate a <c>run_test</c> script with a different top level directory,
- simply by specifying the directory, instead of <c>local</c>, when running
- <c>install.sh</c>. Example:</p>
-
- <p><c>
- $ install.sh /usr/local/test_tools
+ <p>This generates the executable run_test script in the
+ <c><![CDATA[common_test-<vsn>/priv/bin]]></c> directory. The script
+ will include absolute paths to the Common Test and Test Server
+ application directories, so it's possible to copy or move the script to
+ a different location on the file system, if desired, without having to
+ update it. It's of course possible to leave the script under the
+ <c>priv/bin</c> directory and update the PATH variable accordingly (or
+ create a link or alias to it).</p>
+
+ <p>If you, for any reason, have copied Common Test and Test Server
+ to a different location than the default OTP lib directory, you can
+ generate a run_test script with a different top level directory,
+ simply by specifying the directory, instead of <c>local</c>, when running
+ <c>install.sh</c>. Example:</p>
+
+ <p><c>
+ $ install.sh /usr/local/test_tools
</c></p>
<p>Note that the <c><![CDATA[common_test-<vsn>]]></c> and
- <c><![CDATA[test_server-<vsn>]]></c> directories must be located under the
- same top directory. Note also that the install script does not copy files
- or update environment variables. It only generates the <c>run_test</c>
- script.</p>
+ <c><![CDATA[test_server-<vsn>]]></c> directories must be located under the
+ same top directory. Note also that the install script does not copy files
+ or update environment variables. It only generates the run_test
+ script.</p>
- <p>Whenever you install a new version of Erlang/OTP, the <c>run_test</c>
- script needs to be regenerated, or updated manually with new directory names
- (new version numbers), for it to "see" the latest Common Test and Test Server
- versions.</p>
+ <p>Whenever you install a new version of Erlang/OTP, the run_test
+ script needs to be regenerated, or updated manually with new directory names
+ (new version numbers), for it to "see" the latest Common Test and Test Server
+ versions.</p>
- <p>For more information on the <c>run_test</c> script and the <c>ct</c>
- module, please see the reference manual.</p>
- </section>
-
- <section>
- <title>Windows</title>
-
- <p>On Windows it is very convenient to use Cygwin (<c>www.cygwin.com</c>)
- for running Common Test and Erlang, since it enables you to use the
- <c>run_test</c> script for starting Common Test. If you are a Cygwin
- user, simply follow the instructions above for generating the <c>run_test</c>
- script.</p>
-
- <p>If you do not use Cygwin, you have to rely on the API functions
- in the <c>ct</c> module (instead of <c>run_test</c>) for running
- Common Test as described initially in this chapter.</p>
-
- <p>If you, for any reason, have chosen to store Common Test and Test Server
- in a different location than the default OTP lib directory, make
- sure the <c>ebin</c> directories of these applications are included
- in the Erlang code server path (so the application modules can be loaded).</p>
</section>
</chapter>
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index 4f5f6caa8c..fef1222fcb 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -32,6 +32,304 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.5.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Added an option to test specs which allow the execution
+ of tests as is, instead of doing merging of tests on the
+ same "level". See the merge_tests directive the test
+ specification documentation.</p>
+ <p>
+ Own Id: OTP-9026 Aux Id: seq11768 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Alpha release of Common Test Hooks (CTH). CTHs allow the
+ users of common test to abtract out common behaviours
+ from test suites in a much more elegant and flexible way
+ than was possible before. Note that the addition of this
+ feature may introduce minor changes in the undocumented
+ behaviour of the interface inbetween common_test and
+ test_server.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Updated ct:get_status documentation to describe
+ no_tests_running return value.</p>
+ <p>
+ Own Id: OTP-8895 Aux Id: seq11701 </p>
+ </item>
+ <item>
+ <p>
+ Fixed race condition test failures in the test suites
+ testing common test's parallel groups feature.</p>
+ <p>
+ Own Id: OTP-8921</p>
+ </item>
+ <item>
+ <p>
+ The include directive of testspecs now work when used on
+ a remote node.</p>
+ <p>
+ Own Id: OTP-8935 Aux Id: seq11731 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ ct:parse_table can now handle multiline sql rows</p>
+ <p>
+ Own Id: OTP-8907 Aux Id: seq11702 </p>
+ </item>
+ <item>
+ <p>
+ The run_test executable has been renamed to the less
+ generic ct_run to better work with other applications.
+ run_test will remain until R16B at which point it will be
+ removed.</p>
+ <p>
+ Own Id: OTP-8936</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Returning {return_group_result,failed} from end_per_group
+ in a group that is part of a sequence, did not cause the
+ proceeding cases (or groups) to get skipped. This has
+ been fixed.</p>
+ <p>
+ Own Id: OTP-8753 Aux Id: seq11644 </p>
+ </item>
+ <item>
+ <p>
+ ct:install now works as the documentation describes.</p>
+ <p>
+ Own Id: OTP-8818 Aux Id: seq-11666 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Common Test has been updated to handle start options and
+ test specification terms for test case groups (and test
+ cases in groups). Also, an option named 'label', has been
+ added that associates the test run with a name that
+ Common Test prints in the overview HTML logs.</p>
+ <p>
+ Own Id: OTP-8725 Aux Id: OTP-8727 </p>
+ </item>
+ <item>
+ <p>
+ Andrey Pampukha has been added to the AUTHORS file. Thank
+ you Andrey for your work on configuration data handling,
+ Large Scale Testing improvements, and other useful
+ updates and fixes.</p>
+ <p>
+ Own Id: OTP-8803</p>
+ </item>
+ <item>
+ <p>
+ The Configuration Data chapter in the User's Guide has
+ been updated.</p>
+ <p>
+ Own Id: OTP-8804</p>
+ </item>
+ <item>
+ <p>
+ Milliseconds are now included in timestamps in Common
+ Test log entries. (Thanks to Tomas Johansson.)</p>
+ <p>
+ Own Id: OTP-8808</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Common_Test 1.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Process calls using monitors in Common Test would not
+ clear the inbox of remaining DOWN messages. This has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-8621 Aux Id: seq11560 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ It is now possible for the user to provide specific
+ callback modules that handle test configuration data, so
+ that data on arbitray form can be accessed (e.g. by
+ reading files or by communicating with a configuration
+ server process). Two default callback modules have been
+ introduced in Common Test: ct_config_plain and
+ ct_config_xml. The former is used to handle the
+ traditional Common Test configuration files (with terms
+ on key-value tuple form) and the latter to handle
+ configuration data on XML representation.</p>
+ <p>
+ Own Id: OTP-8485</p>
+ </item>
+ <item>
+ <p>
+ It is now possible to execute test suites that are not
+ necessarily available on the local file system, but have
+ been loaded on the test node in advance (e.g. sent as
+ binaries from a remote node and loaded by RPC). A
+ requirement is that the no_auto_compile (or
+ {auto_compile,false}) parameter has been set.</p>
+ <p>
+ Own Id: OTP-8490 Aux Id: seq11500 </p>
+ </item>
+ <item>
+ <p>
+ Test Server will now call the end_per_testcase/2 function
+ even if the test case has been terminated explicitly
+ (with abort_current_testcase/1), or after a timetrap
+ timeout. Under these circumstances the return value of
+ end_per_testcase is completely ignored. Therefore the
+ function will not be able to change the reason for test
+ case termination by returning {fail,Reason}, nor will it
+ be able to save data with {save_config,Data}.</p>
+ <p>
+ Own Id: OTP-8500 Aux Id: seq11521 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to use the test specification term
+ 'init' to start Common Test nodes automatically, as well
+ as have initial function calls evaluated on the nodes. A
+ default callback module for the 'init' term, ct_slave,
+ has been introduced to enable Common Test Master to
+ perform host login and node startup operations over ssh.</p>
+ <p>
+ Own Id: OTP-8570</p>
+ </item>
+ <item>
+ <p>
+ The run_test script has been replaced by a program (with
+ the same name) which can be executed without explicit
+ installation. The start flags are the same as for the
+ legacy start script.</p>
+ <p>
+ Own Id: OTP-8650</p>
+ </item>
+ <item>
+ <p>
+ Previously, a repeat property of a test case group
+ specified the number of times the group should be
+ repeated after the main test run. I.e. {repeat,N} would
+ case the group to execute 1+N times. To be consistent
+ with the behaviour of the run_test repeat option, this
+ has been changed. N now specifies the absolute number of
+ executions instead.</p>
+ <p>
+ Own Id: OTP-8689 Aux Id: seq11502 </p>
+ </item>
+ <item>
+ <p>
+ With the run_test -erl_args option, it's possible to
+ divide the options on the run_test command line into ones
+ that Common Test should process (those preceding
+ -erl_args, and ones it should ignore (those succeeding
+ -erl_args). Options preceding -erl_args that Common Test
+ doesn't recognize are also ignored (i.e. the same
+ behaviour as earlier versions of Common Test).</p>
+ <p>
+ Own Id: OTP-8690 Aux Id: OTP-8650 </p>
+ </item>
+ <item>
+ <p>
+ Directories added with -pa or -pz in the pre-erl_args
+ part of the run_test command line will be converted from
+ relative to absolute, this to avoid problems loading user
+ modules when Common Test switches working directory
+ during the test run.</p>
+ <p>
+ Own Id: OTP-8691 Aux Id: OTP-8650 </p>
+ </item>
+ <item>
+ <p>
+ The timetrap handling has been made more user
+ controllable by means of new start options and new ct
+ interface functions. With the 'multiply_timetraps' start
+ option, it's possible to specify a value which all
+ timetrap timeout values get multiplied by. This is useful
+ e.g. to extend the timetraps temporarily while running
+ cover or trace. The 'scale_timetraps' start option
+ switches on or off the Test Server timetrap scaling
+ feature (which tries to detect if the tests may benefit
+ from extended timetraps, e.g. due to running certain test
+ tools, and performs the scaling automatically).
+ Furthermore, the ct:timetrap/1 function has been
+ introduced, which makes it possible to set/reset
+ timetraps during test execution. Also, a ct:sleep/1
+ function is now available, which takes the timetrap
+ parameters into account when calculating the time to
+ suspend the process.</p>
+ <p>
+ Own Id: OTP-8693</p>
+ </item>
+ <item>
+ <p>
+ A new run_test start option, event_handler_init, has been
+ added that takes a start argument which gets passed to
+ the init function of the event handler.</p>
+ <p>
+ Own Id: OTP-8694</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.4.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/doc/src/part.xml b/lib/common_test/doc/src/part.xml
index 53a4cb1bbf..3284bcadaa 100644
--- a/lib/common_test/doc/src/part.xml
+++ b/lib/common_test/doc/src/part.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -75,6 +75,7 @@
<xi:include href="ct_master_chapter.xml"/>
<xi:include href="event_handler_chapter.xml"/>
<xi:include href="dependencies_chapter.xml"/>
+ <xi:include href="ct_hooks_chapter.xml"/>
<xi:include href="why_test_chapter.xml"/>
</part>
diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml
index beb3ed3247..a9fdef7359 100644
--- a/lib/common_test/doc/src/ref_man.xml
+++ b/lib/common_test/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Common Test Reference Manual</title>
@@ -63,7 +63,7 @@
Server application.</p>
</description>
<xi:include href="common_test_app.xml"/>
- <xi:include href="run_test.xml"/>
+ <xi:include href="ct_run.xml"/>
<!-- If you make modifications in the module list below,
you also need to update CT_MODULES in Makefile. -->
<xi:include href="ct.xml"/>
@@ -75,6 +75,8 @@
<xi:include href="ct_snmp.xml"/>
<xi:include href="ct_telnet.xml"/>
<xi:include href="unix_telnet.xml"/>
+ <xi:include href="ct_slave.xml"/>
+ <xi:include href="ct_hooks.xml"/>
</application>
diff --git a/lib/common_test/doc/src/run_test.xml b/lib/common_test/doc/src/run_test.xml
deleted file mode 100644
index d9dd22d411..0000000000
--- a/lib/common_test/doc/src/run_test.xml
+++ /dev/null
@@ -1,140 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE comref SYSTEM "comref.dtd">
-
-<comref>
- <header>
- <copyright>
- <year>2007</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>The run_test shell script</title>
- <prepared>Peter Andersson</prepared>
- <responsible>Peter Andersson</responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>2007-07-04</date>
- <rev>PA1</rev>
- <file>run_test.xml</file>
- </header>
- <com>run_test</com>
- <comsummary>Shell script used for starting
- Common Test from the Unix command line.
- </comsummary>
-
- <description>
- <p>The <c>run_test</c> script is automatically generated as Common
- Test is installed (please see the Installation chapter in the Common
- Test User's Guide for more information). The script accepts a number
- of different start flags. Some flags trigger <c>run_test</c>
- to start the Common Test application and pass on data to it. Some
- flags start an Erlang node prepared for running Common Test in a
- particular mode.</p>
- <p><c>run_test</c> also accepts Erlang emulator
- flags. These are used when <c>run_test</c> calls <c>erl</c> to
- start the Erlang node (making it possible to e.g. add directories to
- the code server path, change the cookie on the node, start
- additional applications, etc).</p>
- <p>If <c>run_test</c> is called without parameters, it prints all valid
- start flags to stdout.</p>
- </description>
-
- <section>
- <title>Run tests from command line</title>
- <pre>
- run_test [-dir TestDir1 TestDir2 .. TestDirN] |
- [-suite Suite1 Suite2 .. SuiteN
- [[-group Group1 Group2 .. GroupN] [-case Case1 Case2 .. CaseN]]]
- [-step [config | keep_inactive]]
- [-config ConfigFile1 ConfigFile2 .. ConfigFileN]
- [-decrypt_key Key] | [-decrypt_file KeyFile]
- [-logdir LogDir]
- [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
- [-stylesheet CSSFile]
- [-cover CoverCfgFile]
- [-event_handler EvHandler1 EvHandler2 .. EvHandlerN]
- [-include InclDir1 InclDir2 .. InclDirN]
- [-no_auto_compile]
- [-repeat N [-force_stop]] |
- [-duration HHMMSS [-force_stop]] |
- [-until [YYMoMoDD]HHMMSS [-force_stop]]
- [-basic_html]</pre>
- </section>
- <section>
- <title>Run tests using test specification</title>
- <pre>
- run_test -spec TestSpec1 TestSpec2 .. TestSpecN
- [-config ConfigFile1 ConfigFile2 .. ConfigFileN]
- [-decrypt_key Key] | [-decrypt_file KeyFile]
- [-logdir LogDir]
- [-allow_user_terms]
- [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]
- [-stylesheet CSSFile]
- [-cover CoverCfgFile]
- [-event_handler EvHandler1 EvHandler2 .. EvHandlerN]
- [-include InclDir1 InclDir2 .. InclDirN]
- [-no_auto_compile]
- [-repeat N [-force_stop]] |
- [-duration HHMMSS [-force_stop]] |
- [-until [YYMoMoDD]HHMMSS [-force_stop]]
- [-basic_html]</pre>
- </section>
- <section>
- <title>Run tests in web based GUI</title>
- <pre>
- run_test -vts [-browser Browser]
- [-config ConfigFile1 ConfigFile2 .. ConfigFileN]
- [-decrypt_key Key] | [-decrypt_file KeyFile]
- [-dir TestDir1 TestDir2 .. TestDirN] |
- [-suite Suite [[-group Group] [-case Case]]]
- [-include InclDir1 InclDir2 .. InclDirN]
- [-no_auto_compile]
- [-basic_html]</pre>
- </section>
- <section>
- <title>Refresh the HTML index files</title>
- <pre>
- run_test -refresh_logs [-logdir LogDir] [-basic_html]</pre>
- </section>
- <section>
- <title>Run CT in interactive mode</title>
- <pre>
- run_test -shell
- [-config ConfigFile1 ConfigFile2 ... ConfigFileN]
- [-decrypt_key Key] | [-decrypt_file KeyFile]</pre>
- </section>
- <section>
- <title>Start an Erlang node with a given name</title>
- <pre>
- run_test -ctname NodeName</pre>
- </section>
- <section>
- <title>Start a Common Test Master node</title>
- <pre>
- run_test -ctmaster</pre>
- </section>
-
- <section>
- <title>See also</title>
- <p>Please read the <seealso marker="run_test_chapter">Running Test Suites</seealso>
- chapter in the Common Test User's Guide for information about the meaning of the
- different start flags.</p>
- </section>
-
-</comref>
-
-
diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml
index d731d18783..e6fb85634f 100644
--- a/lib/common_test/doc/src/run_test_chapter.xml
+++ b/lib/common_test/doc/src/run_test_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,15 +13,15 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
- <title>Running Test Suites</title>
+ <title>Running Tests</title>
<prepared>Peter Andersson, Kenneth Lundin</prepared>
<docno></docno>
<date></date>
@@ -71,7 +71,7 @@
<p>If test suites or help modules include header files stored in other
locations than the test directory, you may specify these include directories
- by means of the <c><![CDATA[-include]]></c> flag with <c><![CDATA[run_test]]></c>,
+ by means of the <c><![CDATA[-include]]></c> flag with <c><![CDATA[ct_run]]></c>,
or the <c><![CDATA[include]]></c> option with <c><![CDATA[ct:run_test/1]]></c>.
In addition to this, an include path may be specified with an OS
environment variable; <c><![CDATA[CT_INCLUDE_PATH]]></c>. Example (bash):</p>
@@ -93,36 +93,46 @@
there instead.</p>
<p>It is possible to disable the automatic compilation feature by using the
- <c><![CDATA[-no_auto_compile]]></c> flag with <c><![CDATA[run_test]]></c>, or
+ <c><![CDATA[-no_auto_compile]]></c> flag with <c><![CDATA[ct_run]]></c>, or
the <c><![CDATA[{auto_compile,false}]]></c> option with
<c><![CDATA[ct:run_test/1]]></c>. With automatic compilation
disabled, the user is responsible for compiling the test suite modules
- (and any help modules) before the test run. Common Test will only verify
- that the specified test suites exist before starting the tests.</p>
+ (and any help modules) before the test run. If the modules can not be loaded
+ from the local file system during startup of Common Test, the user needs to
+ pre-load the modules before starting the test. Common Test will only verify
+ that the specified test suites exist (i.e. that they are, or can be, loaded).
+ This is useful e.g. if the test suites are transferred and loaded as binaries via
+ RPC from a remote node.</p>
</section>
+ <marker id="ct_run"></marker>
<section>
- <title>Running tests from the UNIX command line</title>
+ <title>Running tests from the OS command line</title>
- <p>The script <c>run_test</c> can be used for running tests from
- the Unix/Linux command line, e.g.
+ <p>The <c>ct_run</c> program can be used for running tests from
+ the OS command line, e.g.
</p>
<list>
- <item><c><![CDATA[run_test -config <configfilenames> -dir <dirs>]]></c></item>
- <item><c><![CDATA[run_test -config <configfilenames> -suite <suiteswithfullpath>]]></c>
+ <item><c><![CDATA[ct_run -config <configfilenames> -dir <dirs>]]></c></item>
+ <item><c><![CDATA[ct_run -config <configfilenames> -suite <suiteswithfullpath>]]></c>
</item>
- <item><c><![CDATA[run_test -config <configfilenames> -suite <suitewithfullpath>
+ <item><c><![CDATA[ct_run -userconfig <callbackmodulename> <configfilenames> -suite <suiteswithfullpath>]]></c>
+ </item>
+ <item><c><![CDATA[ct_run -config <configfilenames> -suite <suitewithfullpath>
-group <groupnames> -case <casenames>]]></c></item>
</list>
<p>Examples:</p>
- <p><c>$ run_test -config $CFGS/sys1.cfg $CFGS/sys2.cfg -dir $SYS1_TEST $SYS2_TEST</c></p>
- <p><c>$ run_test -suite $SYS1_TEST/setup_SUITE $SYS2_TEST/config_SUITE</c></p>
- <p><c>$ run_test -suite $SYS1_TEST/setup_SUITE -case start stop</c></p>
- <p><c>$ run_test -suite $SYS1_TEST/setup_SUITE -group installation -case start stop</c></p>
+ <p><c>$ ct_run -config $CFGS/sys1.cfg $CFGS/sys2.cfg -dir $SYS1_TEST $SYS2_TEST</c></p>
+ <p><c>$ ct_run -userconfig ct_config_xml $CFGS/sys1.xml $CFGS/sys2.xml -dir $SYS1_TEST $SYS2_TEST</c></p>
+ <p><c>$ ct_run -suite $SYS1_TEST/setup_SUITE $SYS2_TEST/config_SUITE</c></p>
+ <p><c>$ ct_run -suite $SYS1_TEST/setup_SUITE -case start stop</c></p>
+ <p><c>$ ct_run -suite $SYS1_TEST/setup_SUITE -group installation -case start stop</c></p>
- <p>Other flags that may be used with <c>run_test</c>:</p>
+ <p>Other flags that may be used with <c>ct_run</c>:</p>
<list>
<item><c><![CDATA[-logdir <dir>]]></c>, specifies where the HTML log files are to be written.</item>
+ <item><c><![CDATA[-label <name_of_test_run>]]></c>, associates the test run with a name that gets printed
+ in the overview HTML log files.</item>
<item><c>-refresh_logs</c>, refreshes the top level HTML index files.</item>
<item><c>-vts</c>, start web based GUI (see below).</item>
<item><c>-shell</c>, start interactive shell mode (see below).</item>
@@ -136,8 +146,16 @@
<seealso marker="cover_chapter#cover">Code Coverage Analysis</seealso>).</item>
<item><c><![CDATA[-event_handler <event_handlers>]]></c>, to install
<seealso marker="event_handler_chapter#event_handling">event handlers</seealso>.</item>
+ <item><c><![CDATA[-event_handler_init <event_handlers>]]></c>, to install
+ <seealso marker="event_handler_chapter#event_handling">event handlers</seealso> including start arguments.</item>
+ <item><c><![CDATA[-ct_hooks <ct_hooks>]]></c>, to install
+ <seealso marker="ct_hooks_chapter#installing">Common Test Hooks</seealso> including start arguments.</item>
<item><c><![CDATA[-include]]></c>, specifies include directories (see above).</item>
<item><c><![CDATA[-no_auto_compile]]></c>, disables the automatic test suite compilation feature (see above).</item>
+ <item><c><![CDATA[-multiply_timetraps <n>]]></c>, extends <seealso marker="write_test_chapter#timetraps">timetrap
+ timeout</seealso> values.</item>
+ <item><c><![CDATA[-scale_timetraps <bool>]]></c>, enables automatic <seealso marker="write_test_chapter#timetraps">timetrap
+ timeout</seealso> scaling.</item>
<item><c><![CDATA[-repeat <n>]]></c>, tells Common Test to repeat the tests n times (see below).</item>
<item><c><![CDATA[-duration <time>]]></c>, tells Common Test to repeat the tests for duration of time (see below).</item>
<item><c><![CDATA[-until <stop_time>]]></c>, tells Common Test to repeat the tests until stop_time (see below).</item>
@@ -152,20 +170,20 @@
<note><p>Directories passed to Common Test may have either relative or absolute paths.</p></note>
<note><p>Arbitrary start flags to the Erlang Runtime System may also be passed as
- parameters to <c>run_test</c>. It is, for example, useful to be able to
+ parameters to <c>ct_run</c>. It is, for example, useful to be able to
pass directories that should be added to the Erlang code server search path
with the <c>-pa</c> or <c>-pz</c> flag. If you have common help- or library
modules for test suites (separately compiled), stored in other directories
than the test suite directories, these help/lib directories are preferrably
added to the code path this way. Example:</p>
- <p><c>$ run_test -dir ./chat_server -logdir ./chat_server/testlogs -pa $PWD/chat_server/ebin</c></p>
+ <p><c>$ ct_run -dir ./chat_server -logdir ./chat_server/testlogs -pa $PWD/chat_server/ebin</c></p>
<p>Note how in this example, the absolute path of the <c>chat_server/ebin</c>
directory is passed to the code server. This is essential since relative
paths are stored by the code server as relative, and Common Test changes
the current working directory of the Erlang Runtime System during the test run!</p>
</note>
- <p>For details on how to generate the <c>run_test</c> script, see the
+ <p>For more information about the <c>ct_run</c> program, see the
<seealso marker="install_chapter#general">Installation</seealso> chapter.
</p>
</section>
@@ -173,35 +191,36 @@
<section>
<title>Running tests from the Web based GUI</title>
- <p>The web based GUI, VTS, is started with the <c>run_test</c>
- script. From the GUI you can load config files, and select
+ <p>The web based GUI, VTS, is started with the <c>ct_run</c>
+ program. From the GUI you can load config files, and select
directories, suites and cases to run. You can also state the
config files, directories, suites and cases on the command line
when starting the web based GUI.
</p>
<list>
- <item><c>run_test -vts</c></item>
- <item><c><![CDATA[run_test -vts -config <configfilename>]]></c></item>
- <item><c><![CDATA[run_test -vts -config <configfilename> -suite <suitewithfullpath>
+ <item><c>ct_run -vts</c></item>
+ <item><c><![CDATA[ct_run -vts -config <configfilename>]]></c></item>
+ <item><c><![CDATA[ct_run -vts -config <configfilename> -suite <suitewithfullpath>
-case <casename>]]></c></item>
</list>
<p>From the GUI you can run tests and view the result and the logs.
</p>
- <p>Note that <c>run_test -vts</c> will try to open the Common Test start
+ <p>Note that <c>ct_run -vts</c> will try to open the Common Test start
page in an existing web browser window or start the browser if it is
not running. Which browser should be started may be specified with
the browser start command option:</p>
- <p><c><![CDATA[run_test -vts -browser <browser_start_cmd>]]></c></p>
+ <p><c><![CDATA[ct_run -vts -browser <browser_start_cmd>]]></c></p>
<p>Example:</p>
- <p><c><![CDATA[$ run_test -vts -browser 'firefox&']]></c></p>
+ <p><c><![CDATA[$ ct_run -vts -browser 'firefox&']]></c></p>
<p>Note that the browser must run as a separate OS process or VTS will hang!</p>
- <p>If no specific browser start command is specified, netscape will
+ <p>If no specific browser start command is specified, Firefox will
be the default browser on Unix platforms and Internet Explorer on Windows.
- If Common Test fails to start a browser automatically, start your
- favourite browser manually instead and type in the URL that Common Test
+ If Common Test fails to start a browser automatically, or <c>'none'</c> is
+ specified as the value for -browser (i.e. <c>-browser none</c>), start your
+ favourite browser manually and type in the URL that Common Test
displays in the shell.</p>
</section>
@@ -211,10 +230,10 @@
<p>Common Test provides an Erlang API for running tests. The main (and most
flexible) function for specifying and executing tests is called
<c>ct:run_test/1</c>. This function takes the same start parameters as
- the <c>run_test</c> script described above, only the flags are instead
+ the <c>ct_run</c> program described above, only the flags are instead
given as options in a list of key-value tuples. E.g. a test specified
- with <c>run_test</c> like:</p>
- <p><c>$ run_test -suite ./my_SUITE -logdir ./results</c></p>
+ with <c>ct_run</c> like:</p>
+ <p><c>$ ct_run -suite ./my_SUITE -logdir ./results</c></p>
<p>is with <c>ct:run_test/1</c> specified as:</p>
<p><c>1> ct:run_test([{suite,"./my_SUITE"},{logdir,"./results"}]).</c></p>
<p>For detailed documentation, please see the <c>ct</c> manual page.</p>
@@ -237,16 +256,17 @@
manually and call <c>ct:install/1</c> to install any configuration
data you might need (use <c>[]</c> as argument otherwise), then
call <c>ct:start_interactive/0</c> to start Common Test. If you use
- the <c>run_test</c> script, you may start the Erlang shell and Common Test
+ the <c>ct_run</c> program, you may start the Erlang shell and Common Test
in the same go by using the <c>-shell</c> and, optionally, the <c>-config</c>
- flag:
+ and/or <c>-userconfig</c> flag. Examples:
</p>
<list>
- <item><c>run_test -shell</c></item>
- <item><c><![CDATA[run_test -shell -config <configfilename>]]></c></item>
+ <item><c>ct_run -shell</c></item>
+ <item><c><![CDATA[ct_run -shell -config cfg/db.cfg]]></c></item>
+ <item><c><![CDATA[ct_run -shell -userconfig db_login testuser x523qZ]]></c></item>
</list>
- <p>If no config file is given with the <c>run_test</c> command,
+ <p>If no config file is given with the <c>ct_run</c> command,
a warning will be displayed. If Common Test has been run from the same
directory earlier, the same config file(s) will be used
again. If Common Test has not been run from this directory before, no
@@ -268,14 +288,15 @@
2> ct_telnet:open(unix_telnet).
{ok,&lt;0.105.0&gt;}
4> ct_telnet:cmd(unix_telnet, "ls .").
- {ok,["ls .","file1 ...",...]}</pre>
+ {ok,["ls .","file1 ...",...]}
+ </pre>
<p>Everything that Common Test normally prints in the test case logs,
will in the interactive mode be written to a log named
<c>ctlog.html</c> in the <c><![CDATA[ct_run.<timestamp>]]></c>
directory. A link to this file will be available in the file
named <c>last_interactive.html</c> in the directory from which
- you executed <c>run_test</c>. Currently, specifying a different
+ you executed <c>ct_run</c>. Currently, specifying a different
root directory for the logs than the current working directory,
is not supported.</p>
@@ -291,7 +312,7 @@
<section>
<title>Step by step execution of test cases with the Erlang Debugger</title>
- <p>By means of <c>run_test -step [opts]</c>, or by passing the
+ <p>By means of <c>ct_run -step [opts]</c>, or by passing the
<c>{step,Opts}</c> option to <c>ct:run_test/1</c>, it is possible
to get the Erlang Debugger started automatically and use its
graphical interface to investigate the state of the current test
@@ -315,60 +336,109 @@
with <c>dir</c>.</p>
</section>
+ <marker id="test_specifications"></marker>
<section>
- <marker id="test_specifications"></marker>
<title>Using test specifications</title>
- <p>The most expressive way to specify what to test is to use a so
- called test specification. A test specification is a sequence of
- Erlang terms. The terms may be declared in a text file or passed
- to the test server at runtime as a list (see <c>run_testspec/1</c>
- in the manual page for <c>ct</c>). There are two general types
- of terms: configuration terms and test specification terms.</p>
- <p>With configuration terms it is possible to import configuration
- data (similar to <c>run_test -config</c>), specify HTML log
- directories (similar to <c>run_test -logdir</c>), give aliases
- to test nodes and test directories (to make a specification
- easier to read and maintain), enable code coverage analysis
- (see the <seealso marker="cover_chapter#cover">Code Coverage
- Analysis</seealso> chapter) and specify event_handler plugins
+ <p>The most flexible way to specify what to test, is to use a so
+ called test specification. A test specification is a sequence of
+ Erlang terms. The terms may be declared in a text file or passed
+ to the test server at runtime as a list
+ (see <c>run_testspec/1</c> in the manual page
+ for <c>ct</c>). There are two general types of terms:
+ configuration terms and test specification terms.</p>
+ <p>With configuration terms it is possible to e.g. label the test
+ run (similar to <c>ct_run -label</c>), evaluate arbitrary expressions
+ before starting a test, import configuration
+ data (similar to
+ <c>ct_run -config/-userconfig</c>), specify HTML log directories (similar
+ to
+ <c>ct_run -logdir</c>), give aliases to test nodes and test
+ directories (to make a specification easier to read and
+ maintain), enable code coverage analysis (see
+ the <seealso marker="cover_chapter#cover">Code Coverage
+ Analysis</seealso> chapter) and specify event_handler plugins
(see the <seealso marker="event_handler_chapter#event_handling">
- Event Handling</seealso> chapter). There is also a term
- for specifying include directories that should be passed on
- to the compiler when automatic compilation is performed
- (similar to <c>run_test -include</c>, see above).</p>
- <p>With test specification terms it is possible to state exactly which
- tests should run and in which order. A test term specifies either
- one or more suites or one or more test cases. An arbitrary number of test
- terms may be declared in sequence. A test term can also specify one or
- more test suites or test cases to be skipped. Skipped suites and cases
- are not executed and show up in the HTML test log as SKIPPED.</p>
-
- <note><p>It is not yet possible to specify test case groups in
- test specifications. This will be supported in a soon upcoming
- release.</p></note>
+ Event Handling</seealso> chapter). There is also a term for
+ specifying include directories that should be passed on to the
+ compiler when automatic compilation is performed (similar
+ to <c>ct_run -include</c>, see above).</p>
+ <p>With test specification terms it is possible to state exactly
+ which tests should run and in which order. A test term specifies
+ either one or more suites, one or more test case groups, or one
+ or more test cases in a group or suite.</p>
+ <p>An arbitrary number of test terms may be declared in sequence.
+ Common Test will by default compile the terms into one or more tests
+ to be performed in one resulting test run. Note that a term that
+ specifies a set of test cases will "swallow" one that only
+ specifies a subset of these cases. E.g. the result of merging
+ one term that specifies that all cases in suite S should be
+ executed, with another term specifying only test case X and Y in
+ S, is a test of all cases in S. However, if a term specifying
+ test case X and Y in S is merged with a term specifying case Z
+ in S, the result is a test of X, Y and Z in S. To disable this
+ behaviour, it is possible in test specification to set the
+ <c>merge_tests</c> term to <c>false</c>.</p>
+ <p>A test term can also specify one or more test suites, groups,
+ or test cases to be skipped. Skipped suites, groups and cases
+ are not executed and show up in the HTML test log files as
+ SKIPPED.</p>
+ <p>When a test case group is specified, the resulting test
+ executes the
+ <c>init_per_group</c> function, followed by all test cases and
+ sub groups (including their configuration functions), and
+ finally the <c>end_per_group</c> function. Also if particular
+ test cases in a group are specified, <c>init_per_group</c>
+ and <c>end_per_group</c> for the group in question are
+ called. If a group which is defined (in <c>Suite:group/0</c>) to
+ be a sub group of another group, is specified (or particular test
+ cases of a sub group are), Common Test will call the configuration
+ functions for the top level groups as well as for the sub group
+ in question (making it possible to pass configuration data all
+ the way from <c>init_per_suite</c> down to the test cases in the
+ sub group).</p>
<p>Below is the test specification syntax. Test specifications can
- be used to run tests both in a single test host environment and in
- a distributed Common Test environment. Node parameters are only relevant in the
- latter (see the chapter about running Common Test in distributed mode for information).
- For details on the event_handler term, see the
- <seealso marker="event_handler_chapter#event_handling">Event Handling</seealso>
- chapter.</p>
+ be used to run tests both in a single test host environment and
+ in a distributed Common Test environment (Large Scale
+ Testing). The node parameters in the init term are only
+ relevant in the latter (see the
+ <seealso marker="ct_master_chapter#test_specifications">Large
+ Scale Testing</seealso> chapter for information). For details on
+ the event_handler term, see the
+ <seealso marker="event_handler_chapter#event_handling">Event
+ Handling</seealso> chapter.</p>
<p>Config terms:</p>
<pre>
{node, NodeAlias, Node}.
+
+ {init, InitOptions}.
+ {init, [NodeAlias], InitOptions}.
+
+ {label, Label}.
+ {label, NodeRefs, Label}.
+
+ {multiply_timetraps, N}.
+ {multiply_timetraps, NodeRefs, N}.
+
+ {scale_timetraps, Bool}.
+ {scale_timetraps, NodeRefs, Bool}.
{cover, CoverSpecFile}.
- {cover, NodeRef, CoverSpecFile}.
+ {cover, NodeRefs, CoverSpecFile}.
{include, IncludeDirs}.
{include, NodeRefs, IncludeDirs}.
{config, ConfigFiles}.
{config, NodeRefs, ConfigFiles}.
+
+ {userconfig, {CallbackModule, ConfigStrings}}.
+ {userconfig, NodeRefs, {CallbackModule, ConfigStrings}}.
{alias, DirAlias, Dir}.
+
+ {merge_tests, Bool}.
{logdir, LogDir}.
{logdir, NodeRefs, LogDir}.
@@ -377,12 +447,21 @@
{event_handler, NodeRefs, EventHandlers}.
{event_handler, EventHandlers, InitArgs}.
{event_handler, NodeRefs, EventHandlers, InitArgs}.
+
+ {ct_hooks, CTHModules}.
+ {ct_hooks, NodeRefs, CTHModules}.
</pre>
<p>Test terms:</p>
<pre>
{suites, DirRef, Suites}.
{suites, NodeRefs, DirRef, Suites}.
+ {groups, DirRef, Suite, Groups}.
+ {groups, NodeRefsDirRef, Suite, Groups}.
+
+ {groups, DirRef, Suite, Group, {cases,Cases}}.
+ {groups, NodeRefsDirRef, Suite, Group, {cases,Cases}}.
+
{cases, DirRef, Suite, Cases}.
{cases, NodeRefs, DirRef, Suite, Cases}.
@@ -395,9 +474,12 @@
<p>Types:</p>
<pre>
NodeAlias = atom()
+ InitOptions = term()
Node = node()
NodeRef = NodeAlias | Node | master
NodeRefs = all_nodes | [NodeRef] | NodeRef
+ N = integer()
+ Bool = true | false
CoverSpecFile = string()
IncludeDirs = string() | [string()]
ConfigFiles = string() | [string()]
@@ -406,8 +488,14 @@
LogDir = string()
EventHandlers = atom() | [atom()]
InitArgs = [term()]
+ CTHModules = [CTHModule | {CTHModule, CTHInitArgs}]
+ CTHModule = atom()
+ CTHInitArgs = term()
DirRef = DirAlias | Dir
Suites = atom() | [atom()] | all
+ Suite = atom()
+ Groups = atom() | [atom()] | all
+ Group = atom()
Cases = atom() | [atom()] | all
Comment = string() | ""
</pre>
@@ -449,13 +537,18 @@
<item>Secondly, the test for system t2 should run. The included suites are
t2B and t2C. Included are also test cases test4, test1 and test7 in suite
t2A. Note that the test cases will be executed in the specified order.</item>
- <item>Lastly, all suites for systems t3 are to be completely skipped and this
+ <item>Lastly, all suites for systems t3 are to be completely skipped and this
should be explicitly noted in the log files.</item>
</list>
+ <p>It is possible to specify initialization options for nodes defined in the
+ test specification. Currently, there are options to start the node and/or to
+ evaluate any function on the node.
+ See the <seealso marker="ct_master_chapter#ct_slave">Automatic startup of
+ the test target nodes</seealso> chapter for details.</p>
<p>It is possible for the user to provide a test specification that
includes (for Common Test) unrecognizable terms. If this is desired,
the <c>-allow_user_terms</c> flag should be used when starting tests with
- <c>run_test</c>. This forces Common Test to ignore unrecognizable terms.
+ <c>ct_run</c>. This forces Common Test to ignore unrecognizable terms.
Note that in this mode, Common Test is not able to check the specification
for errors as efficiently as if the scanner runs in default mode.
If <c>ct:run_test/1</c> is used for starting the tests, the relaxed scanner
@@ -581,11 +674,11 @@
</pre>
<p>To install the CSS file (Common Test inlines the definition in the
- HTML code), the name may be provided when executing <c>run_test</c>.
+ HTML code), the name may be provided when executing <c>ct_run</c>.
Example:</p>
<pre>
- $ run_test -dir $TEST/prog -stylesheet $TEST/styles/test_categories.css
+ $ ct_run -dir $TEST/prog -stylesheet $TEST/styles/test_categories.css
</pre>
<p>Categories in a CSS file installed with the <c>-stylesheet</c> flag
@@ -658,7 +751,7 @@
means of time, it is also possible to specify what action Common Test should
take upon timeout. Either Common Test performs all tests in the current run before stopping,
or it stops as soon as the current test job is finished. Repetition can be activated by
- means of <c>run_test</c> start flags, or tuples in the <c>ct:run:test/1</c>
+ means of <c>ct_run</c> start flags, or tuples in the <c>ct:run:test/1</c>
option list argument. The flags (options in parenthesis) are:</p>
<list>
<item><c>-repeat N ({repeat,N})</c>, where <c>N</c> is a positive integer.</item>
@@ -694,7 +787,7 @@
<p>Example 1:</p>
<pre>
- $ run_test -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -duration 001000 -force_stop</pre>
+ $ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -duration 001000 -force_stop</pre>
<p>Here the suites in test directory to1, followed by the suites in to2, will be executed
in one test run. A timeout event will occur after 10 minutes. As long as there is time
left, Common Test will repeat the test run (i.e. starting over with the to1 test).
@@ -707,7 +800,7 @@
$ date
Fri Sep 28 15:00:00 MEST 2007
- $ run_test -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -until 160000</pre>
+ $ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -until 160000</pre>
<p>Here the same test run as in the example above will be executed (and possibly repeated).
In this example, however, the timeout will occur after 1 hour and when that happens,
Common Test will finish the entire test run before stopping (i.e. the to1 and to2 test
@@ -715,7 +808,7 @@
<p>Example 3:</p>
<pre>
- $ run_test -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -repeat 5</pre>
+ $ ct_run -dir $TEST_ROOT/to1 $TEST_ROOT/to2 -repeat 5</pre>
<p>Here the test run, including both the to1 and the to2 test, will be repeated 5 times.</p>
<note><p>This feature should not be confused with the <c>repeat</c> property of a test
@@ -734,7 +827,7 @@
of the <c>-silent_connections</c> flag:</p>
<pre>
- run_test -silent_connections [conn_types]
+ ct_run -silent_connections [conn_types]
</pre>
<p>where <c>conn_types</c> specifies <c>telnet, ftp, rpc</c> and/or <c>snmp</c>.</p>
@@ -742,11 +835,11 @@
<p>Example:</p>
<pre>
- run_test ... -silent_connections telnet ftp</pre>
+ ct_run ... -silent_connections telnet ftp</pre>
<p>switches off logging for telnet and ftp connections.</p>
<pre>
- run_test ... -silent_connections</pre>
+ ct_run ... -silent_connections</pre>
<p>switches off logging for all connection types.</p>
diff --git a/lib/common_test/doc/src/test_structure_chapter.xml b/lib/common_test/doc/src/test_structure_chapter.xml
index c8628b3a7a..b9ca59135d 100644
--- a/lib/common_test/doc/src/test_structure_chapter.xml
+++ b/lib/common_test/doc/src/test_structure_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Test Structure</title>
@@ -144,10 +144,10 @@
be used when the test suite needs to write to files.
</item>
- <tag><em>run_test</em></tag>
+ <tag><em>ct_run</em></tag>
<item>
- The name of an executable Bourne shell script that may be
- used on Linux/Unix as an interface for specifying and running
+ The name of an executable program that may be
+ used as an interface for specifying and running
tests with Common Test.
</item>
diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml
index 212e3d85be..723492d8f3 100644
--- a/lib/common_test/doc/src/write_test_chapter.xml
+++ b/lib/common_test/doc/src/write_test_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>Writing Test Suites</title>
@@ -115,6 +115,7 @@
</p>
</section>
+ <marker id="per_testcase"/>
<section>
<title>Init and end per test case</title>
@@ -157,6 +158,15 @@
<c>{skipped,Reason}</c> (where Reason is a user specific term).
</p>
+ <p>The <c>end_per_testcase/2</c> function is called even after a
+ test case terminates due to a call to <c>ct:abort_current_testcase/1</c>,
+ or after a timetrap timeout. However, <c>end_per_testcase</c>
+ will then execute on a different process than the test case
+ function, and in this situation, <c>end_per_testcase</c> will
+ not be able to change the reason for test case termination by
+ returning <c>{fail,Reason}</c>, nor will it be able to save data with
+ <c>{save_config,Data}</c>.</p>
+
<p>If <c>init_per_testcase</c> crashes, the test case itself is skipped
automatically (so called <em>auto skipped</em>). If <c>init_per_testcase</c>
returns a <c>skip</c> tuple, also then will the test case be skipped (so
@@ -682,12 +692,33 @@
<c>end_per_suite</c> execute, like test cases, on dedicated Erlang
processes.
</p>
+ </section>
+ <section>
+ <title>Timetrap timeouts</title>
+ <marker id="timetraps"></marker>
<p>The default time limit for a test case is 30 minutes, unless a
- <c>timetrap</c> is specified either by the test case info function
- or the <c>suite/0</c> function.
- </p>
-
+ <c>timetrap</c> is specified either by the suite info function
+ or a test case info function. The timetrap timeout value defined
+ in <c>suite/0</c> is the value that will be used for each test case
+ in the suite (as well as for the configuration functions
+ <c>init_per_suite/1</c> and <c>end_per_suite</c>). A timetrap timeout
+ value set with the test case info function will override the value set
+ by <c>suite/0</c>, but only for that particular test case.</p>
+ <p>It is also possible to set/reset a timetrap during test case (or
+ configuration function) execution. This is done by calling
+ <c>ct:timetrap/1</c>. This function will cancel the current timetrap
+ and start a new one.</p>
+ <p>Timetrap values can be extended with a multiplier value specified at
+ startup with the <c>multiply_timetraps</c> option. It is also possible
+ to let Test Server decide to scale up timetrap timeout values
+ automatically, e.g. if tools such as cover or trace are running during
+ the test. This feature is disabled by default and can be enabled with
+ the <c>scale_timetraps</c> start option.</p>
+ <p>If a test case needs to suspend itself for a time that also gets
+ multipled by <c>multiply_timetraps</c>, and possibly scaled up if
+ <c>scale_timetraps</c> is enabled, the function <c>ct:sleep/1</c>
+ may be called.</p>
</section>
<section>
diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in
index f144847a29..f4a0c181f9 100644
--- a/lib/common_test/priv/Makefile.in
+++ b/lib/common_test/priv/Makefile.in
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -56,8 +56,9 @@ ifneq ($(findstring win32,$(TARGET)),win32)
#
# Files
#
-FILES = vts.tool run_test.in
-SCRIPTS = install.sh
+FILES = vts.tool
+SCRIPTS =
+IMAGES = tile1.jpg
#
# Rules
@@ -83,14 +84,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk
ifeq ($(XNIX),true)
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/priv/bin
- $(INSTALL_SCRIPT) $(SCRIPTS) $(RELSYSDIR)
- $(INSTALL_DATA) $(FILES) $(RELSYSDIR)/priv
+ $(INSTALL_DIR) $(RELSYSDIR)/priv
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(RELSYSDIR)/priv
else
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/priv/bin
- $(INSTALL_SCRIPT) $(SCRIPTS) $(RELSYSDIR)
- $(INSTALL_DATA) $(FILES) $(RELSYSDIR)/priv
+ $(INSTALL_DIR) $(RELSYSDIR)/priv
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(RELSYSDIR)/priv
endif
release_docs_spec:
@@ -105,6 +104,7 @@ else
# Files
#
FILES = vts.tool
+IMAGES = tile1.jpg
#
# Rules
@@ -123,8 +123,8 @@ clean:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/priv/bin
- $(INSTALL_DATA) $(FILES) $(RELSYSDIR)/priv
+ $(INSTALL_DIR) $(RELSYSDIR)/priv
+ $(INSTALL_DATA) $(FILES) $(IMAGES) $(RELSYSDIR)/priv
release_docs_spec:
diff --git a/lib/common_test/priv/tile1.jpg b/lib/common_test/priv/tile1.jpg
new file mode 100644
index 0000000000..8749383716
--- /dev/null
+++ b/lib/common_test/priv/tile1.jpg
Binary files differ
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index e7e2d1275d..84b122b5e4 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
@@ -63,7 +63,13 @@ MODULES= \
ct_telnet_client \
ct_make \
vts \
- unix_telnet
+ unix_telnet \
+ ct_config \
+ ct_config_plain \
+ ct_config_xml \
+ ct_slave \
+ ct_hooks\
+ ct_hooks_lock
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index 7b72932ad4..b42173f412 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -1,19 +1,19 @@
% This is an -*- erlang -*- file.
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
{application, common_test,
@@ -42,10 +42,15 @@
ct_testspec,
ct_util,
unix_telnet,
- vts
+ vts,
+ ct_config,
+ ct_config_plain,
+ ct_config_xml,
+ ct_slave
]},
{registered, [ct_logs,
ct_util_server,
+ ct_config_server,
ct_make_ref,
vts,
ct_master,
diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl
index 8ae041e5b4..dfec2b7a67 100644
--- a/lib/common_test/src/ct.erl
+++ b/lib/common_test/src/ct.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -59,18 +59,22 @@
%% Test suite API
-export([require/1, require/2,
get_config/1, get_config/2, get_config/3,
+ reload_config/1,
log/1, log/2, log/3,
print/1, print/2, print/3,
pal/1, pal/2, pal/3,
fail/1, comment/1,
- testcases/2, userdata/2, userdata/3]).
+ testcases/2, userdata/2, userdata/3,
+ timetrap/1, sleep/1]).
+
+%% New API for manipulating with config handlers
+-export([add_config/2, remove_config/2]).
%% Other interface functions
-export([get_status/0, abort_current_testcase/1,
encrypt_config_file/2, encrypt_config_file/3,
decrypt_config_file/2, decrypt_config_file/3]).
-
-export([get_target_name/1]).
-export([parse_table/1, listenv/1]).
@@ -93,7 +97,7 @@
%%% <code>install([{config,["config_node.ctc","config_user.ctc"]}])</code>.</p>
%%%
%%% <p>Note that this function is automatically run by the
-%%% <code>run_test</code> script.</p>
+%%% <code>ct_run</code> program.</p>
install(Opts) ->
ct_run:install(Opts).
@@ -134,22 +138,31 @@ run(TestDirs) ->
%%%-----------------------------------------------------------------
%%% @spec run_test(Opts) -> Result
%%% Opts = [OptTuples]
-%%% OptTuples = {config,CfgFiles} | {dir,TestDirs} | {suite,Suites} |
-%%% {testcase,Cases} | {group,Groups} | {spec,TestSpecs} |
-%%% {allow_user_terms,Bool} | {logdir,LogDir} |
-%%% {silent_connections,Conns} | {cover,CoverSpecFile} |
-%%% {step,StepOpts} | {event_handler,EventHandlers} | {include,InclDirs} |
-%%% {auto_compile,Bool} | {repeat,N} | {duration,DurTime} |
-%%% {until,StopTime} | {force_stop,Bool} | {decrypt,DecryptKeyOrFile} |
-%%% {refresh_logs,LogDir} | {basic_html,Bool}
-%%% CfgFiles = [string()] | string()
+%%% OptTuples = {dir,TestDirs} | {suite,Suites} | {group,Groups} |
+%%% {testcase,Cases} | {spec,TestSpecs} | {label,Label} |
+%%% {config,CfgFiles} | {userconfig, UserConfig} |
+%%% {allow_user_terms,Bool} | {logdir,LogDir} |
+%%% {silent_connections,Conns} | {stylesheet,CSSFile} |
+%%% {cover,CoverSpecFile} | {step,StepOpts} |
+%%% {event_handler,EventHandlers} | {include,InclDirs} |
+%%% {auto_compile,Bool} | {multiply_timetraps,M} | {scale_timetraps,Bool} |
+%%% {repeat,N} | {duration,DurTime} | {until,StopTime} |
+%%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} |
+%%% {refresh_logs,LogDir} | {basic_html,Bool} |
+%%% {ct_hooks, CTHs}
%%% TestDirs = [string()] | string()
%%% Suites = [string()] | string()
%%% Cases = [atom()] | atom()
%%% Groups = [atom()] | atom()
%%% TestSpecs = [string()] | string()
+%%% Label = string() | atom()
+%%% CfgFiles = [string()] | string()
+%%% UserConfig = [{CallbackMod,CfgStrings}] | {CallbackMod,CfgStrings}
+%%% CallbackMod = atom()
+%%% CfgStrings = [string()] | string()
%%% LogDir = string()
%%% Conns = all | [atom()]
+%%% CSSFile = string()
%%% CoverSpecFile = string()
%%% StepOpts = [StepOpt] | []
%%% StepOpt = config | keep_inactive
@@ -157,19 +170,24 @@ run(TestDirs) ->
%%% EH = atom() | {atom(),InitArgs} | {[atom()],InitArgs}
%%% InitArgs = [term()]
%%% InclDirs = [string()] | string()
+%%% M = integer()
%%% N = integer()
%%% DurTime = string(HHMMSS)
%%% StopTime = string(YYMoMoDDHHMMSS) | string(HHMMSS)
%%% DecryptKeyOrFile = {key,DecryptKey} | {file,DecryptFile}
%%% DecryptKey = string()
%%% DecryptFile = string()
+%%% CTHs = [CTHModule | {CTHModule, CTHInitArgs}]
+%%% CTHModule = atom()
+%%% CTHInitArgs = term()
%%% Result = [TestResult] | {error,Reason}
%%% @doc Run tests as specified by the combination of options in <code>Opts</code>.
-%%% The options are the same as those used with the <code>run_test</code> script.
+%%% The options are the same as those used with the
+%%% <seealso marker="ct_run#ct_run"><code>ct_run</code></seealso> program.
%%% Note that here a <code>TestDir</code> can be used to point out the path to
%%% a <code>Suite</code>. Note also that the option <code>testcase</code>
-%%% corresponds to the <code>-case</code> option in the <code>run_test</code>
-%%% script. Configuration files specified in <code>Opts</code> will be
+%%% corresponds to the <code>-case</code> option in the <code>ct_run</code>
+%%% program. Configuration files specified in <code>Opts</code> will be
%%% installed automatically at startup.
run_test(Opts) ->
ct_run:run_test(Opts).
@@ -211,7 +229,7 @@ step(TestDir,Suite,Case,Opts) ->
%%%
%%% <p>From this mode all test case support functions can be executed
%%% directly from the erlang shell. The interactive mode can also be
-%%% started from the unix command line with <code>run_test -shell
+%%% started from the OS command line with <code>ct_run -shell
%%% [-config File...]</code>.</p>
%%%
%%% <p>If any functions using "required config data" (e.g. telnet or
@@ -269,7 +287,7 @@ stop_interactive() ->
%%% @see get_config/2
%%% @see get_config/3
require(Required) ->
- ct_util:require(Required).
+ ct_config:require(Required).
%%%-----------------------------------------------------------------
%%% @spec require(Name,Required) -> ok | {error,Reason}
@@ -304,19 +322,19 @@ require(Required) ->
%%% @see get_config/2
%%% @see get_config/3
require(Name,Required) ->
- ct_util:require(Name,Required).
+ ct_config:require(Name,Required).
%%%-----------------------------------------------------------------
%%% @spec get_config(Required) -> Value
%%% @equiv get_config(Required,undefined,[])
get_config(Required) ->
- ct_util:get_config(Required,undefined,[]).
+ ct_config:get_config(Required,undefined,[]).
%%%-----------------------------------------------------------------
%%% @spec get_config(Required,Default) -> Value
%%% @equiv get_config(Required,Default,[])
get_config(Required,Default) ->
- ct_util:get_config(Required,Default,[]).
+ ct_config:get_config(Required,Default,[]).
%%%-----------------------------------------------------------------
%%% @spec get_config(Required,Default,Opts) -> ValueOrElement
@@ -375,7 +393,26 @@ get_config(Required,Default) ->
%%% @see require/1
%%% @see require/2
get_config(Required,Default,Opts) ->
- ct_util:get_config(Required,Default,Opts).
+ ct_config:get_config(Required,Default,Opts).
+
+%%%-----------------------------------------------------------------
+%%% @spec reload_config(Required) -> ValueOrElement
+%%% Required = KeyOrName | {KeyOrName,SubKey}
+%%% KeyOrName = atom()
+%%% SubKey = atom()
+%%% ValueOrElement = term()
+%%%
+%%% @doc Reload config file which contains specified configuration key.
+%%%
+%%% <p>This function performs updating of the configuration data from which the
+%%% given configuration variable was read, and returns the (possibly) new
+%%% value of this variable.</p>
+%%% <p>Note that if some variables were present in the configuration but are not loaded
+%%% using this function, they will be removed from the configuration table together
+%%% with their aliases.</p>
+%%%
+reload_config(Required)->
+ ct_config:reload_config(Required).
%%%-----------------------------------------------------------------
%%% @spec log(Format) -> ok
@@ -661,7 +698,7 @@ userdata(TestDir, Suite, Case) ->
%%%-----------------------------------------------------------------
-%%% @spec get_status() -> TestStatus | {error,Reason}
+%%% @spec get_status() -> TestStatus | {error,Reason} | no_tests_running
%%% TestStatus = [StatusElem]
%%% StatusElem = {current,{Suite,TestCase}} | {successful,Successful} |
%%% {failed,Failed} | {skipped,Skipped} | {total,Total}
@@ -734,7 +771,7 @@ abort_current_testcase(Reason) ->
%%% <p>See the <code>crypto</code> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName) ->
- ct_util:encrypt_config_file(SrcFileName, EncryptFileName).
+ ct_config:encrypt_config_file(SrcFileName, EncryptFileName).
%%%-----------------------------------------------------------------
%%% @spec encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
@@ -754,7 +791,7 @@ encrypt_config_file(SrcFileName, EncryptFileName) ->
%%% <p>See the <code>crypto</code> application for details on DES3
%%% encryption/decryption.</p>
encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
- ct_util:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile).
+ ct_config:encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile).
%%%-----------------------------------------------------------------
%%% @spec decrypt_config_file(EncryptFileName, TargetFileName) ->
@@ -770,7 +807,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, KeyOrFile) ->
%%% <code>.ct_config.crypt</code> in the current directory, or the
%%% home directory of the user (it is searched for in that order).</p>
decrypt_config_file(EncryptFileName, TargetFileName) ->
- ct_util:decrypt_config_file(EncryptFileName, TargetFileName).
+ ct_config:decrypt_config_file(EncryptFileName, TargetFileName).
%%%-----------------------------------------------------------------
%%% @spec decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
@@ -785,5 +822,65 @@ decrypt_config_file(EncryptFileName, TargetFileName) ->
%%% file contents is saved in the target file. The key must have the
%%% the same value as that used for encryption.</p>
decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile) ->
- ct_util:decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile).
+ ct_config:decrypt_config_file(EncryptFileName, TargetFileName, KeyOrFile).
+
+
+%%%-----------------------------------------------------------------
+%%% @spec add_config(Callback, Config) -> ok | {error, Reason}
+%%% Callback = atom()
+%%% Config = string()
+%%% Reason = term()
+%%%
+%%% @doc <p>This function loads configuration variables using the
+%%% given callback module and configuration string. Callback module
+%%% should be either loaded or present in the code part. Loaded
+%%% configuration variables can later be removed using
+%%% <code>remove_config/2</code> function.</p>
+add_config(Callback, Config)->
+ ct_config:add_config(Callback, Config).
+%%%-----------------------------------------------------------------
+%%% @spec remove_config(Callback, Config) -> ok
+%%% Callback = atom()
+%%% Config = string()
+%%% Reason = term()
+%%%
+%%% @doc <p>This function removes configuration variables (together with
+%%% their aliases) which were loaded with specified callback module and
+%%% configuration string.</p>
+remove_config(Callback, Config) ->
+ ct_config:remove_config(Callback, Config).
+
+%%%-----------------------------------------------------------------
+%%% @spec timetrap(Time) -> ok
+%%% Time = {hours,Hours} | {minutes,Mins} | {seconds,Secs} | Millisecs | infinity
+%%% Hours = integer()
+%%% Mins = integer()
+%%% Secs = integer()
+%%% Millisecs = integer() | float()
+%%%
+%%% @doc <p>Use this function to set a new timetrap for the running test case.</p>
+timetrap(Time) ->
+ test_server:timetrap(Time).
+
+%%%-----------------------------------------------------------------
+%%% @spec sleep(Time) -> ok
+%%% Time = {hours,Hours} | {minutes,Mins} | {seconds,Secs} | Millisecs | infinity
+%%% Hours = integer()
+%%% Mins = integer()
+%%% Secs = integer()
+%%% Millisecs = integer() | float()
+%%%
+%%% @doc <p>This function, similar to <c>timer:sleep/1</c>, suspends the test
+%%% case for specified time. However, this function also multiplies
+%%% <c>Time</c> with the 'multiply_timetraps' value (if set) and under
+%%% certain circumstances also scales up the time automatically
+%%% if 'scale_timetraps' is set to true (default is false).</p>
+sleep({hours,Hs}) ->
+ sleep(trunc(Hs * 1000 * 60 * 60));
+sleep({minutes,Ms}) ->
+ sleep(trunc(Ms * 1000 * 60));
+sleep({seconds,Ss}) ->
+ sleep(trunc(Ss * 1000));
+sleep(Time) ->
+ test_server:adjusted_sleep(Time).
diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl
new file mode 100644
index 0000000000..6b75937668
--- /dev/null
+++ b/lib/common_test/src/ct_config.erl
@@ -0,0 +1,804 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%----------------------------------------------------------------------
+%% File : ct_config.erl
+%% Description : CT module for reading and manipulating of configuration
+%% data
+%%
+%% Created : 15 February 2010
+%%----------------------------------------------------------------------
+-module(ct_config).
+
+-export([start/1, stop/0]).
+
+-export([read_config_files/1,
+ get_config_file_list/1]).
+
+-export([require/1, require/2]).
+
+-export([get_config/1, get_config/2, get_config/3,
+ get_all_config/0]).
+
+-export([set_default_config/2, set_default_config/3]).
+
+-export([delete_default_config/1]).
+
+-export([reload_config/1, update_config/2]).
+
+-export([release_allocated/0]).
+
+-export([encrypt_config_file/2, encrypt_config_file/3,
+ decrypt_config_file/2, decrypt_config_file/3,
+ get_crypt_key_from_file/0, get_crypt_key_from_file/1]).
+
+-export([get_ref_from_name/1, get_name_from_ref/1, get_key_from_name/1]).
+
+-export([check_config_files/1, add_default_callback/1, prepare_config_list/1]).
+
+-export([add_config/2, remove_config/2]).
+
+-include("ct_util.hrl").
+
+-define(cryptfile, ".ct_config.crypt").
+
+-record(ct_conf,{key,value,handler,config,ref,name='_UNDEF',default=false}).
+
+start(Mode) ->
+ case whereis(ct_config_server) of
+ undefined ->
+ Me = self(),
+ Pid = spawn_link(fun() -> do_start(Me) end),
+ receive
+ {Pid,started} -> Pid;
+ {Pid,Error} -> exit(Error)
+ end;
+ Pid ->
+ case ct_util:get_mode() of
+ interactive when Mode==interactive ->
+ Pid;
+ interactive ->
+ {error,interactive_mode};
+ _OtherMode ->
+ Pid
+ end
+ end.
+
+do_start(Parent) ->
+ process_flag(trap_exit,true),
+ register(ct_config_server,self()),
+ ct_util:create_table(?attr_table,bag,#ct_conf.key),
+ {ok,StartDir} = file:get_cwd(),
+ Opts = case ct_util:read_opts() of
+ {ok,Opts1} ->
+ Opts1;
+ Error ->
+ Parent ! {self(),Error},
+ exit(Error)
+ end,
+ case read_config_files(Opts) of
+ ok ->
+ Parent ! {self(),started},
+ loop(StartDir);
+ ReadError ->
+ Parent ! {self(),ReadError},
+ exit(ReadError)
+ end.
+
+stop() ->
+ case whereis(ct_config_server) of
+ undefined -> ok;
+ _ -> call({stop})
+ end.
+
+call(Msg) ->
+ MRef = erlang:monitor(process, whereis(ct_config_server)),
+ Ref = make_ref(),
+ ct_config_server ! {Msg,{self(),Ref}},
+ receive
+ {Ref, Result} ->
+ erlang:demonitor(MRef, [flush]),
+ Result;
+ {'DOWN',MRef,process,_,Reason} ->
+ {error,{ct_util_server_down,Reason}}
+ end.
+
+return({To,Ref},Result) ->
+ To ! {Ref, Result}.
+
+loop(StartDir) ->
+ receive
+ {{require,Name,Tag,SubTags},From} ->
+ Result = do_require(Name,Tag,SubTags),
+ return(From,Result),
+ loop(StartDir);
+ {{set_default_config,{Config,Scope}},From} ->
+ set_config(Config,{true,Scope}),
+ return(From,ok),
+ loop(StartDir);
+ {{set_default_config,{Name,Config,Scope}},From} ->
+ set_config(Name,Config,{true,Scope}),
+ return(From,ok),
+ loop(StartDir);
+ {{delete_default_config,Scope},From} ->
+ delete_config({true,Scope}),
+ return(From,ok),
+ loop(StartDir);
+ {{update_config,{Name,NewConfig}},From} ->
+ update_conf(Name,NewConfig),
+ return(From,ok),
+ loop(StartDir);
+ {{reload_config, KeyOrName},From}->
+ NewValue = reload_conf(KeyOrName),
+ return(From, NewValue),
+ loop(StartDir);
+ {{stop},From} ->
+ ets:delete(?attr_table),
+ file:set_cwd(StartDir),
+ return(From,ok)
+ end.
+
+set_default_config(NewConfig, Scope) ->
+ call({set_default_config, {NewConfig, Scope}}).
+
+set_default_config(Name, NewConfig, Scope) ->
+ call({set_default_config, {Name, NewConfig, Scope}}).
+
+delete_default_config(Scope) ->
+ call({delete_default_config, Scope}).
+
+update_config(Name, Config) ->
+ call({update_config, {Name, Config}}).
+
+reload_config(KeyOrName) ->
+ call({reload_config, KeyOrName}).
+
+process_default_configs(Opts) ->
+ case lists:keysearch(config, 1, Opts) of
+ {value,{_,Files=[File|_]}} when is_list(File) ->
+ Files;
+ {value,{_,File=[C|_]}} when is_integer(C) ->
+ [File];
+ {value,{_,[]}} ->
+ [];
+ false ->
+ []
+ end.
+
+process_user_configs(Opts, Acc) ->
+ case lists:keytake(userconfig, 1, Opts) of
+ false ->
+ lists:reverse(Acc);
+ {value, {userconfig, Config=[{_,_}|_]}, NewOpts} ->
+ Acc1 = lists:map(fun({_Callback, []}=Cfg) ->
+ Cfg;
+ ({Callback, Files=[File|_]}) when is_list(File) ->
+ {Callback, Files};
+ ({Callback, File=[C|_]}) when is_integer(C) ->
+ {Callback, [File]}
+ end, Config),
+ process_user_configs(NewOpts, lists:reverse(Acc1)++Acc);
+ {value, {userconfig, {Callback, []}}, NewOpts} ->
+ process_user_configs(NewOpts, [{Callback, []} | Acc]);
+ {value, {userconfig, {Callback, Files=[File|_]}}, NewOpts} when is_list(File) ->
+ process_user_configs(NewOpts, [{Callback, Files} | Acc]);
+ {value, {userconfig, {Callback, File=[C|_]}}, NewOpts} when is_integer(C) ->
+ process_user_configs(NewOpts, [{Callback, [File]} | Acc])
+ end.
+
+get_config_file_list(Opts) ->
+ DefaultConfigs = process_default_configs(Opts),
+ CfgFiles =
+ if
+ DefaultConfigs == []->
+ [];
+ true->
+ [{?ct_config_txt, DefaultConfigs}]
+ end ++
+ process_user_configs(Opts, []),
+ CfgFiles.
+
+add_default_callback(Opts) ->
+ case lists:keytake(config, 1, Opts) of
+ {value, {config, [File | _] = Files}, NoConfigOpts}
+ when is_integer(File) =/= true ->
+ [{config, lists:flatmap(fun add_def_cb/1, Files)} | NoConfigOpts];
+ {value, {config, File}, NoConfigOpts} ->
+ [{config, add_def_cb(File)} | NoConfigOpts];
+ false ->
+ Opts
+ end.
+
+add_def_cb([]) ->
+ [];
+add_def_cb(Config) when is_tuple(Config) ->
+ [Config];
+add_def_cb([H|_T] = Config ) when is_integer(H) ->
+ [{?ct_config_txt, [Config]}].
+
+read_config_files(Opts) ->
+ AddCallback = fun(CallBack, []) ->
+ [{CallBack, []}];
+ (CallBack, [F|_]=Files) when is_integer(F) ->
+ [{CallBack, Files}];
+ (CallBack, [F|_]=Files) when is_list(F) ->
+ lists:map(fun(X) -> {CallBack, X} end, Files)
+ end,
+
+ ConfigFiles = case lists:keyfind(config, 1, Opts) of
+ {config,ConfigLists}->
+ lists:foldr(fun({Callback,Files}, Acc) ->
+ AddCallback(Callback,Files)
+ ++ Acc
+ end,[],ConfigLists);
+ false->
+ []
+ end,
+ read_config_files_int(ConfigFiles, fun store_config/3).
+
+read_config_files_int([{Callback, File}|Files], FunToSave) ->
+ case Callback:read_config(File) of
+ {ok, Config} ->
+ FunToSave(Config, Callback, File),
+ read_config_files_int(Files, FunToSave);
+ {error, ErrorName, ErrorDetail}->
+ {user_error, {ErrorName, File, ErrorDetail}}
+ end;
+read_config_files_int([], _FunToSave) ->
+ ok.
+
+store_config(Config, Callback, File) ->
+ [ets:insert(?attr_table,
+ #ct_conf{key=Key,
+ value=Val,
+ handler=Callback,
+ config=File,
+ ref=ct_util:ct_make_ref(),
+ default=false}) ||
+ {Key,Val} <- Config].
+
+keyfindall(Key, Pos, List) ->
+ [E || E <- List, element(Pos, E) =:= Key].
+
+rewrite_config(Config, Callback, File) ->
+ OldRows = ets:match_object(?attr_table,
+ #ct_conf{handler=Callback,
+ config=File,_='_'}),
+ ets:match_delete(?attr_table,
+ #ct_conf{handler=Callback,
+ config=File,_='_'}),
+ Updater = fun({Key, Value}) ->
+ case keyfindall(Key, #ct_conf.key, OldRows) of
+ []->
+ ets:insert(?attr_table,
+ #ct_conf{key=Key,
+ value=Value,
+ handler=Callback,
+ config=File,
+ ref=ct_util:ct_make_ref()});
+ RowsToUpdate ->
+ Inserter = fun(Row) ->
+ ets:insert(?attr_table,
+ Row#ct_conf{value=Value,
+ ref=ct_util:ct_make_ref()})
+ end,
+ lists:foreach(Inserter, RowsToUpdate)
+ end
+ end,
+ [Updater({Key, Value})||{Key, Value}<-Config].
+
+set_config(Config,Default) ->
+ set_config('_UNDEF',Config,Default).
+
+set_config(Name,Config,Default) ->
+ [ets:insert(?attr_table,
+ #ct_conf{key=Key,value=Val,ref=ct_util:ct_make_ref(),
+ name=Name,default=Default}) ||
+ {Key,Val} <- Config].
+
+get_config(KeyOrName) ->
+ get_config(KeyOrName,undefined,[]).
+
+get_config(KeyOrName,Default) ->
+ get_config(KeyOrName,Default,[]).
+
+get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) ->
+ case lookup_config(KeyOrName) of
+ [] ->
+ Default;
+ [{_Ref,Val}|_] = Vals ->
+ case {lists:member(all,Opts),lists:member(element,Opts)} of
+ {true,true} ->
+ [{KeyOrName,V} || {_R,V} <- lists:sort(Vals)];
+ {true,false} ->
+ [V || {_R,V} <- lists:sort(Vals)];
+ {false,true} ->
+ {KeyOrName,Val};
+ {false,false} ->
+ Val
+ end
+ end;
+
+get_config({KeyOrName,SubKey},Default,Opts) ->
+ case lookup_config(KeyOrName) of
+ [] ->
+ Default;
+ Vals ->
+ Vals1 = case [Val || {_Ref,Val} <- lists:sort(Vals)] of
+ Result=[L|_] when is_list(L) ->
+ case L of
+ [{_,_}|_] ->
+ Result;
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end,
+ case get_subconfig([SubKey],Vals1,[],Opts) of
+ {ok,[{_,SubVal}|_]=SubVals} ->
+ case {lists:member(all,Opts),lists:member(element,Opts)} of
+ {true,true} ->
+ [{{KeyOrName,SubKey},Val} || {_,Val} <- SubVals];
+ {true,false} ->
+ [Val || {_SubKey,Val} <- SubVals];
+ {false,true} ->
+ {{KeyOrName,SubKey},SubVal};
+ {false,false} ->
+ SubVal
+ end;
+ _ ->
+ Default
+ end
+ end.
+
+get_subconfig(SubKeys,Values) ->
+ get_subconfig(SubKeys,Values,[],[]).
+
+get_subconfig(SubKeys,[Value|Rest],Mapped,Opts) ->
+ case do_get_config(SubKeys,Value,[]) of
+ {ok,SubMapped} ->
+ case lists:member(all,Opts) of
+ true ->
+ get_subconfig(SubKeys,Rest,Mapped++SubMapped,Opts);
+ false ->
+ {ok,SubMapped}
+ end;
+ _Error ->
+ get_subconfig(SubKeys,Rest,Mapped,Opts)
+ end;
+get_subconfig(SubKeys,[],[],_) ->
+ {error,{not_available,SubKeys}};
+get_subconfig(_SubKeys,[],Mapped,_) ->
+ {ok,Mapped}.
+
+do_get_config([Key|Required],Available,Mapped) ->
+ case lists:keysearch(Key,1,Available) of
+ {value,{Key,Value}} ->
+ NewAvailable = lists:keydelete(Key,1,Available),
+ NewMapped = [{Key,Value}|Mapped],
+ do_get_config(Required,NewAvailable,NewMapped);
+ false ->
+ {error,{not_available,Key}}
+ end;
+do_get_config([],_Available,Mapped) ->
+ {ok,lists:reverse(Mapped)}.
+
+get_all_config() ->
+ ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3',
+ default='$4',_='_'},
+ [],
+ [{{'$1','$2','$3','$4'}}]}]).
+
+lookup_config(KeyOrName) ->
+ case lookup_name(KeyOrName) of
+ [] ->
+ lookup_key(KeyOrName);
+ Values ->
+ Values
+ end.
+
+lookup_name(Name) ->
+ ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'},
+ [],
+ [{{'$1','$2'}}]}]).
+lookup_key(Key) ->
+ ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'},
+ [],
+ [{{'$1','$2'}}]}]).
+
+lookup_handler_for_config({Key, _Subkey}) ->
+ lookup_handler_for_config(Key);
+lookup_handler_for_config(KeyOrName) ->
+ case lookup_handler_for_name(KeyOrName) of
+ [] ->
+ lookup_handler_for_key(KeyOrName);
+ Values ->
+ Values
+ end.
+
+lookup_handler_for_name(Name) ->
+ ets:select(?attr_table,[{#ct_conf{handler='$1',config='$2',name=Name,_='_'},
+ [],
+ [{{'$1','$2'}}]}]).
+
+lookup_handler_for_key(Key) ->
+ ets:select(?attr_table,[{#ct_conf{handler='$1',config='$2',key=Key,_='_'},
+ [],
+ [{{'$1','$2'}}]}]).
+
+
+update_conf(Name, NewConfig) ->
+ Old = ets:select(?attr_table,[{#ct_conf{name=Name,_='_'},[],['$_']}]),
+ lists:foreach(fun(OldElem) ->
+ NewElem = OldElem#ct_conf{value=NewConfig},
+ ets:delete_object(?attr_table, OldElem),
+ ets:insert(?attr_table, NewElem)
+ end, Old),
+ ok.
+
+reload_conf(KeyOrName) ->
+ case lookup_handler_for_config(KeyOrName) of
+ []->
+ undefined;
+ HandlerList->
+ HandlerList2 = lists:usort(HandlerList),
+ read_config_files_int(HandlerList2, fun rewrite_config/3),
+ get_config(KeyOrName)
+ end.
+
+release_allocated() ->
+ Allocated = ets:select(?attr_table,[{#ct_conf{name='$1',_='_'},
+ [{'=/=','$1','_UNDEF'}],
+ ['$_']}]),
+ release_allocated(Allocated).
+release_allocated([H|T]) ->
+ ets:delete_object(?attr_table,H),
+ ets:insert(?attr_table,H#ct_conf{name='_UNDEF'}),
+ release_allocated(T);
+release_allocated([]) ->
+ ok.
+
+allocate(Name,Key,SubKeys) ->
+ case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of
+ [] ->
+ {error,{not_available,Key}};
+ Available ->
+ case allocate_subconfig(Name,SubKeys,Available,false) of
+ ok ->
+ ok;
+ Error ->
+ Error
+ end
+ end.
+
+allocate_subconfig(Name,SubKeys,[C=#ct_conf{value=Value}|Rest],Found) ->
+ case do_get_config(SubKeys,Value,[]) of
+ {ok,_SubMapped} ->
+ ets:insert(?attr_table,C#ct_conf{name=Name}),
+ allocate_subconfig(Name,SubKeys,Rest,true);
+ _Error ->
+ allocate_subconfig(Name,SubKeys,Rest,Found)
+ end;
+allocate_subconfig(_Name,_SubKeys,[],true) ->
+ ok;
+allocate_subconfig(_Name,SubKeys,[],false) ->
+ {error,{not_available,SubKeys}}.
+
+delete_config(Default) ->
+ ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}),
+ ok.
+
+require(Key) when is_atom(Key) ->
+ require({Key,[]});
+require({Key,SubKeys}) when is_atom(Key) ->
+ allocate('_UNDEF',Key,to_list(SubKeys));
+require(Key) ->
+ {error,{invalid,Key}}.
+
+require(Name,Key) when is_atom(Key) ->
+ require(Name,{Key,[]});
+require(Name,{Key,SubKeys}) when is_atom(Name), is_atom(Key) ->
+ call({require,Name,Key,to_list(SubKeys)});
+require(Name,Keys) ->
+ {error,{invalid,{Name,Keys}}}.
+
+to_list(X) when is_list(X) -> X;
+to_list(X) -> [X].
+
+do_require(Name,Key,SubKeys) when is_list(SubKeys) ->
+ case get_key_from_name(Name) of
+ {error,_} ->
+ allocate(Name,Key,SubKeys);
+ {ok,Key} ->
+ %% already allocated - check that it has all required subkeys
+ Vals = [Val || {_Ref,Val} <- lookup_name(Name)],
+ case get_subconfig(SubKeys,Vals) of
+ {ok,_SubMapped} ->
+ ok;
+ Error ->
+ Error
+ end;
+ {ok,OtherKey} ->
+ {error,{name_in_use,Name,OtherKey}}
+ end.
+
+encrypt_config_file(SrcFileName, EncryptFileName) ->
+ case get_crypt_key_from_file() of
+ {error,_} = E ->
+ E;
+ Key ->
+ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key})
+ end.
+
+get_ref_from_name(Name) ->
+ case ets:select(?attr_table,[{#ct_conf{name=Name,ref='$1',_='_'},
+ [],
+ ['$1']}]) of
+ [Ref] ->
+ {ok,Ref};
+ _ ->
+ {error,{no_such_name,Name}}
+ end.
+
+get_name_from_ref(Ref) ->
+ case ets:select(?attr_table,[{#ct_conf{name='$1',ref=Ref,_='_'},
+ [],
+ ['$1']}]) of
+ [Name] ->
+ {ok,Name};
+ _ ->
+ {error,{no_such_ref,Ref}}
+ end.
+
+get_key_from_name(Name) ->
+ case ets:select(?attr_table,[{#ct_conf{name=Name,key='$1',_='_'},
+ [],
+ ['$1']}]) of
+ [Key|_] ->
+ {ok,Key};
+ _ ->
+ {error,{no_such_name,Name}}
+ end.
+
+encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) ->
+ case get_crypt_key_from_file(KeyFile) of
+ {error,_} = E ->
+ E;
+ Key ->
+ encrypt_config_file(SrcFileName, EncryptFileName, {key,Key})
+ end;
+
+encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) ->
+ crypto:start(),
+ {K1,K2,K3,IVec} = make_crypto_key(Key),
+ case file:read_file(SrcFileName) of
+ {ok,Bin0} ->
+ Bin1 = term_to_binary({SrcFileName,Bin0}),
+ Bin2 = case byte_size(Bin1) rem 8 of
+ 0 -> Bin1;
+ N -> list_to_binary([Bin1,random_bytes(8-N)])
+ end,
+ EncBin = crypto:des3_cbc_encrypt(K1, K2, K3, IVec, Bin2),
+ case file:write_file(EncryptFileName, EncBin) of
+ ok ->
+ io:format("~s --(encrypt)--> ~s~n",
+ [SrcFileName,EncryptFileName]),
+ ok;
+ {error,Reason} ->
+ {error,{Reason,EncryptFileName}}
+ end;
+ {error,Reason} ->
+ {error,{Reason,SrcFileName}}
+ end.
+
+decrypt_config_file(EncryptFileName, TargetFileName) ->
+ case get_crypt_key_from_file() of
+ {error,_} = E ->
+ E;
+ Key ->
+ decrypt_config_file(EncryptFileName, TargetFileName, {key,Key})
+ end.
+
+decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) ->
+ case get_crypt_key_from_file(KeyFile) of
+ {error,_} = E ->
+ E;
+ Key ->
+ decrypt_config_file(EncryptFileName, TargetFileName, {key,Key})
+ end;
+
+decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) ->
+ crypto:start(),
+ {K1,K2,K3,IVec} = make_crypto_key(Key),
+ case file:read_file(EncryptFileName) of
+ {ok,Bin} ->
+ DecBin = crypto:des3_cbc_decrypt(K1, K2, K3, IVec, Bin),
+ case catch binary_to_term(DecBin) of
+ {'EXIT',_} ->
+ {error,bad_file};
+ {_SrcFile,SrcBin} ->
+ case TargetFileName of
+ undefined ->
+ {ok,SrcBin};
+ _ ->
+ case file:write_file(TargetFileName, SrcBin) of
+ ok ->
+ io:format("~s --(decrypt)--> ~s~n",
+ [EncryptFileName,TargetFileName]),
+ ok;
+ {error,Reason} ->
+ {error,{Reason,TargetFileName}}
+ end
+ end
+ end;
+ {error,Reason} ->
+ {error,{Reason,EncryptFileName}}
+ end.
+
+get_crypt_key_from_file(File) ->
+ case file:read_file(File) of
+ {ok,Bin} ->
+ case catch string:tokens(binary_to_list(Bin), [$\n,$\r]) of
+ [Key] ->
+ Key;
+ _ ->
+ {error,{bad_crypt_file,File}}
+ end;
+ {error,Reason} ->
+ {error,{Reason,File}}
+ end.
+
+get_crypt_key_from_file() ->
+ CwdFile = filename:join(".",?cryptfile),
+ {Result,FullName} =
+ case file:read_file(CwdFile) of
+ {ok,Bin} ->
+ {Bin,CwdFile};
+ _ ->
+ case init:get_argument(home) of
+ {ok,[[Home]]} ->
+ HomeFile = filename:join(Home,?cryptfile),
+ case file:read_file(HomeFile) of
+ {ok,Bin} ->
+ {Bin,HomeFile};
+ _ ->
+ {{error,no_crypt_file},noent}
+ end;
+ _ ->
+ {{error,no_crypt_file},noent}
+ end
+ end,
+ case FullName of
+ noent ->
+ Result;
+ _ ->
+ case catch string:tokens(binary_to_list(Result), [$\n,$\r]) of
+ [Key] ->
+ io:format("~nCrypt key file: ~s~n", [FullName]),
+ Key;
+ _ ->
+ {error,{bad_crypt_file,FullName}}
+ end
+ end.
+
+make_crypto_key(String) ->
+ <<K1:8/binary,K2:8/binary>> = First = erlang:md5(String),
+ <<K3:8/binary,IVec:8/binary>> = erlang:md5([First|lists:reverse(String)]),
+ {K1,K2,K3,IVec}.
+
+random_bytes(N) ->
+ {A,B,C} = now(),
+ random:seed(A, B, C),
+ random_bytes_1(N, []).
+
+random_bytes_1(0, Acc) -> Acc;
+random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]).
+
+check_callback_load(Callback) ->
+ case code:is_loaded(Callback) of
+ {file, _Filename}->
+ check_exports(Callback);
+ false->
+ case code:load_file(Callback) of
+ {module, Callback}->
+ check_exports(Callback);
+ {error, Error}->
+ {error, Error}
+ end
+ end.
+
+check_exports(Callback) ->
+ Fs = Callback:module_info(exports),
+ case {lists:member({check_parameter,1},Fs),
+ lists:member({read_config,1},Fs)} of
+ {true, true} ->
+ {ok, Callback};
+ _ ->
+ {error, missing_callback_functions}
+ end.
+
+check_config_files(Configs) ->
+ ConfigChecker = fun
+ ({Callback, [F|_R]=Files}) ->
+ case check_callback_load(Callback) of
+ {ok, Callback} ->
+ if is_integer(F) ->
+ Callback:check_parameter(Files);
+ is_list(F) ->
+ lists:map(fun(File) ->
+ Callback:check_parameter(File)
+ end,
+ Files)
+ end;
+ {error, Why}->
+ {error, {callback, {Callback,Why}}}
+ end;
+ ({Callback, []}) ->
+ case check_callback_load(Callback) of
+ {ok, Callback}->
+ Callback:check_parameter([]);
+ {error, Why}->
+ {error, {callback, {Callback,Why}}}
+ end
+ end,
+ lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))).
+
+prepare_user_configs([ConfigString|UserConfigs], Acc, new) ->
+ prepare_user_configs(UserConfigs,
+ [{list_to_atom(ConfigString), []}|Acc],
+ cur);
+prepare_user_configs(["and"|UserConfigs], Acc, _) ->
+ prepare_user_configs(UserConfigs, Acc, new);
+prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur) ->
+ prepare_user_configs(UserConfigs,
+ [{LastMod, [ConfigString|LastList]}|Acc],
+ cur);
+prepare_user_configs([], Acc, _) ->
+ Acc.
+
+prepare_config_list(Args) ->
+ ConfigFiles = case lists:keysearch(ct_config, 1, Args) of
+ {value,{ct_config,Files}}->
+ [{?ct_config_txt,[filename:absname(F) || F <- Files]}];
+ false->
+ []
+ end,
+ UserConfigs = case lists:keysearch(userconfig, 1, Args) of
+ {value,{userconfig,UserConfigFiles}}->
+ prepare_user_configs(UserConfigFiles, [], new);
+ false->
+ []
+ end,
+ ConfigFiles ++ UserConfigs.
+
+% TODO: add logging of the loaded configuration file to the CT FW log!!!
+add_config(Callback, []) ->
+ read_config_files_int([{Callback, []}], fun store_config/3);
+add_config(Callback, [File|_Files]=Config) when is_list(File) ->
+ lists:foreach(fun(CfgStr) ->
+ read_config_files_int([{Callback, CfgStr}], fun store_config/3) end,
+ Config);
+add_config(Callback, [C|_]=Config) when is_integer(C) ->
+ read_config_files_int([{Callback, Config}], fun store_config/3),
+ ok.
+
+remove_config(Callback, Config) ->
+ ets:match_delete(?attr_table,
+ #ct_conf{handler=Callback,
+ config=Config,_='_'}),
+ ok.
diff --git a/lib/common_test/src/ct_config_plain.erl b/lib/common_test/src/ct_config_plain.erl
new file mode 100644
index 0000000000..3fbc8af9fb
--- /dev/null
+++ b/lib/common_test/src/ct_config_plain.erl
@@ -0,0 +1,109 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%----------------------------------------------------------------------
+%% File : ct_config_plain.erl
+%% Description : CT callback module for reading configs from text files
+%%
+%% Created : 15 February 2010
+%%----------------------------------------------------------------------
+-module(ct_config_plain).
+-export([read_config/1, check_parameter/1]).
+
+read_config(ConfigFile) ->
+ case file:consult(ConfigFile) of
+ {ok,Config} ->
+ {ok, Config};
+ {error,enoent} ->
+ {error, config_file_error, enoent};
+ {error,Reason} ->
+ Key =
+ case application:get_env(common_test, decrypt) of
+ {ok,KeyOrFile} ->
+ case KeyOrFile of
+ {key,K} ->
+ K;
+ {file,F} ->
+ ct_config:get_crypt_key_from_file(F)
+ end;
+ _ ->
+ ct_config:get_crypt_key_from_file()
+ end,
+ case Key of
+ {error,no_crypt_file} ->
+ {error, config_file_error, Reason};
+ {error,CryptError} ->
+ {error, decrypt_file_error, CryptError};
+ _ when is_list(Key) ->
+ case ct_config:decrypt_config_file(ConfigFile, undefined, {key,Key}) of
+ {ok,CfgBin} ->
+ case read_config_terms(CfgBin) of
+ {error,ReadFail} ->
+ {error, config_file_error, ReadFail};
+ Config ->
+ {ok, Config}
+ end;
+ {error,DecryptFail} ->
+ {error, decrypt_config_error, DecryptFail}
+ end;
+ _ ->
+ {error, bad_decrypt_key, Key}
+ end
+ end.
+
+% check if config file exists
+check_parameter(File)->
+ case filelib:is_file(File) of
+ true->
+ {ok, {file, File}};
+ false->
+ {error, {nofile, File}}
+ end.
+
+read_config_terms(Bin) when is_binary(Bin) ->
+ case catch binary_to_list(Bin) of
+ {'EXIT',_} ->
+ {error,invalid_textfile};
+ Lines ->
+ read_config_terms(Lines)
+ end;
+read_config_terms(Lines) when is_list(Lines) ->
+ read_config_terms1(erl_scan:tokens([], Lines, 0), 1, [], []).
+
+read_config_terms1({done,{ok,Ts,EL},Rest}, L, Terms, _) ->
+ case erl_parse:parse_term(Ts) of
+ {ok,Term} when Rest == [] ->
+ lists:reverse([Term|Terms]);
+ {ok,Term} ->
+ read_config_terms1(erl_scan:tokens([], Rest, 0),
+ EL+1, [Term|Terms], Rest);
+ _ ->
+ {error,{bad_term,{L,EL}}}
+ end;
+read_config_terms1({done,{eof,_},_}, _, Terms, Rest) when Rest == [] ->
+ lists:reverse(Terms);
+read_config_terms1({done,{eof,EL},_}, L, _, _) ->
+ {error,{bad_term,{L,EL}}};
+read_config_terms1({done,{error,Info,EL},_}, L, _, _) ->
+ {error,{Info,{L,EL}}};
+read_config_terms1({more,_}, L, Terms, Rest) ->
+ case string:tokens(Rest, [$\n,$\r,$\t]) of
+ [] ->
+ lists:reverse(Terms);
+ _ ->
+ {error,{bad_term,L}}
+ end.
diff --git a/lib/common_test/src/ct_config_xml.erl b/lib/common_test/src/ct_config_xml.erl
new file mode 100644
index 0000000000..8a6e75e635
--- /dev/null
+++ b/lib/common_test/src/ct_config_xml.erl
@@ -0,0 +1,118 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%----------------------------------------------------------------------
+%% File : ct_config_xml.erl
+%% Description : CT callback module for reading configs from XML files
+%%
+%% Created : 16 February 2010
+%%----------------------------------------------------------------------
+-module(ct_config_xml).
+-export([read_config/1, check_parameter/1]).
+
+% read config file
+read_config(ConfigFile) ->
+ case catch do_read_xml_config(ConfigFile) of
+ {ok, Config}->
+ {ok, Config};
+ {error, Error, ErroneousString}->
+ {error, Error, ErroneousString}
+ end.
+
+% check file exists
+check_parameter(File)->
+ case filelib:is_file(File) of
+ true->
+ {ok, {file, File}};
+ false->
+ {error, {nofile, File}}
+ end.
+
+% actual reading of the config
+do_read_xml_config(ConfigFile)->
+ case catch xmerl_sax_parser:file(ConfigFile,
+ [{event_fun, fun event/3},
+ {event_state, []}]) of
+ {ok, EntityList, _}->
+ {ok, lists:reverse(transform_entity_list(EntityList))};
+ Oops->
+ {error, parsing_failed, Oops}
+ end.
+
+% event callback for xmerl_sax_parser
+event(Event, _LineNo, State) ->
+ tag(Event, State).
+
+% document start
+tag(startDocument, State) ->
+ State;
+
+% start of the config
+tag({startElement, _Uri, "config", _QName, _Attributes}, []) ->
+ [{"config", []}];
+
+% start tag
+tag({startElement, _Uri, Name, _QName, _Attributes}, Tags) ->
+ [{Name, []}|Tags];
+
+% value
+tag({characters, String}, [{Tag, _Value}|Tags]) ->
+ [{Tag, String}|Tags];
+
+% end tag
+tag({endElement, _Uri, _Name, _QName},
+ [Entity, {PrevEntityTag, PrevEntityValue}|Tags]) ->
+ NewHead = {PrevEntityTag, [Entity|PrevEntityValue]},
+ [NewHead|Tags];
+
+% end of the config
+tag({endElement, _Uri, "config", _QName}, [{"config", Config}]) ->
+ Config;
+
+% end of document, return result
+tag(endDocument, {_Tags, Result}) ->
+ Result;
+
+% default
+tag(_El, State) ->
+ State.
+
+% transform of the ugly deeply nested entity list to the key-value "tree"
+transform_entity_list(EntityList)->
+ lists:map(fun transform_entity/1, EntityList).
+
+% transform entity from {list(), list()} to {atom(), term()}
+transform_entity({Tag, [Value|Rest]}) when
+ is_tuple(Value)->
+ {list_to_atom(Tag), transform_entity_list(lists:reverse([Value|Rest]))};
+transform_entity({Tag, String})->
+ case list_to_term(String) of
+ {ok, Value}->
+ {list_to_atom(Tag), Value};
+ Error->
+ throw(Error)
+ end.
+
+% transform a string with Erlang terms
+list_to_term(String) ->
+ {ok, T, _} = erl_scan:string(String++"."),
+ case catch erl_parse:parse_term(T) of
+ {ok, Term} ->
+ {ok, Term};
+ Error ->
+ {error, Error, String}
+ end.
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index ed8b564921..38a2aa53ac 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,11 +24,15 @@
-module(ct_framework).
--export([init_tc/3, end_tc/3, get_suite/2, report/2, warn/1]).
+-export([init_tc/3, end_tc/4, get_suite/2, report/2, warn/1]).
-export([error_notification/4]).
+-export([overview_html_header/1]).
+
-export([error_in_suite/1, ct_init_per_group/2, ct_end_per_group/2]).
+-export([make_all_conf/3, make_conf/5]).
+
-include("ct_event.hrl").
-include("ct_util.hrl").
@@ -101,7 +105,8 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
[{saved_config,{LastFunc,SavedConfig}} |
lists:keydelete(saved_config,1,Config0)];
{{LastSuite,InitOrEnd},SavedConfig} when InitOrEnd == init_per_suite ;
- InitOrEnd == end_per_suite -> % last suite
+ InitOrEnd == end_per_suite ->
+ %% last suite
[{saved_config,{LastSuite,SavedConfig}} |
lists:keydelete(saved_config,1,Config0)];
undefined ->
@@ -113,9 +118,9 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
ok;
true ->
%% delete all default values used in previous suite
- ct_util:delete_default_config(suite),
+ ct_config:delete_default_config(suite),
%% release all name -> key bindings (once per suite)
- ct_util:release_allocated()
+ ct_config:release_allocated()
end,
TestCaseInfo =
case catch apply(Mod,Func,[]) of
@@ -125,7 +130,7 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) ->
%% clear all config data default values set by previous
%% testcase info function (these should only survive the
%% testcase, not the whole suite)
- ct_util:delete_default_config(testcase),
+ ct_config:delete_default_config(testcase),
case add_defaults(Mod,Func,TestCaseInfo,DoInit) of
Error = {suite0_failed,_} ->
ct_logs:init_tc(),
@@ -161,6 +166,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
_ ->
MergeResult
end,
+
%% timetrap must be handled before require
MergedInfo1 = timetrap_first(MergedInfo, [], []),
%% tell logger to use specified style sheet
@@ -201,7 +207,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
{skip,{require_failed_in_suite0,Reason}};
{error,Reason} ->
{auto_skip,{require_failed,Reason}};
- FinalConfig ->
+ {ok, FinalConfig} ->
case MergeResult of
{error,Reason} ->
%% suite0 configure finished now, report that
@@ -210,13 +216,25 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) ->
_ ->
case get('$test_server_framework_test') of
undefined ->
- FinalConfig;
+ ct_suite_init(Mod, FuncSpec, FinalConfig);
Fun ->
- Fun(init_tc, FinalConfig)
+ case Fun(init_tc, FinalConfig) of
+ NewConfig when is_list(NewConfig) ->
+ {ok,NewConfig};
+ Else ->
+ Else
+ end
end
end
end.
-
+
+ct_suite_init(Mod, Func, [Config]) when is_list(Config) ->
+ case ct_hooks:init_tc( Mod, Func, Config) of
+ NewConfig when is_list(NewConfig) ->
+ {ok, [NewConfig]};
+ Else ->
+ Else
+ end.
add_defaults(Mod,Func,FuncInfo,DoInit) ->
case (catch Mod:suite()) of
@@ -233,7 +251,9 @@ add_defaults(Mod,Func,FuncInfo,DoInit) ->
(_) -> false
end, SuiteInfo) of
true ->
- SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfo),
+ SuiteInfoNoCTH =
+ lists:keydelete(ct_hooks,1,SuiteInfo),
+ SuiteInfo1 = merge_with_suite_defaults(Mod,SuiteInfoNoCTH),
case add_defaults1(Mod,Func,FuncInfo,SuiteInfo1,DoInit) of
Error = {error,_} -> {SuiteInfo1,Error};
MergedInfo -> {SuiteInfo1,MergedInfo}
@@ -244,8 +264,8 @@ add_defaults(Mod,Func,FuncInfo,DoInit) ->
_ ->
{suite0_failed,bad_return_value}
end.
-
-add_defaults1(_Mod,init_per_suite,[],SuiteInfo,_) ->
+
+add_defaults1(_Mod,init_per_suite,[],SuiteInfo,_DoInit) ->
SuiteInfo;
add_defaults1(Mod,Func,FuncInfo,SuiteInfo,DoInit) ->
@@ -253,15 +273,27 @@ add_defaults1(Mod,Func,FuncInfo,SuiteInfo,DoInit) ->
%% can result in weird behaviour (suite values get overwritten)
SuiteReqs =
[SDDef || SDDef <- SuiteInfo,
- require == element(1,SDDef)],
- case [element(2,Clash) || Clash <- SuiteReqs,
- true == lists:keymember(element(2,Clash),2,FuncInfo)] of
+ ((require == element(1,SDDef)) or
+ (default_config == element(1,SDDef)))],
+ FuncReqs =
+ [FIDef || FIDef <- FuncInfo,
+ require == element(1,FIDef)],
+ case [element(2,Clash) || Clash <- SuiteReqs,
+ require == element(1, Clash),
+ true == lists:keymember(element(2,Clash),2,
+ FuncReqs)] of
[] ->
add_defaults2(Mod,Func,FuncInfo,SuiteInfo,SuiteReqs,DoInit);
Clashes ->
{error,{config_name_already_in_use,Clashes}}
end.
+add_defaults2(Mod,init_per_suite,IPSInfo,SuiteInfo,SuiteReqs,false) ->
+ %% not common practise to use a test case info function for
+ %% init_per_suite (usually handled by suite/0), but let's support
+ %% it just in case...
+ add_defaults2(Mod,init_per_suite,IPSInfo,SuiteInfo,SuiteReqs,true);
+
add_defaults2(_Mod,_Func,FuncInfo,SuiteInfo,_,false) ->
%% include require elements from test case info, but not from suite/0
%% (since we've already required those vars)
@@ -344,6 +376,8 @@ configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) ->
configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,Config) ->
Dog = test_server:timetrap(Time),
configure(Rest,Info,SuiteInfo,Scope,[{watchdog,Dog}|Config]);
+configure([{ct_hooks, Hook} | Rest], Info, SuiteInfo, Scope, Config) ->
+ configure(Rest, Info, SuiteInfo, Scope, [{ct_hooks, Hook} | Config]);
configure([_|Rest],Info,SuiteInfo,Scope,Config) ->
configure(Rest,Info,SuiteInfo,Scope,Config);
configure([],_,_,_,Config) ->
@@ -381,10 +415,10 @@ try_set_default(Name,Key,Info,Where) ->
{_,[]} ->
no_default;
{'_UNDEF',_} ->
- [ct_util:set_default_config([CfgVal],Where) || CfgVal <- CfgElems],
+ [ct_config:set_default_config([CfgVal],Where) || CfgVal <- CfgElems],
ok;
_ ->
- [ct_util:set_default_config(Name,[CfgVal],Where) || CfgVal <- CfgElems],
+ [ct_config:set_default_config(Name,[CfgVal],Where) || CfgVal <- CfgElems],
ok
end.
@@ -400,14 +434,14 @@ try_set_default(Name,Key,Info,Where) ->
%%%
%%% @doc Test server framework callback, called by the test_server
%%% when a test case is finished.
-end_tc(?MODULE,error_in_suite,_) -> % bad start!
+end_tc(?MODULE,error_in_suite,_, _) -> % bad start!
ok;
-end_tc(Mod,Func,{TCPid,Result,[Args]}) when is_pid(TCPid) ->
- end_tc(Mod,Func,TCPid,Result,Args);
-end_tc(Mod,Func,{Result,[Args]}) ->
- end_tc(Mod,Func,self(),Result,Args).
+end_tc(Mod,Func,{TCPid,Result,[Args]}, Return) when is_pid(TCPid) ->
+ end_tc(Mod,Func,TCPid,Result,Args,Return);
+end_tc(Mod,Func,{Result,[Args]}, Return) ->
+ end_tc(Mod,Func,self(),Result,Args,Return).
-end_tc(Mod,Func,TCPid,Result,Args) ->
+end_tc(Mod,Func,TCPid,Result,Args,Return) ->
case lists:keysearch(watchdog,1,Args) of
{value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog);
false -> ok
@@ -430,8 +464,10 @@ end_tc(Mod,Func,TCPid,Result,Args) ->
{_,GroupName,_Props} = Group ->
case lists:keysearch(save_config,1,Args) of
{value,{save_config,SaveConfig}} ->
- ct_util:save_suite_data(last_saved_config,
- {Mod,{group,GroupName}},SaveConfig),
+ ct_util:save_suite_data(
+ last_saved_config,
+ {Mod,{group,GroupName}},
+ SaveConfig),
Group;
false ->
Group
@@ -448,12 +484,33 @@ end_tc(Mod,Func,TCPid,Result,Args) ->
end,
ct_util:reset_silent_connections(),
- %% send sync notification so that event handlers may print
- %% in the log file before it gets closed
- ct_event:sync_notify(#event{name=tc_done,
- node=node(),
- data={Mod,FuncSpec,tag(Result)}}),
- case Result of
+ case get('$test_server_framework_test') of
+ undefined ->
+ {FinalResult,FinalNotify} =
+ case ct_hooks:end_tc(
+ Mod, FuncSpec, Args, Result, Return) of
+ '$ct_no_change' ->
+ {FinalResult = ok,Result};
+ FinalResult ->
+ {FinalResult,FinalResult}
+ end,
+ % send sync notification so that event handlers may print
+ % in the log file before it gets closed
+ ct_event:sync_notify(#event{name=tc_done,
+ node=node(),
+ data={Mod,FuncSpec,
+ tag_cth(FinalNotify)}});
+ Fun ->
+ % send sync notification so that event handlers may print
+ % in the log file before it gets closed
+ ct_event:sync_notify(#event{name=tc_done,
+ node=node(),
+ data={Mod,FuncSpec,tag(Result)}}),
+ FinalResult = Fun(end_tc, Return)
+ end,
+
+
+ case FinalResult of
{skip,{sequence_failed,_,_}} ->
%% ct_logs:init_tc is never called for a skipped test case
%% in a failing sequence, so neither should end_tc
@@ -472,12 +529,7 @@ end_tc(Mod,Func,TCPid,Result,Args) ->
_ ->
ok
end,
- case get('$test_server_framework_test') of
- undefined ->
- ok;
- Fun ->
- Fun(end_tc, ok)
- end.
+ FinalResult.
%% {error,Reason} | {skip,Reason} | {timetrap_timeout,TVal} |
%% {testcase_aborted,Reason} | testcase_aborted_or_killed |
@@ -493,6 +545,21 @@ tag(E = testcase_aborted_or_killed) ->
tag(Other) ->
Other.
+tag_cth({STag,Reason}) when STag == skip; STag == skipped ->
+ {skipped,Reason};
+tag_cth({fail, Reason}) ->
+ {failed, {error,Reason}};
+tag_cth(E = {ETag,_}) when ETag == error; ETag == 'EXIT';
+ ETag == timetrap_timeout;
+ ETag == testcase_aborted ->
+ {failed,E};
+tag_cth(E = testcase_aborted_or_killed) ->
+ {failed,E};
+tag_cth(List) when is_list(List) ->
+ ok;
+tag_cth(Other) ->
+ Other.
+
%%%-----------------------------------------------------------------
%%% @spec error_notification(Mod,Func,Args,Error) -> ok
%%% Mod = atom()
@@ -631,12 +698,12 @@ group_or_func(Func, _Config) ->
%%% and every test case. If the former, all test cases in the suite
%%% should be returned.
-get_suite(Mod, all) ->
+get_suite(Mod, all) ->
case catch apply(Mod, groups, []) of
{'EXIT',_} ->
get_all(Mod, []);
GroupDefs when is_list(GroupDefs) ->
- case catch check_groups(Mod, GroupDefs) of
+ case catch find_groups(Mod, all, all, GroupDefs) of
{error,_} = Error ->
%% this makes test_server call error_in_suite as first
%% (and only) test case so we can report Error properly
@@ -651,96 +718,224 @@ get_suite(Mod, all) ->
%%!============================================================
%%! Note: The handling of sequences in get_suite/2 and get_all/2
-%%! is deprecated and should be removed after OTP R13!
+%%! is deprecated and should be removed at some point...
%%!============================================================
-get_suite(Mod, Name) ->
- %% Name may be name of a group or a test case. If it's a group,
- %% it should be expanded to list of cases (in a conf term)
+%% group
+get_suite(Mod, Group={conf,Props,_Init,TCs,_End}) ->
+ Name = proplists:get_value(name, Props),
case catch apply(Mod, groups, []) of
{'EXIT',_} ->
- get_seq(Mod, Name);
+ [Group];
GroupDefs when is_list(GroupDefs) ->
- case catch check_groups(Mod, GroupDefs) of
+ case catch find_groups(Mod, Name, TCs, GroupDefs) of
{error,_} = Error ->
%% this makes test_server call error_in_suite as first
%% (and only) test case so we can report Error properly
[{?MODULE,error_in_suite,[[Error]]}];
+ [] ->
+ {error,{invalid_group_spec,Name}};
ConfTests ->
- FindConf = fun({conf,Props,_,_,_}) ->
- case proplists:get_value(name, Props) of
- Name -> true;
- _ -> false
- end
- end,
- case lists:filter(FindConf, ConfTests) of
- [] -> % must be a test case
- get_seq(Mod, Name);
- [ConfTest|_] ->
- ConfTest
+ case lists:member(skipped, Props) of
+ true ->
+ %% a *subgroup* specified *only* as skipped (and not
+ %% as an explicit test) should not be returned, or
+ %% init/end functions for top groups will be executed
+ case catch proplists:get_value(name, element(2, hd(ConfTests))) of
+ Name -> % top group
+ delete_subs(ConfTests, ConfTests);
+ _ ->
+ []
+ end;
+ false ->
+ delete_subs(ConfTests, ConfTests)
end
end;
_ ->
E = "Bad return value from "++atom_to_list(Mod)++":groups/0",
[{?MODULE,error_in_suite,[[{error,list_to_atom(E)}]]}]
- end.
+ end;
-check_groups(_Mod, []) ->
- [];
-check_groups(Mod, Defs) ->
- check_groups(Mod, Defs, Defs, []).
+%% testcase
+get_suite(Mod, Name) ->
+ get_seq(Mod, Name).
-check_groups(Mod, [TC | Gs], Defs, Levels) when is_atom(TC), length(Levels)>0 ->
- [TC | check_groups(Mod, Gs, Defs, Levels)];
+%%%-----------------------------------------------------------------
-check_groups(Mod, [{group,SubName} | Gs], Defs, Levels) when is_atom(SubName) ->
- case lists:member(SubName, Levels) of
- true ->
- E = "Cyclic reference to group "++atom_to_list(SubName)++
- " in "++atom_to_list(Mod)++":groups/0",
- throw({error,list_to_atom(E)});
- false ->
- case find_group(Mod, SubName, Defs) of
- {error,_} = Error ->
- throw(Error);
- G ->
- [check_groups(Mod, [G], Defs, Levels) |
- check_groups(Mod, Gs, Defs, Levels)]
- end
+find_groups(Mod, Name, TCs, GroupDefs) ->
+ Found = find(Mod, Name, TCs, GroupDefs, [], GroupDefs, false),
+ Trimmed = trim(Found),
+ %% I cannot find a reason to why this function is called,
+ %% It deletes any group which is referenced in any other
+ %% group. i.e.
+ %% groups() ->
+ %% [{test, [], [testcase1]},
+ %% {testcases, [], [{group, test}]}].
+ %% Would be changed to
+ %% groups() ->
+ %% [{testcases, [], [testcase1]}].
+ %% instead of what I believe is correct:
+ %% groups() ->
+ %% [{test, [], [testcase1]},
+ %% {testcases, [], [testcase1]}].
+ %% Have to double check with peppe
+ delete_subs(Trimmed, Trimmed),
+ Trimmed.
+
+find(Mod, all, _TCs, [{Name,Props,Tests} | Gs], Known, Defs, _)
+ when is_atom(Name), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name, Known),
+ [make_conf(Mod, Name, Props,
+ find(Mod, all, all, Tests, [Name | Known], Defs, true)) |
+ find(Mod, all, all, Gs, [], Defs, true)];
+
+find(Mod, Name, TCs, [{Name,Props,Tests} | _Gs], Known, Defs, false)
+ when is_atom(Name), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name, Known),
+ case TCs of
+ all ->
+ [make_conf(Mod, Name, Props,
+ find(Mod, Name, TCs, Tests, [Name | Known], Defs, true))];
+ _ ->
+ Tests1 = [TC || TC <- TCs,
+ lists:member(TC, Tests) == true],
+ [make_conf(Mod, Name, Props, Tests1)]
end;
-check_groups(Mod, [{Name,Tests} | Gs], Defs, Levels) when is_atom(Name),
- is_list(Tests) ->
- check_groups(Mod, [{Name,[],Tests} | Gs], Defs, Levels);
-
-check_groups(Mod, [{Name,Props,Tests} | Gs], Defs, Levels) when is_atom(Name),
- is_list(Props),
- is_list(Tests) ->
- {TestSpec,Levels1} =
- case Levels of
- [] ->
- {check_groups(Mod, Tests, Defs, [Name]),[]};
- _ ->
- {check_groups(Mod, Tests, Defs, [Name|Levels]),Levels}
- end,
- [make_conf(Mod, Name, Props, TestSpec) |
- check_groups(Mod, Gs, Defs, Levels1)];
+find(Mod, Name, TCs, [{Name1,Props,Tests} | Gs], Known, Defs, false)
+ when is_atom(Name1), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name1, Known),
+ [make_conf(Mod,Name1,Props,
+ find(Mod, Name, TCs, Tests, [Name1 | Known], Defs, false)) |
+ find(Mod, Name, TCs, Gs, [], Defs, false)];
+
+find(Mod, Name, _TCs, [{Name,_Props,_Tests} | _Gs], _Known, _Defs, true)
+ when is_atom(Name) ->
+ E = "Duplicate groups named "++atom_to_list(Name)++" in "++
+ atom_to_list(Mod)++":groups/0",
+ throw({error,list_to_atom(E)});
-check_groups(Mod, [BadTerm | _Gs], _Defs, Levels) ->
- Where = if length(Levels) == 0 ->
+find(Mod, Name, all, [{Name1,Props,Tests} | Gs], Known, Defs, true)
+ when is_atom(Name1), is_list(Props), is_list(Tests) ->
+ cyclic_test(Mod, Name1, Known),
+ [make_conf(Mod, Name1, Props,
+ find(Mod, Name, all, Tests, [Name1 | Known], Defs, true)) |
+ find(Mod, Name, all, Gs, [], Defs, true)];
+
+find(Mod, Name, TCs, [{group,Name1} | Gs], Known, Defs, Found)
+ when is_atom(Name1) ->
+ find(Mod, Name, TCs, [expand(Mod, Name1, Defs) | Gs], Known, Defs, Found);
+
+%% Undocumented remote group feature, use with caution
+find(Mod, Name, TCs, [{group, ExtMod, ExtGrp} | Gs], Known, Defs, true)
+ when is_atom(ExtMod), is_atom(ExtGrp) ->
+ ExternalDefs = ExtMod:groups(),
+ ExternalTCs = find(ExtMod, ExtGrp, TCs, [{group, ExtGrp}],
+ [], ExternalDefs, false),
+ ExternalTCs ++ find(Mod, Name, TCs, Gs, Known, Defs, true);
+
+find(Mod, Name, TCs, [{Name1,Tests} | Gs], Known, Defs, Found)
+ when is_atom(Name1), is_list(Tests) ->
+ find(Mod, Name, TCs, [{Name1,[],Tests} | Gs], Known, Defs, Found);
+
+find(Mod, Name, TCs, [_TC | Gs], Known, Defs, false) ->
+ find(Mod, Name, TCs, Gs, Known, Defs, false);
+
+find(Mod, Name, TCs, [TC | Gs], Known, Defs, true) when is_atom(TC) ->
+ [{Mod, TC} | find(Mod, Name, TCs, Gs, Known, Defs, true)];
+
+find(Mod, Name, TCs, [{ExternalTC, Case} = TC | Gs], Known, Defs, true)
+ when is_atom(ExternalTC),
+ is_atom(Case) ->
+ [TC | find(Mod, Name, TCs, Gs, Known, Defs, true)];
+
+find(Mod, _Name, _TCs, [BadTerm | _Gs], Known, _Defs, _Found) ->
+ Where = if length(Known) == 0 ->
atom_to_list(Mod)++":groups/0";
true ->
- "group "++atom_to_list(lists:last(Levels))++
+ "group "++atom_to_list(lists:last(Known))++
" in "++atom_to_list(Mod)++":groups/0"
end,
Term = io_lib:format("~p", [BadTerm]),
E = "Bad term "++lists:flatten(Term)++" in "++Where,
throw({error,list_to_atom(E)});
-check_groups(_Mod, [], _Defs, _) ->
+find(_Mod, _Name, _TCs, [], _Known, _Defs, false) ->
+ ['$NOMATCH'];
+
+find(_Mod, _Name, _TCs, [], _Known, _Defs, _Found) ->
[].
-find_group(Mod, Name, Defs) ->
+delete_subs([{conf, _,_,_,_} = Conf | Confs], All) ->
+ All1 = delete_conf(Conf, All),
+ case is_sub(Conf, All1) of
+ true ->
+ delete_subs(Confs, All1);
+ false ->
+ delete_subs(Confs, All)
+ end;
+delete_subs([_Else | Confs], All) ->
+ delete_subs(Confs, All);
+delete_subs([], All) ->
+ All.
+
+delete_conf({conf,Props,_,_,_}, Confs) ->
+ Name = proplists:get_value(name, Props),
+ [Conf || Conf = {conf,Props0,_,_,_} <- Confs,
+ Name =/= proplists:get_value(name, Props0)].
+
+is_sub({conf,Props,_,_,_}=Conf, [{conf,_,_,Tests,_} | Confs]) ->
+ Name = proplists:get_value(name, Props),
+ case lists:any(fun({conf,Props0,_,_,_}) ->
+ case proplists:get_value(name, Props0) of
+ N when N == Name ->
+ true;
+ _ ->
+ false
+ end;
+ (_) ->
+ false
+ end, Tests) of
+ true ->
+ true;
+ false ->
+ is_sub(Conf, Tests) or is_sub(Conf, Confs)
+ end;
+
+is_sub(Conf, [_TC | Tests]) ->
+ is_sub(Conf, Tests);
+
+is_sub(_Conf, []) ->
+ false.
+
+trim(['$NOMATCH' | Tests]) ->
+ trim(Tests);
+
+trim([{conf,Props,Init,Tests,End} | Confs]) ->
+ case trim(Tests) of
+ [] ->
+ trim(Confs);
+ Trimmed ->
+ [{conf,Props,Init,Trimmed,End} | trim(Confs)]
+ end;
+
+trim([TC | Tests]) ->
+ [TC | trim(Tests)];
+
+trim([]) ->
+ [].
+
+cyclic_test(Mod, Name, Names) ->
+ case lists:member(Name, Names) of
+ true ->
+ E = "Cyclic reference to group "++atom_to_list(Name)++
+ " in "++atom_to_list(Mod)++":groups/0",
+ throw({error,list_to_atom(E)});
+ false ->
+ ok
+ end.
+
+expand(Mod, Name, Defs) ->
case lists:keysearch(Name, 1, Defs) of
{value,Def} ->
Def;
@@ -750,7 +945,50 @@ find_group(Mod, Name, Defs) ->
throw({error,list_to_atom(E)})
end.
+make_all_conf(Dir, Mod, _Props) ->
+ case code:is_loaded(Mod) of
+ false ->
+ code:load_abs(filename:join(Dir,atom_to_list(Mod)));
+ _ ->
+ ok
+ end,
+ make_all_conf(Mod).
+
+make_all_conf(Mod) ->
+ case catch apply(Mod, groups, []) of
+ {'EXIT',_} ->
+ {error,{invalid_group_definition,Mod}};
+ GroupDefs when is_list(GroupDefs) ->
+ case catch find_groups(Mod, all, all, GroupDefs) of
+ {error,_} = Error ->
+ %% this makes test_server call error_in_suite as first
+ %% (and only) test case so we can report Error properly
+ [{?MODULE,error_in_suite,[[Error]]}];
+ [] ->
+ {error,{invalid_group_spec,Mod}};
+ ConfTests ->
+ [{conf,Props,Init,all,End} ||
+ {conf,Props,Init,_,End}
+ <- delete_subs(ConfTests, ConfTests)]
+ end
+ end.
+
+make_conf(Dir, Mod, Name, Props, TestSpec) ->
+ case code:is_loaded(Mod) of
+ false ->
+ code:load_abs(filename:join(Dir,atom_to_list(Mod)));
+ _ ->
+ ok
+ end,
+ make_conf(Mod, Name, Props, TestSpec).
+
make_conf(Mod, Name, Props, TestSpec) ->
+ case code:is_loaded(Mod) of
+ false ->
+ code:load_file(Mod);
+ _ ->
+ ok
+ end,
{InitConf,EndConf} =
case erlang:function_exported(Mod,init_per_group,2) of
true ->
@@ -761,6 +999,7 @@ make_conf(Mod, Name, Props, TestSpec) ->
end,
{conf,[{name,Name}|Props],InitConf,TestSpec,EndConf}.
+%%%-----------------------------------------------------------------
get_all(Mod, ConfTests) ->
case catch apply(Mod, all, []) of
@@ -776,31 +1015,11 @@ get_all(Mod, ConfTests) ->
[{?MODULE,error_in_suite,[[{error,What}]]}];
SeqsAndTCs ->
%% expand group references in all() using ConfTests
- Expand =
- fun({group,Name}) ->
- FindConf =
- fun({conf,Props,_,_,_}) ->
- case proplists:get_value(name, Props) of
- Name -> true;
- _ -> false
- end
- end,
- case lists:filter(FindConf, ConfTests) of
- [ConfTest|_] ->
- ConfTest;
- [] ->
- E = "Invalid reference to group "++
- atom_to_list(Name)++" in "++
- atom_to_list(Mod)++":all/0",
- throw({error,list_to_atom(E)})
- end;
- (SeqOrTC) -> SeqOrTC
- end,
- case catch lists:map(Expand, SeqsAndTCs) of
+ case catch expand_groups(SeqsAndTCs, ConfTests, Mod) of
{error,_} = Error ->
[{?MODULE,error_in_suite,[[Error]]}];
Tests ->
- Tests
+ delete_subs(Tests, Tests)
end
end;
Skip = {skip,_Reason} ->
@@ -811,6 +1030,30 @@ get_all(Mod, ConfTests) ->
[{?MODULE,error_in_suite,[[{error,Reason}]]}]
end.
+expand_groups([H | T], ConfTests, Mod) ->
+ [expand_groups(H, ConfTests, Mod) | expand_groups(T, ConfTests, Mod)];
+expand_groups([], _ConfTests, _Mod) ->
+ [];
+expand_groups({group,Name}, ConfTests, Mod) ->
+ FindConf =
+ fun({conf,Props,_,_,_}) ->
+ case proplists:get_value(name, Props) of
+ Name -> true;
+ _ -> false
+ end
+ end,
+ case lists:filter(FindConf, ConfTests) of
+ [ConfTest|_] ->
+ expand_groups(ConfTest, ConfTests, Mod);
+ [] ->
+ E = "Invalid reference to group "++
+ atom_to_list(Name)++" in "++
+ atom_to_list(Mod)++":all/0",
+ throw({error,list_to_atom(E)})
+ end;
+expand_groups(SeqOrTC, _ConfTests, _Mod) ->
+ SeqOrTC.
+
%%!============================================================
%%! The support for sequences by means of using sequences/0
@@ -980,6 +1223,18 @@ report(What,Data) ->
ok;
tc_done ->
{_Suite,Case,Result} = Data,
+ case Result of
+ {failed, _} ->
+ ct_hooks:on_tc_fail(What, Data);
+ {skipped,{failed,{_,init_per_testcase,_}}} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ {skipped,{require_failed,_}} ->
+ ct_hooks:on_tc_skip(tc_auto_skip, Data);
+ {skipped,_} ->
+ ct_hooks:on_tc_skip(tc_user_skip, Data);
+ _Else ->
+ ok
+ end,
case {Case,Result} of
{init_per_suite,_} ->
ok;
@@ -997,8 +1252,8 @@ report(What,Data) ->
add_to_stats(auto_skipped);
{_,{skipped,_}} ->
add_to_stats(user_skipped);
- {_,{FailOrSkip,_Reason}} ->
- add_to_stats(FailOrSkip)
+ {_,{SkipOrFail,_Reason}} ->
+ add_to_stats(SkipOrFail)
end;
tc_user_skip ->
%% test case specified as skipped in testspec
@@ -1006,6 +1261,7 @@ report(What,Data) ->
ct_event:sync_notify(#event{name=tc_user_skip,
node=node(),
data=Data}),
+ ct_hooks:on_tc_skip(What, Data),
add_to_stats(user_skipped);
tc_auto_skip ->
%% test case skipped because of error in init_per_suite
@@ -1018,6 +1274,7 @@ report(What,Data) ->
ct_event:sync_notify(#event{name=tc_auto_skip,
node=node(),
data=Data}),
+ ct_hooks:on_tc_skip(What, Data),
if Case /= end_per_suite, Case /= end_per_group ->
add_to_stats(auto_skipped);
true ->
@@ -1076,4 +1333,31 @@ add_data_dir(File,Config) when is_list(File) ->
File
end.
+%%%-----------------------------------------------------------------
+%%% @spec overview_html_header(TestName) -> Header
+overview_html_header(TestName) ->
+ TestName1 = lists:flatten(io_lib:format("~p", [TestName])),
+ Label = case application:get_env(common_test, test_label) of
+ {ok,Lbl} when Lbl =/= undefined ->
+ "<H1><FONT color=\"green\">" ++ Lbl ++ "</FONT></H1>\n";
+ _ ->
+ ""
+ end,
+ Bgr = case ct_logs:basic_html() of
+ true ->
+ "";
+ false ->
+ CTPath = code:lib_dir(common_test),
+ TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"),
+ " background=\"" ++ TileFile ++ "\""
+ end,
+
+ ["<html>\n",
+ "<head><title>Test ", TestName1, " results</title>\n",
+ "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ "</head>\n",
+ "<body", Bgr, " bgcolor=\"white\" text=\"black\" ",
+ "link=\"blue\" vlink=\"purple\" alink=\"red\">\n",
+ Label,
+ "<H2>Results from test ", TestName1, "</H2>\n"].
diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl
index a31e57c7ea..5aab4dd2dd 100644
--- a/lib/common_test/src/ct_gen_conn.erl
+++ b/lib/common_test/src/ct_gen_conn.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -76,7 +76,7 @@ start(Name,Address,InitData,CallbackMod) ->
MRef = erlang:monitor(process,Pid),
receive
{connected,Pid} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
ct_util:register_connection(Name,Address,CallbackMod,Pid),
{ok,Pid};
{Error,Pid} ->
@@ -182,7 +182,7 @@ call(Pid,Msg) ->
Pid ! {Msg,{self(),Ref}},
receive
{Ref, Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
case Result of
{retry,_Data} ->
call(Pid,Result);
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl
new file mode 100644
index 0000000000..5eddefffce
--- /dev/null
+++ b/lib/common_test/src/ct_hooks.erl
@@ -0,0 +1,307 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Framework test execution control module.
+%%%
+%%% <p>This module is a proxy for calling and handling common test hooks.</p>
+
+-module(ct_hooks).
+
+%% API Exports
+-export([init/1]).
+-export([init_tc/3]).
+-export([end_tc/5]).
+-export([terminate/1]).
+-export([on_tc_skip/2]).
+-export([on_tc_fail/2]).
+
+-type proplist() :: [{atom(),term()}].
+
+%% If you change this, remember to update ct_util:look -> stop clause as well.
+-define(config_name, ct_hooks).
+
+%% -------------------------------------------------------------------------
+%% API Functions
+%% -------------------------------------------------------------------------
+
+%% @doc Called before any suites are started
+-spec init(State :: term()) -> ok |
+ {error, Reason :: term()}.
+init(Opts) ->
+ call([{Hook, call_id, undefined} || Hook <- get_new_hooks(Opts)],
+ ok, init, []).
+
+
+%% @doc Called after all suites are done.
+-spec terminate(Hooks :: term()) ->
+ ok.
+terminate(Hooks) ->
+ call([{HookId, fun call_terminate/3} || {HookId,_,_} <- Hooks],
+ ct_hooks_terminate_dummy, terminate, Hooks),
+ ok.
+
+%% @doc Called as each test case is started. This includes all configuration
+%% tests.
+-spec init_tc(Mod :: atom(), Func :: atom(), Args :: list()) ->
+ NewConfig :: proplist() |
+ {skip, Reason :: term()} |
+ {auto_skip, Reason :: term()} |
+ {fail, Reason :: term()}.
+init_tc(ct_framework, _Func, Args) ->
+ Args;
+init_tc(Mod, init_per_suite, Config) ->
+ Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of
+ List when is_list(List) ->
+ [{ct_hooks,List}];
+ CTHook when is_atom(CTHook) ->
+ [{ct_hooks,[CTHook]}]
+ catch error:undef ->
+ [{ct_hooks,[]}]
+ end,
+ call(fun call_generic/3, Config ++ Info, [pre_init_per_suite, Mod]);
+init_tc(Mod, end_per_suite, Config) ->
+ call(fun call_generic/3, Config, [pre_end_per_suite, Mod]);
+init_tc(Mod, {init_per_group, GroupName, Opts}, Config) ->
+ maybe_start_locker(Mod, GroupName, Opts),
+ call(fun call_generic/3, Config, [pre_init_per_group, GroupName]);
+init_tc(_Mod, {end_per_group, GroupName, _}, Config) ->
+ call(fun call_generic/3, Config, [pre_end_per_group, GroupName]);
+init_tc(_Mod, TC, Config) ->
+ call(fun call_generic/3, Config, [pre_init_per_testcase, TC]).
+
+%% @doc Called as each test case is completed. This includes all configuration
+%% tests.
+-spec end_tc(Mod :: atom(),
+ Func :: atom(),
+ Args :: list(),
+ Result :: term(),
+ Resturn :: term()) ->
+ NewConfig :: proplist() |
+ {skip, Reason :: term()} |
+ {auto_skip, Reason :: term()} |
+ {fail, Reason :: term()} |
+ ok | '$ct_no_change'.
+end_tc(ct_framework, _Func, _Args, Result, _Return) ->
+ Result;
+
+end_tc(Mod, init_per_suite, Config, _Result, Return) ->
+ call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config],
+ '$ct_no_change');
+
+end_tc(Mod, end_per_suite, Config, Result, _Return) ->
+ call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config],
+ '$ct_no_change');
+
+end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) ->
+ call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config],
+ '$ct_no_change');
+
+end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) ->
+ Res = call(fun call_generic/3, Result,
+ [post_end_per_group, GroupName, Config], '$ct_no_change'),
+ maybe_stop_locker(Mod, GroupName,Opts),
+ Res;
+
+end_tc(_Mod, TC, Config, Result, _Return) ->
+ call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config],
+ '$ct_no_change').
+
+on_tc_skip(How, {_Suite, Case, Reason}) ->
+ call(fun call_cleanup/3, {How, Reason}, [on_tc_skip, Case]).
+
+on_tc_fail(_How, {_Suite, Case, Reason}) ->
+ call(fun call_cleanup/3, Reason, [on_tc_fail, Case]).
+
+%% -------------------------------------------------------------------------
+%% Internal Functions
+%% -------------------------------------------------------------------------
+call_id(Mod, Config, Meta) when is_atom(Mod) ->
+ call_id({Mod, []}, Config, Meta);
+call_id({Mod, Opts}, Config, Scope) ->
+ Id = catch_apply(Mod,id,[Opts], make_ref()),
+ {Config, {Id, scope(Scope), {Mod, {Id,Opts}}}}.
+
+call_init({Mod,{Id,Opts}},Config,_Meta) ->
+ NewState = Mod:init(Id, Opts),
+ {Config, {Mod, NewState}}.
+
+call_terminate({Mod, State}, _, _) ->
+ catch_apply(Mod,terminate,[State], ok),
+ {[],{Mod,State}}.
+
+call_cleanup({Mod, State}, Reason, [Function | Args]) ->
+ NewState = catch_apply(Mod,Function, Args ++ [Reason, State],
+ State),
+ {Reason, {Mod, NewState}}.
+
+call_generic({Mod, State}, Value, [Function | Args]) ->
+ {NewValue, NewState} = catch_apply(Mod, Function, Args ++ [Value, State],
+ {Value,State}),
+ {NewValue, {Mod, NewState}}.
+
+%% Generic call function
+call(Fun, Config, Meta) ->
+ maybe_lock(),
+ Hooks = get_hooks(),
+ Res = call([{HookId,Fun} || {HookId,_, _} <- Hooks] ++
+ get_new_hooks(Config, Fun),
+ remove(?config_name,Config), Meta, Hooks),
+ maybe_unlock(),
+ Res.
+
+call(Fun, Config, Meta, NoChangeRet) when is_function(Fun) ->
+ case call(Fun,Config,Meta) of
+ Config -> NoChangeRet;
+ NewReturn -> NewReturn
+ end;
+
+call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) ->
+ try
+ {Config, {NewId, _, _} = NewHook} = call_id(Hook, Config, Meta),
+ {NewHooks, NewRest} =
+ case lists:keyfind(NewId, 1, Hooks) of
+ false when NextFun =:= undefined ->
+ {Hooks ++ [NewHook],
+ [{NewId, fun call_init/3} | Rest]};
+ ExistingHook when is_tuple(ExistingHook) ->
+ {Hooks, Rest};
+ _ ->
+ {Hooks ++ [NewHook],
+ [{NewId, fun call_init/3},{NewId,NextFun} | Rest]}
+ end,
+ call(NewRest, Config, Meta, NewHooks)
+ catch Error:Reason ->
+ Trace = erlang:get_stacktrace(),
+ ct_logs:log("Suite Hook","Failed to start a CTH: ~p:~p",
+ [Error,{Reason,Trace}]),
+ call([], {fail,"Failed to start CTH"
+ ", see the CT Log for details"}, Meta, Hooks)
+ end;
+call([{HookId, Fun} | Rest], Config, Meta, Hooks) ->
+ try
+ {_,Scope,ModState} = lists:keyfind(HookId, 1, Hooks),
+ {NewConf, NewHookInfo} = Fun(ModState, Config, Meta),
+ NewCalls = get_new_hooks(NewConf, Fun),
+ NewHooks = lists:keyreplace(HookId, 1, Hooks, {HookId, Scope, NewHookInfo}),
+ call(NewCalls ++ Rest, remove(?config_name, NewConf), Meta,
+ terminate_if_scope_ends(HookId, Meta, NewHooks))
+ catch throw:{error_in_cth_call,Reason} ->
+ call(Rest, {fail, Reason}, Meta,
+ terminate_if_scope_ends(HookId, Meta, Hooks))
+ end;
+call([], Config, _Meta, Hooks) ->
+ save_suite_data_async(Hooks),
+ Config.
+
+remove(Key,List) when is_list(List) ->
+ [Conf || Conf <- List, is_tuple(Conf) =:= false
+ orelse element(1, Conf) =/= Key];
+remove(_, Else) ->
+ Else.
+
+%% Translate scopes, i.e. init_per_group,group1 -> end_per_group,group1 etc
+scope([pre_init_per_testcase, TC|_]) ->
+ [post_end_per_testcase, TC];
+scope([pre_init_per_group, GroupName|_]) ->
+ [post_end_per_group, GroupName];
+scope([post_init_per_group, GroupName|_]) ->
+ [post_end_per_group, GroupName];
+scope([pre_init_per_suite, SuiteName|_]) ->
+ [post_end_per_suite, SuiteName];
+scope([post_init_per_suite, SuiteName|_]) ->
+ [post_end_per_suite, SuiteName];
+scope(init) ->
+ none.
+
+terminate_if_scope_ends(HookId, [Function,Tag|T], Hooks) when T =/= [] ->
+ terminate_if_scope_ends(HookId,[Function,Tag],Hooks);
+terminate_if_scope_ends(HookId, Function, Hooks) ->
+ case lists:keyfind(HookId, 1, Hooks) of
+ {HookId, Function, _ModState} = Hook ->
+ terminate([Hook]),
+ lists:keydelete(HookId, 1, Hooks);
+ _ ->
+ Hooks
+ end.
+
+%% Fetch hook functions
+get_new_hooks(Config, Fun) ->
+ lists:foldl(fun(NewHook, Acc) ->
+ [{NewHook, call_id, Fun} | Acc]
+ end, [], get_new_hooks(Config)).
+
+get_new_hooks(Config) when is_list(Config) ->
+ lists:flatmap(fun({?config_name, HookConfigs}) ->
+ HookConfigs;
+ (_) ->
+ []
+ end, Config);
+get_new_hooks(_Config) ->
+ [].
+
+save_suite_data_async(Hooks) ->
+ ct_util:save_suite_data_async(?config_name, Hooks).
+
+get_hooks() ->
+ ct_util:read_suite_data(?config_name).
+
+catch_apply(M,F,A, Default) ->
+ try
+ apply(M,F,A)
+ catch error:Reason ->
+ case erlang:get_stacktrace() of
+ %% Return the default if it was the CTH module which did not have the function.
+ [{M,F,A}|_] when Reason == undef ->
+ Default;
+ Trace ->
+ ct_logs:log("Suite Hook","Call to CTH failed: ~p:~p",
+ [error,{Reason,Trace}]),
+ throw({error_in_cth_call,
+ lists:flatten(
+ io_lib:format("~p:~p/~p CTH call failed",
+ [M,F,length(A)]))})
+ end
+ end.
+
+
+%% We need to lock around the state for parallel groups only. This is because
+%% we will get several processes reading and writing the state for a single
+%% cth at the same time.
+maybe_start_locker(Mod,GroupName,Opts) ->
+ case lists:member(parallel,Opts) of
+ true ->
+ {ok, _Pid} = ct_hooks_lock:start({Mod,GroupName});
+ false ->
+ ok
+ end.
+
+maybe_stop_locker(Mod,GroupName,Opts) ->
+ case lists:member(parallel,Opts) of
+ true ->
+ stopped = ct_hooks_lock:stop({Mod,GroupName});
+ false ->
+ ok
+ end.
+
+
+maybe_lock() ->
+ locked = ct_hooks_lock:request().
+
+maybe_unlock() ->
+ unlocked = ct_hooks_lock:release().
diff --git a/lib/common_test/src/ct_hooks_lock.erl b/lib/common_test/src/ct_hooks_lock.erl
new file mode 100644
index 0000000000..e33fa278dc
--- /dev/null
+++ b/lib/common_test/src/ct_hooks_lock.erl
@@ -0,0 +1,132 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Framework test execution control module.
+%%%
+%%% <p>This module is a proxy for calling and handling locks in
+%%% common test hooks.</p>
+
+-module(ct_hooks_lock).
+
+-behaviour(gen_server).
+
+%% API
+-export([start/1, stop/1, request/0, release/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+-record(state, { id, locked = false, requests = [] }).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%% @doc Starts the server
+start(Id) ->
+ case gen_server:start({local, ?SERVER}, ?MODULE, Id, []) of
+ {error,{already_started, Pid}} ->
+ {ok,Pid};
+ Else ->
+ Else
+ end.
+
+stop(Id) ->
+ try
+ gen_server:call(?SERVER, {stop,Id})
+ catch exit:{noproc,_} ->
+ stopped
+ end.
+
+request() ->
+ try
+ gen_server:call(?SERVER,{request,self()},infinity)
+ catch exit:{noproc,_} ->
+ locked
+ end.
+
+release() ->
+ try
+ gen_server:call(?SERVER,{release,self()})
+ catch exit:{noproc,_} ->
+ unlocked
+ end.
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+
+%% @doc Initiates the server
+init(Id) ->
+ {ok, #state{ id = Id }}.
+
+%% @doc Handling call messages
+handle_call({stop,Id}, _From, #state{ id = Id, requests = Reqs } = State) ->
+ [gen_server:reply(Req, locker_stopped) || {Req,_ReqId} <- Reqs],
+ {stop, normal, stopped, State};
+handle_call({stop,_Id}, _From, State) ->
+ {reply, stopped, State};
+handle_call({request, Pid}, _From, #state{ locked = false,
+ requests = [] } = State) ->
+ Ref = monitor(process, Pid),
+ {reply, locked, State#state{ locked = {true, Pid, Ref}} };
+handle_call({request, Pid}, From, #state{ requests = Reqs } = State) ->
+ {noreply, State#state{ requests = Reqs ++ [{From,Pid}] }};
+handle_call({release, Pid}, _From, #state{ locked = {true, Pid, Ref},
+ requests = []} = State) ->
+ demonitor(Ref,[flush]),
+ {reply, unlocked, State#state{ locked = false }};
+handle_call({release, Pid}, _From,
+ #state{ locked = {true, Pid, Ref},
+ requests = [{NextFrom,NextPid}|Rest]} = State) ->
+ demonitor(Ref,[flush]),
+ gen_server:reply(NextFrom,locked),
+ NextRef = monitor(process, NextPid),
+ {reply,unlocked,State#state{ locked = {true, NextPid, NextRef},
+ requests = Rest } };
+handle_call({release, _Pid}, _From, State) ->
+ {reply, not_locked, State}.
+
+%% @doc Handling cast messages
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%% @doc Handling all non call/cast messages
+handle_info({'DOWN',Ref,process,Pid,_},
+ #state{ locked = {true, Pid, Ref},
+ requests = [{NextFrom,NextPid}|Rest] } = State) ->
+ gen_server:reply(NextFrom, locked),
+ NextRef = monitor(process, NextPid),
+ {noreply,State#state{ locked = {true, NextPid, NextRef},
+ requests = Rest } }.
+
+%% @doc This function is called by a gen_server when it is about to terminate.
+terminate(_Reason, _State) ->
+ ok.
+
+%% @doc Convert process state when code is changed
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%% -------------------------------------------------------------------------
+%% Internal Functions
+%% -------------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl
index bd1a89ae1f..f8ace73cbf 100644
--- a/lib/common_test/src/ct_logs.erl
+++ b/lib/common_test/src/ct_logs.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -36,7 +36,8 @@
-export([make_all_suites_index/1,make_all_runs_index/1]).
%% Logging stuff directly from testcase
--export([tc_log/3,tc_print/3,tc_pal/3]).
+-export([tc_log/3,tc_print/3,tc_pal/3,
+ basic_html/0]).
%% Simulate logger process for use without ct environment running
-export([simulate/0]).
@@ -57,7 +58,7 @@
-define(table_color2,"#E4F0FE").
-define(table_color3,"#F0F8FF").
--define(testname_width, 70).
+-define(testname_width, 60).
-define(abs(Name), filename:absname(Name)).
@@ -80,7 +81,7 @@ init(Mode) ->
MRef = erlang:monitor(process,Pid),
receive
{started,Pid,Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
exit({could_not_start_process,?MODULE,Reason})
@@ -163,7 +164,7 @@ call(Msg) ->
?MODULE ! {Msg,{self(),Ref}},
receive
{Ref, Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{process_down,?MODULE,Reason}}
@@ -383,11 +384,14 @@ maybe_log_timestamp() ->
[{"<i>~s</i>",[log_timestamp({MS,S,US})]}]})
end.
-log_timestamp(Now) ->
- put(log_timestamp,Now),
- {_,{H,M,S}} = calendar:now_to_local_time(Now),
- lists:flatten(io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w",
- [H,M,S])).
+log_timestamp({MS,S,US}) ->
+ put(log_timestamp, {MS,S,US}),
+ {{Year,Month,Day}, {Hour,Min,Sec}} =
+ calendar:now_to_local_time({MS,S,US}),
+ MilliSec = trunc(US/1000),
+ lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B "
+ "~2.10.0B:~2.10.0B:~2.10.0B.~3.10.0B",
+ [Year,Month,Day,Hour,Min,Sec,MilliSec])).
%%%-----------------------------------------------------------------
%%% The logger server
@@ -460,9 +464,11 @@ logger_loop(State) ->
{'EXIT',_Reason} ->
Fd = State#logger_state.ct_log_fd,
io:format(Fd,
- "Logging fails! Str: ~p, Args: ~p~n",
+ "Logging fails! "
+ "Str: ~p, Args: ~p~n",
[Str,Args]),
- %% stop the testcase, we need to see the fault
+ %% stop the testcase, we need
+ %% to see the fault
exit(Pid,logging_failed),
ok;
IoStr when IoList == [] ->
@@ -505,7 +511,7 @@ logger_loop(State) ->
logger_loop(State);
{set_stylesheet,TC,SSFile} ->
Fd = State#logger_state.ct_log_fd,
- io:format(Fd, "~p uses external style sheet: ~s~n", [TC,SSFile]),
+ io:format(Fd, "~p loading external style sheet: ~s~n", [TC,SSFile]),
logger_loop(State#logger_state{stylesheet=SSFile});
{clear_stylesheet,_} when State#logger_state.stylesheet == undefined ->
logger_loop(State);
@@ -716,7 +722,7 @@ make_last_run_index1(StartTime,IndexName) ->
[Log];
Logs ->
case read_totals_file(?totals_name) of
- {_Node,Logs0,_Totals} ->
+ {_Node,_Lbl,Logs0,_Totals} ->
insert_dirs(Logs,Logs0);
_ ->
%% someone deleted the totals file!?
@@ -728,10 +734,15 @@ make_last_run_index1(StartTime,IndexName) ->
{ok,Bin} -> binary_to_term(Bin);
_ -> []
end,
- {ok,Index0,Totals} = make_last_run_index(Logs1, index_header(StartTime),
+ Label = case application:get_env(common_test, test_label) of
+ {ok,Lbl} -> Lbl;
+ _ -> undefined
+ end,
+ {ok,Index0,Totals} = make_last_run_index(Logs1,
+ index_header(Label,StartTime),
0, 0, 0, 0, 0, Missing),
%% write current Totals to file, later to be used in all_runs log
- write_totals_file(?totals_name,Logs1,Totals),
+ write_totals_file(?totals_name,Label,Logs1,Totals),
Index = [Index0|index_footer()],
case force_write_file(IndexName, Index) of
ok ->
@@ -761,7 +772,7 @@ make_last_run_index([Name|Rest], Result, TotSucc, TotFail, UserSkip, AutoSkip,
TotNotBuilt, Missing);
LastLogDir ->
SuiteName = filename:rootname(filename:basename(Name)),
- case make_one_index_entry(SuiteName, LastLogDir, false, Missing) of
+ case make_one_index_entry(SuiteName, LastLogDir, "-", false, Missing) of
{Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
%% for backwards compatibility
AutoSkip1 = case catch AutoSkip+ASkip of
@@ -780,18 +791,18 @@ make_last_run_index([], Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuil
{ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt, false)],
{TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}.
-make_one_index_entry(SuiteName, LogDir, All, Missing) ->
+make_one_index_entry(SuiteName, LogDir, Label, All, Missing) ->
case count_cases(LogDir) of
{Succ,Fail,UserSkip,AutoSkip} ->
NotBuilt = not_built(SuiteName, LogDir, All, Missing),
- NewResult = make_one_index_entry1(SuiteName, LogDir, Succ, Fail,
+ NewResult = make_one_index_entry1(SuiteName, LogDir, Label, Succ, Fail,
UserSkip, AutoSkip, NotBuilt, All),
{NewResult,Succ,Fail,UserSkip,AutoSkip,NotBuilt};
error ->
error
end.
-make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip,
+make_one_index_entry1(SuiteName, Link, Label, Success, Fail, UserSkip, AutoSkip,
NotBuilt, All) ->
LogFile = filename:join(Link, ?suitelog_name ++ ".html"),
CrashDumpName = SuiteName ++ "_erl_crash.dump",
@@ -803,7 +814,7 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip,
false ->
""
end,
- {Timestamp,Node,AllInfo} =
+ {Lbl,Timestamp,Node,AllInfo} =
case All of
{true,OldRuns} ->
[_Prefix,NodeOrDate|_] = string:tokens(Link,"."),
@@ -811,20 +822,21 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip,
0 -> "-";
_ -> NodeOrDate
end,
- N = ["<TD ALIGN=right>",Node1,"</TD>\n"],
+ N = ["<TD ALIGN=right><FONT SIZE=-1>",Node1,"</FONT></TD>\n"],
CtRunDir = filename:dirname(filename:dirname(Link)),
- T = ["<TD>",timestamp(CtRunDir),"</TD>\n"],
+ L = ["<TD ALIGN=center><FONT SIZE=-1><B>",Label,"</FONT></B></TD>\n"],
+ T = ["<TD><FONT SIZE=-1>",timestamp(CtRunDir),"</FONT></TD>\n"],
CtLogFile = filename:join(CtRunDir,?ct_log_name),
OldRunsLink =
case OldRuns of
[] -> "none";
_ -> "<A HREF=\""++?all_runs_name++"\">Old Runs</A>"
end,
- A=["<TD><A HREF=\"",CtLogFile,"\">CT Log</A></TD>\n",
- "<TD>",OldRunsLink,"</TD>\n"],
- {T,N,A};
+ A=["<TD><FONT SIZE=-1><A HREF=\"",CtLogFile,"\">CT Log</A></FONT></TD>\n",
+ "<TD><FONT SIZE=-1>",OldRunsLink,"</FONT></TD>\n"],
+ {L,T,N,A};
false ->
- {"","",""}
+ {"","","",""}
end,
NotBuiltStr =
if NotBuilt == 0 ->
@@ -851,7 +863,8 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip,
{UserSkip+AutoSkip,integer_to_list(UserSkip),ASStr}
end,
["<TR valign=top>\n",
- "<TD><A HREF=\"",LogFile,"\">",SuiteName,"</A>",CrashDumpLink,"</TD>\n",
+ "<TD><FONT SIZE=-1><A HREF=\"",LogFile,"\">",SuiteName,"</A>",CrashDumpLink,"</FONT></TD>\n",
+ Lbl,
Timestamp,
"<TD ALIGN=right>",integer_to_list(Success),"</TD>\n",
"<TD ALIGN=right>",FailStr,"</TD>\n",
@@ -862,12 +875,14 @@ make_one_index_entry1(SuiteName, Link, Success, Fail, UserSkip, AutoSkip,
AllInfo,
"</TR>\n"].
total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) ->
- {TimestampCell,AllInfo} =
+ {Label,TimestampCell,AllInfo} =
case All of
- true ->
- {"<TD>&nbsp;</TD>\n","<TD>&nbsp;</TD>\n<TD>&nbsp;</TD>\n"};
+ true ->
+ {"<TD>&nbsp;</TD>\n",
+ "<TD>&nbsp;</TD>\n",
+ "<TD>&nbsp;</TD>\n<TD>&nbsp;</TD>\n"};
false ->
- {"",""}
+ {"","",""}
end,
{AllSkip,UserSkipStr,AutoSkipStr} =
@@ -877,6 +892,7 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) ->
end,
["<TR valign=top>\n",
"<TD><B>Total</B></TD>",
+ Label,
TimestampCell,
"<TD ALIGN=right><B>",integer_to_list(Success),"<B></TD>\n",
"<TD ALIGN=right><B>",integer_to_list(Fail),"<B></TD>\n",
@@ -937,13 +953,21 @@ term_to_text(Term) ->
%%% Headers and footers.
-index_header(StartTime) ->
- [header("Test Results " ++ format_time(StartTime)) |
+index_header(Label, StartTime) ->
+ Head =
+ case Label of
+ undefined ->
+ header("Test Results", format_time(StartTime));
+ _ ->
+ header("Test Results for \"" ++ Label ++ "\"",
+ format_time(StartTime))
+ end,
+ [Head |
["<CENTER>\n",
"<P><A HREF=\"",?ct_log_name,"\">Common Test Framework Log</A></P>",
"<TABLE border=\"3\" cellpadding=\"5\" "
"BGCOLOR=\"",?table_color3,"\">\n"
- "<th><B>Name</B></th>\n",
+ "<th><B>Test Name</B></th>\n",
"<th><font color=\"",?table_color3,"\">_</font>Ok"
"<font color=\"",?table_color3,"\">_</font></th>\n"
"<th>Failed</th>\n",
@@ -952,13 +976,17 @@ index_header(StartTime) ->
"\n"]].
all_suites_index_header() ->
+ {ok,Cwd} = file:get_cwd(),
+ LogDir = filename:basename(Cwd),
+ AllRuns = "All test runs in \"" ++ LogDir ++ "\"",
[header("Test Results") |
["<CENTER>\n",
- "<A HREF=\"",?all_runs_name,"\">All Test Runs in this directory</A>\n",
+ "<A HREF=\"",?all_runs_name,"\">",AllRuns,"</A>\n",
"<br><br>\n",
"<TABLE border=\"3\" cellpadding=\"5\" "
"BGCOLOR=\"",?table_color2,"\">\n"
- "<th>Name</th>\n",
+ "<th>Test Name</th>\n",
+ "<th>Label</th>\n",
"<th>Test Run Started</th>\n",
"<th><font color=\"",?table_color2,"\">_</font>Ok"
"<font color=\"",?table_color2,"\">_</font></th>\n"
@@ -971,13 +999,17 @@ all_suites_index_header() ->
"\n"]].
all_runs_header() ->
- [header("All test runs in current directory") |
+ {ok,Cwd} = file:get_cwd(),
+ LogDir = filename:basename(Cwd),
+ Title = "All test runs in \"" ++ LogDir ++ "\"",
+ [header(Title) |
["<CENTER><TABLE border=\"3\" cellpadding=\"5\" "
"BGCOLOR=\"",?table_color1,"\">\n"
"<th><B>History</B></th>\n"
"<th><B>Node</B></th>\n"
+ "<th><B>Label</B></th>\n"
"<th>Tests</th>\n"
- "<th><B>Names</B></th>\n"
+ "<th><B>Test Names</B></th>\n"
"<th>Total</th>\n"
"<th><font color=\"",?table_color1,"\">_</font>Ok"
"<font color=\"",?table_color1,"\">_</font></th>\n"
@@ -987,12 +1019,23 @@ all_runs_header() ->
"\n"]].
header(Title) ->
+ header1(Title, "").
+header(Title, SubTitle) ->
+ header1(Title, SubTitle).
+
+header1(Title, SubTitle) ->
+ SubTitleHTML = if SubTitle =/= "" ->
+ ["<CENTER>\n",
+ "<H2>" ++ SubTitle ++ "</H2>\n",
+ "</CENTER>\n<BR>\n"];
+ true -> "<BR>\n"
+ end,
["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
"<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
"<HTML>\n",
"<HEAD>\n",
- "<TITLE>" ++ Title ++ "</TITLE>\n",
+ "<TITLE>" ++ Title ++ " " ++ SubTitle ++ "</TITLE>\n",
"<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\">\n",
"</HEAD>\n",
@@ -1004,6 +1047,7 @@ header(Title) ->
"<CENTER>\n",
"<H1>" ++ Title ++ "</H1>\n",
"</CENTER>\n",
+ SubTitleHTML,
"<!-- ---- CONTENT ---- -->\n"].
@@ -1013,19 +1057,28 @@ index_footer() ->
footer() ->
["<P><CENTER>\n"
- "<HR>\n"
- "<P><FONT SIZE=-1>\n"
- "Copyright &copy; ", year(),
- " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n"
- "Updated: <!date>", current_time(), "<!/date><BR>\n"
- "</FONT>\n"
- "</CENTER>\n"
- "</body>\n"].
+ "<BR><BR>\n"
+ "<HR>\n"
+ "<P><FONT SIZE=-1>\n"
+ "Copyright &copy; ", year(),
+ " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n"
+ "Updated: <!date>", current_time(), "<!/date><BR>\n"
+ "</FONT>\n"
+ "</CENTER>\n"
+ "</body>\n"].
body_tag() ->
- "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\""
- "vlink=\"#800080\" alink=\"#FF0000\">\n".
+ case basic_html() of
+ true ->
+ "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" "
+ "vlink=\"#800080\" alink=\"#FF0000\">\n";
+ false ->
+ CTPath = code:lib_dir(common_test),
+ TileFile = filename:join(filename:join(CTPath,"priv"),"tile1.jpg"),
+ "<body background=\"" ++ TileFile ++ "\" bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" "
+ "vlink=\"#800080\" alink=\"#FF0000\">\n"
+ end.
current_time() ->
format_time(calendar:local_time()).
@@ -1217,7 +1270,7 @@ runentry(Dir, BasicHtml) ->
TotalsFile = filename:join(Dir,?totals_name),
TotalsStr =
case read_totals_file(TotalsFile) of
- {Node,Logs,{TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}} ->
+ {Node,Label,Logs,{TotSucc,TotFail,UserSkip,AutoSkip,NotBuilt}} ->
TotFailStr =
if TotFail > 0 ->
["<FONT color=\"red\">",
@@ -1263,6 +1316,7 @@ runentry(Dir, BasicHtml) ->
end,
Total = TotSucc+TotFail+AllSkip,
A = ["<TD ALIGN=center><FONT SIZE=-1>",Node,"</FONT></TD>\n",
+ "<TD ALIGN=center><FONT SIZE=-1><B>",Label,"</B></FONT></TD>\n",
"<TD ALIGN=right>",NoOfTests,"</TD>\n"],
B = if BasicHtml ->
["<TD ALIGN=center><FONT SIZE=-1>",TestNamesTrunc,"</FONT></TD>\n"];
@@ -1283,17 +1337,19 @@ runentry(Dir, BasicHtml) ->
end,
Index = filename:join(Dir,?index_name),
["<TR>\n"
- "<TD><A HREF=\"",Index,"\">",timestamp(Dir),"</A>",TotalsStr,"</TD>\n"
+ "<TD><FONT SIZE=-1><A HREF=\"",Index,"\">",timestamp(Dir),"</A>",TotalsStr,"</FONT></TD>\n"
"</TR>\n"].
-write_totals_file(Name,Logs,Totals) ->
+write_totals_file(Name,Label,Logs,Totals) ->
AbsName = ?abs(Name),
notify_and_lock_file(AbsName),
force_write_file(AbsName,
term_to_binary({atom_to_list(node()),
- Logs,Totals})),
+ Label,Logs,Totals})),
notify_and_unlock_file(AbsName).
+%% this function needs to convert from old formats to new so that old
+%% test results (prev ct versions) can be listed together with new
read_totals_file(Name) ->
AbsName = ?abs(Name),
notify_and_lock_file(AbsName),
@@ -1303,12 +1359,23 @@ read_totals_file(Name) ->
case catch binary_to_term(Bin) of
{'EXIT',_Reason} -> % corrupt file
{"-",[],undefined};
- R = {Node,Ls,Tot} ->
+ {Node,Label,Ls,Tot} -> % all info available
+ Label1 = case Label of
+ undefined -> "-";
+ _ -> Label
+ end,
case Tot of
- {_,_,_,_,_} -> % latest format
- R;
+ {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format
+ {Node,Label1,Ls,Tot};
{TotSucc,TotFail,AllSkip,NotBuilt} ->
- {Node,Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
+ {Node,Label1,Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
+ end;
+ {Node,Ls,Tot} -> % no label found
+ case Tot of
+ {_Ok,_Fail,_USkip,_ASkip,_NoBuild} -> % latest format
+ {Node,"-",Ls,Tot};
+ {TotSucc,TotFail,AllSkip,NotBuilt} ->
+ {Node,"-",Ls,{TotSucc,TotFail,AllSkip,undefined,NotBuilt}}
end;
%% for backwards compatibility
{Ls,Tot} -> {"-",Ls,Tot};
@@ -1411,7 +1478,7 @@ make_all_suites_index1(When,AllSuitesLogDirs) ->
make_all_suites_index2(IndexName,AllSuitesLogDirs) ->
{ok,Index0,_Totals} = make_all_suites_index3(AllSuitesLogDirs,
all_suites_index_header(),
- 0, 0, 0, 0, 0),
+ 0, 0, 0, 0, 0, []),
Index = [Index0|index_footer()],
case force_write_file(IndexName, Index) of
ok ->
@@ -1421,14 +1488,25 @@ make_all_suites_index2(IndexName,AllSuitesLogDirs) ->
end.
make_all_suites_index3([{SuiteName,[LastLogDir|OldDirs]}|Rest],
- Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt) ->
+ Result, TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,
+ Labels) ->
[EntryDir|_] = filename:split(LastLogDir),
Missing =
case file:read_file(filename:join(EntryDir,?missing_suites_info)) of
{ok,Bin} -> binary_to_term(Bin);
_ -> []
end,
- case make_one_index_entry(SuiteName, LastLogDir, {true,OldDirs}, Missing) of
+ {Label,Labels1} =
+ case proplists:get_value(EntryDir, Labels) of
+ undefined ->
+ case read_totals_file(filename:join(EntryDir,?totals_name)) of
+ {_,Lbl,_,_} -> {Lbl,[{EntryDir,Lbl}|Labels]};
+ _ -> {"-",[{EntryDir,"-"}|Labels]}
+ end;
+ Lbl ->
+ {Lbl,Labels}
+ end,
+ case make_one_index_entry(SuiteName, LastLogDir, Label, {true,OldDirs}, Missing) of
{Result1,Succ,Fail,USkip,ASkip,NotBuilt} ->
%% for backwards compatibility
AutoSkip1 = case catch AutoSkip+ASkip of
@@ -1437,13 +1515,13 @@ make_all_suites_index3([{SuiteName,[LastLogDir|OldDirs]}|Rest],
end,
make_all_suites_index3(Rest, [Result|Result1], TotSucc+Succ,
TotFail+Fail, UserSkip+USkip, AutoSkip1,
- TotNotBuilt+NotBuilt);
+ TotNotBuilt+NotBuilt,Labels1);
error ->
make_all_suites_index3(Rest, Result, TotSucc, TotFail,
- UserSkip, AutoSkip, TotNotBuilt)
+ UserSkip, AutoSkip, TotNotBuilt,Labels1)
end;
make_all_suites_index3([], Result, TotSucc, TotFail, UserSkip, AutoSkip,
- TotNotBuilt) ->
+ TotNotBuilt,_) ->
{ok, [Result|total_row(TotSucc, TotFail, UserSkip, AutoSkip, TotNotBuilt,true)],
{TotSucc,TotFail,UserSkip,AutoSkip,TotNotBuilt}}.
diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl
index 7eb2c3cfef..2ea2ba106a 100644
--- a/lib/common_test/src/ct_master.erl
+++ b/lib/common_test/src/ct_master.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -28,7 +28,7 @@
-export([abort/0,abort/1,progress/0]).
--export([init_master/6, init_node_ctrl/3]).
+-export([init_master/7, init_node_ctrl/3]).
-export([status/2]).
@@ -49,7 +49,8 @@
%%% OptTuples = {config,CfgFiles} | {dir,TestDirs} | {suite,Suites} |
%%% {testcase,Cases} | {spec,TestSpecs} | {allow_user_terms,Bool} |
%%% {logdir,LogDir} | {event_handler,EventHandlers} |
-%%% {silent_connections,Conns} | {cover,CoverSpecFile}
+%%% {silent_connections,Conns} | {cover,CoverSpecFile} |
+%%% {userconfig, UserCfgFiles}
%%% CfgFiles = string() | [string()]
%%% TestDirs = string() | [string()]
%%% Suites = atom() | [atom()]
@@ -98,11 +99,16 @@ run([TS|TestSpecs],AllowUserTerms,InclNodes,ExclNodes) when is_list(TS),
{error,Reason} ->
{error,Reason};
TSRec=#testspec{logdir=AllLogDirs,
- config=AllCfgFiles,
+ config=StdCfgFiles,
+ userconfig=UserCfgFiles,
+ include=AllIncludes,
+ init=AllInitOpts,
event_handler=AllEvHs} ->
+ AllCfgFiles = {StdCfgFiles, UserCfgFiles},
RunSkipPerNode = ct_testspec:prepare_tests(TSRec),
RunSkipPerNode2 = exclude_nodes(ExclNodes,RunSkipPerNode),
- run_all(RunSkipPerNode2,AllLogDirs,AllCfgFiles,AllEvHs,[],[],TS1)
+ run_all(RunSkipPerNode2,AllLogDirs,AllCfgFiles,AllEvHs,
+ AllIncludes,[],[],AllInitOpts,TS1)
end,
[{TS,Result} | run(TestSpecs,AllowUserTerms,InclNodes,ExclNodes)];
run([],_,_,_) ->
@@ -157,10 +163,15 @@ run_on_node([TS|TestSpecs],AllowUserTerms,Node) when is_list(TS),is_atom(Node) -
{error,Reason} ->
{error,Reason};
TSRec=#testspec{logdir=AllLogDirs,
- config=AllCfgFiles,
+ config=StdCfgFiles,
+ init=AllInitOpts,
+ include=AllIncludes,
+ userconfig=UserCfgFiles,
event_handler=AllEvHs} ->
+ AllCfgFiles = {StdCfgFiles, UserCfgFiles},
{Run,Skip} = ct_testspec:prepare_tests(TSRec,Node),
- run_all([{Node,Run,Skip}],AllLogDirs,AllCfgFiles,AllEvHs,[],[],TS1)
+ run_all([{Node,Run,Skip}],AllLogDirs,AllCfgFiles,AllEvHs,
+ AllIncludes, [],[],AllInitOpts,TS1)
end,
[{TS,Result} | run_on_node(TestSpecs,AllowUserTerms,Node)];
run_on_node([],_,_) ->
@@ -180,7 +191,9 @@ run_on_node(TestSpecs,Node) ->
-run_all([{Node,Run,Skip}|Rest],AllLogDirs,AllCfgFiles,AllEvHs,NodeOpts,LogDirs,Specs) ->
+run_all([{Node,Run,Skip}|Rest],AllLogDirs,
+ {AllStdCfgFiles, AllUserCfgFiles}=AllCfgFiles,
+ AllEvHs,AllIncludes,NodeOpts,LogDirs,InitOptions,Specs) ->
LogDir =
lists:foldl(fun({N,Dir},_Found) when N == Node ->
Dir;
@@ -191,29 +204,47 @@ run_all([{Node,Run,Skip}|Rest],AllLogDirs,AllCfgFiles,AllEvHs,NodeOpts,LogDirs,S
(_Dir,Found) ->
Found
end,".",AllLogDirs),
- CfgFiles =
+
+ StdCfgFiles =
lists:foldr(fun({N,F},Fs) when N == Node -> [F|Fs];
({_N,_F},Fs) -> Fs;
(F,Fs) -> [F|Fs]
- end,[],AllCfgFiles),
+ end,[],AllStdCfgFiles),
+ UserCfgFiles =
+ lists:foldr(fun({N,F},Fs) when N == Node -> [{userconfig, F}|Fs];
+ ({_N,_F},Fs) -> Fs;
+ (F,Fs) -> [{userconfig, F}|Fs]
+ end,[],AllUserCfgFiles),
+
+ Includes = lists:foldr(fun({N,I},Acc) when N =:= Node ->
+ [I|Acc];
+ ({_,_},Acc) ->
+ Acc;
+ (I,Acc) ->
+ [I | Acc]
+ end, [], AllIncludes),
EvHs =
lists:foldr(fun({N,H,A},Hs) when N == Node -> [{H,A}|Hs];
({_N,_H,_A},Hs) -> Hs;
({H,A},Hs) -> [{H,A}|Hs]
end,[],AllEvHs),
+
NO = {Node,[{prepared_tests,{Run,Skip},Specs},
{logdir,LogDir},
- {config,CfgFiles},
- {event_handler,EvHs}]},
- run_all(Rest,AllLogDirs,AllCfgFiles,AllEvHs,[NO|NodeOpts],[LogDir|LogDirs],Specs);
-run_all([],AllLogDirs,_,AllEvHs,NodeOpts,LogDirs,Specs) ->
+ {include, Includes},
+ {config,StdCfgFiles},
+ {event_handler,EvHs}] ++ UserCfgFiles},
+ run_all(Rest,AllLogDirs,AllCfgFiles,AllEvHs,AllIncludes,
+ [NO|NodeOpts],[LogDir|LogDirs],InitOptions,Specs);
+run_all([],AllLogDirs,_,AllEvHs,_AllIncludes,
+ NodeOpts,LogDirs,InitOptions,Specs) ->
Handlers = [{H,A} || {Master,H,A} <- AllEvHs, Master == master],
MasterLogDir = case lists:keysearch(master,1,AllLogDirs) of
{value,{_,Dir}} -> Dir;
false -> "."
end,
log(tty,"Master Logdir","~s",[MasterLogDir]),
- start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir,LogDirs,Specs),
+ start_master(lists:reverse(NodeOpts),Handlers,MasterLogDir,LogDirs,InitOptions,Specs),
ok.
@@ -251,17 +282,17 @@ progress() ->
%%% MASTER, runs on central controlling node.
%%%-----------------------------------------------------------------
start_master(NodeOptsList) ->
- start_master(NodeOptsList,[],".",[],[]).
+ start_master(NodeOptsList,[],".",[],[],[]).
-start_master(NodeOptsList,EvHandlers,MasterLogDir,LogDirs,Specs) ->
+start_master(NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Specs) ->
Master = spawn_link(?MODULE,init_master,[self(),NodeOptsList,EvHandlers,
- MasterLogDir,LogDirs,Specs]),
+ MasterLogDir,LogDirs,InitOptions,Specs]),
receive
{Master,Result} -> Result
end.
%%% @hidden
-init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,Specs) ->
+init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,InitOptions,Specs) ->
case whereis(ct_master) of
undefined ->
register(ct_master,self()),
@@ -314,10 +345,10 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs,Specs) ->
Pid when is_pid(Pid) ->
ok
end,
- init_master1(Parent,NodeOptsList,LogDirs).
+ init_master1(Parent,NodeOptsList,InitOptions,LogDirs).
-init_master1(Parent,NodeOptsList,LogDirs) ->
- {Inaccessible,NodeOptsList1} = ping_nodes(NodeOptsList,[],[]),
+init_master1(Parent,NodeOptsList,InitOptions,LogDirs) ->
+ {Inaccessible,NodeOptsList1,InitOptions1} = init_nodes(NodeOptsList,InitOptions),
case Inaccessible of
[] ->
init_master2(Parent,NodeOptsList,LogDirs);
@@ -331,7 +362,7 @@ init_master1(Parent,NodeOptsList,LogDirs) ->
"Proceeding without: ~p",[Inaccessible]),
init_master2(Parent,NodeOptsList1,LogDirs);
"r\n" ->
- init_master1(Parent,NodeOptsList,LogDirs);
+ init_master1(Parent,NodeOptsList,InitOptions1,LogDirs);
_ ->
log(html,"Aborting Tests","",[]),
ct_master_event:stop(),
@@ -542,6 +573,9 @@ get_pid(Node,NodeCtrlPids) ->
undefined
end.
+ping_nodes(NodeOptions)->
+ ping_nodes(NodeOptions, [], []).
+
ping_nodes([NO={Node,_Opts}|NOs],Inaccessible,NodeOpts) ->
case net_adm:ping(Node) of
pong ->
@@ -678,13 +712,80 @@ call(Pid,Msg) ->
{'DOWN', Ref, _, _, _} ->
{error,master_died}
end,
- erlang:demonitor(Ref),
+ erlang:demonitor(Ref, [flush]),
Return.
reply(Result,To) ->
To ! {self(),Result},
ok.
+init_nodes(NodeOptions, InitOptions)->
+ ping_nodes(NodeOptions),
+ start_nodes(InitOptions),
+ eval_on_nodes(InitOptions),
+ {Inaccessible, NodeOptions1}=ping_nodes(NodeOptions),
+ InitOptions1 = filter_accessible(InitOptions, Inaccessible),
+ {Inaccessible, NodeOptions1, InitOptions1}.
+
+% only nodes which are inaccessible now, should be initiated later
+filter_accessible(InitOptions, Inaccessible)->
+ [{Node,Option}||{Node,Option}<-InitOptions, lists:member(Node, Inaccessible)].
+
+start_nodes(InitOptions)->
+ lists:foreach(fun({NodeName, Options})->
+ [NodeS,HostS]=string:tokens(atom_to_list(NodeName), "@"),
+ Node=list_to_atom(NodeS),
+ Host=list_to_atom(HostS),
+ HasNodeStart = lists:keymember(node_start, 1, Options),
+ IsAlive = lists:member(NodeName, nodes()),
+ case {HasNodeStart, IsAlive} of
+ {false, false}->
+ io:format("WARNING: Node ~p is not alive but has no node_start option~n", [NodeName]);
+ {false, true}->
+ io:format("Node ~p is alive~n", [NodeName]);
+ {true, false}->
+ {node_start, NodeStart} = lists:keyfind(node_start, 1, Options),
+ {value, {callback_module, Callback}, NodeStart2}=
+ lists:keytake(callback_module, 1, NodeStart),
+ case Callback:start(Host, Node, NodeStart2) of
+ {ok, NodeName} ->
+ io:format("Node ~p started successfully with callback ~p~n", [NodeName,Callback]);
+ {error, Reason, _NodeName} ->
+ io:format("Failed to start node ~p with callback ~p! Reason: ~p~n", [NodeName, Callback, Reason])
+ end;
+ {true, true}->
+ io:format("WARNING: Node ~p is alive but has node_start option~n", [NodeName])
+ end
+ end,
+ InitOptions).
+
+eval_on_nodes(InitOptions)->
+ lists:foreach(fun({NodeName, Options})->
+ HasEval = lists:keymember(eval, 1, Options),
+ IsAlive = lists:member(NodeName, nodes()),
+ case {HasEval, IsAlive} of
+ {false,_}->
+ ok;
+ {true,false}->
+ io:format("WARNING: Node ~p is not alive but has eval option ~n", [NodeName]);
+ {true,true}->
+ {eval, MFAs} = lists:keyfind(eval, 1, Options),
+ evaluate(NodeName, MFAs)
+ end
+ end,
+ InitOptions).
+
+evaluate(Node, [{M,F,A}|MFAs])->
+ case rpc:call(Node, M, F, A) of
+ {badrpc,Reason}->
+ io:format("WARNING: Failed to call ~p:~p/~p on node ~p due to ~p~n", [M,F,length(A),Node,Reason]);
+ Result->
+ io:format("Called ~p:~p/~p on node ~p, result: ~p~n", [M,F,length(A),Node,Result])
+ end,
+ evaluate(Node, MFAs);
+evaluate(_Node, [])->
+ ok.
+
%cast(Msg) ->
% cast(whereis(ct_master),Msg).
diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl
index 63f60b1182..244faace06 100644
--- a/lib/common_test/src/ct_master_logs.erl
+++ b/lib/common_test/src/ct_master_logs.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -44,7 +44,7 @@ start(LogDir,Nodes) ->
MRef = erlang:monitor(process,Pid),
receive
{started,Pid,Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
{Pid,Result};
{'DOWN',MRef,process,_,Reason} ->
exit({could_not_start_process,?MODULE,Reason})
@@ -435,7 +435,7 @@ call(Msg) ->
?MODULE ! {Msg,{self(),Ref}},
receive
{Ref, Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{process_down,?MODULE,Reason}}
diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl
index 7ac6e045d7..be3c485b75 100644
--- a/lib/common_test/src/ct_repeat.erl
+++ b/lib/common_test/src/ct_repeat.erl
@@ -1,26 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%% @doc Common Test Framework module that handles repeated test runs
%%%
%%% <p>This module exports functions for repeating tests. The following
-%%% script flags (or equivalent ct:run_test/1 options) are supported:
+%%% start flags (or equivalent ct:run_test/1 options) are supported:
%%% -until <StopTime>, StopTime = YYMoMoDDHHMMSS | HHMMSS
%%% -duration <DurTime>, DurTime = HHMMSS
%%% -force_stop
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 6b1063f74c..7bd7dc7d66 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,6 @@
-module(ct_run).
-
%% Script interface
-export([script_start/0,script_usage/0]).
@@ -46,26 +45,84 @@
-define(abs(Name), filename:absname(Name)).
-define(testdir(Name, Suite), ct_util:get_testdir(Name, Suite)).
+-record(opts, {label,
+ vts,
+ shell,
+ cover,
+ coverspec,
+ step,
+ logdir,
+ config = [],
+ event_handlers = [],
+ ct_hooks = [],
+ include = [],
+ silent_connections,
+ stylesheet,
+ multiply_timetraps = 1,
+ scale_timetraps = false,
+ testspecs = [],
+ tests}).
+
%%%-----------------------------------------------------------------
%%% @spec script_start() -> void()
%%%
-%%% @doc Start tests via the run_test script.
-%%%
-%%% <p>Example:<br/><code>./run_test -config config.ctc -dir
+%%% @doc Start tests via the ct_run program or script.
+%%%
+%%% <p>Example:<br/><code>./ct_run -config config.ctc -dir
%%% $TEST_DIR</code></p>
%%%
-%%% <p>Example:<br/><code>./run_test -config config.ctc -suite
+%%% <p>Example:<br/><code>./ct_run -config config.ctc -suite
%%% $SUITE_PATH/$SUITE_NAME [-case $CASE_NAME]</code></p>
%%%
script_start() ->
process_flag(trap_exit, true),
- Args = merge_arguments(init:get_arguments()),
+ Init = init:get_arguments(),
+ CtArgs = lists:takewhile(fun({ct_erl_args,_}) -> false;
+ (_) -> true end, Init),
+
+ %% convert relative dirs added with pa or pz (pre erl_args on
+ %% the ct_run command line) to absolute so that app modules
+ %% can be found even after CT changes CWD to logdir
+ rel_to_abs(CtArgs),
+
+ Args =
+ case application:get_env(common_test, run_test_start_opts) of
+ {ok,EnvStartOpts} ->
+ FlagFilter = fun(Flags) ->
+ lists:filter(fun({root,_}) -> false;
+ ({progname,_}) -> false;
+ ({home,_}) -> false;
+ ({noshell,_}) -> false;
+ ({noinput,_}) -> false;
+ (_) -> true
+ end, Flags)
+ end,
+ %% used for purpose of testing the run_test interface
+ io:format(user, "~n-------------------- START ARGS --------------------~n", []),
+ io:format(user, "--- Init args:~n~p~n", [FlagFilter(Init)]),
+ io:format(user, "--- CT args:~n~p~n", [FlagFilter(CtArgs)]),
+ EnvArgs = opts2args(EnvStartOpts),
+ io:format(user, "--- Env opts -> args:~n~p~n =>~n~p~n",
+ [EnvStartOpts,EnvArgs]),
+ Merged = merge_arguments(CtArgs ++ EnvArgs),
+ io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]),
+ io:format(user, "----------------------------------------------------~n~n", []),
+ Merged;
+ _ ->
+ merge_arguments(CtArgs)
+ end,
+ case proplists:get_value(help, Args) of
+ undefined -> script_start(Args);
+ _ -> script_usage()
+ end.
+
+script_start(Args) ->
Tracing = start_trace(Args),
- Res =
+ Res =
case ct_repeat:loop_test(script, Args) of
- false ->
+ false ->
{ok,Cwd} = file:get_cwd(),
- CTVsn =
+ CTVsn =
case filename:basename(code:lib_dir(common_test)) of
CTBase when is_list(CTBase) ->
case string:tokens(CTBase, "-") of
@@ -76,7 +133,7 @@ script_start() ->
io:format("~nCommon Test~s starting (cwd is ~s)~n~n", [CTVsn,Cwd]),
Self = self(),
Pid = spawn_link(fun() -> script_start1(Self, Args) end),
- receive
+ receive
{'EXIT',Pid,Reason} ->
case Reason of
{user_error,What} ->
@@ -98,326 +155,384 @@ script_start() ->
Result
end,
stop_trace(Tracing),
+ timer:sleep(1000),
Res.
script_start1(Parent, Args) ->
- case lists:keymember(preload, 1, Args) of
- true -> preload();
- false -> ok
- end,
-
- VtsOrShell =
- case lists:keymember(vts, 1, Args) of
- true ->
- vts;
- false ->
- case lists:keymember(shell, 1, Args) of
- true -> shell;
- false -> false
- end
- end,
- LogDir =
- case lists:keysearch(logdir, 1, Args) of
- {value,{logdir,[LogD]}} -> LogD;
- false -> "."
- end,
- EvHandlers =
- case lists:keysearch(event_handler, 1, Args) of
- {value,{event_handler,Handlers}} ->
- lists:map(fun(H) -> {list_to_atom(H),[]} end, Handlers);
- false ->
- []
- end,
- Cover =
- case lists:keysearch(cover, 1, Args) of
- {value,{cover,CoverFile}} ->
- {cover,?abs(CoverFile)};
- false ->
- false
- end,
-
- case lists:keysearch(ct_decrypt_key, 1, Args) of
- {value,{_,[DecryptKey]}} ->
+ %% read general start flags
+ Label = get_start_opt(label, fun([Lbl]) -> Lbl end, Args),
+ Vts = get_start_opt(vts, true, Args),
+ Shell = get_start_opt(shell, true, Args),
+ Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args),
+ LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args),
+ MultTT = get_start_opt(multiply_timetraps,
+ fun([MT]) -> list_to_integer(MT) end, 1, Args),
+ ScaleTT = get_start_opt(scale_timetraps,
+ fun([CT]) -> list_to_atom(CT);
+ ([]) -> true
+ end, false, Args),
+ EvHandlers = event_handler_args2opts(Args),
+ CTHooks = ct_hooks_args2opts(Args),
+
+ %% check flags and set corresponding application env variables
+
+ %% ct_decrypt_key | ct_decrypt_file
+ case proplists:get_value(ct_decrypt_key, Args) of
+ [DecryptKey] ->
application:set_env(common_test, decrypt, {key,DecryptKey});
- false ->
- case lists:keysearch(ct_decrypt_file, 1, Args) of
- {value,{_,[DecryptFile]}} ->
- application:set_env(common_test, decrypt,
- {file,filename:absname(DecryptFile)});
- false ->
+ undefined ->
+ case proplists:get_value(ct_decrypt_file, Args) of
+ [DecryptFile] ->
+ application:set_env(common_test, decrypt,
+ {file,?abs(DecryptFile)});
+ undefined ->
application:unset_env(common_test, decrypt)
end
end,
-
- case lists:keysearch(no_auto_compile, 1, Args) of
- {value,_} ->
- application:set_env(common_test, auto_compile, false);
- false ->
- application:set_env(common_test, auto_compile, true),
-
- InclDirs =
- case lists:keysearch(include,1,Args) of
- {value,{include,Incl}} when is_list(hd(Incl)) ->
- Incl;
- {value,{include,Incl}} when is_list(Incl) ->
- [Incl];
+ %% no_auto_compile + include
+ IncludeDirs =
+ case proplists:get_value(no_auto_compile, Args) of
+ undefined ->
+ application:set_env(common_test, auto_compile, true),
+ InclDirs =
+ case proplists:get_value(include, Args) of
+ Incl when is_list(hd(Incl)) ->
+ Incl;
+ Incl when is_list(Incl) ->
+ [Incl];
+ undefined ->
+ []
+ end,
+ case os:getenv("CT_INCLUDE_PATH") of
false ->
- []
- end,
- case os:getenv("CT_INCLUDE_PATH") of
- false ->
- application:set_env(common_test, include, InclDirs);
- CtInclPath ->
- InclDirs1 = string:tokens(CtInclPath,[$:,$ ,$,]),
- application:set_env(common_test, include, InclDirs1++InclDirs)
- end
- end,
-
- case lists:keysearch(basic_html, 1, Args) of
- {value,_} ->
- application:set_env(common_test, basic_html, true);
- false ->
- application:set_env(common_test, basic_html, false)
- end,
-
- Result =
- case lists:keysearch(refresh_logs, 1, Args) of
- {value,{refresh_logs,Refresh}} ->
- LogDir1 = case Refresh of
- [] -> LogDir;
- [RefreshDir] -> ?abs(RefreshDir)
- end,
- {ok,Cwd} = file:get_cwd(),
- file:set_cwd(LogDir1),
- timer:sleep(500), % give the shell time to print version etc
- io:nl(),
- case catch ct_logs:make_all_suites_index(refresh) of
- {'EXIT',ASReason} ->
- file:set_cwd(Cwd),
- {error,{all_suites_index,ASReason}};
- _ ->
- case catch ct_logs:make_all_runs_index(refresh) of
- {'EXIT',ARReason} ->
- file:set_cwd(Cwd),
- {error,{all_runs_index,ARReason}};
- _ ->
- file:set_cwd(Cwd),
- io:format("Logs in ~s refreshed!~n~n", [LogDir1]),
- timer:sleep(500), % time to flush io before quitting
- ok
- end
+ application:set_env(common_test, include, InclDirs),
+ InclDirs;
+ CtInclPath ->
+ AllInclDirs =
+ string:tokens(CtInclPath,[$:,$ ,$,]) ++ InclDirs,
+ application:set_env(common_test, include, AllInclDirs),
+ AllInclDirs
end;
- false ->
- case lists:keysearch(ct_config, 1, Args) of
- {value,{ct_config,ConfigFiles}} ->
- case lists:keysearch(spec, 1, Args) of
- false ->
- case get_configfiles(ConfigFiles, [], LogDir,
- EvHandlers) of
- ok ->
- script_start2(VtsOrShell, ConfigFiles,
- EvHandlers, Args, LogDir,
- Cover);
- Error ->
- Error
- end;
- _ ->
- script_start2(VtsOrShell, ConfigFiles,
- EvHandlers, Args, LogDir, Cover)
- end;
- false ->
- case install([{config,[]},
- {event_handler,EvHandlers}],
- LogDir) of
- ok ->
- script_start2(VtsOrShell, [], EvHandlers,
- Args, LogDir, Cover);
- Error ->
- Error
- end
- end
+ _ ->
+ application:set_env(common_test, auto_compile, false),
+ []
end,
+ %% silent connections
+ SilentConns =
+ get_start_opt(silent_connections,
+ fun(["all"]) -> [];
+ (Conns) -> [list_to_atom(Conn) || Conn <- Conns]
+ end, Args),
+ %% stylesheet
+ Stylesheet = get_start_opt(stylesheet,
+ fun([SS]) -> ?abs(SS) end, Args),
+ %% basic_html - used by ct_logs
+ case proplists:get_value(basic_html, Args) of
+ undefined ->
+ application:set_env(common_test, basic_html, false);
+ _ ->
+ application:set_env(common_test, basic_html, true)
+ end,
+
+ StartOpts = #opts{label = Label, vts = Vts, shell = Shell, cover = Cover,
+ logdir = LogDir, event_handlers = EvHandlers,
+ ct_hooks = CTHooks,
+ include = IncludeDirs,
+ silent_connections = SilentConns,
+ stylesheet = Stylesheet,
+ multiply_timetraps = MultTT,
+ scale_timetraps = ScaleTT},
+
+ %% check if log files should be refreshed or go on to run tests...
+ Result = run_or_refresh(StartOpts, Args),
+ %% send final results to starting process waiting in script_start/0
Parent ! {self(), Result}.
-get_configfiles([File|Files], Acc, LogDir, EvHandlers) ->
- case filelib:is_file(File) of
- true ->
- get_configfiles(Files, [?abs(File)|Acc],
- LogDir, EvHandlers);
- false ->
- {error,{cant_read_config_file,File}}
- end;
-get_configfiles([], Acc, LogDir, EvHandlers) ->
- install([{config,lists:reverse(Acc)}, {event_handler,EvHandlers}], LogDir).
+run_or_refresh(StartOpts = #opts{logdir = LogDir}, Args) ->
+ case proplists:get_value(refresh_logs, Args) of
+ undefined ->
+ script_start2(StartOpts, Args);
+ Refresh ->
+ LogDir1 = case Refresh of
+ [] -> which(logdir,LogDir);
+ [RefreshDir] -> ?abs(RefreshDir)
+ end,
+ {ok,Cwd} = file:get_cwd(),
+ file:set_cwd(LogDir1),
+ %% give the shell time to print version etc
+ timer:sleep(500),
+ io:nl(),
+ case catch ct_logs:make_all_suites_index(refresh) of
+ {'EXIT',ASReason} ->
+ file:set_cwd(Cwd),
+ {error,{all_suites_index,ASReason}};
+ _ ->
+ case catch ct_logs:make_all_runs_index(refresh) of
+ {'EXIT',ARReason} ->
+ file:set_cwd(Cwd),
+ {error,{all_runs_index,ARReason}};
+ _ ->
+ file:set_cwd(Cwd),
+ io:format("Logs in ~s refreshed!~n~n", [LogDir1]),
+ timer:sleep(500), % time to flush io before quitting
+ ok
+ end
+ end
+ end.
-script_start2(false, ConfigFiles, EvHandlers, Args, LogDir, Cover) ->
- case lists:keysearch(spec, 1, Args) of
- {value,{spec,[]}} ->
+script_start2(StartOpts = #opts{vts = undefined,
+ shell = undefined}, Args) ->
+ TestSpec = proplists:get_value(spec, Args),
+ {Terms,Opts} =
+ case TestSpec of
+ Specs when Specs =/= [], Specs =/= undefined ->
+ %% using testspec as input for test
+ Relaxed = get_start_opt(allow_user_terms, true, false, Args),
+ case catch ct_testspec:collect_tests_from_file(Specs, Relaxed) of
+ {E,Reason} when E == error ; E == 'EXIT' ->
+ {{error,Reason},StartOpts};
+ TS ->
+ SpecStartOpts = get_data_for_node(TS, node()),
+
+ Label = choose_val(StartOpts#opts.label,
+ SpecStartOpts#opts.label),
+
+ LogDir = choose_val(StartOpts#opts.logdir,
+ SpecStartOpts#opts.logdir),
+
+ Cover = choose_val(StartOpts#opts.cover,
+ SpecStartOpts#opts.cover),
+ MultTT = choose_val(StartOpts#opts.multiply_timetraps,
+ SpecStartOpts#opts.multiply_timetraps),
+ ScaleTT = choose_val(StartOpts#opts.scale_timetraps,
+ SpecStartOpts#opts.scale_timetraps),
+ AllEvHs = merge_vals([StartOpts#opts.event_handlers,
+ SpecStartOpts#opts.event_handlers]),
+ AllCTHooks = merge_vals(
+ [StartOpts#opts.ct_hooks,
+ SpecStartOpts#opts.ct_hooks]),
+
+ AllInclude = merge_vals([StartOpts#opts.include,
+ SpecStartOpts#opts.include]),
+ application:set_env(common_test, include, AllInclude),
+
+ {TS,StartOpts#opts{label = Label,
+ testspecs = Specs,
+ cover = Cover,
+ logdir = LogDir,
+ config = SpecStartOpts#opts.config,
+ event_handlers = AllEvHs,
+ ct_hooks = AllCTHooks,
+ include = AllInclude,
+ multiply_timetraps = MultTT,
+ scale_timetraps = ScaleTT}}
+ end;
+ _ ->
+ {undefined,StartOpts}
+ end,
+ %% read config/userconfig from start flags
+ InitConfig = ct_config:prepare_config_list(Args),
+ TheLogDir = which(logdir, Opts#opts.logdir),
+ case {TestSpec,Terms} of
+ {_,{error,_}=Error} ->
+ Error;
+ {[],_} ->
{error,no_testspec_specified};
- {value,{spec,Specs}} ->
- Relaxed = lists:keymember(allow_user_terms, 1, Args),
- %% using testspec as input for test
- case catch ct_testspec:collect_tests_from_file(Specs, Relaxed) of
- {error,Reason} ->
- {error,Reason};
- TS ->
- {LogDir1,TSCoverFile,ConfigFiles1,EvHandlers1,Include1} =
- get_data_for_node(TS,node()),
- UserInclude =
- case application:get_env(common_test, include) of
- {ok,Include} -> Include++Include1;
- _ -> Include1
- end,
- application:set_env(common_test, include, UserInclude),
- LogDir2 = which_logdir(LogDir,LogDir1),
- CoverOpt = case {Cover,TSCoverFile} of
- {false,undef} -> [];
- {_,undef} -> [Cover];
- {false,_} -> [{cover,TSCoverFile}]
- end,
- case get_configfiles(ConfigFiles++ConfigFiles1,
- [], LogDir2,
- EvHandlers++EvHandlers1) of
- ok ->
- {Run,Skip} = ct_testspec:prepare_tests(TS, node()),
- do_run(Run, Skip, CoverOpt, Args, LogDir2);
- Error ->
- Error
- end
+ {undefined,_} -> % no testspec used
+ case check_and_install_configfiles(InitConfig, TheLogDir,
+ Opts#opts.event_handlers,
+ Opts#opts.ct_hooks) of
+ ok -> % go on read tests from start flags
+ script_start3(Opts#opts{config=InitConfig,
+ logdir=TheLogDir}, Args);
+ Error ->
+ Error
end;
- false ->
- script_start3(false, ConfigFiles, EvHandlers, Args, LogDir, Cover)
+ {_,_} -> % testspec used
+ %% merge config from start flags with config from testspec
+ AllConfig = merge_vals([InitConfig, Opts#opts.config]),
+ case check_and_install_configfiles(AllConfig, TheLogDir,
+ Opts#opts.event_handlers,
+ Opts#opts.ct_hooks) of
+ ok -> % read tests from spec
+ {Run,Skip} = ct_testspec:prepare_tests(Terms, node()),
+ do_run(Run, Skip, Opts#opts{config=AllConfig,
+ logdir=TheLogDir}, Args);
+ Error ->
+ Error
+ end
end;
-script_start2(VtsOrShell, ConfigFiles, EvHandlers, Args, LogDir, Cover) ->
- script_start3(VtsOrShell, ConfigFiles, EvHandlers, Args, LogDir, Cover).
-script_start3(VtsOrShell, ConfigFiles, EvHandlers, Args, LogDir, Cover) ->
- case lists:keysearch(dir, 1, Args) of
- {value,{dir,[]}} ->
- {error,no_dir_specified};
- {value,{dir,Dirs}} ->
- script_start4(VtsOrShell, ConfigFiles, EvHandlers, tests(Dirs),
- Cover, Args, LogDir);
+script_start2(StartOpts, Args) ->
+ %% read config/userconfig from start flags
+ InitConfig = ct_config:prepare_config_list(Args),
+ LogDir = which(logdir, StartOpts#opts.logdir),
+ case check_and_install_configfiles(InitConfig, LogDir,
+ StartOpts#opts.event_handlers,
+ StartOpts#opts.ct_hooks) of
+ ok -> % go on read tests from start flags
+ script_start3(StartOpts#opts{config=InitConfig,
+ logdir=LogDir}, Args);
+ Error ->
+ Error
+ end.
+
+check_and_install_configfiles(Configs, LogDir, EvHandlers, CTHooks) ->
+ case ct_config:check_config_files(Configs) of
false ->
- case lists:keysearch(suite, 1, Args) of
- {value,{suite,[]}} ->
+ install([{config,Configs},
+ {event_handler,EvHandlers},
+ {ct_hooks,CTHooks}], LogDir);
+ {value,{error,{nofile,File}}} ->
+ {error,{cant_read_config_file,File}};
+ {value,{error,{wrong_config,Message}}}->
+ {error,{wrong_config,Message}};
+ {value,{error,{callback,Info}}} ->
+ {error,{cant_load_callback_module,Info}}
+ end.
+
+script_start3(StartOpts, Args) ->
+ case proplists:get_value(dir, Args) of
+ [] ->
+ {error,no_dir_specified};
+ Dirs when is_list(Dirs) ->
+ script_start4(StartOpts#opts{tests = tests(Dirs)}, Args);
+ undefined ->
+ case proplists:get_value(suite, Args) of
+ [] ->
{error,no_suite_specified};
- {value,{suite,Suites}} ->
- StepOrCover =
- case lists:keysearch(step, 1, Args) of
- {value,Step} -> Step;
- false -> Cover
- end,
- S2M = fun(S) ->
- {filename:dirname(S),
- list_to_atom(
- filename:rootname(filename:basename(S)))}
- end,
- DirMods = lists:map(S2M, Suites),
- {Specified,GroupsAndCases} =
- case {lists:keysearch(group, 1, Args),
- lists:keysearch('case', 1, Args)} of
- {{value,{_,Gs}},{value,{_,Cs}}} -> {true,Gs++Cs};
- {{value,{_,Gs}},_} -> {true,Gs};
- {_,{value,{_,Cs}}} -> {true,Cs};
- _ -> {false,[]}
- end,
- if Specified, length(GroupsAndCases) == 0 ->
- {error,no_case_or_group_specified};
- Specified, length(DirMods) > 1 ->
+ Suites when is_list(Suites) ->
+ StartOpts1 =
+ get_start_opt(step,
+ fun(Step) ->
+ StartOpts#opts{step = Step,
+ cover = undefined}
+ end, StartOpts, Args),
+ DirMods = [suite_to_test(S) || S <- Suites],
+ case groups_and_cases(proplists:get_value(group, Args),
+ proplists:get_value(testcase, Args)) of
+ Error = {error,_} ->
+ Error;
+ [] when DirMods =/= [] ->
+ Ts = tests(DirMods),
+ script_start4(StartOpts1#opts{tests = Ts}, Args);
+ GroupsAndCases when length(DirMods) == 1 ->
+ Ts = tests(DirMods, GroupsAndCases),
+ script_start4(StartOpts1#opts{tests = Ts}, Args);
+ [_,_|_] when length(DirMods) > 1 ->
{error,multiple_suites_and_cases};
- length(GroupsAndCases) > 0, length(DirMods) == 1 ->
- GsAndCs = lists:map(fun(C) -> list_to_atom(C) end,
- GroupsAndCases),
- script_start4(VtsOrShell, ConfigFiles, EvHandlers,
- tests(DirMods, GsAndCs),
- StepOrCover, Args, LogDir);
- not Specified, length(DirMods) > 0 ->
- script_start4(VtsOrShell, ConfigFiles, EvHandlers,
- tests(DirMods),
- StepOrCover, Args, LogDir);
- true ->
- {error,incorrect_suite_and_case_options}
+ _ ->
+ {error,incorrect_suite_option}
end;
- false when VtsOrShell=/=false ->
- script_start4(VtsOrShell, ConfigFiles, EvHandlers,
- [], Cover, Args, LogDir);
- false ->
- script_usage(),
- {error,incorrect_usage}
+ undefined ->
+ if StartOpts#opts.vts ; StartOpts#opts.shell ->
+ script_start4(StartOpts#opts{tests = []}, Args);
+ true ->
+ script_usage(),
+ {error,incorrect_usage}
+ end
end
end.
-script_start4(vts, ConfigFiles, EvHandlers, Tests, false, _Args, LogDir) ->
+script_start4(#opts{vts = true, config = Config, event_handlers = EvHandlers,
+ tests = Tests, logdir = LogDir}, _Args) ->
+ ConfigFiles =
+ lists:foldl(fun({ct_config_plain,CfgFiles}, AllFiles) when
+ is_list(hd(CfgFiles)) ->
+ AllFiles ++ CfgFiles;
+ ({ct_config_plain,CfgFile}, AllFiles) when
+ is_integer(hd(CfgFile)) ->
+ AllFiles ++ [CfgFile];
+ (_, AllFiles) ->
+ AllFiles
+ end, [], Config),
vts:init_data(ConfigFiles, EvHandlers, ?abs(LogDir), Tests);
-script_start4(shell, ConfigFiles, EvHandlers, _Tests, false, Args, LogDir) ->
- Opts = [{config,ConfigFiles},{event_handler,EvHandlers}],
- if ConfigFiles == [] ->
+
+script_start4(#opts{label = Label, shell = true, config = Config,
+ event_handlers = EvHandlers,
+ ct_hooks = CTHooks,
+ logdir = LogDir, testspecs = Specs}, _Args) ->
+ %% label - used by ct_logs
+ application:set_env(common_test, test_label, Label),
+
+ InstallOpts = [{config,Config},{event_handler,EvHandlers},
+ {ct_hooks, CTHooks}],
+ if Config == [] ->
ok;
true ->
- io:format("\nInstalling: ~p\n\n", [ConfigFiles])
+ io:format("\nInstalling: ~p\n\n", [Config])
end,
- case install(Opts) of
+ case install(InstallOpts) of
ok ->
ct_util:start(interactive, LogDir),
- log_ts_names(Args),
+ log_ts_names(Specs),
io:nl(),
ok;
Error ->
Error
end;
-script_start4(vts, _CfgFs, _EvHs, _Tests, _Cover={cover,_}, _Args, _LogDir) ->
- %% Add support later (maybe).
- script_usage(),
- erlang:halt();
-script_start4(shell, _CfgFs, _EvHs, _Tests, _Cover={cover,_}, _Args, _LogDir) ->
- %% Add support later (maybe).
- script_usage();
-script_start4(false, _CfgFs, _EvHs, Tests, Cover={cover,_}, Args, LogDir) ->
- do_run(Tests, [], [Cover], Args, LogDir);
-script_start4(false, _ConfigFiles, _EvHandlers, Tests, false, Args, LogDir) ->
- do_run(Tests, [], [], Args, LogDir);
-script_start4(false, _ConfigFiles, _EvHandlers, Test, Step, Args, LogDir) ->
- do_run(Test, [], [Step], Args, LogDir);
-script_start4(vts, _ConfigFiles, _EvHandlers, _Test, _Step, _Args, _LogDir) ->
- script_usage(),
+
+script_start4(#opts{vts = true, cover = Cover}, _) ->
+ case Cover of
+ undefined ->
+ script_usage();
+ _ ->
+ %% Add support later (maybe).
+ io:format("\nCan't run cover in vts mode.\n\n", [])
+ end,
erlang:halt();
-script_start4(shell, _ConfigFiles, _EvHandlers, _Test, _Step, _Args, _LogDir) ->
- script_usage().
+
+script_start4(#opts{shell = true, cover = Cover}, _) ->
+ case Cover of
+ undefined ->
+ script_usage();
+ _ ->
+ %% Add support later (maybe).
+ io:format("\nCan't run cover in interactive mode.\n\n", [])
+ end;
+
+script_start4(Opts = #opts{tests = Tests}, Args) ->
+ do_run(Tests, [], Opts, Args).
%%%-----------------------------------------------------------------
%%% @spec script_usage() -> ok
-%%% @doc Print script usage information for <code>run_test</code>.
+%%% @doc Print usage information for <code>ct_run</code>.
script_usage() ->
io:format("\n\nUsage:\n\n"),
io:format("Run tests in web based GUI:\n\n"
- "\trun_test -vts [-browser Browser]"
+ "\tct_run -vts [-browser Browser]"
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-dir TestDir1 TestDir2 .. TestDirN] |"
"\n\t[-suite Suite [-case Case]]"
- "\n\t[-include InclDir1 InclDir2 .. InclDirN]"
+ "\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
+ "\n\t[-multiply_timetraps N]"
+ "\n\t[-scale_timetraps]"
"\n\t[-basic_html]\n\n"),
io:format("Run tests from command line:\n\n"
- "\trun_test [-dir TestDir1 TestDir2 .. TestDirN] |"
+ "\tct_run [-dir TestDir1 TestDir2 .. TestDirN] |"
"\n\t[-suite Suite1 Suite2 .. SuiteN [-case Case1 Case2 .. CaseN]]"
"\n\t[-step [config | keep_inactive]]"
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
+ "\n\t[-userconfig CallbackModule ConfigFile1 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-logdir LogDir]"
"\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]"
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-include InclDir1 InclDir2 .. InclDirN]"
+ "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
+ "\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
- "\n\t[-basic_html]"
- "\n\t[-repeat N [-force_stop]] |"
+ "\n\t[-multiply_timetraps N]"
+ "\n\t[-scale_timetraps]"
+ "\n\t[-basic_html]"
+ "\n\t[-repeat N [-force_stop]] |"
"\n\t[-duration HHMMSS [-force_stop]] |"
"\n\t[-until [YYMoMoDD]HHMMSS [-force_stop]]\n\n"),
io:format("Run tests using test specification:\n\n"
- "\trun_test -spec TestSpec1 TestSpec2 .. TestSpecN"
+ "\tct_run -spec TestSpec1 TestSpec2 .. TestSpecN"
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t[-logdir LogDir]"
@@ -426,21 +541,23 @@ script_usage() ->
"\n\t[-stylesheet CSSFile]"
"\n\t[-cover CoverCfgFile]"
"\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]"
- "\n\t[-include InclDir1 InclDir2 .. InclDirN]"
+ "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]"
+ "\n\t[-include InclDir1 InclDir2 .. InclDirN]"
"\n\t[-no_auto_compile]"
- "\n\t[-basic_html]"
- "\n\t[-repeat N [-force_stop]] |"
+ "\n\t[-multiply_timetraps N]"
+ "\n\t[-scale_timetraps]"
+ "\n\t[-basic_html]"
+ "\n\t[-repeat N [-force_stop]] |"
"\n\t[-duration HHMMSS [-force_stop]] |"
"\n\t[-until [YYMoMoDD]HHMMSS [-force_stop]]\n\n"),
io:format("Refresh the HTML index files:\n\n"
- "\trun_test -refresh_logs [LogDir]"
+ "\tct_run -refresh_logs [LogDir]"
"[-logdir LogDir] "
"[-basic_html]\n\n"),
io:format("Run CT in interactive mode:\n\n"
- "\trun_test -shell"
+ "\tct_run -shell"
"\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]\n\n").
-
%%%-----------------------------------------------------------------
%%% @hidden
@@ -449,6 +566,9 @@ install(Opts) ->
install(Opts, ".").
install(Opts, LogDir) ->
+
+ ConfOpts = ct_config:add_default_callback(Opts),
+
case application:get_env(common_test, decrypt) of
{ok,_} ->
ok;
@@ -465,10 +585,10 @@ install(Opts, LogDir) ->
VarFile = variables_file_name(LogDir),
case file:open(VarFile, [write]) of
{ok,Fd} ->
- [io:format(Fd, "~p.\n", [Opt]) || Opt <- Opts],
+ [io:format(Fd, "~p.\n", [Opt]) || Opt <- ConfOpts ],
file:close(Fd),
ok;
- {error,Reason} ->
+ {error,Reason} ->
io:format("CT failed to install configuration data. Please "
"verify that the log directory exists and that "
"write permission is set.\n\n", []),
@@ -487,69 +607,62 @@ variables_file_name(Dir) ->
filename:join(Dir, "variables-"++atom_to_list(node())).
%%%-----------------------------------------------------------------
-%%% @hidden
+%%% @spec run_test(Opts) -> Result
+%%% Opts = [tuple()]
+%%% Result = [TestResult] | {error,Reason}
+%%%
+%%% @doc Start tests from the erlang shell or from an erlang program.
%%% @equiv ct:run_test/1
+%%%-----------------------------------------------------------------
-%% Opts = [OptTuples]
-%% OptTuples = {config,CfgFiles} | {dir,TestDirs} | {suite,Suites} |
-%% {testcase,Cases} | {spec,TestSpecs} | {allow_user_terms,Bool} |
-%% {logdir,LogDir} | {cover,CoverSpecFile} | {step,StepOpts} |
-%% {silent_connections,Conns} | {event_handler,EventHandlers} |
-%% {include,InclDirs} | {auto_compile,Bool} |
-%% {repeat,N} | {duration,DurTime} | {until,StopTime} | {force_stop,Bool} |
-%% {decrypt,KeyOrFile}
-
-run_test(Opt) when is_tuple(Opt) ->
- run_test([Opt]);
-
-run_test(Opts) when is_list(Opts) ->
- case lists:keysearch(refresh_logs, 1, Opts) of
- {value,{_,RefreshDir}} ->
- refresh_logs(?abs(RefreshDir)),
- ok;
- false ->
- Tracing = start_trace(Opts),
+run_test(StartOpt) when is_tuple(StartOpt) ->
+ run_test([StartOpt]);
+
+run_test(StartOpts) when is_list(StartOpts) ->
+ case proplists:get_value(refresh_logs, StartOpts) of
+ undefined ->
+ Tracing = start_trace(StartOpts),
{ok,Cwd} = file:get_cwd(),
- io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]),
+ io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]),
Res =
- case ct_repeat:loop_test(func, Opts) of
+ case ct_repeat:loop_test(func, StartOpts) of
false ->
- case catch run_test1(Opts) of
- {'EXIT',Reason} ->
+ case catch run_test1(StartOpts) of
+ {'EXIT',Reason} ->
file:set_cwd(Cwd),
{error,Reason};
- Result ->
+ Result ->
Result
end;
Result ->
Result
end,
stop_trace(Tracing),
- Res
+ Res;
+ RefreshDir ->
+ refresh_logs(?abs(RefreshDir)),
+ ok
end.
-run_test1(Opts) ->
- LogDir =
- case lists:keysearch(logdir, 1, Opts) of
- {value,{_,LD}} when is_list(LD) -> LD;
- false -> "."
- end,
- CfgFiles =
- case lists:keysearch(config, 1, Opts) of
- {value,{_,Files=[File|_]}} when is_list(File) ->
- Files;
- {value,{_,File=[C|_]}} when is_integer(C) ->
- [File];
- {value,{_,[]}} ->
- [];
- false ->
- []
- end,
+run_test1(StartOpts) ->
+ %% label
+ Label = get_start_opt(label, fun(Lbl) when is_list(Lbl) -> Lbl;
+ (Lbl) when is_atom(Lbl) -> atom_to_list(Lbl)
+ end, StartOpts),
+ %% logdir
+ LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end,
+ StartOpts),
+ %% config & userconfig
+ CfgFiles = ct_config:get_config_file_list(StartOpts),
+
+ %% event handlers
EvHandlers =
- case lists:keysearch(event_handler, 1, Opts) of
- {value,{_,H}} when is_atom(H) ->
+ case proplists:get_value(event_handler, StartOpts) of
+ undefined ->
+ [];
+ H when is_atom(H) ->
[{H,[]}];
- {value,{_,H}} ->
+ H ->
Hs =
if is_tuple(H) -> [H];
is_list(H) -> H;
@@ -564,41 +677,42 @@ run_test1(Opts) ->
{EH,Args};
(_) ->
[]
- end, Hs));
- _ ->
- []
- end,
- SilentConns =
- case lists:keysearch(silent_connections, 1, Opts) of
- {value,{_,all}} ->
- [];
- {value,{_,Conns}} ->
- Conns;
- _ ->
- undefined
- end,
- Cover =
- case lists:keysearch(cover, 1, Opts) of
- {value,{_,CoverFile}} ->
- [{cover,?abs(CoverFile)}];
- _ ->
- []
+ end, Hs))
end,
+
+ %% CT Hooks
+ CTHooks = get_start_opt(ct_hooks, value, [], StartOpts),
+
+ %% silent connections
+ SilentConns = get_start_opt(silent_connections,
+ fun(all) -> [];
+ (Conns) -> Conns
+ end, StartOpts),
+ %% stylesheet
+ Stylesheet = get_start_opt(stylesheet,
+ fun(SS) -> ?abs(SS) end,
+ StartOpts),
+ %% code coverage
+ Cover = get_start_opt(cover,
+ fun(CoverFile) -> ?abs(CoverFile) end, StartOpts),
+
+ %% timetrap manipulation
+ MultiplyTT = get_start_opt(multiply_timetraps, value, 1, StartOpts),
+ ScaleTT = get_start_opt(scale_timetraps, value, false, StartOpts),
+
+ %% auto compile & include files
Include =
- case lists:keysearch(auto_compile, 1, Opts) of
- {value,{auto_compile,ACBool}} ->
- application:set_env(common_test, auto_compile, ACBool),
- [];
- _ ->
+ case proplists:get_value(auto_compile, StartOpts) of
+ undefined ->
application:set_env(common_test, auto_compile, true),
InclDirs =
- case lists:keysearch(include, 1, Opts) of
- {value,{include,Incl}} when is_list(hd(Incl)) ->
- Incl;
- {value,{include,Incl}} when is_list(Incl) ->
- [Incl];
- false ->
- []
+ case proplists:get_value(include, StartOpts) of
+ undefined ->
+ [];
+ Incl when is_list(hd(Incl)) ->
+ Incl;
+ Incl when is_list(Incl) ->
+ [Incl]
end,
case os:getenv("CT_INCLUDE_PATH") of
false ->
@@ -609,117 +723,187 @@ run_test1(Opts) ->
AllInclDirs = InclDirs1++InclDirs,
application:set_env(common_test, include, AllInclDirs),
AllInclDirs
- end
+ end;
+ ACBool ->
+ application:set_env(common_test, auto_compile, ACBool),
+ []
end,
- case lists:keysearch(decrypt, 1, Opts) of
- {value,{_,Key={key,_}}} ->
+ %% decrypt config file
+ case proplists:get_value(decrypt, StartOpts) of
+ undefined ->
+ application:unset_env(common_test, decrypt);
+ Key={key,_} ->
application:set_env(common_test, decrypt, Key);
- {value,{_,{file,KeyFile}}} ->
- application:set_env(common_test, decrypt, {file,filename:absname(KeyFile)});
- false ->
- application:unset_env(common_test, decrypt)
+ {file,KeyFile} ->
+ application:set_env(common_test, decrypt, {file,?abs(KeyFile)})
end,
- case lists:keysearch(basic_html, 1, Opts) of
- {value,{basic_html,BasicHtmlBool}} ->
- application:set_env(common_test, basic_html, BasicHtmlBool);
- _ ->
- application:set_env(common_test, basic_html, false)
+ %% basic html - used by ct_logs
+ case proplists:get_value(basic_html, StartOpts) of
+ undefined ->
+ application:set_env(common_test, basic_html, false);
+ BasicHtmlBool ->
+ application:set_env(common_test, basic_html, BasicHtmlBool)
end,
- case lists:keysearch(spec, 1, Opts) of
- {value,{_,Specs}} ->
- Relaxed =
- case lists:keysearch(allow_user_terms, 1, Opts) of
- {value,{_,true}} -> true;
- _ -> false
- end,
- %% using testspec(s) as input for test
- run_spec_file(LogDir, CfgFiles, EvHandlers, Include, Specs, Relaxed, Cover,
- replace_opt([{silent_connections,SilentConns}], Opts));
- false ->
- case lists:keysearch(prepared_tests, 1, Opts) of
+ %% stepped execution
+ Step = get_start_opt(step, value, StartOpts),
+
+ Opts = #opts{label = Label,
+ cover = Cover, step = Step, logdir = LogDir, config = CfgFiles,
+ event_handlers = EvHandlers,
+ ct_hooks = CTHooks,
+ include = Include,
+ silent_connections = SilentConns,
+ stylesheet = Stylesheet,
+ multiply_timetraps = MultiplyTT,
+ scale_timetraps = ScaleTT},
+
+ %% test specification
+ case proplists:get_value(spec, StartOpts) of
+ undefined ->
+ case lists:keysearch(prepared_tests, 1, StartOpts) of
{value,{_,{Run,Skip},Specs}} -> % use prepared tests
- run_prepared(LogDir, CfgFiles, EvHandlers,
- Run, Skip, Cover,
- replace_opt([{silent_connections,SilentConns},
- {spec,Specs}],Opts));
- false -> % use dir|suite|case
- StepOrCover =
- case lists:keysearch(step, 1, Opts) of
- {value,Step} -> [Step];
- false -> Cover
- end,
- run_dir(LogDir, CfgFiles, EvHandlers, StepOrCover,
- replace_opt([{silent_connections,SilentConns}], Opts))
- end
+ run_prepared(Run, Skip, Opts#opts{testspecs = Specs},
+ StartOpts);
+ false ->
+ run_dir(Opts, StartOpts)
+ end;
+ Specs ->
+ Relaxed = get_start_opt(allow_user_terms, value, false, StartOpts),
+ %% using testspec(s) as input for test
+ run_spec_file(Relaxed, Opts#opts{testspecs = Specs}, StartOpts)
end.
-replace_opt([O={Key,_Val}|Os], Opts) ->
- [O | replace_opt(Os, lists:keydelete(Key, 1, Opts))];
-replace_opt([], Opts) ->
- Opts.
-
-run_spec_file(LogDir, CfgFiles, EvHandlers, Include, Specs, Relaxed, Cover, Opts) ->
+run_spec_file(Relaxed,
+ Opts = #opts{testspecs = Specs, config = CfgFiles},
+ StartOpts) ->
Specs1 = case Specs of
[X|_] when is_integer(X) -> [Specs];
_ -> Specs
end,
- AbsSpecs = lists:map(fun(SF) -> ?abs(SF) end, Specs1),
+ AbsSpecs = lists:map(fun(SF) -> ?abs(SF) end, Specs1),
log_ts_names(AbsSpecs),
case catch ct_testspec:collect_tests_from_file(AbsSpecs, Relaxed) of
- {error,CTReason} ->
+ {Error,CTReason} when Error == error ; Error == 'EXIT' ->
exit(CTReason);
TS ->
- {LogDir1,TSCoverFile,CfgFiles1,EvHandlers1,Include1} =
- get_data_for_node(TS, node()),
- application:set_env(common_test, include, Include++Include1),
- LogDir2 = which_logdir(LogDir, LogDir1),
- CoverOpt = case {Cover,TSCoverFile} of
- {[],undef} -> [];
- {_,undef} -> Cover;
- {[],_} -> [{cover,TSCoverFile}]
- end,
- case get_configfiles(CfgFiles++CfgFiles1, [], LogDir2,
- EvHandlers++EvHandlers1) of
+ SpecOpts = get_data_for_node(TS, node()),
+ Label = choose_val(Opts#opts.label,
+ SpecOpts#opts.label),
+ LogDir = choose_val(Opts#opts.logdir,
+ SpecOpts#opts.logdir),
+ AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]),
+ Cover = choose_val(Opts#opts.cover,
+ SpecOpts#opts.cover),
+ MultTT = choose_val(Opts#opts.multiply_timetraps,
+ SpecOpts#opts.multiply_timetraps),
+ ScaleTT = choose_val(Opts#opts.scale_timetraps,
+ SpecOpts#opts.scale_timetraps),
+ AllEvHs = merge_vals([Opts#opts.event_handlers,
+ SpecOpts#opts.event_handlers]),
+ AllInclude = merge_vals([Opts#opts.include,
+ SpecOpts#opts.include]),
+
+ AllCTHooks = merge_vals([Opts#opts.ct_hooks,
+ SpecOpts#opts.ct_hooks]),
+
+ application:set_env(common_test, include, AllInclude),
+
+ case check_and_install_configfiles(AllConfig,
+ which(logdir,LogDir),
+ AllEvHs,
+ AllCTHooks) of
ok ->
+ Opts1 = Opts#opts{label = Label,
+ cover = Cover,
+ logdir = which(logdir, LogDir),
+ config = AllConfig,
+ event_handlers = AllEvHs,
+ include = AllInclude,
+ testspecs = AbsSpecs,
+ multiply_timetraps = MultTT,
+ scale_timetraps = ScaleTT,
+ ct_hooks = AllCTHooks},
{Run,Skip} = ct_testspec:prepare_tests(TS, node()),
- do_run(Run, Skip, CoverOpt,
- replace_opt([{spec,AbsSpecs}], Opts),
- LogDir2);
+ reformat_result(catch do_run(Run, Skip, Opts1, StartOpts));
{error,GCFReason} ->
exit(GCFReason)
end
end.
-run_prepared(LogDir, CfgFiles, EvHandlers, Run, Skip, Cover, Opts) ->
- case get_configfiles(CfgFiles, [], LogDir, EvHandlers) of
+run_prepared(Run, Skip, Opts = #opts{logdir = LogDir,
+ config = CfgFiles,
+ event_handlers = EvHandlers,
+ ct_hooks = CTHooks},
+ StartOpts) ->
+ LogDir1 = which(logdir, LogDir),
+ case check_and_install_configfiles(CfgFiles, LogDir1,
+ EvHandlers, CTHooks) of
ok ->
- do_run(Run, Skip, Cover, Opts, LogDir);
+ reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1},
+ StartOpts));
{error,Reason} ->
exit(Reason)
- end.
-
-run_dir(LogDir, CfgFiles, EvHandlers, StepOrCover, Opts) ->
- AbsCfgFiles =
- lists:map(fun(F) ->
- AbsName = ?abs(F),
- case filelib:is_file(AbsName) of
- true -> AbsName;
- false -> exit({no_such_file,AbsName})
- end
- end, CfgFiles),
-
- case install([{config,AbsCfgFiles},{event_handler,EvHandlers}], LogDir) of
+ end.
+
+check_config_file(Callback, File)->
+ case code:is_loaded(Callback) of
+ false ->
+ case code:load_file(Callback) of
+ {module,_} -> ok;
+ {error,Why} -> exit({cant_load_callback_module,Why})
+ end;
+ _ ->
+ ok
+ end,
+ case Callback:check_parameter(File) of
+ {ok,{file,File}}->
+ ?abs(File);
+ {ok,{config,_}}->
+ File;
+ {error,{wrong_config,Message}}->
+ exit({wrong_config,{Callback,Message}});
+ {error,{nofile,File}}->
+ exit({no_such_file,?abs(File)})
+ end.
+
+run_dir(Opts = #opts{logdir = LogDir,
+ config = CfgFiles,
+ event_handlers = EvHandlers,
+ ct_hooks = CTHook }, StartOpts) ->
+ LogDir1 = which(logdir, LogDir),
+ Opts1 = Opts#opts{logdir = LogDir1},
+ AbsCfgFiles =
+ lists:map(fun({Callback,FileList})->
+ case code:is_loaded(Callback) of
+ {file,_Path}->
+ ok;
+ false ->
+ case code:load_file(Callback) of
+ {module,Callback}->
+ ok;
+ {error,_}->
+ exit({no_such_module,Callback})
+ end
+ end,
+ {Callback,
+ lists:map(fun(File)->
+ check_config_file(Callback, File)
+ end, FileList)}
+ end, CfgFiles),
+ case install([{config,AbsCfgFiles},
+ {event_handler,EvHandlers},
+ {ct_hooks, CTHook}], LogDir1) of
ok -> ok;
{error,IReason} -> exit(IReason)
end,
- case lists:keysearch(dir,1,Opts) of
+ case lists:keysearch(dir, 1, StartOpts) of
{value,{_,Dirs=[Dir|_]}} when not is_integer(Dir),
length(Dirs)>1 ->
%% multiple dirs (no suite)
- do_run(tests(Dirs), [], StepOrCover, Opts, LogDir);
+ reformat_result(catch do_run(tests(Dirs), [], Opts1, StartOpts));
false -> % no dir
%% fun for converting suite name to {Dir,Mod} tuple
S2M = fun(S) when is_list(S) ->
@@ -728,105 +912,139 @@ run_dir(LogDir, CfgFiles, EvHandlers, StepOrCover, Opts) ->
(A) ->
{".",A}
end,
- case lists:keysearch(suite, 1, Opts) of
+ case lists:keysearch(suite, 1, StartOpts) of
{value,{_,Suite}} when is_integer(hd(Suite)) ; is_atom(Suite) ->
{Dir,Mod} = S2M(Suite),
- case listify(proplists:get_value(group, Opts, [])) ++
- listify(proplists:get_value(testcase, Opts, [])) of
+ case groups_and_cases(proplists:get_value(group, StartOpts),
+ proplists:get_value(testcase, StartOpts)) of
+ Error = {error,_} ->
+ exit(Error);
[] ->
- do_run(tests(Dir, listify(Mod)), [], StepOrCover, Opts, LogDir);
+ reformat_result(catch do_run(tests(Dir, listify(Mod)),
+ [], Opts1, StartOpts));
GsAndCs ->
- do_run(tests(Dir, Mod, GsAndCs), [], StepOrCover, Opts, LogDir)
+ reformat_result(catch do_run(tests(Dir, Mod, GsAndCs),
+ [], Opts1, StartOpts))
end;
{value,{_,Suites}} ->
- do_run(tests(lists:map(S2M, Suites)), [], StepOrCover, Opts, LogDir);
+ reformat_result(catch do_run(tests(lists:map(S2M, Suites)),
+ [], Opts1, StartOpts));
_ ->
exit(no_tests_specified)
- end;
+ end;
{value,{_,Dir}} ->
- case lists:keysearch(suite, 1, Opts) of
+ case lists:keysearch(suite, 1, StartOpts) of
{value,{_,Suite}} when is_integer(hd(Suite)) ; is_atom(Suite) ->
- Mod = if is_atom(Suite) -> Suite;
- true -> list_to_atom(Suite)
+ Mod = if is_atom(Suite) -> Suite;
+ true -> list_to_atom(Suite)
end,
- case listify(proplists:get_value(group, Opts, [])) ++
- listify(proplists:get_value(testcase, Opts, [])) of
+ case groups_and_cases(proplists:get_value(group, StartOpts),
+ proplists:get_value(testcase, StartOpts)) of
+ Error = {error,_} ->
+ exit(Error);
[] ->
- do_run(tests(Dir, listify(Mod)), [], StepOrCover, Opts, LogDir);
+ reformat_result(catch do_run(tests(Dir, listify(Mod)),
+ [], Opts1, StartOpts));
GsAndCs ->
- do_run(tests(Dir, Mod, GsAndCs), [], StepOrCover, Opts, LogDir)
+ reformat_result(catch do_run(tests(Dir, Mod, GsAndCs),
+ [], Opts1, StartOpts))
end;
{value,{_,Suites=[Suite|_]}} when is_list(Suite) ->
Mods = lists:map(fun(Str) -> list_to_atom(Str) end, Suites),
- do_run(tests(delistify(Dir), Mods), [], StepOrCover, Opts, LogDir);
+ reformat_result(catch do_run(tests(delistify(Dir), Mods),
+ [], Opts1, StartOpts));
{value,{_,Suites}} ->
- do_run(tests(delistify(Dir), Suites), [], StepOrCover, Opts, LogDir);
+ reformat_result(catch do_run(tests(delistify(Dir), Suites),
+ [], Opts1, StartOpts));
false -> % no suite, only dir
- do_run(tests(listify(Dir)), [], StepOrCover, Opts, LogDir)
- end
+ reformat_result(catch do_run(tests(listify(Dir)),
+ [], Opts1, StartOpts))
+ end
end.
%%%-----------------------------------------------------------------
-%%% @hidden
+%%% @spec run_testspec(TestSpec) -> Result
+%%% TestSpec = [term()]
%%%
+%%% @doc Run test specified by <code>TestSpec</code>. The terms are
+%%% the same as those used in test specification files.
+%%% @equiv ct:run_testspec/1
+%%%-----------------------------------------------------------------
-%% using testspec(s) as input for test
run_testspec(TestSpec) ->
{ok,Cwd} = file:get_cwd(),
io:format("~nCommon Test starting (cwd is ~s)~n~n", [Cwd]),
case catch run_testspec1(TestSpec) of
- {'EXIT',Reason} ->
+ {'EXIT',Reason} ->
file:set_cwd(Cwd),
{error,Reason};
- Result ->
+ Result ->
Result
end.
run_testspec1(TestSpec) ->
- case ct_testspec:collect_tests_from_list(TestSpec,false) of
- {error,CTReason} ->
+ case catch ct_testspec:collect_tests_from_list(TestSpec, false) of
+ {E,CTReason} when E == error ; E == 'EXIT' ->
exit(CTReason);
TS ->
- {LogDir,TSCoverFile,CfgFiles,EvHandlers,Include} =
- get_data_for_node(TS,node()),
- case os:getenv("CT_INCLUDE_PATH") of
- false ->
- application:set_env(common_test, include, Include);
- CtInclPath ->
- EnvInclude = string:tokens(CtInclPath, [$:,$ ,$,]),
- application:set_env(common_test, include, EnvInclude++Include)
- end,
- CoverOpt = if TSCoverFile == undef -> [];
- true -> [{cover,TSCoverFile}]
- end,
- case get_configfiles(CfgFiles,[],LogDir,EvHandlers) of
+ Opts = get_data_for_node(TS, node()),
+
+ AllInclude =
+ case os:getenv("CT_INCLUDE_PATH") of
+ false ->
+ Opts#opts.include;
+ CtInclPath ->
+ EnvInclude = string:tokens(CtInclPath, [$:,$ ,$,]),
+ EnvInclude++Opts#opts.include
+ end,
+ application:set_env(common_test, include, AllInclude),
+ LogDir1 = which(logdir,Opts#opts.logdir),
+ case check_and_install_configfiles(Opts#opts.config, LogDir1,
+ Opts#opts.event_handlers,
+ Opts#opts.ct_hooks) of
ok ->
- {Run,Skip} = ct_testspec:prepare_tests(TS,node()),
- do_run(Run,Skip,CoverOpt,[],LogDir);
+ Opts1 = Opts#opts{testspecs = [],
+ logdir = LogDir1,
+ include = AllInclude},
+ {Run,Skip} = ct_testspec:prepare_tests(TS, node()),
+ reformat_result(catch do_run(Run, Skip, Opts1, []));
{error,GCFReason} ->
exit(GCFReason)
end
end.
-
-get_data_for_node(#testspec{logdir=LogDirs,
- cover=CoverFs,
- config=Cfgs,
- event_handler=EvHs,
- include=Incl}, Node) ->
- LogDir = case lists:keysearch(Node,1,LogDirs) of
- {value,{Node,Dir}} -> Dir;
- false -> "."
+get_data_for_node(#testspec{label = Labels,
+ logdir = LogDirs,
+ cover = CoverFs,
+ config = Cfgs,
+ userconfig = UsrCfgs,
+ event_handler = EvHs,
+ ct_hooks = CTHooks,
+ include = Incl,
+ multiply_timetraps = MTs,
+ scale_timetraps = STs}, Node) ->
+ Label = proplists:get_value(Node, Labels),
+ LogDir = case proplists:get_value(Node, LogDirs) of
+ undefined -> ".";
+ Dir -> Dir
end,
- Cover = case lists:keysearch(Node,1,CoverFs) of
- {value,{Node,CovFile}} -> CovFile;
- false -> undef
- end,
- ConfigFiles = [F || {N,F} <- Cfgs, N==Node],
+ Cover = proplists:get_value(Node, CoverFs),
+ MT = proplists:get_value(Node, MTs),
+ ST = proplists:get_value(Node, STs),
+ ConfigFiles = [{?ct_config_txt,F} || {N,F} <- Cfgs, N==Node] ++
+ [CBF || {N,CBF} <- UsrCfgs, N==Node],
EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node],
+ FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node],
Include = [I || {N,I} <- Incl, N==Node],
- {LogDir,Cover,ConfigFiles,EvHandlers,Include}.
-
+ #opts{label = Label,
+ logdir = LogDir,
+ cover = Cover,
+ config = ConfigFiles,
+ event_handlers = EvHandlers,
+ ct_hooks = FiltCTHooks,
+ include = Include,
+ multiply_timetraps = MT,
+ scale_timetraps = ST}.
refresh_logs(LogDir) ->
{ok,Cwd} = file:get_cwd(),
@@ -851,11 +1069,19 @@ refresh_logs(LogDir) ->
end
end.
-which_logdir(".",Dir) ->
- Dir;
-which_logdir(Dir,_) ->
+which(logdir, undefined) ->
+ ".";
+which(logdir, Dir) ->
Dir.
-
+
+choose_val(undefined, V1) ->
+ V1;
+choose_val(V0, _V1) ->
+ V0.
+
+merge_vals(Vs) ->
+ lists:append(Vs).
+
listify([C|_]=Str) when is_integer(C) -> [Str];
listify(L) when is_list(L) -> L;
listify(E) -> [E].
@@ -869,22 +1095,45 @@ delistify(E) -> E.
%%% @equiv ct:run/3
run(TestDir, Suite, Cases) ->
install([]),
- do_run(tests(TestDir, Suite, Cases), []).
+ reformat_result(catch do_run(tests(TestDir, Suite, Cases), [])).
%%%-----------------------------------------------------------------
%%% @hidden
%%% @equiv ct:run/2
run(TestDir, Suite) when is_list(TestDir), is_integer(hd(TestDir)) ->
install([]),
- do_run(tests(TestDir, Suite), []).
+ reformat_result(catch do_run(tests(TestDir, Suite), [])).
%%%-----------------------------------------------------------------
%%% @hidden
%%% @equiv ct:run/1
run(TestDirs) ->
install([]),
- do_run(tests(TestDirs), []).
+ reformat_result(catch do_run(tests(TestDirs), [])).
+reformat_result({user_error,Reason}) ->
+ {error,Reason};
+reformat_result(Result) ->
+ Result.
+
+suite_to_test(Suite) ->
+ {filename:dirname(Suite),list_to_atom(filename:rootname(filename:basename(Suite)))}.
+
+groups_and_cases(Gs, Cs) when ((Gs == undefined) or (Gs == [])) and
+ ((Cs == undefined) or (Cs == [])) ->
+ [];
+groups_and_cases(Gs, Cs) when Gs == undefined ; Gs == [] ->
+ [ensure_atom(C) || C <- listify(Cs)];
+groups_and_cases(Gs, Cs) when Cs == undefined ; Cs == [] ->
+ [{ensure_atom(G),all} || G <- listify(Gs)];
+groups_and_cases(G, Cs) when is_atom(G) ->
+ [{G,[ensure_atom(C) || C <- listify(Cs)]}];
+groups_and_cases([G], Cs) ->
+ [{ensure_atom(G),[ensure_atom(C) || C <- listify(Cs)]}];
+groups_and_cases([_,_|_] , Cs) when Cs =/= [] ->
+ {error,multiple_groups_and_cases};
+groups_and_cases(_Gs, _Cs) ->
+ {error,incorrect_group_or_case_option}.
tests(TestDir, Suites, []) when is_list(TestDir), is_integer(hd(TestDir)) ->
[{?testdir(TestDir,Suites),ensure_atom(Suites),all}];
@@ -901,30 +1150,52 @@ tests(TestDir) when is_list(TestDir), is_integer(hd(TestDir)) ->
tests(TestDirs) when is_list(TestDirs), is_list(hd(TestDirs)) ->
[{?testdir(TestDir,all),all,all} || TestDir <- TestDirs].
-do_run(Tests, Opt) ->
- do_run(Tests, [], Opt, [], ".").
+do_run(Tests, Misc) when is_list(Misc) ->
+ do_run(Tests, Misc, ".").
-do_run(Tests, Opt, LogDir) ->
- do_run(Tests, [], Opt, [], LogDir).
+do_run(Tests, Misc, LogDir) when is_list(Misc) ->
+ Opts =
+ case proplists:get_value(step, Misc) of
+ undefined ->
+ #opts{};
+ StepOpts ->
+ #opts{step = StepOpts}
+ end,
+ Opts1 =
+ case proplists:get_value(cover, Misc) of
+ undefined ->
+ Opts;
+ CoverFile ->
+ Opts#opts{cover = CoverFile}
+ end,
+ do_run(Tests, [], Opts1#opts{logdir = LogDir}, []).
+
+do_run(Tests, Skip, Opts, Args) ->
+ #opts{label = Label, cover = Cover} = Opts,
+
+ %% label - used by ct_logs
+ TestLabel =
+ if Label == undefined -> undefined;
+ is_atom(Label) -> atom_to_list(Label);
+ is_list(Label) -> Label;
+ true -> undefined
+ end,
+ application:set_env(common_test, test_label, TestLabel),
-do_run(Tests, Skip, Opt, Args, LogDir) ->
case code:which(test_server) of
non_existing ->
exit({error,no_path_to_test_server});
_ ->
- Opt1 =
- case lists:keysearch(cover, 1, Opt) of
- {value,{_,CoverFile}} ->
- case ct_cover:get_spec(CoverFile) of
- {error,Reason} ->
- exit({error,Reason});
- Spec ->
- [{cover_spec,Spec} |
- lists:keydelete(cover, 1, Opt)]
- end;
- _ ->
- Opt
- end,
+ Opts1 = if Cover == undefined ->
+ Opts;
+ true ->
+ case ct_cover:get_spec(Cover) of
+ {error,Reason} ->
+ exit({error,Reason});
+ CoverSpec ->
+ Opts#opts{coverspec = CoverSpec}
+ end
+ end,
%% This env variable is used by test_server to determine
%% which framework it runs under.
case os:getenv("TEST_SERVER_FRAMEWORK") of
@@ -935,60 +1206,58 @@ do_run(Tests, Skip, Opt, Args, LogDir) ->
Other ->
erlang:display(list_to_atom("Note: TEST_SERVER_FRAMEWORK = " ++ Other))
end,
- case ct_util:start(LogDir) of
+ case ct_util:start(Opts#opts.logdir) of
{error,interactive_mode} ->
io:format("CT is started in interactive mode. "
"To exit this mode, run ct:stop_interactive().\n"
"To enter the interactive mode again, "
"run ct:start_interactive()\n\n",[]),
{error,interactive_mode};
-
_Pid ->
- %% save style sheet info
- case lists:keysearch(stylesheet, 1, Args) of
- {value,{_,SSFile}} ->
- ct_util:set_testdata({stylesheet,SSFile});
- _ ->
- ct_util:set_testdata({stylesheet,undefined})
- end,
-
- case lists:keysearch(silent_connections, 1, Args) of
- {value,{silent_connections,undefined}} ->
- ok;
- {value,{silent_connections,[]}} ->
+ %% save stylesheet info
+ ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}),
+ %% enable silent connections
+ case Opts#opts.silent_connections of
+ [] ->
Conns = ct_util:override_silence_all_connections(),
ct_logs:log("Silent connections", "~p", [Conns]);
- {value,{silent_connections,Cs}} ->
- Conns = lists:map(fun(S) when is_list(S) ->
- list_to_atom(S);
- (A) -> A
- end, Cs),
+ Conns when is_list(Conns) ->
ct_util:override_silence_connections(Conns),
ct_logs:log("Silent connections", "~p", [Conns]);
_ ->
ok
end,
- log_ts_names(Args),
+ log_ts_names(Opts1#opts.testspecs),
TestSuites = suite_tuples(Tests),
- {SuiteMakeErrors,AllMakeErrors} =
+ {_TestSuites1,SuiteMakeErrors,AllMakeErrors} =
case application:get_env(common_test, auto_compile) of
{ok,false} ->
- SuitesNotFound = verify_suites(TestSuites),
- {SuitesNotFound,SuitesNotFound};
+ {TestSuites1,SuitesNotFound} =
+ verify_suites(TestSuites),
+ {TestSuites1,SuitesNotFound,SuitesNotFound};
_ ->
{SuiteErrs,HelpErrs} = auto_compile(TestSuites),
- {SuiteErrs,SuiteErrs++HelpErrs}
+ {TestSuites,SuiteErrs,SuiteErrs++HelpErrs}
end,
case continue(AllMakeErrors) of
true ->
SavedErrors = save_make_errors(SuiteMakeErrors),
ct_repeat:log_loop_info(Args),
- {Tests1,Skip1} = final_tests(Tests,[],Skip,SavedErrors),
- R = do_run_test(Tests1, Skip1, Opt1),
- ct_util:stop(normal),
- R;
+
+ {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors),
+
+ R = (catch do_run_test(Tests1, Skip1, Opts1)),
+ case R of
+ {EType,_} = Error when EType == user_error ;
+ EType == error ->
+ ct_util:stop(clean),
+ exit(Error);
+ _ ->
+ ct_util:stop(normal),
+ R
+ end;
false ->
io:nl(),
ct_util:stop(clean),
@@ -1012,7 +1281,7 @@ auto_compile(TestSuites) ->
case application:get_env(common_test, include) of
{ok,UserInclDirs} when length(UserInclDirs) > 0 ->
io:format("Including the following directories:~n"),
- [begin io:format("~p~n",[UserInclDir]), {i,UserInclDir} end ||
+ [begin io:format("~p~n",[UserInclDir]), {i,UserInclDir} end ||
UserInclDir <- UserInclDirs];
_ ->
[]
@@ -1020,11 +1289,11 @@ auto_compile(TestSuites) ->
SuiteMakeErrors =
lists:flatmap(fun({TestDir,Suite} = TS) ->
case run_make(suites, TestDir, Suite, UserInclude) of
- {error,{make_failed,Bad}} ->
+ {error,{make_failed,Bad}} ->
[{TS,Bad}];
- {error,_} ->
+ {error,_} ->
[{TS,[filename:join(TestDir,"*_SUITE")]}];
- _ ->
+ _ ->
[]
end
end, TestSuites),
@@ -1048,39 +1317,63 @@ auto_compile(TestSuites) ->
true -> % already visited
{Done,Failed}
end
- end, {[],[]}, TestSuites),
+ end, {[],[]}, TestSuites),
{SuiteMakeErrors,lists:reverse(HelpMakeErrors)}.
%% verify that specified test suites exist (if auto compile is disabled)
verify_suites(TestSuites) ->
io:nl(),
- Verify =
- fun({Dir,Suite},NotFound) ->
+ Verify =
+ fun({Dir,Suite}=DS,{Found,NotFound}) ->
case locate_test_dir(Dir, Suite) of
{ok,TestDir} ->
if Suite == all ->
- NotFound;
+ {[DS|Found],NotFound};
true ->
- Beam = filename:join(TestDir, atom_to_list(Suite)++".beam"),
+ Beam = filename:join(TestDir,
+ atom_to_list(Suite)++".beam"),
case filelib:is_regular(Beam) of
- true ->
- NotFound;
- false ->
- Name = filename:join(TestDir, atom_to_list(Suite)),
- io:format("Suite ~w not found in directory ~s~n",
- [Suite,TestDir]),
- [{{Dir,Suite},[Name]} | NotFound]
+ true ->
+ {[DS|Found],NotFound};
+ false ->
+ case code:is_loaded(Suite) of
+ {file,SuiteFile} ->
+ %% test suite is already loaded and
+ %% since auto_compile == false,
+ %% let's assume the user has
+ %% loaded the beam file explicitly
+ ActualDir = filename:dirname(SuiteFile),
+ {[{ActualDir,Suite}|Found],NotFound};
+ false ->
+ Name =
+ filename:join(TestDir,
+ atom_to_list(Suite)),
+ io:format(user,
+ "Suite ~w not found"
+ "in directory ~s~n",
+ [Suite,TestDir]),
+ {Found,[{DS,[Name]}|NotFound]}
+ end
end
end;
{error,_Reason} ->
- io:format("Directory ~s is invalid~n", [Dir]),
- Name = filename:join(Dir, atom_to_list(Suite)),
- [{{Dir,Suite},[Name]} | NotFound]
+ case code:is_loaded(Suite) of
+ {file,SuiteFile} ->
+ %% test suite is already loaded and since
+ %% auto_compile == false, let's assume the
+ %% user has loaded the beam file explicitly
+ ActualDir = filename:dirname(SuiteFile),
+ {[{ActualDir,Suite}|Found],NotFound};
+ false ->
+ io:format(user, "Directory ~s is invalid~n", [Dir]),
+ Name = filename:join(Dir, atom_to_list(Suite)),
+ {Found,[{DS,[Name]}|NotFound]}
+ end
end
end,
- lists:reverse(lists:foldl(Verify, [], TestSuites)).
-
-
+ {ActualFound,Missing} = lists:foldl(Verify, {[],[]}, TestSuites),
+ {lists:reverse(ActualFound),lists:reverse(Missing)}.
+
save_make_errors([]) ->
[];
save_make_errors(Errors) ->
@@ -1096,7 +1389,7 @@ get_bad_suites([{{_TestDir,_Suite},Failed}|Errors], BadSuites) ->
get_bad_suites([], BadSuites) ->
BadSuites.
-
+
%%%-----------------------------------------------------------------
%%% @hidden
@@ -1107,7 +1400,7 @@ step(TestDir, Suite, Case) ->
%%%-----------------------------------------------------------------
%%% @hidden
%%% @equiv ct:step/4
-step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case),
+step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case),
Suite =/= all, Case =/= all ->
do_run([{TestDir,Suite,Case}], [{step,Opts}]).
@@ -1121,8 +1414,13 @@ suite_tuples([{TestDir,Suite,_} | Tests]) when is_atom(Suite) ->
suite_tuples([]) ->
[].
-final_tests([{TestDir,Suites,_}|Tests],
- Final, Skip, Bad) when is_list(Suites), is_atom(hd(Suites)) ->
+final_tests(Tests, Skip, Bad) ->
+ {Tests1,Skip1} = final_tests1(Tests, [], Skip, Bad),
+ Skip2 = final_skip(Skip1, []),
+ {Tests1,Skip2}.
+
+final_tests1([{TestDir,Suites,_}|Tests], Final, Skip, Bad) when
+ is_list(Suites), is_atom(hd(Suites)) ->
% Separate =
% fun(S,{DoSuite,Dont}) ->
% case lists:keymember({TestDir,S},1,Bad) of
@@ -1140,10 +1438,10 @@ final_tests([{TestDir,Suites,_}|Tests],
Skip1 = [{TD,S,"Make failed"} || {{TD,S},_} <- Bad, S1 <- Suites,
S == S1, TD == TestDir],
- Final1 = [{TestDir,S,all} || S <- Suites],
- final_tests(Tests, lists:reverse(Final1)++Final, Skip++Skip1, Bad);
+ Final1 = [{TestDir,S,all} || S <- Suites],
+ final_tests1(Tests, lists:reverse(Final1)++Final, Skip++Skip1, Bad);
-final_tests([{TestDir,all,all}|Tests], Final, Skip, Bad) ->
+final_tests1([{TestDir,all,all}|Tests], Final, Skip, Bad) ->
MissingSuites =
case lists:keysearch({TestDir,all}, 1, Bad) of
{value,{_,Failed}} ->
@@ -1153,27 +1451,59 @@ final_tests([{TestDir,all,all}|Tests], Final, Skip, Bad) ->
end,
Missing = [{TestDir,S,"Make failed"} || S <- MissingSuites],
Final1 = [{TestDir,all,all}|Final],
- final_tests(Tests, Final1, Skip++Missing, Bad);
+ final_tests1(Tests, Final1, Skip++Missing, Bad);
-final_tests([{TestDir,Suite,Cases}|Tests],
- Final, Skip, Bad) when Cases==[]; Cases==all ->
- final_tests([{TestDir,[Suite],all}|Tests], Final, Skip, Bad);
+final_tests1([{TestDir,Suite,Cases}|Tests], Final, Skip, Bad) when
+ Cases==[]; Cases==all ->
+ final_tests1([{TestDir,[Suite],all}|Tests], Final, Skip, Bad);
-final_tests([{TestDir,Suite,Cases}|Tests], Final, Skip, Bad) ->
+final_tests1([{TestDir,Suite,GrsOrCs}|Tests], Final, Skip, Bad) when
+ is_list(GrsOrCs) ->
case lists:keymember({TestDir,Suite}, 1, Bad) of
- false ->
- Do = {TestDir,Suite,Cases},
- final_tests(Tests, [Do|Final], Skip, Bad);
true ->
- Do = {TestDir,Suite,Cases},
- Skip1 = Skip ++ [{TestDir,Suite,Cases,"Make failed"}],
- final_tests(Tests, [Do|Final], Skip1, Bad)
+ Skip1 = Skip ++ [{TestDir,Suite,all,"Make failed"}],
+ final_tests1(Tests, [{TestDir,Suite,all}|Final], Skip1, Bad);
+ false ->
+ GrsOrCs1 =
+ lists:flatmap(
+ %% for now, only flat group defs are allowed as
+ %% start options and test spec terms
+ fun({all,all}) ->
+ ct_framework:make_all_conf(TestDir,
+ Suite, []);
+ ({skipped,Group,TCs}) ->
+ [ct_framework:make_conf(TestDir, Suite,
+ Group, [skipped], TCs)];
+ ({Group,TCs}) ->
+ [ct_framework:make_conf(TestDir, Suite,
+ Group, [], TCs)];
+ (TC) ->
+ [TC]
+ end, GrsOrCs),
+ Do = {TestDir,Suite,GrsOrCs1},
+ final_tests1(Tests, [Do|Final], Skip, Bad)
end;
-final_tests([], Final, Skip, _Bad) ->
+final_tests1([], Final, Skip, _Bad) ->
{lists:reverse(Final),Skip}.
-continue([]) ->
+final_skip([{TestDir,Suite,{all,all},Reason}|Skips], Final) ->
+ SkipConf = ct_framework:make_conf(TestDir, Suite, all, [], all),
+ Skip = {TestDir,Suite,SkipConf,Reason},
+ final_skip(Skips, [Skip|Final]);
+
+final_skip([{TestDir,Suite,{Group,TCs},Reason}|Skips], Final) ->
+ Conf = ct_framework:make_conf(TestDir, Suite, Group, [], TCs),
+ Skip = {TestDir,Suite,Conf,Reason},
+ final_skip(Skips, [Skip|Final]);
+
+final_skip([Skip|Skips], Final) ->
+ final_skip(Skips, [Skip|Final]);
+
+final_skip([], Final) ->
+ lists:reverse(Final).
+
+continue([]) ->
true;
continue(_MakeErrors) ->
io:nl(),
@@ -1214,7 +1544,7 @@ set_group_leader_same_as_shell() ->
false
end
end,
- case [P || P <- processes(), GS2or3(P),
+ case [P || P <- processes(), GS2or3(P),
true == lists:keymember(shell,1,element(2,process_info(P,dictionary)))] of
[GL|_] ->
group_leader(GL, self());
@@ -1238,29 +1568,29 @@ check_and_add([{TestDir0,M,_} | Tests], Added) ->
check_and_add([], _) ->
ok.
-do_run_test(Tests, Skip, Opt) ->
+do_run_test(Tests, Skip, Opts) ->
case check_and_add(Tests, []) of
ok ->
ct_util:set_testdata({stats,{0,0,{0,0}}}),
ct_util:set_testdata({cover,undefined}),
test_server_ctrl:start_link(local),
- case lists:keysearch(cover_spec, 1, Opt) of
- {value,{_,CovData={CovFile,
- CovNodes,
- _CovImport,
- CovExport,
- #cover{app = CovApp,
- level = CovLevel,
- excl_mods = CovExcl,
- incl_mods = CovIncl,
- cross = CovCross,
- src = _CovSrc}}}} ->
+ case Opts#opts.coverspec of
+ CovData={CovFile,
+ CovNodes,
+ _CovImport,
+ CovExport,
+ #cover{app = CovApp,
+ level = CovLevel,
+ excl_mods = CovExcl,
+ incl_mods = CovIncl,
+ cross = CovCross,
+ src = _CovSrc}} ->
ct_logs:log("COVER INFO","Using cover specification file: ~s~n"
"App: ~w~n"
"Cross cover: ~w~n"
"Including ~w modules~n"
"Excluding ~w modules",
- [CovFile,CovApp,CovCross,length(CovIncl),length(CovExcl)]),
+ [CovFile,CovApp,CovCross,length(CovIncl),length(CovExcl)]),
%% cover export file will be used for export and import
%% between tests so make sure it doesn't exist initially
@@ -1293,33 +1623,38 @@ do_run_test(Tests, Skip, Opt) ->
true;
_ ->
false
- end,
+ end,
+
%% let test_server expand the test tuples and count no of cases
{Suites,NoOfCases} = count_test_cases(Tests, Skip),
Suites1 = delete_dups(Suites),
NoOfTests = length(Tests),
NoOfSuites = length(Suites1),
- ct_util:warn_duplicates(Suites1),
+ ct_util:warn_duplicates(Suites1),
{ok,Cwd} = file:get_cwd(),
io:format("~nCWD set to: ~p~n", [Cwd]),
if NoOfCases == unknown ->
- io:format("~nTEST INFO: ~w test(s), ~w suite(s)~n~n",
+ io:format("~nTEST INFO: ~w test(s), ~w suite(s)~n~n",
[NoOfTests,NoOfSuites]),
- ct_logs:log("TEST INFO","~w test(s), ~w suite(s)",
+ ct_logs:log("TEST INFO","~w test(s), ~w suite(s)",
[NoOfTests,NoOfSuites]);
true ->
- io:format("~nTEST INFO: ~w test(s), ~w case(s) in ~w suite(s)~n~n",
+ io:format("~nTEST INFO: ~w test(s), ~w case(s) in ~w suite(s)~n~n",
[NoOfTests,NoOfCases,NoOfSuites]),
- ct_logs:log("TEST INFO","~w test(s), ~w case(s) in ~w suite(s)",
+ ct_logs:log("TEST INFO","~w test(s), ~w case(s) in ~w suite(s)",
[NoOfTests,NoOfCases,NoOfSuites])
end,
+
+ test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps),
+ test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps),
+
ct_event:notify(#event{name=start_info,
node=node(),
data={NoOfTests,NoOfSuites,NoOfCases}}),
- CleanUp = add_jobs(Tests, Skip, Opt, []),
+ CleanUp = add_jobs(Tests, Skip, Opts, []),
unlink(whereis(test_server_ctrl)),
- catch test_server_ctrl:wait_finish(),
- %% check if last testcase has left a "dead" trace window
+ catch test_server_ctrl:wait_finish(),
+ %% check if last testcase has left a "dead" trace window
%% behind, and if so, kill it
case ct_util:get_testdata(interpret) of
{_What,kill,{TCPid,AttPid}} ->
@@ -1327,8 +1662,8 @@ do_run_test(Tests, Skip, Opt) ->
_ ->
ok
end,
- lists:foreach(fun(Suite) ->
- maybe_cleanup_interpret(Suite, Opt)
+ lists:foreach(fun(Suite) ->
+ maybe_cleanup_interpret(Suite, Opts#opts.step)
end, CleanUp);
Error ->
Error
@@ -1344,21 +1679,32 @@ count_test_cases(Tests, Skip) ->
SendResult = fun(Me, Result) -> Me ! {no_of_cases,Result} end,
TSPid = test_server_ctrl:start_get_totals(SendResult),
Ref = erlang:monitor(process, TSPid),
- add_jobs(Tests, Skip, [], []),
- {Suites,NoOfCases} = count_test_cases1(length(Tests), 0, [], Ref),
- erlang:demonitor(Ref),
- test_server_ctrl:stop_get_totals(),
- {Suites,NoOfCases}.
+ add_jobs(Tests, Skip, #opts{}, []),
+ Counted = (catch count_test_cases1(length(Tests), 0, [], Ref)),
+ erlang:demonitor(Ref, [flush]),
+ case Counted of
+ {error,{test_server_died}} = Error ->
+ throw(Error);
+ {error,Reason} ->
+ unlink(whereis(test_server_ctrl)),
+ test_server_ctrl:stop(),
+ throw({user_error,Reason});
+ Result ->
+ test_server_ctrl:stop_get_totals(),
+ Result
+ end.
count_test_cases1(0, N, Suites, _) ->
{lists:flatten(Suites), N};
count_test_cases1(Jobs, N, Suites, Ref) ->
receive
- {no_of_cases,{Ss,N1}} ->
+ {_,{error,_Reason} = Error} ->
+ throw(Error);
+ {no_of_cases,{Ss,N1}} ->
count_test_cases1(Jobs-1, add_known(N,N1), [Ss|Suites], Ref);
- {'DOWN', Ref, _, _, _} ->
- {[],0}
- end.
+ {'DOWN', Ref, _, _, Info} ->
+ throw({error,{test_server_died,Info}})
+ end.
add_known(unknown, _) ->
unknown;
@@ -1367,72 +1713,109 @@ add_known(_, unknown) ->
add_known(N, N1) ->
N+N1.
-add_jobs([{TestDir,all,_}|Tests], Skip, Opt, CleanUp) ->
+add_jobs([{TestDir,all,_}|Tests], Skip, Opts, CleanUp) ->
Name = get_name(TestDir),
case catch test_server_ctrl:add_dir_with_skip(Name, TestDir,
skiplist(TestDir,Skip)) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
CleanUp;
_ ->
wait_for_idle(),
- add_jobs(Tests, Skip, Opt, CleanUp)
+ add_jobs(Tests, Skip, Opts, CleanUp)
end;
-add_jobs([{TestDir,[Suite],all}|Tests], Skip, Opt, CleanUp) when is_atom(Suite) ->
- add_jobs([{TestDir,Suite,all}|Tests], Skip, Opt, CleanUp);
-add_jobs([{TestDir,Suites,all}|Tests], Skip, Opt, CleanUp) when is_list(Suites) ->
+add_jobs([{TestDir,[Suite],all}|Tests], Skip, Opts, CleanUp) when is_atom(Suite) ->
+ add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp);
+add_jobs([{TestDir,Suites,all}|Tests], Skip, Opts, CleanUp) when is_list(Suites) ->
Name = get_name(TestDir) ++ ".suites",
case catch test_server_ctrl:add_module_with_skip(Name, Suites,
skiplist(TestDir,Skip)) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
CleanUp;
_ ->
wait_for_idle(),
- add_jobs(Tests, Skip, Opt, CleanUp)
+ add_jobs(Tests, Skip, Opts, CleanUp)
end;
-add_jobs([{TestDir,Suite,all}|Tests], Skip, Opt, CleanUp) ->
- case maybe_interpret(Suite, all, Opt) of
+add_jobs([{TestDir,Suite,all}|Tests], Skip, Opts, CleanUp) ->
+ case maybe_interpret(Suite, all, Opts) of
ok ->
Name = get_name(TestDir) ++ "." ++ atom_to_list(Suite),
case catch test_server_ctrl:add_module_with_skip(Name, [Suite],
skiplist(TestDir,Skip)) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
+ CleanUp;
+ _ ->
+ wait_for_idle(),
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
+ end;
+ Error ->
+ Error
+ end;
+
+%% group (= conf case in test_server)
+add_jobs([{TestDir,Suite,Confs}|Tests], Skip, Opts, CleanUp) when
+ element(1, hd(Confs)) == conf ->
+ Group = fun(Conf) -> proplists:get_value(name, element(2, Conf)) end,
+ TestCases = fun(Conf) -> element(4, Conf) end,
+ TCTestName = fun(all) -> "";
+ ([C]) when is_atom(C) -> "." ++ atom_to_list(C);
+ (Cs) when is_list(Cs) -> ".cases"
+ end,
+ GrTestName =
+ case Confs of
+ [Conf] ->
+ "." ++ atom_to_list(Group(Conf)) ++ TCTestName(TestCases(Conf));
+ _ ->
+ ".groups"
+ end,
+ TestName = get_name(TestDir) ++ "." ++ atom_to_list(Suite) ++ GrTestName,
+ case maybe_interpret(Suite, init_per_group, Opts) of
+ ok ->
+ case catch test_server_ctrl:add_conf_with_skip(TestName, Suite, Confs,
+ skiplist(TestDir,Skip)) of
+ {'EXIT',_} ->
CleanUp;
_ ->
wait_for_idle(),
- add_jobs(Tests, Skip, Opt, [Suite|CleanUp])
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
end;
Error ->
Error
end;
-add_jobs([{TestDir,Suite,[Case]}|Tests], Skip, Opt, CleanUp) when is_atom(Case) ->
- add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opt, CleanUp);
-add_jobs([{TestDir,Suite,Cases}|Tests], Skip, Opt, CleanUp) when is_list(Cases) ->
- case maybe_interpret(Suite, Cases, Opt) of
+
+%% test case
+add_jobs([{TestDir,Suite,[Case]}|Tests], Skip, Opts, CleanUp) when is_atom(Case) ->
+ add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opts, CleanUp);
+
+add_jobs([{TestDir,Suite,Cases}|Tests], Skip, Opts, CleanUp) when is_list(Cases) ->
+ Cases1 = lists:map(fun({GroupName,_}) when is_atom(GroupName) -> GroupName;
+ (Case) -> Case
+ end, Cases),
+ case maybe_interpret(Suite, Cases1, Opts) of
ok ->
Name = get_name(TestDir) ++ "." ++ atom_to_list(Suite) ++ ".cases",
- case catch test_server_ctrl:add_cases_with_skip(Name, Suite, Cases,
+ case catch test_server_ctrl:add_cases_with_skip(Name, Suite, Cases1,
skiplist(TestDir,Skip)) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
CleanUp;
_ ->
wait_for_idle(),
- add_jobs(Tests, Skip, Opt, [Suite|CleanUp])
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
end;
Error ->
Error
end;
-add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opt, CleanUp) when is_atom(Case) ->
- case maybe_interpret(Suite, Case, Opt) of
+add_jobs([{TestDir,Suite,Case}|Tests], Skip, Opts, CleanUp) when is_atom(Case) ->
+ case maybe_interpret(Suite, Case, Opts) of
ok ->
- Name = get_name(TestDir) ++ "." ++ atom_to_list(Suite) ++ "." ++
+ Name = get_name(TestDir) ++ "." ++ atom_to_list(Suite) ++ "." ++
atom_to_list(Case),
case catch test_server_ctrl:add_case_with_skip(Name, Suite, Case,
skiplist(TestDir,Skip)) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
CleanUp;
_ ->
wait_for_idle(),
- add_jobs(Tests, Skip, Opt, [Suite|CleanUp])
+ add_jobs(Tests, Skip, Opts, [Suite|CleanUp])
end;
Error ->
Error
@@ -1453,7 +1836,7 @@ wait_for_idle() ->
idle -> ok;
{'DOWN', Ref, _, _, _} -> error
end,
- erlang:demonitor(Ref),
+ erlang:demonitor(Ref, [flush]),
ct_util:update_last_run_index(),
Result
end.
@@ -1482,7 +1865,7 @@ get_name(Dir) ->
end,
Base = filename:basename(TestDir),
case filename:basename(filename:dirname(TestDir)) of
- "" ->
+ "" ->
Base;
TopDir ->
TopDir ++ "." ++ Base
@@ -1513,15 +1896,15 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
{i,CtInclude},
{i,XmerlInclude},
debug_info],
- Result =
+ Result =
if Mod == all ; Targets == helpmods ->
case (catch ct_make:all([noexec|ErlFlags])) of
- {'EXIT',_} = Failure ->
+ {'EXIT',_} = Failure ->
Failure;
MakeInfo ->
FileTest = fun(F, suites) -> is_suite(F);
- (F, helpmods) -> not is_suite(F);
- (_, _) -> true end,
+ (F, helpmods) -> not is_suite(F)
+ end,
Files = lists:flatmap(fun({F,out_of_date}) ->
case FileTest(F, Targets) of
true -> [F];
@@ -1535,7 +1918,7 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
true ->
(catch ct_make:files([Mod], [load|ErlFlags]))
end,
-
+
ok = file:set_cwd(Cwd),
%% send finished_make notification
ct_event:notify(#event{name=finished_make,
@@ -1549,7 +1932,7 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
{error,{make_crashed,TestDir,Reason}};
{error,ModInfo} ->
io:format("{error,make_failed}\n", []),
- Bad = [filename:join(TestDir, M) || {M,R} <- ModInfo,
+ Bad = [filename:join(TestDir, M) || {M,R} <- ModInfo,
R == error],
{error,{make_failed,Bad}}
end;
@@ -1561,8 +1944,8 @@ run_make(Targets, TestDir0, Mod, UserInclude) ->
get_dir(App, Dir) ->
filename:join(code:lib_dir(App), Dir).
-maybe_interpret(Suite, Cases, [{step,StepOpts}]) ->
- %% if other suite has run before this one, check if last testcase
+maybe_interpret(Suite, Cases, #opts{step = StepOpts}) when StepOpts =/= undefined ->
+ %% if other suite has run before this one, check if last testcase
%% has left a "dead" trace window behind, and if so, kill it
case ct_util:get_testdata(interpret) of
{_What,kill,{TCPid,AttPid}} ->
@@ -1605,7 +1988,7 @@ maybe_interpret2(Suite, Cases, StepOpts) ->
WinOp = case lists:member(keep_inactive, ensure_atom(StepOpts)) of
true -> no_kill;
false -> kill
- end,
+ end,
ct_util:set_testdata({interpret,{{Suite,Cases},WinOp,
{undefined,undefined}}}),
ok.
@@ -1621,37 +2004,44 @@ set_break_on_config(Suite, StepOpts) ->
ok
end.
-maybe_cleanup_interpret(Suite, [{step,_}]) ->
- i:iq(Suite);
-maybe_cleanup_interpret(_, _) ->
- ok.
+maybe_cleanup_interpret(_, undefined) ->
+ ok;
+maybe_cleanup_interpret(Suite, _) ->
+ i:iq(Suite).
+
+log_ts_names([]) ->
+ ok;
+log_ts_names(Specs) ->
+ List = lists:map(fun(Name) ->
+ Name ++ " "
+ end, Specs),
+ ct_logs:log("Test Specification file(s)", "~s",
+ [lists:flatten(List)]).
-log_ts_names(Args) ->
- case lists:keysearch(spec, 1, Args) of
- {value,{_,Specs}} ->
- List = lists:map(fun(Name) ->
- Name ++ " "
- end, Specs),
- ct_logs:log("Test Specification file(s)", "~s",
- [lists:flatten(List)]);
- _ ->
- ok
- end.
-
merge_arguments(Args) ->
merge_arguments(Args, []).
merge_arguments([LogDir={logdir,_}|Args], Merged) ->
merge_arguments(Args, handle_arg(replace, LogDir, Merged));
+
merge_arguments([CoverFile={cover,_}|Args], Merged) ->
merge_arguments(Args, handle_arg(replace, CoverFile, Merged));
-merge_arguments([Arg={_,_}|Args], Merged) ->
+
+merge_arguments([{'case',TC}|Args], Merged) ->
+ merge_arguments(Args, handle_arg(merge, {testcase,TC}, Merged));
+
+merge_arguments([Arg|Args], Merged) ->
merge_arguments(Args, handle_arg(merge, Arg, Merged));
+
merge_arguments([], Merged) ->
Merged.
handle_arg(replace, {Key,Elems}, [{Key,_}|Merged]) ->
[{Key,Elems}|Merged];
+handle_arg(merge, {event_handler_init,Elems}, [{event_handler_init,PrevElems}|Merged]) ->
+ [{event_handler_init,PrevElems++["add"|Elems]}|Merged];
+handle_arg(merge, {userconfig,Elems}, [{userconfig,PrevElems}|Merged]) ->
+ [{userconfig,PrevElems++["add"|Elems]}|Merged];
handle_arg(merge, {Key,Elems}, [{Key,PrevElems}|Merged]) ->
[{Key,PrevElems++Elems}|Merged];
handle_arg(Op, Arg, [Other|Merged]) ->
@@ -1659,6 +2049,205 @@ handle_arg(Op, Arg, [Other|Merged]) ->
handle_arg(_,Arg,[]) ->
[Arg].
+get_start_opt(Key, IfExists, Args) ->
+ get_start_opt(Key, IfExists, undefined, Args).
+
+get_start_opt(Key, IfExists, IfNotExists, Args) ->
+ case lists:keysearch(Key, 1, Args) of
+ {value,{Key,Val}} when is_function(IfExists) ->
+ IfExists(Val);
+ {value,{Key,Val}} when IfExists == value ->
+ Val;
+ {value,{Key,_Val}} ->
+ IfExists;
+ _ ->
+ IfNotExists
+ end.
+
+ct_hooks_args2opts(Args) ->
+ ct_hooks_args2opts(
+ proplists:get_value(ct_hooks, Args, []),[]).
+
+ct_hooks_args2opts([CTH,Arg,"and"| Rest],Acc) ->
+ ct_hooks_args2opts(Rest,[{list_to_atom(CTH),
+ parse_cth_args(Arg)}|Acc]);
+ct_hooks_args2opts([CTH], Acc) ->
+ ct_hooks_args2opts([CTH,"and"],Acc);
+ct_hooks_args2opts([CTH, "and" | Rest], Acc) ->
+ ct_hooks_args2opts(Rest,[list_to_atom(CTH)|Acc]);
+ct_hooks_args2opts([CTH, Args], Acc) ->
+ ct_hooks_args2opts([CTH, Args, "and"],Acc);
+ct_hooks_args2opts([],Acc) ->
+ lists:reverse(Acc).
+
+parse_cth_args(String) ->
+ try
+ true = io_lib:printable_list(String),
+ {ok,Toks,_} = erl_scan:string(String++"."),
+ {ok, Args} = erl_parse:parse_term(Toks),
+ Args
+ catch _:_ ->
+ String
+ end.
+
+
+event_handler_args2opts(Args) ->
+ case proplists:get_value(event_handler, Args) of
+ undefined ->
+ event_handler_args2opts([], Args);
+ EHs ->
+ event_handler_args2opts([{list_to_atom(EH),[]} || EH <- EHs], Args)
+ end.
+event_handler_args2opts(Default, Args) ->
+ case proplists:get_value(event_handler_init, Args) of
+ undefined ->
+ Default;
+ EHs ->
+ event_handler_init_args2opts(EHs)
+ end.
+event_handler_init_args2opts([EH, Arg, "and" | EHs]) ->
+ [{list_to_atom(EH),lists:flatten(io_lib:format("~s",[Arg]))} |
+ event_handler_init_args2opts(EHs)];
+event_handler_init_args2opts([EH, Arg]) ->
+ [{list_to_atom(EH),lists:flatten(io_lib:format("~s",[Arg]))}];
+event_handler_init_args2opts([]) ->
+ [].
+
+%% This function reads pa and pz arguments, converts dirs from relative
+%% to absolute, and re-inserts them in the code path. The order of the
+%% dirs in the code path remain the same. Note however that since this
+%% function is only used for arguments "pre run_test erl_args", the order
+%% relative dirs "post run_test erl_args" is not kept!
+rel_to_abs(CtArgs) ->
+ {PA,PZ} = get_pa_pz(CtArgs, [], []),
+ io:format(user, "~n", []),
+ [begin
+ code:del_path(filename:basename(D)),
+ Abs = filename:absname(D),
+ code:add_pathz(Abs),
+ if D /= Abs ->
+ io:format(user, "Converting ~p to ~p and re-inserting "
+ "with add_pathz/1~n",
+ [D, Abs]);
+ true ->
+ ok
+ end
+ end || D <- PZ],
+ [begin
+ code:del_path(filename:basename(D)),
+ Abs = filename:absname(D),
+ code:add_patha(Abs),
+ if D /= Abs ->
+ io:format(user, "Converting ~p to ~p and re-inserting "
+ "with add_patha/1~n",
+ [D, Abs]);
+ true ->ok
+ end
+ end || D <- PA],
+ io:format(user, "~n", []).
+
+get_pa_pz([{pa,Dirs} | Args], PA, PZ) ->
+ get_pa_pz(Args, PA ++ Dirs, PZ);
+get_pa_pz([{pz,Dirs} | Args], PA, PZ) ->
+ get_pa_pz(Args, PA, PZ ++ Dirs);
+get_pa_pz([_ | Args], PA, PZ) ->
+ get_pa_pz(Args, PA, PZ);
+get_pa_pz([], PA, PZ) ->
+ {PA,PZ}.
+
+%% This function translates ct:run_test/1 start options
+%% to ct_run start arguments (on the init arguments format) -
+%% this is useful mainly for testing the ct_run start functions.
+opts2args(EnvStartOpts) ->
+ lists:flatmap(fun({config,CfgFiles}) ->
+ [{ct_config,[CfgFiles]}];
+ ({userconfig,{CBM,CfgStr=[X|_]}}) when is_integer(X) ->
+ [{userconfig,[atom_to_list(CBM),CfgStr]}];
+ ({userconfig,{CBM,CfgStrs}}) when is_list(CfgStrs) ->
+ [{userconfig,[atom_to_list(CBM) | CfgStrs]}];
+ ({userconfig,UserCfg}) when is_list(UserCfg) ->
+ Strs =
+ lists:map(fun({CBM,CfgStr=[X|_]}) when is_integer(X) ->
+ [atom_to_list(CBM),CfgStr,"and"];
+ ({CBM,CfgStrs}) when is_list(CfgStrs) ->
+ [atom_to_list(CBM) | CfgStrs] ++ ["and"]
+ end, UserCfg),
+ [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
+ [{userconfig,lists:reverse(StrsR)}];
+ ({testcase,Case}) when is_atom(Case) ->
+ [{'case',[atom_to_list(Case)]}];
+ ({testcase,Cases}) ->
+ [{'case',[atom_to_list(C) || C <- Cases]}];
+ ({'case',Cases}) ->
+ [{'case',[atom_to_list(C) || C <- Cases]}];
+ ({allow_user_terms,true}) ->
+ [{allow_user_terms,[]}];
+ ({allow_user_terms,false}) ->
+ [];
+ ({auto_compile,false}) ->
+ [{no_auto_compile,[]}];
+ ({auto_compile,true}) ->
+ [];
+ ({scale_timetraps,true}) ->
+ [{scale_timetraps,[]}];
+ ({scale_timetraps,false}) ->
+ [];
+ ({force_stop,true}) ->
+ [{force_stop,[]}];
+ ({force_stop,false}) ->
+ [];
+ ({decrypt,{key,Key}}) ->
+ [{ct_decrypt_key,[Key]}];
+ ({decrypt,{file,File}}) ->
+ [{ct_decrypt_file,[File]}];
+ ({basic_html,true}) ->
+ ({basic_html,[]});
+ ({basic_html,false}) ->
+ [];
+ ({event_handler,EH}) when is_atom(EH) ->
+ [{event_handler,[atom_to_list(EH)]}];
+ ({event_handler,EHs}) when is_list(EHs) ->
+ [{event_handler,[atom_to_list(EH) || EH <- EHs]}];
+ ({event_handler,{EH,Arg}}) when is_atom(EH) ->
+ ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
+ [{event_handler_init,[atom_to_list(EH),ArgStr]}];
+ ({event_handler,{EHs,Arg}}) when is_list(EHs) ->
+ ArgStr = lists:flatten(io_lib:format("~p", [Arg])),
+ Strs = lists:map(fun(EH) ->
+ [atom_to_list(EH),ArgStr,"and"]
+ end, EHs),
+ [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)),
+ [{event_handler_init,lists:reverse(StrsR)}];
+ ({ct_hooks,[]}) ->
+ [];
+ ({ct_hooks,CTHs}) when is_list(CTHs) ->
+ io:format(user,"ct_hooks: ~p",[CTHs]),
+ Strs = lists:flatmap(
+ fun({CTH,Arg}) ->
+ [atom_to_list(CTH),
+ lists:flatten(
+ io_lib:format("~p",[Arg])),
+ "and"];
+ (CTH) when is_atom(CTH) ->
+ [atom_to_list(CTH),"and"]
+ end,CTHs),
+ [_LastAnd|StrsR] = lists:reverse(Strs),
+ io:format(user,"return: ~p",[lists:reverse(StrsR)]),
+ [{ct_hooks,lists:reverse(StrsR)}];
+ ({Opt,As=[A|_]}) when is_atom(A) ->
+ [{Opt,[atom_to_list(Atom) || Atom <- As]}];
+ ({Opt,Strs=[S|_]}) when is_list(S) ->
+ [{Opt,Strs}];
+ ({Opt,A}) when is_atom(A) ->
+ [{Opt,[atom_to_list(A)]}];
+ ({Opt,I}) when is_integer(I) ->
+ [{Opt,[integer_to_list(I)]}];
+ ({Opt,S}) when is_list(S) ->
+ [{Opt,[S]}];
+ (Opt) ->
+ Opt
+ end, EnvStartOpts).
+
locate_test_dir(Dir, Suite) ->
TestDir = case ct_util:is_test_dir(Dir) of
true -> Dir;
@@ -1723,18 +2312,18 @@ start_trace(Args) ->
case file:consult(TraceSpec) of
{ok,Terms} ->
case catch do_trace(Terms) of
- ok ->
+ ok ->
true;
{_,Error} ->
io:format("Warning! Tracing not started. Reason: ~p~n~n",
[Error]),
false
- end;
+ end;
{_,Error} ->
io:format("Warning! Tracing not started. Reason: ~p~n~n",
[Error]),
false
- end;
+ end;
false ->
false
end.
@@ -1743,64 +2332,32 @@ do_trace(Terms) ->
dbg:tracer(),
dbg:p(self(), [sos,call]),
lists:foreach(fun({m,M}) ->
- case dbg:tpl(M,[{'_',[],[{return_trace}]}]) of
+ case dbg:tpl(M,x) of
+ {error,What} -> exit({error,{tracing_failed,What}});
+ _ -> ok
+ end;
+ ({me,M}) ->
+ case dbg:tp(M,[{'_',[],[{exception_trace},
+ {message,{caller}}]}]) of
{error,What} -> exit({error,{tracing_failed,What}});
_ -> ok
- end;
+ end;
({f,M,F}) ->
- case dbg:tpl(M,F,[{'_',[],[{return_trace}]}]) of
+ case dbg:tpl(M,F,[{'_',[],[{exception_trace},
+ {message,{caller}}]}]) of
{error,What} -> exit({error,{tracing_failed,What}});
_ -> ok
- end;
+ end;
(Huh) ->
exit({error,{unrecognized_trace_term,Huh}})
end, Terms),
ok.
-
+
stop_trace(true) ->
dbg:stop_clear();
stop_trace(false) ->
ok.
-preload() ->
- io:format("~nLoading Common Test and Test Server modules...~n~n"),
- preload_mod([ct_logs,
- ct_make,
- ct_telnet,
- ct,
- ct_master,
- ct_testspec,
- ct_cover,
- ct_master_event,
- ct_util,
- ct_event,
- ct_master_logs,
- ct_framework,
- teln,
- ct_ftp,
- ct_rpc,
- unix_telnet,
- ct_gen_conn,
- ct_line,
- ct_snmp,
- test_server_sup,
- test_server,
- test_server_ctrl,
- test_server_h,
- test_server_line,
- test_server_node]).
-
-preload_mod([M|Ms]) ->
- case code:is_loaded(M) of
- false ->
- {module,M} = code:load_file(M),
- preload_mod(Ms);
- _ ->
- ok
- end;
-preload_mod([]) ->
- ok.
-
ensure_atom(Atom) when is_atom(Atom) ->
Atom;
ensure_atom(String) when is_list(String), is_integer(hd(String)) ->
@@ -1809,4 +2366,3 @@ ensure_atom(List) when is_list(List) ->
[ensure_atom(Item) || Item <- List];
ensure_atom(Other) ->
Other.
-
diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl
new file mode 100644
index 0000000000..aa3413fa89
--- /dev/null
+++ b/lib/common_test/src/ct_slave.erl
@@ -0,0 +1,439 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%% @doc Common Test Framework functions for starting and stopping nodes for
+%%% Large Scale Testing.
+%%%
+%%% <p>This module exports functions which are used by the Common Test Master
+%%% to start and stop "slave" nodes. It is the default callback module for the
+%%% <code>{init, node_start}</code> term of the Test Specification.</p>
+
+%%----------------------------------------------------------------------
+%% File : ct_slave.erl
+%% Description : CT module for starting nodes for large-scale testing.
+%%
+%% Created : 7 April 2010
+%%----------------------------------------------------------------------
+-module(ct_slave).
+
+-export([start/1, start/2, start/3, stop/1, stop/2]).
+
+-export([slave_started/2, slave_ready/2, monitor_master/1]).
+
+-record(options, {username, password, boot_timeout, init_timeout,
+ startup_timeout, startup_functions, monitor_master,
+ kill_if_fail, erl_flags}).
+
+%%%-----------------------------------------------------------------
+%%% @spec start(Node) -> Result
+%%% Node = atom()
+%%% Result = {ok, NodeName} |
+%%% {error, already_started, NodeName} |
+%%% {error, started_not_connected, NodeName} |
+%%% {error, boot_timeout, NodeName} |
+%%% {error, init_timeout, NodeName} |
+%%% {error, startup_timeout, NodeName} |
+%%% {error, not_alive, NodeName}
+%%% NodeName = atom()
+%%% @doc Starts an Erlang node with name <code>Node</code> on the local host.
+%%% @see start/3
+start(Node) ->
+ start(gethostname(), Node).
+
+%%%-----------------------------------------------------------------
+%%% @spec start(Host, Node) -> Result
+%%% Node = atom()
+%%% Host = atom()
+%%% Result = {ok, NodeName} |
+%%% {error, already_started, NodeName} |
+%%% {error, started_not_connected, NodeName} |
+%%% {error, boot_timeout, NodeName} |
+%%% {error, init_timeout, NodeName} |
+%%% {error, startup_timeout, NodeName} |
+%%% {error, not_alive, NodeName}
+%%% NodeName = atom()
+%%% @doc Starts an Erlang node with name <code>Node</code> on host
+%%% <code>Host</code> with the default options.
+%%% @see start/3
+start(Host, Node) ->
+ start(Host, Node, []).
+
+%%%-----------------------------------------------------------------
+%%% @spec start(Host, Node, Opts) -> Result
+%%% Node = atom()
+%%% Host = atom()
+%%% Opts = [OptTuples]
+%%% OptTuples = {username, Username} |
+%%% {password, Password} |
+%%% {boot_timeout, BootTimeout} | {init_timeout, InitTimeout} |
+%%% {startup_timeout, StartupTimeout} |
+%%% {startup_functions, StartupFunctions} |
+%%% {monitor_master, Monitor} |
+%%% {kill_if_fail, KillIfFail} |
+%%% {erl_flags, ErlangFlags}
+%%% Username = string()
+%%% Password = string()
+%%% BootTimeout = integer()
+%%% InitTimeout = integer()
+%%% StartupTimeout = integer()
+%%% StartupFunctions = [StartupFunctionSpec]
+%%% StartupFunctionSpec = {Module, Function, Arguments}
+%%% Module = atom()
+%%% Function = atom()
+%%% Arguments = [term]
+%%% Monitor = bool()
+%%% KillIfFail = bool()
+%%% ErlangFlags = string()
+%%% Result = {ok, NodeName} | {error, already_started, NodeName} |
+%%% {error, started_not_connected, NodeName} |
+%%% {error, boot_timeout, NodeName} |
+%%% {error, init_timeout, NodeName} |
+%%% {error, startup_timeout, NodeName} |
+%%% {error, not_alive, NodeName}
+%%% NodeName = atom()
+%%% @doc Starts an Erlang node with name <code>Node</code> on host
+%%% <code>Host</code> as specified by the combination of options in
+%%% <code>Opts</code>.
+%%%
+%%% <p>Options <code>Username</code> and <code>Password</code> will be used
+%%% to log in onto the remote host <code>Host</code>.
+%%% Username, if omitted, defaults to the current user name,
+%%% and password is empty by default.</p>
+%%%
+%%% <p>A list of functions specified in the <code>Startup</code> option will be
+%%% executed after startup of the node. Note that all used modules should be
+%%% present in the code path on the <code>Host</code>.</p>
+%%%
+%%% <p>The timeouts are applied as follows:
+%%% <list>
+%%% <item>
+%%% <code>BootTimeout</code> - time to start the Erlang node, in seconds.
+%%% Defaults to 3 seconds. If node does not become pingable within this time,
+%%% the result <code>{error, boot_timeout, NodeName}</code> is returned;
+%%% </item>
+%%% <item>
+%%% <code>InitTimeout</code> - time to wait for the node until it calls the
+%%% internal callback function informing master about successfull startup.
+%%% Defaults to one second.
+%%% In case of timed out message the result
+%%% <code>{error, init_timeout, NodeName}</code> is returned;
+%%% </item>
+%%% <item>
+%%% <code>StartupTimeout</code> - time to wait intil the node finishes to run
+%%% the <code>StartupFunctions</code>. Defaults to one second.
+%%% If this timeout occurs, the result
+%%% <code>{error, startup_timeout, NodeName}</code> is returned.
+%%% </item>
+%%% </list></p>
+%%%
+%%% <p>Option <code>monitor_master</code> specifies, if the slave node should be
+%%% stopped in case of master node stop. Defaults to false.</p>
+%%%
+%%% <p>Option <code>kill_if_fail</code> specifies, if the slave node should be
+%%% killed in case of a timeout during initialization or startup.
+%%% Defaults to true. Note that node also may be still alive it the boot
+%%% timeout occurred, but it will not be killed in this case.</p>
+%%%
+%%% <p>Option <code>erlang_flags</code> specifies, which flags will be added
+%%% to the parameters of the <code>erl</code> executable.</p>
+%%%
+%%% <p>Special return values are:
+%%% <list>
+%%% <item><code>{error, already_started, NodeName}</code> - if the node with
+%%% the given name is already started on a given host;</item>
+%%% <item><code>{error, started_not_connected, NodeName}</code> - if node is
+%%% started, but not connected to the master node.</item>
+%%% <item><code>{error, not_alive, NodeName}</code> - if node on which the
+%%% <code>ct_slave:start/3</code> is called, is not alive. Note that
+%%% <code>NodeName</code> is the name of current node in this case.</item>
+%%% </list></p>
+%%%
+start(Host, Node, Options) ->
+ ENode = enodename(Host, Node),
+ case erlang:is_alive() of
+ false->
+ {error, not_alive, node()};
+ true->
+ case is_started(ENode) of
+ false->
+ OptionsRec = fetch_options(Options),
+ do_start(Host, Node, OptionsRec);
+ {true, not_connected}->
+ {error, started_not_connected, ENode};
+ {true, connected}->
+ {error, already_started, ENode}
+ end
+ end.
+
+%%% @spec stop(Node) -> Result
+%%% Node = atom()
+%%% Result = {ok, NodeName} |
+%%% {error, not_started, NodeName} |
+%%% {error, not_connected, NodeName} |
+%%% {error, stop_timeout, NodeName}
+%%% NodeName = atom()
+%%% @doc Stops the running Erlang node with name <code>Node</code> on
+%%% the localhost.
+stop(Node) ->
+ stop(gethostname(), Node).
+
+%%% @spec stop(Host, Node) -> Result
+%%% Host = atom()
+%%% Node = atom()
+%%% Result = {ok, NodeName} |
+%%% {error, not_started, NodeName} |
+%%% {error, not_connected, NodeName} |
+%%% {error, stop_timeout, NodeName}
+%%% NodeName = atom()
+%%% @doc Stops the running Erlang node with name <code>Node</code> on
+%%% host <code>Host</code>.
+stop(Host, Node) ->
+ ENode = enodename(Host, Node),
+ case is_started(ENode) of
+ {true, connected}->
+ do_stop(ENode);
+ {true, not_connected}->
+ {error, not_connected, ENode};
+ false->
+ {error, not_started, ENode}
+ end.
+
+%%% fetch an option value from the tagged tuple list with default
+get_option_value(Key, OptionList, Default) ->
+ case lists:keyfind(Key, 1, OptionList) of
+ false->
+ Default;
+ {Key, Value}->
+ Value
+ end.
+
+%%% convert option list to the option record, fill all defaults
+fetch_options(Options) ->
+ UserName = get_option_value(username, Options, []),
+ Password = get_option_value(password, Options, []),
+ BootTimeout = get_option_value(boot_timeout, Options, 3),
+ InitTimeout = get_option_value(init_timeout, Options, 1),
+ StartupTimeout = get_option_value(startup_timeout, Options, 1),
+ StartupFunctions = get_option_value(startup_functions, Options, []),
+ Monitor = get_option_value(monitor_master, Options, false),
+ KillIfFail = get_option_value(kill_if_fail, Options, true),
+ ErlFlags = get_option_value(erl_flags, Options, []),
+ #options{username=UserName, password=Password,
+ boot_timeout=BootTimeout, init_timeout=InitTimeout,
+ startup_timeout=StartupTimeout, startup_functions=StartupFunctions,
+ monitor_master=Monitor, kill_if_fail=KillIfFail, erl_flags=ErlFlags}.
+
+% send a message when slave node is started
+% @hidden
+slave_started(ENode, MasterPid) ->
+ MasterPid ! {node_started, ENode},
+ ok.
+
+% send a message when slave node has finished startup
+% @hidden
+slave_ready(ENode, MasterPid) ->
+ MasterPid ! {node_ready, ENode},
+ ok.
+
+% start monitoring of the master node
+% @hidden
+monitor_master(MasterNode) ->
+ spawn(fun() -> monitor_master_int(MasterNode) end).
+
+% code of the masterdeath-waiter process
+monitor_master_int(MasterNode) ->
+ erlang:monitor_node(MasterNode, true),
+ receive
+ {nodedown, MasterNode}->
+ init:stop()
+ end.
+
+% check if node is listed in the nodes()
+is_connected(ENode) ->
+ [N||N<-nodes(), N==ENode] == [ENode].
+
+% check if node is alive (ping and disconnect if pingable)
+is_started(ENode) ->
+ case is_connected(ENode) of
+ true->
+ {true, connected};
+ false->
+ case net_adm:ping(ENode) of
+ pang->
+ false;
+ pong->
+ erlang:disconnect_node(ENode),
+ {true, not_connected}
+ end
+ end.
+
+% make a Erlang node name from name and hostname
+enodename(Host, Node) ->
+ list_to_atom(atom_to_list(Node)++"@"++atom_to_list(Host)).
+
+% performs actual start of the "slave" node
+do_start(Host, Node, Options) ->
+ ENode = enodename(Host, Node),
+ Functions =
+ lists:concat([[{ct_slave, slave_started, [ENode, self()]}],
+ Options#options.startup_functions,
+ [{ct_slave, slave_ready, [ENode, self()]}]]),
+ Functions2 = if
+ Options#options.monitor_master->
+ [{ct_slave, monitor_master, [node()]}|Functions];
+ true->
+ Functions
+ end,
+ MasterHost = gethostname(),
+ if
+ MasterHost == Host ->
+ spawn_local_node(Node, Options);
+ true->
+ spawn_remote_node(Host, Node, Options)
+ end,
+ BootTimeout = Options#options.boot_timeout,
+ InitTimeout = Options#options.init_timeout,
+ StartupTimeout = Options#options.startup_timeout,
+ Result = case wait_for_node_alive(ENode, BootTimeout) of
+ pong->
+ call_functions(ENode, Functions2),
+ receive
+ {node_started, ENode}->
+ receive
+ {node_ready, ENode}->
+ {ok, ENode}
+ after StartupTimeout*1000->
+ {error, startup_timeout, ENode}
+ end
+ after InitTimeout*1000 ->
+ {error, init_timeout, ENode}
+ end;
+ pang->
+ {error, boot_timeout, ENode}
+ end,
+ case Result of
+ {ok, ENode}->
+ ok;
+ {error, Timeout, ENode}
+ when ((Timeout==init_timeout) or (Timeout==startup_timeout)) and
+ Options#options.kill_if_fail->
+ do_stop(ENode);
+ _-> ok
+ end,
+ Result.
+
+% are we using fully qualified hostnames
+long_or_short() ->
+ case net_kernel:longnames() of
+ true->
+ " -name ";
+ false->
+ " -sname "
+ end.
+
+% get the localhost's name, depending on the using name policy
+gethostname() ->
+ Hostname = case net_kernel:longnames() of
+ true->
+ net_adm:localhost();
+ _->
+ {ok, Name}=inet:gethostname(),
+ Name
+ end,
+ list_to_atom(Hostname).
+
+% get cmd for starting Erlang
+get_cmd(Node, Flags) ->
+ Cookie = erlang:get_cookie(),
+ "erl -detached -noinput -setcookie "++ atom_to_list(Cookie) ++
+ long_or_short() ++ atom_to_list(Node) ++ " " ++ Flags.
+
+% spawn node locally
+spawn_local_node(Node, Options) ->
+ ErlFlags = Options#options.erl_flags,
+ Cmd = get_cmd(Node, ErlFlags),
+ open_port({spawn, Cmd}, [stream]).
+
+% start crypto and ssh if not yet started
+check_for_ssh_running() ->
+ case application:get_application(crypto) of
+ undefined->
+ application:start(crypto),
+ case application:get_application(ssh) of
+ undefined->
+ application:start(ssh);
+ {ok, ssh}->
+ ok
+ end;
+ {ok, crypto}->
+ ok
+ end.
+
+% spawn node remotely
+spawn_remote_node(Host, Node, Options) ->
+ Username = Options#options.username,
+ Password = Options#options.password,
+ ErlFlags = Options#options.erl_flags,
+ SSHOptions = case {Username, Password} of
+ {[], []}->
+ [];
+ {_, []}->
+ [{user, Username}];
+ {_, _}->
+ [{user, Username}, {password, Password}]
+ end ++ [{silently_accept_hosts, true}],
+ check_for_ssh_running(),
+ {ok, SSHConnRef} = ssh:connect(atom_to_list(Host), 22, SSHOptions),
+ {ok, SSHChannelId} = ssh_connection:session_channel(SSHConnRef, infinity),
+ ssh_connection:exec(SSHConnRef, SSHChannelId, get_cmd(Node, ErlFlags), infinity).
+
+% call functions on a remote Erlang node
+call_functions(_Node, []) ->
+ ok;
+call_functions(Node, [{M, F, A}|Functions]) ->
+ rpc:call(Node, M, F, A),
+ call_functions(Node, Functions).
+
+% wait N seconds until node is pingable
+wait_for_node_alive(_Node, 0) ->
+ pang;
+wait_for_node_alive(Node, N) ->
+ timer:sleep(1000),
+ case net_adm:ping(Node) of
+ pong->
+ pong;
+ pang->
+ wait_for_node_alive(Node, N-1)
+ end.
+
+% call init:stop on a remote node
+do_stop(ENode) ->
+ spawn(ENode, init, stop, []),
+ wait_for_node_dead(ENode, 5).
+
+% wait N seconds until node is disconnected
+wait_for_node_dead(Node, 0) ->
+ {error, stop_timeout, Node};
+wait_for_node_dead(Node, N) ->
+ timer:sleep(1000),
+ case lists:member(Node, nodes()) of
+ true->
+ wait_for_node_dead(Node, N-1);
+ false->
+ {ok, Node}
+ end.
diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl
index 7ff88ad7d3..8fe63e8ed1 100644
--- a/lib/common_test/src/ct_snmp.erl
+++ b/lib/common_test/src/ct_snmp.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -332,7 +332,7 @@ set_info(Config) ->
register_users(MgrAgentConfName, Users) ->
{snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users, Users}),
- ct_util:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+ ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
setup_users(Users).
%%% @spec register_agents(MgrAgentConfName, ManagedAgents) -> ok | {error, Reason}
@@ -347,7 +347,7 @@ register_agents(MgrAgentConfName, ManagedAgents) ->
{snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
{managed_agents, ManagedAgents}),
- ct_util:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+ ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
setup_managed_agents(ManagedAgents).
%%% @spec register_usm_users(MgrAgentConfName, UsmUsers) -> ok | {error, Reason}
@@ -361,7 +361,7 @@ register_agents(MgrAgentConfName, ManagedAgents) ->
register_usm_users(MgrAgentConfName, UsmUsers) ->
{snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {usm_users, UsmUsers}),
- ct_util:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+ ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
setup_usm_users(UsmUsers, EngineID).
@@ -376,7 +376,7 @@ unregister_users(MgrAgentConfName) ->
ct:get_config({MgrAgentConfName, users})),
{snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
NewSnmpVals = lists:keyreplace(users, 1, SnmpVals, {users, []}),
- ct_util:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+ ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
takedown_users(Users).
%%% @spec unregister_agents(MgrAgentConfName) -> ok | {error, Reason}
@@ -393,7 +393,7 @@ unregister_agents(MgrAgentConfName) ->
{snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
NewSnmpVals = lists:keyreplace(managed_agents, 1, SnmpVals,
{managed_agents, []}),
- ct_util:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+ ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
takedown_managed_agents(ManagedAgents).
@@ -409,7 +409,7 @@ update_usm_users(MgrAgentConfName, UsmUsers) ->
{snmp, SnmpVals} = ct:get_config(MgrAgentConfName),
NewSnmpVals = lists:keyreplace(usm_users, 1, SnmpVals,
{usm_users, UsmUsers}),
- ct_util:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
+ ct_config:update_config(MgrAgentConfName, {snmp, NewSnmpVals}),
EngineID = ct:get_config({MgrAgentConfName, engine_id}, ?ENGINE_ID),
do_update_usm_users(UsmUsers, EngineID).
diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl
index 1a12c5e343..d703b39ac5 100644
--- a/lib/common_test/src/ct_telnet_client.erl
+++ b/lib/common_test/src/ct_telnet_client.erl
@@ -35,8 +35,6 @@
-export([open/1, open/2, open/3, open/4, close/1]).
-export([send_data/2, get_data/1]).
--define(DBG, false).
-
-define(TELNET_PORT, 23).
-define(OPEN_TIMEOUT,10000).
-define(IDLE_TIMEOUT,10000).
@@ -287,35 +285,38 @@ get_subcmd([?SE | Rest], Acc) ->
get_subcmd([Opt | Rest], Acc) ->
get_subcmd(Rest, [Opt | Acc]).
-
+-ifdef(debug).
dbg(_Str,_Args) ->
- if ?DBG -> io:format(_Str,_Args);
- true -> ok
+ io:format(_Str,_Args).
+
+cmd_dbg(_Cmd) ->
+ case _Cmd of
+ [?IAC|Cmd1] ->
+ cmd_dbg(Cmd1);
+ [Ctrl|Opts] ->
+ CtrlStr =
+ case Ctrl of
+ ?DO -> "DO";
+ ?DONT -> "DONT";
+ ?WILL -> "WILL";
+ ?WONT -> "WONT";
+ ?NOP -> "NOP";
+ _ -> "CMD"
+ end,
+ Opts1 =
+ case Opts of
+ [Opt] -> Opt;
+ _ -> Opts
+ end,
+ io:format("~s(~w): ~w\n", [CtrlStr,Ctrl,Opts1]);
+ Any ->
+ io:format("Unexpected in cmd_dbg:~n~w~n",[Any])
end.
+-else.
+dbg(_Str,_Args) ->
+ ok.
+
cmd_dbg(_Cmd) ->
- if ?DBG ->
- case _Cmd of
- [?IAC|Cmd1] ->
- cmd_dbg(Cmd1);
- [Ctrl|Opts] ->
- CtrlStr =
- case Ctrl of
- ?DO -> "DO";
- ?DONT -> "DONT";
- ?WILL -> "WILL";
- ?WONT -> "WONT";
- ?NOP -> "NOP";
- _ -> "CMD"
- end,
- Opts1 =
- case Opts of
- [Opt] -> Opt;
- _ -> Opts
- end,
- io:format("~s(~w): ~w\n", [CtrlStr,Ctrl,Opts1]);
- Any ->
- io:format("Unexpected in cmd_dbg:~n~w~n",[Any])
- end;
- true -> ok
- end.
+ ok.
+-endif.
diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl
index 4378ec5a52..d845358bb2 100644
--- a/lib/common_test/src/ct_testspec.erl
+++ b/lib/common_test/src/ct_testspec.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,7 @@
%% %CopyrightEnd%
%%
-%%% @doc Common Test Framework functions handlig test specifikations.
+%%% @doc Common Test Framework functions handling test specifications.
%%%
%%% <p>This module exports functions that are used within CT to
%%% scan and parse test specifikations.</p>
@@ -68,7 +68,8 @@ prepare_tests(TestSpec) when is_record(TestSpec,testspec) ->
%% Create initial list of {Node,{Run,Skip}} tuples
NodeList = lists:map(fun(N) -> {N,{[],[]}} end, list_nodes(TestSpec)),
%% Get all Run tests sorted per node basis.
- NodeList1 = run_per_node(Run,NodeList),
+ NodeList1 = run_per_node(Run,NodeList,
+ TestSpec#testspec.merge_tests),
%% Get all Skip entries sorted per node basis.
NodeList2 = skip_per_node(Skip,NodeList1),
%% Change representation.
@@ -89,11 +90,17 @@ prepare_tests(TestSpec) when is_record(TestSpec,testspec) ->
%% run_per_node/2 takes the Run list as input and returns a list
%% of {Node,RunPerNode,[]} tuples where the tests have been sorted
%% on a per node basis.
-run_per_node([{{Node,Dir},Test}|Ts],Result) ->
+run_per_node([{{Node,Dir},Test}|Ts],Result, MergeTests) ->
{value,{Node,{Run,Skip}}} = lists:keysearch(Node,1,Result),
- Run1 = merge_tests(Dir,Test,Run),
- run_per_node(Ts,insert_in_order({Node,{Run1,Skip}},Result));
-run_per_node([],Result) ->
+ Run1 = case MergeTests of
+ false ->
+ append({Dir, Test}, Run);
+ true ->
+ merge_tests(Dir,Test,Run)
+ end,
+ run_per_node(Ts,insert_in_order({Node,{Run1,Skip}},Result),
+ MergeTests);
+run_per_node([],Result,_) ->
Result.
merge_tests(Dir,Test={all,_},TestDirs) ->
@@ -185,7 +192,15 @@ prepare_cases(Node,Dir,Suite,Cases) ->
{[{{Node,Dir},{Suite,all}}],SkipAll};
Skipped ->
%% note: this adds a test even if only skip is specified
- PrepC = lists:foldr(fun({C,{skip,_Cmt}},Acc) ->
+ PrepC = lists:foldr(fun({{G,Cs},{skip,_Cmt}}, Acc) when
+ is_atom(G) ->
+ case lists:keymember(G, 1, Cases) of
+ true ->
+ Acc;
+ false ->
+ [{skipped,G,Cs}|Acc]
+ end;
+ ({C,{skip,_Cmt}},Acc) ->
case lists:member(C,Cases) of
true ->
Acc;
@@ -194,7 +209,7 @@ prepare_cases(Node,Dir,Suite,Cases) ->
end;
(C,Acc) -> [C|Acc]
end, [], Cases),
- {{{Node,Dir},{Suite,PrepC}},Skipped}
+ {{{Node,Dir},{Suite,PrepC}},Skipped}
end.
get_skipped_suites(Node,Dir,Suites) ->
@@ -210,7 +225,7 @@ get_skipped_cases(Node,Dir,Suite,Cases) ->
case lists:keysearch(all,1,Cases) of
{value,{all,{skip,Cmt}}} ->
[{{Node,Dir},{Suite,Cmt}}];
- false ->
+ _ ->
get_skipped_cases1(Node,Dir,Suite,Cases)
end.
@@ -270,34 +285,11 @@ collect_tests(Terms,TestSpec,Relaxed) ->
put(relaxed,Relaxed),
TestSpec1 = get_global(Terms,TestSpec),
TestSpec2 = get_all_nodes(Terms,TestSpec1),
- case catch evaluate(Terms,TestSpec2) of
- {error,{Node,{M,F,A},Reason}} ->
- io:format("Error! Common Test failed to evaluate ~w:~w/~w on ~w. "
- "Reason: ~p~n~n", [M,F,A,Node,Reason]);
- _ -> ok
- end,
- add_tests(Terms,TestSpec2).
-
-evaluate([{eval,NodeRef,{M,F,Args}}|Ts],Spec) ->
- Node = ref2node(NodeRef,Spec#testspec.nodes),
- case rpc:call(Node,M,F,Args) of
- {badrpc,Reason} ->
- throw({error,{Node,{M,F,length(Args)},Reason}});
- _ ->
- ok
- end,
- evaluate(Ts,Spec);
-evaluate([{eval,{M,F,Args}}|Ts],Spec) ->
- case catch apply(M,F,Args) of
- {'EXIT',Reason} ->
- throw({error,{node(),{M,F,length(Args)},Reason}});
- _ ->
- ok
- end,
- evaluate(Ts,Spec);
-evaluate([],_Spec) ->
- ok.
+ {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2),
+ add_tests(Terms2,TestSpec3).
+get_global([{merge_tests, Bool} | Ts], Spec) ->
+ get_global(Ts,Spec#testspec{ merge_tests = Bool });
get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) ->
get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]});
get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) ->
@@ -305,6 +297,26 @@ get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) ->
get_global([_|Ts],Spec) -> get_global(Ts,Spec);
get_global([],Spec) -> Spec.
+get_absfile(Callback, FullName,#testspec{spec_dir=SpecDir}) ->
+ % we need to temporary switch to new cwd here, because
+ % otherwise config files cannot be found
+ {ok, OldWd} = file:get_cwd(),
+ ok = file:set_cwd(SpecDir),
+ R = Callback:check_parameter(FullName),
+ ok = file:set_cwd(OldWd),
+ case R of
+ {ok, {file, FullName}}->
+ File = filename:basename(FullName),
+ Dir = get_absname(filename:dirname(FullName),SpecDir),
+ filename:join(Dir,File);
+ {ok, {config, FullName}}->
+ FullName;
+ {error, {nofile, FullName}}->
+ FullName;
+ {error, {wrong_config, FullName}}->
+ FullName
+ end.
+
get_absfile(FullName,#testspec{spec_dir=SpecDir}) ->
File = filename:basename(FullName),
Dir = get_absname(filename:dirname(FullName),SpecDir),
@@ -353,6 +365,66 @@ get_all_nodes([_|Ts],Spec) ->
get_all_nodes([],Spec) ->
Spec.
+filter_init_terms([{init, InitOptions}|Ts], NewTerms, Spec)->
+ filter_init_terms([{init, list_nodes(Spec), InitOptions}|Ts], NewTerms, Spec);
+filter_init_terms([{init, NodeRef, InitOptions}|Ts], NewTerms, Spec)
+ when is_atom(NodeRef)->
+ filter_init_terms([{init, [NodeRef], InitOptions}|Ts], NewTerms, Spec);
+filter_init_terms([{init, NodeRefs, InitOption}|Ts], NewTerms, Spec) when is_tuple(InitOption) ->
+ filter_init_terms([{init, NodeRefs, [InitOption]}|Ts], NewTerms, Spec);
+filter_init_terms([{init, [NodeRef|NodeRefs], InitOptions}|Ts], NewTerms, Spec=#testspec{init=InitData})->
+ NodeStartOptions = case lists:keyfind(node_start, 1, InitOptions) of
+ {node_start, NSOptions}->
+ case lists:keyfind(callback_module, 1, NSOptions) of
+ {callback_module, _Callback}->
+ NSOptions;
+ false->
+ [{callback_module, ct_slave}|NSOptions]
+ end;
+ false->
+ []
+ end,
+ EvalTerms = case lists:keyfind(eval, 1, InitOptions) of
+ {eval, MFA} when is_tuple(MFA)->
+ [MFA];
+ {eval, MFAs} when is_list(MFAs)->
+ MFAs;
+ false->
+ []
+ end,
+ Node = ref2node(NodeRef,Spec#testspec.nodes),
+ InitData2 = add_option({node_start, NodeStartOptions}, Node, InitData, true),
+ InitData3 = add_option({eval, EvalTerms}, Node, InitData2, false),
+ filter_init_terms([{init, NodeRefs, InitOptions}|Ts], NewTerms, Spec#testspec{init=InitData3});
+filter_init_terms([{init, [], _}|Ts], NewTerms, Spec)->
+ filter_init_terms(Ts, NewTerms, Spec);
+filter_init_terms([Term|Ts], NewTerms, Spec)->
+ filter_init_terms(Ts, [Term|NewTerms], Spec);
+filter_init_terms([], NewTerms, Spec)->
+ {lists:reverse(NewTerms), Spec}.
+
+add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)->
+ OldOptions = case lists:keyfind(Node, 1, List) of
+ {Node, Options}->
+ Options;
+ false->
+ []
+ end,
+ NewOption = case lists:keyfind(Key, 1, OldOptions) of
+ {Key, OldOption} when WarnIfExists, OldOption/=[]->
+ io:format("There is an option ~w=~w already defined for node ~p, skipping new ~w~n",
+ [Key, OldOption, Node, Value]),
+ OldOption;
+ {Key, OldOption}->
+ OldOption ++ Value;
+ false->
+ Value
+ end,
+ lists:keystore(Node, 1, List,
+ {Node, lists:keystore(Key, 1, OldOptions, {Key, NewOption})});
+add_option({Key, Value}, Node, List, WarnIfExists)->
+ add_option({Key, [Value]}, Node, List, WarnIfExists).
+
save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
NodeRefs1 =
lists:foldr(fun(all_nodes,NR) ->
@@ -375,6 +447,15 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) ->
list_nodes(#testspec{nodes=NodeRefs}) ->
lists:map(fun({_Ref,Node}) -> Node end, NodeRefs).
+
+
+%% ---------------------------------------------------------
+%% / \
+%% | When adding tests, remember to update valid_terms/0 also! |
+%% \ /
+%% ---------------------------------------------------------
+
+
%% Associate a "global" logdir with all nodes
%% except those with specific logdir, e.g:
%% ["/tmp/logdir",{ct1@finwe,"/tmp/logdir2"}]
@@ -400,6 +481,24 @@ add_tests([{logdir,Node,Dir}|Ts],Spec) ->
add_tests([{logdir,Dir}|Ts],Spec) ->
add_tests([{logdir,all_nodes,Dir}|Ts],Spec);
+%% --- label ---
+add_tests([{label,all_nodes,Lbl}|Ts],Spec) ->
+ Labels = Spec#testspec.label,
+ Tests = [{label,N,Lbl} || N <- list_nodes(Spec),
+ lists:keymember(ref2node(N,Spec#testspec.nodes),
+ 1,Labels) == false],
+ add_tests(Tests++Ts,Spec);
+add_tests([{label,Nodes,Lbl}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,label,[Lbl],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{label,Node,Lbl}|Ts],Spec) ->
+ Labels = Spec#testspec.label,
+ Labels1 = [{ref2node(Node,Spec#testspec.nodes),Lbl} |
+ lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Labels)],
+ add_tests(Ts,Spec#testspec{label=Labels1});
+add_tests([{label,Lbl}|Ts],Spec) ->
+ add_tests([{label,all_nodes,Lbl}|Ts],Spec);
+
%% --- cover ---
add_tests([{cover,all_nodes,File}|Ts],Spec) ->
Tests = lists:map(fun(N) -> {cover,N,File} end, list_nodes(Spec)),
@@ -415,6 +514,36 @@ add_tests([{cover,Node,File}|Ts],Spec) ->
add_tests([{cover,File}|Ts],Spec) ->
add_tests([{cover,all_nodes,File}|Ts],Spec);
+%% --- multiply_timetraps ---
+add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec) ->
+ Tests = lists:map(fun(N) -> {multiply_timetraps,N,MT} end, list_nodes(Spec)),
+ add_tests(Tests++Ts,Spec);
+add_tests([{multiply_timetraps,Nodes,MT}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,multiply_timetraps,[MT],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{multiply_timetraps,Node,MT}|Ts],Spec) ->
+ MTs = Spec#testspec.multiply_timetraps,
+ MTs1 = [{ref2node(Node,Spec#testspec.nodes),MT} |
+ lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,MTs)],
+ add_tests(Ts,Spec#testspec{multiply_timetraps=MTs1});
+add_tests([{multiply_timetraps,MT}|Ts],Spec) ->
+ add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec);
+
+%% --- scale_timetraps ---
+add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec) ->
+ Tests = lists:map(fun(N) -> {scale_timetraps,N,ST} end, list_nodes(Spec)),
+ add_tests(Tests++Ts,Spec);
+add_tests([{scale_timetraps,Nodes,ST}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,scale_timetraps,[ST],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{scale_timetraps,Node,ST}|Ts],Spec) ->
+ STs = Spec#testspec.scale_timetraps,
+ STs1 = [{ref2node(Node,Spec#testspec.nodes),ST} |
+ lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,STs)],
+ add_tests(Ts,Spec#testspec{scale_timetraps=STs1});
+add_tests([{scale_timetraps,ST}|Ts],Spec) ->
+ add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec);
+
%% --- config ---
add_tests([{config,all_nodes,Files}|Ts],Spec) ->
Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)),
@@ -434,6 +563,27 @@ add_tests([{config,Node,F}|Ts],Spec) ->
add_tests([{config,Files}|Ts],Spec) ->
add_tests([{config,all_nodes,Files}|Ts],Spec);
+
+%% --- userconfig ---
+add_tests([{userconfig,all_nodes,CBF}|Ts],Spec) ->
+ Tests = lists:map(fun(N) -> {userconfig,N,CBF} end, list_nodes(Spec)),
+ add_tests(Tests++Ts,Spec);
+add_tests([{userconfig,Nodes,CBF}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,userconfig,[CBF],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{userconfig,Node,[{Callback, Config}|CBF]}|Ts],Spec) ->
+ Cfgs = Spec#testspec.userconfig,
+ Node1 = ref2node(Node,Spec#testspec.nodes),
+ add_tests([{userconfig,Node,CBF}|Ts],
+ Spec#testspec{userconfig=[{Node1,{Callback,
+ get_absfile(Callback, Config ,Spec)}}|Cfgs]});
+add_tests([{userconfig,_Node,[]}|Ts],Spec) ->
+ add_tests(Ts,Spec);
+add_tests([{userconfig,Node,CBF}|Ts],Spec) ->
+ add_tests([{userconfig,Node,[CBF]}|Ts],Spec);
+add_tests([{userconfig,CBF}|Ts],Spec) ->
+ add_tests([{userconfig,all_nodes,CBF}|Ts],Spec);
+
%% --- event_handler ---
add_tests([{event_handler,all_nodes,Hs}|Ts],Spec) ->
Tests = lists:map(fun(N) -> {event_handler,N,Hs,[]} end, list_nodes(Spec)),
@@ -482,6 +632,20 @@ add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) ->
Node1 = ref2node(Node,Spec#testspec.nodes),
add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]});
+%% --- ct_hooks --
+add_tests([{ct_hooks, all_nodes, Hooks} | Ts], Spec) ->
+ Tests = [{ct_hooks,N,Hooks} || N <- list_nodes(Spec)],
+ add_tests(Tests ++ Ts, Spec);
+add_tests([{ct_hooks, Node, [Hook|Hooks]}|Ts], Spec) ->
+ SuiteCbs = Spec#testspec.ct_hooks,
+ Node1 = ref2node(Node,Spec#testspec.nodes),
+ add_tests([{ct_hooks, Node, Hooks} | Ts],
+ Spec#testspec{ct_hooks = [{Node1,Hook} | SuiteCbs]});
+add_tests([{ct_hooks, _Node, []}|Ts], Spec) ->
+ add_tests(Ts, Spec);
+add_tests([{ct_hooks, Hooks}|Ts], Spec) ->
+ add_tests([{ct_hooks, all_nodes, Hooks}|Ts], Spec);
+
%% --- include ---
add_tests([{include,all_nodes,InclDirs}|Ts],Spec) ->
Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)),
@@ -513,7 +677,41 @@ add_tests([{suites,Node,Dir,Ss}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_suites(ref2node(Node,Spec#testspec.nodes),
ref2dir(Dir,Spec#testspec.alias),
- Ss,Tests),
+ Ss,Tests, Spec#testspec.merge_tests),
+ add_tests(Ts,Spec#testspec{tests=Tests1});
+
+%% --- groups ---
+%% Later make it possible to specify group execution properties
+%% that will override thse in the suite. Also make it possible
+%% create dynamic groups in specification, i.e. to group test cases
+%% by means of groups defined only in the test specification.
+add_tests([{groups,all_nodes,Dir,Suite,Gs}|Ts],Spec) ->
+ add_tests([{groups,list_nodes(Spec),Dir,Suite,Gs}|Ts],Spec);
+add_tests([{groups,all_nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
+ add_tests([{groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs}}|Ts],Spec);
+add_tests([{groups,Dir,Suite,Gs}|Ts],Spec) ->
+ add_tests([{groups,all_nodes,Dir,Suite,Gs}|Ts],Spec);
+add_tests([{groups,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
+ add_tests([{groups,all_nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec);
+add_tests([{groups,Nodes,Dir,Suite,Gs}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,groups,[Dir,Suite,Gs],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{groups,Node,Dir,Suite,Gs}|Ts],Spec) ->
+ Tests = Spec#testspec.tests,
+ Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes),
+ ref2dir(Dir,Spec#testspec.alias),
+ Suite,Gs,all,Tests,
+ Spec#testspec.merge_tests),
+ add_tests(Ts,Spec#testspec{tests=Tests1});
+add_tests([{groups,Node,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) ->
+ Tests = Spec#testspec.tests,
+ Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes),
+ ref2dir(Dir,Spec#testspec.alias),
+ Suite,Gs,TCs,Tests,
+ Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
%% --- cases ---
@@ -528,7 +726,7 @@ add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = insert_cases(ref2node(Node,Spec#testspec.nodes),
ref2dir(Dir,Spec#testspec.alias),
- Suite,Cs,Tests),
+ Suite,Cs,Tests, Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
%% --- skip_suites ---
@@ -543,7 +741,38 @@ add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_suites(ref2node(Node,Spec#testspec.nodes),
ref2dir(Dir,Spec#testspec.alias),
- Ss,Cmt,Tests),
+ Ss,Cmt,Tests,
+ Spec#testspec.merge_tests),
+ add_tests(Ts,Spec#testspec{tests=Tests1});
+
+%% --- skip_groups ---
+add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
+ add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,Cmt}|Ts],Spec);
+add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
+ add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec);
+add_tests([{skip_groups,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
+ add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec);
+add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
+ add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec);
+add_tests([{skip_groups,Nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,Cmt],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) when is_list(Nodes) ->
+ Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,Spec#testspec.nodes),
+ add_tests(Ts1,Spec);
+add_tests([{skip_groups,Node,Dir,Suite,Gs,Cmt}|Ts],Spec) ->
+ Tests = Spec#testspec.tests,
+ Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes),
+ ref2dir(Dir,Spec#testspec.alias),
+ Suite,Gs,all,Cmt,Tests,
+ Spec#testspec.merge_tests),
+ add_tests(Ts,Spec#testspec{tests=Tests1});
+add_tests([{skip_groups,Node,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) ->
+ Tests = Spec#testspec.tests,
+ Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes),
+ ref2dir(Dir,Spec#testspec.alias),
+ Suite,Gs,TCs,Cmt,Tests,
+ Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
%% --- skip_cases ---
@@ -558,7 +787,7 @@ add_tests([{skip_cases,Node,Dir,Suite,Cs,Cmt}|Ts],Spec) ->
Tests = Spec#testspec.tests,
Tests1 = skip_cases(ref2node(Node,Spec#testspec.nodes),
ref2dir(Dir,Spec#testspec.alias),
- Suite,Cs,Cmt,Tests),
+ Suite,Cs,Cmt,Tests,Spec#testspec.merge_tests),
add_tests(Ts,Spec#testspec{tests=Tests1});
%% --- handled/errors ---
@@ -568,6 +797,9 @@ add_tests([{alias,_,_}|Ts],Spec) -> % handled
add_tests([{node,_,_}|Ts],Spec) -> % handled
add_tests(Ts,Spec);
+add_tests([{merge_tests, _} | Ts], Spec) -> % handled
+ add_tests(Ts,Spec);
+
%% check if it's a CT term that has bad format or if the user seems to
%% have added something of his/her own, which we'll let pass if relaxed
%% mode is enabled.
@@ -614,18 +846,77 @@ separate([],_,_,_) ->
%% Representation:
-%% {{Node,Dir},[{Suite1,[case11,case12,...]},{Suite2,[case21,case22,...]},...]}
-%% {{Node,Dir},[{Suite1,{skip,Cmt}},{Suite2,[{case21,{skip,Cmt}},case22,...]},...]}
-
-insert_suites(Node,Dir,[S|Ss],Tests) ->
- Tests1 = insert_cases(Node,Dir,S,all,Tests),
- insert_suites(Node,Dir,Ss,Tests1);
-insert_suites(_Node,_Dir,[],Tests) ->
+%% {{Node,Dir},[{Suite1,[GrOrCase11,GrOrCase12,...]},
+%% {Suite2,[GrOrCase21,GrOrCase22,...]},...]}
+%% {{Node,Dir},[{Suite1,{skip,Cmt}},
+%% {Suite2,[{GrOrCase21,{skip,Cmt}},GrOrCase22,...]},...]}
+%% GrOrCase = {GroupName,[Case1,Case2,...]} | Case
+
+insert_suites(Node,Dir,[S|Ss],Tests, MergeTests) ->
+ Tests1 = insert_cases(Node,Dir,S,all,Tests,MergeTests),
+ insert_suites(Node,Dir,Ss,Tests1,MergeTests);
+insert_suites(_Node,_Dir,[],Tests,_MergeTests) ->
Tests;
-insert_suites(Node,Dir,S,Tests) ->
- insert_suites(Node,Dir,[S],Tests).
+insert_suites(Node,Dir,S,Tests,MergeTests) ->
+ insert_suites(Node,Dir,[S],Tests,MergeTests).
+
+insert_groups(Node,Dir,Suite,Group,Cases,Tests,MergeTests)
+ when is_atom(Group) ->
+ insert_groups(Node,Dir,Suite,[Group],Cases,Tests,MergeTests);
+insert_groups(Node,Dir,Suite,Groups,Cases,Tests,false) when
+ ((Cases == all) or is_list(Cases)) and is_list(Groups) ->
+ Groups1 = [{Gr,Cases} || Gr <- Groups],
+ append({{Node,Dir},[{Suite,Groups1}]},Tests);
+insert_groups(Node,Dir,Suite,Groups,Cases,Tests,true) when
+ ((Cases == all) or is_list(Cases)) and is_list(Groups) ->
+ case lists:keysearch({Node,Dir},1,Tests) of
+ {value,{{Node,Dir},[{all,_}]}} ->
+ Tests;
+ {value,{{Node,Dir},Suites0}} ->
+ Suites1 = insert_groups1(Suite,
+ [{Gr,Cases} || Gr <- Groups],
+ Suites0),
+ insert_in_order({{Node,Dir},Suites1},Tests);
+ false ->
+ Groups1 = [{Gr,Cases} || Gr <- Groups],
+ insert_in_order({{Node,Dir},[{Suite,Groups1}]},Tests)
+ end;
+insert_groups(Node,Dir,Suite,Groups,Case,Tests, MergeTests)
+ when is_atom(Case) ->
+ Cases = if Case == all -> all; true -> [Case] end,
+ insert_groups(Node,Dir,Suite,Groups,Cases,Tests, MergeTests).
+
+insert_groups1(_Suite,_Groups,all) ->
+ all;
+insert_groups1(Suite,Groups,Suites0) ->
+ case lists:keysearch(Suite,1,Suites0) of
+ {value,{Suite,all}} ->
+ Suites0;
+ {value,{Suite,GrAndCases0}} ->
+ GrAndCases = insert_groups2(Groups,GrAndCases0),
+ insert_in_order({Suite,GrAndCases},Suites0);
+ false ->
+ insert_in_order({Suite,Groups},Suites0)
+ end.
-insert_cases(Node,Dir,Suite,Cases,Tests) when is_list(Cases) ->
+insert_groups2(_Groups,all) ->
+ all;
+insert_groups2([Group={GrName,Cases}|Groups],GrAndCases) ->
+ case lists:keysearch(GrName,1,GrAndCases) of
+ {value,{GrName,all}} ->
+ GrAndCases;
+ {value,{GrName,Cases0}} ->
+ Cases1 = insert_in_order(Cases,Cases0),
+ insert_groups2(Groups,insert_in_order({GrName,Cases1},GrAndCases));
+ false ->
+ insert_groups2(Groups,insert_in_order(Group,GrAndCases))
+ end;
+insert_groups2([],GrAndCases) ->
+ GrAndCases.
+
+insert_cases(Node,Dir,Suite,Cases,Tests,false) when is_list(Cases) ->
+ append({{Node,Dir},[{Suite,Cases}]},Tests);
+insert_cases(Node,Dir,Suite,Cases,Tests,true) when is_list(Cases) ->
case lists:keysearch({Node,Dir},1,Tests) of
{value,{{Node,Dir},[{all,_}]}} ->
Tests;
@@ -635,8 +926,8 @@ insert_cases(Node,Dir,Suite,Cases,Tests) when is_list(Cases) ->
false ->
insert_in_order({{Node,Dir},[{Suite,Cases}]},Tests)
end;
-insert_cases(Node,Dir,Suite,Case,Tests) when is_atom(Case) ->
- insert_cases(Node,Dir,Suite,[Case],Tests).
+insert_cases(Node,Dir,Suite,Case,Tests,MergeTests) when is_atom(Case) ->
+ insert_cases(Node,Dir,Suite,[Case],Tests,MergeTests).
insert_cases1(_Suite,_Cases,all) ->
all;
@@ -651,15 +942,59 @@ insert_cases1(Suite,Cases,Suites0) ->
insert_in_order({Suite,Cases},Suites0)
end.
-skip_suites(Node,Dir,[S|Ss],Cmt,Tests) ->
- Tests1 = skip_cases(Node,Dir,S,all,Cmt,Tests),
- skip_suites(Node,Dir,Ss,Cmt,Tests1);
-skip_suites(_Node,_Dir,[],_Cmt,Tests) ->
+skip_suites(Node,Dir,[S|Ss],Cmt,Tests,MergeTests) ->
+ Tests1 = skip_cases(Node,Dir,S,all,Cmt,Tests,MergeTests),
+ skip_suites(Node,Dir,Ss,Cmt,Tests1,MergeTests);
+skip_suites(_Node,_Dir,[],_Cmt,Tests,_MergeTests) ->
Tests;
-skip_suites(Node,Dir,S,Cmt,Tests) ->
- skip_suites(Node,Dir,[S],Cmt,Tests).
+skip_suites(Node,Dir,S,Cmt,Tests,MergeTests) ->
+ skip_suites(Node,Dir,[S],Cmt,Tests,MergeTests).
+
+skip_groups(Node,Dir,Suite,Group,all,Cmt,Tests,MergeTests)
+ when is_atom(Group) ->
+ skip_groups(Node,Dir,Suite,[Group],all,Cmt,Tests,MergeTests);
+skip_groups(Node,Dir,Suite,Group,Cases,Cmt,Tests,MergeTests)
+ when is_atom(Group) ->
+ skip_groups(Node,Dir,Suite,[Group],Cases,Cmt,Tests,MergeTests);
+skip_groups(Node,Dir,Suite,Groups,Case,Cmt,Tests,MergeTests)
+ when is_atom(Case),Case =/= all ->
+ skip_groups(Node,Dir,Suite,Groups,[Case],Cmt,Tests,MergeTests);
+skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,false) when
+ ((Cases == all) or is_list(Cases)) and is_list(Groups) ->
+ Suites1 = skip_groups1(Suite,[{Gr,Cases} || Gr <- Groups],Cmt,[]),
+ append({{Node,Dir},Suites1},Tests);
+skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,true) when
+ ((Cases == all) or is_list(Cases)) and is_list(Groups) ->
+ Suites =
+ case lists:keysearch({Node,Dir},1,Tests) of
+ {value,{{Node,Dir},Suites0}} ->
+ Suites0;
+ false ->
+ []
+ end,
+ Suites1 = skip_groups1(Suite,[{Gr,Cases} || Gr <- Groups],Cmt,Suites),
+ insert_in_order({{Node,Dir},Suites1},Tests);
+skip_groups(Node,Dir,Suite,Groups,Case,Cmt,Tests,MergeTests)
+ when is_atom(Case) ->
+ Cases = if Case == all -> all; true -> [Case] end,
+ skip_groups(Node,Dir,Suite,Groups,Cases,Cmt,Tests,MergeTests).
+
+skip_groups1(Suite,Groups,Cmt,Suites0) ->
+ SkipGroups = lists:map(fun(Group) ->
+ {Group,{skip,Cmt}}
+ end,Groups),
+ case lists:keysearch(Suite,1,Suites0) of
+ {value,{Suite,GrAndCases0}} ->
+ GrAndCases1 = GrAndCases0 ++ SkipGroups,
+ insert_in_order({Suite,GrAndCases1},Suites0);
+ false ->
+ insert_in_order({Suite,SkipGroups},Suites0)
+ end.
-skip_cases(Node,Dir,Suite,Cases,Cmt,Tests) when is_list(Cases) ->
+skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,false) when is_list(Cases) ->
+ Suites1 = skip_cases1(Suite,Cases,Cmt,[]),
+ append({{Node,Dir},Suites1},Tests);
+skip_cases(Node,Dir,Suite,Cases,Cmt,Tests,true) when is_list(Cases) ->
Suites =
case lists:keysearch({Node,Dir},1,Tests) of
{value,{{Node,Dir},Suites0}} ->
@@ -669,8 +1004,8 @@ skip_cases(Node,Dir,Suite,Cases,Cmt,Tests) when is_list(Cases) ->
end,
Suites1 = skip_cases1(Suite,Cases,Cmt,Suites),
insert_in_order({{Node,Dir},Suites1},Tests);
-skip_cases(Node,Dir,Suite,Case,Cmt,Tests) when is_atom(Case) ->
- skip_cases(Node,Dir,Suite,[Case],Cmt,Tests).
+skip_cases(Node,Dir,Suite,Case,Cmt,Tests,MergeTests) when is_atom(Case) ->
+ skip_cases(Node,Dir,Suite,[Case],Cmt,Tests,MergeTests).
skip_cases1(Suite,Cases,Cmt,Suites0) ->
SkipCases = lists:map(fun(C) ->
@@ -684,6 +1019,9 @@ skip_cases1(Suite,Cases,Cmt,Suites0) ->
insert_in_order({Suite,SkipCases},Suites0)
end.
+append(Elem, List) ->
+ List ++ [Elem].
+
insert_in_order([E|Es],List) ->
List1 = insert_elem(E,List,[]),
insert_in_order(Es,List1);
@@ -753,21 +1091,37 @@ valid_terms() ->
{cover,3},
{config,2},
{config,3},
+ {userconfig,2},
+ {userconfig,3},
{alias,3},
+ {merge_tests,1},
{logdir,2},
{logdir,3},
+ {label,2},
+ {label,3},
{event_handler,2},
{event_handler,3},
{event_handler,4},
+ {ct_hooks,2},
+ {ct_hooks,3},
+ {multiply_timetraps,2},
+ {multiply_timetraps,3},
+ {scale_timetraps,2},
+ {scale_timetraps,3},
{include,2},
{include,3},
-
{suites,3},
{suites,4},
+ {groups,4},
+ {groups,5},
+ {groups,6},
{cases,4},
{cases,5},
{skip_suites,4},
{skip_suites,5},
+ {skip_groups,5},
+ {skip_groups,6},
+ {skip_groups,7},
{skip_cases,5},
{skip_cases,6}
].
@@ -816,7 +1170,3 @@ common_letters([L|Ls],Term,Count) ->
end;
common_letters([],_,Count) ->
Count.
-
-
-
-
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index ba3d789f8d..115207beed 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -30,12 +30,11 @@
-export([register_connection/4,unregister_connection/1,
does_connection_exist/3,get_key_from_name/1]).
--export([require/1, require/2, get_config/1, get_config/2, get_config/3,
- set_default_config/2, set_default_config/3, delete_default_config/1,
- get_all_config/0, update_config/2,
- release_allocated/0, close_connections/0]).
+-export([close_connections/0]).
--export([save_suite_data/3, save_suite_data/2, read_suite_data/1,
+-export([save_suite_data/3, save_suite_data/2,
+ save_suite_data_async/3, save_suite_data_async/2,
+ read_suite_data/1,
delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1,
delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1,
update_testdata/2]).
@@ -46,6 +45,8 @@
silence_all_connections/0, silence_connections/1, is_silenced/1,
reset_silent_connections/0]).
+-export([get_mode/0, create_table/3, read_opts/0]).
+
-export([set_cwd/1, reset_cwd/0]).
-export([parse_table/1]).
@@ -56,23 +57,15 @@
-export([is_test_dir/1, get_testdir/2]).
--export([encrypt_config_file/2, encrypt_config_file/3,
- decrypt_config_file/2, decrypt_config_file/3]).
-
--export([kill_attached/2, get_attached/1]).
+-export([kill_attached/2, get_attached/1, ct_make_ref/0]).
-export([warn_duplicates/1]).
-include("ct_event.hrl").
-include("ct_util.hrl").
--record(ct_conf,{key,value,ref,name='_UNDEF',default=false}).
-%% default = {true,suite} | {true,testcase} | false
-
-record(suite_data, {key,name,value}).
--define(cryptfile, ".ct_config.crypt").
-
%%%-----------------------------------------------------------------
%%% @spec start(Mode) -> Pid | exit(Error)
%%% Mode = normal | interactive
@@ -119,7 +112,6 @@ start(Mode,LogDir) ->
do_start(Parent,Mode,LogDir) ->
process_flag(trap_exit,true),
register(ct_util_server,self()),
- create_table(?attr_table,bag,#ct_conf.key),
create_table(?conn_table,#conn.handle),
create_table(?board_table,2),
create_table(?suite_table,#suite_data.key),
@@ -135,7 +127,6 @@ do_start(Parent,Mode,LogDir) ->
Parent ! {self(),Error},
exit(Error)
end,
-
%% start an event manager (if not already started by master)
case ct_event:start_link() of
{error,{already_started,_}} ->
@@ -148,38 +139,45 @@ do_start(Parent,Mode,LogDir) ->
ct_event:add_handler([{vts,VtsPid}])
end
end,
- case read_config_files(Opts) of
- ok ->
- %% add user handlers
- case lists:keysearch(event_handler,1,Opts) of
- {value,{_,Handlers}} ->
- Add = fun({H,Args}) ->
- case catch gen_event:add_handler(?CT_EVMGR_REF,H,Args) of
- ok -> ok;
- {'EXIT',Why} -> exit(Why);
- Other -> exit({event_handler,Other})
- end
- end,
- case catch lists:foreach(Add,Handlers) of
- {'EXIT',Reason} ->
- Parent ! {self(),Reason};
- _ ->
- ok
- end;
- false ->
+ %% start ct_config server
+ ct_config:start(Mode),
+ %% add user event handlers
+ case lists:keysearch(event_handler,1,Opts) of
+ {value,{_,Handlers}} ->
+ Add = fun({H,Args}) ->
+ case catch gen_event:add_handler(?CT_EVMGR_REF,H,Args) of
+ ok -> ok;
+ {'EXIT',Why} -> exit(Why);
+ Other -> exit({event_handler,Other})
+ end
+ end,
+ case catch lists:foreach(Add,Handlers) of
+ {'EXIT',Reason} ->
+ Parent ! {self(),Reason};
+ _ ->
ok
- end,
- {StartTime,TestLogDir} = ct_logs:init(Mode),
- ct_event:notify(#event{name=test_start,
- node=node(),
- data={StartTime,
- lists:flatten(TestLogDir)}}),
- Parent ! {self(),started},
- loop(Mode,[],StartDir);
- ReadError ->
- Parent ! {self(),ReadError},
- exit(ReadError)
- end.
+ end;
+ false ->
+ ok
+ end,
+ {StartTime,TestLogDir} = ct_logs:init(Mode),
+
+ %% Initiate ct_hooks
+ case catch ct_hooks:init(Opts) of
+ ok ->
+ ok;
+ {_,CTHReason} ->
+ ct_logs:tc_print('Suite Callback',CTHReason,[]),
+ Parent ! {self(), CTHReason},
+ self() ! {{stop,normal},{self(),make_ref()}}
+ end,
+
+ ct_event:notify(#event{name=test_start,
+ node=node(),
+ data={StartTime,
+ lists:flatten(TestLogDir)}}),
+ Parent ! {self(),started},
+ loop(Mode,[],StartDir).
create_table(TableName,KeyPos) ->
create_table(TableName,set,KeyPos).
@@ -197,105 +195,6 @@ read_opts() ->
{error,{bad_installation,Error}}
end.
-read_config_files(Opts) ->
- ConfigFiles =
- lists:foldl(fun({config,Files},Acc) ->
- Acc ++ Files;
- (_,Acc) ->
- Acc
- end,[],Opts),
- read_config_files1(ConfigFiles).
-
-read_config_files1([ConfigFile|Files]) ->
- case file:consult(ConfigFile) of
- {ok,Config} ->
- set_config(Config),
- read_config_files1(Files);
- {error,enoent} ->
- {user_error,{config_file_error,ConfigFile,enoent}};
- {error,Reason} ->
- Key =
- case application:get_env(common_test, decrypt) of
- {ok,KeyOrFile} ->
- case KeyOrFile of
- {key,K} ->
- K;
- {file,F} ->
- get_crypt_key_from_file(F)
- end;
- _ ->
- get_crypt_key_from_file()
- end,
- case Key of
- {error,no_crypt_file} ->
- {user_error,{config_file_error,ConfigFile,Reason}};
- {error,CryptError} ->
- {user_error,{decrypt_file_error,ConfigFile,CryptError}};
- _ when is_list(Key) ->
- case decrypt_config_file(ConfigFile, undefined, {key,Key}) of
- {ok,CfgBin} ->
- case read_config_terms(CfgBin) of
- {error,ReadFail} ->
- {user_error,{config_file_error,ConfigFile,ReadFail}};
- Config ->
- set_config(Config),
- read_config_files1(Files)
- end;
- {error,DecryptFail} ->
- {user_error,{decrypt_config_error,ConfigFile,DecryptFail}}
- end;
- _ ->
- {user_error,{bad_decrypt_key,ConfigFile,Key}}
- end
- end;
-read_config_files1([]) ->
- ok.
-
-read_config_terms(Bin) when is_binary(Bin) ->
- case catch binary_to_list(Bin) of
- {'EXIT',_} ->
- {error,invalid_textfile};
- Lines ->
- read_config_terms(Lines)
- end;
-read_config_terms(Lines) when is_list(Lines) ->
- read_config_terms1(erl_scan:tokens([], Lines, 0), 1, [], []).
-
-read_config_terms1({done,{ok,Ts,EL},Rest}, L, Terms, _) ->
- case erl_parse:parse_term(Ts) of
- {ok,Term} when Rest == [] ->
- lists:reverse([Term|Terms]);
- {ok,Term} ->
- read_config_terms1(erl_scan:tokens([], Rest, 0),
- EL+1, [Term|Terms], Rest);
- _ ->
- {error,{bad_term,{L,EL}}}
- end;
-read_config_terms1({done,{eof,_},_}, _, Terms, Rest) when Rest == [] ->
- lists:reverse(Terms);
-read_config_terms1({done,{eof,EL},_}, L, _, _) ->
- {error,{bad_term,{L,EL}}};
-read_config_terms1({done,{error,Info,EL},_}, L, _, _) ->
- {error,{Info,{L,EL}}};
-read_config_terms1({more,_}, L, Terms, Rest) ->
- case string:tokens(Rest, [$\n,$\r,$\t]) of
- [] ->
- lists:reverse(Terms);
- _ ->
- {error,{bad_term,L}}
- end.
-
-set_default_config(NewConfig, Scope) ->
- call({set_default_config, {NewConfig, Scope}}).
-
-set_default_config(Name, NewConfig, Scope) ->
- call({set_default_config, {Name, NewConfig, Scope}}).
-
-delete_default_config(Scope) ->
- call({delete_default_config, Scope}).
-
-update_config(Name, Config) ->
- call({update_config, {Name, Config}}).
save_suite_data(Key, Value) ->
call({save_suite_data, {Key, undefined, Value}}).
@@ -303,6 +202,12 @@ save_suite_data(Key, Value) ->
save_suite_data(Key, Name, Value) ->
call({save_suite_data, {Key, Name, Value}}).
+save_suite_data_async(Key, Value) ->
+ save_suite_data_async(Key, undefined, Value).
+
+save_suite_data_async(Key, Name, Value) ->
+ cast({save_suite_data, {Key, Name, Value}}).
+
read_suite_data(Key) ->
call({read_suite_data, Key}).
@@ -342,26 +247,6 @@ loop(Mode,TestData,StartDir) ->
ct_logs:make_last_run_index(),
return(From,ok),
loop(Mode,TestData,StartDir);
- {{require,Name,Tag,SubTags},From} ->
- Result = do_require(Name,Tag,SubTags),
- return(From,Result),
- loop(Mode,TestData,StartDir);
- {{set_default_config,{Config,Scope}},From} ->
- set_config(Config,{true,Scope}),
- return(From,ok),
- loop(Mode,TestData,StartDir);
- {{set_default_config,{Name,Config,Scope}},From} ->
- set_config(Name,Config,{true,Scope}),
- return(From,ok),
- loop(Mode,TestData,StartDir);
- {{delete_default_config,Scope},From} ->
- delete_config({true,Scope}),
- return(From,ok),
- loop(Mode,TestData,StartDir);
- {{update_config,{Name,NewConfig}},From} ->
- update_conf(Name,NewConfig),
- return(From,ok),
- loop(Mode,TestData,StartDir);
{{save_suite_data,{Key,Name,Value}},From} ->
ets:insert(?suite_table, #suite_data{key=Key,
name=Name,
@@ -403,6 +288,9 @@ loop(Mode,TestData,StartDir) ->
TestData1 = lists:keydelete(Key,1,TestData),
return(From,ok),
loop(Mode,[New|TestData1],StartDir);
+ {{get_testdata, all}, From} ->
+ return(From, TestData),
+ loop(From, TestData, StartDir);
{{get_testdata,Key},From} ->
case lists:keysearch(Key,1,TestData) of
{value,{Key,Val}} ->
@@ -434,15 +322,22 @@ loop(Mode,TestData,StartDir) ->
ct_event:sync_notify(#event{name=test_done,
node=node(),
data=Time}),
- ets:delete(?attr_table),
+ Callbacks = ets:lookup_element(?suite_table,
+ ct_hooks,
+ #suite_data.value),
+ ct_hooks:terminate(Callbacks),
close_connections(ets:tab2list(?conn_table)),
ets:delete(?conn_table),
ets:delete(?board_table),
ets:delete(?suite_table),
ct_logs:close(How),
- file:set_cwd(StartDir),
ct_event:stop(),
+ ct_config:stop(),
+ file:set_cwd(StartDir),
return(From,ok);
+ {Ref, _Msg} when is_reference(Ref) ->
+ %% This clause is used when doing cast operations.
+ loop(Mode,TestData,StartDir);
{get_mode,From} ->
return(From,Mode),
loop(Mode,TestData,StartDir);
@@ -463,6 +358,8 @@ close_connections([#conn{handle=Handle,callback=CB}|Conns]) ->
close_connections([]) ->
ok.
+get_key_from_name(Name)->
+ ct_config:get_key_from_name(Name).
%%%-----------------------------------------------------------------
%%% @spec register_connection(TargetName,Address,Callback,Handle) ->
@@ -480,7 +377,7 @@ close_connections([]) ->
%%% test is finished by calling <code>Callback:close/1</code>.</p>
register_connection(TargetName,Address,Callback,Handle) ->
TargetRef =
- case get_ref_from_name(TargetName) of
+ case ct_config:get_ref_from_name(TargetName) of
{ok,Ref} ->
Ref;
_ ->
@@ -518,7 +415,7 @@ unregister_connection(Handle) ->
%%%
%%% @doc Check if a connection already exists.
does_connection_exist(TargetName,Address,Callback) ->
- case get_ref_from_name(TargetName) of
+ case ct_config:get_ref_from_name(TargetName) of
{ok,TargetRef} ->
case ets:select(?conn_table,[{#conn{handle='$1',
targetref=TargetRef,
@@ -548,7 +445,7 @@ does_connection_exist(TargetName,Address,Callback) ->
%%% @doc Return all connections for the <code>Callback</code> on the
%%% given target (<code>TargetName</code>).
get_connections(TargetName,Callback) ->
- case get_ref_from_name(TargetName) of
+ case ct_config:get_ref_from_name(TargetName) of
{ok,Ref} ->
{ok,ets:select(?conn_table,[{#conn{handle='$1',
address='$2',
@@ -568,250 +465,11 @@ get_target_name(ConnPid) ->
[],
['$1']}]) of
[TargetRef] ->
- get_name_from_ref(TargetRef);
+ ct_config:get_name_from_ref(TargetRef);
[] ->
{error,{unknown_connection,ConnPid}}
end.
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:require/1
-require(Key) when is_atom(Key) ->
- require({Key,[]});
-require({Key,SubKeys}) when is_atom(Key) ->
- allocate('_UNDEF',Key,to_list(SubKeys));
-require(Key) ->
- {error,{invalid,Key}}.
-
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:require/2
-require(Name,Key) when is_atom(Key) ->
- require(Name,{Key,[]});
-require(Name,{Key,SubKeys}) when is_atom(Name), is_atom(Key) ->
- call({require,Name,Key,to_list(SubKeys)});
-require(Name,Keys) ->
- {error,{invalid,{Name,Keys}}}.
-
-to_list(X) when is_list(X) -> X;
-to_list(X) -> [X].
-
-do_require(Name,Key,SubKeys) when is_list(SubKeys) ->
- case get_key_from_name(Name) of
- {error,_} ->
- allocate(Name,Key,SubKeys);
- {ok,Key} ->
- %% already allocated - check that it has all required subkeys
- Vals = [Val || {_Ref,Val} <- lookup_name(Name)],
- case get_subconfig(SubKeys,Vals) of
- {ok,_SubMapped} ->
- ok;
- Error ->
- Error
- end;
- {ok,OtherKey} ->
- {error,{name_in_use,Name,OtherKey}}
- end.
-
-allocate(Name,Key,SubKeys) ->
- case ets:match_object(?attr_table,#ct_conf{key=Key,name='_UNDEF',_='_'}) of
- [] ->
- {error,{not_available,Key}};
- Available ->
- case allocate_subconfig(Name,SubKeys,Available,false) of
- ok ->
- ok;
- Error ->
- Error
- end
- end.
-
-allocate_subconfig(Name,SubKeys,[C=#ct_conf{value=Value}|Rest],Found) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,_SubMapped} ->
- ets:insert(?attr_table,C#ct_conf{name=Name}),
- allocate_subconfig(Name,SubKeys,Rest,true);
- _Error ->
- allocate_subconfig(Name,SubKeys,Rest,Found)
- end;
-allocate_subconfig(_Name,_SubKeys,[],true) ->
- ok;
-allocate_subconfig(_Name,SubKeys,[],false) ->
- {error,{not_available,SubKeys}}.
-
-
-
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:get_config/1
-get_config(KeyOrName) ->
- get_config(KeyOrName,undefined,[]).
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:get_config/2
-get_config(KeyOrName,Default) ->
- get_config(KeyOrName,Default,[]).
-
-%%%-----------------------------------------------------------------
-%%% @hidden
-%%% @equiv ct:get_config/3
-get_config(KeyOrName,Default,Opts) when is_atom(KeyOrName) ->
- case lookup_config(KeyOrName) of
- [] ->
- Default;
- [{_Ref,Val}|_] = Vals ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{KeyOrName,V} || {_R,V} <- lists:sort(Vals)];
- {true,false} ->
- [V || {_R,V} <- lists:sort(Vals)];
- {false,true} ->
- {KeyOrName,Val};
- {false,false} ->
- Val
- end
- end;
-
-get_config({KeyOrName,SubKey},Default,Opts) ->
- case lookup_config(KeyOrName) of
- [] ->
- Default;
- Vals ->
- Vals1 = case [Val || {_Ref,Val} <- lists:sort(Vals)] of
- Result=[L|_] when is_list(L) ->
- case L of
- [{_,_}|_] ->
- Result;
- _ ->
- []
- end;
- _ ->
- []
- end,
- case get_subconfig([SubKey],Vals1,[],Opts) of
- {ok,[{_,SubVal}|_]=SubVals} ->
- case {lists:member(all,Opts),lists:member(element,Opts)} of
- {true,true} ->
- [{{KeyOrName,SubKey},Val} || {_,Val} <- SubVals];
- {true,false} ->
- [Val || {_SubKey,Val} <- SubVals];
- {false,true} ->
- {{KeyOrName,SubKey},SubVal};
- {false,false} ->
- SubVal
- end;
- _ ->
- Default
- end
- end.
-
-
-get_subconfig(SubKeys,Values) ->
- get_subconfig(SubKeys,Values,[],[]).
-
-get_subconfig(SubKeys,[Value|Rest],Mapped,Opts) ->
- case do_get_config(SubKeys,Value,[]) of
- {ok,SubMapped} ->
- case lists:member(all,Opts) of
- true ->
- get_subconfig(SubKeys,Rest,Mapped++SubMapped,Opts);
- false ->
- {ok,SubMapped}
- end;
- _Error ->
- get_subconfig(SubKeys,Rest,Mapped,Opts)
- end;
-get_subconfig(SubKeys,[],[],_) ->
- {error,{not_available,SubKeys}};
-get_subconfig(_SubKeys,[],Mapped,_) ->
- {ok,Mapped}.
-
-do_get_config([Key|Required],Available,Mapped) ->
- case lists:keysearch(Key,1,Available) of
- {value,{Key,Value}} ->
- NewAvailable = lists:keydelete(Key,1,Available),
- NewMapped = [{Key,Value}|Mapped],
- do_get_config(Required,NewAvailable,NewMapped);
- false ->
- {error,{not_available,Key}}
- end;
-do_get_config([],_Available,Mapped) ->
- {ok,lists:reverse(Mapped)}.
-
-get_all_config() ->
- ets:select(?attr_table,[{#ct_conf{name='$1',key='$2',value='$3',
- default='$4',_='_'},
- [],
- [{{'$1','$2','$3','$4'}}]}]).
-
-lookup_config(KeyOrName) ->
- case lookup_name(KeyOrName) of
- [] ->
- lookup_key(KeyOrName);
- Values ->
- Values
- end.
-
-lookup_name(Name) ->
- ets:select(?attr_table,[{#ct_conf{ref='$1',value='$2',name=Name,_='_'},
- [],
- [{{'$1','$2'}}]}]).
-lookup_key(Key) ->
- ets:select(?attr_table,[{#ct_conf{key=Key,ref='$1',value='$2',name='_UNDEF',_='_'},
- [],
- [{{'$1','$2'}}]}]).
-
-set_config(Config) ->
- set_config('_UNDEF',Config,false).
-
-set_config(Config,Default) ->
- set_config('_UNDEF',Config,Default).
-
-set_config(Name,Config,Default) ->
- [ets:insert(?attr_table,
- #ct_conf{key=Key,value=Val,ref=ct_make_ref(),
- name=Name,default=Default}) ||
- {Key,Val} <- Config].
-
-delete_config(Default) ->
- ets:match_delete(?attr_table,#ct_conf{default=Default,_='_'}),
- ok.
-
-
-%%%-----------------------------------------------------------------
-%%% @spec release_allocated() -> ok
-%%%
-%%% @doc Release all allocated resources, but don't take down any
-%%% connections.
-release_allocated() ->
- Allocated = ets:select(?attr_table,[{#ct_conf{name='$1',_='_'},
- [{'=/=','$1','_UNDEF'}],
- ['$_']}]),
- release_allocated(Allocated).
-release_allocated([H|T]) ->
- ets:delete_object(?attr_table,H),
- ets:insert(?attr_table,H#ct_conf{name='_UNDEF'}),
- release_allocated(T);
-release_allocated([]) ->
- ok.
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-update_conf(Name, NewConfig) ->
- Old = ets:select(?attr_table,[{#ct_conf{name=Name,_='_'},[],['$_']}]),
- lists:foreach(fun(OldElem) ->
- NewElem = OldElem#ct_conf{value=NewConfig},
- ets:delete_object(?attr_table, OldElem),
- ets:insert(?attr_table, NewElem)
- end, Old),
- ok.
-
%%%-----------------------------------------------------------------
%%% @spec close_connections() -> ok
%%%
@@ -928,10 +586,37 @@ listenv(Telnet) ->
%%% @hidden
%%% @equiv ct:parse_table/1
parse_table(Data) ->
- [Heading|Lines]=
- [remove_space(string:tokens(L, "|"),[]) || L <- Data, hd(L)==$|],
+ {Heading, Rest} = get_headings(Data),
+ Lines = parse_row(Rest,[],size(Heading)),
{Heading,Lines}.
+get_headings(["|" ++ Headings | Rest]) ->
+ {remove_space(string:tokens(Headings, "|"),[]), Rest};
+get_headings([_ | Rest]) ->
+ get_headings(Rest);
+get_headings([]) ->
+ {{},[]}.
+
+parse_row(["|" ++ _ = Row | T], Rows, NumCols) when NumCols > 1 ->
+ case string:tokens(Row, "|") of
+ Values when length(Values) =:= NumCols ->
+ parse_row(T,[remove_space(Values,[])|Rows], NumCols);
+ Values when length(Values) < NumCols ->
+ parse_row([Row ++"\n"++ hd(T) | tl(T)], Rows, NumCols)
+ end;
+parse_row(["|" ++ _ = Row | T], Rows, 1 = NumCols) ->
+ case string:rchr(Row, $|) of
+ 1 ->
+ parse_row([Row ++"\n"++hd(T) | tl(T)], Rows, NumCols);
+ _Else ->
+ parse_row(T, [remove_space(string:tokens(Row,"|"),[])|Rows],
+ NumCols)
+ end;
+parse_row([_Skip | T], Rows, NumCols) ->
+ parse_row(T, Rows, NumCols);
+parse_row([], Rows, _NumCols) ->
+ lists:reverse(Rows).
+
remove_space([Str|Rest],Acc) ->
remove_space(Rest,[string:strip(string:strip(Str),both,$')|Acc]);
remove_space([],Acc) ->
@@ -995,167 +680,6 @@ get_testdir(Dir, _) ->
%%% @spec
%%%
%%% @doc
-encrypt_config_file(SrcFileName, EncryptFileName) ->
- case get_crypt_key_from_file() of
- {error,_} = E ->
- E;
- Key ->
- encrypt_config_file(SrcFileName, EncryptFileName, {key,Key})
- end.
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) ->
- case get_crypt_key_from_file(KeyFile) of
- {error,_} = E ->
- E;
- Key ->
- encrypt_config_file(SrcFileName, EncryptFileName, {key,Key})
- end;
-
-encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) ->
- crypto:start(),
- {K1,K2,K3,IVec} = make_crypto_key(Key),
- case file:read_file(SrcFileName) of
- {ok,Bin0} ->
- Bin1 = term_to_binary({SrcFileName,Bin0}),
- Bin2 = case byte_size(Bin1) rem 8 of
- 0 -> Bin1;
- N -> list_to_binary([Bin1,random_bytes(8-N)])
- end,
- EncBin = crypto:des3_cbc_encrypt(K1, K2, K3, IVec, Bin2),
- case file:write_file(EncryptFileName, EncBin) of
- ok ->
- io:format("~s --(encrypt)--> ~s~n",
- [SrcFileName,EncryptFileName]),
- ok;
- {error,Reason} ->
- {error,{Reason,EncryptFileName}}
- end;
- {error,Reason} ->
- {error,{Reason,SrcFileName}}
- end.
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-decrypt_config_file(EncryptFileName, TargetFileName) ->
- case get_crypt_key_from_file() of
- {error,_} = E ->
- E;
- Key ->
- decrypt_config_file(EncryptFileName, TargetFileName, {key,Key})
- end.
-
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
-decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) ->
- case get_crypt_key_from_file(KeyFile) of
- {error,_} = E ->
- E;
- Key ->
- decrypt_config_file(EncryptFileName, TargetFileName, {key,Key})
- end;
-
-decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) ->
- crypto:start(),
- {K1,K2,K3,IVec} = make_crypto_key(Key),
- case file:read_file(EncryptFileName) of
- {ok,Bin} ->
- DecBin = crypto:des3_cbc_decrypt(K1, K2, K3, IVec, Bin),
- case catch binary_to_term(DecBin) of
- {'EXIT',_} ->
- {error,bad_file};
- {_SrcFile,SrcBin} ->
- case TargetFileName of
- undefined ->
- {ok,SrcBin};
- _ ->
- case file:write_file(TargetFileName, SrcBin) of
- ok ->
- io:format("~s --(decrypt)--> ~s~n",
- [EncryptFileName,TargetFileName]),
- ok;
- {error,Reason} ->
- {error,{Reason,TargetFileName}}
- end
- end
- end;
- {error,Reason} ->
- {error,{Reason,EncryptFileName}}
- end.
-
-
-get_crypt_key_from_file(File) ->
- case file:read_file(File) of
- {ok,Bin} ->
- case catch string:tokens(binary_to_list(Bin), [$\n,$\r]) of
- [Key] ->
- Key;
- _ ->
- {error,{bad_crypt_file,File}}
- end;
- {error,Reason} ->
- {error,{Reason,File}}
- end.
-
-get_crypt_key_from_file() ->
- CwdFile = filename:join(".",?cryptfile),
- {Result,FullName} =
- case file:read_file(CwdFile) of
- {ok,Bin} ->
- {Bin,CwdFile};
- _ ->
- case init:get_argument(home) of
- {ok,[[Home]]} ->
- HomeFile = filename:join(Home,?cryptfile),
- case file:read_file(HomeFile) of
- {ok,Bin} ->
- {Bin,HomeFile};
- _ ->
- {{error,no_crypt_file},noent}
- end;
- _ ->
- {{error,no_crypt_file},noent}
- end
- end,
- case FullName of
- noent ->
- Result;
- _ ->
- case catch string:tokens(binary_to_list(Result), [$\n,$\r]) of
- [Key] ->
- io:format("~nCrypt key file: ~s~n", [FullName]),
- Key;
- _ ->
- {error,{bad_crypt_file,FullName}}
- end
- end.
-
-make_crypto_key(String) ->
- <<K1:8/binary,K2:8/binary>> = First = erlang:md5(String),
- <<K3:8/binary,IVec:8/binary>> = erlang:md5([First|lists:reverse(String)]),
- {K1,K2,K3,IVec}.
-
-random_bytes(N) ->
- {A,B,C} = now(),
- random:seed(A, B, C),
- random_bytes_1(N, []).
-
-random_bytes_1(0, Acc) -> Acc;
-random_bytes_1(N, Acc) -> random_bytes_1(N-1, [random:uniform(255)|Acc]).
-
-
-%%%-----------------------------------------------------------------
-%%% @spec
-%%%
-%%% @doc
get_attached(TCPid) ->
case dbg_iserver:safe_call({get_attpid,TCPid}) of
{ok,AttPid} when is_pid(AttPid) ->
@@ -1210,7 +734,7 @@ call(Msg) ->
ct_util_server ! {Msg,{self(),Ref}},
receive
{Ref, Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{ct_util_server_down,Reason}}
@@ -1219,6 +743,9 @@ call(Msg) ->
return({To,Ref},Result) ->
To ! {Ref, Result}.
+cast(Msg) ->
+ ct_util_server ! {Msg, {ct_util_server, make_ref()}}.
+
seconds(T) ->
test_server:seconds(T).
@@ -1244,37 +771,6 @@ ct_make_ref_loop(N) ->
From ! {self(),N},
ct_make_ref_loop(N+1)
end.
-
-get_ref_from_name(Name) ->
- case ets:select(?attr_table,[{#ct_conf{name=Name,ref='$1',_='_'},
- [],
- ['$1']}]) of
- [Ref] ->
- {ok,Ref};
- _ ->
- {error,{no_such_name,Name}}
- end.
-
-get_name_from_ref(Ref) ->
- case ets:select(?attr_table,[{#ct_conf{name='$1',ref=Ref,_='_'},
- [],
- ['$1']}]) of
- [Name] ->
- {ok,Name};
- _ ->
- {error,{no_such_ref,Ref}}
- end.
-
-get_key_from_name(Name) ->
- case ets:select(?attr_table,[{#ct_conf{name=Name,key='$1',_='_'},
- [],
- ['$1']}]) of
- [Key|_] ->
- {ok,Key};
- _ ->
- {error,{no_such_name,Name}}
- end.
-
abs_name(Dir0) ->
Abs = filename:absname(Dir0),
diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl
index c1dc14f943..556f88c84d 100644
--- a/lib/common_test/src/ct_util.hrl
+++ b/lib/common_test/src/ct_util.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,13 +29,20 @@
-record(testspec, {spec_dir,
nodes=[],
+ init=[],
+ label=[],
logdir=["."],
cover=[],
config=[],
+ userconfig=[],
event_handler=[],
+ ct_hooks=[],
include=[],
+ multiply_timetraps=[],
+ scale_timetraps=[],
alias=[],
- tests=[]}).
+ tests=[],
+ merge_tests = true }).
-record(cover, {app=none,
level=details,
@@ -50,3 +57,4 @@
-define(CT_MEVMGR_REF, ct_master_event).
-define(missing_suites_info, "missing_suites.info").
+-define(ct_config_txt, ct_config_plain).
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
index ad4845a7c3..2ee982d726 100644
--- a/lib/common_test/src/vts.erl
+++ b/lib/common_test/src/vts.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -100,7 +100,7 @@ start_link() ->
MRef = erlang:monitor(process,Pid),
receive
{Pid,started} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
{ok,Pid};
{'DOWN',MRef,process,_,Reason} ->
{error,{vts,died,Reason}}
@@ -160,11 +160,13 @@ init(Parent) ->
loop(State) ->
receive
- {{init_data,ConfigFiles,EvHandlers,LogDir,Tests},From} ->
- ct_install(State),
+ {{init_data,Config,EvHandlers,LogDir,Tests},From} ->
+ %% ct:pal("State#state.current_log_dir=~p", [State#state.current_log_dir]),
+ NewState = State#state{config=Config,event_handler=EvHandlers,
+ current_log_dir=LogDir,tests=Tests},
+ ct_install(NewState),
return(From,ok),
- loop(#state{config=ConfigFiles,event_handler=EvHandlers,
- current_log_dir=LogDir,tests=Tests});
+ loop(NewState);
{start_page,From} ->
return(From,start_page1()),
loop(State);
@@ -257,7 +259,7 @@ call(Msg) ->
Pid ! {Msg,{self(),Ref}},
receive
{Ref, Result} ->
- erlang:demonitor(MRef),
+ erlang:demonitor(MRef, [flush]),
Result;
{'DOWN',MRef,process,_,Reason} ->
{error,{process_down,Pid,Reason}}
diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile
index 35ba22aa59..b7b099069c 100644
--- a/lib/common_test/test/Makefile
+++ b/lib/common_test/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2010. All Rights Reserved.
+# Copyright Ericsson AB 2008-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -27,13 +27,21 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
ct_test_support \
ct_test_support_eh \
+ ct_userconfig_callback \
ct_smoke_test_SUITE \
ct_event_handler_SUITE \
ct_groups_test_1_SUITE \
ct_groups_test_2_SUITE \
+ ct_sequence_1_SUITE \
+ ct_repeat_1_SUITE \
+ ct_testspec_1_SUITE \
ct_skip_SUITE \
ct_error_SUITE \
- ct_test_server_if_1_SUITE
+ ct_test_server_if_1_SUITE \
+ ct_config_SUITE \
+ ct_master_SUITE \
+ ct_misc_1_SUITE \
+ ct_hooks_SUITE
ERL_FILES= $(MODULES:%=%.erl)
@@ -64,15 +72,15 @@ EBIN = .
#.PHONY: make_emakefile
#make_emakefile:
-# $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) \
-# '*_SUITE_make' > $(EMAKEFILE)
# $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\
-# >> $(EMAKEFILE)
+# > $(EMAKEFILE)
tests debug opt:
+ erl $(ERL_MAKE_FLAGS) -make
clean:
rm -f $(TARGET_FILES)
+# rm -f $(EMAKEFILE)
rm -f core
docs:
@@ -87,8 +95,8 @@ release_spec: opt
release_tests_spec:
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR)
- $(INSTALL_PROGRAM) common_test.spec $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) common_test.spec $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/common_test/test/common_test.spec b/lib/common_test/test/common_test.spec
index 7619a75b31..8755b08117 100644
--- a/lib/common_test/test/common_test.spec
+++ b/lib/common_test/test/common_test.spec
@@ -1 +1 @@
-{topcase, {dir, "../common_test"}}.
+{suites,"../common_test_test",all}. \ No newline at end of file
diff --git a/lib/common_test/test/ct_config_SUITE.erl b/lib/common_test/test/ct_config_SUITE.erl
new file mode 100644
index 0000000000..b6b50f33e0
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE.erl
@@ -0,0 +1,275 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_config_SUITE
+%%%
+%%% Description:
+%%% Test configuration handling in Common Test suites.
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_config_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ DataDir = ?config(data_dir, Config),
+ PathDir = filename:join(DataDir, "config/test"),
+ Config1 = ct_test_support:init_per_suite([{path_dirs,[PathDir]} | Config]),
+ PrivDir = ?config(priv_dir, Config1),
+ ConfigDir = filename:join(PrivDir, "config"),
+ ok = file:make_dir(ConfigDir),
+ [{config_dir,ConfigDir} | Config1].
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(install_config = TestCase, Config) ->
+ ok = rpc:call(proplists:get_value(ct_node, Config), ct_config, stop, []),
+ ct_test_support:end_per_testcase(TestCase, Config);
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [require, install_config, userconfig_static,
+ userconfig_dynamic, testspec_legacy, testspec_static,
+ testspec_dynamic].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+require(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ run_test(config_static_SUITE,
+ Config,
+ {config, filename:join(DataDir, "config/config.txt")},
+ ["config_static_SUITE"]).
+
+install_config(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CTNode = proplists:get_value(ct_node, Config),
+ rpc:call(CTNode, ct, install,
+ [[{config, [filename:join(DataDir, "config/config.txt")]}]]),
+ case rpc:call(CTNode, ct_config, start, [interactive]) of
+ Pid when is_pid(Pid) ->
+ ok
+ end.
+
+
+userconfig_static(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ run_test(config_static_SUITE,
+ Config,
+ {userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}},
+ ["config_static_SUITE"]).
+
+userconfig_dynamic(Config) when is_list(Config) ->
+ run_test(config_dynamic_SUITE,
+ Config,
+ {userconfig, {config_driver, "config_server"}},
+ ["config_dynamic_SUITE"]).
+
+testspec_legacy(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ConfigDir = ?config(config_dir, Config),
+ make_spec(DataDir, ConfigDir,
+ "spec_legacy.spec",
+ [config_static_SUITE],
+ [{config, filename:join(DataDir, "config/config.txt")}]),
+ run_test(config_static_SUITE,
+ Config,
+ {spec, filename:join(ConfigDir, "spec_legacy.spec")},
+ []),
+ file:delete(filename:join(ConfigDir, "spec_legacy.spec")).
+
+testspec_static(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ConfigDir = ?config(config_dir, Config),
+ make_spec(DataDir, ConfigDir,
+ "spec_static.spec",
+ [config_static_SUITE],
+ [{userconfig, {ct_config_xml, filename:join(DataDir, "config/config.xml")}}]),
+ run_test(config_static_SUITE,
+ Config,
+ {spec, filename:join(ConfigDir, "spec_static.spec")},
+ []),
+ file:delete(filename:join(ConfigDir, "spec_static.spec")).
+
+testspec_dynamic(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ ConfigDir = ?config(config_dir, Config),
+ make_spec(DataDir, ConfigDir, "spec_dynamic.spec",
+ [config_dynamic_SUITE],
+ [{userconfig, {config_driver, "config_server"}}]),
+ run_test(config_dynamic_SUITE,
+ Config,
+ {spec, filename:join(ConfigDir, "spec_dynamic.spec")},
+ []),
+ file:delete(filename:join(ConfigDir, "spec_dynamic.spec")).
+
+
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+make_spec(DataDir, ConfigDir, Filename, Suites, Config)->
+ {ok, Fd} = file:open(filename:join(ConfigDir, Filename), [write]),
+ ok = file:write(Fd,
+ io_lib:format("{suites, \"~sconfig/test/\", ~p}.~n", [DataDir, Suites])),
+ lists:foreach(fun(C)-> ok=file:write(Fd, io_lib:format("~p.~n", [C])) end, Config),
+ ok = file:close(Fd).
+
+run_test(Name, Config, CTConfig, SuiteNames)->
+ DataDir = ?config(data_dir, Config),
+ Joiner = fun(Suite) -> filename:join(DataDir, "config/test/"++Suite) end,
+ Suites = lists:map(Joiner, SuiteNames),
+ {Opts,ERPid} = setup_env({suite,Suites}, Config, CTConfig),
+ ok = ct_test_support:run(Opts, Config),
+ TestEvents = ct_test_support:get_events(ERPid, Config),
+ ct_test_support:log_events(Name,
+ reformat_events(TestEvents, ?eh),
+ ?config(config_dir, Config)),
+ ExpEvents = events_to_check(Name),
+ ok = ct_test_support:verify_events(ExpEvents, TestEvents, Config).
+
+setup_env(Test, Config, CTConfig) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}}, CTConfig],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat_events(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ expected_events(Test) ++ events_to_check(Test, N-1).
+
+expected_events(config_static_SUITE)->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,8}},
+ {?eh,tc_start,{config_static_SUITE,init_per_suite}},
+ {?eh,tc_done,{config_static_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{config_static_SUITE,test_get_config_simple}},
+ {?eh,tc_done,{config_static_SUITE,test_get_config_simple,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_get_config_nested}},
+ {?eh,tc_done,{config_static_SUITE,test_get_config_nested,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_default_suitewide}},
+ {?eh,tc_done,{config_static_SUITE,test_default_suitewide,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use1}},
+ {?eh,tc_done,
+ {config_static_SUITE,test_config_name_already_in_use1,{skipped,{config_name_already_in_use,[x1]}}}},
+ {?eh,test_stats,{3,0,{1,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_default_tclocal}},
+ {?eh,tc_done,{config_static_SUITE,test_default_tclocal,ok}},
+ {?eh,test_stats,{4,0,{1,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_config_name_already_in_use2}},
+ {?eh,tc_done,
+ {config_static_SUITE,test_config_name_already_in_use2,
+ {skipped,{config_name_already_in_use,[x1,alias]}}}},
+ {?eh,test_stats,{4,0,{2,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_alias_tclocal}},
+ {?eh,tc_done,{config_static_SUITE,test_alias_tclocal,ok}},
+ {?eh,test_stats,{5,0,{2,0}}},
+ {?eh,tc_start,{config_static_SUITE,test_get_config_undefined}},
+ {?eh,tc_done,{config_static_SUITE,test_get_config_undefined,ok}},
+ {?eh,test_stats,{6,0,{2,0}}},
+ {?eh,tc_start,{config_static_SUITE,end_per_suite}},
+ {?eh,tc_done,{config_static_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+expected_events(config_dynamic_SUITE)->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,5}},
+ {?eh,tc_start,{config_dynamic_SUITE,init_per_suite}},
+ {?eh,tc_done,{config_dynamic_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{config_dynamic_SUITE,test_get_known_variable}},
+ {?eh,tc_done,
+ {config_dynamic_SUITE,test_get_known_variable,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{config_dynamic_SUITE,test_localtime_update}},
+ {?eh,tc_done,{config_dynamic_SUITE,test_localtime_update,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{config_dynamic_SUITE,test_server_pid}},
+ {?eh,tc_done,{config_dynamic_SUITE,test_server_pid,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,
+ {config_dynamic_SUITE,test_disappearable_variable}},
+ {?eh,tc_done,
+ {config_dynamic_SUITE,test_disappearable_variable,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,
+ {config_dynamic_SUITE,test_disappearable_variable_alias}},
+ {?eh,tc_done,
+ {config_dynamic_SUITE,test_disappearable_variable_alias,ok}},
+ {?eh,test_stats,{5,0,{0,0}}},
+ {?eh,tc_start,{config_dynamic_SUITE,end_per_suite}},
+ {?eh,tc_done,{config_dynamic_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.txt b/lib/common_test/test/ct_config_SUITE_data/config/config.txt
new file mode 100644
index 0000000000..fcbffcd7f3
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/config.txt
@@ -0,0 +1,31 @@
+{x, suite}.
+{gen_cfg,
+ [
+ {a,a_value},
+ {b,b_value}
+ ]}.
+{gen_cfg2,
+ [
+ {c, "Hello, world!"},
+ {d, atom_value},
+ {e, {tuple,1,"third value",[]}},
+ {f, []},
+ {g, [1,atom,"string",13.6,{1,2,3}]}
+ ]}.
+{gen_cfg3,
+ [
+ {h,
+ [
+ {i, third1},
+ {j, "Third2"},
+ {k, 'THIRD3'}
+ ]},
+ {l,
+ [
+ {m,
+ [
+ {n, "N"},
+ {o, 'O'}
+ ]}
+ ]}
+ ]}.
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/config.xml b/lib/common_test/test/ct_config_SUITE_data/config/config.xml
new file mode 100644
index 0000000000..0a3e5f2e31
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/config.xml
@@ -0,0 +1,27 @@
+<config>
+ <x>suite</x>
+ <gen_cfg>
+ <a>a_value</a>
+ <b>b_value</b>
+ </gen_cfg>
+ <gen_cfg2>
+ <c>"Hello, world!"</c>
+ <d>atom_value</d>
+ <e>{tuple,1,"third value",[]}</e>
+ <f>[]</f>
+ <g>[1,atom,"string",13.6,{1,2,3}]</g>
+ </gen_cfg2>
+ <gen_cfg3>
+ <h>
+ <i>third1</i>
+ <j>"Third2"</j>
+ <k>'THIRD3'</k>
+ </h>
+ <l>
+ <m>
+ <n>"N"</n>
+ <o>'O'</o>
+ </m>
+ </l>
+ </gen_cfg3>
+</config>
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_driver.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_driver.erl
new file mode 100644
index 0000000000..d93faf6ec6
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_driver.erl
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_config_SUITE
+%%%
+%%% Description:
+%%% Config driver used in the CT's tests (config_2_SUITE)
+%%%-------------------------------------------------------------------
+-module(config_driver).
+-export([read_config/1, check_parameter/1]).
+
+read_config(ServerName)->
+ ServerModule = list_to_atom(ServerName),
+ ServerModule:start(),
+ ServerModule:get_config().
+
+check_parameter(ServerName)->
+ ServerModule = list_to_atom(ServerName),
+ case code:is_loaded(ServerModule) of
+ {file, _}->
+ {ok, {config, ServerName}};
+ false->
+ case code:load_file(ServerModule) of
+ {module, ServerModule}->
+ {ok, {config, ServerName}};
+ {error, nofile}->
+ {error, {wrong_config, "File not found: " ++ ServerName ++ ".beam"}}
+ end
+ end.
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl
new file mode 100644
index 0000000000..8ee12a2e4d
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_dynamic_SUITE.erl
@@ -0,0 +1,145 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: config_dynamic_SUITE
+%%%
+%%% Description:
+%%% Test suite for common_test which tests the userconfig functionality
+%%%-------------------------------------------------------------------
+-module(config_dynamic_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%% This suite will be run with dynamic userconfig
+%% test_driver.erl is compliant to ct_config_* callback
+%% and test_server is simple server for getting runtime-changing data
+%% which will return the list with the following variables:
+%% localtime = the erlang:localtime() result in list [{date, Date}, {time, Time}]
+%% node = erlang:node() - can be compared in the testcase
+%% now = erlang:now() - easier to compare than localtime()
+%% config_server_pid - pid of the config server, should NOT change!
+%% config_server_vsn - .19
+%% config_server_iteration - a number of iteration config_server's loop done
+%% disappearable_variable - hereAmI - will be absent on even iterations
+
+suite() ->
+ [
+ {timetrap, {seconds,10}}
+ ].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_) ->
+ ok.
+
+all() -> [test_get_known_variable, test_localtime_update,
+ test_server_pid, test_disappearable_variable,
+ test_disappearable_variable_alias].
+
+init_per_testcase(_, Config) ->
+ Config.
+
+end_per_testcase(_, _) ->
+ ok.
+
+% test that usual config works
+test_get_known_variable(_)->
+ Node = erlang:node(),
+ 0.19 = ct:get_config(config_server_vsn),
+ Node = ct:get_config(node),
+ ok.
+
+% localtime will be updated in 5 seconds, check that
+test_localtime_update(_)->
+ Seconds = 5,
+ LT1 = ct:get_config(localtime),
+ timer:sleep(Seconds*1000),
+ LT2 = ct:reload_config(localtime),
+ case is_diff_ok(LT1, LT2, Seconds) of
+ {false, Actual, Exp}->
+ ct:fail(io_lib:format("Time difference ~p is not ok, expected ~p", [Actual, Exp]));
+ true->
+ ok
+ end.
+
+% server pid should not change
+test_server_pid()->
+ [{require, cfvsn, config_server_vsn}].
+test_server_pid(_)->
+ Pid = ct:get_config(config_server_pid),
+ Pid = ct:reload_config(config_server_pid),
+ Vsn = ct:get_config(config_server_vsn),
+ % aliases remain after config reloading
+ Vsn = ct:get_config(cfvsn),
+ ok.
+
+% test that variables may disappear from the config_2_SUITE
+test_disappearable_variable(_)->
+ % ask CT for config_server_iteration variable
+ Iter = ct:reload_config(config_server_iteration),
+ % here we should reload this variable in case it's odd
+ if Iter rem 2 == 1->
+ Iter2 = ct:reload_config(config_server_iteration),
+ Iter2 = Iter+1;
+ true->ok
+ end,
+ % now disappearable_variable should be in place
+ hereAmI = ct:get_config(disappearable_variable),
+ % and now it should disappear
+ undefined = ct:reload_config(disappearable_variable).
+
+% alias of disappearable_variable should disappear too
+test_disappearable_variable_alias(_)->
+ % the same rules apply for this testcase as for previous one
+ Iter = ct:reload_config(config_server_iteration),
+ Iter2 = if
+ Iter rem 2 == 1 ->
+ NewIter = ct:reload_config(config_server_iteration),
+ NewIter = Iter+1;
+ true->
+ Iter
+ end,
+ ct:require(diav, disappearable_variable),
+ hereAmI = ct:get_config(disappearable_variable),
+ hereAmI = ct:get_config(diav),
+ ct:reload_config(disappearable_variable),
+ undefined = ct:get_config(disappearable_variable),
+ % after reloading, it's even again
+ Iter3=ct:get_config(config_server_iteration),
+ Iter3 = Iter2+1,
+ % and alias does not exist
+ undefined = ct:get_config(diav).
+
+my_dt_to_datetime([{date, D},{time, T}])->
+ {D, T}.
+
+is_diff_ok(DT1, DT2, Seconds)->
+ GS1 = calendar:datetime_to_gregorian_seconds(my_dt_to_datetime(DT1)),
+ GS2 = calendar:datetime_to_gregorian_seconds(my_dt_to_datetime(DT2)),
+ if
+ GS2-GS1 > Seconds+Seconds/2;
+ GS2-GS1 < Seconds-Seconds/2->
+ {false, GS2-GS1, Seconds};
+ true->
+ true
+ end.
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_server.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_server.erl
new file mode 100644
index 0000000000..8463fea645
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_server.erl
@@ -0,0 +1,93 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_config_SUITE
+%%%
+%%% Description:
+%%% Config server used in the CT's tests (config_2_SUITE)
+%%%-------------------------------------------------------------------
+-module(config_server).
+-export([start/0, stop/0, loop/1, init/1, get_config/0]).
+
+-define(REGISTERED_NAME, ct_test_config_server).
+-define(vsn, 0.19).
+
+start()->
+ case whereis(?REGISTERED_NAME) of
+ undefined->
+ spawn(?MODULE, init, [?REGISTERED_NAME]),
+ wait();
+ _Pid->
+ ok
+ end,
+ ?REGISTERED_NAME.
+
+init(Name)->
+ register(Name, self()),
+ loop(0).
+
+get_config()->
+ call(self(), get_config).
+
+stop()->
+ call(self(), stop).
+
+call(Client, Request)->
+ case whereis(?REGISTERED_NAME) of
+ undefined->
+ {error, not_started, Request};
+ Pid->
+ Pid ! {Client, Request},
+ receive
+ Reply->
+ {ok, Reply}
+ after 4000->
+ {error, timeout, Request}
+ end
+ end.
+
+loop(Iteration)->
+ receive
+ {Pid, stop}->
+ Pid ! ok;
+ {Pid, get_config}->
+ {D,T} = erlang:localtime(),
+ Config =
+ [{localtime, [{date, D}, {time, T}]},
+ {node, erlang:node()},
+ {config_server_iteration, Iteration},
+ {now, erlang:now()},
+ {config_server_pid, self()},
+ {config_server_vsn, ?vsn}],
+ Config2 = if Iteration rem 2 == 0->
+ Config ++ [{disappearable_variable, hereAmI}];
+ true-> Config
+ end,
+ Pid ! Config2,
+ ?MODULE:loop(Iteration+1)
+ end.
+
+wait()->
+ case whereis(?REGISTERED_NAME) of
+ undefined->
+ wait();
+ _Pid->
+ ok
+ end.
diff --git a/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl
new file mode 100644
index 0000000000..8751a2e8f3
--- /dev/null
+++ b/lib/common_test/test/ct_config_SUITE_data/config/test/config_static_SUITE.erl
@@ -0,0 +1,123 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: config_static_SUITE
+%%%
+%%% Description:
+%%% Test suite for common_test which tests the get_config and require
+%%% functionality
+%%%-------------------------------------------------------------------
+-module(config_static_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+% The config contains variables:
+% x - atom
+% gen_cfg - list with two key-values tagged with a and b
+% gen_cfg2 - list of five key-values tagged with c, d, e, f and g
+% gen_cfg3 - list of two complex key-values taggen with:
+% h: three elements inside - i, j and k
+% l: m inside, contains n and o
+
+suite() ->
+ [
+ {timetrap, {seconds,10}},
+ %% x1 doesn't exist in cfg-file!
+ {require, x1, x},
+ {require, gen_cfg3},
+ {require, alias, gen_cfg},
+ %% x1 default value
+ {x1, {x,suite}}
+ ].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_) ->
+ ok.
+
+all() -> [test_get_config_simple, test_get_config_nested, test_default_suitewide,
+ test_config_name_already_in_use1, test_default_tclocal,
+ test_config_name_already_in_use2, test_alias_tclocal,
+ test_get_config_undefined].
+
+init_per_testcase(_, Config) ->
+ Config.
+
+end_per_testcase(_, _) ->
+ ok.
+
+%% test getting a simple value
+test_get_config_simple(_)->
+ suite = ct:get_config(x),
+ ok.
+
+%% test getting a nested value
+test_get_config_nested(_)->
+ a_value = ct:get_config({gen_cfg, a}),
+ ok.
+
+%% test suite-wide default value
+test_default_suitewide(_)->
+ suite = ct:get_config(x1),
+ ok.
+
+%% should get skipped
+test_config_name_already_in_use1() ->
+ [{timetrap, {seconds,2}},
+ {require, x1, x},
+ {x1, {x,test2}}].
+test_config_name_already_in_use1(_) ->
+ ct:fail("Test should've been skipped, you shouldn't see this!"),
+ ok.
+
+%% test defaults in a testcase
+test_default_tclocal() ->
+ [{timetrap, {seconds,3}},
+ {require, y1, y},
+ {y1, {y,test3}}].
+test_default_tclocal(_) ->
+ test3 = ct:get_config(y1),
+ ok.
+
+%% should get skipped
+test_config_name_already_in_use2() ->
+ [{require,alias,something},
+ {alias,{something,else}},
+ {require, x1, x},
+ {x1, {x,test4}}].
+test_config_name_already_in_use2(_) ->
+ ct:fail("Test should've been skipped, you shouldn't see this!"),
+ ok.
+
+%% test aliases
+test_alias_tclocal() ->
+ [{require,newalias,gen_cfg}].
+test_alias_tclocal(_) ->
+ A = [{a,a_value},{b,b_value}] = ct:get_config(newalias),
+ A = ct:get_config(gen_cfg),
+ ok.
+
+%% test for getting undefined variables
+test_get_config_undefined(_) ->
+ undefined = ct:get_config(y1),
+ ok.
diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl
index be75d768fc..ad6cf1ba8f 100644
--- a/lib/common_test/test/ct_error_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-define(eh, ct_test_support_eh).
@@ -56,16 +56,22 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-all(doc) ->
- [""];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [cfg_error, lib_error, no_compile, timetrap_end_conf,
+ timetrap_normal, timetrap_extended].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-all(suite) ->
- [
- cfg_error,
- lib_error,
- no_compile
- ].
-
%%--------------------------------------------------------------------
%% TEST CASES
@@ -84,17 +90,22 @@ cfg_error(Config) when is_list(Config) ->
Join(DataDir, "cfg_error_6_SUITE"),
Join(DataDir, "cfg_error_7_SUITE"),
Join(DataDir, "cfg_error_8_SUITE"),
- Join(DataDir, "cfg_error_9_SUITE")
+ Join(DataDir, "cfg_error_9_SUITE"),
+ Join(DataDir, "cfg_error_10_SUITE"),
+ Join(DataDir, "cfg_error_11_SUITE"),
+ Join(DataDir, "cfg_error_12_SUITE"),
+ Join(DataDir, "cfg_error_13_SUITE"),
+ Join(DataDir, "cfg_error_14_SUITE")
],
- {Opts,ERPid} = setup({suite,Suites}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ {Opts,ERPid} = setup([{suite,Suites}], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(cfg_error,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(cfg_error),
+ TestEvents = events_to_check(cfg_error),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -104,17 +115,17 @@ lib_error(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
Suites = [Join(DataDir, "lib_error_1_SUITE")],
- {Opts,ERPid} = setup({suite,Suites}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ {Opts,ERPid} = setup([{suite,Suites}], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(lib_error,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(lib_error),
+ TestEvents = events_to_check(lib_error),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
-
+
%%%-----------------------------------------------------------------
%%%
@@ -122,17 +133,75 @@ no_compile(Config) when is_list(Config) ->
DataDir = ?config(data_dir, Config),
Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
Suites = [Join(DataDir, "no_compile_SUITE")],
- {Opts,ERPid} = setup({suite,Suites}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ {Opts,ERPid} = setup([{suite,Suites}], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(no_compile,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(no_compile),
+ TestEvents = events_to_check(no_compile),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+timetrap_end_conf(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
+ Suites = [Join(DataDir, "timetrap_1_SUITE")],
+ {Opts,ERPid} = setup([{suite,Suites}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(timetrap_end_conf,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(timetrap_end_conf),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+timetrap_normal(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
+ Suite = Join(DataDir, "timetrap_2_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},
+ {userconfig,{ct_userconfig_callback,
+ "multiply 1 scale false"}}],
+ Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(timetrap_normal,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(timetrap_normal),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+timetrap_extended(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ Join = fun(D, S) -> filename:join(D, "error/test/"++S) end,
+ Suite = Join(DataDir, "timetrap_2_SUITE"),
+ {Opts,ERPid} = setup([{suite,Suite},
+ {multiply_timetraps,2},
+ {scale_timetraps,false},
+ {userconfig,{ct_userconfig_callback,
+ "multiply 2 scale false"}}],
+ Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(timetrap_extended,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(timetrap_extended),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
-
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
@@ -142,23 +211,32 @@ setup(Test, Config) ->
Opts0 = ct_test_support:get_opts(Config),
Level = ?config(trace_level, Config),
EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
- Opts = Opts0 ++ [Test,{event_handler,{?eh,EvHArgs}}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
ERPid = ct_test_support:start_event_receiver(Config),
{Opts,ERPid}.
reformat(Events, EH) ->
ct_test_support:reformat(Events, EH).
-%reformat(Events, _EH) ->
-% Events.
+ %reformat(Events, _EH) ->
+ % Events.
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
test_events(cfg_error) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
- {?eh,start_info,{9,9,33}},
+ {?eh,start_info,{14,14,42}},
{?eh,tc_start,{cfg_error_1_SUITE,init_per_suite}},
{?eh,tc_done,
@@ -173,7 +251,7 @@ test_events(cfg_error) ->
{?eh,test_stats,{0,0,{0,2}}},
{?eh,tc_auto_skip,
{cfg_error_1_SUITE,end_per_suite,{failed,{cfg_error_1_SUITE,init_per_suite,
- {'EXIT',init_per_suite_fails}}}}},
+ {'EXIT',init_per_suite_fails}}}}},
{?eh,tc_start,{cfg_error_2_SUITE,init_per_suite}},
{?eh,tc_done,
@@ -303,12 +381,12 @@ test_events(cfg_error) ->
{?eh,tc_auto_skip,
{cfg_error_8_SUITE,tc1,
{failed,{cfg_error_8_SUITE,init_per_group,
- {'EXIT',{init_per_group_fails,g1}}}}}},
+ {'EXIT',{init_per_group_fails,g1}}}}}},
{?eh,test_stats,{4,0,{0,11}}},
{?eh,tc_auto_skip,
{cfg_error_8_SUITE,end_per_group,
{failed,{cfg_error_8_SUITE,init_per_group,
- {'EXIT',{init_per_group_fails,g1}}}}}}],
+ {'EXIT',{init_per_group_fails,g1}}}}}}],
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g2,[]}}},
{?eh,tc_done,{cfg_error_8_SUITE,
@@ -321,7 +399,7 @@ test_events(cfg_error) ->
{?eh,tc_auto_skip,{cfg_error_8_SUITE,end_per_group,
{failed,{cfg_error_8_SUITE,init_per_group,
{timetrap_timeout,2000}}}}}],
-
+
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g3,[]}}},
{?eh,tc_done,
{cfg_error_8_SUITE,{init_per_group,g3,[]},
@@ -361,7 +439,7 @@ test_events(cfg_error) ->
{?eh,test_stats,{5,0,{0,13}}},
{?eh,tc_start,{cfg_error_8_SUITE,{end_per_group,g4,[]}}},
{?eh,tc_done,{cfg_error_8_SUITE,{end_per_group,g4,[]},ok}}],
-
+
[{?eh,tc_start,{cfg_error_8_SUITE,{init_per_group,g5,[]}}},
{?eh,tc_done,{cfg_error_8_SUITE,{init_per_group,g5,[]},ok}},
{?eh,tc_start,{cfg_error_8_SUITE,tc1}},
@@ -470,6 +548,59 @@ test_events(cfg_error) ->
{?eh,tc_start,{cfg_error_9_SUITE,end_per_suite}},
{?eh,tc_done,{cfg_error_9_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{cfg_error_10_SUITE,init_per_suite}},
+ {?eh,tc_done,{cfg_error_10_SUITE,init_per_suite,
+ {failed,{error,fail_init_per_suite}}}},
+ {?eh,tc_auto_skip,{cfg_error_10_SUITE,tc1,
+ {failed,{cfg_error_10_SUITE,init_per_suite,
+ {failed,fail_init_per_suite}}}}},
+ {?eh,test_stats,{12,3,{0,19}}},
+ {?eh,tc_auto_skip,{cfg_error_10_SUITE,end_per_suite,
+ {failed,{cfg_error_10_SUITE,init_per_suite,
+ {failed,fail_init_per_suite}}}}},
+ {?eh,tc_start,{cfg_error_11_SUITE,init_per_suite}},
+ {?eh,tc_done,{cfg_error_11_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{cfg_error_11_SUITE,tc1}},
+ {?eh,tc_done,{cfg_error_11_SUITE,tc1,
+ {skipped,{config_name_already_in_use,[dummy0]}}}},
+ {?eh,test_stats,{12,3,{1,19}}},
+ {?eh,tc_start,{cfg_error_11_SUITE,tc2}},
+ {?eh,tc_done,{cfg_error_11_SUITE,tc2,ok}},
+ {?eh,test_stats,{13,3,{1,19}}},
+ {?eh,tc_start,{cfg_error_11_SUITE,end_per_suite}},
+ {?eh,tc_done,{cfg_error_11_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{cfg_error_12_SUITE,tc1}},
+ {?eh,tc_done,{cfg_error_12_SUITE,tc1,{failed,{timetrap_timeout,500}}}},
+ {?eh,test_stats,{13,4,{1,19}}},
+ {?eh,tc_start,{cfg_error_12_SUITE,tc2}},
+ {?eh,tc_done,{cfg_error_12_SUITE,tc2,{failed,
+ {cfg_error_12_SUITE,end_per_testcase,
+ {timetrap_timeout,500}}}}},
+ {?eh,test_stats,{14,4,{1,19}}},
+ {?eh,tc_start,{cfg_error_12_SUITE,tc3}},
+ {?eh,tc_done,{cfg_error_12_SUITE,tc3,ok}},
+ {?eh,test_stats,{15,4,{1,19}}},
+ {?eh,tc_start,{cfg_error_12_SUITE,tc4}},
+ {?eh,tc_done,{cfg_error_12_SUITE,tc4,{failed,
+ {cfg_error_12_SUITE,end_per_testcase,
+ {timetrap_timeout,500}}}}},
+ {?eh,test_stats,{16,4,{1,19}}},
+ {?eh,tc_start,{cfg_error_13_SUITE,init_per_suite}},
+ {?eh,tc_done,{cfg_error_13_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{cfg_error_13_SUITE,tc1}},
+ {?eh,tc_done,{cfg_error_13_SUITE,tc1,ok}},
+ {?eh,test_stats,{17,4,{1,19}}},
+ {?eh,tc_start,{cfg_error_13_SUITE,end_per_suite}},
+ {?eh,tc_done,{cfg_error_13_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{cfg_error_14_SUITE,init_per_suite}},
+ {?eh,tc_done,{cfg_error_14_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{cfg_error_14_SUITE,tc1}},
+ {?eh,tc_done,{cfg_error_14_SUITE,tc1,ok}},
+ {?eh,test_stats,{18,4,{1,19}}},
+ {?eh,tc_start,{cfg_error_14_SUITE,end_per_suite}},
+ {?eh,tc_done,{cfg_error_14_SUITE,end_per_suite,
+ {comment,
+ "should succeed since ct_fw cancels timetrap in end_tc"}}},
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
];
@@ -555,4 +686,91 @@ test_events(lib_error) ->
];
test_events(no_compile) ->
- [].
+ [];
+
+test_events(timetrap_end_conf) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,6}},
+ {?eh,tc_start,{timetrap_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{timetrap_1_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc1}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc1,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc2}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc2,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc3}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc3,{failed,{testcase_aborted,testing_end_conf}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc4}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc4,{failed,{testcase_aborted,testing_end_conf}}}},
+ {?eh,test_stats,{0,4,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc5}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc5,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,5,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,tc6}},
+ {?eh,tc_done,
+ {timetrap_1_SUITE,tc6,{failed,{testcase_aborted,testing_end_conf}}}},
+ {?eh,test_stats,{0,6,{0,0}}},
+ {?eh,tc_start,{timetrap_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{timetrap_1_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(timetrap_normal) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,3}},
+ {?eh,tc_start,{timetrap_2_SUITE,init_per_suite}},
+ {?eh,tc_done,{timetrap_2_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc0}},
+ {?eh,tc_done,
+ {timetrap_2_SUITE,tc0,{failed,{timetrap_timeout,3000}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc1}},
+ {?eh,tc_done,
+ {timetrap_2_SUITE,tc1,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc2}},
+ {?eh,tc_done,
+ {timetrap_2_SUITE,tc2,{failed,{timetrap_timeout,500}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,end_per_suite}},
+ {?eh,tc_done,{timetrap_2_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(timetrap_extended) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,3}},
+ {?eh,tc_start,{timetrap_2_SUITE,init_per_suite}},
+ {?eh,tc_done,{timetrap_2_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc0}},
+ {?eh,tc_done,
+ {timetrap_2_SUITE,tc0,{failed,{timetrap_timeout,6000}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc1}},
+ {?eh,tc_done,
+ {timetrap_2_SUITE,tc1,{failed,{timetrap_timeout,2000}}}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,tc2}},
+ {?eh,tc_done,
+ {timetrap_2_SUITE,tc2,{failed,{timetrap_timeout,1000}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_start,{timetrap_2_SUITE,end_per_suite}},
+ {?eh,tc_done,{timetrap_2_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_10_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_10_SUITE.erl
new file mode 100644
index 0000000000..9f9a90372b
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_10_SUITE.erl
@@ -0,0 +1,123 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(cfg_error_10_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,2}},
+ {require, dummy0}, {default_config, dummy0, "suite/0"},
+ {require, dummy1}, {default_config, dummy1, "suite/0"},
+ {require, dummy2}, {default_config, dummy2, "suite/0"}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite() ->
+ put('$test_server_framework_test',
+ fun(init_tc, _Default) -> {fail,fail_init_per_suite};
+ (_, Default) -> Default
+ end),
+ [{require, dummy3}, {default_config, dummy3, "init_per_suite/0"},
+ {timetrap,3000}].
+
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ done.
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [tc1].
+
+tc1(_) ->
+ exit(should_never_run).
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_11_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_11_SUITE.erl
new file mode 100644
index 0000000000..ce94533110
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_11_SUITE.erl
@@ -0,0 +1,134 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(cfg_error_11_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,2}},
+ {require, dummy0}, {default_config, dummy0, "suite/0"},
+ {require, dummy1}, {default_config, dummy1, "suite/0"},
+ {require, dummy2}, {default_config, dummy2, "suite/0"}].
+
+
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite() ->
+ put('$test_server_framework_test',
+ fun(end_tc, _Default) -> {fail,fail_end_per_suite};
+ (_, Default) -> Default
+ end),
+ [{require, dummy3}, {default_config, dummy3, "end_per_suite/0"},
+ {timetrap,3000}].
+
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ done.
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [tc1, tc2].
+
+tc1() ->
+ [{require, dummy0}, {default_config, dummy0, "tc1"}].
+
+tc1(_) ->
+ dummy.
+
+tc2() ->
+ [{timetrap,1}].
+
+tc2(_) ->
+ dummy.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl
new file mode 100644
index 0000000000..806d3caf72
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_12_SUITE.erl
@@ -0,0 +1,88 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(cfg_error_12_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+init_per_testcase(_, Config) ->
+ Config.
+
+end_per_testcase(tc2, _Config) ->
+ timer:sleep(2000),
+ exit(this_should_not_be_printed);
+end_per_testcase(tc4, _Config) ->
+ timer:sleep(2000),
+ exit(this_should_not_be_printed);
+end_per_testcase(_, _) ->
+ ok.
+
+all() ->
+ [tc1, tc2, tc3, tc4].
+
+%%%-----------------------------------------------------------------
+tc1() ->
+ put('$test_server_framework_test',
+ fun(init_tc, _Default) ->
+ ct:pal("init_tc(~p): Night time...",[self()]),
+ timer:sleep(2000),
+ ct:pal("init_tc(~p): Day time!",[self()]),
+ exit(this_should_not_be_printed);
+ (_, Default) -> Default
+ end),
+ [{timetrap,500}].
+
+tc1(_) ->
+ exit(this_should_not_be_printed).
+
+%%%-----------------------------------------------------------------
+tc2() ->
+ [{timetrap,500}].
+
+tc2(_) ->
+ ok.
+
+%%%-----------------------------------------------------------------
+tc3() ->
+ [{timetrap,500}].
+
+tc3(_) ->
+ put('$test_server_framework_test',
+ fun(end_tc, _Default) ->
+ ct:pal("end_tc(~p): Night time...",[self()]),
+ timer:sleep(1000),
+ ct:pal("end_tc(~p): Day time!",[self()]);
+ (_, Default) -> Default
+ end),
+ {comment,"should succeed since ct_fw cancels timetrap in end_tc"}.
+
+%%%-----------------------------------------------------------------
+tc4() ->
+ put('$test_server_framework_test',
+ fun(end_tc, _Default) ->
+ ct:pal("end_tc(~p): Night time...",[self()]),
+ timer:sleep(1000),
+ ct:pal("end_tc(~p): Day time!",[self()]);
+ (_, Default) -> Default
+ end),
+ [{timetrap,500}].
+
+tc4(_) ->
+ ok.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl
new file mode 100644
index 0000000000..c8a3c1d15e
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_13_SUITE.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(cfg_error_13_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+init_per_suite() ->
+ put('$test_server_framework_test',
+ fun(end_tc, _Default) ->
+ ct:pal("end_tc(~p): Night time...",[self()]),
+ timer:sleep(1000),
+ ct:pal("end_tc(~p): Day time!",[self()]);
+ (_, Default) -> Default
+ end),
+ [{timetrap,500}].
+
+init_per_suite(Config) ->
+ ct:comment("should succeed since ct_fw cancels timetrap in end_tc"),
+ Config.
+
+end_per_suite(_) ->
+ ok.
+
+all() ->
+ [tc1].
+
+%%%-----------------------------------------------------------------
+tc1(_) ->
+ dummy.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl
new file mode 100644
index 0000000000..960d0f61b0
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_14_SUITE.erl
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(cfg_error_14_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite() ->
+ put('$test_server_framework_test',
+ fun(end_tc, _Default) ->
+ ct:pal("end_tc(~p): Night time...",[self()]),
+ timer:sleep(1000),
+ ct:pal("end_tc(~p): Day time!",[self()]);
+ (_, Default) -> Default
+ end),
+ [{timetrap,500}].
+
+end_per_suite(_Config) ->
+ {comment,"should succeed since ct_fw cancels timetrap in end_tc"}.
+
+all() ->
+ [tc1].
+
+%%%-----------------------------------------------------------------
+tc1(_) ->
+ dummy.
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl
index bf01bb52d9..08c57887ef 100644
--- a/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/cfg_error_3_SUITE.erl
@@ -37,7 +37,8 @@ suite() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
timer:sleep(5000),
- Config.
+ exit(shouldnt_happen).
+% Config.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl
new file mode 100644
index 0000000000..cb3109349b
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl
@@ -0,0 +1,194 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(timetrap_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,1}}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ TabPid = spawn(fun() ->
+ ets:new(?MODULE, [named_table, set, public]),
+ ets:insert(?MODULE, {last_case,ok}),
+ receive _ -> ok end
+ end),
+ [{tab,TabPid} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ exit(?config(tab, Config), kill),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_testcase(TC, Config) ->
+ {_,_} = process_info(?config(tab, Config), priority),
+ [{_,ok}] = ets:lookup(?MODULE, last_case),
+ ets:insert(?MODULE, {last_case,fail}),
+ init_per_testcase1(TC, Config).
+
+init_per_testcase1(tc1, Config) ->
+ [{tc,tc1}|Config];
+
+init_per_testcase1(tc2, Config) ->
+ [{tc,tc2}|Config];
+
+init_per_testcase1(tc3, Config) ->
+ [{tc,tc3}|Config];
+
+init_per_testcase1(tc4, Config) ->
+ [{tc,tc4},{default_timeout,5000}|Config];
+
+init_per_testcase1(tc5, Config) ->
+ [{tc,tc5}|Config];
+
+init_per_testcase1(tc6, Config) ->
+ [{tc,tc6}|Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(TC, Config) ->
+ {_,_} = process_info(?config(tab, Config), priority),
+ [{_,fail}] = ets:lookup(?MODULE, last_case),
+ ets:insert(?MODULE, {last_case,ok}),
+ end_per_testcase1(TC, Config).
+
+end_per_testcase1(tc1, Config) ->
+ ct:pal("end_per_testcase(tc1): ~p", [Config]),
+ tc1 = ?config(tc, Config),
+ {failed,timetrap_timeout} = ?config(tc_status, Config),
+ ok;
+
+end_per_testcase1(tc2, Config) ->
+ ct:pal("end_per_testcase(tc2): ~p", [Config]),
+ tc2 = ?config(tc, Config),
+ {failed,timetrap_timeout} = ?config(tc_status, Config),
+ timer:sleep(2000);
+
+end_per_testcase1(tc3, Config) ->
+ ct:pal("end_per_testcase(tc3): ~p", [Config]),
+ tc3 = ?config(tc, Config),
+ {failed,{testcase_aborted,testing_end_conf}} = ?config(tc_status, Config),
+ ok;
+
+end_per_testcase1(tc4, Config) ->
+ ct:pal("end_per_testcase(tc4): ~p", [Config]),
+ tc4 = ?config(tc, Config),
+ {failed,{testcase_aborted,testing_end_conf}} = ?config(tc_status, Config),
+ timer:sleep(2000);
+
+end_per_testcase1(tc5, Config) ->
+ ct:pal("end_per_testcase(tc5): ~p", [Config]),
+ tc5 = ?config(tc, Config),
+ exit(end_per_tc_fail_after_timeout);
+
+end_per_testcase1(tc6, Config) ->
+ ct:pal("end_per_testcase(tc6): ~p", [Config]),
+ tc6 = ?config(tc, Config),
+ exit(end_per_tc_fail_after_abort).
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [tc1, tc2, tc3, tc4, tc5, tc6].
+
+tc1(_) ->
+ timer:sleep(2000).
+
+tc2(_) ->
+ timer:sleep(2000).
+
+tc3(_) ->
+ spawn(ct, abort_current_testcase, [testing_end_conf]),
+ timer:sleep(2000).
+
+tc4(_) ->
+ spawn(ct, abort_current_testcase, [testing_end_conf]),
+ timer:sleep(2000).
+
+tc5(_) ->
+ timer:sleep(2000).
+
+tc6(_) ->
+ spawn(ct, abort_current_testcase, [testing_end_conf]),
+ timer:sleep(2000).
diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl
new file mode 100644
index 0000000000..99bb400137
--- /dev/null
+++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_2_SUITE.erl
@@ -0,0 +1,138 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(timetrap_2_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%% Info = [tuple()]
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,3}},
+ {require,multiply},
+ {require,scale}].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%%--------------------------------------------------------------------
+init_per_testcase(tc1, Config) ->
+ ct:timetrap({seconds,1}),
+ Config;
+
+init_per_testcase(tc3, Config) ->
+ ct:timetrap({seconds,1}),
+ Config;
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%%--------------------------------------------------------------------
+end_per_testcase(_, Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%%--------------------------------------------------------------------
+groups() ->
+ [].
+
+%%--------------------------------------------------------------------
+%% Function: all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%%--------------------------------------------------------------------
+all() ->
+ [tc0,tc1,tc2].
+
+tc0(_) ->
+ N = list_to_integer(ct:get_config(multiply)),
+ ct:comment(io_lib:format("TO after ~w sec", [3*N])),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc1(_) ->
+ N =list_to_integer( ct:get_config(multiply)),
+ ct:comment(io_lib:format("TO after ~w sec", [1*N])),
+ ct:sleep({seconds,5}),
+ ok.
+
+tc2(_) ->
+ N = list_to_integer(ct:get_config(multiply)),
+ ct:comment(io_lib:format("TO after ~w sec", [0.5*N])),
+ ct:timetrap(500),
+ ct:sleep(2000),
+ ok.
diff --git a/lib/common_test/test/ct_event_handler_SUITE.erl b/lib/common_test/test/ct_event_handler_SUITE.erl
index bafd32f937..5ef04c0e75 100644
--- a/lib/common_test/test/ct_event_handler_SUITE.erl
+++ b/lib/common_test/test/ct_event_handler_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
%-include_lib("common_test/include/ct_event.hrl").
@@ -56,12 +56,21 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-all(doc) ->
- [];
+suite() -> [{ct_hooks,[ts_install_cth]}].
-all(suite) ->
+all() ->
[start_stop, results].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%--------------------------------------------------------------------
%% TEST CASES
@@ -88,7 +97,7 @@ start_stop(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -110,8 +119,7 @@ start_stop(Config) when is_list(Config) ->
{eh_A,test_done,{'DEF','STOP_TIME'}},
{eh_A,stop_logging,[]}],
- ok = ct_test_support:verify_events(TestEvents, Events, Config),
- {comment,"NOTE! Known problem with test_start event!"}.
+ ok = ct_test_support:verify_events(TestEvents++TestEvents, Events, Config).
results(doc) ->
@@ -135,7 +143,7 @@ results(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -163,7 +171,7 @@ results(Config) when is_list(Config) ->
{eh_A,test_done,{'DEF','STOP_TIME'}},
{eh_A,stop_logging,[]}],
- ok = ct_test_support:verify_events(TestEvents, Events, Config).
+ ok = ct_test_support:verify_events(TestEvents++TestEvents, Events, Config).
%%%-----------------------------------------------------------------
diff --git a/lib/common_test/test/ct_event_handler_SUITE_data/eh_A.erl b/lib/common_test/test/ct_event_handler_SUITE_data/eh_A.erl
index 6e526f15a2..54cf3a22e7 100644
--- a/lib/common_test/test/ct_event_handler_SUITE_data/eh_A.erl
+++ b/lib/common_test/test/ct_event_handler_SUITE_data/eh_A.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -44,6 +44,19 @@
%% Description: Whenever a new event handler is added to an event manager,
%% this function is called to initialize the event handler.
%%--------------------------------------------------------------------
+init(String = [X|_]) when is_integer(X) ->
+ case erl_scan:string(String++".") of
+ {ok,Ts,_} ->
+ case erl_parse:parse_term(Ts) of
+ {ok,Args} ->
+ init(Args);
+ _ ->
+ init(String)
+ end;
+ _ ->
+ init(String)
+ end;
+
init(Args) ->
S1 = case lists:keysearch(cbm, 1, Args) of
diff --git a/lib/common_test/test/ct_groups_test_1_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE.erl
index 1761b773f5..7775d8a55d 100644
--- a/lib/common_test/test/ct_groups_test_1_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-define(eh, ct_test_support_eh).
@@ -56,12 +56,21 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-all(doc) ->
- ["Run smoke tests of Common Test."];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [groups_suite_1, groups_suite_2, groups_suites_1,
+ groups_dir_1, groups_dirs_1].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [groups_suite_1, groups_suite_2,
- groups_suites_1, groups_dir_1, groups_dirs_1].
%%--------------------------------------------------------------------
%% TEST CASES
@@ -76,14 +85,14 @@ groups_suite_1(Config) when is_list(Config) ->
Suite = filename:join(DataDir, "groups_1/test/groups_11_SUITE"),
{Opts,ERPid} = setup({suite,Suite}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suite_1,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(groups_suite_1),
+ TestEvents = events_to_check(groups_suite_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -96,14 +105,14 @@ groups_suite_2(Config) when is_list(Config) ->
Suite = filename:join(DataDir, "groups_1/test/groups_12_SUITE"),
{Opts,ERPid} = setup({suite,Suite}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suite_2,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(groups_suite_2),
+ TestEvents = events_to_check(groups_suite_2),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -117,14 +126,14 @@ groups_suites_1(Config) when is_list(Config) ->
filename:join(DataDir, "groups_1/test/groups_12_SUITE")],
{Opts,ERPid} = setup({suite,Suites}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_suites_1,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(groups_suites_1),
+ TestEvents = events_to_check(groups_suites_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -137,14 +146,14 @@ groups_dir_1(Config) when is_list(Config) ->
Dir = filename:join(DataDir, "groups_1"),
{Opts,ERPid} = setup({dir,Dir}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_dir_1,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(groups_dir_1),
+ TestEvents = events_to_check(groups_dir_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -157,14 +166,14 @@ groups_dirs_1(Config) when is_list(Config) ->
filename:join(DataDir, "groups_2")],
{Opts,ERPid} = setup({dir,Dirs}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(groups_dirs_1,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(groups_dirs_1),
+ TestEvents = events_to_check(groups_dirs_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -188,6 +197,14 @@ reformat(Events, EH) ->
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
test_events(groups_suite_1) ->
[{?eh,start_logging,{'DEF','RUNDIR'}},
@@ -327,14 +344,14 @@ test_events(groups_suite_2) ->
{?eh,tc_start,{groups_12_SUITE,testcase_2a}},
{?eh,tc_done,{groups_12_SUITE,testcase_2a,ok}},
- [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
{?eh,tc_done,{groups_12_SUITE,testcase_3a,ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3b}},
{?eh,tc_done,{groups_12_SUITE,testcase_3b,ok}},
- {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]},ok}}],
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]},ok}}],
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[]},ok}},
@@ -361,12 +378,8 @@ test_events(groups_suite_2) ->
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
-
- %% the done event could come in during the parallel subgroup
- %% and we can't test that, yet...
- %% {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
- %% {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
-
+ {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
@@ -525,14 +538,14 @@ test_events(groups_suites_1) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_2a}},
{?eh,tc_done,{groups_12_SUITE,testcase_2a,ok}},
- [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
{?eh,tc_done,{groups_12_SUITE,testcase_3a,ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3b}},
{?eh,tc_done,{groups_12_SUITE,testcase_3b,ok}},
- {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]},ok}}],
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]},ok}}],
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
@@ -555,12 +568,8 @@ test_events(groups_suites_1) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_4,[]},ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
-
- %% the done event could come in during the parallel subgroup
- %% and we can't test that, yet...
- %% {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
- %% {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
-
+ {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,[sequence]}}},
@@ -715,14 +724,14 @@ test_events(groups_dir_1) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_2a}},
{?eh,tc_done,{groups_12_SUITE,testcase_2a,ok}},
- [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
{?eh,tc_done,{groups_12_SUITE,testcase_3a,ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3b}},
{?eh,tc_done,{groups_12_SUITE,testcase_3b,ok}},
- {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]},ok}}],
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]},ok}}],
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
@@ -745,12 +754,8 @@ test_events(groups_dir_1) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_4,[]},ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
-
- %% the done event could come in during the parallel subgroup
- %% and we can't test that, yet...
- %% {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
- %% {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
-
+ {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,[sequence]}}},
@@ -906,14 +911,14 @@ test_events(groups_dirs_1) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_2a}},
{?eh,tc_done,{groups_12_SUITE,testcase_2a,ok}},
- [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,1}]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
{?eh,tc_done,{groups_12_SUITE,testcase_3a,ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3b}},
{?eh,tc_done,{groups_12_SUITE,testcase_3b,ok}},
- {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]}}},
- {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,1}]},ok}}],
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]},ok}}],
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_3,[]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_3,[]},ok}},
{?eh,tc_start,{groups_12_SUITE,testcase_3a}},
@@ -936,12 +941,8 @@ test_events(groups_dirs_1) ->
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_4,[]},ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
-
- %% the done event could come in during the parallel subgroup
- %% and we can't test that, yet...
- %% {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
- %% {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
-
+ {?eh,tc_start,{groups_12_SUITE,testcase_5a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_5a,ok}},
{parallel,[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
{?eh,tc_done,{groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
[{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,[sequence]}}},
@@ -1138,17 +1139,17 @@ test_events(groups_dirs_1) ->
{?eh,tc_start,{groups_22_SUITE,testcase_2a}},
{?eh,tc_done,{groups_22_SUITE,testcase_2a,ok}},
[{?eh,tc_start,
- {groups_22_SUITE,{init_per_group,test_group_3,[{repeat,1}]}}},
+ {groups_22_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
{?eh,tc_done,
- {groups_22_SUITE,{init_per_group,test_group_3,[{repeat,1}]},ok}},
+ {groups_22_SUITE,{init_per_group,test_group_3,[{repeat,2}]},ok}},
{?eh,tc_start,{groups_22_SUITE,testcase_3a}},
{?eh,tc_done,{groups_22_SUITE,testcase_3a,ok}},
{?eh,tc_start,{groups_22_SUITE,testcase_3b}},
{?eh,tc_done,{groups_22_SUITE,testcase_3b,ok}},
{?eh,tc_start,
- {groups_22_SUITE,{end_per_group,test_group_3,[{repeat,1}]}}},
+ {groups_22_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}},
{?eh,tc_done,
- {groups_22_SUITE,{end_per_group,test_group_3,[{repeat,1}]},ok}}],
+ {groups_22_SUITE,{end_per_group,test_group_3,[{repeat,2}]},ok}}],
[{?eh,tc_start,
{groups_22_SUITE,{init_per_group,test_group_3,[]}}},
{?eh,tc_done,
@@ -1181,12 +1182,8 @@ test_events(groups_dirs_1) ->
{groups_22_SUITE,{init_per_group,test_group_5,[parallel]}}},
{?eh,tc_done,
{groups_22_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
-
- %% the done event could come in during the parallel subgroup
- %% and we can't test that, yet...
- %% {?eh,tc_start,{groups_22_SUITE,testcase_5a}},
- %% {?eh,tc_done,{groups_22_SUITE,testcase_5a,ok}},
-
+ {?eh,tc_start,{groups_22_SUITE,testcase_5a}},
+ {?eh,tc_done,{groups_22_SUITE,testcase_5a,ok}},
{parallel,
[{?eh,tc_start,
{groups_22_SUITE,{init_per_group,test_group_6,[parallel]}}},
diff --git a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl
index c6d50443d0..c69400e938 100644
--- a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_11_SUITE.erl
@@ -99,6 +99,7 @@ init_per_group(Group, Config) ->
{Grs,_} = grs_and_tcs(),
case lists:member(Group, Grs) of
true ->
+ ct:comment(Group),
init = ?config(suite,Config),
[{Group,Group} | Config];
false ->
@@ -109,6 +110,7 @@ end_per_group(Group, Config) ->
{Grs,_} = grs_and_tcs(),
case lists:member(Group, Grs) of
true ->
+ ct:comment(Group),
init = ?config(suite,Config),
Group = ?config(Group,Config),
ok;
diff --git a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl
index b261ef581f..ec90ef95d1 100644
--- a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_1/test/groups_12_SUITE.erl
@@ -37,7 +37,7 @@ groups() ->
{test_group_2, [parallel], [testcase_2a,
- {test_group_3, [{repeat,1}],
+ {test_group_3, [{repeat,2}],
[testcase_3a, testcase_3b]},
testcase_2b]},
@@ -102,8 +102,8 @@ init_per_group(Group, Config) ->
io_lib:format("shuffled, ~w", [S]);
{test_group_1b,[{name,test_group_1b},parallel]} -> "parallel";
{test_group_2,[{name,test_group_2},parallel]} -> "parallel";
- {test_group_3,[{name,test_group_3},{repeat,1}]} -> "repeat 1";
- {test_group_3,[{name,test_group_3}]} -> "repeat 0";
+ {test_group_3,[{name,test_group_3},{repeat,2}]} -> "repeat 2";
+ {test_group_3,[{name,test_group_3}]} -> "repeat 1";
{test_group_4,[{name,test_group_4}]} -> ok;
{test_group_5,[{name,test_group_5},parallel]} -> "parallel";
{test_group_6,[{name,test_group_6},parallel]} -> "parallel";
@@ -275,6 +275,10 @@ testcase_5a(Config) ->
test_group_5 = ?config(test_group_5,Config),
undefined = ?config(testcase_3,Config),
testcase_5a = ?config(testcase_5a,Config),
+ %% increase chance the done event will come
+ %% during execution of subgroup (could be
+ %% tricky to handle)
+ timer:sleep(3),
ok.
testcase_5b() ->
[].
diff --git a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_2/test/groups_22_SUITE.erl b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_2/test/groups_22_SUITE.erl
index 2e19cf6310..ec0adc5df0 100644
--- a/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_2/test/groups_22_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_1_SUITE_data/groups_2/test/groups_22_SUITE.erl
@@ -37,7 +37,7 @@ groups() ->
{test_group_2, [parallel], [testcase_2a,
- {test_group_3, [{repeat,1}],
+ {test_group_3, [{repeat,2}],
[testcase_3a, testcase_3b]},
testcase_2b]},
@@ -102,8 +102,8 @@ init_per_group(Group, Config) ->
io_lib:format("shuffled, ~w", [S]);
{test_group_1b,[{name,test_group_1b},parallel]} -> "parallel";
{test_group_2,[{name,test_group_2},parallel]} -> "parallel";
- {test_group_3,[{name,test_group_3},{repeat,1}]} -> "repeat 1";
- {test_group_3,[{name,test_group_3}]} -> "repeat 0";
+ {test_group_3,[{name,test_group_3},{repeat,2}]} -> "repeat 2";
+ {test_group_3,[{name,test_group_3}]} -> "repeat 1";
{test_group_4,[{name,test_group_4}]} -> ok;
{test_group_5,[{name,test_group_5},parallel]} -> "parallel";
{test_group_6,[{name,test_group_6},parallel]} -> "parallel";
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl
index 5a60d855b7..2ae63f4f99 100644
--- a/lib/common_test/test/ct_groups_test_2_SUITE.erl
+++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-define(eh, ct_test_support_eh).
@@ -56,11 +56,20 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-all(doc) ->
- ["Run smoke tests of Common Test."];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [missing_conf, repeat_1].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [missing_conf].
%%--------------------------------------------------------------------
%% TEST CASES
@@ -75,16 +84,35 @@ missing_conf(Config) when is_list(Config) ->
Suite = filename:join(DataDir, "groups_1/missing_conf_SUITE"),
{Opts,ERPid} = setup({suite,Suite}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(missing_conf_SUITE,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(missing_conf),
+ TestEvents = events_to_check(missing_conf),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+repeat_1(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ Suite = filename:join(DataDir, "groups_1/repeat_1_SUITE"),
+
+ {Opts,ERPid} = setup({suite,Suite}, Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(repeat_1,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(repeat_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
-
+
%%%-----------------------------------------------------------------
%%% HELP FUNCTIONS
%%%-----------------------------------------------------------------
@@ -105,6 +133,127 @@ reformat(Events, EH) ->
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
test_events(missing_conf) ->
- exit(must_handle_this).
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,2}},
+ {?eh,tc_start,{ct_framework,ct_init_per_group}},
+ {?eh,tc_done,{ct_framework,ct_init_per_group,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{missing_conf_SUITE,tc1}},
+ {?eh,tc_done,{missing_conf_SUITE,tc1,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{missing_conf_SUITE,tc2}},
+ {?eh,tc_done,{missing_conf_SUITE,tc2,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,{ct_framework,ct_end_per_group}},
+ {?eh,tc_done,{ct_framework,ct_end_per_group,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(repeat_1) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ {?eh,tc_start,{repeat_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{repeat_1_SUITE,init_per_suite,ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,test_group_1,[{repeat,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,test_group_1,[{repeat,2}]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_1a}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_1a,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_1b}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_1b,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,test_group_1,[{repeat,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,test_group_1,[{repeat,2}]},ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,test_group_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,test_group_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_1a}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_1a,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_1b}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_1b,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,test_group_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,test_group_1,[]},ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,test_group_2,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,test_group_2,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_2a}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_2a,ok}},
+ {?eh,test_stats,{5,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_2b}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_2b,ok}},
+ {?eh,test_stats,{6,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,test_group_2,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,test_group_2,[]},ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,test_group_3,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,test_group_3,[]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_3a}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_3a,ok}},
+ {?eh,test_stats,{7,0,{0,0}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,test_group_4,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,test_group_4,[]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_4a}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_4a,ok}},
+ {?eh,test_stats,{8,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,testcase_4b}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_4b,ok}},
+ {?eh,test_stats,{9,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,test_group_4,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,test_group_4,[]},
+ ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,testcase_3b}},
+ {?eh,tc_done,{repeat_1_SUITE,testcase_3b,ok}},
+ {?eh,test_stats,{10,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,test_group_3,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,test_group_3,[]},
+ ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{repeat_1_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/cfgs/groups_2.1.cfg b/lib/common_test/test/ct_groups_test_2_SUITE_data/cfgs/groups_2.1.cfg
new file mode 100644
index 0000000000..4928505157
--- /dev/null
+++ b/lib/common_test/test/ct_groups_test_2_SUITE_data/cfgs/groups_2.1.cfg
@@ -0,0 +1 @@
+{dummy_key, "dummy_data"}.
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_1/repeat_1_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_1/repeat_1_SUITE.erl
new file mode 100644
index 0000000000..b4b9b03ca5
--- /dev/null
+++ b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_1/repeat_1_SUITE.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(repeat_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1, [{repeat,2}], [testcase_1a,testcase_1b]},
+ {test_group_2, [{repeat,1}], [testcase_2a,testcase_2b]},
+
+ {test_group_3, [{repeat_until_all_fail,1}],
+ [testcase_3a,
+ {test_group_4, [{repeat_until_any_fail,1}],
+ [testcase_4a, testcase_4b]},
+ testcase_3b]}
+ ].
+
+all() ->
+ [
+ {group, test_group_1},
+ {group, test_group_2},
+ {group, test_group_3}
+ ].
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ Group = proplists:get_value(name,?config(tc_group_properties,Config)),
+ ct:comment(Group),
+ Config.
+
+end_per_group(_Group, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1a(_) ->
+ ok.
+testcase_1b(_) ->
+ ok.
+
+testcase_2a(_) ->
+ ok.
+testcase_2b(_) ->
+ ok.
+
+testcase_3a(_) ->
+ ok.
+testcase_3b(_) ->
+ ok.
+
+testcase_4a(_) ->
+ ok.
+testcase_4b(_) ->
+ ok.
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_21_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_21_SUITE.erl
new file mode 100644
index 0000000000..2533ac8e84
--- /dev/null
+++ b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_21_SUITE.erl
@@ -0,0 +1,281 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(groups_21_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1a, [testcase_1a,testcase_1b]},
+
+ {test_group_1b, [], [testcase_1a,testcase_1b]},
+
+ {test_group_2, [], [testcase_2a,
+
+ {test_group_3, [], [testcase_3a,
+ testcase_3b]},
+ testcase_2b]},
+
+ {test_group_4, [{test_group_5, [], [testcase_5a,
+
+ {group, test_group_6},
+
+ testcase_5b]}]},
+
+ {test_group_6, [{group, test_group_7}]},
+
+ {test_group_7, [testcase_7a,testcase_7b]}
+ ].
+
+all() ->
+ [testcase_1,
+ {group, test_group_1a},
+ {group, test_group_1b},
+ testcase_2,
+ {group, test_group_2},
+ testcase_3,
+ {group, test_group_4}].
+
+%% this func only for internal test purposes
+grs_and_tcs() ->
+ {[
+ test_group_1a, test_group_1b,
+ test_group_2, test_group_3,
+ test_group_4, test_group_5,
+ test_group_6, test_group_7
+ ],
+ [
+ testcase_1,
+ testcase_1a, testcase_1b,
+ testcase_2,
+ testcase_2a, testcase_2b,
+ testcase_3a, testcase_3b,
+ testcase_3,
+ testcase_5a, testcase_5b,
+ testcase_7a, testcase_7b
+ ]}.
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ [{suite,init}|Config].
+
+end_per_suite(Config) ->
+ init = ?config(suite,Config).
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ [{name,Group}] = ?config(tc_group_properties,Config),
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ ct:comment(io_lib:format("~w", [Group])),
+ init = ?config(suite,Config),
+ [{Group,Group} | Config];
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+end_per_group(Group, Config) ->
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ Group = ?config(Group,Config),
+ ok;
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ [{TestCase,TestCase} | Config];
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+end_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ TestCase = ?config(TestCase,Config),
+ ok;
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1() ->
+ [].
+testcase_1(Config) ->
+ init = ?config(suite,Config),
+ testcase_1 = ?config(testcase_1,Config),
+ ok.
+
+testcase_1a() ->
+ [].
+testcase_1a(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ testcase_1a = ?config(testcase_1a,Config),
+ ok.
+testcase_1b() ->
+ [].
+testcase_1b(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1a,Config),
+ testcase_1b = ?config(testcase_1b,Config),
+ ok.
+
+testcase_2() ->
+ [].
+testcase_2(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_1a,Config),
+ undefined = ?config(test_group_1b,Config),
+ testcase_2 = ?config(testcase_2,Config),
+ ok.
+
+testcase_2a() ->
+ [].
+testcase_2a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ testcase_2a = ?config(testcase_2a,Config),
+ ok.
+testcase_2b() ->
+ [].
+testcase_2b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ undefined = ?config(testcase_2a,Config),
+ testcase_2b = ?config(testcase_2b,Config),
+ ok.
+
+testcase_3a() ->
+ [].
+testcase_3a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_2b,Config),
+ testcase_3a = ?config(testcase_3a,Config),
+ ok.
+testcase_3b() ->
+ [].
+testcase_3b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_3a,Config),
+ testcase_3b = ?config(testcase_3b,Config),
+ ok.
+
+testcase_3() ->
+ [].
+testcase_3(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_2,Config),
+ undefined = ?config(test_group_3,Config),
+ testcase_3 = ?config(testcase_3,Config),
+ ok.
+
+testcase_5a() ->
+ [].
+testcase_5a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_3,Config),
+ testcase_5a = ?config(testcase_5a,Config),
+ ok.
+testcase_5b() ->
+ [].
+testcase_5b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_5a,Config),
+ testcase_5b = ?config(testcase_5b,Config),
+ ok.
+
+testcase_7a() ->
+ [].
+testcase_7a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ testcase_7a = ?config(testcase_7a,Config),
+ ok.
+testcase_7b() ->
+ [].
+testcase_7b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ undefined = ?config(testcase_7a,Config),
+ testcase_7b = ?config(testcase_7b,Config),
+ ok.
diff --git a/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl
new file mode 100644
index 0000000000..cd517876df
--- /dev/null
+++ b/lib/common_test/test/ct_groups_test_2_SUITE_data/groups_2/groups_22_SUITE.erl
@@ -0,0 +1,314 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(groups_22_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]},
+
+ {test_group_1b, [parallel], [testcase_1a,testcase_1b]},
+
+ {test_group_2, [parallel], [testcase_2a,
+
+ {test_group_3, [{repeat,1}],
+ [testcase_3a, testcase_3b]},
+
+ testcase_2b]},
+
+ {test_group_4, [{test_group_5, [parallel], [testcase_5a,
+
+ {group, test_group_6},
+
+ testcase_5b]}]},
+
+ {test_group_6, [parallel], [{group, test_group_7}]},
+
+ {test_group_7, [sequence], [testcase_7a,testcase_7b]}
+ ].
+
+all() ->
+ [{group, test_group_1a},
+ {group, test_group_1b},
+ testcase_1,
+ testcase_2,
+ {group, test_group_2},
+ testcase_3,
+ {group, test_group_4}].
+
+%% this func only for internal test purposes
+grs_and_tcs() ->
+ {[
+ test_group_1a, test_group_1b,
+ test_group_2, test_group_3,
+ test_group_4, test_group_5,
+ test_group_6, test_group_7
+ ],
+ [
+ testcase_1a, testcase_1b, testcase_1c,
+ testcase_1,
+ testcase_2,
+ testcase_2a, testcase_2b,
+ testcase_3a, testcase_3b,
+ testcase_3,
+ testcase_5a, testcase_5b,
+ testcase_7a, testcase_7b
+ ]}.
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ [{suite,init}|Config].
+
+end_per_suite(Config) ->
+ init = ?config(suite,Config).
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ Cmt =
+ case {Group,?config(tc_group_properties,Config)} of
+ {test_group_1a,[{shuffle,S},{name,test_group_1a}]} ->
+ io_lib:format("shuffled, ~w", [S]);
+ {test_group_1b,[{name,test_group_1b},parallel]} -> "parallel";
+ {test_group_2,[{name,test_group_2},parallel]} -> "parallel";
+ {test_group_3,[{name,test_group_3},{repeat,1}]} -> "repeat 1";
+ {test_group_3,[{name,test_group_3}]} -> "repeat 0";
+ {test_group_4,[{name,test_group_4}]} -> ok;
+ {test_group_5,[{name,test_group_5},parallel]} -> "parallel";
+ {test_group_6,[{name,test_group_6},parallel]} -> "parallel";
+ {test_group_7,[{name,test_group_7},sequence]} -> "sequence"
+ end,
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ ct:comment(io_lib:format("~w, ~s", [Group,Cmt])),
+ [{Group,Group} | Config];
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+end_per_group(Group, Config) ->
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ Group = ?config(Group,Config),
+ ok;
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ [{TestCase,TestCase} | Config];
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+end_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ TestCase = ?config(TestCase,Config),
+ ok;
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1a() ->
+ [].
+testcase_1a(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ testcase_1a = ?config(testcase_1a,Config),
+ ok.
+testcase_1b() ->
+ [].
+testcase_1b(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1a,Config),
+ testcase_1b = ?config(testcase_1b,Config),
+ ok.
+
+testcase_1c() ->
+ [].
+testcase_1c(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1b,Config),
+ testcase_1c = ?config(testcase_1c,Config),
+ ok.
+
+testcase_1() ->
+ [].
+testcase_1(Config) ->
+ init = ?config(suite,Config),
+ testcase_1 = ?config(testcase_1,Config),
+ ok.
+
+testcase_2() ->
+ [].
+testcase_2(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_1a,Config),
+ undefined = ?config(test_group_1b,Config),
+ testcase_2 = ?config(testcase_2,Config),
+ ok.
+
+testcase_2a() ->
+ [].
+testcase_2a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ testcase_2a = ?config(testcase_2a,Config),
+ ok.
+testcase_2b() ->
+ [].
+testcase_2b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ undefined = ?config(testcase_2a,Config),
+ testcase_2b = ?config(testcase_2b,Config),
+ ok.
+
+testcase_3a() ->
+ [].
+testcase_3a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_2b,Config),
+ testcase_3a = ?config(testcase_3a,Config),
+ ok.
+testcase_3b() ->
+ [].
+testcase_3b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_3a,Config),
+ testcase_3b = ?config(testcase_3b,Config),
+ ok.
+
+testcase_3() ->
+ [].
+testcase_3(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_2,Config),
+ undefined = ?config(test_group_3,Config),
+ testcase_3 = ?config(testcase_3,Config),
+ ok.
+
+testcase_5a() ->
+ [].
+testcase_5a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_3,Config),
+ testcase_5a = ?config(testcase_5a,Config),
+ %% increase chance the done event will come
+ %% during execution of subgroup (could be
+ %% tricky to handle)
+ timer:sleep(3),
+ ok.
+testcase_5b() ->
+ [].
+testcase_5b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_5a,Config),
+ testcase_5b = ?config(testcase_5b,Config),
+ ok.
+
+testcase_7a() ->
+ [].
+testcase_7a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ testcase_7a = ?config(testcase_7a,Config),
+ ok.
+testcase_7b() ->
+ [].
+testcase_7b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ undefined = ?config(testcase_7a,Config),
+ testcase_7b = ?config(testcase_7b,Config),
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl
new file mode 100644
index 0000000000..64f4e277ff
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE.erl
@@ -0,0 +1,1021 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_error_SUITE
+%%%
+%%% Description:
+%%% Test various errors in Common Test suites.
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_hooks_SUITE).
+
+-compile(export_all).
+
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ DataDir = ?config(data_dir, Config),
+ TestDir = filename:join(DataDir,"cth/tests/"),
+ CTHs = filelib:wildcard(filename:join(TestDir,"*_cth.erl")),
+ io:format("CTHs: ~p",[CTHs]),
+ [io:format("Compiling ~p: ~p",
+ [FileName,compile:file(FileName,[{outdir,TestDir},debug_info])]) ||
+ FileName <- CTHs],
+ ct_test_support:init_per_suite([{path_dirs,[TestDir]} | Config]).
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+
+suite() ->
+ [{timetrap,{seconds,20}}].
+
+all() ->
+ all(suite).
+
+all(suite) ->
+ lists:reverse(
+ [
+ one_cth, two_cth, faulty_cth_no_init, faulty_cth_id_no_init,
+ faulty_cth_exit_in_init, faulty_cth_exit_in_id,
+ faulty_cth_exit_in_init_scope_suite, minimal_cth,
+ minimal_and_maximal_cth, faulty_cth_undef,
+ scope_per_suite_cth, scope_per_group_cth, scope_suite_cth,
+ scope_per_suite_state_cth, scope_per_group_state_cth,
+ scope_suite_state_cth,
+ fail_pre_suite_cth, fail_post_suite_cth, skip_pre_suite_cth,
+ skip_post_suite_cth, recover_post_suite_cth, update_config_cth,
+ state_update_cth, options_cth, same_id_cth,
+ fail_n_skip_with_minimal_cth
+ ]
+ )
+ .
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+one_cth(Config) when is_list(Config) ->
+ do_test(one_empty_cth, "ct_cth_empty_SUITE.erl",[empty_cth], Config).
+
+two_cth(Config) when is_list(Config) ->
+ do_test(two_empty_cth, "ct_cth_empty_SUITE.erl",[empty_cth,empty_cth],
+ Config).
+
+faulty_cth_no_init(Config) when is_list(Config) ->
+ do_test(faulty_cth_no_init, "ct_cth_empty_SUITE.erl",[askjhdkljashdkaj],
+ Config,{error,"Failed to start CTH, see the "
+ "CT Log for details"}).
+
+faulty_cth_id_no_init(Config) when is_list(Config) ->
+ do_test(faulty_cth_id_no_init, "ct_cth_empty_SUITE.erl",[id_no_init_cth],
+ Config,{error,"Failed to start CTH, see the "
+ "CT Log for details"}).
+
+minimal_cth(Config) when is_list(Config) ->
+ do_test(minimal_cth, "ct_cth_empty_SUITE.erl",[minimal_cth],Config).
+
+minimal_and_maximal_cth(Config) when is_list(Config) ->
+ do_test(minimal_and_maximal_cth, "ct_cth_empty_SUITE.erl",
+ [minimal_cth, empty_cth],Config).
+
+faulty_cth_undef(Config) when is_list(Config) ->
+ do_test(faulty_cth_undef, "ct_cth_empty_SUITE.erl",
+ [undef_cth],Config).
+
+faulty_cth_exit_in_init_scope_suite(Config) when is_list(Config) ->
+ do_test(faulty_cth_exit_in_init_scope_suite,
+ "ct_exit_in_init_scope_suite_cth_SUITE.erl",
+ [],Config).
+
+faulty_cth_exit_in_init(Config) when is_list(Config) ->
+ do_test(faulty_cth_exit_in_init, "ct_cth_empty_SUITE.erl",
+ [crash_init_cth], Config,
+ {error,"Failed to start CTH, see the "
+ "CT Log for details"}).
+
+faulty_cth_exit_in_id(Config) when is_list(Config) ->
+ do_test(faulty_cth_exit_in_id, "ct_cth_empty_SUITE.erl",
+ [crash_id_cth], Config,
+ {error,"Failed to start CTH, see the "
+ "CT Log for details"}).
+
+scope_per_suite_cth(Config) when is_list(Config) ->
+ do_test(scope_per_suite_cth, "ct_scope_per_suite_cth_SUITE.erl",
+ [],Config).
+
+scope_suite_cth(Config) when is_list(Config) ->
+ do_test(scope_suite_cth, "ct_scope_suite_cth_SUITE.erl",
+ [],Config).
+
+scope_per_group_cth(Config) when is_list(Config) ->
+ do_test(scope_per_group_cth, "ct_scope_per_group_cth_SUITE.erl",
+ [],Config).
+
+scope_per_suite_state_cth(Config) when is_list(Config) ->
+ do_test(scope_per_suite_state_cth, "ct_scope_per_suite_state_cth_SUITE.erl",
+ [],Config).
+
+scope_suite_state_cth(Config) when is_list(Config) ->
+ do_test(scope_suite_state_cth, "ct_scope_suite_state_cth_SUITE.erl",
+ [],Config).
+
+scope_per_group_state_cth(Config) when is_list(Config) ->
+ do_test(scope_per_group_state_cth, "ct_scope_per_group_state_cth_SUITE.erl",
+ [],Config).
+
+fail_pre_suite_cth(Config) when is_list(Config) ->
+ do_test(fail_pre_suite_cth, "ct_cth_empty_SUITE.erl",
+ [fail_pre_suite_cth],Config).
+
+fail_post_suite_cth(Config) when is_list(Config) ->
+ do_test(fail_post_suite_cth, "ct_cth_empty_SUITE.erl",
+ [fail_post_suite_cth],Config).
+
+skip_pre_suite_cth(Config) when is_list(Config) ->
+ do_test(skip_pre_suite_cth, "ct_cth_empty_SUITE.erl",
+ [skip_pre_suite_cth],Config).
+
+skip_post_suite_cth(Config) when is_list(Config) ->
+ do_test(skip_post_suite_cth, "ct_cth_empty_SUITE.erl",
+ [skip_post_suite_cth],Config).
+
+recover_post_suite_cth(Config) when is_list(Config) ->
+ do_test(recover_post_suite_cth, "ct_cth_fail_per_suite_SUITE.erl",
+ [recover_post_suite_cth],Config).
+
+update_config_cth(Config) when is_list(Config) ->
+ do_test(update_config_cth, "ct_update_config_SUITE.erl",
+ [update_config_cth],Config).
+
+state_update_cth(Config) when is_list(Config) ->
+ do_test(state_update_cth, "ct_cth_fail_one_skip_one_SUITE.erl",
+ [state_update_cth,state_update_cth],Config).
+
+options_cth(Config) when is_list(Config) ->
+ do_test(options_cth, "ct_cth_empty_SUITE.erl",
+ [{empty_cth,[test]}],Config).
+
+same_id_cth(Config) when is_list(Config) ->
+ do_test(same_id_cth, "ct_cth_empty_SUITE.erl",
+ [same_id_cth,same_id_cth],Config).
+
+fail_n_skip_with_minimal_cth(Config) when is_list(Config) ->
+ do_test(fail_n_skip_with_minimal_cth, "ct_cth_fail_one_skip_one_SUITE.erl",
+ [minimal_terminate_cth],Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+do_test(Tag, SWC, CTHs, Config) ->
+ do_test(Tag, SWC, CTHs, Config, ok).
+do_test(Tag, SWC, CTHs, Config, {error,_} = Res) ->
+ do_test(Tag, SWC, CTHs, Config, Res, 1);
+do_test(Tag, SWC, CTHs, Config, Res) ->
+ do_test(Tag, SWC, CTHs, Config, Res, 2).
+
+do_test(Tag, SuiteWildCard, CTHs, Config, Res, EC) ->
+
+ DataDir = ?config(data_dir, Config),
+ Suites = filelib:wildcard(
+ filename:join([DataDir,"cth/tests",SuiteWildCard])),
+ {Opts,ERPid} = setup([{suite,Suites},
+ {ct_hooks,CTHs},{label,Tag}], Config),
+ Res = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(Tag,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(Tag, EC),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+%reformat(Events, _EH) ->
+% Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(one_empty_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,cth,{empty_cth,init,[{'_','_','_'},[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,[test_case,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(two_empty_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(faulty_cth_no_init) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(faulty_cth_id_no_init) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {negative,{?eh,tc_start,'_'},
+ {?eh,test_done,{'DEF','STOP_TIME'}}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(minimal_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {negative,{?eh,cth,{'_',id,['_',[]]}},
+ {?eh,cth,{'_',init,['_',[]]}}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(minimal_and_maximal_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {negative,{?eh,cth,{'_',id,['_',[]]}},
+ {?eh,cth,{'_',init,['_',[]]}}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(faulty_cth_undef) ->
+ FailReasonStr = "undef_cth:pre_init_per_suite/3 CTH call failed",
+ FailReason = {ct_cth_empty_SUITE,init_per_suite,
+ {failed,FailReasonStr}},
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,
+ {failed, {error,FailReasonStr}}}},
+ {?eh,cth,{'_',on_tc_fail,'_'}},
+
+ {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,
+ {failed, FailReason}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+
+ {?eh,tc_auto_skip,{ct_cth_empty_SUITE,end_per_suite,
+ {failed, FailReason}}},
+ {?eh,cth,{'_',on_tc_skip,'_'}},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(faulty_cth_exit_in_init_scope_suite) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',init_per_suite}},
+ {?eh,cth,{empty_cth,init,['_',[]]}},
+ {?eh,tc_done,
+ {ct_exit_in_init_scope_suite_cth_SUITE,init_per_suite,
+ {failed,
+ {error,
+ "Failed to start CTH, see the CT Log for details"}}}},
+ {?eh,tc_auto_skip,
+ {ct_exit_in_init_scope_suite_cth_SUITE,test_case,
+ {failed,
+ {ct_exit_in_init_scope_suite_cth_SUITE,init_per_suite,
+ {failed,
+ "Failed to start CTH, see the CT Log for details"}}}}},
+ {?eh,tc_auto_skip,
+ {ct_exit_in_init_scope_suite_cth_SUITE,end_per_suite,
+ {failed,
+ {ct_exit_in_init_scope_suite_cth_SUITE,init_per_suite,
+ {failed,
+ "Failed to start CTH, see the CT Log for details"}}}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(faulty_cth_exit_in_init) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{empty_cth,init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(faulty_cth_exit_in_id) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{empty_cth,id,[[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {negative, {?eh,tc_start,'_'},
+ {?eh,test_done,{'DEF','STOP_TIME'}}},
+ {?eh,stop_logging,[]}];
+
+test_events(scope_per_suite_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_scope_per_suite_cth_SUITE,init_per_suite}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_scope_per_suite_cth_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_scope_per_suite_cth_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_scope_per_suite_cth_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_scope_per_suite_cth_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_scope_per_suite_cth_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,
+ [ct_scope_per_suite_cth_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_scope_per_suite_cth_SUITE,'$proplist','_',[]]}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,tc_done,{ct_scope_per_suite_cth_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(scope_suite_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_scope_suite_cth_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_scope_suite_cth_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_scope_suite_cth_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_scope_suite_cth_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_scope_suite_cth_SUITE,'$proplist','_',[]]}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,tc_done,{ct_scope_suite_cth_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(scope_per_group_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_scope_per_group_cth_SUITE,init_per_suite}},
+ {?eh,tc_done,{ct_scope_per_group_cth_SUITE,init_per_suite,ok}},
+
+ [{?eh,tc_start,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]}}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_scope_per_group_cth_SUITE,{init_per_group,group1,[]},ok}},
+
+ {?eh,tc_start,{ct_scope_per_group_cth_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_scope_per_group_cth_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]}}},
+ {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[]]}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,tc_done,{ct_scope_per_group_cth_SUITE,{end_per_group,group1,[]},ok}}],
+
+ {?eh,tc_start,{ct_scope_per_group_cth_SUITE,end_per_suite}},
+ {?eh,tc_done,{ct_scope_per_group_cth_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(scope_per_suite_state_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,init_per_suite}},
+ {?eh,cth,{'_',id,[[test]]}},
+ {?eh,cth,{'_',init,['_',[test]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_scope_per_suite_state_cth_SUITE,'$proplist','$proplist',[test]]}},
+ {?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}},
+ {?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_scope_per_suite_state_cth_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,
+ [ct_scope_per_suite_state_cth_SUITE,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_scope_per_suite_state_cth_SUITE,'$proplist','_',[test]]}},
+ {?eh,cth,{'_',terminate,[[test]]}},
+ {?eh,tc_done,{ct_scope_per_suite_state_cth_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(scope_suite_state_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_scope_suite_state_cth_SUITE,init_per_suite}},
+ {?eh,cth,{'_',id,[[test]]}},
+ {?eh,cth,{'_',init,['_',[test]]}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist','$proplist',[test]]}},
+ {?eh,tc_done,{ct_scope_suite_state_cth_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_scope_suite_state_cth_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}},
+ {?eh,tc_done,{ct_scope_suite_state_cth_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_scope_suite_state_cth_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_scope_suite_state_cth_SUITE,'$proplist','_',[test]]}},
+ {?eh,cth,{'_',terminate,[[test]]}},
+ {?eh,tc_done,{ct_scope_suite_state_cth_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(scope_per_group_state_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,init_per_suite}},
+ {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,init_per_suite,ok}},
+
+ [{?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,{init_per_group,group1,[]}}},
+ {?eh,cth,{'_',id,[[test]]}},
+ {?eh,cth,{'_',init,['_',[test]]}},
+ {?eh,cth,{'_',post_init_per_group,[group1,'$proplist','$proplist',[test]]}},
+ {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,{init_per_group,group1,[]},ok}},
+
+ {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[test]]}},
+ {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,{end_per_group,group1,[]}}},
+ {?eh,cth,{'_',pre_end_per_group,[group1,'$proplist',[test]]}},
+ {?eh,cth,{'_',post_end_per_group,[group1,'$proplist','_',[test]]}},
+ {?eh,cth,{'_',terminate,[[test]]}},
+ {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,{end_per_group,group1,[]},ok}}],
+
+ {?eh,tc_start,{ct_scope_per_group_state_cth_SUITE,end_per_suite}},
+ {?eh,tc_done,{ct_scope_per_group_state_cth_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(fail_pre_suite_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist',
+ {fail,"Test failure"},[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,
+ {failed, {error,"Test failure"}}}},
+ {?eh,cth,{'_',on_tc_fail,
+ [init_per_suite,{failed,"Test failure"},[]]}},
+
+
+ {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,
+ {failed,{ct_cth_empty_SUITE,init_per_suite,
+ {failed,"Test failure"}}}}},
+ {?eh,cth,{'_',on_tc_skip,
+ [test_case, {tc_auto_skip,
+ {failed, {ct_cth_empty_SUITE, init_per_suite,
+ {failed, "Test failure"}}}},[]]}},
+
+
+ {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,
+ {failed, {ct_cth_empty_SUITE, init_per_suite,
+ {failed, "Test failure"}}}}},
+ {?eh,cth,{'_',on_tc_skip,
+ [end_per_suite, {tc_auto_skip,
+ {failed, {ct_cth_empty_SUITE, init_per_suite,
+ {failed, "Test failure"}}}},[]]}},
+
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth, {'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(fail_post_suite_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,
+ {failed,{error,"Test failure"}}}},
+ {?eh,cth,{'_',on_tc_fail,[init_per_suite, {failed,"Test failure"}, []]}},
+
+ {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,
+ {failed,{ct_cth_empty_SUITE,init_per_suite,
+ {failed,"Test failure"}}}}},
+ {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,'_'},[]]}},
+
+ {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,
+ {failed, {ct_cth_empty_SUITE, init_per_suite,
+ {failed, "Test failure"}}}}},
+ {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,'_'},[]]}},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth, {'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(skip_pre_suite_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist',{skip,"Test skip"},[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,{skipped,"Test skip"}}},
+ {?eh,cth,{'_',on_tc_skip,
+ [init_per_suite,{tc_user_skip,{skipped,"Test skip"}},[]]}},
+
+ {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,"Test skip"}},
+ {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,"Test skip"},[]]}},
+
+ {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,"Test skip"}},
+ {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,"Test skip"},[]]}},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth, {'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(skip_post_suite_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,[ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,{skipped,"Test skip"}}},
+ {?eh,cth,{'_',on_tc_skip,
+ [init_per_suite,{tc_user_skip,{skipped,"Test skip"}},[]]}},
+
+ {?eh,tc_auto_skip,{ct_cth_empty_SUITE,test_case,"Test skip"}},
+ {?eh,cth,{'_',on_tc_skip,[test_case,{tc_auto_skip,"Test skip"},[]]}},
+
+ {?eh,tc_auto_skip, {ct_cth_empty_SUITE, end_per_suite,"Test skip"}},
+ {?eh,cth,{'_',on_tc_skip,[end_per_suite,{tc_auto_skip,"Test skip"},[]]}},
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(recover_post_suite_cth) ->
+ Suite = ct_cth_fail_per_suite_SUITE,
+ [
+ {?eh,start_logging,'_'},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{Suite,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[Suite,'$proplist','$proplist']}},
+ {?eh,cth,{'_',post_init_per_suite,[Suite,contains([tc_status]),
+ {'EXIT',{'_','_'}},[]]}},
+ {?eh,tc_done,{Suite,init_per_suite,ok}},
+
+ {?eh,tc_start,{Suite,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,
+ [test_case, not_contains([tc_status]),[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [test_case, contains([tc_status]),'_',[]]}},
+ {?eh,tc_done,{Suite,test_case,ok}},
+
+ {?eh,tc_start,{Suite,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,
+ [Suite,not_contains([tc_status]),[]]}},
+ {?eh,cth,{'_',post_end_per_suite,
+ [Suite,not_contains([tc_status]),'_',[]]}},
+ {?eh,tc_done,{Suite,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(update_config_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+
+ {?eh,tc_start,{ct_update_config_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,
+ [ct_update_config_SUITE,contains([]),[]]}},
+ {?eh,cth,{'_',post_init_per_suite,
+ [ct_update_config_SUITE,
+ '$proplist',
+ contains(
+ [init_per_suite,
+ pre_init_per_suite]),
+ []]}},
+ {?eh,tc_done,{ct_update_config_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_update_config_SUITE, {init_per_group,group1,[]}}},
+ {?eh,cth,{'_',pre_init_per_group,
+ [group1,contains(
+ [post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ []]}},
+ {?eh,cth,{'_',post_init_per_group,
+ [group1,
+ contains(
+ [post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ contains(
+ [init_per_group,
+ pre_init_per_group,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ []]}},
+ {?eh,tc_done,{ct_update_config_SUITE,{init_per_group,group1,[]},ok}},
+
+ {?eh,tc_start,{ct_update_config_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,
+ [test_case,contains(
+ [post_init_per_group,
+ init_per_group,
+ pre_init_per_group,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ []]}},
+ {?eh,cth,{'_',post_end_per_testcase,
+ [test_case,contains(
+ [init_per_testcase,
+ pre_init_per_testcase,
+ post_init_per_group,
+ init_per_group,
+ pre_init_per_group,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ ok,[]]}},
+ {?eh,tc_done,{ct_update_config_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_update_config_SUITE, {end_per_group,group1,[]}}},
+ {?eh,cth,{'_',pre_end_per_group,
+ [group1,contains(
+ [post_init_per_group,
+ init_per_group,
+ pre_init_per_group,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ []]}},
+ {?eh,cth,{'_',post_end_per_group,
+ [group1,
+ contains(
+ [pre_end_per_group,
+ post_init_per_group,
+ init_per_group,
+ pre_init_per_group,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ ok,[]]}},
+ {?eh,tc_done,{ct_update_config_SUITE,{end_per_group,group1,[]},ok}},
+
+ {?eh,tc_start,{ct_update_config_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,
+ [ct_update_config_SUITE,contains(
+ [post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ []]}},
+ {?eh,cth,{'_',post_end_per_suite,
+ [ct_update_config_SUITE,contains(
+ [pre_end_per_suite,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite]),
+ '_',[]]}},
+ {?eh,tc_done,{ct_update_config_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[contains(
+ [post_end_per_suite,
+ pre_end_per_suite,
+ post_init_per_suite,
+ init_per_suite,
+ pre_init_per_suite])]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(state_update_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',init_per_suite}},
+
+ {?eh,tc_done,{'_',end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[contains(
+ [post_end_per_suite,pre_end_per_suite,
+ post_end_per_group,pre_end_per_group,
+ {not_in_order,
+ [post_end_per_testcase,pre_init_per_testcase,
+ on_tc_skip,post_end_per_testcase,
+ pre_init_per_testcase,on_tc_fail,
+ post_end_per_testcase,pre_init_per_testcase]
+ },
+ post_init_per_group,pre_init_per_group,
+ post_init_per_suite,pre_init_per_suite,
+ init])]}},
+ {?eh,cth,{'_',terminate,[contains(
+ [post_end_per_suite,pre_end_per_suite,
+ post_end_per_group,pre_end_per_group,
+ {not_in_order,
+ [post_end_per_testcase,pre_init_per_testcase,
+ on_tc_skip,post_end_per_testcase,
+ pre_init_per_testcase,on_tc_fail,
+ post_end_per_testcase,pre_init_per_testcase]
+ },
+ post_init_per_group,pre_init_per_group,
+ post_init_per_suite,pre_init_per_suite,
+ init]
+ )]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(options_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{empty_cth,init,['_',[test]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{empty_cth,pre_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist',[test]]}},
+ {?eh,cth,{empty_cth,post_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist','$proplist',[test]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,cth,{empty_cth,pre_init_per_testcase,[test_case,'$proplist',[test]]}},
+ {?eh,cth,{empty_cth,post_end_per_testcase,[test_case,'$proplist','_',[test]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,cth,{empty_cth,pre_end_per_suite,
+ [ct_cth_empty_SUITE,'$proplist',[test]]}},
+ {?eh,cth,{empty_cth,post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[test]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{empty_cth,terminate,[[test]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(same_id_cth) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,cth,{'_',init,[same_id_cth,[]]}},
+ {?eh,cth,{'_',id,[[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{ct_cth_empty_SUITE,init_per_suite}},
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {negative,
+ {?eh,cth,{'_',pre_init_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}}},
+ {negative,
+ {?eh,cth,{'_',post_init_per_suite,
+ [ct_cth_empty_SUITE,'$proplist','$proplist',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,init_per_suite,ok}}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,test_case}},
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {negative,
+ {?eh,cth,{'_',pre_init_per_testcase,[test_case,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}}},
+ {negative,
+ {?eh,cth,{'_',post_end_per_testcase,[test_case,'$proplist',ok,[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,test_case,ok}}},
+
+ {?eh,tc_start,{ct_cth_empty_SUITE,end_per_suite}},
+ {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {negative,
+ {?eh,cth,{'_',pre_end_per_suite,[ct_cth_empty_SUITE,'$proplist',[]]}},
+ {?eh,cth,{'_',post_end_per_suite,[ct_cth_empty_SUITE,'$proplist','_',[]]}}},
+ {negative,
+ {?eh,cth,{'_',post_end_per_suite,
+ [ct_cth_empty_SUITE,'$proplist','_',[]]}},
+ {?eh,tc_done,{ct_cth_empty_SUITE,end_per_suite,ok}}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(fail_n_skip_with_minimal_cth) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,cth,{'_',init,['_',[]]}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,tc_start,{'_',init_per_suite}},
+
+ {?eh,tc_done,{'_',end_per_suite,ok}},
+ {?eh,cth,{'_',terminate,[[]]}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(ok) ->
+ ok.
+
+
+%% test events help functions
+contains(List) ->
+ fun(Proplist) when is_list(Proplist) ->
+ contains(List,Proplist)
+ end.
+
+contains([{not_in_order,List}|T],Rest) ->
+ contains_parallel(List,Rest),
+ contains(T,Rest);
+contains([{Ele,Pos}|T] = L,[H|T2]) ->
+ case element(Pos,H) of
+ Ele ->
+ contains(T,T2);
+ _ ->
+ contains(L,T2)
+ end;
+contains([Ele|T],[{Ele,_}|T2])->
+ contains(T,T2);
+contains([Ele|T],[Ele|T2])->
+ contains(T,T2);
+contains(List,[_|T]) ->
+ contains(List,T);
+contains([],_) ->
+ match.
+
+contains_parallel([Key | T], Elems) ->
+ contains([Key],Elems),
+ contains_parallel(T,Elems);
+contains_parallel([],_Elems) ->
+ match.
+
+not_contains(List) ->
+ fun(Proplist) when is_list(Proplist) ->
+ [] = [Ele || {Ele,_} <- Proplist,
+ Test <- List,
+ Test =:= Ele]
+ end.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl
new file mode 100644
index 0000000000..b5541f2053
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_id_cth.erl
@@ -0,0 +1,34 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(crash_id_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([id/1]).
+
+id(Opts) ->
+ empty_cth:id(Opts),
+ exit(diediedie).
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl
new file mode 100644
index 0000000000..596b4fade0
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/crash_init_cth.erl
@@ -0,0 +1,34 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(crash_init_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([init/2]).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts),
+ exit(diediedie).
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl
new file mode 100644
index 0000000000..dcba113eab
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_empty_SUITE.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_empty_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl
new file mode 100644
index 0000000000..b2f22d8257
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_one_skip_one_SUITE.erl
@@ -0,0 +1,64 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_fail_one_skip_one_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_Group,Config) ->
+ Config.
+
+end_per_group(_Group,_Config) ->
+ ok.
+
+init_per_testcase(test_case2, Config) ->
+ {skip,"skip it"};
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+groups() ->
+ [{group1,[parallel],[{group2,[parallel],[test_case1,test_case2,test_case3]}]}].
+
+all() ->
+ [{group,group1}].
+
+%% Test cases starts here.
+test_case1(Config) ->
+ ok = nok.
+
+test_case2(Config) ->
+ ok.
+
+test_case3(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl
new file mode 100644
index 0000000000..48816523c7
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_cth_fail_per_suite_SUITE.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_cth_fail_per_suite_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ ok = nok.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl
new file mode 100644
index 0000000000..6fa77128ab
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_exit_in_init_scope_suite_cth_SUITE.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_exit_in_init_scope_suite_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[crash_init_cth]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl
new file mode 100644
index 0000000000..18af37096a
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_cth_SUITE.erl
@@ -0,0 +1,56 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_group_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(GroupName, Config) ->
+ [{ct_hooks,[empty_cth]}|Config].
+
+end_per_group(GroupName, Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl
new file mode 100644
index 0000000000..a34474ebfd
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_group_state_cth_SUITE.erl
@@ -0,0 +1,56 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_group_state_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ [{ct_hooks,[{empty_cth,[test]}]}|Config].
+
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl
new file mode 100644
index 0000000000..a3a8f2602f
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_cth_SUITE.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_suite_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{ct_hooks,[empty_cth]}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl
new file mode 100644
index 0000000000..3f643d6709
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_suite_state_cth_SUITE.erl
@@ -0,0 +1,47 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_suite_state_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{ct_hooks,[{empty_cth,[test]}]}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl
new file mode 100644
index 0000000000..1c942937eb
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_per_tc_cth_SUITE.erl
@@ -0,0 +1,110 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_per_tc_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% @doc
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%
+%% @spec init_per_suite(Config) -> Config
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Cleanup after the whole suite
+%%
+%% @spec end_per_suite(Config) -> _
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%% Initiation before each test case
+%%
+%% @spec init_per_testcase(TestCase, Config) -> Config
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ [{ct_hooks,[empty_cth]}|Config].
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Cleanup after each test case
+%%
+%% @spec end_per_testcase(TestCase, Config) -> _
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%%
+%% Returns a list of all test cases in this test suite
+%%
+%% @spec all() -> TestCases
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl
new file mode 100644
index 0000000000..482e87a54f
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_cth_SUITE.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_suite_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[empty_cth]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl
new file mode 100644
index 0000000000..7b4c9b3fab
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_scope_suite_state_cth_SUITE.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_scope_suite_state_cth_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+suite() ->
+ [{ct_hooks,[{empty_cth,[test]}]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+all() ->
+ [test_case].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
new file mode 100644
index 0000000000..3c1f5669e8
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_update_config_SUITE.erl
@@ -0,0 +1,56 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_update_config_SUITE).
+
+-suite_defaults([{timetrap, {minutes, 10}}]).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%% Test server callback functions
+init_per_suite(Config) ->
+ [{init_per_suite,now()}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ [{init_per_testcase,now()}|Config].
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+init_per_group(GroupName, Config) ->
+ [{init_per_group,now()}|Config].
+
+end_per_group(GroupName, Config) ->
+ ok.
+
+all() ->
+ [{group,group1}].
+
+groups() ->
+ [{group1,[],[test_case]}].
+
+%% Test cases starts here.
+test_case(Config) when is_list(Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
new file mode 100644
index 0000000000..ebebfd18a9
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl
@@ -0,0 +1,278 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc Common Test Example Suite Callback module.
+%%%
+%%% <p>This module gives an example of a common test CTH (Common Test Hook).
+%%% There are many ways to add a CTH to a test run, you can do it either in
+%%% the command line using -ct_hook, in a test spec using
+%%% {ct_hook,M} or in the suite it self by returning ct_hook
+%%% from either suite/0, init_per_suite/1, init_per_group/2 and
+%%% init_per_testcase/2. The scope of the CTH is determined by where is it
+%%% started. If it is started in the command line or test spec then it will
+%%% be stopped at the end of all tests. If it is started in init_per_suite,
+%%% it will be stopped after end_per_suite and so on. See terminate
+%%% documentation for a table describing the scoping machanics.
+%%%
+%%% All of callbacks except init/1 in a CTH are optional.</p>
+
+-module(empty_cth).
+
+%% CT Hooks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-type proplist() :: list({atom(),term()}).
+-type config() :: proplist().
+-type reason() :: term().
+-type skip_or_fail() :: {skip, reason()} |
+ {auto_skip, reason()} |
+ {fail, reason()} |
+ {'EXIT',reason()}.
+
+-record(state, { id = ?MODULE :: term()}).
+
+%% @doc Always called before any other callback function. Use this to initiate
+%% any common state. It should return an state for this CTH.
+-spec init(Id :: term(), Opts :: proplist()) ->
+ State :: #state{}.
+init(Id, Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, init, [Id, Opts]}}),
+ Opts.
+
+%% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
+%% return the same ID the seconds CTH is ignored. This function should NOT
+%% have any side effects as it might be called multiple times by common test.
+-spec id(Opts :: proplist()) ->
+ Id :: term().
+id(Opts) ->
+ gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, id, [Opts]}}),
+ now().
+
+%% @doc Called before init_per_suite is called. Note that this callback is
+%% only called if the CTH is added before init_per_suite is run (eg. in a test
+%% specification, suite/0 function etc).
+%% You can change the config in the this function.
+-spec pre_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_suite(Suite,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_suite,
+ [Suite,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after init_per_suite.
+%% you can change the return value in this function.
+-spec post_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_suite(Suite,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_init_per_suite,
+ [Suite,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before end_per_suite. The config/state can be changed here,
+%% though it will only affect the *end_per_suite function.
+-spec pre_end_per_suite(Suite :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_suite(Suite,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_end_per_suite,
+ [Suite,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after end_per_suite. Note that the config cannot be
+%% changed here, only the status of the suite.
+-spec post_end_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_suite(Suite,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_suite,
+ [Suite,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before each init_per_group.
+%% You can change the config in this function.
+-spec pre_init_per_group(Group :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_group(Group,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_group,
+ [Group,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each init_per_group.
+%% You can change the return value in this function.
+-spec post_init_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_group(Group,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_init_per_group,
+ [Group,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called after each end_per_group. The config/state can be changed here,
+%% though it will only affect the *end_per_group functions.
+-spec pre_end_per_group(Group :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_group(Group,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_end_per_group,
+ [Group,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each end_per_group. Note that the config cannot be
+%% changed here, only the status of the group.
+-spec post_end_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_group(Group,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_group,
+ [Group,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called before each test case.
+%% You can change the config in this function.
+-spec pre_init_per_testcase(TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_testcase(TC,Config,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, pre_init_per_testcase,
+ [TC,Config,State]}}),
+ {Config, State}.
+
+%% @doc Called after each test case. Note that the config cannot be
+%% changed here, only the status of the test case.
+-spec post_end_per_testcase(TC :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_testcase(TC,Config,Return,State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, post_end_per_testcase,
+ [TC,Config,Return,State]}}),
+ {Return, State}.
+
+%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
+%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
+%% This function should be used for extra cleanup which might be needed.
+%% It is not possible to modify the config or the status of the test run.
+-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
+ init_per_group | end_per_group | atom(),
+ Reason :: term(), State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_fail(TC, Reason, State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, on_tc_fail,
+ [TC,Reason,State]}}),
+ State.
+
+%% @doc Called when a test case is skipped by either user action
+%% or due to an init function failing. Test case can be
+%% end_per_suite, init_per_group, end_per_group and the actual test cases.
+-spec on_tc_skip(TC :: end_per_suite |
+ init_per_group | end_per_group | atom(),
+ {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_skip(TC, Reason, State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, on_tc_skip,
+ [TC,Reason,State]}}),
+ State.
+
+%% @doc Called when the scope of the CTH is done, this depends on
+%% when the CTH was specified. This translation table describes when this
+%% function is called.
+%%
+%% | Started in | terminate called |
+%% |---------------------|-------------------------|
+%% | command_line | after all tests are run |
+%% | test spec | after all tests are run |
+%% | suite/0 | after SUITE is done |
+%% | init_per_suite/1 | after SUITE is done |
+%% | init_per_group/2 | after group is done |
+%% |-----------------------------------------------|
+%%
+-spec terminate(State :: #state{}) ->
+ term().
+terminate(State) ->
+ gen_event:notify(
+ ?CT_EVMGR_REF, #event{ name = cth, node = node(),
+ data = {?MODULE, terminate, [State]}}),
+ ok.
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl
new file mode 100644
index 0000000000..5af9906df0
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_suite_cth.erl
@@ -0,0 +1,72 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(fail_post_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {{fail, "Test failure"}, State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl
new file mode 100644
index 0000000000..8227b408cd
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_pre_suite_cth.erl
@@ -0,0 +1,72 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(fail_pre_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {{fail, "Test failure"}, State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl
new file mode 100644
index 0000000000..1e222c1dbf
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/id_no_init_cth.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(id_no_init_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([id/1]).
+
+id(Opts) ->
+ empty_cth:id(Opts).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl
new file mode 100644
index 0000000000..b87da4e330
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_cth.erl
@@ -0,0 +1,33 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(minimal_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([init/2]).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl
new file mode 100644
index 0000000000..30721a6b3a
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/minimal_terminate_cth.erl
@@ -0,0 +1,38 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(minimal_terminate_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-export([init/2]).
+-export([terminate/1]).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+terminate(State) ->
+ empty_cth:terminate(State).
+
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl
new file mode 100644
index 0000000000..2629448943
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/recover_post_suite_cth.erl
@@ -0,0 +1,74 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(recover_post_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,{'EXIT',Reason} = Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {lists:keydelete(tc_status,1,Config),State};
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl
new file mode 100644
index 0000000000..49b1b9cada
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/same_id_cth.erl
@@ -0,0 +1,75 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(same_id_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+id(Opts) ->
+ empty_cth:id(Opts),
+ ?MODULE.
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl
new file mode 100644
index 0000000000..770fec0a51
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_post_suite_cth.erl
@@ -0,0 +1,72 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(skip_post_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {{skip, "Test skip"}, State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl
new file mode 100644
index 0000000000..60b1a558ae
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/skip_pre_suite_cth.erl
@@ -0,0 +1,73 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(skip_pre_suite_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {{skip, "Test skip"}, State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl
new file mode 100644
index 0000000000..35c990c0be
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/state_update_cth.erl
@@ -0,0 +1,83 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(state_update_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ State = empty_cth:init(Id, Opts),
+ [init|State].
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {Config, [pre_init_per_suite|State]}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {Config, [post_init_per_suite|State]}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State),
+ {Config, [pre_end_per_suite|State]}.
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State),
+ {Return, [post_end_per_suite|State]}.
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State),
+ {Config, [pre_init_per_group|State]}.
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State),
+ {Return, [post_init_per_group|State]}.
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State),
+ {Config, [pre_end_per_group|State]}.
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State),
+ {Return, [post_end_per_group|State]}.
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State),
+ {Config, [pre_init_per_testcase|State]}.
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State),
+ {Return, [post_end_per_testcase|State]}.
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State),
+ [on_tc_fail|State].
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State),
+ [on_tc_skip|State].
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl
new file mode 100644
index 0000000000..cd561771d5
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/undef_cth.erl
@@ -0,0 +1,71 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(undef_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(_Suite, _Config, _State) ->
+ lists:flaten([1,2,[3,4]]).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
new file mode 100644
index 0000000000..2ee0d7da9c
--- /dev/null
+++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/update_config_cth.erl
@@ -0,0 +1,82 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(update_config_cth).
+
+
+-include_lib("common_test/src/ct_util.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+
+%% CT Hooks
+-compile(export_all).
+
+init(Id, Opts) ->
+ empty_cth:init(Id, Opts).
+
+pre_init_per_suite(Suite, Config, State) ->
+ empty_cth:pre_init_per_suite(Suite,Config,State),
+ {[{pre_init_per_suite,now()}|Config],State}.
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_init_per_suite(Suite,Config,Return,State),
+ {[{post_init_per_suite,now()}|Return],State}.
+
+pre_end_per_suite(Suite,Config,State) ->
+ empty_cth:pre_end_per_suite(Suite,Config,State),
+ {[{pre_end_per_suite,now()}|Config],State}.
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ empty_cth:post_end_per_suite(Suite,Config,Return,State),
+ NewConfig = [{post_end_per_suite,now()}|Config],
+ {NewConfig,NewConfig}.
+
+pre_init_per_group(Group,Config,State) ->
+ empty_cth:pre_init_per_group(Group,Config,State),
+ {[{pre_init_per_group,now()}|Config],State}.
+
+post_init_per_group(Group,Config,Return,State) ->
+ empty_cth:post_init_per_group(Group,Config,Return,State),
+ {[{post_init_per_group,now()}|Return],State}.
+
+pre_end_per_group(Group,Config,State) ->
+ empty_cth:pre_end_per_group(Group,Config,State),
+ {[{pre_end_per_group,now()}|Config],State}.
+
+post_end_per_group(Group,Config,Return,State) ->
+ empty_cth:post_end_per_group(Group,Config,Return,State),
+ {[{post_end_per_group,now()}|Config],State}.
+
+pre_init_per_testcase(TC,Config,State) ->
+ empty_cth:pre_init_per_testcase(TC,Config,State),
+ {[{pre_init_per_testcase,now()}|Config],State}.
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ empty_cth:post_end_per_testcase(TC,Config,Return,State),
+ {[{post_end_per_testcase,now()}|Config],State}.
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl
new file mode 100644
index 0000000000..e89b6f7de6
--- /dev/null
+++ b/lib/common_test/test/ct_master_SUITE.erl
@@ -0,0 +1,223 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_master_SUITE
+%%%
+%%% Description:
+%%% Test ct_master.
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_master_SUITE).
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+-define(TEMP_DIR, case os:type() of
+ {win32,_} ->
+ "c:/Temp";
+ _ ->
+ "/tmp"
+ end).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ct_test_support:init_per_suite(Config).
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ NodeCount = 5,
+ NodeNames = [list_to_atom("t_"++integer_to_list(N)) ||
+ N <- lists:seq(1, NodeCount)],
+ ct_test_support:init_per_testcase(
+ TestCase,[{node_names,NodeNames},
+ {master, true}|Config]).
+
+end_per_testcase(TestCase, Config) ->
+ case os:type() of
+ {win32,_} ->
+ %% If this is a windows run the logs are saved to /tmp and
+ %% then moved to private_dir as a tar because otherwise
+ %% the file names become too long! :(
+ Files = filelib:wildcard(filename:join(?TEMP_DIR,"slave.*")),
+ erl_tar:create(
+ filename:join(
+ proplists:get_value(priv_dir,Config),"slaves.tar.gz"),
+ Files,[compressed]),
+ os:cmd("rm -rf "++filename:join(?TEMP_DIR,"slave.*"));
+ _ ->
+ ok
+ end,
+
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [ct_master_test].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+ct_master_test(Config) when is_list(Config)->
+ NodeNames = proplists:get_value(node_names, Config),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ FileName = filename:join(PrivDir, "ct_master_spec.spec"),
+ Suites = [master_SUITE],
+ TSFile = make_spec(DataDir, FileName, NodeNames, Suites, Config),
+ ERPid = ct_test_support:start_event_receiver(Config),
+ spawn(ct@ancalagon,
+ fun() ->
+ dbg:tracer(),dbg:p(all,c),
+ dbg:tpl(erlang, spawn_link, 4,x),
+ receive ok -> ok end
+ end),
+
+ [{TSFile, ok}] = run_test(ct_master_test, FileName, Config),
+
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(groups_suite_1,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+ find_events(NodeNames, [{tc_start,{master_SUITE,init_per_suite}},
+ {tc_start,{master_SUITE,first_testcase}},
+ {tc_start,{master_SUITE,second_testcase}},
+ {tc_start,{master_SUITE,third_testcase}},
+ {tc_start,{master_SUITE,end_per_suite}}],
+ Events),
+
+ ok.
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+make_spec(DataDir, FileName, NodeNames, Suites, Config)->
+ {ok, HostName} = inet:gethostname(),
+
+ N = lists:map(fun(NodeName)->
+ {node, NodeName, list_to_atom(atom_to_list(NodeName)++"@"++HostName)}
+ end,
+ NodeNames),
+
+ C = lists:map(fun(NodeName)->
+ Rnd = random:uniform(2),
+ if Rnd == 1->
+ {config, NodeName, filename:join(DataDir, "master/config.txt")};
+ true->
+ {userconfig, NodeName, {ct_config_xml, filename:join(DataDir, "master/config.xml")}}
+ end
+ end,
+ NodeNames),
+
+ NS = lists:map(fun(NodeName)->
+ {init, NodeName, [
+ {node_start, [{startup_functions, []}, {monitor_master, true}]},
+ {eval, {erlang, nodes, []}}
+ ]
+ }
+ end,
+ NodeNames),
+
+ S = [{suites, NodeNames, filename:join(DataDir, "master"), Suites}],
+
+ PrivDir = ?config(priv_dir, Config),
+ LD = lists:map(fun(NodeName)->
+ {logdir, NodeName, get_log_dir(os:type(),PrivDir, NodeName)}
+ end,
+ NodeNames) ++ [{logdir, master, PrivDir}],
+ EvHArgs = [{cbm,ct_test_support},{trace_level,?config(trace_level,Config)}],
+ EH = [{event_handler,master,[?eh],EvHArgs}],
+
+ Include = [{include,filename:join([DataDir,"master/include"])}],
+
+ ct_test_support:write_testspec(N++Include++EH++C++S++LD++NS, FileName).
+
+get_log_dir({win32,_},PrivDir, NodeName)->
+ case filelib:is_dir(?TEMP_DIR) of
+ false ->
+ file:make_dir(?TEMP_DIR);
+ _ ->
+ ok
+ end,
+ get_log_dir(tmp, ?TEMP_DIR,NodeName);
+get_log_dir(_,PrivDir,NodeName) ->
+ LogDir = filename:join(PrivDir, io_lib:format("slave.~p", [NodeName])),
+ file:make_dir(LogDir),
+ LogDir.
+
+run_test(_Name, FileName, Config)->
+ [{FileName, ok}] = ct_test_support:run(ct_master, run, [FileName], Config).
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+find_events([], _CheckEvents, _) ->
+ ok;
+find_events([NodeName|NodeNames],CheckEvents,AllEvents) ->
+ find_events(NodeNames, CheckEvents,
+ remove_events(add_host(NodeName),CheckEvents, AllEvents, [])).
+
+remove_events(Node,[{Name,Data} | RestChecks],
+ [{?eh,#event{ name = Name, node = Node, data = Data }}|RestEvs],
+ Acc) ->
+ remove_events(Node, RestChecks, RestEvs, Acc);
+remove_events(Node, Checks, [Event|RestEvs], Acc) ->
+ remove_events(Node, Checks, RestEvs, [Event | Acc]);
+remove_events(_Node, [], [], Acc) ->
+ lists:reverse(Acc);
+remove_events(Node, Events, [], Acc) ->
+ test_server:format("Could not find events: ~p in ~p for node ~p",
+ [Events, lists:reverse(Acc), Node]),
+ exit(event_not_found).
+
+add_host(NodeName) ->
+ {ok, HostName} = inet:gethostname(),
+ list_to_atom(atom_to_list(NodeName)++"@"++HostName).
+
+expected_events(_)->
+ [].
diff --git a/lib/common_test/test/ct_master_SUITE_data/master/config.txt b/lib/common_test/test/ct_master_SUITE_data/master/config.txt
new file mode 100644
index 0000000000..3baf9e392c
--- /dev/null
+++ b/lib/common_test/test/ct_master_SUITE_data/master/config.txt
@@ -0,0 +1,2 @@
+{a, b}.
+{c, d}.
diff --git a/lib/common_test/test/ct_master_SUITE_data/master/config.xml b/lib/common_test/test/ct_master_SUITE_data/master/config.xml
new file mode 100644
index 0000000000..c031f45f35
--- /dev/null
+++ b/lib/common_test/test/ct_master_SUITE_data/master/config.xml
@@ -0,0 +1,4 @@
+<config>
+ <a>b</a>
+ <c>d</c>
+</config>
diff --git a/lib/ssl/examples/certs/ebin/.gitignore b/lib/common_test/test/ct_master_SUITE_data/master/include/test.hrl
index e69de29bb2..e69de29bb2 100644
--- a/lib/ssl/examples/certs/ebin/.gitignore
+++ b/lib/common_test/test/ct_master_SUITE_data/master/include/test.hrl
diff --git a/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl b/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl
new file mode 100644
index 0000000000..032d69ad9f
--- /dev/null
+++ b/lib/common_test/test/ct_master_SUITE_data/master/master_SUITE.erl
@@ -0,0 +1,58 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: master_SUITE
+%%%
+%%% Description:
+%%% Test suite for common_test which tests the ct_master functionality
+%%%-------------------------------------------------------------------
+-module(master_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include("test.hrl").
+
+suite() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_) ->
+ ok.
+
+all() -> [first_testcase, second_testcase, third_testcase].
+
+init_per_testcase(_, Config) ->
+ Config.
+
+end_per_testcase(_, _) ->
+ ok.
+
+first_testcase(_)->
+ b = ct:get_config(a).
+
+second_testcase(_)->
+ d = ct:get_config(c).
+
+third_testcase(_)->
+ A = 4,
+ A = 2*2.
diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl
new file mode 100644
index 0000000000..a8bd2c2189
--- /dev/null
+++ b/lib/common_test/test/ct_misc_1_SUITE.erl
@@ -0,0 +1,228 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_misc_1_SUITE
+%%%
+%%% Description:
+%%% Test misc things in Common Test suites.
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_misc_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("test_server/include/test_server_line.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [beam_me_up, {group,parse_table}].
+
+groups() ->
+ [{parse_table,[parallel],
+ [parse_table_empty, parse_table_single,
+ parse_table_multiline_row,
+ parse_table_one_column_multiline,
+ parse_table_one_column_simple]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+beam_me_up(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ CTNode = ?config(ct_node, Config),
+
+ %% Path = rpc:call(CTNode, code, get_path, []),
+ %% [_ | Parts] = lists:reverse(filename:split(DataDir)),
+ %% TSDir = filename:join(lists:reverse(Parts)),
+ %% true = rpc:call(CTNode, code, del_path, [TSDir]),
+
+ Mods = [beam_1_SUITE, beam_2_SUITE],
+ Suites = [atom_to_list(M) || M <- Mods],
+ [{error,_} = rpc:call(CTNode, code, load_file, [M]) || M <- Mods],
+
+ code:add_path(DataDir),
+ CRes =
+ [compile:file(filename:join(DataDir,F),
+ [verbose,report_errors,
+ report_warnings,binary]) || F <- Suites],
+
+ [{module,_} = rpc:call(CTNode, code, load_binary,
+ [Mod, atom_to_list(Mod), Bin]) ||
+ {ok,Mod,Bin} <- CRes],
+
+ {Opts,ERPid} = setup([{suite,Suites},{auto_compile,false}], Config),
+
+ ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(beam_me_up,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(beam_me_up, 1),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+parse_table_empty(Config) when is_list(Config) ->
+
+ String = ["+----+-------+---------+---------+----------+------+--------+",
+ "| id | col11 | col2222 | col3333 | col4 | col5 | col6666 |",
+ "+----+-------+---------+---------+----------+------+--------+",
+ "+----+-------+---------+---------+----------+------+--------+",
+ "Query Done: 0 records selected"],
+
+ {{"id","col11","col2222","col3333","col4","col5","col6666"},[]} =
+ ct:parse_table(String).
+
+
+parse_table_single(Config) when is_list(Config) ->
+
+ String = ["+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+",
+ "| id | col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 |",
+"+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+",
+ "| 0 | 0 | -1407231560 | -256 | -1407231489 | 1500 | 1 | 1 | 1 |",
+ "+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+"
+ "Query Done: 1 record selected"],
+
+ {{"id","col1","col2","col3","col4","col5","col6","col7","col8"},
+ [{"0","0","-1407231560","-256","-1407231489", "1500","1","1","1"}]} =
+ ct:parse_table(String).
+
+parse_table_multiline_row(Config) when is_list(Config) ->
+
+ String = ["+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+",
+ "| id | col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 |",
+"+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+",
+ "| 0 | 0 | Free test string",
+ " on more lines",
+ "than one",
+ "| -256 | -1407231489 | 1500 | 1 | 1 | 1 |",
+ "+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+"
+ "Query Done: 1 record selected"],
+
+ {{"id","col1","col2","col3","col4","col5","col6","col7","col8"},
+ [{"0","0","Free test string\n on more lines\nthan one\n",
+ "-256","-1407231489", "1500","1","1","1"}]} =
+ ct:parse_table(String).
+
+parse_table_one_column_simple(Config) when is_list(Config) ->
+
+ String = ["|test|","|test value|"],
+
+ {{"test"},[{"test value"}]} = ct:parse_table(String).
+
+parse_table_one_column_multiline(Config) when is_list(Config) ->
+ String = ["|test|","|test","value|"],
+
+ {{"test"},[{"test\nvalue"}]} = ct:parse_table(String).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+%reformat(Events, _EH) ->
+% Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(beam_me_up) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{2,2,4}},
+ {?eh,tc_start,{beam_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{beam_1_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{beam_1_SUITE,tc1}},
+ {?eh,tc_done,{beam_1_SUITE,tc1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{beam_1_SUITE,tc2}},
+ {?eh,tc_done,{beam_1_SUITE,tc2,{failed,{error,'tc2 failed'}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_start,{beam_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{beam_1_SUITE,end_per_suite,ok}},
+ {?eh,tc_start,{beam_2_SUITE,init_per_suite}},
+ {?eh,tc_done,{beam_2_SUITE,init_per_suite,ok}},
+ {?eh,tc_start,{beam_2_SUITE,tc1}},
+ {?eh,tc_done,{beam_2_SUITE,tc1,ok}},
+ {?eh,test_stats,{2,1,{0,0}}},
+ {?eh,tc_start,{beam_2_SUITE,tc2}},
+ {?eh,tc_done,{beam_2_SUITE,tc2,{failed,{error,'tc2 failed'}}}},
+ {?eh,test_stats,{2,2,{0,0}}},
+ {?eh,tc_start,{beam_2_SUITE,end_per_suite}},
+ {?eh,tc_done,{beam_2_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_misc_1_SUITE_data/beam_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE_data/beam_1_SUITE.erl
new file mode 100644
index 0000000000..382bdefded
--- /dev/null
+++ b/lib/common_test/test/ct_misc_1_SUITE_data/beam_1_SUITE.erl
@@ -0,0 +1,134 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_1_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% COMMON TEST CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%%
+%% Info = [tuple()]
+%% List of key/value pairs.
+%%
+%% Description: Returns list of tuples to set default properties
+%% for the suite.
+%%
+%% Note: The suite/0 function is only meant to be used to return
+%% default data values, not perform any other operations.
+%%--------------------------------------------------------------------
+suite() ->
+ [
+ {timetrap,{seconds,10}}
+ ].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the suite.
+%%
+%% Description: Initialization before the suite.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after the suite.
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Initialization before each test case.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is finished.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after each test case.
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: all() -> TestCases | {skip,Reason}
+%%
+%% TestCases = [TestCase | {sequence,SeqName}]
+%% TestCase = atom()
+%% Name of a test case.
+%% SeqName = atom()
+%% Name of a test case sequence.
+%% Reason = term()
+%% The reason for skipping all test cases.
+%%
+%% Description: Returns the list of test cases that are to be executed.
+%%--------------------------------------------------------------------
+all() ->
+ [tc1, tc2].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+tc1(_Config) ->
+ ct:comment("tc1 executed"),
+ ok.
+
+tc2(_Config) ->
+ exit('tc2 failed').
diff --git a/lib/common_test/test/ct_misc_1_SUITE_data/beam_2_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE_data/beam_2_SUITE.erl
new file mode 100644
index 0000000000..70c1f2b471
--- /dev/null
+++ b/lib/common_test/test/ct_misc_1_SUITE_data/beam_2_SUITE.erl
@@ -0,0 +1,134 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(beam_2_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% COMMON TEST CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%%
+%% Info = [tuple()]
+%% List of key/value pairs.
+%%
+%% Description: Returns list of tuples to set default properties
+%% for the suite.
+%%
+%% Note: The suite/0 function is only meant to be used to return
+%% default data values, not perform any other operations.
+%%--------------------------------------------------------------------
+suite() ->
+ [
+ {timetrap,{seconds,10}}
+ ].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the suite.
+%%
+%% Description: Initialization before the suite.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after the suite.
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Initialization before each test case.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is finished.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after each test case.
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: all() -> TestCases | {skip,Reason}
+%%
+%% TestCases = [TestCase | {sequence,SeqName}]
+%% TestCase = atom()
+%% Name of a test case.
+%% SeqName = atom()
+%% Name of a test case sequence.
+%% Reason = term()
+%% The reason for skipping all test cases.
+%%
+%% Description: Returns the list of test cases that are to be executed.
+%%--------------------------------------------------------------------
+all() ->
+ [tc1, tc2].
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+tc1(_Config) ->
+ ct:comment("tc1 executed"),
+ ok.
+
+tc2(_Config) ->
+ exit('tc2 failed').
diff --git a/lib/common_test/test/ct_repeat_1_SUITE.erl b/lib/common_test/test/ct_repeat_1_SUITE.erl
new file mode 100644
index 0000000000..e674315526
--- /dev/null
+++ b/lib/common_test/test/ct_repeat_1_SUITE.erl
@@ -0,0 +1,1539 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_repeat_1_SUITE.erl
+%%%
+%%% Description:
+%%% Test some simple test case group scenarios with repeat.
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_repeat_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [repeat_cs, repeat_cs_and_grs, repeat_seq,
+ repeat_cs_until_any_ok, repeat_gr_until_any_ok,
+ repeat_cs_until_any_fail, repeat_gr_until_any_fail,
+ repeat_cs_until_all_ok, repeat_gr_until_all_ok,
+ repeat_cs_until_all_fail, repeat_gr_until_all_fail,
+ repeat_seq_until_any_fail,
+ repeat_shuffled_seq_until_any_fail].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+repeat_cs(Config) when is_list(Config) ->
+ execute(repeat_cs,
+ "repeat_1_SUITE", repeat_cs,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_cs_and_grs(Config) when is_list(Config) ->
+ execute(repeat_cs_and_grs,
+ "repeat_1_SUITE", repeat_cs_and_grs,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_seq(Config) when is_list(Config) ->
+ execute(repeat_seq,
+ "repeat_1_SUITE", repeat_seq,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_cs_until_any_ok(Config) when is_list(Config) ->
+ execute(repeat_cs_until_any_ok,
+ "repeat_1_SUITE", repeat_cs_until_any_ok,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_gr_until_any_ok(Config) when is_list(Config) ->
+ execute(repeat_gr_until_any_ok,
+ "repeat_1_SUITE", repeat_gr_until_any_ok,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_cs_until_any_fail(Config) when is_list(Config) ->
+ execute(repeat_cs_until_any_fail,
+ "repeat_1_SUITE", repeat_cs_until_any_fail,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_gr_until_any_fail(Config) when is_list(Config) ->
+ execute(repeat_gr_until_any_fail,
+ "repeat_1_SUITE", repeat_gr_until_any_fail,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_cs_until_all_ok(Config) when is_list(Config) ->
+ execute(repeat_cs_until_all_ok,
+ "repeat_1_SUITE", repeat_cs_until_all_ok,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_gr_until_all_ok(Config) when is_list(Config) ->
+ execute(repeat_gr_until_all_ok,
+ "repeat_1_SUITE", repeat_gr_until_all_ok,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_cs_until_all_fail(Config) when is_list(Config) ->
+ execute(repeat_cs_until_all_fail,
+ "repeat_1_SUITE", repeat_cs_until_all_fail,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_gr_until_all_fail(Config) when is_list(Config) ->
+ execute(repeat_gr_until_all_fail,
+ "repeat_1_SUITE", repeat_gr_until_all_fail,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_seq_until_any_fail(Config) when is_list(Config) ->
+ execute(repeat_seq_until_any_fail,
+ "repeat_1_SUITE", repeat_seq_until_any_fail,
+ Config).
+%%%-------------------------------------------------------------------
+repeat_shuffled_seq_until_any_fail(Config) when is_list(Config) ->
+ execute(repeat_shuffled_seq_until_any_fail,
+ "repeat_1_SUITE", repeat_shuffled_seq_until_any_fail,
+ Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+execute(TestCase, SuiteName, Group, Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, SuiteName),
+
+ {Opts,ERPid} = setup([{suite,Suite},{group,Group},{label,TestCase}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(TestCase,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(TestCase),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}} | Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+%reformat(Events, _EH) ->
+% Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(repeat_cs) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs_1,[]},ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_cs_1,[]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs_2,[{repeat,2}]},ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_cs_2,[{repeat,2}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs_2,[]},ok}},
+ {?eh,test_stats,{6,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_cs_2,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_cs,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_cs_and_grs) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs_and_grs,[{repeat,2}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_1,{failed,{error,{{badmatch,2},'_'}}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_result,[]},ok}},
+ {?eh,test_stats,{2,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,test_stats,{3,1,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}},
+ {?eh,test_stats,{3,1,{0,1}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,test_stats,{4,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_cs_and_grs,[{repeat,2}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs_and_grs,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,test_stats,{5,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_1,{failed,{error,{{badmatch,2},'_'}}}}},
+ {?eh,test_stats,{5,2,{0,1}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_result,[]},ok}},
+ {?eh,test_stats,{6,2,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,test_stats,{7,2,{0,1}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}},
+ {?eh,test_stats,{7,2,{0,2}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,test_stats,{8,2,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_cs_and_grs,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(repeat_seq) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_seq,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_1,[sequence,{repeat,2}]},
+ ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,test_stats,{1,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_1,[sequence,{repeat,2}]},
+ ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_seq_1,
+ [sequence]},ok}},
+ {?eh,test_stats,{2,2,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_seq_1,
+ [sequence]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_2,[sequence,{repeat,2}]},
+ ok}},
+ {?eh,test_stats,{3,2,{0,2}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_result,[]},ok}},
+ {?eh,test_stats,{4,2,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {group_result,gr_fail_result,failed}}},
+ {?eh,test_stats,{4,2,{0,3}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_2,[sequence,{repeat,2}]},
+ ok}}],
+ [{?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,repeat_seq_2,[sequence]},ok}},
+ {?eh,test_stats,{6,2,{0,4}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_seq_2,
+ [sequence]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_3,[sequence,{repeat,2}]},
+ ok}},
+ {?eh,test_stats,{7,2,{0,4}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,test_stats,{7,2,{0,5}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,
+ {repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {group_result,gr_fail_init,failed}}},
+ {?eh,test_stats,{7,2,{0,6}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_3,[sequence,{repeat,2}]},
+ ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_3,[sequence]},ok}},
+ {?eh,test_stats,{8,2,{0,8}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_3,[sequence]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_4,[sequence,{repeat,2}]},
+ ok}},
+ {?eh,test_stats,{8,3,{0,8}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,
+ tc_ok_1,{failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,test_stats,{8,3,{0,9}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,
+ tc_ok_1,{failed,{repeat_1_SUITE,tc_fail_1}}}},
+ {?eh,test_stats,{8,3,{0,10}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_4,[sequence,{repeat,2}]},
+ ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_seq_4,[sequence]},ok}},
+ {?eh,test_stats,{8,4,{0,12}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_4,[sequence]},ok}}],
+
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,repeat_seq,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_cs_until_any_ok) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_ok,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_ok_1,
+ [{repeat_until_any_ok,3}]},ok}},
+ {?eh,test_stats,{0,2,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{0,3,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_ok_1,
+ [{repeat_until_any_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_ok_1,
+ [{repeat_until_any_ok,2}]},ok}},
+ {?eh,test_stats,{0,5,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,ok}},
+ {?eh,test_stats,{1,5,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_ok_1,
+ [{repeat_until_any_ok,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_ok_2,
+ [{repeat_until_any_ok,3}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{2,5,{0,0}}},
+ {?eh,test_stats,{2,6,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_ok_2,
+ [{repeat_until_any_ok,3}]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_ok,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_gr_until_any_ok) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,repeat_gr_until_any_ok,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_ok_1,
+ [{repeat_until_any_ok,3}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_result,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_1,
+ {failed,{error,{{badmatch,2},'_'}}}}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,test_stats,{1,1,{0,1}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,test_stats,{1,2,{0,1}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_result_then_ok,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result_then_ok,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_ok_1,
+ [{repeat_until_any_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_ok_1,
+ [{repeat_until_any_ok,2}]},ok}},
+ %% ...
+ {?eh,test_stats,{3,4,{0,2}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_result_then_ok,[]},ok}},
+ {?eh,test_stats,{4,4,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result_then_ok,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_ok_1,
+ [{repeat_until_any_ok,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_ok_2,
+ [{repeat_until_any_ok,3}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_1,
+ {failed,{error,{{badmatch,2},'_'}}}}},
+ {?eh,test_stats,{5,5,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{5,6,{0,2}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,test_stats,{5,6,{0,3}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_ok_2,
+ [{repeat_until_any_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_ok_2,
+ [{repeat_until_any_ok,2}]},ok}},
+ {?eh,test_stats,{6,7,{0,3}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_then_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,ok}},
+ {?eh,test_stats,{7,7,{0,3}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,test_stats,{7,7,{0,4}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_ok_2,
+ [{repeat_until_any_ok,2}]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_ok,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_cs_until_any_fail) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ {?eh,tc_start,{repeat_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{repeat_1_SUITE,init_per_suite,ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,repeat_cs_until_any_fail,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,repeat_cs_until_any_fail,[]},ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,3}]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_then_fail_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,3}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,2}]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{5,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_then_fail_1}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{5,1,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_fail_1,
+ [{repeat_until_any_fail,2}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_fail_2,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_any_fail_2,
+ [{repeat_until_any_fail,3}]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,tc_fail_1,
+ {failed,
+ {error,
+ {{badmatch,2},
+ [{repeat_1_SUITE,tc_fail_1,1},
+ {repeat_1_SUITE,tc_fail_1,1},
+ {test_server,my_apply,3},
+ {test_server,ts_tc,3},
+ {test_server,run_test_case_eval1,6},
+ {test_server,run_test_case_eval,8}]}}}}},
+ {?eh,test_stats,{5,2,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_2}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,tc_fail_2,{failed,{error,exit_on_purpose}}}},
+ {?eh,test_stats,{5,3,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_fail_2,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_any_fail_2,
+ [{repeat_until_any_fail,3}]},
+ ok}}],
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,repeat_cs_until_any_fail,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,repeat_cs_until_any_fail,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{repeat_1_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_gr_until_any_fail) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ {?eh,tc_start,{repeat_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{repeat_1_SUITE,init_per_suite,ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,repeat_gr_until_any_fail,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,repeat_gr_until_any_fail,[]},ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,3}]},
+ ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_result,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_result,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_then_fail_result,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_then_fail_result,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,3}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,2}]},
+ ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{5,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{6,0,{0,0}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_result,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_result,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{7,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_then_fail_result,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,gr_ok_then_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{8,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_1,
+ [{repeat_until_any_fail,2}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,3}]},
+ ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{9,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{10,0,{0,0}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_init,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_init,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{11,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_then_fail_init,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_then_fail_init,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{12,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,3}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,2}]},
+ ok}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{13,0,{0,0}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{14,0,{0,0}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_then_fail_init,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,gr_ok_then_fail_init,[]},
+ {failed,{error,failing_this_time}}}},
+ {?eh,tc_auto_skip,
+ {repeat_1_SUITE,tc_ok_1,
+ {failed,
+ {repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}},
+ {?eh,test_stats,{14,0,{0,1}}},
+ {?eh,tc_auto_skip,
+ {repeat_1_SUITE,end_per_group,
+ {failed,
+ {repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{15,0,{0,1}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_2,
+ [{repeat_until_any_fail,2}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,3}]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_then_fail_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,test_stats,{16,0,{0,1}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{17,0,{0,1}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{18,0,{0,1}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,3}]},
+ ok}}],
+ [{?eh,tc_start,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,2}]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_then_fail_1}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{18,1,{0,1}}},
+ [{?eh,tc_start,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{19,1,{0,1}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{20,1,{0,1}}},
+ {?eh,tc_start,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_any_fail_3,
+ [{repeat_until_any_fail,2}]},
+ ok}}],
+ {?eh,tc_start,
+ {repeat_1_SUITE,{end_per_group,repeat_gr_until_any_fail,[]}}},
+ {?eh,tc_done,
+ {repeat_1_SUITE,{end_per_group,repeat_gr_until_any_fail,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,end_per_suite}},
+ {?eh,tc_done,{repeat_1_SUITE,end_per_suite,ok}},
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_cs_until_all_ok) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,repeat_cs_until_all_ok,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_ok_1,
+ [{repeat_until_all_ok,3}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_2,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{1,2,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_ok_1,
+ [{repeat_until_all_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_ok_1,
+ [{repeat_until_all_ok,2}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,ok}},
+ {?eh,test_stats,{2,2,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{3,2,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_2,ok}},
+ {?eh,test_stats,{4,2,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_ok_1,
+ [{repeat_until_all_ok,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_ok_2,
+ [{repeat_until_all_ok,3}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{6,2,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_ok_2,
+ [{repeat_until_all_ok,3}]},ok}}],
+
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_ok,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_gr_until_all_ok) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok_1,
+ [{repeat_until_all_ok,3}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{2,1,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_result_then_ok,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{3,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result_then_ok,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok_1,
+ [{repeat_until_all_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok_1,
+ [{repeat_until_all_ok,2}]},ok}},
+ {?eh,test_stats,{5,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,ok}},
+ {?eh,test_stats,{6,1,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_result_then_ok,[]},ok}},
+ {?eh,test_stats,{7,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result_then_ok,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok_1,
+ [{repeat_until_all_ok,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok_2,
+ [{repeat_until_all_ok,3}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init_then_ok,[]},
+ {failed,{error,failing_this_time}}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}},
+ {?eh,test_stats,{7,1,{0,1}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{8,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok_2,
+ [{repeat_until_all_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok_2,
+ [{repeat_until_all_ok,2}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init_then_ok,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{9,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_init_then_ok,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{10,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok_2,
+ [{repeat_until_all_ok,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok_3,
+ [{repeat_until_all_ok,3}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{11,1,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{11,2,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok_3,
+ [{repeat_until_all_ok,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_ok_3,
+ [{repeat_until_all_ok,2}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,gr_ok_1,[]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{12,2,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_fail_then_ok_1,ok}},
+ {?eh,test_stats,{13,2,{0,1}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok_3,
+ [{repeat_until_all_ok,2}]},ok}}],
+
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_ok,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_cs_until_all_fail) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_fail,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_fail_1,
+ [{repeat_until_all_fail,3}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{1,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_2,ok}},
+ {?eh,test_stats,{2,1,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_fail_1,
+ [{repeat_until_all_fail,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_fail_1,
+ [{repeat_until_all_fail,2}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{2,2,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{2,3,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_2,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{2,4,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_fail_1,
+ [{repeat_until_all_fail,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_cs_until_all_fail_2,
+ [{repeat_until_all_fail,3}]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{2,5,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_fail_2,
+ [{repeat_until_all_fail,3}]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_cs_until_all_fail,[]},ok}}],
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_gr_until_all_fail) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail_1,
+ [{repeat_until_all_fail,3}]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,test_stats,{0,1,{0,1}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,test_stats,{1,1,{0,1}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,
+ gr_ok_then_fail_result,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail_1,
+ [{repeat_until_all_fail,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail_1,
+ [{repeat_until_all_fail,2}]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{2,2,{0,1}}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_fail_init,[]},
+ {failed,{error,fails_on_purpose}}}},
+ {?eh,test_stats,{2,2,{0,2}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',fails_on_purpose}}}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{2,3,{0,2}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{init_per_group,
+ gr_ok_then_fail_result,[]},ok}},
+ {?eh,test_stats,{3,3,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_ok_then_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail_1,
+ [{repeat_until_all_fail,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail_2,
+ [{repeat_until_all_fail,3}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_then_fail_init,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{4,4,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail_2,
+ [{repeat_until_all_fail,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail_2,
+ [{repeat_until_all_fail,2}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_ok_then_fail_init,[]},
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{4,4,{0,3}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}}],
+ {?eh,tc_start,{repeat_1_SUITE,tc_fail_1}},
+ {?eh,test_stats,{4,5,{0,3}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail_2,
+ [{repeat_until_all_fail,2}]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail_3,
+ [{repeat_until_all_fail,3}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,test_stats,{6,5,{0,3}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail_3,
+ [{repeat_until_all_fail,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_gr_until_all_fail_3,
+ [{repeat_until_all_fail,2}]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{7,6,{0,3}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail_3,
+ [{repeat_until_all_fail,2}]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_gr_until_all_fail,[]},ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+test_events(repeat_seq_until_any_fail) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail,[]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_1,
+ [sequence,{repeat_until_any_fail,2}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_1,
+ [sequence,{repeat_until_any_fail,2}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,
+ repeat_seq_until_any_fail_1,[sequence]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,
+ repeat_seq_until_any_fail_1,[sequence]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_2,
+ [{repeat_until_any_fail,2},sequence]},ok}},
+ {?eh,test_stats,{5,0,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,test_stats,{7,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_2,
+ [{repeat_until_any_fail,2},sequence]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_2,[sequence]},
+ ok}},
+ {?eh,test_stats,{8,0,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,test_stats,{10,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_2,[sequence]},
+ ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_3,
+ [sequence,{repeat_until_any_fail,3}]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{11,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,test_stats,{12,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ {?eh,test_stats,{13,0,{0,0}}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_3,
+ [sequence,{repeat_until_any_fail,3}]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_3,
+ [{repeat_until_any_fail,2},sequence]},ok}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{15,0,{0,0}}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{15,1,{0,0}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_2,
+ {failed,{repeat_1_SUITE,tc_ok_then_fail_1}}}},
+ {?eh,test_stats,{15,1,{0,1}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {failed,{repeat_1_SUITE,tc_ok_then_fail_1}}}},
+ {?eh,test_stats,{15,1,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_3,
+ [{repeat_until_any_fail,2},sequence]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_4,
+ [{repeat_until_any_fail,3},sequence]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,
+ gr_ok_then_fail_result,[]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{18,1,{0,2}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_4,
+ [{repeat_until_any_fail,3},sequence]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_4,
+ [{repeat_until_any_fail,2},sequence]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_ok_then_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {group_result,gr_ok_then_fail_result,failed}}},
+ {?eh,test_stats,{19,1,{0,3}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {group_result,gr_ok_then_fail_result,failed}}},
+ {?eh,test_stats,{19,1,{0,4}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_4,
+ [{repeat_until_any_fail,2},sequence]},ok}}],
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_5,
+ [{repeat_until_any_fail,3},sequence]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_then_fail_init,[]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_2,[]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,test_stats,{23,1,{0,4}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_5,
+ [{repeat_until_any_fail,3},sequence]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_seq_until_any_fail_5,
+ [{repeat_until_any_fail,2},sequence]},ok}},
+ [{?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,gr_ok_then_fail_init,[]},
+ {failed,{error,failing_this_time}}}},
+ {?eh,test_stats,{24,1,{0,5}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}}],
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {group_result,gr_ok_then_fail_init,failed}}},
+ {?eh,test_stats,{24,1,{0,6}}},
+ {?eh,tc_auto_skip,{repeat_1_SUITE,tc_ok_1,
+ {group_result,gr_ok_then_fail_init,failed}}},
+ {?eh,test_stats,{24,1,{0,7}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail_5,
+ [{repeat_until_any_fail,2},sequence]},ok}}],
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_seq_until_any_fail,[]},ok}}],
+
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}];
+
+%%! Note that when testing shuffled groups, ct_test_support expects
+%%! both the start and done event for cases and init/end_per_group
+test_events(repeat_shuffled_seq_until_any_fail) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,unknown}},
+
+ [{?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail,[]},
+ ok}},
+
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [{shuffle,'_'},sequence,
+ {repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [{shuffle,'_'},sequence,
+ {repeat_until_any_fail,2}]},ok}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [sequence,shuffle,{repeat_until_any_fail,2}]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [sequence,shuffle,{repeat_until_any_fail,2}]},ok}}]},
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [{shuffle,'_'},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [{shuffle,'_'},sequence]},ok}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [{shuffle,repeated},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_1,
+ [{shuffle,repeated},sequence]},ok}}]},
+
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{shuffle,{1,2,3}},{repeat_until_any_fail,2},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{shuffle,{1,2,3}},{repeat_until_any_fail,2},sequence]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,test_stats,{7,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{repeat_until_any_fail,2},{shuffle,{1,2,3}},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{repeat_until_any_fail,2},{shuffle,{1,2,3}},sequence]},
+ ok}}]},
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{shuffle,'_'},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{shuffle,'_'},sequence]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{shuffle,repeated},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_2,
+ [{shuffle,repeated},sequence]},ok}}]},
+
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [{shuffle,'_'},sequence,
+ {repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [{shuffle,'_'},sequence,
+ {repeat_until_any_fail,3}]},ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_then_fail_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_2}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_2,ok}},
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ {?eh,test_stats,{14,0,{0,0}}},
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [shuffle,sequence,{repeat_until_any_fail,3}]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [shuffle,sequence,{repeat_until_any_fail,3}]},ok}}]},
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [{shuffle,'_'},{repeat_until_any_fail,2},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [{shuffle,'_'},{repeat_until_any_fail,2},sequence]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_then_fail_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_then_fail_1,
+ {failed,{error,failing_this_time}}}},
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [{shuffle,repeated},{repeat_until_any_fail,2},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_3,
+ [{shuffle,repeated},{repeat_until_any_fail,2},sequence]},
+ ok}}]},
+
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{shuffle,{1,2,3}},{repeat_until_any_fail,3},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{shuffle,{1,2,3}},{repeat_until_any_fail,3},sequence]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,
+ gr_ok_then_fail_result,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,
+ gr_ok_then_fail_result,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{repeat_until_any_fail,3},sequence,{shuffle,{1,2,3}}]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{repeat_until_any_fail,3},sequence,{shuffle,{1,2,3}}]},
+ ok}}]},
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{shuffle,'_'},{repeat_until_any_fail,2},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{shuffle,'_'},{repeat_until_any_fail,2},sequence]},
+ ok}},
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,gr_ok_then_fail_result,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,gr_ok_then_fail_result,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{shuffle,repeated},{repeat_until_any_fail,2},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_4,
+ [{shuffle,repeated},{repeat_until_any_fail,2},sequence]},
+ ok}}]},
+
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{shuffle,{1,2,3}},{repeat_until_any_fail,3},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{shuffle,{1,2,3}},{repeat_until_any_fail,3},sequence]},
+ ok}},
+ {?eh,tc_start,{repeat_1_SUITE,tc_ok_1}},
+ {?eh,tc_done,{repeat_1_SUITE,tc_ok_1,ok}},
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_1,[]},ok}}],
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,gr_ok_2,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,gr_ok_2,[]},ok}}],
+ [{?eh,tc_start,{repeat_1_SUITE,{end_per_group,
+ gr_ok_then_fail_init,[]}}},
+ {?eh,tc_done,{repeat_1_SUITE,{end_per_group,
+ gr_ok_then_fail_init,[]},ok}}],
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{repeat_until_any_fail,3},sequence,{shuffle,{1,2,3}}]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{repeat_until_any_fail,3},sequence,{shuffle,{1,2,3}}]},
+ ok}}]},
+ {shuffle,
+ [{?eh,tc_start,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{shuffle,'_'},{repeat_until_any_fail,2},
+ sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {init_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{shuffle,'_'},{repeat_until_any_fail,2},
+ sequence]},ok}},
+ [{?eh,tc_auto_skip,{repeat_1_SUITE,end_per_group,
+ {failed,{repeat_1_SUITE,init_per_group,
+ {'EXIT',failing_this_time}}}}}],
+ {?eh,tc_start,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{shuffle,repeated},{repeat_until_any_fail,2},sequence]}}},
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail_5,
+ [{shuffle,repeated},{repeat_until_any_fail,2},sequence]},
+ ok}}]},
+
+ {?eh,tc_done,{repeat_1_SUITE,
+ {end_per_group,repeat_shuffled_seq_until_any_fail,[]},
+ ok}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}].
diff --git a/lib/common_test/test/ct_repeat_1_SUITE_data/repeat_1_SUITE.erl b/lib/common_test/test/ct_repeat_1_SUITE_data/repeat_1_SUITE.erl
new file mode 100644
index 0000000000..fb8d31edd4
--- /dev/null
+++ b/lib/common_test/test/ct_repeat_1_SUITE_data/repeat_1_SUITE.erl
@@ -0,0 +1,373 @@
+%%%-------------------------------------------------------------------
+%%% @author Peter Andersson <[email protected]>
+%%% @copyright (C) 2010, Peter Andersson
+%%% @doc
+%%%
+%%% @end
+%%% Created : 11 Aug 2010 by Peter Andersson <[email protected]>
+%%%-------------------------------------------------------------------
+-module(repeat_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% @spec suite() -> Info
+%% Info = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{seconds,30}}].
+
+%%--------------------------------------------------------------------
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ spawn(fun() -> db() end),
+ Config.
+%%--------------------------------------------------------------------
+%% @spec end_per_suite(Config0) -> void() | {save_config,Config1}
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ db(stop, ok),
+ ok.
+
+db() ->
+ register(?MODULE, self()),
+ db_loop([]).
+
+db_loop(Dict) ->
+ receive
+ {insert,From,Key,Val} ->
+ From ! {?MODULE,ok},
+ db_loop([{Key,Val} | proplists:delete(Key, Dict)]);
+ {lookup,From,Key} ->
+ From ! {?MODULE,proplists:get_value(Key, Dict)},
+ db_loop(Dict);
+ {delete,From,Key} ->
+ From ! {?MODULE,ok},
+ db_loop(proplists:delete(Key, Dict));
+ {stop,From,_} ->
+ From ! {?MODULE,ok}
+ end.
+
+ db(Op, Key, Val) ->
+ ?MODULE ! {Op,self(),Key,Val},
+ receive {?MODULE,Result} -> Result end.
+
+ db(Op, Key) ->
+ ?MODULE ! {Op,self(),Key},
+ receive {?MODULE,Result} -> Result end.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_group(G, Config) when G == gr_ok_1 ; G == gr_ok_2 ;
+ G == gr_fail_result;
+ G == gr_ok_then_fail_result ->
+ ct:comment(G),
+ Config;
+
+init_per_group(G, _Config) when G == gr_fail_init ->
+ ct:comment(G),
+ exit(fails_on_purpose);
+
+init_per_group(G, Config) when G == gr_ok_then_fail_init ->
+ ct:comment(G),
+ do_2nd_time(G,
+ fun() -> exit(failing_this_time) end,
+ fun() -> Config end);
+
+init_per_group(G, Config) when G == gr_fail_init_then_ok ->
+ ct:comment(G),
+ do_2nd_time(G,
+ fun() -> Config end,
+ fun() -> exit(failing_this_time) end);
+
+init_per_group(G, Config) ->
+ ct:comment(G),
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+%% GroupName = atom()
+%% Config0 = Config1 = [tuple()]
+%% @end
+%%--------------------------------------------------------------------
+end_per_group(G, _Config) when G == gr_fail_result ->
+ ct:comment(G),
+ {return_group_result,failed};
+
+end_per_group(G, _Config) when G == gr_ok_then_fail_result ->
+ ct:comment(G),
+ do_2nd_time(G,
+ fun() -> {return_group_result,failed} end,
+ fun() -> ok end);
+
+end_per_group(G, _Config) when G == gr_fail_result_then_ok ->
+ ct:comment(G),
+ do_2nd_time(G,
+ fun() -> ok end,
+ fun() -> {return_group_result,failed} end);
+
+end_per_group(G, _Config) ->
+ ct:comment(G),
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+%% TestCase = atom()
+%% Config0 = Config1 = [tuple()]
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% @spec groups() -> [Group]
+%% Group = {GroupName,Properties,GroupsAndTestCases}
+%% GroupName = atom()
+%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}]
+%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase]
+%% TestCase = atom()
+%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}}
+%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail |
+%% repeat_until_any_ok | repeat_until_any_fail
+%% N = integer() | forever
+%% @end
+%%--------------------------------------------------------------------
+groups() ->
+ [
+ %%---------------------------------------------------------------
+ {repeat_cs, [], [{group,repeat_cs_0},
+ {group,repeat_cs_1},
+ {group,repeat_cs_2}]},
+ {repeat_cs_0, [{repeat,0}], [tc_ok_1,tc_ok_2]},
+ {repeat_cs_1, [{repeat,1}], [tc_ok_1,tc_ok_2]},
+ {repeat_cs_2, [{repeat,2}], [tc_ok_1,tc_ok_2]},
+
+ {repeat_cs_and_grs, [{repeat,2}], [{group,gr_ok_1},tc_fail_1,
+ {group,gr_fail_result},tc_ok_1,
+ {group,gr_fail_init},tc_ok_2]},
+
+ %%---------------------------------------------------------------
+ {repeat_seq, [], [{group,repeat_seq_1},
+ {group,repeat_seq_2},
+ {group,repeat_seq_3},
+ {group,repeat_seq_4}]},
+ {repeat_seq_1, [sequence,{repeat,2}], [tc_ok_1,tc_fail_1,tc_ok_2]},
+ {repeat_seq_2, [sequence,{repeat,2}], [tc_ok_1,{group,gr_fail_result},tc_ok_2]},
+ {repeat_seq_3, [sequence,{repeat,2}], [tc_ok_1,{group,gr_fail_init},tc_ok_2]},
+ {repeat_seq_4, [sequence,{repeat,2}], [tc_fail_1,{group,gr_ok_1},tc_ok_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_cs_until_any_ok, [], [{group,repeat_cs_until_any_ok_1},
+ {group,repeat_cs_until_any_ok_2}]},
+ {repeat_cs_until_any_ok_1, [{repeat_until_any_ok,3}], [tc_fail_1,
+ tc_fail_2,
+ tc_fail_then_ok_1]},
+ {repeat_cs_until_any_ok_2, [{repeat_until_any_ok,3}], [tc_ok_1,tc_fail_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_gr_until_any_ok, [], [{group,repeat_gr_until_any_ok_1},
+ {group,repeat_gr_until_any_ok_2}]},
+ {repeat_gr_until_any_ok_1, [{repeat_until_any_ok,3}],
+ [{group,gr_fail_result}, tc_fail_1, {group,gr_fail_init}, tc_fail_2,
+ {group,gr_fail_result_then_ok}]},
+ {repeat_gr_until_any_ok_2, [{repeat_until_any_ok,3}],
+ [{group,gr_fail_result}, tc_fail_1, tc_fail_then_ok_1,
+ {group,gr_fail_init}]},
+
+ %%---------------------------------------------------------------
+ {repeat_cs_until_any_fail, [], [{group,repeat_cs_until_any_fail_1},
+ {group,repeat_cs_until_any_fail_2}]},
+ {repeat_cs_until_any_fail_1, [{repeat_until_any_fail,3}], [tc_ok_1,
+ tc_ok_2,
+ tc_ok_then_fail_1]},
+ {repeat_cs_until_any_fail_2, [{repeat_until_any_fail,3}], [tc_fail_1,tc_fail_2]},
+
+ %%---------------------------------------------------------------
+ {repeat_gr_until_any_fail, [], [{group,repeat_gr_until_any_fail_1},
+ {group,repeat_gr_until_any_fail_2},
+ {group,repeat_gr_until_any_fail_3}]},
+ {repeat_gr_until_any_fail_1, [{repeat_until_any_fail,3}],
+ [{group,gr_ok_1}, tc_ok_1, {group,gr_ok_then_fail_result}, tc_ok_2]},
+ {repeat_gr_until_any_fail_2, [{repeat_until_any_fail,3}],
+ [{group,gr_ok_1}, tc_ok_1, {group,gr_ok_then_fail_init}, tc_ok_2]},
+ {repeat_gr_until_any_fail_3, [{repeat_until_any_fail,3}], [tc_ok_then_fail_1,
+ {group,gr_ok_1},
+ tc_ok_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_cs_until_all_ok, [], [{group,repeat_cs_until_all_ok_1},
+ {group,repeat_cs_until_all_ok_2}]},
+ {repeat_cs_until_all_ok_1, [{repeat_until_all_ok,3}], [tc_fail_then_ok_1,
+ tc_ok_1,
+ tc_fail_then_ok_2]},
+ {repeat_cs_until_all_ok_2, [{repeat_until_all_ok,3}], [tc_ok_1,tc_ok_2]},
+
+ %%---------------------------------------------------------------
+ {repeat_gr_until_all_ok, [], [{group,repeat_gr_until_all_ok_1},
+ {group,repeat_gr_until_all_ok_2},
+ {group,repeat_gr_until_all_ok_3}]},
+ {repeat_gr_until_all_ok_1, [{repeat_until_all_ok,3}],
+ [tc_ok_1, {group,gr_ok_1}, tc_fail_then_ok_1, {group,gr_fail_result_then_ok}]},
+ {repeat_gr_until_all_ok_2, [{repeat_until_all_ok,3}],
+ [{group,gr_fail_init_then_ok}, tc_ok_1]},
+ {repeat_gr_until_all_ok_3, [{repeat_until_all_ok,3}],
+ [{group,gr_ok_1}, tc_fail_then_ok_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_cs_until_all_fail, [], [{group,repeat_cs_until_all_fail_1},
+ {group,repeat_cs_until_all_fail_2}]},
+ {repeat_cs_until_all_fail_1, [{repeat_until_all_fail,3}], [tc_ok_then_fail_1,
+ tc_fail_1,
+ tc_ok_then_fail_2]},
+ {repeat_cs_until_all_fail_2, [{repeat_until_all_fail,3}], [tc_fail_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_gr_until_all_fail, [], [{group,repeat_gr_until_all_fail_1},
+ {group,repeat_gr_until_all_fail_2},
+ {group,repeat_gr_until_all_fail_3}]},
+ {repeat_gr_until_all_fail_1, [{repeat_until_all_fail,3}],
+ [tc_fail_1, {group,gr_fail_init}, tc_ok_then_fail_1, {group,gr_ok_then_fail_result}]},
+ {repeat_gr_until_all_fail_2, [{repeat_until_all_fail,3}],
+ [{group,gr_ok_then_fail_init}, tc_fail_1]},
+ {repeat_gr_until_all_fail_3, [{repeat_until_all_fail,3}],
+ [{group,gr_fail_result}, tc_ok_then_fail_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_seq_until_any_fail, [], [{group,repeat_seq_until_any_fail_1},
+ {group,repeat_seq_until_any_fail_2},
+ {group,repeat_seq_until_any_fail_3},
+ {group,repeat_seq_until_any_fail_4},
+ {group,repeat_seq_until_any_fail_5}]},
+ {repeat_seq_until_any_fail_1, [sequence,{repeat_until_any_fail,2}],
+ [tc_ok_1, tc_ok_2]},
+ {repeat_seq_until_any_fail_2, [{repeat_until_any_fail,2},sequence],
+ [tc_ok_1, {group,gr_ok_1}, tc_ok_2]},
+ {repeat_seq_until_any_fail_3, [sequence,{repeat_until_any_fail,3}],
+ [tc_ok_1, tc_ok_then_fail_1, tc_ok_2, {group,gr_ok_1}]},
+ {repeat_seq_until_any_fail_4, [{repeat_until_any_fail,3},sequence],
+ [{group,gr_ok_then_fail_result}, {group,gr_ok_1}, tc_ok_1]},
+ {repeat_seq_until_any_fail_5, [{repeat_until_any_fail,3},sequence],
+ [{group,gr_ok_1}, {group,gr_ok_then_fail_init}, {group,gr_ok_2}, tc_ok_1]},
+
+ %%---------------------------------------------------------------
+ {repeat_shuffled_seq_until_any_fail, [], [{group,repeat_shuffled_seq_until_any_fail_1},
+ {group,repeat_shuffled_seq_until_any_fail_2},
+ {group,repeat_shuffled_seq_until_any_fail_3},
+ {group,repeat_shuffled_seq_until_any_fail_4},
+ {group,repeat_shuffled_seq_until_any_fail_5}]},
+ {repeat_shuffled_seq_until_any_fail_1, [sequence,shuffle,{repeat_until_any_fail,2}],
+ [tc_ok_1, tc_ok_2]},
+ {repeat_shuffled_seq_until_any_fail_2, [{repeat_until_any_fail,2},{shuffle,{1,2,3}},sequence],
+ [tc_ok_1, {group,gr_ok_1}, tc_ok_2]},
+ {repeat_shuffled_seq_until_any_fail_3, [shuffle,sequence,{repeat_until_any_fail,3}],
+ [tc_ok_1, tc_ok_then_fail_1, tc_ok_2, {group,gr_ok_1}]},
+ {repeat_shuffled_seq_until_any_fail_4, [{repeat_until_any_fail,3},sequence,{shuffle,{1,2,3}}],
+ [{group,gr_ok_then_fail_result}, {group,gr_ok_1}, tc_ok_1]},
+ {repeat_shuffled_seq_until_any_fail_5, [{repeat_until_any_fail,3},sequence,{shuffle,{1,2,3}}],
+ [{group,gr_ok_1}, {group,gr_ok_then_fail_init}, {group,gr_ok_2}, tc_ok_1]},
+
+ %%---------------------------------------------------------------
+ {gr_ok_1, [], [tc_ok_1]},
+
+ {gr_ok_2, [], [tc_ok_1]},
+
+ {gr_fail_init, [], [tc_ok_1]},
+
+ {gr_fail_result, [], [tc_ok_1]},
+
+ {gr_ok_then_fail_init, [], [tc_ok_1]},
+
+ {gr_ok_then_fail_result, [], [tc_ok_1]},
+
+ {gr_fail_result_then_ok, [], [tc_ok_1]},
+
+ {gr_fail_init_then_ok, [], [tc_ok_1]}
+ ].
+
+%%--------------------------------------------------------------------
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+%% GroupsAndTestCases = [{group,GroupName} | TestCase]
+%% GroupName = atom()
+%% TestCase = atom()
+%% Reason = term()
+%% @end
+%%--------------------------------------------------------------------
+all() ->
+ [].
+
+tc_ok_1(_) ->
+ ok.
+
+tc_ok_2(_) ->
+ ok.
+
+tc_fail_1(_) ->
+ x=2.
+
+tc_fail_2(_) ->
+ exit(exit_on_purpose).
+
+tc_ok_then_fail_1(_) ->
+ do_2nd_time(tc_ok_then_fail_1,
+ fun() -> exit(failing_this_time) end,
+ fun() -> ok end),
+ ok.
+
+tc_ok_then_fail_2(_) ->
+ do_2nd_time(tc_ok_then_fail_2,
+ fun() -> exit(failing_this_time) end,
+ fun() -> ok end),
+ ok.
+
+tc_fail_then_ok_1(_) ->
+ do_2nd_time(tc_fail_then_ok_1,
+ fun() -> ok end,
+ fun() -> exit(failing_this_time) end),
+ ok.
+
+tc_fail_then_ok_2(_) ->
+ do_2nd_time(tc_fail_then_ok_2,
+ fun() -> ok end,
+ fun() -> exit(failing_this_time) end),
+ ok.
+
+do_2nd_time(Case, True, False) ->
+ case db(lookup, Case) of
+ undefined ->
+ db(insert, Case, 1),
+ False();
+ 1 ->
+ ct:log("This is the second call...", []),
+ db(delete, Case),
+ True()
+ end.
diff --git a/lib/common_test/test/ct_sequence_1_SUITE.erl b/lib/common_test/test/ct_sequence_1_SUITE.erl
new file mode 100644
index 0000000000..c7650b169c
--- /dev/null
+++ b/lib/common_test/test/ct_sequence_1_SUITE.erl
@@ -0,0 +1,311 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_sequence_1_SUITE
+%%%
+%%% Description:
+%%% Test sequences
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_sequence_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [subgroup_return_fail, subgroup_init_fail,
+ subgroup_after_failed_case,
+ case_after_subgroup_return_fail,
+ case_after_subgroup_fail_init].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+
+subgroup_return_fail(Config) when is_list(Config) ->
+ execute(subgroup_return_fail,
+ "subgroups_1_SUITE", subgroup_return_fail,
+ Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+subgroup_init_fail(Config) when is_list(Config) ->
+ execute(subgroup_init_fail,
+ "subgroups_1_SUITE", subgroup_init_fail,
+ Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+subgroup_after_failed_case(Config) when is_list(Config) ->
+ execute(subgroup_after_failed_case,
+ "subgroups_1_SUITE", subgroup_after_failed_case,
+ Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+case_after_subgroup_return_fail(Config) when is_list(Config) ->
+ execute(case_after_subgroup_return_fail,
+ "subgroups_1_SUITE", case_after_subgroup_return_fail,
+ Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+case_after_subgroup_fail_init(Config) when is_list(Config) ->
+ execute(case_after_subgroup_fail_init,
+ "subgroups_1_SUITE", case_after_subgroup_fail_init,
+ Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+execute(TestCase, SuiteName, Group, Config) ->
+ DataDir = ?config(data_dir, Config),
+ Suite = filename:join(DataDir, SuiteName),
+
+ {Opts,ERPid} = setup([{suite,Suite},{group,Group},{label,TestCase}], Config),
+ ok = ct_test_support:run(Opts, Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(TestCase,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(TestCase),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+setup(Test, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}} | Test],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+%reformat(Events, _EH) ->
+% Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(subgroup_return_fail) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,2}},
+ [{?eh,tc_start,
+ {subgroups_1_SUITE,{init_per_group,subgroup_return_fail,[sequence]}}},
+ {?eh,tc_done,
+ {subgroups_1_SUITE,{init_per_group,subgroup_return_fail,[sequence]},ok}},
+ [{?eh,tc_start,
+ {subgroups_1_SUITE,{init_per_group,return_fail,[]}}},
+ {?eh,tc_done,
+ {subgroups_1_SUITE,{init_per_group,return_fail,[]},ok}},
+ {?eh,tc_start,{subgroups_1_SUITE,failing_tc}},
+ {?eh,tc_done,
+ {subgroups_1_SUITE,failing_tc,{failed,{error,{{badmatch,3},'_'}}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,
+ {subgroups_1_SUITE,{end_per_group,return_fail,[]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_auto_skip,
+ {subgroups_1_SUITE,ok_tc,{group_result,return_fail,failed}}},
+ {?eh,test_stats,{0,1,{0,1}}},
+ {?eh,tc_start,
+ {subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]}}},
+ {?eh,tc_done,
+ {subgroups_1_SUITE,{end_per_group,subgroup_return_fail,[sequence]},
+ {return_group_result,failed}}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(subgroup_init_fail) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,2}},
+ [{?eh,tc_start,
+ {subgroups_1_SUITE,{init_per_group,subgroup_init_fail,[sequence]}}},
+ {?eh,tc_done,
+ {subgroups_1_SUITE,{init_per_group,subgroup_init_fail,[sequence]},ok}},
+ [{?eh,tc_start,{subgroups_1_SUITE,{init_per_group,fail_init,[]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,{init_per_group,fail_init,[]},
+ {failed,{error,init_per_group_fails_on_purpose}}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,
+ {failed,{subgroups_1_SUITE,init_per_group,
+ {'EXIT',init_per_group_fails_on_purpose}}}}},
+ {?eh,test_stats,{0,0,{0,1}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,end_per_group,
+ {failed,{subgroups_1_SUITE,init_per_group,
+ {'EXIT',init_per_group_fails_on_purpose}}}}}],
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{group_result,fail_init,failed}}},
+ {?eh,test_stats,{0,0,{0,2}}},
+ {?eh,tc_start,{subgroups_1_SUITE,{end_per_group,subgroup_init_fail,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {end_per_group,subgroup_init_fail,[sequence]},
+ {return_group_result,failed}}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(subgroup_after_failed_case) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,2}},
+ [{?eh,tc_start,{subgroups_1_SUITE,
+ {init_per_group,subgroup_after_failed_case,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {init_per_group,subgroup_after_failed_case,[sequence]},ok}},
+ {?eh,tc_start,{subgroups_1_SUITE,failing_tc}},
+ {?eh,tc_done,{subgroups_1_SUITE,failing_tc,{failed,{error,{{badmatch,3},'_'}}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{failed,{subgroups_1_SUITE,failing_tc}}}},
+ {?eh,test_stats,{0,1,{0,1}}},
+ {?eh,tc_start,{subgroups_1_SUITE,
+ {end_per_group,subgroup_after_failed_case,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {end_per_group,subgroup_after_failed_case,[sequence]},
+ {return_group_result,failed}}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+];
+
+test_events(case_after_subgroup_return_fail) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,2}},
+ [{?eh,tc_start,{subgroups_1_SUITE,
+ {init_per_group,case_after_subgroup_return_fail,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {init_per_group,case_after_subgroup_return_fail,[sequence]},ok}},
+ [{?eh,tc_start,{subgroups_1_SUITE,{init_per_group,return_fail,[]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,{init_per_group,return_fail,[]},ok}},
+ {?eh,tc_start,{subgroups_1_SUITE,failing_tc}},
+ {?eh,tc_done,{subgroups_1_SUITE,failing_tc,{failed,{error,{{badmatch,3},'_'}}}}},
+ {?eh,test_stats,{0,1,{0,0}}},
+ {?eh,tc_start,{subgroups_1_SUITE,{end_per_group,return_fail,[]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,{end_per_group,return_fail,[]},
+ {return_group_result,failed}}}],
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,{group_result,return_fail,failed}}},
+ {?eh,test_stats,{0,1,{0,1}}},
+ {?eh,tc_start,{subgroups_1_SUITE,
+ {end_per_group,case_after_subgroup_return_fail,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {end_per_group,case_after_subgroup_return_fail,[sequence]},
+ {return_group_result,failed}}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(case_after_subgroup_fail_init) ->
+ [
+ {?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
+ {?eh,start_info,{1,1,2}},
+ [{?eh,tc_start,{subgroups_1_SUITE,
+ {init_per_group,case_after_subgroup_fail_init,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {init_per_group,case_after_subgroup_fail_init,[sequence]},ok}},
+ [{?eh,tc_start,{subgroups_1_SUITE,{init_per_group,fail_init,[]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {init_per_group,fail_init,[]},
+ {failed,{error,init_per_group_fails_on_purpose}}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,ok_tc,
+ {failed,
+ {subgroups_1_SUITE,init_per_group,
+ {'EXIT',init_per_group_fails_on_purpose}}}}},
+ {?eh,test_stats,{0,0,{0,1}}},
+ {?eh,tc_auto_skip,{subgroups_1_SUITE,end_per_group,
+ {failed,
+ {subgroups_1_SUITE,init_per_group,
+ {'EXIT',init_per_group_fails_on_purpose}}}}}],
+
+ {?eh,tc_auto_skip,
+ {subgroups_1_SUITE,ok_tc,{group_result,fail_init,failed}}},
+ {?eh,test_stats,{0,0,{0,2}}},
+ {?eh,tc_start,{subgroups_1_SUITE,
+ {end_per_group,case_after_subgroup_fail_init,[sequence]}}},
+ {?eh,tc_done,{subgroups_1_SUITE,
+ {end_per_group,case_after_subgroup_fail_init,[sequence]},
+ {return_group_result,failed}}}],
+ {?eh,test_done,{'DEF','STOP_TIME'}},
+ {?eh,stop_logging,[]}
+ ].
diff --git a/lib/common_test/test/ct_sequence_1_SUITE_data/subgroups_1_SUITE.erl b/lib/common_test/test/ct_sequence_1_SUITE_data/subgroups_1_SUITE.erl
new file mode 100644
index 0000000000..741b1165c1
--- /dev/null
+++ b/lib/common_test/test/ct_sequence_1_SUITE_data/subgroups_1_SUITE.erl
@@ -0,0 +1,108 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(subgroups_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+all() ->
+ [{group, subgroup_return_fail},
+ {group, subgroup_init_fail},
+ {group, subgroup_after_failed_case},
+ {group, case_after_subgroup_return_fail},
+ {group, case_after_subgroup_fail_init}].
+
+groups() ->
+ [{return_fail, [], [failing_tc]},
+ {fail_init, [], [ok_tc]},
+ {ok_group, [], [ok_tc]},
+
+ {subgroup_return_fail, [sequence], [{group, return_fail}, {group, ok_group}]},
+
+ {subgroup_init_fail, [sequence], [{group, fail_init}, {group, ok_group}]},
+
+ {subgroup_after_failed_case, [sequence], [failing_tc, {group, ok_group}]},
+
+ {case_after_subgroup_return_fail, [sequence], [{group, return_fail}, ok_tc]},
+
+ {case_after_subgroup_fail_init, [sequence], [{group, fail_init}, ok_tc]}
+ ].
+
+failed_subgroup(subgroup_return_fail) -> return_fail;
+failed_subgroup(subgroup_init_fail) -> fail_init;
+failed_subgroup(case_after_subgroup_return_fail) -> return_fail;
+failed_subgroup(case_after_subgroup_fail_init) -> fail_init;
+failed_subgroup(_) -> undefined.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(fail_init, Config) ->
+ ct:comment(fail_init),
+ exit(init_per_group_fails_on_purpose);
+
+init_per_group(Group, Config) ->
+ ct:comment(Group),
+ [{Group,failed_subgroup(Group)} | Config].
+
+end_per_group(subgroup_after_failed_case, Config) ->
+ ct:comment(subgroup_after_failed_case),
+ Status = ?config(tc_group_result, Config),
+ [{subgroups_1_SUITE,failing_tc}] = proplists:get_value(failed, Status),
+ {return_group_result,failed};
+
+end_per_group(Group, Config) when Group == subgroup_return_fail;
+ Group == subgroup_init_fail;
+ Group == case_after_subgroup_return_fail;
+ Group == case_after_subgroup_fail_init ->
+ ct:comment(Group),
+ Status = ?config(tc_group_result, Config),
+ Failed = proplists:get_value(failed, Status),
+ true = lists:member({group_result,?config(Group,Config)}, Failed),
+ {return_group_result,failed};
+
+end_per_group(return_fail, Config) ->
+ ct:comment(return_fail),
+ Status = ?config(tc_group_result, Config),
+ [{subgroups_1_SUITE,failing_tc}] = proplists:get_value(failed, Status),
+ {return_group_result,failed};
+
+end_per_group(Group, _Config) ->
+ ct:comment(Group),
+ ok.
+
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(failing_tc, Config) ->
+ {failed,_} = proplists:get_value(tc_status, Config),
+ ok;
+
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+failing_tc(_Config) ->
+ 2=3.
+
+ok_tc(_Config) ->
+ ok.
diff --git a/lib/common_test/test/ct_skip_SUITE.erl b/lib/common_test/test/ct_skip_SUITE.erl
index 9f428723f5..62c5f10b7c 100644
--- a/lib/common_test/test/ct_skip_SUITE.erl
+++ b/lib/common_test/test/ct_skip_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-define(eh, ct_test_support_eh).
@@ -56,14 +56,20 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-all(doc) ->
- [""];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [auto_skip, user_skip].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [
- auto_skip,
- user_skip
- ].
%%--------------------------------------------------------------------
@@ -89,14 +95,14 @@ auto_skip(Config) when is_list(Config) ->
],
{Opts,ERPid} = setup({suite,Suites}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(auto_skip,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(auto_skip),
+ TestEvents = events_to_check(auto_skip),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -112,14 +118,14 @@ user_skip(Config) when is_list(Config) ->
Join(DataDir, "user_skip_5_SUITE")],
{Opts,ERPid} = setup({suite,Suites}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(user_skip,
reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(user_skip),
+ TestEvents = events_to_check(user_skip),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -142,6 +148,15 @@ reformat(Events, EH) ->
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
test_events(auto_skip) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
diff --git a/lib/common_test/test/ct_smoke_test_SUITE.erl b/lib/common_test/test/ct_smoke_test_SUITE.erl
index f1c695f614..c3d49a5afa 100644
--- a/lib/common_test/test/ct_smoke_test_SUITE.erl
+++ b/lib/common_test/test/ct_smoke_test_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-define(eh, ct_test_support_eh).
@@ -112,14 +112,22 @@ end_per_testcase(TestCase, Config) ->
%% Description: Returns a description of the test suite (doc) and a
%% list of all test cases in the suite (suite).
%%--------------------------------------------------------------------
-all(doc) ->
- ["Run smoke tests of Common Test."];
+suite() -> [{ct_hooks,[ts_install_cth]}].
-all(suite) ->
- [dir1, dir2, dir1_2,
- suite11, suite21, suite11_21,
+all() ->
+ [dir1, dir2, dir1_2, suite11, suite21, suite11_21,
tc111, tc211, tc111_112].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%--------------------------------------------------------------------
%% TEST CASES
@@ -162,7 +170,7 @@ dir1(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -170,7 +178,7 @@ dir1(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(dir1),
+ TestEvents = events_to_check(dir1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -191,7 +199,7 @@ dir2(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -199,7 +207,7 @@ dir2(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(dir2),
+ TestEvents = events_to_check(dir2),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -221,7 +229,7 @@ dir1_2(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -229,7 +237,7 @@ dir1_2(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(dir1_2),
+ TestEvents = events_to_check(dir1_2),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -251,7 +259,7 @@ suite11(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -259,7 +267,7 @@ suite11(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(suite11),
+ TestEvents = events_to_check(suite11),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -280,7 +288,7 @@ suite21(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -288,7 +296,7 @@ suite21(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(suite21),
+ TestEvents = events_to_check(suite21),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -311,7 +319,7 @@ suite11_21(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -319,7 +327,7 @@ suite11_21(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(suite11_21),
+ TestEvents = events_to_check(suite11_21),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -342,7 +350,7 @@ tc111(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -350,7 +358,7 @@ tc111(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(tc111),
+ TestEvents = events_to_check(tc111),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -372,7 +380,7 @@ tc211(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -380,7 +388,7 @@ tc211(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(tc211),
+ TestEvents = events_to_check(tc211),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
%%%-----------------------------------------------------------------
@@ -403,7 +411,7 @@ tc111_112(Config) when is_list(Config) ->
ERPid = ct_test_support:start_event_receiver(Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
@@ -411,7 +419,7 @@ tc111_112(Config) when is_list(Config) ->
ct_test_support:reformat(Events, ?eh),
?config(priv_dir, Config)),
- TestEvents = test_events(tc111_112),
+ TestEvents = events_to_check(tc111_112),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -423,8 +431,16 @@ eh_opts(Config) ->
Level = ?config(trace_level, Config),
[{event_handler,{?eh,[{cbm,ct_test_support},{trace_level,Level}]}}].
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ events(Test) ++ events_to_check(Test, N-1).
-test_events(Test) when Test == dir1 ; Test == dir2 ;
+events(Test) when Test == dir1 ; Test == dir2 ;
Test == suite11 ; Test == suite21 ->
Suite = if Test == dir1 ; Test == suite11 -> happy_11_SUITE;
true -> happy_21_SUITE
@@ -465,7 +481,7 @@ test_events(Test) when Test == dir1 ; Test == dir2 ;
{?eh,test_done,{'DEF','STOP_TIME'}},
{?eh,stop_logging,[]}
];
-test_events(Test) when Test == dir1_2 ; Test == suite11_21 ->
+events(Test) when Test == dir1_2 ; Test == suite11_21 ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
@@ -532,7 +548,7 @@ test_events(Test) when Test == dir1_2 ; Test == suite11_21 ->
{?eh,stop_logging,[]}
];
-test_events(Test) when Test == tc111 ; Test == tc211 ->
+events(Test) when Test == tc111 ; Test == tc211 ->
Suite = if Test == tc111 -> happy_11_SUITE; true -> happy_21_SUITE end,
[
{?eh,start_logging,{'DEF','RUNDIR'}},
@@ -549,7 +565,7 @@ test_events(Test) when Test == tc111 ; Test == tc211 ->
{?eh,stop_logging,[]}
];
-test_events(tc111_112) ->
+events(tc111_112) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
{?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}},
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
index 069f8c75fc..9d3e6a9e59 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
-define(eh, ct_test_support_eh).
@@ -56,13 +56,20 @@ init_per_testcase(TestCase, Config) ->
end_per_testcase(TestCase, Config) ->
ct_test_support:end_per_testcase(TestCase, Config).
-all(doc) ->
- [""];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [ts_if_1].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [
- ts_if_1
- ].
%%--------------------------------------------------------------------
@@ -87,14 +94,14 @@ ts_if_1(Config) when is_list(Config) ->
TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir, "ts_if_1_spec"),
{Opts,ERPid} = setup({spec,TestSpecName}, Config),
- ok = ct_test_support:run(ct, run_test, [Opts], Config),
+ ok = ct_test_support:run(Opts, Config),
Events = ct_test_support:get_events(ERPid, Config),
ct_test_support:log_events(ts_if_1,
reformat(Events, ?eh),
PrivDir),
- TestEvents = test_events(ts_if_1),
+ TestEvents = events_to_check(ts_if_1),
ok = ct_test_support:verify_events(TestEvents, Events, Config).
@@ -119,6 +126,15 @@ reformat(Events, EH) ->
%%%-----------------------------------------------------------------
%%% TEST EVENTS
%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
test_events(ts_if_1) ->
[
{?eh,start_logging,{'DEF','RUNDIR'}},
@@ -193,14 +209,14 @@ test_events(ts_if_1) ->
{?eh,tc_done,{ts_if_1_SUITE,{end_per_group,g2,[parallel]},ok}}]},
{?eh,tc_start,{ts_if_1_SUITE,tc12}},
- {?eh,tc_done,{undefined,undefined,{testcase_aborted,{abort_current_testcase,tc12},'_'}}},
+ {?eh,tc_done,{ts_if_1_SUITE,tc12,{failed,{testcase_aborted,'stopping tc12'}}}},
{?eh,test_stats,{2,5,{3,6}}},
{?eh,tc_start,{ts_if_1_SUITE,tc13}},
{?eh,tc_done,{ts_if_1_SUITE,tc13,ok}},
{?eh,test_stats,{3,5,{3,6}}},
{?eh,tc_start,{ts_if_1_SUITE,end_per_suite}},
{?eh,tc_done,{ts_if_1_SUITE,end_per_suite,ok}},
-%%!
+
{?eh,tc_start,{ts_if_2_SUITE,init_per_suite}},
{?eh,tc_done,{ts_if_2_SUITE,init_per_suite,
{failed,{error,{suite0_failed,{exited,suite0_goes_boom}}}}}},
diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl
index 8e90df21ce..47cea190dd 100644
--- a/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl
+++ b/lib/common_test/test/ct_test_server_if_1_SUITE_data/test_server_if/test/ts_if_1_SUITE.erl
@@ -93,6 +93,10 @@ init_per_testcase(_TestCase, Config) ->
%%--------------------------------------------------------------------
end_per_testcase(tc2, Config) ->
timer:sleep(5000);
+end_per_testcase(tc12, Config) ->
+ ct:comment("end_per_testcase(tc12) called!"),
+ ct:pal("end_per_testcase(tc12) called!", []),
+ ok;
end_per_testcase(_TestCase, _Config) ->
ok.
@@ -180,9 +184,9 @@ gtc2(_) ->
exit(should_have_been_skipped).
tc12(_) ->
- F = fun() -> ct:abort_current_testcase({abort_current_testcase,tc12}) end,
+ F = fun() -> ct:abort_current_testcase('stopping tc12') end,
spawn(F),
- timer:sleep(500),
+ timer:sleep(1000),
exit(should_have_been_aborted).
tc13(_) ->
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 6148e3280e..b4f1a0e71f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -23,12 +23,13 @@
%%%
-module(ct_test_support).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("common_test/include/ct_event.hrl").
-export([init_per_suite/1, init_per_suite/2, end_per_suite/1,
- init_per_testcase/2, end_per_testcase/2, write_testspec/3,
- run/4, get_opts/1, wait_for_ct_stop/1]).
+ init_per_testcase/2, end_per_testcase/2,
+ write_testspec/2, write_testspec/3,
+ run/2, run/4, get_opts/1, wait_for_ct_stop/1]).
-export([handle_event/2, start_event_receiver/1, get_events/2,
verify_events/3, reformat/2, log_events/3]).
@@ -42,6 +43,14 @@ init_per_suite(Config) ->
init_per_suite(Config, 50).
init_per_suite(Config, Level) ->
+ case os:type() of
+ {win32, _} ->
+ %% Extend timeout for windows as starting node
+ %% can take a long time there
+ test_server:timetrap( 120000 * test_server:timetrap_scale_factor());
+ _ ->
+ ok
+ end,
case delete_old_logs(os:type(), Config) of
{'EXIT',DelLogsReason} ->
test_server:format(0, "Failed to delete old log directories: ~p~n",
@@ -49,23 +58,40 @@ init_per_suite(Config, Level) ->
_ ->
ok
end,
+
+ start_slave(Config, Level).
+
+start_slave(Config,Level) ->
[_,Host] = string:tokens(atom_to_list(node()), "@"),
+
+ test_server:format(0, "Trying to start ~s~n", ["ct@"++Host]),
case slave:start(Host, ct, []) of
{error,Reason} ->
test_server:fail(Reason);
{ok,CTNode} ->
test_server:format(0, "Node ~p started~n", [CTNode]),
+ IsCover = test_server:is_cover(),
+ if IsCover ->
+ cover:start(CTNode);
+ true->
+ ok
+ end,
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
%% PrivDir as well as directory of Test Server suites
%% have to be in code path on Common Test node.
- true = rpc:call(CTNode, code, add_patha, [PrivDir]),
[_ | Parts] = lists:reverse(filename:split(DataDir)),
TSDir = filename:join(lists:reverse(Parts)),
- true = rpc:call(CTNode, code, add_patha, [TSDir]),
- test_server:format(Level, "Dirs added to code path (on ~w):~n"
- "~s~n~s~n", [CTNode,TSDir,PrivDir]),
+ AddPathDirs = case ?config(path_dirs, Config) of
+ undefined -> [];
+ Ds -> Ds
+ end,
+ PathDirs = [PrivDir,TSDir | AddPathDirs],
+ [true = rpc:call(CTNode, code, add_patha, [D]) || D <- PathDirs],
+ test_server:format(Level, "Dirs added to code path (on ~w):~n",
+ [CTNode]),
+ [io:format("~s~n", [D]) || D <- PathDirs],
TraceFile = filename:join(DataDir, "ct.trace"),
case file:read_file_info(TraceFile) of
@@ -87,6 +113,7 @@ end_per_suite(Config) ->
CTNode = ?config(ct_node, Config),
PrivDir = ?config(priv_dir, Config),
true = rpc:call(CTNode, code, del_path, [filename:join(PrivDir,"")]),
+ cover:stop(CTNode),
slave:stop(CTNode),
ok.
@@ -95,9 +122,17 @@ end_per_suite(Config) ->
init_per_testcase(_TestCase, Config) ->
{_,{_,LogDir}} = lists:keysearch(logdir, 1, get_opts(Config)),
- test_server:format("See Common Test logs here:\n"
- "<a href=\"file://~s/all_runs.html\">~s/all_runs.html</a>",
- [LogDir,LogDir]),
+ case lists:keysearch(master, 1, Config) of
+ false->
+ test_server:format("See Common Test logs here:\n\n"
+ "<a href=\"file://~s/all_runs.html\">~s/all_runs.html</a>\n"
+ "<a href=\"file://~s/index.html\">~s/index.html</a>",
+ [LogDir,LogDir,LogDir,LogDir]);
+ {value, _}->
+ test_server:format("See CT Master Test logs here:\n\n"
+ "<a href=\"file://~s/master_runs.html\">~s/master_runs.html</a>",
+ [LogDir,LogDir])
+ end,
Config.
%%%-----------------------------------------------------------------
@@ -105,15 +140,23 @@ init_per_testcase(_TestCase, Config) ->
end_per_testcase(_TestCase, Config) ->
CTNode = ?config(ct_node, Config),
- wait_for_ct_stop(CTNode),
- ok.
-
+ case wait_for_ct_stop(CTNode) of
+ %% Common test was not stopped to we restart node.
+ false ->
+ cover:stop(CTNode),
+ slave:stop(CTNode),
+ start_slave(Config,proplists:get_value(trace_level,Config)),
+ {fail, "Could not stop common_test"};
+ true ->
+ ok
+ end.
%%%-----------------------------------------------------------------
%%%
-
write_testspec(TestSpec, Dir, Name) ->
- TSFile = filename:join(Dir, Name),
+ write_testspec(TestSpec, filename:join(Dir, Name)).
+
+write_testspec(TestSpec, TSFile) ->
{ok,Dev} = file:open(TSFile, [write]),
[io:format(Dev, "~p.~n", [Entry]) || Entry <- TestSpec],
file:close(Dev),
@@ -158,10 +201,32 @@ get_opts(Config) ->
%%%-----------------------------------------------------------------
%%%
+run(Opts, Config) ->
+ CTNode = ?config(ct_node, Config),
+ Level = ?config(trace_level, Config),
+ %% use ct interface
+ test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n",
+ [Opts, CTNode]),
+ Result1 = rpc:call(CTNode, ct, run_test, [Opts]),
+
+ %% use run_test interface (simulated)
+ test_server:format(Level, "Saving start opts on ~p: ~p~n", [CTNode,Opts]),
+ rpc:call(CTNode, application, set_env, [common_test, run_test_start_opts, Opts]),
+ test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", [CTNode]),
+ Result2 = rpc:call(CTNode, ct_run, script_start, []),
+ case {Result1,Result2} of
+ {ok,ok} ->
+ ok;
+ {E,_} when E =/= ok ->
+ E;
+ {_,E} when E =/= ok ->
+ E
+ end.
+
run(M, F, A, Config) ->
CTNode = ?config(ct_node, Config),
Level = ?config(trace_level, Config),
- test_server:format(Level, "Calling ~w:~w(~p) on ~p~n",
+ test_server:format(Level, "~nCalling ~w:~w(~p) on ~p~n",
[M, F, A, CTNode]),
rpc:call(CTNode, M, F, A).
@@ -175,11 +240,11 @@ wait_for_ct_stop(CTNode) ->
wait_for_ct_stop(0, CTNode) ->
test_server:format(0, "Giving up! Stopping ~p.", [CTNode]),
- ok;
+ false;
wait_for_ct_stop(Retries, CTNode) ->
case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of
undefined ->
- ok;
+ true;
Pid ->
test_server:format(0, "Waiting for CT (~p) to finish (~p)...",
[Pid,Retries]),
@@ -231,8 +296,12 @@ verify_events(TEvs, Evs, Config) ->
ok
end.
+verify_events1([TestEv|_], [{TEH,#event{name=stop_logging,node=Node,data=_}}|_], Node, _)
+ when element(1,TestEv) == TEH, element(2,TestEv) =/= stop_logging ->
+ test_server:format("Failed to find ~p in the list of events!~n", [TestEv]),
+ exit({event_not_found,TestEv});
+
verify_events1(TEvs = [TestEv | TestEvs], Evs = [_|Events], Node, Config) ->
-%% test_server:format("Next expected event: ~p~n", [TestEv]),
case catch locate(TestEv, Node, Evs, Config) of
nomatch ->
verify_events1(TEvs, Events, Node, Config);
@@ -303,13 +372,33 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
case Evs of
[{TEH,#event{name=tc_start,
node=Node,
- data={M,{init_per_group,GroupName,Props}}}},
- {TEH,#event{name=tc_done,
- node=Node,
- data={M,{init_per_group,GroupName,Props},R}}} | Es] ->
+ data={M,{init_per_group,
+ GroupName,Props}}}}|Es] ->
+ %% Use dropwhile here as a tc_done from a
+ %% previous testcase might sneak in here
+ EvsG = lists:dropwhile(
+ fun({EH,#event{name=tc_done,
+ node=EvNode,
+ data={EvM,{init_per_group,
+ EvGroupName,
+ EvProps},EvR}}})
+ when TEH == EH, EvNode == Node, EvM == M,
+ EvGroupName == GroupName,
+ EvProps == Props,
+ EvR == R ->
+ false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}})
+ when EH == TEH, EvNode == Node ->
+ exit({group_init_done_not_found,
+ GroupName,Props});
+ (_) ->
+ true
+ end, Es),
+
test_server:format("Found ~p!", [InitStart]),
test_server:format("Found ~p!", [InitDone]),
- {TEs,Es};
+ {TEs,EvsG};
_ ->
nomatch
end;
@@ -332,6 +421,10 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node,
Mod == M, Func == F ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_start_not_found,TEv});
(_) ->
true
end, Evs1),
@@ -345,6 +438,10 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node,
Mod == M, Func == F ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_done_not_found,TEv});
(_) ->
true
end, Evs2),
@@ -388,6 +485,10 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node, Mod == M,
EvGName == GroupName, EvProps == Props ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_start_not_found,TEv});
(_) ->
true
end, RemEvs),
@@ -410,6 +511,10 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node, Mod == M,
EvGName == GroupName, EvProps == Props, Res == R ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_done_not_found,TEv});
(_) ->
true
end, RemEvs),
@@ -429,6 +534,10 @@ locate({parallel,TEvs}, Node, Evs, Config) ->
data={Mod,end_per_group,Reason}}}) when
EH == TEH, EvNode == Node, Mod == M, Reason == R ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_auto_skip_not_found,TEv});
(_) ->
true
end, RemEvs),
@@ -533,6 +642,10 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node,
Mod == M, Func == F ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_start_not_found,TEv});
(_) ->
true
end, Evs1),
@@ -576,6 +689,10 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node, Mod == M,
EvGName == GroupName ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_start_not_found,TEv});
(_) ->
true
end, RemEvs),
@@ -611,6 +728,10 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
EH == TEH, EvNode == Node, Mod == M,
EvGName == GroupName, Res == R ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_done_not_found,TEv});
(_) ->
true
end, RemEvs),
@@ -643,6 +764,10 @@ locate({shuffle,TEvs}, Node, Evs, Config) ->
data={Mod,end_per_group,Reason}}}) when
EH == TEH, EvNode == Node, Mod == M, Reason == R ->
false;
+ ({EH,#event{name=stop_logging,
+ node=EvNode,data=_}}) when
+ EH == TEH, EvNode == Node ->
+ exit({tc_auto_skip_not_found,TEv});
(_) ->
true
end, RemEvs),
@@ -762,22 +887,49 @@ locate({TEH,tc_done,{undefined,undefined,{testcase_aborted,
nomatch
end;
-%% matches any event of type Name
-locate({TEH,Name,Data}, Node, [Ev|Evs], Config) when Data == '_' ->
- case Ev of
- {TEH,#event{name=Name, node=Node}} ->
- {Config,Evs};
+%% Negative matching: Given two events, the first should not be present before
+%% the other is matched.
+locate({negative,NotMatch, Match} = Neg, Node, Evs, Config) ->
+ case locate(NotMatch, Node, Evs, Config) of
+ nomatch ->
+ locate(Match, Node, Evs, Config);
_ ->
- nomatch
+ exit({found_negative_event,Neg})
end;
-locate({TEH,Name,Data}, Node, [Ev|Evs], Config) ->
- case Ev of
- {TEH,#event{name=Name, node=Node, data=Data}} ->
- {Config,Evs};
- _ ->
+%% matches any event of type Name
+locate({TEH,Name,Data}, Node, [{TEH,#event{name=Name,
+ data = EvData,
+ node = Node}}|Evs],
+ Config) ->
+ try match_data(Data, EvData) of
+ match ->
+ {Config,Evs}
+ catch _:_ ->
nomatch
- end.
+ end;
+
+locate({_TEH,_Name,_Data}, _Node, [_|_Evs], _Config) ->
+ nomatch.
+
+match_data(D,D) ->
+ match;
+match_data('_',_) ->
+ match;
+match_data(Fun,Data) when is_function(Fun) ->
+ Fun(Data);
+match_data('$proplist',Proplist) ->
+ match_data(
+ fun(List) ->
+ lists:foreach(fun({_,_}) -> ok end,List)
+ end,Proplist);
+match_data([H1|MatchT],[H2|ValT]) ->
+ match_data(H1,H2),
+ match_data(MatchT,ValT);
+match_data(Tuple1,Tuple2) when is_tuple(Tuple1),is_tuple(Tuple2) ->
+ match_data(tuple_to_list(Tuple1),tuple_to_list(Tuple2));
+match_data([],[]) ->
+ match.
log_events(TC, Events, PrivDir) ->
LogFile = filename:join(PrivDir, atom_to_list(TC)++".events"),
@@ -785,7 +937,8 @@ log_events(TC, Events, PrivDir) ->
io:format(Dev, "[~n", []),
log_events1(Events, Dev, " "),
file:close(Dev),
- io:format("Events written to logfile: ~p~n", [LogFile]),
+ io:format("Events written to logfile: <a href=\"file://~s\">~s</a>~n",
+ [LogFile,LogFile]),
io:format(user, "Events written to logfile: ~p~n", [LogFile]).
log_events1(Evs, Dev, "") ->
diff --git a/lib/common_test/test/ct_test_support_eh.erl b/lib/common_test/test/ct_test_support_eh.erl
index fd3ae18746..70f73b9b81 100644
--- a/lib/common_test/test/ct_test_support_eh.erl
+++ b/lib/common_test/test/ct_test_support_eh.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -44,8 +44,20 @@
%% Description: Whenever a new event handler is added to an event manager,
%% this function is called to initialize the event handler.
%%--------------------------------------------------------------------
+init(String = [X|_]) when is_integer(X) ->
+ case erl_scan:string(String++".") of
+ {ok,Ts,_} ->
+ case erl_parse:parse_term(Ts) of
+ {ok,Args} ->
+ init(Args);
+ _ ->
+ init(String)
+ end;
+ _ ->
+ init(String)
+ end;
+
init(Args) ->
-
S1 = case lists:keysearch(cbm, 1, Args) of
{_,{cbm,CBM}} ->
#state{cbm=CBM};
@@ -58,7 +70,8 @@ init(Args) ->
_ ->
S1
end,
- print(S2#state.trace_level, "Event Handler ~w started!~n", [?MODULE]),
+ print(S2#state.trace_level, "Event Handler ~w started with ~p~n",
+ [?MODULE,Args]),
{ok,S2}.
%%--------------------------------------------------------------------
diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl
new file mode 100644
index 0000000000..616c2db869
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE.erl
@@ -0,0 +1,1380 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File: ct_testspec_1_SUITE
+%%%
+%%% Description:
+%%% Test test specifications
+%%%
+%%% The suites used for the test are located in the data directory.
+%%%-------------------------------------------------------------------
+-module(ct_testspec_1_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
+
+-define(eh, ct_test_support_eh).
+
+%%--------------------------------------------------------------------
+%% TEST SERVER CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Description: Since Common Test starts another Test Server
+%% instance, the tests need to be performed on a separate node (or
+%% there will be clashes with logging processes etc).
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config1 = ct_test_support:init_per_suite(Config),
+ Config1.
+
+end_per_suite(Config) ->
+ ct_test_support:end_per_suite(Config).
+
+init_per_testcase(TestCase, Config) ->
+ ct_test_support:init_per_testcase(TestCase, Config).
+
+end_per_testcase(TestCase, Config) ->
+ ct_test_support:end_per_testcase(TestCase, Config).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [all_suites, skip_all_suites, suite, skip_suite,
+ all_testcases, skip_all_testcases, testcase,
+ skip_testcase, all_groups, skip_all_groups, group,
+ skip_group, group_all_testcases,
+ skip_group_all_testcases, group_testcase,
+ skip_group_testcase, topgroup, subgroup, skip_subgroup,
+ subgroup_all_testcases, skip_subgroup_all_testcases,
+ subgroup_testcase, skip_subgroup_testcase,
+ sub_skipped_by_top, testcase_in_multiple_groups,
+ order_of_tests_in_multiple_dirs_no_merge_tests,
+ order_of_tests_in_multiple_suites_no_merge_tests,
+ order_of_suites_in_multiple_dirs_no_merge_tests,
+ order_of_groups_in_multiple_dirs_no_merge_tests,
+ order_of_groups_in_multiple_suites_no_merge_tests,
+ order_of_tests_in_multiple_dirs,
+ order_of_tests_in_multiple_suites,
+ order_of_suites_in_multiple_dirs,
+ order_of_groups_in_multiple_dirs,
+ order_of_groups_in_multiple_suites,
+ order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
+ order_of_tests_in_multiple_suites_with_skip,
+ all_plus_one_tc_no_merge_tests,
+ all_plus_one_tc].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+%%%-----------------------------------------------------------------
+%%%
+
+all_suites(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{label,"all_suites"},
+ {suites,TestDir,all}],
+
+ setup_and_execute(all_suites, TestSpec, Config).
+
+skip_all_suites(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{label,skip_all_suites},
+ {suites,TestDir,all},
+ {skip_suites,TestDir,all,"SKIPPED!"}],
+
+ setup_and_execute(skip_all_suites, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+suite(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{label,undefined},
+ {suites,TestDir,simple_1_SUITE}],
+
+ setup_and_execute(suite, TestSpec, Config).
+
+skip_suite(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{suites,TestDir,[simple_1_SUITE,simple_2_SUITE]},
+ {skip_suites,TestDir,simple_1_SUITE,"SKIPPED!"}],
+
+ setup_and_execute(skip_suite, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+all_testcases(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{cases,TestDir,simple_1_SUITE,all}],
+
+ setup_and_execute(all_testcases, TestSpec, Config).
+
+skip_all_testcases(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{suites,TestDir,[simple_1_SUITE]},
+ {skip_cases,TestDir,simple_1_SUITE,all,"SKIPPED!"}],
+
+ setup_and_execute(skip_all_testcases, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+testcase(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{cases,TestDir,simple_1_SUITE,tc1}],
+
+ setup_and_execute(testcase, TestSpec, Config).
+
+skip_testcase(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "suites_1"),
+ TestSpec = [{cases,TestDir,simple_1_SUITE,[tc1,tc2]},
+ {cases,TestDir,simple_2_SUITE,[tc2,tc1]},
+ {skip_cases,TestDir,simple_1_SUITE,[tc1],"SKIPPED!"},
+ {skip_cases,TestDir,simple_2_SUITE,tc2,"SKIPPED!"}],
+
+ setup_and_execute(skip_testcase, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+all_groups(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_11_SUITE,all}],
+
+ setup_and_execute(all_groups, TestSpec, Config).
+
+skip_all_groups(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_11_SUITE,all},
+ {skip_groups,TestDir,groups_11_SUITE,all,"SKIPPED!"}],
+
+ setup_and_execute(skip_all_groups, TestSpec, 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).
+
+skip_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_1b]},
+ {skip_groups,TestDir,groups_11_SUITE,
+ [test_group_1b,test_group_2,test_group_7],"SKIPPED!"}],
+
+ setup_and_execute(skip_group, TestSpec, Config).
+
+
+%%%-----------------------------------------------------------------
+%%%
+
+group_all_testcases(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,{cases,all}}],
+
+ setup_and_execute(group_all_testcases, TestSpec, Config).
+
+skip_group_all_testcases(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_1b]},
+ {skip_groups,TestDir,groups_11_SUITE,
+ test_group_1b,{cases,all},"SKIPPED!"},
+ {skip_groups,TestDir,groups_11_SUITE,
+ test_group_1a,{cases,all},"SKIPPED!"}],
+
+ setup_and_execute(skip_group_all_testcases, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+group_testcase(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,{cases,testcase_1a}}],
+
+ setup_and_execute(group_testcase, TestSpec, Config).
+
+skip_group_testcase(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,
+ {cases,[testcase_1a,testcase_1b]}},
+ {groups,TestDir,groups_11_SUITE,test_group_1b,
+ {cases,[testcase_1b,testcase_1a]}},
+ {skip_groups,TestDir,groups_11_SUITE,
+ test_group_1a,{cases,testcase_1b},"SKIPPED!"},
+ {skip_groups,TestDir,groups_11_SUITE,
+ test_group_1b,{cases,[testcase_1a]},"SKIPPED!"}],
+
+ setup_and_execute(skip_group_testcase, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+topgroup(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_2},
+ {groups,TestDir,groups_12_SUITE,test_group_4}],
+
+ setup_and_execute(topgroup, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+subgroup(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_3}],
+
+ setup_and_execute(subgroup, TestSpec, Config).
+
+skip_subgroup(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,[test_group_4]},
+ {skip_groups,TestDir,groups_12_SUITE,
+ test_group_8,"SKIPPED!"}],
+
+ setup_and_execute(skip_subgroup, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+subgroup_all_testcases(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,
+ test_group_5,{cases,all}},
+ {groups,TestDir,groups_12_SUITE,
+ test_group_3,{cases,all}}],
+
+ setup_and_execute(subgroup_all_testcases, TestSpec, Config).
+
+skip_subgroup_all_testcases(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_4},
+ {skip_groups,TestDir,groups_12_SUITE,
+ test_group_5,{cases,all},"SKIPPED!"}],
+
+ setup_and_execute(skip_subgroup_all_testcases, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+subgroup_testcase(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,
+ test_group_7,{cases,testcase_7a}},
+ {groups,TestDir,groups_12_SUITE,
+ test_group_3,{cases,testcase_3b}}],
+
+ setup_and_execute(subgroup_testcase, TestSpec, Config).
+
+skip_subgroup_testcase(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_5},
+ {skip_groups,TestDir,groups_12_SUITE,
+ test_group_7,{cases,[testcase_7a,testcase_7b]},"SKIPPED!"}],
+
+ setup_and_execute(skip_subgroup_testcase, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+%%!
+%%! Somewhat weird result from this one:
+%%!
+sub_skipped_by_top(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir,groups_12_SUITE,test_group_5},
+ {skip_groups,TestDir,groups_12_SUITE,test_group_4,"SKIPPED!"}],
+
+ setup_and_execute(sub_skipped_by_top, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+testcase_in_multiple_groups(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir = filename:join(DataDir, "groups_1"),
+ TestSpec = [{cases,TestDir,groups_12_SUITE,[testcase_1a,testcase_1b]},
+ {skip_cases,TestDir,groups_12_SUITE,[testcase_1b],"SKIPPED!"}],
+
+ setup_and_execute(testcase_in_multiple_groups, TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_tests_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestDir2 = filename:join(DataDir, "groups_2"),
+ TestSpec = [{merge_tests, false},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1a]},
+ {cases,TestDir2,groups_22_SUITE,[testcase_1]},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
+
+ setup_and_execute(order_of_tests_in_multiple_dirs_no_merge_tests,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_tests_in_multiple_suites_no_merge_tests(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{merge_tests, false},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1a]},
+ {cases,TestDir1,groups_11_SUITE,[testcase_1]},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
+
+ setup_and_execute(order_of_tests_in_multiple_suites_no_merge_tests,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_suites_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestDir2 = filename:join(DataDir, "groups_2"),
+ TestSpec = [{merge_tests, false},
+ {suites,TestDir1,groups_12_SUITE},
+ {suites,TestDir2,groups_22_SUITE},
+ {suites,TestDir1,groups_11_SUITE}],
+
+ setup_and_execute(order_of_suites_in_multiple_dirs_no_merge_tests,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_groups_in_multiple_dirs_no_merge_tests(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestDir2 = filename:join(DataDir, "groups_2"),
+ TestSpec = [{merge_tests, false},
+ {groups,TestDir1,groups_12_SUITE,test_group_1a},
+ {groups,TestDir2,groups_22_SUITE,test_group_1a},
+ {groups,TestDir1,groups_12_SUITE,test_group_1b}],
+
+ setup_and_execute(order_of_groups_in_multiple_dirs_no_merge_tests,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_groups_in_multiple_suites_no_merge_tests(Config)
+ when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{merge_tests, false},
+ {groups,TestDir1,groups_12_SUITE,test_group_1a},
+ {groups,TestDir1,groups_11_SUITE,test_group_1a},
+ {groups,TestDir1,groups_12_SUITE,test_group_1b}],
+
+ setup_and_execute(order_of_groups_in_multiple_suites_no_merge_tests,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_tests_in_multiple_suites_with_skip_no_merge_tests(Config)
+ when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{merge_tests, false},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1a]},
+ {cases,TestDir1,groups_11_SUITE,[testcase_1]},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1b]},
+ {cases,TestDir1,groups_11_SUITE,[testcase_2]},
+ {skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it"}],
+
+ setup_and_execute(
+ order_of_tests_in_multiple_suites_with_skip_no_merge_tests,
+ TestSpec, Config).
+
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_tests_in_multiple_dirs(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestDir2 = filename:join(DataDir, "groups_2"),
+ TestSpec = [{cases,TestDir1,groups_12_SUITE,[testcase_1a]},
+ {cases,TestDir2,groups_22_SUITE,[testcase_1]},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
+
+ setup_and_execute(order_of_tests_in_multiple_dirs,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_tests_in_multiple_suites(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{cases,TestDir1,groups_12_SUITE,[testcase_1a]},
+ {cases,TestDir1,groups_11_SUITE,[testcase_1]},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1b]}],
+
+ setup_and_execute(order_of_tests_in_multiple_suites,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_suites_in_multiple_dirs(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestDir2 = filename:join(DataDir, "groups_2"),
+ TestSpec = [{suites,TestDir1,groups_12_SUITE},
+ {suites,TestDir2,groups_22_SUITE},
+ {suites,TestDir1,groups_11_SUITE}],
+
+ setup_and_execute(order_of_suites_in_multiple_dirs,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_groups_in_multiple_dirs(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestDir2 = filename:join(DataDir, "groups_2"),
+ TestSpec = [{groups,TestDir1,groups_12_SUITE,test_group_1a},
+ {groups,TestDir2,groups_22_SUITE,test_group_1a},
+ {groups,TestDir1,groups_12_SUITE,test_group_1b}],
+
+ setup_and_execute(order_of_groups_in_multiple_dirs,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_groups_in_multiple_suites(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{groups,TestDir1,groups_12_SUITE,test_group_1a},
+ {groups,TestDir1,groups_11_SUITE,test_group_1a},
+ {groups,TestDir1,groups_12_SUITE,test_group_1b}],
+
+ setup_and_execute(order_of_groups_in_multiple_suites,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+order_of_tests_in_multiple_suites_with_skip(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{cases,TestDir1,groups_12_SUITE,[testcase_1a]},
+ {cases,TestDir1,groups_11_SUITE,[testcase_1]},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1b]},
+ {cases,TestDir1,groups_11_SUITE,[testcase_2]},
+ {skip_cases,TestDir1,groups_12_SUITE,[testcase_1b],"Skip it!"}],
+
+ setup_and_execute(order_of_tests_in_multiple_suites_with_skip,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+all_plus_one_tc_no_merge_tests(Config) when is_list(Config) ->
+
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{merge_tests,false},
+ {suites,TestDir1,groups_12_SUITE},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1a]}],
+
+ setup_and_execute(all_plus_one_tc_no_merge_tests,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%%
+
+all_plus_one_tc(Config) when is_list(Config) ->
+
+ DataDir = ?config(data_dir, Config),
+
+ TestDir1 = filename:join(DataDir, "groups_1"),
+ TestSpec = [{suites,TestDir1,groups_12_SUITE},
+ {cases,TestDir1,groups_12_SUITE,[testcase_1a]}],
+
+ setup_and_execute(all_plus_one_tc,
+ TestSpec, Config).
+
+%%%-----------------------------------------------------------------
+%%% HELP FUNCTIONS
+%%%-----------------------------------------------------------------
+
+setup_and_execute(TCName, TestSpec, Config) ->
+ SpecFile = create_spec_file(?config(priv_dir, Config),
+ TCName, TestSpec),
+ TestTerms =
+ case lists:keymember(label, 1, TestSpec) of
+ true -> [{spec,SpecFile}];
+ false -> [{spec,SpecFile},{label,TCName}]
+ end,
+ {Opts,ERPid} = setup(TestTerms, Config),
+ ok = ct_test_support:run(Opts, Config),
+ TestSpec1 = [{logdir,proplists:get_value(logdir,Opts)},
+ {label,proplists:get_value(label,TestTerms)} | TestSpec],
+ ok = ct_test_support:run(ct, run_testspec, [TestSpec1], Config),
+ Events = ct_test_support:get_events(ERPid, Config),
+
+ ct_test_support:log_events(TCName,
+ reformat(Events, ?eh),
+ ?config(priv_dir, Config)),
+
+ TestEvents = events_to_check(TCName),
+ ok = ct_test_support:verify_events(TestEvents, Events, Config).
+
+create_spec_file(SpecDir, TCName, TestSpec) ->
+ FileName = filename:join(SpecDir,
+ atom_to_list(TCName)++".spec"),
+ {ok,Dev} = file:open(FileName, [write]),
+ [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec],
+ file:close(Dev),
+ io:format("~nTest spec created here~n~n<a href=\"file://~s\">~s</a>~n",
+ [FileName,FileName]),
+ FileName.
+
+setup(Test, Config) when is_tuple(Test) ->
+ setup([Test], Config);
+setup(Tests, Config) ->
+ Opts0 = ct_test_support:get_opts(Config),
+ Level = ?config(trace_level, Config),
+ EvHArgs = [{cbm,ct_test_support},{trace_level,Level}],
+ Opts = Opts0 ++ Tests ++ [{event_handler,{?eh,EvHArgs}}],
+ ERPid = ct_test_support:start_event_receiver(Config),
+ {Opts,ERPid}.
+
+reformat(Events, EH) ->
+ ct_test_support:reformat(Events, EH).
+%reformat(Events, _EH) ->
+% Events.
+
+%%%-----------------------------------------------------------------
+%%% TEST EVENTS
+%%%-----------------------------------------------------------------
+events_to_check(Test) ->
+ %% 2 tests (ct:run_test + script_start) is default
+ events_to_check(Test, 2).
+
+events_to_check(_, 0) ->
+ [];
+events_to_check(Test, N) ->
+ test_events(Test) ++ events_to_check(Test, N-1).
+
+test_events(all_suites) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{simple_1_SUITE,init_per_suite}},
+ {?eh,tc_done,{simple_1_SUITE,end_per_suite,'_'}},
+ {?eh,tc_start,{simple_2_SUITE,init_per_suite}},
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_done,{simple_2_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_all_suites) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_user_skip,{simple_1_SUITE,all,"SKIPPED!"}},
+ {?eh,tc_user_skip,{simple_2_SUITE,all,"SKIPPED!"}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(suite) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{simple_1_SUITE,init_per_suite}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{simple_1_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_suite) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_user_skip,{simple_1_SUITE,all,"SKIPPED!"}},
+ {?eh,tc_done,{simple_2_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(all_testcases) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{simple_1_SUITE,init_per_suite}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{simple_1_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_all_testcases) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_user_skip,{simple_1_SUITE,all,"SKIPPED!"}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(testcase) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{simple_1_SUITE,init_per_suite}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {negative,{?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{simple_1_SUITE,end_per_suite,'_'}}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_testcase) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{simple_1_SUITE,init_per_suite}},
+ {?eh,tc_user_skip,{simple_1_SUITE,tc1,"SKIPPED!"}},
+ {?eh,tc_start,{simple_1_SUITE,tc2}},
+ {?eh,tc_start,{simple_1_SUITE,end_per_suite}},
+
+ {?eh,tc_start,{simple_2_SUITE,init_per_suite}},
+ {?eh,tc_user_skip,{simple_2_SUITE,tc2,"SKIPPED!"}},
+ {?eh,tc_start,{simple_2_SUITE,tc1}},
+ {?eh,test_stats,{2,0,{2,0}}},
+ {?eh,tc_start,{simple_2_SUITE,end_per_suite}},
+
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(all_groups) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_11_SUITE,init_per_suite}},
+ {?eh,test_stats,{12,0,{0,0}}},
+ {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_all_groups) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_11_SUITE,init_per_suite}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_1a},"SKIPPED!"}},
+ {?eh,test_stats,{0,0,{1,0}}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_1b},"SKIPPED!"}},
+ {?eh,test_stats,{0,0,{2,0}}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_2},"SKIPPED!"}},
+ {?eh,test_stats,{0,0,{3,0}}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_4},"SKIPPED!"}},
+ {?eh,test_stats,{0,0,{4,0}}},
+ {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(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_done,{groups_11_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_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_user_skip, {groups_11_SUITE,{group,test_group_1b},"SKIPPED!"}},
+ {?eh,test_stats,{2,0,{1,0}}},
+ {negative,{?eh,tc_user_skip,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(group_all_testcases) ->
+ [
+ {?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_done,{groups_11_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_group_all_testcases) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_11_SUITE,init_per_suite}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_1a},"SKIPPED!"}},
+ {?eh,tc_user_skip, {groups_11_SUITE,{group,test_group_1b},"SKIPPED!"}},
+ {?eh,test_stats,{0,0,{2,0}}},
+ {?eh,tc_start,{groups_11_SUITE,end_per_suite}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(group_testcase) ->
+ [
+ {?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,test_stats,{1,0,{0,0}}},
+ {negative,{?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},'_'}}},
+
+ {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_group_testcase) ->
+ [
+ {?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_user_skip,{groups_11_SUITE,testcase_1b,"SKIPPED!"}},
+ {?eh,test_stats,{1,0,{1,0}}},
+ {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1a,[]},'_'}},
+
+ {?eh,tc_start,{groups_11_SUITE,{init_per_group,test_group_1b,[]}}},
+ {?eh,tc_start,{groups_11_SUITE,testcase_1b}},
+ {?eh,tc_user_skip,{groups_11_SUITE,testcase_1a,"SKIPPED!"}},
+ {?eh,test_stats,{2,0,{2,0}}},
+ {?eh,tc_done,{groups_11_SUITE,{end_per_group,test_group_1b,[]},'_'}},
+
+ {negative,{?eh,tc_user_skip,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(topgroup) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}}
+ ],
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[]}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[]}}}
+ ],
+ {?eh,test_stats,{6,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]},ok}}]},
+
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]}}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
+ {shuffle,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_8,
+ [{shuffle,'_'},sequence]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_8,
+ [{shuffle,'_'},sequence]},ok}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_8,
+ [shuffle,sequence]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_8,
+ [shuffle,sequence]},ok}}
+ ]},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]},ok}}
+ ]},
+ {?eh,test_stats,{12,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]},ok}}]},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_4,[]}}}],
+
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(subgroup) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}}
+ ],
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[]}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[]}}}
+ ],
+ {?eh,test_stats,{4,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]},ok}}]},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_subgroup) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]}}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
+ {?eh,tc_user_skip,
+ {groups_12_SUITE,{group,test_group_8},"SKIPPED!"}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]},ok}}
+ ]},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]},ok}}]},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_4,[]}}}],
+
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(subgroup_all_testcases) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]}}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
+ {shuffle,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_8,
+ [{shuffle,'_'},sequence]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_8,
+ [{shuffle,'_'},sequence]},ok}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_8,
+ [shuffle,sequence]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_8,
+ [shuffle,sequence]},ok}}
+ ]},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]},ok}}
+ ]},
+ {?eh,test_stats,{6,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]},ok}}]},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_4,[]}}}],
+
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}}
+ ],
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[]}}},
+ {?eh,test_stats,{10,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[]}}}
+ ],
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]},ok}}]},
+
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_subgroup_all_testcases) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]},ok}},
+ {?eh,tc_user_skip,{groups_12_SUITE,{group,test_group_5},"SKIPPED!"}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_4,[]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_4,[]},ok}}
+ ],
+
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(subgroup_testcase) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]}}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
+ {?eh,test_stats,{1,0,{0,0}}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]},ok}}
+ ]},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]},ok}}]},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_4,[]}}}],
+
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_2,[parallel]},ok}},
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[{repeat,2}]}}},
+ {?eh,test_stats,{2,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[{repeat,2}]}}}
+ ],
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_3,[]}}},
+ {?eh,test_stats,{3,0,{0,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_3,[]}}}
+ ],
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_2,[parallel]},ok}}]},
+
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(skip_subgroup_testcase) ->
+ [
+
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_4,[]}}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_5,[parallel]},ok}},
+ {parallel,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_6,[parallel]},ok}},
+ [{?eh,tc_start,{groups_12_SUITE,{init_per_group,test_group_7,'_'}}},
+ {?eh,tc_user_skip, {groups_12_SUITE,testcase_7a,"SKIPPED!"}},
+ {?eh,test_stats,{1,0,{1,0}}},
+ {?eh,tc_user_skip, {groups_12_SUITE,testcase_7b,"SKIPPED!"}},
+ {?eh,test_stats,{1,0,{2,0}}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_7,'_'}}}],
+ {shuffle,
+ [{?eh,tc_start,
+ {groups_12_SUITE,{init_per_group,test_group_8,
+ [{shuffle,'_'},sequence]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{init_per_group,test_group_8,
+ [{shuffle,'_'},sequence]},ok}},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_8,
+ [shuffle,sequence]}}},
+ {?eh,tc_done,{groups_12_SUITE,{end_per_group,test_group_8,
+ [shuffle,sequence]},ok}}
+ ]},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_6,[parallel]},ok}}
+ ]},
+ {?eh,test_stats,{4,0,{2,0}}},
+ {?eh,tc_start,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]}}},
+ {?eh,tc_done,
+ {groups_12_SUITE,{end_per_group,test_group_5,[parallel]},ok}}]},
+ {?eh,tc_start,{groups_12_SUITE,{end_per_group,test_group_4,[]}}}],
+
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+
+ ];
+
+test_events(sub_skipped_by_top) ->
+ [
+ {?eh,start_logging,'_'},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+
+ {?eh,tc_user_skip,{groups_12_SUITE,{group,test_group_4},"SKIPPED!"}},
+
+ {negative,
+ {?eh,tc_user_skip,{groups_12_SUITE,{group,test_group_4},"SKIPPED!"}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}}},
+
+ {negative,{?eh,tc_start,'_'},{?eh,stop_logging,'_'}}
+ ];
+
+test_events(testcase_in_multiple_groups) ->
+ [];
+
+test_events(order_of_tests_in_multiple_dirs_no_merge_tests) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1a}},
+ {?eh,tc_done, {groups_12_SUITE,testcase_1a,
+ {failed,{error,{test_case_failed,no_group_data}}}}},
+ {?eh,tc_start,{groups_22_SUITE,testcase_1}},
+ {?eh,tc_done,{groups_22_SUITE,testcase_1,ok}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1b}},
+ {?eh,tc_done, {groups_12_SUITE,testcase_1b,
+ {failed,{error,{test_case_failed,no_group_data}}}}},
+ {?eh,stop_logging,[]}
+ ];
+test_events(order_of_tests_in_multiple_suites_no_merge_tests) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,testcase_1}},
+ {?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1b}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_1b,'_'}},
+ {?eh,stop_logging,[]}
+ ];
+test_events(order_of_suites_in_multiple_dirs_no_merge_tests) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
+ {?eh,tc_start,{groups_12_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {?eh,tc_start,{groups_22_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_22_SUITE,init_per_suite,'_'}},
+ {?eh,tc_start,{groups_22_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_22_SUITE,end_per_suite,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_11_SUITE,init_per_suite,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
+ {?eh,stop_logging,[]}];
+test_events(order_of_groups_in_multiple_dirs_no_merge_tests) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,tc_start, {groups_22_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_22_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1b,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
+
+ {?eh,stop_logging,[]}];
+test_events(order_of_groups_in_multiple_suites_no_merge_tests) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,tc_start, {groups_11_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_11_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1b,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
+
+ {?eh,stop_logging,[]}];
+test_events(order_of_tests_in_multiple_suites_with_skip_no_merge_tests) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,testcase_1}},
+ {?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
+ {?eh,tc_user_skip,{groups_12_SUITE,testcase_1b,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,testcase_2}},
+ {?eh,tc_done,{groups_11_SUITE,testcase_2,ok}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(order_of_tests_in_multiple_dirs) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1a}},
+ {?eh,tc_done,
+ {groups_12_SUITE,testcase_1a,
+ {failed,{error,{test_case_failed,no_group_data}}}}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1b}},
+ {?eh,tc_done,
+ {groups_12_SUITE,testcase_1b,
+ {failed,{error,{test_case_failed,no_group_data}}}}},
+ {?eh,tc_start,{groups_22_SUITE,testcase_1}},
+ {?eh,tc_done,{groups_22_SUITE,testcase_1,ok}},
+ {?eh,stop_logging,[]}
+ ];
+test_events(order_of_tests_in_multiple_suites) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
+
+ {?eh,tc_start,{groups_12_SUITE,testcase_1b}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_1b,'_'}},
+
+ {?eh,tc_start,{groups_11_SUITE,testcase_1}},
+ {?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
+ {?eh,stop_logging,[]}
+ ];
+test_events(order_of_suites_in_multiple_dirs) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,init_per_suite,'_'}},
+ {?eh,tc_start,{groups_12_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+
+ {?eh,tc_start,{groups_11_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_11_SUITE,init_per_suite,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_11_SUITE,end_per_suite,'_'}},
+
+ {?eh,tc_start,{groups_22_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_22_SUITE,init_per_suite,'_'}},
+ {?eh,tc_start,{groups_22_SUITE,end_per_suite}},
+ {?eh,tc_done,{groups_22_SUITE,end_per_suite,'_'}},
+ {?eh,stop_logging,[]}];
+test_events(order_of_groups_in_multiple_dirs) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1b,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
+
+ {?eh,tc_start, {groups_22_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_22_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,stop_logging,[]}];
+test_events(order_of_groups_in_multiple_suites) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,tc_start, {groups_12_SUITE,{init_per_group,test_group_1b,'_'}}},
+ {?eh,tc_done, {groups_12_SUITE,{end_per_group,test_group_1b,'_'},'_'}},
+
+ {?eh,tc_start, {groups_11_SUITE,{init_per_group,test_group_1a,'_'}}},
+ {?eh,tc_done, {groups_11_SUITE,{end_per_group,test_group_1a,'_'},'_'}},
+
+ {?eh,stop_logging,[]}];
+
+test_events(order_of_tests_in_multiple_suites_with_skip) ->
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,testcase_1a}},
+ {?eh,tc_done,{groups_12_SUITE,testcase_1a,'_'}},
+ {?eh,tc_user_skip,{groups_12_SUITE,testcase_1b,'_'}},
+ {?eh,tc_start,{groups_11_SUITE,testcase_1}},
+ {?eh,tc_done,{groups_11_SUITE,testcase_1,ok}},
+ {?eh,tc_start,{groups_11_SUITE,testcase_2}},
+ {?eh,tc_done,{groups_11_SUITE,testcase_2,ok}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(all_plus_one_tc_no_merge_tests) ->
+
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {?eh,stop_logging,[]}
+ ];
+
+test_events(all_plus_one_tc) ->
+
+ [{?eh,start_logging,{'DEF','RUNDIR'}},
+ {?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+ {?eh,tc_done,{groups_12_SUITE,end_per_suite,'_'}},
+ {negative,{?eh,tc_start,{groups_12_SUITE,init_per_suite}},
+ {?eh,stop_logging,[]}}
+ ];
+
+test_events(_) ->
+ [
+ ].
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
new file mode 100644
index 0000000000..4f11d8a0e8
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_11_SUITE.erl
@@ -0,0 +1,281 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(groups_11_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1a, [testcase_1a,testcase_1b]},
+
+ {test_group_1b, [], [testcase_1a,testcase_1b]},
+
+ {test_group_2, [], [testcase_2a,
+
+ {test_group_3, [], [testcase_3a,
+ testcase_3b]},
+ testcase_2b]},
+
+ {test_group_4, [{test_group_5, [], [testcase_5a,
+
+ {group, test_group_6},
+
+ testcase_5b]}]},
+ {test_group_6, [{group, test_group_7}]},
+
+ {test_group_7, [testcase_7a,testcase_7b]}
+ ].
+
+all() ->
+ [testcase_1,
+ {group, test_group_1a},
+ {group, test_group_1b},
+ testcase_2,
+ {group, test_group_2},
+ testcase_3,
+ {group, test_group_4}].
+
+%% this func only for internal test purposes
+grs_and_tcs() ->
+ {[
+ test_group_1a, test_group_1b,
+ test_group_2, test_group_3,
+ test_group_4, test_group_5,
+ test_group_6, test_group_7
+ ],
+ [
+ testcase_1,
+ testcase_1a, testcase_1b,
+ testcase_2,
+ testcase_2a, testcase_2b,
+ testcase_3a, testcase_3b,
+ testcase_3,
+ testcase_5a, testcase_5b,
+ testcase_7a, testcase_7b
+ ]}.
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ [{suite,init}|Config].
+
+end_per_suite(Config) ->
+ init = ?config(suite,Config).
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ [{name,Group}] = ?config(tc_group_properties,Config),
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ ct:comment(Group),
+ init = ?config(suite,Config),
+ [{Group,Group} | Config];
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+end_per_group(Group, Config) ->
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ ct:comment(Group),
+ init = ?config(suite,Config),
+ Group = ?config(Group,Config),
+ ok;
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ [{TestCase,TestCase} | Config];
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+end_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ TestCase = ?config(TestCase,Config),
+ ok;
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1() ->
+ [].
+testcase_1(Config) ->
+ init = ?config(suite,Config),
+ testcase_1 = ?config(testcase_1,Config),
+ ok.
+
+testcase_1a() ->
+ [].
+testcase_1a(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ testcase_1a = ?config(testcase_1a,Config),
+ ok.
+testcase_1b() ->
+ [].
+testcase_1b(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1a,Config),
+ testcase_1b = ?config(testcase_1b,Config),
+ ok.
+
+testcase_2() ->
+ [].
+testcase_2(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_1a,Config),
+ undefined = ?config(test_group_1b,Config),
+ testcase_2 = ?config(testcase_2,Config),
+ ok.
+
+testcase_2a() ->
+ [].
+testcase_2a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ testcase_2a = ?config(testcase_2a,Config),
+ ok.
+testcase_2b() ->
+ [].
+testcase_2b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ undefined = ?config(testcase_2a,Config),
+ testcase_2b = ?config(testcase_2b,Config),
+ ok.
+
+testcase_3a() ->
+ [].
+testcase_3a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_2b,Config),
+ testcase_3a = ?config(testcase_3a,Config),
+ ok.
+testcase_3b() ->
+ [].
+testcase_3b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_3a,Config),
+ testcase_3b = ?config(testcase_3b,Config),
+ ok.
+
+testcase_3() ->
+ [].
+testcase_3(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_2,Config),
+ undefined = ?config(test_group_3,Config),
+ testcase_3 = ?config(testcase_3,Config),
+ ok.
+
+testcase_5a() ->
+ [].
+testcase_5a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_3,Config),
+ testcase_5a = ?config(testcase_5a,Config),
+ ok.
+testcase_5b() ->
+ [].
+testcase_5b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_5a,Config),
+ testcase_5b = ?config(testcase_5b,Config),
+ ok.
+
+testcase_7a() ->
+ [].
+testcase_7a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ testcase_7a = ?config(testcase_7a,Config),
+ ok.
+testcase_7b() ->
+ [].
+testcase_7b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ undefined = ?config(testcase_7a,Config),
+ testcase_7b = ?config(testcase_7b,Config),
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl
new file mode 100644
index 0000000000..69c06f9b83
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_1/groups_12_SUITE.erl
@@ -0,0 +1,344 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(groups_12_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]},
+
+ {test_group_1b, [parallel], [testcase_1a,testcase_1b]},
+
+ {test_group_2, [parallel], [testcase_2a,
+
+ {test_group_3, [{repeat,2}],
+ [testcase_3a, testcase_3b]},
+
+ testcase_2b]},
+
+ {test_group_4, [{test_group_5, [parallel], [testcase_5a,
+
+ {group, test_group_6},
+
+ testcase_5b]}]},
+
+ {test_group_6, [parallel], [{group, test_group_7},
+ {group, test_group_8}]},
+
+ {test_group_7, [sequence], [testcase_7a,testcase_7b]},
+
+ {test_group_8, [shuffle,sequence], [testcase_8a,testcase_8b]}
+ ].
+
+all() ->
+ [{group, test_group_1a},
+ {group, test_group_1b},
+ testcase_1,
+ testcase_2,
+ {group, test_group_2},
+ testcase_3,
+ {group, test_group_4}].
+
+%% this func only for internal test purposes
+grs_and_tcs() ->
+ {[
+ test_group_1a, test_group_1b,
+ test_group_2, test_group_3,
+ test_group_4, test_group_5,
+ test_group_6, test_group_7,
+ test_group_8
+ ],
+ [
+ testcase_1a, testcase_1b, testcase_1c,
+ testcase_1,
+ testcase_2,
+ testcase_2a, testcase_2b,
+ testcase_3a, testcase_3b,
+ testcase_3,
+ testcase_5a, testcase_5b,
+ testcase_7a, testcase_7b,
+ testcase_8a, testcase_8b
+ ]}.
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ [{suite,init}|Config].
+
+end_per_suite(Config) ->
+ init = ?config(suite,Config).
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ Cmt =
+ case {Group,?config(tc_group_properties,Config)} of
+ {test_group_1a,[{shuffle,S},{name,test_group_1a}]} ->
+ io_lib:format("shuffled, ~w", [S]);
+ {test_group_1b,[{name,test_group_1b},parallel]} -> "parallel";
+ {test_group_2,[{name,test_group_2},parallel]} -> "parallel";
+ {test_group_3,[{name,test_group_3},{repeat,2}]} -> "repeat 2";
+ {test_group_3,[{name,test_group_3}]} -> "repeat 1";
+ {test_group_4,[{name,test_group_4}]} -> ok;
+ {test_group_5,[{name,test_group_5},parallel]} -> "parallel";
+ {test_group_6,[{name,test_group_6},parallel]} -> "parallel";
+ {test_group_7,[{name,test_group_7},sequence]} -> "sequence";
+ {test_group_8,[{shuffle,_},{name,test_group_8},sequence]} ->
+ "shuffle & sequence"
+ end,
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ ct:comment(io_lib:format("~w, ~s", [Group,Cmt])),
+ [{Group,Group} | Config];
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+end_per_group(Group, Config) ->
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ Group = ?config(Group,Config),
+ ok;
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ [{TestCase,TestCase} | Config];
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+end_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ TestCase = ?config(TestCase,Config),
+ ok;
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1a() ->
+ [].
+testcase_1a(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ testcase_1a = ?config(testcase_1a,Config),
+ ok.
+testcase_1b() ->
+ [].
+testcase_1b(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1a,Config),
+ testcase_1b = ?config(testcase_1b,Config),
+ ok.
+
+testcase_1c() ->
+ [].
+testcase_1c(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1b,Config),
+ testcase_1c = ?config(testcase_1c,Config),
+ ok.
+
+testcase_1() ->
+ [].
+testcase_1(Config) ->
+ init = ?config(suite,Config),
+ testcase_1 = ?config(testcase_1,Config),
+ ok.
+
+testcase_2() ->
+ [].
+testcase_2(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_1a,Config),
+ undefined = ?config(test_group_1b,Config),
+ testcase_2 = ?config(testcase_2,Config),
+ ok.
+
+testcase_2a() ->
+ [].
+testcase_2a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ testcase_2a = ?config(testcase_2a,Config),
+ ok.
+testcase_2b() ->
+ [].
+testcase_2b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ undefined = ?config(testcase_2a,Config),
+ testcase_2b = ?config(testcase_2b,Config),
+ ok.
+
+testcase_3a() ->
+ [].
+testcase_3a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_2b,Config),
+ testcase_3a = ?config(testcase_3a,Config),
+ ok.
+testcase_3b() ->
+ [].
+testcase_3b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_3a,Config),
+ testcase_3b = ?config(testcase_3b,Config),
+ ok.
+
+testcase_3() ->
+ [].
+testcase_3(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_2,Config),
+ undefined = ?config(test_group_3,Config),
+ testcase_3 = ?config(testcase_3,Config),
+ ok.
+
+testcase_5a() ->
+ [].
+testcase_5a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_3,Config),
+ testcase_5a = ?config(testcase_5a,Config),
+ %% increase chance the done event will come
+ %% during execution of subgroup (could be
+ %% tricky to handle)
+ timer:sleep(3),
+ ok.
+testcase_5b() ->
+ [].
+testcase_5b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_5a,Config),
+ testcase_5b = ?config(testcase_5b,Config),
+ ok.
+
+testcase_7a() ->
+ [].
+testcase_7a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ testcase_7a = ?config(testcase_7a,Config),
+ ok.
+testcase_7b() ->
+ [].
+testcase_7b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ undefined = ?config(testcase_7a,Config),
+ testcase_7b = ?config(testcase_7b,Config),
+ ok.
+
+testcase_8a() ->
+ [].
+testcase_8a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_8 = ?config(test_group_8,Config),
+ testcase_8a = ?config(testcase_8a,Config),
+ ok.
+testcase_8b() ->
+ [].
+testcase_8b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_8 = ?config(test_group_8,Config),
+ undefined = ?config(testcase_8a,Config),
+ testcase_8b = ?config(testcase_8b,Config),
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl
new file mode 100644
index 0000000000..2533ac8e84
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_21_SUITE.erl
@@ -0,0 +1,281 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(groups_21_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1a, [testcase_1a,testcase_1b]},
+
+ {test_group_1b, [], [testcase_1a,testcase_1b]},
+
+ {test_group_2, [], [testcase_2a,
+
+ {test_group_3, [], [testcase_3a,
+ testcase_3b]},
+ testcase_2b]},
+
+ {test_group_4, [{test_group_5, [], [testcase_5a,
+
+ {group, test_group_6},
+
+ testcase_5b]}]},
+
+ {test_group_6, [{group, test_group_7}]},
+
+ {test_group_7, [testcase_7a,testcase_7b]}
+ ].
+
+all() ->
+ [testcase_1,
+ {group, test_group_1a},
+ {group, test_group_1b},
+ testcase_2,
+ {group, test_group_2},
+ testcase_3,
+ {group, test_group_4}].
+
+%% this func only for internal test purposes
+grs_and_tcs() ->
+ {[
+ test_group_1a, test_group_1b,
+ test_group_2, test_group_3,
+ test_group_4, test_group_5,
+ test_group_6, test_group_7
+ ],
+ [
+ testcase_1,
+ testcase_1a, testcase_1b,
+ testcase_2,
+ testcase_2a, testcase_2b,
+ testcase_3a, testcase_3b,
+ testcase_3,
+ testcase_5a, testcase_5b,
+ testcase_7a, testcase_7b
+ ]}.
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ [{suite,init}|Config].
+
+end_per_suite(Config) ->
+ init = ?config(suite,Config).
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ [{name,Group}] = ?config(tc_group_properties,Config),
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ ct:comment(io_lib:format("~w", [Group])),
+ init = ?config(suite,Config),
+ [{Group,Group} | Config];
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+end_per_group(Group, Config) ->
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ Group = ?config(Group,Config),
+ ok;
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ [{TestCase,TestCase} | Config];
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+end_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ TestCase = ?config(TestCase,Config),
+ ok;
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1() ->
+ [].
+testcase_1(Config) ->
+ init = ?config(suite,Config),
+ testcase_1 = ?config(testcase_1,Config),
+ ok.
+
+testcase_1a() ->
+ [].
+testcase_1a(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ testcase_1a = ?config(testcase_1a,Config),
+ ok.
+testcase_1b() ->
+ [].
+testcase_1b(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1a,Config),
+ testcase_1b = ?config(testcase_1b,Config),
+ ok.
+
+testcase_2() ->
+ [].
+testcase_2(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_1a,Config),
+ undefined = ?config(test_group_1b,Config),
+ testcase_2 = ?config(testcase_2,Config),
+ ok.
+
+testcase_2a() ->
+ [].
+testcase_2a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ testcase_2a = ?config(testcase_2a,Config),
+ ok.
+testcase_2b() ->
+ [].
+testcase_2b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ undefined = ?config(testcase_2a,Config),
+ testcase_2b = ?config(testcase_2b,Config),
+ ok.
+
+testcase_3a() ->
+ [].
+testcase_3a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_2b,Config),
+ testcase_3a = ?config(testcase_3a,Config),
+ ok.
+testcase_3b() ->
+ [].
+testcase_3b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_3a,Config),
+ testcase_3b = ?config(testcase_3b,Config),
+ ok.
+
+testcase_3() ->
+ [].
+testcase_3(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_2,Config),
+ undefined = ?config(test_group_3,Config),
+ testcase_3 = ?config(testcase_3,Config),
+ ok.
+
+testcase_5a() ->
+ [].
+testcase_5a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_3,Config),
+ testcase_5a = ?config(testcase_5a,Config),
+ ok.
+testcase_5b() ->
+ [].
+testcase_5b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_5a,Config),
+ testcase_5b = ?config(testcase_5b,Config),
+ ok.
+
+testcase_7a() ->
+ [].
+testcase_7a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ testcase_7a = ?config(testcase_7a,Config),
+ ok.
+testcase_7b() ->
+ [].
+testcase_7b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ undefined = ?config(testcase_7a,Config),
+ testcase_7b = ?config(testcase_7b,Config),
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl
new file mode 100644
index 0000000000..cd517876df
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE_data/groups_2/groups_22_SUITE.erl
@@ -0,0 +1,314 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(groups_22_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%====================================================================
+%% COMMON TEST CALLBACK FUNCTIONS
+%%====================================================================
+
+suite() ->
+ [{timetrap,{minutes,1}}].
+
+groups() ->
+ [
+ {test_group_1a, [shuffle], [testcase_1a,testcase_1b,testcase_1c]},
+
+ {test_group_1b, [parallel], [testcase_1a,testcase_1b]},
+
+ {test_group_2, [parallel], [testcase_2a,
+
+ {test_group_3, [{repeat,1}],
+ [testcase_3a, testcase_3b]},
+
+ testcase_2b]},
+
+ {test_group_4, [{test_group_5, [parallel], [testcase_5a,
+
+ {group, test_group_6},
+
+ testcase_5b]}]},
+
+ {test_group_6, [parallel], [{group, test_group_7}]},
+
+ {test_group_7, [sequence], [testcase_7a,testcase_7b]}
+ ].
+
+all() ->
+ [{group, test_group_1a},
+ {group, test_group_1b},
+ testcase_1,
+ testcase_2,
+ {group, test_group_2},
+ testcase_3,
+ {group, test_group_4}].
+
+%% this func only for internal test purposes
+grs_and_tcs() ->
+ {[
+ test_group_1a, test_group_1b,
+ test_group_2, test_group_3,
+ test_group_4, test_group_5,
+ test_group_6, test_group_7
+ ],
+ [
+ testcase_1a, testcase_1b, testcase_1c,
+ testcase_1,
+ testcase_2,
+ testcase_2a, testcase_2b,
+ testcase_3a, testcase_3b,
+ testcase_3,
+ testcase_5a, testcase_5b,
+ testcase_7a, testcase_7b
+ ]}.
+
+%%--------------------------------------------------------------------
+%% Suite Configuration
+%%--------------------------------------------------------------------
+
+init_per_suite(Config) ->
+ [{suite,init}|Config].
+
+end_per_suite(Config) ->
+ init = ?config(suite,Config).
+
+%%--------------------------------------------------------------------
+%% Group Configuration
+%%--------------------------------------------------------------------
+
+init_per_group(Group, Config) ->
+ Cmt =
+ case {Group,?config(tc_group_properties,Config)} of
+ {test_group_1a,[{shuffle,S},{name,test_group_1a}]} ->
+ io_lib:format("shuffled, ~w", [S]);
+ {test_group_1b,[{name,test_group_1b},parallel]} -> "parallel";
+ {test_group_2,[{name,test_group_2},parallel]} -> "parallel";
+ {test_group_3,[{name,test_group_3},{repeat,1}]} -> "repeat 1";
+ {test_group_3,[{name,test_group_3}]} -> "repeat 0";
+ {test_group_4,[{name,test_group_4}]} -> ok;
+ {test_group_5,[{name,test_group_5},parallel]} -> "parallel";
+ {test_group_6,[{name,test_group_6},parallel]} -> "parallel";
+ {test_group_7,[{name,test_group_7},sequence]} -> "sequence"
+ end,
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ ct:comment(io_lib:format("~w, ~s", [Group,Cmt])),
+ [{Group,Group} | Config];
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+end_per_group(Group, Config) ->
+ {Grs,_} = grs_and_tcs(),
+ case lists:member(Group, Grs) of
+ true ->
+ init = ?config(suite,Config),
+ Group = ?config(Group,Config),
+ ok;
+ false ->
+ ct:fail({bad_group,Group})
+ end.
+
+%%--------------------------------------------------------------------
+%% Testcase Configuration
+%%--------------------------------------------------------------------
+
+init_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ [{TestCase,TestCase} | Config];
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+end_per_testcase(TestCase, Config) ->
+ {_,TCs} = grs_and_tcs(),
+ case lists:member(TestCase, TCs) of
+ true ->
+ init = ?config(suite,Config),
+ TestCase = ?config(TestCase,Config),
+ ok;
+ false ->
+ ct:fail({unknown_testcase,TestCase})
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Testcases
+%%--------------------------------------------------------------------
+
+testcase_1a() ->
+ [].
+testcase_1a(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ testcase_1a = ?config(testcase_1a,Config),
+ ok.
+testcase_1b() ->
+ [].
+testcase_1b(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1a,Config),
+ testcase_1b = ?config(testcase_1b,Config),
+ ok.
+
+testcase_1c() ->
+ [].
+testcase_1c(Config) ->
+ init = ?config(suite,Config),
+ case ?config(test_group_1a,Config) of
+ test_group_1a -> ok;
+ _ ->
+ case ?config(test_group_1b,Config) of
+ test_group_1b -> ok;
+ _ -> ct:fail(no_group_data)
+ end
+ end,
+ undefined = ?config(testcase_1b,Config),
+ testcase_1c = ?config(testcase_1c,Config),
+ ok.
+
+testcase_1() ->
+ [].
+testcase_1(Config) ->
+ init = ?config(suite,Config),
+ testcase_1 = ?config(testcase_1,Config),
+ ok.
+
+testcase_2() ->
+ [].
+testcase_2(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_1a,Config),
+ undefined = ?config(test_group_1b,Config),
+ testcase_2 = ?config(testcase_2,Config),
+ ok.
+
+testcase_2a() ->
+ [].
+testcase_2a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ testcase_2a = ?config(testcase_2a,Config),
+ ok.
+testcase_2b() ->
+ [].
+testcase_2b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ undefined = ?config(testcase_2a,Config),
+ testcase_2b = ?config(testcase_2b,Config),
+ ok.
+
+testcase_3a() ->
+ [].
+testcase_3a(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_2b,Config),
+ testcase_3a = ?config(testcase_3a,Config),
+ ok.
+testcase_3b() ->
+ [].
+testcase_3b(Config) ->
+ init = ?config(suite,Config),
+ test_group_2 = ?config(test_group_2,Config),
+ test_group_3 = ?config(test_group_3,Config),
+ undefined = ?config(testcase_3a,Config),
+ testcase_3b = ?config(testcase_3b,Config),
+ ok.
+
+testcase_3() ->
+ [].
+testcase_3(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_2,Config),
+ undefined = ?config(test_group_3,Config),
+ testcase_3 = ?config(testcase_3,Config),
+ ok.
+
+testcase_5a() ->
+ [].
+testcase_5a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_3,Config),
+ testcase_5a = ?config(testcase_5a,Config),
+ %% increase chance the done event will come
+ %% during execution of subgroup (could be
+ %% tricky to handle)
+ timer:sleep(3),
+ ok.
+testcase_5b() ->
+ [].
+testcase_5b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ undefined = ?config(testcase_5a,Config),
+ testcase_5b = ?config(testcase_5b,Config),
+ ok.
+
+testcase_7a() ->
+ [].
+testcase_7a(Config) ->
+ init = ?config(suite,Config),
+ undefined = ?config(test_group_3,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ testcase_7a = ?config(testcase_7a,Config),
+ ok.
+testcase_7b() ->
+ [].
+testcase_7b(Config) ->
+ init = ?config(suite,Config),
+ test_group_4 = ?config(test_group_4,Config),
+ test_group_5 = ?config(test_group_5,Config),
+ test_group_6 = ?config(test_group_6,Config),
+ test_group_7 = ?config(test_group_7,Config),
+ undefined = ?config(testcase_7a,Config),
+ testcase_7b = ?config(testcase_7b,Config),
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl
new file mode 100644
index 0000000000..b789851134
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_1_SUITE.erl
@@ -0,0 +1,146 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(simple_1_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% COMMON TEST CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%%
+%% Info = [tuple()]
+%% List of key/value pairs.
+%%
+%% Description: Returns list of tuples to set default properties
+%% for the suite.
+%%
+%% Note: The suite/0 function is only meant to be used to return
+%% default data values, not perform any other operations.
+%%--------------------------------------------------------------------
+suite() ->
+ [
+ {timetrap,{seconds,10}}
+ ].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the suite.
+%%
+%% Description: Initialization before the suite.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ [{ips,ips_data} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after the suite.
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ ips_data = ?config(ips, Config).
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Initialization before each test case.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(TestCase, Config) ->
+ [{TestCase,{TestCase,data}} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is finished.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after each test case.
+%%--------------------------------------------------------------------
+end_per_testcase(TestCase, Config) ->
+ {TestCase,data} = ?config(TestCase, Config).
+
+%%--------------------------------------------------------------------
+%% Function: all() -> TestCases | {skip,Reason}
+%%
+%% TestCases = [TestCase | {sequence,SeqName}]
+%% TestCase = atom()
+%% Name of a test case.
+%% SeqName = atom()
+%% Name of a test case sequence.
+%% Reason = term()
+%% The reason for skipping all test cases.
+%%
+%% Description: Returns the list of test cases that are to be executed.
+%%--------------------------------------------------------------------
+all() ->
+ [tc1,
+ tc2].
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+tc1() ->
+ [{userdata,{info, "This is a testcase"}}].
+
+tc1(Config) ->
+ ips_data = ?config(ips, Config),
+ {tc1,data} = ?config(tc1, Config),
+ ok.
+
+tc2() ->
+ [{timetrap,5000}].
+
+tc2(Config) ->
+ ips_data = ?config(ips, Config),
+ undefined = ?config(tc1, Config),
+ {tc2,data} = ?config(tc2, Config),
+ ok.
diff --git a/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl
new file mode 100644
index 0000000000..eb7e9cdf7b
--- /dev/null
+++ b/lib/common_test/test/ct_testspec_1_SUITE_data/suites_1/simple_2_SUITE.erl
@@ -0,0 +1,146 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(simple_2_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+%%--------------------------------------------------------------------
+%% COMMON TEST CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% Function: suite() -> Info
+%%
+%% Info = [tuple()]
+%% List of key/value pairs.
+%%
+%% Description: Returns list of tuples to set default properties
+%% for the suite.
+%%
+%% Note: The suite/0 function is only meant to be used to return
+%% default data values, not perform any other operations.
+%%--------------------------------------------------------------------
+suite() ->
+ [
+ {timetrap,{seconds,10}}
+ ].
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the suite.
+%%
+%% Description: Initialization before the suite.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ [{ips,ips_data} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config0) -> void() | {save_config,Config1}
+%%
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after the suite.
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ ips_data = ?config(ips, Config).
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is about to run.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Reason = term()
+%% The reason for skipping the test case.
+%%
+%% Description: Initialization before each test case.
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(TestCase, Config) ->
+ [{TestCase,{TestCase,data}} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1}
+%%
+%% TestCase = atom()
+%% Name of the test case that is finished.
+%% Config0 = Config1 = [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Cleanup after each test case.
+%%--------------------------------------------------------------------
+end_per_testcase(TestCase, Config) ->
+ {TestCase,data} = ?config(TestCase, Config).
+
+%%--------------------------------------------------------------------
+%% Function: all() -> TestCases | {skip,Reason}
+%%
+%% TestCases = [TestCase | {sequence,SeqName}]
+%% TestCase = atom()
+%% Name of a test case.
+%% SeqName = atom()
+%% Name of a test case sequence.
+%% Reason = term()
+%% The reason for skipping all test cases.
+%%
+%% Description: Returns the list of test cases that are to be executed.
+%%--------------------------------------------------------------------
+all() ->
+ [tc1,
+ tc2].
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+
+tc1() ->
+ [{userdata,{info, "This is a testcase"}}].
+
+tc1(Config) ->
+ ips_data = ?config(ips, Config),
+ {tc1,data} = ?config(tc1, Config),
+ ok.
+
+tc2() ->
+ [{timetrap,5000}].
+
+tc2(Config) ->
+ ips_data = ?config(ips, Config),
+ undefined = ?config(tc1, Config),
+ {tc2,data} = ?config(tc2, Config),
+ ok.
diff --git a/lib/common_test/test/ct_userconfig_callback.erl b/lib/common_test/test/ct_userconfig_callback.erl
new file mode 100644
index 0000000000..ca51bf240b
--- /dev/null
+++ b/lib/common_test/test/ct_userconfig_callback.erl
@@ -0,0 +1,32 @@
+%%--------------------------------------------------------------------
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+-module(ct_userconfig_callback).
+
+-export([check_parameter/1, read_config/1]).
+
+read_config(Str) ->
+ KeyVals = string:tokens(Str, " "),
+ {ok,read_config1(KeyVals)}.
+
+read_config1([Key,Val | KeyVals]) ->
+ [{list_to_atom(Key),Val} | read_config1(KeyVals)];
+read_config1([]) ->
+ [].
+
+check_parameter(Str) ->
+ {ok,{config,Str}}.
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index cdb8e1f71c..8a4853e070 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1,3 +1,3 @@
-COMMON_TEST_VSN = 1.4.8
+COMMON_TEST_VSN = 1.5.3
diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml
index bbd3f1043d..f2af932aef 100644
--- a/lib/compiler/doc/src/compile.xml
+++ b/lib/compiler/doc/src/compile.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2010</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -164,6 +164,70 @@
for details.</p>
</item>
+ <tag><c>makedep</c></tag>
+ <item>
+ <p>Produce a Makefile rule to track headers dependencies.
+ No object file is produced.
+ </p>
+ <p>By default, this rule is written to
+ <c><![CDATA[<File>.Pbeam]]></c>. However, if the option
+ <c>binary</c> is set, nothing is written and the rule is
+ returned in <c>Binary</c>.
+ </p>
+ <p>For instance, if one has the following module:
+ </p>
+ <code>
+-module(module).
+
+-include_lib("eunit/include/eunit.hrl").
+-include("header.hrl").
+ </code>
+ <p>Here is the Makefile rule generated by this option:
+ </p>
+ <code>
+module.beam: module.erl \
+ /usr/local/lib/erlang/lib/eunit/include/eunit.hrl \
+ header.hrl
+ </code>
+ </item>
+
+ <tag><c>{makedep_output, Output}</c></tag>
+ <item>
+ <p>Write generated rule(s) to <c>Output</c> instead of the
+ default <c><![CDATA[<File>.Pbeam]]></c>. <c>Output</c>
+ can be a filename or an <c>io_device()</c>. To write to
+ stdout, use <c>standard_io</c>. However if <c>binary</c>
+ is set, nothing is written to <c>Output</c> and the
+ result is returned to the caller with
+ <c>{ok, ModuleName, Binary}</c>.
+ </p>
+ </item>
+
+ <tag><c>{makedep_target, Target}</c></tag>
+ <item>
+ <p>Change the name of the rule emitted to <c>Target</c>.
+ </p>
+ </item>
+
+ <tag><c>makedep_quote_target</c></tag>
+ <item>
+ <p>Characters in <c>Target</c> special to make(1) are quoted.
+ </p>
+ </item>
+
+ <tag><c>makedep_add_missing</c></tag>
+ <item>
+ <p>Consider missing headers as generated files and add them to the
+ dependencies.
+ </p>
+ </item>
+
+ <tag><c>makedep_phony</c></tag>
+ <item>
+ <p>Add a phony target for each dependency.
+ </p>
+ </item>
+
<tag><c>'P'</c></tag>
<item>
<p>Produces a listing of the parsed code after preprocessing
@@ -310,6 +374,27 @@
(there will not even be a warning if there is a mismatch).</p>
</item>
+ <tag><c>{no_auto_import,[{F,A}, ...]}</c></tag>
+ <item>
+ <p>Makes the function <c>F/A</c> no longer being
+ auto-imported from the module <c>erlang</c>, which resolves
+ BIF name clashes. This option has to be used to resolve name
+ clashes with BIFs auto-imported before R14A, if one wants to
+ call the local function with the same name as an
+ auto-imported BIF without module prefix.</p>
+ <note>
+ <p>From R14A and forward, the compiler resolves calls
+ without module prefix to local or imported functions before
+ trying auto-imported BIFs. If the BIF is to be
+ called, use the <c>erlang</c> module prefix in the call, not
+ <c>{ no_auto_import,[{F,A}, ...]}</c></p>
+ </note>
+ <p>If this option is written in the source code, as a
+ <c>-compile</c> directive, the syntax <c>F/A</c> can be used instead
+ of <c>{F,A}</c>. Example:</p>
+ <code>-compile({no_auto_import,[error/1]}).</code>
+ </item>
+
</taglist>
<p>If warnings are turned on (the <c>report_warnings</c> option
@@ -338,31 +423,35 @@
<tag><c>nowarn_bif_clash</c></tag>
<item>
- <p>By default, there will be a compilation error if a
- module contains an exported function with the same name
- as an auto-imported BIF (such as <c>size/1</c>) AND
- there is a call to it without a qualifying module name.
- The reason is that the BIF will be called, not
- the function in the same module. The recommended way to
- eliminate that warning is to use a call with a module
- name - either <c>erlang</c> to call the BIF or
- <c>?MODULE</c> to call the function in the same module.
- The warning can also be turned off using this option,
- but that is not recommended.</p>
+ <p>This option is removed, it will generate a fatal error if used.</p>
- <p><em>The use of this option is strongly discouraged,
- as code that uses it will probably break in a future
- major release (R14 or R15).</em></p>
+ <warning>
+ <p>Beginning with R14A, the compiler no longer calls the
+ auto-imported BIF if the name clashes with a local or
+ explicitly imported function and a call without explicit
+ module name is issued. Instead the local or imported
+ function is called. Still accepting <c>nowarn_bif_clash</c> would makes a
+ module calling functions clashing with autoimported BIFs
+ compile with both the old and new compilers, but with
+ completely different semantics, why the option was removed.</p>
+
+ <p>The use of this option has always been strongly discouraged.
+ From OTP R14A and forward it's an error to use it.</p>
+ <p>To resolve BIF clashes, use explicit module names or the
+ <c>{no_auto_import,[F/A]}</c> compiler directive.</p>
+ </warning>
</item>
<tag><c>{nowarn_bif_clash, FAs}</c></tag>
<item>
- <p>Turns off warnings as <c>nowarn_bif_clash</c> but only
- for the mentioned local functions. <c>FAs</c> is a tuple
- <c>{Name,Arity}</c> or a list of such tuples.</p>
- <p><em>The use of this option is strongly discouraged,
- as code that uses it will probably break in a future
- major release (R14 or R15).</em></p>
+ <p>This option is removed, it will generate a fatal error if used.</p>
+
+ <warning>
+ <p>The use of this option has always been strongly discouraged.
+ From OTP R14A and forward it's an error to use it.</p>
+ <p>To resolve BIF clashes, use explicit module names or the
+ <c>{no_auto_import,[F/A]}</c> compiler directive.</p>
+ </warning>
</item>
<tag><c>warn_export_all</c></tag>
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index 7ea000a895..25a6db4ce0 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -31,6 +31,210 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 4.7.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>-export_type()</c> directive is no longer included
+ among the attributes.</p>
+ <p>
+ Own Id: OTP-8998</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The maximum number of allowed arguments for an Erlang
+ function has been lowered from 256 to 255, so that the
+ number of arguments can now fit in a byte.</p>
+ <p>
+ Own Id: OTP-9049</p>
+ </item>
+ <item>
+ <p>
+ Dependency generation for Makefiles has been added to the
+ compiler and erlc. See the manual pages for
+ <c>compile</c> and <c>erlc</c>. (Thanks to Jean-Sebastien
+ Pedron.)</p>
+ <p>
+ Own Id: OTP-9065</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 4.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Two compiler bugs (that would cause the compiler to
+ terminate) reported by Christopher Williams have been
+ fixed.</p>
+ <p>
+ Own Id: OTP-8949</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>The compiler would translate binary comprehensions
+ containing tail segments in a way that would would
+ confuse Dialyzer. For instance:</p>
+ <p><c>[42 || &lt;&lt;_:8/integer, _/bits&gt;&gt; &lt;=
+ Bits]</c></p>
+ <p>
+ would produce a Dialyzer warning.</p>
+ <p>
+ Own Id: OTP-8864</p>
+ </item>
+ <item>
+ <p>
+ Code such as <c>foo(A) -&gt; &lt;&lt;A:0&gt;&gt;</c>
+ would crash the compiler.</p>
+ <p>
+ Own Id: OTP-8865</p>
+ </item>
+ <item>
+ <p>
+ The compiler could fail with an internal error when
+ variables were exported from a receive block but the
+ return value of the receive block were not used. (Thanks
+ to Jim Engquist for reporting this error.)</p>
+ <p>
+ Own Id: OTP-8888</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 4.7.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Eliminated warnings for auto-imported BIF clashes.</p>
+ <p>
+ Own Id: OTP-8840</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 4.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Several problems in the inliner have been fixed.</p>
+ <p>
+ Own Id: OTP-8552</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The module binary from EEP31 (and EEP9) is implemented.</p>
+ <p>
+ Own Id: OTP-8217</p>
+ </item>
+ <item>
+ <p>Local and imported functions now override the
+ auto-imported BIFs when the names clash. The pre R14
+ behaviour was that auto-imported BIFs would override
+ local functions. To avoid that old programs change
+ behaviour, the following will generate an error:</p>
+ <list><item><p>Doing a call without explicit module name
+ to a local function having a name clashing with the name
+ of an auto-imported BIF that was present (and
+ auto-imported) before OTP R14A</p></item>
+ <item><p>Explicitly importing a function having a name
+ clashing with the name of an autoimported BIF that was
+ present (and autoimported) before OTP R14A</p></item>
+ <item><p>Using any form of the old compiler directive
+ <c>nowarn_bif_clash</c></p></item> </list> <p>If the BIF
+ was added or auto-imported in OTP R14A or later,
+ overriding it with an import or a local function will
+ only result in a warning,</p> <p>To resolve clashes, you
+ can either use the explicit module name <c>erlang</c> to
+ call the BIF, or you can remove the auto-import of that
+ specific BIF by using the new compiler directive
+ <c>-compile({no_auto_import,[F/A]}).</c>, which makes all
+ calls to the local or imported function without explicit
+ module name pass without warnings or errors.</p> <p>The
+ change makes it possible to add auto-imported BIFs
+ without breaking or silently changing old code in the
+ future. However some current code ingeniously utilizing
+ the old behaviour or the <c>nowarn_bif_clash</c> compiler
+ directive, might need changing to be accepted by the
+ compiler.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8579</p>
+ </item>
+ <item>
+ <p>The undocumented, unsupport, and deprecated function
+ <c>lists:flat_length/1</c> has been removed.</p>
+ <p>
+ Own Id: OTP-8584</p>
+ </item>
+ <item>
+ <p>Nested records can now be accessed without
+ parenthesis. See the Reference Manual for examples.
+ (Thanks to YAMASHINA Hio and Tuncer Ayaz.)</p>
+ <p>
+ Own Id: OTP-8597</p>
+ </item>
+ <item>
+ <p>It is now possible to suppress the warning in code
+ such as "<c>list_to_integer(S), ok</c>" by assigning the
+ ignored value "_" like this: "<c>_ = list_to_integer(S),
+ ok</c>".</p>
+ <p>
+ Own Id: OTP-8602</p>
+ </item>
+ <item>
+ <p><c>receive</c> statements that can only read out a
+ newly created reference are now specially optimized so
+ that it will execute in constant time regardless of the
+ number of messages in the receive queue for the process.
+ That optimization will benefit calls to
+ <c>gen_server:call()</c>. (See <c>gen:do_call/4</c> for
+ an example of a receive statement that will be
+ optimized.)</p>
+ <p>
+ Own Id: OTP-8623</p>
+ </item>
+ <item>
+ <p>The compiler optimizes record operations better.</p>
+ <p>
+ Own Id: OTP-8668</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 4.6.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/doc/src/part_notes_history.xml b/lib/compiler/doc/src/part_notes_history.xml
index cd17c4285e..12366f0006 100644
--- a/lib/compiler/doc/src/part_notes_history.xml
+++ b/lib/compiler/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile
index 0f6d2f6193..1238d113e1 100644
--- a/lib/compiler/src/Makefile
+++ b/lib/compiler/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -118,7 +118,9 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
-ERL_COMPILE_FLAGS += +inline +warn_unused_import -I../../stdlib/include -I$(EGEN) -W
+ERL_COMPILE_FLAGS += +inline +warn_unused_import \
+ +warnings_as_errors \
+ -I../../stdlib/include -I$(EGEN) -W
# ----------------------------------------------------
# Targets
diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl
index 115c228b0a..89d64834cf 100644
--- a/lib/compiler/src/beam_asm.erl
+++ b/lib/compiler/src/beam_asm.erl
@@ -261,7 +261,8 @@ make_op({gc_bif,Bif,Fail,Live,Args,Dest}, Dict) ->
Arity = length(Args),
BifOp = case Arity of
1 -> gc_bif1;
- 2 -> gc_bif2
+ 2 -> gc_bif2;
+ 3 -> gc_bif3
end,
encode_op(BifOp, [Fail,Live,{extfunc,erlang,Bif,Arity}|Args++[Dest]],Dict);
make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) ->
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 32703b4dd1..c45874597a 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -36,12 +36,13 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) ->
%% Collect basic blocks and optimize them.
Is2 = blockify(Is1),
- Is3 = beam_utils:live_opt(Is2),
- Is4 = opt_blocks(Is3),
- Is5 = beam_utils:delete_live_annos(Is4),
+ Is3 = move_allocates(Is2),
+ Is4 = beam_utils:live_opt(Is3),
+ Is5 = opt_blocks(Is4),
+ Is6 = beam_utils:delete_live_annos(Is5),
%% Optimize bit syntax.
- {Is,Lc} = bsm_opt(Is5, Lc0),
+ {Is,Lc} = bsm_opt(Is6, Lc0),
%% Done.
{{function,Name,Arity,CLabel,Is},Lc}
@@ -156,11 +157,7 @@ opt_blocks([I|Is]) ->
opt_blocks([]) -> [].
opt_block(Is0) ->
- %% We explicitly move any allocate instruction upwards before optimising
- %% moves, to avoid any potential problems with the calculation of live
- %% registers.
- Is1 = move_allocates(Is0),
- Is = find_fixpoint(fun opt/1, Is1),
+ Is = find_fixpoint(fun opt/1, Is0),
opt_alloc(Is).
find_fixpoint(OptFun, Is0) ->
@@ -170,11 +167,21 @@ find_fixpoint(OptFun, Is0) ->
end.
%% move_allocates(Is0) -> Is
-%% Move allocates upwards in the instruction stream, in the hope of
-%% getting more possibilities for optimizing away moves later.
-
-move_allocates(Is) ->
- move_allocates_1(reverse(Is), []).
+%% Move allocate instructions upwards in the instruction stream, in the
+%% hope of getting more possibilities for optimizing away moves later.
+%%
+%% NOTE: Moving allocation instructions is only safe because it is done
+%% immediately after code generation so that we KNOW that if {x,X} is
+%% initialized, all x registers with lower numbers are also initialized.
+%% That assumption may not be true after other optimizations, such as
+%% the beam_utils:live_opt/1 optimization.
+
+move_allocates([{block,Bl0}|Is]) ->
+ Bl = move_allocates_1(reverse(Bl0), []),
+ [{block,Bl}|move_allocates(Is)];
+move_allocates([I|Is]) ->
+ [I|move_allocates(Is)];
+move_allocates([]) -> [].
move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) ->
{Is,Acc} = move_allocates_2(Alloc, Is0, Acc0),
@@ -201,7 +208,6 @@ move_allocates_2(Alloc, [], Acc) ->
alloc_may_pass({set,_,_,{alloc,_,_}}) -> false;
alloc_may_pass({set,_,_,{set_tuple_element,_}}) -> false;
alloc_may_pass({set,_,_,put_list}) -> false;
-alloc_may_pass({set,_,_,{put_tuple,_}}) -> false;
alloc_may_pass({set,_,_,put}) -> false;
alloc_may_pass({set,_,_,_}) -> true.
diff --git a/lib/compiler/src/beam_bool.erl b/lib/compiler/src/beam_bool.erl
index dcc6ad4c7c..d9ea6f5a70 100644
--- a/lib/compiler/src/beam_bool.erl
+++ b/lib/compiler/src/beam_bool.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Purpose: Optimizes booleans in guards.
@@ -631,10 +631,10 @@ fetch_reg(V, [{I,V}|_]) -> {x,I};
fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
live_regs(Regs) ->
- foldl(fun ({I,_}, _) -> I;
- ([], Max) -> Max end,
- -1, Regs)+1.
-
+ foldl(fun ({I,_}, _) ->
+ I
+ end, -1, Regs)+1.
+
%%%
%%% Convert a block to Static Single Assignment (SSA) form.
@@ -748,8 +748,7 @@ initialized_regs([{bs_context_to_binary,Src}|Is], Regs) ->
initialized_regs([{label,_},{func_info,_,_,Arity}|_], Regs) ->
InitRegs = free_vars_regs(Arity),
add_init_regs(InitRegs, Regs);
-initialized_regs([_|_], Regs) -> Regs;
-initialized_regs([], Regs) -> Regs.
+initialized_regs([_|_], Regs) -> Regs.
add_init_regs([{x,_}=X|T], Regs) ->
add_init_regs(T, ordsets:add_element(X, Regs));
diff --git a/lib/compiler/src/beam_dead.erl b/lib/compiler/src/beam_dead.erl
index 7b4cd814a2..bb93110176 100644
--- a/lib/compiler/src/beam_dead.erl
+++ b/lib/compiler/src/beam_dead.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -281,12 +281,12 @@ forward([{test,is_eq_exact,_,[Dst,Src]}=I,{move,Src,Dst}|Is], D, Lc, Acc) ->
forward([I|Is], D, Lc, Acc);
forward([{test,is_nil,_,[Dst]}=I,{move,nil,Dst}|Is], D, Lc, Acc) ->
forward([I|Is], D, Lc, Acc);
-forward([{test,is_eq_exact,_,[_,{atom,_}]}=I|Is], D, Lc, [{label,_}|_]=Acc) ->
+forward([{test,is_eq_exact,_,_}=I|Is], D, Lc, Acc) ->
case Is of
[{label,_}|_] -> forward(Is, D, Lc, [I|Acc]);
_ -> forward(Is, D, Lc+1, [{label,Lc},I|Acc])
end;
-forward([{test,is_ne_exact,_,[_,{atom,_}]}=I|Is], D, Lc, [{label,_}|_]=Acc) ->
+forward([{test,is_ne_exact,_,_}=I|Is], D, Lc, Acc) ->
case Is of
[{label,_}|_] -> forward(Is, D, Lc, [I|Acc]);
_ -> forward(Is, D, Lc+1, [{label,Lc},I|Acc])
@@ -371,10 +371,10 @@ backward([{test,bs_start_match2,{f,To0},Live,[Src|_]=Info,Dst}|Is], D, Acc) ->
To = shortcut_bs_start_match(To0, Src, D),
I = {test,bs_start_match2,{f,To},Live,Info,Dst},
backward(Is, D, [I|Acc]);
-backward([{test,is_eq_exact=Op,{f,To0},[Reg,{atom,Val}]=Ops}|Is], D, Acc) ->
+backward([{test,is_eq_exact,{f,To0},[Reg,{atom,Val}]=Ops}|Is], D, Acc) ->
To1 = shortcut_bs_test(To0, Is, D),
To = shortcut_fail_label(To1, Reg, Val, D),
- I = {test,Op,{f,To},Ops},
+ I = combine_eqs(To, Ops, D, Acc),
backward(Is, D, [I|Acc]);
backward([{test,Op,{f,To0},Ops0}|Is], D, Acc) ->
To1 = shortcut_bs_test(To0, Is, D),
@@ -394,7 +394,10 @@ backward([{test,Op,{f,To0},Ops0}|Is], D, Acc) ->
_Code ->
To2
end,
- I = {test,Op,{f,To},Ops0},
+ I = case Op of
+ is_eq_exact -> combine_eqs(To, Ops0, D, Acc);
+ _ -> {test,Op,{f,To},Ops0}
+ end,
backward(Is, D, [I|Acc]);
backward([{test,Op,{f,To0},Live,Ops0,Dst}|Is], D, Acc) ->
To1 = shortcut_bs_test(To0, Is, D),
@@ -519,6 +522,41 @@ bif_to_test(Name, Args, Fail) ->
not_possible() -> throw(not_possible).
+%% combine_eqs(To, Operands, Acc) -> Instruction.
+%% Combine two is_eq_exact instructions or (an is_eq_exact
+%% instruction and a select_val instruction) to a select_val
+%% instruction if possible.
+%%
+%% Example:
+%%
+%% is_eq_exact F1 Reg Lit1 select_val Reg F2 [ Lit1 L1
+%% L1: . Lit2 L2 ]
+%% .
+%% . ==>
+%% .
+%% F1: is_eq_exact F2 Reg Lit2 F1: is_eq_exact F2 Reg Lit2
+%% L2: .... L2:
+%%
+combine_eqs(To, [Reg,{Type,_}=Lit1]=Ops, D, [{label,L1}|_])
+ when Type =:= atom; Type =:= integer ->
+ case beam_utils:code_at(To, D) of
+ [{test,is_eq_exact,{f,F2},[Reg,{Type,_}=Lit2]},
+ {label,L2}|_] when Lit1 =/= Lit2 ->
+ {select_val,Reg,{f,F2},{list,[Lit1,{f,L1},Lit2,{f,L2}]}};
+ [{select_val,Reg,{f,F2},{list,[{Type,_}|_]=List0}}|_] ->
+ List = remove_from_list(Lit1, List0),
+ {select_val,Reg,{f,F2},{list,[Lit1,{f,L1}|List]}};
+ _Is ->
+ {test,is_eq_exact,{f,To},Ops}
+ end;
+combine_eqs(To, Ops, _D, _Acc) ->
+ {test,is_eq_exact,{f,To},Ops}.
+
+remove_from_list(Lit, [Lit,{f,_}|T]) ->
+ T;
+remove_from_list(Lit, [Val,{f,_}=Fail|T]) ->
+ [Val,Fail|remove_from_list(Lit, T)];
+remove_from_list(_, []) -> [].
%% shortcut_bs_test(TargetLabel, [Instruction], D) -> TargetLabel'
%% Try to shortcut the failure label for a bit syntax matching.
diff --git a/lib/compiler/src/beam_dict.erl b/lib/compiler/src/beam_dict.erl
index 4ffe8bc606..c50ed28aa9 100644
--- a/lib/compiler/src/beam_dict.erl
+++ b/lib/compiler/src/beam_dict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,10 +33,9 @@
exports = [] :: [{label(), arity(), label()}],
locals = [] :: [{label(), arity(), label()}],
imports = gb_trees:empty() :: gb_tree(), %{{M,F,A},Index}
- strings = [] :: [string()], %String pool
+ strings = <<>> :: binary(), %String pool
lambdas = [], %[{...}]
literals = dict:new() :: dict(), %Format: {Literal,Number}
- next_atom = 1 :: pos_integer(),
next_import = 0 :: non_neg_integer(),
string_offset = 0 :: non_neg_integer(),
next_literal = 0 :: non_neg_integer(),
@@ -66,13 +65,14 @@ highest_opcode(#asm{highest_opcode=Op}) -> Op.
%% atom(Atom, Dict) -> {Index,Dict'}
-spec atom(atom(), bdict()) -> {pos_integer(), bdict()}.
-atom(Atom, #asm{atoms=Atoms0,next_atom=NextIndex}=Dict) when is_atom(Atom) ->
+atom(Atom, #asm{atoms=Atoms0}=Dict) when is_atom(Atom) ->
case gb_trees:lookup(Atom, Atoms0) of
{value,Index} ->
{Index,Dict};
none ->
+ NextIndex = gb_trees:size(Atoms0) + 1,
Atoms = gb_trees:insert(Atom, NextIndex, Atoms0),
- {NextIndex,Dict#asm{atoms=Atoms,next_atom=NextIndex+1}}
+ {NextIndex,Dict#asm{atoms=Atoms}}
end.
%% Remembers an exported function.
@@ -119,10 +119,11 @@ import(Mod0, Name0, Arity, #asm{imports=Imp0,next_import=NextIndex}=D0)
string(Str, Dict) when is_list(Str) ->
#asm{strings=Strings,string_offset=NextOffset} = Dict,
- case old_string(Str, Strings) of
+ StrBin = list_to_binary(Str),
+ case old_string(StrBin, Strings) of
none ->
- NewDict = Dict#asm{strings=Strings++Str,
- string_offset=NextOffset+length(Str)},
+ NewDict = Dict#asm{strings = <<Strings/binary,StrBin/binary>>,
+ string_offset=NextOffset+byte_size(StrBin)},
{NextOffset,NewDict};
Offset when is_integer(Offset) ->
{NextOffset-Offset,Dict}
@@ -138,7 +139,7 @@ lambda(Lbl, Index, OldUniq, NumFree, #asm{lambdas=Lambdas0}=Dict) ->
Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0],
{OldIndex,Dict#asm{lambdas=Lambdas}}.
-%% Returns the index for a literal (adding it to the atom table if necessary).
+%% Returns the index for a literal (adding it to the literal table if necessary).
%% literal(Literal, Dict) -> {Index,Dict'}
-spec literal(term(), bdict()) -> {non_neg_integer(), bdict()}.
@@ -155,14 +156,15 @@ literal(Lit, #asm{literals=Tab0,next_literal=NextIndex}=Dict) ->
%% atom_table(Dict) -> {LastIndex,[Length,AtomString...]}
-spec atom_table(bdict()) -> {non_neg_integer(), [[non_neg_integer(),...]]}.
-atom_table(#asm{atoms=Atoms,next_atom=NumAtoms}) ->
+atom_table(#asm{atoms=Atoms}) ->
+ NumAtoms = gb_trees:size(Atoms),
Sorted = lists:keysort(2, gb_trees:to_list(Atoms)),
Fun = fun({A,_}) ->
L = atom_to_list(A),
[length(L)|L]
end,
AtomTab = lists:map(Fun, Sorted),
- {NumAtoms-1,AtomTab}.
+ {NumAtoms,AtomTab}.
%% Returns the table of local functions.
%% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]}
@@ -187,7 +189,7 @@ import_table(#asm{imports=Imp,next_import=NumImports}) ->
ImpTab = [MFA || {MFA,_} <- Sorted],
{NumImports,ImpTab}.
--spec string_table(bdict()) -> {non_neg_integer(), [string()]}.
+-spec string_table(bdict()) -> {non_neg_integer(), binary()}.
string_table(#asm{strings=Strings,string_offset=Size}) ->
{Size,Strings}.
@@ -217,15 +219,12 @@ literal_table(#asm{literals=Tab,next_literal=NumLiterals}) ->
my_term_to_binary(Term) ->
term_to_binary(Term, [{minor_version,1}]).
-%% Search for string Str in the string pool Pool.
+%% Search for binary string Str in the binary string pool Pool.
%% old_string(Str, Pool) -> none | Index
--spec old_string(string(), [string()]) -> 'none' | pos_integer().
-
-old_string([C|Str]=Str0, [C|Pool]) ->
- case lists:prefix(Str, Pool) of
- true -> length(Pool)+1;
- false -> old_string(Str0, Pool)
- end;
-old_string([_|_]=Str, [_|Pool]) ->
- old_string(Str, Pool);
-old_string([_|_], []) -> none.
+-spec old_string(binary(), binary()) -> 'none' | pos_integer().
+
+old_string(Str, Pool) ->
+ case binary:match(Pool, Str) of
+ nomatch -> none;
+ {Start,_Length} -> byte_size(Pool) - Start
+ end.
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 9571f817e3..017ca129b0 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -621,8 +621,7 @@ resolve_names(Fun, Imports, Str, Lbls, Lambdas, Literals, M) ->
%%
%% New make_fun2/4 instruction added in August 2001 (R8).
-%% New put_literal/2 instruction added in Feb 2006 R11B-4.
-%% We handle them specially here to avoid adding an argument to
+%% We handle it specially here to avoid adding an argument to
%% the clause for every instruction.
%%
@@ -631,8 +630,6 @@ resolve_inst({make_fun2,Args}, _, _, _, Lambdas, _, M) ->
{OldIndex,{F,A,_Lbl,_Index,NumFree,OldUniq}} =
lists:keyfind(OldIndex, 1, Lambdas),
{make_fun2,{M,F,A},OldIndex,OldUniq,NumFree};
-resolve_inst({put_literal,[{u,Index},Dst]},_,_,_,_,Literals,_) ->
- {put_literal,{literal,gb_trees:get(Index, Literals)},Dst};
resolve_inst(Instr, Imports, Str, Lbls, _Lambdas, _Literals, _M) ->
%% io:format(?MODULE_STRING":resolve_inst ~p.~n", [Instr]),
resolve_inst(Instr, Imports, Str, Lbls).
@@ -1004,13 +1001,17 @@ resolve_inst({gc_bif2,Args},Imports,_,_) ->
[F,Live,Bif,A1,A2,Reg] = resolve_args(Args),
{extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
{gc_bif,BifName,F,Live,[A1,A2],Reg};
+%%
+%% New instruction in R14, gc_bif with 3 arguments
+%%
+resolve_inst({gc_bif3,Args},Imports,_,_) ->
+ [F,Live,Bif,A1,A2,A3,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
+ {gc_bif,BifName,F,Live,[A1,A2,A3],Reg};
%%
%% New instructions for creating non-byte aligned binaries.
%%
-resolve_inst({bs_bits_to_bytes2,[_Arg2,_Arg3]=Args},_,_,_) ->
- [A2,A3] = resolve_args(Args),
- {bs_bits_to_bytes2,A2,A3};
resolve_inst({bs_final2,[X,Y]},_,_,_) ->
{bs_final2,X,Y};
diff --git a/lib/compiler/src/beam_peep.erl b/lib/compiler/src/beam_peep.erl
index d03ac4b1f4..f39fc50b95 100644
--- a/lib/compiler/src/beam_peep.erl
+++ b/lib/compiler/src/beam_peep.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -64,22 +64,7 @@ function({function,Name,Arity,CLabel,Is0}) ->
%% InEncoding =:= latin1, OutEncoding =:= unicode;
%% InEncoding =:= latin1, OutEncoding =:= utf8 ->
%%
-%% (2) Code like
-%%
-%% is_ne_exact Fail Reg Literal1
-%% is_ne_exact Fail Reg Literal2
-%% is_ne_exact Fail Reg Literal3
-%% is_eq_exact UltimateFail Reg Literal4
-%% Fail: ....
-%%
-%% can be rewritten to
-%%
-%% select_val Reg UltimateFail [ Literal1 Fail
-%% Literal2 Fail
-%% Literal3 Fail
-%% Literal4 Fail ]
-%%
-%% (3) A select_val/4 instruction that only verifies that
+%% (2) A select_val/4 instruction that only verifies that
%% its argument is either 'true' or 'false' can be
%% be replaced with an is_boolean/2 instruction. That is:
%%
@@ -132,7 +117,7 @@ peep([{test,Op,_,Ops}=I|Is], SeenTests0, Acc) ->
false ->
%% Remember that we have seen this test.
SeenTests = gb_sets:insert(Test, SeenTests0),
- make_select_val(I, Is, SeenTests, Acc)
+ peep(Is, SeenTests, [I|Acc])
end
end;
peep([{select_val,Src,Fail,
@@ -151,33 +136,6 @@ peep([I|Is], _, Acc) ->
peep(Is, gb_sets:empty(), [I|Acc]);
peep([], _, Acc) -> reverse(Acc).
-make_select_val({test,is_ne_exact,{f,Fail},[Val,Lit]}=I0,
- Is0, SeenTests, Acc) ->
- try
- Type = case Lit of
- {atom,_} -> atom;
- {integer,_} -> integer;
- _ -> throw(impossible)
- end,
- {I,Is} = make_select_val_1(Is0, Fail, Val, Type, [Lit,{f,Fail}]),
- peep([I|Is], SeenTests, Acc)
- catch
- impossible ->
- peep(Is0, SeenTests, [I0|Acc])
- end;
-make_select_val(I, Is, SeenTests, Acc) ->
- peep(Is, SeenTests, [I|Acc]).
-
-make_select_val_1([{test,is_ne_exact,{f,Fail},[Val,{Type,_}=Lit]}|Is],
- Fail, Val, Type, Acc) ->
- make_select_val_1(Is, Fail, Val, Type, [Lit,{f,Fail}|Acc]);
-make_select_val_1([{test,is_eq_exact,{f,UltimateFail},[Val,{Type,_}=Lit]} |
- [{label,Fail}|_]=Is], Fail, Val, Type, Acc) ->
- Choices = [Lit,{f,Fail}|Acc],
- I = {select_val,Val,{f,UltimateFail},{list,Choices}},
- {I,Is};
-make_select_val_1(_Is, _Fail, _Val, _Type, _Acc) -> throw(impossible).
-
kill_seen(Dst, Seen0) ->
gb_sets:from_ordset(kill_seen_1(gb_sets:to_list(Seen0), Dst)).
@@ -187,5 +145,3 @@ kill_seen_1([{_,Ops}=Test|T], Dst) ->
false -> [Test|kill_seen_1(T, Dst)]
end;
kill_seen_1([], _) -> [].
-
-
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 3729ccb0da..f83f73b224 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -76,9 +76,6 @@ simplify_basic_1([{set,[D],[{integer,Index},Reg],{bif,element,_}}=I0|Is], Ts0, A
end,
Ts = update(I, Ts0),
simplify_basic_1(Is, Ts, [I|Acc]);
-simplify_basic_1([{set,[_],[_],{bif,_,{f,0}}}=I|Is], Ts0, Acc) ->
- Ts = update(I, Ts0),
- simplify_basic_1(Is, Ts, [I|Acc]);
simplify_basic_1([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Acc) ->
case tdb_find(TupleReg, Ts0) of
{tuple,_,[Contents]} ->
@@ -118,7 +115,6 @@ simplify_basic_1([{test,is_record,_,[R,{atom,_}=Tag,{integer,Arity}]}=I|Is], Ts0
Ts = update(I, Ts0),
simplify_basic_1(Is, Ts, [I|Acc])
end;
-
simplify_basic_1([I|Is], Ts0, Acc) ->
Ts = update(I, Ts0),
simplify_basic_1(Is, Ts, [I|Acc]);
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index ac249e6672..45cdf8a659 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Purpose : Common utilities used by several optimization passes.
@@ -407,16 +407,23 @@ check_liveness(R, [{bif,Op,{f,Fail},Ss,D}|Is], St0) ->
Other ->
Other
end;
-check_liveness(R, [{gc_bif,Op,{f,Fail},_,Ss,D}|Is], St0) ->
- case check_liveness_fail(R, Op, Ss, Fail, St0) of
- {killed,St} = Killed ->
- case member(R, Ss) of
- true -> {used,St};
- false when R =:= D -> Killed;
- false -> check_liveness(R, Is, St)
- end;
- Other ->
- Other
+check_liveness(R, [{gc_bif,Op,{f,Fail},Live,Ss,D}|Is], St0) ->
+ case R of
+ {x,X} when X >= Live ->
+ {killed,St0};
+ {x,_} ->
+ {used,St0};
+ _ ->
+ case check_liveness_fail(R, Op, Ss, Fail, St0) of
+ {killed,St}=Killed ->
+ case member(R, Ss) of
+ true -> {used,St};
+ false when R =:= D -> Killed;
+ false -> check_liveness(R, Is, St)
+ end;
+ Other ->
+ Other
+ end
end;
check_liveness(R, [{bs_add,{f,0},Ss,D}|Is], St) ->
case member(R, Ss) of
@@ -424,12 +431,6 @@ check_liveness(R, [{bs_add,{f,0},Ss,D}|Is], St) ->
false when R =:= D -> {killed,St};
false -> check_liveness(R, Is, St)
end;
-check_liveness(R, [{bs_bits_to_bytes2,Src,Dst}|Is], St) ->
- case R of
- Src -> {used,St};
- Dst -> {killed,St};
- _ -> check_liveness(R, Is, St)
- end;
check_liveness(R, [{bs_put_binary,{f,0},Sz,_,_,Src}|Is], St) ->
case member(R, [Sz,Src]) of
true -> {used,St};
@@ -488,10 +489,13 @@ check_liveness(R, [{bs_context_to_binary,S}|Is], St) ->
S -> {used,St};
_ -> check_liveness(R, Is, St)
end;
-check_liveness(R, [{loop_rec,{f,_},{x,0}}|Is], St) ->
+check_liveness(R, [{loop_rec,{f,_},{x,0}}|_], St) ->
case R of
- {x,_} -> {killed,St};
- _ -> check_liveness(R, Is, St)
+ {x,_} ->
+ {killed,St};
+ _ ->
+ %% y register. Rarely happens. Be very conversative.
+ {unknown,St}
end;
check_liveness(R, [{loop_rec_end,{f,Fail}}|_], St) ->
check_liveness_at(R, Fail, St);
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 34065cfdce..fb267b35b6 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -18,6 +18,10 @@
-module(beam_validator).
+-compile({no_auto_import,[min/2]}).
+
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([file/1, files/1]).
%% Interface for compiler.
@@ -757,9 +761,6 @@ valfun_4({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->
valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
-valfun_4({bs_bits_to_bytes2,Src,Dst}, Vst) ->
- assert_term(Src, Vst),
- set_type_reg({integer,[]}, Dst, Vst);
valfun_4({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst) ->
assert_term(Src, Vst),
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl
index 74fc0878cf..4b74d60e9f 100644
--- a/lib/compiler/src/cerl.erl
+++ b/lib/compiler/src/cerl.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%% =====================================================================
@@ -122,6 +122,9 @@
bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1,
bitstr_flags/1]).
+-export_type([c_binary/0, c_call/0, c_clause/0, c_cons/0, c_fun/0, c_literal/0,
+ c_module/0, c_tuple/0, c_values/0, c_var/0, cerl/0, var_name/0]).
+
%%
%% needed by the include file below -- do not move
%%
@@ -970,7 +973,7 @@ atom_name(Node) ->
%% TODO: replace the use of the unofficial 'write_string/2'.
--spec atom_lit(cerl()) -> string().
+-spec atom_lit(cerl()) -> nonempty_string().
atom_lit(Node) ->
io_lib:write_string(atom_name(Node), $'). %' stupid Emacs.
@@ -1076,7 +1079,7 @@ char_val(Node) ->
%%
%% @see c_char/1
--spec char_lit(c_literal()) -> string().
+-spec char_lit(c_literal()) -> nonempty_string().
char_lit(Node) ->
io_lib:write_char(char_val(Node)).
@@ -1175,7 +1178,7 @@ string_val(Node) ->
%%
%% @see c_string/1
--spec string_lit(c_literal()) -> string().
+-spec string_lit(c_literal()) -> nonempty_string().
string_lit(Node) ->
io_lib:write_string(string_val(Node)).
diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl
index 6d7eca0113..c15103999f 100644
--- a/lib/compiler/src/cerl_inline.erl
+++ b/lib/compiler/src/cerl_inline.erl
@@ -65,7 +65,6 @@
try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
type/1, values_es/1, var_name/1]).
--import(erlang, [max/2]).
-import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]).
%%
@@ -201,9 +200,9 @@ start(Reply, Tree, Ctxt, Opts) ->
false ->
ok
end,
- Size = max(1, proplists:get_value(inline_size, Opts)),
- Effort = max(1, proplists:get_value(inline_effort, Opts)),
- Unroll = max(1, proplists:get_value(inline_unroll, Opts)),
+ Size = erlang:max(1, proplists:get_value(inline_size, Opts)),
+ Effort = erlang:max(1, proplists:get_value(inline_effort, Opts)),
+ Unroll = erlang:max(1, proplists:get_value(inline_unroll, Opts)),
case proplists:get_bool(verbose, Opts) of
true ->
io:fwrite("Inlining: inline_size=~w inline_effort=~w\n",
diff --git a/lib/compiler/src/cerl_trees.erl b/lib/compiler/src/cerl_trees.erl
index 7a2057713e..1e3755025f 100644
--- a/lib/compiler/src/cerl_trees.erl
+++ b/lib/compiler/src/cerl_trees.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%% @doc Basic functions on Core Erlang abstract syntax trees.
@@ -73,14 +73,12 @@ depth(T) ->
[] ->
0;
Gs ->
- 1 + lists:foldl(fun (G, A) -> max(depth_1(G), A) end, 0, Gs)
+ 1 + lists:foldl(fun (G, A) -> erlang:max(depth_1(G), A) end, 0, Gs)
end.
depth_1(Ts) ->
- lists:foldl(fun (T, A) -> max(depth(T), A) end, 0, Ts).
+ lists:foldl(fun (T, A) -> erlang:max(depth(T), A) end, 0, Ts).
-max(X, Y) when X > Y -> X;
-max(_, Y) -> Y.
%% @spec size(Tree::cerl()) -> integer()
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 3f250a6d5a..ce8a5bf864 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,8 @@
%% Erlc interface.
-export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]).
+-export_type([option/0]).
+
-include("erl_compile.hrl").
-include("core_parse.hrl").
@@ -39,8 +41,7 @@
-type option() :: atom() | {atom(), term()} | {'d', atom(), term()}.
--type line() :: integer().
--type err_info() :: {line(), module(), term()}. %% ErrorDescriptor
+-type err_info() :: {erl_scan:line(), module(), term()}. %% ErrorDescriptor
-type errors() :: [{file:filename(), [err_info()]}].
-type warnings() :: [{file:filename(), [err_info()]}].
-type mod_ret() :: {'ok', module()}
@@ -68,7 +69,7 @@
file(File) -> file(File, ?DEFAULT_OPTIONS).
--spec file(module() | file:filename(), [option()]) -> comp_ret().
+-spec file(module() | file:filename(), [option()] | option()) -> comp_ret().
file(File, Opts) when is_list(Opts) ->
do_compile({file,File}, Opts++env_default_opts());
@@ -86,6 +87,8 @@ forms(Forms, Opt) when is_atom(Opt) ->
%% would have generated a Beam file, false otherwise (if only a binary or a
%% listing file would have been generated).
+-spec output_generated([option()]) -> boolean().
+
output_generated(Opts) ->
noenv_output_generated(Opts++env_default_opts()).
@@ -94,6 +97,8 @@ output_generated(Opts) ->
%% for default options.
%%
+-spec noenv_file(module() | file:filename(), [option()] | option()) -> comp_ret().
+
noenv_file(File, Opts) when is_list(Opts) ->
do_compile({file,File}, Opts);
noenv_file(File, Opt) ->
@@ -104,10 +109,13 @@ noenv_forms(Forms, Opts) when is_list(Opts) ->
noenv_forms(Forms, Opt) when is_atom(Opt) ->
noenv_forms(Forms, [Opt|?DEFAULT_OPTIONS]).
+-spec noenv_output_generated([option()]) -> boolean().
+
noenv_output_generated(Opts) ->
+ {_,Passes} = passes(file, expand_opts(Opts)),
any(fun ({save_binary,_F}) -> true;
(_Other) -> false
- end, passes(file, expand_opts(Opts))).
+ end, Passes).
%%
%% Local functions
@@ -198,6 +206,9 @@ format_error(write_error) ->
format_error({rename,From,To,Error}) ->
io_lib:format("failed to rename ~s to ~s: ~s",
[From,To,file:format_error(Error)]);
+format_error({delete,File,Error}) ->
+ io_lib:format("failed to delete file ~s: ~s",
+ [File,file:format_error(Error)]);
format_error({delete_temp,File,Error}) ->
io_lib:format("failed to delete temporary file ~s: ~s",
[File,file:format_error(Error)]);
@@ -214,16 +225,16 @@ format_error({module_name,Mod,Filename}) ->
[Mod,Filename]).
%% The compile state record.
--record(compile, {filename="",
- dir="",
- base="",
- ifile="",
- ofile="",
+-record(compile, {filename="" :: file:filename(),
+ dir="" :: file:filename(),
+ base="" :: file:filename(),
+ ifile="" :: file:filename(),
+ ofile="" :: file:filename(),
module=[],
code=[],
core_code=[],
abstract_code=[], %Abstract code for debugger.
- options=[],
+ options=[] :: [option()],
errors=[],
warnings=[]}).
@@ -233,26 +244,12 @@ internal(Master, Input, Opts) ->
end}.
internal({forms,Forms}, Opts) ->
- Ps = passes(forms, Opts),
+ {_,Ps} = passes(forms, Opts),
internal_comp(Ps, "", "", #compile{code=Forms,options=Opts});
internal({file,File}, Opts) ->
- Ps = passes(file, Opts),
+ {Ext,Ps} = passes(file, Opts),
Compile = #compile{options=Opts},
- case member(from_core, Opts) of
- true -> internal_comp(Ps, File, ".core", Compile);
- false ->
- case member(from_beam, Opts) of
- true ->
- internal_comp(Ps, File, ".beam", Compile);
- false ->
- case member(from_asm, Opts) orelse member(asm, Opts) of
- true ->
- internal_comp(Ps, File, ".S", Compile);
- false ->
- internal_comp(Ps, File, ".erl", Compile)
- end
- end
- end.
+ internal_comp(Ps, File, Ext, Compile).
internal_comp(Passes, File, Suffix, St0) ->
Dir = filename:dirname(File),
@@ -294,15 +291,6 @@ fold_comp([{Name,Pass}|Ps], Run, St0) ->
end;
fold_comp([], _Run, St) -> {ok,St}.
-os_process_size() ->
- case os:type() of
- {unix, sunos} ->
- Size = os:cmd("ps -o vsz -p " ++ os:getpid() ++ " | tail -1"),
- list_to_integer(lib:nonl(Size));
- _ ->
- 0
- end.
-
run_tc({Name,Fun}, St) ->
Before0 = statistics(runtime),
Val = (catch Fun(St)),
@@ -311,9 +299,8 @@ run_tc({Name,Fun}, St) ->
{After_c, _} = After0,
Mem0 = erts_debug:flat_size(Val)*erlang:system_info(wordsize),
Mem = lists:flatten(io_lib:format("~.1f kB", [Mem0/1024])),
- Sz = lists:flatten(io_lib:format("~.1f MB", [os_process_size()/1024])),
- io:format(" ~-30s: ~10.2f s ~12s ~10s\n",
- [Name,(After_c-Before_c) / 1000,Mem,Sz]),
+ io:format(" ~-30s: ~10.2f s ~12s\n",
+ [Name,(After_c-Before_c) / 1000,Mem]),
Val.
comp_ret_ok(#compile{code=Code,warnings=Warn0,module=Mod,options=Opts}=St) ->
@@ -370,42 +357,52 @@ mpf(Ms) ->
[{File,[M || {F,M} <- Ms, F =:= File]} ||
File <- lists:usort([F || {F,_} <- Ms])].
-%% passes(form|file, [Option]) -> [{Name,PassFun}]
-%% Figure out which passes that need to be run.
-
-passes(forms, Opts) ->
- case member(from_core, Opts) of
- true ->
- select_passes(core_passes(), Opts);
- false ->
- select_passes(standard_passes(), Opts)
+%% passes(forms|file, [Option]) -> {Extension,[{Name,PassFun}]}
+%% Figure out the extension of the input file and which passes
+%% that need to be run.
+
+passes(Type, Opts) ->
+ {Ext,Passes0} = passes_1(Opts),
+ Passes1 = case Type of
+ file -> Passes0;
+ forms -> tl(Passes0)
+ end,
+ Passes = select_passes(Passes1, Opts),
+
+ %% If the last pass saves the resulting binary to a file,
+ %% insert a first pass to remove the file (unless the
+ %% source file is a BEAM file).
+ {Ext,case last(Passes) of
+ {save_binary,_Fun} ->
+ case Passes of
+ [{read_beam_file,_}|_] ->
+ %% The BEAM is both input and output.
+ %% Don't remove it.
+ Passes;
+ _ ->
+ [?pass(remove_file)|Passes]
+ end;
+ _ ->
+ Passes
+ end}.
+
+passes_1([Opt|Opts]) ->
+ case pass(Opt) of
+ {_,_}=Res -> Res;
+ none -> passes_1(Opts)
end;
-passes(file, Opts) ->
- case member(from_beam, Opts) of
- true ->
- Ps = [?pass(read_beam_file)|binary_passes()],
- select_passes(Ps, Opts);
- false ->
- Ps = case member(from_asm, Opts) orelse member(asm, Opts) of
- true ->
- [?pass(beam_consult_asm)|asm_passes()];
- false ->
- case member(from_core, Opts) of
- true ->
- [?pass(parse_core)|core_passes()];
- false ->
- [?pass(parse_module)|standard_passes()]
- end
- end,
- Fs = select_passes(Ps, Opts),
-
- %% If the last pass saves the resulting binary to a file,
- %% insert a first pass to remove the file.
- case last(Fs) of
- {save_binary,_Fun} -> [?pass(remove_file)|Fs];
- _Other -> Fs
- end
- end.
+passes_1([]) ->
+ {".erl",[?pass(parse_module)|standard_passes()]}.
+
+pass(from_core) ->
+ {".core",[?pass(parse_core)|core_passes()]};
+pass(from_asm) ->
+ {".S",[?pass(beam_consult_asm)|asm_passes()]};
+pass(asm) ->
+ pass(from_asm);
+pass(from_beam) ->
+ {".beam",[?pass(read_beam_file)|binary_passes()]};
+pass(_) -> none.
%% select_passes([Command], Opts) -> [{Name,Function}]
%% Interpret the lists of commands to return a pure list of passes.
@@ -438,6 +435,8 @@ passes(file, Opts) ->
%% file will be Ext. (Ext should not contain
%% a period.) No more passes will be run.
%%
+%% done End compilation at this point.
+%%
%% {done,Ext} End compilation at this point. Produce a listing
%% as with {listing,Ext}, unless 'binary' is
%% specified, in which case the current
@@ -471,6 +470,8 @@ select_passes([{src_listing,Ext}|_], _Opts) ->
[{listing,fun (St) -> src_listing(Ext, St) end}];
select_passes([{listing,Ext}|_], _Opts) ->
[{listing,fun (St) -> listing(Ext, St) end}];
+select_passes([done|_], _Opts) ->
+ [];
select_passes([{done,Ext}|_], Opts) ->
select_passes([{unless,binary,{listing,Ext}}], Opts);
select_passes([{iff,Flag,Pass}|Ps], Opts) ->
@@ -553,6 +554,13 @@ select_list_passes_1([], _, Acc) ->
standard_passes() ->
[?pass(transform_module),
+
+ {iff,makedep,[
+ ?pass(makedep),
+ {unless,binary,?pass(makedep_output)}
+ ]},
+ {iff,makedep,done},
+
{iff,'dpp',{listing,"pp"}},
?pass(lint_module),
{iff,'P',{src_listing,"P"}},
@@ -827,6 +835,10 @@ foldl_transform(St, [T|Ts]) ->
{'EXIT',R} ->
Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}],
{error,St#compile{errors=St#compile.errors ++ Es}};
+ {warning, Forms, Ws} ->
+ foldl_transform(
+ St#compile{code=Forms,
+ warnings=St#compile.warnings ++ Ws}, Ts);
Forms ->
foldl_transform(St#compile{code=Forms}, Ts)
end;
@@ -836,7 +848,6 @@ get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts].
core_transforms(St) ->
%% The options field holds the complete list of options at this
-
Ts = get_core_transforms(St#compile.options),
foldl_core_transforms(St, Ts).
@@ -901,6 +912,184 @@ core_lint_module(St) ->
errors=St#compile.errors ++ Es}}
end.
+makedep(#compile{code=Code,options=Opts}=St) ->
+ Ifile = St#compile.ifile,
+ Ofile = St#compile.ofile,
+
+ %% Get the target of the Makefile rule.
+ Target0 =
+ case proplists:get_value(makedep_target, Opts) of
+ undefined ->
+ %% The target is derived from the output filename: possibly
+ %% remove the current working directory to obtain a relative
+ %% path.
+ shorten_filename(Ofile);
+ T ->
+ %% The caller specified one.
+ T
+ end,
+
+ %% Quote the target is the called asked for this.
+ Target1 = case proplists:get_value(makedep_quote_target, Opts) of
+ true ->
+ %% For now, only "$" is replaced by "$$".
+ Fun = fun
+ ($$) -> "$$";
+ (C) -> C
+ end,
+ map(Fun, Target0);
+ _ ->
+ Target0
+ end,
+ Target = Target1 ++ ":",
+
+ %% List the dependencies (includes) for this target.
+ {MainRule,PhonyRules} = makedep_add_headers(
+ Ifile, % The input file name.
+ Code, % The parsed source.
+ [], % The list of dependencies already added.
+ length(Target), % The current line length.
+ Target, % The target.
+ "", % Phony targets.
+ Opts),
+
+ %% Prepare the content of the Makefile. For instance:
+ %% hello.erl: hello.hrl common.hrl
+ %%
+ %% Or if phony targets are enabled:
+ %% hello.erl: hello.hrl common.hrl
+ %%
+ %% hello.hrl:
+ %%
+ %% common.hrl:
+ Makefile = case proplists:get_value(makedep_phony, Opts) of
+ true -> MainRule ++ PhonyRules;
+ _ -> MainRule
+ end,
+ {ok,St#compile{code=iolist_to_binary([Makefile,"\n"])}}.
+
+makedep_add_headers(Ifile, [{attribute,_,file,{File,_}}|Rest],
+ Included, LineLen, MainTarget, Phony, Opts) ->
+ %% The header "File" exists, add it to the dependencies.
+ {Included1,LineLen1,MainTarget1,Phony1} =
+ makedep_add_header(Ifile, Included, LineLen, MainTarget, Phony, File),
+ makedep_add_headers(Ifile, Rest, Included1, LineLen1,
+ MainTarget1, Phony1, Opts);
+makedep_add_headers(Ifile, [{error,{_,epp,{include,file,File}}}|Rest],
+ Included, LineLen, MainTarget, Phony, Opts) ->
+ %% The header "File" doesn't exist, do we add it to the dependencies?
+ case proplists:get_value(makedep_add_missing, Opts) of
+ true ->
+ {Included1,LineLen1,MainTarget1,Phony1} =
+ makedep_add_header(Ifile, Included, LineLen, MainTarget,
+ Phony, File),
+ makedep_add_headers(Ifile, Rest, Included1, LineLen1,
+ MainTarget1, Phony1, Opts);
+ _ ->
+ makedep_add_headers(Ifile, Rest, Included, LineLen,
+ MainTarget, Phony, Opts)
+ end;
+makedep_add_headers(Ifile, [_|Rest], Included, LineLen,
+ MainTarget, Phony, Opts) ->
+ makedep_add_headers(Ifile, Rest, Included,
+ LineLen, MainTarget, Phony, Opts);
+makedep_add_headers(_Ifile, [], _Included, _LineLen,
+ MainTarget, Phony, _Opts) ->
+ {MainTarget,Phony}.
+
+makedep_add_header(Ifile, Included, LineLen, MainTarget, Phony, File) ->
+ case member(File, Included) of
+ true ->
+ %% This file was already listed in the dependencies, skip it.
+ {Included,LineLen,MainTarget,Phony};
+ false ->
+ Included1 = [File|Included],
+
+ %% Remove "./" in front of the dependency filename.
+ File1 = case File of
+ "./" ++ File0 -> File0;
+ _ -> File
+ end,
+
+ %% Prepare the phony target name.
+ Phony1 = case File of
+ Ifile -> Phony;
+ _ -> Phony ++ "\n\n" ++ File1 ++ ":"
+ end,
+
+ %% Add the file to the dependencies. Lines longer than 76 columns
+ %% are splitted.
+ if
+ LineLen + 1 + length(File1) > 76 ->
+ LineLen1 = 2 + length(File1),
+ MainTarget1 = MainTarget ++ " \\\n " ++ File1,
+ {Included1,LineLen1,MainTarget1,Phony1};
+ true ->
+ LineLen1 = LineLen + 1 + length(File1),
+ MainTarget1 = MainTarget ++ " " ++ File1,
+ {Included1,LineLen1,MainTarget1,Phony1}
+ end
+ end.
+
+makedep_output(#compile{code=Code,options=Opts,ofile=Ofile}=St) ->
+ %% Write this Makefile (Code) to the selected output.
+ %% If no output is specified, the default is to write to a file named after
+ %% the output file.
+ Output0 = case proplists:get_value(makedep_output, Opts) of
+ undefined ->
+ %% Prepare the default filename.
+ outfile(filename:basename(Ofile, ".beam"), "Pbeam", Opts);
+ O ->
+ O
+ end,
+
+ %% If the caller specified an io_device(), there's nothing to do. If he
+ %% specified a filename, we must create it. Furthermore, this created file
+ %% must be closed before returning.
+ Ret = case Output0 of
+ _ when is_list(Output0) ->
+ case file:delete(Output0) of
+ Ret2 when Ret2 =:= ok; Ret2 =:= {error,enoent} ->
+ case file:open(Output0, [write]) of
+ {ok,IODev} ->
+ {ok,IODev,true};
+ {error,Reason2} ->
+ {error,open,Reason2}
+ end;
+ {error,Reason1} ->
+ {error,delete,Reason1}
+ end;
+ _ ->
+ {ok,Output0,false}
+ end,
+
+ case Ret of
+ {ok,Output1,CloseOutput} ->
+ try
+ %% Write the Makefile.
+ io:fwrite(Output1, "~s", [Code]),
+ %% Close the file if relevant.
+ if
+ CloseOutput -> file:close(Output1);
+ true -> ok
+ end,
+ {ok,St}
+ catch
+ exit:_ ->
+ %% Couldn't write to output Makefile.
+ Err = {St#compile.ifile,[{none,?MODULE,write_error}]},
+ {error,St#compile{errors=St#compile.errors++[Err]}}
+ end;
+ {error,open,Reason} ->
+ %% Couldn't open output Makefile.
+ Err = {St#compile.ifile,[{none,?MODULE,{open,Reason}}]},
+ {error,St#compile{errors=St#compile.errors++[Err]}};
+ {error,delete,Reason} ->
+ %% Couldn't open output Makefile.
+ Err = {St#compile.ifile,[{none,?MODULE,{delete,Output0,Reason}}]},
+ {error,St#compile{errors=St#compile.errors++[Err]}}
+ end.
+
%% expand_module(State) -> State'
%% Do the common preprocessing of the input forms.
@@ -910,13 +1099,8 @@ expand_module(#compile{code=Code,options=Opts0}=St0) ->
{ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}.
core_module(#compile{code=Code0,options=Opts}=St) ->
- case v3_core:module(Code0, Opts) of
- {ok,Code,Ws} ->
- {ok,St#compile{code=Code,warnings=St#compile.warnings ++ Ws}};
- {error,Es,Ws} ->
- {error,St#compile{warnings=St#compile.warnings ++ Ws,
- errors=St#compile.errors ++ Es}}
- end.
+ {ok,Code,Ws} = v3_core:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ Ws}}.
core_fold_module(#compile{code=Code0,options=Opts,warnings=Warns}=St) ->
{ok,Code,Ws} = sys_core_fold:module(Code0, Opts),
@@ -1185,12 +1369,12 @@ write_binary(Name, Bin, St) ->
%% report_errors(State) -> ok
%% report_warnings(State) -> ok
-report_errors(St) ->
- case member(report_errors, St#compile.options) of
+report_errors(#compile{options=Opts,errors=Errors}) ->
+ case member(report_errors, Opts) of
true ->
foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds);
({F,Eds}) -> list_errors(F, Eds) end,
- St#compile.errors);
+ Errors);
false -> ok
end.
diff --git a/lib/compiler/src/core_lint.erl b/lib/compiler/src/core_lint.erl
index b633f568c9..b513a8965c 100644
--- a/lib/compiler/src/core_lint.erl
+++ b/lib/compiler/src/core_lint.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -65,7 +65,8 @@
| {'return_mismatch', fa()} | {'undefined_function', fa()}
| {'duplicate_var', cerl:var_name(), fa()}
| {'unbound_var', cerl:var_name(), fa()}
- | {'undefined_function', fa(), fa()}.
+ | {'undefined_function', fa(), fa()}
+ | {'tail_segment_not_at_end', fa()}.
-type error() :: {module(), err_desc()}.
-type warning() :: {module(), term()}.
@@ -116,7 +117,9 @@ format_error({duplicate_var,N,{F,A}}) ->
format_error({unbound_var,N,{F,A}}) ->
io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]);
format_error({undefined_function,{F1,A1},{F2,A2}}) ->
- io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]).
+ io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]);
+format_error({tail_segment_not_at_end,{F,A}}) ->
+ io_lib:format("binary tail segment not at end in ~w/~w", [F,A]).
-type ret() :: {'ok', [{module(), [warning(),...]}]}
| {'error', [{module(), [error(),...]}],
@@ -450,7 +453,8 @@ pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) ->
pattern_list([H,T], Def, Ps, St);
pattern(#c_tuple{es=Es}, Def, Ps, St) ->
pattern_list(Es, Def, Ps, St);
-pattern(#c_binary{segments=Ss}, Def, Ps, St) ->
+pattern(#c_binary{segments=Ss}, Def, Ps, St0) ->
+ St = pat_bin_tail_check(Ss, St0),
pat_bin(Ss, Def, Ps, St);
pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) ->
{Vvs,St1} = variable(V, Ps, St0),
@@ -482,6 +486,19 @@ pat_segment(#c_bitstr{val=V,size=S,type=T}, Def0, Ps0, St0) ->
pat_segment(_, Def, Ps, St) ->
{Ps,Def,add_error({not_bs_pattern,St#lint.func}, St)}.
+%% pat_bin_tail_check([Elem], State) -> State.
+%% There must be at most one tail segment (a size-less segment of
+%% type binary) and it must occur at the end.
+
+pat_bin_tail_check([#c_bitstr{size=#c_literal{val=all}}], St) ->
+ %% Size-less field is OK at the end of the list of segments.
+ St;
+pat_bin_tail_check([#c_bitstr{size=#c_literal{val=all}}|_], St) ->
+ add_error({tail_segment_not_at_end,St#lint.func}, St);
+pat_bin_tail_check([_|Ss], St) ->
+ pat_bin_tail_check(Ss, St);
+pat_bin_tail_check([], St) -> St.
+
%% pat_bit_expr(SizePat, Type, Defined, State) -> State.
%% Check the Size pattern, this is an input! Because of optimizations,
%% we must allow any kind of constant and literal here.
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index e87bb276de..f8128702dd 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Purpose: Information about the Erlang built-in functions.
@@ -65,6 +65,8 @@ is_pure(erlang, 'xor', 2) -> true;
is_pure(erlang, abs, 1) -> true;
is_pure(erlang, atom_to_binary, 2) -> true;
is_pure(erlang, atom_to_list, 1) -> true;
+is_pure(erlang, binary_part, 2) -> true;
+is_pure(erlang, binary_part, 3) -> true;
is_pure(erlang, binary_to_atom, 2) -> true;
is_pure(erlang, binary_to_list, 1) -> true;
is_pure(erlang, binary_to_list, 3) -> true;
diff --git a/lib/compiler/src/genop.tab b/lib/compiler/src/genop.tab
index b57508ea8e..63527bda8f 100644
--- a/lib/compiler/src/genop.tab
+++ b/lib/compiler/src/genop.tab
@@ -208,7 +208,7 @@ BEAM_FORMAT_NUMBER=0
# New instructions in R10B.
109: bs_init2/6
-110: bs_bits_to_bytes/3
+110: -bs_bits_to_bytes/3
111: bs_add/5
112: apply/1
113: apply_last/2
@@ -279,3 +279,4 @@ BEAM_FORMAT_NUMBER=0
150: recv_mark/1
151: recv_set/1
+152: gc_bif3/7
diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl
index 9b73e08ad8..31a1f8b0b7 100644
--- a/lib/compiler/src/rec_env.erl
+++ b/lib/compiler/src/rec_env.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% @author Richard Carlsson <[email protected]>
@@ -32,7 +32,7 @@
get/2, is_defined/2, is_empty/1, keys/1, lookup/2, new_key/1,
new_key/2, new_keys/2, new_keys/3, size/1, to_list/1]).
--import(erlang, [max/2]).
+-export_type([environment/0]).
-ifdef(DEBUG).
-export([test/1, test_custom/1, test_custom/2]).
@@ -586,7 +586,7 @@ new_key(N, R, _T, F, Env) ->
new_key(generate(N, R1), R1, 0, F, Env).
start_range(Env) ->
- max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
+ erlang:max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
%% The previous key might or might not be used to compute the next key
%% to be tried. It is currently not used.
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl
index 6202f07479..9360556e00 100644
--- a/lib/compiler/src/sys_core_fold.erl
+++ b/lib/compiler/src/sys_core_fold.erl
@@ -460,7 +460,8 @@ eval_binary(#c_binary{anno=Anno,segments=Ss}=Bin) ->
Bin;
throw:{badarg,Warning} ->
add_warning(Bin, Warning),
- #c_call{module=#c_literal{val=erlang},
+ #c_call{anno=Anno,
+ module=#c_literal{val=erlang},
name=#c_literal{val=error},
args=[#c_literal{val=badarg}]}
end.
@@ -658,36 +659,34 @@ call_0(Call, M, N, As0, Sub) ->
%% We inline some very common higher order list operations.
%% We use the same evaluation order as the library function.
-call_1(_Call, lists, all, [Arg1,Arg2], Sub) ->
+call_1(#c_call{anno=Anno}, lists, all, [Arg1,Arg2], Sub) ->
Loop = #c_var{name={'lists^all',1}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
Err1 = #c_tuple{es=[#c_literal{val='case_clause'}, X]},
CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true},
- body=#c_apply{op=Loop, args=[Xs]}},
+ body=#c_apply{anno=Anno, op=Loop, args=[Xs]}},
CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true},
body=#c_literal{val=false}},
CC3 = #c_clause{pats=[X], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err1]}},
+ body=match_fail(Anno, Err1)},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=#c_case{arg=#c_apply{op=F, args=[X]},
+ body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]},
clauses = [CC1, CC2, CC3]}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
body=#c_literal{val=true}},
Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err2]}},
+ body=match_fail(Anno, Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L]}}},
Sub);
-call_1(_Call, lists, any, [Arg1,Arg2], Sub) ->
+call_1(#c_call{anno=Anno}, lists, any, [Arg1,Arg2], Sub) ->
Loop = #c_var{name={'lists^any',1}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
@@ -696,72 +695,71 @@ call_1(_Call, lists, any, [Arg1,Arg2], Sub) ->
CC1 = #c_clause{pats=[#c_literal{val=true}], guard=#c_literal{val=true},
body=#c_literal{val=true}},
CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true},
- body=#c_apply{op=Loop, args=[Xs]}},
+ body=#c_apply{anno=Anno, op=Loop, args=[Xs]}},
CC3 = #c_clause{pats=[X], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err1]}},
+ body=match_fail(Anno, Err1)},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=#c_case{arg=#c_apply{op=F, args=[X]},
+ body=#c_case{arg=#c_apply{anno=Anno, op=F, args=[X]},
clauses = [CC1, CC2, CC3]}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
body=#c_literal{val=false}},
Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err2]}},
+ body=match_fail(Anno, Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L]}}},
Sub);
-call_1(_Call, lists, foreach, [Arg1,Arg2], Sub) ->
+call_1(#c_call{anno=Anno}, lists, foreach, [Arg1,Arg2], Sub) ->
Loop = #c_var{name={'lists^foreach',1}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=#c_seq{arg=#c_apply{op=F, args=[X]},
- body=#c_apply{op=Loop, args=[Xs]}}},
+ body=#c_seq{arg=#c_apply{anno=Anno, op=F, args=[X]},
+ body=#c_apply{anno=Anno, op=Loop, args=[Xs]}}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
body=#c_literal{val=ok}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L]}}},
Sub);
-call_1(_Call, lists, map, [Arg1,Arg2], Sub) ->
+call_1(#c_call{anno=Anno}, lists, map, [Arg1,Arg2], Sub) ->
Loop = #c_var{name={'lists^map',1}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
H = #c_var{name='H'},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=#c_let{vars=[H], arg=#c_apply{op=F, args=[X]},
+ body=#c_let{vars=[H], arg=#c_apply{anno=Anno,
+ op=F,
+ args=[X]},
body=#c_cons{hd=H,
- tl=#c_apply{op=Loop,
+ tl=#c_apply{anno=Anno,
+ op=Loop,
args=[Xs]}}}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
body=#c_literal{val=[]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L]}}},
Sub);
-call_1(_Call, lists, flatmap, [Arg1,Arg2], Sub) ->
+call_1(#c_call{anno=Anno}, lists, flatmap, [Arg1,Arg2], Sub) ->
Loop = #c_var{name={'lists^flatmap',1}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
@@ -769,26 +767,27 @@ call_1(_Call, lists, flatmap, [Arg1,Arg2], Sub) ->
H = #c_var{name='H'},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_let{vars=[H],
- arg=#c_apply{op=F, args=[X]},
- body=#c_call{module=#c_literal{val=erlang},
+ arg=#c_apply{anno=Anno, op=F, args=[X]},
+ body=#c_call{anno=Anno,
+ module=#c_literal{val=erlang},
name=#c_literal{val='++'},
args=[H,
- #c_apply{op=Loop,
+ #c_apply{anno=Anno,
+ op=Loop,
args=[Xs]}]}}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
body=#c_literal{val=[]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L]}}},
Sub);
-call_1(_Call, lists, filter, [Arg1,Arg2], Sub) ->
+call_1(#c_call{anno=Anno}, lists, filter, [Arg1,Arg2], Sub) ->
Loop = #c_var{name={'lists^filter',1}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
@@ -800,72 +799,75 @@ call_1(_Call, lists, filter, [Arg1,Arg2], Sub) ->
CC2 = #c_clause{pats=[#c_literal{val=false}], guard=#c_literal{val=true},
body=Xs},
CC3 = #c_clause{pats=[X], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err1]}},
+ body=match_fail(Anno, Err1)},
Case = #c_case{arg=B, clauses = [CC1, CC2, CC3]},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
body=#c_let{vars=[B],
- arg=#c_apply{op=F, args=[X]},
+ arg=#c_apply{anno=Anno, op=F, args=[X]},
body=#c_let{vars=[Xs],
- arg=#c_apply{op=Loop,
+ arg=#c_apply{anno=Anno,
+ op=Loop,
args=[Xs]},
body=Case}}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true},
body=#c_literal{val=[]}},
Err2 = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err2]}},
+ body=match_fail(Anno, Err2)},
Fun = #c_fun{vars=[Xs],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, L], arg=#c_values{es=[Arg1, Arg2]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L]}}},
Sub);
-call_1(_Call, lists, foldl, [Arg1,Arg2,Arg3], Sub) ->
+call_1(#c_call{anno=Anno}, lists, foldl, [Arg1,Arg2,Arg3], Sub) ->
Loop = #c_var{name={'lists^foldl',2}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
A = #c_var{name='A'},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=#c_apply{op=Loop,
- args=[Xs, #c_apply{op=F, args=[X, A]}]}},
+ body=#c_apply{anno=Anno,
+ op=Loop,
+ args=[Xs, #c_apply{anno=Anno,
+ op=F,
+ args=[X, A]}]}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, body=A},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, A, L], arg=#c_values{es=[Arg1, Arg2, Arg3]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L, A]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L, A]}}},
Sub);
-call_1(_Call, lists, foldr, [Arg1,Arg2,Arg3], Sub) ->
+call_1(#c_call{anno=Anno}, lists, foldr, [Arg1,Arg2,Arg3], Sub) ->
Loop = #c_var{name={'lists^foldr',2}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
X = #c_var{name='X'},
A = #c_var{name='A'},
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=#c_apply{op=F, args=[X, #c_apply{op=Loop,
- args=[Xs, A]}]}},
+ body=#c_apply{anno=Anno,
+ op=F,
+ args=[X, #c_apply{anno=Anno,
+ op=Loop,
+ args=[Xs, A]}]}},
C2 = #c_clause{pats=[#c_literal{val=[]}], guard=#c_literal{val=true}, body=A},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs, A],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, A, L], arg=#c_values{es=[Arg1, Arg2, Arg3]},
body=#c_letrec{defs=[{Loop,Fun}],
- body=#c_apply{op=Loop, args=[L, A]}}},
+ body=#c_apply{anno=Anno, op=Loop, args=[L, A]}}},
Sub);
-call_1(_Call, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) ->
+call_1(#c_call{anno=Anno}, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) ->
Loop = #c_var{name={'lists^mapfoldl',2}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
@@ -876,15 +878,16 @@ call_1(_Call, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) ->
C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E},
Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]},
C2 = #c_clause{pats=[X], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
#c_case{arg=A, clauses=[C1, C2]}
end,
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
- body=Match(#c_apply{op=F, args=[X, Avar]},
+ body=Match(#c_apply{anno=Anno, op=F, args=[X, Avar]},
#c_tuple{es=[X, Avar]},
%%% Tuple passing version
- Match(#c_apply{op=Loop, args=[Xs, Avar]},
+ Match(#c_apply{anno=Anno,
+ op=Loop,
+ args=[Xs, Avar]},
#c_tuple{es=[Xs, Avar]},
#c_tuple{es=[#c_cons{hd=X, tl=Xs}, Avar]})
%%% Multiple-value version
@@ -902,22 +905,23 @@ call_1(_Call, lists, mapfoldl, [Arg1,Arg2,Arg3], Sub) ->
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, Avar, L], arg=#c_values{es=[Arg1, Arg2, Arg3]},
body=#c_letrec{defs=[{Loop,Fun}],
%%% Tuple passing version
- body=#c_apply{op=Loop, args=[L, Avar]}}},
+ body=#c_apply{anno=Anno,
+ op=Loop,
+ args=[L, Avar]}}},
%%% Multiple-value version
%%% body=#c_let{vars=[Xs, A],
%%% arg=#c_apply{op=Loop,
%%% args=[L, A]},
%%% body=#c_tuple{es=[Xs, A]}}}},
Sub);
-call_1(_Call, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
+call_1(#c_call{anno=Anno}, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
Loop = #c_var{name={'lists^mapfoldr',2}},
F = #c_var{name='F'},
Xs = #c_var{name='Xs'},
@@ -928,15 +932,16 @@ call_1(_Call, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
C1 = #c_clause{pats=[P], guard=#c_literal{val=true}, body=E},
Err = #c_tuple{es=[#c_literal{val='badmatch'}, X]},
C2 = #c_clause{pats=[X], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
#c_case{arg=A, clauses=[C1, C2]}
end,
C1 = #c_clause{pats=[#c_cons{hd=X, tl=Xs}], guard=#c_literal{val=true},
%%% Tuple passing version
- body=Match(#c_apply{op=Loop, args=[Xs, Avar]},
+ body=Match(#c_apply{anno=Anno,
+ op=Loop,
+ args=[Xs, Avar]},
#c_tuple{es=[Xs, Avar]},
- Match(#c_apply{op=F, args=[X, Avar]},
+ Match(#c_apply{anno=Anno, op=F, args=[X, Avar]},
#c_tuple{es=[X, Avar]},
#c_tuple{es=[#c_cons{hd=X, tl=Xs}, Avar]}))
%%% Multiple-value version
@@ -955,15 +960,16 @@ call_1(_Call, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
%%% body=#c_values{es=[#c_literal{val=[]}, A]}},
Err = #c_tuple{es=[#c_literal{val='function_clause'}, Xs]},
C3 = #c_clause{pats=[Xs], guard=#c_literal{val=true},
- body=#c_primop{name=#c_literal{val='match_fail'},
- args=[Err]}},
+ body=match_fail(Anno, Err)},
Fun = #c_fun{vars=[Xs, Avar],
body=#c_case{arg=Xs, clauses=[C1, C2, C3]}},
L = #c_var{name='L'},
expr(#c_let{vars=[F, Avar, L], arg=#c_values{es=[Arg1, Arg2, Arg3]},
body=#c_letrec{defs=[{Loop,Fun}],
%%% Tuple passing version
- body=#c_apply{op=Loop, args=[L, Avar]}}},
+ body=#c_apply{anno=Anno,
+ op=Loop,
+ args=[L, Avar]}}},
%%% Multiple-value version
%%% body=#c_let{vars=[Xs, A],
%%% arg=#c_apply{op=Loop,
@@ -973,6 +979,11 @@ call_1(_Call, lists, mapfoldr, [Arg1,Arg2,Arg3], Sub) ->
call_1(#c_call{module=M, name=N}=Call, _, _, As, Sub) ->
call_0(Call, M, N, As, Sub).
+match_fail(Anno, Arg) ->
+ #c_primop{anno=Anno,
+ name=#c_literal{val='match_fail'},
+ args=[Arg]}.
+
%% fold_call(Call, Mod, Name, Args, Sub) -> Expr.
%% Try to safely evaluate the call. Just try to evaluate arguments,
%% do the call and convert return values to literals. If this
@@ -1038,6 +1049,8 @@ fold_non_lit_args(Call, lists, append, [Arg1,Arg2], _) ->
eval_append(Call, Arg1, Arg2);
fold_non_lit_args(Call, erlang, setelement, [Arg1,Arg2,Arg3], _) ->
eval_setelement(Call, Arg1, Arg2, Arg3);
+fold_non_lit_args(Call, erlang, is_record, [Arg1,Arg2,Arg3], Sub) ->
+ eval_is_record(Call, Arg1, Arg2, Arg3, Sub);
fold_non_lit_args(Call, erlang, N, Args, Sub) ->
NumArgs = length(Args),
case erl_internal:comp_op(N, NumArgs) of
@@ -1194,19 +1207,22 @@ eval_element(Call, #c_literal{val=Pos}, #c_tuple{es=Es}, _Types) when is_integer
true ->
eval_failure(Call, badarg)
end;
-%% eval_element(Call, #c_literal{val=Pos}, #c_var{name=V}, Types)
-%% when is_integer(Pos) ->
-%% case orddict:find(V, Types#sub.t) of
-%% {ok,#c_tuple{es=Elements}} ->
-%% if
-%% 1 =< Pos, Pos =< length(Elements) ->
-%% lists:nth(Pos, Elements);
-%% true ->
-%% eval_failure(Call, badarg)
-%% end;
-%% error ->
-%% Call
-%% end;
+eval_element(Call, #c_literal{val=Pos}, #c_var{name=V}, Types)
+ when is_integer(Pos) ->
+ case orddict:find(V, Types#sub.t) of
+ {ok,#c_tuple{es=Elements}} ->
+ if
+ 1 =< Pos, Pos =< length(Elements) ->
+ case lists:nth(Pos, Elements) of
+ #c_alias{var=Alias} -> Alias;
+ Res -> Res
+ end;
+ true ->
+ eval_failure(Call, badarg)
+ end;
+ error ->
+ Call
+ end;
eval_element(Call, Pos, Tuple, _Types) ->
case is_not_integer(Pos) orelse is_not_tuple(Tuple) of
true ->
@@ -1215,6 +1231,20 @@ eval_element(Call, Pos, Tuple, _Types) ->
Call
end.
+%% eval_is_record(Call, Var, Tag, Size, Types) -> Val.
+%% Evaluates is_record/3 using type information.
+%%
+eval_is_record(Call, #c_var{name=V}, #c_literal{val=NeededTag}=Lit,
+ #c_literal{val=Size}, Types) ->
+ case orddict:find(V, Types#sub.t) of
+ {ok,#c_tuple{es=[#c_literal{val=Tag}|_]=Es}} ->
+ Lit#c_literal{val=Tag =:= NeededTag andalso
+ length(Es) =:= Size};
+ _ ->
+ Call
+ end;
+eval_is_record(Call, _, _, _, _) -> Call.
+
%% is_not_integer(Core) -> true | false.
%% Returns true if Core is definitely not an integer.
@@ -1261,9 +1291,9 @@ eval_setelement_2(Pos, [H|T], NewVal) when Pos > 1 ->
%%
eval_failure(Call, Reason) ->
add_warning(Call, {eval_failure,Reason}),
- #c_call{module=#c_literal{val=erlang},
- name=#c_literal{val=error},
- args=[#c_literal{val=Reason}]}.
+ Call#c_call{module=#c_literal{val=erlang},
+ name=#c_literal{val=error},
+ args=[#c_literal{val=Reason}]}.
%% simplify_apply(Call0, Mod, Func, Args) -> Call
%% Simplify an apply/3 to a call if the number of arguments
@@ -1723,23 +1753,24 @@ opt_bool_clauses([_|_], _, _) ->
opt_bool_not(#c_case{arg=Arg,clauses=Cs0}=Case0) ->
case Arg of
- #c_call{module=#c_literal{val=erlang},
+ #c_call{anno=Anno,module=#c_literal{val=erlang},
name=#c_literal{val='not'},
args=[Expr]} ->
- Cs = opt_bool_not(Expr, Cs0),
+ Cs = opt_bool_not(Anno, Expr, Cs0),
Case = Case0#c_case{arg=Expr,clauses=Cs},
opt_bool_not(Case);
_ ->
opt_bool_case_redundant(Case0)
end.
-opt_bool_not(Expr, Cs) ->
+opt_bool_not(Anno, Expr, Cs) ->
Tail = case is_bool_expr(Expr) of
false ->
[#c_clause{anno=[compiler_generated],
pats=[#c_var{name=cor_variable}],
guard=#c_literal{val=true},
- body=#c_call{module=#c_literal{val=erlang},
+ body=#c_call{anno=Anno,
+ module=#c_literal{val=erlang},
name=#c_literal{val=error},
args=[#c_literal{val=badarg}]}}];
true -> []
@@ -1938,13 +1969,25 @@ case_tuple_pat([#c_tuple{es=Ps}], Arity) when length(Ps) =:= Arity ->
case_tuple_pat([#c_literal{val=T}], Arity) when tuple_size(T) =:= Arity ->
Ps = [#c_literal{val=E} || E <- tuple_to_list(T)],
{ok,Ps,[]};
-case_tuple_pat([#c_var{anno=A}=V], Arity) ->
- Vars = make_vars(A, 1, Arity),
- {ok,Vars,[{V,#c_tuple{es=Vars}}]};
+case_tuple_pat([#c_var{anno=Anno0}=V], Arity) ->
+ Vars = make_vars(Anno0, 1, Arity),
+
+ %% If the entire case statement is evaluated in an effect
+ %% context (e.g. "case {A,B} of ... end, ok"), there will
+ %% be a warning that a term is constructed but never used.
+ %% To avoid that warning, we must annotate the tuple as
+ %% compiler generated.
+
+ Anno = [compiler_generated|Anno0],
+ {ok,Vars,[{V,#c_tuple{anno=Anno,es=Vars}}]};
case_tuple_pat([#c_alias{var=V,pat=P}], Arity) ->
case case_tuple_pat([P], Arity) of
- {ok,Ps,Avs} -> {ok,Ps,[{V,#c_tuple{es=unalias_pat_list(Ps)}}|Avs]};
- error -> error
+ {ok,Ps,Avs} ->
+ Anno0 = core_lib:get_anno(P),
+ Anno = [compiler_generated|Anno0],
+ {ok,Ps,[{V,#c_tuple{anno=Anno,es=unalias_pat_list(Ps)}}|Avs]};
+ error ->
+ error
end;
case_tuple_pat(_, _) -> error.
diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl
index f80d03dfac..480954adac 100644
--- a/lib/compiler/src/sys_pre_expand.erl
+++ b/lib/compiler/src/sys_pre_expand.erl
@@ -403,16 +403,21 @@ expr({'fun',Line,Body}, St) ->
expr({call,Line,{atom,La,N}=Atom,As0}, St0) ->
{As,St1} = expr_list(As0, St0),
Ar = length(As),
- case erl_internal:bif(N, Ar) of
- true ->
- {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1};
- false ->
- case imported(N, Ar, St1) of
- {yes,Mod} ->
- {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1};
- no ->
- {{call,Line,Atom,As},St1}
- end
+ case defined(N,Ar,St1) of
+ true ->
+ {{call,Line,Atom,As},St1};
+ _ ->
+ case imported(N, Ar, St1) of
+ {yes,Mod} ->
+ {{call,Line,{remote,La,{atom,La,Mod},Atom},As},St1};
+ no ->
+ case erl_internal:bif(N, Ar) of
+ true ->
+ {{call,Line,{remote,La,{atom,La,erlang},Atom},As},St1};
+ false -> %% This should have been handled by erl_lint
+ {{call,Line,Atom,As},St1}
+ end
+ end
end;
expr({call,Line,{record_field,_,_,_}=M,As0}, St0) ->
expr({call,Line,expand_package(M, St0),As0}, St0);
@@ -685,3 +690,6 @@ imported(F, A, St) ->
{ok,Mod} -> {yes,Mod};
error -> no
end.
+
+defined(F, A, St) ->
+ ordsets:is_element({F,A}, St#expand.defined).
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl
index 948937c438..55e3c58d2a 100644
--- a/lib/compiler/src/v3_codegen.erl
+++ b/lib/compiler/src/v3_codegen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -235,7 +235,7 @@ match_cg(M, Rs, Le, Vdb, Bef, St0) ->
I = Le#l.i,
{Sis,Int0} = adjust_stack(Bef, I, I+1, Vdb),
{B,St1} = new_label(St0),
- {Mis,Int1,St2} = match_cg(M, St0#cg.ultimate_failure,
+ {Mis,Int1,St2} = match_cg(M, St1#cg.ultimate_failure,
Int0, St1#cg{break=B}),
%% Put return values in registers.
Reg = load_vars(Rs, Int1#sr.reg),
@@ -1523,7 +1523,9 @@ cg_binary_size_1([], Bits, Acc) ->
[{1,_}|_] ->
{bs_init_bits,cg_binary_bytes_to_bits(Sizes, [])};
[{8,_}|_] ->
- {bs_init2,[E || {8,E} <- Sizes]}
+ {bs_init2,[E || {8,E} <- Sizes]};
+ [] ->
+ {bs_init_bits,[]}
end.
cg_binary_size_2({integer,N}, U, _, Next, Bits, Acc) ->
diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl
index b2f0ac75c7..e1a593fffa 100644
--- a/lib/compiler/src/v3_core.erl
+++ b/lib/compiler/src/v3_core.erl
@@ -122,7 +122,6 @@
| iclause() | ifun() | iletrec() | imatch() | iprimop()
| iprotect() | ireceive1() | ireceive2() | iset() | itry().
--type error() :: {file:filename(), [{integer(), module(), term()}]}.
-type warning() :: {file:filename(), [{integer(), module(), term()}]}.
-record(core, {vcount=0 :: non_neg_integer(), %Variable counter
@@ -130,7 +129,6 @@
in_guard=false :: boolean(), %In guard or not.
wanted=true :: boolean(), %Result wanted or not.
opts :: [compile:option()], %Options.
- es=[] :: [error()], %Errors.
ws=[] :: [warning()], %Warnings.
file=[{file,""}]}). %File
@@ -141,46 +139,41 @@
| {attribute, integer(), attribute(), _}.
-spec module({module(), [fa()], [form()]}, [compile:option()]) ->
- {'ok',cerl:c_module(),[warning()]} | {'error',[error()],[warning()]}.
+ {'ok',cerl:c_module(),[warning()]}.
module({Mod,Exp,Forms}, Opts) ->
Cexp = map(fun ({_N,_A} = NA) -> #c_var{name=NA} end, Exp),
- {Kfs0,As0,Es,Ws,_File} = foldl(fun (F, Acc) ->
- form(F, Acc, Opts)
- end, {[],[],[],[],[]}, Forms),
+ {Kfs0,As0,Ws,_File} = foldl(fun (F, Acc) ->
+ form(F, Acc, Opts)
+ end, {[],[],[],[]}, Forms),
Kfs = reverse(Kfs0),
As = reverse(As0),
- case Es of
- [] ->
- {ok,#c_module{name=#c_literal{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws};
- _ ->
- {error,Es,Ws}
- end.
+ {ok,#c_module{name=#c_literal{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}.
-form({function,_,_,_,_}=F0, {Fs,As,Es0,Ws0,File}, Opts) ->
- {F,Es,Ws} = function(F0, Es0, Ws0, File, Opts),
- {[F|Fs],As,Es,Ws,File};
-form({attribute,_,file,{File,_Line}}, {Fs,As,Es,Ws,_}, _Opts) ->
- {Fs,As,Es,Ws,File};
-form({attribute,_,_,_}=F, {Fs,As,Es,Ws,File}, _Opts) ->
- {Fs,[attribute(F)|As],Es,Ws,File}.
+form({function,_,_,_,_}=F0, {Fs,As,Ws0,File}, Opts) ->
+ {F,Ws} = function(F0, Ws0, File, Opts),
+ {[F|Fs],As,Ws,File};
+form({attribute,_,file,{File,_Line}}, {Fs,As,Ws,_}, _Opts) ->
+ {Fs,As,Ws,File};
+form({attribute,_,_,_}=F, {Fs,As,Ws,File}, _Opts) ->
+ {Fs,[attribute(F)|As],Ws,File}.
attribute({attribute,Line,Name,Val}) ->
{#c_literal{val=Name, anno=[Line]}, #c_literal{val=Val, anno=[Line]}}.
-function({function,_,Name,Arity,Cs0}, Es0, Ws0, File, Opts) ->
+function({function,_,Name,Arity,Cs0}, Ws0, File, Opts) ->
%%ok = io:fwrite("~p - ", [{Name,Arity}]),
- St0 = #core{vcount=0,opts=Opts,es=Es0,ws=Ws0,file=[{file,File}]},
+ St0 = #core{vcount=0,opts=Opts,ws=Ws0,file=[{file,File}]},
{B0,St1} = body(Cs0, Name, Arity, St0),
%%ok = io:fwrite("1", []),
%%ok = io:fwrite("~w:~p~n", [?LINE,B0]),
{B1,St2} = ubody(B0, St1),
%%ok = io:fwrite("2", []),
%%ok = io:fwrite("~w:~p~n", [?LINE,B1]),
- {B2,#core{es=Es,ws=Ws}} = cbody(B1, St2),
+ {B2,#core{ws=Ws}} = cbody(B1, St2),
%%ok = io:fwrite("3~n", []),
%%ok = io:fwrite("~w:~p~n", [?LINE,B2]),
- {{#c_var{name={Name,Arity}},B2},Es,Ws}.
+ {{#c_var{name={Name,Arity}},B2},Ws}.
body(Cs0, Name, Arity, St0) ->
Anno = lineno_anno(element(2, hd(Cs0)), St0),
@@ -899,25 +892,22 @@ lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], Mc, St0) ->
lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) ->
{Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
{Name,St1} = new_fun_name("blc", St0),
- {Tname,St2} = new_var_name(St1),
- LA = lineno_anno(Line, St2),
+ LA = lineno_anno(Line, St1),
LAnno = #a{anno=LA},
- HeadBinPattern = pattern(P,St2),
- #c_binary{segments=Ps} = HeadBinPattern,
- {EPs,St3} = emasculate_segments(Ps,St2),
- Tail = #c_var{anno=LA,name=Tname},
- TailSegment = #c_bitstr{val=Tail,size=#c_literal{val=all},
- unit=#c_literal{val=1},
- type=#c_literal{val=binary},
- flags=#c_literal{val=[big,unsigned]}},
- Pattern = HeadBinPattern#c_binary{segments=Ps ++ [TailSegment]},
- EPattern = HeadBinPattern#c_binary{segments=EPs ++ [TailSegment]},
+ HeadBinPattern = pattern(P, St1),
+ #c_binary{segments=Ps0} = HeadBinPattern,
+ {Ps,Tail,St2} = append_tail_segment(Ps0, St1),
+ {EPs,St3} = emasculate_segments(Ps, St2),
+ Pattern = HeadBinPattern#c_binary{segments=Ps},
+ EPattern = HeadBinPattern#c_binary{segments=EPs},
{Arg,St4} = new_var(St3),
{Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
+ Tname = Tail#c_var.name,
{Nc,[],St6} = expr({call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]}, St5),
{Bc,Bps,St7} = lc_tq(Line, E, Qs1, Nc, St6),
{Gc,Gps,St10} = safe(G, St7), %Will be a function argument!
Fc = function_clause([Arg], LA, {Name,1}),
+ {TailSegList,_,St} = append_tail_segment([], St10),
Cs = [#iclause{anno=#a{anno=[compiler_generated|LA]},
pats=[Pattern],
guard=Guardc,
@@ -929,14 +919,14 @@ lc_tq(Line, E, [{b_generate,Lg,P,G}|Qs0], Mc, St0) ->
op=#c_var{anno=LA,name={Name,1}},
args=[Tail]}]},
#iclause{anno=LAnno,
- pats=[#c_binary{anno=LA, segments=[TailSegment]}],guard=[],
+ pats=[#c_binary{anno=LA,segments=TailSegList}],guard=[],
body=[Mc]}],
Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
{#iletrec{anno=LAnno,defs=[{{Name,1},Fun}],
body=Gps ++ [#iapply{anno=LAnno,
op=#c_var{anno=LA,name={Name,1}},
args=[Gc]}]},
- [],St10};
+ [],St};
lc_tq(Line, E, [Fil0|Qs0], Mc, St0) ->
%% Special case sequences guard tests.
LA = lineno_anno(Line, St0),
@@ -1044,26 +1034,24 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) ->
{Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
{Name,St1} = new_fun_name("lbc", St0),
LA = lineno_anno(Line, St1),
- {[Tail,AccVar],St2} = new_vars(LA, 2, St1),
+ {AccVar,St2} = new_var(LA, St1),
LAnno = #a{anno=LA},
HeadBinPattern = pattern(P, St2),
- #c_binary{segments=Ps} = HeadBinPattern,
- {EPs,St3} = emasculate_segments(Ps, St2),
- TailSegment = #c_bitstr{val=Tail,size=#c_literal{val=all},
- unit=#c_literal{val=1},
- type=#c_literal{val=binary},
- flags=#c_literal{val=[big,unsigned]}},
- Pattern = HeadBinPattern#c_binary{segments=Ps ++ [TailSegment]},
- EPattern = HeadBinPattern#c_binary{segments=EPs ++ [TailSegment]},
- {Arg,St4} = new_var(St3),
+ #c_binary{segments=Ps0} = HeadBinPattern,
+ {Ps,Tail,St3} = append_tail_segment(Ps0, St2),
+ {EPs,St4} = emasculate_segments(Ps, St3),
+ Pattern = HeadBinPattern#c_binary{segments=Ps},
+ EPattern = HeadBinPattern#c_binary{segments=EPs},
+ {Arg,St5} = new_var(St4),
NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tail#c_var.name},
{var,Lg,AccVar#c_var.name}]},
- {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
- {Bc,Bps,St6} = bc_tq1(Line, E, Qs1, AccVar, St5),
- {Nc,Nps,St7} = expr(NewMore, St6),
- {Gc,Gps,St8} = safe(G, St7), %Will be a function argument!
+ {Guardc,St6} = lc_guard_tests(Gs, St5), %These are always flat!
+ {Bc,Bps,St7} = bc_tq1(Line, E, Qs1, AccVar, St6),
+ {Nc,Nps,St8} = expr(NewMore, St7),
+ {Gc,Gps,St9} = safe(G, St8), %Will be a function argument!
Fc = function_clause([Arg,AccVar], LA, {Name,2}),
Body = Bps ++ Nps ++ [#iset{var=AccVar,arg=Bc},Nc],
+ {TailSegList,_,St} = append_tail_segment([], St9),
Cs = [#iclause{anno=LAnno,
pats=[Pattern,AccVar],
guard=Guardc,
@@ -1073,7 +1061,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) ->
guard=[],
body=Nps ++ [Nc]},
#iclause{anno=LAnno,
- pats=[#c_binary{anno=LA,segments=[TailSegment]},AccVar],
+ pats=[#c_binary{anno=LA,segments=TailSegList},AccVar],
guard=[],
body=[AccVar]}],
Fun = #ifun{anno=LAnno,id=[],vars=[Arg,AccVar],clauses=Cs,fc=Fc},
@@ -1081,7 +1069,7 @@ bc_tq1(Line, E, [{b_generate,Lg,P,G}|Qs0], AccExpr, St0) ->
body=Gps ++ [#iapply{anno=LAnno,
op=#c_var{anno=LA,name={Name,2}},
args=[Gc,AccExpr]}]},
- [],St8};
+ [],St};
bc_tq1(Line, E, [Fil0|Qs0], AccVar, St0) ->
%% Special case sequences guard tests.
LA = lineno_anno(Line, St0),
@@ -1127,6 +1115,29 @@ bc_tq1(_, {bin,Bl,Elements}, [], AccVar, St0) ->
%%Anno = Anno0#a{anno=[compiler_generated|A]},
{set_anno(E, Anno),Pre,St}.
+append_tail_segment(Segs, St) ->
+ app_tail_seg(Segs, St, []).
+
+app_tail_seg([#c_bitstr{val=Var0,size=#c_literal{val=all}}=Seg0]=L,
+ St0, Acc) ->
+ case Var0 of
+ #c_var{name='_'} ->
+ {Var,St} = new_var(St0),
+ Seg = Seg0#c_bitstr{val=Var},
+ {reverse(Acc, [Seg]),Var,St};
+ #c_var{} ->
+ {reverse(Acc, L),Var0,St0}
+ end;
+app_tail_seg([H|T], St, Acc) ->
+ app_tail_seg(T, St, [H|Acc]);
+app_tail_seg([], St0, Acc) ->
+ {Var,St} = new_var(St0),
+ Tail = #c_bitstr{val=Var,size=#c_literal{val=all},
+ unit=#c_literal{val=1},
+ type=#c_literal{val=binary},
+ flags=#c_literal{val=[unsigned,big]}},
+ {reverse(Acc, [Tail]),Var,St}.
+
emasculate_segments(Segs, St) ->
emasculate_segments(Segs, St, []).
@@ -1809,7 +1820,21 @@ upattern_list([], _, St) -> {[],[],[],[],St}.
%% upat_bin([Pat], [KnownVar], State) ->
%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
upat_bin(Es0, Ks, St0) ->
- upat_bin(Es0, Ks, [], St0).
+ {Es1,Pg,Pv,Pu0,St1} = upat_bin(Es0, Ks, [], St0),
+
+ %% In a clause such as <<Sz:8,V:Sz>> in a function head, Sz will both
+ %% be new and used; a situation that is not handled properly by
+ %% uclause/4. (Basically, since Sz occurs in two sets that are
+ %% subtracted from each other, Sz will not be added to the list of
+ %% known variables and will seem to be new the next time it is
+ %% used in a match.)
+ %% Since the variable Sz really is new (it does not use a
+ %% value bound prior to the binary matching), Sz should only be
+ %% included in the set of new variables. Thus we should take it
+ %% out of the set of used variables.
+
+ Pu1 = subtract(Pu0, intersection(Pv, Pu0)),
+ {Es1,Pg,Pv,Pu1,St1}.
%% upat_bin([Pat], [KnownVar], [LocalVar], State) ->
%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
@@ -1821,35 +1846,36 @@ upat_bin([], _, _, St) -> {[],[],[],[],St}.
%% upat_element(Segment, [KnownVar], [LocalVar], State) ->
-%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
-upat_element(#c_bitstr{val=H0,size=Sz}=Seg, Ks, Bs, St0) ->
- {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
- Bs1 = case H0 of
- #c_var{name=Hname} ->
- case H1 of
+%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
+upat_element(#c_bitstr{val=H0,size=Sz0}=Seg, Ks, Bs0, St0) ->
+ {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
+ Bs1 = case H0 of
#c_var{name=Hname} ->
- Bs;
- #c_var{name=Other} ->
- [{Hname, Other}|Bs]
- end;
- _ ->
- Bs
- end,
- {Sz1, Us} = case Sz of
- #c_var{name=Vname} ->
- rename_bitstr_size(Vname, Bs);
- _Other -> {Sz, []}
- end,
- {Seg#c_bitstr{val=H1, size=Sz1},Hg,Hv,Us,Bs1,St1}.
-
-rename_bitstr_size(V, [{V, N}|_]) ->
- New = #c_var{name=N},
- {New, [N]};
+ case H1 of
+ #c_var{name=Hname} ->
+ Bs0;
+ #c_var{name=Other} ->
+ [{Hname,Other}|Bs0]
+ end;
+ _ ->
+ Bs0
+ end,
+ {Sz1,Us} = case Sz0 of
+ #c_var{name=Vname} ->
+ rename_bitstr_size(Vname, Bs0);
+ _Other ->
+ {Sz0,[]}
+ end,
+ {Seg#c_bitstr{val=H1,size=Sz1},Hg,Hv,Us,Bs1,St1}.
+
+rename_bitstr_size(V, [{V,N}|_]) ->
+ New = #c_var{name=N},
+ {New,[N]};
rename_bitstr_size(V, [_|Rest]) ->
- rename_bitstr_size(V, Rest);
+ rename_bitstr_size(V, Rest);
rename_bitstr_size(V, []) ->
- Old = #c_var{name=V},
- {Old, [V]}.
+ Old = #c_var{name=V},
+ {Old,[V]}.
used_in_any(Les) ->
foldl(fun (Le, Ns) -> union((get_anno(Le))#a.us, Ns) end,
@@ -2096,20 +2122,12 @@ is_simple(#c_literal{}) -> true;
is_simple(#c_cons{hd=H,tl=T}) ->
is_simple(H) andalso is_simple(T);
is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
-is_simple(#c_binary{segments=Es}) -> is_simp_bin(Es);
is_simple(_) -> false.
-spec is_simple_list([cerl:cerl()]) -> boolean().
is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
--spec is_simp_bin([cerl:cerl()]) -> boolean().
-
-is_simp_bin(Es) ->
- lists:all(fun (#c_bitstr{val=E,size=S}) ->
- is_simple(E) andalso is_simple(S)
- end, Es).
-
%%%
%%% Handling of warnings.
%%%
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index fbe4d8617e..3b33a08cf7 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -147,6 +147,7 @@ attributes([]) -> [].
include_attribute(type) -> false;
include_attribute(spec) -> false;
include_attribute(opaque) -> false;
+include_attribute(export_type) -> false;
include_attribute(_) -> true.
function({#c_var{name={F,Arity}=FA},Body}, St0) ->
diff --git a/lib/compiler/src/v3_kernel_pp.erl b/lib/compiler/src/v3_kernel_pp.erl
index a300dd283f..e363a5387a 100644
--- a/lib/compiler/src/v3_kernel_pp.erl
+++ b/lib/compiler/src/v3_kernel_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,10 +20,12 @@
-module(v3_kernel_pp).
--include("v3_kernel.hrl").
-
-export([format/1]).
+%%-define(INCLUDE_ANNOTATIONS, 1).
+
+-include("v3_kernel.hrl").
+
%% These are "internal" structures in sys_kernel which are here for
%% debugging purposes.
-record(iset, {anno=[],vars,arg,body}).
@@ -50,28 +52,33 @@ format(Node) -> format(Node, #ctxt{}).
format(Node, Ctxt) ->
case canno(Node) of
-%% [] ->
-%% format_1(Node, Ctxt);
-%% [L,{file,_}] when is_integer(L) ->
-%% format_1(Node, Ctxt);
-%% #k{a=Anno}=K when Anno =/= [] ->
-%% format(setelement(2, Node, K#k{a=[]}), Ctxt);
-%% List ->
-%% format_anno(List, Ctxt, fun (Ctxt1) ->
-%% format_1(Node, Ctxt1)
-%% end);
- _ ->
- format_1(Node, Ctxt)
+ [] ->
+ format_1(Node, Ctxt);
+ [L,{file,_}] when is_integer(L) ->
+ format_1(Node, Ctxt);
+ #k{a=Anno}=K when Anno =/= [] ->
+ format(setelement(2, Node, K#k{a=[]}), Ctxt);
+ List ->
+ format_anno(List, Ctxt, fun (Ctxt1) ->
+ format_1(Node, Ctxt1)
+ end)
end.
-%% format_anno(Anno, Ctxt0, ObjFun) ->
-%% Ctxt1 = ctxt_bump_indent(Ctxt0, 1),
-%% ["( ",
-%% ObjFun(Ctxt0),
-%% nl_indent(Ctxt1),
-%% "-| ",io_lib:write(Anno),
-%% " )"].
-
+
+-ifndef(INCLUDE_ANNOTATIONS).
+%% Don't include annotations (for readability).
+format_anno(_Anno, Ctxt, ObjFun) ->
+ ObjFun(Ctxt).
+-else.
+%% Include annotations (for debugging of annotations).
+format_anno(Anno, Ctxt0, ObjFun) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt0, 1),
+ ["( ",
+ ObjFun(Ctxt0),
+ nl_indent(Ctxt1),
+ "-| ",io_lib:write(Anno),
+ " )"].
+-endif.
%% format_1(Kexpr, Context) -> string().
@@ -107,6 +114,8 @@ format_1(#k_bin_int{size=Sz,unit=U,flags=Fs,val=Val,next=Next}, Ctxt) ->
[format_bin_seg_1(S, Ctxt),
format_bin_seg(Next, ctxt_bump_indent(Ctxt, 2))];
format_1(#k_bin_end{}, _Ctxt) -> "#<>#";
+format_1(#k_literal{val=Term}, _Ctxt) ->
+ io_lib:format("~p", [Term]);
format_1(#k_local{name=N,arity=A}, Ctxt) ->
"local " ++ format_fa_pair({N,A}, Ctxt);
format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) ->
diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl
index 9fda37530b..a7a4d4dc91 100644
--- a/lib/compiler/src/v3_life.erl
+++ b/lib/compiler/src/v3_life.erl
@@ -361,8 +361,6 @@ match_fail(#k_literal{anno=Anno,val={Atom,Val}}, I, A) when is_atom(Atom) ->
match_fail(#k_tuple{anno=Anno,es=[#k_atom{val=Atom},#k_literal{val=Val}]}, I, A);
match_fail(#k_literal{anno=Anno,val={Atom}}, I, A) when is_atom(Atom) ->
match_fail(#k_tuple{anno=Anno,es=[#k_atom{val=Atom}]}, I, A);
-match_fail(#k_literal{anno=Anno,val=Atom}, I, A) when is_atom(Atom) ->
- match_fail(#k_atom{anno=Anno,val=Atom}, I, A);
match_fail(#k_tuple{es=[#k_atom{val=function_clause}|As]}, I, A) ->
#l{ke={match_fail,{function_clause,literal_list(As, [])}},i=I,a=A};
match_fail(#k_tuple{es=[#k_atom{val=badmatch},Val]}, I, A) ->
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 2d08e71e09..fe713fd019 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -153,11 +153,11 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) compiler.dynspec compiler.cover \
+ $(INSTALL_DATA) compiler.spec compiler.cover \
$(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \
$(INLINE_ERL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/compiler/test/andor_SUITE.erl b/lib/compiler/test/andor_SUITE.erl
index a460d54239..cab22e03d0 100644
--- a/lib/compiler/test/andor_SUITE.erl
+++ b/lib/compiler/test/andor_SUITE.erl
@@ -1,33 +1,52 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(andor_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
t_case/1,t_and_or/1,t_andalso/1,t_orelse/1,inside/1,overlap/1,
combined/1,in_case/1,before_and_inside_if/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(andor_SUITE),
+ [t_case, t_and_or, t_andalso, t_orelse, inside, overlap,
+ combined, in_case, before_and_inside_if].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [t_case,t_and_or,t_andalso,t_orelse,inside,overlap,combined,in_case,
- before_and_inside_if].
t_case(Config) when is_list(Config) ->
%% We test boolean cases almost but not quite like cases
@@ -141,6 +160,10 @@ t_and_or(Config) when is_list(Config) ->
ok.
+-define(GUARD(E), if E -> true;
+ true -> false
+ end).
+
t_andalso(Config) when is_list(Config) ->
Bs = [true,false],
Ps = [{X,Y} || X <- Bs, Y <- Bs],
@@ -151,6 +174,11 @@ t_andalso(Config) when is_list(Config) ->
?line false = false andalso true,
?line false = false andalso false,
+ ?line true = ?GUARD(true andalso true),
+ ?line false = ?GUARD(true andalso false),
+ ?line false = ?GUARD(false andalso true),
+ ?line false = ?GUARD(false andalso false),
+
?line false = false andalso glurf,
?line false = false andalso exit(exit_now),
@@ -176,6 +204,11 @@ t_orelse(Config) when is_list(Config) ->
?line true = false orelse true,
?line false = false orelse false,
+ ?line true = ?GUARD(true orelse true),
+ ?line true = ?GUARD(true orelse false),
+ ?line true = ?GUARD(false orelse true),
+ ?line false = ?GUARD(false orelse false),
+
?line true = true orelse glurf,
?line true = true orelse exit(exit_now),
diff --git a/lib/compiler/test/apply_SUITE.erl b/lib/compiler/test/apply_SUITE.erl
index f23dd6c2db..c517c4465e 100644
--- a/lib/compiler/test/apply_SUITE.erl
+++ b/lib/compiler/test/apply_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,15 +18,34 @@
%%
-module(apply_SUITE).
--export([all/1,mfa/1,fun_apply/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,mfa/1,fun_apply/1]).
-export([foo/0,bar/1,baz/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(apply_SUITE),
+ [mfa, fun_apply].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [mfa,fun_apply].
-define(APPLY0(M, F), (fun(Res) -> Res = M:F() end)(apply(M, F, []))).
-define(APPLY1(M, F, A1), (fun(Res) -> Res = M:F(A1) end)(apply(M, F, [A1]))).
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 74b5d7c7eb..fc88ebeb41 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,9 @@
%%
-module(beam_validator_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
beam_files/1,compiler_bug/1,stupid_but_valid/1,
xrange/1,yrange/1,stack/1,call_last/1,merge_undefined/1,
uninit/1,unsafe_catch/1,
@@ -30,34 +32,49 @@
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(10)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [beam_files,compiler_bug,stupid_but_valid,
- xrange,yrange,stack,call_last,merge_undefined,
- uninit,unsafe_catch,
- dead_code,mult_labels,
- overwrite_catchtag,overwrite_trytag,accessing_tags,bad_catch_try,
- cons_guard,
- freg_range,freg_uninit,freg_state,
- bin_match,bin_aligned,
- bad_dsetel,state_after_fault_in_catch,no_exception_in_catch,
- undef_label,illegal_instruction,failing_gc_guard_bif].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(beam_validator_SUITE),
+ [beam_files, compiler_bug, stupid_but_valid, xrange,
+ yrange, stack, call_last, merge_undefined, uninit,
+ unsafe_catch, dead_code, mult_labels,
+ overwrite_catchtag, overwrite_trytag, accessing_tags,
+ bad_catch_try, cons_guard, freg_range, freg_uninit,
+ freg_state, bin_match, bin_aligned, bad_dsetel,
+ state_after_fault_in_catch, no_exception_in_catch,
+ undef_label, illegal_instruction, failing_gc_guard_bif].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
beam_files(Config) when is_list(Config) ->
- ?line {ok,Cwd} = file:get_cwd(),
- ?line Parent = filename:dirname(Cwd),
- ?line Wc = filename:join([Parent,"*","*.beam"]),
+ ?line DataDir = proplists:get_value(data_dir, Config),
+ ?line Wc = filename:join([DataDir,"..","..","*","*.beam"]),
%% Must have at least two files here, or there will be
%% a grammatical error in the output of the io:format/2 call below. ;-)
?line [_,_|_] = Fs = filelib:wildcard(Wc),
diff --git a/lib/compiler/test/bs_bincomp_SUITE.erl b/lib/compiler/test/bs_bincomp_SUITE.erl
index a64a5d590b..30c04f80cf 100644
--- a/lib/compiler/test/bs_bincomp_SUITE.erl
+++ b/lib/compiler/test/bs_bincomp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,19 +21,36 @@
-module(bs_bincomp_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
byte_aligned/1,bit_aligned/1,extended_byte_aligned/1,
extended_bit_aligned/1,mixed/1,filters/1,trim_coverage/1,
- nomatch/1,sizes/1]).
+ nomatch/1,sizes/1,tail/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-all(suite) ->
- test_lib:recompile(?MODULE),
- [byte_aligned,bit_aligned,extended_byte_aligned,
- extended_bit_aligned,mixed,filters,trim_coverage,
- nomatch,sizes].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+all() ->
+ test_lib:recompile(bs_bincomp_SUITE),
+ [byte_aligned, bit_aligned, extended_byte_aligned,
+ extended_bit_aligned, mixed, filters, trim_coverage,
+ nomatch, sizes, tail].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
byte_aligned(Config) when is_list(Config) ->
cs_init(),
@@ -270,6 +287,38 @@ sizes(Config) when is_list(Config) ->
?line cs_end(),
ok.
+tail(Config) when is_list(Config) ->
+ ?line [] = tail_1(<<0:7>>),
+ ?line [0] = tail_1(<<0>>),
+ ?line [0] = tail_1(<<0:12>>),
+ ?line [0,0] = tail_1(<<0:20>>),
+
+ ?line [] = tail_2(<<0:7>>),
+ ?line [42] = tail_2(<<0>>),
+ ?line [] = tail_2(<<0:12>>),
+ ?line [42,42] = tail_2(<<0,1>>),
+
+ ?line <<>> = tail_3(<<0:7>>),
+ ?line <<42>> = tail_3(<<0>>),
+ ?line <<42>> = tail_3(<<0:12>>),
+ ?line <<42,42>> = tail_3(<<0:20>>),
+
+ ?line [] = tail_4(<<0:15>>),
+ ?line [7] = tail_4(<<7,8>>),
+ ?line [9] = tail_4(<<9,17:12>>),
+ ok.
+
+tail_1(Bits) ->
+ [X || <<X:8/integer, _/bits>> <= Bits].
+
+tail_2(Bits) ->
+ [42 || <<_:8/integer, _/bytes>> <= Bits].
+
+tail_3(Bits) ->
+ << <<42>> || <<_:8/integer, _/bits>> <= Bits >>.
+
+tail_4(Bits) ->
+ [X || <<X:8/integer, Tail/bits>> <= Bits, bit_size(Tail) >= 8].
cs_init() ->
diff --git a/lib/compiler/test/bs_bit_binaries_SUITE.erl b/lib/compiler/test/bs_bit_binaries_SUITE.erl
index 6337460b13..8be0c4196a 100644
--- a/lib/compiler/test/bs_bit_binaries_SUITE.erl
+++ b/lib/compiler/test/bs_bit_binaries_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,19 +21,39 @@
-module(bs_bit_binaries_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
misc/1,horrid_match/1,test_bitstr/1,test_bit_size/1,asymmetric_tests/1,
big_asymmetric_tests/1,binary_to_and_from_list/1,
big_binary_to_and_from_list/1,send_and_receive/1,
send_and_receive_alot/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(bs_bit_binaries_SUITE),
+ [misc, horrid_match, test_bitstr, test_bit_size,
+ asymmetric_tests, big_asymmetric_tests,
+ binary_to_and_from_list, big_binary_to_and_from_list,
+ send_and_receive, send_and_receive_alot].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [misc,horrid_match,test_bitstr,test_bit_size,asymmetric_tests,
- big_asymmetric_tests,binary_to_and_from_list,big_binary_to_and_from_list,
- send_and_receive,send_and_receive_alot].
misc(Config) when is_list(Config) ->
?line <<1:100>> = <<1:100>>,
diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl
index 1862a28bbe..c430b12b70 100644
--- a/lib/compiler/test/bs_construct_SUITE.erl
+++ b/lib/compiler/test/bs_construct_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,23 +23,44 @@
-module(bs_construct_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
two/1,test1/1,fail/1,float_bin/1,in_guard/1,in_catch/1,
nasty_literals/1,coerce_to_float/1,side_effect/1,
opt/1,otp_7556/1,float_arith/1,otp_8054/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(bs_construct_SUITE),
+ [two, test1, fail, float_bin, in_guard, in_catch,
+ nasty_literals, side_effect, opt, otp_7556, float_arith,
+ otp_8054].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [two,test1,fail,float_bin,in_guard,in_catch,nasty_literals,
- side_effect,opt,otp_7556,float_arith,otp_8054].
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
@@ -66,6 +87,8 @@ id(I) -> I.
l(I_13, I_big1, I_16, Bin) ->
[
+ ?T(<<I_13:0>>,
+ []),
?T(<<-43>>,
[256-43]),
?T(<<4:4,7:4>>,
@@ -208,7 +231,7 @@ one_test({C_bin, E_bin, Str, Result}) ->
ok;
%% For situations where the final bits may not matter, like
%% for floats:
- N when integer(N) ->
+ N when is_integer(N) ->
io:format("Info: compiled and interpreted differ in the"
" last bytes:~n ~p, ~p.~n",
[bitstring_to_list(C_bin), bitstring_to_list(E_bin)]),
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 75b6f801e7..9184e14cb2 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,9 @@
-module(bs_match_SUITE).
-compile(nowarn_shadow_vars).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
fun_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
bin_tail/1,save_restore/1,shadowed_size_var/1,
partitioned_bs_match/1,function_clause/1,
@@ -30,28 +32,50 @@
multiple_uses/1,zero_label/1,followed_by_catch/1,
matching_meets_construction/1,simon/1,matching_and_andalso/1,
otp_7188/1,otp_7233/1,otp_7240/1,otp_7498/1,
- match_string/1,zero_width/1,bad_size/1,haystack/1]).
+ match_string/1,zero_width/1,bad_size/1,haystack/1,
+ cover_beam_bool/1]).
-export([coverage_id/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-all(suite) ->
- test_lib:recompile(?MODULE),
- [fun_shadow,int_float,otp_5269,null_fields,wiger,bin_tail,save_restore,
- shadowed_size_var,partitioned_bs_match,function_clause,unit,
- shared_sub_bins,bin_and_float,dec_subidentifiers,skip_optional_tag,
- wfbm,degenerated_match,bs_sum,coverage,multiple_uses,zero_label,
- followed_by_catch,matching_meets_construction,simon,matching_and_andalso,
- otp_7188,otp_7233,otp_7240,otp_7498,match_string,zero_width,bad_size,
- haystack].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(bs_match_SUITE),
+ [fun_shadow, int_float, otp_5269, null_fields, wiger,
+ bin_tail, save_restore, shadowed_size_var,
+ partitioned_bs_match, function_clause, unit,
+ shared_sub_bins, bin_and_float, dec_subidentifiers,
+ skip_optional_tag, wfbm, degenerated_match, bs_sum,
+ coverage, multiple_uses, zero_label, followed_by_catch,
+ matching_meets_construction, simon,
+ matching_and_andalso, otp_7188, otp_7233, otp_7240,
+ otp_7498, match_string, zero_width, bad_size, haystack,
+ cover_beam_bool].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
@@ -118,7 +142,14 @@ otp_5269(Config) when is_list(Config) ->
[X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end,
%% "binsize variable" ^
[1,2]),
-
+ ?line check(fun() ->
+ (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) ->
+ case A of
+ B -> wrong;
+ _ -> ok
+ end
+ end)(<<1,2,3,4>>) end,
+ ok),
ok.
null_fields(Config) when is_list(Config) ->
@@ -985,6 +1016,25 @@ fc(_, Args, {'EXIT',{{case_clause,ActualArgs},_}})
when ?MODULE =:= bs_match_inline_SUITE ->
Args = tuple_to_list(ActualArgs).
+%% Cover the clause handling bs_context to binary in
+%% beam_block:initialized_regs/2.
+cover_beam_bool(Config) when is_list(Config) ->
+ ?line ok = do_cover_beam_bool(<<>>, 3),
+ ?line <<19>> = do_cover_beam_bool(<<19>>, 2),
+ ?line <<42>> = do_cover_beam_bool(<<42>>, 1),
+ ?line <<17>> = do_cover_beam_bool(<<13,17>>, 0),
+ ok.
+
+do_cover_beam_bool(Bin, X) when X > 0 ->
+ if
+ X =:= 1; X =:= 2 ->
+ Bin;
+ true ->
+ ok
+ end;
+do_cover_beam_bool(<<_,Bin/binary>>, X) ->
+ do_cover_beam_bool(Bin, X+1).
+
check(F, R) ->
R = F().
diff --git a/lib/compiler/test/bs_utf_SUITE.erl b/lib/compiler/test/bs_utf_SUITE.erl
index 4281874a24..d37943ce3a 100644
--- a/lib/compiler/test/bs_utf_SUITE.erl
+++ b/lib/compiler/test/bs_utf_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,18 +19,37 @@
-module(bs_utf_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
utf8_roundtrip/1,unused_utf_char/1,utf16_roundtrip/1,
utf32_roundtrip/1,guard/1,extreme_tripping/1,
literals/1,coverage/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(bs_utf_SUITE),
+ [utf8_roundtrip, unused_utf_char, utf16_roundtrip,
+ utf32_roundtrip, guard, extreme_tripping, literals,
+ coverage].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [utf8_roundtrip,unused_utf_char,utf16_roundtrip,
- utf32_roundtrip,guard,extreme_tripping,
- literals,coverage].
utf8_roundtrip(Config) when is_list(Config) ->
?line [utf8_roundtrip_1(P) || P <- utf_data()],
diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl
index d4843c9eba..ba225b66d0 100644
--- a/lib/compiler/test/compilation_SUITE.erl
+++ b/lib/compiler/test/compilation_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,34 +20,46 @@
-module(compilation_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-compile(export_all).
-all(suite) ->
- test_lib:recompile(?MODULE),
- [self_compile_old_inliner,self_compile,
- compiler_1,compiler_3,compiler_5,
- beam_compiler_1, beam_compiler_2, beam_compiler_3,
- beam_compiler_4, beam_compiler_5, beam_compiler_6,
- beam_compiler_7, beam_compiler_8, beam_compiler_9,
- beam_compiler_10, beam_compiler_11, beam_compiler_12,
- nested_tuples_in_case_expr,
- otp_2330, guards, vsn,
- otp_2380, otp_2141, otp_2173, otp_4790,
- const_list_256,
- bin_syntax_1, bin_syntax_2, bin_syntax_3,
- bin_syntax_4, bin_syntax_5, bin_syntax_6,
- live_var, convopts,
- bad_functional_value,
- catch_in_catch, redundant_case, long_string,
- otp_5076, complex_guard, otp_5092, otp_5151,
- otp_5235,otp_5244,
- trycatch_4, opt_crash,
- otp_5404,otp_5436,otp_5481,otp_5553,otp_5632,
- otp_5714,otp_5872,otp_6121,otp_6121a,otp_6121b,
- otp_7202,otp_7345,on_load
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(compilation_SUITE),
+ [self_compile_old_inliner, self_compile, compiler_1,
+ compiler_3, compiler_5, beam_compiler_1,
+ beam_compiler_2, beam_compiler_3, beam_compiler_4,
+ beam_compiler_5, beam_compiler_6, beam_compiler_7,
+ beam_compiler_8, beam_compiler_9, beam_compiler_10,
+ beam_compiler_11, beam_compiler_12,
+ nested_tuples_in_case_expr, otp_2330, guards,
+ {group, vsn}, otp_2380, otp_2141, otp_2173, otp_4790,
+ const_list_256, bin_syntax_1, bin_syntax_2,
+ bin_syntax_3, bin_syntax_4, bin_syntax_5, bin_syntax_6,
+ live_var, convopts, bad_functional_value,
+ catch_in_catch, redundant_case, long_string, otp_5076,
+ complex_guard, otp_5092, otp_5151, otp_5235, otp_5244,
+ trycatch_4, opt_crash, otp_5404, otp_5436, otp_5481,
+ otp_5553, otp_5632, otp_5714, otp_5872, otp_6121,
+ otp_6121a, otp_6121b, otp_7202, otp_7345, on_load,
+ string_table,otp_8949_a,otp_8949_a].
+
+groups() ->
+ [{vsn, [], [vsn_1, vsn_2, vsn_3]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-define(comp(N),
N(Config) when is_list(Config) -> try_it(N, Config)).
@@ -151,7 +163,7 @@ split({int, N}, <<N:16,B:N/binary,T/binary>>) ->
beam_compiler_7(doc) ->
"Code snippet submitted from Ulf Wiger which fails in R3 Beam.";
beam_compiler_7(suite) -> [];
-beam_compiler_7(Config) when list(Config) ->
+beam_compiler_7(Config) when is_list(Config) ->
?line done = empty(2, false).
empty(N, Toggle) when N > 0 ->
@@ -311,12 +323,11 @@ from(H, [H | T]) -> T;
from(H, [_ | T]) -> from(H, T);
from(_, []) -> [].
-vsn(suite) -> [vsn_1, vsn_2, vsn_3].
vsn_1(doc) ->
"Test generation of 'vsn' attribute";
vsn_1(suite) -> [];
-vsn_1(Conf) when list(Conf) ->
+vsn_1(Conf) when is_list(Conf) ->
?line M = vsn_1,
?line compile_load(M, ?config(data_dir, Conf), Conf),
@@ -340,7 +351,7 @@ vsn_1(Conf) when list(Conf) ->
vsn_2(doc) ->
"Test overriding of generation of 'vsn' attribute";
vsn_2(suite) -> [];
-vsn_2(Conf) when list(Conf) ->
+vsn_2(Conf) when is_list(Conf) ->
?line M = vsn_2,
?line compile_load(M, ?config(data_dir, Conf), Conf),
@@ -356,7 +367,7 @@ vsn_2(Conf) when list(Conf) ->
vsn_3(doc) ->
"Test that different code yields different generated 'vsn'";
vsn_3(suite) -> [];
-vsn_3(Conf) when list(Conf) ->
+vsn_3(Conf) when is_list(Conf) ->
?line M = vsn_3,
?line compile_load(M, ?config(data_dir, Conf), Conf),
@@ -596,4 +607,55 @@ otp_7345(ObjRef, _RdEnv, Args) ->
10},
id(LlUnitdataReq).
+%% Check the generation of the string table.
+
+string_table(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line File = filename:join(DataDir, "string_table.erl"),
+ ?line {ok,string_table,Beam,[]} = compile:file(File, [return, binary]),
+ ?line {ok,{string_table,[StringTableChunk]}} = beam_lib:chunks(Beam, ["StrT"]),
+ ?line {"StrT", <<"stringabletringtable">>} = StringTableChunk,
+ ok.
+
+otp_8949_a(Config) when is_list(Config) ->
+ value = otp_8949_a(),
+ ok.
+
+-record(cs, {exs,keys = [],flags = 1}).
+-record(exs, {children = []}).
+
+otp_8949_a() ->
+ case id([#cs{}]) of
+ [#cs{}=Cs] ->
+ SomeVar = id(value),
+ if
+ Cs#cs.flags band 1 =/= 0 ->
+ id(SomeVar);
+ (((Cs#cs.exs)#exs.children /= [])
+ and
+ (Cs#cs.flags band (1 bsl 0 bor (1 bsl 22)) == 0));
+ Cs#cs.flags band (1 bsl 22) =/= 0 ->
+ ok
+ end
+ end.
+
+otp_8949_b(Config) when is_list(Config) ->
+ self() ! something,
+ ?line value = otp_8949_b([], false),
+ ?line {'EXIT',_} = (catch otp_8949_b([], true)),
+ ok.
+
+%% Would cause an endless loop in beam_utils.
+otp_8949_b(A, B) ->
+ Var = id(value),
+ if
+ A == [], B == false ->
+ ok
+ end,
+ receive
+ something ->
+ id(Var)
+ end.
+
+
id(I) -> I.
diff --git a/lib/compiler/test/compilation_SUITE_data/string_table.erl b/lib/compiler/test/compilation_SUITE_data/string_table.erl
new file mode 100644
index 0000000000..1da1d015dd
--- /dev/null
+++ b/lib/compiler/test/compilation_SUITE_data/string_table.erl
@@ -0,0 +1,8 @@
+-module(string_table).
+-export([f/1, g/1]).
+
+f(<<"string">>) -> string;
+f(<<"stringtable">>) -> stringtable.
+
+g(<<"stringtable">>) -> stringtable;
+g(<<"table">>) -> table.
diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl
index e1cc5dafb5..037c078fd0 100644
--- a/lib/compiler/test/compile_SUITE.erl
+++ b/lib/compiler/test/compile_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,33 +20,50 @@
%% Tests compile:file/1 and compile:file/2 with various options.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
app_test/1,
file_1/1, module_mismatch/1, big_file/1, outdir/1,
- binary/1, cond_and_ifdef/1, listings/1, listings_big/1,
+ binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1,
other_output/1, package_forms/1, encrypted_abstr/1,
- bad_record_use/1, bad_record_use1/1, bad_record_use2/1, strict_record/1,
+ bad_record_use1/1, bad_record_use2/1, strict_record/1,
missing_testheap/1, cover/1, env/1, core/1, asm/1]).
-export([init/3]).
+suite() -> [{ct_hooks,[ts_install_cth]}].
%% To cover the stripping of 'type' and 'spec' in beam_asm.
-type all_return_type() :: [atom()].
--spec all('suite' | [_]) -> all_return_type().
-
-all(suite) ->
- test_lib:recompile(?MODULE),
- [app_test,
- file_1, module_mismatch, big_file, outdir, binary,
- cond_and_ifdef, listings, listings_big,
- other_output, package_forms,
- encrypted_abstr,
- bad_record_use, strict_record,
+-spec all() -> all_return_type().
+
+all() ->
+ test_lib:recompile(compile_SUITE),
+ [app_test, file_1, module_mismatch, big_file, outdir,
+ binary, makedep, cond_and_ifdef, listings, listings_big,
+ other_output, package_forms, encrypted_abstr,
+ {group, bad_record_use}, strict_record,
missing_testheap, cover, env, core, asm].
+groups() ->
+ [{bad_record_use, [],
+ [bad_record_use1, bad_record_use2]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%% Test that the Application file has no `basic' errors.";
app_test(Config) when is_list(Config) ->
@@ -132,6 +149,76 @@ binary(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+%% Tests that the dependencies-Makefile-related options work.
+
+makedep(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(60)),
+ ?line {Simple,Target} = files(Config, "makedep"),
+ ?line DataDir = ?config(data_dir, Config),
+ ?line SimpleRootname = filename:rootname(Simple),
+ ?line IncludeDir = filename:join(filename:dirname(Simple), "include"),
+ ?line IncludeOptions = [
+ {d,need_foo},
+ {d,foo_value,42},
+ {d,include_generated},
+ {i,IncludeDir}
+ ],
+ %% Basic rule.
+ ?line BasicMf1Name = SimpleRootname ++ "-basic1.mk",
+ ?line {ok,BasicMf1} = file:read_file(BasicMf1Name),
+ ?line {ok,_,Mf1} = compile:file(Simple, [binary,makedep]),
+ ?line BasicMf1 = makedep_canonicalize_result(Mf1, DataDir),
+ %% Basic rule with one existing header.
+ ?line BasicMf2Name = SimpleRootname ++ "-basic2.mk",
+ ?line {ok,BasicMf2} = file:read_file(BasicMf2Name),
+ ?line {ok,_,Mf2} = compile:file(Simple, [binary,makedep|IncludeOptions]),
+ ?line BasicMf2 = makedep_canonicalize_result(Mf2, DataDir),
+ %% Rule with one existing header and one missing header.
+ ?line MissingMfName = SimpleRootname ++ "-missing.mk",
+ ?line {ok,MissingMf} = file:read_file(MissingMfName),
+ ?line {ok,_,Mf3} = compile:file(Simple,
+ [binary,makedep,makedep_add_missing|IncludeOptions]),
+ ?line MissingMf = makedep_canonicalize_result(Mf3, DataDir),
+ %% Rule with modified target.
+ ?line TargetMf1Name = SimpleRootname ++ "-target1.mk",
+ ?line {ok,TargetMf1} = file:read_file(TargetMf1Name),
+ ?line {ok,_,Mf4} = compile:file(Simple,
+ [binary,makedep,{makedep_target,"$target"}|IncludeOptions]),
+ ?line TargetMf1 = makedep_modify_target(
+ makedep_canonicalize_result(Mf4, DataDir), "$$target"),
+ %% Rule with quoted modified target.
+ ?line TargetMf2Name = SimpleRootname ++ "-target2.mk",
+ ?line {ok,TargetMf2} = file:read_file(TargetMf2Name),
+ ?line {ok,_,Mf5} = compile:file(Simple,
+ [binary,makedep,{makedep_target,"$target"},makedep_quote_target|
+ IncludeOptions]),
+ ?line TargetMf2 = makedep_modify_target(
+ makedep_canonicalize_result(Mf5, DataDir), "$$target"),
+ %% Basic rule written to some file.
+ ?line {ok,_} = compile:file(Simple,
+ [makedep,{makedep_output,Target}|IncludeOptions]),
+ ?line {ok,Mf6} = file:read_file(Target),
+ ?line BasicMf2 = makedep_canonicalize_result(Mf6, DataDir),
+
+ ?line ok = file:delete(Target),
+ ?line ok = file:del_dir(filename:dirname(Target)),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+makedep_canonicalize_result(Mf, DataDir) ->
+ Mf0 = binary_to_list(Mf),
+ %% Replace the Datadir by "$(srcdir)".
+ Mf1 = re:replace(Mf0, DataDir, "$(srcdir)/",
+ [global,multiline,{return,list}]),
+ %% Long lines are splitted, put back everything on one line.
+ Mf2 = re:replace(Mf1, "\\\\\n ", "", [global,multiline,{return,list}]),
+ list_to_binary(Mf2).
+
+makedep_modify_target(Mf, Target) ->
+ Mf0 = binary_to_list(Mf),
+ Mf1 = re:replace(Mf0, Target, "$target", [{return,list}]),
+ list_to_binary(Mf1).
+
%% Tests that conditional compilation, defining values, including files work.
cond_and_ifdef(Config) when is_list(Config) ->
@@ -465,7 +552,6 @@ exists(Name) ->
{error, _} -> false
end.
-bad_record_use(suite) -> [bad_record_use1, bad_record_use2].
%% Tests that the compiler does not accept
%% bad use of records.
diff --git a/lib/compiler/test/compile_SUITE_data/simple-basic1.mk b/lib/compiler/test/compile_SUITE_data/simple-basic1.mk
new file mode 100644
index 0000000000..4073fa82d0
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-basic1.mk
@@ -0,0 +1 @@
+simple.beam: $(srcdir)/simple.erl
diff --git a/lib/compiler/test/compile_SUITE_data/simple-basic2.mk b/lib/compiler/test/compile_SUITE_data/simple-basic2.mk
new file mode 100644
index 0000000000..761d1d9582
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-basic2.mk
@@ -0,0 +1 @@
+simple.beam: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl
diff --git a/lib/compiler/test/compile_SUITE_data/simple-missing.mk b/lib/compiler/test/compile_SUITE_data/simple-missing.mk
new file mode 100644
index 0000000000..b13d44ec36
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-missing.mk
@@ -0,0 +1 @@
+simple.beam: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl generated.hrl
diff --git a/lib/compiler/test/compile_SUITE_data/simple-target1.mk b/lib/compiler/test/compile_SUITE_data/simple-target1.mk
new file mode 100644
index 0000000000..dd9fa0d6e5
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-target1.mk
@@ -0,0 +1 @@
+$target: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl
diff --git a/lib/compiler/test/compile_SUITE_data/simple-target2.mk b/lib/compiler/test/compile_SUITE_data/simple-target2.mk
new file mode 100644
index 0000000000..a5fc6f461d
--- /dev/null
+++ b/lib/compiler/test/compile_SUITE_data/simple-target2.mk
@@ -0,0 +1 @@
+$$target: $(srcdir)/simple.erl $(srcdir)/include/simple.hrl
diff --git a/lib/compiler/test/compile_SUITE_data/simple.erl b/lib/compiler/test/compile_SUITE_data/simple.erl
index 2021056388..0c1c70a778 100644
--- a/lib/compiler/test/compile_SUITE_data/simple.erl
+++ b/lib/compiler/test/compile_SUITE_data/simple.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,3 +37,7 @@ foo() ->
{?included_value, ?foo_value}.
-endif.
+
+-ifdef(include_generated).
+-include("generated.hrl").
+-endif.
diff --git a/lib/compiler/test/compiler.cover b/lib/compiler/test/compiler.cover
index 5ec2408a35..9fc4c7dd43 100644
--- a/lib/compiler/test/compiler.cover
+++ b/lib/compiler/test/compiler.cover
@@ -1,3 +1,5 @@
+{incl_app,compiler,details}.
+
%% -*- erlang -*-
-{exclude,[sys_pre_attributes,core_parse]}.
+{excl_mods,[sys_pre_attributes,core_scan,core_parse]}.
diff --git a/lib/compiler/test/compiler.dynspec b/lib/compiler/test/compiler.dynspec
deleted file mode 100644
index 7e452cef6c..0000000000
--- a/lib/compiler/test/compiler.dynspec
+++ /dev/null
@@ -1,10 +0,0 @@
-%% -*- erlang -*-
-%% You can test this file using this command.
-%% file:script("compiler.dynspec", [{'Os',"Unix"}]).
-
-case Os of
- "VxWorks" ->
- [{skip,{compile_SUITE,listings,"VxWorks filesystem too slow"}}];
- _ ->
- []
-end.
diff --git a/lib/compiler/test/compiler.spec b/lib/compiler/test/compiler.spec
new file mode 100644
index 0000000000..f2546c3ced
--- /dev/null
+++ b/lib/compiler/test/compiler.spec
@@ -0,0 +1,2 @@
+%% -*- erlang -*-
+{suites,"../compiler_test",all}.
diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl
index 54cf799057..21a5f65dee 100644
--- a/lib/compiler/test/core_SUITE.erl
+++ b/lib/compiler/test/core_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,10 +18,12 @@
%%
-module(core_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
dehydrated_itracer/1,nested_tries/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(comp(N),
N(Config) when is_list(Config) -> try_it(N, Config)).
@@ -30,14 +32,32 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [dehydrated_itracer,nested_tries].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(core_SUITE),
+ [dehydrated_itracer, nested_tries].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
?comp(dehydrated_itracer).
?comp(nested_tries).
diff --git a/lib/compiler/test/core_SUITE_data/.gitignore b/lib/compiler/test/core_SUITE_data/.gitignore
new file mode 100644
index 0000000000..d11d93d37f
--- /dev/null
+++ b/lib/compiler/test/core_SUITE_data/.gitignore
@@ -0,0 +1 @@
+!*.core
diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl
index 5f2c905d4a..710751b09d 100644
--- a/lib/compiler/test/core_fold_SUITE.erl
+++ b/lib/compiler/test/core_fold_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,18 +18,37 @@
%%
-module(core_fold_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
t_element/1,setelement/1,t_length/1,append/1,t_apply/1,bifs/1,
eq/1,nested_call_in_case/1,coverage/1]).
-export([foo/0,foo/1,foo/2,foo/3]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(core_fold_SUITE),
+ [t_element, setelement, t_length, append, t_apply, bifs,
+ eq, nested_call_in_case, coverage].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [t_element,setelement,t_length,append,t_apply,bifs,
- eq,nested_call_in_case,coverage].
t_element(Config) when is_list(Config) ->
X = make_ref(),
diff --git a/lib/compiler/test/error_SUITE.erl b/lib/compiler/test/error_SUITE.erl
index 4530313bb0..c9823665b4 100644
--- a/lib/compiler/test/error_SUITE.erl
+++ b/lib/compiler/test/error_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,14 +18,154 @@
%%
-module(error_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ head_mismatch_line/1,warnings_as_errors/1, bif_clashes/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(error_SUITE),
+ [head_mismatch_line, warnings_as_errors, bif_clashes].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+bif_clashes(Config) when is_list(Config) ->
+ Ts = [{bif_clashes1,
+ <<"
+ -export([t/0]).
+ t() ->
+ length([a,b,c]).
+
+ length(X) ->
+ erlang:length(X).
+ ">>,
+ [return_warnings],
+ {error,
+ [{4, erl_lint,{call_to_redefined_old_bif,{length,1}}}], []} }],
+ ?line [] = run(Config, Ts),
+ Ts1 = [{bif_clashes2,
+ <<"
+ -export([t/0]).
+ -import(x,[length/1]).
+ t() ->
+ length([a,b,c]).
+ ">>,
+ [return_warnings],
+ {error,
+ [{3, erl_lint,{redefine_old_bif_import,{length,1}}}], []} }],
+ ?line [] = run(Config, Ts1),
+ Ts00 = [{bif_clashes3,
+ <<"
+ -export([t/0]).
+ -compile({no_auto_import,[length/1]}).
+ t() ->
+ length([a,b,c]).
+
+ length(X) ->
+ erlang:length(X).
+ ">>,
+ [return_warnings],
+ []}],
+ ?line [] = run(Config, Ts00),
+ Ts11 = [{bif_clashes4,
+ <<"
+ -export([t/0]).
+ -compile({no_auto_import,[length/1]}).
+ -import(x,[length/1]).
+ t() ->
+ length([a,b,c]).
+ ">>,
+ [return_warnings],
+ []}],
+ ?line [] = run(Config, Ts11),
+ Ts000 = [{bif_clashes5,
+ <<"
+ -export([t/0]).
+ t() ->
+ binary_part(<<1,2,3,4>>,1,2).
+
+ binary_part(X,Y,Z) ->
+ erlang:binary_part(X,Y,Z).
+ ">>,
+ [return_warnings],
+ {warning,
+ [{4, erl_lint,{call_to_redefined_bif,{binary_part,3}}}]} }],
+ ?line [] = run(Config, Ts000),
+ Ts111 = [{bif_clashes6,
+ <<"
+ -export([t/0]).
+ -import(x,[binary_part/3]).
+ t() ->
+ binary_part(<<1,2,3,4>>,1,2).
+ ">>,
+ [return_warnings],
+ {warning,
+ [{3, erl_lint,{redefine_bif_import,{binary_part,3}}}]} }],
+ ?line [] = run(Config, Ts111),
+ Ts2 = [{bif_clashes7,
+ <<"
+ -export([t/0]).
+ -compile({no_auto_import,[length/1]}).
+ -import(x,[length/1]).
+ t() ->
+ length([a,b,c]).
+ length(X) ->
+ erlang:length(X).
+ ">>,
+ [],
+ {error,
+ [{7,erl_lint,{define_import,{length,1}}}],
+ []} }],
+ ?line [] = run2(Config, Ts2),
+ Ts3 = [{bif_clashes8,
+ <<"
+ -export([t/1]).
+ -compile({no_auto_import,[length/1]}).
+ t(X) when length(X) > 3 ->
+ length([a,b,c]).
+ length(X) ->
+ erlang:length(X).
+ ">>,
+ [],
+ {error,
+ [{4,erl_lint,{illegal_guard_local_call,{length,1}}}],
+ []} }],
+ ?line [] = run2(Config, Ts3),
+ Ts4 = [{bif_clashes9,
+ <<"
+ -export([t/1]).
+ -compile({no_auto_import,[length/1]}).
+ -import(x,[length/1]).
+ t(X) when length(X) > 3 ->
+ length([a,b,c]).
+ ">>,
+ [],
+ {error,
+ [{5,erl_lint,{illegal_guard_local_call,{length,1}}}],
+ []} }],
+ ?line [] = run2(Config, Ts4),
+
+ ok.
+
--export([all/1,
- head_mismatch_line/1,warnings_as_errors/1]).
-all(suite) ->
- test_lib:recompile(?MODULE),
- [head_mismatch_line,warnings_as_errors].
%% Tests that a head mismatch is reported on the correct line (OTP-2125).
head_mismatch_line(Config) when is_list(Config) ->
@@ -49,7 +189,7 @@ warnings_as_errors(Config) when is_list(Config) ->
A = unused,
ok.
">>,
- [warnings_as_errors],
+ [export_all,warnings_as_errors],
{error,
[],
[{3,erl_lint,{unused_var,'A'}}]} }],
@@ -70,6 +210,24 @@ run(Config, Tests) ->
end,
lists:foldl(F, [], Tests).
+run2(Config, Tests) ->
+ F = fun({N,P,Ws,E}, BadL) ->
+ case catch filter(run_test(Config, P, Ws)) of
+ E ->
+ BadL;
+ Bad ->
+ ?t:format("~nTest ~p failed. Expected~n ~p~n"
+ "but got~n ~p~n", [N, E, Bad]),
+ fail()
+ end
+ end,
+ lists:foldl(F, [], Tests).
+
+filter({error,Es,_Ws}) ->
+ {error,Es,[]};
+filter(X) ->
+ X.
+
%% Compiles a test module and returns the list of errors and warnings.
@@ -78,17 +236,29 @@ run_test(Conf, Test0, Warnings) ->
?line DataDir = ?config(priv_dir, Conf),
?line Test = ["-module(errors_test). ", Test0],
?line File = filename:join(DataDir, Filename),
- ?line Opts = [binary,export_all,return|Warnings],
+ ?line Opts = [binary,return_errors|Warnings],
?line ok = file:write_file(File, Test),
%% Compile once just to print all errors and warnings.
- ?line compile:file(File, [binary,export_all,report|Warnings]),
+ ?line compile:file(File, [binary,report|Warnings]),
%% Test result of compilation.
?line Res = case compile:file(File, Opts) of
- {error,[{_File,Es}],Ws} ->
+ {ok,errors_test,_,[{_File,Ws}]} ->
+ %io:format("compile:file(~s,~p) ->~n~p~n",
+ % [File,Opts,Ws]),
+ {warning,Ws};
+ {ok,errors_test,_,[]} ->
+ %io:format("compile:file(~s,~p) ->~n~p~n",
+ % [File,Opts,Ws]),
+ [];
+ {error,[{XFile,Es}],Ws} = _ZZ when is_list(XFile) ->
+ %io:format("compile:file(~s,~p) ->~n~p~n",
+ % [File,Opts,_ZZ]),
{error,Es,Ws};
- {error,Es,[{_File,Ws}]} ->
+ {error,Es,[{_File,Ws}]} = _ZZ->
+ %io:format("compile:file(~s,~p) ->~n~p~n",
+ % [File,Opts,_ZZ]),
{error,Es,Ws}
end,
file:delete(File),
diff --git a/lib/compiler/test/float_SUITE.erl b/lib/compiler/test/float_SUITE.erl
index 07779ddd5a..6738265776 100644
--- a/lib/compiler/test/float_SUITE.erl
+++ b/lib/compiler/test/float_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,13 +17,34 @@
%% %CopyrightEnd%
%%
-module(float_SUITE).
--export([all/1,pending/1,bif_calls/1,math_functions/1,mixed_float_and_int/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ pending/1,bif_calls/1,math_functions/1,mixed_float_and_int/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(float_SUITE),
+ [pending, bif_calls, math_functions,
+ mixed_float_and_int].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [pending,bif_calls,math_functions,mixed_float_and_int].
%% Thanks to Tobias Lindahl <[email protected]>
%% Shows the effect of pending exceptions on the x86.
@@ -82,6 +103,14 @@ bad_negate(X, Y) when is_float(X) ->
Y1 = -Y, %BIF call.
{X2, Y1}.
+%% Some math functions are not implemented on all platforms.
+-define(OPTIONAL(Expected, Expr),
+ try
+ Expected = Expr
+ catch
+ error:undef -> ok
+ end).
+
math_functions(Config) when is_list(Config) ->
%% Mostly silly coverage.
?line 0.0 = math:tan(0),
@@ -93,6 +122,14 @@ math_functions(Config) when is_list(Config) ->
?line -1.0 = math:cos(math:pi()),
?line 1.0 = math:exp(0),
?line 1.0 = math:pow(math:pi(), 0),
+ ?line 0.0 = math:log(1),
+ ?line 0.0 = math:asin(0),
+ ?line 0.0 = math:acos(1),
+ ?line ?OPTIONAL(0.0, math:asinh(0)),
+ ?line ?OPTIONAL(0.0, math:acosh(1)),
+ ?line ?OPTIONAL(0.0, math:atanh(0)),
+ ?line ?OPTIONAL(0.0, math:erf(0)),
+ ?line ?OPTIONAL(1.0, math:erfc(0)),
?line 0.0 = math:tan(id(0)),
?line 0.0 = math:atan2(id(0), 1),
@@ -101,6 +138,14 @@ math_functions(Config) when is_list(Config) ->
?line 0.0 = math:tanh(id(0)),
?line 1.0 = math:log10(id(10)),
?line 1.0 = math:exp(id(0)),
+ ?line 0.0 = math:log(id(1)),
+ ?line 0.0 = math:asin(id(0)),
+ ?line 0.0 = math:acos(id(1)),
+ ?line ?OPTIONAL(0.0, math:asinh(id(0))),
+ ?line ?OPTIONAL(0.0, math:acosh(id(1))),
+ ?line ?OPTIONAL(0.0, math:atanh(id(0))),
+ ?line ?OPTIONAL(0.0, math:erf(id(0))),
+ ?line ?OPTIONAL(1.0, math:erfc(id(0))),
%% Only for coverage (of beam_type.erl).
?line {'EXIT',{undef,_}} = (catch math:fnurfla(0)),
diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl
index fb2667245a..aa9be83c82 100644
--- a/lib/compiler/test/fun_SUITE.erl
+++ b/lib/compiler/test/fun_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,14 +18,33 @@
%%
-module(fun_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(fun_SUITE),
+ [test1, overwritten_fun, otp_7202, bif_fun].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [test1,overwritten_fun,otp_7202,bif_fun].
%%% The help functions below are copied from emulator:bs_construct_SUITE.
diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl
index f3960b28c3..482564a32b 100644
--- a/lib/compiler/test/guard_SUITE.erl
+++ b/lib/compiler/test/guard_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,10 @@
%%
-module(guard_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
misc/1,const_cond/1,basic_not/1,complex_not/1,nested_nots/1,
semicolon/1,complex_semicolon/1,comma/1,
or_guard/1,more_or_guards/1,
@@ -31,19 +32,35 @@
t_is_boolean/1,is_function_2/1,
tricky/1,rel_ops/1,literal_type_tests/1,
basic_andalso_orelse/1,traverse_dcd/1,
- check_qlc_hrl/1,andalso_semi/1,tuple_size/1]).
-
-all(suite) ->
- test_lib:recompile(?MODULE),
- [misc,const_cond,basic_not,complex_not,nested_nots,
- semicolon,complex_semicolon,
- comma,or_guard,more_or_guards,
- complex_or_guards,and_guard,
- xor_guard,more_xor_guards,
- build_in_guard,old_guard_tests,gbif,
- t_is_boolean,is_function_2,tricky,rel_ops,literal_type_tests,
- basic_andalso_orelse,traverse_dcd,check_qlc_hrl,andalso_semi,
- tuple_size].
+ check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(guard_SUITE),
+ [misc, const_cond, basic_not, complex_not, nested_nots,
+ semicolon, complex_semicolon, comma, or_guard,
+ more_or_guards, complex_or_guards, and_guard, xor_guard,
+ more_xor_guards, build_in_guard, old_guard_tests, gbif,
+ t_is_boolean, is_function_2, tricky, rel_ops,
+ literal_type_tests, basic_andalso_orelse, traverse_dcd,
+ check_qlc_hrl, andalso_semi, t_tuple_size, binary_part].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
misc(Config) when is_list(Config) ->
?line 42 = case id(42) of
@@ -94,8 +111,8 @@ const_cond(Config) when is_list(Config) ->
const_cond(T, Sz) ->
case T of
_X when false -> never;
- _X when tuple(T), eq == eq, tuple_size(T) == Sz -> ok;
- _X when tuple(T), eq == leq, tuple_size(T) =< Sz -> ok;
+ _X when is_tuple(T), eq == eq, tuple_size(T) == Sz -> ok;
+ _X when is_tuple(T), eq == leq, tuple_size(T) =< Sz -> ok;
_X -> error
end.
@@ -1137,7 +1154,7 @@ make_test([{T,L}|Ts]) ->
make_test([]) -> [].
test(T, L) ->
- S0 = io_lib:format("begin io:format(\"~~p~~n\", [{~p,~p}]), if ~w(~w) -> true; true -> false end end. ", [T,L,T,L]),
+ S0 = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p}]), if ~w(~w) -> true; true -> false end end. ", [T,L,T,L]),
S = lists:flatten(S0),
{ok,Toks,_Line} = erl_scan:string(S),
{ok,E} = erl_parse:parse_exprs(Toks),
@@ -1145,7 +1162,7 @@ test(T, L) ->
{match,0,{atom,0,Val},hd(E)}.
test(T, L1, L2) ->
- S0 = io_lib:format("begin io:format(\"~~p~~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ", [T,L1,L2,T,L1,L2]),
+ S0 = io_lib:format("begin io:format(\"~~p~n\", [{~p,~p,~p}]), if ~w(~w, ~w) -> true; true -> false end end. ", [T,L1,L2,T,L1,L2]),
S = lists:flatten(S0),
{ok,Toks,_Line} = erl_scan:string(S),
{ok,E} = erl_parse:parse_exprs(Toks),
@@ -1330,7 +1347,7 @@ andalso_semi_bar(Bar) when is_list(Bar) andalso length(Bar) =:= 3; Bar =:= 1 ->
ok.
-tuple_size(Config) when is_list(Config) ->
+t_tuple_size(Config) when is_list(Config) ->
?line 10 = do_tuple_size({1,2,3,4}),
?line fc(catch do_tuple_size({1,2,3})),
?line fc(catch do_tuple_size(42)),
@@ -1362,6 +1379,146 @@ ludicrous_tuple_size(T)
when tuple_size(T) =:= 16#FFFFFFFFFFFFFFFF -> ok;
ludicrous_tuple_size(_) -> error.
+%%
+%% The binary_part/2,3 guard BIFs
+%%
+-define(MASK_ERROR(EXPR),mask_error((catch (EXPR)))).
+mask_error({'EXIT',{Err,_}}) ->
+ Err;
+mask_error(Else) ->
+ Else.
+
+binary_part(doc) ->
+ ["Tests the binary_part/2,3 guard (GC) bif's"];
+binary_part(Config) when is_list(Config) ->
+ %% This is more or less a copy of what the guard_SUITE in emulator
+ %% does to cover the guard bif's
+ ?line 1 = bptest(<<1,2,3>>),
+ ?line 2 = bptest(<<2,1,3>>),
+ ?line error = bptest(<<1>>),
+ ?line error = bptest(<<>>),
+ ?line error = bptest(apa),
+ ?line 3 = bptest(<<2,3,3>>),
+ % With one variable (pos)
+ ?line 1 = bptest(<<1,2,3>>,1),
+ ?line 2 = bptest(<<2,1,3>>,1),
+ ?line error = bptest(<<1>>,1),
+ ?line error = bptest(<<>>,1),
+ ?line error = bptest(apa,1),
+ ?line 3 = bptest(<<2,3,3>>,1),
+ % With one variable (length)
+ ?line 1 = bptesty(<<1,2,3>>,1),
+ ?line 2 = bptesty(<<2,1,3>>,1),
+ ?line error = bptesty(<<1>>,1),
+ ?line error = bptesty(<<>>,1),
+ ?line error = bptesty(apa,1),
+ ?line 3 = bptesty(<<2,3,3>>,2),
+ % With one variable (whole tuple)
+ ?line 1 = bptestx(<<1,2,3>>,{1,1}),
+ ?line 2 = bptestx(<<2,1,3>>,{1,1}),
+ ?line error = bptestx(<<1>>,{1,1}),
+ ?line error = bptestx(<<>>,{1,1}),
+ ?line error = bptestx(apa,{1,1}),
+ ?line 3 = bptestx(<<2,3,3>>,{1,2}),
+ % With two variables
+ ?line 1 = bptest(<<1,2,3>>,1,1),
+ ?line 2 = bptest(<<2,1,3>>,1,1),
+ ?line error = bptest(<<1>>,1,1),
+ ?line error = bptest(<<>>,1,1),
+ ?line error = bptest(apa,1,1),
+ ?line 3 = bptest(<<2,3,3>>,1,2),
+ % Direct (autoimported) call, these will be evaluated by the compiler...
+ ?line <<2>> = binary_part(<<1,2,3>>,1,1),
+ ?line <<1>> = binary_part(<<2,1,3>>,1,1),
+ % Compiler warnings due to constant evaluation expected (3)
+ ?line badarg = ?MASK_ERROR(binary_part(<<1>>,1,1)),
+ ?line badarg = ?MASK_ERROR(binary_part(<<>>,1,1)),
+ ?line badarg = ?MASK_ERROR(binary_part(apa,1,1)),
+ ?line <<3,3>> = binary_part(<<2,3,3>>,1,2),
+ % Direct call through apply
+ ?line <<2>> = apply(erlang,binary_part,[<<1,2,3>>,1,1]),
+ ?line <<1>> = apply(erlang,binary_part,[<<2,1,3>>,1,1]),
+ % Compiler warnings due to constant evaluation expected (3)
+ ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<1>>,1,1])),
+ ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[<<>>,1,1])),
+ ?line badarg = ?MASK_ERROR(apply(erlang,binary_part,[apa,1,1])),
+ ?line <<3,3>> = apply(erlang,binary_part,[<<2,3,3>>,1,2]),
+ % Constant propagation
+ ?line Bin = <<1,2,3>>,
+ ?line ok = if
+ binary_part(Bin,1,1) =:= <<2>> ->
+ ok;
+ %% Compiler warning, clause cannot match (expected)
+ true ->
+ error
+ end,
+ ?line ok = if
+ binary_part(Bin,{1,1}) =:= <<2>> ->
+ ok;
+ %% Compiler warning, clause cannot match (expected)
+ true ->
+ error
+ end,
+ ok.
+
+
+bptest(B) when length(B) =:= 1337 ->
+ 1;
+bptest(B) when binary_part(B,{1,1}) =:= <<2>> ->
+ 1;
+bptest(B) when erlang:binary_part(B,1,1) =:= <<1>> ->
+ 2;
+bptest(B) when erlang:binary_part(B,{1,2}) =:= <<3,3>> ->
+ 3;
+bptest(_) ->
+ error.
+
+bptest(B,A) when length(B) =:= A ->
+ 1;
+bptest(B,A) when binary_part(B,{A,1}) =:= <<2>> ->
+ 1;
+bptest(B,A) when erlang:binary_part(B,A,1) =:= <<1>> ->
+ 2;
+bptest(B,A) when erlang:binary_part(B,{A,2}) =:= <<3,3>> ->
+ 3;
+bptest(_,_) ->
+ error.
+
+bptestx(B,A) when length(B) =:= A ->
+ 1;
+bptestx(B,A) when binary_part(B,A) =:= <<2>> ->
+ 1;
+bptestx(B,A) when erlang:binary_part(B,A) =:= <<1>> ->
+ 2;
+bptestx(B,A) when erlang:binary_part(B,A) =:= <<3,3>> ->
+ 3;
+bptestx(_,_) ->
+ error.
+
+bptesty(B,A) when length(B) =:= A ->
+ 1;
+bptesty(B,A) when binary_part(B,{1,A}) =:= <<2>> ->
+ 1;
+bptesty(B,A) when erlang:binary_part(B,1,A) =:= <<1>> ->
+ 2;
+bptesty(B,A) when erlang:binary_part(B,{1,A}) =:= <<3,3>> ->
+ 3;
+bptesty(_,_) ->
+ error.
+
+bptest(B,A,_C) when length(B) =:= A ->
+ 1;
+bptest(B,A,C) when binary_part(B,{A,C}) =:= <<2>> ->
+ 1;
+bptest(B,A,C) when erlang:binary_part(B,A,C) =:= <<1>> ->
+ 2;
+bptest(B,A,C) when erlang:binary_part(B,{A,C}) =:= <<3,3>> ->
+ 3;
+bptest(_,_,_) ->
+ error.
+
+
+
%% Call this function to turn off constant propagation.
id(I) -> I.
diff --git a/lib/compiler/test/inline_SUITE.erl b/lib/compiler/test/inline_SUITE.erl
index 396fb450b7..7b9600c2f6 100644
--- a/lib/compiler/test/inline_SUITE.erl
+++ b/lib/compiler/test/inline_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(inline_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-compile(export_all).
-compile({inline,[badarg/2]}).
@@ -28,10 +28,29 @@
%% Needed by test case `lists'.
-compile(inline_list_funcs).
-all(suite) ->
- test_lib:recompile(?MODULE),
- [attribute,bsdecode,bsdes,barnes2,decode1,smith,itracer,pseudoknot,lists,
- really_inlined,otp_7223,coverage].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(inline_SUITE),
+ [attribute, bsdecode, bsdes, barnes2, decode1, smith,
+ itracer, pseudoknot, lists, really_inlined, otp_7223,
+ coverage].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
attribute(Config) when is_list(Config) ->
Name = "attribute",
@@ -49,7 +68,7 @@ attribute(Config) when is_list(Config) ->
ok.
-define(comp(Name),
- Name(Config) when list(Config) ->
+ Name(Config) when is_list(Config) ->
try_inline(Name, Config)).
?comp(bsdecode).
diff --git a/lib/compiler/test/lc_SUITE.erl b/lib/compiler/test/lc_SUITE.erl
index 40bf67e1fa..bcdcf2fd9f 100644
--- a/lib/compiler/test/lc_SUITE.erl
+++ b/lib/compiler/test/lc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,21 +19,41 @@
-module(lc_SUITE).
-author('[email protected]').
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
basic/1,deeply_nested/1,no_generator/1,
empty_generator/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(lc_SUITE),
+ [basic, deeply_nested, no_generator, empty_generator].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [basic,deeply_nested,no_generator,empty_generator].
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
@@ -160,7 +180,7 @@ empty_generator(Config) when is_list(Config) ->
id(I) -> I.
fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Args}|_]}}) -> ok;
-fc(Args, {'EXIT',{function_clause,[{?MODULE,Name,Arity}|_]}})
+fc(Args, {'EXIT',{function_clause,[{?MODULE,_,Arity}|_]}})
when length(Args) =:= Arity ->
true = test_server:is_native(?MODULE);
fc(Args, {'EXIT',{{case_clause,ActualArgs},_}})
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index 9c4687efa1..04879300d1 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,17 +18,36 @@
%%
-module(match_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
pmatch/1,mixed/1,aliases/1,match_in_call/1,
untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,underscore/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(match_SUITE),
+ [pmatch, mixed, aliases, match_in_call, untuplify,
+ shortcut_boolean, letify_guard, selectify, underscore].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [pmatch,mixed,aliases,match_in_call,untuplify,shortcut_boolean,
- letify_guard,selectify,underscore].
pmatch(Config) when is_list(Config) ->
?line ok = doit(1),
@@ -112,6 +131,12 @@ aliases(Config) when is_list(Config) ->
?line {42,42,42,42} = multiple_aliases_1(42),
?line {7,7,7} = multiple_aliases_2(7),
?line {{a,b},{a,b},{a,b}} = multiple_aliases_3({a,b}),
+
+ %% Lists/literals.
+ ?line {a,b} = list_alias1([a,b]),
+ ?line {a,b} = list_alias2([a,b]),
+ ?line {a,b} = list_alias3([a,b]),
+
ok.
str_alias(V) ->
@@ -206,6 +231,15 @@ multiple_aliases_2((A=B)=(A=C)) ->
multiple_aliases_3((A={_,_}=B)={_,_}=C) ->
{A,B,C}.
+list_alias1([a,b]=[X,Y]) ->
+ {X,Y}.
+
+list_alias2([X,Y]=[a,b]) ->
+ {X,Y}.
+
+list_alias3([X,b]=[a,Y]) ->
+ {X,Y}.
+
%% OTP-7018.
match_in_call(Config) when is_list(Config) ->
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index 793c53ac31..f1f9b17084 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,11 +18,26 @@
%%
-module(misc_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
tobias/1,empty_string/1,md5/1,silly_coverage/1,
- confused_literals/1,integer_encoding/1]).
+ confused_literals/1,integer_encoding/1,override_bif/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+%% For the override_bif testcase.
+%% NB, no other testcases in this testsuite can use these without erlang:prefix!
+-compile({no_auto_import,[abs/1]}).
+-compile({no_auto_import,[binary_part/3]}).
+-compile({no_auto_import,[binary_part/2]}).
+-import(test_lib,[binary_part/2]).
+
+%% This should do no harm (except for fun byte_size/1 which does not, by design, work with import
+-compile({no_auto_import,[byte_size/1]}).
+-import(erlang,[byte_size/1]).
+
+
%% Include an opaque declaration to cover the stripping of
%% opaque types from attributes in v3_kernel.
@@ -32,17 +47,66 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(10)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
--spec all(any()) -> misc_SUITE_test_cases().
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+-spec all() -> misc_SUITE_test_cases().
+all() ->
+ test_lib:recompile(misc_SUITE),
+ [tobias, empty_string, md5, silly_coverage,
+ confused_literals, integer_encoding, override_bif].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-all(suite) ->
- test_lib:recompile(?MODULE),
- [tobias,empty_string,md5,silly_coverage,confused_literals,
- integer_encoding].
+
+%%
+%% Functions that override new and old bif's
+%%
+abs(_N) ->
+ dummy_abs.
+
+binary_part(_,_,_) ->
+ dummy_bp.
+
+% Make sure that auto-imported BIF's are overridden correctly
+
+override_bif(suite) ->
+ [];
+override_bif(doc) ->
+ ["Test dat local functions and imports override auto-imported BIFs."];
+override_bif(Config) when is_list(Config) ->
+ ?line dummy_abs = abs(1),
+ ?line dummy_bp = binary_part(<<"hello">>,1,1),
+ ?line dummy = binary_part(<<"hello">>,{1,1}),
+ ?line 1 = erlang:abs(1),
+ ?line <<"e">> = erlang:binary_part(<<"hello">>,1,1),
+ ?line <<"e">> = erlang:binary_part(<<"hello">>,{1,1}),
+ F = fun(X) when byte_size(X) =:= 4 ->
+ four;
+ (X) ->
+ byte_size(X)
+ end,
+ ?line four = F(<<1,2,3,4>>),
+ ?line 5 = F(<<1,2,3,4,5>>),
+ ok.
%% A bug reported by Tobias Lindahl for a development version of R11B.
@@ -104,7 +168,17 @@ silly_coverage(Config) when is_list(Config) ->
?line expect_error(fun() -> sys_core_dsetel:module(BadCoreErlang, []) end),
?line expect_error(fun() -> v3_kernel:module(BadCoreErlang, []) end),
- %% v3_codgen
+ %% v3_life
+ BadKernel = {k_mdef,[],?MODULE,
+ [{foo,0}],
+ [],
+ [{k_fdef,
+ {k,[],[],[]},
+ f,0,[],
+ seriously_bad_body}]},
+ ?line expect_error(fun() -> v3_life:module(BadKernel, []) end),
+
+ %% v3_codegen
CodegenInput = {?MODULE,[{foo,0}],[],[{function,foo,0,[a|b],a,b}]},
?line expect_error(fun() -> v3_codegen:module(CodegenInput, []) end),
diff --git a/lib/compiler/test/num_bif_SUITE.erl b/lib/compiler/test/num_bif_SUITE.erl
index 912f7366dd..0a4750dc08 100644
--- a/lib/compiler/test/num_bif_SUITE.erl
+++ b/lib/compiler/test/num_bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
%%
-module(num_bif_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Tests optimization of the BIFs:
%% abs/1
@@ -30,17 +30,37 @@
%% round/1
%% trunc/1
--export([all/1, t_abs/1, t_float/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, t_abs/1, t_float/1,
t_float_to_list/1, t_integer_to_list/1,
t_list_to_integer/1,
- t_list_to_float/1, t_list_to_float_safe/1, t_list_to_float_risky/1,
+ t_list_to_float_safe/1, t_list_to_float_risky/1,
t_round/1, t_trunc/1]).
-all(suite) ->
- test_lib:recompile(?MODULE),
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(num_bif_SUITE),
[t_abs, t_float, t_float_to_list, t_integer_to_list,
- t_list_to_float, t_list_to_integer,
- t_round, t_trunc].
+ {group, t_list_to_float}, t_list_to_integer, t_round,
+ t_trunc].
+
+groups() ->
+ [{t_list_to_float, [],
+ [t_list_to_float_safe, t_list_to_float_risky]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
t_abs(Config) when is_list(Config) ->
%% Floats.
@@ -142,7 +162,6 @@ t_integer_to_list(Config) when is_list(Config) ->
%% Tests list_to_float/1.
-t_list_to_float(suite) -> [t_list_to_float_safe, t_list_to_float_risky].
t_list_to_float_safe(Config) when is_list(Config) ->
?line 0.0 = list_to_float("0.0"),
diff --git a/lib/compiler/test/parteval_SUITE.erl b/lib/compiler/test/parteval_SUITE.erl
index 3ef84571b9..6b1ae38c1b 100644
--- a/lib/compiler/test/parteval_SUITE.erl
+++ b/lib/compiler/test/parteval_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,11 +18,31 @@
%%
-module(parteval_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1, pe2/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, pe2/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [pe2].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> [pe2].
%% (This is more general than needed, since we once compiled the same
%% source code with and without a certain option.)
diff --git a/lib/compiler/test/pmod_SUITE.erl b/lib/compiler/test/pmod_SUITE.erl
index 13503ce905..4c68d777ca 100644
--- a/lib/compiler/test/pmod_SUITE.erl
+++ b/lib/compiler/test/pmod_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,20 +18,40 @@
%%
-module(pmod_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
basic/1, otp_8447/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-all(suite) ->
- test_lib:recompile(?MODULE),
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(pmod_SUITE),
[basic, otp_8447].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
[{watchdog,Dog}|Config].
-fin_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
+end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index fca3f0387b..75e8045693 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,23 +20,42 @@
-module(receive_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
- recv/1,coverage/1,otp_7980/1,ref_opt/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ export/1,recv/1,coverage/1,otp_7980/1,ref_opt/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
init_per_testcase(_Case, Config) ->
?line Dog = test_server:timetrap(test_server:minutes(2)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [recv,coverage,otp_7980,ref_opt].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(receive_SUITE),
+ [recv, coverage, otp_7980, ref_opt, export].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-record(state, {ena = true}).
@@ -205,4 +224,25 @@ collect_recv_opt_instrs(Code) ->
end] || {function,_,_,_,Is} <- Code],
lists:append(L).
+export(Config) when is_list(Config) ->
+ Ref = make_ref(),
+ ?line self() ! {result,Ref,42},
+ ?line 42 = export_1(Ref),
+ ?line {error,timeout} = export_1(Ref),
+ ok.
+
+export_1(Reference) ->
+ id(Reference),
+ receive
+ {result,Reference,Result} ->
+ Result
+ after 1 ->
+ Result = {error,timeout}
+ end,
+ %% Result ({x,1}) is used, but not the return value ({x,0})
+ %% of the receive. Used to be incorrectly optimized
+ %% by beam_block.
+ id({build,self()}),
+ Result.
+
id(I) -> I.
diff --git a/lib/compiler/test/record_SUITE.erl b/lib/compiler/test/record_SUITE.erl
index f26ff769c7..65b96590ed 100644
--- a/lib/compiler/test/record_SUITE.erl
+++ b/lib/compiler/test/record_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,9 +20,11 @@
-module(record_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
errors/1,record_test_2/1,record_test_3/1,record_access_in_guards/1,
guard_opt/1,eval_once/1,foobar/1,missing_test_heap/1, nested_access/1]).
@@ -30,15 +32,34 @@ init_per_testcase(_Case, Config) ->
?line Dog = test_server:timetrap(test_server:minutes(2)),
[{watchdog,Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [errors,record_test_2,record_test_3,record_access_in_guards,
- guard_opt,eval_once,foobar,missing_test_heap,nested_access].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(record_SUITE),
+ [errors, record_test_2, record_test_3,
+ record_access_in_guards, guard_opt, eval_once, foobar,
+ missing_test_heap, nested_access].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-record(foo, {a,b,c,d}).
-record(bar, {a,b,c,d}).
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index 05236ee010..53d8c04169 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,8 +19,8 @@
-module(test_lib).
-include("test_server.hrl").
-
--export([recompile/1,opt_opts/1,get_data_dir/1,smoke_disasm/1,p_run/2]).
+-compile({no_auto_import,[binary_part/2]}).
+-export([recompile/1,opt_opts/1,get_data_dir/1,smoke_disasm/1,p_run/2,binary_part/2]).
recompile(Mod) when is_atom(Mod) ->
case whereis(cover_server) of
@@ -41,7 +41,7 @@ smoke_disasm(Mod) when is_atom(Mod) ->
smoke_disasm(code:which(Mod));
smoke_disasm(File) when is_list(File) ->
Res = beam_disasm:file(File),
- {beam_file,Mod} = {element(1, Res),element(2, Res)}.
+ {beam_file,_Mod} = {element(1, Res),element(2, Res)}.
%% Retrieve the "interesting" compiler options (options for optimization
%% and compatibility) for the given module.
@@ -62,16 +62,16 @@ opt_opts(Mod) ->
(_) -> false
end, Opts).
-%% Some test suites gets cloned (e.g. to "record_SUITE" to "record_no_opt_SUITE"),
-%% but the data directory is not cloned. This function retrieves the path to
-%% the original data directory.
+%% Some test suites gets cloned (e.g. to "record_SUITE" to
+%% "record_no_opt_SUITE"), but the data directory is not cloned.
+%% This function retrieves the path to the original data directory.
get_data_dir(Config) ->
Data0 = ?config(data_dir, Config),
- {ok,Data1,_} = regexp:sub(Data0, "_no_opt_SUITE", "_SUITE"),
- {ok,Data2,_} = regexp:sub(Data1, "_post_opt_SUITE", "_SUITE"),
- {ok,Data,_} = regexp:sub(Data2, "_inline_SUITE", "_SUITE"),
- Data.
+ Opts = [{return,list}],
+ Data1 = re:replace(Data0, "_no_opt_SUITE", "_SUITE", Opts),
+ Data = re:replace(Data1, "_post_opt_SUITE", "_SUITE", Opts),
+ re:replace(Data, "_inline_SUITE", "_SUITE", Opts).
%% p_run(fun(Data) -> ok|error, List) -> ok
%% Will fail the test case if there were any errors.
@@ -104,3 +104,7 @@ p_run_loop(Test, List, N, Refs0, Errors0, Ws0) ->
Refs = Refs0 -- [Ref],
p_run_loop(Test, List, N, Refs, Errors, Ws)
end.
+
+%% This is for the misc_SUITE:override_bif testcase
+binary_part(_A,_B) ->
+ dummy.
diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl
index c2f6dc24be..92a79d3cba 100644
--- a/lib/compiler/test/trycatch_SUITE.erl
+++ b/lib/compiler/test/trycatch_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,21 +18,40 @@
%%
-module(trycatch_SUITE).
--export([all/1,basic/1,lean_throw/1,try_of/1,try_after/1,%after_bind/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,basic/1,lean_throw/1,
+ try_of/1,try_after/1,%after_bind/1,
catch_oops/1,after_oops/1,eclectic/1,rethrow/1,
nested_of/1,nested_catch/1,nested_after/1,
nested_horrid/1,last_call_optimization/1,bool/1,
plain_catch_coverage/1,andalso_orelse/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(trycatch_SUITE),
+ [basic, lean_throw, try_of, try_after, catch_oops,
+ after_oops, eclectic, rethrow, nested_of, nested_catch,
+ nested_after, nested_horrid, last_call_optimization,
+ bool, plain_catch_coverage, andalso_orelse].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [basic,lean_throw,try_of,try_after,%after_bind,
- catch_oops,after_oops,eclectic,rethrow,
- nested_of,nested_catch,nested_after,
- nested_horrid,last_call_optimization,
- bool,plain_catch_coverage,andalso_orelse].
basic(Conf) when is_list(Conf) ->
diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl
index 5ed8836c70..dd18a6e1a3 100644
--- a/lib/compiler/test/warnings_SUITE.erl
+++ b/lib/compiler/test/warnings_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,12 +26,14 @@
-define(privdir, "warnings_SUITE_priv").
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Conf)).
-define(privdir, ?config(priv_dir, Conf)).
-endif.
--export([all/1,init_per_testcase/2,fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2]).
-export([pattern/1,pattern2/1,pattern3/1,pattern4/1,
guard/1,bad_arith/1,bool_cases/1,bad_apply/1,
@@ -44,16 +46,34 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- test_lib:recompile(?MODULE),
- [pattern,pattern2,pattern3,pattern4,
- guard,bad_arith,bool_cases,bad_apply,files,effect,
- bin_opt_info,bin_construction].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ test_lib:recompile(warnings_SUITE),
+ [pattern, pattern2, pattern3, pattern4, guard,
+ bad_arith, bool_cases, bad_apply, files, effect,
+ bin_opt_info, bin_construction].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
pattern(Config) when is_list(Config) ->
%% Test warnings generated by v3_core.
@@ -433,6 +453,16 @@ effect(Config) when is_list(Config) ->
true -> ok
end,
ok.
+
+ m8(A, B) ->
+ case {A,B} of
+ V -> V
+ end,
+ ok.
+
+ m9(Bs) ->
+ [{B,ok} = {B,foo:bar(B)} || B <- Bs],
+ ok.
">>,
[],
{warnings,[{5,sys_core_fold,{no_effect,{erlang,is_integer,1}}},
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index a5e6de7b5f..e46096a6df 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 4.6.5
+COMPILER_VSN = 4.7.3
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml
index e579d6f6f4..95941fefdd 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ConsumerAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml
index 809bf89762..51f9f11613 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_EventChannel.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml
index 811c8615b9..9690c9406d 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml
index 6c22c5ed39..fb17c450f4 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPullSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml
index 2b50f8858a..21e6cfce6f 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml
index cda162f4cd..be2dfcafbe 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_ProxyPushSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml b/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml
index abcd7b6c1f..ca301bb860 100644
--- a/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml
+++ b/lib/cosEvent/doc/src/CosEventChannelAdmin_SupplierAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/ch_contents.xml b/lib/cosEvent/doc/src/ch_contents.xml
index bc2838b36d..943e00b967 100644
--- a/lib/cosEvent/doc/src/ch_contents.xml
+++ b/lib/cosEvent/doc/src/ch_contents.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/ch_introduction.xml b/lib/cosEvent/doc/src/ch_introduction.xml
index 8f948a5530..101c3e1212 100644
--- a/lib/cosEvent/doc/src/ch_introduction.xml
+++ b/lib/cosEvent/doc/src/ch_introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/cosEventApp.xml b/lib/cosEvent/doc/src/cosEventApp.xml
index d83f44acb1..55ea790203 100644
--- a/lib/cosEvent/doc/src/cosEventApp.xml
+++ b/lib/cosEvent/doc/src/cosEventApp.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2001</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEvent/doc/src/notes.xml b/lib/cosEvent/doc/src/notes.xml
index b6c4531901..1a5c8afa17 100644
--- a/lib/cosEvent/doc/src/notes.xml
+++ b/lib/cosEvent/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2010</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,42 +33,48 @@
</header>
<section>
- <title>cosEvent 2.1.9</title>
+ <title>cosEvent 2.1.10</title>
<section>
<title>Improvements and New Features</title>
<list type="bulleted">
<item>
<p>
- Test suites published.</p>
+ Eliminated Dialyzer warnings when using exit or throw.</p>
<p>
- Own Id: OTP-8543 Aux Id:</p>
+ Own Id: OTP-9050 Aux Id:</p>
</item>
</list>
</section>
</section>
<section>
- <title>cosEvent 2.1.8</title>
+ <title>cosEvent 2.1.9</title>
<section>
<title>Improvements and New Features</title>
<list type="bulleted">
<item>
<p>
- Removed the usage of the codeinclude tag in the documentation.</p>
+ Test suites published.</p>
<p>
- Own Id: OTP-8409 Aux Id:</p>
+ Own Id: OTP-8543 Aux Id:</p>
</item>
</list>
</section>
+ </section>
+
+ <section>
+ <title>cosEvent 2.1.8</title>
<section>
- <title>Fixed Bugs and Malfunctions</title>
+ <title>Improvements and New Features</title>
<list type="bulleted">
<item>
- <p>The documentation EIX file was not generated.</p>
- <p>Own id: OTP-8355 Aux Id:</p>
+ <p>
+ Removed the usage of the codeinclude tag in the documentation.</p>
+ <p>
+ Own Id: OTP-8409 Aux Id:</p>
</item>
</list>
</section>
diff --git a/lib/cosEvent/src/cosEventApp.erl b/lib/cosEvent/src/cosEventApp.erl
index 084490f845..143c241448 100644
--- a/lib/cosEvent/src/cosEventApp.erl
+++ b/lib/cosEvent/src/cosEventApp.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,26 +66,31 @@
%% Effect : Install necessary data in the IFR DB
%%------------------------------------------------------------
install() ->
- install_loop(?IDL_MODULES, []).
+ case install_loop(?IDL_MODULES, []) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end.
install_loop([], _) ->
ok;
install_loop([H|T], Accum) ->
case catch H:'oe_register'() of
{'EXIT',{unregistered,App}} ->
- ?write_ErrorMsg("Unable to register '~p'; application ~p not registered.
-Trying to unregister ~p~n", [H,App,Accum]),
+ ?write_ErrorMsg("Unable to register '~p'; application ~p not registered.\n"
+ "Trying to unregister ~p~n", [H,App,Accum]),
uninstall_loop(Accum, {exit, register});
{'EXCEPTION',_} ->
- ?write_ErrorMsg("Unable to register '~p'; propably already registered.
-You are adviced to confirm this.
-Trying to unregister ~p~n", [H,Accum]),
+ ?write_ErrorMsg("Unable to register '~p'; propably already registered.\n"
+ "You are adviced to confirm this.\n"
+ "Trying to unregister ~p~n", [H,Accum]),
uninstall_loop(Accum, {exit, register});
ok ->
install_loop(T, [H|Accum]);
_ ->
- ?write_ErrorMsg("Unable to register '~p'; reason unknown.
-Trying to unregister ~p~n", [H,Accum]),
+ ?write_ErrorMsg("Unable to register '~p'; reason unknown.\n"
+ "Trying to unregister ~p~n", [H,Accum]),
uninstall_loop(Accum, {exit, register})
end.
@@ -96,27 +101,32 @@ Trying to unregister ~p~n", [H,Accum]),
%% Effect : Remove data related to cosEvent from the IFR DB
%%------------------------------------------------------------
uninstall() ->
- uninstall_loop(lists:reverse(?IDL_MODULES), ok).
+ case uninstall_loop(lists:reverse(?IDL_MODULES), ok) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end.
uninstall_loop([],ok) ->
ok;
uninstall_loop([],{exit, register}) ->
- exit({?MODULE, "oe_register failed"});
+ {error, {?MODULE, "oe_register failed"}};
uninstall_loop([],{exit, unregister}) ->
- exit({?MODULE, "oe_unregister failed"});
+ {error, {?MODULE, "oe_unregister failed"}};
uninstall_loop([],{exit, both}) ->
- exit({?MODULE, "oe_register and, for some of those already registered, oe_unregister failed"});
+ {error, {?MODULE, "oe_register and, for some of those already registered, oe_unregister failed"}};
uninstall_loop([H|T], Status) ->
case catch H:'oe_unregister'() of
ok ->
uninstall_loop(T, Status);
_ when Status == ok ->
- ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.
-You are adviced to confirm this.~n",[H]),
+ ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.\n"
+ "You are adviced to confirm this.\n",[H]),
uninstall_loop(T, {exit, unregister});
_ ->
- ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.
-You are adviced to confirm this.~n",[H]),
+ ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.\n"
+ "You are adviced to confirm this.\n",[H]),
uninstall_loop(T, {exit, both})
end.
diff --git a/lib/cosEvent/test/Makefile b/lib/cosEvent/test/Makefile
index 3d95075ee1..c59c7ee315 100644
--- a/lib/cosEvent/test/Makefile
+++ b/lib/cosEvent/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/cosEvent_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = cosEvent.spec
+COVER_FILE = cosEvent.cover
IDL_FILES = \
@@ -146,7 +147,7 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
- $(ERL_FILES) $(RELSYSDIR)
+ $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR)
$(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \
diff --git a/lib/cosEvent/test/cosEvent.cover b/lib/cosEvent/test/cosEvent.cover
new file mode 100644
index 0000000000..df12ea3ca9
--- /dev/null
+++ b/lib/cosEvent/test/cosEvent.cover
@@ -0,0 +1,2 @@
+{incl_app,cosEvent,details}.
+
diff --git a/lib/cosEvent/test/cosEvent.spec b/lib/cosEvent/test/cosEvent.spec
index 910f7a7c28..f793693779 100644
--- a/lib/cosEvent/test/cosEvent.spec
+++ b/lib/cosEvent/test/cosEvent.spec
@@ -1,19 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../cosEvent_test"}}.
+{suites,"../cosEvent_test",all}.
diff --git a/lib/cosEvent/test/event_channel_SUITE.erl b/lib/cosEvent/test/event_channel_SUITE.erl
index 2b0cf1fe30..9017f489bf 100644
--- a/lib/cosEvent/test/event_channel_SUITE.erl
+++ b/lib/cosEvent/test/event_channel_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-module(event_channel_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -53,21 +53,33 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, event_objects_api/1, events_api/1, events_sync_api/1,
- cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2, app_test/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ event_objects_api/1, events_api/1, events_sync_api/1,
+ cases/0, init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2, app_test/1]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosEvent interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [events_api, events_sync_api, event_objects_api, app_test].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [events_api, events_sync_api, event_objects_api,
+ app_test].
%%-----------------------------------------------------------------
%% Init and cleanup functions.
@@ -78,12 +90,12 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
mnesia:delete_schema([node()]),
@@ -96,7 +108,7 @@ init_all(Config) when is_list(Config) ->
oe_event_test_server:oe_register(),
Config.
-finish_all(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
oe_event_test_server:oe_unregister(),
cosEventApp:stop(),
cosEventApp:uninstall(),
diff --git a/lib/cosEvent/test/event_test_PullC_impl.erl b/lib/cosEvent/test/event_test_PullC_impl.erl
index 186d1cbd51..4b81572cad 100644
--- a/lib/cosEvent/test/event_test_PullC_impl.erl
+++ b/lib/cosEvent/test/event_test_PullC_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEvent/test/event_test_PullS_impl.erl b/lib/cosEvent/test/event_test_PullS_impl.erl
index b7fa0c34f0..81685980fb 100644
--- a/lib/cosEvent/test/event_test_PullS_impl.erl
+++ b/lib/cosEvent/test/event_test_PullS_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEvent/test/event_test_PushC_impl.erl b/lib/cosEvent/test/event_test_PushC_impl.erl
index 6eadf74a31..c2be1d4c08 100644
--- a/lib/cosEvent/test/event_test_PushC_impl.erl
+++ b/lib/cosEvent/test/event_test_PushC_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEvent/test/event_test_PushS_impl.erl b/lib/cosEvent/test/event_test_PushS_impl.erl
index da82e97211..35cfc66e6b 100644
--- a/lib/cosEvent/test/event_test_PushS_impl.erl
+++ b/lib/cosEvent/test/event_test_PushS_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosEvent/test/generated_SUITE.erl b/lib/cosEvent/test/generated_SUITE.erl
index 2d75b18451..e1e4e719b0 100644
--- a/lib/cosEvent/test/generated_SUITE.erl
+++ b/lib/cosEvent/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -71,12 +71,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -84,19 +84,42 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['CosEventChannelAdmin_AlreadyConnected', 'CosEventChannelAdmin_TypeError',
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['CosEventChannelAdmin_AlreadyConnected',
+ 'CosEventChannelAdmin_TypeError',
'CosEventComm_Disconnected',
- 'CosEventChannelAdmin_ConsumerAdmin', 'CosEventChannelAdmin_EventChannel',
- 'CosEventChannelAdmin_ProxyPullConsumer', 'CosEventChannelAdmin_ProxyPullSupplier',
- 'CosEventChannelAdmin_ProxyPushConsumer', 'CosEventChannelAdmin_ProxyPushSupplier',
- 'CosEventChannelAdmin_SupplierAdmin', oe_CosEventComm_CAdmin,
- oe_CosEventComm_Channel, oe_CosEventComm_Event, oe_CosEventComm_PullerS,
- oe_CosEventComm_PusherS, 'CosEventComm_PullConsumer',
- 'CosEventComm_PullSupplier', 'CosEventComm_PushConsumer',
+ 'CosEventChannelAdmin_ConsumerAdmin',
+ 'CosEventChannelAdmin_EventChannel',
+ 'CosEventChannelAdmin_ProxyPullConsumer',
+ 'CosEventChannelAdmin_ProxyPullSupplier',
+ 'CosEventChannelAdmin_ProxyPushConsumer',
+ 'CosEventChannelAdmin_ProxyPushSupplier',
+ 'CosEventChannelAdmin_SupplierAdmin',
+ oe_CosEventComm_CAdmin, oe_CosEventComm_Channel,
+ oe_CosEventComm_Event, oe_CosEventComm_PullerS,
+ oe_CosEventComm_PusherS, 'CosEventComm_PullConsumer',
+ 'CosEventComm_PullSupplier',
+ 'CosEventComm_PushConsumer',
'CosEventComm_PushSupplier'].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -105,7 +128,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/cosEvent/vsn.mk b/lib/cosEvent/vsn.mk
index 9f326b39b6..38999db5fa 100644
--- a/lib/cosEvent/vsn.mk
+++ b/lib/cosEvent/vsn.mk
@@ -1,15 +1,3 @@
-COSEVENT_VSN = 2.1.9
+COSEVENT_VSN = 2.1.10
-TICKETS = OTP-8543
-
-TICKETS_2.1.8 = OTP-8355 \
- OTP-8409
-
-TICKETS_2.1.7 = OTP-8201
-
-TICKETS_2.1.6 = OTP-7987
-
-TICKETS_2.1.5 = OTP-7837
-
-TICKETS_2.1.4 = OTP-7595
diff --git a/lib/cosEventDomain/doc/src/CosEventDomainAdmin.xml b/lib/cosEventDomain/doc/src/CosEventDomainAdmin.xml
index d0aac961d7..60f26dda96 100644
--- a/lib/cosEventDomain/doc/src/CosEventDomainAdmin.xml
+++ b/lib/cosEventDomain/doc/src/CosEventDomainAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml b/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml
index 0720a4b930..ea605f23a0 100644
--- a/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml
+++ b/lib/cosEventDomain/doc/src/CosEventDomainAdmin_EventDomainFactory.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2001</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEventDomain/doc/src/cosEventDomainApp.xml b/lib/cosEventDomain/doc/src/cosEventDomainApp.xml
index fe8df55929..e7704b90b5 100644
--- a/lib/cosEventDomain/doc/src/cosEventDomainApp.xml
+++ b/lib/cosEventDomain/doc/src/cosEventDomainApp.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2001</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosEventDomain/doc/src/notes.xml b/lib/cosEventDomain/doc/src/notes.xml
index deb1985c86..522dcea829 100644
--- a/lib/cosEventDomain/doc/src/notes.xml
+++ b/lib/cosEventDomain/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2010</year>
+ <year>2001</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,7 +32,24 @@
</header>
<section>
+ <title>cosEventDomain 1.1.10</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Eliminated Dialyzer warnings when using exit or throw.</p>
+ <p>
+ Own Id: OTP-9050 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
<title>cosEventDomain 1.1.9</title>
+
<section>
<title>Improvements and New Features</title>
<list type="bulleted">
@@ -44,7 +61,6 @@
</item>
</list>
</section>
-
</section>
<section>
diff --git a/lib/cosEventDomain/src/CosEventDomainAdmin_EventDomain_impl.erl b/lib/cosEventDomain/src/CosEventDomainAdmin_EventDomain_impl.erl
index 0b73100540..f5dd6d5c14 100644
--- a/lib/cosEventDomain/src/CosEventDomainAdmin_EventDomain_impl.erl
+++ b/lib/cosEventDomain/src/CosEventDomainAdmin_EventDomain_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -778,12 +778,17 @@ get_qos(_OE_This, #state{cyclic = Cyclic, diamonds = Diamonds} = State) ->
%%----------------------------------------------------------------------
set_qos(_OE_This, State, NewQoS) ->
QoS = cosEventDomainApp:get_qos(NewQoS),
- set_qos_helper(QoS, State, []).
+ case set_qos_helper(QoS, State, []) of
+ {ok, NewState} ->
+ {reply, ok, NewState};
+ {error, Errors} ->
+ corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Errors})
+ end.
set_qos_helper([], State, []) ->
- {reply, ok, State};
+ {ok, State}; %{reply, ok, State};
set_qos_helper([], _, Errors) ->
- corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Errors});
+ {error, Errors};
set_qos_helper([{?DiamondDetection, Diamonds}|T], #state{diamonds = Diamonds} = State,
Errors) ->
set_qos_helper(T, State, Errors);
@@ -828,12 +833,17 @@ set_qos_helper([{?CycleDetection, _}|T], #state{cyclic = Cyclic} = State, Errors
%%----------------------------------------------------------------------
validate_qos(_OE_This, State, WantedQoS) ->
QoS = cosEventDomainApp:get_qos(WantedQoS),
- {reply, {ok, validate_qos_helper(QoS, State, [], [])}, State}.
+ case validate_qos_helper(QoS, State, [], []) of
+ {ok, Properties} ->
+ {reply, {ok, Properties}, State};
+ {error, Errors} ->
+ corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Errors})
+ end.
validate_qos_helper([], _, Properties, []) ->
- Properties;
+ {ok, Properties};
validate_qos_helper([], _, _, Errors) ->
- corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Errors});
+ {error, Errors};
validate_qos_helper([{?DiamondDetection, ?ForbidDiamonds}|T], State, Properties,
Errors) ->
case get_diamonds_helper(State, false) of
diff --git a/lib/cosEventDomain/src/cosEventDomainApp.erl b/lib/cosEventDomain/src/cosEventDomainApp.erl
index d57f51443c..734e4deccb 100644
--- a/lib/cosEventDomain/src/cosEventDomainApp.erl
+++ b/lib/cosEventDomain/src/cosEventDomainApp.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -270,12 +270,17 @@ create_id() ->
get_qos([]) ->
[];
get_qos(Properties) ->
- get_qos(Properties, [], []).
+ case get_qos(Properties, [], []) of
+ {ok, Supported} ->
+ Supported;
+ {error, Unsupported} ->
+ corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupported})
+ end.
get_qos([], Supported, []) ->
- Supported;
+ {ok, Supported};
get_qos([], _, Unsupported) ->
- corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupported});
+ {error, Unsupported};
get_qos([#'CosNotification_Property'{name = ?CycleDetection,
value= #any{value = ?AuthorizeCycles}}|T],
Supported, Unsupported) ->
diff --git a/lib/cosEventDomain/test/Makefile b/lib/cosEventDomain/test/Makefile
index 9893b05b8c..160c8565e8 100644
--- a/lib/cosEventDomain/test/Makefile
+++ b/lib/cosEventDomain/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/cosEventDomain_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = cosEventDomain.spec
+COVER_FILE = cosEventDomain.cover
MODULES = \
@@ -99,6 +100,6 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(TEST_SPEC_FILE) \
- $(ERL_FILES) $(RELSYSDIR)
+ $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
diff --git a/lib/cosEventDomain/test/cosEventDomain.cover b/lib/cosEventDomain/test/cosEventDomain.cover
new file mode 100644
index 0000000000..f87f6d97bf
--- /dev/null
+++ b/lib/cosEventDomain/test/cosEventDomain.cover
@@ -0,0 +1,2 @@
+{incl_app,cosEventDomain,details}.
+
diff --git a/lib/cosEventDomain/test/cosEventDomain.spec b/lib/cosEventDomain/test/cosEventDomain.spec
index 0d3e307071..bcee74c5f1 100644
--- a/lib/cosEventDomain/test/cosEventDomain.spec
+++ b/lib/cosEventDomain/test/cosEventDomain.spec
@@ -1,19 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../cosEventDomain_test"}}.
+{suites,"../cosEventDomain_test",all}.
diff --git a/lib/cosEventDomain/test/event_domain_SUITE.erl b/lib/cosEventDomain/test/event_domain_SUITE.erl
index ddf0af3489..d568708429 100644
--- a/lib/cosEventDomain/test/event_domain_SUITE.erl
+++ b/lib/cosEventDomain/test/event_domain_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-module(event_domain_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("cosNotification/include/CosNotifyChannelAdmin.hrl").
-include_lib("cosNotification/include/CosNotification.hrl").
@@ -56,20 +56,31 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, event_domain_api/1, event_domain_factory_api/1,
- cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2, app_test/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ event_domain_api/1, event_domain_factory_api/1,
+ cases/0, init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2, app_test/1]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosEventDomain interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber, cosNotification],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
[event_domain_api, event_domain_factory_api, app_test].
%%-----------------------------------------------------------------
@@ -81,12 +92,12 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
mnesia:delete_schema([node()]),
mnesia:create_schema([node()]),
ok = corba:orb_init([{flags, 16#02},
@@ -102,7 +113,7 @@ init_all(Config) when is_list(Config) ->
cosEventDomainApp:start(),
Config.
-finish_all(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
cosEventDomainApp:stop(),
cosEventDomainApp:uninstall(),
cosNotificationApp:stop(),
diff --git a/lib/cosEventDomain/test/generated_SUITE.erl b/lib/cosEventDomain/test/generated_SUITE.erl
index 6c6996ca79..e8dbafbe75 100644
--- a/lib/cosEventDomain/test/generated_SUITE.erl
+++ b/lib/cosEventDomain/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -71,12 +71,11 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -84,17 +83,41 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['CosEventDomainAdmin', 'CosEventDomainAdmin_DiamondSeq',
- 'CosEventDomainAdmin_AlreadyExists', 'CosEventDomainAdmin_DomainIDSeq',
- 'CosEventDomainAdmin_Connection', 'CosEventDomainAdmin_ConnectionIDSeq',
- 'CosEventDomainAdmin_ConnectionNotFound', 'CosEventDomainAdmin_CycleCreationForbidden',
- 'CosEventDomainAdmin_CycleSeq', 'CosEventDomainAdmin_DiamondCreationForbidden',
- 'CosEventDomainAdmin_DomainNotFound', 'CosEventDomainAdmin_MemberIDSeq',
- 'CosEventDomainAdmin_RouteSeq', 'CosEventDomainAdmin_EventDomainFactory',
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['CosEventDomainAdmin',
+ 'CosEventDomainAdmin_DiamondSeq',
+ 'CosEventDomainAdmin_AlreadyExists',
+ 'CosEventDomainAdmin_DomainIDSeq',
+ 'CosEventDomainAdmin_Connection',
+ 'CosEventDomainAdmin_ConnectionIDSeq',
+ 'CosEventDomainAdmin_ConnectionNotFound',
+ 'CosEventDomainAdmin_CycleCreationForbidden',
+ 'CosEventDomainAdmin_CycleSeq',
+ 'CosEventDomainAdmin_DiamondCreationForbidden',
+ 'CosEventDomainAdmin_DomainNotFound',
+ 'CosEventDomainAdmin_MemberIDSeq',
+ 'CosEventDomainAdmin_RouteSeq',
+ 'CosEventDomainAdmin_EventDomainFactory',
'CosEventDomainAdmin_EventDomain'].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -103,7 +126,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/cosEventDomain/vsn.mk b/lib/cosEventDomain/vsn.mk
index dceee34181..f4a77ab7a8 100644
--- a/lib/cosEventDomain/vsn.mk
+++ b/lib/cosEventDomain/vsn.mk
@@ -1,15 +1,3 @@
-COSEVENTDOMAIN_VSN = 1.1.9
+COSEVENTDOMAIN_VSN = 1.1.10
-TICKETS = OTP-8543
-
-TICKETS_1.1.8 = OTP-8353 \
- OTP-8355
-
-TICKETS_1.1.7 = OTP-8201
-
-TICKETS_1.1.6 = OTP-7987
-
-TICKETS_1.1.5 = OTP-7837
-
-TICKETS_1.1.4 = OTP-7595
diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml
index 9499f7019e..af9141b205 100644
--- a/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml
+++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_Directory.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml
index e5050eaffb..bef7cb882f 100644
--- a/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml
+++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_File.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml b/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml
index a43482eccf..8aa02b2153 100644
--- a/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml
+++ b/lib/cosFileTransfer/doc/src/CosFileTransfer_VirtualFileSystem.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosFileTransfer/test/Makefile b/lib/cosFileTransfer/test/Makefile
new file mode 100644
index 0000000000..b46fb35356
--- /dev/null
+++ b/lib/cosFileTransfer/test/Makefile
@@ -0,0 +1,133 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(COSFILETRANSFER_VSN)
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/cosFileTransfer_test
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+TEST_SPEC_FILE = cosFileTransfer.spec
+COVER_FILE = cosFileTransfer.cover
+
+
+IDL_FILES =
+
+IDLOUTDIR = idl_output
+
+MODULES = \
+ fileTransfer_SUITE \
+
+GEN_MODULES = \
+
+GEN_HRL_FILES = \
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+HRL_FILES =
+
+GEN_FILES = \
+ $(GEN_HRL_FILES:%=$(IDLOUTDIR)/%) \
+ $(GEN_MODULES:%=$(IDLOUTDIR)/%.erl)
+
+GEN_TARGET_FILES = $(GEN_MODULES:%=$(IDLOUTDIR)/%.$(EMULATOR))
+
+SUITE_TARGET_FILES = $(MODULES:%=%.$(EMULATOR))
+
+TARGET_FILES = \
+ $(GEN_TARGET_FILES) \
+ $(SUITE_TARGET_FILES)
+
+
+# ----------------------------------------------------
+# PROGRAMS
+# ----------------------------------------------------
+LOCAL_CLASSPATH = $(ERL_TOP)lib/cosFileTransfer/priv:$(ERL_TOP)lib/cosFileTransfer/test
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_IDL_FLAGS += -pa $(ERL_TOP)/lib/cosFileTransfer/ebin \
+ -pa $(ERL_TOP)/lib/cosFileTransfer/src \
+ -pa $(ERL_TOP)/lib/cosFileTransfer/include \
+ -pa $(ERL_TOP)/lib/cosProperty/ebin \
+ -pa $(ERL_TOP)/lib/cosProperty/include \
+ -pa $(ERL_TOP)/lib/orber/ebin \
+ -pa $(ERL_TOP)/lib/ic/ebin
+
+ERL_COMPILE_FLAGS += \
+ $(ERL_IDL_FLAGS) \
+ -pa $(ERL_TOP)/lib/orber/include \
+ -pa $(ERL_TOP)/lib/cosProperty/include \
+ -pa $(ERL_TOP)/internal_tools/test_server/ebin \
+ -pa $(ERL_TOP)/lib/cosFileTransfer/ebin \
+ -pa $(ERL_TOP)/lib/cosFileTransfer/include \
+ -pa $(ERL_TOP)/lib/cosFileTransfer/test/idl_output \
+ -I$(ERL_TOP)/lib/orber/include \
+ -I$(ERL_TOP)/lib/cosProperty/include \
+ -I$(ERL_TOP)/lib/cosFileTransfer/src \
+ -I$(ERL_TOP)/lib/cosFileTransfer/include \
+ -I$(ERL_TOP)/lib/cosFileTransfer \
+ -I$(ERL_TOP)/lib/cosFileTransfer/test/$(IDLOUTDIR) \
+ -I$(ERL_TOP)/lib/test_server/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+
+tests debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f idl_output/*
+ rm -f $(TARGET_FILES)
+ rm -f errs core *~
+
+docs:
+
+# ----------------------------------------------------
+# Special Targets
+# ----------------------------------------------------
+
+# ----------------------------------------------------
+# Release Targets
+# ----------------------------------------------------
+# We don't copy generated intermediate erlang and hrl files
+
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+
+release_docs_spec:
+
+release_tests_spec: tests
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
+ $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
diff --git a/lib/cosFileTransfer/test/cosFileTransfer.cover b/lib/cosFileTransfer/test/cosFileTransfer.cover
new file mode 100644
index 0000000000..063dd66990
--- /dev/null
+++ b/lib/cosFileTransfer/test/cosFileTransfer.cover
@@ -0,0 +1,2 @@
+{incl_app,cosFileTransfer,details}.
+
diff --git a/lib/cosFileTransfer/test/cosFileTransfer.spec b/lib/cosFileTransfer/test/cosFileTransfer.spec
new file mode 100644
index 0000000000..290b27d048
--- /dev/null
+++ b/lib/cosFileTransfer/test/cosFileTransfer.spec
@@ -0,0 +1 @@
+{suites,"../cosFileTransfer_test", all}.
diff --git a/lib/cosFileTransfer/test/fileTransfer_SUITE.erl b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
new file mode 100644
index 0000000000..e94c307ef8
--- /dev/null
+++ b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl
@@ -0,0 +1,972 @@
+%%-----------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% File : fileTransfer_SUITE.erl
+%% Purpose :
+%%----------------------------------------------------------------------
+
+-module(fileTransfer_SUITE).
+
+%%--------------- INCLUDES -----------------------------------
+-include_lib("cosFileTransfer/src/cosFileTransferApp.hrl").
+
+-include_lib("test_server/include/test_server.hrl").
+
+%%--------------- DEFINES ------------------------------------
+-define(default_timeout, ?t:minutes(20)).
+-define(match(ExpectedRes, Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ case AcTuAlReS of
+ ExpectedRes ->
+ io:format("------ CORRECT RESULT ------~n~p~n",
+ [AcTuAlReS]),
+ AcTuAlReS;
+ _ ->
+ io:format("###### ERROR ERROR ######~n~p~n",
+ [AcTuAlReS]),
+ exit(AcTuAlReS)
+ end
+ end()).
+
+-define(matchnopr(ExpectedRes, Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ case AcTuAlReS of
+ ExpectedRes ->
+ io:format("------ CORRECT RESULT (~p) ------~n", [?LINE]),
+ AcTuAlReS;
+ _ ->
+ io:format("###### ERROR ERROR ######~n~p~n",
+ [AcTuAlReS]),
+ exit(AcTuAlReS)
+ end
+ end()).
+
+
+
+
+
+%%-----------------------------------------------------------------
+%% External exports
+%%-----------------------------------------------------------------
+-export([all/0,suite/0,groups/0,
+ init_per_group/2,end_per_group/2,
+ cases/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ fileIterator_api/1,
+ fts_ftp_file_api/1,
+ fts_ftp_file_ssl_api/1,
+ fts_ftp_dir_api/1,
+ fts_native_file_api/1,
+ fts_native_file_ssl_api/1,
+ fts_native_dir_api/1,
+ init_per_testcase/2,
+ end_per_testcase/2,
+ install_data/2,
+ uninstall_data/1,
+ slave_sup/0,
+ app_test/1]).
+
+%%-----------------------------------------------------------------
+%% Func: all/1
+%% Args:
+%% Returns:
+%%-----------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [fts_ftp_dir_api, fts_ftp_file_api,
+ fts_ftp_file_ssl_api, fts_native_dir_api,
+ fts_native_file_api, fts_native_file_ssl_api,
+ fileIterator_api, app_test].
+
+%%-----------------------------------------------------------------
+%% Init and cleanup functions.
+%%-----------------------------------------------------------------
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) ->
+ case code:which(crypto) of
+ Res when is_atom(Res) ->
+ {skip,"Could not start crypto!"};
+ _Else ->
+ orber:jump_start(),
+ cosProperty:install(),
+ cosProperty:start(),
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ %% Client
+ cosFileTransferApp:configure(ssl_client_certfile,
+ filename:join([Dir, "client", "cert.pem"])),
+ cosFileTransferApp:configure(ssl_client_cacertfile,
+ filename:join([Dir, "client", "cacerts.pem"])),
+ cosFileTransferApp:configure(ssl_client_verify, 1),
+ cosFileTransferApp:configure(ssl_client_depth, 0),
+ %% Server
+ cosFileTransferApp:configure(ssl_server_certfile,
+ filename:join([Dir, "server", "cert.pem"])),
+ cosFileTransferApp:configure(ssl_server_cacertfile,
+ filename:join([Dir, "server", "cacerts.pem"])),
+ cosFileTransferApp:configure(ssl_server_verify, 1),
+ cosFileTransferApp:configure(ssl_server_depth, 0),
+ crypto:start(),
+ ssl:start(),
+ cosFileTransferApp:install(),
+ cosFileTransferApp:start(),
+ if
+ is_list(Config) ->
+ Config;
+ true ->
+ exit("Config not a list")
+ end
+ end.
+
+end_per_suite(Config) ->
+ ssl:stop(),
+ crypto:stop(),
+ cosFileTransferApp:stop(),
+ cosProperty:stop(),
+ cosProperty:uninstall(),
+ cosFileTransferApp:uninstall(),
+ orber:jump_stop(),
+ Config.
+
+%%-----------------------------------------------------------------
+%% Local definitions
+%%-----------------------------------------------------------------
+-define(FTP_USER, "anonymous").
+-define(FTP_PASS, "fileTransfer_SUITE@localhost").
+-define(TEST_DIR,["/", "incoming"]).
+
+
+-define(FTP_PORT, 21).
+-define(FTP_ACC, "anonymous").
+
+-define(BAD_HOST, "badhostname").
+-define(BAD_USER, "baduser").
+-define(BAD_DIR, "baddirectory").
+
+-define(TEST_FILE_DATA, "If this file exists after a completed test an error occurred.").
+-define(TEST_FILE_DATA2, "1234567890123").
+
+
+%%-----------------------------------------------------------------
+%% aoo-file test
+%%-----------------------------------------------------------------
+app_test(doc) -> [];
+app_test(suite) -> [];
+app_test(_Config) ->
+ ?line ok=?t:app_test(cosFileTransfer),
+ ok.
+
+%%-----------------------------------------------------------------
+%% FileIterator API tests
+%%-----------------------------------------------------------------
+fileIterator_api(doc) -> ["CosFileTransfer FileIterator API tests.", ""];
+fileIterator_api(suite) -> [];
+fileIterator_api(Config) ->
+ case ftp_host(Config) of
+ {skipped, SkippedReason} ->
+ {skipped, SkippedReason};
+ Host ->
+
+ ?line {ok, Node} = create_node("fileIterator_api", 4008, normal),
+ ?line ?match(ok, remote_apply(Node, ?MODULE, install_data,
+ [tcp, {{'NATIVE',
+ 'cosFileTransferNATIVE_file'}, Host,
+ "fileIterator_api"}])),
+
+ %% Create a Virtual File System.
+%% ?line VFS = ?match({_,_,_,_,_,_},
+%% cosFileTransferApp:create_VFS({'NATIVE',
+%% 'cosFileTransferNATIVE_file'},
+%% [], Host, ?FTP_PORT)),
+ ?line VFS = ?matchnopr({'IOP_IOR',"IDL:omg.org/CosFileTransfer/VirtualFileSystem:1.0",_},
+ corba:string_to_object("corbaname::1.2@localhost:4008/NameService#fileIterator_api")),
+
+ %% Start two File Transfer Sessions (Source and Target).
+ ?line {FS, Dir} = ?matchnopr({{_,_,_},{_,_,_}},
+ 'CosFileTransfer_VirtualFileSystem':login(VFS,
+ ?FTP_USER,
+ ?FTP_PASS,
+ ?FTP_ACC)),
+
+ %% Do some basic test on one of the Directories attributes.
+ ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_name'(Dir)),
+ ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_complete_file_name'(Dir)),
+ ?line ?match({'IOP_IOR',[],[]}, 'CosFileTransfer_Directory':'_get_parent'(Dir)),
+ ?line ?matchnopr(FS, 'CosFileTransfer_Directory':'_get_associated_session'(Dir)),
+ {ok,[],FileIter} = ?match({ok,[],_}, 'CosFileTransfer_Directory':list(Dir, 0)),
+ %% Usually the working directory for the test is not empty so no need for
+ %% creating files of our own?!
+ #any{value=Children} = ?match({any, _, _},
+ 'CosPropertyService_PropertySet':
+ get_property_value(Dir, "num_children")),
+
+ if
+ Children > 5 ->
+ ?line ?matchnopr({true, _}, 'CosFileTransfer_FileIterator':next_one(FileIter)),
+ ?line ?matchnopr({true, _}, 'CosFileTransfer_FileIterator':next_n(FileIter, 3)),
+ ?line ?matchnopr({true, _}, 'CosFileTransfer_FileIterator':next_n(FileIter,
+ Children)),
+ ?line ?matchnopr({false, _}, 'CosFileTransfer_FileIterator':next_one(FileIter)),
+ ?line ?match({false, []}, 'CosFileTransfer_FileIterator':next_n(FileIter, 1)),
+ ok;
+ true ->
+ ok
+ end,
+ ?line ?match(ok, 'CosFileTransfer_FileIterator':destroy(FileIter)),
+ ?line ?match(false, corba_object:non_existent(FS)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FS)),
+ %% To make sure Orber can remove it from mnesia.
+ timer:sleep(1000),
+ ?line ?match(true, corba_object:non_existent(FS)),
+ ?line ?match(ok, remote_apply(Node, ?MODULE, uninstall_data, ["fileIterator_api"])),
+ stop_orber_remote(Node, normal),
+ ok
+ end.
+
+
+%%-----------------------------------------------------------------
+%% FileTransferSession API tests
+%%-----------------------------------------------------------------
+fts_ftp_file_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""];
+fts_ftp_file_api(suite) -> [];
+fts_ftp_file_api(Config) ->
+ ?line {ok, Node} = create_node("ftp_file_api", 4004, normal),
+ file_helper(Config, 'FTP', ?TEST_DIR, Node, 4004, "ftp_file_api", tcp).
+
+fts_ftp_file_ssl_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""];
+fts_ftp_file_ssl_api(suite) -> [];
+fts_ftp_file_ssl_api(Config) ->
+ case os:type() of
+ vxworks ->
+ {skipped, "No SSL-support for VxWorks."};
+ _ ->
+ ?line {ok, Node} = create_node("ftp_file_api_ssl", {4005, 1}, ssl),
+ file_helper(Config, 'FTP', ?TEST_DIR, Node, 4005, "ftp_file_api_ssl", ssl)
+ end.
+
+fts_native_file_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""];
+fts_native_file_api(suite) -> [];
+fts_native_file_api(Config) ->
+ ?line {ok, Node} = create_node("native_file_api", 4006, normal),
+ {ok, Pwd} = file:get_cwd(),
+ file_helper(Config,{'NATIVE', 'cosFileTransferNATIVE_file'},filename:split(Pwd),
+ Node, 4006, "native_file_api", tcp).
+
+fts_native_file_ssl_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""];
+fts_native_file_ssl_api(suite) -> [];
+fts_native_file_ssl_api(Config) ->
+ case os:type() of
+ vxworks ->
+ {skipped, "No SSL-support for VxWorks."};
+ _ ->
+ ?line {ok, Node} = create_node("native_file_ssl_api", {4007, 1}, ssl),
+ {ok, Pwd} = file:get_cwd(),
+ file_helper(Config,{'NATIVE', 'cosFileTransferNATIVE_file'},filename:split(Pwd),
+ Node, 4007, "native_file_ssl_api", ssl)
+ end.
+
+
+
+file_helper(Config, WhichType, TEST_DIR, Node, Port, Name, Type) ->
+ case ftp_host(Config) of
+ {skipped, SkippedReason} ->
+ {skipped, SkippedReason};
+ Host ->
+ TEST_SOURCE = TEST_DIR ++ [create_name(remove_me_source)],
+ TEST_SOURCE2 = TEST_DIR ++ [create_name(remove_me_source)],
+ TEST_TARGET = TEST_DIR ++ [create_name(remove_me_target)],
+
+ io:format("<<<<<< CosFileTransfer Testing Configuration >>>>>>~n",[]),
+ io:format("Source: ~p~nTarget: ~p~n", [TEST_SOURCE, TEST_TARGET]),
+
+ ?line ?match(ok, remote_apply(Node, ?MODULE, install_data,
+ [Type, {WhichType, Host, Name}])),
+
+ ?line VFST = ?match({'IOP_IOR',"IDL:omg.org/CosFileTransfer/VirtualFileSystem:1.0",_},
+ corba:string_to_object("corbaname::1.2@localhost:"++integer_to_list(Port)++"/NameService#"++Name)),
+
+
+ %% Create a Virtual File System.
+ ?line VFS = ?match({_,_,_,_,_,_},
+ cosFileTransferApp:create_VFS(WhichType, [], Host, ?FTP_PORT,
+ [{protocol, Type}])),
+ %% Start two File Transfer Sessions (Source and Target).
+ ?line {FST, _DirT} = ?match({{_,_,_},{_,_,_}},
+ 'CosFileTransfer_VirtualFileSystem':login(VFST,
+ ?FTP_USER,
+ ?FTP_PASS,
+ ?FTP_ACC)),
+ ?line {FSS, DirS} = ?match({{_,_,_,_,_,_},{_,_,_,_,_,_}},
+ 'CosFileTransfer_VirtualFileSystem':login(VFS,
+ ?FTP_USER,
+ ?FTP_PASS,
+ ?FTP_ACC)),
+
+ %% Do some basic test on one of the Directories attributes.
+ ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_name'(DirS)),
+ ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_complete_file_name'(DirS)),
+ ?line ?match({'IOP_IOR',[],[]}, 'CosFileTransfer_Directory':'_get_parent'(DirS)),
+ ?line ?match(FSS, 'CosFileTransfer_Directory':'_get_associated_session'(DirS)),
+
+ %% Get a FileList before we create any new Files
+ ?line #'CosFileTransfer_FileWrapper'{the_file = Dir} =
+ ?match({'CosFileTransfer_FileWrapper', _, ndirectory},
+ 'CosFileTransfer_FileTransferSession':get_file(FSS, TEST_DIR)),
+ ?line {ok,FileList, Iter1} = ?match({ok,_,_}, 'CosFileTransfer_Directory':list(Dir, 10)),
+ ?line loop_files(FileList),
+
+ case Iter1 of
+ {'IOP_IOR',[],[]} ->
+ ok;
+ _->
+ ?line ?match(ok, 'CosFileTransfer_FileIterator':destroy(Iter1))
+ end,
+
+ #any{value=Count1} = ?match({any, _, _}, 'CosPropertyService_PropertySet':
+ get_property_value(Dir, "num_children")),
+
+ %% Now we want to transfer a file from source to target. First, we'll create
+ %% a a file to work with.
+ ?line create_file_on_source_node(WhichType, Config, Host,
+ filename:join(TEST_SOURCE), TEST_DIR,
+ ?TEST_FILE_DATA),
+ ?line create_file_on_source_node(WhichType, Config, Host,
+ filename:join(TEST_SOURCE2), TEST_DIR,
+ ?TEST_FILE_DATA2),
+
+ ?line #'CosFileTransfer_FileWrapper'{the_file = FileS} =
+ ?matchnopr({'CosFileTransfer_FileWrapper', _, nfile},
+ 'CosFileTransfer_FileTransferSession':get_file(FSS, TEST_SOURCE)),
+ ?line #'CosFileTransfer_FileWrapper'{the_file = FileS2} =
+ ?matchnopr({'CosFileTransfer_FileWrapper', _, nfile},
+ 'CosFileTransfer_FileTransferSession':get_file(FSS, TEST_SOURCE2)),
+
+ #any{value=Count2} = ?match({any, _, _}, 'CosPropertyService_PropertySet':
+ get_property_value(Dir, "num_children")),
+ timer:sleep(2000),
+ ?match(true, (Count1+2 == Count2)),
+
+ %% Create a target File
+ ?line FileT = ?matchnopr({_,_,_},
+ 'CosFileTransfer_FileTransferSession':create_file(FST, TEST_TARGET)),
+ %% Try to delete the non-existing file.
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_FileTransferSession':delete(FST, FileT)),
+
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':transfer(FSS, FileS, FileT)),
+
+ %% Remove this test when ftp supports append.
+ case WhichType of
+ {'NATIVE', 'cosFileTransferNATIVE_file'} ->
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':append(FSS, FileS, FileT)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':insert(FSS, FileS2, FileT, 7));
+ _->
+ ok
+ end,
+
+ %% Delete source and target files
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FSS, FileS)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FSS, FileS2)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FST, FileT)),
+
+ %% Should be back where we started.
+ timer:sleep(2000),
+ #any{value=Count3} = ?match({any, _, _}, 'CosPropertyService_PropertySet':
+ get_property_value(Dir, "num_children")),
+ ?match(true, (Count1 == Count3)),
+
+
+ ?line ?match(false, corba_object:non_existent(FSS)),
+ ?line ?match(false, corba_object:non_existent(FST)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FSS)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FST)),
+ %% To make sure Orber can remove it from mnesia.
+ timer:sleep(2000),
+ ?line ?match(true, corba_object:non_existent(FSS)),
+ ?line ?match(true, corba_object:non_existent(FST)),
+ ?line ?match(ok, remote_apply(Node, ?MODULE, uninstall_data, [Name])),
+ stop_orber_remote(Node, normal),
+ ok
+ end.
+
+%%-----------------------------------------------------------------
+%% FileTransferSession API tests
+%%-----------------------------------------------------------------
+fts_ftp_dir_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""];
+fts_ftp_dir_api(suite) -> [];
+fts_ftp_dir_api(Config) ->
+ ?line {ok, Node} = create_node("ftp_dir_api", 4009, normal),
+ dir_helper(Config, 'FTP', ?TEST_DIR, Node, 4009, "ftp_dir_api").
+
+
+fts_native_dir_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""];
+fts_native_dir_api(suite) -> [];
+fts_native_dir_api(Config) ->
+ ?line {ok, Node} = create_node("native_dir_api", 4010, normal),
+ {ok, Pwd} = file:get_cwd(),
+ dir_helper(Config, {'NATIVE', 'cosFileTransferNATIVE_file'},
+ filename:split(Pwd), Node, 4010, "native_dir_api").
+
+dir_helper(Config, WhichType, TEST_DIR, Node, Port, Name) ->
+ case ftp_host(Config) of
+ {skipped, SkippedReason} ->
+ {skipped, SkippedReason};
+ Host ->
+ TEST_DIR_LEVEL1 = TEST_DIR ++ [create_name(remove_me_dir1)],
+ TEST_DIR_LEVEL2 = TEST_DIR_LEVEL1 ++ [create_name(remove_me_dir2)],
+
+ io:format("<<<<<< CosFileTransfer Testing Configuration >>>>>>~n",[]),
+ io:format("Top Dir: ~p~nLevel2 Dir: ~p~n", [TEST_DIR_LEVEL1, TEST_DIR_LEVEL2]),
+
+ ?line ?match(ok, remote_apply(Node, ?MODULE, install_data,
+ [tcp, {WhichType, Host, Name}])),
+
+ ?line VFS = ?matchnopr({'IOP_IOR',"IDL:omg.org/CosFileTransfer/VirtualFileSystem:1.0",_},
+ corba:string_to_object("corbaname::1.2@localhost:"++integer_to_list(Port)++"/NameService#"++Name)),
+
+ %% Start two File Transfer Sessions (Source and Target).
+ ?line {FS, DirS} = ?matchnopr({{'IOP_IOR',_,_}, _},
+ 'CosFileTransfer_VirtualFileSystem':login(VFS,
+ ?FTP_USER,
+ ?FTP_PASS,
+ ?FTP_ACC)),
+
+ %% Do some basic test on one of the Directories attributes.
+ ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_name'(DirS)),
+ ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_complete_file_name'(DirS)),
+ ?line ?match({'IOP_IOR',[],[]}, 'CosFileTransfer_Directory':'_get_parent'(DirS)),
+ ?line ?matchnopr(FS, 'CosFileTransfer_Directory':'_get_associated_session'(DirS)),
+
+ %% Create a Root Directory. Currently we only need to create one but
+ %% later on, when supporting other protocols than FTP it's not enough.
+ ?line Dir1 = 'CosFileTransfer_FileTransferSession':create_directory(FS,
+ TEST_DIR_LEVEL1),
+ io:format("<<<<<< CosFileTransfer Testing Properties >>>>>>~n",[]),
+ ?line ?match({ok, [tk_long, tk_boolean]},
+ 'CosFileTransfer_Directory':get_allowed_property_types(Dir1)),
+ ?line ?match({ok, [_,_]},
+ 'CosFileTransfer_Directory':get_allowed_properties(Dir1)),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property_with_mode(Dir1,
+ "num_children",
+ #any{typecode=tk_long, value=0},
+ fixed_readonly)),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property_with_mode(Dir1,
+ "wrong",
+ #any{typecode=tk_long, value=0},
+ fixed_readonly)),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property_with_mode(Dir1,
+ "num_children",
+ #any{typecode=tk_short, value=0},
+ fixed_readonly)),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property_with_mode(Dir1,
+ "num_children",
+ #any{typecode=tk_long, value=0},
+ fixed_normal)),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_properties_with_modes(Dir1,
+ [#'CosPropertyService_PropertyDef'
+ {property_name = "num_children",
+ property_value = #any{typecode=tk_long, value=0},
+ property_mode = fixed_readonly}])),
+ ?line ?match(fixed_readonly,
+ 'CosFileTransfer_Directory':get_property_mode(Dir1, "num_children")),
+ ?line ?match({true,
+ [#'CosPropertyService_PropertyMode'{property_name = "num_children",
+ property_mode = fixed_readonly}]},
+ 'CosFileTransfer_Directory':get_property_modes(Dir1, ["num_children"])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':set_property_mode(Dir1, "num_children", fixed_readonly)),
+
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':
+ set_property_modes(Dir1,
+ [#'CosPropertyService_PropertyMode'
+ {property_name = "num_children",
+ property_mode = fixed_readonly}])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':
+ set_property_modes(Dir1,
+ [#'CosPropertyService_PropertyMode'
+ {property_name = "wrong",
+ property_mode = fixed_readonly}])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':
+ set_property_modes(Dir1,
+ [#'CosPropertyService_PropertyMode'
+ {property_name = "num_children",
+ property_mode = fixed_normal}])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property(Dir1,
+ "num_children",
+ #any{typecode=tk_long, value=0})),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property(Dir1,
+ "wrong",
+ #any{typecode=tk_long, value=0})),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property(Dir1,
+ "num_children",
+ #any{typecode=tk_short, value=0})),
+
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':define_property(Dir1,
+ "num_children",
+ #any{typecode=tk_long, value=0})),
+
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':
+ define_properties(Dir1,
+ [#'CosPropertyService_Property'
+ {property_name = "num_children",
+ property_value = #any{typecode=tk_long,
+ value=0}}])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':
+ define_properties(Dir1,
+ [#'CosPropertyService_Property'
+ {property_name = "wrong",
+ property_value = #any{typecode=tk_long,
+ value=0}}])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':
+ define_properties(Dir1,
+ [#'CosPropertyService_Property'
+ {property_name = "num_children",
+ property_value = #any{typecode=tk_short,
+ value=0}}])),
+ ?line ?match(2, 'CosFileTransfer_Directory':get_number_of_properties(Dir1)),
+
+ ?line ?match({ok, ["num_children", "is_directory"], {'IOP_IOR',[],[]}},
+ 'CosFileTransfer_Directory':get_all_property_names(Dir1, 2)),
+ ?line ?match({ok, ["is_directory"], _},
+ 'CosFileTransfer_Directory':get_all_property_names(Dir1, 1)),
+
+ ?line ?match(#any{},
+ 'CosFileTransfer_Directory':get_property_value(Dir1, "num_children")),
+ ?line ?match(#any{},
+ 'CosFileTransfer_Directory':get_property_value(Dir1, "is_directory")),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':get_property_value(Dir1, "wrong")),
+
+ ?line ?match({true,
+ [#'CosPropertyService_Property'{property_name = "num_children"}]},
+ 'CosFileTransfer_Directory':get_properties(Dir1, ["num_children"])),
+ ?line ?match({false,
+ [#'CosPropertyService_Property'{property_name = "wrong"}]},
+ 'CosFileTransfer_Directory':get_properties(Dir1, ["wrong"])),
+
+ ?line ?match({ok, [_],_},
+ 'CosFileTransfer_Directory':get_all_properties(Dir1, 1)),
+ ?line ?match({ok, [_,_], {'IOP_IOR',[],[]}},
+ 'CosFileTransfer_Directory':get_all_properties(Dir1, 2)),
+
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':delete_property(Dir1, "num_children")),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':delete_property(Dir1, "wrong")),
+
+
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':delete_properties(Dir1, ["num_children"])),
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_Directory':delete_properties(Dir1, ["wrong"])),
+ ?line ?match(false, 'CosFileTransfer_Directory':delete_all_properties(Dir1)),
+ ?line ?match(true,
+ 'CosFileTransfer_Directory':is_property_defined(Dir1, "num_children")),
+ ?line ?match(false,
+ 'CosFileTransfer_Directory':is_property_defined(Dir1, "wrong")),
+
+ %% The Top Dir should be empty and ...
+ ?line ?match({ok,[],_}, 'CosFileTransfer_Directory':list(Dir1, 1000)),
+ ?line ?match( #any{value=0},
+ 'CosPropertyService_PropertySet':get_property_value(Dir1, "num_children")),
+ %% Create a sub-directory.
+ ?line Dir2 = 'CosFileTransfer_FileTransferSession':create_directory(FS,
+ TEST_DIR_LEVEL2),
+ ?line ?match( #any{value=1},
+ 'CosPropertyService_PropertySet':get_property_value(Dir1, "num_children")),
+
+ ?line ?match({ok, [_,_], {'IOP_IOR',[],[]}},
+ 'CosFileTransfer_Directory':get_all_properties(Dir1, 2)),
+ ?line {_,_,Iterator1} = ?match({ok, [_], _},
+ 'CosFileTransfer_Directory':get_all_properties(Dir1, 1)),
+ ?line ?match({false, [_]},
+ 'CosPropertyService_PropertiesIterator':next_n(Iterator1,4)),
+
+ ?line {_,_,Iterator0} = ?match({ok, [], _},
+ 'CosFileTransfer_Directory':get_all_properties(Dir1, 0)),
+
+ ?line ?match({false, [_, {'CosPropertyService_Property',
+ "num_children",{any,tk_long,1}}]},
+ 'CosPropertyService_PropertiesIterator':next_n(Iterator0,4)),
+
+ ?line ?match({true,
+ [#'CosPropertyService_Property'{property_name = "num_children"}]},
+ 'CosFileTransfer_Directory':get_properties(Dir1, ["num_children"])),
+
+ %% The Top Directory is not emtpy any more and ...
+ ?line {ok,[#'CosFileTransfer_FileWrapper'{the_file = DirRef}],_} =
+ ?matchnopr({ok,[{'CosFileTransfer_FileWrapper', _, ndirectory}],_},
+ 'CosFileTransfer_Directory':list(Dir1, 1000)),
+ %% ... its name eq. to 'TEST_DIR_LEVEL2'
+ ?line ?match(TEST_DIR_LEVEL2,
+ 'CosFileTransfer_Directory':'_get_complete_file_name'(DirRef)),
+
+ ?line #'CosFileTransfer_FileWrapper'{the_file = Dir3} =
+ ?matchnopr({'CosFileTransfer_FileWrapper', _, ndirectory},
+ 'CosFileTransfer_FileTransferSession':get_file(FS, TEST_DIR_LEVEL1)),
+
+ %% Must get the same result for the 'get_file' operation.
+ ?line {ok,[#'CosFileTransfer_FileWrapper'{the_file = DirRef2}],_} =
+ ?matchnopr({ok,[{'CosFileTransfer_FileWrapper', _, ndirectory}],_},
+ 'CosFileTransfer_Directory':list(Dir3,1000)),
+ ?line ?match(TEST_DIR_LEVEL2,
+ 'CosFileTransfer_Directory':'_get_complete_file_name'(DirRef2)),
+
+ %% Since the top directory isn't empty deleting it must fail.
+ ?line ?match({'EXCEPTION', _},
+ 'CosFileTransfer_FileTransferSession':delete(FS, Dir1)),
+
+ %% Delete the sub-directory and ...
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FS, Dir2)),
+ %% ... see if the top directory realyy is empty.
+ ?line ?match({ok,[],_}, 'CosFileTransfer_Directory':list(Dir1, 1000)),
+
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FS, Dir1)),
+ %% Test if the top directory been removed as intended.
+ ?line ?match({'EXCEPTION', {'CosFileTransfer_FileNotFoundException', _, _}},
+ 'CosFileTransfer_FileTransferSession':get_file(FS, TEST_DIR_LEVEL1)),
+
+ ?line ?match(false, corba_object:non_existent(FS)),
+ ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FS)),
+ %% To make sure Orber can remove it from mnesia.
+ timer:sleep(1000),
+ ?line ?match(true, corba_object:non_existent(FS)),
+ ?line ?match(ok, remote_apply(Node, ?MODULE, uninstall_data, [Name])),
+ stop_orber_remote(Node, normal),
+ ok
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Internal functions
+%%-----------------------------------------------------------------
+ftp_host(Config) ->
+ case ?config(ftp_remote_host, Config) of
+ undefined ->
+ {skipped, "The configuration parameter 'ftp_remote_host' not defined."};
+ Host ->
+ Host
+ end.
+
+loop_files([]) ->
+ io:format("@@@ DONE @@@~n", []);
+loop_files([#'CosFileTransfer_FileWrapper'{the_file = H}|T]) ->
+ FullName = 'CosFileTransfer_File':'_get_complete_file_name'(H),
+ Name = 'CosFileTransfer_File':'_get_name'(H),
+ io:format("FULL NAME: ~p SHORT NAME: ~p~n", [FullName, Name]),
+ loop_files(T).
+
+
+create_file_on_source_node('FTP', _Config, Host, FileName, Path, Data) ->
+ io:format("<<<<<< CosFileTransfer Testing File >>>>>>~n",[]),
+ io:format("Host: ~p~nPath: ~p~nFile: ~p~n", [Host, Path, FileName]),
+ {ok, Pid} = ?match({ok, _}, inets:start(ftpc, [{host, Host}], stand_alone)),
+ ?match(ok, ftp:user(Pid, ?FTP_USER, ?FTP_PASS)),
+ ?match(ok, ftp:cd(Pid, Path)),
+ ?match(ok, ftp:send_bin(Pid, list_to_binary(Data), FileName)),
+ ?match(ok, inets:stop(ftpc, Pid));
+create_file_on_source_node({'NATIVE', _}, _Config, Host, FileName, Path, Data) ->
+ io:format("<<<<<< CosFileTransfer Testing File >>>>>>~n",[]),
+ io:format("Host: ~p~nPath: ~p~nFile: ~p~n", [Host, Path, FileName]),
+ ?match(ok, file:write_file(FileName, list_to_binary(Data))).
+
+create_name(Type) ->
+ {MSec, Sec, USec} = erlang:now(),
+ lists:concat([Type,'_',MSec, '_', Sec, '_', USec]).
+
+
+
+
+%%------------------------------------------------------------
+%% function : create_node/4
+%% Arguments: Name - the name of the new node (atom())
+%% Port - which iiop_port (integer())
+%% Domain - which domain.
+%% Type - if /4 used the types defines the extra arguments
+%% to be used.
+%% Returns : {ok, Node} | {error, _}
+%% Effect : Starts a new slave-node with given (optinally)
+%% extra arguments. If fails it retries 'Retries' times.
+%%------------------------------------------------------------
+create_node(Name, Port, normal) ->
+ Args = basic_args(Name),
+ create_node(Name, Port, 10, normal, Args, []);
+create_node(Name, {Port, _Depth}, ssl) ->
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ Args = basic_args(Name),
+ {ok, Node} = create_node(list_to_atom(Name), Port, 10, ssl, Args, []),
+ %% Client
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_certfile,
+ filename:join([Dir, "client", "cert.pem"])]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_cacertfile,
+ filename:join([Dir, "client", "cacerts.pem"])]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_keyfile,
+ filename:join([Dir, "client", "key.pem"])]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_verify, 1]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_depth, 0]),
+
+ %% Server
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_certfile,
+ filename:join([Dir, "server", "cert.pem"])]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_cacertfile,
+ filename:join([Dir, "server", "cacerts.pem"])]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_keyfile,
+ filename:join([Dir, "server", "key.pem"])]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_verify, 1]),
+ rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_depth, 0]),
+ {ok, Node}.
+
+%create_node(Name, {Port, Depth}, ssl) ->
+% TestLibs = filename:join(filename:dirname(code:which(?MODULE)), "ssl_data"),
+% Args = basic_args(Name),
+% SArgs = basic_ssl_args(TestLibs, Args),
+% LArgs = level_based_ssl(Depth, TestLibs, SArgs),
+% create_node(list_to_atom(Name), Port, 10, ssl, LArgs, [{sslpath, TestLibs}]).
+
+create_node(Name, Port, Retries, Type, Args, Options) ->
+ [_, Host] = ?match([_,_],string:tokens(atom_to_list(node()), [$@])),
+ case starter(Host, Name, Args) of
+ {ok, NewNode} ->
+ ?line ?match(pong, net_adm:ping(NewNode)),
+ {ok, Cwd} = file:get_cwd(),
+ Path = code:get_path(),
+ ?line ?match(ok, rpc:call(NewNode, file, set_cwd, [Cwd])),
+ true = rpc:call(NewNode, code, set_path, [Path]),
+ ?match(ok, start_orber_remote(NewNode, Type, Options, Port)),
+ spawn_link(NewNode, ?MODULE, slave_sup, []),
+ rpc:multicall([node() | nodes()], global, sync, []),
+ {ok, NewNode};
+ {error, Reason} when Retries == 0->
+ {error, Reason};
+ {error, Reason} ->
+ io:format("Could not start slavenode ~p ~p retrying~n",
+ [{Host, Name, Args}, Reason]),
+ timer:sleep(500),
+ create_node(Name, Port, Retries - 1, Type, Args, Options)
+ end.
+
+starter(Host, Name, Args) ->
+ case os:type() of
+ vxworks ->
+ test_server:start_node(Name, slave, [{args,Args}]);
+ _ ->
+ slave:start(Host, Name, Args)
+ end.
+
+slave_sup() ->
+ process_flag(trap_exit, true),
+ receive
+ {'EXIT', _, _} ->
+ case os:type() of
+ vxworks ->
+ erlang:halt();
+ _ ->
+ ignore
+ end
+ end.
+
+
+%%------------------------------------------------------------
+%% function : destroy_node
+%% Arguments: Node - which node to destroy.
+%% Type - normal | ssl
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+-ifdef(false).
+destroy_node(Node, Type) ->
+ stopper(Node, Type).
+
+stopper(Node, Type) ->
+ catch stop_orber_remote(Node, Type),
+ case os:type() of
+ vxworks ->
+ test_server:stop_node(Node);
+ _ ->
+ slave:stop(Node)
+ end.
+-endif.
+
+%%------------------------------------------------------------
+%% function : remote_apply
+%% Arguments: N - Node, M - Module,
+%% F - Function, A - Arguments (list)
+%% Returns :
+%% Effect :
+%%------------------------------------------------------------
+remote_apply(N, M,F,A) ->
+ case rpc:call(N, M, F, A) of
+ {badrpc, Reason} ->
+ exit(Reason);
+ Other ->
+ Other
+ end.
+
+%%------------------------------------------------------------
+%% function : stop_orber_remote
+%% Arguments: Node - which node to stop orber on.
+%% Type - normal | ssl | light | .......
+%% Returns : ok
+%% Effect : Stops orber on given node and, if specified,
+%% other applications or programs.
+%%------------------------------------------------------------
+stop_orber_remote(Node, ssl) ->
+ rpc:call(Node, ssl, stop, []),
+ rpc:call(Node, crypto, stop, []),
+ orb_rpc_blast(Node, ssl);
+stop_orber_remote(Node, Type) ->
+ orb_rpc_blast(Node, Type).
+
+orb_rpc_blast(Node, _) ->
+ rpc:call(Node, cosFileTransferApp, stop, []),
+ rpc:call(Node, cosProperty, stop, []),
+ rpc:call(Node, cosFileTransferApp, uninstall, []),
+ rpc:call(Node, cosProperty, uninstall, []),
+ rpc:call(Node, orber, jump_stop, []).
+
+%%------------------------------------------------------------
+%% function : start_orber_remote
+%% Arguments: Node - which node to start orber on.
+%% Type - normal | ssl | light | .......
+%% Returns : ok
+%% Effect : Starts orber on given node and, if specified,
+%% other applications or programs.
+%%------------------------------------------------------------
+start_orber_remote(Node, ssl, _Options, Port) ->
+ rpc:call(Node, ssl, start, []),
+ rpc:call(Node, crypto, start, []),
+ rpc:call(Node, ssl, seed, ["testing"]),
+ orb_rpc_setup(Node, ssl, Port);
+start_orber_remote(Node, Type, _, Port) ->
+ orb_rpc_setup(Node, Type, Port).
+
+orb_rpc_setup(Node, _, Port) ->
+ rpc:call(Node, orber, jump_start, [Port]),
+ rpc:call(Node, cosProperty, install, []),
+ rpc:call(Node, cosProperty, start, []),
+ rpc:call(Node, cosFileTransferApp, install, []).
+
+%%--------------- MISC FUNCTIONS -----------------------------
+basic_args(_Name) ->
+ TestLibs = filename:dirname(code:which(?MODULE)),
+ " -orber orber_debug_level 10" ++
+ " -pa " ++
+ TestLibs ++
+ " -pa " ++
+ filename:join(TestLibs, "all_SUITE_data") ++
+ " -pa " ++
+ filename:dirname(code:which(cosFileTransferApp)).
+
+-ifdef(false).
+basic_ssl_args(TestLibs, Args) ->
+% Args ++
+% " -cosFileTransfer ssl_client_certfile \\\"" ++
+% filename:join(TestLibs, "ssl_client_cert.pem") ++
+% "\\\" -cosFileTransfer ssl_server_certfile \\\""++
+% filename:join(TestLibs, "ssl_server_cert.pem")++"\\\"".
+
+ io:format("<<<<<< SSL LIBS ~p >>>>>>~n",[TestLibs]),
+ NewArgs = Args ++
+ " -cosFileTransfer ssl_client_certfile \\\"" ++
+ filename:join(TestLibs, "ssl_client_cert.pem") ++
+ "\\\" -cosFileTransfer ssl_server_certfile \\\""++
+ filename:join(TestLibs, "ssl_server_cert.pem")++"\\\"",
+ io:format("<<<<<< SSL LIBS ARGS ~p >>>>>>~n",[NewArgs]),
+ NewArgs.
+
+level_based_ssl(1, _TestLibs, Args) ->
+ Args;
+level_based_ssl(2, _TestLibs, Args) ->
+ Args.% ++
+% " -cosFileTransfer ssl_server_depth 2 " ++
+% " -cosFileTransfer ssl_client_depth 2 " ++
+% " -cosFileTransfer ssl_server_verify " ++
+% " -cosFileTransfer ssl_client_verify " ++
+% " -cosFileTransfer ssl_server_cacertfile " ++
+% " -cosFileTransfer ssl_client_cacertfile " ++
+
+-endif.
+
+install_data(Protocol, {WhichType, Host, Name}) ->
+ io:format("<<<<<< Starting ~p/~p VFS at ~p/~p>>>>>>~n",
+ [Protocol, WhichType, Host, Name]),
+ %% Create a Virtual File System.
+ ?line VFS = ?match({_,_,_,_,_,_},
+ cosFileTransferApp:create_VFS(WhichType, [], Host, ?FTP_PORT,
+ [{protocol, Protocol}])),
+ NS = corba:resolve_initial_references("NameService"),
+ NC1 = lname_component:set_id(lname_component:create(), Name),
+ N = lname:insert_component(lname:create(), 1, NC1),
+ 'CosNaming_NamingContext':rebind(NS, N, VFS).
+
+uninstall_data(Name) ->
+ ?line VFS = ?match({_,_,_,_,_,_},
+ corba:string_to_object("corbaname:rir:/NameService#"++Name)),
+ ?line ?match(ok, corba:dispose(VFS)),
+ ok.
+
+
+
+%%------------------- EOF MODULE-----------------------------------
diff --git a/lib/cosFileTransfer/vsn.mk b/lib/cosFileTransfer/vsn.mk
index 2700ecb3e3..ef8ee53c5e 100644
--- a/lib/cosFileTransfer/vsn.mk
+++ b/lib/cosFileTransfer/vsn.mk
@@ -1,16 +1 @@
COSFILETRANSFER_VSN = 1.1.10
-
-TICKETS = \
- OTP-8355 \
- OTP-8374
-
-
-TICKETS_1.1.9 = OTP-8201
-
-TICKETS_1.1.8 = OTP-7987
-
-TICKETS_1.1.7 = OTP-7837
-
-TICKETS_1.1.6 = \
- OTP-7595 \
- OTP-7599 \ No newline at end of file
diff --git a/lib/cosNotification/doc/src/CosNotification.xml b/lib/cosNotification/doc/src/CosNotification.xml
index 22e9bcb27c..cd965bc46b 100644
--- a/lib/cosNotification/doc/src/CosNotification.xml
+++ b/lib/cosNotification/doc/src/CosNotification.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml b/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml
index 6e2a102051..57015b3621 100644
--- a/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml
+++ b/lib/cosNotification/doc/src/CosNotification_AdminPropertiesAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml
index 2cdb2d54a8..671f68d482 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ConsumerAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml
index 69b1e78b82..8bc182a50c 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml
index 29dc59871d..43818e5238 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml
index daa0f3cc49..4c0aac7ae6 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPullSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml
index 63d3f53101..697d00ea51 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml
index 54d100c353..f6fc3a0f7b 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxyPushSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml
index daf2aab388..81d4de929a 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_ProxySupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml
index aa9fae47df..4084fd443b 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml
index a46c53c9c1..16b093b9aa 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPullSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml
index 60dfa2c230..f8ce2072e1 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SequenceProxyPushSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml
index 070f9a3b92..0623d2891b 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml
index 4a454b224a..0f0bb5d985 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPullSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml
index db7f1ddb44..7b7a60723e 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushConsumer.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml
index b2dab10998..ab0a260a4b 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_StructuredProxyPushSupplier.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml
index 0f262accb8..a567463f7d 100644
--- a/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml
+++ b/lib/cosNotification/doc/src/CosNotifyChannelAdmin_SupplierAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyComm_NotifyPublish.xml b/lib/cosNotification/doc/src/CosNotifyComm_NotifyPublish.xml
index 427ca87810..2ea19a2dfb 100644
--- a/lib/cosNotification/doc/src/CosNotifyComm_NotifyPublish.xml
+++ b/lib/cosNotification/doc/src/CosNotifyComm_NotifyPublish.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml b/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml
index 1ed7f860c0..dd8ef713e8 100644
--- a/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml
+++ b/lib/cosNotification/doc/src/CosNotifyComm_NotifySubscribe.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyFilter_FilterAdmin.xml b/lib/cosNotification/doc/src/CosNotifyFilter_FilterAdmin.xml
index ebbba8763d..9e0fe693d4 100644
--- a/lib/cosNotification/doc/src/CosNotifyFilter_FilterAdmin.xml
+++ b/lib/cosNotification/doc/src/CosNotifyFilter_FilterAdmin.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/CosNotifyFilter_FilterFactory.xml b/lib/cosNotification/doc/src/CosNotifyFilter_FilterFactory.xml
index c4712e481f..886b5b4729 100644
--- a/lib/cosNotification/doc/src/CosNotifyFilter_FilterFactory.xml
+++ b/lib/cosNotification/doc/src/CosNotifyFilter_FilterFactory.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml
index de5a3e5f4c..125e25e67e 100644
--- a/lib/cosNotification/doc/src/notes.xml
+++ b/lib/cosNotification/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2010</year>
+ <year>2000</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,60 @@
<file>notes.xml</file>
</header>
+ <section><title>cosNotification 1.1.16</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Eliminated Dialyzer warnings when using exit or throw.</p>
+ <p>
+ Own Id: OTP-9050</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+ <section><title>cosNotification 1.1.15</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Switched from using the deprecated regexp to re instead.</p>
+ <p>
+ Own Id: OTP-8846</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section>
+ <title>cosNotification 1.1.14</title>
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Test suites published.</p>
+ <p>
+ Own Id: OTP-8543 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Added missing trailing bracket to define in hrl-file.</p>
+ <p>Own Id: OTP-8489 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
<section>
<title>cosNotification 1.1.14</title>
<section>
@@ -64,15 +118,15 @@
<list type="bulleted">
<item>
<p>Removed superfluous VT in the documentation.</p>
- <p>Own id: OTP-8353 Aux Id:</p>
+ <p>Own Id: OTP-8353 Aux Id:</p>
</item>
<item>
<p>Removed superfluous backslash in the documentation.</p>
- <p>Own id: OTP-8354 Aux Id:</p>
+ <p>Own Id: OTP-8354 Aux Id:</p>
</item>
<item>
<p>The documentation EIX file was not generated.</p>
- <p>Own id: OTP-8355 Aux Id:</p>
+ <p>Own Id: OTP-8355 Aux Id:</p>
</item>
</list>
</section>
@@ -104,7 +158,7 @@
<item>
<p>Obsolete guards, e.g. record vs is_record, has been changed
to avoid compiler warnings.</p>
- <p>Own id: OTP-7987</p>
+ <p>Own Id: OTP-7987</p>
</item>
</list>
</section>
@@ -118,7 +172,7 @@
<list type="bulleted">
<item>
<p>Updated file headers.</p>
- <p>Own id: OTP-7837 Aux Id:</p>
+ <p>Own Id: OTP-7837 Aux Id:</p>
</item>
</list>
</section>
@@ -132,7 +186,7 @@
<list type="bulleted">
<item>
<p>Documentation source included in open source releases.</p>
- <p>Own id: OTP-7595 Aux Id:</p>
+ <p>Own Id: OTP-7595 Aux Id:</p>
</item>
</list>
</section>
@@ -147,7 +201,7 @@
<item>
<p>The CosNotification proxy objects ignored the gcLimit option, instead
the gcTime value was used.</p>
- <p>Own id: OTP-7553 Aux Id:</p>
+ <p>Own Id: OTP-7553 Aux Id:</p>
</item>
</list>
</section>
@@ -161,7 +215,7 @@
<list type="bulleted">
<item>
<p>Updated file headers.</p>
- <p>Own id: OTP-7011</p>
+ <p>Own Id: OTP-7011</p>
</item>
</list>
</section>
@@ -175,7 +229,7 @@
<list type="bulleted">
<item>
<p>The documentation source has been converted from SGML to XML.</p>
- <p>Own id: OTP-6754</p>
+ <p>Own Id: OTP-6754</p>
</item>
</list>
</section>
@@ -189,7 +243,7 @@
<list type="bulleted">
<item>
<p>Minor Makefile changes.</p>
- <p>Own id: OTP-6701</p>
+ <p>Own Id: OTP-6701</p>
</item>
</list>
</section>
@@ -203,7 +257,7 @@
<list type="bulleted">
<item>
<p>Removed some unused code.</p>
- <p>Own id: OTP-6527</p>
+ <p>Own Id: OTP-6527</p>
</item>
</list>
</section>
@@ -219,7 +273,7 @@
<p>A user can now define the QoS EventReliability to be
Persistent. Note, this is only a lightweight version
and events will be lost if a proxy is terminated.</p>
- <p>Own id: OTP-5923</p>
+ <p>Own Id: OTP-5923</p>
</item>
</list>
</section>
@@ -235,7 +289,7 @@
<p>Possible to configure cosNotification not to type check,
by invoking corba_object:is_a/2, supplied IOR:s. When
a type check fails, the feedback has been improved.</p>
- <p>Own id: OTP-5823 Aux Id: seq10143</p>
+ <p>Own Id: OTP-5823 Aux Id: seq10143</p>
</item>
</list>
</section>
@@ -249,7 +303,7 @@
<list type="bulleted">
<item>
<p>The app-file contained duplicated modules.</p>
- <p>Own id: OTP-4976</p>
+ <p>Own Id: OTP-4976</p>
</item>
</list>
</section>
@@ -268,7 +322,7 @@
Interface Repository. It is necessary to re-compile all IDL-files
and use COS-applications, including Orber, compiled with
IC-4.2.</p>
- <p>Own id: OTP-4576</p>
+ <p>Own Id: OTP-4576</p>
</item>
</list>
</section>
diff --git a/lib/cosNotification/src/CosNotification_Common.erl b/lib/cosNotification/src/CosNotification_Common.erl
index 0e0f1da0d5..af9b2d4368 100644
--- a/lib/cosNotification/src/CosNotification_Common.erl
+++ b/lib/cosNotification/src/CosNotification_Common.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,6 @@
%%--------------- INCLUDES -----------------------------------
-include_lib("orber/include/corba.hrl").
--include_lib("orber/include/ifr_types.hrl").
%% Application files
-include("CosNotification.hrl").
-include("CosNotifyChannelAdmin.hrl").
@@ -945,14 +944,19 @@ check_limits(LQS, NPR) ->
%% supported.
%%------------------------------------------------------------
validate_event_qos(Wanted, Curr) ->
- v_e_q_helper(Wanted, Curr, []),
- [].
+ case v_e_q_helper(Wanted, Curr, []) of
+ ok ->
+ [];
+ {error, Unsupp} ->
+ corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupp})
+ end.
+
v_e_q_helper([], _Curr, []) ->
- %% Parsed all and foynd no conflicts.
+ %% Parsed all and found no conflicts.
ok;
v_e_q_helper([], _Curr, Unsupp) ->
%% Not possible to use these requested QoS.
- corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupp});
+ {error, Unsupp};
%%--- EventReliability ---%%
v_e_q_helper([#'CosNotification_Property'{name=?not_EventReliability,
@@ -1071,30 +1075,38 @@ v_e_q_helper(What, _, _) ->
%% LQS - local representation of QoS.
%% Returns : {NewOMGStyleQoS, NewLocalQoS} | #'CosNotification_UnsupportedQoS'{}
%%------------------------------------------------------------
-set_properties([], Curr, channelAdm, _, [], NewQoS,_,_,LAS) ->
+set_properties(Wanted, Current, Type, Supported, Unsupp, NewQoS, Parent, Childs, LQS) ->
+ case do_set_properties(Wanted, Current, Type, Supported, Unsupp, NewQoS, Parent, Childs, LQS) of
+ {error, Exc} ->
+ corba:raise(Exc);
+ Result ->
+ Result
+ end.
+
+do_set_properties([], Curr, channelAdm, _, [], NewQoS,_,_,LAS) ->
merge_properties(NewQoS, Curr, LAS);
-set_properties([], Curr, _, _, [], NewQoS,_,_,LQS) ->
+do_set_properties([], Curr, _, _, [], NewQoS,_,_,LQS) ->
%% set_local_qos and merge_properties are help functions found at the end of QoS
%% functions.
NewLQS = set_local_qos(NewQoS, LQS),
merge_properties(NewQoS, Curr, NewLQS);
-set_properties([], _, channelAdm, _, Unsupp, _,_,_,_) ->
- corba:raise(#'CosNotification_UnsupportedAdmin'{admin_err = Unsupp});
-set_properties([], _, _, _, Unsupp, _,_,_,_) ->
- corba:raise(#'CosNotification_UnsupportedQoS'{qos_err = Unsupp});
+do_set_properties([], _, channelAdm, _, Unsupp, _,_,_,_) ->
+ {error, #'CosNotification_UnsupportedAdmin'{admin_err = Unsupp}};
+do_set_properties([], _, _, _, Unsupp, _,_,_,_) ->
+ {error, #'CosNotification_UnsupportedQoS'{qos_err = Unsupp}};
-set_properties([Req|Tail], Curr, Type, Supported, Unsupp, NewQoS, Parent, Childs,LQS) ->
+do_set_properties([Req|Tail], Curr, Type, Supported, Unsupp, NewQoS, Parent, Childs,LQS) ->
%% set_values and is_supported are help functions found at the end of QoS
%% functions.
case set_values(is_supported(Supported, Req), Req, Type, Curr, Parent, Childs,LQS) of
{unsupported, U} ->
- set_properties(Tail, Curr, Type, Supported, [U|Unsupp], NewQoS, Parent, Childs,LQS);
+ do_set_properties(Tail, Curr, Type, Supported, [U|Unsupp], NewQoS, Parent, Childs,LQS);
{ok, S, NewLQS} ->
- set_properties(Tail, Curr, Type, Supported, Unsupp, [S|NewQoS], Parent, Childs,NewLQS);
+ do_set_properties(Tail, Curr, Type, Supported, Unsupp, [S|NewQoS], Parent, Childs,NewLQS);
{ok, S} ->
- set_properties(Tail, Curr, Type, Supported, Unsupp, [S|NewQoS], Parent, Childs,LQS);
+ do_set_properties(Tail, Curr, Type, Supported, Unsupp, [S|NewQoS], Parent, Childs,LQS);
ok ->
- set_properties(Tail, Curr, Type, Supported, Unsupp, NewQoS, Parent, Childs,LQS)
+ do_set_properties(Tail, Curr, Type, Supported, Unsupp, NewQoS, Parent, Childs,LQS)
end.
diff --git a/lib/cosNotification/src/cosNotification_Filter.erl b/lib/cosNotification/src/cosNotification_Filter.erl
index dd3b5beb93..7201f7d6e2 100644
--- a/lib/cosNotification/src/cosNotification_Filter.erl
+++ b/lib/cosNotification/src/cosNotification_Filter.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -877,9 +877,9 @@ check_wildcard(Types, Which, WC, Domain, Type) ->
end,
check_types(Types, Which, NewWC).
-%% Change '*' to '.*', see regexp:parse/2 documentation.
+%% Change '*' to '.*', see re:compile/1 documentation.
convert_wildcard([], Acc) ->
- case regexp:parse(lists:reverse(Acc)) of
+ case re:compile(lists:reverse(Acc)) of
{ok, Expr} ->
Expr;
_ ->
@@ -900,37 +900,37 @@ match_types(_, _, []) ->
false;
match_types(Domain, Type, [{domain, WCDomain, Type}|T]) ->
L=length(Domain),
- case catch regexp:matches(Domain, WCDomain) of
- {match, []} ->
+ case catch re:run(Domain, WCDomain) of
+ nomatch ->
match_types(Domain, Type, T);
- {match, [{1, L}]} ->
+ {match, [{0, L}]} ->
true;
_->
match_types(Domain, Type, T)
end;
match_types(Domain, Type, [{type, Domain, WCType}|T]) ->
L=length(Type),
- case catch regexp:matches(Type, WCType) of
- {match, []} ->
+ case catch re:run(Type, WCType) of
+ nomatch ->
match_types(Domain, Type, T);
- {match, [{1, L}]} ->
+ {match, [{0, L}]} ->
true;
_->
match_types(Domain, Type, T)
end;
match_types(Domain, Type, [{both, WCDomain, WCType}|T]) ->
L1=length(Domain),
- case catch regexp:matches(Domain, WCDomain) of
- {match, []} ->
+ case catch re:run(Domain, WCDomain) of
+ nomatch ->
match_types(Domain, Type, T);
- {match, [{1, L1}]} ->
+ {match, [{0, L1}]} ->
L2=length(Type),
- case catch regexp:matches(Type, WCType) of
- {match, []} ->
+ case catch re:run(Type, WCType) of
+ nomatch ->
match_types(Domain, Type, T);
- {match, [{1, L2}]} ->
+ {match, [{0, L2}]} ->
true;
- _->
+ _ ->
match_types(Domain, Type, T)
end;
_->
diff --git a/lib/cosNotification/test/Makefile b/lib/cosNotification/test/Makefile
index df8f9e919b..43f73addae 100644
--- a/lib/cosNotification/test/Makefile
+++ b/lib/cosNotification/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/cosNotification_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = cosNotification.spec
+COVER_FILE = cosNotification.cover
IDL_FILES =
@@ -182,7 +183,7 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
- $(ERL_FILES) $(RELSYSDIR)
+ $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR)
$(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \
$(RELSYSDIR)/$(IDLOUTDIR)
diff --git a/lib/cosNotification/test/cosNotification.cover b/lib/cosNotification/test/cosNotification.cover
new file mode 100644
index 0000000000..604f313521
--- /dev/null
+++ b/lib/cosNotification/test/cosNotification.cover
@@ -0,0 +1,2 @@
+{incl_app,cosNotification,details}.
+
diff --git a/lib/cosNotification/test/cosNotification.spec b/lib/cosNotification/test/cosNotification.spec
index 8df89e7908..8ec1baca33 100644
--- a/lib/cosNotification/test/cosNotification.spec
+++ b/lib/cosNotification/test/cosNotification.spec
@@ -1,19 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../cosNotification_test"}}.
+{suites,"../cosNotification_test",all}.
diff --git a/lib/cosNotification/test/eventDB_SUITE.erl b/lib/cosNotification/test/eventDB_SUITE.erl
index 9ddfb3d902..64b8b712a9 100644
--- a/lib/cosNotification/test/eventDB_SUITE.erl
+++ b/lib/cosNotification/test/eventDB_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -41,7 +41,7 @@
-include("idl_output/notify_test.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%--------------- DEFINES ------------------------------------
-define(default_timeout, ?t:minutes(20)).
@@ -259,25 +259,37 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1, reorder_api/1, lookup_api/1,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ cases/0, init_per_suite/1, end_per_suite/1, reorder_api/1,
+ lookup_api/1,
discard_api/1, max_events_api/1, gc_api/1, auto_gc_api/1,
start_stop_time_api/1, mapping_filter_api/1, persisten_event_api/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
%%-----------------------------------------------------------------
%% Func: all/1
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosNotification interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [persisten_event_api, start_stop_time_api, mapping_filter_api,
- max_events_api, discard_api, reorder_api, lookup_api, gc_api,
- auto_gc_api].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [persisten_event_api, start_stop_time_api,
+ mapping_filter_api, max_events_api, discard_api,
+ reorder_api, lookup_api, gc_api, auto_gc_api].
@@ -290,12 +302,12 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
orber:jump_start(),
@@ -308,7 +320,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
cosTime:stop(),
diff --git a/lib/cosNotification/test/generated_SUITE.erl b/lib/cosNotification/test/generated_SUITE.erl
index 34b84041f0..fcf0d3967a 100644
--- a/lib/cosNotification/test/generated_SUITE.erl
+++ b/lib/cosNotification/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -71,12 +71,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -84,52 +84,110 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['CosNotification', 'CosNotification_AdminPropertiesAdmin',
- 'CosNotification_EventHeader', 'CosNotification_EventType',
- 'CosNotification_FixedEventHeader', 'CosNotification_NamedPropertyRange',
- 'CosNotification_Property', 'CosNotification_PropertyError',
- 'CosNotification_PropertyRange', 'CosNotification_QoSAdmin',
- 'CosNotification_StructuredEvent', 'CosNotification_UnsupportedAdmin',
- 'CosNotification_UnsupportedQoS', 'CosNotification_EventBatch',
- 'CosNotification_EventTypeSeq', 'CosNotification_NamedPropertyRangeSeq',
- 'CosNotification_PropertyErrorSeq', 'CosNotifyChannelAdmin_AdminLimit',
- 'CosNotifyChannelAdmin_AdminNotFound', 'CosNotifyChannelAdmin_ChannelNotFound',
- 'CosNotifyChannelAdmin_ConnectionAlreadyActive', 'CosNotifyChannelAdmin_ConnectionAlreadyInactive',
- 'CosNotifyChannelAdmin_NotConnected', 'CosNotifyChannelAdmin_AdminIDSeq',
- 'CosNotifyChannelAdmin_ChannelIDSeq', 'CosNotifyChannelAdmin_ProxyIDSeq',
- 'CosNotifyFilter_CallbackNotFound', 'CosNotifyFilter_ConstraintExp',
- 'CosNotifyFilter_ConstraintInfo', 'CosNotifyFilter_ConstraintNotFound',
- 'CosNotifyFilter_DuplicateConstraintID', 'CosNotifyFilter_FilterNotFound',
- 'CosNotifyFilter_InvalidConstraint', 'CosNotifyFilter_InvalidGrammar',
- 'CosNotifyFilter_InvalidValue', 'CosNotifyFilter_MappingConstraintInfo',
- 'CosNotifyFilter_MappingConstraintPair', 'CosNotifyFilter_UnsupportedFilterableData',
- 'CosNotifyFilter_CallbackIDSeq', 'CosNotifyFilter_ConstraintExpSeq',
- 'CosNotifyFilter_ConstraintIDSeq', 'CosNotifyFilter_ConstraintInfoSeq',
- 'CosNotifyFilter_FilterIDSeq', 'CosNotifyFilter_MappingConstraintInfoSeq',
- 'CosNotifyFilter_MappingConstraintPairSeq', 'CosNotifyComm_InvalidEventType',
- 'CosNotifyChannelAdmin_ConsumerAdmin', 'CosNotifyChannelAdmin_EventChannel',
- 'CosNotifyChannelAdmin_EventChannelFactory', 'CosNotifyChannelAdmin_ProxyConsumer',
- 'CosNotifyChannelAdmin_ProxyNotFound', 'CosNotifyChannelAdmin_ProxyPullConsumer',
- 'CosNotifyChannelAdmin_ProxyPullSupplier', 'CosNotifyChannelAdmin_ProxyPushConsumer',
- 'CosNotifyChannelAdmin_ProxyPushSupplier', 'CosNotifyChannelAdmin_ProxySupplier',
- 'CosNotifyChannelAdmin_SequenceProxyPullConsumer', 'CosNotifyChannelAdmin_SequenceProxyPullSupplier',
- 'CosNotifyChannelAdmin_SequenceProxyPushConsumer', 'CosNotifyChannelAdmin_SequenceProxyPushSupplier',
- 'CosNotifyChannelAdmin_StructuredProxyPullConsumer', 'CosNotifyChannelAdmin_StructuredProxyPullSupplier',
- 'CosNotifyChannelAdmin_StructuredProxyPushConsumer', 'CosNotifyChannelAdmin_StructuredProxyPushSupplier',
- 'CosNotifyChannelAdmin_SupplierAdmin', 'CosNotifyFilter_Filter',
- 'CosNotifyFilter_FilterAdmin', 'CosNotifyFilter_FilterFactory',
- 'CosNotifyFilter_MappingFilter', 'CosNotifyComm_NotifyPublish',
- 'CosNotifyComm_NotifySubscribe', 'CosNotifyComm_PullConsumer',
- 'CosNotifyComm_PullSupplier', 'CosNotifyComm_PushConsumer',
- 'CosNotifyComm_PushSupplier', 'CosNotifyComm_SequencePullConsumer',
- 'CosNotifyComm_SequencePullSupplier', 'CosNotifyComm_SequencePushConsumer',
- 'CosNotifyComm_SequencePushSupplier', 'CosNotifyComm_StructuredPullConsumer',
- 'CosNotifyComm_StructuredPullSupplier', 'CosNotifyComm_StructuredPushConsumer',
- 'CosNotifyComm_StructuredPushSupplier', 'oe_CosNotificationComm_Event',
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['CosNotification',
+ 'CosNotification_AdminPropertiesAdmin',
+ 'CosNotification_EventHeader',
+ 'CosNotification_EventType',
+ 'CosNotification_FixedEventHeader',
+ 'CosNotification_NamedPropertyRange',
+ 'CosNotification_Property',
+ 'CosNotification_PropertyError',
+ 'CosNotification_PropertyRange',
+ 'CosNotification_QoSAdmin',
+ 'CosNotification_StructuredEvent',
+ 'CosNotification_UnsupportedAdmin',
+ 'CosNotification_UnsupportedQoS',
+ 'CosNotification_EventBatch',
+ 'CosNotification_EventTypeSeq',
+ 'CosNotification_NamedPropertyRangeSeq',
+ 'CosNotification_PropertyErrorSeq',
+ 'CosNotifyChannelAdmin_AdminLimit',
+ 'CosNotifyChannelAdmin_AdminNotFound',
+ 'CosNotifyChannelAdmin_ChannelNotFound',
+ 'CosNotifyChannelAdmin_ConnectionAlreadyActive',
+ 'CosNotifyChannelAdmin_ConnectionAlreadyInactive',
+ 'CosNotifyChannelAdmin_NotConnected',
+ 'CosNotifyChannelAdmin_AdminIDSeq',
+ 'CosNotifyChannelAdmin_ChannelIDSeq',
+ 'CosNotifyChannelAdmin_ProxyIDSeq',
+ 'CosNotifyFilter_CallbackNotFound',
+ 'CosNotifyFilter_ConstraintExp',
+ 'CosNotifyFilter_ConstraintInfo',
+ 'CosNotifyFilter_ConstraintNotFound',
+ 'CosNotifyFilter_DuplicateConstraintID',
+ 'CosNotifyFilter_FilterNotFound',
+ 'CosNotifyFilter_InvalidConstraint',
+ 'CosNotifyFilter_InvalidGrammar',
+ 'CosNotifyFilter_InvalidValue',
+ 'CosNotifyFilter_MappingConstraintInfo',
+ 'CosNotifyFilter_MappingConstraintPair',
+ 'CosNotifyFilter_UnsupportedFilterableData',
+ 'CosNotifyFilter_CallbackIDSeq',
+ 'CosNotifyFilter_ConstraintExpSeq',
+ 'CosNotifyFilter_ConstraintIDSeq',
+ 'CosNotifyFilter_ConstraintInfoSeq',
+ 'CosNotifyFilter_FilterIDSeq',
+ 'CosNotifyFilter_MappingConstraintInfoSeq',
+ 'CosNotifyFilter_MappingConstraintPairSeq',
+ 'CosNotifyComm_InvalidEventType',
+ 'CosNotifyChannelAdmin_ConsumerAdmin',
+ 'CosNotifyChannelAdmin_EventChannel',
+ 'CosNotifyChannelAdmin_EventChannelFactory',
+ 'CosNotifyChannelAdmin_ProxyConsumer',
+ 'CosNotifyChannelAdmin_ProxyNotFound',
+ 'CosNotifyChannelAdmin_ProxyPullConsumer',
+ 'CosNotifyChannelAdmin_ProxyPullSupplier',
+ 'CosNotifyChannelAdmin_ProxyPushConsumer',
+ 'CosNotifyChannelAdmin_ProxyPushSupplier',
+ 'CosNotifyChannelAdmin_ProxySupplier',
+ 'CosNotifyChannelAdmin_SequenceProxyPullConsumer',
+ 'CosNotifyChannelAdmin_SequenceProxyPullSupplier',
+ 'CosNotifyChannelAdmin_SequenceProxyPushConsumer',
+ 'CosNotifyChannelAdmin_SequenceProxyPushSupplier',
+ 'CosNotifyChannelAdmin_StructuredProxyPullConsumer',
+ 'CosNotifyChannelAdmin_StructuredProxyPullSupplier',
+ 'CosNotifyChannelAdmin_StructuredProxyPushConsumer',
+ 'CosNotifyChannelAdmin_StructuredProxyPushSupplier',
+ 'CosNotifyChannelAdmin_SupplierAdmin',
+ 'CosNotifyFilter_Filter', 'CosNotifyFilter_FilterAdmin',
+ 'CosNotifyFilter_FilterFactory',
+ 'CosNotifyFilter_MappingFilter',
+ 'CosNotifyComm_NotifyPublish',
+ 'CosNotifyComm_NotifySubscribe',
+ 'CosNotifyComm_PullConsumer',
+ 'CosNotifyComm_PullSupplier',
+ 'CosNotifyComm_PushConsumer',
+ 'CosNotifyComm_PushSupplier',
+ 'CosNotifyComm_SequencePullConsumer',
+ 'CosNotifyComm_SequencePullSupplier',
+ 'CosNotifyComm_SequencePushConsumer',
+ 'CosNotifyComm_SequencePushSupplier',
+ 'CosNotifyComm_StructuredPullConsumer',
+ 'CosNotifyComm_StructuredPullSupplier',
+ 'CosNotifyComm_StructuredPushConsumer',
+ 'CosNotifyComm_StructuredPushSupplier',
+ oe_CosNotificationComm_Event,
'CosNotification_PropertySeq'].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -138,7 +196,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/cosNotification/test/grammar_SUITE.erl b/lib/cosNotification/test/grammar_SUITE.erl
index 30aec89e5f..2e63924b93 100644
--- a/lib/cosNotification/test/grammar_SUITE.erl
+++ b/lib/cosNotification/test/grammar_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,7 +42,7 @@
-include("idl_output/notify_test.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%--------------- DEFINES ------------------------------------
-define(default_timeout, ?t:minutes(20)).
@@ -64,10 +64,11 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ cases/0, init_per_suite/1, end_per_suite/1,
union_api/1, enum_api/1, simple_types_api/1,
components_api/1, positional_api/1, variable_api/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-import(cosNotification_Filter, [create_filter/1, eval/2]).
@@ -76,15 +77,25 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosNotification interfaces", ""];
-all(suite) -> {req,
- [],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [variable_api, union_api, enum_api, simple_types_api, components_api,
- positional_api].
-
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [variable_api, union_api, enum_api, simple_types_api,
+ components_api, positional_api].
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -96,14 +107,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
if
@@ -113,7 +124,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Config.
diff --git a/lib/cosNotification/test/notification_SUITE.erl b/lib/cosNotification/test/notification_SUITE.erl
index e2c560e4de..876a82d4a5 100644
--- a/lib/cosNotification/test/notification_SUITE.erl
+++ b/lib/cosNotification/test/notification_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,7 @@
-include("idl_output/notify_test.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%--------------- DEFINES ------------------------------------
-define(default_timeout, ?t:minutes(20)).
@@ -123,10 +123,11 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1, qos_api/1, adm_api/1,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1, qos_api/1, adm_api/1,
cosevent_api/1, filter_adm_api/1, events_api/1, events2_api/1,
event_qos_api/1, filter_api/1, mapping_filter_api/1, subscription_api/1,
- init_per_testcase/2, fin_per_testcase/2, persistent_max_events_api/1,
+ init_per_testcase/2, end_per_testcase/2, persistent_max_events_api/1,
persistent_timeout_events_api/1, persistent_recover_events_api/1,
app_test/1]).
@@ -137,19 +138,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosNotification interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [persistent_max_events_api, persistent_timeout_events_api,
- persistent_recover_events_api, mapping_filter_api, filter_api, filter_adm_api,
- event_qos_api, qos_api, adm_api, cosevent_api, subscription_api,
- events_api, events2_api, app_test].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+cases() ->
+ [persistent_max_events_api,
+ persistent_timeout_events_api,
+ persistent_recover_events_api, mapping_filter_api,
+ filter_api, filter_adm_api, event_qos_api, qos_api,
+ adm_api, cosevent_api, subscription_api, events_api,
+ events2_api, app_test].
-
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -161,14 +171,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
ok = corba:orb_init([{flags, 16#02}, {orber_debug_level, 10}]),
@@ -184,7 +194,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
cosNotificationApp:stop(),
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
diff --git a/lib/cosNotification/test/notify_test_impl.erl b/lib/cosNotification/test/notify_test_impl.erl
index 483610befd..dae7777089 100644
--- a/lib/cosNotification/test/notify_test_impl.erl
+++ b/lib/cosNotification/test/notify_test_impl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk
index a19facffd5..6613385579 100644
--- a/lib/cosNotification/vsn.mk
+++ b/lib/cosNotification/vsn.mk
@@ -1,18 +1,2 @@
-COSNOTIFICATION_VSN = 1.1.14
+COSNOTIFICATION_VSN = 1.1.16
-TICKETS = OTP-8489 \
- OTP-8543
-
-TICKETS_1.1.13 = OTP-8353 \
- OTP-8354 \
- OTP-8355
-
-TICKETS_1.1.12 = OTP-8201
-
-TICKETS_1.1.11 = OTP-7987
-
-TICKETS_1.1.10 = OTP-7837
-
-TICKETS_1.1.9 = OTP-7595
-
-TICKETS_1.1.8 = OTP-7553
diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml
index 54e29a5c01..1710769661 100644
--- a/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml
+++ b/lib/cosProperty/doc/src/CosPropertyService_PropertyNamesIterator.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml
index 4a2073d88d..2c1671bf77 100644
--- a/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml
+++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySet.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml
index 82c04e5573..67aa579e6a 100644
--- a/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml
+++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySetDefFactory.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml b/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml
index 06b3d2b26d..3fb4822948 100644
--- a/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml
+++ b/lib/cosProperty/doc/src/CosPropertyService_PropertySetFactory.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosProperty/doc/src/notes.xml b/lib/cosProperty/doc/src/notes.xml
index 11e6205ee9..540fdce762 100644
--- a/lib/cosProperty/doc/src/notes.xml
+++ b/lib/cosProperty/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2010</year>
+ <year>2000</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,16 +32,28 @@
</header>
<section>
- <title>cosProperty 1.1.12</title>
+ <title>cosProperty 1.1.13</title>
<section>
<title>Improvements and New Features</title>
<list type="bulleted">
<item>
- <p>
- Test suites published.</p>
- <p>
- Own Id: OTP-8543 Aux Id:</p>
+ <p>Eliminated Dialyzer warnings when using exit or throw.</p>
+ <p>Own id: OTP-9050 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>cosProperty 1.1.12</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Test suites published.</p>
+ <p>Own id: OTP-8543 Aux Id:</p>
</item>
</list>
</section>
diff --git a/lib/cosProperty/src/CosPropertyService_PropertySetDefFactory_impl.erl b/lib/cosProperty/src/CosPropertyService_PropertySetDefFactory_impl.erl
index b099026b88..202df42b61 100644
--- a/lib/cosProperty/src/CosPropertyService_PropertySetDefFactory_impl.erl
+++ b/lib/cosProperty/src/CosPropertyService_PropertySetDefFactory_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -152,12 +152,18 @@ create_initial_propertysetdef(_OE_This, State, PropDefs) ->
%% Internal functions
%%======================================================================
evaluate_propertysetdef(SetDefs) ->
- evaluate_propertysetdef(SetDefs, [], []).
+ case evaluate_propertysetdef(SetDefs, [], []) of
+ {ok, NewProperties} ->
+ NewProperties;
+ {error, Exc} ->
+ corba:raise(#'CosPropertyService_MultipleExceptions'{exceptions = Exc})
+ end.
+
evaluate_propertysetdef([], NewProperties, []) ->
%% No exceptions found.
- NewProperties;
+ {ok, NewProperties};
evaluate_propertysetdef([], _, Exc) ->
- corba:raise(#'CosPropertyService_MultipleExceptions'{exceptions = Exc});
+ {error, Exc};
evaluate_propertysetdef([#'CosPropertyService_PropertyDef'
{property_name = Name,
property_value = Value,
diff --git a/lib/cosProperty/src/CosPropertyService_PropertySetFactory_impl.erl b/lib/cosProperty/src/CosPropertyService_PropertySetFactory_impl.erl
index ad3cdb62d4..4bc29b99ac 100644
--- a/lib/cosProperty/src/CosPropertyService_PropertySetFactory_impl.erl
+++ b/lib/cosProperty/src/CosPropertyService_PropertySetFactory_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -153,12 +153,18 @@ create_initial_propertyset(_OE_This, State, Properties) ->
%% Internal functions
%%======================================================================
evaluate_propertyset(Sets) ->
- evaluate_propertyset(Sets, [], []).
+ case evaluate_propertyset(Sets, [], []) of
+ {ok, NewProperties} ->
+ NewProperties;
+ {error, Exc} ->
+ corba:raise(#'CosPropertyService_MultipleExceptions'{exceptions = Exc})
+ end.
+
evaluate_propertyset([], NewProperties, []) ->
%% No exceptions found.
- NewProperties;
+ {ok, NewProperties};
evaluate_propertyset([], _, Exc) ->
- corba:raise(#'CosPropertyService_MultipleExceptions'{exceptions = Exc});
+ {error, Exc};
evaluate_propertyset([#'CosPropertyService_Property'
{property_name = Name,
property_value = Value}|T], X, Exc) ->
diff --git a/lib/cosProperty/test/Makefile b/lib/cosProperty/test/Makefile
index ac0f4e298d..f6e0d0dbba 100644
--- a/lib/cosProperty/test/Makefile
+++ b/lib/cosProperty/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/cosProperty_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = cosProperty.spec
+COVER_FILE = cosProperty.cover
IDL_FILES =
@@ -121,7 +122,7 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
- $(ERL_FILES) $(RELSYSDIR)
+ $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
# $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR)
# $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \
diff --git a/lib/cosProperty/test/cosProperty.cover b/lib/cosProperty/test/cosProperty.cover
new file mode 100644
index 0000000000..a0f5f17671
--- /dev/null
+++ b/lib/cosProperty/test/cosProperty.cover
@@ -0,0 +1,2 @@
+{incl_app,cosProperty,details}.
+
diff --git a/lib/cosProperty/test/cosProperty.spec b/lib/cosProperty/test/cosProperty.spec
index d3e0001eef..d3d44321c8 100644
--- a/lib/cosProperty/test/cosProperty.spec
+++ b/lib/cosProperty/test/cosProperty.spec
@@ -1,20 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../cosProperty_test"}}.
-
+{suites,"../cosProperty_test",all}.
diff --git a/lib/cosProperty/test/generated_SUITE.erl b/lib/cosProperty/test/generated_SUITE.erl
index 80a7953949..1007ee2180 100644
--- a/lib/cosProperty/test/generated_SUITE.erl
+++ b/lib/cosProperty/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -71,12 +71,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -84,21 +84,51 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['CosPropertyService_ConflictingProperty', 'CosPropertyService_ConstraintNotSupported',
- 'CosPropertyService_FixedProperty', 'CosPropertyService_InvalidPropertyName',
- 'CosPropertyService_MultipleExceptions', 'CosPropertyService_Properties',
- 'CosPropertyService_Property', 'CosPropertyService_PropertyDef',
- 'CosPropertyService_PropertyDefs', 'CosPropertyService_PropertyException',
- 'CosPropertyService_PropertyExceptions', 'CosPropertyService_PropertyMode',
- 'CosPropertyService_PropertyModes', 'CosPropertyService_PropertyNames',
- 'CosPropertyService_PropertyNotFound', 'CosPropertyService_PropertyTypes',
- 'CosPropertyService_ReadOnlyProperty', 'CosPropertyService_UnsupportedMode',
- 'CosPropertyService_UnsupportedProperty', 'CosPropertyService_UnsupportedTypeCode',
- 'CosPropertyService_PropertyNamesIterator', 'CosPropertyService_PropertiesIterator',
- 'CosPropertyService_PropertySet', 'CosPropertyService_PropertySetDef',
- 'CosPropertyService_PropertySetDefFactory', 'CosPropertyService_PropertySetFactory'].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['CosPropertyService_ConflictingProperty',
+ 'CosPropertyService_ConstraintNotSupported',
+ 'CosPropertyService_FixedProperty',
+ 'CosPropertyService_InvalidPropertyName',
+ 'CosPropertyService_MultipleExceptions',
+ 'CosPropertyService_Properties',
+ 'CosPropertyService_Property',
+ 'CosPropertyService_PropertyDef',
+ 'CosPropertyService_PropertyDefs',
+ 'CosPropertyService_PropertyException',
+ 'CosPropertyService_PropertyExceptions',
+ 'CosPropertyService_PropertyMode',
+ 'CosPropertyService_PropertyModes',
+ 'CosPropertyService_PropertyNames',
+ 'CosPropertyService_PropertyNotFound',
+ 'CosPropertyService_PropertyTypes',
+ 'CosPropertyService_ReadOnlyProperty',
+ 'CosPropertyService_UnsupportedMode',
+ 'CosPropertyService_UnsupportedProperty',
+ 'CosPropertyService_UnsupportedTypeCode',
+ 'CosPropertyService_PropertyNamesIterator',
+ 'CosPropertyService_PropertiesIterator',
+ 'CosPropertyService_PropertySet',
+ 'CosPropertyService_PropertySetDef',
+ 'CosPropertyService_PropertySetDefFactory',
+ 'CosPropertyService_PropertySetFactory'].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%-----------------------------------------------------------------
@@ -109,7 +139,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/cosProperty/test/property_SUITE.erl b/lib/cosProperty/test/property_SUITE.erl
index 8fed3128ef..f440ffc2a1 100644
--- a/lib/cosProperty/test/property_SUITE.erl
+++ b/lib/cosProperty/test/property_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,7 @@
-include_lib("cosProperty/src/cosProperty.hrl").
-include_lib("cosProperty/include/CosPropertyService.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%--------------- DEFINES ------------------------------------
-define(default_timeout, ?t:minutes(20)).
@@ -86,8 +86,9 @@
%% External exports
%%-----------------------------------------------------------------
%% Fixed exports
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
%% Test cases
-export([create_setdef_api/1, create_set_api/1, define_with_mode_api/1,
define_api/1, names_iterator_api/1, properties_iterator_api/1,
@@ -98,16 +99,24 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosProperty interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [create_setdef_api, create_set_api, define_with_mode_api, define_api,
- names_iterator_api, properties_iterator_api, app_test].
-
-
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+cases() ->
+ [create_setdef_api, create_set_api,
+ define_with_mode_api, define_api, names_iterator_api,
+ properties_iterator_api, app_test].
%%-----------------------------------------------------------------
%% Init and cleanup functions.
@@ -120,14 +129,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
orber:jump_start(),
@@ -141,7 +150,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
application:stop(cosProperty),
diff --git a/lib/cosProperty/vsn.mk b/lib/cosProperty/vsn.mk
index 3d594a8329..deb1eb0450 100644
--- a/lib/cosProperty/vsn.mk
+++ b/lib/cosProperty/vsn.mk
@@ -1,13 +1,2 @@
-COSPROPERTY_VSN = 1.1.12
+COSPROPERTY_VSN = 1.1.13
-TICKETS = OTP-8543
-
-TICKETS_1.1.11 = OTP-8355
-
-TICKETS_1.1.10 = OTP-8201
-
-TICKETS_1.1.9 = OTP-7987
-
-TICKETS_1.1.8 = OTP-7837
-
-TICKETS_1.1.7 = OTP-7595
diff --git a/lib/cosTime/doc/src/CosTime_TIO.xml b/lib/cosTime/doc/src/CosTime_TIO.xml
index 91aa34d8c8..7b955c64e3 100644
--- a/lib/cosTime/doc/src/CosTime_TIO.xml
+++ b/lib/cosTime/doc/src/CosTime_TIO.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTime/doc/src/CosTime_TimeService.xml b/lib/cosTime/doc/src/CosTime_TimeService.xml
index 9b20f24794..66cfb694e6 100644
--- a/lib/cosTime/doc/src/CosTime_TimeService.xml
+++ b/lib/cosTime/doc/src/CosTime_TimeService.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTime/doc/src/CosTime_UTO.xml b/lib/cosTime/doc/src/CosTime_UTO.xml
index 73784e50f6..26e6eef978 100644
--- a/lib/cosTime/doc/src/CosTime_UTO.xml
+++ b/lib/cosTime/doc/src/CosTime_UTO.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml b/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml
index bc1ef39132..4b2e57642a 100644
--- a/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml
+++ b/lib/cosTime/doc/src/CosTimerEvent_TimerEventHandler.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml b/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml
index 90eeb5b2c5..fb3fe747e5 100644
--- a/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml
+++ b/lib/cosTime/doc/src/CosTimerEvent_TimerEventService.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTime/doc/src/cosTime.xml b/lib/cosTime/doc/src/cosTime.xml
index 8bc80f2322..978e048d48 100644
--- a/lib/cosTime/doc/src/cosTime.xml
+++ b/lib/cosTime/doc/src/cosTime.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTime/doc/src/notes.xml b/lib/cosTime/doc/src/notes.xml
index 40ebf42753..718ca23bc5 100644
--- a/lib/cosTime/doc/src/notes.xml
+++ b/lib/cosTime/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2010</year>
+ <year>2000</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,6 +33,22 @@
</header>
<section>
+ <title>cosTime 1.1.10</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Eliminated Dialyzer warnings when using exit or throw.</p>
+ <p>
+ Own Id: OTP-9050 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
<title>cosTime 1.1.9</title>
<section>
diff --git a/lib/cosTime/src/cosTime.erl b/lib/cosTime/src/cosTime.erl
index f4e67570ad..f7d03650af 100644
--- a/lib/cosTime/src/cosTime.erl
+++ b/lib/cosTime/src/cosTime.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -71,29 +71,39 @@
%%------------------------------------------------------------
install_time() ->
- install_loop(?IDL_TIME_MODULES,[]).
+ case install_loop(?IDL_TIME_MODULES,[]) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end.
install_timerevent() ->
- install_loop(?IDL_TIMEREVENT_MODULES,[]).
+ case install_loop(?IDL_TIMEREVENT_MODULES,[]) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end.
install_loop([], _) ->
ok;
install_loop([H|T], Accum) ->
case catch H:'oe_register'() of
{'EXIT',{unregistered,App}} ->
- ?write_ErrorMsg("Unable to register '~p'; application ~p not registered.
-Trying to unregister ~p~n", [H,App,Accum]),
+ ?write_ErrorMsg("Unable to register '~p'; application ~p not registered.\n"
+ "Trying to unregister ~p\n", [H,App,Accum]),
uninstall_loop(Accum, {exit, register});
{'EXCEPTION',_} ->
- ?write_ErrorMsg("Unable to register '~p'; propably already registered.
-You are adviced to confirm this.
-Trying to unregister ~p~n", [H,Accum]),
+ ?write_ErrorMsg("Unable to register '~p'; propably already registered.\n"
+ "You are adviced to confirm this.\n"
+ "Trying to unregister ~p\n", [H,Accum]),
uninstall_loop(Accum, {exit, register});
ok ->
install_loop(T, [H|Accum]);
_ ->
- ?write_ErrorMsg("Unable to register '~p'; reason unknown.
-Trying to unregister ~p~n", [H,Accum]),
+ ?write_ErrorMsg("Unable to register '~p'; reason unknown.\n"
+ "Trying to unregister ~p\n", [H,Accum]),
uninstall_loop(Accum, {exit, register})
end.
@@ -105,33 +115,43 @@ Trying to unregister ~p~n", [H,Accum]),
%%------------------------------------------------------------
uninstall_time() ->
- uninstall_loop(lists:reverse(?IDL_TIME_MODULES),ok).
+ case uninstall_loop(lists:reverse(?IDL_TIME_MODULES),ok) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end.
uninstall_timerevent() ->
- uninstall_loop(lists:reverse(?IDL_TIMEREVENT_MODULES),ok).
+ case uninstall_loop(lists:reverse(?IDL_TIMEREVENT_MODULES),ok) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end.
uninstall_loop([],ok) ->
ok;
uninstall_loop([],{exit, register}) ->
- exit({?MODULE, "oe_register failed"});
+ {error, {?MODULE, "oe_register failed"}};
uninstall_loop([],{exit, unregister}) ->
- exit({?MODULE, "oe_unregister failed"});
+ {error, {?MODULE, "oe_unregister failed"}};
uninstall_loop([],{exit, both}) ->
- exit({?MODULE, "oe_register and, for some of those already registered, oe_unregister failed"});
+ {error, {?MODULE, "oe_register and, for some of those already registered, oe_unregister failed"}};
uninstall_loop([H|T], Status) ->
case catch H:'oe_unregister'() of
ok ->
uninstall_loop(T, Status);
_ when Status == ok ->
- ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.
-You are adviced to confirm this.~n",[H]),
+ ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.\n"
+ "You are adviced to confirm this.~n",[H]),
uninstall_loop(T, {exit, unregister});
_ ->
- ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.
-You are adviced to confirm this.~n",[H]),
+ ?write_ErrorMsg("Unable to unregister '~p'; propably already unregistered.\n"
+ "You are adviced to confirm this.~n",[H]),
uninstall_loop(T, {exit, both})
end.
-
+
%%------------------------------------------------------------
%% function : start/stop
%% Arguments:
diff --git a/lib/cosTime/test/Makefile b/lib/cosTime/test/Makefile
index fde5c4facc..a07b27eecb 100644
--- a/lib/cosTime/test/Makefile
+++ b/lib/cosTime/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/cosTime_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = cosTime.spec
+COVER_FILE = cosTime.cover
IDL_FILES =
@@ -127,7 +128,7 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
- $(ERL_FILES) $(RELSYSDIR)
+ $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
# $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR)
# $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \
diff --git a/lib/cosTime/test/cosTime.cover b/lib/cosTime/test/cosTime.cover
new file mode 100644
index 0000000000..81a05b8cfd
--- /dev/null
+++ b/lib/cosTime/test/cosTime.cover
@@ -0,0 +1,2 @@
+{incl_app,cosTime,details}.
+
diff --git a/lib/cosTime/test/cosTime.spec b/lib/cosTime/test/cosTime.spec
index 3f50946043..8bf6f740fe 100644
--- a/lib/cosTime/test/cosTime.spec
+++ b/lib/cosTime/test/cosTime.spec
@@ -1,19 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../cosTime_test"}}.
+{suites,"../cosTime_test",all}.
diff --git a/lib/cosTime/test/generated_SUITE.erl b/lib/cosTime/test/generated_SUITE.erl
index 3a2153528f..119a5e322c 100644
--- a/lib/cosTime/test/generated_SUITE.erl
+++ b/lib/cosTime/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -71,12 +71,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -84,13 +84,31 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['TimeBase_IntervalT', 'TimeBase_UtcT', 'CosTime_TimeUnavailable',
- 'CosTimerEvent_TimerEventT', 'CosTime_TIO', 'CosTime_TimeService',
- 'CosTime_UTO', 'CosTimerEvent_TimerEventHandler',
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['TimeBase_IntervalT', 'TimeBase_UtcT',
+ 'CosTime_TimeUnavailable', 'CosTimerEvent_TimerEventT',
+ 'CosTime_TIO', 'CosTime_TimeService', 'CosTime_UTO',
+ 'CosTimerEvent_TimerEventHandler',
'CosTimerEvent_TimerEventService'].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -99,7 +117,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/cosTime/test/time_SUITE.erl b/lib/cosTime/test/time_SUITE.erl
index bb00395885..c92095eba5 100644
--- a/lib/cosTime/test/time_SUITE.erl
+++ b/lib/cosTime/test/time_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,9 +27,9 @@
%%--------------- INCLUDES -----------------------------------
--include("../src/cosTimeApp.hrl").
+-include_lib("cosTime/src/cosTimeApp.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%--------------- DEFINES ------------------------------------
-define(default_timeout, ?t:minutes(20)).
@@ -67,8 +67,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1, time_api/1, timerevent_api/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1, time_api/1, timerevent_api/1,
+ init_per_testcase/2, end_per_testcase/2,
app_test/1]).
%%-----------------------------------------------------------------
@@ -76,12 +77,22 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosTime interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
[time_api, timerevent_api, app_test].
@@ -97,14 +108,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
mnesia:delete_schema([node()]),
@@ -123,7 +134,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
cosTime:uninstall_time(),
diff --git a/lib/cosTime/vsn.mk b/lib/cosTime/vsn.mk
index 9d8057e8db..ebc5aff1cc 100644
--- a/lib/cosTime/vsn.mk
+++ b/lib/cosTime/vsn.mk
@@ -1,13 +1,2 @@
-COSTIME_VSN = 1.1.9
+COSTIME_VSN = 1.1.10
-TICKETS = OTP-8543
-
-TICKETS_1.1.8 = OTP-8355
-
-TICKETS_1.1.7 = OTP-8201
-
-TICKETS_1.1.6 = OTP-7987
-
-TICKETS_1.1.5 = OTP-7837
-
-TICKETS_1.1.4 = OTP-7595
diff --git a/lib/cosTransactions/doc/src/CosTransactions_Control.xml b/lib/cosTransactions/doc/src/CosTransactions_Control.xml
index f4d9a38d13..39cffa1889 100644
--- a/lib/cosTransactions/doc/src/CosTransactions_Control.xml
+++ b/lib/cosTransactions/doc/src/CosTransactions_Control.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTransactions/doc/src/CosTransactions_Synchronization.xml b/lib/cosTransactions/doc/src/CosTransactions_Synchronization.xml
index 62d19fe98f..cca0396e33 100644
--- a/lib/cosTransactions/doc/src/CosTransactions_Synchronization.xml
+++ b/lib/cosTransactions/doc/src/CosTransactions_Synchronization.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTransactions/doc/src/CosTransactions_Terminator.xml b/lib/cosTransactions/doc/src/CosTransactions_Terminator.xml
index 0a8ebe6975..c4457bcaa7 100644
--- a/lib/cosTransactions/doc/src/CosTransactions_Terminator.xml
+++ b/lib/cosTransactions/doc/src/CosTransactions_Terminator.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml b/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml
index 181801c574..162e6e8cd1 100644
--- a/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml
+++ b/lib/cosTransactions/doc/src/CosTransactions_TransactionFactory.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTransactions/doc/src/cosTransactions.xml b/lib/cosTransactions/doc/src/cosTransactions.xml
index 836506974c..f93004641f 100644
--- a/lib/cosTransactions/doc/src/cosTransactions.xml
+++ b/lib/cosTransactions/doc/src/cosTransactions.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/cosTransactions/test/Makefile b/lib/cosTransactions/test/Makefile
index 8b1264d404..44c90e8f84 100644
--- a/lib/cosTransactions/test/Makefile
+++ b/lib/cosTransactions/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -38,6 +38,7 @@ RELSYSDIR = $(RELEASE_PATH)/cosTransactions_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = cosTransactions.spec
+COVER_FILE = cosTransactions.cover
IDL_FILES = \
@@ -142,7 +143,7 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
- $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+ $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR)
$(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \
diff --git a/lib/cosTransactions/test/cosTransactions.cover b/lib/cosTransactions/test/cosTransactions.cover
new file mode 100644
index 0000000000..b27bae999d
--- /dev/null
+++ b/lib/cosTransactions/test/cosTransactions.cover
@@ -0,0 +1,2 @@
+{incl_app,cosTransactions,details}.
+
diff --git a/lib/cosTransactions/test/cosTransactions.spec b/lib/cosTransactions/test/cosTransactions.spec
index 8ad9259964..9918c8ca16 100644
--- a/lib/cosTransactions/test/cosTransactions.spec
+++ b/lib/cosTransactions/test/cosTransactions.spec
@@ -1,19 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../cosTransactions_test"}}.
+{suites,"../cosTransactions_test",all}.
diff --git a/lib/cosTransactions/test/etrap_test_lib.erl b/lib/cosTransactions/test/etrap_test_lib.erl
index 913a94510f..18a1cda35b 100644
--- a/lib/cosTransactions/test/etrap_test_lib.erl
+++ b/lib/cosTransactions/test/etrap_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTransactions/test/etrap_test_lib.hrl b/lib/cosTransactions/test/etrap_test_lib.hrl
index d488bf9d12..127d803515 100644
--- a/lib/cosTransactions/test/etrap_test_lib.hrl
+++ b/lib/cosTransactions/test/etrap_test_lib.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/cosTransactions/test/generated_SUITE.erl b/lib/cosTransactions/test/generated_SUITE.erl
index cc54eb168e..23ba631b69 100644
--- a/lib/cosTransactions/test/generated_SUITE.erl
+++ b/lib/cosTransactions/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -72,12 +72,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -85,21 +85,49 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['CosTransactions_Control', 'CosTransactions_Coordinator',
- 'CosTransactions_HeuristicCommit', 'CosTransactions_HeuristicHazard',
- 'CosTransactions_HeuristicMixed', 'CosTransactions_HeuristicRollback',
- 'CosTransactions_Inactive', 'CosTransactions_InvalidControl',
- 'CosTransactions_NoTransaction', 'CosTransactions_NotPrepared',
- 'CosTransactions_NotSubtransaction', 'CosTransactions_RecoveryCoordinator',
- 'CosTransactions_Resource', 'CosTransactions_SubtransactionAwareResource',
- 'CosTransactions_SubtransactionsUnavailable', 'CosTransactions_Terminator',
- 'CosTransactions_TransactionFactory', 'CosTransactions_Unavailable',
- 'CosTransactions_SynchronizationUnavailable', 'CosTransactions_TransIdentity',
- 'CosTransactions_PropagationContext', 'CosTransactions_otid_t',
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['CosTransactions_Control',
+ 'CosTransactions_Coordinator',
+ 'CosTransactions_HeuristicCommit',
+ 'CosTransactions_HeuristicHazard',
+ 'CosTransactions_HeuristicMixed',
+ 'CosTransactions_HeuristicRollback',
+ 'CosTransactions_Inactive',
+ 'CosTransactions_InvalidControl',
+ 'CosTransactions_NoTransaction',
+ 'CosTransactions_NotPrepared',
+ 'CosTransactions_NotSubtransaction',
+ 'CosTransactions_RecoveryCoordinator',
+ 'CosTransactions_Resource',
+ 'CosTransactions_SubtransactionAwareResource',
+ 'CosTransactions_SubtransactionsUnavailable',
+ 'CosTransactions_Terminator',
+ 'CosTransactions_TransactionFactory',
+ 'CosTransactions_Unavailable',
+ 'CosTransactions_SynchronizationUnavailable',
+ 'CosTransactions_TransIdentity',
+ 'CosTransactions_PropagationContext',
+ 'CosTransactions_otid_t',
'CosTransactions_WrongTransaction', 'ETraP_Server'].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -108,7 +136,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/cosTransactions/test/transactions_SUITE.erl b/lib/cosTransactions/test/transactions_SUITE.erl
index 8385d5a0fb..6480b956b3 100644
--- a/lib/cosTransactions/test/transactions_SUITE.erl
+++ b/lib/cosTransactions/test/transactions_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-include_lib("cosTransactions/include/CosTransactions.hrl").
-include("etrap_test_lib.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(20)).
@@ -36,20 +36,31 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1, resource_api/1, etrap_api/1,
- init_per_testcase/2, fin_per_testcase/2, app_test/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1, resource_api/1, etrap_api/1,
+ init_per_testcase/2, end_per_testcase/2, app_test/1]).
%%-----------------------------------------------------------------
%% Func: all/1
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the cosTransactions interfaces", ""];
-all(suite) -> {req,
- [mnesia, orber],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
[etrap_api, resource_api, app_test].
@@ -67,7 +78,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
'oe_etrap_test':'oe_unregister'(),
'oe_CosTransactions':'oe_unregister'(),
Path = code:which(?MODULE),
@@ -76,7 +87,7 @@ fin_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
mnesia:delete_schema([node()]),
mnesia:create_schema([node()]),
orber:install([node()]),
@@ -89,7 +100,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
application:stop(orber),
application:stop(mnesia),
mnesia:delete_schema([node()]),
diff --git a/lib/cosTransactions/vsn.mk b/lib/cosTransactions/vsn.mk
index 34775be7e4..82e46f51dd 100644
--- a/lib/cosTransactions/vsn.mk
+++ b/lib/cosTransactions/vsn.mk
@@ -1,14 +1 @@
COSTRANSACTIONS_VSN = 1.2.10
-
-TICKETS = OTP-8489 \
- OTP-8543
-
-TICKETS_1.2.9 = OTP-8355
-
-TICKETS_1.2.8 = OTP-8201
-
-TICKETS_1.2.7 = OTP-7987
-
-TICKETS_1.2.6 = OTP-7837
-
-TICKETS_1.2.5 = OTP-7595
diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in
index e728db18eb..040adcfd09 100644
--- a/lib/crypto/c_src/Makefile.in
+++ b/lib/crypto/c_src/Makefile.in
@@ -68,13 +68,13 @@ RELSYSDIR = $(RELEASE_PATH)/lib/crypto-$(VSN)
# ----------------------------------------------------
# Misc Macros
# ----------------------------------------------------
-OBJS = $(OBJDIR)/crypto.o
+OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o
NIF_MAKEFILE = $(PRIVDIR)/Makefile
ifeq ($(findstring win32,$(TARGET)), win32)
-NIF_LIB = $(LIBDIR)/crypto.dll
+NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).dll
else
-NIF_LIB = $(LIBDIR)/crypto.so
+NIF_LIB = $(LIBDIR)/crypto$(TYPEMARKER).so
endif
ifeq ($(HOST_OS),)
@@ -102,20 +102,30 @@ $(OBJDIR):
$(LIBDIR):
-@mkdir -p $(LIBDIR)
-$(OBJDIR)/%.o: %.c
+$(OBJDIR)/%$(TYPEMARKER).o: %.c
$(INSTALL_DIR) $(OBJDIR)
$(CC) -c -o $@ $(ALL_CFLAGS) $<
-$(LIBDIR)/crypto.so: $(OBJS)
+$(LIBDIR)/crypto$(TYPEMARKER).so: $(OBJS)
$(INSTALL_DIR) $(LIBDIR)
$(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(CRYPTO_LINK_LIB)
-$(LIBDIR)/crypto.dll: $(OBJS)
+$(LIBDIR)/crypto$(TYPEMARKER).dll: $(OBJS)
$(INSTALL_DIR) $(LIBDIR)
$(LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(OBJS) -llibeay32
clean:
- rm -f $(NIF_LIB) $(OBJS)
+ifeq ($(findstring win32,$(TARGET)), win32)
+ rm -f $(LIBDIR)/crypto.dll
+ rm -f $(LIBDIR)/crypto.debug.dll
+else
+ rm -f $(LIBDIR)/crypto.so
+ rm -f $(LIBDIR)/crypto.debug.so
+ rm -f $(LIBDIR)/crypto.valgrind.so
+endif
+ rm -f $(OBJDIR)/crypto.o
+ rm -f $(OBJDIR)/crypto.debug.o
+ rm -f $(OBJDIR)/crypto.valgrind.o
rm -f core *~
docs:
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index a71df1d7fd..b8786f6f94 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2010. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -62,10 +62,16 @@
# define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size) \
VALGRIND_MAKE_MEM_DEFINED(ptr,size)
- # define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size) \
- ((void) ((VALGRIND_CHECK_MEM_IS_DEFINED(ptr,size) == 0) ? 1 : \
- (fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%d) failed at %s:%d\r\n",\
- (ptr),(size), __FILE__, __LINE__), abort(), 0)))
+ # define ERL_VALGRIND_ASSERT_MEM_DEFINED(Ptr,Size) \
+ do { \
+ int __erl_valgrind_mem_defined = VALGRIND_CHECK_MEM_IS_DEFINED((Ptr),(Size)); \
+ if (__erl_valgrind_mem_defined != 0) { \
+ fprintf(stderr,"\r\n####### VALGRIND_ASSSERT(%p,%ld) failed at %s:%d\r\n", \
+ (Ptr),(long)(Size), __FILE__, __LINE__); \
+ abort(); \
+ } \
+ } while (0)
+
#else
# define ERL_VALGRIND_MAKE_MEM_DEFINED(ptr,size)
# define ERL_VALGRIND_ASSERT_MEM_DEFINED(ptr,size)
@@ -126,6 +132,7 @@ static ERL_NIF_TERM des_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
static ERL_NIF_TERM des_ecb_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM des_ede3_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_bytes_3(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
@@ -194,11 +201,13 @@ static ErlNifFunc nif_funcs[] = {
{"des_ecb_crypt", 3, des_ecb_crypt},
{"des_ede3_cbc_crypt", 6, des_ede3_cbc_crypt},
{"aes_cfb_128_crypt", 4, aes_cfb_128_crypt},
+ {"aes_ctr_encrypt", 3, aes_ctr_encrypt},
+ {"aes_ctr_decrypt", 3, aes_ctr_encrypt},
{"rand_bytes", 1, rand_bytes_1},
{"rand_bytes", 3, rand_bytes_3},
{"rand_uniform_nif", 2, rand_uniform_nif},
{"mod_exp_nif", 3, mod_exp_nif},
- {"dss_verify", 3, dss_verify},
+ {"dss_verify", 4, dss_verify},
{"rsa_verify", 4, rsa_verify},
{"aes_cbc_crypt", 4, aes_cbc_crypt},
{"exor", 2, exor},
@@ -207,7 +216,7 @@ static ErlNifFunc nif_funcs[] = {
{"rc4_encrypt_with_state", 2, rc4_encrypt_with_state},
{"rc2_40_cbc_crypt", 4, rc2_40_cbc_crypt},
{"rsa_sign_nif", 3, rsa_sign_nif},
- {"dss_sign_nif", 2, dss_sign_nif},
+ {"dss_sign_nif", 3, dss_sign_nif},
{"rsa_public_crypt", 4, rsa_public_crypt},
{"rsa_private_crypt", 4, rsa_private_crypt},
{"dh_generate_parameters_nif", 2, dh_generate_parameters_nif},
@@ -255,6 +264,7 @@ static ERL_NIF_TERM atom_unable_to_check_generator;
static ERL_NIF_TERM atom_not_suitable_generator;
static ERL_NIF_TERM atom_check_failed;
static ERL_NIF_TERM atom_unknown;
+static ERL_NIF_TERM atom_none;
static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info)
@@ -264,15 +274,15 @@ static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info)
}
static void* crypto_alloc(size_t size)
{
- return enif_alloc(NULL, size);
+ return enif_alloc(size);
}
static void* crypto_realloc(void* ptr, size_t size)
{
- return enif_realloc(NULL, ptr, size);
+ return enif_realloc(ptr, size);
}
static void crypto_free(void* ptr)
{
- enif_free(NULL, ptr);
+ enif_free(ptr);
}
static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
@@ -289,7 +299,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
if (sys_info.scheduler_threads > 1) {
int i;
- lock_vec = enif_alloc(env,CRYPTO_num_locks()*sizeof(*lock_vec));
+ lock_vec = enif_alloc(CRYPTO_num_locks()*sizeof(*lock_vec));
if (lock_vec==NULL) return -1;
memset(lock_vec,0,CRYPTO_num_locks()*sizeof(*lock_vec));
@@ -322,6 +332,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
atom_not_suitable_generator = enif_make_atom(env,"not_suitable_generator");
atom_check_failed = enif_make_atom(env,"check_failed");
atom_unknown = enif_make_atom(env,"unknown");
+ atom_none = enif_make_atom(env,"none");
*priv_data = NULL;
library_refc++;
@@ -371,7 +382,7 @@ static void unload(ErlNifEnv* env, void* priv_data)
enif_rwlock_destroy(lock_vec[i]);
}
}
- enif_free(env,lock_vec);
+ enif_free(lock_vec);
}
}
/*else NIF library still used by other (new) module code */
@@ -652,6 +663,34 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
return ret;
}
+/* Common for both encrypt and decrypt
+*/
+static ERL_NIF_TERM aes_ctr_encrypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Key, IVec, Data) */
+ ErlNifBinary key, ivec, text;
+ AES_KEY aes_key;
+ unsigned char ivec_clone[16]; /* writable copy */
+ unsigned char ecount_buf[AES_BLOCK_SIZE];
+ unsigned int num = 0;
+ ERL_NIF_TERM ret;
+
+ if (!enif_inspect_iolist_as_binary(env, argv[0], &key)
+ || AES_set_encrypt_key(key.data, key.size*8, &aes_key) != 0
+ || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16
+ || !enif_inspect_iolist_as_binary(env, argv[2], &text)) {
+ return enif_make_badarg(env);
+ }
+ memcpy(ivec_clone, ivec.data, 16);
+ memset(ecount_buf, 0, sizeof(ecount_buf));
+ AES_ctr128_encrypt((unsigned char *) text.data,
+ enif_make_new_binary(env, text.size, &ret),
+ text.size, &aes_key, ivec_clone, ecount_buf, &num);
+
+ /* To do an incremental {en|de}cryption, the state to to keep between calls
+ must include ivec_clone, ecount_buf and num. */
+ return ret;
+}
+
static ERL_NIF_TERM rand_bytes_1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Bytes) */
unsigned bytes;
@@ -704,12 +743,13 @@ static int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp)
static ERL_NIF_TERM rand_uniform_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (Lo,Hi) */
- BIGNUM *bn_from, *bn_to, *bn_rand;
+ BIGNUM *bn_from = NULL, *bn_to, *bn_rand;
unsigned char* data;
unsigned dlen;
ERL_NIF_TERM ret;
if (!get_bn_from_mpint(env, argv[0], &bn_from)
|| !get_bn_from_mpint(env, argv[1], &bn_rand)) {
+ if (bn_from) BN_free(bn_from);
return enif_make_badarg(env);
}
@@ -766,17 +806,16 @@ static int inspect_mpint(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin)
}
static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Data,Signature,Key=[P, Q, G, Y]) */
+{/* (DigestType,Data,Signature,Key=[P, Q, G, Y]) */
ErlNifBinary data_bin, sign_bin;
- BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_y;
+ BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL;
unsigned char hmacbuf[SHA_DIGEST_LENGTH];
ERL_NIF_TERM head, tail;
DSA *dsa;
int i;
- if (!inspect_mpint(env,argv[0],&data_bin)
- || !inspect_mpint(env,argv[1],&sign_bin)
- || !enif_get_list_cell(env, argv[2], &head, &tail)
+ if (!inspect_mpint(env, argv[2], &sign_bin)
+ || !enif_get_list_cell(env, argv[3], &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa_p)
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa_q)
@@ -785,10 +824,23 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa_y)
|| !enif_is_empty_list(env,tail)) {
-
+ badarg:
+ if (dsa_p) BN_free(dsa_p);
+ if (dsa_q) BN_free(dsa_q);
+ if (dsa_g) BN_free(dsa_g);
+ if (dsa_y) BN_free(dsa_y);
return enif_make_badarg(env);
}
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) {
+ SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ }
+ else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin)
+ && data_bin.size == SHA_DIGEST_LENGTH) {
+ memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH);
+ }
+ else {
+ goto badarg;
+ }
dsa = DSA_new();
dsa->p = dsa_p;
@@ -941,7 +993,7 @@ static ERL_NIF_TERM rc4_encrypt_with_state(ErlNifEnv* env, int argc, const ERL_N
RC4(rc4_key, data.size, data.data,
enif_make_new_binary(env, data.size, &new_data));
- return enif_make_tuple2(env,argv[0],new_data);
+ return enif_make_tuple2(env,new_state,new_data);
}
static ERL_NIF_TERM rc2_40_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
@@ -994,7 +1046,7 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
RSA_free(rsa);
return enif_make_badarg(env);
}
- enif_alloc_binary(env, RSA_size(rsa), &ret_bin);
+ enif_alloc_binary(RSA_size(rsa), &ret_bin);
if (is_sha) {
SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, SHA_DIGEST_LENGTH);
@@ -1011,19 +1063,19 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
if (i) {
ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, rsa_s_len);
if (rsa_s_len != data_bin.size) {
- enif_realloc_binary(env, &ret_bin, rsa_s_len);
+ enif_realloc_binary(&ret_bin, rsa_s_len);
ERL_VALGRIND_ASSERT_MEM_DEFINED(ret_bin.data, rsa_s_len);
}
return enif_make_binary(env,&ret_bin);
}
else {
- enif_release_binary(env, &ret_bin);
+ enif_release_binary(&ret_bin);
return atom_error;
}
}
static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
-{/* (Data,Key=[P,Q,G,PrivKey]) */
+{/* (DigesType, Data, Key=[P,Q,G,PrivKey]) */
ErlNifBinary data_bin, ret_bin;
ERL_NIF_TERM head, tail;
unsigned char hmacbuf[SHA_DIGEST_LENGTH];
@@ -1032,8 +1084,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
int i;
dsa->pub_key = NULL;
- if (!inspect_mpint(env, argv[0], &data_bin)
- || !enif_get_list_cell(env, argv[1], &head, &tail)
+ if (!enif_get_list_cell(env, argv[2], &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa->p)
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa->q)
@@ -1042,20 +1093,28 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dsa->priv_key)
|| !enif_is_empty_list(env,tail)) {
-
+ goto badarg;
+ }
+ if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) {
+ SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
+ }
+ else if (argv[0] == atom_none && enif_inspect_binary(env,argv[1],&data_bin)
+ && data_bin.size == SHA_DIGEST_LENGTH) {
+ memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH);
+ }
+ else {
+ badarg:
DSA_free(dsa);
return enif_make_badarg(env);
}
- SHA1(data_bin.data+4, data_bin.size-4, hmacbuf);
-
- enif_alloc_binary(env, DSA_size(dsa), &ret_bin);
+ enif_alloc_binary(DSA_size(dsa), &ret_bin);
i = DSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH,
ret_bin.data, &dsa_s_len, dsa);
DSA_free(dsa);
if (i) {
if (dsa_s_len != ret_bin.size) {
- enif_realloc_binary(env, &ret_bin, dsa_s_len);
+ enif_realloc_binary(&ret_bin, dsa_s_len);
}
return enif_make_binary(env, &ret_bin);
}
@@ -1100,10 +1159,10 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER
return enif_make_badarg(env);
}
- enif_alloc_binary(env, RSA_size(rsa), &ret_bin);
+ enif_alloc_binary(RSA_size(rsa), &ret_bin);
if (argv[3] == atom_true) {
- ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,data_len);
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(data_bin.data,data_bin.size);
i = RSA_public_encrypt(data_bin.size, data_bin.data,
ret_bin.data, rsa, padding);
if (i > 0) {
@@ -1115,7 +1174,7 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER
ret_bin.data, rsa, padding);
if (i > 0) {
ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i);
- enif_realloc_binary(env, &ret_bin, i);
+ enif_realloc_binary(&ret_bin, i);
}
}
RSA_free(rsa);
@@ -1123,6 +1182,7 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER
return enif_make_binary(env,&ret_bin);
}
else {
+ enif_release_binary(&ret_bin);
return atom_error;
}
}
@@ -1148,10 +1208,10 @@ static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
return enif_make_badarg(env);
}
- enif_alloc_binary(env, RSA_size(rsa), &ret_bin);
+ enif_alloc_binary(RSA_size(rsa), &ret_bin);
if (argv[3] == atom_true) {
- ERL_VALGRIND_ASSERT_MEM_DEFINED(buf+i,data_len);
+ ERL_VALGRIND_ASSERT_MEM_DEFINED(data_bin.data,data_bin.size);
i = RSA_private_encrypt(data_bin.size, data_bin.data,
ret_bin.data, rsa, padding);
if (i > 0) {
@@ -1163,7 +1223,7 @@ static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
ret_bin.data, rsa, padding);
if (i > 0) {
ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, i);
- enif_realloc_binary(env, &ret_bin, i);
+ enif_realloc_binary(&ret_bin, i);
}
}
RSA_free(rsa);
@@ -1171,6 +1231,7 @@ static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE
return enif_make_binary(env,&ret_bin);
}
else {
+ enif_release_binary(&ret_bin);
return atom_error;
}
}
@@ -1250,7 +1311,7 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_
|| !enif_get_list_cell(env, tail, &head, &tail)
|| !get_bn_from_mpint(env, head, &dh_params->g)
|| !enif_is_empty_list(env, tail)) {
-
+ DH_free(dh_params);
return enif_make_badarg(env);
}
@@ -1277,7 +1338,7 @@ static ERL_NIF_TERM dh_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_
static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{/* (OthersPublicKey, MyPrivateKey, DHParams=[P,G]) */
DH* dh_params = DH_new();
- BIGNUM* pubkey;
+ BIGNUM* pubkey = NULL;
int i;
ErlNifBinary ret_bin;
ERL_NIF_TERM ret, head, tail;
@@ -1293,11 +1354,11 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T
ret = enif_make_badarg(env);
}
else {
- enif_alloc_binary(env, DH_size(dh_params), &ret_bin);
+ enif_alloc_binary(DH_size(dh_params), &ret_bin);
i = DH_compute_key(ret_bin.data, pubkey, dh_params);
if (i > 0) {
if (i != ret_bin.size) {
- enif_realloc_binary(env, &ret_bin, i);
+ enif_realloc_binary(&ret_bin, i);
}
ret = enif_make_binary(env, &ret_bin);
}
@@ -1305,6 +1366,7 @@ static ERL_NIF_TERM dh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_T
ret = atom_error;
}
}
+ if (pubkey) BN_free(pubkey);
DH_free(dh_params);
return ret;
}
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 256eab3e3c..c407350c47 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -53,7 +53,7 @@
<p>aes: Advanced Encryption Standard (AES) (FIPS 197) </p>
</item>
<item>
- <p>ecb, cbc, cfb, ofb: Recommendation for Block Cipher Modes
+ <p>ecb, cbc, cfb, ofb, ctr: Recommendation for Block Cipher Modes
of Operation (NIST SP 800-38A).</p>
</item>
<item>
@@ -557,6 +557,34 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
</desc>
</func>
<func>
+ <name>aes_ctr_encrypt(Key, IVec, Text) -> Cipher</name>
+ <fsummary>Encrypt <c>Text</c>according to AES in Counter mode</fsummary>
+ <type>
+ <v>Key = Text = iolist() | binary()</v>
+ <v>IVec = Cipher = binary()</v>
+ </type>
+ <desc>
+ <p>Encrypts <c>Text</c> according to AES in Counter mode (CTR). <c>Text</c>
+ can be any number of bytes. <c>Key</c> is the AES key and must be either
+ 128, 192 or 256 bits long. <c>IVec</c> is an arbitrary initializing vector of 128 bits
+ (16 bytes).</p>
+ </desc>
+ </func>
+ <func>
+ <name>aes_ctr_decrypt(Key, IVec, Cipher) -> Text</name>
+ <fsummary>Decrypt <c>Cipher</c>according to AES in Counter mode</fsummary>
+ <type>
+ <v>Key = Cipher = iolist() | binary()</v>
+ <v>IVec = Text = binary()</v>
+ </type>
+ <desc>
+ <p>Decrypts <c>Cipher</c> according to AES in Counter mode (CTR). <c>Cipher</c>
+ can be any number of bytes. <c>Key</c> is the AES key and must be either
+ 128, 192 or 256 bits long. <c>IVec</c> is an arbitrary initializing vector of 128 bits
+ (16 bytes).</p>
+ </desc>
+ </func>
+ <func>
<name>erlint(Mpint) -> N</name>
<name>mpint(N) -> Mpint</name>
<fsummary>Convert between binary multi-precision integer and erlang big integer</fsummary>
@@ -755,39 +783,44 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]>
<func>
<name>dss_sign(Data, Key) -> Signature</name>
+ <name>dss_sign(DigestType, Data, Key) -> Signature</name>
<fsummary>Sign the data using dsa with given private key.</fsummary>
<type>
- <v>Digest = Mpint</v>
+ <v>DigestType = sha | none (default is sha)</v>
+ <v>Data = Mpint | ShaDigest</v>
<v>Key = [P, Q, G, X]</v>
<v>P, Q, G, X = Mpint</v>
<d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss
parameters and <c>X</c> is the private key.</d>
- <v>Mpint = binary()</v>
+ <v>ShaDigest = binary() with length 20 bytes</v>
<v>Signature = binary()</v>
</type>
<desc>
- <p>Calculates the sha digest of the <c>Data</c>
- and creates a DSS signature with the private key <c>Key</c>
- of the digest.</p>
+ <p>Creates a DSS signature with the private key <c>Key</c> of a digest.
+ If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>.
+ If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p>
</desc>
</func>
<func>
<name>dss_verify(Data, Signature, Key) -> Verified</name>
+ <name>dss_verify(DigestType, Data, Signature, Key) -> Verified</name>
<fsummary>Verify the data and signature using dsa with given public key.</fsummary>
<type>
<v>Verified = boolean()</v>
- <v>Digest, Signature = Mpint</v>
+ <v>DigestType = sha | none</v>
+ <v>Data = Mpint | ShaDigest</v>
+ <v>Signature = Mpint</v>
<v>Key = [P, Q, G, Y]</v>
<v>P, Q, G, Y = Mpint</v>
<d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss
parameters and <c>Y</c> is the public key.</d>
- <v>Mpint = binary()</v>
+ <v>ShaDigest = binary() with length 20 bytes</v>
</type>
<desc>
- <p>Calculates the sha digest of the <c>Data</c> and verifies that the
- digest matches the DSS signature using the public key <c>Key</c>.
- </p>
+ <p>Verifies that a digest matches the DSS signature using the public key <c>Key</c>.
+ If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>.
+ If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p>
</desc>
</func>
diff --git a/lib/crypto/doc/src/crypto_app.xml b/lib/crypto/doc/src/crypto_app.xml
index bf1d1ae1f7..1c01e3f099 100644
--- a/lib/crypto/doc/src/crypto_app.xml
+++ b/lib/crypto/doc/src/crypto_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 6b9d1f56f1..5e9bda3920 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -30,6 +30,123 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 2.0.2.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Misc. Updates.</p>
+ <p>
+ Own Id: OTP-9132</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Crypto 2.0.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ AES CTR encryption support in <c>crypto</c>.</p>
+ <p>
+ Own Id: OTP-8752 Aux Id: seq11642 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Crypto 2.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Crypto dialyzer type error in md5_mac and sha_mac.</p>
+ <p>
+ Own Id: OTP-8718</p>
+ </item>
+ <item>
+ <p>
+ RC4 stream cipher didn't work. This since the new NIF
+ implementation of <c>crypto:rc4_encrypt_with_state/2</c>
+ introduced in <c>crypto-2.0</c> didn't return an updated
+ state. (Thanks to Paul Guyot)</p>
+ <p>
+ Own Id: OTP-8781</p>
+ </item>
+ <item>
+ <p>
+ A number of memory leaks in the crypto NIF library have
+ been fixed.</p>
+ <p>
+ Own Id: OTP-8810</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added erlang:system_info(build_type) which makes it
+ easier to chose drivers, NIF libraries, etc based on
+ build type of the runtime system.</p>
+ <p>
+ The NIF library for crypto can now be built for valgrind
+ and/or debug as separate NIF libraries that will be
+ automatically loaded if the runtime system has been built
+ with a matching build type.</p>
+ <p>
+ Own Id: OTP-8760</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Crypto 2.0</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ crypto application changed to use NIFs instead of driver.</p>
+ <p>
+ Own Id: OTP-8333</p>
+ </item>
+ <item>
+ <p>
+ des_ecb_encrypt/2 and des_ecb_decrypt/2 has been added to
+ the crypto module. The crypto:md4/1 function has been
+ documented.</p>
+ <p>
+ Own Id: OTP-8551</p>
+ </item>
+ <item>
+ <p>The undocumented, unsupport, and deprecated function
+ <c>lists:flat_length/1</c> has been removed.</p>
+ <p>
+ Own Id: OTP-8584</p>
+ </item>
+ <item>
+ <p>
+ New variants of <c>crypto:dss_sign</c> and
+ <c>crypto:dss_verify</c> with an extra argument to
+ control how the digest is calculated.</p>
+ <p>
+ Own Id: OTP-8700</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 1.6.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/crypto/doc/src/release_notes.xml b/lib/crypto/doc/src/release_notes.xml
index 0c2ee23e22..0a84ca1c15 100644
--- a/lib/crypto/doc/src/release_notes.xml
+++ b/lib/crypto/doc/src/release_notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/crypto/src/crypto.app.src b/lib/crypto/src/crypto.app.src
index a24760a781..5548b6a1b5 100644
--- a/lib/crypto/src/crypto.app.src
+++ b/lib/crypto/src/crypto.app.src
@@ -1,23 +1,23 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
{application, crypto,
- [{description, "CRYPTO version 1"},
+ [{description, "CRYPTO version 2"},
{vsn, "%VSN%"},
{modules, [crypto,
crypto_app,
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 5b1ce96caf..d6e2e033c0 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -40,8 +40,8 @@
-export([exor/2]).
-export([rc4_encrypt/2, rc4_set_key/1, rc4_encrypt_with_state/2]).
-export([rc2_40_cbc_encrypt/3, rc2_40_cbc_decrypt/3]).
--export([dss_verify/3, rsa_verify/3, rsa_verify/4]).
--export([dss_sign/2, rsa_sign/2, rsa_sign/3]).
+-export([dss_verify/3, dss_verify/4, rsa_verify/3, rsa_verify/4]).
+-export([dss_sign/2, dss_sign/3, rsa_sign/2, rsa_sign/3]).
-export([rsa_public_encrypt/3, rsa_private_decrypt/3]).
-export([rsa_private_encrypt/3, rsa_public_decrypt/3]).
-export([dh_generate_key/1, dh_generate_key/2, dh_compute_key/3]).
@@ -51,6 +51,7 @@
-export([aes_cbc_128_encrypt/3, aes_cbc_128_decrypt/3]).
-export([aes_cbc_256_encrypt/3, aes_cbc_256_decrypt/3]).
-export([aes_cbc_ivec/1]).
+-export([aes_ctr_encrypt/3, aes_ctr_decrypt/3]).
-export([dh_generate_parameters/2, dh_check/1]). %% Testing see below
@@ -80,8 +81,13 @@
rc2_40_cbc_encrypt, rc2_40_cbc_decrypt,
%% idea_cbc_encrypt, idea_cbc_decrypt,
aes_cbc_256_encrypt, aes_cbc_256_decrypt,
+ aes_ctr_encrypt, aes_ctr_decrypt,
info_lib]).
+-type rsa_digest_type() :: 'md5' | 'sha'.
+-type dss_digest_type() :: 'none' | 'sha'.
+-type crypto_integer() :: binary() | integer().
+
-define(nif_stub,nif_stub_error(?LINE)).
-on_load(on_load/0).
@@ -89,22 +95,42 @@
-define(CRYPTO_NIF_VSN,101).
on_load() ->
- LibName = "crypto",
+ LibBaseName = "crypto",
PrivDir = code:priv_dir(crypto),
- Lib1 = filename:join([PrivDir, "lib", LibName]),
- Status = case erlang:load_nif(Lib1, ?CRYPTO_NIF_VSN) of
+ LibName = case erlang:system_info(build_type) of
+ opt ->
+ LibBaseName;
+ Type ->
+ LibTypeName = LibBaseName ++ "." ++ atom_to_list(Type),
+ case (filelib:wildcard(
+ filename:join(
+ [PrivDir,
+ "lib",
+ LibTypeName ++ "*"])) /= []) orelse
+ (filelib:wildcard(
+ filename:join(
+ [PrivDir,
+ "lib",
+ erlang:system_info(system_architecture),
+ LibTypeName ++ "*"])) /= []) of
+ true -> LibTypeName;
+ false -> LibBaseName
+ end
+ end,
+ Lib = filename:join([PrivDir, "lib", LibName]),
+ Status = case erlang:load_nif(Lib, ?CRYPTO_NIF_VSN) of
ok -> ok;
{error, {load_failed, _}}=Error1 ->
- LibDir2 =
+ ArchLibDir =
filename:join([PrivDir, "lib",
erlang:system_info(system_architecture)]),
Candidate =
- filelib:wildcard(filename:join([LibDir2,LibName ++ "*" ])),
+ filelib:wildcard(filename:join([ArchLibDir,LibName ++ "*" ])),
case Candidate of
[] -> Error1;
_ ->
- Lib2 = filename:join([LibDir2, LibName]),
- erlang:load_nif(Lib2, ?CRYPTO_NIF_VSN)
+ ArchLib = filename:join([ArchLibDir, LibName]),
+ erlang:load_nif(ArchLib, ?CRYPTO_NIF_VSN)
end;
Error1 -> Error1
end,
@@ -115,10 +141,9 @@ on_load() ->
"OpenSSL might not be installed on this system.~n",[E,Str]),
Status
end.
-
nif_stub_error(Line) ->
- erlang:error({nif_not_loaded,module,?MODULE,line,Line}).
+ erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}).
start() ->
application:start(crypto).
@@ -146,6 +171,12 @@ version() -> ?CRYPTO_VSN.
%%
%% MD5
%%
+
+-spec md5(iodata()) -> binary().
+-spec md5_init() -> binary().
+-spec md5_update(binary(), iodata()) -> binary().
+-spec md5_final(binary()) -> binary().
+
md5(_Data) -> ?nif_stub.
md5_init() -> ?nif_stub.
md5_update(_Context, _Data) -> ?nif_stub.
@@ -154,6 +185,11 @@ md5_final(_Context) -> ?nif_stub.
%%
%% MD4
%%
+-spec md4(iodata()) -> binary().
+-spec md4_init() -> binary().
+-spec md4_update(binary(), iodata()) -> binary().
+-spec md4_final(binary()) -> binary().
+
md4(_Data) -> ?nif_stub.
md4_init() -> ?nif_stub.
md4_update(_Context, _Data) -> ?nif_stub.
@@ -162,6 +198,11 @@ md4_final(_Context) -> ?nif_stub.
%%
%% SHA
%%
+-spec sha(iodata()) -> binary().
+-spec sha_init() -> binary().
+-spec sha_update(binary(), iodata()) -> binary().
+-spec sha_final(binary()) -> binary().
+
sha(_Data) -> ?nif_stub.
sha_init() -> ?nif_stub.
sha_update(_Context, _Data) -> ?nif_stub.
@@ -175,6 +216,9 @@ sha_final(_Context) -> ?nif_stub.
%%
%% MD5_MAC
%%
+-spec md5_mac(iodata(), iodata()) -> binary().
+-spec md5_mac_96(iodata(), iodata()) -> binary().
+
md5_mac(Key, Data) ->
md5_mac_n(Key,Data,16).
@@ -186,6 +230,9 @@ md5_mac_n(_Key,_Data,_MacSz) -> ?nif_stub.
%%
%% SHA_MAC
%%
+-spec sha_mac(iodata(), iodata()) -> binary().
+-spec sha_mac_96(iodata(), iodata()) -> binary().
+
sha_mac(Key, Data) ->
sha_mac_n(Key,Data,20).
@@ -201,6 +248,9 @@ sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub.
%%
%% DES - in cipher block chaining mode (CBC)
%%
+-spec des_cbc_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec des_cbc_decrypt(iodata(), binary(), iodata()) -> binary().
+
des_cbc_encrypt(Key, IVec, Data) ->
des_cbc_crypt(Key, IVec, Data, true).
@@ -215,6 +265,8 @@ des_cbc_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%% Returns the IVec to be used in the next iteration of
%% des_cbc_[encrypt|decrypt].
%%
+-spec des_cbc_ivec(iodata()) -> binary().
+
des_cbc_ivec(Data) when is_binary(Data) ->
{_, IVec} = split_binary(Data, size(Data) - 8),
IVec;
@@ -224,6 +276,9 @@ des_cbc_ivec(Data) when is_list(Data) ->
%%
%% DES - in electronic codebook mode (ECB)
%%
+-spec des_ecb_encrypt(iodata(), iodata()) -> binary().
+-spec des_ecb_decrypt(iodata(), iodata()) -> binary().
+
des_ecb_encrypt(Key, Data) ->
des_ecb_crypt(Key, Data, true).
des_ecb_decrypt(Key, Data) ->
@@ -233,6 +288,11 @@ des_ecb_crypt(_Key, _Data, _IsEncrypt) -> ?nif_stub.
%%
%% DES3 - in cipher block chaining mode (CBC)
%%
+-spec des3_cbc_encrypt(iodata(), iodata(), iodata(), binary(), iodata()) ->
+ binary().
+-spec des3_cbc_decrypt(iodata(), iodata(), iodata(), binary(), iodata()) ->
+ binary().
+
des3_cbc_encrypt(Key1, Key2, Key3, IVec, Data) ->
des_ede3_cbc_encrypt(Key1, Key2, Key3, IVec, Data).
des_ede3_cbc_encrypt(Key1, Key2, Key3, IVec, Data) ->
@@ -248,6 +308,14 @@ des_ede3_cbc_crypt(_Key1, _Key2, _Key3, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%%
%% Blowfish
%%
+-spec blowfish_ecb_encrypt(iodata(), iodata()) -> binary().
+-spec blowfish_ecb_decrypt(iodata(), iodata()) -> binary().
+-spec blowfish_cbc_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec blowfish_cbc_decrypt(iodata(), binary(), iodata()) -> binary().
+-spec blowfish_cfb64_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec blowfish_cfb64_decrypt(iodata(), binary(), iodata()) -> binary().
+-spec blowfish_ofb64_encrypt(iodata(), binary(), iodata()) -> binary().
+
blowfish_ecb_encrypt(Key, Data) ->
bf_ecb_crypt(Key,Data, true).
@@ -277,6 +345,9 @@ blowfish_ofb64_encrypt(_Key, _IVec, _Data) -> ?nif_stub.
%%
%% AES in cipher feedback mode (CFB)
%%
+-spec aes_cfb_128_encrypt(iodata(), binary(), iodata()) -> binary().
+-spec aes_cfb_128_decrypt(iodata(), binary(), iodata()) -> binary().
+
aes_cfb_128_encrypt(Key, IVec, Data) ->
aes_cfb_128_crypt(Key, IVec, Data, true).
@@ -289,6 +360,10 @@ aes_cfb_128_crypt(_Key, _IVec, _Data, _IsEncrypt) -> ?nif_stub.
%%
%% RAND - pseudo random numbers using RN_ functions in crypto lib
%%
+-spec rand_bytes(non_neg_integer()) -> binary().
+-spec rand_uniform(crypto_integer(), crypto_integer()) ->
+ crypto_integer().
+
rand_bytes(_Bytes) -> ?nif_stub.
rand_bytes(_Bytes, _Topmask, _Bottommask) -> ?nif_stub.
@@ -331,9 +406,16 @@ mod_exp_nif(_Base,_Exp,_Mod) -> ?nif_stub.
%%
%% DSS, RSA - verify
%%
+-spec dss_verify(binary(), binary(), [binary()]) -> boolean().
+-spec dss_verify(dss_digest_type(), binary(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(binary(), binary(), [binary()]) -> boolean().
+-spec rsa_verify(rsa_digest_type(), binary(), binary(), [binary()]) ->
+ boolean().
%% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey
-dss_verify(_Data,_Signature,_Key) -> ?nif_stub.
+dss_verify(Data,Signature,Key) ->
+ dss_verify(sha, Data, Signature, Key).
+dss_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub.
% Key = [E,N] E=PublicExponent N=PublicModulus
rsa_verify(Data,Signature,Key) ->
@@ -345,13 +427,20 @@ rsa_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub.
%% DSS, RSA - sign
%%
%% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey
-dss_sign(Data, Key) ->
- case dss_sign_nif(Data,Key) of
+-spec dss_sign(binary(), [binary()]) -> binary().
+-spec dss_sign(dss_digest_type(), binary(), [binary()]) -> binary().
+-spec rsa_sign(binary(), [binary()]) -> binary().
+-spec rsa_sign(rsa_digest_type(), binary(), [binary()]) -> binary().
+
+dss_sign(Data,Key) ->
+ dss_sign(sha,Data,Key).
+dss_sign(Type, Data, Key) ->
+ case dss_sign_nif(Type,Data,Key) of
error -> erlang:error(badkey, [Data, Key]);
Sign -> Sign
end.
-dss_sign_nif(_Data,_Key) -> ?nif_stub.
+dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
%% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent
rsa_sign(Data,Key) ->
@@ -368,6 +457,16 @@ rsa_sign_nif(_Type,_Data,_Key) -> ?nif_stub.
%%
%% rsa_public_encrypt
%% rsa_private_decrypt
+-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'.
+
+-spec rsa_public_encrypt(binary(), [binary()], rsa_padding()) ->
+ binary().
+-spec rsa_public_decrypt(binary(), [binary()], rsa_padding()) ->
+ binary().
+-spec rsa_private_encrypt(binary(), [binary()], rsa_padding()) ->
+ binary().
+-spec rsa_private_decrypt(binary(), [binary()], rsa_padding()) ->
+ binary().
%% Binary, Key = [E,N]
rsa_public_encrypt(BinMesg, Key, Padding) ->
@@ -409,6 +508,14 @@ rsa_public_decrypt(BinMesg, Key, Padding) ->
%%
%% AES - with 128 or 256 bit key in cipher block chaining mode (CBC)
%%
+-spec aes_cbc_128_encrypt(iodata(), binary(), iodata()) ->
+ binary().
+-spec aes_cbc_128_decrypt(iodata(), binary(), iodata()) ->
+ binary().
+-spec aes_cbc_256_encrypt(iodata(), binary(), iodata()) ->
+ binary().
+-spec aes_cbc_256_decrypt(iodata(), binary(), iodata()) ->
+ binary().
aes_cbc_128_encrypt(Key, IVec, Data) ->
aes_cbc_crypt(Key, IVec, Data, true).
@@ -437,17 +544,31 @@ aes_cbc_ivec(Data) when is_binary(Data) ->
aes_cbc_ivec(Data) when is_list(Data) ->
aes_cbc_ivec(list_to_binary(Data)).
+%%
+%% AES - in counter mode (CTR)
+%%
+-spec aes_ctr_encrypt(iodata(), binary(), iodata()) ->
+ binary().
+-spec aes_ctr_decrypt(iodata(), binary(), iodata()) ->
+ binary().
+
+aes_ctr_encrypt(_Key, _IVec, _Data) -> ?nif_stub.
+aes_ctr_decrypt(_Key, _IVec, _Cipher) -> ?nif_stub.
%%
%% XOR - xor to iolists and return a binary
%% NB doesn't check that they are the same size, just concatenates
%% them and sends them to the driver
%%
+-spec exor(iodata(), iodata()) -> binary().
+
exor(_A, _B) -> ?nif_stub.
%%
%% RC4 - symmetric stream cipher
%%
+-spec rc4_encrypt(iodata(), iodata()) -> binary().
+
rc4_encrypt(_Key, _Data) -> ?nif_stub.
rc4_set_key(_Key) -> ?nif_stub.
rc4_encrypt_with_state(_State, _Data) -> ?nif_stub.
@@ -490,6 +611,10 @@ dh_check([_Prime,_Gen]) -> ?nif_stub.
%% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()]
%% PrivKey = mpint()
+-spec dh_generate_key([binary()]) -> {binary(),binary()}.
+-spec dh_generate_key(binary()|undefined, [binary()]) ->
+ {binary(),binary()}.
+
dh_generate_key(DHParameters) ->
dh_generate_key(undefined, DHParameters).
dh_generate_key(PrivateKey, DHParameters) ->
@@ -502,6 +627,8 @@ dh_generate_key_nif(_PrivateKey, _DHParameters) -> ?nif_stub.
%% DHParameters = [P (Prime)= mpint(), G(Generator) = mpint()]
%% MyPrivKey, OthersPublicKey = mpint()
+-spec dh_compute_key(binary(), binary(), [binary()]) -> binary().
+
dh_compute_key(OthersPublicKey, MyPrivateKey, DHParameters) ->
case dh_compute_key_nif(OthersPublicKey,MyPrivateKey,DHParameters) of
error -> erlang:error(computation_failed, [OthersPublicKey,MyPrivateKey,DHParameters]);
diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile
index e728875027..3150bd472d 100644
--- a/lib/crypto/test/Makefile
+++ b/lib/crypto/test/Makefile
@@ -76,8 +76,8 @@ release_spec:
release_tests_spec: $(TEST_TARGET)
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) crypto.spec $(RELTEST_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
release_docs_spec:
diff --git a/lib/crypto/test/blowfish_SUITE.erl b/lib/crypto/test/blowfish_SUITE.erl
index d117e7cc3d..a7a2c25467 100644
--- a/lib/crypto/test/blowfish_SUITE.erl
+++ b/lib/crypto/test/blowfish_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("test_server_line.hrl").
-define(TIMEOUT, 120000). % 2 min
@@ -45,8 +45,12 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- crypto:start(),
- Config.
+ case catch crypto:start() of
+ ok ->
+ Config;
+ _Else ->
+ {skip,"Could not start crypto!"}
+ end.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
@@ -100,15 +104,20 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test Blowfish functionality"];
-
-all(suite) ->
- [ecb,
- cbc,
- cfb64,
- ofb64
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+[ecb, cbc, cfb64, ofb64].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Test cases start here.
%%--------------------------------------------------------------------
diff --git a/lib/crypto/test/crypto.cover b/lib/crypto/test/crypto.cover
new file mode 100644
index 0000000000..61ee372ec5
--- /dev/null
+++ b/lib/crypto/test/crypto.cover
@@ -0,0 +1,2 @@
+{incl_app,crypto,details}.
+
diff --git a/lib/crypto/test/crypto.spec b/lib/crypto/test/crypto.spec
index 7ba5696189..cc09970cb3 100644
--- a/lib/crypto/test/crypto.spec
+++ b/lib/crypto/test/crypto.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../crypto_test"}}.
-
+{suites,"../crypto_test",all}.
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 08d7a0ce99..fe8f8e69a0 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,11 @@
%%
-module(crypto_SUITE).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
+ end_per_testcase/2,
info/1,
link_test/1,
md5/1,
@@ -44,6 +43,7 @@
aes_cfb/1,
aes_cbc/1,
aes_cbc_iter/1,
+ aes_ctr/1,
mod_exp_test/1,
rand_uniform_test/1,
rsa_verify_test/1,
@@ -54,53 +54,48 @@
dh/1,
exor_test/1,
rc4_test/1,
+ rc4_stream_test/1,
blowfish_cfb64/1,
smp/1,
cleanup/1]).
-export([hexstr2bin/1]).
-all(suite) ->
- [link_test,
- {conf,info,[md5,
- md5_update,
- md4,
- md4_update,
- md5_mac,
- md5_mac_io,
- sha,
- sha_update,
-%% sha256,
-%% sha256_update,
-%% sha512,
-%% sha512_update,
- des_cbc,
- aes_cfb,
- aes_cbc,
- aes_cbc_iter,
- des_cbc_iter,
- des_ecb,
- rand_uniform_test,
- rsa_verify_test,
- dsa_verify_test,
- rsa_sign_test,
- dsa_sign_test,
- rsa_encrypt_decrypt,
- dh,
- exor_test,
- rc4_test,
- mod_exp_test,
- blowfish_cfb64,
- smp],
- cleanup}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [link_test, md5, md5_update, md4, md4_update, md5_mac,
+ md5_mac_io, sha, sha_update,
+ %% sha256, sha256_update, sha512,sha512_update,
+ des_cbc, aes_cfb, aes_cbc,
+ aes_cbc_iter, aes_ctr, des_cbc_iter, des_ecb, rand_uniform_test,
+ rsa_verify_test, dsa_verify_test, rsa_sign_test,
+ dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test,
+ rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64,
+ smp].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
init_per_testcase(_Name,Config) ->
io:format("init_per_testcase\n"),
?line crypto:start(),
Config.
-fin_per_testcase(_Name,Config) ->
- io:format("fin_per_testcase\n"),
+end_per_testcase(_Name,Config) ->
+ io:format("end_per_testcase\n"),
?line crypto:stop(),
Config.
@@ -617,6 +612,65 @@ aes_cbc_decrypt_iter(Key,IVec,Data, Acc) ->
aes_cbc_decrypt_iter(Key,IVec2,Rest, <<Acc/binary, Plain/binary>>).
+aes_ctr(doc) -> "CTR";
+aes_ctr(Config) when is_list(Config) ->
+ %% Sample data from NIST Spec.Publ. 800-38A
+ %% F.5.1 CTR-AES128.Encrypt
+ Key128 = hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"),
+ Samples128 = [{"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", % Input Block
+ "6bc1bee22e409f96e93d7e117393172a", % Plaintext
+ "874d6191b620e3261bef6864990db6ce"},% Ciphertext
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff00",
+ "ae2d8a571e03ac9c9eb76fac45af8e51",
+ "9806f66b7970fdff8617187bb9fffdff"},
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff01",
+ "30c81c46a35ce411e5fbc1191a0a52ef",
+ "5ae4df3edbd5d35e5b4f09020db03eab"},
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff02",
+ "f69f2445df4f9b17ad2b417be66c3710",
+ "1e031dda2fbe03d1792170a0f3009cee"}],
+ lists:foreach(fun(S) -> aes_ctr_do(Key128,S) end, Samples128),
+
+ %% F.5.3 CTR-AES192.Encrypt
+ Key192 = hexstr2bin("8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"),
+ Samples192 = [{"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", % Input Block
+ "6bc1bee22e409f96e93d7e117393172a", % Plaintext
+ "1abc932417521ca24f2b0459fe7e6e0b"},% Ciphertext
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff00",
+ "ae2d8a571e03ac9c9eb76fac45af8e51",
+ "090339ec0aa6faefd5ccc2c6f4ce8e94"},
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff01",
+ "30c81c46a35ce411e5fbc1191a0a52ef",
+ "1e36b26bd1ebc670d1bd1d665620abf7"},
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff02",
+ "f69f2445df4f9b17ad2b417be66c3710",
+ "4f78a7f6d29809585a97daec58c6b050"}],
+ lists:foreach(fun(S) -> aes_ctr_do(Key192,S) end, Samples192),
+
+ %% F.5.5 CTR-AES256.Encrypt
+ Key256 = hexstr2bin("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"),
+ Samples256 = [{"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", % Input Block
+ "6bc1bee22e409f96e93d7e117393172a", % Plaintext
+ "601ec313775789a5b7a7f504bbf3d228"},% Ciphertext
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff00",
+ "ae2d8a571e03ac9c9eb76fac45af8e51",
+ "f443e3ca4d62b59aca84e990cacaf5c5"},
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff01",
+ "30c81c46a35ce411e5fbc1191a0a52ef",
+ "2b0930daa23de94ce87017ba2d84988d"},
+ {"f0f1f2f3f4f5f6f7f8f9fafbfcfdff02",
+ "f69f2445df4f9b17ad2b417be66c3710",
+ "dfc9c58db67aada613c2dd08457941a6"}],
+ lists:foreach(fun(S) -> aes_ctr_do(Key256,S) end, Samples256).
+
+
+aes_ctr_do(Key,{IVec, Plain, Cipher}) ->
+ ?line I = hexstr2bin(IVec),
+ ?line P = hexstr2bin(Plain),
+ ?line C = crypto:aes_ctr_encrypt(Key, I, P),
+ ?line m(C, hexstr2bin(Cipher)),
+ ?line m(P, crypto:aes_ctr_decrypt(Key, I, C)).
+
%%
%%
mod_exp_test(doc) ->
@@ -770,18 +824,18 @@ dsa_verify_test(Config) when is_list(Config) ->
crypto:mpint(Key)
],
- ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(SigBlob),
+ ?line m(my_dss_verify(sized_binary(Msg), sized_binary(SigBlob),
ValidKey), true),
BadMsg = one_bit_wrong(Msg),
- ?line m(crypto:dss_verify(sized_binary(BadMsg), sized_binary(SigBlob),
+ ?line m(my_dss_verify(sized_binary(BadMsg), sized_binary(SigBlob),
ValidKey), false),
BadSig = one_bit_wrong(SigBlob),
- ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(BadSig),
+ ?line m(my_dss_verify(sized_binary(Msg), sized_binary(BadSig),
ValidKey), false),
SizeErr = size(SigBlob) - 13,
- BadArg = (catch crypto:dss_verify(sized_binary(Msg), <<SizeErr:32, SigBlob/binary>>,
+ BadArg = (catch my_dss_verify(sized_binary(Msg), <<SizeErr:32, SigBlob/binary>>,
ValidKey)),
?line m(element(1,element(2,BadArg)), badarg),
@@ -791,9 +845,12 @@ dsa_verify_test(Config) when is_list(Config) ->
crypto:mpint(Key+17)
],
- ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(SigBlob),
+ ?line m(my_dss_verify(sized_binary(Msg), sized_binary(SigBlob),
InValidKey), false).
+
+one_bit_wrong(List) when is_list(List) ->
+ lists:map(fun(Bin) -> one_bit_wrong(Bin) end, List);
one_bit_wrong(Bin) ->
Half = size(Bin) div 2,
<<First:Half/binary, Byte:8, Last/binary>> = Bin,
@@ -843,15 +900,15 @@ dsa_sign_test(Config) when is_list(Config) ->
ParamG = 18320614775012672475365915366944922415598782131828709277168615511695849821411624805195787607930033958243224786899641459701930253094446221381818858674389863050420226114787005820357372837321561754462061849169568607689530279303056075793886577588606958623645901271866346406773590024901668622321064384483571751669,
Params = [crypto:mpint(ParamP), crypto:mpint(ParamQ), crypto:mpint(ParamG)],
- ?line Sig1 = crypto:dss_sign(sized_binary(Msg), Params ++ [crypto:mpint(PrivKey)]),
+ ?line Sig1 = my_dss_sign(sized_binary(Msg), Params ++ [crypto:mpint(PrivKey)]),
- ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(Sig1),
+ ?line m(my_dss_verify(sized_binary(Msg), Sig1,
Params ++ [crypto:mpint(PubKey)]), true),
- ?line m(crypto:dss_verify(sized_binary(one_bit_wrong(Msg)), sized_binary(Sig1),
+ ?line m(my_dss_verify(sized_binary(one_bit_wrong(Msg)), Sig1,
Params ++ [crypto:mpint(PubKey)]), false),
- ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(one_bit_wrong(Sig1)),
+ ?line m(my_dss_verify(sized_binary(Msg), one_bit_wrong(Sig1),
Params ++ [crypto:mpint(PubKey)]), false),
%%?line Bad = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PubKey)]),
@@ -976,6 +1033,21 @@ rc4_test(Config) when is_list(Config) ->
CT2 = binary_to_list(crypto:rc4_encrypt(K, R2)),
ok.
+rc4_stream_test(doc) ->
+ ["Test rc4 stream encryption ."];
+rc4_stream_test(suite) ->
+ [];
+rc4_stream_test(Config) when is_list(Config) ->
+ CT1 = <<"hej">>,
+ CT2 = <<" p� dig">>,
+ K = "apaapa",
+ State0 = crypto:rc4_set_key(K),
+ {State1, R1} = crypto:rc4_encrypt_with_state(State0, CT1),
+ {_State2, R2} = crypto:rc4_encrypt_with_state(State1, CT2),
+ R = list_to_binary([R1, R2]),
+ <<71,112,14,44,140,33,212,144,155,47>> = R,
+ ok.
+
blowfish_cfb64(doc) -> ["Test Blowfish encrypt/decrypt."];
blowfish_cfb64(suite) -> [];
blowfish_cfb64(Config) when is_list(Config) ->
@@ -1026,7 +1098,7 @@ worker_loop(0, _) ->
worker_loop(N, Config) ->
Funcs = { md5, md5_update, md5_mac, md5_mac_io, sha, sha_update, des_cbc,
aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test,
- rsa_verify_test, exor_test, rc4_test, mod_exp_test },
+ rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test },
F = element(random:uniform(size(Funcs)),Funcs),
%%io:format("worker ~p calling ~p\n",[self(),F]),
@@ -1132,3 +1204,24 @@ zero_bin(N) when is_integer(N) ->
<<0:N8/integer>>;
zero_bin(B) when is_binary(B) ->
zero_bin(size(B)).
+
+my_dss_verify(Data,[Sign|Tail],Key) ->
+ Res = my_dss_verify(Data,sized_binary(Sign),Key),
+ case Tail of
+ [] -> Res;
+ _ -> ?line Res = my_dss_verify(Data,Tail,Key)
+ end;
+my_dss_verify(Data,Sign,Key) ->
+ ?line Res = crypto:dss_verify(Data, Sign, Key),
+ ?line Res = crypto:dss_verify(sha, Data, Sign, Key),
+ ?line <<_:32,Raw/binary>> = Data,
+ ?line Res = crypto:dss_verify(none, crypto:sha(Raw), Sign, Key),
+ Res.
+
+my_dss_sign(Data,Key) ->
+ ?line S1 = crypto:dss_sign(Data, Key),
+ ?line S2 = crypto:dss_sign(sha, Data, Key),
+ ?line <<_:32,Raw/binary>> = Data,
+ ?line S3 = crypto:dss_sign(none, crypto:sha(Raw), Key),
+ [S1,S2,S3].
+
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index d04736fc2b..e2d6fd0b37 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 2.0
+CRYPTO_VSN = 2.0.2.1
diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml
index afd49e8593..3aa169a135 100644
--- a/lib/debugger/doc/src/notes.xml
+++ b/lib/debugger/doc/src/notes.xml
@@ -32,6 +32,75 @@
<p>This document describes the changes made to the Debugger
application.</p>
+<section><title>Debugger 3.2.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix issues reported by dialyzer.</p>
+ <p>
+ Own Id: OTP-9107</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Debugger 3.2.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Miscellaneous updates</p>
+ <p>
+ Own Id: OTP-8976</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Debugger 3.2.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Type specs have been added/cleaned up. (Thanks to Kostis
+ Sagonas.)</p>
+ <p>
+ Own Id: OTP-8757</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Debugger 3.2.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warnings due to new autoimported BIFs removed</p>
+ <p>
+ Own Id: OTP-8674 Aux Id: OTP-8579 </p>
+ </item>
+ <item>
+ <p>
+ The predefined builtin type tid() has been removed.
+ Instead, ets:tid() should be used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8687</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Debugger 3.2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl
index b56ebef14a..3732c40c73 100644
--- a/lib/debugger/src/dbg_debugged.erl
+++ b/lib/debugger/src/dbg_debugged.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,6 +19,8 @@
-module(dbg_debugged).
%% External exports
+%% Avoid warning for local function demonitor/1 clashing with autoimported BIF.
+-compile({no_auto_import,[demonitor/1]}).
-export([eval/3]).
%%====================================================================
diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl
index 7ccb9793a3..e9502eaa2b 100644
--- a/lib/debugger/src/dbg_icmd.erl
+++ b/lib/debugger/src/dbg_icmd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -94,7 +94,7 @@ break_p(Mod, Line, Le, Bs) ->
Bool = case Cond of
null -> true;
{CM, CN} ->
- try apply(CM, CN, [Bs]) of
+ try CM:CN(Bs) of
true -> true;
false -> false;
_Term -> false
@@ -245,7 +245,7 @@ handle_int_msg({attached, AttPid}, Status, _Bs,
%% Tell attached process in which module evalution is located
if
- Le==1 ->
+ Le =:= 1 ->
tell_attached({attached, undefined, -1, get(trace)});
true ->
tell_attached({attached, M, Line, get(trace)}),
@@ -269,7 +269,7 @@ handle_int_msg(detached, _Status, _Bs, _Ieval) ->
handle_int_msg({old_code,Mod}, Status, Bs,
#ieval{level=Le,module=M}=Ieval) ->
if
- Status==idle, Le==1 ->
+ Status =:= idle, Le =:= 1 ->
erase([Mod|db]),
put(cache, []);
true ->
@@ -352,9 +352,9 @@ set_stack_trace(true) ->
set_stack_trace(all);
set_stack_trace(Flag) ->
if
- Flag==false ->
+ Flag =:= false ->
put(stack, []);
- Flag==no_tail; Flag==all ->
+ Flag =:= no_tail; Flag =:= all ->
ignore
end,
put(trace_stack, Flag),
@@ -367,7 +367,7 @@ bindings(Bs, nostack) ->
Bs;
bindings(Bs, SP) ->
case dbg_ieval:stack_level() of
- Le when SP>Le ->
+ Le when SP > Le ->
Bs;
_ ->
dbg_ieval:bindings(SP)
@@ -377,7 +377,6 @@ messages() ->
{messages, Msgs} = erlang:process_info(get(self), messages),
Msgs.
-
%%====================================================================
%% Evaluating expressions within process context
%%====================================================================
@@ -398,7 +397,7 @@ eval_restricted({From,_Mod,Cmd,SP}, Bs) ->
From ! {self(), {eval_rsp, Rsp}}
end.
-eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP<Le->
+eval_nonrestricted({From,Mod,Cmd,SP}, Bs, #ieval{level=Le}) when SP < Le->
%% Evaluate in stack
eval_restricted({From, Mod, Cmd, SP}, Bs),
Bs;
@@ -424,15 +423,15 @@ eval_nonrestricted({From, _Mod, Cmd, _SP}, Bs,
eval_nonrestricted_1({match,_,{var,_,Var},Expr}, Bs, Ieval) ->
{value,Res,Bs2} =
dbg_ieval:eval_expr(Expr, Bs, Ieval#ieval{last_call=false}),
- Bs3 = case lists:keysearch(Var, 1, Bs) of
- {value, {Var,_Value}} ->
+ Bs3 = case lists:keyfind(Var, 1, Bs) of
+ {Var,_Value} ->
lists:keyreplace(Var, 1, Bs2, {Var,Res});
false -> [{Var,Res} | Bs2]
end,
{Res,Bs3};
eval_nonrestricted_1({var,_,Var}, Bs, _Ieval) ->
- Res = case lists:keysearch(Var, 1, Bs) of
- {value, {Var, Value}} -> Value;
+ Res = case lists:keyfind(Var, 1, Bs) of
+ {Var, Value} -> Value;
false -> unbound
end,
{Res,Bs};
@@ -458,7 +457,6 @@ parse_cmd(Cmd, LineNo) ->
{ok,Forms} = erl_parse:parse_exprs(Tokens),
Forms.
-
%%====================================================================
%% Library functions for attached process handling
%%====================================================================
@@ -470,13 +468,12 @@ tell_attached(Msg) ->
AttPid ! {self(), Msg}
end.
-
%%====================================================================
%% get_binding/2
%%====================================================================
get_binding(Var, Bs) ->
- case lists:keysearch(Var, 1, Bs) of
- {value, {Var, Value}} -> {value, Value};
+ case lists:keyfind(Var, 1, Bs) of
+ {Var, Value} -> {value, Value};
false -> unbound
end.
diff --git a/lib/debugger/src/dbg_ieval.erl b/lib/debugger/src/dbg_ieval.erl
index c13fda7ac1..306323f8ea 100644
--- a/lib/debugger/src/dbg_ieval.erl
+++ b/lib/debugger/src/dbg_ieval.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -120,9 +120,9 @@ check_exit_msg({'EXIT', Int, Reason}, _Bs, #ieval{level=Le}) ->
%% This *must* be interpreter which has terminated,
%% we are not linked to anyone else
if
- Le==1 ->
+ Le =:= 1 ->
exit(Reason);
- Le>1 ->
+ Le > 1 ->
exit({Int, Reason})
end;
check_exit_msg({'DOWN',_,_,_,Reason}, Bs,
@@ -139,9 +139,9 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs,
%% importance in this case
%% If we don't save them, however, post-mortem analysis
%% of the process isn't possible
- undefined when Le==1 -> % died outside interpreted code
+ undefined when Le =:= 1 -> % died outside interpreted code
{};
- undefined when Le>1 ->
+ undefined when Le > 1 ->
StackBin = term_to_binary(get(stack)),
{{Mod, Li}, Bs, StackBin};
@@ -152,9 +152,9 @@ check_exit_msg({'DOWN',_,_,_,Reason}, Bs,
dbg_iserver:cast(get(int), {set_exit_info,self(),ExitInfo}),
if
- Le==1 ->
+ Le =:= 1 ->
exit(Reason);
- Le>1 ->
+ Le > 1 ->
exit({get(self), Reason})
end;
check_exit_msg(_Msg, _Bs, _Ieval) ->
@@ -271,7 +271,7 @@ meta_loop(Debugged, Bs, #ieval{level=Le} = Ieval) ->
end;
%% Re-entry to Meta from non-interpreted code
- {re_entry, Debugged, {eval,{M,F,As}}} when Le==1 ->
+ {re_entry, Debugged, {eval,{M,F,As}}} when Le =:= 1 ->
%% Reset process dictionary
%% This is really only necessary if the process left
%% interpreted code at a call level > 1
@@ -346,7 +346,7 @@ push(MFA, Bs, #ieval{level=Le,module=Cm,line=Li,last_call=Lc}) ->
[] -> put(stack, [Entry]);
[_Entry|Entries] -> put(stack, [Entry|Entries])
end;
- _ -> % all | no_tail when Lc==false
+ _ -> % all | no_tail when Lc =:= false
put(stack, [Entry|get(stack)])
end.
@@ -413,10 +413,10 @@ sublist(L, Start, Length) ->
lists:sublist(L, Start, Length).
fix_stacktrace2([{_,{{M,F,As1},_,_}}, {_,{{M,F,As2},_,_}}|_])
- when length(As1)==length(As2) ->
+ when length(As1) =:= length(As2) ->
[{M,F,As1}];
fix_stacktrace2([{_,{{Fun,As1},_,_}}, {_,{{Fun,As2},_,_}}|_])
- when length(As1)==length(As2) ->
+ when length(As1) =:= length(As2) ->
[{Fun,As1}];
fix_stacktrace2([{_,{MFA,_,_}}|Entries]) ->
[MFA|fix_stacktrace2(Entries)];
@@ -465,9 +465,9 @@ stack_frame(SP, Dir, [{SP, _}|Stack]) ->
case Stack of
[{Le, {_MFA,Where,Bs}}|_] ->
{Le, Where, Bs};
- [] when Dir==up ->
+ [] when Dir =:= up ->
top;
- [] when Dir==down ->
+ [] when Dir =:= down ->
bottom
end;
stack_frame(SP, Dir, [_Entry|Stack]) ->
@@ -509,7 +509,7 @@ trace(What, Args, true) ->
end,
io_lib:format(" (~w) receive " ++ Tail, [Le]);
- received when Args==null ->
+ received when Args =:= null ->
io_lib:format("~n", []);
received -> % Args=Msg
io_lib:format("~n<== ~p~n", [Args]);
@@ -586,8 +586,8 @@ eval_mfa(Debugged, M, F, As, Ieval) ->
end.
eval_function(Mod, Fun, As0, Bs0, _Called, Ieval) when is_function(Fun);
- Mod==?MODULE,
- Fun==eval_fun ->
+ Mod =:= ?MODULE,
+ Fun =:= eval_fun ->
#ieval{level=Le, line=Li, last_call=Lc} = Ieval,
case lambda(Fun, As0) of
{Cs,Module,Name,As,Bs} ->
@@ -657,7 +657,7 @@ eval_function(Mod, Name, As0, Bs0, Called, Ieval) ->
lambda(eval_fun, [Cs,As,Bs,{Mod,Name}=F]) ->
%% Fun defined in interpreted code, called from outside
if
- length(element(3,hd(Cs))) == length(As) ->
+ length(element(3,hd(Cs))) =:= length(As) ->
db_ref(Mod), %% Adds ref between module and process
{Cs,Mod,Name,As,Bs};
true ->
@@ -672,7 +672,7 @@ lambda(Fun, As) when is_function(Fun) ->
{env, [{Mod,Name},Bs,Cs]} = erlang:fun_info(Fun, env),
{arity, Arity} = erlang:fun_info(Fun, arity),
if
- length(As) == Arity ->
+ length(As) =:= Arity ->
db_ref(Mod), %% Adds ref between module and process
{Cs,Mod,Name,As,Bs};
true ->
@@ -731,7 +731,7 @@ db_ref(Mod) ->
ModDb ->
Node = node(get(int)),
DbRef = if
- Node/=node() -> {Node,ModDb};
+ Node =/= node() -> {Node,ModDb};
true -> ModDb
end,
put([Mod|db], DbRef),
@@ -741,13 +741,12 @@ db_ref(Mod) ->
DbRef
end.
-
cache(Key, Data) ->
put(cache, lists:sublist([{Key,Data}|get(cache)], 5)).
cached(Key) ->
- case lists:keysearch(Key, 1, get(cache)) of
- {value,{Key,Data}} -> Data;
+ case lists:keyfind(Key, 1, get(cache)) of
+ {Key,Data} -> Data;
false -> false
end.
@@ -844,7 +843,7 @@ expr({'try',Line,Es,CaseCs,CatchCs,[]}, Bs0, Ieval0) ->
case_clauses(Val, CaseCs, Bs, try_clause, Ieval)
end
catch
- Class:Reason when CatchCs=/=[] ->
+ Class:Reason when CatchCs =/= [] ->
catch_clauses({Class,Reason,[]}, CatchCs, Bs0, Ieval)
end;
expr({'try',Line,Es,CaseCs,CatchCs,As}, Bs0, Ieval0) ->
@@ -1498,10 +1497,7 @@ guard(Gs, Bs) -> or_guard(Gs, Bs).
or_guard([G|Gs], Bs) ->
%% Short-circuit OR.
- case and_guard(G, Bs) of
- true -> true;
- false -> or_guard(Gs, Bs)
- end;
+ and_guard(G, Bs) orelse or_guard(Gs, Bs);
or_guard([], _) -> false.
and_guard([G|Gs], Bs) ->
@@ -1598,8 +1594,7 @@ match1({bin,_,Fs}, B, Bs0, BBs0) when is_bitstring(B) ->
try eval_bits:match_bits(Fs, B, Bs1, BBs,
fun(L, R, Bs) -> match1(L, R, Bs, BBs) end,
fun(E, Bs) -> expr(E, Bs, #ieval{}) end,
- false) of
- Match -> Match
+ false)
catch
_:_ -> throw(nomatch)
end;
@@ -1687,7 +1682,7 @@ merge_bindings([{Name,V}|B1s], B2s, Ieval) ->
case binding(Name, B2s) of
{value,V} -> % Already there, and the same
merge_bindings(B1s, B2s, Ieval);
- {value,_} when Name=='_' -> % Already there, but anonymous
+ {value,_} when Name =:= '_' -> % Already there, but anonymous
B2s1 = lists:keydelete('_', 1, B2s),
[{Name,V}|merge_bindings(B1s, B2s1, Ieval)];
{value,_} -> % Already there, but different => badmatch
diff --git a/lib/debugger/src/dbg_iload.erl b/lib/debugger/src/dbg_iload.erl
index 1216338006..2ae0c333da 100644
--- a/lib/debugger/src/dbg_iload.erl
+++ b/lib/debugger/src/dbg_iload.erl
@@ -1,64 +1,60 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dbg_iload).
-%% External exports
-export([load_mod/4]).
-%% Internal exports
--export([load_mod1/4]).
-
%%====================================================================
%% External exports
%%====================================================================
%%--------------------------------------------------------------------
%% load_mod(Mod, File, Binary, Db) -> {ok, Mod}
-%% Mod = atom()
+%% Mod = module()
%% File = string() Source file (including path)
%% Binary = binary()
%% Db = ETS identifier
%% Load a new module into the database.
%%
-%% We want the loading of a module to be syncronous so no other
+%% We want the loading of a module to be synchronous so that no other
%% process tries to interpret code in a module not being completely
%% loaded. This is achieved as this function is called from
%% dbg_iserver. We are suspended until the module has been loaded.
%%--------------------------------------------------------------------
+-spec load_mod(Mod, file:filename(), binary(), ets:tid()) ->
+ {'ok', Mod} when is_subtype(Mod, atom()).
+
load_mod(Mod, File, Binary, Db) ->
Flag = process_flag(trap_exit, true),
- Pid = spawn_link(?MODULE, load_mod1, [Mod, File, Binary, Db]),
+ Pid = spawn_link(fun () -> load_mod1(Mod, File, Binary, Db) end),
receive
{'EXIT', Pid, What} ->
process_flag(trap_exit, Flag),
What
end.
-%%====================================================================
-%% Internal exports
-%%====================================================================
+-spec load_mod1(atom(), file:filename(), binary(), ets:tid()) -> no_return().
load_mod1(Mod, File, Binary, Db) ->
store_module(Mod, File, Binary, Db),
exit({ok, Mod}).
-
%%====================================================================
%% Internal functions
%%====================================================================
@@ -84,7 +80,7 @@ store_module(Mod, File, Binary, Db) ->
Attr = store_forms(Forms, Mod, Db, Exp, []),
erase(mod_md5),
erase(current_function),
-% store_funs(Db, Mod),
+ %% store_funs(Db, Mod),
erase(vcount),
erase(funs),
erase(fun_count),
@@ -412,8 +408,7 @@ expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,fault}},[_]=As}) ->
{dbg,Line,fault,expr_list(As)};
expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,exit}},[_]=As}) ->
{dbg,Line,exit,expr_list(As)};
-expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},As0})
- when length(As0) == 3 ->
+expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,apply}},[_,_,_]=As0}) ->
As = expr_list(As0),
{apply,Line,As};
expr({call,Line,{remote,_,{atom,_,Mod},{atom,_,Func}},As0}) ->
@@ -521,15 +516,9 @@ expr(Other) ->
%% here as sys_pre_expand has transformed source.
is_guard_test({op,_,Op,L,R}) ->
- case erl_internal:comp_op(Op, 2) of
- true -> is_gexpr_list([L,R]);
- false -> false
- end;
+ erl_internal:comp_op(Op, 2) andalso is_gexpr_list([L,R]);
is_guard_test({call,_,{remote,_,{atom,_,erlang},{atom,_,Test}},As}) ->
- case erl_internal:type_test(Test, length(As)) of
- true -> is_gexpr_list(As);
- false -> false
- end;
+ erl_internal:type_test(Test, length(As)) andalso is_gexpr_list(As);
is_guard_test({atom,_,true}) -> true;
is_guard_test(_) -> false.
@@ -546,22 +535,12 @@ is_gexpr({call,_,{remote,_,{atom,_,erlang},{atom,_,F}},As}) ->
Ar = length(As),
case erl_internal:guard_bif(F, Ar) of
true -> is_gexpr_list(As);
- false ->
- case erl_internal:arith_op(F, Ar) of
- true -> is_gexpr_list(As);
- false -> false
- end
+ false -> erl_internal:arith_op(F, Ar) andalso is_gexpr_list(As)
end;
is_gexpr({op,_,Op,A}) ->
- case erl_internal:arith_op(Op, 1) of
- true -> is_gexpr(A);
- false -> false
- end;
+ erl_internal:arith_op(Op, 1) andalso is_gexpr(A);
is_gexpr({op,_,Op,A1,A2}) ->
- case erl_internal:arith_op(Op, 2) of
- true -> is_gexpr_list([A1,A2]);
- false -> false
- end;
+ erl_internal:arith_op(Op, 2) andalso is_gexpr_list([A1,A2]);
is_gexpr(_) -> false.
is_gexpr_list(Es) -> lists:all(fun (E) -> is_gexpr(E) end, Es).
diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl
index 4c1e9ccb7b..212bc2b8ab 100644
--- a/lib/debugger/src/dbg_iserver.erl
+++ b/lib/debugger/src/dbg_iserver.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -155,11 +155,8 @@ handle_call(get_stack_trace, _From, State) ->
%% Retrieving information
handle_call(snapshot, _From, State) ->
- Reply = lists:map(fun(Proc) ->
- {Proc#proc.pid, Proc#proc.function,
- Proc#proc.status, Proc#proc.info}
- end,
- State#state.procs),
+ Reply = [{Proc#proc.pid, Proc#proc.function,
+ Proc#proc.status, Proc#proc.info} || Proc <- State#state.procs],
{reply, Reply, State};
handle_call({get_meta, Pid}, _From, State) ->
Reply = case get_proc({pid, Pid}, State#state.procs) of
@@ -181,21 +178,21 @@ handle_call({get_attpid, Pid}, _From, State) ->
%% Breakpoint handling
handle_call({new_break, Point, Options}, _From, State) ->
- case lists:keysearch(Point, 1, State#state.breaks) of
+ case lists:keymember(Point, 1, State#state.breaks) of
false ->
Break = {Point, Options},
send_all([subscriber, meta, attached],
{new_break, Break}, State),
Breaks = keyinsert(Break, 1, State#state.breaks),
{reply, ok, State#state{breaks=Breaks}};
- {value, _Break} ->
+ true ->
{reply, {error, break_exists}, State}
end;
handle_call(all_breaks, _From, State) ->
{reply, State#state.breaks, State};
handle_call({all_breaks, Mod}, _From, State) ->
Reply = lists:filter(fun({{M,_L}, _Options}) ->
- if M==Mod -> true; true -> false end
+ M =/= Mod
end,
State#state.breaks),
{reply, Reply, State};
@@ -276,7 +273,7 @@ handle_call({contents, Mod, Pid}, _From, State) ->
Db = State#state.db,
[{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}),
ModDb = if
- Pid==any -> hd(ModDbs);
+ Pid =:= any -> hd(ModDbs);
true ->
lists:foldl(fun(T, not_found) ->
[{T, Pids}] = ets:lookup(Db, T),
@@ -295,7 +292,7 @@ handle_call({raw_contents, Mod, Pid}, _From, State) ->
Db = State#state.db,
[{{Mod, refs}, ModDbs}] = ets:lookup(Db, {Mod, refs}),
ModDb = if
- Pid==any -> hd(ModDbs);
+ Pid =:= any -> hd(ModDbs);
true ->
lists:foldl(fun(T, not_found) ->
[{T, Pids}] = ets:lookup(Db, T),
@@ -360,15 +357,15 @@ handle_cast({set_stack_trace, Flag}, State) ->
%% Retrieving information
handle_cast(clear, State) ->
Procs = lists:filter(fun(#proc{status=Status}) ->
- if Status==exit -> false; true -> true end
+ Status =/= exit
end,
State#state.procs),
{noreply, State#state{procs=Procs}};
%% Breakpoint handling
handle_cast({delete_break, Point}, State) ->
- case lists:keysearch(Point, 1, State#state.breaks) of
- {value, _Break} ->
+ case lists:keymember(Point, 1, State#state.breaks) of
+ true ->
send_all([subscriber, meta, attached],
{delete_break, Point}, State),
Breaks = lists:keydelete(Point, 1, State#state.breaks),
@@ -377,8 +374,8 @@ handle_cast({delete_break, Point}, State) ->
{noreply, State}
end;
handle_cast({break_option, Point, Option, Value}, State) ->
- case lists:keysearch(Point, 1, State#state.breaks) of
- {value, {Point, Options}} ->
+ case lists:keyfind(Point, 1, State#state.breaks) of
+ {Point, Options} ->
N = case Option of
status -> 1;
action -> 2;
@@ -399,7 +396,7 @@ handle_cast(no_break, State) ->
handle_cast({no_break, Mod}, State) ->
send_all([subscriber, meta, attached], {no_break, Mod}, State),
Breaks = lists:filter(fun({{M, _L}, _O}) ->
- if M==Mod -> false; true -> true end
+ M =/= Mod
end,
State#state.breaks),
{noreply, State#state{breaks=Breaks}};
@@ -409,7 +406,7 @@ handle_cast({set_status, Meta, Status, Info}, State) ->
{true, Proc} = get_proc({meta, Meta}, State#state.procs),
send_all(subscriber, {new_status, Proc#proc.pid, Status, Info}, State),
if
- Status==break ->
+ Status =:= break ->
auto_attach(break, State#state.auto, Proc);
true -> ignore
end,
@@ -526,11 +523,10 @@ code_change(_OldVsn, State, _Extra) ->
%% Internal functions
%%====================================================================
-auto_attach(Why, Auto, Proc) when is_record(Proc, proc) ->
- case Proc#proc.attpid of
- AttPid when is_pid(AttPid) -> ignore;
- undefined ->
- auto_attach(Why, Auto, Proc#proc.pid)
+auto_attach(Why, Auto, #proc{attpid = Attpid, pid = Pid}) ->
+ case Attpid of
+ undefined -> auto_attach(Why, Auto, Pid);
+ _ when is_pid(Attpid) -> ignore
end;
auto_attach(Why, Auto, Pid) when is_pid(Pid) ->
case Auto of
@@ -545,7 +541,7 @@ auto_attach(Why, Auto, Pid) when is_pid(Pid) ->
keyinsert(Tuple1, N, [Tuple2|Tuples]) ->
if
- element(N, Tuple1)<element(N, Tuple2) ->
+ element(N, Tuple1) < element(N, Tuple2) ->
[Tuple1, Tuple2|Tuples];
true ->
[Tuple2 | keyinsert(Tuple1, N, Tuples)]
@@ -576,7 +572,7 @@ send_all([], _Msg, _State) -> ok;
send_all(subscriber, Msg, State) ->
send_all(State#state.subs, Msg);
send_all(meta, Msg, State) ->
- Metas = lists:map(fun(Proc) -> Proc#proc.meta end, State#state.procs),
+ Metas = [Proc#proc.meta || Proc <- State#state.procs],
send_all(Metas, Msg);
send_all(attached, Msg, State) ->
AttPids= mapfilter(fun(Proc) ->
@@ -600,7 +596,7 @@ get_proc({Type, Pid}, Procs) ->
meta -> #proc.meta;
attpid -> #proc.attpid
end,
- case lists:keysearch(Pid, Index, Procs) of
- {value, Proc} -> {true, Proc};
- false -> false
+ case lists:keyfind(Pid, Index, Procs) of
+ false -> false;
+ Proc -> {true, Proc}
end.
diff --git a/lib/debugger/src/dbg_ui_break_win.erl b/lib/debugger/src/dbg_ui_break_win.erl
index a56fe36828..4039bf785f 100644
--- a/lib/debugger/src/dbg_ui_break_win.erl
+++ b/lib/debugger/src/dbg_ui_break_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -268,12 +268,7 @@ handle_event({gs, _Id, click, _Data, ["Ok"|_]}, WinInfo) ->
IndexL ->
Funcs = WinInfo#winInfo.funcs,
Breaks =
- lists:map(fun(Index) ->
- Func = lists:nth(Index+1,
- Funcs),
- [Mod | Func]
- end,
- IndexL),
+ [[Mod|lists:nth(Index+1, Funcs)] || Index <- IndexL],
{break, Breaks, enable}
end
end;
diff --git a/lib/debugger/src/dbg_ui_filedialog_win.erl b/lib/debugger/src/dbg_ui_filedialog_win.erl
index f7d76076a5..3203991c1f 100644
--- a/lib/debugger/src/dbg_ui_filedialog_win.erl
+++ b/lib/debugger/src/dbg_ui_filedialog_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -202,8 +202,7 @@ handle_event({gs, 'Files', doubleclick, _Data, _Arg}, WinInfo) ->
handle_event({gs, _Id, click, select, _Arg}, _WinInfo) ->
{select, gs:read('Selection', text)};
handle_event({gs, _Id, click, multiselect, _Arg}, WinInfo) ->
- Files = lists:map(fun(File) -> untag(File) end,
- gs:read('Files', items)),
+ Files = [untag(File) || File <- gs:read('Files', items)],
{multiselect, WinInfo#winInfo.cwd, Files};
handle_event({gs, _Id, click, filter, _Arg}, WinInfo) ->
{Cwd, Pattern} = update_win(gs:read('Filter', text),
@@ -286,7 +285,7 @@ max_existing([Name | Names]) ->
max_existing(Dir, [Name | Names]) ->
Dir2 = filename:join(Dir, Name),
case filelib:is_file(Dir2, erl_prim_loader) of
- true when Names==[] -> {Dir2, []};
+ true when Names =:= [] -> {Dir2, []};
true -> max_existing(Dir2, Names);
false -> {Dir, [Name | Names]}
end.
@@ -309,11 +308,8 @@ extra_filter([], _Dir, _Fun) -> [].
get_subdirs(Dir) ->
case erl_prim_loader:list_dir(Dir) of
{ok, FileNames} ->
- X = lists:filter(fun(FileName) ->
- File = filename:join(Dir, FileName),
- filelib:is_dir(File, erl_prim_loader)
- end,
- FileNames),
+ X = [FN || FN <- FileNames,
+ filelib:is_dir(filename:join(Dir, FN), erl_prim_loader)],
lists:sort(X);
_Error ->
[]
@@ -335,4 +331,3 @@ compare([], [$/|File]) ->
File;
compare(_, _) ->
error.
-
diff --git a/lib/debugger/src/dbg_ui_mon.erl b/lib/debugger/src/dbg_ui_mon.erl
index 8888075124..82fe210968 100644
--- a/lib/debugger/src/dbg_ui_mon.erl
+++ b/lib/debugger/src/dbg_ui_mon.erl
@@ -162,7 +162,7 @@ init2(CallingPid, Mode, SFile, GS) ->
CallingPid ! {initialization_complete, self()},
if
- SFile==default ->
+ SFile =:= default ->
loop(State3);
true ->
loop(load_settings(SFile, State3))
@@ -226,7 +226,7 @@ loop(State) ->
gui_cmd(stopped, State);
%% From the GUI
- GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent)==gs ->
+ GuiEvent when is_tuple(GuiEvent), element(1, GuiEvent) =:= gs ->
Cmd = dbg_ui_mon_win:handle_event(GuiEvent,State#state.win),
State2 = gui_cmd(Cmd, State),
loop(State2);
@@ -269,7 +269,7 @@ gui_cmd(ignore, State) ->
State;
gui_cmd(stopped, State) ->
if
- State#state.starter==true -> int:stop();
+ State#state.starter =:= true -> int:stop();
true -> int:auto_attach(false)
end,
exit(stop);
@@ -413,9 +413,9 @@ gui_cmd({'Trace Window', TraceWin}, State) ->
State2;
gui_cmd({'Auto Attach', When}, State) ->
if
- When==[] -> int:auto_attach(false);
+ When =:= [] -> int:auto_attach(false);
true ->
- Flags = lists:map(fun(Name) -> map(Name) end, When),
+ Flags = [map(Name) || Name <- When],
int:auto_attach(Flags, trace_function(State))
end,
State;
@@ -676,7 +676,7 @@ load_settings2(Settings, State) ->
Break,
int:break(Mod, Line),
if
- Status==inactive ->
+ Status =:= inactive ->
int:disable_break(Mod, Line);
true -> ignore
end,
@@ -700,12 +700,8 @@ save_settings(SFile, State) ->
int:auto_attach(),
int:stack_trace(),
State#state.backtrace,
- lists:map(fun(Mod) ->
- int:file(Mod)
- end,
- int:interpreted()),
+ [int:file(Mod) || Mod <- int:interpreted()],
int:all_breaks()},
-
Binary = term_to_binary({debugger_settings, Settings}),
case file:write_file(SFile, Binary) of
ok ->
@@ -720,13 +716,12 @@ save_settings(SFile, State) ->
%%====================================================================
registered_name(Pid) ->
-
%% Yield in order to give Pid more time to register its name
timer:sleep(200),
Node = node(Pid),
if
- Node==node() ->
+ Node =:= node() ->
case erlang:process_info(Pid, registered_name) of
{registered_name, Name} -> Name;
_ -> undefined
diff --git a/lib/debugger/src/dbg_ui_mon_win.erl b/lib/debugger/src/dbg_ui_mon_win.erl
index ba2f94c550..52e8f433ba 100644
--- a/lib/debugger/src/dbg_ui_mon_win.erl
+++ b/lib/debugger/src/dbg_ui_mon_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -224,8 +224,7 @@ select(MenuItem, Bool) ->
%%--------------------------------------------------------------------
add_module(WinInfo, Menu, Mod) ->
Modules = WinInfo#winInfo.modules,
- case lists:keysearch(Mod, #moduleInfo.module, Modules) of
- {value, _ModInfo} -> WinInfo;
+ case lists:keymember(Mod, #moduleInfo.module, Modules) of
false ->
%% Create a menu for the module
Font = dbg_ui_win:font(normal),
@@ -244,7 +243,8 @@ add_module(WinInfo, Menu, Mod) ->
gs:config(WinInfo#winInfo.listbox, {add, Mod}),
ModInfo = #moduleInfo{module=Mod, menubtn=MenuBtn},
- WinInfo#winInfo{modules=[ModInfo | Modules]}
+ WinInfo#winInfo{modules=[ModInfo | Modules]};
+ true -> WinInfo
end.
%%--------------------------------------------------------------------
@@ -491,14 +491,13 @@ handle_event({gs, _Id, click, autoattach, _Arg}, WinInfo) ->
%% Process grid
handle_event({gs, _Id, keypress, _Data, [Key|_]}, WinInfo) when
- Key=='Up'; Key=='Down' ->
- Dir = if Key=='Up' -> up; Key=='Down' -> down end,
+ Key =:= 'Up'; Key =:= 'Down' ->
+ Dir = if Key =:= 'Up' -> up; Key =:= 'Down' -> down end,
Row = move(WinInfo, Dir),
-
if Row>1 ->
WinInfo2 = highlight(WinInfo, Row),
- {value, #procInfo{pid=Pid}} =
- lists:keysearch(Row, #procInfo.row, WinInfo#winInfo.processes),
+ #procInfo{pid=Pid} =
+ lists:keyfind(Row, #procInfo.row, WinInfo#winInfo.processes),
{focus, Pid, WinInfo2};
true ->
ignore
@@ -515,10 +514,9 @@ handle_event(_GSEvent, _WinInfo) ->
move(WinInfo, Dir) ->
Row = WinInfo#winInfo.focus,
Last = WinInfo#winInfo.row,
-
if
- Dir==up, Row>1 -> Row-1;
- Dir==down, Row<Last -> Row+1;
+ Dir =:= up, Row > 1 -> Row-1;
+ Dir =:= down, Row < Last -> Row+1;
true -> Row
end.
@@ -533,7 +531,6 @@ highlight(WinInfo, Row) ->
GridLine2 = gs:read(Grid, {obj_at_row, Row}),
gs:config(GridLine2, {fg, white}),
WinInfo#winInfo{focus=Row}.
-
%%====================================================================
%% Internal functions
@@ -545,7 +542,7 @@ configure(WinInfo, {W, H}) ->
Dx = NewW - gs:read(Grid, width),
Dy = H-42 - gs:read(Grid, height),
if
- (Dx+Dy)=/=0 ->
+ (Dx+Dy) =/= 0 ->
gs:config(Grid, [{width, NewW}, {height, H-30}]),
Cols = calc_columnwidths(NewW),
gs:config(Grid, Cols);
@@ -555,10 +552,9 @@ configure(WinInfo, {W, H}) ->
calc_columnwidths(Width) ->
W = if
- Width=<?Wg -> ?Wg;
+ Width =< ?Wg -> ?Wg;
true -> Width
end,
- First = lists:map(fun (X) -> round(X) end,
- [0.13*W, 0.27*W, 0.18*W, 0.18*W]),
+ First = [round(X) || X <- [0.13*W, 0.27*W, 0.18*W, 0.18*W]],
Last = W - lists:sum(First) - 30,
{columnwidths, First++[Last]}.
diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl
index dbf93c7f45..82d4199630 100644
--- a/lib/debugger/src/dbg_ui_trace_win.erl
+++ b/lib/debugger/src/dbg_ui_trace_win.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dbg_ui_trace_win).
@@ -106,7 +106,7 @@ create_win(GS, Title, TraceWin, Menus) ->
gs:read('CodeArea', height) +
gs:read('RB1', height) +
gs:read('ButtonArea', height) +
- max(gs:read('EvalArea', height),
+ erlang:max(gs:read('EvalArea', height),
gs:read('BindArea', height)) +
gs:read('RB2', height) +
gs:read('TraceArea', height)}),
@@ -147,17 +147,17 @@ configure(WinInfo, TraceWin) ->
H = gs:read(Win, height),
H2 = if
- Bu1==close, Bu2==open ->
+ Bu1 =:= close, Bu2 =:= open ->
resize_button_area(open, width, W-4),
gs:config('ButtonArea', {height, 30}),
H+30;
- Bu1==open, Bu2==close ->
+ Bu1 =:= open, Bu2 =:= close ->
gs:config('ButtonArea', [{width, 0}, {height, 0}]),
H-30;
true -> H
end,
H3 = if
- Ev1==close, Ev2==open, Bi1==open ->
+ Ev1 =:= close, Ev2 =:= open, Bi1 =:= open ->
Wnew1 = round((W-10-4)/2), % W = window/2 - rb - pads
Hbi1 = gs:read('BindArea', height), % H = bind area h
resize_eval_area(open, width, Wnew1),
@@ -167,25 +167,25 @@ configure(WinInfo, TraceWin) ->
resize_bind_area(open, width,
Wnew1-gs:read('BindArea', width)),
H2;
- Ev1==close, Ev2==open, Bi1==close ->
+ Ev1 =:= close, Ev2 =:= open, Bi1 =:= close ->
resize_eval_area(open, width, W-4),
resize_eval_area(open, height, 200),
H2+200;
- Ev1==open, Ev2==close, Bi1==open ->
+ Ev1 =:= open, Ev2 =:= close, Bi1 =:= open ->
gs:config('EvalArea', [{width,0}, {height,0}]),
gs:config('RB3', [{width, 0}, {height, 0}]),
Wnew2 = W-4,
resize_bind_area(open, width,
Wnew2-gs:read('BindArea', width)),
H2;
- Ev1==open, Ev2==close, Bi1==close ->
+ Ev1 =:= open, Ev2 =:= close, Bi1 =:= close ->
Hs1 = gs:read('EvalArea', height),
gs:config('EvalArea', [{width, 0}, {height, 0}]),
H2-Hs1;
true -> H2
end,
H4 = if
- Bi1==close, Bi2==open, Ev2==open ->
+ Bi1 =:= close, Bi2 =:= open, Ev2 =:= open ->
Wnew3 = round((W-10-4)/2), % W = window/2 - rb - pads
Hs2 = gs:read('EvalArea', height), % H = eval area h
resize_bind_area(open, width, Wnew3),
@@ -194,29 +194,29 @@ configure(WinInfo, TraceWin) ->
resize_eval_area(open, width,
Wnew3-gs:read('EvalArea', width)),
H3;
- Bi1==close, Bi2==open, Ev2==close ->
+ Bi1 =:= close, Bi2 =:= open, Ev2 =:= close ->
resize_bind_area(open, width, W-4),
resize_bind_area(open, height, 200),
H3+200;
- Bi1==open, Bi2==close, Ev2==open ->
+ Bi1 =:= open, Bi2 =:= close, Ev2 =:= open ->
gs:config('BindArea', [{width, 0}, {height, 0}]),
gs:config('RB3', [{width, 0}, {height, 0}]),
Wnew4 = W-4,
resize_eval_area(open, width,
Wnew4-gs:read('EvalArea', width)),
H3;
- Bi1==open, Bi2==close, Ev2==close ->
+ Bi1 =:= open, Bi2 =:= close, Ev2 =:= close ->
Hbi2 = gs:read('BindArea', height),
gs:config('BindArea', [{width, 0}, {height, 0}]),
H3-Hbi2;
true -> H3
end,
H5 = if
- Tr1==close, Tr2==open ->
+ Tr1 =:= close, Tr2 =:= open ->
resize_trace_area(open, width, W-4),
resize_trace_area(open, height, 200),
H4+200;
- Tr1==open, Tr2==close ->
+ Tr1 =:= open, Tr2 =:= close ->
Hf = gs:read('TraceArea', height),
gs:config('TraceArea', [{width, 0}, {height, 0}]),
H4-Hf;
@@ -226,10 +226,10 @@ configure(WinInfo, TraceWin) ->
RB1old = rb1(OldFlags), RB1new = rb1(NewFlags),
if
- RB1old==close, RB1new==open ->
+ RB1old =:= close, RB1new =:= open ->
gs:config('RB1', [{width, W-4}, {height, 10}]),
gs:config(Win, {height, gs:read(Win, height)+10});
- RB1old==open, RB1new==close ->
+ RB1old =:= open, RB1new =:= close ->
gs:config('RB1', [{width, 0}, {height, 0}, lower]),
gs:config(Win, {height, gs:read(Win, height)-10});
true -> ignore
@@ -237,10 +237,10 @@ configure(WinInfo, TraceWin) ->
RB2old = rb2(OldFlags), RB2new = rb2(NewFlags),
if
- RB2old==close, RB2new==open ->
+ RB2old =:= close, RB2new =:= open ->
gs:config('RB2', [{width, W-4}, {height, 10}]),
gs:config(Win, {height,gs:read(Win, height)+10});
- RB2old==open, RB2new==close ->
+ RB2old =:= open, RB2new =:= close ->
gs:config('RB2', [{width, 0}, {height, 0}, lower]),
gs:config(Win, {height, gs:read(Win, height)-10});
true -> ignore
@@ -301,15 +301,15 @@ select(MenuItem, Bool) ->
%% Cond = null | {Mod, Func}
%%--------------------------------------------------------------------
add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) ->
- case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of
- {value, {Mod, Editor}} ->
+ case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of
+ {Mod, Editor} ->
add_break_to_code(Editor, Line, Status);
false -> ignore
end,
add_break_to_menu(WinInfo, Menu, Break).
add_break_to_code(Editor, Line, Status) ->
- Color = if Status==active -> red; Status==inactive -> blue end,
+ Color = if Status =:= active -> red; Status =:= inactive -> blue end,
config_editor(Editor, [{overwrite,{{Line,0},"-@- "}},
{fg,{{{Line,0},{Line,lineend}}, Color}}]).
@@ -330,8 +330,8 @@ add_break_to_menu(WinInfo, Menu, {Point, [Status|_Options]=Options}) ->
%% Cond = null | {Mod, Func}
%%--------------------------------------------------------------------
update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) ->
- case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of
- {value, {Mod, Editor}} ->
+ case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of
+ {Mod, Editor} ->
add_break_to_code(Editor, Line, Status);
false -> ignore
end,
@@ -352,8 +352,8 @@ update_break_in_menu(WinInfo, {Point, [Status|_Options]=Options}) ->
%% Point = {Mod, Line}
%%--------------------------------------------------------------------
delete_break(WinInfo, {Mod,Line}=Point) ->
- case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of
- {value, {Mod, Editor}} -> delete_break_from_code(Editor, Line);
+ case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of
+ {Mod, Editor} -> delete_break_from_code(Editor, Line);
false -> ignore
end,
delete_break_from_menu(WinInfo, Point).
@@ -379,11 +379,11 @@ clear_breaks(WinInfo) ->
clear_breaks(WinInfo, all).
clear_breaks(WinInfo, Mod) ->
Remove = if
- Mod==all -> WinInfo#winInfo.breaks;
+ Mod =:= all -> WinInfo#winInfo.breaks;
true ->
lists:filter(fun(#breakInfo{point={Mod2,_L}}) ->
if
- Mod2==Mod -> true;
+ Mod2 =:= Mod -> true;
true -> false
end
end,
@@ -450,8 +450,8 @@ display(Arg) ->
%% Note: remove_code/2 should not be used for currently shown module.
%%--------------------------------------------------------------------
is_shown(WinInfo, Mod) ->
- case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of
- {value, {Mod, Editor}} ->
+ case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of
+ {Mod, Editor} ->
gs:config(Editor, raise),
{true, WinInfo#winInfo{editor={Mod, Editor}}};
false -> false
@@ -459,24 +459,22 @@ is_shown(WinInfo, Mod) ->
show_code(WinInfo, Mod, Contents) ->
Editors = WinInfo#winInfo.editors,
- {Flag, Editor} = case lists:keysearch(Mod, 1, Editors) of
- {value, {Mod, Ed}} -> {existing, Ed};
+ {Flag, Editor} = case lists:keyfind(Mod, 1, Editors) of
+ {Mod, Ed} -> {existing, Ed};
false -> {new, code_editor()}
end,
-
%% Insert code and update breakpoints, if any
config_editor(Editor, [raise, clear]),
show_code(Editor, Contents),
lists:foreach(fun(BreakInfo) ->
case BreakInfo#breakInfo.point of
- {Mod2, Line} when Mod2==Mod ->
+ {Mod2, Line} when Mod2 =:= Mod ->
Status = BreakInfo#breakInfo.status,
add_break_to_code(Editor, Line,Status);
_Point -> ignore
end
end,
WinInfo#winInfo.breaks),
-
case Flag of
existing ->
WinInfo#winInfo{editor={Mod, Editor}};
@@ -485,7 +483,7 @@ show_code(WinInfo, Mod, Contents) ->
editors=[{Mod, Editor} | Editors]}
end.
-show_code(Editor, Text) when length(Text)>1500 ->
+show_code(Editor, Text) when length(Text) > 1500 ->
%% Add some text at a time so that other processes may get scheduled
Str = string:sub_string(Text, 1, 1500),
config_editor(Editor, {insert,{'end', Str}}),
@@ -494,21 +492,19 @@ show_code(Editor, Text) ->
config_editor(Editor, {insert,{'end',Text}}).
show_no_code(WinInfo) ->
- {value, {'$top', Editor}} =
- lists:keysearch('$top', 1, WinInfo#winInfo.editors),
+ {'$top', Editor} = lists:keyfind('$top', 1, WinInfo#winInfo.editors),
gs:config(Editor, raise),
WinInfo#winInfo{editor={'$top', Editor}}.
remove_code(WinInfo, Mod) ->
Editors = WinInfo#winInfo.editors,
- case lists:keysearch(Mod, 1, Editors) of
- {value, {Mod, Editor}} ->
+ case lists:keyfind(Mod, 1, Editors) of
+ {Mod, Editor} ->
gs:destroy(Editor),
WinInfo#winInfo{editors=lists:keydelete(Mod, 1, Editors)};
false ->
WinInfo
end.
-
%%--------------------------------------------------------------------
%% mark_line(WinInfo, Line, How) -> WinInfo
@@ -522,7 +518,7 @@ mark_line(WinInfo, Line, How) ->
mark_line2(Editor, WinInfo#winInfo.marked_line, false),
mark_line2(Editor, Line, How),
if
- Line/=0 -> config_editor(Editor, {vscrollpos, Line-5});
+ Line =/= 0 -> config_editor(Editor, {vscrollpos, Line-5});
true -> ignore
end,
WinInfo#winInfo{marked_line=Line}.
@@ -537,7 +533,7 @@ mark_line2(Editor, Line, How) ->
false -> " "
end,
Font = if
- How==false -> dbg_ui_win:font(normal);
+ How =:= false -> dbg_ui_win:font(normal);
true -> dbg_ui_win:font(bold)
end,
config_editor(Editor, [{overwrite, {{Line,5}, Prefix}},
@@ -558,10 +554,10 @@ select_line(WinInfo, Line) ->
%% help window, it must be checked that it is correct
Size = gs:read(Editor, size),
if
- Line==0 ->
+ Line =:= 0 ->
select_line(Editor, WinInfo#winInfo.selected_line, false),
WinInfo#winInfo{selected_line=0};
- Line<Size ->
+ Line < Size ->
select_line(Editor, Line, true),
config_editor(Editor, {vscrollpos, Line-5}),
WinInfo#winInfo{selected_line=Line};
@@ -712,10 +708,10 @@ handle_event({gs, Editor, buttonpress, code_editor, _Arg}, WinInfo) ->
{Row, _} ->
{Mod, _Editor} = WinInfo#winInfo.editor,
Point = {Mod, Row},
- case lists:keysearch(Point, #breakInfo.point,
+ case lists:keymember(Point, #breakInfo.point,
WinInfo#winInfo.breaks) of
- {value, _BreakInfo} -> {break, Point, delete};
- false -> {break, Point, add}
+ false -> {break, Point, add};
+ true -> {break, Point, delete}
end;
{Row2, _} ->
select_line(Editor, Row2, true),
@@ -776,7 +772,7 @@ code_editor() ->
code_editor(Name, W, H) ->
Editor = if
- Name==null -> gs:editor('CodeArea', []);
+ Name =:= null -> gs:editor('CodeArea', []);
true -> gs:editor(Name, 'CodeArea', [])
end,
gs:config(Editor, [{x,5}, {y,30}, {width,W}, {height,H},
@@ -814,8 +810,8 @@ buttons() ->
{'Where','WhereButton'}, {'Up','UpButton'}, {'Down','DownButton'}].
is_button(Name) ->
- case lists:keysearch(Name, 1, buttons()) of
- {value, {Name, Button}} -> {true, Button};
+ case lists:keyfind(Name, 1, buttons()) of
+ {Name, Button} -> {true, Button};
false -> false
end.
@@ -847,7 +843,7 @@ resize_button_area(open, width, Diff) ->
eval_area({Ev,Bi}, X, Y, FrameOpts, Win) ->
{W,H} = if
- Ev==open -> {289,200};
+ Ev =:= open -> {289,200};
true -> {0,0}
end,
Font = dbg_ui_win:font(normal),
@@ -870,7 +866,7 @@ eval_area({Ev,Bi}, X, Y, FrameOpts, Win) ->
{font_style,{{{1,0},'end'},Font}}]),
gs:config('EvalEditor', {enable, false}),
if
- Ev==open, Bi==close -> resize_eval_area(Ev, width, 257);
+ Ev =:= open, Bi =:= close -> resize_eval_area(Ev, width, 257);
true -> ignore
end.
@@ -891,7 +887,7 @@ resize_eval_area(open, Key, Diff) ->
bind_area({Ev,Bi}, X, Y, FrameOpts, Win) ->
{W,H} = if
- Bi==open -> {249,200};
+ Bi =:= open -> {249,200};
true -> {0,0}
end,
gs:frame('BindArea', Win,
@@ -908,7 +904,7 @@ bind_area({Ev,Bi}, X, Y, FrameOpts, Win) ->
{text,{1,"Name"}}, {text,{2,"Value"}}, {font,Font}]),
gs:config('BindGrid', {rows,{1,1}}),
if
- Bi==open, Ev==close -> resize_bind_area(Bi, width, 297);
+ Bi =:= open, Ev =:= close -> resize_bind_area(Bi, width, 297);
true -> ignore
end.
@@ -993,15 +989,15 @@ resizebar(Flag, Name, X, Y, W, H, Obj) ->
rb1({_Bu,Ev,Bi,Tr}) ->
if
- Ev==close, Bi==close, Tr==close -> close;
+ Ev =:= close, Bi =:= close, Tr =:= close -> close;
true -> open
end.
rb2({_Bu,Ev,Bi,Tr}) ->
if
- Tr==open ->
+ Tr =:= open ->
if
- Ev==close, Bi==close -> close;
+ Ev =:= close, Bi =:= close -> close;
true -> open
end;
true -> close
@@ -1009,7 +1005,7 @@ rb2({_Bu,Ev,Bi,Tr}) ->
rb3({_Bu,Ev,Bi,_Tr}) ->
if
- Ev==open, Bi==open -> open;
+ Ev =:= open, Bi =:= open -> open;
true -> close
end.
@@ -1032,7 +1028,7 @@ config_v() ->
gs:config('RB3', {y,Y3}),
gs:config('BindArea', {y,Y3}),
- Y4 = Y3 + max(gs:read('EvalArea', height),
+ Y4 = Y3 + erlang:max(gs:read('EvalArea', height),
gs:read('BindArea', height)),
gs:config('RB2', {y,Y4}),
@@ -1061,13 +1057,13 @@ configure(WinInfo, NewW, NewH) ->
OldH = 25+gs:read('CodeArea', height)+
gs:read('RB1', height)+
gs:read('ButtonArea', height)+
- max(gs:read('EvalArea', height), gs:read('BindArea', height))+
+ erlang:max(gs:read('EvalArea', height), gs:read('BindArea', height))+
gs:read('RB2', height)+
gs:read('TraceArea', height),
%% Adjust width unless it is unchanged or less than minimum width
if
- OldW/=NewW ->
+ OldW =/= NewW ->
{Dcode,Deval,Dbind} = configure_widths(OldW,NewW,Flags),
resize_code_area(WinInfo, width, Dcode),
case rb1(Flags) of
@@ -1090,7 +1086,7 @@ configure(WinInfo, NewW, NewH) ->
%% Adjust height unless it is unchanged or less than minimum height
if
- OldH/=NewH ->
+ OldH =/= NewH ->
{Dcode2,Deval2,Dtrace} = configure_heights(OldH,NewH,Flags),
resize_code_area(WinInfo, height, Dcode2),
resize_eval_area(Ev, height, Deval2),
@@ -1112,16 +1108,16 @@ configure_widths(OldW, NewW, Flags) ->
{_Bu,Ev,Bi,_Tr} = Flags,
%% Difference between old and new width, considering min window width
- Diff = abs(max(OldW,330)-max(NewW,330)),
+ Diff = abs(erlang:max(OldW,330)-erlang:max(NewW,330)),
%% Check how much the frames can be resized in reality
Limits = if
%% Window larger
- NewW>OldW ->
+ NewW > OldW ->
if
- Ev==open,Bi==open -> {0,Diff,Diff};
- Ev==open -> {0,Diff,0};
- Bi==open -> {0,0,Diff};
+ Ev =:= open, Bi =:= open -> {0,Diff,Diff};
+ Ev =:= open -> {0,Diff,0};
+ Bi =:= open -> {0,0,Diff};
true -> {Diff,0,0}
end;
@@ -1129,12 +1125,12 @@ configure_widths(OldW, NewW, Flags) ->
%% and current size
OldW>NewW ->
if
- Ev==open,Bi==open ->
+ Ev =:= open, Bi =:= open ->
{0,
gs:read('EvalArea',width)-204,
gs:read('BindArea',width)-112};
- Ev==open -> {0,Diff,0};
- Bi==open -> {0,0,Diff};
+ Ev =:= open -> {0,Diff,0};
+ Bi =:= open -> {0,0,Diff};
true -> {Diff,0,0}
end
end,
@@ -1142,13 +1138,13 @@ configure_widths(OldW, NewW, Flags) ->
case Limits of
%% No Shell or Bind frame, larger window
- {T,0,0} when NewW>OldW -> {T,0,0};
+ {T,0,0} when NewW > OldW -> {T,0,0};
%% No Shell or Bind frame, smaller window
- {T,0,0} when OldW>NewW -> {-T,0,0};
+ {T,0,0} when OldW > NewW -> {-T,0,0};
%% Window larger; divide Diff among the frames and return result
- {_,Sf,B} when NewW>OldW ->
+ {_,Sf,B} when NewW > OldW ->
{_,Sf2,B2} = divide([{0,0},{0,Sf},{0,B}],Diff),
{Sf2+B2,Sf2,B2};
@@ -1166,38 +1162,38 @@ configure_heights(OldH, NewH, Flags) ->
%% Difference between old and new height, considering min win height
MinH = min_height(Flags),
- Diff = abs(max(OldH,MinH)-max(NewH,MinH)),
+ Diff = abs(erlang:max(OldH,MinH)-erlang:max(NewH,MinH)),
%% Check how much the frames can be resized in reality
{T,Sf,Ff} = if
%% Window larger
- NewH>OldH ->
+ NewH > OldH ->
{Diff,
if
- Ev==close, Bi==close -> 0;
+ Ev =:= close, Bi =:= close -> 0;
true -> Diff
end,
if
- Tr==open -> Diff;
+ Tr =:= open -> Diff;
true -> 0
end};
%% Window smaller; get difference between min size
%% and current size
- OldH>NewH ->
+ OldH > NewH ->
{gs:read('CodeArea',height)-100,
if
- Ev==close, Bi==close -> 0;
+ Ev =:= close, Bi =:= close -> 0;
true ->
if
- Ev==open ->
+ Ev =:= open ->
gs:read('EvalArea',height)-100;
- Bi==open ->
+ Bi =:= open ->
gs:read('BindArea',height)-100
end
end,
if
- Tr==open -> gs:read('TraceArea',height)-100;
+ Tr =:= open -> gs:read('TraceArea',height)-100;
true -> 0
end}
end,
@@ -1251,10 +1247,8 @@ divide(L, Diff) ->
if
%% All of Diff has been distributed
- D==0 -> {T,S,F};
-
+ D =:= 0 -> {T,S,F};
true ->
-
%% For each element, try to add as much as possible of D
{NewT,Dt} = divide2(D,T,Tmax),
{NewS,Ds} = divide2(D,S,Smax),
@@ -1296,25 +1290,25 @@ resize(WinInfo, ResizeBar) ->
rblimits('RB2',W,H),
rblimits('RB3',W,H)).
-resizeloop(WI, RB, Prev, {Min1,Max1},{Min2,Max2},{Min3,Max3}) ->
+resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3}) ->
receive
- {gs,_,motion,_,[_,Y]} when RB=='RB1', Y>Min1,Y<Max1 ->
+ {gs,_,motion,_,[_,Y]} when RB =:= 'RB1', Y > Min1, Y < Max1 ->
gs:config('RB1', {y,Y}),
- resizeloop(WI, RB, Y, {Min1,Max1},{Min2,Max2},{Min3,Max3});
- {gs,_,motion,_,_} when RB=='RB1' ->
- resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3});
+ resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3});
+ {gs,_,motion,_,_} when RB =:= 'RB1' ->
+ resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3});
- {gs,_,motion,_,[_,Y]} when RB=='RB2', Y>Min2,Y<Max2 ->
+ {gs,_,motion,_,[_,Y]} when RB =:= 'RB2', Y > Min2, Y < Max2 ->
gs:config('RB2', {y,Y}),
- resizeloop(WI, RB, Y, {Min1,Max1},{Min2,Max2},{Min3,Max3});
- {gs,_,motion,_,_} when RB=='RB2' ->
- resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3});
+ resizeloop(WI, RB, Y, {Min1,Max1}, {Min2,Max2}, {Min3,Max3});
+ {gs,_,motion,_,_} when RB =:= 'RB2' ->
+ resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3});
- {gs,_,motion,_,[X,_]} when RB=='RB3', X>Min3,X<Max3 ->
+ {gs,_,motion,_,[X,_]} when RB =:= 'RB3', X > Min3, X < Max3 ->
gs:config('RB3', {x,X}),
- resizeloop(WI, RB, X, {Min1,Max1},{Min2,Max2},{Min3,Max3});
- {gs,_,motion,_,_} when RB=='RB3' ->
- resizeloop(WI, RB, Prev,{Min1,Max1},{Min2,Max2},{Min3,Max3});
+ resizeloop(WI, RB, X, {Min1,Max1}, {Min2,Max2}, {Min3,Max3});
+ {gs,_,motion,_,_} when RB =:= 'RB3' ->
+ resizeloop(WI, RB, Prev, {Min1,Max1}, {Min2,Max2}, {Min3,Max3});
{gs,_,buttonrelease,_,_} ->
resize_win(WI, RB, Prev)
@@ -1329,7 +1323,7 @@ resize_win(WinInfo, 'RB1', Y) ->
%% Resize Code, Evaluator and Binding areas
resize_code_area(WinInfo, height, -Diff),
if
- S==close, Bi==close, F==open ->
+ S =:= close, Bi =:= close, F =:= open ->
resize_trace_area(open, height, Diff);
true ->
resize_eval_area(S, height, Diff),
@@ -1388,37 +1382,27 @@ rblimits('RB1',_W,H) ->
RB2 = gs:read('RB2',height),
FF = gs:read('TraceArea',height),
Max = case RB2 of
- 0 when FF/=0 ->
+ 0 when FF =/= 0 ->
H-112;
_ ->
Y = gs:read('RB2',y),
- max(Min,Y-140)
+ erlang:max(Min,Y-140)
end,
{Min,Max};
rblimits('RB2',_W,H) ->
-
- %% TraceFrame should not have height <100
+ %% TraceFrame should not have height < 100
Max = H-112,
-
%% Min is decided by a minimum distance to 'RB1'
Y = gs:read('RB1',y),
- Min = min(Max,Y+140),
-
+ Min = erlang:min(Max,Y+140),
{Min,Max};
rblimits('RB3',W,_H) ->
-
%% Neither CodeArea nor BindArea should occupy
%% less than 1/3 of the total window width and EvalFrame should
%% be at least 289 pixels wide
- {max(round(W/3),289),round(2*W/3)}.
-
-max(A, B) when A>B -> A;
-max(_A, B) -> B.
-
-min(A, B) when A<B -> A;
-min(_A, B) -> B.
+ {erlang:max(round(W/3),289),round(2*W/3)}.
%%====================================================================
@@ -1490,7 +1474,7 @@ helpwin_action(gotoline, default, AttPid, _Editor, Data, Win) ->
end,
Data;
helpwin_action(search, case_sensitive, _AttPid, _Ed, {Pos, CS}, _Win) ->
- Bool = if CS==true -> false; CS==false -> true end,
+ Bool = if CS =:= true -> false; CS =:= false -> true end,
{Pos, Bool};
helpwin_action(search, default, _AttPid, Editor, {Pos, CS}, Win) ->
gs:config(lbl(Win), {label, {text, ""}}),
@@ -1523,13 +1507,9 @@ search(Str, Editor, Max, {Row, Col}, CS) ->
lowercase(true, Str) -> Str;
lowercase(false, Str) ->
- lists:map(fun(Char) ->
- if
- Char>=$A, Char=<$Z -> Char+32;
- true -> Char
- end
- end,
- Str).
+ [if Char >= $A, Char =< $Z -> Char+32;
+ true -> Char
+ end || Char <- Str].
mark_string(Editor, {Row, Col}, Str) ->
Between = {{Row,Col}, {Row,Col+length(Str)}},
@@ -1546,10 +1526,9 @@ unmark_string(Editor, {Row, Col}) ->
{fg, {Between, black}}]).
helpwin(Type, GS, {X, Y}) ->
- W = 200, Pad=10, Wbtn = 50,
+ W = 200, Pad = 10, Wbtn = 50,
- Title =
- case Type of search -> "Search"; gotoline -> "Go To Line" end,
+ Title = case Type of search -> "Search"; gotoline -> "Go To Line" end,
Win = gs:window(GS, [{title, Title}, {x, X}, {y, Y}, {width, W},
{destroy, true}]),
diff --git a/lib/debugger/src/dbg_ui_view.erl b/lib/debugger/src/dbg_ui_view.erl
index 075275f196..be998f22ff 100644
--- a/lib/debugger/src/dbg_ui_view.erl
+++ b/lib/debugger/src/dbg_ui_view.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,9 +21,6 @@
%% External exports
-export([start/2]).
-%% Internal exports
--export([init/3]).
-
-record(state, {gs, % term() Graphics system id
win, % term() Attach process window data
coords, % {X,Y} Mouse point position
@@ -42,16 +39,18 @@ start(GS, Mod) ->
Title = "View Module " ++ atom_to_list(Mod),
case dbg_ui_winman:is_started(Title) of
true -> ignore;
- false -> spawn(?MODULE, init, [GS, Mod, Title])
+ false -> spawn(fun () -> init(GS, Mod, Title) end)
end.
+-spec stop() -> no_return().
+stop() ->
+ exit(stop).
%%====================================================================
%% Main loop and message handling
%%====================================================================
init(GS, Mod, Title) ->
-
%% Subscribe to messages from the interpreter
int:subscribe(),
@@ -94,7 +93,7 @@ loop(State) ->
dbg_ui_winman:update_windows_menu(Data),
loop(State);
{dbg_ui_winman, destroy} ->
- exit(stop);
+ stop();
%% Help window termination -- ignore
{'EXIT', _Pid, _Reason} ->
@@ -108,7 +107,7 @@ gui_cmd(ignore, State) ->
gui_cmd({win, Win}, State) ->
State#state{win=Win};
gui_cmd(stopped, _State) ->
- exit(stop);
+ stop();
gui_cmd({coords, Coords}, State) ->
State#state{coords=Coords};
@@ -119,8 +118,8 @@ gui_cmd({shortcut, Key}, State) ->
end;
%% File menu
-gui_cmd('Close', State) ->
- gui_cmd(stopped, State);
+gui_cmd('Close', _State) ->
+ stop();
%% Edit menu
gui_cmd('Go To Line...', State) ->
diff --git a/lib/debugger/src/dbg_ui_win.erl b/lib/debugger/src/dbg_ui_win.erl
index 9840aa54da..9bed6a1ec5 100644
--- a/lib/debugger/src/dbg_ui_win.erl
+++ b/lib/debugger/src/dbg_ui_win.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dbg_ui_win).
@@ -24,7 +24,6 @@
create_menus/2, select/2, selected/1,
add_break/2, update_break/2, delete_break/1,
motion/2
-
]).
-record(break, {mb, smi, emi, dimi, demi}).
@@ -49,11 +48,11 @@ init() ->
font(Style) ->
GS = init(),
Style2 = if
- Style==normal -> [];
+ Style =:= normal -> [];
true -> [Style]
end,
case gs:read(GS, {choose_font, {screen,Style2,12}}) of
- Font when element(1, Font)==screen ->
+ Font when element(1, Font) =:= screen ->
Font;
_ ->
gs:read(GS, {choose_font, {courier,Style2,12}})
@@ -76,13 +75,10 @@ min_size(Font, Strings, MinW, MinH) ->
min_size(GS, Font, [String|Strings], MinW, MinH) ->
{W, H} = gs:read(GS, {font_wh, {Font, String}}),
- min_size(GS, Font, Strings, max(MinW, W), max(MinH, H));
+ min_size(GS, Font, Strings, erlang:max(MinW, W), erlang:max(MinH, H));
min_size(_GS, _Font, [], W, H) ->
{W, H}.
-max(X, Y) when X>Y -> X;
-max(_X, Y) -> Y.
-
%%--------------------------------------------------------------------
%% create_menus(MenuBar, [Menu])
%% MenuBar = gsobj()
@@ -171,8 +167,7 @@ select(MenuItem, Bool) ->
%%--------------------------------------------------------------------
selected(Menu) ->
Children = gs:read(Menu, children),
- Selected = lists:filter(fun(Child) -> gs:read(Child, select) end,
- Children),
+ Selected = [gs:read(Child, select) || Child <- Children],
lists:map(fun(Child) ->
{text, Name} = gs:read(Child, label),
list_to_atom(Name)
diff --git a/lib/debugger/src/dbg_ui_winman.erl b/lib/debugger/src/dbg_ui_winman.erl
index 71023cd0d6..c7aac0df23 100644
--- a/lib/debugger/src/dbg_ui_winman.erl
+++ b/lib/debugger/src/dbg_ui_winman.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -120,9 +120,9 @@ init(_Arg) ->
{ok, #state{}}.
handle_call({is_started, Title}, _From, State) ->
- Reply = case lists:keysearch(Title, #win.title, State#state.wins) of
- {value, Win} -> {true, Win#win.win};
- false -> false
+ Reply = case lists:keyfind(Title, #win.title, State#state.wins) of
+ false -> false;
+ Win -> {true, Win#win.win}
end,
{reply, Reply, State}.
@@ -134,8 +134,8 @@ handle_cast({insert, Pid, Title, Win}, State) ->
handle_cast({clear_process, Title}, State) ->
OldWins = State#state.wins,
- Wins = case lists:keysearch(Title, #win.title, OldWins) of
- {value, #win{owner=Pid}} ->
+ Wins = case lists:keyfind(Title, #win.title, OldWins) of
+ #win{owner=Pid} ->
Msg = {dbg_ui_winman, destroy},
Pid ! Msg,
lists:keydelete(Title, #win.title, OldWins);
@@ -147,7 +147,7 @@ handle_cast({clear_process, Title}, State) ->
handle_info({'EXIT', Pid, _Reason}, State) ->
[Mon | _Wins] = State#state.wins,
if
- Pid==Mon#win.owner -> {stop, normal, State};
+ Pid =:= Mon#win.owner -> {stop, normal, State};
true ->
Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins),
inform_all(Wins2),
diff --git a/lib/debugger/src/dbg_wx_break_win.erl b/lib/debugger/src/dbg_wx_break_win.erl
index 5dafb0fbe6..7ac82c8fb4 100644
--- a/lib/debugger/src/dbg_wx_break_win.erl
+++ b/lib/debugger/src/dbg_wx_break_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -206,11 +206,7 @@ handle_event(#wx{id=OKorListBox, event=#wxCommand{type=OkorDoubleClick}},
OkorDoubleClick =:= command_listbox_doubleclicked ->
Mod = wxComboBox:getValue(Text),
{_, IndexL} = wxListBox:getSelections(LB),
- Breaks = lists:map(fun(Index) ->
- Func = lists:nth(Index+1, Funcs),
- [list_to_atom(Mod) | Func]
- end,
- IndexL),
+ Breaks = [[list_to_atom(Mod)|lists:nth(Index+1, Funcs)] || Index <- IndexL],
wxDialog:destroy(Win),
{break, Breaks, enable};
handle_event(#wx{id=?wxID_OK},#winInfo{win=Win,text=Text, entries=Es, trigger=Trigger}) ->
diff --git a/lib/debugger/src/dbg_wx_interpret.erl b/lib/debugger/src/dbg_wx_interpret.erl
index f711ba679d..67bcbb1203 100644
--- a/lib/debugger/src/dbg_wx_interpret.erl
+++ b/lib/debugger/src/dbg_wx_interpret.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -115,11 +115,11 @@ interpret_all(Dir, [File0|Files], Mode, Window, Errors) ->
interpret_all(_Dir, [], _Mode, _Window, []) ->
true;
interpret_all(Dir, [], _Mode, Window, Errors) ->
- Msg = lists:map(fun(Name) ->
- File = filename:join(Dir, Name),
- Error = format_error(int:interpretable(File)),
- ["\n ",Name,": ",Error]
- end, Errors),
+ Msg = [begin
+ File = filename:join(Dir, Name),
+ Error = format_error(int:interpretable(File)),
+ ["\n ",Name,": ",Error]
+ end || Name <- Errors],
All = ["Error when interpreting: ", Msg],
dbg_wx_win:confirm(Window, lists:flatten(All)),
true.
diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl
index 3f55c38d35..6bdec994b1 100644
--- a/lib/debugger/src/dbg_wx_mon.erl
+++ b/lib/debugger/src/dbg_wx_mon.erl
@@ -170,7 +170,7 @@ init2(CallingPid, Mode, SFile, GS) ->
CallingPid ! {initialization_complete, self()},
if
- SFile==default ->
+ SFile =:= default ->
loop(State3);
true ->
loop(load_settings(SFile, State3))
@@ -268,7 +268,7 @@ gui_cmd(ignore, State) ->
State;
gui_cmd(stopped, State) ->
if
- State#state.starter==true -> int:stop();
+ State#state.starter =:= true -> int:stop();
true -> int:auto_attach(false)
end,
exit(stop);
@@ -420,9 +420,9 @@ gui_cmd({'Trace Window', TraceWin}, State) ->
State2;
gui_cmd({'Auto Attach', When}, State) ->
if
- When==[] -> int:auto_attach(false);
+ When =:= [] -> int:auto_attach(false);
true ->
- Flags = lists:map(fun(Name) -> map(Name) end, When),
+ Flags = [map(Name) || Name <- When],
int:auto_attach(Flags, trace_function(State))
end,
State;
@@ -691,12 +691,12 @@ load_settings2(Settings, State) ->
Break,
int:break(Mod, Line),
if
- Status==inactive ->
+ Status =:= inactive ->
int:disable_break(Mod, Line);
true -> ignore
end,
if
- Action/=enable ->
+ Action =/= enable ->
int:action_at_break(Mod,Line,Action);
true -> ignore
end,
@@ -715,10 +715,7 @@ save_settings(SFile, State) ->
int:auto_attach(),
int:stack_trace(),
State#state.backtrace,
- lists:map(fun(Mod) ->
- int:file(Mod)
- end,
- int:interpreted()),
+ [int:file(Mod) || Mod <- int:interpreted()],
int:all_breaks()},
Binary = term_to_binary({debugger_settings, Settings}),
@@ -741,7 +738,7 @@ registered_name(Pid) ->
Node = node(Pid),
if
- Node==node() ->
+ Node =:= node() ->
case erlang:process_info(Pid, registered_name) of
{registered_name, Name} -> Name;
_ -> undefined
diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl
index 8ad4f4213f..04c3501b8c 100644
--- a/lib/debugger/src/dbg_wx_mon_win.erl
+++ b/lib/debugger/src/dbg_wx_mon_win.erl
@@ -266,8 +266,7 @@ select(MenuItem, Bool) ->
add_module(WinInfo, MenuName, Mod) ->
Win = WinInfo#winInfo.window,
Modules = WinInfo#winInfo.modules,
- case lists:keysearch(Mod, #moduleInfo.module, Modules) of
- {value, _ModInfo} -> WinInfo;
+ case lists:keymember(Mod, #moduleInfo.module, Modules) of
false ->
%% Create a menu for the module
Menu = get(MenuName),
@@ -284,8 +283,9 @@ add_module(WinInfo, MenuName, Mod) ->
wxListBox:append(WinInfo#winInfo.listbox, atom_to_list(Mod)),
ModInfo = #moduleInfo{module=Mod, menubtn={Menu,MenuBtn}},
- WinInfo#winInfo{modules=[ModInfo | Modules]}
- end.
+ WinInfo#winInfo{modules=[ModInfo | Modules]};
+ true -> WinInfo
+ end.
%%--------------------------------------------------------------------
%% delete_module(WinInfo, Mod) -> WinInfo
@@ -559,8 +559,7 @@ handle_event(#wx{event=#wxCommand{type=command_checkbox_clicked}},
handle_event(#wx{event=#wxList{type=command_list_item_selected,
itemIndex=Row}}, WinInfo) ->
#winInfo{processes=Pids} = WinInfo,
- {value, #procInfo{pid=Pid}} =
- lists:keysearch(Row, #procInfo.row, Pids),
+ #procInfo{pid=Pid} = lists:keyfind(Row, #procInfo.row, Pids),
{focus, Pid, WinInfo#winInfo{focus=Row}};
handle_event(#wx{event=#wxList{type=command_list_item_activated}},
_WinInfo) ->
diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl
index f9fdf593c4..2fdf39ba5a 100644
--- a/lib/debugger/src/dbg_wx_trace.erl
+++ b/lib/debugger/src/dbg_wx_trace.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -180,12 +180,12 @@ init_contents(Breaks, State) ->
State#state{win=Win}.
-loop(#state{meta=Meta} = State) ->
+loop(#state{meta=Meta, win=Win} = State) ->
receive
%% From the GUI main window
- GuiEvent when element(1, GuiEvent)==wx ->
+ GuiEvent when element(1, GuiEvent) =:= wx ->
Cmd = wx:batch(fun() ->
- dbg_wx_trace_win:handle_event(GuiEvent,State#state.win)
+ dbg_wx_trace_win:handle_event(GuiEvent,Win)
end),
State2 = gui_cmd(Cmd, State),
loop(State2);
@@ -211,11 +211,11 @@ loop(#state{meta=Meta} = State) ->
%% From the dbg_wx_winman process (Debugger window manager)
{dbg_ui_winman, update_windows_menu, Data} ->
- Window = dbg_wx_trace_win:get_window(State#state.win),
+ Window = dbg_wx_trace_win:get_window(Win),
dbg_wx_winman:update_windows_menu(Window,Data),
loop(State);
{dbg_ui_winman, destroy} ->
- dbg_wx_trace_win:stop(State#state.win),
+ dbg_wx_trace_win:stop(Win),
exit(stop)
end.
@@ -269,7 +269,7 @@ gui_cmd('Continue', State) ->
int:meta(State#state.meta, continue),
{Status, Mod, Line} = State#state.status,
if
- Status==wait_break ->
+ Status =:= wait_break ->
Win = dbg_wx_trace_win:unmark_line(State#state.win),
gui_enable_functions(wait_running),
State#state{win=Win, status={wait_running,Mod,Line}};
@@ -291,7 +291,7 @@ gui_cmd('Stop', State) ->
int:meta(State#state.meta, stop),
{Status, Mod, Line} = State#state.status,
if
- Status==wait_running ->
+ Status =:= wait_running ->
Win = dbg_wx_trace_win:mark_line(State#state.win, Line,
break),
gui_enable_functions(wait_break),
@@ -421,7 +421,7 @@ gui_cmd('Function Break...', State) ->
gui_cmd('Enable All', State) ->
Breaks = int:all_breaks(),
ThisMod = State#state.cm,
- lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod ->
+ lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod ->
int:enable_break(Mod, Line);
(_Break) ->
ignore
@@ -431,7 +431,7 @@ gui_cmd('Enable All', State) ->
gui_cmd('Disable All', State) ->
Breaks = int:all_breaks(),
ThisMod = State#state.cm,
- lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod ->
+ lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod ->
int:disable_break(Mod, Line);
(_Break) ->
ignore
@@ -458,7 +458,7 @@ gui_cmd({'Trace Window', TraceWin}, State) ->
Win = dbg_wx_trace_win:configure(State#state.win, TraceWin),
{Status,_,_} = State#state.status,
if
- Status==break; Status==wait_break ->
+ Status =:= break; Status =:= wait_break ->
gui_enable_btrace(Trace, State#state.stack_trace);
true -> ignore
end,
@@ -467,7 +467,7 @@ gui_cmd({'Stack Trace', [Name]}, State) ->
int:meta(State#state.meta, stack_trace, map(Name)),
{Status,_,_} = State#state.status,
if
- Status==break; Status==wait_break ->
+ Status =:= break; Status =:= wait_break ->
gui_enable_btrace(State#state.trace, map(Name));
true -> ignore
end,
@@ -490,9 +490,9 @@ gui_cmd('Debugger', State) ->
gui_cmd({user_command, Cmd}, State) ->
{Status, _Mod, _Line} = State#state.status,
if
- Status==break;
- Status==wait_break;
- Status==wait_running ->
+ Status =:= break;
+ Status =:= wait_break;
+ Status =:= wait_running ->
Cm = State#state.cm,
Arg = case State#state.stack of
{Cur, Max} when Cur<Max -> {Cm, Cmd, Cur};
@@ -531,14 +531,14 @@ add_break(WI, Coords, Type, Mod, Line) ->
int_cmd({interpret, Mod}, State) ->
if
- Mod==State#state.cm ->
+ Mod =:= State#state.cm ->
State#state{cm_obsolete=true};
true ->
State
end;
int_cmd({no_interpret, Mod}, State) ->
if
- Mod==State#state.cm ->
+ Mod =:= State#state.cm ->
State#state{cm_obsolete=true};
true ->
Win = dbg_wx_trace_win:remove_code(State#state.win, Mod),
@@ -584,7 +584,7 @@ meta_cmd({re_entry, dbg_ieval, eval_fun}, State) ->
meta_cmd({re_entry, Mod, _Func}, State) ->
Obs = State#state.cm_obsolete,
case State#state.cm of
- Mod when Obs==true ->
+ Mod when Obs =:= true ->
Win = gui_load_module(State#state.win, Mod,State#state.pid),
State#state{win=Win, cm_obsolete=false};
Mod -> State;
@@ -630,11 +630,11 @@ meta_cmd({func_at, Mod, Line, Cur}, State) ->
gui_enable_functions(idle),
dbg_wx_trace_win:display(State#state.win, idle),
State#state{win=Win, cm=Mod, status={idle,Mod,Line}, stack=Stack};
-meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}}=State)
- when Status/=init, Status/=break ->
+meta_cmd({wait_at, Mod, Line, Cur}, #state{status={Status,_,_}, win=Win}=State)
+ when Status =/= init, Status =/= break ->
Stack = {Cur,Cur},
gui_enable_functions(wait_running),
- dbg_wx_trace_win:display(State#state.win, {wait,Mod,Line}),
+ dbg_wx_trace_win:display(Win, {wait,Mod,Line}),
State#state{status={wait_running,Mod,Line}, stack=Stack};
meta_cmd({wait_at, Mod, Line, Cur}, State) ->
Stack = {Cur,Cur},
@@ -675,7 +675,7 @@ meta_cmd({stack_trace, Flag}, State) ->
gui_enable_updown(Flag, State#state.stack),
{Status,_,_} = State#state.status,
if
- Status==break; Status==wait_break ->
+ Status =:= break; Status =:= wait_break ->
gui_enable_btrace(State#state.trace, Flag);
true -> ignore
end,
@@ -813,7 +813,7 @@ gui_enable_functions(Status) ->
gui_enable_updown(Flag, Stack) ->
{Enable, Disable} =
if
- Flag==false -> {[], ['Up', 'Down']};
+ Flag =:= false -> {[], ['Up', 'Down']};
true ->
case Stack of
{1,1} -> {[], ['Up', 'Down']};
@@ -826,14 +826,14 @@ gui_enable_updown(Flag, Stack) ->
dbg_wx_trace_win:enable(Enable, true),
dbg_wx_trace_win:enable(Disable, false),
if
- Enable==[] -> dbg_wx_trace_win:enable(['Where'], false);
+ Enable =:= [] -> dbg_wx_trace_win:enable(['Where'], false);
true -> dbg_wx_trace_win:enable(['Where'], true)
end.
gui_enable_btrace(Trace, StackTrace) ->
Bool = if
- Trace==false -> false;
- StackTrace==false -> false;
+ Trace =:= false -> false;
+ StackTrace =:= false -> false;
true -> true
end,
dbg_wx_trace_win:enable(['Back Trace'], Bool).
diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl
index 3799acdc1b..720b913024 100755
--- a/lib/debugger/src/dbg_wx_trace_win.erl
+++ b/lib/debugger/src/dbg_wx_trace_win.erl
@@ -410,11 +410,11 @@ clear_breaks(WinInfo) ->
clear_breaks(WinInfo, all).
clear_breaks(WinInfo, Mod) ->
Remove = if
- Mod==all -> WinInfo#winInfo.breaks;
+ Mod =:= all -> WinInfo#winInfo.breaks;
true ->
lists:filter(fun(#breakInfo{point={Mod2,_L}}) ->
if
- Mod2==Mod -> true;
+ Mod2 =:= Mod -> true;
true -> false
end
end,
@@ -481,8 +481,8 @@ display(#winInfo{window=Win, sb=Sb},Arg) ->
%% Note: remove_code/2 should not be used for currently shown module.
%%--------------------------------------------------------------------
is_shown(WinInfo, Mod) ->
- case lists:keysearch(Mod, 1, WinInfo#winInfo.editors) of
- {value, {Mod, Editor}} ->
+ case lists:keyfind(Mod, 1, WinInfo#winInfo.editors) of
+ {Mod, Editor} ->
gs:config(Editor, raise), %% BUGBUG
{true, WinInfo#winInfo{editor={Mod, Editor}}};
false -> false
@@ -494,7 +494,7 @@ show_code(WinInfo = #winInfo{editor={_, Ed}}, Mod, Contents) ->
lists:foreach(fun(BreakInfo) ->
case BreakInfo#breakInfo.point of
- {Mod2, Line} when Mod2==Mod ->
+ {Mod2, Line} when Mod2 =:= Mod ->
Status = BreakInfo#breakInfo.status,
dbg_wx_code:add_break_to_code(Ed, Line,Status);
_Point -> ignore
@@ -540,10 +540,10 @@ select_line(WinInfo, Line) ->
%% help window, it must be checked that it is correct
Size = dbg_wx_code:get_no_lines(Ed),
if
- Line==0 ->
+ Line =:= 0 ->
dbg_wx_code:goto_line(Ed,1),
WinInfo#winInfo{selected_line=0};
- Line<Size ->
+ Line < Size ->
dbg_wx_code:goto_line(Ed,Line),
WinInfo#winInfo{selected_line=Line};
true ->
@@ -632,7 +632,7 @@ handle_event(#wx{id=?SASH_CODE, event=#wxSash{dragRect={_X,_Y,_W,H}}}, Wi) ->
Change = CH - H,
ChangeH = fun(Item) ->
{ItemW, ItemH} = wxSizerItem:getMinSize(Item),
- wxSizerItem:setInitSize(Item, ItemW, max(ItemH+Change,-1))
+ wxSizerItem:setInitSize(Item, ItemW, erlang:max(ItemH+Change,-1))
end,
if Enable ->
{IW, IH} = wxSizer:getMinSize(InfoSzr),
@@ -694,7 +694,7 @@ handle_event(#wx{id=?SASH_TRACE, event=#wxSash{dragRect={_X,_Y,_W,H}}}, Wi) ->
true -> %% Change the Eval and Bindings area
ChangeH = fun(Item) ->
{ItemW, ItemH} = wxSizerItem:getMinSize(Item),
- wxSizerItem:setInitSize(Item, ItemW, max(ItemH+Change,-1))
+ wxSizerItem:setInitSize(Item, ItemW, erlang:max(ItemH+Change,-1))
end,
{IW, IH} = wxSizer:getMinSize(InfoSzr),
[ChangeH(Child) || Child <- wxSizer:getChildren(InfoSzr)],
@@ -764,8 +764,8 @@ handle_event(#wx{event=#wxStyledText{type=stc_doubleclick}},
WinInfo = #winInfo{editor={Mod,Ed}}) ->
Line = wxStyledTextCtrl:getCurrentLine(Ed),
Point = {Mod, Line+1},
- case lists:keysearch(Point, #breakInfo.point,WinInfo#winInfo.breaks) of
- {value, _BreakInfo} -> {break, Point, delete};
+ case lists:keymember(Point, #breakInfo.point, WinInfo#winInfo.breaks) of
+ true -> {break, Point, delete};
false -> {break, Point, add}
end;
@@ -837,7 +837,7 @@ handle_event(#wx{id=?SEARCH_ENTRY, event=#wxCommand{cmdString=Str}},
%% Button area
handle_event(#wx{id=ID, event=#wxCommand{type=command_button_clicked}},_Wi) ->
- {value, {Button, _}} = lists:keysearch(ID, 2, buttons()),
+ {Button, _} = lists:keyfind(ID, 2, buttons()),
Button;
%% Evaluator area
@@ -908,8 +908,8 @@ buttons() ->
{'Where',?WhereButton}, {'Up',?UpButton}, {'Down',?DownButton}].
is_button(Name) ->
- case lists:keysearch(Name, 1, buttons()) of
- {value, {Name, Button}} -> {true, Button};
+ case lists:keyfind(Name, 1, buttons()) of
+ {Name, Button} -> {true, Button};
false -> false
end.
@@ -1021,9 +1021,3 @@ helpwin(Type, WinInfo = #winInfo{sg=Sg =#sub{in=Sa}}) ->
search -> wxWindow:setFocus(Sa#sa.search)
end,
Wi.
-
-max(X,Y) when X > Y -> X;
-max(_,Y) -> Y.
-
-
-
diff --git a/lib/debugger/src/dbg_wx_view.erl b/lib/debugger/src/dbg_wx_view.erl
index 6d34e5650c..6242b9d0e0 100644
--- a/lib/debugger/src/dbg_wx_view.erl
+++ b/lib/debugger/src/dbg_wx_view.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,9 +23,6 @@
%% External exports
-export([start/2]).
-%% Internal exports
--export([init/4]).
-
-record(state, {gs, % term() Graphics system id
win, % term() Attach process window data
coords, % {X,Y} Mouse point position
@@ -46,9 +43,12 @@ start(GS, Mod) ->
true -> ignore;
false ->
Env = wx:get_env(),
- spawn_link(?MODULE, init, [GS, Env, Mod, Title])
+ spawn_link(fun () -> init(GS, Env, Mod, Title) end)
end.
+-spec stop() -> no_return().
+stop() ->
+ exit(normal).
%%====================================================================
%% Main loop and message handling
@@ -84,7 +84,7 @@ loop(State) ->
receive
%% From the GUI main window
- GuiEvent when element(1, GuiEvent)==wx ->
+ GuiEvent when element(1, GuiEvent) =:= wx ->
Cmd = wx:batch(fun() ->
dbg_wx_trace_win:handle_event(GuiEvent, State#state.win)
end),
@@ -116,13 +116,13 @@ loop(State) ->
end.
%%--Commands from the GUI---------------------------------------------
-
+
gui_cmd(ignore, State) ->
State;
gui_cmd({win, Win}, State) ->
State#state{win=Win};
gui_cmd(stopped, _State) ->
- exit(normal);
+ stop();
gui_cmd({coords, Coords}, State) ->
State#state{coords=Coords};
@@ -135,7 +135,7 @@ gui_cmd({shortcut, Key}, State) ->
%% File menu
gui_cmd('Close', State) ->
dbg_wx_trace_win:stop(State#state.win),
- gui_cmd(stopped, State);
+ stop();
%% Edit menu
gui_cmd('Go To Line', State) ->
@@ -167,7 +167,7 @@ gui_cmd('Function Break...', State) ->
gui_cmd('Enable All', State) ->
Breaks = int:all_breaks(),
ThisMod = State#state.mod,
- lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod ->
+ lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod ->
int:enable_break(Mod, Line);
(_Break) ->
ignore
@@ -177,7 +177,7 @@ gui_cmd('Enable All', State) ->
gui_cmd('Disable All', State) ->
Breaks = int:all_breaks(),
ThisMod = State#state.mod,
- lists:foreach(fun ({{Mod, Line}, _Options}) when Mod==ThisMod ->
+ lists:foreach(fun ({{Mod, Line}, _Options}) when Mod =:= ThisMod ->
int:disable_break(Mod, Line);
(_Break) ->
ignore
@@ -214,21 +214,19 @@ add_break(GS, Coords, Type, Mod, Line) ->
%%--Commands from the interpreter-------------------------------------
-int_cmd({new_break, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) ->
- Win = dbg_wx_trace_win:add_break(State#state.win, 'Break', Break),
- State#state{win=Win};
-int_cmd({delete_break, {Mod,_Line}=Point}, #state{mod=Mod}=State) ->
- Win = dbg_wx_trace_win:delete_break(State#state.win, Point),
- State#state{win=Win};
-int_cmd({break_options, {{Mod,_Line},_Options}=Break}, #state{mod=Mod}=State) ->
- Win = dbg_wx_trace_win:update_break(State#state.win, Break),
- State#state{win=Win};
-int_cmd(no_break, State) ->
- Win = dbg_wx_trace_win:clear_breaks(State#state.win),
- State#state{win=Win};
-int_cmd({no_break, _Mod}, State) ->
- Win = dbg_wx_trace_win:clear_breaks(State#state.win),
- State#state{win=Win};
+int_cmd({new_break, {{Mod,_Line},_Options}=Break},
+ #state{mod = Mod, win = Win}=State) ->
+ State#state{win = dbg_wx_trace_win:add_break(Win, 'Break', Break)};
+int_cmd({delete_break, {Mod,_Line}=Point},
+ #state{mod = Mod, win = Win}=State) ->
+ State#state{win = dbg_wx_trace_win:delete_break(Win, Point)};
+int_cmd({break_options, {{Mod,_Line},_Options}=Break},
+ #state{mod = Mod, win = Win}=State) ->
+ State#state{win = dbg_wx_trace_win:update_break(Win, Break)};
+int_cmd(no_break, #state{win = Win}=State) ->
+ State#state{win = dbg_wx_trace_win:clear_breaks(Win)};
+int_cmd({no_break, _Mod}, #state{win = Win}=State) ->
+ State#state{win = dbg_wx_trace_win:clear_breaks(Win)};
int_cmd(_, State) ->
State.
diff --git a/lib/debugger/src/dbg_wx_winman.erl b/lib/debugger/src/dbg_wx_winman.erl
index 1daabe3435..79dcc47f6f 100755
--- a/lib/debugger/src/dbg_wx_winman.erl
+++ b/lib/debugger/src/dbg_wx_winman.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -118,9 +118,9 @@ init([]) ->
{ok, #state{}}.
handle_call({is_started, Title}, _From, State) ->
- Reply = case lists:keysearch(Title, #win.title, State#state.wins) of
- {value, Win} -> {true, Win#win.win};
- false -> false
+ Reply = case lists:keyfind(Title, #win.title, State#state.wins) of
+ false -> false;
+ Win -> {true, Win#win.win}
end,
{reply, Reply, State}.
@@ -132,8 +132,8 @@ handle_cast({insert, Pid, Title, Win}, State) ->
handle_cast({clear_process, Title}, State) ->
OldWins = State#state.wins,
- Wins = case lists:keysearch(Title, #win.title, OldWins) of
- {value, #win{owner=Pid}} ->
+ Wins = case lists:keyfind(Title, #win.title, OldWins) of
+ #win{owner=Pid} ->
Msg = {dbg_ui_winman, destroy},
Pid ! Msg,
lists:keydelete(Title, #win.title, OldWins);
@@ -145,7 +145,7 @@ handle_cast({clear_process, Title}, State) ->
handle_info({'EXIT', Pid, _Reason}, State) ->
[Mon | _Wins] = State#state.wins,
if
- Pid==Mon#win.owner -> {stop, normal, State};
+ Pid =:= Mon#win.owner -> {stop, normal, State};
true ->
Wins2 = lists:keydelete(Pid, #win.owner, State#state.wins),
inform_all(Wins2),
diff --git a/lib/debugger/src/i.erl b/lib/debugger/src/i.erl
index 7c2fb22946..4d0b862196 100644
--- a/lib/debugger/src/i.erl
+++ b/lib/debugger/src/i.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,6 @@
iv() ->
Vsn = string:substr(filename:basename(code:lib_dir(debugger)), 10),
list_to_atom(Vsn).
-
%% -------------------------------------------
%% Start a new graphical monitor.
@@ -288,10 +287,9 @@ ia(X,Y,Z) ->
%% -------------------------------------------
ia(Pid,Fnk) ->
- case lists:keysearch(Pid, 1, int:snapshot()) of
- {value, _PidTuple} ->
- int:attach(Pid,Fnk);
- false -> no_proc
+ case lists:keymember(Pid, 1, int:snapshot()) of
+ false -> no_proc;
+ true -> int:attach(Pid,Fnk)
end.
ia(X,Y,Z,Fnk) ->
diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl
index eeb4df4a8e..b3a8a07f03 100644
--- a/lib/debugger/src/int.erl
+++ b/lib/debugger/src/int.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -261,7 +261,7 @@ del_break_in(Mod, Func, Arity) when is_atom(Mod), is_atom(Func), is_integer(Arit
end.
first_lines(Clauses) ->
- lists:map(fun(Clause) -> first_line(Clause) end, Clauses).
+ [first_line(Clause) || Clause <- Clauses].
first_line({clause,_L,_Vars,_,Exprs}) ->
first_line(Exprs);
@@ -469,10 +469,10 @@ contents(Mod, Pid) ->
%% Arity = integer()
%%--------------------------------------------------------------------
functions(Mod) ->
- lists:filter(fun([module_info, _Arity]) -> false;
- (_Func) -> true
- end,
- dbg_iserver:call({functions, Mod})).
+ [F || F <- dbg_iserver:call({functions, Mod}), functions_1(F)].
+
+functions_1([module_info, _Arity]) -> false;
+functions_1(_Func) -> true.
%%====================================================================
diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile
new file mode 100644
index 0000000000..2296bd0ae6
--- /dev/null
+++ b/lib/debugger/test/Makefile
@@ -0,0 +1,106 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ andor_SUITE \
+ bs_bincomp_SUITE \
+ bs_construct_SUITE \
+ bs_match_bin_SUITE \
+ bs_match_int_SUITE \
+ bs_match_misc_SUITE \
+ bs_match_tail_SUITE \
+ bs_utf_SUITE \
+ bug_SUITE \
+ erl_eval_SUITE \
+ dbg_ui_SUITE \
+ debugger_SUITE \
+ int_SUITE \
+ int_break_SUITE \
+ int_eval_SUITE \
+ guard_SUITE \
+ exception_SUITE \
+ fun_SUITE \
+ lc_SUITE \
+ record_SUITE \
+ trycatch_SUITE \
+ test_lib \
+ cleanup
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/debugger_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' \
+ >> $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) debugger.spec debugger.cover $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
+ @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
diff --git a/lib/debugger/test/andor_SUITE.erl b/lib/debugger/test/andor_SUITE.erl
new file mode 100644
index 0000000000..13a6e3da1e
--- /dev/null
+++ b/lib/debugger/test/andor_SUITE.erl
@@ -0,0 +1,320 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(andor_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ t_andalso/1,t_orelse/1,inside/1,overlap/1,
+ combined/1,in_case/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ ?line Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+cases() ->
+ [t_andalso, t_orelse, inside, overlap, combined,
+ in_case].
+
+t_andalso(Config) when is_list(Config) ->
+ Bs = [true,false],
+ Ps = [{X,Y} || X <- Bs, Y <- Bs],
+ lists:foreach(fun (P) -> t_andalso_1(P) end, Ps),
+
+ ?line true = true andalso true,
+ ?line false = true andalso false,
+ ?line false = false andalso true,
+ ?line false = false andalso false,
+
+ ?line false = false andalso glurf,
+ ?line false = false andalso exit(exit_now),
+
+ ?line true = not id(false) andalso not id(false),
+ ?line false = not id(false) andalso not id(true),
+ ?line false = not id(true) andalso not id(false),
+ ?line false = not id(true) andalso not id(true),
+
+ ?line {'EXIT',{badarg,_}} = (catch not id(glurf) andalso id(true)),
+ ?line {'EXIT',{badarg,_}} = (catch not id(false) andalso not id(glurf)),
+ ?line false = id(false) andalso not id(glurf),
+ ?line false = false andalso not id(glurf),
+
+ ok.
+
+t_orelse(Config) when is_list(Config) ->
+ Bs = [true,false],
+ Ps = [{X,Y} || X <- Bs, Y <- Bs],
+ lists:foreach(fun (P) -> t_orelse_1(P) end, Ps),
+
+ ?line true = true orelse true,
+ ?line true = true orelse false,
+ ?line true = false orelse true,
+ ?line false = false orelse false,
+
+ ?line true = true orelse glurf,
+ ?line true = true orelse exit(exit_now),
+
+ ?line true = not id(false) orelse not id(false),
+ ?line true = not id(false) orelse not id(true),
+ ?line true = not id(true) orelse not id(false),
+ ?line false = not id(true) orelse not id(true),
+
+ ?line {'EXIT',{badarg,_}} = (catch not id(glurf) orelse id(true)),
+ ?line {'EXIT',{badarg,_}} = (catch not id(true) orelse not id(glurf)),
+ ?line true = id(true) orelse not id(glurf),
+ ?line true = true orelse not id(glurf),
+
+ ok.
+
+t_andalso_1({X,Y}) ->
+ io:fwrite("~w andalso ~w: ",[X,Y]),
+ V1 = echo(X) andalso echo(Y),
+ V1 = if
+ X andalso Y -> true;
+ true -> false
+ end,
+ check(V1, X and Y).
+
+t_orelse_1({X,Y}) ->
+ io:fwrite("~w orelse ~w: ",[X,Y]),
+ V1 = echo(X) orelse echo(Y),
+ V1 = if
+ X orelse Y -> true;
+ true -> false
+ end,
+ check(V1, X or Y).
+
+inside(Config) when is_list(Config) ->
+ ?line true = inside(-8, 1),
+ ?line false = inside(-53.5, -879798),
+ ?line false = inside(1.0, -879),
+ ?line false = inside(59, -879),
+ ?line false = inside(-11, 1.0),
+ ?line false = inside(100, 0.2),
+ ?line false = inside(100, 1.2),
+ ?line false = inside(-53.5, 4),
+ ?line false = inside(1.0, 5.3),
+ ?line false = inside(59, 879),
+ ok.
+
+inside(Xm, Ym) ->
+ X = -10.0,
+ Y = -2.0,
+ W = 20.0,
+ H = 4.0,
+ Res = inside(Xm, Ym, X, Y, W, H),
+ Res = if
+ X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H -> true;
+ true -> false
+ end,
+ case not id(Res) of
+ Outside ->
+ Outside = if
+ not(X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H) -> true;
+ true -> false
+ end
+ end,
+ {Res,Xm,Ym,X,Y,W,H} = inside_guard(Xm, Ym, X, Y, W, H),
+ io:format("~p =< ~p andalso ~p < ~p andalso ~p =< ~p andalso ~p < ~p ==> ~p",
+ [X,Xm,Xm,X+W,Y,Ym,Ym,Y+H,Res]),
+ Res.
+
+inside(Xm, Ym, X, Y, W, H) ->
+ X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H.
+
+inside_guard(Xm, Ym, X, Y, W, H) when X =< Xm andalso Xm < X+W
+ andalso Y =< Ym andalso Ym < Y+H ->
+ {true,Xm,Ym,X,Y,W,H};
+inside_guard(Xm, Ym, X, Y, W, H) ->
+ {false,Xm,Ym,X,Y,W,H}.
+
+overlap(Config) when is_list(Config) ->
+ ?line true = overlap(7.0, 2.0, 8.0, 0.5),
+ ?line true = overlap(7.0, 2.0, 8.0, 2.5),
+ ?line true = overlap(7.0, 2.0, 5.3, 2),
+ ?line true = overlap(7.0, 2.0, 0.0, 100.0),
+
+ ?line false = overlap(-1, 2, -35, 0.5),
+ ?line false = overlap(-1, 2, 777, 0.5),
+ ?line false = overlap(-1, 2, 2, 10),
+ ?line false = overlap(2, 10, 12, 55.3),
+ ok.
+
+overlap(Pos1, Len1, Pos2, Len2) ->
+ Res = case Pos1 of
+ Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
+ orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) ->
+ true;
+ Pos1 -> false
+ end,
+ Res = (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
+ orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1),
+ Res = case Pos1 of
+ Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
+ orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) ->
+ true;
+ Pos1 -> false
+ end,
+ id(Res).
+
+
+-define(COMB(A,B,C), (A andalso B orelse C)).
+
+combined(Config) when is_list(Config) ->
+ ?line false = comb(false, false, false),
+ ?line true = comb(false, false, true),
+ ?line false = comb(false, true, false),
+ ?line true = comb(false, true, true),
+
+ ?line false = comb(true, false, false),
+ ?line true = comb(true, true, false),
+ ?line true = comb(true, false, true),
+ ?line true = comb(true, true, true),
+
+ ?line false = comb(false, blurf, false),
+ ?line true = comb(false, blurf, true),
+ ?line true = comb(true, true, blurf),
+
+ ?line false = ?COMB(false, false, false),
+ ?line true = ?COMB(false, false, true),
+ ?line false = ?COMB(false, true, false),
+ ?line true = ?COMB(false, true, true),
+
+ ?line false = ?COMB(true, false, false),
+ ?line true = ?COMB(true, true, false),
+ ?line true = ?COMB(true, false, true),
+ ?line true = ?COMB(true, true, true),
+
+ ?line false = ?COMB(false, blurf, false),
+ ?line true = ?COMB(false, blurf, true),
+ ?line true = ?COMB(true, true, blurf),
+
+ ok.
+-undef(COMB).
+
+comb(A, B, C) ->
+ Res = A andalso B orelse C,
+ Res = if
+ A andalso B orelse C -> true;
+ true -> false
+ end,
+ NotRes = if
+ not(A andalso B orelse C) -> true;
+ true -> false
+ end,
+ NotRes = id(not Res),
+ Res = A andalso B orelse C,
+ Res = if
+ A andalso B orelse C -> true;
+ true -> false
+ end,
+ NotRes = id(not Res),
+ Res = if
+ A andalso B orelse C -> true;
+ true -> false
+ end,
+ id(Res).
+
+%% Test that a boolean expression in a case expression is properly
+%% optimized (in particular, that the error behaviour is correct).
+in_case(Config) when is_list(Config) ->
+ ?line edge_rings = in_case_1(1, 1, 1, 1, 1),
+ ?line not_loop = in_case_1(0.5, 1, 1, 1, 1),
+ ?line loop = in_case_1(0.5, 0.9, 1.1, 1, 4),
+ ?line {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, 0)),
+ ?line {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, nan)),
+ ?line {'EXIT',{badarg,_}} = (catch in_case_1(1, 1, 1, blurf, 1)),
+ ?line {'EXIT',{badarith,_}} = (catch in_case_1([nan], 1, 1, 1, 1)),
+ ok.
+
+in_case_1(LenUp, LenDw, LenN, Rotation, Count) ->
+ Res = in_case_1_body(LenUp, LenDw, LenN, Rotation, Count),
+ Res = in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count),
+ Res.
+
+in_case_1_body(LenUp, LenDw, LenN, Rotation, Count) ->
+ case (LenUp/Count > 0.707) and (LenN/Count > 0.707) and
+ (abs(Rotation) > 0.707) of
+ true ->
+ edge_rings;
+ false ->
+ case (LenUp >= 1) or (LenDw >= 1) or
+ (LenN =< 1) or (Count < 4) of
+ true ->
+ not_loop;
+ false ->
+ loop
+ end
+ end.
+
+in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count) ->
+ case (LenUp/Count > 0.707) andalso (LenN/Count > 0.707) andalso
+ (abs(Rotation) > 0.707) of
+ true -> edge_rings;
+ false when LenUp >= 1 orelse LenDw >= 1 orelse
+ LenN =< 1 orelse Count < 4 -> not_loop;
+ false -> loop
+ end.
+
+check(V1, V0) ->
+ if V1 /= V0 ->
+ io:fwrite("error: ~w.\n", [V1]),
+ ?t:fail();
+ true ->
+ io:fwrite("ok: ~w.\n", [V1])
+ end.
+
+echo(X) ->
+ io:fwrite("eval(~w); ",[X]),
+ X.
+
+id(I) -> I.
diff --git a/lib/debugger/test/bs_bincomp_SUITE.erl b/lib/debugger/test/bs_bincomp_SUITE.erl
new file mode 100644
index 0000000000..6c2fd255a1
--- /dev/null
+++ b/lib/debugger/test/bs_bincomp_SUITE.erl
@@ -0,0 +1,126 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Originally based on Per Gustafsson's test suite.
+%%
+
+-module(bs_bincomp_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ byte_aligned/1,bit_aligned/1,extended_byte_aligned/1,
+ extended_bit_aligned/1,mixed/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [byte_aligned, bit_aligned, extended_byte_aligned,
+ extended_bit_aligned, mixed].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+byte_aligned(Config) when is_list(Config) ->
+ ?line <<"abcdefg">> = << <<(X+32)>> || <<X>> <= <<"ABCDEFG">> >>,
+ ?line <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || <<X:32>> <= <<1:32,2:32,3:32,4:32>> >>,
+ ?line <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || <<X:16>> <= <<1:16,2:16,3:16,4:16>> >>,
+ ok.
+
+bit_aligned(Config) when is_list(Config) ->
+ ?line <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ << <<(X+32):7>> || <<X>> <= <<"ABCDEFG">> >>,
+ ?line <<"ABCDEFG">> =
+ << <<(X-32)>> || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> >>,
+ ?line <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || <<X:31>> <= <<1:31,2:31,3:31,4:31>> >>,
+ ?line <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || <<X:15>> <= <<1:15,2:15,3:15,4:15>> >>,
+ ok.
+
+extended_byte_aligned(Config) when is_list(Config) ->
+ ?line <<"abcdefg">> = << <<(X+32)>> || X <- "ABCDEFG" >>,
+ ?line "abcdefg" = [(X+32) || <<X>> <= <<"ABCDEFG">>],
+ ?line <<1:32/little,2:32/little,3:32/little,4:32/little>> =
+ << <<X:32/little>> || X <- [1,2,3,4] >>,
+ ?line [256,512,768,1024] =
+ [X || <<X:16/little>> <= <<1:16,2:16,3:16,4:16>>],
+ ok.
+
+extended_bit_aligned(Config) when is_list(Config) ->
+ ?line <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>> =
+ << <<(X+32):7>> || X <- "ABCDEFG" >>,
+ ?line "ABCDEFG" = [(X-32) || <<X:7>> <= <<$a:7,$b:7,$c:7,$d:7,$e:7,$f:7,$g:7>>],
+ ?line <<1:31/little,2:31/little,3:31/little,4:31/little>> =
+ << <<X:31/little>> || X <- [1,2,3,4] >>,
+ ?line [256,512,768,1024] =
+ [X || <<X:15/little>> <= <<1:15,2:15,3:15,4:15>>],
+ ok.
+
+mixed(Config) when is_list(Config) ->
+ ?line <<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>> >>,
+ ?line <<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || <<X>> <= <<1,2,3,4>>, Y <- [1,2] >>,
+ ?line <<2,3,3,4,4,5,5,6>> =
+ << <<(X+Y)>> || X <- [1,2,3,4], Y <- [1,2] >>,
+ ?line [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X>> <= <<1,2,3,4>>, <<Y>> <= <<1,2>>],
+ ?line [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X>> <= <<1,2,3,4>>, Y <- [1,2]],
+ ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>> >>,
+ ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2] >>,
+ ?line <<2:3,3:3,3:3,4:3,4:3,5:3,5:3,6:3>> =
+ << <<(X+Y):3>> || X <- [1,2,3,4], Y <- [1,2] >>,
+ ?line [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, <<Y:3>> <= <<1:3,2:3>>],
+ ?line [2,3,3,4,4,5,5,6] =
+ [(X+Y) || <<X:3>> <= <<1:3,2:3,3:3,4:3>>, Y <- [1,2]],
+ ok.
diff --git a/lib/debugger/test/bs_construct_SUITE.erl b/lib/debugger/test/bs_construct_SUITE.erl
new file mode 100644
index 0000000000..5c7d49e951
--- /dev/null
+++ b/lib/debugger/test/bs_construct_SUITE.erl
@@ -0,0 +1,446 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(bs_construct_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, not_used/1, in_guard/1,
+ coerce_to_float/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [test1, test2, test3, test4, test5, testf, not_used,
+ in_guard, coerce_to_float].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+big(1) ->
+ 57285702734876389752897683.
+
+i(X) -> X.
+
+r(L) ->
+ lists:reverse(L).
+
+-define(T(B, L), {B, ??B, L}).
+-define(N(B), {B, ??B, unknown}).
+
+-define(FAIL(Expr), ?line {'EXIT',{badarg,_}} = (catch Expr)).
+
+l(I_13, I_big1) ->
+ [
+ ?T(<<-43>>,
+ [256-43]),
+ ?T(<<56>>,
+ [56]),
+ ?T(<<1,2>>,
+ [1, 2]),
+ ?T(<<4:4, 7:4>>,
+ [4*16+7]),
+ ?T(<<777:16/big>>,
+ [3, 9]),
+ ?T(<<777:16/little>>,
+ [9, 3]),
+ ?T(<<0.0:32/float>>,
+ [0,0,0,0]),
+ ?T(<<0.125:32/float>>,
+ [62,0,0,0]),
+ ?T(<<0.125:32/little-float>>,
+ [0,0,0,62]),
+ ?T(<<I_big1:32>>,
+ [138, 99, 0, 147]),
+ ?T(<<57285702734876389752897684:32>>,
+ [138, 99, 0, 148]),
+ ?T(<<I_big1:32/little>>,
+ r([138, 99, 0, 147])),
+ ?T(<<-1:17/unit:8>>,
+ lists:duplicate(17, 255)),
+
+ ?T(<<I_13>>,
+ [13]),
+
+ ?T(<<4:8/unit:2,5:2/unit:8>>,
+ [0, 4, 0, 5]),
+
+ ?T(<<1:1, 0:6, 1:1>>,
+ [129]),
+ ?T(<<1:1/little, 0:6/little, 1:1/little>>,
+ [129]),
+
+ ?T(<<<<1,2>>/binary>>,
+ [1, 2]),
+ ?T(<<<<1,2>>:1/binary>>,
+ [1]),
+ ?T(<<4,3,<<1,2>>:1/binary>>,
+ [4,3,1]),
+
+ ?T(<<(256*45+47)>>,
+ [47]),
+
+ ?T(<<57:0>>,
+ []),
+
+ ?T(<<"apa">>,
+ "apa"),
+
+ ?T(<<1:3,"string",9:5>>,
+ [46,110,142,77,45,204,233]),
+
+ ?T(<<>>,
+ []),
+
+ ?T(<<37.98:64/native-float>>,
+ native_3798()),
+
+ ?T(<<32978297842987249827298387697777669766334937:128/native-integer>>,
+ native_bignum())
+
+ ].
+
+native_3798() ->
+ case <<1:16/native>> of
+ <<0,1>> -> [64,66,253,112,163,215,10,61];
+ <<1,0>> -> [61,10,215,163,112,253,66,64]
+ end.
+
+native_bignum() ->
+ case <<1:16/native>> of
+ <<0,1>> -> [129,205,18,177,1,213,170,101,39,231,109,128,176,11,73,217];
+ <<1,0>> -> [217,73,11,176,128,109,231,39,101,170,213,1,177,18,205,129]
+ end.
+
+evaluate(Str, Vars) ->
+ {ok,Tokens,_} =
+ erl_scan:string(Str ++ " . "),
+ {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ case erl_eval:expr(Expr, Vars) of
+ {value, Result, _} ->
+ Result
+ end.
+
+eval_list([], _Vars) ->
+ [];
+eval_list([{C_bin, Str, Bytes} | Rest], Vars) ->
+ case catch evaluate(Str, Vars) of
+ {'EXIT', Error} ->
+ io:format("Evaluation error: ~p, ~p, ~p~n", [Str, Vars, Error]),
+ exit(Error);
+ E_bin ->
+ [{C_bin, E_bin, Str, Bytes} | eval_list(Rest, Vars)]
+ end.
+
+one_test({C_bin, E_bin, Str, Bytes}) when list(Bytes) ->
+ io:format(" ~s, ~p~n", [Str, Bytes]),
+ Bin = list_to_binary(Bytes),
+ if
+ C_bin == Bin ->
+ ok;
+ true ->
+ io:format("ERROR: Compiled: ~p. Expected ~p. Got ~p.~n",
+ [Str, Bytes, binary_to_list(C_bin)]),
+ test_server:fail(comp)
+ end,
+ if
+ E_bin == Bin ->
+ ok;
+ true ->
+ io:format("ERROR: Interpreted: ~p. Expected ~p. Got ~p.~n",
+ [Str, Bytes, binary_to_list(E_bin)]),
+ test_server:fail(comp)
+ end;
+one_test({C_bin, E_bin, Str, Result}) ->
+ io:format(" ~s ~p~n", [Str, C_bin]),
+ if
+ C_bin == E_bin ->
+ ok;
+ true ->
+ Arbitrary = case Result of
+ unknown ->
+ size(C_bin);
+ _ ->
+ Result
+ end,
+ case equal_lists(binary_to_list(C_bin),
+ binary_to_list(E_bin),
+ Arbitrary) of
+ false ->
+ io:format("ERROR: Compiled not equal to interpreted:"
+ "~n ~p, ~p.~n",
+ [binary_to_list(C_bin), binary_to_list(E_bin)]),
+ test_server:fail(comp);
+ 0 ->
+ ok;
+ %% For situations where the final bits may not matter, like
+ %% for floats:
+ N when integer(N) ->
+ io:format("Info: compiled and interpreted differ in the"
+ " last bytes:~n ~p, ~p.~n",
+ [binary_to_list(C_bin), binary_to_list(E_bin)]),
+ ok
+ end
+ end.
+
+equal_lists([], [], _) ->
+ 0;
+equal_lists([], _, _) ->
+ false;
+equal_lists(_, [], _) ->
+ false;
+equal_lists([A|AR], [A|BR], R) ->
+ equal_lists(AR, BR, R);
+equal_lists(A, B, R) ->
+ if
+ length(A) /= length(B) ->
+ false;
+ length(A) =< R ->
+ R;
+ true ->
+ false
+ end.
+
+%%% Simple working cases
+test1(suite) -> [];
+test1(Config) when list(Config) ->
+ ?line I_13 = i(13),
+ ?line I_big1 = big(1),
+ ?line Vars = [{'I_13', I_13},
+ {'I_big1', I_big1}],
+ ?line lists:foreach(fun one_test/1, eval_list(l(I_13, I_big1), Vars)).
+
+%%% Misc
+
+%%% <<A:S, A:(N-S)>>
+comp(N, A, S) ->
+ M1 = (1 bsl S) - 1,
+ M2 = (1 bsl (N-S)) - 1,
+ [((A band M1) bsl (N-S)) bor (A band M2)].
+
+gen(N, S, A) ->
+ [?T(<<A:S, A:(N-S)>>, comp(N, A, S))].
+
+gen_l(N, S, A) ->
+ [?T(<<A:S/little, A:(N-S)/little>>, comp(N, A, S))].
+
+test2(suite) -> [];
+test2(Config) when list(Config) ->
+ ?line test2(0, 8, 2#10101010101010101),
+ ?line test2(0, 8, 2#1111111111).
+
+test2(End, End, _) ->
+ ok;
+test2(I, End, A) ->
+ test2(I, A),
+ test2(I+1, End, A).
+
+test2(S, A) ->
+ N = 8,
+ Vars = [{'A',A}, {'N',N}, {'S',S}],
+ io:format("Vars: ~p\n", [Vars]),
+ lists:foreach(fun one_test/1, eval_list(gen(N, S, A), Vars)),
+ lists:foreach(fun one_test/1, eval_list(gen_l(N, S, A), Vars)).
+
+%%% Tests without facit
+
+t3() ->
+ [?N(<<4711:13, 9876:13, 3:6>>),
+ ?N(<<4.57:64/float>>),
+ ?N(<<4.57:32/float>>),
+
+ ?N(<<>>)
+ ].
+
+test3(suite) -> [];
+test3(Config) when list(Config) ->
+ ?line Vars = [],
+ ?line lists:foreach(fun one_test/1, eval_list(t3(), Vars)).
+
+gen_u(N, S, A) ->
+ [?N(<<A:S, A:(N-S)>>)].
+
+gen_u_l(N, S, A) ->
+ [?N(<<A:S/little, A:(N-S)/little>>)].
+
+test4(suite) -> [];
+test4(Config) when list(Config) ->
+ ?line test4(0, 16, 2#10101010101010101),
+ ?line test4(0, 16, 2#1111111111).
+
+test4(End, End, _) ->
+ ok;
+test4(I, End, A) ->
+ test4(I, A),
+ test4(I+1, End, A).
+
+test4(S, A) ->
+ N = 16,
+ Vars = [{'A', A}, {'N', 16}, {'S', S}],
+ lists:foreach(fun one_test/1, eval_list(gen_u(N, S, A), Vars)),
+ lists:foreach(fun one_test/1, eval_list(gen_u_l(N, S, A), Vars)).
+
+gen_b(N, S, A) ->
+ [?T(<<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>,
+ binary_to_list(<<A:S/binary-unit:1, A:(N-S)/binary-unit:1>>))].
+
+test5(suite) -> [];
+test5(doc) -> ["OTP-3995"];
+test5(Config) when list(Config) ->
+ ?line test5(0, 8, <<73>>),
+ ?line test5(0, 8, <<68>>).
+
+test5(End, End, _) ->
+ ok;
+test5(I, End, A) ->
+ test5(I, A),
+ test5(I+1, End, A).
+
+test5(S, A) ->
+ N = 8,
+ Vars = [{'A', A}, {'N', 8}, {'S', S}],
+ lists:foreach(fun one_test/1, eval_list(gen_b(N, S, A), Vars)).
+
+%%% Failure cases
+testf(suite) -> [];
+testf(Config) when list(Config) ->
+ ?FAIL(<<3.14>>),
+ ?FAIL(<<<<1,2>>>>),
+
+ ?FAIL(<<2.71/binary>>),
+ ?FAIL(<<24334/binary>>),
+ ?FAIL(<<24334344294788947129487129487219847/binary>>),
+
+ ?FAIL(<<<<1,2,3>>/float>>),
+
+ %% Negative field widths.
+ testf_1(-8, <<1,2,3,4,5>>),
+
+ ?FAIL(<<42:(-16)>>),
+ ?FAIL(<<3.14:(-8)/float>>),
+ ?FAIL(<<<<23,56,0,2>>:(-16)/binary>>),
+ ?FAIL(<<<<23,56,0,2>>:(2.5)/binary>>),
+ ?FAIL(<<<<23,56,0,2>>:(anka)>>),
+
+ ok.
+
+testf_1(W, B) ->
+ ?FAIL(<<42:W>>),
+ ?FAIL(<<3.14:W/float>>),
+ ?FAIL(<<B:W/binary>>).
+
+not_used(doc) ->
+ "Test that constructed binaries that are not used will still give an exception.";
+not_used(Config) when is_list(Config) ->
+ ?line ok = not_used1(3, <<"dum">>),
+ ?line ?FAIL(not_used1(3, "dum")),
+ ?line ?FAIL(not_used2(444, -2)),
+ ?line ?FAIL(not_used2(444, anka)),
+ ?line ?FAIL(not_used3(444)),
+ ok.
+
+not_used1(I, BinString) ->
+ <<I:32,BinString/binary>>,
+ ok.
+
+not_used2(I, Sz) ->
+ <<I:Sz>>,
+ ok.
+
+not_used3(I) ->
+ <<I:(-8)>>,
+ ok.
+
+in_guard(Config) when list(Config) ->
+ ?line 1 = in_guard(<<16#74ad:16>>, 16#e95, 5),
+ ?line 2 = in_guard(<<16#3A,16#F7,"hello">>, 16#3AF7, <<"hello">>),
+ ?line 3 = in_guard(<<16#FBCD:14,3.1415/float,3:2>>, 16#FBCD, 3.1415),
+ nope = in_guard(<<1>>, 42, b),
+ nope = in_guard(<<1>>, a, b),
+ nope = in_guard(<<1,2>>, 1, 1),
+ nope = in_guard(<<4,5>>, 1, 2.71),
+ nope = in_guard(<<4,5>>, 1, <<12,13>>),
+ ok.
+
+in_guard(Bin, A, B) when <<A:13,B:3>> == Bin -> 1;
+in_guard(Bin, A, B) when <<A:16,B/binary>> == Bin -> 2;
+in_guard(Bin, A, B) when <<A:14,B/float,3:2>> == Bin -> 3;
+in_guard(Bin, A, B) when {a,b,<<A:14,B/float,3:2>>} == Bin -> cant_happen;
+in_guard(_, _, _) -> nope.
+
+-define(COF(Int0),
+ ?line (fun(Int) ->
+ true = <<Int:32/float>> =:= <<(float(Int)):32/float>>,
+ true = <<Int:64/float>> =:= <<(float(Int)):64/float>>
+ end)(nonliteral(Int0)),
+ ?line true = <<Int0:32/float>> =:= <<(float(Int0)):32/float>>,
+ ?line true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>).
+
+-define(COF64(Int0),
+ ?line (fun(Int) ->
+ true = <<Int:64/float>> =:= <<(float(Int)):64/float>>
+ end)(nonliteral(Int0)),
+ ?line true = <<Int0:64/float>> =:= <<(float(Int0)):64/float>>).
+
+nonliteral(X) -> X.
+
+coerce_to_float(Config) when list(Config) ->
+ ?COF(0),
+ ?COF(-1),
+ ?COF(1),
+ ?COF(42),
+ ?COF(255),
+ ?COF(-255),
+ ?COF(38474),
+ ?COF(387498738948729893849444444443),
+ ?COF(-37489378937773899999999999999993),
+ ?COF64(298748888888888888888888888883478264866528467367364766666666666666663),
+ ?COF64(-367546729879999999999947826486652846736736476555566666663),
+ ok.
diff --git a/lib/debugger/test/bs_match_bin_SUITE.erl b/lib/debugger/test/bs_match_bin_SUITE.erl
new file mode 100644
index 0000000000..b42b84aef2
--- /dev/null
+++ b/lib/debugger/test/bs_match_bin_SUITE.erl
@@ -0,0 +1,128 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(bs_match_bin_SUITE).
+
+-author('[email protected]').
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ byte_split_binary/1,bit_split_binary/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [byte_split_binary, bit_split_binary].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+byte_split_binary(doc) -> "Tries to split a binary at all byte-aligned positions.";
+byte_split_binary(suite) -> [];
+byte_split_binary(Config) when list(Config) ->
+ ?line L = lists:seq(0, 57),
+ ?line B = mkbin(L),
+ ?line byte_split(L, B, size(B)).
+
+byte_split(L, B, Pos) when Pos >= 0 ->
+ ?line Sz1 = Pos,
+ ?line Sz2 = size(B) - Pos,
+ ?line <<B1:Sz1/binary,B2:Sz2/binary>> = B,
+ ?line B1 = list_to_binary(lists:sublist(L, 1, Pos)),
+ ?line B2 = list_to_binary(lists:nthtail(Pos, L)),
+ ?line byte_split(L, B, Pos-1);
+byte_split(_L, _B, _) -> ok.
+
+bit_split_binary(doc) -> "Tries to split a binary at all positions.";
+bit_split_binary(suite) -> [];
+bit_split_binary(Config) when list(Config) ->
+ Fun = fun(Bin, List, SkipBef, N) ->
+ ?line SkipAft = 8*size(Bin) - N - SkipBef,
+ io:format("~p, ~p, ~p", [SkipBef,N,SkipAft]),
+ ?line <<_I1:SkipBef,OutBin:N/binary-unit:1,_I2:SkipAft>> = Bin,
+ ?line OutBin = make_bin_from_list(List, N)
+ end,
+ ?line bit_split_binary1(Fun, erlang:md5(<<1,2,3>>)),
+ ok.
+
+bit_split_binary1(Action, Bin) ->
+ BitList = bits_to_list(binary_to_list(Bin), 16#80),
+ bit_split_binary2(Action, Bin, BitList, 0).
+
+bit_split_binary2(Action, Bin, [_|T]=List, Bef) ->
+ bit_split_binary3(Action, Bin, List, Bef, size(Bin)*8),
+ bit_split_binary2(Action, Bin, T, Bef+1);
+bit_split_binary2(_Action, _Bin, [], _Bef) -> ok.
+
+bit_split_binary3(Action, Bin, List, Bef, Aft) when Bef =< Aft ->
+ Action(Bin, List, Bef, (Aft-Bef) div 8 * 8),
+ bit_split_binary3(Action, Bin, List, Bef, Aft-8);
+bit_split_binary3(_, _, _, _, _) -> ok.
+
+make_bin_from_list(_List, 0) ->
+ mkbin([]);
+make_bin_from_list(List, N) ->
+ list_to_binary([make_int(List, 8, 0),
+ make_bin_from_list(lists:nthtail(8, List), N-8)]).
+
+
+make_int(_List, 0, Acc) -> Acc;
+make_int([H|T], N, Acc) -> make_int(T, N-1, Acc bsl 1 bor H).
+
+bits_to_list([_H|T], 0) -> bits_to_list(T, 16#80);
+bits_to_list([H|_]=List, Mask) ->
+ [case H band Mask of
+ 0 -> 0;
+ _ -> 1
+ end|bits_to_list(List, Mask bsr 1)];
+bits_to_list([], _) -> [].
+
+
+mkbin(L) when list(L) -> list_to_binary(L).
diff --git a/lib/debugger/test/bs_match_int_SUITE.erl b/lib/debugger/test/bs_match_int_SUITE.erl
new file mode 100644
index 0000000000..745368fdfc
--- /dev/null
+++ b/lib/debugger/test/bs_match_int_SUITE.erl
@@ -0,0 +1,244 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(bs_match_int_SUITE).
+
+-author('[email protected]').
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ integer/1,signed_integer/1,dynamic/1,more_dynamic/1,mml/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+-import(lists, [seq/2]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [cases()].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [integer, signed_integer, dynamic, more_dynamic, mml].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(4)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+integer(suite) -> [];
+integer(Config) when list(Config) ->
+ ?line 0 = get_int(mkbin([])),
+ ?line 0 = get_int(mkbin([0])),
+ ?line 42 = get_int(mkbin([42])),
+ ?line 255 = get_int(mkbin([255])),
+ ?line 256 = get_int(mkbin([1,0])),
+ ?line 257 = get_int(mkbin([1,1])),
+ ?line 258 = get_int(mkbin([1,2])),
+ ?line 258 = get_int(mkbin([1,2])),
+ ?line 65534 = get_int(mkbin([255,254])),
+ ?line 16776455 = get_int(mkbin([255,253,7])),
+ ?line 4245492555 = get_int(mkbin([253,13,19,75])),
+ ?line Eight = [200,1,19,128,222,42,97,111],
+ ?line cmp128(Eight, uint(Eight)),
+ ?line fun_clause(catch get_int(mkbin(seq(1,5)))),
+ ok.
+
+get_int(<<I:0>>) -> I;
+get_int(<<I:8>>) -> I;
+get_int(<<I:16>>) -> I;
+get_int(<<I:24>>) -> I;
+get_int(<<I:32>>) -> I.
+
+cmp128(<<I:128>>, I) -> equal;
+cmp128(_B, _I) -> not_equal.
+
+signed_integer(suite) -> [];
+signed_integer(Config) when list(Config) ->
+ ?line {no_match,_} = sint(mkbin([])),
+ ?line {no_match,_} = sint(mkbin([1,2,3])),
+ ?line 127 = sint(mkbin([127])),
+ ?line -1 = sint(mkbin([255])),
+ ?line -128 = sint(mkbin([128])),
+ ?line 42 = sint(mkbin([42,255])),
+ ?line 127 = sint(mkbin([127,255])).
+
+sint(Bin) ->
+ case Bin of
+ <<I:8/signed>> -> I;
+ <<I:8/signed,_:3,_:5>> -> I;
+ Other -> {no_match,Other}
+ end.
+
+uint(L) -> uint(L, 0).
+uint([H|T], Acc) -> uint(T, Acc bsl 8 bor H);
+uint([], Acc) -> Acc.
+
+dynamic(Config) when list(Config) ->
+ dynamic(mkbin([255]), 8),
+ dynamic(mkbin([255,255]), 16),
+ dynamic(mkbin([255,255,255]), 24),
+ dynamic(mkbin([255,255,255,255]), 32),
+ ok.
+
+dynamic(Bin, S1) when S1 >= 0 ->
+ S2 = size(Bin) * 8 - S1,
+ dynamic(Bin, S1, S2, (1 bsl S1) - 1, (1 bsl S2) - 1),
+ dynamic(Bin, S1-1);
+dynamic(_Bin, _) -> ok.
+
+dynamic(Bin, S1, S2, A, B) ->
+% io:format("~p ~p ~p ~p\n", [S1,S2,A,B]),
+ case Bin of
+ <<A:S1,B:S2>> ->
+ io:format("~p ~p ~p ~p\n", [S1,S2,A,B]),
+ ok;
+ _Other ->
+ erlang:error(badmatch, [Bin,S1,S2,A,B])
+ end.
+
+more_dynamic(doc) -> "Extract integers at different alignments and of different sizes.";
+more_dynamic(Config) when list(Config) ->
+
+ % Unsigned big-endian numbers.
+ Unsigned = fun(Bin, List, SkipBef, N) ->
+ SkipAft = 8*size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N,_I2:SkipAft>> = Bin,
+ Int = make_int(List, N, 0)
+ end,
+ ?line more_dynamic1(Unsigned, funny_binary(42)),
+
+ % Signed big-endian numbers.
+ Signed = fun(Bin, List, SkipBef, N) ->
+ SkipAft = 8*size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N/signed,_I2:SkipAft>> = Bin,
+ case make_signed_int(List, N) of
+ Int -> ok;
+ Other ->
+ io:format("Bin = ~p,", [Bin]),
+ io:format("SkipBef = ~p, N = ~p", [SkipBef,N]),
+ io:format("Expected ~p, got ~p", [Int,Other]),
+ ?t:fail()
+ end
+ end,
+ ?line more_dynamic1(Signed, funny_binary(43)),
+
+ % Unsigned little-endian numbers.
+ UnsLittle = fun(Bin, List, SkipBef, N) ->
+ SkipAft = 8*size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N/little,_I2:SkipAft>> = Bin,
+ Int = make_int(big_to_little(List, N), N, 0)
+ end,
+ ?line more_dynamic1(UnsLittle, funny_binary(44)),
+
+ % Signed little-endian numbers.
+ SignLittle = fun(Bin, List, SkipBef, N) ->
+ SkipAft = 8*size(Bin) - N - SkipBef,
+ <<_I1:SkipBef,Int:N/signed-little,_I2:SkipAft>> = Bin,
+ Little = big_to_little(List, N),
+ Int = make_signed_int(Little, N)
+ end,
+ ?line more_dynamic1(SignLittle, funny_binary(45)),
+
+ ok.
+
+funny_binary(N) ->
+ B0 = erlang:md5([N]),
+ {B1,_B2} = split_binary(B0, size(B0) div 2),
+ B1.
+
+more_dynamic1(Action, Bin) ->
+ BitList = bits_to_list(binary_to_list(Bin), 16#80),
+ more_dynamic2(Action, Bin, BitList, 0).
+
+more_dynamic2(Action, Bin, [_|T]=List, Bef) ->
+ more_dynamic3(Action, Bin, List, Bef, size(Bin)*8),
+ more_dynamic2(Action, Bin, T, Bef+1);
+more_dynamic2(_Action, _Bin, [], _Bef) -> ok.
+
+more_dynamic3(Action, Bin, List, Bef, Aft) when Bef =< Aft ->
+%% io:format("~p, ~p", [Bef,Aft-Bef]),
+ Action(Bin, List, Bef, Aft-Bef),
+ more_dynamic3(Action, Bin, List, Bef, Aft-1);
+more_dynamic3(_, _, _, _, _) -> ok.
+
+big_to_little(List, N) -> big_to_little(List, N, []).
+
+big_to_little([B0,B1,B2,B3,B4,B5,B6,B7|T], N, Acc) when N >= 8 ->
+ big_to_little(T, N-8, [B0,B1,B2,B3,B4,B5,B6,B7|Acc]);
+big_to_little(List, N, Acc) -> lists:sublist(List, 1, N) ++ Acc.
+
+make_signed_int(_List, 0) -> 0;
+make_signed_int([0|_T]=List, N) -> make_int(List, N, 0);
+make_signed_int([1|_T]=List0, N) ->
+ List1 = reversed_sublist(List0, N, []),
+ List2 = two_complement_and_reverse(List1, 1, []),
+ -make_int(List2, length(List2), 0).
+
+reversed_sublist(_List, 0, Acc) -> Acc;
+reversed_sublist([H|T], N, Acc) -> reversed_sublist(T, N-1, [H|Acc]).
+
+two_complement_and_reverse([H|T], Carry, Acc) ->
+ Sum = 1-H+Carry,
+ two_complement_and_reverse(T, Sum div 2, [Sum rem 2|Acc]);
+two_complement_and_reverse([], Carry, Acc) -> [Carry|Acc].
+
+make_int(_List, 0, Acc) -> Acc;
+make_int([H|T], N, Acc) -> make_int(T, N-1, Acc bsl 1 bor H).
+
+bits_to_list([_H|T], 0) -> bits_to_list(T, 16#80);
+bits_to_list([H|_]=List, Mask) ->
+ [case H band Mask of
+ 0 -> 0;
+ _ -> 1
+ end|bits_to_list(List, Mask bsr 1)];
+bits_to_list([], _) -> [].
+
+fun_clause({'EXIT',{function_clause,_}}) -> ok.
+mkbin(L) when list(L) -> list_to_binary(L).
+
+mml(Config) when list(Config) ->
+ ?line single_byte_binary = mml_choose(<<42>>),
+ ?line multi_byte_binary = mml_choose(<<42,43>>).
+
+mml_choose(<<_A:8>>) -> single_byte_binary;
+mml_choose(<<_A:8, _T/binary>>) -> multi_byte_binary.
diff --git a/lib/debugger/test/bs_match_misc_SUITE.erl b/lib/debugger/test/bs_match_misc_SUITE.erl
new file mode 100644
index 0000000000..53d11ba179
--- /dev/null
+++ b/lib/debugger/test/bs_match_misc_SUITE.erl
@@ -0,0 +1,166 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(bs_match_misc_SUITE).
+
+-author('[email protected]').
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ bound_var/1,bound_tail/1,t_float/1,little_float/1,sean/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [bound_var, bound_tail, t_float, little_float, sean].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+bound_var(doc) -> "Test matching of bound variables.";
+bound_var(Config) when list(Config) ->
+ ?line ok = bound_var(42, 13, <<42,13>>),
+ ?line nope = bound_var(42, 13, <<42,255>>),
+ ?line nope = bound_var(42, 13, <<154,255>>),
+ ok.
+
+bound_var(A, B, <<A:8,B:8>>) -> ok;
+bound_var(_, _, _) -> nope.
+
+bound_tail(doc) -> "Test matching of a bound tail.";
+bound_tail(Config) when list(Config) ->
+ ?line ok = bound_tail(<<>>, <<13,14>>),
+ ?line ok = bound_tail(<<2,3>>, <<1,1,2,3>>),
+ ?line nope = bound_tail(<<2,3>>, <<1,1,2,7>>),
+ ?line nope = bound_tail(<<2,3>>, <<1,1,2,3,4>>),
+ ?line nope = bound_tail(<<2,3>>, <<>>),
+ ok.
+
+bound_tail(T, <<_:16,T/binary>>) -> ok;
+bound_tail(_, _) -> nope.
+
+t_float(Config) when list(Config) ->
+ F = f1(),
+ G = f_one(),
+
+ ?line G = match_float(<<63,128,0,0>>, 32, 0),
+ ?line G = match_float(<<63,240,0,0,0,0,0,0>>, 64, 0),
+
+ ?line fcmp(F, match_float(<<F:32/float>>, 32, 0)),
+ ?line fcmp(F, match_float(<<F:64/float>>, 64, 0)),
+ ?line fcmp(F, match_float(<<1:1,F:32/float,127:7>>, 32, 1)),
+ ?line fcmp(F, match_float(<<1:1,F:64/float,127:7>>, 64, 1)),
+ ?line fcmp(F, match_float(<<1:13,F:32/float,127:3>>, 32, 13)),
+ ?line fcmp(F, match_float(<<1:13,F:64/float,127:3>>, 64, 13)),
+ ok.
+
+
+fcmp(F1, F2) when (F1 - F2) / F2 < 0.0000001 -> ok.
+
+match_float(Bin0, Fsz, I) ->
+ Bin = make_sub_bin(Bin0),
+ Bsz = size(Bin) * 8,
+ Tsz = Bsz - Fsz - I,
+ <<_:I,F:Fsz/float,_:Tsz>> = Bin,
+ F.
+
+little_float(Config) when list(Config) ->
+ F = f2(),
+ G = f_one(),
+
+ ?line G = match_float_little(<<0,0,0,0,0,0,240,63>>, 64, 0),
+ ?line G = match_float_little(<<0,0,128,63>>, 32, 0),
+
+ ?line fcmp(F, match_float_little(<<F:32/float-little>>, 32, 0)),
+ ?line fcmp(F, match_float_little(<<F:64/float-little>>, 64, 0)),
+ ?line fcmp(F, match_float_little(<<1:1,F:32/float-little,127:7>>, 32, 1)),
+ ?line fcmp(F, match_float_little(<<1:1,F:64/float-little,127:7>>, 64, 1)),
+ ?line fcmp(F, match_float_little(<<1:13,F:32/float-little,127:3>>, 32, 13)),
+ ?line fcmp(F, match_float_little(<<1:13,F:64/float-little,127:3>>, 64, 13)),
+
+ ok.
+
+match_float_little(Bin0, Fsz, I) ->
+ Bin = make_sub_bin(Bin0),
+ Bsz = size(Bin) * 8,
+ Tsz = Bsz - Fsz - I,
+ <<_:I,F:Fsz/float-little,_:Tsz>> = Bin,
+ F.
+
+
+make_sub_bin(Bin0) ->
+ Sz = size(Bin0),
+ Bin1 = <<37,Bin0/binary,38,39>>,
+ <<_:8,Bin:Sz/binary,_:8,_:8>> = Bin1,
+ Bin.
+
+f1() ->
+ 3.1415.
+
+f2() ->
+ 2.7133.
+
+f_one() ->
+ 1.0.
+
+sean(Config) when list(Config) ->
+ ?line small = sean1(<<>>),
+ ?line small = sean1(<<1>>),
+ ?line small = sean1(<<1,2>>),
+ ?line small = sean1(<<1,2,3>>),
+ ?line large = sean1(<<1,2,3,4>>),
+
+ ?line small = sean1(<<4>>),
+ ?line small = sean1(<<4,5>>),
+ ?line small = sean1(<<4,5,6>>),
+ ?line {'EXIT',{function_clause,_}} = (catch sean1(<<4,5,6,7>>)),
+ ok.
+
+sean1(<<B/binary>>) when size(B) < 4 -> small;
+sean1(<<1, _B/binary>>) -> large.
diff --git a/lib/debugger/test/bs_match_tail_SUITE.erl b/lib/debugger/test/bs_match_tail_SUITE.erl
new file mode 100644
index 0000000000..961ccbb599
--- /dev/null
+++ b/lib/debugger/test/bs_match_tail_SUITE.erl
@@ -0,0 +1,120 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(bs_match_tail_SUITE).
+
+-author('[email protected]').
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ aligned/1,unaligned/1,zero_tail/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [aligned, unaligned, zero_tail].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+aligned(doc) -> "Test aligned tails.";
+aligned(Config) when list(Config) ->
+ ?line Tail1 = mkbin([]),
+ ?line {258,Tail1} = al_get_tail_used(mkbin([1,2])),
+ ?line Tail2 = mkbin(lists:seq(1, 127)),
+ ?line {35091,Tail2} = al_get_tail_used(mkbin([137,19|Tail2])),
+
+ ?line 64896 = al_get_tail_unused(mkbin([253,128])),
+ ?line 64895 = al_get_tail_unused(mkbin([253,127|lists:seq(42, 255)])),
+
+ ?line Tail3 = mkbin(lists:seq(0, 19)),
+ ?line {0,Tail1} = get_dyn_tail_used(Tail1, 0),
+ ?line {0,Tail3} = get_dyn_tail_used(mkbin([Tail3]), 0),
+ ?line {73,Tail3} = get_dyn_tail_used(mkbin([73|Tail3]), 8),
+
+ ?line 0 = get_dyn_tail_unused(mkbin([]), 0),
+ ?line 233 = get_dyn_tail_unused(mkbin([233]), 8),
+ ?line 23 = get_dyn_tail_unused(mkbin([23,22,2]), 8),
+ ok.
+
+al_get_tail_used(<<A:16,T/binary>>) -> {A,T}.
+al_get_tail_unused(<<A:16,_T/binary>>) -> A.
+
+unaligned(doc) -> "Test that an non-aligned tail cannot be matched out.";
+unaligned(Config) when list(Config) ->
+ ?line {'EXIT',{function_clause,_}} = (catch get_tail_used(mkbin([42]))),
+ ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_used(mkbin([137]), 3)),
+ ?line {'EXIT',{function_clause,_}} = (catch get_tail_unused(mkbin([42,33]))),
+ ?line {'EXIT',{{badmatch,_},_}} = (catch get_dyn_tail_unused(mkbin([44]), 7)),
+ ok.
+
+get_tail_used(<<A:1,T/binary>>) -> {A,T}.
+
+get_tail_unused(<<A:15,_/binary>>) -> A.
+
+get_dyn_tail_used(Bin, Sz) ->
+ <<A:Sz,T/binary>> = Bin,
+ {A,T}.
+
+get_dyn_tail_unused(Bin, Sz) ->
+ <<A:Sz,_T/binary>> = Bin,
+ A.
+
+zero_tail(doc) -> "Test that zero tails are tested correctly.";
+zero_tail(Config) when list(Config) ->
+ ?line 7 = (catch test_zero_tail(mkbin([7]))),
+ ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail(mkbin([1,2]))),
+ ?line {'EXIT',{function_clause,_}} = (catch test_zero_tail2(mkbin([1,2,3]))),
+ ok.
+
+test_zero_tail(<<A:8>>) -> A.
+
+test_zero_tail2(<<_A:4,_B:4>>) -> ok.
+
+mkbin(L) when list(L) -> list_to_binary(L).
diff --git a/lib/debugger/test/bs_utf_SUITE.erl b/lib/debugger/test/bs_utf_SUITE.erl
new file mode 100644
index 0000000000..7a1d3baaca
--- /dev/null
+++ b/lib/debugger/test/bs_utf_SUITE.erl
@@ -0,0 +1,305 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(bs_utf_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1,end_per_suite/1,
+ init_per_testcase/2,end_per_testcase/2,
+ utf8_roundtrip/1,unused_utf_char/1,utf16_roundtrip/1,
+ utf32_roundtrip/1,guard/1,extreme_tripping/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+-compile([no_jopt,time]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [utf8_roundtrip, unused_utf_char, utf16_roundtrip,
+ utf32_roundtrip, guard, extreme_tripping].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+utf8_roundtrip(Config) when is_list(Config) ->
+ ?line [utf8_roundtrip_1(P) || P <- utf_data()],
+ ok.
+
+utf8_roundtrip_1({Str,Bin,Bin}) ->
+ ?line Str = utf8_to_list(Bin),
+ ?line Bin = list_to_utf8(Str),
+ ?line [ok = utf8_guard(C, <<42,C/utf8>>) || C <- Str],
+ ?line [error = utf8_guard(C, <<C/utf8>>) || C <- Str],
+ ok.
+
+utf8_guard(C, Bin) when <<42,C/utf8>> =:= Bin -> ok;
+utf8_guard(_, _) -> error.
+
+utf8_to_list(<<C/utf8,T/binary>>) ->
+ [C|utf8_to_list(T)];
+utf8_to_list(<<>>) -> [].
+
+list_to_utf8(L) ->
+ list_to_utf8(L, <<>>).
+
+list_to_utf8([H|T], Bin) ->
+ list_to_utf8(T, <<Bin/binary,H/utf8>>);
+list_to_utf8([], Bin) -> Bin.
+
+unused_utf_char(Config) when is_list(Config) ->
+ [true = utf8_len(Utf8) =:= length(Str) ||
+ {Str,Utf8} <- utf_data()],
+ ok.
+
+utf8_len(B) ->
+ utf8_len(B, 0).
+
+utf8_len(<<_/utf8,T/binary>>, N) ->
+ utf8_len(T, N+1);
+utf8_len(<<>>, N) -> N.
+
+utf16_roundtrip(Config) when is_list(Config) ->
+ ?line {Str,Big,Big,Little,Little} = utf16_data(),
+ ?line 4 = utf16_big_len(Big),
+ ?line 4 = utf16_little_len(Little),
+ ?line Str = big_utf16_to_list(Big),
+ ?line Str = little_utf16_to_list(Little),
+
+ ?line Big = list_to_big_utf16(Str),
+ ?line Little = list_to_little_utf16(Str),
+
+ ok.
+
+utf16_big_len(B) ->
+ utf16_big_len(B, 0).
+
+utf16_big_len(<<_/utf16,T/binary>>, N) ->
+ utf16_big_len(T, N+1);
+utf16_big_len(<<>>, N) -> N.
+
+utf16_little_len(B) ->
+ utf16_little_len(B, 0).
+
+utf16_little_len(<<_/little-utf16,T/binary>>, N) ->
+ utf16_little_len(T, N+1);
+utf16_little_len(<<>>, N) -> N.
+
+list_to_big_utf16(List) ->
+ list_to_big_utf16(List, <<>>).
+
+list_to_big_utf16([H|T], Bin) ->
+ list_to_big_utf16(T, <<Bin/binary,H/utf16>>);
+list_to_big_utf16([], Bin) -> Bin.
+
+list_to_little_utf16(List) ->
+ list_to_little_utf16(List, <<>>).
+
+list_to_little_utf16([H|T], Bin) ->
+ list_to_little_utf16(T, <<Bin/binary,H/little-utf16>>);
+list_to_little_utf16([], Bin) -> Bin.
+
+big_utf16_to_list(<<H/utf16,T/binary>>) ->
+ [H|big_utf16_to_list(T)];
+big_utf16_to_list(<<>>) -> [].
+
+little_utf16_to_list(<<H/little-utf16,T/binary>>) ->
+ [H|little_utf16_to_list(T)];
+little_utf16_to_list(<<>>) -> [].
+
+utf32_roundtrip(Config) when is_list(Config) ->
+ ?line {Str,Big,Big,Little,Little} = utf32_data(),
+ ?line 4 = utf32_big_len(Big),
+ ?line 4 = utf32_little_len(Little),
+ ?line Str = big_utf32_to_list(Big),
+ ?line Str = little_utf32_to_list(Little),
+
+ ?line Big = list_to_big_utf32(Str),
+ ?line Little = list_to_little_utf32(Str),
+
+ ok.
+
+utf32_big_len(B) ->
+ utf32_big_len(B, 0).
+
+utf32_big_len(<<_/utf32,T/binary>>, N) ->
+ utf32_big_len(T, N+1);
+utf32_big_len(<<>>, N) -> N.
+
+utf32_little_len(B) ->
+ utf32_little_len(B, 0).
+
+utf32_little_len(<<_/little-utf32,T/binary>>, N) ->
+ utf32_little_len(T, N+1);
+utf32_little_len(<<>>, N) -> N.
+
+list_to_big_utf32(List) ->
+ list_to_big_utf32(List, <<>>).
+
+list_to_big_utf32([H|T], Bin) ->
+ list_to_big_utf32(T, <<Bin/binary,H/utf32>>);
+list_to_big_utf32([], Bin) -> Bin.
+
+list_to_little_utf32(List) ->
+ list_to_little_utf32(List, <<>>).
+
+list_to_little_utf32([H|T], Bin) ->
+ list_to_little_utf32(T, <<Bin/binary,H/little-utf32>>);
+list_to_little_utf32([], Bin) -> Bin.
+
+big_utf32_to_list(<<H/utf32,T/binary>>) ->
+ [H|big_utf32_to_list(T)];
+big_utf32_to_list(<<>>) -> [].
+
+little_utf32_to_list(<<H/little-utf32,T/binary>>) ->
+ [H|little_utf32_to_list(T)];
+little_utf32_to_list(<<>>) -> [].
+
+
+guard(Config) when is_list(Config) ->
+ ?line error = do_guard(16#D800),
+ ok.
+
+do_guard(C) when byte_size(<<C/utf8>>) =/= 42 -> ok;
+do_guard(C) when byte_size(<<C/utf16>>) =/= 42 -> ok;
+do_guard(C) when byte_size(<<C/utf32>>) =/= 42 -> ok;
+do_guard(_) -> error.
+
+%% The purpose of this test is to make sure that
+%% the delayed creation of sub-binaries works.
+
+extreme_tripping(Config) when is_list(Config) ->
+ ?line Unicode = lists:seq(0, 1024),
+ ?line Utf8 = unicode_to_utf8(Unicode, <<>>),
+ ?line Utf16 = utf8_to_utf16(Utf8, <<>>),
+ ?line Utf32 = utf8_to_utf32(Utf8, <<>>),
+ ?line Utf32 = utf16_to_utf32(Utf16, <<>>),
+ ?line Utf8 = utf32_to_utf8(Utf32, <<>>),
+ ?line Unicode = utf32_to_unicode(Utf32),
+ ok.
+
+unicode_to_utf8([C|T], Bin) ->
+ unicode_to_utf8(T, <<Bin/bytes,C/utf8>>);
+unicode_to_utf8([], Bin) -> Bin.
+
+utf8_to_utf16(<<C/utf8,T/binary>>, Bin) ->
+ utf8_to_utf16(T, <<Bin/bytes,C/utf16>>);
+utf8_to_utf16(<<>>, Bin) -> Bin.
+
+utf16_to_utf32(<<C/utf16,T/binary>>, Bin) ->
+ utf16_to_utf32(T, <<Bin/bytes,C/utf32>>);
+utf16_to_utf32(<<>>, Bin) -> Bin.
+
+utf8_to_utf32(<<C/utf8,T/binary>>, Bin) ->
+ utf8_to_utf32(T, <<Bin/bytes,C/utf32>>);
+utf8_to_utf32(<<>>, Bin) -> Bin.
+
+utf32_to_utf8(<<C/utf32,T/binary>>, Bin) ->
+ utf32_to_utf8(T, <<Bin/bytes,C/utf8>>);
+utf32_to_utf8(<<>>, Bin) -> Bin.
+
+utf32_to_unicode(<<C/utf32,T/binary>>) ->
+ [C|utf32_to_unicode(T)];
+utf32_to_unicode(<<>>) -> [].
+
+utf_data() ->
+%% From RFC-3629.
+
+ %% Give the compiler a change to do some constant propagation.
+ NotIdentical = 16#2262,
+
+ [
+ %% "A<NOT IDENTICAL TO><ALPHA>."
+ {[16#0041,NotIdentical,16#0391,16#002E],
+ <<16#0041/utf8,NotIdentical/utf8,16#0391/utf8,16#002E/utf8>>,
+ <<16#41,16#E2,16#89,16#A2,16#CE,16#91,16#2E>>},
+
+ %% Korean "hangugeo" (meaning "the Korean language")
+ {[16#D55C,16#AD6D,16#C5B4],
+ <<16#D55C/utf8,16#AD6D/utf8,16#C5B4/utf8>>,
+ <<16#ED,16#95,16#9C,16#EA,16#B5,16#AD,16#EC,16#96,16#B4>>},
+
+ %% Japanese "nihongo" (meaning "the Japanese language").
+ {[16#65E5,16#672C,16#8A9E],
+ <<16#65E5/utf8,16#672C/utf8,16#8A9E/utf8>>,
+ <<16#E6,16#97,16#A5,16#E6,16#9C,16#AC,16#E8,16#AA,16#9E>>}
+ ].
+
+utf16_data() ->
+ %% Example from RFC-2781. "*=Ra", where "*" represents a
+ %% hypothetical Ra hieroglyph (code point 16#12345).
+
+ %% Give the compiler a change to do some constant propagation.
+ RaHieroglyph = 16#12345,
+
+ %% First as a list of Unicode characters.
+ {[RaHieroglyph,16#3D,16#52,16#61],
+
+ %% Big endian (the two binaries should be equal).
+ <<RaHieroglyph/big-utf16,16#3D/big-utf16,16#52/big-utf16,16#61/big-utf16>>,
+ <<16#D8,16#08,16#DF,16#45,16#00,16#3D,16#00,16#52,16#00,16#61>>,
+
+ %% Little endian (the two binaries should be equal).
+ <<RaHieroglyph/little-utf16,16#3D/little-utf16,
+ 16#52/little-utf16,16#61/little-utf16>>,
+ <<16#08,16#D8,16#45,16#DF,16#3D,16#00,16#52,16#00,16#61,16#00>>}.
+
+utf32_data() ->
+ %% "A<NOT IDENTICAL TO><ALPHA>."
+ NotIdentical = 16#2262,
+ {[16#0041,NotIdentical,16#0391,16#002E],
+
+ %% Big endian.
+ <<16#0041/utf32,NotIdentical/utf32,16#0391/utf32,16#002E/utf32>>,
+ <<16#41:32,NotIdentical:32,16#0391:32,16#2E:32>>,
+
+ %% Little endian.
+ <<16#0041/little-utf32,NotIdentical/little-utf32,
+ 16#0391/little-utf32,16#002E/little-utf32>>,
+ <<16#41:32/little,NotIdentical:32/little,
+ 16#0391:32/little,16#2E:32/little>>}.
diff --git a/lib/debugger/test/bug_SUITE.erl b/lib/debugger/test/bug_SUITE.erl
new file mode 100644
index 0000000000..a831897dfb
--- /dev/null
+++ b/lib/debugger/test/bug_SUITE.erl
@@ -0,0 +1,95 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(bug_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+
+-export([otp2163/1, otp4845/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, ticket_tests}].
+
+groups() ->
+ [{ticket_tests, [], [otp2163, otp4845]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+otp2163(doc) -> ["BIF exit reason"];
+otp2163(suite) -> [];
+otp2163(Config) when list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+
+ %% First compile and get the expected results:
+
+ ?line FileName = filename:join(DataDir, "otp2163"),
+ ?line {module,otp2163} = code:load_abs(FileName),
+
+ ?line {'EXIT',{badarg,[ApplyRes|_]}} = (catch otp2163:apply_test()),
+ ?line {'EXIT',{badarg,[ListRes|_]}} = (catch otp2163:list_to_atom_test()),
+
+ %% Then interpret, and check if the results are OK.
+ ?line {module,otp2163} = int:i(FileName),
+
+ ?line ok = io:format("Expecting ~p", [ApplyRes]),
+ ?line {'EXIT',{badarg,[ApplyRes|_]}} = (catch otp2163:apply_test()),
+ ?line ok = io:format("Expecting ~p", [ListRes]),
+ ?line {'EXIT',{badarg,[ListRes|_]}} = (catch otp2163:list_to_atom_test()),
+ ok.
+
+
+otp4845(doc) -> ["BIF not loading and not bug compatible, OTP-4845 OTP-4859"];
+otp4845(suite) -> [];
+otp4845(Config) when list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+
+ %% First compile and get the expected results:
+
+ ?line FileName = filename:join(DataDir, "otp4845"),
+ ?line {module,otp4845} = code:load_abs(FileName),
+
+ ?line CompiledRes = (catch otp4845:test()),
+ ?line ok = io:format("Compiled ~p", [CompiledRes]),
+
+ %% Then interpret, and check if the results are OK.
+ ?line {module,otp4845} = int:i(FileName),
+
+ ?line IntRes = (catch otp4845:test()),
+ ?line ok = io:format("Interpreted ~p", [IntRes]),
+
+ ?line CompiledRes = IntRes,
+ ok.
diff --git a/lib/debugger/test/bug_SUITE_data/Makefile.src b/lib/debugger/test/bug_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..792b3299e1
--- /dev/null
+++ b/lib/debugger/test/bug_SUITE_data/Makefile.src
@@ -0,0 +1,25 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+all: otp2163.@EMULATOR@ otp4845.@EMULATOR@
+EFLAGS=+debug_info
+
+otp2163.@EMULATOR@: otp2163.erl
+ erlc $(EFLAGS) otp2163.erl
+otp4845.@EMULATOR@: otp4845.erl
+ erlc $(EFLAGS) otp4845.erl
diff --git a/lib/debugger/test/bug_SUITE_data/otp2163.erl b/lib/debugger/test/bug_SUITE_data/otp2163.erl
new file mode 100644
index 0000000000..4e3c487ef7
--- /dev/null
+++ b/lib/debugger/test/bug_SUITE_data/otp2163.erl
@@ -0,0 +1,60 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%% Purpose : Test OTP-2163
+
+-module(otp2163).
+
+
+-export([apply_test/0, list_to_atom_test/0, error/1]).
+-export([test/0]).
+
+apply_test() ->
+ M = {},
+ apply(M,dummy,[]).
+
+list_to_atom_test() ->
+ list_to_atom(id({1,2})).
+
+id(I) -> I.
+
+%% OTP-4845 OTP 4859
+-record(sune, {a,sd,g,s}).
+-record(error, {a,sd,g,s}).
+
+test() ->
+ sune = error(#sune{}),
+ {false,false} = error(false),
+ {true,true} = error(true),
+ error(#error{}).
+
+error(X) ->
+ if
+ is_record(X, sune) ->
+ sune;
+ X ->
+ {true, X};
+ not X ->
+ {false, X};
+ not is_record(X, error) ->
+ error;
+ true ->
+ ok
+ end.
diff --git a/lib/debugger/test/bug_SUITE_data/otp4845.erl b/lib/debugger/test/bug_SUITE_data/otp4845.erl
new file mode 100644
index 0000000000..18ca08b977
--- /dev/null
+++ b/lib/debugger/test/bug_SUITE_data/otp4845.erl
@@ -0,0 +1,52 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%% Purpose : Test OTP-4845
+
+-module(otp4845).
+
+
+%%-export([error/1]).
+-export([test/0]).
+
+%% OTP-4845 OTP 4859
+-record(sune, {a,sd,g,s}).
+-record(error, {a,sd,g,s}).
+
+test() ->
+ R1 = error(#sune{}),
+ R2 = error(false),
+ R3 = error(true),
+ R4 = error(#error{}),
+ {R1,R2,R3,R4}.
+
+error(X) ->
+ if
+ is_record(X, sune) ->
+ sune;
+ X ->
+ {true, X};
+ not X ->
+ {false, X};
+ not is_record(X, error) ->
+ error;
+ true ->
+ ok
+ end.
diff --git a/lib/debugger/test/cleanup.erl b/lib/debugger/test/cleanup.erl
new file mode 100644
index 0000000000..5f1ea71d2e
--- /dev/null
+++ b/lib/debugger/test/cleanup.erl
@@ -0,0 +1,56 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(cleanup).
+
+-export([all/0,groups/0,init_per_group/2,end_per_group/2, cleanup/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+all() ->
+[cleanup].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cleanup(suite) -> [];
+cleanup(_) ->
+ ?line Mods = int:interpreted(),
+ ?line ok = int:n(Mods),
+ case whereis(interpret) of
+ undefined ->
+ ok;
+ Pid ->
+ exit(Pid, kill)
+ end,
+ case whereis(int_db) of
+ undefined ->
+ ok;
+ Pid2 ->
+ exit(Pid2, kill)
+ end,
+ ok.
diff --git a/lib/debugger/test/dbg_ui_SUITE.erl b/lib/debugger/test/dbg_ui_SUITE.erl
new file mode 100644
index 0000000000..86156ebbf5
--- /dev/null
+++ b/lib/debugger/test/dbg_ui_SUITE.erl
@@ -0,0 +1,269 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(dbg_ui_SUITE).
+
+
+-include_lib("test_server/include/test_server.hrl").
+
+
+% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+
+% Test cases must be exported.
+-export ([dbg_ui/1]).
+
+% Manual test suites/cases exports
+-export([start1/1, interpret1/1, quit1/1,
+ start2/1, interpret2/1, break2/1, options2/1, quit2/1,
+ interpret3/1, all_step3/1,all_next3/1,save3/1,restore3/1,finish3/1,
+ killinit3/1, killone3/1, killall3/1, deleteone3/1, deleteall3/1,
+ viewbreak4/1, delete4/1,
+ attach5/1, normal5/1, exit5/1, options5/1,
+ distsetup6/1, all_step6/1, all_next6/1]).
+
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+init_per_testcase(_Func, Config) ->
+ Dog=test_server:timetrap(60*1000),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Func, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog).
+
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [dbg_ui, {group, manual_tests}].
+
+groups() ->
+ [{manual_tests, [],
+ [start1, interpret1, quit1, start2, interpret2, break2,
+ options2, interpret3, all_step3, all_next3, save3,
+ restore3, finish3, killinit3, killone3, killall3,
+ deleteone3, deleteall3, viewbreak4, delete4, attach5,
+ normal5, exit5, options5, distsetup6, all_step6,
+ all_next6]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+dbg_ui (doc) ->
+ ["Debugger GUI"];
+
+dbg_ui (suite) ->
+ [];
+
+dbg_ui (_Config) ->
+ case os:getenv("DISPLAY") of
+ false ->
+ {skipped,"No display"};
+ Other when is_list(Other) ->
+% ?line {ok, Pid} = debugger:start (),
+% ?line ok = is_pid (Pid),
+% ?line true = erlang:is_process_alive(Pid),
+% ?line ok = debugger:stop(),
+% ?line false = erlang:is_process_alive(Pid)
+ {skipped,"Gunilla: Workaround"}
+ end.
+
+%% check/2 - returns the result for the specified testcase.
+%% pass - means the user has run the case, and it passed
+%% fail - means the user has run the case, and it failed
+%% unknown - means the user has (tried to) run the case, and the result is unclear.
+%% skip - means the user has not yet run the case.
+
+check(Case, Config) ->
+
+ ?line DataDir = ?config(data_dir, Config),
+ ?line ResultFileName = filename:join([DataDir, "manual_results.erl"]),
+ case file:consult(ResultFileName) of
+ {ok, Results} ->
+ ?line io:format("Results: ~p~n",[Results]),
+ case Results of
+ [] ->
+ no_result;
+ %% Incomplete "sanity" check of file contents.
+ [{_Key,_Value}|_Rest] ->
+ case lists:keysearch(Case, 1, Results) of
+ {value, {Case, Value}} ->
+ Value; % pass, fail, unknown
+ false ->
+ no_result; % skip
+ _Otherwise ->
+ {error, "Contents of results file not valid"}
+ end;
+ _Otherwise2 ->
+ {error, "Contents of results file not valid"}
+ end;
+ _Otherwise3 ->
+ {error, "Problems reading results file"}
+ end.
+
+
+
+
+
+
+-define(MAN_CASE(Name,Doc, Description),
+ Name(doc) -> [Doc];
+ Name(suite) -> [];
+ Name(Config) ->
+ ?line io:format("Checking ~p~n",[Name]),
+ ?line io:format("Config = ~p~n",[Config]),
+ case check(Name, Config) of
+ pass ->
+ ?line ok;
+ fail ->
+ ?line test_server:fail("Manual test failed");
+ unknown ->
+ ?line {skipped, "Manual test result unknown"};
+
+ no_result ->
+ ?line {skipped, Description};
+
+ {error, _Reason} ->
+%% Text = lists:flatten(
+%% io_lib:format("[File problem: ~s]~s",
+%% [Reason,Description])),
+ ?line {skipped, Description}
+ end
+ ).
+
+
+%% SET 1
+?MAN_CASE(start1, "Start the debugger from the toolbar",
+ "Before proceeding with the test cases, please move or remove
+the directory .erlang_tools/debugger in your home directory. Next,
+please start the debugger from the toolbar").
+
+?MAN_CASE(interpret1, "Interpreting modules",
+ "In this test case and all of the ones following, the source code
+files to use can be found in the test data directory for this debugger test
+ suite (probably in
+/clearcase/otp/tools/debugger/test/dbg_ui_SUITE_data/manual_data/src ).
+Interpret one module").
+
+?MAN_CASE(quit1, "Quit the debugger",
+"Quit the debugger using File->Exit in the main window").
+
+
+%% SET 2
+?MAN_CASE(start2, "Start the debugger from the shell",
+"Start the debugger from the shell. Use debugger:start()").
+
+?MAN_CASE(interpret2, "Interpret all modules",
+"Interpret all modules").
+
+?MAN_CASE(break2, "Set break points",
+"Set break points").
+
+?MAN_CASE(options2, "Set options to attach on break",
+"Set options to attach on break").
+
+?MAN_CASE(quit2, "Quit the debugger",
+"Quit the debugger using the close box in the main window title frame").
+
+
+%% SET3
+?MAN_CASE(interpret3, "Test attach options",
+"Start the debugger and interpret the modules [test, lists1, ordsets1]. Close the Interpret dialog. Set Attach on First Call and Attach on Break.").
+
+?MAN_CASE(all_step3, "Click Step through all evaluation",
+"In the shell, call test:test1(). Use the Step button, the Process->Step menu item and the ctrl-s shortcut to step through the *entire* execution of the call. (Approx 36 steps). Then close the Attach window. The result printed in the shell should be: {\"peter\",[1,2,4,a,b,c],\"olin\"}").
+
+?MAN_CASE(all_next3,"Click Next through all evaluation",
+"Again call test:test1() in the shell. This time Use the Next button, the Process->Next menu and the ctrl-n shortcut to quickly step over the execution of the four lines in the test1-function. The result printed in the shell should be: {\"peter\",[1,2,4,a,b,c],\"olin\"}").
+
+?MAN_CASE(save3, "Save the debugger state",
+"Use File->Save Settings to save the debugger state with the name 'three.state'").
+
+?MAN_CASE(restore3,"Quit the debugger, restart and restore the state",
+"Quit the debugger. Start it again. Use File->Load Settings to restore the state saved in 'three.state'. Check that the Attach-options are the same as what you set them to in the interpret3 test case. Check that the three modules [test,lists1,ordsets1] are interpreted.").
+
+
+?MAN_CASE(finish3, "Finish the current function body",
+"Call the fucntion test:test1() from the shell. Press Finish to evaluate the remaining lines in the function. The result printed in the shell should be: {\"peter\",[1,2,4,a,b,c],\"olin\"}").
+
+?MAN_CASE(killinit3,"Set up for killing and clearing processes",
+"Call test:test2() from the shell. Set a break point at the last line of test:test2. Click Continue. This should open three new attach windows. One for each spawn called in test:test2/0. ").
+
+?MAN_CASE(killone3, "Kill a process and clear it",
+"In one of the newly openend Attach windows: select Process->Kill. A message should appear above the Code Area in the Attach window. Use Windows->Monitor to verify that the Monitor window also shows that the process has been killed. In the Monitor window: select Edit->Clear. This should do two things: 1) close/remove the window of the killed process. 2) Remove the entry of the killed process from the monitor window.").
+
+?MAN_CASE(killall3,"KIll all processes, and clear them",
+"In the Monitor window: Select Edit->Kill All. Verify that all processes have been killed (in their respective windows and in the monitor window). Windows will be raised as their processes die. Next select, Edit->Clear. All attach windows should now be closed. Their entris should also disappear from the monitor window. The shell should have reported: ** exited: killed **").
+
+?MAN_CASE(deleteone3,"Delete/uniterpret one module",
+"In the Monitor window: Select Module->test->Delete. This should remove the breakpoints set in the test module, and the test module should disappear from the Module menu.").
+
+?MAN_CASE(deleteall3,"Delete/uniterpret all modules",
+"In the Monitor window: Select Module->Delete All Modules. This should remove all modules from the Module menu. ").
+
+%% SET 4
+
+
+
+?MAN_CASE(viewbreak4, "Test the View window",
+"Restore the settings from the three.state file again. In the Monitor window: Use Module->test->View to view the source code of the test module. In the View window, select Break->Line Break and set a break at line 53. Check that it appears in the View window and in the Monitor Window Break-menu. Also in the View window, select Break->Function Break and set a break at function test:test4. Check that the break (at line 59) appears in the View Window and in the Monitor Window Break-menu.").
+
+?MAN_CASE(delete4, "Remove breaks",
+"Use the Break->Delete All function in the View window to remove all breaks in the test module. Check that they are all removed. Close the View window.").
+
+%% SET 5
+
+?MAN_CASE(attach5,"Set attach options",
+"Set the attach options to only attach on exit").
+
+?MAN_CASE(normal5, "Test normal exit",
+"Call test:test12(normal) in the shell. This should return the atom 'done', and no windows should be opened.").
+
+?MAN_CASE(exit5, "Test abnormal exit",
+"Call test:test12(crash) in the shell. This should give the error message ** exited: crash **, and an attach window should be opened highlighting the last line in the test12-function.").
+
+?MAN_CASE(options5, "Experiment with the frames in the attach window",
+"Try all possible configurations of the [Button, Evaluator, Bindings, Trace] Frames in the attach window and see that the expected frames are shown/hidden.").
+
+
+%% SET 6 (Distribution)
+
+?MAN_CASE(distsetup6,"Set up distribution",
+"Start two erlang systems [foo,bar] (with option -sname), make them aware of eachother using net_adm:ping/1. Start the debugger on foo. Interpret the modules [test, lists1, ordsets1]. Set attach on First call. ").
+
+
+
+
+?MAN_CASE(all_step6, "Click Step through all evaluation",
+"In the bar shell, call test:test1().This should open an attach window. Use the Step button, the Process->Step menu item and the ctrl-s shortcut to step through the *entire* execution of the call. (Approx 36 steps). Then close the Attach window. The result printed in the bar shell should be: {\"peter\",[1,2,4,a,b,c],\"olin\"}").
+
+?MAN_CASE(all_next6,"Click Next through all evaluation",
+"Again, in the bar shell, call test:test1(). This time Use the Next button, the Process->Next menu and the ctrl-n shortcut to quickly step over the execution of the four lines in the test1-function. The result printed in the shell should be: {\"peter\",[1,2,4,a,b,c],\"olin\"}").
diff --git a/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/lists1.erl b/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/lists1.erl
new file mode 100644
index 0000000000..0214983c11
--- /dev/null
+++ b/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/lists1.erl
@@ -0,0 +1,469 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Purpose : Basic lists processing functions.
+
+-module(lists1).
+
+
+-export([member/2, append/2, append/1, subtract/2, reverse/1, reverse/2,
+ nth/2, nthtail/2, prefix/2, suffix/2, last/1,
+ seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
+ delete/2, sort/1, merge/2, concat/1,
+ flatten/1, flatten/2, flat_length/1, flatlength/1,
+ keymember/3, keysearch/3, keydelete/3, keyreplace/4,
+ keysort/2, keymerge/3, keymap/3, keymap/4]).
+
+-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,zf/2,
+ mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2]).
+-export([all/3,any/3,map/3,flatmap/3,foldl/4,foldr/4,filter/3,zf/3,
+ mapfoldl/4,mapfoldr/4,foreach/3]).
+
+%% member(X, L) -> (true | false)
+%% test if X is a member of the list L
+
+member(X, [X|_]) -> true;
+member(X, [_|Y]) ->
+ member(X, Y);
+member(X, []) -> false.
+
+%% append(X, Y) appends lists X and Y
+
+append(L1, L2) -> L1 ++ L2.
+
+%% append(L) appends the list of lists L
+
+append([E]) -> E;
+append([H|T]) -> H ++ append(T);
+append([]) -> [].
+
+%% subtract(List1, List2) subtract elements in List2 form List1.
+
+subtract(L1, L2) -> L1 -- L2.
+
+%% reverse(L) reverse all elements in the list L
+
+reverse(X) -> reverse(X, []).
+
+reverse([H|T], Y) ->
+ reverse(T, [H|Y]);
+reverse([], X) -> X.
+
+%% nth(N, L) returns the N`th element of the list L
+%% nthtail(N, L) returns the N`th tail of the list L
+
+nth(1, [H|T]) -> H;
+nth(N, [_|T]) when N > 1 ->
+ nth(N - 1, T).
+
+nthtail(1, [H|T]) -> T;
+nthtail(N, [H|T]) when N > 1 ->
+ nthtail(N - 1, T);
+nthtail(0, L) when list(L) -> L.
+
+%% prefix(Prefix, List) -> (true | false)
+
+prefix([X|PreTail], [X|Tail]) ->
+ prefix(PreTail, Tail);
+prefix([], List) -> true;
+prefix(_,_) -> false.
+
+
+%% suffix(Suffix, List) -> (true | false)
+
+suffix(Suffix, Suffix) -> true;
+suffix(Suffix, [_|Tail]) ->
+ suffix(Suffix, Tail);
+suffix(Suffix, []) -> false.
+
+%% last(List) returns the last element in a list.
+
+last([E]) -> E;
+last([E|Es]) ->
+ last(Es).
+
+%% seq(Min, Max) -> [Min,Min+1, ..., Max]
+%% seq(Min, Max, Incr) -> [Min,Min+Incr, ..., Max]
+%% returns the sequence Min..Max
+%% Min <= Max and Min and Max must be integers
+
+seq(Min, Max) when integer(Min), integer(Max), Min =< Max ->
+ seq(Min, Max, 1, []).
+
+seq(Min, Max, Incr) ->
+ seq(Min, Min + ((Max-Min) div Incr) * Incr, Incr, []).
+
+seq(Min, Min, I, L) -> [Min|L];
+seq(Min, Max, I, L) -> seq(Min, Max-I, I, [Max|L]).
+
+%% sum(L) suns the sum of the elements in L
+
+sum(L) -> sum(L, 0).
+sum([H|T], Sum) -> sum(T, Sum + H);
+sum([], Sum) -> Sum.
+
+%% duplicate(N, X) -> [X,X,X,.....,X] (N times)
+%% return N copies of X
+
+duplicate(N, X) when integer(N), N >= 0 -> duplicate(N, X, []).
+
+duplicate(0, _, L) -> L;
+duplicate(N, X, L) -> duplicate(N-1, X, [X|L]).
+
+
+%% min(L) -> returns the minimum element of the list L
+
+min([H|T]) -> min(T, H).
+
+min([H|T], Min) when H < Min -> min(T, H);
+min([_|T], Min) -> min(T, Min);
+min([], Min) -> Min.
+
+%% max(L) -> returns the maximum element of the list L
+
+max([H|T]) -> max(T, H).
+
+max([H|T], Max) when H > Max -> max(T, H);
+max([_|T], Max) -> max(T, Max);
+max([], Max) -> Max.
+
+%% sublist(List, Start, Length)
+%% Returns the sub-list starting at Start of length Length.
+
+sublist(List, S, L) when L >= 0 ->
+ sublist(nthtail(S-1, List), L).
+
+sublist([H|T], L) when L > 0 ->
+ [H|sublist(T, L-1)];
+sublist(List, L) -> [].
+
+%% delete(Item, List) -> List'
+%% Delete the first occurance of Item from the list L.
+
+delete(Item, [Item|Rest]) -> Rest;
+delete(Item, [H|Rest]) ->
+ [H|delete(Item, Rest)];
+delete(Item, []) -> [].
+
+%% sort(L) -> sorts the list L
+
+sort([X]) -> [X];
+sort([]) -> [];
+sort(X) -> split_and_sort(X, [], []).
+
+split_and_sort([A,B|T], X, Y) ->
+ split_and_sort(T, [A|X], [B|Y]);
+split_and_sort([H], X, Y) ->
+ split_and_sort([], [H|X], Y);
+split_and_sort([], X, Y) ->
+ merge(sort(X), sort(Y), []).
+
+%% merge(X, Y) -> L
+%% merges two sorted lists X and Y
+
+merge(X, Y) -> merge(X, Y, []).
+
+merge([H1|T1], [H2|T2], L) when H1 < H2 ->
+ merge(T1, [H2|T2], [H1|L]);
+merge(T1, [H2|T2], L) ->
+ merge(T1, T2, [H2|L]);
+merge([H|T], T2, L) ->
+ merge(T, T2, [H|L]);
+merge([], [], L) ->
+ reverse(L).
+
+%% concat(L) concatinate the list representation of the elements
+%% in L - the elements in L can be atoms, integers of strings.
+%% Returns a list of characters.
+
+concat(List) ->
+ flatmap(fun thing_to_list/1, List).
+
+thing_to_list(X) when integer(X) -> integer_to_list(X);
+thing_to_list(X) when float(X) -> float_to_list(X);
+thing_to_list(X) when atom(X) -> atom_to_list(X);
+thing_to_list(X) when list(X) -> X. %Assumed to be a string
+
+%% flatten(List)
+%% flatten(List, Tail)
+%% Flatten a list, adding optional tail.
+
+flatten(List) ->
+ flatten(List, [], []).
+
+flatten(List, Tail) ->
+ flatten(List, [], Tail).
+
+flatten([H|T], Cont, Tail) when list(H) ->
+ flatten(H, [T|Cont], Tail);
+flatten([H|T], Cont, Tail) ->
+ [H|flatten(T, Cont, Tail)];
+flatten([], [H|Cont], Tail) ->
+ flatten(H, Cont, Tail);
+flatten([], [], Tail) ->
+ Tail.
+
+%% flat_length(List) (undocumented can be rmove later)
+%% Calculate the length of a list of lists.
+
+flat_length(List) -> flatlength(List).
+
+%% flatlength(List)
+%% Calculate the length of a list of lists.
+
+flatlength(List) ->
+ flatlength(List, 0).
+
+flatlength([H|T], L) when list(H) ->
+ flatlength(H, flatlength(T, L));
+flatlength([H|T], L) ->
+ flatlength(T, L + 1);
+flatlength([], L) -> L.
+
+%% keymember(Key, Index, [Tuple])
+%% keysearch(Key, Index, [Tuple])
+%% keydelete(Key, Index, [Tuple])
+%% keyreplace(Key, Index, [Tuple], NewTuple)
+%% keysort(Index, [Tuple])
+%% keymerge(Index, [Tuple], [Tuple])
+%% keymap(Function, Index, [Tuple])
+%% keymap(Function, ExtraArgs, Index, [Tuple])
+
+keymember(Key, N, [T|Ts]) when element(N, T) == Key -> true;
+keymember(Key, N, [T|Ts]) ->
+ keymember(Key, N, Ts);
+keymember(Key, N, []) -> false.
+
+keysearch(Key, N, [H|T]) when element(N, H) == Key ->
+ {value, H};
+keysearch(Key, N, [H|T]) ->
+ keysearch(Key, N, T);
+keysearch(Key, N, []) -> false.
+
+keydelete(Key, N, [H|T]) when element(N, H) == Key -> T;
+keydelete(Key, N, [H|T]) ->
+ [H|keydelete(Key, N, T)];
+keydelete(Key, N, []) -> [].
+
+keyreplace(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
+ [New|Tail];
+keyreplace(Key, Pos, [H|T], New) ->
+ [H|keyreplace(Key, Pos, T, New)];
+keyreplace(Key, Pos, [], New) -> [].
+
+keysort(Index, [X]) -> [X];
+keysort(Index, []) -> [];
+keysort(Index, X) -> split_and_keysort(X, [], [], Index).
+
+split_and_keysort([A,B|T], X, Y, Index) ->
+ split_and_keysort(T, [A|X], [B|Y], Index);
+split_and_keysort([H], X, Y, Index) ->
+ split_and_keysort([], [H|X], Y, Index);
+split_and_keysort([], X, Y, Index) ->
+ keymerge(Index, keysort(Index, X), keysort(Index, Y), []).
+
+keymerge(Index, X, Y) -> keymerge(Index, X, Y, []).
+
+keymerge(I, [H1|T1], [H2|T2], L) when element(I, H1) < element(I, H2) ->
+ keymerge(I, T1, [H2|T2], [H1|L]);
+keymerge(Index, T1, [H2|T2], L) ->
+ keymerge(Index,T1, T2, [H2|L]);
+keymerge(Index,[H|T], T2, L) ->
+ keymerge(Index,T, T2, [H|L]);
+keymerge(Index, [], [], L) ->
+ reverse(L).
+
+keymap(Fun, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)];
+keymap( _, _ , []) -> [].
+
+keymap(Fun, ExtraArgs, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, apply(Fun, [element(Index, Tup)|ExtraArgs]))|
+ keymap(Fun, ExtraArgs, Index, Tail)];
+keymap( _, _ , _, []) -> [].
+
+%% all(Predicate, List)
+%% any(Predicate, List)
+%% map(Function, List)
+%% flatmap(Function, List)
+%% foldl(Function, First, List)
+%% foldr(Function, Last, List)
+%% filter(Predicate, List)
+%% zf(Function, List)
+%% mapfoldl(Function, First, List)
+%% mapfoldr(Function, Last, List)
+%% foreach(Function, List)
+%% takewhile(Predicate, List)
+%% dropwhile(Predicate, List)
+%% splitwith(Predicate, List)
+%% for list programming. Function here is either a 'fun' or a tuple
+%% {Module,Name} and we use apply/2 to evaluate. The name zf is a joke!
+%%
+%% N.B. Unless where the functions actually needs it only foreach/2/3,
+%% which is meant to be used for its side effects, has a defined order
+%% of evaluation.
+%%
+%% There are also versions with an extra argument, ExtraArgs, which is a
+%% list of extra arguments to each call.
+
+all(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> all(Pred, Tail);
+ false -> false
+ end;
+all(Pred, []) -> true.
+
+any(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any(Pred, Tail)
+ end;
+any(Pred, []) -> false.
+
+map(F, List) -> [ F(E) || E <- List ].
+
+flatmap(F, [Hd|Tail]) ->
+ F(Hd) ++ flatmap(F, Tail);
+flatmap(F, []) -> [].
+
+foldl(F, Accu, [Hd|Tail]) ->
+ foldl(F, F(Hd, Accu), Tail);
+foldl(F, Accu, []) -> Accu.
+
+foldr(F, Accu, [Hd|Tail]) ->
+ F(Hd, foldr(F, Accu, Tail));
+foldr(F, Accu, []) -> Accu.
+
+filter(Pred, List) -> [ E || E <- List, Pred(E) ].
+
+zf(F, [Hd|Tail]) ->
+ case F(Hd) of
+ true ->
+ [Hd|zf(F, Tail)];
+ {true,Val} ->
+ [Val|zf(F, Tail)];
+ false ->
+ zf(F, Tail)
+ end;
+zf(F, []) -> [].
+
+foreach(F, [Hd|Tail]) ->
+ F(Hd),
+ foreach(F, Tail);
+foreach(F, []) -> ok.
+
+mapfoldl(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = mapfoldl(F, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl(F, Accu, []) -> {[],Accu}.
+
+mapfoldr(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {[R|Rs],Accu2};
+mapfoldr(F, Accu, []) -> {[],Accu}.
+
+takewhile(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> [Hd|takewhile(Pred, Tail)];
+ false -> []
+ end;
+takewhile(Pred, []) -> [].
+
+dropwhile(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> dropwhile(Pred, Tail);
+ false -> [Hd|Tail]
+ end;
+dropwhile(Pred, []) -> [].
+
+splitwith(Pred, List) -> splitwith(Pred, List, []).
+
+splitwith(Pred, [Hd|Tail], Taken) ->
+ case Pred(Hd) of
+ true -> splitwith(Pred, Tail, [Hd|Taken]);
+ false -> {reverse(Taken), [Hd|Tail]}
+ end;
+splitwith(Pred, [], Taken) -> {reverse(Taken),[]}.
+
+%% Versions of the above functions with extra arguments.
+
+all(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> all(Pred, Eas, Tail);
+ false -> false
+ end;
+all(Pred, Eas, []) -> true.
+
+any(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> true;
+ false -> any(Pred, Eas, Tail)
+ end;
+any(Pred, Eas, []) -> false.
+
+map(F, Eas, List) -> [ apply(F, [E|Eas]) || E <- List ].
+
+flatmap(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]) ++ flatmap(F, Eas, Tail);
+flatmap(F, Eas, []) -> [].
+
+foldl(F, Eas, Accu, [Hd|Tail]) ->
+ foldl(F, Eas, apply(F, [Hd,Accu|Eas]), Tail);
+foldl(F, Eas, Accu, []) -> Accu.
+
+foldr(F, Eas, Accu, [Hd|Tail]) ->
+ apply(F, [Hd,foldr(F, Eas, Accu, Tail)|Eas]);
+foldr(F, Eas, Accu, []) ->
+ Accu.
+
+filter(Pred, Eas, List) -> [ E || E <- List, apply(Pred, [E|Eas]) ].
+
+zf(F, Eas, [Hd|Tail]) ->
+ case apply(F, [Hd|Eas]) of
+ true ->
+ [Hd|zf(F, Eas, Tail)];
+ {true,Val} ->
+ [Val|zf(F, Eas, Tail)];
+ false ->
+ zf(F, Eas, Tail)
+ end;
+zf(F, Eas, []) -> [].
+
+foreach(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]),
+ foreach(F, Eas, Tail);
+foreach(F, Eas, []) -> ok.
+
+mapfoldl(F, Eas, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = apply(F, [Hd,Accu0|Eas]),
+ {Rs,Accu2} = mapfoldl(F, Eas, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl(F, Eas, Accu, []) -> {[],Accu}.
+
+mapfoldr(F, Eas, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr(F, Eas, Accu0, Tail),
+ {R,Accu2} = apply(F, [Hd,Accu1|Eas]),
+ {[R|Rs],Accu2};
+mapfoldr(F, Eas, Accu, []) -> {[],Accu}.
+
+%% takewhile/2, dropwhile/2 and splitwith/2 do not have versions with
+%% extra arguments as this going to be discontinued.
diff --git a/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/ordsets1.erl b/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/ordsets1.erl
new file mode 100644
index 0000000000..ea72150bca
--- /dev/null
+++ b/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/ordsets1.erl
@@ -0,0 +1,188 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Purpose : Functions for manipulating sets as ordered lists.
+
+%% As yet some of these are not very efficiently written.
+
+-module(ordsets1).
+
+-export([new_set/0,is_set/1,set_to_list/1,list_to_set/1]).
+-export([is_element/2,add_element/2,del_element/2]).
+-export([union/2,union/1,intersection/2,intersection/1]).
+-export([subtract/2,subset/2]).
+
+%% new_set()
+%% Return a new empty ordered set.
+
+new_set() ->
+ [].
+
+%% is_set(Set)
+%% Return 'true' if Set is an ordered set of elements, else 'false'.
+
+is_set([E|Es]) ->
+ is_set(Es, E);
+is_set([]) ->
+ true.
+
+is_set([E2|Es], E1) when E1 < E2 ->
+ is_set(Es, E2);
+is_set([E2|Es], E1) ->
+ false;
+is_set([], E1) ->
+ true.
+
+%% set_to_list(OrdSet)
+%% Return the elements in OrdSet as a list.
+
+set_to_list(S) ->
+ S.
+
+%% list_to_set(List)
+%% Build an ordered set from the elements in List.
+
+list_to_set([E|Es]) ->
+ add_element(E, list_to_set(Es));
+list_to_set([]) ->
+ [].
+
+%% is_element(Element, OrdSet)
+%% Return 'true' if Element is an element of OrdSet, else 'false'.
+
+is_element(E, [H|Es]) when E < H ->
+ false;
+is_element(E, [H|Es]) when E == H ->
+ true;
+is_element(E, [H|Es]) when E > H ->
+ is_element(E, Es);
+is_element(E, []) ->
+ false.
+
+%% add_element(Element, OrdSet)
+%% Return OrdSet with Element inserted in it.
+
+add_element(E, [H|Es]) when E < H ->
+ [E,H|Es];
+add_element(E, [H|Es]) when E == H ->
+ [H|Es];
+add_element(E, [H|Es]) when E > H ->
+ [H|add_element(E, Es)];
+add_element(E, []) ->
+ [E].
+
+%% del_element(Element, OrdSet)
+%% Return OrdSet but with Element removed.
+
+del_element(E, [H|Es]) when E < H ->
+ [H|Es];
+del_element(E, [H|Es]) when E == H ->
+ Es;
+del_element(E, [H|Es]) when E > H ->
+ [H|del_element(E, Es)];
+del_element(E, []) ->
+ [].
+
+%% union(Set1, Set2)
+%% Return the union of Set1 and Set2.
+
+union([H1|Es1], [H2|Es2]) when H1 < H2 ->
+ [H1|union(Es1, [H2|Es2])];
+union([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ [H1|union(Es1, Es2)];
+union([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ [H2|union([H1|Es1], Es2)];
+union([], Es2) ->
+ Es2;
+union(Es1, []) ->
+ Es1.
+
+%% union(OrdSets)
+%% Return the union of the list of sets.
+
+union([S1,S2|Ss]) ->
+ union1(union(S1,S2), Ss);
+union([S]) ->
+ S;
+union([]) ->
+ [].
+
+union1(S1, [S2|Ss]) ->
+ union1(union(S1, S2), Ss);
+union1(S1, []) ->
+ S1.
+
+%% intersection(Set1, Set2)
+%% Return the intersection of Set1 and Set2.
+
+intersection([H1|Es1], [H2|Es2]) when H1 < H2 ->
+ intersection(Es1, [H2|Es2]);
+intersection([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ [H1|intersection(Es1, Es2)];
+intersection([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ intersection([H1|Es1], Es2);
+intersection([], Es2) ->
+ [];
+intersection(Es1, []) ->
+ [].
+
+%% intersection(OrdSets)
+%% Return the intersection of the list of sets.
+
+intersection([S1,S2|Ss]) ->
+ intersection1(intersection(S1,S2), Ss);
+intersection([S]) ->
+ S;
+intersection([]) ->
+ [].
+
+intersection1(S1, [S2|Ss]) ->
+ intersection1(intersection(S1, S2), Ss);
+intersection1(S1, []) ->
+ S1.
+
+%% subtract(Set1, Set2)
+%% Return all and only the elements of Set1 which are not also in Set2.
+
+subtract([H1|Es1], [H2|Es2]) when H1 < H2 ->
+ [H1|subtract(Es1, [H2|Es2])];
+subtract([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ subtract(Es1, Es2);
+subtract([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ subtract([H1|Es1], Es2);
+subtract([], Es2) ->
+ [];
+subtract(Es1, []) ->
+ Es1.
+
+%% subset(Set1, Set2)
+%% Return 'true' when every element of Set1 is also a member of Set2,
+%% else 'false'.
+
+subset([H1|Es1], [H2|Es2]) when H1 < H2 -> %H1 not in Set2
+ false;
+subset([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ subset(Es1, Es2);
+subset([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ subset([H1|Es1], Es2);
+subset([], Es2) ->
+ true;
+subset(Es1, []) ->
+ false.
diff --git a/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/test.erl b/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/test.erl
new file mode 100644
index 0000000000..a48a7e112f
--- /dev/null
+++ b/lib/debugger/test/dbg_ui_SUITE_data/manual_data/src/test.erl
@@ -0,0 +1,157 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(test).
+
+%%-compile(export_all).
+-export([test1/0,
+ test2/0,
+ test3/0,
+ test4/0,
+ test5/0,
+ test6/1,
+ test7/0,
+ test8/1,
+ test9/0,
+ test10/0,
+ test11/0,
+ test12/1
+ ]).
+
+
+-export([test9_server/1]).
+
+
+test1() ->
+ R1 = lists1:reverse("retep"),
+ R2 = ordsets1:list_to_set([b,c,a,2,4,1]),
+ R3 = lists1:reverse("nilo"),
+ {R1,R2, R3}.
+
+test2() ->
+ R1 = spawn(lists1,reverse,["retep"]),
+ R2 = spawn(ordsets1,list_to_set,[[b,c,a,2,4,1]]),
+ R3 = spawn(lists1,reverse,["nilo"]),
+ {R1,R2, R3}.
+
+
+test3() ->
+ A = a,
+ Pid = spawn(?MODULE, test1,[]),
+ B = b,
+ {A, B, Pid}.
+
+test4() ->
+ Pid = spawn(?MODULE, test1,[]),
+ A = a,
+ B = b,
+ {A, B, Pid}.
+test5() ->
+ L1 = [a,b,c],
+ L = length(L1),
+ A = a,
+ B = b,
+ {A, B, L, L1}.
+
+
+
+test6(0) ->
+ ok;
+test6(N) when N>0 ->
+ spawn(lists1,reverse,["adolfiparisrorsirapifloda"]),
+ test6(N-1).
+
+
+test7() ->
+ CurDirReturn = file:get_cwd(),
+ {ok, CurDir} = CurDirReturn,
+ DirListReturn = file:list_dir(CurDir),
+ {ok, DirList} = DirListReturn,
+ io:format("~w~n",[DirList]).
+
+
+test8(List) ->
+ %% foo
+ %%bar
+ %% foo
+ %%bar
+ %% foo
+ %%bar
+ %% foo
+ %%bar
+
+ L2 = [gamma|List],
+ {L2, List}.
+
+test9() ->
+ S1 = spawn(?MODULE, test9_server,[self()]),
+ S2 = spawn(?MODULE, test9_server,[bongo]),
+ S3 = spawn(?MODULE, test9_server,[42]),
+
+ test9_loop(S1,S2,S3).
+
+test9_loop(S1,S2,S3) ->
+ receive
+ {S1, hej} ->
+ io:format("S1 ~n"),
+ test9_loop(S1,S2,S3);
+ {S2, hej} ->
+ io:format("S2 ~n"),
+ test9_loop(S1,S2,S3);
+ {S3, hej} ->
+ io:format("S3 ~n"),
+ test9_loop(S1,S2,S3)
+ end.
+
+
+test9_server(Pid) ->
+ io:format("started server: ~p~n",[Pid]),
+ test9_server1(Pid).
+
+test9_server1(Pid) ->
+ Pad = {pad, Pid},
+ test9_server2(Pad).
+
+test9_server2(Pad) ->
+ {pad, Pid} = Pad,
+ Pid ! {self(), hej}.
+
+
+
+
+
+test10() ->
+ receive
+ X ->
+ done
+ after 20000 ->
+ timeout
+ end.
+
+test11() ->
+ receive
+ X ->
+ done
+ end.
+
+test12(normal) ->
+ done;
+test12(crash) ->
+ exit(crash).
diff --git a/lib/debugger/test/debugger.cover b/lib/debugger/test/debugger.cover
new file mode 100644
index 0000000000..509ddc0ec1
--- /dev/null
+++ b/lib/debugger/test/debugger.cover
@@ -0,0 +1,2 @@
+{incl_app,debugger,details}.
+
diff --git a/lib/debugger/test/debugger.spec b/lib/debugger/test/debugger.spec
new file mode 100644
index 0000000000..7aef026e77
--- /dev/null
+++ b/lib/debugger/test/debugger.spec
@@ -0,0 +1 @@
+{suites,"../debugger_test",all}.
diff --git a/lib/debugger/test/debugger_SUITE.erl b/lib/debugger/test/debugger_SUITE.erl
new file mode 100644
index 0000000000..6f5442e97d
--- /dev/null
+++ b/lib/debugger/test/debugger_SUITE.erl
@@ -0,0 +1,144 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(debugger_SUITE).
+
+%% Test break points.
+
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ app_test/1,erts_debug/1,encrypted_debug_info/1,
+ no_abstract_code/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test, erts_debug, no_abstract_code,
+ encrypted_debug_info].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(_Case, Config) ->
+ Dog=test_server:timetrap(?t:minutes(0.5)),
+ [{watchdog, Dog}|Config].
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+app_test(Config) when is_list(Config) ->
+ ?line ?t:app_test(debugger),
+ ok.
+
+erts_debug(Config) when is_list(Config) ->
+ c:l(erts_debug),
+ ok.
+
+no_abstract_code(Config) when is_list(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Simple = filename:join(PrivDir, "simple"),
+ ?line Source = Simple ++ ".erl",
+ ?line BeamFile = Simple ++ ".beam",
+ ?line simple_file(Source),
+
+ %% Compile module without abstract code.
+ CompileFlags = [{outdir,PrivDir}],
+ ?line {ok,_} = compile:file(Source, CompileFlags),
+ ?line error = int:i(Simple),
+
+ %% Cleanup.
+ ?line ok = file:delete(Source),
+ ?line ok = file:delete(BeamFile),
+
+ ok.
+
+encrypted_debug_info(Config) when is_list(Config) ->
+ try begin crypto:start(), crypto:info(), crypto:stop(), ok end of
+ ok ->
+ encrypted_debug_info_1(Config)
+ catch
+ error:_ ->
+ {skip,"The crypto application is missing or broken"}
+ end.
+
+encrypted_debug_info_1(Config) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Simple = filename:join(PrivDir, "simple"),
+ ?line Source = Simple ++ ".erl",
+ ?line BeamFile = Simple ++ ".beam",
+ ?line simple_file(Source),
+
+ %% Compile module.
+ Key = "_This a Crypto Key_",
+ CompileFlags = [{outdir,PrivDir},debug_info,{debug_info_key,Key}],
+ ?line {ok,_} = compile:file(Source, CompileFlags),
+
+ %% Interpret module
+ ?line ok = beam_lib:crypto_key_fun(simple_crypto_fun(Key)),
+ ?line {module,simple} = int:i(Simple),
+
+ %% Remove key.
+ ?line {ok,_} = beam_lib:clear_crypto_key_fun(),
+ ?line error = int:i(Simple),
+
+ %% Cleanup.
+ ?line ok = file:delete(Source),
+ ?line ok = file:delete(BeamFile),
+
+ ok.
+
+simple_crypto_fun(Key) ->
+ fun(init) -> ok;
+ ({debug_info, des3_cbc, simple, _}) -> Key
+ end.
+
+
+simple_file(File) ->
+ simple_file(File, simple).
+
+simple_file(File, Module) ->
+ simple_file(File, Module, member).
+
+simple_file(File, Module, F) ->
+ B = list_to_binary(["-module(", atom_to_list(Module), "). "
+ "-export([t/0]). "
+ "t() -> "
+ " t([]). "
+ "t(L) -> "
+ " lists:",
+ atom_to_list(F), "(a, L). "]),
+ ok = file:write_file(File, B).
diff --git a/lib/debugger/test/debugger_test.erl b/lib/debugger/test/debugger_test.erl
new file mode 100644
index 0000000000..a64bed5db1
--- /dev/null
+++ b/lib/debugger/test/debugger_test.erl
@@ -0,0 +1,154 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : This is the test module to be used in with the test cases
+%%% in the debugger test document.
+%%%----------------------------------------------------------------------
+
+-module(debugger_test).
+
+
+-export ([installation_ok/0,
+ list/0,
+ fac/1,
+ c_break/1]).
+
+
+-define (INT_DIR, "misc"). % The directory of the Interpreter directory
+-define (INT, "interpreter"). % The Interpreter directory name.
+-define (DBG, "debugger"). % Debugger name
+
+
+%%% installation_ok /0
+%%%
+
+installation_ok () ->
+ debugger_ok (),
+ interpreter_ok ().
+
+
+
+%%% debugger_ok /0
+%%%
+
+debugger_ok () ->
+ L = code:get_path (),
+
+ case debugger_in_codepath (L) of
+ {true, Msg} ->
+ out_put (Msg);
+
+ Other ->
+ out_put (Other)
+ end.
+
+
+
+%%% debugger_in_codepath /2
+%%%
+
+debugger_in_codepath ([]) ->
+ Msg = io_lib:format ("False: ~s not in code path", [?DBG]),
+ lists:flatten (Msg);
+
+debugger_in_codepath ([Path | T]) ->
+ case string:str (Path, ?DBG) =/= 0 of
+ true ->
+ Msg = io_lib:format ("Ok: ~s in code path (~s)", [?DBG, Path]),
+ Msg1 = lists:flatten (Msg),
+ {true, Msg1};
+
+ _Other ->
+ debugger_in_codepath (T)
+ end.
+
+
+
+%%% interpreter_ok /0
+%%%
+
+interpreter_ok () ->
+ Root_dir = code:root_dir (),
+ Misc_dir = filename:join (Root_dir, ?INT_DIR),
+
+ In_misc = case file:list_dir (Misc_dir) of
+ {ok, L} ->
+ lists:member (?INT, L);
+
+ Other ->
+ Other
+ end,
+
+ case In_misc of
+ true ->
+ Msg = io_lib:format ("Ok: ~s is in ~s", [?INT, ?INT_DIR]),
+ Msg1 = lists:flatten (Msg),
+ out_put (Msg1);
+
+ Other1 ->
+ Msg = io_lib:format ("Error: interpreter in misc - ~s", [Other1]),
+ Msg1 = lists:flatten (Msg),
+ out_put (Msg1)
+ end.
+
+
+
+%%% list /0
+%%%
+
+list () ->
+ A = [1, 2, 3, 4, 5],
+ B = [a, b, c, d, e],
+ lists:append (A, B).
+
+
+
+%%% fac /1
+%%%
+
+fac (0) ->
+ 1;
+
+
+fac (N) ->
+ N * fac (N - 1).
+
+
+
+%%% c_break /1
+%%%
+
+c_break (Bindings) ->
+ case int:get_binding ('N', Bindings) of
+ {value, 3} ->
+ true;
+
+ _Other ->
+ false
+ end.
+
+
+
+%%% out_put /1
+%%%
+
+out_put (X) ->
+ io:format ("~n~p~n", [X]).
diff --git a/lib/debugger/test/debugger_testdoc.fm5 b/lib/debugger/test/debugger_testdoc.fm5
new file mode 100644
index 0000000000..56882b8bd5
--- /dev/null
+++ b/lib/debugger/test/debugger_testdoc.fm5
Binary files differ
diff --git a/lib/debugger/test/erl_eval_SUITE.erl b/lib/debugger/test/erl_eval_SUITE.erl
new file mode 100644
index 0000000000..a92251e1af
--- /dev/null
+++ b/lib/debugger/test/erl_eval_SUITE.erl
@@ -0,0 +1,1406 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+-module(erl_eval_SUITE).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+
+-export([guard_1/1, guard_2/1,
+ match_pattern/1,
+ match_bin/1,
+ string_plusplus/1,
+ pattern_expr/1,
+ guard_3/1, guard_4/1,
+ lc/1,
+ simple_cases/1,
+ unary_plus/1,
+ apply_atom/1,
+ otp_5269/1,
+ otp_6539/1,
+ otp_6543/1,
+ otp_6787/1,
+ otp_6977/1,
+ otp_7550/1,
+ otp_8133/1,
+ funs/1,
+ try_catch/1,
+ eval_expr_5/1]).
+
+%%
+%% Define to run outside of test server
+%%
+%%-define(STANDALONE,1).
+
+-import(lists,[concat/1, sort/1]).
+
+-export([count_down/2, count_down_fun/0, do_apply/2,
+ local_func/3, local_func_value/2]).
+
+-ifdef(STANDALONE).
+-define(config(A,B),config(A,B)).
+-export([config/2]).
+-define(line, noop, ).
+config(priv_dir,_) ->
+ ".".
+-else.
+-include_lib("test_server/include/test_server.hrl").
+-export([init_per_testcase/2, end_per_testcase/2]).
+% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+init_per_testcase(_Case, Config) ->
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+-endif.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [guard_1, guard_2, match_pattern, string_plusplus,
+ pattern_expr, match_bin, guard_3, guard_4, lc,
+ simple_cases, unary_plus, apply_atom, otp_5269,
+ otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
+ otp_8133, funs, try_catch, eval_expr_5].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+guard_1(doc) ->
+ ["(OTP-2405)"];
+guard_1(suite) ->
+ [];
+guard_1(Config) when is_list(Config) ->
+ ?line {ok,Tokens ,_} =
+ erl_scan:string("if a+4 == 4 -> yes; true -> no end. "),
+ ?line {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ ?line no = guard_1_compiled(),
+ ?line {value, no, []} = erl_eval:expr(Expr, []),
+ ok.
+
+guard_1_compiled() ->
+ if a+4 == 4 -> yes; true -> no end.
+
+guard_2(doc) ->
+ ["Similar to guard_1, but type-correct"];
+guard_2(suite) ->
+ [];
+guard_2(Config) when is_list(Config) ->
+ ?line {ok,Tokens ,_} =
+ erl_scan:string("if 6+4 == 4 -> yes; true -> no end. "),
+ ?line {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ ?line no = guard_2_compiled(),
+ ?line {value, no, []} = erl_eval:expr(Expr, []),
+ ok.
+
+guard_2_compiled() ->
+ if 6+4 == 4 -> yes; true -> no end.
+
+string_plusplus(doc) ->
+ ["OTP-3069: syntactic sugar string ++ ..."];
+string_plusplus(suite) ->
+ [];
+string_plusplus(Config) when is_list(Config) ->
+ ?line check(fun() -> case "abc" of "ab" ++ L -> L end end,
+ "case \"abc\" of \"ab\" ++ L -> L end. ",
+ "c"),
+ ?line check(fun() -> case "abcde" of "ab" ++ "cd" ++ L -> L end end,
+ "case \"abcde\" of \"ab\" ++ \"cd\" ++ L -> L end. ",
+ "e"),
+ ?line check(fun() -> case "abc" of [97, 98] ++ L -> L end end,
+ "case \"abc\" of [97, 98] ++ L -> L end. ",
+ "c"),
+ ok.
+
+match_pattern(doc) ->
+ ["OTP-2983: match operator in pattern"];
+match_pattern(suite) ->
+ [];
+match_pattern(Config) when is_list(Config) ->
+ ?line check(fun() -> case {a, b} of {a, _X}=Y -> {x,Y} end end,
+ "case {a, b} of {a, X}=Y -> {x,Y} end. ",
+ {x, {a, b}}),
+ ?line check(fun() -> case {a, b} of Y={a, _X} -> {x,Y} end end,
+ "case {a, b} of Y={a, X} -> {x,Y} end. ",
+ {x, {a, b}}),
+ ?line check(fun() -> case {a, b} of Y={a, _X}=Z -> {Z,Y} end end,
+ "case {a, b} of Y={a, X}=Z -> {Z,Y} end. ",
+ {{a, b}, {a, b}}),
+ ?line check(fun() -> A = 4, B = 28, <<13:(A+(X=B))>>, X end,
+ "begin A = 4, B = 28, <<13:(A+(X=B))>>, X end.",
+ 28),
+ ok.
+
+match_bin(doc) ->
+ ["binary match problems"];
+match_bin(suite) ->
+ [];
+match_bin(Config) when is_list(Config) ->
+ ?line check(fun() -> <<"abc">> = <<"abc">> end,
+ "<<\"abc\">> = <<\"abc\">>. ",
+ <<"abc">>),
+ ?line check(fun() ->
+ <<Size,B:Size/binary,Rest/binary>> = <<2,"AB","CD">>,
+ {Size,B,Rest}
+ end,
+ "begin <<Size,B:Size/binary,Rest/binary>> = <<2,\"AB\",\"CD\">>, "
+ "{Size,B,Rest} end. ",
+ {2,<<"AB">>,<<"CD">>}),
+ ok.
+
+pattern_expr(doc) ->
+ ["OTP-3144: compile-time expressions in pattern"];
+pattern_expr(suite) ->
+ [];
+pattern_expr(Config) when is_list(Config) ->
+ ?line check(fun() -> case 4 of 2+2 -> ok end end,
+ "case 4 of 2+2 -> ok end. ",
+ ok),
+ ?line check(fun() -> case 2 of +2 -> ok end end,
+ "case 2 of +2 -> ok end. ",
+ ok),
+ ok.
+
+guard_3(doc) ->
+ ["OTP-4518."];
+guard_3(suite) ->
+ [];
+guard_3(Config) when is_list(Config) ->
+ ?line check(fun() -> if false -> false; true -> true end end,
+ "if false -> false; true -> true end.",
+ true),
+ ?line check(fun() -> if <<"hej">> == <<"hopp">> -> true;
+ true -> false end end,
+ "begin if <<\"hej\">> == <<\"hopp\">> -> true;
+ true -> false end end.",
+ false),
+ ?line check(fun() -> if <<"hej">> == <<"hej">> -> true;
+ true -> false end end,
+ "begin if <<\"hej\">> == <<\"hej\">> -> true;
+ true -> false end end.",
+ true),
+ ok.
+
+guard_4(doc) ->
+ ["OTP-4885."];
+guard_4(suite) ->
+ [];
+guard_4(Config) when is_list(Config) ->
+ ?line check(fun() -> if {erlang,'+'}(3,a) -> true ; true -> false end end,
+ "if {erlang,'+'}(3,a) -> true ; true -> false end.",
+ false),
+ ?line check(fun() -> if {erlang,is_integer}(3) -> true ; true -> false end
+ end,
+ "if {erlang,is_integer}(3) -> true ; true -> false end.",
+ true),
+ ?line check(fun() -> [X || X <- [1,2,3], erlang:is_integer(X)] end,
+ "[X || X <- [1,2,3], erlang:is_integer(X)].",
+ [1,2,3]),
+ ?line check(fun() -> if is_atom(is_integer(a)) -> true ; true -> false end
+ end,
+ "if is_atom(is_integer(a)) -> true ; true -> false end.",
+ true),
+ ?line check(fun() -> if {erlang,is_atom}({erlang,is_integer}(a)) -> true;
+ true -> false end end,
+ "if {erlang,is_atom}({erlang,is_integer}(a)) -> true; "
+ "true -> false end.",
+ true),
+ ?line check(fun() -> if is_atom(3+a) -> true ; true -> false end end,
+ "if is_atom(3+a) -> true ; true -> false end.",
+ false),
+ ?line check(fun() -> if erlang:is_atom(3+a) -> true ; true -> false end
+ end,
+ "if erlang:is_atom(3+a) -> true ; true -> false end.",
+ false),
+ ok.
+
+
+lc(doc) ->
+ ["OTP-4518."];
+lc(suite) ->
+ [];
+lc(Config) when is_list(Config) ->
+ ?line check(fun() -> X = 32, [X || X <- [1,2,3]] end,
+ "begin X = 32, [X || X <- [1,2,3]] end.",
+ [1,2,3]),
+ ?line check(fun() -> X = 32,
+ [X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end,
+ %% "binsize variable" ^
+ "begin X = 32,
+ [X || <<X:X>> <- [<<1:32>>,<<2:32>>,<<3:8>>]] end.",
+ [1,2]),
+ ?line check(fun() -> Y = 13,[X || {X,Y} <- [{1,2}]] end,
+ "begin Y = 13,[X || {X,Y} <- [{1,2}]] end.",
+ [1]),
+ ?line error_check("begin [A || X <- [{1,2}], 1 == A] end.",
+ {unbound_var,'A'}),
+ ?line error_check("begin X = 32,
+ [{Y,W} || X <- [1,2,32,Y=4], Z <- [1,2,W=3]] end.",
+ {unbound_var,'Y'}),
+ ?line error_check("begin X = 32,<<A:B>> = <<100:X>> end.",
+ {unbound_var,'B'}),
+ ?line check(fun() -> [X || X <- [1,2,3,4], not (X < 2)] end,
+ "begin [X || X <- [1,2,3,4], not (X < 2)] end.",
+ [2,3,4]),
+ ?line check(fun() -> [X || X <- [true,false], X] end,
+ "[X || X <- [true,false], X].", [true]),
+ ok.
+
+simple_cases(doc) ->
+ ["Simple cases, just to cover some code."];
+simple_cases(suite) ->
+ [];
+simple_cases(Config) when is_list(Config) ->
+ ?line check(fun() -> A = $C end, "A = $C.", $C),
+ %% ?line check(fun() -> A = 3.14 end, "A = 3.14.", 3.14),
+ ?line check(fun() -> self() ! a, A = receive a -> true end end,
+ "begin self() ! a, A = receive a -> true end end.",
+ true),
+ ?line check(fun() -> c:flush(), self() ! a, self() ! b, self() ! c,
+ receive b -> b end,
+ {messages, [a,c]} =
+ erlang:process_info(self(), messages),
+ c:flush() end,
+ "begin c:flush(), self() ! a, self() ! b, self() ! c,"
+ "receive b -> b end,"
+ "{messages, [a,c]} ="
+ " erlang:process_info(self(), messages), c:flush() end.",
+ ok),
+ ?line check(fun() -> self() ! a, A = receive a -> true
+ after 0 -> false end end,
+ "begin self() ! a, A = receive a -> true"
+ " after 0 -> false end end.",
+ true),
+ ?line check(fun() -> c:flush(), self() ! a, self() ! b, self() ! c,
+ receive b -> b after 0 -> true end,
+ {messages, [a,c]} =
+ erlang:process_info(self(), messages),
+ c:flush() end,
+ "begin c:flush(), self() ! a, self() ! b, self() ! c,"
+ "receive b -> b after 0 -> true end,"
+ "{messages, [a,c]} ="
+ " erlang:process_info(self(), messages), c:flush() end.",
+ ok),
+ ?line check(fun() -> receive _ -> true after 10 -> false end end,
+ "receive _ -> true after 10 -> false end.",
+ false),
+ ?line check(fun() -> F = fun(A) -> A end, true = 3 == F(3) end,
+ "begin F = fun(A) -> A end, true = 3 == F(3) end.",
+ true),
+ ?line check(fun() -> F = fun(A) -> A end, true = 3 == apply(F, [3]) end,
+ "begin F = fun(A) -> A end, true = 3 == apply(F,[3]) end.",
+ true),
+ ?line check(fun() -> catch throw(a) end, "catch throw(a).", a),
+ ?line check(fun() -> catch a end, "catch a.", a),
+ ?line check(fun() -> 4 == 3 end, "4 == 3.", false),
+ ?line check(fun() -> not true end, "not true.", false),
+ ?line check(fun() -> -3 end, "-3.", -3),
+
+ ?line error_check("3.0 = 4.0.", {badmatch,4.0}),
+ ?line check(fun() -> <<(3.0+2.0):32/float>> = <<5.0:32/float>> end,
+ "<<(3.0+2.0):32/float>> = <<5.0:32/float>>.",
+ <<5.0:32/float>>),
+
+ ?line check(fun() -> false andalso kludd end, "false andalso kludd.",
+ false),
+ ?line check(fun() -> true andalso true end, "true andalso true.",
+ true),
+ ?line check(fun() -> true andalso false end, "true andalso false.",
+ false),
+ ?line check(fun() -> true andalso kludd end, "true andalso kludd.",
+ kludd),
+ ?line error_check("kladd andalso kludd.", {badarg,kladd}),
+
+ ?line check(fun() -> if false andalso kludd -> a; true -> b end end,
+ "if false andalso kludd -> a; true -> b end.",
+ b),
+ ?line check(fun() -> if true andalso true -> a; true -> b end end,
+ "if true andalso true -> a; true -> b end.",
+ a),
+ ?line check(fun() -> if true andalso false -> a; true -> b end end,
+ "if true andalso false -> a; true -> b end.",
+ b),
+
+ ?line check(fun() -> true orelse kludd end,
+ "true orelse kludd.", true),
+ ?line check(fun() -> false orelse false end,
+ "false orelse false.", false),
+ ?line check(fun() -> false orelse true end,
+ "false orelse true.", true),
+ ?line check(fun() -> false orelse kludd end,
+ "false orelse kludd.", kludd),
+ ?line error_check("kladd orelse kludd.", {badarg,kladd}),
+ ?line error_check("[X || X <- [1,2,3], begin 1 end].",{bad_filter,1}),
+ ?line error_check("[X || X <- a].",{bad_generator,a}),
+
+ ?line check(fun() -> if true orelse kludd -> a; true -> b end end,
+ "if true orelse kludd -> a; true -> b end.", a),
+ ?line check(fun() -> if false orelse false -> a; true -> b end end,
+ "if false orelse false -> a; true -> b end.", b),
+ ?line check(fun() -> if false orelse true -> a; true -> b end end,
+ "if false orelse true -> a; true -> b end.", a),
+
+ ?line check(fun() -> [X || X <- [1,2,3], X+2] end,
+ "[X || X <- [1,2,3], X+2].", []),
+
+ ?line check(fun() -> [X || X <- [1,2,3], [X] == [X || X <- [2]]] end,
+ "[X || X <- [1,2,3], [X] == [X || X <- [2]]].",
+ [2]),
+ ?line check(fun() -> F = fun(1) -> ett; (2) -> zwei end,
+ ett = F(1), zwei = F(2) end,
+ "begin F = fun(1) -> ett; (2) -> zwei end,
+ ett = F(1), zwei = F(2) end.",
+ zwei),
+ ?line check(fun() -> F = fun(X) when X == 1 -> ett;
+ (X) when X == 2 -> zwei end,
+ ett = F(1), zwei = F(2) end,
+ "begin F = fun(X) when X == 1 -> ett;
+ (X) when X == 2 -> zwei end,
+ ett = F(1), zwei = F(2) end.",
+ zwei),
+ ?line error_check("begin F = fun(1) -> ett end, zwei = F(2) end.",
+ function_clause),
+ ?line check(fun() -> if length([1]) == 1 -> yes;
+ true -> no end end,
+ "if length([1]) == 1 -> yes;
+ true -> no end.",
+ yes),
+ ?line check(fun() -> if is_integer(3) -> true; true -> false end end,
+ "if is_integer(3) -> true; true -> false end.", true),
+ ?line check(fun() -> if integer(3) -> true; true -> false end end,
+ "if integer(3) -> true; true -> false end.", true),
+ ?line check(fun() -> if is_float(3) -> true; true -> false end end,
+ "if is_float(3) -> true; true -> false end.", false),
+ ?line check(fun() -> if float(3) -> true; true -> false end end,
+ "if float(3) -> true; true -> false end.", false),
+ ?line check(fun() -> if is_number(3) -> true; true -> false end end,
+ "if is_number(3) -> true; true -> false end.", true),
+ ?line check(fun() -> if number(3) -> true; true -> false end end,
+ "if number(3) -> true; true -> false end.", true),
+ ?line check(fun() -> if is_atom(a) -> true; true -> false end end,
+ "if is_atom(a) -> true; true -> false end.", true),
+ ?line check(fun() -> if atom(a) -> true; true -> false end end,
+ "if atom(a) -> true; true -> false end.", true),
+ ?line check(fun() -> if is_list([]) -> true; true -> false end end,
+ "if is_list([]) -> true; true -> false end.", true),
+ ?line check(fun() -> if list([]) -> true; true -> false end end,
+ "if list([]) -> true; true -> false end.", true),
+ ?line check(fun() -> if is_tuple({}) -> true; true -> false end end,
+ "if is_tuple({}) -> true; true -> false end.", true),
+ ?line check(fun() -> if tuple({}) -> true; true -> false end end,
+ "if tuple({}) -> true; true -> false end.", true),
+ ?line check(fun() -> if is_pid(self()) -> true; true -> false end end,
+ "if is_pid(self()) -> true; true -> false end.", true),
+ ?line check(fun() -> if pid(self()) -> true; true -> false end end,
+ "if pid(self()) -> true; true -> false end.", true),
+ ?line check(fun() -> R = make_ref(), if is_reference(R) -> true;
+ true -> false end end,
+ "begin R = make_ref(), if is_reference(R) -> true;"
+ "true -> false end end.", true),
+ ?line check(fun() -> R = make_ref(), if reference(R) -> true;
+ true -> false end end,
+ "begin R = make_ref(), if reference(R) -> true;"
+ "true -> false end end.", true),
+ ?line check(fun() -> if is_port(a) -> true; true -> false end end,
+ "if is_port(a) -> true; true -> false end.", false),
+ ?line check(fun() -> if port(a) -> true; true -> false end end,
+ "if port(a) -> true; true -> false end.", false),
+ ?line check(fun() -> if is_function(a) -> true; true -> false end end,
+ "if is_function(a) -> true; true -> false end.", false),
+ ?line check(fun() -> if function(a) -> true; true -> false end end,
+ "if function(a) -> true; true -> false end.", false),
+ ?line check(fun() -> if is_binary(<<>>) -> true; true -> false end end,
+ "if is_binary(<<>>) -> true; true -> false end.", true),
+ ?line check(fun() -> if binary(<<>>) -> true; true -> false end end,
+ "if binary(<<>>) -> true; true -> false end.", true),
+ ?line check(fun() -> if is_integer(a) == true -> yes;
+ true -> no end end,
+ "if is_integer(a) == true -> yes;
+ true -> no end.",
+ no),
+ ?line check(fun() -> if [] -> true; true -> false end end,
+ "if [] -> true; true -> false end.", false),
+ ?line error_check("if lists:member(1,[1]) -> true; true -> false end.",
+ illegal_guard_expr),
+ ?line error_check("if false -> true end.", if_clause),
+ ?line check(fun() -> if a+b -> true; true -> false end end,
+ "if a + b -> true; true -> false end.", false),
+ ?line check(fun() -> if + b -> true; true -> false end end,
+ "if + b -> true; true -> false end.", false),
+ ?line error_check("case foo of bar -> true end.", {case_clause,foo}),
+ ?line error_check("case 4 of 2+a -> true; _ -> false end.",
+ illegal_pattern),
+ ?line error_check("case 4 of +a -> true; _ -> false end.",
+ illegal_pattern),
+ ?line check(fun() -> case a of
+ X when X == b -> one;
+ X when X == a -> two
+ end end,
+ "begin case a of
+ X when X == b -> one;
+ X when X == a -> two
+ end end.", two),
+ ?line error_check("3 = 4.", {badmatch,4}),
+ ?line error_check("a = 3.", {badmatch,3}),
+ %% ?line error_check("3.1 = 2.7.",{badmatch,2.7}),
+ ?line error_check("$c = 4.", {badmatch,4}),
+ ?line check(fun() -> $c = $c end, "$c = $c.", $c),
+ ?line check(fun() -> _ = bar end, "_ = bar.", bar),
+ ?line check(fun() -> A = 14, A = 14 end,
+ "begin A = 14, A = 14 end.", 14),
+ ?line error_check("begin A = 14, A = 16 end.", {badmatch,16}),
+ ?line error_check("\"hej\" = \"san\".", {badmatch,"san"}),
+ ?line check(fun() -> "hej" = "hej" end,
+ "\"hej\" = \"hej\".", "hej"),
+ ?line error_check("[] = [a].", {badmatch,[a]}),
+ ?line check(fun() -> [] = [] end, "[] = [].", []),
+ ?line error_check("[a] = [].", {badmatch,[]}),
+ ?line error_check("{a,b} = 34.", {badmatch,34}),
+ ?line check(fun() -> <<X:7>> = <<8:7>>, X end,
+ "begin <<X:7>> = <<8:7>>, X end.", 8),
+ ?line error_check("<<34:32>> = \"hej\".", {badmatch,"hej"}),
+ ?line check(fun() -> trunc((1 * 3 div 3 + 4 - 3) / 1) rem 2 end,
+ "begin trunc((1 * 3 div 3 + 4 - 3) / 1) rem 2 end.", 0),
+ ?line check(fun() -> (2#101 band 2#10101) bor (2#110 bxor 2#010) end,
+ "(2#101 band 2#10101) bor (2#110 bxor 2#010).", 5),
+ ?line check(fun() -> (2#1 bsl 4) + (2#10000 bsr 3) end,
+ "(2#1 bsl 4) + (2#10000 bsr 3).", 18),
+ ?line check(fun() -> ((1<3) and ((1 =:= 2) or (1 =/= 2))) xor (1=<2) end,
+ "((1<3) and ((1 =:= 2) or (1 =/= 2))) xor (1=<2).", false),
+ ?line check(fun() -> (a /= b) or (2 > 4) or (3 >= 3) end,
+ "(a /= b) or (2 > 4) or (3 >= 3).", true),
+ ?line check(fun() -> "hej" ++ "san" =/= "hejsan" -- "san" end,
+ "\"hej\" ++ \"san\" =/= \"hejsan\" -- \"san\".", true),
+ ?line check(fun() -> (bnot 1) < -0 end, "(bnot (+1)) < -0.", true),
+ ok.
+
+unary_plus(doc) ->
+ ["OTP-4929. Unary plus rejects non-numbers."];
+unary_plus(suite) ->
+ [];
+unary_plus(Config) when is_list(Config) ->
+ ?line check(fun() -> F = fun(X) -> + X end,
+ true = -1 == F(-1) end,
+ "begin F = fun(X) -> + X end,"
+ " true = -1 == F(-1) end.", true, ['F'], none, none),
+ ?line error_check("+a.", badarith),
+ ok.
+
+apply_atom(doc) ->
+ ["OTP-5064. Can no longer apply atoms."];
+apply_atom(suite) ->
+ [];
+apply_atom(Config) when is_list(Config) ->
+ ?line error_check("[X || X <- [[1],[2]],
+ begin L = length, L(X) =:= 1 end].",
+ {badfun,length}),
+ ok.
+
+otp_5269(doc) ->
+ ["OTP-5269. Bugs in the bit syntax."];
+otp_5269(suite) ->
+ [];
+otp_5269(Config) when is_list(Config) ->
+ ?line check(fun() -> L = 8,
+ F = fun(<<A:L,B:A>>) -> B end,
+ F(<<16:8, 7:16>>)
+ end,
+ "begin
+ L = 8, F = fun(<<A:L,B:A>>) -> B end, F(<<16:8, 7:16>>)
+ end.",
+ 7),
+ ?line check(fun() -> L = 8,
+ F = fun(<<L:L,B:L>>) -> B end,
+ F(<<16:8, 7:16>>)
+ end,
+ "begin
+ L = 8, F = fun(<<L:L,B:L>>) -> B end, F(<<16:8, 7:16>>)
+ end.",
+ 7),
+ ?line check(fun() -> L = 8, <<A:L,B:A>> = <<16:8, 7:16>>, B end,
+ "begin L = 8, <<A:L,B:A>> = <<16:8, 7:16>>, B end.",
+ 7),
+ ?line error_check("begin L = 8, <<L:L,B:L>> = <<16:8, 7:16>> end.",
+ {badmatch,<<16:8,7:16>>}),
+
+ ?line error_check("begin <<L:16,L:L>> = <<16:16,8:16>>, L end.",
+ {badmatch, <<16:16,8:16>>}),
+ ?line check(fun() -> U = 8, (fun(<<U:U>>) -> U end)(<<32:8>>) end,
+ "begin U = 8, (fun(<<U:U>>) -> U end)(<<32:8>>) end.",
+ 32),
+ ?line check(fun() -> U = 8, [U || <<U:U>> <- [<<32:8>>]] end,
+ "begin U = 8, [U || <<U:U>> <- [<<32:8>>]] end.",
+ [32]),
+ ?line error_check("(fun({3,<<A:32,A:32>>}) -> a end)
+ ({3,<<17:32,19:32>>}).",
+ function_clause),
+ ?line check(fun() -> [X || <<A:8,
+ B:A>> <- [<<16:8,19:16>>],
+ <<X:8>> <- [<<B:8>>]] end,
+ "[X || <<A:8,
+ B:A>> <- [<<16:8,19:16>>],
+ <<X:8>> <- [<<B:8>>]].",
+ [19]),
+ ok.
+
+otp_6539(doc) ->
+ ["OTP-6539. try/catch bugs."];
+otp_6539(suite) ->
+ [];
+otp_6539(Config) when is_list(Config) ->
+ ?line check(fun() ->
+ F = fun(A,B) ->
+ try A+B
+ catch _:_ -> dontthinkso
+ end
+ end,
+ lists:zipwith(F, [1,2], [2,3])
+ end,
+ "begin
+ F = fun(A,B) ->
+ try A+B
+ catch _:_ -> dontthinkso
+ end
+ end,
+ lists:zipwith(F, [1,2], [2,3])
+ end.",
+ [3, 5]),
+ ok.
+
+otp_6543(doc) ->
+ ["OTP-6543. bitlevel binaries."];
+otp_6543(suite) ->
+ [];
+otp_6543(Config) when is_list(Config) ->
+ ?line check(fun() ->
+ << <<X>> || <<X>> <- [1,2,3] >>
+ end,
+ "<< <<X>> || <<X>> <- [1,2,3] >>.",
+ <<>>),
+ ?line check(fun() ->
+ << <<X>> || X <- [1,2,3] >>
+ end,
+ "<< <<X>> || X <- [1,2,3] >>.",
+ <<1,2,3>>),
+ ?line check(fun() ->
+ << <<X:8>> || <<X:2>> <= <<"hej">> >>
+ end,
+ "<< <<X:8>> || <<X:2>> <= <<\"hej\">> >>.",
+ <<1,2,2,0,1,2,1,1,1,2,2,2>>),
+ ?line check(fun() ->
+ << <<X:8>> ||
+ <<65,X:4>> <= <<65,7:4,65,3:4,66,8:4>> >>
+ end,
+ "<< <<X:8>> ||
+ <<65,X:4>> <= <<65,7:4,65,3:4,66,8:4>> >>.",
+ <<7,3>>),
+ ?line check(fun() -> <<34:18/big>> end,
+ "<<34:18/big>>.",
+ <<0,8,2:2>>),
+ ?line check(fun() -> <<34:18/big-unit:2>> end,
+ "<<34:18/big-unit:2>>.",
+ <<0,0,0,2,2:4>>),
+ ?line check(fun() -> <<34:18/little>> end,
+ "<<34:18/little>>.",
+ <<34,0,0:2>>),
+ ?line case eval_string("<<34:18/native>>.") of
+ <<0,8,2:2>> -> ok;
+ <<34,0,0:2>> -> ok
+ end,
+ ?line check(fun() -> <<34:18/big-signed>> end,
+ "<<34:18/big-signed>>.",
+ <<0,8,2:2>>),
+ ?line check(fun() -> <<34:18/little-signed>> end,
+ "<<34:18/little-signed>>.",
+ <<34,0,0:2>>),
+ ?line case eval_string("<<34:18/native-signed>>.") of
+ <<0,8,2:2>> -> ok;
+ <<34,0,0:2>> -> ok
+ end,
+ ?line check(fun() -> <<34:18/big-unsigned>> end,
+ "<<34:18/big-unsigned>>.",
+ <<0,8,2:2>>),
+ ?line check(fun() -> <<34:18/little-unsigned>> end,
+ "<<34:18/little-unsigned>>.",
+ <<34,0,0:2>>),
+ ?line case eval_string("<<34:18/native-unsigned>>.") of
+ <<0,8,2:2>> -> ok;
+ <<34,0,0:2>> -> ok
+ end,
+ ?line check(fun() -> <<3.14:32/float-big>> end,
+ "<<3.14:32/float-big>>.",
+ <<64,72,245,195>>),
+ ?line check(fun() -> <<3.14:32/float-little>> end,
+ "<<3.14:32/float-little>>.",
+ <<195,245,72,64>>),
+ ?line case eval_string("<<3.14:32/float-native>>.") of
+ <<64,72,245,195>> -> ok;
+ <<195,245,72,64>> -> ok
+ end,
+ ?line error_check("<<(<<17,3:2>>)/binary>>.", badarg),
+ ?line check(fun() -> <<(<<17,3:2>>)/bitstring>> end,
+ "<<(<<17,3:2>>)/bitstring>>.",
+ <<17,3:2>>),
+ ?line check(fun() -> <<(<<17,3:2>>):10/bitstring>> end,
+ "<<(<<17,3:2>>):10/bitstring>>.",
+ <<17,3:2>>),
+ ?line check(fun() -> <<<<344:17>>/binary-unit:17>> end,
+ "<<<<344:17>>/binary-unit:17>>.",
+ <<344:17>>),
+
+ ?line check(fun() -> <<X:18/big>> = <<34:18/big>>, X end,
+ "begin <<X:18/big>> = <<34:18/big>>, X end.",
+ 34),
+ ?line check(fun() -> <<X:18/big-unit:2>> = <<34:18/big-unit:2>>, X end,
+ "begin <<X:18/big-unit:2>> = <<34:18/big-unit:2>>, X end.",
+ 34),
+ ?line check(fun() -> <<X:18/little>> = <<34:18/little>>, X end,
+ "begin <<X:18/little>> = <<34:18/little>>, X end.",
+ 34),
+ ?line check(fun() -> <<X:18/native>> = <<34:18/native>>, X end,
+ "begin <<X:18/native>> = <<34:18/native>>, X end.",
+ 34),
+ ?line check(fun() -> <<X:18/big-signed>> = <<34:18/big-signed>>, X end,
+ "begin <<X:18/big-signed>> = <<34:18/big-signed>>, X end.",
+ 34),
+ ?line check(fun() -> <<X:18/little-signed>> = <<34:18/little-signed>>,
+ X end,
+ "begin <<X:18/little-signed>> = <<34:18/little-signed>>,
+ X end.",
+ 34),
+ ?line check(fun() -> <<X:18/native-signed>> = <<34:18/native-signed>>,
+ X end,
+ "begin <<X:18/native-signed>> = <<34:18/native-signed>>,
+ X end.",
+ 34),
+ ?line check(fun() -> <<X:18/big-unsigned>> = <<34:18/big-unsigned>>,
+ X end,
+ "begin <<X:18/big-unsigned>> = <<34:18/big-unsigned>>,
+ X end.",
+ 34),
+ ?line check(fun() ->
+ <<X:18/little-unsigned>> = <<34:18/little-unsigned>>,
+ X end,
+ "begin <<X:18/little-unsigned>> = <<34:18/little-unsigned>>,
+ X end.",
+ 34),
+ ?line check(fun() ->
+ <<X:18/native-unsigned>> = <<34:18/native-unsigned>>,
+ X end,
+ "begin <<X:18/native-unsigned>> = <<34:18/native-unsigned>>,
+ X end.",
+ 34),
+ ?line check(fun() -> <<X:32/float-big>> = <<2.0:32/float-big>>, X end,
+ "begin <<X:32/float-big>> = <<2.0:32/float-big>>,
+ X end.",
+ 2.0),
+ ?line check(fun() -> <<X:32/float-little>> = <<2.0:32/float-little>>,
+ X end,
+ "begin <<X:32/float-little>> = <<2.0:32/float-little>>,
+ X end.",
+ 2.0),
+ ?line check(fun() -> <<X:32/float-native>> = <<2.0:32/float-native>>,
+ X end,
+ "begin <<X:32/float-native>> = <<2.0:32/float-native>>,
+ X end.",
+ 2.0),
+
+ ?line check(
+ fun() ->
+ [X || <<"hej",X:8>> <= <<"hej",8,"san",9,"hej",17,"hej">>]
+ end,
+ "[X || <<\"hej\",X:8>> <=
+ <<\"hej\",8,\"san\",9,\"hej\",17,\"hej\">>].",
+ [8,17]),
+ ?line check(
+ fun() ->
+ L = 8, << <<B:32>> || <<L:L,B:L>> <= <<16:8, 7:16>> >>
+ end,
+ "begin L = 8, << <<B:32>> || <<L:L,B:L>> <= <<16:8, 7:16>> >>
+ end.",
+ <<0,0,0,7>>),
+ %% Test the Value part of a binary segment.
+ %% "Old" bugs have been fixed (partial_eval is called on Value).
+ ?line check(fun() -> [ 3 || <<17/float>> <= <<17.0/float>>] end,
+ "[ 3 || <<17/float>> <= <<17.0/float>>].",
+ [3]),
+ ?line check(fun() -> [ 3 || <<17/float>> <- [<<17.0/float>>]] end,
+ "[ 3 || <<17/float>> <- [<<17.0/float>>]].",
+ [3]),
+ ?line check(fun() -> [ X || <<17/float,X:3>> <= <<17.0/float,2:3>>] end,
+ "[ X || <<17/float,X:3>> <= <<17.0/float,2:3>>].",
+ [2]),
+ ?line check(fun() ->
+ [ foo || <<(1 bsl 1023)/float>> <= <<(1 bsl 1023)/float>>]
+ end,
+ "[ foo || <<(1 bsl 1023)/float>> <= <<(1 bsl 1023)/float>>].",
+ [foo]),
+ ?line check(fun() ->
+ [ foo || <<(1 bsl 1023)/float>> <- [<<(1 bsl 1023)/float>>]]
+ end,
+ "[ foo || <<(1 bsl 1023)/float>> <- [<<(1 bsl 1023)/float>>]].",
+ [foo]),
+ ?line error_check("[ foo || <<(1 bsl 1024)/float>> <-
+ [<<(1 bsl 1024)/float>>]].",
+ badarg),
+ ?line check(fun() ->
+ [ foo || <<(1 bsl 1024)/float>> <- [<<(1 bsl 1023)/float>>]]
+ end,
+ "[ foo || <<(1 bsl 1024)/float>> <-
+ [<<(1 bsl 1023)/float>>]].",
+ []),
+ ?line check(fun() ->
+ [ foo || <<(1 bsl 1024)/float>> <= <<(1 bsl 1023)/float>>]
+ end,
+ "[ foo || <<(1 bsl 1024)/float>> <=
+ <<(1 bsl 1023)/float>>].",
+ []),
+ ?line check(fun() ->
+ L = 8,
+ [{L,B} || <<L:L,B:L/float>> <= <<32:8,7:32/float>>]
+ end,
+ "begin L = 8,
+ [{L,B} || <<L:L,B:L/float>> <= <<32:8,7:32/float>>]
+ end.",
+ [{32,7.0}]),
+ ?line check(fun() ->
+ L = 8,
+ [{L,B} || <<L:L,B:L/float>> <- [<<32:8,7:32/float>>]]
+ end,
+ "begin L = 8,
+ [{L,B} || <<L:L,B:L/float>> <- [<<32:8,7:32/float>>]]
+ end.",
+ [{32,7.0}]),
+ ?line check(fun() ->
+ [foo || <<"s">> <= <<"st">>]
+ end,
+ "[foo || <<\"s\">> <= <<\"st\">>].",
+ [foo]),
+ ?line check(fun() -> <<_:32>> = <<17:32>> end,
+ "<<_:32>> = <<17:32>>.",
+ <<17:32>>),
+ ?line check(fun() -> [foo || <<_:32>> <= <<17:32,20:32>>] end,
+ "[foo || <<_:32>> <= <<17:32,20:32>>].",
+ [foo,foo]),
+
+ ?line check(fun() -> << <<X:32>> || X <- [1,2,3], X > 1 >> end,
+ "<< <<X:32>> || X <- [1,2,3], X > 1 >>.",
+ <<0,0,0,2,0,0,0,3>>),
+ ?line error_check("[X || <<X>> <= [a,b]].",{bad_generator,[a,b]}),
+ ok.
+
+otp_6787(doc) ->
+ ["OTP-6787. bitlevel binaries."];
+otp_6787(suite) ->
+ [];
+otp_6787(Config) when is_list(Config) ->
+ ?line check(
+ fun() -> <<16:(1024*1024)>> = <<16:(1024*1024)>> end,
+ "<<16:(1024*1024)>> = <<16:(1024*1024)>>.",
+ <<16:1048576>>),
+ ok.
+
+otp_6977(doc) ->
+ ["OTP-6977. ++ bug."];
+otp_6977(suite) ->
+ [];
+otp_6977(Config) when is_list(Config) ->
+ ?line check(
+ fun() -> (fun([$X] ++ _) -> ok end)("X") end,
+ "(fun([$X] ++ _) -> ok end)(\"X\").",
+ ok),
+ ok.
+
+otp_7550(doc) ->
+ ["OTP-7550. Support for UTF-8, UTF-16, UTF-32."];
+otp_7550(Config) when is_list(Config) ->
+
+ %% UTF-8.
+ ?line check(
+ fun() -> <<65>> = <<65/utf8>> end,
+ "<<65>> = <<65/utf8>>.",
+ <<65>>),
+ ?line check(
+ fun() -> <<350/utf8>> = <<197,158>> end,
+ "<<350/utf8>> = <<197,158>>.",
+ <<197,158>>),
+ ?line check(
+ fun() -> <<$b,$j,$\303,$\266,$r,$n>> = <<"bj\366rn"/utf8>> end,
+ "<<$b,$j,$\303,$\266,$r,$n>> = <<\"bj\366rn\"/utf8>>.",
+ <<$b,$j,$\303,$\266,$r,$n>>),
+
+ %% UTF-16.
+ ?line check(
+ fun() -> <<0,65>> = <<65/utf16>> end,
+ "<<0,65>> = <<65/utf16>>.",
+ <<0,65>>),
+ ?line check(
+ fun() -> <<16#D8,16#08,16#DF,16#45>> = <<16#12345/utf16>> end,
+ "<<16#D8,16#08,16#DF,16#45>> = <<16#12345/utf16>>.",
+ <<16#D8,16#08,16#DF,16#45>>),
+ ?line check(
+ fun() -> <<16#08,16#D8,16#45,16#DF>> = <<16#12345/little-utf16>> end,
+ "<<16#08,16#D8,16#45,16#DF>> = <<16#12345/little-utf16>>.",
+ <<16#08,16#D8,16#45,16#DF>>),
+
+ ?line check(
+ fun() -> <<350/utf16>> = <<1,94>> end,
+ "<<350/utf16>> = <<1,94>>.",
+ <<1,94>>),
+ ?line check(
+ fun() -> <<350/little-utf16>> = <<94,1>> end,
+ "<<350/little-utf16>> = <<94,1>>.",
+ <<94,1>>),
+ ?line check(
+ fun() -> <<16#12345/utf16>> = <<16#D8,16#08,16#DF,16#45>> end,
+ "<<16#12345/utf16>> = <<16#D8,16#08,16#DF,16#45>>.",
+ <<16#D8,16#08,16#DF,16#45>>),
+ ?line check(
+ fun() -> <<16#12345/little-utf16>> = <<16#08,16#D8,16#45,16#DF>> end,
+ "<<16#12345/little-utf16>> = <<16#08,16#D8,16#45,16#DF>>.",
+ <<16#08,16#D8,16#45,16#DF>>),
+
+ %% UTF-32.
+ ?line check(
+ fun() -> <<16#12345/utf32>> = <<16#0,16#01,16#23,16#45>> end,
+ "<<16#12345/utf32>> = <<16#0,16#01,16#23,16#45>>.",
+ <<16#0,16#01,16#23,16#45>>),
+ ?line check(
+ fun() -> <<16#0,16#01,16#23,16#45>> = <<16#12345/utf32>> end,
+ "<<16#0,16#01,16#23,16#45>> = <<16#12345/utf32>>.",
+ <<16#0,16#01,16#23,16#45>>),
+ ?line check(
+ fun() -> <<16#12345/little-utf32>> = <<16#45,16#23,16#01,16#00>> end,
+ "<<16#12345/little-utf32>> = <<16#45,16#23,16#01,16#00>>.",
+ <<16#45,16#23,16#01,16#00>>),
+ ?line check(
+ fun() -> <<16#12345/little-utf32>> end,
+ "<<16#12345/little-utf32>>.",
+ <<16#45,16#23,16#01,16#00>>),
+
+ %% Mixed.
+ ?line check(
+ fun() -> <<16#41,16#12345/utf32,16#0391:16,16#2E:8>> end,
+ "<<16#41,16#12345/utf32,16#0391:16,16#2E:8>>.",
+ <<16#41,16#00,16#01,16#23,16#45,16#03,16#91,16#2E>>),
+ ok.
+
+
+otp_8133(doc) ->
+ ["OTP-8133. Bit comprehension bug."];
+otp_8133(suite) ->
+ [];
+otp_8133(Config) when is_list(Config) ->
+ ?line check(
+ fun() ->
+ E = fun(N) ->
+ if
+ is_integer(N) -> <<N/integer>>;
+ true -> throw(foo)
+ end
+ end,
+ try << << (E(V))/binary >> || V <- [1,2,3,a] >>
+ catch foo -> ok
+ end
+ end,
+ "begin
+ E = fun(N) ->
+ if is_integer(N) -> <<N/integer>>;
+ true -> throw(foo)
+ end
+ end,
+ try << << (E(V))/binary >> || V <- [1,2,3,a] >>
+ catch foo -> ok
+ end
+ end.",
+ ok),
+ ?line check(
+ fun() ->
+ E = fun(N) ->
+ if
+ is_integer(N) -> <<N/integer>>;
+ true -> erlang:error(foo)
+ end
+ end,
+ try << << (E(V))/binary >> || V <- [1,2,3,a] >>
+ catch error:foo -> ok
+ end
+ end,
+ "begin
+ E = fun(N) ->
+ if is_integer(N) -> <<N/integer>>;
+ true -> erlang:error(foo)
+ end
+ end,
+ try << << (E(V))/binary >> || V <- [1,2,3,a] >>
+ catch error:foo -> ok
+ end
+ end.",
+ ok),
+ ok.
+
+funs(doc) ->
+ ["Simple cases, just to cover some code."];
+funs(suite) ->
+ [];
+funs(Config) when is_list(Config) ->
+ do_funs(none, none),
+ do_funs(lfh(), none),
+ do_funs(lfh(), efh()),
+
+ ?line error_check("nix:foo().", {access_not_allowed,nix}, lfh(), efh()),
+ ?line error_check("bar().", undef, none, none),
+
+ ?line check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end,
+ F1(F1, 1000) end,
+ "begin F1 = fun(F,N) -> count_down(F, N) end,"
+ "F1(F1,1000) end.",
+ 0, ['F1'], lfh(), none),
+
+ ?line check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end,
+ F1(F1, 1000) end,
+ "begin F1 = fun(F,N) -> count_down(F, N) end,"
+ "F1(F1,1000) end.",
+ 0, ['F1'], lfh_value(), none),
+
+ ?line check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end,
+ F1(F1, 1000) end,
+ "begin F1 = fun(F,N) -> count_down(F, N) end,"
+ "F1(F1,1000) end.",
+ 0, ['F1'], lfh_value_extra(), none),
+
+ ?line check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end,
+ F1(F1, 1000) end,
+ "begin F1 = fun(F,N) -> count_down(F, N) end,"
+ "F1(F1,1000) end.",
+ 0, ['F1'], {?MODULE,local_func_value}, none),
+ %% This is not documented, and only for backward compatibility (good!).
+ B0 = erl_eval:new_bindings(),
+ ?line check(fun() -> is_function(?MODULE:count_down_fun()) end,
+ "begin is_function(count_down_fun()) end.",
+ true, [], {?MODULE,local_func,[B0]},none),
+
+ EF = fun({timer,sleep}, As) when length(As) == 1 -> exit({got_it,sleep});
+ ({M,F}, As) -> apply(M, F, As)
+ end,
+ EFH = {value, EF},
+ ?line error_check("apply(timer, sleep, [1]).", got_it, none, EFH),
+ ?line error_check("begin F = fun(T) -> timer:sleep(T) end,F(1) end.",
+ got_it, none, EFH),
+ ?line error_check("fun c/1.", undef),
+ ?line error_check("fun a:b/0().", undef),
+
+ MaxArgs = 20,
+ ?line [true] =
+ lists:usort([run_many_args(SAs) || SAs <- many_args(MaxArgs)]),
+ ?line {'EXIT',{{argument_limit,_},_}} =
+ (catch run_many_args(many_args1(MaxArgs+1))),
+ ok.
+
+run_many_args({S, As}) ->
+ apply(eval_string(S), As) =:= As.
+
+many_args(N) ->
+ [many_args1(I) || I <- lists:seq(1, N)].
+
+many_args1(N) ->
+ F = fun(L, P) ->
+ tl(lists:flatten([","++P++integer_to_list(E) || E <- L]))
+ end,
+ L = lists:seq(1, N),
+ T = F(L, "V"),
+ S = lists:flatten(io_lib:format("fun(~s) -> [~s] end.", [T, T])),
+ {S, L}.
+
+do_funs(LFH, EFH) ->
+ %% LFH is not really used by these examples...
+
+ %% These tests do not prove that tail recursive functions really
+ %% work (that the process does not grow); one should also run them
+ %% manually with 1000 replaced by 1000000.
+
+ M = atom_to_list(?MODULE),
+ ?line check(fun() -> F1 = fun(F,N) -> ?MODULE:count_down(F, N) end,
+ F1(F1, 1000) end,
+ concat(["begin F1 = fun(F,N) -> ", M,
+ ":count_down(F, N) end, F1(F1,1000) end."]),
+ 0, ['F1'], LFH, EFH),
+ ?line check(fun() -> F1 = fun(F,N) -> apply(?MODULE,count_down,[F,N])
+ end, F1(F1, 1000) end,
+ concat(["begin F1 = fun(F,N) -> apply(", M,
+ ",count_down,[F, N]) end, F1(F1,1000) end."]),
+ 0, ['F1'], LFH, EFH),
+ ?line check(fun() -> F1 = fun(F,N) -> {?MODULE,count_down}(F,N)
+ end, F1(F1, 1000) end,
+ concat(["begin F1 = fun(F,N) -> {", M,
+ ",count_down}(F, N) end, F1(F1,1000) end."]),
+ 0, ['F1'], LFH, EFH),
+ ?line check(fun() -> F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);
+ (_F,0) -> ok end,
+ F(F, 1000)
+ end,
+ "begin F = fun(F,N) when N > 0 -> apply(F,[F,N-1]);"
+ "(_F,0) -> ok end,"
+ "F(F, 1000) end.",
+ ok, ['F'], LFH, EFH),
+ ?line check(fun() -> F = fun(F,N) when N > 0 ->
+ apply(erlang,apply,[F,[F,N-1]]);
+ (_F,0) -> ok end,
+ F(F, 1000)
+ end,
+ "begin F = fun(F,N) when N > 0 ->"
+ "apply(erlang,apply,[F,[F,N-1]]);"
+ "(_F,0) -> ok end,"
+ "F(F, 1000) end.",
+ ok, ['F'], LFH, EFH),
+ ?line check(fun() -> F = count_down_fun(),
+ SF = fun(SF, F1, N) -> F(SF, F1, N) end,
+ SF(SF, F, 1000) end,
+ concat(["begin F = ", M, ":count_down_fun(),"
+ "SF = fun(SF, F1, N) -> F(SF, F1, N) end,"
+ "SF(SF, F, 1000) end."]),
+ ok, ['F','SF'], LFH, EFH),
+
+
+ ?line check(fun() -> F = fun(X) -> A = 1+X, {X,A} end,
+ true = {2,3} == F(2) end,
+ "begin F = fun(X) -> A = 1+X, {X,A} end,
+ true = {2,3} == F(2) end.", true, ['F'], LFH, EFH),
+ ?line check(fun() -> F = fun(X) -> {erlang,'+'}(X,2) end,
+ true = 3 == F(1) end,
+ "begin F = fun(X) -> {erlang,'+'}(X,2) end,"
+ " true = 3 == F(1) end.", true, ['F'],
+ LFH, EFH),
+ ?line check(fun() -> F = fun(X) -> byte_size(X) end,
+ ?MODULE:do_apply(F,<<"hej">>) end,
+ concat(["begin F = fun(X) -> size(X) end,",
+ M,":do_apply(F,<<\"hej\">>) end."]),
+ 3, ['F'], LFH, EFH),
+
+ ?line check(fun() -> F1 = fun(X, Z) -> {X,Z} end,
+ Z = 5,
+ F2 = fun(X, Y) -> F1(Z,{X,Y}) end,
+ F3 = fun(X, Y) -> {a,F1(Z,{X,Y})} end,
+ {5,{x,y}} = F2(x,y),
+ {a,{5,{y,x}}} = F3(y,x),
+ {5,{5,y}} = F2(Z,y),
+ true = {5,{x,5}} == F2(x,Z) end,
+ "begin F1 = fun(X, Z) -> {X,Z} end,
+ Z = 5,
+ F2 = fun(X, Y) -> F1(Z,{X,Y}) end,
+ F3 = fun(X, Y) -> {a,F1(Z,{X,Y})} end,
+ {5,{x,y}} = F2(x,y),
+ {a,{5,{y,x}}} = F3(y,x),
+ {5,{5,y}} = F2(Z,y),
+ true = {5,{x,5}} == F2(x,Z) end.",
+ true, ['F1','Z','F2','F3'], LFH, EFH),
+ ?line check(fun() -> F = fun(X) -> byte_size(X) end,
+ F2 = fun(Y) -> F(Y) end,
+ ?MODULE:do_apply(F2,<<"hej">>) end,
+ concat(["begin F = fun(X) -> size(X) end,",
+ "F2 = fun(Y) -> F(Y) end,",
+ M,":do_apply(F2,<<\"hej\">>) end."]),
+ 3, ['F','F2'], LFH, EFH),
+ ?line check(fun() -> Z = 5, F = fun(X) -> {Z,X} end,
+ F2 = fun(Z) -> F(Z) end, F2(3) end,
+ "begin Z = 5, F = fun(X) -> {Z,X} end,
+ F2 = fun(Z) -> F(Z) end, F2(3) end.",
+ {5,3},['F','F2','Z'], LFH, EFH),
+ ?line check(fun() -> F = fun(Z) -> Z end,
+ F2 = fun(X) -> F(X), Z = {X,X}, Z end,
+ {1,1} = F2(1), Z = 7, Z end,
+ "begin F = fun(Z) -> Z end,
+ F2 = fun(X) -> F(X), Z = {X,X}, Z end,
+ {1,1} = F2(1), Z = 7, Z end.", 7, ['F','F2','Z'],
+ LFH, EFH),
+ ?line check(fun() -> F = fun(F, N) -> [?MODULE:count_down(F,N) || X <-[1]]
+ end, F(F,2) end,
+ concat(["begin F = fun(F, N) -> [", M,
+ ":count_down(F,N) || X <-[1]] end, F(F,2) end."]),
+ [[[0]]], ['F'], LFH, EFH),
+ ok.
+
+count_down(F, N) when N > 0 ->
+ F(F, N-1);
+count_down(_F, N) ->
+ N.
+
+count_down_fun() ->
+ fun(SF,F,N) when N > 0 -> SF(SF,F,N-1);
+ (_SF,_F,_N) -> ok
+ end.
+
+do_apply(F, V) ->
+ F(V).
+
+lfh() ->
+ {eval, fun(F, As, Bs) -> local_func(F, As, Bs) end}.
+
+local_func(F, As0, Bs0) when is_atom(F) ->
+ {As,Bs} = erl_eval:expr_list(As0, Bs0, {eval,lfh()}),
+ case erlang:function_exported(?MODULE, F, length(As)) of
+ true ->
+ {value,apply(?MODULE, F, As),Bs};
+ false ->
+ {value,apply(shell_default, F, As),Bs}
+ end.
+
+lfh_value_extra() ->
+ %% Not documented.
+ {value, fun(F, As) -> local_func_value(F, As) end, []}.
+
+lfh_value() ->
+ {value, fun(F, As) -> local_func_value(F, As) end}.
+
+local_func_value(F, As) when is_atom(F) ->
+ case erlang:function_exported(?MODULE, F, length(As)) of
+ true ->
+ apply(?MODULE, F, As);
+ false ->
+ apply(shell_default, F, As)
+ end.
+
+efh() ->
+ {value, fun(F, As) -> external_func(F, As) end}.
+
+external_func({M,_}, _As) when M == nix ->
+ exit({{access_not_allowed,M},[mfa]});
+external_func(F, As) when is_function(F) ->
+ apply(F, As);
+external_func({M,F}, As) ->
+ apply(M, F, As).
+
+
+
+try_catch(doc) ->
+ ["Test try-of-catch-after-end statement"];
+try_catch(suite) ->
+ [];
+try_catch(Config) when is_list(Config) ->
+ %% Match in of with catch
+ ?line check(fun() -> try 1 of 1 -> 2 catch _:_ -> 3 end end,
+ "try 1 of 1 -> 2 catch _:_ -> 3 end.", 2),
+ ?line check(fun() -> try 1 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end end,
+ "try 1 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end.", 2),
+ ?line check(fun() -> try 3 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end end,
+ "try 3 of 1 -> 2; 3 -> 4 catch _:_ -> 5 end.", 4),
+ %% Just after
+ ?line check(fun () -> X = try 1 after put(try_catch, 2) end,
+ {X,get(try_catch)} end,
+ "begin X = try 1 after put(try_catch, 2) end, "
+ "{X,get(try_catch)} end.", {1,2}),
+ %% Match in of with after
+ ?line check(fun() -> X = try 1 of 1 -> 2 after put(try_catch, 3) end,
+ {X,get(try_catch)} end,
+ "begin X = try 1 of 1 -> 2 after put(try_catch, 3) end, "
+ "{X,get(try_catch)} end.", {2,3}),
+ ?line check(fun() -> X = try 1 of 1 -> 2; 3 -> 4
+ after put(try_catch, 5) end,
+ {X,get(try_catch)} end,
+ "begin X = try 1 of 1 -> 2; 3 -> 4 "
+ " after put(try_catch, 5) end, "
+ " {X,get(try_catch)} end.", {2,5}),
+ ?line check(fun() -> X = try 3 of 1 -> 2; 3 -> 4
+ after put(try_catch, 5) end,
+ {X,get(try_catch)} end,
+ "begin X = try 3 of 1 -> 2; 3 -> 4 "
+ " after put(try_catch, 5) end, "
+ " {X,get(try_catch)} end.", {4,5}),
+ %% Nomatch in of
+ ?line error_check("try 1 of 2 -> 3 catch _:_ -> 4 end.",
+ {try_clause,1}),
+ %% Nomatch in of with after
+ ?line check(fun () -> {'EXIT',{{try_clause,1},_}} =
+ begin catch try 1 of 2 -> 3
+ after put(try_catch, 4) end end,
+ get(try_catch) end,
+ "begin {'EXIT',{{try_clause,1},_}} = "
+ " begin catch try 1 of 2 -> 3 "
+ " after put(try_catch, 4) end end, "
+ " get(try_catch) end. ", 4),
+ %% Exception in try
+ ?line check(fun () -> try 1=2 catch error:{badmatch,2} -> 3 end end,
+ "try 1=2 catch error:{badmatch,2} -> 3 end.", 3),
+ ?line check(fun () -> try 1=2 of 3 -> 4
+ catch error:{badmatch,2} -> 5 end end,
+ "try 1=2 of 3 -> 4 "
+ "catch error:{badmatch,2} -> 5 end.", 5),
+ %% Exception in try with after
+ ?line check(fun () -> X = try 1=2
+ catch error:{badmatch,2} -> 3
+ after put(try_catch, 4) end,
+ {X,get(try_catch)} end,
+ "begin X = try 1=2 "
+ " catch error:{badmatch,2} -> 3 "
+ " after put(try_catch, 4) end, "
+ " {X,get(try_catch)} end. ", {3,4}),
+ ?line check(fun () -> X = try 1=2 of 3 -> 4
+ catch error:{badmatch,2} -> 5
+ after put(try_catch, 6) end,
+ {X,get(try_catch)} end,
+ "begin X = try 1=2 of 3 -> 4"
+ " catch error:{badmatch,2} -> 5 "
+ " after put(try_catch, 6) end, "
+ " {X,get(try_catch)} end. ", {5,6}),
+ %% Uncaught exception
+ ?line error_check("try 1=2 catch error:undefined -> 3 end. ",
+ {badmatch,2}),
+ ?line error_check("try 1=2 of 3 -> 4 catch error:undefined -> 5 end. ",
+ {badmatch,2}),
+ %% Uncaught exception with after
+ ?line check(fun () -> {'EXIT',{{badmatch,2},_}} =
+ begin catch try 1=2
+ after put(try_catch, 3) end end,
+ get(try_catch) end,
+ "begin {'EXIT',{{badmatch,2},_}} = "
+ " begin catch try 1=2 "
+ " after put(try_catch, 3) end end, "
+ " get(try_catch) end. ", 3),
+ ?line check(fun () -> {'EXIT',{{badmatch,2},_}} =
+ begin catch try 1=2 of 3 -> 4
+ after put(try_catch, 5) end end,
+ get(try_catch) end,
+ "begin {'EXIT',{{badmatch,2},_}} = "
+ " begin catch try 1=2 of 3 -> 4"
+ " after put(try_catch, 5) end end, "
+ " get(try_catch) end. ", 5),
+ ?line check(fun () -> {'EXIT',{{badmatch,2},_}} =
+ begin catch try 1=2 catch error:undefined -> 3
+ after put(try_catch, 4) end end,
+ get(try_catch) end,
+ "begin {'EXIT',{{badmatch,2},_}} = "
+ " begin catch try 1=2 catch error:undefined -> 3 "
+ " after put(try_catch, 4) end end, "
+ " get(try_catch) end. ", 4),
+ ?line check(fun () -> {'EXIT',{{badmatch,2},_}} =
+ begin catch try 1=2 of 3 -> 4
+ catch error:undefined -> 5
+ after put(try_catch, 6) end end,
+ get(try_catch) end,
+ "begin {'EXIT',{{badmatch,2},_}} = "
+ " begin catch try 1=2 of 3 -> 4 "
+ " catch error:undefined -> 5 "
+ " after put(try_catch, 6) end end, "
+ " get(try_catch) end. ", 6),
+ ok.
+
+
+eval_expr_5(doc) ->
+ ["(OTP-7933)"];
+eval_expr_5(suite) ->
+ [];
+eval_expr_5(Config) when is_list(Config) ->
+ ?line {ok,Tokens ,_} =
+ erl_scan:string("if a+4 == 4 -> yes; true -> no end. "),
+ ?line {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ ?line {value, no, []} = erl_eval:expr(Expr, [], none, none, none),
+ ?line no = erl_eval:expr(Expr, [], none, none, value),
+ try
+ erl_eval:expr(Expr, [], none, none, 4711),
+ ?line function_clause = should_never_reach_here
+ catch
+ error:function_clause ->
+ ok
+ end.
+
+%% Check the string in different contexts: as is; in fun; from compiled code.
+check(F, String, Result) ->
+ check1(F, String, Result),
+ FunString = concat(["fun() -> ", no_final_dot(String), " end(). "]),
+ check1(F, FunString, Result),
+ CompileString = concat(["hd(lists:map(fun(_) -> ", no_final_dot(String),
+ " end, [foo])). "]),
+ check1(F, CompileString, Result).
+
+check1(F, String, Result) ->
+ Result = F(),
+ case catch parse_and_run(String) of
+ {value, Result, _} ->
+ ok;
+ Other ->
+ test_server:fail({eval, Other, Result})
+ end.
+
+check(F, String, Result, BoundVars, LFH, EFH) ->
+ Result = F(),
+ case catch parse_and_run(String, LFH, EFH) of
+ {value, Result, Bs} ->
+ %% We just assume that Bs is an orddict...
+ Keys = orddict:fetch_keys(Bs),
+ case sort(BoundVars) == Keys of
+ true ->
+ ok;
+ false ->
+ test_server:fail({check, BoundVars, Keys})
+ end,
+ ok;
+ Other ->
+ test_server:fail({check, Other, Result})
+ end.
+
+error_check(String, Result) ->
+ case catch parse_and_run(String) of
+ {'EXIT', {Result,_}} ->
+ ok;
+ Other ->
+ test_server:fail({eval, Other, Result})
+ end.
+
+error_check(String, Result, LFH, EFH) ->
+ case catch parse_and_run(String, LFH, EFH) of
+ {'EXIT', {Result,_}} ->
+ ok;
+ Other ->
+ test_server:fail({eval, Other, Result})
+ end.
+
+eval_string(String) ->
+ {value, Result, _} = parse_and_run(String),
+ Result.
+
+parse_and_run(String) ->
+ {ok,Tokens,_} = erl_scan:string(String),
+ {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ erl_eval:expr(Expr, []).
+
+parse_and_run(String, LFH, EFH) ->
+ {ok,Tokens,_} = erl_scan:string(String),
+ {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
+ erl_eval:expr(Expr, [], LFH, EFH).
+
+no_final_dot(S) ->
+ case lists:reverse(S) of
+ " ." ++ R -> lists:reverse(R);
+ "." ++ R -> lists:reverse(R);
+ _ -> S
+ end.
diff --git a/lib/debugger/test/exception_SUITE.erl b/lib/debugger/test/exception_SUITE.erl
new file mode 100644
index 0000000000..8c864e4b5f
--- /dev/null
+++ b/lib/debugger/test/exception_SUITE.erl
@@ -0,0 +1,270 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(exception_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ badmatch/1,pending_errors/1,nil_arith/1]).
+
+-export([bad_guy/2]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [badmatch, pending_errors, nil_arith].
+
+-define(try_match(E),
+ catch ?MODULE:bar(),
+ {'EXIT', {{badmatch, nomatch}, _}} = (catch E = nomatch)).
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+badmatch(doc) -> "Test that deliberately bad matches are reported correctly.";
+badmatch(suite) -> [];
+badmatch(Config) when list(Config) ->
+ ?line ?try_match(a),
+ ?line ?try_match(42),
+ ?line ?try_match({a, b, c}),
+ ?line ?try_match([]),
+ ?line ?try_match(1.0),
+ ok.
+
+pending_errors(doc) ->
+ ["Test various exceptions, in the presence of a previous error suppressed ",
+ "in a guard."];
+pending_errors(suite) -> [];
+pending_errors(Config) when list(Config) ->
+ ?line pending(e_badmatch, {badmatch, b}),
+ ?line pending(x, function_clause),
+ ?line pending(e_case, {case_clause, xxx}),
+ ?line pending(e_if, if_clause),
+ ?line pending(e_badarith, badarith),
+ ?line pending(e_undef, undef),
+ ?line pending(e_timeoutval, timeout_value),
+ ?line pending(e_badarg, badarg),
+ ?line pending(e_badarg_spawn, badarg),
+ ok.
+
+bad_guy(pe_badarith, Other) when Other+1 == 0 -> % badarith (suppressed)
+ ok;
+bad_guy(pe_badarg, Other) when length(Other) > 0 -> % badarg (suppressed)
+ ok;
+bad_guy(_, e_case) ->
+ case xxx of
+ ok -> ok
+ end; % case_clause
+bad_guy(_, e_if) ->
+ if
+ a == b -> ok
+ end; % if_clause
+bad_guy(_, e_badarith) ->
+ 1+b; % badarith
+bad_guy(_, e_undef) ->
+ non_existing_module:foo(); % undef
+bad_guy(_, e_timeoutval) ->
+ receive
+ after arne -> % timeout_value
+ ok
+ end;
+bad_guy(_, e_badarg) ->
+ node(xxx); % badarg
+bad_guy(_, e_badarg_spawn) ->
+ spawn({}, {}, {}); % badarg
+bad_guy(_, e_badmatch) ->
+ a = b. % badmatch
+
+pending(Arg, Expected) ->
+ pending(pe_badarith, Arg, Expected),
+ pending(pe_badarg, Arg, Expected).
+
+pending(First, Second, Expected) ->
+ pending_catched(First, Second, Expected),
+ pending_exit_message([First, Second], Expected).
+
+pending_catched(First, Second, Expected) ->
+ ok = io:format("Catching bad_guy(~p, ~p)", [First, Second]),
+ case catch bad_guy(First, Second) of
+ {'EXIT', Reason} ->
+ pending(Reason, bad_guy, [First, Second], Expected);
+ Other ->
+ test_server:fail({not_exit, Other})
+ end.
+
+pending_exit_message(Args, Expected) ->
+ ok = io:format("Trapping EXITs from spawn_link(~p, ~p, ~p)",
+ [?MODULE, bad_guy, Args]),
+ process_flag(trap_exit, true),
+ Pid = spawn_link(?MODULE, bad_guy, Args),
+ receive
+ {'EXIT', Pid, Reason} ->
+ pending(Reason, bad_guy, Args, Expected);
+ Other ->
+ test_server:fail({unexpected_message, Other})
+ after 10000 ->
+ test_server:fail(timeout)
+ end,
+ process_flag(trap_exit, false).
+
+pending({badarg,[{erlang,Bif,BifArgs},{?MODULE,Func,Arity}|_]}, Func, Args, _Code)
+ when atom(Bif), list(BifArgs), length(Args) == Arity -> %Threaded code.
+ ok;
+pending({badarg,[{erlang,Bif,BifArgs},{?MODULE,Func,Args}|_]}, Func, Args, _Code)
+ when atom(Bif), list(BifArgs) -> %From interpreted code.
+ ok;
+pending({undef,[{non_existing_module,foo,[]}|_]}, _, _, _) ->
+ ok;
+pending({function_clause,[{?MODULE,Func,Args}|_]}, Func, Args, _Code) ->
+ ok;
+pending({Code,[{?MODULE,Func,Arity}|_]}, Func, Args, Code) when length(Args) == Arity -> %Threaded code
+ ok;
+pending({Code,[{?MODULE,Func,Args}|_]}, Func, Args, Code) -> %From interpreted code.
+ ok;
+pending(Reason, Func, Args, Code) ->
+ test_server:fail({bad_exit_reason,Reason,{Func,Args,Code}}).
+
+nil_arith(doc) ->
+ "Test that doing arithmetics on [] gives a badarith EXIT and not a crash.";
+nil_arith(suite) ->
+ [];
+nil_arith(Config) when list(Config) ->
+ ?line ba_plus_minus_times([], []),
+
+ ?line ba_plus_minus_times([], 0),
+ ?line ba_plus_minus_times([], 42),
+ ?line ba_plus_minus_times([], 38724978123478923784),
+ ?line ba_plus_minus_times([], 38.72),
+
+ ?line ba_plus_minus_times(0, []),
+ ?line ba_plus_minus_times(334, []),
+ ?line ba_plus_minus_times(387249797813478923784, []),
+ ?line ba_plus_minus_times(344.22, []),
+
+ ?line ba_div_rem([], []),
+
+ ?line ba_div_rem([], 0),
+ ?line ba_div_rem([], 1),
+ ?line ba_div_rem([], 42),
+ ?line ba_div_rem([], 38724978123478923784),
+ ?line ba_div_rem(344.22, []),
+
+ ?line ba_div_rem(0, []),
+ ?line ba_div_rem(1, []),
+ ?line ba_div_rem(334, []),
+ ?line ba_div_rem(387249797813478923784, []),
+ ?line ba_div_rem(344.22, []),
+
+ ?line ba_div_rem(344.22, 0.0),
+ ?line ba_div_rem(1, 0.0),
+ ?line ba_div_rem(392873498733971, 0.0),
+
+ ?line ba_bop([], []),
+ ?line ba_bop(0, []),
+ ?line ba_bop(42, []),
+ ?line ba_bop(-42342742987343, []),
+ ?line ba_bop(238.342, []),
+ ?line ba_bop([], 0),
+ ?line ba_bop([], -243),
+ ?line ba_bop([], 243),
+ ?line ba_bop([], 2438724982478933),
+ ?line ba_bop([], 3987.37),
+
+ ?line ba_bnot([]),
+ ?line ba_bnot(23.33),
+
+ ?line ba_shift([], []),
+ ?line ba_shift([], 0),
+ ?line ba_shift([], 4),
+ ?line ba_shift([], -4),
+ ?line ba_shift([], 2343333333333),
+ ?line ba_shift([], -333333333),
+ ?line ba_shift([], 234.00),
+ ?line ba_shift(23, []),
+ ?line ba_shift(0, []),
+ ?line ba_shift(-3433443433433323, []),
+ ?line ba_shift(433443433433323, []),
+ ?line ba_shift(343.93, []),
+ ok.
+
+ba_plus_minus_times(A, B) ->
+ io:format("~p + ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A + B),
+ io:format("~p - ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A - B),
+ io:format("~p * ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A * B).
+
+ba_div_rem(A, B) ->
+ io:format("~p / ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A / B),
+ io:format("~p div ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A div B),
+ io:format("~p rem ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A rem B).
+
+ba_bop(A, B) ->
+ io:format("~p band ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A band B),
+ io:format("~p bor ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bor B),
+ io:format("~p bxor ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bxor B).
+
+ba_shift(A, B) ->
+ io:format("~p bsl ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bsl B),
+ io:format("~p bsr ~p", [A, B]),
+ {'EXIT', {badarith, _}} = (catch A bsr B).
+
+ba_bnot(A) ->
+ io:format("bnot ~p", [A]),
+ {'EXIT', {badarith, _}} = (catch bnot A).
diff --git a/lib/debugger/test/fun_SUITE.erl b/lib/debugger/test/fun_SUITE.erl
new file mode 100644
index 0000000000..8103d9c692
--- /dev/null
+++ b/lib/debugger/test/fun_SUITE.erl
@@ -0,0 +1,246 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(fun_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ good_call/1,bad_apply/1,bad_fun_call/1,badarity/1,
+ ext_badarity/1,otp_6061/1]).
+-export([nothing/0]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [good_call, bad_apply, bad_fun_call, badarity,
+ ext_badarity, otp_6061].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+good_call(Config) when is_list(Config) ->
+ ?line F = fun() -> ok end,
+ ?line ok = F(),
+ ?line FF = fun ?MODULE:nothing/0,
+ ?line ok = FF(),
+ ok.
+
+bad_apply(doc) ->
+ "Test that the correct EXIT code is returned for all types of bad funs.";
+bad_apply(suite) -> [];
+bad_apply(Config) when is_list(Config) ->
+ ?line bad_apply_fc(42, [0]),
+ ?line bad_apply_fc(xx, [1]),
+ ?line bad_apply_fc({}, [2]),
+ ?line bad_apply_fc({1}, [3]),
+ ?line bad_apply_fc({1,2,3}, [4]),
+ ?line bad_apply_fc({1,2,3}, [5]),
+ ?line bad_apply_fc({1,2,3,4}, [6]),
+ ?line bad_apply_fc({1,2,3,4,5,6}, [7]),
+ ?line bad_apply_fc({1,2,3,4,5}, [8]),
+ ?line bad_apply_badarg({1,2}, [9]),
+ ok.
+
+bad_apply_fc(Fun, Args) ->
+ Res = (catch apply(Fun, Args)),
+ erlang:garbage_collect(),
+ erlang:yield(),
+ case Res of
+ {'EXIT',{{badfun,Fun},_Where}} ->
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
+ Other ->
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
+ ?t:fail({bad_result,Other})
+ end.
+
+bad_apply_badarg(Fun, Args) ->
+ Res = (catch apply(Fun, Args)),
+ erlang:garbage_collect(),
+ erlang:yield(),
+ case Res of
+ {'EXIT',{{badfun,Fun},_Where}} ->
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]);
+ Other ->
+ ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res]),
+ ?t:fail({bad_result, Other})
+ end.
+
+bad_fun_call(doc) ->
+ "Try directly calling bad funs.";
+bad_fun_call(suite) -> [];
+bad_fun_call(Config) when is_list(Config) ->
+ ?line bad_call_fc(42),
+ ?line bad_call_fc(xx),
+ ?line bad_call_fc({}),
+ ?line bad_call_fc({1}),
+ ?line bad_call_fc({1,2,3}),
+ ?line bad_call_fc({1,2,3}),
+ ?line bad_call_fc({1,2,3,4}),
+ ?line bad_call_fc({1,2,3,4,5,6}),
+ ?line bad_call_fc({1,2,3,4,5}),
+ ?line bad_call_fc({1,2}),
+ ok.
+
+bad_call_fc(Fun) ->
+ Args = [some,stupid,args],
+ Res = (catch Fun(Args)),
+ case Res of
+ {'EXIT',{{badfun,Fun},_Where}} ->
+ ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
+ Other ->
+ ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
+ ?t:fail({bad_result,Other})
+ end.
+
+%% Call and apply valid external funs with wrong number of arguments.
+
+badarity(Config) when is_list(Config) ->
+ ?line Fun = fun() -> ok end,
+ ?line Stupid = {stupid,arguments},
+ ?line Args = [some,{stupid,arguments},here],
+
+ %% Simple call.
+
+ ?line Res = (catch Fun(some, Stupid, here)),
+ erlang:garbage_collect(),
+ erlang:yield(),
+ case Res of
+ {'EXIT',{{badarity,{Fun,Args}},[_|_]}} ->
+ ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
+ _ ->
+ ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
+ ?line ?t:fail({bad_result,Res})
+ end,
+
+ %% Apply.
+
+ ?line Res2 = (catch apply(Fun, Args)),
+ erlang:garbage_collect(),
+ erlang:yield(),
+ case Res2 of
+ {'EXIT',{{badarity,{Fun,Args}},[_|_]}} ->
+ ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
+ _ ->
+ ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
+ ?line ?t:fail({bad_result,Res2})
+ end,
+ ok.
+
+%% Call and apply valid external funs with wrong number of arguments.
+
+ext_badarity(Config) when is_list(Config) ->
+ ?line Fun = fun ?MODULE:nothing/0,
+ ?line Stupid = {stupid,arguments},
+ ?line Args = [some,{stupid,arguments},here],
+
+ %% Simple call.
+
+ ?line Res = (catch Fun(some, Stupid, here)),
+ erlang:garbage_collect(),
+ erlang:yield(),
+ case Res of
+ {'EXIT',{{badarity,{Fun,Args}},_}} ->
+ ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]);
+ _ ->
+ ?line ok = io:format("~p(~p) -> ~p\n", [Fun,Args,Res]),
+ ?line ?t:fail({bad_result,Res})
+ end,
+
+ %% Apply.
+
+ ?line Res2 = (catch apply(Fun, Args)),
+ erlang:garbage_collect(),
+ erlang:yield(),
+ case Res2 of
+ {'EXIT',{{badarity,{Fun,Args}},_}} ->
+ ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]);
+ _ ->
+ ?line ok = io:format("apply(~p, ~p) -> ~p\n", [Fun,Args,Res2]),
+ ?line ?t:fail({bad_result,Res2})
+ end,
+ ok.
+
+nothing() ->
+ ok.
+
+otp_6061(suite) ->
+ [];
+otp_6061(doc) ->
+ ["Test handling of fun expression referring to uninterpreted code"];
+otp_6061(Config) when is_list(Config) ->
+
+ ?line OrigFlag = process_flag(trap_exit, true),
+
+ ?line Self = self(),
+ ?line Pid = spawn_link(fun() -> test_otp_6061(Self) end),
+
+ receive
+ working ->
+ ?line ok;
+ not_working ->
+ ?line ?t:fail(not_working);
+ {'EXIT', Pid, Reason} ->
+ ?line ?t:fail({crash, Reason})
+ after
+ 5000 ->
+ ?line ?t:fail(timeout)
+ end,
+
+ ?line process_flag(trap_exit, OrigFlag),
+
+ ok.
+
+test_otp_6061(Starter) ->
+ Passes = [2],
+ PassesF = [fun() -> Starter ! not_working end,
+ fun() -> Starter ! working end,
+ fun() -> Starter ! not_working end],
+ lists:foreach(fun(P)->(lists:nth(P,PassesF))() end,Passes).
diff --git a/lib/debugger/test/guard_SUITE.erl b/lib/debugger/test/guard_SUITE.erl
new file mode 100644
index 0000000000..611dcb4dff
--- /dev/null
+++ b/lib/debugger/test/guard_SUITE.erl
@@ -0,0 +1,1492 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(guard_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ bad_arith/1,bad_tuple/1,test_heap_guards/1,guard_bifs/1,
+ type_tests/1,const_guard/1,
+ const_cond/1,basic_not/1,complex_not/1,
+ semicolon/1,complex_semicolon/1,comma/1,
+ or_guard/1,more_or_guards/1,
+ complex_or_guards/1,and_guard/1,
+ xor_guard/1,more_xor_guards/1,
+ old_guard_tests/1,
+ build_in_guard/1,gbif/1,
+ t_is_boolean/1,is_function_2/1,
+ tricky/1,rel_ops/1,
+ basic_andalso_orelse/1,traverse_dcd/1,
+ check_qlc_hrl/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+-export([init/4]).
+-import(lists, [member/2]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [bad_arith, bad_tuple, test_heap_guards, guard_bifs,
+ type_tests, const_guard, const_cond, basic_not,
+ complex_not, semicolon, complex_semicolon, comma,
+ or_guard, more_or_guards, complex_or_guards, and_guard,
+ xor_guard, more_xor_guards, build_in_guard,
+ old_guard_tests, gbif, t_is_boolean, is_function_2,
+ tricky, rel_ops, basic_andalso_orelse, traverse_dcd,
+ check_qlc_hrl].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ ?line Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+bad_arith(doc) -> "Test that a bad arithmetic operation in a guard works correctly.";
+bad_arith(suite) -> [];
+bad_arith(Config) when list(Config) ->
+ ?line 5 = bad_arith1(2, 3),
+ ?line 10 = bad_arith1(1, infinity),
+ ?line 10 = bad_arith1(infinity, 1),
+ ?line 42 = bad_div(24, 0),
+ ok.
+
+bad_arith1(T1, T2) when T1+T2 < 10 ->
+ T1+T2;
+bad_arith1(_, _) ->
+ 10.
+
+bad_div(A, B) when A/B > 0 ->
+ A/B;
+bad_div(A, B) when A div B > 0 ->
+ A div B;
+bad_div(_A, _B) ->
+ 42.
+
+bad_tuple(doc) -> "Test that bad arguments to element/2 are handled correctly.";
+bad_tuple(suite) -> [];
+bad_tuple(Config) when list(Config) ->
+ ?line error = bad_tuple1(a),
+ ?line error = bad_tuple1({a, b}),
+ ?line x = bad_tuple1({x, b}),
+ ?line y = bad_tuple1({a, b, y}),
+ ok.
+
+bad_tuple1(T) when element(1, T) == x -> x;
+bad_tuple1(T) when element(3, T) == y -> y;
+bad_tuple1(_) -> error.
+
+test_heap_guards(doc) -> "";
+test_heap_guards(suite) -> [];
+test_heap_guards(Config) when list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Tuple = {a, tuple, is, built, here, xxx},
+ ?line List = [a, list, is, built, here],
+
+ ?line try_fun(fun a_case/1, [Tuple], [Tuple]),
+ ?line try_fun(fun a_case/1, [List], [List, List]),
+ ?line try_fun(fun a_case/1, [a], [a]),
+
+ ?line try_fun(fun an_if/1, [Tuple], [Tuple]),
+ ?line try_fun(fun an_if/1, [List], [List, List]),
+ ?line try_fun(fun an_if/1, [a], [a]),
+
+ ?line try_fun(fun receive_test/1, [Tuple], [Tuple]),
+ ?line try_fun(fun receive_test/1, [List], [List, List]),
+ ?line try_fun(fun receive_test/1, [a], [a]),
+ ok.
+
+a_case(V) ->
+ case V of
+ T when T == {a, tuple, is, built, here, xxx} ->
+ [T];
+ L when L == [a, list, is, built, here] ->
+ [L, L];
+ a ->
+ [a]
+ end.
+
+an_if(V) ->
+ if
+ V == {a, tuple, is, built, here, xxx} ->
+ [V];
+ V == [a, list, is, built, here] ->
+ [V, V];
+ V == a ->
+ [a]
+ end.
+
+receive_test(V) ->
+ self() ! V,
+ a_receive().
+
+a_receive() ->
+ receive
+ T when T == {a, tuple, is, built, here, xxx} ->
+ [T];
+ L when L == [a, list, is, built, here] ->
+ [L, L];
+ a ->
+ [a]
+ end.
+
+try_fun(Fun, Args, Result) ->
+ try_fun(16, Fun, Args, Result, []).
+
+try_fun(0, _, _, _, _) ->
+ ok;
+try_fun(Iter, Fun, Args, Result, Filler) ->
+ Pid = spawn_link(?MODULE, init, [self(),Fun,Args,list_to_tuple(Filler)]),
+ receive
+ {'EXIT',Pid,{result,Result}} ->
+ ?line try_fun(Iter-1, Fun, Args, Result, [0|Filler]);
+ {'EXIT',Pid,{result,Other}} ->
+ ?line io:format("Expected ~p; got ~p~n", [Result,Other]),
+ ?line test_server:fail();
+ Other ->
+ ?line test_server:fail({unexpected_message,Other})
+ end.
+
+init(_ReplyTo, Fun, Args, Filler) ->
+ Result = {result, apply(Fun, Args)},
+ dummy(Filler),
+ io:format("~p: result = ~p\n", [?LINE,Result]),
+ exit(Result).
+
+dummy(_) ->
+ ok.
+
+guard_bifs(doc) -> "Test all guard bifs with nasty (but legal arguments).";
+guard_bifs(suite) -> [];
+guard_bifs(Config) when list(Config) ->
+ ?line Big = -237849247829874297658726487367328971246284736473821617265433,
+ ?line Float = 387924.874,
+
+ %% Succeding use of guard bifs.
+
+ ?line try_gbif('abs/1', Big, -Big),
+ ?line try_gbif('float/1', Big, float(Big)),
+ ?line try_gbif('trunc/1', Float, 387924.0),
+ ?line try_gbif('round/1', Float, 387925.0),
+ ?line try_gbif('length/1', [], 0),
+
+ ?line try_gbif('length/1', [a], 1),
+ ?line try_gbif('length/1', [a, b], 2),
+ ?line try_gbif('length/1', lists:seq(0, 31), 32),
+
+ ?line try_gbif('hd/1', [a], a),
+ ?line try_gbif('hd/1', [a, b], a),
+
+ ?line try_gbif('tl/1', [a], []),
+ ?line try_gbif('tl/1', [a, b], [b]),
+ ?line try_gbif('tl/1', [a, b, c], [b, c]),
+
+ ?line try_gbif('size/1', {}, 0),
+ ?line try_gbif('size/1', {a}, 1),
+ ?line try_gbif('size/1', {a, b}, 2),
+ ?line try_gbif('size/1', {a, b, c}, 3),
+ ?line try_gbif('size/1', list_to_binary([]), 0),
+ ?line try_gbif('size/1', list_to_binary([1]), 1),
+ ?line try_gbif('size/1', list_to_binary([1, 2]), 2),
+ ?line try_gbif('size/1', list_to_binary([1, 2, 3]), 3),
+
+ ?line try_gbif('element/2', {x}, {1, x}),
+ ?line try_gbif('element/2', {x, y}, {1, x}),
+ ?line try_gbif('element/2', {x, y}, {2, y}),
+
+ ?line try_gbif('self/0', 0, self()),
+ ?line try_gbif('node/0', 0, node()),
+ ?line try_gbif('node/1', self(), node()),
+
+ %% Failing use of guard bifs.
+
+ ?line try_fail_gbif('abs/1', Big, 1),
+ ?line try_fail_gbif('abs/1', [], 1),
+
+ ?line try_fail_gbif('float/1', Big, 42),
+ ?line try_fail_gbif('float/1', [], 42),
+
+ ?line try_fail_gbif('trunc/1', Float, 0.0),
+ ?line try_fail_gbif('trunc/1', [], 0.0),
+
+ ?line try_fail_gbif('round/1', Float, 1.0),
+ ?line try_fail_gbif('round/1', [], a),
+
+ ?line try_fail_gbif('length/1', [], 1),
+ ?line try_fail_gbif('length/1', [a], 0),
+ ?line try_fail_gbif('length/1', a, 0),
+ ?line try_fail_gbif('length/1', {a}, 0),
+
+ ?line try_fail_gbif('hd/1', [], 0),
+ ?line try_fail_gbif('hd/1', [a], x),
+ ?line try_fail_gbif('hd/1', x, x),
+
+ ?line try_fail_gbif('tl/1', [], 0),
+ ?line try_fail_gbif('tl/1', [a], x),
+ ?line try_fail_gbif('tl/1', x, x),
+
+ ?line try_fail_gbif('size/1', {}, 1),
+ ?line try_fail_gbif('size/1', [], 0),
+ ?line try_fail_gbif('size/1', [a], 1),
+
+ ?line try_fail_gbif('element/2', {}, {1, x}),
+ ?line try_fail_gbif('element/2', {x}, {1, y}),
+ ?line try_fail_gbif('element/2', [], {1, z}),
+
+ ?line try_fail_gbif('self/0', 0, list_to_pid("<0.0.0>")),
+ ?line try_fail_gbif('node/0', 0, xxxx),
+ ?line try_fail_gbif('node/1', self(), xxx),
+ ?line try_fail_gbif('node/1', yyy, xxx),
+ ok.
+
+try_gbif(Id, X, Y) ->
+ case guard_bif(Id, X, Y) of
+ {Id, X, Y} ->
+ io:format("guard_bif(~p, ~p, ~p) -- ok", [Id, X, Y]);
+ Other ->
+ ?line ok = io:format("guard_bif(~p, ~p, ~p) -- bad result: ~p\n",
+ [Id, X, Y, Other]),
+ ?line test_server:fail()
+ end.
+
+try_fail_gbif(Id, X, Y) ->
+ case catch guard_bif(Id, X, Y) of
+ {'EXIT', {function_clause,{?MODULE,guard_bif,[Id,X,Y]}}} -> %Jam
+ io:format("guard_bif(~p, ~p, ~p) -- ok", [Id,X,Y]);
+ {'EXIT', {function_clause,[{?MODULE,guard_bif,[Id,X,Y]}|_]}} -> %Beam
+ io:format("guard_bif(~p, ~p, ~p) -- ok", [Id,X,Y]);
+ Other ->
+ ?line ok = io:format("guard_bif(~p, ~p, ~p) -- bad result: ~p\n",
+ [Id, X, Y, Other]),
+ ?line test_server:fail()
+ end.
+
+guard_bif('abs/1', X, Y) when abs(X) == Y ->
+ {'abs/1', X, Y};
+guard_bif('float/1', X, Y) when float(X) == Y ->
+ {'float/1', X, Y};
+guard_bif('trunc/1', X, Y) when trunc(X) == Y ->
+ {'trunc/1', X, Y};
+guard_bif('round/1', X, Y) when round(X) == Y ->
+ {'round/1', X, Y};
+guard_bif('length/1', X, Y) when length(X) == Y ->
+ {'length/1', X, Y};
+guard_bif('hd/1', X, Y) when hd(X) == Y ->
+ {'hd/1', X, Y};
+guard_bif('tl/1', X, Y) when tl(X) == Y ->
+ {'tl/1', X, Y};
+guard_bif('size/1', X, Y) when size(X) == Y ->
+ {'size/1', X, Y};
+guard_bif('element/2', X, {Pos, Expected}) when element(Pos, X) == Expected ->
+ {'element/2', X, {Pos, Expected}};
+guard_bif('self/0', X, Y) when self() == Y ->
+ {'self/0', X, Y};
+guard_bif('node/0', X, Y) when node() == Y ->
+ {'node/0', X, Y};
+guard_bif('node/1', X, Y) when node(X) == Y ->
+ {'node/1', X, Y}.
+
+type_tests(doc) -> "Test the type tests.";
+type_tests(suite) -> [];
+type_tests(Config) when list(Config) ->
+ ?line Types = all_types(),
+ ?line Tests = type_test_desc(),
+ ?line put(errors, 0),
+ ?line put(violations, 0),
+ ?line type_tests(Tests, Types),
+ ?line case {get(errors), get(violations)} of
+ {0, 0} ->
+ ok;
+ {0, N} ->
+ {comment, integer_to_list(N) ++ " standard violation(s)"};
+ {Errors, Violations} ->
+ io:format("~p sub test(s) failed, ~p violation(s)",
+ [Errors, Violations]),
+ ?line test_server:fail()
+ end.
+
+type_tests([{Test, AllowedTypes}| T], AllTypes) ->
+ type_tests(Test, AllTypes, AllowedTypes),
+ type_tests(T, AllTypes);
+type_tests([], _) ->
+ ok.
+
+type_tests(Test, [Type|T], Allowed) ->
+ {TypeTag, Value} = Type,
+ case member(TypeTag, Allowed) of
+ true ->
+ case catch type_test(Test, Value) of
+ Test ->
+ ok;
+ _Other ->
+ io:format("Test ~p(~p) failed", [Test, Value]),
+ put(errors, get(errors) + 1)
+ end;
+ false ->
+ case catch type_test(Test, Value) of
+ {'EXIT', {function_clause, {?MODULE, type_test, [Test, Value]}}} ->
+ ok;
+ {'EXIT', {function_clause,[{?MODULE,type_test,[Test,Value]}|_]}} ->
+ ok;
+ {'EXIT',Other} ->
+ ?line test_server:fail({unexpected_error_reason,Other});
+ tuple when function(Value) ->
+ io:format("Standard violation: Test ~p(~p) should fail",
+ [Test, Value]),
+ put(violations, get(violations) + 1);
+ _Other ->
+ io:format("Test ~p(~p) succeeded (should fail)", [Test, Value]),
+ put(errors, get(errors) + 1)
+ end
+ end,
+ type_tests(Test, T, Allowed);
+type_tests(_, [], _) ->
+ ok.
+
+all_types() ->
+ [{small, 42},
+ {big, 392742928742947293873938792874019287447829874290742},
+ {float, 3.14156},
+ {nil, []},
+ {cons, [a]},
+ {tuple, {a, b}},
+ {atom, xxxx},
+ {ref, make_ref()},
+ {pid, self()},
+ {port, open_port({spawn, efile}, [])},
+ {function, fun(X) -> X+1, "" end},
+ {binary, list_to_binary([])}].
+
+type_test_desc() ->
+ [{integer, [small, big]},
+ {float, [float]},
+ {number, [small, big, float]},
+ {atom, [atom]},
+ {list, [cons, nil]},
+ {nonempty_list, [cons]},
+ {nil, [nil]},
+ {tuple, [tuple]},
+ {pid, [pid]},
+ {port, [port]},
+ {reference, [ref]},
+ {function, [function]}].
+
+type_test(integer, X) when integer(X) ->
+ integer;
+type_test(float, X) when float(X) ->
+ float;
+type_test(number, X) when number(X) ->
+ number;
+type_test(atom, X) when atom(X) ->
+ atom;
+type_test(list, X) when list(X) ->
+ list;
+type_test(nonempty_list, [_]) ->
+ nonempty_list;
+type_test(nil, []) ->
+ nil;
+type_test(tuple, X) when tuple(X) ->
+ tuple;
+type_test(pid, X) when pid(X) ->
+ pid;
+type_test(reference, X) when reference(X) ->
+ reference;
+type_test(port, X) when port(X) ->
+ port;
+type_test(binary, X) when binary(X) ->
+ binary;
+type_test(function, X) when function(X) ->
+ function.
+
+const_guard(Config) when is_list(Config) ->
+ ?line if
+ (0 == 0) and ((0 == 0) or (0 == 0)) ->
+ ok
+ end.
+
+
+const_cond(Config) when is_list(Config) ->
+ ?line ok = const_cond({}, 0),
+ ?line ok = const_cond({a}, 1),
+ ?line error = const_cond({a,b}, 3),
+ ?line error = const_cond({a}, 0),
+ ?line error = const_cond({a,b}, 1),
+ ok.
+
+const_cond(T, Sz) ->
+ case T of
+ _X when false -> never;
+ _X when tuple(T), eq == eq, size(T) == Sz -> ok;
+ _X when tuple(T), eq == leq, size(T) =< Sz -> ok;
+ _X -> error
+ end.
+
+basic_not(Config) when is_list(Config) ->
+ True = id(true),
+ False = id(false),
+ Glurf = id(glurf),
+ A = id(5),
+ B = id(37.5),
+ C = id(-1),
+ D = id(5),
+ ATuple = {False,True,Glurf},
+
+ ?line check(fun() -> if not false -> ok; true -> error end end, ok),
+ ?line check(fun() -> if not true -> ok; true -> error end end, error),
+ ?line check(fun() -> if not False -> ok; true -> error end end, ok),
+ ?line check(fun() -> if not True -> ok; true -> error end end, error),
+
+ ?line check(fun() -> if A > B -> gt; A < B -> lt; A == B -> eq end end, lt),
+ ?line check(fun() -> if A > C -> gt; A < C -> lt; A == C -> eq end end, gt),
+ ?line check(fun() -> if A > D -> gt; A < D -> lt; A == D -> eq end end, eq),
+
+ ?line check(fun() -> if not (7 > 453) -> le; not (7 < 453) -> ge;
+ not (7 == 453) -> ne; true -> eq end end, le),
+ ?line check(fun() -> if not (7 > -8) -> le; not (7 < -8) -> ge;
+ not (7 == -8) -> ne; true -> eq end end, ge),
+ ?line check(fun() -> if not (7 > 7) -> le; not (7 < 7) -> ge;
+ not (7 == 7) -> ne; true -> eq end end, le),
+
+ ?line check(fun() -> if not (A > B) -> le; not (A < B) -> ge;
+ not (A == B) -> ne; true -> eq end end, le),
+ ?line check(fun() -> if not (A > C) -> le; not (A < C) -> ge;
+ not (A == C) -> ne; true -> eq end end, ge),
+ ?line check(fun() -> if not (A > D) -> le; not (A < D) -> ge;
+ not (A == D) -> ne; true -> eq end end, le),
+
+ ?line check(fun() -> if not element(1, ATuple) -> ok; true -> error end end, ok),
+ ?line check(fun() -> if not element(2, ATuple) -> ok; true -> error end end, error),
+ ?line check(fun() -> if not element(3, ATuple) -> ok; true -> error end end, error),
+
+ ?line check(fun() -> if not glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if not Glurf -> ok; true -> error end end, error),
+
+ ok.
+
+complex_not(Config) when is_list(Config) ->
+ ATuple = id({false,true,gurka}),
+ ?line check(fun() -> if not(element(1, ATuple)) -> ok; true -> error end end, ok),
+ ?line check(fun() -> if not(element(2, ATuple)) -> ok; true -> error end end, error),
+
+ ?line check(fun() -> if not(element(3, ATuple) == gurka) -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if not(element(3, ATuple) =/= gurka) -> ok;
+ true -> error end end, ok),
+
+ ?line check(fun() -> if {a,not(element(2, ATuple))} == {a,false} -> ok;
+ true -> error end end, ok),
+ ?line check(fun() -> if {a,not(element(1, ATuple))} == {a,false} -> ok;
+ true -> error end end, error),
+
+ ?line check(fun() -> if not(element(1, ATuple) or element(3, ATuple)) -> ok;
+ true -> error end end, error),
+
+ %% orelse
+ ?line check(fun() -> if not(element(1, ATuple) orelse element(3, ATuple)) -> ok;
+ true -> error end end, error),
+
+ ok.
+
+semicolon(Config) when is_list(Config) ->
+
+ %% True/false combined using ';' (literal atoms).
+
+ ?line check(fun() -> if true; false -> ok end end, ok),
+ ?line check(fun() -> if false; true -> ok end end, ok),
+ ?line check(fun() -> if true; true -> ok end end, ok),
+ ?line check(fun() -> if false; false -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if false; false -> ok end),
+ exit
+ end, exit),
+
+ %% True/false combined used ';'.
+
+ True = id(true),
+ False = id(false),
+
+ ?line check(fun() -> if True; False -> ok end end, ok),
+ ?line check(fun() -> if False; True -> ok end end, ok),
+ ?line check(fun() -> if True; True -> ok end end, ok),
+ ?line check(fun() -> if False; False -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if False; False -> ok end),
+ exit
+ end, exit),
+
+ %% Combine true/false with a non-boolean value.
+ Glurf = id(glurf),
+
+
+ ?line check(fun() -> if True; Glurf -> ok end end, ok),
+ ?line check(fun() -> if Glurf; True -> ok end end, ok),
+ ?line check(fun() -> if Glurf; Glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if False; Glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if Glurf; False -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if Glurf; Glurf -> ok end),
+ exit
+ end, exit),
+
+ %% Combine true/false with errors.
+
+ ATuple = id({false,true,gurka}),
+
+ ?line check(fun() -> if True; element(42, ATuple) -> ok end end, ok),
+ ?line check(fun() -> if element(42, ATuple); True -> ok end end, ok),
+ ?line check(fun() -> if element(42, ATuple); element(42, ATuple) -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if False; element(42, ATuple) -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if element(42, ATuple);
+ False -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if element(42, ATuple);
+ element(42, ATuple) -> ok end),
+ exit
+ end, exit),
+
+ ok.
+
+complex_semicolon(Config) when is_list(Config) ->
+ ?line ok = csemi1(int, {blurf}),
+ ?line ok = csemi1(string, {blurf}),
+ ?line ok = csemi1(float, [a]),
+ ?line error = csemi1(35, 42),
+
+ %% 2
+ ?line ok = csemi2({}, {a,b,c}),
+ ?line ok = csemi2({1,3.5}, {a,b,c}),
+ ?line ok = csemi2(dum, {a,b,c}),
+
+ ?line ok = csemi2({45,-19.3}, {}),
+ ?line ok = csemi2({45,-19.3}, {dum}),
+ ?line ok = csemi2({45,-19.3}, {dum,dum}),
+
+ ?line error = csemi2({45}, {dum}),
+ ?line error = csemi2([], {dum}),
+ ?line error = csemi2({dum}, []),
+ ?line error = csemi2([], []),
+
+ %% 3
+ ?line csemi3(fun csemi3a/4),
+ ?line csemi3(fun csemi3b/4),
+ ?line csemi3(fun csemi3c/4),
+
+ %% 4
+ ?line csemi4(fun csemi4a/4),
+ ?line csemi4(fun csemi4b/4),
+ ?line csemi4(fun csemi4c/4),
+ ?line csemi4(fun csemi4d/4),
+
+ %% 4, 'orelse' instead of 'or'
+ ?line csemi4_orelse(fun csemi4_orelse_a/4),
+ ?line csemi4_orelse(fun csemi4_orelse_b/4),
+ ?line csemi4_orelse(fun csemi4_orelse_c/4),
+ ?line csemi4_orelse(fun csemi4_orelse_d/4),
+
+ ok.
+
+csemi1(Type, Val) when is_list(Val), Type == float;
+ Type == int; Type == string -> ok;
+csemi1(_, _) -> error.
+
+csemi2(A, B) when size(A) > 1; size(B) > 2 -> ok;
+csemi2(_, _) -> error.
+
+csemi3(Csemi3) ->
+ ok = Csemi3({}, {a,b,c}, [0], [0]),
+ ok = Csemi3({1,3.5}, {a,b,c}, -1, -1),
+ ok = Csemi3(dum, {a,b,c}, 0.0, 0.0),
+ ok = Csemi3(dum, {c}, b, a),
+ ok = Csemi3(dum, <<1,2,3>>, 0.0, 0.0),
+ ok = Csemi3(<<3.5/float>>, {a,b,c}, -1, -1),
+
+ ok = Csemi3({45,-19.3}, {}, [], []),
+ ok = Csemi3({45,-19.3}, {dum}, 42, 42),
+ ok = Csemi3({45,-19.3}, {dum,dum}, 33, 33),
+
+ ok = Csemi3({45}, {dum}, 1.0, 0),
+ ok = Csemi3([a], {dum}, 1.0, 0),
+ ok = Csemi3({dum}, [], 1.0, 0),
+ ok = Csemi3([], [], 1.0, 0),
+ ok = Csemi3(blurf, {dum}, 1.0, 0),
+ ok = Csemi3({a}, blurf, 1.0, 0),
+ ok = Csemi3([a], [dum], 1.0, 0),
+ ok = Csemi3({dum}, [], 1.0, 0),
+ ok = Csemi3([], [], 1.0, 0),
+
+ error = Csemi3({45}, {dum}, 0, 0),
+ error = Csemi3([a], {dum}, 0, 0),
+ error = Csemi3({dum}, [], 0, 0),
+ error = Csemi3([], [], 0, 0),
+ ok.
+
+csemi3a(A, B, X, Y) when X > Y; size(A) > 1; size(B) > 2 -> ok;
+csemi3a(_, _, _, _) -> error.
+
+csemi3b(A, B, X, Y) when size(A) > 1; X > Y; size(B) > 2 -> ok;
+csemi3b(_, _, _, _) -> error.
+
+csemi3c(A, B, X, Y) when size(A) > 1; size(B) > 2; X > Y -> ok;
+csemi3c(_, _, _, _) -> error.
+
+
+csemi4(Test) ->
+ ok = Test({a,b}, 2, {c,d}, 2),
+ ok = Test({1,2,3}, 0, [], 0),
+ ok = Test({}, 2, blurf, 0),
+ ok = Test({}, 2, {1}, 2),
+
+ error = Test([], 4, {}, 0),
+ error = Test({}, 0, [a,b], 4),
+ error = Test({}, 0, [a,b], 0),
+ error = Test([], 0, {}, 0),
+ error = Test({}, 0, {}, 0),
+
+ ok.
+
+csemi4a(A, X, B, Y) when (size(A) > 1) or (X > 1);
+ (size(B) > 1) or (Y > 1) -> ok;
+csemi4a(_, _, _, _) -> error.
+
+csemi4b(A, X, B, Y) when (X > 1) or (size(A) > 1);
+ (size(B) > 1) or (Y > 1) -> ok;
+csemi4b(_, _, _, _) -> error.
+
+csemi4c(A, X, B, Y) when (size(A) > 1) or (X > 1);
+ (Y > 1) or (size(B) > 1) -> ok;
+csemi4c(_, _, _, _) -> error.
+
+csemi4d(A, X, B, Y) when (X > 1) or (size(A) > 1);
+ (Y > 1) or (size(B) > 1) -> ok;
+csemi4d(_, _, _, _) -> error.
+
+
+csemi4_orelse(Test) ->
+ ok = Test({a,b}, 2, {c,d}, 2),
+ ok = Test({1,2,3}, 0, [], 0),
+ ok = Test({}, 2, blurf, 0),
+ ok = Test({}, 2, {1}, 2),
+
+ ?line error = Test([], 1, {}, 0),
+
+ ok.
+
+csemi4_orelse_a(A, X, B, Y) when (size(A) > 1) orelse (X > 1);
+ (size(B) > 1) orelse (Y > 1) -> ok;
+csemi4_orelse_a(_, _, _, _) -> error.
+
+csemi4_orelse_b(A, X, B, Y) when (X > 1) orelse (size(A) > 1);
+ (size(B) > 1) orelse (Y > 1) -> ok;
+csemi4_orelse_b(_, _, _, _) -> error.
+
+csemi4_orelse_c(A, X, B, Y) when (size(A) > 1) orelse (X > 1);
+ (Y > 1) orelse (size(B) > 1) -> ok;
+csemi4_orelse_c(_, _, _, _) -> error.
+
+csemi4_orelse_d(A, X, B, Y) when (X > 1) or (size(A) > 1);
+ (Y > 1) or (size(B) > 1) -> ok;
+csemi4_orelse_d(_, _, _, _) -> error.
+
+
+comma(Config) when is_list(Config) ->
+
+ %% ',' combinations of literal true/false.
+
+ ?line check(fun() -> if true, false -> ok; true -> error end end, error),
+ ?line check(fun() -> if false, true -> ok; true -> error end end, error),
+ ?line check(fun() -> if true, true -> ok end end, ok),
+ ?line check(fun() -> if false, false -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if true, false -> ok;
+ false, true -> ok;
+ false, false -> ok
+ end),
+ exit
+ end, exit),
+
+ %% ',' combinations of true/false in variables.
+
+ True = id(true),
+ False = id(false),
+
+ ?line check(fun() -> if True, False -> ok; true -> error end end, error),
+ ?line check(fun() -> if False, True -> ok; true -> error end end, error),
+ ?line check(fun() -> if True, True -> ok end end, ok),
+ ?line check(fun() -> if False, False -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if True, False -> ok;
+ False, True -> ok;
+ False, False -> ok
+ end),
+ exit
+ end, exit),
+
+ %% ',' combinations of true/false, and non-boolean in variables.
+
+ Glurf = id(glurf),
+
+ ?line check(fun() -> if True, Glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if Glurf, True -> ok; true -> error end end, error),
+ ?line check(fun() -> if True, True -> ok end end, ok),
+ ?line check(fun() -> if Glurf, Glurf -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if True, Glurf -> ok;
+ Glurf, True -> ok;
+ Glurf, Glurf -> ok
+ end),
+ exit
+ end, exit),
+
+ %% ',' combinations of true/false with errors.
+ ATuple = id({a,b,c}),
+
+ ?line check(fun() -> if True, element(42, ATuple) -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if element(42, ATuple), True -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if True, True -> ok end end, ok),
+ ?line check(fun() -> if element(42, ATuple), element(42, ATuple) -> ok;
+ true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if True, element(42, ATuple) -> ok;
+ element(42, ATuple), True -> ok;
+ element(42, ATuple), element(42, ATuple) -> ok
+ end),
+ exit
+ end, exit),
+
+ ok.
+
+or_guard(Config) when is_list(Config) ->
+ True = id(true),
+ False = id(false),
+ Glurf = id(glurf),
+
+ %% 'or' combinations of literal true/false.
+ ?line check(fun() -> if true or false -> ok end end, ok),
+ ?line check(fun() -> if false or true -> ok end end, ok),
+ ?line check(fun() -> if true or true -> ok end end, ok),
+ ?line check(fun() -> if false or false -> ok; true -> error end end, error),
+
+ ?line check(fun() -> if glurf or true -> ok; true -> error end end, error),
+ ?line check(fun() -> if true or glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if glurf or glurf -> ok; true -> error end end, error),
+
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if false or false -> ok end),
+ exit
+ end, exit),
+
+
+ %% 'or' combinations using variables containing true/false.
+ ?line check(fun() -> if True or False -> ok end end, ok),
+ ?line check(fun() -> if False or True -> ok end end, ok),
+ ?line check(fun() -> if True or True -> ok end end, ok),
+ ?line check(fun() -> if False or False -> ok; true -> error end end, error),
+
+ ?line check(fun() -> if True or Glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if Glurf or True -> ok; true -> error end end, error),
+ ?line check(fun() -> if Glurf or Glurf -> ok; true -> error end end, error),
+
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if False or False -> ok end),
+ exit
+ end, exit),
+
+ ok.
+
+more_or_guards(Config) when is_list(Config) ->
+ True = id(true),
+ False = id(false),
+ ATuple = id({false,true,gurka}),
+
+ ?line check(fun() ->
+ if element(42, ATuple) or False -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if False or element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(18, ATuple) or element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if True or element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(42, ATuple) or True -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(1, ATuple) or element(42, ATuple) or True -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(1, ATuple) or True or element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if
+ (<<False:8>> == <<0>>) or element(2, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if
+ element(2, ATuple) or (<<True:8>> == <<1>>) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(2, ATuple) or element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if
+ element(1, ATuple) or
+ element(2, ATuple) or
+ element(19, ATuple) -> ok;
+ true -> error end
+ end, error),
+ ok.
+
+complex_or_guards(Config) when is_list(Config) ->
+ %% complex_or_1/2
+ ?line ok = complex_or_1({a,b,c,d}, {1,2,3}),
+ ?line ok = complex_or_1({a,b,c,d}, {1}),
+ ?line ok = complex_or_1({a}, {1,2,3}),
+ ?line error = complex_or_1({a}, {1}),
+
+ ?line error = complex_or_1(1, 2),
+ ?line error = complex_or_1([], {a,b,c,d}),
+ ?line error = complex_or_1({a,b,c,d}, []),
+
+
+ %% complex_or_2/1
+ ?line ok = complex_or_2({true,{}}),
+ ?line ok = complex_or_2({false,{a}}),
+ ?line ok = complex_or_2({false,{a,b,c}}),
+ ?line ok = complex_or_2({true,{a,b,c,d}}),
+
+ ?line error = complex_or_2({blurf,{a,b,c}}),
+
+ ?line error = complex_or_2({true}),
+ ?line error = complex_or_2({true,no_tuple}),
+ ?line error = complex_or_2({true,[]}),
+
+ %% complex_or_3/2
+ ?line ok = complex_or_3({true}, {}),
+ ?line ok = complex_or_3({false}, {a}),
+ ?line ok = complex_or_3({false}, {a,b,c}),
+ ?line ok = complex_or_3({true}, {a,b,c,d}),
+ ?line ok = complex_or_3({false}, <<1,2,3>>),
+ ?line ok = complex_or_3({true}, <<1,2,3,4>>),
+
+ ?line error = complex_or_3(blurf, {a,b,c}),
+
+ ?line error = complex_or_3({false}, <<1,2,3,4>>),
+ ?line error = complex_or_3([], <<1,2>>),
+ ?line error = complex_or_3({true}, 45),
+ ?line error = complex_or_3(<<>>, <<>>),
+
+ %% complex_or_4/2
+ ?line ok = complex_or_4(<<1,2,3>>, {true}),
+ ?line ok = complex_or_4(<<1,2,3>>, {false}),
+ ?line ok = complex_or_4(<<1,2,3>>, {true}),
+ ?line ok = complex_or_4({1,2,3}, {true}),
+ ?line error = complex_or_4({1,2,3,4}, {false}),
+
+ ?line error = complex_or_4(<<1,2,3,4>>, []),
+ ?line error = complex_or_4([], {true}),
+
+ %% complex_or_5/2
+ ?line ok = complex_or_5(<<1>>, {false}),
+ ?line ok = complex_or_5(<<1,2,3>>, {true}),
+ ?line ok = complex_or_5(<<1,2,3,4>>, {false}),
+ ?line ok = complex_or_5({1,2,3}, {false}),
+ ?line ok = complex_or_5({1,2,3,4}, {false}),
+
+ ?line error = complex_or_5(blurf, {false}),
+ ?line error = complex_or_5(<<1>>, klarf),
+ ?line error = complex_or_5(blurf, klarf),
+
+ %% complex_or_6/2
+ ?line ok = complex_or_6({true,true}, {1,2,3,4}),
+ ?line ok = complex_or_6({true,true}, <<1,2,3,4>>),
+ ?line ok = complex_or_6({false,false}, <<1,2,3,4>>),
+ ?line ok = complex_or_6({false,true}, <<1>>),
+ ?line ok = complex_or_6({true,false}, {1}),
+ ?line ok = complex_or_6({true,true}, {1}),
+
+ ?line error = complex_or_6({false,false}, {1}),
+
+ ?line error = complex_or_6({true}, {1,2,3,4}),
+ ?line error = complex_or_6({}, {1,2,3,4}),
+ ?line error = complex_or_6([], {1,2,3,4}),
+ ?line error = complex_or_6([], {1,2,3,4}),
+ ?line error = complex_or_6({true,false}, klurf),
+
+ ok.
+
+complex_or_1(A, B) ->
+ if
+ ((3 < size(A)) and (size(A) < 9)) or
+ ((2 < size(B)) and (size(B) < 7)) -> ok;
+ true -> error
+ end.
+
+complex_or_2(Tuple) ->
+ if
+ element(1, Tuple) or not (size(element(2, Tuple)) > 3) -> ok;
+ true -> error
+ end.
+
+complex_or_3(A, B) ->
+ if
+ not (size(B) > 3) or element(1, A) -> ok;
+ true -> error
+ end.
+
+complex_or_4(A, B) ->
+ if
+ not (is_tuple(A) and (size(A) > 3)) or element(1, B) -> ok;
+ true -> error
+ end.
+
+complex_or_5(A, B) ->
+ if
+ not (is_tuple(A) or (size(A) > 3)) or not element(1, B) -> ok;
+ true -> error
+ end.
+
+complex_or_6(A, B) ->
+ if
+ not (not element(1, A) and not element(2, A)) or
+ not (not (size(B) > 3)) -> ok;
+ true -> error
+ end.
+
+and_guard(Config) when is_list(Config) ->
+
+ %% 'and' combinations of literal true/false.
+
+ ?line check(fun() -> if true and false -> ok; true -> error end end, error),
+ ?line check(fun() -> if false and true -> ok; true -> error end end, error),
+ ?line check(fun() -> if true and true -> ok end end, ok),
+ ?line check(fun() -> if false and false -> ok; true -> error end end, error),
+
+ ?line check(fun() -> if glurf and true -> ok; true -> error end end, error),
+ ?line check(fun() -> if true and glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if glurf and glurf -> ok; true -> error end end, error),
+
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if true and false -> ok;
+ false and true -> ok;
+ false and false -> ok
+ end),
+ exit
+ end, exit),
+
+ %% 'and' combinations of true/false in variables.
+
+ True = id(true),
+ False = id(false),
+
+ ?line check(fun() -> if True and False -> ok; true -> error end end, error),
+ ?line check(fun() -> if False and True -> ok; true -> error end end, error),
+ ?line check(fun() -> if True and True -> ok end end, ok),
+ ?line check(fun() -> if False and False -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if True and False -> ok;
+ False and True -> ok;
+ False and False -> ok
+ end),
+ exit
+ end, exit),
+
+ %% 'and' combinations of true/false and a non-boolean in variables.
+
+ Glurf = id(glurf),
+
+ ?line check(fun() -> if True and Glurf -> ok; true -> error end end, error),
+ ?line check(fun() -> if Glurf and True -> ok; true -> error end end, error),
+ ?line check(fun() -> if True and True -> ok end end, ok),
+ ?line check(fun() -> if Glurf and Glurf -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if True and Glurf -> ok;
+ Glurf and True -> ok;
+ Glurf and Glurf -> ok
+ end),
+ exit
+ end, exit),
+
+ %% 'and' combinations of true/false with errors.
+ ATuple = id({a,b,c}),
+
+ ?line check(fun() -> if True and element(42, ATuple) -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if element(42, ATuple) and True -> ok;
+ true -> error end end, error),
+ ?line check(fun() -> if True and True -> ok end end, ok),
+ ?line check(fun() -> if element(42, ATuple) and element(42, ATuple) -> ok;
+ true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} =
+ (catch if True and element(42, ATuple) -> ok;
+ element(42, ATuple) and True -> ok;
+ element(42, ATuple) and element(42, ATuple) -> ok
+ end),
+ exit
+ end, exit),
+
+ ?line ok = relprod({'Set',a,b}, {'Set',a,b}),
+
+ ok.
+
+relprod(R1, R2) when (erlang:size(R1) =:= 3) and (erlang:element(1,R1) =:= 'Set'), (erlang:size(R2) =:= 3) and (erlang:element(1,R2) =:= 'Set') ->
+ ok.
+
+
+xor_guard(Config) when is_list(Config) ->
+
+ %% 'xor' combinations of literal true/false.
+ ?line check(fun() -> if true xor false -> ok end end, ok),
+ ?line check(fun() -> if false xor true -> ok end end, ok),
+ ?line check(fun() -> if true xor true -> ok; true -> error end end, error),
+ ?line check(fun() -> if false xor false -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if false xor false -> ok end),
+ exit
+ end, exit),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if true xor true -> ok end),
+ exit
+ end, exit),
+
+
+ %% 'xor' combinations using variables containing true/false.
+
+ True = id(true),
+ False = id(false),
+
+ ?line check(fun() -> if True xor False -> ok end end, ok),
+ ?line check(fun() -> if False xor True -> ok end end, ok),
+ ?line check(fun() -> if True xor True -> ok; true -> error end end, error),
+ ?line check(fun() -> if False xor False -> ok; true -> error end end, error),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if False xor False -> ok end),
+ exit
+ end, exit),
+ ?line check(fun() ->
+ {'EXIT',{if_clause,_}} = (catch if True xor True -> ok end),
+ exit
+ end, exit),
+
+ ok.
+
+more_xor_guards(Config) when is_list(Config) ->
+ True = id(true),
+ False = id(false),
+ ATuple = id({false,true,gurka}),
+
+ ?line check(fun() ->
+ if element(42, ATuple) xor False -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if False xor element(42, ATuple) xor False -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(18, ATuple) xor element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if True xor element(42, ATuple) -> ok;
+ true -> error end
+ end, error),
+
+ ?line check(fun() ->
+ if element(42, ATuple) xor True -> ok;
+ true -> error end
+ end, error),
+ ok.
+
+build_in_guard(Config) when is_list(Config) ->
+ SubBin = <<5.0/float>>,
+ ?line B = <<1,SubBin/binary,3.5/float>>,
+ ?line if
+ B =:= <<1,SubBin/binary,3.5/float>> -> ok
+ end.
+
+old_guard_tests(Config) when list(Config) ->
+ %% Check that all the old guard tests are still recognized.
+ ?line list = og(Config),
+ ?line atom = og(an_atom),
+ ?line binary = og(<<1,2>>),
+ ?line float = og(3.14),
+ ?line integer = og(43),
+ ?line a_function = og(fun() -> ok end),
+ ?line pid = og(self()),
+ ?line reference = og(make_ref()),
+ ?line tuple = og({}),
+
+ ?line number = on(45.333),
+ ?line number = on(-19),
+ ok.
+
+og(V) when atom(V) -> atom;
+og(V) when binary(V) -> binary;
+og(V) when float(V) -> float;
+og(V) when integer(V) -> integer;
+og(V) when function(V) -> a_function;
+og(V) when list(V) -> list;
+og(V) when pid(V) -> pid;
+og(V) when port(V) -> port;
+og(V) when reference(V) -> reference;
+og(V) when tuple(V) -> tuple;
+og(_) -> what.
+
+on(V) when number(V) -> number;
+on(_) -> not_number.
+
+gbif(Config) when is_list(Config) ->
+ ?line error = gbif_1(1, {false,true}),
+ ?line ok = gbif_1(2, {false,true}),
+ ok.
+
+gbif_1(P, T) when element(P, T) -> ok;
+gbif_1(_, _) -> error.
+
+
+t_is_boolean(Config) when is_list(Config) ->
+ ?line true = is_boolean(true),
+ ?line true = is_boolean(false),
+ ?line true = is_boolean(id(true)),
+ ?line true = is_boolean(id(false)),
+
+ ?line false = is_boolean(glurf),
+ ?line false = is_boolean(id(glurf)),
+
+ ?line false = is_boolean([]),
+ ?line false = is_boolean(id([])),
+ ?line false = is_boolean(42),
+ ?line false = is_boolean(id(-42)),
+
+ ?line false = is_boolean(math:pi()),
+ ?line false = is_boolean(384793478934378924978439789873478934897),
+
+ ?line false = is_boolean(id(self())),
+ ?line false = is_boolean(id({x,y,z})),
+ ?line false = is_boolean(id([a,b,c])),
+ ?line false = is_boolean(id(make_ref())),
+ ?line false = is_boolean(id(<<1,2,3>>)),
+
+ ?line ok = bool(true),
+ ?line ok = bool(false),
+ ?line ok = bool(id(true)),
+ ?line ok = bool(id(false)),
+
+ ?line error = bool(glurf),
+ ?line error = bool(id(glurf)),
+
+ ?line error = bool([]),
+ ?line error = bool(id([])),
+ ?line error = bool(42),
+ ?line error = bool(id(-42)),
+
+ ?line error = bool(math:pi()),
+ ?line error = bool(384793478934378924978439789873478934897),
+
+ ?line error = bool(id(self())),
+ ?line error = bool(id({x,y,z})),
+ ?line error = bool(id([a,b,c])),
+ ?line error = bool(id(make_ref())),
+ ?line error = bool(id(<<1,2,3>>)),
+
+ ok.
+
+bool(X) when is_boolean(X) -> ok;
+bool(_) -> error.
+
+
+is_function_2(Config) when is_list(Config) ->
+ true = is_function(id(fun ?MODULE:all/1), 1),
+ true = is_function(id(fun() -> ok end), 0),
+ false = is_function(id(fun ?MODULE:all/1), 0),
+ false = is_function(id(fun() -> ok end), 1),
+
+ F = fun(_) -> ok end,
+ if
+ is_function(F, 1) -> ok
+ end.
+
+tricky(Config) when is_list(Config) ->
+ ?line not_ok = tricky_1(1, 2),
+ ?line not_ok = tricky_1(1, blurf),
+ ?line not_ok = tricky_1(foo, 2),
+ ?line not_ok = tricky_1(a, b),
+
+ ?line false = rb(100000, [1], 42),
+ ?line true = rb(100000, [], 42),
+ ?line true = rb(555, [a,b,c], 19),
+ ok.
+
+tricky_1(X, Y) when abs((X == 1) or (Y == 2)) -> ok;
+tricky_1(_, _) -> not_ok.
+
+%% From dets_v9:read_buckets/11, simplified.
+
+rb(Size, ToRead, SoFar) when SoFar + Size < 81920; ToRead == [] -> true;
+rb(_, _, _) -> false.
+
+
+-define(T(Op,A,B),
+ ok = if A Op B -> ok; true -> error end,
+ ok = if not (A Op B) -> error; true -> ok end,
+ (fun(X, Y, True, False) ->
+ ok = if X Op Y -> ok; true -> error end,
+ ok = if False; X Op Y; False -> ok; true -> error end,
+ ok = if X Op Y, True -> ok; true -> error end,
+ ok = if not (X Op Y) -> error; true -> ok end,
+ ok = if False; not (X Op Y); False -> error; true -> ok end
+ end)(id(A), id(B), id(true), id(false))).
+
+-define(F(Op,A,B),
+ ok = if A Op B -> error; true -> ok end,
+ ok = if not (A Op B) -> ok; true -> error end,
+ (fun(X, Y, True, False) ->
+ ok = if X Op Y -> error; true -> ok end,
+ ok = if False; X Op Y; False -> error; true -> ok end,
+ ok = if not (X Op Y); False -> ok; true -> error end,
+ ok = if not (X Op Y), True -> ok; true -> error end
+ end)(id(A), id(B), id(true), id(false))).
+
+
+rel_ops(Config) when is_list(Config) ->
+ ?line ?T(=/=, 1, 1.0),
+ ?line ?F(=/=, 2, 2),
+ ?line ?F(=/=, {a}, {a}),
+
+ ?line ?F(/=, a, a),
+ ?line ?F(/=, 0, 0.0),
+ ?line ?T(/=, 0, 1),
+ ?line ?F(/=, {a}, {a}),
+
+ ?line ?T(==, 1, 1.0),
+ ?line ?F(==, a, {}),
+
+ ?line ?F(=:=, 1, 1.0),
+ ?line ?T(=:=, 42.0, 42.0),
+
+ ?line ?F(>, a, b),
+ ?line ?T(>, 42, 1.0),
+ ?line ?F(>, 42, 42.0),
+
+ ?line ?T(<, a, b),
+ ?line ?F(<, 42, 1.0),
+ ?line ?F(<, 42, 42.0),
+
+ ?line ?T(=<, 1.5, 5),
+ ?line ?F(=<, -9, -100.344),
+ ?line ?T(=<, 42, 42.0),
+
+ ?line ?T(>=, 42, 42.0),
+ ?line ?F(>=, a, b),
+ ?line ?T(>=, 1.0, 0),
+
+ ok.
+
+-undef(TestOp).
+
+basic_andalso_orelse(Config) when is_list(Config) ->
+ ?line T = id({type,integers,23,42}),
+ ?line 65 = if
+ ((element(1, T) =:= type) andalso (size(T) =:= 4) andalso
+ element(2, T) == integers) ->
+ element(3, T) + element(4, T);
+ true -> error
+ end,
+ ?line 65 = case [] of
+ [] when ((element(1, T) =:= type) andalso (size(T) =:= 4) andalso
+ element(2, T) == integers) ->
+ element(3, T) + element(4, T)
+ end,
+
+ ?line 42 = basic_rt({type,integers,40,2}),
+ ?line 5.0 = basic_rt({vector,{3.0,4.0}}),
+ ?line 20 = basic_rt(['+',3,7]),
+ ?line {'Set',a,b} = basic_rt({{'Set',a,b},{'Set',a,b}}),
+ ?line 12 = basic_rt({klurf,4}),
+
+ ?line error = basic_rt({type,integers,40,2,3}),
+ ?line error = basic_rt({kalle,integers,40,2}),
+ ?line error = basic_rt({kalle,integers,40,2}),
+ ?line error = basic_rt({1,2}),
+ ?line error = basic_rt([]),
+
+ RelProdBody =
+ fun(R1, R2) ->
+ if
+ (erlang:size(R1) =:= 3) andalso (erlang:element(1,R1) =:= 'Set'),
+ (erlang:size(R2) =:= 3) andalso (erlang:element(1,R2) =:= 'Set') ->
+ ok
+ end
+ end,
+
+ ?line ok = RelProdBody({'Set',a,b}, {'Set',a,b}),
+ ok.
+
+basic_rt(T) when is_tuple(T) andalso size(T) =:= 4 andalso element(1, T) =:= type andalso
+ element(2, T) == integers ->
+ element(3, T) + element(4, T);
+basic_rt(T) when is_tuple(T) andalso size(T) =:= 2 andalso element(1, T) =:= vector ->
+ {X,Y} = element(2, T),
+ if
+ is_float(X), is_float(Y) ->
+ math:sqrt(X*X+Y*Y)
+ end;
+basic_rt(['+',A,B]) ->
+ 2*id(A+B);
+basic_rt({R1,R2}) when erlang:size(R1) =:= 3 andalso erlang:element(1,R1) =:= 'Set',
+ erlang:size(R2) =:= 3 andalso erlang:element(1,R2) =:= 'Set' ->
+ R1 = id(R1),
+ R2 = id(R2),
+ R1;
+basic_rt(T) when is_tuple(T) andalso size(T) =:= 2 andalso element(1, T) =:= klurf ->
+ 3*id(element(2, T));
+basic_rt(_) ->
+ error.
+
+traverse_dcd(Config) when is_list(Config) ->
+ L0 = [{log_header,dcd_log,"1.0",a,b,c},{log_header,dcd_log,"2.0",a,b,c},
+ {log_header,dcd_log,"0.0",a,b,c},blurf],
+ {cont,[{log_header,dcd_log,"0.0",a,b,c},blurf],log,funny} =
+ traverse_dcd({cont,L0}, log, funny),
+ L1 = [{log_header,dcd_log,"1.0"}],
+ {cont,L1,log,funny} = traverse_dcd({cont,L1}, log, funny),
+ L2 = [{a,tuple}],
+ {cont,L2,log,funny} = traverse_dcd({cont,L2}, log, funny),
+ ok.
+
+%% The function starts out with 3 arguments in {x,0}, {x,1}, {x,2}.
+%% The outer match of a two tuple will places the first element in {x,3} and
+%% second in {x,4}. The guard for the first clause must make ensure that all of those
+%% registers are restored befor entering the second clause.
+%%
+%% (From mnesia_checkpoint.erl, modified.)
+
+traverse_dcd({Cont,[LogH|Rest]},Log,Fun)
+ when is_tuple(LogH) andalso size(LogH) =:= 6 andalso element(1, LogH) =:= log_header
+andalso erlang:element(2,LogH) == dcd_log,
+is_tuple(LogH) andalso size(LogH) =:= 6 andalso element(1, LogH) =:= log_header
+andalso erlang:element(3,LogH) >= "1.0" ->
+ traverse_dcd({Cont,Rest},Log,Fun);
+traverse_dcd({Cont,Recs},Log,Fun) ->
+ {Cont,Recs,Log,Fun}.
+
+
+check_qlc_hrl(Config) when is_list(Config) ->
+ St = {r1,false,dum},
+ ?line foo = cqlc(qlc, q, [{lc,1,2,3}], St),
+ ?line foo = cqlc(qlc, q, [{lc,1,2,3},b], St),
+ ?line St = cqlc(qlc, q, [], St),
+ ?line St = cqlc(qlc, blurf, [{lc,1,2,3},b], St),
+ ?line St = cqlc(q, q, [{lc,1,2,3},b], St),
+ ?line St = cqlc(qlc, q, [{lc,1,2,3},b,c], St),
+ ?line St = cqlc(qlc, q, [a,b], St),
+ ?line {r1,true,kalle} = cqlc(qlc, q, [{lc,1,2,3},b], {r1,true,kalle}),
+ ok.
+
+%% From erl_lint.erl; original name was check_qlc_hrl/4.
+cqlc(M, F, As, St) ->
+ Arity = length(As),
+ case As of
+ [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q,
+ Arity < 3,
+ not (((element(1, St) =:= r1) orelse fail) and (size(St) =:= 3) and element(2, St)) ->
+ foo;
+ _ ->
+ St
+ end.
+
+
+
+%% Call this function to turn off constant propagation.
+id(I) -> I.
+
+check(F, Result) ->
+ case F() of
+ Result -> ok;
+ Other ->
+ io:format("Expected: ~p\n", [Result]),
+ io:format(" Got: ~p\n", [Other]),
+ test_server:fail()
+ end.
diff --git a/lib/debugger/test/int_SUITE.erl b/lib/debugger/test/int_SUITE.erl
new file mode 100644
index 0000000000..6e9e81bc52
--- /dev/null
+++ b/lib/debugger/test/int_SUITE.erl
@@ -0,0 +1,291 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(int_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([interpret/1, guards/1, interpretable/1]).
+-export([ append_1/1, append_2/1, member/1, reverse/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(interpretable, Config) ->
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config];
+init_per_testcase(_Case, Config) ->
+
+ %% Interpret some existing and non-existing modules
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {module, lists1} = int:i(filename:join([DataDir,lists1])),
+ ?line {module, guards} = int:i(filename:join([DataDir,guards])),
+
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(interpretable, Config) ->
+ ?line Dog=?config(watchdog, Config),
+ ?line test_server:timetrap_cancel(Dog),
+ ok;
+end_per_testcase(_Case, Config) ->
+
+ %% Quit interpreting
+ ?line ok = int:n(lists1),
+ ?line ok = int:n(guards),
+
+ ?line Dog=?config(watchdog, Config),
+ ?line test_server:timetrap_cancel(Dog),
+ ?line ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [interpret, guards, {group, list_suite}, interpretable].
+
+groups() ->
+ [{list_suite, [], [{group, append}, reverse, member]},
+ {append, [], [append_1, append_2]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+interpret(suite) ->
+ [];
+interpret(doc) ->
+ ["Interpreting modules"];
+interpret(Config) when is_list(Config) ->
+ ?line int:n(int:interpreted()),
+
+ %% Interpret some existing and non-existing modules
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {module, lists1} = int:i(filename:join([DataDir,lists1])),
+ ?line {module, ordsets1} = int:i(filename:join([DataDir,ordsets1])),
+ ?line error = int:i(non_existent_module),
+
+ %% Check that the interpreter has the right view.
+ ?line ExpectedResult = lists:sort([lists1, ordsets1]),
+ ?line Result = int:interpreted(),
+ ?line ExpectedResult = lists:sort(Result),
+
+ %% Uniterpret the modules.
+ ?line ok = int:n(non_existent_module),
+ ?line ok = int:n(lists1),
+ ?line [ordsets1] = int:interpreted(),
+ ?line ok = int:n("ordsets1"),
+ ?line [] = int:interpreted(),
+
+ ok.
+
+guards(suite) ->
+ [];
+guards(doc) ->
+ "Evaluate guards.";
+guards(Config) when is_list(Config) ->
+ ok = guards:guards().
+
+
+
+
+append_1(suite) ->
+ [];
+append_1(doc) ->
+ [];
+append_1(Config) when is_list(Config) ->
+ ?line test_server:format("In append_1~n"),
+ ?line test_server:format("code:which(lists1)=~p~n",
+ [code:which(lists1)]),
+ ?line test_server:format("lists1:append([a],[b])=~p~n",
+ [spawn_eval(lists1,append,[[a],[b]])]),
+
+ ?line "abcdef"=spawn_eval(lists1,append,[["abc","def"]]),
+ ?line [hej, du,[glade, [bagare]]]=
+ spawn_eval(lists1,append,[[[hej], [du], [[glade, [bagare]]]]]),
+ ?line [10, [elem]]=spawn_eval(lists1,append,[[[10], [[elem]]]]),
+ ok.
+
+append_2(suite) ->
+ [];
+append_2(doc) ->
+ [];
+append_2(Config) when is_list(Config) ->
+ ?line test_server:format("In append_2~n"),
+ ?line test_server:format("code:which(lists1)=~p~n",
+ [code:which(lists1)]),
+
+ ?line "abcdef"=spawn_eval(lists1,append,["abc", "def"]),
+ ?line [hej, du]=spawn_eval(lists1,append,[[hej], [du]]),
+ ?line [10, [elem]]=spawn_eval(lists1,append,[[10], [[elem]]]),
+ ok.
+
+reverse(suite) ->
+ [];
+reverse(doc) ->
+ [];
+reverse(Config) when is_list(Config) ->
+ ?line ok=reverse_test(0),
+ ?line ok=reverse_test(1),
+ ?line ok=reverse_test(2),
+ ?line ok=reverse_test(537),
+ ok.
+
+reverse_test(0) ->
+ case spawn_eval(lists1,reverse,[[]]) of
+ [] ->
+ ok;
+ _Other ->
+ error
+ end;
+reverse_test(Num) ->
+ List=spawn_eval(lists1,reverse,
+ [['The Element'|lists1:duplicate(Num, 'Ele')]]),
+ case spawn_eval(lists1,reverse,[List]) of
+ ['The Element'|_Rest] ->
+ ok;
+ _Other ->
+ error
+ end.
+
+member(suite) ->
+ [];
+member(doc) ->
+ ["Tests the lists1:member() implementation. The function "
+ "is `non-blocking', and only processes 2000 elements "
+ "at a time.",
+ "This test case depends on lists1:reverse() to work, "
+ "wich is tested in a separate test case."];
+member(Config) when list(Config) ->
+ ?line ok=member_test(0),
+ ?line ok=member_test(1),
+ ?line ok=member_test(100),
+ ?line ok=member_test(537),
+ ok.
+
+member_test(0) ->
+ case spawn_eval(lists1,member,['The Element', []]) of
+ false ->
+ ok;
+ true ->
+ {error, 'Found (!?)'}
+ end;
+member_test(Num) ->
+ List=spawn_eval(lists1,reverse,
+ [['The Element'|spawn_eval(lists1,duplicate,
+ [Num, 'Elem'])]]),
+ case spawn_eval(lists1,member,['The Element', List]) of
+ true ->
+ ok;
+ false ->
+ {error, not_found}
+ end.
+
+spawn_eval(M,F,A) ->
+ Self = self(),
+ spawn(fun() -> evaluator(Self, M,F,A) end),
+ receive
+ Result ->
+ Result
+ end.
+
+evaluator(Pid, M,F,A) ->
+ Pid ! (catch apply(M,F,A)).
+
+interpretable(suite) ->
+ [];
+interpretable(doc) ->
+ ["Test int:interpretable/1"];
+interpretable(Config) when is_list(Config) ->
+
+ %% First make sure that 'lists1' is not loaded
+ case code:is_loaded(lists1) of
+ {file, _Loaded} ->
+ ?line code:purge(lists1),
+ ?line code:delete(lists1),
+ ?line code:purge(lists1);
+ false -> ignore
+ end,
+
+ %% true
+ ?line DataDir = filename:dirname(?config(data_dir, Config)),
+ ?line true = code:add_patha(DataDir),
+ ?line true = int:interpretable(lists1),
+ ?line true = int:interpretable(filename:join([DataDir,lists1])),
+ ?line true = code:del_path(DataDir),
+
+ %% {error, no_src}
+ ?line PrivDir = filename:join(?config(priv_dir, Config), ""),
+ ?line {ok, _} = file:copy(filename:join([DataDir,"lists1.beam"]),
+ filename:join([PrivDir,"lists1.beam"])),
+ ?line true = code:add_patha(PrivDir),
+
+ ?line {error, no_src} = int:interpretable(lists1),
+ ?line ok = file:delete(filename:join([PrivDir,"lists1.beam"])),
+
+ %% {error, no_beam}
+ Src = filename:join([PrivDir,"lists1.erl"]),
+ ?line {ok, _} = file:copy(filename:join([DataDir,"lists1.erl"]),
+ Src),
+ ?line {error, no_beam} = int:interpretable(Src),
+
+ %% {error, no_debug_info}
+ ?line {ok, _} = compile:file(Src, [{outdir,PrivDir}]),
+ ?line {error, no_debug_info} = int:interpretable(Src),
+ ?line {error, no_debug_info} = int:interpretable(lists1),
+ ?line ok = file:delete(Src),
+ ?line true = code:del_path(PrivDir),
+
+ %% {error, badarg}
+ ?line {error, badarg} = int:interpretable(pride),
+ ?line {error, badarg} = int:interpretable("prejudice.erl"),
+
+ %% {error, {app,App}}
+ ?line {error, {app,_}} = int:interpretable(file),
+ ?line {error, {app,_}} = int:interpretable(lists),
+ ?line {error, {app,_}} = int:interpretable(gs),
+ ?line case int:interpretable(dbg_ieval) of
+ {error, {app,_}} ->
+ ok;
+ {error, badarg} ->
+ case code:which(dbg_ieval) of
+ cover_compiled ->
+ ok;
+ Other1 ->
+ ?line ?t:fail({unexpected_result, Other1})
+ end;
+ Other2 ->
+ ?line ?t:fail({unexpected_result, Other2})
+ end,
+
+ ok.
diff --git a/lib/debugger/test/int_SUITE_data/Makefile.src b/lib/debugger/test/int_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..95b96b5d00
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/Makefile.src
@@ -0,0 +1,39 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+EFLAGS=+debug_info
+all: guards.@EMULATOR@ lists1.@EMULATOR@ my_lists.@EMULATOR@ \
+ ordsets1.@EMULATOR@ test.@EMULATOR@ test1.@EMULATOR@
+
+guards.@EMULATOR@: guards.erl
+ erlc $(EFLAGS) guards.erl
+
+lists1.@EMULATOR@: lists1.erl
+ erlc $(EFLAGS) lists1.erl
+
+my_lists.@EMULATOR@: my_lists.erl
+ erlc $(EFLAGS) my_lists.erl
+
+ordsets1.@EMULATOR@: ordsets1.erl
+ erlc $(EFLAGS) ordsets1.erl
+
+test.@EMULATOR@: test.erl
+ erlc $(EFLAGS) test.erl
+
+test1.@EMULATOR@: test1.erl
+ erlc $(EFLAGS) test1.erl
diff --git a/lib/debugger/test/int_SUITE_data/guards.erl b/lib/debugger/test/int_SUITE_data/guards.erl
new file mode 100644
index 0000000000..c847bb6a8c
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/guards.erl
@@ -0,0 +1,108 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(guards).
+
+-export([guards/0]).
+
+guards() ->
+ ok = t(),
+ ok = f(),
+ ok = ct(1),
+ ok = multi(1),
+ ok = multi(2),
+ ok = multi(3).
+
+%% The following tests are always true.
+t() when integer(42) ->
+ ok;
+t() when float(2.0) ->
+ ok;
+t() when number(7) ->
+ ok;
+t() when number(3.14) ->
+ ok;
+t() when atom(error) ->
+ ok;
+t() when list([a]) ->
+ ok;
+t() when tuple({}) ->
+ ok;
+t() when tuple({1, 2}) ->
+ ok.
+
+%% The following tests are always false.
+f() when integer(a) ->
+ ok;
+f() when float(b) ->
+ ok;
+f() when number(c) ->
+ ok;
+f() when atom(42) ->
+ ok;
+f() when list(33) ->
+ ok;
+f() when list({}) ->
+ ok;
+f() when list({1, 2}) ->
+ ok;
+f() when tuple(33) ->
+ ok;
+f() when tuple([a]) ->
+ ok;
+f() when tuple([]) ->
+ ok;
+f() when tuple(35) ->
+ ok;
+f() ->
+ ok.
+
+%% The following tests are always true.
+ct(X) ->
+ case X of
+ Y when integer(42) ->
+ ok;
+ Y when float(2.0) ->
+ ok;
+ Y when number(7) ->
+ ok;
+ Y when number(3.14) ->
+ ok;
+ Y when atom(error) ->
+ ok;
+ Y when list([a]) ->
+ ok;
+ Y when tuple({}) ->
+ ok;
+ Y when tuple({1, 2}) ->
+ ok
+ end.
+
+multi(X) ->
+ case X of
+ Y when float(Y) ; integer(Y) ->
+ ok;
+ Y when Y > 1, Y < 10 ; atom(Y) ->
+ ok;
+ Y when Y == 4, number(Y) ; list(Y) ->
+ pannkaka;
+ Y when Y==3 ; Y==5 ; Y==6 ->
+ ok
+ end.
diff --git a/lib/debugger/test/int_SUITE_data/lists1.erl b/lib/debugger/test/int_SUITE_data/lists1.erl
new file mode 100644
index 0000000000..0214983c11
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/lists1.erl
@@ -0,0 +1,469 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Purpose : Basic lists processing functions.
+
+-module(lists1).
+
+
+-export([member/2, append/2, append/1, subtract/2, reverse/1, reverse/2,
+ nth/2, nthtail/2, prefix/2, suffix/2, last/1,
+ seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
+ delete/2, sort/1, merge/2, concat/1,
+ flatten/1, flatten/2, flat_length/1, flatlength/1,
+ keymember/3, keysearch/3, keydelete/3, keyreplace/4,
+ keysort/2, keymerge/3, keymap/3, keymap/4]).
+
+-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,zf/2,
+ mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2]).
+-export([all/3,any/3,map/3,flatmap/3,foldl/4,foldr/4,filter/3,zf/3,
+ mapfoldl/4,mapfoldr/4,foreach/3]).
+
+%% member(X, L) -> (true | false)
+%% test if X is a member of the list L
+
+member(X, [X|_]) -> true;
+member(X, [_|Y]) ->
+ member(X, Y);
+member(X, []) -> false.
+
+%% append(X, Y) appends lists X and Y
+
+append(L1, L2) -> L1 ++ L2.
+
+%% append(L) appends the list of lists L
+
+append([E]) -> E;
+append([H|T]) -> H ++ append(T);
+append([]) -> [].
+
+%% subtract(List1, List2) subtract elements in List2 form List1.
+
+subtract(L1, L2) -> L1 -- L2.
+
+%% reverse(L) reverse all elements in the list L
+
+reverse(X) -> reverse(X, []).
+
+reverse([H|T], Y) ->
+ reverse(T, [H|Y]);
+reverse([], X) -> X.
+
+%% nth(N, L) returns the N`th element of the list L
+%% nthtail(N, L) returns the N`th tail of the list L
+
+nth(1, [H|T]) -> H;
+nth(N, [_|T]) when N > 1 ->
+ nth(N - 1, T).
+
+nthtail(1, [H|T]) -> T;
+nthtail(N, [H|T]) when N > 1 ->
+ nthtail(N - 1, T);
+nthtail(0, L) when list(L) -> L.
+
+%% prefix(Prefix, List) -> (true | false)
+
+prefix([X|PreTail], [X|Tail]) ->
+ prefix(PreTail, Tail);
+prefix([], List) -> true;
+prefix(_,_) -> false.
+
+
+%% suffix(Suffix, List) -> (true | false)
+
+suffix(Suffix, Suffix) -> true;
+suffix(Suffix, [_|Tail]) ->
+ suffix(Suffix, Tail);
+suffix(Suffix, []) -> false.
+
+%% last(List) returns the last element in a list.
+
+last([E]) -> E;
+last([E|Es]) ->
+ last(Es).
+
+%% seq(Min, Max) -> [Min,Min+1, ..., Max]
+%% seq(Min, Max, Incr) -> [Min,Min+Incr, ..., Max]
+%% returns the sequence Min..Max
+%% Min <= Max and Min and Max must be integers
+
+seq(Min, Max) when integer(Min), integer(Max), Min =< Max ->
+ seq(Min, Max, 1, []).
+
+seq(Min, Max, Incr) ->
+ seq(Min, Min + ((Max-Min) div Incr) * Incr, Incr, []).
+
+seq(Min, Min, I, L) -> [Min|L];
+seq(Min, Max, I, L) -> seq(Min, Max-I, I, [Max|L]).
+
+%% sum(L) suns the sum of the elements in L
+
+sum(L) -> sum(L, 0).
+sum([H|T], Sum) -> sum(T, Sum + H);
+sum([], Sum) -> Sum.
+
+%% duplicate(N, X) -> [X,X,X,.....,X] (N times)
+%% return N copies of X
+
+duplicate(N, X) when integer(N), N >= 0 -> duplicate(N, X, []).
+
+duplicate(0, _, L) -> L;
+duplicate(N, X, L) -> duplicate(N-1, X, [X|L]).
+
+
+%% min(L) -> returns the minimum element of the list L
+
+min([H|T]) -> min(T, H).
+
+min([H|T], Min) when H < Min -> min(T, H);
+min([_|T], Min) -> min(T, Min);
+min([], Min) -> Min.
+
+%% max(L) -> returns the maximum element of the list L
+
+max([H|T]) -> max(T, H).
+
+max([H|T], Max) when H > Max -> max(T, H);
+max([_|T], Max) -> max(T, Max);
+max([], Max) -> Max.
+
+%% sublist(List, Start, Length)
+%% Returns the sub-list starting at Start of length Length.
+
+sublist(List, S, L) when L >= 0 ->
+ sublist(nthtail(S-1, List), L).
+
+sublist([H|T], L) when L > 0 ->
+ [H|sublist(T, L-1)];
+sublist(List, L) -> [].
+
+%% delete(Item, List) -> List'
+%% Delete the first occurance of Item from the list L.
+
+delete(Item, [Item|Rest]) -> Rest;
+delete(Item, [H|Rest]) ->
+ [H|delete(Item, Rest)];
+delete(Item, []) -> [].
+
+%% sort(L) -> sorts the list L
+
+sort([X]) -> [X];
+sort([]) -> [];
+sort(X) -> split_and_sort(X, [], []).
+
+split_and_sort([A,B|T], X, Y) ->
+ split_and_sort(T, [A|X], [B|Y]);
+split_and_sort([H], X, Y) ->
+ split_and_sort([], [H|X], Y);
+split_and_sort([], X, Y) ->
+ merge(sort(X), sort(Y), []).
+
+%% merge(X, Y) -> L
+%% merges two sorted lists X and Y
+
+merge(X, Y) -> merge(X, Y, []).
+
+merge([H1|T1], [H2|T2], L) when H1 < H2 ->
+ merge(T1, [H2|T2], [H1|L]);
+merge(T1, [H2|T2], L) ->
+ merge(T1, T2, [H2|L]);
+merge([H|T], T2, L) ->
+ merge(T, T2, [H|L]);
+merge([], [], L) ->
+ reverse(L).
+
+%% concat(L) concatinate the list representation of the elements
+%% in L - the elements in L can be atoms, integers of strings.
+%% Returns a list of characters.
+
+concat(List) ->
+ flatmap(fun thing_to_list/1, List).
+
+thing_to_list(X) when integer(X) -> integer_to_list(X);
+thing_to_list(X) when float(X) -> float_to_list(X);
+thing_to_list(X) when atom(X) -> atom_to_list(X);
+thing_to_list(X) when list(X) -> X. %Assumed to be a string
+
+%% flatten(List)
+%% flatten(List, Tail)
+%% Flatten a list, adding optional tail.
+
+flatten(List) ->
+ flatten(List, [], []).
+
+flatten(List, Tail) ->
+ flatten(List, [], Tail).
+
+flatten([H|T], Cont, Tail) when list(H) ->
+ flatten(H, [T|Cont], Tail);
+flatten([H|T], Cont, Tail) ->
+ [H|flatten(T, Cont, Tail)];
+flatten([], [H|Cont], Tail) ->
+ flatten(H, Cont, Tail);
+flatten([], [], Tail) ->
+ Tail.
+
+%% flat_length(List) (undocumented can be rmove later)
+%% Calculate the length of a list of lists.
+
+flat_length(List) -> flatlength(List).
+
+%% flatlength(List)
+%% Calculate the length of a list of lists.
+
+flatlength(List) ->
+ flatlength(List, 0).
+
+flatlength([H|T], L) when list(H) ->
+ flatlength(H, flatlength(T, L));
+flatlength([H|T], L) ->
+ flatlength(T, L + 1);
+flatlength([], L) -> L.
+
+%% keymember(Key, Index, [Tuple])
+%% keysearch(Key, Index, [Tuple])
+%% keydelete(Key, Index, [Tuple])
+%% keyreplace(Key, Index, [Tuple], NewTuple)
+%% keysort(Index, [Tuple])
+%% keymerge(Index, [Tuple], [Tuple])
+%% keymap(Function, Index, [Tuple])
+%% keymap(Function, ExtraArgs, Index, [Tuple])
+
+keymember(Key, N, [T|Ts]) when element(N, T) == Key -> true;
+keymember(Key, N, [T|Ts]) ->
+ keymember(Key, N, Ts);
+keymember(Key, N, []) -> false.
+
+keysearch(Key, N, [H|T]) when element(N, H) == Key ->
+ {value, H};
+keysearch(Key, N, [H|T]) ->
+ keysearch(Key, N, T);
+keysearch(Key, N, []) -> false.
+
+keydelete(Key, N, [H|T]) when element(N, H) == Key -> T;
+keydelete(Key, N, [H|T]) ->
+ [H|keydelete(Key, N, T)];
+keydelete(Key, N, []) -> [].
+
+keyreplace(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
+ [New|Tail];
+keyreplace(Key, Pos, [H|T], New) ->
+ [H|keyreplace(Key, Pos, T, New)];
+keyreplace(Key, Pos, [], New) -> [].
+
+keysort(Index, [X]) -> [X];
+keysort(Index, []) -> [];
+keysort(Index, X) -> split_and_keysort(X, [], [], Index).
+
+split_and_keysort([A,B|T], X, Y, Index) ->
+ split_and_keysort(T, [A|X], [B|Y], Index);
+split_and_keysort([H], X, Y, Index) ->
+ split_and_keysort([], [H|X], Y, Index);
+split_and_keysort([], X, Y, Index) ->
+ keymerge(Index, keysort(Index, X), keysort(Index, Y), []).
+
+keymerge(Index, X, Y) -> keymerge(Index, X, Y, []).
+
+keymerge(I, [H1|T1], [H2|T2], L) when element(I, H1) < element(I, H2) ->
+ keymerge(I, T1, [H2|T2], [H1|L]);
+keymerge(Index, T1, [H2|T2], L) ->
+ keymerge(Index,T1, T2, [H2|L]);
+keymerge(Index,[H|T], T2, L) ->
+ keymerge(Index,T, T2, [H|L]);
+keymerge(Index, [], [], L) ->
+ reverse(L).
+
+keymap(Fun, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)];
+keymap( _, _ , []) -> [].
+
+keymap(Fun, ExtraArgs, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, apply(Fun, [element(Index, Tup)|ExtraArgs]))|
+ keymap(Fun, ExtraArgs, Index, Tail)];
+keymap( _, _ , _, []) -> [].
+
+%% all(Predicate, List)
+%% any(Predicate, List)
+%% map(Function, List)
+%% flatmap(Function, List)
+%% foldl(Function, First, List)
+%% foldr(Function, Last, List)
+%% filter(Predicate, List)
+%% zf(Function, List)
+%% mapfoldl(Function, First, List)
+%% mapfoldr(Function, Last, List)
+%% foreach(Function, List)
+%% takewhile(Predicate, List)
+%% dropwhile(Predicate, List)
+%% splitwith(Predicate, List)
+%% for list programming. Function here is either a 'fun' or a tuple
+%% {Module,Name} and we use apply/2 to evaluate. The name zf is a joke!
+%%
+%% N.B. Unless where the functions actually needs it only foreach/2/3,
+%% which is meant to be used for its side effects, has a defined order
+%% of evaluation.
+%%
+%% There are also versions with an extra argument, ExtraArgs, which is a
+%% list of extra arguments to each call.
+
+all(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> all(Pred, Tail);
+ false -> false
+ end;
+all(Pred, []) -> true.
+
+any(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any(Pred, Tail)
+ end;
+any(Pred, []) -> false.
+
+map(F, List) -> [ F(E) || E <- List ].
+
+flatmap(F, [Hd|Tail]) ->
+ F(Hd) ++ flatmap(F, Tail);
+flatmap(F, []) -> [].
+
+foldl(F, Accu, [Hd|Tail]) ->
+ foldl(F, F(Hd, Accu), Tail);
+foldl(F, Accu, []) -> Accu.
+
+foldr(F, Accu, [Hd|Tail]) ->
+ F(Hd, foldr(F, Accu, Tail));
+foldr(F, Accu, []) -> Accu.
+
+filter(Pred, List) -> [ E || E <- List, Pred(E) ].
+
+zf(F, [Hd|Tail]) ->
+ case F(Hd) of
+ true ->
+ [Hd|zf(F, Tail)];
+ {true,Val} ->
+ [Val|zf(F, Tail)];
+ false ->
+ zf(F, Tail)
+ end;
+zf(F, []) -> [].
+
+foreach(F, [Hd|Tail]) ->
+ F(Hd),
+ foreach(F, Tail);
+foreach(F, []) -> ok.
+
+mapfoldl(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = mapfoldl(F, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl(F, Accu, []) -> {[],Accu}.
+
+mapfoldr(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {[R|Rs],Accu2};
+mapfoldr(F, Accu, []) -> {[],Accu}.
+
+takewhile(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> [Hd|takewhile(Pred, Tail)];
+ false -> []
+ end;
+takewhile(Pred, []) -> [].
+
+dropwhile(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> dropwhile(Pred, Tail);
+ false -> [Hd|Tail]
+ end;
+dropwhile(Pred, []) -> [].
+
+splitwith(Pred, List) -> splitwith(Pred, List, []).
+
+splitwith(Pred, [Hd|Tail], Taken) ->
+ case Pred(Hd) of
+ true -> splitwith(Pred, Tail, [Hd|Taken]);
+ false -> {reverse(Taken), [Hd|Tail]}
+ end;
+splitwith(Pred, [], Taken) -> {reverse(Taken),[]}.
+
+%% Versions of the above functions with extra arguments.
+
+all(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> all(Pred, Eas, Tail);
+ false -> false
+ end;
+all(Pred, Eas, []) -> true.
+
+any(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> true;
+ false -> any(Pred, Eas, Tail)
+ end;
+any(Pred, Eas, []) -> false.
+
+map(F, Eas, List) -> [ apply(F, [E|Eas]) || E <- List ].
+
+flatmap(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]) ++ flatmap(F, Eas, Tail);
+flatmap(F, Eas, []) -> [].
+
+foldl(F, Eas, Accu, [Hd|Tail]) ->
+ foldl(F, Eas, apply(F, [Hd,Accu|Eas]), Tail);
+foldl(F, Eas, Accu, []) -> Accu.
+
+foldr(F, Eas, Accu, [Hd|Tail]) ->
+ apply(F, [Hd,foldr(F, Eas, Accu, Tail)|Eas]);
+foldr(F, Eas, Accu, []) ->
+ Accu.
+
+filter(Pred, Eas, List) -> [ E || E <- List, apply(Pred, [E|Eas]) ].
+
+zf(F, Eas, [Hd|Tail]) ->
+ case apply(F, [Hd|Eas]) of
+ true ->
+ [Hd|zf(F, Eas, Tail)];
+ {true,Val} ->
+ [Val|zf(F, Eas, Tail)];
+ false ->
+ zf(F, Eas, Tail)
+ end;
+zf(F, Eas, []) -> [].
+
+foreach(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]),
+ foreach(F, Eas, Tail);
+foreach(F, Eas, []) -> ok.
+
+mapfoldl(F, Eas, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = apply(F, [Hd,Accu0|Eas]),
+ {Rs,Accu2} = mapfoldl(F, Eas, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl(F, Eas, Accu, []) -> {[],Accu}.
+
+mapfoldr(F, Eas, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr(F, Eas, Accu0, Tail),
+ {R,Accu2} = apply(F, [Hd,Accu1|Eas]),
+ {[R|Rs],Accu2};
+mapfoldr(F, Eas, Accu, []) -> {[],Accu}.
+
+%% takewhile/2, dropwhile/2 and splitwith/2 do not have versions with
+%% extra arguments as this going to be discontinued.
diff --git a/lib/debugger/test/int_SUITE_data/my_lists.erl b/lib/debugger/test/int_SUITE_data/my_lists.erl
new file mode 100644
index 0000000000..98eb4396e3
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/my_lists.erl
@@ -0,0 +1,5681 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%% A rather large file to test the attach delay.
+%%% Use only the ordinary lists commands.
+
+-module(my_lists).
+
+
+-export([member/2, append/2, append/1, subtract/2, reverse/1, reverse/2,
+ nth/2, nthtail/2, prefix/2, suffix/2, last/1,
+ seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
+ delete/2, sort/1, merge/2, concat/1,
+ flatten/1, flatten/2, flat_length/1, flatlength/1,
+ keymember/3, keysearch/3, keydelete/3, keyreplace/4,
+ keysort/2, keymerge/3, keymap/3, keymap/4]).
+
+-export([all/2,any/2,map/2,flatmap/2,foldl/3,foldr/3,filter/2,zf/2,
+ mapfoldl/3,mapfoldr/3,foreach/2,takewhile/2,dropwhile/2,splitwith/2]).
+-export([all/3,any/3,map/3,flatmap/3,foldl/4,foldr/4,filter/3,zf/3,
+ mapfoldl/4,mapfoldr/4,foreach/3]).
+
+%% member(X, L) -> (true | false)
+%% test if X is a member of the list L
+
+member(X, [X|_]) -> true;
+member(X, [_|Y]) ->
+ member(X, Y);
+member(X, []) -> false.
+
+%% append(X, Y) appends lists X and Y
+
+append(L1, L2) -> L1 ++ L2.
+
+%% append(L) appends the list of lists L
+
+append([E]) -> E;
+append([H|T]) -> H ++ append(T);
+append([]) -> [].
+
+%% subtract(List1, List2) subtract elements in List2 form List1.
+
+subtract(L1, L2) -> L1 -- L2.
+
+%% reverse(L) reverse all elements in the list L
+
+reverse(X) -> reverse(X, []).
+
+reverse([H|T], Y) ->
+ reverse(T, [H|Y]);
+reverse([], X) -> X.
+
+%% nth(N, L) returns the N`th element of the list L
+%% nthtail(N, L) returns the N`th tail of the list L
+
+nth(1, [H|T]) -> H;
+nth(N, [_|T]) when N > 1 ->
+ nth(N - 1, T).
+
+nthtail(1, [H|T]) -> T;
+nthtail(N, [H|T]) when N > 1 ->
+ nthtail(N - 1, T);
+nthtail(0, L) when list(L) -> L.
+
+%% prefix(Prefix, List) -> (true | false)
+
+prefix([X|PreTail], [X|Tail]) ->
+ prefix(PreTail, Tail);
+prefix([], List) -> true;
+prefix(_,_) -> false.
+
+
+%% suffix(Suffix, List) -> (true | false)
+
+suffix(Suffix, Suffix) -> true;
+suffix(Suffix, [_|Tail]) ->
+ suffix(Suffix, Tail);
+suffix(Suffix, []) -> false.
+
+%% last(List) returns the last element in a list.
+
+last([E]) -> E;
+last([E|Es]) ->
+ last(Es).
+
+%% seq(Min, Max) -> [Min,Min+1, ..., Max]
+%% seq(Min, Max, Incr) -> [Min,Min+Incr, ..., Max]
+%% returns the sequence Min..Max
+%% Min <= Max and Min and Max must be integers
+
+seq(Min, Max) when integer(Min), integer(Max), Min =< Max ->
+ seq(Min, Max, 1, []).
+
+seq(Min, Max, Incr) ->
+ seq(Min, Min + ((Max-Min) div Incr) * Incr, Incr, []).
+
+seq(Min, Min, I, L) -> [Min|L];
+seq(Min, Max, I, L) -> seq(Min, Max-I, I, [Max|L]).
+
+%% sum(L) suns the sum of the elements in L
+
+sum(L) -> sum(L, 0).
+sum([H|T], Sum) -> sum(T, Sum + H);
+sum([], Sum) -> Sum.
+
+%% duplicate(N, X) -> [X,X,X,.....,X] (N times)
+%% return N copies of X
+
+duplicate(N, X) when integer(N), N >= 0 -> duplicate(N, X, []).
+
+duplicate(0, _, L) -> L;
+duplicate(N, X, L) -> duplicate(N-1, X, [X|L]).
+
+
+%% min(L) -> returns the minimum element of the list L
+
+min([H|T]) -> min(T, H).
+
+min([H|T], Min) when H < Min -> min(T, H);
+min([_|T], Min) -> min(T, Min);
+min([], Min) -> Min.
+
+%% max(L) -> returns the maximum element of the list L
+
+max([H|T]) -> max(T, H).
+
+max([H|T], Max) when H > Max -> max(T, H);
+max([_|T], Max) -> max(T, Max);
+max([], Max) -> Max.
+
+%% sublist(List, Start, Length)
+%% Returns the sub-list starting at Start of length Length.
+
+sublist(List, S, L) when L >= 0 ->
+ sublist(nthtail(S-1, List), L).
+
+sublist([H|T], L) when L > 0 ->
+ [H|sublist(T, L-1)];
+sublist(List, L) -> [].
+
+%% delete(Item, List) -> List'
+%% Delete the first occurance of Item from the list L.
+
+delete(Item, [Item|Rest]) -> Rest;
+delete(Item, [H|Rest]) ->
+ [H|delete(Item, Rest)];
+delete(Item, []) -> [].
+
+%% sort(L) -> sorts the list L
+
+sort([X]) -> [X];
+sort([]) -> [];
+sort(X) -> split_and_sort(X, [], []).
+
+split_and_sort([A,B|T], X, Y) ->
+ split_and_sort(T, [A|X], [B|Y]);
+split_and_sort([H], X, Y) ->
+ split_and_sort([], [H|X], Y);
+split_and_sort([], X, Y) ->
+ merge(sort(X), sort(Y), []).
+
+%% merge(X, Y) -> L
+%% merges two sorted lists X and Y
+
+merge(X, Y) -> merge(X, Y, []).
+
+merge([H1|T1], [H2|T2], L) when H1 < H2 ->
+ merge(T1, [H2|T2], [H1|L]);
+merge(T1, [H2|T2], L) ->
+ merge(T1, T2, [H2|L]);
+merge([H|T], T2, L) ->
+ merge(T, T2, [H|L]);
+merge([], [], L) ->
+ reverse(L).
+
+%% concat(L) concatinate the list representation of the elements
+%% in L - the elements in L can be atoms, integers of strings.
+%% Returns a list of characters.
+
+concat(List) ->
+ flatmap(fun thing_to_list/1, List).
+
+thing_to_list(X) when integer(X) -> integer_to_list(X);
+thing_to_list(X) when float(X) -> float_to_list(X);
+thing_to_list(X) when atom(X) -> atom_to_list(X);
+thing_to_list(X) when list(X) -> X. %Assumed to be a string
+
+%% flatten(List)
+%% flatten(List, Tail)
+%% Flatten a list, adding optional tail.
+
+flatten(List) ->
+ flatten(List, [], []).
+
+flatten(List, Tail) ->
+ flatten(List, [], Tail).
+
+flatten([H|T], Cont, Tail) when list(H) ->
+ flatten(H, [T|Cont], Tail);
+flatten([H|T], Cont, Tail) ->
+ [H|flatten(T, Cont, Tail)];
+flatten([], [H|Cont], Tail) ->
+ flatten(H, Cont, Tail);
+flatten([], [], Tail) ->
+ Tail.
+
+%% flat_length(List) (undocumented can be rmove later)
+%% Calculate the length of a list of lists.
+
+flat_length(List) -> flatlength(List).
+
+%% flatlength(List)
+%% Calculate the length of a list of lists.
+
+flatlength(List) ->
+ flatlength(List, 0).
+
+flatlength([H|T], L) when list(H) ->
+ flatlength(H, flatlength(T, L));
+flatlength([H|T], L) ->
+ flatlength(T, L + 1);
+flatlength([], L) -> L.
+
+%% keymember(Key, Index, [Tuple])
+%% keysearch(Key, Index, [Tuple])
+%% keydelete(Key, Index, [Tuple])
+%% keyreplace(Key, Index, [Tuple], NewTuple)
+%% keysort(Index, [Tuple])
+%% keymerge(Index, [Tuple], [Tuple])
+%% keymap(Function, Index, [Tuple])
+%% keymap(Function, ExtraArgs, Index, [Tuple])
+
+keymember(Key, N, [T|Ts]) when element(N, T) == Key -> true;
+keymember(Key, N, [T|Ts]) ->
+ keymember(Key, N, Ts);
+keymember(Key, N, []) -> false.
+
+keysearch(Key, N, [H|T]) when element(N, H) == Key ->
+ {value, H};
+keysearch(Key, N, [H|T]) ->
+ keysearch(Key, N, T);
+keysearch(Key, N, []) -> false.
+
+keydelete(Key, N, [H|T]) when element(N, H) == Key -> T;
+keydelete(Key, N, [H|T]) ->
+ [H|keydelete(Key, N, T)];
+keydelete(Key, N, []) -> [].
+
+keyreplace(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
+ [New|Tail];
+keyreplace(Key, Pos, [H|T], New) ->
+ [H|keyreplace(Key, Pos, T, New)];
+keyreplace(Key, Pos, [], New) -> [].
+
+keysort(Index, [X]) -> [X];
+keysort(Index, []) -> [];
+keysort(Index, X) -> split_and_keysort(X, [], [], Index).
+
+split_and_keysort([A,B|T], X, Y, Index) ->
+ split_and_keysort(T, [A|X], [B|Y], Index);
+split_and_keysort([H], X, Y, Index) ->
+ split_and_keysort([], [H|X], Y, Index);
+split_and_keysort([], X, Y, Index) ->
+ keymerge(Index, keysort(Index, X), keysort(Index, Y), []).
+
+keymerge(Index, X, Y) -> keymerge(Index, X, Y, []).
+
+keymerge(I, [H1|T1], [H2|T2], L) when element(I, H1) < element(I, H2) ->
+ keymerge(I, T1, [H2|T2], [H1|L]);
+keymerge(Index, T1, [H2|T2], L) ->
+ keymerge(Index,T1, T2, [H2|L]);
+keymerge(Index,[H|T], T2, L) ->
+ keymerge(Index,T, T2, [H|L]);
+keymerge(Index, [], [], L) ->
+ reverse(L).
+
+keymap(Fun, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)];
+keymap( _, _ , []) -> [].
+
+keymap(Fun, ExtraArgs, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, apply(Fun, [element(Index, Tup)|ExtraArgs]))|
+ keymap(Fun, ExtraArgs, Index, Tail)];
+keymap( _, _ , _, []) -> [].
+
+%% all(Predicate, List)
+%% any(Predicate, List)
+%% map(Function, List)
+%% flatmap(Function, List)
+%% foldl(Function, First, List)
+%% foldr(Function, Last, List)
+%% filter(Predicate, List)
+%% zf(Function, List)
+%% mapfoldl(Function, First, List)
+%% mapfoldr(Function, Last, List)
+%% foreach(Function, List)
+%% takewhile(Predicate, List)
+%% dropwhile(Predicate, List)
+%% splitwith(Predicate, List)
+%% for list programming. Function here is either a 'fun' or a tuple
+%% {Module,Name} and we use apply/2 to evaluate. The name zf is a joke!
+%%
+%% N.B. Unless where the functions actually needs it only foreach/2/3,
+%% which is meant to be used for its side effects, has a defined order
+%% of evaluation.
+%%
+%% There are also versions with an extra argument, ExtraArgs, which is a
+%% list of extra arguments to each call.
+
+all(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> all(Pred, Tail);
+ false -> false
+ end;
+all(Pred, []) -> true.
+
+any(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any(Pred, Tail)
+ end;
+any(Pred, []) -> false.
+
+map(F, List) -> [ F(E) || E <- List ].
+
+flatmap(F, [Hd|Tail]) ->
+ F(Hd) ++ flatmap(F, Tail);
+flatmap(F, []) -> [].
+
+foldl(F, Accu, [Hd|Tail]) ->
+ foldl(F, F(Hd, Accu), Tail);
+foldl(F, Accu, []) -> Accu.
+
+foldr(F, Accu, [Hd|Tail]) ->
+ F(Hd, foldr(F, Accu, Tail));
+foldr(F, Accu, []) -> Accu.
+
+filter(Pred, List) -> [ E || E <- List, Pred(E) ].
+
+zf(F, [Hd|Tail]) ->
+ case F(Hd) of
+ true ->
+ [Hd|zf(F, Tail)];
+ {true,Val} ->
+ [Val|zf(F, Tail)];
+ false ->
+ zf(F, Tail)
+ end;
+zf(F, []) -> [].
+
+foreach(F, [Hd|Tail]) ->
+ F(Hd),
+ foreach(F, Tail);
+foreach(F, []) -> ok.
+
+mapfoldl(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = mapfoldl(F, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl(F, Accu, []) -> {[],Accu}.
+
+mapfoldr(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {[R|Rs],Accu2};
+mapfoldr(F, Accu, []) -> {[],Accu}.
+
+takewhile(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> [Hd|takewhile(Pred, Tail)];
+ false -> []
+ end;
+takewhile(Pred, []) -> [].
+
+dropwhile(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> dropwhile(Pred, Tail);
+ false -> [Hd|Tail]
+ end;
+dropwhile(Pred, []) -> [].
+
+splitwith(Pred, List) -> splitwith(Pred, List, []).
+
+splitwith(Pred, [Hd|Tail], Taken) ->
+ case Pred(Hd) of
+ true -> splitwith(Pred, Tail, [Hd|Taken]);
+ false -> {reverse(Taken), [Hd|Tail]}
+ end;
+splitwith(Pred, [], Taken) -> {reverse(Taken),[]}.
+
+%% Versions of the above functions with extra arguments.
+
+all(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> all(Pred, Eas, Tail);
+ false -> false
+ end;
+all(Pred, Eas, []) -> true.
+
+any(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> true;
+ false -> any(Pred, Eas, Tail)
+ end;
+any(Pred, Eas, []) -> false.
+
+map(F, Eas, List) -> [ apply(F, [E|Eas]) || E <- List ].
+
+flatmap(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]) ++ flatmap(F, Eas, Tail);
+flatmap(F, Eas, []) -> [].
+
+foldl(F, Eas, Accu, [Hd|Tail]) ->
+ foldl(F, Eas, apply(F, [Hd,Accu|Eas]), Tail);
+foldl(F, Eas, Accu, []) -> Accu.
+
+foldr(F, Eas, Accu, [Hd|Tail]) ->
+ apply(F, [Hd,foldr(F, Eas, Accu, Tail)|Eas]);
+foldr(F, Eas, Accu, []) ->
+ Accu.
+
+filter(Pred, Eas, List) -> [ E || E <- List, apply(Pred, [E|Eas]) ].
+
+zf(F, Eas, [Hd|Tail]) ->
+ case apply(F, [Hd|Eas]) of
+ true ->
+ [Hd|zf(F, Eas, Tail)];
+ {true,Val} ->
+ [Val|zf(F, Eas, Tail)];
+ false ->
+ zf(F, Eas, Tail)
+ end;
+zf(F, Eas, []) -> [].
+
+foreach(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]),
+ foreach(F, Eas, Tail);
+foreach(F, Eas, []) -> ok.
+
+mapfoldl(F, Eas, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = apply(F, [Hd,Accu0|Eas]),
+ {Rs,Accu2} = mapfoldl(F, Eas, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl(F, Eas, Accu, []) -> {[],Accu}.
+
+mapfoldr(F, Eas, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr(F, Eas, Accu0, Tail),
+ {R,Accu2} = apply(F, [Hd,Accu1|Eas]),
+ {[R|Rs],Accu2};
+mapfoldr(F, Eas, Accu, []) -> {[],Accu}.
+
+%% takewhile/2, dropwhile/2 and splitwith/2 do not have versions with
+%% extra arguments as this going to be discontinued.
+
+
+
+
+
+%%% +++++++++++++++++++++++++++++++++++++++
+%%%
+%%% Same as above but with "_1" added to the function names
+%%%
+
+
+%% member_1(X, L) -> (true | false)
+%% test if X is a member of the list L
+
+%member_1(X, [X|_]) -> true;
+%member_1(X, [_|Y]) ->
+% member_1(X, Y);
+%member_1(X, []) -> false.
+
+member_1(X, L) when list(L) ->
+ case erlang:member_1(X, L) of
+ L1 when list(L1) ->
+ receive after 1 -> ok end,
+ member_1(X, L1);
+ Boolean ->
+ Boolean
+ end.
+
+%% append_1(X, Y) appends lists X and Y
+
+append_1(L1, L2) -> L1 ++ L2.
+
+%% append_1(L) appends the list of lists L
+
+append_1([E]) -> E;
+append_1([H|T]) -> H ++ append_1(T);
+append_1([]) -> [].
+
+%% subtract_1(List1, List2) subtract elements in List2 form List1.
+
+subtract_1(L1, L2) -> L1 -- L2.
+
+%% reverse_1(L) reverse all elements in the list L
+
+reverse_1(X) -> reverse_1(X, []).
+
+%reverse_1([H|T], Y) ->
+% reverse_1(T, [H|Y]);
+%reverse_1([], X) -> X.
+
+reverse_1(List0, Result0) when list(List0) ->
+ case erlang:reverse_1(List0, Result0) of
+ {List, Result} ->
+ receive after 1 -> ok end,
+ reverse_1(List, Result);
+ Result ->
+ Result
+ end.
+
+
+%% nth_1(N, L) returns the N`th element of the list L
+%% nthtail_1(N, L) returns the N`th tail of the list L
+
+nth_1(1, [H|T]) -> H;
+nth_1(N, [_|T]) when N > 1 ->
+ nth_1(N - 1, T).
+
+nthtail_1(1, [H|T]) -> T;
+nthtail_1(N, [H|T]) when N > 1 ->
+ nthtail_1(N - 1, T);
+nthtail_1(0, L) when list(L) -> L.
+
+%% prefix_1(Prefix, List) -> (true | false)
+
+prefix_1([X|PreTail], [X|Tail]) ->
+ prefix_1(PreTail, Tail);
+prefix_1([], List) -> true;
+prefix_1(_,_) -> false.
+
+
+%% suffix_1(Suffix, List) -> (true | false)
+
+suffix_1(Suffix, Suffix) -> true;
+suffix_1(Suffix, [_|Tail]) ->
+ suffix_1(Suffix, Tail);
+suffix_1(Suffix, []) -> false.
+
+%% last(List) returns the last element in a list.
+
+last_1([E]) -> E;
+last_1([E|Es]) ->
+ last_1(Es).
+
+%% seq(Min, Max) -> [Min,Min+1, ..., Max]
+%% seq(Min, Max, Incr) -> [Min,Min+Incr, ..., Max]
+%% returns the sequence Min..Max
+%% Min <= Max and Min and Max must be integers
+
+seq_1(Min, Max) when integer(Min), integer(Max), Min =< Max ->
+ seq_1(Min, Max, 1, []).
+
+seq_1(Min, Max, Incr) ->
+ seq_1(Min, Min + ((Max-Min) div Incr) * Incr, Incr, []).
+
+seq_1(Min, Min, I, L) -> [Min|L];
+seq_1(Min, Max, I, L) -> seq_1(Min, Max-I, I, [Max|L]).
+
+%% sum(L) suns the sum of the elements in L
+
+sum_1(L) -> sum_1(L, 0).
+sum_1([H|T], Sum) -> sum_1(T, Sum + H);
+sum_1([], Sum) -> Sum.
+
+%% duplicate(N, X) -> [X,X,X,.....,X] (N times)
+%% return N copies of X
+
+duplicate_1(N, X) when integer(N), N >= 0 -> duplicate_1(N, X, []).
+
+duplicate_1(0, _, L) -> L;
+duplicate_1(N, X, L) -> duplicate_1(N-1, X, [X|L]).
+
+
+%% min(L) -> returns the minimum element of the list L
+
+min_1([H|T]) -> min_1(T, H).
+
+min_1([H|T], Min) when H < Min -> min_1(T, H);
+min_1([_|T], Min) -> min_1(T, Min);
+min_1([], Min) -> Min.
+
+%% max(L) -> returns the maximum element of the list L
+
+max_1([H|T]) -> max_1(T, H).
+
+max_1([H|T], Max) when H > Max -> max_1(T, H);
+max_1([_|T], Max) -> max_1(T, Max);
+max_1([], Max) -> Max.
+
+%% sublist(List, Start, Length)
+%% Returns the sub-list starting at Start of length Length.
+
+sublist_1(List, S, L) when L >= 0 ->
+ sublist_1(nthtail_1(S-1, List), L).
+
+sublist_1([H|T], L) when L > 0 ->
+ [H|sublist_1(T, L-1)];
+sublist_1(List, L) -> [].
+
+%% delete(Item, List) -> List'
+%% Delete the first occurance of Item from the list L.
+
+delete_1(Item, [Item|Rest]) -> Rest;
+delete_1(Item, [H|Rest]) ->
+ [H|delete_1(Item, Rest)];
+delete_1(Item, []) -> [].
+
+%% sort(L) -> sorts the list L
+
+sort_1([X]) -> [X];
+sort_1([]) -> [];
+sort_1(X) -> split_and_sort_1(X, [], []).
+
+split_and_sort_1([A,B|T], X, Y) ->
+ split_and_sort_1(T, [A|X], [B|Y]);
+split_and_sort_1([H], X, Y) ->
+ split_and_sort_1([], [H|X], Y);
+split_and_sort_1([], X, Y) ->
+ merge_1(sort_1(X), sort_1(Y), []).
+
+%% merge(X, Y) -> L
+%% merges two sorted lists X and Y
+
+merge_1(X, Y) -> merge_1(X, Y, []).
+
+merge_1([H1|T1], [H2|T2], L) when H1 < H2 ->
+ merge_1(T1, [H2|T2], [H1|L]);
+merge_1(T1, [H2|T2], L) ->
+ merge_1(T1, T2, [H2|L]);
+merge_1([H|T], T2, L) ->
+ merge_1(T, T2, [H|L]);
+merge_1([], [], L) ->
+ reverse_1(L).
+
+%% concat(L) concatinate the list representation of the elements
+%% in L - the elements in L can be atoms, integers of strings.
+%% Returns a list of characters.
+
+concat_1(List) ->
+ flatmap_1(fun thing_to_list/1, List).
+
+thing_to_list_1(X) when integer(X) -> integer_to_list(X);
+thing_to_list_1(X) when float(X) -> float_to_list(X);
+thing_to_list_1(X) when atom(X) -> atom_to_list(X);
+thing_to_list_1(X) when list(X) -> X. %Assumed to be a string
+
+%% flatten(List)
+%% flatten(List, Tail)
+%% Flatten a list, adding optional tail.
+
+flatten_1(List) ->
+ flatten_1(List, [], []).
+
+flatten_1(List, Tail) ->
+ flatten_1(List, [], Tail).
+
+flatten_1([H|T], Cont, Tail) when list(H) ->
+ flatten_1(H, [T|Cont], Tail);
+flatten_1([H|T], Cont, Tail) ->
+ [H|flatten_1(T, Cont, Tail)];
+flatten_1([], [H|Cont], Tail) ->
+ flatten_1(H, Cont, Tail);
+flatten_1([], [], Tail) ->
+ Tail.
+
+%% flat_length(List) (undocumented can be rmove later)
+%% Calculate the length of a list of lists.
+
+flat_length_1(List) -> flatlength_1(List).
+
+%% flatlength(List)
+%% Calculate the length of a list of lists.
+
+flatlength_1(List) ->
+ flatlength_1(List, 0).
+
+flatlength_1([H|T], L) when list(H) ->
+ flatlength_1(H, flatlength_1(T, L));
+flatlength_1([H|T], L) ->
+ flatlength_1(T, L + 1);
+flatlength_1([], L) -> L.
+
+%% keymember(Key, Index, [Tuple])
+%% keysearch(Key, Index, [Tuple])
+%% keydelete(Key, Index, [Tuple])
+%% keyreplace(Key, Index, [Tuple], NewTuple)
+%% keysort(Index, [Tuple])
+%% keymerge(Index, [Tuple], [Tuple])
+%% keymap(Function, Index, [Tuple])
+%% keymap(Function, ExtraArgs, Index, [Tuple])
+
+keymember_1(Key, N, [T|Ts]) when element(N, T) == Key -> true;
+keymember_1(Key, N, [T|Ts]) ->
+ keymember_1(Key, N, Ts);
+keymember_1(Key, N, []) -> false.
+
+keysearch_1(Key, N, [H|T]) when element(N, H) == Key ->
+ {value, H};
+keysearch_1(Key, N, [H|T]) ->
+ keysearch_1(Key, N, T);
+keysearch_1(Key, N, []) -> false.
+
+keydelete_1(Key, N, [H|T]) when element(N, H) == Key -> T;
+keydelete_1(Key, N, [H|T]) ->
+ [H|keydelete_1(Key, N, T)];
+keydelete_1(Key, N, []) -> [].
+
+keyreplace_1(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
+ [New|Tail];
+keyreplace_1(Key, Pos, [H|T], New) ->
+ [H|keyreplace_1(Key, Pos, T, New)];
+keyreplace_1(Key, Pos, [], New) -> [].
+
+keysort_1(Index, [X]) -> [X];
+keysort_1(Index, []) -> [];
+keysort_1(Index, X) -> split_and_keysort_1(X, [], [], Index).
+
+split_and_keysort_1([A,B|T], X, Y, Index) ->
+ split_and_keysort_1(T, [A|X], [B|Y], Index);
+split_and_keysort_1([H], X, Y, Index) ->
+ split_and_keysort_1([], [H|X], Y, Index);
+split_and_keysort_1([], X, Y, Index) ->
+ keymerge_1(Index, keysort_1(Index, X), keysort_1(Index, Y), []).
+
+keymerge_1(Index, X, Y) -> keymerge_1(Index, X, Y, []).
+
+keymerge_1(I, [H1|T1], [H2|T2], L) when element(I, H1) < element(I, H2) ->
+ keymerge_1(I, T1, [H2|T2], [H1|L]);
+keymerge_1(Index, T1, [H2|T2], L) ->
+ keymerge_1(Index,T1, T2, [H2|L]);
+keymerge_1(Index,[H|T], T2, L) ->
+ keymerge_1(Index,T, T2, [H|L]);
+keymerge_1(Index, [], [], L) ->
+ reverse_1(L).
+
+keymap_1(Fun, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap_1(Fun, Index, Tail)];
+keymap_1( _, _ , []) -> [].
+
+keymap_1(Fun, ExtraArgs, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, apply(Fun, [element(Index, Tup)|ExtraArgs]))|
+ keymap_1(Fun, ExtraArgs, Index, Tail)];
+keymap_1( _, _ , _, []) -> [].
+
+%% all(Predicate, List)
+%% any(Predicate, List)
+%% map(Function, List)
+%% flatmap(Function, List)
+%% foldl(Function, First, List)
+%% foldr(Function, Last, List)
+%% filter(Predicate, List)
+%% zf(Function, List)
+%% mapfoldl(Function, First, List)
+%% mapfoldr(Function, Last, List)
+%% foreach(Function, List)
+%% takewhile(Predicate, List)
+%% dropwhile(Predicate, List)
+%% splitwith(Predicate, List)
+%% for list programming. Function here is either a 'fun' or a tuple
+%% {Module,Name} and we use apply/2 to evaluate. The name zf is a joke!
+%%
+%% N.B. Unless where the functions actually needs it only foreach/2/3,
+%% which is meant to be used for its side effects, has a defined order
+%% of evaluation.
+%%
+%% There are also versions with an extra argument, ExtraArgs, which is a
+%% list of extra arguments to each call.
+
+all_1(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> all_1(Pred, Tail);
+ false -> false
+ end;
+all_1(Pred, []) -> true.
+
+any_1(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any_1(Pred, Tail)
+ end;
+any_1(Pred, []) -> false.
+
+map_1(F, List) -> [ F(E) || E <- List ].
+
+flatmap_1(F, [Hd|Tail]) ->
+ F(Hd) ++ flatmap_1(F, Tail);
+flatmap_1(F, []) -> [].
+
+foldl_1(F, Accu, [Hd|Tail]) ->
+ foldl_1(F, F(Hd, Accu), Tail);
+foldl_1(F, Accu, []) -> Accu.
+
+foldr_1(F, Accu, [Hd|Tail]) ->
+ F(Hd, foldr_1(F, Accu, Tail));
+foldr_1(F, Accu, []) -> Accu.
+
+filter_1(Pred, List) -> [ E || E <- List, Pred(E) ].
+
+zF(F, [Hd|Tail]) ->
+ case F(Hd) of
+ true ->
+ [Hd|zF(F, Tail)];
+ {true,Val} ->
+ [Val|zF(F, Tail)];
+ false ->
+ zF(F, Tail)
+ end;
+zF(F, []) -> [].
+
+foreach_1(F, [Hd|Tail]) ->
+ F(Hd),
+ foreach_1(F, Tail);
+foreach_1(F, []) -> ok.
+
+mapfoldl_1(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = mapfoldl_1(F, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl_1(F, Accu, []) -> {[],Accu}.
+
+mapfoldr_1(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr_1(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {[R|Rs],Accu2};
+mapfoldr_1(F, Accu, []) -> {[],Accu}.
+
+takewhile_1(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> [Hd|takewhile_1(Pred, Tail)];
+ false -> []
+ end;
+takewhile_1(Pred, []) -> [].
+
+dropwhile_1(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> dropwhile_1(Pred, Tail);
+ false -> [Hd|Tail]
+ end;
+dropwhile_1(Pred, []) -> [].
+
+splitwith_1(Pred, List) -> splitwith_1(Pred, List, []).
+
+splitwith_1(Pred, [Hd|Tail], Taken) ->
+ case Pred(Hd) of
+ true -> splitwith_1(Pred, Tail, [Hd|Taken]);
+ false -> {reverse_1(Taken), [Hd|Tail]}
+ end;
+splitwith_1(Pred, [], Taken) -> {reverse_1(Taken),[]}.
+
+%% Versions of the above functions with extra arguments.
+
+all_1(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> all_1(Pred, Eas, Tail);
+ false -> false
+ end;
+all_1(Pred, Eas, []) -> true.
+
+any_1(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> true;
+ false -> any_1(Pred, Eas, Tail)
+ end;
+any_1(Pred, Eas, []) -> false.
+
+map_1(F, Eas, List) -> [ apply(F, [E|Eas]) || E <- List ].
+
+flatmap_1(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]) ++ flatmap_1(F, Eas, Tail);
+flatmap_1(F, Eas, []) -> [].
+
+foldl_1(F, Eas, Accu, [Hd|Tail]) ->
+ foldl_1(F, Eas, apply(F, [Hd,Accu|Eas]), Tail);
+foldl_1(F, Eas, Accu, []) -> Accu.
+
+foldr_1(F, Eas, Accu, [Hd|Tail]) ->
+ apply(F, [Hd,foldr_1(F, Eas, Accu, Tail)|Eas]);
+foldr_1(F, Eas, Accu, []) ->
+ Accu.
+
+filter_1(Pred, Eas, List) -> [ E || E <- List, apply(Pred, [E|Eas]) ].
+
+zF(F, Eas, [Hd|Tail]) ->
+ case apply(F, [Hd|Eas]) of
+ true ->
+ [Hd|zF(F, Eas, Tail)];
+ {true,Val} ->
+ [Val|zF(F, Eas, Tail)];
+ false ->
+ zF(F, Eas, Tail)
+ end;
+zF(F, Eas, []) -> [].
+
+foreach_1(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]),
+ foreach_1(F, Eas, Tail);
+foreach_1(F, Eas, []) -> ok.
+
+mapfoldl_1(F, Eas, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = apply(F, [Hd,Accu0|Eas]),
+ {Rs,Accu2} = mapfoldl_1(F, Eas, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl_1(F, Eas, Accu, []) -> {[],Accu}.
+
+mapfoldr_1(F, Eas, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr_1(F, Eas, Accu0, Tail),
+ {R,Accu2} = apply(F, [Hd,Accu1|Eas]),
+ {[R|Rs],Accu2};
+mapfoldr_1(F, Eas, Accu, []) -> {[],Accu}.
+
+%% takewhile/2, dropwhile/2 and splitwith/2 do not have versions with
+%% extra arguments as this going to be discontinued.
+
+
+
+
+
+
+%%% +++++++++++++++++++++
+%%%
+%%% "_2"
+
+
+
+
+
+%% member_2(X, L) -> _2(true | false)
+%% test if X is a member of the list L
+
+%member_2(X, [X|_]) -> true;
+%member_2(X, [_|Y]) ->
+% member_2(X, Y);
+%member_2(X, []) -> false.
+
+member_2(X, L) when list(L) ->
+ case erlang:member_2(X, L) of
+ L1 when list(L1) ->
+ receive after 1 -> ok end,
+ member_2(X, L1);
+ Boolean ->
+ Boolean
+ end.
+
+%% append_2(X, Y) appends lists X and Y
+
+append_2(L1, L2) -> L1 ++ L2.
+
+%% append_2(L) appends the list of lists L
+
+append_2([E]) -> E;
+append_2([H|T]) -> H ++ append_2(T);
+append_2([]) -> [].
+
+%% subtract_2(List1, List2) subtract elements in List2 form List1.
+
+subtract_2(L1, L2) -> L1 -- L2.
+
+%% reverse_2(L) reverse all elements in the list L
+
+reverse_2(X) -> reverse_2(X, []).
+
+%reverse_2([H|T], Y) ->
+% reverse_2(T, [H|Y]);
+%reverse_2([], X) -> X.
+
+reverse_2(List0, Result0) when list(List0) ->
+ case erlang:reverse_2(List0, Result0) of
+ {List, Result} ->
+ receive after 1 -> ok end,
+ reverse_2(List, Result);
+ Result ->
+ Result
+ end.
+
+
+%% nth_2(N, L) returns the N`th element of the list L
+%% nthtail_2(N, L) returns the N`th tail of the list L
+
+nth_2(1, [H|T]) -> H;
+nth_2(N, [_|T]) when N > 1 ->
+ nth_2(N - 1, T).
+
+nthtail_2(1, [H|T]) -> T;
+nthtail_2(N, [H|T]) when N > 1 ->
+ nthtail_2(N - 1, T);
+nthtail_2(0, L) when list(L) -> L.
+
+%% prefix_2(Prefix, List) -> _2(true | false)
+
+prefix_2([X|PreTail], [X|Tail]) ->
+ prefix_2(PreTail, Tail);
+prefix_2([], List) -> true;
+prefix_2(_,_) -> false.
+
+
+%% suffix_2(Suffix, List) -> _2(true | false)
+
+suffix_2(Suffix, Suffix) -> true;
+suffix_2(Suffix, [_|Tail]) ->
+ suffix_2(Suffix, Tail);
+suffix_2(Suffix, []) -> false.
+
+%% last_2(List) returns the last element in a list.
+
+last_2([E]) -> E;
+last_2([E|Es]) ->
+ last_2(Es).
+
+%% seq_2(Min, Max) -> [Min,Min+1, ..., Max]
+%% seq_2(Min, Max, Incr) -> [Min,Min+Incr, ..., Max]
+%% returns the sequence Min..Max
+%% Min <= Max and Min and Max must be integers
+
+seq_2(Min, Max) when integer(Min), integer(Max), Min =< Max ->
+ seq_2(Min, Max, 1, []).
+
+seq_2(Min, Max, Incr) ->
+ seq_2(Min, Min + ((Max-Min) div Incr) * Incr, Incr, []).
+
+seq_2(Min, Min, I, L) -> [Min|L];
+seq_2(Min, Max, I, L) -> seq_2(Min, Max-I, I, [Max|L]).
+
+%% sum_2(L) suns the sum of the elements in L
+
+sum_2(L) -> sum_2(L, 0).
+sum_2([H|T], Sum) -> sum_2(T, Sum + H);
+sum_2([], Sum) -> Sum.
+
+%% duplicate_2(N, X) -> [X,X,X,.....,X] _2(N times)
+%% return N copies of X
+
+duplicate_2(N, X) when integer(N), N >= 0 -> duplicate_2(N, X, []).
+
+duplicate_2(0, _, L) -> L;
+duplicate_2(N, X, L) -> duplicate_2(N-1, X, [X|L]).
+
+
+%% min_2(L) -> returns the minimum element of the list L
+
+min_2([H|T]) -> min_2(T, H).
+
+min_2([H|T], Min) when H < Min -> min_2(T, H);
+min_2([_|T], Min) -> min_2(T, Min);
+min_2([], Min) -> Min.
+
+%% max_2(L) -> returns the maximum element of the list L
+
+max_2([H|T]) -> max_2(T, H).
+
+max_2([H|T], Max) when H > Max -> max_2(T, H);
+max_2([_|T], Max) -> max_2(T, Max);
+max_2([], Max) -> Max.
+
+%% sublist(List, Start, Length)
+%% Returns the sub-list starting at Start of length Length.
+
+sublist_2(List, S, L) when L >= 0 ->
+ sublist_2(nthtail_2(S-1, List), L).
+
+sublist_2([H|T], L) when L > 0 ->
+ [H|sublist_2(T, L-1)];
+sublist_2(List, L) -> [].
+
+%% delete_2(Item, List) -> List'
+%% Delete the first occurance of Item from the list L.
+
+delete_2(Item, [Item|Rest]) -> Rest;
+delete_2(Item, [H|Rest]) ->
+ [H|delete_2(Item, Rest)];
+delete_2(Item, []) -> [].
+
+%% sort_2(L) -> sorts the list L
+
+sort_2([X]) -> [X];
+sort_2([]) -> [];
+sort_2(X) -> split_and_sort_2(X, [], []).
+
+split_and_sort_2([A,B|T], X, Y) ->
+ split_and_sort_2(T, [A|X], [B|Y]);
+split_and_sort_2([H], X, Y) ->
+ split_and_sort_2([], [H|X], Y);
+split_and_sort_2([], X, Y) ->
+ merge_2(sort_2(X), sort_2(Y), []).
+
+%% merge_2(X, Y) -> L
+%% merges two sorted lists X and Y
+
+merge_2(X, Y) -> merge_2(X, Y, []).
+
+merge_2([H1|T1], [H2|T2], L) when H1 < H2 ->
+ merge_2(T1, [H2|T2], [H1|L]);
+merge_2(T1, [H2|T2], L) ->
+ merge_2(T1, T2, [H2|L]);
+merge_2([H|T], T2, L) ->
+ merge_2(T, T2, [H|L]);
+merge_2([], [], L) ->
+ reverse_2(L).
+
+%% concat_2(L) concatinate the list representation of the elements
+%% in L - the elements in L can be atoms, integers of strings.
+%% Returns a list of characters.
+
+concat_2(List) ->
+ flatmap_2(fun thing_to_list/1, List).
+
+thing_to_list_2(X) when integer(X) -> integer_to_list(X);
+thing_to_list_2(X) when float(X) -> float_to_list(X);
+thing_to_list_2(X) when atom(X) -> atom_to_list(X);
+thing_to_list_2(X) when list(X) -> X. %Assumed to be a string
+
+%% flatten_2(List)
+%% flatten_2(List, Tail)
+%% Flatten a list, adding optional tail.
+
+flatten_2(List) ->
+ flatten_2(List, [], []).
+
+flatten_2(List, Tail) ->
+ flatten_2(List, [], Tail).
+
+flatten_2([H|T], Cont, Tail) when list(H) ->
+ flatten_2(H, [T|Cont], Tail);
+flatten_2([H|T], Cont, Tail) ->
+ [H|flatten_2(T, Cont, Tail)];
+flatten_2([], [H|Cont], Tail) ->
+ flatten_2(H, Cont, Tail);
+flatten_2([], [], Tail) ->
+ Tail.
+
+%% flat_length_2(List) _2(undocumented can be rmove later)
+%% Calculate the length of a list of lists.
+
+flat_length_2(List) -> flatlength_2(List).
+
+%% flatlength_2(List)
+%% Calculate the length of a list of lists.
+
+flatlength_2(List) ->
+ flatlength_2(List, 0).
+
+flatlength_2([H|T], L) when list(H) ->
+ flatlength_2(H, flatlength_2(T, L));
+flatlength_2([H|T], L) ->
+ flatlength_2(T, L + 1);
+flatlength_2([], L) -> L.
+
+%% keymember_2(Key, Index, [Tuple])
+%% keysearch_2(Key, Index, [Tuple])
+%% keydelete_2(Key, Index, [Tuple])
+%% keyreplace_2(Key, Index, [Tuple], NewTuple)
+%% keysort_2(Index, [Tuple])
+%% keymerge_2(Index, [Tuple], [Tuple])
+%% keymap_2(Function, Index, [Tuple])
+%% keymap_2(Function, ExtraArgs, Index, [Tuple])
+
+keymember_2(Key, N, [T|Ts]) when element(N, T) == Key -> true;
+keymember_2(Key, N, [T|Ts]) ->
+ keymember_2(Key, N, Ts);
+keymember_2(Key, N, []) -> false.
+
+keysearch_2(Key, N, [H|T]) when element(N, H) == Key ->
+ {value, H};
+keysearch_2(Key, N, [H|T]) ->
+ keysearch_2(Key, N, T);
+keysearch_2(Key, N, []) -> false.
+
+keydelete_2(Key, N, [H|T]) when element(N, H) == Key -> T;
+keydelete_2(Key, N, [H|T]) ->
+ [H|keydelete_2(Key, N, T)];
+keydelete_2(Key, N, []) -> [].
+
+keyreplace_2(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
+ [New|Tail];
+keyreplace_2(Key, Pos, [H|T], New) ->
+ [H|keyreplace_2(Key, Pos, T, New)];
+keyreplace_2(Key, Pos, [], New) -> [].
+
+keysort_2(Index, [X]) -> [X];
+keysort_2(Index, []) -> [];
+keysort_2(Index, X) -> split_and_keysort_2(X, [], [], Index).
+
+split_and_keysort_2([A,B|T], X, Y, Index) ->
+ split_and_keysort_2(T, [A|X], [B|Y], Index);
+split_and_keysort_2([H], X, Y, Index) ->
+ split_and_keysort_2([], [H|X], Y, Index);
+split_and_keysort_2([], X, Y, Index) ->
+ keymerge_2(Index, keysort_2(Index, X), keysort_2(Index, Y), []).
+
+keymerge_2(Index, X, Y) -> keymerge_2(Index, X, Y, []).
+
+keymerge_2(I, [H1|T1], [H2|T2], L) when element(I, H1) < element(I, H2) ->
+ keymerge_2(I, T1, [H2|T2], [H1|L]);
+keymerge_2(Index, T1, [H2|T2], L) ->
+ keymerge_2(Index,T1, T2, [H2|L]);
+keymerge_2(Index,[H|T], T2, L) ->
+ keymerge_2(Index,T, T2, [H|L]);
+keymerge_2(Index, [], [], L) ->
+ reverse_2(L).
+
+keymap_2(Fun, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap_2(Fun, Index, Tail)];
+keymap_2( _, _ , []) -> [].
+
+keymap_2(Fun, ExtraArgs, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, apply(Fun, [element(Index, Tup)|ExtraArgs]))|
+ keymap_2(Fun, ExtraArgs, Index, Tail)];
+keymap_2( _, _ , _, []) -> [].
+
+%% all_2(Predicate, List)
+%% any_2(Predicate, List)
+%% map_2(Function, List)
+%% flatmap_2(Function, List)
+%% foldl_2(Function, First, List)
+%% foldr_2(Function, Last, List)
+%% filter_2(Predicate, List)
+%% zF(Function, List)
+%% mapfoldl_2(Function, First, List)
+%% mapfoldr_2(Function, Last, List)
+%% foreach_2(Function, List)
+%% takewhile_2(Predicate, List)
+%% dropwhile_2(Predicate, List)
+%% splitwith_2(Predicate, List)
+%% for list programming. Function here is either a 'fun' or a tuple
+%% {Module,Name} and we use apply/2 to evaluate. The name zf is a joke!
+%%
+%% N.B. Unless where the functions actually needs it only foreach/2/3,
+%% which is meant to be used for its side effects, has a defined order
+%% of evaluation.
+%%
+%% There are also versions with an extra argument, ExtraArgs, which is a
+%% list of extra arguments to each call.
+
+all_2(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> all_2(Pred, Tail);
+ false -> false
+ end;
+all_2(Pred, []) -> true.
+
+any_2(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any_2(Pred, Tail)
+ end;
+any_2(Pred, []) -> false.
+
+map_2(F, List) -> [ F(E) || E <- List ].
+
+flatmap_2(F, [Hd|Tail]) ->
+ F(Hd) ++ flatmap_2(F, Tail);
+flatmap_2(F, []) -> [].
+
+foldl_2(F, Accu, [Hd|Tail]) ->
+ foldl_2(F, F(Hd, Accu), Tail);
+foldl_2(F, Accu, []) -> Accu.
+
+foldr_2(F, Accu, [Hd|Tail]) ->
+ F(Hd, foldr_2(F, Accu, Tail));
+foldr_2(F, Accu, []) -> Accu.
+
+filter_2(Pred, List) -> [ E || E <- List, Pred(E) ].
+
+zF_2(F, [Hd|Tail]) ->
+ case F(Hd) of
+ true ->
+ [Hd|zF_2(F, Tail)];
+ {true,Val} ->
+ [Val|zF_2(F, Tail)];
+ false ->
+ zF_2(F, Tail)
+ end;
+zF_2(F, []) -> [].
+
+foreach_2(F, [Hd|Tail]) ->
+ F(Hd),
+ foreach_2(F, Tail);
+foreach_2(F, []) -> ok.
+
+mapfoldl_2(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = mapfoldl_2(F, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl_2(F, Accu, []) -> {[],Accu}.
+
+mapfoldr_2(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr_2(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {[R|Rs],Accu2};
+mapfoldr_2(F, Accu, []) -> {[],Accu}.
+
+takewhile_2(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> [Hd|takewhile_2(Pred, Tail)];
+ false -> []
+ end;
+takewhile_2(Pred, []) -> [].
+
+dropwhile_2(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> dropwhile_2(Pred, Tail);
+ false -> [Hd|Tail]
+ end;
+dropwhile_2(Pred, []) -> [].
+
+splitwith_2(Pred, List) -> splitwith_2(Pred, List, []).
+
+splitwith_2(Pred, [Hd|Tail], Taken) ->
+ case Pred(Hd) of
+ true -> splitwith_2(Pred, Tail, [Hd|Taken]);
+ false -> {reverse_2(Taken), [Hd|Tail]}
+ end;
+splitwith_2(Pred, [], Taken) -> {reverse_2(Taken),[]}.
+
+%% Versions of the above functions with extra arguments.
+
+all_2(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> all_2(Pred, Eas, Tail);
+ false -> false
+ end;
+all_2(Pred, Eas, []) -> true.
+
+any_2(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> true;
+ false -> any_2(Pred, Eas, Tail)
+ end;
+any_2(Pred, Eas, []) -> false.
+
+map_2(F, Eas, List) -> [ apply(F, [E|Eas]) || E <- List ].
+
+flatmap_2(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]) ++ flatmap_2(F, Eas, Tail);
+flatmap_2(F, Eas, []) -> [].
+
+foldl_2(F, Eas, Accu, [Hd|Tail]) ->
+ foldl_2(F, Eas, apply(F, [Hd,Accu|Eas]), Tail);
+foldl_2(F, Eas, Accu, []) -> Accu.
+
+foldr_2(F, Eas, Accu, [Hd|Tail]) ->
+ apply(F, [Hd,foldr_2(F, Eas, Accu, Tail)|Eas]);
+foldr_2(F, Eas, Accu, []) ->
+ Accu.
+
+filter_2(Pred, Eas, List) -> [ E || E <- List, apply(Pred, [E|Eas]) ].
+
+zF_2(F, Eas, [Hd|Tail]) ->
+ case apply(F, [Hd|Eas]) of
+ true ->
+ [Hd|zF_2(F, Eas, Tail)];
+ {true,Val} ->
+ [Val|zF_2(F, Eas, Tail)];
+ false ->
+ zF_2(F, Eas, Tail)
+ end;
+zF_2(F, Eas, []) -> [].
+
+foreach_2(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]),
+ foreach_2(F, Eas, Tail);
+foreach_2(F, Eas, []) -> ok.
+
+mapfoldl_2(F, Eas, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = apply(F, [Hd,Accu0|Eas]),
+ {Rs,Accu2} = mapfoldl_2(F, Eas, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl_2(F, Eas, Accu, []) -> {[],Accu}.
+
+mapfoldr_2(F, Eas, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr_2(F, Eas, Accu0, Tail),
+ {R,Accu2} = apply(F, [Hd,Accu1|Eas]),
+ {[R|Rs],Accu2};
+mapfoldr_2(F, Eas, Accu, []) -> {[],Accu}.
+
+%% takewhile/2, dropwhile/2 and splitwith/2 do not have versions with
+%% extra arguments as this going to be discontinued.
+
+
+
+
+
+%%% +++++++++++++++++++
+%%%
+%%% With "_3"
+
+
+
+
+%% member_3(X, L) -> _3(true | false)
+%% test if X is a member of the list L
+
+%member_3(X, [X|_]) -> true;
+%member_3(X, [_|Y]) ->
+% member_3(X, Y);
+%member_3(X, []) -> false.
+
+member_3(X, L) when list(L) ->
+ case erlang:member(X, L) of
+ L1 when list(L1) ->
+ receive after 1 -> ok end,
+ member_3(X, L1);
+ Boolean ->
+ Boolean
+ end.
+
+%% append_3(X, Y) appends lists X and Y
+
+append_3(L1, L2) -> L1 ++ L2.
+
+%% append_3(L) appends the list of lists L
+
+append_3([E]) -> E;
+append_3([H|T]) -> H ++ append_3(T);
+append_3([]) -> [].
+
+%% subtract_3(List1, List2) subtract elements in List2 form List1.
+
+subtract_3(L1, L2) -> L1 -- L2.
+
+%% reverse_3(L) reverse all elements in the list L
+
+reverse_3(X) -> reverse_3(X, []).
+
+%reverse_3([H|T], Y) ->
+% reverse_3(T, [H|Y]);
+%reverse_3([], X) -> X.
+
+reverse_3(List0, Result0) when list(List0) ->
+ case erlang:reverse_3(List0, Result0) of
+ {List, Result} ->
+ receive after 1 -> ok end,
+ reverse_3(List, Result);
+ Result ->
+ Result
+ end.
+
+
+%% nth_3(N, L) returns the N`th element of the list L
+%% nthtail_3(N, L) returns the N`th tail of the list L
+
+nth_3(1, [H|T]) -> H;
+nth_3(N, [_|T]) when N > 1 ->
+ nth_3(N - 1, T).
+
+nthtail_3(1, [H|T]) -> T;
+nthtail_3(N, [H|T]) when N > 1 ->
+ nthtail_3(N - 1, T);
+nthtail_3(0, L) when list(L) -> L.
+
+%% prefix_3(Prefix, List) -> _3(true | false)
+
+prefix_3([X|PreTail], [X|Tail]) ->
+ prefix_3(PreTail, Tail);
+prefix_3([], List) -> true;
+prefix_3(_,_) -> false.
+
+
+%% suffix_3(Suffix, List) -> _3(true | false)
+
+suffix_3(Suffix, Suffix) -> true;
+suffix_3(Suffix, [_|Tail]) ->
+ suffix_3(Suffix, Tail);
+suffix_3(Suffix, []) -> false.
+
+%% last_3(List) returns the last element in a list.
+
+last_3([E]) -> E;
+last_3([E|Es]) ->
+ last_3(Es).
+
+%% seq_3(Min, Max) -> [Min,Min+1, ..., Max]
+%% seq_3(Min, Max, Incr) -> [Min,Min+Incr, ..., Max]
+%% returns the sequence Min..Max
+%% Min <= Max and Min and Max must be integers
+
+seq_3(Min, Max) when integer(Min), integer(Max), Min =< Max ->
+ seq_3(Min, Max, 1, []).
+
+seq_3(Min, Max, Incr) ->
+ seq_3(Min, Min + ((Max-Min) div Incr) * Incr, Incr, []).
+
+seq_3(Min, Min, I, L) -> [Min|L];
+seq_3(Min, Max, I, L) -> seq_3(Min, Max-I, I, [Max|L]).
+
+%% sum_3(L) suns the sum of the elements in L
+
+sum_3(L) -> sum_3(L, 0).
+sum_3([H|T], Sum) -> sum_3(T, Sum + H);
+sum_3([], Sum) -> Sum.
+
+%% duplicate_3(N, X) -> [X,X,X,.....,X] _3(N times)
+%% return N copies of X
+
+duplicate_3(N, X) when integer(N), N >= 0 -> duplicate_3(N, X, []).
+
+duplicate_3(0, _, L) -> L;
+duplicate_3(N, X, L) -> duplicate_3(N-1, X, [X|L]).
+
+
+%% min_3(L) -> returns the minimum element of the list L
+
+min_3([H|T]) -> min_3(T, H).
+
+min_3([H|T], Min) when H < Min -> min_3(T, H);
+min_3([_|T], Min) -> min_3(T, Min);
+min_3([], Min) -> Min.
+
+%% max_3(L) -> returns the maximum element of the list L
+
+max_3([H|T]) -> max_3(T, H).
+
+max_3([H|T], Max) when H > Max -> max_3(T, H);
+max_3([_|T], Max) -> max_3(T, Max);
+max_3([], Max) -> Max.
+
+%% sublist_3(List, Start, Length)
+%% Returns the sub-list starting at Start of length Length.
+
+sublist_3(List, S, L) when L >= 0 ->
+ sublist_3(nthtail_3(S-1, List), L).
+
+sublist_3([H|T], L) when L > 0 ->
+ [H|sublist_3(T, L-1)];
+sublist_3(List, L) -> [].
+
+%% delete_3(Item, List) -> List'
+%% Delete the first occurance of Item from the list L.
+
+delete_3(Item, [Item|Rest]) -> Rest;
+delete_3(Item, [H|Rest]) ->
+ [H|delete_3(Item, Rest)];
+delete_3(Item, []) -> [].
+
+%% sort_3(L) -> sorts the list L
+
+sort_3([X]) -> [X];
+sort_3([]) -> [];
+sort_3(X) -> split_and_sort_3(X, [], []).
+
+split_and_sort_3([A,B|T], X, Y) ->
+ split_and_sort_3(T, [A|X], [B|Y]);
+split_and_sort_3([H], X, Y) ->
+ split_and_sort_3([], [H|X], Y);
+split_and_sort_3([], X, Y) ->
+ merge_3(sort_3(X), sort_3(Y), []).
+
+%% merge_3(X, Y) -> L
+%% merges two sorted lists X and Y
+
+merge_3(X, Y) -> merge_3(X, Y, []).
+
+merge_3([H1|T1], [H2|T2], L) when H1 < H2 ->
+ merge_3(T1, [H2|T2], [H1|L]);
+merge_3(T1, [H2|T2], L) ->
+ merge_3(T1, T2, [H2|L]);
+merge_3([H|T], T2, L) ->
+ merge_3(T, T2, [H|L]);
+merge_3([], [], L) ->
+ reverse_3(L).
+
+%% concat_3(L) concatinate the list representation of the elements
+%% in L - the elements in L can be atoms, integers of strings.
+%% Returns a list of characters.
+
+concat_3(List) ->
+ flatmap_3(fun thing_to_list/1, List).
+
+thing_to_list_3(X) when integer(X) -> integer_to_list(X);
+thing_to_list_3(X) when float(X) -> float_to_list(X);
+thing_to_list_3(X) when atom(X) -> atom_to_list(X);
+thing_to_list_3(X) when list(X) -> X. %Assumed to be a string
+
+%% flatten_3(List)
+%% flatten_3(List, Tail)
+%% Flatten a list, adding optional tail.
+
+flatten_3(List) ->
+ flatten_3(List, [], []).
+
+flatten_3(List, Tail) ->
+ flatten_3(List, [], Tail).
+
+flatten_3([H|T], Cont, Tail) when list(H) ->
+ flatten_3(H, [T|Cont], Tail);
+flatten_3([H|T], Cont, Tail) ->
+ [H|flatten_3(T, Cont, Tail)];
+flatten_3([], [H|Cont], Tail) ->
+ flatten_3(H, Cont, Tail);
+flatten_3([], [], Tail) ->
+ Tail.
+
+%% flat_length_3(List) _3(undocumented can be rmove later)
+%% Calculate the length of a list of lists.
+
+flat_length_3(List) -> flatlength_3(List).
+
+%% flatlength_3(List)
+%% Calculate the length of a list of lists.
+
+flatlength_3(List) ->
+ flatlength_3(List, 0).
+
+flatlength_3([H|T], L) when list(H) ->
+ flatlength_3(H, flatlength_3(T, L));
+flatlength_3([H|T], L) ->
+ flatlength_3(T, L + 1);
+flatlength_3([], L) -> L.
+
+%% keymember_3(Key, Index, [Tuple])
+%% keysearch_3(Key, Index, [Tuple])
+%% keydelete_3(Key, Index, [Tuple])
+%% keyreplace_3(Key, Index, [Tuple], NewTuple)
+%% keysort_3(Index, [Tuple])
+%% keymerge_3(Index, [Tuple], [Tuple])
+%% keymap_3(Function, Index, [Tuple])
+%% keymap_3(Function, ExtraArgs, Index, [Tuple])
+
+keymember_3(Key, N, [T|Ts]) when element(N, T) == Key -> true;
+keymember_3(Key, N, [T|Ts]) ->
+ keymember_3(Key, N, Ts);
+keymember_3(Key, N, []) -> false.
+
+keysearch_3(Key, N, [H|T]) when element(N, H) == Key ->
+ {value, H};
+keysearch_3(Key, N, [H|T]) ->
+ keysearch_3(Key, N, T);
+keysearch_3(Key, N, []) -> false.
+
+keydelete_3(Key, N, [H|T]) when element(N, H) == Key -> T;
+keydelete_3(Key, N, [H|T]) ->
+ [H|keydelete_3(Key, N, T)];
+keydelete_3(Key, N, []) -> [].
+
+keyreplace_3(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
+ [New|Tail];
+keyreplace_3(Key, Pos, [H|T], New) ->
+ [H|keyreplace_3(Key, Pos, T, New)];
+keyreplace_3(Key, Pos, [], New) -> [].
+
+keysort_3(Index, [X]) -> [X];
+keysort_3(Index, []) -> [];
+keysort_3(Index, X) -> split_and_keysort_3(X, [], [], Index).
+
+split_and_keysort_3([A,B|T], X, Y, Index) ->
+ split_and_keysort_3(T, [A|X], [B|Y], Index);
+split_and_keysort_3([H], X, Y, Index) ->
+ split_and_keysort_3([], [H|X], Y, Index);
+split_and_keysort_3([], X, Y, Index) ->
+ keymerge_3(Index, keysort_3(Index, X), keysort_3(Index, Y), []).
+
+keymerge_3(Index, X, Y) -> keymerge_3(Index, X, Y, []).
+
+keymerge_3(I, [H1|T1], [H2|T2], L) when element(I, H1) < element(I, H2) ->
+ keymerge_3(I, T1, [H2|T2], [H1|L]);
+keymerge_3(Index, T1, [H2|T2], L) ->
+ keymerge_3(Index,T1, T2, [H2|L]);
+keymerge_3(Index,[H|T], T2, L) ->
+ keymerge_3(Index,T, T2, [H|L]);
+keymerge_3(Index, [], [], L) ->
+ reverse_3(L).
+
+keymap_3(Fun, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap_3(Fun, Index, Tail)];
+keymap_3( _, _ , []) -> [].
+
+keymap_3(Fun, ExtraArgs, Index, [Tup|Tail]) ->
+ [setelement(Index, Tup, apply(Fun, [element(Index, Tup)|ExtraArgs]))|
+ keymap_3(Fun, ExtraArgs, Index, Tail)];
+keymap_3( _, _ , _, []) -> [].
+
+%% all_3(Predicate, List)
+%% any_3(Predicate, List)
+%% map_3(Function, List)
+%% flatmap_3(Function, List)
+%% foldl_3(Function, First, List)
+%% foldr_3(Function, Last, List)
+%% filter_3(Predicate, List)
+%% zF(Function, List)
+%% mapfoldl_3(Function, First, List)
+%% mapfoldr_3(Function, Last, List)
+%% foreach_3(Function, List)
+%% takewhile_3(Predicate, List)
+%% dropwhile_3(Predicate, List)
+%% splitwith_3(Predicate, List)
+%% for list programming. Function here is either a 'fun' or a tuple
+%% {Module,Name} and we use apply/2 to evaluate. The name zf is a joke!
+%%
+%% N.B. Unless where the functions actually needs it only foreach/2/3,
+%% which is meant to be used for its side effects, has a defined order
+%% of evaluation.
+%%
+%% There are also versions with an extra argument, ExtraArgs, which is a
+%% list of extra arguments to each call.
+
+all_3(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> all_3(Pred, Tail);
+ false -> false
+ end;
+all_3(Pred, []) -> true.
+
+any_3(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> true;
+ false -> any_3(Pred, Tail)
+ end;
+any_3(Pred, []) -> false.
+
+map_3(F, List) -> [ F(E) || E <- List ].
+
+flatmap_3(F, [Hd|Tail]) ->
+ F(Hd) ++ flatmap_3(F, Tail);
+flatmap_3(F, []) -> [].
+
+foldl_3(F, Accu, [Hd|Tail]) ->
+ foldl_3(F, F(Hd, Accu), Tail);
+foldl_3(F, Accu, []) -> Accu.
+
+foldr_3(F, Accu, [Hd|Tail]) ->
+ F(Hd, foldr_3(F, Accu, Tail));
+foldr_3(F, Accu, []) -> Accu.
+
+filter_3(Pred, List) -> [ E || E <- List, Pred(E) ].
+
+zF_3(F, [Hd|Tail]) ->
+ case F(Hd) of
+ true ->
+ [Hd|zF_3(F, Tail)];
+ {true,Val} ->
+ [Val|zF_3(F, Tail)];
+ false ->
+ zF_3(F, Tail)
+ end;
+zF_3(F, []) -> [].
+
+foreach_3(F, [Hd|Tail]) ->
+ F(Hd),
+ foreach_3(F, Tail);
+foreach_3(F, []) -> ok.
+
+mapfoldl_3(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = mapfoldl_3(F, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl_3(F, Accu, []) -> {[],Accu}.
+
+mapfoldr_3(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr_3(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {[R|Rs],Accu2};
+mapfoldr_3(F, Accu, []) -> {[],Accu}.
+
+takewhile_3(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> [Hd|takewhile_3(Pred, Tail)];
+ false -> []
+ end;
+takewhile_3(Pred, []) -> [].
+
+dropwhile_3(Pred, [Hd|Tail]) ->
+ case Pred(Hd) of
+ true -> dropwhile_3(Pred, Tail);
+ false -> [Hd|Tail]
+ end;
+dropwhile_3(Pred, []) -> [].
+
+splitwith_3(Pred, List) -> splitwith_3(Pred, List, []).
+
+splitwith_3(Pred, [Hd|Tail], Taken) ->
+ case Pred(Hd) of
+ true -> splitwith_3(Pred, Tail, [Hd|Taken]);
+ false -> {reverse_3(Taken), [Hd|Tail]}
+ end;
+splitwith_3(Pred, [], Taken) -> {reverse_3(Taken),[]}.
+
+%% Versions of the above functions with extra arguments.
+
+all_3(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> all_3(Pred, Eas, Tail);
+ false -> false
+ end;
+all_3(Pred, Eas, []) -> true.
+
+any_3(Pred, Eas, [Hd|Tail]) ->
+ case apply(Pred, [Hd|Eas]) of
+ true -> true;
+ false -> any_3(Pred, Eas, Tail)
+ end;
+any_3(Pred, Eas, []) -> false.
+
+map_3(F, Eas, List) -> [ apply(F, [E|Eas]) || E <- List ].
+
+flatmap_3(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]) ++ flatmap_3(F, Eas, Tail);
+flatmap_3(F, Eas, []) -> [].
+
+foldl_3(F, Eas, Accu, [Hd|Tail]) ->
+ foldl_3(F, Eas, apply(F, [Hd,Accu|Eas]), Tail);
+foldl_3(F, Eas, Accu, []) -> Accu.
+
+foldr_3(F, Eas, Accu, [Hd|Tail]) ->
+ apply(F, [Hd,foldr_3(F, Eas, Accu, Tail)|Eas]);
+foldr_3(F, Eas, Accu, []) ->
+ Accu.
+
+filter_3(Pred, Eas, List) -> [ E || E <- List, apply(Pred, [E|Eas]) ].
+
+zF_3(F, Eas, [Hd|Tail]) ->
+ case apply(F, [Hd|Eas]) of
+ true ->
+ [Hd|zF(F, Eas, Tail)];
+ {true,Val} ->
+ [Val|zF(F, Eas, Tail)];
+ false ->
+ zF(F, Eas, Tail)
+ end;
+zF_3(F, Eas, []) -> [].
+
+foreach_3(F, Eas, [Hd|Tail]) ->
+ apply(F, [Hd|Eas]),
+ foreach_3(F, Eas, Tail);
+foreach_3(F, Eas, []) -> ok.
+
+mapfoldl_3(F, Eas, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = apply(F, [Hd,Accu0|Eas]),
+ {Rs,Accu2} = mapfoldl_3(F, Eas, Accu1, Tail),
+ {[R|Rs],Accu2};
+mapfoldl_3(F, Eas, Accu, []) -> {[],Accu}.
+
+mapfoldr_3(F, Eas, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = mapfoldr_3(F, Eas, Accu0, Tail),
+ {R,Accu2} = apply(F, [Hd,Accu1|Eas]),
+ {[R|Rs],Accu2};
+mapfoldr_3(F, Eas, Accu, []) -> {[],Accu}.
+
+%% takewhile/2, dropwhile/2 and splitwith/2 do not have versions with
+%% extra arguments as this going to be discontinued.
+
+
+
+
+lots_of_atoms () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms1 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms2 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms3 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms4 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms5 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms6 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms7 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms8 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms9 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms10 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms11 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
+
+
+lots_of_atoms12 () ->
+
+Aa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Af = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ag = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ah = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aba = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aca = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ada = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aea = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aga = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aha = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aab = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahb = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aac = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aec = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahc = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aad = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Add = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aed = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahd = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aae = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ace = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ade = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aee = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Age = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahe = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+
+Aaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+
+Aaaaaa= 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahaaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aabaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aebaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahbaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aacaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Accaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aecaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahcaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aadaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Acdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Addaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aedaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Agdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ahdaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aaeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Abeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aceaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Adeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aeeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Afeaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Ageaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg',
+
+Aheaaa = 'rnevgoirnvire iriej iirejgierjg ioegjrigj oiegj oijetig oijtgej oj oiejg iojetijwokfopkewmf irjgo ergjerifjiugnuignuit euhg uieuignuiergnuienrgueringiun uiegniuerngui iugne uegniuetgniuetgnuientg unegngiuethgiueugnieg'.
diff --git a/lib/debugger/test/int_SUITE_data/ordsets1.erl b/lib/debugger/test/int_SUITE_data/ordsets1.erl
new file mode 100644
index 0000000000..a01d35eb51
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/ordsets1.erl
@@ -0,0 +1,191 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Copyright (C) 1991, Ellemtel Telecommunications Systems Laboratories
+%% File : ordsets.erl
+%% Author : Robert Virding
+%% Purpose : Functions for manipulating sets as ordered lists.
+
+%% As yet some of these are not very efficiently written.
+
+-module(ordsets1).
+
+-export([new_set/0,is_set/1,set_to_list/1,list_to_set/1]).
+-export([is_element/2,add_element/2,del_element/2]).
+-export([union/2,union/1,intersection/2,intersection/1]).
+-export([subtract/2,subset/2]).
+
+%% new_set()
+%% Return a new empty ordered set.
+
+new_set() ->
+ [].
+
+%% is_set(Set)
+%% Return 'true' if Set is an ordered set of elements, else 'false'.
+
+is_set([E|Es]) ->
+ is_set(Es, E);
+is_set([]) ->
+ true.
+
+is_set([E2|Es], E1) when E1 < E2 ->
+ is_set(Es, E2);
+is_set([E2|Es], E1) ->
+ false;
+is_set([], E1) ->
+ true.
+
+%% set_to_list(OrdSet)
+%% Return the elements in OrdSet as a list.
+
+set_to_list(S) ->
+ S.
+
+%% list_to_set(List)
+%% Build an ordered set from the elements in List.
+
+list_to_set([E|Es]) ->
+ add_element(E, list_to_set(Es));
+list_to_set([]) ->
+ [].
+
+%% is_element(Element, OrdSet)
+%% Return 'true' if Element is an element of OrdSet, else 'false'.
+
+is_element(E, [H|Es]) when E < H ->
+ false;
+is_element(E, [H|Es]) when E == H ->
+ true;
+is_element(E, [H|Es]) when E > H ->
+ is_element(E, Es);
+is_element(E, []) ->
+ false.
+
+%% add_element(Element, OrdSet)
+%% Return OrdSet with Element inserted in it.
+
+add_element(E, [H|Es]) when E < H ->
+ [E,H|Es];
+add_element(E, [H|Es]) when E == H ->
+ [H|Es];
+add_element(E, [H|Es]) when E > H ->
+ [H|add_element(E, Es)];
+add_element(E, []) ->
+ [E].
+
+%% del_element(Element, OrdSet)
+%% Return OrdSet but with Element removed.
+
+del_element(E, [H|Es]) when E < H ->
+ [H|Es];
+del_element(E, [H|Es]) when E == H ->
+ Es;
+del_element(E, [H|Es]) when E > H ->
+ [H|del_element(E, Es)];
+del_element(E, []) ->
+ [].
+
+%% union(Set1, Set2)
+%% Return the union of Set1 and Set2.
+
+union([H1|Es1], [H2|Es2]) when H1 < H2 ->
+ [H1|union(Es1, [H2|Es2])];
+union([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ [H1|union(Es1, Es2)];
+union([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ [H2|union([H1|Es1], Es2)];
+union([], Es2) ->
+ Es2;
+union(Es1, []) ->
+ Es1.
+
+%% union(OrdSets)
+%% Return the union of the list of sets.
+
+union([S1,S2|Ss]) ->
+ union1(union(S1,S2), Ss);
+union([S]) ->
+ S;
+union([]) ->
+ [].
+
+union1(S1, [S2|Ss]) ->
+ union1(union(S1, S2), Ss);
+union1(S1, []) ->
+ S1.
+
+%% intersection(Set1, Set2)
+%% Return the intersection of Set1 and Set2.
+
+intersection([H1|Es1], [H2|Es2]) when H1 < H2 ->
+ intersection(Es1, [H2|Es2]);
+intersection([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ [H1|intersection(Es1, Es2)];
+intersection([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ intersection([H1|Es1], Es2);
+intersection([], Es2) ->
+ [];
+intersection(Es1, []) ->
+ [].
+
+%% intersection(OrdSets)
+%% Return the intersection of the list of sets.
+
+intersection([S1,S2|Ss]) ->
+ intersection1(intersection(S1,S2), Ss);
+intersection([S]) ->
+ S;
+intersection([]) ->
+ [].
+
+intersection1(S1, [S2|Ss]) ->
+ intersection1(intersection(S1, S2), Ss);
+intersection1(S1, []) ->
+ S1.
+
+%% subtract(Set1, Set2)
+%% Return all and only the elements of Set1 which are not also in Set2.
+
+subtract([H1|Es1], [H2|Es2]) when H1 < H2 ->
+ [H1|subtract(Es1, [H2|Es2])];
+subtract([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ subtract(Es1, Es2);
+subtract([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ subtract([H1|Es1], Es2);
+subtract([], Es2) ->
+ [];
+subtract(Es1, []) ->
+ Es1.
+
+%% subset(Set1, Set2)
+%% Return 'true' when every element of Set1 is also a member of Set2,
+%% else 'false'.
+
+subset([H1|Es1], [H2|Es2]) when H1 < H2 -> %H1 not in Set2
+ false;
+subset([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ subset(Es1, Es2);
+subset([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ subset([H1|Es1], Es2);
+subset([], Es2) ->
+ true;
+subset(Es1, []) ->
+ false.
diff --git a/lib/debugger/test/int_SUITE_data/test.erl b/lib/debugger/test/int_SUITE_data/test.erl
new file mode 100644
index 0000000000..679b266380
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/test.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(test).
+
+%%-compile(export_all).
+-export([test1/0,
+ test2/0,
+ test3/0,
+ test4/0,
+ test5/0,
+ test6/1,
+ test7/0,
+ test8/1,
+ test9/0,
+ test10/0,
+ test11/0
+ ]).
+
+
+-export([test9_server/1]).
+
+
+test1() ->
+ R1 = lists1:reverse("retep"),
+ R2 = ordsets1:list_to_set([b,c,a,2,4,1]),
+ R3 = lists1:reverse("nilo"),
+ {R1,R2, R3}.
+
+test2() ->
+ R1 = spawn(lists1,reverse,["retep"]),
+ R2 = spawn(ordsets1,list_to_set,[[b,c,a,2,4,1]]),
+ R3 = spawn(lists1,reverse,["nilo"]),
+ {R1,R2, R3}.
+
+
+test3() ->
+ A = a,
+ Pid = spawn(?MODULE, test1,[]),
+ B = b,
+ {A, B, Pid}.
+
+test4() ->
+ Pid = spawn(?MODULE, test1,[]),
+ A = a,
+ B = b,
+ {A, B, Pid}.
+test5() ->
+ L1 = [a,b,c],
+ L = length(L1),
+ A = a,
+ B = b,
+ {A, B, L, L1}.
+
+
+
+test6(0) ->
+ ok;
+test6(N) when N>0 ->
+ spawn(lists1,reverse,["adolfiparisrorsirapifloda"]),
+ test6(N-1).
+
+
+test7() ->
+ CurDirReturn = file:get_cwd(),
+ {ok, CurDir} = CurDirReturn,
+ DirListReturn = file:list_dir(CurDir),
+ {ok, DirList} = DirListReturn,
+ io:format("~w~n",[DirList]).
+
+
+test8(List) ->
+ %% foo
+ %%bar
+ %% foo
+ %%bar
+ %% foo
+ %%bar
+ %% foo
+ %%bar
+
+ L2 = [gamma|List],
+ {L2, List}.
+
+test9() ->
+ S1 = spawn(?MODULE, test9_server,[self()]),
+ S2 = spawn(?MODULE, test9_server,[bongo]),
+ S3 = spawn(?MODULE, test9_server,[42]),
+
+ test9_loop(S1,S2,S3).
+
+test9_loop(S1,S2,S3) ->
+ receive
+ {S1, hej} ->
+ io:format("S1 ~n"),
+ test9_loop(S1,S2,S3);
+ {S2, hej} ->
+ io:format("S2 ~n"),
+ test9_loop(S1,S2,S3);
+ {S3, hej} ->
+ io:format("S3 ~n"),
+ test9_loop(S1,S2,S3)
+ end.
+
+
+test9_server(Pid) ->
+ io:format("started server: ~p~n",[Pid]),
+ test9_server1(Pid).
+
+test9_server1(Pid) ->
+ Pad = {pad, Pid},
+ test9_server2(Pad).
+
+test9_server2(Pad) ->
+ {pad, Pid} = Pad,
+ Pid ! {self(), hej}.
+
+
+
+
+
+test10() ->
+ receive
+ X ->
+ done
+ after 20000 ->
+ timeout
+ end.
+
+test11() ->
+ receive
+ X ->
+ done
+ end.
diff --git a/lib/debugger/test/int_SUITE_data/test1.erl b/lib/debugger/test/int_SUITE_data/test1.erl
new file mode 100644
index 0000000000..a93416cbac
--- /dev/null
+++ b/lib/debugger/test/int_SUITE_data/test1.erl
@@ -0,0 +1,34 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(test1).
+
+-compile(export_all).
+
+
+foo() ->
+ A = [1,2,3],
+ B = [a,b,c],
+ C = [d,e,f],
+ D = my_lists:append(A,B),
+ E = my_lists:append(D,C),
+ F = [1],
+ G = my_lists:append(E,F),
+ {A,B,C,D,E,F,G}.
diff --git a/lib/debugger/test/int_break_SUITE.erl b/lib/debugger/test/int_break_SUITE.erl
new file mode 100644
index 0000000000..159678a1f9
--- /dev/null
+++ b/lib/debugger/test/int_break_SUITE.erl
@@ -0,0 +1,112 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(int_break_SUITE).
+
+%% Test break points.
+
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ basic/1,cleanup/1]).
+
+-export([auto_attach/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic, cleanup].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(_Case, Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line Mod = ordsets1,
+ ?line {module,Mod} = int:i(filename:join(DataDir, Mod)),
+ ?line ok = io:format("Interpreted modules: ~p", [int:interpreted()]),
+ ?line Dog = test_server:timetrap(?t:minutes(0.5)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ ?line ok = io:format("Interpreted modules: ~p", [int:interpreted()]),
+ ?line Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+basic(doc) -> "Tests setting a few break points.";
+basic(suite) -> [];
+basic(Config) when list(Config) ->
+ ?line int:auto_attach([init], {?MODULE,auto_attach}),
+ ?line S1 = [] = ordsets1:new_set(),
+ ?line ok = i:ib(ordsets1, 86),
+ ?line S2 = [xxx] = ordsets1:add_element(xxx, S1),
+ ?line S3 = [xxx,y] = ordsets1:add_element(y, S2),
+ ?line ok = i:ib(ordsets1, union, 2),
+ ?line [xxx,y,z] = ordsets1:union(S3, [z]),
+ ok.
+
+cleanup(doc) -> "Make sure that the auto-attach flag is turned off.";
+cleanup(suite) -> [];
+cleanup(Config) when list(Config) ->
+ ?line int:auto_attach(false),
+ ok.
+
+auto_attach(Pid) ->
+ {ok, Meta} = int:attached(Pid),
+ io:format("Pid = ~p; Meta = ~p", [Pid,Meta]),
+ link(Meta),
+ attach_loop(Pid, Meta).
+
+attach_loop(Pid, Meta) ->
+ receive
+ Msg ->
+ io:format("attached: ~p", [Msg]),
+ attach_cmd(Msg, Pid, Meta),
+ attach_loop(Pid, Meta)
+ end.
+
+attach_cmd({Meta,{break_at,ordsets1,36,2}}, _Pid, Meta) ->
+ int:meta(Meta, continue);
+attach_cmd({Meta,{break_at,ordsets1,87,_}}, _Pid, Meta) ->
+ int:meta(Meta, continue);
+attach_cmd({Meta,{break_at,ordsets1,89,_}}, _Pid, Meta) ->
+ int:meta(Meta, continue);
+attach_cmd({Meta,{break_at,ordsets1,Line,_}}, _Pid, Meta) when 107 =< Line, Line =< 115 ->
+ int:meta(Meta, finish);
+attach_cmd({Meta,{break_at,_Mod,_Line,_Other}}=Cmd, _Pid, Meta) ->
+ io:format("attached: no action for ~p", [Cmd]);
+attach_cmd(_, _Pid, _Meta) ->
+ ok.
diff --git a/lib/debugger/test/int_break_SUITE_data/Makefile.src b/lib/debugger/test/int_break_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..0d1ee35a67
--- /dev/null
+++ b/lib/debugger/test/int_break_SUITE_data/Makefile.src
@@ -0,0 +1,25 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+EFLAGS=+debug_info
+
+all: ordsets1.@EMULATOR@
+
+ordsets1.@EMULATOR@: ordsets1.erl
+ erlc $(EFLAGS) ordsets1.erl
diff --git a/lib/debugger/test/int_break_SUITE_data/ordsets1.erl b/lib/debugger/test/int_break_SUITE_data/ordsets1.erl
new file mode 100644
index 0000000000..6300c6097c
--- /dev/null
+++ b/lib/debugger/test/int_break_SUITE_data/ordsets1.erl
@@ -0,0 +1,188 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Purpose : Functions for manipulating sets as ordered lists.
+
+%% As yet some of these are not very efficiently written.
+
+-module(ordsets1).
+
+-export([new_set/0,is_set/1,set_to_list/1,list_to_set/1]).
+-export([is_element/2,add_element/2,del_element/2]).
+-export([union/2,union/1,intersection/2,intersection/1]).
+-export([subtract/2,subset/2]).
+
+%% new_set()
+%% Return a new empty ordered set.
+
+new_set() ->
+ [].
+
+%% is_set(Set)
+%% Return 'true' if Set is an ordered set of elements, else 'false'.
+
+is_set([E|Es]) ->
+ is_set(Es, E);
+is_set([]) ->
+ true.
+
+is_set([E2|Es], E1) when E1 < E2 ->
+ is_set(Es, E2);
+is_set([E2|Es], E1) ->
+ false;
+is_set([], E1) ->
+ true.
+
+%% set_to_list(OrdSet)
+%% Return the elements in OrdSet as a list.
+
+set_to_list(S) ->
+ S.
+
+%% list_to_set(List)
+%% Build an ordered set from the elements in List.
+
+list_to_set([E|Es]) ->
+ add_element(E, list_to_set(Es));
+list_to_set([]) ->
+ [].
+
+%% is_element(Element, OrdSet)
+%% Return 'true' if Element is an element of OrdSet, else 'false'.
+
+is_element(E, [H|Es]) when E < H ->
+ false;
+is_element(E, [H|Es]) when E == H ->
+ true;
+is_element(E, [H|Es]) when E > H ->
+ is_element(E, Es);
+is_element(E, []) ->
+ false.
+
+%% add_element(Element, OrdSet)
+%% Return OrdSet with Element inserted in it.
+
+add_element(E, [H|_]=Es) when E < H ->
+ [E|Es];
+add_element(E, [H|_]=Es) when E == H ->
+ Es;
+add_element(E, [H|Es]) when E > H ->
+ [H|add_element(E, Es)];
+add_element(E, []) ->
+ [E].
+
+%% del_element(Element, OrdSet)
+%% Return OrdSet but with Element removed.
+
+del_element(E, [H|_]=Es) when E < H ->
+ Es;
+del_element(E, [H|Es]) when E == H ->
+ Es;
+del_element(E, [H|Es]) when E > H ->
+ [H|del_element(E, Es)];
+del_element(E, []) ->
+ [].
+
+%% union(Set1, Set2)
+%% Return the union of Set1 and Set2.
+
+union([H1|Es1], [H2|_]=Es2) when H1 < H2 ->
+ [H1|union(Es1, Es2)];
+union([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ [H1|union(Es1, Es2)];
+union([H1|_]=Es1, [H2|Es2]) when H1 > H2 ->
+ [H2|union(Es1, Es2)];
+union([], Es2) ->
+ Es2;
+union(Es1, []) ->
+ Es1.
+
+%% union(OrdSets)
+%% Return the union of the list of sets.
+
+union([S1,S2|Ss]) ->
+ union1(union(S1,S2), Ss);
+union([S]) ->
+ S;
+union([]) ->
+ [].
+
+union1(S1, [S2|Ss]) ->
+ union1(union(S1, S2), Ss);
+union1(S1, []) ->
+ S1.
+
+%% intersection(Set1, Set2)
+%% Return the intersection of Set1 and Set2.
+
+intersection([H1|Es1], [H2|_]=Es2) when H1 < H2 ->
+ intersection(Es1, Es2);
+intersection([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ [H1|intersection(Es1, Es2)];
+intersection([H1|_]=Es1, [H2|Es2]) when H1 > H2 ->
+ intersection(Es1, Es2);
+intersection([], Es2) ->
+ [];
+intersection(Es1, []) ->
+ [].
+
+%% intersection(OrdSets)
+%% Return the intersection of the list of sets.
+
+intersection([S1,S2|Ss]) ->
+ intersection1(intersection(S1,S2), Ss);
+intersection([S]) ->
+ S;
+intersection([]) ->
+ [].
+
+intersection1(S1, [S2|Ss]) ->
+ intersection1(intersection(S1, S2), Ss);
+intersection1(S1, []) ->
+ S1.
+
+%% subtract(Set1, Set2)
+%% Return all and only the elements of Set1 which are not also in Set2.
+
+subtract([H1|Es1], [H2|_]=Es2) when H1 < H2 ->
+ [H1|subtract(Es1, Es2)];
+subtract([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ subtract(Es1, Es2);
+subtract([H1|_]=Es1, [H2|Es2]) when H1 > H2 ->
+ subtract(Es1, Es2);
+subtract([], Es2) ->
+ [];
+subtract(Es1, []) ->
+ Es1.
+
+%% subset(Set1, Set2)
+%% Return 'true' when every element of Set1 is also a member of Set2,
+%% else 'false'.
+
+subset([H1|Es1], [H2|Es2]) when H1 < H2 -> %H1 not in Set2
+ false;
+subset([H1|Es1], [H2|Es2]) when H1 == H2 ->
+ subset(Es1, Es2);
+subset([H1|Es1], [H2|Es2]) when H1 > H2 ->
+ subset([H1|Es1], Es2);
+subset([], Es2) ->
+ true;
+subset(Es1, []) ->
+ false.
diff --git a/lib/debugger/test/int_eval_SUITE.erl b/lib/debugger/test/int_eval_SUITE.erl
new file mode 100644
index 0000000000..f36ed213d1
--- /dev/null
+++ b/lib/debugger/test/int_eval_SUITE.erl
@@ -0,0 +1,291 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(int_eval_SUITE).
+
+%% Purpose: Deeper test of the evaluator.
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+ bifs_outside_erlang/1, spawning/1, applying/1,
+ catch_and_throw/1, external_call/1, test_module_info/1,
+ apply_interpreted_fun/1, apply_uninterpreted_fun/1,
+ interpreted_exit/1, otp_8310/1]).
+
+%% Helpers.
+-export([applier/3]).
+
+-define(IM, my_int_eval_module).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
+all() ->
+ [bifs_outside_erlang, spawning, applying,
+ catch_and_throw, external_call, test_module_info,
+ apply_interpreted_fun, apply_uninterpreted_fun,
+ interpreted_exit, otp_8310].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(_Case, Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {module,?IM} = int:i(filename:join(DataDir, ?IM)),
+ ?line ok = io:format("Interpreted modules: ~p",[int:interpreted()]),
+ Config.
+
+end_per_testcase(_Case, _Config) ->
+ ok = io:format("Interpreted modules: ~p", [int:interpreted()]),
+ ok.
+
+bifs_outside_erlang(doc) ->
+ "Test that BIFs outside the erlang module are correctly evaluated.";
+bifs_outside_erlang(suite) ->
+ [];
+bifs_outside_erlang(Config) when is_list(Config) ->
+ Fun = fun() ->
+ Id = ?IM:ets_new(),
+ Self = self(),
+ ok = io:format("Self: ~p", [Self]),
+ Info = ets:info(Id),
+ Self = proplists:get_value(owner, Info),
+ ?IM:ets_delete(Id),
+ ok
+ end,
+ ?line ok = spawn_eval(Fun),
+ ok.
+
+spawning(doc) ->
+ "Try evalutate spawn_link/3.";
+spawning(suite) ->
+ [];
+spawning(Config) when is_list(Config) ->
+ ?line ok = spawn_eval(fun() -> ?IM:spawn_test() end).
+
+applying(doc) ->
+ "Try various sorts of applies.";
+applying(suite) ->
+ [];
+applying(Config) when is_list(Config) ->
+ Fun = fun({number,X}, {number,Y}) -> X+Y end,
+ ?line ok = spawn_eval(fun() -> ?IM:apply_test(Fun) end).
+
+catch_and_throw(doc) ->
+ "Test catch and throw/1.";
+catch_and_throw(suite) ->
+ [];
+catch_and_throw(Config) when is_list(Config) ->
+ {a,ball} = spawn_eval(fun() -> ok = ?IM:catch_a_ball(),
+ catch ?IM:throw_a_ball() end),
+
+ %% Throw and catch without any extra outer catch.
+
+ ?line process_flag(trap_exit, true),
+ ?line Pid1 = spawn_link(fun() -> exit(?IM:catch_a_ball()) end),
+ receive
+ {'EXIT',Pid1,ok} -> ok;
+ {'EXIT',Pid1,Bad1} -> ?line ?t:fail({bad_message,Bad1})
+ after 5000 ->
+ ?line ?t:fail(timeout)
+ end,
+
+
+ %% Throw without catch.
+
+ ?line Pid2 = spawn_link(fun() -> ?IM:throw_a_ball() end),
+ receive
+ {'EXIT',Pid2,{{nocatch,{a,ball}},[_|_]}} -> ok;
+ {'EXIT',Pid2,Bad2} -> ?line ?t:fail({bad_message,Bad2})
+ after 5000 ->
+ ?line ?t:fail(timeout)
+ end,
+
+ ?line ok = ?IM:more_catch(fun(_) -> ?IM:exit_me() end),
+ ?line ok = ?IM:more_catch(fun(_) -> exit({unint, exit}) end),
+ ?line {a, ball} = ?IM:more_catch(fun(_) -> ?IM:throw_a_ball() end),
+ ?line {b, ball} = ?IM:more_catch(fun(_) -> throw({b,ball}) end),
+
+ ExitInt = {'EXIT',{int,exit}},
+ ExitU = {'EXIT',{unint,exit}},
+
+ ?line ExitInt = (catch ?IM:more_nocatch(fun(_) -> ?IM:exit_me() end)),
+ ?line ExitU = (catch ?IM:more_nocatch(fun(_) -> exit({unint, exit}) end)),
+ ?line {a, ball} = (catch {error, ?IM:more_nocatch(fun(_) -> ?IM:throw_a_ball() end)}),
+ ?line {b, ball} = (catch {error, ?IM:more_nocatch(fun(_) -> throw({b,ball}) end)}),
+ ok.
+
+external_call(doc) ->
+ "Test external calls.";
+external_call(suite) ->
+ [];
+external_call(Config) when is_list(Config) ->
+ ?line ok = spawn_eval(fun() -> ?IM:external_call_test({some,stupid,data}) end).
+
+test_module_info(doc) ->
+ "Test the module_info/0,1 functions.";
+test_module_info(suite) ->
+ [];
+test_module_info(Config) when is_list(Config) ->
+ ?line ModInfo = ?IM:module_info(),
+ ?line {value,{exports,Exp}} = lists:keysearch(exports, 1, ModInfo),
+ ?line {value,{attributes,Attr}} = lists:keysearch(attributes, 1, ModInfo),
+ ?line Exp = ?IM:module_info(exports),
+ ?line Attr = ?IM:module_info(attributes),
+ ?line {value,{stupid_attribute,[{a,b}]}} =
+ lists:keysearch(stupid_attribute, 1, Attr),
+
+ %% Check exports using a list comprehension in the module itself.
+
+ ?line ok = ?IM:check_exports(Exp),
+
+ %% Call module_info/0,1 from the module itself.
+
+ ?line ok = ?IM:check_module_info(ModInfo, Exp),
+
+ ok.
+
+apply_interpreted_fun(doc) ->
+ "Apply a fun defined in interpreted code.";
+apply_interpreted_fun(suite) -> [];
+apply_interpreted_fun(Config) when is_list(Config) ->
+
+ %% Called from uninterpreted code
+ ?line F1 = spawn_eval(fun() -> ?IM:give_me_a_fun_0() end),
+ ?line perfectly_alright = spawn_eval(fun() -> F1() end),
+ ?line ATerm = {a,term},
+ ?line F2 = spawn_eval(fun() -> ?IM:give_me_a_fun_0(ATerm) end),
+ ?line {ok,ATerm} = spawn_eval(fun() -> F2() end),
+
+ %% Called from uninterpreted code, badarity
+ ?line {'EXIT',{{badarity,{F1,[snape]}},[{?MODULE,_,_}|_]}} =
+ spawn_eval(fun() -> F1(snape) end),
+
+ %% Called from uninterpreted code, error in fun
+ ?line F3 = spawn_eval(fun() -> ?IM:give_me_a_bad_fun() end),
+ ?line {'EXIT',{snape,[{?IM,_FunName,_}|_]}} =
+ spawn_eval(fun() -> F3(snape) end),
+
+ %% Called from within interpreted code
+ ?line perfectly_alright = spawn_eval(fun() -> ?IM:do_apply(F1) end),
+
+ %% Called from within interpreted code, badarity
+ ?line {'EXIT',{{badarity,{F1,[snape]}},[{?IM,do_apply,_}|_]}} =
+ spawn_eval(fun() -> ?IM:do_apply(F1, snape) end),
+
+ %% Called from within interpreted code, error in fun
+ ?line {'EXIT',{snape,[{?IM,_FunName,_}|_]}} =
+ spawn_eval(fun() -> ?IM:do_apply(F3, snape) end),
+
+ %% Try some more complex funs.
+ ?line F4 = ?IM:give_me_a_fun_1(14, 42),
+ ?line {false,yes,yeah,false} =
+ F4({{1,nope},{14,yes},{42,yeah},{100,forget_it}}),
+ ?line [this_is_ok,me_too] =
+ F4([{-24,no_way},{15,this_is_ok},{1333,forget_me},{37,me_too}]),
+
+ %% OTP-5837
+ %% Try fun with guard containing variable bound in environment
+ ?line [yes,no,no,no] = ?IM:otp_5837(1),
+
+ ok.
+
+apply_uninterpreted_fun(doc) ->
+ "Apply a fun defined outside interpreted code.";
+apply_uninterpreted_fun(suite) -> [];
+apply_uninterpreted_fun(Config) when is_list(Config) ->
+
+ ?line F1 = fun(snape) ->
+ erlang:error(snape);
+ (_Arg) ->
+ perfectly_alright
+ end,
+
+ %% Ok
+ ?line perfectly_alright =
+ spawn_eval(fun() -> ?IM:do_apply(F1, any_arg) end),
+
+ %% Badarity (evaluated in dbg_debugged, which calls erlang:apply/2)
+ ?line {'EXIT',{{badarity,{F1,[]}},[{erlang,apply,_}|_]}} =
+ spawn_eval(fun() -> ?IM:do_apply(F1) end),
+
+ %% Error in fun
+ ?line {'EXIT',{snape,[{?MODULE,_FunName,_}|_]}} =
+ spawn_eval(fun() -> ?IM:do_apply(F1, snape) end),
+
+ ok.
+
+%%
+%% Try executing an interpreted exit/1 call.
+%%
+
+interpreted_exit(Config) when is_list(Config) ->
+ ?line process_flag(trap_exit, true),
+ ?line Reason = make_ref(),
+ ?line Pid = spawn_link(fun() -> ?IM:please_call_exit(Reason) end),
+ ?line receive
+ {'EXIT',Pid,Reason} ->
+ ok;
+ {'EXIT',Pid,BadReason} ->
+ ?line ?t:fail({bad_message,BadReason})
+ after 10000 ->
+ ?line ?t:fail(timeout)
+ end,
+ ok.
+
+otp_8310(doc) ->
+ "OTP-8310. Bugfixes lc/bc and andalso/orelse.";
+otp_8310(Config) when is_list(Config) ->
+ ?line ok = ?IM:otp_8310(),
+ ok.
+
+applier(M, F, A) ->
+ Res = apply(M, F, A),
+ io:format("~p:~p(~p) => ~p\n", [M,F,A,Res]),
+ Res.
+
+%%
+%% Evaluate in another process, to prevent the test_case process to become
+%% interpreted.
+%%
+
+spawn_eval(Fun) ->
+ Self = self(),
+ spawn_link(fun() -> Self ! (catch Fun()) end),
+ receive
+ Result ->
+ Result
+ end.
diff --git a/lib/debugger/test/int_eval_SUITE_data/Makefile.src b/lib/debugger/test/int_eval_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..28a1432157
--- /dev/null
+++ b/lib/debugger/test/int_eval_SUITE_data/Makefile.src
@@ -0,0 +1,26 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+
+EFLAGS=+debug_info
+
+all: my_int_eval_module.@EMULATOR@
+
+my_int_eval_module.@EMULATOR@: my_int_eval_module.erl
+ erlc $(EFLAGS) my_int_eval_module.erl
diff --git a/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
new file mode 100644
index 0000000000..997ee6e17d
--- /dev/null
+++ b/lib/debugger/test/int_eval_SUITE_data/my_int_eval_module.erl
@@ -0,0 +1,245 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(my_int_eval_module).
+-stupid_attribute({a,b}).
+
+-export([ets_new/0,ets_delete/1,spawn_test/0,apply_test/1,external_call_test/1]).
+-export([check_exports/1,check_module_info/2]).
+-export([give_me_a_fun_0/0,give_me_a_fun_0/1,give_me_a_fun_1/2,
+ give_me_a_bad_fun/0, do_apply/1, do_apply/2]).
+-export([please_call_exit/1,i_will_do_the_exit/1]).
+-export([more_catch/1,more_nocatch/1,exit_me/0]).
+-export([f/1, f_try/1, f_catch/1]).
+-export([otp_5837/1, otp_8310/0]).
+
+%% Internal exports.
+-export([echo/2,my_subtract/2,catch_a_ball/0,throw_a_ball/0]).
+-export([i_am_exported/1]).
+
+-import(lists, [member/2]).
+
+-define(line,put(test_server_loc,{?MODULE,?LINE}),).
+-define(t,test_server).
+-define(m,test_server:match).
+-define(config,test_server:lookup_config).
+
+ets_new() ->
+ Id = ets:new(my_int_eval_table, [private]),
+ Id.
+
+ets_delete(Tab) ->
+ ets:delete(Tab).
+
+%% Spawning.
+
+spawn_test() ->
+ Term = {a,tuple},
+ Pid = spawn_link(?MODULE, echo, [self(),Term]),
+ receive
+ {result,Pid,Term} -> ok;
+ Other -> {bad_response,Other}
+ after 5000 ->
+ timeout
+ end.
+
+echo(Parent, Term) ->
+ Parent ! {result,self(),Term}.
+
+%% Applying.
+
+apply_test(Fun) ->
+ 42 = Fun(number(2), number(40)),
+ 12 = apply(Fun, [number(7),number(5)]),
+
+ Mod = module(),
+ Func = func(),
+ [a] = Mod:my_subtract(same([a,b,c]), same([b,c])),
+ [a,b] = Mod:Func(same([a,b,c]), same([c])),
+ [a,b,d] = ?MODULE:Func(same([a,b,c,d]), same([c])),
+ [d,e] = apply(Mod, Func, [same([d,e,f]), same([f])]),
+ [3] = apply(?MODULE, Func, [same([3,4]),same([4])]),
+
+ %% This is obsolete, but it should work anyway.
+ HomeMadeFun = {?MODULE,my_subtract},
+ [a] = HomeMadeFun(same([a,x,c]), same([x,c])),
+ [x] = apply(HomeMadeFun, [[x,y],[y,z]]),
+
+ ok.
+
+number(X) -> {number,X}.
+module() -> ?MODULE.
+func() -> my_subtract.
+same(X) -> X.
+
+my_subtract(X, Y) -> X -- Y.
+
+%% Catch and throw.
+
+catch_a_ball() ->
+ {a,ball} = (catch throw_a_ball()),
+ ok.
+
+throw_a_ball() ->
+ throw({a,ball}),
+ not_ok.
+
+exit_me() ->
+ exit({int,exit}).
+
+more_catch(Fun) ->
+ case catch lists:filter(Fun, [a]) of
+ {'EXIT', {_, exit}} ->
+ ok;
+ Else -> Else
+ end.
+
+more_nocatch(Fun) ->
+ lists:filter(Fun, [a]).
+
+%% External calls.
+
+external_call_test(Data) ->
+ {'EXIT',{undef,[{?MODULE,not_exported,[42,Data]}|_]}} =
+ (catch ?MODULE:not_exported(42, Data)),
+ {yes,Data} = i_am_exported(Data),
+ {yes,Data} = ?MODULE:i_am_exported(Data),
+
+ %% Excercise the function cache in the interpreter.
+
+ {ok,Data,[a,b]} = not_exported(Data, [a,b]),
+ {yes,Data} = i_am_exported(Data),
+ {ok,Data,[a,b]} = not_exported(Data, [a,b]),
+ {'EXIT',{undef,[{?MODULE,not_exported,[7,Data]}|_]}} =
+ (catch ?MODULE:not_exported(7, Data)),
+ {yes,Data} = ?MODULE:i_am_exported(Data),
+ ok.
+
+not_exported(N, D) ->
+ {ok,N,D}.
+
+i_am_exported(D) ->
+ {yes,D}.
+
+%% The module_info/0,1 functions and list comprehensions (funs).
+
+check_exports(Exp) ->
+ %% Check the structure of the export list and that there are more
+ %% than 4 elements.
+
+ Exp = [{F,A} || {F,A} <- Exp, erlang:is_atom(F), erlang:is_integer(A)],
+ case length(Exp) of
+ Len when Len > 4 -> ok
+ end.
+
+check_module_info(ModInfo, Exports) ->
+ ModInfo = module_info(),
+ Exports = module_info(exports),
+ ok.
+
+%% Testcase apply_interpreted_fun/1.
+
+give_me_a_fun_0() ->
+ fun() -> perfectly_alright end.
+
+give_me_a_fun_0(Term) ->
+ fun() -> {ok,Term} end.
+
+give_me_a_fun_1(Min, Max) ->
+ Seq = lists:seq(Min, Max),
+ fun (L) when list(L) ->
+ [Info || {Key,Info} <- L, lists:member(Key, Seq)];
+ (T) when tuple(T) ->
+ L = tuple_to_list(T),
+ F = fun({Key,Info}) ->
+ case lists:member(Key, Seq) of
+ true -> Info;
+ false -> false
+ end
+ end,
+ list_to_tuple(lists:map(F, L))
+ end.
+
+give_me_a_bad_fun() ->
+ fun(Arg) -> erlang:error(Arg) end.
+
+do_apply(Fun) ->
+ Fun().
+do_apply(Fun, Arg) ->
+ Fun(Arg).
+
+
+please_call_exit(Reason) ->
+ put(asked_to_call_exit, Reason),
+ put(will_call_my_good_friend, ''),
+ Res = int_eval_SUITE:applier(?MODULE, i_will_do_the_exit, [Reason]),
+
+ %% We don't want a tail-recursive call above.
+ io:format("Returned from exit/1 -- how strange\n").
+
+i_will_do_the_exit(Reason) ->
+ exit(Reason).
+
+f(Arg) ->
+ g(Arg).
+
+f_try(Arg) ->
+ try g(Arg)
+ catch
+ Class:Reason ->
+ {Class, Reason}
+ end.
+
+f_catch(Arg) ->
+ catch g(Arg).
+
+g({error, Reason}) ->
+ erlang:error(Reason);
+g({exit, Reason}) ->
+ erlang:exit(Reason);
+g({throw, Reason}) ->
+ erlang:throw(Reason);
+g(Value) ->
+ Value.
+
+otp_5837(N) ->
+ n(N).
+
+n(N) ->
+ lists:map(fun(X) when N==X ->
+ yes;
+ (_) ->
+ no
+ end,
+ [1,2,3,4]).
+
+otp_8310() ->
+ a = if (false orelse a) =:= a -> a; true -> b end,
+ F1 = fun() -> a end,
+ {'EXIT',{{bad_filter,a},_}} =
+ (catch {a, [X || X <- [1,2,3], _ = F1()]}),
+ F2 = fun() -> << 3:8 >> end,
+ {'EXIT',{{bad_filter,<<3>>},_}} =
+ (catch {a, << << X >> || << X >> <= << 7:8 >>,_ = F2() >>}),
+ {'EXIT',{{bad_generator,a},_}} =
+ (catch {a, [X || X <- a]}),
+ {'EXIT',{{bad_generator,b},_}} =
+ (catch {a, << <<X>> || << X >> <= b >>}),
+ ok.
diff --git a/lib/debugger/test/lc_SUITE.erl b/lib/debugger/test/lc_SUITE.erl
new file mode 100644
index 0000000000..92a03ef58e
--- /dev/null
+++ b/lib/debugger/test/lc_SUITE.erl
@@ -0,0 +1,88 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(lc_SUITE).
+
+-author('[email protected]').
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ basic/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [basic].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+basic(Config) when list(Config) ->
+ ?line L0 = lists:seq(1, 10),
+ ?line L1 = my_map(fun(X) -> {x,X} end, L0),
+ ?line L1 = [{x,X} || X <- L0],
+ ?line L0 = my_map(fun({x,X}) -> X end, L1),
+ ?line [1,2,3,4,5] = [X || X <- L0, X < 6],
+ ?line [4,5,6] = [X || X <- L0, X > 3, X < 7],
+ ?line [] = [X || X <- L0, X > 32, X < 7],
+ ?line [1,3,5,7,9] = [X || X <- L0, odd(X)],
+
+ %% Error cases.
+ ?line [] = [X || X <- L1, X+1 < 2],
+ ?line [] = [{xx,X} || X <- L0, element(2, X) == no_no_no],
+ ?line {'EXIT',_} = (catch [X || X <- L1, odd(X)]),
+
+ ok.
+
+my_map(F, L) ->
+ [F(X) || X <- L].
+
+odd(X) ->
+ X rem 2 == 1.
diff --git a/lib/debugger/test/record_SUITE.erl b/lib/debugger/test/record_SUITE.erl
new file mode 100644
index 0000000000..873bbdb4bc
--- /dev/null
+++ b/lib/debugger/test/record_SUITE.erl
@@ -0,0 +1,266 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%% Purpose : Test records.
+
+-module(record_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ errors/1,record_test/1,eval_once/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [errors, record_test, eval_once].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+-record(foo, {a,b,c,d}).
+-record(bar, {a,b,c,d}).
+-record(barf, {a,b,c,d,e}).
+
+errors(Config) when is_list(Config) ->
+ Foo = #foo{a=1,b=2,c=3,d=4},
+ ?line #foo{a=19,b=42,c=3,d=4} = update_foo(Foo, 19, 42),
+
+ ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19)),
+ ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35)),
+ ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17)),
+ ?line {'EXIT',{{badrecord,bar},_}} = (catch update_foo_bar(Foo, 19, 35, 17, 42)),
+
+ ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19)),
+ ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35)),
+ ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17)),
+ ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19, 35, 17, 42)),
+ ?line {'EXIT',{{badrecord,barf},_}} = (catch update_foo_barf(Foo, 19,
+ 35, 17, 42, -2)),
+
+ ok.
+
+update_foo(#foo{}=R, A, B) ->
+ R#foo{a=A,b=B}.
+
+update_foo_bar(#foo{}=R, A) ->
+ R#bar{a=A}.
+
+update_foo_bar(#foo{}=R, A, _B) ->
+ R#bar{a=A,b=A}.
+
+update_foo_bar(#foo{}=R, A, _B, C) ->
+ R#bar{a=A,b=A,c=C}.
+
+update_foo_bar(#foo{}=R, A, _B, C, D) ->
+ R#bar{a=A,b=A,c=C,d=D}.
+
+update_foo_barf(#foo{}=R, A) ->
+ R#barf{a=A}.
+
+update_foo_barf(#foo{}=R, A, _B) ->
+ R#barf{a=A,b=A}.
+
+update_foo_barf(#foo{}=R, A, _B, C) ->
+ R#barf{a=A,b=A,c=C}.
+
+update_foo_barf(#foo{}=R, A, _B, C, D) ->
+ R#barf{a=A,b=A,c=C,d=D}.
+
+update_foo_barf(#foo{}=R, A, _B, C, D, E) ->
+ R#barf{a=A,b=A,c=C,d=D,e=E}.
+
+
+-define(TrueGuard(Expr), if Expr -> ok; true -> ?t:fail() end).
+-define(FalseGuard(Expr), if Expr -> ?t:fail(); true -> ok end).
+
+record_test(Config) when is_list(Config) ->
+ ?line true = is_record(#foo{}, foo),
+ ?line false = is_record(#foo{}, barf),
+ ?line false = is_record({foo}, foo),
+
+ ?line true = erlang:is_record(#foo{}, foo),
+ ?line false = erlang:is_record(#foo{}, barf),
+ ?line false = erlang:is_record({foo}, foo),
+
+ ?line false = is_record([], foo),
+ ?line false = is_record(Config, foo),
+
+ ?line ?TrueGuard(is_record(#foo{}, foo)),
+ ?line ?FalseGuard(is_record(#foo{}, barf)),
+ ?line ?FalseGuard(is_record({foo}, foo)),
+
+ ?line ?TrueGuard(erlang:is_record(#foo{}, foo)),
+ ?line ?FalseGuard(erlang:is_record(#foo{}, barf)),
+ ?line ?FalseGuard(erlang:is_record({foo}, foo)),
+
+ ?line ?FalseGuard(is_record([], foo)),
+ ?line ?FalseGuard(is_record(Config, foo)),
+
+ %% 'not is_record/2' to test guard optimization.
+
+ ?line ?FalseGuard(not is_record(#foo{}, foo)),
+ ?line ?TrueGuard(not is_record(#foo{}, barf)),
+ ?line ?TrueGuard(not is_record({foo}, foo)),
+
+ ?line ?FalseGuard(not erlang:is_record(#foo{}, foo)),
+ ?line ?TrueGuard(not erlang:is_record(#foo{}, barf)),
+ ?line ?TrueGuard(not erlang:is_record({foo}, foo)),
+
+ Foo = id(#foo{}),
+ ?line ?FalseGuard(not erlang:is_record(Foo, foo)),
+ ?line ?TrueGuard(not erlang:is_record(Foo, barf)),
+
+ ?line ?TrueGuard(not is_record(Config, foo)),
+
+ ?line ?TrueGuard(not is_record(a, foo)),
+ ?line ?TrueGuard(not is_record([], foo)),
+
+ %% Pass non-literal first argument.
+
+ ?line true = is_record(id(#foo{}), foo),
+ ?line false = is_record(id(#foo{}), barf),
+ ?line false = is_record(id({foo}), foo),
+
+ ?line true = erlang:is_record(id(#foo{}), foo),
+ ?line false = erlang:is_record(id(#foo{}), barf),
+ ?line false = erlang:is_record(id({foo}), foo),
+
+ NoRec1 = id(blurf),
+ NoRec2 = id([]),
+
+ ?line ?TrueGuard(not is_record(NoRec1, foo)),
+ ?line ?TrueGuard(not is_record(NoRec2, foo)),
+
+ %% Force the use of guard bifs by using the 'xor' operation.
+
+ False = id(false),
+ ?line ?TrueGuard(is_record(#foo{}, foo) xor False),
+ ?line ?FalseGuard(is_record(#foo{}, barf) xor False),
+ ?line ?FalseGuard(is_record({foo}, foo) xor False ),
+
+ ?line ?TrueGuard(is_record(Foo, foo) xor False),
+ ?line ?FalseGuard(is_record(Foo, barf) xor False),
+
+
+ %% Implicit guards by using a list comprehension.
+
+ List = id([1,#foo{a=2},3,#bar{d=4},5,#foo{a=6},7]),
+
+ ?line [#foo{a=2},#foo{a=6}] = [X || X <- List, is_record(X, foo)],
+ ?line [#bar{d=4}] = [X || X <- List, is_record(X, bar)],
+ ?line [1,#foo{a=2},3,5,#foo{a=6},7] =
+ [X || X <- List, not is_record(X, bar)],
+ ?line [1,3,5,7] =
+ [X || X <- List, ((not is_record(X, bar)) and (not is_record(X, foo)))],
+ ?line [#foo{a=2},#bar{d=4},#foo{a=6}] =
+ [X || X <- List, ((is_record(X, bar)) or (is_record(X, foo)))],
+ ?line [1,3,#bar{d=4}] =
+ [X || X <- List, ((is_record(X, bar)) or (X < 5))],
+
+ ?line MyList = [#foo{a=3},x,[],{a,b}],
+ ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo)],
+ ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo)],
+ ?line [#foo{a=3}] = [X || X <- MyList, begin is_record(X, foo) end],
+ ?line [x,[],{a,b}] = [X || X <- MyList, begin not is_record(X, foo) end],
+ ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, is_record(X, foo) or
+ not is_binary(X)],
+ ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
+ not is_binary(X)],
+ ?line [#foo{a=3}] = [X || X <- MyList, is_record(X, foo) or is_reference(X)],
+ ?line [x,[],{a,b}] = [X || X <- MyList, not is_record(X, foo) or
+ is_reference(X)],
+ ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
+ begin is_record(X, foo) or
+ not is_binary(X) end],
+ ?line [#foo{a=3},x,[],{a,b}] = [X || X <- MyList,
+ begin not is_record(X, foo) or
+ not is_binary(X) end],
+ ?line [#foo{a=3}] = [X || X <- MyList,
+ begin is_record(X, foo) or is_reference(X) end],
+ ?line [x,[],{a,b}] = [X || X <- MyList,
+ begin not is_record(X, foo) or
+ is_reference(X) end],
+ ok.
+
+eval_once(Config) when is_list(Config) ->
+ ?line once(fun(GetRec) ->
+ true = erlang:is_record(GetRec(), foo)
+ end, #foo{}),
+ ?line once(fun(GetRec) ->
+ (GetRec())#foo{a=1}
+ end, #foo{}),
+ ?line once(fun(GetRec) ->
+ (GetRec())#foo{a=1,b=2}
+ end, #foo{}),
+ ?line once(fun(GetRec) ->
+ (GetRec())#foo{a=1,b=2,c=3}
+ end, #foo{}),
+ ?line once(fun(GetRec) ->
+ (GetRec())#foo{a=1,b=2,c=3,d=4}
+ end, #foo{}),
+ ok.
+
+once(Test, Record) ->
+ put(?MODULE, 0),
+ GetRec = fun() ->
+ put(?MODULE, 1+get(?MODULE)),
+ Record
+ end,
+ Result = Test(GetRec),
+ case get(?MODULE) of
+ 1 -> ok;
+ N ->
+ io:format("Evaluated ~w times\n", [N]),
+ ?t:fail()
+ end,
+ Result.
+
+id(I) -> I.
diff --git a/lib/debugger/test/test_lib.erl b/lib/debugger/test/test_lib.erl
new file mode 100644
index 0000000000..541375e64a
--- /dev/null
+++ b/lib/debugger/test/test_lib.erl
@@ -0,0 +1,29 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+-module(test_lib).
+
+-export([interpret/1]).
+
+interpret(Mod) when atom(Mod) ->
+ case lists:member(Mod, int:interpreted()) of
+ true -> ok;
+ false -> {module,Mod} = i:ii(Mod)
+ end.
diff --git a/lib/debugger/test/trycatch_SUITE.erl b/lib/debugger/test/trycatch_SUITE.erl
new file mode 100644
index 0000000000..a87c5db138
--- /dev/null
+++ b/lib/debugger/test/trycatch_SUITE.erl
@@ -0,0 +1,810 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(trycatch_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
+ init_per_suite/1,end_per_suite/1,
+ basic/1,lean_throw/1,try_of/1,try_after/1,%after_bind/1,
+ catch_oops/1,after_oops/1,eclectic/1,rethrow/1,
+ nested_of/1,nested_catch/1,nested_after/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [basic, lean_throw, try_of, try_after, catch_oops,
+ after_oops, eclectic, rethrow, nested_of, nested_catch,
+ nested_after].
+
+init_per_testcase(_Case, Config) ->
+ test_lib:interpret(?MODULE),
+ Dog = test_server:timetrap(?t:minutes(1)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line test_lib:interpret(?MODULE),
+ ?line true = lists:member(?MODULE, int:interpreted()),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ok.
+
+basic(Conf) when is_list(Conf) ->
+ ?line 2 =
+ try my_div(4, 2)
+ catch
+ Class:Reason -> {Class,Reason}
+ end,
+ ?line error =
+ try my_div(1, 0)
+ catch
+ error:badarith -> error
+ end,
+ ?line error =
+ try 1/0
+ catch
+ error:badarith -> error
+ end,
+ ?line ok =
+ try my_add(53, atom)
+ catch
+ error:badarith -> ok
+ end,
+ ?line exit_nisse =
+ try exit(nisse)
+ catch
+ exit:nisse -> exit_nisse
+ end,
+ ?line ok =
+ try throw(kalle)
+ catch
+ kalle -> ok
+ end,
+
+ %% Try some stuff where the compiler will optimize away the try.
+
+ V = id({a,variable}),
+ ?line V = try V catch nisse -> error end,
+ ?line 42 = try 42 catch nisse -> error end,
+ ?line [V] = try [V] catch nisse -> error end,
+ ?line {ok,V} = try {ok,V} catch nisse -> error end,
+
+ %% Same idea, but use an after too.
+
+ ?line V = try V catch nisse -> error after after_call() end,
+ ?line after_clean(),
+ ?line 42 = try 42 after after_call() end,
+ ?line after_clean(),
+ ?line [V] = try [V] catch nisse -> error after after_call() end,
+ ?line after_clean(),
+ ?line {ok,V} = try {ok,V} after after_call() end,
+
+ %% Try/of
+ ?line ok = try V of
+ {a,variable} -> ok
+ catch nisse -> erro
+ end,
+
+ ok.
+
+after_call() ->
+ put(basic, after_was_called).
+
+after_clean() ->
+ after_was_called = erase(basic).
+
+lean_throw(Conf) when is_list(Conf) ->
+ ?line {throw,kalle} =
+ try throw(kalle)
+ catch
+ Kalle -> {throw,Kalle}
+ end,
+ ?line {exit,kalle} =
+ try exit(kalle)
+ catch
+ Throw1 -> {throw,Throw1};
+ exit:Reason1 -> {exit,Reason1}
+ end,
+ ?line {exit,kalle} =
+ try exit(kalle)
+ catch
+ exit:Reason2 -> {exit,Reason2};
+ Throw2 -> {throw,Throw2}
+ end,
+ ?line {exit,kalle} =
+ try try exit(kalle)
+ catch
+ Throw3 -> {throw,Throw3}
+ end
+ catch
+ exit:Reason3 -> {exit,Reason3}
+ end,
+ ok.
+
+try_of(Conf) when is_list(Conf) ->
+ ?line {ok,{some,content}} =
+ try_of_1({value,{good,{some,content}}}),
+ ?line {error,[other,content]} =
+ try_of_1({value,{bad,[other,content]}}),
+ ?line {caught,{exit,{ex,it,[reason]}}} =
+ try_of_1({exit,{ex,it,[reason]}}),
+ ?line {caught,{throw,[term,{in,a,{tuple}}]}} =
+ try_of_1({throw,[term,{in,a,{tuple}}]}),
+ ?line {caught,{error,[bad,arg]}} =
+ try_of_1({error,[bad,arg]}),
+ ?line {caught,{error,badarith}} =
+ try_of_1({'div',{1,0}}),
+ ?line {caught,{error,badarith}} =
+ try_of_1({'add',{a,0}}),
+ ?line {caught,{error,badarg}} =
+ try_of_1({'abs',x}),
+ ?line {caught,{error,function_clause}} =
+ try_of_1(illegal),
+ ?line {error,{try_clause,{some,other_garbage}}} =
+ try try_of_1({value,{some,other_garbage}})
+ catch error:Reason -> {error,Reason}
+ end,
+ ok.
+
+try_of_1(X) ->
+ try foo(X) of
+ {good,Y} -> {ok,Y};
+ {bad,Y} -> {error,Y}
+ catch
+ Class:Reason ->
+ {caught,{Class,Reason}}
+ end.
+
+try_after(Conf) when is_list(Conf) ->
+ ?line {{ok,[some,value],undefined},finalized} =
+ try_after_1({value,{ok,[some,value]}},finalized),
+ ?line {{error,badarith,undefined},finalized} =
+ try_after_1({'div',{1,0}},finalized),
+ ?line {{error,badarith,undefined},finalized} =
+ try_after_1({'add',{1,a}},finalized),
+ ?line {{error,badarg,undefined},finalized} =
+ try_after_1({'abs',a},finalized),
+ ?line {{error,[the,{reason}],undefined},finalized} =
+ try_after_1({error,[the,{reason}]},finalized),
+ ?line {{throw,{thrown,[reason]},undefined},finalized} =
+ try_after_1({throw,{thrown,[reason]}},finalized),
+ ?line {{exit,{exited,{reason}},undefined},finalized} =
+ try_after_1({exit,{exited,{reason}}},finalized),
+ ?line {{error,function_clause,undefined},finalized} =
+ try_after_1(function_clause,finalized),
+ ?line ok =
+ try try_after_1({'add',{1,1}}, finalized)
+ catch
+ error:{try_clause,2} -> ok
+ end,
+ ?line finalized = erase(try_after),
+ ?line ok =
+ try try foo({exit,[reaso,{n}]})
+ after put(try_after, finalized)
+ end
+ catch
+ exit:[reaso,{n}] -> ok
+ end,
+ ok.
+
+try_after_1(X, Y) ->
+ erase(try_after),
+ Try =
+ try foo(X) of
+ {ok,Value} -> {ok,Value,get(try_after)}
+ catch
+ Reason -> {throw,Reason,get(try_after)};
+ error:Reason -> {error,Reason,get(try_after)};
+ exit:Reason -> {exit,Reason,get(try_after)}
+ after
+ put(try_after, Y)
+ end,
+ {Try,erase(try_after)}.
+
+-ifdef(begone).
+
+after_bind(Conf) when is_list(Conf) ->
+ V = [make_ref(),self()|value],
+ ?line {value,{value,V}} =
+ after_bind_1({value,V}, V, {value,V}),
+ ok.
+
+after_bind_1(X, V, Y) ->
+ try
+ Try =
+ try foo(X) of
+ V -> value
+ catch
+ C1:V -> {caught,C1}
+ after
+ After = foo(Y)
+ end,
+ {Try,After}
+ of
+ V -> {value,V}
+ catch
+ C:D -> {caught,{C,D}}
+ end.
+
+-endif.
+
+catch_oops(Conf) when is_list(Conf) ->
+ V = {v,[a,l|u],{e},self()},
+ ?line {value,V} = catch_oops_1({value,V}),
+ ?line {value,1} = catch_oops_1({'div',{1,1}}),
+ ?line {error,badarith} = catch_oops_1({'div',{1,0}}),
+ ?line {error,function_clause} = catch_oops_1(function_clause),
+ ?line {throw,V} = catch_oops_1({throw,V}),
+ ?line {exit,V} = catch_oops_1({exit,V}),
+ ok.
+
+catch_oops_1(X) ->
+ Ref = make_ref(),
+ try try foo({error,Ref})
+ catch
+ error:Ref ->
+ foo(X)
+ end of
+ Value -> {value,Value}
+ catch
+ Class:Data -> {Class,Data}
+ end.
+
+
+
+after_oops(Conf) when is_list(Conf) ->
+ V = {self(),make_ref()},
+ ?line {{value,V},V} = after_oops_1({value,V}, {value,V}),
+ ?line {{exit,V},V} = after_oops_1({exit,V}, {value,V}),
+ ?line {{error,V},undefined} = after_oops_1({value,V}, {error,V}),
+ ?line {{error,function_clause},undefined} =
+ after_oops_1({exit,V}, function_clause),
+ ok.
+
+after_oops_1(X, Y) ->
+ erase(after_oops),
+ Try =
+ try try foo(X)
+ after
+ put(after_oops, foo(Y))
+ end of
+ V -> {value,V}
+ catch
+ C:D -> {C,D}
+ end,
+ {Try,erase(after_oops)}.
+
+
+
+eclectic(Conf) when is_list(Conf) ->
+ V = {make_ref(),3.1415926535,[[]|{}]},
+ ?line {{value,{value,V},V},V} =
+ eclectic_1({foo,{value,{value,V}}}, undefined, {value,V}),
+ ?line {{'EXIT',{V,[{?MODULE,foo,_}|_]}},V} =
+ eclectic_1({catch_foo,{error,V}}, undefined, {value,V}),
+ ?line {{error,{exit,V},{'EXIT',V}},V} =
+ eclectic_1({foo,{error,{exit,V}}}, error, {value,V}),
+ ?line {{value,{value,V},V},{'EXIT',{badarith,[{?MODULE,my_add,_}|_]}}} =
+ eclectic_1({foo,{value,{value,V}}}, undefined, {'add',{0,a}}),
+ ?line {{'EXIT',V},V} =
+ eclectic_1({catch_foo,{exit,V}}, undefined, {throw,V}),
+ ?line {{error,{'div',{1,0}},{'EXIT',{badarith,[{?MODULE,my_div,_}|_]}}}, {'EXIT',V}} =
+ eclectic_1({foo,{error,{'div',{1,0}}}}, error, {exit,V}),
+ ?line {{{error,V},{'EXIT',{V,[{?MODULE,foo,_}|_]}}},{'EXIT',V}} =
+ eclectic_1({catch_foo,{throw,{error,V}}}, undefined, {exit,V}),
+ %%
+ ?line {{value,{value,{value,V},V}},V} =
+ eclectic_2({value,{value,V}}, undefined, {value,V}),
+ ?line {{value,{throw,{value,V},V}},V} =
+ eclectic_2({throw,{value,V}}, throw, {value,V}),
+ ?line {{caught,{'EXIT',V}},undefined} =
+ eclectic_2({value,{value,V}}, undefined, {exit,V}),
+ ?line {{caught,{'EXIT',{V,[{?MODULE,foo,_}|_]}}},undefined} =
+ eclectic_2({error,{value,V}}, throw, {error,V}),
+ ?line {{caught,{'EXIT',{badarg,[{erlang,abs,[V]}|_]}}},V} =
+ eclectic_2({value,{'abs',V}}, undefined, {value,V}),
+ ?line {{caught,{'EXIT',{badarith,[{?MODULE,my_add,_}|_]}}},V} =
+ eclectic_2({exit,{'add',{0,a}}}, exit, {value,V}),
+ ?line {{caught,{'EXIT',V}},undefined} =
+ eclectic_2({value,{error,V}}, undefined, {exit,V}),
+ ?line {{caught,{'EXIT',{V,[{?MODULE,foo,_}|_]}}},undefined} =
+ eclectic_2({throw,{'div',{1,0}}}, throw, {error,V}),
+ ok.
+
+eclectic_1(X, C, Y) ->
+ erase(eclectic),
+ Done = make_ref(),
+ Try =
+ try case X of
+ {catch_foo,V} -> catch {Done,foo(V)};
+ {foo,V} -> {Done,foo(V)}
+ end of
+ {Done,D} -> {value,D,catch foo(D)};
+ {'EXIT',_}=Exit -> Exit;
+ D -> {D,catch foo(D)}
+ catch
+ C:D -> {C,D,catch foo(D)}
+ after
+ put(eclectic, catch foo(Y))
+ end,
+ {Try,erase(eclectic)}.
+
+eclectic_2(X, C, Y) ->
+ Done = make_ref(),
+ erase(eclectic),
+ Catch =
+ case
+ catch
+ {Done,
+ try foo(X) of
+ V -> {value,V,foo(V)}
+ catch
+ C:D -> {C,D,foo(D)}
+ after
+ put(eclectic, foo(Y))
+ end} of
+ {Done,Z} -> {value,Z};
+ Z -> {caught,Z}
+ end,
+ {Catch,erase(eclectic)}.
+
+rethrow(Conf) when is_list(Conf) ->
+ V = {a,[b,{c,self()},make_ref]},
+ ?line {value2,value1} =
+ rethrow_1({value,V}, V),
+ ?line {caught2,{error,V}} =
+ rethrow_2({error,V}, undefined),
+ ?line {caught2,{exit,V}} =
+ rethrow_1({exit,V}, error),
+ ?line {caught2,{throw,V}} =
+ rethrow_1({throw,V}, undefined),
+ ?line {caught2,{throw,V}} =
+ rethrow_2({throw,V}, undefined),
+ ?line {caught2,{error,badarith}} =
+ rethrow_1({'add',{0,a}}, throw),
+ ?line {caught2,{error,function_clause}} =
+ rethrow_2(function_clause, undefined),
+ ?line {caught2,{error,{try_clause,V}}} =
+ rethrow_1({value,V}, exit),
+ ?line {value2,{caught1,V}} =
+ rethrow_1({error,V}, error),
+ ?line {value2,{caught1,V}} =
+ rethrow_1({exit,V}, exit),
+ ?line {value2,caught1} =
+ rethrow_2({throw,V}, V),
+ ok.
+
+rethrow_1(X, C1) ->
+ try try foo(X) of
+ C1 -> value1
+ catch
+ C1:D1 -> {caught1,D1}
+ end of
+ V2 -> {value2,V2}
+ catch
+ C2:D2 -> {caught2,{C2,D2}}
+ end.
+
+rethrow_2(X, C1) ->
+ try try foo(X) of
+ C1 -> value1
+ catch
+ C1 -> caught1 % Implicit class throw:
+ end of
+ V2 -> {value2,V2}
+ catch
+ C2:D2 -> {caught2,{C2,D2}}
+ end.
+
+
+
+nested_of(Conf) when is_list(Conf) ->
+ V = {[self()|make_ref()],1.4142136},
+ ?line {{value,{value1,{V,x2}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,{V,x1}},
+ {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{throw,{V,x2}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,{V,x1}},
+ {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,{V,x1}},
+ {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,{V,x1}},
+ {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}),
+ %%
+ ?line {{caught,{error,{try_clause,{V,x1}}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,try_clause},
+ void, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{exit,{V,x3}}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,try_clause},
+ void, {exit,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{throw,{V,x4}}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_of_1({{value,{V,x1}},void,try_clause},
+ void, {exit,{V,x3}}, {throw,{V,x4}}),
+ %%
+ ?line {{value,{caught1,{V,x2}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_of_1({{error,{V,x1}},error,{V,x1}},
+ {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_of_1({{error,{V,x1}},error,{V,x1}},
+ {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_of_1({{error,{V,x1}},error,{V,x1}},
+ {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarg}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_of_1({{error,{V,x1}},error,{V,x1}},
+ {'add',{1,c}}, {'div',{17,0}}, {'abs',V}),
+ %%
+ ?line {{caught,{error,badarith}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_of_1({{'add',{2,c}},rethrow,void},
+ void, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarg}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_of_1({{'add',{2,c}},rethrow,void},
+ void, {'abs',V}, {value,{V,x4}}),
+ ?line {{caught,{error,function_clause}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_of_1({{'add',{2,c}},rethrow,void},
+ void, {'abs',V}, function_clause),
+ ok.
+
+nested_of_1({X1,C1,V1},
+ X2, X3, X4) ->
+ erase(nested3),
+ erase(nested4),
+ erase(nested),
+ Self = self(),
+ Try =
+ try
+ try self()
+ of
+ Self ->
+ try
+ foo(X1)
+ of
+ V1 -> {value1,foo(X2)}
+ catch
+ C1:V1 -> {caught1,foo(X2)}
+ after
+ put(nested3, foo(X3))
+ end
+ after
+ put(nested4, foo(X4))
+ end
+ of
+ V -> {value,V}
+ catch
+ C:D -> {caught,{C,D}}
+ after
+ put(nested, finalized)
+ end,
+ {Try,erase(nested3),erase(nested4),erase(nested)}.
+
+
+
+nested_catch(Conf) when is_list(Conf) ->
+ V = {[make_ref(),1.4142136,self()]},
+ ?line {{value,{value1,{V,x2}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,{V,x1}},
+ {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{throw,{V,x2}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,{V,x1}},
+ {throw,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,{V,x1}},
+ {throw,{V,x2}}, {'div',{1,0}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,{V,x1}},
+ {throw,{V,x2}}, {'div',{1,0}}, {'add',{0,b}}),
+ %%
+ ?line {{caught,{error,{try_clause,{V,x1}}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,try_clause},
+ void, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{exit,{V,x3}}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,try_clause},
+ void, {exit,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{throw,{V,x4}}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_catch_1({{value,{V,x1}},void,try_clause},
+ void, {exit,{V,x3}}, {throw,{V,x4}}),
+ %%
+ ?line {{value,{caught1,{V,x2}}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_catch_1({{error,{V,x1}},error,{V,x1}},
+ {value,{V,x2}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_catch_1({{error,{V,x1}},error,{V,x1}},
+ {'add',{1,c}}, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_catch_1({{error,{V,x1}},error,{V,x1}},
+ {'add',{1,c}}, {'div',{17,0}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarg}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_catch_1({{error,{V,x1}},error,{V,x1}},
+ {'add',{1,c}}, {'div',{17,0}}, {'abs',V}),
+ %%
+ ?line {{caught,{error,badarith}},
+ {V,x3},
+ {V,x4},
+ finalized} =
+ nested_catch_1({{'add',{2,c}},rethrow,void},
+ void, {value,{V,x3}}, {value,{V,x4}}),
+ ?line {{caught,{error,badarg}},
+ undefined,
+ {V,x4},
+ finalized} =
+ nested_catch_1({{'add',{2,c}},rethrow,void},
+ void, {'abs',V}, {value,{V,x4}}),
+ ?line {{caught,{error,function_clause}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_catch_1({{'add',{2,c}},rethrow,void},
+ void, {'abs',V}, function_clause),
+ ok.
+
+nested_catch_1({X1,C1,V1},
+ X2, X3, X4) ->
+ erase(nested3),
+ erase(nested4),
+ erase(nested),
+ Throw = make_ref(),
+ Try =
+ try
+ try throw(Throw)
+ catch
+ Throw ->
+ try
+ foo(X1)
+ of
+ V1 -> {value1,foo(X2)}
+ catch
+ C1:V1 -> {caught1,foo(X2)}
+ after
+ put(nested3, foo(X3))
+ end
+ after
+ put(nested4, foo(X4))
+ end
+ of
+ V -> {value,V}
+ catch
+ C:D -> {caught,{C,D}}
+ after
+ put(nested, finalized)
+ end,
+ {Try,erase(nested3),erase(nested4),erase(nested)}.
+
+nested_after(Conf) when is_list(Conf) ->
+ V = [{make_ref(),1.4142136,self()}],
+ ?line {value,
+ {V,x3},
+ {value1,{V,x2}},
+ finalized} =
+ nested_after_1({{value,{V,x1}},void,{V,x1}},
+ {value,{V,x2}}, {value,{V,x3}}),
+ ?line {{caught,{error,{V,x2}}},
+ {V,x3},
+ undefined,
+ finalized} =
+ nested_after_1({{value,{V,x1}},void,{V,x1}},
+ {error,{V,x2}}, {value,{V,x3}}),
+ ?line {{caught,{exit,{V,x3}}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_after_1({{value,{V,x1}},void,{V,x1}},
+ {error,{V,x2}}, {exit,{V,x3}}),
+ %%
+ ?line {{caught,{error,{try_clause,{V,x1}}}},
+ {V,x3},
+ undefined,
+ finalized} =
+ nested_after_1({{value,{V,x1}},void,try_clause},
+ void, {value,{V,x3}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_after_1({{value,{V,x1}},void,try_clause},
+ void, {'div',{17,0}}),
+ %%
+ ?line {value,
+ {V,x3},
+ {caught1,{V,x2}},
+ finalized} =
+ nested_after_1({{throw,{V,x1}},throw,{V,x1}},
+ {value,{V,x2}}, {value,{V,x3}}),
+ ?line {{caught,{error,badarith}},
+ {V,x3},
+ undefined,
+ finalized} =
+ nested_after_1({{throw,{V,x1}},throw,{V,x1}},
+ {'add',{a,b}}, {value,{V,x3}}),
+ ?line {{caught,{error,badarg}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_after_1({{throw,{V,x1}},throw,{V,x1}},
+ {'add',{a,b}}, {'abs',V}),
+ %%
+ ?line {{caught,{throw,{V,x1}}},
+ {V,x3},
+ undefined,
+ finalized} =
+ nested_after_1({{throw,{V,x1}},rethrow,void},
+ void, {value,{V,x3}}),
+ ?line {{caught,{error,badarith}},
+ undefined,
+ undefined,
+ finalized} =
+ nested_after_1({{throw,{V,x1}},rethrow,void},
+ void, {'div',{1,0}}),
+ ok.
+
+nested_after_1({X1,C1,V1},
+ X2, X3) ->
+ erase(nested3),
+ erase(nested4),
+ erase(nested),
+ Self = self(),
+ Try =
+ try
+ try self()
+ after
+ After =
+ try
+ foo(X1)
+ of
+ V1 -> {value1,foo(X2)}
+ catch
+ C1:V1 -> {caught1,foo(X2)}
+ after
+ put(nested3, foo(X3))
+ end,
+ put(nested4, After)
+ end
+ of
+ Self -> value
+ catch
+ C:D -> {caught,{C,D}}
+ after
+ put(nested, finalized)
+ end,
+ {Try,erase(nested3),erase(nested4),erase(nested)}.
+
+foo({value,Value}) -> Value;
+foo({'div',{A,B}}) ->
+ my_div(A, B);
+foo({'add',{A,B}}) ->
+ my_add(A, B);
+foo({'abs',X}) ->
+ my_abs(X);
+foo({error,Error}) ->
+ erlang:error(Error);
+foo({throw,Throw}) ->
+ erlang:throw(Throw);
+foo({exit,Exit}) ->
+ erlang:exit(Exit);
+foo({raise,{Class,Reason}}) ->
+ erlang:raise(Class, Reason).
+%%foo(function_clause) -> % must not be defined!
+
+my_div(A, B) ->
+ A div B.
+
+my_add(A, B) ->
+ A + B.
+
+my_abs(X) -> abs(X).
+
+id(I) -> I.
diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk
index 5ce37a6bde..0f70dafc19 100644
--- a/lib/debugger/vsn.mk
+++ b/lib/debugger/vsn.mk
@@ -1 +1 @@
-DEBUGGER_VSN = 3.2.2
+DEBUGGER_VSN = 3.2.6
diff --git a/lib/dialyzer/RELEASE_NOTES b/lib/dialyzer/RELEASE_NOTES
index b668142327..4e311bb543 100644
--- a/lib/dialyzer/RELEASE_NOTES
+++ b/lib/dialyzer/RELEASE_NOTES
@@ -3,6 +3,54 @@
(in reversed chronological order)
==============================================================================
+Version 2.4.2 (in Erlang/OTP R14B02)
+------------------------------------
+ - Added --fullpath option to display files with warnings with their full
+ file names (thanks to Magnus Henoch for the original patch).
+ - Better handling of 'and'/'or'/'not' guards that generate warnings
+ (thanks to Stavros Aronis).
+ - Better blame assignment for cases when a function's spec is erroneous
+ (thanks to Stavros Aronis).
+ - More descriptive warnings when a tuple/record pattern contains subterms
+ that violate the declared types of record fields (thanks to Matthias Lang
+ for the test case and for Stavros Aronis for the actual fix).
+
+Version 2.4.0 (in Erlang/OTP R14B01)
+------------------------------------
+ - Added ability to supply multiple PLTs for the analysis (option --plts).
+ Currently these PLTs must be independent (i.e., no module appears in more
+ than one PLT) and there must not include files with module name clashes.
+ - Strengthened and streamlined hard-coded type information for some BIFs
+ and key library functions.
+ - Fixed pretty rare infinite loop when refining the types of an SCC whose
+ functions all returned none() (thanks to Stavros Aronis).
+ - Fixed pretty rare crash when taking the infimum of two tuple_sets.
+ - Fixed pretty rare crash when using parameterized types containing unbound
+ variables (thanks to Nicolas Trangez for reporting it).
+ - Deeper unfolding of recursive types (thanks to Maria Christakis).
+ - Fixed some incomplete and erroneous specs in modules of kernel and stdlib.
+ - Fixed problems in the handling of remote types in records used as types
+ (thanks to Nico Kruber for the report and to Maria Christakis for the fix).
+ - Fixed handling of nested opaque types (thanks to Thorsten Schuett for
+ reporting it and to Maria Christakis for fixing it).
+
+Version 2.3.1 (in Erlang/OTP R14B)
+----------------------------------
+ - Eliminated warnings for auto-imported BIF clashes.
+
+Version 2.3.0 (in Erlang/OTP R14A)
+----------------------------------
+ - Dialyzer properly supports the new attribute -export_type and checks
+ that remote types only refer to exported types. A warning is produced
+ if some files/applications refer to types defined in modules which are
+ neither in the PLT nor in the analyzed applications.
+ - Support for detecting data races involving whereis/1 and unregister/1.
+ - More precise identification of the reason(s) why a record construction
+ violates the types declared for its fields.
+ - Fixed bug in the handling of the 'or' guard.
+ - Better handling of the erlang:element/2 BIF.
+ - Complete handling of Erlang BIFs.
+
Version 2.2.0 (in Erlang/OTP R13B04)
------------------------------------
- Much better support for opaque types (thanks to Manouk Manoukian).
diff --git a/lib/dialyzer/doc/manual.txt b/lib/dialyzer/doc/manual.txt
index 470ddd6c73..1d7a1a6222 100644
--- a/lib/dialyzer/doc/manual.txt
+++ b/lib/dialyzer/doc/manual.txt
@@ -123,70 +123,87 @@ The exit status of the command line version is:
Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
- [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]*
- [--output_plt file] [-Wwarn]* [--src] [--gui | --wx]
- [files_or_dirs] [-r dirs] [--apps applications] [-o outfile]
+ [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]*
+ [-I include_dir]* [--output_plt file] [-Wwarn]*
+ [--src] [--gui | --wx] [files_or_dirs] [-r dirs]
+ [--apps applications] [-o outfile]
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
- [--no_native]
+ [--no_native] [--fullpath]
Options:
files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
Use Dialyzer from the command line to detect defects in the
specified files or directories containing .erl or .beam files,
- depending on the type of the analysis
+ depending on the type of the analysis.
-r dirs
Same as the previous but the specified directories are searched
recursively for subdirectories containing .erl or .beam files in
- them, depending on the type of analysis
+ them, depending on the type of analysis.
--apps applications
- Option typically used when building or modifying PLT as in:
+ Option typically used when building or modifying a plt as in:
dialyzer --build_plt --apps erts kernel stdlib mnesia ...
to conveniently refer to library applications corresponding to the
Erlang/OTP installation. However, the option is general and can also
be used during analysis in order to refer to Erlang/OTP applications.
In addition, file or directory names can also be included, as in:
dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam
+ -o outfile (or --output outfile)
+ When using Dialyzer from the command line, send the analysis
+ results to the specified outfile rather than to stdout.
--raw
When using Dialyzer from the command line, output the raw analysis
results (Erlang terms) instead of the formatted result.
The raw format is easier to post-process (for instance, to filter
- warnings or to output HTML pages)
+ warnings or to output HTML pages).
--src
- Override the default, which is to analyze BEAM bytecode, and
- analyze starting from Erlang source code instead
+ Override the default, which is to analyze BEAM files, and
+ analyze starting from Erlang source code instead.
-Dname (or -Dname=value)
- When analyzing from source, pass the define to Dialyzer (**)
+ When analyzing from source, pass the define to Dialyzer. (**)
-I include_dir
- When analyzing from source, pass the include_dir to Dialyzer (**)
+ When analyzing from source, pass the include_dir to Dialyzer. (**)
-pa dir
Include dir in the path for Erlang (useful when analyzing files
- that have '-include_lib()' directives)
+ that have '-include_lib()' directives).
--output_plt file
- Store the plt at the specified file after building it
+ Store the plt at the specified file after building it.
--plt plt
Use the specified plt as the initial plt (if the plt was built
- during setup the files will be checked for consistency)
+ during setup the files will be checked for consistency).
+ --plts plt*
+ Merge the specified plts to create the initial plt -- requires
+ that the plts are disjoint (i.e., do not have any module
+ appearing in more than one plt).
+ The plts are created in the usual way:
+ dialyzer --build_plt --output_plt plt_1 files_to_include
+ ...
+ dialyzer --build_plt --output_plt plt_n files_to_include
+ and then can be used in either of the following ways:
+ dialyzer files_to_analyze --plts plt_1 ... plt_n
+ or:
+ dialyzer --plts plt_1 ... plt_n -- files_to_analyze
+ (Note the -- delimiter in the second case)
-Wwarn
A family of options which selectively turn on/off warnings
- (for help on the names of warnings use dialyzer -Whelp)
+ (for help on the names of warnings use dialyzer -Whelp).
--shell
- Do not disable the Erlang shell while running the GUI
+ Do not disable the Erlang shell while running the GUI.
--version (or -v)
- Prints the Dialyzer version and some more information and exits
+ Print the Dialyzer version and some more information and exit.
--help (or -h)
- Prints this message and exits
+ Print this message and exit.
--quiet (or -q)
- Makes Dialyzer a bit more quiet
+ Make Dialyzer a bit more quiet.
--verbose
- Makes Dialyzer a bit more verbose
+ Make Dialyzer a bit more verbose.
--build_plt
The analysis starts from an empty plt and creates a new one from the
files specified with -c and -r. Only works for beam files.
Use --plt or --output_plt to override the default plt location.
--add_to_plt
The plt is extended to also include the files specified with -c and -r.
- Use --plt to specify wich plt to start from, and --output_plt to
+ Use --plt to specify which plt to start from, and --output_plt to
specify where to put the plt. Note that the analysis might include
files from the plt if they depend on the new files.
This option only works with beam files.
@@ -195,25 +212,27 @@ Options:
from the plt. Note that this may cause a re-analysis of the remaining
dependent files.
--check_plt
- Checks the plt for consistency and rebuilds it if it is not up-to-date.
+ Check the plt for consistency and rebuild it if it is not up-to-date.
--no_check_plt
Skip the plt check when running Dialyzer. Useful when working with
installed plts that never change.
--plt_info
- Makes Dialyzer print information about the plt and then quit. The plt
- can be specified with --plt.
+ Make Dialyzer print information about the plt and then quit. The plt
+ can be specified with --plt(s).
--get_warnings
- Makes Dialyzer emit warnings even when manipulating the plt. Only
- emits warnings for files that are actually analyzed.
+ Make Dialyzer emit warnings even when manipulating the plt. Warnings
+ are only emitted for files that are actually analyzed.
--dump_callgraph file
Dump the call graph into the specified file whose format is determined
by the file name extension. Supported extensions are: raw, dot, and ps.
If something else is used as file name extension, default format '.raw'
will be used.
--no_native (or -nn)
- Bypass the native code compilation of some key files that dialyzer
+ Bypass the native code compilation of some key files that Dialyzer
heuristically performs when dialyzing many files; this avoids the
compilation time but it may result in (much) longer analysis time.
+ --fullpath
+ Display the full path names of files for which warnings are emitted.
--gui
Use the gs-based GUI.
--wx
@@ -231,12 +250,17 @@ Warning options:
Suppress warnings for unused functions.
-Wno_improper_lists
Suppress warnings for construction of improper lists.
+ -Wno_tuple_as_fun
+ Suppress warnings for using tuples instead of funs.
-Wno_fun_app
Suppress warnings for fun applications that will fail.
-Wno_match
Suppress warnings for patterns that are unused or cannot match.
+ -Wno_opaque
+ Suppress warnings for violations of opaqueness of data types.
-Wunmatched_returns ***
- Include warnings for function calls which ignore the return value(s).
+ Include warnings for function calls which ignore a structured return
+ value or do not match against one of many possible return value(s).
-Werror_handling ***
Include warnings for functions that only return by means of an exception.
-Wrace_conditions ***
@@ -257,7 +281,7 @@ The following options are also available but their use is not recommended:
Warn when the -spec is different than the success typing.
Note:
- *** These are options that turn on warnings rather than turning them off.
+ *** Identifies options that turn on warnings rather than turning them off.
-----------------------------------------------
@@ -294,6 +318,7 @@ Option :: {files, [Filename :: string()]}
| {defines, [{Macro :: atom(), Value :: term()}]}
| {from, src_code | byte_code} %% Defaults to byte_code
| {init_plt, FileName :: string()} %% If changed from default
+ | {plts, [FileName :: string()]} %% If changed from default
| {include_dirs, [DirName :: string()]}
| {output_file, FileName :: string()}
| {output_plt, FileName :: string()}
diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml
index 1ec2ce830a..b6547b11e1 100644
--- a/lib/dialyzer/doc/src/dialyzer.xml
+++ b/lib/dialyzer/doc/src/dialyzer.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2006</year><year>2010</year>
+ <year>2006</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -64,81 +64,146 @@
]]></code>
<p>Usage:</p>
<code type="none"><![CDATA[
- dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
- [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]*
- [--output_plt file] [-Wwarn]* [--src]
- [-c applications] [-r applications] [-o outfile]
+ dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
+ [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]*
+ [-I include_dir]* [--output_plt file] [-Wwarn]*
+ [--src] [--gui | --wx] [files_or_dirs] [-r dirs]
+ [--apps applications] [-o outfile]
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
+ [--no_native] [--fullpath]
]]></code>
<p>Options:</p>
<taglist>
- <tag><c><![CDATA[-c applications]]></c>(or <c><![CDATA[--command-line applications]]></c>)</tag>
- <item>use Dialyzer from the command line (no GUI) to detect defects in the
- specified applications (directories or <c><![CDATA[.erl]]></c> or <c><![CDATA[.beam]]></c> files)</item>
- <tag><c><![CDATA[-r applications]]></c></tag>
- <item>same as <c><![CDATA[-c]]></c> only that directories are searched recursively for
- subdirectories containing <c><![CDATA[.erl]]></c> or <c><![CDATA[.beam]]></c> files (depending on the
- type of analysis)</item>
- <tag><c><![CDATA[-o outfile]]></c>(or <c><![CDATA[--output outfile]]></c>)</tag>
- <item>when using Dialyzer from the command line, send the analysis
- results in the specified <c><![CDATA[outfile]]></c> rather than in stdout</item>
- <tag><c><![CDATA[--src]]></c></tag>
- <item>override the default, which is to analyze debug compiled BEAM
- bytecode, and analyze starting from Erlang source code instead</item>
+ <tag><c><![CDATA[files_or_dirs]]></c> (for backwards compatibility also
+ as: <c><![CDATA[-c files_or_dirs]]></c></tag>
+ <item>Use Dialyzer from the command line to detect defects in the
+ specified files or directories containing <c><![CDATA[.erl]]></c> or
+ <c><![CDATA[.beam]]></c> files, depending on the type of the
+ analysis.</item>
+ <tag><c><![CDATA[-r dirs]]></c></tag>
+ <item>Same as the previous but the specified directories are searched
+ recursively for subdirectories containing <c><![CDATA[.erl]]></c> or
+ <c><![CDATA[.beam]]></c> files in them, depending on the type of
+ analysis.</item>
+ <tag><c><![CDATA[--apps applications]]></c></tag>
+ <item>Option typically used when building or modifying a plt as in:
+ <code type="none"><![CDATA[
+ dialyzer --build_plt --apps erts kernel stdlib mnesia ...
+ ]]></code>
+ to conveniently refer to library applications corresponding to the
+ Erlang/OTP installation. However, the option is general and can also
+ be used during analysis in order to refer to Erlang/OTP applications.
+ In addition, file or directory names can also be included, as in:
+ <code type="none"><![CDATA[
+ dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam
+ ]]></code></item>
+ <tag><c><![CDATA[-o outfile]]></c> (or
+ <c><![CDATA[--output outfile]]></c>)</tag>
+ <item>When using Dialyzer from the command line, send the analysis
+ results to the specified outfile rather than to stdout.</item>
<tag><c><![CDATA[--raw]]></c></tag>
<item>When using Dialyzer from the command line, output the raw analysis
- results (Erlang terms) instead of the formatted result.
- The raw format is easier to post-process (for instance, to filter
- warnings or to output HTML pages).</item>
- <tag><c><![CDATA[-Dname]]></c>(or <c><![CDATA[-Dname=value]]></c>)</tag>
- <item>when analyzing from source, pass the define to Dialyzer (**)</item>
+ results (Erlang terms) instead of the formatted result. The raw format
+ is easier to post-process (for instance, to filter warnings or to
+ output HTML pages).</item>
+ <tag><c><![CDATA[--src]]></c></tag>
+ <item>Override the default, which is to analyze BEAM files, and
+ analyze starting from Erlang source code instead.</item>
+ <tag><c><![CDATA[-Dname]]></c> (or <c><![CDATA[-Dname=value]]></c>)</tag>
+ <item>When analyzing from source, pass the define to Dialyzer. (**)</item>
<tag><c><![CDATA[-I include_dir]]></c></tag>
- <item>when analyzing from source, pass the <c><![CDATA[include_dir]]></c> to Dialyzer (**)</item>
+ <item>When analyzing from source, pass the <c><![CDATA[include_dir]]></c>
+ to Dialyzer. (**)</item>
<tag><c><![CDATA[-pa dir]]></c></tag>
- <item>Include <c><![CDATA[dir]]></c> in the path for Erlang. Useful when analyzing files
- that have <c><![CDATA[-include_lib()]]></c> directives.</item>
+ <item>Include <c><![CDATA[dir]]></c> in the path for Erlang (useful when
+ analyzing files that have <c><![CDATA['-include_lib()']]></c>
+ directives).</item>
<tag><c><![CDATA[--output_plt file]]></c></tag>
- <item>Store the PLT at the specified location after building it.</item>
+ <item>Store the plt at the specified file after building it.</item>
<tag><c><![CDATA[--plt plt]]></c></tag>
- <item>Use the specified plt as the initial persistent lookup table.</item>
+ <item>Use the specified plt as the initial plt (if the plt was built
+ during setup the files will be checked for consistency).</item>
+ <tag><c><![CDATA[--plts plt*]]></c></tag>
+ <item>Merge the specified plts to create the initial plt -- requires
+ that the plts are disjoint (i.e., do not have any module
+ appearing in more than one plt).
+ The plts are created in the usual way:
+ <code type="none"><![CDATA[
+ dialyzer --build_plt --output_plt plt_1 files_to_include
+ ...
+ dialyzer --build_plt --output_plt plt_n files_to_include
+ ]]></code>
+ and then can be used in either of the following ways:
+ <code type="none"><![CDATA[
+ dialyzer files_to_analyze --plts plt_1 ... plt_n
+ ]]></code>
+ or:
+ <code type="none"><![CDATA[
+ dialyzer --plts plt_1 ... plt_n -- files_to_analyze
+ ]]></code>
+ (Note the -- delimiter in the second case)</item>
<tag><c><![CDATA[-Wwarn]]></c></tag>
- <item>a family of option which selectively turn on/off warnings.
- (for help on the names of warnings use <c><![CDATA[dialyzer -Whelp]]></c>)</item>
+ <item>A family of options which selectively turn on/off warnings
+ (for help on the names of warnings use
+ <c><![CDATA[dialyzer -Whelp]]></c>).</item>
<tag><c><![CDATA[--shell]]></c></tag>
- <item>do not disable the Erlang shell while running the GUI</item>
- <tag><c><![CDATA[--version (or -v)]]></c></tag>
- <item>prints the Dialyzer version and some more information and exits</item>
- <tag><c><![CDATA[--help (or -h)]]></c></tag>
- <item>prints this message and exits</item>
- <tag><c><![CDATA[--quiet (or -q)]]></c></tag>
- <item>makes Dialyzer a bit more quiet</item>
+ <item>Do not disable the Erlang shell while running the GUI.</item>
+ <tag><c><![CDATA[--version]]></c> (or <c><![CDATA[-v]]></c>)</tag>
+ <item>Print the Dialyzer version and some more information and
+ exit.</item>
+ <tag><c><![CDATA[--help]]></c> (or <c><![CDATA[-h]]></c>)</tag>
+ <item>Print this message and exit.</item>
+ <tag><c><![CDATA[--quiet]]></c> (or <c><![CDATA[-q]]></c>)</tag>
+ <item>Make Dialyzer a bit more quiet.</item>
<tag><c><![CDATA[--verbose]]></c></tag>
- <item>makes Dialyzer a bit more verbose</item>
- <tag><c><![CDATA[--check_plt]]></c></tag>
- <item>Only checks if the initial PLT is up to date and rebuilds it if this is not the case</item>
- <tag><c><![CDATA[--no_check_plt (or -n)]]></c></tag>
- <item>Skip the PLT integrity check when running Dialyzer.
- Useful when working with installed PLTs that never change.</item>
+ <item>Make Dialyzer a bit more verbose.</item>
<tag><c><![CDATA[--build_plt]]></c></tag>
- <item>The analysis starts from an empty PLT and creates a new one from
- the files specified with -c and -r. Only works for beam files.
- Use --plt or --output_plt to override the default PLT location.</item>
- <tag><c><![CDATA[--add_to_plt]]></c></tag>
- <item> The PLT is extended to also include the files specified with
- -c and -r. Use --plt to specify which PLT to start from, and --output_plt
- to specify where to put the PLT. Note that the analysis might include
- files from the PLT if they depend on the new files.
- This option only works with beam files.</item>
+ <item>The analysis starts from an empty plt and creates a new one from
+ the files specified with <c><![CDATA[-c]]></c> and
+ <c><![CDATA[-r]]></c>. Only works for beam files. Use
+ <c><![CDATA[--plt]]></c> or <c><![CDATA[--output_plt]]></c> to
+ override the default plt location.</item>
+ <tag><c><![CDATA[--add_to_plt]]></c></tag>
+ <item>The plt is extended to also include the files specified with
+ <c><![CDATA[-c]]></c> and <c><![CDATA[-r]]></c>. Use
+ <c><![CDATA[--plt]]></c> to specify which plt to start from,
+ and <c><![CDATA[--output_plt]]></c> to specify where to put the plt.
+ Note that the analysis might include files from the plt if they depend
+ on the new files. This option only works with beam files.</item>
<tag><c><![CDATA[--remove_from_plt]]></c></tag>
- <item>The information from the files specified with -c and -r is removed
- from the PLT. Note that this may cause a re-analysis of the remaining
- dependent files.</item>
+ <item>The information from the files specified with
+ <c><![CDATA[-c]]></c> and <c><![CDATA[-r]]></c> is removed
+ from the plt. Note that this may cause a re-analysis of the remaining
+ dependent files.</item>
+ <tag><c><![CDATA[--check_plt]]></c></tag>
+ <item>Check the plt for consistency and rebuild it if it is not
+ up-to-date.</item>
+ <tag><c><![CDATA[--no_check_plt]]></c></tag>
+ <item>Skip the plt check when running Dialyzer. Useful when working with
+ installed plts that never change.</item>
+ <tag><c><![CDATA[--plt_info]]></c></tag>
+ <item>Make Dialyzer print information about the plt and then quit. The
+ plt can be specified with <c><![CDATA[--plt(s)]]></c>.</item>
<tag><c><![CDATA[--get_warnings]]></c></tag>
- <item>Makes Dialyzer emit warnings even when manipulating the PLT. Only
- emits warnings for files that are actually analyzed. The default is to
- not emit any warnings when manipulating the PLT. This option has no
- effect when performing a normal analysis.</item>
+ <item>Make Dialyzer emit warnings even when manipulating the plt.
+ Warnings are only emitted for files that are actually analyzed.</item>
+ <tag><c><![CDATA[--dump_callgraph file]]></c></tag>
+ <item>Dump the call graph into the specified file whose format is
+ determined by the file name extension. Supported extensions are: raw,
+ dot, and ps. If something else is used as file name extension, default
+ format '.raw' will be used.</item>
+ <tag><c><![CDATA[--no_native]]></c> (or <c><![CDATA[-nn]]></c>)</tag>
+ <item>Bypass the native code compilation of some key files that Dialyzer
+ heuristically performs when dialyzing many files; this avoids the
+ compilation time but it may result in (much) longer analysis
+ time.</item>
+ <tag><c><![CDATA[--fullpath]]></c></tag>
+ <item>Display the full path names of files for which warnings are emitted.</item>
+ <tag><c><![CDATA[--gui]]></c></tag>
+ <item>Use the gs-based GUI.</item>
+ <tag><c><![CDATA[--wx]]></c></tag>
+ <item>Use the wx-based GUI.</item>
</taglist>
<note>
<p>* denotes that multiple occurrences of these options are possible.</p>
@@ -148,33 +213,49 @@
<p>Warning options:</p>
<taglist>
<tag><c><![CDATA[-Wno_return]]></c></tag>
- <item>Suppress warnings for functions of no return.</item>
+ <item>Suppress warnings for functions that will never return a
+ value.</item>
<tag><c><![CDATA[-Wno_unused]]></c></tag>
<item>Suppress warnings for unused functions.</item>
<tag><c><![CDATA[-Wno_improper_lists]]></c></tag>
<item>Suppress warnings for construction of improper lists.</item>
+ <tag><c><![CDATA[-Wno_tuple_as_fun]]></c></tag>
+ <item>Suppress warnings for using tuples instead of funs.</item>
<tag><c><![CDATA[-Wno_fun_app]]></c></tag>
<item>Suppress warnings for fun applications that will fail.</item>
<tag><c><![CDATA[-Wno_match]]></c></tag>
<item>Suppress warnings for patterns that are unused or cannot
match.</item>
+ <tag><c><![CDATA[-Wno_opaque]]></c></tag>
+ <item>Suppress warnings for violations of opaqueness of data types.</item>
+ <tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
+ <item>Include warnings for function calls which ignore a structured return
+ value or do not match against one of many possible return
+ value(s).</item>
<tag><c><![CDATA[-Werror_handling]]></c>***</tag>
<item>Include warnings for functions that only return by means of an
exception.</item>
- <tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag>
- <item>Include warnings for function calls which ignore a structured return
- value or do not match against one of many possible return value(s).</item>
+ <tag><c><![CDATA[-Wrace_conditions]]></c>***</tag>
+ <item>Include warnings for possible race conditions.</item>
+ <tag><c><![CDATA[-Wbehaviours]]></c>***</tag>
+ <item>Include warnings about behaviour callbacks which drift from the
+ published recommended interfaces.</item>
<tag><c><![CDATA[-Wunderspecs]]></c>***</tag>
<item>Warn about underspecified functions
- (the -spec is strictly more allowing than the success typing)</item>
+ (the -spec is strictly more allowing than the success typing).</item>
+ </taglist>
+ <p>The following options are also available but their use is not
+ recommended: (they are mostly for Dialyzer developers and internal
+ debugging)</p>
+ <taglist>
<tag><c><![CDATA[-Woverspecs]]></c>***</tag>
<item>Warn about overspecified functions
- (the -spec is strictly less allowing than the success typing)</item>
+ (the -spec is strictly less allowing than the success typing).</item>
<tag><c><![CDATA[-Wspecdiffs]]></c>***</tag>
- <item>Warn when the -spec is different than the success typing</item>
+ <item>Warn when the -spec is different than the success typing.</item>
</taglist>
<note>
- <p>*** These are options that turn on warnings rather than
+ <p>*** Identifies options that turn on warnings rather than
turning them off.</p>
</note>
</section>
@@ -203,6 +284,7 @@ Option : {files, [Filename : string()]}
| {defines, [{Macro: atom(), Value : term()}]}
| {from, src_code | byte_code} %% Defaults to byte_code
| {init_plt, FileName : string()} %% If changed from default
+ | {plts, [FileName :: string()]} %% If changed from default
| {include_dirs, [DirName : string()]}
| {output_file, FileName : string()}
| {output_plt, FileName :: string()}
@@ -215,8 +297,11 @@ WarnOpts : no_return
| no_improper_lists
| no_fun_app
| no_match
+ | no_opaque
| no_fail_call
| error_handling
+ | race_conditions
+ | behaviours
| unmatched_returns
| overspecs
| underspecs
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index b6106b928a..f132a50e0d 100755
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -31,6 +31,176 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 2.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add a --fullpath option to Dialyzer</p>
+ <p>
+ This change adds a --fullpath option to Dialyzer, which
+ makes the warning messages contain the full path of the
+ corresponding file.</p>
+ <p>
+ Original patch submitted by Magnus Henoch (legoscia) on
+ 15/9/2010 and cooked to death in the 'pu' branch all this
+ time.</p>
+ <p>
+ The patch was essentially correct and most of it has been
+ used as is, but there have been some changes to make the
+ code slightly prettier, avoid some code duplication, and
+ add documentation to dialyzer's doc files and to its help
+ message.</p>
+ <p>
+ Own Id: OTP-9098</p>
+ </item>
+ <item>
+ <p>
+ Fix warnings about guards containing not</p>
+ <p>
+ The wording of warnings about unsatisfiable guards that
+ used 'not' was incorrect (the 'not' was not mentioned and
+ it appeared as "Guard test is_atom(atom()) can never
+ succeed") (thanks to Stavros Aronis).</p>
+ <p>
+ Own Id: OTP-9099</p>
+ </item>
+ <item>
+ <p>
+ Version 2.4.2 (in Erlang/OTP R14B02)
+ ------------------------------------ - Added --fullpath
+ option to display files with warnings with their full
+ file names (thanks to Magnus Henoch for the original
+ patch). - Better handling of 'and'/'or'/'not' guards that
+ generate warnings (thanks to Stavros Aronis). - Better
+ blame assignment for cases when a function's spec is
+ erroneous (thanks to Stavros Aronis). - More descriptive
+ warnings when a tuple/record pattern contains subterms
+ that violate the declared types of record fields (thanks
+ to Matthias Lang for the test case and for Stavros Aronis
+ for the actual fix).</p>
+ <p>
+ Own Id: OTP-9126</p>
+ </item>
+ <item>
+ <p>
+ Add spec to dialyzer_cl_parse:get_lib_dir/1</p>
+ <p>
+ Own Id: OTP-9129</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Test suites for Dialyzer</p>
+ <p>
+ This is a transcription of most of the
+ cvs.srv.it.uu.se:/hipe repository dialyzer_tests into
+ test suites that use the test server framework.</p>
+ <p>
+ See README for information on how to use the included
+ scripts for modifications and updates.</p>
+ <p>
+ When testing Dialyzer it's important that several OTP
+ modules are included in the plt. The suites takes care of
+ that too.</p>
+ <p>
+ Own Id: OTP-9116</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 2.4.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> - Fixed pretty rare infinite loop when refining the
+ types of an SCC whose functions all returned none()
+ (thanks to Stavros Aronis). </p><p> - Fixed pretty rare
+ crash when taking the infimum of two tuple_sets. </p>
+ <p>
+ Own Id: OTP-8979</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> - Added ability to supply multiple PLTs for the
+ analysis (option --plts). Currently these PLTs must be
+ independent (i.e., no module appears in more than one
+ PLT) and there must not include files with module name
+ clashes.</p><p> - Strengthened and streamlined hard-coded
+ type information for some BIFs and key library
+ functions.</p>
+ <p>
+ Own Id: OTP-8962</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 2.3.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Eliminated warnings for auto-imported BIF clashes.</p>
+ <p>
+ Own Id: OTP-8840</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Dialyzer 2.3.0</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Various changes to dialyzer-related files for R14.</p>
+ <p>
+ - Dialyzer properly supports the new attribute
+ -export_type and checks that remote types only refer to
+ exported types. A warning is produced if some
+ files/applications refer to types defined in modules
+ which are neither in the PLT nor in the analyzed
+ applications.</p>
+ <p>
+ - Support for detecting data races involving whereis/1
+ and unregister/1.</p>
+ <p>
+ - More precise identification of the reason(s) why a
+ record construction violates the types declared for its
+ fields.</p>
+ <p>
+ - Fixed bug in the handling of the 'or' guard.</p>
+ <p>
+ - Better handling of the erlang:element/2 BIF.</p>
+ <p>
+ - Complete handling of Erlang BIFs.</p>
+ <p>
+ Own Id: OTP-8699</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 2.2.0</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 3b7b68e8c4..5014a4244c 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,12 +33,13 @@
%% NOTE: Only functions exported by this module are available to
%% other applications.
%%--------------------------------------------------------------------
--export([plain_cl/0,
- run/1,
+-export([plain_cl/0,
+ run/1,
gui/0,
gui/1,
plt_info/1,
- format_warning/1]).
+ format_warning/1,
+ format_warning/2]).
-include("dialyzer.hrl").
@@ -48,6 +49,8 @@
%% - run/1: Erlang interface for a command line-like analysis
%% - gui/0/1: Erlang interface for the gui.
%% - format_warning/1: Get the string representation of a warning.
+%% - format_warning/1: Likewise, but with an option whether
+%% to display full path names or not
%% - plt_info/1: Get information of the specified plt.
%%--------------------------------------------------------------------
@@ -55,7 +58,7 @@
plain_cl() ->
case dialyzer_cl_parse:start() of
- {check_init, Opts} ->
+ {check_init, Opts} ->
cl_halt(cl_check_init(Opts), Opts);
{plt_info, Opts} ->
cl_halt(cl_print_plt_info(Opts), Opts);
@@ -72,7 +75,7 @@ plain_cl() ->
false ->
gui_halt(internal_gui(Type, Opts), Opts)
end;
- {cl, Opts} ->
+ {cl, Opts} ->
case Opts#options.check_plt of
true ->
case cl_check_init(Opts#options{get_warnings = false}) of
@@ -82,7 +85,7 @@ plain_cl() ->
false ->
cl_halt(cl(Opts), Opts)
end;
- {error, Msg} ->
+ {error, Msg} ->
cl_error(Msg)
end.
@@ -106,27 +109,35 @@ cl_print_plt_info(Opts) ->
end,
doit(F).
-print_plt_info(#options{init_plt = PLT, output_file = OutputFile}) ->
+print_plt_info(#options{init_plts = PLTs, output_file = OutputFile}) ->
+ PLTInfo = get_plt_info(PLTs),
+ do_print_plt_info(PLTInfo, OutputFile).
+
+get_plt_info([PLT|PLTs]) ->
String =
case dialyzer_plt:included_files(PLT) of
{ok, Files} ->
- io_lib:format("The PLT ~s includes the following files:\n~p\n",
+ io_lib:format("The PLT ~s includes the following files:\n~p\n\n",
[PLT, Files]);
{error, read_error} ->
- Msg = io_lib:format("Could not read the PLT file ~p\n", [PLT]),
+ Msg = io_lib:format("Could not read the PLT file ~p\n\n", [PLT]),
throw({dialyzer_error, Msg});
{error, no_such_file} ->
- Msg = io_lib:format("The PLT file ~p does not exist\n", [PLT]),
+ Msg = io_lib:format("The PLT file ~p does not exist\n\n", [PLT]),
throw({dialyzer_error, Msg})
end,
+ String ++ get_plt_info(PLTs);
+get_plt_info([]) -> "".
+
+do_print_plt_info(PLTInfo, OutputFile) ->
case OutputFile =:= none of
true ->
- io:format("~s", [String]),
+ io:format("~s", [PLTInfo]),
?RET_NOTHING_SUSPICIOUS;
false ->
case file:open(OutputFile, [write]) of
{ok, FileDesc} ->
- io:format(FileDesc, "~s", [String]),
+ io:format(FileDesc, "~s", [PLTInfo]),
ok = file:close(FileDesc),
?RET_NOTHING_SUSPICIOUS;
{error, Reason} ->
@@ -146,7 +157,7 @@ cl(Opts) ->
-spec run(dial_options()) -> [dial_warning()].
run(Opts) ->
- try dialyzer_options:build([{report_mode, quiet},
+ try dialyzer_options:build([{report_mode, quiet},
{erlang_mode, true}|Opts]) of
{error, Msg} ->
throw({dialyzer_error, Msg});
@@ -161,7 +172,7 @@ run(Opts) ->
throw({dialyzer_error, ErrorMsg1})
end
catch
- throw:{dialyzer_error, ErrorMsg} ->
+ throw:{dialyzer_error, ErrorMsg} ->
erlang:error({dialyzer_error, lists:flatten(ErrorMsg)})
end.
@@ -225,25 +236,31 @@ plt_info(Plt) ->
%% Machinery
%%-----------
+-type doit_ret() :: {'ok', dial_ret()} | {'error', string()}.
+
doit(F) ->
- try
+ try
{ok, F()}
catch
throw:{dialyzer_error, Msg} ->
{error, lists:flatten(Msg)}
end.
+-spec cl_error(string()) -> no_return().
+
cl_error(Msg) ->
cl_halt({error, Msg}, #options{}).
+-spec gui_halt(doit_ret(), #options{}) -> no_return().
+
gui_halt(R, Opts) ->
cl_halt(R, Opts#options{report_mode = quiet}).
--spec cl_halt({'ok',dial_ret()} | {'error',string()}, #options{}) -> no_return().
+-spec cl_halt(doit_ret(), #options{}) -> no_return().
-cl_halt({ok, R = ?RET_NOTHING_SUSPICIOUS}, #options{report_mode = quiet}) ->
+cl_halt({ok, R = ?RET_NOTHING_SUSPICIOUS}, #options{report_mode = quiet}) ->
halt(R);
-cl_halt({ok, R = ?RET_DISCREPANCIES}, #options{report_mode = quiet}) ->
+cl_halt({ok, R = ?RET_DISCREPANCIES}, #options{report_mode = quiet}) ->
halt(R);
cl_halt({ok, R = ?RET_NOTHING_SUSPICIOUS}, #options{}) ->
io:put_chars("done (passed successfully)\n"),
@@ -267,11 +284,19 @@ cl_check_log(Output) ->
-spec format_warning(dial_warning()) -> string().
-format_warning({_Tag, {File, Line}, Msg}) when is_list(File),
- is_integer(Line) ->
- BaseName = filename:basename(File),
+format_warning(W) ->
+ format_warning(W, basename).
+
+-spec format_warning(dial_warning(), fopt()) -> string().
+
+format_warning({_Tag, {File, Line}, Msg}, FOpt) when is_list(File),
+ is_integer(Line) ->
+ F = case FOpt of
+ fullpath -> File;
+ basename -> filename:basename(File)
+ end,
String = lists:flatten(message_to_string(Msg)),
- lists:flatten(io_lib:format("~s:~w: ~s", [BaseName, Line, String])).
+ lists:flatten(io_lib:format("~s:~w: ~s", [F, Line, String])).
%%-----------------------------------------------------------------------------
@@ -290,7 +315,7 @@ message_to_string({app_call, [M, F, Args, Culprit, ExpectedType, FoundType]}) ->
message_to_string({bin_construction, [Culprit, Size, Seg, Type]}) ->
io_lib:format("Binary construction will fail since the ~s field ~s in"
" segment ~s has type ~s\n", [Culprit, Size, Seg, Type]);
-message_to_string({call, [M, F, Args, ArgNs, FailReason,
+message_to_string({call, [M, F, Args, ArgNs, FailReason,
SigArgs, SigRet, Contract]}) ->
io_lib:format("The call ~w:~w~s ", [M, F, Args]) ++
call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract);
@@ -309,8 +334,13 @@ message_to_string({guard_fail, []}) ->
"Clause guard cannot succeed.\n";
message_to_string({guard_fail, [Arg1, Infix, Arg2]}) ->
io_lib:format("Guard test ~s ~s ~s can never succeed\n", [Arg1, Infix, Arg2]);
+message_to_string({neg_guard_fail, [Arg1, Infix, Arg2]}) ->
+ io_lib:format("Guard test not(~s ~s ~s) can never succeed\n",
+ [Arg1, Infix, Arg2]);
message_to_string({guard_fail, [Guard, Args]}) ->
io_lib:format("Guard test ~w~s can never succeed\n", [Guard, Args]);
+message_to_string({neg_guard_fail, [Guard, Args]}) ->
+ io_lib:format("Guard test not(~w~s) can never succeed\n", [Guard, Args]);
message_to_string({guard_fail_pat, [Pat, Type]}) ->
io_lib:format("Clause guard cannot succeed. The ~s was matched"
" against the type ~s\n", [Pat, Type]);
@@ -329,15 +359,18 @@ message_to_string({no_return, [Type|Name]}) ->
only_normal -> NameString ++ "has no local return\n";
both -> NameString ++ "has no local return\n"
end;
-message_to_string({record_constr, [Types, Name]}) ->
+message_to_string({record_constr, [RecConstr, FieldDiffs]}) ->
io_lib:format("Record construction ~s violates the"
- " declared type for #~w{}\n", [Types, Name]);
+ " declared type of field ~s\n", [RecConstr, FieldDiffs]);
message_to_string({record_constr, [Name, Field, Type]}) ->
io_lib:format("Record construction violates the declared type for #~w{}"
" since ~s cannot be of type ~s\n", [Name, Field, Type]);
message_to_string({record_matching, [String, Name]}) ->
io_lib:format("The ~s violates the"
" declared type for #~w{}\n", [String, Name]);
+message_to_string({record_match, [Pat, Type]}) ->
+ io_lib:format("Matching of ~s tagged with a record name violates the declared"
+ " type of ~s\n", [Pat, Type]);
message_to_string({pattern_match, [Pat, Type]}) ->
io_lib:format("The ~s can never match the type ~s\n", [Pat, Type]);
message_to_string({pattern_match_cov, [Pat, Type]}) ->
@@ -358,12 +391,16 @@ message_to_string({contract_diff, [M, F, _A, Contract, Sig]}) ->
[M, F, Contract, M, F, Sig]);
message_to_string({contract_subtype, [M, F, _A, Contract, Sig]}) ->
io_lib:format("Type specification ~w:~w~s"
- " is a subtype of the success typing: ~w:~w~s\n",
+ " is a subtype of the success typing: ~w:~w~s\n",
[M, F, Contract, M, F, Sig]);
message_to_string({contract_supertype, [M, F, _A, Contract, Sig]}) ->
io_lib:format("Type specification ~w:~w~s"
" is a supertype of the success typing: ~w:~w~s\n",
[M, F, Contract, M, F, Sig]);
+message_to_string({contract_range, [Contract, M, F, ArgStrings, Line, CRet]}) ->
+ io_lib:format("The contract ~w:~w~s cannot be right because the inferred"
+ " return for ~w~s on line ~w is ~s\n",
+ [M, F, Contract, F, ArgStrings, Line, CRet]);
message_to_string({invalid_contract, [M, F, A, Sig]}) ->
io_lib:format("Invalid type specification for function ~w:~w/~w."
" The success typing is ~s\n", [M, F, A, Sig]);
@@ -379,7 +416,7 @@ message_to_string({spec_missing_fun, [M, F, A]}) ->
[M, F, A]);
%%----- Warnings for opaque type violations -------------------
message_to_string({call_with_opaque, [M, F, Args, ArgNs, ExpArgs]}) ->
- io_lib:format("The call ~w:~w~s contains ~s argument when ~s\n",
+ io_lib:format("The call ~w:~w~s contains ~s when ~s\n",
[M, F, Args, form_positions(ArgNs), form_expected(ExpArgs)]);
message_to_string({call_without_opaque, [M, F, Args, ExpectedTriples]}) ->
io_lib:format("The call ~w:~w~s does not have ~s\n",
@@ -427,7 +464,7 @@ message_to_string({spec_missing, [B, F, A]}) ->
%% Auxiliary functions below
%%-----------------------------------------------------------------------------
-call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
+call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
{IsOverloaded, Contract}) ->
PositionString = form_position_string(ArgNs),
case FailReason of
@@ -442,7 +479,7 @@ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
" from the success typing arguments: ~s\n",
[PositionString, SigArgs])
end;
- only_contract ->
+ only_contract ->
case (ArgNs =:= []) orelse IsOverloaded of
true ->
%% We do not know which arguments caused the failure
@@ -494,7 +531,7 @@ form_position_string(ArgNs) ->
case ArgNs of
[] -> "";
[N1] -> ordinal(N1);
- [_,_|_] ->
+ [_,_|_] ->
[Last|Prevs] = lists:reverse(ArgNs),
", " ++ Head = lists:flatten([io_lib:format(", ~s",[ordinal(N)]) ||
N <- lists:reverse(Prevs)]),
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index 2da8ed2e5d..9d2e554981 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -2,7 +2,7 @@
%%%
%%% %CopyrightBegin%
%%%
-%%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@
-define(RET_DISCREPANCIES, 2).
-type dial_ret() :: ?RET_NOTHING_SUSPICIOUS
- | ?RET_INTERNAL_ERROR
+ | ?RET_INTERNAL_ERROR
| ?RET_DISCREPANCIES.
%%--------------------------------------------------------------------
@@ -52,10 +52,11 @@
-define(WARN_CONTRACT_NOT_EQUAL, warn_contract_not_equal).
-define(WARN_CONTRACT_SUBTYPE, warn_contract_subtype).
-define(WARN_CONTRACT_SUPERTYPE, warn_contract_supertype).
+-define(WARN_CONTRACT_RANGE, warn_contract_range).
-define(WARN_CALLGRAPH, warn_callgraph).
-define(WARN_UNMATCHED_RETURN, warn_umatched_return).
-define(WARN_RACE_CONDITION, warn_race_condition).
--define(WARN_BEHAVIOUR,warn_behaviour).
+-define(WARN_BEHAVIOUR, warn_behaviour).
%%
%% The following type has double role:
@@ -70,7 +71,7 @@
| ?WARN_CONTRACT_NOT_EQUAL | ?WARN_CONTRACT_SUBTYPE
| ?WARN_CONTRACT_SUPERTYPE | ?WARN_CALLGRAPH
| ?WARN_UNMATCHED_RETURN | ?WARN_RACE_CONDITION
- | ?WARN_BEHAVIOUR.
+ | ?WARN_BEHAVIOUR | ?WARN_CONTRACT_RANGE.
%%
%% This is the representation of each warning as they will be returned
@@ -87,7 +88,7 @@
%%--------------------------------------------------------------------
%% THIS TYPE SHOULD ONE DAY DISAPPEAR -- IT DOES NOT BELONG HERE
%%--------------------------------------------------------------------
-
+
-type ordset(T) :: [T] . %% XXX: temporarily
%%--------------------------------------------------------------------
@@ -102,6 +103,8 @@
-type dial_define() :: {atom(), term()}.
-type dial_option() :: {atom(), term()}.
-type dial_options() :: [dial_option()].
+-type fopt() :: 'basename' | 'fullpath'.
+-type format() :: 'formatted' | 'raw'.
-type label() :: non_neg_integer().
-type rep_mode() :: 'quiet' | 'normal' | 'verbose'.
-type start_from() :: 'byte_code' | 'src_code'.
@@ -129,7 +132,7 @@
defines = [] :: [dial_define()],
from = byte_code :: start_from(),
get_warnings = maybe :: boolean() | 'maybe',
- init_plt = none :: 'none' | file:filename(),
+ init_plts = [] :: [file:filename()],
include_dirs = [] :: [file:filename()],
output_plt = none :: 'none' | file:filename(),
legal_warnings = ordsets:new() :: ordset(dial_warn_tag()),
@@ -137,10 +140,10 @@
erlang_mode = false :: boolean(),
use_contracts = true :: boolean(),
output_file = none :: 'none' | file:filename(),
- output_format = formatted :: 'raw' | 'formatted',
+ output_format = formatted :: format(),
+ filename_opt = basename :: fopt(),
callgraph_file = "" :: file:filename(),
- check_plt = true :: boolean()
- }).
+ check_plt = true :: boolean()}).
-record(contract, {contracts = [] :: [contract_pair()],
args = [] :: [erl_types:erl_type()],
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index ab1bbe5ade..abad1f3a75 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -21,7 +21,7 @@
%%%-------------------------------------------------------------------
%%% File : dialyzer_analysis_callgraph.erl
%%% Author : Tobias Lindahl <[email protected]>
-%%% Description :
+%%% Description :
%%%
%%% Created : 5 Apr 2005 by Tobias Lindahl <[email protected]>
%%%-------------------------------------------------------------------
@@ -32,7 +32,7 @@
-include("dialyzer.hrl").
--record(analysis_state,
+-record(analysis_state,
{
codeserver :: dialyzer_codeserver:codeserver(),
analysis_type = succ_typings :: anal_type(),
@@ -44,7 +44,7 @@
plt :: dialyzer_plt:plt(),
start_from = byte_code :: start_from(),
use_contracts = true :: boolean(),
- behaviours = {false,[]} :: {boolean(),[atom()]}
+ behaviours = {false,[]} :: {boolean(),[atom()]}
}).
-record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}).
@@ -83,10 +83,10 @@ loop(#server_state{parent = Parent, legal_warnings = LegalWarnings} = State,
send_warnings(Parent, SendWarnings)
end,
loop(State, Analysis, ExtCalls);
- {AnalPid, cserver, CServer, Plt} ->
+ {AnalPid, cserver, CServer, Plt} ->
send_codeserver_plt(Parent, CServer, Plt),
loop(State, Analysis, ExtCalls);
- {AnalPid, done, Plt, DocPlt} ->
+ {AnalPid, done, Plt, DocPlt} ->
case ExtCalls =:= none of
true ->
send_analysis_done(Parent, Plt, DocPlt);
@@ -96,6 +96,9 @@ loop(#server_state{parent = Parent, legal_warnings = LegalWarnings} = State,
end;
{AnalPid, ext_calls, NewExtCalls} ->
loop(State, Analysis, NewExtCalls);
+ {AnalPid, ext_types, ExtTypes} ->
+ send_ext_types(Parent, ExtTypes),
+ loop(State, Analysis, ExtCalls);
{AnalPid, unknown_behaviours, UnknownBehaviour} ->
send_unknown_behaviours(Parent, UnknownBehaviour),
loop(State, Analysis, ExtCalls);
@@ -123,8 +126,7 @@ analysis_start(Parent, Analysis) ->
parent = Parent,
start_from = Analysis#analysis.start_from,
use_contracts = Analysis#analysis.use_contracts,
- behaviours = {Analysis#analysis.behaviours_chk,
- []}
+ behaviours = {Analysis#analysis.behaviours_chk, []}
},
Files = ordsets:from_list(Analysis#analysis.files),
{Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State),
@@ -132,22 +134,36 @@ analysis_start(Parent, Analysis) ->
NewCServer =
try
NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer0),
- OldRecords = dialyzer_plt:get_types(State#analysis_state.plt),
+ NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer0),
+ OldRecords = dialyzer_plt:get_types(Plt),
+ OldExpTypes0 = dialyzer_plt:get_exported_types(Plt),
MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ RemMods =
+ [case Analysis#analysis.start_from of
+ byte_code -> list_to_atom(filename:basename(F, ".beam"));
+ src_code -> list_to_atom(filename:basename(F, ".erl"))
+ end || F <- Files],
+ OldExpTypes1 = dialyzer_utils:sets_filter(RemMods, OldExpTypes0),
+ MergedExpTypes = sets:union(NewExpTypes, OldExpTypes1),
TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer0),
- TmpCServer2 = dialyzer_utils:process_record_remote_types(TmpCServer1),
- dialyzer_contracts:process_contract_remote_types(TmpCServer2)
+ TmpCServer2 =
+ dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
+ TmpCServer1),
+ TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3)
catch
throw:{error, _ErrorMsg} = Error -> exit(Error)
end,
- NewPlt = dialyzer_plt:insert_types(Plt, dialyzer_codeserver:get_records(NewCServer)),
- State0 = State#analysis_state{plt = NewPlt},
+ NewPlt0 = dialyzer_plt:insert_types(Plt, dialyzer_codeserver:get_records(NewCServer)),
+ ExpTypes = dialyzer_codeserver:get_exported_types(NewCServer),
+ NewPlt1 = dialyzer_plt:insert_exported_types(NewPlt0, ExpTypes),
+ State0 = State#analysis_state{plt = NewPlt1},
dump_callgraph(Callgraph, State0, Analysis),
State1 = State0#analysis_state{codeserver = NewCServer},
State2 = State1#analysis_state{no_warn_unused = NoWarn},
%% Remove all old versions of the files being analyzed
AllNodes = dialyzer_callgraph:all_nodes(Callgraph),
- Plt1 = dialyzer_plt:delete_list(NewPlt, AllNodes),
+ Plt1 = dialyzer_plt:delete_list(NewPlt1, AllNodes),
Exports = dialyzer_codeserver:get_exports(NewCServer),
NewCallgraph =
case Analysis#analysis.race_detection of
@@ -155,11 +171,12 @@ analysis_start(Parent, Analysis) ->
false -> Callgraph
end,
State3 = analyze_callgraph(NewCallgraph, State2#analysis_state{plt = Plt1}),
+ rcv_and_send_ext_types(Parent),
NonExports = sets:subtract(sets:from_list(AllNodes), Exports),
NonExportsList = sets:to_list(NonExports),
Plt3 = dialyzer_plt:delete_list(State3#analysis_state.plt, NonExportsList),
Plt4 = dialyzer_plt:delete_contract_list(Plt3, NonExportsList),
- send_codeserver_plt(Parent, CServer, State3#analysis_state.plt),
+ send_codeserver_plt(Parent, CServer, State3#analysis_state.plt),
send_analysis_done(Parent, Plt4, State3#analysis_state.doc_plt).
analyze_callgraph(Callgraph, State) ->
@@ -212,24 +229,24 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
{error, Reason} ->
{TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn,
TmpMods};
- {ok, NewCG, NoWarn, NewCServer, Mod} ->
+ {ok, NewCG, NoWarn, NewCServer, Mod} ->
{NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn,
[Mod|TmpMods]}
end
end;
byte_code ->
- fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) ->
+ fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) ->
case compile_byte(File, TmpCG, TmpCServer, UseContracts) of
{error, Reason} ->
{TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn,
TmpMods};
- {ok, NewCG, NoWarn, NewCServer, Mod} ->
+ {ok, NewCG, NoWarn, NewCServer, Mod} ->
{NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn,
[Mod|TmpMods]}
end
end
end,
- {NewCallgraph1, NewCServer, Failed, NoWarn, Modules} =
+ {NewCallgraph1, NewCServer, Failed, NoWarn, Modules} =
lists:foldl(Fun, {Callgraph, CServer, [], [], []}, Files),
case Failed =:= [] of
true ->
@@ -238,7 +255,7 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
lists:foldl(fun({Mod, F}, Dict) -> dict:append(Mod, F, Dict) end,
dict:new(), NewFiles),
check_for_duplicate_modules(ModDict);
- false ->
+ false ->
Msg = io_lib:format("Could not scan the following file(s): ~p",
[lists:flatten(Failed)]),
exit({error, Msg})
@@ -251,14 +268,14 @@ compile_and_store(Files, #analysis_state{codeserver = CServer,
if UnknownBehaviours =:= [] -> ok;
true -> send_unknown_behaviours(Parent, UnknownBehaviours)
end,
- State1 = State#analysis_state{behaviours = {BehChk,KnownBehaviours}},
+ State1 = State#analysis_state{behaviours = {BehChk, KnownBehaviours}},
NewCallgraph2 = cleanup_callgraph(State1, NewCServer, NewCallgraph1, Modules),
{T3, _} = statistics(runtime),
Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]),
- send_log(Parent, Msg2),
+ send_log(Parent, Msg2),
{NewCallgraph2, sets:from_list(NoWarn), NewCServer}.
-cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
+cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
codeserver = CodeServer,
behaviours = {BehChk, KnownBehaviours}
},
@@ -281,9 +298,9 @@ cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent,
not dialyzer_plt:contains_mfa(InitPlt, To)],
{BadCalls1, RealExtCalls} =
if ExtCalls1 =:= [] -> {[], []};
- true ->
+ true ->
ModuleSet = sets:from_list(Modules),
- lists:partition(fun({_From, {M, _F, _A}}) ->
+ lists:partition(fun({_From, {M, _F, _A}}) ->
sets:is_element(M, ModuleSet) orelse
dialyzer_plt:contains_module(InitPlt, M)
end, ExtCalls1)
@@ -350,14 +367,14 @@ compile_byte(File, Callgraph, CServer, UseContracts) ->
case dialyzer_utils:get_record_and_type_info(AbstrCode) of
{error, _} = Error -> Error;
{ok, RecInfo} ->
- CServer1 =
+ CServer1 =
dialyzer_codeserver:store_temp_records(Mod, RecInfo, CServer),
case UseContracts of
true ->
case dialyzer_utils:get_spec_info(Mod, AbstrCode, RecInfo) of
{error, _} = Error -> Error;
{ok, SpecInfo} ->
- CServer2 =
+ CServer2 =
dialyzer_codeserver:store_temp_contracts(Mod, SpecInfo,
CServer1),
store_core(Mod, Core, NoWarn, Callgraph, CServer2)
@@ -371,14 +388,28 @@ compile_byte(File, Callgraph, CServer, UseContracts) ->
store_core(Mod, Core, NoWarn, Callgraph, CServer) ->
Exp = get_exports_from_core(Core),
+ OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer),
+ NewExpTypes = get_exported_types_from_core(Core),
+ MergedExpTypes = sets:union(NewExpTypes, OldExpTypes),
CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer),
- {LabeledCore, CServer2} = label_core(Core, CServer1),
- store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer2, NoWarn).
+ CServer2 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
+ CServer1),
+ {LabeledCore, CServer3} = label_core(Core, CServer2),
+ store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer3, NoWarn).
abs_get_nowarn(Abs, M) ->
- [{M, F, A}
+ [{M, F, A}
|| {attribute, _, compile, {nowarn_unused_function, {F, A}}} <- Abs].
+get_exported_types_from_core(Core) ->
+ Attrs = cerl:module_attrs(Core),
+ ExpTypes1 = [cerl:concrete(L2) || {L1, L2} <- Attrs, cerl:is_literal(L1),
+ cerl:is_literal(L2),
+ cerl:concrete(L1) =:= 'export_type'],
+ ExpTypes2 = lists:flatten(ExpTypes1),
+ M = cerl:atom_val(cerl:module_name(Core)),
+ sets:from_list([{M, F, A} || {F, A} <- ExpTypes2]).
+
get_exports_from_core(Core) ->
Tree = cerl:from_records(Core),
Exports1 = cerl:module_exports(Tree),
@@ -390,7 +421,7 @@ label_core(Core, CServer) ->
NextLabel = dialyzer_codeserver:get_next_core_label(CServer),
CoreTree = cerl:from_records(Core),
{LabeledTree, NewNextLabel} = cerl_trees:label(CoreTree, NextLabel),
- {cerl:to_records(LabeledTree),
+ {cerl:to_records(LabeledTree),
dialyzer_codeserver:set_next_core_label(NewNextLabel, CServer)}.
store_code_and_build_callgraph(Mod, Core, Callgraph, CServer, NoWarn) ->
@@ -424,8 +455,12 @@ expand_files([File|Left], Ext, FileAcc) ->
case filelib:is_dir(File) of
true ->
{ok, List} = file:list_dir(File),
- NewFiles =
- [filename:join(File, X) || X <- List, filename:extension(X) =:= Ext],
+ NewFiles = lists:foldl(fun (X, Acc) ->
+ case filename:extension(X) =:= Ext of
+ true -> [filename:join(File, X)|Acc];
+ false -> Acc
+ end
+ end, FileAcc, List),
expand_files(Left, Ext, NewFiles);
false ->
expand_files(Left, Ext, [File|FileAcc])
@@ -454,6 +489,20 @@ default_includes(Dir) ->
%% Handle Messages
%%-------------------------------------------------------------------
+rcv_and_send_ext_types(Parent) ->
+ Self = self(),
+ Self ! {Self, done},
+ ExtTypes = rcv_ext_types(Self, []),
+ Parent ! {Self, ext_types, ExtTypes},
+ ok.
+
+rcv_ext_types(Self, ExtTypes) ->
+ receive
+ {Self, ext_types, ExtType} ->
+ rcv_ext_types(Self, [ExtType|ExtTypes]);
+ {Self, done} -> lists:usort(ExtTypes)
+ end.
+
send_log(Parent, Msg) ->
Parent ! {self(), log, Msg},
ok.
@@ -471,11 +520,15 @@ filter_warnings(LegalWarnings, Warnings) ->
send_analysis_done(Parent, Plt, DocPlt) ->
Parent ! {self(), done, Plt, DocPlt},
ok.
-
+
send_ext_calls(Parent, ExtCalls) ->
Parent ! {self(), ext_calls, ExtCalls},
ok.
+send_ext_types(Parent, ExtTypes) ->
+ Parent ! {self(), ext_types, ExtTypes},
+ ok.
+
send_unknown_behaviours(Parent, UnknownBehaviours) ->
Parent ! {self(), unknown_behaviours, UnknownBehaviours},
ok.
@@ -491,7 +544,7 @@ send_mod_deps(Parent, ModuleDeps) ->
Parent ! {self(), mod_deps, ModuleDeps},
ok.
-format_bad_calls([{{_, _, _}, {_, module_info, A}}|Left], CodeServer, Acc)
+format_bad_calls([{{_, _, _}, {_, module_info, A}}|Left], CodeServer, Acc)
when A =:= 0; A =:= 1 ->
format_bad_calls(Left, CodeServer, Acc);
format_bad_calls([{FromMFA, {M, F, A} = To}|Left], CodeServer, Acc) ->
@@ -504,7 +557,7 @@ format_bad_calls([], _CodeServer, Acc) ->
Acc.
find_call_file_and_line(Tree, MFA) ->
- Fun =
+ Fun =
fun(SubTree, Acc) ->
case cerl:is_c_call(SubTree) of
true ->
diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl
index 4e8dceaa8e..47ce9ba6eb 100644
--- a/lib/dialyzer/src/dialyzer_behaviours.erl
+++ b/lib/dialyzer/src/dialyzer_behaviours.erl
@@ -34,19 +34,25 @@
translate_behaviour_api_call/5, translatable_behaviours/1,
translate_callgraph/3]).
+-export_type([behaviour/0, behaviour_api_dict/0]).
+
%%--------------------------------------------------------------------
-include("dialyzer.hrl").
%%--------------------------------------------------------------------
+-type behaviour() :: atom().
+
-record(state, {plt :: dialyzer_plt:plt(),
codeserver :: dialyzer_codeserver:codeserver(),
- filename :: string(),
- behlines :: [{atom(), number()}]}).
+ filename :: file:filename(),
+ behlines :: [{behaviour(), non_neg_integer()}]}).
+
+%%--------------------------------------------------------------------
-spec get_behaviours([module()], dialyzer_codeserver:codeserver()) ->
- {[atom()], [atom()]}.
+ {[behaviour()], [behaviour()]}.
get_behaviours(Modules, Codeserver) ->
get_behaviours(Modules, Codeserver, [], []).
@@ -59,29 +65,37 @@ check_callbacks(Module, Attrs, Plt, Codeserver) ->
{Behaviours, BehLines} = get_behaviours(Attrs),
case Behaviours of
[] -> [];
- _ -> {_Var,Code} =
- dialyzer_codeserver:lookup_mfa_code({Module,module_info,0},
- Codeserver),
- File = get_file(cerl:get_ann(Code)),
- State = #state{plt = Plt, codeserver = Codeserver, filename = File,
- behlines = BehLines},
- Warnings = get_warnings(Module, Behaviours, State),
- [add_tag_file_line(Module, W, State) || W <- Warnings]
+ _ ->
+ MFA = {Module,module_info,0},
+ {_Var,Code} = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver),
+ File = get_file(cerl:get_ann(Code)),
+ State = #state{plt = Plt, codeserver = Codeserver, filename = File,
+ behlines = BehLines},
+ Warnings = get_warnings(Module, Behaviours, State),
+ [add_tag_file_line(Module, W, State) || W <- Warnings]
end.
--spec translatable_behaviours(cerl:c_module()) -> [{atom(),[_]}].
+-spec translatable_behaviours(cerl:c_module()) -> behaviour_api_dict().
translatable_behaviours(Tree) ->
Attrs = cerl:module_attrs(Tree),
{Behaviours, _BehLines} = get_behaviours(Attrs),
[{B, Calls} || B <- Behaviours, (Calls = behaviour_api_calls(B)) =/= []].
--spec get_behaviour_apis([atom()]) -> [mfa()].
+-spec get_behaviour_apis([behaviour()]) -> [mfa()].
get_behaviour_apis(Behaviours) ->
get_behaviour_apis(Behaviours, []).
--spec translate_behaviour_api_call(_, _, _, _, _) -> _.
+-spec translate_behaviour_api_call(dialyzer_races:mfa_or_funlbl(),
+ [erl_types:erl_type()],
+ [dialyzer_races:core_vars()],
+ module(),
+ behaviour_api_dict()) ->
+ {dialyzer_races:mfa_or_funlbl(),
+ [erl_types:erl_type()],
+ [dialyzer_races:core_vars()]}
+ | 'plain_call'.
translate_behaviour_api_call(_Fun, _ArgTypes, _Args, _Module, []) ->
plain_call;
@@ -101,8 +115,9 @@ translate_behaviour_api_call({Module, Fun, Arity}, ArgTypes, Args,
translate_behaviour_api_call(_Fun, _ArgTypes, _Args, _Module, _BehApiInfo) ->
plain_call.
--spec translate_callgraph([{atom(), _}], atom(), dialyzer_callgraph:callgraph())
- -> dialyzer_callgraph:callgraph().
+-spec translate_callgraph(behaviour_api_dict(), atom(),
+ dialyzer_callgraph:callgraph()) ->
+ dialyzer_callgraph:callgraph().
translate_callgraph([{Behaviour,_}|Behaviours], Module, Callgraph) ->
UsedCalls = [Call || {_From, {M, _F, _A}} = Call <-
@@ -156,9 +171,11 @@ check_all_callbacks(Module, Behaviour, Callbacks, State) ->
check_all_callbacks(_Module, _Behaviour, [], _State, Acc) ->
Acc;
-check_all_callbacks(Module, Behaviour, [{Fun, Arity, Spec}|Rest], State, Acc) ->
- Records = dialyzer_codeserver:get_records(State#state.codeserver),
- case parse_spec(Spec, Records) of
+check_all_callbacks(Module, Behaviour, [{Fun, Arity, Spec}|Rest],
+ #state{codeserver = CServer} = State, Acc) ->
+ Records = dialyzer_codeserver:get_records(CServer),
+ ExpTypes = dialyzer_codeserver:get_exported_types(CServer),
+ case parse_spec(Spec, ExpTypes, Records) of
{ok, Fun, Type} ->
RetType = erl_types:t_fun_range(Type),
ArgTypes = erl_types:t_fun_args(Type),
@@ -172,7 +189,7 @@ check_all_callbacks(Module, Behaviour, [{Fun, Arity}|Rest], State, Acc) ->
Warns = {spec_missing, [Behaviour, Fun, Arity]},
check_all_callbacks(Module, Behaviour, Rest, State, [Warns|Acc]).
-parse_spec(String, Records) ->
+parse_spec(String, ExpTypes, Records) ->
case erl_scan:string(String) of
{ok, Tokens, _} ->
case erl_parse:parse(Tokens) of
@@ -181,7 +198,8 @@ parse_spec(String, Records) ->
{attribute, _, 'spec', {{Fun, _}, [TypeForm|_Constraint]}} ->
MaybeRemoteType = erl_types:t_from_form(TypeForm),
try
- Type = erl_types:t_solve_remote(MaybeRemoteType, Records),
+ Type = erl_types:t_solve_remote(MaybeRemoteType, ExpTypes,
+ Records),
{ok, Fun, Type}
catch
throw:{error,Msg} -> {spec_remote_error, Msg}
@@ -260,7 +278,7 @@ get_line([]) -> -1.
get_file([{file, File}|_]) -> File;
get_file([_|Tail]) -> get_file(Tail).
-%%------------------------------------------------------------------------------
+%%-----------------------------------------------------------------------------
get_behaviours([], _Codeserver, KnownAcc, UnknownAcc) ->
{KnownAcc, UnknownAcc};
@@ -289,7 +307,7 @@ call_behaviours([Behaviour|Rest], KnownAcc, UnknownAcc) ->
_:_ -> call_behaviours(Rest, KnownAcc, [Behaviour | UnknownAcc])
end.
-%-------------------------------------------------------------------------------
+%------------------------------------------------------------------------------
get_behaviour_apis([], Acc) ->
Acc;
@@ -298,14 +316,22 @@ get_behaviour_apis([Behaviour | Rest], Acc) ->
{{Fun, Arity}, _} <- behaviour_api_calls(Behaviour)],
get_behaviour_apis(Rest, MFAs ++ Acc).
-%-------------------------------------------------------------------------------
+%------------------------------------------------------------------------------
nth_or_0(0, _List, Zero) ->
Zero;
nth_or_0(N, List, _Zero) ->
lists:nth(N, List).
-%-------------------------------------------------------------------------------
+%------------------------------------------------------------------------------
+
+-type behaviour_api_dict()::[{behaviour(), behaviour_api_info()}].
+-type behaviour_api_info()::[{original_fun(), replacement_fun()}].
+-type original_fun()::{atom(), arity()}.
+-type replacement_fun()::{atom(), arity(), arg_list()}.
+-type arg_list()::[byte()].
+
+-spec behaviour_api_calls(behaviour()) -> behaviour_api_info().
behaviour_api_calls(gen_server) ->
[{{start_link, 3}, {init, 1, [2]}},
diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl
index f932f43548..d3de5aaf45 100644
--- a/lib/dialyzer/src/dialyzer_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_callgraph.erl
@@ -59,6 +59,8 @@
put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2,
get_behaviour_api_calls/1]).
+-export_type([callgraph/0]).
+
-include("dialyzer.hrl").
%%----------------------------------------------------------------------
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index d533e734db..8d61216b7a 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -2,7 +2,7 @@
%%-------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,6 +29,10 @@
-module(dialyzer_cl).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([start/1]).
-include("dialyzer.hrl").
@@ -38,16 +42,18 @@
{backend_pid :: pid(),
erlang_mode = false :: boolean(),
external_calls = [] :: [mfa()],
+ external_types = [] :: [mfa()],
legal_warnings = ordsets:new() :: [dial_warn_tag()],
mod_deps = dict:new() :: dict(),
output = standard_io :: io:device(),
- output_format = formatted :: 'raw' | 'formatted',
+ output_format = formatted :: format(),
+ filename_opt = basename :: fopt(),
output_plt = none :: 'none' | file:filename(),
plt_info = none :: 'none' | dialyzer_plt:plt_info(),
report_mode = normal :: rep_mode(),
return_status= ?RET_NOTHING_SUSPICIOUS :: dial_ret(),
stored_warnings = [] :: [dial_warning()],
- unknown_behaviours = [] :: [atom()]
+ unknown_behaviours = [] :: [dialyzer_behaviours:behaviour()]
}).
%%--------------------------------------------------------------------
@@ -76,11 +82,15 @@ build_plt(Opts) ->
init_opts_for_build(Opts) ->
case Opts#options.output_plt =:= none of
true ->
- case Opts#options.init_plt of
- none -> Opts#options{init_plt = none, output_plt = get_default_plt()};
- Plt -> Opts#options{init_plt = none, output_plt = Plt}
+ case Opts#options.init_plts of
+ [] -> Opts#options{output_plt = get_default_output_plt()};
+ [Plt] -> Opts#options{init_plts = [], output_plt = Plt};
+ Plts ->
+ Msg = io_lib:format("Could not build multiple PLT files: ~s\n",
+ [format_plts(Plts)]),
+ error(Msg)
end;
- false -> Opts#options{init_plt = none}
+ false -> Opts#options{init_plts = []}
end.
%%--------------------------------------------------------------------
@@ -93,39 +103,58 @@ add_to_plt(Opts) ->
init_opts_for_add(Opts) ->
case Opts#options.output_plt =:= none of
true ->
- case Opts#options.init_plt of
- none -> Opts#options{output_plt = get_default_plt(),
- init_plt = get_default_plt()};
- Plt -> Opts#options{output_plt = Plt}
+ case Opts#options.init_plts of
+ [] -> Opts#options{output_plt = get_default_output_plt(),
+ init_plts = get_default_init_plt()};
+ [Plt] -> Opts#options{output_plt = Plt};
+ Plts ->
+ Msg = io_lib:format("Could not add to multiple PLT files: ~s\n",
+ [format_plts(Plts)]),
+ error(Msg)
end;
false ->
- case Opts#options.init_plt =:= none of
- true -> Opts#options{init_plt = get_default_plt()};
+ case Opts#options.init_plts =:= [] of
+ true -> Opts#options{init_plts = get_default_init_plt()};
false -> Opts
end
end.
%%--------------------------------------------------------------------
-check_plt(Opts) ->
+check_plt(#options{init_plts = []} = Opts) ->
Opts1 = init_opts_for_check(Opts),
- report_check(Opts),
- plt_common(Opts1, [], []).
+ report_check(Opts1),
+ plt_common(Opts1, [], []);
+check_plt(#options{init_plts = Plts} = Opts) ->
+ check_plt_aux(Plts, Opts).
+
+check_plt_aux([_] = Plt, Opts) ->
+ Opts1 = Opts#options{init_plts = Plt},
+ Opts2 = init_opts_for_check(Opts1),
+ report_check(Opts2),
+ plt_common(Opts2, [], []);
+check_plt_aux([Plt|Plts], Opts) ->
+ Opts1 = Opts#options{init_plts = [Plt]},
+ Opts2 = init_opts_for_check(Opts1),
+ report_check(Opts2),
+ plt_common(Opts2, [], []),
+ check_plt_aux(Plts, Opts).
init_opts_for_check(Opts) ->
- Plt =
- case Opts#options.init_plt of
- none -> get_default_plt();
- Plt0 -> Plt0
+ InitPlt =
+ case Opts#options.init_plts of
+ []-> get_default_init_plt();
+ Plt -> Plt
end,
+ [OutputPlt] = InitPlt,
Opts#options{files = [],
files_rec = [],
analysis_type = plt_check,
defines = [],
from = byte_code,
- init_plt = Plt,
+ init_plts = InitPlt,
include_dirs = [],
- output_plt = Plt,
+ output_plt = OutputPlt,
use_contracts = true
}.
@@ -139,23 +168,33 @@ remove_from_plt(Opts) ->
init_opts_for_remove(Opts) ->
case Opts#options.output_plt =:= none of
true ->
- case Opts#options.init_plt of
- none -> Opts#options{output_plt = get_default_plt(),
- init_plt = get_default_plt()};
- Plt -> Opts#options{output_plt = Plt}
+ case Opts#options.init_plts of
+ [] -> Opts#options{output_plt = get_default_output_plt(),
+ init_plts = get_default_init_plt()};
+ [Plt] -> Opts#options{output_plt = Plt};
+ Plts ->
+ Msg = io_lib:format("Could not remove from multiple PLT files: ~s\n",
+ [format_plts(Plts)]),
+ error(Msg)
end;
false ->
- case Opts#options.init_plt =:= none of
- true -> Opts#options{init_plt = get_default_plt()};
+ case Opts#options.init_plts =:= [] of
+ true -> Opts#options{init_plts = get_default_init_plt()};
false -> Opts
end
end.
%%--------------------------------------------------------------------
-plt_common(Opts, RemoveFiles, AddFiles) ->
+plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) ->
case check_plt(Opts, RemoveFiles, AddFiles) of
ok ->
+ case Opts#options.output_plt of
+ none -> ok;
+ OutPlt ->
+ {ok, Binary} = file:read_file(InitPlt),
+ file:write_file(OutPlt, Binary)
+ end,
case Opts#options.report_mode of
quiet -> ok;
_ -> io:put_chars(" yes\n")
@@ -169,7 +208,7 @@ plt_common(Opts, RemoveFiles, AddFiles) ->
report_failed_plt_check(Opts, DiffMd5),
{AnalFiles, RemovedMods, ModDeps1} =
expand_dependent_modules(Md5, DiffMd5, ModDeps),
- Plt = clean_plt(Opts#options.init_plt, RemovedMods),
+ Plt = clean_plt(InitPlt, RemovedMods),
case AnalFiles =:= [] of
true ->
%% Only removed stuff. Just write the PLT.
@@ -181,19 +220,19 @@ plt_common(Opts, RemoveFiles, AddFiles) ->
end;
{error, no_such_file} ->
Msg = io_lib:format("Could not find the PLT: ~s\n~s",
- [Opts#options.init_plt, default_plt_error_msg()]),
+ [InitPlt, default_plt_error_msg()]),
error(Msg);
{error, not_valid} ->
Msg = io_lib:format("The file: ~s is not a valid PLT file\n~s",
- [Opts#options.init_plt, default_plt_error_msg()]),
+ [InitPlt, default_plt_error_msg()]),
error(Msg);
{error, read_error} ->
Msg = io_lib:format("Could not read the PLT: ~s\n~s",
- [Opts#options.init_plt, default_plt_error_msg()]),
+ [InitPlt, default_plt_error_msg()]),
error(Msg);
{error, {no_file_to_remove, F}} ->
Msg = io_lib:format("Could not remove the file ~s from the PLT: ~s\n",
- [F, Opts#options.init_plt]),
+ [F, InitPlt]),
error(Msg)
end.
@@ -213,8 +252,7 @@ default_plt_error_msg() ->
%%--------------------------------------------------------------------
-check_plt(Opts, RemoveFiles, AddFiles) ->
- Plt = Opts#options.init_plt,
+check_plt(#options{init_plts = [Plt]} = Opts, RemoveFiles, AddFiles) ->
case dialyzer_plt:check_plt(Plt, RemoveFiles, AddFiles) of
{old_version, _MD5} = OldVersion ->
report_old_version(Opts),
@@ -229,14 +267,14 @@ check_plt(Opts, RemoveFiles, AddFiles) ->
%%--------------------------------------------------------------------
-report_check(#options{report_mode = ReportMode, init_plt = InitPlt}) ->
+report_check(#options{report_mode = ReportMode, init_plts = [InitPlt]}) ->
case ReportMode of
quiet -> ok;
_ ->
io:format(" Checking whether the PLT ~s is up-to-date...", [InitPlt])
end.
-report_old_version(#options{report_mode = ReportMode, init_plt = InitPlt}) ->
+report_old_version(#options{report_mode = ReportMode, init_plts = [InitPlt]}) ->
case ReportMode of
quiet -> ok;
_ ->
@@ -259,7 +297,7 @@ report_failed_plt_check(#options{analysis_type = AnalType,
report_analysis_start(#options{analysis_type = Type,
report_mode = ReportMode,
- init_plt = InitPlt,
+ init_plts = InitPlts,
output_plt = OutputPlt}) ->
case ReportMode of
quiet -> ok;
@@ -267,6 +305,7 @@ report_analysis_start(#options{analysis_type = Type,
io:format(" "),
case Type of
plt_add ->
+ [InitPlt] = InitPlts,
case InitPlt =:= OutputPlt of
true -> io:format("Adding information to ~s...", [OutputPlt]);
false -> io:format("Adding information from ~s to ~s...",
@@ -277,6 +316,7 @@ report_analysis_start(#options{analysis_type = Type,
plt_check ->
io:format("Rebuilding the information in ~s...", [OutputPlt]);
plt_remove ->
+ [InitPlt] = InitPlts,
case InitPlt =:= OutputPlt of
true -> io:format("Removing information from ~s...", [OutputPlt]);
false -> io:format("Removing information from ~s to ~s...",
@@ -315,16 +355,28 @@ report_md5_diff(List) ->
%%--------------------------------------------------------------------
-get_default_plt() ->
+get_default_init_plt() ->
+ [dialyzer_plt:get_default_plt()].
+
+get_default_output_plt() ->
dialyzer_plt:get_default_plt().
%%--------------------------------------------------------------------
+format_plts([Plt]) -> Plt;
+format_plts([Plt|Plts]) ->
+ Plt ++ ", " ++ format_plts(Plts).
+
+%%--------------------------------------------------------------------
+
do_analysis(Options) ->
Files = get_files_from_opts(Options),
- case Options#options.init_plt of
- none -> do_analysis(Files, Options, dialyzer_plt:new(), none);
- File -> do_analysis(Files, Options, dialyzer_plt:from_file(File), none)
+ case Options#options.init_plts of
+ [] -> do_analysis(Files, Options, dialyzer_plt:new(), none);
+ PltFiles ->
+ Plts = [dialyzer_plt:from_file(F) || F <- PltFiles],
+ Plt = dialyzer_plt:merge_plts_or_report_conflicts(PltFiles, Plts),
+ do_analysis(Files, Options, Plt, none)
end.
do_analysis(Files, Options, Plt, PltInfo) ->
@@ -487,8 +539,10 @@ hc(Mod) ->
new_state() ->
#cl_state{}.
-init_output(State0, #options{output_file = OutFile, output_format = OutFormat}) ->
- State = State0#cl_state{output_format = OutFormat},
+init_output(State0, #options{output_file = OutFile,
+ output_format = OutFormat,
+ filename_opt = FOpt}) ->
+ State = State0#cl_state{output_format = OutFormat, filename_opt = FOpt},
case OutFile =:= none of
true ->
State;
@@ -538,6 +592,8 @@ cl_loop(State, LogCache) ->
return_value(State, NewPlt);
{BackendPid, ext_calls, ExtCalls} ->
cl_loop(State#cl_state{external_calls = ExtCalls}, LogCache);
+ {BackendPid, ext_types, ExtTypes} ->
+ cl_loop(State#cl_state{external_types = ExtTypes}, LogCache);
{BackendPid, mod_deps, ModDeps} ->
NewState = State#cl_state{mod_deps = ModDeps},
cl_loop(NewState, LogCache);
@@ -552,7 +608,7 @@ cl_loop(State, LogCache) ->
cl_loop(State, LogCache)
end.
--spec failed_anal_msg(string(), [_]) -> string().
+-spec failed_anal_msg(string(), [_]) -> nonempty_string().
failed_anal_msg(Reason, LogCache) ->
Msg = "Analysis failed with error: " ++ Reason ++ "\n",
@@ -574,7 +630,7 @@ format_log_cache(LogCache) ->
store_warnings(#cl_state{stored_warnings = StoredWarnings} = St, Warnings) ->
St#cl_state{stored_warnings = StoredWarnings ++ Warnings}.
--spec store_unknown_behaviours(#cl_state{}, [_]) -> #cl_state{}.
+-spec store_unknown_behaviours(#cl_state{}, [dialyzer_behaviours:behaviour()]) -> #cl_state{}.
store_unknown_behaviours(#cl_state{unknown_behaviours = Behs} = St, Beh) ->
St#cl_state{unknown_behaviours = Beh ++ Behs}.
@@ -613,6 +669,7 @@ return_value(State = #cl_state{erlang_mode = ErlangMode,
false ->
print_warnings(State),
print_ext_calls(State),
+ print_ext_types(State),
print_unknown_behaviours(State),
maybe_close_output_file(State),
{RetValue, []};
@@ -649,10 +706,41 @@ do_print_ext_calls(Output, [{M,F,A}|T], Before) ->
do_print_ext_calls(_, [], _) ->
ok.
+print_ext_types(#cl_state{report_mode = quiet}) ->
+ ok;
+print_ext_types(#cl_state{output = Output,
+ external_calls = Calls,
+ external_types = Types,
+ stored_warnings = Warnings,
+ output_format = Format}) ->
+ case Types =:= [] of
+ true -> ok;
+ false ->
+ case Warnings =:= [] andalso Calls =:= [] of
+ true -> io:nl(Output); %% Need to do a newline first
+ false -> ok
+ end,
+ case Format of
+ formatted ->
+ io:put_chars(Output, "Unknown types:\n"),
+ do_print_ext_types(Output, Types, " ");
+ raw ->
+ io:put_chars(Output, "%% Unknown types:\n"),
+ do_print_ext_types(Output, Types, "%% ")
+ end
+ end.
+
+do_print_ext_types(Output, [{M,F,A}|T], Before) ->
+ io:format(Output, "~s~p:~p/~p\n", [Before,M,F,A]),
+ do_print_ext_types(Output, T, Before);
+do_print_ext_types(_, [], _) ->
+ ok.
+
%%print_unknown_behaviours(#cl_state{report_mode = quiet}) ->
%% ok;
print_unknown_behaviours(#cl_state{output = Output,
external_calls = Calls,
+ external_types = Types,
stored_warnings = Warnings,
unknown_behaviours = DupBehaviours,
legal_warnings = LegalWarnings,
@@ -662,7 +750,7 @@ print_unknown_behaviours(#cl_state{output = Output,
false -> ok;
true ->
Behaviours = lists:usort(DupBehaviours),
- case Warnings =:= [] andalso Calls =:= [] of
+ case Warnings =:= [] andalso Calls =:= [] andalso Types =:= [] of
true -> io:nl(Output); %% Need to do a newline first
false -> ok
end,
@@ -687,6 +775,7 @@ print_warnings(#cl_state{stored_warnings = []}) ->
ok;
print_warnings(#cl_state{output = Output,
output_format = Format,
+ filename_opt = FOpt,
stored_warnings = Warnings}) ->
PrWarnings = process_warnings(Warnings),
case PrWarnings of
@@ -694,7 +783,7 @@ print_warnings(#cl_state{output = Output,
[_|_] ->
S = case Format of
formatted ->
- [dialyzer:format_warning(W) || W <- PrWarnings];
+ [dialyzer:format_warning(W, FOpt) || W <- PrWarnings];
raw ->
[io_lib:format("~p. \n", [W]) || W <- PrWarnings]
end,
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index 9a522e906a..f80eb81ac6 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,8 +20,8 @@
-module(dialyzer_cl_parse).
--export([start/0]).
--export([collect_args/1]). % used also by typer_options.erl
+-export([start/0, get_lib_dir/1]).
+-export([collect_args/1]). % used also by typer
-include("dialyzer.hrl").
@@ -30,9 +30,11 @@
-type dial_cl_parse_ret() :: {'check_init', #options{}}
| {'plt_info', #options{}}
| {'cl', #options{}}
- | {{'gui', 'gs' | 'wx'}, #options{}}
+ | {{'gui', 'gs' | 'wx'}, #options{}}
| {'error', string()}.
+-type deep_string() :: string() | [deep_string()].
+
%%-----------------------------------------------------------------------
-spec start() -> dial_cl_parse_ret().
@@ -53,7 +55,7 @@ cl(["--add_to_plt"|T]) ->
put(dialyzer_options_analysis_type, plt_add),
cl(T);
cl(["--apps"|T]) ->
- T1 = get_lib_dir(T, []),
+ T1 = get_lib_dir(T),
{Args, T2} = collect_args(T1),
append_var(dialyzer_options_files_rec, Args),
cl(T2);
@@ -80,7 +82,7 @@ cl(["--get_warnings"|T]) ->
put(dialyzer_options_get_warnings, true),
cl(T);
cl(["-D"|_]) ->
- error("No defines specified after -D");
+ cl_error("No defines specified after -D");
cl(["-D"++Define|T]) ->
Def = re:split(Define, "=", [{return, list}]),
append_defines(Def),
@@ -90,7 +92,7 @@ cl(["-h"|_]) ->
cl(["--help"|_]) ->
help_message();
cl(["-I"]) ->
- error("no include directory specified after -I");
+ cl_error("no include directory specified after -I");
cl(["-I", Dir|T]) ->
append_include(Dir),
cl(T);
@@ -111,14 +113,14 @@ cl(["--com"++_|T]) ->
NewTail = command_line(T),
cl(NewTail);
cl(["--output"]) ->
- error("No outfile specified");
+ cl_error("No outfile specified");
cl(["-o"]) ->
- error("No outfile specified");
+ cl_error("No outfile specified");
cl(["--output",Output|T]) ->
put(dialyzer_output, Output),
cl(T);
cl(["--output_plt"]) ->
- error("No outfile specified for --output_plt");
+ cl_error("No outfile specified for --output_plt");
cl(["--output_plt",Output|T]) ->
put(dialyzer_output_plt, Output),
cl(T);
@@ -131,16 +133,25 @@ cl(["-o"++Output|T]) ->
cl(["--raw"|T]) ->
put(dialyzer_output_format, raw),
cl(T);
+cl(["--fullpath"|T]) ->
+ put(dialyzer_filename_opt, fullpath),
+ cl(T);
cl(["-pa", Path|T]) ->
case code:add_patha(Path) of
true -> cl(T);
- {error, _} -> error("Bad directory for -pa: "++Path)
+ {error, _} -> cl_error("Bad directory for -pa: " ++ Path)
end;
-cl(["--plt", PLT|T]) ->
- put(dialyzer_init_plt, PLT),
- cl(T);
cl(["--plt"]) ->
error("No plt specified for --plt");
+cl(["--plt", PLT|T]) ->
+ put(dialyzer_init_plts, [PLT]),
+ cl(T);
+cl(["--plts"]) ->
+ error("No plts specified for --plts");
+cl(["--plts"|T]) ->
+ {PLTs, NewT} = get_plts(T, []),
+ put(dialyzer_init_plts, PLTs),
+ cl(NewT);
cl(["-q"|T]) ->
put(dialyzer_options_report_mode, quiet),
cl(T);
@@ -163,14 +174,14 @@ cl(["--verbose"|T]) ->
put(dialyzer_options_report_mode, verbose),
cl(T);
cl(["-W"|_]) ->
- error("-W given without warning");
+ cl_error("-W given without warning");
cl(["-Whelp"|_]) ->
help_warnings();
cl(["-W"++Warn|T]) ->
append_var(dialyzer_warnings, [list_to_atom(Warn)]),
cl(T);
cl(["--dump_callgraph"]) ->
- error("No outfile specified for --dump_callgraph");
+ cl_error("No outfile specified for --dump_callgraph");
cl(["--dump_callgraph", File|T]) ->
put(dialyzer_callgraph_file, File),
cl(T);
@@ -186,7 +197,7 @@ cl([H|_] = L) ->
NewTail = command_line(L),
cl(NewTail);
false ->
- error("Unknown option: " ++ H)
+ cl_error("Unknown option: " ++ H)
end;
cl([]) ->
{RetTag, Opts} =
@@ -205,7 +216,7 @@ cl([]) ->
end
end,
case dialyzer_options:build(Opts) of
- {error, Msg} -> error(Msg);
+ {error, Msg} -> cl_error(Msg);
OptsRecord -> {RetTag, OptsRecord}
end.
@@ -221,7 +232,9 @@ command_line(T0) ->
end,
T.
-error(Str) ->
+-spec cl_error(deep_string()) -> no_return().
+
+cl_error(Str) ->
Msg = lists:flatten(Str),
throw({dialyzer_cl_parse_error, Msg}).
@@ -235,6 +248,7 @@ init() ->
put(dialyzer_options_defines, DefaultOpts#options.defines),
put(dialyzer_options_files, DefaultOpts#options.files),
put(dialyzer_output_format, formatted),
+ put(dialyzer_filename_opt, basename),
put(dialyzer_options_check_plt, DefaultOpts#options.check_plt),
ok.
@@ -273,6 +287,7 @@ cl_options() ->
{files_rec, get(dialyzer_options_files_rec)},
{output_file, get(dialyzer_output)},
{output_format, get(dialyzer_output_format)},
+ {filename_opt, get(dialyzer_filename_opt)},
{analysis_type, get(dialyzer_options_analysis_type)},
{get_warnings, get(dialyzer_options_get_warnings)},
{callgraph_file, get(dialyzer_callgraph_file)}
@@ -282,7 +297,7 @@ common_options() ->
[{defines, get(dialyzer_options_defines)},
{from, get(dialyzer_options_from)},
{include_dirs, get(dialyzer_include)},
- {init_plt, get(dialyzer_init_plt)},
+ {plts, get(dialyzer_init_plts)},
{output_plt, get(dialyzer_output_plt)},
{report_mode, get(dialyzer_options_report_mode)},
{use_spec, get(dialyzer_options_use_contracts)},
@@ -291,6 +306,11 @@ common_options() ->
%%-----------------------------------------------------------------------
+-spec get_lib_dir([string()]) -> [string()].
+
+get_lib_dir(Apps) ->
+ get_lib_dir(Apps, []).
+
get_lib_dir([H|T], Acc) ->
NewElem =
case code:lib_dir(list_to_atom(H)) of
@@ -307,30 +327,42 @@ get_lib_dir([], Acc) ->
%%-----------------------------------------------------------------------
+get_plts(["--"|T], Acc) -> {lists:reverse(Acc), T};
+get_plts(["-"++_Opt = H|T], Acc) -> {lists:reverse(Acc), [H|T]};
+get_plts([H|T], Acc) -> get_plts(T, [H|Acc]);
+get_plts([], Acc) -> {lists:reverse(Acc), []}.
+
+%%-----------------------------------------------------------------------
+
+-spec help_warnings() -> no_return().
+
help_warnings() ->
S = warning_options_msg(),
io:put_chars(S),
erlang:halt(?RET_NOTHING_SUSPICIOUS).
+-spec help_message() -> no_return().
+
help_message() ->
S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
- [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]*
- [--output_plt file] [-Wwarn]* [--src] [--gui | --wx]
- [files_or_dirs] [-r dirs] [--apps applications] [-o outfile]
+ [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]*
+ [-I include_dir]* [--output_plt file] [-Wwarn]*
+ [--src] [--gui | --wx] [files_or_dirs] [-r dirs]
+ [--apps applications] [-o outfile]
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
- [--no_native]
+ [--no_native] [--fullpath]
Options:
files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
Use Dialyzer from the command line to detect defects in the
specified files or directories containing .erl or .beam files,
- depending on the type of the analysis
+ depending on the type of the analysis.
-r dirs
Same as the previous but the specified directories are searched
recursively for subdirectories containing .erl or .beam files in
- them, depending on the type of analysis
+ them, depending on the type of analysis.
--apps applications
- Option typically used when building or modifying a PLT as in:
+ Option typically used when building or modifying a plt as in:
dialyzer --build_plt --apps erts kernel stdlib mnesia ...
to conveniently refer to library applications corresponding to the
Erlang/OTP installation. However, the option is general and can also
@@ -339,75 +371,90 @@ Options:
dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam
-o outfile (or --output outfile)
When using Dialyzer from the command line, send the analysis
- results to the specified \"outfile\" rather than to stdout
+ results to the specified outfile rather than to stdout.
--raw
When using Dialyzer from the command line, output the raw analysis
results (Erlang terms) instead of the formatted result.
The raw format is easier to post-process (for instance, to filter
- warnings or to output HTML pages)
+ warnings or to output HTML pages).
--src
Override the default, which is to analyze BEAM files, and
- analyze starting from Erlang source code instead
+ analyze starting from Erlang source code instead.
-Dname (or -Dname=value)
- When analyzing from source, pass the define to Dialyzer (**)
+ When analyzing from source, pass the define to Dialyzer. (**)
-I include_dir
- When analyzing from source, pass the include_dir to Dialyzer (**)
+ When analyzing from source, pass the include_dir to Dialyzer. (**)
-pa dir
Include dir in the path for Erlang (useful when analyzing files
- that have '-include_lib()' directives)
+ that have '-include_lib()' directives).
--output_plt file
- Store the plt at the specified file after building it
+ Store the plt at the specified file after building it.
--plt plt
Use the specified plt as the initial plt (if the plt was built
- during setup the files will be checked for consistency)
+ during setup the files will be checked for consistency).
+ --plts plt*
+ Merge the specified plts to create the initial plt -- requires
+ that the plts are disjoint (i.e., do not have any module
+ appearing in more than one plt).
+ The plts are created in the usual way:
+ dialyzer --build_plt --output_plt plt_1 files_to_include
+ ...
+ dialyzer --build_plt --output_plt plt_n files_to_include
+ and then can be used in either of the following ways:
+ dialyzer files_to_analyze --plts plt_1 ... plt_n
+ or:
+ dialyzer --plts plt_1 ... plt_n -- files_to_analyze
+ (Note the -- delimiter in the second case)
-Wwarn
A family of options which selectively turn on/off warnings
- (for help on the names of warnings use dialyzer -Whelp)
+ (for help on the names of warnings use dialyzer -Whelp).
--shell
- Do not disable the Erlang shell while running the GUI
+ Do not disable the Erlang shell while running the GUI.
--version (or -v)
- Prints the Dialyzer version and some more information and exits
+ Print the Dialyzer version and some more information and exit.
--help (or -h)
- Prints this message and exits
+ Print this message and exit.
--quiet (or -q)
- Makes Dialyzer a bit more quiet
+ Make Dialyzer a bit more quiet.
--verbose
- Makes Dialyzer a bit more verbose
+ Make Dialyzer a bit more verbose.
--build_plt
The analysis starts from an empty plt and creates a new one from the
files specified with -c and -r. Only works for beam files.
- Use --plt or --output_plt to override the default plt location.
+ Use --plt(s) or --output_plt to override the default plt location.
--add_to_plt
The plt is extended to also include the files specified with -c and -r.
- Use --plt to specify wich plt to start from, and --output_plt to
- specify where to put the plt. Note that the analysis might include
- files from the plt if they depend on the new files.
+ Use --plt(s) to specify which plt to start from, and --output_plt to
+ specify where to put the plt. Note that the analysis might include
+ files from the plt if they depend on the new files.
This option only works with beam files.
--remove_from_plt
The information from the files specified with -c and -r is removed
from the plt. Note that this may cause a re-analysis of the remaining
dependent files.
--check_plt
- Checks the plt for consistency and rebuilds it if it is not up-to-date.
+ Check the plt for consistency and rebuild it if it is not up-to-date.
Actually, this option is of rare use as it is on by default.
--no_check_plt (or -n)
Skip the plt check when running Dialyzer. Useful when working with
installed plts that never change.
--plt_info
- Makes Dialyzer print information about the plt and then quit. The plt
- can be specified with --plt.
+ Make Dialyzer print information about the plt and then quit. The plt
+ can be specified with --plt(s).
--get_warnings
- Makes Dialyzer emit warnings even when manipulating the plt. Only
- emits warnings for files that are actually analyzed.
+ Make Dialyzer emit warnings even when manipulating the plt. Warnings
+ are only emitted for files that are actually analyzed.
--dump_callgraph file
Dump the call graph into the specified file whose format is determined
by the file name extension. Supported extensions are: raw, dot, and ps.
If something else is used as file name extension, default format '.raw'
will be used.
--no_native (or -nn)
- Bypass the native code compilation of some key files that dialyzer
+ Bypass the native code compilation of some key files that Dialyzer
heuristically performs when dialyzing many files; this avoids the
compilation time but it may result in (much) longer analysis time.
+ --fullpath
+ Display the full path names of files for which warnings are emitted.
--gui
Use the gs-based GUI.
--wx
@@ -455,13 +502,13 @@ warning_options_msg() ->
Include warnings about behaviour callbacks which drift from the published
recommended interfaces.
-Wunderspecs ***
- Warn about underspecified functions
+ Warn about underspecified functions
(those whose -spec is strictly more allowing than the success typing).
The following options are also available but their use is not recommended:
(they are mostly for Dialyzer developers and internal debugging)
-Woverspecs ***
- Warn about overspecified functions
+ Warn about overspecified functions
(those whose -spec is strictly less allowing than the success typing).
-Wspecdiffs ***
Warn when the -spec is different than the success typing.
diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl
index 3bc5fadc21..b2097f7e53 100644
--- a/lib/dialyzer/src/dialyzer_codeserver.erl
+++ b/lib/dialyzer/src/dialyzer_codeserver.erl
@@ -21,7 +21,7 @@
%%%-------------------------------------------------------------------
%%% File : dialyzer_codeserver.erl
%%% Author : Tobias Lindahl <[email protected]>
-%%% Description :
+%%% Description :
%%%
%%% Created : 4 Apr 2005 by Tobias Lindahl <[email protected]>
%%%-------------------------------------------------------------------
@@ -29,15 +29,19 @@
-export([delete/1,
finalize_contracts/2,
+ finalize_exported_types/2,
finalize_records/2,
get_contracts/1,
- get_exports/1,
+ get_exported_types/1,
+ get_exports/1,
get_records/1,
get_next_core_label/1,
get_temp_contracts/1,
+ get_temp_exported_types/1,
get_temp_records/1,
- insert/3,
- insert_exports/2,
+ insert/3,
+ insert_exports/2,
+ insert_temp_exported_types/2,
is_exported/2,
lookup_mod_code/2,
lookup_mfa_code/2,
@@ -52,17 +56,21 @@
store_contracts/3,
store_temp_contracts/3]).
+-export_type([codeserver/0]).
+
-include("dialyzer.hrl").
%%--------------------------------------------------------------------
--record(codeserver, {table_pid :: pid(),
- exports = sets:new() :: set(), % set(mfa())
- next_core_label = 0 :: label(),
- records = dict:new() :: dict(),
- temp_records = dict:new() :: dict(),
- contracts = dict:new() :: dict(),
- temp_contracts = dict:new() :: dict()}).
+-record(codeserver, {table_pid :: pid(),
+ exported_types = sets:new() :: set(), % set(mfa())
+ temp_exported_types = sets:new() :: set(), % set(mfa())
+ exports = sets:new() :: set(), % set(mfa())
+ next_core_label = 0 :: label(),
+ records = dict:new() :: dict(),
+ temp_records = dict:new() :: dict(),
+ contracts = dict:new() :: dict(),
+ temp_contracts = dict:new() :: dict()}).
-opaque codeserver() :: #codeserver{}.
@@ -78,12 +86,17 @@ new() ->
delete(#codeserver{table_pid = TablePid}) ->
table__delete(TablePid).
--spec insert(module(), cerl:c_module(), codeserver()) -> codeserver().
+-spec insert(atom(), cerl:c_module(), codeserver()) -> codeserver().
insert(Mod, ModCode, CS) ->
NewTablePid = table__insert(CS#codeserver.table_pid, Mod, ModCode),
CS#codeserver{table_pid = NewTablePid}.
+-spec insert_temp_exported_types(set(), codeserver()) -> codeserver().
+
+insert_temp_exported_types(Set, CS) ->
+ CS#codeserver{temp_exported_types = Set}.
+
-spec insert_exports([mfa()], codeserver()) -> codeserver().
insert_exports(List, #codeserver{exports = Exports} = CS) ->
@@ -96,12 +109,27 @@ insert_exports(List, #codeserver{exports = Exports} = CS) ->
is_exported(MFA, #codeserver{exports = Exports}) ->
sets:is_element(MFA, Exports).
+-spec get_exported_types(codeserver()) -> set(). % set(mfa())
+
+get_exported_types(#codeserver{exported_types = ExpTypes}) ->
+ ExpTypes.
+
+-spec get_temp_exported_types(codeserver()) -> set().
+
+get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) ->
+ TempExpTypes.
+
-spec get_exports(codeserver()) -> set(). % set(mfa())
get_exports(#codeserver{exports = Exports}) ->
Exports.
--spec lookup_mod_code(module(), codeserver()) -> cerl:c_module().
+-spec finalize_exported_types(set(), codeserver()) -> codeserver().
+
+finalize_exported_types(Set, CS) ->
+ CS#codeserver{exported_types = Set, temp_exported_types = sets:new()}.
+
+-spec lookup_mod_code(atom(), codeserver()) -> cerl:c_module().
lookup_mod_code(Mod, CS) when is_atom(Mod) ->
table__lookup(CS#codeserver.table_pid, Mod).
@@ -121,7 +149,7 @@ get_next_core_label(#codeserver{next_core_label = NCL}) ->
set_next_core_label(NCL, CS) ->
CS#codeserver{next_core_label = NCL}.
--spec store_records(module(), dict(), codeserver()) -> codeserver().
+-spec store_records(atom(), dict(), codeserver()) -> codeserver().
store_records(Mod, Dict, #codeserver{records = RecDict} = CS)
when is_atom(Mod) ->
@@ -130,7 +158,7 @@ store_records(Mod, Dict, #codeserver{records = RecDict} = CS)
false -> CS#codeserver{records = dict:store(Mod, Dict, RecDict)}
end.
--spec lookup_mod_records(module(), codeserver()) -> dict().
+-spec lookup_mod_records(atom(), codeserver()) -> dict().
lookup_mod_records(Mod, #codeserver{records = RecDict})
when is_atom(Mod) ->
@@ -139,12 +167,12 @@ lookup_mod_records(Mod, #codeserver{records = RecDict})
{ok, Dict} -> Dict
end.
--spec get_records(codeserver()) -> dict().
+-spec get_records(codeserver()) -> dict().
get_records(#codeserver{records = RecDict}) ->
RecDict.
--spec store_temp_records(module(), dict(), codeserver()) -> codeserver().
+-spec store_temp_records(atom(), dict(), codeserver()) -> codeserver().
store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS)
when is_atom(Mod) ->
@@ -153,7 +181,7 @@ store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS)
false -> CS#codeserver{temp_records = dict:store(Mod, Dict, TempRecDict)}
end.
--spec get_temp_records(codeserver()) -> dict().
+-spec get_temp_records(codeserver()) -> dict().
get_temp_records(#codeserver{temp_records = TempRecDict}) ->
TempRecDict.
@@ -163,12 +191,12 @@ get_temp_records(#codeserver{temp_records = TempRecDict}) ->
set_temp_records(Dict, CS) ->
CS#codeserver{temp_records = Dict}.
--spec finalize_records(dict(), codeserver()) -> codeserver().
+-spec finalize_records(dict(), codeserver()) -> codeserver().
finalize_records(Dict, CS) ->
CS#codeserver{records = Dict, temp_records = dict:new()}.
--spec store_contracts(module(), dict(), codeserver()) -> codeserver().
+-spec store_contracts(atom(), dict(), codeserver()) -> codeserver().
store_contracts(Mod, Dict, #codeserver{contracts = C} = CS) when is_atom(Mod) ->
case dict:size(Dict) =:= 0 of
@@ -176,7 +204,7 @@ store_contracts(Mod, Dict, #codeserver{contracts = C} = CS) when is_atom(Mod) ->
false -> CS#codeserver{contracts = dict:store(Mod, Dict, C)}
end.
--spec lookup_mod_contracts(module(), codeserver()) -> dict().
+-spec lookup_mod_contracts(atom(), codeserver()) -> dict().
lookup_mod_contracts(Mod, #codeserver{contracts = ContDict})
when is_atom(Mod) ->
@@ -185,7 +213,7 @@ lookup_mod_contracts(Mod, #codeserver{contracts = ContDict})
{ok, Dict} -> Dict
end.
--spec lookup_mfa_contract(mfa(), codeserver()) ->
+-spec lookup_mfa_contract(mfa(), codeserver()) ->
'error' | {'ok', dialyzer_contracts:file_contract()}.
lookup_mfa_contract({M,_F,_A} = MFA, #codeserver{contracts = ContDict}) ->
@@ -194,12 +222,12 @@ lookup_mfa_contract({M,_F,_A} = MFA, #codeserver{contracts = ContDict}) ->
{ok, Dict} -> dict:find(MFA, Dict)
end.
--spec get_contracts(codeserver()) -> dict().
+-spec get_contracts(codeserver()) -> dict().
get_contracts(#codeserver{contracts = ContDict}) ->
ContDict.
--spec store_temp_contracts(module(), dict(), codeserver()) -> codeserver().
+-spec store_temp_contracts(atom(), dict(), codeserver()) -> codeserver().
store_temp_contracts(Mod, Dict, #codeserver{temp_contracts = C} = CS)
when is_atom(Mod) ->
@@ -263,7 +291,7 @@ table__loop(Cached, Map) ->
Pid ! {self(), Mod, Ans},
table__loop({Mod, Ans}, Map);
{insert, List} ->
- NewMap = lists:foldl(fun({Key, Val}, AccMap) ->
+ NewMap = lists:foldl(fun({Key, Val}, AccMap) ->
dict:store(Key, Val, AccMap)
end, Map, List),
table__loop(Cached, NewMap)
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 3486c72748..bcdcf2685d 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -21,7 +21,7 @@
-module(dialyzer_contracts).
-export([check_contract/2,
- check_contracts/3,
+ check_contracts/3,
contracts_without_fun/3,
contract_to_string/1,
get_invalid_contract_warnings/3,
@@ -33,6 +33,8 @@
process_contract_remote_types/1,
store_tmp_contract/5]).
+-export_type([file_contract/0, plt_contracts/0]).
+
%%-----------------------------------------------------------------------
-include("dialyzer.hrl").
@@ -50,7 +52,7 @@
%% to expand records and/or remote types that they might contain.
%%-----------------------------------------------------------------------
--type tmp_contract_fun() :: fun((dict()) -> contract_pair()).
+-type tmp_contract_fun() :: fun((set(), dict()) -> contract_pair()).
-record(tmp_contract, {contract_funs = [] :: [tmp_contract_fun()],
forms = [] :: [{_, _}]}).
@@ -104,13 +106,13 @@ contract_to_string(#contract{forms = Forms}) ->
contract_to_string_1([{Contract, []}]) ->
strip_fun(erl_types:t_form_to_string(Contract));
contract_to_string_1([{Contract, []}|Rest]) ->
- strip_fun(erl_types:t_form_to_string(Contract)) ++ "\n ; "
+ strip_fun(erl_types:t_form_to_string(Contract)) ++ "\n ; "
++ contract_to_string_1(Rest);
contract_to_string_1([{Contract, Constraints}]) ->
- strip_fun(erl_types:t_form_to_string(Contract)) ++ " when "
+ strip_fun(erl_types:t_form_to_string(Contract)) ++ " when "
++ constraints_to_string(Constraints);
contract_to_string_1([{Contract, Constraints}|Rest]) ->
- strip_fun(erl_types:t_form_to_string(Contract)) ++ " when "
+ strip_fun(erl_types:t_form_to_string(Contract)) ++ " when "
++ constraints_to_string(Constraints) ++ ";" ++
contract_to_string_1(Rest).
@@ -128,7 +130,7 @@ constraints_to_string([{type, _, constraint, [{atom, _, What}, Types]}]) ->
sequence([erl_types:t_form_to_string(T) || T <- Types], ",") ++ ")";
constraints_to_string([{type, _, constraint, [{atom, _, What}, Types]}|Rest]) ->
atom_to_list(What) ++ "(" ++
- sequence([erl_types:t_form_to_string(T) || T <- Types], ",")
+ sequence([erl_types:t_form_to_string(T) || T <- Types], ",")
++ "), " ++ constraints_to_string(Rest).
sequence([], _Delimiter) -> "";
@@ -140,10 +142,11 @@ sequence([H|T], Delimiter) -> H ++ Delimiter ++ sequence(T, Delimiter).
process_contract_remote_types(CodeServer) ->
TmpContractDict = dialyzer_codeserver:get_temp_contracts(CodeServer),
+ ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer),
RecordDict = dialyzer_codeserver:get_records(CodeServer),
ContractFun =
fun({_M, _F, _A}, {File, #tmp_contract{contract_funs = CFuns, forms = Forms}}) ->
- NewCs = [CFun(RecordDict) || CFun <- CFuns],
+ NewCs = [CFun(ExpTypes, RecordDict) || CFun <- CFuns],
Args = general_domain(NewCs),
{File, #contract{contracts = NewCs, args = Args, forms = Forms}}
end,
@@ -153,27 +156,30 @@ process_contract_remote_types(CodeServer) ->
end,
NewContractDict = dict:map(ModuleFun, TmpContractDict),
dialyzer_codeserver:finalize_contracts(NewContractDict, CodeServer).
-
+
-spec check_contracts([{mfa(), file_contract()}],
dialyzer_callgraph:callgraph(), dict()) -> plt_contracts().
check_contracts(Contracts, Callgraph, FunTypes) ->
FoldFun =
- fun(Label, Type, NewContracts) ->
- {ok, {M,F,A} = MFA} = dialyzer_callgraph:lookup_name(Label, Callgraph),
- case orddict:find(MFA, Contracts) of
- {ok, {_FileLine, Contract}} ->
- case check_contract(Contract, Type) of
- ok ->
- case erl_bif_types:is_known(M, F, A) of
- true ->
- %% Disregard the contracts since
- %% this is a known function.
- NewContracts;
- false ->
- [{MFA, Contract}|NewContracts]
+ fun(Label, Type, NewContracts) ->
+ case dialyzer_callgraph:lookup_name(Label, Callgraph) of
+ {ok, {M,F,A} = MFA} ->
+ case orddict:find(MFA, Contracts) of
+ {ok, {_FileLine, Contract}} ->
+ case check_contract(Contract, Type) of
+ ok ->
+ case erl_bif_types:is_known(M, F, A) of
+ true ->
+ %% Disregard the contracts since
+ %% this is a known function.
+ NewContracts;
+ false ->
+ [{MFA, Contract}|NewContracts]
+ end;
+ {error, _Error} -> NewContracts
end;
- {error, _Error} -> NewContracts
+ error -> NewContracts
end;
error -> NewContracts
end
@@ -184,8 +190,8 @@ check_contracts(Contracts, Callgraph, FunTypes) ->
-spec check_contract(#contract{}, erl_types:erl_type()) -> 'ok' | {'error', term()}.
check_contract(#contract{contracts = Contracts}, SuccType) ->
- try
- Contracts1 = [{Contract, insert_constraints(Constraints, dict:new())}
+ try
+ Contracts1 = [{Contract, insert_constraints(Constraints, dict:new())}
|| {Contract, Constraints} <- Contracts],
Contracts2 = [erl_types:t_subst(Contract, Dict)
|| {Contract, Dict} <- Contracts1],
@@ -194,7 +200,7 @@ check_contract(#contract{contracts = Contracts}, SuccType) ->
error ->
{error, {overlapping_contract, []}};
ok ->
- InfList = [erl_types:t_inf(Contract, SuccType, opaque)
+ InfList = [erl_types:t_inf(Contract, SuccType, opaque)
|| Contract <- Contracts2],
case check_contract_inf_list(InfList, SuccType) of
{error, _} = Invalid -> Invalid;
@@ -226,7 +232,7 @@ check_contract_inf_list([FunType|Left], SuccType) ->
STRange = erl_types:t_fun_range(SuccType),
case erl_types:t_is_none_or_unit(STRange) of
true -> ok;
- false ->
+ false ->
Range = erl_types:t_fun_range(FunType),
case erl_types:t_is_none(erl_types:t_inf(STRange, Range, opaque)) of
true -> check_contract_inf_list(Left, SuccType);
@@ -258,9 +264,9 @@ check_extraneous_1(Contract, SuccType) ->
process_contracts(OverContracts, Args) ->
process_contracts(OverContracts, Args, erl_types:t_none()).
-
+
process_contracts([OverContract|Left], Args, AccRange) ->
- NewAccRange =
+ NewAccRange =
case process_contract(OverContract, Args) of
error -> AccRange;
{ok, Range} -> erl_types:t_sup(AccRange, Range)
@@ -273,12 +279,12 @@ process_contracts([], _Args, AccRange) ->
process_contract({Contract, Constraints}, CallTypes0) ->
CallTypesFun = erl_types:t_fun(CallTypes0, erl_types:t_any()),
- ContArgsFun = erl_types:t_fun(erl_types:t_fun_args(Contract),
+ ContArgsFun = erl_types:t_fun(erl_types:t_fun_args(Contract),
erl_types:t_any()),
?debug("Instance: Contract: ~s\n Arguments: ~s\n",
- [erl_types:t_to_string(ContArgsFun),
+ [erl_types:t_to_string(ContArgsFun),
erl_types:t_to_string(CallTypesFun)]),
- case solve_constraints(ContArgsFun, CallTypesFun, Constraints) of
+ case solve_constraints(ContArgsFun, CallTypesFun, Constraints) of
{ok, VarDict} ->
{ok, erl_types:t_subst(erl_types:t_fun_range(Contract), VarDict)};
error -> error
@@ -288,7 +294,7 @@ solve_constraints(Contract, Call, Constraints) ->
%% First make sure the call follows the constraints
CDict = insert_constraints(Constraints, dict:new()),
Contract1 = erl_types:t_subst(Contract, CDict),
- %% Just a safe over-approximation.
+ %% Just a safe over-approximation.
%% TODO: Find the types for type variables properly
ContrArgs = erl_types:t_fun_args(Contract1),
CallArgs = erl_types:t_fun_args(Call),
@@ -309,7 +315,7 @@ solve_constraints(Contract, Call, Constraints) ->
-spec contracts_without_fun(dict(), [_], dialyzer_callgraph:callgraph()) -> [dial_warning()].
contracts_without_fun(Contracts, AllFuns0, Callgraph) ->
- AllFuns1 = [{dialyzer_callgraph:lookup_name(Label, Callgraph), Arity}
+ AllFuns1 = [{dialyzer_callgraph:lookup_name(Label, Callgraph), Arity}
|| {Label, Arity} <- AllFuns0],
AllFuns2 = [{M, F, A} || {{ok, {M, F, _}}, A} <- AllFuns1],
AllContractMFAs = dict:fetch_keys(Contracts),
@@ -351,46 +357,49 @@ contract_from_form(Forms, RecDict) ->
{CFuns, Forms1} = contract_from_form(Forms, RecDict, [], []),
#tmp_contract{contract_funs = CFuns, forms = Forms1}.
-contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], RecDict,
+contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], RecDict,
TypeAcc, FormAcc) ->
- TypeFun =
- fun(AllRecords) ->
+ TypeFun =
+ fun(ExpTypes, AllRecords) ->
Type = erl_types:t_from_form(Form, RecDict),
- NewType = erl_types:t_solve_remote(Type, AllRecords),
+ NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords),
{NewType, []}
end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, []} | FormAcc],
contract_from_form(Left, RecDict, NewTypeAcc, NewFormAcc);
-contract_from_form([{type, _L1, bounded_fun,
+contract_from_form([{type, _L1, bounded_fun,
[{type, _L2, 'fun', [_, _]} = Form, Constr]}| Left],
RecDict, TypeAcc, FormAcc) ->
- TypeFun =
- fun(AllRecords) ->
- Constr1 = [constraint_from_form(C, RecDict, AllRecords) || C <- Constr],
+ TypeFun =
+ fun(ExpTypes, AllRecords) ->
+ Constr1 = [constraint_from_form(C, RecDict, ExpTypes, AllRecords)
+ || C <- Constr],
VarDict = insert_constraints(Constr1, dict:new()),
Type = erl_types:t_from_form(Form, RecDict, VarDict),
- NewType = erl_types:t_solve_remote(Type, AllRecords),
+ NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords),
{NewType, Constr1}
- end,
+ end,
NewTypeAcc = [TypeFun | TypeAcc],
NewFormAcc = [{Form, Constr} | FormAcc],
contract_from_form(Left, RecDict, NewTypeAcc, NewFormAcc);
-contract_from_form([], _RecDict, TypeAcc, FormAcc) ->
+contract_from_form([], _RecDict, TypeAcc, FormAcc) ->
{lists:reverse(TypeAcc), lists:reverse(FormAcc)}.
-constraint_from_form({type, _, constraint, [{atom, _, is_subtype},
- [Type1, Type2]]}, RecDict, AllRecords) ->
+constraint_from_form({type, _, constraint, [{atom, _, is_subtype},
+ [Type1, Type2]]}, RecDict,
+ ExpTypes, AllRecords) ->
T1 = erl_types:t_from_form(Type1, RecDict),
T2 = erl_types:t_from_form(Type2, RecDict),
- T3 = erl_types:t_solve_remote(T1, AllRecords),
- T4 = erl_types:t_solve_remote(T2, AllRecords),
+ T3 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords),
+ T4 = erl_types:t_solve_remote(T2, ExpTypes, AllRecords),
{subtype, T3, T4};
-constraint_from_form({type, _, constraint, [{atom,_,Name}, List]}, _RecDict, _) ->
+constraint_from_form({type, _, constraint, [{atom,_,Name}, List]}, _RecDict,
+ _ExpTypes, _AllRecords) ->
N = length(List),
throw({error, io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])}).
-%% Gets the most general domain of a list of domains of all
+%% Gets the most general domain of a list of domains of all
%% the overloaded contracts
general_domain(List) ->
@@ -419,7 +428,7 @@ get_invalid_contract_warnings_modules([Mod|Mods], CodeServer, Plt, Acc) ->
get_invalid_contract_warnings_modules([], _CodeServer, _Plt, Acc) ->
Acc.
-get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract}}|Left],
+get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract}}|Left],
Plt, RecDict, Acc) ->
case dialyzer_plt:lookup(Plt, MFA) of
none ->
@@ -447,15 +456,15 @@ get_invalid_contract_warnings_funs([{MFA, {FileLine, Contract}}|Left],
BifRet = erl_bif_types:type(M, F, A),
BifSig = erl_types:t_fun(BifArgs, BifRet),
case check_contract(Contract, BifSig) of
- {error, _} ->
+ {error, _} ->
[invalid_contract_warning(MFA, FileLine, BifSig, RecDict)
|Acc];
ok ->
- picky_contract_check(CSig, BifSig, MFA, FileLine,
+ picky_contract_check(CSig, BifSig, MFA, FileLine,
Contract, RecDict, Acc)
end;
false ->
- picky_contract_check(CSig, Sig, MFA, FileLine, Contract,
+ picky_contract_check(CSig, Sig, MFA, FileLine, Contract,
RecDict, Acc)
end
end,
@@ -479,12 +488,12 @@ picky_contract_check(CSig0, Sig0, MFA, FileLine, Contract, RecDict, Acc) ->
Sig = erl_types:t_abstract_records(Sig0, RecDict),
case erl_types:t_is_equal(CSig, Sig) of
true -> Acc;
- false ->
+ false ->
case (erl_types:t_is_none(erl_types:t_fun_range(Sig)) andalso
erl_types:t_is_unit(erl_types:t_fun_range(CSig))) of
true -> Acc;
false ->
- case extra_contract_warning(MFA, FileLine, Contract,
+ case extra_contract_warning(MFA, FileLine, Contract,
CSig, Sig, RecDict) of
no_warning -> Acc;
{warning, Warning} -> [Warning|Acc]
@@ -503,16 +512,16 @@ extra_contract_warning({M, F, A}, FileLine, Contract, CSig, Sig, RecDict) ->
ContractString = contract_to_string(Contract),
{Tag, Msg} =
case erl_types:t_is_subtype(CSig, Sig) of
- true ->
- {?WARN_CONTRACT_SUBTYPE,
+ true ->
+ {?WARN_CONTRACT_SUBTYPE,
{contract_subtype, [M, F, A, ContractString, SigString]}};
false ->
case erl_types:t_is_subtype(Sig, CSig) of
true ->
- {?WARN_CONTRACT_SUPERTYPE,
+ {?WARN_CONTRACT_SUPERTYPE,
{contract_supertype, [M, F, A, ContractString, SigString]}};
false ->
- {?WARN_CONTRACT_NOT_EQUAL,
+ {?WARN_CONTRACT_NOT_EQUAL,
{contract_diff, [M, F, A, ContractString, SigString]}}
end
end,
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 1ccfaaa52f..7137dbc036 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
%%%-------------------------------------------------------------------
%%% File : dialyzer_dataflow.erl
%%% Author : Tobias Lindahl <[email protected]>
-%%% Description :
+%%% Description :
%%%
%%% Created : 19 Apr 2005 by Tobias Lindahl <[email protected]>
%%%-------------------------------------------------------------------
@@ -30,6 +30,7 @@
-export([get_fun_types/4, get_warnings/5, format_args/3]).
+%% Data structure interfaces.
-export([state__add_warning/2, state__cleanup/1,
state__get_callgraph/1, state__get_races/1,
state__get_records/1, state__put_callgraph/2,
@@ -38,9 +39,11 @@
%% Debug and test interfaces.
-export([get_top_level_signatures/2, pp/1]).
+-export_type([state/0]).
+
-include("dialyzer.hrl").
--import(erl_types,
+-import(erl_types,
[any_none/1, t_any/0, t_atom/0, t_atom/1, t_atom_vals/1,
t_binary/0, t_boolean/0,
t_bitstr/0, t_bitstr/2, t_bitstr_concat/1, t_bitstr_match/2,
@@ -88,14 +91,15 @@
fun_tab :: dict(),
plt :: dialyzer_plt:plt(),
opaques :: [erl_types:erl_type()],
- races :: dialyzer_races:races(),
- records :: dict(),
+ races = dialyzer_races:new() :: dialyzer_races:races(),
+ records = dict:new() :: dict(),
tree_map :: dict(),
warning_mode = false :: boolean(),
warnings = [] :: [dial_warning()],
work :: {[_], [_], set()},
module :: module(),
- behaviour_api_info = [] :: [{atom(),[_]}]}).
+ behaviour_api_dict = [] ::
+ dialyzer_behaviours:behaviour_api_dict()}).
%% Exported Types
@@ -163,20 +167,20 @@ get_top_level_signatures(Code, Records) ->
error ->
Arity = cerl:fname_arity(V),
Type = t_fun(lists:duplicate(Arity,
- t_none()),
+ t_none()),
t_none()),
dict:store(Label, Type, Acc);
{ok, _} -> Acc
end
end, FunTypes, cerl:module_defs(Tree)),
dialyzer_callgraph:delete(Callgraph),
- Sigs = [{{cerl:fname_id(V), cerl:fname_arity(V)},
- dict:fetch(get_label(F), FunTypes1)}
+ Sigs = [{{cerl:fname_id(V), cerl:fname_arity(V)},
+ dict:fetch(get_label(F), FunTypes1)}
|| {V, F} <- cerl:module_defs(Tree)],
ordsets:from_list(Sigs).
get_def_plt() ->
- try
+ try
dialyzer_plt:from_file(dialyzer_plt:get_default_plt())
catch
throw:{dialyzer_error, _} -> dialyzer_plt:new()
@@ -202,7 +206,7 @@ annotate_module(Code, Plt) ->
annotate(Tree, State) ->
case cerl:subtrees(Tree) of
[] -> set_type(Tree, State);
- List ->
+ List ->
NewSubTrees = [[annotate(Subtree, State) || Subtree <- Group]
|| Group <- List],
NewTree = cerl:update_tree(Tree, NewSubTrees),
@@ -214,9 +218,9 @@ set_type(Tree, State) ->
'fun' ->
Type = state__fun_type(Tree, State),
case t_is_any(Type) of
- true ->
+ true ->
cerl:set_ann(Tree, delete_ann(typesig, cerl:get_ann(Tree)));
- false ->
+ false ->
cerl:set_ann(Tree, append_ann(typesig, Type, cerl:get_ann(Tree)))
end;
apply ->
@@ -224,10 +228,10 @@ set_type(Tree, State) ->
unknown -> Tree;
ReturnType ->
case t_is_any(ReturnType) of
- true ->
+ true ->
cerl:set_ann(Tree, delete_ann(type, cerl:get_ann(Tree)));
- false ->
- cerl:set_ann(Tree, append_ann(type, ReturnType,
+ false ->
+ cerl:set_ann(Tree, append_ann(type, ReturnType,
cerl:get_ann(Tree)))
end
end;
@@ -236,7 +240,7 @@ set_type(Tree, State) ->
end.
append_ann(Tag, Val, [X | Xs]) ->
- if tuple_size(X) >= 1, element(1, X) =:= Tag ->
+ if tuple_size(X) >= 1, element(1, X) =:= Tag ->
append_ann(Tag, Val, Xs);
true ->
[X | append_ann(Tag, Val, Xs)]
@@ -245,7 +249,7 @@ append_ann(Tag, Val, []) ->
[{Tag, Val}].
delete_ann(Tag, [X | Xs]) ->
- if tuple_size(X) >= 1, element(1, X) =:= Tag ->
+ if tuple_size(X) >= 1, element(1, X) =:= Tag ->
delete_ann(Tag, Xs);
true ->
[X | delete_ann(Tag, Xs)]
@@ -314,21 +318,21 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) ->
{Fun, NewState} ->
ArgTypes = state__get_args(Fun, NewState),
case any_none(ArgTypes) of
- true ->
- ?debug("Not handling1 ~w: ~s\n",
- [state__lookup_name(get_label(Fun), State),
+ true ->
+ ?debug("Not handling1 ~w: ~s\n",
+ [state__lookup_name(get_label(Fun), State),
t_to_string(t_product(ArgTypes))]),
analyze_loop(NewState);
- false ->
+ false ->
case state__fun_env(Fun, NewState) of
- none ->
- ?debug("Not handling2 ~w: ~s\n",
- [state__lookup_name(get_label(Fun), State),
+ none ->
+ ?debug("Not handling2 ~w: ~s\n",
+ [state__lookup_name(get_label(Fun), State),
t_to_string(t_product(ArgTypes))]),
analyze_loop(NewState);
Map ->
- ?debug("Handling fun ~p: ~s\n",
- [state__lookup_name(get_label(Fun), State),
+ ?debug("Handling fun ~p: ~s\n",
+ [state__lookup_name(get_label(Fun), State),
t_to_string(state__fun_type(Fun, NewState))]),
NewState1 = state__mark_fun_as_handled(NewState, Fun),
Vars = cerl:fun_vars(Fun),
@@ -339,19 +343,19 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) ->
RaceAnalysis = dialyzer_races:get_race_analysis(Races),
NewState3 =
case RaceDetection andalso RaceAnalysis of
- true ->
+ true ->
NewState2 = state__renew_curr_fun(
state__lookup_name(FunLabel, NewState1), FunLabel,
NewState1),
state__renew_race_list([], 0, NewState2);
false -> NewState1
end,
- {NewState4, _Map2, BodyType} =
+ {NewState4, _Map2, BodyType} =
traverse(Body, Map1, NewState3),
- ?debug("Done analyzing: ~w:~s\n",
+ ?debug("Done analyzing: ~w:~s\n",
[state__lookup_name(get_label(Fun), State),
t_to_string(t_fun(ArgTypes, BodyType))]),
- NewState5 =
+ NewState5 =
case RaceDetection andalso RaceAnalysis of
true ->
Races1 = NewState4#state.races,
@@ -382,7 +386,7 @@ traverse(Tree, Map, State) ->
%% This only happens when checking for illegal record patterns
%% so the handling is a bit rudimentary.
traverse(cerl:alias_pat(Tree), Map, State);
- apply ->
+ apply ->
handle_apply(Tree, Map, State);
binary ->
Segs = cerl:binary_segments(Tree),
@@ -416,7 +420,7 @@ traverse(Tree, Map, State) ->
%% By not including the variables in scope we can assure that we
%% will get the current function type when using the variables.
FoldFun = fun({Var, Fun}, {AccState, AccMap}) ->
- {NewAccState, NewAccMap0, FunType} =
+ {NewAccState, NewAccMap0, FunType} =
traverse(Fun, AccMap, AccState),
NewAccMap = enter_type(Var, FunType, NewAccMap0),
{NewAccState, NewAccMap}
@@ -428,7 +432,7 @@ traverse(Tree, Map, State) ->
case cerl:unfold_literal(Tree) of
Tree ->
Type = literal_type(Tree),
- NewType =
+ NewType =
case erl_types:t_opaque_match_atom(Type, State#state.opaques) of
[Opaque] -> Opaque;
_ -> Type
@@ -446,8 +450,8 @@ traverse(Tree, Map, State) ->
bs_init_writable -> t_from_term(<<>>);
Other -> erlang:error({'Unsupported primop', Other})
end,
- {State, Map, Type};
- 'receive' ->
+ {State, Map, Type};
+ 'receive' ->
handle_receive(Tree, Map, State);
seq ->
Arg = cerl:seq_arg(Tree),
@@ -457,13 +461,13 @@ traverse(Tree, Map, State) ->
true ->
SMA;
false ->
- State2 =
+ State2 =
case (t_is_any(ArgType) orelse t_is_simple(ArgType)
orelse is_call_to_send(Arg)) of
true -> % do not warn in these cases
State1;
false ->
- state__add_warning(State1, ?WARN_UNMATCHED_RETURN, Arg,
+ state__add_warning(State1, ?WARN_UNMATCHED_RETURN, Arg,
{unmatched_return,
[format_type(ArgType, State1)]})
end,
@@ -481,12 +485,12 @@ traverse(Tree, Map, State) ->
var ->
?debug("Looking up unknown variable: ~p\n", [Tree]),
case state__lookup_type_for_rec_var(Tree, State) of
- error ->
+ error ->
LType = lookup_type(Tree, Map),
Opaques = State#state.opaques,
case t_opaque_match_record(LType, Opaques) of
[Opaque] -> {State, Map, Opaque};
- _ ->
+ _ ->
case t_opaque_match_atom(LType, Opaques) of
[Opaque] -> {State, Map, Opaque};
_ -> {State, Map, LType}
@@ -506,7 +510,7 @@ traverse_list([Tree|Tail], Map, State, Acc) ->
traverse_list(Tail, Map1, State1, [Type|Acc]);
traverse_list([], Map, State, Acc) ->
{State, Map, lists:reverse(Acc)}.
-
+
%%________________________________________
%%
%% Special instructions
@@ -518,7 +522,7 @@ handle_apply(Tree, Map, State) ->
{State1, Map1, ArgTypes} = traverse_list(Args, Map, State),
{State2, Map2, OpType} = traverse(Op, Map1, State1),
case any_none(ArgTypes) of
- true ->
+ true ->
{State2, Map2, t_none()};
false ->
{CallSitesKnown, FunList} =
@@ -533,7 +537,7 @@ handle_apply(Tree, Map, State) ->
OpType1 = t_inf(OpType, t_fun(Arity, t_any())),
case t_is_none(OpType1) of
true ->
- Msg = {fun_app_no_fun,
+ Msg = {fun_app_no_fun,
[format_cerl(Op), format_type(OpType, State2), Arity]},
State3 = state__add_warning(State2, ?WARN_FAILING_CALL,
Tree, Msg),
@@ -541,7 +545,7 @@ handle_apply(Tree, Map, State) ->
false ->
NewArgs = t_inf_lists(ArgTypes, t_fun_args(OpType1)),
case any_none(NewArgs) of
- true ->
+ true ->
Msg = {fun_app_args,
[format_args(Args, ArgTypes, State),
format_type(OpType, State)]},
@@ -554,7 +558,7 @@ handle_apply(Tree, Map, State) ->
end
end;
true ->
- FunInfoList = [{local, state__fun_info(Fun, State)}
+ FunInfoList = [{local, state__fun_info(Fun, State)}
|| Fun <- FunList],
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map2, Tree, State1)
end
@@ -562,7 +566,7 @@ handle_apply(Tree, Map, State) ->
handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State) ->
None = t_none(),
- handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State,
+ handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State,
[None || _ <- ArgTypes], None).
handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State,
@@ -577,7 +581,7 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
Any = t_any(),
AnyArgs = [Any || _ <- Args],
GenSig = {AnyArgs, fun(_) -> t_any() end},
- {CArgs, CRange} =
+ {CArgs, CRange} =
case Contr of
{value, #contract{args = As} = C} ->
{As, fun(FunArgs) ->
@@ -627,9 +631,9 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
end
end,
ArgModeMask = [case lists:member(Arg, Opaques) of
- true -> opaque;
- false -> structured
- end || Arg <- ArgTypes],
+ true -> opaque;
+ false -> structured
+ end || Arg <- ArgTypes],
NewArgsSig = t_inf_lists_masked(SigArgs, ArgTypes, ArgModeMask),
NewArgsContract = t_inf_lists_masked(CArgs, ArgTypes, ArgModeMask),
NewArgsBif = t_inf_lists_masked(BifArgs, ArgTypes, ArgModeMask),
@@ -637,7 +641,7 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
NewArgTypes = t_inf_lists_masked(NewArgTypes0, NewArgsBif, ArgModeMask),
BifRet = BifRange(NewArgTypes),
{TmpArgTypes, TmpArgsContract} =
- case (TypeOfApply == remote) andalso (not IsBIF) of
+ case (TypeOfApply =:= remote) andalso (not IsBIF) of
true ->
List1 = lists:zip(CArgs, NewArgTypes),
List2 = lists:zip(CArgs, NewArgsContract),
@@ -648,19 +652,22 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
false -> {NewArgTypes, NewArgsContract}
end,
ContrRet = CRange(TmpArgTypes),
- RetMode = case t_contains_opaque(ContrRet) orelse t_contains_opaque(BifRet) of
- true -> opaque;
- false -> structured
- end,
- RetWithoutLocal = t_inf(t_inf(ContrRet, BifRet, RetMode), SigRange, RetMode),
+ RetMode =
+ case t_contains_opaque(ContrRet) orelse t_contains_opaque(BifRet) of
+ true -> opaque;
+ false -> structured
+ end,
+ RetWithoutContr = t_inf(SigRange, BifRet, RetMode),
+ RetWithoutLocal = t_inf(ContrRet, RetWithoutContr, RetMode),
?debug("--------------------------------------------------------\n", []),
?debug("Fun: ~p\n", [Fun]),
?debug("Args: ~s\n", [erl_types:t_to_string(t_product(ArgTypes))]),
?debug("NewArgsSig: ~s\n", [erl_types:t_to_string(t_product(NewArgsSig))]),
- ?debug("NewArgsContract: ~s\n",
+ ?debug("NewArgsContract: ~s\n",
[erl_types:t_to_string(t_product(NewArgsContract))]),
?debug("NewArgsBif: ~s\n", [erl_types:t_to_string(t_product(NewArgsBif))]),
?debug("NewArgTypes: ~s\n", [erl_types:t_to_string(t_product(NewArgTypes))]),
+ ?debug("RetWithoutContr: ~s\n",[erl_types:t_to_string(RetWithoutContr)]),
?debug("RetWithoutLocal: ~s\n", [erl_types:t_to_string(RetWithoutLocal)]),
?debug("BifRet: ~s\n", [erl_types:t_to_string(BifRange(NewArgTypes))]),
?debug("ContrRet: ~s\n", [erl_types:t_to_string(CRange(TmpArgTypes))]),
@@ -677,11 +684,11 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
%% respective callback module's function.
Module = State#state.module,
- BehApiInfo = State#state.behaviour_api_info,
+ BehApiDict = State#state.behaviour_api_dict,
{RealFun, RealArgTypes, RealArgs} =
case dialyzer_behaviours:translate_behaviour_api_call(Fun, ArgTypes,
Args, Module,
- BehApiInfo) of
+ BehApiDict) of
plain_call -> {Fun, ArgTypes, Args};
BehaviourAPI -> BehaviourAPI
end,
@@ -695,22 +702,39 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
State2 =
case FailedConj andalso not (IsFailBif orelse IsFailSig) of
true ->
- FailedSig = any_none(NewArgsSig),
- FailedContract = any_none([CRange(TmpArgsContract)|NewArgsContract]),
- FailedBif = any_none([BifRange(NewArgsBif)|NewArgsBif]),
- InfSig = t_inf(t_fun(SigArgs, SigRange),
- t_fun(BifArgs, BifRange(BifArgs))),
- FailReason = apply_fail_reason(FailedSig, FailedBif, FailedContract),
- Msg = get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes, InfSig,
- Contr, CArgs, State1, FailReason),
- WarnType = case Msg of
- {call, _} -> ?WARN_FAILING_CALL;
- {apply, _} -> ?WARN_FAILING_CALL;
- {call_with_opaque, _} -> ?WARN_OPAQUE;
- {call_without_opaque, _} -> ?WARN_OPAQUE;
- {opaque_type_test, _} -> ?WARN_OPAQUE
- end,
- state__add_warning(State1, WarnType, Tree, Msg);
+ case t_is_none(RetWithoutLocal) andalso
+ not t_is_none(RetWithoutContr) andalso
+ not any_none(NewArgTypes) of
+ true ->
+ {value, C1} = Contr,
+ Contract = dialyzer_contracts:contract_to_string(C1),
+ {M1, F1, A1} = state__lookup_name(Fun, State),
+ ArgStrings = format_args(Args, ArgTypes, State),
+ CRet = erl_types:t_to_string(RetWithoutContr),
+ %% This Msg will be post_processed by dialyzer_succ_typings
+ Msg =
+ {contract_range, [Contract, M1, F1, A1, ArgStrings, CRet]},
+ state__add_warning(State1, ?WARN_CONTRACT_RANGE, Tree, Msg);
+ false ->
+ FailedSig = any_none(NewArgsSig),
+ FailedContract =
+ any_none([CRange(TmpArgsContract)|NewArgsContract]),
+ FailedBif = any_none([BifRange(NewArgsBif)|NewArgsBif]),
+ InfSig = t_inf(t_fun(SigArgs, SigRange),
+ t_fun(BifArgs, BifRange(BifArgs))),
+ FailReason =
+ apply_fail_reason(FailedSig, FailedBif, FailedContract),
+ Msg = get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes, InfSig,
+ Contr, CArgs, State1, FailReason),
+ WarnType = case Msg of
+ {call, _} -> ?WARN_FAILING_CALL;
+ {apply, _} -> ?WARN_FAILING_CALL;
+ {call_with_opaque, _} -> ?WARN_OPAQUE;
+ {call_without_opaque, _} -> ?WARN_OPAQUE;
+ {opaque_type_test, _} -> ?WARN_OPAQUE
+ end,
+ state__add_warning(State1, WarnType, Tree, Msg)
+ end;
false -> State1
end,
State3 =
@@ -725,15 +749,15 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
remote ->
add_bif_warnings(Fun, NewArgTypes, Tree, State2)
end,
- NewAccArgTypes =
+ NewAccArgTypes =
case FailedConj of
true -> AccArgTypes;
false -> [t_sup(X, Y) || {X, Y} <- lists:zip(NewArgTypes, AccArgTypes)]
end,
NewAccRet = t_sup(AccRet, t_inf(RetWithoutLocal, LocalRet, opaque)),
- handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
+ handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
State3, NewAccArgTypes, NewAccRet);
-handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
+handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
AccArgTypes, AccRet) ->
NewMap = enter_type_lists(Args, AccArgTypes, Map),
{State, NewMap, AccRet}.
@@ -745,13 +769,13 @@ apply_fail_reason(FailedSig, FailedBif, FailedContract) ->
true -> both
end.
-get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes,
+get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes,
Sig, Contract, ContrArgs, State, FailReason) ->
ArgStrings = format_args(Args, ArgTypes, State),
ContractInfo =
case Contract of
{value, #contract{} = C} ->
- {dialyzer_contracts:is_overloaded(C),
+ {dialyzer_contracts:is_overloaded(C),
dialyzer_contracts:contract_to_string(C)};
none -> {false, none}
end,
@@ -765,7 +789,7 @@ get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes,
{M, F, _A} ->
case is_opaque_type_test_problem(Fun, NewArgTypes, State) of
true ->
- [Opaque] = NewArgTypes,
+ [Opaque] = NewArgTypes,
{opaque_type_test, [atom_to_list(F), erl_types:t_to_string(Opaque)]};
false ->
SigArgs = t_fun_args(Sig),
@@ -789,7 +813,7 @@ get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes,
{call_without_opaque, [M, F, ArgStrings, ExpectedTriples]};
false -> %% there is a structured term clash in some argument
{call, [M, F, ArgStrings,
- ArgNs, FailReason,
+ ArgNs, FailReason,
format_sig_args(Sig, State),
format_type(t_fun_range(Sig), State),
ContractInfo]}
@@ -797,8 +821,8 @@ get_apply_fail_msg(Fun, Args, ArgTypes, NewArgTypes,
end
end;
Label when is_integer(Label) ->
- {apply, [ArgStrings,
- ArgNs, FailReason,
+ {apply, [ArgStrings,
+ ArgNs, FailReason,
format_sig_args(Sig, State),
format_type(t_fun_range(Sig), State),
ContractInfo]}
@@ -826,7 +850,7 @@ is_opaque_type_test_problem(Fun, ArgTypes, State) ->
FN =:= is_number; FN =:= is_pid; FN =:= is_port;
FN =:= is_reference; FN =:= is_tuple ->
[Type] = ArgTypes,
- erl_types:t_is_opaque(Type) andalso
+ erl_types:t_is_opaque(Type) andalso
not lists:member(Type, State#state.opaques);
_ -> false
end.
@@ -1043,7 +1067,7 @@ handle_cons(Tree, Map, State) ->
Tl = cerl:cons_tl(Tree),
{State1, Map1, HdType} = traverse(Hd, Map, State),
{State2, Map2, TlType} = traverse(Tl, Map1, State1),
- State3 =
+ State3 =
case t_is_none(t_inf(TlType, t_list())) of
true ->
Msg = {improper_list_constr, [format_type(TlType, State2)]},
@@ -1088,7 +1112,7 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) ->
case cerl:is_literal(Mod) andalso
cerl:concrete(Mod) =:= ets andalso
cerl:is_literal(Name) andalso
- cerl:concrete(Name) =:= new of
+ cerl:concrete(Name) =:= new of
true ->
NewTable = dialyzer_races:get_new_table(State1#state.races),
renew_public_tables(Vars, NewTable,
@@ -1112,7 +1136,7 @@ handle_module(Tree, Map, State) ->
%% By not including the variables in scope we can assure that we
%% will get the current function type when using the variables.
Defs = cerl:module_defs(Tree),
- PartFun = fun({_Var, Fun}) ->
+ PartFun = fun({_Var, Fun}) ->
state__is_escaping(get_label(Fun), State)
end,
{Defs1, Defs2} = lists:partition(PartFun, Defs),
@@ -1143,12 +1167,12 @@ handle_receive(Tree, Map,
RaceListSize + 1, State);
false -> State
end,
- {MapList, State2, ReceiveType} =
+ {MapList, State2, ReceiveType} =
handle_clauses(Clauses, ?no_arg, t_any(), t_any(), State1, [], Map,
[], []),
Map1 = join_maps(MapList, Map),
{State3, Map2, TimeoutType} = traverse(Timeout, Map1, State2),
- case (t_is_atom(TimeoutType) andalso
+ case (t_is_atom(TimeoutType) andalso
(t_atom_vals(TimeoutType) =:= ['infinity'])) of
true ->
{State3, Map2, ReceiveType};
@@ -1168,17 +1192,17 @@ handle_try(Tree, Map, State) ->
Vars = cerl:try_vars(Tree),
Body = cerl:try_body(Tree),
Handler = cerl:try_handler(Tree),
- {State1, Map1, ArgType} = traverse(Arg, Map, State),
+ {State1, Map1, ArgType} = traverse(Arg, Map, State),
Map2 = mark_as_fresh(Vars, Map1),
{SuccState, SuccMap, SuccType} =
case bind_pat_vars(Vars, t_to_tlist(ArgType), [], Map2, State1) of
{error, _, _, _, _} ->
{State1, map__new(), t_none()};
{SuccMap1, VarTypes} ->
- %% Try to bind the argument. Will only succeed if
+ %% Try to bind the argument. Will only succeed if
%% it is a simple structured term.
SuccMap2 =
- case bind_pat_vars_reverse([Arg], [t_product(VarTypes)], [],
+ case bind_pat_vars_reverse([Arg], [t_product(VarTypes)], [],
SuccMap1, State1) of
{error, _, _, _, _} -> SuccMap1;
{SM, _} -> SM
@@ -1212,10 +1236,10 @@ handle_tuple(Tree, Map, State) ->
RecFields = t_tuple_args(RecStruct),
case bind_pat_vars(Elements, RecFields, [], Map1, State1) of
{error, _, ErrorPat, ErrorType, _} ->
- Msg = {record_constr,
+ Msg = {record_constr,
[TagVal, format_patterns(ErrorPat),
format_type(ErrorType, State1)]},
- State2 = state__add_warning(State1, ?WARN_MATCHING,
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
Tree, Msg),
{State2, Map1, t_none()};
{Map2, _ETypes} ->
@@ -1224,26 +1248,24 @@ handle_tuple(Tree, Map, State) ->
_ ->
case state__lookup_record(TagVal, length(Left), State1) of
error -> {State1, Map1, TupleType};
- {ok, Prototype} ->
- %% io:format("In handle_tuple:\n Prototype = ~p\n", [Prototype]),
- InfTupleType = t_inf(Prototype, TupleType),
- %% io:format(" TupleType = ~p,\n Inf = ~p\n", [TupleType, InfTupleType]),
+ {ok, RecType} ->
+ InfTupleType = t_inf(RecType, TupleType),
case t_is_none(InfTupleType) of
true ->
- Msg = {record_constr,
- [format_type(TupleType, State1), TagVal]},
- State2 = state__add_warning(State1, ?WARN_MATCHING,
+ RecC = format_type(TupleType, State1),
+ FieldDiffs = format_field_diffs(TupleType, State1),
+ Msg = {record_constr, [RecC, FieldDiffs]},
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
Tree, Msg),
{State2, Map1, t_none()};
false ->
- case bind_pat_vars(Elements, t_tuple_args(Prototype),
+ case bind_pat_vars(Elements, t_tuple_args(RecType),
[], Map1, State1) of
{error, bind, ErrorPat, ErrorType, _} ->
- %% io:format("error\n", []),
- Msg = {record_constr,
+ Msg = {record_constr,
[TagVal, format_patterns(ErrorPat),
format_type(ErrorType, State1)]},
- State2 = state__add_warning(State1, ?WARN_MATCHING,
+ State2 = state__add_warning(State1, ?WARN_MATCHING,
Tree, Msg),
{State2, Map1, t_none()};
{Map2, ETypes} ->
@@ -1305,7 +1327,7 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType,
handle_clauses([], _Arg, _ArgType, _OrigArgType,
#state{callgraph = Callgraph, races = Races} = State,
CaseTypes, _MapIn, Acc, ClauseAcc) ->
- State1 =
+ State1 =
case dialyzer_callgraph:get_race_detection(Callgraph) andalso
dialyzer_races:get_race_analysis(Races) of
true ->
@@ -1313,7 +1335,7 @@ handle_clauses([], _Arg, _ArgType, _OrigArgType,
[dialyzer_races:end_case_new(ClauseAcc)|
dialyzer_races:get_race_list(Races)],
dialyzer_races:get_race_list_size(Races) + 1, State);
- false -> State
+ false -> State
end,
{lists:reverse(Acc), State1, t_sup(CaseTypes)}.
@@ -1324,7 +1346,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
Body = cerl:clause_body(C),
RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph),
RaceAnalysis = dialyzer_races:get_race_analysis(Races),
- State1 =
+ State1 =
case RaceDetection andalso RaceAnalysis of
true ->
state__renew_fun_args(Pats, State);
@@ -1339,7 +1361,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
true ->
{error, bind, Pats, ArgType0, ArgType0};
false ->
- ArgTypes =
+ ArgTypes =
case t_is_any(ArgType0) of
true -> [ArgType0 || _ <- Pats];
false -> t_to_tlist(ArgType0)
@@ -1347,19 +1369,20 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
bind_pat_vars(Pats, ArgTypes, [], Map1, State1)
end,
case BindRes of
- {error, BindOrOpaque, NewPats, Type, OpaqueTerm} ->
- ?debug("Failed binding pattern: ~s\nto ~s\n",
+ {error, ErrorType, NewPats, Type, OpaqueTerm} ->
+ ?debug("Failed binding pattern: ~s\nto ~s\n",
[cerl_prettypr:format(C), format_type(ArgType0, State1)]),
case state__warning_mode(State1) of
false ->
{State1, Map, t_none(), ArgType0};
true ->
PatString =
- case BindOrOpaque of
+ case ErrorType of
bind -> format_patterns(Pats);
+ record -> format_patterns(Pats);
opaque -> format_patterns(NewPats)
end,
- {Msg, Force} =
+ {Msg, Force} =
case t_is_none(ArgType0) of
true ->
PatTypes = [PatString, format_type(OrigArgType, State1)],
@@ -1375,13 +1398,13 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
{_, _} -> {{pattern_match_cov, PatTypes}, false}
end;
false ->
- %% Try to find out if this is a default clause in a list
+ %% Try to find out if this is a default clause in a list
%% comprehension and supress this. A real Hack(tm)
Force0 =
case is_compiler_generated(cerl:get_ann(C)) of
true ->
case Pats of
- [Pat] ->
+ [Pat] ->
case cerl:is_c_cons(Pat) of
true ->
not (cerl:is_c_var(cerl:cons_hd(Pat)) andalso
@@ -1396,13 +1419,15 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
false ->
true
end,
- PatTypes = case BindOrOpaque of
+ PatTypes = case ErrorType of
bind -> [PatString, format_type(ArgType0, State1)];
- opaque -> [PatString, format_type(Type, State1),
+ record -> [PatString, format_type(Type, State1)];
+ opaque -> [PatString, format_type(Type, State1),
format_type(OpaqueTerm, State1)]
- end,
- FailedMsg = case BindOrOpaque of
+ end,
+ FailedMsg = case ErrorType of
bind -> {pattern_match, PatTypes};
+ record -> {record_match, PatTypes};
opaque -> {opaque_match, PatTypes}
end,
{FailedMsg, Force0}
@@ -1410,6 +1435,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
WarnType = case Msg of
{opaque_match, _} -> ?WARN_OPAQUE;
{pattern_match, _} -> ?WARN_MATCHING;
+ {record_match, _} -> ?WARN_MATCHING;
{pattern_match_cov, _} -> ?WARN_MATCHING
end,
{state__add_warning(State1, WarnType, C, Msg, Force),
@@ -1420,9 +1446,9 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
case Arg =:= ?no_arg of
true -> Map2;
false ->
- %% Try to bind the argument. Will only succeed if
+ %% Try to bind the argument. Will only succeed if
%% it is a simple structured term.
- case bind_pat_vars_reverse([Arg], [t_product(PatTypes)],
+ case bind_pat_vars_reverse([Arg], [t_product(PatTypes)],
[], Map2, State1) of
{error, _, _, _, _} -> Map2;
{NewMap, _} -> NewMap
@@ -1436,11 +1462,11 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
t_subtract(t_product(t_to_tlist(ArgType0)), GenType)
end,
case bind_guard(Guard, Map3, State1) of
- {error, Reason} ->
- ?debug("Failed guard: ~s\n",
+ {error, Reason} ->
+ ?debug("Failed guard: ~s\n",
[cerl_prettypr:format(C, [{hook, cerl_typean:pp_hook()}])]),
PatString = format_patterns(Pats),
- DefaultMsg =
+ DefaultMsg =
case Pats =:= [] of
true -> {guard_fail, []};
false ->
@@ -1454,6 +1480,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
false ->
WarnType = case Msg of
{guard_fail, _} -> ?WARN_MATCHING;
+ {neg_guard_fail, _} -> ?WARN_MATCHING;
{opaque_guard, _} -> ?WARN_OPAQUE
end,
state__add_warning(State1, WarnType, FailGuard, Msg);
@@ -1470,7 +1497,7 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map,
bind_subst(Arg, Pats, Map) ->
case cerl:type(Arg) of
- values ->
+ values ->
bind_subst_list(cerl:values_es(Arg), Pats, Map);
var ->
[Pat] = Pats,
@@ -1499,17 +1526,21 @@ bind_subst_list([], [], Map) ->
%%
bind_pat_vars(Pats, Types, Acc, Map, State) ->
- try
+ try
bind_pat_vars(Pats, Types, Acc, Map, State, false)
- catch
- throw:Error -> Error % Error = {error, bind | opaque, ErrorPats, ErrorType}
+ catch
+ throw:Error ->
+ %% Error = {error, bind | opaque | record, ErrorPats, ErrorType}
+ Error
end.
bind_pat_vars_reverse(Pats, Types, Acc, Map, State) ->
- try
+ try
bind_pat_vars(Pats, Types, Acc, Map, State, true)
- catch
- throw:Error -> Error % Error = {error, bind | opaque, ErrorPats, ErrorType}
+ catch
+ throw:Error ->
+ %% Error = {error, bind | opaque | record, ErrorPats, ErrorType}
+ Error
end.
bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
@@ -1520,7 +1551,7 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
AliasPat = cerl:alias_pat(Pat),
Var = cerl:alias_var(Pat),
Map1 = enter_subst(Var, AliasPat, Map),
- {Map2, [PatType]} = bind_pat_vars([AliasPat], [Type], [],
+ {Map2, [PatType]} = bind_pat_vars([AliasPat], [Type], [],
Map1, State, Rev),
{enter_type(Var, PatType, Map2), PatType};
binary ->
@@ -1541,18 +1572,18 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
cons ->
Cons = t_inf(Type, t_cons()),
case t_is_none(Cons) of
- true ->
+ true ->
bind_opaque_pats(t_cons(), Type, Pat, Map, State, Rev);
false ->
- {Map1, [HdType, TlType]} =
+ {Map1, [HdType, TlType]} =
bind_pat_vars([cerl:cons_hd(Pat), cerl:cons_tl(Pat)],
- [t_cons_hd(Cons), t_cons_tl(Cons)],
+ [t_cons_hd(Cons), t_cons_tl(Cons)],
[], Map, State, Rev),
{Map1, t_cons(HdType, TlType)}
end;
literal ->
Literal = literal_type(Pat),
- LiteralOrOpaque =
+ LiteralOrOpaque =
case t_opaque_match_atom(Literal, State#state.opaques) of
[Opaque] -> Opaque;
_ -> Literal
@@ -1564,18 +1595,21 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
end;
tuple ->
Es = cerl:tuple_es(Pat),
- Prototype =
+ {TypedRecord, Prototype} =
case Es of
- [] -> t_tuple([]);
+ [] -> {false, t_tuple([])};
[Tag|Left] ->
case cerl:is_c_atom(Tag) of
true ->
TagAtom = cerl:atom_val(Tag),
case state__lookup_record(TagAtom, length(Left), State) of
- error -> t_tuple(length(Es));
- {ok, Record} -> Record
+ error -> {false, t_tuple(length(Es))};
+ {ok, Record} ->
+ [_Head|AnyTail] = [t_any() || _ <- Es],
+ UntypedRecord = t_tuple([t_atom(TagAtom)|AnyTail]),
+ {not erl_types:t_is_equal(Record, UntypedRecord), Record}
end;
- false -> t_tuple(length(Es))
+ false -> {false, t_tuple(length(Es))}
end
end,
Tuple = t_inf(Prototype, Type),
@@ -1585,10 +1619,10 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
false ->
SubTuples = t_tuple_subtypes(Tuple),
%% Need to call the top function to get the try-catch wrapper
- Results =
+ Results =
case Rev of
true ->
- [bind_pat_vars_reverse(Es, t_tuple_args(SubTuple), [],
+ [bind_pat_vars_reverse(Es, t_tuple_args(SubTuple), [],
Map, State)
|| SubTuple <- SubTuples];
false ->
@@ -1600,7 +1634,11 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
bind_error([Pat], Tuple, Opaque, opaque);
false ->
case [M || {M, _} <- Results, M =/= error] of
- [] -> bind_error([Pat], Tuple, t_none(), bind);
+ [] ->
+ case TypedRecord of
+ true -> bind_error([Pat], Tuple, Prototype, record);
+ false -> bind_error([Pat], Tuple, t_none(), bind)
+ end;
Maps ->
Map1 = join_maps(Maps, Map),
TupleType = t_sup([t_tuple(EsTypes)
@@ -1632,12 +1670,12 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
end,
%% Must do inf when binding args to pats. Vars in pats are fresh.
VarType2 = t_inf(VarType1, Type),
- VarType3 =
+ VarType3 =
case Opaques =/= [] of
true ->
case t_opaque_match_record(VarType2, Opaques) of
[OpaqueRec] -> OpaqueRec;
- _ ->
+ _ ->
case t_opaque_match_atom(VarType2, Opaques) of
[OpaqueAtom] -> OpaqueAtom;
_ -> VarType2
@@ -1648,9 +1686,9 @@ bind_pat_vars([Pat|PatLeft], [Type|TypeLeft], Acc, Map, State, Rev) ->
case t_is_none(VarType3) of
true ->
case t_find_opaque_mismatch(VarType1, Type) of
- {ok, T1, T2} ->
+ {ok, T1, T2} ->
bind_error([Pat], T1, T2, opaque);
- error ->
+ error ->
bind_error([Pat], Type, t_none(), bind)
end;
false ->
@@ -1745,17 +1783,17 @@ bind_opaque_pats(GenType, Type, Pat, Map, State, Rev) ->
bind_guard(Guard, Map, State) ->
try bind_guard(Guard, Map, dict:new(), pos, State) of
- {Map1, _Type} -> Map1
+ {Map1, _Type} -> Map1
catch
throw:{fail, Warning} -> {error, Warning};
throw:{fatal_fail, Warning} -> {error, Warning}
end.
bind_guard(Guard, Map, Env, Eval, State) ->
- ?debug("Handling ~w guard: ~s\n",
+ ?debug("Handling ~w guard: ~s\n",
[Eval, cerl_prettypr:format(Guard, [{noann, true}])]),
case cerl:type(Guard) of
- binary ->
+ binary ->
{Map, t_binary()};
'case' ->
Arg = cerl:case_arg(Guard),
@@ -1793,10 +1831,10 @@ bind_guard(Guard, Map, Env, Eval, State) ->
var ->
?debug("Looking for var(~w)...", [cerl_trees:get_label(Guard)]),
case dict:find(get_label(Guard), Env) of
- error ->
+ error ->
?debug("Did not find it\n", []),
Type = lookup_type(Guard, Map),
- Constr =
+ Constr =
case Eval of
pos -> t_atom(true);
neg -> t_atom(false);
@@ -1804,7 +1842,7 @@ bind_guard(Guard, Map, Env, Eval, State) ->
end,
Inf = t_inf(Constr, Type),
{enter_type(Guard, Inf, Map), Inf};
- {ok, Tree} ->
+ {ok, Tree} ->
?debug("Found it\n", []),
{Map1, Type} = bind_guard(Tree, Map, Env, Eval, State),
{enter_type(Guard, Type, Map1), Type}
@@ -1827,7 +1865,7 @@ handle_guard_call(Guard, Map, Env, Eval, State) ->
handle_guard_type_test(Guard, F, Map, Env, Eval, State);
{erlang, is_function, 2} ->
handle_guard_is_function(Guard, Map, Env, Eval, State);
- MFA when (MFA =:= {erlang, internal_is_record, 3}) or
+ MFA when (MFA =:= {erlang, internal_is_record, 3}) or
(MFA =:= {erlang, is_record, 3}) ->
handle_guard_is_record(Guard, Map, Env, Eval, State);
{erlang, '=:=', 2} ->
@@ -1840,7 +1878,7 @@ handle_guard_call(Guard, Map, Env, Eval, State) ->
handle_guard_or(Guard, Map, Env, Eval, State);
{erlang, 'not', 1} ->
handle_guard_not(Guard, Map, Env, Eval, State);
- {erlang, Comp, 2} when Comp =:= '<'; Comp =:= '=<';
+ {erlang, Comp, 2} when Comp =:= '<'; Comp =:= '=<';
Comp =:= '>'; Comp =:= '>=' ->
handle_guard_comp(Guard, Comp, Map, Env, Eval, State);
_ ->
@@ -1866,8 +1904,8 @@ handle_guard_gen_fun({M, F, A}, Guard, Map, Env, Eval, State) ->
true ->
%% Is this an error-bif?
case t_is_none(erl_bif_types:type(M, F, A)) of
- true -> signal_guard_fail(Guard, As, State);
- false -> signal_guard_fatal_fail(Guard, As, State)
+ true -> signal_guard_fail(Eval, Guard, As, State);
+ false -> signal_guard_fatal_fail(Eval, Guard, As, State)
end;
false ->
BifArgs = case erl_bif_types:arg_types(M, F, A) of
@@ -1875,7 +1913,7 @@ handle_guard_gen_fun({M, F, A}, Guard, Map, Env, Eval, State) ->
List -> List
end,
Map2 = enter_type_lists(Args, t_inf_lists(BifArgs, As0, Mode), Map1),
- Ret =
+ Ret =
case Eval of
pos -> t_inf(t_atom(true), BifRet);
neg -> t_inf(t_atom(false), BifRet);
@@ -1884,7 +1922,7 @@ handle_guard_gen_fun({M, F, A}, Guard, Map, Env, Eval, State) ->
case t_is_none(Ret) of
true ->
case Eval =:= pos of
- true -> signal_guard_fail(Guard, As, State);
+ true -> signal_guard_fail(Eval, Guard, As, State);
false -> throw({fail, none})
end;
false -> {Map2, Ret}
@@ -1892,14 +1930,14 @@ handle_guard_gen_fun({M, F, A}, Guard, Map, Env, Eval, State) ->
end.
handle_guard_type_test(Guard, F, Map, Env, Eval, State) ->
- [Arg] = cerl:call_args(Guard),
+ [Arg] = cerl:call_args(Guard),
{Map1, ArgType} = bind_guard(Arg, Map, Env, dont_know, State),
case bind_type_test(Eval, F, ArgType, State) of
- error ->
+ error ->
?debug("Type test: ~w failed\n", [F]),
- signal_guard_fail(Guard, [ArgType], State);
- {ok, NewArgType, Ret} ->
- ?debug("Type test: ~w succeeded, NewType: ~s, Ret: ~s\n",
+ signal_guard_fail(Eval, Guard, [ArgType], State);
+ {ok, NewArgType, Ret} ->
+ ?debug("Type test: ~w succeeded, NewType: ~s, Ret: ~s\n",
[F, t_to_string(NewArgType), t_to_string(Ret)]),
{enter_type(Arg, NewArgType, Map1), Ret}
end.
@@ -1930,13 +1968,13 @@ bind_type_test(Eval, TypeTest, ArgType, State) ->
end;
neg ->
case Mode of
- opaque ->
+ opaque ->
Struct = erl_types:t_opaque_structure(ArgType),
case t_is_none(t_subtract(Struct, Type)) of
true -> error;
false -> {ok, ArgType, t_atom(false)}
end;
- structured ->
+ structured ->
Sub = t_subtract(ArgType, Type),
case t_is_none(Sub) of
true -> error;
@@ -1960,21 +1998,22 @@ handle_guard_comp(Guard, Comp, Map, Env, Eval, State) ->
true when Eval =:= pos -> {Map, t_atom(true)};
true when Eval =:= dont_know -> {Map, t_atom(true)};
true when Eval =:= neg -> {Map, t_atom(true)};
- false when Eval =:= pos -> signal_guard_fail(Guard, ArgTypes, State);
+ false when Eval =:= pos ->
+ signal_guard_fail(Eval, Guard, ArgTypes, State);
false when Eval =:= dont_know -> {Map, t_atom(false)};
false when Eval =:= neg -> {Map, t_atom(false)}
end;
{literal, var} when IsInt1 andalso IsInt2 andalso (Eval =:= pos) ->
case bind_comp_literal_var(Arg1, Arg2, Type2, Comp, Map1) of
- error -> signal_guard_fail(Guard, ArgTypes, State);
+ error -> signal_guard_fail(Eval, Guard, ArgTypes, State);
{ok, NewMap} -> {NewMap, t_atom(true)}
end;
{var, literal} when IsInt1 andalso IsInt2 andalso (Eval =:= pos) ->
case bind_comp_literal_var(Arg2, Arg1, Type1, invert_comp(Comp), Map1) of
- error -> signal_guard_fail(Guard, ArgTypes, State);
+ error -> signal_guard_fail(Eval, Guard, ArgTypes, State);
{ok, NewMap} -> {NewMap, t_atom(true)}
end;
- {_, _} ->
+ {_, _} ->
handle_guard_gen_fun({erlang, Comp, 2}, Guard, Map, Env, Eval, State)
end.
@@ -2011,7 +2050,7 @@ handle_guard_is_function(Guard, Map, Env, Eval, State) ->
[FunType0, ArityType0] = ArgTypes0,
ArityType = t_inf(ArityType0, t_integer()),
case t_is_none(ArityType) of
- true -> signal_guard_fail(Guard, ArgTypes0, State);
+ true -> signal_guard_fail(Eval, Guard, ArgTypes0, State);
false ->
FunTypeConstr =
case t_number_vals(ArityType) of
@@ -2021,9 +2060,9 @@ handle_guard_is_function(Guard, Map, Env, Eval, State) ->
end,
FunType = t_inf(FunType0, FunTypeConstr),
case t_is_none(FunType) of
- true ->
+ true ->
case Eval of
- pos -> signal_guard_fail(Guard, ArgTypes0, State);
+ pos -> signal_guard_fail(Eval, Guard, ArgTypes0, State);
neg -> {Map1, t_atom(false)};
dont_know -> {Map1, t_atom(false)}
end;
@@ -2057,16 +2096,16 @@ handle_guard_is_record(Guard, Map, Env, Eval, State) ->
end,
Type = t_inf(NewTupleType, RecType, Mode),
case t_is_none(Type) of
- true ->
+ true ->
case Eval of
- pos -> signal_guard_fail(Guard,
- [RecType, t_from_term(Tag),
+ pos -> signal_guard_fail(Eval, Guard,
+ [RecType, t_from_term(Tag),
t_from_term(Arity)],
State);
neg -> {Map1, t_atom(false)};
dont_know -> {Map1, t_atom(false)}
end;
- false ->
+ false ->
case Eval of
pos -> {enter_type(Rec, Type, Map1), t_atom(true)};
neg -> {Map1, t_atom(false)};
@@ -2079,20 +2118,23 @@ handle_guard_eq(Guard, Map, Env, Eval, State) ->
case {cerl:type(Arg1), cerl:type(Arg2)} of
{literal, literal} ->
case cerl:concrete(Arg1) =:= cerl:concrete(Arg2) of
- true ->
- if
+ true ->
+ if
Eval =:= pos -> {Map, t_atom(true)};
- Eval =:= neg -> throw({fail, none});
+ Eval =:= neg ->
+ ArgTypes = [t_from_term(cerl:concrete(Arg1)),
+ t_from_term(cerl:concrete(Arg2))],
+ signal_guard_fail(Eval, Guard, ArgTypes, State);
Eval =:= dont_know -> {Map, t_atom(true)}
end;
false ->
- if
+ if
Eval =:= neg -> {Map, t_atom(false)};
Eval =:= dont_know -> {Map, t_atom(false)};
- Eval =:= pos ->
+ Eval =:= pos ->
ArgTypes = [t_from_term(cerl:concrete(Arg1)),
t_from_term(cerl:concrete(Arg2))],
- signal_guard_fail(Guard, ArgTypes, State)
+ signal_guard_fail(Eval, Guard, ArgTypes, State)
end
end;
{literal, _} when Eval =:= pos ->
@@ -2137,17 +2179,20 @@ handle_guard_eqeq(Guard, Map, Env, Eval, State) ->
{literal, literal} ->
case cerl:concrete(Arg1) =:= cerl:concrete(Arg2) of
true ->
- if Eval =:= neg -> throw({fail, none});
+ if Eval =:= neg ->
+ ArgTypes = [t_from_term(cerl:concrete(Arg1)),
+ t_from_term(cerl:concrete(Arg2))],
+ signal_guard_fail(Eval, Guard, ArgTypes, State);
Eval =:= pos -> {Map, t_atom(true)};
Eval =:= dont_know -> {Map, t_atom(true)}
end;
false ->
if Eval =:= neg -> {Map, t_atom(false)};
Eval =:= dont_know -> {Map, t_atom(false)};
- Eval =:= pos ->
+ Eval =:= pos ->
ArgTypes = [t_from_term(cerl:concrete(Arg1)),
t_from_term(cerl:concrete(Arg2))],
- signal_guard_fail(Guard, ArgTypes, State)
+ signal_guard_fail(Eval, Guard, ArgTypes, State)
end
end;
{literal, _} when Eval =:= pos ->
@@ -2161,7 +2206,7 @@ handle_guard_eqeq(Guard, Map, Env, Eval, State) ->
bind_eqeq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State) ->
{Map1, Type1} = bind_guard(Arg1, Map, Env, dont_know, State),
{Map2, Type2} = bind_guard(Arg2, Map1, Env, dont_know, State),
- ?debug("Types are:~s =:= ~s\n", [t_to_string(Type1),
+ ?debug("Types are:~s =:= ~s\n", [t_to_string(Type1),
t_to_string(Type2)]),
Inf = t_inf(Type1, Type2),
case t_is_none(Inf) of
@@ -2169,7 +2214,7 @@ bind_eqeq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State) ->
case Eval of
neg -> {Map2, t_atom(false)};
dont_know -> {Map2, t_atom(false)};
- pos -> signal_guard_fail(Guard, [Type1, Type2], State)
+ pos -> signal_guard_fail(Eval, Guard, [Type1, Type2], State)
end;
false ->
case Eval of
@@ -2196,29 +2241,29 @@ bind_eqeq_guard(Guard, Arg1, Arg2, Map, Env, Eval, State) ->
end.
bind_eqeq_guard_lit_other(Guard, Arg1, Arg2, Map, Env, State) ->
- %% Assumes positive evaluation
+ Eval = dont_know,
case cerl:concrete(Arg1) of
true ->
{_, Type} = MT = bind_guard(Arg2, Map, Env, pos, State),
case t_is_atom(true, Type) of
true -> MT;
- false ->
- {_, Type0} = bind_guard(Arg2, Map, Env, dont_know, State),
- signal_guard_fail(Guard, [Type0, t_atom(true)], State)
+ false ->
+ {_, Type0} = bind_guard(Arg2, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0, t_atom(true)], State)
end;
- false ->
+ false ->
{Map1, Type} = bind_guard(Arg2, Map, Env, neg, State),
case t_is_atom(false, Type) of
true -> {Map1, t_atom(true)};
- false ->
- {_, Type0} = bind_guard(Arg2, Map, Env, dont_know, State),
- signal_guard_fail(Guard, [Type0, t_atom(true)], State)
+ false ->
+ {_, Type0} = bind_guard(Arg2, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0, t_atom(false)], State)
end;
Term ->
LitType = t_from_term(Term),
- {Map1, Type} = bind_guard(Arg2, Map, Env, dont_know, State),
+ {Map1, Type} = bind_guard(Arg2, Map, Env, Eval, State),
case t_is_subtype(LitType, Type) of
- false -> signal_guard_fail(Guard, [Type, LitType], State);
+ false -> signal_guard_fail(Eval, Guard, [Type, LitType], State);
true ->
case cerl:is_c_var(Arg2) of
true -> {enter_type(Arg2, LitType, Map1), t_atom(true)};
@@ -2233,97 +2278,118 @@ handle_guard_and(Guard, Map, Env, Eval, State) ->
pos ->
{Map1, Type1} = bind_guard(Arg1, Map, Env, Eval, State),
case t_is_atom(true, Type1) of
- false -> throw({fail, none});
+ false -> signal_guard_fail(Eval, Guard, [Type1, t_any()], State);
true ->
{Map2, Type2} = bind_guard(Arg2, Map1, Env, Eval, State),
case t_is_atom(true, Type2) of
- false -> throw({fail, none});
+ false -> signal_guard_fail(Eval, Guard, [Type1, Type2], State);
true -> {Map2, t_atom(true)}
end
end;
neg ->
- {Map1, Type1} =
+ {Map1, Type1} =
try bind_guard(Arg1, Map, Env, neg, State)
catch throw:{fail, _} -> bind_guard(Arg2, Map, Env, pos, State)
end,
- {Map2, Type2} =
- try bind_guard(Arg1, Map, Env, neg, State)
- catch throw:{fail, _} -> bind_guard(Arg2, Map, Env, pos, State)
+ {Map2, Type2} =
+ try bind_guard(Arg2, Map, Env, neg, State)
+ catch throw:{fail, _} -> bind_guard(Arg1, Map, Env, pos, State)
end,
case t_is_atom(false, Type1) orelse t_is_atom(false, Type2) of
true -> {join_maps([Map1, Map2], Map), t_atom(false)};
- false -> throw({fail, none})
+ false -> signal_guard_fail(Eval, Guard, [Type1, Type2], State)
end;
dont_know ->
- True = t_atom(true),
{Map1, Type1} = bind_guard(Arg1, Map, Env, dont_know, State),
- case t_is_none(t_inf(Type1, t_boolean())) of
- true -> throw({fail, none});
+ {Map2, Type2} = bind_guard(Arg2, Map, Env, dont_know, State),
+ Bool1 = t_inf(Type1, t_boolean()),
+ Bool2 = t_inf(Type2, t_boolean()),
+ case t_is_none(Bool1) orelse t_is_none(Bool2) of
+ true -> throw({fatal_fail, none});
false ->
- {Map2, Type2} = bind_guard(Arg2, Map1, Env, Eval, State),
- case t_is_none(t_inf(Type2, t_boolean())) of
- true -> throw({fail, none});
- false -> {Map2, True}
- end
+ NewMap = join_maps([Map1, Map2], Map),
+ NewType =
+ case {t_atom_vals(Bool1), t_atom_vals(Bool2)} of
+ {['true'] , ['true'] } -> t_atom(true);
+ {['false'], _ } -> t_atom(false);
+ {_ , ['false']} -> t_atom(false);
+ {_ , _ } -> t_boolean()
+ end,
+ {NewMap, NewType}
end
end.
handle_guard_or(Guard, Map, Env, Eval, State) ->
[Arg1, Arg2] = cerl:call_args(Guard),
case Eval of
- pos ->
- {Map1, Bool1} =
+ pos ->
+ {Map1, Bool1} =
try bind_guard(Arg1, Map, Env, pos, State)
- catch
+ catch
throw:{fail,_} -> bind_guard(Arg1, Map, Env, dont_know, State)
end,
- {Map2, Bool2} =
+ {Map2, Bool2} =
try bind_guard(Arg2, Map, Env, pos, State)
- catch
+ catch
throw:{fail,_} -> bind_guard(Arg2, Map, Env, dont_know, State)
end,
case ((t_is_atom(true, Bool1) andalso t_is_boolean(Bool2))
- orelse
+ orelse
(t_is_atom(true, Bool2) andalso t_is_boolean(Bool1))) of
true -> {join_maps([Map1, Map2], Map), t_atom(true)};
- false -> throw({fail, none})
+ false -> signal_guard_fail(Eval, Guard, [Bool1, Bool2], State)
end;
neg ->
{Map1, Type1} = bind_guard(Arg1, Map, Env, neg, State),
case t_is_atom(false, Type1) of
- false -> throw({fail, none});
+ false -> signal_guard_fail(Eval, Guard, [Type1, t_any()], State);
true ->
{Map2, Type2} = bind_guard(Arg2, Map1, Env, neg, State),
case t_is_atom(false, Type2) of
- false -> throw({fail, none});
+ false -> signal_guard_fail(Eval, Guard, [Type1, Type2], State);
true -> {Map2, t_atom(false)}
end
end;
dont_know ->
- {Map1, Bool1} = bind_guard(Arg1, Map, Env, dont_know, State),
- {Map2, Bool2} = bind_guard(Arg2, Map, Env, dont_know, State),
- case t_is_boolean(Bool1) andalso t_is_boolean(Bool2) of
- true -> {join_maps([Map1, Map2], Map), t_sup(Bool1, Bool2)};
- false -> throw({fail, none})
+ {Map1, Type1} = bind_guard(Arg1, Map, Env, dont_know, State),
+ {Map2, Type2} = bind_guard(Arg2, Map, Env, dont_know, State),
+ Bool1 = t_inf(Type1, t_boolean()),
+ Bool2 = t_inf(Type2, t_boolean()),
+ case t_is_none(Bool1) orelse t_is_none(Bool2) of
+ true -> throw({fatal_fail, none});
+ false ->
+ NewMap = join_maps([Map1, Map2], Map),
+ NewType =
+ case {t_atom_vals(Bool1), t_atom_vals(Bool2)} of
+ {['false'], ['false']} -> t_atom(false);
+ {['true'] , _ } -> t_atom(true);
+ {_ , ['true'] } -> t_atom(true);
+ {_ , _ } -> t_boolean()
+ end,
+ {NewMap, NewType}
end
end.
handle_guard_not(Guard, Map, Env, Eval, State) ->
[Arg] = cerl:call_args(Guard),
case Eval of
- neg ->
+ neg ->
{Map1, Type} = bind_guard(Arg, Map, Env, pos, State),
case t_is_atom(true, Type) of
true -> {Map1, t_atom(false)};
- false -> throw({fail, none})
+ false ->
+ {_, Type0} = bind_guard(Arg, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0], State)
end;
- pos ->
+ pos ->
{Map1, Type} = bind_guard(Arg, Map, Env, neg, State),
case t_is_atom(false, Type) of
true -> {Map1, t_atom(true)};
- false -> throw({fail, none})
+ false ->
+ {_, Type0} = bind_guard(Arg, Map, Env, Eval, State),
+ signal_guard_fail(Eval, Guard, [Type0], State)
end;
- dont_know ->
+ dont_know ->
{Map1, Type} = bind_guard(Arg, Map, Env, dont_know, State),
Bool = t_inf(Type, t_boolean()),
case t_is_none(Bool) of
@@ -2346,23 +2412,31 @@ bind_guard_list([G|Gs], Map, Env, Eval, State, Acc) ->
bind_guard_list([], Map, _Env, _Eval, _State, Acc) ->
{Map, lists:reverse(Acc)}.
--spec signal_guard_fail(cerl:c_call(), [erl_types:erl_type()], state()) ->
- no_return().
+-type eval() :: 'pos' | 'neg' | 'dont_know'.
+
+-spec signal_guard_fail(eval(), cerl:c_call(), [erl_types:erl_type()],
+ state()) -> no_return().
-signal_guard_fail(Guard, ArgTypes, State) ->
+signal_guard_fail(Eval, Guard, ArgTypes, State) ->
Args = cerl:call_args(Guard),
F = cerl:atom_val(cerl:call_name(Guard)),
MFA = {cerl:atom_val(cerl:call_module(Guard)), F, length(Args)},
Msg =
case is_infix_op(MFA) of
- true ->
+ true ->
[ArgType1, ArgType2] = ArgTypes,
[Arg1, Arg2] = Args,
- {guard_fail, [format_args_1([Arg1], [ArgType1], State),
- atom_to_list(F),
- format_args_1([Arg2], [ArgType2], State)]};
+ Kind =
+ case Eval of
+ neg -> neg_guard_fail;
+ pos -> guard_fail;
+ dont_know -> guard_fail
+ end,
+ {Kind, [format_args_1([Arg1], [ArgType1], State),
+ atom_to_list(F),
+ format_args_1([Arg2], [ArgType2], State)]};
false ->
- mk_guard_msg(F, Args, ArgTypes, State)
+ mk_guard_msg(Eval, F, Args, ArgTypes, State)
end,
throw({fail, {Guard, Msg}}).
@@ -2377,26 +2451,31 @@ is_infix_op({erlang, '>=', 2}) -> true;
is_infix_op({M, F, A}) when is_atom(M), is_atom(F),
is_integer(A), 0 =< A, A =< 255 -> false.
--spec signal_guard_fatal_fail(cerl:c_call(), [erl_types:erl_type()], state()) ->
- no_return().
+-spec signal_guard_fatal_fail(eval(), cerl:c_call(), [erl_types:erl_type()],
+ state()) -> no_return().
-signal_guard_fatal_fail(Guard, ArgTypes, State) ->
- Args = cerl:call_args(Guard),
+signal_guard_fatal_fail(Eval, Guard, ArgTypes, State) ->
+ Args = cerl:call_args(Guard),
F = cerl:atom_val(cerl:call_name(Guard)),
- Msg = mk_guard_msg(F, Args, ArgTypes, State),
+ Msg = mk_guard_msg(Eval, F, Args, ArgTypes, State),
throw({fatal_fail, {Guard, Msg}}).
-mk_guard_msg(F, Args, ArgTypes, State) ->
+mk_guard_msg(Eval, F, Args, ArgTypes, State) ->
FArgs = [F, format_args(Args, ArgTypes, State)],
case any_has_opaque_subtype(ArgTypes) of
true -> {opaque_guard, FArgs};
- false -> {guard_fail, FArgs}
+ false ->
+ case Eval of
+ neg -> {neg_guard_fail, FArgs};
+ pos -> {guard_fail, FArgs};
+ dont_know -> {guard_fail, FArgs}
+ end
end.
-
+
bind_guard_case_clauses(Arg, Clauses, Map, Env, Eval, State) ->
Clauses1 = filter_fail_clauses(Clauses),
{GenMap, GenArgType} = bind_guard(Arg, Map, Env, dont_know, State),
- bind_guard_case_clauses(GenArgType, GenMap, Arg, Clauses1, Map, Env, Eval,
+ bind_guard_case_clauses(GenArgType, GenMap, Arg, Clauses1, Map, Env, Eval,
t_none(), [], State).
filter_fail_clauses([Clause|Left]) ->
@@ -2413,7 +2492,7 @@ filter_fail_clauses([Clause|Left]) ->
filter_fail_clauses([]) ->
[].
-bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
+bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
Map, Env, Eval, AccType, AccMaps, State) ->
Pats = cerl:clause_pats(Clause),
{NewMap0, ArgType} =
@@ -2427,7 +2506,7 @@ bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
false -> bind_guard(ArgExpr, Map, Env, neg, State);
_ -> {GenMap, GenArgType}
end
- catch
+ catch
throw:{fail, _} -> {none, GenArgType}
end;
false ->
@@ -2457,7 +2536,7 @@ bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
NewGenArgType = t_subtract(GenArgType, GenPatType),
case (NewMap1 =:= none) orelse t_is_none(GenArgType) of
true ->
- bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
+ bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
Eval, AccType, AccMaps, State);
false ->
{NewAccType, NewAccMaps} =
@@ -2467,15 +2546,15 @@ bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
true -> throw({fail, none});
false -> ok
end,
- {NewMap3, CType} = bind_guard(cerl:clause_body(Clause), NewMap2,
+ {NewMap3, CType} = bind_guard(cerl:clause_body(Clause), NewMap2,
Env, Eval, State),
case Eval of
- pos ->
+ pos ->
case t_is_atom(true, CType) of
true -> ok;
false -> throw({fail, none})
end;
- neg ->
+ neg ->
case t_is_atom(false, CType) of
true -> ok;
false -> throw({fail, none})
@@ -2487,10 +2566,10 @@ bind_guard_case_clauses(GenArgType, GenMap, ArgExpr, [Clause|Left],
catch
throw:{fail, _What} -> {AccType, AccMaps}
end,
- bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
+ bind_guard_case_clauses(NewGenArgType, GenMap, ArgExpr, Left, Map, Env,
Eval, NewAccType, NewAccMaps, State)
end;
-bind_guard_case_clauses(_GenArgType, _GenMap, _ArgExpr, [], Map, _Env, _Eval,
+bind_guard_case_clauses(_GenArgType, _GenMap, _ArgExpr, [], Map, _Env, _Eval,
AccType, AccMaps, _State) ->
case t_is_none(AccType) of
true -> throw({fail, none});
@@ -2576,7 +2655,7 @@ enter_type(Key, Val, {Map, Subst} = MS) ->
enter_subst(Key, Val, {Map, Subst} = MS) ->
KeyLabel = get_label(Key),
case cerl:is_literal(Val) of
- true ->
+ true ->
NewMap = dict:store(KeyLabel, literal_type(Val), Map),
{NewMap, Subst};
false ->
@@ -2598,13 +2677,13 @@ enter_subst(Key, Val, {Map, Subst} = MS) ->
end
end.
-lookup_type(Key, {Map, Subst}) ->
+lookup_type(Key, {Map, Subst}) ->
lookup(Key, Map, Subst, t_none()).
lookup(Key, Map, Subst, AnyNone) ->
case cerl:is_literal(Key) of
true -> literal_type(Key);
- false ->
+ false ->
Label = get_label(Key),
case dict:find(Label, Subst) of
{ok, NewKey} -> lookup(NewKey, Map, Subst, AnyNone);
@@ -2669,7 +2748,7 @@ get_label(T) ->
t_is_simple(ArgType) ->
t_is_atom(ArgType) orelse t_is_number(ArgType) orelse t_is_port(ArgType)
- orelse t_is_pid(ArgType) orelse t_is_reference(ArgType)
+ orelse t_is_pid(ArgType) orelse t_is_reference(ArgType)
orelse t_is_nil(ArgType).
%% t_is_structured(ArgType) ->
@@ -2687,8 +2766,8 @@ is_call_to_send(Tree) ->
Mod = cerl:call_module(Tree),
Name = cerl:call_name(Tree),
Arity = cerl:call_arity(Tree),
- cerl:is_c_atom(Mod)
- andalso cerl:is_c_atom(Name)
+ cerl:is_c_atom(Mod)
+ andalso cerl:is_c_atom(Name)
andalso (cerl:atom_val(Name) =:= '!')
andalso (cerl:atom_val(Mod) =:= erlang)
andalso (Arity =:= 2)
@@ -2714,7 +2793,7 @@ filter_match_fail([Clause] = Cls) ->
filter_match_fail([H|T]) ->
[H|filter_match_fail(T)];
filter_match_fail([]) ->
- %% This can actually happen, for example in
+ %% This can actually happen, for example in
%% receive after 1 -> ok end
[].
@@ -2731,22 +2810,22 @@ determine_mode(Type, Opaques) ->
%%% ===========================================================================
state__new(Callgraph, Tree, Plt, Module, Records, BehaviourTranslations) ->
+ Opaques = erl_types:module_builtin_opaques(Module) ++
+ erl_types:t_opaque_from_records(Records),
TreeMap = build_tree_map(Tree),
Funs = dict:fetch_keys(TreeMap),
- FunTab = init_fun_tab(Funs, dict:new(), TreeMap, Callgraph, Plt),
+ FunTab = init_fun_tab(Funs, dict:new(), TreeMap, Callgraph, Plt, Opaques),
Work = init_work([get_label(Tree)]),
Env = dict:store(top, map__new(), dict:new()),
- Opaques = erl_types:module_builtin_opaques(Module) ++
- erl_types:t_opaque_from_records(Records),
#state{callgraph = Callgraph, envs = Env, fun_tab = FunTab, opaques = Opaques,
plt = Plt, races = dialyzer_races:new(), records = Records,
warning_mode = false, warnings = [], work = Work, tree_map = TreeMap,
- module = Module, behaviour_api_info = BehaviourTranslations}.
+ module = Module, behaviour_api_dict = BehaviourTranslations}.
state__mark_fun_as_handled(#state{fun_tab = FunTab} = State, Fun0) ->
Fun = get_label(Fun0),
case dict:find(Fun, FunTab) of
- {ok, {not_handled, Entry}} ->
+ {ok, {not_handled, Entry}} ->
State#state{fun_tab = dict:store(Fun, Entry, FunTab)};
{ok, {_, _}} ->
State
@@ -2800,7 +2879,7 @@ state__add_warning(State, Tag, Tree, Msg) ->
state__add_warning(#state{warning_mode = false} = State, _, _, _, _) ->
State;
-state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
+state__add_warning(#state{warnings = Warnings, warning_mode = true} = State,
Tag, Tree, Msg, Force) ->
Ann = cerl:get_ann(Tree),
case Force of
@@ -2848,7 +2927,7 @@ state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab,
{Name, Contract} =
case dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of
error -> {[], none};
- {ok, {_M, F, A} = MFA} ->
+ {ok, {_M, F, A} = MFA} ->
{[F, A], dialyzer_plt:lookup_contract(Plt, MFA)}
end,
case t_is_none(Ret) of
@@ -2866,19 +2945,19 @@ state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab,
case classify_returns(Fun) of
no_match ->
Msg = {no_return, [no_match|Name]},
- state__add_warning(AccState, ?WARN_RETURN_NO_RETURN,
+ state__add_warning(AccState, ?WARN_RETURN_NO_RETURN,
Fun, Msg);
only_explicit ->
Msg = {no_return, [only_explicit|Name]},
- state__add_warning(AccState, ?WARN_RETURN_ONLY_EXIT,
+ state__add_warning(AccState, ?WARN_RETURN_ONLY_EXIT,
Fun, Msg);
only_normal ->
Msg = {no_return, [only_normal|Name]},
- state__add_warning(AccState, ?WARN_RETURN_NO_RETURN,
+ state__add_warning(AccState, ?WARN_RETURN_NO_RETURN,
Fun, Msg);
both ->
Msg = {no_return, [both|Name]},
- state__add_warning(AccState, ?WARN_RETURN_NO_RETURN,
+ state__add_warning(AccState, ?WARN_RETURN_NO_RETURN,
Fun, Msg)
end;
false ->
@@ -2916,10 +2995,10 @@ state__lookup_name(Fun, #state{callgraph = Callgraph}) ->
state__lookup_record(Tag, Arity, #state{records = Records}) ->
case erl_types:lookup_record(Tag, Arity, Records) of
- {ok, Fields} ->
+ {ok, Fields} ->
{ok, t_tuple([t_atom(Tag)|
[FieldType || {_FieldName, FieldType} <- Fields]])};
- error ->
+ error ->
error
end.
@@ -2942,15 +3021,15 @@ build_tree_map(Tree) ->
end,
cerl_trees:fold(Fun, dict:new(), Tree).
-init_fun_tab([top|Left], Dict, TreeMap, Callgraph, Plt) ->
+init_fun_tab([top|Left], Dict, TreeMap, Callgraph, Plt, Opaques) ->
NewDict = dict:store(top, {not_handled, {[], t_none()}}, Dict),
- init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt);
-init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt) ->
+ init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques);
+init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, Opaques) ->
Arity = cerl:fun_arity(dict:fetch(Fun, TreeMap)),
FunEntry =
case dialyzer_callgraph:is_escaping(Fun, Callgraph) of
true ->
- Args = lists:duplicate(Arity, t_any()),
+ Args = lists:duplicate(Arity, t_any()),
case lookup_fun_sig(Fun, Callgraph, Plt) of
none -> {Args, t_unit()};
{value, {RetType, _}} ->
@@ -2962,8 +3041,8 @@ init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt) ->
false -> {lists:duplicate(Arity, t_none()), t_unit()}
end,
NewDict = dict:store(Fun, {not_handled, FunEntry}, Dict),
- init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt);
-init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt) ->
+ init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques);
+init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt, _Opaques) ->
Dict.
state__update_fun_env(Tree, Map, #state{envs = Envs} = State) ->
@@ -2990,7 +3069,7 @@ state__all_fun_types(#state{fun_tab = FunTab}) ->
dict:map(fun(_Fun, {Args, Ret}) -> t_fun(Args, Ret)end, Tab1).
state__fun_type(Fun, #state{fun_tab = FunTab}) ->
- Label =
+ Label =
if is_integer(Fun) -> Fun;
true -> get_label(Fun)
end,
@@ -3001,10 +3080,10 @@ state__fun_type(Fun, #state{fun_tab = FunTab}) ->
t_fun(A, R)
end.
-state__update_fun_entry(Tree, ArgTypes, Out0,
+state__update_fun_entry(Tree, ArgTypes, Out0,
#state{fun_tab=FunTab, callgraph=CG, plt=Plt} = State)->
Fun = get_label(Tree),
- Out1 =
+ Out1 =
if Fun =:= top -> Out0;
true ->
case lookup_fun_sig(Fun, CG, Plt) of
@@ -3016,15 +3095,15 @@ state__update_fun_entry(Tree, ArgTypes, Out0,
case dict:find(Fun, FunTab) of
{ok, {ArgTypes, OldOut}} ->
case t_is_equal(OldOut, Out) of
- true ->
- ?debug("Fixpoint for ~w: ~s\n",
- [state__lookup_name(Fun, State),
+ true ->
+ ?debug("Fixpoint for ~w: ~s\n",
+ [state__lookup_name(Fun, State),
t_to_string(t_fun(ArgTypes, Out))]),
State;
false ->
NewEntry = {ArgTypes, Out},
- ?debug("New Entry for ~w: ~s\n",
- [state__lookup_name(Fun, State),
+ ?debug("New Entry for ~w: ~s\n",
+ [state__lookup_name(Fun, State),
t_to_string(t_fun(ArgTypes, Out))]),
NewFunTab = dict:store(Fun, NewEntry, FunTab),
State1 = State#state{fun_tab = NewFunTab},
@@ -3033,8 +3112,8 @@ state__update_fun_entry(Tree, ArgTypes, Out0,
{ok, {NewArgTypes, _OldOut}} ->
%% Can only happen in self-recursive functions. Only update the out type.
NewEntry = {NewArgTypes, Out},
- ?debug("New Entry for ~w: ~s\n",
- [state__lookup_name(Fun, State),
+ ?debug("New Entry for ~w: ~s\n",
+ [state__lookup_name(Fun, State),
t_to_string(t_fun(NewArgTypes, Out))]),
NewFunTab = dict:store(Fun, NewEntry, FunTab),
State1 = State#state{fun_tab = NewFunTab},
@@ -3053,9 +3132,9 @@ state__add_work_from_fun(Tree, #state{callgraph = Callgraph,
MFAList ->
LabelList = [dialyzer_callgraph:lookup_label(MFA, Callgraph)
|| MFA <- MFAList],
- %% Must filter the result for results in this module.
+ %% Must filter the result for results in this module.
FilteredList = [L || {ok, L} <- LabelList, dict:is_key(L, TreeMap)],
- ?debug("~w: Will try to add:~w\n",
+ ?debug("~w: Will try to add:~w\n",
[state__lookup_name(get_label(Tree), State), MFAList]),
lists:foldl(fun(L, AccState) ->
state__add_work(L, AccState)
@@ -3086,15 +3165,15 @@ state__fun_info(external, #state{}) ->
external;
state__fun_info({_, _, _} = MFA, #state{plt = PLT}) ->
{MFA,
- dialyzer_plt:lookup(PLT, MFA),
+ dialyzer_plt:lookup(PLT, MFA),
dialyzer_plt:lookup_contract(PLT, MFA),
t_any()};
state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) ->
{Sig, Contract} =
case dialyzer_callgraph:lookup_name(Fun, CG) of
- error ->
+ error ->
{dialyzer_plt:lookup(PLT, Fun), none};
- {ok, MFA} ->
+ {ok, MFA} ->
{dialyzer_plt:lookup(PLT, MFA), dialyzer_plt:lookup_contract(PLT, MFA)}
end,
LocalRet =
@@ -3122,18 +3201,18 @@ state__find_apply_return(Tree, #state{callgraph = Callgraph} = State) ->
forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) ->
{OldArgTypes, OldOut, Fixpoint} =
case dict:find(Fun, FunTab) of
- {ok, {not_handled, {OldArgTypes0, OldOut0}}} ->
+ {ok, {not_handled, {OldArgTypes0, OldOut0}}} ->
{OldArgTypes0, OldOut0, false};
{ok, {OldArgTypes0, OldOut0}} ->
- {OldArgTypes0, OldOut0,
+ {OldArgTypes0, OldOut0,
t_is_subtype(t_product(ArgTypes), t_product(OldArgTypes0))}
end,
case Fixpoint of
true -> State;
- false ->
+ false ->
NewArgTypes = [t_sup(X, Y) || {X, Y} <- lists:zip(ArgTypes, OldArgTypes)],
NewWork = add_work(Fun, Work),
- ?debug("~w: forwarding args ~s\n",
+ ?debug("~w: forwarding args ~s\n",
[state__lookup_name(Fun, State),
t_to_string(t_product(NewArgTypes))]),
NewFunTab = dict:store(Fun, {NewArgTypes, OldOut}, FunTab),
@@ -3248,7 +3327,7 @@ get_file([_|Tail]) -> get_file(Tail).
is_compiler_generated(Ann) ->
lists:member(compiler_generated, Ann) orelse (get_line(Ann) < 1).
--spec format_args([term()], [erl_types:erl_type()], state()) ->
+-spec format_args([cerl:cerl()], [erl_types:erl_type()], state()) ->
nonempty_string().
format_args([], [], _State) ->
@@ -3256,9 +3335,6 @@ format_args([], [], _State) ->
format_args(ArgList, TypeList, State) ->
"(" ++ format_args_1(ArgList, TypeList, State) ++ ")".
--spec format_args_1([term(),...], [erl_types:erl_type(),...], state()) ->
- string().
-
format_args_1([Arg], [Type], State) ->
format_arg(Arg) ++ format_type(Type, State);
format_args_1([Arg|Args], [Type|Types], State) ->
@@ -3291,6 +3367,11 @@ format_arg(Arg) ->
format_type(Type, #state{records = R}) ->
t_to_string(Type, R).
+-spec format_field_diffs(erl_types:erl_type(), state()) -> string().
+
+format_field_diffs(RecConstruction, #state{records = R}) ->
+ erl_types:record_field_diffs_to_string(RecConstruction, R).
+
-spec format_sig_args(erl_types:erl_type(), state()) -> string().
format_sig_args(Type, #state{records = R}) ->
@@ -3298,12 +3379,12 @@ format_sig_args(Type, #state{records = R}) ->
case SigArgs of
[] -> "()";
[SArg|SArgs] ->
- lists:flatten("(" ++ t_to_string(SArg, R)
+ lists:flatten("(" ++ t_to_string(SArg, R)
++ ["," ++ t_to_string(T, R) || T <- SArgs] ++ ")")
end.
format_cerl(Tree) ->
- cerl_prettypr:format(cerl:set_ann(Tree, []),
+ cerl_prettypr:format(cerl:set_ann(Tree, []),
[{hook, dialyzer_utils:pp_hook()},
{noann, true},
{paper, 100000}, %% These guys strip
@@ -3366,7 +3447,7 @@ find_terminals(Tree) ->
true ->
M = cerl:concrete(M0),
F = cerl:concrete(F0),
- case (erl_bif_types:is_known(M, F, A)
+ case (erl_bif_types:is_known(M, F, A)
andalso t_is_none(erl_bif_types:type(M, F, A))) of
true -> {true, false};
false -> {false, true}
@@ -3381,12 +3462,12 @@ find_terminals(Tree) ->
letrec -> find_terminals(cerl:letrec_body(Tree));
literal -> {false, true};
primop -> {false, false}; %% match_fail, etc. are not explicit exits.
- 'receive' ->
+ 'receive' ->
Timeout = cerl:receive_timeout(Tree),
Clauses = cerl:receive_clauses(Tree),
case (cerl:is_literal(Timeout) andalso
(cerl:concrete(Timeout) =:= infinity)) of
- true ->
+ true ->
if Clauses =:= [] -> {false, true}; %% A never ending receive.
true -> find_terminals_list(Clauses)
end;
@@ -3454,11 +3535,11 @@ find_rec_warnings_tuple(Tree, State) ->
TagVal = cerl:atom_val(Tag),
case state__lookup_record(TagVal, length(Left), State) of
error -> State;
- {ok, Prototype} ->
+ {ok, Prototype} ->
InfTupleType = t_inf(Prototype, TupleType),
case t_is_none(InfTupleType) of
true ->
- Msg = {record_matching,
+ Msg = {record_matching,
[format_patterns([Tree]), TagVal]},
state__add_warning(State, ?WARN_MATCHING, Tree, Msg);
false ->
@@ -3476,7 +3557,7 @@ find_rec_warnings_tuple(Tree, State) ->
%%----------------------------------------------------------------------------
-ifdef(DEBUG_PP).
-debug_pp(Tree, true) ->
+debug_pp(Tree, true) ->
io:put_chars(cerl_prettypr:format(Tree, [{hook, cerl_typean:pp_hook()}])),
io:nl(),
ok;
diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl
index f353638cdf..ccd80a4835 100644
--- a/lib/dialyzer/src/dialyzer_gui.erl
+++ b/lib/dialyzer/src/dialyzer_gui.erl
@@ -2,7 +2,7 @@
%%------------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -88,8 +88,8 @@
-spec start(#options{}) -> ?RET_NOTHING_SUSPICIOUS.
-start(DialyzerOptions = #options{from = From, init_plt = InitPltFile,
- legal_warnings = LegalWarnings}) ->
+start(#options{from = From, init_plts = InitPltFiles,
+ legal_warnings = LegalWarnings} = DialyzerOptions) ->
process_flag(trap_exit, true),
GS = gs:start(),
@@ -336,9 +336,13 @@ start(DialyzerOptions = #options{from = From, init_plt = InitPltFile,
gs:config(Packer, WH),
{ok, CWD} = file:get_cwd(),
- InitPlt = try dialyzer_plt:from_file(InitPltFile)
- catch throw:{dialyzer_error, _} -> dialyzer_plt:new()
- end,
+ InitPlt =
+ case InitPltFiles of
+ [] -> dialyzer_plt:new();
+ _ ->
+ Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles],
+ dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts)
+ end,
State = #gui_state{add_all = AddAll,
add_file = AddFile,
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 2e309d7ec1..e711c15ea7 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -88,7 +88,7 @@ start(DialyzerOptions) ->
State = wx:batch(fun() -> create_window(Wx, DialyzerOptions) end),
gui_loop(State).
-create_window(Wx, DialyzerOptions) ->
+create_window(Wx, #options{init_plts = InitPltFiles} = DialyzerOptions) ->
{ok, Host} = inet:gethostname(),
%%---------- initializing frame ---------
@@ -258,11 +258,15 @@ create_window(Wx, DialyzerOptions) ->
plt = PltMenu,
options =OptionsMenu,
help = HelpMenu},
-
- InitPlt = try dialyzer_plt:from_file(DialyzerOptions#options.init_plt)
- catch throw:{dialyzer_error, _} -> dialyzer_plt:new()
- end,
+ InitPlt =
+ case InitPltFiles of
+ [] -> dialyzer_plt:new();
+ _ ->
+ Plts = [dialyzer_plt:from_file(F) || F <- InitPltFiles],
+ dialyzer_plt:merge_plts_or_report_conflicts(InitPltFiles, Plts)
+ end,
+
#gui_state{add = AddButton,
add_dir = AddDirButton,
add_rec = AddRecButton,
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index da0e1f9aaf..b2a67de8bd 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,20 +47,28 @@ build(Opts) ->
?WARN_FAILING_CALL,
?WARN_BIN_CONSTRUCTION,
?WARN_CALLGRAPH,
+ ?WARN_CONTRACT_RANGE,
?WARN_CONTRACT_TYPES,
?WARN_CONTRACT_SYNTAX],
DefaultWarns1 = ordsets:from_list(DefaultWarns),
InitPlt = dialyzer_plt:get_default_plt(),
DefaultOpts = #options{},
DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns1,
- init_plt = InitPlt},
- try
- NewOpts = build_options(Opts, DefaultOpts1),
+ init_plts = [InitPlt]},
+ try
+ Opts1 = preprocess_opts(Opts),
+ NewOpts = build_options(Opts1, DefaultOpts1),
postprocess_opts(NewOpts)
catch
throw:{dialyzer_options_error, Msg} -> {error, Msg}
end.
+preprocess_opts([]) -> [];
+preprocess_opts([{init_plt, File}|Opts]) ->
+ [{plts, [File]}|preprocess_opts(Opts)];
+preprocess_opts([Opt|Opts]) ->
+ [Opt|preprocess_opts(Opts)].
+
postprocess_opts(Opts = #options{}) ->
Opts1 = check_output_plt(Opts),
adapt_get_warnings(Opts1).
@@ -113,12 +121,18 @@ build_options([{OptName, undefined}|Rest], Options) when is_atom(OptName) ->
build_options(Rest, Options);
build_options([{OptionName, Value} = Term|Rest], Options) ->
case OptionName of
+ apps ->
+ OldValues = Options#options.files_rec,
+ AppDirs = get_app_dirs(Value),
+ assert_filenames(Term, AppDirs),
+ build_options(Rest, Options#options{files_rec = AppDirs ++ OldValues});
files ->
assert_filenames(Term, Value),
build_options(Rest, Options#options{files = Value});
files_rec ->
+ OldValues = Options#options.files_rec,
assert_filenames(Term, Value),
- build_options(Rest, Options#options{files_rec = Value});
+ build_options(Rest, Options#options{files_rec = Value ++ OldValues});
analysis_type ->
NewOptions =
case Value of
@@ -144,9 +158,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
build_options(Rest, Options#options{from = Value});
get_warnings ->
build_options(Rest, Options#options{get_warnings = Value});
- init_plt ->
- assert_filenames([Term], [Value]),
- build_options(Rest, Options#options{init_plt = Value});
+ plts ->
+ assert_filenames(Term, Value),
+ build_options(Rest, Options#options{init_plts = Value});
include_dirs ->
assert_filenames(Term, Value),
OldVal = Options#options.include_dirs,
@@ -162,6 +176,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
output_format ->
assert_output_format(Value),
build_options(Rest, Options#options{output_format = Value});
+ filename_opt ->
+ assert_filename_opt(Value),
+ build_options(Rest, Options#options{filename_opt = Value});
output_plt ->
assert_filename(Value),
build_options(Rest, Options#options{output_plt = Value});
@@ -181,10 +198,15 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
build_options([], Options) ->
Options.
+get_app_dirs(Apps) when is_list(Apps) ->
+ dialyzer_cl_parse:get_lib_dir([atom_to_list(A) || A <- Apps]);
+get_app_dirs(Apps) ->
+ bad_option("Use a list of otp applications", Apps).
+
assert_filenames(Term, [FileName|Left]) when length(FileName) >= 0 ->
case filelib:is_file(FileName) orelse filelib:is_dir(FileName) of
true -> ok;
- false -> bad_option("No such file or directory", FileName)
+ false -> bad_option("No such file, directory or application", FileName)
end,
assert_filenames(Term, Left);
assert_filenames(_Term, []) ->
@@ -211,6 +233,13 @@ assert_output_format(formatted) ->
assert_output_format(Term) ->
bad_option("Illegal value for output_format", Term).
+assert_filename_opt(basename) ->
+ ok;
+assert_filename_opt(fullpath) ->
+ ok;
+assert_filename_opt(Term) ->
+ bad_option("Illegal value for filename_opt", Term).
+
assert_plt_op(#options{analysis_type = OldVal},
#options{analysis_type = NewVal}) ->
case is_plt_mode(OldVal) andalso is_plt_mode(NewVal) of
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl
index e387077a46..6033d7f17c 100644
--- a/lib/dialyzer/src/dialyzer_plt.erl
+++ b/lib/dialyzer/src/dialyzer_plt.erl
@@ -2,7 +2,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
%%%-------------------------------------------------------------------
%%% File : dialyzer_plt.erl
%%% Author : Tobias Lindahl <[email protected]>
-%%% Description : Interface to display information in the persistent
+%%% Description : Interface to display information in the persistent
%%% lookup tables.
%%%
%%% Created : 23 Jul 2004 by Tobias Lindahl <[email protected]>
@@ -39,28 +39,34 @@
from_file/1,
get_default_plt/0,
get_types/1,
+ get_exported_types/1,
%% insert/3,
insert_list/2,
insert_contract_list/2,
insert_types/2,
+ insert_exported_types/2,
lookup/2,
lookup_contract/2,
lookup_module/2,
merge_plts/1,
+ merge_plts_or_report_conflicts/2,
new/0,
plt_and_info_from_file/1,
get_specs/1,
get_specs/4,
- to_file/4
- ]).
+ to_file/4]).
%% Debug utilities
-export([pp_non_returning/0, pp_mod/1]).
+-export_type([plt/0, plt_info/0]).
+
%%----------------------------------------------------------------------
-type mod_deps() :: dict().
+-type deep_string() :: string() | [deep_string()].
+
%% The following are used for searching the PLT when using the GUI
%% (e.g. in show or search PLT contents). The user might be searching
%% with a partial specification, in which case the missing items
@@ -70,9 +76,10 @@
%%----------------------------------------------------------------------
--record(plt, {info = table_new() :: dict(),
- types = table_new() :: dict(),
- contracts = table_new() :: dict()}).
+-record(plt, {info = table_new() :: dict(),
+ types = table_new() :: dict(),
+ contracts = table_new() :: dict(),
+ exported_types = sets:new() :: set()}).
-opaque plt() :: #plt{}.
-include("dialyzer.hrl").
@@ -80,13 +87,14 @@
-type file_md5() :: {file:filename(), binary()}.
-type plt_info() :: {[file_md5()], dict()}.
--record(file_plt, {version = "" :: string(),
- file_md5_list = [] :: [file_md5()],
- info = dict:new() :: dict(),
- contracts = dict:new() :: dict(),
- types = dict:new() :: dict(),
- mod_deps :: mod_deps(),
- implementation_md5 = [] :: [file_md5()]}).
+-record(file_plt, {version = "" :: string(),
+ file_md5_list = [] :: [file_md5()],
+ info = dict:new() :: dict(),
+ contracts = dict:new() :: dict(),
+ types = dict:new() :: dict(),
+ exported_types = sets:new() :: set(),
+ mod_deps :: mod_deps(),
+ implementation_md5 = [] :: [file_md5()]}).
%%----------------------------------------------------------------------
@@ -95,19 +103,23 @@
new() ->
#plt{}.
--spec delete_module(plt(), module()) -> plt().
+-spec delete_module(plt(), atom()) -> plt().
-delete_module(#plt{info = Info, types = Types, contracts = Contracts}, Mod) ->
+delete_module(#plt{info = Info, types = Types, contracts = Contracts,
+ exported_types = ExpTypes}, Mod) ->
#plt{info = table_delete_module(Info, Mod),
types = table_delete_module2(Types, Mod),
- contracts = table_delete_module(Contracts, Mod)}.
+ contracts = table_delete_module(Contracts, Mod),
+ exported_types = table_delete_module1(ExpTypes, Mod)}.
-spec delete_list(plt(), [mfa() | integer()]) -> plt().
-delete_list(#plt{info = Info, types = Types, contracts = Contracts}, List) ->
+delete_list(#plt{info = Info, types = Types, contracts = Contracts,
+ exported_types = ExpTypes}, List) ->
#plt{info = table_delete_list(Info, List),
types = Types,
- contracts = table_delete_list(Contracts, List)}.
+ contracts = table_delete_list(Contracts, List),
+ exported_types = ExpTypes}.
-spec insert_contract_list(plt(), dialyzer_contracts:plt_contracts()) -> plt().
@@ -126,7 +138,7 @@ delete_contract_list(#plt{contracts = Contracts} = PLT, List) ->
PLT#plt{contracts = table_delete_list(Contracts, List)}.
%% -spec insert(plt(), mfa() | integer(), {_, _}) -> plt().
-%%
+%%
%% insert(#plt{info = Info} = PLT, Id, Types) ->
%% PLT#plt{info = table_insert(Info, Id, Types)}.
@@ -150,19 +162,29 @@ lookup(#plt{info = Info}, Label) when is_integer(Label) ->
insert_types(PLT, Rec) ->
PLT#plt{types = Rec}.
+-spec insert_exported_types(plt(), set()) -> plt().
+
+insert_exported_types(PLT, Set) ->
+ PLT#plt{exported_types = Set}.
+
-spec get_types(plt()) -> dict().
get_types(#plt{types = Types}) ->
Types.
+-spec get_exported_types(plt()) -> set().
+
+get_exported_types(#plt{exported_types = ExpTypes}) ->
+ ExpTypes.
+
-type mfa_types() :: {mfa(), erl_types:erl_type(), [erl_types:erl_type()]}.
--spec lookup_module(plt(), module()) -> 'none' | {'value', [mfa_types()]}.
+-spec lookup_module(plt(), atom()) -> 'none' | {'value', [mfa_types()]}.
lookup_module(#plt{info = Info}, M) when is_atom(M) ->
table_lookup_module(Info, M).
--spec contains_module(plt(), module()) -> boolean().
+-spec contains_module(plt(), atom()) -> boolean().
contains_module(#plt{info = Info, contracts = Cs}, M) when is_atom(M) ->
table_contains_module(Info, M) orelse table_contains_module(Cs, M).
@@ -170,7 +192,7 @@ contains_module(#plt{info = Info, contracts = Cs}, M) when is_atom(M) ->
-spec contains_mfa(plt(), mfa()) -> boolean().
contains_mfa(#plt{info = Info, contracts = Contracts}, MFA) ->
- (table_lookup(Info, MFA) =/= none)
+ (table_lookup(Info, MFA) =/= none)
orelse (table_lookup(Contracts, MFA) =/= none).
-spec get_default_plt() -> file:filename().
@@ -180,8 +202,8 @@ get_default_plt() ->
false ->
case os:getenv("HOME") of
false ->
- error("The HOME environment variable needs to be set " ++
- "so that Dialyzer knows where to find the default PLT");
+ plt_error("The HOME environment variable needs to be set " ++
+ "so that Dialyzer knows where to find the default PLT");
HomeDir -> filename:join(HomeDir, ".dialyzer_plt")
end;
UserSpecPlt -> UserSpecPlt
@@ -201,13 +223,14 @@ from_file(FileName, ReturnInfo) ->
case get_record_from_file(FileName) of
{ok, Rec} ->
case check_version(Rec) of
- error ->
+ error ->
Msg = io_lib:format("Old PLT file ~s\n", [FileName]),
- error(Msg);
- ok ->
+ plt_error(Msg);
+ ok ->
Plt = #plt{info = Rec#file_plt.info,
types = Rec#file_plt.types,
- contracts = Rec#file_plt.contracts},
+ contracts = Rec#file_plt.contracts,
+ exported_types = Rec#file_plt.exported_types},
case ReturnInfo of
false -> Plt;
true ->
@@ -217,13 +240,15 @@ from_file(FileName, ReturnInfo) ->
end
end;
{error, Reason} ->
- error(io_lib:format("Could not read PLT file ~s: ~p\n",
- [FileName, Reason]))
+ Msg = io_lib:format("Could not read PLT file ~s: ~p\n",
+ [FileName, Reason]),
+ plt_error(Msg)
end.
--type inc_file_err_rsn() :: 'no_such_file' | 'read_error'.
--spec included_files(file:filename()) -> {'ok', [file:filename()]}
- | {'error', inc_file_err_rsn()}.
+-type err_rsn() :: 'not_valid' | 'no_such_file' | 'read_error'.
+
+-spec included_files(file:filename()) -> {'ok', [file:filename()]}
+ | {'error', err_rsn()}.
included_files(FileName) ->
case get_record_from_file(FileName) of
@@ -247,12 +272,12 @@ get_record_from_file(FileName) ->
try binary_to_term(Bin) of
#file_plt{} = FilePLT -> {ok, FilePLT};
_ -> {error, not_valid}
- catch
+ catch
_:_ -> {error, not_valid}
end;
{error, enoent} ->
{error, no_such_file};
- {error, _} ->
+ {error, _} ->
{error, read_error}
end.
@@ -261,19 +286,54 @@ get_record_from_file(FileName) ->
merge_plts(List) ->
InfoList = [Info || #plt{info = Info} <- List],
TypesList = [Types || #plt{types = Types} <- List],
+ ExpTypesList = [ExpTypes || #plt{exported_types = ExpTypes} <- List],
ContractsList = [Contracts || #plt{contracts = Contracts} <- List],
#plt{info = table_merge(InfoList),
types = table_merge(TypesList),
+ exported_types = sets_merge(ExpTypesList),
contracts = table_merge(ContractsList)}.
+-spec merge_disj_plts([plt()]) -> plt().
+
+merge_disj_plts(List) ->
+ InfoList = [Info || #plt{info = Info} <- List],
+ TypesList = [Types || #plt{types = Types} <- List],
+ ExpTypesList = [ExpTypes || #plt{exported_types = ExpTypes} <- List],
+ ContractsList = [Contracts || #plt{contracts = Contracts} <- List],
+ #plt{info = table_disj_merge(InfoList),
+ types = table_disj_merge(TypesList),
+ exported_types = sets_disj_merge(ExpTypesList),
+ contracts = table_disj_merge(ContractsList)}.
+
+-spec merge_plts_or_report_conflicts([file:filename()], [plt()]) -> plt().
+
+merge_plts_or_report_conflicts(PltFiles, Plts) ->
+ try
+ merge_disj_plts(Plts)
+ catch throw:{dialyzer_error, not_disjoint_plts} ->
+ IncFiles = lists:append([begin {ok, Fs} = included_files(F), Fs end
+ || F <- PltFiles]),
+ ConfFiles = find_duplicates(IncFiles),
+ Msg = io_lib:format("Could not merge PLTs since they are not disjoint\n"
+ "The following files are included in more than one "
+ "PLTs:\n~p\n", [ConfFiles]),
+ plt_error(Msg)
+ end.
+
+find_duplicates(List) ->
+ ModList = [filename:basename(E) || E <- List],
+ SortedList = lists:usort(ModList),
+ lists:usort(ModList -- SortedList).
+
-spec to_file(file:filename(), plt(), mod_deps(), {[file_md5()], mod_deps()}) -> 'ok'.
to_file(FileName,
- #plt{info = Info, types = Types, contracts = Contracts},
+ #plt{info = Info, types = Types, contracts = Contracts,
+ exported_types = ExpTypes},
ModDeps, {MD5, OldModDeps}) ->
- NewModDeps = dict:merge(fun(_Key, OldVal, NewVal) ->
+ NewModDeps = dict:merge(fun(_Key, OldVal, NewVal) ->
ordsets:union(OldVal, NewVal)
- end,
+ end,
OldModDeps, ModDeps),
ImplMd5 = compute_implementation_md5(),
Record = #file_plt{version = ?VSN,
@@ -281,22 +341,22 @@ to_file(FileName,
info = Info,
contracts = Contracts,
types = Types,
+ exported_types = ExpTypes,
mod_deps = NewModDeps,
implementation_md5 = ImplMd5},
Bin = term_to_binary(Record, [compressed]),
case file:write_file(FileName, Bin) of
ok -> ok;
{error, Reason} ->
- Msg = io_lib:format("Could not write PLT file ~s: ~w\n",
+ Msg = io_lib:format("Could not write PLT file ~s: ~w\n",
[FileName, Reason]),
throw({dialyzer_error, Msg})
end.
-type md5_diff() :: [{'differ', atom()} | {'removed', atom()}].
--type check_error() :: 'not_valid' | 'no_such_file' | 'read_error'
- | {'no_file_to_remove', file:filename()}.
-
--spec check_plt(file:filename(), [file:filename()], [file:filename()]) ->
+-type check_error() :: err_rsn() | {'no_file_to_remove', file:filename()}.
+
+-spec check_plt(file:filename(), [file:filename()], [file:filename()]) ->
'ok'
| {'error', check_error()}
| {'differ', [file_md5()], md5_diff(), mod_deps()}
@@ -306,7 +366,7 @@ check_plt(FileName, RemoveFiles, AddFiles) ->
case get_record_from_file(FileName) of
{ok, #file_plt{file_md5_list = Md5, mod_deps = ModDeps} = Rec} ->
case check_version(Rec) of
- ok ->
+ ok ->
case compute_new_md5(Md5, RemoveFiles, AddFiles) of
ok -> ok;
{differ, NewMd5, DiffMd5} -> {differ, NewMd5, DiffMd5, ModDeps};
@@ -363,7 +423,7 @@ compute_md5_from_files(Files) ->
compute_md5_from_file(File) ->
case filelib:is_regular(File) of
- false ->
+ false ->
Msg = io_lib:format("Not a regular file: ~s\n", [File]),
throw({dialyzer_error, Msg});
true ->
@@ -394,7 +454,7 @@ init_md5_list_1([{File, _Md5}|Md5Left], [{remove, File}|DiffLeft], Acc) ->
init_md5_list_1(Md5Left, DiffLeft, Acc);
init_md5_list_1([{File, _Md5} = Entry|Md5Left], [{add, File}|DiffLeft], Acc) ->
init_md5_list_1(Md5Left, DiffLeft, [Entry|Acc]);
-init_md5_list_1([{File1, _Md5} = Entry|Md5Left] = Md5List,
+init_md5_list_1([{File1, _Md5} = Entry|Md5Left] = Md5List,
[{Tag, File2}|DiffLeft] = DiffList, Acc) ->
case File1 < File2 of
true -> init_md5_list_1(Md5Left, DiffList, [Entry|Acc]);
@@ -425,7 +485,7 @@ get_specs(#plt{info = Info}) ->
beam_file_to_module(Filename) ->
list_to_atom(filename:basename(Filename, ".beam")).
--spec get_specs(plt(), module(), atom(), arity_patt()) -> 'none' | string().
+-spec get_specs(plt(), atom(), atom(), arity_patt()) -> 'none' | string().
get_specs(#plt{info = Info}, M, F, A) when is_atom(M), is_atom(F) ->
MFA = {M, F, A},
@@ -435,7 +495,7 @@ get_specs(#plt{info = Info}, M, F, A) when is_atom(M), is_atom(F) ->
end.
create_specs([{{M, F, _A}, {Ret, Args}}|Left], M) ->
- [io_lib:format("-spec ~w(~s) -> ~s\n",
+ [io_lib:format("-spec ~w(~s) -> ~s\n",
[F, expand_args(Args), erl_types:t_to_string(Ret)])
| create_specs(Left, M)];
create_specs(List = [{{M, _F, _A}, {_Ret, _Args}}| _], _M) ->
@@ -458,7 +518,9 @@ expand_args([ArgType|Left]) ->
end ++
","|expand_args(Left)].
-error(Msg) ->
+-spec plt_error(deep_string()) -> no_return().
+
+plt_error(Msg) ->
throw({dialyzer_error, lists:flatten(Msg)}).
%%---------------------------------------------------------------------------
@@ -475,6 +537,9 @@ table_delete_module(Plt, Mod) ->
(_, _) -> true
end, Plt).
+table_delete_module1(Plt, Mod) ->
+ sets:filter(fun({M, _F, _A}) -> M =/= Mod end, Plt).
+
table_delete_module2(Plt, Mod) ->
dict:filter(fun(M, _Val) -> M =/= Mod end, Plt).
@@ -488,7 +553,7 @@ table_insert_list(Plt, [{Key, Val}|Left]) ->
table_insert_list(Plt, []) ->
Plt.
-table_insert(Plt, Key, {_Ret, _Arg} = Obj) ->
+table_insert(Plt, Key, {_Ret, _Arg} = Obj) ->
dict:store(Key, Obj, Plt);
table_insert(Plt, Key, #contract{} = C) ->
dict:store(Key, C, Plt).
@@ -526,6 +591,47 @@ table_merge([Plt|Plts], Acc) ->
NewAcc = dict:merge(fun(_Key, Val, Val) -> Val end, Plt, Acc),
table_merge(Plts, NewAcc).
+table_disj_merge([H|T]) ->
+ table_disj_merge(T, H).
+
+table_disj_merge([], Acc) ->
+ Acc;
+table_disj_merge([Plt|Plts], Acc) ->
+ case table_is_disjoint(Plt, Acc) of
+ true ->
+ NewAcc = dict:merge(fun(_Key, _Val1, _Val2) -> gazonk end,
+ Plt, Acc),
+ table_disj_merge(Plts, NewAcc);
+ false -> throw({dialyzer_error, not_disjoint_plts})
+ end.
+
+table_is_disjoint(T1, T2) ->
+ K1 = dict:fetch_keys(T1),
+ K2 = dict:fetch_keys(T2),
+ lists:all(fun(E) -> not lists:member(E, K2) end, K1).
+
+sets_merge([H|T]) ->
+ sets_merge(T, H).
+
+sets_merge([], Acc) ->
+ Acc;
+sets_merge([Plt|Plts], Acc) ->
+ NewAcc = sets:union(Plt, Acc),
+ sets_merge(Plts, NewAcc).
+
+sets_disj_merge([H|T]) ->
+ sets_disj_merge(T, H).
+
+sets_disj_merge([], Acc) ->
+ Acc;
+sets_disj_merge([Plt|Plts], Acc) ->
+ case sets:is_disjoint(Plt, Acc) of
+ true ->
+ NewAcc = sets:union(Plt, Acc),
+ sets_disj_merge(Plts, NewAcc);
+ false -> throw({dialyzer_error, not_disjoint_plts})
+ end.
+
%%---------------------------------------------------------------------------
%% Debug utilities.
@@ -555,7 +661,7 @@ pp_non_returning() ->
[M, F, dialyzer_utils:format_sig(Type)])
end, lists:sort(None)).
--spec pp_mod(module()) -> 'ok'.
+-spec pp_mod(atom()) -> 'ok'.
pp_mod(Mod) when is_atom(Mod) ->
PltFile = get_default_plt(),
diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl
index 4972967960..ee9d5e88a3 100644
--- a/lib/dialyzer/src/dialyzer_races.erl
+++ b/lib/dialyzer/src/dialyzer_races.erl
@@ -21,7 +21,7 @@
%%%----------------------------------------------------------------------
%%% File : dialyzer_races.erl
%%% Author : Maria Christakis <[email protected]>
-%%% Description : Utility functions for race condition detection
+%%% Description : Utility functions for race condition detection
%%%
%%% Created : 21 Nov 2008 by Maria Christakis <[email protected]>
%%%----------------------------------------------------------------------
@@ -39,6 +39,8 @@
let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2,
put_race_analysis/2, put_race_list/3]).
+-export_type([races/0, mfa_or_funlbl/0, core_vars/0]).
+
-include("dialyzer.hrl").
%%% ===========================================================================
@@ -80,7 +82,7 @@
-type call() :: 'whereis' | 'register' | 'unregister' | 'ets_new'
| 'ets_lookup' | 'ets_insert' | 'mnesia_dirty_read1'
| 'mnesia_dirty_read2' | 'mnesia_dirty_write1'
- | 'mnesia_dirty_write2' | 'function_call'.
+ | 'mnesia_dirty_write2' | 'function_call'.
-type race_tag() :: 'whereis_register' | 'whereis_unregister'
| 'ets_lookup_insert' | 'mnesia_dirty_read_write'.
@@ -116,7 +118,7 @@
var_map :: dict()}).
-type case_tags() :: 'beg_case' | #beg_clause{} | #end_clause{} | #end_case{}.
--type code() :: [#dep_call{} | #warn_call{} | #fun_call{} |
+-type code() :: [#dep_call{} | #fun_call{} | #warn_call{} |
#curr_fun{} | #let_tag{} | case_tags() | race_tag()].
-type table_var() :: label() | ?no_label.
@@ -157,7 +159,7 @@
%%% ===========================================================================
-spec store_race_call(mfa_or_funlbl(), [erl_types:erl_type()], [core_vars()],
- file_line(), dialyzer_dataflow:state()) ->
+ file_line(), dialyzer_dataflow:state()) ->
dialyzer_dataflow:state().
store_race_call(Fun, ArgTypes, Args, FileLine, State) ->
@@ -166,7 +168,7 @@ store_race_call(Fun, ArgTypes, Args, FileLine, State) ->
CurrFunLabel = Races#races.curr_fun_label,
RaceTags = Races#races.race_tags,
CleanState = dialyzer_dataflow:state__records_only(State),
- {NewRaceList, NewRaceListSize, NewRaceTags, NewTable} =
+ {NewRaceList, NewRaceListSize, NewRaceTags, NewTable} =
case CurrFun of
{_Module, module_info, A} when A =:= 0 orelse A =:= 1 ->
{[], 0, RaceTags, no_t};
@@ -422,7 +424,7 @@ fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls, Code, RaceList,
Races = dialyzer_dataflow:state__get_races(State),
{RetCurrFun, RetCurrFunLabel, RetCalls, RetCode,
RetRaceList, RetRaceVarMap, RetFunDefVars, RetFunCallVars,
- RetFunArgTypes, RetNestingLevel} =
+ RetFunArgTypes, RetNestingLevel} =
fixup_race_forward_helper(NewCurrFun,
NewCurrFunLabel, Fun, Int, NewCalls, NewCalls,
[#curr_fun{status = out, mfa = NewCurrFun,
@@ -477,23 +479,11 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
_Other ->
{RaceList, [], NestingLevel, false}
end;
- #dep_call{call_name = ets_lookup, args = DepCallArgs} ->
+ #dep_call{call_name = ets_lookup} ->
case RaceWarnTag of
?WARN_ETS_LOOKUP_INSERT ->
- [Tab, Names, _, _] = DepCallArgs,
- case compare_var_list(Tab,
- dialyzer_callgraph:get_public_tables(Callgraph),
- RaceVarMap)
- orelse
- length(Names --
- dialyzer_callgraph:get_named_tables(Callgraph)) <
- length(Names) of
- true ->
- {[Head#dep_call{var_map = RaceVarMap}|RaceList],
- [], NestingLevel, false};
- false ->
- {RaceList, [], NestingLevel, false}
- end;
+ {[Head#dep_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
_Other ->
{RaceList, [], NestingLevel, false}
end;
@@ -515,23 +505,11 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
_Other ->
{RaceList, [], NestingLevel, false}
end;
- #warn_call{call_name = ets_insert, args = WarnCallArgs} ->
+ #warn_call{call_name = ets_insert} ->
case RaceWarnTag of
?WARN_ETS_LOOKUP_INSERT ->
- [Tab, Names, _, _] = WarnCallArgs,
- case compare_var_list(Tab,
- dialyzer_callgraph:get_public_tables(Callgraph),
- RaceVarMap)
- orelse
- length(Names --
- dialyzer_callgraph:get_named_tables(Callgraph)) <
- length(Names) of
- true ->
- {[Head#warn_call{var_map = RaceVarMap}|RaceList],
- [], NestingLevel, false};
- false ->
- {RaceList, [], NestingLevel, false}
- end;
+ {[Head#warn_call{var_map = RaceVarMap}|RaceList],
+ [], NestingLevel, false};
_Other ->
{RaceList, [], NestingLevel, false}
end;
@@ -576,7 +554,7 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
RaceTag ->
PublicTables = dialyzer_callgraph:get_public_tables(Callgraph),
NamedTables = dialyzer_callgraph:get_named_tables(Callgraph),
- WarnVarArgs1 =
+ WarnVarArgs1 =
var_type_analysis(FunDefVars, FunArgTypes, WarnVarArgs,
RaceWarnTag, RaceVarMap,
dialyzer_dataflow:state__records_only(State)),
@@ -598,7 +576,7 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
[#warn_call{call_name = ets_insert, args = WarnVarArgs,
var_map = RaceVarMap}],
[Tab, Names, _, _] = WarnVarArgs,
- case IsPublic orelse
+ case IsPublic orelse
compare_var_list(Tab, PublicTables, RaceVarMap)
orelse
length(Names -- NamedTables) < length(Names) of
@@ -636,7 +614,7 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
#curr_fun{mfa = CurrFun2, label = CurrFunLabel2,
var_map = RaceVarMap2, def_vars = FunDefVars2,
call_vars = FunCallVars2, arg_types = FunArgTypes2},
- Code2, NestingLevel2} =
+ Code2, NestingLevel2} =
remove_clause(NewRL,
#curr_fun{mfa = CurrFun, label = CurrFunLabel,
var_map = RaceVarMap1,
@@ -648,7 +626,7 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
RaceVarMap2, FunDefVars2, FunCallVars2, FunArgTypes2,
NestingLevel2, false};
false ->
- {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap1,
+ {CurrFun, CurrFunLabel, Tail, NewRL, RaceVarMap1,
FunDefVars, FunCallVars, FunArgTypes, NewNL, false}
end;
#end_clause{arg = Arg, pats = Pats, guard = Guard} ->
@@ -893,7 +871,7 @@ do_clause(RaceList, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
PublicTables, NamedTables) ->
{DepList, IsPublic, Continue} =
get_deplist_paths(fixup_case_path(RaceList, 0), WarnVarArgs,
- RaceWarnTag, RaceVarMap, CurrLevel,
+ RaceWarnTag, RaceVarMap, CurrLevel,
PublicTables, NamedTables),
{fixup_case_rest_paths(RaceList, 0), DepList, IsPublic, Continue}.
@@ -963,7 +941,7 @@ fixup_race_forward_helper(CurrFun, CurrFunLabel, Fun, FunLabel,
#curr_fun{mfa = NewCurrFun, label = NewCurrFunLabel,
var_map = NewRaceVarMap, def_vars = NewFunDefVars,
call_vars = NewFunCallVars, arg_types = NewFunArgTypes},
- NewCode, NewNestingLevel} =
+ NewCode, NewNestingLevel} =
remove_clause(RaceList,
#curr_fun{mfa = CurrFun, label = CurrFunLabel, var_map = RaceVarMap,
def_vars = FunDefVars, call_vars = FunCallVars,
@@ -995,7 +973,7 @@ fixup_race_forward_helper(CurrFun, CurrFunLabel, Fun, FunLabel,
arg_types = NewFunTypes}],
[#curr_fun{status = in, mfa = Fun,
label = FunLabel, var_map = NewRaceVarMap,
- def_vars = Args, call_vars = NewFunArgs,
+ def_vars = Args, call_vars = NewFunArgs,
arg_types = NewFunTypes}|
lists:reverse(StateRaceList)] ++
RetC, NewRaceVarMap),
@@ -1060,7 +1038,7 @@ fixup_race_backward(CurrFun, Calls, CallsToAnalyze, Parents, Height) ->
case Height =:= 0 of
true -> Parents;
false ->
- case Calls of
+ case Calls of
[] ->
case is_integer(CurrFun) orelse lists:member(CurrFun, Parents) of
true -> Parents;
@@ -1219,7 +1197,7 @@ are_bound_vars(Vars1, Vars2, RaceVarMap) ->
callgraph__renew_tables(Table, Callgraph) ->
case Table of
{named, NameLabel, Names} ->
- PTablesToAdd =
+ PTablesToAdd =
case NameLabel of
?no_label -> [];
_Other -> [NameLabel]
@@ -1438,7 +1416,7 @@ lists_key_members_lists_helper(Elem, List, N) when is_integer(Elem) ->
end;
lists_key_members_lists_helper(_Elem, _List, _N) ->
[0].
-
+
lists_key_replace(N, List, NewMember) ->
{Before, [_|After]} = lists:split(N - 1, List),
Before ++ [NewMember|After].
@@ -1488,7 +1466,7 @@ refine_race_helper(RaceCall, VarArgs, WarnVarArgs, RaceWarnTag, DependencyList,
false -> DependencyList
end.
-remove_clause(RaceList, CurrTuple, Code, NestingLevel) ->
+remove_clause(RaceList, CurrTuple, Code, NestingLevel) ->
NewRaceList = fixup_case_rest_paths(RaceList, 0),
{NewCurrTuple, NewCode} =
cleanup_clause_code(CurrTuple, Code, 0, NestingLevel),
@@ -1621,7 +1599,7 @@ compare_ets_insert(OldWarnVarArgs, NewWarnVarArgs, RaceVarMap) ->
end
end,
case Bool of
- true ->
+ true ->
case any_args(Old4) of
true ->
case compare_list_vars(Old3, ets_list_args(New3), [], RaceVarMap) of
@@ -1690,7 +1668,6 @@ compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) ->
false ->
compare_var_list(VA1, WVA1, RaceVarMap) orelse
compare_argtypes(VA2, WVA2)
-
end
end;
?WARN_WHEREIS_UNREGISTER ->
@@ -1704,7 +1681,6 @@ compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) ->
false ->
compare_var_list(VA1, WVA1, RaceVarMap) orelse
compare_argtypes(VA2, WVA2)
-
end
end;
?WARN_ETS_LOOKUP_INSERT ->
@@ -1716,12 +1692,12 @@ compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) ->
false ->
case any_args(WVA2) of
true -> compare_var_list(VA1, WVA1, RaceVarMap);
- false ->
+ false ->
compare_var_list(VA1, WVA1, RaceVarMap) orelse
compare_argtypes(VA2, WVA2)
end
end,
- Bool andalso
+ Bool andalso
(case any_args(VA4) of
true ->
compare_var_list(VA3, WVA3, RaceVarMap);
@@ -2158,7 +2134,7 @@ race_var_map_guard_helper1(Arg, Pats, RaceVarMap, Op) ->
_Else -> {RaceVarMap, false}
end;
false -> {RaceVarMap, false}
- end;
+ end;
_Other -> {RaceVarMap, false}
end;
_Other -> {RaceVarMap, false}
@@ -2241,7 +2217,7 @@ var_analysis(FunDefArgs, FunCallArgs, WarnVarArgs, RaceWarnTag) ->
[WVA1, WVA2|T] = WarnVarArgs,
ArgNos = lists_key_members_lists(WVA1, FunDefArgs),
[[lists_get(N, FunCallArgs) || N <- ArgNos], WVA2|T]
- end.
+ end.
var_type_analysis(FunDefArgs, FunCallTypes, WarnVarArgs, RaceWarnTag,
RaceVarMap, CleanState) ->
@@ -2286,7 +2262,7 @@ var_type_analysis(FunDefArgs, FunCallTypes, WarnVarArgs, RaceWarnTag,
ets_tuple_argtypes1(lists:nth(N2 + 1, FunVarArgs), [], [], 0),
[]),
FirstVarArg ++ [Vars2, NewWVA4]
-
+
end;
?WARN_MNESIA_DIRTY_READ_WRITE ->
[WVA1, WVA2|T] = WarnVarArgs,
@@ -2330,7 +2306,7 @@ get_race_warn(Fun, Args, ArgTypes, DepList, State) ->
-spec get_race_warnings(races(), dialyzer_dataflow:state()) ->
{races(), dialyzer_dataflow:state()}.
-
+
get_race_warnings(#races{race_warnings = RaceWarnings}, State) ->
get_race_warnings_helper(RaceWarnings, State).
@@ -2430,12 +2406,12 @@ end_clause_new(Arg, Pats, Guard) ->
#end_clause{arg = Arg, pats = Pats, guard = Guard}.
-spec get_curr_fun(races()) -> mfa_or_funlbl().
-
+
get_curr_fun(#races{curr_fun = CurrFun}) ->
CurrFun.
-spec get_curr_fun_args(races()) -> core_args().
-
+
get_curr_fun_args(#races{curr_fun_args = CurrFunArgs}) ->
CurrFunArgs.
@@ -2445,17 +2421,17 @@ get_new_table(#races{new_table = Table}) ->
Table.
-spec get_race_analysis(races()) -> boolean().
-
+
get_race_analysis(#races{race_analysis = RaceAnalysis}) ->
RaceAnalysis.
-spec get_race_list(races()) -> code().
-
+
get_race_list(#races{race_list = RaceList}) ->
RaceList.
-spec get_race_list_size(races()) -> non_neg_integer().
-
+
get_race_list_size(#races{race_list_size = RaceListSize}) ->
RaceListSize.
@@ -2483,10 +2459,10 @@ put_fun_args(Args, #races{curr_fun_args = CurrFunArgs} = Races) ->
empty -> Races#races{curr_fun_args = Args};
_Other -> Races
end.
-
+
-spec put_race_analysis(boolean(), races()) ->
races().
-
+
put_race_analysis(Analysis, Races) ->
Races#races{race_analysis = Analysis}.
diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl
index 1ff4783852..24d6013692 100644
--- a/lib/dialyzer/src/dialyzer_succ_typings.erl
+++ b/lib/dialyzer/src/dialyzer_succ_typings.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -131,8 +131,9 @@ get_warnings_from_modules([M|Ms], State, DocPlt,
%% Check if there are contracts for functions that do not exist
Warnings1 =
dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph),
- {Warnings2, FunTypes, RaceCode, PublicTables, NamedTables} =
+ {RawWarnings2, FunTypes, RaceCode, PublicTables, NamedTables} =
dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused),
+ {NewAcc, Warnings2} = postprocess_dataflow_warns(RawWarnings2, State, Acc),
Attrs = cerl:module_attrs(ModCode),
Warnings3 = if BehavioursChk ->
dialyzer_behaviours:check_callbacks(M, Attrs,
@@ -145,10 +146,31 @@ get_warnings_from_modules([M|Ms], State, DocPlt,
NamedTables),
State1 = st__renew_state_calls(NewCallgraph, State),
get_warnings_from_modules(Ms, State1, NewDocPlt, BehavioursChk,
- [Warnings1, Warnings2, Warnings3|Acc]);
+ [Warnings1, Warnings2, Warnings3|NewAcc]);
get_warnings_from_modules([], #st{plt = Plt}, DocPlt, _, Acc) ->
{lists:flatten(Acc), Plt, DocPlt}.
+postprocess_dataflow_warns(RawWarnings, State, WarnAcc) ->
+ postprocess_dataflow_warns(RawWarnings, State, WarnAcc, []).
+
+postprocess_dataflow_warns([], _State, WAcc, Acc) ->
+ {WAcc, lists:reverse(Acc)};
+postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {File, CallL}, Msg}|Rest],
+ #st{codeserver = Codeserver} = State, WAcc, Acc) ->
+ {contract_range, [Contract, M, F, A, ArgStrings, CRet]} = Msg,
+ {ok, {{File, _ContrL} = FileLine, _C}} =
+ dialyzer_codeserver:lookup_mfa_contract({M,F,A}, Codeserver),
+ NewMsg =
+ {contract_range, [Contract, M, F, ArgStrings, CallL, CRet]},
+ W = {?WARN_CONTRACT_RANGE, FileLine, NewMsg},
+ Filter =
+ fun({?WARN_CONTRACT_TYPES, FL, _}) when FL =:= FileLine -> false;
+ (_) -> true
+ end,
+ postprocess_dataflow_warns(Rest, State, lists:filter(Filter, WAcc), [W|Acc]);
+postprocess_dataflow_warns([W|Rest], State, Wacc, Acc) ->
+ postprocess_dataflow_warns(Rest, State, Wacc, [W|Acc]).
+
refine_succ_typings(ModulePostorder, State) ->
?debug("Module postorder: ~p\n", [ModulePostorder]),
refine_succ_typings(ModulePostorder, State, []).
@@ -435,7 +457,7 @@ format_scc(SCC) ->
%%
%% ============================================================================
--spec doit(module() | string()) -> 'ok'.
+-spec doit(atom() | file:filename()) -> 'ok'.
doit(Module) ->
{ok, AbstrCode} = dialyzer_utils:get_abstract_code_from_src(Module),
diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl
index 35b283a00a..c45615d670 100644
--- a/lib/dialyzer/src/dialyzer_typesig.erl
+++ b/lib/dialyzer/src/dialyzer_typesig.erl
@@ -21,7 +21,7 @@
%%%-------------------------------------------------------------------
%%% File : dialyzer_typesig.erl
%%% Author : Tobias Lindahl <[email protected]>
-%%% Description :
+%%% Description :
%%%
%%% Created : 25 Apr 2005 by Tobias Lindahl <[email protected]>
%%%-------------------------------------------------------------------
@@ -31,12 +31,12 @@
-export([analyze_scc/5]).
-export([get_safe_underapprox/2]).
--import(erl_types,
+-import(erl_types,
[t_any/0, t_atom/0, t_atom_vals/1,
t_binary/0, t_bitstr/0, t_bitstr/2, t_bitstr_concat/1, t_boolean/0,
t_collect_vars/1, t_cons/2, t_cons_hd/1, t_cons_tl/1,
t_float/0, t_from_range/2, t_from_term/1,
- t_fun/0, t_fun/2, t_fun_args/1, t_fun_range/1,
+ t_fun/0, t_fun/2, t_fun_args/1, t_fun_range/1,
t_has_var/1,
t_inf/2, t_inf/3, t_integer/0,
t_is_any/1, t_is_atom/1, t_is_atom/2, t_is_cons/1, t_is_equal/2,
@@ -44,7 +44,7 @@
t_is_integer/1, t_non_neg_integer/0,
t_is_list/1, t_is_nil/1, t_is_none/1, t_is_number/1,
- t_is_subtype/2, t_limit/2, t_list/0, t_list/1,
+ t_is_subtype/2, t_limit/2, t_list/0, t_list/1,
t_list_elements/1, t_nonempty_list/1, t_maybe_improper_list/0,
t_module/0, t_number/0, t_number_vals/1,
t_opaque_match_record/2, t_opaque_matching_structure/2,
@@ -101,7 +101,7 @@
name_map = dict:new() :: dict(),
next_label :: label(),
non_self_recs = [] :: [label()],
- plt :: dialyzer_plt:plt(),
+ plt :: dialyzer_plt:plt(),
prop_types = dict:new() :: dict(),
records = dict:new() :: dict(),
opaques = [] :: [erl_types:erl_type()],
@@ -140,8 +140,8 @@
%% where Def = {Var, Fun} as in the Core Erlang module definitions.
%% Records = dict(RecName, {Arity, [{FieldName, FieldType}]})
%% NextLabel - An integer that is higher than any label in the code.
-%% CallGraph - A callgraph as produced by dialyzer_callgraph.erl
-%% Note: The callgraph must have been built with all the
+%% CallGraph - A callgraph as produced by dialyzer_callgraph.erl
+%% Note: The callgraph must have been built with all the
%% code that the SCC is a part of.
%% PLT - A dialyzer PLT. This PLT should contain available information
%% about functions that can be called by this SCC.
@@ -150,7 +150,7 @@
%%-----------------------------------------------------------------------------
-spec analyze_scc(typesig_scc(), label(),
- dialyzer_callgraph:callgraph(),
+ dialyzer_callgraph:callgraph(),
dialyzer_plt:plt(), dict()) -> dict().
analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes) ->
@@ -202,7 +202,7 @@ traverse(Tree, DefinedVars, State) ->
{State1, OpType} = traverse(Op, DefinedVars, State0),
{State2, FunType} = state__get_fun_prototype(OpType, Arity, State1),
State3 = state__store_conj(FunType, eq, OpType, State2),
- State4 = state__store_conj(mk_var(Tree), sub, t_fun_range(FunType),
+ State4 = state__store_conj(mk_var(Tree), sub, t_fun_range(FunType),
State3),
State5 = state__store_conj_lists(ArgTypes, sub, t_fun_args(FunType),
State4),
@@ -216,7 +216,7 @@ traverse(Tree, DefinedVars, State) ->
end
end;
binary ->
- {State1, SegTypes} = traverse_list(cerl:binary_segments(Tree),
+ {State1, SegTypes} = traverse_list(cerl:binary_segments(Tree),
DefinedVars, State),
Type = mk_fun_var(fun(Map) ->
TmpSegTypes = lookup_type_list(SegTypes, Map),
@@ -227,7 +227,7 @@ traverse(Tree, DefinedVars, State) ->
Size = cerl:bitstr_size(Tree),
UnitVal = cerl:int_val(cerl:bitstr_unit(Tree)),
Val = cerl:bitstr_val(Tree),
- {State1, [SizeType, ValType]} =
+ {State1, [SizeType, ValType]} =
traverse_list([Size, Val], DefinedVars, State),
{State2, TypeConstr} =
case cerl:bitstr_bitsize(Tree) of
@@ -250,7 +250,7 @@ traverse(Tree, DefinedVars, State) ->
case state__is_in_match(State1) of
true ->
Flags = cerl:concrete(cerl:bitstr_flags(Tree)),
- mk_fun_var(bitstr_val_constr(SizeType, UnitVal, Flags),
+ mk_fun_var(bitstr_val_constr(SizeType, UnitVal, Flags),
[SizeType]);
false -> t_integer()
end;
@@ -282,7 +282,7 @@ traverse(Tree, DefinedVars, State) ->
false ->
ConsVar = mk_var(Tree),
ConsType = mk_fun_var(fun(Map) ->
- t_cons(lookup_type(HdVar, Map),
+ t_cons(lookup_type(HdVar, Map),
lookup_type(TlVar, Map))
end, [HdVar, TlVar]),
HdType = mk_fun_var(fun(Map) ->
@@ -299,8 +299,8 @@ traverse(Tree, DefinedVars, State) ->
true -> t_cons_tl(Cons)
end
end, [ConsVar]),
- State2 = state__store_conj_lists([HdVar, TlVar, ConsVar], sub,
- [HdType, TlType, ConsType],
+ State2 = state__store_conj_lists([HdVar, TlVar, ConsVar], sub,
+ [HdType, TlType, ConsType],
State1),
{State2, ConsVar}
end;
@@ -314,33 +314,35 @@ traverse(Tree, DefinedVars, State) ->
error -> t_fun(length(Vars), t_none());
{ok, Dom} -> t_fun(Dom, t_none())
end,
- State2 =
+ TreeVar = mk_var(Tree),
+ State2 =
try
State1 = case state__add_prop_constrs(Tree, State0) of
not_called -> State0;
PropState -> PropState
end,
{BodyState, BodyVar} = traverse(Body, DefinedVars1, State1),
- state__store_conj(mk_var(Tree), eq,
+ state__store_conj(TreeVar, eq,
t_fun(mk_var_list(Vars), BodyVar), BodyState)
catch
throw:error ->
- state__store_conj(mk_var(Tree), eq, FunFailType, State0)
+ state__store_conj(TreeVar, eq, FunFailType, State0)
end,
Cs = state__cs(State2),
- State3 = state__store_constrs(mk_var(Tree), Cs, State2),
- Ref = mk_constraint_ref(mk_var(Tree), get_deps(Cs)),
+ State3 = state__store_constrs(TreeVar, Cs, State2),
+ Ref = mk_constraint_ref(TreeVar, get_deps(Cs)),
OldCs = state__cs(State),
State4 = state__new_constraint_context(State3),
State5 = state__store_conj_list([OldCs, Ref], State4),
State6 = state__store_fun_arity(Tree, State5),
- {State6, mk_var(Tree)};
+ State7 = state__add_fun_to_scc(TreeVar, State6),
+ {State7, TreeVar};
'let' ->
Vars = cerl:let_vars(Tree),
Arg = cerl:let_arg(Tree),
Body = cerl:let_body(Tree),
{State1, ArgVars} = traverse(Arg, DefinedVars, State),
- State2 = state__store_conj(t_product(mk_var_list(Vars)), eq,
+ State2 = state__store_conj(t_product(mk_var_list(Vars)), eq,
ArgVars, State1),
DefinedVars1 = add_def_list(Vars, DefinedVars),
traverse(Body, DefinedVars1, State2);
@@ -353,12 +355,12 @@ traverse(Tree, DefinedVars, State) ->
DefinedVars1 = add_def_list(Vars, DefinedVars),
{State2, _} = traverse_list(Funs, DefinedVars1, State1),
traverse(Body, DefinedVars1, State2);
- literal ->
+ literal ->
%% This is needed for finding records
case cerl:unfold_literal(Tree) of
- Tree ->
+ Tree ->
Type = t_from_term(cerl:concrete(Tree)),
- NewType =
+ NewType =
case erl_types:t_opaque_match_atom(Type, State#state.opaques) of
[Opaque] -> Opaque;
_ -> Type
@@ -370,7 +372,7 @@ traverse(Tree, DefinedVars, State) ->
Defs = cerl:module_defs(Tree),
Funs = [Fun || {_Var, Fun} <- Defs],
Vars = [Var || {Var, _Fun} <- Defs],
- DefinedVars1 = add_def_list(Vars, DefinedVars),
+ DefinedVars1 = add_def_list(Vars, DefinedVars),
State1 = state__store_funs(Vars, Funs, State),
FoldFun = fun(Fun, AccState) ->
{S, _} = traverse(Fun, DefinedVars1,
@@ -388,7 +390,7 @@ traverse(Tree, DefinedVars, State) ->
'receive' ->
Clauses = filter_match_fail(cerl:receive_clauses(Tree)),
Timeout = cerl:receive_timeout(Tree),
- case (cerl:is_c_atom(Timeout) andalso
+ case (cerl:is_c_atom(Timeout) andalso
(cerl:atom_val(Timeout) =:= infinity)) of
true ->
handle_clauses(Clauses, mk_var(Tree), [], DefinedVars, State);
@@ -421,7 +423,7 @@ traverse(Tree, DefinedVars, State) ->
case t_has_var(Var) of
true ->
{AccState1, NewVar} = state__mk_var(AccState),
- {NewVar,
+ {NewVar,
state__store_conj(Var, eq, NewVar, AccState1)};
false ->
{Var, AccState}
@@ -431,7 +433,7 @@ traverse(Tree, DefinedVars, State) ->
{TmpState, t_tuple(NewEvars)}
end,
case Elements of
- [Tag|Fields] ->
+ [Tag|Fields] ->
case cerl:is_c_atom(Tag) of
true ->
%% Check if an opaque term is constructed.
@@ -534,7 +536,7 @@ handle_try(Tree, DefinedVars, State) ->
Handler = cerl:try_handler(Tree),
State1 = state__new_constraint_context(State),
{ArgBodyState, BodyVar} =
- try
+ try
{State2, ArgVar} = traverse(Arg, DefinedVars, State1),
DefinedVars1 = add_def_list(Vars, DefinedVars),
{State3, BodyVar1} = traverse(Body, DefinedVars1, State2),
@@ -542,17 +544,17 @@ handle_try(Tree, DefinedVars, State) ->
State3),
{State4, BodyVar1}
catch
- throw:error ->
+ throw:error ->
{State1, t_none()}
end,
State6 = state__new_constraint_context(ArgBodyState),
{HandlerState, HandlerVar} =
try
- DefinedVars2 = add_def_list([X || X <- EVars, cerl:is_c_var(X)],
+ DefinedVars2 = add_def_list([X || X <- EVars, cerl:is_c_var(X)],
DefinedVars),
traverse(Handler, DefinedVars2, State6)
catch
- throw:error ->
+ throw:error ->
{State6, t_none()}
end,
ArgBodyCs = state__cs(ArgBodyState),
@@ -561,7 +563,7 @@ handle_try(Tree, DefinedVars, State) ->
OldCs = state__cs(State),
case state__is_in_guard(State) of
true ->
- Conj1 = mk_conj_constraint_list([ArgBodyCs,
+ Conj1 = mk_conj_constraint_list([ArgBodyCs,
mk_constraint(BodyVar, eq, TreeVar)]),
Disj = mk_disj_constraint_list([Conj1,
mk_constraint(HandlerVar, eq, TreeVar)]),
@@ -573,14 +575,14 @@ handle_try(Tree, DefinedVars, State) ->
{NewCs, ReturnVar} =
case {t_is_none(BodyVar), t_is_none(HandlerVar)} of
{false, false} ->
- Conj1 =
+ Conj1 =
mk_conj_constraint_list([ArgBodyCs,
mk_constraint(TreeVar, eq, BodyVar)]),
- Conj2 =
+ Conj2 =
mk_conj_constraint_list([HandlerCs,
mk_constraint(TreeVar, eq, HandlerVar)]),
Disj = mk_disj_constraint_list([Conj1, Conj2]),
- {Disj, mk_var(Tree)};
+ {Disj, TreeVar};
{false, true} ->
{mk_conj_constraint_list([ArgBodyCs,
mk_constraint(TreeVar, eq, BodyVar)]),
@@ -603,7 +605,7 @@ handle_try(Tree, DefinedVars, State) ->
%% Call
%%
-handle_call(Call, DefinedVars, State) ->
+handle_call(Call, DefinedVars, State) ->
Args = cerl:call_args(Call),
Mod = cerl:call_module(Call),
Fun = cerl:call_name(Call),
@@ -618,7 +620,7 @@ handle_call(Call, DefinedVars, State) ->
case state__lookup_rec_var_in_scope(MFA, State) of
error ->
case get_bif_constr(MFA, Dst, ArgVars, State1) of
- none ->
+ none ->
{get_plt_constr(MFA, Dst, ArgVars, State1), Dst};
C ->
{state__store_conj(C, State1), Dst}
@@ -647,7 +649,7 @@ get_plt_constr(MFA, Dst, ArgVars, State) ->
case PltRes of
none -> State;
{value, {PltRetType, PltArgTypes}} ->
- state__store_conj_lists([Dst|ArgVars], sub,
+ state__store_conj_lists([Dst|ArgVars], sub,
[PltRetType|PltArgTypes], State)
end;
{value, #contract{args = GenArgs} = C} ->
@@ -655,7 +657,7 @@ get_plt_constr(MFA, Dst, ArgVars, State) ->
case PltRes of
none ->
{mk_fun_var(fun(Map) ->
- ArgTypes = lookup_type_list(ArgVars, Map),
+ ArgTypes = lookup_type_list(ArgVars, Map),
dialyzer_contracts:get_contract_return(C, ArgTypes)
end, ArgVars), GenArgs};
{value, {PltRetType, PltArgTypes}} ->
@@ -692,7 +694,7 @@ filter_match_fail([Clause] = Cls) ->
filter_match_fail([H|T]) ->
[H|filter_match_fail(T)];
filter_match_fail([]) ->
- %% This can actually happen, for example in
+ %% This can actually happen, for example in
%% receive after 1 -> ok end
[].
@@ -714,16 +716,16 @@ handle_clauses(Clauses, TopVar, Arg, Action, DefinedVars, State) ->
if length(Clauses) > ?MAX_NOF_CLAUSES -> overflow;
true -> []
end,
- {State1, CList} = handle_clauses_1(Clauses, TopVar, Arg, DefinedVars,
+ {State1, CList} = handle_clauses_1(Clauses, TopVar, Arg, DefinedVars,
State, SubtrTypeList, []),
{NewCs, NewState} =
case Action of
- none ->
+ none ->
if CList =:= [] -> throw(error);
true -> {CList, State1}
end;
- _ ->
- try
+ _ ->
+ try
{State2, ActionVar} = traverse(Action, DefinedVars, State1),
TmpC = mk_constraint(TopVar, eq, ActionVar),
ActionCs = mk_conj_constraint_list([state__cs(State2),TmpC]),
@@ -740,7 +742,7 @@ handle_clauses(Clauses, TopVar, Arg, Action, DefinedVars, State) ->
FinalState = state__new_constraint_context(NewState),
{state__store_conj_list([OldCs, NewCList], FinalState), TopVar}.
-handle_clauses_1([Clause|Tail], TopVar, Arg, DefinedVars,
+handle_clauses_1([Clause|Tail], TopVar, Arg, DefinedVars,
State, SubtrTypes, Acc) ->
State0 = state__new_constraint_context(State),
Pats = cerl:clause_pats(Clause),
@@ -749,22 +751,22 @@ handle_clauses_1([Clause|Tail], TopVar, Arg, DefinedVars,
NewSubtrTypes =
case SubtrTypes =:= overflow of
true -> overflow;
- false ->
+ false ->
ordsets:add_element(get_safe_underapprox(Pats, Guard), SubtrTypes)
end,
- try
+ try
DefinedVars1 = add_def_from_tree_list(Pats, DefinedVars),
State1 = state__set_in_match(State0, true),
{State2, PatVars} = traverse_list(Pats, DefinedVars1, State1),
State3 =
case Arg =:= [] of
true -> State2;
- false ->
+ false ->
S = state__store_conj(Arg, eq, t_product(PatVars), State2),
case SubtrTypes =:= overflow of
true -> S;
false ->
- SubtrPatVar = mk_fun_var(fun(Map) ->
+ SubtrPatVar = mk_fun_var(fun(Map) ->
TmpType = lookup_type(Arg, Map),
t_subtract_list(TmpType, SubtrTypes)
end, [Arg]),
@@ -772,15 +774,15 @@ handle_clauses_1([Clause|Tail], TopVar, Arg, DefinedVars,
end
end,
State4 = handle_guard(Guard, DefinedVars1, State3),
- {State5, BodyVar} = traverse(Body, DefinedVars1,
+ {State5, BodyVar} = traverse(Body, DefinedVars1,
state__set_in_match(State4, false)),
State6 = state__store_conj(TopVar, eq, BodyVar, State5),
Cs = state__cs(State6),
- handle_clauses_1(Tail, TopVar, Arg, DefinedVars, State6,
+ handle_clauses_1(Tail, TopVar, Arg, DefinedVars, State6,
NewSubtrTypes, [Cs|Acc])
catch
- throw:error ->
- handle_clauses_1(Tail, TopVar, Arg, DefinedVars,
+ throw:error ->
+ handle_clauses_1(Tail, TopVar, Arg, DefinedVars,
State, NewSubtrTypes, Acc)
end;
handle_clauses_1([], _TopVar, _Arg, _DefinedVars, State, _SubtrType, Acc) ->
@@ -792,7 +794,7 @@ get_safe_underapprox(Pats, Guard) ->
try
Map1 = cerl_trees:fold(fun(X, Acc) ->
case cerl:is_c_var(X) of
- true ->
+ true ->
dict:store(cerl_trees:get_label(X), t_any(),
Acc);
false -> Acc
@@ -804,8 +806,8 @@ get_safe_underapprox(Pats, Guard) ->
false ->
case cerl:is_c_var(Guard) of
false -> Map2;
- true ->
- dict:store(cerl_trees:get_label(Guard),
+ true ->
+ dict:store(cerl_trees:get_label(Guard),
t_from_term(true), Map2)
end
end,
@@ -819,8 +821,8 @@ get_underapprox_from_guard(Tree, Map) ->
True = t_from_term(true),
case cerl:type(Tree) of
call ->
- case {cerl:concrete(cerl:call_module(Tree)),
- cerl:concrete(cerl:call_name(Tree)),
+ case {cerl:concrete(cerl:call_module(Tree)),
+ cerl:concrete(cerl:call_name(Tree)),
length(cerl:call_args(Tree))} of
{erlang, is_function, 2} ->
[Fun, Arity] = cerl:call_args(Tree),
@@ -856,15 +858,15 @@ get_underapprox_from_guard(Tree, Map) ->
{erlang, '==', 2} -> throw(dont_know);
{erlang, 'and', 2} ->
[Arg1, Arg2] = cerl:call_args(Tree),
- case ((cerl:is_c_var(Arg1) orelse cerl:is_literal(Arg1))
+ case ((cerl:is_c_var(Arg1) orelse cerl:is_literal(Arg1))
andalso
(cerl:is_c_var(Arg2) orelse cerl:is_literal(Arg2))) of
true ->
{Arg1Type, _} = get_underapprox_from_guard(Arg1, Map),
{Arg2Type, _} = get_underapprox_from_guard(Arg2, Map),
- case (t_is_equal(True, Arg1Type) andalso
+ case (t_is_equal(True, Arg1Type) andalso
t_is_equal(True, Arg2Type)) of
- true -> {True, Map};
+ true -> {True, Map};
false -> throw(dont_know)
end;
false ->
@@ -876,7 +878,7 @@ get_underapprox_from_guard(Tree, Map) ->
end
end;
var ->
- Type =
+ Type =
case dict:find(cerl_trees:get_label(Tree), Map) of
error -> throw(dont_know);
{ok, T} -> T
@@ -931,7 +933,7 @@ bitstr_constr(SizeType, UnitVal) ->
MinSize = erl_types:number_min(TmpSizeType),
t_bitstr(UnitVal, MinSize * UnitVal)
end;
- false ->
+ false ->
t_bitstr(UnitVal, 0)
end
end.
@@ -975,9 +977,9 @@ get_safe_underapprox_1([Pat|Left], Acc, Map) ->
end;
binary ->
%% TODO: Can maybe do something here
- throw(dont_know);
+ throw(dont_know);
cons ->
- {[Hd, Tl], Map1} =
+ {[Hd, Tl], Map1} =
get_safe_underapprox_1([cerl:cons_hd(Pat), cerl:cons_tl(Pat)], [], Map),
case t_is_any(Tl) of
true -> get_safe_underapprox_1(Left, [t_nonempty_list(Hd)|Acc], Map1);
@@ -1020,7 +1022,7 @@ get_safe_underapprox_1([], Acc, Map) ->
%% Guards
%%
-handle_guard(Guard, DefinedVars, State) ->
+handle_guard(Guard, DefinedVars, State) ->
True = t_from_term(true),
State1 = state__set_in_guard(State, true),
State2 = state__new_constraint_context(State1),
@@ -1039,7 +1041,7 @@ handle_guard(Guard, DefinedVars, State) ->
%%
%%=============================================================================
-get_bif_constr({erlang, Op, 2}, Dst, Args = [Arg1, Arg2], _State)
+get_bif_constr({erlang, Op, 2}, Dst, Args = [Arg1, Arg2], _State)
when Op =:= '+'; Op =:= '-'; Op =:= '*' ->
ReturnType = mk_fun_var(fun(Map) ->
TmpArgTypes = lookup_type_list(Args, Map),
@@ -1047,14 +1049,14 @@ get_bif_constr({erlang, Op, 2}, Dst, Args = [Arg1, Arg2], _State)
end, Args),
ArgFun =
fun(A, Pos) ->
- F =
+ F =
fun(Map) ->
DstType = lookup_type(Dst, Map),
AType = lookup_type(A, Map),
case t_is_integer(DstType) of
true ->
case t_is_integer(AType) of
- true ->
+ true ->
eval_inv_arith(Op, Pos, DstType, AType);
false ->
%% This must be temporary.
@@ -1062,7 +1064,7 @@ get_bif_constr({erlang, Op, 2}, Dst, Args = [Arg1, Arg2], _State)
end;
false ->
case t_is_float(DstType) of
- true ->
+ true ->
case t_is_integer(AType) of
true -> t_float();
false -> t_number()
@@ -1079,9 +1081,9 @@ get_bif_constr({erlang, Op, 2}, Dst, Args = [Arg1, Arg2], _State)
mk_conj_constraint_list([mk_constraint(Dst, sub, ReturnType),
mk_constraint(Arg1, sub, Arg1FunVar),
mk_constraint(Arg2, sub, Arg2FunVar)]);
-get_bif_constr({erlang, Op, 2}, Dst, [Arg1, Arg2] = Args, _State)
+get_bif_constr({erlang, Op, 2}, Dst, [Arg1, Arg2] = Args, _State)
when Op =:= '<'; Op =:= '=<'; Op =:= '>'; Op =:= '>=' ->
- ArgFun =
+ ArgFun =
fun(LocalArg1, LocalArg2, LocalOp) ->
fun(Map) ->
DstType = lookup_type(Dst, Map),
@@ -1098,19 +1100,19 @@ get_bif_constr({erlang, Op, 2}, Dst, [Arg1, Arg2] = Args, _State)
Max2 = erl_types:number_max(Arg2Type),
Min2 = erl_types:number_min(Arg2Type),
case LocalOp of
- '=<' ->
+ '=<' ->
if IsTrue -> t_from_range(Min1, Max2);
IsFalse -> t_from_range(range_inc(Min2), Max1)
end;
- '<' ->
+ '<' ->
if IsTrue -> t_from_range(Min1, range_dec(Max2));
IsFalse -> t_from_range(Min2, Max1)
end;
- '>=' ->
+ '>=' ->
if IsTrue -> t_from_range(Min2, Max1);
IsFalse -> t_from_range(Min1, range_dec(Max2))
end;
- '>' ->
+ '>' ->
if IsTrue -> t_from_range(range_inc(Min2), Max1);
IsFalse -> t_from_range(Min1, Max2)
end
@@ -1131,7 +1133,7 @@ get_bif_constr({erlang, Op, 2}, Dst, [Arg1, Arg2] = Args, _State)
DstArgs = [Dst, Arg1, Arg2],
Arg1Var = mk_fun_var(Arg1Fun, DstArgs),
Arg2Var = mk_fun_var(Arg2Fun, DstArgs),
- DstVar = mk_fun_var(fun(Map) ->
+ DstVar = mk_fun_var(fun(Map) ->
TmpArgTypes = lookup_type_list(Args, Map),
erl_bif_types:type(erlang, Op, 2, TmpArgTypes)
end, Args),
@@ -1143,9 +1145,9 @@ get_bif_constr({erlang, '++', 2}, Dst, [Hd, Tl] = Args, _State) ->
DstType = lookup_type(Dst, Map),
case t_is_cons(DstType) of
true -> t_list(t_cons_hd(DstType));
- false ->
+ false ->
case t_is_list(DstType) of
- true ->
+ true ->
case t_is_nil(DstType) of
true -> DstType;
false -> t_list(t_list_elements(DstType))
@@ -1160,7 +1162,7 @@ get_bif_constr({erlang, '++', 2}, Dst, [Hd, Tl] = Args, _State) ->
true -> t_sup(t_cons_tl(DstType), DstType);
false ->
case t_is_list(DstType) of
- true ->
+ true ->
case t_is_nil(DstType) of
true -> DstType;
false -> t_list(t_list_elements(DstType))
@@ -1170,10 +1172,10 @@ get_bif_constr({erlang, '++', 2}, Dst, [Hd, Tl] = Args, _State) ->
end
end,
DstL = [Dst],
- HdVar = mk_fun_var(HdFun, DstL),
+ HdVar = mk_fun_var(HdFun, DstL),
TlVar = mk_fun_var(TlFun, DstL),
ArgTypes = erl_bif_types:arg_types(erlang, '++', 2),
- ReturnType = mk_fun_var(fun(Map) ->
+ ReturnType = mk_fun_var(fun(Map) ->
TmpArgTypes = lookup_type_list(Args, Map),
erl_bif_types:type(erlang, '++', 2, TmpArgTypes)
end, Args),
@@ -1198,7 +1200,7 @@ get_bif_constr({erlang, is_function, 2}, Dst, [Fun, Arity], _State) ->
ArgFun = fun(Map) ->
DstType = lookup_type(Dst, Map),
case t_is_atom(true, DstType) of
- true ->
+ true ->
ArityType = lookup_type(Arity, Map),
case t_number_vals(ArityType) of
unknown -> t_fun();
@@ -1231,7 +1233,7 @@ get_bif_constr({erlang, is_record, 2}, Dst, [Var, Tag] = Args, _State) ->
end
end,
ArgV = mk_fun_var(ArgFun, [Dst]),
- DstFun = fun(Map) ->
+ DstFun = fun(Map) ->
TmpArgTypes = lookup_type_list(Args, Map),
erl_bif_types:type(erlang, is_record, 2, TmpArgTypes)
end,
@@ -1241,7 +1243,7 @@ get_bif_constr({erlang, is_record, 2}, Dst, [Var, Tag] = Args, _State) ->
mk_constraint(Var, sub, ArgV)]);
get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
%% TODO: Revise this to make it precise for Tag and Arity.
- ArgFun =
+ ArgFun =
fun(Map) ->
case t_is_atom(true, lookup_type(Dst, Map)) of
true ->
@@ -1257,14 +1259,14 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
GenRecord = t_tuple([TagType|AnyElems]),
case t_atom_vals(TagType) of
[TagVal] ->
- case state__lookup_record(State, TagVal,
+ case state__lookup_record(State, TagVal,
ArityVal - 1) of
{ok, Type} ->
AllOpaques = State#state.opaques,
case t_opaque_match_record(Type, AllOpaques) of
[Opaque] -> Opaque;
_ -> Type
- end;
+ end;
error -> GenRecord
end;
_ -> GenRecord
@@ -1279,9 +1281,9 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
end
end,
ArgV = mk_fun_var(ArgFun, [Tag, Arity, Dst]),
- DstFun = fun(Map) ->
+ DstFun = fun(Map) ->
[TmpVar, TmpTag, TmpArity] = TmpArgTypes = lookup_type_list(Args, Map),
- TmpArgTypes2 =
+ TmpArgTypes2 =
case lists:member(TmpVar, State#state.opaques) of
true ->
case t_is_integer(TmpArity) of
@@ -1293,7 +1295,7 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
case t_atom_vals(TmpTag) of
[TmpTagVal] ->
case state__lookup_record(State, TmpTagVal, TmpArityVal - 1) of
- {ok, TmpType} ->
+ {ok, TmpType} ->
case t_is_none(t_inf(TmpType, TmpVar, opaque)) of
true -> TmpArgTypes;
false -> [TmpType, TmpTag, TmpArity]
@@ -1312,7 +1314,7 @@ get_bif_constr({erlang, is_record, 3}, Dst, [Var, Tag, Arity] = Args, State) ->
end,
erl_bif_types:type(erlang, is_record, 3, TmpArgTypes2)
end,
- DstV = mk_fun_var(DstFun, Args),
+ DstV = mk_fun_var(DstFun, Args),
mk_conj_constraint_list([mk_constraint(Dst, sub, DstV),
mk_constraint(Arity, sub, t_integer()),
mk_constraint(Tag, sub, t_atom()),
@@ -1334,7 +1336,7 @@ get_bif_constr({erlang, 'and', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
true -> False;
false -> t_boolean()
end;
- false ->
+ false ->
t_boolean()
end
end
@@ -1349,7 +1351,7 @@ get_bif_constr({erlang, 'and', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
case t_is_atom(false, Arg2Type) of
true -> False;
false ->
- case (t_is_atom(true, Arg1Type)
+ case (t_is_atom(true, Arg1Type)
andalso t_is_atom(true, Arg2Type)) of
true -> True;
false -> t_boolean()
@@ -1378,7 +1380,7 @@ get_bif_constr({erlang, 'or', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
true -> True;
false -> t_boolean()
end;
- false ->
+ false ->
t_boolean()
end
end
@@ -1393,7 +1395,7 @@ get_bif_constr({erlang, 'or', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
case t_is_atom(true, Arg2Type) of
true -> True;
false ->
- case (t_is_atom(false, Arg1Type)
+ case (t_is_atom(false, Arg1Type)
andalso t_is_atom(false, Arg2Type)) of
true -> False;
false -> t_boolean()
@@ -1404,9 +1406,13 @@ get_bif_constr({erlang, 'or', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
ArgV1 = mk_fun_var(ArgFun(Arg2), [Arg2, Dst]),
ArgV2 = mk_fun_var(ArgFun(Arg1), [Arg1, Dst]),
DstV = mk_fun_var(DstFun, Args),
- Disj = mk_disj_constraint_list([mk_constraint(Arg1, sub, True),
- mk_constraint(Arg2, sub, True),
- mk_constraint(Dst, sub, False)]),
+ F = fun(A) ->
+ try [mk_constraint(A, sub, True)]
+ catch throw:error -> []
+ end
+ end,
+ Constrs = F(Arg1) ++ F(Arg2),
+ Disj = mk_disj_constraint_list([mk_constraint(Dst, sub, False)|Constrs]),
mk_conj_constraint_list([mk_constraint(Dst, sub, DstV),
mk_constraint(Arg1, sub, ArgV1),
mk_constraint(Arg2, sub, ArgV2),
@@ -1414,7 +1420,7 @@ get_bif_constr({erlang, 'or', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
get_bif_constr({erlang, 'not', 1}, Dst, [Arg] = Args, _State) ->
True = t_from_term(true),
False = t_from_term(false),
- Fun = fun(Var) ->
+ Fun = fun(Var) ->
fun(Map) ->
Type = lookup_type(Var, Map),
case t_is_atom(true, Type) of
@@ -1439,7 +1445,7 @@ get_bif_constr({erlang, '=:=', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
OtherVarType = lookup_type(OtherVar, Map),
case t_is_atom(true, DstType) of
true -> OtherVarType;
- false ->
+ false ->
case t_is_atom(false, DstType) of
true ->
case is_singleton_type(OtherVarType) of
@@ -1492,7 +1498,7 @@ get_bif_constr({erlang, '==', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
true ->
case t_is_number(VarType) of
true -> t_number();
- false ->
+ false ->
case t_is_atom(VarType) of
true -> VarType;
false -> t_any()
@@ -1511,7 +1517,8 @@ get_bif_constr({erlang, '==', 2}, Dst, [Arg1, Arg2] = Args, _State) ->
mk_conj_constraint_list([mk_constraint(Dst, sub, DstV),
mk_constraint(Arg1, sub, ArgV1),
mk_constraint(Arg2, sub, ArgV2)]);
-get_bif_constr({erlang, element, 2} = _BIF, Dst, Args, State) ->
+get_bif_constr({erlang, element, 2} = _BIF, Dst, Args,
+ #state{cs = Constrs} = State) ->
GenType = erl_bif_types:type(erlang, element, 2),
case t_is_none(GenType) of
true -> ?debug("Bif: ~w failed\n", [_BIF]), throw(error);
@@ -1525,9 +1532,14 @@ get_bif_constr({erlang, element, 2} = _BIF, Dst, Args, State) ->
erl_bif_types:type(erlang, element, 2, ATs2)
end,
ReturnType = mk_fun_var(Fun, Args),
- ArgTypes = erl_bif_types:arg_types(erlang, element, 2),
+ ArgTypes = erl_bif_types:arg_types(erlang, element, 2),
Cs = mk_constraints(Args, sub, ArgTypes),
- mk_conj_constraint_list([mk_constraint(Dst, sub, ReturnType)|Cs])
+ NewCs =
+ case find_element(Args, Constrs) of
+ 'unknown' -> Cs;
+ Elem -> [mk_constraint(Dst, eq, Elem)|Cs]
+ end,
+ mk_conj_constraint_list([mk_constraint(Dst, sub, ReturnType)|NewCs])
end;
get_bif_constr({M, F, A} = _BIF, Dst, Args, State) ->
GenType = erl_bif_types:type(M, F, A),
@@ -1541,7 +1553,7 @@ get_bif_constr({M, F, A} = _BIF, Dst, Args, State) ->
false -> T
end
end,
- ReturnType = mk_fun_var(fun(Map) ->
+ ReturnType = mk_fun_var(fun(Map) ->
TmpArgTypes0 = lookup_type_list(Args, Map),
TmpArgTypes = [UnopaqueFun(T) || T<- TmpArgTypes0],
erl_bif_types:type(M, F, A, TmpArgTypes)
@@ -1561,12 +1573,12 @@ get_bif_constr({M, F, A} = _BIF, Dst, Args, State) ->
end
end.
-eval_inv_arith('+', _Pos, Dst, Arg) ->
+eval_inv_arith('+', _Pos, Dst, Arg) ->
erl_bif_types:type(erlang, '-', 2, [Dst, Arg]);
-eval_inv_arith('*', _Pos, Dst, Arg) ->
+eval_inv_arith('*', _Pos, Dst, Arg) ->
case t_number_vals(Arg) of
[0] -> t_integer();
- _ ->
+ _ ->
TmpRet = erl_bif_types:type(erlang, 'div', 2, [Dst, Arg]),
Zero = t_from_term(0),
%% If 0 is not part of the result, it cannot be part of the argument.
@@ -1575,9 +1587,9 @@ eval_inv_arith('*', _Pos, Dst, Arg) ->
true -> TmpRet
end
end;
-eval_inv_arith('-', 1, Dst, Arg) ->
+eval_inv_arith('-', 1, Dst, Arg) ->
erl_bif_types:type(erlang, '-', 2, [Arg, Dst]);
-eval_inv_arith('-', 2, Dst, Arg) ->
+eval_inv_arith('-', 2, Dst, Arg) ->
erl_bif_types:type(erlang, '+', 2, [Arg, Dst]).
range_inc(neg_inf) -> neg_inf;
@@ -1614,7 +1626,7 @@ get_bif_test_constr(Dst, Arg, Type, State) ->
end;
false -> t_from_term(false)
end;
- false ->
+ false ->
case t_is_subtype(ArgType, Type) of
true -> t_from_term(true);
false -> t_boolean()
@@ -1632,11 +1644,11 @@ get_bif_test_constr(Dst, Arg, Type, State) ->
%%=============================================================================
solve([Fun], State) ->
- ?debug("============ Analyzing Fun: ~w ===========\n",
+ ?debug("============ Analyzing Fun: ~w ===========\n",
[debug_lookup_name(Fun)]),
solve_fun(Fun, dict:new(), State);
solve([_|_] = SCC, State) ->
- ?debug("============ Analyzing SCC: ~w ===========\n",
+ ?debug("============ Analyzing SCC: ~w ===========\n",
[[debug_lookup_name(F) || F <- SCC]]),
solve_scc(SCC, dict:new(), State, false).
@@ -1655,7 +1667,7 @@ solve_fun(Fun, FunMap, State) ->
solve_scc(SCC, Map, State, TryingUnit) ->
State1 = state__mark_as_non_self_rec(SCC, State),
- Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC],
+ Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC],
Vars = [Var || {_, {ok, Var}} <- Vars0],
Funs = [Fun || {Fun, {ok, _}} <- Vars0],
Types = unsafe_lookup_type_list(Funs, Map),
@@ -1682,7 +1694,7 @@ solve_scc(SCC, Map, State, TryingUnit) ->
false ->
Map2
end;
- false ->
+ false ->
?debug("SCC ~w did not reach fixpoint\n", [SCC]),
solve_scc(SCC, Map2, State, TryingUnit)
end.
@@ -1704,29 +1716,29 @@ scc_fold_fun(F, FunMap, State) ->
format_type(NewType)]),
NewFunMap.
-solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
+solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
Map, MapDict, State) ->
- {OldLocalMap, Check} =
+ {OldLocalMap, Check} =
case dict:find(Id, MapDict) of
error -> {dict:new(), false};
{ok, M} -> {M, true}
- end,
+ end,
?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]),
CheckDeps = ordsets:del_element(t_var_name(Id), Deps),
case Check andalso maps_are_equal(OldLocalMap, Map, CheckDeps) of
- true ->
+ true ->
?debug("Equal\n", []),
{ok, MapDict, Map};
false ->
?debug("Not equal. Solving\n", []),
Cs = state__get_cs(Id, State),
- Res =
+ Res =
case state__is_self_rec(Id, State) of
true -> solve_self_recursive(Cs, Map, MapDict, Id, t_none(), State);
false -> solve_ref_or_list(Cs, Map, MapDict, State)
end,
case Res of
- {error, NewMapDict} ->
+ {error, NewMapDict} ->
?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]),
Arity = state__fun_arity(Id, State),
FunType =
@@ -1755,17 +1767,17 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps},
end;
solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id},
Map, MapDict, State) ->
- {OldLocalMap, Check} =
+ {OldLocalMap, Check} =
case dict:find(Id, MapDict) of
error -> {dict:new(), false};
{ok, M} -> {M, true}
end,
?debug("Checking ref to list: ~w\n", [Id]),
case Check andalso maps_are_equal(OldLocalMap, Map, Deps) of
- true ->
+ true ->
?debug("~w equal ~w\n", [Type, Id]),
{ok, MapDict, Map};
- false ->
+ false ->
?debug("~w not equal: ~w. Solving\n", [Type, Id]),
solve_clist(Cs, Type, Id, Deps, MapDict, Map, State)
end.
@@ -1793,7 +1805,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
[[{X, format_type(Y)} || {X, Y} <- dict:to_list(NewMap)]]),
NewRecType = unsafe_lookup_type(Id, NewMap),
case t_is_equal(NewRecType, RecType0) of
- true ->
+ true ->
{ok, NewMapDict, enter_type(RecVar, NewRecType, NewMap)};
false ->
solve_self_recursive(Cs, Map, MapDict, Id, NewRecType, State)
@@ -1801,7 +1813,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) ->
end.
solve_clist(Cs, conj, Id, Deps, MapDict, Map, State) ->
- case solve_cs(Cs, Map, MapDict, State) of
+ case solve_cs(Cs, Map, MapDict, State) of
{error, _} = Error -> Error;
{ok, NewMapDict, NewMap} = Ret ->
case Cs of
@@ -1821,12 +1833,12 @@ solve_clist(Cs, disj, Id, _Deps, MapDict, Map, State) ->
{ok, NewDict, NewMap} -> {{ok, NewMap}, NewDict};
{error, _NewDict} = Error -> Error
end
- end,
+ end,
{Maps, NewMapDict} = lists:mapfoldl(Fun, MapDict, Cs),
case [X || {ok, X} <- Maps] of
[] -> {error, NewMapDict};
- MapList ->
- NewMap = join_maps(MapList),
+ MapList ->
+ NewMap = join_maps(MapList),
{ok, dict:store(Id, NewMap, NewMapDict), NewMap}
end.
@@ -1844,13 +1856,13 @@ solve_cs([#constraint{} = C|Tail], Map, MapDict, State) ->
case solve_one_c(C, Map, State#state.opaques) of
error ->
?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n",
- [format_type(C#constraint.lhs),
+ [format_type(C#constraint.lhs),
format_type(lookup_type(C#constraint.lhs, Map)),
C#constraint.op,
- format_type(C#constraint.rhs),
+ format_type(C#constraint.rhs),
format_type(lookup_type(C#constraint.rhs, Map))]),
{error, MapDict};
- {ok, NewMap} ->
+ {ok, NewMap} ->
solve_cs(Tail, NewMap, MapDict, State)
end;
solve_cs([], Map, MapDict, _State) ->
@@ -1863,7 +1875,7 @@ solve_one_c(#constraint{lhs = Lhs, rhs = Rhs, op = Op}, Map, Opaques) ->
?debug("Solving: ~s :: ~s ~w ~s :: ~s\n\tInf: ~s\n",
[format_type(Lhs), format_type(LhsType), Op,
format_type(Rhs), format_type(RhsType), format_type(Inf)]),
- case t_is_none(Inf) of
+ case t_is_none(Inf) of
true -> error;
false ->
case Op of
@@ -1887,8 +1899,8 @@ solve_subtype(Type, Inf, Map, Opaques) ->
try t_unify(Type, Inf, Opaques) of
{_, List} -> {ok, enter_type_list(List, Map)}
catch
- throw:{mismatch, _T1, _T2} ->
- ?debug("Mismatch between ~s and ~s\n",
+ throw:{mismatch, _T1, _T2} ->
+ ?debug("Mismatch between ~s and ~s\n",
[format_type(_T1), format_type(_T2)]),
error
end.
@@ -1936,9 +1948,9 @@ maps_are_equal_1(Map1, Map2, [H|Tail]) ->
T2 = lookup_type(H, Map2),
case t_is_equal(T1, T2) of
true -> maps_are_equal_1(Map1, Map2, Tail);
- false ->
+ false ->
?debug("~w: ~s =/= ~s\n", [H, format_type(T1), format_type(T2)]),
- false
+ false
end;
maps_are_equal_1(_Map1, _Map2, []) ->
true.
@@ -1953,7 +1965,7 @@ prune_keys(Map1, Map2, Deps) ->
true ->
Keys1 = dict:fetch_keys(Map1),
case length(Keys1) > NofDeps of
- true ->
+ true ->
Set1 = lists:sort(Keys1),
Set2 = lists:sort(dict:fetch_keys(Map2)),
ordsets:intersection(ordsets:union(Set1, Set2), Deps);
@@ -2035,7 +2047,7 @@ lookup_type(Key, Map) ->
mk_var(Var) ->
case cerl:is_literal(Var) of
true -> Var;
- false ->
+ false ->
case cerl:is_c_values(Var) of
true -> t_product(mk_var_no_lit_list(cerl:values_es(Var)));
false -> t_var(cerl_trees:get_label(Var))
@@ -2064,7 +2076,7 @@ new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) ->
NameMap = dict:from_list([{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0]),
SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0],
#state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel,
- prop_types = PropTypes, plt = Plt, scc = SCC}.
+ prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC)}.
state__set_rec_dict(State, RecDict) ->
State#state{records = RecDict}.
@@ -2076,10 +2088,10 @@ state__set_opaques(#state{records = RecDict} = State, {M, _F, _A}) ->
state__lookup_record(#state{records = Records}, Tag, Arity) ->
case erl_types:lookup_record(Tag, Arity, Records) of
- {ok, Fields} ->
+ {ok, Fields} ->
{ok, t_tuple([t_from_term(Tag)|
[FieldType || {_FieldName, FieldType} <- Fields]])};
- error ->
+ error ->
error
end.
@@ -2098,12 +2110,12 @@ state__is_in_guard(#state{in_guard = Bool}) ->
state__get_fun_prototype(Op, Arity, State) ->
case t_is_fun(Op) of
true -> {State, Op};
- false ->
+ false ->
{State1, [Ret|Args]} = state__mk_vars(Arity+1, State),
Fun = t_fun(Args, Ret),
{State1, Fun}
end.
-
+
state__lookup_rec_var_in_scope(MFA, #state{name_map = NameMap}) ->
dict:find(MFA, NameMap).
@@ -2115,11 +2127,11 @@ state__store_fun_arity(Tree, #state{fun_arities = Map} = State) ->
state__fun_arity(Id, #state{fun_arities = Map}) ->
dict:fetch(Id, Map).
-state__lookup_undef_var(Tree, #state{callgraph = CG, plt = Plt}) ->
+state__lookup_undef_var(Tree, #state{callgraph = CG, plt = Plt}) ->
Label = cerl_trees:get_label(Tree),
case dialyzer_callgraph:lookup_rec_var(Label, CG) of
error -> error;
- {ok, MFA} ->
+ {ok, MFA} ->
case dialyzer_plt:lookup(Plt, MFA) of
none -> error;
{value, {RetType, ArgTypes}} -> {ok, t_fun(ArgTypes, RetType)}
@@ -2155,6 +2167,9 @@ get_apply_constr(FunLabels, Dst, ArgTypes, #state{callgraph = CG} = State) ->
state__scc(#state{scc = SCC}) ->
SCC.
+state__add_fun_to_scc(Fun, #state{scc = SCC} = State) ->
+ State#state{scc = ordsets:add_element(Fun, SCC)}.
+
state__plt(#state{plt = PLT}) ->
PLT.
@@ -2179,7 +2194,7 @@ state__add_prop_constrs(Tree, #state{prop_types = PropTypes} = State) ->
case erl_types:any_none(ArgTypes) of
true -> not_called;
false ->
- ?debug("Adding propagated constr: ~s for function ~w\n",
+ ?debug("Adding propagated constr: ~s for function ~w\n",
[format_type(FunType), debug_lookup_name(mk_var(Tree))]),
FunVar = mk_var(Tree),
state__store_conj(FunVar, sub, FunType, State)
@@ -2225,7 +2240,7 @@ state__store_conj_lists_1([], _Op, [], State) ->
state__mk_var(#state{next_label = NL} = State) ->
{State#state{next_label = NL+1}, t_var(NL)}.
-
+
state__mk_vars(N, #state{next_label = NL} = State) ->
NewLabel = NL + N,
Vars = [t_var(X) || X <- lists:seq(NL, NewLabel-1)],
@@ -2235,7 +2250,7 @@ state__store_constrs(Id, Cs, #state{cmap = Dict} = State) ->
NewDict = dict:store(Id, Cs, Dict),
State#state{cmap = NewDict}.
-state__get_cs(Var, #state{cmap = Dict}) ->
+state__get_cs(Var, #state{cmap = Dict}) ->
dict:fetch(Var, Dict).
%% The functions here will not be treated as self recursive.
@@ -2286,7 +2301,7 @@ mk_constraint(Lhs, Op, Rhs) ->
%% This constraint is constant. Solve it immediately.
case solve_one_c(C, dict:new(), []) of
error -> throw(error);
- _ ->
+ _ ->
%% This is always true, keep it anyway for logistic reasons
C
end;
@@ -2335,7 +2350,7 @@ mk_constraint_1(Lhs, eq, Rhs) when Lhs < Rhs ->
mk_constraint_1(Lhs, eq, Rhs) ->
#constraint{lhs = Rhs, op = eq, rhs = Lhs};
mk_constraint_1(Lhs, Op, Rhs) ->
- #constraint{lhs = Lhs, op = Op, rhs = Rhs}.
+ #constraint{lhs = Lhs, op = Op, rhs = Rhs}.
mk_constraints([Lhs|LhsTail], Op, [Rhs|RhsTail]) ->
[mk_constraint(Lhs, Op, Rhs)|mk_constraints(LhsTail, Op, RhsTail)];
@@ -2350,7 +2365,7 @@ mk_constraint_list(Type, List) ->
List2 = ordsets:filter(fun(X) -> get_deps(X) =/= [] end, List1),
Deps = calculate_deps(List2),
case Deps =:= [] of
- true -> #constraint_list{type = conj,
+ true -> #constraint_list{type = conj,
list = [mk_constraint(t_any(), eq, t_any())],
deps = []};
false -> #constraint_list{type = Type, list = List2, deps = Deps}
@@ -2372,11 +2387,11 @@ update_constraint_list(CL, List) ->
%% We expand guard constraints into dijunctive normal form to gain
%% precision in simple guards. However, because of the exponential
%% growth of this expansion in the presens of disjunctions we can even
-%% get into trouble while expanding.
+%% get into trouble while expanding.
%%
%% To limit this we only expand when the number of disjunctions are
%% below a certain limit. This limit is currently set based on the
-%% behaviour of boolean 'or'.
+%% behaviour of boolean 'or'.
%%
%% V1 = V2 or V3
%%
@@ -2395,7 +2410,7 @@ update_constraint_list(CL, List) ->
-define(DISJ_NORM_FORM_LIMIT, 28).
mk_disj_norm_form(#constraint_list{} = CL) ->
- try
+ try
List1 = expand_to_conjunctions(CL),
mk_disj_constraint_list(List1)
catch
@@ -2409,7 +2424,7 @@ expand_to_conjunctions(#constraint_list{type = conj, list = List}) ->
true -> [mk_conj_constraint_list(List1)];
false ->
case List2 of
- [JustOneList] ->
+ [JustOneList] ->
[mk_conj_constraint_list([L|List1]) || L <- JustOneList];
_ ->
combine_conj_lists(List2, List1)
@@ -2422,7 +2437,7 @@ expand_to_conjunctions(#constraint_list{type = disj, list = List}) ->
List1 = [C || C <- List, is_simple_constraint(C)],
%% Just an assert.
[] = [C || #constraint{} = C <- List1],
- Expanded = lists:flatten([expand_to_conjunctions(C)
+ Expanded = lists:flatten([expand_to_conjunctions(C)
|| #constraint_list{} = C <- List]),
ReturnList = Expanded ++ List1,
if length(ReturnList) > ?DISJ_NORM_FORM_LIMIT -> throw(too_many_disj);
@@ -2467,7 +2482,7 @@ wrap_simple_constr(#constraint_list{} = C) -> C;
wrap_simple_constr(#constraint_ref{} = C) -> C.
enumerate_constraints(State) ->
- Cs = [mk_constraint_ref(Id, get_deps(state__get_cs(Id, State)))
+ Cs = [mk_constraint_ref(Id, get_deps(state__get_cs(Id, State)))
|| Id <- state__scc(State)],
{_, _, NewState} = enumerate_constraints(Cs, 0, [], State),
NewState.
@@ -2475,9 +2490,9 @@ enumerate_constraints(State) ->
enumerate_constraints([#constraint_ref{id = Id} = C|Tail], N, Acc, State) ->
Cs = state__get_cs(Id, State),
{[NewCs], NewN, NewState1} = enumerate_constraints([Cs], N, [], State),
- NewState2 = state__store_constrs(Id, NewCs, NewState1),
+ NewState2 = state__store_constrs(Id, NewCs, NewState1),
enumerate_constraints(Tail, NewN+1, [C|Acc], NewState2);
-enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail],
+enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail],
N, Acc, State) ->
%% Separate the flat constraints from the deep ones to make a
%% separate fixpoint interation over the flat ones for speed.
@@ -2496,7 +2511,7 @@ enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail],
end,
NewAcc = [C#constraint_list{list = NewList, id = {list, N3}}|Acc],
enumerate_constraints(Tail, N3+1, NewAcc, State2);
-enumerate_constraints([#constraint_list{list = List, type = disj} = C|Tail],
+enumerate_constraints([#constraint_list{list = List, type = disj} = C|Tail],
N, Acc, State) ->
{NewList, NewN, NewState} = enumerate_constraints(List, N, [], State),
NewAcc = [C#constraint_list{list = NewList, id = {list, NewN}}|Acc],
@@ -2515,7 +2530,7 @@ group_constraints_in_components(Cs, N) ->
case find_dep_components(DepList, []) of
[_] -> {Cs, N};
[_|_] = Components ->
- ConstrComp = [[C || #constraint{deps = D} = C <- Cs,
+ ConstrComp = [[C || #constraint{deps = D} = C <- Cs,
ordsets:is_subset(D, Comp)]
|| Comp <- Components],
lists:mapfoldl(fun(CComp, TmpN) ->
@@ -2545,7 +2560,7 @@ find_dep_components([], AccSet, Ungrouped) ->
%% Put the fun ref constraints last in any conjunction since we need
%% to separate the environment from the interior of the function.
order_fun_constraints(State) ->
- Cs = [mk_constraint_ref(Id, get_deps(state__get_cs(Id, State)))
+ Cs = [mk_constraint_ref(Id, get_deps(state__get_cs(Id, State)))
|| Id <- state__scc(State)],
order_fun_constraints(Cs, State).
@@ -2565,8 +2580,8 @@ order_fun_constraints([#constraint_list{list = List, type = Type} = C|Tail],
case Type of
conj -> order_fun_constraints(List, [], [], State);
disj ->
- FoldFun = fun(X, AccState) ->
- {[NewX], NewAccState} =
+ FoldFun = fun(X, AccState) ->
+ {[NewX], NewAccState} =
order_fun_constraints([X], [], [], AccState),
{NewX, NewAccState}
end,
@@ -2588,7 +2603,7 @@ order_fun_constraints([], Funs, Acc, State) ->
is_singleton_non_number_type(Type) ->
case t_is_number(Type) of
- true -> false;
+ true -> false;
false -> is_singleton_type(Type)
end.
@@ -2613,6 +2628,41 @@ is_singleton_type(Type) ->
end
end.
+find_element(Args, Cs) ->
+ [Pos, Tuple] = Args,
+ case erl_types:t_is_number(Pos) of
+ true ->
+ case erl_types:t_number_vals(Pos) of
+ 'unknown' -> 'unknown';
+ [I] ->
+ case find_constraint(Tuple, Cs) of
+ 'unknown' -> 'unknown';
+ #constraint{lhs = ExTuple} ->
+ case erl_types:t_is_tuple(ExTuple) of
+ true ->
+ Elems = erl_types:t_tuple_args(ExTuple),
+ Elem = lists:nth(I, Elems),
+ case erl_types:t_is_var(Elem) of
+ true -> Elem;
+ false -> 'unknown'
+ end;
+ false -> 'unknown'
+ end
+ end;
+ _ -> 'unknown'
+ end;
+ false -> 'unknown'
+ end.
+
+find_constraint(_Tuple, []) ->
+ 'unknown';
+find_constraint(Tuple, [#constraint{op = 'eq', rhs = Tuple} = C|_]) ->
+ C;
+find_constraint(Tuple, [#constraint_list{list = List}|Cs]) ->
+ find_constraint(Tuple, List ++ Cs);
+find_constraint(Tuple, [_|Cs]) ->
+ find_constraint(Tuple, Cs).
+
%% ============================================================================
%%
%% Pretty printer and debug facilities.
@@ -2638,7 +2688,7 @@ format_type(Type) ->
-ifdef(DEBUG_NAME_MAP).
debug_make_name_map(Vars, Funs) ->
Map = get(dialyzer_typesig_map),
- NewMap =
+ NewMap =
if Map =:= undefined -> debug_make_name_map(Vars, Funs, dict:new());
true -> debug_make_name_map(Vars, Funs, Map)
end,
@@ -2676,15 +2726,15 @@ pp_constraints(Cs, State) ->
io:nl(),
Res.
-pp_constraints([List|Tail], Separator, Level, MaxDepth,
+pp_constraints([List|Tail], Separator, Level, MaxDepth,
State) when is_list(List) ->
pp_constraints(List++Tail, Separator, Level, MaxDepth, State);
-pp_constraints([#constraint_ref{id = Id}|Left], Separator,
+pp_constraints([#constraint_ref{id = Id}|Left], Separator,
Level, MaxDepth, State) ->
Cs = state__get_cs(Id, State),
io:format("%Ref ~w%", [t_var_name(Id)]),
pp_constraints([Cs|Left], Separator, Level, MaxDepth, State);
-pp_constraints([#constraint{lhs = Lhs, op = Op, rhs = Rhs}], _Separator,
+pp_constraints([#constraint{lhs = Lhs, op = Op, rhs = Rhs}], _Separator,
Level, MaxDepth, _State) ->
io:format("~s ~w ~s", [format_type(Lhs), Op, format_type(Rhs)]),
erlang:max(Level, MaxDepth);
@@ -2721,7 +2771,7 @@ pp_constrs_scc(_SCC, _State) ->
constraints_to_dot_scc(SCC, State) ->
io:format("SCC: ~p\n", [SCC]),
- Name = lists:flatten([io_lib:format("'~w'", [debug_lookup_name(Fun)])
+ Name = lists:flatten([io_lib:format("'~w'", [debug_lookup_name(Fun)])
|| Fun <- SCC]),
Cs = [state__get_cs(Fun, State) || Fun <- SCC],
constraints_to_dot(Cs, Name, State).
@@ -2737,22 +2787,22 @@ constraints_to_dot(Cs0, Name, State) ->
constraints_to_nodes([{Name, #constraint_list{type = Type, list = List, id=Id}}
|Left], N, Level, Graph, Opts, State) ->
- N1 = N + length(List),
+ N1 = N + length(List),
NewList = lists:zip(lists:seq(N, N1 - 1), List),
Names = [SubName || {SubName, _C} <- NewList],
Edges = [{Name, SubName} || SubName <- Names],
- ThisNode = [{Name, Opt} || Opt <- [{label,
+ ThisNode = [{Name, Opt} || Opt <- [{label,
lists:flatten(io_lib:format("~w", [Id]))},
{shape, get_shape(Type)},
{level, Level}]],
- {NewGraph, NewOpts, N2} = constraints_to_nodes(NewList, N1, Level+1,
- [Edges|Graph],
+ {NewGraph, NewOpts, N2} = constraints_to_nodes(NewList, N1, Level+1,
+ [Edges|Graph],
[ThisNode|Opts], State),
constraints_to_nodes(Left, N2, Level, NewGraph, NewOpts, State);
constraints_to_nodes([{Name, #constraint{lhs = Lhs, op = Op, rhs = Rhs}}|Left],
N, Level, Graph, Opts, State) ->
- Label = lists:flatten(io_lib:format("~s ~w ~s",
- [format_type(Lhs), Op,
+ Label = lists:flatten(io_lib:format("~s ~w ~s",
+ [format_type(Lhs), Op,
format_type(Rhs)])),
ThisNode = [{Name, Opt} || Opt <- [{label, Label}, {level, Level}]],
NewOpts = [ThisNode|Opts],
@@ -2761,20 +2811,20 @@ constraints_to_nodes([{Name, #constraint_ref{id = Id0}}|Left],
N, Level, Graph, Opts, State) ->
Id = debug_lookup_name(Id0),
CList = state__get_cs(Id0, State),
- ThisNode = [{Name, Opt} || Opt <- [{label,
+ ThisNode = [{Name, Opt} || Opt <- [{label,
lists:flatten(io_lib:format("~w", [Id]))},
{shape, ellipse},
- {level, Level}]],
- NewList = [{N, CList}],
- {NewGraph, NewOpts, N1} = constraints_to_nodes(NewList, N + 1, Level + 1,
+ {level, Level}]],
+ NewList = [{N, CList}],
+ {NewGraph, NewOpts, N1} = constraints_to_nodes(NewList, N + 1, Level + 1,
[{Name, N}|Graph],
[ThisNode|Opts], State),
constraints_to_nodes(Left, N1, Level, NewGraph, NewOpts, State);
constraints_to_nodes([], N, _Level, Graph, Opts, _State) ->
{lists:flatten(Graph), lists:flatten(Opts), N}.
-
+
get_shape(conj) -> box;
-get_shape(disj) -> diamond.
+get_shape(disj) -> diamond.
-else.
constraints_to_dot_scc(_SCC, _State) ->
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 6ea243c26f..12f8dec67e 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -2,7 +2,7 @@
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
%%%-------------------------------------------------------------------
%%% File : dialyzer_utils.erl
%%% Author : Tobias Lindahl <[email protected]>
-%%% Description :
+%%% Description :
%%%
%%% Created : 5 Dec 2006 by Tobias Lindahl <[email protected]>
%%%-------------------------------------------------------------------
@@ -42,6 +42,7 @@
merge_records/2,
pp_hook/0,
process_record_remote_types/1,
+ sets_filter/2,
src_compiler_opts/0
]).
@@ -65,7 +66,7 @@ print_types1([{opaque, _Name} = Key|T], RecDict) ->
io:format("\n~w: ~w\n", [Key, erl_types:t_from_form(Form, RecDict)]),
print_types1(T, RecDict);
print_types1([{record, _Name} = Key|T], RecDict) ->
- {ok, [{Arity, Fields} = AF]} = dict:find(Key, RecDict),
+ {ok, [{_Arity, _Fields} = AF]} = dict:find(Key, RecDict),
io:format("~w: ~w\n\n", [Key, AF]),
print_types1(T, RecDict).
-define(debug(D_), print_types(D_)).
@@ -73,12 +74,11 @@ print_types1([{record, _Name} = Key|T], RecDict) ->
-define(debug(D_), ok).
-endif.
-%%
-%% Types that need to be imported from somewhere else
-%%
+%% ----------------------------------------------------------------------------
--type abstract_code() :: [tuple()]. %% XXX: refine
--type comp_options() :: [atom()]. %% XXX: only a resticted set of options used
+-type abstract_code() :: [tuple()]. %% XXX: import from somewhere
+-type comp_options() :: [compile:option()].
+-type mod_or_fname() :: atom() | file:filename().
%% ============================================================================
%%
@@ -86,13 +86,13 @@ print_types1([{record, _Name} = Key|T], RecDict) ->
%%
%% ============================================================================
--spec get_abstract_code_from_src(atom() | file:filename()) ->
+-spec get_abstract_code_from_src(mod_or_fname()) ->
{'ok', abstract_code()} | {'error', [string()]}.
get_abstract_code_from_src(File) ->
get_abstract_code_from_src(File, src_compiler_opts()).
--spec get_abstract_code_from_src(atom() | file:filename(), comp_options()) ->
+-spec get_abstract_code_from_src(mod_or_fname(), comp_options()) ->
{'ok', abstract_code()} | {'error', [string()]}.
get_abstract_code_from_src(File, Opts) ->
@@ -169,13 +169,13 @@ get_record_and_type_info(AbstractCode) ->
Module = get_module(AbstractCode),
get_record_and_type_info(AbstractCode, Module, dict:new()).
--spec get_record_and_type_info(abstract_code(), atom(), dict()) ->
+-spec get_record_and_type_info(abstract_code(), module(), dict()) ->
{'ok', dict()} | {'error', string()}.
get_record_and_type_info(AbstractCode, Module, RecDict) ->
get_record_and_type_info(AbstractCode, Module, [], RecDict).
-get_record_and_type_info([{attribute, _, record, {Name, Fields0}}|Left],
+get_record_and_type_info([{attribute, _, record, {Name, Fields0}}|Left],
Module, Records, RecDict) ->
{ok, Fields} = get_record_fields(Fields0, RecDict),
Arity = length(Fields),
@@ -188,7 +188,7 @@ get_record_and_type_info([{attribute, _, type, {{record, Name}, Fields0, []}}
Arity = length(Fields),
NewRecDict = dict:store({record, Name}, [{Arity, Fields}], RecDict),
get_record_and_type_info(Left, Module, Records, NewRecDict);
-get_record_and_type_info([{attribute, _, Attr, {Name, TypeForm}}|Left],
+get_record_and_type_info([{attribute, _, Attr, {Name, TypeForm}}|Left],
Module, Records, RecDict) when Attr =:= 'type';
Attr =:= 'opaque' ->
try
@@ -197,7 +197,7 @@ get_record_and_type_info([{attribute, _, Attr, {Name, TypeForm}}|Left],
catch
throw:{error, _} = Error -> Error
end;
-get_record_and_type_info([{attribute, _, Attr, {Name, TypeForm, Args}}|Left],
+get_record_and_type_info([{attribute, _, Attr, {Name, TypeForm, Args}}|Left],
Module, Records, RecDict) when Attr =:= 'type';
Attr =:= 'opaque' ->
try
@@ -211,17 +211,16 @@ get_record_and_type_info([_Other|Left], Module, Records, RecDict) ->
get_record_and_type_info([], _Module, Records, RecDict) ->
case type_record_fields(lists:reverse(Records), RecDict) of
{ok, _NewRecDict} = Ok ->
- ?debug(NewRecDict),
+ ?debug(_NewRecDict),
Ok;
- {Name, {error, Error}} ->
- {error, lists:flatten(io_lib:format(" Error while parsing #~w{}: ~s\n",
- [Name, Error]))}
+ {error, Name, Error} ->
+ {error, flat_format(" Error while parsing #~w{}: ~s\n", [Name, Error])}
end.
add_new_type(TypeOrOpaque, Name, TypeForm, ArgForms, Module, RecDict) ->
- case erl_types:type_is_defined(TypeOrOpaque, Name, RecDict) of
+ case erl_types:type_is_defined(TypeOrOpaque, Name, RecDict) of
true ->
- throw({error, io_lib:format("Type already defined: ~w\n", [Name])});
+ throw({error, flat_format("Type ~s already defined\n", [Name])});
false ->
ArgTypes = [erl_types:t_from_form(X) || X <- ArgForms],
case lists:all(fun erl_types:t_is_var/1, ArgTypes) of
@@ -229,15 +228,15 @@ add_new_type(TypeOrOpaque, Name, TypeForm, ArgForms, Module, RecDict) ->
ArgNames = [erl_types:t_var_name(X) || X <- ArgTypes],
dict:store({TypeOrOpaque, Name}, {Module, TypeForm, ArgNames}, RecDict);
false ->
- throw({error, io_lib:format("Type declaration for ~w does not "
- "have variables as parameters", [Name])})
+ throw({error, flat_format("Type declaration for ~w does not "
+ "have variables as parameters", [Name])})
end
end.
get_record_fields(Fields, RecDict) ->
get_record_fields(Fields, RecDict, []).
-get_record_fields([{typed_record_field, OrdRecField, TypeForm}|Left],
+get_record_fields([{typed_record_field, OrdRecField, TypeForm}|Left],
RecDict, Acc) ->
Name =
case OrdRecField of
@@ -269,22 +268,25 @@ type_record_fields([RecKey|Recs], RecDict) ->
RecDict2 = dict:update(RecKey, Fun, RecDict1),
type_record_fields(Recs, RecDict2)
catch
- throw:{error, _} = Error ->
+ throw:{error, Error} ->
{record, Name} = RecKey,
- {Name, Error}
+ {error, Name, Error}
end.
-spec process_record_remote_types(dialyzer_codeserver:codeserver()) -> dialyzer_codeserver:codeserver().
process_record_remote_types(CServer) ->
TempRecords = dialyzer_codeserver:get_temp_records(CServer),
+ TempExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer),
RecordFun =
fun(Key, Value) ->
case Key of
{record, _Name} ->
FieldFun =
fun(_Arity, Fields) ->
- [{Name, erl_types:t_solve_remote(Field, TempRecords)} || {Name, Field} <- Fields]
+ [{Name, erl_types:t_solve_remote(Field, TempExpTypes,
+ TempRecords)}
+ || {Name, Field} <- Fields]
end,
orddict:map(FieldFun, Value);
_Other -> Value
@@ -295,7 +297,8 @@ process_record_remote_types(CServer) ->
dict:map(RecordFun, Record)
end,
NewRecords = dict:map(ModuleFun, TempRecords),
- dialyzer_codeserver:finalize_records(NewRecords, CServer).
+ CServer1 = dialyzer_codeserver:finalize_records(NewRecords, CServer),
+ dialyzer_codeserver:finalize_exported_types(TempExpTypes, CServer1).
-spec merge_records(dict(), dict()) -> dict().
@@ -308,19 +311,19 @@ merge_records(NewRecords, OldRecords) ->
%%
%% ============================================================================
--spec get_spec_info(module(), abstract_code(), dict()) ->
+-spec get_spec_info(atom(), abstract_code(), dict()) ->
{'ok', dict()} | {'error', string()}.
get_spec_info(ModName, AbstractCode, RecordsDict) ->
get_spec_info(AbstractCode, dict:new(), RecordsDict, ModName, "nofile").
-%% TypeSpec is a list of conditional contracts for a function.
+%% TypeSpec is a list of conditional contracts for a function.
%% Each contract is of the form {[Argument], Range, [Constraint]} where
%% - Argument and Range are in erl_types:erl_type() format and
%% - Constraint is of the form {subtype, T1, T2} where T1 and T2
%% are erl_types:erl_type()
-get_spec_info([{attribute, Ln, spec, {Id, TypeSpec}}|Left],
+get_spec_info([{attribute, Ln, spec, {Id, TypeSpec}}|Left],
SpecDict, RecordsDict, ModName, File) when is_list(TypeSpec) ->
MFA = case Id of
{_, _, _} = T -> T;
@@ -334,14 +337,14 @@ get_spec_info([{attribute, Ln, spec, {Id, TypeSpec}}|Left],
get_spec_info(Left, NewSpecDict, RecordsDict, ModName, File);
{ok, {{OtherFile, L},_C}} ->
{Mod, Fun, Arity} = MFA,
- Msg = io_lib:format(" Contract for function ~w:~w/~w "
- "already defined in ~s:~w\n",
- [Mod, Fun, Arity, OtherFile, L]),
+ Msg = flat_format(" Contract for function ~w:~w/~w "
+ "already defined in ~s:~w\n",
+ [Mod, Fun, Arity, OtherFile, L]),
throw({error, Msg})
catch
throw:{error, Error} ->
- {error, lists:flatten(io_lib:format(" Error while parsing contract "
- "in line ~w: ~s\n", [Ln, Error]))}
+ {error, flat_format(" Error while parsing contract in line ~w: ~s\n",
+ [Ln, Error])}
end;
get_spec_info([{attribute, _, file, {IncludeFile, _}}|Left],
SpecDict, RecordsDict, ModName, _File) ->
@@ -353,15 +356,30 @@ get_spec_info([], SpecDict, _RecordsDict, _ModName, _File) ->
%% ============================================================================
%%
+%% Exported types
+%%
+%% ============================================================================
+
+-spec sets_filter([module()], set()) -> set().
+
+sets_filter([], ExpTypes) ->
+ ExpTypes;
+sets_filter([Mod|Mods], ExpTypes) ->
+ NewExpTypes = sets:filter(fun({M, _F, _A}) -> M =/= Mod end, ExpTypes),
+ sets_filter(Mods, NewExpTypes).
+
+%% ============================================================================
+%%
%% Util utils
%%
%% ============================================================================
--spec src_compiler_opts() -> comp_options().
+-spec src_compiler_opts() -> [compile:option(),...].
src_compiler_opts() ->
- [no_copt, to_core, binary, return_errors,
- no_inline, strict_record_tests, strict_record_updates].
+ [no_copt, to_core, binary, return_errors,
+ no_inline, strict_record_tests, strict_record_updates,
+ no_is_record_optimization].
-spec get_module(abstract_code()) -> module().
@@ -381,7 +399,7 @@ cleanup_parse_transforms([]) ->
-spec format_errors([{module(), string()}]) -> [string()].
format_errors([{Mod, Errors}|Left]) ->
- FormatedError =
+ FormatedError =
[io_lib:format("~s:~w: ~s\n", [Mod, Line, M:format_error(Desc)])
|| {Line, M, Desc} <- Errors],
[lists:flatten(FormatedError) | format_errors(Left)];
@@ -400,6 +418,9 @@ format_sig(Type, RecDict) ->
")" ++ RevSig = lists:reverse(Sig),
lists:reverse(RevSig).
+flat_format(Fmt, Lst) ->
+ lists:flatten(io_lib:format(Fmt, Lst)).
+
%%-------------------------------------------------------------------
%% Author : Per Gustafsson <[email protected]>
%% Description : Provides better printing of binaries.
@@ -456,7 +477,7 @@ pp_size(Size, Ctxt, Cont) ->
end.
pp_opts(Type, Flags) ->
- FinalFlags =
+ FinalFlags =
case cerl:atom_val(Type) of
binary -> [];
float -> keep_endian(cerl:concrete(Flags));
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
new file mode 100644
index 0000000000..a8549278a5
--- /dev/null
+++ b/lib/dialyzer/test/Makefile
@@ -0,0 +1,75 @@
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ callgraph_tests_SUITE \
+ opaque_tests_SUITE \
+ options1_tests_SUITE \
+ options2_tests_SUITE \
+ plt_tests_SUITE \
+ r9c_tests_SUITE \
+ race_tests_SUITE \
+ small_tests_SUITE \
+ user_tests_SUITE \
+ dialyzer_common\
+ file_utils
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+INSTALL_PROGS= $(TARGET_FILES)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/dialyzer_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
+ > $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES) $(GEN_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) dialyzer.spec dialyzer_test_constants.hrl $(RELSYSDIR)
+ chmod -f -R u+w $(RELSYSDIR)
+ @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
diff --git a/lib/dialyzer/test/README b/lib/dialyzer/test/README
new file mode 100644
index 0000000000..07340c7266
--- /dev/null
+++ b/lib/dialyzer/test/README
@@ -0,0 +1,44 @@
+-------------------------------
+To add test cases in any suite:
+-------------------------------
+
+ 1) If the test requires dialyzer to analyze a single file place it in the
+ suite's 'src' directory. If analysis of more files is needed place them
+ all in a new directory in suite's 'src' directory.
+
+ 2) Create a file with the same name as the test (if single file, omit the
+ extension else directory name) containing the expected result in suite's
+ 'result' directory.
+
+ 3) Run './remake <suite>', where <suite> is the suite's name omitting
+ "_tests_SUITE".
+
+----------------------
+To create a new suite:
+----------------------
+
+ 1) Create a directory with the suffix 'tests_SUITE_data'. The name should
+ describe the suite.
+
+ 2) In the suite's directory create subdirectories 'src' and 'results' as
+ well as a 'dialyzer_options' file with the following content:
+
+ {dialyzer_options, List}.
+ {time_limit, Limit}.
+
+ where:
+
+ List = a list of dialyzer options. Common case will be something
+ like [{warnings, Warnings}], where Warnings is a list of valid
+ '-W' prefixed dialyzer options without the 'W' prefix (e.g.
+ '-Wfoo' would be declared as [{warnings, [foo]}].
+ Limit = the amount of time each test case is allowed to run. Must be
+ bigger than the time it takes the most time-consuming test to
+ finish.
+
+ Any of these lines may be missing. Default options list is empty and
+ default time limit is 1 minute.
+
+ 3) Add tests as described in previous section.
+
+ 4) Add the resulting suite's name in the Makefile's MODULES variable.
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE.erl b/lib/dialyzer/test/callgraph_tests_SUITE.erl
new file mode 100644
index 0000000000..6148adf971
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_tests_SUITE.erl
@@ -0,0 +1,52 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(callgraph_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([callgraph_tests_SUITE_consistency/1, test_missing_functions/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, []}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [callgraph_tests_SUITE_consistency,test_missing_functions].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+callgraph_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+test_missing_functions(Config) ->
+ case dialyze(Config, test_missing_functions) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..50991c9bc5
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_tests_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, []}.
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions b/lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions
new file mode 100644
index 0000000000..4150bdb7c0
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_tests_SUITE_data/results/test_missing_functions
@@ -0,0 +1,3 @@
+
+t1.erl:16: Call to missing or unexported function t2:t2/1
+t2.erl:13: Call to missing or unexported function t1:t3/1
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl b/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl
new file mode 100644
index 0000000000..3b320e1ed4
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t1.erl
@@ -0,0 +1,16 @@
+%%%-------------------------------------------------------------------
+%%% File : t1.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 26 Jul 2006 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(t1).
+
+-export([t1/1, t2/1]).
+
+t1(X) ->
+ t2:t1(X).
+
+t2(X) ->
+ t2:t2(X).
diff --git a/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl b/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl
new file mode 100644
index 0000000000..5ac8aa328c
--- /dev/null
+++ b/lib/dialyzer/test/callgraph_tests_SUITE_data/src/test_missing_functions/t2.erl
@@ -0,0 +1,16 @@
+%%%-------------------------------------------------------------------
+%%% File : t2.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 26 Jul 2006 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(t2).
+
+-export([t1/1]).
+
+t1(X) ->
+ t1:t3(X) + t2(X).
+
+t2(X) ->
+ X + 1.
diff --git a/lib/dialyzer/test/dialyzer.spec b/lib/dialyzer/test/dialyzer.spec
new file mode 100644
index 0000000000..7499dbad1e
--- /dev/null
+++ b/lib/dialyzer/test/dialyzer.spec
@@ -0,0 +1,5 @@
+{alias, tests, "../dialyzer_test"}.
+
+{suites, tests, all}.
+
+{skip_cases, tests, small_tests_SUITE, cerl_hipeify, "Needs compiler in plt"}. \ No newline at end of file
diff --git a/lib/dialyzer/test/dialyzer_common.erl b/lib/dialyzer/test/dialyzer_common.erl
new file mode 100644
index 0000000000..5577405483
--- /dev/null
+++ b/lib/dialyzer/test/dialyzer_common.erl
@@ -0,0 +1,377 @@
+%%% File : dialyzer_common.erl
+%%% Author : Stavros Aronis <[email protected]>
+%%% Description : Generator and common infrastructure for simple dialyzer
+%%% test suites (some options, some input files or directories
+%%% and the relevant results).
+%%% Created : 11 Jun 2010 by Stavros Aronis <stavros@enjoy>
+
+-module(dialyzer_common).
+
+-export([check_plt/1, check/4, create_suite/1,
+ create_all_suites/0, new_tests/2]).
+
+-include_lib("kernel/include/file.hrl").
+
+-define(suite_suffix, "_tests_SUITE").
+-define(data_folder, "_data").
+-define(erlang_extension, ".erl").
+-define(output_file_mode, write).
+-define(dialyzer_option_file, "dialyzer_options").
+-define(input_files_directory, "src").
+-define(result_files_directory, "results").
+-define(plt_filename,"dialyzer_plt").
+-define(home_plt_filename,".dialyzer_plt").
+-define(plt_lockfile,"plt_lock").
+-define(required_modules, [erts, kernel, stdlib]).
+
+-record(suite, {suitename :: string(),
+ outputfile :: file:io_device(),
+ options :: options(),
+ testcases :: [testcase()]}).
+
+-record(options, {time_limit = 1 :: integer(),
+ dialyzer_options = [] :: dialyzer:dial_options()}).
+
+-type options() :: #options{}.
+-type testcase() :: {atom(), 'file' | 'dir'}.
+
+-spec check_plt(string()) -> ok.
+
+check_plt(OutDir) ->
+ io:format("Checking plt:"),
+ PltFilename = filename:join(OutDir, ?plt_filename),
+ case file:read_file_info(PltFilename) of
+ {ok, _} -> dialyzer_check_plt(PltFilename);
+ {error, _ } ->
+ io:format("No plt found in test run directory!"),
+ PltLockFile = filename:join(OutDir, ?plt_lockfile),
+ case file:read_file_info(PltLockFile) of
+ {ok, _} ->
+ explain_fail_with_lock(),
+ fail;
+ {error, _} ->
+ io:format("Locking plt generation."),
+ case file:open(PltLockFile,[?output_file_mode]) of
+ {ok, OutFile} ->
+ io:format(OutFile,"Locking plt generation.",[]),
+ file:close(OutFile);
+ {error, Reason} ->
+ io:format("Couldn't write lock file ~p.",[Reason]),
+ fail
+ end,
+ obtain_plt(PltFilename)
+ end
+ end.
+
+dialyzer_check_plt(PltFilename) ->
+ try dialyzer:run([{analysis_type, plt_check},
+ {init_plt, PltFilename}]) of
+ [] -> ok
+ catch
+ Class:Info ->
+ io:format("Failed. The error was: ~w\n~p",[Class, Info]),
+ io:format("A previously run dialyzer suite failed to generate"
+ " a correct plt."),
+ fail
+ end.
+
+explain_fail_with_lock() ->
+ io:format("Some other suite started creating a plt. It might not have"
+ " finished (Dialyzer's suites shouldn't run in parallel), or"
+ " it reached timeout and was killed (in which case"
+ " plt_timeout, defined in dialyzer_test_constants.hrl"
+ " should be increased), or it failed.").
+
+obtain_plt(PltFilename) ->
+ io:format("Obtaining plt:"),
+ HomeDir = os:getenv("HOME"),
+ HomePlt = filename:join(HomeDir, ?home_plt_filename),
+ io:format("Will try to use ~s as a starting point and add otp apps ~w.",
+ [HomePlt, ?required_modules]),
+ try dialyzer:run([{analysis_type, plt_add},
+ {apps, ?required_modules},
+ {output_plt, PltFilename},
+ {init_plt, HomePlt}]) of
+ [] ->
+ io:format("Successfully added everything!"),
+ ok
+ catch
+ Class:Reason ->
+ io:format("Failed. The error was: ~w\n~p",[Class, Reason]),
+ build_plt(PltFilename)
+ end.
+
+build_plt(PltFilename) ->
+ io:format("Building plt from scratch:"),
+ try dialyzer:run([{analysis_type, plt_build},
+ {apps, ?required_modules},
+ {output_plt, PltFilename}]) of
+ [] ->
+ io:format("Successfully created plt!"),
+ ok
+ catch
+ Class:Reason ->
+ io:format("Failed. The error was: ~w\n~p",[Class, Reason]),
+ fail
+ end.
+
+-spec check(atom(), dialyzer:dial_options(), string(), string()) ->
+ 'same' | {differ, [term()]}.
+
+check(TestCase, Opts, Dir, OutDir) ->
+ PltFilename = filename:join(OutDir, ?plt_filename),
+ SrcDir = filename:join(Dir, ?input_files_directory),
+ ResDir = filename:join(Dir, ?result_files_directory),
+ Filename = filename:join(SrcDir, atom_to_list(TestCase)),
+ Files =
+ case file_utils:file_type(Filename) of
+ {ok, 'directory'} ->
+ {ok, ListFiles} = file_utils:list_dir(Filename, ".erl",
+ false),
+ ListFiles;
+ {error, _} ->
+ FilenameErl = Filename ++ ".erl",
+ case file_utils:file_type(FilenameErl) of
+ {ok, 'regular'} -> [FilenameErl]
+ end
+ end,
+ ResFile = atom_to_list(TestCase),
+ NewResFile = filename:join(OutDir, ResFile),
+ OldResFile = filename:join(ResDir, ResFile),
+ ProperOpts = fix_options(Opts, Dir),
+ try dialyzer:run([{files, Files},{from, src_code},{init_plt, PltFilename},
+ {check_plt, false}|ProperOpts]) of
+ RawWarns ->
+ Warns = lists:sort([dialyzer:format_warning(W) || W <- RawWarns]),
+ case Warns of
+ [] -> ok;
+ _ ->
+ case file:open(NewResFile,[?output_file_mode]) of
+ {ok, OutFile} ->
+ io:format(OutFile,"\n~s",[Warns]),
+ file:close(OutFile);
+ Other -> erlang:error(Other)
+ end
+ end,
+ case file_utils:diff(NewResFile, OldResFile) of
+ 'same' -> file:delete(NewResFile),
+ 'same';
+ Any -> escape_strings(Any)
+ end
+ catch
+ Kind:Error -> {'dialyzer crashed', Kind, Error}
+ end.
+
+fix_options(Opts, Dir) ->
+ fix_options(Opts, Dir, []).
+
+fix_options([], _Dir, Acc) ->
+ Acc;
+fix_options([{pa, Path} | Rest], Dir, Acc) ->
+ case code:add_patha(filename:join(Dir, Path)) of
+ true -> fix_options(Rest, Dir, Acc);
+ {error, _} -> erlang:error("Bad directory for pa: " ++ Path)
+ end;
+fix_options([{DirOption, RelativeDirs} | Rest], Dir, Acc)
+ when DirOption =:= include_dirs ;
+ DirOption =:= files_rec ;
+ DirOption =:= files ->
+ ProperRelativeDirs = [filename:join(Dir,RDir) || RDir <- RelativeDirs],
+ fix_options(Rest, Dir, [{include_dirs, ProperRelativeDirs} | Acc]);
+fix_options([Opt | Rest], Dir, Acc) ->
+ fix_options(Rest, Dir, [Opt | Acc]).
+
+-spec new_tests(string(), [atom()]) -> [atom()].
+
+new_tests(Dirname, DeclaredTestcases) ->
+ SrcDir = filename:join(Dirname, ?input_files_directory),
+ get_testcases(SrcDir) -- DeclaredTestcases.
+
+get_testcases(Dirname) ->
+ {ok, Files} = file_utils:list_dir(Dirname, ".erl", true),
+ [list_to_atom(filename:basename(F,".erl")) || F <-Files].
+
+-spec create_all_suites() -> 'ok'.
+
+create_all_suites() ->
+ {ok, Cwd} = file:get_cwd(),
+ Suites = get_suites(Cwd),
+ lists:foreach(fun create_suite/1, Suites).
+
+escape_strings({differ,List}) ->
+ Map = fun({T,L,S}) -> {T,L,xmerl_lib:export_text(S)} end,
+ {differ, lists:keysort(3, lists:map(Map, List))}.
+
+-spec get_suites(file:filename()) -> [string()].
+
+get_suites(Dir) ->
+ case file:list_dir(Dir) of
+ {error, _} -> [];
+ {ok, Filenames} ->
+ FullFilenames = [filename:join(Dir, F) || F <-Filenames ],
+ Dirs = [suffix(filename:basename(F), "_tests_SUITE_data") ||
+ F <- FullFilenames,
+ file_utils:file_type(F) =:= {ok, 'directory'}],
+ [S || {yes, S} <- Dirs]
+ end.
+
+suffix(String, Suffix) ->
+ Index = string:rstr(String, Suffix),
+ case string:substr(String, Index) =:= Suffix of
+ true -> {yes, string:sub_string(String,1,Index-1)};
+ false -> no
+ end.
+
+-spec create_suite(string()) -> 'ok'.
+
+create_suite(SuiteName) ->
+ {ok, Cwd} = file:get_cwd(),
+ SuiteDirN = generate_suite_dir_from_name(Cwd, SuiteName),
+ OutputFile = generate_suite_file(Cwd, SuiteName),
+ {OptionsFileN, InputDirN} = check_neccessary_files(SuiteDirN),
+ generate_suite(SuiteName, OutputFile, OptionsFileN, InputDirN).
+
+generate_suite_dir_from_name(Cwd, SuiteName) ->
+ filename:join(Cwd, SuiteName ++ ?suite_suffix ++ ?data_folder).
+
+generate_suite_file(Cwd, SuiteName) ->
+ OutputFilename =
+ filename:join(Cwd, SuiteName ++ ?suite_suffix ++ ?erlang_extension),
+ case file:open(OutputFilename, [?output_file_mode]) of
+ {ok, IoDevice} -> IoDevice;
+ {error, _} = E -> exit({E, OutputFilename})
+ end.
+
+check_neccessary_files(SuiteDirN) ->
+ InputDirN = filename:join(SuiteDirN, ?input_files_directory),
+ check_file_exists(InputDirN, directory),
+ OptionsFileN = filename:join(SuiteDirN, ?dialyzer_option_file),
+ check_file_exists(OptionsFileN, regular),
+ {OptionsFileN, InputDirN}.
+
+check_file_exists(Filename, Type) ->
+ case file:read_file_info(Filename) of
+ {ok, FileInfo} ->
+ case FileInfo#file_info.type of
+ Type -> ok;
+ Else -> exit({error, {wrong_input_file_type, Else}})
+ end;
+ {error, _} = E -> exit({E, Filename, Type})
+ end.
+
+generate_suite(SuiteName, OutputFile, OptionsFileN, InputDirN) ->
+ Options = read_options(OptionsFileN),
+ TestCases = list_testcases(InputDirN),
+ Suite = #suite{suitename = SuiteName, outputfile = OutputFile,
+ options = Options, testcases = TestCases},
+ write_suite(Suite),
+ file:close(OutputFile).
+
+read_options(OptionsFileN) ->
+ case file:consult(OptionsFileN) of
+ {ok, Opts} -> read_options(Opts, #options{});
+ _ = E -> exit({error, {incorrect_options_file, E}})
+ end.
+
+read_options([List], Options) when is_list(List) ->
+ read_options(List, Options);
+read_options([], Options) ->
+ Options;
+read_options([{time_limit, TimeLimit}|Opts], Options) ->
+ read_options(Opts, Options#options{time_limit = TimeLimit});
+read_options([{dialyzer_options, DialyzerOptions}|Opts], Options) ->
+ read_options(Opts, Options#options{dialyzer_options = DialyzerOptions}).
+
+list_testcases(Dirname) ->
+ {ok, Files} = file_utils:list_dir(Dirname, ".erl", true),
+ [list_to_atom(filename:basename(F,".erl")) || F <-Files].
+
+write_suite(Suite) ->
+ write_header(Suite),
+ write_consistency(Suite),
+ write_testcases(Suite).
+
+write_header(#suite{suitename = SuiteName, outputfile = OutputFile,
+ options = Options, testcases = TestCases}) ->
+ Test_Plus_Consistency =
+ [list_to_atom(SuiteName ++ ?suite_suffix ++ "_consistency")|TestCases],
+ Exports = format_export(Test_Plus_Consistency),
+ TimeLimit = Options#options.time_limit,
+ DialyzerOptions = Options#options.dialyzer_options,
+ io:format(OutputFile,
+ "%% ATTENTION!\n"
+ "%% This is an automatically generated file. Do not edit.\n"
+ "%% Use './remake' script to refresh it if needed.\n"
+ "%% All Dialyzer options should be defined in dialyzer_options\n"
+ "%% file.\n\n"
+ "-module(~s).\n\n"
+ "-include(\"ct.hrl\").\n"
+ "-include(\"dialyzer_test_constants.hrl\").\n\n"
+ "-export([suite/0, init_per_suite/0, init_per_suite/1,\n"
+ " end_per_suite/1, all/0]).\n"
+ "~s\n\n"
+ "suite() ->\n"
+ " [{timetrap, {minutes, ~w}}].\n\n"
+ "init_per_suite() ->\n"
+ " [{timetrap, ?plt_timeout}].\n"
+ "init_per_suite(Config) ->\n"
+ " OutDir = ?config(priv_dir, Config),\n"
+ " case dialyzer_common:check_plt(OutDir) of\n"
+ " fail -> {skip, \"Plt creation/check failed.\"};\n"
+ " ok -> [{dialyzer_options, ~p}|Config]\n"
+ " end.\n\n"
+ "end_per_suite(_Config) ->\n"
+ " ok.\n\n"
+ "all() ->\n"
+ " ~p.\n\n"
+ "dialyze(Config, TestCase) ->\n"
+ " Opts = ?config(dialyzer_options, Config),\n"
+ " Dir = ?config(data_dir, Config),\n"
+ " OutDir = ?config(priv_dir, Config),\n"
+ " dialyzer_common:check(TestCase, Opts, Dir, OutDir)."
+ "\n\n"
+ ,[SuiteName ++ ?suite_suffix, Exports, TimeLimit,
+ DialyzerOptions, Test_Plus_Consistency]).
+
+format_export(TestCases) ->
+ TestCasesArity =
+ [list_to_atom(atom_to_list(N)++"/1") || N <- TestCases],
+ TestCaseString = io_lib:format("-export(~p).", [TestCasesArity]),
+ strip_quotes(lists:flatten(TestCaseString),[]).
+
+strip_quotes([], Result) ->
+ lists:reverse(Result);
+strip_quotes([$' |Rest], Result) ->
+ strip_quotes(Rest, Result);
+strip_quotes([$\, |Rest], Result) ->
+ strip_quotes(Rest, [$\ , $\, |Result]);
+strip_quotes([C|Rest], Result) ->
+ strip_quotes(Rest, [C|Result]).
+
+write_consistency(#suite{suitename = SuiteName, outputfile = OutputFile}) ->
+ write_consistency(SuiteName, OutputFile).
+
+write_consistency(SuiteName, OutputFile) ->
+ io:format(OutputFile,
+ "~s_consistency(Config) ->\n"
+ " Dir = ?config(data_dir, Config),\n"
+ " case dialyzer_common:new_tests(Dir, all()) of\n"
+ " [] -> ok;\n"
+ " New -> ct:fail({missing_tests,New})\n"
+ " end.\n\n",
+ [SuiteName ++ ?suite_suffix]).
+
+write_testcases(#suite{outputfile = OutputFile, testcases = TestCases}) ->
+ write_testcases(OutputFile, TestCases).
+
+write_testcases(OutputFile, [TestCase| Rest]) ->
+ io:format(OutputFile,
+ "~p(Config) ->\n"
+ " case dialyze(Config, ~p) of\n"
+ " 'same' -> 'same';\n"
+ " Error -> ct:fail(Error)\n"
+ " end.\n\n",
+ [TestCase, TestCase]),
+ write_testcases(OutputFile, Rest);
+write_testcases(_OutputFile, []) ->
+ ok.
diff --git a/lib/dialyzer/test/dialyzer_test_constants.hrl b/lib/dialyzer/test/dialyzer_test_constants.hrl
new file mode 100644
index 0000000000..5672327724
--- /dev/null
+++ b/lib/dialyzer/test/dialyzer_test_constants.hrl
@@ -0,0 +1 @@
+-define(plt_timeout, {hours, 2}).
diff --git a/lib/dialyzer/test/file_utils.erl b/lib/dialyzer/test/file_utils.erl
new file mode 100644
index 0000000000..36b368760c
--- /dev/null
+++ b/lib/dialyzer/test/file_utils.erl
@@ -0,0 +1,155 @@
+-module(file_utils).
+
+-export([list_dir/3, file_type/1, diff/2]).
+
+-include_lib("kernel/include/file.hrl").
+
+-type ext_posix()::posix()|'badarg'.
+-type posix()::atom().
+
+-spec list_dir(file:filename(), string(), boolean()) ->
+ {error, ext_posix()} | {ok, [file:filename()]}.
+
+list_dir(Dir, Extension, Dirs) ->
+ case file:list_dir(Dir) of
+ {error, _} = Error-> Error;
+ {ok, Filenames} ->
+ FullFilenames = [filename:join(Dir, F) || F <-Filenames ],
+ Matches1 = case Dirs of
+ true ->
+ [F || F <- FullFilenames,
+ file_type(F) =:= {ok, 'directory'}];
+ false -> []
+ end,
+ Matches2 = [F || F <- FullFilenames,
+ file_type(F) =:= {ok, 'regular'},
+ filename:extension(F) =:= Extension],
+ {ok, lists:sort(Matches1 ++ Matches2)}
+ end.
+
+-spec file_type(file:filename()) ->
+ {ok, 'device' | 'directory' | 'regular' | 'other'} |
+ {error, ext_posix()}.
+
+file_type(Filename) ->
+ case file:read_file_info(Filename) of
+ {ok, FI} -> {ok, FI#file_info.type};
+ Error -> Error
+ end.
+
+-type diff_result()::'same' | {'differ', diff_list()} |
+ {error, {file:filename(), term()}}.
+-type diff_list()::[{id(), line(), string()}].
+-type id()::'new'|'old'.
+-type line()::non_neg_integer().
+
+-spec diff(file:filename(), file:filename()) -> diff_result().
+
+diff(Filename1, Filename2) ->
+ File1 =
+ case file:open(Filename1, [read]) of
+ {ok, F1} -> {file, F1};
+ _ -> empty
+ end,
+ File2 =
+ case file:open(Filename2, [read]) of
+ {ok, F2} -> {file, F2};
+ _ -> empty
+ end,
+ case diff1(File1, File2) of
+ {error, {N, Error}} ->
+ case N of
+ 1 -> {error, {Filename1, Error}};
+ 2 -> {error, {Filename2, Error}}
+ end;
+ [] -> 'same';
+ DiffList -> {'differ', DiffList}
+ end.
+
+diff1(File1, File2) ->
+ case file_to_lines(File1) of
+ {error, Error} -> {error, {1, Error}};
+ Lines1 ->
+ case file_to_lines(File2) of
+ {error, Error} -> {error, {2, Error}};
+ Lines2 ->
+ Common = lcs_fast(Lines1, Lines2),
+ diff2(Lines1, 1, Lines2, 1, Common, [])
+ end
+ end.
+
+diff2([], _, [], _, [], Acc) -> lists:keysort(2,Acc);
+diff2([H1|T1], N1, [], N2, [], Acc) ->
+ diff2(T1, N1+1, [], N2, [], [{new, N1, H1}|Acc]);
+diff2([], N1, [H2|T2], N2, [], Acc) ->
+ diff2([], N1, T2, N2+1, [], [{old, N2, H2}|Acc]);
+diff2([H1|T1], N1, [H2|T2], N2, [], Acc) ->
+ diff2(T1, N1+1, T2, N2+1, [], [{new, N1, H1}, {old, N2, H2}|Acc]);
+diff2([H1|T1]=L1, N1, [H2|T2]=L2, N2, [HC|TC]=LC, Acc) ->
+ case H1 =:= H2 of
+ true -> diff2(T1, N1+1, T2, N2+1, TC, Acc);
+ false ->
+ case H1 =:= HC of
+ true -> diff2(L1, N1, T2, N2+1, LC, [{old, N2, H2}|Acc]);
+ false -> diff2(T1, N1+1, L2, N2, LC, [{new, N1, H1}|Acc])
+ end
+ end.
+
+-spec lcs_fast([string()], [string()]) -> [string()].
+
+lcs_fast(S1, S2) ->
+ M = length(S1),
+ N = length(S2),
+ Acc = array:new(M*N, {default, 0}),
+ {L, _} = lcs_fast(S1, S2, 1, 1, N, Acc),
+ L.
+
+-spec lcs_fast([string()], [string()],
+ pos_integer(), pos_integer(),
+ non_neg_integer(), array()) -> {[string()], array()}.
+
+lcs_fast([], _, _, _, _, Acc) ->
+ {[], Acc};
+lcs_fast(_, [], _, _, _, Acc) ->
+ {[], Acc};
+lcs_fast([H1|T1] = S1, [H2|T2] = S2, N1, N2, N, Acc) ->
+ I = (N1-1) * N + N2 - 1,
+ case array:get(I, Acc) of
+ 0 ->
+ case string:equal(H1, H2) of
+ true ->
+ {T, NAcc} = lcs_fast(T1, T2, N1+1, N2+1, N, Acc),
+ L = [H1|T],
+ {L, array:set(I, L, NAcc)};
+ false ->
+ {L1, NAcc1} = lcs_fast(S1, T2, N1, N2+1, N, Acc),
+ {L2, NAcc2} = lcs_fast(T1, S2, N1+1, N2, N, NAcc1),
+ L = longest(L1, L2),
+ {L, array:set(I, L, NAcc2)}
+ end;
+ L ->
+ {L, Acc}
+ end.
+
+-spec longest([string()], [string()]) -> [string()].
+
+longest(S1, S2) ->
+ case length(S1) > length(S2) of
+ true -> S1;
+ false -> S2
+ end.
+
+file_to_lines(empty) ->
+ [];
+file_to_lines({file, File}) ->
+ case file_to_lines(File, []) of
+ {error, _} = Error -> Error;
+ Lines -> lists:reverse(Lines)
+ end.
+
+file_to_lines(File, Acc) ->
+ case io:get_line(File, "") of
+ {error, _}=Error -> Error;
+ eof -> Acc;
+ A -> file_to_lines(File, [A|Acc])
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE.erl b/lib/dialyzer/test/opaque_tests_SUITE.erl
new file mode 100644
index 0000000000..6b90e7a646
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE.erl
@@ -0,0 +1,184 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(opaque_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([opaque_tests_SUITE_consistency/1, array/1, crash/1, dict/1,
+ ets/1, gb_sets/1, inf_loop1/1, int/1, mixed_opaque/1,
+ my_digraph/1, my_queue/1, opaque/1, queue/1, rec/1, timer/1,
+ union/1, wings/1, zoltan_kis1/1, zoltan_kis2/1, zoltan_kis3/1,
+ zoltan_kis4/1, zoltan_kis5/1, zoltan_kis6/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, [{warnings,[no_unused,no_return]}]}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [opaque_tests_SUITE_consistency,array,crash,dict,ets,gb_sets,inf_loop1,int,
+ mixed_opaque,my_digraph,my_queue,opaque,queue,rec,timer,union,wings,
+ zoltan_kis1,zoltan_kis2,zoltan_kis3,zoltan_kis4,zoltan_kis5,zoltan_kis6].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+opaque_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+array(Config) ->
+ case dialyze(Config, array) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+crash(Config) ->
+ case dialyze(Config, crash) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+dict(Config) ->
+ case dialyze(Config, dict) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets(Config) ->
+ case dialyze(Config, ets) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+gb_sets(Config) ->
+ case dialyze(Config, gb_sets) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+inf_loop1(Config) ->
+ case dialyze(Config, inf_loop1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+int(Config) ->
+ case dialyze(Config, int) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mixed_opaque(Config) ->
+ case dialyze(Config, mixed_opaque) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+my_digraph(Config) ->
+ case dialyze(Config, my_digraph) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+my_queue(Config) ->
+ case dialyze(Config, my_queue) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+opaque(Config) ->
+ case dialyze(Config, opaque) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+queue(Config) ->
+ case dialyze(Config, queue) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+rec(Config) ->
+ case dialyze(Config, rec) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+timer(Config) ->
+ case dialyze(Config, timer) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+union(Config) ->
+ case dialyze(Config, union) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+wings(Config) ->
+ case dialyze(Config, wings) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zoltan_kis1(Config) ->
+ case dialyze(Config, zoltan_kis1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zoltan_kis2(Config) ->
+ case dialyze(Config, zoltan_kis2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zoltan_kis3(Config) ->
+ case dialyze(Config, zoltan_kis3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zoltan_kis4(Config) ->
+ case dialyze(Config, zoltan_kis4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zoltan_kis5(Config) ->
+ case dialyze(Config, zoltan_kis5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zoltan_kis6(Config) ->
+ case dialyze(Config, zoltan_kis6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..3ff26b87db
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [no_unused, no_return]}]}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/array b/lib/dialyzer/test/opaque_tests_SUITE_data/results/array
new file mode 100644
index 0000000000..b05d088a03
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/array
@@ -0,0 +1,3 @@
+
+array_use.erl:12: The type test is_tuple(array()) breaks the opaqueness of the term array()
+array_use.erl:9: The attempt to match a term of type array() against the pattern {'array', _, _, 'undefined', _} breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/crash b/lib/dialyzer/test/opaque_tests_SUITE_data/results/crash
new file mode 100644
index 0000000000..6bdd934169
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/crash
@@ -0,0 +1,7 @@
+
+crash_1.erl:42: The specification for crash_1:empty/0 states that the function might also return crash_1:targetlist() but the inferred return is none()
+crash_1.erl:45: Record construction #targetlist{list::[]} violates the declared type of field list::'undefined' | crash_1:target()
+crash_1.erl:48: The call crash_1:get_using_branch2(Branch::maybe_improper_list(),L::'undefined' | crash_1:target()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
+crash_1.erl:50: The pattern <_Branch, []> can never match the type <maybe_improper_list(),'undefined' | crash_1:target()>
+crash_1.erl:52: The pattern <Branch, [H = {'target', _, _} | _T]> can never match the type <maybe_improper_list(),'undefined' | crash_1:target()>
+crash_1.erl:54: The pattern <Branch, [{'target', _, _} | T]> can never match the type <maybe_improper_list(),'undefined' | crash_1:target()>
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/dict b/lib/dialyzer/test/opaque_tests_SUITE_data/results/dict
new file mode 100644
index 0000000000..5c6bf6a927
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/dict
@@ -0,0 +1,15 @@
+
+dict_use.erl:41: The attempt to match a term of type dict() against the pattern 'gazonk' breaks the opaqueness of the term
+dict_use.erl:45: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
+dict_use.erl:46: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:51: The attempt to match a term of type dict() against the pattern [] breaks the opaqueness of the term
+dict_use.erl:52: The attempt to match a term of type dict() against the pattern 42 breaks the opaqueness of the term
+dict_use.erl:58: Attempt to test for equality between a term of type maybe_improper_list() and a term of opaque type dict()
+dict_use.erl:60: Attempt to test for inequality between a term of type atom() and a term of opaque type dict()
+dict_use.erl:64: Guard test length(D::dict()) breaks the opaqueness of its argument
+dict_use.erl:65: Guard test is_atom(D::dict()) breaks the opaqueness of its argument
+dict_use.erl:66: Guard test is_list(D::dict()) breaks the opaqueness of its argument
+dict_use.erl:70: The type test is_list(dict()) breaks the opaqueness of the term dict()
+dict_use.erl:73: The call dict:fetch('foo',[1 | 2 | 3,...]) does not have an opaque term of type dict() as 2nd argument
+dict_use.erl:76: The call dict:merge(Fun::any(),42,[1 | 2,...]) does not have opaque terms as 2nd and 3rd arguments
+dict_use.erl:79: The call dict:store(42,'elli',{'dict',0,16,16,8,80,48,{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}) does not have an opaque term of type dict() as 3rd argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/ets b/lib/dialyzer/test/opaque_tests_SUITE_data/results/ets
new file mode 100644
index 0000000000..5498ba1538
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/ets
@@ -0,0 +1,3 @@
+
+ets_use.erl:12: Guard test is_integer(T::atom() | tid()) breaks the opaqueness of its argument
+ets_use.erl:7: Guard test is_integer(T::tid()) breaks the opaqueness of its argument
diff --git a/lib/xmerl/src/xmerl_dtd.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/results/gb_sets
index e69de29bb2..e69de29bb2 100644
--- a/lib/xmerl/src/xmerl_dtd.erl
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/gb_sets
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1 b/lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1
new file mode 100644
index 0000000000..eb8f304905
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/inf_loop1
@@ -0,0 +1,5 @@
+
+inf_loop1.erl:119: The pattern [{_, LNorms}] can never match the type []
+inf_loop1.erl:121: The pattern [{LinksA, LNormA}, {LinksB, LNormB}] can never match the type []
+inf_loop1.erl:129: The pattern [{_, Norm} | _] can never match the type []
+inf_loop1.erl:71: The call gb_trees:get(Edge::any(),Etab::array()) contains an opaque term as 2nd argument when terms of different types are expected in these positions
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/int b/lib/dialyzer/test/opaque_tests_SUITE_data/results/int
new file mode 100644
index 0000000000..3ee4def34b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/int
@@ -0,0 +1,3 @@
+
+int_adt.erl:28: Invalid type specification for function int_adt:add_f/2. The success typing is (number(),float()) -> number()
+int_adt.erl:32: Invalid type specification for function int_adt:div_f/2. The success typing is (number(),number()) -> float()
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque b/lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque
new file mode 100644
index 0000000000..ab850b613e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/mixed_opaque
@@ -0,0 +1,2 @@
+
+mixed_opaque_use.erl:31: The call mixed_opaque_rec_adt:get_a(Q::mixed_opaque_queue_adt:my_queue()) contains an opaque term as 1st argument when an opaque term of type mixed_opaque_rec_adt:rec() is expected
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph b/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_digraph
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue b/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue
new file mode 100644
index 0000000000..2860b91084
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/my_queue
@@ -0,0 +1,7 @@
+
+my_queue_use.erl:15: The call my_queue_adt:is_empty([]) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
+my_queue_use.erl:19: The call my_queue_adt:add(42,Q0::[]) does not have an opaque term of type my_queue_adt:my_queue() as 2nd argument
+my_queue_use.erl:24: The attempt to match a term of type my_queue_adt:my_queue() against the pattern [42 | Q2] breaks the opaqueness of the term
+my_queue_use.erl:30: Attempt to test for equality between a term of type [] and a term of opaque type my_queue_adt:my_queue()
+my_queue_use.erl:34: Cons will produce an improper list since its 2nd argument is my_queue_adt:my_queue()
+my_queue_use.erl:34: The call my_queue_adt:dequeue(nonempty_improper_list(42,my_queue_adt:my_queue())) does not have an opaque term of type my_queue_adt:my_queue() as 1st argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque b/lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque
new file mode 100644
index 0000000000..ca76f57b54
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/opaque
@@ -0,0 +1,2 @@
+
+opaque_bug4.erl:20: The attempt to match a term of type opaque_adt:abc() against the pattern 'a' breaks the opaqueness of the term
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/queue b/lib/dialyzer/test/opaque_tests_SUITE_data/results/queue
new file mode 100644
index 0000000000..59ce33f098
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/queue
@@ -0,0 +1,11 @@
+
+queue_use.erl:18: The call queue:is_empty({[],[]}) does not have an opaque term of type queue() as 1st argument
+queue_use.erl:22: The call queue:in(42,Q0::{[],[]}) does not have an opaque term of type queue() as 2nd argument
+queue_use.erl:27: The attempt to match a term of type queue() against the pattern {"*", Q2} breaks the opaqueness of the term
+queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue()
+queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term
+queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument
+queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions
+queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue()
+queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument
+queue_use.erl:65: The call queue:in(F::42,Q::'gazonk') does not have an opaque term of type queue() as 2nd argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/rec b/lib/dialyzer/test/opaque_tests_SUITE_data/results/rec
new file mode 100644
index 0000000000..72736b3b3c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/rec
@@ -0,0 +1,6 @@
+
+rec_use.erl:17: The attempt to match a term of type rec_adt:rec() against the pattern {'rec', _, 42} breaks the opaqueness of the term
+rec_use.erl:18: Guard test tuple_size(R::rec_adt:rec()) breaks the opaqueness of its argument
+rec_use.erl:23: The call rec_adt:get_a(R::tuple()) does not have an opaque term of type rec_adt:rec() as 1st argument
+rec_use.erl:27: Attempt to test for equality between a term of type {'rec','gazonk',42} and a term of opaque type rec_adt:rec()
+rec_use.erl:30: The call erlang:tuple_size(rec_adt:rec()) contains an opaque term as 1st argument when a structured term of type tuple() is expected
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/timer b/lib/dialyzer/test/opaque_tests_SUITE_data/results/timer
new file mode 100644
index 0000000000..e917b76b08
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/timer
@@ -0,0 +1,4 @@
+
+timer_use.erl:16: The pattern 'gazonk' can never match the type {'error',_} | {'ok',timer:tref()}
+timer_use.erl:17: The attempt to match a term of type {'ok',timer:tref()} against the pattern {'ok', 42} breaks the opaqueness of timer:tref()
+timer_use.erl:18: The attempt to match a term of type {'error',_} | {'ok',timer:tref()} against the pattern {Tag, 'gazonk'} breaks the opaqueness of timer:tref()
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/union b/lib/dialyzer/test/opaque_tests_SUITE_data/results/union
new file mode 100644
index 0000000000..98829b424a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/union
@@ -0,0 +1,5 @@
+
+union_use.erl:12: The attempt to match a term of type union_adt:u() against the pattern 'aaa' breaks the opaqueness of the term
+union_use.erl:16: The type test is_tuple(union_adt:u()) breaks the opaqueness of the term union_adt:u()
+union_use.erl:7: Guard test is_atom(A::union_adt:u()) breaks the opaqueness of its argument
+union_use.erl:8: Guard test is_tuple(T::union_adt:u()) breaks the opaqueness of its argument
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/results/wings b/lib/dialyzer/test/opaque_tests_SUITE_data/results/wings
new file mode 100644
index 0000000000..a9571441f8
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/results/wings
@@ -0,0 +1,11 @@
+
+wings_dissolve.erl:103: Guard test is_list(List::gb_set()) breaks the opaqueness of its argument
+wings_dissolve.erl:19: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
+wings_dissolve.erl:272: Guard test is_list(Faces::gb_set()) breaks the opaqueness of its argument
+wings_dissolve.erl:31: The call gb_sets:is_empty(Faces::[any(),...]) does not have an opaque term of type gb_set() as 1st argument
+wings_edge.erl:205: The pattern <Edge, 'hard', Htab> can never match the type <_,'soft',_>
+wings_edge_cmd.erl:30: The call gb_trees:size(P::gb_set()) contains an opaque term as 1st argument when an opaque term of type gb_tree() is expected
+wings_edge_cmd.erl:32: The pattern [_ | Parts] can never match the type []
+wings_edge_cmd.erl:32: The pattern [{_, P} | _] can never match the type []
+wings_io.erl:30: The attempt to match a term of type {'empty',queue()} against the pattern {'empty', {In, Out}} breaks the opaqueness of queue()
+wings_we.erl:155: The call wings_util:gb_trees_largest_key(Etab::gb_tree()) contains an opaque term as 1st argument when a structured term of type {_,{_,_,_,'nil' | {_,_,_,'nil' | {_,_,_,_}}}} is expected
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl
new file mode 100644
index 0000000000..1702dc8f03
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/array/array_use.erl
@@ -0,0 +1,15 @@
+-module(array_use).
+
+-export([ok1/0, wrong1/0, wrong2/0]).
+
+ok1() ->
+ array:set(17, gazonk, array:new()).
+
+wrong1() ->
+ {array, _, _, undefined, _} = array:new(42).
+
+wrong2() ->
+ case is_tuple(array:new(42)) of
+ true -> structure_is_exposed;
+ false -> cannot_possibly_be
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl
new file mode 100644
index 0000000000..eebeed15af
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/crash/crash_1.erl
@@ -0,0 +1,55 @@
+%%%-------------------------------------------------------------------
+%%% From : Fredrik Thulin <[email protected]>
+%%%
+%%% A module with an erroneous record field declaration which mixes up
+%%% structured and opaque terms and causes a crash in dialyzer.
+%%%
+%%% In addition, it revealed that the compiler produced extraneous
+%%% warnings about unused record definitions when in fact they are
+%%% needed for type declarations. This is now fixed.
+%%%-------------------------------------------------------------------
+-module(crash_1).
+
+-export([add/3, empty/0]).
+
+%%--------------------------------------------------------------------
+
+-record(sipurl, {proto = "sip" :: string(), host :: string()}).
+-record(keylist, {list = [] :: [_]}).
+-type sip_headers() :: #keylist{}.
+-record(request, {uri :: #sipurl{}, header :: sip_headers()}).
+-type sip_request() :: #request{}.
+
+%%--------------------------------------------------------------------
+
+-record(target, {branch :: string(), request :: sip_request()}).
+-opaque target() :: #target{}.
+
+-record(targetlist, {list :: target()}). % XXX: THIS ONE SHOULD READ [target()]
+-opaque targetlist() :: #targetlist{}.
+
+%%====================================================================
+
+add(Branch, #request{} = Request, #targetlist{list = L} = TargetList) ->
+ case get_using_branch(Branch, TargetList) of
+ none ->
+ NewTarget = #target{branch = Branch, request = Request},
+ #targetlist{list = L ++ [NewTarget]};
+ #target{} ->
+ TargetList
+ end.
+
+-spec empty() -> targetlist().
+
+empty() ->
+ #targetlist{list = []}.
+
+get_using_branch(Branch, #targetlist{list = L}) when is_list(Branch) ->
+ get_using_branch2(Branch, L).
+
+get_using_branch2(_Branch, []) ->
+ none;
+get_using_branch2(Branch, [#target{branch=Branch}=H | _T]) ->
+ H;
+get_using_branch2(Branch, [#target{} | T]) ->
+ get_using_branch2(Branch, T).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl
new file mode 100644
index 0000000000..2a632a910d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/dict/dict_use.erl
@@ -0,0 +1,83 @@
+-module(dict_use).
+
+-export([ok1/0, ok2/0, ok3/0, ok4/0, ok5/0, ok6/0]).
+-export([middle/0]).
+-export([w1/0, w2/0, w3/0, w4/1, w5/0, w6/0, w7/0, w8/1, w9/0]).
+
+-define(DICT, dict).
+
+%%---------------------------------------------------------------------
+%% Cases that are OK
+%%---------------------------------------------------------------------
+
+ok1() ->
+ dict:new().
+
+ok2() ->
+ case dict:new() of X -> X end.
+
+ok3() ->
+ Dict1 = dict:new(),
+ Dict2 = dict:new(),
+ Dict1 =:= Dict2.
+
+ok4() ->
+ dict:fetch(foo, dict:new()).
+
+ok5() -> % this is OK since some_mod:new/0 might be returning a dict()
+ dict:fetch(foo, some_mod:new()).
+
+ok6() ->
+ dict:store(42, elli, dict:new()).
+
+middle() ->
+ {w1(), w2()}.
+
+%%---------------------------------------------------------------------
+%% Cases that are problematic w.r.t. opaqueness of types
+%%---------------------------------------------------------------------
+
+w1() ->
+ gazonk = dict:new().
+
+w2() ->
+ case dict:new() of
+ [] -> nil;
+ 42 -> weird
+ end.
+
+w3() ->
+ try dict:new() of
+ [] -> nil;
+ 42 -> weird
+ catch
+ _:_ -> exception
+ end.
+
+w4(Dict) when is_list(Dict) ->
+ Dict =:= dict:new();
+w4(Dict) when is_atom(Dict) ->
+ Dict =/= dict:new().
+
+w5() ->
+ case dict:new() of
+ D when length(D) =/= 42 -> weird;
+ D when is_atom(D) -> weirder;
+ D when is_list(D) -> gazonk
+ end.
+
+w6() ->
+ is_list(dict:new()).
+
+w7() ->
+ dict:fetch(foo, [1,2,3]).
+
+w8(Fun) ->
+ dict:merge(Fun, 42, [1,2]).
+
+w9() ->
+ dict:store(42, elli,
+ {dict,0,16,16,8,80,48,
+ {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
+ {{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]}}}).
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl
new file mode 100644
index 0000000000..20be9803eb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/ets/ets_use.erl
@@ -0,0 +1,17 @@
+-module(ets_use).
+-export([t1/0, t2/0]).
+
+t1() ->
+ case n() of
+ T when is_atom(T) -> atm;
+ T when is_integer(T) -> int
+ end.
+
+t2() ->
+ case n() of
+ T when is_integer(T) -> int;
+ T when is_atom(T) -> atm
+ end.
+
+n() -> ets:new(n, [named_table]).
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl
new file mode 100644
index 0000000000..008b0a486a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/gb_sets/gb_sets_rec.erl
@@ -0,0 +1,23 @@
+%%---------------------------------------------------------------------
+%% This module does not test gb_sets. Instead it tests that we can
+%% create records whose fields are declared with an opaque type and
+%% retrieve these fields without problems. Unitialized record fields
+%% used to cause trouble for the analysis due to the implicit
+%% 'undefined' value that record fields contain. The problem was the
+%% strange interaction of ?opaque() and ?union() in the definition of
+%% erl_types:t_inf/3. This was fixed 18/1/2009.
+%% --------------------------------------------------------------------
+
+-module(gb_sets_rec).
+
+-export([new/0, get_g/1]).
+
+-record(rec, {g :: gb_set()}).
+
+-spec new() -> #rec{}.
+new() ->
+ #rec{g = gb_sets:empty()}.
+
+-spec get_g(#rec{}) -> gb_set().
+get_g(R) ->
+ R#rec.g.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl
new file mode 100644
index 0000000000..0dff16cf14
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/inf_loop1.erl
@@ -0,0 +1,172 @@
+%% -*- erlang-indent-level: 2 -*-
+%%----------------------------------------------------------------------------
+%% Non-sensical (i.e., stripped-down) program that sends the analysis
+%% into an infinite loop. The #we.es field was originally a gb_tree()
+%% but the programmer declared it as an array in order to change it to
+%% that data type instead. In the file, there are two calls to function
+%% gb_trees:get/2 which seem to be the ones responsible for sending the
+%% analysis into an infinite loop. Currently, these calls are marked and
+%% have been changed to gbee_trees:get/2 in order to be able to see that
+%% the analysis works if these two calls are taken out of the picture.
+%%----------------------------------------------------------------------------
+-module(inf_loop1).
+
+-export([command/1]).
+
+-record(we, {id,
+ es = array:new() :: array(),
+ vp,
+ mirror = none}).
+-record(edge, {vs,ve,a = none,b = none,lf,rf,ltpr,ltsu,rtpr,rtsu}).
+
+command(St) ->
+ State = drag_mode(offset_region),
+ SetupSt = wings_sel_conv:more(St),
+ Tvs = wings_sel:fold(fun(Faces, #we{id = Id} = We, Acc) ->
+ FaceRegions = wings_sel:face_regions(Faces, We),
+ {AllVs0,VsData} =
+ collect_offset_regions_data(FaceRegions, We, [], []),
+ AllVs = ordsets:from_list(AllVs0),
+ [{Id,{AllVs,offset_regions_fun(VsData, State)}}|Acc]
+ end,
+ [],
+ SetupSt),
+ wings_drag:setup(Tvs, 42, [], St).
+
+drag_mode(Type) ->
+ {Mode,Norm} = wings_pref:get_value(Type, {average,loop}),
+ {Type,Mode,Norm}.
+
+collect_offset_regions_data([Faces|Regions], We, AllVs, VsData) ->
+ {FaceNormTab,OuterEdges,RegVs} =
+ some_fake_module:faces_data_0(Faces, We, [], [], []),
+ {LoopNorm,LoopVsData,LoopVs} =
+ offset_regions_loop_data(OuterEdges, Faces, We, FaceNormTab),
+ Vs = RegVs -- LoopVs,
+ RegVsData = vertex_normals(Vs, FaceNormTab, We, LoopVsData),
+ collect_offset_regions_data(Regions, We, RegVs ++ AllVs,
+ [{LoopNorm,RegVsData}|VsData]);
+collect_offset_regions_data([], _, AllVs, VsData) ->
+ {AllVs,VsData}.
+
+offset_regions_loop_data(Edges, Faces, We, FNtab) ->
+ EdgeSet = gb_sets:from_list(Edges),
+ offset_loop_data_0(EdgeSet, Faces, We, FNtab, [], [], []).
+
+offset_loop_data_0(EdgeSet0, Faces, We, FNtab, LNorms, VData0, Vs0) ->
+ case gb_sets:is_empty(EdgeSet0) of
+ false ->
+ {Edge,EdgeSet1} = gb_sets:take_smallest(EdgeSet0),
+ {EdgeSet,VData,Links,LoopNorm,Vs} =
+ offset_loop_data_1(Edge, EdgeSet1, Faces, We, FNtab, VData0, Vs0),
+ offset_loop_data_0(EdgeSet, Faces, We, FNtab,
+ [{Links,LoopNorm}|LNorms], VData, Vs);
+ true ->
+ AvgLoopNorm = average_loop_norm(LNorms),
+ {AvgLoopNorm,VData0,Vs0}
+ end.
+
+offset_loop_data_1(Edge, EdgeSet, _Faces,
+ #we{es = Etab, vp = Vtab} = We, FNtab, VData, Vs) ->
+ #edge{vs = Va, ve = Vb, lf = Lf, ltsu = NextLeft} = gb_trees:get(Edge, Etab),
+ VposA = gb_trees:get(Va, Vtab),
+ VposB = gb_trees:get(Vb, Vtab),
+ VDir = e3d_vec:sub(VposB, VposA),
+ FNorm = wings_face:normal(Lf, We),
+ EdgeData = gb_trees:get(NextLeft, Etab),
+ offset_loop_data_2(NextLeft, EdgeData, Va, VposA, Lf, Edge, We, FNtab,
+ EdgeSet, VDir, [], [FNorm], VData, [], Vs, 0).
+
+offset_loop_data_2(CurE, #edge{vs = Va, ve = Vb, lf = PrevFace,
+ rtsu = NextEdge, ltsu = IfCurIsMember},
+ Vb, VposB, PrevFace, LastE,
+ #we{mirror = M} = We,
+ FNtab, EdgeSet0, VDir, EDir0, VNorms0, VData0, VPs0, Vs0,
+ Links) ->
+ Mirror = M == PrevFace,
+ offset_loop_is_member(Mirror, Vb, Va, VposB, CurE, IfCurIsMember, VNorms0,
+ NextEdge, EdgeSet0, VDir, EDir0, FNtab, PrevFace,
+ LastE, We, VData0, VPs0, Vs0, Links).
+
+offset_loop_is_member(Mirror, V1, V2, Vpos1, CurE, NextE, VNorms0, NEdge,
+ EdgeSet0, VDir, EDir0, FNtab, PFace, LastE, We,
+ VData0, VPs0, Vs0, Links) ->
+ #we{es = Etab, vp = Vtab} = We,
+ Vpos2 = gb_trees:get(V2, Vtab),
+ Dir = e3d_vec:sub(Vpos2, Vpos1),
+ NextVDir = e3d_vec:neg(Dir),
+ EdgeSet = gb_sets:delete(CurE, EdgeSet0),
+ EdgeData = gbee_trees:get(NextE, Etab), %% HERE
+ [FNorm|_] = VNorms0,
+ VData = offset_loop_data_3(Mirror, V1, Vpos1, VNorms0, NEdge, VDir,
+ Dir, EDir0, FNtab, We, VData0),
+ VPs = [Vpos1|VPs0],
+ Vs = [V1|Vs0],
+ offset_loop_data_2(NextE, EdgeData, V2, Vpos2, PFace, LastE, We, FNtab,
+ EdgeSet, NextVDir, [], [FNorm], VData, VPs, Vs, Links + 1).
+
+offset_loop_data_3(false, V, Vpos, VNorms0, NextEdge,
+ VDir, Dir, EDir0, FNtab, We, VData0) ->
+ #we{es = Etab} = We,
+ VNorm = e3d_vec:norm(e3d_vec:add(VNorms0)),
+ NV = wings_vertex:other(V, gbee_trees:get(NextEdge, Etab)), %% HERE
+ ANorm = vertex_normal(NV, FNtab, We),
+ EDir = some_fake_module:average_edge_dir(VNorm, VDir, Dir, EDir0),
+ AvgDir = some_fake_module:evaluate_vdata(VDir, Dir, VNorm),
+ ScaledDir = some_fake_module:along_edge_scale_factor(VDir, Dir, EDir, ANorm),
+ [{V,{Vpos,AvgDir,EDir,ScaledDir}}|VData0].
+
+average_loop_norm([{_,LNorms}]) ->
+ e3d_vec:norm(LNorms);
+average_loop_norm([{LinksA,LNormA},{LinksB,LNormB}]) ->
+ case LinksA < LinksB of
+ true ->
+ e3d_vec:norm(e3d_vec:add(e3d_vec:neg(LNormA), LNormB));
+ false ->
+ e3d_vec:norm(e3d_vec:add(e3d_vec:neg(LNormB), LNormA))
+ end;
+average_loop_norm(LNorms) ->
+ LoopNorms = [Norm || {_,Norm} <- LNorms],
+ e3d_vec:norm(e3d_vec:neg(e3d_vec:add(LoopNorms))).
+
+vertex_normals([V|Vs], FaceNormTab, #we{vp = Vtab, mirror = M} = We, Acc) ->
+ FaceNorms =
+ wings_vertex:fold(fun(_, Face, _, A) when Face == M ->
+ [e3d_vec:neg(wings_face:normal(M, We))|A];
+ (_, Face, _, A) ->
+ [gb_trees:get(Face, FaceNormTab)|A]
+ end, [], V, We),
+ VNorm = e3d_vec:norm(e3d_vec:add(FaceNorms)),
+ Vpos = gb_trees:get(V, Vtab),
+ vertex_normals(Vs, FaceNormTab, We, [{V,{Vpos,VNorm}}|Acc]);
+vertex_normals([], _, _, Acc) ->
+ Acc.
+
+vertex_normal(V, FaceNormTab, #we{mirror = M} = We) ->
+ wings_vertex:fold(fun(_, Face, _, A) when Face == M ->
+ [e3d_vec:neg(wings_face:normal(Face, We))|A];
+ (_, Face, _, A) ->
+ N = gb_trees:get(Face, FaceNormTab),
+ case e3d_vec:is_zero(N) of
+ true -> A;
+ false -> [N|A]
+ end
+ end, [], V, We).
+
+offset_regions_fun(OffsetData, {_,Solution,_} = State) ->
+ fun(new_mode_data, {NewState,_}) ->
+ offset_regions_fun(OffsetData, NewState);
+ ([Dist,_,_,Bump|_], A) ->
+ lists:foldl(fun({LoopNormal,VsData}, VsAcc0) ->
+ lists:foldl(fun({V,{Vpos0,VNorm}}, VsAcc) ->
+ [{V,Vpos0}|VsAcc];
+ ({V,{Vpos0,Dir,EDir,ScaledEDir}}, VsAcc) ->
+ Vec = case Solution of
+ average -> Dir;
+ along_edges -> EDir;
+ scaled -> ScaledEDir
+ end,
+ [{V,Vpos0}|VsAcc]
+ end, VsAcc0, VsData)
+ end, A, OffsetData)
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl
new file mode 100644
index 0000000000..99f8cbdc4a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_adt.erl
@@ -0,0 +1,33 @@
+%%----------------------------------------------------------------------------
+%% Module that tests consistency of spec declarations in the presence of
+%% opaque types. Contains both valid and invalid contracts with opaque types.
+%%----------------------------------------------------------------------------
+
+-module(int_adt).
+
+-export([new_i/0, add_i/2, div_i/2, add_f/2, div_f/2]).
+
+-export_type([int/0]).
+
+-opaque int() :: integer().
+
+%% the user has declared the return to be an opaque type, but the success
+%% typing inference is too strong and finds a subtype as a return: this is OK
+-spec new_i() -> int().
+new_i() -> 42.
+
+%% the success typing is more general than the contract: this is OK
+-spec add_i(int(), int()) -> int().
+add_i(X, Y) -> X + Y.
+
+%% the success typing coincides with the contract: this is OK, of course
+-spec div_i(int(), int()) -> int().
+div_i(X, Y) -> X div Y.
+
+%% the success typing has an incompatible domain element: this is invalid
+-spec add_f(int(), int()) -> int().
+add_f(X, Y) when is_float(Y) -> X + trunc(Y).
+
+%% the success typing has an incompatible range: this is invalid
+-spec div_f(int(), int()) -> int().
+div_f(X, Y) -> X / Y.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl
new file mode 100644
index 0000000000..b4471e1cee
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/int/int_use.erl
@@ -0,0 +1,11 @@
+%%---------------------------------------------------------------------------
+%% Module that uses the opaque types of int_adt.
+%% TODO: Should be extended with invalid contracts.
+%%---------------------------------------------------------------------------
+-module(int_use).
+
+-export([test/0]).
+
+-spec test() -> int_adt:int().
+test() ->
+ int_adt:new_i().
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
new file mode 100644
index 0000000000..ac59f19cd3
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_queue_adt.erl
@@ -0,0 +1,26 @@
+%%---------------------------------------------------------------------------
+%% A clone of 'queue_adt' so as to test its combination with 'rec_adt'
+%%---------------------------------------------------------------------------
+-module(mixed_opaque_queue_adt).
+
+-export([new/0, add/2, dequeue/1, is_empty/1]).
+
+-opaque my_queue() :: list().
+
+-spec new() -> my_queue().
+new() ->
+ [].
+
+-spec add(term(), my_queue()) -> my_queue().
+add(E, Q) ->
+ Q ++ [E].
+
+-spec dequeue(my_queue()) -> {term(), my_queue()}.
+dequeue([H|T]) ->
+ {H, T}.
+
+-spec is_empty(my_queue()) -> boolean().
+is_empty([]) ->
+ true;
+is_empty([_|_]) ->
+ false.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
new file mode 100644
index 0000000000..61bae5110d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_rec_adt.erl
@@ -0,0 +1,25 @@
+%%---------------------------------------------------------------------------
+%% A clone of 'rec_adt' so as to test its combination with 'queue_adt'
+%%---------------------------------------------------------------------------
+-module(mixed_opaque_rec_adt).
+
+-export([new/0, get_a/1, get_b/1, set_a/2, set_b/2]).
+
+-record(rec, {a :: atom(), b = 0 :: integer()}).
+
+-opaque rec() :: #rec{}.
+
+-spec new() -> rec().
+new() -> #rec{a = gazonk, b = 42}.
+
+-spec get_a(rec()) -> atom().
+get_a(#rec{a = A}) -> A.
+
+-spec get_b(rec()) -> integer().
+get_b(#rec{b = B}) -> B.
+
+-spec set_a(rec(), atom()) -> rec().
+set_a(R, A) -> R#rec{a = A}.
+
+-spec set_b(rec(), integer()) -> rec().
+set_b(R, B) -> R#rec{b = B}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
new file mode 100644
index 0000000000..e82dcd5f38
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/mixed_opaque/mixed_opaque_use.erl
@@ -0,0 +1,31 @@
+%%---------------------------------------------------------------------------
+%% Test that tries some combinations of using more than one opaque data type
+%% in the same function(s).
+%%----------------------------------------------------------------------------
+-module(mixed_opaque_use).
+
+-export([ok1/1, ok2/0, wrong1/0]).
+
+-define(REC, mixed_opaque_rec_adt).
+-define(QUEUE, mixed_opaque_queue_adt).
+
+%% Currently returning unions of opaque types is considered OK
+ok1(Type) ->
+ case Type of
+ queue -> ?QUEUE:new();
+ rec -> ?REC:new()
+ end.
+
+%% Constructing a queue of records is OK
+ok2() ->
+ Q0 = ?QUEUE:new(),
+ R0 = ?REC:new(),
+ Q1 = ?QUEUE:add(R0, Q0),
+ {R1,_Q2} = ?QUEUE:dequeue(Q1),
+ ?REC:get_a(R1).
+
+%% But of course calling a function expecting some opaque type
+%% with some other opaque typs is not OK
+wrong1() ->
+ Q = ?QUEUE:new(),
+ ?REC:get_a(Q).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl
new file mode 100644
index 0000000000..20c72aa6eb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_digraph/my_digraph_adt.erl
@@ -0,0 +1,51 @@
+-module(my_digraph_adt).
+
+-export([new/0, new/1]).
+
+-record(my_digraph, {vtab = notable,
+ etab = notable,
+ ntab = notable,
+ cyclic = true :: boolean()}).
+
+-opaque my_digraph() :: #my_digraph{}.
+
+-type d_protection() :: 'private' | 'protected'.
+-type d_cyclicity() :: 'acyclic' | 'cyclic'.
+-type d_type() :: d_cyclicity() | d_protection().
+
+-spec new() -> my_digraph().
+new() -> new([]).
+
+-spec new([atom()]) -> my_digraph().
+new(Type) ->
+ try check_type(Type, protected, []) of
+ {Access, Ts} ->
+ V = ets:new(vertices, [set, Access]),
+ E = ets:new(edges, [set, Access]),
+ N = ets:new(neighbours, [bag, Access]),
+ ets:insert(N, [{'$vid', 0}, {'$eid', 0}]),
+ set_type(Ts, #my_digraph{vtab=V, etab=E, ntab=N})
+ catch
+ throw:Error -> throw(Error)
+ end.
+
+-spec check_type([atom()], d_protection(), [{'cyclic', boolean()}]) ->
+ {d_protection(), [{'cyclic', boolean()}]}.
+
+check_type([acyclic|Ts], A, L) ->
+ check_type(Ts, A,[{cyclic,false} | L]);
+check_type([cyclic | Ts], A, L) ->
+ check_type(Ts, A, [{cyclic,true} | L]);
+check_type([protected | Ts], _, L) ->
+ check_type(Ts, protected, L);
+check_type([private | Ts], _, L) ->
+ check_type(Ts, private, L);
+check_type([T | _], _, _) ->
+ throw({error, {unknown_type, T}});
+check_type([], A, L) -> {A, L}.
+
+-spec set_type([{'cyclic', boolean()}], my_digraph()) -> my_digraph().
+
+set_type([{cyclic,V} | Ks], G) ->
+ set_type(Ks, G#my_digraph{cyclic = V});
+set_type([], G) -> G.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl
new file mode 100644
index 0000000000..52688062ce
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_adt.erl
@@ -0,0 +1,23 @@
+-module(my_queue_adt).
+
+-export([new/0, add/2, dequeue/1, is_empty/1]).
+
+-opaque my_queue() :: list().
+
+-spec new() -> my_queue().
+new() ->
+ [].
+
+-spec add(term(), my_queue()) -> my_queue().
+add(E, Q) ->
+ Q ++ [E].
+
+-spec dequeue(my_queue()) -> {term(), my_queue()}.
+dequeue([H|T]) ->
+ {H, T}.
+
+-spec is_empty(my_queue()) -> boolean().
+is_empty([]) ->
+ true;
+is_empty([_|_]) ->
+ false.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl
new file mode 100644
index 0000000000..98f9972c1e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/my_queue/my_queue_use.erl
@@ -0,0 +1,35 @@
+-module(my_queue_use).
+
+-export([ok1/0, ok2/0, wrong1/0, wrong2/0, wrong3/0, wrong4/0, wrong5/0]).
+
+ok1() ->
+ my_queue_adt:is_empty(my_queue_adt:new()).
+
+ok2() ->
+ Q0 = my_queue_adt:new(),
+ Q1 = my_queue_adt:add(42, Q0),
+ {42, Q2} = my_queue_adt:dequeue(Q1),
+ my_queue_adt:is_empty(Q2).
+
+wrong1() ->
+ my_queue_adt:is_empty([]).
+
+wrong2() ->
+ Q0 = [],
+ my_queue_adt:add(42, Q0).
+
+wrong3() ->
+ Q0 = my_queue_adt:new(),
+ Q1 = my_queue_adt:add(42, Q0),
+ [42|Q2] = Q1,
+ Q2.
+
+wrong4() ->
+ Q0 = my_queue_adt:new(),
+ Q1 = my_queue_adt:add(42, Q0),
+ Q1 =:= [].
+
+wrong5() ->
+ Q0 = my_queue_adt:new(),
+ {42, Q2} = my_queue_adt:dequeue([42|Q0]),
+ Q2.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl
new file mode 100644
index 0000000000..3456f0e9c6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_adt.erl
@@ -0,0 +1,9 @@
+-module(opaque_adt).
+-export([atom_or_list/1]).
+
+-opaque abc() :: 'a' | 'b' | 'c'.
+
+atom_or_list(1) -> a;
+atom_or_list(2) -> b;
+atom_or_list(3) -> c;
+atom_or_list(N) -> lists:duplicate(N, a).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl
new file mode 100644
index 0000000000..ff0b1d05ab
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug1.erl
@@ -0,0 +1,17 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis went into an infinite loop due to
+%% specialization using structured type instead of the opaque one.
+%%---------------------------------------------------------------------
+
+-module(opaque_bug1).
+
+-export([test/1]).
+
+-record(c, {a::atom()}).
+
+-opaque erl_type() :: 'any' | #c{}.
+
+test(#c{a=foo} = T) -> local(T).
+
+local(#c{a=foo}) -> any.
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl
new file mode 100644
index 0000000000..f193a58f59
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug2.erl
@@ -0,0 +1,13 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis gave a bogus warning due to
+%% considering the function call name to be of opaque type...
+%%---------------------------------------------------------------------
+
+-module(opaque_bug2).
+
+-export([test/0]).
+
+-opaque o() :: 'map'.
+
+test() ->
+ lists:map(fun(X) -> X+1 end, [1,2]).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl
new file mode 100644
index 0000000000..71da82a1f6
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug3.erl
@@ -0,0 +1,19 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis gave wrong results because it did not
+%% handle the is_tuple/1 guard properly.
+%%---------------------------------------------------------------------
+
+-module(opaque_bug3).
+
+-export([test/1]).
+
+-record(c, {}).
+
+-opaque o() :: 'a' | #c{}.
+
+-spec test(o()) -> 42.
+
+test(#c{} = O) -> t(O).
+
+t(T) when is_tuple(T) -> 42;
+t(a) -> gazonk.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl
new file mode 100644
index 0000000000..a7ddc80fe8
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/opaque/opaque_bug4.erl
@@ -0,0 +1,21 @@
+%%---------------------------------------------------------------------
+%% A test for which the analysis gave wrong results due to erroneous
+%% specialization and incorrect handling of unions.
+%%---------------------------------------------------------------------
+
+-module(opaque_bug4).
+
+-export([ok/0, wrong/0]).
+
+%-spec ok() -> 'ok'.
+ok() ->
+ L = opaque_adt:atom_or_list(42),
+ foo(L).
+
+%-spec wrong() -> 'not_ok'.
+wrong() ->
+ A = opaque_adt:atom_or_list(1),
+ foo(A).
+
+foo(a) -> not_ok;
+foo([_|_]) -> ok.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl
new file mode 100644
index 0000000000..5682f2281e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/queue/queue_use.erl
@@ -0,0 +1,66 @@
+-module(queue_use).
+
+-export([ok1/0, ok2/0]).
+-export([wrong1/0, wrong2/0, wrong3/0, wrong4/0, wrong5/0, wrong6/0, wrong7/0, wrong8/0]).
+
+ok1() ->
+ queue:is_empty(queue:new()).
+
+ok2() ->
+ Q0 = queue:new(),
+ Q1 = queue:in(42, Q0),
+ {{value, 42}, Q2} = queue:out(Q1),
+ queue:is_empty(Q2).
+
+%%--------------------------------------------------
+
+wrong1() ->
+ queue:is_empty({[],[]}).
+
+wrong2() ->
+ Q0 = {[],[]},
+ queue:in(42, Q0).
+
+wrong3() ->
+ Q0 = queue:new(),
+ Q1 = queue:in(42, Q0),
+ {[42],Q2} = Q1,
+ Q2.
+
+wrong4() ->
+ Q0 = queue:new(),
+ Q1 = queue:in(42, Q0),
+ Q1 =:= {[42],[]}.
+
+wrong5() ->
+ {F, _R} = queue:new(),
+ F.
+
+wrong6() ->
+ {{value, 42}, Q2} = queue:out({[42],[]}),
+ Q2.
+
+%%--------------------------------------------------
+
+-record(db, {p, q}).
+
+wrong7() ->
+ add_unique(42, #db{p = [], q = queue:new()}).
+
+add_unique(E, DB) ->
+ case is_in_queue(E, DB) of
+ true -> DB;
+ false -> DB#db{q = queue:in(E, DB#db.q)}
+ end.
+
+is_in_queue(P, #db{q = {L1,L2}}) ->
+ lists:member(P, L1) orelse lists:member(P, L2).
+
+%%--------------------------------------------------
+
+wrong8() ->
+ tuple_queue({42, gazonk}).
+
+tuple_queue({F, Q}) ->
+ queue:in(F, Q).
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl
new file mode 100644
index 0000000000..f01cc5e519
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_adt.erl
@@ -0,0 +1,22 @@
+-module(rec_adt).
+
+-export([new/0, get_a/1, get_b/1, set_a/2, set_b/2]).
+
+-record(rec, {a :: atom(), b = 0 :: integer()}).
+
+-opaque rec() :: #rec{}.
+
+-spec new() -> rec().
+new() -> #rec{a = gazonk, b = 42}.
+
+-spec get_a(rec()) -> atom().
+get_a(#rec{a = A}) -> A.
+
+-spec get_b(rec()) -> integer().
+get_b(#rec{b = B}) -> B.
+
+-spec set_a(rec(), atom()) -> rec().
+set_a(R, A) -> R#rec{a = A}.
+
+-spec set_b(rec(), integer()) -> rec().
+set_b(R, B) -> R#rec{b = B}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl
new file mode 100644
index 0000000000..358e9f918c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/rec/rec_use.erl
@@ -0,0 +1,30 @@
+-module(rec_use).
+
+-export([ok1/0, ok2/0, wrong1/0, wrong2/0, wrong3/0, wrong4/0]).
+
+ok1() ->
+ rec_adt:set_a(rec_adt:new(), foo).
+
+ok2() ->
+ R1 = rec_adt:new(),
+ B1 = rec_adt:get_b(R1),
+ R2 = rec_adt:set_b(R1, 42),
+ B2 = rec_adt:get_b(R2),
+ B1 =:= B2.
+
+wrong1() ->
+ case rec_adt:new() of
+ {rec, _, 42} -> weird1;
+ R when tuple_size(R) =:= 3 -> weird2
+ end.
+
+wrong2() ->
+ R = list_to_tuple([rec, a, 42]),
+ rec_adt:get_a(R).
+
+wrong3() ->
+ R = rec_adt:new(),
+ R =:= {rec, gazonk, 42}.
+
+wrong4() ->
+ tuple_size(rec_adt:new()).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl
new file mode 100644
index 0000000000..9c8ea0af1c
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/timer/timer_use.erl
@@ -0,0 +1,20 @@
+%%---------------------------------------------------------------------------
+%% A test case with:
+%% - a genuine matching error -- 1st branch
+%% - a violation of the opaqueness of timer:tref() -- 2nd branch
+%% - a subtle violation of the opaqueness of timer:tref() -- 3rd branch
+%% The test is supposed to check that these cases are treated properly.
+%%---------------------------------------------------------------------------
+
+-module(timer_use).
+-export([wrong/0]).
+
+-spec wrong() -> error.
+
+wrong() ->
+ case timer:kill_after(42, self()) of
+ gazonk -> weird;
+ {ok, 42} -> weirder;
+ {Tag, gazonk} when Tag =/= error -> weirdest;
+ {error, _} -> error
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl
new file mode 100644
index 0000000000..5ca3202bba
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_adt.erl
@@ -0,0 +1,19 @@
+-module(union_adt).
+-export([new/1, new_a/1, new_rec/1]).
+
+-record(rec, {x = 42 :: integer()}).
+
+-opaque u() :: 'aaa' | 'bbb' | #rec{}.
+
+new(a) -> aaa;
+new(b) -> bbb;
+new(X) when is_integer(X) ->
+ #rec{x = X}.
+
+%% the following two functions (and their uses in union_use.erl) test
+%% that the return type is the opaque one and not just a subtype of it
+
+new_a(a) -> aaa.
+
+new_rec(X) when is_integer(X) ->
+ #rec{x = X}.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl
new file mode 100644
index 0000000000..6a103279cd
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/union/union_use.erl
@@ -0,0 +1,16 @@
+-module(union_use).
+
+-export([test/1, wrong_a/0, wrong_rec/0]).
+
+test(X) ->
+ case union_adt:new(X) of
+ A when is_atom(A) -> atom;
+ T when is_tuple(T) -> tuple
+ end.
+
+wrong_a() ->
+ aaa = union_adt:new_a(a),
+ ok.
+
+wrong_rec() ->
+ is_tuple(union_adt:new_rec(42)).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl
new file mode 100644
index 0000000000..b9339a8eb1
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings.hrl
@@ -0,0 +1,205 @@
+%%
+%% wings.hrl --
+%%
+%% Global record definition and defines.
+%%
+%% Copyright (c) 2001-2005 Bjorn Gustavsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings.hrl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+
+-include("wings_intl.hrl").
+
+-ifdef(NEED_ESDL).
+-include_lib("esdl/include/sdl.hrl").
+-include_lib("esdl/include/sdl_events.hrl").
+-include_lib("esdl/include/sdl_video.hrl").
+-include_lib("esdl/include/sdl_keyboard.hrl").
+-include_lib("esdl/include/sdl_mouse.hrl").
+-include_lib("esdl/src/sdl_util.hrl").
+-define(CTRL_BITS, ?KMOD_CTRL).
+-define(ALT_BITS, ?KMOD_ALT).
+-define(SHIFT_BITS, ?KMOD_SHIFT).
+-define(META_BITS, ?KMOD_META).
+-endif.
+
+-define(WINGS_VERSION, ?wings_version).
+
+-define(CHAR_HEIGHT, wings_text:height()).
+-define(CHAR_WIDTH, wings_text:width()).
+
+-define(LINE_HEIGHT, (?CHAR_HEIGHT+2)).
+-define(GROUND_GRID_SIZE, 1).
+-define(CAMERA_DIST, (8.0*?GROUND_GRID_SIZE)).
+-define(NORMAL_LINEWIDTH, 1.0).
+-define(DEGREE, 176). %Degree character.
+
+-define(HIT_BUF_SIZE, (1024*1024)).
+
+-define(PANE_COLOR, {0.52,0.52,0.52}).
+-define(BEVEL_HIGHLIGHT, {0.9,0.9,0.9}).
+-define(BEVEL_LOWLIGHT, {0.3,0.3,0.3}).
+-define(BEVEL_HIGHLIGHT_MIX, 0.5).
+-define(BEVEL_LOWLIGHT_MIX, 0.5).
+
+-define(SLOW(Cmd), begin wings_io:hourglass(), Cmd end).
+-define(TC(Cmd), wings_util:tc(fun() -> Cmd end, ?MODULE, ?LINE)).
+
+-ifdef(DEBUG).
+-define(ASSERT(E), case E of
+ true -> ok;
+ _ ->
+ erlang:error({assertion_failed,?MODULE,?LINE})
+ end).
+-define(CHECK_ERROR(), wings_gl:check_error(?MODULE, ?LINE)).
+-else.
+-define(ASSERT(E),ok).
+-define(CHECK_ERROR(), ok).
+-endif.
+
+%% Display lists per object.
+%% Important: Plain integers and integers in lists will be assumed to
+%% be display lists. Arbitrary integers must be stored inside a tuple
+%% or record to not be interpreted as a display list.
+-record(dlo,
+ {work=none, %Workmode faces.
+ smooth=none, %Smooth-shaded faces.
+ edges=none, %Edges and wire-frame.
+ vs=none, %Unselected vertices.
+ hard=none, %Hard edges.
+ sel=none, %Selected items.
+ orig_sel=none, %Original selection.
+ normals=none, %Normals.
+ pick=none, %For picking.
+ proxy_faces=none, %Smooth proxy faces.
+ proxy_edges=none, %Smooth proxy edges.
+
+ %% Miscellanous.
+ hilite=none, %Hilite display list.
+ mirror=none, %Virtual mirror data.
+ ns=none, %Normals/positions per face.
+
+ %% Source for display lists.
+ src_we=none, %Source object.
+ src_sel=none, %Source selection.
+ orig_mode=none, %Original selection mode.
+ split=none, %Split data.
+ drag=none, %For dragging.
+ transparent=false, %Object includes transparancy.
+ proxy_data=none, %Data for smooth proxy.
+ open=false, %Open (has hole).
+
+ %% List of display lists known to be needed only based
+ %% on display modes, not whether the lists themselves exist.
+ %% Example: [work,edges]
+ needed=[]
+ }).
+
+%% Main state record containing all objects and other important state.
+-record(st,
+ {shapes, %All visible shapes
+ selmode, %Selection mode:
+ % vertex, edge, face, body
+ sh=false, %Smart highlight active: true|false
+ sel=[], %Current sel: [{Id,GbSet}]
+ ssels=[], %Saved selections:
+ % [{Name,Mode,GbSet}]
+ temp_sel=none, %Selection only temporary?
+
+ mat, %Defined materials (GbTree).
+ pal=[], %Palette
+ file, %Current filename.
+ saved, %True if model has been saved.
+ onext, %Next object id to use.
+ bb=none, %Saved bounding box.
+ edge_loop=none, %Previous edge loop.
+ views={0,{}}, %{Current,TupleOfViews}
+ pst=gb_trees:empty(), %Plugin State Info
+ % gb_tree where key is plugin module
+
+ %% Previous commands.
+ repeatable, %Last repeatable command.
+ ask_args, %Ask arguments.
+ drag_args, %Drag arguments for command.
+ def, %Default operations.
+
+ %% Undo information.
+ top, %Top of stack.
+ bottom, %Bottom of stack.
+ next_is_undo, %State of undo/redo toggle.
+ undone %States that were undone.
+ }).
+
+%% The Winged-Edge data structure.
+%% See http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/model/winged-e.html
+-record(we,
+ {id, %Shape id.
+ perm=0, %Permissions:
+ % 0 - Everything allowed.
+ % 1 - Visible, can't select.
+ % [] or {Mode,GbSet} -
+ % Invisible, can't select.
+ % The GbSet contains the
+ % object's selection.
+ name, %Name.
+ es, %gb_tree containing edges
+ fs, %gb_tree containing faces
+ he, %gb_sets containing hard edges
+ vc, %Connection info (=incident edge)
+ % for vertices.
+ vp, %Vertex positions.
+ pst=gb_trees:empty(), %Plugin State Info,
+ % gb_tree where key is plugin module
+ mat=default, %Materials.
+ next_id, %Next free ID for vertices,
+ % edges, and faces.
+ % (Needed because we never re-use
+ % IDs.)
+ mode, %'vertex'/'material'/'uv'
+ mirror=none, %Mirror: none|Face
+ light=none, %Light data: none|Light
+ has_shape=true %true|false
+ }).
+
+-define(IS_VISIBLE(Perm), (Perm =< 1)).
+-define(IS_NOT_VISIBLE(Perm), (Perm > 1)).
+-define(IS_SELECTABLE(Perm), (Perm == 0)).
+-define(IS_NOT_SELECTABLE(Perm), (Perm =/= 0)).
+
+-define(IS_LIGHT(We), ((We#we.light =/= none) and (not We#we.has_shape))).
+-define(IS_ANY_LIGHT(We), (We#we.light =/= none)).
+-define(HAS_SHAPE(We), (We#we.has_shape)).
+%-define(IS_LIGHT(We), (We#we.light =/= none)).
+%-define(IS_NOT_LIGHT(We), (We#we.light =:= none)).
+
+%% Edge in a winged-edge shape.
+-record(edge,
+ {vs, %Start vertex for edge
+ ve, %End vertex for edge
+ a=none, %Color or UV coordinate.
+ b=none, %Color or UV coordinate.
+ lf, %Left face
+ rf, %Right face
+ ltpr, %Left traversal predecessor
+ ltsu, %Left traversal successor
+ rtpr, %Right traversal predecessor
+ rtsu %Right traversal successor
+ }).
+
+%% The current view/camera.
+-record(view,
+ {origin,
+ distance, % From origo.
+ azimuth,
+ elevation,
+ pan_x, %Panning in X direction.
+ pan_y, %Panning in Y direction.
+ along_axis=none, %Which axis viewed along.
+ fov, %Field of view.
+ hither, %Near clipping plane.
+ yon %Far clipping plane.
+ }).
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl
new file mode 100644
index 0000000000..d7af9bb1d3
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_dissolve.erl
@@ -0,0 +1,375 @@
+%%
+%% wings_dissolve.erl --
+%%
+%% This module implements dissolve of faces.
+%%
+
+-module(wings_dissolve).
+
+-export([faces/2, complement/2]).
+
+-include("wings.hrl").
+
+%% faces([Face], We) -> We'
+%% Dissolve the given faces.
+faces([], We) -> We;
+faces(Faces, #we{fs=Ftab0}=We) ->
+ case gb_sets:is_empty(Faces) of
+ true -> We;
+ false when is_list(Faces) ->
+ Complement = ordsets:subtract(gb_trees:keys(Ftab0),
+ ordsets:from_list(Faces)),
+ dissolve_1(Faces, Complement, We);
+ false ->
+ Complement = ordsets:subtract(gb_trees:keys(Ftab0),
+ gb_sets:to_list(Faces)),
+ dissolve_1(Faces, Complement, We)
+ end.
+
+faces([], _, We) -> We;
+faces(Faces,Complement,We) ->
+ case gb_sets:is_empty(Faces) of
+ true -> We;
+ false -> dissolve_1(Faces, Complement,We)
+ end.
+
+dissolve_1(Faces, Complement, We0) ->
+ We1 = optimistic_dissolve(Faces,Complement,We0#we{vc=undefined}),
+ NewFaces = wings_we:new_items_as_ordset(face, We0, We1),
+ We2 = wings_face:delete_bad_faces(NewFaces, We1),
+ We = wings_we:rebuild(We2),
+ case wings_we:is_consistent(We) of
+ true ->
+ We;
+ false ->
+ io:format("Dissolving would cause an inconsistent object structure.")
+ end.
+
+%% complement([Face], We) -> We'
+%% Dissolve all faces BUT the given faces. Also invalidate the
+%% mirror face if it existed and was dissolved.
+complement(Fs0, #we{fs=Ftab0}=We0) when is_list(Fs0) ->
+ Fs = ordsets:subtract(gb_trees:keys(Ftab0), ordsets:from_list(Fs0)),
+ case faces(Fs, Fs0, We0) of
+ #we{mirror=none}=We -> We;
+ #we{mirror=Face,fs=Ftab}=We ->
+ case gb_trees:is_defined(Face, Ftab) of
+ false -> We;
+ true -> We#we{mirror=none}
+ end
+ end;
+complement(Fs, We) -> complement(gb_sets:to_list(Fs), We).
+
+optimistic_dissolve(Faces0, Compl, We0) ->
+ %% Optimistically assume that we have a simple region without
+ %% any holes.
+ case outer_edge_loop(Faces0, We0) of
+ error ->
+ %% Assumption was wrong. We need to partition the selection
+ %% and dissolve each partition in turn.
+ Parts = wings_sel:face_regions(Faces0, We0),
+ complex_dissolve(Parts, We0);
+ [_|_]=Loop ->
+ %% Assumption was correct.
+ simple_dissolve(Faces0, Compl, Loop, We0)
+ end.
+
+%% simple_dissolve(Faces, Loop, We0) -> We
+%% Dissolve a region of faces with no holes and no
+%% repeated vertices in the outer edge loop.
+
+simple_dissolve(Faces0, Compl, Loop, We0) ->
+ Faces = to_gb_set(Faces0),
+ OldFace = gb_sets:smallest(Faces),
+ Mat = wings_facemat:face(OldFace, We0),
+ We1 = fix_materials(Faces, Compl, We0),
+ #we{es=Etab0,fs=Ftab0,he=Htab0} = We1,
+ {Ftab1,Etab1,Htab} = simple_del(Faces, Ftab0, Etab0, Htab0, We1),
+ {NewFace,We2} = wings_we:new_id(We1),
+ Ftab = gb_trees:insert(NewFace, hd(Loop), Ftab1),
+ Last = lists:last(Loop),
+ Etab = update_outer([Last|Loop], Loop, NewFace, Ftab, Etab1),
+ We = We2#we{es=Etab,fs=Ftab,he=Htab},
+ wings_facemat:assign(Mat, [NewFace], We).
+
+fix_materials(Del,Keep,We) ->
+ case gb_sets:size(Del) < length(Keep) of
+ true ->
+ wings_facemat:delete_faces(Del,We);
+ false ->
+ wings_facemat:keep_faces(Keep,We)
+ end.
+
+to_gb_set(List) when is_list(List) ->
+ gb_sets:from_list(List);
+to_gb_set(S) -> S.
+
+%% Delete faces and inner edges for a simple region.
+simple_del(Faces, Ftab0, Etab0, Htab0, We) ->
+ case {gb_trees:size(Ftab0),gb_sets:size(Faces)} of
+ {AllSz,FaceSz} when AllSz < 2*FaceSz ->
+ %% At least half of the faces are selected.
+ %% It is faster to find the edges for the
+ %% unselected faces.
+ UnselFaces = ordsets:subtract(gb_trees:keys(Ftab0),
+ gb_sets:to_list(Faces)),
+
+ UnselSet = sofs:from_external(UnselFaces, [face]),
+ Ftab1 = sofs:from_external(gb_trees:to_list(Ftab0),
+ [{face,edge}]),
+ Ftab2 = sofs:restriction(Ftab1, UnselSet),
+ Ftab = gb_trees:from_orddict(sofs:to_external(Ftab2)),
+
+ Keep0 = wings_face:to_edges(UnselFaces, We),
+ Keep = sofs:set(Keep0, [edge]),
+ Etab1 = sofs:from_external(gb_trees:to_list(Etab0),
+ [{edge,info}]),
+ Etab2 = sofs:restriction(Etab1, Keep),
+ Etab = gb_trees:from_orddict(sofs:to_external(Etab2)),
+
+ Htab = simple_del_hard(Htab0, sofs:to_external(Keep), undefined),
+ {Ftab,Etab,Htab};
+ {_,_} ->
+ Ftab = lists:foldl(fun(Face, Ft) ->
+ gb_trees:delete(Face, Ft)
+ end, Ftab0, gb_sets:to_list(Faces)),
+ Inner = wings_face:inner_edges(Faces, We),
+ Etab = lists:foldl(fun(Edge, Et) ->
+ gb_trees:delete(Edge, Et)
+ end, Etab0, Inner),
+ Htab = simple_del_hard(Htab0, undefined, Inner),
+ {Ftab,Etab,Htab}
+ end.
+
+simple_del_hard(Htab, Keep, Remove) ->
+ case gb_sets:is_empty(Htab) of
+ true -> Htab;
+ false -> simple_del_hard_1(Htab, Keep, Remove)
+ end.
+
+simple_del_hard_1(Htab, Keep, undefined) ->
+ gb_sets:intersection(Htab, gb_sets:from_ordset(Keep));
+simple_del_hard_1(Htab, undefined, Remove) ->
+ gb_sets:difference(Htab, gb_sets:from_ordset(Remove)).
+
+%% complex([Partition], We0) -> We0
+%% The general dissolve.
+
+complex_dissolve([Faces|T], We0) ->
+ Face = gb_sets:smallest(Faces),
+ Mat = wings_facemat:face(Face, We0),
+ We1 = wings_facemat:delete_faces(Faces, We0),
+ Parts = outer_edge_partition(Faces, We1),
+ We = do_dissolve(Faces, Parts, Mat, We0, We1),
+ complex_dissolve(T, We);
+complex_dissolve([], We) -> We.
+
+do_dissolve(Faces, Ess, Mat, WeOrig, We0) ->
+ We1 = do_dissolve_faces(Faces, We0),
+ Inner = wings_face:inner_edges(Faces, WeOrig),
+ We2 = delete_inner(Inner, We1),
+ #we{he=Htab0} = We = do_dissolve_1(Ess, Mat, We2),
+ Htab = gb_sets:difference(Htab0, gb_sets:from_list(Inner)),
+ We#we{he=Htab}.
+
+do_dissolve_1([EdgeList|Ess], Mat, #we{es=Etab0,fs=Ftab0}=We0) ->
+ {Face,We1} = wings_we:new_id(We0),
+ Ftab = gb_trees:insert(Face, hd(EdgeList), Ftab0),
+ Last = lists:last(EdgeList),
+ Etab = update_outer([Last|EdgeList], EdgeList, Face, Ftab, Etab0),
+ We2 = We1#we{es=Etab,fs=Ftab},
+ We = wings_facemat:assign(Mat, [Face], We2),
+ do_dissolve_1(Ess, Mat, We);
+do_dissolve_1([], _Mat, We) -> We.
+
+do_dissolve_faces(Faces, #we{fs=Ftab0}=We) ->
+ Ftab = lists:foldl(fun(Face, Ft) ->
+ gb_trees:delete(Face, Ft)
+ end, Ftab0, gb_sets:to_list(Faces)),
+ We#we{fs=Ftab}.
+
+delete_inner(Inner, #we{es=Etab0}=We) ->
+ Etab = lists:foldl(fun(Edge, Et) ->
+ gb_trees:delete(Edge, Et)
+ end, Etab0, Inner),
+ We#we{es=Etab}.
+
+update_outer([Pred|[Edge|Succ]=T], More, Face, Ftab, Etab0) ->
+ #edge{rf=Rf} = R0 = gb_trees:get(Edge, Etab0),
+ Rec = case gb_trees:is_defined(Rf, Ftab) of
+ true ->
+ ?ASSERT(false == gb_trees:is_defined(R0#edge.lf, Ftab)),
+ LS = succ(Succ, More),
+ R0#edge{lf=Face,ltpr=Pred,ltsu=LS};
+ false ->
+ ?ASSERT(true == gb_trees:is_defined(R0#edge.lf, Ftab)),
+ RS = succ(Succ, More),
+ R0#edge{rf=Face,rtpr=Pred,rtsu=RS}
+ end,
+ Etab = gb_trees:update(Edge, Rec, Etab0),
+ update_outer(T, More, Face, Ftab, Etab);
+update_outer([_], _More, _Face, _Ftab, Etab) -> Etab.
+
+succ([Succ|_], _More) -> Succ;
+succ([], [Succ|_]) -> Succ.
+
+%% outer_edge_loop(FaceSet,WingedEdge) -> [Edge] | error.
+%% Partition the outer edges of the FaceSet into a single closed loop.
+%% Return 'error' if the faces in FaceSet does not form a
+%% simple region without holes.
+%%
+%% Equvivalent to
+%% case outer_edge_partition(FaceSet,WingedEdge) of
+%% [Loop] -> Loop;
+%% [_|_] -> error
+%% end.
+%% but faster.
+
+outer_edge_loop(Faces, We) ->
+ case lists:sort(collect_outer_edges(Faces, We)) of
+ [] -> error;
+ [{Key,Val}|Es0] ->
+ case any_duplicates(Es0, Key) of
+ false ->
+ Es = gb_trees:from_orddict(Es0),
+ N = gb_trees:size(Es),
+ outer_edge_loop_1(Val, Es, Key, N, []);
+ true -> error
+ end
+ end.
+
+outer_edge_loop_1({Edge,V}, _, V, 0, Acc) ->
+ %% This edge completes the loop, and we have used all possible edges.
+ [Edge|Acc];
+outer_edge_loop_1({_,V}, _, V, _N, _) ->
+ %% Loop is complete, but we haven't used all edges.
+ error;
+outer_edge_loop_1({_,_}, _, _, 0, _) ->
+ %% We have used all possible edges, but somehow the loop
+ %% is not complete. I can't see how this is possible.
+ erlang:error(internal_error);
+outer_edge_loop_1({Edge,Vb}, Es, EndV, N, Acc0) ->
+ Acc = [Edge|Acc0],
+ outer_edge_loop_1(gb_trees:get(Vb, Es), Es, EndV, N-1, Acc).
+
+any_duplicates([{V,_}|_], V) -> true;
+any_duplicates([_], _) -> false;
+any_duplicates([{V,_}|Es], _) -> any_duplicates(Es, V).
+
+%% outer_edge_partition(FaceSet, WingedEdge) -> [[Edge]].
+%% Partition the outer edges of the FaceSet. Each partion
+%% of edges form a closed loop with no repeated vertices.
+%% Outer edges are edges that have one face in FaceSet
+%% and one outside.
+%% It is assumed that FaceSet consists of one region returned by
+%% wings_sel:face_regions/2.
+
+outer_edge_partition(Faces, We) ->
+ F0 = collect_outer_edges(Faces, We),
+ F = gb_trees:from_orddict(wings_util:rel2fam(F0)),
+ partition_edges(F, []).
+
+collect_outer_edges(Faces, We) when is_list(Faces) ->
+ collect_outer_edges_1(Faces, gb_sets:from_list(Faces), We);
+collect_outer_edges(Faces, We) ->
+ collect_outer_edges_1(gb_sets:to_list(Faces), Faces, We).
+
+collect_outer_edges_1(Fs0, Faces0, #we{fs=Ftab}=We) ->
+ case {gb_trees:size(Ftab),gb_sets:size(Faces0)} of
+ {AllSz,FaceSz} when AllSz < 2*FaceSz ->
+ Fs = ordsets:subtract(gb_trees:keys(Ftab), Fs0),
+ Faces = gb_sets:from_ordset(Fs),
+ Coll = collect_outer_edges_a(Faces),
+ wings_face:fold_faces(Coll, [], Fs, We);
+ {_,_} ->
+ Coll = collect_outer_edges_b(Faces0),
+ wings_face:fold_faces(Coll, [], Fs0, We)
+ end.
+
+collect_outer_edges_a(Faces) ->
+ fun(Face, _, Edge, #edge{ve=V,vs=OtherV,lf=Face,rf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end;
+ (Face, _, Edge, #edge{ve=OtherV,vs=V,rf=Face,lf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end
+ end.
+
+collect_outer_edges_b(Faces) ->
+ fun(Face, _, Edge, #edge{vs=V,ve=OtherV,lf=Face,rf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end;
+ (Face, _, Edge, #edge{vs=OtherV,ve=V,rf=Face,lf=Other}, Acc) ->
+ case gb_sets:is_member(Other, Faces) of
+ false -> [{V,{Edge,OtherV}}|Acc];
+ true -> Acc
+ end
+ end.
+
+partition_edges(Es0, Acc) ->
+ case gb_trees:is_empty(Es0) of
+ true -> Acc;
+ false ->
+ {Key,Val,Es1} = gb_trees:take_smallest(Es0),
+ {Cycle,Es} = part_collect_cycle(Key, Val, Es1, []),
+ partition_edges(Es, [Cycle|Acc])
+ end.
+
+%% part_collect_cycle(Vertex, VertexInfo, EdgeInfo, Acc0) ->
+%% none | {[Edge],EdgeInfo}
+%% Collect the cycle starting with Vertex.
+%%
+%% Note: This function can only return 'none' when called
+%% recursively.
+
+part_collect_cycle(_, repeated, _, _) ->
+ %% Repeated vertex - we are not allowed to go this way.
+ %% Can only happen if we were called recursively because
+ %% a fork was encountered.
+ none;
+part_collect_cycle(_Va, [{Edge,Vb}], Es0, Acc0) ->
+ %% Basic case. Only one way to go.
+ Acc = [Edge|Acc0],
+ case gb_trees:lookup(Vb, Es0) of
+ none ->
+ {Acc,Es0};
+ {value,Val} ->
+ Es = gb_trees:delete(Vb, Es0),
+ part_collect_cycle(Vb, Val, Es, Acc)
+ end;
+part_collect_cycle(Va, [Val|More], Es0, []) ->
+ %% No cycle started yet and we have multiple choice of
+ %% edges out from this vertex. It doesn't matter which
+ %% edge we follow, so we'll follow the first one.
+ {Cycle,Es} = part_collect_cycle(Va, [Val], Es0, []),
+ {Cycle,gb_trees:insert(Va, More, Es)};
+part_collect_cycle(Va, Edges, Es0, Acc) ->
+ %% We have a partially collected cycle and we have a
+ %% fork (multiple choice of edges). Here we must choose
+ %% an edge that closes the cycle without passing Va
+ %% again (because repeated vertices are not allowed).
+ Es = gb_trees:insert(Va, repeated, Es0),
+ part_fork(Va, Edges, Es, Acc, []).
+
+part_fork(Va, [Val|More], Es0, Acc, Tried) ->
+ %% Try to complete the cycle by following this edge.
+ case part_collect_cycle(Va, [Val], Es0, Acc) of
+ none ->
+ %% Failure - try the next edge.
+ part_fork(Va, More, Es0, Acc, [Val|Tried]);
+ {Cycle,Es} ->
+ %% Found a cycle. Update the vertex information
+ %% with all edges remaining.
+ {Cycle,gb_trees:update(Va, lists:reverse(Tried, More), Es)}
+ end;
+part_fork(_, [], _, _, _) ->
+ %% None of edges were possible. Can only happen if this function
+ %% was called recursively (i.e. if we hit another fork while
+ %% processing a fork).
+ none.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl
new file mode 100644
index 0000000000..3483acb711
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge.erl
@@ -0,0 +1,243 @@
+%%
+%% wings_edge.erl --
+%%
+%% This module contains most edge command and edge utility functions.
+%%
+%% Copyright (c) 2001-2008 Bjorn Gustavsson.
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings_edge.erl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+
+-module(wings_edge).
+
+-export([dissolve_edges/2]).
+
+-include("wings.hrl").
+
+%%%
+%%% Dissolve.
+%%%
+
+dissolve_edges(Edges0, We0) when is_list(Edges0) ->
+ #we{es=Etab} = We1 = lists:foldl(fun internal_dissolve_edge/2, We0, Edges0),
+ case [E || E <- Edges0, gb_trees:is_defined(E, Etab)] of
+ Edges0 ->
+ %% No edge was deleted in the last pass. We are done.
+ We = wings_we:rebuild(We0#we{vc=undefined}),
+ wings_we:validate_mirror(We);
+ Edges ->
+ dissolve_edges(Edges, We1)
+ end;
+dissolve_edges(Edges, We) ->
+ dissolve_edges(gb_sets:to_list(Edges), We).
+
+internal_dissolve_edge(Edge, #we{es=Etab}=We0) ->
+ case gb_trees:lookup(Edge, Etab) of
+ none -> We0;
+ {value,#edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same}} ->
+ Empty = gb_trees:empty(),
+ We0#we{vc=Empty,vp=Empty,es=Empty,fs=Empty,he=gb_sets:empty()};
+ {value,#edge{rtpr=Back,ltsu=Back}=Rec} ->
+ merge_edges(backward, Edge, Rec, We0);
+ {value,#edge{rtsu=Forward,ltpr=Forward}=Rec} ->
+ merge_edges(forward, Edge, Rec, We0);
+ {value,Rec} ->
+ try dissolve_edge_1(Edge, Rec, We0) of
+ We -> We
+ catch
+ throw:hole -> We0
+ end
+ end.
+
+%% dissolve_edge_1(Edge, EdgeRecord, We) -> We
+%% Remove an edge and a face. If one of the faces is degenerated
+%% (only consists of two edges), remove that one. Otherwise, it
+%% doesn't matter which face we remove.
+dissolve_edge_1(Edge, #edge{lf=Remove,rf=Keep,ltpr=Same,ltsu=Same}=Rec, We) ->
+ dissolve_edge_2(Edge, Remove, Keep, Rec, We);
+dissolve_edge_1(Edge, #edge{lf=Keep,rf=Remove}=Rec, We) ->
+ dissolve_edge_2(Edge, Remove, Keep, Rec, We).
+
+dissolve_edge_2(Edge, FaceRemove, FaceKeep,
+ #edge{ltpr=LP,ltsu=LS,rtpr=RP,rtsu=RS},
+ #we{fs=Ftab0,es=Etab0,he=Htab0}=We0) ->
+ %% First change face for all edges surrounding the face we will remove.
+ Etab1 = wings_face:fold(
+ fun (_, E, _, IntEtab) when E =:= Edge -> IntEtab;
+ (_, E, R, IntEtab) ->
+ case R of
+ #edge{lf=FaceRemove,rf=FaceKeep} ->
+ throw(hole);
+ #edge{rf=FaceRemove,lf=FaceKeep} ->
+ throw(hole);
+ #edge{lf=FaceRemove} ->
+ gb_trees:update(E, R#edge{lf=FaceKeep}, IntEtab);
+ #edge{rf=FaceRemove} ->
+ gb_trees:update(E, R#edge{rf=FaceKeep}, IntEtab)
+ end
+ end, Etab0, FaceRemove, We0),
+
+ %% Patch all predecessors and successor of the edge we will remove.
+ Etab2 = patch_edge(LP, RS, Edge, Etab1),
+ Etab3 = patch_edge(LS, RP, Edge, Etab2),
+ Etab4 = patch_edge(RP, LS, Edge, Etab3),
+ Etab5 = patch_edge(RS, LP, Edge, Etab4),
+
+ %% Remove the edge.
+ Etab = gb_trees:delete(Edge, Etab5),
+ Htab = hardness(Edge, soft, Htab0),
+
+ %% Remove the face. Patch the face entry for the remaining face.
+ Ftab1 = gb_trees:delete(FaceRemove, Ftab0),
+ We1 = wings_facemat:delete_face(FaceRemove, We0),
+ Ftab = gb_trees:update(FaceKeep, LP, Ftab1),
+
+ %% Return result.
+ We = We1#we{es=Etab,fs=Ftab,vc=undefined,he=Htab},
+ AnEdge = gb_trees:get(FaceKeep, Ftab),
+ case gb_trees:get(AnEdge, Etab) of
+ #edge{lf=FaceKeep,ltpr=Same,ltsu=Same} ->
+ internal_dissolve_edge(AnEdge, We);
+ #edge{rf=FaceKeep,rtpr=Same,rtsu=Same} ->
+ internal_dissolve_edge(AnEdge, We);
+ _Other ->
+ case wings_we:is_face_consistent(FaceKeep, We) of
+ true ->
+ We;
+ false ->
+ io:format("Dissolving would cause a badly formed face.")
+ end
+ end.
+
+%%
+%% We like winged edges, but not winged vertices (a vertex with
+%% only two edges connected to it). We will remove the winged vertex
+%% by joining the two edges connected to it.
+%%
+
+merge_edges(Dir, Edge, Rec, #we{es=Etab}=We) ->
+ {Va,Vb,_,_,_,_,To,To} = half_edge(Dir, Rec),
+ case gb_trees:get(To, Etab) of
+ #edge{vs=Va,ve=Vb} ->
+ del_2edge_face(Dir, Edge, Rec, To, We);
+ #edge{vs=Vb,ve=Va} ->
+ del_2edge_face(Dir, Edge, Rec, To, We);
+ _Other ->
+ merge_1(Dir, Edge, Rec, To, We)
+ end.
+
+merge_1(Dir, Edge, Rec, To, #we{es=Etab0,fs=Ftab0,he=Htab0}=We) ->
+ OtherDir = reverse_dir(Dir),
+ {Vkeep,Vdelete,Lf,Rf,A,B,L,R} = half_edge(OtherDir, Rec),
+ Etab1 = patch_edge(L, To, Edge, Etab0),
+ Etab2 = patch_edge(R, To, Edge, Etab1),
+ Etab3 = patch_half_edge(To, Vkeep, Lf, A, L, Rf, B, R, Vdelete, Etab2),
+ Htab = hardness(Edge, soft, Htab0),
+ Etab = gb_trees:delete(Edge, Etab3),
+ #edge{lf=Lf,rf=Rf} = Rec,
+ Ftab1 = update_face(Lf, To, Edge, Ftab0),
+ Ftab = update_face(Rf, To, Edge, Ftab1),
+ merge_2(To, We#we{es=Etab,fs=Ftab,he=Htab,vc=undefined}).
+
+merge_2(Edge, #we{es=Etab}=We) ->
+ %% If the merged edge is part of a two-edge face, we must
+ %% remove that edge too.
+ case gb_trees:get(Edge, Etab) of
+ #edge{ltpr=Same,ltsu=Same} ->
+ internal_dissolve_edge(Edge, We);
+ #edge{rtpr=Same,rtsu=Same} ->
+ internal_dissolve_edge(Edge, We);
+ _Other -> We
+ end.
+
+update_face(Face, Edge, OldEdge, Ftab) ->
+ case gb_trees:get(Face, Ftab) of
+ OldEdge -> gb_trees:update(Face, Edge, Ftab);
+ _Other -> Ftab
+ end.
+
+del_2edge_face(Dir, EdgeA, RecA, EdgeB,
+ #we{es=Etab0,fs=Ftab0,he=Htab0}=We) ->
+ {_,_,Lf,Rf,_,_,_,_} = half_edge(reverse_dir(Dir), RecA),
+ RecB = gb_trees:get(EdgeB, Etab0),
+ Del = gb_sets:from_list([EdgeA,EdgeB]),
+ EdgeANear = stabile_neighbor(RecA, Del),
+ EdgeBNear = stabile_neighbor(RecB, Del),
+ Etab1 = patch_edge(EdgeANear, EdgeBNear, EdgeA, Etab0),
+ Etab2 = patch_edge(EdgeBNear, EdgeANear, EdgeB, Etab1),
+ Etab3 = gb_trees:delete(EdgeA, Etab2),
+ Etab = gb_trees:delete(EdgeB, Etab3),
+
+ %% Patch hardness table.
+ Htab1 = hardness(EdgeA, soft, Htab0),
+ Htab = hardness(EdgeB, soft, Htab1),
+
+ %% Patch the face table.
+ #edge{lf=Klf,rf=Krf} = gb_trees:get(EdgeANear, Etab),
+ KeepFaces = ordsets:from_list([Klf,Krf]),
+ EdgeAFaces = ordsets:from_list([Lf,Rf]),
+ [DelFace] = ordsets:subtract(EdgeAFaces, KeepFaces),
+ Ftab1 = gb_trees:delete(DelFace, Ftab0),
+ [KeepFace] = ordsets:intersection(KeepFaces, EdgeAFaces),
+ Ftab2 = update_face(KeepFace, EdgeANear, EdgeA, Ftab1),
+ Ftab = update_face(KeepFace, EdgeBNear, EdgeB, Ftab2),
+
+ %% Return result.
+ We#we{vc=undefined,es=Etab,fs=Ftab,he=Htab}.
+
+stabile_neighbor(#edge{ltpr=Ea,ltsu=Eb,rtpr=Ec,rtsu=Ed}, Del) ->
+ [Edge] = lists:foldl(fun(E, A) ->
+ case gb_sets:is_member(E, Del) of
+ true -> A;
+ false -> [E|A]
+ end
+ end, [], [Ea,Eb,Ec,Ed]),
+ Edge.
+
+%%%
+%%% Setting hard/soft edges.
+%%%
+
+hardness(Edge, soft, Htab) -> gb_sets:delete_any(Edge, Htab);
+hardness(Edge, hard, Htab) -> gb_sets:add(Edge, Htab).
+
+%%%
+%%% Utilities.
+%%%
+
+reverse_dir(forward) -> backward;
+reverse_dir(backward) -> forward.
+
+half_edge(backward, #edge{vs=Va,ve=Vb,lf=Lf,rf=Rf,a=A,b=B,ltsu=L,rtpr=R}) ->
+ {Va,Vb,Lf,Rf,A,B,L,R};
+half_edge(forward, #edge{ve=Va,vs=Vb,lf=Lf,rf=Rf,a=A,b=B,ltpr=L,rtsu=R}) ->
+ {Va,Vb,Lf,Rf,A,B,L,R}.
+
+patch_half_edge(Edge, V, FaceA, A, Ea, FaceB, B, Eb, OrigV, Etab) ->
+ New = case gb_trees:get(Edge, Etab) of
+ #edge{vs=OrigV,lf=FaceA,rf=FaceB}=Rec ->
+ Rec#edge{a=A,vs=V,ltsu=Ea,rtpr=Eb};
+ #edge{vs=OrigV,lf=FaceB,rf=FaceA}=Rec ->
+ Rec#edge{a=B,vs=V,ltsu=Eb,rtpr=Ea};
+ #edge{ve=OrigV,lf=FaceA,rf=FaceB}=Rec ->
+ Rec#edge{b=B,ve=V,ltpr=Ea,rtsu=Eb};
+ #edge{ve=OrigV,lf=FaceB,rf=FaceA}=Rec ->
+ Rec#edge{b=A,ve=V,ltpr=Eb,rtsu=Ea}
+ end,
+ gb_trees:update(Edge, New, Etab).
+
+patch_edge(Edge, ToEdge, OrigEdge, Etab) ->
+ New = case gb_trees:get(Edge, Etab) of
+ #edge{ltsu=OrigEdge}=R ->
+ R#edge{ltsu=ToEdge};
+ #edge{ltpr=OrigEdge}=R ->
+ R#edge{ltpr=ToEdge};
+ #edge{rtsu=OrigEdge}=R ->
+ R#edge{rtsu=ToEdge};
+ #edge{rtpr=OrigEdge}=R ->
+ R#edge{rtpr=ToEdge}
+ end,
+ gb_trees:update(Edge, New, Etab).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl
new file mode 100644
index 0000000000..e478ec245b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_edge_cmd.erl
@@ -0,0 +1,91 @@
+%%
+%% wings_edge.erl --
+%%
+%% This module contains most edge command and edge utility functions.
+%%
+
+-module(wings_edge_cmd).
+
+-export([loop_cut/1]).
+
+-include("wings.hrl").
+
+%%%
+%%% The Loop Cut command.
+%%%
+
+loop_cut(St0) ->
+ {Sel,St} = wings_sel:fold(fun loop_cut/3, {[],St0}, St0),
+ wings_sel:set(body, Sel, St).
+
+loop_cut(Edges, #we{name=Name,id=Id,fs=Ftab}=We0, {Sel,St0}) ->
+ AdjFaces = wings_face:from_edges(Edges, We0),
+ case loop_cut_partition(AdjFaces, Edges, We0, []) of
+ [_] ->
+ io:format("Edge loop doesn't divide ~p into two parts.", [Name]);
+ Parts0 ->
+ %% We arbitrarily decide that the largest part of the object
+ %% will be left unselected and will keep the name of the object.
+
+ Parts1 = [{gb_trees:size(P),P} || P <- Parts0],
+ Parts2 = lists:reverse(lists:sort(Parts1)),
+ [_|Parts] = [gb_sets:to_list(P) || {_,P} <- Parts2],
+
+ %% Also, this first part will also contain any sub-object
+ %% that was not reachable from any of the edges. Therefore,
+ %% we calculate the first part as the complement of the union
+ %% of all other parts.
+
+ FirstComplement = ordsets:union(Parts),
+ First = ordsets:subtract(gb_trees:keys(Ftab), FirstComplement),
+
+ We = wings_dissolve:complement(First, We0),
+ Shs = St0#st.shapes,
+ St = St0#st{shapes=gb_trees:update(Id, We, Shs)},
+ loop_cut_make_copies(Parts, We0, Sel, St)
+ end.
+
+loop_cut_make_copies([P|Parts], We0, Sel0, #st{onext=Id}=St0) ->
+ Sel = [{Id,gb_sets:singleton(0)}|Sel0],
+ We = wings_dissolve:complement(P, We0),
+ St = wings_shape:insert(We, cut, St0),
+ loop_cut_make_copies(Parts, We0, Sel, St);
+loop_cut_make_copies([], _, Sel, St) -> {Sel,St}.
+
+loop_cut_partition(Faces0, Edges, We, Acc) ->
+ case gb_sets:is_empty(Faces0) of
+ true -> Acc;
+ false ->
+ {AFace,Faces1} = gb_sets:take_smallest(Faces0),
+ Reachable = collect_faces(AFace, Edges, We),
+ Faces = gb_sets:difference(Faces1, Reachable),
+ loop_cut_partition(Faces, Edges, We, [Reachable|Acc])
+ end.
+
+collect_faces(Face, Edges, We) ->
+ collect_faces(gb_sets:singleton(Face), We, Edges, gb_sets:empty()).
+
+collect_faces(Work0, We, Edges, Acc0) ->
+ case gb_sets:is_empty(Work0) of
+ true -> Acc0;
+ false ->
+ {Face,Work1} = gb_sets:take_smallest(Work0),
+ Acc = gb_sets:insert(Face, Acc0),
+ Work = collect_maybe_add(Work1, Face, Edges, We, Acc),
+ collect_faces(Work, We, Edges, Acc)
+ end.
+
+collect_maybe_add(Work, Face, Edges, We, Res) ->
+ wings_face:fold(
+ fun(_, Edge, Rec, A) ->
+ case gb_sets:is_member(Edge, Edges) of
+ true -> A;
+ false ->
+ Of = wings_face:other(Face, Rec),
+ case gb_sets:is_member(Of, Res) of
+ true -> A;
+ false -> gb_sets:add(Of, A)
+ end
+ end
+ end, Work, Face, We).
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl
new file mode 100644
index 0000000000..487c05aa58
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_face.erl
@@ -0,0 +1,127 @@
+%%
+%% wings_face.erl --
+%%
+%% This module contains help routines for faces, such as fold functions
+%% face iterators.
+%%
+
+-module(wings_face).
+
+-export([delete_bad_faces/2, fold/4, fold_faces/4, from_edges/2,
+ inner_edges/2, to_edges/2, other/2]).
+
+-include("wings.hrl").
+
+from_edges(Es, #we{es=Etab}) when is_list(Es) ->
+ from_edges_1(Es, Etab, []);
+from_edges(Es, We) ->
+ from_edges(gb_sets:to_list(Es), We).
+
+from_edges_1([E|Es], Etab, Acc) ->
+ #edge{lf=Lf,rf=Rf} = gb_trees:get(E, Etab),
+ from_edges_1(Es, Etab, [Lf,Rf|Acc]);
+from_edges_1([], _, Acc) -> gb_sets:from_list(Acc).
+
+%% other(Face, EdgeRecord) -> OtherFace
+%% Pick up the "other face" from an edge record.
+other(Face, #edge{lf=Face,rf=Other}) -> Other;
+other(Face, #edge{rf=Face,lf=Other}) -> Other.
+
+%% to_edges(Faces, We) -> [Edge]
+%% Convert a set or list of faces to a list of edges.
+to_edges(Fs, We) ->
+ ordsets:from_list(to_edges_raw(Fs, We)).
+
+%% inner_edges(Faces, We) -> [Edge]
+%% Given a set of faces, return all inner edges.
+inner_edges(Faces, We) ->
+ S = to_edges_raw(Faces, We),
+ inner_edges_1(lists:sort(S), []).
+
+inner_edges_1([E,E|T], In) ->
+ inner_edges_1(T, [E|In]);
+inner_edges_1([_|T], In) ->
+ inner_edges_1(T, In);
+inner_edges_1([], In) -> lists:reverse(In).
+
+%% Fold over all edges surrounding a face.
+
+fold(F, Acc, Face, #we{es=Etab,fs=Ftab}) ->
+ Edge = gb_trees:get(Face, Ftab),
+ fold(Edge, Etab, F, Acc, Face, Edge, not_done).
+
+fold(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
+fold(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
+ Acc = F(V, Edge, E, Acc0),
+ fold(NextEdge, Etab, F, Acc, Face, LastEdge, done);
+ #edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
+ Acc = F(V, Edge, E, Acc0),
+ fold(NextEdge, Etab, F, Acc, Face, LastEdge, done)
+ end.
+
+%% Fold over a set of faces.
+
+fold_faces(F, Acc0, [Face|Faces], #we{es=Etab,fs=Ftab}=We) ->
+ Edge = gb_trees:get(Face, Ftab),
+ Acc = fold_faces_1(Edge, Etab, F, Acc0, Face, Edge, not_done),
+ fold_faces(F, Acc, Faces, We);
+fold_faces(_F, Acc, [], _We) -> Acc;
+fold_faces(F, Acc, Faces, We) ->
+ fold_faces(F, Acc, gb_sets:to_list(Faces), We).
+
+fold_faces_1(LastEdge, _, _, Acc, _, LastEdge, done) -> Acc;
+fold_faces_1(Edge, Etab, F, Acc0, Face, LastEdge, _) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ve=V,lf=Face,ltsu=NextEdge}=E ->
+ Acc = F(Face, V, Edge, E, Acc0),
+ fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done);
+ #edge{vs=V,rf=Face,rtsu=NextEdge}=E ->
+ Acc = F(Face, V, Edge, E, Acc0),
+ fold_faces_1(NextEdge, Etab, F, Acc, Face, LastEdge, done)
+ end.
+
+%% Return an unsorted list of edges for the faces (with duplicates).
+
+to_edges_raw(Faces, #we{es=Etab,fs=Ftab}) when is_list(Faces) ->
+ to_edges_raw(Faces, Ftab, Etab, []);
+to_edges_raw(Faces, We) ->
+ to_edges_raw(gb_sets:to_list(Faces), We).
+
+to_edges_raw([Face|Faces], Ftab, Etab, Acc0) ->
+ Edge = gb_trees:get(Face, Ftab),
+ Acc = to_edges_raw_1(Edge, Etab, Acc0, Face, Edge, not_done),
+ to_edges_raw(Faces, Ftab, Etab, Acc);
+to_edges_raw([], _, _, Acc) -> Acc.
+
+to_edges_raw_1(LastEdge, _, Acc, _, LastEdge, done) -> Acc;
+to_edges_raw_1(Edge, Etab, Acc, Face, LastEdge, _) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{lf=Face,ltsu=NextEdge} ->
+ to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done);
+ #edge{rf=Face,rtsu=NextEdge} ->
+ to_edges_raw_1(NextEdge, Etab, [Edge|Acc], Face, LastEdge, done)
+ end.
+
+delete_bad_faces(Fs, #we{fs=Ftab,es=Etab}=We) when is_list(Fs) ->
+ Es = bad_edges(Fs, Ftab, Etab, []),
+ wings_edge:dissolve_edges(Es, We);
+delete_bad_faces(Fs, We) ->
+ delete_bad_faces(gb_sets:to_list(Fs), We).
+
+bad_edges([F|Fs], Ftab, Etab, Acc) ->
+ case gb_trees:lookup(F, Ftab) of
+ {value,Edge} ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ltpr=Same,ltsu=Same,rtpr=Same,rtsu=Same} ->
+ erlang:error({internal_error,one_edged_face,F});
+ #edge{ltpr=Same,ltsu=Same} ->
+ bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
+ #edge{rtpr=Same,rtsu=Same} ->
+ bad_edges(Fs, Ftab, Etab, [Edge|Acc]);
+ _ -> bad_edges(Fs, Ftab, Etab, Acc)
+ end;
+ none -> bad_edges(Fs, Ftab, Etab, Acc)
+ end;
+bad_edges([], _, _, Acc) -> Acc.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl
new file mode 100644
index 0000000000..6e018e49b5
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_facemat.erl
@@ -0,0 +1,299 @@
+%%
+%% wings_facemat.erl --
+%%
+%% This module keeps tracks of the mapping from a face number
+%% to its material name.
+%%
+%% Copyright (c) 2001-2005 Bjorn Gustavsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings_facemat.erl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+%%
+%%
+
+-module(wings_facemat).
+-export([all/1,face/2,used_materials/1,mat_faces/2,
+ assign/2,assign/3,
+ delete_face/2,delete_faces/2,keep_faces/2,
+ hide_faces/1,show_faces/1,
+ renumber/2,gc/1,merge/1]).
+
+-include("wings.hrl").
+-import(lists, [keysearch/3,reverse/1,reverse/2,sort/1]).
+
+%%%
+%%% API functions for retrieving information.
+%%%
+
+%% all(We) -> [{Face,MaterialName}]
+%% Return materials for all faces as an ordered list.
+all(#we{mat=M}=We) when is_atom(M) ->
+ Vis = visible_faces(We),
+ make_tab(Vis, M);
+all(#we{mat=L}) when is_list(L) ->
+ remove_invisible(L).
+
+%% face(Face, We) -> MaterialName
+%% Return the material for the face Face.
+face(_, #we{mat=M}) when is_atom(M) -> M;
+face(Face, #we{mat=Tab}) ->
+ {value,{_,Mat}} = keysearch(Face, 1, Tab),
+ Mat.
+
+%% used_materials(We) -> [MaterialName]
+%% Return an ordered list of all materials used in the We.
+used_materials(#we{mat=M}) when is_atom(M) -> [M];
+used_materials(#we{mat=L}) when is_list(L) ->
+ used_materials_1(L, []).
+
+%% mat_faces([{Face,Info}], We) -> [{Mat,[{Face,Info}]}]
+%% Group face tab into groups based on material.
+%% Used for displaying objects.
+mat_faces(Ftab, #we{mat=AtomMat}) when is_atom(AtomMat) ->
+ [{AtomMat,Ftab}];
+mat_faces(Ftab, #we{mat=MatTab}) ->
+ mat_faces_1(Ftab, remove_invisible(MatTab), []).
+
+%%%
+%%% API functions for updating material name mapping.
+%%%
+
+%% assign([{Face,MaterialName}], We) -> We'
+%% Assign materials.
+assign([], We) -> We;
+assign([{F,M}|_]=FaceMs, We) when is_atom(M), is_integer(F) ->
+ Tab = ordsets:from_list(FaceMs),
+ assign_face_ms(Tab, We).
+
+%% assign(MaterialName, Faces, We) -> We'
+%% Assign MaterialName to all faces Faces.
+assign(Mat, _, #we{mat=Mat}=We) when is_atom(Mat) -> We;
+assign(Mat, Fs, We) when is_atom(Mat), is_list(Fs) ->
+ assign_1(Mat, Fs, We);
+assign(Mat, Fs, We) when is_atom(Mat) ->
+ assign_1(Mat, gb_sets:to_list(Fs), We).
+
+%% delete_face(Face, We) -> We'
+%% Delete the material name mapping for the face Face.
+delete_face(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
+delete_face(Face, #we{mat=MatTab0}=We) ->
+ MatTab = orddict:erase(Face, MatTab0),
+ We#we{mat=MatTab}.
+
+%% delete_face(Faces, We) -> We'
+%% Delete the material name mapping for all faces Faces.
+delete_faces(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
+delete_faces(Faces0, #we{mat=MatTab0}=We) when is_list(Faces0) ->
+ Faces = sofs:from_external(Faces0, [face]),
+ MatTab1 = sofs:from_external(MatTab0, [{face,mat}]),
+ MatTab2 = sofs:drestriction(MatTab1, Faces),
+ MatTab = sofs:to_external(MatTab2),
+ We#we{mat=MatTab};
+delete_faces(Faces, We) ->
+ delete_faces(gb_sets:to_list(Faces), We).
+
+%% keep_faces(Faces, We) -> We'
+%% Delete all the other material names mapping for all faces other Faces.
+keep_faces(_, #we{mat=AtomMat}=We) when is_atom(AtomMat) -> We;
+keep_faces([Face], We) ->
+ Mat = face(Face,We),
+ We#we{mat=[{Face,Mat}]};
+keep_faces(Faces0, #we{mat=MatTab0}=We) when is_list(Faces0) ->
+ Faces = sofs:from_external(Faces0, [face]),
+ MatTab1 = sofs:from_external(MatTab0, [{face,mat}]),
+ MatTab2 = sofs:restriction(MatTab1, Faces),
+ MatTab = sofs:to_external(MatTab2),
+ We#we{mat=MatTab};
+keep_faces(Faces, We) ->
+ keep_faces(gb_sets:to_list(Faces), We).
+
+%% hide_faces(We) -> We'
+%% Update the material name mapping in the We to reflect
+%% the newly hidden faces in the face tab.
+hide_faces(#we{mat=M}=We) when is_atom(M) -> We;
+hide_faces(#we{mat=L0,fs=Ftab}=We) ->
+ L = hide_faces_1(L0, Ftab, []),
+ We#we{mat=L}.
+
+%% show_faces(We) -> We'
+%% Update the material name mapping in the We to reflect
+%% that all faces are again visible.
+show_faces(#we{mat=M}=We) when is_atom(M) -> We;
+show_faces(#we{mat=L0}=We) ->
+ L = show_faces_1(L0, []),
+ We#we{mat=L}.
+
+%% renumber(MaterialMapping, FaceOldToNew) -> MaterialMapping.
+%% Renumber face number in material name mapping.
+renumber(Mat, _) when is_atom(Mat) -> Mat;
+renumber(L, Fmap) when is_list(L) -> renumber_1(L, Fmap, []).
+
+%% gc(We) -> We'
+%% Garbage collect the material mapping information, removing
+%% the mapping for any face no longer present in the face table.
+gc(#we{mat=Mat}=We) when is_atom(Mat) -> We;
+gc(#we{mat=Tab0,fs=Ftab}=We) ->
+ Fs = sofs:from_external(gb_trees:keys(Ftab), [face]),
+ Tab1 = sofs:from_external(Tab0, [{face,material}]),
+ Tab2 = sofs:restriction(Tab1, Fs),
+ Tab = sofs:to_external(Tab2),
+ We#we{mat=compress(Tab)}.
+
+%% merge([We]) -> [{Face,MaterialName}] | MaterialName.
+%% Merge materials for several objects.
+merge([#we{mat=M}|Wes]=L) when is_atom(M) ->
+ case merge_all_same(Wes, M) of
+ true -> M;
+ false -> merge_1(L, [])
+ end;
+merge(L) -> merge_1(L, []).
+
+merge_1([#we{mat=M,es=Etab}|T], Acc) when is_atom(M) ->
+ FsM = merge_2(gb_trees:values(Etab), M, []),
+ merge_1(T, [FsM|Acc]);
+merge_1([#we{mat=FsMs}|T], Acc) ->
+ merge_1(T, [FsMs|Acc]);
+merge_1([], Acc) -> lists:merge(Acc).
+
+merge_2([#edge{lf=Lf,rf=Rf}|T], M, Acc) ->
+ merge_2(T, M, [{Lf,M},{Rf,M}|Acc]);
+merge_2([], _, Acc) -> ordsets:from_list(Acc).
+
+merge_all_same([#we{mat=M}|Wes], M) -> merge_all_same(Wes, M);
+merge_all_same([_|_], _) -> false;
+merge_all_same([], _) -> true.
+
+%%%
+%%% Local functions.
+%%%
+
+assign_1(Mat, Fs, #we{fs=Ftab}=We) ->
+ case length(Fs) =:= gb_trees:size(Ftab) of
+ true -> We#we{mat=Mat};
+ false -> assign_2(Mat, Fs, We)
+ end.
+
+assign_2(Mat, Fs0, #we{fs=Ftab,mat=Mat0}=We) when is_atom(Mat0) ->
+ Fs = ordsets:from_list(Fs0),
+ OtherFaces = ordsets:subtract(gb_trees:keys(Ftab), Fs),
+ Tab0 = make_tab(OtherFaces, Mat0),
+ Tab1 = make_tab(Fs, Mat),
+ Tab = lists:merge(Tab0, Tab1),
+ We#we{mat=Tab};
+assign_2(Mat, Fs0, #we{mat=Tab0}=We) when is_list(Tab0) ->
+ Fs = ordsets:from_list(Fs0),
+ Tab1 = make_tab(Fs, Mat),
+ Tab = mat_merge(Tab1, Tab0, []),
+ We#we{mat=Tab}.
+
+assign_face_ms(Tab, #we{fs=Ftab}=We) ->
+ case length(Tab) =:= gb_trees:size(Ftab) of
+ true -> We#we{mat=compress(Tab)};
+ false -> assign_face_ms_1(Tab, We)
+ end.
+
+assign_face_ms_1(Tab1, #we{fs=Ftab,mat=Mat0}=We) when is_atom(Mat0) ->
+ Tab0 = make_tab(gb_trees:keys(Ftab), Mat0),
+ Tab = mat_merge(Tab1, Tab0, []),
+ We#we{mat=Tab};
+assign_face_ms_1(Tab1, #we{mat=Tab0}=We) when is_list(Tab0) ->
+ Tab = mat_merge(Tab1, Tab0, []),
+ We#we{mat=Tab}.
+
+mat_merge([{Fn,_}|_]=Fns, [{Fo,_}=Fold|Fos], Acc) when Fo < Fn ->
+ mat_merge(Fns, Fos, [Fold|Acc]);
+mat_merge([{Fn,_}=Fnew|Fns], [{Fo,_}|_]=Fos, Acc) when Fo > Fn ->
+ mat_merge(Fns, Fos, [Fnew|Acc]);
+mat_merge([Fnew|Fns], [_|Fos], Acc) -> % Equality
+ mat_merge(Fns, Fos, [Fnew|Acc]);
+mat_merge([], Fos, Acc) ->
+ rev_compress(Acc, Fos);
+mat_merge(Fns, [], Acc) ->
+ rev_compress(Acc, Fns).
+
+make_tab(Fs, M) ->
+ make_tab_1(Fs, M, []).
+
+make_tab_1([F|Fs], M, Acc) ->
+ make_tab_1(Fs, M, [{F,M}|Acc]);
+make_tab_1([], _, Acc) -> reverse(Acc).
+
+
+visible_faces(#we{fs=Ftab}) ->
+ visible_faces_1(gb_trees:keys(Ftab)).
+
+visible_faces_1([F|Fs]) when F < 0 ->
+ visible_faces_1(Fs);
+visible_faces_1(Fs) -> Fs.
+
+remove_invisible([{F,_}|Fs]) when F < 0 ->
+ remove_invisible(Fs);
+remove_invisible(Fs) -> Fs.
+
+hide_faces_1([{F,_}=P|Fms], Ftab, Acc) when F < 0 ->
+ hide_faces_1(Fms, Ftab, [P|Acc]);
+hide_faces_1([{F,M}=P|Fms], Ftab, Acc) ->
+ case gb_trees:is_defined(F, Ftab) of
+ false -> hide_faces_1(Fms, Ftab, [{-F-1,M}|Acc]);
+ true -> hide_faces_1(Fms, Ftab, [P|Acc])
+ end;
+hide_faces_1([], _, Acc) -> sort(Acc).
+
+show_faces_1([{F,M}|Fms], Acc) when F < 0 ->
+ show_faces_1(Fms, [{-F-1,M}|Acc]);
+show_faces_1(Fs, Acc) -> sort(Acc++Fs).
+
+renumber_1([{F,M}|T], Fmap, Acc) ->
+ renumber_1(T, Fmap, [{gb_trees:get(F, Fmap),M}|Acc]);
+renumber_1([], _, Acc) -> sort(Acc).
+
+%% rev_compress([{Face,Mat}], [{Face,Mat}]) -> [{Face,Mat}] | Mat.
+%% Reverse just like lists:reverse/2, but if all materials
+%% turns out to be just the same, return that material.
+rev_compress(L, Acc) ->
+ case same_mat(Acc) of
+ [] -> reverse(L, Acc);
+ M -> rev_compress_1(L, M, Acc)
+ end.
+
+rev_compress_1([{_,M}=E|T], M, Acc) ->
+ %% Same material.
+ rev_compress_1(T, M, [E|Acc]);
+rev_compress_1([_|_]=L, _, Acc) ->
+ %% Another material. Finish by using reverse/2.
+ reverse(L, Acc);
+rev_compress_1([], M, _) ->
+ %% All materials turned out to be the same.
+ M.
+
+%% compress(MaterialTab) -> [{Face,Mat}] | Mat.
+%% Compress a face mapping if possible.
+compress(M) when is_atom(M) -> M;
+compress(L) when is_list(L) ->
+ case same_mat(L) of
+ [] -> L;
+ M -> M
+ end.
+
+same_mat([]) -> [];
+same_mat([{_,M}|T]) -> same_mat_1(T, M).
+
+same_mat_1([{_,M}|T], M) -> same_mat_1(T, M);
+same_mat_1([], M) -> M;
+same_mat_1(_, _) -> [].
+
+used_materials_1([{_,M}|T], [M|_]=Acc) ->
+ used_materials_1(T, Acc);
+used_materials_1([{_,M}|T], Acc) ->
+ used_materials_1(T, [M|Acc]);
+used_materials_1([], Acc) ->
+ ordsets:from_list(Acc).
+
+mat_faces_1([{F1,_}|_]=Fs, [{F2,_}|Ms], Acc) when F2 < F1 ->
+ mat_faces_1(Fs, Ms, Acc);
+mat_faces_1([{F,Info}|Fs], [{F,Mat}|Ms], Acc) ->
+ mat_faces_1(Fs, Ms, [{Mat,{F,Info}}|Acc]);
+mat_faces_1([], _, Acc) -> wings_util:rel2fam(Acc).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl
new file mode 100644
index 0000000000..ebcb560f27
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_intl.hrl
@@ -0,0 +1,15 @@
+%%
+%% wings_intl.hrl --
+%%
+%% Defines for translations
+%%
+%% Copyright (c) 2001-2005 Bjorn Gustavsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id: wings_intl.hrl,v 1.1 2009/01/25 18:55:33 kostis Exp $
+%%
+
+-define(STR(A,B,Str), wings_lang:str({?MODULE,A,B},Str)).
+-define(__(Key,Str), wings_lang:str({?MODULE,Key},Str)).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl
new file mode 100644
index 0000000000..39002c675d
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_io.erl
@@ -0,0 +1,37 @@
+%%
+%% wings_io.erl --
+%%
+%% This module contains most of the low-level GUI for Wings.
+%%
+
+-module(wings_io).
+
+-export([get_matching_events/1]).
+
+-define(EVENT_QUEUE, wings_io_event_queue).
+
+%%%
+%%% Input.
+%%%
+
+get_matching_events(Filter) ->
+ Eq = get(?EVENT_QUEUE),
+ get_matching_events_1(Filter, Eq, [], []).
+
+get_matching_events_1(Filter, Eq0, Match, NoMatch) ->
+ case queue:out(Eq0) of
+ {{value,Ev},Eq} ->
+ case Filter(Ev) of
+ false ->
+ get_matching_events_1(Filter, Eq, Match, [Ev|NoMatch]);
+ true ->
+ get_matching_events_1(Filter, Eq, [Ev|Match], NoMatch)
+ end;
+ {empty,{In,Out}} ->
+ case Match of
+ [] -> [];
+ _ ->
+ put(?EVENT_QUEUE, {In, lists:reverse(NoMatch, Out)}),
+ Match
+ end
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl
new file mode 100644
index 0000000000..eef797027e
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_sel.erl
@@ -0,0 +1,68 @@
+%%
+%% wings_sel.erl --
+%%
+%% This module implements selection utilities.
+%%
+
+-module(wings_sel).
+
+-export([face_regions/2, fold/3, set/3]).
+
+-include("wings.hrl").
+
+set(Mode, Sel, St) ->
+ St#st{selmode=Mode, sel=lists:sort(Sel), sh=false}.
+
+%%%
+%%% Fold over the selection.
+%%%
+
+fold(F, Acc, #st{sel=Sel,shapes=Shapes}) ->
+ fold_1(F, Acc, Shapes, Sel).
+
+fold_1(F, Acc0, Shapes, [{Id,Items}|T]) ->
+ We = gb_trees:get(Id, Shapes),
+ ?ASSERT(We#we.id =:= Id),
+ fold_1(F, F(Items, We, Acc0), Shapes, T);
+fold_1(_F, Acc, _Shapes, []) -> Acc.
+
+%%%
+%%% Divide the face selection into regions where each face shares at least
+%%% one edge with another face in the same region. Two faces can share a
+%%% vertex without necessarily being in the same region.
+%%%
+
+face_regions(Faces, We) when is_list(Faces) ->
+ face_regions_1(gb_sets:from_list(Faces), We);
+face_regions(Faces, We) ->
+ face_regions_1(Faces, We).
+
+face_regions_1(Faces, We) ->
+ find_face_regions(Faces, We, fun collect_face_fun/5, []).
+
+find_face_regions(Faces0, We, Coll, Acc) ->
+ case gb_sets:is_empty(Faces0) of
+ true -> Acc;
+ false ->
+ {Face,Faces1} = gb_sets:take_smallest(Faces0),
+ Ws = [Face],
+ {Reg,Faces} = collect_face_region(Ws, We, Coll, [], Faces1),
+ find_face_regions(Faces, We, Coll, [Reg|Acc])
+ end.
+
+collect_face_region([_|_]=Ws0, We, Coll, Reg0, Faces0) ->
+ Reg = Ws0++Reg0,
+ {Ws,Faces} = wings_face:fold_faces(Coll, {[],Faces0}, Ws0, We),
+ collect_face_region(Ws, We, Coll, Reg, Faces);
+collect_face_region([], _, _, Reg, Faces) ->
+ {gb_sets:from_list(Reg),Faces}.
+
+collect_face_fun(Face, _, _, Rec, {Ws,Faces}=A) ->
+ Of = case Rec of
+ #edge{lf=Face,rf=Of0} -> Of0;
+ #edge{rf=Face,lf=Of0} -> Of0
+ end,
+ case gb_sets:is_member(Of, Faces) of
+ true -> {[Of|Ws],gb_sets:delete(Of, Faces)};
+ false -> A
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl
new file mode 100644
index 0000000000..0df8ca68eb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_shape.erl
@@ -0,0 +1,69 @@
+%%
+%% wings_shape.erl --
+%%
+%% Utilities for shape records.
+%%
+
+-module(wings_shape).
+
+-export([insert/3]).
+
+-include("wings.hrl").
+
+%%%
+%%% Exported functions.
+%%%
+
+%% new(We, Suffix, St0) -> St.
+%% Suffix = cut | clone | copy | extract | sep
+%%
+%% Create a new object based on an old object. The name
+%% will be created from the old name (with digits and known
+%% suffixes stripped) with the given Suffix and a number
+%% appended.
+insert(#we{name=OldName}=We0, Suffix, #st{shapes=Shapes0,onext=Oid}=St) ->
+ Name = new_name(OldName, Suffix, Oid),
+ We = We0#we{id=Oid,name=Name},
+ Shapes = gb_trees:insert(Oid, We, Shapes0),
+ St#st{shapes=Shapes,onext=Oid+1}.
+
+%%%
+%%% Local functions follow.
+%%%
+
+new_name(OldName, Suffix0, Id) ->
+ Suffix = suffix(Suffix0),
+ Base = base(lists:reverse(OldName)),
+ lists:reverse(Base, "_" ++ Suffix ++ integer_to_list(Id)).
+
+%% Note: Filename suffixes are intentionally not translated.
+%% If we are to translate them in the future, base/1 below
+%% must be updated to strip suffixes (both for the current language
+%% and for English).
+
+suffix(cut) -> "cut";
+suffix(clone) -> "clone";
+suffix(copy) -> "copy";
+suffix(extract) -> "extract";
+suffix(mirror) -> "mirror";
+suffix(sep) -> "sep".
+
+%% base_1(ReversedName) -> ReversedBaseName
+%% Given an object name, strip digits and known suffixes to
+%% create a base name. Returns the unchanged name if
+%% no known suffix could be stripped.
+
+base(OldName) ->
+ case base_1(OldName) of
+ error -> OldName;
+ Base -> Base
+ end.
+
+base_1([H|T]) when $0 =< H, H =< $9 -> base_1(T);
+base_1("tuc_"++Base) -> Base; %"_cut"
+base_1("enolc_"++Base) -> Base; %"_clone"
+base_1("ypoc_"++Base) -> Base; %"_copy"
+base_1("tcartxe_"++Base) -> Base; %"_extract"
+base_1("rorrim_"++Base) -> Base; %"_mirror"
+base_1("pes_"++Base) -> Base; %"_sep"
+base_1(_Base) -> error.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl
new file mode 100644
index 0000000000..9572e19955
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_util.erl
@@ -0,0 +1,39 @@
+%%
+%% wings_util.erl --
+%%
+%% Various utility functions that not obviously fit somewhere else.
+%%
+
+-module(wings_util).
+
+-export([gb_trees_smallest_key/1, gb_trees_largest_key/1,
+ gb_trees_map/2, rel2fam/1]).
+
+-include("wings.hrl").
+
+rel2fam(Rel) ->
+ sofs:to_external(sofs:relation_to_family(sofs:relation(Rel))).
+
+%% a definition that does not violate the opaqueness of gb_tree()
+gb_trees_smallest_key(Tree) ->
+ {Key, _V} = gb_trees:smallest(Tree),
+ Key.
+
+%% a definition that violates the opaqueness of gb_tree()
+gb_trees_largest_key({_, Tree}) ->
+ largest_key1(Tree).
+
+largest_key1({Key, _Value, _Smaller, nil}) ->
+ Key;
+largest_key1({_Key, _Value, _Smaller, Larger}) ->
+ largest_key1(Larger).
+
+gb_trees_map(F, {Size,Tree}) ->
+ {Size,gb_trees_map_1(F, Tree)}.
+
+gb_trees_map_1(_, nil) -> nil;
+gb_trees_map_1(F, {K,V,Smaller,Larger}) ->
+ {K,F(K, V),
+ gb_trees_map_1(F, Smaller),
+ gb_trees_map_1(F, Larger)}.
+
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl
new file mode 100644
index 0000000000..d782144def
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/wings/wings_we.erl
@@ -0,0 +1,250 @@
+%%
+%% wings_we.erl --
+%%
+%% This module contains functions to build and manipulate
+%% we records (winged-edged records, the central data structure
+%% in Wings 3D).
+
+-module(wings_we).
+
+-export([rebuild/1, is_consistent/1, is_face_consistent/2, new_id/1,
+ new_items_as_ordset/3, validate_mirror/1, visible/1, visible_edges/1]).
+
+-include("wings.hrl").
+
+%%%
+%%% API.
+%%%
+
+validate_mirror(#we{mirror=none}=We) -> We;
+validate_mirror(#we{fs=Ftab,mirror=Face}=We) ->
+ case gb_trees:is_defined(Face, Ftab) of
+ false -> We#we{mirror=none};
+ true -> We
+ end.
+
+%% rebuild(We) -> We'
+%% Rebuild any missing 'vc' and 'fs' tables. If there are
+%% fewer elements in the 'vc' table than in the 'vp' table,
+%% remove redundant entries in the 'vp' table. Updated id
+%% bounds.
+rebuild(#we{vc=undefined,fs=undefined,es=Etab0}=We0) ->
+ Etab = gb_trees:to_list(Etab0),
+ Ftab = rebuild_ftab(Etab),
+ VctList = rebuild_vct(Etab),
+ We = We0#we{vc=gb_trees:from_orddict(VctList),fs=Ftab},
+ rebuild_1(VctList, We);
+rebuild(#we{vc=undefined,es=Etab}=We) ->
+ VctList = rebuild_vct(gb_trees:to_list(Etab), []),
+ rebuild_1(VctList, We#we{vc=gb_trees:from_orddict(VctList)});
+rebuild(#we{fs=undefined,es=Etab}=We) ->
+ Ftab = rebuild_ftab(gb_trees:to_list(Etab)),
+ rebuild(We#we{fs=Ftab});
+rebuild(We) -> update_id_bounds(We).
+
+%%% Utilities for allocating IDs.
+
+new_id(#we{next_id=Id}=We) ->
+ {Id,We#we{next_id=Id+1}}.
+
+%%% Returns sets of newly created items.
+
+new_items_as_ordset(vertex, #we{next_id=Wid}, #we{next_id=NewWid,vp=Tab}) ->
+ new_items_as_ordset_1(Tab, Wid, NewWid);
+new_items_as_ordset(edge, #we{next_id=Wid}, #we{next_id=NewWid,es=Tab}) ->
+ new_items_as_ordset_1(Tab, Wid, NewWid);
+new_items_as_ordset(face, #we{next_id=Wid}, #we{next_id=NewWid,fs=Tab}) ->
+ new_items_as_ordset_1(Tab, Wid, NewWid).
+
+any_hidden(#we{fs=Ftab}) ->
+ not gb_trees:is_empty(Ftab) andalso
+ wings_util:gb_trees_smallest_key(Ftab) < 0.
+
+%%%
+%%% Local functions.
+%%%
+
+rebuild_1(VctList, #we{vc=Vct,vp=Vtab0}=We) ->
+ case {gb_trees:size(Vct),gb_trees:size(Vtab0)} of
+ {Same,Same} -> rebuild(We);
+ {Sz1,Sz2} when Sz1 < Sz2 ->
+ Vtab = vertex_gc_1(VctList, gb_trees:to_list(Vtab0), []),
+ rebuild(We#we{vp=Vtab})
+ end.
+
+rebuild_vct(Es) ->
+ rebuild_vct(Es, []).
+
+rebuild_vct([{Edge,#edge{vs=Va,ve=Vb}}|Es], Acc0) ->
+ Acc = rebuild_maybe_add(Va, Vb, Edge, Acc0),
+ rebuild_vct(Es, Acc);
+rebuild_vct([], VtoE) ->
+ build_incident_tab(VtoE).
+
+rebuild_ftab(Es) ->
+ rebuild_ftab_1(Es, []).
+
+rebuild_ftab_1([{Edge,#edge{lf=Lf,rf=Rf}}|Es], Acc0) ->
+ Acc = rebuild_maybe_add(Lf, Rf, Edge, Acc0),
+ rebuild_ftab_1(Es, Acc);
+rebuild_ftab_1([], FtoE) ->
+ gb_trees:from_orddict(build_incident_tab(FtoE)).
+
+rebuild_maybe_add(Ka, Kb, E, [_,{Ka,_}|_]=Acc) ->
+ [{Kb,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, [_,{Kb,_}|_]=Acc) ->
+ [{Ka,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, [{Ka,_}|_]=Acc) ->
+ [{Kb,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, [{Kb,_}|_]=Acc) ->
+ [{Ka,E}|Acc];
+rebuild_maybe_add(Ka, Kb, E, Acc) ->
+ [{Ka,E},{Kb,E}|Acc].
+
+vertex_gc_1([{V,_}|Vct], [{V,_}=Vtx|Vpos], Acc) ->
+ vertex_gc_1(Vct, Vpos, [Vtx|Acc]);
+vertex_gc_1([_|_]=Vct, [_|Vpos], Acc) ->
+ vertex_gc_1(Vct, Vpos, Acc);
+vertex_gc_1([], _, Acc) ->
+ gb_trees:from_orddict(lists:reverse(Acc)).
+
+%%%
+%%% Handling of hidden faces.
+%%%
+
+visible(#we{mirror=none,fs=Ftab}) ->
+ visible_2(gb_trees:keys(Ftab));
+visible(#we{mirror=Face,fs=Ftab}) ->
+ visible_2(gb_trees:keys(gb_trees:delete(Face, Ftab))).
+
+visible_2([F|Fs]) when F < 0 -> visible_2(Fs);
+visible_2(Fs) -> Fs.
+
+visible_edges(#we{es=Etab,mirror=Face}=We) ->
+ case any_hidden(We) of
+ false -> gb_trees:keys(Etab);
+ true -> visible_es_1(gb_trees:to_list(Etab), Face, [])
+ end.
+
+visible_es_1([{E,#edge{lf=Lf,rf=Rf}}|Es], Face, Acc) ->
+ if
+ Lf < 0 ->
+ %% Left face hidden.
+ if
+ Rf < 0; Rf =:= Face ->
+ %% Both faces invisible (in some way).
+ visible_es_1(Es, Face, Acc);
+ true ->
+ %% Right face is visible.
+ visible_es_1(Es, Face, [E|Acc])
+ end;
+ Lf =:= Face, Rf < 0 ->
+ %% Left face mirror, right face hidden.
+ visible_es_1(Es, Face, Acc);
+ true ->
+ %% At least one face visible.
+ visible_es_1(Es, Face, [E|Acc])
+ end;
+visible_es_1([], _, Acc) -> ordsets:from_list(Acc).
+
+update_id_bounds(#we{vp=Vtab,es=Etab,fs=Ftab}=We) ->
+ case gb_trees:is_empty(Etab) of
+ true -> We#we{next_id=0};
+ false ->
+ LastId = lists:max([wings_util:gb_trees_largest_key(Vtab),
+ wings_util:gb_trees_largest_key(Etab),
+ wings_util:gb_trees_largest_key(Ftab)]),
+ We#we{next_id=LastId+1}
+ end.
+
+%% build_incident_tab([{Elem,Edge}]) -> [{Elem,Edge}]
+%% Elem = Face or Vertex
+%% Build the table of incident edges for either faces or vertices.
+%% Returns an ordered list where each Elem is unique.
+
+build_incident_tab(ElemToEdgeRel) ->
+ T = ets:new(?MODULE, [ordered_set]),
+ ets:insert(T, ElemToEdgeRel),
+ R = ets:tab2list(T),
+ ets:delete(T),
+ R.
+
+%%%
+%%% Calculate normals.
+%%%
+
+new_items_as_ordset_1(Tab, Wid, NewWid) when NewWid-Wid < 32 ->
+ new_items_as_ordset_2(Wid, NewWid, Tab, []);
+new_items_as_ordset_1(Tab, Wid, _NewWid) ->
+ [Item || Item <- gb_trees:keys(Tab), Item >= Wid].
+
+new_items_as_ordset_2(Wid, NewWid, Tab, Acc) when Wid < NewWid ->
+ case gb_trees:is_defined(Wid, Tab) of
+ true -> new_items_as_ordset_2(Wid+1, NewWid, Tab, [Wid|Acc]);
+ false -> new_items_as_ordset_2(Wid+1, NewWid, Tab, Acc)
+ end;
+new_items_as_ordset_2(_Wid, _NewWid, _Tab, Acc) -> lists:reverse(Acc).
+
+%%%
+%%% Test the consistency of a #we{}.
+%%%
+
+is_consistent(#we{}=We) ->
+ try
+ validate_vertex_tab(We),
+ validate_faces(We)
+ catch error:_ -> false
+ end.
+
+is_face_consistent(Face, #we{fs=Ftab,es=Etab}) ->
+ Edge = gb_trees:get(Face, Ftab),
+ try validate_face(Face, Edge, Etab)
+ catch error:_ -> false
+ end.
+
+validate_faces(#we{fs=Ftab,es=Etab}) ->
+ validate_faces_1(gb_trees:to_list(Ftab), Etab).
+
+validate_faces_1([{Face,Edge}|Fs], Etab) ->
+ validate_face(Face, Edge, Etab),
+ validate_faces_1(Fs, Etab);
+validate_faces_1([], _) -> true.
+
+validate_face(Face, Edge, Etab) ->
+ Ccw = walk_face_ccw(Edge, Etab, Face, Edge, []),
+ Edge = walk_face_cw(Edge, Etab, Face, Ccw),
+ [V|Vs] = lists:sort(Ccw),
+ validate_face_vertices(Vs, V).
+
+validate_face_vertices([V|_], V) ->
+ erlang:error(repeated_vertex);
+validate_face_vertices([_], _) ->
+ true;
+validate_face_vertices([V|Vs], _) ->
+ validate_face_vertices(Vs, V).
+
+walk_face_ccw(LastEdge, _, _, LastEdge, [_|_]=Acc) -> Acc;
+walk_face_ccw(Edge, Etab, Face, LastEdge, Acc) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{ve=V,lf=Face,ltpr=Next} ->
+ walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc]);
+ #edge{vs=V,rf=Face,rtpr=Next} ->
+ walk_face_ccw(Next, Etab, Face, LastEdge, [V|Acc])
+ end.
+
+walk_face_cw(Edge, _, _, []) -> Edge;
+walk_face_cw(Edge, Etab, Face, [V|Vs]) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{vs=V,lf=Face,ltsu=Next} ->
+ walk_face_cw(Next, Etab, Face, Vs);
+ #edge{ve=V,rf=Face,rtsu=Next} ->
+ walk_face_cw(Next, Etab, Face, Vs)
+ end.
+
+validate_vertex_tab(#we{es=Etab,vc=Vct}) ->
+ lists:foreach(fun({V,Edge}) ->
+ case gb_trees:get(Edge, Etab) of
+ #edge{vs=V} -> ok;
+ #edge{ve=V} -> ok
+ end
+ end, gb_trees:to_list(Vct)).
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl
new file mode 100644
index 0000000000..82bcf2edcf
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis1.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis1).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> integer().
+
+%BIF and Unification(t_unify) issue
+f() -> erlang:length(gen()).
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl
new file mode 100644
index 0000000000..3a269622fd
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis2.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis2).
+
+-export([get/2]).
+
+-opaque data() :: gb_tree().
+
+-spec get(term(), data()) -> term().
+
+get(Key, Data) ->
+ %%Should unopaque data for remote calls
+ case gb_trees:lookup(Key, Data) of
+ 'none' -> 'undefined';
+ {'value', Val} -> Val
+ end.
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl
new file mode 100644
index 0000000000..d92c6766ff
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis3.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis3).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> char().
+
+%%List pattern matching issue
+f() -> [H|_T] = gen(), H.
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl
new file mode 100644
index 0000000000..aa1a4abcb7
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis4.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis4).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> boolean().
+
+%%Equality test issue
+f() -> "Dummy" == gen().
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl
new file mode 100644
index 0000000000..30cebf806a
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis5.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis5).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: string().
+
+-spec f() -> boolean().
+
+%% Equality test issue
+f() -> "Dummy" == gen().
+
+-spec gen() -> id().
+
+gen() -> "Dummy".
diff --git a/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl
new file mode 100644
index 0000000000..6f0779d7d1
--- /dev/null
+++ b/lib/dialyzer/test/opaque_tests_SUITE_data/src/zoltan_kis6.erl
@@ -0,0 +1,14 @@
+-module(zoltan_kis6).
+
+-export([f/0, gen/0]).
+
+-opaque id() :: {integer(),atom()}.
+
+%%-spec f() -> id().
+
+%% Tuple Unification (t_unify) issue
+f() -> {X,Y} = gen().
+
+-spec gen() -> id().
+
+gen() -> {34, leprecon}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE.erl b/lib/dialyzer/test/options1_tests_SUITE.erl
new file mode 100644
index 0000000000..02cafe6c5f
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE.erl
@@ -0,0 +1,54 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(options1_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([options1_tests_SUITE_consistency/1, compiler/1]).
+
+suite() ->
+ [{timetrap, {minutes, 30}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, [{include_dirs,["my_include"]},
+ {defines,[{'COMPILER_VSN',42}]},
+ {warnings,[no_improper_lists]}]}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [options1_tests_SUITE_consistency,compiler].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+options1_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+compiler(Config) ->
+ case dialyze(Config, compiler) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..c612e77d3e
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/dialyzer_options
@@ -0,0 +1,2 @@
+{dialyzer_options, [{include_dirs, ["my_include"]}, {defines, [{'COMPILER_VSN', 42}]}, {warnings, [no_improper_lists]}]}.
+{time_limit, 30}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries
new file mode 100644
index 0000000000..513d4a315a
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Entries
@@ -0,0 +1,3 @@
+/erl_bits.hrl/1.1/Wed Dec 17 09:53:40 2008//
+/erl_compile.hrl/1.1/Wed Dec 17 09:53:40 2008//
+D
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository
new file mode 100644
index 0000000000..1c6511fec3
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Repository
@@ -0,0 +1 @@
+dialyzer_tests/option_tests/compiler/my_include
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root
new file mode 100644
index 0000000000..f6cdd6158b
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/CVS/Root
@@ -0,0 +1 @@
+:pserver:[email protected]:/hipe
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl
new file mode 100644
index 0000000000..96d5cec268
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_bits.hrl
@@ -0,0 +1,43 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.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.erlang.org/EPL1_0.txt
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Original Code is Erlang-4.7.3, December, 1998.
+%%
+%% The Initial Developer of the Original Code is Ericsson Telecom
+%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%% Telecom AB. All Rights Reserved.
+%%
+%% Contributor(s): ______________________________________.''
+%%
+%% This is an -*- erlang -*- file.
+%% Generic compiler options, passed from the erl_compile module.
+
+-record(bittype, {
+ type, %% integer/float/binary
+ unit, %% element unit
+ sign, %% signed/unsigned
+ endian %% big/little
+ }).
+
+-record(bitdefault, {
+ integer, %% default type for integer
+ float, %% default type for float
+ binary %% default type for binary
+ }).
+
+%%% (From config.hrl in the bitsyntax branch.)
+-define(SYS_ENDIAN, big).
+-define(SIZEOF_CHAR, 1).
+-define(SIZEOF_DOUBLE, 8).
+-define(SIZEOF_FLOAT, 4).
+-define(SIZEOF_INT, 4).
+-define(SIZEOF_LONG, 4).
+-define(SIZEOF_LONG_LONG, 8).
+-define(SIZEOF_SHORT, 2).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl
new file mode 100644
index 0000000000..ef2b68ac9a
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/my_include/erl_compile.hrl
@@ -0,0 +1,42 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: erl_compile.hrl,v 1.1 2008/12/17 09:53:40 mikpe Exp $
+%%
+
+%% Generic compiler options, passed from the erl_compile module.
+
+-record(options,
+ {includes=[], % Include paths (list of absolute
+ % directory names).
+ outdir=".", % Directory for result (absolute
+ % path).
+ output_type=undefined, % Type of output file (atom).
+ defines=[], % Preprocessor defines. Each
+ % element is an atom (the name to
+ % define), or a {Name, Value}
+ % tuple.
+ warning=1, % Warning level (0 - no
+ % warnings, 1 - standard level,
+ % 2, 3, ... - more warnings).
+ verbose=false, % Verbose (true/false).
+ optimize=999, % Optimize options.
+ specific=[], % Compiler specific options.
+ outfile="", % Name of output file (internal
+ % use in erl_compile.erl).
+ cwd % Current working directory
+ % for erlc.
+ }).
+
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/results/compiler b/lib/dialyzer/test/options1_tests_SUITE_data/results/compiler
new file mode 100644
index 0000000000..924ef389df
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/results/compiler
@@ -0,0 +1,35 @@
+
+beam_asm.erl:32: The pattern {'error', Error} can never match the type <<_:64,_:_*8>>
+beam_bool.erl:193: The pattern {[], _} can never match the type {[{_,_,_,_},...],[any()]}
+beam_bool.erl:510: The pattern [{'set', [Dst], _, _}, {'%live', _}] can never match the type [{_,_,_,_}]
+beam_disasm.erl:537: The variable X can never match since previous clauses completely covered the type 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
+beam_type.erl:284: The pattern <'pi', 0> can never match the type <_,1 | 2>
+beam_validator.erl:396: The pattern <{'jump', {'f', _}}, Vst = {'vst', 'none', _}> can never match the type <_,#vst{current::#st{ct::[]}}>
+beam_validator.erl:690: The pattern <'term', OldT> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:692: Clause guard cannot succeed. The pattern <NewT = {Type, New}, OldT = {_, Old}> was matched against the type <{'tuple',[any(),...]},_>
+beam_validator.erl:699: Clause guard cannot succeed. The pattern <NewT = {Type, _}, 'number'> was matched against the type <{'tuple',[any(),...]},_>
+beam_validator.erl:702: The pattern <'number', OldT = {Type, _}> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:705: The pattern <'bool', {'atom', A}> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:707: The pattern <{'atom', A}, 'bool'> can never match the type <{'tuple',[any(),...]},_>
+beam_validator.erl:713: Guard test is_integer(Sz::[any(),...]) can never succeed
+beam_validator.erl:727: Function upgrade_bool/1 will never be called
+cerl_inline.erl:190: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:219: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:230: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2333: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2355: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:238: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2436: Function filename/1 will never be called
+cerl_inline.erl:2700: The pattern 'true' can never match the type 'false'
+cerl_inline.erl:2730: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2738: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2750: The pattern <{[], L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2752: The pattern <{[], _L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2754: The pattern <{F, L, D}, Vs> can never match the type <[1..255,...],[any()]>
+cerl_inline.erl:2756: The pattern <{F, _L, D}, Vs> can never match the type <[1..255,...],[any()]>
+compile.erl:788: The pattern {'error', Es} can never match the type {'ok',<<_:64,_:_*8>>}
+core_lint.erl:473: The pattern <{'c_atom', _, 'all'}, 'binary', _Def, St> can never match the type <_,#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::{_,_} | {_,_,_} | {_,_,_,_},tl::{_,_} | {_,_,_} | {_,_,_,_}},tl::#c_nil{} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple',_,_} | #c_cons{hd::{_,_} | {_,_,_} | {_,_,_,_},tl::{_,_} | {_,_,_} | {_,_,_,_}}},[any()],_>
+core_lint.erl:505: The pattern <_Req, 'unknown', St> can never match the type <non_neg_integer(),non_neg_integer(),_>
+v3_codegen.erl:1569: The call v3_codegen:load_reg_1(V::any(),I::0,Rs::any(),pos_integer()) will never return since it differs in the 4th argument from the success typing arguments: (any(),0,maybe_improper_list(),0)
+v3_codegen.erl:1571: The call v3_codegen:load_reg_1(V::any(),I::0,[],pos_integer()) will never return since it differs in the 4th argument from the success typing arguments: (any(),0,maybe_improper_list(),0)
+v3_core.erl:646: The pattern <Prim = {'iprimop', _, _, _}, St> can never match the type <#c_nil{anno::[any(),...]} | {'c_atom' | 'c_char' | 'c_float' | 'c_int' | 'c_string' | 'c_tuple' | 'c_var' | 'ibinary' | 'icatch' | 'ireceive1',[any(),...] | {_,_,_,_},_} | #c_cons{anno::[any(),...]} | #c_fname{anno::[any(),...]} | #iletrec{anno::{_,_,_,_},defs::[any(),...],body::[any(),...]} | #icase{anno::{_,_,_,_},args::[any()],clauses::[any()],fc::{_,_,_,_,_,_}} | #ireceive2{anno::{_,_,_,_},clauses::[any()],action::[any()]} | #ifun{anno::{_,_,_,_},id::[any(),...],vars::[any()],clauses::[any(),...],fc::{_,_,_,_,_,_}} | #imatch{anno::{_,_,_,_},guard::[],fc::{_,_,_,_,_,_}} | #itry{anno::{_,_,_,_},args::[any()],vars::[any(),...],body::[any(),...],evars::[any(),...],handler::[any(),...]},_>
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl
new file mode 100644
index 0000000000..c2d9edcaa7
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_asm.erl
@@ -0,0 +1,358 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_asm.erl,v 1.1 2008/12/17 09:53:40 mikpe Exp $
+%%
+%% Purpose : Assembler for threaded Beam.
+
+-module(beam_asm).
+
+-export([module/4,format_error/1]).
+-export([encode/2]).
+
+-import(lists, [map/2,member/2,keymember/3,duplicate/2]).
+-include("beam_opcodes.hrl").
+
+-define(bs_aligned, 1).
+
+module(Code, Abst, SourceFile, Opts) ->
+ case assemble(Code, Abst, SourceFile, Opts) of
+ {error, Error} ->
+ {error, [{none, ?MODULE, Error}]};
+ Bin when binary(Bin) ->
+ {ok, Bin}
+ end.
+
+format_error({crashed, Why}) ->
+ io_lib:format("beam_asm_int: EXIT: ~p", [Why]).
+
+assemble({Mod,Exp,Attr,Asm,NumLabels}, Abst, SourceFile, Opts) ->
+ {1,Dict0} = beam_dict:atom(Mod, beam_dict:new()),
+ NumFuncs = length(Asm),
+ {Code,Dict1} = assemble_1(Asm, Exp, Dict0, []),
+ build_file(Code, Attr, Dict1, NumLabels, NumFuncs, Abst, SourceFile, Opts).
+
+assemble_1([{function,Name,Arity,Entry,Asm}|T], Exp, Dict0, Acc) ->
+ Dict1 = case member({Name,Arity}, Exp) of
+ true ->
+ beam_dict:export(Name, Arity, Entry, Dict0);
+ false ->
+ beam_dict:local(Name, Arity, Entry, Dict0)
+ end,
+ {Code, Dict2} = assemble_function(Asm, Acc, Dict1),
+ assemble_1(T, Exp, Dict2, Code);
+assemble_1([], _Exp, Dict0, Acc) ->
+ {IntCodeEnd,Dict1} = make_op(int_code_end, Dict0),
+ {list_to_binary(lists:reverse(Acc, [IntCodeEnd])),Dict1}.
+
+assemble_function([H|T], Acc, Dict0) ->
+ {Code, Dict} = make_op(H, Dict0),
+ assemble_function(T, [Code| Acc], Dict);
+assemble_function([], Code, Dict) ->
+ {Code, Dict}.
+
+build_file(Code, Attr, Dict, NumLabels, NumFuncs, Abst, SourceFile, Opts) ->
+ %% Create the code chunk.
+
+ CodeChunk = chunk(<<"Code">>,
+ <<16:32,
+ (beam_opcodes:format_number()):32,
+ (beam_dict:highest_opcode(Dict)):32,
+ NumLabels:32,
+ NumFuncs:32>>,
+ Code),
+
+ %% Create the atom table chunk.
+
+ {NumAtoms, AtomTab} = beam_dict:atom_table(Dict),
+ AtomChunk = chunk(<<"Atom">>, <<NumAtoms:32>>, AtomTab),
+
+ %% Create the import table chunk.
+
+ {NumImps, ImpTab0} = beam_dict:import_table(Dict),
+ Imp = flatten_imports(ImpTab0),
+ ImportChunk = chunk(<<"ImpT">>, <<NumImps:32>>, Imp),
+
+ %% Create the export table chunk.
+
+ {NumExps, ExpTab0} = beam_dict:export_table(Dict),
+ Exp = flatten_exports(ExpTab0),
+ ExpChunk = chunk(<<"ExpT">>, <<NumExps:32>>, Exp),
+
+ %% Create the local function table chunk.
+
+ {NumLocals, Locals} = beam_dict:local_table(Dict),
+ Loc = flatten_exports(Locals),
+ LocChunk = chunk(<<"LocT">>, <<NumLocals:32>>, Loc),
+
+ %% Create the string table chunk.
+
+ {_,StringTab} = beam_dict:string_table(Dict),
+ StringChunk = chunk(<<"StrT">>, StringTab),
+
+ %% Create the fun table chunk. It is important not to build an empty chunk,
+ %% as that would change the MD5.
+
+ LambdaChunk = case beam_dict:lambda_table(Dict) of
+ {0,[]} -> [];
+ {NumLambdas,LambdaTab} ->
+ chunk(<<"FunT">>, <<NumLambdas:32>>, LambdaTab)
+ end,
+
+ %% Create the attributes and compile info chunks.
+
+ Essentials = [AtomChunk,CodeChunk,StringChunk,ImportChunk,ExpChunk,LambdaChunk],
+ {Attributes,Compile} = build_attributes(Opts, SourceFile, Attr, Essentials),
+ AttrChunk = chunk(<<"Attr">>, Attributes),
+ CompileChunk = chunk(<<"CInf">>, Compile),
+
+ %% Create the abstract code chunk.
+
+ AbstChunk = chunk(<<"Abst">>, Abst),
+
+ %% Create IFF chunk.
+
+ Chunks = case member(slim, Opts) of
+ true -> [Essentials,AttrChunk,CompileChunk,AbstChunk];
+ false -> [Essentials,LocChunk,AttrChunk,CompileChunk,AbstChunk]
+ end,
+ build_form(<<"BEAM">>, Chunks).
+
+%% Build an IFF form.
+
+build_form(Id, Chunks0) when size(Id) == 4, list(Chunks0) ->
+ Chunks = list_to_binary(Chunks0),
+ Size = size(Chunks),
+ 0 = Size rem 4, % Assertion: correct padding?
+ <<"FOR1",(Size+4):32,Id/binary,Chunks/binary>>.
+
+%% Build a correctly padded chunk (with no sub-header).
+
+chunk(Id, Contents) when size(Id) == 4, binary(Contents) ->
+ Size = size(Contents),
+ [<<Id/binary,Size:32>>,Contents|pad(Size)];
+chunk(Id, Contents) when list(Contents) ->
+ chunk(Id, list_to_binary(Contents)).
+
+%% Build a correctly padded chunk (with a sub-header).
+
+chunk(Id, Head, Contents) when size(Id) == 4, is_binary(Head), is_binary(Contents) ->
+ Size = size(Head)+size(Contents),
+ [<<Id/binary,Size:32,Head/binary>>,Contents|pad(Size)];
+chunk(Id, Head, Contents) when list(Contents) ->
+ chunk(Id, Head, list_to_binary(Contents)).
+
+pad(Size) ->
+ case Size rem 4 of
+ 0 -> [];
+ Rem -> duplicate(4 - Rem, 0)
+ end.
+
+flatten_exports(Exps) ->
+ list_to_binary(map(fun({F,A,L}) -> <<F:32,A:32,L:32>> end, Exps)).
+
+flatten_imports(Imps) ->
+ list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)).
+
+build_attributes(Opts, SourceFile, Attr, Essentials) ->
+ Misc = case member(slim, Opts) of
+ false ->
+ {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(),
+ [{time,{Y,Mo,D,H,Mi,S}},{source,SourceFile}];
+ true -> []
+ end,
+ Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc],
+ {term_to_binary(calc_vsn(Attr, Essentials)),term_to_binary(Compile)}.
+
+%%
+%% If the attributes contains no 'vsn' attribute, we'll insert one
+%% with an MD5 "checksum" calculated on the code as its value.
+%% We'll not change an existing 'vsn' attribute.
+%%
+
+calc_vsn(Attr, Essentials) ->
+ case keymember(vsn, 1, Attr) of
+ true -> Attr;
+ false ->
+ <<Number:128>> = erlang:md5(Essentials),
+ [{vsn,[Number]}|Attr]
+ end.
+
+bif_type('-', 1) -> negate;
+bif_type('+', 2) -> {op, m_plus};
+bif_type('-', 2) -> {op, m_minus};
+bif_type('*', 2) -> {op, m_times};
+bif_type('/', 2) -> {op, m_div};
+bif_type('div', 2) -> {op, int_div};
+bif_type('rem', 2) -> {op, int_rem};
+bif_type('band', 2) -> {op, int_band};
+bif_type('bor', 2) -> {op, int_bor};
+bif_type('bxor', 2) -> {op, int_bxor};
+bif_type('bsl', 2) -> {op, int_bsl};
+bif_type('bsr', 2) -> {op, int_bsr};
+bif_type('bnot', 1) -> {op, int_bnot};
+bif_type(fnegate, 1) -> {op, fnegate};
+bif_type(fadd, 2) -> {op, fadd};
+bif_type(fsub, 2) -> {op, fsub};
+bif_type(fmul, 2) -> {op, fmul};
+bif_type(fdiv, 2) -> {op, fdiv};
+bif_type(_, _) -> bif.
+
+make_op(Comment, Dict) when element(1, Comment) == '%' ->
+ {[],Dict};
+make_op({'%live',_R}, Dict) ->
+ {[],Dict};
+make_op({bif, Bif, nofail, [], Dest}, Dict) ->
+ encode_op(bif0, [{extfunc, erlang, Bif, 0}, Dest], Dict);
+make_op({bif, raise, _Fail, [A1,A2], _Dest}, Dict) ->
+ encode_op(raise, [A1,A2], Dict);
+make_op({bif, Bif, Fail, Args, Dest}, Dict) ->
+ Arity = length(Args),
+ case bif_type(Bif, Arity) of
+ {op, Op} ->
+ make_op(list_to_tuple([Op, Fail|Args++[Dest]]), Dict);
+ negate ->
+ %% Fake negation operator.
+ make_op({m_minus, Fail, {integer,0}, hd(Args), Dest}, Dict);
+ bif ->
+ BifOp = list_to_atom(lists:concat([bif, Arity])),
+ encode_op(BifOp, [Fail, {extfunc, erlang, Bif, Arity}|Args++[Dest]],
+ Dict)
+ end;
+make_op({bs_add=Op,Fail,[Src1,Src2,Unit],Dest}, Dict) ->
+ encode_op(Op, [Fail,Src1,Src2,Unit,Dest], Dict);
+make_op({test,Cond,Fail,Ops}, Dict) when list(Ops) ->
+ encode_op(Cond, [Fail|Ops], Dict);
+make_op({make_fun2,{f,Lbl},Index,OldUniq,NumFree}, Dict0) ->
+ {Fun,Dict} = beam_dict:lambda(Lbl, Index, OldUniq, NumFree, Dict0),
+ make_op({make_fun2,Fun}, Dict);
+make_op(Op, Dict) when atom(Op) ->
+ encode_op(Op, [], Dict);
+make_op({kill,Y}, Dict) ->
+ make_op({init,Y}, Dict);
+make_op({Name,Arg1}, Dict) ->
+ encode_op(Name, [Arg1], Dict);
+make_op({Name,Arg1,Arg2}, Dict) ->
+ encode_op(Name, [Arg1,Arg2], Dict);
+make_op({Name,Arg1,Arg2,Arg3}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3], Dict);
+make_op({Name,Arg1,Arg2,Arg3,Arg4}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3,Arg4], Dict);
+make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5], Dict);
+make_op({Name,Arg1,Arg2,Arg3,Arg4,Arg5,Arg6}, Dict) ->
+ encode_op(Name, [Arg1,Arg2,Arg3,Arg4,Arg5,Arg6], Dict).
+
+encode_op(Name, Args, Dict0) when atom(Name) ->
+ {EncArgs,Dict1} = encode_args(Args, Dict0),
+ Op = beam_opcodes:opcode(Name, length(Args)),
+ Dict2 = beam_dict:opcode(Op, Dict1),
+ {list_to_binary([Op|EncArgs]),Dict2}.
+
+encode_args([Arg| T], Dict0) ->
+ {EncArg, Dict1} = encode_arg(Arg, Dict0),
+ {EncTail, Dict2} = encode_args(T, Dict1),
+ {[EncArg| EncTail], Dict2};
+encode_args([], Dict) ->
+ {[], Dict}.
+
+encode_arg({x, X}, Dict) when X >= 0 ->
+ {encode(?tag_x, X), Dict};
+encode_arg({y, Y}, Dict) when Y >= 0 ->
+ {encode(?tag_y, Y), Dict};
+encode_arg({atom, Atom}, Dict0) when atom(Atom) ->
+ {Index, Dict} = beam_dict:atom(Atom, Dict0),
+ {encode(?tag_a, Index), Dict};
+encode_arg({integer, N}, Dict) ->
+ {encode(?tag_i, N), Dict};
+encode_arg(nil, Dict) ->
+ {encode(?tag_a, 0), Dict};
+encode_arg({f, W}, Dict) ->
+ {encode(?tag_f, W), Dict};
+encode_arg({'char', C}, Dict) ->
+ {encode(?tag_h, C), Dict};
+encode_arg({string, String}, Dict0) ->
+ {Offset, Dict} = beam_dict:string(String, Dict0),
+ {encode(?tag_u, Offset), Dict};
+encode_arg({extfunc, M, F, A}, Dict0) ->
+ {Index, Dict} = beam_dict:import(M, F, A, Dict0),
+ {encode(?tag_u, Index), Dict};
+encode_arg({list, List}, Dict0) ->
+ {L, Dict} = encode_list(List, Dict0, []),
+ {[encode(?tag_z, 1), encode(?tag_u, length(List))|L], Dict};
+encode_arg({float, Float}, Dict) when float(Float) ->
+ {[encode(?tag_z, 0)|<<Float:64/float>>], Dict};
+encode_arg({fr,Fr}, Dict) ->
+ {[encode(?tag_z, 2),encode(?tag_u,Fr)], Dict};
+encode_arg({field_flags,Flags0}, Dict) ->
+ Flags = lists:foldl(fun (F, S) -> S bor flag_to_bit(F) end, 0, Flags0),
+ {encode(?tag_u, Flags), Dict};
+encode_arg({alloc,List}, Dict) ->
+ {encode_alloc_list(List),Dict};
+encode_arg(Int, Dict) when is_integer(Int) ->
+ {encode(?tag_u, Int),Dict}.
+
+flag_to_bit(aligned) -> 16#01;
+flag_to_bit(little) -> 16#02;
+flag_to_bit(big) -> 16#00;
+flag_to_bit(signed) -> 16#04;
+flag_to_bit(unsigned)-> 16#00;
+flag_to_bit(exact) -> 16#08;
+flag_to_bit(native) -> 16#10.
+
+encode_list([H|T], _Dict, _Acc) when is_list(H) ->
+ exit({illegal_nested_list,encode_arg,[H|T]});
+encode_list([H|T], Dict0, Acc) ->
+ {Enc,Dict} = encode_arg(H, Dict0),
+ encode_list(T, Dict, [Enc|Acc]);
+encode_list([], Dict, Acc) ->
+ {lists:reverse(Acc), Dict}.
+
+encode_alloc_list(L0) ->
+ L = encode_alloc_list_1(L0),
+ [encode(?tag_z, 3),encode(?tag_u, length(L0))|L].
+
+encode_alloc_list_1([{words,Words}|T]) ->
+ [encode(?tag_u, 0),encode(?tag_u, Words)|encode_alloc_list_1(T)];
+encode_alloc_list_1([{floats,Floats}|T]) ->
+ [encode(?tag_u, 1),encode(?tag_u, Floats)|encode_alloc_list_1(T)];
+encode_alloc_list_1([]) -> [].
+
+encode(Tag, N) when N < 0 ->
+ encode1(Tag, negative_to_bytes(N, []));
+encode(Tag, N) when N < 16 ->
+ (N bsl 4) bor Tag;
+encode(Tag, N) when N < 16#800 ->
+ [((N bsr 3) band 2#11100000) bor Tag bor 2#00001000, N band 16#ff];
+encode(Tag, N) ->
+ encode1(Tag, to_bytes(N, [])).
+
+encode1(Tag, Bytes) ->
+ case length(Bytes) of
+ Num when 2 =< Num, Num =< 8 ->
+ [((Num-2) bsl 5) bor 2#00011000 bor Tag| Bytes];
+ Num when 8 < Num ->
+ [2#11111000 bor Tag, encode(?tag_u, Num-9)| Bytes]
+ end.
+
+to_bytes(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+to_bytes(N, Acc) ->
+ to_bytes(N bsr 8, [N band 16#ff| Acc]).
+
+negative_to_bytes(-1, [B1, B2|T]) when B1 > 127 ->
+ [B1, B2|T];
+negative_to_bytes(N, Acc) ->
+ negative_to_bytes(N bsr 8, [N band 16#ff|Acc]).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl
new file mode 100644
index 0000000000..b0dd3e6380
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_block.erl
@@ -0,0 +1,601 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_block.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Partitions assembly instructions into basic blocks and
+%% optimizes them.
+
+-module(beam_block).
+
+-export([module/2]).
+-export([live_at_entry/1]). %Used by beam_type, beam_bool.
+-export([is_killed/2]). %Used by beam_dead, beam_type, beam_bool.
+-export([is_not_used/2]). %Used by beam_bool.
+-export([merge_blocks/2]). %Used by beam_jump.
+-import(lists, [map/2,mapfoldr/3,reverse/1,reverse/2,foldl/3,
+ member/2,sort/1,all/2]).
+-define(MAXREG, 1024).
+
+module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
+ {ok,{Mod,Exp,Attr,map(fun function/1, Fs),Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ %% Collect basic blocks and optimize them.
+ Is = blockify(Is0),
+
+ %% Done.
+ {function,Name,Arity,CLabel,Is}.
+
+%% blockify(Instructions0) -> Instructions
+%% Collect sequences of instructions to basic blocks and
+%% optimize the contents of the blocks. Also do some simple
+%% optimations on instructions outside the blocks.
+
+blockify(Is) ->
+ blockify(Is, []).
+
+blockify([{loop_rec,{f,Fail},{x,0}},{loop_rec_end,_Lbl},{label,Fail}|Is], Acc) ->
+ %% Useless instruction sequence.
+ blockify(Is, Acc);
+blockify([{test,bs_test_tail,F,[Bits]}|Is],
+ [{test,bs_skip_bits,F,[{integer,I},Unit,_Flags]}|Acc]) ->
+ blockify(Is, [{test,bs_test_tail,F,[Bits+I*Unit]}|Acc]);
+blockify([{test,bs_skip_bits,F,[{integer,I1},Unit1,_]}|Is],
+ [{test,bs_skip_bits,F,[{integer,I2},Unit2,Flags]}|Acc]) ->
+ blockify(Is, [{test,bs_skip_bits,F,
+ [{integer,I1*Unit1+I2*Unit2},1,Flags]}|Acc]);
+blockify([{test,is_atom,{f,Fail},[Reg]}=I|
+ [{select_val,Reg,{f,Fail},
+ {list,[{atom,false},{f,_}=BrFalse,
+ {atom,true}=AtomTrue,{f,_}=BrTrue]}}|Is]=Is0],
+ [{block,Bl}|_]=Acc) ->
+ case is_last_bool(Bl, Reg) of
+ false ->
+ blockify(Is0, [I|Acc]);
+ true ->
+ blockify(Is, [{jump,BrTrue},
+ {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
+ end;
+blockify([{test,is_atom,{f,Fail},[Reg]}=I|
+ [{select_val,Reg,{f,Fail},
+ {list,[{atom,true}=AtomTrue,{f,_}=BrTrue,
+ {atom,false},{f,_}=BrFalse]}}|Is]=Is0],
+ [{block,Bl}|_]=Acc) ->
+ case is_last_bool(Bl, Reg) of
+ false ->
+ blockify(Is0, [I|Acc]);
+ true ->
+ blockify(Is, [{jump,BrTrue},
+ {test,is_eq_exact,BrFalse,[Reg,AtomTrue]}|Acc])
+ end;
+blockify([I|Is0]=IsAll, Acc) ->
+ case is_bs_put(I) of
+ true ->
+ {BsPuts0,Is} = collect_bs_puts(IsAll),
+ BsPuts = opt_bs_puts(BsPuts0),
+ blockify(Is, reverse(BsPuts, Acc));
+ false ->
+ case collect(I) of
+ error -> blockify(Is0, [I|Acc]);
+ Instr when is_tuple(Instr) ->
+ {Block0,Is} = collect_block(IsAll),
+ Block = opt_block(Block0),
+ blockify(Is, [{block,Block}|Acc])
+ end
+ end;
+blockify([], Acc) -> reverse(Acc).
+
+is_last_bool([I,{'%live',_}], Reg) ->
+ is_last_bool([I], Reg);
+is_last_bool([{set,[Reg],As,{bif,N,_}}], Reg) ->
+ Ar = length(As),
+ erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar)
+ orelse erl_internal:bool_op(N, Ar);
+is_last_bool([_|Is], Reg) -> is_last_bool(Is, Reg);
+is_last_bool([], _) -> false.
+
+collect_block(Is) ->
+ collect_block(Is, []).
+
+collect_block([{allocate_zero,Ns,R},{test_heap,Nh,R}|Is], Acc) ->
+ collect_block(Is, [{allocate,R,{no_opt,Ns,Nh,[]}}|Acc]);
+collect_block([I|Is]=Is0, Acc) ->
+ case collect(I) of
+ error -> {reverse(Acc),Is0};
+ Instr -> collect_block(Is, [Instr|Acc])
+ end;
+collect_block([], Acc) -> {reverse(Acc),[]}.
+
+collect({allocate_zero,N,R}) -> {allocate,R,{zero,N,0,[]}};
+collect({test_heap,N,R}) -> {allocate,R,{nozero,nostack,N,[]}};
+collect({bif,N,nofail,As,D}) -> {set,[D],As,{bif,N}};
+collect({bif,N,F,As,D}) -> {set,[D],As,{bif,N,F}};
+collect({move,S,D}) -> {set,[D],[S],move};
+collect({put_list,S1,S2,D}) -> {set,[D],[S1,S2],put_list};
+collect({put_tuple,A,D}) -> {set,[D],[],{put_tuple,A}};
+collect({put,S}) -> {set,[],[S],put};
+collect({put_string,L,S,D}) -> {set,[D],[],{put_string,L,S}};
+collect({get_tuple_element,S,I,D}) -> {set,[D],[S],{get_tuple_element,I}};
+collect({set_tuple_element,S,D,I}) -> {set,[],[S,D],{set_tuple_element,I}};
+collect({get_list,S,D1,D2}) -> {set,[D1,D2],[S],get_list};
+collect(remove_message) -> {set,[],[],remove_message};
+collect({'catch',R,L}) -> {set,[R],[],{'catch',L}};
+collect({'%live',_}=Live) -> Live;
+collect(_) -> error.
+
+opt_block(Is0) ->
+ %% We explicitly move any allocate instruction upwards before optimising
+ %% moves, to avoid any potential problems with the calculation of live
+ %% registers.
+ Is1 = find_fixpoint(fun move_allocates/1, Is0),
+ Is2 = find_fixpoint(fun opt/1, Is1),
+ Is = opt_alloc(Is2),
+ share_floats(Is).
+
+find_fixpoint(OptFun, Is0) ->
+ case OptFun(Is0) of
+ Is0 -> Is0;
+ Is1 -> find_fixpoint(OptFun, Is1)
+ end.
+
+move_allocates([{set,_Ds,_Ss,{set_tuple_element,_}}|_]=Is) -> Is;
+move_allocates([{set,Ds,Ss,_Op}=Set,{allocate,R,Alloc}|Is]) when is_integer(R) ->
+ [{allocate,live_regs(Ds, Ss, R),Alloc},Set|Is];
+move_allocates([{allocate,R1,Alloc1},{allocate,R2,Alloc2}|Is]) ->
+ R1 = R2, % Assertion.
+ move_allocates([{allocate,R1,combine_alloc(Alloc1, Alloc2)}|Is]);
+move_allocates([I|Is]) ->
+ [I|move_allocates(Is)];
+move_allocates([]) -> [].
+
+combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
+ {zero,Ns,Nh1+Nh2,Init}.
+
+merge_blocks([{allocate,R,{Attr,Ns,Nh1,Init}}|B1],
+ [{allocate,_,{_,nostack,Nh2,[]}}|B2]) ->
+ Alloc = {allocate,R,{Attr,Ns,Nh1+Nh2,Init}},
+ [Alloc|merge_blocks(B1, B2)];
+merge_blocks(B1, B2) -> merge_blocks_1(B1++[{set,[],[],stop_here}|B2]).
+
+merge_blocks_1([{set,[],_,stop_here}|Is]) -> Is;
+merge_blocks_1([{set,[D],_,move}=I|Is]) ->
+ case is_killed(D, Is) of
+ true -> merge_blocks_1(Is);
+ false -> [I|merge_blocks_1(Is)]
+ end;
+merge_blocks_1([I|Is]) -> [I|merge_blocks_1(Is)].
+
+opt([{set,[Dst],As,{bif,Bif,Fail}}=I1,
+ {set,[Dst],[Dst],{bif,'not',Fail}}=I2|Is]) ->
+ %% Get rid of the 'not' if the operation can be inverted.
+ case inverse_comp_op(Bif) of
+ none -> [I1,I2|opt(Is)];
+ RevBif -> [{set,[Dst],As,{bif,RevBif,Fail}}|opt(Is)]
+ end;
+opt([{set,[X],[X],move}|Is]) -> opt(Is);
+opt([{set,[D1],[{integer,Idx1},Reg],{bif,element,{f,0}}}=I1,
+ {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
+ when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
+ opt([I2,I1|Is]);
+opt([{set,Ds0,Ss,Op}|Is0]) ->
+ {Ds,Is} = opt_moves(Ds0, Is0),
+ [{set,Ds,Ss,Op}|opt(Is)];
+opt([I|Is]) -> [I|opt(Is)];
+opt([]) -> [].
+
+opt_moves([], Is0) -> {[],Is0};
+opt_moves([D0], Is0) ->
+ {D1,Is1} = opt_move(D0, Is0),
+ {[D1],Is1};
+opt_moves([X0,Y0]=Ds, Is0) ->
+ {X1,Is1} = opt_move(X0, Is0),
+ case opt_move(Y0, Is1) of
+ {Y1,Is2} when X1 =/= Y1 -> {[X1,Y1],Is2};
+ _Other when X1 =/= Y0 -> {[X1,Y0],Is1};
+ _Other -> {Ds,Is0}
+ end.
+
+opt_move(R, [{set,[D],[R],move}|Is]=Is0) ->
+ case is_killed(R, Is) of
+ true -> {D,Is};
+ false -> {R,Is0}
+ end;
+opt_move(R, [I|Is0]) ->
+ case is_transparent(R, I) of
+ true ->
+ {D,Is1} = opt_move(R, Is0),
+ case is_transparent(D, I) of
+ true -> {D,[I|Is1]};
+ false -> {R,[I|Is0]}
+ end;
+ false -> {R,[I|Is0]}
+ end;
+opt_move(R, []) -> {R,[]}.
+
+is_transparent(R, {set,Ds,Ss,_Op}) ->
+ case member(R, Ds) of
+ true -> false;
+ false -> not member(R, Ss)
+ end;
+is_transparent(_, _) -> false.
+
+%% is_killed(Register, [Instruction]) -> true|false
+%% Determine whether a register is killed by the instruction sequence.
+%% If true is returned, it means that the register will not be
+%% referenced in ANY way (not even indirectly by an allocate instruction);
+%% i.e. it is OK to enter the instruction sequence with Register
+%% containing garbage.
+
+is_killed({x,N}=R, [{block,Blk}|Is]) ->
+ case is_killed(R, Blk) of
+ true -> true;
+ false ->
+ %% Before looking beyond the block, we must be
+ %% sure that the register is not referenced by
+ %% any allocate instruction in the block.
+ case all(fun({allocate,Live,_}) when N < Live -> false;
+ (_) -> true
+ end, Blk) of
+ true -> is_killed(R, Is);
+ false -> false
+ end
+ end;
+is_killed(R, [{block,Blk}|Is]) ->
+ case is_killed(R, Blk) of
+ true -> true;
+ false -> is_killed(R, Is)
+ end;
+is_killed(R, [{set,Ds,Ss,_Op}|Is]) ->
+ case member(R, Ss) of
+ true -> false;
+ false ->
+ case member(R, Ds) of
+ true -> true;
+ false -> is_killed(R, Is)
+ end
+ end;
+is_killed(R, [{case_end,Used}|_]) -> R =/= Used;
+is_killed(R, [{badmatch,Used}|_]) -> R =/= Used;
+is_killed(_, [if_end|_]) -> true;
+is_killed(R, [{func_info,_,_,Ar}|_]) ->
+ case R of
+ {x,X} when X < Ar -> false;
+ _ -> true
+ end;
+is_killed(R, [{kill,R}|_]) -> true;
+is_killed(R, [{kill,_}|Is]) -> is_killed(R, Is);
+is_killed(R, [{bs_init2,_,_,_,_,_,Dst}|Is]) ->
+ if
+ R =:= Dst -> true;
+ true -> is_killed(R, Is)
+ end;
+is_killed(R, [{bs_put_string,_,_}|Is]) -> is_killed(R, Is);
+is_killed({x,R}, [{'%live',Live}|_]) when R >= Live -> true;
+is_killed({x,R}, [{'%live',_}|Is]) -> is_killed(R, Is);
+is_killed({x,R}, [{allocate,Live,_}|_]) ->
+ %% Note: To be safe here, we must return either true or false,
+ %% not looking further at the instructions beyond the allocate
+ %% instruction.
+ R >= Live;
+is_killed({x,R}, [{call,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_last,Live,_,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_only,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_ext,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_ext_last,Live,_,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [{call_ext_only,Live,_}|_]) when R >= Live -> true;
+is_killed({x,R}, [return|_]) when R > 0 -> true;
+is_killed(_, _) -> false.
+
+%% is_not_used(Register, [Instruction]) -> true|false
+%% Determine whether a register is used by the instruction sequence.
+%% If true is returned, it means that the register will not be
+%% referenced directly, but it may be referenced by an allocate
+%% instruction (meaning that it is NOT allowed to contain garbage).
+
+is_not_used(R, [{block,Blk}|Is]) ->
+ case is_not_used(R, Blk) of
+ true -> true;
+ false -> is_not_used(R, Is)
+ end;
+is_not_used({x,R}=Reg, [{allocate,Live,_}|Is]) ->
+ if
+ R >= Live -> true;
+ true -> is_not_used(Reg, Is)
+ end;
+is_not_used(R, [{set,Ds,Ss,_Op}|Is]) ->
+ case member(R, Ss) of
+ true -> false;
+ false ->
+ case member(R, Ds) of
+ true -> true;
+ false -> is_not_used(R, Is)
+ end
+ end;
+is_not_used(R, Is) -> is_killed(R, Is).
+
+%% opt_alloc(Instructions) -> Instructions'
+%% Optimises all allocate instructions.
+
+opt_alloc([{allocate,R,{_,Ns,Nh,[]}}|Is]) ->
+ [opt_alloc(Is, Ns, Nh, R)|opt(Is)];
+opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
+opt_alloc([]) -> [].
+
+%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
+%% Generates the optimal sequence of instructions for
+%% allocating and initalizing the stack frame and needed heap.
+
+opt_alloc(_Is, nostack, Nh, LivingRegs) ->
+ {allocate,LivingRegs,{nozero,nostack,Nh,[]}};
+opt_alloc(Is, Ns, Nh, LivingRegs) ->
+ InitRegs = init_yreg(Is, 0),
+ case count_ones(InitRegs) of
+ N when N*2 > Ns ->
+ {allocate,LivingRegs,{nozero,Ns,Nh,gen_init(Ns, InitRegs)}};
+ _ ->
+ {allocate,LivingRegs,{zero,Ns,Nh,[]}}
+ end.
+
+gen_init(Fs, Regs) -> gen_init(Fs, Regs, 0, []).
+
+gen_init(SameFs, _Regs, SameFs, Acc) -> reverse(Acc);
+gen_init(Fs, Regs, Y, Acc) when Regs band 1 == 0 ->
+ gen_init(Fs, Regs bsr 1, Y+1, [{init, {y,Y}}|Acc]);
+gen_init(Fs, Regs, Y, Acc) ->
+ gen_init(Fs, Regs bsr 1, Y+1, Acc).
+
+%% init_yreg(Instructions, RegSet) -> RegSetInitialized
+%% Calculate the set of initialized y registers.
+
+init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg;
+init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg));
+init_yreg(_Is, Reg) -> Reg.
+
+add_yregs(Ys, Reg) -> foldl(fun(Y, R0) -> add_yreg(Y, R0) end, Reg, Ys).
+
+add_yreg({y,Y}, Reg) -> Reg bor (1 bsl Y);
+add_yreg(_, Reg) -> Reg.
+
+count_ones(Bits) -> count_ones(Bits, 0).
+count_ones(0, Acc) -> Acc;
+count_ones(Bits, Acc) ->
+ count_ones(Bits bsr 1, Acc + (Bits band 1)).
+
+%% live_at_entry(Is) -> NumberOfRegisters
+%% Calculate the number of register live at the entry to the code
+%% sequence.
+
+live_at_entry([{block,[{allocate,R,_}|_]}|_]) ->
+ R;
+live_at_entry([{label,_}|Is]) ->
+ live_at_entry(Is);
+live_at_entry([{block,Bl}|_]) ->
+ live_at_entry(Bl);
+live_at_entry([{func_info,_,_,Ar}|_]) ->
+ Ar;
+live_at_entry(Is0) ->
+ case reverse(Is0) of
+ [{'%live',Regs}|Is] -> live_at_entry_1(Is, (1 bsl Regs)-1);
+ _ -> unknown
+ end.
+
+live_at_entry_1([{set,Ds,Ss,_}|Is], Rset0) ->
+ Rset = x_live(Ss, x_dead(Ds, Rset0)),
+ live_at_entry_1(Is, Rset);
+live_at_entry_1([{allocate,_,_}|Is], Rset) ->
+ live_at_entry_1(Is, Rset);
+live_at_entry_1([], Rset) -> live_regs_1(0, Rset).
+
+%% Calculate the new number of live registers when we move an allocate
+%% instruction upwards, passing a 'set' instruction.
+
+live_regs(Ds, Ss, Regs0) ->
+ Rset = x_live(Ss, x_dead(Ds, (1 bsl Regs0)-1)),
+ live_regs_1(0, Rset).
+
+live_regs_1(N, 0) -> N;
+live_regs_1(N, Regs) -> live_regs_1(N+1, Regs bsr 1).
+
+x_dead([{x,N}|Rs], Regs) -> x_dead(Rs, Regs band (bnot (1 bsl N)));
+x_dead([_|Rs], Regs) -> x_dead(Rs, Regs);
+x_dead([], Regs) -> Regs.
+
+x_live([{x,N}|Rs], Regs) -> x_live(Rs, Regs bor (1 bsl N));
+x_live([_|Rs], Regs) -> x_live(Rs, Regs);
+x_live([], Regs) -> Regs.
+
+%%
+%% If a floating point literal occurs more than once, move it into
+%% a free register and re-use it.
+%%
+
+share_floats([{allocate,_,_}=Alloc|Is]) ->
+ [Alloc|share_floats(Is)];
+share_floats(Is0) ->
+ All = get_floats(Is0, []),
+ MoreThanOnce0 = more_than_once(sort(All), gb_sets:empty()),
+ case gb_sets:is_empty(MoreThanOnce0) of
+ true -> Is0;
+ false ->
+ MoreThanOnce = gb_sets:to_list(MoreThanOnce0),
+ FreeX = highest_used(Is0, -1) + 1,
+ Regs0 = make_reg_map(MoreThanOnce, FreeX, []),
+ Regs = gb_trees:from_orddict(Regs0),
+ Is = map(fun({set,Ds,[{float,F}],Op}=I) ->
+ case gb_trees:lookup(F, Regs) of
+ none -> I;
+ {value,R} -> {set,Ds,[R],Op}
+ end;
+ (I) -> I
+ end, Is0),
+ [{set,[R],[{float,F}],move} || {F,R} <- Regs0] ++ Is
+ end.
+
+get_floats([{set,_,[{float,F}],_}|Is], Acc) ->
+ get_floats(Is, [F|Acc]);
+get_floats([_|Is], Acc) ->
+ get_floats(Is, Acc);
+get_floats([], Acc) -> Acc.
+
+more_than_once([F,F|Fs], Set) ->
+ more_than_once(Fs, gb_sets:add(F, Set));
+more_than_once([_|Fs], Set) ->
+ more_than_once(Fs, Set);
+more_than_once([], Set) -> Set.
+
+highest_used([{set,Ds,Ss,_}|Is], High) ->
+ highest_used(Is, highest(Ds, highest(Ss, High)));
+highest_used([{'%live',Live}|Is], High) when Live > High ->
+ highest_used(Is, Live);
+highest_used([_|Is], High) ->
+ highest_used(Is, High);
+highest_used([], High) -> High.
+
+highest([{x,R}|Rs], High) when R > High ->
+ highest(Rs, R);
+highest([_|Rs], High) ->
+ highest(Rs, High);
+highest([], High) -> High.
+
+make_reg_map([F|Fs], R, Acc) when R < ?MAXREG ->
+ make_reg_map(Fs, R+1, [{F,{x,R}}|Acc]);
+make_reg_map(_, _, Acc) -> sort(Acc).
+
+%% inverse_comp_op(Op) -> none|RevOp
+
+inverse_comp_op('=:=') -> '=/=';
+inverse_comp_op('=/=') -> '=:=';
+inverse_comp_op('==') -> '/=';
+inverse_comp_op('/=') -> '==';
+inverse_comp_op('>') -> '=<';
+inverse_comp_op('<') -> '>=';
+inverse_comp_op('>=') -> '<';
+inverse_comp_op('=<') -> '>';
+inverse_comp_op(_) -> none.
+
+%%%
+%%% Evaluation of constant bit fields.
+%%%
+
+is_bs_put({bs_put_integer,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_float,_,_,_,_,_}) -> true;
+is_bs_put(_) -> false.
+
+collect_bs_puts(Is) ->
+ collect_bs_puts_1(Is, []).
+
+collect_bs_puts_1([I|Is]=Is0, Acc) ->
+ case is_bs_put(I) of
+ false -> {reverse(Acc),Is0};
+ true -> collect_bs_puts_1(Is, [I|Acc])
+ end;
+collect_bs_puts_1([], Acc) -> {reverse(Acc),[]}.
+
+opt_bs_puts(Is) ->
+ opt_bs_1(Is, []).
+
+opt_bs_1([{bs_put_float,Fail,{integer,Sz},1,Flags0,Src}=I0|Is], Acc) ->
+ case catch eval_put_float(Src, Sz, Flags0) of
+ {'EXIT',_} ->
+ opt_bs_1(Is, [I0|Acc]);
+ <<Int:Sz>> ->
+ Flags = force_big(Flags0),
+ I = {bs_put_integer,Fail,{integer,Sz},1,Flags,{integer,Int}},
+ opt_bs_1([I|Is], Acc)
+ end;
+opt_bs_1([{bs_put_integer,_,{integer,8},1,_,{integer,_}}|_]=IsAll, Acc0) ->
+ {Is,Acc} = bs_collect_string(IsAll, Acc0),
+ opt_bs_1(Is, Acc);
+opt_bs_1([{bs_put_integer,Fail,{integer,Sz},1,F,{integer,N}}=I|Is0], Acc) when Sz > 8 ->
+ case field_endian(F) of
+ big ->
+ case bs_split_int(N, Sz, Fail, Is0) of
+ no_split -> opt_bs_1(Is0, [I|Acc]);
+ Is -> opt_bs_1(Is, Acc)
+ end;
+ little ->
+ case catch <<N:Sz/little>> of
+ {'EXIT',_} ->
+ opt_bs_1(Is0, [I|Acc]);
+ <<Int:Sz>> ->
+ Flags = force_big(F),
+ Is = [{bs_put_integer,Fail,{integer,Sz},1,
+ Flags,{integer,Int}}|Is0],
+ opt_bs_1(Is, Acc)
+ end;
+ native -> opt_bs_1(Is0, [I|Acc])
+ end;
+opt_bs_1([{Op,Fail,{integer,Sz},U,F,Src}|Is], Acc) when U > 1 ->
+ opt_bs_1([{Op,Fail,{integer,U*Sz},1,F,Src}|Is], Acc);
+opt_bs_1([I|Is], Acc) ->
+ opt_bs_1(Is, [I|Acc]);
+opt_bs_1([], Acc) -> reverse(Acc).
+
+eval_put_float(Src, Sz, Flags) ->
+ Val = value(Src),
+ case field_endian(Flags) of
+ little -> <<Val:Sz/little-float-unit:1>>;
+ big -> <<Val:Sz/big-float-unit:1>>
+ %% native intentionally not handled here - we can't optimize it.
+ end.
+
+value({integer,I}) -> I;
+value({float,F}) -> F;
+value({atom,A}) -> A.
+
+bs_collect_string(Is, [{bs_put_string,Len,{string,Str}}|Acc]) ->
+ bs_coll_str_1(Is, Len, reverse(Str), Acc);
+bs_collect_string(Is, Acc) ->
+ bs_coll_str_1(Is, 0, [], Acc).
+
+bs_coll_str_1([{bs_put_integer,_,{integer,Sz},U,_,{integer,V}}|Is],
+ Len, StrAcc, IsAcc) when U*Sz =:= 8 ->
+ Byte = V band 16#FF,
+ bs_coll_str_1(Is, Len+1, [Byte|StrAcc], IsAcc);
+bs_coll_str_1(Is, Len, StrAcc, IsAcc) ->
+ {Is,[{bs_put_string,Len,{string,reverse(StrAcc)}}|IsAcc]}.
+
+field_endian({field_flags,F}) -> field_endian_1(F).
+
+field_endian_1([big=E|_]) -> E;
+field_endian_1([little=E|_]) -> E;
+field_endian_1([native=E|_]) -> E;
+field_endian_1([_|Fs]) -> field_endian_1(Fs).
+
+force_big({field_flags,F}) ->
+ {field_flags,force_big_1(F)}.
+
+force_big_1([big|_]=Fs) -> Fs;
+force_big_1([little|Fs]) -> [big|Fs];
+force_big_1([F|Fs]) -> [F|force_big_1(Fs)].
+
+bs_split_int(0, Sz, _, _) when Sz > 64 ->
+ %% We don't want to split in this case because the
+ %% string will consist of only zeroes.
+ no_split;
+bs_split_int(N, Sz, Fail, Acc) ->
+ FirstByteSz = case Sz rem 8 of
+ 0 -> 8;
+ Rem -> Rem
+ end,
+ bs_split_int_1(N, FirstByteSz, Sz, Fail, Acc).
+
+bs_split_int_1(N, ByteSz, Sz, Fail, Acc) when Sz > 0 ->
+ Mask = (1 bsl ByteSz) - 1,
+ I = {bs_put_integer,Fail,{integer,ByteSz},1,
+ {field_flags,[big]},{integer,N band Mask}},
+ bs_split_int_1(N bsr ByteSz, 8, Sz-ByteSz, Fail, [I|Acc]);
+bs_split_int_1(_, _, _, _, Acc) -> Acc.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl
new file mode 100644
index 0000000000..3180a22433
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_bool.erl
@@ -0,0 +1,617 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_bool.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose: Optimizes booleans in guards.
+
+-module(beam_bool).
+
+-export([module/2]).
+
+-import(lists, [reverse/1,foldl/3,mapfoldl/3,sort/1,member/2]).
+-define(MAXREG, 1024).
+
+-record(st,
+ {next, %Next label number.
+ ll %Live regs at labels.
+ }).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opts) ->
+ %%io:format("~p:\n", [Mod]),
+ {Fs,_} = mapfoldl(fun(Fn, Lbl) -> function(Fn, Lbl) end, 100000000, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}, Lbl0) ->
+ %%io:format("~p/~p:\n", [Name,Arity]),
+ {Is,#st{next=Lbl}} = bool_opt(Is0, Lbl0),
+ {{function,Name,Arity,CLabel,Is},Lbl}.
+
+%%
+%% Optimize boolean expressions that use guard bifs. Rewrite to
+%% use test instructions if possible.
+%%
+
+bool_opt(Asm, Lbl) ->
+ LiveInfo = index_instructions(Asm),
+ bopt(Asm, [], #st{next=Lbl,ll=LiveInfo}).
+
+bopt([{block,Bl0}=Block|
+ [{jump,{f,Succ}},
+ {label,Fail},
+ {block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
+ {label,Succ}|Is]=Is0], Acc0, St) ->
+ case split_block(Bl0, Dst, Fail) of
+ failed ->
+ bopt(Is0, [Block|Acc0], St);
+ {Bl,PreBlock} ->
+ Acc1 = case PreBlock of
+ [] -> Acc0;
+ _ -> [{block,PreBlock}|Acc0]
+ end,
+ Acc = [{protected,[Dst],Bl,{Fail,Succ,Live}}|Acc1],
+ bopt(Is, Acc, St)
+ end;
+bopt([{test,is_eq_exact,{f,Fail},[Reg,{atom,true}]}=I|Is], [{block,_}|_]=Acc0, St0) ->
+ case bopt_block(Reg, Fail, Is, Acc0, St0) of
+ failed -> bopt(Is, [I|Acc0], St0);
+ {Acc,St} -> bopt(Is, Acc, St)
+ end;
+bopt([I|Is], Acc, St) ->
+ bopt(Is, [I|Acc], St);
+bopt([], Acc, St) ->
+ {bopt_reverse(Acc, []),St}.
+
+bopt_reverse([{protected,[Dst],Block,{Fail,Succ,Live}}|Is], Acc0) ->
+ Acc = [{block,Block},{jump,{f,Succ}},
+ {label,Fail},
+ {block,[{set,[Dst],[{atom,false}],move},{'%live',Live}]},
+ {label,Succ}|Acc0],
+ bopt_reverse(Is, Acc);
+bopt_reverse([I|Is], Acc) ->
+ bopt_reverse(Is, [I|Acc]);
+bopt_reverse([], Acc) -> Acc.
+
+%% bopt_block(Reg, Fail, OldIs, Accumulator, St) -> failed | {NewAcc,St}
+%% Attempt to optimized a block of guard BIFs followed by a test
+%% instruction.
+bopt_block(Reg, Fail, OldIs, [{block,Bl0}|Acc0], St0) ->
+ case split_block(Bl0, Reg, Fail) of
+ failed ->
+ %% Reason for failure: The block either contained no
+ %% guard BIFs with the failure label Fail, or the final
+ %% instruction in the block did not assign the Reg register.
+
+ %%io:format("split ~p: ~P\n", [Reg,Bl0,20]),
+ failed;
+ {Bl1,BlPre} ->
+ %% The block has been splitted. Bl1 is a non-empty list
+ %% of guard BIF instructions having the failure label Fail.
+ %% BlPre is a (possibly empty list) of instructions preceeding
+ %% Bl1.
+ Acc1 = make_block(BlPre, Acc0),
+ {Bl,Acc} = extend_block(Bl1, Fail, Acc1),
+ case catch bopt_block_1(Bl, Fail, St0) of
+ {'EXIT',_Reason} ->
+ %% Optimization failed for one of the following reasons:
+ %%
+ %% 1. Not possible to rewrite because a boolean value is
+ %% passed to another guard bif, e.g. 'abs(A > B)'
+ %% (in this case, obviously nonsense code). Rare in
+ %% practice.
+ %%
+ %% 2. Not possible to rewrite because we have not seen
+ %% the complete boolan expression (it is spread out
+ %% over several blocks with jumps and labels).
+ %% The 'or' and 'and' instructions need to that fully
+ %% known operands in order to be eliminated.
+ %%
+ %% 3. Other bug or limitation.
+
+ %%io:format("~P\n", [_Reason,20]),
+ failed;
+ {NewCode,St} ->
+ case is_opt_safe(Bl, NewCode, OldIs, St) of
+ false ->
+ %% The optimization is not safe. (A register
+ %% used by the instructions following the
+ %% optimized code is either not assigned a
+ %% value at all or assigned a different value.)
+
+ %%io:format("\nNot safe:\n"),
+ %%io:format("~p\n", [Bl]),
+ %%io:format("~p\n", [reverse(NewCode)]),
+ failed;
+ true -> {NewCode++Acc,St}
+ end
+ end
+ end.
+
+bopt_block_1(Block, Fail, St) ->
+ {Pre0,[{_,Tree}]} = bopt_tree(Block),
+ Pre = update_fail_label(Pre0, Fail, []),
+ bopt_cg(Tree, Fail, make_block(Pre, []), St).
+
+%% is_opt_safe(OriginalCode, OptCode, FollowingCode, State) -> true|false
+%% Comparing the original code to the optimized code, determine
+%% whether the optimized code is guaranteed to work in the same
+%% way as the original code.
+
+is_opt_safe(Bl, NewCode, OldIs, St) ->
+ %% Here are the conditions that must be true for the
+ %% optimization to be safe.
+ %%
+ %% 1. Any register that was assigned a value in the original
+ %% code, but is not in the optimized code, must be guaranteed
+ %% to be KILLED in the following code. (NotSet below.)
+ %%
+ %% 2. Any register that is assigned a value in the optimized
+ %% code must be UNUSED in the following code. (NewDst, Set.)
+ %% (Possible future improvement: Registers that are known
+ %% to be assigned the SAME value in the original and optimized
+ %% code don't need to be unused in the following code.)
+
+ PrevDst = dst_regs(Bl),
+ NewDst = dst_regs(NewCode),
+ NotSet = ordsets:subtract(PrevDst, NewDst),
+
+ %% Note: The following line is an optimization. We don't need
+ %% to test whether variables in NotSet for being unused, because
+ %% they will all be tested for being killed (a stronger condition
+ %% than being unused).
+
+ Set = ordsets:subtract(NewDst, NotSet),
+
+ all_killed(NotSet, OldIs, St) andalso
+ none_used(Set, OldIs, St).
+
+% update_fail_label([{set,_,_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
+% update_fail_label(Is, Fail, [I|Acc]);
+update_fail_label([{set,Ds,As,{bif,N,{f,_}}}|Is], Fail, Acc) ->
+ update_fail_label(Is, Fail, [{set,Ds,As,{bif,N,{f,Fail}}}|Acc]);
+update_fail_label([], _, Acc) -> Acc.
+
+make_block([], Acc) -> Acc;
+make_block(Bl, Acc) -> [{block,Bl}|Acc].
+
+extend_block(BlAcc, Fail, [{protected,_,_,_}=Prot|OldAcc]) ->
+ extend_block([Prot|BlAcc], Fail, OldAcc);
+extend_block(BlAcc0, Fail, [{block,Is0}|OldAcc]=OldAcc0) ->
+ case extend_block_1(reverse(Is0), Fail, BlAcc0) of
+ {[],_} -> {BlAcc0,OldAcc0};
+ {BlAcc,[]} -> extend_block(BlAcc, Fail, OldAcc);
+ {BlAcc,Is} -> {BlAcc,[{block,Is}|OldAcc]}
+ end;
+extend_block(BlAcc, _, OldAcc) -> {BlAcc,OldAcc}.
+
+extend_block_1([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
+ extend_block_1(Is, Fail, [I|Acc]);
+extend_block_1([{set,[_],As,{bif,Bif,_}}=I|Is]=Is0, Fail, Acc) ->
+ case safe_bool_op(Bif, length(As)) of
+ false -> {Acc,reverse(Is0)};
+ true -> extend_block_1(Is, Fail, [I|Acc])
+ end;
+extend_block_1([_|_]=Is, _, Acc) -> {Acc,reverse(Is)};
+extend_block_1([], _, Acc) -> {Acc,[]}.
+
+split_block(Is0, Dst, Fail) ->
+ case reverse(Is0) of
+ [{'%live',_}|[{set,[Dst],_,_}|_]=Is] ->
+ split_block_1(Is, Fail);
+ [{set,[Dst],_,_}|_]=Is ->
+ split_block_1(Is, Fail);
+ _ -> failed
+ end.
+
+split_block_1(Is, Fail) ->
+ case split_block_2(Is, Fail, []) of
+ {[],_} -> failed;
+ {_,_}=Res -> Res
+ end.
+
+% split_block_2([{set,[_],_,{bif,_,{f,0}}}=I|Is], Fail, Acc) ->
+% split_block_2(Is, Fail, [I|Acc]);
+split_block_2([{set,[_],_,{bif,_,{f,Fail}}}=I|Is], Fail, Acc) ->
+ split_block_2(Is, Fail, [I|Acc]);
+split_block_2([{'%live',_}|Is], Fail, Acc) ->
+ split_block_2(Is, Fail, Acc);
+split_block_2(Is, _, Acc) -> {Acc,reverse(Is)}.
+
+dst_regs(Is) ->
+ dst_regs(Is, []).
+
+dst_regs([{block,Bl}|Is], Acc) ->
+ dst_regs(Bl, dst_regs(Is, Acc));
+dst_regs([{set,[D],_,{bif,_,{f,_}}}|Is], Acc) ->
+ dst_regs(Is, [D|Acc]);
+dst_regs([_|Is], Acc) ->
+ dst_regs(Is, Acc);
+dst_regs([], Acc) -> ordsets:from_list(Acc).
+
+all_killed([R|Rs], OldIs, St) ->
+ case is_killed(R, OldIs, St) of
+ false -> false;
+ true -> all_killed(Rs, OldIs, St)
+ end;
+all_killed([], _, _) -> true.
+
+none_used([R|Rs], OldIs, St) ->
+ case is_not_used(R, OldIs, St) of
+ false -> false;
+ true -> none_used(Rs, OldIs, St)
+ end;
+none_used([], _, _) -> true.
+
+bopt_tree(Block0) ->
+ Block = ssa_block(Block0),
+ Reg = free_variables(Block),
+ %%io:format("~p\n", [Block]),
+ %%io:format("~p\n", [Reg]),
+ Res = bopt_tree_1(Block, Reg, []),
+ %%io:format("~p\n", [Res]),
+ Res.
+
+bopt_tree_1([{set,[Dst],As0,{bif,'not',_}}|Is], Forest0, Pre) ->
+ {[Arg],Forest1} = bopt_bool_args(As0, Forest0),
+ Forest = gb_trees:enter(Dst, {'not',Arg}, Forest1),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{set,[Dst],As0,{bif,'and',_}}|Is], Forest0, Pre) ->
+ {As,Forest1} = bopt_bool_args(As0, Forest0),
+ AndList = make_and_list(As),
+ Forest = gb_trees:enter(Dst, {'and',AndList}, Forest1),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{set,[Dst],[L0,R0],{bif,'or',_}}|Is], Forest0, Pre) ->
+ L = gb_trees:get(L0, Forest0),
+ R = gb_trees:get(R0, Forest0),
+ Forest1 = gb_trees:delete(L0, gb_trees:delete(R0, Forest0)),
+ OrList = make_or_list([L,R]),
+ Forest = gb_trees:enter(Dst, {'or',OrList}, Forest1),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{protected,[Dst],_,_}=Prot|Is], Forest0, Pre) ->
+ Forest = gb_trees:enter(Dst, Prot, Forest0),
+ bopt_tree_1(Is, Forest, Pre);
+bopt_tree_1([{set,[Dst],As,{bif,N,_}}=Bif|Is], Forest0, Pre) ->
+ Ar = length(As),
+ case safe_bool_op(N, Ar) of
+ false ->
+ bopt_good_args(As, Forest0),
+ Forest = gb_trees:enter(Dst, any, Forest0),
+ bopt_tree_1(Is, Forest, [Bif|Pre]);
+ true ->
+ bopt_good_args(As, Forest0),
+ Test = bif_to_test(Dst, N, As),
+ Forest = gb_trees:enter(Dst, Test, Forest0),
+ bopt_tree_1(Is, Forest, Pre)
+ end;
+bopt_tree_1([], Forest, Pre) ->
+ {Pre,[R || {_,V}=R <- gb_trees:to_list(Forest), V =/= any]}.
+
+safe_bool_op(internal_is_record, 3) -> true;
+safe_bool_op(N, Ar) ->
+ erl_internal:new_type_test(N, Ar) orelse erl_internal:comp_op(N, Ar).
+
+bopt_bool_args(As, Forest) ->
+ mapfoldl(fun bopt_bool_arg/2, Forest, As).
+
+bopt_bool_arg({T,_}=R, Forest) when T == x; T == y ->
+ {gb_trees:get(R, Forest),gb_trees:delete(R, Forest)};
+bopt_bool_arg(Term, Forest) ->
+ {Term,Forest}.
+
+bopt_good_args([A|As], Regs) ->
+ bopt_good_arg(A, Regs),
+ bopt_good_args(As, Regs);
+bopt_good_args([], _) -> ok.
+
+bopt_good_arg({x,_}=X, Regs) ->
+ case gb_trees:get(X, Regs) of
+ any -> ok;
+ _Other ->
+ %%io:format("not any: ~p: ~p\n", [X,_Other]),
+ exit(bad_contents)
+ end;
+bopt_good_arg(_, _) -> ok.
+
+bif_to_test(_, N, As) ->
+ bif_to_test(N, As).
+
+bif_to_test(internal_is_record, [_,_,_]=As) ->
+ {test,internal_is_record,fail,As};
+bif_to_test('=:=', As) -> {test,is_eq_exact,fail,As};
+bif_to_test('=/=', As) -> {test,is_ne_exact,fail,As};
+bif_to_test('==', As) -> {test,is_eq,fail,As};
+bif_to_test('/=', As) -> {test,is_ne,fail,As};
+bif_to_test('=<', [L,R]) -> {test,is_ge,fail,[R,L]};
+bif_to_test('>=', As) -> {test,is_ge,fail,As};
+bif_to_test('>', [L,R]) -> {test,is_lt,fail,[R,L]};
+bif_to_test('<', As) -> {test,is_lt,fail,As};
+bif_to_test(Name, [_]=As) ->
+ case erl_internal:new_type_test(Name, 1) of
+ false -> exit({bif_to_test,Name,As,failed});
+ true -> {test,Name,fail,As}
+ end.
+
+make_and_list([{'and',As}|Is]) ->
+ make_and_list(As++Is);
+make_and_list([I|Is]) ->
+ [I|make_and_list(Is)];
+make_and_list([]) -> [].
+
+make_or_list([{'or',As}|Is]) ->
+ make_or_list(As++Is);
+make_or_list([I|Is]) ->
+ [I|make_or_list(Is)];
+make_or_list([]) -> [].
+
+%% Code generation for a boolean tree.
+
+bopt_cg({'not',Arg}, Fail, Acc, St) ->
+ I = bopt_cg_not(Arg),
+ bopt_cg(I, Fail, Acc, St);
+bopt_cg({'and',As}, Fail, Acc, St) ->
+ bopt_cg_and(As, Fail, Acc, St);
+bopt_cg({'or',As}, Fail, Acc, St0) ->
+ {Succ,St} = new_label(St0),
+ bopt_cg_or(As, Succ, Fail, Acc, St);
+bopt_cg({test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
+ {[{test,is_eq_exact,{f,Fail},[Tmp,RecordTag]},
+ {get_tuple_element,Tuple,0,Tmp}|Acc],St};
+bopt_cg({inverted_test,is_tuple_element,fail,[Tmp,Tuple,RecordTag]}, Fail, Acc, St) ->
+ {[{test,is_ne_exact,{f,Fail},[Tmp,RecordTag]},
+ {get_tuple_element,Tuple,0,Tmp}|Acc],St};
+bopt_cg({test,N,fail,As}, Fail, Acc, St) ->
+ Test = {test,N,{f,Fail},As},
+ {[Test|Acc],St};
+bopt_cg({inverted_test,N,fail,As}, Fail, Acc, St0) ->
+ {Lbl,St} = new_label(St0),
+ {[{label,Lbl},{jump,{f,Fail}},{test,N,{f,Lbl},As}|Acc],St};
+bopt_cg({protected,_,Bl0,{_,_,_}}, Fail, Acc, St0) ->
+ {Bl,St} = bopt_block_1(Bl0, Fail, St0),
+ {Bl++Acc,St};
+bopt_cg([_|_]=And, Fail, Acc, St) ->
+ bopt_cg_and(And, Fail, Acc, St).
+
+bopt_cg_not({'and',As0}) ->
+ As = [bopt_cg_not(A) || A <- As0],
+ {'or',As};
+bopt_cg_not({'or',As0}) ->
+ As = [bopt_cg_not(A) || A <- As0],
+ {'and',As};
+bopt_cg_not({test,Test,Fail,As}) ->
+ {inverted_test,Test,Fail,As}.
+
+bopt_cg_and([{atom,false}|_], Fail, _, St) ->
+ {[{jump,{f,Fail}}],St};
+bopt_cg_and([{atom,true}|Is], Fail, Acc, St) ->
+ bopt_cg_and(Is, Fail, Acc, St);
+bopt_cg_and([I|Is], Fail, Acc0, St0) ->
+ {Acc,St} = bopt_cg(I, Fail, Acc0, St0),
+ bopt_cg_and(Is, Fail, Acc, St);
+bopt_cg_and([], _, Acc, St) -> {Acc,St}.
+
+bopt_cg_or([I], Succ, Fail, Acc0, St0) ->
+ {Acc,St} = bopt_cg(I, Fail, Acc0, St0),
+ {[{label,Succ}|Acc],St};
+bopt_cg_or([I|Is], Succ, Fail, Acc0, St0) ->
+ {Lbl,St1} = new_label(St0),
+ {Acc,St} = bopt_cg(I, Lbl, Acc0, St1),
+ bopt_cg_or(Is, Succ, Fail, [{label,Lbl},{jump,{f,Succ}}|Acc], St).
+
+new_label(#st{next=LabelNum}=St) when is_integer(LabelNum) ->
+ {LabelNum,St#st{next=LabelNum+1}}.
+
+free_variables(Is) ->
+ E = gb_sets:empty(),
+ free_vars_1(Is, E, E).
+
+free_vars_1([{set,[Dst],As,{bif,_,_}}|Is], F0, N0) ->
+ F = gb_sets:union(F0, gb_sets:difference(var_list(As), N0)),
+ N = gb_sets:union(N0, var_list([Dst])),
+ free_vars_1(Is, F, N);
+free_vars_1([{protected,_,Pa,_}|Is], F, N) ->
+ free_vars_1(Pa++Is, F, N);
+free_vars_1([], F, _) ->
+ gb_trees:from_orddict([{K,any} || K <- gb_sets:to_list(F)]).
+
+var_list(Is) ->
+ var_list_1(Is, gb_sets:empty()).
+
+var_list_1([{x,_}=X|Is], D) ->
+ var_list_1(Is, gb_sets:add(X, D));
+var_list_1([_|Is], D) ->
+ var_list_1(Is, D);
+var_list_1([], D) -> D.
+
+%%%
+%%% Convert a block to Static Single Assignment (SSA) form.
+%%%
+
+-record(ssa,
+ {live,
+ sub}).
+
+ssa_block(Is0) ->
+ Next = ssa_first_free(Is0, 0),
+ {Is,_} = ssa_block_1(Is0, #ssa{live=Next,sub=gb_trees:empty()}, []),
+ Is.
+
+ssa_block_1([{protected,[_],Pa0,Pb}|Is], Sub0, Acc) ->
+ {Pa,Sub} = ssa_block_1(Pa0, Sub0, []),
+ Dst = ssa_last_target(Pa),
+ ssa_block_1(Is, Sub, [{protected,[Dst],Pa,Pb}|Acc]);
+ssa_block_1([{set,[Dst],As,Bif}|Is], Sub0, Acc0) ->
+ Sub1 = ssa_in_use_list(As, Sub0),
+ Sub = ssa_assign(Dst, Sub1),
+ Acc = [{set,[ssa_sub(Dst, Sub)],ssa_sub_list(As, Sub0),Bif}|Acc0],
+ ssa_block_1(Is, Sub, Acc);
+ssa_block_1([], Sub, Acc) -> {reverse(Acc),Sub}.
+
+ssa_in_use_list(As, Sub) ->
+ foldl(fun ssa_in_use/2, Sub, As).
+
+ssa_in_use({x,_}=R, #ssa{sub=Sub0}=Ssa) ->
+ case gb_trees:is_defined(R, Sub0) of
+ true -> Ssa;
+ false ->
+ Sub = gb_trees:insert(R, R, Sub0),
+ Ssa#ssa{sub=Sub}
+ end;
+ssa_in_use(_, Ssa) -> Ssa.
+
+ssa_assign({x,_}=R, #ssa{sub=Sub0}=Ssa0) ->
+ case gb_trees:is_defined(R, Sub0) of
+ false ->
+ Sub = gb_trees:insert(R, R, Sub0),
+ Ssa0#ssa{sub=Sub};
+ true ->
+ {NewReg,Ssa} = ssa_new_reg(Ssa0),
+ Sub1 = gb_trees:update(R, NewReg, Sub0),
+ Sub = gb_trees:insert(NewReg, NewReg, Sub1),
+ Ssa#ssa{sub=Sub}
+ end;
+ssa_assign(_, Ssa) -> Ssa.
+
+ssa_sub_list(List, Sub) ->
+ [ssa_sub(E, Sub) || E <- List].
+
+ssa_sub(R0, #ssa{sub=Sub}) ->
+ case gb_trees:lookup(R0, Sub) of
+ none -> R0;
+ {value,R} -> R
+ end.
+
+ssa_new_reg(#ssa{live=Reg}=Ssa) ->
+ {{x,Reg},Ssa#ssa{live=Reg+1}}.
+
+ssa_first_free([{protected,Ds,_,_}|Is], Next0) ->
+ Next = ssa_first_free_list(Ds, Next0),
+ ssa_first_free(Is, Next);
+ssa_first_free([{set,[Dst],As,_}|Is], Next0) ->
+ Next = ssa_first_free_list([Dst|As], Next0),
+ ssa_first_free(Is, Next);
+ssa_first_free([], Next) -> Next.
+
+ssa_first_free_list(Regs, Next) ->
+ foldl(fun({x,R}, N) when R >= N -> R+1;
+ (_, N) -> N end, Next, Regs).
+
+ssa_last_target([{set,[Dst],_,_},{'%live',_}]) -> Dst;
+ssa_last_target([{set,[Dst],_,_}]) -> Dst;
+ssa_last_target([_|Is]) -> ssa_last_target(Is).
+
+%% index_instructions(FunctionIs) -> GbTree([{Label,Is}])
+%% Index the instruction sequence so that we can quickly
+%% look up the instruction following a specific label.
+
+index_instructions(Is) ->
+ ii_1(Is, []).
+
+ii_1([{label,Lbl}|Is0], Acc) ->
+ Is = lists:dropwhile(fun({label,_}) -> true;
+ (_) -> false end, Is0),
+ ii_1(Is0, [{Lbl,Is}|Acc]);
+ii_1([_|Is], Acc) ->
+ ii_1(Is, Acc);
+ii_1([], Acc) -> gb_trees:from_orddict(sort(Acc)).
+
+%% is_killed(Register, [Instruction], State) -> true|false
+%% Determine whether a register is killed in the instruction sequence.
+%% The state is used to allow us to determine the kill state
+%% across branches.
+
+is_killed(R, Is, St) ->
+ case is_killed_1(R, Is, St) of
+ false ->
+ %%io:format("nk ~p: ~P\n", [R,Is,15]),
+ false;
+ true -> true
+ end.
+
+is_killed_1(R, [{block,Blk}|Is], St) ->
+ case is_killed_1(R, Blk, St) of
+ true -> true;
+ false -> is_killed_1(R, Is, St)
+ end;
+is_killed_1(R, [{test,_,{f,Fail},As}|Is], St) ->
+ case not member(R, As) andalso is_reg_killed_at(R, Fail, St) of
+ false -> false;
+ true -> is_killed_1(R, Is, St)
+ end;
+is_killed_1(R, [{select_val,R,_,_}|_], _) -> false;
+is_killed_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
+ is_killed_at_all(R, [Fail|Branches], St);
+is_killed_1(R, [{jump,{f,F}}|_], St) ->
+ is_reg_killed_at(R, F, St);
+is_killed_1(Reg, Is, _) ->
+ beam_block:is_killed(Reg, Is).
+
+is_reg_killed_at(R, Lbl, #st{ll=Ll}=St) ->
+ Is = gb_trees:get(Lbl, Ll),
+ is_killed_1(R, Is, St).
+
+is_killed_at_all(R, [{f,Lbl}|T], St) ->
+ case is_reg_killed_at(R, Lbl, St) of
+ false -> false;
+ true -> is_killed_at_all(R, T, St)
+ end;
+is_killed_at_all(R, [_|T], St) ->
+ is_killed_at_all(R, T, St);
+is_killed_at_all(_, [], _) -> true.
+
+%% is_not_used(Register, [Instruction], State) -> true|false
+%% Determine whether a register is never used in the instruction sequence
+%% (it could still referenced by an allocate instruction, meaning that
+%% it MUST be initialized).
+%% The state is used to allow us to determine the usage state
+%% across branches.
+
+is_not_used(R, Is, St) ->
+ case is_not_used_1(R, Is, St) of
+ false ->
+ %%io:format("used ~p: ~P\n", [R,Is,15]),
+ false;
+ true -> true
+ end.
+
+is_not_used_1(R, [{block,Blk}|Is], St) ->
+ case is_not_used_1(R, Blk, St) of
+ true -> true;
+ false -> is_not_used_1(R, Is, St)
+ end;
+is_not_used_1(R, [{test,_,{f,Fail},As}|Is], St) ->
+ case not member(R, As) andalso is_reg_not_used_at(R, Fail, St) of
+ false -> false;
+ true -> is_not_used_1(R, Is, St)
+ end;
+is_not_used_1(R, [{select_val,R,_,_}|_], _) -> false;
+is_not_used_1(R, [{select_val,_,Fail,{list,Branches}}|_], St) ->
+ is_used_at_none(R, [Fail|Branches], St);
+is_not_used_1(R, [{jump,{f,F}}|_], St) ->
+ is_reg_not_used_at(R, F, St);
+is_not_used_1(Reg, Is, _) ->
+ beam_block:is_not_used(Reg, Is).
+
+is_reg_not_used_at(R, Lbl, #st{ll=Ll}=St) ->
+ Is = gb_trees:get(Lbl, Ll),
+ is_not_used_1(R, Is, St).
+
+is_used_at_none(R, [{f,Lbl}|T], St) ->
+ case is_reg_not_used_at(R, Lbl, St) of
+ false -> false;
+ true -> is_used_at_none(R, T, St)
+ end;
+is_used_at_none(R, [_|T], St) ->
+ is_used_at_none(R, T, St);
+is_used_at_none(_, [], _) -> true.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl
new file mode 100644
index 0000000000..d47ae9c896
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_clean.erl
@@ -0,0 +1,232 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_clean.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Clean up, such as removing unused labels and unused functions.
+
+-module(beam_clean).
+
+-export([module/2]).
+-import(lists, [member/2,map/2,foldl/3,mapfoldl/3,reverse/1]).
+
+module({Mod,Exp,Attr,Fs0,_}, _Opt) ->
+ Order = [Lbl || {function,_,_,Lbl,_} <- Fs0],
+ All = foldl(fun({function,_,_,Lbl,_}=Func,D) -> dict:store(Lbl, Func, D) end,
+ dict:new(), Fs0),
+ {WorkList,Used0} = exp_to_labels(Fs0, Exp),
+ Used = find_all_used(WorkList, All, Used0),
+ Fs1 = remove_unused(Order, Used, All),
+ {Fs,Lc} = clean_labels(Fs1),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+%% Convert the export list ({Name,Arity} pairs) to a list of entry labels.
+
+exp_to_labels(Fs, Exp) -> exp_to_labels(Fs, Exp, [], sets:new()).
+
+exp_to_labels([{function,Name,Arity,Lbl,_}|Fs], Exp, Acc, Used) ->
+ case member({Name,Arity}, Exp) of
+ true -> exp_to_labels(Fs, Exp, [Lbl|Acc], sets:add_element(Lbl, Used));
+ false -> exp_to_labels(Fs, Exp, Acc, Used)
+ end;
+exp_to_labels([], _, Acc, Used) -> {Acc,Used}.
+
+%% Remove the unused functions.
+
+remove_unused([F|Fs], Used, All) ->
+ case sets:is_element(F, Used) of
+ false -> remove_unused(Fs, Used, All);
+ true -> [dict:fetch(F, All)|remove_unused(Fs, Used, All)]
+ end;
+remove_unused([], _, _) -> [].
+
+%% Find all used functions.
+
+find_all_used([F|Fs0], All, Used0) ->
+ {function,_,_,_,Code} = dict:fetch(F, All),
+ {Fs,Used} = update_work_list(Code, {Fs0,Used0}),
+ find_all_used(Fs, All, Used);
+find_all_used([], _All, Used) -> Used.
+
+update_work_list([{call,_,{f,L}}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{call_last,_,{f,L},_}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{call_only,_,{f,L}}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{make_fun,{f,L},_,_}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([{make_fun2,{f,L},_,_,_}|Is], Sets) ->
+ update_work_list(Is, add_to_work_list(L, Sets));
+update_work_list([_|Is], Sets) ->
+ update_work_list(Is, Sets);
+update_work_list([], Sets) -> Sets.
+
+add_to_work_list(F, {Fs,Used}=Sets) ->
+ case sets:is_element(F, Used) of
+ true -> Sets;
+ false -> {[F|Fs],sets:add_element(F, Used)}
+ end.
+
+
+%%%
+%%% Coalesce adjacent labels. Renumber all labels to eliminate gaps.
+%%% This cleanup will slightly reduce file size and slightly speed up loading.
+%%%
+%%% We also expand internal_is_record/3 to a sequence of instructions. It is done
+%%% here merely because this module will always be called even if optimization
+%%% is turned off. We don't want to do the expansion in beam_asm because we
+%%% want to see the expanded code in a .S file.
+%%%
+
+-record(st, {lmap, %Translation tables for labels.
+ entry, %Number of entry label.
+ lc %Label counter
+ }).
+
+clean_labels(Fs0) ->
+ St0 = #st{lmap=dict:new(),lc=1},
+ {Fs1,#st{lmap=Lmap,lc=Lc}} = mapfoldl(fun function_renumber/2, St0, Fs0),
+ {map(fun(F) -> function_replace(F, Lmap) end, Fs1),Lc}.
+
+function_renumber({function,Name,Arity,_Entry,Asm0}, St0) ->
+ {Asm,St} = renumber_labels(Asm0, [], St0),
+ {{function,Name,Arity,St#st.entry,Asm},St}.
+
+renumber_labels([{bif,internal_is_record,{f,_},
+ [Term,Tag,{integer,Arity}],Dst}|Is], Acc, St) ->
+ ContLabel = 900000000+2*St#st.lc,
+ FailLabel = ContLabel+1,
+ Fail = {f,FailLabel},
+ Tmp = Dst,
+ renumber_labels([{test,is_tuple,Fail,[Term]},
+ {test,test_arity,Fail,[Term,Arity]},
+ {get_tuple_element,Term,0,Tmp},
+ {test,is_eq_exact,Fail,[Tmp,Tag]},
+ {move,{atom,true},Dst},
+ {jump,{f,ContLabel}},
+ {label,FailLabel},
+ {move,{atom,false},Dst},
+ {label,ContLabel}|Is], Acc, St);
+renumber_labels([{test,internal_is_record,{f,_}=Fail,
+ [Term,Tag,{integer,Arity}]}|Is], Acc, St) ->
+ Tmp = {x,1023},
+ case Term of
+ {Reg,_} when Reg == x; Reg == y ->
+ renumber_labels([{test,is_tuple,Fail,[Term]},
+ {test,test_arity,Fail,[Term,Arity]},
+ {get_tuple_element,Term,0,Tmp},
+ {test,is_eq_exact,Fail,[Tmp,Tag]}|Is], Acc, St);
+ _ ->
+ renumber_labels([{jump,Fail}|Is], Acc, St)
+ end;
+renumber_labels([{label,Old}|Is], [{label,New}|_]=Acc, #st{lmap=D0}=St) ->
+ D = dict:store(Old, New, D0),
+ renumber_labels(Is, Acc, St#st{lmap=D});
+renumber_labels([{label,Old}|Is], Acc, St0) ->
+ New = St0#st.lc,
+ D = dict:store(Old, New, St0#st.lmap),
+ renumber_labels(Is, [{label,New}|Acc], St0#st{lmap=D,lc=New+1});
+renumber_labels([{func_info,_,_,_}=Fi|Is], Acc, St0) ->
+ renumber_labels(Is, [Fi|Acc], St0#st{entry=St0#st.lc});
+renumber_labels([I|Is], Acc, St0) ->
+ renumber_labels(Is, [I|Acc], St0);
+renumber_labels([], Acc, St0) -> {Acc,St0}.
+
+function_replace({function,Name,Arity,Entry,Asm0}, Dict) ->
+ Asm = case catch replace(Asm0, [], Dict) of
+ {'EXIT',_}=Reason ->
+ exit(Reason);
+ {error,{undefined_label,Lbl}=Reason} ->
+ io:format("Function ~s/~w refers to undefined label ~w\n",
+ [Name,Arity,Lbl]),
+ exit(Reason);
+ Asm1 when list(Asm1) -> Asm1
+ end,
+ {function,Name,Arity,Entry,Asm}.
+
+replace([{test,Test,{f,Lbl},Ops}|Is], Acc, D) ->
+ replace(Is, [{test,Test,{f,label(Lbl, D)},Ops}|Acc], D);
+replace([{select_val,R,{f,Fail0},{list,Vls0}}|Is], Acc, D) ->
+ Vls1 = map(fun ({f,L}) -> {f,label(L, D)};
+ (Other) -> Other end, Vls0),
+ Fail = label(Fail0, D),
+ case redundant_values(Vls1, Fail, []) of
+ [] ->
+ %% Oops, no choices left. The loader will not accept that.
+ %% Convert to a plain jump.
+ replace(Is, [{jump,{f,Fail}}|Acc], D);
+ Vls ->
+ replace(Is, [{select_val,R,{f,Fail},{list,Vls}}|Acc], D)
+ end;
+replace([{select_tuple_arity,R,{f,Fail},{list,Vls0}}|Is], Acc, D) ->
+ Vls = map(fun ({f,L}) -> {f,label(L, D)};
+ (Other) -> Other end, Vls0),
+ replace(Is, [{select_tuple_arity,R,{f,label(Fail, D)},{list,Vls}}|Acc], D);
+replace([{'try',R,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{'try',R,{f,label(Lbl, D)}}|Acc], D);
+replace([{'catch',R,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{'catch',R,{f,label(Lbl, D)}}|Acc], D);
+replace([{jump,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{jump,{f,label(Lbl, D)}}|Acc], D);
+replace([{loop_rec,{f,Lbl},R}|Is], Acc, D) ->
+ replace(Is, [{loop_rec,{f,label(Lbl, D)},R}|Acc], D);
+replace([{loop_rec_end,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{loop_rec_end,{f,label(Lbl, D)}}|Acc], D);
+replace([{wait,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{wait,{f,label(Lbl, D)}}|Acc], D);
+replace([{wait_timeout,{f,Lbl},To}|Is], Acc, D) ->
+ replace(Is, [{wait_timeout,{f,label(Lbl, D)},To}|Acc], D);
+replace([{bif,Name,{f,Lbl},As,R}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bif,Name,{f,label(Lbl, D)},As,R}|Acc], D);
+replace([{call,Ar,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{call,Ar,{f,label(Lbl,D)}}|Acc], D);
+replace([{call_last,Ar,{f,Lbl},N}|Is], Acc, D) ->
+ replace(Is, [{call_last,Ar,{f,label(Lbl,D)},N}|Acc], D);
+replace([{call_only,Ar,{f,Lbl}}|Is], Acc, D) ->
+ replace(Is, [{call_only,Ar,{f,label(Lbl, D)}}|Acc], D);
+replace([{make_fun,{f,Lbl},U1,U2}|Is], Acc, D) ->
+ replace(Is, [{make_fun,{f,label(Lbl, D)},U1,U2}|Acc], D);
+replace([{make_fun2,{f,Lbl},U1,U2,U3}|Is], Acc, D) ->
+ replace(Is, [{make_fun2,{f,label(Lbl, D)},U1,U2,U3}|Acc], D);
+replace([{bs_init2,{f,Lbl},Sz,Words,R,F,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_init2,{f,label(Lbl, D)},Sz,Words,R,F,Dst}|Acc], D);
+replace([{bs_put_integer,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_integer,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_put_binary,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_binary,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_put_float,{f,Lbl},Bits,Unit,Fl,Val}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_put_float,{f,label(Lbl, D)},Bits,Unit,Fl,Val}|Acc], D);
+replace([{bs_final,{f,Lbl},R}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_final,{f,label(Lbl, D)},R}|Acc], D);
+replace([{bs_add,{f,Lbl},Src,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_add,{f,label(Lbl, D)},Src,Dst}|Acc], D);
+replace([{bs_bits_to_bytes,{f,Lbl},Bits,Dst}|Is], Acc, D) when Lbl =/= 0 ->
+ replace(Is, [{bs_bits_to_bytes,{f,label(Lbl, D)},Bits,Dst}|Acc], D);
+replace([I|Is], Acc, D) ->
+ replace(Is, [I|Acc], D);
+replace([], Acc, _) -> Acc.
+
+label(Old, D) ->
+ case dict:find(Old, D) of
+ {ok,Val} -> Val;
+ error -> throw({error,{undefined_label,Old}})
+ end.
+
+redundant_values([_,{f,Fail}|Vls], Fail, Acc) ->
+ redundant_values(Vls, Fail, Acc);
+redundant_values([Val,Lbl|Vls], Fail, Acc) ->
+ redundant_values(Vls, Fail, [Lbl,Val|Acc]);
+redundant_values([], _, Acc) -> reverse(Acc).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl
new file mode 100644
index 0000000000..ddab957704
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_dict.erl
@@ -0,0 +1,196 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_dict.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Maintain atom, import, and export tables for assembler.
+
+-module(beam_dict).
+
+-export([new/0, opcode/2, highest_opcode/1,
+ atom/2, local/4, export/4, import/4, string/2, lambda/5,
+ atom_table/1, local_table/1, export_table/1, import_table/1,
+ string_table/1,lambda_table/1]).
+
+-record(asm_dict,
+ {atoms = [], % [{Index, Atom}]
+ exports = [], % [{F, A, Label}]
+ locals = [], % [{F, A, Label}]
+ imports = [], % [{Index, {M, F, A}]
+ strings = [], % Deep list of characters
+ lambdas = [], % [{...}]
+ next_atom = 1,
+ next_import = 0,
+ string_offset = 0,
+ highest_opcode = 0
+ }).
+
+new() ->
+ #asm_dict{}.
+
+%% Remembers highest opcode.
+
+opcode(Op, Dict) when Dict#asm_dict.highest_opcode > Op -> Dict;
+opcode(Op, Dict) -> Dict#asm_dict{highest_opcode=Op}.
+
+%% Returns the highest opcode encountered.
+
+highest_opcode(#asm_dict{highest_opcode=Op}) -> Op.
+
+%% Returns the index for an atom (adding it to the atom table if necessary).
+%% atom(Atom, Dict) -> {Index, Dict'}
+
+atom(Atom, Dict) when atom(Atom) ->
+ NextIndex = Dict#asm_dict.next_atom,
+ case lookup_store(Atom, Dict#asm_dict.atoms, NextIndex) of
+ {Index, _, NextIndex} ->
+ {Index, Dict};
+ {Index, Atoms, NewIndex} ->
+ {Index, Dict#asm_dict{atoms=Atoms, next_atom=NewIndex}}
+ end.
+
+%% Remembers an exported function.
+%% export(Func, Arity, Label, Dict) -> Dict'
+
+export(Func, Arity, Label, Dict0) when atom(Func), integer(Arity), integer(Label) ->
+ {Index, Dict1} = atom(Func, Dict0),
+ Dict1#asm_dict{exports = [{Index, Arity, Label}| Dict1#asm_dict.exports]}.
+
+%% Remembers a local function.
+%% local(Func, Arity, Label, Dict) -> Dict'
+
+local(Func, Arity, Label, Dict0) when atom(Func), integer(Arity), integer(Label) ->
+ {Index,Dict1} = atom(Func, Dict0),
+ Dict1#asm_dict{locals = [{Index,Arity,Label}| Dict1#asm_dict.locals]}.
+
+%% Returns the index for an import entry (adding it to the import table if necessary).
+%% import(Mod, Func, Arity, Dict) -> {Index, Dict'}
+
+import(Mod, Func, Arity, Dict) when atom(Mod), atom(Func), integer(Arity) ->
+ NextIndex = Dict#asm_dict.next_import,
+ case lookup_store({Mod, Func, Arity}, Dict#asm_dict.imports, NextIndex) of
+ {Index, _, NextIndex} ->
+ {Index, Dict};
+ {Index, Imports, NewIndex} ->
+ {_, D1} = atom(Mod, Dict#asm_dict{imports=Imports, next_import=NewIndex}),
+ {_, D2} = atom(Func, D1),
+ {Index, D2}
+ end.
+
+%% Returns the index for a string in the string table (adding the string to the
+%% table if necessary).
+%% string(String, Dict) -> {Offset, Dict'}
+
+string(Str, Dict) when list(Str) ->
+ #asm_dict{strings = Strings, string_offset = NextOffset} = Dict,
+ case old_string(Str, Strings) of
+ {true, Offset} ->
+ {Offset, Dict};
+ false ->
+ NewDict = Dict#asm_dict{strings = Strings++Str,
+ string_offset = NextOffset+length(Str)},
+ {NextOffset, NewDict}
+ end.
+
+%% Returns the index for a funentry (adding it to the table if necessary).
+%% lambda(Dict, Lbl, Index, Uniq, NumFree) -> {Index,Dict'}
+
+lambda(Lbl, Index, OldUniq, NumFree, #asm_dict{lambdas=Lambdas0}=Dict) ->
+ OldIndex = length(Lambdas0),
+ Lambdas = [{Lbl,{OldIndex,Lbl,Index,NumFree,OldUniq}}|Lambdas0],
+ {OldIndex,Dict#asm_dict{lambdas=Lambdas}}.
+
+%% Returns the atom table.
+%% atom_table(Dict) -> [Length,AtomString...]
+
+atom_table(#asm_dict{atoms=Atoms, next_atom=NumAtoms}) ->
+ Sorted = lists:sort(Atoms),
+ Fun = fun({_, A}) ->
+ L = atom_to_list(A),
+ [length(L)|L]
+ end,
+ {NumAtoms-1, lists:map(Fun, Sorted)}.
+
+%% Returns the table of local functions.
+%% local_table(Dict) -> {NumLocals, [{Function, Arity, Label}...]}
+
+local_table(#asm_dict{locals = Locals}) ->
+ {length(Locals),Locals}.
+
+%% Returns the export table.
+%% export_table(Dict) -> {NumExports, [{Function, Arity, Label}...]}
+
+export_table(#asm_dict{exports = Exports}) ->
+ {length(Exports), Exports}.
+
+%% Returns the import table.
+%% import_table(Dict) -> {NumImports, [{Module, Function, Arity}...]}
+
+import_table(Dict) ->
+ #asm_dict{imports = Imports, next_import = NumImports} = Dict,
+ Sorted = lists:sort(Imports),
+ Fun = fun({_, {Mod, Func, Arity}}) ->
+ {Atom0, _} = atom(Mod, Dict),
+ {Atom1, _} = atom(Func, Dict),
+ {Atom0, Atom1, Arity}
+ end,
+ {NumImports, lists:map(Fun, Sorted)}.
+
+string_table(#asm_dict{strings = Strings, string_offset = Size}) ->
+ {Size, Strings}.
+
+lambda_table(#asm_dict{locals=Loc0,lambdas=Lambdas0}) ->
+ Lambdas1 = sofs:relation(Lambdas0),
+ Loc = sofs:relation([{Lbl,{F,A}} || {F,A,Lbl} <- Loc0]),
+ Lambdas2 = sofs:relative_product1(Lambdas1, Loc),
+ Lambdas = [<<F:32,A:32,Lbl:32,Index:32,NumFree:32,OldUniq:32>> ||
+ {{_,Lbl,Index,NumFree,OldUniq},{F,A}} <- sofs:to_external(Lambdas2)],
+ {length(Lambdas),Lambdas}.
+
+%%% Local helper functions.
+
+lookup_store(Key, Dict, NextIndex) ->
+ case catch lookup_store1(Key, Dict, NextIndex) of
+ Index when integer(Index) ->
+ {Index, Dict, NextIndex};
+ {Index, NewDict} ->
+ {Index, NewDict, NextIndex+1}
+ end.
+
+lookup_store1(Key, [Pair|Dict], NextIndex) when Key > element(2, Pair) ->
+ {Index, NewDict} = lookup_store1(Key, Dict, NextIndex),
+ {Index, [Pair|NewDict]};
+lookup_store1(Key, [{Index, Key}|_Dict], _NextIndex) ->
+ throw(Index);
+lookup_store1(Key, Dict, NextIndex) ->
+ {NextIndex, [{NextIndex, Key}|Dict]}.
+
+%% Search for string Str in the string pool Pool.
+%% old_string(Str, Pool) -> false | {true, Offset}
+
+old_string(Str, Pool) ->
+ old_string(Str, Pool, 0).
+
+old_string([C|Str], [C|Pool], Index) ->
+ case lists:prefix(Str, Pool) of
+ true ->
+ {true, Index};
+ false ->
+ old_string([C|Str], Pool, Index+1)
+ end;
+old_string(Str, [_|Pool], Index) ->
+ old_string(Str, Pool, Index+1);
+old_string(_Str, [], _Index) ->
+ false.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl
new file mode 100644
index 0000000000..451b83db66
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_disasm.erl
@@ -0,0 +1,964 @@
+%% -*- erlang-indent-level: 4 -*-
+%%=======================================================================
+%% File : beam_disasm.erl
+%% Author : Kostis Sagonas
+%% Description : Disassembles an R5-R10 .beam file into symbolic BEAM code
+%%=======================================================================
+%% $Id: beam_disasm.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%=======================================================================
+%% Notes:
+%% 1. It does NOT work for .beam files of previous BEAM versions.
+%% 2. If handling of new BEAM instructions is needed, this should be
+%% inserted at the end of function resolve_inst().
+%%=======================================================================
+
+-module(beam_disasm).
+
+-export([file/1, format_error/1]).
+
+-author("Kostis Sagonas").
+
+-include("beam_opcodes.hrl").
+
+%%-----------------------------------------------------------------------
+
+-define(NO_DEBUG(Str,Xs),ok).
+-define(DEBUG(Str,Xs),io:format(Str,Xs)).
+-define(exit(Reason),exit({?MODULE,?LINE,Reason})).
+
+%%-----------------------------------------------------------------------
+%% Error information
+
+format_error({error, Module, Error}) ->
+ Module:format_error(Error);
+format_error({internal, Error}) ->
+ io_lib:format("~p: disassembly failed with reason ~P.",
+ [?MODULE, Error, 25]).
+
+%%-----------------------------------------------------------------------
+%% The main exported function
+%% File is either a file name or a binary containing the code.
+%% Returns `{beam_file, [...]}' or `{error, Module, Reason}'.
+%% Call `format_error({error, Module, Reason})' for an error string.
+%%-----------------------------------------------------------------------
+
+file(File) ->
+ case beam_lib:info(File) of
+ Info when list(Info) ->
+ {value,{chunks,Chunks}} = lists:keysearch(chunks,1,Info),
+ case catch process_chunks(File, Chunks) of
+ {'EXIT', Error} ->
+ {error, ?MODULE, {internal, Error}};
+ Result ->
+ Result
+ end;
+ Error ->
+ Error
+ end.
+
+%%-----------------------------------------------------------------------
+%% Interface might need to be revised -- do not depend on it.
+%%-----------------------------------------------------------------------
+
+process_chunks(F,ChunkInfoList) ->
+ {ok,{_,Chunks}} = beam_lib:chunks(F, ["Atom","Code","StrT","ImpT","ExpT"]),
+ [{"Atom",AtomBin},{"Code",CodeBin},{"StrT",StrBin},
+ {"ImpT",ImpBin},{"ExpT",ExpBin}] = Chunks,
+ LambdaBin = optional_chunk(F, "FunT", ChunkInfoList),
+ LocBin = optional_chunk(F, "LocT", ChunkInfoList),
+ AttrBin = optional_chunk(F, "Attr", ChunkInfoList),
+ CompBin = optional_chunk(F, "CInf", ChunkInfoList),
+ Atoms = beam_disasm_atoms(AtomBin),
+ Exports = beam_disasm_exports(ExpBin, Atoms),
+ Imports = beam_disasm_imports(ImpBin, Atoms),
+ LocFuns = beam_disasm_exports(LocBin, Atoms),
+ Lambdas = beam_disasm_lambdas(LambdaBin, Atoms),
+ Str = beam_disasm_strings(StrBin),
+ Str1 = binary_to_list(Str), %% for debugging -- use Str as far as poss.
+ Sym_Code = beam_disasm_code(CodeBin,Atoms,Imports,Str,Lambdas),
+ Attributes = beam_disasm_attributes(AttrBin),
+ CompInfo = beam_disasm_compilation_info(CompBin),
+ All = [{exports,Exports},
+ {imports,Imports},
+ {code,Sym_Code},
+ {atoms,Atoms},
+ {local_funs,LocFuns},
+ {strings,Str1},
+ {attributes,Attributes},
+ {comp_info,CompInfo}],
+ {beam_file,[Item || {_Key,Data}=Item <- All, Data =/= none]}.
+
+%%-----------------------------------------------------------------------
+%% Retrieve an optional chunk or none if the chunk doesn't exist.
+%%-----------------------------------------------------------------------
+
+optional_chunk(F, ChunkTag, ChunkInfo) ->
+ case lists:keymember(ChunkTag, 1, ChunkInfo) of
+ true ->
+ {ok,{_,[{ChunkTag,Chunk}]}} = beam_lib:chunks(F, [ChunkTag]),
+ Chunk;
+ false -> none
+ end.
+
+%%-----------------------------------------------------------------------
+%% UTILITIES -- these actually exist in file "beam_lib"
+%% -- they should be moved into a common utils file.
+%%-----------------------------------------------------------------------
+
+i32([X1,X2,X3,X4]) ->
+ (X1 bsl 24) bor (X2 bsl 16) bor (X3 bsl 8) bor X4.
+
+get_int(B) ->
+ {I, B1} = split_binary(B, 4),
+ {i32(binary_to_list(I)), B1}.
+
+%%-----------------------------------------------------------------------
+%% Disassembles the atom table of a BEAM file.
+%% - atoms are stored in order 1 ... N (N = Num_atoms, in fact),
+%% - each atom name consists of a length byte, followed by that many
+%% bytes of name
+%% (nb: atom names max 255 chars?!)
+%%-----------------------------------------------------------------------
+
+beam_disasm_atoms(AtomTabBin) ->
+ {_NumAtoms,B} = get_int(AtomTabBin),
+ disasm_atoms(B).
+
+disasm_atoms(AtomBin) ->
+ disasm_atoms(binary_to_list(AtomBin),1).
+
+disasm_atoms([Len|Xs],N) ->
+ {AtomName,Rest} = get_atom_name(Len,Xs),
+ [{N,list_to_atom(AtomName)}|disasm_atoms(Rest,N+1)];
+disasm_atoms([],_) ->
+ [].
+
+get_atom_name(Len,Xs) ->
+ get_atom_name(Len,Xs,[]).
+
+get_atom_name(N,[X|Xs],RevName) when N > 0 ->
+ get_atom_name(N-1,Xs,[X|RevName]);
+get_atom_name(0,Xs,RevName) ->
+ { lists:reverse(RevName), Xs }.
+
+%%-----------------------------------------------------------------------
+%% Disassembles the export table of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_exports(none, _) -> none;
+beam_disasm_exports(ExpTabBin, Atoms) ->
+ {_NumAtoms,B} = get_int(ExpTabBin),
+ disasm_exports(B,Atoms).
+
+disasm_exports(Bin,Atoms) ->
+ resolve_exports(collect_exports(binary_to_list(Bin)),Atoms).
+
+collect_exports([F3,F2,F1,F0,A3,A2,A1,A0,L3,L2,L1,L0|Exps]) ->
+ [{i32([F3,F2,F1,F0]), % F = function (atom ID)
+ i32([A3,A2,A1,A0]), % A = arity (int)
+ i32([L3,L2,L1,L0])} % L = label (int)
+ |collect_exports(Exps)];
+collect_exports([]) ->
+ [].
+
+resolve_exports(Exps,Atoms) ->
+ [ {lookup_key(F,Atoms), A, L} || {F,A,L} <- Exps ].
+
+%%-----------------------------------------------------------------------
+%% Disassembles the import table of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_imports(ExpTabBin,Atoms) ->
+ {_NumAtoms,B} = get_int(ExpTabBin),
+ disasm_imports(B,Atoms).
+
+disasm_imports(Bin,Atoms) ->
+ resolve_imports(collect_imports(binary_to_list(Bin)),Atoms).
+
+collect_imports([M3,M2,M1,M0,F3,F2,F1,F0,A3,A2,A1,A0|Exps]) ->
+ [{i32([M3,M2,M1,M0]), % M = module (atom ID)
+ i32([F3,F2,F1,F0]), % F = function (atom ID)
+ i32([A3,A2,A1,A0])} % A = arity (int)
+ |collect_imports(Exps)];
+collect_imports([]) ->
+ [].
+
+resolve_imports(Exps,Atoms) ->
+ [{extfunc,lookup_key(M,Atoms),lookup_key(F,Atoms),A} || {M,F,A} <- Exps ].
+
+%%-----------------------------------------------------------------------
+%% Disassembles the lambda (fun) table of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_lambdas(none, _) -> none;
+beam_disasm_lambdas(<<_:32,Tab/binary>>, Atoms) ->
+ disasm_lambdas(Tab, Atoms, 0).
+
+disasm_lambdas(<<F:32,A:32,Lbl:32,Index:32,NumFree:32,OldUniq:32,More/binary>>,
+ Atoms, OldIndex) ->
+ Info = {lookup_key(F, Atoms),A,Lbl,Index,NumFree,OldUniq},
+ [{OldIndex,Info}|disasm_lambdas(More, Atoms, OldIndex+1)];
+disasm_lambdas(<<>>, _, _) -> [].
+
+%%-----------------------------------------------------------------------
+%% Disassembles the code chunk of a BEAM file:
+%% - The code is first disassembled into a long list of instructions.
+%% - This list is then split into functions and all names are resolved.
+%%-----------------------------------------------------------------------
+
+beam_disasm_code(CodeBin,Atoms,Imports,Str,Lambdas) ->
+ [_SS3,_SS2,_SS1,_SS0, % Sub-Size (length of information before code)
+ _IS3,_IS2,_IS1,_IS0, % Instruction Set Identifier (always 0)
+ _OM3,_OM2,_OM1,_OM0, % Opcode Max
+ _L3,_L2,_L1,_L0,_F3,_F2,_F1,_F0|Code] = binary_to_list(CodeBin),
+ case catch disasm_code(Code, Atoms) of
+ {'EXIT',Rsn} ->
+ ?NO_DEBUG('code disasm failed: ~p~n',[Rsn]),
+ ?exit(Rsn);
+ DisasmCode ->
+ Functions = get_function_chunks(DisasmCode),
+ LocLabels = local_labels(Functions),
+ [resolve_names(F,Imports,Str,LocLabels,Lambdas) || F <- Functions]
+ end.
+
+%%-----------------------------------------------------------------------
+
+disasm_code([B|Bs], Atoms) ->
+ {Instr,RestBs} = disasm_instr(B, Bs, Atoms),
+ [Instr|disasm_code(RestBs, Atoms)];
+disasm_code([], _) -> [].
+
+%%-----------------------------------------------------------------------
+%% Splits the code stream into chunks representing the code of functions.
+%%
+%% NOTE: code actually looks like
+%% label L1: ... label Ln:
+%% func_info ...
+%% label entry:
+%% ...
+%% <on failure, use label Li to show where things died>
+%% ...
+%% So the labels before each func_info should be included as well.
+%% Ideally, only one such label is needed, but the BEAM compiler
+%% before R8 didn't care to remove the redundant ones.
+%%-----------------------------------------------------------------------
+
+get_function_chunks([I|Code]) ->
+ {LastI,RestCode,Labs} = split_head_labels(I,Code,[]),
+ get_funs(LastI,RestCode,Labs,[]);
+get_function_chunks([]) ->
+ ?exit(empty_code_segment).
+
+get_funs(PrevI,[I|Is],RevF,RevFs) ->
+ case I of
+ {func_info,_Info} ->
+ [H|T] = RevF,
+ {Last,Fun,TrailingLabels} = split_head_labels(H,T,[]),
+ get_funs(I, Is, [PrevI|TrailingLabels], add_funs([Last|Fun],RevFs));
+ _ ->
+ get_funs(I, Is, [PrevI|RevF], RevFs)
+ end;
+get_funs(PrevI,[],RevF,RevFs) ->
+ case PrevI of
+ {int_code_end,[]} ->
+ emit_funs(add_fun(RevF,RevFs));
+ _ ->
+ ?DEBUG('warning: code segment did not end with int_code_end~n',[]),
+ emit_funs(add_funs([PrevI|RevF],RevFs))
+ end.
+
+split_head_labels({label,L},[I|Code],Labs) ->
+ split_head_labels(I,Code,[{label,L}|Labs]);
+split_head_labels(I,Code,Labs) ->
+ {I,Code,Labs}.
+
+add_fun([],Fs) ->
+ Fs;
+add_fun(F,Fs) ->
+ add_funs(F,Fs).
+
+add_funs(F,Fs) ->
+ [ lists:reverse(F) | Fs ].
+
+emit_funs(Fs) ->
+ lists:reverse(Fs).
+
+%%-----------------------------------------------------------------------
+%% Collects local labels -- I am not sure this is 100% what is needed.
+%%-----------------------------------------------------------------------
+
+local_labels(Funs) ->
+ [local_label(Fun) || Fun <- Funs].
+
+%% The first clause below attempts to provide some (limited form of)
+%% backwards compatibility; it is not needed for .beam files generated
+%% by the R8 compiler. The clause should one fine day be taken out.
+local_label([{label,_},{label,L}|Code]) ->
+ local_label([{label,L}|Code]);
+local_label([{label,_},
+ {func_info,[M0,F0,{u,A}]},
+ {label,[{u,L1}]}|_]) ->
+ {atom,M} = resolve_arg(M0),
+ {atom,F} = resolve_arg(F0),
+ {L1, {M, F, A}};
+local_label(Code) ->
+ io:format('beam_disasm: no label in ~p~n', [Code]),
+ {-666,{none,none,0}}.
+
+%%-----------------------------------------------------------------------
+%% Disassembles a single BEAM instruction; most instructions are handled
+%% in a generic way; indexing instructions are handled separately.
+%%-----------------------------------------------------------------------
+
+disasm_instr(B, Bs, Atoms) ->
+ {SymOp,Arity} = beam_opcodes:opname(B),
+ case SymOp of
+ select_val ->
+ disasm_select_inst(select_val, Bs, Atoms);
+ select_tuple_arity ->
+ disasm_select_inst(select_tuple_arity, Bs, Atoms);
+ _ ->
+ case catch decode_n_args(Arity, Bs, Atoms) of
+ {'EXIT',Rsn} ->
+ ?NO_DEBUG("decode_n_args(~p,~p) failed~n",[Arity,Bs]),
+ {{'EXIT',{SymOp,Arity,Rsn}},[]};
+ {Args,RestBs} ->
+ ?NO_DEBUG("instr ~p~n",[{SymOp,Args}]),
+ {{SymOp,Args}, RestBs}
+ end
+ end.
+
+%%-----------------------------------------------------------------------
+%% Disassembles a BEAM select_* instruction used for indexing.
+%% Currently handles {select_val,3} and {select_tuple_arity,3} insts.
+%%
+%% The arruments of a "select"-type instruction look as follows:
+%% <reg>, {f,FailLabel}, {list, <num cases>, [<case1> ... <caseN>]}
+%% where each case is of the form [symbol,{f,Label}].
+%%-----------------------------------------------------------------------
+
+disasm_select_inst(Inst, Bs, Atoms) ->
+ {X, Bs1} = decode_arg(Bs, Atoms),
+ {F, Bs2} = decode_arg(Bs1, Atoms),
+ {Z, Bs3} = decode_arg(Bs2, Atoms),
+ {U, Bs4} = decode_arg(Bs3, Atoms),
+ {u,Len} = U,
+ {List, RestBs} = decode_n_args(Len, Bs4, Atoms),
+ {{Inst,[X,F,{Z,U,List}]},RestBs}.
+
+%%-----------------------------------------------------------------------
+%% decode_arg([Byte]) -> { Arg, [Byte] }
+%%
+%% - an arg can have variable length, so we must return arg + remaining bytes
+%% - decodes an argument into its 'raw' form: { Tag, Value }
+%% several types map to a single tag, so the byte code instr must then
+%% assign a type to it
+%%-----------------------------------------------------------------------
+
+decode_arg([B|Bs]) ->
+ Tag = decode_tag(B band 2#111),
+ ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n',[Tag,B,Bs]),
+ case Tag of
+ z ->
+ decode_z_tagged(Tag, B, Bs);
+ _ ->
+ %% all other cases are handled as if they were integers
+ decode_int(Tag, B, Bs)
+ end.
+
+decode_arg([B|Bs0], Atoms) ->
+ Tag = decode_tag(B band 2#111),
+ ?NO_DEBUG('Tag = ~p, B = ~p, Bs = ~p~n',[Tag,B,Bs]),
+ case Tag of
+ z ->
+ decode_z_tagged(Tag, B, Bs0);
+ a ->
+ %% atom or nil
+ case decode_int(Tag, B, Bs0) of
+ {{a,0},Bs} -> {nil,Bs};
+ {{a,I},Bs} -> {{atom,lookup_key(I, Atoms)},Bs}
+ end;
+ _ ->
+ %% all other cases are handled as if they were integers
+ decode_int(Tag, B, Bs0)
+ end.
+
+%%-----------------------------------------------------------------------
+%% Decodes an integer value. Handles positives, negatives, and bignums.
+%%
+%% Tries to do the opposite of:
+%% beam_asm:encode(1, 5) = [81]
+%% beam_asm:encode(1, 1000) = [105,232]
+%% beam_asm:encode(1, 2047) = [233,255]
+%% beam_asm:encode(1, 2048) = [25,8,0]
+%% beam_asm:encode(1,-1) = [25,255,255]
+%% beam_asm:encode(1,-4294967295) = [121,255,0,0,0,1]
+%% beam_asm:encode(1, 4294967295) = [121,0,255,255,255,255]
+%% beam_asm:encode(1, 429496729501) = [121,99,255,255,255,157]
+%%-----------------------------------------------------------------------
+
+decode_int(Tag,B,Bs) when (B band 16#08) == 0 ->
+ %% N < 16 = 4 bits, NNNN:0:TTT
+ N = B bsr 4,
+ {{Tag,N},Bs};
+decode_int(Tag,B,Bs) when (B band 16#10) == 0 ->
+ %% N < 2048 = 11 bits = 3:8 bits, NNN:01:TTT, NNNNNNNN
+ [B1|Bs1] = Bs,
+ Val0 = B band 2#11100000,
+ N = (Val0 bsl 3) bor B1,
+ ?NO_DEBUG('NNN:01:TTT, NNNNNNNN = ~n~p:01:~p, ~p = ~p~n', [Val0,Tag,B,N]),
+ {{Tag,N},Bs1};
+decode_int(Tag,B,Bs) ->
+ {Len,Bs1} = decode_int_length(B,Bs),
+ {IntBs,RemBs} = take_bytes(Len,Bs1),
+ N = build_arg(IntBs),
+ [F|_] = IntBs,
+ Num = if F > 127, Tag == i -> decode_negative(N,Len);
+ true -> N
+ end,
+ ?NO_DEBUG('Len = ~p, IntBs = ~p, Num = ~p~n', [Len,IntBs,Num]),
+ {{Tag,Num},RemBs}.
+
+decode_int_length(B,Bs) ->
+ %% The following imitates get_erlang_integer() in beam_load.c
+ %% Len is the size of the integer value in bytes
+ case B bsr 5 of
+ 7 ->
+ {Arg,ArgBs} = decode_arg(Bs),
+ case Arg of
+ {u,L} ->
+ {L+9,ArgBs}; % 9 stands for 7+2
+ _ ->
+ ?exit({decode_int,weird_bignum_sublength,Arg})
+ end;
+ L ->
+ {L+2,Bs}
+ end.
+
+decode_negative(N,Len) ->
+ N - (1 bsl (Len*8)). % 8 is number of bits in a byte
+
+%%-----------------------------------------------------------------------
+%% Decodes lists and floating point numbers.
+%%-----------------------------------------------------------------------
+
+decode_z_tagged(Tag,B,Bs) when (B band 16#08) == 0 ->
+ N = B bsr 4,
+ case N of
+ 0 -> % float
+ decode_float(Bs);
+ 1 -> % list
+ {{Tag,N},Bs};
+ 2 -> % fr
+ decode_fr(Bs);
+ 3 -> % allocation list
+ decode_alloc_list(Bs);
+ _ ->
+ ?exit({decode_z_tagged,{invalid_extended_tag,N}})
+ end;
+decode_z_tagged(_,B,_) ->
+ ?exit({decode_z_tagged,{weird_value,B}}).
+
+decode_float(Bs) ->
+ {FL,RestBs} = take_bytes(8,Bs),
+ <<Float:64/float>> = list_to_binary(FL),
+ {{float,Float},RestBs}.
+
+decode_fr(Bs) ->
+ {{u,Fr},RestBs} = decode_arg(Bs),
+ {{fr,Fr},RestBs}.
+
+decode_alloc_list(Bs) ->
+ {{u,N},RestBs} = decode_arg(Bs),
+ decode_alloc_list_1(N, RestBs, []).
+
+decode_alloc_list_1(0, RestBs, Acc) ->
+ {{u,{alloc,lists:reverse(Acc)}},RestBs};
+decode_alloc_list_1(N, Bs0, Acc) ->
+ {{u,Type},Bs1} = decode_arg(Bs0),
+ {{u,Val},Bs} = decode_arg(Bs1),
+ case Type of
+ 0 ->
+ decode_alloc_list_1(N-1, Bs, [{words,Val}|Acc]);
+ 1 ->
+ decode_alloc_list_1(N-1, Bs, [{floats,Val}|Acc])
+ end.
+
+%%-----------------------------------------------------------------------
+%% take N bytes from a stream, return { Taken_bytes, Remaining_bytes }
+%%-----------------------------------------------------------------------
+
+take_bytes(N,Bs) ->
+ take_bytes(N,Bs,[]).
+
+take_bytes(N,[B|Bs],Acc) when N > 0 ->
+ take_bytes(N-1,Bs,[B|Acc]);
+take_bytes(0,Bs,Acc) ->
+ { lists:reverse(Acc), Bs }.
+
+%%-----------------------------------------------------------------------
+%% from a list of bytes Bn,Bn-1,...,B1,B0
+%% build (Bn << 8*n) bor ... bor B1 << 8 bor B0 << 0
+%%-----------------------------------------------------------------------
+
+build_arg(Bs) ->
+ build_arg(Bs,0).
+
+build_arg([B|Bs],N) ->
+ build_arg(Bs, (N bsl 8) bor B);
+build_arg([],N) ->
+ N.
+
+%%-----------------------------------------------------------------------
+%% Decodes a bunch of arguments and returns them in a list
+%%-----------------------------------------------------------------------
+
+decode_n_args(N, Bs, Atoms) when N >= 0 ->
+ decode_n_args(N, [], Bs, Atoms).
+
+decode_n_args(N, Acc, Bs0, Atoms) when N > 0 ->
+ {A1,Bs} = decode_arg(Bs0, Atoms),
+ decode_n_args(N-1, [A1|Acc], Bs, Atoms);
+decode_n_args(0, Acc, Bs, _) ->
+ {lists:reverse(Acc),Bs}.
+
+%%-----------------------------------------------------------------------
+%% Convert a numeric tag value into a symbolic one
+%%-----------------------------------------------------------------------
+
+decode_tag(?tag_u) -> u;
+decode_tag(?tag_i) -> i;
+decode_tag(?tag_a) -> a;
+decode_tag(?tag_x) -> x;
+decode_tag(?tag_y) -> y;
+decode_tag(?tag_f) -> f;
+decode_tag(?tag_h) -> h;
+decode_tag(?tag_z) -> z;
+decode_tag(X) -> ?exit({unknown_tag,X}).
+
+%%-----------------------------------------------------------------------
+%% - replace all references {a,I} with the atom with index I (or {atom,A})
+%% - replace all references to {i,K} in an external call position with
+%% the proper MFA (position in list, first elt = 0, yields MFA to use)
+%% - resolve strings, represented as <offset, length>, into their
+%% actual values by using string table
+%% (note: string table should be passed as a BINARY so that we can
+%% use binary_to_list/3!)
+%% - convert instruction to its readable form ...
+%%
+%% Currently, only the first three are done (systematically, at least).
+%%
+%% Note: It MAY be premature to remove the lists of args, since that
+%% representation means it is simpler to iterate over all args, etc.
+%%-----------------------------------------------------------------------
+
+resolve_names(Fun, Imports, Str, Lbls, Lambdas) ->
+ [resolve_inst(Instr, Imports, Str, Lbls, Lambdas) || Instr <- Fun].
+
+%%
+%% New make_fun2/4 instruction added in August 2001 (R8).
+%% We handle it specially here to avoid adding an argument to
+%% the clause for every instruction.
+%%
+
+resolve_inst({make_fun2,Args},_,_,Lbls,Lambdas) ->
+ [OldIndex] = resolve_args(Args),
+ {value,{OldIndex,{F,A,_Lbl,_Index,NumFree,OldUniq}}} =
+ lists:keysearch(OldIndex, 1, Lambdas),
+ [{_,{M,_,_}}|_] = Lbls, % Slighly kludgy.
+ {make_fun2,{M,F,A},OldIndex,OldUniq,NumFree};
+resolve_inst(Instr, Imports, Str, Lbls, _Lambdas) ->
+ resolve_inst(Instr, Imports, Str, Lbls).
+
+resolve_inst({label,[{u,L}]},_,_,_) ->
+ {label,L};
+resolve_inst({func_info,RawMFA},_,_,_) ->
+ {func_info,resolve_args(RawMFA)};
+% resolve_inst(int_code_end,_,_,_,_) -> % instruction already handled
+% int_code_end; % should not really be handled here
+resolve_inst({call,[{u,N},{f,L}]},_,_,Lbls) ->
+ {call,N,catch lookup_key(L,Lbls)};
+resolve_inst({call_last,[{u,N},{f,L},{u,U}]},_,_,Lbls) ->
+ {call_last,N,catch lookup_key(L,Lbls),U};
+resolve_inst({call_only,[{u,N},{f,L}]},_,_,Lbls) ->
+ {call_only,N,catch lookup_key(L,Lbls)};
+resolve_inst({call_ext,[{u,N},{u,MFAix}]},Imports,_,_) ->
+ {call_ext,N,catch lists:nth(MFAix+1,Imports)};
+resolve_inst({call_ext_last,[{u,N},{u,MFAix},{u,X}]},Imports,_,_) ->
+ {call_ext_last,N,catch lists:nth(MFAix+1,Imports),X};
+resolve_inst({bif0,Args},Imports,_,_) ->
+ [Bif,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
+ %?NO_DEBUG('bif0(~p, ~p)~n',[BifName,Reg]),
+ {bif,BifName,nofail,[],Reg};
+resolve_inst({bif1,Args},Imports,_,_) ->
+ [F,Bif,A1,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
+ %?NO_DEBUG('bif1(~p, ~p, ~p, ~p, ~p)~n',[Bif,BifName,F,[A1],Reg]),
+ {bif,BifName,F,[A1],Reg};
+resolve_inst({bif2,Args},Imports,_,_) ->
+ [F,Bif,A1,A2,Reg] = resolve_args(Args),
+ {extfunc,_Mod,BifName,_Arity} = lists:nth(Bif+1,Imports),
+ %?NO_DEBUG('bif2(~p, ~p, ~p, ~p, ~p)~n',[Bif,BifName,F,[A1,A2],Reg]),
+ {bif,BifName,F,[A1,A2],Reg};
+resolve_inst({allocate,[{u,X0},{u,X1}]},_,_,_) ->
+ {allocate,X0,X1};
+resolve_inst({allocate_heap,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
+ {allocate_heap,X0,X1,X2};
+resolve_inst({allocate_zero,[{u,X0},{u,X1}]},_,_,_) ->
+ {allocate_zero,X0,X1};
+resolve_inst({allocate_heap_zero,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
+ {allocate_heap_zero,X0,X1,X2};
+resolve_inst({test_heap,[{u,X0},{u,X1}]},_,_,_) ->
+ {test_heap,X0,X1};
+resolve_inst({init,[Dst]},_,_,_) ->
+ {init,Dst};
+resolve_inst({deallocate,[{u,L}]},_,_,_) ->
+ {deallocate,L};
+resolve_inst({return,[]},_,_,_) ->
+ return;
+resolve_inst({send,[]},_,_,_) ->
+ send;
+resolve_inst({remove_message,[]},_,_,_) ->
+ remove_message;
+resolve_inst({timeout,[]},_,_,_) ->
+ timeout;
+resolve_inst({loop_rec,[Lbl,Dst]},_,_,_) ->
+ {loop_rec,Lbl,Dst};
+resolve_inst({loop_rec_end,[Lbl]},_,_,_) ->
+ {loop_rec_end,Lbl};
+resolve_inst({wait,[Lbl]},_,_,_) ->
+ {wait,Lbl};
+resolve_inst({wait_timeout,[Lbl,Int]},_,_,_) ->
+ {wait_timeout,Lbl,resolve_arg(Int)};
+resolve_inst({m_plus,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'+',W,[SrcR1,SrcR2],DstR};
+resolve_inst({m_minus,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'-',W,[SrcR1,SrcR2],DstR};
+resolve_inst({m_times,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'*',W,[SrcR1,SrcR2],DstR};
+resolve_inst({m_div,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'/',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_div,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'div',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_rem,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'rem',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_band,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'band',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bor,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bor',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bxor,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bxor',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bsl,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bsl',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bsr,Args},_,_,_) ->
+ [W,SrcR1,SrcR2,DstR] = resolve_args(Args),
+ {arithbif,'bsr',W,[SrcR1,SrcR2],DstR};
+resolve_inst({int_bnot,Args},_,_,_) ->
+ [W,SrcR,DstR] = resolve_args(Args),
+ {arithbif,'bnot',W,[SrcR],DstR};
+resolve_inst({is_lt=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_ge=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_eq=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_ne=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_eq_exact=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_ne_exact=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_integer=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_float=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_number=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_atom=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_pid=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_reference=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_port=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_nil=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_binary=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_constant=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_list=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_nonempty_list=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({is_tuple=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({test_arity=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({select_val,Args},_,_,_) ->
+ [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
+ List = resolve_args(List0),
+ {select_val,Reg,FLbl,{list,List}};
+resolve_inst({select_tuple_arity,Args},_,_,_) ->
+ [Reg,FLbl,{{z,1},{u,_Len},List0}] = Args,
+ List = resolve_args(List0),
+ {select_tuple_arity,Reg,FLbl,{list,List}};
+resolve_inst({jump,[Lbl]},_,_,_) ->
+ {jump,Lbl};
+resolve_inst({'catch',[Dst,Lbl]},_,_,_) ->
+ {'catch',Dst,Lbl};
+resolve_inst({catch_end,[Dst]},_,_,_) ->
+ {catch_end,Dst};
+resolve_inst({move,[Src,Dst]},_,_,_) ->
+ {move,resolve_arg(Src),Dst};
+resolve_inst({get_list,[Src,Dst1,Dst2]},_,_,_) ->
+ {get_list,Src,Dst1,Dst2};
+resolve_inst({get_tuple_element,[Src,{u,Off},Dst]},_,_,_) ->
+ {get_tuple_element,resolve_arg(Src),Off,resolve_arg(Dst)};
+resolve_inst({set_tuple_element,[Src,Dst,{u,Off}]},_,_,_) ->
+ {set_tuple_element,resolve_arg(Src),resolve_arg(Dst),Off};
+resolve_inst({put_string,[{u,Len},{u,Off},Dst]},_,Strings,_) ->
+ String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
+ true -> ""
+ end,
+?NO_DEBUG('put_string(~p, {string,~p}, ~p)~n',[Len,String,Dst]),
+ {put_string,Len,{string,String},Dst};
+resolve_inst({put_list,[Src1,Src2,Dst]},_,_,_) ->
+ {put_list,resolve_arg(Src1),resolve_arg(Src2),Dst};
+resolve_inst({put_tuple,[{u,Arity},Dst]},_,_,_) ->
+ {put_tuple,Arity,Dst};
+resolve_inst({put,[Src]},_,_,_) ->
+ {put,resolve_arg(Src)};
+resolve_inst({badmatch,[X]},_,_,_) ->
+ {badmatch,resolve_arg(X)};
+resolve_inst({if_end,[]},_,_,_) ->
+ if_end;
+resolve_inst({case_end,[X]},_,_,_) ->
+ {case_end,resolve_arg(X)};
+resolve_inst({call_fun,[{u,N}]},_,_,_) ->
+ {call_fun,N};
+resolve_inst({make_fun,Args},_,_,Lbls) ->
+ [{f,L},Magic,FreeVars] = resolve_args(Args),
+ {make_fun,catch lookup_key(L,Lbls),Magic,FreeVars};
+resolve_inst({is_function=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+resolve_inst({call_ext_only,[{u,N},{u,MFAix}]},Imports,_,_) ->
+ {call_ext_only,N,catch lists:nth(MFAix+1,Imports)};
+%%
+%% Instructions for handling binaries added in R7A & R7B
+%%
+resolve_inst({bs_start_match,[F,Reg]},_,_,_) ->
+ {bs_start_match,F,Reg};
+resolve_inst({bs_get_integer=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
+resolve_inst({bs_get_float=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
+resolve_inst({bs_get_binary=I,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {test,I,Lbl,[A2,N,decode_field_flags(U),A5]};
+resolve_inst({bs_skip_bits,[Lbl,Arg2,{u,N},{u,U}]},_,_,_) ->
+ [A2] = resolve_args([Arg2]),
+ {test,bs_skip_bits,Lbl,[A2,N,decode_field_flags(U)]};
+resolve_inst({bs_test_tail,[F,{u,N}]},_,_,_) ->
+ {test,bs_test_tail,F,[N]};
+resolve_inst({bs_save,[{u,N}]},_,_,_) ->
+ {bs_save,N};
+resolve_inst({bs_restore,[{u,N}]},_,_,_) ->
+ {bs_restore,N};
+resolve_inst({bs_init,[{u,N},{u,U}]},_,_,_) ->
+ {bs_init,N,decode_field_flags(U)};
+resolve_inst({bs_final,[F,X]},_,_,_) ->
+ {bs_final,F,X};
+resolve_inst({bs_put_integer,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ {bs_put_integer,Lbl,A2,N,decode_field_flags(U),A5};
+resolve_inst({bs_put_binary,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ ?NO_DEBUG('bs_put_binary(~p,~p,~p,~p,~p})~n',[Lbl,A2,N,U,A5]),
+ {bs_put_binary,Lbl,A2,N,decode_field_flags(U),A5};
+resolve_inst({bs_put_float,[Lbl,Arg2,{u,N},{u,U},Arg5]},_,_,_) ->
+ [A2,A5] = resolve_args([Arg2,Arg5]),
+ ?NO_DEBUG('bs_put_float(~p,~p,~p,~p,~p})~n',[Lbl,A2,N,U,A5]),
+ {bs_put_float,Lbl,A2,N,decode_field_flags(U),A5};
+resolve_inst({bs_put_string,[{u,Len},{u,Off}]},_,Strings,_) ->
+ String = if Len > 0 -> binary_to_list(Strings, Off+1, Off+Len);
+ true -> ""
+ end,
+ ?NO_DEBUG('bs_put_string(~p, {string,~p})~n',[Len,String]),
+ {bs_put_string,Len,{string,String}};
+resolve_inst({bs_need_buf,[{u,N}]},_,_,_) ->
+ {bs_need_buf,N};
+
+%%
+%% Instructions for handling floating point numbers added in June 2001 (R8).
+%%
+resolve_inst({fclearerror,[]},_,_,_) ->
+ fclearerror;
+resolve_inst({fcheckerror,Args},_,_,_) ->
+ [Fail] = resolve_args(Args),
+ {fcheckerror,Fail};
+resolve_inst({fmove,Args},_,_,_) ->
+ [FR,Reg] = resolve_args(Args),
+ {fmove,FR,Reg};
+resolve_inst({fconv,Args},_,_,_) ->
+ [Reg,FR] = resolve_args(Args),
+ {fconv,Reg,FR};
+resolve_inst({fadd=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fsub=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fmul=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fdiv=I,Args},_,_,_) ->
+ [F,A1,A2,Reg] = resolve_args(Args),
+ {arithfbif,I,F,[A1,A2],Reg};
+resolve_inst({fnegate,Args},_,_,_) ->
+ [F,Arg,Reg] = resolve_args(Args),
+ {arithfbif,fnegate,F,[Arg],Reg};
+
+%%
+%% Instructions for try expressions added in January 2003 (R10).
+%%
+
+resolve_inst({'try',[Reg,Lbl]},_,_,_) -> % analogous to 'catch'
+ {'try',Reg,Lbl};
+resolve_inst({try_end,[Reg]},_,_,_) -> % analogous to 'catch_end'
+ {try_end,Reg};
+resolve_inst({try_case,[Reg]},_,_,_) -> % analogous to 'catch_end'
+ {try_case,Reg};
+resolve_inst({try_case_end,[Reg]},_,_,_) ->
+ {try_case_end,Reg};
+resolve_inst({raise,[Reg1,Reg2]},_,_,_) ->
+ {bif,raise,{f,0},[Reg1,Reg2],{x,0}};
+
+%%
+%% New bit syntax instructions added in February 2004 (R10B).
+%%
+
+resolve_inst({bs_init2,[Lbl,Arg2,{u,W},{u,R},{u,F},Arg6]},_,_,_) ->
+ [A2,A6] = resolve_args([Arg2,Arg6]),
+ {bs_init2,Lbl,A2,W,R,decode_field_flags(F),A6};
+resolve_inst({bs_bits_to_bytes,[Lbl,Arg2,Arg3]},_,_,_) ->
+ [A2,A3] = resolve_args([Arg2,Arg3]),
+ {bs_bits_to_bytes,Lbl,A2,A3};
+resolve_inst({bs_add=I,[Lbl,Arg2,Arg3,Arg4,Arg5]},_,_,_) ->
+ [A2,A3,A4,A5] = resolve_args([Arg2,Arg3,Arg4,Arg5]),
+ {I,Lbl,[A2,A3,A4],A5};
+
+%%
+%% New apply instructions added in April 2004 (R10B).
+%%
+resolve_inst({apply,[{u,Arity}]},_,_,_) ->
+ {apply,Arity};
+resolve_inst({apply_last,[{u,Arity},{u,D}]},_,_,_) ->
+ {apply_last,Arity,D};
+
+%%
+%% New test instruction added in April 2004 (R10B).
+%%
+resolve_inst({is_boolean=I,Args0},_,_,_) ->
+ [L|Args] = resolve_args(Args0),
+ {test,I,L,Args};
+
+%%
+%% Catches instructions that are not yet handled.
+%%
+
+resolve_inst(X,_,_,_) -> ?exit({resolve_inst,X}).
+
+%%-----------------------------------------------------------------------
+%% Resolves arguments in a generic way.
+%%-----------------------------------------------------------------------
+
+resolve_args(Args) -> [resolve_arg(A) || A <- Args].
+
+resolve_arg({u,N}) -> N;
+resolve_arg({i,N}) -> {integer,N};
+resolve_arg({atom,Atom}=A) when is_atom(Atom) -> A;
+resolve_arg(nil) -> nil;
+resolve_arg(Arg) -> Arg.
+
+%%-----------------------------------------------------------------------
+%% The purpose of the following is just to add a hook for future changes.
+%% Currently, field flags are numbers 1-2-4-8 and only two of these
+%% numbers (BSF_LITTLE 2 -- BSF_SIGNED 4) have a semantic significance;
+%% others are just hints for speeding up the execution; see "erl_bits.h".
+%%-----------------------------------------------------------------------
+
+decode_field_flags(FF) ->
+ {field_flags,FF}.
+
+%%-----------------------------------------------------------------------
+%% Each string is denoted in the assembled code by its offset into this
+%% binary. This binary contains all strings concatenated together.
+%%-----------------------------------------------------------------------
+
+beam_disasm_strings(Bin) ->
+ Bin.
+
+%%-----------------------------------------------------------------------
+%% Disassembles the attributes of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_attributes(none) -> none;
+beam_disasm_attributes(AttrBin) -> binary_to_term(AttrBin).
+
+%%-----------------------------------------------------------------------
+%% Disassembles the compilation information of a BEAM file.
+%%-----------------------------------------------------------------------
+
+beam_disasm_compilation_info(none) -> none;
+beam_disasm_compilation_info(Bin) -> binary_to_term(Bin).
+
+%%-----------------------------------------------------------------------
+%% Private Utilities
+%%-----------------------------------------------------------------------
+
+%%-----------------------------------------------------------------------
+
+lookup_key(Key,[{Key,Val}|_]) ->
+ Val;
+lookup_key(Key,[_|KVs]) ->
+ lookup_key(Key,KVs);
+lookup_key(Key,[]) ->
+ ?exit({lookup_key,{key_not_found,Key}}).
+
+%%-----------------------------------------------------------------------
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl
new file mode 100644
index 0000000000..a9958f87cd
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_flatten.erl
@@ -0,0 +1,137 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_flatten.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Converts intermediate assembly code to final format.
+
+-module(beam_flatten).
+
+-export([module/2]).
+-import(lists, [reverse/1,reverse/2,map/2]).
+
+module({Mod,Exp,Attr,Fs,Lc}, _Opt) ->
+ {ok,{Mod,Exp,Attr,map(fun function/1, Fs),Lc}}.
+
+function({function,Name,Arity,CLabel,Is0}) ->
+ Is1 = block(Is0),
+ Is = opt(Is1),
+ {function,Name,Arity,CLabel,Is}.
+
+block(Is) ->
+ block(Is, []).
+
+block([{block,Is0}|Is1], Acc) -> block(Is1, norm_block(Is0, Acc));
+block([I|Is], Acc) -> block(Is, [I|Acc]);
+block([], Acc) -> reverse(Acc).
+
+norm_block([{allocate,R,Alloc}|Is], Acc0) ->
+ case insert_alloc_in_bs_init(Acc0, Alloc) of
+ not_possible ->
+ norm_block(Is, reverse(norm_allocate(Alloc, R), Acc0));
+ Acc ->
+ norm_block(Is, Acc)
+ end;
+norm_block([I|Is], Acc) -> norm_block(Is, [norm(I)|Acc]);
+norm_block([], Acc) -> Acc.
+
+norm({set,[D],As,{bif,N}}) -> {bif,N,nofail,As,D};
+norm({set,[D],As,{bif,N,F}}) -> {bif,N,F,As,D};
+norm({set,[D],[S],move}) -> {move,S,D};
+norm({set,[D],[S],fmove}) -> {fmove,S,D};
+norm({set,[D],[S],fconv}) -> {fconv,S,D};
+norm({set,[D],[S1,S2],put_list}) -> {put_list,S1,S2,D};
+norm({set,[D],[],{put_tuple,A}}) -> {put_tuple,A,D};
+norm({set,[],[S],put}) -> {put,S};
+norm({set,[D],[],{put_string,L,S}}) -> {put_string,L,S,D};
+norm({set,[D],[S],{get_tuple_element,I}}) -> {get_tuple_element,S,I,D};
+norm({set,[],[S,D],{set_tuple_element,I}}) -> {set_tuple_element,S,D,I};
+norm({set,[D1,D2],[S],get_list}) -> {get_list,S,D1,D2};
+norm({set,[],[],remove_message}) -> remove_message;
+norm({set,[],[],fclearerror}) -> fclearerror;
+norm({set,[],[],fcheckerror}) -> {fcheckerror,{f,0}};
+norm({'%',_}=Comment) -> Comment;
+norm({'%live',R}) -> {'%live',R}.
+
+norm_allocate({_Zero,nostack,Nh,[]}, Regs) ->
+ [{test_heap,Nh,Regs}];
+norm_allocate({_Zero,nostack,Nh,Nf,[]}, Regs) ->
+ [{test_heap,alloc_list(Nh, Nf),Regs}];
+norm_allocate({zero,0,Nh,[]}, Regs) ->
+ norm_allocate({nozero,0,Nh,[]}, Regs);
+norm_allocate({zero,0,Nh,Nf,[]}, Regs) ->
+ norm_allocate({nozero,0,Nh,Nf,[]}, Regs);
+norm_allocate({zero,Ns,0,[]}, Regs) ->
+ [{allocate_zero,Ns,Regs}];
+norm_allocate({zero,Ns,Nh,[]}, Regs) ->
+ [{allocate_heap_zero,Ns,Nh,Regs}];
+norm_allocate({nozero,Ns,0,Inits}, Regs) ->
+ [{allocate,Ns,Regs}|Inits];
+norm_allocate({nozero,Ns,Nh,Inits}, Regs) ->
+ [{allocate_heap,Ns,Nh,Regs}|Inits];
+norm_allocate({nozero,Ns,Nh,Floats,Inits}, Regs) ->
+ [{allocate_heap,Ns,alloc_list(Nh, Floats),Regs}|Inits];
+norm_allocate({zero,Ns,Nh,Floats,Inits}, Regs) ->
+ [{allocate_heap_zero,Ns,alloc_list(Nh, Floats),Regs}|Inits].
+
+insert_alloc_in_bs_init([I|_]=Is, Alloc) ->
+ case is_bs_put(I) of
+ false ->
+ not_possible;
+ true ->
+ insert_alloc_1(Is, Alloc, [])
+ end.
+
+insert_alloc_1([{bs_init2,Fail,Bs,Ws,Regs,F,Dst}|Is], {_,nostack,Nh,Nf,[]}, Acc) ->
+ Al = alloc_list(Ws+Nh, Nf),
+ I = {bs_init2,Fail,Bs,Al,Regs,F,Dst},
+ reverse(Acc, [I|Is]);
+insert_alloc_1([I|Is], Alloc, Acc) ->
+ insert_alloc_1(Is, Alloc, [I|Acc]).
+
+is_bs_put({bs_put_integer,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_float,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_binary,_,_,_,_,_}) -> true;
+is_bs_put({bs_put_string,_,_}) -> true;
+is_bs_put(_) -> false.
+
+alloc_list(Words, Floats) ->
+ {alloc,[{words,Words},{floats,Floats}]}.
+
+
+%% opt(Is0) -> Is
+%% Simple peep-hole optimization to move a {move,Any,{x,0}} past
+%% any kill up to the next call instruction.
+
+opt(Is) ->
+ opt_1(Is, []).
+
+opt_1([{move,_,{x,0}}=I|Is0], Acc0) ->
+ case move_past_kill(Is0, I, Acc0) of
+ impossible -> opt_1(Is0, [I|Acc0]);
+ {Is,Acc} -> opt_1(Is, Acc)
+ end;
+opt_1([I|Is], Acc) ->
+ opt_1(Is, [I|Acc]);
+opt_1([], Acc) -> reverse(Acc).
+
+move_past_kill([{'%live',_}|Is], Move, Acc) ->
+ move_past_kill(Is, Move, Acc);
+move_past_kill([{kill,Src}|_], {move,Src,_}, _) ->
+ impossible;
+move_past_kill([{kill,_}=I|Is], Move, Acc) ->
+ move_past_kill(Is, Move, [I|Acc]);
+move_past_kill(Is, Move, Acc) ->
+ {Is,[Move|Acc]}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl
new file mode 100644
index 0000000000..fd005898b6
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_jump.erl
@@ -0,0 +1,477 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_jump.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%%% Purpose : Optimise jumps and remove unreachable code.
+
+-module(beam_jump).
+
+-export([module/2,module_labels/1,
+ is_unreachable_after/1,remove_unused_labels/1]).
+
+%%% The following optimisations are done:
+%%%
+%%% (1) This code with two identical instruction sequences
+%%%
+%%% L1: <Instruction sequence>
+%%% L2:
+%%% . . .
+%%% L3: <Instruction sequence>
+%%% L4:
+%%%
+%%% can be replaced with
+%%%
+%%% L1: jump L3
+%%% L2:
+%%% . . .
+%%% L3: <Instruction sequence>
+%%% L4
+%%%
+%%% Note: The instruction sequence must end with an instruction
+%%% such as a jump that never transfers control to the instruction
+%%% following it.
+%%%
+%%% (2) case_end, if_end, and badmatch, and function calls that cause an
+%%% exit (such as calls to exit/1) are moved to the end of the function.
+%%% The purpose is to allow further optimizations at the place from
+%%% which the code was moved.
+%%%
+%%% (3) Any unreachable code is removed. Unreachable code is code after
+%%% jump, call_last and other instructions which never transfer control
+%%% to the following instruction. Code is unreachable up to the next
+%%% *referenced* label. Note that the optimisations below might
+%%% generate more possibilities for removing unreachable code.
+%%%
+%%% (4) This code:
+%%% L1: jump L2
+%%% . . .
+%%% L2: ...
+%%%
+%%% will be changed to
+%%%
+%%% jump L2
+%%% . . .
+%%% L1:
+%%% L2: ...
+%%%
+%%% If the jump is unreachable, it will be removed according to (1).
+%%%
+%%% (5) In
+%%%
+%%% jump L1
+%%% L1:
+%%%
+%%% the jump will be removed.
+%%%
+%%% (6) If test instructions are used to skip a single jump instruction,
+%%% the test is inverted and the jump is eliminated (provided that
+%%% the test can be inverted). Example:
+%%%
+%%% is_eq L1 {x,1} {x,2}
+%%% jump L2
+%%% L1:
+%%%
+%%% will be changed to
+%%%
+%%% is_ne L2 {x,1} {x,2}
+%%%
+%%% (The label L1 will be retained if there were previous references to it.)
+%%%
+%%% (7) Some redundant uses of is_boolean/1 is optimized away.
+%%%
+%%% Terminology note: The optimisation done here is called unreachable-code
+%%% elimination, NOT dead-code elimination. Dead code elimination
+%%% means the removal of instructions that are executed, but have no visible
+%%% effect on the program state.
+%%%
+
+-import(lists, [reverse/1,reverse/2,map/2,mapfoldl/3,foldl/3,
+ last/1,foreach/2,member/2]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, _Opt) ->
+ Fs = map(fun function/1, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+module_labels({Mod,Exp,Attr,Fs,Lc}) ->
+ {Mod,Exp,Attr,map(fun function_labels/1, Fs),Lc}.
+
+function_labels({function,Name,Arity,CLabel,Asm0}) ->
+ Asm = remove_unused_labels(Asm0),
+ {function,Name,Arity,CLabel,Asm}.
+
+function({function,Name,Arity,CLabel,Asm0}) ->
+ Asm1 = share(Asm0),
+ Asm2 = bopt(Asm1),
+ Asm3 = move(Asm2),
+ Asm4 = opt(Asm3, CLabel),
+ Asm = remove_unused_labels(Asm4),
+ {function,Name,Arity,CLabel,Asm}.
+
+%%%
+%%% (1) We try to share the code for identical code segments by replacing all
+%%% occurrences except the last with jumps to the last occurrence.
+%%%
+
+share(Is) ->
+ share_1(reverse(Is), gb_trees:empty(), [], []).
+
+share_1([{label,_}=Lbl|Is], Dict, [], Acc) ->
+ share_1(Is, Dict, [], [Lbl|Acc]);
+share_1([{label,L}=Lbl|Is], Dict0, Seq, Acc) ->
+ case is_unreachable_after(last(Seq)) of
+ false ->
+ share_1(Is, Dict0, [], [Lbl|Seq ++ Acc]);
+ true ->
+ case gb_trees:lookup(Seq, Dict0) of
+ none ->
+ Dict = gb_trees:insert(Seq, L, Dict0),
+ share_1(Is, Dict, [], [Lbl|Seq ++ Acc]);
+ {value,Label} ->
+ share_1(Is, Dict0, [], [Lbl,{jump,{f,Label}}|Acc])
+ end
+ end;
+share_1([{func_info,_,_,_}=I|Is], _, [], Acc) ->
+ Is++[I|Acc];
+share_1([I|Is], Dict, Seq, Acc) ->
+ case is_unreachable_after(I) of
+ false ->
+ share_1(Is, Dict, [I|Seq], Acc);
+ true ->
+ share_1(Is, Dict, [I], Acc)
+ end.
+
+%%%
+%%% (2) Move short code sequences ending in an instruction that causes an exit
+%%% to the end of the function.
+%%%
+
+move(Is) ->
+ move_1(Is, [], []).
+
+move_1([I|Is], End, Acc) ->
+ case is_exit_instruction(I) of
+ false -> move_1(Is, End, [I|Acc]);
+ true -> move_2(I, Is, End, Acc)
+ end;
+move_1([], End, Acc) ->
+ reverse(Acc, reverse(End)).
+
+move_2(Exit, Is, End, [{block,_},{label,_},{func_info,_,_,_}|_]=Acc) ->
+ move_1(Is, End, [Exit|Acc]);
+move_2(Exit, Is, End, [{kill,_Y}|Acc]) ->
+ move_2(Exit, Is, End, Acc);
+move_2(Exit, Is, End, [{block,_}=Blk,{label,_}=Lbl,Dead|More]=Acc) ->
+ case is_unreachable_after(Dead) of
+ false ->
+ move_1(Is, End, [Exit|Acc]);
+ true ->
+ move_1([Dead|Is], [Exit,Blk,Lbl|End], More)
+ end;
+move_2(Exit, Is, End, [{label,_}=Lbl,Dead|More]=Acc) ->
+ case is_unreachable_after(Dead) of
+ false ->
+ move_1(Is, End, [Exit|Acc]);
+ true ->
+ move_1([Dead|Is], [Exit,Lbl|End], More)
+ end;
+move_2(Exit, Is, End, Acc) ->
+ move_1(Is, End, [Exit|Acc]).
+
+%%%
+%%% (7) Remove redundant is_boolean tests.
+%%%
+
+bopt(Is) ->
+ bopt_1(Is, []).
+
+bopt_1([{test,is_boolean,_,_}=I|Is], Acc0) ->
+ case opt_is_bool(I, Acc0) of
+ no -> bopt_1(Is, [I|Acc0]);
+ yes -> bopt_1(Is, Acc0);
+ {yes,Acc} -> bopt_1(Is, Acc)
+ end;
+bopt_1([I|Is], Acc) -> bopt_1(Is, [I|Acc]);
+bopt_1([], Acc) -> reverse(Acc).
+
+opt_is_bool({test,is_boolean,{f,Lbl},[Reg]}, Acc) ->
+ opt_is_bool_1(Acc, Reg, Lbl).
+
+opt_is_bool_1([{test,is_eq_exact,{f,Lbl},[Reg,{atom,true}]}|_], Reg, Lbl) ->
+ %% Instruction not needed in this context.
+ yes;
+opt_is_bool_1([{test,is_ne_exact,{f,Lbl},[Reg,{atom,true}]}|Acc], Reg, Lbl) ->
+ %% Rewrite to shorter test.
+ {yes,[{test,is_eq_exact,{f,Lbl},[Reg,{atom,false}]}|Acc]};
+opt_is_bool_1([{test,_,{f,Lbl},_}=Test|Acc0], Reg, Lbl) ->
+ case opt_is_bool_1(Acc0, Reg, Lbl) of
+ {yes,Acc} -> {yes,[Test|Acc]};
+ Other -> Other
+ end;
+opt_is_bool_1(_, _, _) -> no.
+
+%%%
+%%% (3) (4) (5) (6) Jump and unreachable code optimizations.
+%%%
+
+-record(st, {fc, %Label for function class errors.
+ entry, %Entry label (must not be moved).
+ mlbl, %Moved labels.
+ labels %Set of referenced labels.
+ }).
+
+opt([{label,Fc}|_]=Is, CLabel) ->
+ Lbls = initial_labels(Is),
+ St = #st{fc=Fc,entry=CLabel,mlbl=dict:new(),labels=Lbls},
+ opt(Is, [], St).
+
+opt([{test,Test0,{f,Lnum}=Lbl,Ops}=I|Is0], Acc, St) ->
+ case Is0 of
+ [{jump,To}|[{label,Lnum}|Is2]=Is1] ->
+ case invert_test(Test0) of
+ not_possible ->
+ opt(Is0, [I|Acc], label_used(Lbl, St));
+ Test ->
+ Is = case is_label_used(Lnum, St) of
+ true -> Is1;
+ false -> Is2
+ end,
+ opt([{test,Test,To,Ops}|Is], Acc, label_used(To, St))
+ end;
+ _Other ->
+ opt(Is0, [I|Acc], label_used(Lbl, St))
+ end;
+opt([{select_val,_R,Fail,{list,Vls}}=I|Is], Acc, St) ->
+ skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
+opt([{select_tuple_arity,_R,Fail,{list,Vls}}=I|Is], Acc, St) ->
+ skip_unreachable(Is, [I|Acc], label_used([Fail|Vls], St));
+opt([{'try',_R,Lbl}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{'catch',_R,Lbl}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{label,L}=I|Is], Acc, #st{entry=L}=St) ->
+ %% NEVER move the entry label.
+ opt(Is, [I|Acc], St);
+opt([{label,L1},{jump,{f,L2}}=I|Is], [Prev|Acc], St0) ->
+ St = St0#st{mlbl=dict:append(L2, L1, St0#st.mlbl)},
+ opt([Prev,I|Is], Acc, label_used({f,L2}, St));
+opt([{label,Lbl}=I|Is], Acc, #st{mlbl=Mlbl}=St0) ->
+ case dict:find(Lbl, Mlbl) of
+ {ok,Lbls} ->
+ %% Essential to remove the list of labels from the dictionary,
+ %% since we will rescan the inserted labels. We MUST rescan.
+ St = St0#st{mlbl=dict:erase(Lbl, Mlbl)},
+ insert_labels([Lbl|Lbls], Is, Acc, St);
+ error -> opt(Is, [I|Acc], St0)
+ end;
+opt([{jump,{f,Lbl}},{label,Lbl}=I|Is], Acc, St) ->
+ opt([I|Is], Acc, St);
+opt([{jump,Lbl}=I|Is], Acc, St) ->
+ skip_unreachable(Is, [I|Acc], label_used(Lbl, St));
+opt([{loop_rec,Lbl,_R}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bif,_Name,Lbl,_As,_R}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_put_integer,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_put_binary,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_put_float,Lbl,_Bits,_Unit,_Fl,_Val}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_final,Lbl,_R}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_init2,Lbl,_,_,_,_,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_add,Lbl,_,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([{bs_bits_to_bytes,Lbl,_,_}=I|Is], Acc, St) ->
+ opt(Is, [I|Acc], label_used(Lbl, St));
+opt([I|Is], Acc, St) ->
+ case is_unreachable_after(I) of
+ true -> skip_unreachable(Is, [I|Acc], St);
+ false -> opt(Is, [I|Acc], St)
+ end;
+opt([], Acc, #st{fc=Fc,mlbl=Mlbl}) ->
+ Code = reverse(Acc),
+ case dict:find(Fc, Mlbl) of
+ {ok,Lbls} -> insert_fc_labels(Lbls, Mlbl, Code);
+ error -> Code
+ end.
+
+insert_fc_labels([L|Ls], Mlbl, Acc0) ->
+ Acc = [{label,L}|Acc0],
+ case dict:find(L, Mlbl) of
+ error ->
+ insert_fc_labels(Ls, Mlbl, Acc);
+ {ok,Lbls} ->
+ insert_fc_labels(Lbls++Ls, Mlbl, Acc)
+ end;
+insert_fc_labels([], _, Acc) -> Acc.
+
+%% invert_test(Test0) -> not_possible | Test
+
+invert_test(is_ge) -> is_lt;
+invert_test(is_lt) -> is_ge;
+invert_test(is_eq) -> is_ne;
+invert_test(is_ne) -> is_eq;
+invert_test(is_eq_exact) -> is_ne_exact;
+invert_test(is_ne_exact) -> is_eq_exact;
+invert_test(_) -> not_possible.
+
+insert_labels([L|Ls], Is, [{jump,{f,L}}|Acc], St) ->
+ insert_labels(Ls, [{label,L}|Is], Acc, St);
+insert_labels([L|Ls], Is, Acc, St) ->
+ insert_labels(Ls, [{label,L}|Is], Acc, St);
+insert_labels([], Is, Acc, St) ->
+ opt(Is, Acc, St).
+
+%% Skip unreachable code up to the next referenced label.
+
+skip_unreachable([{label,L}|Is], [{jump,{f,L}}|Acc], St) ->
+ opt([{label,L}|Is], Acc, St);
+skip_unreachable([{label,L}|Is], Acc, St) ->
+ case is_label_used(L, St) of
+ true -> opt([{label,L}|Is], Acc, St);
+ false -> skip_unreachable(Is, Acc, St)
+ end;
+skip_unreachable([_|Is], Acc, St) ->
+ skip_unreachable(Is, Acc, St);
+skip_unreachable([], Acc, St) ->
+ opt([], Acc, St).
+
+%% Add one or more label to the set of used labels.
+
+label_used({f,0}, St) -> St;
+label_used({f,L}, St) -> St#st{labels=gb_sets:add(L, St#st.labels)};
+label_used([H|T], St0) -> label_used(T, label_used(H, St0));
+label_used([], St) -> St;
+label_used(_Other, St) -> St.
+
+%% Test if label is used.
+
+is_label_used(L, St) ->
+ gb_sets:is_member(L, St#st.labels).
+
+%% is_unreachable_after(Instruction) -> true|false
+%% Test whether the code after Instruction is unreachable.
+
+is_unreachable_after({func_info,_M,_F,_A}) -> true;
+is_unreachable_after(return) -> true;
+is_unreachable_after({call_ext_last,_Ar,_ExtFunc,_D}) -> true;
+is_unreachable_after({call_ext_only,_Ar,_ExtFunc}) -> true;
+is_unreachable_after({call_last,_Ar,_Lbl,_D}) -> true;
+is_unreachable_after({call_only,_Ar,_Lbl}) -> true;
+is_unreachable_after({apply_last,_Ar,_N}) -> true;
+is_unreachable_after({jump,_Lbl}) -> true;
+is_unreachable_after({select_val,_R,_Lbl,_Cases}) -> true;
+is_unreachable_after({select_tuple_arity,_R,_Lbl,_Cases}) -> true;
+is_unreachable_after({loop_rec_end,_}) -> true;
+is_unreachable_after({wait,_}) -> true;
+is_unreachable_after(I) -> is_exit_instruction(I).
+
+%% is_exit_instruction(Instruction) -> true|false
+%% Test whether the instruction Instruction always
+%% causes an exit/failure.
+
+is_exit_instruction({call_ext,_,{extfunc,M,F,A}}) ->
+ is_exit_instruction_1(M, F, A);
+is_exit_instruction({call_ext_last,_,{extfunc,M,F,A},_}) ->
+ is_exit_instruction_1(M, F, A);
+is_exit_instruction({call_ext_only,_,{extfunc,M,F,A}}) ->
+ is_exit_instruction_1(M, F, A);
+is_exit_instruction(if_end) -> true;
+is_exit_instruction({case_end,_}) -> true;
+is_exit_instruction({try_case_end,_}) -> true;
+is_exit_instruction({badmatch,_}) -> true;
+is_exit_instruction(_) -> false.
+
+is_exit_instruction_1(erlang, exit, 1) -> true;
+is_exit_instruction_1(erlang, throw, 1) -> true;
+is_exit_instruction_1(erlang, error, 1) -> true;
+is_exit_instruction_1(erlang, error, 2) -> true;
+is_exit_instruction_1(erlang, fault, 1) -> true;
+is_exit_instruction_1(erlang, fault, 2) -> true;
+is_exit_instruction_1(_, _, _) -> false.
+
+%% remove_unused_labels(Instructions0) -> Instructions
+%% Remove all unused labels.
+
+remove_unused_labels(Is) ->
+ Used0 = initial_labels(Is),
+ Used = foldl(fun ulbl/2, Used0, Is),
+ rem_unused(Is, Used, []).
+
+rem_unused([{label,Lbl}=I|Is], Used, Acc) ->
+ case gb_sets:is_member(Lbl, Used) of
+ false -> rem_unused(Is, Used, Acc);
+ true -> rem_unused(Is, Used, [I|Acc])
+ end;
+rem_unused([I|Is], Used, Acc) ->
+ rem_unused(Is, Used, [I|Acc]);
+rem_unused([], _, Acc) -> reverse(Acc).
+
+initial_labels(Is) ->
+ initial_labels(Is, []).
+
+initial_labels([{label,Lbl}|Is], Acc) ->
+ initial_labels(Is, [Lbl|Acc]);
+initial_labels([{func_info,_,_,_},{label,Lbl}|_], Acc) ->
+ gb_sets:from_list([Lbl|Acc]).
+
+ulbl({test,_,Fail,_}, Used) ->
+ mark_used(Fail, Used);
+ulbl({select_val,_,Fail,{list,Vls}}, Used) ->
+ mark_used_list(Vls, mark_used(Fail, Used));
+ulbl({select_tuple_arity,_,Fail,{list,Vls}}, Used) ->
+ mark_used_list(Vls, mark_used(Fail, Used));
+ulbl({'try',_,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({'catch',_,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({jump,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({loop_rec,Lbl,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({loop_rec_end,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({wait,Lbl}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({wait_timeout,Lbl,_To}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bif,_Name,Lbl,_As,_R}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_init2,Lbl,_,_,_,_,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_put_integer,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_put_float,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_put_binary,Lbl,_Bits,_Unit,_Fl,_Val}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_final,Lbl,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_add,Lbl,_,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl({bs_bits_to_bytes,Lbl,_,_}, Used) ->
+ mark_used(Lbl, Used);
+ulbl(_, Used) -> Used.
+
+mark_used({f,0}, Used) -> Used;
+mark_used({f,L}, Used) -> gb_sets:add(L, Used);
+mark_used(_, Used) -> Used.
+
+mark_used_list([H|T], Used) ->
+ mark_used_list(T, mark_used(H, Used));
+mark_used_list([], Used) -> Used.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl
new file mode 100644
index 0000000000..006b8c551a
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_listing.erl
@@ -0,0 +1,117 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_listing.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+-module(beam_listing).
+
+-export([module/2]).
+
+-include("v3_life.hrl").
+
+-import(lists, [foreach/2]).
+
+module(File, Core) when element(1, Core) == c_module ->
+ %% This is a core module.
+ io:put_chars(File, core_pp:format(Core));
+module(File, Kern) when element(1, Kern) == k_mdef ->
+ %% This is a kernel module.
+ io:put_chars(File, v3_kernel_pp:format(Kern));
+ %%io:put_chars(File, io_lib:format("~p~n", [Kern]));
+module(File, {Mod,Exp,Attr,Kern}) ->
+ %% This is output from beam_life (v3).
+ io:fwrite(File, "~w.~n~p.~n~p.~n", [Mod,Exp,Attr]),
+ foreach(fun (F) -> function(File, F) end, Kern);
+module(Stream, {Mod,Exp,Attr,Code,NumLabels}) ->
+ %% This is output from beam_codegen.
+ io:format(Stream, "{module, ~s}. %% version = ~w\n",
+ [Mod, beam_opcodes:format_number()]),
+ io:format(Stream, "\n{exports, ~p}.\n", [Exp]),
+ io:format(Stream, "\n{attributes, ~p}.\n", [Attr]),
+ io:format(Stream, "\n{labels, ~p}.\n", [NumLabels]),
+ foreach(
+ fun ({function,Name,Arity,Entry,Asm}) ->
+ io:format(Stream, "\n\n{function, ~w, ~w, ~w}.\n",
+ [Name, Arity, Entry]),
+ foreach(fun(Op) -> print_op(Stream, Op) end, Asm) end,
+ Code);
+module(Stream, {Mod,Exp,Inter}) ->
+ %% Other kinds of intermediate formats.
+ io:fwrite(Stream, "~w.~n~p.~n", [Mod,Exp]),
+ foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Inter);
+module(Stream, [_|_]=Fs) ->
+ %% Form-based abstract format.
+ foreach(fun (F) -> io:format(Stream, "~p.\n", [F]) end, Fs).
+
+print_op(Stream, Label) when element(1, Label) == label ->
+ io:format(Stream, " ~p.\n", [Label]);
+print_op(Stream, Op) ->
+ io:format(Stream, " ~p.\n", [Op]).
+
+function(File, {function,Name,Arity,Args,Body,Vdb}) ->
+ io:nl(File),
+ io:format(File, "function ~p/~p.\n", [Name,Arity]),
+ io:format(File, " ~p.\n", [Args]),
+ print_vdb(File, Vdb),
+ put(beam_listing_nl, true),
+ foreach(fun(F) -> format(File, F, []) end, Body),
+ nl(File),
+ erase(beam_listing_nl).
+
+format(File, #l{ke=Ke,i=I,vdb=Vdb}, Ind) ->
+ nl(File),
+ ind_format(File, Ind, "~p ", [I]),
+ print_vdb(File, Vdb),
+ nl(File),
+ format(File, Ke, Ind);
+format(File, Tuple, Ind) when is_tuple(Tuple) ->
+ ind_format(File, Ind, "{", []),
+ format_list(File, tuple_to_list(Tuple), [$\s|Ind]),
+ ind_format(File, Ind, "}", []);
+format(File, List, Ind) when is_list(List) ->
+ ind_format(File, Ind, "[", []),
+ format_list(File, List, [$\s|Ind]),
+ ind_format(File, Ind, "]", []);
+format(File, F, Ind) ->
+ ind_format(File, Ind, "~p", [F]).
+
+format_list(File, [F], Ind) ->
+ format(File, F, Ind);
+format_list(File, [F|Fs], Ind) ->
+ format(File, F, Ind),
+ ind_format(File, Ind, ",", []),
+ format_list(File, Fs, Ind);
+format_list(_, [], _) -> ok.
+
+
+print_vdb(File, [{Var,F,E}|Vs]) ->
+ io:format(File, "~p:~p..~p ", [Var,F,E]),
+ print_vdb(File, Vs);
+print_vdb(_, []) -> ok.
+
+ind_format(File, Ind, Format, Args) ->
+ case get(beam_listing_nl) of
+ true ->
+ put(beam_listing_nl, false),
+ io:put_chars(File, Ind);
+ false -> ok
+ end,
+ io:format(File, Format, Args).
+
+nl(File) ->
+ case put(beam_listing_nl, true) of
+ true -> ok;
+ false -> io:nl(File)
+ end.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl
new file mode 100644
index 0000000000..a4f5fd34d2
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.erl
@@ -0,0 +1,240 @@
+-module(beam_opcodes).
+%% Warning: Do not edit this file. It was automatically
+%% generated by 'beam_makeops' on Wed Nov 24 17:52:43 2004.
+
+-export([format_number/0]).
+-export([opcode/2,opname/1]).
+
+format_number() -> 0.
+
+opcode(label, 1) -> 1;
+opcode(func_info, 3) -> 2;
+opcode(int_code_end, 0) -> 3;
+opcode(call, 2) -> 4;
+opcode(call_last, 3) -> 5;
+opcode(call_only, 2) -> 6;
+opcode(call_ext, 2) -> 7;
+opcode(call_ext_last, 3) -> 8;
+opcode(bif0, 2) -> 9;
+opcode(bif1, 4) -> 10;
+opcode(bif2, 5) -> 11;
+opcode(allocate, 2) -> 12;
+opcode(allocate_heap, 3) -> 13;
+opcode(allocate_zero, 2) -> 14;
+opcode(allocate_heap_zero, 3) -> 15;
+opcode(test_heap, 2) -> 16;
+opcode(init, 1) -> 17;
+opcode(deallocate, 1) -> 18;
+opcode(return, 0) -> 19;
+opcode(send, 0) -> 20;
+opcode(remove_message, 0) -> 21;
+opcode(timeout, 0) -> 22;
+opcode(loop_rec, 2) -> 23;
+opcode(loop_rec_end, 1) -> 24;
+opcode(wait, 1) -> 25;
+opcode(wait_timeout, 2) -> 26;
+opcode(m_plus, 4) -> 27;
+opcode(m_minus, 4) -> 28;
+opcode(m_times, 4) -> 29;
+opcode(m_div, 4) -> 30;
+opcode(int_div, 4) -> 31;
+opcode(int_rem, 4) -> 32;
+opcode(int_band, 4) -> 33;
+opcode(int_bor, 4) -> 34;
+opcode(int_bxor, 4) -> 35;
+opcode(int_bsl, 4) -> 36;
+opcode(int_bsr, 4) -> 37;
+opcode(int_bnot, 3) -> 38;
+opcode(is_lt, 3) -> 39;
+opcode(is_ge, 3) -> 40;
+opcode(is_eq, 3) -> 41;
+opcode(is_ne, 3) -> 42;
+opcode(is_eq_exact, 3) -> 43;
+opcode(is_ne_exact, 3) -> 44;
+opcode(is_integer, 2) -> 45;
+opcode(is_float, 2) -> 46;
+opcode(is_number, 2) -> 47;
+opcode(is_atom, 2) -> 48;
+opcode(is_pid, 2) -> 49;
+opcode(is_reference, 2) -> 50;
+opcode(is_port, 2) -> 51;
+opcode(is_nil, 2) -> 52;
+opcode(is_binary, 2) -> 53;
+opcode(is_constant, 2) -> 54;
+opcode(is_list, 2) -> 55;
+opcode(is_nonempty_list, 2) -> 56;
+opcode(is_tuple, 2) -> 57;
+opcode(test_arity, 3) -> 58;
+opcode(select_val, 3) -> 59;
+opcode(select_tuple_arity, 3) -> 60;
+opcode(jump, 1) -> 61;
+opcode('catch', 2) -> 62;
+opcode(catch_end, 1) -> 63;
+opcode(move, 2) -> 64;
+opcode(get_list, 3) -> 65;
+opcode(get_tuple_element, 3) -> 66;
+opcode(set_tuple_element, 3) -> 67;
+opcode(put_string, 3) -> 68;
+opcode(put_list, 3) -> 69;
+opcode(put_tuple, 2) -> 70;
+opcode(put, 1) -> 71;
+opcode(badmatch, 1) -> 72;
+opcode(if_end, 0) -> 73;
+opcode(case_end, 1) -> 74;
+opcode(call_fun, 1) -> 75;
+opcode(make_fun, 3) -> 76;
+opcode(is_function, 2) -> 77;
+opcode(call_ext_only, 2) -> 78;
+opcode(bs_start_match, 2) -> 79;
+opcode(bs_get_integer, 5) -> 80;
+opcode(bs_get_float, 5) -> 81;
+opcode(bs_get_binary, 5) -> 82;
+opcode(bs_skip_bits, 4) -> 83;
+opcode(bs_test_tail, 2) -> 84;
+opcode(bs_save, 1) -> 85;
+opcode(bs_restore, 1) -> 86;
+opcode(bs_init, 2) -> 87;
+opcode(bs_final, 2) -> 88;
+opcode(bs_put_integer, 5) -> 89;
+opcode(bs_put_binary, 5) -> 90;
+opcode(bs_put_float, 5) -> 91;
+opcode(bs_put_string, 2) -> 92;
+opcode(bs_need_buf, 1) -> 93;
+opcode(fclearerror, 0) -> 94;
+opcode(fcheckerror, 1) -> 95;
+opcode(fmove, 2) -> 96;
+opcode(fconv, 2) -> 97;
+opcode(fadd, 4) -> 98;
+opcode(fsub, 4) -> 99;
+opcode(fmul, 4) -> 100;
+opcode(fdiv, 4) -> 101;
+opcode(fnegate, 3) -> 102;
+opcode(make_fun2, 1) -> 103;
+opcode('try', 2) -> 104;
+opcode(try_end, 1) -> 105;
+opcode(try_case, 1) -> 106;
+opcode(try_case_end, 1) -> 107;
+opcode(raise, 2) -> 108;
+opcode(bs_init2, 6) -> 109;
+opcode(bs_bits_to_bytes, 3) -> 110;
+opcode(bs_add, 5) -> 111;
+opcode(apply, 1) -> 112;
+opcode(apply_last, 2) -> 113;
+opcode(is_boolean, 2) -> 114;
+opcode(Name, Arity) -> erlang:error(badarg, [Name,Arity]).
+
+opname(1) -> {label,1};
+opname(2) -> {func_info,3};
+opname(3) -> {int_code_end,0};
+opname(4) -> {call,2};
+opname(5) -> {call_last,3};
+opname(6) -> {call_only,2};
+opname(7) -> {call_ext,2};
+opname(8) -> {call_ext_last,3};
+opname(9) -> {bif0,2};
+opname(10) -> {bif1,4};
+opname(11) -> {bif2,5};
+opname(12) -> {allocate,2};
+opname(13) -> {allocate_heap,3};
+opname(14) -> {allocate_zero,2};
+opname(15) -> {allocate_heap_zero,3};
+opname(16) -> {test_heap,2};
+opname(17) -> {init,1};
+opname(18) -> {deallocate,1};
+opname(19) -> {return,0};
+opname(20) -> {send,0};
+opname(21) -> {remove_message,0};
+opname(22) -> {timeout,0};
+opname(23) -> {loop_rec,2};
+opname(24) -> {loop_rec_end,1};
+opname(25) -> {wait,1};
+opname(26) -> {wait_timeout,2};
+opname(27) -> {m_plus,4};
+opname(28) -> {m_minus,4};
+opname(29) -> {m_times,4};
+opname(30) -> {m_div,4};
+opname(31) -> {int_div,4};
+opname(32) -> {int_rem,4};
+opname(33) -> {int_band,4};
+opname(34) -> {int_bor,4};
+opname(35) -> {int_bxor,4};
+opname(36) -> {int_bsl,4};
+opname(37) -> {int_bsr,4};
+opname(38) -> {int_bnot,3};
+opname(39) -> {is_lt,3};
+opname(40) -> {is_ge,3};
+opname(41) -> {is_eq,3};
+opname(42) -> {is_ne,3};
+opname(43) -> {is_eq_exact,3};
+opname(44) -> {is_ne_exact,3};
+opname(45) -> {is_integer,2};
+opname(46) -> {is_float,2};
+opname(47) -> {is_number,2};
+opname(48) -> {is_atom,2};
+opname(49) -> {is_pid,2};
+opname(50) -> {is_reference,2};
+opname(51) -> {is_port,2};
+opname(52) -> {is_nil,2};
+opname(53) -> {is_binary,2};
+opname(54) -> {is_constant,2};
+opname(55) -> {is_list,2};
+opname(56) -> {is_nonempty_list,2};
+opname(57) -> {is_tuple,2};
+opname(58) -> {test_arity,3};
+opname(59) -> {select_val,3};
+opname(60) -> {select_tuple_arity,3};
+opname(61) -> {jump,1};
+opname(62) -> {'catch',2};
+opname(63) -> {catch_end,1};
+opname(64) -> {move,2};
+opname(65) -> {get_list,3};
+opname(66) -> {get_tuple_element,3};
+opname(67) -> {set_tuple_element,3};
+opname(68) -> {put_string,3};
+opname(69) -> {put_list,3};
+opname(70) -> {put_tuple,2};
+opname(71) -> {put,1};
+opname(72) -> {badmatch,1};
+opname(73) -> {if_end,0};
+opname(74) -> {case_end,1};
+opname(75) -> {call_fun,1};
+opname(76) -> {make_fun,3};
+opname(77) -> {is_function,2};
+opname(78) -> {call_ext_only,2};
+opname(79) -> {bs_start_match,2};
+opname(80) -> {bs_get_integer,5};
+opname(81) -> {bs_get_float,5};
+opname(82) -> {bs_get_binary,5};
+opname(83) -> {bs_skip_bits,4};
+opname(84) -> {bs_test_tail,2};
+opname(85) -> {bs_save,1};
+opname(86) -> {bs_restore,1};
+opname(87) -> {bs_init,2};
+opname(88) -> {bs_final,2};
+opname(89) -> {bs_put_integer,5};
+opname(90) -> {bs_put_binary,5};
+opname(91) -> {bs_put_float,5};
+opname(92) -> {bs_put_string,2};
+opname(93) -> {bs_need_buf,1};
+opname(94) -> {fclearerror,0};
+opname(95) -> {fcheckerror,1};
+opname(96) -> {fmove,2};
+opname(97) -> {fconv,2};
+opname(98) -> {fadd,4};
+opname(99) -> {fsub,4};
+opname(100) -> {fmul,4};
+opname(101) -> {fdiv,4};
+opname(102) -> {fnegate,3};
+opname(103) -> {make_fun2,1};
+opname(104) -> {'try',2};
+opname(105) -> {try_end,1};
+opname(106) -> {try_case,1};
+opname(107) -> {try_case_end,1};
+opname(108) -> {raise,2};
+opname(109) -> {bs_init2,6};
+opname(110) -> {bs_bits_to_bytes,3};
+opname(111) -> {bs_add,5};
+opname(112) -> {apply,1};
+opname(113) -> {apply_last,2};
+opname(114) -> {is_boolean,2};
+opname(Number) -> erlang:error(badarg, [Number]).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl
new file mode 100644
index 0000000000..1ad0887314
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_opcodes.hrl
@@ -0,0 +1,12 @@
+%% Warning: Do not edit this file. It was automatically
+%% generated by 'beam_makeops' on Wed Nov 24 17:52:43 2004.
+
+-define(tag_u, 0).
+-define(tag_i, 1).
+-define(tag_a, 2).
+-define(tag_x, 3).
+-define(tag_y, 4).
+-define(tag_f, 5).
+-define(tag_h, 6).
+-define(tag_z, 7).
+
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl
new file mode 100644
index 0000000000..7d288b249c
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_type.erl
@@ -0,0 +1,551 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_type.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Purpose : Type-based optimisations.
+
+-module(beam_type).
+
+-export([module/2]).
+
+-import(lists, [map/2,foldl/3,reverse/1,reverse/2,filter/2,member/2]).
+
+module({Mod,Exp,Attr,Fs0,Lc}, Opt) ->
+ AllowFloatOpts = not member(no_float_opt, Opt),
+ Fs = map(fun(F) -> function(F, AllowFloatOpts) end, Fs0),
+ {ok,{Mod,Exp,Attr,Fs,Lc}}.
+
+function({function,Name,Arity,CLabel,Asm0}, AllowFloatOpts) ->
+ Asm = opt(Asm0, AllowFloatOpts, [], tdb_new()),
+ {function,Name,Arity,CLabel,Asm}.
+
+%% opt([Instruction], AllowFloatOpts, Accumulator, TypeDb) -> {[Instruction'],TypeDb'}
+%% Keep track of type information; try to simplify.
+
+opt([{block,Body1}|Is], AllowFloatOpts, [{block,Body0}|Acc], Ts0) ->
+ {Body2,Ts} = simplify(Body1, Ts0, AllowFloatOpts),
+ Body = beam_block:merge_blocks(Body0, Body2),
+ opt(Is, AllowFloatOpts, [{block,Body}|Acc], Ts);
+opt([{block,Body0}|Is], AllowFloatOpts, Acc, Ts0) ->
+ {Body,Ts} = simplify(Body0, Ts0, AllowFloatOpts),
+ opt(Is, AllowFloatOpts, [{block,Body}|Acc], Ts);
+opt([I0|Is], AllowFloatOpts, Acc, Ts0) ->
+ case simplify([I0], Ts0, AllowFloatOpts) of
+ {[],Ts} -> opt(Is, AllowFloatOpts, Acc, Ts);
+ {[I],Ts} -> opt(Is, AllowFloatOpts, [I|Acc], Ts)
+ end;
+opt([], _, Acc, _) -> reverse(Acc).
+
+%% simplify(Instruction, TypeDb, AllowFloatOpts) -> NewInstruction
+%% Simplify an instruction using type information (this is
+%% technically a "strength reduction").
+
+simplify(Is, TypeDb, false) ->
+ simplify(Is, TypeDb, no_float_opt, []);
+simplify(Is, TypeDb, true) ->
+ case are_live_regs_determinable(Is) of
+ false -> simplify(Is, TypeDb, no_float_opt, []);
+ true -> simplify(Is, TypeDb, [], [])
+ end.
+
+simplify([{set,[D],[{integer,Index},Reg],{bif,element,_}}=I0|Is]=Is0, Ts0, Rs0, Acc0) ->
+ I = case max_tuple_size(Reg, Ts0) of
+ Sz when 0 < Index, Index =< Sz ->
+ {set,[D],[Reg],{get_tuple_element,Index-1}};
+ _Other -> I0
+ end,
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
+simplify([{set,[D0],[A],{bif,'-',{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0)
+ when Rs0 =/= no_float_opt ->
+ case tdb_find(A, Ts0) of
+ float ->
+ {Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
+ {D,Rs} = find_dest(D0, Rs1),
+ Areg = fetch_reg(A, Rs),
+ Acc = [{set,[D],[Areg],{bif,fnegate,{f,0}}}|clearerror(Acc1)],
+ Ts = tdb_update([{D0,float}], Ts0),
+ simplify(Is, Ts, Rs, Acc);
+ _Other ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)])
+ end;
+simplify([{set,[_],[_],{bif,_,{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0) ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
+simplify([{set,[D0],[A,B],{bif,Op0,{f,0}}}=I|Is]=Is0, Ts0, Rs0, Acc0)
+ when Rs0 =/= no_float_opt ->
+ case float_op(Op0, A, B, Ts0) of
+ no ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|checkerror(Acc)]);
+ {yes,Op} ->
+ {Rs1,Acc1} = load_reg(A, Ts0, Rs0, Acc0),
+ {Rs2,Acc2} = load_reg(B, Ts0, Rs1, Acc1),
+ {D,Rs} = find_dest(D0, Rs2),
+ Areg = fetch_reg(A, Rs),
+ Breg = fetch_reg(B, Rs),
+ Acc = [{set,[D],[Areg,Breg],{bif,Op,{f,0}}}|clearerror(Acc2)],
+ Ts = tdb_update([{D0,float}], Ts0),
+ simplify(Is, Ts, Rs, Acc)
+ end;
+simplify([{set,[D],[TupleReg],{get_tuple_element,0}}=I|Is0], Ts0, Rs0, Acc0) ->
+ case tdb_find(TupleReg, Ts0) of
+ {tuple,_,[Contents]} ->
+ Ts = tdb_update([{D,Contents}], Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is0, Ts, Rs, [{set,[D],[Contents],move}|Acc]);
+ _ ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is0, Ts, Rs, [I|checkerror(Acc)])
+ end;
+simplify([{set,_,_,{'catch',_}}=I|Is]=Is0, _Ts, Rs0, Acc0) ->
+ Acc = flush_all(Rs0, Is0, Acc0),
+ simplify(Is, tdb_new(), Rs0, [I|Acc]);
+simplify([{test,is_tuple,_,[R]}=I|Is], Ts, Rs, Acc) ->
+ case tdb_find(R, Ts) of
+ {tuple,_,_} -> simplify(Is, Ts, Rs, Acc);
+ _ ->
+ simplify(Is, Ts, Rs, [I|Acc])
+ end;
+simplify([{test,test_arity,_,[R,Arity]}=I|Is], Ts0, Rs, Acc) ->
+ case tdb_find(R, Ts0) of
+ {tuple,Arity,_} ->
+ simplify(Is, Ts0, Rs, Acc);
+ _Other ->
+ Ts = update(I, Ts0),
+ simplify(Is, Ts, Rs, [I|Acc])
+ end;
+simplify([{test,is_eq_exact,Fail,[R,{atom,_}=Atom]}=I|Is0], Ts0, Rs0, Acc0) ->
+ Acc1 = case tdb_find(R, Ts0) of
+ {atom,_}=Atom -> Acc0;
+ {atom,_} -> [{jump,Fail}|Acc0];
+ _ -> [I|Acc0]
+ end,
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc1),
+ simplify(Is0, Ts, Rs, Acc);
+simplify([I|Is]=Is0, Ts0, Rs0, Acc0) ->
+ Ts = update(I, Ts0),
+ {Rs,Acc} = flush(Rs0, Is0, Acc0),
+ simplify(Is, Ts, Rs, [I|Acc]);
+simplify([], Ts, Rs, Acc) ->
+ Is0 = reverse(flush_all(Rs, [], Acc)),
+ Is1 = opt_fmoves(Is0, []),
+ Is = add_ftest_heap(Is1),
+ {Is,Ts}.
+
+opt_fmoves([{set,[{x,_}=R],[{fr,_}]=Src,fmove}=I1,
+ {set,[{y,_}]=Dst,[{x,_}=R],move}=I2|Is], Acc) ->
+ case beam_block:is_killed(R, Is) of
+ false -> opt_fmoves(Is, [I2,I1|Acc]);
+ true -> opt_fmoves(Is, [{set,Dst,Src,fmove}|Acc])
+ end;
+opt_fmoves([I|Is], Acc) ->
+ opt_fmoves(Is, [I|Acc]);
+opt_fmoves([], Acc) -> reverse(Acc).
+
+clearerror(Is) ->
+ clearerror(Is, Is).
+
+clearerror([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
+clearerror([{set,[],[],fcheckerror}|_], OrigIs) -> [{set,[],[],fclearerror}|OrigIs];
+clearerror([_|Is], OrigIs) -> clearerror(Is, OrigIs);
+clearerror([], OrigIs) -> [{set,[],[],fclearerror}|OrigIs].
+
+%% update(Instruction, TypeDb) -> NewTypeDb
+%% Update the type database to account for executing an instruction.
+%%
+%% First the cases for instructions inside basic blocks.
+update({set,[D],[S],move}, Ts0) ->
+ Ops = case tdb_find(S, Ts0) of
+ error -> [{D,kill}];
+ Info -> [{D,Info}]
+ end,
+ tdb_update(Ops, Ts0);
+update({set,[D],[{integer,I},Reg],{bif,element,_}}, Ts0) ->
+ tdb_update([{Reg,{tuple,I,[]}},{D,kill}], Ts0);
+update({set,[D],[_Index,Reg],{bif,element,_}}, Ts0) ->
+ tdb_update([{Reg,{tuple,0,[]}},{D,kill}], Ts0);
+update({set,[D],[S],{get_tuple_element,0}}, Ts) ->
+ tdb_update([{D,{tuple_element,S,0}}], Ts);
+update({set,[D],[S],{bif,float,{f,0}}}, Ts0) ->
+ %% Make sure we reject non-numeric literal argument.
+ case possibly_numeric(S) of
+ true -> tdb_update([{D,float}], Ts0);
+ false -> Ts0
+ end;
+update({set,[D],[S1,S2],{bif,'/',{f,0}}}, Ts0) ->
+ %% Make sure we reject non-numeric literals.
+ case possibly_numeric(S1) andalso possibly_numeric(S2) of
+ true -> tdb_update([{D,float}], Ts0);
+ false -> Ts0
+ end;
+update({set,[D],[S1,S2],{bif,Op,{f,0}}}, Ts0) ->
+ case arith_op(Op) of
+ no ->
+ tdb_update([{D,kill}], Ts0);
+ {yes,_} ->
+ case {tdb_find(S1, Ts0),tdb_find(S2, Ts0)} of
+ {float,_} -> tdb_update([{D,float}], Ts0);
+ {_,float} -> tdb_update([{D,float}], Ts0);
+ {_,_} -> tdb_update([{D,kill}], Ts0)
+ end
+ end;
+update({set,[],_Src,_Op}, Ts0) -> Ts0;
+update({set,[D],_Src,_Op}, Ts0) ->
+ tdb_update([{D,kill}], Ts0);
+update({set,[D1,D2],_Src,_Op}, Ts0) ->
+ tdb_update([{D1,kill},{D2,kill}], Ts0);
+update({allocate,_,_}, Ts) -> Ts;
+update({init,D}, Ts) ->
+ tdb_update([{D,kill}], Ts);
+update({kill,D}, Ts) ->
+ tdb_update([{D,kill}], Ts);
+update({'%live',_}, Ts) -> Ts;
+
+%% Instructions outside of blocks.
+update({test,is_float,_Fail,[Src]}, Ts0) ->
+ tdb_update([{Src,float}], Ts0);
+update({test,test_arity,_Fail,[Src,Arity]}, Ts0) ->
+ tdb_update([{Src,{tuple,Arity,[]}}], Ts0);
+update({test,is_eq_exact,_,[Reg,{atom,_}=Atom]}, Ts) ->
+ case tdb_find(Reg, Ts) of
+ error ->
+ Ts;
+ {tuple_element,TupleReg,0} ->
+ tdb_update([{TupleReg,{tuple,1,[Atom]}}], Ts);
+ _ ->
+ Ts
+ end;
+update({test,_Test,_Fail,_Other}, Ts) -> Ts;
+update({call_ext,1,{extfunc,math,Math,1}}, Ts) ->
+ case is_math_bif(Math, 1) of
+ true -> tdb_update([{{x,0},float}], Ts);
+ false -> tdb_kill_xregs(Ts)
+ end;
+update({call_ext,2,{extfunc,math,Math,2}}, Ts) ->
+ case is_math_bif(Math, 2) of
+ true -> tdb_update([{{x,0},float}], Ts);
+ false -> tdb_kill_xregs(Ts)
+ end;
+update({call_ext,3,{extfunc,erlang,setelement,3}}, Ts0) ->
+ Op = case tdb_find({x,1}, Ts0) of
+ error -> kill;
+ Info -> Info
+ end,
+ Ts1 = tdb_kill_xregs(Ts0),
+ tdb_update([{{x,0},Op}], Ts1);
+update({call,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
+update({call_ext,_Arity,_Func}, Ts) -> tdb_kill_xregs(Ts);
+update({make_fun2,_,_,_,_}, Ts) -> tdb_kill_xregs(Ts);
+
+%% The instruction is unknown. Kill all information.
+update(_I, _Ts) -> tdb_new().
+
+is_math_bif(cos, 1) -> true;
+is_math_bif(cosh, 1) -> true;
+is_math_bif(sin, 1) -> true;
+is_math_bif(sinh, 1) -> true;
+is_math_bif(tan, 1) -> true;
+is_math_bif(tanh, 1) -> true;
+is_math_bif(acos, 1) -> true;
+is_math_bif(acosh, 1) -> true;
+is_math_bif(asin, 1) -> true;
+is_math_bif(asinh, 1) -> true;
+is_math_bif(atan, 1) -> true;
+is_math_bif(atanh, 1) -> true;
+is_math_bif(erf, 1) -> true;
+is_math_bif(erfc, 1) -> true;
+is_math_bif(exp, 1) -> true;
+is_math_bif(log, 1) -> true;
+is_math_bif(log10, 1) -> true;
+is_math_bif(sqrt, 1) -> true;
+is_math_bif(atan2, 2) -> true;
+is_math_bif(pow, 2) -> true;
+is_math_bif(pi, 0) -> true;
+is_math_bif(_, _) -> false.
+
+%% Reject non-numeric literals.
+possibly_numeric({x,_}) -> true;
+possibly_numeric({y,_}) -> true;
+possibly_numeric({integer,_}) -> true;
+possibly_numeric({float,_}) -> true;
+possibly_numeric(_) -> false.
+
+max_tuple_size(Reg, Ts) ->
+ case tdb_find(Reg, Ts) of
+ {tuple,Sz,_} -> Sz;
+ _Other -> 0
+ end.
+
+float_op('/', A, B, _) ->
+ case possibly_numeric(A) andalso possibly_numeric(B) of
+ true -> {yes,fdiv};
+ false -> no
+ end;
+float_op(Op, {float,_}, B, _) ->
+ case possibly_numeric(B) of
+ true -> arith_op(Op);
+ false -> no
+ end;
+float_op(Op, A, {float,_}, _) ->
+ case possibly_numeric(A) of
+ true -> arith_op(Op);
+ false -> no
+ end;
+float_op(Op, A, B, Ts) ->
+ case {tdb_find(A, Ts),tdb_find(B, Ts)} of
+ {float,_} -> arith_op(Op);
+ {_,float} -> arith_op(Op);
+ {_,_} -> no
+ end.
+
+find_dest(V, Rs0) ->
+ case find_reg(V, Rs0) of
+ {ok,FR} ->
+ {FR,mark(V, Rs0, dirty)};
+ error ->
+ Rs = put_reg(V, Rs0, dirty),
+ {ok,FR} = find_reg(V, Rs),
+ {FR,Rs}
+ end.
+
+load_reg({float,_}=F, _, Rs0, Is0) ->
+ Rs = put_reg(F, Rs0, clean),
+ {ok,FR} = find_reg(F, Rs),
+ Is = [{set,[FR],[F],fmove}|Is0],
+ {Rs,Is};
+load_reg(V, Ts, Rs0, Is0) ->
+ case find_reg(V, Rs0) of
+ {ok,_FR} -> {Rs0,Is0};
+ error ->
+ Rs = put_reg(V, Rs0, clean),
+ {ok,FR} = find_reg(V, Rs),
+ Op = case tdb_find(V, Ts) of
+ float -> fmove;
+ _ -> fconv
+ end,
+ Is = [{set,[FR],[V],Op}|Is0],
+ {Rs,Is}
+ end.
+
+arith_op('+') -> {yes,fadd};
+arith_op('-') -> {yes,fsub};
+arith_op('*') -> {yes,fmul};
+arith_op('/') -> {yes,fdiv};
+arith_op(_) -> no.
+
+flush(no_float_opt, _, Acc) -> {no_float_opt,Acc};
+flush(Rs, [{set,[_],[],{put_tuple,_}}|_]=Is0, Acc0) ->
+ Acc = flush_all(Rs, Is0, Acc0),
+ {[],Acc};
+flush(Rs0, [{set,Ds,Ss,_Op}|_], Acc0) ->
+ Save = gb_sets:from_list(Ss),
+ Acc = save_regs(Rs0, Save, Acc0),
+ Rs1 = foldl(fun(S, A) -> mark(S, A, clean) end, Rs0, Ss),
+ Kill = gb_sets:from_list(Ds),
+ Rs = kill_regs(Rs1, Kill),
+ {Rs,Acc};
+flush(Rs0, Is, Acc0) ->
+ Acc = flush_all(Rs0, Is, Acc0),
+ {[],Acc}.
+
+flush_all(no_float_opt, _, Acc) -> Acc;
+flush_all([{_,{float,_},_}|Rs], Is, Acc) ->
+ flush_all(Rs, Is, Acc);
+flush_all([{I,V,dirty}|Rs], Is, Acc0) ->
+ Acc = checkerror(Acc0),
+ case beam_block:is_killed(V, Is) of
+ true -> flush_all(Rs, Is, Acc);
+ false -> flush_all(Rs, Is, [{set,[V],[{fr,I}],fmove}|Acc])
+ end;
+flush_all([{_,_,clean}|Rs], Is, Acc) -> flush_all(Rs, Is, Acc);
+flush_all([free|Rs], Is, Acc) -> flush_all(Rs, Is, Acc);
+flush_all([], _, Acc) -> Acc.
+
+save_regs(Rs, Save, Acc) ->
+ foldl(fun(R, A) -> save_reg(R, Save, A) end, Acc, Rs).
+
+save_reg({I,V,dirty}, Save, Acc) ->
+ case gb_sets:is_member(V, Save) of
+ true -> [{set,[V],[{fr,I}],fmove}|checkerror(Acc)];
+ false -> Acc
+ end;
+save_reg(_, _, Acc) -> Acc.
+
+kill_regs(Rs, Kill) ->
+ map(fun(R) -> kill_reg(R, Kill) end, Rs).
+
+kill_reg({_,V,_}=R, Kill) ->
+ case gb_sets:is_member(V, Kill) of
+ true -> free;
+ false -> R
+ end;
+kill_reg(R, _) -> R.
+
+mark(V, [{I,V,_}|Rs], Mark) -> [{I,V,Mark}|Rs];
+mark(V, [R|Rs], Mark) -> [R|mark(V, Rs, Mark)];
+mark(_, [], _) -> [].
+
+fetch_reg(V, [{I,V,_}|_]) -> {fr,I};
+fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
+
+find_reg(V, [{I,V,_}|_]) -> {ok,{fr,I}};
+find_reg(V, [_|SRs]) -> find_reg(V, SRs);
+find_reg(_, []) -> error.
+
+put_reg(V, Rs, Dirty) -> put_reg_1(V, Rs, Dirty, 0).
+
+put_reg_1(V, [free|Rs], Dirty, I) -> [{I,V,Dirty}|Rs];
+put_reg_1(V, [R|Rs], Dirty, I) -> [R|put_reg_1(V, Rs, Dirty, I+1)];
+put_reg_1(V, [], Dirty, I) -> [{I,V,Dirty}].
+
+checkerror(Is) ->
+ checkerror_1(Is, Is).
+
+checkerror_1([{set,[],[],fcheckerror}|_], OrigIs) -> OrigIs;
+checkerror_1([{set,[],[],fclearerror}|_], OrigIs) -> OrigIs;
+checkerror_1([{set,_,_,{bif,fadd,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fsub,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fmul,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fdiv,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([{set,_,_,{bif,fnegate,_}}|_], OrigIs) -> checkerror_2(OrigIs);
+checkerror_1([_|Is], OrigIs) -> checkerror_1(Is, OrigIs);
+checkerror_1([], OrigIs) -> OrigIs.
+
+checkerror_2(OrigIs) -> [{set,[],[],fcheckerror}|OrigIs].
+
+add_ftest_heap(Is) ->
+ add_ftest_heap_1(reverse(Is), 0, []).
+
+add_ftest_heap_1([{set,_,[{fr,_}],fmove}=I|Is], Floats, Acc) ->
+ add_ftest_heap_1(Is, Floats+1, [I|Acc]);
+add_ftest_heap_1([{allocate,_,_}=I|Is], 0, Acc) ->
+ reverse(Is, [I|Acc]);
+add_ftest_heap_1([{allocate,Regs,{Z,Stk,Heap,Inits}}|Is], Floats, Acc) ->
+ reverse(Is, [{allocate,Regs,{Z,Stk,Heap,Floats,Inits}}|Acc]);
+add_ftest_heap_1([I|Is], Floats, Acc) ->
+ add_ftest_heap_1(Is, Floats, [I|Acc]);
+add_ftest_heap_1([], 0, Acc) ->
+ Acc;
+add_ftest_heap_1([], Floats, Is) ->
+ Regs = beam_block:live_at_entry(Is),
+ [{allocate,Regs,{nozero,nostack,0,Floats,[]}}|Is].
+
+are_live_regs_determinable([{allocate,_,_}|_]) -> true;
+are_live_regs_determinable([{'%live',_}|_]) -> true;
+are_live_regs_determinable([_|Is]) -> are_live_regs_determinable(Is);
+are_live_regs_determinable([]) -> false.
+
+
+%%% Routines for maintaining a type database. The type database
+%%% associates type information with registers.
+%%%
+%%% {tuple,Size,First} means that the corresponding register contains a
+%%% tuple with *at least* Size elements. An tuple with unknown
+%%% size is represented as {tuple,0}. First is either [] (meaning that
+%%% the tuple's first element is unknown) or [FirstElement] (the contents
+%%% of the first element).
+%%%
+%%% 'float' means that the register contains a float.
+
+%% tdb_new() -> EmptyDataBase
+%% Creates a new, empty type database.
+
+tdb_new() -> [].
+
+%% tdb_find(Register, Db) -> Information|error
+%% Returns type information or the atom error if there are no type
+%% information available for Register.
+
+tdb_find(Key, [{K,_}|_]) when Key < K -> error;
+tdb_find(Key, [{Key,Info}|_]) -> Info;
+tdb_find(Key, [_|Db]) -> tdb_find(Key, Db);
+tdb_find(_, []) -> error.
+
+%% tdb_update([UpdateOp], Db) -> NewDb
+%% UpdateOp = {Register,kill}|{Register,NewInfo}
+%% Updates a type database. If a 'kill' operation is given, the type
+%% information for that register will be removed from the database.
+%% A kill operation takes precende over other operations for the same
+%% register (i.e. [{{x,0},kill},{{x,0},{tuple,5}}] means that the
+%% the existing type information, if any, will be discarded, and the
+%% the '{tuple,5}' information ignored.
+%%
+%% If NewInfo information is given and there exists information about
+%% the register, the old and new type information will be merged.
+%% For instance, {tuple,5} and {tuple,10} will be merged to produce
+%% {tuple,10}.
+
+tdb_update(Uis0, Ts0) ->
+ Uis1 = filter(fun ({{x,_},_Op}) -> true;
+ ({{y,_},_Op}) -> true;
+ (_) -> false
+ end, Uis0),
+ tdb_update1(lists:sort(Uis1), Ts0).
+
+tdb_update1([{Key,kill}|Ops], [{K,_Old}|_]=Db) when Key < K ->
+ tdb_update1(remove_key(Key, Ops), Db);
+tdb_update1([{Key,_New}=New|Ops], [{K,_Old}|_]=Db) when Key < K ->
+ [New|tdb_update1(Ops, Db)];
+tdb_update1([{Key,kill}|Ops], [{Key,_}|Db]) ->
+ tdb_update1(remove_key(Key, Ops), Db);
+tdb_update1([{Key,NewInfo}|Ops], [{Key,OldInfo}|Db]) ->
+ [{Key,merge_type_info(NewInfo, OldInfo)}|tdb_update1(Ops, Db)];
+tdb_update1([{_,_}|_]=Ops, [Old|Db]) ->
+ [Old|tdb_update1(Ops, Db)];
+tdb_update1([{Key,kill}|Ops], []) ->
+ tdb_update1(remove_key(Key, Ops), []);
+tdb_update1([{_,_}=New|Ops], []) ->
+ [New|tdb_update1(Ops, [])];
+tdb_update1([], Db) -> Db.
+
+%% tdb_kill_xregs(Db) -> NewDb
+%% Kill all information about x registers. Also kill all tuple_element
+%% dependencies from y registers to x registers.
+
+tdb_kill_xregs([{{x,_},_Type}|Db]) -> tdb_kill_xregs(Db);
+tdb_kill_xregs([{{y,_},{tuple_element,{x,_},_}}|Db]) -> tdb_kill_xregs(Db);
+tdb_kill_xregs([Any|Db]) -> [Any|tdb_kill_xregs(Db)];
+tdb_kill_xregs([]) -> [].
+
+remove_key(Key, [{Key,_Op}|Ops]) -> remove_key(Key, Ops);
+remove_key(_, Ops) -> Ops.
+
+merge_type_info(I, I) -> I;
+merge_type_info({tuple,Sz1,Same}, {tuple,Sz2,Same}=Max) when Sz1 < Sz2 ->
+ Max;
+merge_type_info({tuple,Sz1,Same}=Max, {tuple,Sz2,Same}) when Sz1 > Sz2 ->
+ Max;
+merge_type_info({tuple,Sz1,[]}, {tuple,Sz2,First}) ->
+ merge_type_info({tuple,Sz1,First}, {tuple,Sz2,First});
+merge_type_info({tuple,Sz1,First}, {tuple,Sz2,_}) ->
+ merge_type_info({tuple,Sz1,First}, {tuple,Sz2,First});
+merge_type_info(NewType, _) ->
+ verify_type(NewType),
+ NewType.
+
+verify_type({tuple,Sz,[]}) when is_integer(Sz) -> ok;
+verify_type({tuple,Sz,[_]}) when is_integer(Sz) -> ok;
+verify_type({tuple_element,_,_}) -> ok;
+verify_type(float) -> ok;
+verify_type({atom,_}) -> ok.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl
new file mode 100644
index 0000000000..a01be447b0
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/beam_validator.erl
@@ -0,0 +1,1022 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: beam_validator.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+
+-module(beam_validator).
+
+-export([file/1,files/1]).
+
+%% Interface for compiler.
+-export([module/2,format_error/1]).
+
+-import(lists, [reverse/1,foldl/3]).
+
+-define(MAXREG, 1024).
+
+-define(DEBUG, 1).
+-undef(DEBUG).
+-ifdef(DEBUG).
+-define(DBG_FORMAT(F, D), (io:format((F), (D)))).
+-else.
+-define(DBG_FORMAT(F, D), ok).
+-endif.
+
+%%%
+%%% API functions.
+%%%
+
+files([F|Fs]) ->
+ ?DBG_FORMAT("# Verifying: ~p~n", [F]),
+ case file(F) of
+ ok -> ok;
+ {error,Es} ->
+ io:format("~p:~n~s~n", [F,format_error(Es)])
+ end,
+ files(Fs);
+files([]) -> ok.
+
+file(Name) when is_list(Name) ->
+ case case filename:extension(Name) of
+ ".S" -> s_file(Name);
+ ".beam" -> beam_file(Name)
+ end of
+ [] -> ok;
+ Es -> {error,Es}
+ end.
+
+%% To be called by the compiler.
+module({Mod,Exp,Attr,Fs,Lc}=Code, _Opts)
+ when is_atom(Mod), is_list(Exp), is_list(Attr), is_integer(Lc) ->
+ case validate(Fs) of
+ [] -> {ok,Code};
+ Es0 ->
+ Es = [{?MODULE,E} || E <- Es0],
+ {error,[{atom_to_list(Mod),Es}]}
+ end.
+
+format_error([]) -> [];
+format_error([{{M,F,A},{I,Off,Desc}}|Es]) ->
+ [io_lib:format(" ~p:~p/~p+~p:~n ~p - ~p~n",
+ [M,F,A,Off,I,Desc])|format_error(Es)];
+format_error({{_M,F,A},{I,Off,Desc}}) ->
+ io_lib:format(
+ "function ~p/~p+~p:~n"
+ " Internal consistency check failed - please report this bug.~n"
+ " Instruction: ~p~n"
+ " Error: ~p:~n", [F,A,Off,I,Desc]).
+
+%%%
+%%% Local functions follow.
+%%%
+
+s_file(Name) ->
+ {ok,Is} = file:consult(Name),
+ Fs = find_functions(Is),
+ validate(Fs).
+
+find_functions(Fs) ->
+ find_functions_1(Fs, none, [], []).
+
+find_functions_1([{function,Name,Arity,Entry}|Is], Func, FuncAcc, Acc0) ->
+ Acc = add_func(Func, FuncAcc, Acc0),
+ find_functions_1(Is, {Name,Arity,Entry}, [], Acc);
+find_functions_1([I|Is], Func, FuncAcc, Acc) ->
+ find_functions_1(Is, Func, [I|FuncAcc], Acc);
+find_functions_1([], Func, FuncAcc, Acc) ->
+ reverse(add_func(Func, FuncAcc, Acc)).
+
+add_func(none, _, Acc) -> Acc;
+add_func({Name,Arity,Entry}, Is, Acc) ->
+ [{function,Name,Arity,Entry,reverse(Is)}|Acc].
+
+beam_file(Name) ->
+ try beam_disasm:file(Name) of
+ {error,beam_lib,Reason} -> [{beam_lib,Reason}];
+ {beam_file,L} ->
+ {value,{code,Code0}} = lists:keysearch(code, 1, L),
+ Code = beam_file_1(Code0, []),
+ validate(Code)
+ catch _:_ -> [disassembly_failed]
+ end.
+
+beam_file_1([F0|Fs], Acc) ->
+ F = conv_func(F0),
+ beam_file_1(Fs, [F|Acc]);
+beam_file_1([], Acc) -> reverse(Acc).
+
+%% Convert from the disassembly format to the internal format
+%% used by the compiler (as passed to the assembler).
+
+conv_func(Is) ->
+ conv_func_1(labels(Is)).
+
+conv_func_1({Ls,[{func_info,[{atom,M},{atom,F},Ar]},
+ {label,Entry}=Le|Is]}) ->
+ %% The entry label gets maybe not correct here
+ {function,F,Ar,Entry,
+ [{label,L}||L<-Ls]++[{func_info,{atom,M},{atom,F},Ar},Le|Is]}.
+
+%%%
+%%% The validator follows.
+%%%
+%%% The purpose of the validator is find errors in the generated code
+%%% that may cause the emulator to crash or behave strangely.
+%%% We don't care about type errors in the user's code that will
+%%% cause a proper exception at run-time.
+%%%
+
+%%% Things currently not checked. XXX
+%%%
+%%% - That floating point registers are initialized before used.
+%%% - That fclearerror and fcheckerror are used properly.
+%%% - Heap allocation for floating point numbers.
+%%% - Heap allocation for binaries.
+%%% - That a catchtag or trytag is not overwritten by the wrong
+%%% type of instruction (such as move/2).
+%%% - Make sure that all catchtags and trytags have been removed
+%%% from the stack at return/tail call.
+%%% - Verify get_list instructions.
+%%%
+
+%% validate([Function]) -> [] | [Error]
+%% A list of functions with their code. The code is in the same
+%% format as used in the compiler and in .S files.
+validate([]) -> [];
+validate([{function,Name,Ar,Entry,Code}|Fs]) ->
+ try validate_1(Code, Name, Ar, Entry) of
+ _ -> validate(Fs)
+ catch
+ Error ->
+ [Error|validate(Fs)];
+ error:Error ->
+ [validate_error(Error, Name, Ar)|validate(Fs)]
+ end.
+
+-ifdef(DEBUG).
+validate_error(Error, Name, Ar) ->
+ exit(validate_error_1(Error, Name, Ar)).
+-else.
+validate_error(Error, Name, Ar) ->
+ validate_error_1(Error, Name, Ar).
+-endif.
+validate_error_1(Error, Name, Ar) ->
+ {{'_',Name,Ar},
+ {internal_error,'_',{Error,erlang:get_stacktrace()}}}.
+
+-record(st, %Emulation state
+ {x=init_regs(0, term), %x register info.
+ y=init_regs(0, initialized), %y register info.
+ numy=none, %Number of y registers.
+ h=0, %Available heap size.
+ ct=[] %List of hot catch/try labels
+ }).
+
+-record(vst, %Validator state
+ {current=none, %Current state
+ branched=gb_trees:empty() %States at jumps
+ }).
+
+-ifdef(DEBUG).
+print_st(#st{x=Xs,y=Ys,numy=NumY,h=H,ct=Ct}) ->
+ io:format(" #st{x=~p~n"
+ " y=~p~n"
+ " numy=~p,h=~p,ct=~w~n",
+ [gb_trees:to_list(Xs),gb_trees:to_list(Ys),NumY,H,Ct]).
+-endif.
+
+validate_1(Is, Name, Arity, Entry) ->
+ validate_2(labels(Is), Name, Arity, Entry).
+
+validate_2({Ls1,[{func_info,{atom,Mod},{atom,Name},Arity}=_F|Is]},
+ Name, Arity, Entry) ->
+ lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [_L]) end, Ls1),
+ ?DBG_FORMAT(" ~p.~n", [_F]),
+ validate_3(labels(Is), Name, Arity, Entry, Mod, Ls1);
+validate_2({Ls1,Is}, Name, Arity, _Entry) ->
+ error({{'_',Name,Arity},{first(Is),length(Ls1),illegal_instruction}}).
+
+validate_3({Ls2,Is}, Name, Arity, Entry, Mod, Ls1) ->
+ lists:foreach(fun (_L) -> ?DBG_FORMAT(" ~p.~n", [_L]) end, Ls2),
+ Offset = 1 + length(Ls2),
+ case lists:member(Entry, Ls2) of
+ true ->
+ St = init_state(Arity),
+ Vst = #vst{current=St,
+ branched=gb_trees_from_list([{L,St} || L <- Ls1])},
+ valfun(Is, {Mod,Name,Arity}, Offset, Vst);
+ false ->
+ error({{Mod,Name,Arity},{first(Is),Offset,no_entry_label}})
+ end.
+
+first([X|_]) -> X;
+first([]) -> [].
+
+labels(Is) ->
+ labels_1(Is, []).
+
+labels_1([{label,L}|Is], R) ->
+ labels_1(Is, [L|R]);
+labels_1(Is, R) ->
+ {lists:reverse(R),Is}.
+
+init_state(Arity) ->
+ Xs = init_regs(Arity, term),
+ Ys = init_regs(0, initialized),
+ #st{x=Xs,y=Ys,numy=none,h=0,ct=[]}.
+
+init_regs(0, _) ->
+ gb_trees:empty();
+init_regs(N, Type) ->
+ gb_trees_from_list([{R,Type} || R <- lists:seq(0, N-1)]).
+
+valfun([], _MFA, _Offset, Vst) -> Vst;
+valfun([I|Is], MFA, Offset, Vst) ->
+ ?DBG_FORMAT(" ~p.\n", [I]),
+ valfun(Is, MFA, Offset+1,
+ try valfun_1(I, Vst)
+ catch Error ->
+ error({MFA,{I,Offset,Error}})
+ end).
+
+%% Instructions that are allowed in dead code or when failing,
+%% that is while the state is undecided in some way.
+valfun_1({label,Lbl}, #vst{current=St0,branched=B}=Vst) ->
+ St = merge_states(Lbl, St0, B),
+ Vst#vst{current=St,branched=gb_trees:enter(Lbl, St, B)};
+valfun_1(_I, #vst{current=none}=Vst) ->
+ %% Ignore instructions after erlang:error/1,2, which
+ %% the original R10B compiler thought would return.
+ ?DBG_FORMAT("Ignoring ~p\n", [_I]),
+ Vst;
+valfun_1({badmatch,Src}, Vst) ->
+ assert_term(Src, Vst),
+ kill_state(Vst);
+valfun_1({case_end,Src}, Vst) ->
+ assert_term(Src, Vst),
+ kill_state(Vst);
+valfun_1(if_end, Vst) ->
+ kill_state(Vst);
+valfun_1({try_case_end,Src}, Vst) ->
+ assert_term(Src, Vst),
+ kill_state(Vst);
+%% Instructions that can not cause exceptions
+valfun_1({move,Src,Dst}, Vst) ->
+ Type = get_term_type(Src, Vst),
+ set_type_reg(Type, Dst, Vst);
+valfun_1({fmove,Src,{fr,_}}, Vst) ->
+ assert_type(float, Src, Vst);
+valfun_1({fmove,{fr,_},Dst}, Vst) ->
+ set_type_reg({float,[]}, Dst, Vst);
+valfun_1({kill,{y,_}=Reg}, Vst) ->
+ set_type_y(initialized, Reg, Vst);
+valfun_1({test_heap,Heap,Live}, Vst) ->
+ test_heap(Heap, Live, Vst);
+valfun_1({bif,_Op,nofail,Src,Dst}, Vst) ->
+ validate_src(Src, Vst),
+ set_type_reg(term, Dst, Vst);
+%% Put instructions.
+valfun_1({put_list,A,B,Dst}, Vst0) ->
+ assert_term(A, Vst0),
+ assert_term(B, Vst0),
+ Vst = eat_heap(2, Vst0),
+ set_type_reg(cons, Dst, Vst);
+valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) ->
+ Vst = eat_heap(1, Vst0),
+ set_type_reg({tuple,Sz}, Dst, Vst);
+valfun_1({put,Src}, Vst) ->
+ assert_term(Src, Vst),
+ eat_heap(1, Vst);
+valfun_1({put_string,Sz,_,Dst}, Vst0) when is_integer(Sz) ->
+ Vst = eat_heap(2*Sz, Vst0),
+ set_type_reg(cons, Dst, Vst);
+%% Allocate and deallocate, et.al
+valfun_1({allocate,Stk,Live}, Vst) ->
+ allocate(false, Stk, 0, Live, Vst);
+valfun_1({allocate_heap,Stk,Heap,Live}, Vst) ->
+ allocate(false, Stk, Heap, Live, Vst);
+valfun_1({allocate_zero,Stk,Live}, Vst) ->
+ allocate(true, Stk, 0, Live, Vst);
+valfun_1({allocate_heap_zero,Stk,Heap,Live}, Vst) ->
+ allocate(true, Stk, Heap, Live, Vst);
+valfun_1({init,{y,_}=Reg}, Vst) ->
+ set_type_y(initialized, Reg, Vst);
+valfun_1({deallocate,StkSize}, #vst{current=#st{numy=StkSize,ct=[]}}=Vst) ->
+ deallocate(Vst);
+valfun_1({deallocate,_}, #vst{current=#st{numy=NumY,ct=[]}}) ->
+ error({allocated,NumY});
+valfun_1({deallocate,_}, #vst{current=#st{ct=Fails}}) ->
+ error({catch_try_stack,Fails});
+%% Catch & try.
+valfun_1({'catch',Dst,{f,Fail}}, Vst0) when Fail /= none ->
+ Vst = #vst{current=#st{ct=Fails}=St} =
+ set_type_y({catchtag,Fail}, Dst, Vst0),
+ Vst#vst{current=St#st{ct=[Fail|Fails]}};
+valfun_1({'try',Dst,{f,Fail}}, Vst0) ->
+ Vst = #vst{current=#st{ct=Fails}=St} =
+ set_type_y({trytag,Fail}, Dst, Vst0),
+ Vst#vst{current=St#st{ct=[Fail|Fails]}};
+%% Do a postponed state branch if necessary and try next set of instructions
+valfun_1(I, #vst{current=#st{ct=[]}}=Vst) ->
+ valfun_2(I, Vst);
+valfun_1(I, #vst{current=#st{ct=Fails}}=Vst0) ->
+ %% Perform a postponed state branch
+ Vst = #vst{current=St} = lists:foldl(fun branch_state/2, Vst0, Fails),
+ valfun_2(I, Vst#vst{current=St#st{ct=[]}}).
+
+%% Instructions that can cause exceptions.
+valfun_2({apply,Live}, Vst) ->
+ call(Live+2, Vst);
+valfun_2({apply_last,Live,_}, Vst) ->
+ tail_call(Live+2, Vst);
+valfun_2({call_fun,Live}, Vst) ->
+ call(Live, Vst);
+valfun_2({call,Live,_}, Vst) ->
+ call(Live, Vst);
+valfun_2({call_ext,Live,Func}, Vst) ->
+ call(Func, Live, Vst);
+valfun_2({call_only,Live,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({call_ext_only,Live,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({call_last,Live,_,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({call_ext_last,Live,_,_}, Vst) ->
+ tail_call(Live, Vst);
+valfun_2({make_fun,_,_,Live}, Vst) ->
+ call(Live, Vst);
+valfun_2({make_fun2,_,_,_,Live}, Vst) ->
+ call(Live, Vst);
+%% Floating point.
+valfun_2({fconv,Src,{fr,_}}, Vst) ->
+ assert_term(Src, Vst);
+valfun_2({bif,fadd,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fdiv,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fmul,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fnegate,_,[{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2({bif,fsub,_,[{fr,_},{fr,_}],{fr,_}}, Vst) ->
+ Vst;
+valfun_2(fclearerror, Vst) ->
+ Vst;
+valfun_2({fcheckerror,_}, Vst) ->
+ Vst;
+%% Other BIFs
+valfun_2({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) ->
+ TupleType0 = get_term_type(Tuple, Vst0),
+ PosType = get_term_type(Pos, Vst0),
+ Vst1 = branch_state(Fail, Vst0),
+ TupleType = upgrade_type({tuple,[get_tuple_size(PosType)]}, TupleType0),
+ Vst = set_type(TupleType, Tuple, Vst1),
+ set_type_reg(term, Dst, Vst);
+valfun_2({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
+ validate_src(Src, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ Type = bif_type(Op, Src, Vst),
+ set_type_reg(Type, Dst, Vst);
+valfun_2(return, #vst{current=#st{numy=none}}=Vst) ->
+ kill_state(Vst);
+valfun_2(return, #vst{current=#st{numy=NumY}}) ->
+ error({stack_frame,NumY});
+valfun_2({jump,{f,_}}, #vst{current=none}=Vst) ->
+ %% Must be an unreachable jump which was not optimized away.
+ %% Do nothing.
+ Vst;
+valfun_2({jump,{f,Lbl}}, Vst) ->
+ kill_state(branch_state(Lbl, Vst));
+valfun_2({loop_rec,{f,Fail},Dst}, Vst0) ->
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg(term, Dst, Vst);
+valfun_2(remove_message, Vst) ->
+ Vst;
+valfun_2({wait,_}, Vst) ->
+ kill_state(Vst);
+valfun_2({wait_timeout,_,Src}, Vst) ->
+ assert_term(Src, Vst);
+valfun_2({loop_rec_end,_}, Vst) ->
+ kill_state(Vst);
+valfun_2(timeout, #vst{current=St}=Vst) ->
+ Vst#vst{current=St#st{x=init_regs(0, term)}};
+valfun_2(send, Vst) ->
+ call(2, Vst);
+%% Catch & try.
+valfun_2({catch_end,Reg}, Vst0) ->
+ case get_type(Reg, Vst0) of
+ {catchtag,_} ->
+ Vst = #vst{current=St} = set_type_reg(initialized, Reg, Vst0),
+ Xs = gb_trees_from_list([{0,term}]),
+ Vst#vst{current=St#st{x=Xs}};
+ Type ->
+ error({bad_type,Type})
+ end;
+valfun_2({try_end,Reg}, Vst) ->
+ case get_type(Reg, Vst) of
+ {trytag,_} ->
+ set_type_reg(initialized, Reg, Vst);
+ Type ->
+ error({bad_type,Type})
+ end;
+valfun_2({try_case,Reg}, Vst0) ->
+ case get_type(Reg, Vst0) of
+ {trytag,_} ->
+ Vst = #vst{current=St} = set_type_reg(initialized, Reg, Vst0),
+ Xs = gb_trees_from_list([{0,{atom,[]}},{1,term},{2,term}]),
+ Vst#vst{current=St#st{x=Xs}};
+ Type ->
+ error({bad_type,Type})
+ end;
+valfun_2({set_tuple_element,Src,Tuple,I}, Vst) ->
+ assert_term(Src, Vst),
+ assert_type({tuple_element,I+1}, Tuple, Vst);
+%% Match instructions.
+valfun_2({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
+ assert_term(Src, Vst),
+ Lbls = [L || {f,L} <- Choices]++[Fail],
+ kill_state(foldl(fun(L, S) -> branch_state(L, S) end, Vst, Lbls));
+valfun_2({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
+ assert_type(tuple, Tuple, Vst),
+ kill_state(branch_arities(Choices, Tuple, branch_state(Fail, Vst)));
+valfun_2({get_list,Src,D1,D2}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = set_type_reg(term, D1, Vst0),
+ set_type_reg(term, D2, Vst);
+valfun_2({get_tuple_element,Src,I,Dst}, Vst) ->
+ assert_type({tuple_element,I+1}, Src, Vst),
+ set_type_reg(term, Dst, Vst);
+valfun_2({bs_restore,_}, Vst) ->
+ Vst;
+valfun_2({bs_save,_}, Vst) ->
+ Vst;
+valfun_2({bs_start_match,{f,Fail},Src}, Vst) ->
+ assert_term(Src, Vst),
+ branch_state(Fail, Vst);
+valfun_2({test,bs_skip_bits,{f,Fail},[Src,_,_]}, Vst) ->
+ assert_term(Src, Vst),
+ branch_state(Fail, Vst);
+valfun_2({test,_,{f,Fail},[_,_,_,Dst]}, Vst0) ->
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg({integer,[]}, Dst, Vst);
+valfun_2({test,bs_test_tail,{f,Fail},_}, Vst) ->
+ branch_state(Fail, Vst);
+%% Other test instructions.
+valfun_2({test,is_float,{f,Lbl},[Float]}, Vst0) ->
+ assert_term(Float, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ set_type({float,[]}, Float, Vst);
+valfun_2({test,is_tuple,{f,Lbl},[Tuple]}, Vst0) ->
+ assert_term(Tuple, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ set_type({tuple,[0]}, Tuple, Vst);
+valfun_2({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst0) when is_integer(Sz) ->
+ assert_type(tuple, Tuple, Vst0),
+ Vst = branch_state(Lbl, Vst0),
+ set_type_reg({tuple,Sz}, Tuple, Vst);
+valfun_2({test,_Op,{f,Lbl},Src}, Vst) ->
+ validate_src(Src, Vst),
+ branch_state(Lbl, Vst);
+valfun_2({bs_add,{f,Fail},[A,B,_],Dst}, Vst0) ->
+ assert_term(A, Vst0),
+ assert_term(B, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg({integer,[]}, Dst, Vst);
+valfun_2({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst0) ->
+ assert_term(Src, Vst0),
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg({integer,[]}, Dst, Vst);
+valfun_2({bs_init2,{f,Fail},_,Heap,_,_,Dst}, Vst0) ->
+ Vst1 = heap_alloc(Heap, Vst0),
+ Vst = branch_state(Fail, Vst1),
+ set_type_reg(binary, Dst, Vst);
+valfun_2({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
+ Vst;
+valfun_2({bs_put_binary,{f,Fail},_,_,_,Src}, Vst0) ->
+ assert_term(Src, Vst0),
+ branch_state(Fail, Vst0);
+valfun_2({bs_put_float,{f,Fail},_,_,_,Src}, Vst0) ->
+ assert_term(Src, Vst0),
+ branch_state(Fail, Vst0);
+valfun_2({bs_put_integer,{f,Fail},_,_,_,Src}, Vst0) ->
+ assert_term(Src, Vst0),
+ branch_state(Fail, Vst0);
+%% Old bit syntax construction (before R10B).
+valfun_2({bs_init,_,_}, Vst) -> Vst;
+valfun_2({bs_need_buf,_}, Vst) -> Vst;
+valfun_2({bs_final,{f,Fail},Dst}, Vst0) ->
+ Vst = branch_state(Fail, Vst0),
+ set_type_reg(binary, Dst, Vst);
+%% Misc.
+valfun_2({'%live',Live}, Vst) ->
+ verify_live(Live, Vst),
+ Vst;
+valfun_2(_, _) ->
+ error(unknown_instruction).
+
+kill_state(#vst{current=#st{ct=[]}}=Vst) ->
+ Vst#vst{current=none};
+kill_state(#vst{current=#st{ct=Fails}}=Vst0) ->
+ Vst = lists:foldl(fun branch_state/2, Vst0, Fails),
+ Vst#vst{current=none}.
+
+%% A "plain" call.
+%% The stackframe must have a known size and be initialized.
+%% The instruction will return to the instruction following the call.
+call(Live, #vst{current=St}=Vst) ->
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
+ Xs = gb_trees_from_list([{0,term}]),
+ Vst#vst{current=St#st{x=Xs}}.
+
+%% A "plain" call.
+%% The stackframe must have a known size and be initialized.
+%% The instruction will return to the instruction following the call.
+call(Name, Live, #vst{current=St}=Vst) ->
+ verify_live(Live, Vst),
+ case return_type(Name, Vst) of
+ exception ->
+ kill_state(Vst);
+ Type ->
+ verify_y_init(Vst),
+ Xs = gb_trees_from_list([{0,Type}]),
+ Vst#vst{current=St#st{x=Xs}}
+ end.
+
+%% Tail call.
+%% The stackframe must have a known size and be initialized.
+%% Does not return to the instruction following the call.
+tail_call(Live, Vst) ->
+ kill_state(call(Live, Vst)).
+
+allocate(Zero, Stk, Heap, Live, #vst{current=#st{numy=none}=St}=Vst) ->
+ verify_live(Live, Vst),
+ Ys = init_regs(case Zero of
+ true -> Stk;
+ false -> 0
+ end, initialized),
+ Vst#vst{current=St#st{y=Ys,numy=Stk,h=heap_alloc_1(Heap)}};
+allocate(_, _, _, _, #vst{current=#st{numy=Numy}}) ->
+ error({existing_stack_frame,{size,Numy}}).
+
+deallocate(#vst{current=St}=Vst) ->
+ Vst#vst{current=St#st{y=init_regs(0, initialized),numy=none}}.
+
+test_heap(Heap, Live, Vst) ->
+ verify_live(Live, Vst),
+ heap_alloc(Heap, Vst).
+
+heap_alloc(Heap, #vst{current=St}=Vst) ->
+ Vst#vst{current=St#st{h=heap_alloc_1(Heap)}}.
+
+heap_alloc_1({alloc,Alloc}) ->
+ {value,{_,Heap}} = lists:keysearch(words, 1, Alloc),
+ Heap;
+heap_alloc_1(Heap) when is_integer(Heap) -> Heap.
+
+
+set_type(Type, {x,_}=Reg, Vst) -> set_type_reg(Type, Reg, Vst);
+set_type(Type, {y,_}=Reg, Vst) -> set_type_y(Type, Reg, Vst);
+set_type(_, _, #vst{}=Vst) -> Vst.
+
+set_type_reg(Type, {x,X}, #vst{current=#st{x=Xs}=St}=Vst)
+ when 0 =< X, X < ?MAXREG ->
+ Vst#vst{current=St#st{x=gb_trees:enter(X, Type, Xs)}};
+set_type_reg(Type, Reg, Vst) ->
+ set_type_y(Type, Reg, Vst).
+
+set_type_y(Type, {y,Y}=Reg, #vst{current=#st{y=Ys,numy=NumY}=St}=Vst)
+ when is_integer(Y), 0 =< Y, Y < ?MAXREG ->
+ case {Y,NumY} of
+ {_,none} ->
+ error({no_stack_frame,Reg});
+ {_,_} when Y > NumY ->
+ error({y_reg_out_of_range,Reg,NumY});
+ {_,_} ->
+ Vst#vst{current=St#st{y=gb_trees:enter(Y, Type, Ys)}}
+ end;
+set_type_y(Type, Reg, #vst{}) -> error({invalid_store,Reg,Type}).
+
+assert_term(Src, Vst) ->
+ get_term_type(Src, Vst),
+ Vst.
+
+%% The possible types.
+%%
+%% First non-term types:
+%%
+%% initialized Only for Y registers. Means that the Y register
+%% has been initialized with some valid term so that
+%% it is safe to pass to the garbage collector.
+%% NOT safe to use in any other way (will not crash the
+%% emulator, but clearly points to a bug in the compiler).
+%%
+%% {catchtag,Lbl} A special term used within a catch. Must only be used
+%% by the catch instructions; NOT safe to use in other
+%% instructions.
+%%
+%% {trytag,Lbl} A special term used within a try block. Must only be
+%% used by the catch instructions; NOT safe to use in other
+%% instructions.
+%%
+%% exception Can only be used as a type returned by return_type/2
+%% (which gives the type of the value returned by a BIF).
+%% Thus 'exception' is never stored as type descriptor
+%% for a register.
+%%
+%% Normal terms:
+%%
+%% term Any valid Erlang (but not of the special types above).
+%%
+%% bool The atom 'true' or the atom 'false'.
+%%
+%% cons Cons cell: [_|_]
+%%
+%% nil Empty list: []
+%%
+%% {tuple,[Sz]} Tuple. An element has been accessed using
+%% element/2 or setelement/3 so that it is known that
+%% the type is a tuple of size at least Sz.
+%%
+%% {tuple,Sz} Tuple. A test_arity instruction has been seen
+%% so that it is known that the size is exactly Sz.
+%%
+%% {atom,[]} Atom.
+%% {atom,Atom}
+%%
+%% {integer,[]} Integer.
+%% {integer,Integer}
+%%
+%% {float,[]} Float.
+%% {float,Float}
+%%
+%% number Integer or Float of unknown value
+%%
+
+assert_type(WantedType, Term, Vst) ->
+ assert_type(WantedType, get_type(Term, Vst)),
+ Vst.
+
+assert_type(float, {float,_}) -> ok;
+assert_type(tuple, {tuple,_}) -> ok;
+assert_type({tuple_element,I}, {tuple,[Sz]})
+ when 1 =< I, I =< Sz ->
+ ok;
+assert_type({tuple_element,I}, {tuple,Sz})
+ when is_integer(Sz), 1 =< I, I =< Sz ->
+ ok;
+assert_type(Needed, Actual) ->
+ error({bad_type,{needed,Needed},{actual,Actual}}).
+
+%% upgrade_type/2 is used when linear code finds out more and
+%% more information about a type, so the type gets "narrower"
+%% or perhaps inconsistent. In the case of inconsistency
+%% we mostly widen the type to 'term' to make subsequent
+%% code fail if it assumes anything about the type.
+
+upgrade_type(Same, Same) -> Same;
+upgrade_type(term, OldT) -> OldT;
+upgrade_type(NewT, term) -> NewT;
+upgrade_type({Type,New}=NewT, {Type,Old}=OldT)
+ when Type == atom; Type == integer; Type == float ->
+ if New =:= Old -> OldT;
+ New =:= [] -> OldT;
+ Old =:= [] -> NewT;
+ true -> term
+ end;
+upgrade_type({Type,_}=NewT, number)
+ when Type == integer; Type == float ->
+ NewT;
+upgrade_type(number, {Type,_}=OldT)
+ when Type == integer; Type == float ->
+ OldT;
+upgrade_type(bool, {atom,A}) ->
+ upgrade_bool(A);
+upgrade_type({atom,A}, bool) ->
+ upgrade_bool(A);
+upgrade_type({tuple,[Sz]}, {tuple,[OldSz]})
+ when is_integer(Sz) ->
+ {tuple,[max(Sz, OldSz)]};
+upgrade_type({tuple,Sz}=T, {tuple,[_]})
+ when is_integer(Sz) ->
+ %% This also takes care of the user error when a tuple element
+ %% is accesed outside the known exact tuple size; there is
+ %% no more type information, just a runtime error which is not
+ %% our problem.
+ T;
+upgrade_type({tuple,[Sz]}, {tuple,_}=T)
+ when is_integer(Sz) ->
+ %% Same as the previous clause but mirrored.
+ T;
+upgrade_type(_A, _B) ->
+ %%io:format("upgrade_type: ~p ~p\n", [_A,_B]),
+ term.
+
+upgrade_bool([]) -> bool;
+upgrade_bool(true) -> {atom,true};
+upgrade_bool(false) -> {atom,false};
+upgrade_bool(_) -> term.
+
+get_tuple_size({integer,[]}) -> 0;
+get_tuple_size({integer,Sz}) -> Sz;
+get_tuple_size(_) -> 0.
+
+validate_src(Ss, Vst) when is_list(Ss) ->
+ foldl(fun(S, _) -> get_type(S, Vst) end, ok, Ss).
+
+get_term_type(Src, Vst) ->
+ case get_type(Src, Vst) of
+ initialized -> error({not_assigned,Src});
+ exception -> error({exception,Src});
+ {catchtag,_} -> error({catchtag,Src});
+ {trytag,_} -> error({trytag,Src});
+ Type -> Type
+ end.
+
+get_type(nil=T, _) -> T;
+get_type({atom,A}=T, _) when is_atom(A) -> T;
+get_type({float,F}=T, _) when is_float(F) -> T;
+get_type({integer,I}=T, _) when is_integer(I) -> T;
+get_type({x,X}=Reg, #vst{current=#st{x=Xs}}) when is_integer(X) ->
+ case gb_trees:lookup(X, Xs) of
+ {value,Type} -> Type;
+ none -> error({uninitialized_reg,Reg})
+ end;
+get_type({y,Y}=Reg, #vst{current=#st{y=Ys}}) when is_integer(Y) ->
+ case gb_trees:lookup(Y, Ys) of
+ {value,initialized} -> error({unassigned_reg,Reg});
+ {value,Type} -> Type;
+ none -> error({uninitialized_reg,Reg})
+ end;
+get_type(Src, _) -> error({bad_source,Src}).
+
+branch_arities([], _, #vst{}=Vst) -> Vst;
+branch_arities([Sz,{f,L}|T], Tuple, #vst{current=St}=Vst0)
+ when is_integer(Sz) ->
+ Vst1 = set_type_reg({tuple,Sz}, Tuple, Vst0),
+ Vst = branch_state(L, Vst1),
+ branch_arities(T, Tuple, Vst#vst{current=St}).
+
+branch_state(0, #vst{}=Vst) -> Vst;
+branch_state(L, #vst{current=St,branched=B}=Vst) ->
+ Vst#vst{
+ branched=case gb_trees:is_defined(L, B) of
+ false ->
+ gb_trees:insert(L, St#st{ct=[]}, B);
+ true ->
+ MergedSt = merge_states(L, St, B),
+ gb_trees:update(L, MergedSt#st{ct=[]}, B)
+ end}.
+
+%% merge_states/3 is used when there are more than one way to arrive
+%% at this point, and the type states for the different paths has
+%% to be merged. The type states are downgraded to the least common
+%% subset for the subsequent code.
+
+merge_states(0, St, _Branched) -> St;
+merge_states(L, St, Branched) ->
+ case gb_trees:lookup(L, Branched) of
+ none -> St;
+ {value,OtherSt} when St == none -> OtherSt;
+ {value,OtherSt} ->
+ merge_states_1(St, OtherSt)
+ end.
+
+merge_states_1(#st{x=Xs0,y=Ys0,numy=NumY0,h=H0}=St,
+ #st{x=Xs1,y=Ys1,numy=NumY1,h=H1}) ->
+ NumY = merge_stk(NumY0, NumY1),
+ Xs = merge_regs(Xs0, Xs1),
+ Ys = merge_regs(Ys0, Ys1),
+ St#st{x=Xs,y=Ys,numy=NumY,h=min(H0, H1)}.
+
+merge_stk(S, S) -> S;
+merge_stk(_, _) -> undecided.
+
+merge_regs(Rs0, Rs1) ->
+ Rs = merge_regs_1(gb_trees:to_list(Rs0), gb_trees:to_list(Rs1)),
+ gb_trees_from_list(Rs).
+
+merge_regs_1([Same|Rs1], [Same|Rs2]) ->
+ [Same|merge_regs_1(Rs1, Rs2)];
+merge_regs_1([{R1,_}|Rs1], [{R2,_}|_]=Rs2) when R1 < R2 ->
+ merge_regs_1(Rs1, Rs2);
+merge_regs_1([{R1,_}|_]=Rs1, [{R2,_}|Rs2]) when R1 > R2 ->
+ merge_regs_1(Rs1, Rs2);
+merge_regs_1([{R,Type1}|Rs1], [{R,Type2}|Rs2]) ->
+ [{R,merge_types(Type1, Type2)}|merge_regs_1(Rs1, Rs2)];
+merge_regs_1([], []) -> [];
+merge_regs_1([], [_|_]) -> [];
+merge_regs_1([_|_], []) -> [].
+
+merge_types(T, T) -> T;
+merge_types(initialized=I, _) -> I;
+merge_types(_, initialized=I) -> I;
+merge_types({tuple,Same}=T, {tuple,Same}) -> T;
+merge_types({tuple,A}, {tuple,B}) ->
+ {tuple,[min(tuple_sz(A), tuple_sz(B))]};
+merge_types({Type,A}, {Type,B})
+ when Type == atom; Type == integer; Type == float ->
+ if A =:= B -> {Type,A};
+ true -> {Type,[]}
+ end;
+merge_types({Type,_}, number)
+ when Type == integer; Type == float ->
+ number;
+merge_types(number, {Type,_})
+ when Type == integer; Type == float ->
+ number;
+merge_types(bool, {atom,A}) ->
+ merge_bool(A);
+merge_types({atom,A}, bool) ->
+ merge_bool(A);
+merge_types(_, _) -> term.
+
+tuple_sz([Sz]) -> Sz;
+tuple_sz(Sz) -> Sz.
+
+merge_bool([]) -> {atom,[]};
+merge_bool(true) -> bool;
+merge_bool(false) -> bool;
+merge_bool(_) -> {atom,[]}.
+
+verify_y_init(#vst{current=#st{numy=none}}) -> ok;
+verify_y_init(#vst{current=#st{numy=undecided}}) ->
+ error(unknown_size_of_stackframe);
+verify_y_init(#vst{current=#st{y=Ys,numy=NumY}}) ->
+ verify_y_init_1(NumY, Ys).
+
+verify_y_init_1(0, _) -> ok;
+verify_y_init_1(N, Ys) ->
+ Y = N-1,
+ case gb_trees:is_defined(Y, Ys) of
+ false -> error({{y,Y},not_initialized});
+ true -> verify_y_init_1(Y, Ys)
+ end.
+
+verify_live(0, #vst{}) -> ok;
+verify_live(N, #vst{current=#st{x=Xs}}) ->
+ verify_live_1(N, Xs).
+
+verify_live_1(0, _) -> ok;
+verify_live_1(N, Xs) ->
+ X = N-1,
+ case gb_trees:is_defined(X, Xs) of
+ false -> error({{x,X},not_live});
+ true -> verify_live_1(X, Xs)
+ end.
+
+eat_heap(N, #vst{current=#st{h=Heap0}=St}=Vst) ->
+ case Heap0-N of
+ Neg when Neg < 0 ->
+ error({heap_overflow,{left,Heap0},{wanted,N}});
+ Heap ->
+ Vst#vst{current=St#st{h=Heap}}
+ end.
+
+bif_type('-', Src, Vst) ->
+ arith_type(Src, Vst);
+bif_type('+', Src, Vst) ->
+ arith_type(Src, Vst);
+bif_type('*', Src, Vst) ->
+ arith_type(Src, Vst);
+bif_type(abs, [Num], Vst) ->
+ case get_type(Num, Vst) of
+ {float,_}=T -> T;
+ {integer,_}=T -> T;
+ _ -> number
+ end;
+bif_type(float, _, _) -> {float,[]};
+bif_type('/', _, _) -> {float,[]};
+%% Integer operations.
+bif_type('div', [_,_], _) -> {integer,[]};
+bif_type('rem', [_,_], _) -> {integer,[]};
+bif_type(length, [_], _) -> {integer,[]};
+bif_type(size, [_], _) -> {integer,[]};
+bif_type(trunc, [_], _) -> {integer,[]};
+bif_type(round, [_], _) -> {integer,[]};
+bif_type('band', [_,_], _) -> {integer,[]};
+bif_type('bor', [_,_], _) -> {integer,[]};
+bif_type('bxor', [_,_], _) -> {integer,[]};
+bif_type('bnot', [_], _) -> {integer,[]};
+bif_type('bsl', [_,_], _) -> {integer,[]};
+bif_type('bsr', [_,_], _) -> {integer,[]};
+%% Booleans.
+bif_type('==', [_,_], _) -> bool;
+bif_type('/=', [_,_], _) -> bool;
+bif_type('=<', [_,_], _) -> bool;
+bif_type('<', [_,_], _) -> bool;
+bif_type('>=', [_,_], _) -> bool;
+bif_type('>', [_,_], _) -> bool;
+bif_type('=:=', [_,_], _) -> bool;
+bif_type('=/=', [_,_], _) -> bool;
+bif_type('not', [_], _) -> bool;
+bif_type('and', [_,_], _) -> bool;
+bif_type('or', [_,_], _) -> bool;
+bif_type('xor', [_,_], _) -> bool;
+bif_type(is_atom, [_], _) -> bool;
+bif_type(is_boolean, [_], _) -> bool;
+bif_type(is_binary, [_], _) -> bool;
+bif_type(is_constant, [_], _) -> bool;
+bif_type(is_float, [_], _) -> bool;
+bif_type(is_function, [_], _) -> bool;
+bif_type(is_integer, [_], _) -> bool;
+bif_type(is_list, [_], _) -> bool;
+bif_type(is_number, [_], _) -> bool;
+bif_type(is_pid, [_], _) -> bool;
+bif_type(is_port, [_], _) -> bool;
+bif_type(is_reference, [_], _) -> bool;
+bif_type(is_tuple, [_], _) -> bool;
+%% Misc.
+bif_type(node, [], _) -> {atom,[]};
+bif_type(node, [_], _) -> {atom,[]};
+bif_type(hd, [_], _) -> term;
+bif_type(tl, [_], _) -> term;
+bif_type(get, [_], _) -> term;
+bif_type(raise, [_,_], _) -> exception;
+bif_type(_, _, _) -> term.
+
+arith_type([A,B], Vst) ->
+ case {get_type(A, Vst),get_type(B, Vst)} of
+ {{float,_},_} -> {float,[]};
+ {_,{float,_}} -> {float,[]};
+ {_,_} -> number
+ end;
+arith_type(_, _) -> number.
+
+return_type({extfunc,M,F,A}, Vst) ->
+ return_type_1(M, F, A, Vst).
+
+return_type_1(erlang, setelement, 3, Vst) ->
+ Tuple = {x,1},
+ TupleType =
+ case get_type(Tuple, Vst) of
+ {tuple,_}=TT -> TT;
+ _ -> {tuple,[0]}
+ end,
+ case get_type({x,0}, Vst) of
+ {integer,[]} -> TupleType;
+ {integer,I} -> upgrade_type({tuple,[I]}, TupleType);
+ _ -> TupleType
+ end;
+return_type_1(erlang, F, A, _) ->
+ return_type_erl(F, A);
+return_type_1(math, F, A, _) ->
+ return_type_math(F, A);
+return_type_1(_, _, _, _) -> term.
+
+return_type_erl(exit, 1) -> exception;
+return_type_erl(throw, 1) -> exception;
+return_type_erl(fault, 1) -> exception;
+return_type_erl(fault, 2) -> exception;
+return_type_erl(error, 1) -> exception;
+return_type_erl(error, 2) -> exception;
+return_type_erl(_, _) -> term.
+
+return_type_math(cos, 1) -> {float,[]};
+return_type_math(cosh, 1) -> {float,[]};
+return_type_math(sin, 1) -> {float,[]};
+return_type_math(sinh, 1) -> {float,[]};
+return_type_math(tan, 1) -> {float,[]};
+return_type_math(tanh, 1) -> {float,[]};
+return_type_math(acos, 1) -> {float,[]};
+return_type_math(acosh, 1) -> {float,[]};
+return_type_math(asin, 1) -> {float,[]};
+return_type_math(asinh, 1) -> {float,[]};
+return_type_math(atan, 1) -> {float,[]};
+return_type_math(atanh, 1) -> {float,[]};
+return_type_math(erf, 1) -> {float,[]};
+return_type_math(erfc, 1) -> {float,[]};
+return_type_math(exp, 1) -> {float,[]};
+return_type_math(log, 1) -> {float,[]};
+return_type_math(log10, 1) -> {float,[]};
+return_type_math(sqrt, 1) -> {float,[]};
+return_type_math(atan2, 2) -> {float,[]};
+return_type_math(pow, 2) -> {float,[]};
+return_type_math(pi, 0) -> {float,[]};
+return_type_math(_, _) -> term.
+
+min(A, B) when is_integer(A), is_integer(B), A < B -> A;
+min(A, B) when is_integer(A), is_integer(B) -> B.
+
+max(A, B) when is_integer(A), is_integer(B), A > B -> A;
+max(A, B) when is_integer(A), is_integer(B) -> B.
+
+gb_trees_from_list(L) -> gb_trees:from_orddict(orddict:from_list(L)).
+
+-ifdef(DEBUG).
+error(Error) -> exit(Error).
+-else.
+error(Error) -> throw(Error).
+-endif.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl
new file mode 100644
index 0000000000..be9e088276
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl.erl
@@ -0,0 +1,4169 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl.erl,v 1.3 2010/03/04 13:54:20 maria Exp $
+
+%% =====================================================================
+%% @doc Core Erlang abstract syntax trees.
+%%
+%% <p> This module defines an abstract data type for representing Core
+%% Erlang source code as syntax trees.</p>
+%%
+%% <p>A recommended starting point for the first-time user is the
+%% documentation of the function <a
+%% href="#type-1"><code>type/1</code></a>.</p>
+%%
+%% <h3><b>NOTES:</b></h3>
+%%
+%% <p>This module deals with the composition and decomposition of
+%% <em>syntactic</em> entities (as opposed to semantic ones); its
+%% purpose is to hide all direct references to the data structures
+%% used to represent these entities. With few exceptions, the
+%% functions in this module perform no semantic interpretation of
+%% their inputs, and in general, the user is assumed to pass
+%% type-correct arguments - if this is not done, the effects are not
+%% defined.</p>
+%%
+%% <p>The internal representations of abstract syntax trees are
+%% subject to change without notice, and should not be documented
+%% outside this module. Furthermore, we do not give any guarantees on
+%% how an abstract syntax tree may or may not be represented, <em>with
+%% the following exceptions</em>: no syntax tree is represented by a
+%% single atom, such as <code>none</code>, by a list constructor
+%% <code>[X | Y]</code>, or by the empty list <code>[]</code>. This
+%% can be relied on when writing functions that operate on syntax
+%% trees.</p>
+%%
+%% @type cerl(). An abstract Core Erlang syntax tree.
+%%
+%% <p>Every abstract syntax tree has a <em>type</em>, given by the
+%% function <a href="#type-1"><code>type/1</code></a>. In addition,
+%% each syntax tree has a list of <em>user annotations</em> (cf. <a
+%% href="#get_ann-1"><code>get_ann/1</code></a>), which are included
+%% in the Core Erlang syntax.</p>
+
+-module(cerl).
+
+-export([abstract/1, add_ann/2, alias_pat/1, alias_var/1,
+ ann_abstract/2, ann_c_alias/3, ann_c_apply/3, ann_c_atom/2,
+ ann_c_call/4, ann_c_case/3, ann_c_catch/2, ann_c_char/2,
+ ann_c_clause/3, ann_c_clause/4, ann_c_cons/3, ann_c_float/2,
+ ann_c_fname/3, ann_c_fun/3, ann_c_int/2, ann_c_let/4,
+ ann_c_letrec/3, ann_c_module/4, ann_c_module/5, ann_c_nil/1,
+ ann_c_cons_skel/3, ann_c_tuple_skel/2, ann_c_primop/3,
+ ann_c_receive/2, ann_c_receive/4, ann_c_seq/3, ann_c_string/2,
+ ann_c_try/6, ann_c_tuple/2, ann_c_values/2, ann_c_var/2,
+ ann_make_data/3, ann_make_list/2, ann_make_list/3,
+ ann_make_data_skel/3, ann_make_tree/3, apply_args/1,
+ apply_arity/1, apply_op/1, atom_lit/1, atom_name/1, atom_val/1,
+ c_alias/2, c_apply/2, c_atom/1, c_call/3, c_case/2, c_catch/1,
+ c_char/1, c_clause/2, c_clause/3, c_cons/2, c_float/1,
+ c_fname/2, c_fun/2, c_int/1, c_let/3, c_letrec/2, c_module/3,
+ c_module/4, c_nil/0, c_cons_skel/2, c_tuple_skel/1, c_primop/2,
+ c_receive/1, c_receive/3, c_seq/2, c_string/1, c_try/5,
+ c_tuple/1, c_values/1, c_var/1, call_args/1, call_arity/1,
+ call_module/1, call_name/1, case_arg/1, case_arity/1,
+ case_clauses/1, catch_body/1, char_lit/1, char_val/1,
+ clause_arity/1, clause_body/1, clause_guard/1, clause_pats/1,
+ clause_vars/1, concrete/1, cons_hd/1, cons_tl/1, copy_ann/2,
+ data_arity/1, data_es/1, data_type/1, float_lit/1, float_val/1,
+ fname_arity/1, fname_id/1, fold_literal/1, from_records/1,
+ fun_arity/1, fun_body/1, fun_vars/1, get_ann/1, int_lit/1,
+ int_val/1, is_c_alias/1, is_c_apply/1, is_c_atom/1,
+ is_c_call/1, is_c_case/1, is_c_catch/1, is_c_char/1,
+ is_c_clause/1, is_c_cons/1, is_c_float/1, is_c_fname/1,
+ is_c_fun/1, is_c_int/1, is_c_let/1, is_c_letrec/1, is_c_list/1,
+ is_c_module/1, is_c_nil/1, is_c_primop/1, is_c_receive/1,
+ is_c_seq/1, is_c_string/1, is_c_try/1, is_c_tuple/1,
+ is_c_values/1, is_c_var/1, is_data/1, is_leaf/1, is_literal/1,
+ is_literal_term/1, is_print_char/1, is_print_string/1,
+ let_arg/1, let_arity/1, let_body/1, let_vars/1, letrec_body/1,
+ letrec_defs/1, letrec_vars/1, list_elements/1, list_length/1,
+ make_data/2, make_list/1, make_list/2, make_data_skel/2,
+ make_tree/2, meta/1, module_attrs/1, module_defs/1,
+ module_exports/1, module_name/1, module_vars/1,
+ pat_list_vars/1, pat_vars/1, primop_args/1, primop_arity/1,
+ primop_name/1, receive_action/1, receive_clauses/1,
+ receive_timeout/1, seq_arg/1, seq_body/1, set_ann/2,
+ string_lit/1, string_val/1, subtrees/1, to_records/1,
+ try_arg/1, try_body/1, try_vars/1, try_evars/1, try_handler/1,
+ tuple_arity/1, tuple_es/1, type/1, unfold_literal/1,
+ update_c_alias/3, update_c_apply/3, update_c_call/4,
+ update_c_case/3, update_c_catch/2, update_c_clause/4,
+ update_c_cons/3, update_c_cons_skel/3, update_c_fname/2,
+ update_c_fname/3, update_c_fun/3, update_c_let/4,
+ update_c_letrec/3, update_c_module/5, update_c_primop/3,
+ update_c_receive/4, update_c_seq/3, update_c_try/6,
+ update_c_tuple/2, update_c_tuple_skel/2, update_c_values/2,
+ update_c_var/2, update_data/3, update_list/2, update_list/3,
+ update_data_skel/3, update_tree/2, update_tree/3,
+ values_arity/1, values_es/1, var_name/1, c_binary/1,
+ update_c_binary/2, ann_c_binary/2, is_c_binary/1,
+ binary_segments/1, c_bitstr/3, c_bitstr/4, c_bitstr/5,
+ update_c_bitstr/5, update_c_bitstr/6, ann_c_bitstr/5,
+ ann_c_bitstr/6, is_c_bitstr/1, bitstr_val/1, bitstr_size/1,
+ bitstr_bitsize/1, bitstr_unit/1, bitstr_type/1,
+ bitstr_flags/1]).
+
+-include("core_parse.hrl").
+
+
+%% =====================================================================
+%% Representation (general)
+%%
+%% All nodes are represented by tuples of arity 2 or (generally)
+%% greater, whose first element is an atom which uniquely identifies the
+%% type of the node, and whose second element is a (proper) list of
+%% annotation terms associated with the node - this is by default empty.
+%%
+%% For most node constructor functions, there are analogous functions
+%% named 'ann_...', taking one extra argument 'As' (always the first
+%% argument), specifying an annotation list at node creation time.
+%% Similarly, there are also functions named 'update_...', taking one
+%% extra argument 'Old', specifying a node from which all fields not
+%% explicitly given as arguments should be copied (generally, this is
+%% the annotation field only).
+%% =====================================================================
+
+%% This defines the general representation of constant literals:
+
+-record(literal, {ann = [], val}).
+
+
+%% @spec type(Node::cerl()) -> atom()
+%%
+%% @doc Returns the type tag of <code>Node</code>. Current node types
+%% are:
+%%
+%% <p><center><table border="1">
+%% <tr>
+%% <td>alias</td>
+%% <td>apply</td>
+%% <td>binary</td>
+%% <td>bitstr</td>
+%% <td>call</td>
+%% <td>case</td>
+%% <td>catch</td>
+%% </tr><tr>
+%% <td>clause</td>
+%% <td>cons</td>
+%% <td>fun</td>
+%% <td>let</td>
+%% <td>letrec</td>
+%% <td>literal</td>
+%% <td>module</td>
+%% </tr><tr>
+%% <td>primop</td>
+%% <td>receive</td>
+%% <td>seq</td>
+%% <td>try</td>
+%% <td>tuple</td>
+%% <td>values</td>
+%% <td>var</td>
+%% </tr>
+%% </table></center></p>
+%%
+%% <p>Note: The name of the primary constructor function for a node
+%% type is always the name of the type itself, prefixed by
+%% "<code>c_</code>"; recognizer predicates are correspondingly
+%% prefixed by "<code>is_c_</code>". Furthermore, to simplify
+%% preservation of annotations (cf. <code>get_ann/1</code>), there are
+%% analogous constructor functions prefixed by "<code>ann_c_</code>"
+%% and "<code>update_c_</code>", for setting the annotation list of
+%% the new node to either a specific value or to the annotations of an
+%% existing node, respectively.</p>
+%%
+%% @see abstract/1
+%% @see c_alias/2
+%% @see c_apply/2
+%% @see c_binary/1
+%% @see c_bitstr/5
+%% @see c_call/3
+%% @see c_case/2
+%% @see c_catch/1
+%% @see c_clause/3
+%% @see c_cons/2
+%% @see c_fun/2
+%% @see c_let/3
+%% @see c_letrec/2
+%% @see c_module/3
+%% @see c_primop/2
+%% @see c_receive/1
+%% @see c_seq/2
+%% @see c_try/3
+%% @see c_tuple/1
+%% @see c_values/1
+%% @see c_var/1
+%% @see get_ann/1
+%% @see to_records/1
+%% @see from_records/1
+%% @see data_type/1
+%% @see subtrees/1
+%% @see meta/1
+
+type(Node) ->
+ element(1, Node).
+
+
+%% @spec is_leaf(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is a leaf node,
+%% otherwise <code>false</code>. The current leaf node types are
+%% <code>literal</code> and <code>var</code>.
+%%
+%% <p>Note: all literals (cf. <code>is_literal/1</code>) are leaf
+%% nodes, even if they represent structured (constant) values such as
+%% <code>{foo, [bar, baz]}</code>. Also note that variables are leaf
+%% nodes but not literals.</p>
+%%
+%% @see type/1
+%% @see is_literal/1
+
+is_leaf(Node) ->
+ case type(Node) of
+ literal -> true;
+ var -> true;
+ _ -> false
+ end.
+
+
+%% @spec get_ann(cerl()) -> [term()]
+%%
+%% @doc Returns the list of user annotations associated with a syntax
+%% tree node. For a newly created node, this is the empty list. The
+%% annotations may be any terms.
+%%
+%% @see set_ann/2
+
+get_ann(Node) ->
+ element(2, Node).
+
+
+%% @spec set_ann(Node::cerl(), Annotations::[term()]) -> cerl()
+%%
+%% @doc Sets the list of user annotations of <code>Node</code> to
+%% <code>Annotations</code>.
+%%
+%% @see get_ann/1
+%% @see add_ann/2
+%% @see copy_ann/2
+
+set_ann(Node, List) ->
+ setelement(2, Node, List).
+
+
+%% @spec add_ann(Annotations::[term()], Node::cerl()) -> cerl()
+%%
+%% @doc Appends <code>Annotations</code> to the list of user
+%% annotations of <code>Node</code>.
+%%
+%% <p>Note: this is equivalent to <code>set_ann(Node, Annotations ++
+%% get_ann(Node))</code>, but potentially more efficient.</p>
+%%
+%% @see get_ann/1
+%% @see set_ann/2
+
+add_ann(Terms, Node) ->
+ set_ann(Node, Terms ++ get_ann(Node)).
+
+
+%% @spec copy_ann(Source::cerl(), Target::cerl()) -> cerl()
+%%
+%% @doc Copies the list of user annotations from <code>Source</code>
+%% to <code>Target</code>.
+%%
+%% <p>Note: this is equivalent to <code>set_ann(Target,
+%% get_ann(Source))</code>, but potentially more efficient.</p>
+%%
+%% @see get_ann/1
+%% @see set_ann/2
+
+copy_ann(Source, Target) ->
+ set_ann(Target, get_ann(Source)).
+
+
+%% @spec abstract(Term::term()) -> cerl()
+%%
+%% @doc Creates a syntax tree corresponding to an Erlang term.
+%% <code>Term</code> must be a literal term, i.e., one that can be
+%% represented as a source code literal. Thus, it may not contain a
+%% process identifier, port, reference, binary or function value as a
+%% subterm.
+%%
+%% <p>Note: This is a constant time operation.</p>
+%%
+%% @see ann_abstract/2
+%% @see concrete/1
+%% @see is_literal/1
+%% @see is_literal_term/1
+
+abstract(T) ->
+ #literal{val = T}.
+
+
+%% @spec ann_abstract(Annotations::[term()], Term::term()) -> cerl()
+%% @see abstract/1
+
+ann_abstract(As, T) ->
+ #literal{val = T, ann = As}.
+
+
+%% @spec is_literal_term(Term::term()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Term</code> can be
+%% represented as a literal, otherwise <code>false</code>. This
+%% function takes time proportional to the size of <code>Term</code>.
+%%
+%% @see abstract/1
+
+is_literal_term(T) when integer(T) -> true;
+is_literal_term(T) when float(T) -> true;
+is_literal_term(T) when atom(T) -> true;
+is_literal_term([]) -> true;
+is_literal_term([H | T]) ->
+ case is_literal_term(H) of
+ true ->
+ is_literal_term(T);
+ false ->
+ false
+ end;
+is_literal_term(T) when tuple(T) ->
+ is_literal_term_list(tuple_to_list(T));
+is_literal_term(_) ->
+ false.
+
+is_literal_term_list([T | Ts]) ->
+ case is_literal_term(T) of
+ true ->
+ is_literal_term_list(Ts);
+ false ->
+ false
+ end;
+is_literal_term_list([]) ->
+ true.
+
+
+%% @spec concrete(Node::cerl()) -> term()
+%%
+%% @doc Returns the Erlang term represented by a syntax tree. An
+%% exception is thrown if <code>Node</code> does not represent a
+%% literal term.
+%%
+%% <p>Note: This is a constant time operation.</p>
+%%
+%% @see abstract/1
+%% @see is_literal/1
+
+%% Because the normal tuple and list constructor operations always
+%% return a literal if the arguments are literals, 'concrete' and
+%% 'is_literal' never need to traverse the structure.
+
+concrete(#literal{val = V}) ->
+ V.
+
+
+%% @spec is_literal(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% literal term, otherwise <code>false</code>. This function returns
+%% <code>true</code> if and only if the value of
+%% <code>concrete(Node)</code> is defined.
+%%
+%% <p>Note: This is a constant time operation.</p>
+%%
+%% @see abstract/1
+%% @see concrete/1
+%% @see fold_literal/1
+
+is_literal(#literal{}) ->
+ true;
+is_literal(_) ->
+ false.
+
+
+%% @spec fold_literal(Node::cerl()) -> cerl()
+%%
+%% @doc Assures that literals have a compact representation. This is
+%% occasionally useful if <code>c_cons_skel/2</code>,
+%% <code>c_tuple_skel/1</code> or <code>unfold_literal/1</code> were
+%% used in the construction of <code>Node</code>, and you want to revert
+%% to the normal "folded" representation of literals. If
+%% <code>Node</code> represents a tuple or list constructor, its
+%% elements are rewritten recursively, and the node is reconstructed
+%% using <code>c_cons/2</code> or <code>c_tuple/1</code>, respectively;
+%% otherwise, <code>Node</code> is not changed.
+%%
+%% @see is_literal/1
+%% @see c_cons_skel/2
+%% @see c_tuple_skel/1
+%% @see c_cons/2
+%% @see c_tuple/1
+%% @see unfold_literal/1
+
+fold_literal(Node) ->
+ case type(Node) of
+ tuple ->
+ update_c_tuple(Node, fold_literal_list(tuple_es(Node)));
+ cons ->
+ update_c_cons(Node, fold_literal(cons_hd(Node)),
+ fold_literal(cons_tl(Node)));
+ _ ->
+ Node
+ end.
+
+fold_literal_list([E | Es]) ->
+ [fold_literal(E) | fold_literal_list(Es)];
+fold_literal_list([]) ->
+ [].
+
+
+%% @spec unfold_literal(Node::cerl()) -> cerl()
+%%
+%% @doc Assures that literals have a fully expanded representation. If
+%% <code>Node</code> represents a literal tuple or list constructor, its
+%% elements are rewritten recursively, and the node is reconstructed
+%% using <code>c_cons_skel/2</code> or <code>c_tuple_skel/1</code>,
+%% respectively; otherwise, <code>Node</code> is not changed. The {@link
+%% fold_literal/1} can be used to revert to the normal compact
+%% representation.
+%%
+%% @see is_literal/1
+%% @see c_cons_skel/2
+%% @see c_tuple_skel/1
+%% @see c_cons/2
+%% @see c_tuple/1
+%% @see fold_literal/1
+
+unfold_literal(Node) ->
+ case type(Node) of
+ literal ->
+ copy_ann(Node, unfold_concrete(concrete(Node)));
+ _ ->
+ Node
+ end.
+
+unfold_concrete(Val) ->
+ case Val of
+ _ when tuple(Val) ->
+ c_tuple_skel(unfold_concrete_list(tuple_to_list(Val)));
+ [H|T] ->
+ c_cons_skel(unfold_concrete(H), unfold_concrete(T));
+ _ ->
+ abstract(Val)
+ end.
+
+unfold_concrete_list([E | Es]) ->
+ [unfold_concrete(E) | unfold_concrete_list(Es)];
+unfold_concrete_list([]) ->
+ [].
+
+
+%% ---------------------------------------------------------------------
+
+-record(module, {ann = [], name, exports, attrs, defs}).
+
+
+%% @spec c_module(Name::cerl(), Exports, Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @equiv c_module(Name, Exports, [], Definitions)
+
+c_module(Name, Exports, Es) ->
+ #module{name = Name, exports = Exports, attrs = [], defs = Es}.
+
+
+%% @spec c_module(Name::cerl(), Exports, Attributes, Definitions) ->
+%% cerl()
+%%
+%% Exports = [cerl()]
+%% Attributes = [{cerl(), cerl()}]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @doc Creates an abstract module definition. The result represents
+%% <pre>
+%% module <em>Name</em> [<em>E1</em>, ..., <em>Ek</em>]
+%% attributes [<em>K1</em> = <em>T1</em>, ...,
+%% <em>Km</em> = <em>Tm</em>]
+%% <em>V1</em> = <em>F1</em>
+%% ...
+%% <em>Vn</em> = <em>Fn</em>
+%% end</pre>
+%%
+%% if <code>Exports</code> = <code>[E1, ..., Ek]</code>,
+%% <code>Attributes</code> = <code>[{K1, T1}, ..., {Km, Tm}]</code>,
+%% and <code>Definitions</code> = <code>[{V1, F1}, ..., {Vn,
+%% Fn}]</code>.
+%%
+%% <p><code>Name</code> and all the <code>Ki</code> must be atom
+%% literals, and all the <code>Ti</code> must be constant literals. All
+%% the <code>Vi</code> and <code>Ei</code> must have type
+%% <code>var</code> and represent function names. All the
+%% <code>Fi</code> must have type <code>'fun'</code>.</p>
+%%
+%% @see c_module/3
+%% @see module_name/1
+%% @see module_exports/1
+%% @see module_attrs/1
+%% @see module_defs/1
+%% @see module_vars/1
+%% @see ann_c_module/4
+%% @see ann_c_module/5
+%% @see update_c_module/5
+%% @see c_atom/1
+%% @see c_var/1
+%% @see c_fun/2
+%% @see is_literal/1
+
+c_module(Name, Exports, Attrs, Es) ->
+ #module{name = Name, exports = Exports, attrs = Attrs, defs = Es}.
+
+
+%% @spec ann_c_module(As::[term()], Name::cerl(), Exports,
+%% Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @see c_module/3
+%% @see ann_c_module/5
+
+ann_c_module(As, Name, Exports, Es) ->
+ #module{name = Name, exports = Exports, attrs = [], defs = Es,
+ ann = As}.
+
+
+%% @spec ann_c_module(As::[term()], Name::cerl(), Exports,
+%% Attributes, Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Attributes = [{cerl(), cerl()}]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @see c_module/4
+%% @see ann_c_module/4
+
+ann_c_module(As, Name, Exports, Attrs, Es) ->
+ #module{name = Name, exports = Exports, attrs = Attrs, defs = Es,
+ ann = As}.
+
+
+%% @spec update_c_module(Old::cerl(), Name::cerl(), Exports,
+%% Attributes, Definitions) -> cerl()
+%%
+%% Exports = [cerl()]
+%% Attributes = [{cerl(), cerl()}]
+%% Definitions = [{cerl(), cerl()}]
+%%
+%% @see c_module/4
+
+update_c_module(Node, Name, Exports, Attrs, Es) ->
+ #module{name = Name, exports = Exports, attrs = Attrs, defs = Es,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_module(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% module definition, otherwise <code>false</code>.
+%%
+%% @see type/1
+
+is_c_module(#module{}) ->
+ true;
+is_c_module(_) ->
+ false.
+
+
+%% @spec module_name(Node::cerl()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract module definition.
+%%
+%% @see c_module/4
+
+module_name(Node) ->
+ Node#module.name.
+
+
+%% @spec module_exports(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of exports subtrees of an abstract module
+%% definition.
+%%
+%% @see c_module/4
+
+module_exports(Node) ->
+ Node#module.exports.
+
+
+%% @spec module_attrs(Node::cerl()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of pairs of attribute key/value subtrees of
+%% an abstract module definition.
+%%
+%% @see c_module/4
+
+module_attrs(Node) ->
+ Node#module.attrs.
+
+
+%% @spec module_defs(Node::cerl()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of function definitions of an abstract module
+%% definition.
+%%
+%% @see c_module/4
+
+module_defs(Node) ->
+ Node#module.defs.
+
+
+%% @spec module_vars(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side function variable subtrees
+%% of an abstract module definition.
+%%
+%% @see c_module/4
+
+module_vars(Node) ->
+ [F || {F, _} <- module_defs(Node)].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_int(Value::integer()) -> cerl()
+%%
+%%
+%% @doc Creates an abstract integer literal. The lexical
+%% representation is the canonical decimal numeral of
+%% <code>Value</code>.
+%%
+%% @see ann_c_int/2
+%% @see is_c_int/1
+%% @see int_val/1
+%% @see int_lit/1
+%% @see c_char/1
+
+c_int(Value) ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_int(As::[term()], Value::integer()) -> cerl()
+%% @see c_int/1
+
+ann_c_int(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_int(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents an
+%% integer literal, otherwise <code>false</code>.
+%% @see c_int/1
+
+is_c_int(#literal{val = V}) when integer(V) ->
+ true;
+is_c_int(_) ->
+ false.
+
+
+%% @spec int_val(cerl()) -> integer()
+%%
+%% @doc Returns the value represented by an integer literal node.
+%% @see c_int/1
+
+int_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec int_lit(cerl()) -> string()
+%%
+%% @doc Returns the numeral string represented by an integer literal
+%% node.
+%% @see c_int/1
+
+int_lit(Node) ->
+ integer_to_list(int_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_float(Value::float()) -> cerl()
+%%
+%% @doc Creates an abstract floating-point literal. The lexical
+%% representation is the decimal floating-point numeral of
+%% <code>Value</code>.
+%%
+%% @see ann_c_float/2
+%% @see is_c_float/1
+%% @see float_val/1
+%% @see float_lit/1
+
+%% Note that not all floating-point numerals can be represented with
+%% full precision.
+
+c_float(Value) ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_float(As::[term()], Value::float()) -> cerl()
+%% @see c_float/1
+
+ann_c_float(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_float(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% floating-point literal, otherwise <code>false</code>.
+%% @see c_float/1
+
+is_c_float(#literal{val = V}) when float(V) ->
+ true;
+is_c_float(_) ->
+ false.
+
+
+%% @spec float_val(cerl()) -> float()
+%%
+%% @doc Returns the value represented by a floating-point literal
+%% node.
+%% @see c_float/1
+
+float_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec float_lit(cerl()) -> string()
+%%
+%% @doc Returns the numeral string represented by a floating-point
+%% literal node.
+%% @see c_float/1
+
+float_lit(Node) ->
+ float_to_list(float_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_atom(Name) -> cerl()
+%% Name = atom() | string()
+%%
+%% @doc Creates an abstract atom literal. The print name of the atom
+%% is the character sequence represented by <code>Name</code>.
+%%
+%% <p>Note: passing a string as argument to this function causes a
+%% corresponding atom to be created for the internal representation.</p>
+%%
+%% @see ann_c_atom/2
+%% @see is_c_atom/1
+%% @see atom_val/1
+%% @see atom_name/1
+%% @see atom_lit/1
+
+c_atom(Name) when atom(Name) ->
+ #literal{val = Name};
+c_atom(Name) ->
+ #literal{val = list_to_atom(Name)}.
+
+
+%% @spec ann_c_atom(As::[term()], Name) -> cerl()
+%% Name = atom() | string()
+%% @see c_atom/1
+
+ann_c_atom(As, Name) when atom(Name) ->
+ #literal{val = Name, ann = As};
+ann_c_atom(As, Name) ->
+ #literal{val = list_to_atom(Name), ann = As}.
+
+
+%% @spec is_c_atom(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents an
+%% atom literal, otherwise <code>false</code>.
+%%
+%% @see c_atom/1
+
+is_c_atom(#literal{val = V}) when atom(V) ->
+ true;
+is_c_atom(_) ->
+ false.
+
+%% @spec atom_val(cerl())-> atom()
+%%
+%% @doc Returns the value represented by an abstract atom.
+%%
+%% @see c_atom/1
+
+atom_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec atom_name(cerl()) -> string()
+%%
+%% @doc Returns the printname of an abstract atom.
+%%
+%% @see c_atom/1
+
+atom_name(Node) ->
+ atom_to_list(atom_val(Node)).
+
+
+%% @spec atom_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract
+%% atom. This always includes surrounding single-quote characters.
+%%
+%% <p>Note that an abstract atom may have several literal
+%% representations, and that the representation yielded by this
+%% function is not fixed; e.g.,
+%% <code>atom_lit(c_atom("a\012b"))</code> could yield the string
+%% <code>"\'a\\nb\'"</code>.</p>
+%%
+%% @see c_atom/1
+
+%% TODO: replace the use of the unofficial 'write_string/2'.
+
+atom_lit(Node) ->
+ io_lib:write_string(atom_name(Node), $'). %' stupid Emacs.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_char(Value) -> cerl()
+%%
+%% Value = char() | integer()
+%%
+%% @doc Creates an abstract character literal. If the local
+%% implementation of Erlang defines <code>char()</code> as a subset of
+%% <code>integer()</code>, this function is equivalent to
+%% <code>c_int/1</code>. Otherwise, if the given value is an integer,
+%% it will be converted to the character with the corresponding
+%% code. The lexical representation of a character is
+%% "<code>$<em>Char</em></code>", where <code>Char</code> is a single
+%% printing character or an escape sequence.
+%%
+%% @see c_int/1
+%% @see c_string/1
+%% @see ann_c_char/2
+%% @see is_c_char/1
+%% @see char_val/1
+%% @see char_lit/1
+%% @see is_print_char/1
+
+c_char(Value) when integer(Value), Value >= 0 ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_char(As::[term()], Value::char()) -> cerl()
+%% @see c_char/1
+
+ann_c_char(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_char(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% character literal, otherwise <code>false</code>.
+%%
+%% <p>If the local implementation of Erlang defines
+%% <code>char()</code> as a subset of <code>integer()</code>, then
+%% <code>is_c_int(<em>Node</em>)</code> will also yield
+%% <code>true</code>.</p>
+%%
+%% @see c_char/1
+%% @see is_print_char/1
+
+is_c_char(#literal{val = V}) when integer(V), V >= 0 ->
+ is_char_value(V);
+is_c_char(_) ->
+ false.
+
+
+%% @spec is_print_char(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% "printing" character, otherwise <code>false</code>. (Cf.
+%% <code>is_c_char/1</code>.) A "printing" character has either a
+%% given graphical representation, or a "named" escape sequence such
+%% as "<code>\n</code>". Currently, only ISO 8859-1 (Latin-1)
+%% character values are recognized.
+%%
+%% @see c_char/1
+%% @see is_c_char/1
+
+is_print_char(#literal{val = V}) when integer(V), V >= 0 ->
+ is_print_char_value(V);
+is_print_char(_) ->
+ false.
+
+
+%% @spec char_val(cerl()) -> char()
+%%
+%% @doc Returns the value represented by an abstract character literal.
+%%
+%% @see c_char/1
+
+char_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec char_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract
+%% character. This includes a leading <code>$</code>
+%% character. Currently, all characters that are not in the set of ISO
+%% 8859-1 (Latin-1) "printing" characters will be escaped.
+%%
+%% @see c_char/1
+
+char_lit(Node) ->
+ io_lib:write_char(char_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_string(Value::string()) -> cerl()
+%%
+%% @doc Creates an abstract string literal. Equivalent to creating an
+%% abstract list of the corresponding character literals
+%% (cf. <code>is_c_string/1</code>), but is typically more
+%% efficient. The lexical representation of a string is
+%% "<code>"<em>Chars</em>"</code>", where <code>Chars</code> is a
+%% sequence of printing characters or spaces.
+%%
+%% @see c_char/1
+%% @see ann_c_string/2
+%% @see is_c_string/1
+%% @see string_val/1
+%% @see string_lit/1
+%% @see is_print_string/1
+
+c_string(Value) ->
+ #literal{val = Value}.
+
+
+%% @spec ann_c_string(As::[term()], Value::string()) -> cerl()
+%% @see c_string/1
+
+ann_c_string(As, Value) ->
+ #literal{val = Value, ann = As}.
+
+
+%% @spec is_c_string(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% string literal, otherwise <code>false</code>. Strings are defined
+%% as lists of characters; see <code>is_c_char/1</code> for details.
+%%
+%% @see c_string/1
+%% @see is_c_char/1
+%% @see is_print_string/1
+
+is_c_string(#literal{val = V}) ->
+ is_char_list(V);
+is_c_string(_) ->
+ false.
+
+
+%% @spec is_print_string(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> may represent a
+%% string literal containing only "printing" characters, otherwise
+%% <code>false</code>. See <code>is_c_string/1</code> and
+%% <code>is_print_char/1</code> for details. Currently, only ISO
+%% 8859-1 (Latin-1) character values are recognized.
+%%
+%% @see c_string/1
+%% @see is_c_string/1
+%% @see is_print_char/1
+
+is_print_string(#literal{val = V}) ->
+ is_print_char_list(V);
+is_print_string(_) ->
+ false.
+
+
+%% @spec string_val(cerl()) -> string()
+%%
+%% @doc Returns the value represented by an abstract string literal.
+%%
+%% @see c_string/1
+
+string_val(Node) ->
+ Node#literal.val.
+
+
+%% @spec string_lit(cerl()) -> string()
+%%
+%% @doc Returns the literal string represented by an abstract string.
+%% This includes surrounding double-quote characters
+%% <code>"..."</code>. Currently, characters that are not in the set
+%% of ISO 8859-1 (Latin-1) "printing" characters will be escaped,
+%% except for spaces.
+%%
+%% @see c_string/1
+
+string_lit(Node) ->
+ io_lib:write_string(string_val(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_nil() -> cerl()
+%%
+%% @doc Creates an abstract empty list. The result represents
+%% "<code>[]</code>". The empty list is traditionally called "nil".
+%%
+%% @see ann_c_nil/1
+%% @see is_c_list/1
+%% @see c_cons/2
+
+c_nil() ->
+ #literal{val = []}.
+
+
+%% @spec ann_c_nil(As::[term()]) -> cerl()
+%% @see c_nil/0
+
+ann_c_nil(As) ->
+ #literal{val = [], ann = As}.
+
+
+%% @spec is_c_nil(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% empty list, otherwise <code>false</code>.
+
+is_c_nil(#literal{val = []}) ->
+ true;
+is_c_nil(_) ->
+ false.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_cons(Head::cerl(), Tail::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract list constructor. The result represents
+%% "<code>[<em>Head</em> | <em>Tail</em>]</code>". Note that if both
+%% <code>Head</code> and <code>Tail</code> have type
+%% <code>literal</code>, then the result will also have type
+%% <code>literal</code>, and annotations on <code>Head</code> and
+%% <code>Tail</code> are lost.
+%%
+%% <p>Recall that in Erlang, the tail element of a list constructor is
+%% not necessarily a list.</p>
+%%
+%% @see ann_c_cons/3
+%% @see update_c_cons/3
+%% @see c_cons_skel/2
+%% @see is_c_cons/1
+%% @see cons_hd/1
+%% @see cons_tl/1
+%% @see is_c_list/1
+%% @see c_nil/0
+%% @see list_elements/1
+%% @see list_length/1
+%% @see make_list/2
+
+-record(cons, {ann = [], hd, tl}).
+
+%% *Always* collapse literals.
+
+c_cons(#literal{val = Head}, #literal{val = Tail}) ->
+ #literal{val = [Head | Tail]};
+c_cons(Head, Tail) ->
+ #cons{hd = Head, tl = Tail}.
+
+
+%% @spec ann_c_cons(As::[term()], Head::cerl(), Tail::cerl()) -> cerl()
+%% @see c_cons/2
+
+ann_c_cons(As, #literal{val = Head}, #literal{val = Tail}) ->
+ #literal{val = [Head | Tail], ann = As};
+ann_c_cons(As, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = As}.
+
+
+%% @spec update_c_cons(Old::cerl(), Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons/2
+
+update_c_cons(Node, #literal{val = Head}, #literal{val = Tail}) ->
+ #literal{val = [Head | Tail], ann = get_ann(Node)};
+update_c_cons(Node, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = get_ann(Node)}.
+
+
+%% @spec c_cons_skel(Head::cerl(), Tail::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract list constructor skeleton. Does not fold
+%% constant literals, i.e., the result always has type
+%% <code>cons</code>, representing "<code>[<em>Head</em> |
+%% <em>Tail</em>]</code>".
+%%
+%% <p>This function is occasionally useful when it is necessary to have
+%% annotations on the subnodes of a list constructor node, even when the
+%% subnodes are constant literals. Note however that
+%% <code>is_literal/1</code> will yield <code>false</code> and
+%% <code>concrete/1</code> will fail if passed the result from this
+%% function.</p>
+%%
+%% <p><code>fold_literal/1</code> can be used to revert a node to the
+%% normal-form representation.</p>
+%%
+%% @see ann_c_cons_skel/3
+%% @see update_c_cons_skel/3
+%% @see c_cons/2
+%% @see is_c_cons/1
+%% @see is_c_list/1
+%% @see c_nil/0
+%% @see is_literal/1
+%% @see fold_literal/1
+%% @see concrete/1
+
+%% *Never* collapse literals.
+
+c_cons_skel(Head, Tail) ->
+ #cons{hd = Head, tl = Tail}.
+
+
+%% @spec ann_c_cons_skel(As::[term()], Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons_skel/2
+
+ann_c_cons_skel(As, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = As}.
+
+
+%% @spec update_c_cons_skel(Old::cerl(), Head::cerl(), Tail::cerl()) ->
+%% cerl()
+%% @see c_cons_skel/2
+
+update_c_cons_skel(Node, Head, Tail) ->
+ #cons{hd = Head, tl = Tail, ann = get_ann(Node)}.
+
+
+%% @spec is_c_cons(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% list constructor, otherwise <code>false</code>.
+
+is_c_cons(#cons{}) ->
+ true;
+is_c_cons(#literal{val = [_ | _]}) ->
+ true;
+is_c_cons(_) ->
+ false.
+
+
+%% @spec cons_hd(cerl()) -> cerl()
+%%
+%% @doc Returns the head subtree of an abstract list constructor.
+%%
+%% @see c_cons/2
+
+cons_hd(#cons{hd = Head}) ->
+ Head;
+cons_hd(#literal{val = [Head | _]}) ->
+ #literal{val = Head}.
+
+
+%% @spec cons_tl(cerl()) -> cerl()
+%%
+%% @doc Returns the tail subtree of an abstract list constructor.
+%%
+%% <p>Recall that the tail does not necessarily represent a proper
+%% list.</p>
+%%
+%% @see c_cons/2
+
+cons_tl(#cons{tl = Tail}) ->
+ Tail;
+cons_tl(#literal{val = [_ | Tail]}) ->
+ #literal{val = Tail}.
+
+
+%% @spec is_c_list(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% proper list, otherwise <code>false</code>. A proper list is either
+%% the empty list <code>[]</code>, or a cons cell <code>[<em>Head</em> |
+%% <em>Tail</em>]</code>, where recursively <code>Tail</code> is a
+%% proper list.
+%%
+%% <p>Note: Because <code>Node</code> is a syntax tree, the actual
+%% run-time values corresponding to its subtrees may often be partially
+%% or completely unknown. Thus, if <code>Node</code> represents e.g.
+%% "<code>[... | Ns]</code>" (where <code>Ns</code> is a variable), then
+%% the function will return <code>false</code>, because it is not known
+%% whether <code>Ns</code> will be bound to a list at run-time. If
+%% <code>Node</code> instead represents e.g. "<code>[1, 2, 3]</code>" or
+%% "<code>[A | []]</code>", then the function will return
+%% <code>true</code>.</p>
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see list_elements/1
+%% @see list_length/1
+
+is_c_list(#cons{tl = Tail}) ->
+ is_c_list(Tail);
+is_c_list(#literal{val = V}) ->
+ is_proper_list(V);
+is_c_list(_) ->
+ false.
+
+is_proper_list([_ | Tail]) ->
+ is_proper_list(Tail);
+is_proper_list([]) ->
+ true;
+is_proper_list(_) ->
+ false.
+
+%% @spec list_elements(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract list.
+%% <code>Node</code> must represent a proper list. E.g., if
+%% <code>Node</code> represents "<code>[<em>X1</em>, <em>X2</em> |
+%% [<em>X3</em>, <em>X4</em> | []]</code>", then
+%% <code>list_elements(Node)</code> yields the list <code>[X1, X2, X3,
+%% X4]</code>.
+%%
+%% @see c_cons/2
+%% @see c_nil/1
+%% @see is_c_list/1
+%% @see list_length/1
+%% @see make_list/2
+
+list_elements(#cons{hd = Head, tl = Tail}) ->
+ [Head | list_elements(Tail)];
+list_elements(#literal{val = V}) ->
+ abstract_list(V).
+
+abstract_list([X | Xs]) ->
+ [abstract(X) | abstract_list(Xs)];
+abstract_list([]) ->
+ [].
+
+
+%% @spec list_length(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract list.
+%% <code>Node</code> must represent a proper list. E.g., if
+%% <code>Node</code> represents "<code>[X1 | [X2, X3 | [X4, X5,
+%% X6]]]</code>", then <code>list_length(Node)</code> returns the
+%% integer 6.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(list_elements(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_cons/2
+%% @see c_nil/1
+%% @see is_c_list/1
+%% @see list_elements/1
+
+list_length(L) ->
+ list_length(L, 0).
+
+list_length(#cons{tl = Tail}, A) ->
+ list_length(Tail, A + 1);
+list_length(#literal{val = V}, A) ->
+ A + length(V).
+
+
+%% @spec make_list(List) -> Node
+%% @equiv make_list(List, none)
+
+make_list(List) ->
+ ann_make_list([], List).
+
+
+%% @spec make_list(List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @doc Creates an abstract list from the elements in <code>List</code>
+%% and the optional <code>Tail</code>. If <code>Tail</code> is
+%% <code>none</code>, the result will represent a nil-terminated list,
+%% otherwise it represents "<code>[... | <em>Tail</em>]</code>".
+%%
+%% @see c_cons/2
+%% @see c_nil/0
+%% @see ann_make_list/3
+%% @see update_list/3
+%% @see list_elements/1
+
+make_list(List, Tail) ->
+ ann_make_list([], List, Tail).
+
+
+%% @spec update_list(Old::cerl(), List::[cerl()]) -> cerl()
+%% @equiv update_list(Old, List, none)
+
+update_list(Node, List) ->
+ ann_make_list(get_ann(Node), List).
+
+
+%% @spec update_list(Old::cerl(), List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @see make_list/2
+%% @see update_list/2
+
+update_list(Node, List, Tail) ->
+ ann_make_list(get_ann(Node), List, Tail).
+
+
+%% @spec ann_make_list(As::[term()], List::[cerl()]) -> cerl()
+%% @equiv ann_make_list(As, List, none)
+
+ann_make_list(As, List) ->
+ ann_make_list(As, List, none).
+
+
+%% @spec ann_make_list(As::[term()], List::[cerl()], Tail) -> cerl()
+%%
+%% Tail = cerl() | none
+%%
+%% @see make_list/2
+%% @see ann_make_list/2
+
+ann_make_list(As, [H | T], Tail) ->
+ ann_c_cons(As, H, make_list(T, Tail)); % `c_cons' folds literals
+ann_make_list(As, [], none) ->
+ ann_c_nil(As);
+ann_make_list(_, [], Node) ->
+ Node.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_tuple(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract tuple. If <code>Elements</code> is
+%% <code>[E1, ..., En]</code>, the result represents
+%% "<code>{<em>E1</em>, ..., <em>En</em>}</code>". Note that if all
+%% nodes in <code>Elements</code> have type <code>literal</code>, or if
+%% <code>Elements</code> is empty, then the result will also have type
+%% <code>literal</code> and annotations on nodes in
+%% <code>Elements</code> are lost.
+%%
+%% <p>Recall that Erlang has distinct 1-tuples, i.e., <code>{X}</code>
+%% is always distinct from <code>X</code> itself.</p>
+%%
+%% @see ann_c_tuple/2
+%% @see update_c_tuple/2
+%% @see is_c_tuple/1
+%% @see tuple_es/1
+%% @see tuple_arity/1
+%% @see c_tuple_skel/1
+
+-record(tuple, {ann = [], es}).
+
+%% *Always* collapse literals.
+
+c_tuple(Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #tuple{es = Es};
+ true ->
+ #literal{val = list_to_tuple(lit_list_vals(Es))}
+ end.
+
+
+%% @spec ann_c_tuple(As::[term()], Elements::[cerl()]) -> cerl()
+%% @see c_tuple/1
+
+ann_c_tuple(As, Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #tuple{es = Es, ann = As};
+ true ->
+ #literal{val = list_to_tuple(lit_list_vals(Es)), ann = As}
+ end.
+
+
+%% @spec update_c_tuple(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple/1
+
+update_c_tuple(Node, Es) ->
+ case is_lit_list(Es) of
+ false ->
+ #tuple{es = Es, ann = get_ann(Node)};
+ true ->
+ #literal{val = list_to_tuple(lit_list_vals(Es)),
+ ann = get_ann(Node)}
+ end.
+
+
+%% @spec c_tuple_skel(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract tuple skeleton. Does not fold constant
+%% literals, i.e., the result always has type <code>tuple</code>,
+%% representing "<code>{<em>E1</em>, ..., <em>En</em>}</code>", if
+%% <code>Elements</code> is <code>[E1, ..., En]</code>.
+%%
+%% <p>This function is occasionally useful when it is necessary to have
+%% annotations on the subnodes of a tuple node, even when all the
+%% subnodes are constant literals. Note however that
+%% <code>is_literal/1</code> will yield <code>false</code> and
+%% <code>concrete/1</code> will fail if passed the result from this
+%% function.</p>
+%%
+%% <p><code>fold_literal/1</code> can be used to revert a node to the
+%% normal-form representation.</p>
+%%
+%% @see ann_c_tuple_skel/2
+%% @see update_c_tuple_skel/2
+%% @see c_tuple/1
+%% @see tuple_es/1
+%% @see is_c_tuple/1
+%% @see is_literal/1
+%% @see fold_literal/1
+%% @see concrete/1
+
+%% *Never* collapse literals.
+
+c_tuple_skel(Es) ->
+ #tuple{es = Es}.
+
+
+%% @spec ann_c_tuple_skel(As::[term()], Elements::[cerl()]) -> cerl()
+%% @see c_tuple_skel/1
+
+ann_c_tuple_skel(As, Es) ->
+ #tuple{es = Es, ann = As}.
+
+
+%% @spec update_c_tuple_skel(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_tuple_skel/1
+
+update_c_tuple_skel(Old, Es) ->
+ #tuple{es = Es, ann = get_ann(Old)}.
+
+
+%% @spec is_c_tuple(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% tuple, otherwise <code>false</code>.
+%%
+%% @see c_tuple/1
+
+is_c_tuple(#tuple{}) ->
+ true;
+is_c_tuple(#literal{val = V}) when tuple(V) ->
+ true;
+is_c_tuple(_) ->
+ false.
+
+
+%% @spec tuple_es(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract tuple.
+%%
+%% @see c_tuple/1
+
+tuple_es(#tuple{es = Es}) ->
+ Es;
+tuple_es(#literal{val = V}) ->
+ make_lit_list(tuple_to_list(V)).
+
+
+%% @spec tuple_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract tuple.
+%%
+%% <p>Note: this is equivalent to <code>length(tuple_es(Node))</code>,
+%% but potentially more efficient.</p>
+%%
+%% @see tuple_es/1
+%% @see c_tuple/1
+
+tuple_arity(#tuple{es = Es}) ->
+ length(Es);
+tuple_arity(#literal{val = V}) when tuple(V) ->
+ size(V).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_var(Name::var_name()) -> cerl()
+%%
+%% var_name() = integer() | atom() | {atom(), integer()}
+%%
+%% @doc Creates an abstract variable. A variable is identified by its
+%% name, given by the <code>Name</code> parameter.
+%%
+%% <p>If a name is given by a single atom, it should either be a
+%% "simple" atom which does not need to be single-quoted in Erlang, or
+%% otherwise its print name should correspond to a proper Erlang
+%% variable, i.e., begin with an uppercase character or an
+%% underscore. Names on the form <code>{A, N}</code> represent
+%% function name variables "<code><em>A</em>/<em>N</em></code>"; these
+%% are special variables which may be bound only in the function
+%% definitions of a module or a <code>letrec</code>. They may not be
+%% bound in <code>let</code> expressions and cannot occur in clause
+%% patterns. The atom <code>A</code> in a function name may be any
+%% atom; the integer <code>N</code> must be nonnegative. The functions
+%% <code>c_fname/2</code> etc. are utilities for handling function
+%% name variables.</p>
+%%
+%% <p>When printing variable names, they must have the form of proper
+%% Core Erlang variables and function names. E.g., a name represented
+%% by an integer such as <code>42</code> could be formatted as
+%% "<code>_42</code>", an atom <code>'Xxx'</code> simply as
+%% "<code>Xxx</code>", and an atom <code>foo</code> as
+%% "<code>_foo</code>". However, one must assure that any two valid
+%% distinct names are never mapped to the same strings. Tuples such
+%% as <code>{foo, 2}</code> representing function names can simply by
+%% formatted as "<code>'foo'/2</code>", with no risk of conflicts.</p>
+%%
+%% @see ann_c_var/2
+%% @see update_c_var/2
+%% @see is_c_var/1
+%% @see var_name/1
+%% @see c_fname/2
+%% @see c_module/4
+%% @see c_letrec/2
+
+-record(var, {ann = [], name}).
+
+c_var(Name) ->
+ #var{name = Name}.
+
+
+%% @spec ann_c_var(As::[term()], Name::var_name()) -> cerl()
+%%
+%% @see c_var/1
+
+ann_c_var(As, Name) ->
+ #var{name = Name, ann = As}.
+
+%% @spec update_c_var(Old::cerl(), Name::var_name()) -> cerl()
+%%
+%% @see c_var/1
+
+update_c_var(Node, Name) ->
+ #var{name = Name, ann = get_ann(Node)}.
+
+
+%% @spec is_c_var(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% variable, otherwise <code>false</code>.
+%%
+%% @see c_var/1
+
+is_c_var(#var{}) ->
+ true;
+is_c_var(_) ->
+ false.
+
+
+%% @spec c_fname(Name::atom(), Arity::integer()) -> cerl()
+%% @equiv c_var({Name, Arity})
+%% @see fname_id/1
+%% @see fname_arity/1
+%% @see is_c_fname/1
+%% @see ann_c_fname/3
+%% @see update_c_fname/3
+
+c_fname(Atom, Arity) ->
+ c_var({Atom, Arity}).
+
+
+%% @spec ann_c_fname(As::[term()], Name::atom(), Arity::integer()) ->
+%% cerl()
+%% @equiv ann_c_var(As, {Atom, Arity})
+%% @see c_fname/2
+
+ann_c_fname(As, Atom, Arity) ->
+ ann_c_var(As, {Atom, Arity}).
+
+
+%% @spec update_c_fname(Old::cerl(), Name::atom()) -> cerl()
+%% @doc Like <code>update_c_fname/3</code>, but takes the arity from
+%% <code>Node</code>.
+%% @see update_c_fname/3
+%% @see c_fname/2
+
+update_c_fname(#var{name = {_, Arity}, ann = As}, Atom) ->
+ #var{name = {Atom, Arity}, ann = As}.
+
+
+%% @spec update_c_fname(Old::cerl(), Name::atom(), Arity::integer()) ->
+%% cerl()
+%% @equiv update_c_var(Old, {Atom, Arity})
+%% @see update_c_fname/2
+%% @see c_fname/2
+
+update_c_fname(Node, Atom, Arity) ->
+ update_c_var(Node, {Atom, Arity}).
+
+
+%% @spec is_c_fname(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% function name variable, otherwise <code>false</code>.
+%%
+%% @see c_fname/2
+%% @see c_var/1
+%% @see c_var_name/1
+
+is_c_fname(#var{name = {A, N}}) when atom(A), integer(N), N >= 0 ->
+ true;
+is_c_fname(_) ->
+ false.
+
+
+%% @spec var_name(cerl()) -> var_name()
+%%
+%% @doc Returns the name of an abstract variable.
+%%
+%% @see c_var/1
+
+var_name(Node) ->
+ Node#var.name.
+
+
+%% @spec fname_id(cerl()) -> atom()
+%%
+%% @doc Returns the identifier part of an abstract function name
+%% variable.
+%%
+%% @see fname_arity/1
+%% @see c_fname/2
+
+fname_id(#var{name={A,_}}) ->
+ A.
+
+
+%% @spec fname_arity(cerl()) -> integer()
+%%
+%% @doc Returns the arity part of an abstract function name variable.
+%%
+%% @see fname_id/1
+%% @see c_fname/2
+
+fname_arity(#var{name={_,N}}) ->
+ N.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_values(Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract value list. If <code>Elements</code> is
+%% <code>[E1, ..., En]</code>, the result represents
+%% "<code>&lt;<em>E1</em>, ..., <em>En</em>&gt;</code>".
+%%
+%% @see ann_c_values/2
+%% @see update_c_values/2
+%% @see is_c_values/1
+%% @see values_es/1
+%% @see values_arity/1
+
+-record(values, {ann = [], es}).
+
+c_values(Es) ->
+ #values{es = Es}.
+
+
+%% @spec ann_c_values(As::[term()], Elements::[cerl()]) -> cerl()
+%% @see c_values/1
+
+ann_c_values(As, Es) ->
+ #values{es = Es, ann = As}.
+
+
+%% @spec update_c_values(Old::cerl(), Elements::[cerl()]) -> cerl()
+%% @see c_values/1
+
+update_c_values(Node, Es) ->
+ #values{es = Es, ann = get_ann(Node)}.
+
+
+%% @spec is_c_values(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% value list; otherwise <code>false</code>.
+%%
+%% @see c_values/1
+
+is_c_values(#values{}) ->
+ true;
+is_c_values(_) ->
+ false.
+
+
+%% @spec values_es(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of element subtrees of an abstract value
+%% list.
+%%
+%% @see c_values/1
+%% @see values_arity/1
+
+values_es(Node) ->
+ Node#values.es.
+
+
+%% @spec values_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of element subtrees of an abstract value
+%% list.
+%%
+%% <p>Note: This is equivalent to
+%% <code>length(values_es(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_values/1
+%% @see values_es/1
+
+values_arity(Node) ->
+ length(values_es(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_binary(Segments::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract binary-template. A binary object is a
+%% sequence of 8-bit bytes. It is specified by zero or more bit-string
+%% template <em>segments</em> of arbitrary lengths (in number of bits),
+%% such that the sum of the lengths is evenly divisible by 8. If
+%% <code>Segments</code> is <code>[S1, ..., Sn]</code>, the result
+%% represents "<code>#{<em>S1</em>, ..., <em>Sn</em>}#</code>". All the
+%% <code>Si</code> must have type <code>bitstr</code>.
+%%
+%% @see ann_c_binary/2
+%% @see update_c_binary/2
+%% @see is_c_binary/1
+%% @see binary_segments/1
+%% @see c_bitstr/5
+
+-record(binary, {ann = [], segments}).
+
+c_binary(Segments) ->
+ #binary{segments = Segments}.
+
+
+%% @spec ann_c_binary(As::[term()], Segments::[cerl()]) -> cerl()
+%% @see c_binary/1
+
+ann_c_binary(As, Segments) ->
+ #binary{segments = Segments, ann = As}.
+
+
+%% @spec update_c_binary(Old::cerl(), Segments::[cerl()]) -> cerl()
+%% @see c_binary/1
+
+update_c_binary(Node, Segments) ->
+ #binary{segments = Segments, ann = get_ann(Node)}.
+
+
+%% @spec is_c_binary(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% binary-template; otherwise <code>false</code>.
+%%
+%% @see c_binary/1
+
+is_c_binary(#binary{}) ->
+ true;
+is_c_binary(_) ->
+ false.
+
+
+%% @spec binary_segments(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of segment subtrees of an abstract
+%% binary-template.
+%%
+%% @see c_binary/1
+%% @see c_bitstr/5
+
+binary_segments(Node) ->
+ Node#binary.segments.
+
+
+%% @spec c_bitstr(Value::cerl(), Size::cerl(), Unit::cerl(),
+%% Type::cerl(), Flags::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract bit-string template. These can only occur as
+%% components of an abstract binary-template (see {@link c_binary/1}).
+%% The result represents "<code>#&lt;<em>Value</em>&gt;(<em>Size</em>,
+%% <em>Unit</em>, <em>Type</em>, <em>Flags</em>)</code>", where
+%% <code>Unit</code> must represent a positive integer constant,
+%% <code>Type</code> must represent a constant atom (one of
+%% <code>'integer'</code>, <code>'float'</code>, or
+%% <code>'binary'</code>), and <code>Flags</code> must represent a
+%% constant list <code>"[<em>F1</em>, ..., <em>Fn</em>]"</code> where
+%% all the <code>Fi</code> are atoms.
+%%
+%% @see c_binary/1
+%% @see ann_c_bitstr/6
+%% @see update_c_bitstr/6
+%% @see is_c_bitstr/1
+%% @see bitstr_val/1
+%% @see bitstr_size/1
+%% @see bitstr_unit/1
+%% @see bitstr_type/1
+%% @see bitstr_flags/1
+
+-record(bitstr, {ann = [], val, size, unit, type, flags}).
+
+c_bitstr(Val, Size, Unit, Type, Flags) ->
+ #bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags}.
+
+
+%% @spec c_bitstr(Value::cerl(), Size::cerl(), Type::cerl(),
+%% Flags::cerl()) -> cerl()
+%% @equiv c_bitstr(Value, Size, abstract(1), Type, Flags)
+
+c_bitstr(Val, Size, Type, Flags) ->
+ c_bitstr(Val, Size, abstract(1), Type, Flags).
+
+
+%% @spec c_bitstr(Value::cerl(), Type::cerl(),
+%% Flags::cerl()) -> cerl()
+%% @equiv c_bitstr(Value, abstract(all), abstract(1), Type, Flags)
+
+c_bitstr(Val, Type, Flags) ->
+ c_bitstr(Val, abstract(all), abstract(1), Type, Flags).
+
+
+%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
+%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
+%% @see c_bitstr/5
+%% @see ann_c_bitstr/5
+
+ann_c_bitstr(As, Val, Size, Unit, Type, Flags) ->
+ #bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags, ann = As}.
+
+%% @spec ann_c_bitstr(As::[term()], Value::cerl(), Size::cerl(),
+%% Type::cerl(), Flags::cerl()) -> cerl()
+%% @equiv ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags)
+
+ann_c_bitstr(As, Value, Size, Type, Flags) ->
+ ann_c_bitstr(As, Value, Size, abstract(1), Type, Flags).
+
+
+%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
+%% Unit::cerl(), Type::cerl(), Flags::cerl()) -> cerl()
+%% @see c_bitstr/5
+%% @see update_c_bitstr/5
+
+update_c_bitstr(Node, Val, Size, Unit, Type, Flags) ->
+ #bitstr{val = Val, size = Size, unit = Unit, type = Type,
+ flags = Flags, ann = get_ann(Node)}.
+
+
+%% @spec update_c_bitstr(Old::cerl(), Value::cerl(), Size::cerl(),
+%% Type::cerl(), Flags::cerl()) -> cerl()
+%% @equiv update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags)
+
+update_c_bitstr(Node, Value, Size, Type, Flags) ->
+ update_c_bitstr(Node, Value, Size, abstract(1), Type, Flags).
+
+%% @spec is_c_bitstr(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% bit-string template; otherwise <code>false</code>.
+%%
+%% @see c_bitstr/5
+
+is_c_bitstr(#bitstr{}) ->
+ true;
+is_c_bitstr(_) ->
+ false.
+
+
+%% @spec bitstr_val(cerl()) -> cerl()
+%%
+%% @doc Returns the value subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_val(Node) ->
+ Node#bitstr.val.
+
+
+%% @spec bitstr_size(cerl()) -> cerl()
+%%
+%% @doc Returns the size subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_size(Node) ->
+ Node#bitstr.size.
+
+
+%% @spec bitstr_bitsize(cerl()) -> integer() | any | all
+%%
+%% @doc Returns the total size in bits of an abstract bit-string
+%% template. If the size field is an integer literal, the result is the
+%% product of the size and unit values; if the size field is the atom
+%% literal <code>all</code>, the atom <code>all</code> is returned; in
+%% all other cases, the atom <code>any</code> is returned.
+%%
+%% @see c_bitstr/5
+
+bitstr_bitsize(Node) ->
+ Size = Node#bitstr.size,
+ case is_literal(Size) of
+ true ->
+ case concrete(Size) of
+ all ->
+ all;
+ S when integer(S) ->
+ S*concrete(Node#bitstr.unit);
+ true ->
+ any
+ end;
+ false ->
+ any
+ end.
+
+
+%% @spec bitstr_unit(cerl()) -> cerl()
+%%
+%% @doc Returns the unit subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_unit(Node) ->
+ Node#bitstr.unit.
+
+
+%% @spec bitstr_type(cerl()) -> cerl()
+%%
+%% @doc Returns the type subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_type(Node) ->
+ Node#bitstr.type.
+
+
+%% @spec bitstr_flags(cerl()) -> cerl()
+%%
+%% @doc Returns the flags subtree of an abstract bit-string template.
+%%
+%% @see c_bitstr/5
+
+bitstr_flags(Node) ->
+ Node#bitstr.flags.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_fun(Variables::[cerl()], Body::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract fun-expression. If <code>Variables</code>
+%% is <code>[V1, ..., Vn]</code>, the result represents "<code>fun
+%% (<em>V1</em>, ..., <em>Vn</em>) -> <em>Body</em></code>". All the
+%% <code>Vi</code> must have type <code>var</code>.
+%%
+%% @see ann_c_fun/3
+%% @see update_c_fun/3
+%% @see is_c_fun/1
+%% @see fun_vars/1
+%% @see fun_body/1
+%% @see fun_arity/1
+
+-record('fun', {ann = [], vars, body}).
+
+c_fun(Variables, Body) ->
+ #'fun'{vars = Variables, body = Body}.
+
+
+%% @spec ann_c_fun(As::[term()], Variables::[cerl()], Body::cerl()) ->
+%% cerl()
+%% @see c_fun/2
+
+ann_c_fun(As, Variables, Body) ->
+ #'fun'{vars = Variables, body = Body, ann = As}.
+
+
+%% @spec update_c_fun(Old::cerl(), Variables::[cerl()],
+%% Body::cerl()) -> cerl()
+%% @see c_fun/2
+
+update_c_fun(Node, Variables, Body) ->
+ #'fun'{vars = Variables, body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_fun(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% fun-expression, otherwise <code>false</code>.
+%%
+%% @see c_fun/2
+
+is_c_fun(#'fun'{}) ->
+ true; % Now this is fun!
+is_c_fun(_) ->
+ false.
+
+
+%% @spec fun_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of parameter subtrees of an abstract
+%% fun-expression.
+%%
+%% @see c_fun/2
+%% @see fun_arity/1
+
+fun_vars(Node) ->
+ Node#'fun'.vars.
+
+
+%% @spec fun_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract fun-expression.
+%%
+%% @see c_fun/2
+
+fun_body(Node) ->
+ Node#'fun'.body.
+
+
+%% @spec fun_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of parameter subtrees of an abstract
+%% fun-expression.
+%%
+%% <p>Note: this is equivalent to <code>length(fun_vars(Node))</code>,
+%% but potentially more efficient.</p>
+%%
+%% @see c_fun/2
+%% @see fun_vars/1
+
+fun_arity(Node) ->
+ length(fun_vars(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_seq(Argument::cerl(), Body::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract sequencing expression. The result
+%% represents "<code>do <em>Argument</em> <em>Body</em></code>".
+%%
+%% @see ann_c_seq/3
+%% @see update_c_seq/3
+%% @see is_c_seq/1
+%% @see seq_arg/1
+%% @see seq_body/1
+
+-record(seq, {ann = [], arg, body}).
+
+c_seq(Argument, Body) ->
+ #seq{arg = Argument, body = Body}.
+
+
+%% @spec ann_c_seq(As::[term()], Argument::cerl(), Body::cerl()) ->
+%% cerl()
+%% @see c_seq/2
+
+ann_c_seq(As, Argument, Body) ->
+ #seq{arg = Argument, body = Body, ann = As}.
+
+
+%% @spec update_c_seq(Old::cerl(), Argument::cerl(), Body::cerl()) ->
+%% cerl()
+%% @see c_seq/2
+
+update_c_seq(Node, Argument, Body) ->
+ #seq{arg = Argument, body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_seq(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% sequencing expression, otherwise <code>false</code>.
+%%
+%% @see c_seq/2
+
+is_c_seq(#seq{}) ->
+ true;
+is_c_seq(_) ->
+ false.
+
+
+%% @spec seq_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract sequencing
+%% expression.
+%%
+%% @see c_seq/2
+
+seq_arg(Node) ->
+ Node#seq.arg.
+
+
+%% @spec seq_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract sequencing expression.
+%%
+%% @see c_seq/2
+
+seq_body(Node) ->
+ Node#seq.body.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_let(Variables::[cerl()], Argument::cerl(), Body::cerl()) ->
+%% cerl()
+%%
+%% @doc Creates an abstract let-expression. If <code>Variables</code>
+%% is <code>[V1, ..., Vn]</code>, the result represents "<code>let
+%% &lt;<em>V1</em>, ..., <em>Vn</em>&gt; = <em>Argument</em> in
+%% <em>Body</em></code>". All the <code>Vi</code> must have type
+%% <code>var</code>.
+%%
+%% @see ann_c_let/4
+%% @see update_c_let/4
+%% @see is_c_let/1
+%% @see let_vars/1
+%% @see let_arg/1
+%% @see let_body/1
+%% @see let_arity/1
+
+-record('let', {ann = [], vars, arg, body}).
+
+c_let(Variables, Argument, Body) ->
+ #'let'{vars = Variables, arg = Argument, body = Body}.
+
+
+%% ann_c_let(As, Variables, Argument, Body) -> Node
+%% @see c_let/3
+
+ann_c_let(As, Variables, Argument, Body) ->
+ #'let'{vars = Variables, arg = Argument, body = Body, ann = As}.
+
+
+%% update_c_let(Old, Variables, Argument, Body) -> Node
+%% @see c_let/3
+
+update_c_let(Node, Variables, Argument, Body) ->
+ #'let'{vars = Variables, arg = Argument, body = Body,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_let(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% let-expression, otherwise <code>false</code>.
+%%
+%% @see c_let/3
+
+is_c_let(#'let'{}) ->
+ true;
+is_c_let(_) ->
+ false.
+
+
+%% @spec let_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side variables of an abstract
+%% let-expression.
+%%
+%% @see c_let/3
+%% @see let_arity/1
+
+let_vars(Node) ->
+ Node#'let'.vars.
+
+
+%% @spec let_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract let-expression.
+%%
+%% @see c_let/3
+
+let_arg(Node) ->
+ Node#'let'.arg.
+
+
+%% @spec let_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract let-expression.
+%%
+%% @see c_let/3
+
+let_body(Node) ->
+ Node#'let'.body.
+
+
+%% @spec let_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of left-hand side variables of an abstract
+%% let-expression.
+%%
+%% <p>Note: this is equivalent to <code>length(let_vars(Node))</code>,
+%% but potentially more efficient.</p>
+%%
+%% @see c_let/3
+%% @see let_vars/1
+
+let_arity(Node) ->
+ length(let_vars(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_letrec(Definitions::[{cerl(), cerl()}], Body::cerl()) ->
+%% cerl()
+%%
+%% @doc Creates an abstract letrec-expression. If
+%% <code>Definitions</code> is <code>[{V1, F1}, ..., {Vn, Fn}]</code>,
+%% the result represents "<code>letrec <em>V1</em> = <em>F1</em>
+%% ... <em>Vn</em> = <em>Fn</em> in <em>Body</em></code>. All the
+%% <code>Vi</code> must have type <code>var</code> and represent
+%% function names. All the <code>Fi</code> must have type
+%% <code>'fun'</code>.
+%%
+%% @see ann_c_letrec/3
+%% @see update_c_letrec/3
+%% @see is_c_letrec/1
+%% @see letrec_defs/1
+%% @see letrec_body/1
+%% @see letrec_vars/1
+
+-record(letrec, {ann = [], defs, body}).
+
+c_letrec(Defs, Body) ->
+ #letrec{defs = Defs, body = Body}.
+
+
+%% @spec ann_c_letrec(As::[term()], Definitions::[{cerl(), cerl()}],
+%% Body::cerl()) -> cerl()
+%% @see c_letrec/2
+
+ann_c_letrec(As, Defs, Body) ->
+ #letrec{defs = Defs, body = Body, ann = As}.
+
+
+%% @spec update_c_letrec(Old::cerl(),
+%% Definitions::[{cerl(), cerl()}],
+%% Body::cerl()) -> cerl()
+%% @see c_letrec/2
+
+update_c_letrec(Node, Defs, Body) ->
+ #letrec{defs = Defs, body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_letrec(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% letrec-expression, otherwise <code>false</code>.
+%%
+%% @see c_letrec/2
+
+is_c_letrec(#letrec{}) ->
+ true;
+is_c_letrec(_) ->
+ false.
+
+
+%% @spec letrec_defs(Node::cerl()) -> [{cerl(), cerl()}]
+%%
+%% @doc Returns the list of definitions of an abstract
+%% letrec-expression. If <code>Node</code> represents "<code>letrec
+%% <em>V1</em> = <em>F1</em> ... <em>Vn</em> = <em>Fn</em> in
+%% <em>Body</em></code>", the returned value is <code>[{V1, F1}, ...,
+%% {Vn, Fn}]</code>.
+%%
+%% @see c_letrec/2
+
+letrec_defs(Node) ->
+ Node#letrec.defs.
+
+
+%% @spec letrec_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract letrec-expression.
+%%
+%% @see c_letrec/2
+
+letrec_body(Node) ->
+ Node#letrec.body.
+
+
+%% @spec letrec_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of left-hand side function variable subtrees
+%% of a letrec-expression. If <code>Node</code> represents
+%% "<code>letrec <em>V1</em> = <em>F1</em> ... <em>Vn</em> =
+%% <em>Fn</em> in <em>Body</em></code>", the returned value is
+%% <code>[V1, ..., Vn]</code>.
+%%
+%% @see c_letrec/2
+
+letrec_vars(Node) ->
+ [F || {F, _} <- letrec_defs(Node)].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_case(Argument::cerl(), Clauses::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract case-expression. If <code>Clauses</code>
+%% is <code>[C1, ..., Cn]</code>, the result represents "<code>case
+%% <em>Argument</em> of <em>C1</em> ... <em>Cn</em>
+%% end</code>". <code>Clauses</code> must not be empty.
+%%
+%% @see ann_c_case/3
+%% @see update_c_case/3
+%% @see is_c_case/1
+%% @see c_clause/3
+%% @see case_arg/1
+%% @see case_clauses/1
+%% @see case_arity/1
+
+-record('case', {ann = [], arg, clauses}).
+
+c_case(Expr, Clauses) ->
+ #'case'{arg = Expr, clauses = Clauses}.
+
+
+%% @spec ann_c_case(As::[term()], Argument::cerl(),
+%% Clauses::[cerl()]) -> cerl()
+%% @see c_case/2
+
+ann_c_case(As, Expr, Clauses) ->
+ #'case'{arg = Expr, clauses = Clauses, ann = As}.
+
+
+%% @spec update_c_case(Old::cerl(), Argument::cerl(),
+%% Clauses::[cerl()]) -> cerl()
+%% @see c_case/2
+
+update_c_case(Node, Expr, Clauses) ->
+ #'case'{arg = Expr, clauses = Clauses, ann = get_ann(Node)}.
+
+
+%% is_c_case(Node) -> boolean()
+%%
+%% Node = cerl()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% case-expression; otherwise <code>false</code>.
+%%
+%% @see c_case/2
+
+is_c_case(#'case'{}) ->
+ true;
+is_c_case(_) ->
+ false.
+
+
+%% @spec case_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the argument subtree of an abstract case-expression.
+%%
+%% @see c_case/2
+
+case_arg(Node) ->
+ Node#'case'.arg.
+
+
+%% @spec case_clauses(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of clause subtrees of an abstract
+%% case-expression.
+%%
+%% @see c_case/2
+%% @see case_arity/1
+
+case_clauses(Node) ->
+ Node#'case'.clauses.
+
+
+%% @spec case_arity(Node::cerl()) -> integer()
+%%
+%% @doc Equivalent to
+%% <code>clause_arity(hd(case_clauses(Node)))</code>, but potentially
+%% more efficient.
+%%
+%% @see c_case/2
+%% @see case_clauses/1
+%% @see clause_arity/1
+
+case_arity(Node) ->
+ clause_arity(hd(case_clauses(Node))).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_clause(Patterns::[cerl()], Body::cerl()) -> cerl()
+%% @equiv c_clause(Patterns, c_atom(true), Body)
+%% @see c_atom/1
+
+c_clause(Patterns, Body) ->
+ c_clause(Patterns, c_atom(true), Body).
+
+
+%% @spec c_clause(Patterns::[cerl()], Guard::cerl(), Body::cerl()) ->
+%% cerl()
+%%
+%% @doc Creates an an abstract clause. If <code>Patterns</code> is
+%% <code>[P1, ..., Pn]</code>, the result represents
+%% "<code>&lt;<em>P1</em>, ..., <em>Pn</em>&gt; when <em>Guard</em> ->
+%% <em>Body</em></code>".
+%%
+%% @see c_clause/2
+%% @see ann_c_clause/4
+%% @see update_c_clause/4
+%% @see is_c_clause/1
+%% @see c_case/2
+%% @see c_receive/3
+%% @see clause_pats/1
+%% @see clause_guard/1
+%% @see clause_body/1
+%% @see clause_arity/1
+%% @see clause_vars/1
+
+-record(clause, {ann = [], pats, guard, body}).
+
+c_clause(Patterns, Guard, Body) ->
+ #clause{pats = Patterns, guard = Guard, body = Body}.
+
+
+%% @spec ann_c_clause(As::[term()], Patterns::[cerl()],
+%% Body::cerl()) -> cerl()
+%% @equiv ann_c_clause(As, Patterns, c_atom(true), Body)
+%% @see c_clause/3
+ann_c_clause(As, Patterns, Body) ->
+ ann_c_clause(As, Patterns, c_atom(true), Body).
+
+
+%% @spec ann_c_clause(As::[term()], Patterns::[cerl()], Guard::cerl(),
+%% Body::cerl()) -> cerl()
+%% @see ann_c_clause/3
+%% @see c_clause/3
+
+ann_c_clause(As, Patterns, Guard, Body) ->
+ #clause{pats = Patterns, guard = Guard, body = Body, ann = As}.
+
+
+%% @spec update_c_clause(Old::cerl(), Patterns::[cerl()],
+%% Guard::cerl(), Body::cerl()) -> cerl()
+%% @see c_clause/3
+
+update_c_clause(Node, Patterns, Guard, Body) ->
+ #clause{pats = Patterns, guard = Guard, body = Body,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_clause(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% clause, otherwise <code>false</code>.
+%%
+%% @see c_clause/3
+
+is_c_clause(#clause{}) ->
+ true;
+is_c_clause(_) ->
+ false.
+
+
+%% @spec clause_pats(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of pattern subtrees of an abstract clause.
+%%
+%% @see c_clause/3
+%% @see clause_arity/1
+
+clause_pats(Node) ->
+ Node#clause.pats.
+
+
+%% @spec clause_guard(cerl()) -> cerl()
+%%
+%% @doc Returns the guard subtree of an abstract clause.
+%%
+%% @see c_clause/3
+
+clause_guard(Node) ->
+ Node#clause.guard.
+
+
+%% @spec clause_body(cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract clause.
+%%
+%% @see c_clause/3
+
+clause_body(Node) ->
+ Node#clause.body.
+
+
+%% @spec clause_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of pattern subtrees of an abstract clause.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(clause_pats(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_clause/3
+%% @see clause_pats/1
+
+clause_arity(Node) ->
+ length(clause_pats(Node)).
+
+
+%% @spec clause_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in the patterns of
+%% an abstract clause. The order of listing is not defined.
+%%
+%% @see c_clause/3
+%% @see pat_list_vars/1
+
+clause_vars(Clause) ->
+ pat_list_vars(clause_pats(Clause)).
+
+
+%% @spec pat_vars(Pattern::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in a pattern. An
+%% exception is thrown if <code>Node</code> does not represent a
+%% well-formed Core Erlang clause pattern. The order of listing is not
+%% defined.
+%%
+%% @see pat_list_vars/1
+%% @see clause_vars/1
+
+pat_vars(Node) ->
+ pat_vars(Node, []).
+
+pat_vars(Node, Vs) ->
+ case type(Node) of
+ var ->
+ [Node | Vs];
+ literal ->
+ Vs;
+ cons ->
+ pat_vars(cons_hd(Node), pat_vars(cons_tl(Node), Vs));
+ tuple ->
+ pat_list_vars(tuple_es(Node), Vs);
+ binary ->
+ pat_list_vars(binary_segments(Node), Vs);
+ bitstr ->
+ pat_vars(bitstr_val(Node), Vs);
+ alias ->
+ pat_vars(alias_pat(Node), [alias_var(Node) | Vs])
+ end.
+
+
+%% @spec pat_list_vars(Patterns::[cerl()]) -> [cerl()]
+%%
+%% @doc Returns the list of all abstract variables in the given
+%% patterns. An exception is thrown if some element in
+%% <code>Patterns</code> does not represent a well-formed Core Erlang
+%% clause pattern. The order of listing is not defined.
+%%
+%% @see pat_vars/1
+%% @see clause_vars/1
+
+pat_list_vars(Ps) ->
+ pat_list_vars(Ps, []).
+
+pat_list_vars([P | Ps], Vs) ->
+ pat_list_vars(Ps, pat_vars(P, Vs));
+pat_list_vars([], Vs) ->
+ Vs.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_alias(Variable::cerl(), Pattern::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract pattern alias. The result represents
+%% "<code><em>Variable</em> = <em>Pattern</em></code>".
+%%
+%% @see ann_c_alias/3
+%% @see update_c_alias/3
+%% @see is_c_alias/1
+%% @see alias_var/1
+%% @see alias_pat/1
+%% @see c_clause/3
+
+-record(alias, {ann = [], var, pat}).
+
+c_alias(Var, Pattern) ->
+ #alias{var = Var, pat = Pattern}.
+
+
+%% @spec ann_c_alias(As::[term()], Variable::cerl(),
+%% Pattern::cerl()) -> cerl()
+%% @see c_alias/2
+
+ann_c_alias(As, Var, Pattern) ->
+ #alias{var = Var, pat = Pattern, ann = As}.
+
+
+%% @spec update_c_alias(Old::cerl(), Variable::cerl(),
+%% Pattern::cerl()) -> cerl()
+%% @see c_alias/2
+
+update_c_alias(Node, Var, Pattern) ->
+ #alias{var = Var, pat = Pattern, ann = get_ann(Node)}.
+
+
+%% @spec is_c_alias(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% pattern alias, otherwise <code>false</code>.
+%%
+%% @see c_alias/2
+
+is_c_alias(#alias{}) ->
+ true;
+is_c_alias(_) ->
+ false.
+
+
+%% @spec alias_var(cerl()) -> cerl()
+%%
+%% @doc Returns the variable subtree of an abstract pattern alias.
+%%
+%% @see c_alias/2
+
+alias_var(Node) ->
+ Node#alias.var.
+
+
+%% @spec alias_pat(cerl()) -> cerl()
+%%
+%% @doc Returns the pattern subtree of an abstract pattern alias.
+%%
+%% @see c_alias/2
+
+alias_pat(Node) ->
+ Node#alias.pat.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_receive(Clauses::[cerl()]) -> cerl()
+%% @equiv c_receive(Clauses, c_atom(infinity), c_atom(true))
+%% @see c_atom/1
+
+c_receive(Clauses) ->
+ c_receive(Clauses, c_atom(infinity), c_atom(true)).
+
+
+%% @spec c_receive(Clauses::[cerl()], Timeout::cerl(),
+%% Action::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract receive-expression. If
+%% <code>Clauses</code> is <code>[C1, ..., Cn]</code>, the result
+%% represents "<code>receive <em>C1</em> ... <em>Cn</em> after
+%% <em>Timeout</em> -> <em>Action</em> end</code>".
+%%
+%% @see c_receive/1
+%% @see ann_c_receive/4
+%% @see update_c_receive/4
+%% @see is_c_receive/1
+%% @see receive_clauses/1
+%% @see receive_timeout/1
+%% @see receive_action/1
+
+-record('receive', {ann = [], clauses, timeout, action}).
+
+c_receive(Clauses, Timeout, Action) ->
+ #'receive'{clauses = Clauses, timeout = Timeout, action = Action}.
+
+
+%% @spec ann_c_receive(As::[term()], Clauses::[cerl()]) -> cerl()
+%% @equiv ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true))
+%% @see c_receive/3
+%% @see c_atom/1
+
+ann_c_receive(As, Clauses) ->
+ ann_c_receive(As, Clauses, c_atom(infinity), c_atom(true)).
+
+
+%% @spec ann_c_receive(As::[term()], Clauses::[cerl()],
+%% Timeout::cerl(), Action::cerl()) -> cerl()
+%% @see ann_c_receive/2
+%% @see c_receive/3
+
+ann_c_receive(As, Clauses, Timeout, Action) ->
+ #'receive'{clauses = Clauses, timeout = Timeout, action = Action,
+ ann = As}.
+
+
+%% @spec update_c_receive(Old::cerl(), Clauses::[cerl()],
+%% Timeout::cerl(), Action::cerl()) -> cerl()
+%% @see c_receive/3
+
+update_c_receive(Node, Clauses, Timeout, Action) ->
+ #'receive'{clauses = Clauses, timeout = Timeout, action = Action,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_receive(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% receive-expression, otherwise <code>false</code>.
+%%
+%% @see c_receive/3
+
+is_c_receive(#'receive'{}) ->
+ true;
+is_c_receive(_) ->
+ false.
+
+
+%% @spec receive_clauses(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of clause subtrees of an abstract
+%% receive-expression.
+%%
+%% @see c_receive/3
+
+receive_clauses(Node) ->
+ Node#'receive'.clauses.
+
+
+%% @spec receive_timeout(cerl()) -> cerl()
+%%
+%% @doc Returns the timeout subtree of an abstract receive-expression.
+%%
+%% @see c_receive/3
+
+receive_timeout(Node) ->
+ Node#'receive'.timeout.
+
+
+%% @spec receive_action(cerl()) -> cerl()
+%%
+%% @doc Returns the action subtree of an abstract receive-expression.
+%%
+%% @see c_receive/3
+
+receive_action(Node) ->
+ Node#'receive'.action.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_apply(Operator::cerl(), Arguments::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract function application. If
+%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% represents "<code>apply <em>Operator</em>(<em>A1</em>, ...,
+%% <em>An</em>)</code>".
+%%
+%% @see ann_c_apply/3
+%% @see update_c_apply/3
+%% @see is_c_apply/1
+%% @see apply_op/1
+%% @see apply_args/1
+%% @see apply_arity/1
+%% @see c_call/3
+%% @see c_primop/2
+
+-record(apply, {ann = [], op, args}).
+
+c_apply(Operator, Arguments) ->
+ #apply{op = Operator, args = Arguments}.
+
+
+%% @spec ann_c_apply(As::[term()], Operator::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_apply/2
+
+ann_c_apply(As, Operator, Arguments) ->
+ #apply{op = Operator, args = Arguments, ann = As}.
+
+
+%% @spec update_c_apply(Old::cerl(), Operator::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_apply/2
+
+update_c_apply(Node, Operator, Arguments) ->
+ #apply{op = Operator, args = Arguments, ann = get_ann(Node)}.
+
+
+%% @spec is_c_apply(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% function application, otherwise <code>false</code>.
+%%
+%% @see c_apply/2
+
+is_c_apply(#apply{}) ->
+ true;
+is_c_apply(_) ->
+ false.
+
+
+%% @spec apply_op(cerl()) -> cerl()
+%%
+%% @doc Returns the operator subtree of an abstract function
+%% application.
+%%
+%% @see c_apply/2
+
+apply_op(Node) ->
+ Node#apply.op.
+
+
+%% @spec apply_args(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract function
+%% application.
+%%
+%% @see c_apply/2
+%% @see apply_arity/1
+
+apply_args(Node) ->
+ Node#apply.args.
+
+
+%% @spec apply_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% function application.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(apply_args(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_apply/2
+%% @see apply_args/1
+
+apply_arity(Node) ->
+ length(apply_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_call(Module::cerl(), Name::cerl(), Arguments::[cerl()]) ->
+%% cerl()
+%%
+%% @doc Creates an abstract inter-module call. If
+%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% represents "<code>call <em>Module</em>:<em>Name</em>(<em>A1</em>,
+%% ..., <em>An</em>)</code>".
+%%
+%% @see ann_c_call/4
+%% @see update_c_call/4
+%% @see is_c_call/1
+%% @see call_module/1
+%% @see call_name/1
+%% @see call_args/1
+%% @see call_arity/1
+%% @see c_apply/2
+%% @see c_primop/2
+
+-record(call, {ann = [], module, name, args}).
+
+c_call(Module, Name, Arguments) ->
+ #call{module = Module, name = Name, args = Arguments}.
+
+
+%% @spec ann_c_call(As::[term()], Module::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_call/3
+
+ann_c_call(As, Module, Name, Arguments) ->
+ #call{module = Module, name = Name, args = Arguments, ann = As}.
+
+
+%% @spec update_c_call(Old::cerl(), Module::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_call/3
+
+update_c_call(Node, Module, Name, Arguments) ->
+ #call{module = Module, name = Name, args = Arguments,
+ ann = get_ann(Node)}.
+
+
+%% @spec is_c_call(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% inter-module call expression; otherwise <code>false</code>.
+%%
+%% @see c_call/3
+
+is_c_call(#call{}) ->
+ true;
+is_c_call(_) ->
+ false.
+
+
+%% @spec call_module(cerl()) -> cerl()
+%%
+%% @doc Returns the module subtree of an abstract inter-module call.
+%%
+%% @see c_call/3
+
+call_module(Node) ->
+ Node#call.module.
+
+
+%% @spec call_name(cerl()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract inter-module call.
+%%
+%% @see c_call/3
+
+call_name(Node) ->
+ Node#call.name.
+
+
+%% @spec call_args(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract
+%% inter-module call.
+%%
+%% @see c_call/3
+%% @see call_arity/1
+
+call_args(Node) ->
+ Node#call.args.
+
+
+%% @spec call_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% inter-module call.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(call_args(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_call/3
+%% @see call_args/1
+
+call_arity(Node) ->
+ length(call_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_primop(Name::cerl(), Arguments::[cerl()]) -> cerl()
+%%
+%% @doc Creates an abstract primitive operation call. If
+%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result
+%% represents "<code>primop <em>Name</em>(<em>A1</em>, ...,
+%% <em>An</em>)</code>". <code>Name</code> must be an atom literal.
+%%
+%% @see ann_c_primop/3
+%% @see update_c_primop/3
+%% @see is_c_primop/1
+%% @see primop_name/1
+%% @see primop_args/1
+%% @see primop_arity/1
+%% @see c_apply/2
+%% @see c_call/3
+
+-record(primop, {ann = [], name, args}).
+
+c_primop(Name, Arguments) ->
+ #primop{name = Name, args = Arguments}.
+
+
+%% @spec ann_c_primop(As::[term()], Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_primop/2
+
+ann_c_primop(As, Name, Arguments) ->
+ #primop{name = Name, args = Arguments, ann = As}.
+
+
+%% @spec update_c_primop(Old::cerl(), Name::cerl(),
+%% Arguments::[cerl()]) -> cerl()
+%% @see c_primop/2
+
+update_c_primop(Node, Name, Arguments) ->
+ #primop{name = Name, args = Arguments, ann = get_ann(Node)}.
+
+
+%% @spec is_c_primop(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% primitive operation call, otherwise <code>false</code>.
+%%
+%% @see c_primop/2
+
+is_c_primop(#primop{}) ->
+ true;
+is_c_primop(_) ->
+ false.
+
+
+%% @spec primop_name(cerl()) -> cerl()
+%%
+%% @doc Returns the name subtree of an abstract primitive operation
+%% call.
+%%
+%% @see c_primop/2
+
+primop_name(Node) ->
+ Node#primop.name.
+
+
+%% @spec primop_args(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of argument subtrees of an abstract primitive
+%% operation call.
+%%
+%% @see c_primop/2
+%% @see primop_arity/1
+
+primop_args(Node) ->
+ Node#primop.args.
+
+
+%% @spec primop_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of argument subtrees of an abstract
+%% primitive operation call.
+%%
+%% <p>Note: this is equivalent to
+%% <code>length(primop_args(Node))</code>, but potentially more
+%% efficient.</p>
+%%
+%% @see c_primop/2
+%% @see primop_args/1
+
+primop_arity(Node) ->
+ length(primop_args(Node)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_try(Argument::cerl(), Variables::[cerl()], Body::cerl(),
+%% ExceptionVars::[cerl()], Handler::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract try-expression. If <code>Variables</code> is
+%% <code>[V1, ..., Vn]</code> and <code>ExceptionVars</code> is
+%% <code>[X1, ..., Xm]</code>, the result represents "<code>try
+%% <em>Argument</em> of &lt;<em>V1</em>, ..., <em>Vn</em>&gt; ->
+%% <em>Body</em> catch &lt;<em>X1</em>, ..., <em>Xm</em>&gt; ->
+%% <em>Handler</em></code>". All the <code>Vi</code> and <code>Xi</code>
+%% must have type <code>var</code>.
+%%
+%% @see ann_c_try/6
+%% @see update_c_try/6
+%% @see is_c_try/1
+%% @see try_arg/1
+%% @see try_vars/1
+%% @see try_body/1
+%% @see c_catch/1
+
+-record('try', {ann = [], arg, vars, body, evars, handler}).
+
+c_try(Expr, Vs, Body, Evs, Handler) ->
+ #'try'{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler}.
+
+
+%% @spec ann_c_try(As::[term()], Expression::cerl(),
+%% Variables::[cerl()], Body::cerl(),
+%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
+%% @see c_try/3
+
+ann_c_try(As, Expr, Vs, Body, Evs, Handler) ->
+ #'try'{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler, ann = As}.
+
+
+%% @spec update_c_try(Old::cerl(), Expression::cerl(),
+%% Variables::[cerl()], Body::cerl(),
+%% EVars::[cerl()], EBody::[cerl()]) -> cerl()
+%% @see c_try/3
+
+update_c_try(Node, Expr, Vs, Body, Evs, Handler) ->
+ #'try'{arg = Expr, vars = Vs, body = Body,
+ evars = Evs, handler = Handler, ann = get_ann(Node)}.
+
+
+%% @spec is_c_try(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% try-expression, otherwise <code>false</code>.
+%%
+%% @see c_try/3
+
+is_c_try(#'try'{}) ->
+ true;
+is_c_try(_) ->
+ false.
+
+
+%% @spec try_arg(cerl()) -> cerl()
+%%
+%% @doc Returns the expression subtree of an abstract try-expression.
+%%
+%% @see c_try/3
+
+try_arg(Node) ->
+ Node#'try'.arg.
+
+
+%% @spec try_vars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of success variable subtrees of an abstract
+%% try-expression.
+%%
+%% @see c_try/3
+
+try_vars(Node) ->
+ Node#'try'.vars.
+
+
+%% @spec try_body(cerl()) -> cerl()
+%%
+%% @doc Returns the success body subtree of an abstract try-expression.
+%%
+%% @see c_try/3
+
+try_body(Node) ->
+ Node#'try'.body.
+
+
+%% @spec try_evars(cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of exception variable subtrees of an abstract
+%% try-expression.
+%%
+%% @see c_try/3
+
+try_evars(Node) ->
+ Node#'try'.evars.
+
+
+%% @spec try_handler(cerl()) -> cerl()
+%%
+%% @doc Returns the exception body subtree of an abstract
+%% try-expression.
+%%
+%% @see c_try/3
+
+try_handler(Node) ->
+ Node#'try'.handler.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec c_catch(Body::cerl()) -> cerl()
+%%
+%% @doc Creates an abstract catch-expression. The result represents
+%% "<code>catch <em>Body</em></code>".
+%%
+%% <p>Note: catch-expressions can be rewritten as try-expressions, and
+%% will eventually be removed from Core Erlang.</p>
+%%
+%% @see ann_c_catch/2
+%% @see update_c_catch/2
+%% @see is_c_catch/1
+%% @see catch_body/1
+%% @see c_try/3
+
+-record('catch', {ann = [], body}).
+
+c_catch(Body) ->
+ #'catch'{body = Body}.
+
+
+%% @spec ann_c_catch(As::[term()], Body::cerl()) -> cerl()
+%% @see c_catch/1
+
+ann_c_catch(As, Body) ->
+ #'catch'{body = Body, ann = As}.
+
+
+%% @spec update_c_catch(Old::cerl(), Body::cerl()) -> cerl()
+%% @see c_catch/1
+
+update_c_catch(Node, Body) ->
+ #'catch'{body = Body, ann = get_ann(Node)}.
+
+
+%% @spec is_c_catch(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> is an abstract
+%% catch-expression, otherwise <code>false</code>.
+%%
+%% @see c_catch/1
+
+is_c_catch(#'catch'{}) ->
+ true;
+is_c_catch(_) ->
+ false.
+
+
+%% @spec catch_body(Node::cerl()) -> cerl()
+%%
+%% @doc Returns the body subtree of an abstract catch-expression.
+%%
+%% @see c_catch/1
+
+catch_body(Node) ->
+ Node#'catch'.body.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec to_records(Tree::cerl()) -> record(record_types())
+%%
+%% @doc Translates an abstract syntax tree to a corresponding explicit
+%% record representation. The records are defined in the file
+%% "<code>cerl.hrl</code>".
+%%
+%% <p>Note: Compound constant literals are always unfolded in the
+%% record representation.</p>
+%%
+%% @see type/1
+%% @see from_records/1
+
+to_records(Node) ->
+ A = get_ann(Node),
+ case type(Node) of
+ literal ->
+ lit_to_records(concrete(Node), A);
+ binary ->
+ #c_binary{anno = A,
+ segments =
+ list_to_records(binary_segments(Node))};
+ bitstr ->
+ #c_bitstr{anno = A,
+ val = to_records(bitstr_val(Node)),
+ size = to_records(bitstr_size(Node)),
+ unit = to_records(bitstr_unit(Node)),
+ type = to_records(bitstr_type(Node)),
+ flags = to_records(bitstr_flags(Node))};
+ cons ->
+ #c_cons{anno = A,
+ hd = to_records(cons_hd(Node)),
+ tl = to_records(cons_tl(Node))};
+ tuple ->
+ #c_tuple{anno = A,
+ es = list_to_records(tuple_es(Node))};
+ var ->
+ case is_c_fname(Node) of
+ true ->
+ #c_fname{anno = A,
+ id = fname_id(Node),
+ arity = fname_arity(Node)};
+ false ->
+ #c_var{anno = A, name = var_name(Node)}
+ end;
+ values ->
+ #c_values{anno = A,
+ es = list_to_records(values_es(Node))};
+ 'fun' ->
+ #c_fun{anno = A,
+ vars = list_to_records(fun_vars(Node)),
+ body = to_records(fun_body(Node))};
+ seq ->
+ #c_seq{anno = A,
+ arg = to_records(seq_arg(Node)),
+ body = to_records(seq_body(Node))};
+ 'let' ->
+ #c_let{anno = A,
+ vars = list_to_records(let_vars(Node)),
+ arg = to_records(let_arg(Node)),
+ body = to_records(let_body(Node))};
+ letrec ->
+ #c_letrec{anno = A,
+ defs = [#c_def{name = to_records(N),
+ val = to_records(F)}
+ || {N, F} <- letrec_defs(Node)],
+ body = to_records(letrec_body(Node))};
+ 'case' ->
+ #c_case{anno = A,
+ arg = to_records(case_arg(Node)),
+ clauses =
+ list_to_records(case_clauses(Node))};
+ clause ->
+ #c_clause{anno = A,
+ pats = list_to_records(clause_pats(Node)),
+ guard = to_records(clause_guard(Node)),
+ body = to_records(clause_body(Node))};
+ alias ->
+ #c_alias{anno = A,
+ var = to_records(alias_var(Node)),
+ pat = to_records(alias_pat(Node))};
+ 'receive' ->
+ #c_receive{anno = A,
+ clauses =
+ list_to_records(receive_clauses(Node)),
+ timeout =
+ to_records(receive_timeout(Node)),
+ action =
+ to_records(receive_action(Node))};
+ apply ->
+ #c_apply{anno = A,
+ op = to_records(apply_op(Node)),
+ args = list_to_records(apply_args(Node))};
+ call ->
+ #c_call{anno = A,
+ module = to_records(call_module(Node)),
+ name = to_records(call_name(Node)),
+ args = list_to_records(call_args(Node))};
+ primop ->
+ #c_primop{anno = A,
+ name = to_records(primop_name(Node)),
+ args = list_to_records(primop_args(Node))};
+ 'try' ->
+ #c_try{anno = A,
+ arg = to_records(try_arg(Node)),
+ vars = list_to_records(try_vars(Node)),
+ body = to_records(try_body(Node)),
+ evars = list_to_records(try_evars(Node)),
+ handler = to_records(try_handler(Node))};
+ 'catch' ->
+ #c_catch{anno = A,
+ body = to_records(catch_body(Node))};
+ module ->
+ #c_module{anno = A,
+ name = to_records(module_name(Node)),
+ exports = list_to_records(
+ module_exports(Node)),
+ attrs = [#c_def{name = to_records(K),
+ val = to_records(V)}
+ || {K, V} <- module_attrs(Node)],
+ defs = [#c_def{name = to_records(N),
+ val = to_records(F)}
+ || {N, F} <- module_defs(Node)]}
+ end.
+
+list_to_records([T | Ts]) ->
+ [to_records(T) | list_to_records(Ts)];
+list_to_records([]) ->
+ [].
+
+lit_to_records(V, A) when integer(V) ->
+ #c_int{anno = A, val = V};
+lit_to_records(V, A) when float(V) ->
+ #c_float{anno = A, val = V};
+lit_to_records(V, A) when atom(V) ->
+ #c_atom{anno = A, val = V};
+lit_to_records([H | T] = V, A) ->
+ case is_print_char_list(V) of
+ true ->
+ #c_string{anno = A, val = V};
+ false ->
+ #c_cons{anno = A,
+ hd = lit_to_records(H, []),
+ tl = lit_to_records(T, [])}
+ end;
+lit_to_records([], A) ->
+ #c_nil{anno = A};
+lit_to_records(V, A) when tuple(V) ->
+ #c_tuple{anno = A, es = lit_list_to_records(tuple_to_list(V))}.
+
+lit_list_to_records([T | Ts]) ->
+ [lit_to_records(T, []) | lit_list_to_records(Ts)];
+lit_list_to_records([]) ->
+ [].
+
+
+%% @spec from_records(Tree::record(record_types())) -> cerl()
+%%
+%% record_types() = c_alias | c_apply | c_call | c_case | c_catch |
+%% c_clause | c_cons | c_def| c_fun | c_let |
+%% c_letrec |c_lit | c_module | c_primop |
+%% c_receive | c_seq | c_try | c_tuple |
+%% c_values | c_var
+%%
+%% @doc Translates an explicit record representation to a
+%% corresponding abstract syntax tree. The records are defined in the
+%% file "<code>cerl.hrl</code>".
+%%
+%% <p>Note: Compound constant literals are folded, discarding
+%% annotations on subtrees. There are no <code>c_def</code> nodes in
+%% the abstract representation; annotations on <code>c_def</code>
+%% records are discarded.</p>
+%%
+%% @see type/1
+%% @see to_records/1
+
+from_records(#c_int{val = V, anno = As}) ->
+ ann_c_int(As, V);
+from_records(#c_float{val = V, anno = As}) ->
+ ann_c_float(As, V);
+from_records(#c_atom{val = V, anno = As}) ->
+ ann_c_atom(As, V);
+from_records(#c_char{val = V, anno = As}) ->
+ ann_c_char(As, V);
+from_records(#c_string{val = V, anno = As}) ->
+ ann_c_string(As, V);
+from_records(#c_nil{anno = As}) ->
+ ann_c_nil(As);
+from_records(#c_binary{segments = Ss, anno = As}) ->
+ ann_c_binary(As, from_records_list(Ss));
+from_records(#c_bitstr{val = V, size = S, unit = U, type = T,
+ flags = Fs, anno = As}) ->
+ ann_c_bitstr(As, from_records(V), from_records(S), from_records(U),
+ from_records(T), from_records(Fs));
+from_records(#c_cons{hd = H, tl = T, anno = As}) ->
+ ann_c_cons(As, from_records(H), from_records(T));
+from_records(#c_tuple{es = Es, anno = As}) ->
+ ann_c_tuple(As, from_records_list(Es));
+from_records(#c_var{name = Name, anno = As}) ->
+ ann_c_var(As, Name);
+from_records(#c_fname{id = Id, arity = Arity, anno = As}) ->
+ ann_c_fname(As, Id, Arity);
+from_records(#c_values{es = Es, anno = As}) ->
+ ann_c_values(As, from_records_list(Es));
+from_records(#c_fun{vars = Vs, body = B, anno = As}) ->
+ ann_c_fun(As, from_records_list(Vs), from_records(B));
+from_records(#c_seq{arg = A, body = B, anno = As}) ->
+ ann_c_seq(As, from_records(A), from_records(B));
+from_records(#c_let{vars = Vs, arg = A, body = B, anno = As}) ->
+ ann_c_let(As, from_records_list(Vs), from_records(A),
+ from_records(B));
+from_records(#c_letrec{defs = Fs, body = B, anno = As}) ->
+ ann_c_letrec(As, [{from_records(N), from_records(F)}
+ || #c_def{name = N, val = F} <- Fs],
+ from_records(B));
+from_records(#c_case{arg = A, clauses = Cs, anno = As}) ->
+ ann_c_case(As, from_records(A), from_records_list(Cs));
+from_records(#c_clause{pats = Ps, guard = G, body = B, anno = As}) ->
+ ann_c_clause(As, from_records_list(Ps), from_records(G),
+ from_records(B));
+from_records(#c_alias{var = V, pat = P, anno = As}) ->
+ ann_c_alias(As, from_records(V), from_records(P));
+from_records(#c_receive{clauses = Cs, timeout = T, action = A,
+ anno = As}) ->
+ ann_c_receive(As, from_records_list(Cs), from_records(T),
+ from_records(A));
+from_records(#c_apply{op = Op, args = Es, anno = As}) ->
+ ann_c_apply(As, from_records(Op), from_records_list(Es));
+from_records(#c_call{module = M, name = N, args = Es, anno = As}) ->
+ ann_c_call(As, from_records(M), from_records(N),
+ from_records_list(Es));
+from_records(#c_primop{name = N, args = Es, anno = As}) ->
+ ann_c_primop(As, from_records(N), from_records_list(Es));
+from_records(#c_try{arg = E, vars = Vs, body = B,
+ evars = Evs, handler = H, anno = As}) ->
+ ann_c_try(As, from_records(E), from_records_list(Vs),
+ from_records(B), from_records_list(Evs), from_records(H));
+from_records(#c_catch{body = B, anno = As}) ->
+ ann_c_catch(As, from_records(B));
+from_records(#c_module{name = N, exports = Es, attrs = Ds, defs = Fs,
+ anno = As}) ->
+ ann_c_module(As, from_records(N),
+ from_records_list(Es),
+ [{from_records(K), from_records(V)}
+ || #c_def{name = K, val = V} <- Ds],
+ [{from_records(V), from_records(F)}
+ || #c_def{name = V, val = F} <- Fs]).
+
+from_records_list([T | Ts]) ->
+ [from_records(T) | from_records_list(Ts)];
+from_records_list([]) ->
+ [].
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec is_data(Node::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if <code>Node</code> represents a
+%% data constructor, otherwise <code>false</code>. Data constructors
+%% are cons cells, tuples, and atomic literals.
+%%
+%% @see data_type/1
+%% @see data_es/1
+%% @see data_arity/1
+
+is_data(#literal{}) ->
+ true;
+is_data(#cons{}) ->
+ true;
+is_data(#tuple{}) ->
+ true;
+is_data(_) ->
+ false.
+
+
+%% @spec data_type(Node::cerl()) -> dtype()
+%%
+%% dtype() = cons | tuple | {'atomic', Value}
+%% Value = integer() | float() | atom() | []
+%%
+%% @doc Returns a type descriptor for a data constructor
+%% node. (Cf. <code>is_data/1</code>.) This is mainly useful for
+%% comparing types and for constructing new nodes of the same type
+%% (cf. <code>make_data/2</code>). If <code>Node</code> represents an
+%% integer, floating-point number, atom or empty list, the result is
+%% <code>{'atomic', Value}</code>, where <code>Value</code> is the value
+%% of <code>concrete(Node)</code>, otherwise the result is either
+%% <code>cons</code> or <code>tuple</code>.
+%%
+%% <p>Type descriptors can be compared for equality or order (in the
+%% Erlang term order), but remember that floating-point values should
+%% in general never be tested for equality.</p>
+%%
+%% @see is_data/1
+%% @see make_data/2
+%% @see type/1
+%% @see concrete/1
+
+data_type(#literal{val = V}) ->
+ case V of
+ [_ | _] ->
+ cons;
+ _ when tuple(V) ->
+ tuple;
+ _ ->
+ {'atomic', V}
+ end;
+data_type(#cons{}) ->
+ cons;
+data_type(#tuple{}) ->
+ tuple.
+
+
+%% @spec data_es(Node::cerl()) -> [cerl()]
+%%
+%% @doc Returns the list of subtrees of a data constructor node. If
+%% the arity of the constructor is zero, the result is the empty list.
+%%
+%% <p>Note: if <code>data_type(Node)</code> is <code>cons</code>, the
+%% number of subtrees is exactly two. If <code>data_type(Node)</code>
+%% is <code>{'atomic', Value}</code>, the number of subtrees is
+%% zero.</p>
+%%
+%% @see is_data/1
+%% @see data_type/1
+%% @see data_arity/1
+%% @see make_data/2
+
+data_es(#literal{val = V}) ->
+ case V of
+ [Head | Tail] ->
+ [#literal{val = Head}, #literal{val = Tail}];
+ _ when tuple(V) ->
+ make_lit_list(tuple_to_list(V));
+ _ ->
+ []
+ end;
+data_es(#cons{hd = H, tl = T}) ->
+ [H, T];
+data_es(#tuple{es = Es}) ->
+ Es.
+
+
+%% @spec data_arity(Node::cerl()) -> integer()
+%%
+%% @doc Returns the number of subtrees of a data constructor
+%% node. This is equivalent to <code>length(data_es(Node))</code>, but
+%% potentially more efficient.
+%%
+%% @see is_data/1
+%% @see data_es/1
+
+data_arity(#literal{val = V}) ->
+ case V of
+ [_ | _] ->
+ 2;
+ _ when tuple(V) ->
+ size(V);
+ _ ->
+ 0
+ end;
+data_arity(#cons{}) ->
+ 2;
+data_arity(#tuple{es = Es}) ->
+ length(Es).
+
+
+%% @spec make_data(Type::dtype(), Elements::[cerl()]) -> cerl()
+%%
+%% @doc Creates a data constructor node with the specified type and
+%% subtrees. (Cf. <code>data_type/1</code>.) An exception is thrown
+%% if the length of <code>Elements</code> is invalid for the given
+%% <code>Type</code>; see <code>data_es/1</code> for arity constraints
+%% on constructor types.
+%%
+%% @see data_type/1
+%% @see data_es/1
+%% @see ann_make_data/3
+%% @see update_data/3
+%% @see make_data_skel/2
+
+make_data(CType, Es) ->
+ ann_make_data([], CType, Es).
+
+
+%% @spec ann_make_data(As::[term()], Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data/2
+
+ann_make_data(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
+ann_make_data(As, cons, [H, T]) -> ann_c_cons(As, H, T);
+ann_make_data(As, tuple, Es) -> ann_c_tuple(As, Es).
+
+
+%% @spec update_data(Old::cerl(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data/2
+
+update_data(Node, CType, Es) ->
+ ann_make_data(get_ann(Node), CType, Es).
+
+
+%% @spec make_data_skel(Type::dtype(), Elements::[cerl()]) -> cerl()
+%%
+%% @doc Like <code>make_data/2</code>, but analogous to
+%% <code>c_tuple_skel/1</code> and <code>c_cons_skel/2</code>.
+%%
+%% @see ann_make_data_skel/3
+%% @see update_data_skel/3
+%% @see make_data/2
+%% @see c_tuple_skel/1
+%% @see c_cons_skel/2
+
+make_data_skel(CType, Es) ->
+ ann_make_data_skel([], CType, Es).
+
+
+%% @spec ann_make_data_skel(As::[term()], Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data_skel/2
+
+ann_make_data_skel(As, {'atomic', V}, []) -> #literal{val = V, ann = As};
+ann_make_data_skel(As, cons, [H, T]) -> ann_c_cons_skel(As, H, T);
+ann_make_data_skel(As, tuple, Es) -> ann_c_tuple_skel(As, Es).
+
+
+%% @spec update_data_skel(Old::cerl(), Type::dtype(),
+%% Elements::[cerl()]) -> cerl()
+%% @see make_data_skel/2
+
+update_data_skel(Node, CType, Es) ->
+ ann_make_data_skel(get_ann(Node), CType, Es).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec subtrees(Node::cerl()) -> [[cerl()]]
+%%
+%% @doc Returns the grouped list of all subtrees of a node. If
+%% <code>Node</code> is a leaf node (cf. <code>is_leaf/1</code>), this
+%% is the empty list, otherwise the result is always a nonempty list,
+%% containing the lists of subtrees of <code>Node</code>, in
+%% left-to-right order as they occur in the printed program text, and
+%% grouped by category. Often, each group contains only a single
+%% subtree.
+%%
+%% <p>Depending on the type of <code>Node</code>, the size of some
+%% groups may be variable (e.g., the group consisting of all the
+%% elements of a tuple), while others always contain the same number
+%% of elements - usually exactly one (e.g., the group containing the
+%% argument expression of a case-expression). Note, however, that the
+%% exact structure of the returned list (for a given node type) should
+%% in general not be depended upon, since it might be subject to
+%% change without notice.</p>
+%%
+%% <p>The function <code>subtrees/1</code> and the constructor functions
+%% <code>make_tree/2</code> and <code>update_tree/2</code> can be a
+%% great help if one wants to traverse a syntax tree, visiting all its
+%% subtrees, but treat nodes of the tree in a uniform way in most or all
+%% cases. Using these functions makes this simple, and also assures that
+%% your code is not overly sensitive to extensions of the syntax tree
+%% data type, because any node types not explicitly handled by your code
+%% can be left to a default case.</p>
+%%
+%% <p>For example:
+%% <pre>
+%% postorder(F, Tree) ->
+%% F(case subtrees(Tree) of
+%% [] -> Tree;
+%% List -> update_tree(Tree,
+%% [[postorder(F, Subtree)
+%% || Subtree &lt;- Group]
+%% || Group &lt;- List])
+%% end).
+%% </pre>
+%% maps the function <code>F</code> on <code>Tree</code> and all its
+%% subtrees, doing a post-order traversal of the syntax tree. (Note
+%% the use of <code>update_tree/2</code> to preserve annotations.) For
+%% a simple function like:
+%% <pre>
+%% f(Node) ->
+%% case type(Node) of
+%% atom -> atom("a_" ++ atom_name(Node));
+%% _ -> Node
+%% end.
+%% </pre>
+%% the call <code>postorder(fun f/1, Tree)</code> will yield a new
+%% representation of <code>Tree</code> in which all atom names have
+%% been extended with the prefix "a_", but nothing else (including
+%% annotations) has been changed.</p>
+%%
+%% @see is_leaf/1
+%% @see make_tree/2
+%% @see update_tree/2
+
+subtrees(T) ->
+ case is_leaf(T) of
+ true ->
+ [];
+ false ->
+ case type(T) of
+ values ->
+ [values_es(T)];
+ binary ->
+ [binary_segments(T)];
+ bitstr ->
+ [[bitstr_val(T)], [bitstr_size(T)],
+ [bitstr_unit(T)], [bitstr_type(T)],
+ [bitstr_flags(T)]];
+ cons ->
+ [[cons_hd(T)], [cons_tl(T)]];
+ tuple ->
+ [tuple_es(T)];
+ 'let' ->
+ [let_vars(T), [let_arg(T)], [let_body(T)]];
+ seq ->
+ [[seq_arg(T)], [seq_body(T)]];
+ apply ->
+ [[apply_op(T)], apply_args(T)];
+ call ->
+ [[call_module(T)], [call_name(T)],
+ call_args(T)];
+ primop ->
+ [[primop_name(T)], primop_args(T)];
+ 'case' ->
+ [[case_arg(T)], case_clauses(T)];
+ clause ->
+ [clause_pats(T), [clause_guard(T)],
+ [clause_body(T)]];
+ alias ->
+ [[alias_var(T)], [alias_pat(T)]];
+ 'fun' ->
+ [fun_vars(T), [fun_body(T)]];
+ 'receive' ->
+ [receive_clauses(T), [receive_timeout(T)],
+ [receive_action(T)]];
+ 'try' ->
+ [[try_arg(T)], try_vars(T), [try_body(T)],
+ try_evars(T), [try_handler(T)]];
+ 'catch' ->
+ [[catch_body(T)]];
+ letrec ->
+ Es = unfold_tuples(letrec_defs(T)),
+ [Es, [letrec_body(T)]];
+ module ->
+ As = unfold_tuples(module_attrs(T)),
+ Es = unfold_tuples(module_defs(T)),
+ [[module_name(T)], module_exports(T), As, Es]
+ end
+ end.
+
+
+%% @spec update_tree(Old::cerl(), Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given subtrees, and the same
+%% type and annotations as the <code>Old</code> node. This is
+%% equivalent to <code>ann_make_tree(get_ann(Node), type(Node),
+%% Groups)</code>, but potentially more efficient.
+%%
+%% @see update_tree/3
+%% @see ann_make_tree/3
+%% @see get_ann/1
+%% @see type/1
+
+update_tree(Node, Gs) ->
+ ann_make_tree(get_ann(Node), type(Node), Gs).
+
+
+%% @spec update_tree(Old::cerl(), Type::atom(), Groups::[[cerl()]]) ->
+%% cerl()
+%%
+%% @doc Creates a syntax tree with the given type and subtrees, and
+%% the same annotations as the <code>Old</code> node. This is
+%% equivalent to <code>ann_make_tree(get_ann(Node), Type,
+%% Groups)</code>, but potentially more efficient.
+%%
+%% @see update_tree/2
+%% @see ann_make_tree/3
+%% @see get_ann/1
+
+update_tree(Node, Type, Gs) ->
+ ann_make_tree(get_ann(Node), Type, Gs).
+
+
+%% @spec make_tree(Type::atom(), Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given type and subtrees.
+%% <code>Type</code> must be a node type name
+%% (cf. <code>type/1</code>) that does not denote a leaf node type
+%% (cf. <code>is_leaf/1</code>). <code>Groups</code> must be a
+%% <em>nonempty</em> list of groups of syntax trees, representing the
+%% subtrees of a node of the given type, in left-to-right order as
+%% they would occur in the printed program text, grouped by category
+%% as done by <code>subtrees/1</code>.
+%%
+%% <p>The result of <code>ann_make_tree(get_ann(Node), type(Node),
+%% subtrees(Node))</code> (cf. <code>update_tree/2</code>) represents
+%% the same source code text as the original <code>Node</code>,
+%% assuming that <code>subtrees(Node)</code> yields a nonempty
+%% list. However, it does not necessarily have the exact same data
+%% representation as <code>Node</code>.</p>
+%%
+%% @see ann_make_tree/3
+%% @see type/1
+%% @see is_leaf/1
+%% @see subtrees/1
+%% @see update_tree/2
+
+make_tree(Type, Gs) ->
+ ann_make_tree([], Type, Gs).
+
+
+%% @spec ann_make_tree(As::[term()], Type::atom(),
+%% Groups::[[cerl()]]) -> cerl()
+%%
+%% @doc Creates a syntax tree with the given annotations, type and
+%% subtrees. See <code>make_tree/2</code> for details.
+%%
+%% @see make_tree/2
+
+ann_make_tree(As, values, [Es]) -> ann_c_values(As, Es);
+ann_make_tree(As, binary, [Ss]) -> ann_c_binary(As, Ss);
+ann_make_tree(As, bitstr, [[V],[S],[U],[T],[Fs]]) ->
+ ann_c_bitstr(As, V, S, U, T, Fs);
+ann_make_tree(As, cons, [[H], [T]]) -> ann_c_cons(As, H, T);
+ann_make_tree(As, tuple, [Es]) -> ann_c_tuple(As, Es);
+ann_make_tree(As, 'let', [Vs, [A], [B]]) -> ann_c_let(As, Vs, A, B);
+ann_make_tree(As, seq, [[A], [B]]) -> ann_c_seq(As, A, B);
+ann_make_tree(As, apply, [[Op], Es]) -> ann_c_apply(As, Op, Es);
+ann_make_tree(As, call, [[M], [N], Es]) -> ann_c_call(As, M, N, Es);
+ann_make_tree(As, primop, [[N], Es]) -> ann_c_primop(As, N, Es);
+ann_make_tree(As, 'case', [[A], Cs]) -> ann_c_case(As, A, Cs);
+ann_make_tree(As, clause, [Ps, [G], [B]]) -> ann_c_clause(As, Ps, G, B);
+ann_make_tree(As, alias, [[V], [P]]) -> ann_c_alias(As, V, P);
+ann_make_tree(As, 'fun', [Vs, [B]]) -> ann_c_fun(As, Vs, B);
+ann_make_tree(As, 'receive', [Cs, [T], [A]]) ->
+ ann_c_receive(As, Cs, T, A);
+ann_make_tree(As, 'try', [[E], Vs, [B], Evs, [H]]) ->
+ ann_c_try(As, E, Vs, B, Evs, H);
+ann_make_tree(As, 'catch', [[B]]) -> ann_c_catch(As, B);
+ann_make_tree(As, letrec, [Es, [B]]) ->
+ ann_c_letrec(As, fold_tuples(Es), B);
+ann_make_tree(As, module, [[N], Xs, Es, Ds]) ->
+ ann_c_module(As, N, Xs, fold_tuples(Es), fold_tuples(Ds)).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec meta(Tree::cerl()) -> cerl()
+%%
+%% @doc Creates a meta-representation of a syntax tree. The result
+%% represents an Erlang expression "<code><em>MetaTree</em></code>"
+%% which, if evaluated, will yield a new syntax tree representing the
+%% same source code text as <code>Tree</code> (although the actual
+%% data representation may be different). The expression represented
+%% by <code>MetaTree</code> is <em>implementation independent</em>
+%% with regard to the data structures used by the abstract syntax tree
+%% implementation.
+%%
+%% <p>Any node in <code>Tree</code> whose node type is
+%% <code>var</code> (cf. <code>type/1</code>), and whose list of
+%% annotations (cf. <code>get_ann/1</code>) contains the atom
+%% <code>meta_var</code>, will remain unchanged in the resulting tree,
+%% except that exactly one occurrence of <code>meta_var</code> is
+%% removed from its annotation list.</p>
+%%
+%% <p>The main use of the function <code>meta/1</code> is to transform
+%% a data structure <code>Tree</code>, which represents a piece of
+%% program code, into a form that is <em>representation independent
+%% when printed</em>. E.g., suppose <code>Tree</code> represents a
+%% variable named "V". Then (assuming a function <code>print/1</code>
+%% for printing syntax trees), evaluating
+%% <code>print(abstract(Tree))</code> - simply using
+%% <code>abstract/1</code> to map the actual data structure onto a
+%% syntax tree representation - would output a string that might look
+%% something like "<code>{var, ..., 'V'}</code>", which is obviously
+%% dependent on the implementation of the abstract syntax trees. This
+%% could e.g. be useful for caching a syntax tree in a file. However,
+%% in some situations like in a program generator generator (with two
+%% "generator"), it may be unacceptable. Using
+%% <code>print(meta(Tree))</code> instead would output a
+%% <em>representation independent</em> syntax tree generating
+%% expression; in the above case, something like
+%% "<code>cerl:c_var('V')</code>".</p>
+%%
+%% <p>The implementation tries to generate compact code with respect
+%% to literals and lists.</p>
+%%
+%% @see abstract/1
+%% @see type/1
+%% @see get_ann/1
+
+meta(Node) ->
+ %% First of all we check for metavariables:
+ case type(Node) of
+ var ->
+ case lists:member(meta_var, get_ann(Node)) of
+ false ->
+ meta_0(var, Node);
+ true ->
+ %% A meta-variable: remove the first found
+ %% 'meta_var' annotation, but otherwise leave
+ %% the node unchanged.
+ set_ann(Node, lists:delete(meta_var, get_ann(Node)))
+ end;
+ Type ->
+ meta_0(Type, Node)
+ end.
+
+meta_0(Type, Node) ->
+ case get_ann(Node) of
+ [] ->
+ meta_1(Type, Node);
+ As ->
+ meta_call(set_ann, [meta_1(Type, Node), abstract(As)])
+ end.
+
+meta_1(literal, Node) ->
+ %% We handle atomic literals separately, to get a bit
+ %% more compact code. For the rest, we use 'abstract'.
+ case concrete(Node) of
+ V when atom(V) ->
+ meta_call(c_atom, [Node]);
+ V when integer(V) ->
+ meta_call(c_int, [Node]);
+ V when float(V) ->
+ meta_call(c_float, [Node]);
+ [] ->
+ meta_call(c_nil, []);
+ _ ->
+ meta_call(abstract, [Node])
+ end;
+meta_1(var, Node) ->
+ %% A normal variable or function name.
+ meta_call(c_var, [abstract(var_name(Node))]);
+meta_1(values, Node) ->
+ meta_call(c_values,
+ [make_list(meta_list(values_es(Node)))]);
+meta_1(binary, Node) ->
+ meta_call(c_binary,
+ [make_list(meta_list(binary_segments(Node)))]);
+meta_1(bitstr, Node) ->
+ meta_call(c_bitstr,
+ [meta(bitstr_val(Node)),
+ meta(bitstr_size(Node)),
+ meta(bitstr_unit(Node)),
+ meta(bitstr_type(Node)),
+ meta(bitstr_flags(Node))]);
+meta_1(cons, Node) ->
+ %% The list is split up if some sublist has annotatations. If
+ %% we get exactly one element, we generate a 'c_cons' call
+ %% instead of 'make_list' to reconstruct the node.
+ case split_list(Node) of
+ {[H], none} ->
+ meta_call(c_cons, [meta(H), meta(c_nil())]);
+ {[H], Node1} ->
+ meta_call(c_cons, [meta(H), meta(Node1)]);
+ {L, none} ->
+ meta_call(make_list, [make_list(meta_list(L))]);
+ {L, Node1} ->
+ meta_call(make_list,
+ [make_list(meta_list(L)), meta(Node1)])
+ end;
+meta_1(tuple, Node) ->
+ meta_call(c_tuple,
+ [make_list(meta_list(tuple_es(Node)))]);
+meta_1('let', Node) ->
+ meta_call(c_let,
+ [make_list(meta_list(let_vars(Node))),
+ meta(let_arg(Node)), meta(let_body(Node))]);
+meta_1(seq, Node) ->
+ meta_call(c_seq,
+ [meta(seq_arg(Node)), meta(seq_body(Node))]);
+meta_1(apply, Node) ->
+ meta_call(c_apply,
+ [meta(apply_op(Node)),
+ make_list(meta_list(apply_args(Node)))]);
+meta_1(call, Node) ->
+ meta_call(c_call,
+ [meta(call_module(Node)), meta(call_name(Node)),
+ make_list(meta_list(call_args(Node)))]);
+meta_1(primop, Node) ->
+ meta_call(c_primop,
+ [meta(primop_name(Node)),
+ make_list(meta_list(primop_args(Node)))]);
+meta_1('case', Node) ->
+ meta_call(c_case,
+ [meta(case_arg(Node)),
+ make_list(meta_list(case_clauses(Node)))]);
+meta_1(clause, Node) ->
+ meta_call(c_clause,
+ [make_list(meta_list(clause_pats(Node))),
+ meta(clause_guard(Node)),
+ meta(clause_body(Node))]);
+meta_1(alias, Node) ->
+ meta_call(c_alias,
+ [meta(alias_var(Node)), meta(alias_pat(Node))]);
+meta_1('fun', Node) ->
+ meta_call(c_fun,
+ [make_list(meta_list(fun_vars(Node))),
+ meta(fun_body(Node))]);
+meta_1('receive', Node) ->
+ meta_call(c_receive,
+ [make_list(meta_list(receive_clauses(Node))),
+ meta(receive_timeout(Node)),
+ meta(receive_action(Node))]);
+meta_1('try', Node) ->
+ meta_call(c_try,
+ [meta(try_arg(Node)),
+ make_list(meta_list(try_vars(Node))),
+ meta(try_body(Node)),
+ make_list(meta_list(try_evars(Node))),
+ meta(try_handler(Node))]);
+meta_1('catch', Node) ->
+ meta_call(c_catch, [meta(catch_body(Node))]);
+meta_1(letrec, Node) ->
+ meta_call(c_letrec,
+ [make_list([c_tuple([meta(N), meta(F)])
+ || {N, F} <- letrec_defs(Node)]),
+ meta(letrec_body(Node))]);
+meta_1(module, Node) ->
+ meta_call(c_module,
+ [meta(module_name(Node)),
+ make_list(meta_list(module_exports(Node))),
+ make_list([c_tuple([meta(A), meta(V)])
+ || {A, V} <- module_attrs(Node)]),
+ make_list([c_tuple([meta(N), meta(F)])
+ || {N, F} <- module_defs(Node)])]).
+
+meta_call(F, As) ->
+ c_call(c_atom(?MODULE), c_atom(F), As).
+
+meta_list([T | Ts]) ->
+ [meta(T) | meta_list(Ts)];
+meta_list([]) ->
+ [].
+
+split_list(Node) ->
+ split_list(set_ann(Node, []), []).
+
+split_list(Node, L) ->
+ A = get_ann(Node),
+ case type(Node) of
+ cons when A == [] ->
+ split_list(cons_tl(Node), [cons_hd(Node) | L]);
+ nil when A == [] ->
+ {lists:reverse(L), none};
+ _ ->
+ {lists:reverse(L), Node}
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+%% General utilities
+
+is_lit_list([#literal{} | Es]) ->
+ is_lit_list(Es);
+is_lit_list([_ | _]) ->
+ false;
+is_lit_list([]) ->
+ true.
+
+lit_list_vals([#literal{val = V} | Es]) ->
+ [V | lit_list_vals(Es)];
+lit_list_vals([]) ->
+ [].
+
+make_lit_list([V | Vs]) ->
+ [#literal{val = V} | make_lit_list(Vs)];
+make_lit_list([]) ->
+ [].
+
+%% The following tests are the same as done by 'io_lib:char_list' and
+%% 'io_lib:printable_list', respectively, but for a single character.
+
+is_char_value(V) when V >= $\000, V =< $\377 -> true;
+is_char_value(_) -> false.
+
+is_print_char_value(V) when V >= $\040, V =< $\176 -> true;
+is_print_char_value(V) when V >= $\240, V =< $\377 -> true;
+is_print_char_value(V) when V =:= $\b -> true;
+is_print_char_value(V) when V =:= $\d -> true;
+is_print_char_value(V) when V =:= $\e -> true;
+is_print_char_value(V) when V =:= $\f -> true;
+is_print_char_value(V) when V =:= $\n -> true;
+is_print_char_value(V) when V =:= $\r -> true;
+is_print_char_value(V) when V =:= $\s -> true;
+is_print_char_value(V) when V =:= $\t -> true;
+is_print_char_value(V) when V =:= $\v -> true;
+is_print_char_value(V) when V =:= $\" -> true;
+is_print_char_value(V) when V =:= $\' -> true;
+is_print_char_value(V) when V =:= $\\ -> true;
+is_print_char_value(_) -> false.
+
+is_char_list([V | Vs]) when integer(V) ->
+ case is_char_value(V) of
+ true ->
+ is_char_list(Vs);
+ false ->
+ false
+ end;
+is_char_list([]) ->
+ true;
+is_char_list(_) ->
+ false.
+
+is_print_char_list([V | Vs]) when integer(V) ->
+ case is_print_char_value(V) of
+ true ->
+ is_print_char_list(Vs);
+ false ->
+ false
+ end;
+is_print_char_list([]) ->
+ true;
+is_print_char_list(_) ->
+ false.
+
+unfold_tuples([{X, Y} | Ps]) ->
+ [X, Y | unfold_tuples(Ps)];
+unfold_tuples([]) ->
+ [].
+
+fold_tuples([X, Y | Es]) ->
+ [{X, Y} | fold_tuples(Es)];
+fold_tuples([]) ->
+ [].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl
new file mode 100644
index 0000000000..f207178f13
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_clauses.erl
@@ -0,0 +1,409 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl_clauses.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
+
+%% @doc Utility functions for Core Erlang case/receive clauses.
+%%
+%% <p>Syntax trees are defined in the module <a
+%% href=""><code>cerl</code></a>.</p>
+%%
+%% @type cerl() = cerl:cerl()
+
+-module(cerl_clauses).
+
+-export([any_catchall/1, eval_guard/1, is_catchall/1, match/2,
+ match_list/2, reduce/1, reduce/2]).
+
+-import(cerl, [alias_pat/1, alias_var/1, data_arity/1, data_es/1,
+ data_type/1, clause_guard/1, clause_pats/1, concrete/1,
+ is_data/1, is_c_var/1, let_body/1, letrec_body/1,
+ seq_body/1, try_arg/1, type/1, values_es/1]).
+
+-import(lists, [reverse/1]).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec is_catchall(Clause::cerl()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if an abstract clause is a
+%% catch-all, otherwise <code>false</code>. A clause is a catch-all if
+%% all its patterns are variables, and its guard expression always
+%% evaluates to <code>true</code>; cf. <code>eval_guard/1</code>.
+%%
+%% <p>Note: <code>Clause</code> must have type
+%% <code>clause</code>.</p>
+%%
+%% @see eval_guard/1
+%% @see any_catchall/1
+
+is_catchall(C) ->
+ case all_vars(clause_pats(C)) of
+ true ->
+ case eval_guard(clause_guard(C)) of
+ {value, true} ->
+ true;
+ _ ->
+ false
+ end;
+ false ->
+ false
+ end.
+
+all_vars([C | Cs]) ->
+ case is_c_var(C) of
+ true ->
+ all_vars(Cs);
+ false ->
+ false
+ end;
+all_vars([]) ->
+ true.
+
+
+%% @spec any_catchall(Clauses::[cerl()]) -> boolean()
+%%
+%% @doc Returns <code>true</code> if any of the abstract clauses in
+%% the list is a catch-all, otherwise <code>false</code>. See
+%% <code>is_catchall/1</code> for details.
+%%
+%% <p>Note: each node in <code>Clauses</code> must have type
+%% <code>clause</code>.</p>
+%%
+%% @see is_catchall/1
+
+any_catchall([C | Cs]) ->
+ case is_catchall(C) of
+ true ->
+ true;
+ false ->
+ any_catchall(Cs)
+ end;
+any_catchall([]) ->
+ false.
+
+
+%% @spec eval_guard(Expr::cerl()) -> none | {value, term()}
+%%
+%% @doc Tries to reduce a guard expression to a single constant value,
+%% if possible. The returned value is <code>{value, Term}</code> if the
+%% guard expression <code>Expr</code> always yields the constant value
+%% <code>Term</code>, and is otherwise <code>none</code>.
+%%
+%% <p>Note that although guard expressions should only yield boolean
+%% values, this function does not guarantee that <code>Term</code> is
+%% either <code>true</code> or <code>false</code>. Also note that only
+%% simple constructs like let-expressions are examined recursively;
+%% general constant folding is not performed.</p>
+%%
+%% @see is_catchall/1
+
+%% This function could possibly be improved further, but constant
+%% folding should in general be performed elsewhere.
+
+eval_guard(E) ->
+ case type(E) of
+ literal ->
+ {value, concrete(E)};
+ values ->
+ case values_es(E) of
+ [E1] ->
+ eval_guard(E1);
+ _ ->
+ none
+ end;
+ 'try' ->
+ eval_guard(try_arg(E));
+ seq ->
+ eval_guard(seq_body(E));
+ 'let' ->
+ eval_guard(let_body(E));
+ 'letrec' ->
+ eval_guard(letrec_body(E));
+ _ ->
+ none
+ end.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec reduce(Clauses) -> {true, {Clauses, Bindings}}
+%% | {false, Clauses}
+%%
+%% @equiv reduce(Cs, [])
+
+reduce(Cs) ->
+ reduce(Cs, []).
+
+%% @spec reduce(Clauses::[Clause], Exprs::[Expr]) ->
+%% {true, {Clause, Bindings}}
+%% | {false, [Clause]}
+%%
+%% Clause = cerl()
+%% Expr = any | cerl()
+%% Bindings = [{cerl(), cerl()}]
+%%
+%% @doc Selects a single clause, if possible, or otherwise reduces the
+%% list of selectable clauses. The input is a list <code>Clauses</code>
+%% of abstract clauses (i.e., syntax trees of type <code>clause</code>),
+%% and a list of switch expressions <code>Exprs</code>. The function
+%% tries to uniquely select a single clause or discard unselectable
+%% clauses, with respect to the switch expressions. All abstract clauses
+%% in the list must have the same number of patterns. If
+%% <code>Exprs</code> is not the empty list, it must have the same
+%% length as the number of patterns in each clause; see
+%% <code>match_list/2</code> for details.
+%%
+%% <p>A clause can only be selected if its guard expression always
+%% yields the atom <code>true</code>, and a clause whose guard
+%% expression always yields the atom <code>false</code> can never be
+%% selected. Other guard expressions are considered to have unknown
+%% value; cf. <code>eval_guard/1</code>.</p>
+%%
+%% <p>If a particular clause can be selected, the function returns
+%% <code>{true, {Clause, Bindings}}</code>, where <code>Clause</code> is
+%% the selected clause and <code>Bindings</code> is a list of pairs
+%% <code>{Var, SubExpr}</code> associating the variables occurring in
+%% the patterns of <code>Clause</code> with the corresponding
+%% subexpressions in <code>Exprs</code>. The list of bindings is given
+%% in innermost-first order; see the <code>match/2</code> function for
+%% details.</p>
+%%
+%% <p>If no clause could be definitely selected, the function returns
+%% <code>{false, NewClauses}</code>, where <code>NewClauses</code> is
+%% the list of entries in <code>Clauses</code> that remain after
+%% eliminating unselectable clauses, preserving the relative order.</p>
+%%
+%% @see eval_guard/1
+%% @see match/2
+%% @see match_list/2
+
+reduce(Cs, Es) ->
+ reduce(Cs, Es, []).
+
+reduce([C | Cs], Es, Cs1) ->
+ Ps = clause_pats(C),
+ case match_list(Ps, Es) of
+ none ->
+ %% Here, we know that the current clause cannot possibly be
+ %% selected, so we drop it and visit the rest.
+ reduce(Cs, Es, Cs1);
+ {false, _} ->
+ %% We are not sure if this clause might be selected, so we
+ %% save it and visit the rest.
+ reduce(Cs, Es, [C | Cs1]);
+ {true, Bs} ->
+ case eval_guard(clause_guard(C)) of
+ {value, true} when Cs1 == [] ->
+ %% We have a definite match - we return the residual
+ %% expression and signal that a selection has been
+ %% made. All other clauses are dropped.
+ {true, {C, Bs}};
+ {value, true} ->
+ %% Unless one of the previous clauses is selected,
+ %% this clause will definitely be, so we can drop
+ %% the rest.
+ {false, reverse([C | Cs1])};
+ {value, false} ->
+ %% This clause can never be selected, since its
+ %% guard is never 'true', so we drop it.
+ reduce(Cs, Es, Cs1);
+ _ ->
+ %% We are not sure if this clause might be selected
+ %% (or might even cause a crash), so we save it and
+ %% visit the rest.
+ reduce(Cs, Es, [C | Cs1])
+ end
+ end;
+reduce([], _, Cs) ->
+ %% All clauses visited, without a complete match. Signal "not
+ %% reduced" and return the saved clauses, in the correct order.
+ {false, reverse(Cs)}.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec match(Pattern::cerl(), Expr) ->
+%% none | {true, Bindings} | {false, Bindings}
+%%
+%% Expr = any | cerl()
+%% Bindings = [{cerl(), Expr}]
+%%
+%% @doc Matches a pattern against an expression. The returned value is
+%% <code>none</code> if a match is impossible, <code>{true,
+%% Bindings}</code> if <code>Pattern</code> definitely matches
+%% <code>Expr</code>, and <code>{false, Bindings}</code> if a match is
+%% not definite, but cannot be excluded. <code>Bindings</code> is then
+%% a list of pairs <code>{Var, SubExpr}</code>, associating each
+%% variable in the pattern with either the corresponding subexpression
+%% of <code>Expr</code>, or with the atom <code>any</code> if no
+%% matching subexpression exists. (Recall that variables may not be
+%% repeated in a Core Erlang pattern.) The list of bindings is given
+%% in innermost-first order; this should only be of interest if
+%% <code>Pattern</code> contains one or more alias patterns. If the
+%% returned value is <code>{true, []}</code>, it implies that the
+%% pattern and the expression are syntactically identical.
+%%
+%% <p>Instead of a syntax tree, the atom <code>any</code> can be
+%% passed for <code>Expr</code> (or, more generally, be used for any
+%% subtree of <code>Expr</code>, in as much the abstract syntax tree
+%% implementation allows it); this means that it cannot be decided
+%% whether the pattern will match or not, and the corresponding
+%% variable bindings will all map to <code>any</code>. The typical use
+%% is for producing bindings for <code>receive</code> clauses.</p>
+%%
+%% <p>Note: Binary-syntax patterns are never structurally matched
+%% against binary-syntax expressions by this function.</p>
+%%
+%% <p>Examples:
+%% <ul>
+%% <li>Matching a pattern "<code>{X, Y}</code>" against the
+%% expression "<code>{foo, f(Z)}</code>" yields <code>{true,
+%% Bindings}</code> where <code>Bindings</code> associates
+%% "<code>X</code>" with the subtree "<code>foo</code>" and
+%% "<code>Y</code>" with the subtree "<code>f(Z)</code>".</li>
+%%
+%% <li>Matching pattern "<code>{X, {bar, Y}}</code>" against
+%% expression "<code>{foo, f(Z)}</code>" yields <code>{false,
+%% Bindings}</code> where <code>Bindings</code> associates
+%% "<code>X</code>" with the subtree "<code>foo</code>" and
+%% "<code>Y</code>" with <code>any</code> (because it is not known
+%% if "<code>{foo, Y}</code>" might match the run-time value of
+%% "<code>f(Z)</code>" or not).</li>
+%%
+%% <li>Matching pattern "<code>{foo, bar}</code>" against expression
+%% "<code>{foo, f()}</code>" yields <code>{false, []}</code>,
+%% telling us that there might be a match, but we cannot deduce any
+%% bindings.</li>
+%%
+%% <li>Matching <code>{foo, X = {bar, Y}}</code> against expression
+%% "<code>{foo, {bar, baz}}</code>" yields <code>{true,
+%% Bindings}</code> where <code>Bindings</code> associates
+%% "<code>Y</code>" with "<code>baz</code>", and "<code>X</code>"
+%% with "<code>{bar, baz}</code>".</li>
+%%
+%% <li>Matching a pattern "<code>{X, Y}</code>" against
+%% <code>any</code> yields <code>{false, Bindings}</code> where
+%% <code>Bindings</code> associates both "<code>X</code>" and
+%% "<code>Y</code>" with <code>any</code>.</li>
+%% </ul></p>
+
+match(P, E) ->
+ match(P, E, []).
+
+match(P, E, Bs) ->
+ case type(P) of
+ var ->
+ %% Variables always match, since they cannot have repeated
+ %% occurrences in a pattern.
+ {true, [{P, E} | Bs]};
+ alias ->
+ %% All variables in P1 will be listed before the alias
+ %% variable in the result.
+ match(alias_pat(P), E, [{alias_var(P), E} | Bs]);
+ binary ->
+ %% The most we can do is to say "definitely no match" if a
+ %% binary pattern is matched against non-binary data.
+ if E == any ->
+ {false, Bs};
+ true ->
+ case is_data(E) of
+ true ->
+ none;
+ false ->
+ {false, Bs}
+ end
+ end;
+ _ ->
+ match_1(P, E, Bs)
+ end.
+
+match_1(P, E, Bs) ->
+ case is_data(P) of
+ true when E == any ->
+ %% If we don't know the structure of the value of E at this
+ %% point, we just match the subpatterns against 'any', and
+ %% make sure the result is a "maybe".
+ Ps = data_es(P),
+ Es = lists:duplicate(length(Ps), any),
+ case match_list(Ps, Es, Bs) of
+ {_, Bs1} ->
+ {false, Bs1};
+ none ->
+ none
+ end;
+ true ->
+ %% Test if the expression represents a constructor
+ case is_data(E) of
+ true ->
+ T1 = {data_type(E), data_arity(E)},
+ T2 = {data_type(P), data_arity(P)},
+ %% Note that we must test for exact equality.
+ if T1 =:= T2 ->
+ match_list(data_es(P), data_es(E), Bs);
+ true ->
+ none
+ end;
+ false ->
+ %% We don't know the run-time structure of E, and P
+ %% is not a variable or an alias pattern, so we
+ %% match against 'any' instead.
+ match_1(P, any, Bs)
+ end;
+ false ->
+ %% Strange pattern - give up, but don't say "no match".
+ {false, Bs}
+ end.
+
+
+%% @spec match_list(Patterns::[cerl()], Exprs::[Expr]) ->
+%% none | {true, Bindings} | {false, Bindings}
+%%
+%% Expr = any | cerl()
+%% Bindings = [{cerl(), cerl()}]
+%%
+%% @doc Like <code>match/2</code>, but matching a sequence of patterns
+%% against a sequence of expressions. Passing an empty list for
+%% <code>Exprs</code> is equivalent to passing a list of
+%% <code>any</code> atoms of the same length as <code>Patterns</code>.
+%%
+%% @see match/2
+
+match_list([], []) ->
+ {true, []}; % no patterns always match
+match_list(Ps, []) ->
+ match_list(Ps, lists:duplicate(length(Ps), any), []);
+match_list(Ps, Es) ->
+ match_list(Ps, Es, []).
+
+match_list([P | Ps], [E | Es], Bs) ->
+ case match(P, E, Bs) of
+ {true, Bs1} ->
+ match_list(Ps, Es, Bs1);
+ {false, Bs1} ->
+ %% Make sure "maybe" is preserved
+ case match_list(Ps, Es, Bs1) of
+ {_, Bs2} ->
+ {false, Bs2};
+ none ->
+ none
+ end;
+ none ->
+ none
+ end;
+match_list([], [], Bs) ->
+ {true, Bs}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl
new file mode 100644
index 0000000000..e040904a19
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_inline.erl
@@ -0,0 +1,2762 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl_inline.erl,v 1.1 2008/12/17 09:53:41 mikpe Exp $
+%%
+%% Core Erlang inliner.
+
+%% =====================================================================
+%%
+%% This is an implementation of the algorithm by Waddell and Dybvig
+%% ("Fast and Effective Procedure Inlining", International Static
+%% Analysis Symposium 1997), adapted to the Core Erlang language.
+%%
+%% Instead of always renaming variables and function variables, this
+%% implementation uses the "no-shadowing strategy" of Peyton Jones and
+%% Marlow ("Secrets of the Glasgow Haskell Compiler Inliner", 1999).
+%%
+%% =====================================================================
+
+%% TODO: inline single-source-reference operands without size limit.
+
+-module(cerl_inline).
+
+-export([core_transform/2, transform/1, transform/2]).
+
+-import(cerl, [abstract/1, alias_pat/1, alias_var/1, apply_args/1,
+ apply_op/1, atom_name/1, atom_val/1, bitstr_val/1,
+ bitstr_size/1, bitstr_unit/1, bitstr_type/1,
+ bitstr_flags/1, binary_segments/1, update_c_alias/3,
+ update_c_apply/3, update_c_binary/2, update_c_bitstr/6,
+ update_c_call/4, update_c_case/3, update_c_catch/2,
+ update_c_clause/4, c_fun/2, c_int/1, c_let/3,
+ update_c_let/4, update_c_letrec/3, update_c_module/5,
+ update_c_primop/3, update_c_receive/4, update_c_seq/3,
+ c_seq/2, update_c_try/6, c_tuple/1, update_c_values/2,
+ c_values/1, c_var/1, call_args/1, call_module/1,
+ call_name/1, case_arity/1, case_arg/1, case_clauses/1,
+ catch_body/1, clause_body/1, clause_guard/1,
+ clause_pats/1, clause_vars/1, concrete/1, cons_hd/1,
+ cons_tl/1, data_arity/1, data_es/1, data_type/1,
+ fun_body/1, fun_vars/1, get_ann/1, int_val/1,
+ is_c_atom/1, is_c_cons/1, is_c_fun/1, is_c_int/1,
+ is_c_list/1, is_c_seq/1, is_c_tuple/1, is_c_var/1,
+ is_data/1, is_literal/1, is_literal_term/1, let_arg/1,
+ let_body/1, let_vars/1, letrec_body/1, letrec_defs/1,
+ list_length/1, list_elements/1, update_data/3,
+ make_list/1, make_data_skel/2, module_attrs/1,
+ module_defs/1, module_exports/1, module_name/1,
+ primop_args/1, primop_name/1, receive_action/1,
+ receive_clauses/1, receive_timeout/1, seq_arg/1,
+ seq_body/1, set_ann/2, try_arg/1, try_body/1, try_vars/1,
+ try_evars/1, try_handler/1, tuple_es/1, tuple_arity/1,
+ type/1, values_es/1, var_name/1]).
+
+-import(lists, [foldl/3, foldr/3, mapfoldl/3, reverse/1]).
+
+%%
+%% Constants
+%%
+
+debug_runtime() -> false.
+debug_counters() -> false.
+
+%% Normal execution times for inlining are between 0.1 and 0.3 seconds
+%% (on the author's current equipment). The default effort limit of 150
+%% is high enough that most normal programs never hit the limit even
+%% once, and for difficult programs, it generally keeps the execution
+%% times below 2-5 seconds. Using an effort counter of 1000 will thus
+%% have no further effect on most programs, but some programs may take
+%% as much as 10 seconds or more. Effort counts larger than 2500 have
+%% never been observed even on very ill-conditioned programs.
+%%
+%% Size limits between 6 and 18 tend to actually shrink the code,
+%% because of the simplifications made possible by inlining. A limit of
+%% 16 seems to be optimal for this purpose, often shrinking the
+%% executable code by up to 10%. Size limits between 18 and 30 generally
+%% give the same code size as if no inlining was done (i.e., code
+%% duplication balances out the simplifications at these levels). A size
+%% limit between 1 and 5 tends to inline small functions and propagate
+%% constants, but does not cause much simplifications do be done, so the
+%% net effect will be a slight increase in code size. For size limits
+%% above 30, the executable code size tends to increase with about 10%
+%% per 100 units, with some variations depending on the sizes of
+%% functions in the source code.
+%%
+%% Typically, about 90% of the maximum speedup achievable is already
+%% reached using a size limit of 30, and 98% is reached at limits around
+%% 100-150; there is rarely any point in letting the code size increase
+%% by more than 10-15%. If too large functions are inlined, cache
+%% effects will slow the program down.
+
+default_effort() -> 150.
+default_size() -> 24.
+
+%% Base costs/weights for different kinds of expressions. If these are
+%% modified, the size limits above may have to be adjusted.
+
+weight(var) -> 0; % We count no cost for variable accesses.
+weight(values) -> 0; % Value aggregates have no cost in themselves.
+weight(literal) -> 1; % We assume efficient handling of constants.
+weight(data) -> 1; % Base cost; add 1 per element.
+weight(element) -> 1; % Cost of storing/fetching an element.
+weight(argument) -> 1; % Cost of passing a function argument.
+weight('fun') -> 6; % Base cost + average number of free vars.
+weight('let') -> 0; % Count no cost for let-bindings.
+weight(letrec) -> 0; % Like a let-binding.
+weight('case') -> 0; % Case switches have no base cost.
+weight(clause) -> 1; % Count one jump at the end of each clause body.
+weight('receive') -> 9; % Initialization/cleanup cost.
+weight('try') -> 1; % Assume efficient implementation.
+weight('catch') -> 1; % See `try'.
+weight(apply) -> 3; % Average base cost: call/return.
+weight(call) -> 3; % Assume remote-calls as efficient as `apply'.
+weight(primop) -> 2; % Assume more efficient than `apply'.
+weight(binary) -> 4; % Initialisation base cost.
+weight(bitstr) -> 3; % Coding/decoding a value; like a primop.
+weight(module) -> 1. % Like a letrec with a constant body
+
+%% These "reference" structures are used for variables and function
+%% variables. They keep track of the variable name, any bound operand,
+%% and the associated store location.
+
+-record(ref, {name, opnd, loc}).
+
+%% Operand structures contain the operand expression, the renaming and
+%% environment, the state location, and the effort counter at the call
+%% site (cf. `visit').
+
+-record(opnd, {expr, ren, env, loc, effort}).
+
+%% Since expressions are only visited in `effect' context when they are
+%% not bound to a referenced variable, only expressions visited in
+%% 'value' context are cached.
+
+-record(cache, {expr, size}).
+
+%% The context flags for an application structure are kept separate from
+%% the structure itself. Note that the original algorithm had exactly
+%% one operand in each application context structure, while we can have
+%% several, or none.
+
+-record(app, {opnds, ctxt, loc}).
+
+
+%%
+%% Interface functions
+%%
+
+%% Use compile option `{core_transform, inline}' to insert this as a
+%% compilation pass.
+
+core_transform(Code, Opts) ->
+ cerl:to_records(transform(cerl:from_records(Code), Opts)).
+
+transform(Tree) ->
+ transform(Tree, []).
+
+transform(Tree, Opts) ->
+ main(Tree, value, Opts).
+
+main(Tree, Ctxt, Opts) ->
+ %% We spawn a new process to do the work, so we don't have to worry
+ %% about cluttering the process dictionary with debugging info, or
+ %% proper deallocation of ets-tables.
+ Opts1 = Opts ++ [{inline_size, default_size()},
+ {inline_effort, default_effort()}],
+ Reply = self(),
+ Pid = spawn_link(fun () -> start(Reply, Tree, Ctxt, Opts1) end),
+ receive
+ {Pid1, Tree1} when Pid1 == Pid ->
+ Tree1
+ end.
+
+start(Reply, Tree, Ctxt, Opts) ->
+ init_debug(),
+ case debug_runtime() of
+ true ->
+ put(inline_start_time,
+ element(1, erlang:statistics(runtime)));
+ _ ->
+ ok
+ end,
+ Size = max(1, proplists:get_value(inline_size, Opts)),
+ Effort = max(1, proplists:get_value(inline_effort, Opts)),
+ case proplists:get_bool(verbose, Opts) of
+ true ->
+ io:fwrite("Inlining: inline_size=~w inline_effort=~w\n",
+ [Size, Effort]);
+ false ->
+ ok
+ end,
+
+ %% Note that the counters of the new state are passive.
+ S = st__new(Effort, Size),
+
+%%% Initialization is not needed at present. Note that the code in
+%%% `inline_init' is not up-to-date with this module.
+%%% {Tree1, S1} = inline_init:init(Tree, S),
+%%% {Tree2, _S2} = i(Tree1, Ctxt, S1),
+ {Tree2, _S2} = i(Tree, Ctxt, S),
+ report_debug(),
+ Reply ! {self(), Tree2}.
+
+init_debug() ->
+ case debug_counters() of
+ true ->
+ put(counter_effort_triggers, 0),
+ put(counter_effort_max, 0),
+ put(counter_size_triggers, 0),
+ put(counter_size_max, 0);
+ _ ->
+ ok
+ end.
+
+report_debug() ->
+ case debug_runtime() of
+ true ->
+ {Time, _} = erlang:statistics(runtime),
+ report("Total run time for inlining: ~.2.0f s.\n",
+ [(Time - get(inline_start_time))/1000]);
+ _ ->
+ ok
+ end,
+ case debug_counters() of
+ true ->
+ counter_stats();
+ _ ->
+ ok
+ end.
+
+counter_stats() ->
+ T1 = get(counter_effort_triggers),
+ T2 = get(counter_size_triggers),
+ E = get(counter_effort_max),
+ S = get(counter_size_max),
+ M1 = io_lib:fwrite("\tNumber of triggered "
+ "effort counters: ~p.\n", [T1]),
+ M2 = io_lib:fwrite("\tNumber of triggered "
+ "size counters: ~p.\n", [T2]),
+ M3 = io_lib:fwrite("\tLargest active effort counter: ~p.\n",
+ [E]),
+ M4 = io_lib:fwrite("\tLargest active size counter: ~p.\n",
+ [S]),
+ report("Counter statistics:\n~s", [[M1, M2, M3, M4]]).
+
+
+%% =====================================================================
+%% The main inlining function
+%%
+%% i(E :: coreErlang(),
+%% Ctxt :: value | effect | #app{}
+%% Ren :: renaming(),
+%% Env :: environment(),
+%% S :: state())
+%% -> {E', S'}
+%%
+%% Note: It is expected that the input source code ('E') does not
+%% contain free variables. If it does, there is a risk of accidental
+%% name capture, in case a generated "new" variable name happens to be
+%% the same as the name of a variable that is free further below in the
+%% tree; the algorithm only consults the current environment to check if
+%% a name already exists.
+%%
+%% The renaming maps names of source-code variable and function
+%% variables to new names as necessary to avoid clashes, according to
+%% the "no-shadowing" strategy. The environment maps *residual-code*
+%% variables and function variables to operands and global information.
+%% Separating the renaming from the environment, and using the
+%% residual-code variables instead of the source-code variables as its
+%% domain, improves the behaviour of the algorithm when code needs to be
+%% traversed more than once.
+%%
+%% Note that there is no such thing as a `test' context for expressions
+%% in (Core) Erlang (see `i_case' below for details).
+
+i(E, Ctxt, S) ->
+ i(E, Ctxt, ren__identity(), env__empty(), S).
+
+i(E, Ctxt, Ren, Env, S0) ->
+ %% Count one unit of effort on each pass.
+ S = count_effort(1, S0),
+ case is_data(E) of
+ true ->
+ i_data(E, Ctxt, Ren, Env, S);
+ false ->
+ case type(E) of
+ var ->
+ i_var(E, Ctxt, Ren, Env, S);
+ values ->
+ i_values(E, Ctxt, Ren, Env, S);
+ 'fun' ->
+ i_fun(E, Ctxt, Ren, Env, S);
+ seq ->
+ i_seq(E, Ctxt, Ren, Env, S);
+ 'let' ->
+ i_let(E, Ctxt, Ren, Env, S);
+ letrec ->
+ i_letrec(E, Ctxt, Ren, Env, S);
+ 'case' ->
+ i_case(E, Ctxt, Ren, Env, S);
+ 'receive' ->
+ i_receive(E, Ctxt, Ren, Env, S);
+ apply ->
+ i_apply(E, Ctxt, Ren, Env, S);
+ call ->
+ i_call(E, Ctxt, Ren, Env, S);
+ primop ->
+ i_primop(E, Ren, Env, S);
+ 'try' ->
+ i_try(E, Ctxt, Ren, Env, S);
+ 'catch' ->
+ i_catch(E, Ctxt, Ren, Env, S);
+ binary ->
+ i_binary(E, Ren, Env, S);
+ module ->
+ i_module(E, Ctxt, Ren, Env, S)
+ end
+ end.
+
+i_data(E, Ctxt, Ren, Env, S) ->
+ case is_literal(E) of
+ true ->
+ %% This is the `(const c)' case of the original algorithm:
+ %% literal terms which (regardless of size) do not need to
+ %% be constructed dynamically at runtime - boldly assuming
+ %% that the compiler/runtime system can handle this.
+ case Ctxt of
+ effect ->
+ %% Reduce useless constants to a simple value.
+ {void(), count_size(weight(literal), S)};
+ _ ->
+ %% (In Erlang, we cannot set all non-`false'
+ %% constants to `true' in a `test' context, like we
+ %% could do in Lisp or C, so the above is the only
+ %% special case to be handled here.)
+ {E, count_size(weight(literal), S)}
+ end;
+ false ->
+ %% Data constructors are like to calls to safe built-in
+ %% functions, for which we can "decide to inline"
+ %% immediately; there is no need to create operand
+ %% structures. In `effect' context, we can simply make a
+ %% sequence of the argument expressions, also visited in
+ %% `effect' context. In all other cases, the arguments are
+ %% visited for value.
+ case Ctxt of
+ effect ->
+ %% Note that this will count the sizes of the
+ %% subexpressions, even though some or all of them
+ %% might be discarded by the sequencing afterwards.
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i(E, effect, Ren, Env,
+ S)
+ end,
+ S, data_es(E)),
+ E1 = foldl(fun (E1, E2) -> make_seq(E1, E2) end,
+ void(), Es1),
+ {E1, S1};
+ _ ->
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i(E, value, Ren, Env,
+ S)
+ end,
+ S, data_es(E)),
+ %% The total size/cost is the base cost for a data
+ %% constructor plus the cost for storing each
+ %% element.
+ N = weight(data) + length(Es1) * weight(element),
+ S2 = count_size(N, S1),
+ {update_data(E, data_type(E), Es1), S2}
+ end
+ end.
+
+%% This is the `(ref x)' (variable use) case of the original algorithm.
+%% Note that binding occurrences are always handled in the respective
+%% cases of the binding constructs.
+
+i_var(E, Ctxt, Ren, Env, S) ->
+ case Ctxt of
+ effect ->
+ %% Reduce useless variable references to a simple constant.
+ %% This also avoids useless visiting of bound operands.
+ {void(), count_size(weight(literal), S)};
+ _ ->
+ Name = var_name(E),
+ case env__lookup(ren__map(Name, Ren), Env) of
+ {ok, R} ->
+ case R#ref.opnd of
+ undefined ->
+ %% The variable is not associated with an
+ %% argument expression; just residualize it.
+ residualize_var(R, S);
+ Opnd ->
+ i_var_1(R, Opnd, Ctxt, Env, S)
+ end;
+ error ->
+ %% The variable is unbound. (It has not been
+ %% accidentally captured, however, or it would have
+ %% been in the environment.) We leave it as it is,
+ %% without any warning.
+ {E, count_size(weight(var), S)}
+ end
+ end.
+
+%% This first visits the bound operand and then does copy propagation.
+%% Note that we must first set the "inner-pending" flag, and clear the
+%% flag afterwards.
+
+i_var_1(R, Opnd, Ctxt, Env, S) ->
+ %% If the operand is already "inner-pending", it is residualised.
+ %% (In Lisp/C, if the variable might be assigned to, it should also
+ %% be residualised.)
+ L = Opnd#opnd.loc,
+ case st__test_inner_pending(L, S) of
+ true ->
+ residualize_var(R, S);
+ false ->
+ S1 = st__mark_inner_pending(L, S),
+ case catch {ok, visit(Opnd, S1)} of
+ {ok, {E, S2}} ->
+ %% Note that we pass the current environment and
+ %% context to `copy', but not the current renaming.
+ S3 = st__clear_inner_pending(L, S2),
+ copy(R, Opnd, E, Ctxt, Env, S3);
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ %% If we use destructive update for the
+ %% `inner-pending' flag, we must make sure to clear
+ %% it also if we make a nonlocal return.
+ st__clear_inner_pending(Opnd#opnd.loc, S1),
+ throw(X)
+ end
+ end.
+
+%% A multiple-value aggregate `<e1, ..., en>'. This is very much like a
+%% tuple data constructor `{e1, ..., en}'; cf. `i_data' for details.
+
+i_values(E, Ctxt, Ren, Env, S) ->
+ case values_es(E) of
+ [E1] ->
+ %% Single-value aggregates can be dropped; they are simply
+ %% notation.
+ i(E1, Ctxt, Ren, Env, S);
+ Es ->
+ %% In `effect' context, we can simply make a sequence of the
+ %% argument expressions, also visited in `effect' context.
+ %% In all other cases, the arguments are visited for value.
+ case Ctxt of
+ effect ->
+ {Es1, S1} =
+ mapfoldl(fun (E, S) ->
+ i(E, effect, Ren, Env, S)
+ end,
+ S, Es),
+ E1 = foldl(fun (E1, E2) ->
+ make_seq(E1, E2)
+ end,
+ void(), Es1),
+ {E1, S1}; % drop annotations on E
+ _ ->
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i(E, value, Ren, Env,
+ S)
+ end,
+ S, Es),
+ %% Aggregating values does not write them to memory,
+ %% so we count no extra cost per element.
+ S2 = count_size(weight(values), S1),
+ {update_c_values(E, Es1), S2}
+ end
+ end.
+
+%% A let-expression `let <v1,...,vn> = e0 in e1' is semantically
+%% equivalent to a case-expression `case e0 of <v1,...,vn> when 'true'
+%% -> e1 end'. As a special case, `let <v> = e0 in e1' is also
+%% equivalent to `apply fun (v) -> e0 (e1)'. However, for efficiency,
+%% and in order to allow the handling of `case' clauses to introduce new
+%% let-expressions without entering an infinite rewrite loop, we handle
+%% these directly.
+
+%%% %% Rewriting a `let' to an equivalent expression.
+%%% i_let(E, Ctxt, Ren, Env, S) ->
+%%% case let_vars(E) of
+%%% [V] ->
+%%% E1 = update_c_apply(E, c_fun([V], let_body(E)), [let_arg(E)]),
+%%% i(E1, Ctxt, Ren, Env, S);
+%%% Vs ->
+%%% C = c_clause(Vs, abstract(true), let_body(E)),
+%%% E1 = update_c_case(E, let_arg(E), [C]),
+%%% i(E1, Ctxt, Ren, Env, S)
+%%% end.
+
+i_let(E, Ctxt, Ren, Env, S) ->
+ case let_vars(E) of
+ [V] ->
+ i_let_1(V, E, Ctxt, Ren, Env, S);
+ Vs ->
+ %% Visit the argument expression in `value' context, to
+ %% simplify it as far as possible.
+ {A, S1} = i(let_arg(E), value, Ren, Env, S),
+ case get_components(length(Vs), result(A)) of
+ {true, As} ->
+ %% Note that only the components of the result of
+ %% `A' are passed on; any effects are hoisted.
+ {E1, S2} = i_let_2(Vs, As, E, Ctxt, Ren, Env, S1),
+ {hoist_effects(A, E1), S2};
+ false ->
+ %% We cannot do anything with this `let', since the
+ %% variables cannot be matched against the argument
+ %% components. Just visit the variables for renaming
+ %% and visit the body for value (cf. `i_fun').
+ {_, Ren1, Env1, S2} = bind_locals(Vs, Ren, Env, S1),
+ Vs1 = i_params(Vs, Ren1, Env1),
+ %% The body is always visited for value here.
+ {B, S3} = i(let_body(E), value, Ren1, Env1, S2),
+ S4 = count_size(weight('let'), S3),
+ {update_c_let(E, Vs1, A, B), S4}
+ end
+ end.
+
+%% Single-variable `let' binding.
+
+i_let_1(V, E, Ctxt, Ren, Env, S) ->
+ %% Make an operand structure for the argument expression, create a
+ %% local binding from the parameter to the operand structure, and
+ %% visit the body. Finally create necessary bindings and/or set
+ %% flags.
+ {Opnd, S1} = make_opnd(let_arg(E), Ren, Env, S),
+ {[R], Ren1, Env1, S2} = bind_locals([V], [Opnd], Ren, Env, S1),
+ {E1, S3} = i(let_body(E), Ctxt, Ren1, Env1, S2),
+ i_let_3([R], [Opnd], E1, S3).
+
+%% Multi-variable `let' binding.
+
+i_let_2(Vs, As, E, Ctxt, Ren, Env, S) ->
+ %% Make operand structures for the argument components. Note that
+ %% since the argument has already been visited at this point, we use
+ %% the identity renaming for the operands.
+ {Opnds, S1} = mapfoldl(fun (E, S) ->
+ make_opnd(E, ren__identity(), Env, S)
+ end,
+ S, As),
+ %% Create local bindings from the parameters to their respective
+ %% operand structures, and visit the body.
+ {Rs, Ren1, Env1, S2} = bind_locals(Vs, Opnds, Ren, Env, S1),
+ {E1, S3} = i(let_body(E), Ctxt, Ren1, Env1, S2),
+ i_let_3(Rs, Opnds, E1, S3).
+
+i_let_3(Rs, Opnds, E, S) ->
+ %% Create necessary bindings and/or set flags.
+ {E1, S1} = make_let_bindings(Rs, E, S),
+
+ %% We must also create evaluation for effect, for any unused
+ %% operands, as after an application expression.
+ residualize_operands(Opnds, E1, S1).
+
+%% A sequence `do e1 e2', written `(seq e1 e2)' in the original
+%% algorithm, where `e1' is evaluated for effect only (since its value
+%% is not used), and `e2' yields the final value. Note that we use
+%% `make_seq' to recompose the sequence after visiting the parts.
+
+i_seq(E, Ctxt, Ren, Env, S) ->
+ {E1, S1} = i(seq_arg(E), effect, Ren, Env, S),
+ {E2, S2} = i(seq_body(E), Ctxt, Ren, Env, S1),
+ %% A sequence has no cost in itself.
+ {make_seq(E1, E2), S2}.
+
+
+%% The `case' switch of Core Erlang is rather different from the boolean
+%% `(if e1 e2 e3)' case of the original algorithm, but the central idea
+%% is the same: if, given the simplified switch expression (which is
+%% visited in `value' context - a boolean `test' context would not be
+%% generally useful), there is a clause which could definitely be
+%% selected, such that no clause before it can possibly be selected,
+%% then we can eliminate all other clauses. (And even if this is not the
+%% case, some clauses can often be eliminated.) Furthermore, if a clause
+%% can be selected, we can replace the case-expression (including the
+%% switch expression) with the body of the clause and a set of zero or
+%% more let-bindings of subexpressions of the switch expression. (In the
+%% simplest case, the switch expression is evaluated only for effect.)
+
+i_case(E, Ctxt, Ren, Env, S) ->
+ %% First visit the switch expression in `value' context, to simplify
+ %% it as far as possible. Note that only the result part is passed
+ %% on to the clause matching below; any effects are hoisted.
+ {A, S1} = i(case_arg(E), value, Ren, Env, S),
+ A1 = result(A),
+
+ %% Propagating an application context into the branches could cause
+ %% the arguments of the application to be evaluated *after* the
+ %% switch expression, but *before* the body of the selected clause.
+ %% Such interleaving is not allowed in general, and it does not seem
+ %% worthwile to make a more powerful transformation here. Therefore,
+ %% the clause bodies are conservatively visited for value if the
+ %% context is `application'.
+ Ctxt1 = safe_context(Ctxt),
+ {E1, S2} = case get_components(case_arity(E), A1) of
+ {true, As} ->
+ i_case_1(As, E, Ctxt1, Ren, Env, S1);
+ false ->
+ i_case_1([], E, Ctxt1, Ren, Env, S1)
+ end,
+ {hoist_effects(A, E1), S2}.
+
+i_case_1(As, E, Ctxt, Ren, Env, S) ->
+ case i_clauses(As, case_clauses(E), Ctxt, Ren, Env, S) of
+ {false, {As1, Vs, Env1, Cs}, S1} ->
+ %% We still have a list of clauses. Sanity check:
+ if Cs == [] ->
+ report_warning("empty list of clauses "
+ "in residual program!.\n");
+ true ->
+ ok
+ end,
+ {A, S2} = i(c_values(As1), value, ren__identity(), Env1,
+ S1),
+ {E1, S3} = i_case_2(Cs, A, E, S2),
+ i_case_3(Vs, Env1, E1, S3);
+ {true, {_, Vs, Env1, [C]}, S1} ->
+ %% A single clause was selected; we just take the body.
+ i_case_3(Vs, Env1, clause_body(C), S1)
+ end.
+
+%% Check if all clause bodies are actually equivalent expressions that
+%% do not depent on pattern variables (this sometimes occurs as a
+%% consequence of inlining, e.g., all branches might yield 'true'), and
+%% if so, replace the `case' with a sequence, first evaluating the
+%% clause selection for effect, then evaluating one of the clause bodies
+%% for its value. (Unless the switch contains a catch-all clause, the
+%% clause selection must be evaluated for effect, since there is no
+%% guarantee that any of the clauses will actually match. Assuming that
+%% some clause always matches could make an undefined program produce a
+%% value.) This makes the final size less than what was accounted for
+%% when visiting the clauses, but currently we don't try to adjust for
+%% this.
+
+i_case_2(Cs, A, E, S) ->
+ case equivalent_clauses(Cs) of
+ false ->
+ %% Count the base sizes for the remaining clauses; pattern
+ %% and guard sizes are already counted.
+ N = weight('case') + weight(clause) * length(Cs),
+ S1 = count_size(N, S),
+ {update_c_case(E, A, Cs), S1};
+ true ->
+ case cerl_clauses:any_catchall(Cs) of
+ true ->
+ %% We know that some clause must be selected, so we
+ %% can drop all the testing as well.
+ E1 = make_seq(A, clause_body(hd(Cs))),
+ {E1, S};
+ false ->
+ %% The clause selection must be performed for
+ %% effect.
+ E1 = update_c_case(E, A,
+ set_clause_bodies(Cs, void())),
+ {make_seq(E1, clause_body(hd(Cs))), S}
+ end
+ end.
+
+i_case_3(Vs, Env, E, S) ->
+ %% For the variables bound to the switch expression subexpressions,
+ %% make let bindings or create evaluation for effect.
+ Rs = [env__get(var_name(V), Env) || V <- Vs],
+ {E1, S1} = make_let_bindings(Rs, E, S),
+ Opnds = [R#ref.opnd || R <- Rs],
+ residualize_operands(Opnds, E1, S1).
+
+%% This function takes a sequence of switch expressions `Es' (which can
+%% be the empty list if these are unknown) and a list `Cs' of clauses,
+%% and returns `{Match, {As, Vs, Env1, Cs1}, S1}' where `As' is a list
+%% of residual switch expressions, `Vs' the list of variables used in
+%% the templates, `Env1' the environment for the templates, and `Cs1'
+%% the list of residual clauses. `Match' is `true' if some clause could
+%% be shown to definitely match (in this case, `Cs1' contains exactly
+%% one element), and `false' otherwise. `S1' is the new state. The given
+%% `Ctxt' is the context to be used for visiting the body of clauses.
+%%
+%% Visiting a clause basically amounts to extending the environment for
+%% all variables in the pattern, as for a `fun' (cf. `i_fun'),
+%% propagating match information if possible, and visiting the guard and
+%% body in the new environment.
+%%
+%% To make it cheaper to do handle a set of clauses, and to avoid
+%% unnecessarily exceeding the size limit, we avoid visiting the bodies
+%% of clauses which are subsequently removed, by dividing the visiting
+%% of a clause into two stages: first construct the environment(s) and
+%% visit the pattern (for renaming) and the guard (for value), then
+%% reduce the switch as much as possible, and lastly visit the body.
+
+i_clauses(Cs, Ctxt, Ren, Env, S) ->
+ i_clauses([], Cs, Ctxt, Ren, Env, S).
+
+i_clauses(Es, Cs, Ctxt, Ren, Env, S) ->
+ %% Create templates for the switch expressions.
+ {Ts, {Vs, Env0}} = mapfoldl(fun (E, {Vs, Env}) ->
+ {T, Vs1, Env1} =
+ make_template(E, Env),
+ {T, {Vs1 ++ Vs, Env1}}
+ end,
+ {[], Env}, Es),
+
+ %% Make operand structures for the switch subexpression templates
+ %% (found in `Env0') and add proper ref-structure bindings to the
+ %% environment. Since the subexpressions in general can be
+ %% interdependent (Vs is in reverse-dependency order), the
+ %% environment (and renaming) must be created incrementally. Note
+ %% that since the switch expressions have been visited already, the
+ %% identity renaming is used for the operands.
+ Vs1 = lists:reverse(Vs),
+ {Ren1, Env1, S1} =
+ foldl(fun (V, {Ren, Env, S}) ->
+ E = env__get(var_name(V), Env0),
+ {Opnd, S_1} = make_opnd(E, ren__identity(), Env,
+ S),
+ {_, Ren1, Env1, S_2} = bind_locals([V], [Opnd],
+ Ren, Env, S_1),
+ {Ren1, Env1, S_2}
+ end,
+ {Ren, Env, S}, Vs1),
+
+ %% First we visit the head of each individual clause, renaming
+ %% pattern variables, inserting let-bindings in the guard and body,
+ %% and visiting the guard. The information used for visiting the
+ %% clause body will be prefixed to the clause annotations.
+ {Cs1, S2} = mapfoldl(fun (C, S) ->
+ i_clause_head(C, Ts, Ren1, Env1, S)
+ end,
+ S1, Cs),
+
+ %% Now that the clause guards have been reduced as far as possible,
+ %% we can attempt to reduce the clauses.
+ As = [hd(get_ann(T)) || T <- Ts],
+ case cerl_clauses:reduce(Cs1, Ts) of
+ {false, Cs2} ->
+ %% We still have one or more clauses (with associated
+ %% extended environments). Their bodies have not yet been
+ %% visited, so we do that (in the respective safe
+ %% environments, adding the sizes of the visited heads to
+ %% the current size counter) and return the final list of
+ %% clauses.
+ {Cs3, S3} = mapfoldl(
+ fun (C, S) ->
+ i_clause_body(C, Ctxt, S)
+ end,
+ S2, Cs2),
+ {false, {As, Vs1, Env1, Cs3}, S3};
+ {true, {C, _}} ->
+ %% A clause C could be selected (the bindings have already
+ %% been added to the guard/body). Note that since the clause
+ %% head will probably be discarded, its size is not counted.
+ {C1, Ren2, Env2, _} = get_clause_extras(C),
+ {B, S3} = i(clause_body(C), Ctxt, Ren2, Env2, S2),
+ C2 = update_c_clause(C1, clause_pats(C1), clause_guard(C1), B),
+ {true, {As, Vs1, Env1, [C2]}, S3}
+ end.
+
+%% This visits the head of a clause, renames pattern variables, inserts
+%% let-bindings in the guard and body, and does inlining on the guard
+%% expression. Returns a list of pairs `{NewClause, Data}', where `Data'
+%% is `{Renaming, Environment, Size}' used for visiting the body of the
+%% new clause.
+
+i_clause_head(C, Ts, Ren, Env, S) ->
+ %% Match the templates against the (non-renamed) patterns to get the
+ %% available information about matching subexpressions. We don't
+ %% care at this point whether an exact match/nomatch is detected.
+ Ps = clause_pats(C),
+ Bs = case cerl_clauses:match_list(Ps, Ts) of
+ {_, Bs1} -> Bs1;
+ none -> []
+ end,
+
+ %% The patterns must be visited for renaming; cf. `i_pattern'. We
+ %% use a passive size counter for visiting the patterns and the
+ %% guard (cf. `visit'), because we do not know at this stage whether
+ %% the clause will be kept or not; the final value of the counter is
+ %% included in the returned value below.
+ {_, Ren1, Env1, S1} = bind_locals(clause_vars(C), Ren, Env, S),
+ S2 = new_passive_size(get_size_limit(S1), S1),
+ {Ps1, S3} = mapfoldl(fun (P, S) ->
+ i_pattern(P, Ren1, Env1, Ren, Env, S)
+ end,
+ S2, Ps),
+
+ %% Rewrite guard and body and visit the guard for value. Discard the
+ %% latter size count if the guard turns out to be a constant.
+ G = add_match_bindings(Bs, clause_guard(C)),
+ B = add_match_bindings(Bs, clause_body(C)),
+ {G1, S4} = i(G, value, Ren1, Env1, S3),
+ S5 = case is_literal(G1) of
+ true ->
+ revert_size(S3, S4);
+ false ->
+ S4
+ end,
+
+ %% Revert to the size counter we had on entry to this function. The
+ %% environment and renaming, together with the size of the clause
+ %% head, are prefixed to the annotations for later use.
+ Size = get_size_value(S5),
+ C1 = update_c_clause(C, Ps1, G1, B),
+ {set_clause_extras(C1, Ren1, Env1, Size), revert_size(S, S5)}.
+
+add_match_bindings(Bs, E) ->
+ %% Don't waste time if the variables definitely cannot be used.
+ %% (Most guards are simply `true'.)
+ case is_literal(E) of
+ true ->
+ E;
+ false ->
+ Vs = [V || {V, E} <- Bs, E /= any],
+ Es = [hd(get_ann(E)) || {_V, E} <- Bs, E /= any],
+ c_let(Vs, c_values(Es), E)
+ end.
+
+i_clause_body(C0, Ctxt, S) ->
+ {C, Ren, Env, Size} = get_clause_extras(C0),
+ S1 = count_size(Size, S),
+ {B, S2} = i(clause_body(C), Ctxt, Ren, Env, S1),
+ C1 = update_c_clause(C, clause_pats(C), clause_guard(C), B),
+ {C1, S2}.
+
+get_clause_extras(C) ->
+ [{Ren, Env, Size} | As] = get_ann(C),
+ {set_ann(C, As), Ren, Env, Size}.
+
+set_clause_extras(C, Ren, Env, Size) ->
+ As = [{Ren, Env, Size} | get_ann(C)],
+ set_ann(C, As).
+
+%% This is the `(lambda x e)' case of the original algorithm. A
+%% `fun' is like a lambda expression, but with a varying number of
+%% parameters; possibly zero.
+
+i_fun(E, Ctxt, Ren, Env, S) ->
+ case Ctxt of
+ effect ->
+ %% Reduce useless `fun' expressions to a simple constant;
+ %% visiting the body would be a waste of time, and could
+ %% needlessly mark variables as referenced.
+ {void(), count_size(weight(literal), S)};
+ value ->
+ %% Note that the variables are visited as patterns.
+ Vs = fun_vars(E),
+ {_, Ren1, Env1, S1} = bind_locals(Vs, Ren, Env, S),
+ Vs1 = i_params(Vs, Ren1, Env1),
+
+ %% The body is always visited for value.
+ {B, S2} = i(fun_body(E), value, Ren1, Env1, S1),
+
+ %% We don't bother to include the exact number of free
+ %% variables in the cost for creating a fun-value.
+ S3 = count_size(weight('fun'), S2),
+
+ %% Inlining might have duplicated code, so we must remove
+ %% any 'id'-annotations from the original fun-expression.
+ %% (This forces a later stage to invent new id:s.) This is
+ %% necessary as long as fun:s may still need to be
+ %% identified the old way. Function variables that are not
+ %% in application context also have such annotations, but
+ %% the inlining will currently lose all annotations on
+ %% variable references (I think), so that's not a problem.
+ {set_ann(c_fun(Vs1, B), kill_id_anns(get_ann(E))), S3};
+ #app{} ->
+ %% An application of a fun-expression (in the source code)
+ %% is handled by going directly to `inline'; this is never
+ %% residualised, and we don't set up new counters here. Note
+ %% that inlining of copy-propagated fun-expressions is done
+ %% in `copy'; not here.
+ inline(E, Ctxt, Ren, Env, S)
+ end.
+
+%% A `letrec' requires a circular environment, but is otherwise like a
+%% `let', i.e. like a direct lambda application. Note that only
+%% fun-expressions (lambda abstractions) may occur in the right-hand
+%% side of each definition.
+
+i_letrec(E, Ctxt, Ren, Env, S) ->
+ %% Note that we pass an empty list for the auto-referenced
+ %% (exported) functions here.
+ {Es, B, _, S1} = i_letrec(letrec_defs(E), letrec_body(E), [], Ctxt,
+ Ren, Env, S),
+
+ %% If no bindings remain, only the body is returned.
+ case Es of
+ [] ->
+ {B, S1}; % drop annotations on E
+ _ ->
+ S2 = count_size(weight(letrec), S1),
+ {update_c_letrec(E, Es, B), S2}
+ end.
+
+%% The major part of this is shared by letrec-expressions and module
+%% definitions alike.
+
+i_letrec(Es, B, Xs, Ctxt, Ren, Env, S) ->
+ %% First, we create operands with dummy renamings and environments,
+ %% and with fresh store locations for cached expressions and operand
+ %% info.
+ {Opnds, S1} = mapfoldl(fun ({_, E}, S) ->
+ make_opnd(E, undefined, undefined, S)
+ end,
+ S, Es),
+
+ %% Then we make recursive bindings for the definitions.
+ {Rs, Ren1, Env1, S2} = bind_recursive([F || {F, _} <- Es],
+ Opnds, Ren, Env, S1),
+
+ %% For the function variables listed in Xs (none for a
+ %% letrec-expression), we must make sure that the corresponding
+ %% operand expressions are visited and that the definitions are
+ %% marked as referenced; we also need to return the possibly renamed
+ %% function variables.
+ {Xs1, S3} =
+ mapfoldl(
+ fun (X, S) ->
+ Name = ren__map(var_name(X), Ren1),
+ case env__lookup(Name, Env1) of
+ {ok, R} ->
+ S_1 = i_letrec_export(R, S),
+ {ref_to_var(R), S_1};
+ error ->
+ %% We just skip any exports that are not
+ %% actually defined here, and generate a
+ %% warning message.
+ {N, A} = var_name(X),
+ report_warning("export `~w'/~w "
+ "not defined.\n", [N, A]),
+ {X, S}
+ end
+ end,
+ S2, Xs),
+
+ %% At last, we can then visit the body.
+ {B1, S4} = i(B, Ctxt, Ren1, Env1, S3),
+
+ %% Finally, we create new letrec-bindings for any and all
+ %% residualised definitions. All referenced functions should have
+ %% been visited; the call to `visit' below is expected to retreive a
+ %% cached expression.
+ Rs1 = keep_referenced(Rs, S4),
+ {Es1, S5} = mapfoldl(fun (R, S) ->
+ {E_1, S_1} = visit(R#ref.opnd, S),
+ {{ref_to_var(R), E_1}, S_1}
+ end,
+ S4, Rs1),
+ {Es1, B1, Xs1, S5}.
+
+%% This visits the operand for a function definition exported by a
+%% `letrec' (which is really a `module' module definition, since normal
+%% letrecs have no export declarations). Only the updated state is
+%% returned. We must handle the "inner-pending" flag when doing this;
+%% cf. `i_var'.
+
+i_letrec_export(R, S) ->
+ Opnd = R#ref.opnd,
+ S1 = st__mark_inner_pending(Opnd#opnd.loc, S),
+ {_, S2} = visit(Opnd, S1),
+ {_, S3} = residualize_var(R, st__clear_inner_pending(Opnd#opnd.loc,
+ S2)),
+ S3.
+
+%% This is the `(call e1 e2)' case of the original algorithm. The only
+%% difference is that we must handle multiple (or no) operand
+%% expressions.
+
+i_apply(E, Ctxt, Ren, Env, S) ->
+ {Opnds, S1} = mapfoldl(fun (E, S) ->
+ make_opnd(E, Ren, Env, S)
+ end,
+ S, apply_args(E)),
+
+ %% Allocate a new app-context location and set up an application
+ %% context structure containing the surrounding context.
+ {L, S2} = st__new_app_loc(S1),
+ Ctxt1 = #app{opnds = Opnds, ctxt = Ctxt, loc = L},
+
+ %% Visit the operator expression in the new call context.
+ {E1, S3} = i(apply_op(E), Ctxt1, Ren, Env, S2),
+
+ %% Check the "inlined" flag to find out what to do next. (The store
+ %% location could be recycled after the flag has been tested, but
+ %% there is no real advantage to that, because in practice, only
+ %% 4-5% of all created store locations will ever be reused, while
+ %% there will be a noticable overhead for managing the free list.)
+ case st__get_app_inlined(L, S3) of
+ true ->
+ %% The application was inlined, so we have the final
+ %% expression in `E1'. We just have to handle any operands
+ %% that need to be residualized for effect only (i.e., those
+ %% the values of which are not used).
+ residualize_operands(Opnds, E1, S3);
+ false ->
+ %% Otherwise, `E1' is the residual operator expression. We
+ %% make sure all operands are visited, and rebuild the
+ %% application.
+ {Es, S4} = mapfoldl(fun (Opnd, S) ->
+ visit_and_count_size(Opnd, S)
+ end,
+ S3, Opnds),
+ N = apply_size(length(Es)),
+ {update_c_apply(E, E1, Es), count_size(N, S4)}
+ end.
+
+apply_size(A) ->
+ weight(apply) + weight(argument) * A.
+
+%% Since it is not the task of this transformation to handle
+%% cross-module inlining, all inter-module calls are handled by visiting
+%% the components (the module and function name, and the arguments of
+%% the call) for value. In `effect' context, if the function itself is
+%% known to be completely effect free, the call can be discarded and the
+%% arguments evaluated for effect. Otherwise, if all the visited
+%% arguments are to constants, and the function is known to be safe to
+%% execute at compile time, then we try to evaluate the call. If
+%% evaluation completes normally, the call is replaced by the result;
+%% otherwise the call is residualised.
+
+i_call(E, Ctxt, Ren, Env, S) ->
+ {M, S1} = i(call_module(E), value, Ren, Env, S),
+ {F, S2} = i(call_name(E), value, Ren, Env, S1),
+ As = call_args(E),
+ Arity = length(As),
+
+ %% Check if the name of the called function is static. If so,
+ %% discard the size counts performed above, since the values will
+ %% not cause any runtime cost.
+ Static = is_c_atom(M) and is_c_atom(F),
+ S3 = case Static of
+ true ->
+ revert_size(S, S2);
+ false ->
+ S2
+ end,
+ case Ctxt of
+ effect when Static == true ->
+ case is_safe_call(atom_val(M), atom_val(F), Arity) of
+ true ->
+ %% The result will not be used, and the call is
+ %% effect free, so we create a multiple-value
+ %% aggregate containing the (not yet visited)
+ %% arguments and process that instead.
+ i(c_values(As), effect, Ren, Env, S3);
+ false ->
+ %% We are not allowed to simply discard the call,
+ %% but we can try to evaluate it.
+ i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env,
+ S3)
+ end;
+ _ ->
+ i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env, S3)
+ end.
+
+i_call_1(Static, M, F, Arity, As, E, Ctxt, Ren, Env, S) ->
+ %% Visit the arguments for value.
+ {As1, S1} = mapfoldl(fun (X, A) -> i(X, value, Ren, Env, A) end,
+ S, As),
+ case Static of
+ true ->
+ case erl_bifs:is_pure(atom_val(M), atom_val(F), Arity) of
+ true ->
+ %% It is allowed to evaluate this at compile time.
+ case all_static(As1) of
+ true ->
+ i_call_3(M, F, As1, E, Ctxt, Env, S1);
+ false ->
+ %% See if the call can be rewritten instead.
+ i_call_4(M, F, As1, E, Ctxt, Env, S1)
+ end;
+ false ->
+ i_call_2(M, F, As1, E, S1)
+ end;
+ false ->
+ i_call_2(M, F, As1, E, S1)
+ end.
+
+%% Residualise the call.
+
+i_call_2(M, F, As, E, S) ->
+ N = weight(call) + weight(argument) * length(As),
+ {update_c_call(E, M, F, As), count_size(N, S)}.
+
+%% Attempt to evaluate the call to yield a literal; if that fails, try
+%% to rewrite the expression.
+
+i_call_3(M, F, As, E, Ctxt, Env, S) ->
+ %% Note that we extract the results of argument expessions here; the
+ %% expressions could still be sequences with side effects.
+ Vs = [concrete(result(A)) || A <- As],
+ case catch {ok, apply(atom_val(M), atom_val(F), Vs)} of
+ {ok, V} ->
+ %% Evaluation completed normally - try to turn the result
+ %% back into a syntax tree (representing a literal).
+ case is_literal_term(V) of
+ true ->
+ %% Make a sequence of the arguments (as a
+ %% multiple-value aggregate) and the final value.
+ S1 = count_size(weight(values), S),
+ S2 = count_size(weight(literal), S1),
+ {make_seq(c_values(As), abstract(V)), S2};
+ false ->
+ %% The result could not be represented as a literal.
+ i_call_4(M, F, As, E, Ctxt, Env, S)
+ end;
+ _ ->
+ %% The evaluation attempt did not complete normally.
+ i_call_4(M, F, As, E, Ctxt, Env, S)
+ end.
+
+%% Rewrite the expression, if possible, otherwise residualise it.
+
+i_call_4(M, F, As, E, Ctxt, Env, S) ->
+ case reduce_bif_call(atom_val(M), atom_val(F), As, Env) of
+ false ->
+ %% Nothing more to be done - residualise the call.
+ i_call_2(M, F, As, E, S);
+ {true, E1} ->
+ %% We revisit the result, because the rewriting might have
+ %% opened possibilities for further inlining. Since the
+ %% parts have already been visited once, we use the identity
+ %% renaming here.
+ i(E1, Ctxt, ren__identity(), Env, S)
+ end.
+
+%% For now, we assume that primops cannot be evaluated at compile time,
+%% probably being too special. Also, we have no knowledge about their
+%% side effects.
+
+i_primop(E, Ren, Env, S) ->
+ %% Visit the arguments for value.
+ {As, S1} = mapfoldl(fun (E, S) ->
+ i(E, value, Ren, Env, S)
+ end,
+ S, primop_args(E)),
+ N = weight(primop) + weight(argument) * length(As),
+ {update_c_primop(E, primop_name(E), As), count_size(N, S1)}.
+
+%% This is like having an expression with an extra fun-expression
+%% attached for "exceptional cases"; actually, there are exactly two
+%% parameter variables for the body, but they are easiest handled as if
+%% their number might vary, just as for a `fun'.
+
+i_try(E, Ctxt, Ren, Env, S) ->
+ %% The argument expression is evaluated in `value' context, and the
+ %% surrounding context is propagated into both branches. We do not
+ %% try to recognize cases when the protected expression will
+ %% actually raise an exception. Note that the variables are visited
+ %% as patterns.
+ {A, S1} = i(try_arg(E), value, Ren, Env, S),
+ Vs = try_vars(E),
+ {_, Ren1, Env1, S2} = bind_locals(Vs, Ren, Env, S1),
+ Vs1 = i_params(Vs, Ren1, Env1),
+ {B, S3} = i(try_body(E), Ctxt, Ren1, Env1, S2),
+ case is_safe(A) of
+ true ->
+ %% The `try' wrapper can be dropped in this case. Since the
+ %% expressions have been visited already, the identity
+ %% renaming is used when we revisit the new let-expression.
+ i(c_let(Vs1, A, B), Ctxt, ren__identity(), Env, S3);
+ false ->
+ Evs = try_evars(E),
+ {_, Ren2, Env2, S4} = bind_locals(Evs, Ren, Env, S3),
+ Evs1 = i_params(Evs, Ren2, Env2),
+ {H, S5} = i(try_handler(E), Ctxt, Ren2, Env2, S4),
+ S6 = count_size(weight('try'), S5),
+ {update_c_try(E, A, Vs1, B, Evs1, H), S6}
+ end.
+
+%% A special case of try-expressions:
+
+i_catch(E, Ctxt, Ren, Env, S) ->
+ %% We cannot propagate application contexts into the catch.
+ {E1, S1} = i(catch_body(E), safe_context(Ctxt), Ren, Env, S),
+ case is_safe(E1) of
+ true ->
+ %% The `catch' wrapper can be dropped in this case.
+ {E1, S1};
+ false ->
+ S2 = count_size(weight('catch'), S1),
+ {update_c_catch(E, E1), S2}
+ end.
+
+%% A receive-expression is very much like a case-expression, with the
+%% difference that we do not have access to a switch expression, since
+%% the value being switched on is taken from the mailbox. The fact that
+%% the receive-expression may iterate over an arbitrary number of
+%% messages is not of interest to us. All we can do here is to visit its
+%% subexpressions, and possibly eliminate definitely unselectable
+%% clauses.
+
+i_receive(E, Ctxt, Ren, Env, S) ->
+ %% We first visit the expiry expression (for value) and the expiry
+ %% body (in the surrounding context).
+ {T, S1} = i(receive_timeout(E), value, Ren, Env, S),
+ {B, S2} = i(receive_action(E), Ctxt, Ren, Env, S1),
+
+ %% Then we visit the clauses. Note that application contexts may not
+ %% in general be propagated into the branches (and the expiry body),
+ %% because the execution of the `receive' may remove a message from
+ %% the mailbox as a side effect; the situation is thus analogous to
+ %% that in a `case' expression.
+ Ctxt1 = safe_context(Ctxt),
+ case i_clauses(receive_clauses(E), Ctxt1, Ren, Env, S2) of
+ {false, {[], _, _, Cs}, S3} ->
+ %% We still have a list of clauses. If the list is empty,
+ %% and the expiry expression is the integer zero, the
+ %% expression reduces to the expiry body.
+ if Cs == [] ->
+ case is_c_int(T) andalso (int_val(T) == 0) of
+ true ->
+ {B, S3};
+ false ->
+ i_receive_1(E, Cs, T, B, S3)
+ end;
+ true ->
+ i_receive_1(E, Cs, T, B, S3)
+ end;
+ {true, {_, _, _, Cs}, S3} ->
+ %% Cs is a single clause that will always be matched (if a
+ %% message exists), but we must keep the `receive' statement
+ %% in order to fetch the message from the mailbox.
+ i_receive_1(E, Cs, T, B, S3)
+ end.
+
+i_receive_1(E, Cs, T, B, S) ->
+ %% Here, we just add the base sizes for the receive-expression
+ %% itself and for each remaining clause; cf. `case'.
+ N = weight('receive') + weight(clause) * length(Cs),
+ {update_c_receive(E, Cs, T, B), count_size(N, S)}.
+
+%% A module definition is like a `letrec', with some add-ons (export and
+%% attribute declarations) but without an explicit body. Actually, the
+%% exporting of function names has the same effect as if there was a
+%% body consisting of the list of references to the exported functions.
+%% Thus, the exported functions are exactly those which can be
+%% referenced from outside the module.
+
+i_module(E, Ctxt, Ren, Env, S) ->
+ %% Cf. `i_letrec'. Note that we pass a dummy constant value for the
+ %% "body" parameter.
+ {Es, _, Xs1, S1} = i_letrec(module_defs(E), void(),
+ module_exports(E), Ctxt, Ren, Env, S),
+ %% Sanity check:
+ case Es of
+ [] ->
+ report_warning("no function definitions remaining "
+ "in module `~s'.\n",
+ [atom_name(module_name(E))]);
+ _ ->
+ ok
+ end,
+ E1 = update_c_module(E, module_name(E), Xs1, module_attrs(E), Es),
+ {E1, count_size(weight(module), S1)}.
+
+%% Binary-syntax expressions are too complicated to do anything
+%% interesting with here - that is beyond the scope of this program;
+%% also, their construction could have side effects, so even in effect
+%% context we can't remove them. (We don't bother to identify cases of
+%% "safe" unused binaries which could be removed.)
+
+i_binary(E, Ren, Env, S) ->
+ %% Visit the segments for value.
+ {Es, S1} = mapfoldl(fun (E, S) ->
+ i_bitstr(E, Ren, Env, S)
+ end,
+ S, binary_segments(E)),
+ S2 = count_size(weight(binary), S1),
+ {update_c_binary(E, Es), S2}.
+
+i_bitstr(E, Ren, Env, S) ->
+ %% It is not necessary to visit the Unit, Type and Flags fields,
+ %% since these are always literals.
+ {Val, S1} = i(bitstr_val(E), value, Ren, Env, S),
+ {Size, S2} = i(bitstr_size(E), value, Ren, Env, S1),
+ Unit = bitstr_unit(E),
+ Type = bitstr_type(E),
+ Flags = bitstr_flags(E),
+ S3 = count_size(weight(bitstr), S2),
+ {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
+
+%% This is a simplified version of `i_pattern', for lists of parameter
+%% variables only. It does not modify the state.
+
+i_params([V | Vs], Ren, Env) ->
+ Name = ren__map(var_name(V), Ren),
+ case env__lookup(Name, Env) of
+ {ok, R} ->
+ [ref_to_var(R) | i_params(Vs, Ren, Env)];
+ error ->
+ report_internal_error("variable `~w' not bound "
+ "in pattern.\n", [Name]),
+ exit(error)
+ end;
+i_params([], _, _) ->
+ [].
+
+%% For ordinary patterns, we just visit to rename variables and count
+%% the size/cost. All occurring binding instances of variables should
+%% already have been added to the renaming and environment; however, to
+%% handle the size expressions of binary-syntax patterns, we must pass
+%% the renaming and environment of the containing expression
+
+i_pattern(E, Ren, Env, Ren0, Env0, S) ->
+ case type(E) of
+ var ->
+ %% Count no size.
+ Name = ren__map(var_name(E), Ren),
+ case env__lookup(Name, Env) of
+ {ok, R} ->
+ {ref_to_var(R), S};
+ error ->
+ report_internal_error("variable `~w' not bound "
+ "in pattern.\n", [Name]),
+ exit(error)
+ end;
+ alias ->
+ %% Count no size.
+ V = alias_var(E),
+ Name = ren__map(var_name(V), Ren),
+ case env__lookup(Name, Env) of
+ {ok, R} ->
+ %% Visit the subpattern and recompose.
+ V1 = ref_to_var(R),
+ {P, S1} = i_pattern(alias_pat(E), Ren, Env, Ren0,
+ Env0, S),
+ {update_c_alias(E, V1, P), S1};
+ error ->
+ report_internal_error("variable `~w' not bound "
+ "in pattern.\n", [Name]),
+ exit(error)
+ end;
+ binary ->
+ {Es, S1} = mapfoldl(fun (E, S) ->
+ i_bitstr_pattern(E, Ren, Env,
+ Ren0, Env0, S)
+ end,
+ S, binary_segments(E)),
+ S2 = count_size(weight(binary), S1),
+ {update_c_binary(E, Es), S2};
+ _ ->
+ case is_literal(E) of
+ true ->
+ {E, count_size(weight(literal), S)};
+ false ->
+ {Es1, S1} = mapfoldl(fun (E, S) ->
+ i_pattern(E, Ren, Env,
+ Ren0, Env0,
+ S)
+ end,
+ S, data_es(E)),
+ %% We assume that in general, the elements of the
+ %% constructor will all be fetched.
+ N = weight(data) + length(Es1) * weight(element),
+ S2 = count_size(N, S1),
+ {update_data(E, data_type(E), Es1), S2}
+ end
+ end.
+
+i_bitstr_pattern(E, Ren, Env, Ren0, Env0, S) ->
+ %% It is not necessary to visit the Unit, Type and Flags fields,
+ %% since these are always literals. The Value field is a limited
+ %% pattern - either a literal or an unbound variable. The Size field
+ %% is a limited expression - either a literal or a variable bound in
+ %% the environment of the containing expression.
+ {Val, S1} = i_pattern(bitstr_val(E), Ren, Env, Ren0, Env0, S),
+ {Size, S2} = i(bitstr_size(E), value, Ren0, Env0, S1),
+ Unit = bitstr_unit(E),
+ Type = bitstr_type(E),
+ Flags = bitstr_flags(E),
+ S3 = count_size(weight(bitstr), S2),
+ {update_c_bitstr(E, Val, Size, Unit, Type, Flags), S3}.
+
+
+%% ---------------------------------------------------------------------
+%% Other central inlining functions
+
+%% It is assumed here that `E' is a fun-expression and the context is an
+%% app-structure. If the inlining might be aborted for some reason, a
+%% corresponding catch should have been set up before entering `inline'.
+%%
+%% Note: if the inlined body is a lambda abstraction, and the
+%% surrounding context of the app-context is also an app-context, the
+%% `inlined' flag of the outermost context will be set before that of
+%% the inner context is set. E.g.: `let F = fun (X) -> fun (Y) -> E in
+%% apply apply F(A)(B)' will propagate the body of F, which is a lambda
+%% abstraction, into the outer application context, which will be
+%% inlined to produce expression `E', and the flag of the outer context
+%% will be set. Upon return, the flag of the inner context will also be
+%% set. However, the flags are then tested in innermost-first order.
+%% Thus, if some inlining attempt is aborted, the `inlined' flags of any
+%% nested app-contexts must be cleared.
+%%
+%% This implementation does nothing to handle inlining of calls to
+%% recursive functions in a smart way. This means that as long as the
+%% size and effort counters do not prevent it, the function body will be
+%% inlined (i.e., the first iteration will be unrolled), and the
+%% recursive calls will be residualized.
+
+inline(E, #app{opnds = Opnds, ctxt = Ctxt, loc = L}, Ren, Env, S) ->
+ %% Check that the arities match:
+ Vs = fun_vars(E),
+ if length(Opnds) /= length(Vs) ->
+ report_error("function called with wrong number "
+ "of arguments!\n"),
+ %% TODO: should really just residualise the call...
+ exit(error);
+ true ->
+ ok
+ end,
+ %% Create local bindings for the parameters to their respective
+ %% operand structures from the app-structure, and visit the body in
+ %% the context saved in the structure.
+ {Rs, Ren1, Env1, S1} = bind_locals(Vs, Opnds, Ren, Env, S),
+ {E1, S2} = i(fun_body(E), Ctxt, Ren1, Env1, S1),
+
+ %% Create necessary bindings and/or set flags.
+ {E2, S3} = make_let_bindings(Rs, E1, S2),
+
+ %% Lastly, flag the application as inlined, since the inlining
+ %% attempt was not aborted before we reached this point.
+ {E2, st__set_app_inlined(L, S3)}.
+
+%% For the (possibly renamed) argument variables to an inlined call,
+%% either create `let' bindings for them, if they are still referenced
+%% in the residual expression (in C/Lisp, also if they are assigned to),
+%% or otherwise (if they are not referenced or assigned) mark them for
+%% evaluation for side effects.
+
+make_let_bindings([R | Rs], E, S) ->
+ {E1, S1} = make_let_bindings(Rs, E, S),
+ make_let_binding(R, E1, S1);
+make_let_bindings([], E, S) ->
+ {E, S}.
+
+make_let_binding(R, E, S) ->
+ %% The `referenced' flag is conservatively computed. We therefore
+ %% first check some simple cases where parameter R is definitely not
+ %% referenced in the resulting body E.
+ case is_literal(E) of
+ true ->
+ %% A constant contains no variable references.
+ make_let_binding_1(R, E, S);
+ false ->
+ case is_c_var(E) of
+ true ->
+ case var_name(E) =:= R#ref.name of
+ true ->
+ %% The body is simply the parameter variable
+ %% itself. Visit the operand for value and
+ %% substitute the result for the body.
+ visit_and_count_size(R#ref.opnd, S);
+ false ->
+ %% Not the same variable, so the parameter
+ %% is not referenced at all.
+ make_let_binding_1(R, E, S)
+ end;
+ false ->
+ %% Proceed to check the `referenced' flag.
+ case st__get_var_referenced(R#ref.loc, S) of
+ true ->
+ %% The parameter is probably referenced in
+ %% the residual code (although it might not
+ %% be). Visit the operand for value and
+ %% create a let-binding.
+ {E1, S1} = visit_and_count_size(R#ref.opnd,
+ S),
+ S2 = count_size(weight('let'), S1),
+ {c_let([ref_to_var(R)], E1, E), S2};
+ false ->
+ %% The parameter is definitely not
+ %% referenced.
+ make_let_binding_1(R, E, S)
+ end
+ end
+ end.
+
+%% This marks the operand for evaluation for effect.
+
+make_let_binding_1(R, E, S) ->
+ Opnd = R#ref.opnd,
+ {E, st__set_opnd_effect(Opnd#opnd.loc, S)}.
+
+%% Here, `R' is the ref-structure which is the target of the copy
+%% propagation, and `Opnd' is a visited operand structure, to be
+%% propagated through `R' if possible - if not, `R' is residualised.
+%% `Opnd' is normally the operand that `R' is bound to, and `E' is the
+%% result of visiting `Opnd' for value; we pass this as an argument so
+%% we don't have to fetch it multiple times (because we don't have
+%% constant time access).
+%%
+%% We also pass the environment of the site of the variable reference,
+%% for use when inlining a propagated fun-expression. In the original
+%% algorithm by Waddell, the environment used for inlining such cases is
+%% the identity mapping, because the fun-expression body has already
+%% been visited for value, and their algorithm combines renaming of
+%% source-code variables with the looking up of information about
+%% residual-code variables. We, however, need to check the environment
+%% of the call site when creating new non-shadowed variables, but we
+%% must avoid repeated renaming. We therefore separate the renaming and
+%% the environment (as in the renaming algorithm of Peyton-Jones and
+%% Marlow). This also makes our implementation more general, compared to
+%% the original algorithm, because we do not give up on propagating
+%% variables that were free in the fun-body.
+%%
+%% Example:
+%%
+%% let F = fun (X) -> {'foo', X} in
+%% let G = fun (H) -> apply H(F) % F is free in the fun G
+%% in apply G(fun (F) -> apply F(42))
+%% =>
+%% let F = fun (X) -> {'foo', X} in
+%% apply (fun (H) -> apply H(F))(fun (F) -> apply F(42))
+%% =>
+%% let F = fun (X) -> {'foo', X} in
+%% apply (fun (F) -> apply F(42))(F)
+%% =>
+%% let F = fun (X) -> {'foo', X} in
+%% apply F(42)
+%% =>
+%% apply (fun (X) -> {'foo', X})(2)
+%% =>
+%% {'foo', 42}
+%%
+%% The original algorithm would give up at stage 4, because F was free
+%% in the propagated fun-expression. Our version inlines this example
+%% completely.
+
+copy(R, Opnd, E, Ctxt, Env, S) ->
+ case is_c_var(E) of
+ true ->
+ %% The operand reduces to another variable - get its
+ %% ref-structure and attempt to propagate further.
+ copy_var(env__get(var_name(E), Opnd#opnd.env), Ctxt, Env,
+ S);
+ false ->
+ %% Apart from variables and functional values (the latter
+ %% are handled by `copy_1' below), only constant literals
+ %% are copyable in general; other things, including e.g.
+ %% tuples `{foo, X}', could cause duplication of work, and
+ %% are not copy propagated.
+ case is_literal(E) of
+ true ->
+ {E, count_size(weight(literal), S)};
+ false ->
+ copy_1(R, Opnd, E, Ctxt, Env, S)
+ end
+ end.
+
+copy_var(R, Ctxt, Env, S) ->
+ %% (In Lisp or C, if this other variable might be assigned to, we
+ %% should residualize the "parent" instead, so we don't bypass any
+ %% destructive updates.)
+ case R#ref.opnd of
+ undefined ->
+ %% This variable is not bound to an expression, so just
+ %% residualize it.
+ residualize_var(R, S);
+ Opnd ->
+ %% Note that because operands are always visited before
+ %% copied, all copyable operand expressions will be
+ %% propagated through any number of bindings. If `R' was
+ %% bound to a constant literal, we would never have reached
+ %% this point.
+ case st__lookup_opnd_cache(Opnd#opnd.loc, S) of
+ error ->
+ %% The result for this operand is not yet ready
+ %% (which should mean that it is a recursive
+ %% reference). Thus, we must residualise the
+ %% variable.
+ residualize_var(R, S);
+ {ok, #cache{expr = E1}} ->
+ %% The result for the operand is ready, so we can
+ %% proceed to propagate it.
+ copy_1(R, Opnd, E1, Ctxt, Env, S)
+ end
+ end.
+
+copy_1(R, Opnd, E, Ctxt, Env, S) ->
+ %% Fun-expression (lambdas) are a bit special; they are copyable,
+ %% but should preferably not be duplicated, so they should not be
+ %% copy propagated except into application contexts, where they can
+ %% be inlined.
+ case is_c_fun(E) of
+ true ->
+ case Ctxt of
+ #app{} ->
+ %% First test if the operand is "outer-pending"; if
+ %% so, don't inline.
+ case st__test_outer_pending(Opnd#opnd.loc, S) of
+ false ->
+ copy_inline(R, Opnd, E, Ctxt, Env, S);
+ true ->
+ %% Cyclic reference forced inlining to stop
+ %% (avoiding infinite unfolding).
+ residualize_var(R, S)
+ end;
+ _ ->
+ residualize_var(R, S)
+ end;
+ false ->
+ %% We have no other cases to handle here
+ residualize_var(R, S)
+ end.
+
+%% This inlines a function value that was propagated to an application
+%% context. The inlining is done with an identity renaming (since the
+%% expression is already visited) but in the environment of the call
+%% site (which is OK because of the no-shadowing strategy for renaming,
+%% and because the domain of our environments are the residual-program
+%% variables instead of the source-program variables). Note that we must
+%% first set the "outer-pending" flag, and clear it afterwards.
+
+copy_inline(R, Opnd, E, Ctxt, Env, S) ->
+ S1 = st__mark_outer_pending(Opnd#opnd.loc, S),
+ case catch {ok, copy_inline_1(R, E, Ctxt, Env, S1)} of
+ {ok, {E1, S2}} ->
+ {E1, st__clear_outer_pending(Opnd#opnd.loc, S2)};
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ %% If we use destructive update for the `outer-pending'
+ %% flag, we must make sure to clear it upon a nonlocal
+ %% return.
+ st__clear_outer_pending(Opnd#opnd.loc, S1),
+ throw(X)
+ end.
+
+%% If the current effort counter was passive, we use a new active effort
+%% counter with the inherited limit for this particular inlining.
+
+copy_inline_1(R, E, Ctxt, Env, S) ->
+ case effort_is_active(S) of
+ true ->
+ copy_inline_2(R, E, Ctxt, Env, S);
+ false ->
+ S1 = new_active_effort(get_effort_limit(S), S),
+ case catch {ok, copy_inline_2(R, E, Ctxt, Env, S1)} of
+ {ok, {E1, S2}} ->
+ %% Revert to the old effort counter.
+ {E1, revert_effort(S, S2)};
+ {counter_exceeded, effort, _} ->
+ %% Aborted this inlining attempt because too much
+ %% effort was spent. Residualize the variable and
+ %% revert to the previous state.
+ residualize_var(R, S);
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ throw(X)
+ end
+ end.
+
+%% Regardless of whether the current size counter is active or not, we
+%% use a new active size counter for each inlining. If the current
+%% counter was passive, the new counter gets the inherited size limit;
+%% if it was active, the size limit of the new counter will be equal to
+%% the remaining budget of the current counter (which itself is not
+%% affected by the inlining). This distributes the size budget more
+%% evenly over "inlinings within inlinings", so that the whole size
+%% budget is not spent on the first few call sites (in an inlined
+%% function body) forcing the remaining call sites to be residualised.
+
+copy_inline_2(R, E, Ctxt, Env, S) ->
+ Limit = case size_is_active(S) of
+ true ->
+ get_size_limit(S) - get_size_value(S);
+ false ->
+ get_size_limit(S)
+ end,
+ %% Add the cost of the application to the new size limit, so we
+ %% always inline functions that are small enough, even if `Limit' is
+ %% close to zero at this point. (This is an extension to the
+ %% original algorithm.)
+ S1 = new_active_size(Limit + apply_size(length(Ctxt#app.opnds)), S),
+ case catch {ok, inline(E, Ctxt, ren__identity(), Env, S1)} of
+ {ok, {E1, S2}} ->
+ %% Revert to the old size counter.
+ {E1, revert_size(S, S2)};
+ {counter_exceeded, size, S2} ->
+ %% Aborted this inlining attempt because it got too big.
+ %% Residualize the variable and revert to the old size
+ %% counter. (It is important that we do not also revert the
+ %% effort counter here. Because the effort and size counters
+ %% are always set up together, we know that the effort
+ %% counter returned in S2 is the same that was passed to
+ %% `inline'.)
+ S3 = revert_size(S, S2),
+ %% If we use destructive update for the `inlined' flag, we
+ %% must make sure to clear the flags of any nested
+ %% app-contexts upon aborting; see `inline' for details.
+ reset_nested_apps(Ctxt, S3), % for effect
+ residualize_var(R, S3);
+ {'EXIT', X} ->
+ exit(X);
+ X ->
+ throw(X)
+ end.
+
+reset_nested_apps(#app{ctxt = Ctxt, loc = L}, S) ->
+ reset_nested_apps(Ctxt, st__clear_app_inlined(L, S));
+reset_nested_apps(_, S) ->
+ S.
+
+
+%% ---------------------------------------------------------------------
+%% Support functions
+
+new_var(Env) ->
+ Name = env__new_vname(Env),
+ c_var(Name).
+
+residualize_var(R, S) ->
+ S1 = count_size(weight(var), S),
+ {ref_to_var(R), st__set_var_referenced(R#ref.loc, S1)}.
+
+%% This function returns the value-producing subexpression of any
+%% expression. (Except for sequencing expressions, this is the
+%% expression itself.)
+
+result(E) ->
+ case is_c_seq(E) of
+ true ->
+ %% Also see `make_seq', which is used in all places to build
+ %% sequences so that they are always nested in the first
+ %% position.
+ seq_body(E);
+ false ->
+ E
+ end.
+
+%% This function rewrites E to `do A1 E' if A is `do A1 A2', and
+%% otherwise returns E unchanged.
+
+hoist_effects(A, E) ->
+ case type(A) of
+ seq -> make_seq(seq_arg(A), E);
+ _ -> E
+ end.
+
+%% This "build sequencing expression" operation assures that sequences
+%% are always nested in the first position, which makes it easy to find
+%% the actual value-producing expression of a sequence (cf. `result').
+
+make_seq(E1, E2) ->
+ case is_safe(E1) of
+ true ->
+ %% The first expression can safely be dropped.
+ E2;
+ false ->
+ %% If `E1' is a sequence whose final expression has no side
+ %% effects, then we can lose *that* expression when we
+ %% compose the new sequence, since its value will not be
+ %% used.
+ E3 = case is_c_seq(E1) of
+ true ->
+ case is_safe(seq_body(E1)) of
+ true ->
+ %% Drop the final expression.
+ seq_arg(E1);
+ false ->
+ E1
+ end;
+ false ->
+ E1
+ end,
+ case is_c_seq(E2) of
+ true ->
+ %% `E2' is a sequence (E2' E2''), so we must
+ %% rearrange the nesting to ((E1, E2') E2''), to
+ %% preserve the invariant. Annotations on `E2' are
+ %% lost.
+ c_seq(c_seq(E3, seq_arg(E2)), seq_body(E2));
+ false ->
+ c_seq(E3, E2)
+ end
+ end.
+
+%% Currently, safe expressions include variables, lambda expressions,
+%% constructors with safe subexpressions (this includes atoms, integers,
+%% empty lists, etc.), seq-, let- and letrec-expressions with safe
+%% subexpressions, try- and catch-expressions with safe subexpressions
+%% and calls to safe functions with safe argument subexpressions.
+%% Binaries seem too tricky to be considered.
+
+is_safe(E) ->
+ case is_data(E) of
+ true ->
+ is_safe_list(data_es(E));
+ false ->
+ case type(E) of
+ var ->
+ true;
+ 'fun' ->
+ true;
+ values ->
+ is_safe_list(values_es(E));
+ 'seq' ->
+ case is_safe(seq_arg(E)) of
+ true ->
+ is_safe(seq_body(E));
+ false ->
+ false
+ end;
+ 'let' ->
+ case is_safe(let_arg(E)) of
+ true ->
+ is_safe(let_body(E));
+ false ->
+ false
+ end;
+ letrec ->
+ is_safe(letrec_body(E));
+ 'try' ->
+ %% If the argument expression is not safe, it could
+ %% be modifying the state; thus, even if the body is
+ %% safe, the try-expression as a whole would not be.
+ %% If the argument is safe, the handler is not used.
+ case is_safe(try_arg(E)) of
+ true ->
+ is_safe(try_body(E));
+ false ->
+ false
+ end;
+ 'catch' ->
+ is_safe(catch_body(E));
+ call ->
+ M = call_module(E),
+ F = call_name(E),
+ case is_c_atom(M) and is_c_atom(F) of
+ true ->
+ As = call_args(E),
+ case is_safe_list(As) of
+ true ->
+ is_safe_call(atom_val(M),
+ atom_val(F),
+ length(As));
+ false ->
+ false
+ end;
+ false ->
+ false
+ end;
+ _ ->
+ false
+ end
+ end.
+
+is_safe_list([E | Es]) ->
+ case is_safe(E) of
+ true ->
+ is_safe_list(Es);
+ false ->
+ false
+ end;
+is_safe_list([]) ->
+ true.
+
+is_safe_call(M, F, A) ->
+ erl_bifs:is_safe(M, F, A).
+
+%% When setting up local variables, we only create new names if we have
+%% to, according to the "no-shadowing" strategy.
+
+make_locals(Vs, Ren, Env) ->
+ make_locals(Vs, [], Ren, Env).
+
+make_locals([V | Vs], As, Ren, Env) ->
+ Name = var_name(V),
+ case env__is_defined(Name, Env) of
+ false ->
+ %% The variable need not be renamed. Just make sure that the
+ %% renaming will map it to itself.
+ Name1 = Name,
+ Ren1 = ren__add_identity(Name, Ren);
+ true ->
+ %% The variable must be renamed to maintain the no-shadowing
+ %% invariant. Do the right thing for function variables.
+ Name1 = case Name of
+ {A, N} ->
+ env__new_fname(A, N, Env);
+ _ ->
+ env__new_vname(Env)
+ end,
+ Ren1 = ren__add(Name, Name1, Ren)
+ end,
+ %% This temporary binding is added for correct new-key generation.
+ Env1 = env__bind(Name1, dummy, Env),
+ make_locals(Vs, [Name1 | As], Ren1, Env1);
+make_locals([], As, Ren, Env) ->
+ {reverse(As), Ren, Env}.
+
+%% This adds let-bindings for the source code variables in `Es' to the
+%% environment `Env'.
+%%
+%% Note that we always assign a new state location for the
+%% residual-program variable, since we cannot know when a location for a
+%% particular variable in the source code can be reused.
+
+bind_locals(Vs, Ren, Env, S) ->
+ Opnds = lists:duplicate(length(Vs), undefined),
+ bind_locals(Vs, Opnds, Ren, Env, S).
+
+bind_locals(Vs, Opnds, Ren, Env, S) ->
+ {Ns, Ren1, Env1} = make_locals(Vs, Ren, Env),
+ {Rs, Env2, S1} = bind_locals_1(Ns, Opnds, [], Env1, S),
+ {Rs, Ren1, Env2, S1}.
+
+%% Note that the `Vs' are currently not used for anything except the
+%% number of variables. If we were maintaining "source-referenced"
+%% flags, then the flag in the new variable should be initialized to the
+%% current value of the (residual-) referenced-flag of the "parent".
+
+bind_locals_1([N | Ns], [Opnd | Opnds], Rs, Env, S) ->
+ {R, S1} = new_ref(N, Opnd, S),
+ Env1 = env__bind(N, R, Env),
+ bind_locals_1(Ns, Opnds, [R | Rs], Env1, S1);
+bind_locals_1([], [], Rs, Env, S) ->
+ {lists:reverse(Rs), Env, S}.
+
+new_refs(Ns, Opnds, S) ->
+ new_refs(Ns, Opnds, [], S).
+
+new_refs([N | Ns], [Opnd | Opnds], Rs, S) ->
+ {R, S1} = new_ref(N, Opnd, S),
+ new_refs(Ns, Opnds, [R | Rs], S1);
+new_refs([], [], Rs, S) ->
+ {lists:reverse(Rs), S}.
+
+new_ref(N, Opnd, S) ->
+ {L, S1} = st__new_ref_loc(S),
+ {#ref{name = N, opnd = Opnd, loc = L}, S1}.
+
+%% This adds recursive bindings for the source code variables in `Es' to
+%% the environment `Env'. Note that recursive binding of a set of
+%% variables is an atomic operation on the environment - they cannot be
+%% added one at a time.
+
+bind_recursive(Vs, Opnds, Ren, Env, S) ->
+ {Ns, Ren1, Env1} = make_locals(Vs, Ren, Env),
+ {Rs, S1} = new_refs(Ns, Opnds, S),
+
+ %% When this fun-expression is evaluated, it updates the operand
+ %% structure in the ref-structure to contain the recursively defined
+ %% environment and the correct renaming.
+ Fun = fun (R, Env) ->
+ Opnd = R#ref.opnd,
+ R#ref{opnd = Opnd#opnd{ren = Ren1, env = Env}}
+ end,
+ {Rs, Ren1, env__bind_recursive(Ns, Rs, Fun, Env1), S1}.
+
+safe_context(Ctxt) ->
+ case Ctxt of
+ #app{} ->
+ value;
+ _ ->
+ Ctxt
+ end.
+
+%% Note that the name of a variable encodes its type: a "plain" variable
+%% or a function variable. The latter kind also contains an arity number
+%% which should be preserved upon renaming.
+
+ref_to_var(#ref{name = Name}) ->
+ %% If we were maintaining "source-referenced" flags, the annotation
+ %% `add_ann([#source_ref{loc = L}], E)' should also be done here, to
+ %% make the algorithm reapplicable. This is however not necessary
+ %% since there are no destructive variable assignments in Erlang.
+ c_var(Name).
+
+%% Including the effort counter of the call site assures that the cost
+%% of processing an operand via `visit' is charged to the correct
+%% counter. In particular, if the effort counter of the call site was
+%% passive, the operands will also be processed with a passive counter.
+
+make_opnd(E, Ren, Env, S) ->
+ {L, S1} = st__new_opnd_loc(S),
+ C = st__get_effort(S1),
+ Opnd = #opnd{expr = E, ren = Ren, env = Env, loc = L, effort = C},
+ {Opnd, S1}.
+
+keep_referenced(Rs, S) ->
+ [R || R <- Rs, st__get_var_referenced(R#ref.loc, S)].
+
+residualize_operands(Opnds, E, S) ->
+ foldr(fun (Opnd, {E, S}) -> residualize_operand(Opnd, E, S) end,
+ {E, S}, Opnds).
+
+%% This is the only case where an operand expression can be visited in
+%% `effect' context instead of `value' context.
+
+residualize_operand(Opnd, E, S) ->
+ case st__get_opnd_effect(Opnd#opnd.loc, S) of
+ true ->
+ %% The operand has not been visited, so we do that now, but
+ %% in `effect' context. (Waddell's algoritm does some stuff
+ %% here to account specially for the operand size, which
+ %% appears unnecessary.)
+ {E1, S1} = i(Opnd#opnd.expr, effect, Opnd#opnd.ren,
+ Opnd#opnd.env, S),
+ {make_seq(E1, E), S1};
+ false ->
+ {E, S}
+ end.
+
+%% The `visit' function always visits the operand expression in `value'
+%% context (`residualize_operand' visits an unreferenced operand
+%% expression in `effect' context when necessary). A new passive size
+%% counter is used for visiting the operand, the final value of which is
+%% then cached along with the resulting expression.
+%%
+%% Note that the effort counter of the call site, included in the
+%% operand structure, is not a shared object. Thus, the effort budget is
+%% actually reused over all occurrences of the operands of a single
+%% application. This does not appear to be a problem; just a
+%% modification of the algorithm.
+
+visit(Opnd, S) ->
+ {C, S1} = visit_1(Opnd, S),
+ {C#cache.expr, S1}.
+
+visit_and_count_size(Opnd, S) ->
+ {C, S1} = visit_1(Opnd, S),
+ {C#cache.expr, count_size(C#cache.size, S1)}.
+
+visit_1(Opnd, S) ->
+ case st__lookup_opnd_cache(Opnd#opnd.loc, S) of
+ error ->
+ %% Use a new, passive, size counter for visiting operands,
+ %% and use the effort counter of the context of the operand.
+ %% It turns out that if the latter is active, it must be the
+ %% same object as the one currently used, and if it is
+ %% passive, it does not matter if it is the same object as
+ %% any other counter.
+ Effort = Opnd#opnd.effort,
+ Active = counter__is_active(Effort),
+ S1 = case Active of
+ true ->
+ S; % don't change effort counter
+ false ->
+ st__set_effort(Effort, S)
+ end,
+ S2 = new_passive_size(get_size_limit(S1), S1),
+
+ %% Visit the expression and cache the result, along with the
+ %% final value of the size counter.
+ {E, S3} = i(Opnd#opnd.expr, value, Opnd#opnd.ren,
+ Opnd#opnd.env, S2),
+ Size = get_size_value(S3),
+ C = #cache{expr = E, size = Size},
+ S4 = revert_size(S, st__set_opnd_cache(Opnd#opnd.loc, C,
+ S3)),
+ case Active of
+ true ->
+ {C, S4}; % keep using the same effort counter
+ false ->
+ {C, revert_effort(S, S4)}
+ end;
+ {ok, C} ->
+ {C, S}
+ end.
+
+%% Create a pattern matching template for an expression. A template
+%% contains only data constructors (including atomic ones) and
+%% variables, and compound literals are not folded into a single node.
+%% Each node in the template is annotated with the variable which holds
+%% the corresponding subexpression; these are new, unique variables not
+%% existing in the given `Env'. Returns `{Template, Variables, NewEnv}',
+%% where `Variables' is the list of all variables corresponding to nodes
+%% in the template *listed in reverse dependency order*, and `NewEnv' is
+%% `Env' augmented with mappings from the variable names to
+%% subexpressions of `E' (not #ref{} structures!) rewritten so that no
+%% computations are duplicated. `Variables' is guaranteed to be nonempty
+%% - at least the root node will always be bound to a new variable.
+
+make_template(E, Env) ->
+ make_template(E, [], Env).
+
+make_template(E, Vs0, Env0) ->
+ case is_data(E) of
+ true ->
+ {Ts, {Vs1, Env1}} = mapfoldl(
+ fun (E, {Vs0, Env0}) ->
+ {T, Vs1, Env1} =
+ make_template(E, Vs0,
+ Env0),
+ {T, {Vs1, Env1}}
+ end,
+ {Vs0, Env0}, data_es(E)),
+ T = make_data_skel(data_type(E), Ts),
+ E1 = update_data(E, data_type(E),
+ [hd(get_ann(T)) || T <- Ts]),
+ V = new_var(Env1),
+ Env2 = env__bind(var_name(V), E1, Env1),
+ {set_ann(T, [V]), [V | Vs1], Env2};
+ false ->
+ case type(E) of
+ seq ->
+ %% For a sequencing, we can rebind the variable used
+ %% for the body, and pass on the template as it is.
+ {T, Vs1, Env1} = make_template(seq_body(E), Vs0,
+ Env0),
+ V = var_name(hd(get_ann(T))),
+ E1 = update_c_seq(E, seq_arg(E), env__get(V, Env1)),
+ Env2 = env__bind(V, E1, Env1),
+ {T, Vs1, Env2};
+ _ ->
+ V = new_var(Env0),
+ Env1 = env__bind(var_name(V), E, Env0),
+ {set_ann(V, [V]), [V | Vs0], Env1}
+ end
+ end.
+
+%% Two clauses are equivalent if their bodies are equivalent expressions
+%% given that the respective pattern variables are local.
+
+equivalent_clauses([]) ->
+ true;
+equivalent_clauses([C | Cs]) ->
+ Env = cerl_trees:variables(c_values(clause_pats(C))),
+ equivalent_clauses_1(clause_body(C), Cs, Env).
+
+equivalent_clauses_1(E, [C | Cs], Env) ->
+ Env1 = cerl_trees:variables(c_values(clause_pats(C))),
+ case equivalent(E, clause_body(C), ordsets:union(Env, Env1)) of
+ true ->
+ equivalent_clauses_1(E, Cs, Env);
+ false ->
+ false
+ end;
+equivalent_clauses_1(_, [], _Env) ->
+ true.
+
+%% Two expressions are equivalent if and only if they yield the same
+%% value and has the same side effects in the same order. Currently, we
+%% only accept equality between constructors (constants) and nonlocal
+%% variables, since this should cover most cases of interest. If a
+%% variable is locally bound in one expression, it cannot be equivalent
+%% to one with the same name in the other expression, so we need not
+%% keep track of two environments.
+
+equivalent(E1, E2, Env) ->
+ case is_data(E1) of
+ true ->
+ case is_data(E2) of
+ true ->
+ T1 = {data_type(E1), data_arity(E1)},
+ T2 = {data_type(E2), data_arity(E2)},
+ %% Note that we must test for exact equality.
+ if T1 =:= T2 ->
+ equivalent_lists(data_es(E1), data_es(E2),
+ Env);
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+ false ->
+ case type(E1) of
+ var ->
+ case is_c_var(E2) of
+ true ->
+ N1 = var_name(E1),
+ N2 = var_name(E2),
+ if N1 =:= N2 ->
+ not ordsets:is_element(N1, Env);
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+ _ ->
+ %% Other constructs are not being considered.
+ false
+ end
+ end.
+
+equivalent_lists([E1 | Es1], [E2 | Es2], Env) ->
+ equivalent(E1, E2, Env) and equivalent_lists(Es1, Es2, Env);
+equivalent_lists([], [], _) ->
+ true;
+equivalent_lists(_, _, _) ->
+ false.
+
+%% Return `false' or `{true, EffectExpr, ValueExpr}'. The environment is
+%% passed for new-variable generation.
+
+reduce_bif_call(M, F, As, Env) ->
+ reduce_bif_call_1(M, F, length(As), As, Env).
+
+reduce_bif_call_1(erlang, element, 2, [X, Y], _Env) ->
+ case is_c_int(X) and is_c_tuple(Y) of
+ true ->
+ %% We are free to change the relative evaluation order of
+ %% the elements, so lifting out a particular element is OK.
+ T = list_to_tuple(tuple_es(Y)),
+ N = int_val(X),
+ if integer(N), N > 0, N =< size(T) ->
+ E = element(N, T),
+ Es = tuple_to_list(setelement(N, T, void())),
+ {true, make_seq(c_tuple(Es), E)};
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, hd, 1, [X], _Env) ->
+ case is_c_cons(X) of
+ true ->
+ %% Cf. `element/2' above.
+ {true, make_seq(cons_tl(X), cons_hd(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, length, 1, [X], _Env) ->
+ case is_c_list(X) of
+ true ->
+ %% Cf. `erlang:size/1' below.
+ {true, make_seq(X, c_int(list_length(X)))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, list_to_tuple, 1, [X], _Env) ->
+ case is_c_list(X) of
+ true ->
+ %% This does not actually preserve all the evaluation order
+ %% constraints of the list, but I don't imagine that it will
+ %% be a problem.
+ {true, c_tuple(list_elements(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, setelement, 3, [X, Y, Z], Env) ->
+ case is_c_int(X) and is_c_tuple(Y) of
+ true ->
+ %% Here, unless `Z' is a simple expression, we must bind it
+ %% to a new variable, because in that case, `Z' must be
+ %% evaluated before any part of `Y'.
+ T = list_to_tuple(tuple_es(Y)),
+ N = int_val(X),
+ if integer(N), N > 0, N =< size(T) ->
+ E = element(N, T),
+ case is_simple(Z) of
+ true ->
+ Es = tuple_to_list(setelement(N, T, Z)),
+ {true, make_seq(E, c_tuple(Es))};
+ false ->
+ V = new_var(Env),
+ Es = tuple_to_list(setelement(N, T, V)),
+ E1 = make_seq(E, c_tuple(Es)),
+ {true, c_let([V], Z, E1)}
+ end;
+ true ->
+ false
+ end;
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, size, 1, [X], _Env) ->
+ case is_c_tuple(X) of
+ true ->
+ %% Just evaluate the tuple for effect and use the size (the
+ %% arity) as the result.
+ {true, make_seq(X, c_int(tuple_arity(X)))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, tl, 1, [X], _Env) ->
+ case is_c_cons(X) of
+ true ->
+ %% Cf. `element/2' above.
+ {true, make_seq(cons_hd(X), cons_tl(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(erlang, tuple_to_list, 1, [X], _Env) ->
+ case is_c_tuple(X) of
+ true ->
+ %% This actually introduces slightly stronger constraints on
+ %% the evaluation order of the subexpressions.
+ {true, make_list(tuple_es(X))};
+ false ->
+ false
+ end;
+reduce_bif_call_1(_M, _F, _A, _As, _Env) ->
+ false.
+
+effort_is_active(S) ->
+ counter__is_active(st__get_effort(S)).
+
+size_is_active(S) ->
+ counter__is_active(st__get_size(S)).
+
+get_effort_limit(S) ->
+ counter__limit(st__get_effort(S)).
+
+new_active_effort(Limit, S) ->
+ st__set_effort(counter__new_active(Limit), S).
+
+revert_effort(S1, S2) ->
+ st__set_effort(st__get_effort(S1), S2).
+
+new_active_size(Limit, S) ->
+ st__set_size(counter__new_active(Limit), S).
+
+new_passive_size(Limit, S) ->
+ st__set_size(counter__new_passive(Limit), S).
+
+revert_size(S1, S2) ->
+ st__set_size(st__get_size(S1), S2).
+
+count_effort(N, S) ->
+ C = st__get_effort(S),
+ C1 = counter__add(N, C, effort, S),
+ case debug_counters() of
+ true ->
+ case counter__is_active(C1) of
+ true ->
+ V = counter__value(C1),
+ case V > get(counter_effort_max) of
+ true ->
+ put(counter_effort_max, V);
+ false ->
+ ok
+ end;
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ st__set_effort(C1, S).
+
+count_size(N, S) ->
+ C = st__get_size(S),
+ C1 = counter__add(N, C, size, S),
+ case debug_counters() of
+ true ->
+ case counter__is_active(C1) of
+ true ->
+ V = counter__value(C1),
+ case V > get(counter_size_max) of
+ true ->
+ put(counter_size_max, V);
+ false ->
+ ok
+ end;
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ st__set_size(C1, S).
+
+get_size_value(S) ->
+ counter__value(st__get_size(S)).
+
+get_size_limit(S) ->
+ counter__limit(st__get_size(S)).
+
+kill_id_anns([{'id',_} | As]) ->
+ kill_id_anns(As);
+kill_id_anns([A | As]) ->
+ [A | kill_id_anns(As)];
+kill_id_anns([]) ->
+ [].
+
+
+%% =====================================================================
+%% General utilities
+
+max(X, Y) when X > Y -> X;
+max(_, Y) -> Y.
+
+%% The atom `ok', is widely used in Erlang for "void" values.
+
+void() -> abstract(ok).
+
+is_simple(E) ->
+ case type(E) of
+ literal -> true;
+ var -> true;
+ 'fun' -> true;
+ _ -> false
+ end.
+
+get_components(N, E) ->
+ case type(E) of
+ values ->
+ Es = values_es(E),
+ if length(Es) == N ->
+ {true, Es};
+ true ->
+ false
+ end;
+ _ when N == 1 ->
+ {true, [E]};
+ _ ->
+ false
+ end.
+
+all_static([E | Es]) ->
+ case is_literal(result(E)) of
+ true ->
+ all_static(Es);
+ false ->
+ false
+ end;
+all_static([]) ->
+ true.
+
+set_clause_bodies([C | Cs], B) ->
+ [update_c_clause(C, clause_pats(C), clause_guard(C), B)
+ | set_clause_bodies(Cs, B)];
+set_clause_bodies([], _) ->
+ [].
+
+filename([C | T]) when integer(C), C > 0, C =< 255 ->
+ [C | filename(T)];
+filename([H|T]) ->
+ filename(H) ++ filename(T);
+filename([]) ->
+ [];
+filename(N) when atom(N) ->
+ atom_to_list(N);
+filename(N) ->
+ report_error("bad filename: `~P'.", [N, 25]),
+ exit(error).
+
+
+%% =====================================================================
+%% Abstract datatype: renaming()
+
+ren__identity() ->
+ dict:new().
+
+ren__add(X, Y, Ren) ->
+ dict:store(X, Y, Ren).
+
+ren__map(X, Ren) ->
+ case dict:find(X, Ren) of
+ {ok, Y} ->
+ Y;
+ error ->
+ X
+ end.
+
+ren__add_identity(X, Ren) ->
+ dict:erase(X, Ren).
+
+
+%% =====================================================================
+%% Abstract datatype: environment()
+
+env__empty() ->
+ rec_env:empty().
+
+env__bind(Key, Val, Env) ->
+ rec_env:bind(Key, Val, Env).
+
+%% `Es' should have type `[{Key, Val}]', and `Fun' should have type
+%% `(Val, Env) -> T', mapping a value together with the recursive
+%% environment itself to some term `T' to be returned when the entry is
+%% looked up.
+
+env__bind_recursive(Ks, Vs, F, Env) ->
+ rec_env:bind_recursive(Ks, Vs, F, Env).
+
+env__lookup(Key, Env) ->
+ rec_env:lookup(Key, Env).
+
+env__get(Key, Env) ->
+ rec_env:get(Key, Env).
+
+env__is_defined(Key, Env) ->
+ rec_env:is_defined(Key, Env).
+
+env__new_vname(Env) ->
+ rec_env:new_key(Env).
+
+env__new_fname(A, N, Env) ->
+ rec_env:new_key(fun (X) ->
+ S = integer_to_list(X),
+ {list_to_atom(atom_to_list(A) ++ "_" ++ S),
+ N}
+ end, Env).
+
+
+%% =====================================================================
+%% Abstract datatype: state()
+
+-record(state, {free, % next free location
+ size, % size counter
+ effort, % effort counter
+ cache, % operand expression cache
+ var_flags, % flags for variables (#ref-structures)
+ opnd_flags, % flags for operands
+ app_flags}). % flags for #app-structures
+
+%% Note that we do not have a `var_assigned' flag, since there is no
+%% destructive assignment in Erlang. In the original algorithm, the
+%% "residual-referenced"-flags of the previous inlining pass (or
+%% initialization pass) are used as the "source-referenced"-flags for
+%% the subsequent pass. The latter may then be used as a safe
+%% approximation whenever we need to base a decision on whether or not a
+%% particular variable or function variable could be referenced in the
+%% program being generated, and computation of the new
+%% "residual-referenced" flag for that variable is not yet finished. In
+%% the present algorithm, this can only happen in the presence of
+%% variable assignments, which do not exist in Erlang. Therefore, we do
+%% not keep "source-referenced" flags for residual-code references in
+%% our implementation.
+%%
+%% The "inner-pending" flag tells us whether we are already in the
+%% process of visiting a particular operand, and the "outer-pending"
+%% flag whether we are in the process of inlining a propagated
+%% functional value. The "pending flags" are really counters limiting
+%% the number of times an operand may be inlined recursively, causing
+%% loop unrolling; however, unrolling more than one iteration does not
+%% work offhand in the present implementation. (TODO: find out why.)
+%% Note that the initial value must be greater than zero in order for
+%% any inlining at all to be done.
+
+%% Flags are stored in ETS-tables, one table for each class. The second
+%% element in each stored tuple is the key (the "label").
+
+-record(var_flags, {lab, referenced = false}).
+-record(opnd_flags, {lab, inner_pending = 1, outer_pending = 1,
+ effect = false}).
+-record(app_flags, {lab, inlined = false}).
+
+st__new(Effort, Size) ->
+ #state{free = 0,
+ size = counter__new_passive(Size),
+ effort = counter__new_passive(Effort),
+ cache = dict:new(),
+ var_flags = ets:new(var, [set, private, {keypos, 2}]),
+ opnd_flags = ets:new(opnd, [set, private, {keypos, 2}]),
+ app_flags = ets:new(app, [set, private, {keypos, 2}])}.
+
+st__new_loc(S) ->
+ N = S#state.free,
+ {N, S#state{free = N + 1}}.
+
+st__get_effort(S) ->
+ S#state.effort.
+
+st__set_effort(C, S) ->
+ S#state{effort = C}.
+
+st__get_size(S) ->
+ S#state.size.
+
+st__set_size(C, S) ->
+ S#state{size = C}.
+
+st__set_var_referenced(L, S) ->
+ T = S#state.var_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#var_flags{referenced = true}),
+ S.
+
+st__get_var_referenced(L, S) ->
+ ets:lookup_element(S#state.var_flags, L, #var_flags.referenced).
+
+st__lookup_opnd_cache(L, S) ->
+ dict:find(L, S#state.cache).
+
+%% Note that setting the cache should only be done once.
+
+st__set_opnd_cache(L, C, S) ->
+ S#state{cache = dict:store(L, C, S#state.cache)}.
+
+st__set_opnd_effect(L, S) ->
+ T = S#state.opnd_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#opnd_flags{effect = true}),
+ S.
+
+st__get_opnd_effect(L, S) ->
+ ets:lookup_element(S#state.opnd_flags, L, #opnd_flags.effect).
+
+st__set_app_inlined(L, S) ->
+ T = S#state.app_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#app_flags{inlined = true}),
+ S.
+
+st__clear_app_inlined(L, S) ->
+ T = S#state.app_flags,
+ [F] = ets:lookup(T, L),
+ ets:insert(T, F#app_flags{inlined = false}),
+ S.
+
+st__get_app_inlined(L, S) ->
+ ets:lookup_element(S#state.app_flags, L, #app_flags.inlined).
+
+%% The pending-flags are initialized by `st__new_opnd_loc' below.
+
+st__test_inner_pending(L, S) ->
+ T = S#state.opnd_flags,
+ P = ets:lookup_element(T, L, #opnd_flags.inner_pending),
+ P =< 0.
+
+st__mark_inner_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.inner_pending, -1}),
+ S.
+
+st__clear_inner_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.inner_pending, 1}),
+ S.
+
+st__test_outer_pending(L, S) ->
+ T = S#state.opnd_flags,
+ P = ets:lookup_element(T, L, #opnd_flags.outer_pending),
+ P =< 0.
+
+st__mark_outer_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.outer_pending, -1}),
+ S.
+
+st__clear_outer_pending(L, S) ->
+ ets:update_counter(S#state.opnd_flags, L,
+ {#opnd_flags.outer_pending, 1}),
+ S.
+
+st__new_app_loc(S) ->
+ V = {L, _S1} = st__new_loc(S),
+ ets:insert(S#state.app_flags, #app_flags{lab = L}),
+ V.
+
+st__new_ref_loc(S) ->
+ V = {L, _S1} = st__new_loc(S),
+ ets:insert(S#state.var_flags, #var_flags{lab = L}),
+ V.
+
+st__new_opnd_loc(S) ->
+ V = {L, _S1} = st__new_loc(S),
+ ets:insert(S#state.opnd_flags, #opnd_flags{lab = L}),
+ V.
+
+
+%% =====================================================================
+%% Abstract datatype: counter()
+%%
+%% `counter__add' throws `{counter_exceeded, Type, Data}' if the
+%% resulting counter value would exceed the limit for the counter in
+%% question (`Type' and `Data' are given by the user).
+
+-record(counter, {active, value, limit}).
+
+counter__new_passive(Limit) when Limit > 0 ->
+ {0, Limit}.
+
+counter__new_active(Limit) when Limit > 0 ->
+ {Limit, Limit}.
+
+%% Active counters have values > 0 internally; passive counters start at
+%% zero. The 'limit' field is only accessed by the 'counter__limit'
+%% function.
+
+counter__is_active({C, _}) ->
+ C > 0.
+
+counter__limit({_, L}) ->
+ L.
+
+counter__value({N, L}) ->
+ if N > 0 ->
+ L - N;
+ true ->
+ -N
+ end.
+
+counter__add(N, {V, L}, Type, Data) ->
+ N1 = V - N,
+ if V > 0, N1 =< 0 ->
+ case debug_counters() of
+ true ->
+ case Type of
+ effort ->
+ put(counter_effort_triggers,
+ get(counter_effort_triggers) + 1);
+ size ->
+ put(counter_size_triggers,
+ get(counter_size_triggers) + 1)
+ end;
+ _ ->
+ ok
+ end,
+ throw({counter_exceeded, Type, Data});
+ true ->
+ {N1, L}
+ end.
+
+
+%% =====================================================================
+%% Reporting
+
+% report_internal_error(S) ->
+% report_internal_error(S, []).
+
+report_internal_error(S, Vs) ->
+ report_error("internal error: " ++ S, Vs).
+
+report_error(D) ->
+ report_error(D, []).
+
+report_error({F, L, D}, Vs) ->
+ report({F, L, {error, D}}, Vs);
+report_error(D, Vs) ->
+ report({error, D}, Vs).
+
+report_warning(D) ->
+ report_warning(D, []).
+
+report_warning({F, L, D}, Vs) ->
+ report({F, L, {warning, D}}, Vs);
+report_warning(D, Vs) ->
+ report({warning, D}, Vs).
+
+report(D, Vs) ->
+ io:put_chars(format(D, Vs)).
+
+format({error, D}, Vs) ->
+ ["error: ", format(D, Vs)];
+format({warning, D}, Vs) ->
+ ["warning: ", format(D, Vs)];
+format({"", L, D}, Vs) when integer(L), L > 0 ->
+ [io_lib:fwrite("~w: ", [L]), format(D, Vs)];
+format({"", _L, D}, Vs) ->
+ format(D, Vs);
+format({F, L, D}, Vs) when integer(L), L > 0 ->
+ [io_lib:fwrite("~s:~w: ", [filename(F), L]), format(D, Vs)];
+format({F, _L, D}, Vs) ->
+ [io_lib:fwrite("~s: ", [filename(F)]), format(D, Vs)];
+format(S, Vs) when list(S) ->
+ [io_lib:fwrite(S, Vs), $\n].
+
+
+%% =====================================================================
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl
new file mode 100644
index 0000000000..50384a6ff8
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/cerl_trees.erl
@@ -0,0 +1,801 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Richard Carlsson.
+%% Copyright (C) 1999-2002 Richard Carlsson.
+%% Portions created by Ericsson are Copyright 2001, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: cerl_trees.erl,v 1.2 2010/06/07 06:32:39 kostis Exp $
+
+%% @doc Basic functions on Core Erlang abstract syntax trees.
+%%
+%% <p>Syntax trees are defined in the module <a
+%% href=""><code>cerl</code></a>.</p>
+%%
+%% @type cerl() = cerl:cerl()
+
+-module(cerl_trees).
+
+-export([depth/1, fold/3, free_variables/1, label/1, label/2, map/2,
+ mapfold/3, size/1, variables/1]).
+
+-import(cerl, [alias_pat/1, alias_var/1, ann_c_alias/3, ann_c_apply/3,
+ ann_c_binary/2, ann_c_bitstr/6, ann_c_call/4,
+ ann_c_case/3, ann_c_catch/2, ann_c_clause/4,
+ ann_c_cons_skel/3, ann_c_fun/3, ann_c_let/4,
+ ann_c_letrec/3, ann_c_module/5, ann_c_primop/3,
+ ann_c_receive/4, ann_c_seq/3, ann_c_try/6,
+ ann_c_tuple_skel/2, ann_c_values/2, apply_args/1,
+ apply_op/1, binary_segments/1, bitstr_val/1,
+ bitstr_size/1, bitstr_unit/1, bitstr_type/1,
+ bitstr_flags/1, call_args/1, call_module/1, call_name/1,
+ case_arg/1, case_clauses/1, catch_body/1, clause_body/1,
+ clause_guard/1, clause_pats/1, clause_vars/1, concrete/1,
+ cons_hd/1, cons_tl/1, fun_body/1, fun_vars/1, get_ann/1,
+ let_arg/1, let_body/1, let_vars/1, letrec_body/1,
+ letrec_defs/1, letrec_vars/1, module_attrs/1,
+ module_defs/1, module_exports/1, module_name/1,
+ module_vars/1, primop_args/1, primop_name/1,
+ receive_action/1, receive_clauses/1, receive_timeout/1,
+ seq_arg/1, seq_body/1, set_ann/2, subtrees/1, try_arg/1,
+ try_body/1, try_vars/1, try_evars/1, try_handler/1,
+ tuple_es/1, type/1, update_c_alias/3, update_c_apply/3,
+ update_c_binary/2, update_c_bitstr/6, update_c_call/4,
+ update_c_case/3, update_c_catch/2, update_c_clause/4,
+ update_c_cons/3, update_c_cons_skel/3, update_c_fun/3,
+ update_c_let/4, update_c_letrec/3, update_c_module/5,
+ update_c_primop/3, update_c_receive/4, update_c_seq/3,
+ update_c_try/6, update_c_tuple/2, update_c_tuple_skel/2,
+ update_c_values/2, values_es/1, var_name/1]).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec depth(Tree::cerl) -> integer()
+%%
+%% @doc Returns the length of the longest path in the tree. A leaf
+%% node has depth zero, the tree representing "<code>{foo,
+%% bar}</code>" has depth one, etc.
+
+depth(T) ->
+ case subtrees(T) of
+ [] ->
+ 0;
+ Gs ->
+ 1 + lists:foldl(fun (G, A) -> erlang:max(depth_1(G), A) end, 0, Gs)
+ end.
+
+depth_1(Ts) ->
+ lists:foldl(fun (T, A) -> erlang:max(depth(T), A) end, 0, Ts).
+
+%% max(X, Y) when X > Y -> X;
+%% max(_, Y) -> Y.
+
+
+%% @spec size(Tree::cerl()) -> integer()
+%%
+%% @doc Returns the number of nodes in <code>Tree</code>.
+
+size(T) ->
+ fold(fun (_, S) -> S + 1 end, 0, T).
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec map(Function, Tree::cerl()) -> cerl()
+%%
+%% Function = (cerl()) -> cerl()
+%%
+%% @doc Maps a function onto the nodes of a tree. This replaces each
+%% node in the tree by the result of applying the given function on
+%% the original node, bottom-up.
+%%
+%% @see mapfold/3
+
+map(F, T) ->
+ F(map_1(F, T)).
+
+map_1(F, T) ->
+ case type(T) of
+ literal ->
+ case concrete(T) of
+ [_ | _] ->
+ update_c_cons(T, map(F, cons_hd(T)),
+ map(F, cons_tl(T)));
+ V when tuple_size(V) > 0 ->
+ update_c_tuple(T, map_list(F, tuple_es(T)));
+ _ ->
+ T
+ end;
+ var ->
+ T;
+ values ->
+ update_c_values(T, map_list(F, values_es(T)));
+ cons ->
+ update_c_cons_skel(T, map(F, cons_hd(T)),
+ map(F, cons_tl(T)));
+ tuple ->
+ update_c_tuple_skel(T, map_list(F, tuple_es(T)));
+ 'let' ->
+ update_c_let(T, map_list(F, let_vars(T)),
+ map(F, let_arg(T)),
+ map(F, let_body(T)));
+ seq ->
+ update_c_seq(T, map(F, seq_arg(T)),
+ map(F, seq_body(T)));
+ apply ->
+ update_c_apply(T, map(F, apply_op(T)),
+ map_list(F, apply_args(T)));
+ call ->
+ update_c_call(T, map(F, call_module(T)),
+ map(F, call_name(T)),
+ map_list(F, call_args(T)));
+ primop ->
+ update_c_primop(T, map(F, primop_name(T)),
+ map_list(F, primop_args(T)));
+ 'case' ->
+ update_c_case(T, map(F, case_arg(T)),
+ map_list(F, case_clauses(T)));
+ clause ->
+ update_c_clause(T, map_list(F, clause_pats(T)),
+ map(F, clause_guard(T)),
+ map(F, clause_body(T)));
+ alias ->
+ update_c_alias(T, map(F, alias_var(T)),
+ map(F, alias_pat(T)));
+ 'fun' ->
+ update_c_fun(T, map_list(F, fun_vars(T)),
+ map(F, fun_body(T)));
+ 'receive' ->
+ update_c_receive(T, map_list(F, receive_clauses(T)),
+ map(F, receive_timeout(T)),
+ map(F, receive_action(T)));
+ 'try' ->
+ update_c_try(T, map(F, try_arg(T)),
+ map_list(F, try_vars(T)),
+ map(F, try_body(T)),
+ map_list(F, try_evars(T)),
+ map(F, try_handler(T)));
+ 'catch' ->
+ update_c_catch(T, map(F, catch_body(T)));
+ binary ->
+ update_c_binary(T, map_list(F, binary_segments(T)));
+ bitstr ->
+ update_c_bitstr(T, map(F, bitstr_val(T)),
+ map(F, bitstr_size(T)),
+ map(F, bitstr_unit(T)),
+ map(F, bitstr_type(T)),
+ map(F, bitstr_flags(T)));
+ letrec ->
+ update_c_letrec(T, map_pairs(F, letrec_defs(T)),
+ map(F, letrec_body(T)));
+ module ->
+ update_c_module(T, map(F, module_name(T)),
+ map_list(F, module_exports(T)),
+ map_pairs(F, module_attrs(T)),
+ map_pairs(F, module_defs(T)))
+ end.
+
+map_list(F, [T | Ts]) ->
+ [map(F, T) | map_list(F, Ts)];
+map_list(_, []) ->
+ [].
+
+map_pairs(F, [{T1, T2} | Ps]) ->
+ [{map(F, T1), map(F, T2)} | map_pairs(F, Ps)];
+map_pairs(_, []) ->
+ [].
+
+
+%% @spec fold(Function, Unit::term(), Tree::cerl()) -> term()
+%%
+%% Function = (cerl(), term()) -> term()
+%%
+%% @doc Does a fold operation over the nodes of the tree. The result
+%% is the value of <code>Function(X1, Function(X2, ... Function(Xn,
+%% Unit) ... ))</code>, where <code>X1, ..., Xn</code> are the nodes
+%% of <code>Tree</code> in a post-order traversal.
+%%
+%% @see mapfold/3
+
+fold(F, S, T) ->
+ F(T, fold_1(F, S, T)).
+
+fold_1(F, S, T) ->
+ case type(T) of
+ literal ->
+ case concrete(T) of
+ [_ | _] ->
+ fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
+ V when tuple_size(V) > 0 ->
+ fold_list(F, S, tuple_es(T));
+ _ ->
+ S
+ end;
+ var ->
+ S;
+ values ->
+ fold_list(F, S, values_es(T));
+ cons ->
+ fold(F, fold(F, S, cons_hd(T)), cons_tl(T));
+ tuple ->
+ fold_list(F, S, tuple_es(T));
+ 'let' ->
+ fold(F, fold(F, fold_list(F, S, let_vars(T)),
+ let_arg(T)),
+ let_body(T));
+ seq ->
+ fold(F, fold(F, S, seq_arg(T)), seq_body(T));
+ apply ->
+ fold_list(F, fold(F, S, apply_op(T)), apply_args(T));
+ call ->
+ fold_list(F, fold(F, fold(F, S, call_module(T)),
+ call_name(T)),
+ call_args(T));
+ primop ->
+ fold_list(F, fold(F, S, primop_name(T)), primop_args(T));
+ 'case' ->
+ fold_list(F, fold(F, S, case_arg(T)), case_clauses(T));
+ clause ->
+ fold(F, fold(F, fold_list(F, S, clause_pats(T)),
+ clause_guard(T)),
+ clause_body(T));
+ alias ->
+ fold(F, fold(F, S, alias_var(T)), alias_pat(T));
+ 'fun' ->
+ fold(F, fold_list(F, S, fun_vars(T)), fun_body(T));
+ 'receive' ->
+ fold(F, fold(F, fold_list(F, S, receive_clauses(T)),
+ receive_timeout(T)),
+ receive_action(T));
+ 'try' ->
+ fold(F, fold_list(F, fold(F, fold_list(F, fold(F, S, try_arg(T)),
+ try_vars(T)),
+ try_body(T)),
+ try_evars(T)),
+ try_handler(T));
+ 'catch' ->
+ fold(F, S, catch_body(T));
+ binary ->
+ fold_list(F, S, binary_segments(T));
+ bitstr ->
+ fold(F,
+ fold(F,
+ fold(F,
+ fold(F,
+ fold(F, S, bitstr_val(T)),
+ bitstr_size(T)),
+ bitstr_unit(T)),
+ bitstr_type(T)),
+ bitstr_flags(T));
+ letrec ->
+ fold(F, fold_pairs(F, S, letrec_defs(T)), letrec_body(T));
+ module ->
+ fold_pairs(F,
+ fold_pairs(F,
+ fold_list(F,
+ fold(F, S, module_name(T)),
+ module_exports(T)),
+ module_attrs(T)),
+ module_defs(T))
+ end.
+
+fold_list(F, S, [T | Ts]) ->
+ fold_list(F, fold(F, S, T), Ts);
+fold_list(_, S, []) ->
+ S.
+
+fold_pairs(F, S, [{T1, T2} | Ps]) ->
+ fold_pairs(F, fold(F, fold(F, S, T1), T2), Ps);
+fold_pairs(_, S, []) ->
+ S.
+
+
+%% @spec mapfold(Function, Initial::term(), Tree::cerl()) ->
+%% {cerl(), term()}
+%%
+%% Function = (cerl(), term()) -> {cerl(), term()}
+%%
+%% @doc Does a combined map/fold operation on the nodes of the
+%% tree. This is similar to <code>map/2</code>, but also propagates a
+%% value from each application of <code>Function</code> to the next,
+%% starting with the given value <code>Initial</code>, while doing a
+%% post-order traversal of the tree, much like <code>fold/3</code>.
+%%
+%% @see map/2
+%% @see fold/3
+
+mapfold(F, S0, T) ->
+ case type(T) of
+ literal ->
+ case concrete(T) of
+ [_ | _] ->
+ {T1, S1} = mapfold(F, S0, cons_hd(T)),
+ {T2, S2} = mapfold(F, S1, cons_tl(T)),
+ F(update_c_cons(T, T1, T2), S2);
+ V when tuple_size(V) > 0 ->
+ {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
+ F(update_c_tuple(T, Ts), S1);
+ _ ->
+ F(T, S0)
+ end;
+ var ->
+ F(T, S0);
+ values ->
+ {Ts, S1} = mapfold_list(F, S0, values_es(T)),
+ F(update_c_values(T, Ts), S1);
+ cons ->
+ {T1, S1} = mapfold(F, S0, cons_hd(T)),
+ {T2, S2} = mapfold(F, S1, cons_tl(T)),
+ F(update_c_cons_skel(T, T1, T2), S2);
+ tuple ->
+ {Ts, S1} = mapfold_list(F, S0, tuple_es(T)),
+ F(update_c_tuple_skel(T, Ts), S1);
+ 'let' ->
+ {Vs, S1} = mapfold_list(F, S0, let_vars(T)),
+ {A, S2} = mapfold(F, S1, let_arg(T)),
+ {B, S3} = mapfold(F, S2, let_body(T)),
+ F(update_c_let(T, Vs, A, B), S3);
+ seq ->
+ {A, S1} = mapfold(F, S0, seq_arg(T)),
+ {B, S2} = mapfold(F, S1, seq_body(T)),
+ F(update_c_seq(T, A, B), S2);
+ apply ->
+ {E, S1} = mapfold(F, S0, apply_op(T)),
+ {As, S2} = mapfold_list(F, S1, apply_args(T)),
+ F(update_c_apply(T, E, As), S2);
+ call ->
+ {M, S1} = mapfold(F, S0, call_module(T)),
+ {N, S2} = mapfold(F, S1, call_name(T)),
+ {As, S3} = mapfold_list(F, S2, call_args(T)),
+ F(update_c_call(T, M, N, As), S3);
+ primop ->
+ {N, S1} = mapfold(F, S0, primop_name(T)),
+ {As, S2} = mapfold_list(F, S1, primop_args(T)),
+ F(update_c_primop(T, N, As), S2);
+ 'case' ->
+ {A, S1} = mapfold(F, S0, case_arg(T)),
+ {Cs, S2} = mapfold_list(F, S1, case_clauses(T)),
+ F(update_c_case(T, A, Cs), S2);
+ clause ->
+ {Ps, S1} = mapfold_list(F, S0, clause_pats(T)),
+ {G, S2} = mapfold(F, S1, clause_guard(T)),
+ {B, S3} = mapfold(F, S2, clause_body(T)),
+ F(update_c_clause(T, Ps, G, B), S3);
+ alias ->
+ {V, S1} = mapfold(F, S0, alias_var(T)),
+ {P, S2} = mapfold(F, S1, alias_pat(T)),
+ F(update_c_alias(T, V, P), S2);
+ 'fun' ->
+ {Vs, S1} = mapfold_list(F, S0, fun_vars(T)),
+ {B, S2} = mapfold(F, S1, fun_body(T)),
+ F(update_c_fun(T, Vs, B), S2);
+ 'receive' ->
+ {Cs, S1} = mapfold_list(F, S0, receive_clauses(T)),
+ {E, S2} = mapfold(F, S1, receive_timeout(T)),
+ {A, S3} = mapfold(F, S2, receive_action(T)),
+ F(update_c_receive(T, Cs, E, A), S3);
+ 'try' ->
+ {E, S1} = mapfold(F, S0, try_arg(T)),
+ {Vs, S2} = mapfold_list(F, S1, try_vars(T)),
+ {B, S3} = mapfold(F, S2, try_body(T)),
+ {Evs, S4} = mapfold_list(F, S3, try_evars(T)),
+ {H, S5} = mapfold(F, S4, try_handler(T)),
+ F(update_c_try(T, E, Vs, B, Evs, H), S5);
+ 'catch' ->
+ {B, S1} = mapfold(F, S0, catch_body(T)),
+ F(update_c_catch(T, B), S1);
+ binary ->
+ {Ds, S1} = mapfold_list(F, S0, binary_segments(T)),
+ F(update_c_binary(T, Ds), S1);
+ bitstr ->
+ {Val, S1} = mapfold(F, S0, bitstr_val(T)),
+ {Size, S2} = mapfold(F, S1, bitstr_size(T)),
+ {Unit, S3} = mapfold(F, S2, bitstr_unit(T)),
+ {Type, S4} = mapfold(F, S3, bitstr_type(T)),
+ {Flags, S5} = mapfold(F, S4, bitstr_flags(T)),
+ F(update_c_bitstr(T, Val, Size, Unit, Type, Flags), S5);
+ letrec ->
+ {Ds, S1} = mapfold_pairs(F, S0, letrec_defs(T)),
+ {B, S2} = mapfold(F, S1, letrec_body(T)),
+ F(update_c_letrec(T, Ds, B), S2);
+ module ->
+ {N, S1} = mapfold(F, S0, module_name(T)),
+ {Es, S2} = mapfold_list(F, S1, module_exports(T)),
+ {As, S3} = mapfold_pairs(F, S2, module_attrs(T)),
+ {Ds, S4} = mapfold_pairs(F, S3, module_defs(T)),
+ F(update_c_module(T, N, Es, As, Ds), S4)
+ end.
+
+mapfold_list(F, S0, [T | Ts]) ->
+ {T1, S1} = mapfold(F, S0, T),
+ {Ts1, S2} = mapfold_list(F, S1, Ts),
+ {[T1 | Ts1], S2};
+mapfold_list(_, S, []) ->
+ {[], S}.
+
+mapfold_pairs(F, S0, [{T1, T2} | Ps]) ->
+ {T3, S1} = mapfold(F, S0, T1),
+ {T4, S2} = mapfold(F, S1, T2),
+ {Ps1, S3} = mapfold_pairs(F, S2, Ps),
+ {[{T3, T4} | Ps1], S3};
+mapfold_pairs(_, S, []) ->
+ {[], S}.
+
+
+%% ---------------------------------------------------------------------
+
+%% @spec variables(Tree::cerl()) -> [var_name()]
+%%
+%% var_name() = integer() | atom() | {atom(), integer()}
+%%
+%% @doc Returns an ordered-set list of the names of all variables in
+%% the syntax tree. (This includes function name variables.) An
+%% exception is thrown if <code>Tree</code> does not represent a
+%% well-formed Core Erlang syntax tree.
+%%
+%% @see free_variables/1
+
+variables(T) ->
+ variables(T, false).
+
+
+%% @spec free_variables(Tree::cerl()) -> [var_name()]
+%%
+%% @doc Like <code>variables/1</code>, but only includes variables
+%% that are free in the tree.
+%%
+%% @see variables/1
+
+free_variables(T) ->
+ variables(T, true).
+
+
+%% This is not exported
+
+variables(T, S) ->
+ case type(T) of
+ literal ->
+ [];
+ var ->
+ [var_name(T)];
+ values ->
+ vars_in_list(values_es(T), S);
+ cons ->
+ ordsets:union(variables(cons_hd(T), S),
+ variables(cons_tl(T), S));
+ tuple ->
+ vars_in_list(tuple_es(T), S);
+ 'let' ->
+ Vs = variables(let_body(T), S),
+ Vs1 = var_list_names(let_vars(T)),
+ Vs2 = case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end,
+ ordsets:union(variables(let_arg(T), S), Vs2);
+ seq ->
+ ordsets:union(variables(seq_arg(T), S),
+ variables(seq_body(T), S));
+ apply ->
+ ordsets:union(
+ variables(apply_op(T), S),
+ vars_in_list(apply_args(T), S));
+ call ->
+ ordsets:union(variables(call_module(T), S),
+ ordsets:union(
+ variables(call_name(T), S),
+ vars_in_list(call_args(T), S)));
+ primop ->
+ vars_in_list(primop_args(T), S);
+ 'case' ->
+ ordsets:union(variables(case_arg(T), S),
+ vars_in_list(case_clauses(T), S));
+ clause ->
+ Vs = ordsets:union(variables(clause_guard(T), S),
+ variables(clause_body(T), S)),
+ Vs1 = vars_in_list(clause_pats(T), S),
+ case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end;
+ alias ->
+ ordsets:add_element(var_name(alias_var(T)),
+ variables(alias_pat(T)));
+ 'fun' ->
+ Vs = variables(fun_body(T), S),
+ Vs1 = var_list_names(fun_vars(T)),
+ case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end;
+ 'receive' ->
+ ordsets:union(
+ vars_in_list(receive_clauses(T), S),
+ ordsets:union(variables(receive_timeout(T), S),
+ variables(receive_action(T), S)));
+ 'try' ->
+ Vs = variables(try_body(T), S),
+ Vs1 = var_list_names(try_vars(T)),
+ Vs2 = case S of
+ true ->
+ ordsets:subtract(Vs, Vs1);
+ false ->
+ ordsets:union(Vs, Vs1)
+ end,
+ Vs3 = variables(try_handler(T), S),
+ Vs4 = var_list_names(try_evars(T)),
+ Vs5 = case S of
+ true ->
+ ordsets:subtract(Vs3, Vs4);
+ false ->
+ ordsets:union(Vs3, Vs4)
+ end,
+ ordsets:union(variables(try_arg(T), S),
+ ordsets:union(Vs2, Vs5));
+ 'catch' ->
+ variables(catch_body(T), S);
+ binary ->
+ vars_in_list(binary_segments(T), S);
+ bitstr ->
+ ordsets:union(variables(bitstr_val(T), S),
+ variables(bitstr_size(T), S));
+ letrec ->
+ Vs = vars_in_defs(letrec_defs(T), S),
+ Vs1 = ordsets:union(variables(letrec_body(T), S), Vs),
+ Vs2 = var_list_names(letrec_vars(T)),
+ case S of
+ true ->
+ ordsets:subtract(Vs1, Vs2);
+ false ->
+ ordsets:union(Vs1, Vs2)
+ end;
+ module ->
+ Vs = vars_in_defs(module_defs(T), S),
+ Vs1 = ordsets:union(vars_in_list(module_exports(T), S), Vs),
+ Vs2 = var_list_names(module_vars(T)),
+ case S of
+ true ->
+ ordsets:subtract(Vs1, Vs2);
+ false ->
+ ordsets:union(Vs1, Vs2)
+ end
+ end.
+
+vars_in_list(Ts, S) ->
+ vars_in_list(Ts, S, []).
+
+vars_in_list([T | Ts], S, A) ->
+ vars_in_list(Ts, S, ordsets:union(variables(T, S), A));
+vars_in_list([], _, A) ->
+ A.
+
+%% Note that this function only visits the right-hand side of function
+%% definitions.
+
+vars_in_defs(Ds, S) ->
+ vars_in_defs(Ds, S, []).
+
+vars_in_defs([{_, F} | Ds], S, A) ->
+ vars_in_defs(Ds, S, ordsets:union(variables(F, S), A));
+vars_in_defs([], _, A) ->
+ A.
+
+%% This amounts to insertion sort. Since the lists are generally short,
+%% it is hardly worthwhile to use an asymptotically better sort.
+
+var_list_names(Vs) ->
+ var_list_names(Vs, []).
+
+var_list_names([V | Vs], A) ->
+ var_list_names(Vs, ordsets:add_element(var_name(V), A));
+var_list_names([], A) ->
+ A.
+
+
+%% ---------------------------------------------------------------------
+
+%% label(Tree::cerl()) -> {cerl(), integer()}
+%%
+%% @equiv label(Tree, 0)
+
+label(T) ->
+ label(T, 0).
+
+%% @spec label(Tree::cerl(), N::integer()) -> {cerl(), integer()}
+%%
+%% @doc Labels each expression in the tree. A term <code>{label,
+%% L}</code> is prefixed to the annotation list of each expression node,
+%% where L is a unique number for every node, except for variables (and
+%% function name variables) which get the same label if they represent
+%% the same variable. Constant literal nodes are not labeled.
+%%
+%% <p>The returned value is a tuple <code>{NewTree, Max}</code>, where
+%% <code>NewTree</code> is the labeled tree and <code>Max</code> is 1
+%% plus the largest label value used. All previous annotation terms on
+%% the form <code>{label, X}</code> are deleted.</p>
+%%
+%% <p>The values of L used in the tree is a dense range from
+%% <code>N</code> to <code>Max - 1</code>, where <code>N =&lt; Max
+%% =&lt; N + size(Tree)</code>. Note that it is possible that no
+%% labels are used at all, i.e., <code>N = Max</code>.</p>
+%%
+%% <p>Note: All instances of free variables will be given distinct
+%% labels.</p>
+%%
+%% @see label/1
+%% @see size/1
+
+label(T, N) ->
+ label(T, N, dict:new()).
+
+label(T, N, Env) ->
+ case type(T) of
+ literal ->
+ %% Constant literals are not labeled.
+ {T, N};
+ var ->
+ case dict:find(var_name(T), Env) of
+ {ok, L} ->
+ {As, _} = label_ann(T, L),
+ N1 = N;
+ error ->
+ {As, N1} = label_ann(T, N)
+ end,
+ {set_ann(T, As), N1};
+ values ->
+ {Ts, N1} = label_list(values_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_values(As, Ts), N2};
+ cons ->
+ {T1, N1} = label(cons_hd(T), N, Env),
+ {T2, N2} = label(cons_tl(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_cons_skel(As, T1, T2), N3};
+ tuple ->
+ {Ts, N1} = label_list(tuple_es(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_tuple_skel(As, Ts), N2};
+ 'let' ->
+ {A, N1} = label(let_arg(T), N, Env),
+ {Vs, N2, Env1} = label_vars(let_vars(T), N1, Env),
+ {B, N3} = label(let_body(T), N2, Env1),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_let(As, Vs, A, B), N4};
+ seq ->
+ {A, N1} = label(seq_arg(T), N, Env),
+ {B, N2} = label(seq_body(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_seq(As, A, B), N3};
+ apply ->
+ {E, N1} = label(apply_op(T), N, Env),
+ {Es, N2} = label_list(apply_args(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_apply(As, E, Es), N3};
+ call ->
+ {M, N1} = label(call_module(T), N, Env),
+ {F, N2} = label(call_name(T), N1, Env),
+ {Es, N3} = label_list(call_args(T), N2, Env),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_call(As, M, F, Es), N4};
+ primop ->
+ {F, N1} = label(primop_name(T), N, Env),
+ {Es, N2} = label_list(primop_args(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_primop(As, F, Es), N3};
+ 'case' ->
+ {A, N1} = label(case_arg(T), N, Env),
+ {Cs, N2} = label_list(case_clauses(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_case(As, A, Cs), N3};
+ clause ->
+ {_, N1, Env1} = label_vars(clause_vars(T), N, Env),
+ {Ps, N2} = label_list(clause_pats(T), N1, Env1),
+ {G, N3} = label(clause_guard(T), N2, Env1),
+ {B, N4} = label(clause_body(T), N3, Env1),
+ {As, N5} = label_ann(T, N4),
+ {ann_c_clause(As, Ps, G, B), N5};
+ alias ->
+ {V, N1} = label(alias_var(T), N, Env),
+ {P, N2} = label(alias_pat(T), N1, Env),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_alias(As, V, P), N3};
+ 'fun' ->
+ {Vs, N1, Env1} = label_vars(fun_vars(T), N, Env),
+ {B, N2} = label(fun_body(T), N1, Env1),
+ {As, N3} = label_ann(T, N2),
+ {ann_c_fun(As, Vs, B), N3};
+ 'receive' ->
+ {Cs, N1} = label_list(receive_clauses(T), N, Env),
+ {E, N2} = label(receive_timeout(T), N1, Env),
+ {A, N3} = label(receive_action(T), N2, Env),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_receive(As, Cs, E, A), N4};
+ 'try' ->
+ {E, N1} = label(try_arg(T), N, Env),
+ {Vs, N2, Env1} = label_vars(try_vars(T), N1, Env),
+ {B, N3} = label(try_body(T), N2, Env1),
+ {Evs, N4, Env2} = label_vars(try_evars(T), N3, Env),
+ {H, N5} = label(try_handler(T), N4, Env2),
+ {As, N6} = label_ann(T, N5),
+ {ann_c_try(As, E, Vs, B, Evs, H), N6};
+ 'catch' ->
+ {B, N1} = label(catch_body(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_catch(As, B), N2};
+ binary ->
+ {Ds, N1} = label_list(binary_segments(T), N, Env),
+ {As, N2} = label_ann(T, N1),
+ {ann_c_binary(As, Ds), N2};
+ bitstr ->
+ {Val, N1} = label(bitstr_val(T), N, Env),
+ {Size, N2} = label(bitstr_size(T), N1, Env),
+ {Unit, N3} = label(bitstr_unit(T), N2, Env),
+ {Type, N4} = label(bitstr_type(T), N3, Env),
+ {Flags, N5} = label(bitstr_flags(T), N4, Env),
+ {As, N6} = label_ann(T, N5),
+ {ann_c_bitstr(As, Val, Size, Unit, Type, Flags), N6};
+ letrec ->
+ {_, N1, Env1} = label_vars(letrec_vars(T), N, Env),
+ {Ds, N2} = label_defs(letrec_defs(T), N1, Env1),
+ {B, N3} = label(letrec_body(T), N2, Env1),
+ {As, N4} = label_ann(T, N3),
+ {ann_c_letrec(As, Ds, B), N4};
+ module ->
+ %% The module name is not labeled.
+ {_, N1, Env1} = label_vars(module_vars(T), N, Env),
+ {Ts, N2} = label_defs(module_attrs(T), N1, Env1),
+ {Ds, N3} = label_defs(module_defs(T), N2, Env1),
+ {Es, N4} = label_list(module_exports(T), N3, Env1),
+ {As, N5} = label_ann(T, N4),
+ {ann_c_module(As, module_name(T), Es, Ts, Ds), N5}
+ end.
+
+label_list([T | Ts], N, Env) ->
+ {T1, N1} = label(T, N, Env),
+ {Ts1, N2} = label_list(Ts, N1, Env),
+ {[T1 | Ts1], N2};
+label_list([], N, _Env) ->
+ {[], N}.
+
+label_vars([T | Ts], N, Env) ->
+ Env1 = dict:store(var_name(T), N, Env),
+ {As, N1} = label_ann(T, N),
+ T1 = set_ann(T, As),
+ {Ts1, N2, Env2} = label_vars(Ts, N1, Env1),
+ {[T1 | Ts1], N2, Env2};
+label_vars([], N, Env) ->
+ {[], N, Env}.
+
+label_defs([{F, T} | Ds], N, Env) ->
+ {F1, N1} = label(F, N, Env),
+ {T1, N2} = label(T, N1, Env),
+ {Ds1, N3} = label_defs(Ds, N2, Env),
+ {[{F1, T1} | Ds1], N3};
+label_defs([], N, _Env) ->
+ {[], N}.
+
+label_ann(T, N) ->
+ {[{label, N} | filter_labels(get_ann(T))], N + 1}.
+
+filter_labels([{label, _} | As]) ->
+ filter_labels(As);
+filter_labels([A | As]) ->
+ [A | filter_labels(As)];
+filter_labels([]) ->
+ [].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl
new file mode 100644
index 0000000000..4542bf9eb9
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/compile.erl
@@ -0,0 +1,1109 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: compile.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose: Run the Erlang compiler.
+
+-module(compile).
+-include("erl_compile.hrl").
+-include("core_parse.hrl").
+
+%% High-level interface.
+-export([file/1,file/2,format_error/1,iofile/1]).
+-export([forms/1,forms/2]).
+-export([output_generated/1]).
+-export([options/0]).
+
+%% Erlc interface.
+-export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]).
+
+
+-import(lists, [member/2,reverse/1,keysearch/3,last/1,
+ map/2,flatmap/2,foreach/2,foldr/3,any/2,filter/2]).
+
+%% file(FileName)
+%% file(FileName, Options)
+%% Compile the module in file FileName.
+
+-define(DEFAULT_OPTIONS, [verbose,report_errors,report_warnings]).
+
+-define(pass(P), {P,fun P/1}).
+
+file(File) -> file(File, ?DEFAULT_OPTIONS).
+
+file(File, Opts) when list(Opts) ->
+ do_compile({file,File}, Opts++env_default_opts());
+file(File, Opt) ->
+ file(File, [Opt|?DEFAULT_OPTIONS]).
+
+forms(File) -> forms(File, ?DEFAULT_OPTIONS).
+
+forms(Forms, Opts) when list(Opts) ->
+ do_compile({forms,Forms}, [binary|Opts++env_default_opts()]);
+forms(Forms, Opts) when atom(Opts) ->
+ forms(Forms, [Opts|?DEFAULT_OPTIONS]).
+
+env_default_opts() ->
+ Key = "ERL_COMPILER_OPTIONS",
+ case os:getenv(Key) of
+ false -> [];
+ Str when list(Str) ->
+ case erl_scan:string(Str) of
+ {ok,Tokens,_} ->
+ case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
+ {ok,List} when list(List) -> List;
+ {ok,Term} -> [Term];
+ {error,_Reason} ->
+ io:format("Ignoring bad term in ~s\n", [Key]),
+ []
+ end;
+ {error, {_,_,_Reason}, _} ->
+ io:format("Ignoring bad term in ~s\n", [Key]),
+ []
+ end
+ end.
+
+do_compile(Input, Opts0) ->
+ Opts = expand_opts(Opts0),
+ Self = self(),
+ Serv = spawn_link(fun() -> internal(Self, Input, Opts) end),
+ receive
+ {Serv,Rep} -> Rep
+ end.
+
+%% Given a list of compilation options, returns true if compile:file/2
+%% would have generated a Beam file, false otherwise (if only a binary or a
+%% listing file would have been generated).
+
+output_generated(Opts) ->
+ any(fun ({save_binary,_F}) -> true;
+ (_Other) -> false
+ end, passes(file, expand_opts(Opts))).
+
+expand_opts(Opts) ->
+ foldr(fun expand_opt/2, [], Opts).
+
+expand_opt(basic_validation, Os) ->
+ [no_code_generation,to_pp,binary|Os];
+expand_opt(strong_validation, Os) ->
+ [no_code_generation,to_kernel,binary|Os];
+expand_opt(report, Os) ->
+ [report_errors,report_warnings|Os];
+expand_opt(return, Os) ->
+ [return_errors,return_warnings|Os];
+expand_opt(r7, Os) ->
+ [no_float_opt,no_new_funs,no_new_binaries,no_new_apply|Os];
+expand_opt(O, Os) -> [O|Os].
+
+filter_opts(Opts0) ->
+ %% Native code generation is not supported if no_new_funs is given.
+ case member(no_new_funs, Opts0) of
+ false -> Opts0;
+ true -> Opts0 -- [native]
+ end.
+
+%% format_error(ErrorDescriptor) -> string()
+
+format_error(no_native_support) ->
+ "this system is not configured for native-code compilation.";
+format_error({native, E}) ->
+ io_lib:fwrite("native-code compilation failed with reason: ~P.",
+ [E, 25]);
+format_error({native_crash, E}) ->
+ io_lib:fwrite("native-code compilation crashed with reason: ~P.",
+ [E, 25]);
+format_error({open,E}) ->
+ io_lib:format("open error '~s'", [file:format_error(E)]);
+format_error({epp,E}) ->
+ epp:format_error(E);
+format_error(write_error) ->
+ "error writing file";
+format_error({rename,S}) ->
+ io_lib:format("error renaming ~s", [S]);
+format_error({parse_transform,M,R}) ->
+ io_lib:format("error in parse transform '~s': ~p", [M, R]);
+format_error({core_transform,M,R}) ->
+ io_lib:format("error in core transform '~s': ~p", [M, R]);
+format_error({crash,Pass,Reason}) ->
+ io_lib:format("internal error in ~p;\ncrash reason: ~p", [Pass,Reason]);
+format_error({bad_return,Pass,Reason}) ->
+ io_lib:format("internal error in ~p;\nbad return value: ~p", [Pass,Reason]).
+
+%% The compile state record.
+-record(compile, {filename="",
+ dir="",
+ base="",
+ ifile="",
+ ofile="",
+ module=[],
+ code=[],
+ core_code=[],
+ abstract_code=[], %Abstract code for debugger.
+ options=[],
+ errors=[],
+ warnings=[]}).
+
+internal(Master, Input, Opts) ->
+ Master ! {self(),
+ case catch internal(Input, Opts) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Other ->
+ Other
+ end}.
+
+internal({forms,Forms}, Opts) ->
+ Ps = passes(forms, Opts),
+ internal_comp(Ps, "", "", #compile{code=Forms,options=Opts});
+internal({file,File}, Opts) ->
+ Ps = passes(file, Opts),
+ Compile = #compile{options=Opts},
+ case member(from_core, Opts) of
+ true -> internal_comp(Ps, File, ".core", Compile);
+ false ->
+ case member(from_beam, Opts) of
+ true ->
+ internal_comp(Ps, File, ".beam", Compile);
+ false ->
+ case member(from_asm, Opts) orelse member(asm, Opts) of
+ true ->
+ internal_comp(Ps, File, ".S", Compile);
+ false ->
+ internal_comp(Ps, File, ".erl", Compile)
+ end
+ end
+ end.
+
+internal_comp(Passes, File, Suffix, St0) ->
+ Dir = filename:dirname(File),
+ Base = filename:basename(File, Suffix),
+ St1 = St0#compile{filename=File, dir=Dir, base=Base,
+ ifile=erlfile(Dir, Base, Suffix),
+ ofile=objfile(Base, St0)},
+ Run = case member(time, St1#compile.options) of
+ true ->
+ io:format("Compiling ~p\n", [File]),
+ fun run_tc/2;
+ false -> fun({_Name,Fun}, St) -> catch Fun(St) end
+ end,
+ case fold_comp(Passes, Run, St1) of
+ {ok,St2} -> comp_ret_ok(St2);
+ {error,St2} -> comp_ret_err(St2)
+ end.
+
+fold_comp([{Name,Test,Pass}|Ps], Run, St) ->
+ case Test(St) of
+ false -> %Pass is not needed.
+ fold_comp(Ps, Run, St);
+ true -> %Run pass in the usual way.
+ fold_comp([{Name,Pass}|Ps], Run, St)
+ end;
+fold_comp([{Name,Pass}|Ps], Run, St0) ->
+ case Run({Name,Pass}, St0) of
+ {ok,St1} -> fold_comp(Ps, Run, St1);
+ {error,St1} -> {error,St1};
+ {'EXIT',Reason} ->
+ Es = [{St0#compile.ifile,[{none,?MODULE,{crash,Name,Reason}}]}],
+ {error,St0#compile{errors=St0#compile.errors ++ Es}};
+ Other ->
+ Es = [{St0#compile.ifile,[{none,?MODULE,{bad_return,Name,Other}}]}],
+ {error,St0#compile{errors=St0#compile.errors ++ Es}}
+ end;
+fold_comp([], _Run, St) -> {ok,St}.
+
+os_process_size() ->
+ case os:type() of
+ {unix, sunos} ->
+ Size = os:cmd("ps -o vsz -p " ++ os:getpid() ++ " | tail -1"),
+ list_to_integer(lib:nonl(Size));
+ _ ->
+ 0
+ end.
+
+run_tc({Name,Fun}, St) ->
+ Before0 = statistics(runtime),
+ Val = (catch Fun(St)),
+ After0 = statistics(runtime),
+ {Before_c, _} = Before0,
+ {After_c, _} = After0,
+ io:format(" ~-30s: ~10.3f s (~w k)\n",
+ [Name, (After_c-Before_c) / 1000, os_process_size()]),
+ Val.
+
+comp_ret_ok(#compile{code=Code,warnings=Warn,module=Mod,options=Opts}=St) ->
+ report_warnings(St),
+ Ret1 = case member(binary, Opts) andalso not member(no_code_generation, Opts) of
+ true -> [Code];
+ false -> []
+ end,
+ Ret2 = case member(return_warnings, Opts) of
+ true -> Ret1 ++ [Warn];
+ false -> Ret1
+ end,
+ list_to_tuple([ok,Mod|Ret2]).
+
+comp_ret_err(St) ->
+ report_errors(St),
+ report_warnings(St),
+ case member(return_errors, St#compile.options) of
+ true -> {error,St#compile.errors,St#compile.warnings};
+ false -> error
+ end.
+
+%% passes(form|file, [Option]) -> [{Name,PassFun}]
+%% Figure out which passes that need to be run.
+
+passes(forms, Opts) ->
+ select_passes(standard_passes(), Opts);
+passes(file, Opts) ->
+ case member(from_beam, Opts) of
+ true ->
+ Ps = [?pass(read_beam_file)|binary_passes()],
+ select_passes(Ps, Opts);
+ false ->
+ Ps = case member(from_asm, Opts) orelse member(asm, Opts) of
+ true ->
+ [?pass(beam_consult_asm)|asm_passes()];
+ false ->
+ case member(from_core, Opts) of
+ true ->
+ [?pass(parse_core)|core_passes()];
+ false ->
+ [?pass(parse_module)|standard_passes()]
+ end
+ end,
+ Fs = select_passes(Ps, Opts),
+
+ %% If the last pass saves the resulting binary to a file,
+ %% insert a first pass to remove the file.
+ case last(Fs) of
+ {save_binary,_Fun} -> [?pass(remove_file)|Fs];
+ _Other -> Fs
+ end
+ end.
+
+%% select_passes([Command], Opts) -> [{Name,Function}]
+%% Interpret the lists of commands to return a pure list of passes.
+%%
+%% Command can be one of:
+%%
+%% {pass,Mod} Will be expanded to a call to the external
+%% function Mod:module(Code, Options). This
+%% function must transform the code and return
+%% {ok,NewCode} or {error,Term}.
+%% Example: {pass,beam_codegen}
+%%
+%% {Name,Fun} Name is an atom giving the name of the pass.
+%% Fun is an 'fun' taking one argument: a compile record.
+%% The fun should return {ok,NewCompileRecord} or
+%% {error,NewCompileRecord}.
+%% Note: ?pass(Name) is equvivalent to {Name,fun Name/1}.
+%% Example: ?pass(parse_module)
+%%
+%% {Name,Test,Fun} Like {Name,Fun} above, but the pass will be run
+%% (and listed by the `time' option) only if Test(St)
+%% returns true.
+%%
+%% {src_listing,Ext} Produces an Erlang source listing with the
+%% the file extension Ext. (Ext should not contain
+%% a period.) No more passes will be run.
+%%
+%% {listing,Ext} Produce an listing of the terms in the internal
+%% representation. The extension of the listing
+%% file will be Ext. (Ext should not contain
+%% a period.) No more passes will be run.
+%%
+%% {done,Ext} End compilation at this point. Produce a listing
+%% as with {listing,Ext}, unless 'binary' is
+%% specified, in which case the current
+%% representation of the code is returned without
+%% creating an output file.
+%%
+%% {iff,Flag,Cmd} If the given Flag is given in the option list,
+%% Cmd will be interpreted as a command.
+%% Otherwise, Cmd will be ignored.
+%% Example: {iff,dcg,{listing,"codegen}}
+%%
+%% {unless,Flag,Cmd} If the given Flag is NOT given in the option list,
+%% Cmd will be interpreted as a command.
+%% Otherwise, Cmd will be ignored.
+%% Example: {unless,no_kernopt,{pass,sys_kernopt}}
+%%
+
+select_passes([{pass,Mod}|Ps], Opts) ->
+ F = fun(St) ->
+ case catch Mod:module(St#compile.code, St#compile.options) of
+ {ok,Code} ->
+ {ok,St#compile{code=Code}};
+ {error,Es} ->
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end
+ end,
+ [{Mod,F}|select_passes(Ps, Opts)];
+select_passes([{src_listing,Ext}|_], _Opts) ->
+ [{listing,fun (St) -> src_listing(Ext, St) end}];
+select_passes([{listing,Ext}|_], _Opts) ->
+ [{listing,fun (St) -> listing(Ext, St) end}];
+select_passes([{done,Ext}|_], Opts) ->
+ select_passes([{unless,binary,{listing,Ext}}], Opts);
+select_passes([{iff,Flag,Pass}|Ps], Opts) ->
+ select_cond(Flag, true, Pass, Ps, Opts);
+select_passes([{unless,Flag,Pass}|Ps], Opts) ->
+ select_cond(Flag, false, Pass, Ps, Opts);
+select_passes([{_,Fun}=P|Ps], Opts) when is_function(Fun) ->
+ [P|select_passes(Ps, Opts)];
+select_passes([{_,Test,Fun}=P|Ps], Opts) when is_function(Test),
+ is_function(Fun) ->
+ [P|select_passes(Ps, Opts)];
+select_passes([], _Opts) ->
+ [];
+select_passes([List|Ps], Opts) when is_list(List) ->
+ case select_passes(List, Opts) of
+ [] -> select_passes(Ps, Opts);
+ Nested ->
+ case last(Nested) of
+ {listing,_Fun} -> Nested;
+ _Other -> Nested ++ select_passes(Ps, Opts)
+ end
+ end.
+
+select_cond(Flag, ShouldBe, Pass, Ps, Opts) ->
+ ShouldNotBe = not ShouldBe,
+ case member(Flag, Opts) of
+ ShouldBe -> select_passes([Pass|Ps], Opts);
+ ShouldNotBe -> select_passes(Ps, Opts)
+ end.
+
+%% The standard passes (almost) always run.
+
+standard_passes() ->
+ [?pass(transform_module),
+ {iff,'dpp',{listing,"pp"}},
+ ?pass(lint_module),
+ {iff,'P',{src_listing,"P"}},
+ {iff,'to_pp',{done,"P"}},
+
+ {iff,'dabstr',{listing,"abstr"}},
+ {iff,debug_info,?pass(save_abstract_code)},
+
+ ?pass(expand_module),
+ {iff,'dexp',{listing,"expand"}},
+ {iff,'E',{src_listing,"E"}},
+ {iff,'to_exp',{done,"E"}},
+
+ %% Conversion to Core Erlang.
+ ?pass(core_module),
+ {iff,'dcore',{listing,"core"}},
+ {iff,'to_core0',{done,"core"}}
+ | core_passes()].
+
+core_passes() ->
+ %% Optimization and transforms of Core Erlang code.
+ [{unless,no_copt,
+ [{core_old_inliner,fun test_old_inliner/1,fun core_old_inliner/1},
+ ?pass(core_fold_module),
+ {core_inline_module,fun test_core_inliner/1,fun core_inline_module/1},
+ {core_fold_after_inline,fun test_core_inliner/1,fun core_fold_module/1},
+ ?pass(core_transforms)]},
+ {iff,dcopt,{listing,"copt"}},
+ {iff,'to_core',{done,"core"}}
+ | kernel_passes()].
+
+kernel_passes() ->
+ %% Destructive setelement/3 optimization and core lint.
+ [?pass(core_dsetel_module),
+ {iff,clint,?pass(core_lint_module)},
+ {iff,core,?pass(save_core_code)},
+
+ %% Kernel Erlang and code generation.
+ ?pass(kernel_module),
+ {iff,dkern,{listing,"kernel"}},
+ {iff,'to_kernel',{done,"kernel"}},
+ {pass,v3_life},
+ {iff,dlife,{listing,"life"}},
+ {pass,v3_codegen},
+ {iff,dcg,{listing,"codegen"}}
+ | asm_passes()].
+
+asm_passes() ->
+ %% Assembly level optimisations.
+ [{unless,no_postopt,
+ [{pass,beam_block},
+ {iff,dblk,{listing,"block"}},
+ {unless,no_bopt,{pass,beam_bool}},
+ {iff,dbool,{listing,"bool"}},
+ {unless,no_topt,{pass,beam_type}},
+ {iff,dtype,{listing,"type"}},
+ {pass,beam_dead}, %Must always run since it splits blocks.
+ {iff,ddead,{listing,"dead"}},
+ {unless,no_jopt,{pass,beam_jump}},
+ {iff,djmp,{listing,"jump"}},
+ {pass,beam_clean},
+ {iff,dclean,{listing,"clean"}},
+ {pass,beam_flatten}]},
+
+ %% If post optimizations are turned off, we still coalesce
+ %% adjacent labels and remove unused labels to keep the
+ %% HiPE compiler happy.
+ {iff,no_postopt,
+ [?pass(beam_unused_labels),
+ {pass,beam_clean}]},
+
+ {iff,dopt,{listing,"optimize"}},
+ {iff,'S',{listing,"S"}},
+ {iff,'to_asm',{done,"S"}},
+
+ {pass,beam_validator},
+ ?pass(beam_asm)
+ | binary_passes()].
+
+binary_passes() ->
+ [{native_compile,fun test_native/1,fun native_compile/1},
+ {unless,binary,?pass(save_binary)}].
+
+%%%
+%%% Compiler passes.
+%%%
+
+%% Remove the target file so we don't have an old one if the compilation fail.
+remove_file(St) ->
+ file:delete(St#compile.ofile),
+ {ok,St}.
+
+-record(asm_module, {module,
+ exports,
+ labels,
+ functions=[],
+ cfun,
+ code,
+ attributes=[]}).
+
+preprocess_asm_forms(Forms) ->
+ R = #asm_module{},
+ R1 = collect_asm(Forms, R),
+ {R1#asm_module.module,
+ {R1#asm_module.module,
+ R1#asm_module.exports,
+ R1#asm_module.attributes,
+ R1#asm_module.functions,
+ R1#asm_module.labels}}.
+
+collect_asm([], R) ->
+ case R#asm_module.cfun of
+ undefined ->
+ R;
+ {A,B,C} ->
+ R#asm_module{functions=R#asm_module.functions++
+ [{function,A,B,C,R#asm_module.code}]}
+ end;
+collect_asm([{module,M} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{module=M});
+collect_asm([{exports,M} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{exports=M});
+collect_asm([{labels,M} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{labels=M});
+collect_asm([{function,A,B,C} | Rest], R) ->
+ R1 = case R#asm_module.cfun of
+ undefined ->
+ R;
+ {A0,B0,C0} ->
+ R#asm_module{functions=R#asm_module.functions++
+ [{function,A0,B0,C0,R#asm_module.code}]}
+ end,
+ collect_asm(Rest, R1#asm_module{cfun={A,B,C}, code=[]});
+collect_asm([{attributes, Attr} | Rest], R) ->
+ collect_asm(Rest, R#asm_module{attributes=Attr});
+collect_asm([X | Rest], R) ->
+ collect_asm(Rest, R#asm_module{code=R#asm_module.code++[X]}).
+
+beam_consult_asm(St) ->
+ case file:consult(St#compile.ifile) of
+ {ok, Forms0} ->
+ {Module, Forms} = preprocess_asm_forms(Forms0),
+ {ok,St#compile{module=Module, code=Forms}};
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+read_beam_file(St) ->
+ case file:read_file(St#compile.ifile) of
+ {ok,Beam} ->
+ Infile = St#compile.ifile,
+ case is_too_old(Infile) of
+ true ->
+ {ok,St#compile{module=none,code=none}};
+ false ->
+ Mod0 = filename:rootname(filename:basename(Infile)),
+ Mod = list_to_atom(Mod0),
+ {ok,St#compile{module=Mod,code=Beam,ofile=Infile}}
+ end;
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{open,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+is_too_old(BeamFile) ->
+ case beam_lib:chunks(BeamFile, ["CInf"]) of
+ {ok,{_,[{"CInf",Term0}]}} ->
+ Term = binary_to_term(Term0),
+ Opts = proplists:get_value(options, Term, []),
+ lists:member(no_new_funs, Opts);
+ _ -> false
+ end.
+
+parse_module(St) ->
+ Opts = St#compile.options,
+ Cwd = ".",
+ IncludePath = [Cwd, St#compile.dir|inc_paths(Opts)],
+ Tab = ets:new(compiler__tab, [protected,named_table]),
+ ets:insert(Tab, {compiler_options,Opts}),
+ R = epp:parse_file(St#compile.ifile, IncludePath, pre_defs(Opts)),
+ ets:delete(Tab),
+ case R of
+ {ok,Forms} ->
+ {ok,St#compile{code=Forms}};
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{epp,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+parse_core(St) ->
+ case file:read_file(St#compile.ifile) of
+ {ok,Bin} ->
+ case core_scan:string(binary_to_list(Bin)) of
+ {ok,Toks,_} ->
+ case core_parse:parse(Toks) of
+ {ok,Mod} ->
+ Name = (Mod#c_module.name)#c_atom.val,
+ {ok,St#compile{module=Name,code=Mod}};
+ {error,E} ->
+ Es = [{St#compile.ifile,[E]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {error,E,_} ->
+ Es = [{St#compile.ifile,[E]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {error,E} ->
+ Es = [{St#compile.ifile,[{none,compile,{open,E}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+compile_options([{attribute,_L,compile,C}|Fs]) when is_list(C) ->
+ C ++ compile_options(Fs);
+compile_options([{attribute,_L,compile,C}|Fs]) ->
+ [C|compile_options(Fs)];
+compile_options([_F|Fs]) -> compile_options(Fs);
+compile_options([]) -> [].
+
+transforms(Os) -> [ M || {parse_transform,M} <- Os ].
+
+transform_module(St) ->
+ %% Extract compile options from code into options field.
+ Ts = transforms(St#compile.options ++ compile_options(St#compile.code)),
+ foldl_transform(St, Ts).
+
+foldl_transform(St, [T|Ts]) ->
+ Name = "transform " ++ atom_to_list(T),
+ Fun = fun(S) -> T:parse_transform(S#compile.code, S#compile.options) end,
+ Run = case member(time, St#compile.options) of
+ true -> fun run_tc/2;
+ false -> fun({_Name,F}, S) -> catch F(S) end
+ end,
+ case Run({Name, Fun}, St) of
+ {error,Es,Ws} ->
+ {error,St#compile{warnings=St#compile.warnings ++ Ws,
+ errors=St#compile.errors ++ Es}};
+ {'EXIT',R} ->
+ Es = [{St#compile.ifile,[{none,compile,{parse_transform,T,R}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}};
+ Forms ->
+ foldl_transform(St#compile{code=Forms}, Ts)
+ end;
+foldl_transform(St, []) -> {ok,St}.
+
+get_core_transforms(Opts) -> [M || {core_transform,M} <- Opts].
+
+core_transforms(St) ->
+ %% The options field holds the complete list of options at this
+
+ Ts = get_core_transforms(St#compile.options),
+ foldl_core_transforms(St, Ts).
+
+foldl_core_transforms(St, [T|Ts]) ->
+ Name = "core transform " ++ atom_to_list(T),
+ Fun = fun(S) -> T:core_transform(S#compile.code, S#compile.options) end,
+ Run = case member(time, St#compile.options) of
+ true -> fun run_tc/2;
+ false -> fun({_Name,F}, S) -> catch F(S) end
+ end,
+ case Run({Name, Fun}, St) of
+ {'EXIT',R} ->
+ Es = [{St#compile.ifile,[{none,compile,{core_transform,T,R}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}};
+ Forms ->
+ foldl_core_transforms(St#compile{code=Forms}, Ts)
+ end;
+foldl_core_transforms(St, []) -> {ok,St}.
+
+%%% Fetches the module name from a list of forms. The module attribute must
+%%% be present.
+get_module([{attribute,_,module,{M,_As}} | _]) -> M;
+get_module([{attribute,_,module,M} | _]) -> M;
+get_module([_ | Rest]) ->
+ get_module(Rest).
+
+%%% A #compile state is returned, where St.base has been filled in
+%%% with the module name from Forms, as a string, in case it wasn't
+%%% set in St (i.e., it was "").
+add_default_base(St, Forms) ->
+ F = St#compile.filename,
+ case F of
+ "" ->
+ M = get_module(Forms),
+ St#compile{base = atom_to_list(M)};
+ _ ->
+ St
+ end.
+
+lint_module(St) ->
+ case erl_lint:module(St#compile.code,
+ St#compile.ifile, St#compile.options) of
+ {ok,Ws} ->
+ %% Insert name of module as base name, if needed. This is
+ %% for compile:forms to work with listing files.
+ St1 = add_default_base(St, St#compile.code),
+ {ok,St1#compile{warnings=St1#compile.warnings ++ Ws}};
+ {error,Es,Ws} ->
+ {error,St#compile{warnings=St#compile.warnings ++ Ws,
+ errors=St#compile.errors ++ Es}}
+ end.
+
+core_lint_module(St) ->
+ case core_lint:module(St#compile.code, St#compile.options) of
+ {ok,Ws} ->
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ {error,Es,Ws} ->
+ {error,St#compile{warnings=St#compile.warnings ++ Ws,
+ errors=St#compile.errors ++ Es}}
+ end.
+
+%% expand_module(State) -> State'
+%% Do the common preprocessing of the input forms.
+
+expand_module(#compile{code=Code,options=Opts0}=St0) ->
+ {Mod,Exp,Forms,Opts1} = sys_pre_expand:module(Code, Opts0),
+ Opts2 = expand_opts(Opts1),
+ Opts = filter_opts(Opts2),
+ {ok,St0#compile{module=Mod,options=Opts,code={Mod,Exp,Forms}}}.
+
+core_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
+ {ok,Code,Ws} = v3_core:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
+
+core_fold_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
+ {ok,Code,Ws} = sys_core_fold:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
+
+test_old_inliner(#compile{options=Opts}) ->
+ %% The point of this test is to avoid loading the old inliner
+ %% if we know that it will not be used.
+ case any(fun(no_inline) -> true;
+ (_) -> false
+ end, Opts) of
+ true -> false;
+ false ->
+ any(fun({inline,_}) -> true;
+ (_) -> false
+ end, Opts)
+ end.
+
+test_core_inliner(#compile{options=Opts}) ->
+ case any(fun(no_inline) -> true;
+ (_) -> false
+ end, Opts) of
+ true -> false;
+ false ->
+ any(fun(inline) -> true;
+ (_) -> false
+ end, Opts)
+ end.
+
+core_old_inliner(#compile{code=Code0,options=Opts}=St) ->
+ case catch sys_core_inline:module(Code0, Opts) of
+ {ok,Code} ->
+ {ok,St#compile{code=Code}};
+ {error,Es} ->
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+core_inline_module(#compile{code=Code0,options=Opts}=St) ->
+ Code = cerl_inline:core_transform(Code0, Opts),
+ {ok,St#compile{code=Code}}.
+
+core_dsetel_module(#compile{code=Code0,options=Opts}=St) ->
+ {ok,Code} = sys_core_dsetel:module(Code0, Opts),
+ {ok,St#compile{code=Code}}.
+
+kernel_module(#compile{code=Code0,options=Opts,ifile=File}=St) ->
+ {ok,Code,Ws} = v3_kernel:module(Code0, Opts),
+ {ok,St#compile{code=Code,warnings=St#compile.warnings ++ [{File,Ws}]}}.
+
+save_abstract_code(St) ->
+ {ok,St#compile{abstract_code=abstract_code(St)}}.
+
+abstract_code(#compile{code=Code}) ->
+ Abstr = {raw_abstract_v1,Code},
+ case catch erlang:term_to_binary(Abstr, [compressed]) of
+ {'EXIT',_} -> term_to_binary(Abstr);
+ Other -> Other
+ end.
+
+save_core_code(St) ->
+ {ok,St#compile{core_code=cerl:from_records(St#compile.code)}}.
+
+beam_unused_labels(#compile{code=Code0}=St) ->
+ Code = beam_jump:module_labels(Code0),
+ {ok,St#compile{code=Code}}.
+
+beam_asm(#compile{ifile=File,code=Code0,abstract_code=Abst,options=Opts0}=St) ->
+ Source = filename:absname(File),
+ Opts = filter(fun is_informative_option/1, Opts0),
+ case beam_asm:module(Code0, Abst, Source, Opts) of
+ {ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}};
+ {error,Es} -> {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+test_native(#compile{options=Opts}) ->
+ %% This test must be made late, because the r7 or no_new_funs options
+ %% will turn off the native option.
+ member(native, Opts).
+
+native_compile(#compile{code=none}=St) -> {ok,St};
+native_compile(St) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ Ws = [{St#compile.ifile,[{none,compile,no_native_support}]}],
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ _ ->
+ native_compile_1(St)
+ end.
+
+native_compile_1(St) ->
+ Opts0 = [no_new_binaries|St#compile.options],
+ IgnoreErrors = member(ignore_native_errors, Opts0),
+ Opts = case keysearch(hipe, 1, Opts0) of
+ {value,{hipe,L}} when list(L) -> L;
+ {value,{hipe,X}} -> [X];
+ _ -> []
+ end,
+ case catch hipe:compile(St#compile.module,
+ St#compile.core_code,
+ St#compile.code,
+ Opts) of
+ {ok, {Type,Bin}} when binary(Bin) ->
+ {ok, embed_native_code(St, {Type,Bin})};
+ {error, R} ->
+ case IgnoreErrors of
+ true ->
+ Ws = [{St#compile.ifile,[{none,?MODULE,{native,R}}]}],
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ false ->
+ Es = [{St#compile.ifile,[{none,?MODULE,{native,R}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {'EXIT',R} ->
+ case IgnoreErrors of
+ true ->
+ Ws = [{St#compile.ifile,[{none,?MODULE,{native_crash,R}}]}],
+ {ok,St#compile{warnings=St#compile.warnings ++ Ws}};
+ false ->
+ exit(R)
+ end
+ end.
+
+embed_native_code(St, {Architecture,NativeCode}) ->
+ {ok, _, Chunks0} = beam_lib:all_chunks(St#compile.code),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ Chunks1 = lists:keydelete(ChunkName, 1, Chunks0),
+ Chunks = Chunks1 ++ [{ChunkName,NativeCode}],
+ {ok, BeamPlusNative} = beam_lib:build_module(Chunks),
+ St#compile{code=BeamPlusNative}.
+
+%% Returns true if the option is informative and therefore should be included
+%% in the option list of the compiled module.
+
+is_informative_option(beam) -> false;
+is_informative_option(report_warnings) -> false;
+is_informative_option(report_errors) -> false;
+is_informative_option(binary) -> false;
+is_informative_option(verbose) -> false;
+is_informative_option(_) -> true.
+
+save_binary(#compile{code=none}=St) -> {ok,St};
+save_binary(St) ->
+ Tfile = tmpfile(St#compile.ofile), %Temp working file
+ case write_binary(Tfile, St#compile.code, St) of
+ ok ->
+ case file:rename(Tfile, St#compile.ofile) of
+ ok ->
+ {ok,St};
+ {error,_Error} ->
+ file:delete(Tfile),
+ Es = [{St#compile.ofile,[{none,?MODULE,{rename,Tfile}}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end;
+ {error,_Error} ->
+ Es = [{Tfile,[{compile,write_error}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+write_binary(Name, Bin, St) ->
+ Opts = case member(compressed, St#compile.options) of
+ true -> [compressed];
+ false -> []
+ end,
+ case file:write_file(Name, Bin, Opts) of
+ ok -> ok;
+ {error,_}=Error -> Error
+ end.
+
+%% report_errors(State) -> ok
+%% report_warnings(State) -> ok
+
+report_errors(St) ->
+ case member(report_errors, St#compile.options) of
+ true ->
+ foreach(fun ({{F,_L},Eds}) -> list_errors(F, Eds);
+ ({F,Eds}) -> list_errors(F, Eds) end,
+ St#compile.errors);
+ false -> ok
+ end.
+
+report_warnings(#compile{options=Opts,warnings=Ws0}) ->
+ case member(report_warnings, Opts) of
+ true ->
+ Ws1 = flatmap(fun({{F,_L},Eds}) -> format_message(F, Eds);
+ ({F,Eds}) -> format_message(F, Eds) end,
+ Ws0),
+ Ws = ordsets:from_list(Ws1),
+ foreach(fun({_,Str}) -> io:put_chars(Str) end, Ws);
+ false -> ok
+ end.
+
+format_message(F, [{Line,Mod,E}|Es]) ->
+ M = {Line,io_lib:format("~s:~w: Warning: ~s\n", [F,Line,Mod:format_error(E)])},
+ [M|format_message(F, Es)];
+format_message(F, [{Mod,E}|Es]) ->
+ M = {none,io_lib:format("~s: Warning: ~s\n", [F,Mod:format_error(E)])},
+ [M|format_message(F, Es)];
+format_message(_, []) -> [].
+
+%% list_errors(File, ErrorDescriptors) -> ok
+
+list_errors(F, [{Line,Mod,E}|Es]) ->
+ io:fwrite("~s:~w: ~s\n", [F,Line,Mod:format_error(E)]),
+ list_errors(F, Es);
+list_errors(F, [{Mod,E}|Es]) ->
+ io:fwrite("~s: ~s\n", [F,Mod:format_error(E)]),
+ list_errors(F, Es);
+list_errors(_F, []) -> ok.
+
+%% erlfile(Dir, Base) -> ErlFile
+%% outfile(Base, Extension, Options) -> OutputFile
+%% objfile(Base, Target, Options) -> ObjFile
+%% tmpfile(ObjFile) -> TmpFile
+%% Work out the correct input and output file names.
+
+iofile(File) when atom(File) ->
+ iofile(atom_to_list(File));
+iofile(File) ->
+ {filename:dirname(File), filename:basename(File, ".erl")}.
+
+erlfile(Dir, Base, Suffix) ->
+ filename:join(Dir, Base++Suffix).
+
+outfile(Base, Ext, Opts) when atom(Ext) ->
+ outfile(Base, atom_to_list(Ext), Opts);
+outfile(Base, Ext, Opts) ->
+ Obase = case keysearch(outdir, 1, Opts) of
+ {value, {outdir, Odir}} -> filename:join(Odir, Base);
+ _Other -> Base % Not found or bad format
+ end,
+ Obase++"."++Ext.
+
+objfile(Base, St) ->
+ outfile(Base, "beam", St#compile.options).
+
+tmpfile(Ofile) ->
+ reverse([$#|tl(reverse(Ofile))]).
+
+%% pre_defs(Options)
+%% inc_paths(Options)
+%% Extract the predefined macros and include paths from the option list.
+
+pre_defs([{d,M,V}|Opts]) ->
+ [{M,V}|pre_defs(Opts)];
+pre_defs([{d,M}|Opts]) ->
+ [M|pre_defs(Opts)];
+pre_defs([_|Opts]) ->
+ pre_defs(Opts);
+pre_defs([]) -> [].
+
+inc_paths(Opts) ->
+ [ P || {i,P} <- Opts, list(P) ].
+
+src_listing(Ext, St) ->
+ listing(fun (Lf, {_Mod,_Exp,Fs}) -> do_src_listing(Lf, Fs);
+ (Lf, Fs) -> do_src_listing(Lf, Fs) end,
+ Ext, St).
+
+do_src_listing(Lf, Fs) ->
+ foreach(fun (F) -> io:put_chars(Lf, [erl_pp:form(F),"\n"]) end,
+ Fs).
+
+listing(Ext, St) ->
+ listing(fun(Lf, Fs) -> beam_listing:module(Lf, Fs) end, Ext, St).
+
+listing(LFun, Ext, St) ->
+ Lfile = outfile(St#compile.base, Ext, St#compile.options),
+ case file:open(Lfile, [write,delayed_write]) of
+ {ok,Lf} ->
+ LFun(Lf, St#compile.code),
+ ok = file:close(Lf),
+ {ok,St};
+ {error,_Error} ->
+ Es = [{Lfile,[{none,compile,write_error}]}],
+ {error,St#compile{errors=St#compile.errors ++ Es}}
+ end.
+
+options() ->
+ help(standard_passes()).
+
+help([{iff,Flag,{src_listing,Ext}}|T]) ->
+ io:fwrite("~p - Generate .~s source listing file\n", [Flag,Ext]),
+ help(T);
+help([{iff,Flag,{listing,Ext}}|T]) ->
+ io:fwrite("~p - Generate .~s file\n", [Flag,Ext]),
+ help(T);
+help([{iff,Flag,{Name,Fun}}|T]) when function(Fun) ->
+ io:fwrite("~p - Run ~s\n", [Flag,Name]),
+ help(T);
+help([{iff,_Flag,Action}|T]) ->
+ help(Action),
+ help(T);
+help([{unless,Flag,{pass,Pass}}|T]) ->
+ io:fwrite("~p - Skip the ~s pass\n", [Flag,Pass]),
+ help(T);
+help([{unless,no_postopt=Flag,List}|T]) when list(List) ->
+ %% Hard-coded knowledgde here.
+ io:fwrite("~p - Skip all post optimisation\n", [Flag]),
+ help(List),
+ help(T);
+help([{unless,_Flag,Action}|T]) ->
+ help(Action),
+ help(T);
+help([_|T]) ->
+ help(T);
+help(_) ->
+ ok.
+
+
+%% compile(AbsFileName, Outfilename, Options)
+%% Compile entry point for erl_compile.
+
+compile(File0, _OutFile, Options) ->
+ File = shorten_filename(File0),
+ case file(File, make_erl_options(Options)) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+compile_beam(File0, _OutFile, Opts) ->
+ File = shorten_filename(File0),
+ case file(File, [from_beam|make_erl_options(Opts)]) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+compile_asm(File0, _OutFile, Opts) ->
+ File = shorten_filename(File0),
+ case file(File, [asm|make_erl_options(Opts)]) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+compile_core(File0, _OutFile, Opts) ->
+ File = shorten_filename(File0),
+ case file(File, [from_core|make_erl_options(Opts)]) of
+ {ok,_Mod} -> ok;
+ Other -> Other
+ end.
+
+shorten_filename(Name0) ->
+ {ok,Cwd} = file:get_cwd(),
+ case lists:prefix(Cwd, Name0) of
+ false -> Name0;
+ true ->
+ Name = case lists:nthtail(length(Cwd), Name0) of
+ "/"++N -> N;
+ N -> N
+ end,
+ Name
+ end.
+
+%% Converts generic compiler options to specific options.
+
+make_erl_options(Opts) ->
+
+ %% This way of extracting will work even if the record passed
+ %% has more fields than known during compilation.
+
+ Includes = Opts#options.includes,
+ Defines = Opts#options.defines,
+ Outdir = Opts#options.outdir,
+ Warning = Opts#options.warning,
+ Verbose = Opts#options.verbose,
+ Specific = Opts#options.specific,
+ OutputType = Opts#options.output_type,
+ Cwd = Opts#options.cwd,
+
+ Options =
+ case Verbose of
+ true -> [verbose];
+ false -> []
+ end ++
+ case Warning of
+ 0 -> [];
+ _ -> [report_warnings]
+ end ++
+ map(
+ fun ({Name, Value}) ->
+ {d, Name, Value};
+ (Name) ->
+ {d, Name}
+ end,
+ Defines) ++
+ case OutputType of
+ undefined -> [];
+ jam -> [jam];
+ beam -> [beam];
+ native -> [native]
+ end,
+
+ Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}|
+ map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl
new file mode 100644
index 0000000000..3a6158286f
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lib.erl
@@ -0,0 +1,509 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_lib.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose: Core Erlang abstract syntax functions.
+
+-module(core_lib).
+
+-export([get_anno/1,set_anno/2]).
+-export([is_atomic/1,is_literal/1,is_literal_list/1,
+ is_simple/1,is_simple_list/1,is_simple_top/1]).
+-export([literal_value/1,make_literal/1]).
+-export([make_values/1]).
+-export([map/2, fold/3, mapfold/3]).
+-export([is_var_used/2]).
+
+%% -compile([export_all]).
+
+-include("core_parse.hrl").
+
+%% get_anno(Core) -> Anno.
+%% set_anno(Core, Anno) -> Core.
+%% Generic get/set annotation.
+
+get_anno(C) -> element(2, C).
+set_anno(C, A) -> setelement(2, C, A).
+
+%% is_atomic(Expr) -> true | false.
+
+is_atomic(#c_char{}) -> true;
+is_atomic(#c_int{}) -> true;
+is_atomic(#c_float{}) -> true;
+is_atomic(#c_atom{}) -> true;
+is_atomic(#c_string{}) -> true;
+is_atomic(#c_nil{}) -> true;
+is_atomic(#c_fname{}) -> true;
+is_atomic(_) -> false.
+
+%% is_literal(Expr) -> true | false.
+
+is_literal(#c_cons{hd=H,tl=T}) ->
+ case is_literal(H) of
+ true -> is_literal(T);
+ false -> false
+ end;
+is_literal(#c_tuple{es=Es}) -> is_literal_list(Es);
+is_literal(#c_binary{segments=Es}) -> is_lit_bin(Es);
+is_literal(E) -> is_atomic(E).
+
+is_literal_list(Es) -> lists:all(fun is_literal/1, Es).
+
+is_lit_bin(Es) ->
+ lists:all(fun (#c_bitstr{val=E,size=S}) ->
+ is_literal(E) and is_literal(S)
+ end, Es).
+
+%% is_simple(Expr) -> true | false.
+
+is_simple(#c_var{}) -> true;
+is_simple(#c_cons{hd=H,tl=T}) ->
+ case is_simple(H) of
+ true -> is_simple(T);
+ false -> false
+ end;
+is_simple(#c_tuple{es=Es}) -> is_simple_list(Es);
+is_simple(#c_binary{segments=Es}) -> is_simp_bin(Es);
+is_simple(E) -> is_atomic(E).
+
+is_simple_list(Es) -> lists:all(fun is_simple/1, Es).
+
+is_simp_bin(Es) ->
+ lists:all(fun (#c_bitstr{val=E,size=S}) ->
+ is_simple(E) and is_simple(S)
+ end, Es).
+
+%% is_simple_top(Expr) -> true | false.
+%% Only check if the top-level is a simple.
+
+is_simple_top(#c_var{}) -> true;
+is_simple_top(#c_cons{}) -> true;
+is_simple_top(#c_tuple{}) -> true;
+is_simple_top(#c_binary{}) -> true;
+is_simple_top(E) -> is_atomic(E).
+
+%% literal_value(LitExpr) -> Value.
+%% Return the value of LitExpr.
+
+literal_value(#c_char{val=C}) -> C;
+literal_value(#c_int{val=I}) -> I;
+literal_value(#c_float{val=F}) -> F;
+literal_value(#c_atom{val=A}) -> A;
+literal_value(#c_string{val=S}) -> S;
+literal_value(#c_nil{}) -> [];
+literal_value(#c_cons{hd=H,tl=T}) ->
+ [literal_value(H)|literal_value(T)];
+literal_value(#c_tuple{es=Es}) ->
+ list_to_tuple(literal_value_list(Es)).
+
+literal_value_list(Vals) -> lists:map(fun literal_value/1, Vals).
+
+%% make_literal(Value) -> LitExpr.
+%% Make a literal expression from an Erlang value.
+
+make_literal(I) when integer(I) -> #c_int{val=I};
+make_literal(F) when float(F) -> #c_float{val=F};
+make_literal(A) when atom(A) -> #c_atom{val=A};
+make_literal([]) -> #c_nil{};
+make_literal([H|T]) ->
+ #c_cons{hd=make_literal(H),tl=make_literal(T)};
+make_literal(T) when tuple(T) ->
+ #c_tuple{es=make_literal_list(tuple_to_list(T))}.
+
+make_literal_list(Vals) -> lists:map(fun make_literal/1, Vals).
+
+%% make_values([CoreExpr] | CoreExpr) -> #c_values{} | CoreExpr.
+%% Make a suitable values structure, expr or values, depending on
+%% Expr.
+
+make_values([E]) -> E;
+make_values([H|_]=Es) -> #c_values{anno=get_anno(H),es=Es};
+make_values([]) -> #c_values{es=[]};
+make_values(E) -> E.
+
+%% map(MapFun, CoreExpr) -> CoreExpr.
+%% This function traverses the core parse format, at each level
+%% applying the submited argument function, assumed to do the real
+%% work.
+%%
+%% The "eager" style, where each component of a construct are
+%% descended to before the construct itself, admits that some
+%% companion functions (the F:s) may be made simpler, since it may be
+%% safely assumed that no lower illegal instanced will be
+%% created/uncovered by actions on the current level.
+
+map(F, #c_tuple{es=Es}=R) ->
+ F(R#c_tuple{es=map_list(F, Es)});
+map(F, #c_cons{hd=Hd, tl=Tl}=R) ->
+ F(R#c_cons{hd=map(F, Hd),
+ tl=map(F, Tl)});
+map(F, #c_values{es=Es}=R) ->
+ F(R#c_values{es=map_list(F, Es)});
+
+map(F, #c_alias{var=Var, pat=Pat}=R) ->
+ F(R#c_alias{var=map(F, Var),
+ pat=map(F, Pat)});
+
+map(F, #c_module{defs=Defs}=R) ->
+ F(R#c_module{defs=map_list(F, Defs)});
+map(F, #c_def{val=Val}=R) ->
+ F(R#c_def{val=map(F, Val)});
+
+map(F, #c_fun{vars=Vars, body=Body}=R) ->
+ F(R#c_fun{vars=map_list(F, Vars),
+ body=map(F, Body)});
+map(F, #c_let{vars=Vs, arg=Arg, body=Body}=R) ->
+ F(R#c_let{vars=map_list(F, Vs),
+ arg=map(F, Arg),
+ body=map(F, Body)});
+map(F, #c_letrec{defs=Fs,body=Body}=R) ->
+ F(R#c_letrec{defs=map_list(F, Fs),
+ body=map(F, Body)});
+map(F, #c_seq{arg=Arg, body=Body}=R) ->
+ F(R#c_seq{arg=map(F, Arg),
+ body=map(F, Body)});
+map(F, #c_case{arg=Arg, clauses=Clauses}=R) ->
+ F(R#c_case{arg=map(F, Arg),
+ clauses=map_list(F, Clauses)});
+map(F, #c_clause{pats=Ps, guard=Guard, body=Body}=R) ->
+ F(R#c_clause{pats=map_list(F, Ps),
+ guard=map(F, Guard),
+ body=map(F, Body)});
+map(F, #c_receive{clauses=Cls, timeout=Tout, action=Act}=R) ->
+ F(R#c_receive{clauses=map_list(F, Cls),
+ timeout=map(F, Tout),
+ action=map(F, Act)});
+map(F, #c_apply{op=Op,args=Args}=R) ->
+ F(R#c_apply{op=map(F, Op),
+ args=map_list(F, Args)});
+map(F, #c_call{module=M,name=N,args=Args}=R) ->
+ F(R#c_call{module=map(F, M),
+ name=map(F, N),
+ args=map_list(F, Args)});
+map(F, #c_primop{name=N,args=Args}=R) ->
+ F(R#c_primop{name=map(F, N),
+ args=map_list(F, Args)});
+map(F, #c_try{arg=Expr,vars=Vars,body=Body,evars=Evars,handler=Handler}=R) ->
+ F(R#c_try{arg=map(F, Expr),
+ vars=map(F, Vars),
+ body=map(F, Body),
+ evars=map(F, Evars),
+ handler=map(F, Handler)});
+map(F, #c_catch{body=Body}=R) ->
+ F(R#c_catch{body=map(F, Body)});
+map(F, T) -> F(T). %Atomic nodes.
+
+map_list(F, L) -> lists:map(fun (E) -> map(F, E) end, L).
+
+%% fold(FoldFun, Accumulator, CoreExpr) -> Accumulator.
+%% This function traverses the core parse format, at each level
+%% applying the submited argument function, assumed to do the real
+%% work, and keeping the accumulated result in the A (accumulator)
+%% argument.
+
+fold(F, Acc, #c_tuple{es=Es}=R) ->
+ F(R, fold_list(F, Acc, Es));
+fold(F, Acc, #c_cons{hd=Hd, tl=Tl}=R) ->
+ F(R, fold(F, fold(F, Acc, Hd), Tl));
+fold(F, Acc, #c_values{es=Es}=R) ->
+ F(R, fold_list(F, Acc, Es));
+
+fold(F, Acc, #c_alias{pat=P,var=V}=R) ->
+ F(R, fold(F, fold(F, Acc, P), V));
+
+fold(F, Acc, #c_module{defs=Defs}=R) ->
+ F(R, fold_list(F, Acc, Defs));
+fold(F, Acc, #c_def{val=Val}=R) ->
+ F(R, fold(F, Acc, Val));
+
+fold(F, Acc, #c_fun{vars=Vars, body=Body}=R) ->
+ F(R, fold(F, fold_list(F, Acc, Vars), Body));
+fold(F, Acc, #c_let{vars=Vs, arg=Arg, body=Body}=R) ->
+ F(R, fold(F, fold(F, fold_list(F, Acc, Vs), Arg), Body));
+fold(F, Acc, #c_letrec{defs=Fs,body=Body}=R) ->
+ F(R, fold(F, fold_list(F, Acc, Fs), Body));
+fold(F, Acc, #c_seq{arg=Arg, body=Body}=R) ->
+ F(R, fold(F, fold(F, Acc, Arg), Body));
+fold(F, Acc, #c_case{arg=Arg, clauses=Clauses}=R) ->
+ F(R, fold_list(F, fold(F, Acc, Arg), Clauses));
+fold(F, Acc, #c_clause{pats=Ps,guard=G,body=B}=R) ->
+ F(R, fold(F, fold(F, fold_list(F, Acc, Ps), G), B));
+fold(F, Acc, #c_receive{clauses=Cl, timeout=Ti, action=Ac}=R) ->
+ F(R, fold_list(F, fold(F, fold(F, Acc, Ac), Ti), Cl));
+fold(F, Acc, #c_apply{op=Op, args=Args}=R) ->
+ F(R, fold_list(F, fold(F, Acc, Op), Args));
+fold(F, Acc, #c_call{module=Mod,name=Name,args=Args}=R) ->
+ F(R, fold_list(F, fold(F, fold(F, Acc, Mod), Name), Args));
+fold(F, Acc, #c_primop{name=Name,args=Args}=R) ->
+ F(R, fold_list(F, fold(F, Acc, Name), Args));
+fold(F, Acc, #c_try{arg=E,vars=Vs,body=Body,evars=Evs,handler=H}=R) ->
+ NewB = fold(F, fold_list(F, fold(F, Acc, E), Vs), Body),
+ F(R, fold(F, fold_list(F, NewB, Evs), H));
+fold(F, Acc, #c_catch{body=Body}=R) ->
+ F(R, fold(F, Acc, Body));
+fold(F, Acc, T) -> %Atomic nodes
+ F(T, Acc).
+
+fold_list(F, Acc, L) ->
+ lists:foldl(fun (E, A) -> fold(F, A, E) end, Acc, L).
+
+%% mapfold(MapfoldFun, Accumulator, CoreExpr) -> {CoreExpr,Accumulator}.
+%% This function traverses the core parse format, at each level
+%% applying the submited argument function, assumed to do the real
+%% work, and keeping the accumulated result in the A (accumulator)
+%% argument.
+
+mapfold(F, Acc0, #c_tuple{es=Es0}=R) ->
+ {Es1,Acc1} = mapfold_list(F, Acc0, Es0),
+ F(R#c_tuple{es=Es1}, Acc1);
+mapfold(F, Acc0, #c_cons{hd=H0,tl=T0}=R) ->
+ {H1,Acc1} = mapfold(F, Acc0, H0),
+ {T1,Acc2} = mapfold(F, Acc1, T0),
+ F(R#c_cons{hd=H1,tl=T1}, Acc2);
+mapfold(F, Acc0, #c_values{es=Es0}=R) ->
+ {Es1,Acc1} = mapfold_list(F, Acc0, Es0),
+ F(R#c_values{es=Es1}, Acc1);
+
+mapfold(F, Acc0, #c_alias{pat=P0,var=V0}=R) ->
+ {P1,Acc1} = mapfold(F, Acc0, P0),
+ {V1,Acc2} = mapfold(F, Acc1, V0),
+ F(R#c_alias{pat=P1,var=V1}, Acc2);
+
+mapfold(F, Acc0, #c_module{defs=D0}=R) ->
+ {D1,Acc1} = mapfold_list(F, Acc0, D0),
+ F(R#c_module{defs=D1}, Acc1);
+mapfold(F, Acc0, #c_def{val=V0}=R) ->
+ {V1,Acc1} = mapfold(F, Acc0, V0),
+ F(R#c_def{val=V1}, Acc1);
+
+mapfold(F, Acc0, #c_fun{vars=Vs0, body=B0}=R) ->
+ {Vs1,Acc1} = mapfold_list(F, Acc0, Vs0),
+ {B1,Acc2} = mapfold(F, Acc1, B0),
+ F(R#c_fun{vars=Vs1,body=B1}, Acc2);
+mapfold(F, Acc0, #c_let{vars=Vs0, arg=A0, body=B0}=R) ->
+ {Vs1,Acc1} = mapfold_list(F, Acc0, Vs0),
+ {A1,Acc2} = mapfold(F, Acc1, A0),
+ {B1,Acc3} = mapfold(F, Acc2, B0),
+ F(R#c_let{vars=Vs1,arg=A1,body=B1}, Acc3);
+mapfold(F, Acc0, #c_letrec{defs=Fs0,body=B0}=R) ->
+ {Fs1,Acc1} = mapfold_list(F, Acc0, Fs0),
+ {B1,Acc2} = mapfold(F, Acc1, B0),
+ F(R#c_letrec{defs=Fs1,body=B1}, Acc2);
+mapfold(F, Acc0, #c_seq{arg=A0, body=B0}=R) ->
+ {A1,Acc1} = mapfold(F, Acc0, A0),
+ {B1,Acc2} = mapfold(F, Acc1, B0),
+ F(R#c_seq{arg=A1,body=B1}, Acc2);
+mapfold(F, Acc0, #c_case{arg=A0,clauses=Cs0}=R) ->
+ {A1,Acc1} = mapfold(F, Acc0, A0),
+ {Cs1,Acc2} = mapfold_list(F, Acc1, Cs0),
+ F(R#c_case{arg=A1,clauses=Cs1}, Acc2);
+mapfold(F, Acc0, #c_clause{pats=Ps0,guard=G0,body=B0}=R) ->
+ {Ps1,Acc1} = mapfold_list(F, Acc0, Ps0),
+ {G1,Acc2} = mapfold(F, Acc1, G0),
+ {B1,Acc3} = mapfold(F, Acc2, B0),
+ F(R#c_clause{pats=Ps1,guard=G1,body=B1}, Acc3);
+mapfold(F, Acc0, #c_receive{clauses=Cs0,timeout=T0,action=A0}=R) ->
+ {T1,Acc1} = mapfold(F, Acc0, T0),
+ {Cs1,Acc2} = mapfold_list(F, Acc1, Cs0),
+ {A1,Acc3} = mapfold(F, Acc2, A0),
+ F(R#c_receive{clauses=Cs1,timeout=T1,action=A1}, Acc3);
+mapfold(F, Acc0, #c_apply{op=Op0, args=As0}=R) ->
+ {Op1,Acc1} = mapfold(F, Acc0, Op0),
+ {As1,Acc2} = mapfold_list(F, Acc1, As0),
+ F(R#c_apply{op=Op1,args=As1}, Acc2);
+mapfold(F, Acc0, #c_call{module=M0,name=N0,args=As0}=R) ->
+ {M1,Acc1} = mapfold(F, Acc0, M0),
+ {N1,Acc2} = mapfold(F, Acc1, N0),
+ {As1,Acc3} = mapfold_list(F, Acc2, As0),
+ F(R#c_call{module=M1,name=N1,args=As1}, Acc3);
+mapfold(F, Acc0, #c_primop{name=N0, args=As0}=R) ->
+ {N1,Acc1} = mapfold(F, Acc0, N0),
+ {As1,Acc2} = mapfold_list(F, Acc1, As0),
+ F(R#c_primop{name=N1,args=As1}, Acc2);
+mapfold(F, Acc0, #c_try{arg=E0,vars=Vs0,body=B0,evars=Evs0,handler=H0}=R) ->
+ {E1,Acc1} = mapfold(F, Acc0, E0),
+ {Vs1,Acc2} = mapfold_list(F, Acc1, Vs0),
+ {B1,Acc3} = mapfold(F, Acc2, B0),
+ {Evs1,Acc4} = mapfold_list(F, Acc3, Evs0),
+ {H1,Acc5} = mapfold(F, Acc4, H0),
+ F(R#c_try{arg=E1,vars=Vs1,body=B1,evars=Evs1,handler=H1}, Acc5);
+mapfold(F, Acc0, #c_catch{body=B0}=R) ->
+ {B1,Acc1} = mapfold(F, Acc0, B0),
+ F(R#c_catch{body=B1}, Acc1);
+mapfold(F, Acc, T) -> %Atomic nodes
+ F(T, Acc).
+
+mapfold_list(F, Acc, L) ->
+ lists:mapfoldl(fun (E, A) -> mapfold(F, A, E) end, Acc, L).
+
+%% is_var_used(VarName, Expr) -> true | false.
+%% Test if the variable VarName is used in Expr.
+
+is_var_used(V, B) -> vu_body(V, B).
+
+vu_body(V, #c_values{es=Es}) ->
+ vu_expr_list(V, Es);
+vu_body(V, Body) ->
+ vu_expr(V, Body).
+
+vu_expr(V, #c_var{name=V2}) -> V =:= V2;
+vu_expr(V, #c_cons{hd=H,tl=T}) ->
+ case vu_expr(V, H) of
+ true -> true;
+ false -> vu_expr(V, T)
+ end;
+vu_expr(V, #c_tuple{es=Es}) ->
+ vu_expr_list(V, Es);
+vu_expr(V, #c_binary{segments=Ss}) ->
+ vu_seg_list(V, Ss);
+vu_expr(V, #c_fun{vars=Vs,body=B}) ->
+ %% Variables in fun shadow previous variables
+ case vu_var_list(V, Vs) of
+ true -> false;
+ false -> vu_body(V, B)
+ end;
+vu_expr(V, #c_let{vars=Vs,arg=Arg,body=B}) ->
+ case vu_body(V, Arg) of
+ true -> true;
+ false ->
+ %% Variables in let shadow previous variables.
+ case vu_var_list(V, Vs) of
+ true -> false;
+ false -> vu_body(V, B)
+ end
+ end;
+vu_expr(V, #c_letrec{defs=Fs,body=B}) ->
+ case lists:any(fun (#c_def{val=Fb}) -> vu_body(V, Fb) end, Fs) of
+ true -> true;
+ false -> vu_body(V, B)
+ end;
+vu_expr(V, #c_seq{arg=Arg,body=B}) ->
+ case vu_expr(V, Arg) of
+ true -> true;
+ false -> vu_body(V, B)
+ end;
+vu_expr(V, #c_case{arg=Arg,clauses=Cs}) ->
+ case vu_expr(V, Arg) of
+ true -> true;
+ false -> vu_clauses(V, Cs)
+ end;
+vu_expr(V, #c_receive{clauses=Cs,timeout=T,action=A}) ->
+ case vu_clauses(V, Cs) of
+ true -> true;
+ false ->
+ case vu_expr(V, T) of
+ true -> true;
+ false -> vu_body(V, A)
+ end
+ end;
+vu_expr(V, #c_apply{op=Op,args=As}) ->
+ vu_expr_list(V, [Op|As]);
+vu_expr(V, #c_call{module=M,name=N,args=As}) ->
+ vu_expr_list(V, [M,N|As]);
+vu_expr(V, #c_primop{args=As}) -> %Name is an atom
+ vu_expr_list(V, As);
+vu_expr(V, #c_catch{body=B}) ->
+ vu_body(V, B);
+vu_expr(V, #c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}) ->
+ case vu_body(V, E) of
+ true -> true;
+ false ->
+ %% Variables shadow previous ones.
+ case case vu_var_list(V, Vs) of
+ true -> false;
+ false -> vu_body(V, B)
+ end of
+ true -> true;
+ false ->
+ case vu_var_list(V, Evs) of
+ true -> false;
+ false -> vu_body(V, H)
+ end
+ end
+ end;
+vu_expr(_, _) -> false. %Everything else
+
+vu_expr_list(V, Es) ->
+ lists:any(fun(E) -> vu_expr(V, E) end, Es).
+
+vu_seg_list(V, Ss) ->
+ lists:any(fun (#c_bitstr{val=Val,size=Size}) ->
+ case vu_expr(V, Val) of
+ true -> true;
+ false -> vu_expr(V, Size)
+ end
+ end, Ss).
+
+%% vu_clause(VarName, Clause) -> true | false.
+%% vu_clauses(VarName, [Clause]) -> true | false.
+%% Have to get the pattern results right.
+
+vu_clause(V, #c_clause{pats=Ps,guard=G,body=B}) ->
+ case vu_pattern_list(V, Ps) of
+ {true,_Shad} -> true; %It is used
+ {false,true} -> false; %Shadowed
+ {false,false} -> %Not affected
+ case vu_expr(V, G) of
+ true -> true;
+ false ->vu_body(V, B)
+ end
+ end.
+
+vu_clauses(V, Cs) ->
+ lists:any(fun(C) -> vu_clause(V, C) end, Cs).
+
+%% vu_pattern(VarName, Pattern) -> {Used,Shadow}.
+%% vu_pattern_list(VarName, [Pattern]) -> {Used,Shadow}.
+%% Binaries complicate patterns as a variable can both be properly
+%% used, in a bit segment size, and shadow. They can also do both.
+
+%%vu_pattern(V, Pat) -> vu_pattern(V, Pat, {false,false}).
+
+vu_pattern(V, #c_var{name=V2}, St) ->
+ setelement(2, St, V =:= V2);
+vu_pattern(V, #c_cons{hd=H,tl=T}, St0) ->
+ case vu_pattern(V, H, St0) of
+ {true,true}=St1 -> St1; %Nothing more to know
+ St1 -> vu_pattern(V, T, St1)
+ end;
+vu_pattern(V, #c_tuple{es=Es}, St) ->
+ vu_pattern_list(V, Es, St);
+vu_pattern(V, #c_binary{segments=Ss}, St) ->
+ vu_pat_seg_list(V, Ss, St);
+vu_pattern(V, #c_alias{var=Var,pat=P}, St0) ->
+ case vu_pattern(V, Var, St0) of
+ {true,true}=St1 -> St1;
+ St1 -> vu_pattern(V, P, St1)
+ end;
+vu_pattern(_, _, St) -> St.
+
+vu_pattern_list(V, Ps) -> vu_pattern_list(V, Ps, {false,false}).
+
+vu_pattern_list(V, Ps, St0) ->
+ lists:foldl(fun(P, St) -> vu_pattern(V, P, St) end, St0, Ps).
+
+vu_pat_seg_list(V, Ss, St) ->
+ lists:foldl(fun (#c_bitstr{val=Val,size=Size}, St0) ->
+ case vu_pattern(V, Val, St0) of
+ {true,true}=St1 -> St1;
+ {_Used,Shad} -> {vu_expr(V, Size),Shad}
+ end
+ end, St, Ss).
+
+%% vu_var_list(VarName, [Var]) -> true | false.
+
+vu_var_list(V, Vs) ->
+ lists:any(fun (#c_var{name=V2}) -> V =:= V2 end, Vs).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl
new file mode 100644
index 0000000000..2946fcb8c0
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_lint.erl
@@ -0,0 +1,515 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_lint.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Do necessary checking of Core Erlang code.
+
+%% Check Core module for errors. Seeing this module is used in the
+%% compiler after optimisations wedone more checking than would be
+%% necessary after just parsing. Don't check all constructs.
+%%
+%% We check the following:
+%%
+%% All referred functions, called and exported, are defined.
+%% Format of export list.
+%% Format of attributes
+%% Used variables are defined.
+%% Variables in let and funs.
+%% Patterns case clauses.
+%% Values only as multiple values/variables/patterns.
+%% Return same number of values as requested
+%% Correct number of arguments
+%%
+%% Checks to add:
+%%
+%% Consistency of values/variables
+%% Consistency of function return values/calls.
+%%
+%% We keep the names defined variables and functions in a ordered list
+%% of variable names and function name/arity pairs.
+
+-module(core_lint).
+
+
+-export([module/1,module/2,format_error/1]).
+
+-import(lists, [reverse/1,all/2,foldl/3]).
+-import(ordsets, [add_element/2,is_element/2,union/2]).
+%-import(ordsets, [subtract/2]).
+
+-include("core_parse.hrl").
+
+%% Define the lint state record.
+
+-record(lint, {module=[], %Current module
+ func=[], %Current function
+ errors=[], %Errors
+ warnings=[]}). %Warnings
+
+%% Keep track of defined
+-record(def, {vars=[],
+ funs=[]}).
+
+%%-deftype retcount() -> any | unknown | int().
+
+%% format_error(Error)
+%% Return a string describing the error.
+
+format_error(invalid_exports) -> "invalid exports";
+format_error(invalid_attributes) -> "invalid attributes";
+format_error({undefined_function,{F,A}}) ->
+ io_lib:format("function ~w/~w undefined", [F,A]);
+format_error({undefined_function,{F1,A1},{F2,A2}}) ->
+ io_lib:format("undefined function ~w/~w in ~w/~w", [F1,A1,F2,A2]);
+format_error({illegal_expr,{F,A}}) ->
+ io_lib:format("illegal expression in ~w/~w", [F,A]);
+format_error({illegal_guard,{F,A}}) ->
+ io_lib:format("illegal guard expression in ~w/~w", [F,A]);
+format_error({illegal_pattern,{F,A}}) ->
+ io_lib:format("illegal pattern in ~w/~w", [F,A]);
+format_error({illegal_try,{F,A}}) ->
+ io_lib:format("illegal try expression in ~w/~w", [F,A]);
+format_error({pattern_mismatch,{F,A}}) ->
+ io_lib:format("pattern count mismatch in ~w/~w", [F,A]);
+format_error({return_mismatch,{F,A}}) ->
+ io_lib:format("return count mismatch in ~w/~w", [F,A]);
+format_error({arg_mismatch,{F,A}}) ->
+ io_lib:format("argument count mismatch in ~w/~w", [F,A]);
+format_error({unbound_var,N,{F,A}}) ->
+ io_lib:format("unbound variable ~s in ~w/~w", [N,F,A]);
+format_error({duplicate_var,N,{F,A}}) ->
+ io_lib:format("duplicate variable ~s in ~w/~w", [N,F,A]);
+format_error({not_var,{F,A}}) ->
+ io_lib:format("expecting variable in ~w/~w", [F,A]);
+format_error({not_pattern,{F,A}}) ->
+ io_lib:format("expecting pattern in ~w/~w", [F,A]);
+format_error({not_bs_pattern,{F,A}}) ->
+ io_lib:format("expecting bit syntax pattern in ~w/~w", [F,A]).
+
+%% module(CoreMod) ->
+%% module(CoreMod, [CompileOption]) ->
+%% {ok,[Warning]} | {error,[Error],[Warning]}
+
+module(M) -> module(M, []).
+
+module(#c_module{name=M,exports=Es,attrs=As,defs=Ds}, _Opts) ->
+ Defined = defined_funcs(Ds),
+ St0 = #lint{module=M#c_atom.val},
+ St1 = check_exports(Es, St0),
+ St2 = check_attrs(As, St1),
+ St3 = module_defs(Ds, Defined, St2),
+ St4 = check_state(Es, Defined, St3),
+ return_status(St4).
+
+%% defined_funcs([FuncDef]) -> [Fname].
+
+defined_funcs(Fs) ->
+ foldl(fun (#c_def{name=#c_fname{id=I,arity=A}}, Def) ->
+ add_element({I,A}, Def)
+ end, [], Fs).
+
+%% return_status(State) ->
+%% {ok,[Warning]} | {error,[Error],[Warning]}
+%% Pack errors and warnings properly and return ok | error.
+
+return_status(St) ->
+ Ws = reverse(St#lint.warnings),
+ case reverse(St#lint.errors) of
+ [] -> {ok,[{St#lint.module,Ws}]};
+ Es -> {error,[{St#lint.module,Es}],[{St#lint.module,Ws}]}
+ end.
+
+%% add_error(ErrorDescriptor, State) -> State'
+%% add_warning(ErrorDescriptor, State) -> State'
+%% Note that we don't use line numbers here.
+
+add_error(E, St) -> St#lint{errors=[{none,core_lint,E}|St#lint.errors]}.
+
+%%add_warning(W, St) -> St#lint{warnings=[{none,core_lint,W}|St#lint.warnings]}.
+
+check_exports(Es, St) ->
+ case all(fun (#c_fname{id=Name,arity=Arity}) when
+ atom(Name), integer(Arity) -> true;
+ (_) -> false
+ end, Es) of
+ true -> St;
+ false -> add_error(invalid_exports, St)
+ end.
+
+check_attrs(As, St) ->
+ case all(fun (#c_def{name=#c_atom{},val=V}) -> core_lib:is_literal(V);
+ (_) -> false
+ end, As) of
+ true -> St;
+ false -> add_error(invalid_attributes, St)
+ end.
+
+check_state(Es, Defined, St) ->
+ foldl(fun (#c_fname{id=N,arity=A}, St1) ->
+ F = {N,A},
+ case is_element(F, Defined) of
+ true -> St1;
+ false -> add_error({undefined_function,F}, St)
+ end
+ end, St, Es).
+% Undef = subtract(Es, Defined),
+% St1 = foldl(fun (F, St) -> add_error({undefined_function,F}, St) end,
+% St0, Undef),
+% St1.
+
+%% module_defs(CoreBody, Defined, State) -> State.
+
+module_defs(B, Def, St) ->
+ %% Set top level function name.
+ foldl(fun (Func, St0) ->
+ #c_fname{id=F,arity=A} = Func#c_def.name,
+ St1 = St0#lint{func={F,A}},
+ function(Func, Def, St1)
+ end, St, B).
+
+%% functions([Fdef], Defined, State) -> State.
+
+functions(Fs, Def, St0) ->
+ foldl(fun (F, St) -> function(F, Def, St) end, St0, Fs).
+
+%% function(CoreFunc, Defined, State) -> State.
+
+function(#c_def{name=#c_fname{},val=B}, Def, St) ->
+ %% Body must be a fun!
+ case B of
+ #c_fun{} -> expr(B, Def, any, St);
+ _ -> add_error({illegal_expr,St#lint.func}, St)
+ end.
+
+%% body(Expr, Defined, RetCount, State) -> State.
+
+body(#c_values{es=Es}, Def, Rt, St) ->
+ return_match(Rt, length(Es), expr_list(Es, Def, St));
+body(E, Def, Rt, St0) ->
+ St1 = expr(E, Def, Rt, St0),
+ case core_lib:is_simple_top(E) of
+ true -> return_match(Rt, 1, St1);
+ false -> St1
+ end.
+
+%% guard(Expr, Defined, State) -> State.
+%% Guards are boolean expressions with test wrapped in a protected.
+
+guard(Expr, Def, St) -> gexpr(Expr, Def, 1, St).
+
+%% guard_list([Expr], Defined, State) -> State.
+
+%% guard_list(Es, Def, St0) ->
+%% foldl(fun (E, St) -> guard(E, Def, St) end, St0, Es).
+
+%% gbody(Expr, Defined, RetCount, State) -> State.
+
+gbody(#c_values{es=Es}, Def, Rt, St) ->
+ return_match(Rt, length(Es), gexpr_list(Es, Def, St));
+gbody(E, Def, Rt, St0) ->
+ St1 = gexpr(E, Def, Rt, St0),
+ case core_lib:is_simple_top(E) of
+ true -> return_match(Rt, 1, St1);
+ false -> St1
+ end.
+
+gexpr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
+gexpr(#c_int{}, _Def, _Rt, St) -> St;
+gexpr(#c_float{}, _Def, _Rt, St) -> St;
+gexpr(#c_atom{}, _Def, _Rt, St) -> St;
+gexpr(#c_char{}, _Def, _Rt, St) -> St;
+gexpr(#c_string{}, _Def, _Rt, St) -> St;
+gexpr(#c_nil{}, _Def, _Rt, St) -> St;
+gexpr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
+ gexpr_list([H,T], Def, St);
+gexpr(#c_tuple{es=Es}, Def, _Rt, St) ->
+ gexpr_list(Es, Def, St);
+gexpr(#c_binary{segments=Ss}, Def, _Rt, St) ->
+ gbitstr_list(Ss, Def, St);
+gexpr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = gexpr(Arg, Def, any, St0), %Ignore values
+ gbody(B, Def, Rt, St1);
+gexpr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = gbody(Arg, Def, let_varcount(Vs), St0), %This is a guard body
+ {Lvs,St2} = variable_list(Vs, St1),
+ gbody(B, union(Lvs, Def), Rt, St2);
+gexpr(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{},
+ args=As}, Def, 1, St) ->
+ gexpr_list(As, Def, St);
+gexpr(#c_primop{name=N,args=As}, Def, _Rt, St0) when record(N, c_atom) ->
+ gexpr_list(As, Def, St0);
+gexpr(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
+ evars=[#c_var{},#c_var{},#c_var{}],handler=#c_atom{val=false}},
+ Def, Rt, St) ->
+ gbody(E, Def, Rt, St);
+gexpr(_, _, _, St) ->
+ add_error({illegal_guard,St#lint.func}, St).
+
+%% gexpr_list([Expr], Defined, State) -> State.
+
+gexpr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> gexpr(E, Def, 1, St) end, St0, Es).
+
+%% gbitstr_list([Elem], Defined, State) -> State.
+
+gbitstr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> gbitstr(E, Def, St) end, St0, Es).
+
+gbitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, St0) ->
+ St1 = bit_type(U, T, Fs, St0),
+ gexpr_list([V,S], Def, St1).
+
+%% expr(Expr, Defined, RetCount, State) -> State.
+
+expr(#c_var{name=N}, Def, _Rt, St) -> expr_var(N, Def, St);
+expr(#c_int{}, _Def, _Rt, St) -> St;
+expr(#c_float{}, _Def, _Rt, St) -> St;
+expr(#c_atom{}, _Def, _Rt, St) -> St;
+expr(#c_char{}, _Def, _Rt, St) -> St;
+expr(#c_string{}, _Def, _Rt, St) -> St;
+expr(#c_nil{}, _Def, _Rt, St) -> St;
+expr(#c_cons{hd=H,tl=T}, Def, _Rt, St) ->
+ expr_list([H,T], Def, St);
+expr(#c_tuple{es=Es}, Def, _Rt, St) ->
+ expr_list(Es, Def, St);
+expr(#c_binary{segments=Ss}, Def, _Rt, St) ->
+ bitstr_list(Ss, Def, St);
+expr(#c_fname{id=I,arity=A}, Def, _Rt, St) ->
+ expr_fname({I,A}, Def, St);
+expr(#c_fun{vars=Vs,body=B}, Def, Rt, St0) ->
+ {Vvs,St1} = variable_list(Vs, St0),
+ return_match(Rt, 1, body(B, union(Vvs, Def), any, St1));
+expr(#c_seq{arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = expr(Arg, Def, any, St0), %Ignore values
+ body(B, Def, Rt, St1);
+expr(#c_let{vars=Vs,arg=Arg,body=B}, Def, Rt, St0) ->
+ St1 = body(Arg, Def, let_varcount(Vs), St0), %This is a body
+ {Lvs,St2} = variable_list(Vs, St1),
+ body(B, union(Lvs, Def), Rt, St2);
+expr(#c_letrec{defs=Fs,body=B}, Def0, Rt, St0) ->
+ Def1 = union(defined_funcs(Fs), Def0), %All defined stuff
+ St1 = functions(Fs, Def1, St0),
+ body(B, Def1, Rt, St1#lint{func=St0#lint.func});
+expr(#c_case{arg=Arg,clauses=Cs}, Def, Rt, St0) ->
+ Pc = case_patcount(Cs),
+ St1 = body(Arg, Def, Pc, St0),
+ clauses(Cs, Def, Pc, Rt, St1);
+expr(#c_receive{clauses=Cs,timeout=T,action=A}, Def, Rt, St0) ->
+ St1 = expr(T, Def, 1, St0),
+ St2 = body(A, Def, Rt, St1),
+ clauses(Cs, Def, 1, Rt, St2);
+expr(#c_apply{op=Op,args=As}, Def, _Rt, St0) ->
+ St1 = apply_op(Op, Def, length(As), St0),
+ expr_list(As, Def, St1);
+expr(#c_call{module=M,name=N,args=As}, Def, _Rt, St0) ->
+ St1 = expr(M, Def, 1, St0),
+ St2 = expr(N, Def, 1, St1),
+ expr_list(As, Def, St2);
+expr(#c_primop{name=N,args=As}, Def, _Rt, St0) when record(N, c_atom) ->
+ expr_list(As, Def, St0);
+expr(#c_catch{body=B}, Def, Rt, St) ->
+ return_match(Rt, 1, body(B, Def, 1, St));
+expr(#c_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H}, Def, Rt, St0) ->
+ St1 = case length(Evs) of
+ 2 -> St0;
+ _ -> add_error({illegal_try,St0#lint.func}, St0)
+ end,
+ St2 = body(A, Def, let_varcount(Vs), St1),
+ {Ns,St3} = variable_list(Vs, St2),
+ St4 = body(B, union(Ns, Def), Rt, St3),
+ {Ens,St5} = variable_list(Evs, St4),
+ body(H, union(Ens, Def), Rt, St5);
+expr(_, _, _, St) ->
+ %%io:fwrite("clint: ~p~n", [Other]),
+ add_error({illegal_expr,St#lint.func}, St).
+
+%% expr_list([Expr], Defined, State) -> State.
+
+expr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> expr(E, Def, 1, St) end, St0, Es).
+
+%% bitstr_list([Elem], Defined, State) -> State.
+
+bitstr_list(Es, Def, St0) ->
+ foldl(fun (E, St) -> bitstr(E, Def, St) end, St0, Es).
+
+bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, St0) ->
+ St1 = bit_type(U, T, Fs, St0),
+ expr_list([V,S], Def, St1).
+
+%% apply_op(Op, Defined, ArgCount, State) -> State.
+%% A apply op is either an fname or an expression.
+
+apply_op(#c_fname{id=I,arity=A}, Def, Ac, St0) ->
+ St1 = expr_fname({I,A}, Def, St0),
+ arg_match(Ac, A, St1);
+apply_op(E, Def, _, St) -> expr(E, Def, 1, St). %Hard to check
+
+%% expr_var(VarName, Defined, State) -> State.
+
+expr_var(N, Def, St) ->
+ case is_element(N, Def) of
+ true -> St;
+ false -> add_error({unbound_var,N,St#lint.func}, St)
+ end.
+
+%% expr_fname(Fname, Defined, State) -> State.
+
+expr_fname(Fname, Def, St) ->
+ case is_element(Fname, Def) of
+ true -> St;
+ false -> add_error({undefined_function,Fname,St#lint.func}, St)
+ end.
+
+%% let_varcount([Var]) -> int().
+
+let_varcount([]) -> any; %Ignore values
+let_varcount(Es) -> length(Es).
+
+%% case_patcount([Clause]) -> int().
+
+case_patcount([#c_clause{pats=Ps}|_]) -> length(Ps).
+
+%% clauses([Clause], Defined, PatCount, RetCount, State) -> State.
+
+clauses(Cs, Def, Pc, Rt, St0) ->
+ foldl(fun (C, St) -> clause(C, Def, Pc, Rt, St) end, St0, Cs).
+
+%% clause(Clause, Defined, PatCount, RetCount, State) -> State.
+
+clause(#c_clause{pats=Ps,guard=G,body=B}, Def0, Pc, Rt, St0) ->
+ St1 = pattern_match(Pc, length(Ps), St0),
+ {Pvs,St2} = pattern_list(Ps, Def0, St1),
+ Def1 = union(Pvs, Def0),
+ St3 = guard(G, Def1, St2),
+ body(B, Def1, Rt, St3).
+
+%% variable(Var, [PatVar], State) -> {[VarName],State}.
+
+variable(#c_var{name=N}, Ps, St) ->
+ case is_element(N, Ps) of
+ true -> {[],add_error({duplicate_var,N,St#lint.func}, St)};
+ false -> {[N],St}
+ end;
+variable(_, Def, St) -> {Def,add_error({not_var,St#lint.func}, St)}.
+
+%% variable_list([Var], State) -> {[Var],State}.
+%% variable_list([Var], [PatVar], State) -> {[Var],State}.
+
+variable_list(Vs, St) -> variable_list(Vs, [], St).
+
+variable_list(Vs, Ps, St) ->
+ foldl(fun (V, {Ps0,St0}) ->
+ {Vvs,St1} = variable(V, Ps0, St0),
+ {union(Vvs, Ps0),St1}
+ end, {Ps,St}, Vs).
+
+%% pattern(Pattern, Defined, State) -> {[PatVar],State}.
+%% pattern(Pattern, Defined, [PatVar], State) -> {[PatVar],State}.
+%% Patterns are complicated by sizes in binaries. These are pure
+%% input variables which create no bindings. We, therefor, need to
+%% carry around the original defined variables to get the correct
+%% handling.
+
+%% pattern(P, Def, St) -> pattern(P, Def, [], St).
+
+pattern(#c_var{name=N}, Def, Ps, St) ->
+ pat_var(N, Def, Ps, St);
+pattern(#c_int{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_float{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_atom{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_char{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_string{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_nil{}, _Def, Ps, St) -> {Ps,St};
+pattern(#c_cons{hd=H,tl=T}, Def, Ps, St) ->
+ pattern_list([H,T], Def, Ps, St);
+pattern(#c_tuple{es=Es}, Def, Ps, St) ->
+ pattern_list(Es, Def, Ps, St);
+pattern(#c_binary{segments=Ss}, Def, Ps, St) ->
+ pat_bin(Ss, Def, Ps, St);
+pattern(#c_alias{var=V,pat=P}, Def, Ps, St0) ->
+ {Vvs,St1} = variable(V, Ps, St0),
+ pattern(P, Def, union(Vvs, Ps), St1);
+pattern(_, _, Ps, St) -> {Ps,add_error({not_pattern,St#lint.func}, St)}.
+
+pat_var(N, _Def, Ps, St) ->
+ case is_element(N, Ps) of
+ true -> {Ps,add_error({duplicate_var,N,St#lint.func}, St)};
+ false -> {add_element(N, Ps),St}
+ end.
+
+%% pat_bin_list([Elem], Defined, [PatVar], State) -> {[PatVar],State}.
+
+pat_bin(Es, Def, Ps0, St0) ->
+ foldl(fun (E, {Ps,St}) -> pat_segment(E, Def, Ps, St) end, {Ps0,St0}, Es).
+
+pat_segment(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Def, Ps, St0) ->
+ St1 = bit_type(U, T, Fs, St0),
+ St2 = pat_bit_expr(S, T, Def, St1),
+ pattern(V, Def, Ps, St2);
+pat_segment(_, _, Ps, St) ->
+ {Ps,add_error({not_bs_pattern,St#lint.func}, St)}.
+
+%% pat_bit_expr(SizePat, Type, Defined, State) -> State.
+%% Check the Size pattern, this is an input! Be a bit tough here.
+
+pat_bit_expr(#c_int{val=I}, _, _, St) when I >= 0 -> St;
+pat_bit_expr(#c_var{name=N}, _, Def, St) ->
+ expr_var(N, Def, St);
+pat_bit_expr(#c_atom{val=all}, binary, _Def, St) -> St;
+pat_bit_expr(_, _, _, St) ->
+ add_error({illegal_expr,St#lint.func}, St).
+
+bit_type(Unit, Type, Flags, St) ->
+ U = core_lib:literal_value(Unit),
+ T = core_lib:literal_value(Type),
+ Fs = core_lib:literal_value(Flags),
+ case erl_bits:set_bit_type(default, [T,{unit,U}|Fs]) of
+ {ok,_,_} -> St;
+ {error,E} -> add_error({E,St#lint.func}, St)
+ end.
+
+%% pattern_list([Var], Defined, State) -> {[PatVar],State}.
+%% pattern_list([Var], Defined, [PatVar], State) -> {[PatVar],State}.
+
+pattern_list(Pats, Def, St) -> pattern_list(Pats, Def, [], St).
+
+pattern_list(Pats, Def, Ps0, St0) ->
+ foldl(fun (P, {Ps,St}) -> pattern(P, Def, Ps, St) end, {Ps0,St0}, Pats).
+
+%% pattern_match(Required, Supplied, State) -> State.
+%% Check that the required number of patterns match the supplied.
+
+pattern_match(N, N, St) -> St;
+pattern_match(_Req, _Sup, St) ->
+ add_error({pattern_mismatch,St#lint.func}, St).
+
+%% return_match(Required, Supplied, State) -> State.
+%% Check that the required number of return values match the supplied.
+
+return_match(any, _Sup, St) -> St;
+return_match(_Req, unknown, St) -> St;
+return_match(N, N, St) -> St;
+return_match(_Req, _Sup, St) ->
+ add_error({return_mismatch,St#lint.func}, St).
+
+%% arg_match(Required, Supplied, State) -> State.
+
+arg_match(_Req, unknown, St) -> St;
+arg_match(N, N, St) -> St;
+arg_match(_Req, _Sup, St) ->
+ add_error({arg_mismatch,St#lint.func}, St).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl
new file mode 100644
index 0000000000..942845bef7
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.erl
@@ -0,0 +1,4911 @@
+-module(core_parse).
+-define(THIS_MODULE, core_parse).
+-export([parse/1, parse_and_scan/1, format_error/1]).
+
+-export([abstract/1,abstract/2,normalise/1]).
+
+%% The following directive is needed for (significantly) faster compilation
+%% of the generated .erl file by the HiPE compiler. Please do not remove.
+-compile([{hipe,[{regalloc,linear_scan}]}]).
+
+-include("core_parse.hrl").
+
+tok_val(T) -> element(3, T).
+tok_line(T) -> element(2, T).
+
+abstract(T, _N) -> abstract(T).
+
+abstract(Term) -> core_lib:make_literal(Term).
+
+normalise(Core) -> core_lib:literal_value(Core).
+
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_parse.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% The parser generator will insert appropriate declarations before this line.%
+
+parse(Tokens) ->
+ case catch yeccpars1(Tokens, false, 0, [], []) of
+ error ->
+ Errorline =
+ if Tokens == [] -> 0; true -> element(2, hd(Tokens)) end,
+ {error,
+ {Errorline, ?THIS_MODULE, "syntax error at or after this line."}};
+ Other ->
+ Other
+ end.
+
+parse_and_scan({Mod, Fun, Args}) ->
+ case apply(Mod, Fun, Args) of
+ {eof, _} ->
+ {ok, eof};
+ {error, Descriptor, _} ->
+ {error, Descriptor};
+ {ok, Tokens, _} ->
+ yeccpars1(Tokens, {Mod, Fun, Args}, 0, [], [])
+ end.
+
+format_error(Message) ->
+ case io_lib:deep_char_list(Message) of
+ true ->
+ Message;
+ _ ->
+ io_lib:write(Message)
+ end.
+
+% To be used in grammar files to throw an error message to the parser toplevel.
+% Doesn't have to be exported!
+return_error(Line, Message) ->
+ throw({error, {Line, ?THIS_MODULE, Message}}).
+
+
+% Don't change yeccpars1/6 too much, it is called recursively by yeccpars2/8!
+yeccpars1([Token | Tokens], Tokenizer, State, States, Vstack) ->
+ yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens,
+ Tokenizer);
+yeccpars1([], {M, F, A}, State, States, Vstack) ->
+ case catch apply(M, F, A) of
+ {eof, Endline} ->
+ {error, {Endline, ?THIS_MODULE, "end_of_file"}};
+ {error, Descriptor, _Endline} ->
+ {error, Descriptor};
+ {'EXIT', Reason} ->
+ {error, {0, ?THIS_MODULE, Reason}};
+ {ok, Tokens, _Endline} ->
+ case catch yeccpars1(Tokens, {M, F, A}, State, States, Vstack) of
+ error ->
+ Errorline = element(2, hd(Tokens)),
+ {error, {Errorline, ?THIS_MODULE,
+ "syntax error at or after this line."}};
+ Other ->
+ Other
+ end
+ end;
+yeccpars1([], false, State, States, Vstack) ->
+ yeccpars2(State, '$end', States, Vstack, {'$end', 999999}, [], false).
+
+% For internal use only.
+yeccerror(Token) ->
+ {error,
+ {element(2, Token), ?THIS_MODULE,
+ ["syntax error before: ", yecctoken2string(Token)]}}.
+
+yecctoken2string({atom, _, A}) -> io_lib:write(A);
+yecctoken2string({integer,_,N}) -> io_lib:write(N);
+yecctoken2string({float,_,F}) -> io_lib:write(F);
+yecctoken2string({char,_,C}) -> io_lib:write_char(C);
+yecctoken2string({var,_,V}) -> io_lib:format('~s', [V]);
+yecctoken2string({string,_,S}) -> io_lib:write_string(S);
+yecctoken2string({reserved_symbol, _, A}) -> io_lib:format('~w', [A]);
+yecctoken2string({_Cat, _, Val}) -> io_lib:format('~w', [Val]);
+
+yecctoken2string({'dot', _}) -> io_lib:format('~w', ['.']);
+yecctoken2string({'$end', _}) ->
+ [];
+yecctoken2string({Other, _}) when atom(Other) ->
+ io_lib:format('~w', [Other]);
+yecctoken2string(Other) ->
+ io_lib:write(Other).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+yeccpars2(0, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 1, [0 | __Ss], [__T | __Stack]);
+yeccpars2(0, 'module', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 2, [0 | __Ss], [__T | __Stack]);
+yeccpars2(0, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(1, 'module', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 313, [1 | __Ss], [__T | __Stack]);
+yeccpars2(1, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(2, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 4, [2 | __Ss], [__T | __Stack]);
+yeccpars2(2, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(3, '$end', _, __Stack, _, _, _) ->
+ {ok, hd(__Stack)};
+yeccpars2(3, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(4, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 5, [4 | __Ss], [__T | __Stack]);
+yeccpars2(4, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(5, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [5 | __Ss], [__T | __Stack]);
+yeccpars2(5, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 306, [5 | __Ss], [__T | __Stack]);
+yeccpars2(5, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(6, 'attributes', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 7, [6 | __Ss], [__T | __Stack]);
+yeccpars2(6, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(7, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 276, [7 | __Ss], [__T | __Stack]);
+yeccpars2(7, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(8, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [8 | __Ss], [__T | __Stack]);
+yeccpars2(8, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [8 | __Ss], [__T | __Stack]);
+yeccpars2(8, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(13, __Cat, [8 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(9, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [9 | __Ss], [__T | __Stack]);
+yeccpars2(9, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(10, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 20, [10 | __Ss], [__T | __Stack]);
+yeccpars2(10, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(11, '/', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 18, [11 | __Ss], [__T | __Stack]);
+yeccpars2(11, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(12, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [12 | __Ss], [__T | __Stack]);
+yeccpars2(12, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [12 | __Ss], [__T | __Stack]);
+yeccpars2(12, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(17, __Cat, [12 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(13, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(module_defs, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(14, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_function_name, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(15, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 16, [15 | __Ss], [__T | __Stack]);
+yeccpars2(15, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(16, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_module{name = #c_atom{val = tok_val(__2)}, exports = __3, attrs = __4, defs = __5},
+ __Nss = lists:nthtail(5, __Ss),
+ yeccpars2(yeccgoto(module_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(17, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__2],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(function_definitions, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(18, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 19, [18 | __Ss], [__T | __Stack]);
+yeccpars2(18, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(19, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_fname{id = tok_val(__1), arity = tok_val(__3)},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(function_name, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(20, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [20 | __Ss], [__T | __Stack]);
+yeccpars2(20, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 21, [20 | __Ss], [__T | __Stack]);
+yeccpars2(20, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(21, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [21 | __Ss], [__T | __Stack]);
+yeccpars2(21, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(22, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_def{name = __1, val = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(function_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(23, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 25, [23 | __Ss], [__T | __Stack]);
+yeccpars2(23, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(24, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_fun, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(25, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 27, [25 | __Ss], [__T | __Stack]);
+yeccpars2(25, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [25 | __Ss], [__T | __Stack]);
+yeccpars2(25, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [25 | __Ss], [__T | __Stack]);
+yeccpars2(25, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(26, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [26 | __Ss], [__T | __Stack]);
+yeccpars2(26, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(27, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 265, [27 | __Ss], [__T | __Stack]);
+yeccpars2(27, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(28, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 263, [28 | __Ss], [__T | __Stack]);
+yeccpars2(28, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_variables, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(29, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 32, [29 | __Ss], [__T | __Stack]);
+yeccpars2(29, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(30, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_var{name = tok_val(__1)},
+ yeccpars2(yeccgoto(variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(31, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(32, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 33, [32 | __Ss], [__T | __Stack]);
+yeccpars2(32, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(33, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [33 | __Ss], [__T | __Stack]);
+yeccpars2(33, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(34, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 247, [34 | __Ss], [__T | __Stack]);
+yeccpars2(34, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(35, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [35 | __Ss], [__T | __Stack]);
+yeccpars2(35, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(36, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 240, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [36 | __Ss], [__T | __Stack]);
+yeccpars2(36, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(37, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [37 | __Ss], [__T | __Stack]);
+yeccpars2(37, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(38, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_fun{vars = __3, body = __6},
+ __Nss = lists:nthtail(5, __Ss),
+ yeccpars2(yeccgoto(fun_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(39, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(40, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [40 | __Ss], [__T | __Stack]);
+yeccpars2(40, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(41, '/', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 18, [41 | __Ss], [__T | __Stack]);
+yeccpars2(41, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_atom{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(42, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(43, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(44, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [44 | __Ss], [__T | __Stack]);
+yeccpars2(44, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(45, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(46, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [46 | __Ss], [__T | __Stack]);
+yeccpars2(46, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(47, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(48, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [48 | __Ss], [__T | __Stack]);
+yeccpars2(48, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(49, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(50, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_char{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(51, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(52, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [52 | __Ss], [__T | __Stack]);
+yeccpars2(52, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(53, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(54, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_float{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(55, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(56, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(57, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_int{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(58, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 83, [58 | __Ss], [__T | __Stack]);
+yeccpars2(58, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [58 | __Ss], [__T | __Stack]);
+yeccpars2(58, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [58 | __Ss], [__T | __Stack]);
+yeccpars2(58, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(59, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(60, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [60 | __Ss], [__T | __Stack]);
+yeccpars2(60, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [60 | __Ss], [__T | __Stack]);
+yeccpars2(60, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(210, __Cat, [60 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(61, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(62, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(63, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 208, [63 | __Ss], [__T | __Stack]);
+yeccpars2(63, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(64, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(65, 'after', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 99, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 96, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [65 | __Ss], [__T | __Stack]);
+yeccpars2(65, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(66, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(67, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(68, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(69, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_string{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(70, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [70 | __Ss], [__T | __Stack]);
+yeccpars2(70, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(71, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(72, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(73, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(single_expression, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(74, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 77, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [74 | __Ss], [__T | __Stack]);
+yeccpars2(74, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(75, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 79, [75 | __Ss], [__T | __Stack]);
+yeccpars2(75, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_expressions, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(76, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 78, [76 | __Ss], [__T | __Stack]);
+yeccpars2(76, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(77, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(78, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(79, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [79 | __Ss], [__T | __Stack]);
+yeccpars2(79, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(80, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(anno_expressions, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(81, 'of', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 82, [81 | __Ss], [__T | __Stack]);
+yeccpars2(81, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(82, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 83, [82 | __Ss], [__T | __Stack]);
+yeccpars2(82, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [82 | __Ss], [__T | __Stack]);
+yeccpars2(82, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [82 | __Ss], [__T | __Stack]);
+yeccpars2(82, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(83, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 92, [83 | __Ss], [__T | __Stack]);
+yeccpars2(83, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [83 | __Ss], [__T | __Stack]);
+yeccpars2(83, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [83 | __Ss], [__T | __Stack]);
+yeccpars2(83, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(84, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(let_vars, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(85, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 86, [85 | __Ss], [__T | __Stack]);
+yeccpars2(85, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(86, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [86 | __Ss], [__T | __Stack]);
+yeccpars2(86, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(87, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 88, [87 | __Ss], [__T | __Stack]);
+yeccpars2(87, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(88, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 83, [88 | __Ss], [__T | __Stack]);
+yeccpars2(88, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [88 | __Ss], [__T | __Stack]);
+yeccpars2(88, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [88 | __Ss], [__T | __Stack]);
+yeccpars2(88, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(89, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 90, [89 | __Ss], [__T | __Stack]);
+yeccpars2(89, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(90, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [90 | __Ss], [__T | __Stack]);
+yeccpars2(90, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(91, __Cat, __Ss, [__10,__9,__8,__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = if length(__8) == 2 -> #c_try{arg = __2, vars = __4, body = __6, evars = __8, handler = __10}; true -> return_error(tok_line(__7),"expected 2 exception variables in 'try'") end,
+ __Nss = lists:nthtail(9, __Ss),
+ yeccpars2(yeccgoto(try_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(92, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(let_vars, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(93, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 94, [93 | __Ss], [__T | __Stack]);
+yeccpars2(93, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(94, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(let_vars, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(95, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 190, [95 | __Ss], [__T | __Stack]);
+yeccpars2(95, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(96, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [96 | __Ss], [__T | __Stack]);
+yeccpars2(96, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(97, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 182, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [97 | __Ss], [__T | __Stack]);
+yeccpars2(97, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(98, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [98 | __Ss], [__T | __Stack]);
+yeccpars2(98, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(99, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [99 | __Ss], [__T | __Stack]);
+yeccpars2(99, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(100, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 96, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [100 | __Ss], [__T | __Stack]);
+yeccpars2(100, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_clauses, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(101, 'after', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 99, [101 | __Ss], [__T | __Stack]);
+yeccpars2(101, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(102, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(clause_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(103, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 162, [103 | __Ss], [__T | __Stack]);
+yeccpars2(103, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(104, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_atom{val = tok_val(__1)},
+ yeccpars2(yeccgoto(atomic_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(105, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(atomic_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(106, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(107, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(108, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_clause, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(109, 'when', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 164, [109 | __Ss], [__T | __Stack]);
+yeccpars2(109, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(110, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(111, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(112, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = begin
+ {T,A} = __2, #c_receive{clauses = [], timeout = T, action = A}
+ end,
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(receive_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(113, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(other_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(114, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 118, [114 | __Ss], [__T | __Stack]);
+yeccpars2(114, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(115, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [115 | __Ss], [__T | __Stack]);
+yeccpars2(115, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(116, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 120, [116 | __Ss], [__T | __Stack]);
+yeccpars2(116, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(anno_patterns, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(117, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 119, [117 | __Ss], [__T | __Stack]);
+yeccpars2(117, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(118, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(119, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(120, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [120 | __Ss], [__T | __Stack]);
+yeccpars2(120, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(121, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(anno_patterns, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(122, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 162, [122 | __Ss], [__T | __Stack]);
+yeccpars2(122, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(123, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 159, [123 | __Ss], [__T | __Stack]);
+yeccpars2(123, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(124, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 125, [124 | __Ss], [__T | __Stack]);
+yeccpars2(124, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_variable, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(125, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [125 | __Ss], [__T | __Stack]);
+yeccpars2(125, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(126, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 130, [126 | __Ss], [__T | __Stack]);
+yeccpars2(126, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(127, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 128, [127 | __Ss], [__T | __Stack]);
+yeccpars2(127, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(128, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_variable, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(129, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [129 | __Ss], [__T | __Stack]);
+yeccpars2(129, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(130, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(annotation, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(131, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(132, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(133, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(134, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(135, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 147, [135 | __Ss], [__T | __Stack]);
+yeccpars2(135, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(constants, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(136, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 146, [136 | __Ss], [__T | __Stack]);
+yeccpars2(136, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(137, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(138, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(139, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(140, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = tok_val(__1),
+ yeccpars2(yeccgoto(atomic_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(141, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(142, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 144, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [142 | __Ss], [__T | __Stack]);
+yeccpars2(142, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(143, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 145, [143 | __Ss], [__T | __Stack]);
+yeccpars2(143, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(144, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = {},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(145, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = list_to_tuple(__2),
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(146, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(annotation, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(147, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [147 | __Ss], [__T | __Stack]);
+yeccpars2(147, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(148, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(constants, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(149, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = {nil,tok_line(__1)},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(nil, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(150, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 151, [150 | __Ss], [__T | __Stack]);
+yeccpars2(150, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 154, [150 | __Ss], [__T | __Stack]);
+yeccpars2(150, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 152, [150 | __Ss], [__T | __Stack]);
+yeccpars2(150, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(151, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [151 | __Ss], [__T | __Stack]);
+yeccpars2(151, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(152, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(yeccgoto(tail_constant, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(153, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__2|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(154, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 129, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 142, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 140, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 131, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 137, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 138, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 133, [154 | __Ss], [__T | __Stack]);
+yeccpars2(154, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(155, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 156, [155 | __Ss], [__T | __Stack]);
+yeccpars2(155, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(156, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(157, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 151, [157 | __Ss], [__T | __Stack]);
+yeccpars2(157, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 154, [157 | __Ss], [__T | __Stack]);
+yeccpars2(157, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 152, [157 | __Ss], [__T | __Stack]);
+yeccpars2(157, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(158, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__2|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_constant, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(159, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [159 | __Ss], [__T | __Stack]);
+yeccpars2(159, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(160, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 161, [160 | __Ss], [__T | __Stack]);
+yeccpars2(160, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(161, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(162, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [162 | __Ss], [__T | __Stack]);
+yeccpars2(162, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(163, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_alias{var = __1, pat = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(other_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(164, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [164 | __Ss], [__T | __Stack]);
+yeccpars2(164, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(165, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 166, [165 | __Ss], [__T | __Stack]);
+yeccpars2(165, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(166, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [166 | __Ss], [__T | __Stack]);
+yeccpars2(166, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(167, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_clause{pats = __1, guard = __3, body = __5},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(clause, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(168, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = begin
+ {T,A} = __3, #c_receive{clauses = __2, timeout = T, action = A}
+ end,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(receive_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(169, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__2],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(anno_clauses, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(170, '->', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 171, [170 | __Ss], [__T | __Stack]);
+yeccpars2(170, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(171, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [171 | __Ss], [__T | __Stack]);
+yeccpars2(171, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(172, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = {__2,__4},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(timeout, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(173, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 174, [173 | __Ss], [__T | __Stack]);
+yeccpars2(173, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 177, [173 | __Ss], [__T | __Stack]);
+yeccpars2(173, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 175, [173 | __Ss], [__T | __Stack]);
+yeccpars2(173, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(174, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [174 | __Ss], [__T | __Stack]);
+yeccpars2(174, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(175, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(tail_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(176, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(177, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [177 | __Ss], [__T | __Stack]);
+yeccpars2(177, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(178, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 179, [178 | __Ss], [__T | __Stack]);
+yeccpars2(178, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(179, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(180, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 174, [180 | __Ss], [__T | __Stack]);
+yeccpars2(180, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 177, [180 | __Ss], [__T | __Stack]);
+yeccpars2(180, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 175, [180 | __Ss], [__T | __Stack]);
+yeccpars2(180, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(181, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(182, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(clause_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(183, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 184, [183 | __Ss], [__T | __Stack]);
+yeccpars2(183, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(184, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(clause_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(185, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 187, [185 | __Ss], [__T | __Stack]);
+yeccpars2(185, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(186, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 159, [186 | __Ss], [__T | __Stack]);
+yeccpars2(186, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(anno_pattern, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(187, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [187 | __Ss], [__T | __Stack]);
+yeccpars2(187, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(188, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 189, [188 | __Ss], [__T | __Stack]);
+yeccpars2(188, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(189, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_clause, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(190, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 191, [190 | __Ss], [__T | __Stack]);
+yeccpars2(190, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 194, [190 | __Ss], [__T | __Stack]);
+yeccpars2(190, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(191, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 200, [191 | __Ss], [__T | __Stack]);
+yeccpars2(191, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(192, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 198, [192 | __Ss], [__T | __Stack]);
+yeccpars2(192, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(segment_patterns, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(193, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 196, [193 | __Ss], [__T | __Stack]);
+yeccpars2(193, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(194, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 195, [194 | __Ss], [__T | __Stack]);
+yeccpars2(194, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(195, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = []},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(binary_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(196, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 197, [196 | __Ss], [__T | __Stack]);
+yeccpars2(196, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(197, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = __3},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(binary_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(198, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 191, [198 | __Ss], [__T | __Stack]);
+yeccpars2(198, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(199, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(segment_patterns, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(200, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 115, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [200 | __Ss], [__T | __Stack]);
+yeccpars2(200, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(201, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 202, [201 | __Ss], [__T | __Stack]);
+yeccpars2(201, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(202, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [202 | __Ss], [__T | __Stack]);
+yeccpars2(202, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(203, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 205, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [203 | __Ss], [__T | __Stack]);
+yeccpars2(203, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(204, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = case __5 of [S,U,T,Fs] -> #c_bitstr{val = __3, size = S, unit = U, type = T, flags = Fs}; true -> return_error(tok_line(__1),"expected 4 arguments in binary segment") end,
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(segment_pattern, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(205, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(arg_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(206, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 207, [206 | __Ss], [__T | __Stack]);
+yeccpars2(206, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(207, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(arg_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(208, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [208 | __Ss], [__T | __Stack]);
+yeccpars2(208, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(209, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = begin
+ Name = #c_atom{val = tok_val(__2)}, #c_primop{name = Name, args = __3}
+ end,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(primop_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(210, 'in', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 211, [210 | __Ss], [__T | __Stack]);
+yeccpars2(210, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(211, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [211 | __Ss], [__T | __Stack]);
+yeccpars2(211, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(212, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_letrec{defs = __2, body = __4},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(letrec_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(213, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 214, [213 | __Ss], [__T | __Stack]);
+yeccpars2(213, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(214, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [214 | __Ss], [__T | __Stack]);
+yeccpars2(214, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(215, 'in', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 216, [215 | __Ss], [__T | __Stack]);
+yeccpars2(215, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(216, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [216 | __Ss], [__T | __Stack]);
+yeccpars2(216, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(217, __Cat, __Ss, [__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_let{vars = __2, arg = __4, body = __6},
+ __Nss = lists:nthtail(5, __Ss),
+ yeccpars2(yeccgoto(let_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(218, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [218 | __Ss], [__T | __Stack]);
+yeccpars2(218, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(219, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_seq{arg = __2, body = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(sequence, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(220, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_catch{body = __2},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(catch_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(221, 'of', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 222, [221 | __Ss], [__T | __Stack]);
+yeccpars2(221, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(222, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 97, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 96, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 95, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 98, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 114, [222 | __Ss], [__T | __Stack]);
+yeccpars2(222, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(223, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 224, [223 | __Ss], [__T | __Stack]);
+yeccpars2(223, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(224, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_case{arg = __2, clauses = __4},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(case_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(225, ':', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 226, [225 | __Ss], [__T | __Stack]);
+yeccpars2(225, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(226, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [226 | __Ss], [__T | __Stack]);
+yeccpars2(226, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(227, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [227 | __Ss], [__T | __Stack]);
+yeccpars2(227, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(228, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_call{module = __2, name = __4, args = __5},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(call_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(229, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 203, [229 | __Ss], [__T | __Stack]);
+yeccpars2(229, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(230, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_apply{op = __2, args = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(application_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(231, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 232, [231 | __Ss], [__T | __Stack]);
+yeccpars2(231, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 235, [231 | __Ss], [__T | __Stack]);
+yeccpars2(231, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 233, [231 | __Ss], [__T | __Stack]);
+yeccpars2(231, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(232, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [232 | __Ss], [__T | __Stack]);
+yeccpars2(232, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(233, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(tail, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(234, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(235, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [235 | __Ss], [__T | __Stack]);
+yeccpars2(235, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(236, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 237, [236 | __Ss], [__T | __Stack]);
+yeccpars2(236, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(237, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(238, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 232, [238 | __Ss], [__T | __Stack]);
+yeccpars2(238, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 235, [238 | __Ss], [__T | __Stack]);
+yeccpars2(238, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 233, [238 | __Ss], [__T | __Stack]);
+yeccpars2(238, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(239, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(240, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_values{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(241, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 242, [241 | __Ss], [__T | __Stack]);
+yeccpars2(241, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(242, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_values{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(243, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 244, [243 | __Ss], [__T | __Stack]);
+yeccpars2(243, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(244, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [244 | __Ss], [__T | __Stack]);
+yeccpars2(244, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(245, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 246, [245 | __Ss], [__T | __Stack]);
+yeccpars2(245, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(246, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_expression, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(247, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 248, [247 | __Ss], [__T | __Stack]);
+yeccpars2(247, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 251, [247 | __Ss], [__T | __Stack]);
+yeccpars2(247, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(248, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 257, [248 | __Ss], [__T | __Stack]);
+yeccpars2(248, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(249, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 255, [249 | __Ss], [__T | __Stack]);
+yeccpars2(249, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(segments, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(250, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 253, [250 | __Ss], [__T | __Stack]);
+yeccpars2(250, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(251, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 252, [251 | __Ss], [__T | __Stack]);
+yeccpars2(251, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(252, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = []},
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(binary, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(253, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 254, [253 | __Ss], [__T | __Stack]);
+yeccpars2(253, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(254, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_binary{segments = __3},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(binary, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(255, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 248, [255 | __Ss], [__T | __Stack]);
+yeccpars2(255, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(256, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(segments, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(257, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [257 | __Ss], [__T | __Stack]);
+yeccpars2(257, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(258, '>', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 259, [258 | __Ss], [__T | __Stack]);
+yeccpars2(258, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(259, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 260, [259 | __Ss], [__T | __Stack]);
+yeccpars2(259, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(260, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [260 | __Ss], [__T | __Stack]);
+yeccpars2(260, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(261, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 262, [261 | __Ss], [__T | __Stack]);
+yeccpars2(261, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(262, __Cat, __Ss, [__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = case __6 of [S,U,T,Fs] -> #c_bitstr{val = __3, size = S, unit = U, type = T, flags = Fs}; true -> return_error(tok_line(__1),"expected 4 arguments in binary segment") end,
+ __Nss = lists:nthtail(6, __Ss),
+ yeccpars2(yeccgoto(segment, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(263, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 26, [263 | __Ss], [__T | __Stack]);
+yeccpars2(263, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [263 | __Ss], [__T | __Stack]);
+yeccpars2(263, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(264, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(anno_variables, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(265, 'receive', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 65, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'catch', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 48, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'try', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 70, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'primop', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 63, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'call', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 44, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'apply', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 40, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'case', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 46, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'letrec', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 60, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'let', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 58, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'fun', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 23, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'do', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 52, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 41, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '#', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 34, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 37, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 74, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '<', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 36, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 35, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, 'var', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 30, [265 | __Ss], [__T | __Stack]);
+yeccpars2(265, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(266, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_fun{vars = [], body = __5},
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(fun_expr, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(267, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 125, [267 | __Ss], [__T | __Stack]);
+yeccpars2(267, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(268, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 269, [268 | __Ss], [__T | __Stack]);
+yeccpars2(268, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(269, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [269 | __Ss], [__T | __Stack]);
+yeccpars2(269, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(270, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 271, [270 | __Ss], [__T | __Stack]);
+yeccpars2(270, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(271, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_fun, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(272, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 273, [272 | __Ss], [__T | __Stack]);
+yeccpars2(272, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(273, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [273 | __Ss], [__T | __Stack]);
+yeccpars2(273, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(274, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 275, [274 | __Ss], [__T | __Stack]);
+yeccpars2(274, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(275, __Cat, __Ss, [__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = core_lib:set_anno(__2,__4),
+ __Nss = lists:nthtail(4, __Ss),
+ yeccpars2(yeccgoto(anno_function_name, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(276, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 278, [276 | __Ss], [__T | __Stack]);
+yeccpars2(276, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 277, [276 | __Ss], [__T | __Stack]);
+yeccpars2(276, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(277, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(module_attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(278, '=', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 284, [278 | __Ss], [__T | __Stack]);
+yeccpars2(278, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(279, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 282, [279 | __Ss], [__T | __Stack]);
+yeccpars2(279, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(attribute_list, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(280, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 281, [280 | __Ss], [__T | __Stack]);
+yeccpars2(280, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(281, __Cat, __Ss, [__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __3,
+ __Nss = lists:nthtail(3, __Ss),
+ yeccpars2(yeccgoto(module_attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(282, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 278, [282 | __Ss], [__T | __Stack]);
+yeccpars2(282, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(283, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(attribute_list, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(284, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [284 | __Ss], [__T | __Stack]);
+yeccpars2(284, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(285, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 149, [285 | __Ss], [__T | __Stack]);
+yeccpars2(285, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(286, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(287, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(288, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_def{name = #c_atom{val = tok_val(__1)}, val = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(attribute, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(289, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(290, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 293, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [290 | __Ss], [__T | __Stack]);
+yeccpars2(290, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(291, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 295, [291 | __Ss], [__T | __Stack]);
+yeccpars2(291, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(literals, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(292, '}', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 294, [292 | __Ss], [__T | __Stack]);
+yeccpars2(292, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(293, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = []},
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(tuple_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(294, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_tuple{es = __2},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tuple_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(295, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [295 | __Ss], [__T | __Stack]);
+yeccpars2(295, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(296, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(literals, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(297, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 298, [297 | __Ss], [__T | __Stack]);
+yeccpars2(297, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 301, [297 | __Ss], [__T | __Stack]);
+yeccpars2(297, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 299, [297 | __Ss], [__T | __Stack]);
+yeccpars2(297, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(298, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [298 | __Ss], [__T | __Stack]);
+yeccpars2(298, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(299, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_nil{},
+ yeccpars2(yeccgoto(tail_literal, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(300, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(cons_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(301, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 285, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, '{', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 290, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'string', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 69, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 104, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'float', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 54, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'integer', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 57, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, 'char', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 50, [301 | __Ss], [__T | __Stack]);
+yeccpars2(301, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(302, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 303, [302 | __Ss], [__T | __Stack]);
+yeccpars2(302, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(303, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(304, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 298, [304 | __Ss], [__T | __Stack]);
+yeccpars2(304, '|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 301, [304 | __Ss], [__T | __Stack]);
+yeccpars2(304, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 299, [304 | __Ss], [__T | __Stack]);
+yeccpars2(304, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(305, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_cons{hd = __2, tl = __3},
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(tail_literal, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(306, __Cat, __Ss, [__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [],
+ __Nss = lists:nthtail(1, __Ss),
+ yeccpars2(yeccgoto(module_export, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(307, ',', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 311, [307 | __Ss], [__T | __Stack]);
+yeccpars2(307, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1],
+ yeccpars2(yeccgoto(exported_names, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(308, ']', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 310, [308 | __Ss], [__T | __Stack]);
+yeccpars2(308, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(309, __Cat, __Ss, [__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __1,
+ yeccpars2(yeccgoto(exported_name, hd(__Ss)), __Cat, __Ss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(310, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = __2,
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(module_export, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(311, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [311 | __Ss], [__T | __Stack]);
+yeccpars2(311, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(312, __Cat, __Ss, [__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = [__1|__3],
+ __Nss = lists:nthtail(2, __Ss),
+ yeccpars2(yeccgoto(exported_names, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(313, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 314, [313 | __Ss], [__T | __Stack]);
+yeccpars2(313, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(314, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 5, [314 | __Ss], [__T | __Stack]);
+yeccpars2(314, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(315, 'attributes', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 7, [315 | __Ss], [__T | __Stack]);
+yeccpars2(315, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(316, '(', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 9, [316 | __Ss], [__T | __Stack]);
+yeccpars2(316, 'atom', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 11, [316 | __Ss], [__T | __Stack]);
+yeccpars2(316, __Cat, __Ss, __Stack, __T, __Ts, __Tzr) ->
+ __Val = [],
+ yeccpars2(13, __Cat, [316 | __Ss], [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(317, 'end', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 318, [317 | __Ss], [__T | __Stack]);
+yeccpars2(317, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(318, '-|', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 319, [318 | __Ss], [__T | __Stack]);
+yeccpars2(318, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(319, '[', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 126, [319 | __Ss], [__T | __Stack]);
+yeccpars2(319, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(320, ')', __Ss, __Stack, __T, __Ts, __Tzr) ->
+ yeccpars1(__Ts, __Tzr, 321, [320 | __Ss], [__T | __Stack]);
+yeccpars2(320, _, _, _, __T, _, _) ->
+ yeccerror(__T);
+yeccpars2(321, __Cat, __Ss, [__10,__9,__8,__7,__6,__5,__4,__3,__2,__1|__Stack], __T, __Ts, __Tzr) ->
+ __Val = #c_module{anno = __9, name = tok_val(__3), exports = __4, attrs = __5, defs = __6},
+ __Nss = lists:nthtail(9, __Ss),
+ yeccpars2(yeccgoto(module_definition, hd(__Nss)), __Cat, __Nss, [__Val | __Stack], __T, __Ts, __Tzr);
+yeccpars2(__Other, _, _, _, _, _, _) ->
+ exit({parser, __Other, missing_state_in_action_table}).
+
+yeccgoto(anno_clause, 65) ->
+ 100;
+yeccgoto(anno_clause, 100) ->
+ 100;
+yeccgoto(anno_clause, 222) ->
+ 100;
+yeccgoto(anno_clauses, 65) ->
+ 101;
+yeccgoto(anno_clauses, 100) ->
+ 169;
+yeccgoto(anno_clauses, 222) ->
+ 223;
+yeccgoto(anno_expression, 33) ->
+ 38;
+yeccgoto(anno_expression, 36) ->
+ 75;
+yeccgoto(anno_expression, 37) ->
+ 231;
+yeccgoto(anno_expression, 40) ->
+ 229;
+yeccgoto(anno_expression, 44) ->
+ 225;
+yeccgoto(anno_expression, 46) ->
+ 221;
+yeccgoto(anno_expression, 48) ->
+ 220;
+yeccgoto(anno_expression, 52) ->
+ 218;
+yeccgoto(anno_expression, 70) ->
+ 81;
+yeccgoto(anno_expression, 74) ->
+ 75;
+yeccgoto(anno_expression, 79) ->
+ 75;
+yeccgoto(anno_expression, 86) ->
+ 87;
+yeccgoto(anno_expression, 90) ->
+ 91;
+yeccgoto(anno_expression, 99) ->
+ 170;
+yeccgoto(anno_expression, 164) ->
+ 165;
+yeccgoto(anno_expression, 166) ->
+ 167;
+yeccgoto(anno_expression, 171) ->
+ 172;
+yeccgoto(anno_expression, 203) ->
+ 75;
+yeccgoto(anno_expression, 211) ->
+ 212;
+yeccgoto(anno_expression, 214) ->
+ 215;
+yeccgoto(anno_expression, 216) ->
+ 217;
+yeccgoto(anno_expression, 218) ->
+ 219;
+yeccgoto(anno_expression, 226) ->
+ 227;
+yeccgoto(anno_expression, 232) ->
+ 238;
+yeccgoto(anno_expression, 235) ->
+ 236;
+yeccgoto(anno_expression, 257) ->
+ 258;
+yeccgoto(anno_expression, 260) ->
+ 75;
+yeccgoto(anno_expression, 265) ->
+ 266;
+yeccgoto(anno_expressions, 36) ->
+ 241;
+yeccgoto(anno_expressions, 74) ->
+ 76;
+yeccgoto(anno_expressions, 79) ->
+ 80;
+yeccgoto(anno_expressions, 203) ->
+ 206;
+yeccgoto(anno_expressions, 260) ->
+ 261;
+yeccgoto(anno_fun, 20) ->
+ 22;
+yeccgoto(anno_function_name, 8) ->
+ 10;
+yeccgoto(anno_function_name, 12) ->
+ 10;
+yeccgoto(anno_function_name, 60) ->
+ 10;
+yeccgoto(anno_function_name, 316) ->
+ 10;
+yeccgoto(anno_pattern, 65) ->
+ 102;
+yeccgoto(anno_pattern, 96) ->
+ 102;
+yeccgoto(anno_pattern, 97) ->
+ 116;
+yeccgoto(anno_pattern, 98) ->
+ 173;
+yeccgoto(anno_pattern, 100) ->
+ 102;
+yeccgoto(anno_pattern, 114) ->
+ 116;
+yeccgoto(anno_pattern, 120) ->
+ 116;
+yeccgoto(anno_pattern, 162) ->
+ 163;
+yeccgoto(anno_pattern, 174) ->
+ 180;
+yeccgoto(anno_pattern, 177) ->
+ 178;
+yeccgoto(anno_pattern, 200) ->
+ 201;
+yeccgoto(anno_pattern, 222) ->
+ 102;
+yeccgoto(anno_patterns, 97) ->
+ 183;
+yeccgoto(anno_patterns, 114) ->
+ 117;
+yeccgoto(anno_patterns, 120) ->
+ 121;
+yeccgoto(anno_variable, 25) ->
+ 28;
+yeccgoto(anno_variable, 58) ->
+ 84;
+yeccgoto(anno_variable, 65) ->
+ 103;
+yeccgoto(anno_variable, 82) ->
+ 84;
+yeccgoto(anno_variable, 83) ->
+ 28;
+yeccgoto(anno_variable, 88) ->
+ 84;
+yeccgoto(anno_variable, 96) ->
+ 103;
+yeccgoto(anno_variable, 97) ->
+ 103;
+yeccgoto(anno_variable, 98) ->
+ 103;
+yeccgoto(anno_variable, 100) ->
+ 103;
+yeccgoto(anno_variable, 114) ->
+ 103;
+yeccgoto(anno_variable, 115) ->
+ 122;
+yeccgoto(anno_variable, 120) ->
+ 103;
+yeccgoto(anno_variable, 162) ->
+ 103;
+yeccgoto(anno_variable, 174) ->
+ 103;
+yeccgoto(anno_variable, 177) ->
+ 103;
+yeccgoto(anno_variable, 200) ->
+ 103;
+yeccgoto(anno_variable, 222) ->
+ 103;
+yeccgoto(anno_variable, 263) ->
+ 28;
+yeccgoto(anno_variables, 25) ->
+ 29;
+yeccgoto(anno_variables, 83) ->
+ 93;
+yeccgoto(anno_variables, 263) ->
+ 264;
+yeccgoto(annotation, 125) ->
+ 127;
+yeccgoto(annotation, 159) ->
+ 160;
+yeccgoto(annotation, 187) ->
+ 188;
+yeccgoto(annotation, 244) ->
+ 245;
+yeccgoto(annotation, 269) ->
+ 270;
+yeccgoto(annotation, 273) ->
+ 274;
+yeccgoto(annotation, 319) ->
+ 320;
+yeccgoto(application_expr, 33) ->
+ 39;
+yeccgoto(application_expr, 35) ->
+ 39;
+yeccgoto(application_expr, 36) ->
+ 39;
+yeccgoto(application_expr, 37) ->
+ 39;
+yeccgoto(application_expr, 40) ->
+ 39;
+yeccgoto(application_expr, 44) ->
+ 39;
+yeccgoto(application_expr, 46) ->
+ 39;
+yeccgoto(application_expr, 48) ->
+ 39;
+yeccgoto(application_expr, 52) ->
+ 39;
+yeccgoto(application_expr, 70) ->
+ 39;
+yeccgoto(application_expr, 74) ->
+ 39;
+yeccgoto(application_expr, 79) ->
+ 39;
+yeccgoto(application_expr, 86) ->
+ 39;
+yeccgoto(application_expr, 90) ->
+ 39;
+yeccgoto(application_expr, 99) ->
+ 39;
+yeccgoto(application_expr, 164) ->
+ 39;
+yeccgoto(application_expr, 166) ->
+ 39;
+yeccgoto(application_expr, 171) ->
+ 39;
+yeccgoto(application_expr, 203) ->
+ 39;
+yeccgoto(application_expr, 211) ->
+ 39;
+yeccgoto(application_expr, 214) ->
+ 39;
+yeccgoto(application_expr, 216) ->
+ 39;
+yeccgoto(application_expr, 218) ->
+ 39;
+yeccgoto(application_expr, 226) ->
+ 39;
+yeccgoto(application_expr, 232) ->
+ 39;
+yeccgoto(application_expr, 235) ->
+ 39;
+yeccgoto(application_expr, 257) ->
+ 39;
+yeccgoto(application_expr, 260) ->
+ 39;
+yeccgoto(application_expr, 265) ->
+ 39;
+yeccgoto(arg_list, 202) ->
+ 204;
+yeccgoto(arg_list, 208) ->
+ 209;
+yeccgoto(arg_list, 227) ->
+ 228;
+yeccgoto(arg_list, 229) ->
+ 230;
+yeccgoto(atomic_constant, 126) ->
+ 132;
+yeccgoto(atomic_constant, 129) ->
+ 132;
+yeccgoto(atomic_constant, 142) ->
+ 132;
+yeccgoto(atomic_constant, 147) ->
+ 132;
+yeccgoto(atomic_constant, 151) ->
+ 132;
+yeccgoto(atomic_constant, 154) ->
+ 132;
+yeccgoto(atomic_literal, 33) ->
+ 42;
+yeccgoto(atomic_literal, 35) ->
+ 42;
+yeccgoto(atomic_literal, 36) ->
+ 42;
+yeccgoto(atomic_literal, 37) ->
+ 42;
+yeccgoto(atomic_literal, 40) ->
+ 42;
+yeccgoto(atomic_literal, 44) ->
+ 42;
+yeccgoto(atomic_literal, 46) ->
+ 42;
+yeccgoto(atomic_literal, 48) ->
+ 42;
+yeccgoto(atomic_literal, 52) ->
+ 42;
+yeccgoto(atomic_literal, 65) ->
+ 105;
+yeccgoto(atomic_literal, 70) ->
+ 42;
+yeccgoto(atomic_literal, 74) ->
+ 42;
+yeccgoto(atomic_literal, 79) ->
+ 42;
+yeccgoto(atomic_literal, 86) ->
+ 42;
+yeccgoto(atomic_literal, 90) ->
+ 42;
+yeccgoto(atomic_literal, 96) ->
+ 105;
+yeccgoto(atomic_literal, 97) ->
+ 105;
+yeccgoto(atomic_literal, 98) ->
+ 105;
+yeccgoto(atomic_literal, 99) ->
+ 42;
+yeccgoto(atomic_literal, 100) ->
+ 105;
+yeccgoto(atomic_literal, 114) ->
+ 105;
+yeccgoto(atomic_literal, 115) ->
+ 105;
+yeccgoto(atomic_literal, 120) ->
+ 105;
+yeccgoto(atomic_literal, 162) ->
+ 105;
+yeccgoto(atomic_literal, 164) ->
+ 42;
+yeccgoto(atomic_literal, 166) ->
+ 42;
+yeccgoto(atomic_literal, 171) ->
+ 42;
+yeccgoto(atomic_literal, 174) ->
+ 105;
+yeccgoto(atomic_literal, 177) ->
+ 105;
+yeccgoto(atomic_literal, 200) ->
+ 105;
+yeccgoto(atomic_literal, 203) ->
+ 42;
+yeccgoto(atomic_literal, 211) ->
+ 42;
+yeccgoto(atomic_literal, 214) ->
+ 42;
+yeccgoto(atomic_literal, 216) ->
+ 42;
+yeccgoto(atomic_literal, 218) ->
+ 42;
+yeccgoto(atomic_literal, 222) ->
+ 105;
+yeccgoto(atomic_literal, 226) ->
+ 42;
+yeccgoto(atomic_literal, 232) ->
+ 42;
+yeccgoto(atomic_literal, 235) ->
+ 42;
+yeccgoto(atomic_literal, 257) ->
+ 42;
+yeccgoto(atomic_literal, 260) ->
+ 42;
+yeccgoto(atomic_literal, 265) ->
+ 42;
+yeccgoto(atomic_literal, 284) ->
+ 286;
+yeccgoto(atomic_literal, 285) ->
+ 286;
+yeccgoto(atomic_literal, 290) ->
+ 286;
+yeccgoto(atomic_literal, 295) ->
+ 286;
+yeccgoto(atomic_literal, 298) ->
+ 286;
+yeccgoto(atomic_literal, 301) ->
+ 286;
+yeccgoto(atomic_pattern, 65) ->
+ 106;
+yeccgoto(atomic_pattern, 96) ->
+ 106;
+yeccgoto(atomic_pattern, 97) ->
+ 106;
+yeccgoto(atomic_pattern, 98) ->
+ 106;
+yeccgoto(atomic_pattern, 100) ->
+ 106;
+yeccgoto(atomic_pattern, 114) ->
+ 106;
+yeccgoto(atomic_pattern, 115) ->
+ 106;
+yeccgoto(atomic_pattern, 120) ->
+ 106;
+yeccgoto(atomic_pattern, 162) ->
+ 106;
+yeccgoto(atomic_pattern, 174) ->
+ 106;
+yeccgoto(atomic_pattern, 177) ->
+ 106;
+yeccgoto(atomic_pattern, 200) ->
+ 106;
+yeccgoto(atomic_pattern, 222) ->
+ 106;
+yeccgoto(attribute, 276) ->
+ 279;
+yeccgoto(attribute, 282) ->
+ 279;
+yeccgoto(attribute_list, 276) ->
+ 280;
+yeccgoto(attribute_list, 282) ->
+ 283;
+yeccgoto(binary, 33) ->
+ 43;
+yeccgoto(binary, 35) ->
+ 43;
+yeccgoto(binary, 36) ->
+ 43;
+yeccgoto(binary, 37) ->
+ 43;
+yeccgoto(binary, 40) ->
+ 43;
+yeccgoto(binary, 44) ->
+ 43;
+yeccgoto(binary, 46) ->
+ 43;
+yeccgoto(binary, 48) ->
+ 43;
+yeccgoto(binary, 52) ->
+ 43;
+yeccgoto(binary, 70) ->
+ 43;
+yeccgoto(binary, 74) ->
+ 43;
+yeccgoto(binary, 79) ->
+ 43;
+yeccgoto(binary, 86) ->
+ 43;
+yeccgoto(binary, 90) ->
+ 43;
+yeccgoto(binary, 99) ->
+ 43;
+yeccgoto(binary, 164) ->
+ 43;
+yeccgoto(binary, 166) ->
+ 43;
+yeccgoto(binary, 171) ->
+ 43;
+yeccgoto(binary, 203) ->
+ 43;
+yeccgoto(binary, 211) ->
+ 43;
+yeccgoto(binary, 214) ->
+ 43;
+yeccgoto(binary, 216) ->
+ 43;
+yeccgoto(binary, 218) ->
+ 43;
+yeccgoto(binary, 226) ->
+ 43;
+yeccgoto(binary, 232) ->
+ 43;
+yeccgoto(binary, 235) ->
+ 43;
+yeccgoto(binary, 257) ->
+ 43;
+yeccgoto(binary, 260) ->
+ 43;
+yeccgoto(binary, 265) ->
+ 43;
+yeccgoto(binary_pattern, 65) ->
+ 107;
+yeccgoto(binary_pattern, 96) ->
+ 107;
+yeccgoto(binary_pattern, 97) ->
+ 107;
+yeccgoto(binary_pattern, 98) ->
+ 107;
+yeccgoto(binary_pattern, 100) ->
+ 107;
+yeccgoto(binary_pattern, 114) ->
+ 107;
+yeccgoto(binary_pattern, 115) ->
+ 107;
+yeccgoto(binary_pattern, 120) ->
+ 107;
+yeccgoto(binary_pattern, 162) ->
+ 107;
+yeccgoto(binary_pattern, 174) ->
+ 107;
+yeccgoto(binary_pattern, 177) ->
+ 107;
+yeccgoto(binary_pattern, 200) ->
+ 107;
+yeccgoto(binary_pattern, 222) ->
+ 107;
+yeccgoto(call_expr, 33) ->
+ 45;
+yeccgoto(call_expr, 35) ->
+ 45;
+yeccgoto(call_expr, 36) ->
+ 45;
+yeccgoto(call_expr, 37) ->
+ 45;
+yeccgoto(call_expr, 40) ->
+ 45;
+yeccgoto(call_expr, 44) ->
+ 45;
+yeccgoto(call_expr, 46) ->
+ 45;
+yeccgoto(call_expr, 48) ->
+ 45;
+yeccgoto(call_expr, 52) ->
+ 45;
+yeccgoto(call_expr, 70) ->
+ 45;
+yeccgoto(call_expr, 74) ->
+ 45;
+yeccgoto(call_expr, 79) ->
+ 45;
+yeccgoto(call_expr, 86) ->
+ 45;
+yeccgoto(call_expr, 90) ->
+ 45;
+yeccgoto(call_expr, 99) ->
+ 45;
+yeccgoto(call_expr, 164) ->
+ 45;
+yeccgoto(call_expr, 166) ->
+ 45;
+yeccgoto(call_expr, 171) ->
+ 45;
+yeccgoto(call_expr, 203) ->
+ 45;
+yeccgoto(call_expr, 211) ->
+ 45;
+yeccgoto(call_expr, 214) ->
+ 45;
+yeccgoto(call_expr, 216) ->
+ 45;
+yeccgoto(call_expr, 218) ->
+ 45;
+yeccgoto(call_expr, 226) ->
+ 45;
+yeccgoto(call_expr, 232) ->
+ 45;
+yeccgoto(call_expr, 235) ->
+ 45;
+yeccgoto(call_expr, 257) ->
+ 45;
+yeccgoto(call_expr, 260) ->
+ 45;
+yeccgoto(call_expr, 265) ->
+ 45;
+yeccgoto(case_expr, 33) ->
+ 47;
+yeccgoto(case_expr, 35) ->
+ 47;
+yeccgoto(case_expr, 36) ->
+ 47;
+yeccgoto(case_expr, 37) ->
+ 47;
+yeccgoto(case_expr, 40) ->
+ 47;
+yeccgoto(case_expr, 44) ->
+ 47;
+yeccgoto(case_expr, 46) ->
+ 47;
+yeccgoto(case_expr, 48) ->
+ 47;
+yeccgoto(case_expr, 52) ->
+ 47;
+yeccgoto(case_expr, 70) ->
+ 47;
+yeccgoto(case_expr, 74) ->
+ 47;
+yeccgoto(case_expr, 79) ->
+ 47;
+yeccgoto(case_expr, 86) ->
+ 47;
+yeccgoto(case_expr, 90) ->
+ 47;
+yeccgoto(case_expr, 99) ->
+ 47;
+yeccgoto(case_expr, 164) ->
+ 47;
+yeccgoto(case_expr, 166) ->
+ 47;
+yeccgoto(case_expr, 171) ->
+ 47;
+yeccgoto(case_expr, 203) ->
+ 47;
+yeccgoto(case_expr, 211) ->
+ 47;
+yeccgoto(case_expr, 214) ->
+ 47;
+yeccgoto(case_expr, 216) ->
+ 47;
+yeccgoto(case_expr, 218) ->
+ 47;
+yeccgoto(case_expr, 226) ->
+ 47;
+yeccgoto(case_expr, 232) ->
+ 47;
+yeccgoto(case_expr, 235) ->
+ 47;
+yeccgoto(case_expr, 257) ->
+ 47;
+yeccgoto(case_expr, 260) ->
+ 47;
+yeccgoto(case_expr, 265) ->
+ 47;
+yeccgoto(catch_expr, 33) ->
+ 49;
+yeccgoto(catch_expr, 35) ->
+ 49;
+yeccgoto(catch_expr, 36) ->
+ 49;
+yeccgoto(catch_expr, 37) ->
+ 49;
+yeccgoto(catch_expr, 40) ->
+ 49;
+yeccgoto(catch_expr, 44) ->
+ 49;
+yeccgoto(catch_expr, 46) ->
+ 49;
+yeccgoto(catch_expr, 48) ->
+ 49;
+yeccgoto(catch_expr, 52) ->
+ 49;
+yeccgoto(catch_expr, 70) ->
+ 49;
+yeccgoto(catch_expr, 74) ->
+ 49;
+yeccgoto(catch_expr, 79) ->
+ 49;
+yeccgoto(catch_expr, 86) ->
+ 49;
+yeccgoto(catch_expr, 90) ->
+ 49;
+yeccgoto(catch_expr, 99) ->
+ 49;
+yeccgoto(catch_expr, 164) ->
+ 49;
+yeccgoto(catch_expr, 166) ->
+ 49;
+yeccgoto(catch_expr, 171) ->
+ 49;
+yeccgoto(catch_expr, 203) ->
+ 49;
+yeccgoto(catch_expr, 211) ->
+ 49;
+yeccgoto(catch_expr, 214) ->
+ 49;
+yeccgoto(catch_expr, 216) ->
+ 49;
+yeccgoto(catch_expr, 218) ->
+ 49;
+yeccgoto(catch_expr, 226) ->
+ 49;
+yeccgoto(catch_expr, 232) ->
+ 49;
+yeccgoto(catch_expr, 235) ->
+ 49;
+yeccgoto(catch_expr, 257) ->
+ 49;
+yeccgoto(catch_expr, 260) ->
+ 49;
+yeccgoto(catch_expr, 265) ->
+ 49;
+yeccgoto(clause, 65) ->
+ 108;
+yeccgoto(clause, 96) ->
+ 185;
+yeccgoto(clause, 100) ->
+ 108;
+yeccgoto(clause, 222) ->
+ 108;
+yeccgoto(clause_pattern, 65) ->
+ 109;
+yeccgoto(clause_pattern, 96) ->
+ 109;
+yeccgoto(clause_pattern, 100) ->
+ 109;
+yeccgoto(clause_pattern, 222) ->
+ 109;
+yeccgoto(cons, 33) ->
+ 51;
+yeccgoto(cons, 35) ->
+ 51;
+yeccgoto(cons, 36) ->
+ 51;
+yeccgoto(cons, 37) ->
+ 51;
+yeccgoto(cons, 40) ->
+ 51;
+yeccgoto(cons, 44) ->
+ 51;
+yeccgoto(cons, 46) ->
+ 51;
+yeccgoto(cons, 48) ->
+ 51;
+yeccgoto(cons, 52) ->
+ 51;
+yeccgoto(cons, 70) ->
+ 51;
+yeccgoto(cons, 74) ->
+ 51;
+yeccgoto(cons, 79) ->
+ 51;
+yeccgoto(cons, 86) ->
+ 51;
+yeccgoto(cons, 90) ->
+ 51;
+yeccgoto(cons, 99) ->
+ 51;
+yeccgoto(cons, 164) ->
+ 51;
+yeccgoto(cons, 166) ->
+ 51;
+yeccgoto(cons, 171) ->
+ 51;
+yeccgoto(cons, 203) ->
+ 51;
+yeccgoto(cons, 211) ->
+ 51;
+yeccgoto(cons, 214) ->
+ 51;
+yeccgoto(cons, 216) ->
+ 51;
+yeccgoto(cons, 218) ->
+ 51;
+yeccgoto(cons, 226) ->
+ 51;
+yeccgoto(cons, 232) ->
+ 51;
+yeccgoto(cons, 235) ->
+ 51;
+yeccgoto(cons, 257) ->
+ 51;
+yeccgoto(cons, 260) ->
+ 51;
+yeccgoto(cons, 265) ->
+ 51;
+yeccgoto(cons_constant, 126) ->
+ 134;
+yeccgoto(cons_constant, 129) ->
+ 134;
+yeccgoto(cons_constant, 142) ->
+ 134;
+yeccgoto(cons_constant, 147) ->
+ 134;
+yeccgoto(cons_constant, 151) ->
+ 134;
+yeccgoto(cons_constant, 154) ->
+ 134;
+yeccgoto(cons_literal, 284) ->
+ 287;
+yeccgoto(cons_literal, 285) ->
+ 287;
+yeccgoto(cons_literal, 290) ->
+ 287;
+yeccgoto(cons_literal, 295) ->
+ 287;
+yeccgoto(cons_literal, 298) ->
+ 287;
+yeccgoto(cons_literal, 301) ->
+ 287;
+yeccgoto(cons_pattern, 65) ->
+ 110;
+yeccgoto(cons_pattern, 96) ->
+ 110;
+yeccgoto(cons_pattern, 97) ->
+ 110;
+yeccgoto(cons_pattern, 98) ->
+ 110;
+yeccgoto(cons_pattern, 100) ->
+ 110;
+yeccgoto(cons_pattern, 114) ->
+ 110;
+yeccgoto(cons_pattern, 115) ->
+ 110;
+yeccgoto(cons_pattern, 120) ->
+ 110;
+yeccgoto(cons_pattern, 162) ->
+ 110;
+yeccgoto(cons_pattern, 174) ->
+ 110;
+yeccgoto(cons_pattern, 177) ->
+ 110;
+yeccgoto(cons_pattern, 200) ->
+ 110;
+yeccgoto(cons_pattern, 222) ->
+ 110;
+yeccgoto(constant, 126) ->
+ 135;
+yeccgoto(constant, 129) ->
+ 150;
+yeccgoto(constant, 142) ->
+ 135;
+yeccgoto(constant, 147) ->
+ 135;
+yeccgoto(constant, 151) ->
+ 157;
+yeccgoto(constant, 154) ->
+ 155;
+yeccgoto(constants, 126) ->
+ 136;
+yeccgoto(constants, 142) ->
+ 143;
+yeccgoto(constants, 147) ->
+ 148;
+yeccgoto(exported_name, 5) ->
+ 307;
+yeccgoto(exported_name, 311) ->
+ 307;
+yeccgoto(exported_names, 5) ->
+ 308;
+yeccgoto(exported_names, 311) ->
+ 312;
+yeccgoto(expression, 33) ->
+ 53;
+yeccgoto(expression, 35) ->
+ 243;
+yeccgoto(expression, 36) ->
+ 53;
+yeccgoto(expression, 37) ->
+ 53;
+yeccgoto(expression, 40) ->
+ 53;
+yeccgoto(expression, 44) ->
+ 53;
+yeccgoto(expression, 46) ->
+ 53;
+yeccgoto(expression, 48) ->
+ 53;
+yeccgoto(expression, 52) ->
+ 53;
+yeccgoto(expression, 70) ->
+ 53;
+yeccgoto(expression, 74) ->
+ 53;
+yeccgoto(expression, 79) ->
+ 53;
+yeccgoto(expression, 86) ->
+ 53;
+yeccgoto(expression, 90) ->
+ 53;
+yeccgoto(expression, 99) ->
+ 53;
+yeccgoto(expression, 164) ->
+ 53;
+yeccgoto(expression, 166) ->
+ 53;
+yeccgoto(expression, 171) ->
+ 53;
+yeccgoto(expression, 203) ->
+ 53;
+yeccgoto(expression, 211) ->
+ 53;
+yeccgoto(expression, 214) ->
+ 53;
+yeccgoto(expression, 216) ->
+ 53;
+yeccgoto(expression, 218) ->
+ 53;
+yeccgoto(expression, 226) ->
+ 53;
+yeccgoto(expression, 232) ->
+ 53;
+yeccgoto(expression, 235) ->
+ 53;
+yeccgoto(expression, 257) ->
+ 53;
+yeccgoto(expression, 260) ->
+ 53;
+yeccgoto(expression, 265) ->
+ 53;
+yeccgoto(fun_expr, 20) ->
+ 24;
+yeccgoto(fun_expr, 21) ->
+ 268;
+yeccgoto(fun_expr, 33) ->
+ 55;
+yeccgoto(fun_expr, 35) ->
+ 55;
+yeccgoto(fun_expr, 36) ->
+ 55;
+yeccgoto(fun_expr, 37) ->
+ 55;
+yeccgoto(fun_expr, 40) ->
+ 55;
+yeccgoto(fun_expr, 44) ->
+ 55;
+yeccgoto(fun_expr, 46) ->
+ 55;
+yeccgoto(fun_expr, 48) ->
+ 55;
+yeccgoto(fun_expr, 52) ->
+ 55;
+yeccgoto(fun_expr, 70) ->
+ 55;
+yeccgoto(fun_expr, 74) ->
+ 55;
+yeccgoto(fun_expr, 79) ->
+ 55;
+yeccgoto(fun_expr, 86) ->
+ 55;
+yeccgoto(fun_expr, 90) ->
+ 55;
+yeccgoto(fun_expr, 99) ->
+ 55;
+yeccgoto(fun_expr, 164) ->
+ 55;
+yeccgoto(fun_expr, 166) ->
+ 55;
+yeccgoto(fun_expr, 171) ->
+ 55;
+yeccgoto(fun_expr, 203) ->
+ 55;
+yeccgoto(fun_expr, 211) ->
+ 55;
+yeccgoto(fun_expr, 214) ->
+ 55;
+yeccgoto(fun_expr, 216) ->
+ 55;
+yeccgoto(fun_expr, 218) ->
+ 55;
+yeccgoto(fun_expr, 226) ->
+ 55;
+yeccgoto(fun_expr, 232) ->
+ 55;
+yeccgoto(fun_expr, 235) ->
+ 55;
+yeccgoto(fun_expr, 257) ->
+ 55;
+yeccgoto(fun_expr, 260) ->
+ 55;
+yeccgoto(fun_expr, 265) ->
+ 55;
+yeccgoto(function_definition, 8) ->
+ 12;
+yeccgoto(function_definition, 12) ->
+ 12;
+yeccgoto(function_definition, 60) ->
+ 12;
+yeccgoto(function_definition, 316) ->
+ 12;
+yeccgoto(function_definitions, 8) ->
+ 13;
+yeccgoto(function_definitions, 12) ->
+ 17;
+yeccgoto(function_definitions, 60) ->
+ 210;
+yeccgoto(function_definitions, 316) ->
+ 13;
+yeccgoto(function_name, 5) ->
+ 309;
+yeccgoto(function_name, 8) ->
+ 14;
+yeccgoto(function_name, 9) ->
+ 272;
+yeccgoto(function_name, 12) ->
+ 14;
+yeccgoto(function_name, 33) ->
+ 56;
+yeccgoto(function_name, 35) ->
+ 56;
+yeccgoto(function_name, 36) ->
+ 56;
+yeccgoto(function_name, 37) ->
+ 56;
+yeccgoto(function_name, 40) ->
+ 56;
+yeccgoto(function_name, 44) ->
+ 56;
+yeccgoto(function_name, 46) ->
+ 56;
+yeccgoto(function_name, 48) ->
+ 56;
+yeccgoto(function_name, 52) ->
+ 56;
+yeccgoto(function_name, 60) ->
+ 14;
+yeccgoto(function_name, 70) ->
+ 56;
+yeccgoto(function_name, 74) ->
+ 56;
+yeccgoto(function_name, 79) ->
+ 56;
+yeccgoto(function_name, 86) ->
+ 56;
+yeccgoto(function_name, 90) ->
+ 56;
+yeccgoto(function_name, 99) ->
+ 56;
+yeccgoto(function_name, 164) ->
+ 56;
+yeccgoto(function_name, 166) ->
+ 56;
+yeccgoto(function_name, 171) ->
+ 56;
+yeccgoto(function_name, 203) ->
+ 56;
+yeccgoto(function_name, 211) ->
+ 56;
+yeccgoto(function_name, 214) ->
+ 56;
+yeccgoto(function_name, 216) ->
+ 56;
+yeccgoto(function_name, 218) ->
+ 56;
+yeccgoto(function_name, 226) ->
+ 56;
+yeccgoto(function_name, 232) ->
+ 56;
+yeccgoto(function_name, 235) ->
+ 56;
+yeccgoto(function_name, 257) ->
+ 56;
+yeccgoto(function_name, 260) ->
+ 56;
+yeccgoto(function_name, 265) ->
+ 56;
+yeccgoto(function_name, 311) ->
+ 309;
+yeccgoto(function_name, 316) ->
+ 14;
+yeccgoto(let_expr, 33) ->
+ 59;
+yeccgoto(let_expr, 35) ->
+ 59;
+yeccgoto(let_expr, 36) ->
+ 59;
+yeccgoto(let_expr, 37) ->
+ 59;
+yeccgoto(let_expr, 40) ->
+ 59;
+yeccgoto(let_expr, 44) ->
+ 59;
+yeccgoto(let_expr, 46) ->
+ 59;
+yeccgoto(let_expr, 48) ->
+ 59;
+yeccgoto(let_expr, 52) ->
+ 59;
+yeccgoto(let_expr, 70) ->
+ 59;
+yeccgoto(let_expr, 74) ->
+ 59;
+yeccgoto(let_expr, 79) ->
+ 59;
+yeccgoto(let_expr, 86) ->
+ 59;
+yeccgoto(let_expr, 90) ->
+ 59;
+yeccgoto(let_expr, 99) ->
+ 59;
+yeccgoto(let_expr, 164) ->
+ 59;
+yeccgoto(let_expr, 166) ->
+ 59;
+yeccgoto(let_expr, 171) ->
+ 59;
+yeccgoto(let_expr, 203) ->
+ 59;
+yeccgoto(let_expr, 211) ->
+ 59;
+yeccgoto(let_expr, 214) ->
+ 59;
+yeccgoto(let_expr, 216) ->
+ 59;
+yeccgoto(let_expr, 218) ->
+ 59;
+yeccgoto(let_expr, 226) ->
+ 59;
+yeccgoto(let_expr, 232) ->
+ 59;
+yeccgoto(let_expr, 235) ->
+ 59;
+yeccgoto(let_expr, 257) ->
+ 59;
+yeccgoto(let_expr, 260) ->
+ 59;
+yeccgoto(let_expr, 265) ->
+ 59;
+yeccgoto(let_vars, 58) ->
+ 213;
+yeccgoto(let_vars, 82) ->
+ 85;
+yeccgoto(let_vars, 88) ->
+ 89;
+yeccgoto(letrec_expr, 33) ->
+ 61;
+yeccgoto(letrec_expr, 35) ->
+ 61;
+yeccgoto(letrec_expr, 36) ->
+ 61;
+yeccgoto(letrec_expr, 37) ->
+ 61;
+yeccgoto(letrec_expr, 40) ->
+ 61;
+yeccgoto(letrec_expr, 44) ->
+ 61;
+yeccgoto(letrec_expr, 46) ->
+ 61;
+yeccgoto(letrec_expr, 48) ->
+ 61;
+yeccgoto(letrec_expr, 52) ->
+ 61;
+yeccgoto(letrec_expr, 70) ->
+ 61;
+yeccgoto(letrec_expr, 74) ->
+ 61;
+yeccgoto(letrec_expr, 79) ->
+ 61;
+yeccgoto(letrec_expr, 86) ->
+ 61;
+yeccgoto(letrec_expr, 90) ->
+ 61;
+yeccgoto(letrec_expr, 99) ->
+ 61;
+yeccgoto(letrec_expr, 164) ->
+ 61;
+yeccgoto(letrec_expr, 166) ->
+ 61;
+yeccgoto(letrec_expr, 171) ->
+ 61;
+yeccgoto(letrec_expr, 203) ->
+ 61;
+yeccgoto(letrec_expr, 211) ->
+ 61;
+yeccgoto(letrec_expr, 214) ->
+ 61;
+yeccgoto(letrec_expr, 216) ->
+ 61;
+yeccgoto(letrec_expr, 218) ->
+ 61;
+yeccgoto(letrec_expr, 226) ->
+ 61;
+yeccgoto(letrec_expr, 232) ->
+ 61;
+yeccgoto(letrec_expr, 235) ->
+ 61;
+yeccgoto(letrec_expr, 257) ->
+ 61;
+yeccgoto(letrec_expr, 260) ->
+ 61;
+yeccgoto(letrec_expr, 265) ->
+ 61;
+yeccgoto(literal, 284) ->
+ 288;
+yeccgoto(literal, 285) ->
+ 297;
+yeccgoto(literal, 290) ->
+ 291;
+yeccgoto(literal, 295) ->
+ 291;
+yeccgoto(literal, 298) ->
+ 304;
+yeccgoto(literal, 301) ->
+ 302;
+yeccgoto(literals, 290) ->
+ 292;
+yeccgoto(literals, 295) ->
+ 296;
+yeccgoto(module_attribute, 6) ->
+ 8;
+yeccgoto(module_attribute, 315) ->
+ 316;
+yeccgoto(module_definition, 0) ->
+ 3;
+yeccgoto(module_defs, 8) ->
+ 15;
+yeccgoto(module_defs, 316) ->
+ 317;
+yeccgoto(module_export, 4) ->
+ 6;
+yeccgoto(module_export, 314) ->
+ 315;
+yeccgoto(nil, 33) ->
+ 62;
+yeccgoto(nil, 35) ->
+ 62;
+yeccgoto(nil, 36) ->
+ 62;
+yeccgoto(nil, 37) ->
+ 62;
+yeccgoto(nil, 40) ->
+ 62;
+yeccgoto(nil, 44) ->
+ 62;
+yeccgoto(nil, 46) ->
+ 62;
+yeccgoto(nil, 48) ->
+ 62;
+yeccgoto(nil, 52) ->
+ 62;
+yeccgoto(nil, 65) ->
+ 62;
+yeccgoto(nil, 70) ->
+ 62;
+yeccgoto(nil, 74) ->
+ 62;
+yeccgoto(nil, 79) ->
+ 62;
+yeccgoto(nil, 86) ->
+ 62;
+yeccgoto(nil, 90) ->
+ 62;
+yeccgoto(nil, 96) ->
+ 62;
+yeccgoto(nil, 97) ->
+ 62;
+yeccgoto(nil, 98) ->
+ 62;
+yeccgoto(nil, 99) ->
+ 62;
+yeccgoto(nil, 100) ->
+ 62;
+yeccgoto(nil, 114) ->
+ 62;
+yeccgoto(nil, 115) ->
+ 62;
+yeccgoto(nil, 120) ->
+ 62;
+yeccgoto(nil, 126) ->
+ 139;
+yeccgoto(nil, 129) ->
+ 139;
+yeccgoto(nil, 142) ->
+ 139;
+yeccgoto(nil, 147) ->
+ 139;
+yeccgoto(nil, 151) ->
+ 139;
+yeccgoto(nil, 154) ->
+ 139;
+yeccgoto(nil, 162) ->
+ 62;
+yeccgoto(nil, 164) ->
+ 62;
+yeccgoto(nil, 166) ->
+ 62;
+yeccgoto(nil, 171) ->
+ 62;
+yeccgoto(nil, 174) ->
+ 62;
+yeccgoto(nil, 177) ->
+ 62;
+yeccgoto(nil, 200) ->
+ 62;
+yeccgoto(nil, 203) ->
+ 62;
+yeccgoto(nil, 211) ->
+ 62;
+yeccgoto(nil, 214) ->
+ 62;
+yeccgoto(nil, 216) ->
+ 62;
+yeccgoto(nil, 218) ->
+ 62;
+yeccgoto(nil, 222) ->
+ 62;
+yeccgoto(nil, 226) ->
+ 62;
+yeccgoto(nil, 232) ->
+ 62;
+yeccgoto(nil, 235) ->
+ 62;
+yeccgoto(nil, 257) ->
+ 62;
+yeccgoto(nil, 260) ->
+ 62;
+yeccgoto(nil, 265) ->
+ 62;
+yeccgoto(nil, 284) ->
+ 62;
+yeccgoto(nil, 285) ->
+ 62;
+yeccgoto(nil, 290) ->
+ 62;
+yeccgoto(nil, 295) ->
+ 62;
+yeccgoto(nil, 298) ->
+ 62;
+yeccgoto(nil, 301) ->
+ 62;
+yeccgoto(other_pattern, 65) ->
+ 111;
+yeccgoto(other_pattern, 96) ->
+ 186;
+yeccgoto(other_pattern, 97) ->
+ 111;
+yeccgoto(other_pattern, 98) ->
+ 111;
+yeccgoto(other_pattern, 100) ->
+ 111;
+yeccgoto(other_pattern, 114) ->
+ 111;
+yeccgoto(other_pattern, 115) ->
+ 123;
+yeccgoto(other_pattern, 120) ->
+ 111;
+yeccgoto(other_pattern, 162) ->
+ 111;
+yeccgoto(other_pattern, 174) ->
+ 111;
+yeccgoto(other_pattern, 177) ->
+ 111;
+yeccgoto(other_pattern, 200) ->
+ 111;
+yeccgoto(other_pattern, 222) ->
+ 111;
+yeccgoto(primop_expr, 33) ->
+ 64;
+yeccgoto(primop_expr, 35) ->
+ 64;
+yeccgoto(primop_expr, 36) ->
+ 64;
+yeccgoto(primop_expr, 37) ->
+ 64;
+yeccgoto(primop_expr, 40) ->
+ 64;
+yeccgoto(primop_expr, 44) ->
+ 64;
+yeccgoto(primop_expr, 46) ->
+ 64;
+yeccgoto(primop_expr, 48) ->
+ 64;
+yeccgoto(primop_expr, 52) ->
+ 64;
+yeccgoto(primop_expr, 70) ->
+ 64;
+yeccgoto(primop_expr, 74) ->
+ 64;
+yeccgoto(primop_expr, 79) ->
+ 64;
+yeccgoto(primop_expr, 86) ->
+ 64;
+yeccgoto(primop_expr, 90) ->
+ 64;
+yeccgoto(primop_expr, 99) ->
+ 64;
+yeccgoto(primop_expr, 164) ->
+ 64;
+yeccgoto(primop_expr, 166) ->
+ 64;
+yeccgoto(primop_expr, 171) ->
+ 64;
+yeccgoto(primop_expr, 203) ->
+ 64;
+yeccgoto(primop_expr, 211) ->
+ 64;
+yeccgoto(primop_expr, 214) ->
+ 64;
+yeccgoto(primop_expr, 216) ->
+ 64;
+yeccgoto(primop_expr, 218) ->
+ 64;
+yeccgoto(primop_expr, 226) ->
+ 64;
+yeccgoto(primop_expr, 232) ->
+ 64;
+yeccgoto(primop_expr, 235) ->
+ 64;
+yeccgoto(primop_expr, 257) ->
+ 64;
+yeccgoto(primop_expr, 260) ->
+ 64;
+yeccgoto(primop_expr, 265) ->
+ 64;
+yeccgoto(receive_expr, 33) ->
+ 66;
+yeccgoto(receive_expr, 35) ->
+ 66;
+yeccgoto(receive_expr, 36) ->
+ 66;
+yeccgoto(receive_expr, 37) ->
+ 66;
+yeccgoto(receive_expr, 40) ->
+ 66;
+yeccgoto(receive_expr, 44) ->
+ 66;
+yeccgoto(receive_expr, 46) ->
+ 66;
+yeccgoto(receive_expr, 48) ->
+ 66;
+yeccgoto(receive_expr, 52) ->
+ 66;
+yeccgoto(receive_expr, 70) ->
+ 66;
+yeccgoto(receive_expr, 74) ->
+ 66;
+yeccgoto(receive_expr, 79) ->
+ 66;
+yeccgoto(receive_expr, 86) ->
+ 66;
+yeccgoto(receive_expr, 90) ->
+ 66;
+yeccgoto(receive_expr, 99) ->
+ 66;
+yeccgoto(receive_expr, 164) ->
+ 66;
+yeccgoto(receive_expr, 166) ->
+ 66;
+yeccgoto(receive_expr, 171) ->
+ 66;
+yeccgoto(receive_expr, 203) ->
+ 66;
+yeccgoto(receive_expr, 211) ->
+ 66;
+yeccgoto(receive_expr, 214) ->
+ 66;
+yeccgoto(receive_expr, 216) ->
+ 66;
+yeccgoto(receive_expr, 218) ->
+ 66;
+yeccgoto(receive_expr, 226) ->
+ 66;
+yeccgoto(receive_expr, 232) ->
+ 66;
+yeccgoto(receive_expr, 235) ->
+ 66;
+yeccgoto(receive_expr, 257) ->
+ 66;
+yeccgoto(receive_expr, 260) ->
+ 66;
+yeccgoto(receive_expr, 265) ->
+ 66;
+yeccgoto(segment, 247) ->
+ 249;
+yeccgoto(segment, 255) ->
+ 249;
+yeccgoto(segment_pattern, 190) ->
+ 192;
+yeccgoto(segment_pattern, 198) ->
+ 192;
+yeccgoto(segment_patterns, 190) ->
+ 193;
+yeccgoto(segment_patterns, 198) ->
+ 199;
+yeccgoto(segments, 247) ->
+ 250;
+yeccgoto(segments, 255) ->
+ 256;
+yeccgoto(sequence, 33) ->
+ 67;
+yeccgoto(sequence, 35) ->
+ 67;
+yeccgoto(sequence, 36) ->
+ 67;
+yeccgoto(sequence, 37) ->
+ 67;
+yeccgoto(sequence, 40) ->
+ 67;
+yeccgoto(sequence, 44) ->
+ 67;
+yeccgoto(sequence, 46) ->
+ 67;
+yeccgoto(sequence, 48) ->
+ 67;
+yeccgoto(sequence, 52) ->
+ 67;
+yeccgoto(sequence, 70) ->
+ 67;
+yeccgoto(sequence, 74) ->
+ 67;
+yeccgoto(sequence, 79) ->
+ 67;
+yeccgoto(sequence, 86) ->
+ 67;
+yeccgoto(sequence, 90) ->
+ 67;
+yeccgoto(sequence, 99) ->
+ 67;
+yeccgoto(sequence, 164) ->
+ 67;
+yeccgoto(sequence, 166) ->
+ 67;
+yeccgoto(sequence, 171) ->
+ 67;
+yeccgoto(sequence, 203) ->
+ 67;
+yeccgoto(sequence, 211) ->
+ 67;
+yeccgoto(sequence, 214) ->
+ 67;
+yeccgoto(sequence, 216) ->
+ 67;
+yeccgoto(sequence, 218) ->
+ 67;
+yeccgoto(sequence, 226) ->
+ 67;
+yeccgoto(sequence, 232) ->
+ 67;
+yeccgoto(sequence, 235) ->
+ 67;
+yeccgoto(sequence, 257) ->
+ 67;
+yeccgoto(sequence, 260) ->
+ 67;
+yeccgoto(sequence, 265) ->
+ 67;
+yeccgoto(single_expression, 33) ->
+ 68;
+yeccgoto(single_expression, 35) ->
+ 68;
+yeccgoto(single_expression, 36) ->
+ 68;
+yeccgoto(single_expression, 37) ->
+ 68;
+yeccgoto(single_expression, 40) ->
+ 68;
+yeccgoto(single_expression, 44) ->
+ 68;
+yeccgoto(single_expression, 46) ->
+ 68;
+yeccgoto(single_expression, 48) ->
+ 68;
+yeccgoto(single_expression, 52) ->
+ 68;
+yeccgoto(single_expression, 70) ->
+ 68;
+yeccgoto(single_expression, 74) ->
+ 68;
+yeccgoto(single_expression, 79) ->
+ 68;
+yeccgoto(single_expression, 86) ->
+ 68;
+yeccgoto(single_expression, 90) ->
+ 68;
+yeccgoto(single_expression, 99) ->
+ 68;
+yeccgoto(single_expression, 164) ->
+ 68;
+yeccgoto(single_expression, 166) ->
+ 68;
+yeccgoto(single_expression, 171) ->
+ 68;
+yeccgoto(single_expression, 203) ->
+ 68;
+yeccgoto(single_expression, 211) ->
+ 68;
+yeccgoto(single_expression, 214) ->
+ 68;
+yeccgoto(single_expression, 216) ->
+ 68;
+yeccgoto(single_expression, 218) ->
+ 68;
+yeccgoto(single_expression, 226) ->
+ 68;
+yeccgoto(single_expression, 232) ->
+ 68;
+yeccgoto(single_expression, 235) ->
+ 68;
+yeccgoto(single_expression, 257) ->
+ 68;
+yeccgoto(single_expression, 260) ->
+ 68;
+yeccgoto(single_expression, 265) ->
+ 68;
+yeccgoto(tail, 231) ->
+ 234;
+yeccgoto(tail, 238) ->
+ 239;
+yeccgoto(tail_constant, 150) ->
+ 153;
+yeccgoto(tail_constant, 157) ->
+ 158;
+yeccgoto(tail_literal, 297) ->
+ 300;
+yeccgoto(tail_literal, 304) ->
+ 305;
+yeccgoto(tail_pattern, 173) ->
+ 176;
+yeccgoto(tail_pattern, 180) ->
+ 181;
+yeccgoto(timeout, 65) ->
+ 112;
+yeccgoto(timeout, 101) ->
+ 168;
+yeccgoto(try_expr, 33) ->
+ 71;
+yeccgoto(try_expr, 35) ->
+ 71;
+yeccgoto(try_expr, 36) ->
+ 71;
+yeccgoto(try_expr, 37) ->
+ 71;
+yeccgoto(try_expr, 40) ->
+ 71;
+yeccgoto(try_expr, 44) ->
+ 71;
+yeccgoto(try_expr, 46) ->
+ 71;
+yeccgoto(try_expr, 48) ->
+ 71;
+yeccgoto(try_expr, 52) ->
+ 71;
+yeccgoto(try_expr, 70) ->
+ 71;
+yeccgoto(try_expr, 74) ->
+ 71;
+yeccgoto(try_expr, 79) ->
+ 71;
+yeccgoto(try_expr, 86) ->
+ 71;
+yeccgoto(try_expr, 90) ->
+ 71;
+yeccgoto(try_expr, 99) ->
+ 71;
+yeccgoto(try_expr, 164) ->
+ 71;
+yeccgoto(try_expr, 166) ->
+ 71;
+yeccgoto(try_expr, 171) ->
+ 71;
+yeccgoto(try_expr, 203) ->
+ 71;
+yeccgoto(try_expr, 211) ->
+ 71;
+yeccgoto(try_expr, 214) ->
+ 71;
+yeccgoto(try_expr, 216) ->
+ 71;
+yeccgoto(try_expr, 218) ->
+ 71;
+yeccgoto(try_expr, 226) ->
+ 71;
+yeccgoto(try_expr, 232) ->
+ 71;
+yeccgoto(try_expr, 235) ->
+ 71;
+yeccgoto(try_expr, 257) ->
+ 71;
+yeccgoto(try_expr, 260) ->
+ 71;
+yeccgoto(try_expr, 265) ->
+ 71;
+yeccgoto(tuple, 33) ->
+ 72;
+yeccgoto(tuple, 35) ->
+ 72;
+yeccgoto(tuple, 36) ->
+ 72;
+yeccgoto(tuple, 37) ->
+ 72;
+yeccgoto(tuple, 40) ->
+ 72;
+yeccgoto(tuple, 44) ->
+ 72;
+yeccgoto(tuple, 46) ->
+ 72;
+yeccgoto(tuple, 48) ->
+ 72;
+yeccgoto(tuple, 52) ->
+ 72;
+yeccgoto(tuple, 70) ->
+ 72;
+yeccgoto(tuple, 74) ->
+ 72;
+yeccgoto(tuple, 79) ->
+ 72;
+yeccgoto(tuple, 86) ->
+ 72;
+yeccgoto(tuple, 90) ->
+ 72;
+yeccgoto(tuple, 99) ->
+ 72;
+yeccgoto(tuple, 164) ->
+ 72;
+yeccgoto(tuple, 166) ->
+ 72;
+yeccgoto(tuple, 171) ->
+ 72;
+yeccgoto(tuple, 203) ->
+ 72;
+yeccgoto(tuple, 211) ->
+ 72;
+yeccgoto(tuple, 214) ->
+ 72;
+yeccgoto(tuple, 216) ->
+ 72;
+yeccgoto(tuple, 218) ->
+ 72;
+yeccgoto(tuple, 226) ->
+ 72;
+yeccgoto(tuple, 232) ->
+ 72;
+yeccgoto(tuple, 235) ->
+ 72;
+yeccgoto(tuple, 257) ->
+ 72;
+yeccgoto(tuple, 260) ->
+ 72;
+yeccgoto(tuple, 265) ->
+ 72;
+yeccgoto(tuple_constant, 126) ->
+ 141;
+yeccgoto(tuple_constant, 129) ->
+ 141;
+yeccgoto(tuple_constant, 142) ->
+ 141;
+yeccgoto(tuple_constant, 147) ->
+ 141;
+yeccgoto(tuple_constant, 151) ->
+ 141;
+yeccgoto(tuple_constant, 154) ->
+ 141;
+yeccgoto(tuple_literal, 284) ->
+ 289;
+yeccgoto(tuple_literal, 285) ->
+ 289;
+yeccgoto(tuple_literal, 290) ->
+ 289;
+yeccgoto(tuple_literal, 295) ->
+ 289;
+yeccgoto(tuple_literal, 298) ->
+ 289;
+yeccgoto(tuple_literal, 301) ->
+ 289;
+yeccgoto(tuple_pattern, 65) ->
+ 113;
+yeccgoto(tuple_pattern, 96) ->
+ 113;
+yeccgoto(tuple_pattern, 97) ->
+ 113;
+yeccgoto(tuple_pattern, 98) ->
+ 113;
+yeccgoto(tuple_pattern, 100) ->
+ 113;
+yeccgoto(tuple_pattern, 114) ->
+ 113;
+yeccgoto(tuple_pattern, 115) ->
+ 113;
+yeccgoto(tuple_pattern, 120) ->
+ 113;
+yeccgoto(tuple_pattern, 162) ->
+ 113;
+yeccgoto(tuple_pattern, 174) ->
+ 113;
+yeccgoto(tuple_pattern, 177) ->
+ 113;
+yeccgoto(tuple_pattern, 200) ->
+ 113;
+yeccgoto(tuple_pattern, 222) ->
+ 113;
+yeccgoto(variable, 25) ->
+ 31;
+yeccgoto(variable, 26) ->
+ 267;
+yeccgoto(variable, 33) ->
+ 73;
+yeccgoto(variable, 35) ->
+ 73;
+yeccgoto(variable, 36) ->
+ 73;
+yeccgoto(variable, 37) ->
+ 73;
+yeccgoto(variable, 40) ->
+ 73;
+yeccgoto(variable, 44) ->
+ 73;
+yeccgoto(variable, 46) ->
+ 73;
+yeccgoto(variable, 48) ->
+ 73;
+yeccgoto(variable, 52) ->
+ 73;
+yeccgoto(variable, 58) ->
+ 31;
+yeccgoto(variable, 65) ->
+ 31;
+yeccgoto(variable, 70) ->
+ 73;
+yeccgoto(variable, 74) ->
+ 73;
+yeccgoto(variable, 79) ->
+ 73;
+yeccgoto(variable, 82) ->
+ 31;
+yeccgoto(variable, 83) ->
+ 31;
+yeccgoto(variable, 86) ->
+ 73;
+yeccgoto(variable, 88) ->
+ 31;
+yeccgoto(variable, 90) ->
+ 73;
+yeccgoto(variable, 96) ->
+ 124;
+yeccgoto(variable, 97) ->
+ 31;
+yeccgoto(variable, 98) ->
+ 31;
+yeccgoto(variable, 99) ->
+ 73;
+yeccgoto(variable, 100) ->
+ 31;
+yeccgoto(variable, 114) ->
+ 31;
+yeccgoto(variable, 115) ->
+ 124;
+yeccgoto(variable, 120) ->
+ 31;
+yeccgoto(variable, 162) ->
+ 31;
+yeccgoto(variable, 164) ->
+ 73;
+yeccgoto(variable, 166) ->
+ 73;
+yeccgoto(variable, 171) ->
+ 73;
+yeccgoto(variable, 174) ->
+ 31;
+yeccgoto(variable, 177) ->
+ 31;
+yeccgoto(variable, 200) ->
+ 31;
+yeccgoto(variable, 203) ->
+ 73;
+yeccgoto(variable, 211) ->
+ 73;
+yeccgoto(variable, 214) ->
+ 73;
+yeccgoto(variable, 216) ->
+ 73;
+yeccgoto(variable, 218) ->
+ 73;
+yeccgoto(variable, 222) ->
+ 31;
+yeccgoto(variable, 226) ->
+ 73;
+yeccgoto(variable, 232) ->
+ 73;
+yeccgoto(variable, 235) ->
+ 73;
+yeccgoto(variable, 257) ->
+ 73;
+yeccgoto(variable, 260) ->
+ 73;
+yeccgoto(variable, 263) ->
+ 31;
+yeccgoto(variable, 265) ->
+ 73;
+yeccgoto(__Symbol, __State) ->
+ exit({__Symbol, __State, missing_in_goto_table}).
+
+
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl
new file mode 100644
index 0000000000..aaf913a15a
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_parse.hrl
@@ -0,0 +1,111 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_parse.hrl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Core Erlang syntax trees as records.
+
+%% It would be nice to incorporate some generic functions as well but
+%% this could make including this file difficult.
+
+%% Note: the annotation list is *always* the first record field.
+%% Thus it is possible to define the macros:
+%% -define(get_ann(X), element(2, X)).
+%% -define(set_ann(X, Y), setelement(2, X, Y)).
+
+-record(c_int, {anno=[], val}). % val :: integer()
+
+-record(c_float, {anno=[], val}). % val :: float()
+
+-record(c_atom, {anno=[], val}). % val :: atom()
+
+-record(c_char, {anno=[], val}). % val :: char()
+
+-record(c_string, {anno=[], val}). % val :: string()
+
+-record(c_nil, {anno=[]}).
+
+-record(c_binary, {anno=[], segments}). % segments :: [#ce_bitstr{}]
+
+-record(c_bitstr, {anno=[],val, % val :: Tree,
+ size, % size :: Tree,
+ unit, % unit :: integer(),
+ type, % type :: atom(),
+ flags}). % flags :: [atom()],
+
+-record(c_cons, {anno=[], hd, % hd :: Tree,
+ tl}). % tl :: Tree
+
+-record(c_tuple, {anno=[], es}). % es :: [Tree]
+
+-record(c_var, {anno=[], name}). % name :: integer() | atom()
+
+-record(c_fname, {anno=[], id, % id :: atom(),
+ arity}). % arity :: integer()
+
+-record(c_values, {anno=[], es}). % es :: [Tree]
+
+-record(c_fun, {anno=[], vars, % vars :: [Tree],
+ body}). % body :: Tree
+
+-record(c_seq, {anno=[], arg, % arg :: Tree,
+ body}). % body :: Tree
+
+-record(c_let, {anno=[], vars, % vars :: [Tree],
+ arg, % arg :: Tree,
+ body}). % body :: Tree
+
+-record(c_letrec, {anno=[], defs, % defs :: [#ce_def{}],
+ body}). % body :: Tree
+
+-record(c_def, {anno=[], name, % name :: Tree,
+ val}). % val :: Tree,
+
+-record(c_case, {anno=[], arg, % arg :: Tree,
+ clauses}). % clauses :: [Tree]
+
+-record(c_clause, {anno=[], pats, % pats :: [Tree],
+ guard, % guard :: Tree,
+ body}). % body :: Tree
+
+-record(c_alias, {anno=[], var, % var :: Tree,
+ pat}). % pat :: Tree
+
+-record(c_receive, {anno=[], clauses, % clauses :: [Tree],
+ timeout, % timeout :: Tree,
+ action}). % action :: Tree
+
+-record(c_apply, {anno=[], op, % op :: Tree,
+ args}). % args :: [Tree]
+
+-record(c_call, {anno=[], module, % module :: Tree,
+ name, % name :: Tree,
+ args}). % args :: [Tree]
+
+-record(c_primop, {anno=[], name, % name :: Tree,
+ args}). % args :: [Tree]
+
+-record(c_try, {anno=[], arg, % arg :: Tree,
+ vars, % vars :: [Tree],
+ body, % body :: Tree
+ evars, % evars :: [Tree],
+ handler}). % handler :: Tree
+
+-record(c_catch, {anno=[], body}). % body :: Tree
+
+-record(c_module, {anno=[], name, % name :: Tree,
+ exports, % exports :: [Tree],
+ attrs, % attrs :: [#ce_def{}],
+ defs}). % defs :: [#ce_def{}]
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl
new file mode 100644
index 0000000000..147a0dba6c
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_pp.erl
@@ -0,0 +1,430 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_pp.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Core Erlang (naive) prettyprinter
+
+-module(core_pp).
+
+-export([format/1]).
+
+-include("core_parse.hrl").
+
+%% ====================================================================== %%
+%% format(Node) -> Text
+%% Node = coreErlang()
+%% Text = string() | [Text]
+%%
+%% Prettyprint-formats (naively) an abstract Core Erlang syntax
+%% tree.
+
+-record(ctxt, {class = term,
+ indent = 0,
+ item_indent = 2,
+ body_indent = 4,
+ tab_width = 8,
+ line = 0}).
+
+format(Node) -> case catch format(Node, #ctxt{}) of
+ {'EXIT',_} -> io_lib:format("~p",[Node]);
+ Other -> Other
+ end.
+
+maybe_anno(Node, Fun, Ctxt) ->
+ As = core_lib:get_anno(Node),
+ case get_line(As) of
+ none ->
+ maybe_anno(Node, Fun, Ctxt, As);
+ Line ->
+ if Line > Ctxt#ctxt.line ->
+ [io_lib:format("%% Line ~w",[Line]),
+ nl_indent(Ctxt),
+ maybe_anno(Node, Fun, Ctxt#ctxt{line = Line}, As)
+ ];
+ true ->
+ maybe_anno(Node, Fun, Ctxt, As)
+ end
+ end.
+
+maybe_anno(Node, Fun, Ctxt, As) ->
+ case strip_line(As) of
+ [] ->
+ Fun(Node, Ctxt);
+ List ->
+ Ctxt1 = add_indent(Ctxt, 2),
+ Ctxt2 = add_indent(Ctxt1, 3),
+ ["( ",
+ Fun(Node, Ctxt1),
+ nl_indent(Ctxt1),
+ "-| ",format_1(core_lib:make_literal(List), Ctxt2)," )"
+ ]
+ end.
+
+strip_line([A | As]) when integer(A) ->
+ strip_line(As);
+strip_line([A | As]) ->
+ [A | strip_line(As)];
+strip_line([]) ->
+ [].
+
+get_line([L | _As]) when integer(L) ->
+ L;
+get_line([_ | As]) ->
+ get_line(As);
+get_line([]) ->
+ none.
+
+format(Node, Ctxt) ->
+ maybe_anno(Node, fun format_1/2, Ctxt).
+
+format_1(#c_char{val=C}, _) -> io_lib:write_char(C);
+format_1(#c_int{val=I}, _) -> integer_to_list(I);
+format_1(#c_float{val=F}, _) -> float_to_list(F);
+format_1(#c_atom{val=A}, _) -> core_atom(A);
+format_1(#c_nil{}, _) -> "[]";
+format_1(#c_string{val=S}, _) -> io_lib:write_string(S);
+format_1(#c_var{name=V}, _) ->
+ %% Internal variable names may be:
+ %% - atoms representing proper Erlang variable names, or
+ %% any atoms that may be printed without single-quoting
+ %% - nonnegative integers.
+ %% It is important that when printing variables, no two names
+ %% should ever map to the same string.
+ if atom(V) ->
+ S = atom_to_list(V),
+ case S of
+ [C | _] when C >= $A, C =< $Z ->
+ %% Ordinary uppercase-prefixed names are
+ %% printed just as they are.
+ S;
+ [$_ | _] ->
+ %% Already "_"-prefixed names are prefixed
+ %% with "_X", e.g. '_foo' => '_X_foo', to
+ %% avoid generating things like "____foo" upon
+ %% repeated writing and reading of code.
+ %% ("_X_X_X_foo" is better.)
+ [$_, $X | S];
+ _ ->
+ %% Plain atoms are prefixed with a single "_".
+ %% E.g. foo => "_foo".
+ [$_ | S]
+ end;
+ integer(V) ->
+ %% Integers are also simply prefixed with "_".
+ [$_ | integer_to_list(V)]
+ end;
+format_1(#c_binary{segments=Segs}, Ctxt) ->
+ ["#{",
+ format_vseq(Segs, "", ",", add_indent(Ctxt, 2),
+ fun format_bitstr/2),
+ "}#"
+ ];
+format_1(#c_tuple{es=Es}, Ctxt) ->
+ [${,
+ format_hseq(Es, ",", add_indent(Ctxt, 1), fun format/2),
+ $}
+ ];
+format_1(#c_cons{hd=H,tl=T}, Ctxt) ->
+ Txt = ["["|format(H, add_indent(Ctxt, 1))],
+ [Txt|format_list_tail(T, add_indent(Ctxt, width(Txt, Ctxt)))];
+format_1(#c_values{es=Es}, Ctxt) ->
+ format_values(Es, Ctxt);
+format_1(#c_alias{var=V,pat=P}, Ctxt) ->
+ Txt = [format(V, Ctxt)|" = "],
+ [Txt|format(P, add_indent(Ctxt, width(Txt, Ctxt)))];
+format_1(#c_let{vars=Vs,arg=A,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["let ",
+ format_values(Vs, add_indent(Ctxt, 4)),
+ " =",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "in "
+ | format(B, add_indent(Ctxt, 4))
+ ];
+format_1(#c_letrec{defs=Fs,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["letrec",
+ nl_indent(Ctxt1),
+ format_funcs(Fs, Ctxt1),
+ nl_indent(Ctxt),
+ "in "
+ | format(B, add_indent(Ctxt, 4))
+ ];
+format_1(#c_seq{arg=A,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, 4),
+ ["do ",
+ format(A, Ctxt1),
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#c_case{arg=A,clauses=Cs}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["case ",
+ format(A, add_indent(Ctxt, 5)),
+ " of",
+ nl_indent(Ctxt1),
+ format_clauses(Cs, Ctxt1),
+ nl_indent(Ctxt)
+ | "end"
+ ];
+format_1(#c_receive{clauses=Cs,timeout=T,action=A}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["receive",
+ nl_indent(Ctxt1),
+ format_clauses(Cs, Ctxt1),
+ nl_indent(Ctxt),
+ "after ",
+ format(T, add_indent(Ctxt, 6)),
+ " ->",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1)
+ ];
+format_1(#c_fname{id=I,arity=A}, _) ->
+ [core_atom(I),$/,integer_to_list(A)];
+format_1(#c_fun{vars=Vs,body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["fun (",
+ format_hseq(Vs, ",", add_indent(Ctxt, 5), fun format/2),
+ ") ->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#c_apply{op=O,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 6), %"apply "
+ Op = format(O, Ctxt1),
+ Ctxt2 = add_indent(Ctxt0, 4),
+ ["apply ",Op,
+ nl_indent(Ctxt2),
+ $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$)
+ ];
+format_1(#c_call{module=M,name=N,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 5), %"call "
+ Mod = format(M, Ctxt1),
+ Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1),
+ Name = format(N, Ctxt2),
+ Ctxt3 = add_indent(Ctxt0, 4),
+ ["call ",Mod,":",Name,
+ nl_indent(Ctxt3),
+ $(,format_hseq(As, ", ", add_indent(Ctxt3, 1), fun format/2),$)
+ ];
+format_1(#c_primop{name=N,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 7), %"primop "
+ Name = format(N, Ctxt1),
+ Ctxt2 = add_indent(Ctxt0, 4),
+ ["primop ",Name,
+ nl_indent(Ctxt2),
+ $(,format_hseq(As, ", ", add_indent(Ctxt2, 1), fun format/2),$)
+ ];
+format_1(#c_catch{body=B}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["catch",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1)
+ ];
+format_1(#c_try{arg=E,vars=Vs,body=B,evars=Evs,handler=H}, Ctxt) ->
+ Ctxt1 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["try",
+ nl_indent(Ctxt1),
+ format(E, Ctxt1),
+ nl_indent(Ctxt),
+ "of ",
+ format_values(Vs, add_indent(Ctxt, 3)),
+ " ->",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "catch ",
+ format_values(Evs, add_indent(Ctxt, 6)),
+ " ->",
+ nl_indent(Ctxt1)
+ | format(H, Ctxt1)
+ ];
+format_1(#c_def{name=N,val=V}, Ctxt) ->
+ Ctxt1 = add_indent(set_class(Ctxt, expr), Ctxt#ctxt.body_indent),
+ [format(N, Ctxt),
+ " =",
+ nl_indent(Ctxt1)
+ | format(V, Ctxt1)
+ ];
+format_1(#c_module{name=N,exports=Es,attrs=As,defs=Ds}, Ctxt) ->
+ Mod = ["module ", format(N, Ctxt)],
+ [Mod," [",
+ format_vseq(Es,
+ "", ",",
+ add_indent(set_class(Ctxt, term), width(Mod, Ctxt)+2),
+ fun format/2),
+ "]",
+ nl_indent(Ctxt),
+ " attributes [",
+ format_vseq(As,
+ "", ",",
+ add_indent(set_class(Ctxt, def), 16),
+ fun format/2),
+ "]",
+ nl_indent(Ctxt),
+ format_funcs(Ds, Ctxt),
+ nl_indent(Ctxt)
+ | "end"
+ ];
+format_1(Type, _) ->
+ ["** Unsupported type: ",
+ io_lib:write(Type)
+ | " **"
+ ].
+
+format_funcs(Fs, Ctxt) ->
+ format_vseq(Fs,
+ "", "",
+ set_class(Ctxt, def),
+ fun format/2).
+
+format_values(Vs, Ctxt) ->
+ [$<,
+ format_hseq(Vs, ",", add_indent(Ctxt, 1), fun format/2),
+ $>].
+
+format_bitstr(#c_bitstr{val=V,size=S,unit=U,type=T,flags=Fs}, Ctxt0) ->
+ Vs = [S, U, T, Fs],
+ Ctxt1 = add_indent(Ctxt0, 2),
+ Val = format(V, Ctxt1),
+ Ctxt2 = add_indent(Ctxt1, width(Val, Ctxt1) + 2),
+ ["#<", Val, ">(", format_hseq(Vs,",", Ctxt2, fun format/2), $)].
+
+format_clauses(Cs, Ctxt) ->
+ format_vseq(Cs, "", "", set_class(Ctxt, clause),
+ fun format_clause/2).
+
+format_clause(Node, Ctxt) ->
+ maybe_anno(Node, fun format_clause_1/2, Ctxt).
+
+format_clause_1(#c_clause{pats=Ps,guard=G,body=B}, Ctxt) ->
+ Ptxt = format_values(Ps, Ctxt),
+ Ctxt2 = add_indent(Ctxt, Ctxt#ctxt.body_indent),
+ [Ptxt,
+ " when ",
+ format_guard(G, add_indent(set_class(Ctxt, expr),
+ width(Ptxt, Ctxt) + 6)),
+ " ->",
+ nl_indent(Ctxt2)
+ | format(B, set_class(Ctxt2, expr))
+ ].
+
+format_guard(Node, Ctxt) ->
+ maybe_anno(Node, fun format_guard_1/2, Ctxt).
+
+format_guard_1(#c_call{module=M,name=N,args=As}, Ctxt0) ->
+ Ctxt1 = add_indent(Ctxt0, 5), %"call "
+ Mod = format(M, Ctxt1),
+ Ctxt2 = add_indent(Ctxt1, width(Mod, Ctxt1)+1),
+ Name = format(N, Ctxt2),
+ Ctxt3 = add_indent(Ctxt0, 4),
+ ["call ",Mod,":",Name,
+ nl_indent(Ctxt3),
+ $(,format_vseq(As, "",",", add_indent(Ctxt3, 1), fun format_guard/2),$)
+ ];
+format_guard_1(E, Ctxt) -> format_1(E, Ctxt). %Anno already done
+
+%% format_hseq([Thing], Separator, Context, Fun) -> Txt.
+%% Format a sequence horizontally on the same line with Separator between.
+
+format_hseq([H], _, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_hseq([H|T], Sep, Ctxt, Fun) ->
+ Txt = [Fun(H, Ctxt)|Sep],
+ Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_hseq(T, Sep, Ctxt1, Fun)];
+format_hseq([], _, _, _) -> "".
+
+%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt.
+%% Format a sequence vertically in indented lines adding LinePrefix
+%% to the beginning of each line and LineSuffix to the end of each
+%% line. No prefix on the first line or suffix on the last line.
+
+format_vseq([H], _Pre, _Suf, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_vseq([H|T], Pre, Suf, Ctxt, Fun) ->
+ [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre|
+ format_vseq(T, Pre, Suf, Ctxt, Fun)];
+format_vseq([], _, _, _, _) -> "".
+
+format_list_tail(#c_nil{anno=[]}, _) -> "]";
+format_list_tail(#c_cons{anno=[],hd=H,tl=T}, Ctxt) ->
+ Txt = [$,|format(H, Ctxt)],
+ Ctxt1 = add_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_list_tail(T, Ctxt1)];
+format_list_tail(Tail, Ctxt) ->
+ ["|",format(Tail, add_indent(Ctxt, 1)),"]"].
+
+indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
+
+indent(N, _) when N =< 0 -> "";
+indent(N, Ctxt) ->
+ T = Ctxt#ctxt.tab_width,
+ string:chars($\t, N div T, string:chars($\s, N rem T)).
+
+nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
+
+
+unindent(T, Ctxt) ->
+ unindent(T, Ctxt#ctxt.indent, Ctxt, []).
+
+unindent(T, N, _, C) when N =< 0 ->
+ [T|C];
+unindent([$\s|T], N, Ctxt, C) ->
+ unindent(T, N - 1, Ctxt, C);
+unindent([$\t|T], N, Ctxt, C) ->
+ Tab = Ctxt#ctxt.tab_width,
+ if N >= Tab ->
+ unindent(T, N - Tab, Ctxt, C);
+ true ->
+ unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
+ end;
+unindent([L|T], N, Ctxt, C) when list(L) ->
+ unindent(L, N, Ctxt, [T|C]);
+unindent([H|T], _, _, C) ->
+ [H|[T|C]];
+unindent([], N, Ctxt, [H|T]) ->
+ unindent(H, N, Ctxt, T);
+unindent([], _, _, []) -> [].
+
+
+width(Txt, Ctxt) ->
+ case catch width(Txt, 0, Ctxt, []) of
+ {'EXIT',_} -> exit({bad_text,Txt});
+ Other -> Other
+ end.
+
+width([$\t|T], A, Ctxt, C) ->
+ width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
+width([$\n|T], _, Ctxt, C) ->
+ width(unindent([T|C], Ctxt), Ctxt);
+width([H|T], A, Ctxt, C) when list(H) ->
+ width(H, A, Ctxt, [T|C]);
+width([_|T], A, Ctxt, C) ->
+ width(T, A + 1, Ctxt, C);
+width([], A, Ctxt, [H|T]) ->
+ width(H, A, Ctxt, T);
+width([], A, _, []) -> A.
+
+add_indent(Ctxt, Dx) ->
+ Ctxt#ctxt{indent = Ctxt#ctxt.indent + Dx}.
+
+set_class(Ctxt, Class) ->
+ Ctxt#ctxt{class = Class}.
+
+core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl
new file mode 100644
index 0000000000..f53c3c1631
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/core_scan.erl
@@ -0,0 +1,495 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: core_scan.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose: Scanner for Core Erlang.
+
+%% For handling ISO 8859-1 (Latin-1) we use the following type
+%% information:
+%%
+%% 000 - 037 NUL - US control
+%% 040 - 057 SPC - / punctuation
+%% 060 - 071 0 - 9 digit
+%% 072 - 100 : - @ punctuation
+%% 101 - 132 A - Z uppercase
+%% 133 - 140 [ - ` punctuation
+%% 141 - 172 a - z lowercase
+%% 173 - 176 { - ~ punctuation
+%% 177 DEL control
+%% 200 - 237 control
+%% 240 - 277 NBSP - � punctuation
+%% 300 - 326 � - � uppercase
+%% 327 � punctuation
+%% 330 - 336 � - � uppercase
+%% 337 - 366 � - � lowercase
+%% 367 � punctuation
+%% 370 - 377 � - � lowercase
+%%
+%% Many punctuation characters region have special meaning. Must
+%% watch using � \327, bvery close to x \170
+
+-module(core_scan).
+
+-export([string/1,string/2,tokens/3,format_error/1]).
+
+-import(lists, [reverse/1]).
+
+%% tokens(Continuation, CharList, StartPos) ->
+%% {done, {ok, [Tok], EndPos}, Rest} |
+%% {done, {error,{ErrorPos,core_scan,What}, EndPos}, Rest} |
+%% {more, Continuation'}
+%% This is the main function into the re-entrant scanner. It calls the
+%% re-entrant pre-scanner until this says done, then calls scan/1 on
+%% the result.
+%%
+%% The continuation has the form:
+%% {RestChars,CharsSoFar,CurrentPos,StartPos}
+
+tokens([], Chars, Pos) -> %First call
+ tokens({[],[],Pos,Pos}, Chars, Pos);
+tokens({Chars,SoFar0,Cp,Sp}, MoreChars, _) ->
+ In = Chars ++ MoreChars,
+ case pre_scan(In, SoFar0, Cp) of
+ {done,_,[],Ep} -> %Found nothing
+ {done,{eof,Ep},[]};
+ {done,_,SoFar1,Ep} -> %Got complete tokens
+ Res = case scan(reverse(SoFar1), Sp) of
+ {ok,Toks} -> {ok,Toks,Ep};
+ {error,E} -> {error,E,Ep}
+ end,
+ {done,Res,[]};
+ {more,Rest,SoFar1,Cp1} -> %Missing end token
+ {more,{Rest,SoFar1,Cp1,Sp}};
+ Other -> %An error has occurred
+ {done,Other,[]}
+ end.
+
+%% string([Char]) ->
+%% string([Char], StartPos) ->
+%% {ok, [Tok], EndPos} |
+%% {error,{Pos,core_scan,What}, EndPos}
+
+string(Cs) -> string(Cs, 1).
+
+string(Cs, Sp) ->
+ %% Add an 'eof' to always get correct handling.
+ case string_pre_scan(Cs, [], Sp) of
+ {done,_,SoFar,Ep} -> %Got tokens
+ case scan(reverse(SoFar), Sp) of
+ {ok,Toks} -> {ok,Toks,Ep};
+ {error,E} -> {error,E,Ep}
+ end;
+ Other -> Other %An error has occurred
+ end.
+
+%% string_pre_scan(Cs, SoFar0, StartPos) ->
+%% {done,Rest,SoFar,EndPos} | {error,E,EndPos}.
+
+string_pre_scan(Cs, SoFar0, Sp) ->
+ case pre_scan(Cs, SoFar0, Sp) of
+ {done,Rest,SoFar1,Ep} -> %Got complete tokens
+ {done,Rest,SoFar1,Ep};
+ {more,Rest,SoFar1,Ep} -> %Missing end token
+ string_pre_scan(Rest ++ eof, SoFar1, Ep);
+ Other -> Other %An error has occurred
+ end.
+
+%% format_error(Error)
+%% Return a string describing the error.
+
+format_error({string,Quote,Head}) ->
+ ["unterminated " ++ string_thing(Quote) ++
+ " starting with " ++ io_lib:write_string(Head,Quote)];
+format_error({illegal,Type}) -> io_lib:fwrite("illegal ~w", [Type]);
+format_error(char) -> "unterminated character";
+format_error(scan) -> "premature end";
+format_error({base,Base}) -> io_lib:fwrite("illegal base '~w'", [Base]);
+format_error(float) -> "bad float";
+format_error(Other) -> io_lib:write(Other).
+
+string_thing($') -> "atom";
+string_thing($") -> "string".
+
+%% Re-entrant pre-scanner.
+%%
+%% If the input list of characters is insufficient to build a term the
+%% scanner returns a request for more characters and a continuation to be
+%% used when trying to build a term with more characters. To indicate
+%% end-of-file the input character list should be replaced with 'eof'
+%% as an empty list has meaning.
+%%
+%% When more characters are need inside a comment, string or quoted
+%% atom, which can become rather long, instead of pushing the
+%% characters read so far back onto RestChars to be reread, a special
+%% reentry token is returned indicating the middle of a construct.
+%% The token is the start character as an atom, '%', '"' and '\''.
+
+%% pre_scan([Char], SoFar, StartPos) ->
+%% {done,RestChars,ScannedChars,NewPos} |
+%% {more,RestChars,ScannedChars,NewPos} |
+%% {error,{ErrorPos,core_scan,Description},NewPos}.
+%% Main pre-scan function. It has been split into 2 functions because of
+%% efficiency, with a good indexing compiler it would be unnecessary.
+
+pre_scan([C|Cs], SoFar, Pos) ->
+ pre_scan(C, Cs, SoFar, Pos);
+pre_scan([], SoFar, Pos) ->
+ {more,[],SoFar,Pos};
+pre_scan(eof, SoFar, Pos) ->
+ {done,eof,SoFar,Pos}.
+
+%% pre_scan(Char, [Char], SoFar, Pos)
+
+pre_scan($$, Cs0, SoFar0, Pos) ->
+ case pre_char(Cs0, [$$|SoFar0]) of
+ {Cs,SoFar} ->
+ pre_scan(Cs, SoFar, Pos);
+ more ->
+ {more,[$$|Cs0],SoFar0, Pos};
+ error ->
+ pre_error(char, Pos, Pos)
+ end;
+pre_scan($', Cs, SoFar, Pos) ->
+ pre_string(Cs, $', '\'', Pos, [$'|SoFar], Pos);
+pre_scan({'\'',Sp}, Cs, SoFar, Pos) -> %Re-entering quoted atom
+ pre_string(Cs, $', '\'', Sp, SoFar, Pos);
+pre_scan($", Cs, SoFar, Pos) ->
+ pre_string(Cs, $", '"', Pos, [$"|SoFar], Pos);
+pre_scan({'"',Sp}, Cs, SoFar, Pos) -> %Re-entering string
+ pre_string(Cs, $", '"', Sp, SoFar, Pos);
+pre_scan($%, Cs, SoFar, Pos) ->
+ pre_comment(Cs, SoFar, Pos);
+pre_scan('%', Cs, SoFar, Pos) -> %Re-entering comment
+ pre_comment(Cs, SoFar, Pos);
+pre_scan($\n, Cs, SoFar, Pos) ->
+ pre_scan(Cs, [$\n|SoFar], Pos+1);
+pre_scan(C, Cs, SoFar, Pos) ->
+ pre_scan(Cs, [C|SoFar], Pos).
+
+%% pre_string([Char], Quote, Reent, StartPos, SoFar, Pos)
+
+pre_string([Q|Cs], Q, _, _, SoFar, Pos) ->
+ pre_scan(Cs, [Q|SoFar], Pos);
+pre_string([$\n|Cs], Q, Reent, Sp, SoFar, Pos) ->
+ pre_string(Cs, Q, Reent, Sp, [$\n|SoFar], Pos+1);
+pre_string([$\\|Cs0], Q, Reent, Sp, SoFar0, Pos) ->
+ case pre_escape(Cs0, SoFar0) of
+ {Cs,SoFar} ->
+ pre_string(Cs, Q, Reent, Sp, SoFar, Pos);
+ more ->
+ {more,[{Reent,Sp},$\\|Cs0],SoFar0,Pos};
+ error ->
+ pre_string_error(Q, Sp, SoFar0, Pos)
+ end;
+pre_string([C|Cs], Q, Reent, Sp, SoFar, Pos) ->
+ pre_string(Cs, Q, Reent, Sp, [C|SoFar], Pos);
+pre_string([], _, Reent, Sp, SoFar, Pos) ->
+ {more,[{Reent,Sp}],SoFar,Pos};
+pre_string(eof, Q, _, Sp, SoFar, Pos) ->
+ pre_string_error(Q, Sp, SoFar, Pos).
+
+pre_string_error(Q, Sp, SoFar, Pos) ->
+ S = reverse(string:substr(SoFar, 1, string:chr(SoFar, Q)-1)),
+ pre_error({string,Q,string:substr(S, 1, 16)}, Sp, Pos).
+
+pre_char([C|Cs], SoFar) -> pre_char(C, Cs, SoFar);
+pre_char([], _) -> more;
+pre_char(eof, _) -> error.
+
+pre_char($\\, Cs, SoFar) ->
+ pre_escape(Cs, SoFar);
+pre_char(C, Cs, SoFar) ->
+ {Cs,[C|SoFar]}.
+
+pre_escape([$^|Cs0], SoFar) ->
+ case Cs0 of
+ [C3|Cs] ->
+ {Cs,[C3,$^,$\\|SoFar]};
+ [] -> more;
+ eof -> error
+ end;
+pre_escape([C|Cs], SoFar) ->
+ {Cs,[C,$\\|SoFar]};
+pre_escape([], _) -> more;
+pre_escape(eof, _) -> error.
+
+%% pre_comment([Char], SoFar, Pos)
+%% Comments are replaced by one SPACE.
+
+pre_comment([$\n|Cs], SoFar, Pos) ->
+ pre_scan(Cs, [$\n,$\s|SoFar], Pos+1); %Terminate comment
+pre_comment([_|Cs], SoFar, Pos) ->
+ pre_comment(Cs, SoFar, Pos);
+pre_comment([], SoFar, Pos) ->
+ {more,['%'],SoFar,Pos};
+pre_comment(eof, Sofar, Pos) ->
+ pre_scan(eof, [$\s|Sofar], Pos).
+
+pre_error(E, Epos, Pos) ->
+ {error,{Epos,core_scan,E}, Pos}.
+
+%% scan(CharList, StartPos)
+%% This takes a list of characters and tries to tokenise them.
+%%
+%% The token list is built in reverse order (in a stack) to save appending
+%% and then reversed when all the tokens have been collected. Most tokens
+%% are built in the same way.
+%%
+%% Returns:
+%% {ok,[Tok]}
+%% {error,{ErrorPos,core_scan,What}}
+
+scan(Cs, Pos) ->
+ scan1(Cs, [], Pos).
+
+%% scan1(Characters, TokenStack, Position)
+%% Scan a list of characters into tokens.
+
+scan1([$\n|Cs], Toks, Pos) -> %Skip newline
+ scan1(Cs, Toks, Pos+1);
+scan1([C|Cs], Toks, Pos) when C >= $\000, C =< $\s -> %Skip control chars
+ scan1(Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $\200, C =< $\240 ->
+ scan1(Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $a, C =< $z -> %Keywords
+ scan_key_word(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $�, C =< $�, C /= $� ->
+ scan_key_word(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $A, C =< $Z -> %Variables
+ scan_variable(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $�, C =< $�, C /= $� ->
+ scan_variable(C, Cs, Toks, Pos);
+scan1([C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Numbers
+ scan_number(C, Cs, Toks, Pos);
+scan1([$-,C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Signed numbers
+ scan_signed_number($-, C, Cs, Toks, Pos);
+scan1([$+,C|Cs], Toks, Pos) when C >= $0, C =< $9 -> %Signed numbers
+ scan_signed_number($+, C, Cs, Toks, Pos);
+scan1([$_|Cs], Toks, Pos) -> %_ variables
+ scan_variable($_, Cs, Toks, Pos);
+scan1([$$|Cs0], Toks, Pos) -> %Character constant
+ {C,Cs,Pos1} = scan_char(Cs0, Pos),
+ scan1(Cs, [{char,Pos,C}|Toks], Pos1);
+scan1([$'|Cs0], Toks, Pos) -> %Atom (always quoted)
+ {S,Cs1,Pos1} = scan_string(Cs0, $', Pos),
+ case catch list_to_atom(S) of
+ A when atom(A) ->
+ scan1(Cs1, [{atom,Pos,A}|Toks], Pos1);
+ _Error -> scan_error({illegal,atom}, Pos)
+ end;
+scan1([$"|Cs0], Toks, Pos) -> %String
+ {S,Cs1,Pos1} = scan_string(Cs0, $", Pos),
+ scan1(Cs1, [{string,Pos,S}|Toks], Pos1);
+%% Punctuation characters and operators, first recognise multiples.
+scan1("->" ++ Cs, Toks, Pos) ->
+ scan1(Cs, [{'->',Pos}|Toks], Pos);
+scan1("-|" ++ Cs, Toks, Pos) ->
+ scan1(Cs, [{'-|',Pos}|Toks], Pos);
+scan1([C|Cs], Toks, Pos) -> %Punctuation character
+ P = list_to_atom([C]),
+ scan1(Cs, [{P,Pos}|Toks], Pos);
+scan1([], Toks0, _) ->
+ Toks = reverse(Toks0),
+ {ok,Toks}.
+
+%% scan_key_word(FirstChar, CharList, Tokens, Pos)
+%% scan_variable(FirstChar, CharList, Tokens, Pos)
+
+scan_key_word(C, Cs0, Toks, Pos) ->
+ {Wcs,Cs} = scan_name(Cs0, []),
+ case catch list_to_atom([C|reverse(Wcs)]) of
+ Name when atom(Name) ->
+ scan1(Cs, [{Name,Pos}|Toks], Pos);
+ _Error -> scan_error({illegal,atom}, Pos)
+ end.
+
+scan_variable(C, Cs0, Toks, Pos) ->
+ {Wcs,Cs} = scan_name(Cs0, []),
+ case catch list_to_atom([C|reverse(Wcs)]) of
+ Name when atom(Name) ->
+ scan1(Cs, [{var,Pos,Name}|Toks], Pos);
+ _Error -> scan_error({illegal,var}, Pos)
+ end.
+
+%% scan_name(Cs) -> lists:splitwith(fun (C) -> name_char(C) end, Cs).
+
+scan_name([C|Cs], Ncs) ->
+ case name_char(C) of
+ true -> scan_name(Cs, [C|Ncs]);
+ false -> {Ncs,[C|Cs]} %Must rebuild here, sigh!
+ end;
+scan_name([], Ncs) ->
+ {Ncs,[]}.
+
+name_char(C) when C >= $a, C =< $z -> true;
+name_char(C) when C >= $�, C =< $�, C /= $� -> true;
+name_char(C) when C >= $A, C =< $Z -> true;
+name_char(C) when C >= $�, C =< $�, C /= $� -> true;
+name_char(C) when C >= $0, C =< $9 -> true;
+name_char($_) -> true;
+name_char($@) -> true;
+name_char(_) -> false.
+
+%% scan_string(CharList, QuoteChar, Pos) -> {StringChars,RestChars,NewPos}.
+
+scan_string(Cs, Q, Pos) ->
+ scan_string(Cs, [], Q, Pos).
+
+scan_string([Q|Cs], Scs, Q, Pos) ->
+ {reverse(Scs),Cs,Pos};
+scan_string([$\n|Cs], Scs, Q, Pos) ->
+ scan_string(Cs, [$\n|Scs], Q, Pos+1);
+scan_string([$\\|Cs0], Scs, Q, Pos) ->
+ {C,Cs,Pos1} = scan_escape(Cs0, Pos),
+ scan_string(Cs, [C|Scs], Q, Pos1);
+scan_string([C|Cs], Scs, Q, Pos) ->
+ scan_string(Cs, [C|Scs], Q, Pos).
+
+%% scan_char(Chars, Pos) -> {Char,RestChars,NewPos}.
+%% Read a single character from a character constant. The pre-scan
+%% phase has checked for errors here.
+
+scan_char([$\\|Cs], Pos) ->
+ scan_escape(Cs, Pos);
+scan_char([$\n|Cs], Pos) -> %Newline
+ {$\n,Cs,Pos+1};
+scan_char([C|Cs], Pos) ->
+ {C,Cs,Pos}.
+
+scan_escape([O1,O2,O3|Cs], Pos) when %\<1-3> octal digits
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7, O3 >= $0, O3 =< $7 ->
+ Val = (O1*8 + O2)*8 + O3 - 73*$0,
+ {Val,Cs,Pos};
+scan_escape([O1,O2|Cs], Pos) when
+ O1 >= $0, O1 =< $7, O2 >= $0, O2 =< $7 ->
+ Val = (O1*8 + O2) - 9*$0,
+ {Val,Cs,Pos};
+scan_escape([O1|Cs], Pos) when
+ O1 >= $0, O1 =< $7 ->
+ {O1 - $0,Cs,Pos};
+scan_escape([$^,C|Cs], Pos) -> %\^X -> CTL-X
+ Val = C band 31,
+ {Val,Cs,Pos};
+%scan_escape([$\n,C1|Cs],Pos) ->
+% {C1,Cs,Pos+1};
+%scan_escape([C,C1|Cs],Pos) when C >= $\000, C =< $\s ->
+% {C1,Cs,Pos};
+scan_escape([$\n|Cs],Pos) ->
+ {$\n,Cs,Pos+1};
+scan_escape([C0|Cs],Pos) ->
+ C = escape_char(C0),
+ {C,Cs,Pos}.
+
+escape_char($n) -> $\n; %\n = LF
+escape_char($r) -> $\r; %\r = CR
+escape_char($t) -> $\t; %\t = TAB
+escape_char($v) -> $\v; %\v = VT
+escape_char($b) -> $\b; %\b = BS
+escape_char($f) -> $\f; %\f = FF
+escape_char($e) -> $\e; %\e = ESC
+escape_char($s) -> $\s; %\s = SPC
+escape_char($d) -> $\d; %\d = DEL
+escape_char(C) -> C.
+
+%% scan_number(Char, CharList, TokenStack, Pos)
+%% We can handle simple radix notation:
+%% <digit>#<digits> - the digits read in that base
+%% <digits> - the digits in base 10
+%% <digits>.<digits>
+%% <digits>.<digits>E+-<digits>
+%%
+%% Except for explicitly based integers we build a list of all the
+%% characters and then use list_to_integer/1 or list_to_float/1 to
+%% generate the value.
+
+%% SPos == Start position
+%% CPos == Current position
+
+scan_number(C, Cs0, Toks, Pos) ->
+ {Ncs,Cs,Pos1} = scan_integer(Cs0, [C], Pos),
+ scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
+
+scan_signed_number(S, C, Cs0, Toks, Pos) ->
+ {Ncs,Cs,Pos1} = scan_integer(Cs0, [C,S], Pos),
+ scan_after_int(Cs, Ncs, Toks, Pos, Pos1).
+
+scan_integer([C|Cs], Stack, Pos) when C >= $0, C =< $9 ->
+ scan_integer(Cs, [C|Stack], Pos);
+scan_integer(Cs, Stack, Pos) ->
+ {Stack,Cs,Pos}.
+
+scan_after_int([$.,C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
+ {Ncs,Cs,CPos1} = scan_integer(Cs0, [C,$.|Ncs0], CPos),
+ scan_after_fraction(Cs, Ncs, Toks, SPos, CPos1);
+scan_after_int([$#|Cs], Ncs, Toks, SPos, CPos) ->
+ case list_to_integer(reverse(Ncs)) of
+ Base when Base >= 2, Base =< 16 ->
+ scan_based_int(Cs, 0, Base, Toks, SPos, CPos);
+ Base ->
+ scan_error({base,Base}, CPos)
+ end;
+scan_after_int(Cs, Ncs, Toks, SPos, CPos) ->
+ N = list_to_integer(reverse(Ncs)),
+ scan1(Cs, [{integer,SPos,N}|Toks], CPos).
+
+scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
+ C >= $0, C =< $9, C < Base + $0 ->
+ Next = SoFar * Base + (C - $0),
+ scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
+scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
+ C >= $a, C =< $f, C < Base + $a - 10 ->
+ Next = SoFar * Base + (C - $a + 10),
+ scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
+scan_based_int([C|Cs], SoFar, Base, Toks, SPos, CPos) when
+ C >= $A, C =< $F, C < Base + $A - 10 ->
+ Next = SoFar * Base + (C - $A + 10),
+ scan_based_int(Cs, Next, Base, Toks, SPos, CPos);
+scan_based_int(Cs, SoFar, _, Toks, SPos, CPos) ->
+ scan1(Cs, [{integer,SPos,SoFar}|Toks], CPos).
+
+scan_after_fraction([$E|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
+scan_after_fraction([$e|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent(Cs, [$E|Ncs], Toks, SPos, CPos);
+scan_after_fraction(Cs, Ncs, Toks, SPos, CPos) ->
+ case catch list_to_float(reverse(Ncs)) of
+ N when float(N) ->
+ scan1(Cs, [{float,SPos,N}|Toks], CPos);
+ _Error -> scan_error({illegal,float}, SPos)
+ end.
+
+%% scan_exponent(CharList, NumberCharStack, TokenStack, StartPos, CurPos)
+%% Generate an error here if E{+|-} not followed by any digits.
+
+scan_exponent([$+|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, [$+|Ncs], Toks, SPos, CPos);
+scan_exponent([$-|Cs], Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, [$-|Ncs], Toks, SPos, CPos);
+scan_exponent(Cs, Ncs, Toks, SPos, CPos) ->
+ scan_exponent1(Cs, Ncs, Toks, SPos, CPos).
+
+scan_exponent1([C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
+ {Ncs,Cs,CPos1} = scan_integer(Cs0, [C|Ncs0], CPos),
+ case catch list_to_float(reverse(Ncs)) of
+ N when float(N) ->
+ scan1(Cs, [{float,SPos,N}|Toks], CPos1);
+ _Error -> scan_error({illegal,float}, SPos)
+ end;
+scan_exponent1(_, _, _, _, CPos) ->
+ scan_error(float, CPos).
+
+scan_error(In, Pos) ->
+ {error,{Pos,core_scan,In}}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl
new file mode 100644
index 0000000000..088f44f9fd
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/erl_bifs.erl
@@ -0,0 +1,486 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: erl_bifs.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
+%%
+%% Purpose: Information about the Erlang built-in functions.
+
+-module(erl_bifs).
+
+-export([is_bif/3, is_guard_bif/3, is_pure/3, is_safe/3]).
+
+
+%% =====================================================================
+%% is_bif(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the function `Module:Name/Arity' is a Built-In
+%% Function (BIF) of Erlang. BIFs "come with the implementation",
+%% and can be assumed to exist and have the same behaviour in any
+%% later versions of the same implementation of the language. Being
+%% a BIF does *not* imply that the function belongs to the module
+%% `erlang', nor that it is implemented in C or assembler (cf.
+%% `erlang:is_builtin/3'), or that it is auto-imported by the
+%% compiler (cf. `erl_internal:bif/3').
+
+is_bif(erlang, '!', 2) -> true;
+is_bif(erlang, '*', 2) -> true;
+is_bif(erlang, '+', 1) -> true;
+is_bif(erlang, '+', 2) -> true;
+is_bif(erlang, '++', 2) -> true;
+is_bif(erlang, '-', 1) -> true;
+is_bif(erlang, '-', 2) -> true;
+is_bif(erlang, '--', 2) -> true;
+is_bif(erlang, '/', 2) -> true;
+is_bif(erlang, '/=', 2) -> true;
+is_bif(erlang, '<', 2) -> true;
+is_bif(erlang, '=/=', 2) -> true;
+is_bif(erlang, '=:=', 2) -> true;
+is_bif(erlang, '=<', 2) -> true;
+is_bif(erlang, '==', 2) -> true;
+is_bif(erlang, '>', 2) -> true;
+is_bif(erlang, '>=', 2) -> true;
+is_bif(erlang, 'and', 2) -> true;
+is_bif(erlang, 'band', 2) -> true;
+is_bif(erlang, 'bnot', 1) -> true;
+is_bif(erlang, 'bor', 2) -> true;
+is_bif(erlang, 'bsl', 2) -> true;
+is_bif(erlang, 'bsr', 2) -> true;
+is_bif(erlang, 'bxor', 2) -> true;
+is_bif(erlang, 'div', 2) -> true;
+is_bif(erlang, 'not', 1) -> true;
+is_bif(erlang, 'or', 2) -> true;
+is_bif(erlang, 'rem', 2) -> true;
+is_bif(erlang, 'xor', 2) -> true;
+is_bif(erlang, abs, 1) -> true;
+is_bif(erlang, append_element, 2) -> true;
+is_bif(erlang, apply, 2) -> true;
+is_bif(erlang, apply, 3) -> true;
+is_bif(erlang, atom_to_list, 1) -> true;
+is_bif(erlang, binary_to_list, 1) -> true;
+is_bif(erlang, binary_to_list, 3) -> true;
+is_bif(erlang, binary_to_term, 1) -> true;
+is_bif(erlang, cancel_timer, 1) -> true;
+is_bif(erlang, concat_binary, 1) -> true;
+is_bif(erlang, date, 0) -> true;
+is_bif(erlang, demonitor, 1) -> true;
+is_bif(erlang, disconnect_node, 1) -> true;
+is_bif(erlang, display, 1) -> true;
+is_bif(erlang, element, 2) -> true;
+is_bif(erlang, erase, 0) -> true;
+is_bif(erlang, erase, 1) -> true;
+is_bif(erlang, error, 1) -> true;
+is_bif(erlang, error, 2) -> true;
+is_bif(erlang, exit, 1) -> true;
+is_bif(erlang, exit, 2) -> true;
+is_bif(erlang, fault, 1) -> true;
+is_bif(erlang, fault, 2) -> true;
+is_bif(erlang, float, 1) -> true;
+is_bif(erlang, float_to_list, 1) -> true;
+is_bif(erlang, fun_info, 1) -> true;
+is_bif(erlang, fun_info, 2) -> true;
+is_bif(erlang, fun_to_list, 1) -> true;
+is_bif(erlang, get, 0) -> true;
+is_bif(erlang, get, 1) -> true;
+is_bif(erlang, get_cookie, 0) -> true;
+is_bif(erlang, get_keys, 1) -> true;
+is_bif(erlang, group_leader, 0) -> true;
+is_bif(erlang, group_leader, 2) -> true;
+is_bif(erlang, halt, 0) -> false;
+is_bif(erlang, halt, 1) -> false;
+is_bif(erlang, hash, 2) -> false;
+is_bif(erlang, hd, 1) -> true;
+is_bif(erlang, info, 1) -> true;
+is_bif(erlang, integer_to_list, 1) -> true;
+is_bif(erlang, is_alive, 0) -> true;
+is_bif(erlang, is_atom, 1) -> true;
+is_bif(erlang, is_binary, 1) -> true;
+is_bif(erlang, is_boolean, 1) -> true;
+is_bif(erlang, is_builtin, 3) -> true;
+is_bif(erlang, is_constant, 1) -> true;
+is_bif(erlang, is_float, 1) -> true;
+is_bif(erlang, is_function, 1) -> true;
+is_bif(erlang, is_integer, 1) -> true;
+is_bif(erlang, is_list, 1) -> true;
+is_bif(erlang, is_number, 1) -> true;
+is_bif(erlang, is_pid, 1) -> true;
+is_bif(erlang, is_port, 1) -> true;
+is_bif(erlang, is_process_alive, 1) -> true;
+is_bif(erlang, is_record, 3) -> true;
+is_bif(erlang, is_reference, 1) -> true;
+is_bif(erlang, is_tuple, 1) -> true;
+is_bif(erlang, length, 1) -> true;
+is_bif(erlang, link, 1) -> true;
+is_bif(erlang, list_to_atom, 1) -> true;
+is_bif(erlang, list_to_binary, 1) -> true;
+is_bif(erlang, list_to_float, 1) -> true;
+is_bif(erlang, list_to_integer, 1) -> true;
+is_bif(erlang, list_to_pid, 1) -> true;
+is_bif(erlang, list_to_tuple, 1) -> true;
+is_bif(erlang, loaded, 0) -> true;
+is_bif(erlang, localtime, 0) -> true;
+is_bif(erlang, localtime_to_universaltime, 1) -> true;
+is_bif(erlang, make_ref, 0) -> true;
+is_bif(erlang, make_tuple, 2) -> true;
+is_bif(erlang, md5, 1) -> true;
+is_bif(erlang, md5_final, 1) -> true;
+is_bif(erlang, md5_init, 0) -> true;
+is_bif(erlang, md5_update, 2) -> true;
+is_bif(erlang, monitor, 2) -> true;
+is_bif(erlang, monitor_node, 2) -> true;
+is_bif(erlang, node, 0) -> true;
+is_bif(erlang, node, 1) -> true;
+is_bif(erlang, nodes, 0) -> true;
+is_bif(erlang, now, 0) -> true;
+is_bif(erlang, open_port, 2) -> true;
+is_bif(erlang, phash, 2) -> true;
+is_bif(erlang, pid_to_list, 1) -> true;
+is_bif(erlang, port_close, 2) -> true;
+is_bif(erlang, port_command, 2) -> true;
+is_bif(erlang, port_connect, 2) -> true;
+is_bif(erlang, port_control, 3) -> true;
+is_bif(erlang, port_info, 2) -> true;
+is_bif(erlang, port_to_list, 1) -> true;
+is_bif(erlang, ports, 0) -> true;
+is_bif(erlang, pre_loaded, 0) -> true;
+is_bif(erlang, process_display, 2) -> true;
+is_bif(erlang, process_flag, 2) -> true;
+is_bif(erlang, process_flag, 3) -> true;
+is_bif(erlang, process_info, 1) -> true;
+is_bif(erlang, process_info, 2) -> true;
+is_bif(erlang, processes, 0) -> true;
+is_bif(erlang, put, 2) -> true;
+is_bif(erlang, read_timer, 1) -> true;
+is_bif(erlang, ref_to_list, 1) -> true;
+is_bif(erlang, register, 2) -> true;
+is_bif(erlang, registered, 0) -> true;
+is_bif(erlang, resume_process, 1) -> true;
+is_bif(erlang, round, 1) -> true;
+is_bif(erlang, self, 0) -> true;
+is_bif(erlang, send_after, 3) -> true;
+is_bif(erlang, set_cookie, 2) -> true;
+is_bif(erlang, setelement, 3) -> true;
+is_bif(erlang, size, 1) -> true;
+is_bif(erlang, spawn, 1) -> true;
+is_bif(erlang, spawn, 2) -> true;
+is_bif(erlang, spawn, 3) -> true;
+is_bif(erlang, spawn, 4) -> true;
+is_bif(erlang, spawn_link, 1) -> true;
+is_bif(erlang, spawn_link, 2) -> true;
+is_bif(erlang, spawn_link, 3) -> true;
+is_bif(erlang, spawn_link, 4) -> true;
+is_bif(erlang, spawn_opt, 4) -> true;
+is_bif(erlang, split_binary, 2) -> true;
+is_bif(erlang, start_timer, 3) -> true;
+is_bif(erlang, statistics, 1) -> true;
+is_bif(erlang, suspend_process, 1) -> true;
+is_bif(erlang, system_flag, 2) -> true;
+is_bif(erlang, system_info, 1) -> true;
+is_bif(erlang, term_to_binary, 1) -> true;
+is_bif(erlang, term_to_binary, 2) -> true;
+is_bif(erlang, throw, 1) -> true;
+is_bif(erlang, time, 0) -> true;
+is_bif(erlang, tl, 1) -> true;
+is_bif(erlang, trace, 3) -> true;
+is_bif(erlang, trace_info, 2) -> true;
+is_bif(erlang, trace_pattern, 2) -> true;
+is_bif(erlang, trace_pattern, 3) -> true;
+is_bif(erlang, trunc, 1) -> true;
+is_bif(erlang, tuple_to_list, 1) -> true;
+is_bif(erlang, universaltime, 0) -> true;
+is_bif(erlang, universaltime_to_localtime, 1) -> true;
+is_bif(erlang, unlink, 1) -> true;
+is_bif(erlang, unregister, 1) -> true;
+is_bif(erlang, whereis, 1) -> true;
+is_bif(erlang, yield, 0) -> true;
+is_bif(lists, append, 2) -> true;
+is_bif(lists, reverse, 1) -> true;
+is_bif(lists, reverse, 2) -> true;
+is_bif(lists, subtract, 2) -> true;
+is_bif(math, acos, 1) -> true;
+is_bif(math, acosh, 1) -> true;
+is_bif(math, asin, 1) -> true;
+is_bif(math, asinh, 1) -> true;
+is_bif(math, atan, 1) -> true;
+is_bif(math, atan2, 2) -> true;
+is_bif(math, atanh, 1) -> true;
+is_bif(math, cos, 1) -> true;
+is_bif(math, cosh, 1) -> true;
+is_bif(math, erf, 1) -> true;
+is_bif(math, erfc, 1) -> true;
+is_bif(math, exp, 1) -> true;
+is_bif(math, log, 1) -> true;
+is_bif(math, log10, 1) -> true;
+is_bif(math, pow, 2) -> true;
+is_bif(math, sin, 1) -> true;
+is_bif(math, sinh, 1) -> true;
+is_bif(math, sqrt, 1) -> true;
+is_bif(math, tan, 1) -> true;
+is_bif(math, tanh, 1) -> true;
+is_bif(_, _, _) -> false.
+
+
+%% =====================================================================
+%% is_guard_bif(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the built-in function `Module:Name/Arity' may
+%% be called from a clause guard. Note that such "guard BIFs" are
+%% not necessarily "pure", since some (notably `erlang:self/0') may
+%% depend on the current state, nor "safe", since many guard BIFs
+%% can fail. Also note that even a "pure" function could be
+%% unsuitable for calling from a guard because of its time or space
+%% complexity.
+
+is_guard_bif(erlang, '*', 2) -> true;
+is_guard_bif(erlang, '+', 1) -> true;
+is_guard_bif(erlang, '+', 2) -> true;
+is_guard_bif(erlang, '-', 1) -> true;
+is_guard_bif(erlang, '-', 2) -> true;
+is_guard_bif(erlang, '/', 2) -> true;
+is_guard_bif(erlang, '/=', 2) -> true;
+is_guard_bif(erlang, '<', 2) -> true;
+is_guard_bif(erlang, '=/=', 2) -> true;
+is_guard_bif(erlang, '=:=', 2) -> true;
+is_guard_bif(erlang, '=<', 2) -> true;
+is_guard_bif(erlang, '==', 2) -> true;
+is_guard_bif(erlang, '>', 2) -> true;
+is_guard_bif(erlang, '>=', 2) -> true;
+is_guard_bif(erlang, 'and', 2) -> true;
+is_guard_bif(erlang, 'band', 2) -> true;
+is_guard_bif(erlang, 'bnot', 1) -> true;
+is_guard_bif(erlang, 'bor', 2) -> true;
+is_guard_bif(erlang, 'bsl', 2) -> true;
+is_guard_bif(erlang, 'bsr', 2) -> true;
+is_guard_bif(erlang, 'bxor', 2) -> true;
+is_guard_bif(erlang, 'div', 2) -> true;
+is_guard_bif(erlang, 'not', 1) -> true;
+is_guard_bif(erlang, 'or', 2) -> true;
+is_guard_bif(erlang, 'rem', 2) -> true;
+is_guard_bif(erlang, 'xor', 2) -> true;
+is_guard_bif(erlang, abs, 1) -> true;
+is_guard_bif(erlang, element, 2) -> true;
+is_guard_bif(erlang, error, 1) -> true; % unorthodox
+is_guard_bif(erlang, exit, 1) -> true; % unorthodox
+is_guard_bif(erlang, fault, 1) -> true; % unorthodox
+is_guard_bif(erlang, float, 1) -> true; % (the type coercion function)
+is_guard_bif(erlang, hd, 1) -> true;
+is_guard_bif(erlang, is_atom, 1) -> true;
+is_guard_bif(erlang, is_boolean, 1) -> true;
+is_guard_bif(erlang, is_binary, 1) -> true;
+is_guard_bif(erlang, is_constant, 1) -> true;
+is_guard_bif(erlang, is_float, 1) -> true;
+is_guard_bif(erlang, is_function, 1) -> true;
+is_guard_bif(erlang, is_integer, 1) -> true;
+is_guard_bif(erlang, is_list, 1) -> true;
+is_guard_bif(erlang, is_number, 1) -> true;
+is_guard_bif(erlang, is_pid, 1) -> true;
+is_guard_bif(erlang, is_port, 1) -> true;
+is_guard_bif(erlang, is_reference, 1) -> true;
+is_guard_bif(erlang, is_tuple, 1) -> true;
+is_guard_bif(erlang, length, 1) -> true;
+is_guard_bif(erlang, list_to_atom, 1) -> true; % unorthodox
+is_guard_bif(erlang, node, 0) -> true; % (not pure)
+is_guard_bif(erlang, node, 1) -> true; % (not pure)
+is_guard_bif(erlang, round, 1) -> true;
+is_guard_bif(erlang, self, 0) -> true; % (not pure)
+is_guard_bif(erlang, size, 1) -> true;
+is_guard_bif(erlang, throw, 1) -> true; % unorthodox
+is_guard_bif(erlang, tl, 1) -> true;
+is_guard_bif(erlang, trunc, 1) -> true;
+is_guard_bif(math, acos, 1) -> true; % unorthodox
+is_guard_bif(math, acosh, 1) -> true; % unorthodox
+is_guard_bif(math, asin, 1) -> true; % unorthodox
+is_guard_bif(math, asinh, 1) -> true; % unorthodox
+is_guard_bif(math, atan, 1) -> true; % unorthodox
+is_guard_bif(math, atan2, 2) -> true; % unorthodox
+is_guard_bif(math, atanh, 1) -> true; % unorthodox
+is_guard_bif(math, cos, 1) -> true; % unorthodox
+is_guard_bif(math, cosh, 1) -> true; % unorthodox
+is_guard_bif(math, erf, 1) -> true; % unorthodox
+is_guard_bif(math, erfc, 1) -> true; % unorthodox
+is_guard_bif(math, exp, 1) -> true; % unorthodox
+is_guard_bif(math, log, 1) -> true; % unorthodox
+is_guard_bif(math, log10, 1) -> true; % unorthodox
+is_guard_bif(math, pow, 2) -> true; % unorthodox
+is_guard_bif(math, sin, 1) -> true; % unorthodox
+is_guard_bif(math, sinh, 1) -> true; % unorthodox
+is_guard_bif(math, sqrt, 1) -> true; % unorthodox
+is_guard_bif(math, tan, 1) -> true; % unorthodox
+is_guard_bif(math, tanh, 1) -> true; % unorthodox
+is_guard_bif(_, _, _) -> false.
+
+
+%% =====================================================================
+%% is_pure(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the function `Module:Name/Arity' does not
+%% affect the state, nor depend on the state, although its
+%% evaluation is not guaranteed to complete normally for all input.
+
+is_pure(erlang, '*', 2) -> true;
+is_pure(erlang, '+', 1) -> true; % (even for non-numbers)
+is_pure(erlang, '+', 2) -> true;
+is_pure(erlang, '++', 2) -> true;
+is_pure(erlang, '-', 1) -> true;
+is_pure(erlang, '-', 2) -> true;
+is_pure(erlang, '--', 2) -> true;
+is_pure(erlang, '/', 2) -> true;
+is_pure(erlang, '/=', 2) -> true;
+is_pure(erlang, '<', 2) -> true;
+is_pure(erlang, '=/=', 2) -> true;
+is_pure(erlang, '=:=', 2) -> true;
+is_pure(erlang, '=<', 2) -> true;
+is_pure(erlang, '==', 2) -> true;
+is_pure(erlang, '>', 2) -> true;
+is_pure(erlang, '>=', 2) -> true;
+is_pure(erlang, 'and', 2) -> true;
+is_pure(erlang, 'band', 2) -> true;
+is_pure(erlang, 'bnot', 1) -> true;
+is_pure(erlang, 'bor', 2) -> true;
+is_pure(erlang, 'bsl', 2) -> true;
+is_pure(erlang, 'bsr', 2) -> true;
+is_pure(erlang, 'bxor', 2) -> true;
+is_pure(erlang, 'div', 2) -> true;
+is_pure(erlang, 'not', 1) -> true;
+is_pure(erlang, 'or', 2) -> true;
+is_pure(erlang, 'rem', 2) -> true;
+is_pure(erlang, 'xor', 2) -> true;
+is_pure(erlang, abs, 1) -> true;
+is_pure(erlang, atom_to_list, 1) -> true;
+is_pure(erlang, binary_to_list, 1) -> true;
+is_pure(erlang, binary_to_list, 3) -> true;
+is_pure(erlang, concat_binary, 1) -> true;
+is_pure(erlang, element, 2) -> true;
+is_pure(erlang, float, 1) -> true;
+is_pure(erlang, float_to_list, 1) -> true;
+is_pure(erlang, hash, 2) -> false;
+is_pure(erlang, hd, 1) -> true;
+is_pure(erlang, integer_to_list, 1) -> true;
+is_pure(erlang, is_atom, 1) -> true;
+is_pure(erlang, is_boolean, 1) -> true;
+is_pure(erlang, is_binary, 1) -> true;
+is_pure(erlang, is_builtin, 3) -> true;
+is_pure(erlang, is_constant, 1) -> true;
+is_pure(erlang, is_float, 1) -> true;
+is_pure(erlang, is_function, 1) -> true;
+is_pure(erlang, is_integer, 1) -> true;
+is_pure(erlang, is_list, 1) -> true;
+is_pure(erlang, is_number, 1) -> true;
+is_pure(erlang, is_pid, 1) -> true;
+is_pure(erlang, is_port, 1) -> true;
+is_pure(erlang, is_record, 3) -> true;
+is_pure(erlang, is_reference, 1) -> true;
+is_pure(erlang, is_tuple, 1) -> true;
+is_pure(erlang, length, 1) -> true;
+is_pure(erlang, list_to_atom, 1) -> true;
+is_pure(erlang, list_to_binary, 1) -> true;
+is_pure(erlang, list_to_float, 1) -> true;
+is_pure(erlang, list_to_integer, 1) -> true;
+is_pure(erlang, list_to_pid, 1) -> true;
+is_pure(erlang, list_to_tuple, 1) -> true;
+is_pure(erlang, phash, 2) -> false;
+is_pure(erlang, pid_to_list, 1) -> true;
+is_pure(erlang, round, 1) -> true;
+is_pure(erlang, setelement, 3) -> true;
+is_pure(erlang, size, 1) -> true;
+is_pure(erlang, split_binary, 2) -> true;
+is_pure(erlang, term_to_binary, 1) -> true;
+is_pure(erlang, tl, 1) -> true;
+is_pure(erlang, trunc, 1) -> true;
+is_pure(erlang, tuple_to_list, 1) -> true;
+is_pure(lists, append, 2) -> true;
+is_pure(lists, subtract, 2) -> true;
+is_pure(math, acos, 1) -> true;
+is_pure(math, acosh, 1) -> true;
+is_pure(math, asin, 1) -> true;
+is_pure(math, asinh, 1) -> true;
+is_pure(math, atan, 1) -> true;
+is_pure(math, atan2, 2) -> true;
+is_pure(math, atanh, 1) -> true;
+is_pure(math, cos, 1) -> true;
+is_pure(math, cosh, 1) -> true;
+is_pure(math, erf, 1) -> true;
+is_pure(math, erfc, 1) -> true;
+is_pure(math, exp, 1) -> true;
+is_pure(math, log, 1) -> true;
+is_pure(math, log10, 1) -> true;
+is_pure(math, pow, 2) -> true;
+is_pure(math, sin, 1) -> true;
+is_pure(math, sinh, 1) -> true;
+is_pure(math, sqrt, 1) -> true;
+is_pure(math, tan, 1) -> true;
+is_pure(math, tanh, 1) -> true;
+is_pure(_, _, _) -> false.
+
+
+%% =====================================================================
+%% is_safe(Module, Name, Arity) -> boolean()
+%%
+%% Module = Name = atom()
+%% Arity = integer()
+%%
+%% Returns `true' if the function `Module:Name/Arity' is completely
+%% effect free, i.e., if its evaluation always completes normally
+%% and does not affect the state (although the value it returns
+%% might depend on the state).
+
+is_safe(erlang, '/=', 2) -> true;
+is_safe(erlang, '<', 2) -> true;
+is_safe(erlang, '=/=', 2) -> true;
+is_safe(erlang, '=:=', 2) -> true;
+is_safe(erlang, '=<', 2) -> true;
+is_safe(erlang, '==', 2) -> true;
+is_safe(erlang, '>', 2) -> true;
+is_safe(erlang, '>=', 2) -> true;
+is_safe(erlang, date, 0) -> true;
+is_safe(erlang, get, 0) -> true;
+is_safe(erlang, get, 1) -> true;
+is_safe(erlang, get_cookie, 0) -> true;
+is_safe(erlang, get_keys, 1) -> true;
+is_safe(erlang, group_leader, 0) -> true;
+is_safe(erlang, is_alive, 0) -> true;
+is_safe(erlang, is_atom, 1) -> true;
+is_safe(erlang, is_boolean, 1) -> true;
+is_safe(erlang, is_binary, 1) -> true;
+is_safe(erlang, is_constant, 1) -> true;
+is_safe(erlang, is_float, 1) -> true;
+is_safe(erlang, is_function, 1) -> true;
+is_safe(erlang, is_integer, 1) -> true;
+is_safe(erlang, is_list, 1) -> true;
+is_safe(erlang, is_number, 1) -> true;
+is_safe(erlang, is_pid, 1) -> true;
+is_safe(erlang, is_port, 1) -> true;
+is_safe(erlang, is_record, 3) -> true;
+is_safe(erlang, is_reference, 1) -> true;
+is_safe(erlang, is_tuple, 1) -> true;
+is_safe(erlang, make_ref, 0) -> true;
+is_safe(erlang, node, 0) -> true;
+is_safe(erlang, nodes, 0) -> true;
+is_safe(erlang, ports, 0) -> true;
+is_safe(erlang, pre_loaded, 0) -> true;
+is_safe(erlang, processes, 0) -> true;
+is_safe(erlang, registered, 0) -> true;
+is_safe(erlang, self, 0) -> true;
+is_safe(erlang, term_to_binary, 1) -> true;
+is_safe(erlang, time, 0) -> true;
+is_safe(_, _, _) -> false.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl
new file mode 100644
index 0000000000..0dd31b71ea
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/rec_env.erl
@@ -0,0 +1,611 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: rec_env.erl,v 1.2 2009/09/17 09:46:19 kostis Exp $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 1999-2004 Richard Carlsson
+%% @doc Abstract environments, supporting self-referential bindings and
+%% automatic new-key generation.
+
+%% The current implementation is based on Erlang standard library
+%% dictionaries.
+
+%%% -define(DEBUG, true).
+
+-module(rec_env).
+
+-export([bind/3, bind_list/3, bind_recursive/4, delete/2, empty/0,
+ get/2, is_defined/2, is_empty/1, keys/1, lookup/2, new_key/1,
+ new_key/2, new_keys/2, new_keys/3, size/1, to_list/1]).
+
+-ifdef(DEBUG).
+-export([test/1, test_custom/1, test_custom/2]).
+-endif.
+
+-ifdef(DEBUG).
+%% Code for testing:
+%%@hidden
+test(N) ->
+ test_0(integer, N).
+
+%%@hidden
+test_custom(N) ->
+ F = fun (X) -> list_to_atom("X"++integer_to_list(X)) end,
+ test_custom(F, N).
+
+%%@hidden
+test_custom(F, N) ->
+ test_0({custom, F}, N).
+
+test_0(Type, N) ->
+ put(new_key_calls, 0),
+ put(new_key_retries, 0),
+ put(new_key_max, 0),
+ Env = test_1(Type, N, empty()),
+ io:fwrite("\ncalls: ~w.\n", [get(new_key_calls)]),
+ io:fwrite("\nretries: ~w.\n", [get(new_key_retries)]),
+ io:fwrite("\nmax: ~w.\n", [get(new_key_max)]),
+ dict:to_list(element(1,Env)).
+
+test_1(integer = Type, N, Env) when integer(N), N > 0 ->
+ Key = new_key(Env),
+ test_1(Type, N - 1, bind(Key, value, Env));
+test_1({custom, F} = Type, N, Env) when integer(N), N > 0 ->
+ Key = new_key(F, Env),
+ test_1(Type, N - 1, bind(Key, value, Env));
+test_1(_,0, Env) ->
+ Env.
+-endif.
+
+
+%% Representation:
+%%
+%% environment() = [Mapping]
+%%
+%% Mapping = {map, Dict} | {rec, Dict, Dict}
+%% Dict = dict:dictionary()
+%%
+%% An empty environment is a list containing a single `{map, Dict}'
+%% element - empty lists are not valid environments. To find a key in an
+%% environment, it is searched for in each mapping in the list, in
+%% order, until it the key is found in some mapping, or the end of the
+%% list is reached. In a 'rec' mapping, we keep the original dictionary
+%% together with a version where entries may have been deleted - this
+%% makes it possible to garbage collect the entire 'rec' mapping when
+%% all its entries are unused (for example, by being shadowed by later
+%% definitions).
+
+
+
+%% =====================================================================
+%% @type environment(). An abstract environment.
+
+
+%% =====================================================================
+%% @spec empty() -> environment()
+%%
+%% @doc Returns an empty environment.
+
+empty() ->
+ [{map, dict:new()}].
+
+
+%% =====================================================================
+%% @spec is_empty(Env::environment()) -> boolean()
+%%
+%% @doc Returns <code>true</code> if the environment is empty, otherwise
+%% <code>false</code>.
+
+is_empty([{map, Dict} | Es]) ->
+ N = dict:size(Dict),
+ if N /= 0 -> false;
+ Es == [] -> true;
+ true -> is_empty(Es)
+ end;
+is_empty([{rec, Dict, _} | Es]) ->
+ N = dict:size(Dict),
+ if N /= 0 -> false;
+ Es == [] -> true;
+ true -> is_empty(Es)
+ end.
+
+
+%% =====================================================================
+%% @spec size(Env::environment()) -> integer()
+%%
+%% @doc Returns the number of entries in an environment.
+
+%% (The name 'size' cannot be used in local calls, since there exists a
+%% built-in function with the same name.)
+
+size(Env) ->
+ env_size(Env).
+
+env_size([{map, Dict}]) ->
+ dict:size(Dict);
+env_size([{map, Dict} | Env]) ->
+ dict:size(Dict) + env_size(Env);
+env_size([{rec, Dict, _Dict0} | Env]) ->
+ dict:size(Dict) + env_size(Env).
+
+
+%% =====================================================================
+%% @spec is_defined(Key, Env) -> boolean()
+%%
+%% Key = term()
+%% Env = environment()
+%%
+%% @doc Returns <code>true</code> if <code>Key</code> is bound in the
+%% environment, otherwise <code>false</code>.
+
+is_defined(Key, [{map, Dict} | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ true;
+ false when Env == [] ->
+ false;
+ false ->
+ is_defined(Key, Env)
+ end;
+is_defined(Key, [{rec, Dict, _Dict0} | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ true;
+ false ->
+ is_defined(Key, Env)
+ end.
+
+
+%% =====================================================================
+%% @spec keys(Env::environment()) -> [term()]
+%%
+%% @doc Returns the ordered list of all keys in the environment.
+
+keys(Env) ->
+ lists:sort(keys(Env, [])).
+
+keys([{map, Dict}], S) ->
+ dict:fetch_keys(Dict) ++ S;
+keys([{map, Dict} | Env], S) ->
+ keys(Env, dict:fetch_keys(Dict) ++ S);
+keys([{rec, Dict, _Dict0} | Env], S) ->
+ keys(Env, dict:fetch_keys(Dict) ++ S).
+
+
+%% =====================================================================
+%% @spec to_list(Env) -> [{Key, Value}]
+%%
+%% Env = environment()
+%% Key = term()
+%% Value = term()
+%%
+%% @doc Returns an ordered list of <code>{Key, Value}</code> pairs for
+%% all keys in <code>Env</code>. <code>Value</code> is the same as that
+%% returned by {@link get/2}.
+
+to_list(Env) ->
+ lists:sort(to_list(Env, [])).
+
+to_list([{map, Dict}], S) ->
+ dict:to_list(Dict) ++ S;
+to_list([{map, Dict} | Env], S) ->
+ to_list(Env, dict:to_list(Dict) ++ S);
+to_list([{rec, Dict, _Dict0} | Env], S) ->
+ to_list(Env, dict:to_list(Dict) ++ S).
+
+
+%% =====================================================================
+%% @spec bind(Key, Value, Env) -> environment()
+%%
+%% Key = term()
+%% Value = term()
+%% Env = environment()
+%%
+%% @doc Make a nonrecursive entry. This binds <code>Key</code> to
+%% <code>Value</code>. If the key already existed in the environment,
+%% the old entry is replaced.
+
+%% Note that deletion is done to free old bindings so they can be
+%% garbage collected.
+
+bind(Key, Value, [{map, Dict}]) ->
+ [{map, dict:store(Key, Value, Dict)}];
+bind(Key, Value, [{map, Dict} | Env]) ->
+ [{map, dict:store(Key, Value, Dict)} | delete_any(Key, Env)];
+bind(Key, Value, Env) ->
+ [{map, dict:store(Key, Value, dict:new())} | delete_any(Key, Env)].
+
+
+%% =====================================================================
+%% @spec bind_list(Keys, Values, Env) -> environment()
+%%
+%% Keys = [term()]
+%% Values = [term()]
+%% Env = environment()
+%%
+%% @doc Make N nonrecursive entries. This binds each key in
+%% <code>Keys</code> to the corresponding value in
+%% <code>Values</code>. If some key already existed in the environment,
+%% the previous entry is replaced. If <code>Keys</code> does not have
+%% the same length as <code>Values</code>, an exception is generated.
+
+bind_list(Ks, Vs, [{map, Dict}]) ->
+ [{map, store_list(Ks, Vs, Dict)}];
+bind_list(Ks, Vs, [{map, Dict} | Env]) ->
+ [{map, store_list(Ks, Vs, Dict)} | delete_list(Ks, Env)];
+bind_list(Ks, Vs, Env) ->
+ [{map, store_list(Ks, Vs, dict:new())} | delete_list(Ks, Env)].
+
+store_list([K | Ks], [V | Vs], Dict) ->
+ store_list(Ks, Vs, dict:store(K, V, Dict));
+store_list([], _, Dict) ->
+ Dict.
+
+delete_list([K | Ks], Env) ->
+ delete_list(Ks, delete_any(K, Env));
+delete_list([], Env) ->
+ Env.
+
+%% By not calling `delete' unless we have to, we avoid unnecessary
+%% rewriting of the data.
+
+delete_any(Key, Env) ->
+ case is_defined(Key, Env) of
+ true ->
+ delete(Key, Env);
+ false ->
+ Env
+ end.
+
+%% =====================================================================
+%% @spec delete(Key, Env) -> environment()
+%%
+%% Key = term()
+%% Env = environment()
+%%
+%% @doc Delete an entry. This removes <code>Key</code> from the
+%% environment.
+
+delete(Key, [{map, Dict} = E | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ [{map, dict:erase(Key, Dict)} | Env];
+ false ->
+ delete_1(Key, Env, E)
+ end;
+delete(Key, [{rec, Dict, Dict0} = E | Env]) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ %% The Dict0 component must be preserved as it is until all
+ %% keys in Dict have been deleted.
+ Dict1 = dict:erase(Key, Dict),
+ case dict:size(Dict1) of
+ 0 ->
+ Env; % the whole {rec,...} is now garbage
+ _ ->
+ [{rec, Dict1, Dict0} | Env]
+ end;
+ false ->
+ [E | delete(Key, Env)]
+ end.
+
+%% This is just like above, except we pass on the preceding 'map'
+%% mapping in the list to enable merging when removing 'rec' mappings.
+
+delete_1(Key, [{rec, Dict, Dict0} = E | Env], E1) ->
+ case dict:is_key(Key, Dict) of
+ true ->
+ Dict1 = dict:erase(Key, Dict),
+ case dict:size(Dict1) of
+ 0 ->
+ concat(E1, Env);
+ _ ->
+ [E1, {rec, Dict1, Dict0} | Env]
+ end;
+ false ->
+ [E1, E | delete(Key, Env)]
+ end.
+
+concat({map, D1}, [{map, D2} | Env]) ->
+ [dict:merge(fun (_K, V1, _V2) -> V1 end, D1, D2) | Env];
+concat(E1, Env) ->
+ [E1 | Env].
+
+
+%% =====================================================================
+%% @spec bind_recursive(Keys, Values, Fun, Env) -> NewEnv
+%%
+%% Keys = [term()]
+%% Values = [term()]
+%% Fun = (Value, Env) -> term()
+%% Env = environment()
+%% NewEnv = environment()
+%%
+%% @doc Make N recursive entries. This binds each key in
+%% <code>Keys</code> to the value of <code>Fun(Value, NewEnv)</code> for
+%% the corresponding <code>Value</code>. If <code>Keys</code> does not
+%% have the same length as <code>Values</code>, an exception is
+%% generated. If some key already existed in the environment, the old
+%% entry is replaced.
+%%
+%% <p>Note: the function <code>Fun</code> is evaluated each time one of
+%% the stored keys is looked up, but only then.</p>
+%%
+%% <p>Examples:
+%%<pre>
+%% NewEnv = bind_recursive([foo, bar], [1, 2],
+%% fun (V, E) -> V end,
+%% Env)</pre>
+%%
+%% This does nothing interesting; <code>get(foo, NewEnv)</code> yields
+%% <code>1</code> and <code>get(bar, NewEnv)</code> yields
+%% <code>2</code>, but there is more overhead than if the {@link
+%% bind_list/3} function had been used.
+%%
+%% <pre>
+%% NewEnv = bind_recursive([foo, bar], [1, 2],
+%% fun (V, E) -> {V, E} end,
+%% Env)</pre>
+%%
+%% Here, however, <code>get(foo, NewEnv)</code> will yield <code>{1,
+%% NewEnv}</code> and <code>get(bar, NewEnv)</code> will yield <code>{2,
+%% NewEnv}</code>, i.e., the environment <code>NewEnv</code> contains
+%% recursive bindings.</p>
+
+bind_recursive([], [], _, Env) ->
+ Env;
+bind_recursive(Ks, Vs, F, Env) ->
+ F1 = fun (V) ->
+ fun (Dict) -> F(V, [{rec, Dict, Dict} | Env]) end
+ end,
+ Dict = bind_recursive_1(Ks, Vs, F1, dict:new()),
+ [{rec, Dict, Dict} | Env].
+
+bind_recursive_1([K | Ks], [V | Vs], F, Dict) ->
+ bind_recursive_1(Ks, Vs, F, dict:store(K, F(V), Dict));
+bind_recursive_1([], [], _, Dict) ->
+ Dict.
+
+
+%% =====================================================================
+%% @spec lookup(Key, Env) -> error | {ok, Value}
+%%
+%% Key = term()
+%% Env = environment()
+%% Value = term()
+%%
+%% @doc Returns <code>{ok, Value}</code> if <code>Key</code> is bound to
+%% <code>Value</code> in <code>Env</code>, and <code>error</code>
+%% otherwise.
+
+lookup(Key, [{map, Dict} | Env]) ->
+ case dict:find(Key, Dict) of
+ {ok, _}=Value ->
+ Value;
+ error when Env == [] ->
+ error;
+ error ->
+ lookup(Key, Env)
+ end;
+lookup(Key, [{rec, Dict, Dict0} | Env]) ->
+ case dict:find(Key, Dict) of
+ {ok, F} ->
+ {ok, F(Dict0)};
+ error ->
+ lookup(Key, Env)
+ end.
+
+
+%% =====================================================================
+%% @spec get(Key, Env) -> Value
+%%
+%% Key = term()
+%% Env = environment()
+%% Value = term()
+%%
+%% @doc Returns the value that <code>Key</code> is bound to in
+%% <code>Env</code>. Throws <code>{undefined, Key}</code> if the key
+%% does not exist in <code>Env</code>.
+
+get(Key, Env) ->
+ case lookup(Key, Env) of
+ {ok, Value} -> Value;
+ error -> throw({undefined, Key})
+ end.
+
+
+%% =====================================================================
+%% The key-generating algorithm could possibly be further improved. The
+%% important thing to keep in mind is, that when we need a new key, we
+%% are generally in mid-traversal of a syntax tree, and existing names
+%% in the tree may be closely grouped and evenly distributed or even
+%% forming a compact range (often having been generated by a "gensym",
+%% or by this very algorithm itself). This means that if we generate an
+%% identifier whose value is too close to those already seen (i.e.,
+%% which are in the environment), it is very probable that we will
+%% shadow a not-yet-seen identifier further down in the tree, the result
+%% being that we induce another later renaming, and end up renaming most
+%% of the identifiers, completely contrary to our intention. We need to
+%% generate new identifiers in a way that avoids such systematic
+%% collisions.
+%%
+%% One way of getting a new key to try when the previous attempt failed
+%% is of course to e.g. add one to the last tried value. However, in
+%% general it's a bad idea to try adjacent identifiers: the percentage
+%% of retries will typically increase a lot, so you may lose big on the
+%% extra lookups while gaining only a little from the quicker
+%% computation.
+%%
+%% We want an initial range that is large enough for most typical cases.
+%% If we start with, say, a range of 10, we might quickly use up most of
+%% the values in the range 1-10 (or 1-100) for new top-level variables -
+%% but as we start traversing the syntax tree, it is quite likely that
+%% exactly those variables will be encountered again (this depends on
+%% how the names in the tree were created), and will then need to be
+%% renamed. If we instead begin with a larger range, it is less likely
+%% that any top-level names that we introduce will shadow names that we
+%% will find in the tree. Of course we cannot know how large is large
+%% enough: for any initial range, there is some syntax tree that uses
+%% all the values in that range, and thus any top-level names introduced
+%% will shadow names in the tree. The point is to avoid this happening
+%% all the time - a range of about 1000 seems enough for most programs.
+%%
+%% The following values have been shown to work well:
+
+-define(MINIMUM_RANGE, 1000).
+-define(START_RANGE_FACTOR, 50).
+-define(MAX_RETRIES, 2). % retries before enlarging range
+-define(ENLARGE_FACTOR, 10). % range enlargment factor
+
+-ifdef(DEBUG).
+%% If you want to use these process dictionary counters, make sure to
+%% initialise them to zero before you call any of the key-generating
+%% functions.
+%%
+%% new_key_calls total number of calls
+%% new_key_retries failed key generation attempts
+%% new_key_max maximum generated integer value
+%%
+-define(measure_calls(),
+ put(new_key_calls, 1 + get(new_key_calls))).
+-define(measure_max_key(N),
+ case N > get(new_key_max) of
+ true ->
+ put(new_key_max, N);
+ false ->
+ ok
+ end).
+-define(measure_retries(N),
+ put(new_key_retries, get(new_key_retries) + N)).
+-else.
+-define(measure_calls(), ok).
+-define(measure_max_key(N), ok).
+-define(measure_retries(N), ok).
+-endif.
+
+
+%% =====================================================================
+%% @spec new_key(Env::environment()) -> integer()
+%%
+%% @doc Returns an integer which is not already used as key in the
+%% environment. New integers are generated using an algorithm which
+%% tries to keep the values randomly distributed within a reasonably
+%% small range relative to the number of entries in the environment.
+%%
+%% <p>This function uses the Erlang standard library module
+%% <code>random</code> to generate new keys.</p>
+%%
+%% <p>Note that only the new key is returned; the environment itself is
+%% not updated by this function.</p>
+
+new_key(Env) ->
+ new_key(fun (X) -> X end, Env).
+
+
+%% =====================================================================
+%% @spec new_key(Function, Env) -> term()
+%%
+%% Function = (integer()) -> term()
+%% Env = environment()
+%%
+%% @doc Returns a term which is not already used as key in the
+%% environment. The term is generated by applying <code>Function</code>
+%% to an integer generated as in {@link new_key/1}.
+%%
+%% <p>Note that only the generated term is returned; the environment
+%% itself is not updated by this function.</p>
+
+new_key(F, Env) ->
+ ?measure_calls(),
+ R = start_range(Env),
+%%% io:fwrite("Start range: ~w.\n", [R]),
+ new_key(R, F, Env).
+
+new_key(R, F, Env) ->
+ new_key(generate(R, R), R, 0, F, Env).
+
+new_key(N, R, T, F, Env) when T < ?MAX_RETRIES ->
+ A = F(N),
+ case is_defined(A, Env) of
+ true ->
+%%% io:fwrite("CLASH: ~w.\n", [A]),
+ new_key(generate(N, R), R, T + 1, F, Env);
+ false ->
+ ?measure_max_key(N),
+ ?measure_retries(T),
+%%% io:fwrite("New: ~w.\n", [N]),
+ A
+ end;
+new_key(N, R, _T, F, Env) ->
+ %% Too many retries - enlarge the range and start over.
+ ?measure_retries((_T + 1)),
+ R1 = trunc(R * ?ENLARGE_FACTOR),
+%%% io:fwrite("**NEW RANGE**: ~w.\n", [R1]),
+ new_key(generate(N, R1), R1, 0, F, Env).
+
+start_range(Env) ->
+ max(env_size(Env) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
+
+max(X, Y) when X > Y -> X;
+max(_, Y) -> Y.
+
+%% The previous key might or might not be used to compute the next key
+%% to be tried. It is currently not used.
+%%
+%% In order to avoid causing cascading renamings, it is important that
+%% this function does not generate values in order, but
+%% (pseudo-)randomly distributed over the range.
+
+generate(_N, Range) ->
+ random:uniform(Range). % works well
+
+
+%% =====================================================================
+%% @spec new_keys(N, Env) -> [integer()]
+%%
+%% N = integer()
+%% Env = environment()
+%%
+%% @doc Returns a list of <code>N</code> distinct integers that are not
+%% already used as keys in the environment. See {@link new_key/1} for
+%% details.
+
+new_keys(N, Env) when integer(N) ->
+ new_keys(N, fun (X) -> X end, Env).
+
+
+%% =====================================================================
+%% @spec new_keys(N, Function, Env) -> [term()]
+%%
+%% N = integer()
+%% Function = (integer()) -> term()
+%% Env = environment()
+%%
+%% @doc Returns a list of <code>N</code> distinct terms that are not
+%% already used as keys in the environment. See {@link new_key/3} for
+%% details.
+
+new_keys(N, F, Env) when integer(N) ->
+ R = start_range(Env),
+ new_keys(N, [], R, F, Env).
+
+new_keys(N, Ks, R, F, Env) when N > 0 ->
+ Key = new_key(R, F, Env),
+ Env1 = bind(Key, true, Env), % dummy binding
+ new_keys(N - 1, [Key | Ks], R, F, Env1);
+new_keys(0, Ks, _, _, _) ->
+ Ks.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl
new file mode 100644
index 0000000000..c5052b0e51
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_expand_pmod.erl
@@ -0,0 +1,425 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: sys_expand_pmod.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+-module(sys_expand_pmod).
+
+%% Expand function definition forms of parameterized module. We assume
+%% all record definitions, imports, queries, etc., have been expanded
+%% away. Any calls on the form 'foo(...)' must be calls to local
+%% functions. Auto-generated functions (module_info,...) have not yet
+%% been added to the function definitions, but are listed in 'defined'
+%% and 'exports'. The 'new/N' function is neither added to the
+%% definitions nor to the 'exports'/'defines' lists yet.
+
+-export([forms/4]).
+
+-record(pmod, {parameters, exports, defined, predef}).
+
+%% TODO: more abstract handling of predefined/static functions.
+
+forms(Fs0, Ps, Es0, Ds0) ->
+ PreDef = [{module_info,0},{module_info,1}],
+ forms(Fs0, Ps, Es0, Ds0, PreDef).
+
+forms(Fs0, Ps, Es0, Ds0, PreDef) ->
+ St0 = #pmod{parameters=Ps,exports=Es0,defined=Ds0, predef=PreDef},
+ {Fs1, St1} = forms(Fs0, St0),
+ Es1 = update_function_names(Es0, St1),
+ Ds1 = update_function_names(Ds0, St1),
+ Fs2 = update_forms(Fs1, St1),
+ {Fs2,Es1,Ds1}.
+
+%% This is extremely simplistic for now; all functions get an extra
+%% parameter, whether they need it or not, except for static functions.
+
+update_function_names(Es, St) ->
+ [update_function_name(E, St) || E <- Es].
+
+update_function_name(E={F,A}, St) ->
+ case ordsets:is_element(E, St#pmod.predef) of
+ true -> E;
+ false -> {F, A + 1}
+ end.
+
+update_forms([{function,L,N,A,Cs}|Fs],St) ->
+ [{function,L,N,A+1,Cs}|update_forms(Fs,St)];
+update_forms([F|Fs],St) ->
+ [F|update_forms(Fs,St)];
+update_forms([],_St) ->
+ [].
+
+%% Process the program forms.
+
+forms([F0|Fs0],St0) ->
+ {F1,St1} = form(F0,St0),
+ {Fs1,St2} = forms(Fs0,St1),
+ {[F1|Fs1],St2};
+forms([], St0) ->
+ {[], St0}.
+
+%% Only function definitions are of interest here. State is not updated.
+form({function,Line,Name0,Arity0,Clauses0},St) ->
+ {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0, St),
+ {{function,Line,Name,Arity,Clauses},St};
+%% Pass anything else through
+form(F,St) -> {F,St}.
+
+function(Name, Arity, Clauses0, St) ->
+ Clauses1 = clauses(Clauses0,St),
+ {Name,Arity,Clauses1}.
+
+clauses([C|Cs],St) ->
+ {clause,L,H,G,B} = clause(C,St),
+ T = {tuple,L,[{var,L,V} || V <- ['_'|St#pmod.parameters]]},
+ [{clause,L,H++[{match,L,T,{var,L,'THIS'}}],G,B}|clauses(Cs,St)];
+clauses([],_St) -> [].
+
+clause({clause,Line,H0,G0,B0},St) ->
+ H1 = head(H0,St),
+ G1 = guard(G0,St),
+ B1 = exprs(B0,St),
+ {clause,Line,H1,G1,B1}.
+
+head(Ps,St) -> patterns(Ps,St).
+
+patterns([P0|Ps],St) ->
+ P1 = pattern(P0,St),
+ [P1|patterns(Ps,St)];
+patterns([],_St) -> [].
+
+string_to_conses([], _Line, Tail) ->
+ Tail;
+string_to_conses([E|Rest], Line, Tail) ->
+ {cons, Line, {integer, Line, E}, string_to_conses(Rest, Line, Tail)}.
+
+pattern({var,Line,V},_St) -> {var,Line,V};
+pattern({match,Line,L0,R0},St) ->
+ L1 = pattern(L0,St),
+ R1 = pattern(R0,St),
+ {match,Line,L1,R1};
+pattern({integer,Line,I},_St) -> {integer,Line,I};
+pattern({char,Line,C},_St) -> {char,Line,C};
+pattern({float,Line,F},_St) -> {float,Line,F};
+pattern({atom,Line,A},_St) -> {atom,Line,A};
+pattern({string,Line,S},_St) -> {string,Line,S};
+pattern({nil,Line},_St) -> {nil,Line};
+pattern({cons,Line,H0,T0},St) ->
+ H1 = pattern(H0,St),
+ T1 = pattern(T0,St),
+ {cons,Line,H1,T1};
+pattern({tuple,Line,Ps0},St) ->
+ Ps1 = pattern_list(Ps0,St),
+ {tuple,Line,Ps1};
+pattern({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+pattern({op,_Line,'++',{nil,_},R},St) ->
+ pattern(R,St);
+pattern({op,_Line,'++',{cons,Li,{char,C2,I},T},R},St) ->
+ pattern({cons,Li,{char,C2,I},{op,Li,'++',T,R}},St);
+pattern({op,_Line,'++',{cons,Li,{integer,L2,I},T},R},St) ->
+ pattern({cons,Li,{integer,L2,I},{op,Li,'++',T,R}},St);
+pattern({op,_Line,'++',{string,Li,L},R},St) ->
+ pattern(string_to_conses(L, Li, R),St);
+pattern({op,Line,Op,A},_St) ->
+ {op,Line,Op,A};
+pattern({op,Line,Op,L,R},_St) ->
+ {op,Line,Op,L,R}.
+
+pattern_grp([{bin_element,L1,E1,S1,T1} | Fs],St) ->
+ S2 = case S1 of
+ default ->
+ default;
+ _ ->
+ expr(S1,St)
+ end,
+ T2 = case T1 of
+ default ->
+ default;
+ _ ->
+ bit_types(T1)
+ end,
+ [{bin_element,L1,expr(E1,St),S2,T2} | pattern_grp(Fs,St)];
+pattern_grp([],_St) ->
+ [].
+
+bit_types([]) ->
+ [];
+bit_types([Atom | Rest]) when atom(Atom) ->
+ [Atom | bit_types(Rest)];
+bit_types([{Atom, Integer} | Rest]) when atom(Atom), integer(Integer) ->
+ [{Atom, Integer} | bit_types(Rest)].
+
+pattern_list([P0|Ps],St) ->
+ P1 = pattern(P0,St),
+ [P1|pattern_list(Ps,St)];
+pattern_list([],_St) -> [].
+
+guard([G0|Gs],St) when list(G0) ->
+ [guard0(G0,St) | guard(Gs,St)];
+guard(L,St) ->
+ guard0(L,St).
+
+guard0([G0|Gs],St) ->
+ G1 = guard_test(G0,St),
+ [G1|guard0(Gs,St)];
+guard0([],_St) -> [].
+
+guard_test(Expr={call,Line,{atom,La,F},As0},St) ->
+ case erl_internal:type_test(F, length(As0)) of
+ true ->
+ As1 = gexpr_list(As0,St),
+ {call,Line,{atom,La,F},As1};
+ _ ->
+ gexpr(Expr,St)
+ end;
+guard_test(Any,St) ->
+ gexpr(Any,St).
+
+gexpr({var,L,V},_St) ->
+ {var,L,V};
+% %% alternative implementation of accessing module parameters
+% case index(V,St#pmod.parameters) of
+% N when N > 0 ->
+% {call,L,{remote,L,{atom,L,erlang},{atom,L,element}},
+% [{integer,L,N+1},{var,L,'THIS'}]};
+% _ ->
+% {var,L,V}
+% end;
+gexpr({integer,Line,I},_St) -> {integer,Line,I};
+gexpr({char,Line,C},_St) -> {char,Line,C};
+gexpr({float,Line,F},_St) -> {float,Line,F};
+gexpr({atom,Line,A},_St) -> {atom,Line,A};
+gexpr({string,Line,S},_St) -> {string,Line,S};
+gexpr({nil,Line},_St) -> {nil,Line};
+gexpr({cons,Line,H0,T0},St) ->
+ H1 = gexpr(H0,St),
+ T1 = gexpr(T0,St),
+ {cons,Line,H1,T1};
+gexpr({tuple,Line,Es0},St) ->
+ Es1 = gexpr_list(Es0,St),
+ {tuple,Line,Es1};
+gexpr({call,Line,{atom,La,F},As0},St) ->
+ case erl_internal:guard_bif(F, length(As0)) of
+ true -> As1 = gexpr_list(As0,St),
+ {call,Line,{atom,La,F},As1}
+ end;
+% Pre-expansion generated calls to erlang:is_record/3 must also be handled
+gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As0},St)
+ when length(As0) == 3 ->
+ As1 = gexpr_list(As0,St),
+ {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,is_record}},As1};
+% Guard bif's can be remote, but only in the module erlang...
+gexpr({call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As0},St) ->
+ case erl_internal:guard_bif(F, length(As0)) or
+ erl_internal:arith_op(F, length(As0)) or
+ erl_internal:comp_op(F, length(As0)) or
+ erl_internal:bool_op(F, length(As0)) of
+ true -> As1 = gexpr_list(As0,St),
+ {call,Line,{remote,La,{atom,Lb,erlang},{atom,Lc,F}},As1}
+ end;
+% Unfortunately, writing calls as {M,F}(...) is also allowed.
+gexpr({call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As0},St) ->
+ case erl_internal:guard_bif(F, length(As0)) or
+ erl_internal:arith_op(F, length(As0)) or
+ erl_internal:comp_op(F, length(As0)) or
+ erl_internal:bool_op(F, length(As0)) of
+ true -> As1 = gexpr_list(As0,St),
+ {call,Line,{tuple,La,[{atom,Lb,erlang},{atom,Lc,F}]},As1}
+ end;
+gexpr({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+gexpr({op,Line,Op,A0},St) ->
+ case erl_internal:arith_op(Op, 1) or
+ erl_internal:bool_op(Op, 1) of
+ true -> A1 = gexpr(A0,St),
+ {op,Line,Op,A1}
+ end;
+gexpr({op,Line,Op,L0,R0},St) ->
+ case erl_internal:arith_op(Op, 2) or
+ erl_internal:bool_op(Op, 2) or
+ erl_internal:comp_op(Op, 2) of
+ true ->
+ L1 = gexpr(L0,St),
+ R1 = gexpr(R0,St),
+ {op,Line,Op,L1,R1}
+ end.
+
+gexpr_list([E0|Es],St) ->
+ E1 = gexpr(E0,St),
+ [E1|gexpr_list(Es,St)];
+gexpr_list([],_St) -> [].
+
+exprs([E0|Es],St) ->
+ E1 = expr(E0,St),
+ [E1|exprs(Es,St)];
+exprs([],_St) -> [].
+
+expr({var,L,V},_St) ->
+ {var,L,V};
+% case index(V,St#pmod.parameters) of
+% N when N > 0 ->
+% {call,L,{remote,L,{atom,L,erlang},{atom,L,element}},
+% [{integer,L,N+1},{var,L,'THIS'}]};
+% _ ->
+% {var,L,V}
+% end;
+expr({integer,Line,I},_St) -> {integer,Line,I};
+expr({float,Line,F},_St) -> {float,Line,F};
+expr({atom,Line,A},_St) -> {atom,Line,A};
+expr({string,Line,S},_St) -> {string,Line,S};
+expr({char,Line,C},_St) -> {char,Line,C};
+expr({nil,Line},_St) -> {nil,Line};
+expr({cons,Line,H0,T0},St) ->
+ H1 = expr(H0,St),
+ T1 = expr(T0,St),
+ {cons,Line,H1,T1};
+expr({lc,Line,E0,Qs0},St) ->
+ Qs1 = lc_quals(Qs0,St),
+ E1 = expr(E0,St),
+ {lc,Line,E1,Qs1};
+expr({tuple,Line,Es0},St) ->
+ Es1 = expr_list(Es0,St),
+ {tuple,Line,Es1};
+expr({block,Line,Es0},St) ->
+ Es1 = exprs(Es0,St),
+ {block,Line,Es1};
+expr({'if',Line,Cs0},St) ->
+ Cs1 = icr_clauses(Cs0,St),
+ {'if',Line,Cs1};
+expr({'case',Line,E0,Cs0},St) ->
+ E1 = expr(E0,St),
+ Cs1 = icr_clauses(Cs0,St),
+ {'case',Line,E1,Cs1};
+expr({'receive',Line,Cs0},St) ->
+ Cs1 = icr_clauses(Cs0,St),
+ {'receive',Line,Cs1};
+expr({'receive',Line,Cs0,To0,ToEs0},St) ->
+ To1 = expr(To0,St),
+ ToEs1 = exprs(ToEs0,St),
+ Cs1 = icr_clauses(Cs0,St),
+ {'receive',Line,Cs1,To1,ToEs1};
+expr({'try',Line,Es0,Scs0,Ccs0,As0},St) ->
+ Es1 = exprs(Es0,St),
+ Scs1 = icr_clauses(Scs0,St),
+ Ccs1 = icr_clauses(Ccs0,St),
+ As1 = exprs(As0,St),
+ {'try',Line,Es1,Scs1,Ccs1,As1};
+expr({'fun',Line,Body,Info},St) ->
+ case Body of
+ {clauses,Cs0} ->
+ Cs1 = fun_clauses(Cs0,St),
+ {'fun',Line,{clauses,Cs1},Info};
+ {function,F,A} ->
+ {F1,A1} = update_function_name({F,A},St),
+ if A1 == A ->
+ {'fun',Line,{function,F,A},Info};
+ true ->
+ %% Must rewrite local fun-name to a fun that does a
+ %% call with the extra THIS parameter.
+ As = make_vars(A, Line),
+ As1 = As ++ [{var,Line,'THIS'}],
+ Call = {call,Line,{atom,Line,F1},As1},
+ Cs = [{clause,Line,As,[],[Call]}],
+ {'fun',Line,{clauses,Cs},Info}
+ end;
+ {function,M,F,A} -> %This is an error in lint!
+ {'fun',Line,{function,M,F,A},Info}
+ end;
+expr({call,Lc,{atom,_,new}=Name,As0},#pmod{parameters=Ps}=St)
+ when length(As0) =:= length(Ps) ->
+ %% The new() function does not take a 'THIS' argument (it's static).
+ As1 = expr_list(As0,St),
+ {call,Lc,Name,As1};
+expr({call,Lc,{atom,_,module_info}=Name,As0},St)
+ when length(As0) == 0; length(As0) == 1 ->
+ %% The module_info/0 and module_info/1 functions are also static.
+ As1 = expr_list(As0,St),
+ {call,Lc,Name,As1};
+expr({call,Lc,{atom,Lf,F},As0},St) ->
+ %% Local function call - needs THIS parameter.
+ As1 = expr_list(As0,St),
+ {call,Lc,{atom,Lf,F},As1 ++ [{var,0,'THIS'}]};
+expr({call,Line,F0,As0},St) ->
+ %% Other function call
+ F1 = expr(F0,St),
+ As1 = expr_list(As0,St),
+ {call,Line,F1,As1};
+expr({'catch',Line,E0},St) ->
+ E1 = expr(E0,St),
+ {'catch',Line,E1};
+expr({match,Line,P0,E0},St) ->
+ E1 = expr(E0,St),
+ P1 = pattern(P0,St),
+ {match,Line,P1,E1};
+expr({bin,Line,Fs},St) ->
+ Fs2 = pattern_grp(Fs,St),
+ {bin,Line,Fs2};
+expr({op,Line,Op,A0},St) ->
+ A1 = expr(A0,St),
+ {op,Line,Op,A1};
+expr({op,Line,Op,L0,R0},St) ->
+ L1 = expr(L0,St),
+ R1 = expr(R0,St),
+ {op,Line,Op,L1,R1};
+%% The following are not allowed to occur anywhere!
+expr({remote,Line,M0,F0},St) ->
+ M1 = expr(M0,St),
+ F1 = expr(F0,St),
+ {remote,Line,M1,F1}.
+
+expr_list([E0|Es],St) ->
+ E1 = expr(E0,St),
+ [E1|expr_list(Es,St)];
+expr_list([],_St) -> [].
+
+icr_clauses([C0|Cs],St) ->
+ C1 = clause(C0,St),
+ [C1|icr_clauses(Cs,St)];
+icr_clauses([],_St) -> [].
+
+lc_quals([{generate,Line,P0,E0}|Qs],St) ->
+ E1 = expr(E0,St),
+ P1 = pattern(P0,St),
+ [{generate,Line,P1,E1}|lc_quals(Qs,St)];
+lc_quals([E0|Qs],St) ->
+ E1 = expr(E0,St),
+ [E1|lc_quals(Qs,St)];
+lc_quals([],_St) -> [].
+
+fun_clauses([C0|Cs],St) ->
+ C1 = clause(C0,St),
+ [C1|fun_clauses(Cs,St)];
+fun_clauses([],_St) -> [].
+
+% %% Return index from 1 upwards, or 0 if not in the list.
+%
+% index(X,Ys) -> index(X,Ys,1).
+%
+% index(X,[X|Ys],A) -> A;
+% index(X,[Y|Ys],A) -> index(X,Ys,A+1);
+% index(X,[],A) -> 0.
+
+make_vars(N, L) ->
+ make_vars(1, N, L).
+
+make_vars(N, M, L) when N =< M ->
+ V = list_to_atom("X"++integer_to_list(N)),
+ [{var,L,V} | make_vars(N + 1, M, L)];
+make_vars(_, _, _) ->
+ [].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl
new file mode 100644
index 0000000000..6e68611c66
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_attributes.erl
@@ -0,0 +1,212 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: sys_pre_attributes.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Transform Erlang compiler attributes
+
+-module(sys_pre_attributes).
+
+-export([parse_transform/2]).
+
+-define(OPTION_TAG, attributes).
+
+-record(state, {forms,
+ pre_ops = [],
+ post_ops = [],
+ options}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Inserts, deletes and replaces Erlang compiler attributes.
+%%
+%% Valid options are:
+%%
+%% {attribute, insert, AttrName, NewAttrVal}
+%% {attribute, replace, AttrName, NewAttrVal} % replace first occurrence
+%% {attribute, delete, AttrName}
+%%
+%% The transformation is performed in two passes:
+%%
+%% pre_transform
+%% -------------
+%% Searches for attributes in the list of Forms in order to
+%% delete or replace them. 'delete' will delete all occurrences
+%% of attributes with the given name. 'replace' will replace the
+%% first occurrence of the attribute. This pass is will only be
+%% performed if there are replace or delete operations stated
+%% as options.
+%%
+%% post_transform
+%% -------------
+%% Looks up the module attribute and inserts the new attributes
+%% directly after. This pass will only be performed if there are
+%% any attributes left to be inserted after pre_transform. The left
+%% overs will be those replace operations that not has been performed
+%% due to that the pre_transform pass did not find the attribute plus
+%% all insert operations.
+
+parse_transform(Forms, Options) ->
+ S = #state{forms = Forms, options = Options},
+ S2 = init_transform(S),
+ report_verbose("Pre options: ~p~n", [S2#state.pre_ops], S2),
+ report_verbose("Post options: ~p~n", [S2#state.post_ops], S2),
+ S3 = pre_transform(S2),
+ S4 = post_transform(S3),
+ S4#state.forms.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Computes the lists of pre_ops and post_ops that are
+%% used in the real transformation.
+init_transform(S) ->
+ case S#state.options of
+ Options when list(Options) ->
+ init_transform(Options, S);
+ Option ->
+ init_transform([Option], S)
+ end.
+
+init_transform([{attribute, insert, Name, Val} | Tail], S) ->
+ Op = {insert, Name, Val},
+ PostOps = [Op | S#state.post_ops],
+ init_transform(Tail, S#state{post_ops = PostOps});
+init_transform([{attribute, replace, Name, Val} | Tail], S) ->
+ Op = {replace, Name, Val},
+ PreOps = [Op | S#state.pre_ops],
+ PostOps = [Op | S#state.post_ops],
+ init_transform(Tail, S#state{pre_ops = PreOps, post_ops = PostOps});
+init_transform([{attribute, delete, Name} | Tail], S) ->
+ Op = {delete, Name},
+ PreOps = [Op | S#state.pre_ops],
+ init_transform(Tail, S#state{pre_ops = PreOps});
+init_transform([], S) ->
+ S;
+init_transform([_ | T], S) ->
+ init_transform(T, S);
+init_transform(BadOpt, S) ->
+ report_error("Illegal option (ignored): ~p~n", [BadOpt], S),
+ S.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Handle delete and perhaps replace
+
+pre_transform(S) when S#state.pre_ops == [] ->
+ S;
+pre_transform(S) ->
+ pre_transform(S#state.forms, [], S).
+
+pre_transform([H | T], Acc, S) ->
+ case H of
+ {attribute, Line, Name, Val} ->
+ case lists:keysearch(Name, 2, S#state.pre_ops) of
+ false ->
+ pre_transform(T, [H | Acc], S);
+
+ {value, {replace, Name, NewVal}} ->
+ report_warning("Replace attribute ~p: ~p -> ~p~n",
+ [Name, Val, NewVal],
+ S),
+ New = {attribute, Line, Name, NewVal},
+ Pre = lists:keydelete(Name, 2, S#state.pre_ops),
+ Post = lists:keydelete(Name, 2, S#state.post_ops),
+ S2 = S#state{pre_ops = Pre, post_ops = Post},
+ if
+ Pre == [] ->
+ %% No need to search the rest of the Forms
+ Forms = lists:reverse(Acc, [New | T]),
+ S2#state{forms = Forms};
+ true ->
+ pre_transform(T, [New | Acc], S2)
+ end;
+
+ {value, {delete, Name}} ->
+ report_warning("Delete attribute ~p: ~p~n",
+ [Name, Val],
+ S),
+ pre_transform(T, Acc, S)
+ end;
+ _Any ->
+ pre_transform(T, [H | Acc], S)
+ end;
+pre_transform([], Acc, S) ->
+ S#state{forms = lists:reverse(Acc)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Handle insert and perhaps replace
+
+post_transform(S) when S#state.post_ops == [] ->
+ S;
+post_transform(S) ->
+ post_transform(S#state.forms, [], S).
+
+post_transform([H | T], Acc, S) ->
+ case H of
+ {attribute, Line, module, Val} ->
+ Acc2 = lists:reverse([{attribute, Line, module, Val} | Acc]),
+ Forms = Acc2 ++ attrs(S#state.post_ops, Line, S) ++ T,
+ S#state{forms = Forms, post_ops = []};
+ _Any ->
+ post_transform(T, [H | Acc], S)
+ end;
+post_transform([], Acc, S) ->
+ S#state{forms = lists:reverse(Acc)}.
+
+attrs([{replace, Name, NewVal} | T], Line, S) ->
+ report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
+ [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
+attrs([{insert, Name, NewVal} | T], Line, S) ->
+ report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S),
+ [{attribute, Line, Name, NewVal} | attrs(T, Line, S)];
+attrs([], _, _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Report functions.
+%%
+%% Errors messages are controlled with the 'report_errors' compiler option
+%% Warning messages are controlled with the 'report_warnings' compiler option
+%% Verbose messages are controlled with the 'verbose' compiler option
+
+report_error(Format, Args, S) ->
+ case is_error(S) of
+ true ->
+ io:format("~p: * ERROR * " ++ Format, [?MODULE | Args]);
+ false ->
+ ok
+ end.
+
+report_warning(Format, Args, S) ->
+ case is_warning(S) of
+ true ->
+ io:format("~p: * WARNING * " ++ Format, [?MODULE | Args]);
+ false ->
+ ok
+ end.
+
+report_verbose(Format, Args, S) ->
+ case is_verbose(S) of
+ true ->
+ io:format("~p: " ++ Format, [?MODULE | Args]);
+ false ->
+ ok
+ end.
+
+is_error(S) ->
+ lists:member(report_errors, S#state.options) or is_verbose(S).
+
+is_warning(S) ->
+ lists:member(report_warnings, S#state.options) or is_verbose(S).
+
+is_verbose(S) ->
+ lists:member(verbose, S#state.options).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl
new file mode 100644
index 0000000000..5e7c1c8bbd
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/sys_pre_expand.erl
@@ -0,0 +1,1026 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: sys_pre_expand.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Expand some source Erlang constructions. This is part of the
+%% pre-processing phase.
+
+%% N.B. Although structs (tagged tuples) are not yet allowed in the
+%% language there is code included in pattern/2 and expr/3 (commented out)
+%% that handles them by transforming them to tuples.
+
+-module(sys_pre_expand).
+
+%% Main entry point.
+-export([module/2]).
+
+-import(ordsets, [from_list/1,add_element/2,
+ union/1,union/2,intersection/1,intersection/2,subtract/2]).
+-import(lists, [member/2,map/2,foldl/3,foldr/3,sort/1,reverse/1,duplicate/2]).
+
+-include("../my_include/erl_bits.hrl").
+
+-record(expand, {module=[], %Module name
+ parameters=undefined, %Module parameters
+ package="", %Module package
+ exports=[], %Exports
+ imports=[], %Imports
+ mod_imports, %Module Imports
+ compile=[], %Compile flags
+ records=dict:new(), %Record definitions
+ attributes=[], %Attributes
+ defined=[], %Defined functions
+ vcount=0, %Variable counter
+ func=[], %Current function
+ arity=[], %Arity for current function
+ fcount=0, %Local fun count
+ fun_index=0, %Global index for funs
+ bitdefault,
+ bittypes
+ }).
+
+%% module(Forms, CompileOptions)
+%% {ModuleName,Exports,TransformedForms}
+%% Expand the forms in one module. N.B.: the lists of predefined
+%% exports and imports are really ordsets!
+
+module(Fs, Opts) ->
+ %% Set pre-defined exported functions.
+ PreExp = [{module_info,0},{module_info,1}],
+
+ %% Set pre-defined module imports.
+ PreModImp = [{erlang,erlang},{packages,packages}],
+
+ %% Build initial expand record.
+ St0 = #expand{exports=PreExp,
+ mod_imports=dict:from_list(PreModImp),
+ compile=Opts,
+ defined=PreExp,
+ bitdefault = erl_bits:system_bitdefault(),
+ bittypes = erl_bits:system_bittypes()
+ },
+ %% Expand the functions.
+ {Tfs,St1} = forms(Fs, foldl(fun define_function/2, St0, Fs)),
+ {Efs,St2} = expand_pmod(Tfs, St1),
+ %% Get the correct list of exported functions.
+ Exports = case member(export_all, St2#expand.compile) of
+ true -> St2#expand.defined;
+ false -> St2#expand.exports
+ end,
+ %% Generate all functions from stored info.
+ {Ats,St3} = module_attrs(St2#expand{exports = Exports}),
+ {Mfs,St4} = module_predef_funcs(St3),
+ {St4#expand.module, St4#expand.exports, Ats ++ Efs ++ Mfs,
+ St4#expand.compile}.
+
+expand_pmod(Fs0, St) ->
+ case St#expand.parameters of
+ undefined ->
+ {Fs0,St};
+ Ps ->
+ {Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps,
+ St#expand.exports,
+ St#expand.defined),
+ A = length(Ps),
+ Vs = [{var,0,V} || V <- Ps],
+ N = {atom,0,St#expand.module},
+ B = [{tuple,0,[N|Vs]}],
+ F = {function,0,new,A,[{clause,0,Vs,[],B}]},
+ As = St#expand.attributes,
+ {[F|Fs1],St#expand{exports=add_element({new,A}, Xs),
+ defined=add_element({new,A}, Ds),
+ attributes = [{abstract, true} | As]}}
+ end.
+
+%% -type define_function(Form, State) -> State.
+%% Add function to defined if form a function.
+
+define_function({function,_,N,A,_Cs}, St) ->
+ St#expand{defined=add_element({N,A}, St#expand.defined)};
+define_function(_, St) -> St.
+
+module_attrs(St) ->
+ {[{attribute,0,Name,Val} || {Name,Val} <- St#expand.attributes],St}.
+
+module_predef_funcs(St) ->
+ PreDef = [{module_info,0},{module_info,1}],
+ PreExp = PreDef,
+ {[{function,0,module_info,0,
+ [{clause,0,[],[],
+ [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
+ [{atom,0,St#expand.module}]}]}]},
+ {function,0,module_info,1,
+ [{clause,0,[{var,0,'X'}],[],
+ [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}},
+ [{atom,0,St#expand.module},{var,0,'X'}]}]}]}],
+ St#expand{defined=union(from_list(PreDef), St#expand.defined),
+ exports=union(from_list(PreExp), St#expand.exports)}}.
+
+%% forms(Forms, State) ->
+%% {TransformedForms,State'}
+%% Process the forms. Attributes are lost and just affect the state.
+%% Ignore uninteresting forms like eof and type.
+
+forms([{attribute,_,Name,Val}|Fs0], St0) ->
+ St1 = attribute(Name, Val, St0),
+ forms(Fs0, St1);
+forms([{function,L,N,A,Cs}|Fs0], St0) ->
+ {Ff,St1} = function(L, N, A, Cs, St0),
+ {Fs,St2} = forms(Fs0, St1),
+ {[Ff|Fs],St2};
+forms([_|Fs], St) -> forms(Fs, St);
+forms([], St) -> {[],St}.
+
+%% -type attribute(Attribute, Value, State) ->
+%% State.
+%% Process an attribute, this just affects the state.
+
+attribute(module, {Module, As}, St) ->
+ M = package_to_string(Module),
+ St#expand{module=list_to_atom(M),
+ package = packages:strip_last(M),
+ parameters=As};
+attribute(module, Module, St) ->
+ M = package_to_string(Module),
+ St#expand{module=list_to_atom(M),
+ package = packages:strip_last(M)};
+attribute(export, Es, St) ->
+ St#expand{exports=union(from_list(Es), St#expand.exports)};
+attribute(import, Is, St) ->
+ import(Is, St);
+attribute(compile, C, St) when list(C) ->
+ St#expand{compile=St#expand.compile ++ C};
+attribute(compile, C, St) ->
+ St#expand{compile=St#expand.compile ++ [C]};
+attribute(record, {Name,Defs}, St) ->
+ St#expand{records=dict:store(Name, normalise_fields(Defs),
+ St#expand.records)};
+attribute(file, _File, St) -> St; %This is ignored
+attribute(Name, Val, St) when list(Val) ->
+ St#expand{attributes=St#expand.attributes ++ [{Name,Val}]};
+attribute(Name, Val, St) ->
+ St#expand{attributes=St#expand.attributes ++ [{Name,[Val]}]}.
+
+function(L, N, A, Cs0, St0) ->
+ {Cs,St} = clauses(Cs0, St0#expand{func=N,arity=A,fcount=0}),
+ {{function,L,N,A,Cs},St}.
+
+%% -type clauses([Clause], State) ->
+%% {[TransformedClause],State}.
+%% Expand function clauses.
+
+clauses([{clause,Line,H0,G0,B0}|Cs0], St0) ->
+ {H,Hvs,_Hus,St1} = head(H0, St0),
+ {G,Gvs,_Gus,St2} = guard(G0, Hvs, St1),
+ {B,_Bvs,_Bus,St3} = exprs(B0, union(Hvs, Gvs), St2),
+ {Cs,St4} = clauses(Cs0, St3),
+ {[{clause,Line,H,G,B}|Cs],St4};
+clauses([], St) -> {[],St}.
+
+%% head(HeadPatterns, State) ->
+%% {TransformedPatterns,Variables,UsedVariables,State'}
+
+head(As, St) -> pattern_list(As, St).
+
+%% pattern(Pattern, State) ->
+%% {TransformedPattern,Variables,UsedVariables,State'}
+%% BITS: added used variables for bit patterns with varaible length
+%%
+
+pattern({var,_,'_'}=Var, St) -> %Ignore anonymous variable.
+ {Var,[],[],St};
+pattern({var,_,V}=Var, St) ->
+ {Var,[V],[],St};
+pattern({char,_,_}=Char, St) ->
+ {Char,[],[],St};
+pattern({integer,_,_}=Int, St) ->
+ {Int,[],[],St};
+pattern({float,_,_}=Float, St) ->
+ {Float,[],[],St};
+pattern({atom,_,_}=Atom, St) ->
+ {Atom,[],[],St};
+pattern({string,_,_}=String, St) ->
+ {String,[],[],St};
+pattern({nil,_}=Nil, St) ->
+ {Nil,[],[],St};
+pattern({cons,Line,H,T}, St0) ->
+ {TH,THvs,Hus,St1} = pattern(H, St0),
+ {TT,TTvs,Tus,St2} = pattern(T, St1),
+ {{cons,Line,TH,TT},union(THvs, TTvs),union(Hus,Tus),St2};
+pattern({tuple,Line,Ps}, St0) ->
+ {TPs,TPsvs,Tus,St1} = pattern_list(Ps, St0),
+ {{tuple,Line,TPs},TPsvs,Tus,St1};
+%%pattern({struct,Line,Tag,Ps}, St0) ->
+%% {TPs,TPsvs,St1} = pattern_list(Ps, St0),
+%% {{tuple,Line,[{atom,Line,Tag}|TPs]},TPsvs,St1};
+pattern({record_field,_,_,_}=M, St) ->
+ {expand_package(M, St), [], [], St}; % must be a package name
+pattern({record_index,Line,Name,Field}, St) ->
+ {index_expr(Line, Field, Name, record_fields(Name, St)),[],[],St};
+pattern({record,Line,Name,Pfs}, St0) ->
+ Fs = record_fields(Name, St0),
+ {TMs,TMsvs,Us,St1} = pattern_list(pattern_fields(Fs, Pfs), St0),
+ {{tuple,Line,[{atom,Line,Name}|TMs]},TMsvs,Us,St1};
+pattern({bin,Line,Es0}, St0) ->
+ {Es1,Esvs,Esus,St1} = pattern_bin(Es0, St0),
+ {{bin,Line,Es1},Esvs,Esus,St1};
+pattern({op,_,'++',{nil,_},R}, St) ->
+ pattern(R, St);
+pattern({op,_,'++',{cons,Li,H,T},R}, St) ->
+ pattern({cons,Li,H,{op,Li,'++',T,R}}, St);
+pattern({op,_,'++',{string,Li,L},R}, St) ->
+ pattern(string_to_conses(Li, L, R), St);
+pattern({match,Line,Pat1, Pat2}, St0) ->
+ {TH,Hvt,Hus,St1} = pattern(Pat2, St0),
+ {TT,Tvt,Tus,St2} = pattern(Pat1, St1),
+ {{match,Line,TT,TH}, union(Hvt,Tvt), union(Hus,Tus), St2};
+%% Compile-time pattern expressions, including unary operators.
+pattern({op,Line,Op,A}, St) ->
+ { erl_eval:partial_eval({op,Line,Op,A}), [], [], St};
+pattern({op,Line,Op,L,R}, St) ->
+ { erl_eval:partial_eval({op,Line,Op,L,R}), [], [], St}.
+
+pattern_list([P0|Ps0], St0) ->
+ {P,Pvs,Pus,St1} = pattern(P0, St0),
+ {Ps,Psvs,Psus,St2} = pattern_list(Ps0, St1),
+ {[P|Ps],union(Pvs, Psvs),union(Pus, Psus),St2};
+pattern_list([], St) -> {[],[],[],St}.
+
+%% guard(Guard, VisibleVariables, State) ->
+%% {TransformedGuard,NewVariables,UsedVariables,State'}
+%% Transform a list of guard tests. We KNOW that this has been checked
+%% and what the guards test are. Use expr for transforming the guard
+%% expressions.
+
+guard([G0|Gs0], Vs, St0) ->
+ {G,Hvs,Hus,St1} = guard_tests(G0, Vs, St0),
+ {Gs,Tvs,Tus,St2} = guard(Gs0, Vs, St1),
+ {[G|Gs],union(Hvs, Tvs),union(Hus, Tus),St2};
+guard([], _, St) -> {[],[],[],St}.
+
+guard_tests([Gt0|Gts0], Vs, St0) ->
+ {Gt1,Gvs,Gus,St1} = guard_test(Gt0, Vs, St0),
+ {Gts1,Gsvs,Gsus,St2} = guard_tests(Gts0, union(Gvs, Vs), St1),
+ {[Gt1|Gts1],union(Gvs, Gsvs),union(Gus, Gsus),St2};
+guard_tests([], _, St) -> {[],[],[],St}.
+
+guard_test({call,Line,{atom,_,record},[A,{atom,_,Name}]}, Vs, St) ->
+ record_test_in_guard(Line, A, Name, Vs, St);
+guard_test({call,Line,{atom,Lt,Tname},As}, Vs, St) ->
+ %% XXX This is ugly. We can remove this workaround if/when
+ %% we'll allow 'andalso' in guards. For now, we must have
+ %% different code in guards and in bodies.
+ Test = {remote,Lt,
+ {atom,Lt,erlang},
+ {atom,Lt,normalise_test(Tname, length(As))}},
+ put(sys_pre_expand_in_guard, yes),
+ R = expr({call,Line,Test,As}, Vs, St),
+ erase(sys_pre_expand_in_guard),
+ R;
+guard_test(Test, Vs, St) ->
+ %% XXX See the previous clause.
+ put(sys_pre_expand_in_guard, yes),
+ R = expr(Test, Vs, St),
+ erase(sys_pre_expand_in_guard),
+ R.
+
+%% record_test(Line, Term, Name, Vs, St) -> TransformedExpr
+%% Generate code for is_record/1.
+
+record_test(Line, Term, Name, Vs, St) ->
+ case get(sys_pre_expand_in_guard) of
+ undefined ->
+ record_test_in_body(Line, Term, Name, Vs, St);
+ yes ->
+ record_test_in_guard(Line, Term, Name, Vs, St)
+ end.
+
+record_test_in_guard(Line, Term, Name, Vs, St) ->
+ %% Notes: (1) To keep is_record/3 properly atomic (e.g. when inverted
+ %% using 'not'), we cannot convert it to an instruction
+ %% sequence here. It must remain a single call.
+ %% (2) Later passes assume that the last argument (the size)
+ %% is a literal.
+ %% (3) We don't want calls to erlang:is_record/3 (in the source code)
+ %% confused we the internal instruction. (Reason: (2) above +
+ %% code bloat.)
+ %% (4) Xref may be run on the abstract code, so the name in the
+ %% abstract code must be erlang:is_record/3.
+ %% (5) To achive both (3) and (4) at the same time, set the name
+ %% here to erlang:is_record/3, but mark it as compiler-generated.
+ %% The v3_core pass will change the name to erlang:internal_is_record/3.
+ Fs = record_fields(Name, St),
+ expr({call,-Line,{remote,-Line,{atom,-Line,erlang},{atom,-Line,is_record}},
+ [Term,{atom,Line,Name},{integer,Line,length(Fs)+1}]},
+ Vs, St).
+
+record_test_in_body(Line, Expr, Name, Vs, St0) ->
+ %% As Expr may have side effects, we must evaluate it
+ %% first and bind the value to a new variable.
+ %% We must use also handle the case that Expr does not
+ %% evaluate to a tuple properly.
+ Fs = record_fields(Name, St0),
+ {Var,St} = new_var(Line, St0),
+
+ expr({block,Line,
+ [{match,Line,Var,Expr},
+ {op,Line,
+ 'andalso',
+ {call,Line,{atom,Line,is_tuple},[Var]},
+ {op,Line,'andalso',
+ {op,Line,'=:=',
+ {call,Line,{atom,Line,size},[Var]},
+ {integer,Line,length(Fs)+1}},
+ {op,Line,'=:=',
+ {call,Line,{atom,Line,element},[{integer,Line,1},Var]},
+ {atom,Line,Name}}}}]}, Vs, St).
+
+normalise_test(atom, 1) -> is_atom;
+normalise_test(binary, 1) -> is_binary;
+normalise_test(constant, 1) -> is_constant;
+normalise_test(float, 1) -> is_float;
+normalise_test(function, 1) -> is_function;
+normalise_test(integer, 1) -> is_integer;
+normalise_test(list, 1) -> is_list;
+normalise_test(number, 1) -> is_number;
+normalise_test(pid, 1) -> is_pid;
+normalise_test(port, 1) -> is_port;
+normalise_test(reference, 1) -> is_reference;
+normalise_test(tuple, 1) -> is_tuple;
+normalise_test(Name, _) -> Name.
+
+%% exprs(Expressions, VisibleVariables, State) ->
+%% {TransformedExprs,NewVariables,UsedVariables,State'}
+
+exprs([E0|Es0], Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {Es,Esvs,Esus,St2} = exprs(Es0, union(Evs, Vs), St1),
+ {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2};
+exprs([], _, St) -> {[],[],[],St}.
+
+%% expr(Expression, VisibleVariables, State) ->
+%% {TransformedExpression,NewVariables,UsedVariables,State'}
+
+expr({var,_,V}=Var, _Vs, St) ->
+ {Var,[],[V],St};
+expr({char,_,_}=Char, _Vs, St) ->
+ {Char,[],[],St};
+expr({integer,_,_}=Int, _Vs, St) ->
+ {Int,[],[],St};
+expr({float,_,_}=Float, _Vs, St) ->
+ {Float,[],[],St};
+expr({atom,_,_}=Atom, _Vs, St) ->
+ {Atom,[],[],St};
+expr({string,_,_}=String, _Vs, St) ->
+ {String,[],[],St};
+expr({nil,_}=Nil, _Vs, St) ->
+ {Nil,[],[],St};
+expr({cons,Line,H0,T0}, Vs, St0) ->
+ {H,Hvs,Hus,St1} = expr(H0, Vs, St0),
+ {T,Tvs,Tus,St2} = expr(T0, Vs, St1),
+ {{cons,Line,H,T},union(Hvs, Tvs),union(Hus, Tus),St2};
+expr({lc,Line,E0,Qs0}, Vs, St0) ->
+ {E1,Qs1,_,Lvs,Lus,St1} = lc_tq(Line, E0, Qs0, {nil,Line}, Vs, St0),
+ {{lc,Line,E1,Qs1},Lvs,Lus,St1};
+expr({tuple,Line,Es0}, Vs, St0) ->
+ {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
+ {{tuple,Line,Es1},Esvs,Esus,St1};
+%%expr({struct,Line,Tag,Es0}, Vs, St0) ->
+%% {Es1,Esvs,Esus,St1} = expr_list(Es0, Vs, St0),
+%% {{tuple,Line,[{atom,Line,Tag}|Es1]},Esvs,Esus,St1};
+expr({record_field,_,_,_}=M, _Vs, St) ->
+ {expand_package(M, St), [], [], St}; % must be a package name
+expr({record_index,Line,Name,F}, Vs, St) ->
+ I = index_expr(Line, F, Name, record_fields(Name, St)),
+ expr(I, Vs, St);
+expr({record,Line,Name,Is}, Vs, St) ->
+ expr({tuple,Line,[{atom,Line,Name}|
+ record_inits(record_fields(Name, St), Is)]},
+ Vs, St);
+expr({record_field,Line,R,Name,F}, Vs, St) ->
+ I = index_expr(Line, F, Name, record_fields(Name, St)),
+ expr({call,Line,{atom,Line,element},[I,R]}, Vs, St);
+expr({record,_,R,Name,Us}, Vs, St0) ->
+ {Ue,St1} = record_update(R, Name, record_fields(Name, St0), Us, St0),
+ expr(Ue, Vs, St1);
+expr({bin,Line,Es0}, Vs, St0) ->
+ {Es1,Esvs,Esus,St1} = expr_bin(Es0, Vs, St0),
+ {{bin,Line,Es1},Esvs,Esus,St1};
+expr({block,Line,Es0}, Vs, St0) ->
+ {Es,Esvs,Esus,St1} = exprs(Es0, Vs, St0),
+ {{block,Line,Es},Esvs,Esus,St1};
+expr({'if',Line,Cs0}, Vs, St0) ->
+ {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0),
+ All = new_in_all(Vs, Csvss),
+ {{'if',Line,Cs},All,union(Csuss),St1};
+expr({'case',Line,E0,Cs0}, Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {Cs,Csvss,Csuss,St2} = icr_clauses(Cs0, union(Evs, Vs), St1),
+ All = new_in_all(Vs, Csvss),
+ {{'case',Line,E,Cs},union(Evs, All),union([Eus|Csuss]),St2};
+expr({'cond',Line,Cs}, Vs, St0) ->
+ {V,St1} = new_var(Line,St0),
+ expr(cond_clauses(Cs,V), Vs, St1);
+expr({'receive',Line,Cs0}, Vs, St0) ->
+ {Cs,Csvss,Csuss,St1} = icr_clauses(Cs0, Vs, St0),
+ All = new_in_all(Vs, Csvss),
+ {{'receive',Line,Cs},All,union(Csuss),St1};
+expr({'receive',Line,Cs0,To0,ToEs0}, Vs, St0) ->
+ {To,Tovs,Tous,St1} = expr(To0, Vs, St0),
+ {ToEs,ToEsvs,_ToEsus,St2} = exprs(ToEs0, Vs, St1),
+ {Cs,Csvss,Csuss,St3} = icr_clauses(Cs0, Vs, St2),
+ All = new_in_all(Vs, [ToEsvs|Csvss]),
+ {{'receive',Line,Cs,To,ToEs},union(Tovs, All),union([Tous|Csuss]),St3};
+expr({'fun',Line,Body}, Vs, St) ->
+ fun_tq(Line, Body, Vs, St);
+%%% expr({call,_,{atom,La,this_module},[]}, _Vs, St) ->
+%%% {{atom,La,St#expand.module}, [], [], St};
+%%% expr({call,_,{atom,La,this_package},[]}, _Vs, St) ->
+%%% {{atom,La,list_to_atom(St#expand.package)}, [], [], St};
+%%% expr({call,_,{atom,La,this_package},[{atom,_,Name}]}, _Vs, St) ->
+%%% M = packages:concat(St#expand.package,Name),
+%%% {{atom,La,list_to_atom(M)}, [], [], St};
+%%% expr({call,Line,{atom,La,this_package},[A]}, Vs, St) ->
+%%% M = {call,Line,{remote,La,{atom,La,packages},{atom,La,concat}},
+%%% [{string,La,St#expand.package}, A]},
+%%% expr({call,Line,{atom,Line,list_to_atom},[M]}, Vs, St);
+expr({call,Line,{atom,_,is_record},[A,{atom,_,Name}]}, Vs, St) ->
+ record_test(Line, A, Name, Vs, St);
+expr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
+ [A,{atom,_,Name}]}, Vs, St) ->
+ record_test(Line, A, Name, Vs, St);
+expr({call,Line,{atom,La,N},As0}, Vs, St0) ->
+ {As,Asvs,Asus,St1} = expr_list(As0, Vs, St0),
+ Ar = length(As),
+ case erl_internal:bif(N, Ar) of
+ true ->
+ {{call,Line,{remote,La,{atom,La,erlang},{atom,La,N}},As},
+ Asvs,Asus,St1};
+ false ->
+ case imported(N, Ar, St1) of
+ {yes,Mod} ->
+ {{call,Line,{remote,La,{atom,La,Mod},{atom,La,N}},As},
+ Asvs,Asus,St1};
+ no ->
+ case {N,Ar} of
+ {record_info,2} ->
+ record_info_call(Line, As, St1);
+ _ ->
+ {{call,Line,{atom,La,N},As},Asvs,Asus,St1}
+ end
+ end
+ end;
+expr({call,Line,{record_field,_,_,_}=M,As0}, Vs, St0) ->
+ expr({call,Line,expand_package(M, St0),As0}, Vs, St0);
+expr({call,Line,{remote,Lr,M,F},As0}, Vs, St0) ->
+ M1 = expand_package(M, St0),
+ {[M2,F1|As1],Asvs,Asus,St1} = expr_list([M1,F|As0], Vs, St0),
+ {{call,Line,{remote,Lr,M2,F1},As1},Asvs,Asus,St1};
+expr({call,Line,{tuple,_,[{atom,_,_}=M,{atom,_,_}=F]},As}, Vs, St) ->
+ %% Rewrite {Mod,Function}(Args...) to Mod:Function(Args...).
+ expr({call,Line,{remote,Line,M,F},As}, Vs, St);
+expr({call,Line,F,As0}, Vs, St0) ->
+ {[Fun1|As1],Asvs,Asus,St1} = expr_list([F|As0], Vs, St0),
+ {{call,Line,Fun1,As1},Asvs,Asus,St1};
+expr({'try',Line,Es0,Scs0,Ccs0,As0}, Vs, St0) ->
+ {Es1,Esvs,Esus,St1} = exprs(Es0, Vs, St0),
+ Cvs = union(Esvs, Vs),
+ {Scs1,Scsvss,Scsuss,St2} = icr_clauses(Scs0, Cvs, St1),
+ {Ccs1,Ccsvss,Ccsuss,St3} = icr_clauses(Ccs0, Cvs, St2),
+ Csvss = Scsvss ++ Ccsvss,
+ Csuss = Scsuss ++ Ccsuss,
+ All = new_in_all(Vs, Csvss),
+ {As1,Asvs,Asus,St4} = exprs(As0, Cvs, St3),
+ {{'try',Line,Es1,Scs1,Ccs1,As1}, union([Asvs,Esvs,All]),
+ union([Esus,Asus|Csuss]), St4};
+expr({'catch',Line,E0}, Vs, St0) ->
+ %% Catch exports no new variables.
+ {E,_Evs,Eus,St1} = expr(E0, Vs, St0),
+ {{'catch',Line,E},[],Eus,St1};
+expr({match,Line,P0,E0}, Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {P,Pvs,Pus,St2} = pattern(P0, St1),
+ {{match,Line,P,E},
+ union(subtract(Pvs, Vs), Evs),
+ union(intersection(Pvs, Vs), union(Eus,Pus)),St2};
+expr({op,L,'andalso',E1,E2}, Vs, St0) ->
+ {V,St1} = new_var(L,St0),
+ E = make_bool_switch(L,E1,V,
+ make_bool_switch(L,E2,V,{atom,L,true},
+ {atom,L,false}),
+ {atom,L,false}),
+ expr(E, Vs, St1);
+expr({op,L,'orelse',E1,E2}, Vs, St0) ->
+ {V,St1} = new_var(L,St0),
+ E = make_bool_switch(L,E1,V,{atom,L,true},
+ make_bool_switch(L,E2,V,{atom,L,true},
+ {atom,L,false})),
+ expr(E, Vs, St1);
+expr({op,Line,'++',{lc,Ll,E0,Qs0},M0}, Vs, St0) ->
+ {E1,Qs1,M1,Lvs,Lus,St1} = lc_tq(Ll, E0, Qs0, M0, Vs, St0),
+ {{op,Line,'++',{lc,Ll,E1,Qs1},M1},Lvs,Lus,St1};
+expr({op,_,'++',{string,L1,S1},{string,_,S2}}, _Vs, St) ->
+ {{string,L1,S1 ++ S2},[],[],St};
+expr({op,Ll,'++',{string,L1,S1}=Str,R0}, Vs, St0) ->
+ {R1,Rvs,Rus,St1} = expr(R0, Vs, St0),
+ E = case R1 of
+ {string,_,S2} -> {string,L1,S1 ++ S2};
+ _Other when length(S1) < 8 -> string_to_conses(L1, S1, R1);
+ _Other -> {op,Ll,'++',Str,R1}
+ end,
+ {E,Rvs,Rus,St1};
+expr({op,Ll,'++',{cons,Lc,H,T},L2}, Vs, St) ->
+ expr({cons,Ll,H,{op,Lc,'++',T,L2}}, Vs, St);
+expr({op,_,'++',{nil,_},L2}, Vs, St) ->
+ expr(L2, Vs, St);
+expr({op,Line,Op,A0}, Vs, St0) ->
+ {A,Avs,Aus,St1} = expr(A0, Vs, St0),
+ {{op,Line,Op,A},Avs,Aus,St1};
+expr({op,Line,Op,L0,R0}, Vs, St0) ->
+ {L,Lvs,Lus,St1} = expr(L0, Vs, St0),
+ {R,Rvs,Rus,St2} = expr(R0, Vs, St1),
+ {{op,Line,Op,L,R},union(Lvs, Rvs),union(Lus, Rus),St2}.
+
+expr_list([E0|Es0], Vs, St0) ->
+ {E,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {Es,Esvs,Esus,St2} = expr_list(Es0, Vs, St1),
+ {[E|Es],union(Evs, Esvs),union(Eus, Esus),St2};
+expr_list([], _, St) ->
+ {[],[],[],St}.
+
+%% icr_clauses([Clause], [VisibleVariable], State) ->
+%% {[TransformedClause],[[NewVariable]],[[UsedVariable]],State'}
+%% Be very careful here to return the variables that are really used
+%% and really new.
+
+icr_clauses([], _, St) ->
+ {[],[[]],[],St};
+icr_clauses(Clauses, Vs, St) ->
+ icr_clauses2(Clauses, Vs, St).
+
+icr_clauses2([{clause,Line,H0,G0,B0}|Cs0], Vs, St0) ->
+ {H,Hvs,Hus,St1} = head(H0, St0), %Hvs is really used!
+ {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1),
+ {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2),
+ New = subtract(union([Hvs,Gvs,Bvs]), Vs), %Really new
+ Used = intersection(union([Hvs,Hus,Gus,Bus]), Vs), %Really used
+ {Cs,Csvs,Csus,St4} = icr_clauses2(Cs0, Vs, St3),
+ {[{clause,Line,H,G,B}|Cs],[New|Csvs],[Used|Csus],St4};
+icr_clauses2([], _, St) ->
+ {[],[],[],St}.
+
+%% lc_tq(Line, Expr, Qualifiers, More, [VisibleVar], State) ->
+%% {TransExpr,[TransQual],TransMore,[NewVar],[UsedVar],State'}
+
+lc_tq(Line, E0, [{generate,Lg,P0,G0}|Qs0], M0, Vs, St0) ->
+ {G1,Gvs,Gus,St1} = expr(G0, Vs, St0),
+ {P1,Pvs,Pus,St2} = pattern(P0, St1),
+ {E1,Qs1,M1,Lvs,Lus,St3} = lc_tq(Line, E0, Qs0, M0, union(Pvs, Vs), St2),
+ {E1,[{generate,Lg,P1,G1}|Qs1],M1,
+ union(Gvs, Lvs),union([Gus,Pus,Lus]),St3};
+lc_tq(Line, E0, [F0|Qs0], M0, Vs, St0) ->
+ %% Allow record/2 and expand out as guard test.
+ case erl_lint:is_guard_test(F0) of
+ true ->
+ {F1,Fvs,_Fus,St1} = guard_tests([F0], Vs, St0),
+ {E1,Qs1,M1,Lvs,Lus,St2} = lc_tq(Line, E0, Qs0, M0, union(Fvs, Vs), St1),
+ {E1,F1++Qs1,M1,Lvs,Lus,St2};
+ false ->
+ {F1,Fvs,_Fus,St1} = expr(F0, Vs, St0),
+ {E1,Qs1,M1,Lvs,Lus,St2} = lc_tq(Line, E0, Qs0, M0, union(Fvs, Vs), St1),
+ {E1,[F1|Qs1],M1,Lvs,Lus,St2}
+ end;
+lc_tq(_Line, E0, [], M0, Vs, St0) ->
+ {E1,Evs,Eus,St1} = expr(E0, Vs, St0),
+ {M1,Mvs,Mus,St2} = expr(M0, Vs, St1),
+ {E1,[],M1,union(Evs, Mvs),union(Eus, Mus),St2}.
+
+%% fun_tq(Line, Body, VisibleVariables, State) ->
+%% {Fun,NewVariables,UsedVariables,State'}
+%% Transform an "explicit" fun {'fun', Line, {clauses, Cs}} into an
+%% extended form {'fun', Line, {clauses, Cs}, Info}, unless it is the
+%% name of a BIF (erl_lint has checked that it is not an import).
+%% Process the body sequence directly to get the new and used variables.
+%% "Implicit" funs {'fun', Line, {function, F, A}} are not changed.
+
+fun_tq(Lf, {function,F,A}, Vs, St0) ->
+ {As,St1} = new_vars(A, Lf, St0),
+ Cs = [{clause,Lf,As,[],[{call,Lf,{atom,Lf,F},As}]}],
+ case erl_internal:bif(F, A) of
+ true ->
+ fun_tq(Lf, {clauses,Cs}, Vs, St1);
+ false ->
+ Index = St0#expand.fun_index,
+ Uniq = erlang:hash(Cs, (1 bsl 27)-1),
+ {Fname,St2} = new_fun_name(St1),
+ {{'fun',Lf,{function,F,A},{Index,Uniq,Fname}},[],[],
+ St2#expand{fun_index=Index+1}}
+ end;
+fun_tq(Lf, {clauses,Cs0}, Vs, St0) ->
+ Uniq = erlang:hash(Cs0, (1 bsl 27)-1),
+ {Cs1,_Hvss,Frees,St1} = fun_clauses(Cs0, Vs, St0),
+ Ufrees = union(Frees),
+ Index = St1#expand.fun_index,
+ {Fname,St2} = new_fun_name(St1),
+ {{'fun',Lf,{clauses,Cs1},{Index,Uniq,Fname}},[],Ufrees,
+ St2#expand{fun_index=Index+1}}.
+
+fun_clauses([{clause,L,H0,G0,B0}|Cs0], Vs, St0) ->
+ {H,Hvs,Hus,St1} = head(H0, St0),
+ {G,Gvs,Gus,St2} = guard(G0, union(Hvs, Vs), St1),
+ {B,Bvs,Bus,St3} = exprs(B0, union([Vs,Hvs,Gvs]), St2),
+ %% Free variables cannot be new anywhere in the clause.
+ Free = subtract(union([Gus,Hus,Bus]), union([Hvs,Gvs,Bvs])),
+ %%io:format(" Gus :~p~n Bvs :~p~n Bus :~p~n Free:~p~n" ,[Gus,Bvs,Bus,Free]),
+ {Cs,Hvss,Frees,St4} = fun_clauses(Cs0, Vs, St3),
+ {[{clause,L,H,G,B}|Cs],[Hvs|Hvss],[Free|Frees],St4};
+fun_clauses([], _, St) -> {[],[],[],St}.
+
+%% new_fun_name(State) -> {FunName,State}.
+
+new_fun_name(#expand{func=F,arity=A,fcount=I}=St) ->
+ Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(A)
+ ++ "-fun-" ++ integer_to_list(I) ++ "-",
+ {list_to_atom(Name),St#expand{fcount=I+1}}.
+
+
+%% normalise_fields([RecDef]) -> [Field].
+%% Normalise the field definitions to always have a default value. If
+%% none has been given then use 'undefined'.
+
+normalise_fields(Fs) ->
+ map(fun ({record_field,Lf,Field}) ->
+ {record_field,Lf,Field,{atom,Lf,undefined}};
+ (F) -> F end, Fs).
+
+%% record_fields(RecordName, State)
+%% find_field(FieldName, Fields)
+
+record_fields(R, St) -> dict:fetch(R, St#expand.records).
+
+find_field(F, [{record_field,_,{atom,_,F},Val}|_]) -> {ok,Val};
+find_field(F, [_|Fs]) -> find_field(F, Fs);
+find_field(_, []) -> error.
+
+%% field_names(RecFields) -> [Name].
+%% Return a list of the field names structures.
+
+field_names(Fs) ->
+ map(fun ({record_field,_,Field,_Val}) -> Field end, Fs).
+
+%% index_expr(Line, FieldExpr, Name, Fields) -> IndexExpr.
+%% Return an expression which evaluates to the index of a
+%% field. Currently only handle the case where the field is an
+%% atom. This expansion must be passed through expr again.
+
+index_expr(Line, {atom,_,F}, _Name, Fs) ->
+ {integer,Line,index_expr(F, Fs, 2)}.
+
+index_expr(F, [{record_field,_,{atom,_,F},_}|_], I) -> I;
+index_expr(F, [_|Fs], I) ->
+ index_expr(F, Fs, I+1).
+
+%% pattern_fields([RecDefField], [Match]) -> [Pattern].
+%% Build a list of match patterns for the record tuple elements.
+%% This expansion must be passed through pattern again. N.B. We are
+%% scanning the record definition field list!
+
+pattern_fields(Fs, Ms) ->
+ Wildcard = record_wildcard_init(Ms),
+ map(fun ({record_field,L,{atom,_,F},_}) ->
+ case find_field(F, Ms) of
+ {ok,Match} -> Match;
+ error when Wildcard =:= none -> {var,L,'_'};
+ error -> Wildcard
+ end end,
+ Fs).
+
+%% record_inits([RecDefField], [Init]) -> [InitExpr].
+%% Build a list of initialisation expressions for the record tuple
+%% elements. This expansion must be passed through expr
+%% again. N.B. We are scanning the record definition field list!
+
+record_inits(Fs, Is) ->
+ WildcardInit = record_wildcard_init(Is),
+ map(fun ({record_field,_,{atom,_,F},D}) ->
+ case find_field(F, Is) of
+ {ok,Init} -> Init;
+ error when WildcardInit =:= none -> D;
+ error -> WildcardInit
+ end end,
+ Fs).
+
+record_wildcard_init([{record_field,_,{var,_,'_'},D}|_]) -> D;
+record_wildcard_init([_|Is]) -> record_wildcard_init(Is);
+record_wildcard_init([]) -> none.
+
+%% record_update(Record, RecordName, [RecDefField], [Update], State) ->
+%% {Expr,State'}
+%% Build an expression to update fields in a record returning a new
+%% record. Try to be smart and optimise this. This expansion must be
+%% passed through expr again.
+
+record_update(R, Name, Fs, Us0, St0) ->
+ Line = element(2, R),
+ {Pre,Us,St1} = record_exprs(Us0, St0),
+ Nf = length(Fs), %# of record fields
+ Nu = length(Us), %# of update fields
+ Nc = Nf - Nu, %# of copy fields
+
+ %% We need a new variable for the record expression
+ %% to guarantee that it is only evaluated once.
+ {Var,St2} = new_var(Line, St1),
+
+ %% Try to be intelligent about which method of updating record to use.
+ {Update,St} =
+ if
+ Nu == 0 -> {R,St2}; %No fields updated
+ Nu =< Nc -> %Few fields updated
+ {record_setel(Var, Name, Fs, Us), St2};
+ true -> %The wide area inbetween
+ record_match(Var, Name, Fs, Us, St2)
+ end,
+ {{block,element(2, R),Pre ++ [{match,Line,Var,R},Update]},St}.
+
+%% record_match(Record, RecordName, [RecDefField], [Update], State)
+%% Build a 'case' expression to modify record fields.
+
+record_match(R, Name, Fs, Us, St0) ->
+ {Ps,News,St1} = record_upd_fs(Fs, Us, St0),
+ Lr = element(2, hd(Us)),
+ {{'case',Lr,R,
+ [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Ps]}],[],
+ [{tuple,Lr,[{atom,Lr,Name}|News]}]},
+ {clause,Lr,[{var,Lr,'_'}],[],
+ [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}
+ ]},
+ St1}.
+
+record_upd_fs([{record_field,Lf,{atom,_La,F},_Val}|Fs], Us, St0) ->
+ {P,St1} = new_var(Lf, St0),
+ {Ps,News,St2} = record_upd_fs(Fs, Us, St1),
+ case find_field(F, Us) of
+ {ok,New} -> {[P|Ps],[New|News],St2};
+ error -> {[P|Ps],[P|News],St2}
+ end;
+record_upd_fs([], _, St) -> {[],[],St}.
+
+%% record_setel(Record, RecordName, [RecDefField], [Update])
+%% Build a nested chain of setelement calls to build the
+%% updated record tuple.
+
+record_setel(R, Name, Fs, Us0) ->
+ Us1 = foldl(fun ({record_field,Lf,Field,Val}, Acc) ->
+ I = index_expr(Lf, Field, Name, Fs),
+ [{I,Lf,Val}|Acc]
+ end, [], Us0),
+ Us = sort(Us1),
+ Lr = element(2, hd(Us)),
+ Wildcards = duplicate(length(Fs), {var,Lr,'_'}),
+ {'case',Lr,R,
+ [{clause,Lr,[{tuple,Lr,[{atom,Lr,Name}|Wildcards]}],[],
+ [foldr(fun ({I,Lf,Val}, Acc) ->
+ {call,Lf,{atom,Lf,setelement},[I,Acc,Val]} end,
+ R, Us)]},
+ {clause,Lr,[{var,Lr,'_'}],[],
+ [call_error(Lr, {tuple,Lr,[{atom,Lr,badrecord},{atom,Lr,Name}]})]}]}.
+
+%% Expand a call to record_info/2. We have checked that it is not
+%% shadowed by an import.
+
+record_info_call(Line, [{atom,_Li,Info},{atom,_Ln,Name}], St) ->
+ case Info of
+ size ->
+ {{integer,Line,1+length(record_fields(Name, St))},[],[],St};
+ fields ->
+ {make_list(field_names(record_fields(Name, St)), Line),
+ [],[],St}
+ end.
+
+%% Break out expressions from an record update list and bind to new
+%% variables. The idea is that we will evaluate all update expressions
+%% before starting to update the record.
+
+record_exprs(Us, St) ->
+ record_exprs(Us, St, [], []).
+
+record_exprs([{record_field,Lf,{atom,_La,_F}=Name,Val}=Field0|Us], St0, Pre, Fs) ->
+ case is_simple_val(Val) of
+ true ->
+ record_exprs(Us, St0, Pre, [Field0|Fs]);
+ false ->
+ {Var,St} = new_var(Lf, St0),
+ Bind = {match,Lf,Var,Val},
+ Field = {record_field,Lf,Name,Var},
+ record_exprs(Us, St, [Bind|Pre], [Field|Fs])
+ end;
+record_exprs([], St, Pre, Fs) ->
+ {reverse(Pre),Fs,St}.
+
+is_simple_val({var,_,_}) -> true;
+is_simple_val({atom,_,_}) -> true;
+is_simple_val({integer,_,_}) -> true;
+is_simple_val({float,_,_}) -> true;
+is_simple_val({nil,_}) -> true;
+is_simple_val(_) -> false.
+
+%% pattern_bin([Element], State) -> {[Element],[Variable],[UsedVar],State}.
+
+pattern_bin(Es0, St) ->
+ Es1 = bin_expand_strings(Es0),
+ foldr(fun (E, Acc) -> pattern_element(E, Acc) end, {[],[],[],St}, Es1).
+
+pattern_element({bin_element,Line,Expr,Size,Type}, {Es,Esvs,Esus,St0}) ->
+ {Expr1,Vs1,Us1,St1} = pattern(Expr, St0),
+ {Size1,Vs2,Us2,St2} = pat_bit_size(Size, St1),
+ {Size2,Type1} = make_bit_type(Line, Size1,Type),
+ {[{bin_element,Line,Expr1,Size2,Type1}|Es],
+ union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}.
+
+pat_bit_size(default, St) -> {default,[],[],St};
+pat_bit_size({atom,_La,all}=All, St) -> {All,[],[],St};
+pat_bit_size({var,_Lv,V}=Var, St) -> {Var,[],[V],St};
+pat_bit_size(Size, St) ->
+ Line = element(2, Size),
+ {value,Sz,_} = erl_eval:expr(Size, erl_eval:new_bindings()),
+ {{integer,Line,Sz},[],[],St}.
+
+make_bit_type(Line, default, Type0) ->
+ case erl_bits:set_bit_type(default, Type0) of
+ {ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)};
+ {ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)}
+ end;
+make_bit_type(_Line, Size, Type0) -> %Integer or 'all'
+ {ok,Size,Bt} = erl_bits:set_bit_type(Size, Type0),
+ {Size,erl_bits:as_list(Bt)}.
+
+%% expr_bin([Element], [VisibleVar], State) ->
+%% {[Element],[NewVar],[UsedVar],State}.
+
+expr_bin(Es0, Vs, St) ->
+ Es1 = bin_expand_strings(Es0),
+ foldr(fun (E, Acc) -> bin_element(E, Vs, Acc) end, {[],[],[],St}, Es1).
+
+bin_element({bin_element,Line,Expr,Size,Type}, Vs, {Es,Esvs,Esus,St0}) ->
+ {Expr1,Vs1,Us1,St1} = expr(Expr, Vs, St0),
+ {Size1,Vs2,Us2,St2} = if Size == default -> {default,[],[],St1};
+ true -> expr(Size, Vs, St1)
+ end,
+ {Size2,Type1} = make_bit_type(Line, Size1, Type),
+ {[{bin_element,Line,Expr1,Size2,Type1}|Es],
+ union([Vs1,Vs2,Esvs]),union([Us1,Us2,Esus]),St2}.
+
+bin_expand_strings(Es) ->
+ foldr(fun ({bin_element,Line,{string,_,S},default,default}, Es1) ->
+ foldr(fun (C, Es2) ->
+ [{bin_element,Line,{char,Line,C},default,default}|Es2]
+ end, Es1, S);
+ (E, Es1) -> [E|Es1]
+ end, [], Es).
+
+%% new_var_name(State) -> {VarName,State}.
+
+new_var_name(St) ->
+ C = St#expand.vcount,
+ {list_to_atom("pre" ++ integer_to_list(C)),St#expand{vcount=C+1}}.
+
+%% new_var(Line, State) -> {Var,State}.
+
+new_var(L, St0) ->
+ {New,St1} = new_var_name(St0),
+ {{var,L,New},St1}.
+
+%% new_vars(Count, Line, State) -> {[Var],State}.
+%% Make Count new variables.
+
+new_vars(N, L, St) -> new_vars(N, L, St, []).
+
+new_vars(N, L, St0, Vs) when N > 0 ->
+ {V,St1} = new_var(L, St0),
+ new_vars(N-1, L, St1, [V|Vs]);
+new_vars(0, _L, St, Vs) -> {Vs,St}.
+
+%% make_list(TermList, Line) -> ConsTerm.
+
+make_list(Ts, Line) ->
+ foldr(fun (H, T) -> {cons,Line,H,T} end, {nil,Line}, Ts).
+
+string_to_conses(Line, Cs, Tail) ->
+ foldr(fun (C, T) -> {cons,Line,{char,Line,C},T} end, Tail, Cs).
+
+
+%% In syntax trees, module/package names are atoms or lists of atoms.
+
+package_to_string(A) when atom(A) -> atom_to_list(A);
+package_to_string(L) when list(L) -> packages:concat(L).
+
+expand_package({atom,L,A} = M, St) ->
+ case dict:find(A, St#expand.mod_imports) of
+ {ok, A1} ->
+ {atom,L,A1};
+ error ->
+ case packages:is_segmented(A) of
+ true ->
+ M;
+ false ->
+ M1 = packages:concat(St#expand.package, A),
+ {atom,L,list_to_atom(M1)}
+ end
+ end;
+expand_package(M, _St) ->
+ case erl_parse:package_segments(M) of
+ error ->
+ M;
+ M1 ->
+ {atom,element(2,M),list_to_atom(package_to_string(M1))}
+ end.
+
+%% Create a case-switch on true/false, generating badarg for all other
+%% values.
+
+make_bool_switch(L, E, V, T, F) ->
+ make_bool_switch_1(L, E, V, [T], [F]).
+
+make_bool_switch_1(L, E, V, T, F) ->
+ case get(sys_pre_expand_in_guard) of
+ undefined -> make_bool_switch_body(L, E, V, T, F);
+ yes -> make_bool_switch_guard(L, E, V, T, F)
+ end.
+
+make_bool_switch_guard(_, E, _, [{atom,_,true}], [{atom,_,false}]) -> E;
+make_bool_switch_guard(L, E, V, T, F) ->
+ NegL = -abs(L),
+ {'case',NegL,E,
+ [{clause,NegL,[{atom,NegL,true}],[],T},
+ {clause,NegL,[{atom,NegL,false}],[],F},
+ {clause,NegL,[V],[],[V]}
+ ]}.
+
+make_bool_switch_body(L, E, V, T, F) ->
+ NegL = -abs(L),
+ {'case',NegL,E,
+ [{clause,NegL,[{atom,NegL,true}],[],T},
+ {clause,NegL,[{atom,NegL,false}],[],F},
+ {clause,NegL,[V],[],
+ [call_error(NegL,{tuple,NegL,[{atom,NegL,badarg},V]})]}
+ ]}.
+
+%% Expand a list of cond-clauses to a sequence of case-switches.
+
+cond_clauses([{clause,L,[],[[E]],B}],V) ->
+ make_bool_switch_1(L,E,V,B,[call_error(L,{atom,L,cond_clause})]);
+cond_clauses([{clause,L,[],[[E]],B} | Cs],V) ->
+ make_bool_switch_1(L,E,V,B,[cond_clauses(Cs,V)]).
+
+%% call_error(Line, Reason) -> Expr.
+%% Build a call to erlang:error/1 with reason Reason.
+
+call_error(L, R) ->
+ {call,L,{remote,L,{atom,L,erlang},{atom,L,error}},[R]}.
+
+%% new_in_all(Before, RegionList) -> NewInAll
+%% Return the variables new in all clauses.
+
+new_in_all(Before, Region) ->
+ InAll = intersection(Region),
+ subtract(InAll, Before).
+
+%% import(Line, Imports, State) ->
+%% State'
+%% imported(Name, Arity, State) ->
+%% {yes,Module} | no
+%% Handle import declarations and est for imported functions. No need to
+%% check when building imports as code is correct.
+
+import({Mod0,Fs}, St) ->
+ Mod = list_to_atom(package_to_string(Mod0)),
+ Mfs = from_list(Fs),
+ St#expand{imports=add_imports(Mod, Mfs, St#expand.imports)};
+import(Mod0, St) ->
+ Mod = package_to_string(Mod0),
+ Key = list_to_atom(packages:last(Mod)),
+ St#expand{mod_imports=dict:store(Key, list_to_atom(Mod),
+ St#expand.mod_imports)}.
+
+add_imports(Mod, [F|Fs], Is) ->
+ add_imports(Mod, Fs, orddict:store(F, Mod, Is));
+add_imports(_, [], Is) -> Is.
+
+imported(F, A, St) ->
+ case orddict:find({F,A}, St#expand.imports) of
+ {ok,Mod} -> {yes,Mod};
+ error -> no
+ end.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl
new file mode 100644
index 0000000000..2af4d94655
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_codegen.erl
@@ -0,0 +1,1755 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_codegen.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Code generator for Beam.
+
+%% The following assumptions have been made:
+%%
+%% 1. Matches, i.e. things with {match,M,Ret} wrappers, only return
+%% values; no variables are exported. If the match would have returned
+%% extra variables then these have been transformed to multiple return
+%% values.
+%%
+%% 2. All BIF's called in guards are gc-safe so there is no need to
+%% put thing on the stack in the guard. While this would in principle
+%% work it would be difficult to keep track of the stack depth when
+%% trimming.
+%%
+%% The code generation uses variable lifetime information added by
+%% the v3_life module to save variables, allocate registers and
+%% move registers to the stack when necessary.
+%%
+%% We try to use a consistent variable name scheme throughout. The
+%% StackReg record is always called Bef,Int<n>,Aft.
+
+-module(v3_codegen).
+
+%% The main interface.
+-export([module/2]).
+
+-import(lists, [member/2,keymember/3,keysort/2,keysearch/3,append/1,
+ map/2,flatmap/2,foldl/3,foldr/3,mapfoldl/3,
+ sort/1,reverse/1,reverse/2]).
+-import(v3_life, [vdb_find/2]).
+
+%%-compile([export_all]).
+
+-include("v3_life.hrl").
+
+%% Main codegen structure.
+-record(cg, {lcount=1, %Label counter
+ mod, %Current module
+ func, %Current function
+ finfo, %Function info label
+ fcode, %Function code label
+ btype, %Type of bif used.
+ bfail, %Fail label of bif
+ break, %Break label
+ recv, %Receive label
+ is_top_block, %Boolean: top block or not
+ functable = [], %Table of local functions:
+ %[{{Name, Arity}, Label}...]
+ in_catch=false, %Inside a catch or not.
+ need_frame, %Need a stack frame.
+ new_funs=true}). %Generate new fun instructions.
+
+%% Stack/register state record.
+-record(sr, {reg=[], %Register table
+ stk=[], %Stack table
+ res=[]}). %Reserved regs: [{reserved,I,V}]
+
+module({Mod,Exp,Attr,Forms}, Options) ->
+ NewFunsFlag = not member(no_new_funs, Options),
+ {Fs,St} = functions(Forms, #cg{mod=Mod,new_funs=NewFunsFlag}),
+ {ok,{Mod,Exp,Attr,Fs,St#cg.lcount}}.
+
+functions(Forms, St0) ->
+ mapfoldl(fun (F, St) -> function(F, St) end, St0#cg{lcount=1}, Forms).
+
+function({function,Name,Arity,As0,Vb,Vdb}, St0) ->
+ %%ok = io:fwrite("cg ~w:~p~n", [?LINE,{Name,Arity}]),
+ St1 = St0#cg{func={Name,Arity}},
+ {Fun,St2} = cg_fun(Vb, As0, Vdb, St1),
+ Func0 = {function,Name,Arity,St2#cg.fcode,Fun},
+ Func = bs_function(Func0),
+ {Func,St2}.
+
+%% cg_fun([Lkexpr], [HeadVar], Vdb, State) -> {[Ainstr],State}
+
+cg_fun(Les, Hvs, Vdb, St0) ->
+ {Name,Arity} = St0#cg.func,
+ {Fi,St1} = new_label(St0), %FuncInfo label
+ {Fl,St2} = local_func_label(Name, Arity, St1),
+ %% Create initial stack/register state, clear unused arguments.
+ Bef = clear_dead(#sr{reg=foldl(fun ({var,V}, Reg) ->
+ put_reg(V, Reg)
+ end, [], Hvs),
+ stk=[]}, 0, Vdb),
+ {B2,_Aft,St3} = cg_list(Les, 0, Vdb, Bef, St2#cg{btype=exit,
+ bfail=Fi,
+ finfo=Fi,
+ fcode=Fl,
+ is_top_block=true}),
+ A = [{label,Fi},{func_info,{atom,St3#cg.mod},{atom,Name},Arity},
+ {label,Fl}|B2],
+ {A,St3}.
+
+%% cg(Lkexpr, Vdb, StackReg, State) -> {[Ainstr],StackReg,State}.
+%% Generate code for a kexpr.
+%% Split function into two steps for clarity, not efficiency.
+
+cg(Le, Vdb, Bef, St) ->
+ cg(Le#l.ke, Le, Vdb, Bef, St).
+
+cg({block,Es}, Le, Vdb, Bef, St) ->
+ block_cg(Es, Le, Vdb, Bef, St);
+cg({match,M,Rs}, Le, Vdb, Bef, St) ->
+ match_cg(M, Rs, Le, Vdb, Bef, St);
+cg({match_fail,F}, Le, Vdb, Bef, St) ->
+ match_fail_cg(F, Le, Vdb, Bef, St);
+cg({call,Func,As,Rs}, Le, Vdb, Bef, St) ->
+ call_cg(Func, As, Rs, Le, Vdb, Bef, St);
+cg({enter,Func,As}, Le, Vdb, Bef, St) ->
+ enter_cg(Func, As, Le, Vdb, Bef, St);
+cg({bif,Bif,As,Rs}, Le, Vdb, Bef, St) ->
+ bif_cg(Bif, As, Rs, Le, Vdb, Bef, St);
+cg({receive_loop,Te,Rvar,Rm,Tes,Rs}, Le, Vdb, Bef, St) ->
+ recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St);
+cg(receive_next, Le, Vdb, Bef, St) ->
+ recv_next_cg(Le, Vdb, Bef, St);
+cg(receive_accept, _Le, _Vdb, Bef, St) -> {[remove_message],Bef,St};
+cg({'try',Ta,Vs,Tb,Evs,Th,Rs}, Le, Vdb, Bef, St) ->
+ try_cg(Ta, Vs, Tb, Evs, Th, Rs, Le, Vdb, Bef, St);
+cg({'catch',Cb,R}, Le, Vdb, Bef, St) ->
+ catch_cg(Cb, R, Le, Vdb, Bef, St);
+cg({set,Var,Con}, Le, Vdb, Bef, St) -> set_cg(Var, Con, Le, Vdb, Bef, St);
+cg({return,Rs}, Le, Vdb, Bef, St) -> return_cg(Rs, Le, Vdb, Bef, St);
+cg({break,Bs}, Le, Vdb, Bef, St) -> break_cg(Bs, Le, Vdb, Bef, St);
+cg({need_heap,0}, _Le, _Vdb, Bef, St) ->
+ {[],Bef,St};
+cg({need_heap,H}, _Le, _Vdb, Bef, St) ->
+ {[{test_heap,H,max_reg(Bef#sr.reg)}],Bef,St}.
+
+%% cg_list([Kexpr], FirstI, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+
+cg_list(Kes, I, Vdb, Bef, St0) ->
+ {Keis,{Aft,St1}} =
+ flatmapfoldl(fun (Ke, {Inta,Sta}) ->
+% ok = io:fwrite(" %% ~p\n", [Inta]),
+% ok = io:fwrite("cgl:~p\n", [Ke]),
+ {Keis,Intb,Stb} = cg(Ke, Vdb, Inta, Sta),
+% ok = io:fwrite(" ~p\n", [Keis]),
+% ok = io:fwrite(" %% ~p\n", [Intb]),
+ {comment(Inta) ++ Keis,{Intb,Stb}}
+ end, {Bef,St0}, need_heap(Kes, I)),
+ {Keis,Aft,St1}.
+
+%% need_heap([Lkexpr], I, BifType) -> [Lkexpr].
+%% Insert need_heap instructions in Kexpr list. Try to be smart and
+%% collect them together as much as possible.
+
+need_heap(Kes0, I) ->
+ {Kes1,{H,F}} = flatmapfoldr(fun (Ke, {H0,F0}) ->
+ {Ns,H1,F1} = need_heap_1(Ke, H0, F0),
+ {[Ke|Ns],{H1,F1}}
+ end, {0,false}, Kes0),
+ %% Prepend need_heap if necessary.
+ Kes2 = need_heap_need(I, H, F) ++ Kes1,
+% ok = io:fwrite("need_heap: ~p~n",
+% [{{H,F},
+% map(fun (#l{ke={match,M,Rs}}) -> match;
+% (Lke) -> Lke#l.ke end, Kes2)}]),
+ Kes2.
+
+need_heap_1(#l{ke={set,_,{binary,_}},i=I}, H, F) ->
+ {need_heap_need(I, H, F),0,false};
+need_heap_1(#l{ke={set,_,Val}}, H, F) ->
+ %% Just pass through adding to needed heap.
+ {[],H + case Val of
+ {cons,_} -> 2;
+ {tuple,Es} -> 1 + length(Es);
+ {string,S} -> 2 * length(S);
+ _Other -> 0
+ end,F};
+need_heap_1(#l{ke={call,_Func,_As,_Rs},i=I}, H, F) ->
+ %% Calls generate a need if necessary and also force one.
+ {need_heap_need(I, H, F),0,true};
+need_heap_1(#l{ke={bif,dsetelement,_As,_Rs},i=I}, H, F) ->
+ {need_heap_need(I, H, F),0,true};
+need_heap_1(#l{ke={bif,{make_fun,_,_,_,_},_As,_Rs},i=I}, H, F) ->
+ {need_heap_need(I, H, F),0,true};
+need_heap_1(#l{ke={bif,_Bif,_As,_Rs}}, H, F) ->
+ {[],H,F};
+need_heap_1(#l{i=I}, H, F) ->
+ %% Others kexprs generate a need if necessary but don't force.
+ {need_heap_need(I, H, F),0,false}.
+
+need_heap_need(_I, 0, false) -> [];
+need_heap_need(I, H, _F) -> [#l{ke={need_heap,H},i=I}].
+
+
+%% match_cg(Match, [Ret], Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% Generate code for a match. First save all variables on the stack
+%% that are to survive after the match. We leave saved variables in
+%% their registers as they might actually be in the right place.
+%% Should test this.
+
+match_cg(M, Rs, Le, Vdb, Bef, St0) ->
+ I = Le#l.i,
+ {Sis,Int0} = adjust_stack(Bef, I, I+1, Vdb),
+ {B,St1} = new_label(St0),
+ {Mis,Int1,St2} = match_cg(M, none, Int0, St1#cg{break=B}),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, Int1#sr.reg),
+ {Sis ++ Mis ++ [{label,B}],
+ clear_dead(Int1#sr{reg=Reg}, I, Vdb),
+ St2#cg{break=St1#cg.break}}.
+
+%% match_cg(Match, Fail, StackReg, State) -> {[Ainstr],StackReg,State}.
+%% Generate code for a match tree. N.B. there is no need pass Vdb
+%% down as each level which uses this takes its own internal Vdb not
+%% the outer one.
+
+match_cg(Le, Fail, Bef, St) ->
+ match_cg(Le#l.ke, Le, Fail, Bef, St).
+
+match_cg({alt,F,S}, _Le, Fail, Bef, St0) ->
+ {Tf,St1} = new_label(St0),
+ {Fis,Faft,St2} = match_cg(F, Tf, Bef, St1),
+ {Sis,Saft,St3} = match_cg(S, Fail, Bef, St2),
+ Aft = sr_merge(Faft, Saft),
+ {Fis ++ [{label,Tf}] ++ Sis,Aft,St3};
+match_cg({select,V,Scs}, _Va, Fail, Bef, St) ->
+ match_fmf(fun (S, F, Sta) ->
+ select_cg(S, V, F, Fail, Bef, Sta) end,
+ Fail, St, Scs);
+match_cg({guard,Gcs}, _Le, Fail, Bef, St) ->
+ match_fmf(fun (G, F, Sta) -> guard_clause_cg(G, F, Bef, Sta) end,
+ Fail, St, Gcs);
+match_cg({block,Es}, Le, _Fail, Bef, St) ->
+ %% Must clear registers and stack of dead variables.
+ Int = clear_dead(Bef, Le#l.i, Le#l.vdb),
+ block_cg(Es, Le, Int, St).
+
+%% match_fail_cg(FailReason, Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% Generate code for the match_fail "call". N.B. there is no generic
+%% case for when the fail value has been created elsewhere.
+
+match_fail_cg({function_clause,As}, Le, Vdb, Bef, St) ->
+ %% Must have the args in {x,0}, {x,1},...
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ {Sis ++ [{jump,{f,St#cg.finfo}}],
+ Int#sr{reg=clear_regs(Int#sr.reg)},St};
+match_fail_cg({badmatch,Term}, Le, Vdb, Bef, St) ->
+ R = cg_reg_arg(Term, Bef),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis ++ [{badmatch,R}],
+ Int#sr{reg=clear_regs(Int0#sr.reg)},St};
+match_fail_cg({case_clause,Reason}, Le, Vdb, Bef, St) ->
+ R = cg_reg_arg(Reason, Bef),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis++[{case_end,R}],
+ Int#sr{reg=clear_regs(Bef#sr.reg)},St};
+match_fail_cg(if_clause, Le, Vdb, Bef, St) ->
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int1} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis++[if_end],Int1#sr{reg=clear_regs(Int1#sr.reg)},St};
+match_fail_cg({try_clause,Reason}, Le, Vdb, Bef, St) ->
+ R = cg_reg_arg(Reason, Bef),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ {Sis,Int} = adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb),
+ {Sis ++ [{try_case_end,R}],
+ Int#sr{reg=clear_regs(Int0#sr.reg)},St}.
+
+
+%% block_cg([Kexpr], Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+%% block_cg([Kexpr], Le, StackReg, St) -> {[Ainstr],StackReg,St}.
+
+block_cg(Es, Le, _Vdb, Bef, St) ->
+ block_cg(Es, Le, Bef, St).
+
+block_cg(Es, Le, Bef, St0) ->
+ case St0#cg.is_top_block of
+ false ->
+ cg_block(Es, Le#l.i, Le#l.vdb, Bef, St0);
+ true ->
+ {Keis,Aft,St1} = cg_block(Es, Le#l.i, Le#l.vdb, Bef,
+ St0#cg{is_top_block=false,
+ need_frame=false}),
+ top_level_block(Keis, Aft, max_reg(Bef#sr.reg), St1)
+ end.
+
+cg_block([], _I, _Vdb, Bef, St0) ->
+ {[],Bef,St0};
+cg_block(Kes0, I, Vdb, Bef, St0) ->
+ {Kes2,Int1,St1} =
+ case basic_block(Kes0) of
+ {Kes1,LastI,Args,Rest} ->
+ Ke = hd(Kes1),
+ Fb = Ke#l.i,
+ cg_basic_block(Kes1, Fb, LastI, Args, Vdb, Bef, St0);
+ {Kes1,Rest} ->
+ cg_list(Kes1, I, Vdb, Bef, St0)
+ end,
+ {Kes3,Int2,St2} = cg_block(Rest, I, Vdb, Int1, St1),
+ {Kes2 ++ Kes3,Int2,St2}.
+
+basic_block(Kes) -> basic_block(Kes, []).
+
+basic_block([], Acc) -> {reverse(Acc),[]};
+basic_block([Le|Les], Acc) ->
+ case collect_block(Le#l.ke) of
+ include -> basic_block(Les, [Le|Acc]);
+ {block_end,As} -> {reverse(Acc, [Le]),Le#l.i,As,Les};
+ no_block -> {reverse(Acc, [Le]),Les}
+ end.
+
+collect_block({set,_,{binary,_}}) -> no_block;
+collect_block({set,_,_}) -> include;
+collect_block({call,{var,_}=Var,As,_Rs}) -> {block_end,As++[Var]};
+collect_block({call,Func,As,_Rs}) -> {block_end,As++func_vars(Func)};
+collect_block({enter,{var,_}=Var,As})-> {block_end,As++[Var]};
+collect_block({enter,Func,As}) -> {block_end,As++func_vars(Func)};
+collect_block({return,Rs}) -> {block_end,Rs};
+collect_block({break,Bs}) -> {block_end,Bs};
+collect_block({bif,_Bif,_As,_Rs}) -> include;
+collect_block(_) -> no_block.
+
+func_vars({remote,M,F}) when element(1, M) == var;
+ element(1, F) == var ->
+ [M,F];
+func_vars(_) -> [].
+
+%% cg_basic_block([Kexpr], FirstI, LastI, As, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+
+cg_basic_block(Kes, Fb, Lf, As, Vdb, Bef, St0) ->
+ Res = make_reservation(As, 0),
+ Regs0 = reserve(Res, Bef#sr.reg, Bef#sr.stk),
+ Stk = extend_stack(Bef, Lf, Lf+1, Vdb),
+ Int0 = Bef#sr{reg=Regs0,stk=Stk,res=Res},
+ X0_v0 = x0_vars(As, Fb, Lf, Vdb),
+ {Keis,{Aft,_,St1}} =
+ flatmapfoldl(fun(Ke, St) -> cg_basic_block(Ke, St, Lf, Vdb) end,
+ {Int0,X0_v0,St0}, need_heap(Kes, Fb)),
+ {Keis,Aft,St1}.
+
+cg_basic_block(Ke, {Inta,X0v,Sta}, _Lf, Vdb) when element(1, Ke#l.ke) =:= need_heap ->
+ {Keis,Intb,Stb} = cg(Ke, Vdb, Inta, Sta),
+ {comment(Inta) ++ Keis, {Intb,X0v,Stb}};
+cg_basic_block(Ke, {Inta,X0_v1,Sta}, Lf, Vdb) ->
+ {Sis,Intb} = save_carefully(Inta, Ke#l.i, Lf+1, Vdb),
+ {X0_v2,Intc} = allocate_x0(X0_v1, Ke#l.i, Intb),
+ Intd = reserve(Intc),
+ {Keis,Inte,Stb} = cg(Ke, Vdb, Intd, Sta),
+ {comment(Inta) ++ Sis ++ Keis, {Inte,X0_v2,Stb}}.
+
+make_reservation([], _) -> [];
+make_reservation([{var,V}|As], I) -> [{I,V}|make_reservation(As, I+1)];
+make_reservation([A|As], I) -> [{I,A}|make_reservation(As, I+1)].
+
+reserve(Sr) -> Sr#sr{reg=reserve(Sr#sr.res, Sr#sr.reg, Sr#sr.stk)}.
+
+reserve([{I,V}|Rs], [free|Regs], Stk) -> [{reserved,I,V}|reserve(Rs, Regs, Stk)];
+reserve([{I,V}|Rs], [{I,V}|Regs], Stk) -> [{I,V}|reserve(Rs, Regs, Stk)];
+reserve([{I,V}|Rs], [{I,Var}|Regs], Stk) ->
+ case on_stack(Var, Stk) of
+ true -> [{reserved,I,V}|reserve(Rs, Regs, Stk)];
+ false -> [{I,Var}|reserve(Rs, Regs, Stk)]
+ end;
+reserve([{I,V}|Rs], [{reserved,I,_}|Regs], Stk) ->
+ [{reserved,I,V}|reserve(Rs, Regs, Stk)];
+%reserve([{I,V}|Rs], [Other|Regs], Stk) -> [Other|reserve(Rs, Regs, Stk)];
+reserve([{I,V}|Rs], [], Stk) -> [{reserved,I,V}|reserve(Rs, [], Stk)];
+reserve([], Regs, _) -> Regs.
+
+extend_stack(Bef, Fb, Lf, Vdb) ->
+ Stk0 = clear_dead_stk(Bef#sr.stk, Fb, Vdb),
+ Saves = [V || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk0)],
+ Stk1 = foldl(fun (V, Stk) -> put_stack(V, Stk) end, Stk0, Saves),
+ Bef#sr.stk ++ lists:duplicate(length(Stk1) - length(Bef#sr.stk), free).
+
+save_carefully(Bef, Fb, Lf, Vdb) ->
+ Stk = Bef#sr.stk,
+ %% New variables that are in use but not on stack.
+ New = [ {V,F,L} || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk) ],
+ Saves = [ V || {V,_,_} <- keysort(2, New) ],
+ save_carefully(Saves, Bef, []).
+
+save_carefully([], Bef, Acc) -> {reverse(Acc),Bef};
+save_carefully([V|Vs], Bef, Acc) ->
+ case put_stack_carefully(V, Bef#sr.stk) of
+ error -> {reverse(Acc),Bef};
+ Stk1 ->
+ SrcReg = fetch_reg(V, Bef#sr.reg),
+ Move = {move,SrcReg,fetch_stack(V, Stk1)},
+ {x,_} = SrcReg, %Assertion - must be X register.
+ save_carefully(Vs, Bef#sr{stk=Stk1}, [Move|Acc])
+ end.
+
+x0_vars([], _Fb, _Lf, _Vdb) -> [];
+x0_vars([{var,V}|_], Fb, _Lf, Vdb) ->
+ {V,F,_L} = VFL = vdb_find(V, Vdb),
+ x0_vars1([VFL], Fb, F, Vdb);
+x0_vars([X0|_], Fb, Lf, Vdb) ->
+ x0_vars1([{X0,Lf,Lf}], Fb, Lf, Vdb).
+
+x0_vars1(X0, Fb, Xf, Vdb) ->
+ Vs0 = [VFL || {_V,F,L}=VFL <- Vdb,
+ F >= Fb,
+ L < Xf],
+ Vs1 = keysort(3, Vs0),
+ keysort(2, X0++Vs1).
+
+allocate_x0([], _, Bef) -> {[],Bef#sr{res=[]}};
+allocate_x0([{_,_,L}|Vs], I, Bef) when L =< I ->
+ allocate_x0(Vs, I, Bef);
+allocate_x0([{V,_F,_L}=VFL|Vs], _, Bef) ->
+ {[VFL|Vs],Bef#sr{res=reserve_x0(V, Bef#sr.res)}}.
+
+reserve_x0(V, [_|Res]) -> [{0,V}|Res];
+reserve_x0(V, []) -> [{0,V}].
+
+top_level_block(Keis, Bef, _MaxRegs, St0) when St0#cg.need_frame =:= false,
+ length(Bef#sr.stk) =:= 0 ->
+ %% This block need no stack frame. However, we still need to turn the
+ %% stack frame upside down.
+ MaxY = length(Bef#sr.stk)-1,
+ Keis1 = flatmap(fun (Tuple) when tuple(Tuple) ->
+ [turn_yregs(size(Tuple), Tuple, MaxY)];
+ (Other) ->
+ [Other]
+ end, Keis),
+ {Keis1, Bef, St0#cg{is_top_block=true}};
+top_level_block(Keis, Bef, MaxRegs, St0) ->
+ %% This top block needs an allocate instruction before it, and a
+ %% deallocate instruction before each return.
+ FrameSz = length(Bef#sr.stk),
+ MaxY = FrameSz-1,
+ Keis1 = flatmap(fun ({call_only,Arity,Func}) ->
+ [{call_last,Arity,Func,FrameSz}];
+ ({call_ext_only,Arity,Func}) ->
+ [{call_ext_last,Arity,Func,FrameSz}];
+ ({apply_only,Arity}) ->
+ [{apply_last,Arity,FrameSz}];
+ (return) ->
+ [{deallocate,FrameSz}, return];
+ (Tuple) when tuple(Tuple) ->
+ [turn_yregs(size(Tuple), Tuple, MaxY)];
+ (Other) ->
+ [Other]
+ end, Keis),
+ {[{allocate_zero,FrameSz,MaxRegs}|Keis1], Bef, St0#cg{is_top_block=true}}.
+
+%% turn_yregs(Size, Tuple, MaxY) -> Tuple'
+%% Renumber y register so that {y, 0} becomes {y, FrameSize-1},
+%% {y, FrameSize-1} becomes {y, 0} and so on. This is to make nested
+%% catches work. The code generation algorithm gives a lower register
+%% number to the outer catch, which is wrong.
+
+turn_yregs(0, Tp, _) -> Tp;
+turn_yregs(El, Tp, MaxY) when element(1, element(El, Tp)) == yy ->
+ turn_yregs(El-1, setelement(El, Tp, {y,MaxY-element(2, element(El, Tp))}), MaxY);
+turn_yregs(El, Tp, MaxY) when list(element(El, Tp)) ->
+ New = map(fun ({yy,YY}) -> {y,MaxY-YY};
+ (Other) -> Other end, element(El, Tp)),
+ turn_yregs(El-1, setelement(El, Tp, New), MaxY);
+turn_yregs(El, Tp, MaxY) ->
+ turn_yregs(El-1, Tp, MaxY).
+
+%% select_cg(Sclause, V, TypeFail, ValueFail, StackReg, State) ->
+%% {Is,StackReg,State}.
+%% Selecting type and value needs two failure labels, TypeFail is the
+%% label to jump to of the next type test when this type fails, and
+%% ValueFail is the label when this type is correct but the value is
+%% wrong. These are different as in the second case there is no need
+%% to try the next type, it will always fail.
+
+select_cg(#l{ke={type_clause,cons,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_cons(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,nil,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_nil(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,binary,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_binary(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,bin_seg,S}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_bin_segs(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,bin_end,[S]}}, {var,V}, Tf, Vf, Bef, St) ->
+ select_bin_end(S, V, Tf, Vf, Bef, St);
+select_cg(#l{ke={type_clause,Type,Scs}}, {var,V}, Tf, Vf, Bef, St0) ->
+ {Vis,{Aft,St1}} =
+ mapfoldl(fun (S, {Int,Sta}) ->
+ {Val,Is,Inta,Stb} = select_val(S, V, Vf, Bef, Sta),
+ {{Is,[Val]},{sr_merge(Int, Inta),Stb}}
+ end, {void,St0}, Scs),
+ OptVls = combine(lists:sort(combine(Vis))),
+ {Vls,Sis,St2} = select_labels(OptVls, St1, [], []),
+ {select_val_cg(Type, fetch_var(V, Bef), Vls, Tf, Vf, Sis), Aft, St2}.
+
+select_val_cg(tuple, R, [Arity,{f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
+ [{test,is_tuple,{f,Tf},[R]},{test,test_arity,{f,Vf},[R,Arity]}|Sis];
+select_val_cg(tuple, R, Vls, Tf, Vf, Sis) ->
+ [{test,is_tuple,{f,Tf},[R]},{select_tuple_arity,R,{f,Vf},{list,Vls}}|Sis];
+select_val_cg(Type, R, [Val, {f,Lbl}], Fail, Fail, [{label,Lbl}|Sis]) ->
+ [{test,is_eq_exact,{f,Fail},[R,{Type,Val}]}|Sis];
+select_val_cg(Type, R, [Val, {f,Lbl}], Tf, Vf, [{label,Lbl}|Sis]) ->
+ [{test,select_type_test(Type),{f,Tf},[R]},
+ {test,is_eq_exact,{f,Vf},[R,{Type,Val}]}|Sis];
+select_val_cg(Type, R, Vls0, Tf, Vf, Sis) ->
+ Vls1 = map(fun ({f,Lbl}) -> {f,Lbl};
+ (Value) -> {Type,Value}
+ end, Vls0),
+ [{test,select_type_test(Type),{f,Tf},[R]}, {select_val,R,{f,Vf},{list,Vls1}}|Sis].
+
+select_type_test(tuple) -> is_tuple;
+select_type_test(integer) -> is_integer;
+select_type_test(atom) -> is_atom;
+select_type_test(float) -> is_float.
+
+combine([{Is,Vs1}, {Is,Vs2}|Vis]) -> combine([{Is,Vs1 ++ Vs2}|Vis]);
+combine([V|Vis]) -> [V|combine(Vis)];
+combine([]) -> [].
+
+select_labels([{Is,Vs}|Vis], St0, Vls, Sis) ->
+ {Lbl,St1} = new_label(St0),
+ select_labels(Vis, St1, add_vls(Vs, Lbl, Vls), [[{label,Lbl}|Is]|Sis]);
+select_labels([], St, Vls, Sis) ->
+ {Vls,append(Sis),St}.
+
+add_vls([V|Vs], Lbl, Acc) ->
+ add_vls(Vs, Lbl, [V, {f,Lbl}|Acc]);
+add_vls([], _, Acc) -> Acc.
+
+select_cons(#l{ke={val_clause,{cons,Es},B},i=I,vdb=Vdb}, V, Tf, Vf, Bef, St0) ->
+ {Eis,Int,St1} = select_extract_cons(V, Es, I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Vf, Int, St1),
+ {[{test,is_nonempty_list,{f,Tf},[fetch_var(V, Bef)]}] ++ Eis ++ Bis,Aft,St2}.
+
+select_nil(#l{ke={val_clause,nil,B}}, V, Tf, Vf, Bef, St0) ->
+ {Bis,Aft,St1} = match_cg(B, Vf, Bef, St0),
+ {[{test,is_nil,{f,Tf},[fetch_var(V, Bef)]}] ++ Bis,Aft,St1}.
+
+select_binary(#l{ke={val_clause,{old_binary,Var},B}}=L,
+ V, Tf, Vf, Bef, St) ->
+ %% Currently handled in the same way as new binaries.
+ select_binary(L#l{ke={val_clause,{binary,Var},B}}, V, Tf, Vf, Bef, St);
+select_binary(#l{ke={val_clause,{binary,{var,Ivar}},B},i=I,vdb=Vdb},
+ V, Tf, Vf, Bef, St0) ->
+ Int0 = clear_dead(Bef, I, Vdb),
+ {Bis,Aft,St1} = match_cg(B, Vf, Int0, St0),
+ {[{test,bs_start_match,{f,Tf},[fetch_var(V, Bef)]},{bs_save,Ivar}|Bis],
+ Aft,St1}.
+
+select_bin_segs(Scs, Ivar, Tf, _Vf, Bef, St) ->
+ match_fmf(fun(S, Fail, Sta) ->
+ select_bin_seg(S, Ivar, Fail, Bef, Sta) end,
+ Tf, St, Scs).
+
+select_bin_seg(#l{ke={val_clause,{bin_seg,Size,U,T,Fs,Es},B},i=I,vdb=Vdb},
+ Ivar, Fail, Bef, St0) ->
+ {Mis,Int,St1} = select_extract_bin(Es, Size, U, T, Fs, Fail,
+ I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
+ {[{bs_restore,Ivar}|Mis] ++ Bis,Aft,St2}.
+
+select_extract_bin([{var,Hd},{var,Tl}], Size0, Unit, Type, Flags, Vf,
+ I, Vdb, Bef, St) ->
+ SizeReg = get_bin_size_reg(Size0, Bef),
+ {Es,Aft} =
+ case vdb_find(Hd, Vdb) of
+ {_,_,Lhd} when Lhd =< I ->
+ {[{test,bs_skip_bits,{f,Vf},[SizeReg,Unit,{field_flags,Flags}]},
+ {bs_save,Tl}],Bef};
+ {_,_,_} ->
+ Reg0 = put_reg(Hd, Bef#sr.reg),
+ Int1 = Bef#sr{reg=Reg0},
+ Rhd = fetch_reg(Hd, Reg0),
+ Name = get_bits_instr(Type),
+ {[{test,Name,{f,Vf},[SizeReg,Unit,{field_flags,Flags},Rhd]},
+ {bs_save,Tl}],Int1}
+ end,
+ {Es,clear_dead(Aft, I, Vdb),St}.
+
+get_bin_size_reg({var,V}, Bef) ->
+ fetch_var(V, Bef);
+get_bin_size_reg(Literal, _Bef) ->
+ Literal.
+
+select_bin_end(#l{ke={val_clause,bin_end,B}},
+ Ivar, Tf, Vf, Bef, St0) ->
+ {Bis,Aft,St2} = match_cg(B, Vf, Bef, St0),
+ {[{bs_restore,Ivar},{test,bs_test_tail,{f,Tf},[0]}|Bis],Aft,St2}.
+
+get_bits_instr(integer) -> bs_get_integer;
+get_bits_instr(float) -> bs_get_float;
+get_bits_instr(binary) -> bs_get_binary.
+
+select_val(#l{ke={val_clause,{tuple,Es},B},i=I,vdb=Vdb}, V, Vf, Bef, St0) ->
+ {Eis,Int,St1} = select_extract_tuple(V, Es, I, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Vf, Int, St1),
+ {length(Es),Eis ++ Bis,Aft,St2};
+select_val(#l{ke={val_clause,{_,Val},B}}, _V, Vf, Bef, St0) ->
+ {Bis,Aft,St1} = match_cg(B, Vf, Bef, St0),
+ {Val,Bis,Aft,St1}.
+
+%% select_extract_tuple(Src, [V], I, Vdb, StackReg, State) ->
+%% {[E],StackReg,State}.
+%% Extract tuple elements, but only if they do not immediately die.
+
+select_extract_tuple(Src, Vs, I, Vdb, Bef, St) ->
+ F = fun ({var,V}, {Int0,Elem}) ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L =< I -> {[], {Int0,Elem+1}};
+ _Other ->
+ Reg1 = put_reg(V, Int0#sr.reg),
+ Int1 = Int0#sr{reg=Reg1},
+ Rsrc = fetch_var(Src, Int1),
+ {[{get_tuple_element,Rsrc,Elem,fetch_reg(V, Reg1)}],
+ {Int1,Elem+1}}
+ end
+ end,
+ {Es,{Aft,_}} = flatmapfoldl(F, {Bef,0}, Vs),
+ {Es,Aft,St}.
+
+select_extract_cons(Src, [{var,Hd}, {var,Tl}], I, Vdb, Bef, St) ->
+ {Es,Aft} = case {vdb_find(Hd, Vdb), vdb_find(Tl, Vdb)} of
+ {{_,_,Lhd}, {_,_,Ltl}} when Lhd =< I, Ltl =< I ->
+ %% Both head and tail are dead. No need to generate
+ %% any instruction.
+ {[], Bef};
+ _ ->
+ %% At least one of head and tail will be used,
+ %% but we must always fetch both. We will call
+ %% clear_dead/2 to allow reuse of the register
+ %% in case only of them is used.
+
+ Reg0 = put_reg(Tl, put_reg(Hd, Bef#sr.reg)),
+ Int0 = Bef#sr{reg=Reg0},
+ Rsrc = fetch_var(Src, Int0),
+ Rhd = fetch_reg(Hd, Reg0),
+ Rtl = fetch_reg(Tl, Reg0),
+ Int1 = clear_dead(Int0, I, Vdb),
+ {[{get_list,Rsrc,Rhd,Rtl}], Int1}
+ end,
+ {Es,Aft,St}.
+
+
+guard_clause_cg(#l{ke={guard_clause,G,B},vdb=Vdb}, Fail, Bef, St0) ->
+ {Gis,Int,St1} = guard_cg(G, Fail, Vdb, Bef, St0),
+ {Bis,Aft,St2} = match_cg(B, Fail, Int, St1),
+ {Gis ++ Bis,Aft,St2}.
+
+%% guard_cg(Guard, Fail, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% A guard is a boolean expression of tests. Tests return true or
+%% false. A fault in a test causes the test to return false. Tests
+%% never return the boolean, instead we generate jump code to go to
+%% the correct exit point. Primops and tests all go to the next
+%% instruction on success or jump to a failure label.
+
+guard_cg(#l{ke={protected,Ts,Rs},i=I,vdb=Pdb}, Fail, _Vdb, Bef, St) ->
+ protected_cg(Ts, Rs, Fail, I, Pdb, Bef, St);
+guard_cg(#l{ke={block,Ts},i=I,vdb=Bdb}, Fail, _Vdb, Bef, St) ->
+ guard_cg_list(Ts, Fail, I, Bdb, Bef, St);
+guard_cg(#l{ke={test,Test,As},i=I,vdb=_Tdb}, Fail, Vdb, Bef, St) ->
+ test_cg(Test, As, Fail, I, Vdb, Bef, St);
+guard_cg(G, _Fail, Vdb, Bef, St) ->
+ %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{G,Fail,Vdb,Bef}]),
+ {Gis,Aft,St1} = cg(G, Vdb, Bef, St),
+ %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Aft}]),
+ {Gis,Aft,St1}.
+
+%% protected_cg([Kexpr], [Ret], Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% Do a protected. Protecteds without return values are just done
+%% for effect, the return value is not checked, success passes on to
+%% the next instruction and failure jumps to Fail. If there are
+%% return values then these must be set to 'false' on failure,
+%% control always passes to the next instruction.
+
+protected_cg(Ts, [], Fail, I, Vdb, Bef, St0) ->
+ %% Protect these calls, revert when done.
+ {Tis,Aft,St1} = guard_cg_list(Ts, Fail, I, Vdb, Bef,
+ St0#cg{btype=fail,bfail=Fail}),
+ {Tis,Aft,St1#cg{btype=St0#cg.btype,bfail=St0#cg.bfail}};
+protected_cg(Ts, Rs, _Fail, I, Vdb, Bef, St0) ->
+ {Pfail,St1} = new_label(St0),
+ {Psucc,St2} = new_label(St1),
+ {Tis,Aft,St3} = guard_cg_list(Ts, Pfail, I, Vdb, Bef,
+ St2#cg{btype=fail,bfail=Pfail}),
+ %%ok = io:fwrite("cg ~w: ~p~n", [?LINE,{Rs,I,Vdb,Aft}]),
+ %% Set return values to false.
+ Mis = map(fun ({var,V}) -> {move,{atom,false},fetch_var(V, Aft)} end, Rs),
+ Live = {'%live',max_reg(Aft#sr.reg)},
+ {Tis ++ [Live,{jump,{f,Psucc}},
+ {label,Pfail}] ++ Mis ++ [Live,{label,Psucc}],
+ Aft,St3#cg{btype=St0#cg.btype,bfail=St0#cg.bfail}}.
+
+%% test_cg(TestName, Args, Fail, I, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% Generate test instruction. Use explicit fail label here.
+
+test_cg(Test, As, Fail, I, Vdb, Bef, St) ->
+ case test_type(Test, length(As)) of
+ {cond_op,Op} ->
+ Ars = cg_reg_args(As, Bef),
+ Int = clear_dead(Bef, I, Vdb),
+ {[{test,Op,{f,Fail},Ars}],
+ clear_dead(Int, I, Vdb),
+ St};
+ {rev_cond_op,Op} ->
+ [S1,S2] = cg_reg_args(As, Bef),
+ Int = clear_dead(Bef, I, Vdb),
+ {[{test,Op,{f,Fail},[S2,S1]}],
+ clear_dead(Int, I, Vdb),
+ St}
+ end.
+
+test_type(is_atom, 1) -> {cond_op,is_atom};
+test_type(is_boolean, 1) -> {cond_op,is_boolean};
+test_type(is_binary, 1) -> {cond_op,is_binary};
+test_type(is_constant, 1) -> {cond_op,is_constant};
+test_type(is_float, 1) -> {cond_op,is_float};
+test_type(is_function, 1) -> {cond_op,is_function};
+test_type(is_integer, 1) -> {cond_op,is_integer};
+test_type(is_list, 1) -> {cond_op,is_list};
+test_type(is_number, 1) -> {cond_op,is_number};
+test_type(is_pid, 1) -> {cond_op,is_pid};
+test_type(is_port, 1) -> {cond_op,is_port};
+test_type(is_reference, 1) -> {cond_op,is_reference};
+test_type(is_tuple, 1) -> {cond_op,is_tuple};
+test_type('=<', 2) -> {rev_cond_op,is_ge};
+test_type('>', 2) -> {rev_cond_op,is_lt};
+test_type('<', 2) -> {cond_op,is_lt};
+test_type('>=', 2) -> {cond_op,is_ge};
+test_type('==', 2) -> {cond_op,is_eq};
+test_type('/=', 2) -> {cond_op,is_ne};
+test_type('=:=', 2) -> {cond_op,is_eq_exact};
+test_type('=/=', 2) -> {cond_op,is_ne_exact};
+test_type(internal_is_record, 3) -> {cond_op,internal_is_record}.
+
+%% guard_cg_list([Kexpr], Fail, I, Vdb, StackReg, St) ->
+%% {[Ainstr],StackReg,St}.
+
+guard_cg_list(Kes, Fail, I, Vdb, Bef, St0) ->
+ {Keis,{Aft,St1}} =
+ flatmapfoldl(fun (Ke, {Inta,Sta}) ->
+ {Keis,Intb,Stb} =
+ guard_cg(Ke, Fail, Vdb, Inta, Sta),
+ {comment(Inta) ++ Keis,{Intb,Stb}}
+ end, {Bef,St0}, need_heap(Kes, I)),
+ {Keis,Aft,St1}.
+
+%% match_fmf(Fun, LastFail, State, [Clause]) -> {Is,Aft,State}.
+%% This is a special flatmapfoldl for match code gen where we
+%% generate a "failure" label for each clause. The last clause uses
+%% an externally generated failure label, LastFail. N.B. We do not
+%% know or care how the failure labels are used.
+
+match_fmf(F, LastFail, St, [H]) ->
+ F(H, LastFail, St);
+match_fmf(F, LastFail, St0, [H|T]) ->
+ {Fail,St1} = new_label(St0),
+ {R,Aft1,St2} = F(H, Fail, St1),
+ {Rs,Aft2,St3} = match_fmf(F, LastFail, St2, T),
+ {R ++ [{label,Fail}] ++ Rs,sr_merge(Aft1, Aft2),St3};
+match_fmf(_, _, St, []) -> {[],void,St}.
+
+%% call_cg(Func, [Arg], [Ret], Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+%% enter_cg(Func, [Arg], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% Call and enter first put the arguments into registers and save any
+%% other registers, then clean up and compress the stack and set the
+%% frame size. Finally the actual call is made. Call then needs the
+%% return values filled in.
+
+call_cg({var,V}, As, Rs, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As++[{var,V}], Bef, Le#l.i, Vdb),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
+ {comment({call_fun,{var,V},As}) ++ Sis ++ Frees ++ [{call_fun,Arity}],
+ Aft,need_stack_frame(St0)};
+call_cg({remote,Mod,Name}, As, Rs, Le, Vdb, Bef, St0)
+ when element(1, Mod) == var;
+ element(1, Name) == var ->
+ {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ Call = {apply,Arity},
+ St = need_stack_frame(St0),
+ %%{Call,St1} = build_call(Func, Arity, St0),
+ {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
+ {Sis ++ Frees ++ [Call],Aft,St};
+call_cg(Func, As, Rs, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ %% Put return values in registers.
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {Call,St1} = build_call(Func, Arity, St0),
+ {Frees,Aft} = free_dead(clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb)),
+ {comment({call,Func,As}) ++ Sis ++ Frees ++ Call,Aft,St1}.
+
+build_call({remote,{atom,erlang},{atom,'!'}}, 2, St0) ->
+ {[send],need_stack_frame(St0)};
+build_call({remote,{atom,Mod},{atom,Name}}, Arity, St0) ->
+ {[{call_ext,Arity,{extfunc,Mod,Name,Arity}}],need_stack_frame(St0)};
+build_call(Name, Arity, St0) when atom(Name) ->
+ {Lbl,St1} = local_func_label(Name, Arity, need_stack_frame(St0)),
+ {[{call,Arity,{f,Lbl}}],St1}.
+
+free_dead(#sr{stk=Stk0}=Aft) ->
+ {Instr,Stk} = free_dead(Stk0, 0, [], []),
+ {Instr,Aft#sr{stk=Stk}}.
+
+free_dead([dead|Stk], Y, Instr, StkAcc) ->
+ %% Note: kill/1 is equivalent to init/1 (translated by beam_asm).
+ %% We use kill/1 to help further optimisation passes.
+ free_dead(Stk, Y+1, [{kill,{yy,Y}}|Instr], [free|StkAcc]);
+free_dead([Any|Stk], Y, Instr, StkAcc) ->
+ free_dead(Stk, Y+1, Instr, [Any|StkAcc]);
+free_dead([], _, Instr, StkAcc) -> {Instr,reverse(StkAcc)}.
+
+enter_cg({var,V}, As, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As++[{var,V}], Bef, Le#l.i, Vdb),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {comment({call_fun,{var,V},As}) ++ Sis ++ [{call_fun,Arity},return],
+ clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
+ need_stack_frame(St0)};
+enter_cg({remote,Mod,Name}=Func, As, Le, Vdb, Bef, St0)
+ when element(1, Mod) == var;
+ element(1, Name) == var ->
+ {Sis,Int} = cg_setup_call(As++[Mod,Name], Bef, Le#l.i, Vdb),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ Call = {apply_only,Arity},
+ St = need_stack_frame(St0),
+ {comment({enter,Func,As}) ++ Sis ++ [Call],
+ clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
+ St};
+enter_cg(Func, As, Le, Vdb, Bef, St0) ->
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ %% Build complete code and final stack/register state.
+ Arity = length(As),
+ {Call,St1} = build_enter(Func, Arity, St0),
+ {comment({enter,Func,As}) ++ Sis ++ Call,
+ clear_dead(Int#sr{reg=clear_regs(Int#sr.reg)}, Le#l.i, Vdb),
+ St1}.
+
+build_enter({remote,{atom,erlang},{atom,'!'}}, 2, St0) ->
+ {[send,return],need_stack_frame(St0)};
+build_enter({remote,{atom,Mod},{atom,Name}}, Arity, St0) ->
+ St1 = case trap_bif(Mod, Name, Arity) of
+ true -> need_stack_frame(St0);
+ false -> St0
+ end,
+ {[{call_ext_only,Arity,{extfunc,Mod,Name,Arity}}],St1};
+build_enter(Name, Arity, St0) when is_atom(Name) ->
+ {Lbl,St1} = local_func_label(Name, Arity, St0),
+ {[{call_only,Arity,{f,Lbl}}],St1}.
+
+%% local_func_label(Name, Arity, State) -> {Label,State'}
+%% Get the function entry label for a local function.
+
+local_func_label(Name, Arity, St0) ->
+ Key = {Name,Arity},
+ case keysearch(Key, 1, St0#cg.functable) of
+ {value,{Key,Label}} ->
+ {Label,St0};
+ false ->
+ {Label,St1} = new_label(St0),
+ {Label,St1#cg{functable=[{Key,Label}|St1#cg.functable]}}
+ end.
+
+%% need_stack_frame(State) -> State'
+%% Make a note in the state that this function will need a stack frame.
+
+need_stack_frame(#cg{need_frame=true}=St) -> St;
+need_stack_frame(St) -> St#cg{need_frame=true}.
+
+%% trap_bif(Mod, Name, Arity) -> true|false
+%% Trap bifs that need a stack frame.
+
+trap_bif(erlang, '!', 2) -> true;
+trap_bif(erlang, link, 1) -> true;
+trap_bif(erlang, unlink, 1) -> true;
+trap_bif(erlang, monitor_node, 2) -> true;
+trap_bif(erlang, group_leader, 2) -> true;
+trap_bif(erlang, exit, 2) -> true;
+trap_bif(_, _, _) -> false.
+
+%% bif_cg(Bif, [Arg], [Ret], Le, Vdb, StackReg, State) ->
+%% {[Ainstr],StackReg,State}.
+
+bif_cg(dsetelement, [Index0,Tuple0,New0], _Rs, Le, Vdb, Bef, St0) ->
+ [New,Tuple,{integer,Index1}] = cg_reg_args([New0,Tuple0,Index0], Bef),
+ Index = Index1-1,
+ {[{set_tuple_element,New,Tuple,Index}],
+ clear_dead(Bef, Le#l.i, Vdb), St0};
+bif_cg({make_fun,Func,Arity,Index,Uniq}, As, Rs, Le, Vdb, Bef, St0) ->
+ %% This behaves more like a function call.
+ {Sis,Int} = cg_setup_call(As, Bef, Le#l.i, Vdb),
+ Reg = load_vars(Rs, clear_regs(Int#sr.reg)),
+ {FuncLbl,St1} = local_func_label(Func, Arity, St0),
+ MakeFun = case St0#cg.new_funs of
+ true -> {make_fun2,{f,FuncLbl},Index,Uniq,length(As)};
+ false -> {make_fun,{f,FuncLbl},Uniq,length(As)}
+ end,
+ {comment({make_fun,{Func,Arity,Uniq},As}) ++ Sis ++
+ [MakeFun],
+ clear_dead(Int#sr{reg=Reg}, Le#l.i, Vdb),
+ St1};
+bif_cg(Bif, As, [{var,V}], Le, Vdb, Bef, St0) ->
+ Ars = cg_reg_args(As, Bef),
+
+ %% If we are inside a catch, we must save everything that will
+ %% be alive after the catch (because the BIF might fail and there
+ %% will be a jump to the code after the catch).
+ %% Currently, we are somewhat pessimistic in
+ %% that we save any variable that will be live after this BIF call.
+
+ {Sis,Int0} =
+ case St0#cg.in_catch of
+ true -> adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb);
+ false -> {[],Bef}
+ end,
+
+ Int1 = clear_dead(Int0, Le#l.i, Vdb),
+ Reg = put_reg(V, Int1#sr.reg),
+ Int = Int1#sr{reg=Reg},
+ Dst = fetch_reg(V, Reg),
+ {Sis ++ [{bif,Bif,bif_fail(St0#cg.btype, St0#cg.bfail, length(Ars)),Ars,Dst}],
+ clear_dead(Int, Le#l.i, Vdb), St0}.
+
+bif_fail(_, _, 0) -> nofail;
+bif_fail(exit, _, _) -> {f,0};
+bif_fail(fail, Fail, _) -> {f,Fail}.
+
+%% recv_loop_cg(TimeOut, ReceiveVar, ReceiveMatch, TimeOutExprs,
+%% [Ret], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+
+recv_loop_cg(Te, Rvar, Rm, Tes, Rs, Le, Vdb, Bef, St0) ->
+ {Sis,Int0} = adjust_stack(Bef, Le#l.i, Le#l.i, Vdb),
+ Int1 = Int0#sr{reg=clear_regs(Int0#sr.reg)},
+ %% Get labels.
+ {Rl,St1} = new_label(St0),
+ {Tl,St2} = new_label(St1),
+ {Bl,St3} = new_label(St2),
+ St4 = St3#cg{break=Bl,recv=Rl}, %Set correct receive labels
+ {Ris,Raft,St5} = cg_recv_mesg(Rvar, Rm, Tl, Int1, St4),
+ {Wis,Taft,St6} = cg_recv_wait(Te, Tes, Le#l.i, Int1, St5),
+ Int2 = sr_merge(Raft, Taft), %Merge stack/registers
+ Reg = load_vars(Rs, Int2#sr.reg),
+ {Sis ++ Ris ++ [{label,Tl}] ++ Wis ++ [{label,Bl}],
+ clear_dead(Int2#sr{reg=Reg}, Le#l.i, Vdb),
+ St6#cg{break=St0#cg.break,recv=St0#cg.recv}}.
+
+%% cg_recv_mesg( ) -> {[Ainstr],Aft,St}.
+
+cg_recv_mesg({var,R}, Rm, Tl, Bef, St0) ->
+ Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
+ Ret = fetch_reg(R, Int0#sr.reg),
+ %% Int1 = clear_dead(Int0, I, Rm#l.vdb),
+ Int1 = Int0,
+ {Mis,Int2,St1} = match_cg(Rm, none, Int1, St0),
+ {[{'%live',0},{label,St1#cg.recv},{loop_rec,{f,Tl},Ret}|Mis],Int2,St1}.
+
+%% cg_recv_wait(Te, Tes, I, Vdb, Int2, St3) -> {[Ainstr],Aft,St}.
+
+cg_recv_wait({atom,infinity}, Tes, I, Bef, St0) ->
+ %% We know that the 'after' body will never be executed.
+ %% But to keep the stack and register information up to date,
+ %% we will generate the code for the 'after' body, and then discard it.
+ Int1 = clear_dead(Bef, I, Tes#l.vdb),
+ {_,Int2,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb,
+ Int1#sr{reg=clear_regs(Int1#sr.reg)}, St0),
+ {[{wait,{f,St1#cg.recv}}],Int2,St1};
+cg_recv_wait({integer,0}, Tes, _I, Bef, St0) ->
+ {Tis,Int,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb, Bef, St0),
+ {[timeout|Tis],Int,St1};
+cg_recv_wait(Te, Tes, I, Bef, St0) ->
+ Reg = cg_reg_arg(Te, Bef),
+ %% Must have empty registers here! Bug if anything in registers.
+ Int0 = clear_dead(Bef, I, Tes#l.vdb),
+ {Tis,Int,St1} = cg_block(Tes#l.ke, Tes#l.i, Tes#l.vdb,
+ Int0#sr{reg=clear_regs(Int0#sr.reg)}, St0),
+ {[{wait_timeout,{f,St1#cg.recv},Reg},timeout] ++ Tis,Int,St1}.
+
+%% recv_next_cg(Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+%% Use adjust stack to clear stack, but only need it for Aft.
+
+recv_next_cg(Le, Vdb, Bef, St) ->
+ {Sis,Aft} = adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb),
+ {[{loop_rec_end,{f,St#cg.recv}}] ++ Sis,Aft,St}. %Joke
+
+%% try_cg(TryBlock, [BodyVar], TryBody, [ExcpVar], TryHandler, [Ret],
+%% Le, Vdb, StackReg, St) -> {[Ainstr],StackReg,St}.
+
+try_cg(Ta, Vs, Tb, Evs, Th, Rs, Le, Vdb, Bef, St0) ->
+ {B,St1} = new_label(St0), %Body label
+ {H,St2} = new_label(St1), %Handler label
+ {E,St3} = new_label(St2), %End label
+ TryTag = Ta#l.i,
+ Int1 = Bef#sr{stk=put_catch(TryTag, Bef#sr.stk)},
+ TryReg = fetch_stack({catch_tag,TryTag}, Int1#sr.stk),
+ {Ais,Int2,St4} = cg(Ta, Vdb, Int1, St3#cg{break=B,in_catch=true}),
+ Int3 = Int2#sr{stk=drop_catch(TryTag, Int2#sr.stk)},
+ St5 = St4#cg{break=E,in_catch=St3#cg.in_catch},
+ {Bis,Baft,St6} = cg(Tb, Vdb, Int3#sr{reg=load_vars(Vs, Int3#sr.reg)}, St5),
+ {His,Haft,St7} = cg(Th, Vdb, Int3#sr{reg=load_vars(Evs, Int3#sr.reg)}, St6),
+ Int4 = sr_merge(Baft, Haft), %Merge stack/registers
+ Aft = Int4#sr{reg=load_vars(Rs, Int4#sr.reg)},
+ {[{'try',TryReg,{f,H}}] ++ Ais ++
+ [{label,B},{try_end,TryReg}] ++ Bis ++
+ [{label,H},{try_case,TryReg}] ++ His ++
+ [{label,E}],
+ clear_dead(Aft, Le#l.i, Vdb),
+ St7#cg{break=St0#cg.break}}.
+
+%% catch_cg(CatchBlock, Ret, Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+
+catch_cg(C, {var,R}, Le, Vdb, Bef, St0) ->
+ {B,St1} = new_label(St0),
+ CatchTag = Le#l.i,
+ Int1 = Bef#sr{stk=put_catch(CatchTag, Bef#sr.stk)},
+ CatchReg = fetch_stack({catch_tag,CatchTag}, Int1#sr.stk),
+ {Cis,Int2,St2} = cg_block(C, Le#l.i, Le#l.vdb, Int1,
+ St1#cg{break=B,in_catch=true}),
+ Aft = Int2#sr{reg=load_reg(R, 0, Int2#sr.reg),
+ stk=drop_catch(CatchTag, Int2#sr.stk)},
+ {[{'catch',CatchReg,{f,B}}] ++ Cis ++
+ [{label,B},{catch_end,CatchReg}],
+ clear_dead(Aft, Le#l.i, Vdb),
+ St2#cg{break=St1#cg.break,in_catch=St1#cg.in_catch}}.
+
+%% set_cg([Var], Constr, Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% We have to be careful how a 'set' works. First the structure is
+%% built, then it is filled and finally things can be cleared. The
+%% annotation must reflect this and make sure that the return
+%% variable is allocated first.
+%%
+%% put_list for constructing a cons is an atomic instruction
+%% which can safely resuse one of the source registers as target.
+%% Also binaries can reuse a source register as target.
+
+set_cg([{var,R}], {cons,Es}, Le, Vdb, Bef, St) ->
+ [S1,S2] = map(fun ({var,V}) -> fetch_var(V, Bef);
+ (Other) -> Other
+ end, Es),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ Int1 = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
+ Ret = fetch_reg(R, Int1#sr.reg),
+ {[{put_list,S1,S2,Ret}], Int1, St};
+set_cg([{var,R}], {old_binary,Segs}, Le, Vdb, Bef, St) ->
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ Code = cg_binary_old(PutCode),
+ Int0 = clear_dead(Bef, Le#l.i, Vdb),
+ Aft = Int0#sr{reg=put_reg(R, Int0#sr.reg)},
+ Ret = fetch_reg(R, Aft#sr.reg),
+ {Code ++ [{bs_final,Fail,Ret}],Aft,St};
+set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{in_catch=InCatch}=St) ->
+ Int0 = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
+ Target = fetch_reg(R, Int0#sr.reg),
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ Temp = find_scratch_reg(Int0#sr.reg),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ {Sis,Int1} =
+ case InCatch of
+ true -> adjust_stack(Int0, Le#l.i, Le#l.i+1, Vdb);
+ false -> {[],Int0}
+ end,
+ Aft = clear_dead(Int1, Le#l.i, Vdb),
+ Code = cg_binary(PutCode, Target, Temp, Fail, Aft),
+ {Sis++Code,Aft,St};
+set_cg([{var,R}], Con, Le, Vdb, Bef, St) ->
+ %% Find a place for the return register first.
+ Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)},
+ Ret = fetch_reg(R, Int#sr.reg),
+ Ais = case Con of
+ {tuple,Es} ->
+ [{put_tuple,length(Es),Ret}] ++ cg_build_args(Es, Bef);
+ {var,V} -> % Normally removed by kernel optimizer.
+ [{move,fetch_var(V, Int),Ret}];
+ {string,Str} ->
+ [{put_string,length(Str),{string,Str},Ret}];
+ Other ->
+ [{move,Other,Ret}]
+ end,
+ {Ais,clear_dead(Int, Le#l.i, Vdb),St};
+set_cg([], {binary,Segs}, Le, Vdb, Bef, St) ->
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ Target = find_scratch_reg(Bef#sr.reg),
+ Temp = find_scratch_reg(put_reg(Target, Bef#sr.reg)),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ Code = cg_binary(PutCode, Target, Temp, Fail, Bef),
+ Aft = clear_dead(Bef, Le#l.i, Vdb),
+ {Code,Aft,St};
+set_cg([], {old_binary,Segs}, Le, Vdb, Bef, St) ->
+ Fail = bif_fail(St#cg.btype, St#cg.bfail, 42),
+ PutCode = cg_bin_put(Segs, Fail, Bef),
+ Ais0 = cg_binary_old(PutCode),
+ Ret = find_scratch_reg(Bef#sr.reg),
+ Ais = Ais0 ++ [{bs_final,Fail,Ret}],
+ {Ais,clear_dead(Bef, Le#l.i, Vdb),St};
+set_cg([], _, Le, Vdb, Bef, St) ->
+ %% This should have been stripped by compiler, just cleanup.
+ {[],clear_dead(Bef, Le#l.i, Vdb), St}.
+
+
+%%%
+%%% Code generation for constructing binaries.
+%%%
+
+cg_binary(PutCode, Target, Temp, Fail, Bef) ->
+ SzCode = cg_binary_size(PutCode, Target, Temp, Fail),
+ MaxRegs = max_reg(Bef#sr.reg),
+ Code = SzCode ++ [{bs_init2,Fail,Target,MaxRegs,{field_flags,[]},Target}|PutCode],
+ cg_bin_opt(Code).
+
+cg_binary_size(PutCode, Target, Temp, Fail) ->
+ Szs = cg_binary_size_1(PutCode, 0, []),
+ cg_binary_size_expr(Szs, Target, Temp, Fail).
+
+cg_binary_size_1([{_Put,_Fail,S,U,_Flags,Src}|T], Bits, Acc) ->
+ cg_binary_size_2(S, U, Src, T, Bits, Acc);
+cg_binary_size_1([], Bits, Acc) ->
+ Bytes = Bits div 8,
+ RemBits = Bits rem 8,
+ Res = sort([{1,{integer,RemBits}},{8,{integer,Bytes}}|Acc]),
+ cg_binary_size_3(Res).
+
+cg_binary_size_2({integer,N}, U, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits+N*U, Acc);
+cg_binary_size_2({atom,all}, 8, E, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{8,{size,E}}|Acc]);
+cg_binary_size_2(Reg, 1, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{1,Reg}|Acc]);
+cg_binary_size_2(Reg, 8, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{8,Reg}|Acc]);
+cg_binary_size_2(Reg, U, _, Next, Bits, Acc) ->
+ cg_binary_size_1(Next, Bits, [{1,{'*',Reg,U}}|Acc]).
+
+cg_binary_size_3([{_,{integer,0}}|T]) ->
+ cg_binary_size_3(T);
+cg_binary_size_3([{U,S1},{U,S2}|T]) ->
+ {L0,Rest} = cg_binary_size_4(T, U, []),
+ L = [S1,S2|L0],
+ [{U,L}|cg_binary_size_3(Rest)];
+cg_binary_size_3([{U,S}|T]) ->
+ [{U,[S]}|cg_binary_size_3(T)];
+cg_binary_size_3([]) -> [].
+
+cg_binary_size_4([{U,S}|T], U, Acc) ->
+ cg_binary_size_4(T, U, [S|Acc]);
+cg_binary_size_4(T, _, Acc) ->
+ {Acc,T}.
+
+%% cg_binary_size_expr/4
+%% Generate code for calculating the resulting size of a binary.
+cg_binary_size_expr(Sizes, Target, Temp, Fail) ->
+ cg_binary_size_expr_1(Sizes, Target, Temp, Fail,
+ [{move,{integer,0},Target}]).
+
+cg_binary_size_expr_1([{1,E0}|T], Target, Temp, Fail, Acc) ->
+ E1 = cg_gen_binsize(E0, Target, Temp, Fail, Acc),
+ E = [{bs_bits_to_bytes,Fail,Target,Target}|E1],
+ cg_binary_size_expr_1(T, Target, Temp, Fail, E);
+cg_binary_size_expr_1([{8,E0}], Target, Temp, Fail, Acc) ->
+ E = cg_gen_binsize(E0, Target, Temp, Fail, Acc),
+ reverse(E);
+cg_binary_size_expr_1([], _, _, _, Acc) -> reverse(Acc).
+
+cg_gen_binsize([{'*',A,B}|T], Target, Temp, Fail, Acc) ->
+ cg_gen_binsize(T, Target, Temp, Fail,
+ [{bs_add,Fail,[Target,A,B],Target}|Acc]);
+cg_gen_binsize([{size,B}|T], Target, Temp, Fail, Acc) ->
+ cg_gen_binsize([Temp|T], Target, Temp, Fail,
+ [{bif,size,Fail,[B],Temp}|Acc]);
+cg_gen_binsize([E0|T], Target, Temp, Fail, Acc) ->
+ cg_gen_binsize(T, Target, Temp, Fail,
+ [{bs_add,Fail,[Target,E0,1],Target}|Acc]);
+cg_gen_binsize([], _, _, _, Acc) -> Acc.
+
+%% cg_bin_opt(Code0) -> Code
+%% Optimize the size calculations for binary construction.
+
+cg_bin_opt([{move,{integer,0},D},{bs_add,_,[D,{integer,_}=S,1],Dst}|Is]) ->
+ cg_bin_opt([{move,S,Dst}|Is]);
+cg_bin_opt([{move,{integer,0},D},{bs_add,Fail,[D,S,U],Dst}|Is]) ->
+ cg_bin_opt([{bs_add,Fail,[{integer,0},S,U],Dst}|Is]);
+cg_bin_opt([{move,{integer,Bytes},D},{bs_init2,Fail,D,Regs0,Flags,D}|Is]) ->
+ Regs = cg_bo_newregs(Regs0, D),
+ cg_bin_opt([{bs_init2,Fail,Bytes,Regs,Flags,D}|Is]);
+cg_bin_opt([{move,Src,D},{bs_init2,Fail,D,Regs0,Flags,D}|Is]) ->
+ Regs = cg_bo_newregs(Regs0, D),
+ cg_bin_opt([{bs_init2,Fail,Src,Regs,Flags,D}|Is]);
+cg_bin_opt([{move,Src,Dst},{bs_bits_to_bytes,Fail,Dst,Dst}|Is]) ->
+ cg_bin_opt([{bs_bits_to_bytes,Fail,Src,Dst}|Is]);
+cg_bin_opt([{move,Src1,Dst},{bs_add,Fail,[Dst,Src2,U],Dst}|Is]) ->
+ cg_bin_opt([{bs_add,Fail,[Src1,Src2,U],Dst}|Is]);
+cg_bin_opt([{bs_bits_to_bytes,Fail,{integer,N},_}|Is0]) when N rem 8 =/= 0 ->
+ case Fail of
+ {f,0} ->
+ Is = [{move,{atom,badarg},{x,0}},
+ {call_ext_only,1,{extfunc,erlang,error,1}}|Is0],
+ cg_bin_opt(Is);
+ _ ->
+ cg_bin_opt([{jump,Fail}|Is0])
+ end;
+cg_bin_opt([I|Is]) ->
+ [I|cg_bin_opt(Is)];
+cg_bin_opt([]) -> [].
+
+cg_bo_newregs(R, {x,X}) when R-1 =:= X -> R-1;
+cg_bo_newregs(R, _) -> R.
+
+%% Common for new and old binary code generation.
+
+cg_bin_put({bin_seg,S0,U,T,Fs,[E0,Next]}, Fail, Bef) ->
+ S1 = case S0 of
+ {var,Sv} -> fetch_var(Sv, Bef);
+ _ -> S0
+ end,
+ E1 = case E0 of
+ {var,V} -> fetch_var(V, Bef);
+ Other -> Other
+ end,
+ Op = case T of
+ integer -> bs_put_integer;
+ binary -> bs_put_binary;
+ float -> bs_put_float
+ end,
+ [{Op,Fail,S1,U,{field_flags,Fs},E1}|cg_bin_put(Next, Fail, Bef)];
+cg_bin_put(bin_end, _, _) -> [].
+
+%% Old style.
+
+cg_binary_old(PutCode) ->
+ [cg_bs_init(PutCode)] ++ need_bin_buf(PutCode).
+
+cg_bs_init(Code) ->
+ {Size,Fs} = foldl(fun ({_,_,{integer,N},U,_,_}, {S,Fs}) ->
+ {S + N*U,Fs};
+ (_, {S,_}) ->
+ {S,[]}
+ end, {0,[exact]}, Code),
+ {bs_init,(Size+7) div 8,{field_flags,Fs}}.
+
+need_bin_buf(Code0) ->
+ {Code1,F,H} = foldr(fun ({_,_,{integer,N},U,_,_}=Bs, {Code,F,H}) ->
+ {[Bs|Code],F,H + N*U};
+ ({_,_,_,_,_,_}=Bs, {Code,F,H}) ->
+ {[Bs|need_bin_buf_need(H, F, Code)],true,0}
+ end, {[],false,0}, Code0),
+ need_bin_buf_need(H, F, Code1).
+
+need_bin_buf_need(0, false, Rest) -> Rest;
+need_bin_buf_need(H, _, Rest) -> [{bs_need_buf,H}|Rest].
+
+cg_build_args(As, Bef) ->
+ map(fun ({var,V}) -> {put,fetch_var(V, Bef)};
+ (Other) -> {put,Other}
+ end, As).
+
+%% return_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% break_cg([Val], Le, Vdb, Bef, St) -> {[Ainstr],Aft,St}.
+%% These are very simple, just put return/break values in registers
+%% from 0, then return/break. Use the call setup to clean up stack,
+%% but must clear registers to ensure sr_merge works correctly.
+
+return_cg(Rs, Le, Vdb, Bef, St) ->
+ {Ms,Int} = cg_setup_call(Rs, Bef, Le#l.i, Vdb),
+ {comment({return,Rs}) ++ Ms ++ [return],
+ Int#sr{reg=clear_regs(Int#sr.reg)},St}.
+
+break_cg(Bs, Le, Vdb, Bef, St) ->
+ {Ms,Int} = cg_setup_call(Bs, Bef, Le#l.i, Vdb),
+ {comment({break,Bs}) ++ Ms ++ [{jump,{f,St#cg.break}}],
+ Int#sr{reg=clear_regs(Int#sr.reg)},St}.
+
+%% cg_reg_arg(Arg0, Info) -> Arg
+%% cg_reg_args([Arg0], Info) -> [Arg]
+%% Convert argument[s] into registers. Literal values are returned unchanged.
+
+cg_reg_args(As, Bef) -> [cg_reg_arg(A, Bef) || A <- As].
+
+cg_reg_arg({var,V}, Bef) -> fetch_var(V, Bef);
+cg_reg_arg(Literal, _) -> Literal.
+
+%% cg_setup_call([Arg], Bef, Cur, Vdb) -> {[Instr],Aft}.
+%% Do the complete setup for a call/enter.
+
+cg_setup_call(As, Bef, I, Vdb) ->
+ {Ms,Int0} = cg_call_args(As, Bef, I, Vdb),
+ %% Have set up arguments, can now clean up, compress and save to stack.
+ Int1 = Int0#sr{stk=clear_dead_stk(Int0#sr.stk, I, Vdb),res=[]},
+ {Sis,Int2} = adjust_stack(Int1, I, I+1, Vdb),
+ {Ms ++ Sis ++ [{'%live',length(As)}],Int2}.
+
+%% cg_call_args([Arg], SrState) -> {[Instr],SrState}.
+%% Setup the arguments to a call/enter/bif. Put the arguments into
+%% consecutive registers starting at {x,0} moving any data which
+%% needs to be saved. Return a modified SrState structure with the
+%% new register contents. N.B. the resultant register info will
+%% contain non-variable values when there are non-variable values.
+%%
+%% This routine is complicated by unsaved values in x registers.
+%% We'll move away any unsaved values that are in the registers
+%% to be overwritten by the arguments.
+
+cg_call_args(As, Bef, I, Vdb) ->
+ Regs0 = load_arg_regs(Bef#sr.reg, As),
+ Unsaved = unsaved_registers(Regs0, Bef#sr.stk, I, I+1, Vdb),
+ {UnsavedMoves,Regs} = move_unsaved(Unsaved, Bef#sr.reg, Regs0),
+ Moves0 = gen_moves(As, Bef),
+ Moves = order_moves(Moves0, find_scratch_reg(Regs)),
+ {UnsavedMoves ++ Moves,Bef#sr{reg=Regs}}.
+
+%% load_arg_regs([Reg], Arguments) -> [Reg]
+%% Update the register descriptor to include the arguments (from {x,0}
+%% and upwards). Values in argument register are overwritten.
+%% Values in x registers above the arguments are preserved.
+
+load_arg_regs(Regs, As) -> load_arg_regs(Regs, As, 0).
+
+load_arg_regs([_|Rs], [{var,V}|As], I) -> [{I,V}|load_arg_regs(Rs, As, I+1)];
+load_arg_regs([_|Rs], [A|As], I) -> [{I,A}|load_arg_regs(Rs, As, I+1)];
+load_arg_regs([], [{var,V}|As], I) -> [{I,V}|load_arg_regs([], As, I+1)];
+load_arg_regs([], [A|As], I) -> [{I,A}|load_arg_regs([], As, I+1)];
+load_arg_regs(Rs, [], _) -> Rs.
+
+%% Returns the variables must be saved and are currently in the
+%% x registers that are about to be overwritten by the arguments.
+
+unsaved_registers(Regs, Stk, Fb, Lf, Vdb) ->
+ [V || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk),
+ not in_reg(V, Regs)].
+
+in_reg(V, Regs) -> keymember(V, 2, Regs).
+
+%% Move away unsaved variables from the registers that are to be
+%% overwritten by the arguments.
+move_unsaved(Vs, OrigRegs, NewRegs) ->
+ move_unsaved(Vs, OrigRegs, NewRegs, []).
+
+move_unsaved([V|Vs], OrigRegs, NewRegs0, Acc) ->
+ NewRegs = put_reg(V, NewRegs0),
+ Src = fetch_reg(V, OrigRegs),
+ Dst = fetch_reg(V, NewRegs),
+ move_unsaved(Vs, OrigRegs, NewRegs, [{move,Src,Dst}|Acc]);
+move_unsaved([], _, Regs, Acc) -> {Acc,Regs}.
+
+%% gen_moves(As, Sr)
+%% Generate the basic move instruction to move the arguments
+%% to their proper registers. The list will be sorted on
+%% destinations. (I.e. the move to {x,0} will be first --
+%% see the comment to order_moves/2.)
+
+gen_moves(As, Sr) -> gen_moves(As, Sr, 0, []).
+
+gen_moves([{var,V}|As], Sr, I, Acc) ->
+ case fetch_var(V, Sr) of
+ {x,I} -> gen_moves(As, Sr, I+1, Acc);
+ Reg -> gen_moves(As, Sr, I+1, [{move,Reg,{x,I}}|Acc])
+ end;
+gen_moves([A|As], Sr, I, Acc) ->
+ gen_moves(As, Sr, I+1, [{move,A,{x,I}}|Acc]);
+gen_moves([], _, _, Acc) -> lists:keysort(3, Acc).
+
+%% order_moves([Move], ScratchReg) -> [Move]
+%% Orders move instruction so that source registers are not
+%% destroyed before they are used. If there are cycles
+%% (such as {move,{x,0},{x,1}}, {move,{x,1},{x,1}}),
+%% the scratch register is used to break up the cycle.
+%% If possible, the first move of the input list is placed
+%% last in the result list (to make the move to {x,0} occur
+%% just before the call to allow the Beam loader to coalesce
+%% the instructions).
+
+order_moves(Ms, Scr) -> order_moves(Ms, Scr, []).
+
+order_moves([{move,_,_}=M|Ms0], ScrReg, Acc0) ->
+ {Chain,Ms} = collect_chain(Ms0, [M], ScrReg),
+ Acc = reverse(Chain, Acc0),
+ order_moves(Ms, ScrReg, Acc);
+order_moves([], _, Acc) -> Acc.
+
+collect_chain(Ms, Path, ScrReg) ->
+ collect_chain(Ms, Path, [], ScrReg).
+
+collect_chain([{move,Src,Same}=M|Ms0], [{move,Same,_}|_]=Path, Others, ScrReg) ->
+ case keysearch(Src, 3, Path) of
+ {value,_} -> %We have a cycle.
+ {break_up_cycle(M, Path, ScrReg),reverse(Others, Ms0)};
+ false ->
+ collect_chain(reverse(Others, Ms0), [M|Path], [], ScrReg)
+ end;
+collect_chain([M|Ms], Path, Others, ScrReg) ->
+ collect_chain(Ms, Path, [M|Others], ScrReg);
+collect_chain([], Path, Others, _) ->
+ {Path,Others}.
+
+break_up_cycle({move,Src,_}=M, Path, ScrReg) ->
+ [{move,ScrReg,Src},M|break_up_cycle1(Src, Path, ScrReg)].
+
+break_up_cycle1(Dst, [{move,Src,Dst}|Path], ScrReg) ->
+ [{move,Src,ScrReg}|Path];
+break_up_cycle1(Dst, [M|Path], LastMove) ->
+ [M|break_up_cycle1(Dst, Path, LastMove)].
+
+%% clear_dead(Sr, Until, Vdb) -> Aft.
+%% Remove all variables in Sr which have died AT ALL so far.
+
+clear_dead(Sr, Until, Vdb) ->
+ Sr#sr{reg=clear_dead_reg(Sr, Until, Vdb),
+ stk=clear_dead_stk(Sr#sr.stk, Until, Vdb)}.
+
+clear_dead_reg(Sr, Until, Vdb) ->
+ Reg = map(fun ({I,V}) ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L > Until -> {I,V};
+ _ -> free %Remove anything else
+ end;
+ ({reserved,I,V}) -> {reserved,I,V};
+ (free) -> free
+ end, Sr#sr.reg),
+ reserve(Sr#sr.res, Reg, Sr#sr.stk).
+
+clear_dead_stk(Stk, Until, Vdb) ->
+ map(fun ({V}) ->
+ case vdb_find(V, Vdb) of
+ {V,_,L} when L > Until -> {V};
+ _ -> dead %Remove anything else
+ end;
+ (free) -> free;
+ (dead) -> dead
+ end, Stk).
+
+%% sr_merge(Sr1, Sr2) -> Sr.
+%% Merge two stack/register states keeping the longest of both stack
+%% and register. Perform consistency check on both, elements must be
+%% the same. Allow frame size 'void' to make easy creation of
+%% "empty" frame.
+
+sr_merge(#sr{reg=R1,stk=S1,res=[]}, #sr{reg=R2,stk=S2,res=[]}) ->
+ #sr{reg=longest(R1, R2),stk=longest(S1, S2),res=[]};
+sr_merge(void, S2) -> S2#sr{res=[]};
+sr_merge(S1, void) -> S1#sr{res=[]}.
+
+longest([H|T1], [H|T2]) -> [H|longest(T1, T2)];
+longest([dead|T1], [free|T2]) -> [dead|longest(T1, T2)];
+longest([free|T1], [dead|T2]) -> [dead|longest(T1, T2)];
+longest([dead|T1], []) -> [dead|T1];
+longest([], [dead|T2]) -> [dead|T2];
+longest([free|T1], []) -> [free|T1];
+longest([], [free|T2]) -> [free|T2];
+longest([], []) -> [].
+
+%% adjust_stack(Bef, FirstBefore, LastFrom, Vdb) -> {[Ainstr],Aft}.
+%% Do complete stack adjustment by compressing stack and adding
+%% variables to be saved. Try to optimise ordering on stack by
+%% having reverse order to their lifetimes.
+%%
+%% In Beam, there is a fixed stack frame and no need to do stack compression.
+
+adjust_stack(Bef, Fb, Lf, Vdb) ->
+ Stk0 = Bef#sr.stk,
+ {Stk1,Saves} = save_stack(Stk0, Fb, Lf, Vdb),
+ {saves(Saves, Bef#sr.reg, Stk1),
+ Bef#sr{stk=Stk1}}.
+
+%% save_stack(Stack, FirstBefore, LastFrom, Vdb) -> {[SaveVar],NewStack}.
+%% Save variables which are used past current point and which are not
+%% already on the stack.
+
+save_stack(Stk0, Fb, Lf, Vdb) ->
+ %% New variables that are in use but not on stack.
+ New = [ {V,F,L} || {V,F,L} <- Vdb,
+ F < Fb,
+ L >= Lf,
+ not on_stack(V, Stk0) ],
+ %% Add new variables that are not just dropped immediately.
+ %% N.B. foldr works backwards from the end!!
+ Saves = [ V || {V,_,_} <- keysort(3, New) ],
+ Stk1 = foldr(fun (V, Stk) -> put_stack(V, Stk) end, Stk0, Saves),
+ {Stk1,Saves}.
+
+%% saves([SaveVar], Reg, Stk) -> [{move,Reg,Stk}].
+%% Generate move instructions to save variables onto stack. The
+%% stack/reg info used is that after the new stack has been made.
+
+saves(Ss, Reg, Stk) ->
+ Res = map(fun (V) ->
+ {move,fetch_reg(V, Reg),fetch_stack(V, Stk)}
+ end, Ss),
+ Res.
+
+%% comment(C) -> ['%'{C}].
+
+%comment(C) -> [{'%',C}].
+comment(_) -> [].
+
+%% fetch_var(VarName, StkReg) -> r{R} | sp{Sp}.
+%% find_var(VarName, StkReg) -> ok{r{R} | sp{Sp}} | error.
+%% Fetch/find a variable in either the registers or on the
+%% stack. Fetch KNOWS it's there.
+
+fetch_var(V, Sr) ->
+ case find_reg(V, Sr#sr.reg) of
+ {ok,R} -> R;
+ error -> fetch_stack(V, Sr#sr.stk)
+ end.
+
+% find_var(V, Sr) ->
+% case find_reg(V, Sr#sr.reg) of
+% {ok,R} -> {ok,R};
+% error ->
+% case find_stack(V, Sr#sr.stk) of
+% {ok,S} -> {ok,S};
+% error -> error
+% end
+% end.
+
+load_vars(Vs, Regs) ->
+ foldl(fun ({var,V}, Rs) -> put_reg(V, Rs) end, Regs, Vs).
+
+%% put_reg(Val, Regs) -> Regs.
+%% load_reg(Val, Reg, Regs) -> Regs.
+%% free_reg(Val, Regs) -> Regs.
+%% find_reg(Val, Regs) -> ok{r{R}} | error.
+%% fetch_reg(Val, Regs) -> r{R}.
+%% Functions to interface the registers.
+%% put_reg puts a value into a free register,
+%% load_reg loads a value into a fixed register
+%% free_reg frees a register containing a specific value.
+
+% put_regs(Vs, Rs) -> foldl(fun put_reg/2, Rs, Vs).
+
+put_reg(V, Rs) -> put_reg_1(V, Rs, 0).
+
+put_reg_1(V, [free|Rs], I) -> [{I,V}|Rs];
+put_reg_1(V, [{reserved,I,V}|Rs], I) -> [{I,V}|Rs];
+put_reg_1(V, [R|Rs], I) -> [R|put_reg_1(V, Rs, I+1)];
+put_reg_1(V, [], I) -> [{I,V}].
+
+load_reg(V, R, Rs) -> load_reg_1(V, R, Rs, 0).
+
+load_reg_1(V, I, [_|Rs], I) -> [{I,V}|Rs];
+load_reg_1(V, I, [R|Rs], C) -> [R|load_reg_1(V, I, Rs, C+1)];
+load_reg_1(V, I, [], I) -> [{I,V}];
+load_reg_1(V, I, [], C) -> [free|load_reg_1(V, I, [], C+1)].
+
+% free_reg(V, [{I,V}|Rs]) -> [free|Rs];
+% free_reg(V, [R|Rs]) -> [R|free_reg(V, Rs)];
+% free_reg(V, []) -> [].
+
+fetch_reg(V, [{I,V}|_]) -> {x,I};
+fetch_reg(V, [_|SRs]) -> fetch_reg(V, SRs).
+
+find_reg(V, [{I,V}|_]) -> {ok,{x,I}};
+find_reg(V, [_|SRs]) -> find_reg(V, SRs);
+find_reg(_, []) -> error.
+
+%% For the bit syntax, we need a scratch register if we are constructing
+%% a binary that will not be used.
+
+find_scratch_reg(Rs) -> find_scratch_reg(Rs, 0).
+
+find_scratch_reg([free|_], I) -> {x,I};
+find_scratch_reg([_|Rs], I) -> find_scratch_reg(Rs, I+1);
+find_scratch_reg([], I) -> {x,I}.
+
+%%copy_reg(Val, R, Regs) -> load_reg(Val, R, Regs).
+%%move_reg(Val, R, Regs) -> load_reg(Val, R, free_reg(Val, Regs)).
+
+%%clear_regs(Regs) -> map(fun (R) -> free end, Regs).
+clear_regs(_) -> [].
+
+max_reg(Regs) ->
+ foldl(fun ({I,_}, _) -> I;
+ (_, Max) -> Max end,
+ -1, Regs) + 1.
+
+%% put_stack(Val, [{Val}]) -> [{Val}].
+%% fetch_stack(Var, Stk) -> sp{S}.
+%% find_stack(Var, Stk) -> ok{sp{S}} | error.
+%% Functions to interface the stack.
+
+put_stack(Val, []) -> [{Val}];
+put_stack(Val, [dead|Stk]) -> [{Val}|Stk];
+put_stack(Val, [free|Stk]) -> [{Val}|Stk];
+put_stack(Val, [NotFree|Stk]) -> [NotFree|put_stack(Val, Stk)].
+
+put_stack_carefully(Val, Stk0) ->
+ case catch put_stack_carefully1(Val, Stk0) of
+ error -> error;
+ Stk1 when list(Stk1) -> Stk1
+ end.
+
+put_stack_carefully1(_, []) -> throw(error);
+put_stack_carefully1(Val, [dead|Stk]) -> [{Val}|Stk];
+put_stack_carefully1(Val, [free|Stk]) -> [{Val}|Stk];
+put_stack_carefully1(Val, [NotFree|Stk]) ->
+ [NotFree|put_stack_carefully1(Val, Stk)].
+
+fetch_stack(Var, Stk) -> fetch_stack(Var, Stk, 0).
+
+fetch_stack(V, [{V}|_], I) -> {yy,I};
+fetch_stack(V, [_|Stk], I) -> fetch_stack(V, Stk, I+1).
+
+% find_stack(Var, Stk) -> find_stack(Var, Stk, 0).
+
+% find_stack(V, [{V}|Stk], I) -> {ok,{yy,I}};
+% find_stack(V, [O|Stk], I) -> find_stack(V, Stk, I+1);
+% find_stack(V, [], I) -> error.
+
+on_stack(V, Stk) -> keymember(V, 1, Stk).
+
+%% put_catch(CatchTag, Stack) -> Stack'
+%% drop_catch(CatchTag, Stack) -> Stack'
+%% Special interface for putting and removing catch tags, to ensure that
+%% catches nest properly. Also used for try tags.
+
+put_catch(Tag, Stk0) -> put_catch(Tag, reverse(Stk0), []).
+
+put_catch(Tag, [], Stk) ->
+ put_stack({catch_tag,Tag}, Stk);
+put_catch(Tag, [{{catch_tag,_}}|_]=RevStk, Stk) ->
+ reverse(RevStk, put_stack({catch_tag,Tag}, Stk));
+put_catch(Tag, [Other|Stk], Acc) ->
+ put_catch(Tag, Stk, [Other|Acc]).
+
+drop_catch(Tag, [{{catch_tag,Tag}}|Stk]) -> [free|Stk];
+drop_catch(Tag, [Other|Stk]) -> [Other|drop_catch(Tag, Stk)].
+
+%%%
+%%% Finish the code generation for the bit syntax matching.
+%%%
+
+bs_function({function,Name,Arity,CLabel,Asm0}=Func) ->
+ case bs_needed(Asm0, 0, false, []) of
+ {false,[]} -> Func;
+ {true,Dict} ->
+ Asm = bs_replace(Asm0, Dict, []),
+ {function,Name,Arity,CLabel,Asm}
+ end.
+
+%%%
+%%% Pass 1: Found out which bs_restore's that are needed. For now we assume
+%%% that a bs_restore is needed unless it is directly preceeded by a bs_save.
+%%%
+
+bs_needed([{bs_save,Name},{bs_restore,Name}|T], N, _BsUsed, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_save,_Name}|T], N, _BsUsed, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_restore,Name}|T], N, _BsUsed, Dict) ->
+ case keysearch(Name, 1, Dict) of
+ {value,{Name,_}} -> bs_needed(T, N, true, Dict);
+ false -> bs_needed(T, N+1, true, [{Name,N}|Dict])
+ end;
+bs_needed([{bs_init,_,_}|T], N, _, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_init2,_,_,_,_,_}|T], N, _, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([{bs_start_match,_,_}|T], N, _, Dict) ->
+ bs_needed(T, N, true, Dict);
+bs_needed([_|T], N, BsUsed, Dict) ->
+ bs_needed(T, N, BsUsed, Dict);
+bs_needed([], _, BsUsed, Dict) -> {BsUsed,Dict}.
+
+%%%
+%%% Pass 2: Only needed if there were some bs_* instructions found.
+%%%
+%%% Remove any bs_save with a name that never were found to be restored
+%%% in the first pass.
+%%%
+
+bs_replace([{bs_save,Name}=Save,{bs_restore,Name}|T], Dict, Acc) ->
+ bs_replace([Save|T], Dict, Acc);
+bs_replace([{bs_save,Name}|T], Dict, Acc) ->
+ case keysearch(Name, 1, Dict) of
+ {value,{Name,N}} ->
+ bs_replace(T, Dict, [{bs_save,N}|Acc]);
+ false ->
+ bs_replace(T, Dict, Acc)
+ end;
+bs_replace([{bs_restore,Name}|T], Dict, Acc) ->
+ case keysearch(Name, 1, Dict) of
+ {value,{Name,N}} ->
+ bs_replace(T, Dict, [{bs_restore,N}|Acc]);
+ false ->
+ bs_replace(T, Dict, Acc)
+ end;
+bs_replace([{bs_init2,Fail,Bytes,Regs,Flags,Dst}|T0], Dict, Acc) ->
+ case bs_find_test_heap(T0) of
+ none ->
+ bs_replace(T0, Dict, [{bs_init2,Fail,Bytes,0,Regs,Flags,Dst}|Acc]);
+ {T,Words} ->
+ bs_replace(T, Dict, [{bs_init2,Fail,Bytes,Words,Regs,Flags,Dst}|Acc])
+ end;
+bs_replace([H|T], Dict, Acc) ->
+ bs_replace(T, Dict, [H|Acc]);
+bs_replace([], _, Acc) -> reverse(Acc).
+
+bs_find_test_heap(Is) ->
+ bs_find_test_heap_1(Is, []).
+
+bs_find_test_heap_1([{bs_put_integer,_,_,_,_,_}=I|Is], Acc) ->
+ bs_find_test_heap_1(Is, [I|Acc]);
+bs_find_test_heap_1([{bs_put_float,_,_,_,_,_}=I|Is], Acc) ->
+ bs_find_test_heap_1(Is, [I|Acc]);
+bs_find_test_heap_1([{bs_put_binary,_,_,_,_,_}=I|Is], Acc) ->
+ bs_find_test_heap_1(Is, [I|Acc]);
+bs_find_test_heap_1([{test_heap,Words,_}|Is], Acc) ->
+ {reverse(Acc, Is),Words};
+bs_find_test_heap_1(_, _) -> none.
+
+%% new_label(St) -> {L,St}.
+
+new_label(St) ->
+ L = St#cg.lcount,
+ {L,St#cg{lcount=L+1}}.
+
+flatmapfoldl(F, Accu0, [Hd|Tail]) ->
+ {R,Accu1} = F(Hd, Accu0),
+ {Rs,Accu2} = flatmapfoldl(F, Accu1, Tail),
+ {R++Rs,Accu2};
+flatmapfoldl(_, Accu, []) -> {[],Accu}.
+
+flatmapfoldr(F, Accu0, [Hd|Tail]) ->
+ {Rs,Accu1} = flatmapfoldr(F, Accu0, Tail),
+ {R,Accu2} = F(Hd, Accu1),
+ {R++Rs,Accu2};
+flatmapfoldr(_, Accu, []) -> {[],Accu}.
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl
new file mode 100644
index 0000000000..b561182932
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_core.erl
@@ -0,0 +1,1320 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_core.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $
+%%
+%% Purpose : Transform normal Erlang to Core Erlang
+
+%% At this stage all preprocessing has been done. All that is left are
+%% "pure" Erlang functions.
+%%
+%% Core transformation is done in three stages:
+%%
+%% 1. Flatten expressions into an internal core form without doing
+%% matching.
+%%
+%% 2. Step "forwards" over the icore code annotating each "top-level"
+%% thing with variable usage. Detect bound variables in matching
+%% and replace with explicit guard test. Annotate "internal-core"
+%% expressions with variables they use and create. Convert matches
+%% to cases when not pure assignments.
+%%
+%% 3. Step "backwards" over icore code using variable usage
+%% annotations to change implicit exported variables to explicit
+%% returns.
+%%
+%% To ensure the evaluation order we ensure that all arguments are
+%% safe. A "safe" is basically a core_lib simple with VERY restricted
+%% binaries.
+%%
+%% We have to be very careful with matches as these create variables.
+%% While we try not to flatten things more than necessary we must make
+%% sure that all matches are at the top level. For this we use the
+%% type "novars" which are non-match expressions. Cases and receives
+%% can also create problems due to exports variables so they are not
+%% "novars" either. I.e. a novars will not export variables.
+%%
+%% Annotations in the #iset, #iletrec, and all other internal records
+%% is kept in a record, #a, not in a list as in proper core. This is
+%% easier and faster and creates no problems as we have complete control
+%% over all annotations.
+%%
+%% On output, the annotation for most Core Erlang terms will contain
+%% the source line number. A few terms will be marked with the atom
+%% atom 'compiler_generated', to indicate that the compiler has generated
+%% them and that no warning should be generated if they are optimized
+%% away.
+%%
+%%
+%% In this translation:
+%%
+%% call ops are safes
+%% call arguments are safes
+%% match arguments are novars
+%% case arguments are novars
+%% receive timeouts are novars
+%% let/set arguments are expressions
+%% fun is not a safe
+
+-module(v3_core).
+
+-export([module/2,format_error/1]).
+
+-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2]).
+-import(ordsets, [add_element/2,del_element/2,is_element/2,
+ union/1,union/2,intersection/2,subtract/2]).
+
+-include("core_parse.hrl").
+
+-record(a, {us=[],ns=[],anno=[]}). %Internal annotation
+
+%% Internal core expressions and help functions.
+%% N.B. annotations fields in place as normal Core expressions.
+
+-record(iset, {anno=#a{},var,arg}).
+-record(iletrec, {anno=#a{},defs,body}).
+-record(imatch, {anno=#a{},pat,guard=[],arg,fc}).
+-record(icase, {anno=#a{},args,clauses,fc}).
+-record(iclause, {anno=#a{},pats,pguard=[],guard,body}).
+-record(ifun, {anno=#a{},id,vars,clauses,fc}).
+-record(iapply, {anno=#a{},op,args}).
+-record(icall, {anno=#a{},module,name,args}).
+-record(iprimop, {anno=#a{},name,args}).
+-record(itry, {anno=#a{},args,vars,body,evars,handler}).
+-record(icatch, {anno=#a{},body}).
+-record(ireceive1, {anno=#a{},clauses}).
+-record(ireceive2, {anno=#a{},clauses,timeout,action}).
+-record(iprotect, {anno=#a{},body}).
+-record(ibinary, {anno=#a{},segments}). %Not used in patterns.
+
+-record(core, {vcount=0, %Variable counter
+ fcount=0, %Function counter
+ ws=[]}). %Warnings.
+
+module({Mod,Exp,Forms}, _Opts) ->
+ Cexp = map(fun ({N,A}) -> #c_fname{id=N,arity=A} end, Exp),
+ {Kfs,As,Ws} = foldr(fun form/2, {[],[],[]}, Forms),
+ {ok,#c_module{name=#c_atom{val=Mod},exports=Cexp,attrs=As,defs=Kfs},Ws}.
+
+form({function,_,_,_,_}=F0, {Fs,As,Ws0}) ->
+ {F,Ws} = function(F0, Ws0),
+ {[F|Fs],As,Ws};
+form({attribute,_,_,_}=F, {Fs,As,Ws}) ->
+ {Fs,[attribute(F)|As],Ws}.
+
+attribute({attribute,_,Name,Val}) ->
+ #c_def{name=core_lib:make_literal(Name),
+ val=core_lib:make_literal(Val)}.
+
+function({function,_,Name,Arity,Cs0}, Ws0) ->
+ %%ok = io:fwrite("~p - ", [{Name,Arity}]),
+ St0 = #core{vcount=0,ws=Ws0},
+ {B0,St1} = body(Cs0, Arity, St0),
+ %%ok = io:fwrite("1", []),
+ %%ok = io:fwrite("~w:~p~n", [?LINE,B0]),
+ {B1,St2} = ubody(B0, St1),
+ %%ok = io:fwrite("2", []),
+ %%ok = io:fwrite("~w:~p~n", [?LINE,B1]),
+ {B2,#core{ws=Ws}} = cbody(B1, St2),
+ %%ok = io:fwrite("3~n", []),
+ {#c_def{name=#c_fname{id=Name,arity=Arity},val=B2},Ws}.
+
+body(Cs0, Arity, St0) ->
+ Anno = [element(2, hd(Cs0))],
+ {Args,St1} = new_vars(Anno, Arity, St0),
+ {Cs1,St2} = clauses(Cs0, St1),
+ {Ps,St3} = new_vars(Arity, St2), %Need new variables here
+ Fc = fail_clause(Ps, #c_tuple{es=[#c_atom{val=function_clause}|Ps]}),
+ {#ifun{anno=#a{anno=Anno},id=[],vars=Args,clauses=Cs1,fc=Fc},St3}.
+
+%% clause(Clause, State) -> {Cclause,State} | noclause.
+%% clauses([Clause], State) -> {[Cclause],State}.
+%% Convert clauses. Trap bad pattern aliases and remove clause from
+%% clause list.
+
+clauses([C0|Cs0], St0) ->
+ case clause(C0, St0) of
+ {noclause,St} -> clauses(Cs0, St);
+ {C,St1} ->
+ {Cs,St2} = clauses(Cs0, St1),
+ {[C|Cs],St2}
+ end;
+clauses([], St) -> {[],St}.
+
+clause({clause,Lc,H0,G0,B0}, St0) ->
+ case catch head(H0) of
+ {'EXIT',_}=Exit -> exit(Exit); %Propagate error
+ nomatch ->
+ St = add_warning(Lc, nomatch, St0),
+ {noclause,St}; %Bad pattern
+ H1 ->
+ {G1,St1} = guard(G0, St0),
+ {B1,St2} = exprs(B0, St1),
+ {#iclause{anno=#a{anno=[Lc]},pats=H1,guard=G1,body=B1},St2}
+ end.
+
+%% head([P]) -> [P].
+
+head(Ps) -> pattern_list(Ps).
+
+%% guard([Expr], State) -> {[Cexpr],State}.
+%% Build an explict and/or tree of guard alternatives, then traverse
+%% top-level and/or tree and "protect" inner tests.
+
+guard([], St) -> {[],St};
+guard(Gs0, St) ->
+ Gs = foldr(fun (Gt0, Rhs) ->
+ Gt1 = guard_tests(Gt0),
+ L = element(2, Gt1),
+ {op,L,'or',Gt1,Rhs}
+ end, guard_tests(last(Gs0)), first(Gs0)),
+ gexpr_top(Gs, St).
+
+guard_tests([]) -> [];
+guard_tests(Gs) ->
+ L = element(2, hd(Gs)),
+ {protect,L,foldr(fun (G, Rhs) -> {op,L,'and',G,Rhs} end, last(Gs), first(Gs))}.
+
+%% gexpr_top(Expr, State) -> {Cexpr,State}.
+%% Generate an internal core expression of a guard test. Explicitly
+%% handle outer boolean expressions and "protect" inner tests in a
+%% reasonably smart way.
+
+gexpr_top(E0, St0) ->
+ {E1,Eps0,Bools,St1} = gexpr(E0, [], St0),
+ {E,Eps,St} = force_booleans(Bools, E1, Eps0, St1),
+ {Eps++[E],St}.
+
+%% gexpr(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate an internal core expression of a guard test.
+
+gexpr({protect,Line,Arg}, Bools0, St0) ->
+ case gexpr(Arg, [], St0) of
+ {E0,[],Bools,St1} ->
+ {E,Eps,St} = force_booleans(Bools, E0, [], St1),
+ {E,Eps,Bools0,St};
+ {E0,Eps0,Bools,St1} ->
+ {E,Eps,St} = force_booleans(Bools, E0, Eps0, St1),
+ {#iprotect{anno=#a{anno=[Line]},body=Eps++[E]},[],Bools0,St}
+ end;
+gexpr({op,Line,Op,L,R}=Call, Bools0, St0) ->
+ case erl_internal:bool_op(Op, 2) of
+ true ->
+ {Le,Lps,Bools1,St1} = gexpr(L, Bools0, St0),
+ {Ll,Llps,St2} = force_safe(Le, St1),
+ {Re,Rps,Bools,St3} = gexpr(R, Bools1, St2),
+ {Rl,Rlps,St4} = force_safe(Re, St3),
+ Anno = [Line],
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_atom{anno=Anno,val=erlang},name=#c_atom{anno=Anno,val=Op},
+ args=[Ll,Rl]},Lps ++ Llps ++ Rps ++ Rlps,Bools,St4};
+ false ->
+ gexpr_test(Call, Bools0, St0)
+ end;
+gexpr({op,Line,Op,A}=Call, Bools0, St0) ->
+ case erl_internal:bool_op(Op, 1) of
+ true ->
+ {Ae,Aps,Bools,St1} = gexpr(A, Bools0, St0),
+ {Al,Alps,St2} = force_safe(Ae, St1),
+ Anno = [Line],
+ {#icall{anno=#a{anno=Anno}, %Must have an #a{}
+ module=#c_atom{anno=Anno,val=erlang},name=#c_atom{anno=Anno,val=Op},
+ args=[Al]},Aps ++ Alps,Bools,St2};
+ false ->
+ gexpr_test(Call, Bools0, St0)
+ end;
+gexpr(E0, Bools, St0) ->
+ gexpr_test(E0, Bools, St0).
+
+%% gexpr_test(Expr, Bools, State) -> {Cexpr,[PreExp],Bools,State}.
+%% Generate a guard test. At this stage we must be sure that we have
+%% a proper boolean value here so wrap things with an true test if we
+%% don't know, i.e. if it is not a comparison or a type test.
+
+gexpr_test({atom,L,true}, Bools, St0) ->
+ {#c_atom{anno=[L],val=true},[],Bools,St0};
+gexpr_test({atom,L,false}, Bools, St0) ->
+ {#c_atom{anno=[L],val=false},[],Bools,St0};
+gexpr_test(E0, Bools0, St0) ->
+ {E1,Eps0,St1} = expr(E0, St0),
+ %% Generate "top-level" test and argument calls.
+ case E1 of
+ #icall{anno=Anno,module=#c_atom{val=erlang},name=#c_atom{val=N},args=As} ->
+ Ar = length(As),
+ case erl_internal:type_test(N, Ar) orelse
+ erl_internal:comp_op(N, Ar) orelse
+ (N == internal_is_record andalso Ar == 3) of
+ true -> {E1,Eps0,Bools0,St1};
+ false ->
+ Lanno = Anno#a.anno,
+ {New,St2} = new_var(Lanno, St1),
+ Bools = [New|Bools0],
+ {#icall{anno=Anno, %Must have an #a{}
+ module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val='=:='},
+ args=[New,#c_atom{anno=Lanno,val=true}]},
+ Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
+ end;
+ _ ->
+ Anno = get_ianno(E1),
+ Lanno = get_lineno_anno(E1),
+ case core_lib:is_simple(E1) of
+ true ->
+ Bools = [E1|Bools0],
+ {#icall{anno=Anno, %Must have an #a{}
+ module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val='=:='},
+ args=[E1,#c_atom{anno=Lanno,val=true}]},Eps0,Bools,St1};
+ false ->
+ {New,St2} = new_var(Lanno, St1),
+ Bools = [New|Bools0],
+ {#icall{anno=Anno, %Must have an #a{}
+ module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val='=:='},
+ args=[New,#c_atom{anno=Lanno,val=true}]},
+ Eps0 ++ [#iset{anno=Anno,var=New,arg=E1}],Bools,St2}
+ end
+ end.
+
+force_booleans([], E, Eps, St) ->
+ {E,Eps,St};
+force_booleans([V|Vs], E0, Eps0, St0) ->
+ {E1,Eps1,St1} = force_safe(E0, St0),
+ Lanno = element(2, V),
+ Anno = #a{anno=Lanno},
+ Call = #icall{anno=Anno,module=#c_atom{anno=Lanno,val=erlang},
+ name=#c_atom{anno=Lanno,val=is_boolean},
+ args=[V]},
+ {New,St} = new_var(Lanno, St1),
+ Iset = #iset{anno=Anno,var=New,arg=Call},
+ Eps = Eps0 ++ Eps1 ++ [Iset],
+ E = #icall{anno=Anno,
+ module=#c_atom{anno=Lanno,val=erlang},name=#c_atom{anno=Lanno,val='and'},
+ args=[E1,New]},
+ force_booleans(Vs, E, Eps, St).
+
+%% exprs([Expr], State) -> {[Cexpr],State}.
+%% Flatten top-level exprs.
+
+exprs([E0|Es0], St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Es1,St2} = exprs(Es0, St1),
+ {Eps ++ [E1] ++ Es1,St2};
+exprs([], St) -> {[],St}.
+
+%% expr(Expr, State) -> {Cexpr,[PreExp],State}.
+%% Generate an internal core expression.
+
+expr({var,L,V}, St) -> {#c_var{anno=[L],name=V},[],St};
+expr({char,L,C}, St) -> {#c_char{anno=[L],val=C},[],St};
+expr({integer,L,I}, St) -> {#c_int{anno=[L],val=I},[],St};
+expr({float,L,F}, St) -> {#c_float{anno=[L],val=F},[],St};
+expr({atom,L,A}, St) -> {#c_atom{anno=[L],val=A},[],St};
+expr({nil,L}, St) -> {#c_nil{anno=[L]},[],St};
+expr({string,L,S}, St) -> {#c_string{anno=[L],val=S},[],St};
+expr({cons,L,H0,T0}, St0) ->
+ {H1,Hps,St1} = safe(H0, St0),
+ {T1,Tps,St2} = safe(T0, St1),
+ {#c_cons{anno=[L],hd=H1,tl=T1},Hps ++ Tps,St2};
+expr({lc,L,E,Qs}, St) ->
+ lc_tq(L, E, Qs, {nil,L}, St);
+expr({tuple,L,Es0}, St0) ->
+ {Es1,Eps,St1} = safe_list(Es0, St0),
+ {#c_tuple{anno=[L],es=Es1},Eps,St1};
+expr({bin,L,Es0}, St0) ->
+ {Es1,Eps,St1} = expr_bin(Es0, St0),
+ {#ibinary{anno=#a{anno=[L]},segments=Es1},Eps,St1};
+expr({block,_,Es0}, St0) ->
+ %% Inline the block directly.
+ {Es1,St1} = exprs(first(Es0), St0),
+ {E1,Eps,St2} = expr(last(Es0), St1),
+ {E1,Es1 ++ Eps,St2};
+expr({'if',L,Cs0}, St0) ->
+ {Cs1,St1} = clauses(Cs0, St0),
+ Fc = fail_clause([], #c_atom{val=if_clause}),
+ {#icase{anno=#a{anno=[L]},args=[],clauses=Cs1,fc=Fc},[],St1};
+expr({'case',L,E0,Cs0}, St0) ->
+ {E1,Eps,St1} = novars(E0, St0),
+ {Cs1,St2} = clauses(Cs0, St1),
+ {Fpat,St3} = new_var(St2),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=case_clause},Fpat]}),
+ {#icase{anno=#a{anno=[L]},args=[E1],clauses=Cs1,fc=Fc},Eps,St3};
+expr({'receive',L,Cs0}, St0) ->
+ {Cs1,St1} = clauses(Cs0, St0),
+ {#ireceive1{anno=#a{anno=[L]},clauses=Cs1}, [], St1};
+expr({'receive',L,Cs0,Te0,Tes0}, St0) ->
+ {Te1,Teps,St1} = novars(Te0, St0),
+ {Tes1,St2} = exprs(Tes0, St1),
+ {Cs1,St3} = clauses(Cs0, St2),
+ {#ireceive2{anno=#a{anno=[L]},
+ clauses=Cs1,timeout=Te1,action=Tes1},Teps,St3};
+expr({'try',L,Es0,[],Ecs,[]}, St0) ->
+ %% 'try ... catch ... end'
+ {Es1,St1} = exprs(Es0, St0),
+ {V,St2} = new_var(St1), %This name should be arbitrary
+ {Evs,Hs,St3} = try_exception(Ecs, St2),
+ {#itry{anno=#a{anno=[L]},args=Es1,vars=[V],body=[V],
+ evars=Evs,handler=Hs},
+ [],St3};
+expr({'try',L,Es0,Cs0,Ecs,[]}, St0) ->
+ %% 'try ... of ... catch ... end'
+ {Es1,St1} = exprs(Es0, St0),
+ {V,St2} = new_var(St1), %This name should be arbitrary
+ {Cs1,St3} = clauses(Cs0, St2),
+ {Fpat,St4} = new_var(St3),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=try_clause},Fpat]}),
+ {Evs,Hs,St5} = try_exception(Ecs, St4),
+ {#itry{anno=#a{anno=[L]},args=Es1,
+ vars=[V],body=[#icase{anno=#a{},args=[V],clauses=Cs1,fc=Fc}],
+ evars=Evs,handler=Hs},
+ [],St5};
+expr({'try',L,Es0,[],[],As0}, St0) ->
+ %% 'try ... after ... end'
+ {Es1,St1} = exprs(Es0, St0),
+ {As1,St2} = exprs(As0, St1),
+ {Evs,Hs,St3} = try_after(As1,St2),
+ {V,St4} = new_var(St3), % (must not exist in As1)
+ %% TODO: this duplicates the 'after'-code; should lift to function.
+ {#itry{anno=#a{anno=[L]},args=Es1,vars=[V],body=As1++[V],
+ evars=Evs,handler=Hs},
+ [],St4};
+expr({'try',L,Es,Cs,Ecs,As}, St0) ->
+ %% 'try ... [of ...] [catch ...] after ... end'
+ expr({'try',L,[{'try',L,Es,Cs,Ecs,[]}],[],[],As}, St0);
+expr({'catch',L,E0}, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {#icatch{anno=#a{anno=[L]},body=Eps ++ [E1]},[],St1};
+expr({'fun',L,{function,F,A},{_,_,_}=Id}, St) ->
+ {#c_fname{anno=[L,{id,Id}],id=F,arity=A},[],St};
+expr({'fun',L,{clauses,Cs},Id}, St) ->
+ fun_tq(Id, Cs, L, St);
+expr({call,L0,{remote,_,{atom,_,erlang},{atom,_,is_record}},[_,_,_]=As}, St)
+ when L0 < 0 ->
+ %% Compiler-generated erlang:is_record/3 should be converted to
+ %% erlang:internal_is_record/3.
+ L = -L0,
+ expr({call,L,{remote,L,{atom,L,erlang},{atom,L,internal_is_record}},As}, St);
+expr({call,L,{remote,_,M,F},As0}, St0) ->
+ {[M1,F1|As1],Aps,St1} = safe_list([M,F|As0], St0),
+ {#icall{anno=#a{anno=[L]},module=M1,name=F1,args=As1},Aps,St1};
+expr({call,Lc,{atom,Lf,F},As0}, St0) ->
+ {As1,Aps,St1} = safe_list(As0, St0),
+ Op = #c_fname{anno=[Lf],id=F,arity=length(As1)},
+ {#iapply{anno=#a{anno=[Lc]},op=Op,args=As1},Aps,St1};
+expr({call,L,FunExp,As0}, St0) ->
+ {Fun,Fps,St1} = safe(FunExp, St0),
+ {As1,Aps,St2} = safe_list(As0, St1),
+ {#iapply{anno=#a{anno=[L]},op=Fun,args=As1},Fps ++ Aps,St2};
+expr({match,L,P0,E0}, St0) ->
+ %% First fold matches together to create aliases.
+ {P1,E1} = fold_match(E0, P0),
+ {E2,Eps,St1} = novars(E1, St0),
+ P2 = (catch pattern(P1)),
+ {Fpat,St2} = new_var(St1),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=badmatch},Fpat]}),
+ case P2 of
+ {'EXIT',_}=Exit -> exit(Exit); %Propagate error
+ nomatch ->
+ St = add_warning(L, nomatch, St2),
+ {#icase{anno=#a{anno=[L]},
+ args=[E2],clauses=[],fc=Fc},Eps,St};
+ _Other ->
+ {#imatch{anno=#a{anno=[L]},pat=P2,arg=E2,fc=Fc},Eps,St2}
+ end;
+expr({op,_,'++',{lc,Llc,E,Qs},L2}, St) ->
+ %% Optimise this here because of the list comprehension algorithm.
+ lc_tq(Llc, E, Qs, L2, St);
+expr({op,L,Op,A0}, St0) ->
+ {A1,Aps,St1} = safe(A0, St0),
+ LineAnno = [L],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_atom{anno=LineAnno,val=erlang},
+ name=#c_atom{anno=LineAnno,val=Op},args=[A1]},Aps,St1};
+expr({op,L,Op,L0,R0}, St0) ->
+ {As,Aps,St1} = safe_list([L0,R0], St0),
+ LineAnno = [L],
+ {#icall{anno=#a{anno=LineAnno}, %Must have an #a{}
+ module=#c_atom{anno=LineAnno,val=erlang},
+ name=#c_atom{anno=LineAnno,val=Op},args=As},Aps,St1}.
+
+%% try_exception([ExcpClause], St) -> {[ExcpVar],Handler,St}.
+
+try_exception(Ecs0, St0) ->
+ %% Note that Tag is not needed for rethrow - it is already in Info.
+ {Evs,St1} = new_vars(3, St0), % Tag, Value, Info
+ {Ecs1,St2} = clauses(Ecs0, St1),
+ [_,Value,Info] = Evs,
+ Ec = #iclause{anno=#a{anno=[compiler_generated]},
+ pats=[#c_tuple{es=Evs}],guard=[#c_atom{val=true}],
+ body=[#iprimop{anno=#a{}, %Must have an #a{}
+ name=#c_atom{val=raise},
+ args=[Info,Value]}]},
+ Hs = [#icase{anno=#a{},args=[#c_tuple{es=Evs}],clauses=Ecs1,fc=Ec}],
+ {Evs,Hs,St2}.
+
+try_after(As, St0) ->
+ %% See above.
+ {Evs,St1} = new_vars(3, St0), % Tag, Value, Info
+ [_,Value,Info] = Evs,
+ B = As ++ [#iprimop{anno=#a{}, %Must have an #a{}
+ name=#c_atom{val=raise},
+ args=[Info,Value]}],
+ Ec = #iclause{anno=#a{anno=[compiler_generated]},
+ pats=[#c_tuple{es=Evs}],guard=[#c_atom{val=true}],
+ body=B},
+ Hs = [#icase{anno=#a{},args=[#c_tuple{es=Evs}],clauses=[],fc=Ec}],
+ {Evs,Hs,St1}.
+
+%% expr_bin([ArgExpr], St) -> {[Arg],[PreExpr],St}.
+%% Flatten the arguments of a bin. Do this straight left to right!
+
+expr_bin(Es, St) ->
+ foldr(fun (E, {Ces,Esp,St0}) ->
+ {Ce,Ep,St1} = bitstr(E, St0),
+ {[Ce|Ces],Ep ++ Esp,St1}
+ end, {[],[],St}, Es).
+
+bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) ->
+ {E1,Eps,St1} = safe(E0, St0),
+ {Size1,Eps2,St2} = safe(Size0, St1),
+ {#c_bitstr{val=E1,size=Size1,
+ unit=core_lib:make_literal(Unit),
+ type=core_lib:make_literal(Type),
+ flags=core_lib:make_literal(Flags)},
+ Eps ++ Eps2,St2}.
+
+%% fun_tq(Id, [Clauses], Line, State) -> {Fun,[PreExp],State}.
+
+fun_tq(Id, Cs0, L, St0) ->
+ {Cs1,St1} = clauses(Cs0, St0),
+ Arity = length((hd(Cs1))#iclause.pats),
+ {Args,St2} = new_vars(Arity, St1),
+ {Ps,St3} = new_vars(Arity, St2), %Need new variables here
+ Fc = fail_clause(Ps, #c_tuple{es=[#c_atom{val=function_clause}|Ps]}),
+ Fun = #ifun{anno=#a{anno=[L]},
+ id=[{id,Id}], %We KNOW!
+ vars=Args,clauses=Cs1,fc=Fc},
+ {Fun,[],St3}.
+
+%% lc_tq(Line, Exp, [Qualifier], More, State) -> {LetRec,[PreExp],State}.
+%% This TQ from Simon PJ pp 127-138.
+%% This gets a bit messy as we must transform all directly here. We
+%% recognise guard tests and try to fold them together and join to a
+%% preceding generators, this should give us better and more compact
+%% code.
+%% More could be transformed before calling lc_tq.
+
+lc_tq(Line, E, [{generate,Lg,P,G}|Qs0], More, St0) ->
+ {Gs,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+ {Name,St1} = new_fun_name("lc", St0),
+ {Head,St2} = new_var(St1),
+ {Tname,St3} = new_var_name(St2),
+ LA = [Line],
+ LAnno = #a{anno=LA},
+ Tail = #c_var{anno=LA,name=Tname},
+ {Arg,St4} = new_var(St3),
+ NewMore = {call,Lg,{atom,Lg,Name},[{var,Lg,Tname}]},
+ {Guardc,St5} = lc_guard_tests(Gs, St4), %These are always flat!
+ {Lc,Lps,St6} = lc_tq(Line, E, Qs1, NewMore, St5),
+ {Mc,Mps,St7} = expr(More, St6),
+ {Nc,Nps,St8} = expr(NewMore, St7),
+ case catch pattern(P) of
+ {'EXIT',_}=Exit ->
+ St9 = St8,
+ Pc = nomatch,
+ exit(Exit); %Propagate error
+ nomatch ->
+ St9 = add_warning(Line, nomatch, St8),
+ Pc = nomatch;
+ Pc ->
+ St9 = St8
+ end,
+ {Gc,Gps,St10} = safe(G, St9), %Will be a function argument!
+ Fc = fail_clause([Arg], #c_tuple{anno=LA,
+ es=[#c_atom{val=function_clause},Arg]}),
+ Cs0 = [#iclause{anno=#a{anno=[compiler_generated|LA]},
+ pats=[#c_cons{anno=LA,hd=Head,tl=Tail}],
+ guard=[],
+ body=Nps ++ [Nc]},
+ #iclause{anno=LAnno,
+ pats=[#c_nil{anno=LA}],guard=[],
+ body=Mps ++ [Mc]}],
+ Cs = case Pc of
+ nomatch -> Cs0;
+ _ ->
+ [#iclause{anno=LAnno,
+ pats=[#c_cons{anno=LA,hd=Pc,tl=Tail}],
+ guard=Guardc,
+ body=Lps ++ [Lc]}|Cs0]
+ end,
+ Fun = #ifun{anno=LAnno,id=[],vars=[Arg],clauses=Cs,fc=Fc},
+ {#iletrec{anno=LAnno,defs=[{Name,Fun}],
+ body=Gps ++ [#iapply{anno=LAnno,
+ op=#c_fname{anno=LA,id=Name,arity=1},
+ args=[Gc]}]},
+ [],St10};
+lc_tq(Line, E, [Fil0|Qs0], More, St0) ->
+ %% Special case sequences guard tests.
+ LA = [Line],
+ LAnno = #a{anno=LA},
+ case is_guard_test(Fil0) of
+ true ->
+ {Gs0,Qs1} = splitwith(fun is_guard_test/1, Qs0),
+ {Lc,Lps,St1} = lc_tq(Line, E, Qs1, More, St0),
+ {Mc,Mps,St2} = expr(More, St1),
+ {Gs,St3} = lc_guard_tests([Fil0|Gs0], St2), %These are always flat!
+ {#icase{anno=LAnno,
+ args=[],
+ clauses=[#iclause{anno=LAnno,pats=[],
+ guard=Gs,body=Lps ++ [Lc]}],
+ fc=#iclause{anno=LAnno,pats=[],guard=[],body=Mps ++ [Mc]}},
+ [],St3};
+ false ->
+ {Lc,Lps,St1} = lc_tq(Line, E, Qs0, More, St0),
+ {Mc,Mps,St2} = expr(More, St1),
+ {Fpat,St3} = new_var(St2),
+ Fc = fail_clause([Fpat], #c_tuple{es=[#c_atom{val=case_clause},Fpat]}),
+ %% Do a novars little optimisation here.
+ case Fil0 of
+ {op,_,'not',Fil1} ->
+ {Filc,Fps,St4} = novars(Fil1, St3),
+ {#icase{anno=LAnno,
+ args=[Filc],
+ clauses=[#iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=true}],
+ guard=[],
+ body=Mps ++ [Mc]},
+ #iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=false}],
+ guard=[],
+ body=Lps ++ [Lc]}],
+ fc=Fc},
+ Fps,St4};
+ _Other ->
+ {Filc,Fps,St4} = novars(Fil0, St3),
+ {#icase{anno=LAnno,
+ args=[Filc],
+ clauses=[#iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=true}],
+ guard=[],
+ body=Lps ++ [Lc]},
+ #iclause{anno=LAnno,
+ pats=[#c_atom{anno=LA,val=false}],
+ guard=[],
+ body=Mps ++ [Mc]}],
+ fc=Fc},
+ Fps,St4}
+ end
+ end;
+lc_tq(Line, E, [], More, St) ->
+ expr({cons,Line,E,More}, St).
+
+lc_guard_tests([], St) -> {[],St};
+lc_guard_tests(Gs0, St) ->
+ Gs = guard_tests(Gs0),
+ gexpr_top(Gs, St).
+
+%% is_guard_test(Expression) -> true | false.
+%% Test if a general expression is a guard test. Use erl_lint here
+%% as it now allows sys_pre_expand transformed source.
+
+is_guard_test(E) -> erl_lint:is_guard_test(E).
+
+%% novars(Expr, State) -> {Novars,[PreExpr],State}.
+%% Generate a novars expression, basically a call or a safe. At this
+%% level we do not need to do a deep check.
+
+novars(E0, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Se,Sps,St2} = force_novars(E1, St1),
+ {Se,Eps ++ Sps,St2}.
+
+force_novars(#iapply{}=App, St) -> {App,[],St};
+force_novars(#icall{}=Call, St) -> {Call,[],St};
+force_novars(#iprimop{}=Prim, St) -> {Prim,[],St};
+force_novars(#ifun{}=Fun, St) -> {Fun,[],St}; %These are novars too
+force_novars(#ibinary{}=Bin, St) -> {Bin,[],St};
+force_novars(Ce, St) ->
+ force_safe(Ce, St).
+
+%% safe(Expr, State) -> {Safe,[PreExpr],State}.
+%% Generate an internal safe expression. These are simples without
+%% binaries which can fail. At this level we do not need to do a
+%% deep check. Must do special things with matches here.
+
+safe(E0, St0) ->
+ {E1,Eps,St1} = expr(E0, St0),
+ {Se,Sps,St2} = force_safe(E1, St1),
+ {Se,Eps ++ Sps,St2}.
+
+safe_list(Es, St) ->
+ foldr(fun (E, {Ces,Esp,St0}) ->
+ {Ce,Ep,St1} = safe(E, St0),
+ {[Ce|Ces],Ep ++ Esp,St1}
+ end, {[],[],St}, Es).
+
+force_safe(#imatch{anno=Anno,pat=P,arg=E,fc=Fc}, St0) ->
+ {Le,Lps,St1} = force_safe(E, St0),
+ {Le,Lps ++ [#imatch{anno=Anno,pat=P,arg=Le,fc=Fc}],St1};
+force_safe(Ce, St0) ->
+ case is_safe(Ce) of
+ true -> {Ce,[],St0};
+ false ->
+ {V,St1} = new_var(St0),
+ {V,[#iset{var=V,arg=Ce}],St1}
+ end.
+
+is_safe(#c_cons{}) -> true;
+is_safe(#c_tuple{}) -> true;
+is_safe(#c_var{}) -> true;
+is_safe(E) -> core_lib:is_atomic(E).
+
+%%% %% variable(Expr, State) -> {Variable,[PreExpr],State}.
+%%% %% force_variable(Expr, State) -> {Variable,[PreExpr],State}.
+%%% %% Generate a variable.
+
+%%% variable(E0, St0) ->
+%%% {E1,Eps,St1} = expr(E0, St0),
+%%% {V,Vps,St2} = force_variable(E1, St1),
+%%% {V,Eps ++ Vps,St2}.
+
+%%% force_variable(#c_var{}=Var, St) -> {Var,[],St};
+%%% force_variable(Ce, St0) ->
+%%% {V,St1} = new_var(St0),
+%%% {V,[#iset{var=V,arg=Ce}],St1}.
+
+%%% %% atomic(Expr, State) -> {Atomic,[PreExpr],State}.
+%%% %% force_atomic(Expr, State) -> {Atomic,[PreExpr],State}.
+
+%%% atomic(E0, St0) ->
+%%% {E1,Eps,St1} = expr(E0, St0),
+%%% {A,Aps,St2} = force_atomic(E1, St1),
+%%% {A,Eps ++ Aps,St2}.
+
+%%% force_atomic(Ce, St0) ->
+%%% case core_lib:is_atomic(Ce) of
+%%% true -> {Ce,[],St0};
+%%% false ->
+%%% {V,St1} = new_var(St0),
+%%% {V,[#iset{var=V,arg=Ce}],St1}
+%%% end.
+
+%% fold_match(MatchExpr, Pat) -> {MatchPat,Expr}.
+%% Fold nested matches into one match with aliased patterns.
+
+fold_match({match,L,P0,E0}, P) ->
+ {P1,E1} = fold_match(E0, P),
+ {{match,L,P0,P1},E1};
+fold_match(E, P) -> {P,E}.
+
+%% pattern(Pattern) -> CorePat.
+%% Transform a pattern by removing line numbers. We also normalise
+%% aliases in patterns to standard form, {alias,Pat,[Var]}.
+
+pattern({var,L,V}) -> #c_var{anno=[L],name=V};
+pattern({char,L,C}) -> #c_char{anno=[L],val=C};
+pattern({integer,L,I}) -> #c_int{anno=[L],val=I};
+pattern({float,L,F}) -> #c_float{anno=[L],val=F};
+pattern({atom,L,A}) -> #c_atom{anno=[L],val=A};
+pattern({string,L,S}) -> #c_string{anno=[L],val=S};
+pattern({nil,L}) -> #c_nil{anno=[L]};
+pattern({cons,L,H,T}) ->
+ #c_cons{anno=[L],hd=pattern(H),tl=pattern(T)};
+pattern({tuple,L,Ps}) ->
+ #c_tuple{anno=[L],es=pattern_list(Ps)};
+pattern({bin,L,Ps}) ->
+ %% We don't create a #ibinary record here, since there is
+ %% no need to hold any used/new annoations in a pattern.
+ #c_binary{anno=[L],segments=pat_bin(Ps)};
+pattern({match,_,P1,P2}) ->
+ pat_alias(pattern(P1), pattern(P2)).
+
+%% bin_pattern_list([BinElement]) -> [BinSeg].
+
+pat_bin(Ps) -> map(fun pat_segment/1, Ps).
+
+pat_segment({bin_element,_,Term,Size,[Type,{unit,Unit}|Flags]}) ->
+ #c_bitstr{val=pattern(Term),size=pattern(Size),
+ unit=core_lib:make_literal(Unit),
+ type=core_lib:make_literal(Type),
+ flags=core_lib:make_literal(Flags)}.
+
+%% pat_alias(CorePat, CorePat) -> AliasPat.
+%% Normalise aliases. Trap bad aliases by throwing 'nomatch'.
+
+pat_alias(#c_var{name=V1}, P2) -> #c_alias{var=#c_var{name=V1},pat=P2};
+pat_alias(P1, #c_var{name=V2}) -> #c_alias{var=#c_var{name=V2},pat=P1};
+pat_alias(#c_cons{}=Cons, #c_string{anno=A,val=[H|T]}=S) ->
+ pat_alias(Cons, #c_cons{anno=A,hd=#c_char{anno=A,val=H},
+ tl=S#c_string{val=T}});
+pat_alias(#c_string{anno=A,val=[H|T]}=S, #c_cons{}=Cons) ->
+ pat_alias(#c_cons{anno=A,hd=#c_char{anno=A,val=H},
+ tl=S#c_string{val=T}}, Cons);
+pat_alias(#c_nil{}=Nil, #c_string{val=[]}) ->
+ Nil;
+pat_alias(#c_string{val=[]}, #c_nil{}=Nil) ->
+ Nil;
+pat_alias(#c_cons{anno=A,hd=H1,tl=T1}, #c_cons{hd=H2,tl=T2}) ->
+ #c_cons{anno=A,hd=pat_alias(H1, H2),tl=pat_alias(T1, T2)};
+pat_alias(#c_tuple{es=Es1}, #c_tuple{es=Es2}) ->
+ #c_tuple{es=pat_alias_list(Es1, Es2)};
+pat_alias(#c_char{val=C}=Char, #c_int{val=C}) ->
+ Char;
+pat_alias(#c_int{val=C}, #c_char{val=C}=Char) ->
+ Char;
+pat_alias(#c_alias{var=V1,pat=P1},
+ #c_alias{var=V2,pat=P2}) ->
+ if V1 == V2 -> pat_alias(P1, P2);
+ true -> #c_alias{var=V1,pat=#c_alias{var=V2,pat=pat_alias(P1, P2)}}
+ end;
+pat_alias(#c_alias{var=V1,pat=P1}, P2) ->
+ #c_alias{var=V1,pat=pat_alias(P1, P2)};
+pat_alias(P1, #c_alias{var=V2,pat=P2}) ->
+ #c_alias{var=V2,pat=pat_alias(P1, P2)};
+pat_alias(P, P) -> P;
+pat_alias(_, _) -> throw(nomatch).
+
+%% pat_alias_list([A1], [A2]) -> [A].
+
+pat_alias_list([A1|A1s], [A2|A2s]) ->
+ [pat_alias(A1, A2)|pat_alias_list(A1s, A2s)];
+pat_alias_list([], []) -> [];
+pat_alias_list(_, _) -> throw(nomatch).
+
+%% pattern_list([P]) -> [P].
+
+pattern_list(Ps) -> map(fun pattern/1, Ps).
+
+%% first([A]) -> [A].
+%% last([A]) -> A.
+
+first([_]) -> [];
+first([H|T]) -> [H|first(T)].
+
+last([L]) -> L;
+last([_|T]) -> last(T).
+
+%% make_vars([Name]) -> [{Var,Name}].
+
+make_vars(Vs) -> [ #c_var{name=V} || V <- Vs ].
+
+%% new_fun_name(Type, State) -> {FunName,State}.
+
+new_fun_name(Type, #core{fcount=C}=St) ->
+ {list_to_atom(Type ++ "$^" ++ integer_to_list(C)),St#core{fcount=C+1}}.
+
+%% new_var_name(State) -> {VarName,State}.
+
+new_var_name(#core{vcount=C}=St) ->
+ {list_to_atom("cor" ++ integer_to_list(C)),St#core{vcount=C + 1}}.
+
+%% new_var(State) -> {{var,Name},State}.
+%% new_var(LineAnno, State) -> {{var,Name},State}.
+
+new_var(St) ->
+ new_var([], St).
+
+new_var(Anno, St0) ->
+ {New,St} = new_var_name(St0),
+ {#c_var{anno=Anno,name=New},St}.
+
+%% new_vars(Count, State) -> {[Var],State}.
+%% new_vars(Anno, Count, State) -> {[Var],State}.
+%% Make Count new variables.
+
+new_vars(N, St) -> new_vars_1(N, [], St, []).
+new_vars(Anno, N, St) -> new_vars_1(N, Anno, St, []).
+
+new_vars_1(N, Anno, St0, Vs) when N > 0 ->
+ {V,St1} = new_var(Anno, St0),
+ new_vars_1(N-1, Anno, St1, [V|Vs]);
+new_vars_1(0, _, St, Vs) -> {Vs,St}.
+
+fail_clause(Pats, A) ->
+ #iclause{anno=#a{anno=[compiler_generated]},
+ pats=Pats,guard=[],
+ body=[#iprimop{anno=#a{},name=#c_atom{val=match_fail},args=[A]}]}.
+
+ubody(B, St) -> uexpr(B, [], St).
+
+%% uclauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
+
+uclauses(Lcs, Ks, St0) ->
+ mapfoldl(fun (Lc, St) -> uclause(Lc, Ks, St) end, St0, Lcs).
+
+%% uclause(Lclause, [KnownVar], State) -> {Lclause,State}.
+
+uclause(Cl0, Ks, St0) ->
+ {Cl1,_Pvs,Used,New,St1} = uclause(Cl0, Ks, Ks, St0),
+ A0 = get_ianno(Cl1),
+ A = A0#a{us=Used,ns=New},
+ {Cl1#iclause{anno=A},St1}.
+
+uclause(#iclause{anno=Anno,pats=Ps0,guard=G0,body=B0}, Pks, Ks0, St0) ->
+ {Ps1,Pg,Pvs,Pus,St1} = upattern_list(Ps0, Pks, St0),
+ Pu = union(Pus, intersection(Pvs, Ks0)),
+ Pn = subtract(Pvs, Pu),
+ Ks1 = union(Pn, Ks0),
+ {G1,St2} = uguard(Pg, G0, Ks1, St1),
+ Gu = used_in_any(G1),
+ Gn = new_in_any(G1),
+ Ks2 = union(Gn, Ks1),
+ {B1,St3} = uexprs(B0, Ks2, St2),
+ Used = intersection(union([Pu,Gu,used_in_any(B1)]), Ks0),
+ New = union([Pn,Gn,new_in_any(B1)]),
+ {#iclause{anno=Anno,pats=Ps1,guard=G1,body=B1},Pvs,Used,New,St3}.
+
+%% uguard([Test], [Kexpr], [KnownVar], State) -> {[Kexpr],State}.
+%% Build a guard expression list by folding in the equality tests.
+
+uguard([], [], _, St) -> {[],St};
+uguard(Pg, [], Ks, St) ->
+ %% No guard, so fold together equality tests.
+ uguard(first(Pg), [last(Pg)], Ks, St);
+uguard(Pg, Gs0, Ks, St0) ->
+ %% Gs0 must contain at least one element here.
+ {Gs3,St5} = foldr(fun (T, {Gs1,St1}) ->
+ {L,St2} = new_var(St1),
+ {R,St3} = new_var(St2),
+ {[#iset{var=L,arg=T}] ++ first(Gs1) ++
+ [#iset{var=R,arg=last(Gs1)},
+ #icall{anno=#a{}, %Must have an #a{}
+ module=#c_atom{val=erlang},
+ name=#c_atom{val='and'},
+ args=[L,R]}],
+ St3}
+ end, {Gs0,St0}, Pg),
+ %%ok = io:fwrite("core ~w: ~p~n", [?LINE,Gs3]),
+ uexprs(Gs3, Ks, St5).
+
+%% uexprs([Kexpr], [KnownVar], State) -> {[Kexpr],State}.
+
+uexprs([#imatch{anno=A,pat=P0,arg=Arg,fc=Fc}|Les], Ks, St0) ->
+ %% Optimise for simple set of unbound variable.
+ case upattern(P0, Ks, St0) of
+ {#c_var{},[],_Pvs,_Pus,_} ->
+ %% Throw our work away and just set to iset.
+ uexprs([#iset{var=P0,arg=Arg}|Les], Ks, St0);
+ _Other ->
+ %% Throw our work away and set to icase.
+ if
+ Les == [] ->
+ %% Need to explicitly return match "value", make
+ %% safe for efficiency.
+ {La,Lps,St1} = force_safe(Arg, St0),
+ Mc = #iclause{anno=A,pats=[P0],guard=[],body=[La]},
+ uexprs(Lps ++ [#icase{anno=A,
+ args=[La],clauses=[Mc],fc=Fc}], Ks, St1);
+ true ->
+ Mc = #iclause{anno=A,pats=[P0],guard=[],body=Les},
+ uexprs([#icase{anno=A,args=[Arg],
+ clauses=[Mc],fc=Fc}], Ks, St0)
+ end
+ end;
+uexprs([Le0|Les0], Ks, St0) ->
+ {Le1,St1} = uexpr(Le0, Ks, St0),
+ {Les1,St2} = uexprs(Les0, union((core_lib:get_anno(Le1))#a.ns, Ks), St1),
+ {[Le1|Les1],St2};
+uexprs([], _, St) -> {[],St}.
+
+uexpr(#iset{anno=A,var=V,arg=A0}, Ks, St0) ->
+ {A1,St1} = uexpr(A0, Ks, St0),
+ {#iset{anno=A#a{us=del_element(V#c_var.name, (core_lib:get_anno(A1))#a.us),
+ ns=add_element(V#c_var.name, (core_lib:get_anno(A1))#a.ns)},
+ var=V,arg=A1},St1};
+%% imatch done in uexprs.
+uexpr(#iletrec{anno=A,defs=Fs0,body=B0}, Ks, St0) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{Fs0,B0}]),
+ {Fs1,St1} = mapfoldl(fun ({Name,F0}, St0) ->
+ {F1,St1} = uexpr(F0, Ks, St0),
+ {{Name,F1},St1}
+ end, St0, Fs0),
+ {B1,St2} = uexprs(B0, Ks, St1),
+ Used = used_in_any(map(fun ({_,F}) -> F end, Fs1) ++ B1),
+ {#iletrec{anno=A#a{us=Used,ns=[]},defs=Fs1,body=B1},St2};
+uexpr(#icase{anno=A,args=As0,clauses=Cs0,fc=Fc0}, Ks, St0) ->
+ %% As0 will never generate new variables.
+ {As1,St1} = uexpr_list(As0, Ks, St0),
+ {Cs1,St2} = uclauses(Cs0, Ks, St1),
+ {Fc1,St3} = uclause(Fc0, Ks, St2),
+ Used = union(used_in_any(As1), used_in_any(Cs1)),
+ New = new_in_all(Cs1),
+ {#icase{anno=A#a{us=Used,ns=New},args=As1,clauses=Cs1,fc=Fc1},St3};
+uexpr(#ifun{anno=A,id=Id,vars=As,clauses=Cs0,fc=Fc0}, Ks0, St0) ->
+ Avs = lit_list_vars(As),
+ Ks1 = union(Avs, Ks0),
+ {Cs1,St1} = ufun_clauses(Cs0, Ks1, St0),
+ {Fc1,St2} = ufun_clause(Fc0, Ks1, St1),
+ Used = subtract(intersection(used_in_any(Cs1), Ks0), Avs),
+ {#ifun{anno=A#a{us=Used,ns=[]},id=Id,vars=As,clauses=Cs1,fc=Fc1},St2};
+uexpr(#iapply{anno=A,op=Op,args=As}, _, St) ->
+ Used = union(lit_vars(Op), lit_list_vars(As)),
+ {#iapply{anno=A#a{us=Used},op=Op,args=As},St};
+uexpr(#iprimop{anno=A,name=Name,args=As}, _, St) ->
+ Used = lit_list_vars(As),
+ {#iprimop{anno=A#a{us=Used},name=Name,args=As},St};
+uexpr(#icall{anno=A,module=Mod,name=Name,args=As}, _, St) ->
+ Used = union([lit_vars(Mod),lit_vars(Name),lit_list_vars(As)]),
+ {#icall{anno=A#a{us=Used},module=Mod,name=Name,args=As},St};
+uexpr(#itry{anno=A,args=As0,vars=Vs,body=Bs0,evars=Evs,handler=Hs0}, Ks, St0) ->
+ %% Note that we export only from body and exception.
+ {As1,St1} = uexprs(As0, Ks, St0),
+ {Bs1,St2} = uexprs(Bs0, Ks, St1),
+ {Hs1,St3} = uexprs(Hs0, Ks, St2),
+ Used = intersection(used_in_any(Bs1++Hs1++As1), Ks),
+ New = new_in_all(Bs1++Hs1),
+ {#itry{anno=A#a{us=Used,ns=New},
+ args=As1,vars=Vs,body=Bs1,evars=Evs,handler=Hs1},St3};
+uexpr(#icatch{anno=A,body=Es0}, Ks, St0) ->
+ {Es1,St1} = uexprs(Es0, Ks, St0),
+ {#icatch{anno=A#a{us=used_in_any(Es1)},body=Es1},St1};
+uexpr(#ireceive1{anno=A,clauses=Cs0}, Ks, St0) ->
+ {Cs1,St1} = uclauses(Cs0, Ks, St0),
+ {#ireceive1{anno=A#a{us=used_in_any(Cs1),ns=new_in_all(Cs1)},
+ clauses=Cs1},St1};
+uexpr(#ireceive2{anno=A,clauses=Cs0,timeout=Te0,action=Tes0}, Ks, St0) ->
+ %% Te0 will never generate new variables.
+ {Te1,St1} = uexpr(Te0, Ks, St0),
+ {Cs1,St2} = uclauses(Cs0, Ks, St1),
+ {Tes1,St3} = uexprs(Tes0, Ks, St2),
+ Used = union([used_in_any(Cs1),used_in_any(Tes1),
+ (core_lib:get_anno(Te1))#a.us]),
+ New = case Cs1 of
+ [] -> new_in_any(Tes1);
+ _ -> intersection(new_in_all(Cs1), new_in_any(Tes1))
+ end,
+ {#ireceive2{anno=A#a{us=Used,ns=New},
+ clauses=Cs1,timeout=Te1,action=Tes1},St3};
+uexpr(#iprotect{anno=A,body=Es0}, Ks, St0) ->
+ {Es1,St1} = uexprs(Es0, Ks, St0),
+ Used = used_in_any(Es1),
+ {#iprotect{anno=A#a{us=Used},body=Es1},St1}; %No new variables escape!
+uexpr(#ibinary{anno=A,segments=Ss}, _, St) ->
+ Used = bitstr_vars(Ss),
+ {#ibinary{anno=A#a{us=Used},segments=Ss},St};
+uexpr(Lit, _, St) ->
+ true = core_lib:is_simple(Lit), %Sanity check!
+ Vs = lit_vars(Lit),
+ Anno = core_lib:get_anno(Lit),
+ {core_lib:set_anno(Lit, #a{us=Vs,anno=Anno}),St}.
+
+uexpr_list(Les0, Ks, St0) ->
+ mapfoldl(fun (Le, St) -> uexpr(Le, Ks, St) end, St0, Les0).
+
+%% ufun_clauses([Lclause], [KnownVar], State) -> {[Lclause],State}.
+
+ufun_clauses(Lcs, Ks, St0) ->
+ mapfoldl(fun (Lc, St) -> ufun_clause(Lc, Ks, St) end, St0, Lcs).
+
+%% ufun_clause(Lclause, [KnownVar], State) -> {Lclause,State}.
+
+ufun_clause(Cl0, Ks, St0) ->
+ {Cl1,Pvs,Used,_,St1} = uclause(Cl0, [], Ks, St0),
+ A0 = get_ianno(Cl1),
+ A = A0#a{us=subtract(intersection(Used, Ks), Pvs),ns=[]},
+ {Cl1#iclause{anno=A},St1}.
+
+%% upattern(Pat, [KnownVar], State) ->
+%% {Pat,[GuardTest],[NewVar],[UsedVar],State}.
+
+upattern(#c_var{name='_'}, _, St0) ->
+ {New,St1} = new_var_name(St0),
+ {#c_var{name=New},[],[New],[],St1};
+upattern(#c_var{name=V}=Var, Ks, St0) ->
+ case is_element(V, Ks) of
+ true ->
+ {N,St1} = new_var_name(St0),
+ New = #c_var{name=N},
+ Test = #icall{anno=#a{us=add_element(N, [V])},
+ module=#c_atom{val=erlang},
+ name=#c_atom{val='=:='},
+ args=[New,Var]},
+ %% Test doesn't need protecting.
+ {New,[Test],[N],[],St1};
+ false -> {Var,[],[V],[],St0}
+ end;
+upattern(#c_cons{hd=H0,tl=T0}=Cons, Ks, St0) ->
+ {H1,Hg,Hv,Hu,St1} = upattern(H0, Ks, St0),
+ {T1,Tg,Tv,Tu,St2} = upattern(T0, union(Hv, Ks), St1),
+ {Cons#c_cons{hd=H1,tl=T1},Hg ++ Tg,union(Hv, Tv),union(Hu, Tu),St2};
+upattern(#c_tuple{es=Es0}=Tuple, Ks, St0) ->
+ {Es1,Esg,Esv,Eus,St1} = upattern_list(Es0, Ks, St0),
+ {Tuple#c_tuple{es=Es1},Esg,Esv,Eus,St1};
+upattern(#c_binary{segments=Es0}=Bin, Ks, St0) ->
+ {Es1,Esg,Esv,Eus,St1} = upat_bin(Es0, Ks, St0),
+ {Bin#c_binary{segments=Es1},Esg,Esv,Eus,St1};
+upattern(#c_alias{var=V0,pat=P0}=Alias, Ks, St0) ->
+ {V1,Vg,Vv,Vu,St1} = upattern(V0, Ks, St0),
+ {P1,Pg,Pv,Pu,St2} = upattern(P0, union(Vv, Ks), St1),
+ {Alias#c_alias{var=V1,pat=P1},Vg ++ Pg,union(Vv, Pv),union(Vu, Pu),St2};
+upattern(Other, _, St) -> {Other,[],[],[],St}. %Constants
+
+%% upattern_list([Pat], [KnownVar], State) ->
+%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
+
+upattern_list([P0|Ps0], Ks, St0) ->
+ {P1,Pg,Pv,Pu,St1} = upattern(P0, Ks, St0),
+ {Ps1,Psg,Psv,Psu,St2} = upattern_list(Ps0, union(Pv, Ks), St1),
+ {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
+upattern_list([], _, St) -> {[],[],[],[],St}.
+
+%% upat_bin([Pat], [KnownVar], State) ->
+%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
+upat_bin(Es0, Ks, St0) ->
+ upat_bin(Es0, Ks, [], St0).
+
+%% upat_bin([Pat], [KnownVar], [LocalVar], State) ->
+%% {[Pat],[GuardTest],[NewVar],[UsedVar],State}.
+upat_bin([P0|Ps0], Ks, Bs, St0) ->
+ {P1,Pg,Pv,Pu,Bs1,St1} = upat_element(P0, Ks, Bs, St0),
+ {Ps1,Psg,Psv,Psu,St2} = upat_bin(Ps0, union(Pv, Ks), Bs1, St1),
+ {[P1|Ps1],Pg ++ Psg,union(Pv, Psv),union(Pu, Psu),St2};
+upat_bin([], _, _, St) -> {[],[],[],[],St}.
+
+
+%% upat_element(Segment, [KnownVar], [LocalVar], State) ->
+%% {Segment,[GuardTest],[NewVar],[UsedVar],[LocalVar],State}
+upat_element(#c_bitstr{val=H0,size=Sz}=Seg, Ks, Bs, St0) ->
+ {H1,Hg,Hv,[],St1} = upattern(H0, Ks, St0),
+ Bs1 = case H0 of
+ #c_var{name=Hname} ->
+ case H1 of
+ #c_var{name=Hname} ->
+ Bs;
+ #c_var{name=Other} ->
+ [{Hname, Other}|Bs]
+ end;
+ _ ->
+ Bs
+ end,
+ {Sz1, Us} = case Sz of
+ #c_var{name=Vname} ->
+ rename_bitstr_size(Vname, Bs);
+ _Other -> {Sz, []}
+ end,
+ {Seg#c_bitstr{val=H1, size=Sz1},Hg,Hv,Us,Bs1,St1}.
+
+rename_bitstr_size(V, [{V, N}|_]) ->
+ New = #c_var{name=N},
+ {New, [N]};
+rename_bitstr_size(V, [_|Rest]) ->
+ rename_bitstr_size(V, Rest);
+rename_bitstr_size(V, []) ->
+ Old = #c_var{name=V},
+ {Old, [V]}.
+
+used_in_any(Les) ->
+ foldl(fun (Le, Ns) -> union((core_lib:get_anno(Le))#a.us, Ns) end,
+ [], Les).
+
+new_in_any(Les) ->
+ foldl(fun (Le, Ns) -> union((core_lib:get_anno(Le))#a.ns, Ns) end,
+ [], Les).
+
+new_in_all([Le|Les]) ->
+ foldl(fun (L, Ns) -> intersection((core_lib:get_anno(L))#a.ns, Ns) end,
+ (core_lib:get_anno(Le))#a.ns, Les);
+new_in_all([]) -> [].
+
+%% The AfterVars are the variables which are used afterwards. We need
+%% this to work out which variables are actually exported and used
+%% from case/receive. In subblocks/clauses the AfterVars of the block
+%% are just the exported variables.
+
+cbody(B0, St0) ->
+ {B1,_,_,St1} = cexpr(B0, [], St0),
+ {B1,St1}.
+
+%% cclause(Lclause, [AfterVar], State) -> {Cclause,State}.
+%% The AfterVars are the exported variables.
+
+cclause(#iclause{anno=#a{anno=Anno},pats=Ps,guard=G0,body=B0}, Exp, St0) ->
+ {B1,_Us1,St1} = cexprs(B0, Exp, St0),
+ {G1,St2} = cguard(G0, St1),
+ {#c_clause{anno=Anno,pats=Ps,guard=G1,body=B1},St2}.
+
+cclauses(Lcs, Es, St0) ->
+ mapfoldl(fun (Lc, St) -> cclause(Lc, Es, St) end, St0, Lcs).
+
+cguard([], St) -> {#c_atom{val=true},St};
+cguard(Gs, St0) ->
+ {G,_,St1} = cexprs(Gs, [], St0),
+ {G,St1}.
+
+%% cexprs([Lexpr], [AfterVar], State) -> {Cexpr,[AfterVar],State}.
+%% Must be sneaky here at the last expr when combining exports for the
+%% whole sequence and exports for that expr.
+
+cexprs([#iset{var=#c_var{name=Name}=Var}=Iset], As, St) ->
+ %% Make return value explicit, and make Var true top level.
+ cexprs([Iset,Var#c_var{anno=#a{us=[Name]}}], As, St);
+cexprs([Le], As, St0) ->
+ {Ce,Es,Us,St1} = cexpr(Le, As, St0),
+ Exp = make_vars(As), %The export variables
+ if
+ Es == [] -> {core_lib:make_values([Ce|Exp]),union(Us, As),St1};
+ true ->
+ {R,St2} = new_var(St1),
+ {#c_let{anno=get_lineno_anno(Ce),
+ vars=[R|make_vars(Es)],arg=Ce,
+ body=core_lib:make_values([R|Exp])},
+ union(Us, As),St2}
+ end;
+cexprs([#iset{anno=#a{anno=A},var=V,arg=A0}|Les], As0, St0) ->
+ {Ces,As1,St1} = cexprs(Les, As0, St0),
+ {A1,Es,Us,St2} = cexpr(A0, As1, St1),
+ {#c_let{anno=A,vars=[V|make_vars(Es)],arg=A1,body=Ces},
+ union(Us, As1),St2};
+cexprs([Le|Les], As0, St0) ->
+ {Ces,As1,St1} = cexprs(Les, As0, St0),
+ {Ce,Es,Us,St2} = cexpr(Le, As1, St1),
+ if
+ Es == [] ->
+ {#c_seq{arg=Ce,body=Ces},union(Us, As1),St2};
+ true ->
+ {R,St3} = new_var(St2),
+ {#c_let{vars=[R|make_vars(Es)],arg=Ce,body=Ces},
+ union(Us, As1),St3}
+ end.
+
+%% cexpr(Lexpr, [AfterVar], State) -> {Cexpr,[ExpVar],[UsedVar],State}.
+
+cexpr(#iletrec{anno=A,defs=Fs0,body=B0}, As, St0) ->
+ {Fs1,{_,St1}} = mapfoldl(fun ({Name,F0}, {Used,St0}) ->
+ {F1,[],Us,St1} = cexpr(F0, [], St0),
+ {#c_def{name=#c_fname{id=Name,arity=1},
+ val=F1},
+ {union(Us, Used),St1}}
+ end, {[],St0}, Fs0),
+ Exp = intersection(A#a.ns, As),
+ {B1,_Us,St2} = cexprs(B0, Exp, St1),
+ {#c_letrec{anno=A#a.anno,defs=Fs1,body=B1},Exp,A#a.us,St2};
+cexpr(#icase{anno=A,args=Largs,clauses=Lcs,fc=Lfc}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Cargs,St1} = foldr(fun (La, {Cas,Sta}) ->
+ {Ca,[],_Us1,Stb} = cexpr(La, As, Sta),
+ {[Ca|Cas],Stb}
+ end, {[],St0}, Largs),
+ {Ccs,St2} = cclauses(Lcs, Exp, St1),
+ {Cfc,St3} = cclause(Lfc, [], St2), %Never exports
+ {#c_case{anno=A#a.anno,
+ arg=core_lib:make_values(Cargs),clauses=Ccs ++ [Cfc]},
+ Exp,A#a.us,St3};
+cexpr(#ireceive1{anno=A,clauses=Lcs}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Ccs,St1} = cclauses(Lcs, Exp, St0),
+ {#c_receive{anno=A#a.anno,
+ clauses=Ccs,
+ timeout=#c_atom{val=infinity},action=#c_atom{val=true}},
+ Exp,A#a.us,St1};
+cexpr(#ireceive2{anno=A,clauses=Lcs,timeout=Lto,action=Les}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Cto,[],_Us1,St1} = cexpr(Lto, As, St0),
+ {Ccs,St2} = cclauses(Lcs, Exp, St1),
+ {Ces,_Us2,St3} = cexprs(Les, Exp, St2),
+ {#c_receive{anno=A#a.anno,
+ clauses=Ccs,timeout=Cto,action=Ces},
+ Exp,A#a.us,St3};
+cexpr(#itry{anno=A,args=La,vars=Vs,body=Lb,evars=Evs,handler=Lh}, As, St0) ->
+ Exp = intersection(A#a.ns, As), %Exports
+ {Ca,_Us1,St1} = cexprs(La, [], St0),
+ {Cb,_Us2,St2} = cexprs(Lb, Exp, St1),
+ {Ch,_Us3,St3} = cexprs(Lh, Exp, St2),
+ {#c_try{anno=A#a.anno,arg=Ca,vars=Vs,body=Cb,evars=Evs,handler=Ch},
+ Exp,A#a.us,St3};
+cexpr(#icatch{anno=A,body=Les}, _As, St0) ->
+ {Ces,_Us1,St1} = cexprs(Les, [], St0), %Never export!
+ {#c_catch{body=Ces},[],A#a.us,St1};
+cexpr(#ifun{anno=A,id=Id,vars=Args,clauses=Lcs,fc=Lfc}, _As, St0) ->
+ {Ccs,St1} = cclauses(Lcs, [], St0), %NEVER export!
+ {Cfc,St2} = cclause(Lfc, [], St1),
+ Anno = A#a.anno,
+ {#c_fun{anno=Id++Anno,vars=Args,
+ body=#c_case{anno=Anno,
+ arg=core_lib:set_anno(core_lib:make_values(Args), Anno),
+ clauses=Ccs ++ [Cfc]}},
+ [],A#a.us,St2};
+cexpr(#iapply{anno=A,op=Op,args=Args}, _As, St) ->
+ {#c_apply{anno=A#a.anno,op=Op,args=Args},[],A#a.us,St};
+cexpr(#icall{anno=A,module=Mod,name=Name,args=Args}, _As, St) ->
+ {#c_call{anno=A#a.anno,module=Mod,name=Name,args=Args},[],A#a.us,St};
+cexpr(#iprimop{anno=A,name=Name,args=Args}, _As, St) ->
+ {#c_primop{anno=A#a.anno,name=Name,args=Args},[],A#a.us,St};
+cexpr(#iprotect{anno=A,body=Es}, _As, St0) ->
+ {Ce,_,St1} = cexprs(Es, [], St0),
+ V = #c_var{name='Try'}, %The names are arbitrary
+ Vs = [#c_var{name='T'},#c_var{name='R'}],
+ {#c_try{anno=A#a.anno,arg=Ce,vars=[V],body=V,
+ evars=Vs,handler=#c_atom{val=false}},
+ [],A#a.us,St1};
+cexpr(#ibinary{anno=#a{anno=Anno,us=Us},segments=Segs}, _As, St) ->
+ {#c_binary{anno=Anno,segments=Segs},[],Us,St};
+cexpr(Lit, _As, St) ->
+ true = core_lib:is_simple(Lit), %Sanity check!
+ Anno = core_lib:get_anno(Lit),
+ Vs = Anno#a.us,
+ %%Vs = lit_vars(Lit),
+ {core_lib:set_anno(Lit, Anno#a.anno),[],Vs,St}.
+
+%% lit_vars(Literal) -> [Var].
+
+lit_vars(Lit) -> lit_vars(Lit, []).
+
+lit_vars(#c_cons{hd=H,tl=T}, Vs) -> lit_vars(H, lit_vars(T, Vs));
+lit_vars(#c_tuple{es=Es}, Vs) -> lit_list_vars(Es, Vs);
+lit_vars(#c_var{name=V}, Vs) -> add_element(V, Vs);
+lit_vars(_, Vs) -> Vs. %These are atomic
+
+% lit_bin_vars(Segs, Vs) ->
+% foldl(fun (#c_bitstr{val=V,size=S}, Vs0) ->
+% lit_vars(V, lit_vars(S, Vs0))
+% end, Vs, Segs).
+
+lit_list_vars(Ls) -> lit_list_vars(Ls, []).
+
+lit_list_vars(Ls, Vs) ->
+ foldl(fun (L, Vs0) -> lit_vars(L, Vs0) end, Vs, Ls).
+
+bitstr_vars(Segs) ->
+ bitstr_vars(Segs, []).
+
+bitstr_vars(Segs, Vs) ->
+ foldl(fun (#c_bitstr{val=V,size=S}, Vs0) ->
+ lit_vars(V, lit_vars(S, Vs0))
+ end, Vs, Segs).
+
+get_ianno(Ce) ->
+ case core_lib:get_anno(Ce) of
+ #a{}=A -> A;
+ A when is_list(A) -> #a{anno=A}
+ end.
+
+get_lineno_anno(Ce) ->
+ case core_lib:get_anno(Ce) of
+ #a{anno=A} -> A;
+ A when is_list(A) -> A
+ end.
+
+
+%%%
+%%% Handling of warnings.
+%%%
+
+format_error(nomatch) -> "pattern cannot possibly match".
+
+add_warning(Line, Term, #core{ws=Ws}=St) when Line >= 0 ->
+ St#core{ws=[{Line,?MODULE,Term}|Ws]};
+add_warning(_, _, St) -> St.
+
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl
new file mode 100644
index 0000000000..2d600fabc4
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.erl
@@ -0,0 +1,1568 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_kernel.erl,v 1.3 2010/03/04 13:54:20 maria Exp $
+%%
+%% Purpose : Transform Core Erlang to Kernel Erlang
+
+%% Kernel erlang is like Core Erlang with a few significant
+%% differences:
+%%
+%% 1. It is flat! There are no nested calls or sub-blocks.
+%%
+%% 2. All variables are unique in a function. There is no scoping, or
+%% rather the scope is the whole function.
+%%
+%% 3. Pattern matching (in cases and receives) has been compiled.
+%%
+%% 4. The annotations contain variable usages. Seeing we have to work
+%% this out anyway for funs we might as well pass it on for free to
+%% later passes.
+%%
+%% 5. All remote-calls are to statically named m:f/a. Meta-calls are
+%% passed via erlang:apply/3.
+%%
+%% The translation is done in two passes:
+%%
+%% 1. Basic translation, translate variable/function names, flatten
+%% completely, pattern matching compilation.
+%%
+%% 2. Fun-lifting (lambda-lifting), variable usage annotation and
+%% last-call handling.
+%%
+%% All new Kexprs are created in the first pass, they are just
+%% annotated in the second.
+%%
+%% Functions and BIFs
+%%
+%% Functions are "call"ed or "enter"ed if it is a last call, their
+%% return values may be ignored. BIFs are things which are known to
+%% be internal by the compiler and can only be called, their return
+%% values cannot be ignored.
+%%
+%% Letrec's are handled rather naively. All the functions in one
+%% letrec are handled as one block to find the free variables. While
+%% this is not optimal it reflects how letrec's often are used. We
+%% don't have to worry about variable shadowing and nested letrec's as
+%% this is handled in the variable/function name translation. There
+%% is a little bit of trickery to ensure letrec transformations fit
+%% into the scheme of things.
+%%
+%% To ensure unique variable names we use a variable substitution
+%% table and keep the set of all defined variables. The nested
+%% scoping of Core means that we must also nest the substitution
+%% tables, but the defined set must be passed through to match the
+%% flat structure of Kernel and to make sure variables with the same
+%% name from different scopes get different substitutions.
+%%
+%% We also use these substitutions to handle the variable renaming
+%% necessary in pattern matching compilation.
+%%
+%% The pattern matching compilation assumes that the values of
+%% different types don't overlap. This means that as there is no
+%% character type yet in the machine all characters must be converted
+%% to integers!
+
+-module(v3_kernel).
+
+-export([module/2,format_error/1]).
+
+-import(lists, [map/2,foldl/3,foldr/3,mapfoldl/3,splitwith/2,
+ member/2,reverse/1,reverse/2]).
+-import(ordsets, [add_element/2,del_element/2,union/2,union/1,subtract/2]).
+
+-include("core_parse.hrl").
+-include("v3_kernel.hrl").
+
+%% These are not defined in v3_kernel.hrl.
+get_kanno(Kthing) -> element(2, Kthing).
+set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
+
+%% Internal kernel expressions and help functions.
+%% N.B. the annotation field is ALWAYS the first field!
+
+-record(ivalues, {anno=[],args}).
+-record(ifun, {anno=[],vars,body}).
+-record(iset, {anno=[],vars,arg,body}).
+-record(iletrec, {anno=[],defs}).
+-record(ialias, {anno=[],vars,pat}).
+-record(iclause, {anno=[],sub,pats,guard,body}).
+-record(ireceive_accept, {anno=[],arg}).
+-record(ireceive_next, {anno=[],arg}).
+
+%% State record for kernel translator.
+-record(kern, {func, %Current function
+ vcount=0, %Variable counter
+ fcount=0, %Fun counter
+ ds=[], %Defined variables
+ funs=[], %Fun functions
+ free=[], %Free variables
+ ws=[], %Warnings.
+ extinstr=false}). %Generate extended instructions
+
+module(#c_module{anno=A,name=M,exports=Es,attrs=As,defs=Fs}, Options) ->
+ ExtInstr = not member(no_new_apply, Options),
+ {Kfs,St} = mapfoldl(fun function/2, #kern{extinstr=ExtInstr}, Fs),
+ Kes = map(fun (#c_fname{id=N,arity=Ar}) -> {N,Ar} end, Es),
+ Kas = map(fun (#c_def{name=#c_atom{val=N},val=V}) ->
+ {N,core_lib:literal_value(V)} end, As),
+ {ok,#k_mdef{anno=A,name=M#c_atom.val,exports=Kes,attributes=Kas,
+ body=Kfs ++ St#kern.funs},St#kern.ws}.
+
+function(#c_def{anno=Af,name=#c_fname{id=F,arity=Arity},val=Body}, St0) ->
+ %%ok = io:fwrite("kern: ~p~n", [{F,Arity}]),
+ St1 = St0#kern{func={F,Arity},vcount=0,fcount=0,ds=sets:new()},
+ {#ifun{anno=Ab,vars=Kvs,body=B0},[],St2} = expr(Body, new_sub(), St1),
+ {B1,_,St3} = ubody(B0, return, St2),
+ %%B1 = B0, St3 = St2, %Null second pass
+ {#k_fdef{anno=#k{us=[],ns=[],a=Af ++ Ab},
+ func=F,arity=Arity,vars=Kvs,body=B1},St3}.
+
+%% body(Cexpr, Sub, State) -> {Kexpr,[PreKepxr],State}.
+%% Do the main sequence of a body. A body ends in an atomic value or
+%% values. Must check if vector first so do expr.
+
+body(#c_values{anno=A,es=Ces}, Sub, St0) ->
+ %% Do this here even if only in bodies.
+ {Kes,Pe,St1} = atomic_list(Ces, Sub, St0),
+ %%{Kes,Pe,St1} = expr_list(Ces, Sub, St0),
+ {#ivalues{anno=A,args=Kes},Pe,St1};
+body(#ireceive_next{anno=A}, _, St) ->
+ {#k_receive_next{anno=A},[],St};
+body(Ce, Sub, St0) ->
+ expr(Ce, Sub, St0).
+
+%% guard(Cexpr, Sub, State) -> {Kexpr,State}.
+%% We handle guards almost as bodies. The only special thing we
+%% must do is to make the final Kexpr a #k_test{}.
+%% Also, we wrap the entire guard in a try/catch which is
+%% not strictly needed, but makes sure that every 'bif' instruction
+%% will get a proper failure label.
+
+guard(G0, Sub, St0) ->
+ {G1,St1} = wrap_guard(G0, St0),
+ {Ge0,Pre,St2} = expr(G1, Sub, St1),
+ {Ge,St} = gexpr_test(Ge0, St2),
+ {pre_seq(Pre, Ge),St}.
+
+%% Wrap the entire guard in a try/catch if needed.
+
+wrap_guard(#c_try{}=Try, St) -> {Try,St};
+wrap_guard(Core, St0) ->
+ {VarName,St} = new_var_name(St0),
+ Var = #c_var{name=VarName},
+ Try = #c_try{arg=Core,vars=[Var],body=Var,evars=[],handler=#c_atom{val=false}},
+ {Try,St}.
+
+%% gexpr_test(Kexpr, State) -> {Kexpr,State}.
+%% Builds the final boolean test from the last Kexpr in a guard test.
+%% Must enter try blocks and isets and find the last Kexpr in them.
+%% This must end in a recognised BEAM test!
+
+gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=is_boolean},arity=1}=Op,
+ args=Kargs}, St) ->
+ %% XXX Remove this clause in R11. For bootstrap purposes, we must
+ %% recognize erlang:is_boolean/1 here.
+ {#k_test{anno=A,op=Op,args=Kargs},St};
+gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=internal_is_record},arity=3}=Op,
+ args=Kargs}, St) ->
+ {#k_test{anno=A,op=Op,args=Kargs},St};
+gexpr_test(#k_bif{anno=A,op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=F},arity=Ar}=Op,
+ args=Kargs}=Ke, St) ->
+ %% Either convert to test if ok, or add test.
+ %% At this stage, erlang:float/1 is not a type test. (It should
+ %% have been converted to erlang:is_float/1.)
+ case erl_internal:new_type_test(F, Ar) orelse
+ erl_internal:comp_op(F, Ar) of
+ true -> {#k_test{anno=A,op=Op,args=Kargs},St};
+ false -> gexpr_test_add(Ke, St) %Add equality test
+ end;
+gexpr_test(#k_try{arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false}}=Try, St0) ->
+ {B,St} = gexpr_test(B0, St0),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{B0,B}]),
+ {Try#k_try{arg=B},St};
+gexpr_test(#iset{body=B0}=Iset, St0) ->
+ {B1,St1} = gexpr_test(B0, St0),
+ {Iset#iset{body=B1},St1};
+gexpr_test(Ke, St) -> gexpr_test_add(Ke, St). %Add equality test
+
+gexpr_test_add(Ke, St0) ->
+ Test = #k_remote{mod=#k_atom{val='erlang'},
+ name=#k_atom{val='=:='},
+ arity=2},
+ {Ae,Ap,St1} = force_atomic(Ke, St0),
+ {pre_seq(Ap, #k_test{anno=get_kanno(Ke),
+ op=Test,args=[Ae,#k_atom{val='true'}]}),St1}.
+
+%% expr(Cexpr, Sub, State) -> {Kexpr,[PreKexpr],State}.
+%% Convert a Core expression, flattening it at the same time.
+
+expr(#c_var{anno=A,name=V}, Sub, St) ->
+ {#k_var{anno=A,name=get_vsub(V, Sub)},[],St};
+expr(#c_char{anno=A,val=C}, _Sub, St) ->
+ {#k_int{anno=A,val=C},[],St}; %Convert to integers!
+expr(#c_int{anno=A,val=I}, _Sub, St) ->
+ {#k_int{anno=A,val=I},[],St};
+expr(#c_float{anno=A,val=F}, _Sub, St) ->
+ {#k_float{anno=A,val=F},[],St};
+expr(#c_atom{anno=A,val=At}, _Sub, St) ->
+ {#k_atom{anno=A,val=At},[],St};
+expr(#c_string{anno=A,val=S}, _Sub, St) ->
+ {#k_string{anno=A,val=S},[],St};
+expr(#c_nil{anno=A}, _Sub, St) ->
+ {#k_nil{anno=A},[],St};
+expr(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub, St0) ->
+ %% Do cons in two steps, first the expressions left to right, then
+ %% any remaining literals right to left.
+ {Kh0,Hp0,St1} = expr(Ch, Sub, St0),
+ {Kt0,Tp0,St2} = expr(Ct, Sub, St1),
+ {Kt1,Tp1,St3} = force_atomic(Kt0, St2),
+ {Kh1,Hp1,St4} = force_atomic(Kh0, St3),
+ {#k_cons{anno=A,hd=Kh1,tl=Kt1},Hp0 ++ Tp0 ++ Tp1 ++ Hp1,St4};
+expr(#c_tuple{anno=A,es=Ces}, Sub, St0) ->
+ {Kes,Ep,St1} = atomic_list(Ces, Sub, St0),
+ {#k_tuple{anno=A,es=Kes},Ep,St1};
+expr(#c_binary{anno=A,segments=Cv}, Sub, St0) ->
+ case catch atomic_bin(Cv, Sub, St0, 0) of
+ {'EXIT',R} -> exit(R);
+ bad_element_size ->
+ Erl = #c_atom{val=erlang},
+ Name = #c_atom{val=error},
+ Args = [#c_atom{val=badarg}],
+ Fault = #c_call{module=Erl,name=Name,args=Args},
+ expr(Fault, Sub, St0);
+ {Kv,Ep,St1} ->
+ {#k_binary{anno=A,segs=Kv},Ep,St1}
+ end;
+expr(#c_fname{anno=A,arity=Ar}=Fname, Sub, St) ->
+ %% A local in an expression.
+ %% For now, these are wrapped into a fun by reverse
+ %% etha-conversion, but really, there should be exactly one
+ %% such "lambda function" for each escaping local name,
+ %% instead of one for each occurrence as done now.
+ Vs = [#c_var{name=list_to_atom("V" ++ integer_to_list(V))} ||
+ V <- integers(1, Ar)],
+ Fun = #c_fun{anno=A,vars=Vs,body=#c_apply{op=Fname,args=Vs}},
+ expr(Fun, Sub, St);
+expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, St0) ->
+ {Kvs,Sub1,St1} = pattern_list(Cvs, Sub0, St0),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{{Cvs,Sub0,St0},{Kvs,Sub1,St1}}]),
+ {Kb,Pb,St2} = body(Cb, Sub1, St1),
+ {#ifun{anno=A,vars=Kvs,body=pre_seq(Pb, Kb)},[],St2};
+expr(#c_seq{arg=Ca,body=Cb}, Sub, St0) ->
+ {Ka,Pa,St1} = body(Ca, Sub, St0),
+ case is_exit_expr(Ka) of
+ true -> {Ka,Pa,St1};
+ false ->
+ {Kb,Pb,St2} = body(Cb, Sub, St1),
+ {Kb,Pa ++ [Ka] ++ Pb,St2}
+ end;
+expr(#c_let{anno=A,vars=Cvs,arg=Ca,body=Cb}, Sub0, St0) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{Cvs,Sub0,St0}]),
+ {Ka,Pa,St1} = body(Ca, Sub0, St0),
+ case is_exit_expr(Ka) of
+ true -> {Ka,Pa,St1};
+ false ->
+ {Kps,Sub1,St2} = pattern_list(Cvs, Sub0, St1),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,{Kps,Sub1,St1,St2}]),
+ %% Break known multiple values into separate sets.
+ Sets = case Ka of
+ #ivalues{args=Kas} ->
+ foldr2(fun (V, Val, Sb) ->
+ [#iset{vars=[V],arg=Val}|Sb] end,
+ [], Kps, Kas);
+ _Other ->
+ [#iset{anno=A,vars=Kps,arg=Ka}]
+ end,
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kb,Pa ++ Sets ++ Pb,St3}
+ end;
+expr(#c_letrec{anno=A,defs=Cfs,body=Cb}, Sub0, St0) ->
+ %% Make new function names and store substitution.
+ {Fs0,{Sub1,St1}} =
+ mapfoldl(fun (#c_def{name=#c_fname{id=F,arity=Ar},val=B}, {Sub,St0}) ->
+ {N,St1} = new_fun_name(atom_to_list(F)
+ ++ "/" ++
+ integer_to_list(Ar),
+ St0),
+ {{N,B},{set_fsub(F, Ar, N, Sub),St1}}
+ end, {Sub0,St0}, Cfs),
+ %% Run translation on functions and body.
+ {Fs1,St2} = mapfoldl(fun ({N,Fd0}, St1) ->
+ {Fd1,[],St2} = expr(Fd0, Sub1, St1),
+ Fd = set_kanno(Fd1, A),
+ {{N,Fd},St2}
+ end, St1, Fs0),
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kb,[#iletrec{anno=A,defs=Fs1}|Pb],St3};
+expr(#c_case{arg=Ca,clauses=Ccs}, Sub, St0) ->
+ {Ka,Pa,St1} = body(Ca, Sub, St0), %This is a body!
+ {Kvs,Pv,St2} = match_vars(Ka, St1), %Must have variables here!
+ {Km,St3} = kmatch(Kvs, Ccs, Sub, St2),
+ Match = flatten_seq(build_match(Kvs, Km)),
+ {last(Match),Pa ++ Pv ++ first(Match),St3};
+expr(#c_receive{anno=A,clauses=Ccs0,timeout=Ce,action=Ca}, Sub, St0) ->
+ {Ke,Pe,St1} = atomic_lit(Ce, Sub, St0), %Force this to be atomic!
+ {Rvar,St2} = new_var(St1),
+ %% Need to massage accept clauses and add reject clause before matching.
+ Ccs1 = map(fun (#c_clause{anno=Banno,body=B0}=C) ->
+ B1 = #c_seq{arg=#ireceive_accept{anno=A},body=B0},
+ C#c_clause{anno=Banno,body=B1}
+ end, Ccs0),
+ {Mpat,St3} = new_var_name(St2),
+ Rc = #c_clause{anno=[compiler_generated|A],
+ pats=[#c_var{name=Mpat}],guard=#c_atom{anno=A,val=true},
+ body=#ireceive_next{anno=A}},
+ {Km,St4} = kmatch([Rvar], Ccs1 ++ [Rc], Sub, add_var_def(Rvar, St3)),
+ {Ka,Pa,St5} = body(Ca, Sub, St4),
+ {#k_receive{anno=A,var=Rvar,body=Km,timeout=Ke,action=pre_seq(Pa, Ka)},
+ Pe,St5};
+expr(#c_apply{anno=A,op=Cop,args=Cargs}, Sub, St) ->
+ c_apply(A, Cop, Cargs, Sub, St);
+expr(#c_call{anno=A,module=M0,name=F0,args=Cargs}, Sub, St0) ->
+ {[M1,F1|Kargs],Ap,St1} = atomic_list([M0,F0|Cargs], Sub, St0),
+ Ar = length(Cargs),
+ case {M1,F1} of
+ {#k_atom{val=Ma},#k_atom{val=Fa}} ->
+ Call = case is_remote_bif(Ma, Fa, Ar) of
+ true ->
+ #k_bif{anno=A,
+ op=#k_remote{mod=M1,name=F1,arity=Ar},
+ args=Kargs};
+ false ->
+ #k_call{anno=A,
+ op=#k_remote{mod=M1,name=F1,arity=Ar},
+ args=Kargs}
+ end,
+ {Call,Ap,St1};
+ _Other when St0#kern.extinstr == false -> %Old explicit apply
+ Call = #c_call{anno=A,
+ module=#c_atom{val=erlang},
+ name=#c_atom{val=apply},
+ args=[M0,F0,make_list(Cargs)]},
+ expr(Call, Sub, St0);
+ _Other -> %New instruction in R10.
+ Call = #k_call{anno=A,
+ op=#k_remote{mod=M1,name=F1,arity=Ar},
+ args=Kargs},
+ {Call,Ap,St1}
+ end;
+expr(#c_primop{anno=A,name=#c_atom{val=match_fail},args=Cargs}, Sub, St0) ->
+ %% This special case will disappear.
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ Ar = length(Cargs),
+ Call = #k_call{anno=A,op=#k_internal{name=match_fail,arity=Ar},args=Kargs},
+ {Call,Ap,St1};
+expr(#c_primop{anno=A,name=#c_atom{val=N},args=Cargs}, Sub, St0) ->
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ Ar = length(Cargs),
+ {#k_bif{anno=A,op=#k_internal{name=N,arity=Ar},args=Kargs},Ap,St1};
+expr(#c_try{anno=A,arg=Ca,vars=Cvs,body=Cb,evars=Evs,handler=Ch}, Sub0, St0) ->
+ %% The normal try expression. The body and exception handler
+ %% variables behave as let variables.
+ {Ka,Pa,St1} = body(Ca, Sub0, St0),
+ {Kcvs,Sub1,St2} = pattern_list(Cvs, Sub0, St1),
+ {Kb,Pb,St3} = body(Cb, Sub1, St2),
+ {Kevs,Sub2,St4} = pattern_list(Evs, Sub0, St3),
+ {Kh,Ph,St5} = body(Ch, Sub2, St4),
+ {#k_try{anno=A,arg=pre_seq(Pa, Ka),
+ vars=Kcvs,body=pre_seq(Pb, Kb),
+ evars=Kevs,handler=pre_seq(Ph, Kh)},[],St5};
+expr(#c_catch{anno=A,body=Cb}, Sub, St0) ->
+ {Kb,Pb,St1} = body(Cb, Sub, St0),
+ {#k_catch{anno=A,body=pre_seq(Pb, Kb)},[],St1};
+%% Handle internal expressions.
+expr(#ireceive_accept{anno=A}, _Sub, St) -> {#k_receive_accept{anno=A},[],St}.
+
+%% expr_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
+
+% expr_list(Ces, Sub, St) ->
+% foldr(fun (Ce, {Kes,Esp,St0}) ->
+% {Ke,Ep,St1} = expr(Ce, Sub, St0),
+% {[Ke|Kes],Ep ++ Esp,St1}
+% end, {[],[],St}, Ces).
+
+%% match_vars(Kexpr, State) -> {[Kvar],[PreKexpr],State}.
+%% Force return from body into a list of variables.
+
+match_vars(#ivalues{args=As}, St) ->
+ foldr(fun (Ka, {Vs,Vsp,St0}) ->
+ {V,Vp,St1} = force_variable(Ka, St0),
+ {[V|Vs],Vp ++ Vsp,St1}
+ end, {[],[],St}, As);
+match_vars(Ka, St0) ->
+ {V,Vp,St1} = force_variable(Ka, St0),
+ {[V],Vp,St1}.
+
+%% c_apply(A, Op, [Carg], Sub, State) -> {Kexpr,[PreKexpr],State}.
+%% Transform application, detect which are guaranteed to be bifs.
+
+c_apply(A, #c_fname{anno=Ra,id=F0,arity=Ar}, Cargs, Sub, St0) ->
+ {Kargs,Ap,St1} = atomic_list(Cargs, Sub, St0),
+ F1 = get_fsub(F0, Ar, Sub), %Has it been rewritten
+ {#k_call{anno=A,op=#k_local{anno=Ra,name=F1,arity=Ar},args=Kargs},
+ Ap,St1};
+c_apply(A, Cop, Cargs, Sub, St0) ->
+ {Kop,Op,St1} = variable(Cop, Sub, St0),
+ {Kargs,Ap,St2} = atomic_list(Cargs, Sub, St1),
+ {#k_call{anno=A,op=Kop,args=Kargs},Op ++ Ap,St2}.
+
+flatten_seq(#iset{anno=A,vars=Vs,arg=Arg,body=B}) ->
+ [#iset{anno=A,vars=Vs,arg=Arg}|flatten_seq(B)];
+flatten_seq(Ke) -> [Ke].
+
+pre_seq([#iset{anno=A,vars=Vs,arg=Arg,body=B}|Ps], K) ->
+ B = undefined, %Assertion.
+ #iset{anno=A,vars=Vs,arg=Arg,body=pre_seq(Ps, K)};
+pre_seq([P|Ps], K) ->
+ #iset{vars=[],arg=P,body=pre_seq(Ps, K)};
+pre_seq([], K) -> K.
+
+%% atomic_lit(Cexpr, Sub, State) -> {Katomic,[PreKexpr],State}.
+%% Convert a Core expression making sure the result is an atomic
+%% literal.
+
+atomic_lit(Ce, Sub, St0) ->
+ {Ke,Kp,St1} = expr(Ce, Sub, St0),
+ {Ka,Ap,St2} = force_atomic(Ke, St1),
+ {Ka,Kp ++ Ap,St2}.
+
+force_atomic(Ke, St0) ->
+ case is_atomic(Ke) of
+ true -> {Ke,[],St0};
+ false ->
+ {V,St1} = new_var(St0),
+ {V,[#iset{vars=[V],arg=Ke}],St1}
+ end.
+
+% force_atomic_list(Kes, St) ->
+% foldr(fun (Ka, {As,Asp,St0}) ->
+% {A,Ap,St1} = force_atomic(Ka, St0),
+% {[A|As],Ap ++ Asp,St1}
+% end, {[],[],St}, Kes).
+
+atomic_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
+ Sub, St0, B0) ->
+ {E,Ap1,St1} = atomic_lit(E0, Sub, St0),
+ {S1,Ap2,St2} = atomic_lit(S0, Sub, St1),
+ validate_bin_element_size(S1),
+ U0 = core_lib:literal_value(U),
+ Fs0 = core_lib:literal_value(Fs),
+ {B1,Fs1} = aligned(B0, S1, U0, Fs0),
+ {Es,Ap3,St3} = atomic_bin(Es0, Sub, St2, B1),
+ {#k_bin_seg{anno=A,size=S1,
+ unit=U0,
+ type=core_lib:literal_value(T),
+ flags=Fs1,
+ seg=E,next=Es},
+ Ap1++Ap2++Ap3,St3};
+atomic_bin([], _Sub, St, _Bits) -> {#k_bin_end{},[],St}.
+
+validate_bin_element_size(#k_var{}) -> ok;
+validate_bin_element_size(#k_int{val=V}) when V >= 0 -> ok;
+validate_bin_element_size(#k_atom{val=all}) -> ok;
+validate_bin_element_size(_) -> throw(bad_element_size).
+
+%% atomic_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
+
+atomic_list(Ces, Sub, St) ->
+ foldr(fun (Ce, {Kes,Esp,St0}) ->
+ {Ke,Ep,St1} = atomic_lit(Ce, Sub, St0),
+ {[Ke|Kes],Ep ++ Esp,St1}
+ end, {[],[],St}, Ces).
+
+%% is_atomic(Kexpr) -> boolean().
+%% Is a Kexpr atomic? Strings are NOT considered atomic!
+
+is_atomic(#k_int{}) -> true;
+is_atomic(#k_float{}) -> true;
+is_atomic(#k_atom{}) -> true;
+%%is_atomic(#k_char{}) -> true; %No characters
+%%is_atomic(#k_string{}) -> true;
+is_atomic(#k_nil{}) -> true;
+is_atomic(#k_var{}) -> true;
+is_atomic(_) -> false.
+
+%% variable(Cexpr, Sub, State) -> {Kvar,[PreKexpr],State}.
+%% Convert a Core expression making sure the result is a variable.
+
+variable(Ce, Sub, St0) ->
+ {Ke,Kp,St1} = expr(Ce, Sub, St0),
+ {Kv,Vp,St2} = force_variable(Ke, St1),
+ {Kv,Kp ++ Vp,St2}.
+
+force_variable(#k_var{}=Ke, St) -> {Ke,[],St};
+force_variable(Ke, St0) ->
+ {V,St1} = new_var(St0),
+ {V,[#iset{vars=[V],arg=Ke}],St1}.
+
+%% pattern(Cpat, Sub, State) -> {Kpat,Sub,State}.
+%% Convert patterns. Variables shadow so rename variables that are
+%% already defined.
+
+pattern(#c_var{anno=A,name=V}, Sub, St0) ->
+ case sets:is_element(V, St0#kern.ds) of
+ true ->
+ {New,St1} = new_var_name(St0),
+ {#k_var{anno=A,name=New},
+ set_vsub(V, New, Sub),
+ St1#kern{ds=sets:add_element(New, St1#kern.ds)}};
+ false ->
+ {#k_var{anno=A,name=V},Sub,
+ St0#kern{ds=sets:add_element(V, St0#kern.ds)}}
+ end;
+pattern(#c_char{anno=A,val=C}, Sub, St) ->
+ {#k_int{anno=A,val=C},Sub,St}; %Convert to integers!
+pattern(#c_int{anno=A,val=I}, Sub, St) ->
+ {#k_int{anno=A,val=I},Sub,St};
+pattern(#c_float{anno=A,val=F}, Sub, St) ->
+ {#k_float{anno=A,val=F},Sub,St};
+pattern(#c_atom{anno=A,val=At}, Sub, St) ->
+ {#k_atom{anno=A,val=At},Sub,St};
+pattern(#c_string{val=S}, Sub, St) ->
+ L = foldr(fun (C, T) -> #k_cons{hd=#k_int{val=C},tl=T} end,
+ #k_nil{}, S),
+ {L,Sub,St};
+pattern(#c_nil{anno=A}, Sub, St) ->
+ {#k_nil{anno=A},Sub,St};
+pattern(#c_cons{anno=A,hd=Ch,tl=Ct}, Sub0, St0) ->
+ {Kh,Sub1,St1} = pattern(Ch, Sub0, St0),
+ {Kt,Sub2,St2} = pattern(Ct, Sub1, St1),
+ {#k_cons{anno=A,hd=Kh,tl=Kt},Sub2,St2};
+pattern(#c_tuple{anno=A,es=Ces}, Sub0, St0) ->
+ {Kes,Sub1,St1} = pattern_list(Ces, Sub0, St0),
+ {#k_tuple{anno=A,es=Kes},Sub1,St1};
+pattern(#c_binary{anno=A,segments=Cv}, Sub0, St0) ->
+ {Kv,Sub1,St1} = pattern_bin(Cv, Sub0, St0),
+ {#k_binary{anno=A,segs=Kv},Sub1,St1};
+pattern(#c_alias{anno=A,var=Cv,pat=Cp}, Sub0, St0) ->
+ {Cvs,Cpat} = flatten_alias(Cp),
+ {Kvs,Sub1,St1} = pattern_list([Cv|Cvs], Sub0, St0),
+ {Kpat,Sub2,St2} = pattern(Cpat, Sub1, St1),
+ {#ialias{anno=A,vars=Kvs,pat=Kpat},Sub2,St2}.
+
+flatten_alias(#c_alias{var=V,pat=P}) ->
+ {Vs,Pat} = flatten_alias(P),
+ {[V|Vs],Pat};
+flatten_alias(Pat) -> {[],Pat}.
+
+pattern_bin(Es, Sub, St) -> pattern_bin(Es, Sub, St, 0).
+
+pattern_bin([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
+ Sub0, St0, B0) ->
+ {S1,[],St1} = expr(S0, Sub0, St0),
+ U0 = core_lib:literal_value(U),
+ Fs0 = core_lib:literal_value(Fs),
+ %%ok= io:fwrite("~w: ~p~n", [?LINE,{B0,S1,U0,Fs0}]),
+ {B1,Fs1} = aligned(B0, S1, U0, Fs0),
+ {E,Sub1,St2} = pattern(E0, Sub0, St1),
+ {Es,Sub2,St3} = pattern_bin(Es0, Sub1, St2, B1),
+ {#k_bin_seg{anno=A,size=S1,
+ unit=U0,
+ type=core_lib:literal_value(T),
+ flags=Fs1,
+ seg=E,next=Es},
+ Sub2,St3};
+pattern_bin([], Sub, St, _Bits) -> {#k_bin_end{},Sub,St}.
+
+%% pattern_list([Cexpr], Sub, State) -> {[Kexpr],Sub,State}.
+
+pattern_list(Ces, Sub, St) ->
+ foldr(fun (Ce, {Kes,Sub0,St0}) ->
+ {Ke,Sub1,St1} = pattern(Ce, Sub0, St0),
+ {[Ke|Kes],Sub1,St1}
+ end, {[],Sub,St}, Ces).
+
+%% new_sub() -> Subs.
+%% set_vsub(Name, Sub, Subs) -> Subs.
+%% subst_vsub(Name, Sub, Subs) -> Subs.
+%% get_vsub(Name, Subs) -> SubName.
+%% Add/get substitute Sub for Name to VarSub. Use orddict so we know
+%% the format is a list {Name,Sub} pairs. When adding a new
+%% substitute we fold substitute chains so we never have to search
+%% more than once.
+
+new_sub() -> orddict:new().
+
+get_vsub(V, Vsub) ->
+ case orddict:find(V, Vsub) of
+ {ok,Val} -> Val;
+ error -> V
+ end.
+
+set_vsub(V, S, Vsub) ->
+ orddict:store(V, S, Vsub).
+
+subst_vsub(V, S, Vsub0) ->
+ %% Fold chained substitutions.
+ Vsub1 = orddict:map(fun (_, V1) when V1 =:= V -> S;
+ (_, V1) -> V1
+ end, Vsub0),
+ orddict:store(V, S, Vsub1).
+
+get_fsub(F, A, Fsub) ->
+ case orddict:find({F,A}, Fsub) of
+ {ok,Val} -> Val;
+ error -> F
+ end.
+
+set_fsub(F, A, S, Fsub) ->
+ orddict:store({F,A}, S, Fsub).
+
+new_fun_name(St) ->
+ new_fun_name("anonymous", St).
+
+%% new_fun_name(Type, State) -> {FunName,State}.
+
+new_fun_name(Type, #kern{func={F,Arity},fcount=C}=St) ->
+ Name = "-" ++ atom_to_list(F) ++ "/" ++ integer_to_list(Arity) ++
+ "-" ++ Type ++ "-" ++ integer_to_list(C) ++ "-",
+ {list_to_atom(Name),St#kern{fcount=C+1}}.
+
+%% new_var_name(State) -> {VarName,State}.
+
+new_var_name(#kern{vcount=C}=St) ->
+ {list_to_atom("ker" ++ integer_to_list(C)),St#kern{vcount=C+1}}.
+
+%% new_var(State) -> {#k_var{},State}.
+
+new_var(St0) ->
+ {New,St1} = new_var_name(St0),
+ {#k_var{name=New},St1}.
+
+%% new_vars(Count, State) -> {[#k_var{}],State}.
+%% Make Count new variables.
+
+new_vars(N, St) -> new_vars(N, St, []).
+
+new_vars(N, St0, Vs) when N > 0 ->
+ {V,St1} = new_var(St0),
+ new_vars(N-1, St1, [V|Vs]);
+new_vars(0, St, Vs) -> {Vs,St}.
+
+make_vars(Vs) -> [ #k_var{name=V} || V <- Vs ].
+
+add_var_def(V, St) ->
+ St#kern{ds=sets:add_element(V#k_var.name, St#kern.ds)}.
+
+%%add_vars_def(Vs, St) ->
+%% Ds = foldl(fun (#k_var{name=V}, Ds) -> add_element(V, Ds) end,
+%% St#kern.ds, Vs),
+%% St#kern{ds=Ds}.
+
+%% is_remote_bif(Mod, Name, Arity) -> true | false.
+%% Test if function is really a BIF.
+
+is_remote_bif(erlang, is_boolean, 1) ->
+ %% XXX Remove this clause in R11. For bootstrap purposes, we must
+ %% recognize erlang:is_boolean/1 here.
+ true;
+is_remote_bif(erlang, internal_is_record, 3) -> true;
+is_remote_bif(erlang, get, 1) -> true;
+is_remote_bif(erlang, N, A) ->
+ case erl_internal:guard_bif(N, A) of
+ true -> true;
+ false ->
+ case erl_internal:type_test(N, A) of
+ true -> true;
+ false ->
+ case catch erl_internal:op_type(N, A) of
+ arith -> true;
+ bool -> true;
+ comp -> true;
+ _Other -> false %List, send or not an op
+ end
+ end
+ end;
+is_remote_bif(_, _, _) -> false.
+
+%% bif_vals(Name, Arity) -> integer().
+%% bif_vals(Mod, Name, Arity) -> integer().
+%% Determine how many return values a BIF has. Provision for BIFs to
+%% return multiple values. Only used in bodies where a BIF may be
+%% called for effect only.
+
+bif_vals(dsetelement, 3) -> 0;
+bif_vals(_, _) -> 1.
+
+bif_vals(_, _, _) -> 1.
+
+%% foldr2(Fun, Acc, List1, List2) -> Acc.
+%% Fold over two lists.
+
+foldr2(Fun, Acc0, [E1|L1], [E2|L2]) ->
+ Acc1 = Fun(E1, E2, Acc0),
+ foldr2(Fun, Acc1, L1, L2);
+foldr2(_, Acc, [], []) -> Acc.
+
+%% first([A]) -> [A].
+%% last([A]) -> A.
+
+last([L]) -> L;
+last([_|T]) -> last(T).
+
+first([_]) -> [];
+first([H|T]) -> [H|first(T)].
+
+%% This code implements the algorithm for an optimizing compiler for
+%% pattern matching given "The Implementation of Functional
+%% Programming Languages" by Simon Peyton Jones. The code is much
+%% longer as the meaning of constructors is different from the book.
+%%
+%% In Erlang many constructors can have different values, e.g. 'atom'
+%% or 'integer', whereas in the original algorithm thse would be
+%% different constructors. Our view makes it easier in later passes to
+%% handle indexing over each type.
+%%
+%% Patterns are complicated by having alias variables. The form of a
+%% pattern is Pat | {alias,Pat,[AliasVar]}. This is hidden by access
+%% functions to pattern arguments but the code must be aware of it.
+%%
+%% The compilation proceeds in two steps:
+%%
+%% 1. The patterns in the clauses to converted to lists of kernel
+%% patterns. The Core clause is now hybrid, this is easier to work
+%% with. Remove clauses with trivially false guards, this simplifies
+%% later passes. Add local defined vars and variable subs to each
+%% clause for later use.
+%%
+%% 2. The pattern matching is optimised. Variable substitutions are
+%% added to the VarSub structure and new variables are made visible.
+%% The guard and body are then converted to Kernel form.
+
+%% kmatch([Var], [Clause], Sub, State) -> {Kexpr,[PreExpr],State}.
+
+kmatch(Us, Ccs, Sub, St0) ->
+ {Cs,St1} = match_pre(Ccs, Sub, St0), %Convert clauses
+ %%Def = kernel_match_error, %The strict case
+ %% This should be a kernel expression from the first pass.
+ Def = #k_call{anno=[compiler_generated],
+ op=#k_remote{mod=#k_atom{val=erlang},
+ name=#k_atom{val=exit},
+ arity=1},
+ args=[#k_atom{val=kernel_match_error}]},
+ {Km,St2} = match(Us, Cs, Def, St1), %Do the match.
+ {Km,St2}.
+
+%% match_pre([Cclause], Sub, State) -> {[Clause],State}.
+%% Must be careful not to generate new substitutions here now!
+%% Remove clauses with trivially false guards which will never
+%% succeed.
+
+match_pre(Cs, Sub0, St) ->
+ foldr(fun (#c_clause{anno=A,pats=Ps,guard=G,body=B}, {Cs0,St0}) ->
+ case is_false_guard(G) of
+ true -> {Cs0,St0};
+ false ->
+ {Kps,Sub1,St1} = pattern_list(Ps, Sub0, St0),
+ {[#iclause{anno=A,sub=Sub1,pats=Kps,guard=G,body=B}|
+ Cs0],St1}
+ end
+ end, {[],St}, Cs).
+
+%% match([Var], [Clause], Default, State) -> {MatchExpr,State}.
+
+match([U|Us], Cs, Def, St0) ->
+ %%ok = io:format("match ~p~n", [Cs]),
+ Pcss = partition(Cs),
+ foldr(fun (Pcs, {D,St}) -> match_varcon([U|Us], Pcs, D, St) end,
+ {Def,St0}, Pcss);
+match([], Cs, Def, St) ->
+ match_guard(Cs, Def, St).
+
+%% match_guard([Clause], Default, State) -> {IfExpr,State}.
+%% Build a guard to handle guards. A guard *ALWAYS* fails if no
+%% clause matches, there will be a surrounding 'alt' to catch the
+%% failure. Drop redundant cases, i.e. those after a true guard.
+
+match_guard(Cs0, Def0, St0) ->
+ {Cs1,Def1,St1} = match_guard_1(Cs0, Def0, St0),
+ {build_alt(build_guard(Cs1), Def1),St1}.
+
+match_guard_1([#iclause{anno=A,sub=Sub,guard=G,body=B}|Cs0], Def0, St0) ->
+ case is_true_guard(G) of
+ true ->
+ %% The true clause body becomes the default.
+ {Kb,Pb,St1} = body(B, Sub, St0),
+ Line = get_line(A),
+ St2 = maybe_add_warning(Cs0, Line, St1),
+ St = maybe_add_warning(Def0, Line, St2),
+ {[],pre_seq(Pb, Kb),St};
+ false ->
+ {Kg,St1} = guard(G, Sub, St0),
+ {Kb,Pb,St2} = body(B, Sub, St1),
+ {Cs1,Def1,St3} = match_guard_1(Cs0, Def0, St2),
+ {[#k_guard_clause{guard=Kg,body=pre_seq(Pb, Kb)}|Cs1],
+ Def1,St3}
+ end;
+match_guard_1([], Def, St) -> {[],Def,St}.
+
+maybe_add_warning([C|_], Line, St) ->
+ maybe_add_warning(C, Line, St);
+maybe_add_warning([], _Line, St) -> St;
+maybe_add_warning(fail, _Line, St) -> St;
+maybe_add_warning(Ke, MatchLine, St) ->
+ case get_kanno(Ke) of
+ [compiler_generated|_] -> St;
+ Anno ->
+ Line = get_line(Anno),
+ Warn = case MatchLine of
+ none -> nomatch_shadow;
+ _ -> {nomatch_shadow,MatchLine}
+ end,
+ add_warning(Line, Warn, St)
+ end.
+
+get_line([Line|_]) when is_integer(Line) -> Line;
+get_line([_|T]) -> get_line(T);
+get_line([]) -> none.
+
+
+%% is_true_guard(Guard) -> boolean().
+%% is_false_guard(Guard) -> boolean().
+%% Test if a guard is either trivially true/false. This has probably
+%% already been optimised away, but what the heck!
+
+is_true_guard(G) -> guard_value(G) == true.
+is_false_guard(G) -> guard_value(G) == false.
+
+%% guard_value(Guard) -> true | false | unknown.
+
+guard_value(#c_atom{val=true}) -> true;
+guard_value(#c_atom{val=false}) -> false;
+guard_value(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{val='not'},
+ args=[A]}) ->
+ case guard_value(A) of
+ true -> false;
+ false -> true;
+ unknown -> unknown
+ end;
+guard_value(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{val='and'},
+ args=[Ca,Cb]}) ->
+ case guard_value(Ca) of
+ true -> guard_value(Cb);
+ false -> false;
+ unknown ->
+ case guard_value(Cb) of
+ false -> false;
+ _Other -> unknown
+ end
+ end;
+guard_value(#c_call{module=#c_atom{val=erlang},
+ name=#c_atom{val='or'},
+ args=[Ca,Cb]}) ->
+ case guard_value(Ca) of
+ true -> true;
+ false -> guard_value(Cb);
+ unknown ->
+ case guard_value(Cb) of
+ true -> true;
+ _Other -> unknown
+ end
+ end;
+guard_value(#c_try{arg=E,vars=[#c_var{name=X}],body=#c_var{name=X},
+ handler=#c_atom{val=false}}) ->
+ guard_value(E);
+guard_value(_) -> unknown.
+
+%% partition([Clause]) -> [[Clause]].
+%% Partition a list of clauses into groups which either contain
+%% clauses with a variable first argument, or with a "constructor".
+
+partition([C1|Cs]) ->
+ V1 = is_var_clause(C1),
+ {More,Rest} = splitwith(fun (C) -> is_var_clause(C) == V1 end, Cs),
+ [[C1|More]|partition(Rest)];
+partition([]) -> [].
+
+%% match_varcon([Var], [Clause], Def, [Var], Sub, State) ->
+%% {MatchExpr,State}.
+
+match_varcon(Us, [C|_]=Cs, Def, St) ->
+ case is_var_clause(C) of
+ true -> match_var(Us, Cs, Def, St);
+ false -> match_con(Us, Cs, Def, St)
+ end.
+
+%% match_var([Var], [Clause], Def, State) -> {MatchExpr,State}.
+%% Build a call to "select" from a list of clauses all containing a
+%% variable as the first argument. We must rename the variable in
+%% each clause to be the match variable as these clause will share
+%% this variable and may have different names for it. Rename aliases
+%% as well.
+
+match_var([U|Us], Cs0, Def, St) ->
+ Cs1 = map(fun (#iclause{sub=Sub0,pats=[Arg|As]}=C) ->
+ Vs = [arg_arg(Arg)|arg_alias(Arg)],
+ Sub1 = foldl(fun (#k_var{name=V}, Acc) ->
+ subst_vsub(V, U#k_var.name, Acc)
+ end, Sub0, Vs),
+ C#iclause{sub=Sub1,pats=As}
+ end, Cs0),
+ match(Us, Cs1, Def, St).
+
+%% match_con(Variables, [Clause], Default, State) -> {SelectExpr,State}.
+%% Build call to "select" from a list of clauses all containing a
+%% constructor/constant as first argument. Group the constructors
+%% according to type, the order is really irrelevant but tries to be
+%% smart.
+
+match_con([U|Us], Cs, Def, St0) ->
+ %% Extract clauses for different constructors (types).
+ %%ok = io:format("match_con ~p~n", [Cs]),
+ Ttcs = [ {T,Tcs} || T <- [k_cons,k_tuple,k_atom,k_float,k_int,k_nil,
+ k_binary,k_bin_end],
+ begin Tcs = select(T, Cs),
+ Tcs /= []
+ end ] ++ select_bin_con(Cs),
+ %%ok = io:format("ttcs = ~p~n", [Ttcs]),
+ {Scs,St1} =
+ mapfoldl(fun ({T,Tcs}, St) ->
+ {[S|_]=Sc,S1} = match_value([U|Us], T, Tcs, fail, St),
+ %%ok = io:format("match_con type2 ~p~n", [T]),
+ Anno = get_kanno(S),
+ {#k_type_clause{anno=Anno,type=T,values=Sc},S1} end,
+ St0, Ttcs),
+ {build_alt_1st_no_fail(build_select(U, Scs), Def),St1}.
+
+%% select_bin_con([Clause]) -> [{Type,[Clause]}].
+%% Extract clauses for the k_bin_seg constructor. As k_bin_seg
+%% matching can overlap, the k_bin_seg constructors cannot be
+%% reordered, only grouped.
+
+select_bin_con(Cs0) ->
+ Cs1 = lists:filter(fun (C) ->
+ clause_con(C) == k_bin_seg
+ end, Cs0),
+ select_bin_con_1(Cs1).
+
+select_bin_con_1([C1|Cs]) ->
+ Con = clause_con(C1),
+ {More,Rest} = splitwith(fun (C) -> clause_con(C) == Con end, Cs),
+ [{Con,[C1|More]}|select_bin_con_1(Rest)];
+select_bin_con_1([]) -> [].
+
+%% select(Con, [Clause]) -> [Clause].
+
+select(T, Cs) -> [ C || C <- Cs, clause_con(C) == T ].
+
+%% match_value([Var], Con, [Clause], Default, State) -> {SelectExpr,State}.
+%% At this point all the clauses have the same constructor, we must
+%% now separate them according to value.
+
+match_value(_, _, [], _, St) -> {[],St};
+match_value(Us, T, Cs0, Def, St0) ->
+ Css = group_value(T, Cs0),
+ %%ok = io:format("match_value ~p ~p~n", [T, Css]),
+ {Css1,St1} = mapfoldl(fun (Cs, St) ->
+ match_clause(Us, Cs, Def, St) end,
+ St0, Css),
+ {Css1,St1}.
+ %%{#k_select_val{type=T,var=hd(Us),clauses=Css1},St1}.
+
+%% group_value([Clause]) -> [[Clause]].
+%% Group clauses according to value. Here we know that
+%% 1. Some types are singled valued
+%% 2. The clauses in bin_segs cannot be reordered only grouped
+%% 3. Other types are disjoint and can be reordered
+
+group_value(k_cons, Cs) -> [Cs]; %These are single valued
+group_value(k_nil, Cs) -> [Cs];
+group_value(k_binary, Cs) -> [Cs];
+group_value(k_bin_end, Cs) -> [Cs];
+group_value(k_bin_seg, Cs) ->
+ group_bin_seg(Cs);
+group_value(_, Cs) ->
+ %% group_value(Cs).
+ Cd = foldl(fun (C, Gcs0) -> dict:append(clause_val(C), C, Gcs0) end,
+ dict:new(), Cs),
+ dict:fold(fun (_, Vcs, Css) -> [Vcs|Css] end, [], Cd).
+
+group_bin_seg([C1|Cs]) ->
+ V1 = clause_val(C1),
+ {More,Rest} = splitwith(fun (C) -> clause_val(C) == V1 end, Cs),
+ [[C1|More]|group_bin_seg(Rest)];
+group_bin_seg([]) -> [].
+
+%% Profiling shows that this quadratic implementation account for a big amount
+%% of the execution time if there are many values.
+% group_value([C|Cs]) ->
+% V = clause_val(C),
+% Same = [ Cv || Cv <- Cs, clause_val(Cv) == V ], %Same value
+% Rest = [ Cv || Cv <- Cs, clause_val(Cv) /= V ], % and all the rest
+% [[C|Same]|group_value(Rest)];
+% group_value([]) -> [].
+
+%% match_clause([Var], [Clause], Default, State) -> {Clause,State}.
+%% At this point all the clauses have the same "value". Build one
+%% select clause for this value and continue matching. Rename
+%% aliases as well.
+
+match_clause([U|Us], [C|_]=Cs0, Def, St0) ->
+ Anno = get_kanno(C),
+ {Match0,Vs,St1} = get_match(get_con(Cs0), St0),
+ Match = sub_size_var(Match0, Cs0),
+ {Cs1,St2} = new_clauses(Cs0, U, St1),
+ {B,St3} = match(Vs ++ Us, Cs1, Def, St2),
+ {#k_val_clause{anno=Anno,val=Match,body=B},St3}.
+
+sub_size_var(#k_bin_seg{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{sub=Sub}|_]) ->
+ BinSeg#k_bin_seg{size=Kvar#k_var{name=get_vsub(Name, Sub)}};
+sub_size_var(K, _) -> K.
+
+get_con([C|_]) -> arg_arg(clause_arg(C)). %Get the constructor
+
+get_match(#k_cons{}, St0) ->
+ {[H,T],St1} = new_vars(2, St0),
+ {#k_cons{hd=H,tl=T},[H,T],St1};
+get_match(#k_binary{}, St0) ->
+ {[V]=Mes,St1} = new_vars(1, St0),
+ {#k_binary{segs=V},Mes,St1};
+get_match(#k_bin_seg{}=Seg, St0) ->
+ {[S,N]=Mes,St1} = new_vars(2, St0),
+ {Seg#k_bin_seg{seg=S,next=N},Mes,St1};
+get_match(#k_tuple{es=Es}, St0) ->
+ {Mes,St1} = new_vars(length(Es), St0),
+ {#k_tuple{es=Mes},Mes,St1};
+get_match(M, St) ->
+ {M,[],St}.
+
+new_clauses(Cs0, U, St) ->
+ Cs1 = map(fun (#iclause{sub=Sub0,pats=[Arg|As]}=C) ->
+ Head = case arg_arg(Arg) of
+ #k_cons{hd=H,tl=T} -> [H,T|As];
+ #k_tuple{es=Es} -> Es ++ As;
+ #k_binary{segs=E} -> [E|As];
+ #k_bin_seg{seg=S,next=N} ->
+ [S,N|As];
+ _Other -> As
+ end,
+ Vs = arg_alias(Arg),
+ Sub1 = foldl(fun (#k_var{name=V}, Acc) ->
+ subst_vsub(V, U#k_var.name, Acc)
+ end, Sub0, Vs),
+ C#iclause{sub=Sub1,pats=Head}
+ end, Cs0),
+ {Cs1,St}.
+
+%% build_guard([GuardClause]) -> GuardExpr.
+
+build_guard([]) -> fail;
+build_guard(Cs) -> #k_guard{clauses=Cs}.
+
+%% build_select(Var, [ConClause]) -> SelectExpr.
+
+build_select(V, [Tc|_]=Tcs) ->
+ Anno = get_kanno(Tc),
+ #k_select{anno=Anno,var=V,types=Tcs}.
+
+%% build_alt(First, Then) -> AltExpr.
+%% Build an alt, attempt some simple optimisation.
+
+build_alt(fail, Then) -> Then;
+build_alt(First,Then) -> build_alt_1st_no_fail(First, Then).
+
+build_alt_1st_no_fail(First, fail) -> First;
+build_alt_1st_no_fail(First, Then) -> #k_alt{first=First,then=Then}.
+
+%% build_match([MatchVar], MatchExpr) -> Kexpr.
+%% Build a match expr if there is a match.
+
+build_match(Us, #k_alt{}=Km) -> #k_match{vars=Us,body=Km};
+build_match(Us, #k_select{}=Km) -> #k_match{vars=Us,body=Km};
+build_match(Us, #k_guard{}=Km) -> #k_match{vars=Us,body=Km};
+build_match(_, Km) -> Km.
+
+%% clause_arg(Clause) -> FirstArg.
+%% clause_con(Clause) -> Constructor.
+%% clause_val(Clause) -> Value.
+%% is_var_clause(Clause) -> boolean().
+
+clause_arg(#iclause{pats=[Arg|_]}) -> Arg.
+
+clause_con(C) -> arg_con(clause_arg(C)).
+
+clause_val(C) -> arg_val(clause_arg(C)).
+
+is_var_clause(C) -> clause_con(C) == k_var.
+
+%% arg_arg(Arg) -> Arg.
+%% arg_alias(Arg) -> Aliases.
+%% arg_con(Arg) -> Constructor.
+%% arg_val(Arg) -> Value.
+%% These are the basic functions for obtaining fields in an argument.
+
+arg_arg(#ialias{pat=Con}) -> Con;
+arg_arg(Con) -> Con.
+
+arg_alias(#ialias{vars=As}) -> As;
+arg_alias(_Con) -> [].
+
+arg_con(Arg) ->
+ case arg_arg(Arg) of
+ #k_int{} -> k_int;
+ #k_float{} -> k_float;
+ #k_atom{} -> k_atom;
+ #k_nil{} -> k_nil;
+ #k_cons{} -> k_cons;
+ #k_tuple{} -> k_tuple;
+ #k_binary{} -> k_binary;
+ #k_bin_end{} -> k_bin_end;
+ #k_bin_seg{} -> k_bin_seg;
+ #k_var{} -> k_var
+ end.
+
+arg_val(Arg) ->
+ case arg_arg(Arg) of
+ #k_int{val=I} -> I;
+ #k_float{val=F} -> F;
+ #k_atom{val=A} -> A;
+ #k_nil{} -> 0;
+ #k_cons{} -> 2;
+ #k_tuple{es=Es} -> length(Es);
+ #k_bin_seg{size=S,unit=U,type=T,flags=Fs} ->
+ {set_kanno(S, []),U,T,Fs};
+ #k_bin_end{} -> 0;
+ #k_binary{} -> 0
+ end.
+
+%% ubody(Expr, Break, State) -> {Expr,[UsedVar],State}.
+%% Tag the body sequence with its used variables. These bodies
+%% either end with a #k_break{}, or with #k_return{} or an expression
+%% which itself can return, #k_enter{}, #k_match{} ... .
+
+ubody(#iset{vars=[],arg=#iletrec{}=Let,body=B0}, Br, St0) ->
+ %% An iletrec{} should never be last.
+ St1 = iletrec_funs(Let, St0),
+ ubody(B0, Br, St1);
+ubody(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Br, St0) ->
+ {E1,Eu,St1} = uexpr(E0, {break,Vs}, St0),
+ {B1,Bu,St2} = ubody(B0, Br, St1),
+ Ns = lit_list_vars(Vs),
+ Used = union(Eu, subtract(Bu, Ns)), %Used external vars
+ {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
+ubody(#ivalues{anno=A,args=As}, return, St) ->
+ Au = lit_list_vars(As),
+ {#k_return{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
+ubody(#ivalues{anno=A,args=As}, {break,_Vbs}, St) ->
+ Au = lit_list_vars(As),
+ {#k_break{anno=#k{us=Au,ns=[],a=A},args=As},Au,St};
+ubody(E, return, St0) ->
+ %% Enterable expressions need no trailing return.
+ case is_enter_expr(E) of
+ true -> uexpr(E, return, St0);
+ false ->
+ {Ea,Pa,St1} = force_atomic(E, St0),
+ ubody(pre_seq(Pa, #ivalues{args=[Ea]}), return, St1)
+ end;
+ubody(E, {break,Rs}, St0) ->
+ %%ok = io:fwrite("ubody ~w:~p~n", [?LINE,{E,Br}]),
+ %% Exiting expressions need no trailing break.
+ case is_exit_expr(E) of
+ true -> uexpr(E, return, St0);
+ false ->
+ {Ea,Pa,St1} = force_atomic(E, St0),
+ ubody(pre_seq(Pa, #ivalues{args=[Ea]}), {break,Rs}, St1)
+ end.
+
+iletrec_funs(#iletrec{defs=Fs}, St0) ->
+ %% Use union of all free variables.
+ %% First just work out free variables for all functions.
+ Free = foldl(fun ({_,#ifun{vars=Vs,body=Fb0}}, Free0) ->
+ {_,Fbu,_} = ubody(Fb0, return, St0),
+ Ns = lit_list_vars(Vs),
+ Free1 = subtract(Fbu, Ns),
+ union(Free1, Free0)
+ end, [], Fs),
+ FreeVs = make_vars(Free),
+ %% Add this free info to State.
+ St1 = foldl(fun ({N,#ifun{vars=Vs}}, Lst) ->
+ store_free(N, length(Vs), FreeVs, Lst)
+ end, St0, Fs),
+ %% Now regenerate local functions to use free variable information.
+ St2 = foldl(fun ({N,#ifun{anno=Fa,vars=Vs,body=Fb0}}, Lst0) ->
+ {Fb1,_,Lst1} = ubody(Fb0, return, Lst0),
+ Arity = length(Vs) + length(FreeVs),
+ Fun = #k_fdef{anno=#k{us=[],ns=[],a=Fa},
+ func=N,arity=Arity,
+ vars=Vs ++ FreeVs,body=Fb1},
+ Lst1#kern{funs=[Fun|Lst1#kern.funs]}
+ end, St1, Fs),
+ St2.
+
+%% is_exit_expr(Kexpr) -> boolean().
+%% Test whether Kexpr always exits and never returns.
+
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=throw,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=exit,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=error,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=error,arity=2}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=fault,arity=1}}) -> true;
+is_exit_expr(#k_call{op=#k_remote{mod=erlang,name=fault,arity=2}}) -> true;
+is_exit_expr(#k_call{op=#k_internal{name=match_fail,arity=1}}) -> true;
+is_exit_expr(#k_bif{op=#k_internal{name=rethrow,arity=2}}) -> true;
+is_exit_expr(#k_receive_next{}) -> true;
+is_exit_expr(_) -> false.
+
+%% is_enter_expr(Kexpr) -> boolean().
+%% Test whether Kexpr is "enterable", i.e. can handle return from
+%% within itself without extra #k_return{}.
+
+is_enter_expr(#k_call{}) -> true;
+is_enter_expr(#k_match{}) -> true;
+is_enter_expr(#k_receive{}) -> true;
+is_enter_expr(#k_receive_next{}) -> true;
+%%is_enter_expr(#k_try{}) -> true; %Soon
+is_enter_expr(_) -> false.
+
+%% uguard(Expr, State) -> {Expr,[UsedVar],State}.
+%% Tag the guard sequence with its used variables.
+
+uguard(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false}}=Try, St0) ->
+ {B1,Bu,St1} = uguard(B0, St0),
+ {Try#k_try{anno=#k{us=Bu,ns=[],a=A},arg=B1},Bu,St1};
+uguard(T, St) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,T]),
+ uguard_test(T, St).
+
+%% uguard_test(Expr, State) -> {Test,[UsedVar],State}.
+%% At this stage tests are just expressions which don't return any
+%% values.
+
+uguard_test(T, St) -> uguard_expr(T, [], St).
+
+uguard_expr(#iset{anno=A,vars=Vs,arg=E0,body=B0}, Rs, St0) ->
+ Ns = lit_list_vars(Vs),
+ {E1,Eu,St1} = uguard_expr(E0, Vs, St0),
+ {B1,Bu,St2} = uguard_expr(B0, Rs, St1),
+ Used = union(Eu, subtract(Bu, Ns)),
+ {#k_seq{anno=#k{us=Used,ns=Ns,a=A},arg=E1,body=B1},Used,St2};
+uguard_expr(#k_try{anno=A,arg=B0,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false}}=Try, Rs, St0) ->
+ {B1,Bu,St1} = uguard_expr(B0, Rs, St0),
+ {Try#k_try{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},arg=B1,ret=Rs},
+ Bu,St1};
+uguard_expr(#k_test{anno=A,op=Op,args=As}=Test, Rs, St) ->
+ [] = Rs, %Sanity check
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Test#k_test{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A}},
+ Used,St};
+uguard_expr(#k_bif{anno=A,op=Op,args=As}=Bif, Rs, St) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
+ Used,St};
+uguard_expr(#ivalues{anno=A,args=As}, Rs, St) ->
+ Sets = foldr2(fun (V, Arg, Rhs) ->
+ #iset{anno=A,vars=[V],arg=Arg,body=Rhs}
+ end, #k_atom{val=true}, Rs, As),
+ uguard_expr(Sets, [], St);
+uguard_expr(#k_match{anno=A,vars=Vs,body=B0}, Rs, St0) ->
+ %% Experimental support for andalso/orelse in guards.
+ Br = case Rs of
+ [] -> return;
+ _ -> {break,Rs}
+ end,
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
+ vars=Vs,body=B1,ret=Rs},Bu,St1};
+uguard_expr(Lit, Rs, St) ->
+ %% Transform literals to puts here.
+ Used = lit_vars(Lit),
+ {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
+ arg=Lit,ret=Rs},Used,St}.
+
+%% uexpr(Expr, Break, State) -> {Expr,[UsedVar],State}.
+%% Tag an expression with its used variables.
+%% Break = return | {break,[RetVar]}.
+
+uexpr(#k_call{anno=A,op=#k_local{name=F,arity=Ar}=Op,args=As0}=Call, Br, St) ->
+ Free = get_free(F, Ar, St),
+ As1 = As0 ++ Free, %Add free variables LAST!
+ Used = lit_list_vars(As1),
+ {case Br of
+ {break,Rs} ->
+ Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
+ op=Op#k_local{arity=Ar + length(Free)},
+ args=As1,ret=Rs};
+ return ->
+ #k_enter{anno=#k{us=Used,ns=[],a=A},
+ op=Op#k_local{arity=Ar + length(Free)},
+ args=As1}
+ end,Used,St};
+uexpr(#k_call{anno=A,op=Op,args=As}=Call, {break,Rs}, St) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Call#k_call{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},ret=Rs},
+ Used,St};
+uexpr(#k_call{anno=A,op=Op,args=As}, return, St) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {#k_enter{anno=#k{us=Used,ns=[],a=A},op=Op,args=As},
+ Used,St};
+uexpr(#k_bif{anno=A,op=Op,args=As}=Bif, {break,Rs}, St0) ->
+ Used = union(op_vars(Op), lit_list_vars(As)),
+ {Brs,St1} = bif_returns(Op, Rs, St0),
+ {Bif#k_bif{anno=#k{us=Used,ns=lit_list_vars(Brs),a=A},ret=Brs},
+ Used,St1};
+uexpr(#k_match{anno=A,vars=Vs,body=B0}, Br, St0) ->
+ Rs = break_rets(Br),
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ {#k_match{anno=#k{us=Bu,ns=lit_list_vars(Rs),a=A},
+ vars=Vs,body=B1,ret=Rs},Bu,St1};
+uexpr(#k_receive{anno=A,var=V,body=B0,timeout=T,action=A0}, Br, St0) ->
+ Rs = break_rets(Br),
+ Tu = lit_vars(T), %Timeout is atomic
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ {A1,Au,St2} = ubody(A0, Br, St1),
+ Used = del_element(V#k_var.name, union(Bu, union(Tu, Au))),
+ {#k_receive{anno=#k{us=Used,ns=lit_list_vars(Rs),a=A},
+ var=V,body=B1,timeout=T,action=A1,ret=Rs},
+ Used,St2};
+uexpr(#k_receive_accept{anno=A}, _, St) ->
+ {#k_receive_accept{anno=#k{us=[],ns=[],a=A}},[],St};
+uexpr(#k_receive_next{anno=A}, _, St) ->
+ {#k_receive_next{anno=#k{us=[],ns=[],a=A}},[],St};
+uexpr(#k_try{anno=A,arg=A0,vars=Vs,body=B0,evars=Evs,handler=H0},
+ {break,Rs0}, St0) ->
+ {Avs,St1} = new_vars(length(Vs), St0), %Need dummy names here
+ {A1,Au,St2} = ubody(A0, {break,Avs}, St1), %Must break to clean up here!
+ {B1,Bu,St3} = ubody(B0, {break,Rs0}, St2),
+ {H1,Hu,St4} = ubody(H0, {break,Rs0}, St3),
+ %% Guarantee ONE return variable.
+ NumNew = if
+ Rs0 =:= [] -> 1;
+ true -> 0
+ end,
+ {Ns,St5} = new_vars(NumNew, St4),
+ Rs1 = Rs0 ++ Ns,
+ Used = union([Au,subtract(Bu, lit_list_vars(Vs)),
+ subtract(Hu, lit_list_vars(Evs))]),
+ {#k_try{anno=#k{us=Used,ns=lit_list_vars(Rs1),a=A},
+ arg=A1,vars=Vs,body=B1,evars=Evs,handler=H1,ret=Rs1},
+ Used,St5};
+uexpr(#k_catch{anno=A,body=B0}, {break,Rs0}, St0) ->
+ {Rb,St1} = new_var(St0),
+ {B1,Bu,St2} = ubody(B0, {break,[Rb]}, St1),
+ %% Guarantee ONE return variable.
+ {Ns,St3} = new_vars(1 - length(Rs0), St2),
+ Rs1 = Rs0 ++ Ns,
+ {#k_catch{anno=#k{us=Bu,ns=lit_list_vars(Rs1),a=A},body=B1,ret=Rs1},Bu,St3};
+uexpr(#ifun{anno=A,vars=Vs,body=B0}=IFun, {break,Rs}, St0) ->
+ {B1,Bu,St1} = ubody(B0, return, St0), %Return out of new function
+ Ns = lit_list_vars(Vs),
+ Free = subtract(Bu, Ns), %Free variables in fun
+ Fvs = make_vars(Free),
+ Arity = length(Vs) + length(Free),
+ {{Index,Uniq,Fname}, St3} =
+ case lists:keysearch(id, 1, A) of
+ {value,{id,Id}} ->
+ {Id, St1};
+ false ->
+ %% No id annotation. Must invent one.
+ I = St1#kern.fcount,
+ U = erlang:hash(IFun, (1 bsl 27)-1),
+ {N, St2} = new_fun_name(St1),
+ {{I,U,N}, St2}
+ end,
+ Fun = #k_fdef{anno=#k{us=[],ns=[],a=A},func=Fname,arity=Arity,
+ vars=Vs ++ Fvs,body=B1},
+ {#k_bif{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
+ op=#k_internal{name=make_fun,arity=length(Free)+3},
+ args=[#k_atom{val=Fname},#k_int{val=Arity},
+ #k_int{val=Index},#k_int{val=Uniq}|Fvs],
+ ret=Rs},
+% {#k_call{anno=#k{us=Free,ns=lit_list_vars(Rs),a=A},
+% op=#k_internal{name=make_fun,arity=length(Free)+3},
+% args=[#k_atom{val=Fname},#k_int{val=Arity},
+% #k_int{val=Index},#k_int{val=Uniq}|Fvs],
+% ret=Rs},
+ Free,St3#kern{funs=[Fun|St3#kern.funs]}};
+uexpr(Lit, {break,Rs}, St) ->
+ %% Transform literals to puts here.
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,Lit]),
+ Used = lit_vars(Lit),
+ {#k_put{anno=#k{us=Used,ns=lit_list_vars(Rs),a=get_kanno(Lit)},
+ arg=Lit,ret=Rs},Used,St}.
+
+%% get_free(Name, Arity, State) -> [Free].
+%% store_free(Name, Arity, [Free], State) -> State.
+
+get_free(F, A, St) ->
+ case orddict:find({F,A}, St#kern.free) of
+ {ok,Val} -> Val;
+ error -> []
+ end.
+
+store_free(F, A, Free, St) ->
+ St#kern{free=orddict:store({F,A}, Free, St#kern.free)}.
+
+break_rets({break,Rs}) -> Rs;
+break_rets(return) -> [].
+
+%% bif_returns(Op, [Ret], State) -> {[Ret],State}.
+
+bif_returns(#k_remote{mod=M,name=N,arity=Ar}, Rs, St0) ->
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{M,N,Ar,Rs}]),
+ {Ns,St1} = new_vars(bif_vals(M, N, Ar) - length(Rs), St0),
+ {Rs ++ Ns,St1};
+bif_returns(#k_internal{name=N,arity=Ar}, Rs, St0) ->
+ %%ok = io:fwrite("uexpr ~w:~p~n", [?LINE,{N,Ar,Rs}]),
+ {Ns,St1} = new_vars(bif_vals(N, Ar) - length(Rs), St0),
+ {Rs ++ Ns,St1}.
+
+%% umatch(Match, Break, State) -> {Match,[UsedVar],State}.
+%% Tag a match expression with its used variables.
+
+umatch(#k_alt{anno=A,first=F0,then=T0}, Br, St0) ->
+ {F1,Fu,St1} = umatch(F0, Br, St0),
+ {T1,Tu,St2} = umatch(T0, Br, St1),
+ Used = union(Fu, Tu),
+ {#k_alt{anno=#k{us=Used,ns=[],a=A},first=F1,then=T1},
+ Used,St2};
+umatch(#k_select{anno=A,var=V,types=Ts0}, Br, St0) ->
+ {Ts1,Tus,St1} = umatch_list(Ts0, Br, St0),
+ Used = add_element(V#k_var.name, Tus),
+ {#k_select{anno=#k{us=Used,ns=[],a=A},var=V,types=Ts1},Used,St1};
+umatch(#k_type_clause{anno=A,type=T,values=Vs0}, Br, St0) ->
+ {Vs1,Vus,St1} = umatch_list(Vs0, Br, St0),
+ {#k_type_clause{anno=#k{us=Vus,ns=[],a=A},type=T,values=Vs1},Vus,St1};
+umatch(#k_val_clause{anno=A,val=P,body=B0}, Br, St0) ->
+ {U0,Ps} = pat_vars(P),
+ {B1,Bu,St1} = umatch(B0, Br, St0),
+ Used = union(U0, subtract(Bu, Ps)),
+ {#k_val_clause{anno=#k{us=Used,ns=[],a=A},val=P,body=B1},
+ Used,St1};
+umatch(#k_guard{anno=A,clauses=Gs0}, Br, St0) ->
+ {Gs1,Gus,St1} = umatch_list(Gs0, Br, St0),
+ {#k_guard{anno=#k{us=Gus,ns=[],a=A},clauses=Gs1},Gus,St1};
+umatch(#k_guard_clause{anno=A,guard=G0,body=B0}, Br, St0) ->
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,G0]),
+ {G1,Gu,St1} = uguard(G0, St0),
+ %%ok = io:fwrite("~w: ~p~n", [?LINE,G1]),
+ {B1,Bu,St2} = umatch(B0, Br, St1),
+ Used = union(Gu, Bu),
+ {#k_guard_clause{anno=#k{us=Used,ns=[],a=A},guard=G1,body=B1},Used,St2};
+umatch(B0, Br, St0) -> ubody(B0, Br, St0).
+
+umatch_list(Ms0, Br, St) ->
+ foldr(fun (M0, {Ms1,Us,Sta}) ->
+ {M1,Mu,Stb} = umatch(M0, Br, Sta),
+ {[M1|Ms1],union(Mu, Us),Stb}
+ end, {[],[],St}, Ms0).
+
+%% op_vars(Op) -> [VarName].
+
+op_vars(#k_local{}) -> [];
+op_vars(#k_remote{mod=Mod,name=Name}) ->
+ ordsets:from_list([V || #k_var{name=V} <- [Mod,Name]]);
+op_vars(#k_internal{}) -> [];
+op_vars(Atomic) -> lit_vars(Atomic).
+
+%% lit_vars(Literal) -> [VarName].
+%% Return the variables in a literal.
+
+lit_vars(#k_var{name=N}) -> [N];
+lit_vars(#k_int{}) -> [];
+lit_vars(#k_float{}) -> [];
+lit_vars(#k_atom{}) -> [];
+%%lit_vars(#k_char{}) -> [];
+lit_vars(#k_string{}) -> [];
+lit_vars(#k_nil{}) -> [];
+lit_vars(#k_cons{hd=H,tl=T}) ->
+ union(lit_vars(H), lit_vars(T));
+lit_vars(#k_binary{segs=V}) -> lit_vars(V);
+lit_vars(#k_bin_end{}) -> [];
+lit_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
+ union(lit_vars(Size), union(lit_vars(S), lit_vars(N)));
+lit_vars(#k_tuple{es=Es}) ->
+ lit_list_vars(Es).
+
+lit_list_vars(Ps) ->
+ foldl(fun (P, Vs) -> union(lit_vars(P), Vs) end, [], Ps).
+
+%% pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
+%% Return variables in a pattern. All variables are new variables
+%% except those in the size field of binary segments.
+
+pat_vars(#k_var{name=N}) -> {[],[N]};
+%%pat_vars(#k_char{}) -> {[],[]};
+pat_vars(#k_int{}) -> {[],[]};
+pat_vars(#k_float{}) -> {[],[]};
+pat_vars(#k_atom{}) -> {[],[]};
+pat_vars(#k_string{}) -> {[],[]};
+pat_vars(#k_nil{}) -> {[],[]};
+pat_vars(#k_cons{hd=H,tl=T}) ->
+ pat_list_vars([H,T]);
+pat_vars(#k_binary{segs=V}) ->
+ pat_vars(V);
+pat_vars(#k_bin_seg{size=Size,seg=S,next=N}) ->
+ {U1,New} = pat_list_vars([S,N]),
+ {[],U2} = pat_vars(Size),
+ {union(U1, U2),New};
+pat_vars(#k_bin_end{}) -> {[],[]};
+pat_vars(#k_tuple{es=Es}) ->
+ pat_list_vars(Es).
+
+pat_list_vars(Ps) ->
+ foldl(fun (P, {Used0,New0}) ->
+ {Used,New} = pat_vars(P),
+ {union(Used0, Used),union(New0, New)} end,
+ {[],[]}, Ps).
+
+%% aligned(Bits, Size, Unit, Flags) -> {Size,Flags}
+%% Add 'aligned' to the flags if the current field is aligned.
+%% Number of bits correct modulo 8.
+
+aligned(B, S, U, Fs) when B rem 8 =:= 0 ->
+ {incr_bits(B, S, U),[aligned|Fs]};
+aligned(B, S, U, Fs) ->
+ {incr_bits(B, S, U),Fs}.
+
+incr_bits(B, #k_int{val=S}, U) when integer(B) -> B + S*U;
+incr_bits(_, #k_atom{val=all}, _) -> 0; %Always aligned
+incr_bits(B, _, 8) -> B;
+incr_bits(_, _, _) -> unknown.
+
+make_list(Es) ->
+ foldr(fun (E, Acc) -> #c_cons{hd=E,tl=Acc} end, #c_nil{}, Es).
+
+%% List of integers in interval [N,M]. Empty list if N > M.
+
+integers(N, M) when N =< M ->
+ [N|integers(N + 1, M)];
+integers(_, _) -> [].
+
+%%%
+%%% Handling of warnings.
+%%%
+
+format_error({nomatch_shadow,Line}) ->
+ M = io_lib:format("this clause cannot match because a previous clause at line ~p "
+ "always matches", [Line]),
+ lists:flatten(M);
+format_error(nomatch_shadow) ->
+ "this clause cannot match because a previous clause always matches".
+
+add_warning(none, Term, #kern{ws=Ws}=St) ->
+ St#kern{ws=[{?MODULE,Term}|Ws]};
+add_warning(Line, Term, #kern{ws=Ws}=St) when Line >= 0 ->
+ St#kern{ws=[{Line,?MODULE,Term}|Ws]};
+add_warning(_, _, St) -> St.
+
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl
new file mode 100644
index 0000000000..822a9e34e1
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel.hrl
@@ -0,0 +1,77 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_kernel.hrl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
+%%
+
+%% Purpose : Kernel Erlang as records.
+
+%% It would be nice to incorporate some generic functions as well but
+%% this could make including this file difficult.
+%% N.B. the annotation field is ALWAYS the first field!
+
+%% Kernel annotation record.
+-record(k, {us, %Used variables
+ ns, %New variables
+ a}). %Core annotation
+
+%% Literals
+%% NO CHARACTERS YET.
+%%-record(k_char, {anno=[],val}).
+-record(k_int, {anno=[],val}).
+-record(k_float, {anno=[],val}).
+-record(k_atom, {anno=[],val}).
+-record(k_string, {anno=[],val}).
+-record(k_nil, {anno=[]}).
+
+-record(k_tuple, {anno=[],es}).
+-record(k_cons, {anno=[],hd,tl}).
+-record(k_binary, {anno=[],segs}).
+-record(k_bin_seg, {anno=[],size,unit,type,flags,seg,next}).
+-record(k_bin_end, {anno=[]}).
+-record(k_var, {anno=[],name}).
+
+-record(k_local, {anno=[],name,arity}).
+-record(k_remote, {anno=[],mod,name,arity}).
+-record(k_internal, {anno=[],name,arity}).
+
+-record(k_mdef, {anno=[],name,exports,attributes,body}).
+-record(k_fdef, {anno=[],func,arity,vars,body}).
+
+-record(k_seq, {anno=[],arg,body}).
+-record(k_put, {anno=[],arg,ret=[]}).
+-record(k_bif, {anno=[],op,args,ret=[]}).
+-record(k_test, {anno=[],op,args}).
+-record(k_call, {anno=[],op,args,ret=[]}).
+-record(k_enter, {anno=[],op,args}).
+-record(k_receive, {anno=[],var,body,timeout,action,ret=[]}).
+-record(k_receive_accept, {anno=[]}).
+-record(k_receive_next, {anno=[]}).
+-record(k_try, {anno=[],arg,vars,body,evars,handler,ret=[]}).
+-record(k_catch, {anno=[],body,ret=[]}).
+
+-record(k_match, {anno=[],vars,body,ret=[]}).
+-record(k_alt, {anno=[],first,then}).
+-record(k_select, {anno=[],var,types}).
+-record(k_type_clause, {anno=[],type,values}).
+-record(k_val_clause, {anno=[],val,body}).
+-record(k_guard, {anno=[],clauses}).
+-record(k_guard_clause, {anno=[],guard,body}).
+
+-record(k_break, {anno=[],args=[]}).
+-record(k_return, {anno=[],args=[]}).
+
+%%k_get_anno(Thing) -> element(2, Thing).
+%%k_set_anno(Thing, Anno) -> setelement(2, Thing, Anno).
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl
new file mode 100644
index 0000000000..92ff173834
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_kernel_pp.erl
@@ -0,0 +1,444 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_kernel_pp.erl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
+%%
+%% Purpose : Kernel Erlang (naive) prettyprinter
+
+-module(v3_kernel_pp).
+
+-include("v3_kernel.hrl").
+
+-export([format/1]).
+
+%% These are "internal" structures in sys_kernel which are here for
+%% debugging purposes.
+-record(iset, {anno=[],vars,arg,body}).
+-record(ifun, {anno=[],vars,body}).
+
+%% ====================================================================== %%
+%% format(Node) -> Text
+%% Node = coreErlang()
+%% Text = string() | [Text]
+%%
+%% Prettyprint-formats (naively) an abstract Core Erlang syntax
+%% tree.
+
+-record(ctxt, {indent = 0,
+ item_indent = 2,
+ body_indent = 2,
+ tab_width = 8}).
+
+canno(Cthing) -> element(2, Cthing).
+
+format(Node) -> format(Node, #ctxt{}).
+
+format(Node, Ctxt) ->
+ case canno(Node) of
+ [] ->
+ format_1(Node, Ctxt);
+ List ->
+ format_anno(List, Ctxt, fun (Ctxt1) -> format_1(Node, Ctxt1) end)
+ end.
+
+format_anno(Anno, Ctxt, ObjFun) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ ["( ",
+ ObjFun(Ctxt1),
+ nl_indent(Ctxt1),
+ "-| ",io_lib:write(Anno),
+ " )"].
+
+%% format_1(Kexpr, Context) -> string().
+
+format_1(#k_atom{val=A}, _Ctxt) -> core_atom(A);
+%%format_1(#k_char{val=C}, _Ctxt) -> io_lib:write_char(C);
+format_1(#k_float{val=F}, _Ctxt) -> float_to_list(F);
+format_1(#k_int{val=I}, _Ctxt) -> integer_to_list(I);
+format_1(#k_nil{}, _Ctxt) -> "[]";
+format_1(#k_string{val=S}, _Ctxt) -> io_lib:write_string(S);
+format_1(#k_var{name=V}, _Ctxt) ->
+ if atom(V) ->
+ case atom_to_list(V) of
+ [$_|Cs] -> "_X" ++ Cs;
+ [C|Cs] when C >= $A, C =< $Z -> [C|Cs];
+ Cs -> [$_|Cs]
+ end;
+ integer(V) -> [$_|integer_to_list(V)]
+ end;
+format_1(#k_cons{hd=H,tl=T}, Ctxt) ->
+ Txt = ["["|format(H, ctxt_bump_indent(Ctxt, 1))],
+ [Txt|format_list_tail(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))];
+format_1(#k_tuple{es=Es}, Ctxt) ->
+ [${,
+ format_hseq(Es, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ $}
+ ];
+format_1(#k_binary{segs=S}, Ctxt) ->
+ ["#<",format(S, ctxt_bump_indent(Ctxt, 2)),">#"];
+format_1(#k_bin_seg{}=S, Ctxt) ->
+ [format_bin_seg_1(S, Ctxt),
+ format_bin_seg(S#k_bin_seg.next, ctxt_bump_indent(Ctxt, 2))];
+format_1(#k_bin_end{}, _Ctxt) -> "#<>#";
+format_1(#k_local{name=N,arity=A}, Ctxt) ->
+ "local " ++ format_fa_pair({N,A}, Ctxt);
+format_1(#k_remote{mod=M,name=N,arity=A}, _Ctxt) ->
+ %% This is for our internal translator.
+ io_lib:format("remote ~s:~s/~w", [format(M),format(N),A]);
+format_1(#k_internal{name=N,arity=A}, Ctxt) ->
+ "internal " ++ format_fa_pair({N,A}, Ctxt);
+format_1(#k_seq{arg=A,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ ["do",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "then",
+ nl_indent(Ctxt)
+ | format(B, Ctxt)
+ ];
+format_1(#k_match{vars=Vs,body=Bs,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["match ",
+ format_hseq(Vs, ",", ctxt_bump_indent(Ctxt, 6), fun format/2),
+ nl_indent(Ctxt1),
+ format(Bs, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_alt{first=O,then=T}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["alt",
+ nl_indent(Ctxt1),
+ format(O, Ctxt1),
+ nl_indent(Ctxt1),
+ format(T, Ctxt1)];
+format_1(#k_select{var=V,types=Cs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ ["select ",
+ format(V, Ctxt),
+ nl_indent(Ctxt1),
+ format_vseq(Cs, "", "", Ctxt1, fun format/2)
+ ];
+format_1(#k_type_clause{type=T,values=Cs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["type ",
+ io_lib:write(T),
+ nl_indent(Ctxt1),
+ format_vseq(Cs, "", "", Ctxt1, fun format/2)
+ ];
+format_1(#k_val_clause{val=Val,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ [format(Val, Ctxt),
+ " ->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#k_guard{clauses=Gs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, 5),
+ ["when ",
+ nl_indent(Ctxt1),
+ format_vseq(Gs, "", "", Ctxt1, fun format/2)];
+format_1(#k_guard_clause{guard=G,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ [format(G, Ctxt),
+ nl_indent(Ctxt),
+ "->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(#k_call{op=Op,args=As,ret=Rs}, Ctxt) ->
+ Txt = ["call (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1),
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_enter{op=Op,args=As}, Ctxt) ->
+ Txt = ["enter (",format(Op, ctxt_bump_indent(Ctxt, 7)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1)];
+format_1(#k_bif{op=Op,args=As,ret=Rs}, Ctxt) ->
+ Txt = ["bif (",format(Op, ctxt_bump_indent(Ctxt, 5)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1),
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_test{op=Op,args=As}, Ctxt) ->
+ Txt = ["test (",format(Op, ctxt_bump_indent(Ctxt, 6)),$)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, 2),
+ [Txt,format_args(As, Ctxt1)];
+format_1(#k_put{arg=A,ret=Rs}, Ctxt) ->
+ [format(A, Ctxt),
+ format_ret(Rs, ctxt_bump_indent(Ctxt, 1))
+ ];
+format_1(#k_try{arg=A,vars=Vs,body=B,evars=Evs,handler=H,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["try",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "of ",
+ format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 3), fun format/2),
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "catch ",
+ format_hseq(Evs, ", ", ctxt_bump_indent(Ctxt, 6), fun format/2),
+ nl_indent(Ctxt1),
+ format(H, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_catch{body=B,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["catch",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_receive{var=V,body=B,timeout=T,action=A,ret=Rs}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.item_indent),
+ ["receive ",
+ format(V, Ctxt),
+ nl_indent(Ctxt1),
+ format(B, Ctxt1),
+ nl_indent(Ctxt),
+ "after ",
+ format(T, ctxt_bump_indent(Ctxt, 6)),
+ " ->",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "end",
+ format_ret(Rs, Ctxt1)
+ ];
+format_1(#k_receive_accept{}, _Ctxt) -> "receive_accept";
+format_1(#k_receive_next{}, _Ctxt) -> "receive_next";
+format_1(#k_break{args=As}, Ctxt) ->
+ ["<",
+ format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ ">"
+ ];
+format_1(#k_return{args=As}, Ctxt) ->
+ ["<<",
+ format_hseq(As, ",", ctxt_bump_indent(Ctxt, 1), fun format/2),
+ ">>"
+ ];
+format_1(#k_fdef{func=F,arity=A,vars=Vs,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["fdef ",
+ format_fa_pair({F,A}, ctxt_bump_indent(Ctxt, 5)),
+ format_args(Vs, ctxt_bump_indent(Ctxt, 14)),
+ " =",
+ nl_indent(Ctxt1),
+ format(B, Ctxt1)
+ ];
+format_1(#k_mdef{name=N,exports=Es,attributes=As,body=B}, Ctxt) ->
+ ["module ",
+ format(#k_atom{val=N}, ctxt_bump_indent(Ctxt, 7)),
+ nl_indent(Ctxt),
+ "export [",
+ format_vseq(Es,
+ "", ",",
+ ctxt_bump_indent(Ctxt, 8),
+ fun format_fa_pair/2),
+ "]",
+ nl_indent(Ctxt),
+ "attributes [",
+ format_vseq(As,
+ "", ",",
+ ctxt_bump_indent(Ctxt, 12),
+ fun format_attribute/2),
+ "]",
+ nl_indent(Ctxt),
+ format_vseq(B,
+ "", "",
+ Ctxt,
+ fun format/2),
+ nl_indent(Ctxt)
+ | "end"
+ ];
+%% Internal sys_kernel structures.
+format_1(#iset{vars=Vs,arg=A,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["set <",
+ format_hseq(Vs, ", ", ctxt_bump_indent(Ctxt, 5), fun format/2),
+ "> =",
+ nl_indent(Ctxt1),
+ format(A, Ctxt1),
+ nl_indent(Ctxt),
+ "in "
+ | format(B, ctxt_bump_indent(Ctxt, 2))
+ ];
+format_1(#ifun{vars=Vs,body=B}, Ctxt) ->
+ Ctxt1 = ctxt_bump_indent(Ctxt, Ctxt#ctxt.body_indent),
+ ["fun ",
+ format_args(Vs, ctxt_bump_indent(Ctxt, 4)),
+ " ->",
+ nl_indent(Ctxt1)
+ | format(B, Ctxt1)
+ ];
+format_1(Type, _Ctxt) ->
+ ["** Unsupported type: ",
+ io_lib:write(Type)
+ | " **"
+ ].
+
+%% format_ret([RetVar], Context) -> Txt.
+%% Format the return vars of kexpr.
+
+format_ret(Rs, Ctxt) ->
+ [" >> ",
+ "<",
+ format_hseq(Rs, ",", ctxt_bump_indent(Ctxt, 5), fun format/2),
+ ">"].
+
+%% format_args([Arg], Context) -> Txt.
+%% Format arguments.
+
+format_args(As, Ctxt) ->
+ [$(,format_hseq(As, ", ", ctxt_bump_indent(Ctxt, 1), fun format/2),$)].
+
+%% format_hseq([Thing], Separator, Context, Fun) -> Txt.
+%% Format a sequence horizontally.
+
+format_hseq([H], _Sep, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_hseq([H|T], Sep, Ctxt, Fun) ->
+ Txt = [Fun(H, Ctxt)|Sep],
+ Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_hseq(T, Sep, Ctxt1, Fun)];
+format_hseq([], _, _, _) -> "".
+
+%% format_vseq([Thing], LinePrefix, LineSuffix, Context, Fun) -> Txt.
+%% Format a sequence vertically.
+
+format_vseq([H], _Pre, _Suf, Ctxt, Fun) ->
+ Fun(H, Ctxt);
+format_vseq([H|T], Pre, Suf, Ctxt, Fun) ->
+ [Fun(H, Ctxt),Suf,nl_indent(Ctxt),Pre|
+ format_vseq(T, Pre, Suf, Ctxt, Fun)];
+format_vseq([], _, _, _, _) -> "".
+
+format_fa_pair({F,A}, _Ctxt) -> [core_atom(F),$/,integer_to_list(A)].
+
+%% format_attribute({Name,Val}, Context) -> Txt.
+
+format_attribute({Name,Val}, Ctxt) when list(Val) ->
+ Txt = format(#k_atom{val=Name}, Ctxt),
+ Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt,Ctxt)+4),
+ [Txt," = ",
+ $[,format_vseq(Val, "", ",", Ctxt1,
+ fun (A, _C) -> io_lib:write(A) end),$]
+ ];
+format_attribute({Name,Val}, Ctxt) ->
+ Txt = format(#k_atom{val=Name}, Ctxt),
+ [Txt," = ",io_lib:write(Val)].
+
+format_list_tail(#k_nil{anno=[]}, _Ctxt) -> "]";
+format_list_tail(#k_cons{anno=[],hd=H,tl=T}, Ctxt) ->
+ Txt = [$,|format(H, Ctxt)],
+ Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
+ [Txt|format_list_tail(T, Ctxt1)];
+format_list_tail(Tail, Ctxt) ->
+ ["|",format(Tail, ctxt_bump_indent(Ctxt, 1)), "]"].
+
+format_bin_seg(#k_bin_end{anno=[]}, _Ctxt) -> "";
+format_bin_seg(#k_bin_seg{anno=[],next=N}=Seg, Ctxt) ->
+ Txt = [$,|format_bin_seg_1(Seg, Ctxt)],
+ [Txt|format_bin_seg(N, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))];
+format_bin_seg(Seg, Ctxt) ->
+ ["|",format(Seg, ctxt_bump_indent(Ctxt, 2))].
+
+format_bin_seg_1(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg}, Ctxt) ->
+ [format(Seg, Ctxt),
+ ":",format(S, Ctxt),"*",io_lib:write(U),
+ ":",io_lib:write(T),
+ lists:map(fun (F) -> [$-,io_lib:write(F)] end, Fs)
+ ].
+
+% format_bin_elements(#k_binary_cons{hd=H,tl=T,size=S,info=I}, Ctxt) ->
+% A = canno(T),
+% Fe = fun (Eh, Es, Ei, Ct) ->
+% [format(Eh, Ct),":",format(Es, Ct),"/",io_lib:write(Ei)]
+% end,
+% case T of
+% #k_zero_binary{} when A == [] ->
+% Fe(H, S, I, Ctxt);
+% #k_binary_cons{} when A == [] ->
+% Txt = [Fe(H, S, I, Ctxt)|","],
+% Ctxt1 = ctxt_bump_indent(Ctxt, width(Txt, Ctxt)),
+% [Txt|format_bin_elements(T, Ctxt1)];
+% _ ->
+% Txt = [Fe(H, S, I, Ctxt)|"|"],
+% [Txt|format(T, ctxt_bump_indent(Ctxt, width(Txt, Ctxt)))]
+% end.
+
+indent(Ctxt) -> indent(Ctxt#ctxt.indent, Ctxt).
+
+indent(N, _Ctxt) when N =< 0 -> "";
+indent(N, Ctxt) ->
+ T = Ctxt#ctxt.tab_width,
+ string:chars($\t, N div T, string:chars($\s, N rem T)).
+
+nl_indent(Ctxt) -> [$\n|indent(Ctxt)].
+
+
+unindent(T, Ctxt) ->
+ unindent(T, Ctxt#ctxt.indent, Ctxt, []).
+
+unindent(T, N, _Ctxt, C) when N =< 0 ->
+ [T|C];
+unindent([$\s|T], N, Ctxt, C) ->
+ unindent(T, N - 1, Ctxt, C);
+unindent([$\t|T], N, Ctxt, C) ->
+ Tab = Ctxt#ctxt.tab_width,
+ if N >= Tab ->
+ unindent(T, N - Tab, Ctxt, C);
+ true ->
+ unindent([string:chars($\s, Tab - N)|T], 0, Ctxt, C)
+ end;
+unindent([L|T], N, Ctxt, C) when list(L) ->
+ unindent(L, N, Ctxt, [T|C]);
+unindent([H|T], _N, _Ctxt, C) ->
+ [H|[T|C]];
+unindent([], N, Ctxt, [H|T]) ->
+ unindent(H, N, Ctxt, T);
+unindent([], _, _, []) -> [].
+
+
+width(Txt, Ctxt) ->
+ width(Txt, 0, Ctxt, []).
+
+width([$\t|T], A, Ctxt, C) ->
+ width(T, A + Ctxt#ctxt.tab_width, Ctxt, C);
+width([$\n|T], _A, Ctxt, C) ->
+ width(unindent([T|C], Ctxt), Ctxt);
+width([H|T], A, Ctxt, C) when list(H) ->
+ width(H, A, Ctxt, [T|C]);
+width([_|T], A, Ctxt, C) ->
+ width(T, A + 1, Ctxt, C);
+width([], A, Ctxt, [H|T]) ->
+ width(H, A, Ctxt, T);
+width([], A, _, []) -> A.
+
+ctxt_bump_indent(Ctxt, Dx) ->
+ Ctxt#ctxt{indent=Ctxt#ctxt.indent + Dx}.
+
+core_atom(A) -> io_lib:write_string(atom_to_list(A), $').
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl
new file mode 100644
index 0000000000..ff210d83f5
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.erl
@@ -0,0 +1,448 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_life.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
+%%
+%% Purpose : Convert annotated kernel expressions to annotated beam format.
+
+%% This module creates beam format annotated with variable lifetime
+%% information. Each thing is given an index and for each variable we
+%% store the first and last index for its occurrence. The variable
+%% database, VDB, attached to each thing is only relevant internally
+%% for that thing.
+%%
+%% For nested things like matches the numbering continues locally and
+%% the VDB for that thing refers to the variable usage within that
+%% thing. Variables which live through a such a thing are internally
+%% given a very large last index. Internally the indexes continue
+%% after the index of that thing. This creates no problems as the
+%% internal variable info never escapes and externally we only see
+%% variable which are alive both before or after.
+%%
+%% This means that variables never "escape" from a thing and the only
+%% way to get values from a thing is to "return" them, with 'break' or
+%% 'return'. Externally these values become the return values of the
+%% thing. This is no real limitation as most nested things have
+%% multiple threads so working out a common best variable usage is
+%% difficult.
+
+-module(v3_life).
+
+-export([module/2]).
+
+-export([vdb_find/2]).
+
+-import(lists, [map/2,foldl/3]).
+-import(ordsets, [add_element/2,intersection/2,union/2,union/1]).
+
+-include("v3_kernel.hrl").
+-include("v3_life.hrl").
+
+%% These are not defined in v3_kernel.hrl.
+get_kanno(Kthing) -> element(2, Kthing).
+%%set_kanno(Kthing, Anno) -> setelement(2, Kthing, Anno).
+
+module(#k_mdef{name=M,exports=Es,attributes=As,body=Fs0}, Opts) ->
+ put(?MODULE, Opts),
+ Fs1 = map(fun function/1, Fs0),
+ erase(?MODULE),
+ {ok,{M,Es,As,Fs1}}.
+
+%% function(Kfunc) -> Func.
+
+function(#k_fdef{func=F,arity=Ar,vars=Vs,body=Kb}) ->
+ %%ok = io:fwrite("life ~w: ~p~n", [?LINE,{F,Ar}]),
+ As = var_list(Vs),
+ Vdb0 = foldl(fun ({var,N}, Vdb) -> new_var(N, 0, Vdb) end, [], As),
+ %% Force a top-level match!
+ B0 = case Kb of
+ #k_match{} -> Kb;
+ _ ->
+ Ka = get_kanno(Kb),
+ #k_match{anno=#k{us=Ka#k.us,ns=[],a=Ka#k.a},
+ vars=Vs,body=Kb,ret=[]}
+ end,
+ {B1,_,Vdb1} = body(B0, 1, Vdb0),
+ {function,F,Ar,As,B1,Vdb1}.
+
+%% body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
+%% Handle a body, need special cases for transforming match_fails.
+%% We KNOW that they only occur last in a body.
+
+body(#k_seq{arg=#k_put{anno=Pa,arg=Arg,ret=[R]},
+ body=#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},
+ args=[R]}},
+ I, Vdb0) ->
+ Vdb1 = use_vars(Pa#k.us, I, Vdb0), %All used here
+ {[match_fail(Arg, I, Pa#k.a ++ Ea#k.a)],I,Vdb1};
+body(#k_enter{anno=Ea,op=#k_internal{name=match_fail,arity=1},args=[Arg]},
+ I, Vdb0) ->
+ Vdb1 = use_vars(Ea#k.us, I, Vdb0),
+ {[match_fail(Arg, I, Ea#k.a)],I,Vdb1};
+body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
+ %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ {Es,MaxI,Vdb2} = body(Kb, I+1, Vdb1),
+ E = expr(Ke, I, Vdb2),
+ {[E|Es],MaxI,Vdb2};
+body(Ke, I, Vdb0) ->
+ %%ok = io:fwrite("life ~w:~p~n", [?LINE,{Ke,I,Vdb0}]),
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ E = expr(Ke, I, Vdb1),
+ {[E],I,Vdb1}.
+
+%% guard(Kguard, I, Vdb) -> Guard.
+
+guard(#k_try{anno=A,arg=Ts,vars=[#k_var{name=X}],body=#k_var{name=X},
+ handler=#k_atom{val=false},ret=Rs}, I, Vdb) ->
+ %% Lock variables that are alive before try and used afterwards.
+ %% Don't lock variables that are only used inside the try expression.
+ Pdb0 = vdb_sub(I, I+1, Vdb),
+ {T,MaxI,Pdb1} = guard_body(Ts, I+1, Pdb0),
+ Pdb2 = use_vars(A#k.ns, MaxI+1, Pdb1), %Save "return" values
+ #l{ke={protected,T,var_list(Rs)},i=I,a=A#k.a,vdb=Pdb2};
+guard(#k_seq{}=G, I, Vdb0) ->
+ {Es,_,Vdb1} = guard_body(G, I, Vdb0),
+ #l{ke={block,Es},i=I,vdb=Vdb1,a=[]};
+guard(G, I, Vdb) -> guard_expr(G, I, Vdb).
+
+%% guard_body(Kbody, I, Vdb) -> {[Expr],MaxI,Vdb}.
+
+guard_body(#k_seq{arg=Ke,body=Kb}, I, Vdb0) ->
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ {Es,MaxI,Vdb2} = guard_body(Kb, I+1, Vdb1),
+ E = guard_expr(Ke, I, Vdb2),
+ {[E|Es],MaxI,Vdb2};
+guard_body(Ke, I, Vdb0) ->
+ A = get_kanno(Ke),
+ Vdb1 = use_vars(A#k.us, I, new_vars(A#k.ns, I, Vdb0)),
+ E = guard_expr(Ke, I, Vdb1),
+ {[E],I,Vdb1}.
+
+%% guard_expr(Call, I, Vdb) -> Expr
+
+guard_expr(#k_test{anno=A,op=Op,args=As}, I, _Vdb) ->
+ #l{ke={test,test_op(Op),atomic_list(As)},i=I,a=A#k.a};
+guard_expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
+ #l{ke={bif,bif_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
+guard_expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
+ #l{ke={set,var_list(Rs),literal(Arg)},i=I,a=A#k.a};
+guard_expr(#k_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
+ %% Experimental support for andalso/orelse in guards.
+ %% Work out imported variables which need to be locked.
+ Mdb = vdb_sub(I, I+1, Vdb),
+ M = match(Kb, A#k.us, I+1, Mdb),
+ #l{ke={match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
+guard_expr(G, I, Vdb) -> guard(G, I, Vdb).
+
+%% expr(Kexpr, I, Vdb) -> Expr.
+
+expr(#k_call{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
+ #l{ke={call,call_op(Op),atomic_list(As),var_list(Rs)},i=I,a=A#k.a};
+expr(#k_enter{anno=A,op=Op,args=As}, I, _Vdb) ->
+ #l{ke={enter,call_op(Op),atomic_list(As)},i=I,a=A#k.a};
+expr(#k_bif{anno=A,op=Op,args=As,ret=Rs}, I, _Vdb) ->
+ Bif = k_bif(A, Op, As, Rs),
+ #l{ke=Bif,i=I,a=A#k.a};
+expr(#k_match{anno=A,body=Kb,ret=Rs}, I, Vdb) ->
+ %% Work out imported variables which need to be locked.
+ Mdb = vdb_sub(I, I+1, Vdb),
+ M = match(Kb, A#k.us, I+1, Mdb),
+ #l{ke={match,M,var_list(Rs)},i=I,vdb=use_vars(A#k.us, I+1, Mdb),a=A#k.a};
+expr(#k_try{anno=A,arg=Ka,vars=Vs,body=Kb,evars=Evs,handler=Kh,ret=Rs}, I, Vdb) ->
+ %% Lock variables that are alive before the catch and used afterwards.
+ %% Don't lock variables that are only used inside the try.
+ Tdb0 = vdb_sub(I, I+1, Vdb),
+ %% This is the tricky bit. Lock variables in Arg that are used in
+ %% the body and handler. Add try tag 'variable'.
+ Ab = get_kanno(Kb),
+ Ah = get_kanno(Kh),
+ Tdb1 = use_vars(Ab#k.us, I+3, use_vars(Ah#k.us, I+3, Tdb0)),
+ Tdb2 = vdb_sub(I, I+2, Tdb1),
+ Vnames = fun (Kvar) -> Kvar#k_var.name end, %Get the variable names
+ {Aes,_,Adb} = body(Ka, I+2, add_var({catch_tag,I+1}, I+1, 1000000, Tdb2)),
+ {Bes,_,Bdb} = body(Kb, I+4, new_vars(map(Vnames, Vs), I+3, Tdb2)),
+ {Hes,_,Hdb} = body(Kh, I+4, new_vars(map(Vnames, Evs), I+3, Tdb2)),
+ #l{ke={'try',#l{ke={block,Aes},i=I+1,vdb=Adb,a=[]},
+ var_list(Vs),#l{ke={block,Bes},i=I+3,vdb=Bdb,a=[]},
+ var_list(Evs),#l{ke={block,Hes},i=I+3,vdb=Hdb,a=[]},
+ var_list(Rs)},
+ i=I,vdb=Tdb1,a=A#k.a};
+expr(#k_catch{anno=A,body=Kb,ret=[R]}, I, Vdb) ->
+ %% Lock variables that are alive before the catch and used afterwards.
+ %% Don't lock variables that are only used inside the catch.
+ %% Add catch tag 'variable'.
+ Cdb0 = vdb_sub(I, I+1, Vdb),
+ {Es,_,Cdb1} = body(Kb, I+1, add_var({catch_tag,I}, I, 1000000, Cdb0)),
+ #l{ke={'catch',Es,variable(R)},i=I,vdb=Cdb1,a=A#k.a};
+expr(#k_receive{anno=A,var=V,body=Kb,timeout=T,action=Ka,ret=Rs}, I, Vdb) ->
+ %% Work out imported variables which need to be locked.
+ Rdb = vdb_sub(I, I+1, Vdb),
+ M = match(Kb, add_element(V#k_var.name, A#k.us), I+1,
+ new_var(V#k_var.name, I, Rdb)),
+ {Tes,_,Adb} = body(Ka, I+1, Rdb),
+ #l{ke={receive_loop,atomic_lit(T),variable(V),M,
+ #l{ke=Tes,i=I+1,vdb=Adb,a=[]},var_list(Rs)},
+ i=I,vdb=use_vars(A#k.us, I+1, Vdb),a=A#k.a};
+expr(#k_receive_accept{anno=A}, I, _Vdb) ->
+ #l{ke=receive_accept,i=I,a=A#k.a};
+expr(#k_receive_next{anno=A}, I, _Vdb) ->
+ #l{ke=receive_next,i=I,a=A#k.a};
+expr(#k_put{anno=A,arg=Arg,ret=Rs}, I, _Vdb) ->
+ #l{ke={set,var_list(Rs),literal(Arg)},i=I,a=A#k.a};
+expr(#k_break{anno=A,args=As}, I, _Vdb) ->
+ #l{ke={break,atomic_list(As)},i=I,a=A#k.a};
+expr(#k_return{anno=A,args=As}, I, _Vdb) ->
+ #l{ke={return,atomic_list(As)},i=I,a=A#k.a}.
+
+%% call_op(Op) -> Op.
+%% bif_op(Op) -> Op.
+%% test_op(Op) -> Op.
+%% Do any necessary name translations here to munge into beam format.
+
+call_op(#k_local{name=N}) -> N;
+call_op(#k_remote{mod=M,name=N}) -> {remote,atomic_lit(M),atomic_lit(N)};
+call_op(Other) -> variable(Other).
+
+bif_op(#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=N}}) -> N;
+bif_op(#k_internal{name=N}) -> N.
+
+test_op(#k_remote{mod=#k_atom{val=erlang},name=#k_atom{val=N}}) -> N.
+
+%% k_bif(Anno, Op, [Arg], [Ret]) -> Expr.
+%% Build bifs, do special handling of internal some calls.
+
+k_bif(_A, #k_internal{name=dsetelement,arity=3}, As, []) ->
+ {bif,dsetelement,atomic_list(As),[]};
+k_bif(_A, #k_internal{name=make_fun},
+ [#k_atom{val=Fun},#k_int{val=Arity},
+ #k_int{val=Index},#k_int{val=Uniq}|Free],
+ Rs) ->
+ {bif,{make_fun,Fun,Arity,Index,Uniq},var_list(Free),var_list(Rs)};
+k_bif(_A, Op, As, Rs) ->
+ %% The general case.
+ {bif,bif_op(Op),atomic_list(As),var_list(Rs)}.
+
+%% match(Kexpr, [LockVar], I, Vdb) -> Expr.
+%% Convert match tree to old format.
+
+match(#k_alt{anno=A,first=Kf,then=Kt}, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0),
+ F = match(Kf, Ls, I+1, Vdb1),
+ T = match(Kt, Ls, I+1, Vdb1),
+ #l{ke={alt,F,T},i=I,vdb=Vdb1,a=A#k.a};
+match(#k_select{anno=A,var=V,types=Kts}, Ls0, I, Vdb0) ->
+ Ls1 = add_element(V#k_var.name, Ls0),
+ Vdb1 = use_vars(union(A#k.us, Ls1), I, Vdb0),
+ Ts = map(fun (Tc) -> type_clause(Tc, Ls1, I+1, Vdb1) end, Kts),
+ #l{ke={select,literal(V),Ts},i=I,vdb=Vdb1,a=A#k.a};
+match(#k_guard{anno=A,clauses=Kcs}, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(union(A#k.us, Ls), I, Vdb0),
+ Cs = map(fun (G) -> guard_clause(G, Ls, I+1, Vdb1) end, Kcs),
+ #l{ke={guard,Cs},i=I,vdb=Vdb1,a=A#k.a};
+match(Other, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(Ls, I, Vdb0),
+ {B,_,Vdb2} = body(Other, I+1, Vdb1),
+ #l{ke={block,B},i=I,vdb=Vdb2,a=[]}.
+
+type_clause(#k_type_clause{anno=A,type=T,values=Kvs}, Ls, I, Vdb0) ->
+ %%ok = io:format("life ~w: ~p~n", [?LINE,{T,Kvs}]),
+ Vdb1 = use_vars(union(A#k.us, Ls), I+1, Vdb0),
+ Vs = map(fun (Vc) -> val_clause(Vc, Ls, I+1, Vdb1) end, Kvs),
+ #l{ke={type_clause,type(T),Vs},i=I,vdb=Vdb1,a=A#k.a}.
+
+val_clause(#k_val_clause{anno=A,val=V,body=Kb}, Ls0, I, Vdb0) ->
+ {_Used,New} = match_pat_vars(V),
+ %% Not clear yet how Used should be used.
+ Bus = (get_kanno(Kb))#k.us,
+ %%ok = io:format("Ls0 = ~p, Used=~p\n New=~p, Bus=~p\n", [Ls0,Used,New,Bus]),
+ Ls1 = union(intersection(New, Bus), Ls0), %Lock for safety
+ Vdb1 = use_vars(union(A#k.us, Ls1), I+1, new_vars(New, I, Vdb0)),
+ B = match(Kb, Ls1, I+1, Vdb1),
+ #l{ke={val_clause,literal(V),B},i=I,vdb=use_vars(Bus, I+1, Vdb1),a=A#k.a}.
+
+guard_clause(#k_guard_clause{anno=A,guard=Kg,body=Kb}, Ls, I, Vdb0) ->
+ Vdb1 = use_vars(union(A#k.us, Ls), I+2, Vdb0),
+ Gdb = vdb_sub(I+1, I+2, Vdb1),
+ G = guard(Kg, I+1, Gdb),
+ B = match(Kb, Ls, I+2, Vdb1),
+ #l{ke={guard_clause,G,B},
+ i=I,vdb=use_vars((get_kanno(Kg))#k.us, I+2, Vdb1),
+ a=A#k.a}.
+
+%% match_fail(FailValue, I, Anno) -> Expr.
+%% Generate the correct match_fail instruction. N.B. there is no
+%% generic case for when the fail value has been created elsewhere.
+
+match_fail(#k_tuple{es=[#k_atom{val=function_clause}|As]}, I, A) ->
+ #l{ke={match_fail,{function_clause,literal_list(As)}},i=I,a=A};
+match_fail(#k_tuple{es=[#k_atom{val=badmatch},Val]}, I, A) ->
+ #l{ke={match_fail,{badmatch,literal(Val)}},i=I,a=A};
+match_fail(#k_tuple{es=[#k_atom{val=case_clause},Val]}, I, A) ->
+ #l{ke={match_fail,{case_clause,literal(Val)}},i=I,a=A};
+match_fail(#k_atom{val=if_clause}, I, A) ->
+ #l{ke={match_fail,if_clause},i=I,a=A};
+match_fail(#k_tuple{es=[#k_atom{val=try_clause},Val]}, I, A) ->
+ #l{ke={match_fail,{try_clause,literal(Val)}},i=I,a=A}.
+
+%% type(Ktype) -> Type.
+
+type(k_int) -> integer;
+type(k_char) -> integer; %Hhhmmm???
+type(k_float) -> float;
+type(k_atom) -> atom;
+type(k_nil) -> nil;
+type(k_cons) -> cons;
+type(k_tuple) -> tuple;
+type(k_binary) -> binary;
+type(k_bin_seg) -> bin_seg;
+type(k_bin_end) -> bin_end.
+
+%% variable(Klit) -> Lit.
+%% var_list([Klit]) -> [Lit].
+
+variable(#k_var{name=N}) -> {var,N}.
+
+var_list(Ks) -> map(fun variable/1, Ks).
+
+%% atomic_lit(Klit) -> Lit.
+%% atomic_list([Klit]) -> [Lit].
+
+atomic_lit(#k_var{name=N}) -> {var,N};
+atomic_lit(#k_int{val=I}) -> {integer,I};
+atomic_lit(#k_float{val=F}) -> {float,F};
+atomic_lit(#k_atom{val=N}) -> {atom,N};
+%%atomic_lit(#k_char{val=C}) -> {char,C};
+%%atomic_lit(#k_string{val=S}) -> {string,S};
+atomic_lit(#k_nil{}) -> nil.
+
+atomic_list(Ks) -> map(fun atomic_lit/1, Ks).
+
+%% literal(Klit) -> Lit.
+%% literal_list([Klit]) -> [Lit].
+
+literal(#k_var{name=N}) -> {var,N};
+literal(#k_int{val=I}) -> {integer,I};
+literal(#k_float{val=F}) -> {float,F};
+literal(#k_atom{val=N}) -> {atom,N};
+%%literal(#k_char{val=C}) -> {char,C};
+literal(#k_string{val=S}) -> {string,S};
+literal(#k_nil{}) -> nil;
+literal(#k_cons{hd=H,tl=T}) ->
+ {cons,[literal(H),literal(T)]};
+literal(#k_binary{segs=V}) ->
+ case proplists:get_bool(no_new_binaries, get(?MODULE)) of
+ true ->
+ {old_binary,literal(V)};
+ false ->
+ {binary,literal(V)}
+ end;
+literal(#k_bin_seg{size=S,unit=U,type=T,flags=Fs,seg=Seg,next=N}) ->
+ {bin_seg,literal(S),U,T,Fs,[literal(Seg),literal(N)]};
+literal(#k_bin_end{}) -> bin_end;
+literal(#k_tuple{es=Es}) ->
+ {tuple,literal_list(Es)}.
+
+literal_list(Ks) -> map(fun literal/1, Ks).
+
+%% match_pat_vars(Pattern) -> {[UsedVarName],[NewVarName]}.
+
+match_pat_vars(#k_var{name=N}) -> {[],[N]};
+match_pat_vars(#k_int{}) -> {[],[]};
+match_pat_vars(#k_float{}) -> {[],[]};
+match_pat_vars(#k_atom{}) -> {[],[]};
+%%match_pat_vars(#k_char{}) -> {[],[]};
+match_pat_vars(#k_string{}) -> {[],[]};
+match_pat_vars(#k_nil{}) -> {[],[]};
+match_pat_vars(#k_cons{hd=H,tl=T}) ->
+ match_pat_list_vars([H,T]);
+match_pat_vars(#k_binary{segs=V}) ->
+ match_pat_vars(V);
+match_pat_vars(#k_bin_seg{size=S,seg=Seg,next=N}) ->
+ {U1,New1} = match_pat_vars(Seg),
+ {U2,New2} = match_pat_vars(N),
+ {[],U3} = match_pat_vars(S),
+ {union([U1,U2,U3]),union(New1, New2)};
+match_pat_vars(#k_bin_end{}) -> {[],[]};
+match_pat_vars(#k_tuple{es=Es}) ->
+ match_pat_list_vars(Es).
+
+match_pat_list_vars(Ps) ->
+ foldl(fun (P, {Used0,New0}) ->
+ {Used,New} = match_pat_vars(P),
+ {union(Used0, Used),union(New0, New)} end,
+ {[],[]}, Ps).
+
+%% new_var(VarName, I, Vdb) -> Vdb.
+%% new_vars([VarName], I, Vdb) -> Vdb.
+%% use_var(VarName, I, Vdb) -> Vdb.
+%% use_vars([VarName], I, Vdb) -> Vdb.
+%% add_var(VarName, F, L, Vdb) -> Vdb.
+
+new_var(V, I, Vdb) ->
+ case vdb_find(V, Vdb) of
+ {V,F,L} when I < F -> vdb_store(V, I, L, Vdb);
+ {V,_,_} -> Vdb;
+ error -> vdb_store(V, I, I, Vdb)
+ end.
+
+new_vars(Vs, I, Vdb0) ->
+ foldl(fun (V, Vdb) -> new_var(V, I, Vdb) end, Vdb0, Vs).
+
+use_var(V, I, Vdb) ->
+ case vdb_find(V, Vdb) of
+ {V,F,L} when I > L -> vdb_store(V, F, I, Vdb);
+ {V,_,_} -> Vdb;
+ error -> vdb_store(V, I, I, Vdb)
+ end.
+
+use_vars(Vs, I, Vdb0) ->
+ foldl(fun (V, Vdb) -> use_var(V, I, Vdb) end, Vdb0, Vs).
+
+add_var(V, F, L, Vdb) ->
+ use_var(V, L, new_var(V, F, Vdb)).
+
+vdb_find(V, Vdb) ->
+ %% Peformance note: Profiling shows that this function accounts for
+ %% a lot of the execution time when huge constants terms are built.
+ %% Using the BIF lists:keysearch/3 is a lot faster than the
+ %% original Erlang version.
+ case lists:keysearch(V, 1, Vdb) of
+ {value,Vd} -> Vd;
+ false -> error
+ end.
+
+%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V < V1 -> error;
+%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V == V1 -> Vd;
+%vdb_find(V, [{V1,F,L}=Vd|Vdb]) when V > V1 -> vdb_find(V, Vdb);
+%vdb_find(V, []) -> error.
+
+vdb_store(V, F, L, [{V1,_,_}=Vd|Vdb]) when V > V1 ->
+ [Vd|vdb_store(V, F, L, Vdb)];
+vdb_store(V, F, L, [{V1,_,_}=Vd|Vdb]) when V < V1 -> [{V,F,L},Vd|Vdb];
+vdb_store(V, F, L, [{_V1,_,_}|Vdb]) -> [{V,F,L}|Vdb]; %V == V1
+vdb_store(V, F, L, []) -> [{V,F,L}].
+
+%% vdb_sub(Min, Max, Vdb) -> Vdb.
+%% Extract variables which are used before and after Min. Lock
+%% variables alive after Max.
+
+vdb_sub(Min, Max, Vdb) ->
+ [ if L >= Max -> {V,F,1000000};
+ true -> Vd
+ end || {V,F,L}=Vd <- Vdb, F < Min, L >= Min ].
diff --git a/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl
new file mode 100644
index 0000000000..95adcfcfd8
--- /dev/null
+++ b/lib/dialyzer/test/options1_tests_SUITE_data/src/compiler/v3_life.hrl
@@ -0,0 +1,25 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: v3_life.hrl,v 1.1 2008/12/17 09:53:43 mikpe Exp $
+%%
+%% This record contains variable life-time annotation for a
+%% kernel expression. Added by v3_life, used by v3_codegen.
+
+-record(l, {ke, %Kernel expression
+ i=0, %Op number
+ vdb=[], %Variable database
+ a}). %Core annotation
+
diff --git a/lib/dialyzer/test/options2_tests_SUITE.erl b/lib/dialyzer/test/options2_tests_SUITE.erl
new file mode 100644
index 0000000000..43b5207744
--- /dev/null
+++ b/lib/dialyzer/test/options2_tests_SUITE.erl
@@ -0,0 +1,52 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(options2_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([options2_tests_SUITE_consistency/1, kernel/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, [{defines,[{vsn,4}]},{warnings,[no_return]}]}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [options2_tests_SUITE_consistency,kernel].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+options2_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+kernel(Config) ->
+ case dialyze(Config, kernel) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..5db2e50d23
--- /dev/null
+++ b/lib/dialyzer/test/options2_tests_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{defines, [{'vsn', 4}]}, {warnings, [no_return]}]}.
diff --git a/lib/dialyzer/test/options2_tests_SUITE_data/results/kernel b/lib/dialyzer/test/options2_tests_SUITE_data/results/kernel
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/options2_tests_SUITE_data/results/kernel
diff --git a/lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl b/lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl
new file mode 100644
index 0000000000..1f0e01d074
--- /dev/null
+++ b/lib/dialyzer/test/options2_tests_SUITE_data/src/kernel/global.erl
@@ -0,0 +1,1999 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: global.erl,v 1.4 2009/09/17 09:46:19 kostis Exp $
+%%
+-module(global).
+-behaviour(gen_server).
+
+%% A Global register that allows the global registration of pid's and
+%% name's, that dynamically keeps up to date with the entire network.
+%% global can operate in two modes; in a fully connected network, or
+%% in a non-fully connected network. In the latter case, the name
+%% registration mechanism won't work.
+%%
+
+%% External exports
+-export([start/0, start_link/0, stop/0, sync/0, sync/1,
+ safe_whereis_name/1, whereis_name/1, register_name/2, register_name/3,
+ register_name_external/2, register_name_external/3, unregister_name_external/1,
+ re_register_name/2, re_register_name/3,
+ unregister_name/1, registered_names/0, send/2, node_disconnected/1,
+ set_lock/1, set_lock/2, set_lock/3,
+ del_lock/1, del_lock/2,
+ trans/2, trans/3, trans/4,
+ random_exit_name/3, random_notify_name/3, notify_all_name/3, cnode/3]).
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3, timer/2, sync_init/2, init_locker/5, resolve_it/4,
+ init_the_locker/1]).
+
+-export([info/0]).
+
+
+%-define(PRINT(X), erlang:display(X)).
+-define(PRINT(X), true).
+
+%-define(P2(X), erlang:display(X)).
+%-define(P2(X), erlang:display({cs(),X})).
+-define(P2(X), true).
+
+%-define(P1(X), erlang:display(X)).
+-define(P1(X), true).
+
+%-define(P(X), erlang:display(X)).
+-define(P(X), true).
+
+%-define(FORMAT(S, A), format(S, A)).
+-define(FORMAT(S, A), ok).
+
+%%% In certain places in the server, calling io:format hangs everything,
+%%% so we'd better use erlang:display/1.
+% format(S, A) ->
+% erlang:display({format, cs(), S, A}),
+% % io:format(S, A),
+% ok.
+
+% cs() ->
+% {Big, Small, Tiny} = now(),
+% (Small rem 100) * 100 + (Tiny div 10000).
+
+%% Some notes on the internal structure:
+%% One invariant is that the list of locker processes is keyed; i.e.,
+%% there is only one process per neighboring node.
+%% When an item has been stored in the process dictionary, it is not
+%% necessarily cleared when not in use anymore. In other words, it's
+%% not an error if there is already an item there when one is to be
+%% stored.
+
+
+%% This is the protocol version
+%% Vsn 1 is the original protocol.
+%% Vsn 2 is enhanced with code to take care of registration of names from
+%% non erlang nodes, e.g. c-nodes.
+%% Vsn 3 is enhanced with a tag in the synch messages to distinguish
+%% different synch sessions from each other, see OTP-2766.
+%% Note: This requires also that the ticket OTP-2928 is fixed on the nodes
+%% running vsn 1 or 2; if such nodes will coexist with vsn 3 nodes.
+%% Vsn 4 uses a single, permanent, locker process, but works like vsn 3
+%% when communicating with vsn 3 nodes.
+
+%% -define(vsn, 4). %% Now given in options
+
+%%-----------------------------------------------------------------
+%% connect_all = boolean() - true if we are supposed to set up a
+%% fully connected net
+%% known = [Node] - all nodes known to us
+%% synced = [Node] - all nodes that have the same names as us
+%% lockers = [{Node, MyLockerPid}] - the pid of the locker
+%% process for each Node
+%% syncers = [pid()] - all current syncers processes
+%% node_name = atom() - our node name (can change if distribution
+%% is started/stopped dynamically)
+%%
+%% In addition to these, we keep info about messages arrived in
+%% the process dictionary:
+%% {pre_connect, Node} = {Vsn, InitMsg} - init_connect msgs that
+%% arrived before nodeup
+%% {wait_lock, Node} = {exchange, NameList} | lock_is_set
+%% - see comment below (handle_cast)
+%% {save_ops, Node} = [operation()] - save the ops between
+%% exchange and resolved
+%% {prot_vsn, Node} = Vsn - the exchange protocol version
+%% {sync_tag_my, Node} = My tag, used at synchronization with Node
+%% {sync_tag_his, Node} = The Node's tag, used at synchronization
+%%-----------------------------------------------------------------
+-record(state, {connect_all, known = [], synced = [],
+ lockers = [], syncers = [], node_name = node(),
+ the_locker, the_deleter}).
+
+start() -> gen_server:start({local, global_name_server}, global, [], []).
+start_link() -> gen_server:start_link({local, global_name_server},global,[],[]).
+stop() -> gen_server:call(global_name_server, stop, infinity).
+
+sync() ->
+ case check_sync_nodes() of
+ {error, Error} ->
+ {error, Error};
+ SyncNodes ->
+ gen_server:call(global_name_server, {sync, SyncNodes}, infinity)
+ end.
+sync(Nodes) ->
+ case check_sync_nodes(Nodes) of
+ {error, Error} ->
+ {error, Error};
+ SyncNodes ->
+ gen_server:call(global_name_server, {sync, SyncNodes}, infinity)
+ end.
+
+
+send(Name, Msg) ->
+ case whereis_name(Name) of
+ Pid when pid(Pid) ->
+ Pid ! Msg,
+ Pid;
+ undefined ->
+ exit({badarg, {Name, Msg}})
+ end.
+
+%% See OTP-3737. (safe_whereis_name/1 is in fact not used anywhere in OTP.)
+whereis_name(Name) ->
+ where(Name).
+
+safe_whereis_name(Name) ->
+ gen_server:call(global_name_server, {whereis, Name}, infinity).
+
+
+node_disconnected(Node) ->
+ global_name_server ! {nodedown, Node}.
+
+
+%%-----------------------------------------------------------------
+%% Method = function(Name, Pid1, Pid2) -> Pid | Pid2 | none
+%% Method is called if a name conflict is detected when two nodes
+%% are connecting to each other. It is supposed to return one of
+%% the Pids or 'none'. If a pid is returned, that pid is
+%% registered as Name on all nodes. If 'none' is returned, the
+%% Name is unregistered on all nodes. If anything else is returned,
+%% the Name is unregistered as well.
+%% Method is called once at one of the nodes where the processes reside
+%% only. If different Methods are used for the same name, it is
+%% undefined which one of them is used.
+%% Method is blocking, i.e. when it is called, no calls to whereis/
+%% send is let through until it has returned.
+%%-----------------------------------------------------------------
+register_name(Name, Pid) when pid(Pid) ->
+ register_name(Name, Pid, {global, random_exit_name}).
+register_name(Name, Pid, Method) when pid(Pid) ->
+ trans_all_known(fun(Nodes) ->
+ case where(Name) of
+ undefined ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register, Name, Pid, Method}),
+ yes;
+ _Pid -> no
+ end
+ end).
+
+unregister_name(Name) ->
+ case where(Name) of
+ undefined ->
+ ok;
+ _ ->
+ trans_all_known(fun(Nodes) ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {unregister, Name}),
+ ok
+ end)
+ end.
+
+re_register_name(Name, Pid) when pid(Pid) ->
+ re_register_name(Name, Pid, {global, random_exit_name}).
+re_register_name(Name, Pid, Method) when pid(Pid) ->
+ trans_all_known(fun(Nodes) ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register, Name, Pid, Method}),
+ yes
+ end).
+
+%% Returns all globally registered names
+registered_names() -> lists:map(fun({Name, _Pid, _Method}) -> Name end,
+ ets:tab2list(global_names)).
+
+%%-----------------------------------------------------------------
+%% An external node (i.e not an erlang node) (un)registers a name.
+%% If the registered Pid crashes the name is to be removed from global.
+%% If the external node crashes the name is to be removed from global.
+%% If the erlang node which registers the name crashes the name is also to be
+%% removed, because the registered process is not supervised any more,
+%% (i.e there is no link to the registered Pid).
+%%-----------------------------------------------------------------
+register_name_external(Name, Pid) when pid(Pid) ->
+ register_name_external(Name, Pid, {global, random_exit_name}).
+register_name_external(Name, Pid, Method) when pid(Pid) ->
+ trans_all_known(fun(Nodes) ->
+ case where(Name) of
+ undefined ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register, Name, Pid, Method}),
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {register_ext, Name, Pid, node()}),
+ yes;
+ _Pid -> no
+ end
+ end).
+
+
+
+
+unregister_name_external(Name) ->
+ case where(Name) of
+ undefined ->
+ ok;
+ _ ->
+ trans_all_known(fun(Nodes) ->
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {unregister, Name}),
+ gen_server:multi_call(Nodes,
+ global_name_server,
+ {unregister_ext, Name}),
+ ok
+ end)
+ end.
+
+
+
+
+
+%%-----------------------------------------------------------------
+%% Args: Id = id()
+%% Nodes = [node()]
+%% id() = {ResourceId, LockRequesterId}
+%% Retries = infinity | int() > 0
+%% Purpose: Sets a lock on the specified nodes (or all nodes if
+%% none are specified) on ResourceId for LockRequesterId. If there
+%% already exists a lock on ResourceId for another owner
+%% than LockRequesterId, false is returned, otherwise true.
+%% Returns: boolean()
+%%-----------------------------------------------------------------
+set_lock(Id) ->
+ set_lock(Id, [node() | nodes()], infinity, 1).
+set_lock(Id, Nodes) ->
+ set_lock(Id, Nodes, infinity, 1).
+set_lock(Id, Nodes, Retries) when Retries > 0 ->
+ set_lock(Id, Nodes, Retries, 1);
+set_lock(Id, Nodes, infinity) ->
+ set_lock(Id, Nodes, infinity, 1).
+set_lock(_Id, _Nodes, 0, _) -> false;
+set_lock({ResourceId, LockRequesterId}, Nodes, Retries, Times) ->
+ Id = {ResourceId, LockRequesterId},
+ Msg = {set_lock, Id},
+ {Replies, _} =
+ gen_server:multi_call(Nodes, global_name_server, Msg),
+ ?P2({set_lock, node(), self(), {ResourceId, LockRequesterId},
+ Nodes, Retries, Times, Replies, catch erlang:error(kaka)}),
+ ?P({set_lock, node(), ResourceId,
+ {LockRequesterId, node(LockRequesterId)}}),
+ case check_replies(Replies, Id, Nodes) of
+ true -> ?P({set_lock_true, node(), ResourceId}),
+ true;
+ false ->
+ random_sleep(Times),
+ set_lock(Id, Nodes, dec(Retries), Times+1);
+ N when integer(N) ->
+ ?P({sleeping, N}),
+ timer:sleep(N*500),
+ set_lock(Id, Nodes, Retries, Times);
+ Pid when pid(Pid) ->
+ ?P({waiting_for, Pid}),
+ Ref = erlang:monitor(process, Pid),
+ receive
+ {'DOWN', Ref, process, Pid, _Reason} ->
+ ?P({waited_for, Pid, _Reason}),
+ set_lock(Id, Nodes, Retries, Times)
+ end
+ end.
+
+check_replies([{_Node, true} | T], Id, Nodes) ->
+ check_replies(T, Id, Nodes);
+check_replies([{_Node, Status} | _T], Id, Nodes) ->
+ gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),
+ Status;
+check_replies([], _Id, _Nodes) ->
+ true.
+
+del_lock(Id) ->
+ del_lock(Id, [node() | nodes()]).
+del_lock({ResourceId, LockRequesterId}, Nodes) ->
+ Id = {ResourceId, LockRequesterId},
+ ?P2({del_lock, node(), self(), ResourceId, LockRequesterId, Nodes}),
+ gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}),
+ true.
+
+%%-----------------------------------------------------------------
+%% Args: Id = id()
+%% Fun = fun() | {M,F}
+%% Nodes = [node()]
+%% Retries = infinity | int() > 0
+%% Purpose: Sets a lock on Id (as set_lock), and evaluates
+%% Res = Fun() on success.
+%% Returns: Res | aborted (note, if Retries is infinity, the
+%% transaction won't abort)
+%%-----------------------------------------------------------------
+trans(Id, Fun) -> trans(Id, Fun, [node() | nodes()], infinity).
+trans(Id, Fun, Nodes) -> trans(Id, Fun, Nodes, infinity).
+trans(_Id, _Fun, _Nodes, 0) -> aborted;
+trans(Id, Fun, Nodes, Retries) ->
+ case set_lock(Id, Nodes, Retries) of
+ true ->
+ case catch Fun() of
+ {'EXIT', R} ->
+ del_lock(Id, Nodes),
+ exit(R);
+ Res ->
+ del_lock(Id, Nodes),
+ Res
+ end;
+ false ->
+ aborted
+ end.
+
+%%% Similar to trans(Id, Fun), but always uses global's own lock,
+%%% on all nodes known to global, making sure that no new nodes have
+%%% become known while we got the list of known nodes.
+trans_all_known(F) ->
+ Id = {global, self()},
+ Nodes = [node() | gen_server:call(global_name_server, get_known)],
+ case set_lock(Id, Nodes) of
+ true ->
+ Nodes2 = [node() | gen_server:call(global_name_server, get_known)],
+ case Nodes2 -- Nodes of
+ [] ->
+ case catch F(Nodes2) of
+ {'EXIT', R} ->
+ del_lock(Id, Nodes2),
+ exit(R);
+ Res ->
+ del_lock(Id, Nodes2),
+ Res
+ end;
+ _ ->
+ del_lock(Id, Nodes),
+ trans_all_known(F)
+ end;
+ false ->
+ aborted
+ end.
+
+info() ->
+ gen_server:call(global_name_server, info).
+
+%%%-----------------------------------------------------------------
+%%% Call-back functions from gen_server
+%%%-----------------------------------------------------------------
+init([]) ->
+ process_flag(trap_exit, true),
+ ets:new(global_locks, [set, named_table, protected]),
+ ets:new(global_names, [set, named_table, protected]),
+ ets:new(global_names_ext, [set, named_table, protected]),
+
+ %% multi
+ S = #state{the_locker = start_the_locker(self()),
+ the_deleter = start_the_deleter(self())},
+
+ case init:get_argument(connect_all) of
+ {ok, [["false"]]} ->
+ {ok, S#state{connect_all = false}};
+ _ ->
+ {ok, S#state{connect_all = true}}
+ end.
+
+%%-----------------------------------------------------------------
+%% Connection algorithm
+%% ====================
+%% This alg solves the problem with partitioned nets as well.
+%%
+%% The main idea in the alg is that when two nodes connect, they
+%% try to set a lock in their own partition (i.e. all nodes already
+%% known to them). When the lock is set in each partition, these
+%% two nodes send each other a list with all registered names in
+%% resp partition(*). If no conflict is found, the name tables are
+%% just updated. If a conflict is found, a resolve function is
+%% called once for each conflict. The result of the resolving
+%% is sent to the other node. When the names are exchanged, all
+%% other nodes in each partition are informed of the other nodes,
+%% and they ping each other to form a fully connected net.
+%%
+%% Here's the flow:
+%% Suppose nodes A and B connect, and C is connected to A.
+%%
+%% Node A
+%% ------
+%% << {nodeup, B}
+%% [spawn locker]
+%% B ! {init_connect, MyLocker}
+%% << {init_connect, MyLocker}
+%% [The lockers try to set the lock]
+%% << {lock_is_set, B}
+%% [Now, lock is set in both partitions]
+%% B ! {exchange, Names}
+%% << {exchange, Names}
+%% [solve conflict]
+%% B ! {resolved, Resolved}
+%% << {resolved, Resolved}
+%% C ! {new_nodes, Resolved, [B]}
+%%
+%% Node C
+%% ------
+%% << {new_nodes, ResolvedOps, NewNodes}
+%% [insert Ops]
+%% ping(NewNodes)
+%% << {nodeup, B}
+%% <ignore this one>
+%%
+%% Several things can disturb this picture.
+%%
+%% First, the got_names message may arrive *before* the nodeup
+%% message, due to delay in net_kernel and an optimisation in the
+%% emulator. We handle this by keeping track of these messages in the
+%% pre_connect and lockers variables in our state.
+%%
+%% The most common situation is when a new node connects to an
+%% existing net. In this case there's no need to set the lock on
+%% all nodes in the net, as we know that there won't be any conflict.
+%% This is optimised by sending {first_contact, Node} instead of got_names.
+%% This implies that first_contact may arrive before nodeup as well.
+%%
+%% Of course we must handle that some node goes down during the
+%% connection.
+%%
+%% (*) When this information is being exchanged, no one is allowed
+%% to change the global register table. All calls to register etc
+%% are protected by a lock. If a registered process dies
+%% during this phase, the deregistration is done as soon as possible
+%% on each node (i.e. when the info about the process has arrived).
+%%-----------------------------------------------------------------
+%% Messages in the protocol
+%% ========================
+%% 1. Between connecting nodes (gen_server:casts)
+%% {init_connect, Vsn, Node, InitMsg}
+%% InitMsg = {locker, LockerPid}
+%% {exchange, Node, ListOfNames}
+%% {resolved, Node, Ops, Known}
+%% Known = list of nodes in Node's partition
+%% 2. Between lockers on connecting nodes (!s)
+%% {his_locker, Pid} (from our global)
+%% lockers link to each other
+%% {lock, Bool} loop until both lockers have lock = true,
+%% then send to global {lock_is_set, Node}
+%% 3. From connecting node to other nodes in the partition
+%% {new_nodes, Node, Ops, NewNodes}
+%% 4. sync protocol
+%% {in_sync, Node, IsKnown}
+%% - sent by each node to all new nodes
+%%-----------------------------------------------------------------
+
+handle_call({whereis, Name}, From, S) ->
+ do_whereis(Name, From),
+ {noreply, S};
+
+handle_call({register, Name, Pid, Method}, _From, S) ->
+ ?P2({register, node(), Name}),
+ ins_name(Name, Pid, Method),
+ {reply, yes, S};
+
+handle_call({unregister, Name}, _From, S) ->
+ case ets:lookup(global_names, Name) of
+ [{_, Pid, _}] ->
+ ?P2({unregister, node(), Name, Pid, node(Pid)}),
+ ets:delete(global_names, Name),
+ dounlink(Pid);
+ _ -> ok
+ end,
+ {reply, ok, S};
+
+handle_call({register_ext, Name, Pid, RegNode}, _F, S) ->
+ ins_name_ext(Name, Pid, RegNode),
+ {reply, yes, S};
+
+handle_call({unregister_ext, Name}, _From, S) ->
+ ets:delete(global_names_ext, Name),
+ {reply, ok, S};
+
+
+handle_call({set_lock, Lock}, {Pid, _Tag}, S) ->
+ Reply = handle_set_lock(Lock, Pid),
+ {reply, Reply, S};
+
+handle_call({del_lock, Lock}, {Pid, _Tag}, S) ->
+ handle_del_lock(Lock, Pid),
+ {reply, true, S};
+
+handle_call(get_known, _From, S) ->
+ {reply, S#state.known, S};
+
+%% R7 may call us?
+handle_call(get_known_v2, _From, S) ->
+ {reply, S#state.known, S};
+
+handle_call({sync, Nodes}, From, S) ->
+ %% If we have several global groups, this won't work, since we will
+ %% do start_sync on a nonempty list of nodes even if the system
+ %% is quiet.
+ Pid = start_sync(lists:delete(node(), Nodes) -- S#state.synced, From),
+ {noreply, S#state{syncers = [Pid | S#state.syncers]}};
+
+handle_call(get_protocol_version, _From, S) ->
+ {reply, ?vsn, S};
+
+handle_call(get_names_ext, _From, S) ->
+ {reply, get_names_ext(), S};
+
+handle_call(info, _From, S) ->
+ {reply, S, S};
+
+handle_call(stop, _From, S) ->
+ {stop, normal, stopped, S}.
+
+
+%%=======================================================================================
+%% init_connect
+%%
+%% Vsn 1 is the original protocol.
+%% Vsn 2 is enhanced with code to take care of registration of names from
+%% non erlang nodes, e.g. c-nodes.
+%% Vsn 3 is enhanced with a tag in the synch messages to distinguish
+%% different synch sessions from each other, see OTP-2766.
+%% Note: This requires also that the ticket OTP-2928 is fixed on the nodes
+%% running vsn 1 or 2; if such nodes will coexist with vsn 3 nodes.
+%%=======================================================================================
+handle_cast({init_connect, Vsn, Node, InitMsg}, S) ->
+ ?FORMAT("~p #### init_connect Vsn ~p, Node ~p, InitMsg ~p~n",[node(), Vsn, Node, InitMsg]),
+ case Vsn of
+ %% It is always the responsibility of newer versions to understand
+ %% older versions of the protocol.
+ {HisVsn, HisTag} when HisVsn > ?vsn ->
+ init_connect(?vsn, Node, InitMsg, HisTag, S#state.lockers, S);
+ {HisVsn, HisTag} ->
+ init_connect(HisVsn, Node, InitMsg, HisTag, S#state.lockers, S);
+ %% To be future compatible
+ Tuple when tuple(Tuple) ->
+ List = tuple_to_list(Tuple),
+ [_HisVsn, HisTag | _] = List,
+ %% use own version handling if his is newer.
+ init_connect(?vsn, Node, InitMsg, HisTag, S#state.lockers, S);
+ _ when Vsn < 3 ->
+ init_connect(Vsn, Node, InitMsg, undef, S#state.lockers, S);
+ _ ->
+ Txt = io_lib:format("Illegal global protocol version ~p Node: ~p",[Vsn, Node]),
+ error_logger:info_report(lists:flatten(Txt))
+ end,
+ {noreply, S};
+
+%%=======================================================================================
+%% lock_is_set
+%%
+%% Ok, the lock is now set on both partitions. Send our names to other node.
+%%=======================================================================================
+handle_cast({lock_is_set, Node, MyTag}, S) ->
+ ?FORMAT("~p #### lock_is_set Node ~p~n",[node(), Node]),
+ Sync_tag_my = get({sync_tag_my, Node}),
+ PVsn = get({prot_vsn, Node}),
+ ?P2({lock_is_set, node(), Node, {MyTag, PVsn}, Sync_tag_my}),
+ case {MyTag, PVsn} of
+ {Sync_tag_my, undefined} ->
+ %% Patch for otp-2728, the connection to the Node is flipping up and down
+ %% the messages from the 'older' sync tries can disturb the 'new' sync try
+ %% therefor all messages are discarded if the protocol vsn is not defined.
+ Txt = io_lib:format("undefined global protocol version Node: ~p",[Node]),
+ error_logger:info_report(lists:flatten(Txt)),
+ {noreply, S};
+ {Sync_tag_my, _} ->
+ %% Check that the Node is still not known
+ case lists:member(Node, S#state.known) of
+ false ->
+ ?P2({lset, node(), Node, false}),
+ lock_is_set(Node, S#state.known),
+ {noreply, S};
+ true ->
+ ?P2({lset, node(), Node, true}),
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+ _ ->
+ ?P2({lset, illegal, node(), Node}),
+ %% Illegal tag, delete the locker.
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+
+%%=======================================================================================
+%% exchange
+%%
+%% Here the names are checked to detect name clashes.
+%%=======================================================================================
+%% Vsn 3 of the protocol
+handle_cast({exchange, Node, NameList, NameExtList, MyTag}, S) ->
+ ?FORMAT("~p #### handle_cast 3 lock_is_set exchange ~p~n",
+ [node(),{Node, NameList, NameExtList, MyTag}]),
+ Sync_tag_my = get({sync_tag_my, Node}),
+ PVsn = get({prot_vsn, Node}),
+ case {MyTag, PVsn} of
+ {Sync_tag_my, undefined} ->
+ %% Patch for otp-2728, the connection to the Node is flipping up and down
+ %% the messages from the 'older' sync tries can disturb the 'new' sync try
+ %% therefor all messages are discarded if the protocol vsn is not defined.
+ Txt = lists:flatten(io_lib:format(
+ "undefined global protocol version Node: ~p",[Node])),
+ error_logger:info_report(Txt),
+ {noreply, S};
+ {Sync_tag_my, _} ->
+ exchange(PVsn, Node, {NameList, NameExtList}, S#state.known),
+ {noreply, S};
+ _ ->
+ %% Illegal tag, delete the locker.
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+
+
+
+%%=======================================================================================
+%% resolved
+%%
+%% Here the name clashes are resolved.
+%%=======================================================================================
+%% Vsn 3 of the protocol
+handle_cast({resolved, Node, Resolved, HisKnown, _HisKnown_v2, Names_ext, MyTag}, S) ->
+ ?FORMAT("~p #### 2 resolved ~p~n",[node(),{Node, Resolved, HisKnown, Names_ext}]),
+ Sync_tag_my = get({sync_tag_my, Node}),
+ PVsn = get({prot_vsn, Node}),
+ case {MyTag, PVsn} of
+ {Sync_tag_my, undefined} ->
+ %% Patch for otp-2728, the connection to the Node is flipping up and down
+ %% the messages from the 'older' sync tries can disturb the 'new' sync try
+ %% therefor all messages are discarded if the protocol vsn is not defined.
+ Txt = lists:flatten(io_lib:format(
+ "undefined global protocol version Node: ~p",[Node])),
+ error_logger:info_report(Txt),
+ {noreply, S};
+ {Sync_tag_my, _} ->
+ NewS = resolved(Node, Resolved, {HisKnown, HisKnown}, Names_ext, S),
+ {noreply, NewS};
+ _ ->
+ %% Illegal tag, delete the locker.
+ erase({wait_lock, Node}),
+ NewS = cancel_locker(Node, S),
+ {noreply, NewS}
+ end;
+
+
+
+
+
+
+%%=======================================================================================
+%% new_nodes
+%%
+%% We get to know the other node's known nodes.
+%%=======================================================================================
+%% Vsn 2 and 3 of the protocol
+handle_cast({new_nodes, _Node, Ops, Names_ext, Nodes, _Nodes_v2}, S) ->
+ ?P2({new_nodes, node(), Nodes}),
+ ?FORMAT("~p #### 2 new_nodes ~p~n",[node(),{Ops, Names_ext, Nodes}]),
+ NewS = new_nodes(Ops, Names_ext, Nodes, S),
+ {noreply, NewS};
+
+
+
+
+%%=======================================================================================
+%% in_sync
+%%
+%% We are in sync with this node (from the other node's known world).
+%%=======================================================================================
+handle_cast({in_sync, Node, IsKnown}, S) ->
+ ?FORMAT("~p #### in_sync ~p~n",[node(),{Node, IsKnown}]),
+ lists:foreach(fun(Pid) -> Pid ! {synced, [Node]} end, S#state.syncers),
+ %% moved up:
+ NewS = cancel_locker(Node, S),
+ erase({wait_lock, Node}),
+ erase({pre_connect, Node}),
+ erase({sync_tag_my, Node}),
+ erase({sync_tag_his, Node}),
+ NKnown = case lists:member(Node, Known = NewS#state.known) of
+ false when IsKnown == true ->
+ gen_server:cast({global_name_server, Node},
+ {in_sync, node(), false}),
+ [Node | Known];
+ _ ->
+ Known
+ end,
+ NSynced = case lists:member(Node, Synced = NewS#state.synced) of
+ true -> Synced;
+ false -> [Node | Synced]
+ end,
+ {noreply, NewS#state{known = NKnown, synced = NSynced}};
+
+
+
+
+%% Called when Pid on other node crashed
+handle_cast({async_del_name, Name, Pid}, S) ->
+ ?P2({async_del_name, node(), Name, Pid, node(Pid)}),
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid, _}] ->
+ ets:delete(global_names, Name),
+ dounlink(Pid);
+ _ -> ok
+ end,
+ ets:delete(global_names_ext, Name),
+ {noreply, S};
+
+handle_cast({async_del_lock, _ResourceId, Pid}, S) ->
+ del_locks2(ets:tab2list(global_locks), Pid),
+% ets:match_delete(global_locks, {ResourceId, '_', Pid}),
+ {noreply, S}.
+
+
+handle_info({'EXIT', Deleter, _Reason}=Exit, #state{the_deleter=Deleter}=S) ->
+ {stop, {deleter_died,Exit}, S#state{the_deleter=undefined}};
+handle_info({'EXIT', Pid, _Reason}, #state{the_deleter=Deleter}=S)
+ when pid(Pid) ->
+ ?P2({global, exit, node(), Pid, node(Pid)}),
+ check_exit(Deleter, Pid),
+ Syncers = lists:delete(Pid, S#state.syncers),
+ Lockers = lists:keydelete(Pid, 2, S#state.lockers),
+ ?PRINT({exit, Pid, lockers, node(), S#state.lockers}),
+ {noreply, S#state{syncers = Syncers, lockers = Lockers}};
+
+handle_info({nodedown, Node}, S) when Node == S#state.node_name ->
+ %% Somebody stopped the distribution dynamically - change
+ %% references to old node name (Node) to new node name ('nonode@nohost')
+ {noreply, change_our_node_name(node(), S)};
+
+handle_info({nodedown, Node}, S) ->
+ ?FORMAT("~p #### nodedown 1 ####### Node ~p",[node(),Node]),
+ %% moved up:
+ do_node_down(Node),
+ #state{known = Known, synced = Syncs} = S,
+ NewS = cancel_locker(Node, S),
+
+ erase({wait_lock, Node}),
+ erase({save_ops, Node}),
+ erase({pre_connect, Node}),
+ erase({prot_vsn, Node}),
+ erase({sync_tag_my, Node}),
+ erase({sync_tag_his, Node}),
+ {noreply, NewS#state{known = lists:delete(Node, Known),
+ synced = lists:delete(Node, Syncs)}};
+
+
+
+handle_info({nodeup, Node}, S) when Node == node() ->
+ ?FORMAT("~p #### nodeup S ####### Node ~p~n",[node(), Node]),
+ %% Somebody started the distribution dynamically - change
+ %% references to old node name ('nonode@nohost') to Node.
+ {noreply, change_our_node_name(Node, S)};
+
+handle_info({nodeup, Node}, S) when S#state.connect_all == true ->
+ ?FORMAT("~p #### nodeup 1 ####### Node ~p",[node(),Node]),
+ IsKnown = lists:member(Node, S#state.known) or
+ %% This one is only for double nodeups (shouldn't occur!)
+ lists:keymember(Node, 1, S#state.lockers),
+ case IsKnown of
+ true ->
+ {noreply, S};
+ false ->
+ %% now() is used as a tag to separate different sycnh sessions
+ %% from each others. Global could be confused at bursty nodeups
+ %% because it couldn't separate the messages between the different
+ %% synch sessions started by a nodeup.
+ MyTag = now(),
+ resend_pre_connect(Node),
+
+ %% multi
+ S#state.the_locker ! {nodeup, Node, S#state.known, MyTag, self()},
+
+ Pid = start_locker(Node, S#state.known, MyTag, self(), S#state.the_locker),
+ Ls = S#state.lockers,
+ InitC = {init_connect, {?vsn, MyTag}, node(), {locker, Pid, S#state.known}},
+ ?P2({putting, MyTag}),
+ put({sync_tag_my, Node}, MyTag),
+ gen_server:cast({global_name_server, Node}, InitC),
+ {noreply, S#state{lockers = [{Node, Pid} | Ls]}}
+ end;
+
+
+%% This message is only to test otp-2766 Global may be confused at bursty
+%% nodeup/nodedowns. It's a copy of the complex part of the handling of
+%% the 'nodeup' message.
+handle_info({test_vsn_tag_nodeup, Node}, S) when S#state.connect_all == true,
+ Node == node() ->
+ {noreply, S};
+handle_info({test_vsn_tag_nodeup, Node}, S) when S#state.connect_all == true ->
+ ?FORMAT("~p #### test_nodeup 1 ####### Node ~p~n",[node(), Node]),
+ MyTag = now(),
+ resend_pre_connect(Node),
+ S#state.the_locker ! {nodeup, Node, S#state.known, MyTag, self()},
+ Pid = start_locker(Node, S#state.known, MyTag, self(), S#state.the_locker),
+ Ls = S#state.lockers,
+ InitC = {init_connect, {?vsn, MyTag}, node(), {locker, Pid, S#state.known}},
+ put({sync_tag_my, Node}, MyTag),
+ gen_server:cast({global_name_server, Node}, InitC),
+ ?PRINT({lockers, node(), Ls}),
+ {noreply, S#state{lockers = [{Node, Pid} | Ls]}};
+
+
+handle_info({whereis, Name, From}, S) ->
+ do_whereis(Name, From),
+ {noreply, S};
+
+handle_info(known, S) ->
+ io:format(">>>> ~p~n",[S#state.known]),
+ {noreply, S};
+
+handle_info(_, S) ->
+ {noreply, S}.
+
+
+
+
+%%=======================================================================================
+%%=======================================================================================
+%%=============================== Internal Functions ====================================
+%%=======================================================================================
+%%=======================================================================================
+
+
+
+%%=======================================================================================
+%% Another node wants to synchronize its registered names with us.
+%% Start a locker process. Both nodes must have a lock before they are
+%% allowed to continue.
+%%=======================================================================================
+init_connect(Vsn, Node, InitMsg, HisTag, Lockers, S) ->
+ ?P2({init_connect, node(), Node}),
+ ?FORMAT("~p #### init_connect Vsn, Node, InitMsg ~p~n",[node(),{Vsn, Node, InitMsg}]),
+ %% It is always the responsibility of newer versions to understand
+ %% older versions of the protocol.
+ put({prot_vsn, Node}, Vsn),
+ put({sync_tag_his, Node}, HisTag),
+ if
+ Vsn =< 3 ->
+ case lists:keysearch(Node, 1, Lockers) of
+ {value, {_Node, MyLocker}} ->
+ %% We both have lockers; let them set the lock
+ case InitMsg of
+ {locker, HisLocker, HisKnown} -> %% current version
+ ?PRINT({init_connect1, node(), self(), Node,
+ MyLocker, HisLocker}),
+ MyLocker ! {his_locker, HisLocker, HisKnown};
+
+ {locker, _HisLocker, HisKnown, HisTheLocker} -> %% multi
+ ?PRINT({init_connect1, node(), self(), Node,
+ MyLocker, _HisLocker}),
+ S#state.the_locker ! {his_the_locker, HisTheLocker,
+ HisKnown, S#state.known}
+ end;
+ false ->
+ ?PRINT({init_connect11, node(), self(), Node}),
+ put({pre_connect, Node}, {Vsn, InitMsg, HisTag})
+ end;
+ true -> % Vsn > 3
+ ?P2(vsn4),
+ case lists:keysearch(Node, 1, Lockers) of
+ {value, {_Node, _MyLocker}} ->
+ %% We both have lockers; let them set the lock
+ case InitMsg of
+ {locker, HisLocker, HisKnown} -> %% current version
+ ?PRINT({init_connect1, node(), self(), Node,
+ _MyLocker, HisLocker}),
+ HisLocker ! {his_locker_new, S#state.the_locker,
+ {HisKnown, S#state.known}};
+
+ {locker, _HisLocker, HisKnown, HisTheLocker} -> %% multi
+ ?PRINT({init_connect1, node(), self(), Node,
+ _MyLocker, _HisLocker}),
+ S#state.the_locker ! {his_the_locker, HisTheLocker,
+ HisKnown, S#state.known}
+ end;
+ false ->
+ ?PRINT({init_connect11, node(), self(), Node}),
+ put({pre_connect, Node}, {Vsn, InitMsg, HisTag})
+ end
+ end.
+
+
+
+%%=======================================================================================
+%% In the simple case, we'll get lock_is_set before we get exchange,
+%% but we may get exchange before we get lock_is_set from our locker.
+%% If that's the case, we'll have to remember the exchange info, and
+%% handle it when we get the lock_is_set. We do this by using the
+%% process dictionary - when the lock_is_set msg is received, we store
+%% this info. When exchange is received, we can check the dictionary
+%% if the lock_is_set has been received. If not, we store info about
+%% the exchange instead. In the lock_is_set we must first check if
+%% exchange info is stored, in that case we take care of it.
+%%=======================================================================================
+lock_is_set(Node, Known) ->
+ ?FORMAT("~p #### lock_is_set ~p~n",[node(),{Node, Node, Known}]),
+ PVsn = get({prot_vsn, Node}),
+ case PVsn of
+ _ -> % 3 and higher
+ gen_server:cast({global_name_server, Node},
+ {exchange, node(), get_names(), get_names_ext(),
+ get({sync_tag_his, Node})})
+ end,
+ %% If both have the lock, continue with exchange
+ case get({wait_lock, Node}) of
+ {exchange, NameList, NameExtList} ->
+ %% vsn 2, 3
+ put({wait_lock, Node}, lock_is_set),
+ exchange(PVsn, Node, {NameList, NameExtList}, Known);
+ undefined ->
+ put({wait_lock, Node}, lock_is_set)
+ end.
+
+
+
+%%=======================================================================================
+%% exchange
+%%=======================================================================================
+%% Vsn 3 and higher of the protocol
+exchange(_Vsn, Node, {NameList, NameExtList}, Known) ->
+ ?FORMAT("~p #### 3 lock_is_set exchange ~p~n",[node(),{Node, NameList, NameExtList}]),
+ case erase({wait_lock, Node}) of
+ lock_is_set ->
+ {Ops, Resolved} = exchange_names(NameList, Node, [], []),
+ put({save_ops, Node}, Ops),
+ gen_server:cast({global_name_server, Node},
+ {resolved, node(), Resolved, Known,
+ Known, get_names_ext(), get({sync_tag_his, Node})});
+ undefined ->
+ put({wait_lock, Node}, {exchange, NameList, NameExtList})
+ end.
+
+
+
+
+
+resolved(Node, Resolved, {HisKnown, _HisKnown_v2}, Names_ext, S) ->
+ ?P2({resolved, node(), Node, S#state.known}),
+ ?FORMAT("~p #### 2 resolved ~p~n",[node(),{Node, Resolved, HisKnown, Names_ext}]),
+ erase({prot_vsn, Node}),
+ Ops = erase({save_ops, Node}) ++ Resolved,
+ Known = S#state.known,
+ Synced = S#state.synced,
+ NewNodes = [Node | HisKnown],
+ do_ops(Ops),
+ do_ops_ext(Ops,Names_ext),
+ gen_server:abcast(Known, global_name_server,
+ {new_nodes, node(), Ops, Names_ext, NewNodes, NewNodes}),
+ %% I am synced with Node, but not with HisKnown yet
+ lists:foreach(fun(Pid) -> Pid ! {synced, [Node]} end, S#state.syncers),
+ gen_server:abcast(HisKnown, global_name_server, {in_sync, node(), true}),
+ NewS = lists:foldl(fun(Node1, S1) -> cancel_locker(Node1, S1) end,
+ S,
+ NewNodes),
+ %% See (*) below... we're node b in that description
+ NewKnown = Known ++ (NewNodes -- Known),
+ NewS#state{known = NewKnown, synced = [Node | Synced]}.
+
+
+
+
+new_nodes(Ops, Names_ext, Nodes, S) ->
+ ?FORMAT("~p #### 2 new_nodes ~p~n",[node(),{Ops, Names_ext, Nodes}]),
+ do_ops(Ops),
+ do_ops_ext(Ops,Names_ext),
+ Known = S#state.known,
+ %% (*) This one requires some thought...
+ %% We're node a, other nodes b and c:
+ %% The problem is that {in_sync, a} may arrive before {resolved, [a]} to
+ %% b from c, leading to b sending {new_nodes, [a]} to us (node a).
+ %% Therefore, we make sure we never get duplicates in Known.
+ NewNodes = lists:delete(node(), Nodes -- Known),
+ gen_server:abcast(NewNodes, global_name_server, {in_sync, node(), true}),
+ S#state{known = Known ++ NewNodes}.
+
+
+
+
+
+do_whereis(Name, From) ->
+ case is_lock_set(global) of
+ false ->
+ gen_server:reply(From, where(Name));
+ true ->
+ send_again({whereis, Name, From})
+ end.
+
+terminate(_Reason, _S) ->
+ ets:delete(global_names),
+ ets:delete(global_names_ext),
+ ets:delete(global_locks).
+
+code_change(_OldVsn, S, _Extra) ->
+ {ok, S}.
+
+%% Resend init_connect to ourselves.
+resend_pre_connect(Node) ->
+ case erase({pre_connect, Node}) of
+% {Vsn, InitMsg, undef} ->
+% %% Vsn 1 & 2
+% ?PRINT({resend_pre_connect2, node(), self(), Node}),
+% gen_server:cast(self(), {init_connect, Vsn, Node, InitMsg});
+ {Vsn, InitMsg, HisTag} ->
+ %% Vsn 3
+ ?PRINT({resend_pre_connect3, node(), self(), Node}),
+ gen_server:cast(self(), {init_connect, {Vsn, HisTag}, Node, InitMsg});
+ _ ->
+ ?PRINT({resend_pre_connect0, node(), self(), Node}),
+ ok
+ end.
+
+ins_name(Name, Pid, Method) ->
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid2, _}] ->
+ dounlink(Pid2);
+ [] ->
+ ok
+ end,
+ dolink(Pid),
+ ets:insert(global_names, {Name, Pid, Method}).
+
+ins_name_ext(Name, Pid, RegNode) ->
+ case ets:lookup(global_names_ext, Name) of
+ [{Name, Pid2, _}] ->
+ dounlink(Pid2);
+ [] ->
+ ok
+ end,
+ dolink_ext(Pid, RegNode),
+ ets:insert(global_names_ext, {Name, Pid, RegNode}).
+
+where(Name) ->
+ case ets:lookup(global_names, Name) of
+ [{_, Pid, _}] -> Pid;
+ [] -> undefined
+ end.
+
+handle_set_lock({ResourceId, LockRequesterId}, Pid) ->
+ case ets:lookup(global_locks, ResourceId) of
+ [{ResourceId, LockRequesterId, Pids}] ->
+ case lists:member(Pid, Pids) of
+ true ->
+ true;
+ false ->
+ dolink(Pid),
+ ets:insert(global_locks, {ResourceId, LockRequesterId, [Pid | Pids]}),
+ true
+ end;
+ [{ResourceId, _LockRequesterId2, _Pid2}] ->
+ case ResourceId of
+ global ->
+ ?P({before,
+ LockRequesterId,
+ _LockRequesterId2,
+ S#state.lockers}),
+ false;
+ _ ->
+ false
+ end;
+ [] ->
+ dolink(Pid),
+ ets:insert(global_locks, {ResourceId, LockRequesterId, [Pid]}),
+ true
+ end.
+
+is_lock_set(ResourceId) ->
+ case ets:lookup(global_locks, ResourceId) of
+ [_Lock] -> true;
+ [] -> false
+ end.
+
+handle_del_lock({ResourceId, LockRequesterId}, Pid) ->
+ case ets:lookup(global_locks, ResourceId) of
+ [{ResourceId, LockRequesterId, Pids}] when [Pid] == Pids ->
+ ets:delete(global_locks, ResourceId),
+ dounlink(Pid);
+ [{ResourceId, LockRequesterId, Pids}] ->
+ NewPids = lists:delete(Pid, Pids),
+ ets:insert(global_locks, {ResourceId, LockRequesterId, NewPids}),
+ dounlink(Pid);
+ _ -> ok
+ end.
+
+do_ops(Ops) ->
+ lists:foreach(fun({insert, Item}) -> ets:insert(global_names, Item);
+ ({delete, Name}) ->
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid, _}] ->
+ ?P2({do_ops_delete, node(), Name, Pid, node(Pid)}),
+ ets:delete(global_names, Name),
+ dounlink(Pid);
+ [] ->
+ ok
+ end
+ end, Ops).
+
+%% If a new name, then it must be checked if it is an external name
+%% If delete a name it is always deleted from global_names_ext
+do_ops_ext(Ops, Names_ext) ->
+ lists:foreach(fun({insert, {Name, Pid, _Method}}) ->
+ case lists:keysearch(Name, 1, Names_ext) of
+ {value, {Name, Pid, RegNode}} ->
+ ets:insert(global_names_ext, {Name, Pid, RegNode});
+ _ ->
+ ok
+ end;
+ ({delete, Name}) ->
+ ets:delete(global_names_ext, Name)
+ end, Ops).
+
+%%-----------------------------------------------------------------
+%% A locker is a process spawned by global_name_server when a
+%% nodeup is received from a new node. Its purpose is to try to
+%% set a lock in our partition, i.e. on all nodes known to us.
+%% When the lock is set, it tells global about it, and keeps
+%% the lock set. global sends a cancel message to the locker when
+%% the partitions are connected.
+
+%% Versions: at version 2, the messages exchanged between the lockers
+%% include the known nodes (see OTP-3576). There is no way of knowing
+%% the version number of the other side's locker when sending a message
+%% to it, so we send both version 1 and 2, and flush the version 1 if
+%% we receive version 2.
+%%
+%% Due to a mistake, an intermediate version of the new locking protocol
+%% (using 3-tuples) went out in R7, which only understands itself. This patch
+%% to R7 handles all kinds, which means sending all, and flush the ones we
+%% don't want. (It will remain difficult to make a future version of the
+%% protocol communicate with this one.)
+%%
+%%-----------------------------------------------------------------
+%% (Version 2 in patched R7. No named version in R6 and older - let's call that
+%% version 1.)
+-define(locker_vsn, 2).
+
+%%% multi
+
+-record(multi, {known, others = []}).
+
+start_the_locker(Global) ->
+ spawn_link(?MODULE, init_the_locker, [Global]).
+
+%init_the_locker(Global) ->
+% ok;
+init_the_locker(Global) ->
+ process_flag(trap_exit, true), %needed?
+ loop_the_locker(Global, #multi{}),
+ erlang:error(locker_exited).
+
+remove_node(_Node, []) ->
+ [];
+remove_node(Node, [{Node, _HisTheLocker, _HisKnown, _MyTag} | Rest]) ->
+ Rest;
+remove_node(Node, [E | Rest]) ->
+ [E | remove_node(Node, Rest)].
+
+find_node_tag(_Node, []) ->
+ false;
+find_node_tag(Node, [{Node, _HisTheLocker, _HisKnown, MyTag} | _Rest]) ->
+ {true, MyTag};
+find_node_tag(Node, [_E | Rest]) ->
+ find_node_tag(Node, Rest).
+
+loop_the_locker(Global, S) ->
+ ?P2({others, node(), S#multi.others}),
+% Known = S#multi.known,
+ Timeout = case S#multi.others of
+ [] ->
+ infinity;
+ _ ->
+ 0
+ end,
+ receive
+% {nodeup, Node, Known, Tag, P} ->
+% ?P2({the_locker, nodeup, time(), node(), nodeup, Node, Tag}),
+% loop_the_locker(Global, S);
+ {his_the_locker, HisTheLocker, HisKnown, MyKnown} ->
+ ?P2({his_the_locker, time(), node(), HisTheLocker,
+ node(HisTheLocker)}),
+ receive
+ {nodeup, Node, _Known, MyTag, _P} when node(HisTheLocker) == Node ->
+ ?P2({the_locker, nodeup, node(), Node,
+ node(HisTheLocker), MyTag,
+ process_info(self(), messages)}),
+ Others = S#multi.others,
+ loop_the_locker(Global,
+ S#multi{known=MyKnown,
+ others=[{node(HisTheLocker), HisTheLocker, HisKnown, MyTag} | Others]});
+ {cancel, Node, _Tag} when node(HisTheLocker) == Node ->
+ loop_the_locker(Global, S)
+ after 60000 ->
+ ?P2({nodeupnevercame, node(), node(HisTheLocker)}),
+ error_logger:error_msg("global: nodeup never came ~w ~w~n",
+ [node(), node(HisTheLocker)]),
+ loop_the_locker(Global, S)
+ end;
+ {cancel, Node, undefined} ->
+ ?P2({the_locker, cancel1, undefined, node(), Node}),
+%% If we actually cancel something when a cancel message with the tag
+%% 'undefined' arrives, we may be acting on an old nodedown, to cancel
+%% a new nodeup, so we can't do that.
+% receive
+% {nodeup, Node, _Known, _MyTag, _P} ->
+% ?P2({the_locker, cancelnodeup1, node(), Node}),
+% ok
+% after 0 ->
+% ok
+% end,
+% Others = remove_node(Node, S#multi.others),
+% loop_the_locker(Global, S#multi{others = Others});
+ loop_the_locker(Global, S);
+ {cancel, Node, Tag} ->
+ ?P2({the_locker, cancel1, Tag, node(), Node}),
+ receive
+ {nodeup, Node, _Known, Tag, _P} ->
+ ?P2({the_locker, cancelnodeup2, node(), Node}),
+ ok
+ after 0 ->
+ ok
+ end,
+ Others = remove_node(Node, S#multi.others),
+ loop_the_locker(Global, S#multi{others = Others});
+ {lock_set, _Pid, false, _} ->
+ ?P2({the_locker, spurious, node(), node(_Pid)}),
+ loop_the_locker(Global, S);
+ {lock_set, Pid, true, HisKnown} ->
+ Node = node(Pid),
+ ?P2({the_locker, spontaneous, node(), Node}),
+
+ NewKnown = gen_server:call(global_name_server, get_known),
+
+ Others =
+ case find_node_tag(Node, S#multi.others) of
+ {true, MyTag} ->
+
+ BothsKnown = HisKnown -- (HisKnown -- NewKnown),
+ Known1 = if
+ node() < Node ->
+ [node() | NewKnown];
+ true ->
+ [node() | NewKnown] -- BothsKnown
+ end,
+
+ ?P2({lock1, node()}),
+ LockId = {global, self()},
+ IsLockSet = set_lock(LockId, Known1, 1),
+ Pid ! {lock_set, self(), IsLockSet, NewKnown},
+ ?P2({the_locker, spontaneous, node(), Node, IsLockSet}),
+ case IsLockSet of
+ true ->
+ gen_server:cast(global_name_server,
+ {lock_is_set, Node, MyTag}),
+ ?P1({lock_sync_done, time(), node(),
+ {Pid, node(Pid)}, self()}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ {cancel, Node, _Tag} ->
+ %% All conflicts are resolved,
+ %% remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known1);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died;
+ %% remove lock and ignore him.
+ del_lock(LockId, Known1),
+ link(Global)
+ end,
+ remove_node(Node, S#multi.others);
+ false ->
+ S#multi.others
+ end;
+ false ->
+ ?P2({the_locker, spontaneous, node(), Node, not_there}),
+ Pid ! {lock_set, self(), false, NewKnown},
+ S#multi.others
+ end,
+ loop_the_locker(Global, S#multi{others = Others});
+ Other when element(1, Other) /= nodeup ->
+ ?P2({the_locker, other_msg, Other}),
+ loop_the_locker(Global, S)
+ after Timeout ->
+ NewKnown = gen_server:call(global_name_server, get_known),
+ [{Node, HisTheLocker, HisKnown, MyTag} | Rest] = S#multi.others,
+ BothsKnown = HisKnown -- (HisKnown -- NewKnown),
+ Known1 = if
+ node() < Node ->
+ [node() | NewKnown];
+ true ->
+ [node() | NewKnown] -- BothsKnown
+ end,
+ ?P2({picking, node(), Node}),
+ case lists:member(Node, NewKnown) of
+ false ->
+ LockId = {global, self()},
+ ?P2({lock2, node()}),
+ IsLockSet = set_lock(LockId, Known1, 1),
+ Others =
+ case IsLockSet of
+ true ->
+ HisTheLocker ! {lock_set, self(),
+ IsLockSet, NewKnown},
+ %% OTP-4902
+ lock_set_loop(Global, S,
+ Node, MyTag, Rest,
+ Known1,
+ LockId);
+ false ->
+ ?P2({the_locker, not_locked, node(),
+ Node}),
+ S#multi.others
+ end,
+ loop_the_locker(Global, S#multi{known=NewKnown,
+ others = Others});
+ true ->
+ ?P2({is_known, node(), Node}),
+ loop_the_locker(Global, S#multi{known=NewKnown,
+ others = Rest})
+ end
+ end.
+
+lock_set_loop(Global, S, Node, MyTag, Rest, Known1, LockId) ->
+ receive
+ {lock_set, P, true, _} when node(P) == Node ->
+ ?P2({the_locker, both_set, node(), Node}),
+
+ %% do sync
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ ?P1({lock_sync_done, time(), node(), {Pid, node(Pid)}, self()}),
+
+ %% Wait for global to tell us to remove lock.
+ receive
+ {cancel, Node, _} ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known1);
+ {'EXIT', _Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known1),
+ link(Global)
+ end,
+ Rest;
+ {lock_set, P, false, _} when node(P) == Node ->
+ ?P2({the_locker, not_both_set, node(), Node}),
+ del_lock(LockId, Known1),
+ S#multi.others;
+ {cancel, Node, _} ->
+ ?P2({the_locker, cancel2, node(), Node}),
+ del_lock(LockId, Known1),
+ remove_node(Node, S#multi.others);
+ {'EXIT', _, _} ->
+ ?P2({the_locker, exit, node(), Node}),
+ del_lock(LockId, Known1),
+ S#multi.others
+
+ after
+ %% OTP-4902
+ %% A cyclic deadlock could occur in rare cases where three or
+ %% more nodes waited for a reply from each other.
+ %% Therefore, reject lock_set attempts in this state from
+ %% nodes < this node (its enough if at least one node in
+ %% the cycle rejects and thus breaks the deadlock)
+ 5000 ->
+ reject_lock_set(),
+ lock_set_loop(Global, S, Node, MyTag, Rest, Known1, LockId)
+ end.
+
+reject_lock_set() ->
+ receive
+ {lock_set, P, true, _} when node(P) < node() ->
+ P ! {lock_set, self(), false, []},
+ reject_lock_set()
+ after
+ 0 ->
+ true
+ end.
+
+start_locker(Node, Known, MyTag, Global, TheLocker) ->
+ %% No link here! The del_lock call would delete the link anyway.
+ %% global_name_server has control of these processes anyway...
+ %% When the locker process exits due to being sent the 'cancel' message
+ %% by the server, the server then removes it from its tables.
+ %% When the locker terminates due to other reasons, the server must
+ %% be told, so we make a link to it just before exiting.
+ spawn(?MODULE, init_locker, [Node, Known, MyTag, Global, TheLocker]).
+
+init_locker(Node, Known, MyTag, Global, TheLocker) ->
+ process_flag(trap_exit, true),
+ ?PRINT({init_locker, node(), self(), Node}),
+ ?P1({init_locker, time(), node(), self(), Node}),
+ receive
+ {his_locker, Pid, HisKnown} ->
+ ?PRINT({init_locker, node(), self(), his_locker, Node}),
+ link(Pid),
+ %% If two nodes in a group of nodes first disconnect
+ %% and then reconnect, this causes global to deadlock.
+ %% This because both of the reconnecting nodes
+ %% tries to set lock on the other nodes in the group.
+ %% This is solved by letting only one of the reconneting nodes set the lock.
+ BothsKnown = HisKnown -- (HisKnown -- Known),
+ ?P({loop_locker1, node(), {Pid, node(Pid)}}),
+ Res = loop_locker(Node, Pid, Known, 1, MyTag, BothsKnown, Global),
+ ?P({loop_locker2, node(), {Pid, node(Pid)}}),
+ Res;
+ {his_locker_new, HisTheLocker, {Known1, Known2}} ->
+ %% slide into the vsn 4 stuff
+ ?P2({his_locker_new, node()}),
+ HisTheLocker ! {his_the_locker, TheLocker, Known1, Known2},
+ exit(normal);
+ cancel ->
+ ?PRINT({init_locker, node(), self(), cancel, Node}),
+ exit(normal)
+ end.
+
+loop_locker(Node, Pid, Known0, Try, MyTag, BothsKnown, Global) ->
+ Known = if
+ node() < Node ->
+ [node() | Known0];
+ true ->
+ [node() | Known0] -- BothsKnown
+ end,
+
+ ?PRINT({locking, node(), self(), Known}),
+ LockId = {global, self()},
+ ?P2({lock3, node()}),
+ IsLockSet = set_lock(LockId, Known, 1),
+ ?P({loop_locker, IsLockSet,
+ node(), {Pid, node(Pid)}, self(), Try}),
+ ?P1({loop_locker, time(), IsLockSet,
+ node(), {Pid, node(Pid)}, self(), Try}),
+ ?PRINT({locking1, node(), self(), Known, IsLockSet}),
+ %% Tell other node that we managed to get the lock.
+ Pid ! {lock, ?locker_vsn, IsLockSet, Known},
+ Pid ! {lock, IsLockSet, Known},
+ Pid ! {lock, IsLockSet},
+ %% Wait for other node's result.
+ receive
+ %% R7 patched and later
+ {lock, _LockerVsn, true, _} when IsLockSet == true ->
+ receive
+ {lock, _} ->
+ ok
+ end,
+ receive
+ {lock, _, _} ->
+ ok
+ end,
+ ?PRINT({node(), self(), locked}),
+ %% Now we got the lock in both partitions. Tell
+ %% global, and let him resolve name conflict.
+ ?P1({lock_sync, time(), node(), {Pid, node(Pid)}, self()}),
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ ?P1({lock_sync_done, time(), node(), {Pid, node(Pid)}, self()}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ cancel ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known),
+ link(Global)
+ end;
+ {lock, _LockerVsn, _, HisKnown} ->
+ receive
+ {lock, _} ->
+ ok
+ end,
+ receive
+ {lock, _, _} ->
+ ok
+ end,
+ %% Some of us failed to get the lock; try again
+ ?PRINT({node(), self(), locked0}),
+ d_lock(IsLockSet, LockId, Known),
+ try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global);
+ %% R7 unpatched
+ {lock, true, _} when IsLockSet == true ->
+ ?PRINT({node(), self(), locked}),
+ %% Now we got the lock in both partitions. Tell
+ %% global, and let him resolve name conflict.
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ cancel ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known),
+ link(Global)
+ end;
+ {lock, _, HisKnown} ->
+ %% Some of us failed to get the lock; try again
+ ?PRINT({node(), self(), locked0}),
+ d_lock(IsLockSet, LockId, Known),
+ try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global);
+ %% R6 and earlier
+ {lock, true} when IsLockSet == true ->
+ ?PRINT({node(), self(), locked}),
+ %% Now we got the lock in both partitions. Tell
+ %% global, and let him resolve name conflict.
+ gen_server:cast(global_name_server, {lock_is_set, Node, MyTag}),
+ %% Wait for global to tell us to remove lock.
+ receive
+ cancel ->
+ %% All conflicts are resolved, remove lock.
+ ?PRINT({node(), self(), locked1}),
+ del_lock(LockId, Known);
+ {'EXIT', Pid, _} ->
+ ?PRINT({node(), self(), locked2}),
+ %% Other node died; remove lock and ignore him.
+ del_lock(LockId, Known),
+ link(Global)
+ end;
+ {lock, _} ->
+ %% Some of us failed to get the lock; try again
+ ?PRINT({node(), self(), locked0}),
+ d_lock(IsLockSet, LockId, Known),
+ try_again_locker(Node, Pid, Try, MyTag, BothsKnown, Global);
+ {'EXIT', Pid, _} ->
+ %% Other node died; remove lock and ignore him.
+ ?PRINT({node(), self(), locked7}),
+ d_lock(IsLockSet, LockId, Known),
+ link(Global);
+ cancel ->
+ ?PRINT({node(), self(), locked8}),
+ d_lock(IsLockSet, LockId, Known)
+ end.
+
+d_lock(true, LockId, Known) -> del_lock(LockId, Known);
+d_lock(false, _, _) -> ok.
+
+try_again_locker(Node, Pid, Try, MyTag, HisKnown, Global) ->
+ ?PRINT({try_again, node(), self(), Node, Pid, Known, Try, MyTag}),
+ ?P1({try_again, time(), node(), self(), Node, Pid, Known, Try, MyTag}),
+ random_sleep(Try),
+ ?P1({try_again2, time(), node(), self(), Node, Pid, Known, Try, MyTag}),
+ NewKnown = gen_server:call(global_name_server, get_known),
+ case lists:member(Node, NewKnown) of
+ false ->
+ BothsKnown1 = HisKnown -- (HisKnown -- NewKnown),
+ ?PRINT({node(), self(), Node, again, notknown}),
+ ?PRINT({bothknown, BothsKnown, BothsKnown1}),
+ loop_locker(Node, Pid, NewKnown, Try+1, MyTag,
+ BothsKnown1, Global);
+ true ->
+ ?PRINT({node(), self(), Node, again, known}),
+ link(Global),
+ %% Node is already handled, we are ready.
+ ok
+ end.
+
+cancel_locker(Node, S) ->
+ %% multi
+ ?P2({cancel, node(), Node, get({sync_tag_my, Node})}),
+ S#state.the_locker ! {cancel, Node, get({sync_tag_my, Node})},
+
+ Lockers = S#state.lockers,
+ case lists:keysearch(Node, 1, Lockers) of
+ {value, {_, Pid}} ->
+ Pid ! cancel,
+ ?PRINT({cancel, Node, lockers, node(), Lockers}),
+ S#state{lockers = lists:keydelete(Node, 1, Lockers)};
+ _ ->
+ S
+ end.
+
+%% A node sent us his names. When a name clash is found, the resolve
+%% function is called from the smaller node => all resolve funcs are called
+%% from the same partition.
+exchange_names([{Name, Pid, Method} |Tail], Node, Ops, Res) ->
+ case ets:lookup(global_names, Name) of
+ [{Name, Pid, _}] ->
+ exchange_names(Tail, Node, Ops, Res);
+ [{Name, Pid2, Method2}] when node() < Node ->
+ %% Name clash! Add the result of resolving to Res(olved).
+ %% We know that node(Pid) /= node(), so we don't
+ %% need to link/unlink to Pid.
+ Node2 = node(Pid2), %%&&&&&& check external node???
+ case rpc:call(Node2, ?MODULE, resolve_it,
+ [Method2, Name, Pid, Pid2]) of
+ Pid ->
+ dounlink(Pid2),
+ ets:insert(global_names, {Name, Pid, Method}),
+ Op = {insert, {Name, Pid, Method}},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
+ Pid2 ->
+ Op = {insert, {Name, Pid2, Method2}},
+ exchange_names(Tail, Node, Ops, [Op | Res]);
+ none ->
+ dounlink(Pid2),
+ ?P2({unregister, node(), Name, Pid2, node(Pid2)}),
+ ets:delete(global_names, Name),
+ Op = {delete, Name},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
+ {badrpc, Badrpc} ->
+ error_logger:info_msg("global: badrpc ~w received when "
+ "conflicting name ~w was found",
+ [Badrpc, Name]),
+ dounlink(Pid2),
+ ets:insert(global_names, {Name, Pid, Method}),
+ Op = {insert, {Name, Pid, Method}},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res]);
+ Else ->
+ error_logger:info_msg("global: Resolve method ~w for "
+ "conflicting name ~w returned ~w~n",
+ [Method, Name, Else]),
+ dounlink(Pid2),
+ ets:delete(global_names, Name),
+ Op = {delete, Name},
+ exchange_names(Tail, Node, [Op | Ops], [Op | Res])
+ end;
+ [{Name, _Pid2, _}] ->
+ %% The other node will solve the conflict.
+ exchange_names(Tail, Node, Ops, Res);
+ _ ->
+ %% Entirely new name.
+ ets:insert(global_names, {Name, Pid, Method}),
+ exchange_names(Tail, Node,
+ [{insert, {Name, Pid, Method}} | Ops], Res)
+ end;
+exchange_names([], _, Ops, Res) ->
+ {Ops, Res}.
+
+resolve_it(Method, Name, Pid1, Pid2) ->
+ catch Method(Name, Pid1, Pid2).
+
+minmax(P1,P2) ->
+ if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end.
+
+random_exit_name(Name, Pid, Pid2) ->
+ {Min, Max} = minmax(Pid, Pid2),
+ error_logger:info_msg("global: Name conflict terminating ~w~n",
+ [{Name, Max}]),
+ exit(Max, kill),
+ Min.
+
+random_notify_name(Name, Pid, Pid2) ->
+ {Min, Max} = minmax(Pid, Pid2),
+ Max ! {global_name_conflict, Name},
+ Min.
+
+notify_all_name(Name, Pid, Pid2) ->
+ Pid ! {global_name_conflict, Name, Pid2},
+ Pid2 ! {global_name_conflict, Name, Pid},
+ none.
+
+cnode(Name, Pid, Pid2) ->
+ {Min, Max} = minmax(Pid, Pid2),
+ error_logger:info_msg("global: Name conflict terminating ~w~n",
+ [{Name, Max}]),
+ Max ! {global_name_conflict, Name},
+ Min.
+
+%% Only link to pids on our own node
+dolink(Pid) when node(Pid) == node() ->
+ link(Pid);
+dolink(_) -> ok.
+
+%% Only link to pids on our own node
+dolink_ext(Pid, RegNode) when RegNode == node() -> link(Pid);
+dolink_ext(_, _) -> ok.
+
+dounlink(Pid) when node(Pid) == node() ->
+ case ets:match(global_names, {'_', Pid, '_'}) of
+ [] ->
+ case is_pid_used(Pid) of
+ false ->
+ unlink(Pid);
+ true -> ok
+ end;
+ _ -> ok
+ end;
+dounlink(_Pid) ->
+ ok.
+
+is_pid_used(Pid) ->
+ is_pid_used(ets:tab2list(global_locks), Pid).
+
+is_pid_used([], _Pid) ->
+ false;
+is_pid_used([{_ResourceId, _LockReqId, Pids} | Tail], Pid) ->
+ case lists:member(Pid, Pids) of
+ true ->
+ true;
+ false ->
+ is_pid_used(Tail, Pid)
+ end.
+
+
+
+%% check_exit/3 removes the Pid from affected tables.
+%% This function needs to abcast the thingie since only the local
+%% server is linked to the registered process (or the owner of the
+%% lock). All the other servers rely on the nodedown mechanism.
+check_exit(Deleter, Pid) ->
+ del_names(Deleter, Pid, ets:tab2list(global_names)),
+ del_locks(ets:tab2list(global_locks), Pid).
+
+del_names(Deleter, Pid, [{Name, Pid, _Method} | Tail]) ->
+ %% First, delete the Pid from the local ets; then send to other nodes
+ ets:delete(global_names, Name),
+ ets:delete(global_names_ext, Name),
+ dounlink(Pid),
+ Deleter ! {delete_name,self(),Name,Pid},
+ del_names(Deleter, Pid, Tail);
+del_names(Deleter, Pid, [_|T]) ->
+ del_names(Deleter, Pid, T);
+del_names(_Deleter, _Pid, []) -> done.
+
+del_locks([{ResourceId, LockReqId, Pids} | Tail], Pid) ->
+ case {lists:member(Pid, Pids), Pids} of
+ {true, [Pid]} ->
+ ets:delete(global_locks, ResourceId),
+ gen_server:abcast(nodes(), global_name_server,
+ {async_del_lock, ResourceId, Pid});
+ {true, _} ->
+ NewPids = lists:delete(Pid, Pids),
+ ets:insert(global_locks, {ResourceId, LockReqId, NewPids}),
+ gen_server:abcast(nodes(), global_name_server,
+ {async_del_lock, ResourceId, Pid});
+ _ ->
+ continue
+ end,
+ del_locks(Tail, Pid);
+del_locks([], _Pid) -> done.
+
+del_locks2([{ResourceId, LockReqId, Pids} | Tail], Pid) ->
+ case {lists:member(Pid, Pids), Pids} of
+ {true, [Pid]} ->
+ ets:delete(global_locks, ResourceId);
+ {true, _} ->
+ NewPids = lists:delete(Pid, Pids),
+ ets:insert(global_locks, {ResourceId, LockReqId, NewPids});
+ _ ->
+ continue
+ end,
+ del_locks2(Tail, Pid);
+del_locks2([], _Pid) ->
+ done.
+
+
+
+%% Unregister all Name/Pid pairs such that node(Pid) == Node
+%% and delete all locks where node(Pid) == Node
+do_node_down(Node) ->
+ do_node_down_names(Node, ets:tab2list(global_names)),
+ do_node_down_names_ext(Node, ets:tab2list(global_names_ext)),
+ do_node_down_locks(Node, ets:tab2list(global_locks)).
+
+do_node_down_names(Node, [{Name, Pid, _Method} | T]) when node(Pid) == Node ->
+ ets:delete(global_names, Name),
+ do_node_down_names(Node, T);
+do_node_down_names(Node, [_|T]) ->
+ do_node_down_names(Node, T);
+do_node_down_names(_, []) -> ok.
+
+%%remove all external names registered on the crashed node
+do_node_down_names_ext(Node, [{Name, _Pid, Node} | T]) ->
+ ets:delete(global_names, Name),
+ ets:delete(global_names_ext, Name),
+ do_node_down_names_ext(Node, T);
+do_node_down_names_ext(Node, [_|T]) ->
+ do_node_down_names_ext(Node, T);
+do_node_down_names_ext(_, []) -> ok.
+
+do_node_down_locks(Node, [{ResourceId, LockReqId, Pids} | T]) ->
+ case do_node_down_locks2(Pids, Node) of
+ [] ->
+ continue;
+ RemovePids ->
+ case Pids -- RemovePids of
+ [] ->
+ ets:delete(global_locks, ResourceId);
+ NewPids ->
+ ets:insert(global_locks, {ResourceId, LockReqId, NewPids})
+ end
+ end,
+ do_node_down_locks(Node, T);
+do_node_down_locks(Node, [_|T]) ->
+ do_node_down_locks(Node, T);
+do_node_down_locks(_, []) -> done.
+
+
+do_node_down_locks2(Pids, Node) ->
+ do_node_down_locks2(Pids, Node, []).
+
+do_node_down_locks2([], _Node, Res) ->
+ Res;
+do_node_down_locks2([Pid | Pids], Node, Res) when node(Pid) == Node ->
+ do_node_down_locks2(Pids, Node, [Pid | Res]);
+do_node_down_locks2([_ | Pids], Node, Res) ->
+ do_node_down_locks2(Pids, Node, Res).
+
+
+get_names() ->
+ ets:tab2list(global_names).
+
+get_names_ext() ->
+ ets:tab2list(global_names_ext).
+
+random_sleep(Times) ->
+ case (Times rem 10) of
+ 0 -> erase(random_seed);
+ _ -> ok
+ end,
+ case get(random_seed) of
+ undefined ->
+ {A1, A2, A3} = now(),
+ random:seed(A1, A2, A3 + erlang:phash(node(), 100000));
+ _ -> ok
+ end,
+ %% First time 1/4 seconds, then doubling each time up to 8 seconds max.
+ Tmax = if Times > 5 -> 8000;
+ true -> ((1 bsl Times) * 1000) div 8
+ end,
+ T = random:uniform(Tmax),
+ ?P({random_sleep, node(), self(), Times, T}),
+ receive after T -> ok end.
+
+dec(infinity) -> infinity;
+dec(N) -> N-1.
+
+send_again(Msg) ->
+ spawn_link(?MODULE, timer, [self(), Msg]).
+
+timer(Pid, Msg) ->
+ random_sleep(5),
+ Pid ! Msg.
+
+change_our_node_name(NewNode, S) ->
+ S#state{node_name = NewNode}.
+
+
+%%-----------------------------------------------------------------
+%% Each sync process corresponds to one call to sync. Each such
+%% process asks the global_name_server on all Nodes if it is in sync
+%% with Nodes. If not, that (other) node spawns a syncer process that
+%% waits for global to get in sync with all Nodes. When it is in
+%% sync, the syncer process tells the original sync process about it.
+%%-----------------------------------------------------------------
+start_sync(Nodes, From) ->
+ spawn_link(?MODULE, sync_init, [Nodes, From]).
+
+sync_init(Nodes, From) ->
+ lists:foreach(fun(Node) -> monitor_node(Node, true) end, Nodes),
+ sync_loop(Nodes, From).
+
+sync_loop([], From) ->
+ gen_server:reply(From, ok);
+sync_loop(Nodes, From) ->
+ receive
+ {nodedown, Node} ->
+ monitor_node(Node, false),
+ sync_loop(lists:delete(Node, Nodes), From);
+ {synced, SNodes} ->
+ lists:foreach(fun(N) -> monitor_node(N, false) end, SNodes),
+ sync_loop(Nodes -- SNodes, From)
+ end.
+
+
+%%%====================================================================================
+%%% Get the current global_groups definition
+%%%====================================================================================
+check_sync_nodes() ->
+ case get_own_nodes() of
+ {ok, all} ->
+ nodes();
+ {ok, NodesNG} ->
+ %% global_groups parameter is defined, we are not allowed to sync
+ %% with nodes not in our own global group.
+ (nodes() -- (nodes() -- NodesNG));
+ {error, Error} ->
+ {error, Error}
+ end.
+
+check_sync_nodes(SyncNodes) ->
+ case get_own_nodes() of
+ {ok, all} ->
+ SyncNodes;
+ {ok, NodesNG} ->
+ %% global_groups parameter is defined, we are not allowed to sync
+ %% with nodes not in our own global group.
+ OwnNodeGroup = (nodes() -- (nodes() -- NodesNG)),
+ IllegalSyncNodes = (SyncNodes -- [node() | OwnNodeGroup]),
+ case IllegalSyncNodes of
+ [] -> SyncNodes;
+ _ -> {error, {"Trying to sync nodes not defined in the own global group",
+ IllegalSyncNodes}}
+ end;
+ {error, Error} ->
+ {error, Error}
+ end.
+
+get_own_nodes() ->
+ case global_group:get_own_nodes_with_errors() of
+ {error, Error} ->
+ {error, {"global_groups definition error", Error}};
+ OkTup ->
+ OkTup
+ end.
+
+
+%%-----------------------------------------------------------------
+%% The deleter process is a satellite process to global_name_server
+%% that does background batch deleting of names when a process
+%% that had globally registered names dies. It is started by and
+%% linked to global_name_server.
+%%-----------------------------------------------------------------
+
+start_the_deleter(Global) ->
+ spawn_link(
+ fun () ->
+ loop_the_deleter(Global)
+ end).
+
+loop_the_deleter(Global) ->
+ Deletions = collect_deletions(Global, []),
+ trans({global, self()},
+ fun() ->
+ lists:map(
+ fun ({Name,Pid}) ->
+ ?P2({delete_name2, Name, Pid, nodes()}),
+ gen_server:abcast(nodes(), global_name_server,
+ {async_del_name, Name, Pid})
+ end, Deletions)
+ end,
+ nodes()),
+ loop_the_deleter(Global).
+
+collect_deletions(Global, Deletions) ->
+ receive
+ {delete_name,Global,Name,Pid} ->
+ ?P2({delete_name, node(), self(), Name, Pid, nodes()}),
+ collect_deletions(Global, [{Name,Pid}|Deletions]);
+ Other ->
+ error_logger:error_msg("The global_name_server deleter process "
+ "received an unexpected message:\n~p\n",
+ [Other]),
+ collect_deletions(Global, Deletions)
+ after case Deletions of
+ [] -> infinity;
+ _ -> 0
+ end ->
+ lists:reverse(Deletions)
+ end.
diff --git a/lib/dialyzer/test/plt_tests_SUITE.erl b/lib/dialyzer/test/plt_tests_SUITE.erl
new file mode 100644
index 0000000000..bf45020340
--- /dev/null
+++ b/lib/dialyzer/test/plt_tests_SUITE.erl
@@ -0,0 +1,21 @@
+%% This suite is the only hand made and simply
+%% checks if we can build a plt.
+
+-module(plt_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, all/0, build_plt/1]).
+
+suite() ->
+ [{timetrap, ?plt_timeout}].
+
+all() -> [build_plt].
+
+build_plt(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ ok -> ok;
+ fail -> ct:fail(plt_build_fail)
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE.erl b/lib/dialyzer/test/r9c_tests_SUITE.erl
new file mode 100644
index 0000000000..cd5bd5ec61
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE.erl
@@ -0,0 +1,64 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(r9c_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([r9c_tests_SUITE_consistency/1, asn1/1, inets/1, mnesia/1]).
+
+suite() ->
+ [{timetrap, {minutes, 20}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, [{defines,[{vsn,42}]}]}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [r9c_tests_SUITE_consistency,asn1,inets,mnesia].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+r9c_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+asn1(Config) ->
+ case dialyze(Config, asn1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+inets(Config) ->
+ case dialyze(Config, inets) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia(Config) ->
+ case dialyze(Config, mnesia) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/r9c_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..e00e23bb66
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/dialyzer_options
@@ -0,0 +1,2 @@
+{dialyzer_options, [{defines, [{vsn, 42}]}]}.
+{time_limit, 20}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/results/asn1 b/lib/dialyzer/test/r9c_tests_SUITE_data/results/asn1
new file mode 100644
index 0000000000..ac83366bc8
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/results/asn1
@@ -0,0 +1,106 @@
+
+asn1ct.erl:1500: The variable Err can never match since previous clauses completely covered the type #type{}
+asn1ct.erl:1596: The variable _ can never match since previous clauses completely covered the type 'ber_bin_v2'
+asn1ct.erl:1673: The pattern 'all' can never match the type 'asn1_module' | 'exclusive_decode' | 'partial_decode'
+asn1ct.erl:672: The pattern <{'false', Result}, _, _> can never match the type <{'true','true'},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()],[any()]>
+asn1ct.erl:909: Guard test is_atom(Ext::[49 | 97 | 98 | 100 | 110 | 115]) can never succeed
+asn1ct_check.erl:1698: The pattern {'error', _} can never match the type [any()]
+asn1ct_check.erl:2733: The pattern {'type', Tag, _, _, _, _} can never match the type 'ASN1_OPEN_TYPE' | {_,_} | {'fixedtypevaluefield',_,_}
+asn1ct_check.erl:2738: The pattern <_S, _> can never match since previous clauses completely covered the type <#state{},#ObjectClassFieldType{class::#objectclass{fields::maybe_improper_list() | {_,_,_,_}},fieldname::{_,maybe_improper_list()},type::'ASN1_OPEN_TYPE' | {_,_} | {'fixedtypevaluefield',_,_}}>
+asn1ct_check.erl:2887: The variable Other can never match since previous clauses completely covered the type any()
+asn1ct_check.erl:3188: The pattern <_S, [], B> can never match the type <#state{},{'SingleValue',_},{'ValueRange',_}>
+asn1ct_check.erl:3190: The pattern <_S, A, []> can never match the type <#state{},{'SingleValue',_},{'ValueRange',_}>
+asn1ct_check.erl:3212: The pattern {[], C3} can never match the type {[any(),...],{'ValueRange',{'MIN','MAX'}}}
+asn1ct_check.erl:3225: The pattern {L1, UbNew} can never match the type 'false'
+asn1ct_check.erl:3228: The pattern {L1, LbNew} can never match the type 'false'
+asn1ct_check.erl:3235: The call asn1ct_check:remove_val_from_list(number(),L::[any(),...]) will never return since it differs in the 1st argument from the success typing arguments: ([any()],any())
+asn1ct_check.erl:3240: The call asn1ct_check:remove_val_from_list(number(),L::[any(),...]) will never return since it differs in the 1st argument from the success typing arguments: ([any()],any())
+asn1ct_check.erl:3242: Function remove_val_from_list/2 has no local return
+asn1ct_check.erl:3243: The call lists:member(Val::[any(),...],List::number()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),[any()])
+asn1ct_check.erl:3283: The pattern [] can never match the type [any(),...]
+asn1ct_check.erl:3362: The pattern <_, [], _VR> can never match the type <#state{},[any(),...],[any(),...]>
+asn1ct_check.erl:3364: The pattern <_, _SV, []> can never match the type <#state{},[any(),...],[any(),...]>
+asn1ct_check.erl:4150: The pattern <_, [_]> can never match the type <_,[]>
+asn1ct_check.erl:4314: The pattern <S, Type, {Rlist, ExtList}> can never match the type <#state{},_,[any()]>
+asn1ct_check.erl:4360: The pattern <S, Type, {Rlist, ExtList}> can never match the type <#state{},_,[any()]>
+asn1ct_check.erl:4719: The call asn1ct_check:error({'type',{'asn1',[1..255,...],[any(),...]}}) will never return since it differs in the 1st argument from the success typing arguments: ({'ObjectSet' | 'class' | 'export' | 'ptype' | 'type' | 'value',_,#state{}})
+asn1ct_check.erl:5120: Guard test is_record(Type::{_,_} | {'fixedtypevaluefield',_,_},'type',6) can never succeed
+asn1ct_check.erl:5128: Guard test is_record(Type::{_,_} | {'fixedtypevaluefield',_,_},'type',6) can never succeed
+asn1ct_check.erl:540: The pattern <_S, {'poc', _ObjSet, _Params}> can never match since previous clauses completely covered the type <#state{},_>
+asn1ct_check.erl:5517: The pattern <_, []> can never match the type <_,[{'ABSTRACT-SYNTAX',{_,_,_}} | {'TYPE-IDENTIFIER',{_,_,_}},...]>
+asn1ct_constructed_ber.erl:1075: The pattern {{{'ObjectClassFieldType', _, _, _, {'objectfield', PrimFieldName1, PFNList}}, _}, {'componentrelation', _, _}} can never match the type {#type{},_}
+asn1ct_constructed_ber.erl:695: The pattern {'EXTENSIONMARK', _, _} can never match the type #ComponentType{}
+asn1ct_constructed_ber.erl:748: The pattern <Erules, TopType, {CompList, _ExtList}> can never match the type <_,maybe_improper_list(),[#ComponentType{typespec::{_,_,_,_,_,_}}]>
+asn1ct_constructed_ber_bin_v2.erl:914: The pattern {{{'ObjectClassFieldType', _, _, _, {'objectfield', PrimFieldName1, PFNList}}, _}, {'componentrelation', _, _}} can never match the type {#type{},_}
+asn1ct_gen.erl:740: The pattern [] can never match the type [any(),...]
+asn1ct_gen_ber.erl:974: The pattern <Erules, [{Name, Def} | Rest]> can never match the type <_,[#typedef{name::atom(),typespec::{_,_,_,_,_,_}}]>
+asn1ct_gen_ber_bin_v2.erl:975: The pattern <Erules, [{Name, Def} | Rest]> can never match the type <_,[#typedef{name::atom(),typespec::{_,_,_,_,_,_}}]>
+asn1ct_gen_per.erl:646: The pattern <Erules, [{Name, Def} | Rest]> can never match the type <_,[#typedef{name::atom()}]>
+asn1ct_gen_per_rt2ct.erl:1189: The pattern <Erules, [{Name, Def} | Rest]> can never match the type <_,[#typedef{name::atom()}]>
+asn1ct_gen_per_rt2ct.erl:563: The pattern <C, ['EXT_MARK' | T], _Count> can never match the type <[{'ValueRange',{_,_}},...],[char() | {'asn1_enum',integer()},...],non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:580: The pattern <_C, 'EXT_MARK', _Count> can never match the type <[{'ValueRange',{_,_}},...],char(),non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:583: The pattern <_C, {1, EnumName}, Count> can never match the type <[{'ValueRange',{_,_}},...],char(),non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:587: The pattern <C, {0, EnumName}, Count> can never match the type <[{'ValueRange',{_,_}},...],char(),non_neg_integer()>
+asn1ct_gen_per_rt2ct.erl:656: The pattern <Type, C> can never match since previous clauses completely covered the type <'bitstring' | 'integer',_>
+asn1ct_parser2.erl:2017: Call to missing or unexported function ordsets:list_to_set/1
+asn1ct_parser2.erl:2497: The variable _ can never match since previous clauses completely covered the type 'ok'
+asn1ct_parser2.erl:2628: The pattern {Rlist, ExtList} can never match the type [{_,_,_},...]
+asn1ct_parser2.erl:2660: Call to missing or unexported function ordsets:list_to_set/1
+asn1ct_parser2.erl:2685: Call to missing or unexported function ordsets:list_to_set/1
+asn1ct_parser2.erl:281: The variable Other can never match since previous clauses completely covered the type [any()]
+asn1ct_parser2.erl:529: The variable _ can never match since previous clauses completely covered the type #constraint{}
+asn1ct_parser2.erl:555: The variable _ can never match since previous clauses completely covered the type #constraint{}
+asn1ct_parser2.erl:796: The variable _ can never match since previous clauses completely covered the type {_,_}
+asn1ct_parser2.erl:814: The variable _ can never match since previous clauses completely covered the type {_,_}
+asn1ct_parser2.erl:831: The variable _ can never match since previous clauses completely covered the type {_,_}
+asn1ct_value.erl:247: The pattern <'undefined', Default> can never match the type <maybe_improper_list(),[1..255,...]>
+asn1rt_ber_bin.erl:1125: Cons will produce an improper list since its 2nd argument is binary() | tuple()
+asn1rt_ber_bin.erl:1276: The pattern <{{_Min1, Max1}, {Min2, Max2}}, BitListVal, _DoTag> can never match since previous clauses completely covered the type <{_,_},maybe_improper_list(),_>
+asn1rt_ber_bin.erl:2057: The call asn1rt_ber_bin:check_if_valid_tag2('false',[],[],OptOrMand::any()) will never return since it differs in the 2nd argument from the success typing arguments: ('false' | {'APPLICATION',_} | {'CONTEXT',_} | {'PRIVATE',_} | {'UNIVERSAL',_},nonempty_maybe_improper_list(),[] | {_,_,_},any())
+asn1rt_ber_bin.erl:969: The pattern {Val01, Buffer01, Rb01} can never match the type {'MINUS-INFINITY' | 'PLUS-INFINITY' | 0,binary()}
+asn1rt_ber_bin.erl:998: The pattern {FirstLen, {Exp, Buffer3}, RemBytes2} can never match the type {1..1114111,{integer(),binary(),number()},number()}
+asn1rt_ber_bin_v2.erl:1230: The pattern <{{_Min1, Max1}, {Min2, Max2}}, BitListVal, TagIn> can never match since previous clauses completely covered the type <{_,_},maybe_improper_list(),_>
+asn1rt_ber_bin_v2.erl:328: The variable _ can never match since previous clauses completely covered the type {{0 | 1,non_neg_integer(),'indefinite' | non_neg_integer(),binary()},binary() | []}
+asn1rt_ber_bin_v2.erl:337: The variable _ can never match since previous clauses completely covered the type {{0 | 1,non_neg_integer(),'indefinite' | non_neg_integer(),binary()},binary() | []}
+asn1rt_ber_bin_v2.erl:392: The variable _ can never match since previous clauses completely covered the type {{0 | 1,non_neg_integer(),'indefinite' | non_neg_integer(),binary()},binary() | []}
+asn1rt_ber_bin_v2.erl:963: Function decode_real/3 has no local return
+asn1rt_check.erl:100: The variable _ can never match since previous clauses completely covered the type [any()]
+asn1rt_check.erl:85: The variable _ can never match since previous clauses completely covered the type [any()]
+asn1rt_driver_handler.erl:32: The pattern 'already_done' can never match the type {'error',_}
+asn1rt_per.erl:1065: The pattern {'BMPString', {'octets', Ol}} can never match the type {_,[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
+asn1rt_per.erl:1066: Function will never be called
+asn1rt_per.erl:1231: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:1233: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:1235: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:1237: The call erlang:'not'('implemented') will never return since it differs in the 1st argument from the success typing arguments: (boolean())
+asn1rt_per.erl:989: The pattern <_C, 'true', _Val> can never match the type <_,'false',_>
+asn1rt_per_bin.erl:1361: The pattern <_, 'true', _> can never match the type <_,'false',_>
+asn1rt_per_bin.erl:1436: The pattern {'BMPString', {'octets', Ol}} can never match the type {'BMPString' | 'IA5String' | 'NumericString' | 'PrintableString' | 'UniversalString' | 'VisibleString',[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
+asn1rt_per_bin.erl:1437: Function will never be called
+asn1rt_per_bin.erl:161: The call asn1rt_per_bin:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>})
+asn1rt_per_bin.erl:1812: The pattern {Name, Val} can never match since previous clauses completely covered the type any()
+asn1rt_per_bin.erl:2106: Cons will produce an improper list since its 2nd argument is binary()
+asn1rt_per_bin.erl:2111: Cons will produce an improper list since its 2nd argument is binary()
+asn1rt_per_bin.erl:2111: Cons will produce an improper list since its 2nd argument is integer()
+asn1rt_per_bin.erl:2117: Cons will produce an improper list since its 2nd argument is integer()
+asn1rt_per_bin.erl:2121: Cons will produce an improper list since its 2nd argument is 0
+asn1rt_per_bin.erl:2123: Cons will produce an improper list since its 2nd argument is 0
+asn1rt_per_bin.erl:2127: Cons will produce an improper list since its 2nd argument is 0
+asn1rt_per_bin.erl:2129: Cons will produce an improper list since its 2nd argument is integer()
+asn1rt_per_bin.erl:446: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin.erl:467: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin.erl:474: The pattern <{_N, <<_:8/integer-unit:1,Bs/binary-unit:8>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()>
+asn1rt_per_bin.erl:487: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin.erl:498: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:152: The call asn1rt_per_bin_rt2ct:getbit({0,maybe_improper_list()}) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>> | {non_neg_integer(),<<_:1,_:_*1>>})
+asn1rt_per_bin_rt2ct.erl:1533: The pattern {'BMPString', {'octets', Ol}} can never match the type {_,[[any(),...]]}
+asn1rt_per_bin_rt2ct.erl:1534: Function will never be called
+asn1rt_per_bin_rt2ct.erl:1875: The pattern {Name, Val} can never match since previous clauses completely covered the type any()
+asn1rt_per_bin_rt2ct.erl:443: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:464: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:471: The pattern <{_N, <<_B:8/integer-unit:1,Bs/binary-unit:8>>}, C> can never match since previous clauses completely covered the type <{0,_},integer()>
+asn1rt_per_bin_rt2ct.erl:484: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_bin_rt2ct.erl:495: The variable _ can never match since previous clauses completely covered the type integer()
+asn1rt_per_v1.erl:1209: The pattern <_, 'true', _> can never match the type <_,'false',_>
+asn1rt_per_v1.erl:1290: The pattern {'BMPString', {'octets', Ol}} can never match the type {'BMPString' | 'IA5String' | 'NumericString' | 'PrintableString' | 'UniversalString' | 'VisibleString',[{'bits',1 | 2 | 4 | 8 | 16 | 32,_}]}
+asn1rt_per_v1.erl:1291: Function will never be called
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/results/inets b/lib/dialyzer/test/r9c_tests_SUITE_data/results/inets
new file mode 100644
index 0000000000..fd5e36a3cd
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/results/inets
@@ -0,0 +1,59 @@
+
+ftp.erl:1243: The pattern {'ok', {N, Bytes}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+ftp.erl:640: The pattern {'closed', _Why} can never match the type 'perm_fname_not_allowed' | 'perm_neg_compl' | 'perm_no_space' | 'pos_compl' | 'pos_interm' | 'pos_interm_acct' | 'trans_neg_compl' | 'trans_no_space' | {'error' | 'perm_fname_not_allowed' | 'perm_neg_compl' | 'perm_no_space' | 'pos_compl' | 'pos_interm' | 'pos_interm_acct' | 'pos_prel' | 'trans_neg_compl' | 'trans_no_space',atom() | [any()] | {'invalid_server_response',[any(),...]}}
+http.erl:117: The pattern {'error', Reason} can never match the type #req_headers{connection::[45 | 97 | 101 | 105 | 107 | 108 | 112 | 118,...],content_length::[48,...],other::[{_,_}]}
+http.erl:138: Function close_session/2 will never be called
+http_lib.erl:286: The call http_lib:close('ip_comm' | {'ssl',_},any()) will never return since it differs in the 1st argument from the success typing arguments: ('http' | 'https',any())
+http_lib.erl:424: The variable _ can never match since previous clauses completely covered the type any()
+http_lib.erl:438: The variable _ can never match since previous clauses completely covered the type any()
+http_lib.erl:99: Function getHeaderValue/2 will never be called
+httpc_handler.erl:322: Function status_continue/2 has no local return
+httpc_handler.erl:37: Function init_connection/2 has no local return
+httpc_handler.erl:65: Function next_response_with_request/2 has no local return
+httpc_handler.erl:660: Function exit_session_ok/2 has no local return
+httpc_manager.erl:145: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}}
+httpc_manager.erl:160: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}}
+httpc_manager.erl:478: The pattern {'error', Reason} can never match the type 'ok' | {number(),#session{clientclose::boolean(),pipeline::[],quelength::1}}
+httpc_manager.erl:490: The pattern {'error', Reason} can never match the type 'ok' | {number(),#session{clientclose::boolean(),pipeline::[],quelength::1}}
+httpd.erl:583: The pattern <{'error', Reason}, _Fd, SoFar> can never match the type <[any()],pid(),[[any(),...]]>
+httpd_acceptor.erl:105: The pattern {'error', Reason} can never match the type {'ok',pid()}
+httpd_acceptor.erl:110: Function handle_connection_err/4 will never be called
+httpd_acceptor.erl:168: Function report_error/2 will never be called
+httpd_acceptor.erl:91: The call httpd_acceptor:handle_error({'EXIT',_},ConfigDb::any(),SocketType::any()) will never return since it differs in the 1st argument from the success typing arguments: ('econnaborted' | 'emfile' | 'esslaccept' | 'timeout' | {'enfile',_},any(),any())
+httpd_manager.erl:885: The pattern {'EXIT', Reason} can never match since previous clauses completely covered the type any()
+httpd_manager.erl:919: Function auth_status/1 will never be called
+httpd_manager.erl:926: Function sec_status/1 will never be called
+httpd_manager.erl:933: Function acceptor_status/1 will never be called
+httpd_request_handler.erl:374: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 66 | 98 | 100 | 103 | 105 | 111 | 116 | 121,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any())
+httpd_request_handler.erl:378: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 77 | 97 | 100 | 101 | 104 | 108 | 110 | 111 | 116 | 119,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any())
+httpd_request_handler.erl:401: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 77 | 97 | 100 | 101 | 104 | 108 | 110 | 111 | 116 | 119,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any())
+httpd_request_handler.erl:644: The call lists:reverse(Fields0::{'error',_} | {'ok',[[any()]]}) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+httpd_request_handler.erl:645: Function will never be called
+httpd_sup.erl:63: The variable Else can never match since previous clauses completely covered the type {'error',_} | {'ok',[any()],_,_}
+httpd_sup.erl:88: The pattern {'error', Reason} can never match the type {'ok',_,_}
+httpd_sup.erl:92: The variable Else can never match since previous clauses completely covered the type {'ok',_,_}
+mod_auth.erl:559: The pattern {'error', Reason} can never match the type {_,integer(),maybe_improper_list(),_}
+mod_auth_dets.erl:120: The call lists:foreach(fun((_) -> 'true' | {'error','no_such_group' | 'no_such_group_member'}),{'ok',[any()]}) will never return since it differs in the 2nd argument from the success typing arguments: (fun((_) -> any()),[any()])
+mod_auth_plain.erl:100: The variable _ can never match since previous clauses completely covered the type {'ok',[any()]}
+mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [any()]
+mod_cgi.erl:372: The pattern {'http_response', NewAccResponse} can never match the type 'ok'
+mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | binary() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+mod_dir.erl:72: The pattern {'error', Reason} can never match the type {'ok',[[[any()] | char()],...]}
+mod_get.erl:135: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | binary() | [any()] | char()]>
+mod_head.erl:80: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]>
+mod_htaccess.erl:460: The pattern {'error', BadData} can never match the type {'ok',_}
+mod_include.erl:193: The pattern {_, Name, {[], []}} can never match the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:195: The pattern {_, Name, {PathInfo, []}} can never match the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:197: The pattern {_, Name, {PathInfo, QueryString}} can never match the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:201: The variable Gurka can never match since previous clauses completely covered the type {[any()],[any()],maybe_improper_list()}
+mod_include.erl:692: The pattern <{'read', Reason}, Info, Path> can never match the type <{'open',atom()},#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]>
+mod_include.erl:706: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]>
+mod_include.erl:716: Function read_error/3 will never be called
+mod_include.erl:719: Function read_error/4 will never be called
+mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:433: The variable Other can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:585: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:608: The variable _ can never match since previous clauses completely covered the type [any()]
+mod_security_server.erl:641: The variable _ can never match since previous clauses completely covered the type [any()]
+uri.erl:146: The pattern {'error', Error} can never match since previous clauses completely covered the type {_,{[],[]}}
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/results/mnesia b/lib/dialyzer/test/r9c_tests_SUITE_data/results/mnesia
new file mode 100644
index 0000000000..e199581a0e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/results/mnesia
@@ -0,0 +1,34 @@
+
+mnesia.erl:1319: Guard test size(Spec::[{_,_,_},...]) can never succeed
+mnesia.erl:1498: The call mnesia:bad_info_reply(Tab::atom(),Item::'type') will never return since it differs in the 2nd argument from the success typing arguments: (atom(),'memory' | 'size')
+mnesia.erl:331: Function mod2abs/1 has no local return
+mnesia_bup.erl:111: The created fun has no local return
+mnesia_bup.erl:574: Function fallback_receiver/2 has no local return
+mnesia_bup.erl:967: Function uninstall_fallback_master/2 has no local return
+mnesia_checkpoint.erl:1014: The variable Error can never match since previous clauses completely covered the type {'ok',#checkpoint_args{nodes::[any()],retainers::[any(),...]}}
+mnesia_controller.erl:1666: The variable Tab can never match since previous clauses completely covered the type [any()]
+mnesia_controller.erl:1679: The pattern {'stop', Reason, Reply, State2} can never match the type {'noreply',_} | {'reply',_,_} | {'stop','shutdown',#state{}}
+mnesia_controller.erl:1685: The pattern {'noreply', State2, _Timeout} can never match the type {'reply',_,_}
+mnesia_event.erl:77: The pattern 'remove_handler' can never match the type {'ok',_}
+mnesia_event.erl:79: The pattern {'swap_handler', Args1, State1, Mod2, Args2} can never match the type {'ok',_}
+mnesia_frag.erl:294: The call mnesia_frag:remote_collect(Ref::reference(),{'error',_},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()]))
+mnesia_frag.erl:304: The call mnesia_frag:remote_collect(Ref::reference(),{'error',{'node_not_running',_}},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()]))
+mnesia_frag.erl:312: The call mnesia_frag:remote_collect(Ref::reference(),LocalRes::{'error',_},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()]))
+mnesia_index.erl:52: The call mnesia_lib:other_val(Var::{_,'commit_work' | 'index' | 'setorbag' | 'storage_type' | {'index',_}},_ReASoN_::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
+mnesia_lib.erl:957: The pattern {'ok', {0, _}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+mnesia_lib.erl:959: The pattern {'ok', {_, Bin}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()}
+mnesia_loader.erl:36: The call mnesia_lib:other_val(Var::{_,'access_mode' | 'cstruct' | 'db_nodes' | 'setorbag' | 'snmp' | 'storage_type'},Reason::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
+mnesia_locker.erl:1017: Function system_terminate/4 has no local return
+mnesia_log.erl:707: The test {'error',{[1..255,...],[any(),...]}} | {'ok',_} == atom() can never evaluate to 'true'
+mnesia_log.erl:727: The created fun has no local return
+mnesia_monitor.erl:162: The pattern <[], []> can never match the type <[any(),...],[any(),...]>
+mnesia_monitor.erl:354: The pattern {'error', Reason} can never match the type 'ok'
+mnesia_recover.erl:159: The call mnesia_lib:other_val(Var::'latest_transient_decision' | 'max_wait_for_decision' | 'previous_transient_decisions' | 'recover_nodes',Reason::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any())
+mnesia_recover.erl:884: The pattern {'stop', Reason, Reply, State2} can never match the type {'noreply',_} | {'stop','shutdown',#state{}}
+mnesia_schema.erl:1088: Guard test Storage::'disc_copies' | 'disc_only_copies' | 'ram_copies' == 'unknown' can never succeed
+mnesia_schema.erl:1258: Guard test FromS::'disc_copies' | 'disc_only_copies' | 'ram_copies' == 'unknown' can never succeed
+mnesia_schema.erl:1639: The pattern {'false', 'mandatory'} can never match the type {'false','optional'}
+mnesia_schema.erl:2434: The variable Reason can never match since previous clauses completely covered the type {'error',_} | {'ok',_}
+mnesia_schema.erl:451: Guard test UseDirAnyway::'false' == 'true' can never succeed
+mnesia_tm.erl:1522: Function commit_participant/5 has no local return
+mnesia_tm.erl:2169: Function system_terminate/4 has no local return
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Makefile b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Makefile
new file mode 100644
index 0000000000..b539e88108
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Makefile
@@ -0,0 +1,151 @@
+#
+# Copyright (C) 1997, Ericsson Telecommunications
+# Author: Kenneth Lundin
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(ASN1_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/asn1-$(VSN)
+
+
+
+
+#
+# Common Macros
+#
+# PARSER_SRC = \
+# asn1ct_parser.yrl
+
+# PARSER_MODULE=$(PARSER_SRC:%.yrl=%)
+
+EBIN = ../ebin
+CT_MODULES= \
+ asn1ct \
+ asn1ct_check \
+ asn1_db \
+ asn1ct_pretty_format \
+ asn1ct_gen \
+ asn1ct_gen_per \
+ asn1ct_gen_per_rt2ct \
+ asn1ct_name \
+ asn1ct_constructed_per \
+ asn1ct_constructed_ber \
+ asn1ct_gen_ber \
+ asn1ct_constructed_ber_bin_v2 \
+ asn1ct_gen_ber_bin_v2 \
+ asn1ct_value \
+ asn1ct_tok \
+ asn1ct_parser2
+
+RT_MODULES= \
+ asn1rt \
+ asn1rt_per \
+ asn1rt_per_bin \
+ asn1rt_per_v1 \
+ asn1rt_ber_bin \
+ asn1rt_ber_bin_v2 \
+ asn1rt_per_bin_rt2ct \
+ asn1rt_driver_handler \
+ asn1rt_check
+
+# asn1rt_ber_v1 \
+# asn1rt_ber \
+# the rt module to use is defined in asn1_records.hrl
+# and must be updated when an incompatible change is done in the rt modules
+
+
+MODULES= $(CT_MODULES) $(RT_MODULES)
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+GENERATED_PARSER = $(PARSER_MODULE:%=%.erl)
+
+# internal hrl file
+HRL_FILES = asn1_records.hrl
+
+APP_FILE = asn1.app
+APPUP_FILE = asn1.appup
+
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+APPUP_SRC = $(APPUP_FILE).src
+APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
+
+EXAMPLES = \
+ ../examples/P-Record.asn
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_FLAGS +=
+ERL_COMPILE_FLAGS += \
+ -I$(ERL_TOP)/lib/stdlib \
+ +warn_unused_vars
+YRL_FLAGS =
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
+
+
+clean:
+ rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(GENERATED_PARSER)
+ rm -f core *~
+
+docs:
+
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(EBIN)/asn1ct.$(EMULATOR):asn1ct.erl
+ $(ERLC) -b$(EMULATOR) -o$(EBIN) $(ERL_COMPILE_FLAGS) -Dvsn=\"$(VSN)\" $<
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(PARSER_SRC) $(ERL_FILES) $(HRL_FILES) $(APP_SRC) $(APPUP_SRC) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(EXAMPLES) $(RELSYSDIR)/examples
+
+# there are no include files to be used by the user
+#$(INSTALL_DIR) $(RELSYSDIR)/include
+#$(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include
+
+release_docs_spec:
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Restrictions.txt b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Restrictions.txt
new file mode 100644
index 0000000000..73b725245d
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/Restrictions.txt
@@ -0,0 +1,55 @@
+The following restrictions apply to this implementation of the ASN.1 compiler:
+
+Supported encoding rules are:
+BER
+PER (aligned)
+
+PER (unaligned) IS NOT SUPPORTED
+
+Supported types are:
+
+INTEGER
+BOOLEAN
+ENUMERATION
+SEQUENCE
+SEQUENCE OF
+SET
+SET OF
+CHOICE
+OBJECT IDENTIFIER
+RestrictedCharacterStringTypes
+UnrestrictedCharacterStringTypes
+
+
+NOT SUPPORTED types are:
+ANY IS (IS NOT IN THE STANDARD ANY MORE)
+ANY DEFINED BY (IS NOT IN THE STANDARD ANY MORE)
+EXTERNAL
+EMBEDDED-PDV
+REAL
+
+The support for value definitions in the ASN.1 notation is very limited.
+
+The support for constraints is limited to:
+SizeConstraint SIZE(X)
+SingleValue (1)
+ValueRange (X..Y)
+PermittedAlpabet FROM
+
+The only supported value-notation for SEQUENCE and SET in Erlang is
+the record variant.
+The list notation with named components used by the old ASN.1 compiler
+was supported in the first versions of this compiler both are no longer
+supported.
+
+The decode functions always return a symbolic value if they can.
+
+
+Files with ASN.1 source must have a suffix .asn1 the suffix .py used by the
+old ASN.1 compiler is supported in this version but will not be supported in the future.
+
+Generated files:
+X.asn1db % the intermediate format of a compiled ASN.1 module
+X.hrl % generated Erlang include file for module X
+X.erl % generated Erlang module with encode decode functions for
+ % ASN.1 module X
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.app.src b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.app.src
new file mode 100644
index 0000000000..2ec06ff4db
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.app.src
@@ -0,0 +1,20 @@
+{application, asn1,
+ [{description, "The Erlang ASN1 compiler version %VSN%"},
+ {vsn, "%VSN%"},
+ {modules, [
+ asn1rt,
+ asn1rt_per,
+ asn1rt_per_v1,
+ asn1rt_per_bin,
+ asn1rt_per_bin_rt2ct,
+ asn1rt_ber_bin,
+ asn1rt_ber_bin_v2,
+ asn1rt_check,
+ asn1rt_driver_handler
+ ]},
+ {registered, [
+ asn1_driver_owner
+ ]},
+ {env, []},
+ {applications, [kernel, stdlib]}
+ ]}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.appup.src b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.appup.src
new file mode 100644
index 0000000000..255dec709e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1.appup.src
@@ -0,0 +1,166 @@
+{"%VSN%",
+ [
+ {"1.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin},
+ {add_module, asn1rt_check}
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin},
+ {add_module, asn1rt_check}
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.1.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin},
+ {add_module, asn1rt_check}
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.2",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {add_module, asn1rt_per_bin_rt2ct},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ },
+ {"1.3.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_bin_v2},
+ {add_module, asn1rt_driver_handler}
+ {remove, {asn1rt_ber_v1, soft_purge, soft_purge}},
+ ]
+ }
+ ],
+ [
+ {"1.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin, soft_purge, soft_purge}},
+ {remove, {asn1rt_check, soft_purge, soft_purge}}
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin, soft_purge, soft_purge}},
+ {remove, {asn1rt_check, soft_purge, soft_purge}}
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.1.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin, soft_purge, soft_purge}},
+ {remove, {asn1rt_check, soft_purge, soft_purge}}
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.2",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_per_bin_rt2ct, soft_purge, soft_purge}},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.3",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ },
+ {"1.3.3.1",
+ [
+ {load_module, asn1rt_per_v1, soft_purge, soft_purge, []},
+ {load_module, asn1rt_ber_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin, soft_purge, soft_purge, []},
+ {load_module, asn1rt_check, soft_purge, soft_purge, []},
+ {load_module, asn1rt_per_bin_rt2ct, soft_purge, soft_purge, []},
+ {add_module, asn1rt_ber_v1},
+ {remove, {asn1rt_ber_bin_v2, soft_purge, soft_purge}},
+ {remove, {asn1rt_driver_handler, soft_purge, soft_purge}}
+ ]
+ }
+
+ ]}.
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_db.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_db.erl
new file mode 100644
index 0000000000..cf01e39fed
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_db.erl
@@ -0,0 +1,162 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1_db.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1_db).
+%-compile(export_all).
+-export([dbnew/1,dbsave/2,dbload/1,dbput/3,dbget/2,dbget_all/1]).
+-export([dbget_all_mod/1,dbstop/0,dbclear/0,dberase_module/1,dbstart/1,stop_server/1]).
+%% internal exports
+-export([dbloop0/1,dbloop/2]).
+
+%% Db stuff
+dbstart(Includes) ->
+ start_server(asn1db, asn1_db, dbloop0, [Includes]).
+
+dbloop0(Includes) ->
+ dbloop(Includes, ets:new(asn1, [set,named_table])).
+
+opentab(Tab,Mod,[]) ->
+ opentab(Tab,Mod,["."]);
+opentab(Tab,Mod,Includes) ->
+ Base = lists:concat([Mod,".asn1db"]),
+ opentab2(Tab,Base,Mod,Includes,ok).
+
+opentab2(_Tab,_Base,_Mod,[],Error) ->
+ Error;
+opentab2(Tab,Base,Mod,[Ih|It],_Error) ->
+ File = filename:join(Ih,Base),
+ case ets:file2tab(File) of
+ {ok,Modtab} ->
+ ets:insert(Tab,{Mod, Modtab}),
+ {ok,Modtab};
+ NewErr ->
+ opentab2(Tab,Base,Mod,It,NewErr)
+ end.
+
+
+dbloop(Includes, Tab) ->
+ receive
+ {From,{set, Mod, K2, V}} ->
+ [{_,Modtab}] = ets:lookup(Tab,Mod),
+ ets:insert(Modtab,{K2, V}),
+ From ! {asn1db, ok},
+ dbloop(Includes, Tab);
+ {From, {get, Mod, K2}} ->
+ Result = case ets:lookup(Tab,Mod) of
+ [] ->
+ opentab(Tab,Mod,Includes);
+ [{_,Modtab}] -> {ok,Modtab}
+ end,
+ case Result of
+ {ok,Newtab} ->
+ From ! {asn1db, lookup(Newtab, K2)};
+ _Error ->
+ From ! {asn1db, undefined}
+ end,
+ dbloop(Includes, Tab);
+ {From, {all_mod, Mod}} ->
+ [{_,Modtab}] = ets:lookup(Tab,Mod),
+ From ! {asn1db, ets:tab2list(Modtab)},
+ dbloop(Includes, Tab);
+ {From, {delete_mod, Mod}} ->
+ [{_,Modtab}] = ets:lookup(Tab,Mod),
+ ets:delete(Modtab),
+ ets:delete(Tab,Mod),
+ From ! {asn1db, ok},
+ dbloop(Includes, Tab);
+ {From, {save, OutFile,Mod}} ->
+ [{_,Mtab}] = ets:lookup(Tab,Mod),
+ {From ! {asn1db, ets:tab2file(Mtab,OutFile)}},
+ dbloop(Includes,Tab);
+ {From, {load, Mod}} ->
+ Result = case ets:lookup(Tab,Mod) of
+ [] ->
+ opentab(Tab,Mod,Includes);
+ [{_,Modtab}] -> {ok,Modtab}
+ end,
+ {From, {asn1db,Result}},
+ dbloop(Includes,Tab);
+ {From, {new, Mod}} ->
+ case ets:lookup(Tab,Mod) of
+ [{_,Modtab}] ->
+ ets:delete(Modtab);
+ _ ->
+ true
+ end,
+ Tabname = list_to_atom(lists:concat(["asn1_",Mod])),
+ ets:new(Tabname, [set,named_table]),
+ ets:insert(Tab,{Mod,Tabname}),
+ From ! {asn1db, ok},
+ dbloop(Includes,Tab);
+ {From, stop} ->
+ From ! {asn1db, ok}; %% nothing to store
+ {From, clear} ->
+ ModTabList = [Mt||{_,Mt} <- ets:tab2list(Tab)],
+ lists:foreach(fun(T) -> ets:delete(T) end,ModTabList),
+ ets:delete(Tab),
+ From ! {asn1db, cleared},
+ dbloop(Includes, ets:new(asn1, [set]))
+ end.
+
+
+%%all(Tab, K) ->
+%% pickup(K, ets:match(Tab, {{K, '$1'}, '$2'})).
+%%pickup(K, []) -> [];
+%%pickup(K, [[V1,V2] |T]) ->
+%% [{{K,V1},V2} | pickup(K, T)].
+
+lookup(Tab, K) ->
+ case ets:lookup(Tab, K) of
+ [] -> undefined;
+ [{K,V}] -> V
+ end.
+
+
+dbnew(Module) -> req({new,Module}).
+dbsave(OutFile,Module) -> req({save,OutFile,Module}).
+dbload(Module) -> req({load,Module}).
+
+dbput(Module,K,V) -> req({set, Module, K, V}).
+dbget(Module,K) -> req({get, Module, K}).
+dbget_all(K) -> req({get_all, K}).
+dbget_all_mod(Mod) -> req({all_mod,Mod}).
+dbstop() -> stop_server(asn1db).
+dbclear() -> req(clear).
+dberase_module({module,M})->
+ req({delete_mod, M}).
+
+req(R) ->
+ asn1db ! {self(), R},
+ receive {asn1db, Reply} -> Reply end.
+
+stop_server(Name) ->
+ stop_server(Name, whereis(Name)).
+stop_server(_, undefined) -> stopped;
+stop_server(Name, _Pid) ->
+ Name ! {self(), stop},
+ receive {Name, _} -> stopped end.
+
+
+start_server(Name,Mod,Fun,Args) ->
+ case whereis(Name) of
+ undefined ->
+ register(Name, spawn(Mod,Fun, Args));
+ _Pid ->
+ already_started
+ end.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_records.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_records.hrl
new file mode 100644
index 0000000000..07ca8cccf3
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1_records.hrl
@@ -0,0 +1,96 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1_records.hrl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-define('RT_BER',"asn1rt_ber_v1").
+-define('RT_BER_BIN',"asn1rt_ber_bin").
+-define('RT_PER',"asn1rt_per_v1").
+%% change to this when we have this module -define('RT_PER_BIN',"asn1rt_per_bin").
+-define('RT_PER_BIN',"asn1rt_per_bin").
+
+-record(module,{pos,name,defid,tagdefault='EXPLICIT',exports={exports,[]},imports={imports,[]}, extensiondefault=empty,typeorval}).
+
+-record('SEQUENCE',{pname=false,tablecinf=false,components=[]}).
+-record('SET',{pname=false,sorted=false,tablecinf=false,components=[]}).
+-record('ComponentType',{pos,name,typespec,prop,tags}).
+-record('ObjectClassFieldType',{classname,class,fieldname,type}).
+
+-record(typedef,{checked=false,pos,name,typespec}).
+-record(classdef,{checked=false,pos,name,typespec}).
+-record(valuedef,{checked=false,pos,name,type,value}).
+-record(ptypedef,{checked=false,pos,name,args,typespec}).
+-record(pvaluedef,{checked=false,pos,name,args,type,value}).
+-record(pvaluesetdef,{checked=false,pos,name,args,type,valueset}).
+-record(pobjectdef,{checked=false,pos,name,args,class,def}).
+-record(pobjectsetdef,{checked=false,pos,name,args,class,def}).
+
+-record(typereference,{pos,val}).
+-record(identifier,{pos,val}).
+-record(constraint,{c,e}).
+-record('Constraint',{'SingleValue'=no,'SizeConstraint'=no,'ValueRange'=no,'PermittedAlphabet'=no,
+ 'ContainedSubtype'=no, 'TypeConstraint'=no,'InnerSubtyping'=no,e=no,'Other'=no}).
+-record(simpletableattributes,{objectsetname,c_name,c_index,usedclassfield,
+ uniqueclassfield,valueindex}).
+-record(type,{tag=[],def,constraint=[],tablecinf=[],inlined=no}).
+
+-record(objectclass,{fields=[],syntax}).
+-record('Object',{classname,gen=true,def}).
+-record('ObjectSet',{class,gen=true,uniquefname,set}).
+
+-record(tag,{class,number,type,form=32}). % form = ?CONSTRUCTED
+% This record holds information about allowed constraint types per type
+-record(cmap,{single_value=no,contained_subtype=no,value_range=no,
+ size=no,permitted_alphabet=no,type_constraint=no,
+ inner_subtyping=no}).
+
+
+-record('EXTENSIONMARK',{pos,val}).
+
+% each IMPORT contains a list of 'SymbolsFromModule'
+-record('SymbolsFromModule',{symbols,module,objid}).
+
+% Externaltypereference -> modulename '.' typename
+-record('Externaltypereference',{pos,module,type}).
+% Externalvaluereference -> modulename '.' typename
+-record('Externalvaluereference',{pos,module,value}).
+
+-record(state,{module,mname,type,tname,value,vname,erule,parameters=[],
+ inputmodules,abscomppath=[],recordtopname=[],options}).
+
+%% state record used by backend at partial decode
+%% active is set to 'yes' when a partial decode function is generated.
+%% prefix is set to 'dec-inc-' or 'dec-partial-' is for
+%% incomplete partial decode or partial decode respectively
+%% inc_tag_pattern holds the tags of the significant types/components
+%% for incomplete partial decode.
+%% tag_pattern holds the tags for partial decode.
+%% inc_type_pattern and type_pattern holds the names of the
+%% significant types/components.
+%% func_name holds the name of the function for the toptype.
+%% namelist holds the list of names of types/components that still
+%% haven't been generated.
+%% tobe_refed_funcs is a list of tuples {function names
+%% (Types),namelist of incomplete decode spec}, with function names
+%% that are referenced within other generated partial incomplete
+%% decode functions. They shall be generated as partial incomplete
+%% decode functions.
+
+%% gen_refed_funcs is as list of function names. Unlike
+%% tobe_refed_funcs these have been generated.
+-record(gen_state,{active=false,prefix,inc_tag_pattern,
+ tag_pattern,inc_type_pattern,
+ type_pattern,func_name,namelist,
+ tobe_refed_funcs=[],gen_refed_funcs=[]}).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct.erl
new file mode 100644
index 0000000000..37189e3780
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct.erl
@@ -0,0 +1,1904 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct).
+
+%% Compile Time functions for ASN.1 (e.g ASN.1 compiler).
+
+%%-compile(export_all).
+%% Public exports
+-export([compile/1, compile/2]).
+-export([start/0, start/1, stop/0]).
+-export([encode/2, encode/3, decode/3]).
+-export([test/1, test/2, test/3, value/2]).
+%% Application internal exports
+-export([compile_asn/3,compile_asn1/3,compile_py/3,compile/3,value/1,vsn/0,
+ create_ets_table/2,get_name_of_def/1,get_pos_of_def/1]).
+-export([read_config_data/1,get_gen_state_field/1,get_gen_state/0,
+ partial_inc_dec_toptype/1,save_gen_state/1,update_gen_state/2,
+ get_tobe_refed_func/1,reset_gen_state/0,is_function_generated/1,
+ generated_refed_func/1,next_refed_func/0,pop_namelist/0,
+ next_namelist_el/0,update_namelist/1,step_in_constructed/0,
+ add_tobe_refed_func/1,add_generated_refed_func/1]).
+
+-include("asn1_records.hrl").
+-include_lib("stdlib/include/erl_compile.hrl").
+
+-import(asn1ct_gen_ber_bin_v2,[encode_tag_val/3,decode_class/1]).
+
+-define(unique_names,0).
+-define(dupl_uniquedefs,1).
+-define(dupl_equaldefs,2).
+-define(dupl_eqdefs_uniquedefs,?dupl_equaldefs bor ?dupl_uniquedefs).
+
+-define(CONSTRUCTED, 2#00100000).
+
+%% macros used for partial decode commands
+-define(CHOOSEN,choosen).
+-define(SKIP,skip).
+-define(SKIP_OPTIONAL,skip_optional).
+
+%% macros used for partial incomplete decode commands
+-define(MANDATORY,mandatory).
+-define(DEFAULT,default).
+-define(OPTIONAL,opt).
+-define(PARTS,parts).
+-define(UNDECODED,undec).
+-define(ALTERNATIVE,alt).
+-define(ALTERNATIVE_UNDECODED,alt_undec).
+-define(ALTERNATIVE_PARTS,alt_parts).
+%-define(BINARY,bin).
+
+%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This is the interface to the compiler
+%%
+%%
+
+
+compile(File) ->
+ compile(File,[]).
+
+compile(File,Options) when list(Options) ->
+ Options1 =
+ case {lists:member(optimize,Options),lists:member(ber_bin,Options)} of
+ {true,true} ->
+ [ber_bin_v2|Options--[ber_bin]];
+ _ -> Options
+ end,
+ case (catch input_file_type(File)) of
+ {single_file,PrefixedFile} ->
+ (catch compile1(PrefixedFile,Options1));
+ {multiple_files_file,SetBase,FileName} ->
+ FileList = get_file_list(FileName),
+ (catch compile_set(SetBase,filename:dirname(FileName),
+ FileList,Options1));
+ Err = {input_file_error,_Reason} ->
+ {error,Err}
+ end.
+
+
+compile1(File,Options) when list(Options) ->
+ io:format("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,File]),
+ io:format("Compiler Options: ~p~n",[Options]),
+ Ext = filename:extension(File),
+ Base = filename:basename(File,Ext),
+ OutFile = outfile(Base,"",Options),
+ DbFile = outfile(Base,"asn1db",Options),
+ Includes = [I || {i,I} <- Options],
+ EncodingRule = get_rule(Options),
+ create_ets_table(asn1_functab,[named_table]),
+ Continue1 = scan({true,true},File,Options),
+ Continue2 = parse(Continue1,File,Options),
+ Continue3 = check(Continue2,File,OutFile,Includes,EncodingRule,
+ DbFile,Options,[]),
+ Continue4 = generate(Continue3,OutFile,EncodingRule,Options),
+ delete_tables([asn1_functab]),
+ compile_erl(Continue4,OutFile,Options).
+
+%%****************************************************************************%%
+%% functions dealing with compiling of several input files to one output file %%
+%%****************************************************************************%%
+compile_set(SetBase,DirName,Files,Options) when list(hd(Files)),list(Options) ->
+ %% case when there are several input files in a list
+ io:format("Erlang ASN.1 version ~p compiling ~p ~n",[?vsn,Files]),
+ io:format("Compiler Options: ~p~n",[Options]),
+ OutFile = outfile(SetBase,"",Options),
+ DbFile = outfile(SetBase,"asn1db",Options),
+ Includes = [I || {i,I} <- Options],
+ EncodingRule = get_rule(Options),
+ create_ets_table(asn1_functab,[named_table]),
+ ScanRes = scan_set(DirName,Files,Options),
+ ParseRes = parse_set(ScanRes,Options),
+ Result =
+ case [X||X <- ParseRes,element(1,X)==true] of
+ [] -> %% all were false, time to quit
+ lists:map(fun(X)->element(2,X) end,ParseRes);
+ ParseRes -> %% all were true, continue with check
+ InputModules =
+ lists:map(
+ fun(F)->
+ E = filename:extension(F),
+ B = filename:basename(F,E),
+ if
+ list(B) -> list_to_atom(B);
+ true -> B
+ end
+ end,
+ Files),
+ check_set(ParseRes,SetBase,OutFile,Includes,
+ EncodingRule,DbFile,Options,InputModules);
+ Other ->
+ {error,{'unexpected error in scan/parse phase',
+ lists:map(fun(X)->element(3,X) end,Other)}}
+ end,
+ delete_tables([asn1_functab]),
+ Result.
+
+check_set(ParseRes,SetBase,OutFile,Includes,EncRule,DbFile,
+ Options,InputModules) ->
+ lists:foreach(fun({_T,M,File})->
+ cmp(M#module.name,File)
+ end,
+ ParseRes),
+ MergedModule = merge_modules(ParseRes,SetBase),
+ SetM = MergedModule#module{name=SetBase},
+ Continue1 = check({true,SetM},SetBase,OutFile,Includes,EncRule,DbFile,
+ Options,InputModules),
+ Continue2 = generate(Continue1,OutFile,EncRule,Options),
+
+ delete_tables([renamed_defs,original_imports,automatic_tags]),
+
+ compile_erl(Continue2,OutFile,Options).
+
+%% merge_modules/2 -> returns a module record where the typeorval lists are merged,
+%% the exports lists are merged, the imports lists are merged when the
+%% elements come from other modules than the merge set, the tagdefault
+%% field gets the shared value if all modules have same tagging scheme,
+%% otherwise a tagging_error exception is thrown,
+%% the extensiondefault ...(not handled yet).
+merge_modules(ParseRes,CommonName) ->
+ ModuleList = lists:map(fun(X)->element(2,X) end,ParseRes),
+ NewModuleList = remove_name_collisions(ModuleList),
+ case ets:info(renamed_defs,size) of
+ 0 -> ets:delete(renamed_defs);
+ _ -> ok
+ end,
+ save_imports(NewModuleList),
+% io:format("~p~n~p~n~p~n~n",[ets:lookup(original_imports,'M1'),ets:lookup(original_imports,'M2'),ets:tab2list(original_imports)]),
+ TypeOrVal = lists:append(lists:map(fun(X)->X#module.typeorval end,
+ NewModuleList)),
+ InputMNameList = lists:map(fun(X)->X#module.name end,
+ NewModuleList),
+ CExports = common_exports(NewModuleList),
+
+ ImportsModuleNameList = lists:map(fun(X)->
+ {X#module.imports,
+ X#module.name} end,
+ NewModuleList),
+ %% ImportsModuleNameList: [{Imports,ModuleName},...]
+ %% Imports is a tuple {imports,[#'SymbolsFromModule'{},...]}
+ CImports = common_imports(ImportsModuleNameList,InputMNameList),
+ TagDefault = check_tagdefault(NewModuleList),
+ #module{name=CommonName,tagdefault=TagDefault,exports=CExports,
+ imports=CImports,typeorval=TypeOrVal}.
+
+%% causes an exit if duplicate definition names exist in a module
+remove_name_collisions(Modules) ->
+ create_ets_table(renamed_defs,[named_table]),
+ %% Name duplicates in the same module is not allowed.
+ lists:foreach(fun exit_if_nameduplicate/1,Modules),
+ %% Then remove duplicates in different modules and return the
+ %% new list of modules.
+ remove_name_collisions2(Modules,[]).
+
+%% For each definition in the first module in module list, find
+%% all definitons with same name and rename both definitions in
+%% the first module and in rest of modules
+remove_name_collisions2([M|Ms],Acc) ->
+ TypeOrVal = M#module.typeorval,
+ MName = M#module.name,
+ %% Test each name in TypeOrVal on all modules in Ms
+ {NewM,NewMs} = remove_name_collisions2(MName,TypeOrVal,Ms,[]),
+ remove_name_collisions2(NewMs,[M#module{typeorval=NewM}|Acc]);
+remove_name_collisions2([],Acc) ->
+ finished_warn_prints(),
+ Acc.
+
+%% For each definition in list of defs find definitions in (rest of)
+%% modules that have same name. If duplicate was found rename def.
+%% Test each name in [T|Ts] on all modules in Ms
+remove_name_collisions2(ModName,[T|Ts],Ms,Acc) ->
+ Name = get_name_of_def(T),
+ case discover_dupl_in_mods(Name,T,Ms,[],?unique_names) of
+ {_,?unique_names} -> % there was no name collision
+ remove_name_collisions2(ModName,Ts,Ms,[T|Acc]);
+ {NewMs,?dupl_uniquedefs} -> % renamed defs in NewMs
+ %% rename T
+ NewT = set_name_of_def(ModName,Name,T), %rename def
+ warn_renamed_def(ModName,get_name_of_def(NewT),Name),
+ ets:insert(renamed_defs,{get_name_of_def(NewT),Name,ModName}),
+ remove_name_collisions2(ModName,Ts,NewMs,[NewT|Acc]);
+ {NewMs,?dupl_equaldefs} -> % name duplicates, but identical defs
+ %% keep name of T
+ warn_kept_def(ModName,Name),
+ remove_name_collisions2(ModName,Ts,NewMs,[T|Acc]);
+ {NewMs,?dupl_eqdefs_uniquedefs} ->
+ %% keep name of T, renamed defs in NewMs
+ warn_kept_def(ModName,Name),
+ remove_name_collisions2(ModName,Ts,NewMs,[T|Acc])
+ end;
+remove_name_collisions2(_,[],Ms,Acc) ->
+ {Acc,Ms}.
+
+%% Name is the name of a definition. If a definition with the same name
+%% is found in the modules Ms the definition will be renamed and returned.
+discover_dupl_in_mods(Name,Def,[M=#module{name=N,typeorval=TorV}|Ms],
+ Acc,AnyRenamed) ->
+ Fun = fun(T,RenamedOrDupl)->
+ case {get_name_of_def(T),compare_defs(Def,T)} of
+ {Name,not_equal} ->
+ %% rename def
+ NewT=set_name_of_def(N,Name,T),
+ warn_renamed_def(N,get_name_of_def(NewT),Name),
+ ets:insert(renamed_defs,{get_name_of_def(NewT),
+ Name,N}),
+ {NewT,?dupl_uniquedefs bor RenamedOrDupl};
+ {Name,equal} ->
+ %% delete def
+ warn_deleted_def(N,Name),
+ {[],?dupl_equaldefs bor RenamedOrDupl};
+ _ ->
+ {T,RenamedOrDupl}
+ end
+ end,
+ {NewTorV,NewAnyRenamed} = lists:mapfoldl(Fun,AnyRenamed,TorV),
+ %% have to flatten the NewTorV to remove any empty list elements
+ discover_dupl_in_mods(Name,Def,Ms,
+ [M#module{typeorval=lists:flatten(NewTorV)}|Acc],
+ NewAnyRenamed);
+discover_dupl_in_mods(_,_,[],Acc,AnyRenamed) ->
+ {Acc,AnyRenamed}.
+
+warn_renamed_def(ModName,NewName,OldName) ->
+ maybe_first_warn_print(),
+ io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has been renamed in generated module. New name is ~p.~n",[ModName,OldName,NewName]).
+
+warn_deleted_def(ModName,DefName) ->
+ maybe_first_warn_print(),
+ io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has been deleted in generated module.~n",[ModName,DefName]).
+
+warn_kept_def(ModName,DefName) ->
+ maybe_first_warn_print(),
+ io:format("NOTICE: The ASN.1 definition in module ~p with name ~p has kept its name due to equal definition as duplicate.~n",[ModName,DefName]).
+
+maybe_first_warn_print() ->
+ case get(warn_duplicate_defs) of
+ undefined ->
+ put(warn_duplicate_defs,true),
+ io:format("~nDue to multiple occurrences of a definition name in "
+ "multi-file compiled files:~n");
+ _ ->
+ ok
+ end.
+finished_warn_prints() ->
+ put(warn_duplicate_defs,undefined).
+
+
+exit_if_nameduplicate(#module{typeorval=TorV}) ->
+ exit_if_nameduplicate(TorV);
+exit_if_nameduplicate([]) ->
+ ok;
+exit_if_nameduplicate([Def|Rest]) ->
+ Name=get_name_of_def(Def),
+ exit_if_nameduplicate2(Name,Rest),
+ exit_if_nameduplicate(Rest).
+
+exit_if_nameduplicate2(Name,Rest) ->
+ Pred=fun(Def)->
+ case get_name_of_def(Def) of
+ Name -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(Pred,Rest) of
+ true ->
+ throw({error,{"more than one definition with same name",Name}});
+ _ ->
+ ok
+ end.
+
+compare_defs(D1,D2) ->
+ compare_defs2(unset_pos(D1),unset_pos(D2)).
+compare_defs2(D,D) ->
+ equal;
+compare_defs2(_,_) ->
+ not_equal.
+
+unset_pos(Def) when record(Def,typedef) ->
+ Def#typedef{pos=undefined};
+unset_pos(Def) when record(Def,classdef) ->
+ Def#classdef{pos=undefined};
+unset_pos(Def) when record(Def,valuedef) ->
+ Def#valuedef{pos=undefined};
+unset_pos(Def) when record(Def,ptypedef) ->
+ Def#ptypedef{pos=undefined};
+unset_pos(Def) when record(Def,pvaluedef) ->
+ Def#pvaluedef{pos=undefined};
+unset_pos(Def) when record(Def,pvaluesetdef) ->
+ Def#pvaluesetdef{pos=undefined};
+unset_pos(Def) when record(Def,pobjectdef) ->
+ Def#pobjectdef{pos=undefined};
+unset_pos(Def) when record(Def,pobjectsetdef) ->
+ Def#pobjectsetdef{pos=undefined}.
+
+get_pos_of_def(#typedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#classdef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#valuedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#ptypedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pvaluedef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pvaluesetdef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pobjectdef{pos=Pos}) ->
+ Pos;
+get_pos_of_def(#pobjectsetdef{pos=Pos}) ->
+ Pos.
+
+
+get_name_of_def(#typedef{name=Name}) ->
+ Name;
+get_name_of_def(#classdef{name=Name}) ->
+ Name;
+get_name_of_def(#valuedef{name=Name}) ->
+ Name;
+get_name_of_def(#ptypedef{name=Name}) ->
+ Name;
+get_name_of_def(#pvaluedef{name=Name}) ->
+ Name;
+get_name_of_def(#pvaluesetdef{name=Name}) ->
+ Name;
+get_name_of_def(#pobjectdef{name=Name}) ->
+ Name;
+get_name_of_def(#pobjectsetdef{name=Name}) ->
+ Name.
+
+set_name_of_def(ModName,Name,OldDef) ->
+ NewName = list_to_atom(lists:concat([Name,ModName])),
+ case OldDef of
+ #typedef{} -> OldDef#typedef{name=NewName};
+ #classdef{} -> OldDef#classdef{name=NewName};
+ #valuedef{} -> OldDef#valuedef{name=NewName};
+ #ptypedef{} -> OldDef#ptypedef{name=NewName};
+ #pvaluedef{} -> OldDef#pvaluedef{name=NewName};
+ #pvaluesetdef{} -> OldDef#pvaluesetdef{name=NewName};
+ #pobjectdef{} -> OldDef#pobjectdef{name=NewName};
+ #pobjectsetdef{} -> OldDef#pobjectsetdef{name=NewName}
+ end.
+
+save_imports(ModuleList)->
+ Fun = fun(M) ->
+ case M#module.imports of
+ {_,[]} -> [];
+ {_,I} ->
+ {M#module.name,I}
+ end
+ end,
+ ImportsList = lists:map(Fun,ModuleList),
+ case lists:flatten(ImportsList) of
+ [] ->
+ ok;
+ ImportsList2 ->
+ create_ets_table(original_imports,[named_table]),
+ ets:insert(original_imports,ImportsList2)
+ end.
+
+
+common_exports(ModuleList) ->
+ %% if all modules exports 'all' then export 'all',
+ %% otherwise export each typeorval name
+ case lists:filter(fun(X)->
+ element(2,X#module.exports) /= all
+ end,
+ ModuleList) of
+ []->
+ {exports,all};
+ ModsWithExpList ->
+ CExports1 =
+ lists:append(lists:map(fun(X)->element(2,X#module.exports) end,
+ ModsWithExpList)),
+ CExports2 = export_all(lists:subtract(ModuleList,ModsWithExpList)),
+ {exports,CExports1++CExports2}
+ end.
+
+export_all([])->[];
+export_all(ModuleList) ->
+ ExpList =
+ lists:map(
+ fun(M)->
+ TorVL=M#module.typeorval,
+ MName = M#module.name,
+ lists:map(
+ fun(Def)->
+ case Def of
+ T when record(T,typedef)->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=T#typedef.name};
+ V when record(V,valuedef) ->
+ #'Externalvaluereference'{pos=0,
+ module=MName,
+ value=V#valuedef.name};
+ C when record(C,classdef) ->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=C#classdef.name};
+ P when record(P,ptypedef) ->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=P#ptypedef.name};
+ PV when record(PV,pvaluesetdef) ->
+ #'Externaltypereference'{pos=0,
+ module=MName,
+ type=PV#pvaluesetdef.name};
+ PO when record(PO,pobjectdef) ->
+ #'Externalvaluereference'{pos=0,
+ module=MName,
+ value=PO#pobjectdef.name}
+ end
+ end,
+ TorVL)
+ end,
+ ModuleList),
+ lists:append(ExpList).
+
+%% common_imports/2
+%% IList is a list of tuples, {Imports,MName}, where Imports is the imports of
+%% the module with name MName.
+%% InputMNameL holds the names of all merged modules.
+%% Returns an import tuple with a list of imports that are external the merged
+%% set of modules.
+common_imports(IList,InputMNameL) ->
+ SetExternalImportsList = remove_in_set_imports(IList,InputMNameL,[]),
+ {imports,remove_import_doubles(SetExternalImportsList)}.
+
+check_tagdefault(ModList) ->
+ case have_same_tagdefault(ModList) of
+ {true,TagDefault} -> TagDefault;
+ {false,TagDefault} ->
+ create_ets_table(automatic_tags,[named_table]),
+ save_automatic_tagged_types(ModList),
+ TagDefault
+ end.
+
+have_same_tagdefault([#module{tagdefault=T}|Ms]) ->
+ have_same_tagdefault(Ms,{true,T}).
+
+have_same_tagdefault([],TagDefault) ->
+ TagDefault;
+have_same_tagdefault([#module{tagdefault=T}|Ms],TDefault={_,T}) ->
+ have_same_tagdefault(Ms,TDefault);
+have_same_tagdefault([#module{tagdefault=T1}|Ms],{_,T2}) ->
+ have_same_tagdefault(Ms,{false,rank_tagdef([T1,T2])}).
+
+rank_tagdef(L) ->
+ case lists:member('EXPLICIT',L) of
+ true -> 'EXPLICIT';
+ _ -> 'IMPLICIT'
+ end.
+
+save_automatic_tagged_types([])->
+ done;
+save_automatic_tagged_types([#module{tagdefault='AUTOMATIC',
+ typeorval=TorV}|Ms]) ->
+ Fun =
+ fun(T) ->
+ ets:insert(automatic_tags,{get_name_of_def(T)})
+ end,
+ lists:foreach(Fun,TorV),
+ save_automatic_tagged_types(Ms);
+save_automatic_tagged_types([_M|Ms]) ->
+ save_automatic_tagged_types(Ms).
+
+%% remove_in_set_imports/3 :
+%% input: list with tuples of each module's imports and module name
+%% respectively.
+%% output: one list with same format but each occured import from a
+%% module in the input set (IMNameL) is removed.
+remove_in_set_imports([{{imports,ImpL},_ModName}|Rest],InputMNameL,Acc) ->
+ NewImpL = remove_in_set_imports1(ImpL,InputMNameL,[]),
+ remove_in_set_imports(Rest,InputMNameL,NewImpL++Acc);
+remove_in_set_imports([],_,Acc) ->
+ lists:reverse(Acc).
+
+remove_in_set_imports1([I|Is],InputMNameL,Acc) ->
+ case I#'SymbolsFromModule'.module of
+ #'Externaltypereference'{type=MName} ->
+ case lists:member(MName,InputMNameL) of
+ true ->
+ remove_in_set_imports1(Is,InputMNameL,Acc);
+ false ->
+ remove_in_set_imports1(Is,InputMNameL,[I|Acc])
+ end;
+ _ ->
+ remove_in_set_imports1(Is,InputMNameL,[I|Acc])
+ end;
+remove_in_set_imports1([],_,Acc) ->
+ lists:reverse(Acc).
+
+remove_import_doubles([]) ->
+ [];
+%% If several modules in the merge set imports symbols from
+%% the same external module it might be doubled.
+%% ImportList has #'SymbolsFromModule' elements
+remove_import_doubles(ImportList) ->
+ MergedImportList =
+ merge_symbols_from_module(ImportList,[]),
+%% io:format("MergedImportList: ~p~n",[MergedImportList]),
+ delete_double_of_symbol(MergedImportList,[]).
+
+merge_symbols_from_module([Imp|Imps],Acc) ->
+ #'Externaltypereference'{type=ModName} = Imp#'SymbolsFromModule'.module,
+ IfromModName =
+ lists:filter(
+ fun(I)->
+ case I#'SymbolsFromModule'.module of
+ #'Externaltypereference'{type=ModName} ->
+ true;
+ #'Externalvaluereference'{value=ModName} ->
+ true;
+ _ -> false
+ end
+ end,
+ Imps),
+ NewImps = lists:subtract(Imps,IfromModName),
+%% io:format("Imp: ~p~nIfromModName: ~p~n",[Imp,IfromModName]),
+ NewImp =
+ Imp#'SymbolsFromModule'{
+ symbols = lists:append(
+ lists:map(fun(SL)->
+ SL#'SymbolsFromModule'.symbols
+ end,[Imp|IfromModName]))},
+ merge_symbols_from_module(NewImps,[NewImp|Acc]);
+merge_symbols_from_module([],Acc) ->
+ lists:reverse(Acc).
+
+delete_double_of_symbol([I|Is],Acc) ->
+ SymL=I#'SymbolsFromModule'.symbols,
+ NewSymL = delete_double_of_symbol1(SymL,[]),
+ delete_double_of_symbol(Is,[I#'SymbolsFromModule'{symbols=NewSymL}|Acc]);
+delete_double_of_symbol([],Acc) ->
+ Acc.
+
+delete_double_of_symbol1([TRef=#'Externaltypereference'{type=TrefName}|Rest],Acc)->
+ NewRest =
+ lists:filter(fun(S)->
+ case S of
+ #'Externaltypereference'{type=TrefName}->
+ false;
+ _ -> true
+ end
+ end,
+ Rest),
+ delete_double_of_symbol1(NewRest,[TRef|Acc]);
+delete_double_of_symbol1([VRef=#'Externalvaluereference'{value=VName}|Rest],Acc) ->
+ NewRest =
+ lists:filter(fun(S)->
+ case S of
+ #'Externalvaluereference'{value=VName}->
+ false;
+ _ -> true
+ end
+ end,
+ Rest),
+ delete_double_of_symbol1(NewRest,[VRef|Acc]);
+delete_double_of_symbol1([TRef={#'Externaltypereference'{type=MRef},
+ #'Externaltypereference'{type=TRef}}|Rest],
+ Acc)->
+ NewRest =
+ lists:filter(
+ fun(S)->
+ case S of
+ {#'Externaltypereference'{type=MRef},
+ #'Externaltypereference'{type=TRef}}->
+ false;
+ _ -> true
+ end
+ end,
+ Rest),
+ delete_double_of_symbol1(NewRest,[TRef|Acc]);
+delete_double_of_symbol1([],Acc) ->
+ Acc.
+
+
+scan_set(DirName,Files,Options) ->
+ lists:map(
+ fun(F)->
+ case scan({true,true},filename:join([DirName,F]),Options) of
+ {false,{error,Reason}} ->
+ throw({error,{'scan error in file:',F,Reason}});
+ {TrueOrFalse,Res} ->
+ {TrueOrFalse,Res,F}
+ end
+ end,
+ Files).
+
+parse_set(ScanRes,Options) ->
+ lists:map(
+ fun({TorF,Toks,F})->
+ case parse({TorF,Toks},F,Options) of
+ {false,{error,Reason}} ->
+ throw({error,{'parse error in file:',F,Reason}});
+ {TrueOrFalse,Res} ->
+ {TrueOrFalse,Res,F}
+ end
+ end,
+ ScanRes).
+
+
+%%***********************************
+
+
+scan({true,_}, File,Options) ->
+ case asn1ct_tok:file(File) of
+ {error,Reason} ->
+ io:format("~p~n",[Reason]),
+ {false,{error,Reason}};
+ Tokens ->
+ case lists:member(ss,Options) of
+ true -> % we terminate after scan
+ {false,Tokens};
+ false -> % continue with next pass
+ {true,Tokens}
+ end
+ end;
+scan({false,Result},_,_) ->
+ Result.
+
+
+parse({true,Tokens},File,Options) ->
+ %Presult = asn1ct_parser2:parse(Tokens),
+ %%case lists:member(p1,Options) of
+ %% true ->
+ %% asn1ct_parser:parse(Tokens);
+ %% _ ->
+ %% asn1ct_parser2:parse(Tokens)
+ %% end,
+ case catch asn1ct_parser2:parse(Tokens) of
+ {error,{{Line,_Mod,Message},_TokTup}} ->
+ if
+ integer(Line) ->
+ BaseName = filename:basename(File),
+ io:format("syntax error at line ~p in module ~s:~n",
+ [Line,BaseName]);
+ true ->
+ io:format("syntax error in module ~p:~n",[File])
+ end,
+ print_error_message(Message),
+ {false,{error,Message}};
+ {error,{Line,_Mod,[Message,Token]}} ->
+ io:format("syntax error: ~p ~p at line ~p~n",
+ [Message,Token,Line]),
+ {false,{error,{Line,[Message,Token]}}};
+ {ok,M} ->
+ case lists:member(sp,Options) of
+ true -> % terminate after parse
+ {false,M};
+ false -> % continue with next pass
+ {true,M}
+ end;
+ OtherError ->
+ io:format("~p~n",[OtherError])
+ end;
+parse({false,Tokens},_,_) ->
+ {false,Tokens}.
+
+check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) ->
+ cmp(M#module.name,File),
+ start(["."|Includes]),
+ case asn1ct_check:storeindb(M) of
+ ok ->
+ Module = asn1_db:dbget(M#module.name,'MODULE'),
+ State = #state{mname=Module#module.name,
+ module=Module#module{typeorval=[]},
+ erule=EncodingRule,
+ inputmodules=InputMods,
+ options=Options},
+ Check = asn1ct_check:check(State,Module#module.typeorval),
+ case {Check,lists:member(abs,Options)} of
+ {{error,Reason},_} ->
+ {false,{error,Reason}};
+ {{ok,NewTypeOrVal,_},true} ->
+ NewM = Module#module{typeorval=NewTypeOrVal},
+ asn1_db:dbput(NewM#module.name,'MODULE',NewM),
+ pretty2(M#module.name,lists:concat([OutFile,".abs"])),
+ {false,ok};
+ {{ok,NewTypeOrVal,GenTypeOrVal},_} ->
+ NewM = Module#module{typeorval=NewTypeOrVal},
+ asn1_db:dbput(NewM#module.name,'MODULE',NewM),
+ asn1_db:dbsave(DbFile,M#module.name),
+ io:format("--~p--~n",[{generated,DbFile}]),
+ {true,{M,NewM,GenTypeOrVal}}
+ end
+ end;
+check({false,M},_,_,_,_,_,_,_) ->
+ {false,M}.
+
+generate({true,{M,_Module,GenTOrV}},OutFile,EncodingRule,Options) ->
+ debug_on(Options),
+ case lists:member(compact_bit_string,Options) of
+ true -> put(compact_bit_string,true);
+ _ -> ok
+ end,
+ put(encoding_options,Options),
+ create_ets_table(check_functions,[named_table]),
+
+ %% create decoding function names and taglists for partial decode
+ %% For the time being leave errors unnoticed !!!!!!!!!
+% io:format("Options: ~p~n",[Options]),
+ case catch specialized_decode_prepare(EncodingRule,M,GenTOrV,Options) of
+ {error, enoent} -> ok;
+ {error, Reason} -> io:format("WARNING: Error in configuration"
+ "file: ~n~p~n",[Reason]);
+ {'EXIT',Reason} -> io:format("WARNING: Internal error when "
+ "analyzing configuration"
+ "file: ~n~p~n",[Reason]);
+ _ -> ok
+ end,
+
+ asn1ct_gen:pgen(OutFile,EncodingRule,M#module.name,GenTOrV),
+ debug_off(Options),
+ put(compact_bit_string,false),
+ erase(encoding_options),
+ erase(tlv_format), % used in ber_bin, optimize
+ erase(class_default_type),% used in ber_bin, optimize
+ ets:delete(check_functions),
+ case lists:member(sg,Options) of
+ true -> % terminate here , with .erl file generated
+ {false,true};
+ false ->
+ {true,true}
+ end;
+generate({false,M},_,_,_) ->
+ {false,M}.
+
+compile_erl({true,_},OutFile,Options) ->
+ erl_compile(OutFile,Options);
+compile_erl({false,true},_,_) ->
+ ok;
+compile_erl({false,Result},_,_) ->
+ Result.
+
+input_file_type([]) ->
+ {empty_name,[]};
+input_file_type(File) ->
+ case filename:extension(File) of
+ [] ->
+ case file:read_file_info(lists:concat([File,".asn1"])) of
+ {ok,_FileInfo} ->
+ {single_file, lists:concat([File,".asn1"])};
+ _Error ->
+ case file:read_file_info(lists:concat([File,".asn"])) of
+ {ok,_FileInfo} ->
+ {single_file, lists:concat([File,".asn"])};
+ _Error ->
+ {single_file, lists:concat([File,".py"])}
+ end
+ end;
+ ".asn1config" ->
+ case read_config_file(File,asn1_module) of
+ {ok,Asn1Module} ->
+ put(asn1_config_file,File),
+ input_file_type(Asn1Module);
+ Error ->
+ Error
+ end;
+ Asn1PFix ->
+ Base = filename:basename(File,Asn1PFix),
+ case filename:extension(Base) of
+ [] ->
+ {single_file,File};
+ SetPFix when (SetPFix == ".set") ->
+ {multiple_files_file,
+ filename:basename(Base,SetPFix),
+ File};
+ _Error ->
+ throw({input_file_error,{'Bad input file',File}})
+ end
+ end.
+
+get_file_list(File) ->
+ case file:open(File, [read]) of
+ {error,Reason} ->
+ {error,{File,file:format_error(Reason)}};
+ {ok,Stream} ->
+ get_file_list1(Stream,[])
+ end.
+
+get_file_list1(Stream,Acc) ->
+ Ret = io:get_line(Stream,''),
+ case Ret of
+ eof ->
+ file:close(Stream),
+ lists:reverse(Acc);
+ FileName ->
+ PrefixedNameList =
+ case (catch input_file_type(lists:delete($\n,FileName))) of
+ {empty_name,[]} -> [];
+ {single_file,Name} -> [Name];
+ {multiple_files_file,Name} ->
+ get_file_list(Name);
+ Err = {input_file_error,_Reason} ->
+ throw(Err)
+ end,
+ get_file_list1(Stream,PrefixedNameList++Acc)
+ end.
+
+get_rule(Options) ->
+ case [Rule ||Rule <-[per,ber,ber_bin,ber_bin_v2,per_bin],
+ Opt <- Options,
+ Rule==Opt] of
+ [Rule] ->
+ Rule;
+ [Rule|_] ->
+ Rule;
+ [] ->
+ ber
+ end.
+
+erl_compile(OutFile,Options) ->
+% io:format("Options:~n~p~n",[Options]),
+ case lists:member(noobj,Options) of
+ true ->
+ ok;
+ _ ->
+ ErlOptions = remove_asn_flags(Options),
+ case c:c(OutFile,ErlOptions) of
+ {ok,_Module} ->
+ ok;
+ _ ->
+ {error,'no_compilation'}
+ end
+ end.
+
+remove_asn_flags(Options) ->
+ [X || X <- Options,
+ X /= get_rule(Options),
+ X /= optimize,
+ X /= compact_bit_string,
+ X /= debug,
+ X /= keyed_list].
+
+debug_on(Options) ->
+ case lists:member(debug,Options) of
+ true ->
+ put(asndebug,true);
+ _ ->
+ true
+ end,
+ case lists:member(keyed_list,Options) of
+ true ->
+ put(asn_keyed_list,true);
+ _ ->
+ true
+ end.
+
+
+debug_off(_Options) ->
+ erase(asndebug),
+ erase(asn_keyed_list).
+
+
+outfile(Base, Ext, Opts) when atom(Ext) ->
+ outfile(Base, atom_to_list(Ext), Opts);
+outfile(Base, Ext, Opts) ->
+ Obase = case lists:keysearch(outdir, 1, Opts) of
+ {value, {outdir, Odir}} -> filename:join(Odir, Base);
+ _NotFound -> Base % Not found or bad format
+ end,
+ case Ext of
+ [] ->
+ Obase;
+ _ ->
+ Obase++"."++Ext
+ end.
+
+%% compile(AbsFileName, Options)
+%% Compile entry point for erl_compile.
+
+compile_asn(File,OutFile,Options) ->
+ compile(lists:concat([File,".asn"]),OutFile,Options).
+
+compile_asn1(File,OutFile,Options) ->
+ compile(lists:concat([File,".asn1"]),OutFile,Options).
+
+compile_py(File,OutFile,Options) ->
+ compile(lists:concat([File,".py"]),OutFile,Options).
+
+compile(File, _OutFile, Options) ->
+ case catch compile(File, make_erl_options(Options)) of
+ Exit = {'EXIT',_Reason} ->
+ io:format("~p~n~s~n",[Exit,"error"]),
+ error;
+ {error,_Reason} ->
+ %% case occurs due to error in asn1ct_parser2,asn1ct_check
+%% io:format("~p~n",[_Reason]),
+%% io:format("~p~n~s~n",[_Reason,"error"]),
+ error;
+ ok ->
+ io:format("ok~n"),
+ ok;
+ ParseRes when tuple(ParseRes) ->
+ io:format("~p~n",[ParseRes]),
+ ok;
+ ScanRes when list(ScanRes) ->
+ io:format("~p~n",[ScanRes]),
+ ok;
+ Unknown ->
+ io:format("~p~n~s~n",[Unknown,"error"]),
+ error
+ end.
+
+%% Converts generic compiler options to specific options.
+
+make_erl_options(Opts) ->
+
+ %% This way of extracting will work even if the record passed
+ %% has more fields than known during compilation.
+
+ Includes = Opts#options.includes,
+ Defines = Opts#options.defines,
+ Outdir = Opts#options.outdir,
+%% Warning = Opts#options.warning,
+ Verbose = Opts#options.verbose,
+ Specific = Opts#options.specific,
+ Optimize = Opts#options.optimize,
+ OutputType = Opts#options.output_type,
+ Cwd = Opts#options.cwd,
+
+ Options =
+ case Verbose of
+ true -> [verbose];
+ false -> []
+ end ++
+%%% case Warning of
+%%% 0 -> [];
+%%% _ -> [report_warnings]
+%%% end ++
+ [] ++
+ case Optimize of
+ 1 -> [optimize];
+ 999 -> [];
+ _ -> [{optimize,Optimize}]
+ end ++
+ lists:map(
+ fun ({Name, Value}) ->
+ {d, Name, Value};
+ (Name) ->
+ {d, Name}
+ end,
+ Defines) ++
+ case OutputType of
+ undefined -> [ber]; % temporary default (ber when it's ready)
+ ber -> [ber];
+ ber_bin -> [ber_bin];
+ ber_bin_v2 -> [ber_bin_v2];
+ per -> [per];
+ per_bin -> [per_bin]
+ end,
+
+ Options++[report_errors, {cwd, Cwd}, {outdir, Outdir}|
+ lists:map(fun(Dir) -> {i, Dir} end, Includes)]++Specific.
+
+pretty2(Module,AbsFile) ->
+ start(),
+ {ok,F} = file:open(AbsFile, [write]),
+ M = asn1_db:dbget(Module,'MODULE'),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.defid)]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.tagdefault)]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.exports)]),
+ io:format(F,"~s\n",[asn1ct_pretty_format:term(M#module.imports)]),
+ io:format(F,"~s\n\n",[asn1ct_pretty_format:term(M#module.extensiondefault)]),
+
+ {Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets} = M#module.typeorval,
+ io:format(F,"%%%%%%%%%%%%%%%%%%% TYPES in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Types),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% VALUES in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Values),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Parameterized Types in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,ParameterizedTypes),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Classes in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Classes),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Objects in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,Objects),
+ io:format(F,"%%%%%%%%%%%%%%%%%%% Object Sets in ~p %%%%%%%%%%%%%%%%%%%~n",[Module]),
+ lists:foreach(fun(T)-> io:format(F,"~s\n",
+ [asn1ct_pretty_format:term(asn1_db:dbget(Module,T))])
+ end,ObjectSets).
+start() ->
+ Includes = ["."],
+ start(Includes).
+
+
+start(Includes) when list(Includes) ->
+ asn1_db:dbstart(Includes).
+
+stop() ->
+ save(),
+ asn1_db:stop_server(ns),
+ asn1_db:stop_server(rand),
+ stopped.
+
+save() ->
+ asn1_db:dbstop().
+
+%%clear() ->
+%% asn1_db:dbclear().
+
+encode(Module,Term) ->
+ asn1rt:encode(Module,Term).
+
+encode(Module,Type,Term) when list(Module) ->
+ asn1rt:encode(list_to_atom(Module),Type,Term);
+encode(Module,Type,Term) ->
+ asn1rt:encode(Module,Type,Term).
+
+decode(Module,Type,Bytes) when list(Module) ->
+ asn1rt:decode(list_to_atom(Module),Type,Bytes);
+decode(Module,Type,Bytes) ->
+ asn1rt:decode(Module,Type,Bytes).
+
+
+test(Module) ->
+ start(),
+ M = asn1_db:dbget(Module,'MODULE'),
+ {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = M#module.typeorval,
+ test_each(Module,Types).
+
+test_each(Module,[Type | Rest]) ->
+ case test(Module,Type) of
+ {ok,_Result} ->
+ test_each(Module,Rest);
+ Error ->
+ Error
+ end;
+test_each(_,[]) ->
+ ok.
+
+test(Module,Type) ->
+ io:format("~p:~p~n",[Module,Type]),
+ case (catch value(Module,Type)) of
+ {ok,Val} ->
+ %% io:format("asn1ct:test/2: ~w~n",[Val]),
+ test(Module,Type,Val);
+ {'EXIT',Reason} ->
+ {error,{asn1,{value,Reason}}}
+ end.
+
+
+test(Module,Type,Value) ->
+ case catch encode(Module,Type,Value) of
+ {ok,Bytes} ->
+ %% io:format("test 1: ~p~n",[{Bytes}]),
+ M = if
+ list(Module) ->
+ list_to_atom(Module);
+ true ->
+ Module
+ end,
+ NewBytes =
+ case M:encoding_rule() of
+ ber ->
+ lists:flatten(Bytes);
+ ber_bin when binary(Bytes) ->
+ Bytes;
+ ber_bin ->
+ list_to_binary(Bytes);
+ ber_bin_v2 when binary(Bytes) ->
+ Bytes;
+ ber_bin_v2 ->
+ list_to_binary(Bytes);
+ per ->
+ lists:flatten(Bytes);
+ per_bin when binary(Bytes) ->
+ Bytes;
+ per_bin ->
+ list_to_binary(Bytes)
+ end,
+ case decode(Module,Type,NewBytes) of
+ {ok,Value} ->
+ {ok,{Module,Type,Value}};
+ {ok,Res} ->
+ {error,{asn1,{encode_decode_mismatch,
+ {{Module,Type,Value},Res}}}};
+ Error ->
+ {error,{asn1,{{decode,
+ {Module,Type,Value},Error}}}}
+ end;
+ Error ->
+ {error,{asn1,{encode,{{Module,Type,Value},Error}}}}
+ end.
+
+value(Module) ->
+ start(),
+ M = asn1_db:dbget(Module,'MODULE'),
+ {Types,_Values,_Ptypes,_Classes,_Objects,_ObjectSets} = M#module.typeorval,
+ lists:map(fun(A) ->value(Module,A) end,Types).
+
+value(Module,Type) ->
+ start(),
+ case catch asn1ct_value:get_type(Module,Type,no) of
+ {error,Reason} ->
+ {error,Reason};
+ {'EXIT',Reason} ->
+ {error,Reason};
+ Result ->
+ {ok,Result}
+ end.
+
+cmp(Module,InFile) ->
+ Base = filename:basename(InFile),
+ Dir = filename:dirname(InFile),
+ Ext = filename:extension(Base),
+ Finfo = file:read_file_info(InFile),
+ Minfo = file:read_file_info(filename:join(Dir,lists:concat([Module,Ext]))),
+ case Finfo of
+ Minfo ->
+ ok;
+ _ ->
+ io:format("asn1error: Modulename and filename must be equal~n",[]),
+ throw(error)
+ end.
+
+vsn() ->
+ ?vsn.
+
+print_error_message([got,H|T]) when list(H) ->
+ io:format(" got:"),
+ print_listing(H,"and"),
+ print_error_message(T);
+print_error_message([expected,H|T]) when list(H) ->
+ io:format(" expected one of:"),
+ print_listing(H,"or"),
+ print_error_message(T);
+print_error_message([H|T]) ->
+ io:format(" ~p",[H]),
+ print_error_message(T);
+print_error_message([]) ->
+ io:format("~n").
+
+print_listing([H1,H2|[]],AndOr) ->
+ io:format(" ~p ~s ~p",[H1,AndOr,H2]);
+print_listing([H1,H2|T],AndOr) ->
+ io:format(" ~p,",[H1]),
+ print_listing([H2|T],AndOr);
+print_listing([H],_AndOr) ->
+ io:format(" ~p",[H]);
+print_listing([],_) ->
+ ok.
+
+
+%% functions to administer ets tables
+
+%% Always creates a new table
+create_ets_table(Name,Options) when atom(Name) ->
+ case ets:info(Name) of
+ undefined ->
+ ets:new(Name,Options);
+ _ ->
+ ets:delete(Name),
+ ets:new(Name,Options)
+ end.
+
+%% Creates a new ets table only if no table exists
+create_if_no_table(Name,Options) ->
+ case ets:info(Name) of
+ undefined ->
+ %% create a new table
+ create_ets_table(Name,Options);
+ _ -> ok
+ end.
+
+
+delete_tables([Table|Ts]) ->
+ case ets:info(Table) of
+ undefined -> ok;
+ _ -> ets:delete(Table)
+ end,
+ delete_tables(Ts);
+delete_tables([]) ->
+ ok.
+
+
+specialized_decode_prepare(Erule,M,TsAndVs,Options) ->
+% Asn1confMember =
+% fun([{asn1config,File}|_],_) ->
+% {true,File};
+% ([],_) -> false;
+% ([_H|T],Fun) ->
+% Fun(T,Fun)
+% end,
+% case Asn1confMember(Options,Asn1confMember) of
+% {true,File} ->
+ case lists:member(asn1config,Options) of
+ true ->
+ partial_decode_prepare(Erule,M,TsAndVs,Options);
+ _ ->
+ ok
+ end.
+%% Reads the configuration file if it exists and stores information
+%% about partial decode and incomplete decode
+partial_decode_prepare(ber_bin_v2,M,TsAndVs,Options) when tuple(TsAndVs) ->
+ %% read configure file
+% Types = element(1,TsAndVs),
+ CfgList = read_config_file(M#module.name),
+ SelectedDecode = get_config_info(CfgList,partial_decode),
+ ExclusiveDecode = get_config_info(CfgList,exclusive_decode),
+ CommandList =
+ create_partial_decode_gen_info(M#module.name,SelectedDecode),
+% io:format("partial_decode = ~p~n",[CommandList]),
+
+ save_config(partial_decode,CommandList),
+ CommandList2 =
+ create_partial_inc_decode_gen_info(M#module.name,ExclusiveDecode),
+% io:format("partial_incomplete_decode = ~p~n",[CommandList2]),
+ Part_inc_tlv_tags = tag_format(ber_bin_v2,Options,CommandList2),
+% io:format("partial_incomplete_decode: tlv_tags = ~p~n",[Part_inc_tlv_tags]),
+ save_config(partial_incomplete_decode,Part_inc_tlv_tags),
+ save_gen_state(ExclusiveDecode,Part_inc_tlv_tags);
+partial_decode_prepare(_,_,_,_) ->
+ ok.
+
+
+
+%% create_partial_inc_decode_gen_info/2
+%%
+%% Creats a list of tags out of the information in TypeNameList that
+%% tells which value will be incomplete decoded, i.e. each end
+%% component/type in TypeNameList. The significant types/components in
+%% the path from the toptype must be specified in the
+%% TypeNameList. Significant elements are all constructed types that
+%% branches the path to the leaf and the leaf it selfs.
+%%
+%% Returns a list of elements, where an element may be one of
+%% mandatory|[opt,Tag]|[bin,Tag]. mandatory correspond to a mandatory
+%% element that shall be decoded as usual. [opt,Tag] matches an
+%% OPTIONAL or DEFAULT element that shall be decoded as
+%% usual. [bin,Tag] corresponds to an element, mandatory, OPTIONAL or
+%% DEFAULT, that shall be left encoded (incomplete decoded).
+create_partial_inc_decode_gen_info(ModName,{Mod,[{Name,L}|Ls]}) when list(L) ->
+ TopTypeName = partial_inc_dec_toptype(L),
+ [{Name,TopTypeName,
+ create_partial_inc_decode_gen_info1(ModName,TopTypeName,{Mod,L})}|
+ create_partial_inc_decode_gen_info(ModName,{Mod,Ls})];
+create_partial_inc_decode_gen_info(_,{_,[]}) ->
+ [];
+create_partial_inc_decode_gen_info(_,[]) ->
+ [].
+
+create_partial_inc_decode_gen_info1(ModName,TopTypeName,{ModName,
+ [_TopType|Rest]}) ->
+ case asn1_db:dbget(ModName,TopTypeName) of
+ #typedef{typespec=TS} ->
+ TagCommand = get_tag_command(TS,?MANDATORY,mandatory),
+ create_pdec_inc_command(ModName,get_components(TS#type.def),
+ Rest,[TagCommand]);
+ _ ->
+ throw({error,{"wrong type list in asn1 config file",
+ TopTypeName}})
+ end;
+create_partial_inc_decode_gen_info1(M1,_,{M2,_}) when M1 /= M2 ->
+ throw({error,{"wrong module name in asn1 config file",
+ M2}});
+create_partial_inc_decode_gen_info1(_,_,TNL) ->
+ throw({error,{"wrong type list in asn1 config file",
+ TNL}}).
+
+%%
+%% Only when there is a 'ComponentType' the config data C1 may be a
+%% list, where the incomplete decode is branched. So, C1 may be a
+%% list, a "binary tuple", a "parts tuple" or an atom. The second
+%% element of a binary tuple and a parts tuple is an atom.
+create_pdec_inc_command(_ModName,_,[],Acc) ->
+ lists:reverse(Acc);
+create_pdec_inc_command(ModName,{Comps1,Comps2},TNL,Acc)
+ when list(Comps1),list(Comps2) ->
+ create_pdec_inc_command(ModName,Comps1 ++ Comps2,TNL,Acc);
+create_pdec_inc_command(ModN,Clist,[CL|_Rest],Acc) when list(CL) ->
+ create_pdec_inc_command(ModN,Clist,CL,Acc);
+create_pdec_inc_command(ModName,
+ CList=[#'ComponentType'{name=Name,typespec=TS,
+ prop=Prop}|Comps],
+ TNL=[C1|Cs],Acc) ->
+ case C1 of
+% Name ->
+% %% In this case C1 is an atom
+% TagCommand = get_tag_command(TS,?MANDATORY,Prop),
+% create_pdec_inc_command(ModName,get_components(TS#type.def),Cs,[TagCommand|Acc]);
+ {Name,undecoded} ->
+ TagCommand = get_tag_command(TS,?UNDECODED,Prop),
+ create_pdec_inc_command(ModName,Comps,Cs,[TagCommand|Acc]);
+ {Name,parts} ->
+ TagCommand = get_tag_command(TS,?PARTS,Prop),
+ create_pdec_inc_command(ModName,Comps,Cs,[TagCommand|Acc]);
+ L when list(L) ->
+ %% This case is only possible as the first element after
+ %% the top type element, when top type is SEGUENCE or SET.
+ %% Follow each element in L. Must note every tag on the
+ %% way until the last command is reached, but it ought to
+ %% be enough to have a "complete" or "complete optional"
+ %% command for each component that is not specified in the
+ %% config file. Then in the TLV decode the components with
+ %% a "complete" command will be decoded by an ordinary TLV
+ %% decode.
+ create_pdec_inc_command(ModName,CList,L,Acc);
+ {Name,RestPartsList} when list(RestPartsList) ->
+ %% Same as previous, but this may occur at any place in
+ %% the structure. The previous is only possible as the
+ %% second element.
+ case get_tag_command(TS,?MANDATORY,Prop) of
+ ?MANDATORY ->
+ InnerDirectives=
+ create_pdec_inc_command(ModName,TS#type.def,
+ RestPartsList,[]),
+ create_pdec_inc_command(ModName,Comps,Cs,
+ [[?MANDATORY,InnerDirectives]|Acc]);
+% create_pdec_inc_command(ModName,Comps,Cs,
+% [InnerDirectives,?MANDATORY|Acc]);
+ [Opt,EncTag] ->
+ InnerDirectives =
+ create_pdec_inc_command(ModName,TS#type.def,
+ RestPartsList,[]),
+ create_pdec_inc_command(ModName,Comps,Cs,
+ [[Opt,EncTag,InnerDirectives]|Acc])
+ end;
+% create_pdec_inc_command(ModName,CList,RestPartsList,Acc);
+%% create_pdec_inc_command(ModName,TS#type.def,RestPartsList,Acc);
+ _ -> %% this component may not be in the config list
+ TagCommand = get_tag_command(TS,?MANDATORY,Prop),
+ create_pdec_inc_command(ModName,Comps,TNL,[TagCommand|Acc])
+ end;
+create_pdec_inc_command(ModName,
+ {'CHOICE',[#'ComponentType'{name=C1,
+ typespec=TS,
+ prop=Prop}|Comps]},
+ [{C1,Directive}|Rest],Acc) ->
+ case Directive of
+ List when list(List) ->
+ [Command,Tag] = get_tag_command(TS,?ALTERNATIVE,Prop),
+ CompAcc = create_pdec_inc_command(ModName,TS#type.def,List,[]),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
+ [[Command,Tag,CompAcc]|Acc]);
+ undecoded ->
+ TagCommand = get_tag_command(TS,?ALTERNATIVE_UNDECODED,Prop),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
+ [TagCommand|Acc]);
+ parts ->
+ TagCommand = get_tag_command(TS,?ALTERNATIVE_PARTS,Prop),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},Rest,
+ [TagCommand|Acc])
+ end;
+create_pdec_inc_command(ModName,
+ {'CHOICE',[#'ComponentType'{typespec=TS,
+ prop=Prop}|Comps]},
+ TNL,Acc) ->
+ TagCommand = get_tag_command(TS,?ALTERNATIVE,Prop),
+ create_pdec_inc_command(ModName,{'CHOICE',Comps},TNL,[TagCommand|Acc]);
+create_pdec_inc_command(M,{'CHOICE',{Cs1,Cs2}},TNL,Acc)
+ when list(Cs1),list(Cs2) ->
+ create_pdec_inc_command(M,{'CHOICE',Cs1 ++ Cs2},TNL,Acc);
+create_pdec_inc_command(ModName,#'Externaltypereference'{module=M,type=Name},
+ TNL,Acc) ->
+ #type{def=Def} = get_referenced_type(M,Name),
+ create_pdec_inc_command(ModName,get_components(Def),TNL,Acc);
+create_pdec_inc_command(_,_,TNL,_) ->
+ throw({error,{"unexpected error when creating partial "
+ "decode command",TNL}}).
+
+partial_inc_dec_toptype([T|_]) when atom(T) ->
+ T;
+partial_inc_dec_toptype([{T,_}|_]) when atom(T) ->
+ T;
+partial_inc_dec_toptype([L|_]) when list(L) ->
+ partial_inc_dec_toptype(L);
+partial_inc_dec_toptype(_) ->
+ throw({error,{"no top type found for partial incomplete decode"}}).
+
+
+%% Creats a list of tags out of the information in TypeList and Types
+%% that tells which value will be decoded. Each constructed type that
+%% is in the TypeList will get a "choosen" command. Only the last
+%% type/component in the TypeList may be a primitive type. Components
+%% "on the way" to the final element may get the "skip" or the
+%% "skip_optional" command.
+%% CommandList = [Elements]
+%% Elements = {choosen,Tag}|{skip_optional,Tag}|skip
+%% Tag is a binary with the tag BER encoded.
+create_partial_decode_gen_info(ModName,{{_,ModName},TypeList}) ->
+ case TypeList of
+ [TopType|Rest] ->
+ case asn1_db:dbget(ModName,TopType) of
+ #typedef{typespec=TS} ->
+ TagCommand = get_tag_command(TS,?CHOOSEN),
+ create_pdec_command(ModName,get_components(TS#type.def),
+ Rest,[TagCommand]);
+ _ ->
+ throw({error,{"wrong type list in asn1 config file",
+ TypeList}})
+ end;
+ _ ->
+ []
+ end;
+create_partial_decode_gen_info(_,[]) ->
+ [];
+create_partial_decode_gen_info(_M1,{{_,M2},_}) ->
+ throw({error,{"wrong module name in asn1 config file",
+ M2}}).
+
+%% create_pdec_command/4 for each name (type or component) in the
+%% third argument, TypeNameList, a command is created. The command has
+%% information whether the component/type shall be skipped, looked
+%% into or returned. The list of commands is returned.
+create_pdec_command(_ModName,_,[],Acc) ->
+ lists:reverse(Acc);
+create_pdec_command(ModName,[#'ComponentType'{name=C1,typespec=TS}|_Comps],
+ [C1|Cs],Acc) ->
+ %% this component is a constructed type or the last in the
+ %% TypeNameList otherwise the config spec is wrong
+ TagCommand = get_tag_command(TS,?CHOOSEN),
+ create_pdec_command(ModName,get_components(TS#type.def),
+ Cs,[TagCommand|Acc]);
+create_pdec_command(ModName,[#'ComponentType'{typespec=TS,
+ prop=Prop}|Comps],
+ [C2|Cs],Acc) ->
+ TagCommand =
+ case Prop of
+ mandatory ->
+ get_tag_command(TS,?SKIP);
+ _ ->
+ get_tag_command(TS,?SKIP_OPTIONAL)
+ end,
+ create_pdec_command(ModName,Comps,[C2|Cs],[TagCommand|Acc]);
+create_pdec_command(ModName,{'CHOICE',[Comp=#'ComponentType'{name=C1}|_]},TNL=[C1|_Cs],Acc) ->
+ create_pdec_command(ModName,[Comp],TNL,Acc);
+create_pdec_command(ModName,{'CHOICE',[#'ComponentType'{}|Comps]},TNL,Acc) ->
+ create_pdec_command(ModName,{'CHOICE',Comps},TNL,Acc);
+create_pdec_command(ModName,#'Externaltypereference'{module=M,type=C1},
+ TypeNameList,Acc) ->
+ case get_referenced_type(M,C1) of
+ #type{def=Def} ->
+ create_pdec_command(ModName,get_components(Def),TypeNameList,
+ Acc);
+ Err ->
+ throw({error,{"unexpected result when fetching "
+ "referenced element",Err}})
+ end;
+create_pdec_command(ModName,TS=#type{def=Def},[C1|Cs],Acc) ->
+ %% This case when we got the "components" of a SEQUENCE/SET OF
+ case C1 of
+ [1] ->
+ %% A list with an integer is the only valid option in a 'S
+ %% OF', the other valid option would be an empty
+ %% TypeNameList saying that the entire 'S OF' will be
+ %% decoded.
+ TagCommand = get_tag_command(TS,?CHOOSEN),
+ create_pdec_command(ModName,Def,Cs,[TagCommand|Acc]);
+ [N] when integer(N) ->
+ TagCommand = get_tag_command(TS,?SKIP),
+ create_pdec_command(ModName,Def,[[N-1]|Cs],[TagCommand|Acc]);
+ Err ->
+ throw({error,{"unexpected error when creating partial "
+ "decode command",Err}})
+ end;
+create_pdec_command(_,_,TNL,_) ->
+ throw({error,{"unexpected error when creating partial "
+ "decode command",TNL}}).
+
+% get_components({'CHOICE',Components}) ->
+% Components;
+get_components(#'SEQUENCE'{components=Components}) ->
+ Components;
+get_components(#'SET'{components=Components}) ->
+ Components;
+get_components({'SEQUENCE OF',Components}) ->
+ Components;
+get_components({'SET OF',Components}) ->
+ Components;
+get_components(Def) ->
+ Def.
+
+%% get_tag_command(Type,Command)
+
+%% Type is the type that has information about the tag Command tells
+%% what to do with the encoded value with the tag of Type when
+%% decoding.
+get_tag_command(#type{tag=[]},_) ->
+ [];
+get_tag_command(#type{tag=[_Tag]},?SKIP) ->
+ ?SKIP;
+get_tag_command(#type{tag=[Tag]},Command) ->
+ %% encode the tag according to BER
+ [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
+ Tag#tag.number)];
+get_tag_command(T=#type{tag=[Tag|Tags]},Command) ->
+ [get_tag_command(T#type{tag=Tag},Command)|
+ get_tag_command(T#type{tag=Tags},Command)].
+
+%% get_tag_command/3 used by create_pdec_inc_command
+get_tag_command(#type{tag=[]},_,_) ->
+ [];
+get_tag_command(#type{tag=[Tag]},?MANDATORY,Prop) ->
+ case Prop of
+ mandatory ->
+ ?MANDATORY;
+ {'DEFAULT',_} ->
+ [?DEFAULT,encode_tag_val(decode_class(Tag#tag.class),
+ Tag#tag.form,Tag#tag.number)];
+ _ -> [?OPTIONAL,encode_tag_val(decode_class(Tag#tag.class),
+ Tag#tag.form,Tag#tag.number)]
+ end;
+get_tag_command(#type{tag=[Tag]},Command,_) ->
+ [Command,encode_tag_val(decode_class(Tag#tag.class),Tag#tag.form,
+ Tag#tag.number)].
+
+
+get_referenced_type(M,Name) ->
+ case asn1_db:dbget(M,Name) of
+ #typedef{typespec=TS} ->
+ case TS of
+ #type{def=#'Externaltypereference'{module=M2,type=Name2}} ->
+ %% The tags have already been taken care of in the
+ %% first reference where they were gathered in a
+ %% list of tags.
+ get_referenced_type(M2,Name2);
+ #type{} -> TS;
+ _ ->
+ throw({error,{"unexpected element when"
+ " fetching referenced type",TS}})
+ end;
+ T ->
+ throw({error,{"unexpected element when fetching "
+ "referenced type",T}})
+ end.
+
+tag_format(EncRule,_Options,CommandList) ->
+ case EncRule of
+ ber_bin_v2 ->
+ tlv_tags(CommandList);
+ _ ->
+ CommandList
+ end.
+
+tlv_tags([]) ->
+ [];
+tlv_tags([mandatory|Rest]) ->
+ [mandatory|tlv_tags(Rest)];
+tlv_tags([[Command,Tag]|Rest]) when atom(Command),binary(Tag) ->
+ [[Command,tlv_tag(Tag)]|tlv_tags(Rest)];
+tlv_tags([[Command,Directives]|Rest]) when atom(Command),list(Directives) ->
+ [[Command,tlv_tags(Directives)]|tlv_tags(Rest)];
+%% remove all empty lists
+tlv_tags([[]|Rest]) ->
+ tlv_tags(Rest);
+tlv_tags([{Name,TopType,L1}|Rest]) when list(L1),atom(TopType) ->
+ [{Name,TopType,tlv_tags(L1)}|tlv_tags(Rest)];
+tlv_tags([[Command,Tag,L1]|Rest]) when list(L1),binary(Tag) ->
+ [[Command,tlv_tag(Tag),tlv_tags(L1)]|tlv_tags(Rest)];
+tlv_tags([L=[L1|_]|Rest]) when list(L1) ->
+ [tlv_tags(L)|tlv_tags(Rest)].
+
+tlv_tag(<<Cl:2,_:1,TagNo:5>>) when TagNo < 31 ->
+ (Cl bsl 16) + TagNo;
+tlv_tag(<<Cl:2,_:1,31:5,0:1,TagNo:7>>) ->
+ (Cl bsl 16) + TagNo;
+tlv_tag(<<Cl:2,_:1,31:5,Buffer/binary>>) ->
+ TagNo = tlv_tag1(Buffer,0),
+ (Cl bsl 16) + TagNo.
+tlv_tag1(<<0:1,PartialTag:7>>,Acc) ->
+ (Acc bsl 7) bor PartialTag;
+tlv_tag1(<<1:1,PartialTag:7,Buffer/binary>>,Acc) ->
+ tlv_tag1(Buffer,(Acc bsl 7) bor PartialTag).
+
+%% reads the content from the configuration file and returns the
+%% selected part choosen by InfoType. Assumes that the config file
+%% content is an Erlang term.
+read_config_file(ModuleName,InfoType) when atom(InfoType) ->
+ CfgList = read_config_file(ModuleName),
+ get_config_info(CfgList,InfoType).
+
+
+read_config_file(ModuleName) ->
+ case file:consult(lists:concat([ModuleName,'.asn1config'])) of
+% case file:consult(ModuleName) of
+ {ok,CfgList} ->
+ CfgList;
+ {error,enoent} ->
+ Options = get(encoding_options),
+ Includes = [I || {i,I} <- Options],
+ read_config_file1(ModuleName,Includes);
+ {error,Reason} ->
+ file:format_error(Reason),
+ throw({error,{"error reading asn1 config file",Reason}})
+ end.
+read_config_file1(ModuleName,[]) ->
+ case filename:extension(ModuleName) of
+ ".asn1config" ->
+ throw({error,enoent});
+ _ ->
+ read_config_file(lists:concat([ModuleName,".asn1config"]))
+ end;
+read_config_file1(ModuleName,[H|T]) ->
+% File = filename:join([H,lists:concat([ModuleName,'.asn1config'])]),
+ File = filename:join([H,ModuleName]),
+ case file:consult(File) of
+ {ok,CfgList} ->
+ CfgList;
+ {error,enoent} ->
+ read_config_file1(ModuleName,T);
+ {error,Reason} ->
+ file:format_error(Reason),
+ throw({error,{"error reading asn1 config file",Reason}})
+ end.
+
+get_config_info(CfgList,InfoType) ->
+ case InfoType of
+ all ->
+ CfgList;
+ _ ->
+ case lists:keysearch(InfoType,1,CfgList) of
+ {value,{InfoType,Value}} ->
+ Value;
+ false ->
+ []
+ end
+ end.
+
+%% save_config/2 saves the Info with the key Key
+%% Before saving anything check if a table exists
+save_config(Key,Info) ->
+ create_if_no_table(asn1_general,[named_table]),
+ ets:insert(asn1_general,{{asn1_config,Key},Info}).
+
+read_config_data(Key) ->
+ case ets:info(asn1_general) of
+ undefined -> undefined;
+ _ ->
+ case ets:lookup(asn1_general,{asn1_config,Key}) of
+ [{_,Data}] -> Data;
+ Err ->
+ io:format("strange data from config file ~w~n",[Err]),
+ Err
+ end
+ end.
+
+
+%%
+%% Functions to manipulate the gen_state record saved in the
+%% asn1_general ets table.
+%%
+
+%% saves input data in a new gen_state record
+save_gen_state({_,ConfList},PartIncTlvTagList) ->
+ %ConfList=[{FunctionName,PatternList}|Rest]
+ StateRec = #gen_state{inc_tag_pattern=PartIncTlvTagList,
+ inc_type_pattern=ConfList},
+ save_config(gen_state,StateRec);
+save_gen_state(_,_) ->
+%% ok.
+ save_config(gen_state,#gen_state{}).
+
+save_gen_state(GenState) when record(GenState,gen_state) ->
+ save_config(gen_state,GenState).
+
+
+%% get_gen_state_field returns undefined if no gen_state exists or if
+%% Field is undefined or the data at the field.
+get_gen_state_field(Field) ->
+ case read_config_data(gen_state) of
+ undefined ->
+ undefined;
+ GenState ->
+ get_gen_state_field(GenState,Field)
+ end.
+get_gen_state_field(#gen_state{active=Active},active) ->
+ Active;
+get_gen_state_field(_,active) ->
+ false;
+get_gen_state_field(GS,prefix) ->
+ GS#gen_state.prefix;
+get_gen_state_field(GS,inc_tag_pattern) ->
+ GS#gen_state.inc_tag_pattern;
+get_gen_state_field(GS,tag_pattern) ->
+ GS#gen_state.tag_pattern;
+get_gen_state_field(GS,inc_type_pattern) ->
+ GS#gen_state.inc_type_pattern;
+get_gen_state_field(GS,type_pattern) ->
+ GS#gen_state.type_pattern;
+get_gen_state_field(GS,func_name) ->
+ GS#gen_state.func_name;
+get_gen_state_field(GS,namelist) ->
+ GS#gen_state.namelist;
+get_gen_state_field(GS,tobe_refed_funcs) ->
+ GS#gen_state.tobe_refed_funcs;
+get_gen_state_field(GS,gen_refed_funcs) ->
+ GS#gen_state.gen_refed_funcs.
+
+
+get_gen_state() ->
+ read_config_data(gen_state).
+
+
+update_gen_state(Field,Data) ->
+ case get_gen_state() of
+ State when record(State,gen_state) ->
+ update_gen_state(Field,State,Data);
+ _ ->
+ exit({error,{asn1,{internal,
+ "tried to update nonexistent gen_state",Field,Data}}})
+ end.
+update_gen_state(active,State,Data) ->
+ save_gen_state(State#gen_state{active=Data});
+update_gen_state(prefix,State,Data) ->
+ save_gen_state(State#gen_state{prefix=Data});
+update_gen_state(inc_tag_pattern,State,Data) ->
+ save_gen_state(State#gen_state{inc_tag_pattern=Data});
+update_gen_state(tag_pattern,State,Data) ->
+ save_gen_state(State#gen_state{tag_pattern=Data});
+update_gen_state(inc_type_pattern,State,Data) ->
+ save_gen_state(State#gen_state{inc_type_pattern=Data});
+update_gen_state(type_pattern,State,Data) ->
+ save_gen_state(State#gen_state{type_pattern=Data});
+update_gen_state(func_name,State,Data) ->
+ save_gen_state(State#gen_state{func_name=Data});
+update_gen_state(namelist,State,Data) ->
+% SData =
+% case Data of
+% [D] when list(D) -> D;
+% _ -> Data
+% end,
+ save_gen_state(State#gen_state{namelist=Data});
+update_gen_state(tobe_refed_funcs,State,Data) ->
+ save_gen_state(State#gen_state{tobe_refed_funcs=Data});
+update_gen_state(gen_refed_funcs,State,Data) ->
+ save_gen_state(State#gen_state{gen_refed_funcs=Data}).
+
+update_namelist(Name) ->
+ case get_gen_state_field(namelist) of
+ [Name,Rest] -> update_gen_state(namelist,Rest);
+ [Name|Rest] -> update_gen_state(namelist,Rest);
+ [{Name,List}] when list(List) -> update_gen_state(namelist,List);
+ [{Name,Atom}|Rest] when atom(Atom) -> update_gen_state(namelist,Rest);
+ Other -> Other
+ end.
+
+pop_namelist() ->
+ DeepTail = %% removes next element in order
+ fun([[{_,A}]|T],_Fun) when atom(A) -> T;
+ ([{_N,L}|T],_Fun) when list(L) -> [L|T];
+ ([[]|T],Fun) -> Fun(T,Fun);
+ ([L1|L2],Fun) when list(L1) ->
+ case lists:flatten(L1) of
+ [] -> Fun([L2],Fun);
+ _ -> [Fun(L1,Fun)|L2]
+ end;
+ ([_H|T],_Fun) -> T
+ end,
+ {Pop,NewNL} =
+ case get_gen_state_field(namelist) of
+ [] -> {[],[]};
+ L ->
+ {next_namelist_el(L),
+ DeepTail(L,DeepTail)}
+ end,
+ update_gen_state(namelist,NewNL),
+ Pop.
+
+%% next_namelist_el fetches the next type/component name in turn in
+%% the namelist, without changing the namelist.
+next_namelist_el() ->
+ case get_gen_state_field(namelist) of
+ undefined -> undefined;
+ L when list(L) -> next_namelist_el(L)
+ end.
+
+next_namelist_el([]) ->
+ [];
+next_namelist_el([L]) when list(L) ->
+ next_namelist_el(L);
+next_namelist_el([H|_]) when atom(H) ->
+ H;
+next_namelist_el([L|T]) when list(L) ->
+ case next_namelist_el(L) of
+ [] ->
+ next_namelist_el([T]);
+ R ->
+ R
+ end;
+next_namelist_el([H={_,A}|_]) when atom(A) ->
+ H.
+
+%% removes a bracket from the namelist
+step_in_constructed() ->
+ case get_gen_state_field(namelist) of
+ [L] when list(L) ->
+ update_gen_state(namelist,L);
+ _ -> ok
+ end.
+
+is_function_generated(Name) ->
+ case get_gen_state_field(gen_refed_funcs) of
+ L when list(L) ->
+ lists:member(Name,L);
+ _ ->
+ false
+ end.
+
+get_tobe_refed_func(Name) ->
+ case get_gen_state_field(tobe_refed_funcs) of
+ L when list(L) ->
+ case lists:keysearch(Name,1,L) of
+ {_,Element} ->
+ Element;
+ _ ->
+ undefined
+ end;
+ _ ->
+ undefined
+ end.
+
+add_tobe_refed_func(Data) ->
+ L = get_gen_state_field(tobe_refed_funcs),
+ update_gen_state(tobe_refed_funcs,[Data|L]).
+
+%% moves Name from the to be list to the generated list.
+generated_refed_func(Name) ->
+ L = get_gen_state_field(tobe_refed_funcs),
+ NewL = lists:keydelete(Name,1,L),
+ update_gen_state(tobe_refed_funcs,NewL),
+ L2 = get_gen_state_field(gen_refed_funcs),
+ update_gen_state(gen_refed_funcs,[Name|L2]).
+
+add_generated_refed_func(Data) ->
+ L = get_gen_state_field(gen_refed_funcs),
+ update_gen_state(gen_refed_funcs,[Data|L]).
+
+
+next_refed_func() ->
+ case get_gen_state_field(tobe_refed_funcs) of
+ [] ->
+ [];
+ [H|T] ->
+ update_gen_state(tobe_refed_funcs,T),
+ H
+ end.
+
+reset_gen_state() ->
+ save_gen_state(#gen_state{}).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_check.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_check.erl
new file mode 100644
index 0000000000..9da6611dba
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_check.erl
@@ -0,0 +1,5567 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_check.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_check).
+
+%% Main Module for ASN.1 compile time functions
+
+%-compile(export_all).
+-export([check/2,storeindb/1]).
+-include("asn1_records.hrl").
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8). % constructed
+-define(N_INSTANCE_OF,8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11). % constructed
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_CHARACTER_STRING, 29). % constructed
+-define(N_BMPString, 30).
+
+-define(TAG_PRIMITIVE(Num),
+ case S#state.erule of
+ ber_bin_v2 ->
+ #tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=0};
+ _ -> []
+ end).
+-define(TAG_CONSTRUCTED(Num),
+ case S#state.erule of
+ ber_bin_v2 ->
+ #tag{class='UNIVERSAL',number=Num,type='IMPLICIT',form=32};
+ _ -> []
+ end).
+
+-record(newt,{type=unchanged,tag=unchanged,constraint=unchanged,inlined=no}). % used in check_type to update type and tag
+-record(newv,{type=unchanged,value=unchanged}). % used in check_value to update type and value
+
+check(S,{Types,Values,ParameterizedTypes,Classes,Objects,ObjectSets}) ->
+ %%Predicates used to filter errors
+ TupleIs = fun({T,_},T) -> true;
+ (_,_) -> false
+ end,
+ IsClass = fun(X) -> TupleIs(X,asn1_class) end,
+ IsObjSet = fun(X) -> TupleIs(X,objectsetdef) end,
+ IsPObjSet = fun(X) -> TupleIs(X,pobjectsetdef) end,
+ IsObject = fun(X) -> TupleIs(X,objectdef) end,
+ IsValueSet = fun(X) -> TupleIs(X,valueset) end,
+ Element2 = fun(X) -> element(2,X) end,
+
+ _Perror = checkp(S,ParameterizedTypes,[]), % must do this before the templates are used
+ Terror = checkt(S,Types,[]),
+
+ %% get parameterized object sets sent to checkt/3
+ %% and update Terror
+
+ {PObjSetNames1,Terror2} = filter_errors(IsPObjSet,Terror),
+
+ Verror = checkv(S,Values ++ ObjectSets,[]), %value sets may be parsed as object sets
+
+ %% get information object classes wrongly sent to checkt/3
+ %% and update Terror2
+
+ {AddClasses,Terror3} = filter_errors(IsClass,Terror2),
+
+ NewClasses = Classes++AddClasses,
+
+ Cerror = checkc(S,NewClasses,[]),
+
+ %% get object sets incorrectly sent to checkv/3
+ %% and update Verror
+
+ {ObjSetNames,Verror2} = filter_errors(IsObjSet,Verror),
+
+ %% get parameterized object sets incorrectly sent to checkv/3
+ %% and update Verror2
+
+ {PObjSetNames,Verror3} = filter_errors(IsPObjSet,Verror2),
+
+ %% get objects incorrectly sent to checkv/3
+ %% and update Verror3
+
+ {ObjectNames,Verror4} = filter_errors(IsObject,Verror3),
+
+ NewObjects = Objects++ObjectNames,
+ NewObjectSets = ObjSetNames ++ PObjSetNames ++ PObjSetNames1,
+
+ %% get value sets
+ %% and update Verror4
+
+ {ValueSetNames,Verror5} = filter_errors(IsValueSet,Verror4),
+
+ asn1ct:create_ets_table(inlined_objects,[named_table]),
+ {Oerror,ExclO,ExclOS} = checko(S,NewObjects ++
+ NewObjectSets,
+ [],[],[]),
+ InlinedObjTuples = ets:tab2list(inlined_objects),
+ InlinedObjects = lists:map(Element2,InlinedObjTuples),
+ ets:delete(inlined_objects),
+
+ Exporterror = check_exports(S,S#state.module),
+ case {Terror3,Verror5,Cerror,Oerror,Exporterror} of
+ {[],[],[],[],[]} ->
+ ContextSwitchTs = context_switch_in_spec(),
+ InstanceOf = instance_of_in_spec(),
+ NewTypes = lists:subtract(Types,AddClasses) ++ ContextSwitchTs
+ ++ InstanceOf,
+ NewValues = lists:subtract(Values,PObjSetNames++ObjectNames++
+ ValueSetNames),
+ {ok,
+ {NewTypes,NewValues,ParameterizedTypes,
+ NewClasses,NewObjects,NewObjectSets},
+ {NewTypes,NewValues,ParameterizedTypes,NewClasses,
+ lists:subtract(NewObjects,ExclO)++InlinedObjects,
+ lists:subtract(NewObjectSets,ExclOS)}};
+ _ ->{error,{asn1,lists:flatten([Terror3,Verror5,Cerror,
+ Oerror,Exporterror])}}
+ end.
+
+context_switch_in_spec() ->
+ L = [{external,'EXTERNAL'},
+ {embedded_pdv,'EMBEDDED PDV'},
+ {character_string,'CHARACTER STRING'}],
+ F = fun({T,TName},Acc) ->
+ case get(T) of
+ generate -> erase(T),
+ [TName|Acc];
+ _ -> Acc
+ end
+ end,
+ lists:foldl(F,[],L).
+
+instance_of_in_spec() ->
+ case get(instance_of) of
+ generate ->
+ erase(instance_of),
+ ['INSTANCE OF'];
+ _ ->
+ []
+ end.
+
+filter_errors(Pred,ErrorList) ->
+ Element2 = fun(X) -> element(2,X) end,
+ RemovedTupleElements = lists:filter(Pred,ErrorList),
+ RemovedNames = lists:map(Element2,RemovedTupleElements),
+ %% remove value set name tuples from Verror
+ RestErrors = lists:subtract(ErrorList,RemovedTupleElements),
+ {RemovedNames,RestErrors}.
+
+
+check_exports(S,Module = #module{}) ->
+ case Module#module.exports of
+ {exports,[]} ->
+ [];
+ {exports,all} ->
+ [];
+ {exports,ExportList} when list(ExportList) ->
+ IsNotDefined =
+ fun(X) ->
+ case catch get_referenced_type(S,X) of
+ {error,{asn1,_}} ->
+ true;
+ _ -> false
+ end
+ end,
+ case lists:filter(IsNotDefined,ExportList) of
+ [] ->
+ [];
+ NoDefExp ->
+ GetName =
+ fun(T = #'Externaltypereference'{type=N})->
+ %%{exported,undefined,entity,N}
+ NewS=S#state{type=T,tname=N},
+ error({export,"exported undefined entity",NewS})
+ end,
+ lists:map(GetName,NoDefExp)
+ end
+ end.
+
+checkt(S,[Name|T],Acc) ->
+ %%io:format("check_typedef:~p~n",[Name]),
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Type when record(Type,typedef) ->
+ NewS = S#state{type=Type,tname=Name},
+ case catch(check_type(NewS,Type,Type#typedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1_class,_ClassDef} ->
+ {asn1_class,Name};
+ pobjectsetdef ->
+ {pobjectsetdef,Name};
+ pvalueset ->
+ {pvalueset,Name};
+ Ts ->
+ case Type#typedef.checked of
+ true -> % already checked and updated
+ ok;
+ _ ->
+ NewTypeDef = Type#typedef{checked=true,typespec = Ts},
+ %io:format("checkt:dbput:~p, ~p~n",[S#state.mname,NewTypeDef#typedef.name]),
+ asn1_db:dbput(NewS#state.mname,Name,NewTypeDef), % update the type
+ ok
+ end
+ end
+ end,
+ case Result of
+ ok ->
+ checkt(S,T,Acc);
+ _ ->
+ checkt(S,T,[Result|Acc])
+ end;
+checkt(S,[],Acc) ->
+ case check_contextswitchingtypes(S,[]) of
+ [] ->
+ lists:reverse(Acc);
+ L ->
+ checkt(S,L,Acc)
+ end.
+
+check_contextswitchingtypes(S,Acc) ->
+ CSTList=[{external,'EXTERNAL'},
+ {embedded_pdv,'EMBEDDED PDV'},
+ {character_string,'CHARACTER STRING'}],
+ check_contextswitchingtypes(S,CSTList,Acc).
+
+check_contextswitchingtypes(S,[{T,TName}|Ts],Acc) ->
+ case get(T) of
+ unchecked ->
+ put(T,generate),
+ check_contextswitchingtypes(S,Ts,[TName|Acc]);
+ _ ->
+ check_contextswitchingtypes(S,Ts,Acc)
+ end;
+check_contextswitchingtypes(_,[],Acc) ->
+ Acc.
+
+checkv(S,[Name|T],Acc) ->
+ %%io:format("check_valuedef:~p~n",[Name]),
+ Result = case asn1_db:dbget(S#state.mname,Name) of
+ undefined -> error({value,{internal_error,'???'},S});
+ Value when record(Value,valuedef);
+ record(Value,typedef); %Value set may be parsed as object set.
+ record(Value,pvaluedef);
+ record(Value,pvaluesetdef) ->
+ NewS = S#state{value=Value},
+ case catch(check_value(NewS,Value)) of
+ {error,Reason} ->
+ error({value,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({value,{internal_error,Reason},NewS});
+ {pobjectsetdef} ->
+ {pobjectsetdef,Name};
+ {objectsetdef} ->
+ {objectsetdef,Name};
+ {objectdef} ->
+ %% this is an object, save as typedef
+ #valuedef{checked=C,pos=Pos,name=N,type=Type,
+ value=Def}=Value,
+% Currmod = S#state.mname,
+% #type{def=
+% #'Externaltypereference'{module=Mod,
+% type=CName}} = Type,
+ ClassName =
+ Type#type.def,
+% case Mod of
+% Currmod ->
+% {objectclassname,CName};
+% _ ->
+% {objectclassname,Mod,CName}
+% end,
+ NewSpec = #'Object'{classname=ClassName,
+ def=Def},
+ NewDef = #typedef{checked=C,pos=Pos,name=N,
+ typespec=NewSpec},
+ asn1_db:dbput(NewS#state.mname,Name,NewDef),
+ {objectdef,Name};
+ {valueset,VSet} ->
+ Pos = asn1ct:get_pos_of_def(Value),
+ CheckedVSDef = #typedef{checked=true,pos=Pos,
+ name=Name,typespec=VSet},
+ asn1_db:dbput(NewS#state.mname,Name,CheckedVSDef),
+ {valueset,Name};
+ V ->
+ %% update the valuedef
+ asn1_db:dbput(NewS#state.mname,Name,V),
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkv(S,T,Acc);
+ _ ->
+ checkv(S,T,[Result|Acc])
+ end;
+checkv(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+
+checkp(S,[Name|T],Acc) ->
+ %io:format("check_ptypedef:~p~n",[Name]),
+ Result = case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Type when record(Type,ptypedef) ->
+ NewS = S#state{type=Type,tname=Name},
+ case catch(check_ptype(NewS,Type,Type#ptypedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1_class,_ClassDef} ->
+ {asn1_class,Name};
+ Ts ->
+ NewType = Type#ptypedef{checked=true,typespec = Ts},
+ asn1_db:dbput(NewS#state.mname,Name,NewType), % update the type
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkp(S,T,Acc);
+ _ ->
+ checkp(S,T,[Result|Acc])
+ end;
+checkp(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+
+
+
+checkc(S,[Name|Cs],Acc) ->
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({class,{internal_error,'???'},S});
+ Class ->
+ ClassSpec = if
+ record(Class,classdef) ->
+ Class#classdef.typespec;
+ record(Class,typedef) ->
+ Class#typedef.typespec
+ end,
+ NewS = S#state{type=Class,tname=Name},
+ case catch(check_class(NewS,ClassSpec)) of
+ {error,Reason} ->
+ error({class,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({class,{internal_error,Reason},NewS});
+ C ->
+ %% update the classdef
+ NewClass =
+ if
+ record(Class,classdef) ->
+ Class#classdef{checked=true,typespec=C};
+ record(Class,typedef) ->
+ #classdef{checked=true,name=Name,typespec=C}
+ end,
+ asn1_db:dbput(NewS#state.mname,Name,NewClass),
+ ok
+ end
+ end,
+ case Result of
+ ok ->
+ checkc(S,Cs,Acc);
+ _ ->
+ checkc(S,Cs,[Result|Acc])
+ end;
+checkc(_S,[],Acc) ->
+%% include_default_class(S#state.mname),
+ lists:reverse(Acc).
+
+checko(S,[Name|Os],Acc,ExclO,ExclOS) ->
+ Result =
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ error({type,{internal_error,'???'},S});
+ Object when record(Object,typedef) ->
+ NewS = S#state{type=Object,tname=Name},
+ case catch(check_object(NewS,Object,Object#typedef.typespec)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ O ->
+ NewObj = Object#typedef{checked=true,typespec=O},
+ asn1_db:dbput(NewS#state.mname,Name,NewObj),
+ if
+ record(O,'Object') ->
+ case O#'Object'.gen of
+ true ->
+ {ok,ExclO,ExclOS};
+ false ->
+ {ok,[Name|ExclO],ExclOS}
+ end;
+ record(O,'ObjectSet') ->
+ case O#'ObjectSet'.gen of
+ true ->
+ {ok,ExclO,ExclOS};
+ false ->
+ {ok,ExclO,[Name|ExclOS]}
+ end
+ end
+ end;
+ PObject when record(PObject,pobjectdef) ->
+ NewS = S#state{type=PObject,tname=Name},
+ case (catch check_pobject(NewS,PObject)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ PO ->
+ NewPObj = PObject#pobjectdef{def=PO},
+ asn1_db:dbput(NewS#state.mname,Name,NewPObj),
+ {ok,[Name|ExclO],ExclOS}
+ end;
+ PObjSet when record(PObjSet,pvaluesetdef) ->
+ %% this is a parameterized object set. Might be a parameterized
+ %% value set, couldn't it?
+ NewS = S#state{type=PObjSet,tname=Name},
+ case (catch check_pobjectset(NewS,PObjSet)) of
+ {error,Reason} ->
+ error({type,Reason,NewS});
+ {'EXIT',Reason} ->
+ error({type,{internal_error,Reason},NewS});
+ {asn1,Reason} ->
+ error({type,Reason,NewS});
+ POS ->
+ %%NewPObjSet = PObjSet#pvaluesetdef{valueset=POS},
+ asn1_db:dbput(NewS#state.mname,Name,POS),
+ {ok,ExclO,[Name|ExclOS]}
+ end
+ end,
+ case Result of
+ {ok,NewExclO,NewExclOS} ->
+ checko(S,Os,Acc,NewExclO,NewExclOS);
+ _ ->
+ checko(S,Os,[Result|Acc],ExclO,ExclOS)
+ end;
+checko(_S,[],Acc,ExclO,ExclOS) ->
+ {lists:reverse(Acc),lists:reverse(ExclO),lists:reverse(ExclOS)}.
+
+check_class(S,CDef=#classdef{checked=Ch,name=Name,typespec=TS}) ->
+ case Ch of
+ true -> TS;
+ idle -> TS;
+ _ ->
+ NewCDef = CDef#classdef{checked=idle},
+ asn1_db:dbput(S#state.mname,Name,NewCDef),
+ CheckedTS = check_class(S,TS),
+ asn1_db:dbput(S#state.mname,Name,
+ NewCDef#classdef{checked=true,
+ typespec=CheckedTS}),
+ CheckedTS
+ end;
+check_class(S = #state{mname=M,tname=T},ClassSpec)
+ when record(ClassSpec,type) ->
+ Def = ClassSpec#type.def,
+ case Def of
+ #'Externaltypereference'{module=M,type=T} ->
+ #objectclass{fields=Def}; % in case of recursive definitions
+ Tref when record(Tref,'Externaltypereference') ->
+ {_,RefType} = get_referenced_type(S,Tref),
+% case RefType of
+% RefClass when record(RefClass,classdef) ->
+% check_class(S,RefClass#classdef.typespec)
+% end
+ case is_class(S,RefType) of
+ true ->
+ check_class(S,get_class_def(S,RefType));
+ _ ->
+ error({class,{internal_error,RefType},S})
+ end
+ end;
+% check_class(S,{objectclassname,ModuleName,ClassName}) when atom(ModuleName),atom(ClassName) ->
+% 'fix this';
+check_class(S,C) when record(C,objectclass) ->
+ NewFieldSpec = check_class_fields(S,C#objectclass.fields),
+ C#objectclass{fields=NewFieldSpec};
+%check_class(S,{objectclassname,ClassName}) ->
+check_class(S,ClassName) ->
+ {_,Def} = get_referenced_type(S,ClassName),
+ case Def of
+ ClassDef when record(ClassDef,classdef) ->
+ case ClassDef#classdef.checked of
+ true ->
+ ClassDef#classdef.typespec;
+ idle ->
+ ClassDef#classdef.typespec;
+ false ->
+ check_class(S,ClassDef#classdef.typespec)
+ end;
+ TypeDef when record(TypeDef,typedef) ->
+ %% this case may occur when a definition is a reference
+ %% to a class definition.
+ case TypeDef#typedef.typespec of
+ #type{def=Ext} when record(Ext,'Externaltypereference') ->
+ check_class(S,Ext)
+ end
+ end;
+check_class(_S,{poc,_ObjSet,_Params}) ->
+ 'fix this later'.
+
+check_class_fields(S,Fields) ->
+ check_class_fields(S,Fields,[]).
+
+check_class_fields(S,[F|Fields],Acc) ->
+ NewField =
+ case element(1,F) of
+ fixedtypevaluefield ->
+ {_,Name,Type,Unique,OSpec} = F,
+ RefType = check_type(S,#typedef{typespec=Type},Type),
+ {fixedtypevaluefield,Name,RefType,Unique,OSpec};
+ object_or_fixedtypevalue_field ->
+ {_,Name,Type,Unique,OSpec} = F,
+ Cat =
+ case asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def)) of
+ Def when record(Def,typereference);
+ record(Def,'Externaltypereference') ->
+ {_,D} = get_referenced_type(S,Def),
+ D;
+ {undefined,user} ->
+ %% neither of {primitive,bif} or {constructed,bif}
+%% {_,D} = get_referenced_type(S,#typereference{val=Type#type.def}),
+ {_,D} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=Type#type.def}),
+ D;
+ _ ->
+ Type
+ end,
+ case Cat of
+ Class when record(Class,classdef) ->
+ {objectfield,Name,Type,Unique,OSpec};
+ _ ->
+ RefType = check_type(S,#typedef{typespec=Type},Type),
+ {fixedtypevaluefield,Name,RefType,Unique,OSpec}
+ end;
+ objectset_or_fixedtypevalueset_field ->
+ {_,Name,Type,OSpec} = F,
+%% RefType = check_type(S,#typedef{typespec=Type},Type),
+ RefType =
+ case (catch check_type(S,#typedef{typespec=Type},Type)) of
+ {asn1_class,_ClassDef} ->
+ case if_current_checked_type(S,Type) of
+ true ->
+ Type#type.def;
+ _ ->
+ check_class(S,Type)
+ end;
+ CheckedType when record(CheckedType,type) ->
+ CheckedType;
+ _ ->
+ error({class,"internal error, check_class_fields",S})
+ end,
+ if
+ record(RefType,'Externaltypereference') ->
+ {objectsetfield,Name,Type,OSpec};
+ record(RefType,classdef) ->
+ {objectsetfield,Name,Type,OSpec};
+ record(RefType,objectclass) ->
+ {objectsetfield,Name,Type,OSpec};
+ true ->
+ {fixedtypevaluesetfield,Name,RefType,OSpec}
+ end;
+ typefield ->
+ case F of
+ {TF,Name,{'DEFAULT',Type}} ->
+ {TF,Name,{'DEFAULT',check_type(S,#typedef{typespec=Type},Type)}};
+ _ -> F
+ end;
+ _ -> F
+ end,
+ check_class_fields(S,Fields,[NewField|Acc]);
+check_class_fields(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+if_current_checked_type(S,#type{def=Def}) ->
+ CurrentCheckedName = S#state.tname,
+ MergedModules = S#state.inputmodules,
+ % CurrentCheckedModule = S#state.mname,
+ case Def of
+ #'Externaltypereference'{module=CurrentCheckedName,
+ type=CurrentCheckedName} ->
+ true;
+ #'Externaltypereference'{module=ModuleName,
+ type=CurrentCheckedName} ->
+ case MergedModules of
+ undefined ->
+ false;
+ _ ->
+ lists:member(ModuleName,MergedModules)
+ end;
+ _ ->
+ false
+ end.
+
+
+
+check_pobject(_S,PObject) when record(PObject,pobjectdef) ->
+ Def = PObject#pobjectdef.def,
+ Def.
+
+
+check_pobjectset(S,PObjSet) ->
+ #pvaluesetdef{pos=Pos,name=Name,args=Args,type=Type,
+ valueset=ValueSet}=PObjSet,
+ {Mod,Def} = get_referenced_type(S,Type#type.def),
+ case Def of
+ #classdef{} ->
+ ClassName = #'Externaltypereference'{module=Mod,
+ type=Def#classdef.name},
+ {valueset,Set} = ValueSet,
+% ObjectSet = #'ObjectSet'{class={objectclassname,ClassName},
+ ObjectSet = #'ObjectSet'{class=ClassName,
+ set=Set},
+ #pobjectsetdef{pos=Pos,name=Name,args=Args,class=Type#type.def,
+ def=ObjectSet};
+ _ ->
+ PObjSet
+ end.
+
+check_object(_S,ObjDef,ObjSpec) when (ObjDef#typedef.checked == true) ->
+ ObjSpec;
+check_object(S,_ObjDef,#'Object'{classname=ClassRef,def=ObjectDef}) ->
+ {_,_ClassDef} = get_referenced_type(S,ClassRef),
+ NewClassRef = check_externaltypereference(S,ClassRef),
+ ClassDef =
+ case _ClassDef#classdef.checked of
+ false ->
+ #classdef{checked=true,
+ typespec=check_class(S,_ClassDef#classdef.typespec)};
+ _ ->
+ _ClassDef
+ end,
+ NewObj =
+ case ObjectDef of
+ Def when tuple(Def), (element(1,Def)==object) ->
+ NewSettingList = check_objectdefn(S,Def,ClassDef),
+ #'Object'{def=NewSettingList};
+% Def when tuple(Def), (element(1,Def)=='ObjectFromObject') ->
+% fixa;
+ {po,{object,DefObj},ArgsList} ->
+ {_,Object} = get_referenced_type(S,DefObj),%DefObj is a
+ %%#'Externalvaluereference' or a #'Externaltypereference'
+ %% Maybe this call should be catched and in case of an exception
+ %% an nonallocated parameterized object should be returned.
+ instantiate_po(S,ClassDef,Object,ArgsList);
+ #'Externalvaluereference'{} ->
+ {_,Object} = get_referenced_type(S,ObjectDef),
+ check_object(S,Object,Object#typedef.typespec);
+ _ ->
+ exit({error,{no_object,ObjectDef},S})
+ end,
+ Gen = gen_incl(S,NewObj#'Object'.def,
+ (ClassDef#classdef.typespec)#objectclass.fields),
+ NewObj#'Object'{classname=NewClassRef,gen=Gen};
+
+%%check_object(S,ObjSetDef,ObjSet=#type{def={pt,ObjSetRef,Args}}) ->
+ %% A parameterized
+
+check_object(S,
+ _ObjSetDef,
+ ObjSet=#'ObjectSet'{class=ClassRef}) ->
+ {_,ClassDef} = get_referenced_type(S,ClassRef),
+ NewClassRef = check_externaltypereference(S,ClassRef),
+ UniqueFieldName =
+ case (catch get_unique_fieldname(ClassDef)) of
+ {error,'__undefined_'} -> {unique,undefined};
+ {asn1,Msg,_} -> error({class,Msg,S});
+ Other -> Other
+ end,
+ NewObjSet=
+ case ObjSet#'ObjectSet'.set of
+ {'SingleValue',Set} when list(Set) ->
+ CheckedSet = check_object_list(S,NewClassRef,Set),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {'SingleValue',{definedvalue,ObjName}} ->
+ {_,ObjDef} = get_referenced_type(S,#identifier{val=ObjName}),
+ #'Object'{def=CheckedObj} =
+ check_object(S,ObjDef,ObjDef#typedef.typespec),
+ NewSet = get_unique_valuelist(S,[{ObjDef#typedef.name,
+ CheckedObj}],
+ UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {'SingleValue',#'Externalvaluereference'{value=ObjName}} ->
+ {_,ObjDef} = get_referenced_type(S,#identifier{val=ObjName}),
+ #'Object'{def=CheckedObj} =
+ check_object(S,ObjDef,ObjDef#typedef.typespec),
+ NewSet = get_unique_valuelist(S,[{ObjDef#typedef.name,
+ CheckedObj}],
+ UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ ['EXTENSIONMARK'] ->
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=['EXTENSIONMARK']};
+ Set when list(Set) ->
+ CheckedSet = check_object_list(S,NewClassRef,Set),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet};
+ {Set,Ext} when list(Set) ->
+ CheckedSet = check_object_list(S,NewClassRef,Set++Ext),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet++['EXTENSIONMARK']};
+ {{'SingleValue',Set},Ext} ->
+ CheckedSet = check_object_list(S,NewClassRef,
+ merge_sets(Set,Ext)),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet++['EXTENSIONMARK']};
+ {Type,{'EXCEPT',Exclusion}} when record(Type,type) ->
+ {_,TDef} = get_referenced_type(S,Type#type.def),
+ OS = TDef#typedef.typespec,
+ NewSet = reduce_objectset(OS#'ObjectSet'.set,Exclusion),
+ NewOS = OS#'ObjectSet'{set=NewSet},
+ check_object(S,TDef#typedef{typespec=NewOS},
+ NewOS);
+ #type{def={pt,DefinedObjSet,ParamList}} ->
+ {_,PObjSetDef} = get_referenced_type(S,DefinedObjSet),
+ instantiate_pos(S,ClassDef,PObjSetDef,ParamList);
+ {ObjDef={object,definedsyntax,_ObjFields},_Ext} ->
+ CheckedSet = check_object_list(S,NewClassRef,[ObjDef]),
+ NewSet = get_unique_valuelist(S,CheckedSet,UniqueFieldName),
+ ObjSet#'ObjectSet'{uniquefname=UniqueFieldName,
+ set=NewSet++['EXTENSIONMARK']}
+ end,
+ Gen = gen_incl_set(S,NewObjSet#'ObjectSet'.set,
+ ClassDef),
+ NewObjSet#'ObjectSet'{class=NewClassRef,gen=Gen}.
+
+
+merge_sets(Set,Ext) when list(Set),list(Ext) ->
+ Set ++ Ext;
+merge_sets(Set,Ext) when list(Ext) ->
+ [Set|Ext];
+merge_sets(Set,{'SingleValue',Ext}) when list(Set) ->
+ Set ++ [Ext];
+merge_sets(Set,{'SingleValue',Ext}) ->
+ [Set] ++ [Ext].
+
+reduce_objectset(ObjectSet,Exclusion) ->
+ case Exclusion of
+ {'SingleValue',#'Externalvaluereference'{value=Name}} ->
+ case lists:keysearch(Name,1,ObjectSet) of
+ {value,El} ->
+ lists:subtract(ObjectSet,[El]);
+ _ ->
+ ObjectSet
+ end
+ end.
+
+%% Checks a list of objects or object sets and returns a list of selected
+%% information for the code generation.
+check_object_list(S,ClassRef,ObjectList) ->
+ check_object_list(S,ClassRef,ObjectList,[]).
+
+check_object_list(S,ClassRef,[ObjOrSet|Objs],Acc) ->
+ case ObjOrSet of
+ ObjDef when tuple(ObjDef),(element(1,ObjDef)==object) ->
+ Def =
+ check_object(S,#typedef{typespec=ObjDef},
+% #'Object'{classname={objectclassname,ClassRef},
+ #'Object'{classname=ClassRef,
+ def=ObjDef}),
+ check_object_list(S,ClassRef,Objs,[{no_name,Def#'Object'.def}|Acc]);
+ {'SingleValue',{definedvalue,ObjName}} ->
+ {_,ObjectDef} = get_referenced_type(S,#identifier{val=ObjName}),
+ #'Object'{def=Def} = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ check_object_list(S,ClassRef,Objs,[{ObjectDef#typedef.name,Def}|Acc]);
+ {'SingleValue',Ref = #'Externalvaluereference'{}} ->
+ {_,ObjectDef} = get_referenced_type(S,Ref),
+ #'Object'{def=Def} = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ check_object_list(S,ClassRef,Objs,[{ObjectDef#typedef.name,Def}|Acc]);
+ ObjRef when record(ObjRef,'Externalvaluereference') ->
+ {_,ObjectDef} = get_referenced_type(S,ObjRef),
+ #'Object'{def=Def} = check_object(S,ObjectDef,ObjectDef#typedef.typespec),
+ check_object_list(S,ClassRef,Objs,
+%% [{ObjRef#'Externalvaluereference'.value,Def}|Acc]);
+ [{ObjectDef#typedef.name,Def}|Acc]);
+ {'ValueFromObject',{_,Object},FieldName} ->
+ {_,Def} = get_referenced_type(S,Object),
+%% TypeOrVal = get_fieldname_element(S,Def,FieldName);%% this must result in an object set
+ TypeDef = get_fieldname_element(S,Def,FieldName),
+ (TypeDef#typedef.typespec)#'ObjectSet'.set;
+ ObjSet when record(ObjSet,type) ->
+ ObjSetDef =
+ case ObjSet#type.def of
+ Ref when record(Ref,typereference);
+ record(Ref,'Externaltypereference') ->
+ {_,D} = get_referenced_type(S,ObjSet#type.def),
+ D;
+ Other ->
+ throw({asn1_error,{'unknown objecset',Other,S}})
+ end,
+ #'ObjectSet'{set=ObjectsInSet} =
+ check_object(S,ObjSetDef,ObjSetDef#typedef.typespec),
+ AccList = transform_set_to_object_list(ObjectsInSet,[]),
+ check_object_list(S,ClassRef,Objs,AccList++Acc);
+ union ->
+ check_object_list(S,ClassRef,Objs,Acc);
+ Other ->
+ exit({error,{'unknown object',Other},S})
+ end;
+%% Finally reverse the accumulated list and if there are any extension
+%% marks in the object set put one indicator of that in the end of the
+%% list.
+check_object_list(_,_,[],Acc) ->
+ lists:reverse(Acc).
+%% case lists:member('EXTENSIONMARK',RevAcc) of
+%% true ->
+%% ExclRevAcc = lists:filter(fun(X)->X /= 'EXTENSIONMARK' end,
+%% RevAcc),
+%% ExclRevAcc ++ ['EXTENSIONMARK'];
+%% false ->
+%% RevAcc
+%% end.
+
+
+%% get_fieldname_element/3
+%% gets the type/value/object/... of the referenced element in FieldName
+%% FieldName is a list and may have more than one element.
+%% Each element in FieldName can be either {typefieldreference,AnyFieldName}
+%% or {valuefieldreference,AnyFieldName}
+%% Def is the def of the first object referenced by FieldName
+get_fieldname_element(S,Def,[{_RefType,FieldName}]) when record(Def,typedef) ->
+ {_,_,ObjComps} = (Def#typedef.typespec)#'Object'.def,
+ case lists:keysearch(FieldName,1,ObjComps) of
+ {value,{_,TDef}} when record(TDef,typedef) ->
+ %% ORec = TDef#typedef.typespec, %% XXX This must be made general
+% case TDef#typedef.typespec of
+% ObjSetRec when record(ObjSetRec,'ObjectSet') ->
+% ObjSet = ObjSetRec#'ObjectSet'.set;
+% ObjRec when record(ObjRec,'Object') ->
+% %% now get the field in ObjRec that RestFName points out
+% %ObjRec
+% TDef
+% end;
+ TDef;
+ {value,{_,VDef}} when record(VDef,valuedef) ->
+ check_value(S,VDef);
+ _ ->
+ throw({assigned_object_error,"not_assigned_object",S})
+ end;
+get_fieldname_element(_S,Def,[{_RefType,_FieldName}|_RestFName])
+ when record(Def,typedef) ->
+ ok.
+
+transform_set_to_object_list([{Name,_UVal,Fields}|Objs],Acc) ->
+ transform_set_to_object_list(Objs,[{Name,{object,generatesyntax,Fields}}|Acc]);
+transform_set_to_object_list(['EXTENSIONMARK'|Objs],Acc) ->
+%% transform_set_to_object_list(Objs,['EXTENSIONMARK'|Acc]);
+ transform_set_to_object_list(Objs,Acc);
+transform_set_to_object_list([],Acc) ->
+ Acc.
+
+get_unique_valuelist(_S,ObjSet,{unique,undefined}) -> % no unique field in object
+ lists:map(fun({N,{_,_,F}})->{N,F};
+ (V={_,_,_}) ->V end, ObjSet);
+get_unique_valuelist(S,ObjSet,UFN) ->
+ get_unique_vlist(S,ObjSet,UFN,[]).
+
+get_unique_vlist(S,[],_,Acc) ->
+ case catch check_uniqueness(Acc) of
+ {asn1_error,_} ->
+% exit({error,Reason,S});
+ error({'ObjectSet',"not unique objects in object set",S});
+ true ->
+ lists:reverse(Acc)
+ end;
+get_unique_vlist(S,[{ObjName,Obj}|Rest],UniqueFieldName,Acc) ->
+ {_,_,Fields} = Obj,
+ VDef = get_unique_value(S,Fields,UniqueFieldName),
+ get_unique_vlist(S,Rest,UniqueFieldName,
+ [{ObjName,VDef#valuedef.value,Fields}|Acc]);
+get_unique_vlist(S,[V={_,_,_}|Rest],UniqueFieldName,Acc) ->
+ get_unique_vlist(S,Rest,UniqueFieldName,[V|Acc]).
+
+get_unique_value(S,Fields,UniqueFieldName) ->
+ Module = S#state.mname,
+ case lists:keysearch(UniqueFieldName,1,Fields) of
+ {value,Field} ->
+ case element(2,Field) of
+ VDef when record(VDef,valuedef) ->
+ VDef;
+ {definedvalue,ValName} ->
+ ValueDef = asn1_db:dbget(Module,ValName),
+ case ValueDef of
+ VDef when record(VDef,valuedef) ->
+ ValueDef;
+ undefined ->
+ #valuedef{value=ValName}
+ end;
+ {'ValueFromObject',Object,Name} ->
+ case Object of
+ {object,Ext} when record(Ext,'Externaltypereference') ->
+ OtherModule = Ext#'Externaltypereference'.module,
+ ExtObjName = Ext#'Externaltypereference'.type,
+ ObjDef = asn1_db:dbget(OtherModule,ExtObjName),
+ ObjSpec = ObjDef#typedef.typespec,
+ get_unique_value(OtherModule,element(3,ObjSpec),Name);
+ {object,{_,_,ObjName}} ->
+ ObjDef = asn1_db:dbget(Module,ObjName),
+ ObjSpec = ObjDef#typedef.typespec,
+ get_unique_value(Module,element(3,ObjSpec),Name);
+ {po,Object,_Params} ->
+ exit({error,{'parameterized object not implemented yet',
+ Object},S})
+ end;
+ Value when atom(Value);number(Value) ->
+ #valuedef{value=Value};
+ {'CHOICE',{_,Value}} when atom(Value);number(Value) ->
+ #valuedef{value=Value}
+ end;
+ false ->
+ exit({error,{'no unique value',Fields,UniqueFieldName},S})
+%% io:format("WARNING: no unique value in object"),
+%% exit(uniqueFieldName)
+ end.
+
+check_uniqueness(NameValueList) ->
+ check_uniqueness1(lists:keysort(2,NameValueList)).
+
+check_uniqueness1([]) ->
+ true;
+check_uniqueness1([_]) ->
+ true;
+check_uniqueness1([{_,N,_},{_,N,_}|_Rest]) ->
+ throw({asn1_error,{'objects in set must have unique values in UNIQUE fields',N}});
+check_uniqueness1([_|Rest]) ->
+ check_uniqueness1(Rest).
+
+%% instantiate_po/4
+%% ClassDef is the class of Object,
+%% Object is the Parameterized object, which is referenced,
+%% ArgsList is the list of actual parameters
+%% returns an #'Object' record.
+instantiate_po(S,_ClassDef,Object,ArgsList) when record(Object,pobjectdef) ->
+ FormalParams = get_pt_args(Object),
+ MatchedArgs = match_args(FormalParams,ArgsList,[]),
+ NewS = S#state{type=Object,parameters=MatchedArgs},
+ check_object(NewS,Object,#'Object'{classname=Object#pobjectdef.class,
+ def=Object#pobjectdef.def}).
+
+%% instantiate_pos/4
+%% ClassDef is the class of ObjectSetDef,
+%% ObjectSetDef is the Parameterized object set, which is referenced
+%% on the right side of the assignment,
+%% ArgsList is the list of actual parameters, i.e. real objects
+instantiate_pos(S,ClassDef,ObjectSetDef,ArgsList) ->
+ ClassName = ClassDef#classdef.name,
+ FormalParams = get_pt_args(ObjectSetDef),
+ Set = case get_pt_spec(ObjectSetDef) of
+ {valueset,_Set} -> _Set;
+ _Set -> _Set
+ end,
+ MatchedArgs = match_args(FormalParams,ArgsList,[]),
+ NewS = S#state{type=ObjectSetDef,parameters=MatchedArgs},
+ check_object(NewS,ObjectSetDef,
+ #'ObjectSet'{class=name2Extref(S#state.mname,ClassName),
+ set=Set}).
+
+
+%% gen_incl -> boolean()
+%% If object with Fields has any of the corresponding class' typefields
+%% then return value is true otherwise it is false.
+%% If an object lacks a typefield but the class has a type field that
+%% is OPTIONAL then we want gen to be true
+gen_incl(S,{_,_,Fields},CFields)->
+ gen_incl1(S,Fields,CFields).
+
+gen_incl1(_,_,[]) ->
+ false;
+gen_incl1(S,Fields,[C|CFields]) ->
+ case element(1,C) of
+ typefield ->
+% case lists:keymember(element(2,C),1,Fields) of
+% true ->
+% true;
+% false ->
+% gen_incl1(S,Fields,CFields)
+% end;
+ true; %% should check that field is OPTIONAL or DEFUALT if
+ %% the object lacks this field
+ objectfield ->
+ case lists:keysearch(element(2,C),1,Fields) of
+ {value,Field} ->
+ Type = element(3,C),
+ {_,ClassDef} = get_referenced_type(S,Type#type.def),
+% {_,ClassFields,_} = ClassDef#classdef.typespec,
+ #objectclass{fields=ClassFields} =
+ ClassDef#classdef.typespec,
+ ObjTDef = element(2,Field),
+ case gen_incl(S,(ObjTDef#typedef.typespec)#'Object'.def,
+ ClassFields) of
+ true ->
+ true;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end;
+ _ ->
+ gen_incl1(S,Fields,CFields)
+ end.
+
+%% first if no unique field in the class return false.(don't generate code)
+gen_incl_set(S,Fields,ClassDef) ->
+ case catch get_unique_fieldname(ClassDef) of
+ Tuple when tuple(Tuple) ->
+ false;
+ _ ->
+ gen_incl_set1(S,Fields,
+ (ClassDef#classdef.typespec)#objectclass.fields)
+ end.
+
+%% if any of the existing or potentially existing objects has a typefield
+%% then return true.
+gen_incl_set1(_,[],_CFields)->
+ false;
+gen_incl_set1(_,['EXTENSIONMARK'],_) ->
+ true;
+%% Fields are the fields of an object in the object set.
+%% CFields are the fields of the class of the object set.
+gen_incl_set1(S,[Object|Rest],CFields)->
+ Fields = element(size(Object),Object),
+ case gen_incl1(S,Fields,CFields) of
+ true ->
+ true;
+ false ->
+ gen_incl_set1(S,Rest,CFields)
+ end.
+
+check_objectdefn(S,Def,CDef) when record(CDef,classdef) ->
+ WithSyntax = (CDef#classdef.typespec)#objectclass.syntax,
+ ClassFields = (CDef#classdef.typespec)#objectclass.fields,
+ case Def of
+ {object,defaultsyntax,Fields} ->
+ check_defaultfields(S,Fields,ClassFields);
+ {object,definedsyntax,Fields} ->
+ {_,WSSpec} = WithSyntax,
+ NewFields =
+ case catch( convert_definedsyntax(S,Fields,WSSpec,
+ ClassFields,[])) of
+ {asn1,{_ErrorType,ObjToken,ClassToken}} ->
+ throw({asn1,{'match error in object',ObjToken,
+ 'found in object',ClassToken,'found in class'}});
+ Err={asn1,_} -> throw(Err);
+ Err={'EXIT',_} -> throw(Err);
+ DefaultFields when list(DefaultFields) ->
+ DefaultFields
+ end,
+ {object,defaultsyntax,NewFields};
+ {object,_ObjectId} -> % This is a DefinedObject
+ fixa;
+ Other ->
+ exit({error,{objectdefn,Other}})
+ end.
+
+check_defaultfields(S,Fields,ClassFields) ->
+ check_defaultfields(S,Fields,ClassFields,[]).
+
+check_defaultfields(_S,[],_ClassFields,Acc) ->
+ {object,defaultsyntax,lists:reverse(Acc)};
+check_defaultfields(S,[{FName,Spec}|Fields],ClassFields,Acc) ->
+ case lists:keysearch(FName,2,ClassFields) of
+ {value,CField} ->
+ NewField = convert_to_defaultfield(S,FName,Spec,CField),
+ check_defaultfields(S,Fields,ClassFields,[NewField|Acc]);
+ _ ->
+ throw({error,{asn1,{'unvalid field in object',FName}}})
+ end.
+%% {object,defaultsyntax,Fields}.
+
+convert_definedsyntax(_S,[],[],_ClassFields,Acc) ->
+ lists:reverse(Acc);
+convert_definedsyntax(S,Fields,WithSyntax,ClassFields,Acc) ->
+ case match_field(S,Fields,WithSyntax,ClassFields) of
+ {MatchedField,RestFields,RestWS} ->
+ if
+ list(MatchedField) ->
+ convert_definedsyntax(S,RestFields,RestWS,ClassFields,
+ lists:append(MatchedField,Acc));
+ true ->
+ convert_definedsyntax(S,RestFields,RestWS,ClassFields,
+ [MatchedField|Acc])
+ end
+%% throw({error,{asn1,{'unvalid syntax in object',WorS}}})
+ end.
+
+match_field(S,Fields,WithSyntax,ClassFields) ->
+ match_field(S,Fields,WithSyntax,ClassFields,[]).
+
+match_field(S,Fields,[W|Ws],ClassFields,Acc) when list(W) ->
+ case catch(match_optional_field(S,Fields,W,ClassFields,[])) of
+ {'EXIT',_} ->
+ match_field(Fields,Ws,ClassFields,Acc); %% add S
+%% {[Result],RestFields} ->
+%% {Result,RestFields,Ws};
+ {Result,RestFields} when list(Result) ->
+ {Result,RestFields,Ws};
+ _ ->
+ match_field(S,Fields,Ws,ClassFields,Acc)
+ end;
+match_field(S,Fields,WithSyntax,ClassFields,_Acc) ->
+ match_mandatory_field(S,Fields,WithSyntax,ClassFields,[]).
+
+match_optional_field(_S,RestFields,[],_,Ret) ->
+ {Ret,RestFields};
+%% An additional optional field within an optional field
+match_optional_field(S,Fields,[W|Ws],ClassFields,Ret) when list(W) ->
+ case catch match_optional_field(S,Fields,W,ClassFields,[]) of
+ {'EXIT',_} ->
+ {Ret,Fields};
+ {asn1,{optional_matcherror,_,_}} ->
+ {Ret,Fields};
+ {OptionalField,RestFields} ->
+ match_optional_field(S,RestFields,Ws,ClassFields,
+ lists:append(OptionalField,Ret))
+ end;
+%% identify and skip word
+%match_optional_field(S,[#'Externaltypereference'{type=WorS}|Rest],
+match_optional_field(S,[{_,_,WorS}|Rest],
+ [WorS|Ws],ClassFields,Ret) ->
+ match_optional_field(S,Rest,Ws,ClassFields,Ret);
+match_optional_field(S,[],_,ClassFields,Ret) ->
+ match_optional_field(S,[],[],ClassFields,Ret);
+%% identify and skip comma
+match_optional_field(S,[{WorS,_}|Rest],[{WorS,_}|Ws],ClassFields,Ret) ->
+ match_optional_field(S,Rest,Ws,ClassFields,Ret);
+%% identify and save field data
+match_optional_field(S,[Setting|Rest],[{_,W}|Ws],ClassFields,Ret) ->
+ WorS =
+ case Setting of
+ Type when record(Type,type) -> Type;
+%% #'Externalvaluereference'{value=WordOrSetting} -> WordOrSetting;
+ {'ValueFromObject',_,_} -> Setting;
+ {object,_,_} -> Setting;
+ {_,_,WordOrSetting} -> WordOrSetting;
+%% Atom when atom(Atom) -> Atom
+ Other -> Other
+ end,
+ case lists:keysearch(W,2,ClassFields) of
+ false ->
+ throw({asn1,{optional_matcherror,WorS,W}});
+ {value,CField} ->
+ NewField = convert_to_defaultfield(S,W,WorS,CField),
+ match_optional_field(S,Rest,Ws,ClassFields,[NewField|Ret])
+ end;
+match_optional_field(_S,[WorS|_Rest],[W|_Ws],_ClassFields,_Ret) ->
+ throw({asn1,{optional_matcherror,WorS,W}}).
+
+match_mandatory_field(_S,[],[],_,[Acc]) ->
+ {Acc,[],[]};
+match_mandatory_field(_S,[],[],_,Acc) ->
+ {Acc,[],[]};
+match_mandatory_field(S,[],[H|T],CF,Acc) when list(H) ->
+ match_mandatory_field(S,[],T,CF,Acc);
+match_mandatory_field(_S,[],WithSyntax,_,_Acc) ->
+ throw({asn1,{mandatory_matcherror,[],WithSyntax}});
+%match_mandatory_field(_S,Fields,WithSyntax=[W|_Ws],_ClassFields,[Acc]) when list(W) ->
+match_mandatory_field(_S,Fields,WithSyntax=[W|_Ws],_ClassFields,Acc) when list(W), length(Acc) >= 1 ->
+ {Acc,Fields,WithSyntax};
+%% identify and skip word
+match_mandatory_field(S,[{_,_,WorS}|Rest],
+ [WorS|Ws],ClassFields,Acc) ->
+ match_mandatory_field(S,Rest,Ws,ClassFields,Acc);
+%% identify and skip comma
+match_mandatory_field(S,[{WorS,_}|Rest],[{WorS,_}|Ws],ClassFields,Ret) ->
+ match_mandatory_field(S,Rest,Ws,ClassFields,Ret);
+%% identify and save field data
+match_mandatory_field(S,[Setting|Rest],[{_,W}|Ws],ClassFields,Acc) ->
+ WorS =
+ case Setting of
+%% Atom when atom(Atom) -> Atom;
+%% #'Externalvaluereference'{value=WordOrSetting} -> WordOrSetting;
+ {object,_,_} -> Setting;
+ {_,_,WordOrSetting} -> WordOrSetting;
+ Type when record(Type,type) -> Type;
+ Other -> Other
+ end,
+ case lists:keysearch(W,2,ClassFields) of
+ false ->
+ throw({asn1,{mandatory_matcherror,WorS,W}});
+ {value,CField} ->
+ NewField = convert_to_defaultfield(S,W,WorS,CField),
+ match_mandatory_field(S,Rest,Ws,ClassFields,[NewField|Acc])
+ end;
+
+match_mandatory_field(_S,[WorS|_Rest],[W|_Ws],_ClassFields,_Acc) ->
+ throw({asn1,{mandatory_matcherror,WorS,W}}).
+
+%% Converts a field of an object from defined syntax to default syntax
+convert_to_defaultfield(S,ObjFieldName,ObjFieldSetting,CField)->
+ CurrMod = S#state.mname,
+ case element(1,CField) of
+ typefield ->
+ TypeDef=
+ case ObjFieldSetting of
+ TypeRec when record(TypeRec,type) -> TypeRec#type.def;
+ TDef when record(TDef,typedef) ->
+ TDef#typedef{typespec=check_type(S,TDef,
+ TDef#typedef.typespec)};
+ _ -> ObjFieldSetting
+ end,
+ Type =
+ if
+ record(TypeDef,typedef) -> TypeDef;
+ true ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(TypeDef)) of
+ ERef = #'Externaltypereference'{module=CurrMod} ->
+ {_,T} = get_referenced_type(S,ERef),
+ T#typedef{checked=true,
+ typespec=check_type(S,T,
+ T#typedef.typespec)};
+ ERef = #'Externaltypereference'{module=ExtMod} ->
+ {_,T} = get_referenced_type(S,ERef),
+ #typedef{name=Name} = T,
+ check_type(S,T,T#typedef.typespec),
+ #typedef{checked=true,
+ name={ExtMod,Name},
+ typespec=ERef};
+ Bif when Bif=={primitive,bif};Bif=={constructed,bif} ->
+ T = check_type(S,#typedef{typespec=ObjFieldSetting},
+ ObjFieldSetting),
+ #typedef{checked=true,name=Bif,typespec=T};
+ _ ->
+ {Mod,T} =
+ %% get_referenced_type(S,#typereference{val=ObjFieldSetting}),
+ get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting}),
+ case Mod of
+ CurrMod ->
+ T;
+ ExtMod ->
+ #typedef{name=Name} = T,
+ T#typedef{name={ExtMod,Name}}
+ end
+ end
+ end,
+ {ObjFieldName,Type};
+ fixedtypevaluefield ->
+ case ObjFieldName of
+ Val when atom(Val) ->
+ %% ObjFieldSetting can be a value,an objectidentifiervalue,
+ %% an element in an enumeration or namednumberlist etc.
+ ValRef =
+ case ObjFieldSetting of
+ #'Externalvaluereference'{} -> ObjFieldSetting;
+ {'ValueFromObject',{_,ObjRef},FieldName} ->
+ {_,Object} = get_referenced_type(S,ObjRef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ get_fieldname_element(S,Object#typedef{typespec=ChObject},
+ FieldName);
+ #valuedef{} ->
+ ObjFieldSetting;
+ _ ->
+ #identifier{val=ObjFieldSetting}
+ end,
+ case ValRef of
+ #valuedef{} ->
+ {ObjFieldName,check_value(S,ValRef)};
+ _ ->
+ ValDef =
+ case catch get_referenced_type(S,ValRef) of
+ {error,_} ->
+ check_value(S,#valuedef{name=Val,
+ type=element(3,CField),
+ value=ObjFieldSetting});
+ {_,VDef} when record(VDef,valuedef) ->
+ check_value(S,VDef);%% XXX
+ {_,VDef} ->
+ check_value(S,#valuedef{name=Val,
+ type=element(3,CField),
+ value=VDef})
+ end,
+ {ObjFieldName,ValDef}
+ end;
+ Val ->
+ {ObjFieldName,Val}
+ end;
+ fixedtypevaluesetfield ->
+ {ObjFieldName,ObjFieldSetting};
+ objectfield ->
+ ObjectSpec =
+ case ObjFieldSetting of
+ Ref when record(Ref,typereference);record(Ref,identifier);
+ record(Ref,'Externaltypereference');
+ record(Ref,'Externalvaluereference') ->
+ {_,R} = get_referenced_type(S,ObjFieldSetting),
+ R;
+ {'ValueFromObject',{_,ObjRef},FieldName} ->
+ %% This is an ObjectFromObject
+ {_,Object} = get_referenced_type(S,ObjRef),
+ ChObject = check_object(S,Object,
+ Object#typedef.typespec),
+ _ObjFromObj=
+ get_fieldname_element(S,Object#typedef{
+ typespec=ChObject},
+ FieldName);
+ %%ClassName = ObjFromObj#'Object'.classname,
+ %%#typedef{name=,
+ %% typespec=
+ %% ObjFromObj#'Object'{classname=
+ %% {objectclassname,ClassName}}};
+ {object,_,_} ->
+ %% An object defined inlined in another object
+ #type{def=Ref} = element(3,CField),
+% CRef = case Ref of
+% #'Externaltypereference'{module=CurrMod,
+% type=CName} ->
+% CName;
+% #'Externaltypereference'{module=ExtMod,
+% type=CName} ->
+% {ExtMod,CName}
+% end,
+ InlinedObjName=
+ list_to_atom(lists:concat([S#state.tname]++
+ ['_',ObjFieldName])),
+% ObjSpec = #'Object'{classname={objectclassname,CRef},
+ ObjSpec = #'Object'{classname=Ref,
+ def=ObjFieldSetting},
+ CheckedObj=
+ check_object(S,#typedef{typespec=ObjSpec},ObjSpec),
+ InlObj = #typedef{checked=true,name=InlinedObjName,
+ typespec=CheckedObj},
+ asn1ct_gen:insert_once(inlined_objects,{InlinedObjName,
+ InlinedObjName}),
+ asn1_db:dbput(S#state.mname,InlinedObjName,InlObj),
+ InlObj;
+ #type{def=Eref} when record(Eref,'Externaltypereference') ->
+ {_,R} = get_referenced_type(S,Eref),
+ R;
+ _ ->
+%% {_,R} = get_referenced_type(S,#typereference{val=ObjFieldSetting}),
+ {_,R} = get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting}),
+ R
+ end,
+ {ObjFieldName,
+ ObjectSpec#typedef{checked=true,
+ typespec=check_object(S,ObjectSpec,
+ ObjectSpec#typedef.typespec)}};
+ variabletypevaluefield ->
+ {ObjFieldName,ObjFieldSetting};
+ variabletypevaluesetfield ->
+ {ObjFieldName,ObjFieldSetting};
+ objectsetfield ->
+ {_,ObjSetSpec} =
+ case ObjFieldSetting of
+ Ref when record(Ref,'Externaltypereference');
+ record(Ref,'Externalvaluereference') ->
+ get_referenced_type(S,ObjFieldSetting);
+ ObjectList when list(ObjectList) ->
+ %% an objctset defined in the object,though maybe
+ %% parsed as a SequenceOfValue
+ %% The ObjectList may be a list of references to
+ %% objects, a ValueFromObject
+ {_,_,Type,_} = CField,
+ ClassDef = Type#type.def,
+ case ClassDef#'Externaltypereference'.module of
+ CurrMod ->
+ ClassDef#'Externaltypereference'.type;
+ ExtMod ->
+ {ExtMod,
+ ClassDef#'Externaltypereference'.type}
+ end,
+ {no_name,
+ #typedef{typespec=
+ #'ObjectSet'{class=
+% {objectclassname,ClassRef},
+ ClassDef,
+ set=ObjectList}}};
+ ObjectSet={'SingleValue',_} ->
+ %% a Union of defined objects
+ {_,_,Type,_} = CField,
+ ClassDef = Type#type.def,
+% ClassRef =
+% case ClassDef#'Externaltypereference'.module of
+% CurrMod ->
+% ClassDef#'Externaltypereference'.type;
+% ExtMod ->
+% {ExtMod,
+% ClassDef#'Externaltypereference'.type}
+% end,
+ {no_name,
+% #typedef{typespec=#'ObjectSet'{class={objectclassname,ClassRef},
+ #typedef{typespec=#'ObjectSet'{class=ClassDef,
+ set=ObjectSet}}};
+ {object,_,[#type{def={'TypeFromObject',
+ {object,RefedObj},
+ FieldName}}]} ->
+ %% This case occurs when an ObjectSetFromObjects
+ %% production is used
+ {M,Def} = get_referenced_type(S,RefedObj),
+ {M,get_fieldname_element(S,Def,FieldName)};
+ #type{def=Eref} when
+ record(Eref,'Externaltypereference') ->
+ get_referenced_type(S,Eref);
+ _ ->
+%% get_referenced_type(S,#typereference{val=ObjFieldSetting})
+ get_referenced_type(S,#'Externaltypereference'{module=S#state.mname,type=ObjFieldSetting})
+ end,
+ {ObjFieldName,
+ ObjSetSpec#typedef{checked=true,
+ typespec=check_object(S,ObjSetSpec,
+ ObjSetSpec#typedef.typespec)}}
+ end.
+
+check_value(OldS,V) when record(V,pvaluesetdef) ->
+ #pvaluesetdef{checked=Checked,type=Type} = V,
+ case Checked of
+ true -> V;
+ {error,_} -> V;
+ false ->
+ case get_referenced_type(OldS,Type#type.def) of
+ {_,Class} when record(Class,classdef) ->
+ throw({pobjectsetdef});
+ _ -> continue
+ end
+ end;
+check_value(_OldS,V) when record(V,pvaluedef) ->
+ %% Fix this case later
+ V;
+check_value(OldS,V) when record(V,typedef) ->
+ %% This case when a value set has been parsed as an object set.
+ %% It may be a value set
+ #typedef{typespec=TS} = V,
+ case TS of
+ #'ObjectSet'{class=ClassRef} ->
+ {_,TSDef} = get_referenced_type(OldS,ClassRef),
+ %%IsObjectSet(TSDef);
+ case TSDef of
+ #classdef{} -> throw({objectsetdef});
+ #typedef{typespec=#type{def=Eref}} when
+ record(Eref,'Externaltypereference') ->
+ %% This case if the class reference is a defined
+ %% reference to class
+ check_value(OldS,V#typedef{typespec=TS#'ObjectSet'{class=Eref}});
+ #typedef{} ->
+ % an ordinary value set with a type in #typedef.typespec
+ ValueSet = TS#'ObjectSet'.set,
+ Type=check_type(OldS,TSDef,TSDef#typedef.typespec),
+ Value = check_value(OldS,#valuedef{type=Type,
+ value=ValueSet}),
+ {valueset,Type#type{constraint=Value#valuedef.value}}
+ end;
+ _ ->
+ throw({objectsetdef})
+ end;
+check_value(S,#valuedef{pos=Pos,name=Name,type=Type,
+ value={valueset,Constr}}) ->
+ NewType = Type#type{constraint=[Constr]},
+ {valueset,
+ check_type(S,#typedef{pos=Pos,name=Name,typespec=NewType},NewType)};
+check_value(OldS=#state{recordtopname=TopName},V) when record(V,valuedef) ->
+ #valuedef{name=Name,checked=Checked,type=Vtype,value=Value} = V,
+ case Checked of
+ true ->
+ V;
+ {error,_} ->
+ V;
+ false ->
+ Def = Vtype#type.def,
+ Constr = Vtype#type.constraint,
+ S = OldS#state{type=Vtype,tname=Def,value=V,vname=Name},
+ NewDef =
+ case Def of
+ Ext when record(Ext,'Externaltypereference') ->
+ RecName = Ext#'Externaltypereference'.type,
+ {_,Type} = get_referenced_type(S,Ext),
+ %% If V isn't a value but an object Type is a #classdef{}
+ case Type of
+ #classdef{} ->
+ throw({objectdef});
+ #typedef{} ->
+ case is_contextswitchtype(Type) of
+ true ->
+ #valuedef{value=CheckedVal}=
+ check_value(S,V#valuedef{type=Type#typedef.typespec}),
+ #newv{value=CheckedVal};
+ _ ->
+ #valuedef{value=CheckedVal}=
+ check_value(S#state{recordtopname=[RecName|TopName]},
+ V#valuedef{type=Type#typedef.typespec}),
+ #newv{value=CheckedVal}
+ end
+ end;
+ 'ANY' ->
+ throw({error,{asn1,{'cant check value of type',Def}}});
+ 'INTEGER' ->
+ validate_integer(S,Value,[],Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ {'INTEGER',NamedNumberList} ->
+ validate_integer(S,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ {'BIT STRING',NamedNumberList} ->
+ validate_bitstring(S,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'NULL' ->
+ validate_null(S,Value,Constr),
+ #newv{};
+ 'OBJECT IDENTIFIER' ->
+ validate_objectidentifier(S,Value,Constr),
+ #newv{value = normalize_value(S,Vtype,Value,[])};
+ 'ObjectDescriptor' ->
+ validate_objectdescriptor(S,Value,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ {'ENUMERATED',NamedNumberList} ->
+ validate_enumerated(S,Value,NamedNumberList,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'BOOLEAN'->
+ validate_boolean(S,Value,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'OCTET STRING' ->
+ validate_octetstring(S,Value,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'NumericString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'TeletexString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'VideotexString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'UTCTime' ->
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+% exit({'cant check value of type' ,Def});
+ 'GeneralizedTime' ->
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+% exit({'cant check value of type' ,Def});
+ 'GraphicString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'VisibleString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'GeneralString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'PrintableString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'IA5String' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+ 'BMPString' ->
+ validate_restrictedstring(S,Value,Def,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,[])};
+%% 'UniversalString' -> %added 6/12 -00
+%% #newv{value=validate_restrictedstring(S,Value,Def,Constr)};
+ Seq when record(Seq,'SEQUENCE') ->
+ SeqVal = validate_sequence(S,Value,
+ Seq#'SEQUENCE'.components,
+ Constr),
+ #newv{value=normalize_value(S,Vtype,SeqVal,TopName)};
+ {'SEQUENCE OF',Components} ->
+ validate_sequenceof(S,Value,Components,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ {'CHOICE',Components} ->
+ validate_choice(S,Value,Components,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ Set when record(Set,'SET') ->
+ validate_set(S,Value,Set#'SET'.components,
+ Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ {'SET OF',Components} ->
+ validate_setof(S,Value,Components,Constr),
+ #newv{value=normalize_value(S,Vtype,Value,TopName)};
+ Other ->
+ exit({'cant check value of type' ,Other})
+ end,
+ case NewDef#newv.value of
+ unchanged ->
+ V#valuedef{checked=true,value=Value};
+ ok ->
+ V#valuedef{checked=true,value=Value};
+ {error,Reason} ->
+ V#valuedef{checked={error,Reason},value=Value};
+ _V ->
+ V#valuedef{checked=true,value=_V}
+ end
+ end.
+
+is_contextswitchtype(#typedef{name='EXTERNAL'})->
+ true;
+is_contextswitchtype(#typedef{name='EMBEDDED PDV'}) ->
+ true;
+is_contextswitchtype(#typedef{name='CHARACTER STRING'}) ->
+ true;
+is_contextswitchtype(_) ->
+ false.
+
+% validate_integer(S,{identifier,Pos,Id},NamedNumberList,Constr) ->
+% case lists:keysearch(Id,1,NamedNumberList) of
+% {value,_} -> ok;
+% false -> error({value,"unknown NamedNumber",S})
+% end;
+%% This case occurs when there is a valuereference
+validate_integer(S=#state{mname=M},
+ #'Externalvaluereference'{module=M,value=Id},
+ NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown NamedNumber",S})
+ end;
+validate_integer(S,Id,NamedNumberList,_Constr) when atom(Id) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown NamedNumber",S})
+ end;
+validate_integer(_S,Value,_NamedNumberList,Constr) when integer(Value) ->
+ check_integer_range(Value,Constr).
+
+check_integer_range(Int,Constr) when list(Constr) ->
+ NewConstr = [X || #constraint{c=X} <- Constr],
+ check_constr(Int,NewConstr);
+
+check_integer_range(_Int,_Constr) ->
+ %%io:format("~p~n",[Constr]),
+ ok.
+
+check_constr(Int,[{'ValueRange',Lb,Ub}|T]) when Int >= Lb, Int =< Ub ->
+ check_constr(Int,T);
+check_constr(_Int,[]) ->
+ ok.
+
+validate_bitstring(_S,_Value,_NamedNumberList,_Constr) ->
+ ok.
+
+validate_null(_S,'NULL',_Constr) ->
+ ok.
+
+%%------------
+%% This can be removed when the old parser is removed
+%% The function removes 'space' atoms from the list
+
+is_space_list([H],Acc) ->
+ lists:reverse([H|Acc]);
+is_space_list([H,space|T],Acc) ->
+ is_space_list(T,[H|Acc]);
+is_space_list([],Acc) ->
+ lists:reverse(Acc);
+is_space_list([H|T],Acc) ->
+ is_space_list(T,[H|Acc]).
+
+validate_objectidentifier(S,L,_) ->
+ case is_space_list(L,[]) of
+ NewL when list(NewL) ->
+ case validate_objectidentifier1(S,NewL) of
+ NewL2 when list(NewL2) ->
+ list_to_tuple(NewL2);
+ Other -> Other
+ end;
+ {error,_} ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end.
+
+validate_objectidentifier1(S, [Id|T]) when record(Id,'Externalvaluereference') ->
+ case catch get_referenced_type(S,Id) of
+ {_,V} when record(V,valuedef) ->
+ case check_value(S,V) of
+ #valuedef{type=#type{def='OBJECT IDENTIFIER'},
+ checked=true,value=Value} when tuple(Value) ->
+ validate_objectid(S, T, lists:reverse(tuple_to_list(Value)));
+ _ ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end;
+ _ ->
+ validate_objectid(S, [Id|T], [])
+ end;
+validate_objectidentifier1(S,V) ->
+ validate_objectid(S,V,[]).
+
+validate_objectid(_, [], Acc) ->
+ lists:reverse(Acc);
+validate_objectid(S, [Value|Vrest], Acc) when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+validate_objectid(S, [{'NamedNumber',_Name,Value}|Vrest], Acc)
+ when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+validate_objectid(S, [Id|Vrest], Acc)
+ when record(Id,'Externalvaluereference') ->
+ case catch get_referenced_type(S, Id) of
+ {_,V} when record(V,valuedef) ->
+ case check_value(S, V) of
+ #valuedef{checked=true,value=Value} when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+ _ ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end;
+ _ ->
+ case reserved_objectid(Id#'Externalvaluereference'.value, Acc) of
+ Value when integer(Value) ->
+ validate_objectid(S, Vrest, [Value|Acc]);
+ false ->
+ error({value, "illegal OBJECT IDENTIFIER", S})
+ end
+ end;
+validate_objectid(S, [{Atom,Value}],[]) when atom(Atom),integer(Value) ->
+ %% this case when an OBJECT IDENTIFIER value has been parsed as a
+ %% SEQUENCE value
+ Rec = #'Externalvaluereference'{module=S#state.mname,
+ value=Atom},
+ validate_objectidentifier1(S,[Rec,Value]);
+validate_objectid(S, [{Atom,EVRef}],[])
+ when atom(Atom),record(EVRef,'Externalvaluereference') ->
+ %% this case when an OBJECT IDENTIFIER value has been parsed as a
+ %% SEQUENCE value OTP-4354
+ Rec = #'Externalvaluereference'{module=S#state.mname,
+ value=Atom},
+ validate_objectidentifier1(S,[Rec,EVRef]);
+validate_objectid(S, _V, _Acc) ->
+ error({value, "illegal OBJECT IDENTIFIER",S}).
+
+
+%% ITU-T Rec. X.680 Annex B - D
+reserved_objectid('itu-t',[]) -> 0;
+reserved_objectid('ccitt',[]) -> 0;
+%% arcs below "itu-t"
+reserved_objectid('recommendation',[0]) -> 0;
+reserved_objectid('question',[0]) -> 1;
+reserved_objectid('administration',[0]) -> 2;
+reserved_objectid('network-operator',[0]) -> 3;
+reserved_objectid('identified-organization',[0]) -> 4;
+%% arcs below "recommendation"
+reserved_objectid('a',[0,0]) -> 1;
+reserved_objectid('b',[0,0]) -> 2;
+reserved_objectid('c',[0,0]) -> 3;
+reserved_objectid('d',[0,0]) -> 4;
+reserved_objectid('e',[0,0]) -> 5;
+reserved_objectid('f',[0,0]) -> 6;
+reserved_objectid('g',[0,0]) -> 7;
+reserved_objectid('h',[0,0]) -> 8;
+reserved_objectid('i',[0,0]) -> 9;
+reserved_objectid('j',[0,0]) -> 10;
+reserved_objectid('k',[0,0]) -> 11;
+reserved_objectid('l',[0,0]) -> 12;
+reserved_objectid('m',[0,0]) -> 13;
+reserved_objectid('n',[0,0]) -> 14;
+reserved_objectid('o',[0,0]) -> 15;
+reserved_objectid('p',[0,0]) -> 16;
+reserved_objectid('q',[0,0]) -> 17;
+reserved_objectid('r',[0,0]) -> 18;
+reserved_objectid('s',[0,0]) -> 19;
+reserved_objectid('t',[0,0]) -> 20;
+reserved_objectid('u',[0,0]) -> 21;
+reserved_objectid('v',[0,0]) -> 22;
+reserved_objectid('w',[0,0]) -> 23;
+reserved_objectid('x',[0,0]) -> 24;
+reserved_objectid('y',[0,0]) -> 25;
+reserved_objectid('z',[0,0]) -> 26;
+
+
+reserved_objectid(iso,[]) -> 1;
+%% arcs below "iso", note that number 1 is not used
+reserved_objectid('standard',[1]) -> 0;
+reserved_objectid('member-body',[1]) -> 2;
+reserved_objectid('identified-organization',[1]) -> 3;
+
+reserved_objectid('joint-iso-itu-t',[]) -> 2;
+reserved_objectid('joint-iso-ccitt',[]) -> 2;
+
+reserved_objectid(_,_) -> false.
+
+
+
+
+
+validate_objectdescriptor(_S,_Value,_Constr) ->
+ ok.
+
+validate_enumerated(S,Id,NamedNumberList,_Constr) when atom(Id) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end;
+validate_enumerated(S,{identifier,_Pos,Id},NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end;
+validate_enumerated(S,#'Externalvaluereference'{value=Id},
+ NamedNumberList,_Constr) ->
+ case lists:keysearch(Id,1,NamedNumberList) of
+ {value,_} -> ok;
+ false -> error({value,"unknown ENUMERATED",S})
+ end.
+
+validate_boolean(_S,_Value,_Constr) ->
+ ok.
+
+validate_octetstring(_S,_Value,_Constr) ->
+ ok.
+
+validate_restrictedstring(_S,_Value,_Def,_Constr) ->
+ ok.
+
+validate_sequence(S=#state{type=Vtype},Value,_Components,_Constr) ->
+ case Vtype of
+ #type{tag=[{tag,'UNIVERSAL',8,'IMPLICIT',32}]} ->
+ %% this is an 'EXTERNAL' (or INSTANCE OF)
+ case Value of
+ [{identification,_}|_RestVal] ->
+ to_EXTERNAL1990(S,Value);
+ _ ->
+ Value
+ end;
+ _ ->
+ Value
+ end.
+
+validate_sequenceof(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_choice(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_set(_S,_Value,_Components,_Constr) ->
+ ok.
+
+validate_setof(_S,_Value,_Components,_Constr) ->
+ ok.
+
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{syntax,Stx}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'direct-reference',Stx}]);
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{'presentation-context-id',I}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'indirect-reference',I}]);
+to_EXTERNAL1990(S,[{identification,{'CHOICE',{'context-negotiation',[{_,PCid},{_,TrStx}]}}}|Rest]) ->
+ to_EXTERNAL1990(S,Rest,[{'indirect-reference',PCid},{'direct-reference',TrStx}]);
+to_EXTERNAL1990(S,_) ->
+ error({value,"illegal value in EXTERNAL type",S}).
+
+to_EXTERNAL1990(S,[V={'data-value-descriptor',_}|Rest],Acc) ->
+ to_EXTERNAL1990(S,Rest,[V|Acc]);
+to_EXTERNAL1990(_S,[{'data-value',Val}],Acc) ->
+ Encoding = {encoding,{'CHOICE',{'octet-aligned',Val}}},
+ lists:reverse([Encoding|Acc]);
+to_EXTERNAL1990(S,_,_) ->
+ error({value,"illegal value in EXTERNAL type",S}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Functions to normalize the default values of SEQUENCE
+%% and SET components into Erlang valid format
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+normalize_value(_,_,mandatory,_) ->
+ mandatory;
+normalize_value(_,_,'OPTIONAL',_) ->
+ 'OPTIONAL';
+normalize_value(S,Type,{'DEFAULT',Value},NameList) ->
+ case catch get_canonic_type(S,Type,NameList) of
+ {'BOOLEAN',CType,_} ->
+ normalize_boolean(S,Value,CType);
+ {'INTEGER',CType,_} ->
+ normalize_integer(S,Value,CType);
+ {'BIT STRING',CType,_} ->
+ normalize_bitstring(S,Value,CType);
+ {'OCTET STRING',CType,_} ->
+ normalize_octetstring(S,Value,CType);
+ {'NULL',_CType,_} ->
+ %%normalize_null(Value);
+ 'NULL';
+ {'OBJECT IDENTIFIER',_,_} ->
+ normalize_objectidentifier(S,Value);
+ {'ObjectDescriptor',_,_} ->
+ normalize_objectdescriptor(Value);
+ {'REAL',_,_} ->
+ normalize_real(Value);
+ {'ENUMERATED',CType,_} ->
+ normalize_enumerated(Value,CType);
+ {'CHOICE',CType,NewNameList} ->
+ normalize_choice(S,Value,CType,NewNameList);
+ {'SEQUENCE',CType,NewNameList} ->
+ normalize_sequence(S,Value,CType,NewNameList);
+ {'SEQUENCE OF',CType,NewNameList} ->
+ normalize_seqof(S,Value,CType,NewNameList);
+ {'SET',CType,NewNameList} ->
+ normalize_set(S,Value,CType,NewNameList);
+ {'SET OF',CType,NewNameList} ->
+ normalize_setof(S,Value,CType,NewNameList);
+ {restrictedstring,CType,_} ->
+ normalize_restrictedstring(S,Value,CType);
+ _ ->
+ io:format("WARNING: could not check default value ~p~n",[Value]),
+ Value
+ end;
+normalize_value(S,Type,Val,NameList) ->
+ normalize_value(S,Type,{'DEFAULT',Val},NameList).
+
+normalize_boolean(S,{Name,Bool},CType) when atom(Name) ->
+ normalize_boolean(S,Bool,CType);
+normalize_boolean(_,true,_) ->
+ true;
+normalize_boolean(_,false,_) ->
+ false;
+normalize_boolean(S,Bool=#'Externalvaluereference'{},CType) ->
+ get_normalized_value(S,Bool,CType,fun normalize_boolean/3,[]);
+normalize_boolean(_,Other,_) ->
+ throw({error,{asn1,{'invalid default value',Other}}}).
+
+normalize_integer(_S,Int,_) when integer(Int) ->
+ Int;
+normalize_integer(_S,{Name,Int},_) when atom(Name),integer(Int) ->
+ Int;
+normalize_integer(S,{Name,Int=#'Externalvaluereference'{}},
+ Type) when atom(Name) ->
+ normalize_integer(S,Int,Type);
+normalize_integer(S,Int=#'Externalvaluereference'{value=Name},Type) ->
+ case Type of
+ NNL when list(NNL) ->
+ case lists:keysearch(Name,1,NNL) of
+ {value,{Name,Val}} ->
+ Val;
+ false ->
+ get_normalized_value(S,Int,Type,
+ fun normalize_integer/3,[])
+ end;
+ _ ->
+ get_normalized_value(S,Int,Type,fun normalize_integer/3,[])
+ end;
+normalize_integer(_,Int,_) ->
+ exit({'Unknown INTEGER value',Int}).
+
+normalize_bitstring(S,Value,Type)->
+ %% There are four different Erlang formats of BIT STRING:
+ %% 1 - a list of ones and zeros.
+ %% 2 - a list of atoms.
+ %% 3 - as an integer, for instance in hexadecimal form.
+ %% 4 - as a tuple {Unused, Binary} where Unused is an integer
+ %% and tells how many bits of Binary are unused.
+ %%
+ %% normalize_bitstring/3 transforms Value according to:
+ %% A to 3,
+ %% B to 1,
+ %% C to 1 or 3
+ %% D to 2,
+ %% Value can be on format:
+ %% A - {hstring, String}, where String is a hexadecimal string.
+ %% B - {bstring, String}, where String is a string on bit format
+ %% C - #'Externalvaluereference'{value=V}, where V is a defined value
+ %% D - list of #'Externalvaluereference', where each value component
+ %% is an identifier corresponing to NamedBits in Type.
+ case Value of
+ {hstring,String} when list(String) ->
+ hstring_to_int(String);
+ {bstring,String} when list(String) ->
+ bstring_to_bitlist(String);
+ Rec when record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,Value,Type,
+ fun normalize_bitstring/3,[]);
+ RecList when list(RecList) ->
+ case Type of
+ NBL when list(NBL) ->
+ F = fun(#'Externalvaluereference'{value=Name}) ->
+ case lists:keysearch(Name,1,NBL) of
+ {value,{Name,_}} ->
+ Name;
+ Other ->
+ throw({error,Other})
+ end;
+ (Other) ->
+ throw({error,Other})
+ end,
+ case catch lists:map(F,RecList) of
+ {error,Reason} ->
+ io:format("WARNING: default value not "
+ "compatible with type definition ~p~n",
+ [Reason]),
+ Value;
+ NewList ->
+ NewList
+ end;
+ _ ->
+ io:format("WARNING: default value not "
+ "compatible with type definition ~p~n",
+ [RecList]),
+ Value
+ end;
+ {Name,String} when atom(Name) ->
+ normalize_bitstring(S,String,Type);
+ Other ->
+ io:format("WARNING: illegal default value ~p~n",[Other]),
+ Value
+ end.
+
+hstring_to_int(L) when list(L) ->
+ hstring_to_int(L,0).
+hstring_to_int([H|T],Acc) when H >= $A, H =< $F ->
+ hstring_to_int(T,(Acc bsl 4) + (H - $A + 10) ) ;
+hstring_to_int([H|T],Acc) when H >= $0, H =< $9 ->
+ hstring_to_int(T,(Acc bsl 4) + (H - $0));
+hstring_to_int([],Acc) ->
+ Acc.
+
+bstring_to_bitlist([H|T]) when H == $0; H == $1 ->
+ [H - $0 | bstring_to_bitlist(T)];
+bstring_to_bitlist([]) ->
+ [].
+
+%% normalize_octetstring/1 changes representation of input Value to a
+%% list of octets.
+%% Format of Value is one of:
+%% {bstring,String} each element in String corresponds to one bit in an octet
+%% {hstring,String} each element in String corresponds to one byte in an octet
+%% #'Externalvaluereference'
+normalize_octetstring(S,Value,CType) ->
+ case Value of
+ {bstring,String} ->
+ bstring_to_octetlist(String);
+ {hstring,String} ->
+ hstring_to_octetlist(String);
+ Rec when record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,Value,CType,
+ fun normalize_octetstring/3,[]);
+ {Name,String} when atom(Name) ->
+ normalize_octetstring(S,String,CType);
+ List when list(List) ->
+ %% check if list elements are valid octet values
+ lists:map(fun([])-> ok;
+ (H)when H > 255->
+ io:format("WARNING: not legal octet value ~p in OCTET STRING, ~p~n",[H,List]);
+ (_)-> ok
+ end, List),
+ List;
+ Other ->
+ io:format("WARNING: unknown default value ~p~n",[Other]),
+ Value
+ end.
+
+
+bstring_to_octetlist([]) ->
+ [];
+bstring_to_octetlist([H|T]) when H == $0 ; H == $1 ->
+ bstring_to_octetlist(T,6,[(H - $0) bsl 7]).
+bstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H == $0; H == $1 ->
+ bstring_to_octetlist(T, 7, [0,Hacc + (H -$0)| Tacc]);
+bstring_to_octetlist([H|T],BSL,[Hacc|Tacc]) when H == $0; H == $1 ->
+ bstring_to_octetlist(T, BSL-1, [Hacc + ((H - $0) bsl BSL)| Tacc]);
+bstring_to_octetlist([],7,[0|Acc]) ->
+ lists:reverse(Acc);
+bstring_to_octetlist([],_,Acc) ->
+ lists:reverse(Acc).
+
+hstring_to_octetlist([]) ->
+ [];
+hstring_to_octetlist(L) ->
+ hstring_to_octetlist(L,4,[]).
+hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $A, H =< $F ->
+ hstring_to_octetlist(T,4,[Hacc + (H - $A + 10)|Tacc]);
+hstring_to_octetlist([H|T],BSL,Acc) when H >= $A, H =< $F ->
+ hstring_to_octetlist(T,0,[(H - $A + 10) bsl BSL|Acc]);
+hstring_to_octetlist([H|T],0,[Hacc|Tacc]) when H >= $0; H =< $9 ->
+ hstring_to_octetlist(T,4,[Hacc + (H - $0)|Tacc]);
+hstring_to_octetlist([H|T],BSL,Acc) when H >= $0; H =< $9 ->
+ hstring_to_octetlist(T,0,[(H - $0) bsl BSL|Acc]);
+hstring_to_octetlist([],_,Acc) ->
+ lists:reverse(Acc).
+
+normalize_objectidentifier(S,Value) ->
+ validate_objectidentifier(S,Value,[]).
+
+normalize_objectdescriptor(Value) ->
+ Value.
+
+normalize_real(Value) ->
+ Value.
+
+normalize_enumerated(#'Externalvaluereference'{value=V},CType)
+ when list(CType) ->
+ normalize_enumerated2(V,CType);
+normalize_enumerated(Value,CType) when atom(Value),list(CType) ->
+ normalize_enumerated2(Value,CType);
+normalize_enumerated({Name,EnumV},CType) when atom(Name) ->
+ normalize_enumerated(EnumV,CType);
+normalize_enumerated(Value,{CType1,CType2}) when list(CType1), list(CType2)->
+ normalize_enumerated(Value,CType1++CType2);
+normalize_enumerated(V,CType) ->
+ io:format("WARNING: Enumerated unknown type ~p~n",[CType]),
+ V.
+normalize_enumerated2(V,Enum) ->
+ case lists:keysearch(V,1,Enum) of
+ {value,{Val,_}} -> Val;
+ _ ->
+ io:format("WARNING: Enumerated value is not correct ~p~n",[V]),
+ V
+ end.
+
+normalize_choice(S,{'CHOICE',{C,V}},CType,NameList) when atom(C) ->
+ Value =
+ case V of
+ Rec when record(Rec,'Externalvaluereference') ->
+ get_normalized_value(S,V,CType,
+ fun normalize_choice/4,
+ [NameList]);
+ _ -> V
+ end,
+ case catch lists:keysearch(C,#'ComponentType'.name,CType) of
+ {value,#'ComponentType'{typespec=CT,name=Name}} ->
+ {C,normalize_value(S,CT,{'DEFAULT',Value},
+ [Name|NameList])};
+ Other ->
+ io:format("WARNING: Wrong format of type/value ~p/~p~n",
+ [Other,Value]),
+ {C,Value}
+ end;
+normalize_choice(S,{'DEFAULT',ValueList},CType,NameList) ->
+ lists:map(fun(X)-> normalize_choice(S,X,CType,NameList) end, ValueList);
+normalize_choice(S,Val=#'Externalvaluereference'{},CType,NameList) ->
+ {_,#valuedef{value=V}}=get_referenced_type(S,Val),
+ normalize_choice(S,{'CHOICE',V},CType,NameList);
+% get_normalized_value(S,Val,CType,fun normalize_choice/4,[NameList]);
+normalize_choice(S,{Name,ChoiceVal},CType,NameList)
+ when atom(Name) ->
+ normalize_choice(S,ChoiceVal,CType,NameList).
+
+normalize_sequence(S,{Name,Value},Components,NameList)
+ when atom(Name),list(Value) ->
+ normalize_sequence(S,Value,Components,NameList);
+normalize_sequence(S,Value,Components,NameList) ->
+ normalized_record('SEQUENCE',S,Value,Components,NameList).
+
+normalize_set(S,{Name,Value},Components,NameList)
+ when atom(Name),list(Value) ->
+ normalized_record('SET',S,Value,Components,NameList);
+normalize_set(S,Value,Components,NameList) ->
+ normalized_record('SET',S,Value,Components,NameList).
+
+normalized_record(SorS,S,Value,Components,NameList) ->
+ NewName = list_to_atom(asn1ct_gen:list2name(NameList)),
+ NoComps = length(Components),
+ case normalize_seq_or_set(SorS,S,Value,Components,NameList,[]) of
+ ListOfVals when length(ListOfVals) == NoComps ->
+ list_to_tuple([NewName|ListOfVals]);
+ _ ->
+ error({type,{illegal,default,value,Value},S})
+ end.
+
+normalize_seq_or_set(SorS,S,[{Cname,V}|Vs],
+ [#'ComponentType'{name=Cname,typespec=TS}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Cname|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',V},NewNameList),
+ normalize_seq_or_set(SorS,S,Vs,Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(SorS,S,Values=[{_Cname1,_V}|_Vs],
+ [#'ComponentType'{prop='OPTIONAL'}|Cs],
+ NameList,Acc) ->
+ normalize_seq_or_set(SorS,S,Values,Cs,NameList,[asn1_NOVALUE|Acc]);
+normalize_seq_or_set(SorS,S,Values=[{_Cname1,_V}|_Vs],
+ [#'ComponentType'{name=Cname2,typespec=TS,
+ prop={'DEFAULT',Value}}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Cname2|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',Value},NewNameList),
+ normalize_seq_or_set(SorS,S,Values,Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(_SorS,_S,[],[],_,Acc) ->
+ lists:reverse(Acc);
+%% If default value is {} ComponentTypes in SEQUENCE are marked DEFAULT
+%% or OPTIONAL (or the type is defined SEQUENCE{}, which is handled by
+%% the previous case).
+normalize_seq_or_set(SorS,S,[],
+ [#'ComponentType'{name=Name,typespec=TS,
+ prop={'DEFAULT',Value}}|Cs],
+ NameList,Acc) ->
+ NewNameList =
+ case TS#type.def of
+ #'Externaltypereference'{type=TName} ->
+ [TName];
+ _ -> [Name|NameList]
+ end,
+ NVal = normalize_value(S,TS,{'DEFAULT',Value},NewNameList),
+ normalize_seq_or_set(SorS,S,[],Cs,NameList,[NVal|Acc]);
+normalize_seq_or_set(SorS,S,[],[#'ComponentType'{prop='OPTIONAL'}|Cs],
+ NameList,Acc) ->
+ normalize_seq_or_set(SorS,S,[],Cs,NameList,[asn1_NOVALUE|Acc]);
+normalize_seq_or_set(SorS,S,Value=#'Externalvaluereference'{},
+ Cs,NameList,Acc) ->
+ get_normalized_value(S,Value,Cs,fun normalize_seq_or_set/6,
+ [SorS,NameList,Acc]);
+normalize_seq_or_set(_SorS,S,V,_,_,_) ->
+ error({type,{illegal,default,value,V},S}).
+
+normalize_seqof(S,Value,Type,NameList) ->
+ normalize_s_of('SEQUENCE OF',S,Value,Type,NameList).
+
+normalize_setof(S,Value,Type,NameList) ->
+ normalize_s_of('SET OF',S,Value,Type,NameList).
+
+normalize_s_of(SorS,S,Value,Type,NameList) when list(Value) ->
+ DefValueList = lists:map(fun(X) -> {'DEFAULT',X} end,Value),
+ Suffix = asn1ct_gen:constructed_suffix(SorS,Type),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ NewNameList =
+ case WhatKind of
+ {constructed,bif} ->
+ [Suffix|NameList];
+ #'Externaltypereference'{type=Name} ->
+ [Name];
+ _ -> []
+ end,
+ NormFun = fun (X) -> normalize_value(S,Type,X,
+ NewNameList) end,
+ case catch lists:map(NormFun, DefValueList) of
+ List when list(List) ->
+ List;
+ _ ->
+ io:format("WARNING: ~p could not handle value ~p~n",
+ [SorS,Value]),
+ Value
+ end;
+normalize_s_of(SorS,S,Value,Type,NameList)
+ when record(Value,'Externalvaluereference') ->
+ get_normalized_value(S,Value,Type,fun normalize_s_of/5,
+ [SorS,NameList]).
+% case catch get_referenced_type(S,Value) of
+% {_,#valuedef{value=V}} ->
+% normalize_s_of(SorS,S,V,Type);
+% {error,Reason} ->
+% io:format("WARNING: ~p could not handle value ~p~n",
+% [SorS,Value]),
+% Value;
+% {_,NewVal} ->
+% normalize_s_of(SorS,S,NewVal,Type);
+% _ ->
+% io:format("WARNING: ~p could not handle value ~p~n",
+% [SorS,Value]),
+% Value
+% end.
+
+
+%% normalize_restrictedstring handles all format of restricted strings.
+%% tuple case
+normalize_restrictedstring(_S,[Int1,Int2],_) when integer(Int1),integer(Int2) ->
+ {Int1,Int2};
+%% quadruple case
+normalize_restrictedstring(_S,[Int1,Int2,Int3,Int4],_) when integer(Int1),
+ integer(Int2),
+ integer(Int3),
+ integer(Int4) ->
+ {Int1,Int2,Int3,Int4};
+%% character string list case
+normalize_restrictedstring(S,[H|T],CType) when list(H);tuple(H) ->
+ [normalize_restrictedstring(S,H,CType)|normalize_restrictedstring(S,T,CType)];
+%% character sting case
+normalize_restrictedstring(_S,CString,_) when list(CString) ->
+ Fun =
+ fun(X) ->
+ if
+ $X =< 255, $X >= 0 ->
+ ok;
+ true ->
+ io:format("WARNING: illegal character in string"
+ " ~p~n",[X])
+ end
+ end,
+ lists:foreach(Fun,CString),
+ CString;
+%% definedvalue case or argument in a parameterized type
+normalize_restrictedstring(S,ERef,CType) when record(ERef,'Externalvaluereference') ->
+ get_normalized_value(S,ERef,CType,
+ fun normalize_restrictedstring/3,[]);
+%%
+normalize_restrictedstring(S,{Name,Val},CType) when atom(Name) ->
+ normalize_restrictedstring(S,Val,CType).
+
+
+get_normalized_value(S,Val,Type,Func,AddArg) ->
+ case catch get_referenced_type(S,Val) of
+ {_,#valuedef{type=_T,value=V}} ->
+ %% should check that Type and T equals
+ call_Func(S,V,Type,Func,AddArg);
+ {error,_} ->
+ io:format("WARNING: default value not "
+ "comparable ~p~n",[Val]),
+ Val;
+ {_,NewVal} ->
+ call_Func(S,NewVal,Type,Func,AddArg);
+ _ ->
+ io:format("WARNING: default value not "
+ "comparable ~p~n",[Val]),
+ Val
+ end.
+
+call_Func(S,Val,Type,Func,ArgList) ->
+ case ArgList of
+ [] ->
+ Func(S,Val,Type);
+ [LastArg] ->
+ Func(S,Val,Type,LastArg);
+ [Arg1,LastArg1] ->
+ Func(Arg1,S,Val,Type,LastArg1);
+ [Arg1,LastArg1,LastArg2] ->
+ Func(Arg1,S,Val,Type,LastArg1,LastArg2)
+ end.
+
+
+get_canonic_type(S,Type,NameList) ->
+ {InnerType,NewType,NewNameList} =
+ case Type#type.def of
+ Name when atom(Name) ->
+ {Name,Type,NameList};
+ Ref when record(Ref,'Externaltypereference') ->
+ {_,#typedef{name=Name,typespec=RefedType}} =
+ get_referenced_type(S,Ref),
+ get_canonic_type(S,RefedType,[Name]);
+ {Name,T} when atom(Name) ->
+ {Name,T,NameList};
+ Seq when record(Seq,'SEQUENCE') ->
+ {'SEQUENCE',Seq#'SEQUENCE'.components,NameList};
+ Set when record(Set,'SET') ->
+ {'SET',Set#'SET'.components,NameList}
+ end,
+ {asn1ct_gen:unify_if_string(InnerType),NewType,NewNameList}.
+
+
+
+check_ptype(_S,Type,Ts) when record(Ts,type) ->
+ %Tag = Ts#type.tag,
+ %Constr = Ts#type.constraint,
+ Def = Ts#type.def,
+ NewDef=
+ case Def of
+ Seq when record(Seq,'SEQUENCE') ->
+ #newt{type=Seq#'SEQUENCE'{pname=Type#ptypedef.name}};
+ Set when record(Set,'SET') ->
+ #newt{type=Set#'SET'{pname=Type#ptypedef.name}};
+ _Other ->
+ #newt{}
+ end,
+ Ts2 = case NewDef of
+ #newt{type=unchanged} ->
+ Ts;
+ #newt{type=TDef}->
+ Ts#type{def=TDef}
+ end,
+ Ts2.
+
+
+% check_type(S,Type,ObjSpec={{objectclassname,_},_}) ->
+% check_class(S,ObjSpec);
+check_type(_S,Type,Ts) when record(Type,typedef),
+ (Type#typedef.checked==true) ->
+ Ts;
+check_type(_S,Type,Ts) when record(Type,typedef),
+ (Type#typedef.checked==idle) -> % the check is going on
+ Ts;
+check_type(S=#state{recordtopname=TopName},Type,Ts) when record(Ts,type) ->
+ {Def,Tag,Constr} =
+ case match_parameters(Ts#type.def,S#state.parameters) of
+ #type{constraint=_Ctmp,def=Dtmp} ->
+ {Dtmp,Ts#type.tag,Ts#type.constraint};
+ Dtmp ->
+ {Dtmp,Ts#type.tag,Ts#type.constraint}
+ end,
+ TempNewDef = #newt{type=Def,tag=Tag,constraint=Constr},
+ TestFun =
+ fun(Tref) ->
+ {_,MaybeChoice} = get_referenced_type(S,Tref),
+ case catch((MaybeChoice#typedef.typespec)#type.def) of
+ {'CHOICE',_} ->
+ maybe_illicit_implicit_tag(choice,Tag);
+ 'ANY' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ 'ANY DEFINED BY' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ 'ASN1_OPEN_TYPE' ->
+ maybe_illicit_implicit_tag(open_type,Tag);
+ _ ->
+ Tag
+ end
+ end,
+ NewDef=
+ case Def of
+ Ext when record(Ext,'Externaltypereference') ->
+ {_,RefTypeDef} = get_referenced_type(S,Ext),
+% case RefTypeDef of
+% Class when record(Class,classdef) ->
+% throw({asn1_class,Class});
+% _ -> ok
+% end,
+ case is_class(S,RefTypeDef) of
+ true -> throw({asn1_class,RefTypeDef});
+ _ -> ok
+ end,
+ Ct = TestFun(Ext),
+ RefType =
+%case S#state.erule of
+% ber_bin_v2 ->
+ case RefTypeDef#typedef.checked of
+ true ->
+ RefTypeDef#typedef.typespec;
+ _ ->
+ NewRefTypeDef1 = RefTypeDef#typedef{checked=idle},
+ asn1_db:dbput(S#state.mname,
+ NewRefTypeDef1#typedef.name,NewRefTypeDef1),
+ RefType1 =
+ check_type(S,RefTypeDef,RefTypeDef#typedef.typespec),
+ NewRefTypeDef2 =
+ RefTypeDef#typedef{checked=true,typespec = RefType1},
+ asn1_db:dbput(S#state.mname,
+ NewRefTypeDef2#typedef.name,NewRefTypeDef2),
+ %% update the type and mark as checked
+ RefType1
+ end,
+% _ -> RefTypeDef#typedef.typespec
+% end,
+
+ case asn1ct_gen:prim_bif(asn1ct_gen:get_inner(RefType#type.def)) of
+ true ->
+ %% Here we expand to a built in type and inline it
+ TempNewDef#newt{
+ type=
+ RefType#type.def,
+ tag=
+ merge_tags(Ct,RefType#type.tag),
+ constraint=
+ merge_constraints(check_constraints(S,Constr),
+ RefType#type.constraint)};
+ _ ->
+ %% Here we only expand the tags and keep the ext ref
+
+ TempNewDef#newt{
+ type=
+ check_externaltypereference(S,Ext),
+ tag =
+ case S#state.erule of
+ ber_bin_v2 ->
+ merge_tags(Ct,RefType#type.tag);
+ _ ->
+ Ct
+ end
+ }
+ end;
+ 'ANY' ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+ {'ANY_DEFINED_BY',_} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+ 'INTEGER' ->
+ check_integer(S,[],Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
+
+ {'INTEGER',NamedNumberList} ->
+ TempNewDef#newt{type={'INTEGER',check_integer(S,NamedNumberList,Constr)},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_INTEGER))};
+ {'BIT STRING',NamedNumberList} ->
+ NewL = check_bitstring(S,NamedNumberList,Constr),
+%% erlang:display({asn1ct_check,NamedNumberList,NewL}),
+ TempNewDef#newt{type={'BIT STRING',NewL},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BIT_STRING))};
+ 'NULL' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_NULL))};
+ 'OBJECT IDENTIFIER' ->
+ check_objectidentifier(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OBJECT_IDENTIFIER))};
+ 'ObjectDescriptor' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OBJECT_DESCRIPTOR))};
+ 'EXTERNAL' ->
+%% AssociatedType = asn1_db:dbget(S#state.mname,'EXTERNAL'),
+%% #newt{type=check_type(S,Type,AssociatedType)};
+ put(external,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='EXTERNAL'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_EXTERNAL))};
+ {'INSTANCE OF',DefinedObjectClass,Constraint} ->
+ %% check that DefinedObjectClass is of TYPE-IDENTIFIER class
+ %% If Constraint is empty make it the general INSTANCE OF type
+ %% If Constraint is not empty make an inlined type
+ %% convert INSTANCE OF to the associated type
+ IOFDef=check_instance_of(S,DefinedObjectClass,Constraint),
+ TempNewDef#newt{type=IOFDef,
+ tag=merge_tags(Tag,?TAG_CONSTRUCTED(?N_INSTANCE_OF))};
+ {'ENUMERATED',NamedNumberList} ->
+ TempNewDef#newt{type=
+ {'ENUMERATED',
+ check_enumerated(S,NamedNumberList,Constr)},
+ tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_ENUMERATED))};
+ 'EMBEDDED PDV' ->
+% AssociatedType = asn1_db:dbget(S#state.mname,'EMBEDDED PDV'),
+% CheckedType = check_type(S,Type,
+% AssociatedType#typedef.typespec),
+ put(embedded_pdv,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='EMBEDDED PDV'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_EMBEDDED_PDV))};
+ 'BOOLEAN'->
+ check_boolean(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BOOLEAN))};
+ 'OCTET STRING' ->
+ check_octetstring(S,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_OCTET_STRING))};
+ 'NumericString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_NumericString))};
+ 'TeletexString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_TeletexString))};
+ 'VideotexString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_VideotexString))};
+ 'UTCTime' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UTCTime))};
+ 'GeneralizedTime' ->
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GeneralizedTime))};
+ 'GraphicString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GraphicString))};
+ 'VisibleString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_VisibleString))};
+ 'GeneralString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_GeneralString))};
+ 'PrintableString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_PrintableString))};
+ 'IA5String' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_IA5String))};
+ 'BMPString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_BMPString))};
+ 'UniversalString' ->
+ check_restrictedstring(S,Def,Constr),
+ TempNewDef#newt{tag=
+ merge_tags(Tag,?TAG_PRIMITIVE(?N_UniversalString))};
+ 'CHARACTER STRING' ->
+% AssociatedType = asn1_db:dbget(S#state.mname,
+% 'CHARACTER STRING'),
+% CheckedType = check_type(S,Type,
+% AssociatedType#typedef.typespec),
+ put(character_string,unchecked),
+ TempNewDef#newt{type=
+ #'Externaltypereference'{module=S#state.mname,
+ type='CHARACTER STRING'},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_CHARACTER_STRING))};
+ Seq when record(Seq,'SEQUENCE') ->
+ RecordName =
+ case TopName of
+ [] ->
+ [Type#typedef.name];
+ _ ->
+ TopName
+ end,
+ {TableCInf,Components} =
+ check_sequence(S#state{recordtopname=
+ RecordName},
+ Type,Seq#'SEQUENCE'.components),
+ TempNewDef#newt{type=Seq#'SEQUENCE'{tablecinf=TableCInf,
+ components=Components},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SEQUENCE))};
+ {'SEQUENCE OF',Components} ->
+ TempNewDef#newt{type={'SEQUENCE OF',check_sequenceof(S,Type,Components)},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SEQUENCE))};
+ {'CHOICE',Components} ->
+ Ct = maybe_illicit_implicit_tag(choice,Tag),
+ TempNewDef#newt{type={'CHOICE',check_choice(S,Type,Components)},tag=Ct};
+ Set when record(Set,'SET') ->
+ RecordName=
+ case TopName of
+ [] ->
+ [Type#typedef.name];
+ _ ->
+ TopName
+ end,
+ {Sorted,TableCInf,Components} =
+ check_set(S#state{recordtopname=RecordName},
+ Type,Set#'SET'.components),
+ TempNewDef#newt{type=Set#'SET'{sorted=Sorted,
+ tablecinf=TableCInf,
+ components=Components},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))};
+ {'SET OF',Components} ->
+ TempNewDef#newt{type={'SET OF',check_setof(S,Type,Components)},
+ tag=
+ merge_tags(Tag,?TAG_CONSTRUCTED(?N_SET))};
+ %% This is a temporary hack until the full Information Obj Spec
+ %% in X.681 is supported
+ {{typereference,_,'TYPE-IDENTIFIER'},[{typefieldreference,_,'Type'}]} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+
+ {#'Externaltypereference'{type='TYPE-IDENTIFIER'},
+ [{typefieldreference,_,'Type'}]} ->
+ Ct=maybe_illicit_implicit_tag(open_type,Tag),
+ TempNewDef#newt{type='ASN1_OPEN_TYPE',tag=Ct};
+
+ {pt,Ptype,ParaList} ->
+ %% Ptype might be a parameterized - type, object set or
+ %% value set. If it isn't a parameterized type notify the
+ %% calling function.
+ {_,Ptypedef} = get_referenced_type(S,Ptype),
+ notify_if_not_ptype(S,Ptypedef),
+ NewParaList = [match_parameters(TmpParam,S#state.parameters)||
+ TmpParam <- ParaList],
+ Instance = instantiate_ptype(S,Ptypedef,NewParaList),
+ TempNewDef#newt{type=Instance#type.def,
+ tag=merge_tags(Tag,Instance#type.tag),
+ constraint=Instance#type.constraint,
+ inlined=yes};
+
+% {ClRef,FieldRefList} when record(ClRef,'Externaltypereference') ->
+ OCFT=#'ObjectClassFieldType'{class=ClRef} ->
+ %% this case occures in a SEQUENCE when
+ %% the type of the component is a ObjectClassFieldType
+ ClassSpec = check_class(S,ClRef),
+ NewTypeDef = maybe_open_type(S,ClassSpec,OCFT,Constr),
+ InnerTag = get_innertag(S,NewTypeDef),
+ MergedTag = merge_tags(Tag,InnerTag),
+ Ct =
+ case is_open_type(NewTypeDef) of
+ true ->
+ maybe_illicit_implicit_tag(open_type,MergedTag);
+ _ ->
+ MergedTag
+ end,
+ TempNewDef#newt{type=NewTypeDef,tag=Ct};
+ {valueset,Vtype} ->
+ TempNewDef#newt{type={valueset,check_type(S,Type,Vtype)}};
+ Other ->
+ exit({'cant check' ,Other})
+ end,
+ Ts2 = case NewDef of
+ #newt{type=unchanged} ->
+ Ts#type{def=Def};
+ #newt{type=TDef}->
+ Ts#type{def=TDef}
+ end,
+ NewTag = case NewDef of
+ #newt{tag=unchanged} ->
+ Tag;
+ #newt{tag=TT} ->
+ TT
+ end,
+ T3 = Ts2#type{tag = lists:map(fun(TempTag = #tag{type={default,TTx}}) ->
+ TempTag#tag{type=TTx};
+ (Else) -> Else end, NewTag)},
+ T4 = case NewDef of
+ #newt{constraint=unchanged} ->
+ T3#type{constraint=Constr};
+ #newt{constraint=NewConstr} ->
+ T3#type{constraint=NewConstr}
+ end,
+ T5 = T4#type{inlined=NewDef#newt.inlined},
+ T5#type{constraint=check_constraints(S,T5#type.constraint)}.
+
+
+get_innertag(_S,#'ObjectClassFieldType'{type=Type}) ->
+ case Type of
+ #type{tag=Tag} -> Tag;
+ {fixedtypevaluefield,_,#type{tag=Tag}} -> Tag;
+ {TypeFieldName,_} when atom(TypeFieldName) -> [];
+ _ -> []
+ end;
+get_innertag(_S,_) ->
+ [].
+
+is_class(_S,#classdef{}) ->
+ true;
+is_class(S,#typedef{typespec=#type{def=Eref}})
+ when record(Eref,'Externaltypereference')->
+ {_,NextDef} = get_referenced_type(S,Eref),
+ is_class(S,NextDef);
+is_class(_,_) ->
+ false.
+
+get_class_def(_S,CD=#classdef{}) ->
+ CD;
+get_class_def(S,#typedef{typespec=#type{def=Eref}})
+ when record(Eref,'Externaltypereference') ->
+ {_,NextDef} = get_referenced_type(S,Eref),
+ get_class_def(S,NextDef).
+
+maybe_illicit_implicit_tag(Kind,Tag) ->
+ case Tag of
+ [#tag{type='IMPLICIT'}|_T] ->
+ throw({error,{asn1,{implicit_tag_before,Kind}}});
+ [ChTag = #tag{type={default,_}}|T] ->
+ case Kind of
+ open_type ->
+ [ChTag#tag{type='EXPLICIT',form=32}|T]; %X.680 30.6c, X.690 8.14.2
+ choice ->
+ [ChTag#tag{type='EXPLICIT',form=32}|T] % X.680 28.6 c, 30.6c
+ end;
+ _ ->
+ Tag % unchanged
+ end.
+
+%% maybe_open_type/2 -> {ClassSpec,FieldRefList} | 'ASN1_OPEN_TYPE'
+%% if the FieldRefList points out a typefield and the class don't have
+%% any UNIQUE field, so that a component relation constraint cannot specify
+%% the type of a typefield, return 'ASN1_OPEN_TYPE', otherwise return
+%% {ClassSpec,FieldRefList}.
+maybe_open_type(S,ClassSpec=#objectclass{fields=Fs},
+ OCFT=#'ObjectClassFieldType'{fieldname=FieldRefList},
+ Constr) ->
+ Type = get_ObjectClassFieldType(S,Fs,FieldRefList),
+ FieldNames=get_referenced_fieldname(FieldRefList),
+ case lists:last(FieldRefList) of
+ {valuefieldreference,_} ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type=Type};
+ {typefieldreference,_} ->
+ case {catch get_unique_fieldname(#classdef{typespec=ClassSpec}),
+ asn1ct_gen:get_constraint(Constr,componentrelation)}of
+ {Tuple,_} when tuple(Tuple) ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type='ASN1_OPEN_TYPE'};
+ {_,no} ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type='ASN1_OPEN_TYPE'};
+ _ ->
+ OCFT#'ObjectClassFieldType'{class=ClassSpec,
+ fieldname=FieldNames,
+ type=Type}
+ end
+ end.
+
+is_open_type(#'ObjectClassFieldType'{type='ASN1_OPEN_TYPE'}) ->
+ true;
+is_open_type(#'ObjectClassFieldType'{}) ->
+ false.
+
+
+notify_if_not_ptype(S,#pvaluesetdef{type=Type}) ->
+ case Type#type.def of
+ Ref when record(Ref,'Externaltypereference') ->
+ case get_referenced_type(S,Ref) of
+ {_,#classdef{}} ->
+ throw(pobjectsetdef);
+ {_,#typedef{}} ->
+ throw(pvalueset)
+ end;
+ T when record(T,type) -> % this must be a value set
+ throw(pvalueset)
+ end;
+notify_if_not_ptype(_S,#ptypedef{}) ->
+ ok.
+
+% fix me
+instantiate_ptype(S,Ptypedef,ParaList) ->
+ #ptypedef{args=Args,typespec=Type} = Ptypedef,
+% Args = get_pt_args(Ptypedef),
+% Type = get_pt_spec(Ptypedef),
+ MatchedArgs = match_args(Args, ParaList, []),
+ NewS = S#state{type=Type,parameters=MatchedArgs,abscomppath=[]},
+ %The abscomppath must be empty since a table constraint in a
+ %parameterized type only can refer to components within the type
+ check_type(NewS, Ptypedef, Type).
+
+get_pt_args(#ptypedef{args=Args}) ->
+ Args;
+get_pt_args(#pvaluesetdef{args=Args}) ->
+ Args;
+get_pt_args(#pvaluedef{args=Args}) ->
+ Args;
+get_pt_args(#pobjectdef{args=Args}) ->
+ Args;
+get_pt_args(#pobjectsetdef{args=Args}) ->
+ Args.
+
+get_pt_spec(#ptypedef{typespec=Type}) ->
+ Type;
+get_pt_spec(#pvaluedef{value=Value}) ->
+ Value;
+get_pt_spec(#pvaluesetdef{valueset=VS}) ->
+ VS;
+get_pt_spec(#pobjectdef{def=Def}) ->
+ Def;
+get_pt_spec(#pobjectsetdef{def=Def}) ->
+ Def.
+
+
+
+match_args([FormArg|Ft], [ActArg|At], Acc) ->
+ match_args(Ft, At, [{FormArg,ActArg}|Acc]);
+match_args([], [], Acc) ->
+ lists:reverse(Acc);
+match_args(_, _, _) ->
+ throw({error,{asn1,{wrong_number_of_arguments}}}).
+
+check_constraints(S,C) when list(C) ->
+ check_constraints(S, C, []);
+check_constraints(S,C) when record(C,constraint) ->
+ check_constraints(S, C#constraint.c, []).
+
+
+resolv_tuple_or_list(S,List) when list(List) ->
+ lists:map(fun(X)->resolv_value(S,X) end, List);
+resolv_tuple_or_list(S,{Lb,Ub}) ->
+ {resolv_value(S,Lb),resolv_value(S,Ub)}.
+
+%%%-----------------------------------------
+%% If the constraint value is a defined value the valuename
+%% is replaced by the actual value
+%%
+resolv_value(S,Val) ->
+ case match_parameters(Val, S#state.parameters) of
+ Id -> % unchanged
+ resolv_value1(S,Id);
+ Other ->
+ resolv_value(S,Other)
+ end.
+
+resolv_value1(S = #state{mname=M,inputmodules=InpMods},
+ V=#'Externalvaluereference'{pos=Pos,module=ExtM,value=Name}) ->
+ case ExtM of
+ M -> resolv_value2(S,M,Name,Pos);
+ _ ->
+ case lists:member(ExtM,InpMods) of
+ true ->
+ resolv_value2(S,M,Name,Pos);
+ false ->
+ V
+ end
+ end;
+resolv_value1(S,{gt,V}) ->
+ case V of
+ Int when integer(Int) ->
+ V + 1;
+ #valuedef{value=Int} ->
+ 1 + resolv_value(S,Int);
+ Other ->
+ throw({error,{asn1,{undefined_type_or_value,Other}}})
+ end;
+resolv_value1(S,{lt,V}) ->
+ case V of
+ Int when integer(Int) ->
+ V - 1;
+ #valuedef{value=Int} ->
+ resolv_value(S,Int) - 1;
+ Other ->
+ throw({error,{asn1,{undefined_type_or_value,Other}}})
+ end;
+resolv_value1(S,{'ValueFromObject',{object,Object},[{valuefieldreference,
+ FieldName}]}) ->
+ %% FieldName can hold either a fixed-type value or a variable-type value
+ %% Object is a DefinedObject, i.e. a #'Externaltypereference'
+ {_,ObjTDef} = get_referenced_type(S,Object),
+ TS = check_object(S,ObjTDef,ObjTDef#typedef.typespec),
+ {_,_,Components} = TS#'Object'.def,
+ case lists:keysearch(FieldName,1,Components) of
+ {value,{_,#valuedef{value=Val}}} ->
+ Val;
+ _ ->
+ error({value,"illegal value in constraint",S})
+ end;
+% resolv_value1(S,{'ValueFromObject',{po,Object,Params},FieldName}) ->
+% %% FieldName can hold either a fixed-type value or a variable-type value
+% %% Object is a ParameterizedObject
+resolv_value1(_,V) ->
+ V.
+
+resolv_value2(S,ModuleName,Name,Pos) ->
+ case asn1_db:dbget(ModuleName,Name) of
+ undefined ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ {_,V2} = get_referenced(S,Imodule,Name,Pos),
+ V2#valuedef.value;
+ _ ->
+ throw({error,{asn1,{undefined_type_or_value,Name}}})
+ end;
+ Val ->
+ Val#valuedef.value
+ end.
+
+check_constraints(S,[{'ContainedSubtype',Type} | Rest], Acc) ->
+ {_,CTDef} = get_referenced_type(S,Type#type.def),
+ CType = check_type(S,S#state.tname,CTDef#typedef.typespec),
+ check_constraints(S,Rest,CType#type.constraint ++ Acc);
+check_constraints(S,[C | Rest], Acc) ->
+ check_constraints(S,Rest,[check_constraint(S,C) | Acc]);
+check_constraints(S,[],Acc) ->
+% io:format("Acc: ~p~n",[Acc]),
+ C = constraint_merge(S,lists:reverse(Acc)),
+% io:format("C: ~p~n",[C]),
+ lists:flatten(C).
+
+
+range_check(F={FixV,FixV}) ->
+% FixV;
+ F;
+range_check(VR={Lb,Ub}) when Lb < Ub ->
+ VR;
+range_check(Err={_,_}) ->
+ throw({error,{asn1,{illegal_size_constraint,Err}}});
+range_check(Value) ->
+ Value.
+
+check_constraint(S,Ext) when record(Ext,'Externaltypereference') ->
+ check_externaltypereference(S,Ext);
+
+
+check_constraint(S,{'SizeConstraint',{Lb,Ub}})
+ when list(Lb);tuple(Lb),size(Lb)==2 ->
+ case Lb of
+ #'Externalvaluereference'{} ->
+ check_constraint(S,{'SizeConstraint',{resolv_value(S,Lb),Ub}});
+ _ ->
+ NewLb = range_check(resolv_tuple_or_list(S,Lb)),
+ NewUb = range_check(resolv_tuple_or_list(S,Ub)),
+ {'SizeConstraint',{NewLb,NewUb}}
+ end;
+check_constraint(S,{'SizeConstraint',{Lb,Ub}}) ->
+ case {resolv_value(S,Lb),resolv_value(S,Ub)} of
+ {FixV,FixV} ->
+ {'SizeConstraint',FixV};
+ {Low,High} when Low < High ->
+ {'SizeConstraint',{Low,High}};
+ Err ->
+ throw({error,{asn1,{illegal_size_constraint,Err}}})
+ end;
+check_constraint(S,{'SizeConstraint',Lb}) ->
+ {'SizeConstraint',resolv_value(S,Lb)};
+
+check_constraint(S,{'SingleValue', L}) when list(L) ->
+ F = fun(A) -> resolv_value(S,A) end,
+ {'SingleValue',lists:map(F,L)};
+
+check_constraint(S,{'SingleValue', V}) when integer(V) ->
+ Val = resolv_value(S,V),
+%% [{'SingleValue',Val},{'ValueRange',{Val,Val}}]; % Why adding value range?
+ {'SingleValue',Val};
+check_constraint(S,{'SingleValue', V}) ->
+ {'SingleValue',resolv_value(S,V)};
+
+check_constraint(S,{'ValueRange', {Lb, Ub}}) ->
+ {'ValueRange',{resolv_value(S,Lb),resolv_value(S,Ub)}};
+
+%%check_constraint(S,{'ContainedSubtype',Type}) ->
+%% #typedef{typespec=TSpec} =
+%% check_type(S,S#state.tname,get_referenced_type(S,Type#type.def)),
+%% [C] = TSpec#type.constraint,
+%% C;
+
+check_constraint(S,{valueset,Type}) ->
+ {valueset,check_type(S,S#state.tname,Type)};
+
+check_constraint(S,{simpletable,Type}) ->
+ OSName = (Type#type.def)#'Externaltypereference'.type,
+ C = match_parameters(Type#type.def,S#state.parameters),
+ case C of
+ #'Externaltypereference'{} ->
+ Type#type{def=check_externaltypereference(S,C)},
+ {simpletable,OSName};
+ _ ->
+ check_type(S,S#state.tname,Type),
+ {simpletable,OSName}
+ end;
+
+check_constraint(S,{componentrelation,{objectset,Opos,Objset},Id}) ->
+ %% Objset is an 'Externaltypereference' record, since Objset is
+ %% a DefinedObjectSet.
+ RealObjset = match_parameters(Objset,S#state.parameters),
+ Ext = check_externaltypereference(S,RealObjset),
+ {componentrelation,{objectset,Opos,Ext},Id};
+
+check_constraint(S,Type) when record(Type,type) ->
+ #type{def=Def} = check_type(S,S#state.tname,Type),
+ Def;
+
+check_constraint(S,C) when list(C) ->
+ lists:map(fun(X)->check_constraint(S,X) end,C);
+% else keep the constraint unchanged
+check_constraint(_S,Any) ->
+% io:format("Constraint = ~p~n",[Any]),
+ Any.
+
+%% constraint_merge/2
+%% Compute the intersection of the outermost level of the constraint list.
+%% See Dubuisson second paragraph and fotnote on page 285.
+%% If constraints with extension are included in combined constraints. The
+%% resulting combination will have the extension of the last constraint. Thus,
+%% there will be no extension if the last constraint is without extension.
+%% The rootset of all constraints are considered in the "outermoust
+%% intersection". See section 13.1.2 in Dubuisson.
+constraint_merge(_S,C=[H])when tuple(H) ->
+ C;
+constraint_merge(_S,[]) ->
+ [];
+constraint_merge(S,C) ->
+ %% skip all extension but the last
+ C1 = filter_extensions(C),
+ %% perform all internal level intersections, intersections first
+ %% since they have precedence over unions
+ C2 = lists:map(fun(X)when list(X)->constraint_intersection(S,X);
+ (X) -> X end,
+ C1),
+ %% perform all internal level unions
+ C3 = lists:map(fun(X)when list(X)->constraint_union(S,X);
+ (X) -> X end,
+ C2),
+
+ %% now get intersection of the outermost level
+ %% get the least common single value constraint
+ SVs = get_constraints(C3,'SingleValue'),
+ CombSV = intersection_of_sv(S,SVs),
+ %% get the least common value range constraint
+ VRs = get_constraints(C3,'ValueRange'),
+ CombVR = intersection_of_vr(S,VRs),
+ %% get the least common size constraint
+ SZs = get_constraints(C3,'SizeConstraint'),
+ CombSZ = intersection_of_size(S,SZs),
+ CminusSVs=ordsets:subtract(ordsets:from_list(C3),ordsets:from_list(SVs)),
+ % CminusSVsVRs = ordsets:subtract(ordsets:from_list(CminusSVs),
+% ordsets:from_list(VRs)),
+ RestC = ordsets:subtract(ordsets:from_list(CminusSVs),
+ ordsets:from_list(SZs)),
+ %% get the least common combined constraint. That is the union of each
+ %% deep costraint and merge of single value and value range constraints
+ combine_constraints(S,CombSV,CombVR,CombSZ++RestC).
+
+%% constraint_union(S,C) takes a list of constraints as input and
+%% merge them to a union. Unions are performed when two
+%% constraints is found with an atom union between.
+%% The list may be nested. Fix that later !!!
+constraint_union(_S,[]) ->
+ [];
+constraint_union(_S,C=[_E]) ->
+ C;
+constraint_union(S,C) when list(C) ->
+ case lists:member(union,C) of
+ true ->
+ constraint_union1(S,C,[]);
+ _ ->
+ C
+ end;
+% SV = get_constraints(C,'SingleValue'),
+% SV1 = constraint_union_sv(S,SV),
+% VR = get_constraints(C,'ValueRange'),
+% VR1 = constraint_union_vr(VR),
+% RestC = ordsets:filter(fun({'SingleValue',_})->false;
+% ({'ValueRange',_})->false;
+% (_) -> true end,ordsets:from_list(C)),
+% SV1++VR1++RestC;
+constraint_union(_S,C) ->
+ [C].
+
+constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) ->
+ AunionB = constraint_union_vr([A,B]),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) ->
+ AunionB = constraint_union_sv(S,[A,B]),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) ->
+ AunionB = union_sv_vr(S,A,B),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) ->
+ AunionB = union_sv_vr(S,B,A),
+ constraint_union1(S,Rest,AunionB++Acc);
+constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints
+ constraint_union1(S,Rest,Acc);
+constraint_union1(S,[A|Rest],Acc) ->
+ constraint_union1(S,Rest,[A|Acc]);
+constraint_union1(_S,[],Acc) ->
+ lists:reverse(Acc).
+
+constraint_union_sv(_S,SV) ->
+ Values=lists:map(fun({_,V})->V end,SV),
+ case ordsets:from_list(Values) of
+ [] -> [];
+ [N] -> [{'SingleValue',N}];
+ L -> [{'SingleValue',L}]
+ end.
+
+%% REMOVE????
+%%constraint_union(S,VR,'ValueRange') ->
+%% constraint_union_vr(VR).
+
+%% constraint_union_vr(VR)
+%% VR = [{'ValueRange',{Lb,Ub}},...]
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns if possible only one ValueRange tuple with a range that
+%% is a union of all ranges in VR.
+constraint_union_vr(VR) ->
+ %% Sort VR by Lb in first hand and by Ub in second hand
+ Fun=fun({_,{'MIN',_B1}},{_,{A2,_B2}}) when integer(A2)->true;
+ ({_,{A1,_B1}},{_,{'MAX',_B2}}) when integer(A1) -> true;
+ ({_,{A1,_B1}},{_,{A2,_B2}}) when integer(A1),integer(A2),A1<A2 -> true;
+ ({_,{A,B1}},{_,{A,B2}}) when B1=<B2->true;
+ (_,_)->false end,
+ constraint_union_vr(lists:usort(Fun,VR),[]).
+
+constraint_union_vr([],Acc) ->
+ lists:reverse(Acc);
+constraint_union_vr([C|Rest],[]) ->
+ constraint_union_vr(Rest,[C]);
+constraint_union_vr([{_,{Lb,Ub2}}|Rest],[{_,{Lb,_Ub1}}|Acc]) -> %Ub2 > Ub1
+ constraint_union_vr(Rest,[{'ValueRange',{Lb,Ub2}}|Acc]);
+constraint_union_vr([{_,{_,Ub}}|Rest],A=[{_,{_,Ub}}|_Acc]) ->
+ constraint_union_vr(Rest,A);
+constraint_union_vr([{_,{Lb2,Ub2}}|Rest],[{_,{Lb1,Ub1}}|Acc]) when Lb2=<Ub1,
+ Ub2>Ub1->
+ constraint_union_vr(Rest,[{'ValueRange',{Lb1,Ub2}}|Acc]);
+constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1->
+ constraint_union_vr(Rest,A);
+constraint_union_vr([VR|Rest],Acc) ->
+ constraint_union_vr(Rest,[VR|Acc]).
+
+union_sv_vr(_S,[],B) ->
+ [B];
+union_sv_vr(_S,A,[]) ->
+ [A];
+union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',VR={Lb,Ub}})
+ when integer(SV) ->
+ case is_int_in_vr(SV,C2) of
+ true -> [C2];
+ _ ->
+ case VR of
+ {'MIN',Ub} when SV==Ub+1 -> [{'ValueRange',{'MIN',SV}}];
+ {Lb,'MAX'} when SV==Lb-1 -> [{'ValueRange',{SV,'MAX'}}];
+ {Lb,Ub} when SV==Ub+1 -> [{'ValueRange',{Lb,SV}}];
+ {Lb,Ub} when SV==Lb-1 -> [{'ValueRange',{SV,Ub}}];
+ _ ->
+ [C1,C2]
+ end
+ end;
+union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',{_Lb,_Ub}})
+ when list(SV) ->
+ case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
+ [] -> [C2];
+ L ->
+ case expand_vr(L,C2) of
+ {[],C3} -> [C3];
+ {L,C2} -> [C1,C2];
+ {[Val],C3} -> [{'SingleValue',Val},C3];
+ {L2,C3} -> [{'SingleValue',L2},C3]
+ end
+ end.
+
+expand_vr(L,VR={_,{Lb,Ub}}) ->
+ case lower_Lb(L,Lb) of
+ false ->
+ case higher_Ub(L,Ub) of
+ false ->
+ {L,VR};
+ {L1,UbNew} ->
+ expand_vr(L1,{'ValueRange',{Lb,UbNew}})
+ end;
+ {L1,LbNew} ->
+ expand_vr(L1,{'ValueRange',{LbNew,Ub}})
+ end.
+
+lower_Lb(_,'MIN') ->
+ false;
+lower_Lb(L,Lb) ->
+ remove_val_from_list(Lb - 1,L).
+
+higher_Ub(_,'MAX') ->
+ false;
+higher_Ub(L,Ub) ->
+ remove_val_from_list(Ub + 1,L).
+
+remove_val_from_list(List,Val) ->
+ case lists:member(Val,List) of
+ true ->
+ {lists:delete(Val,List),Val};
+ false ->
+ false
+ end.
+
+%% get_constraints/2
+%% Arguments are a list of constraints, which has the format {key,value},
+%% and a constraint type
+%% Returns a list of constraints only of the requested type or the atom
+%% 'no' if no such constraints were found
+get_constraints(L=[{CType,_}],CType) ->
+ L;
+get_constraints(C,CType) ->
+ keysearch_allwithkey(CType,1,C).
+
+%% keysearch_allwithkey(Key,Ix,L)
+%% Types:
+%% Key = atom()
+%% Ix = integer()
+%% L = [TwoTuple]
+%% TwoTuple = [{atom(),term()}|...]
+%% Returns a List that contains all
+%% elements from L that has a key Key as element Ix
+keysearch_allwithkey(Key,Ix,L) ->
+ lists:filter(fun(X) when tuple(X) ->
+ case element(Ix,X) of
+ Key -> true;
+ _ -> false
+ end;
+ (_) -> false
+ end, L).
+
+
+%% filter_extensions(C)
+%% takes a list of constraints as input and
+%% returns a list with the intersection of all extension roots
+%% and only the extension of the last constraint kept if any
+%% extension in the last constraint
+filter_extensions([]) ->
+ [];
+filter_extensions(C=[_H]) ->
+ C;
+filter_extensions(C) when list(C) ->
+ filter_extensions(C,[]).
+
+filter_extensions([C],Acc) ->
+ lists:reverse([C|Acc]);
+filter_extensions([{C,_E},H2|T],Acc) when tuple(C) ->
+ filter_extensions([H2|T],[C|Acc]);
+filter_extensions([{'SizeConstraint',{A,_B}},H2|T],Acc)
+ when list(A);tuple(A) ->
+ filter_extensions([H2|T],[{'SizeConstraint',A}|Acc]);
+filter_extensions([H1,H2|T],Acc) ->
+ filter_extensions([H2|T],[H1|Acc]).
+
+%% constraint_intersection(S,C) takes a list of constraints as input and
+%% performs intersections. Intersecions are performed when an
+%% atom intersection is found between two constraints.
+%% The list may be nested. Fix that later !!!
+constraint_intersection(_S,[]) ->
+ [];
+constraint_intersection(_S,C=[_E]) ->
+ C;
+constraint_intersection(S,C) when list(C) ->
+% io:format("constraint_intersection: ~p~n",[C]),
+ case lists:member(intersection,C) of
+ true ->
+ constraint_intersection1(S,C,[]);
+ _ ->
+ C
+ end;
+constraint_intersection(_S,C) ->
+ [C].
+
+constraint_intersection1(S,[A,intersection,B|Rest],Acc) ->
+ AisecB = c_intersect(S,A,B),
+ constraint_intersection1(S,Rest,AisecB++Acc);
+constraint_intersection1(S,[A|Rest],Acc) ->
+ constraint_intersection1(S,Rest,[A|Acc]);
+constraint_intersection1(_,[],Acc) ->
+ lists:reverse(Acc).
+
+c_intersect(S,C1={'SingleValue',_},C2={'SingleValue',_}) ->
+ intersection_of_sv(S,[C1,C2]);
+c_intersect(S,C1={'ValueRange',_},C2={'ValueRange',_}) ->
+ intersection_of_vr(S,[C1,C2]);
+c_intersect(S,C1={'ValueRange',_},C2={'SingleValue',_}) ->
+ intersection_sv_vr(S,[C2],[C1]);
+c_intersect(S,C1={'SingleValue',_},C2={'ValueRange',_}) ->
+ intersection_sv_vr(S,[C1],[C2]);
+c_intersect(_S,C1,C2) ->
+ [C1,C2].
+
+%% combine_constraints(S,SV,VR,CComb)
+%% Types:
+%% S = record(state,S)
+%% SV = [] | [SVC]
+%% VR = [] | [VRC]
+%% CComb = [] | [Lists]
+%% SVC = {'SingleValue',integer()} | {'SingleValue',[integer(),...]}
+%% VRC = {'ValueRange',{Lb,Ub}}
+%% Lists = List of lists containing any constraint combination
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns a combination of the least common constraint among SV,VR and all
+%% elements in CComb
+combine_constraints(_S,[],VR,CComb) ->
+ VR ++ CComb;
+% combine_combined_cnstr(S,VR,CComb);
+combine_constraints(_S,SV,[],CComb) ->
+ SV ++ CComb;
+% combine_combined_cnstr(S,SV,CComb);
+combine_constraints(S,SV,VR,CComb) ->
+ C=intersection_sv_vr(S,SV,VR),
+ C ++ CComb.
+% combine_combined_cnstr(S,C,CComb).
+
+intersection_sv_vr(_,[],_VR) ->
+ [];
+intersection_sv_vr(_,_SV,[]) ->
+ [];
+intersection_sv_vr(_S,[C1={'SingleValue',SV}],[C2={'ValueRange',{_Lb,_Ub}}])
+ when integer(SV) ->
+ case is_int_in_vr(SV,C2) of
+ true -> [C1];
+ _ -> %%error({type,{"asn1 illegal constraint",C1,C2},S})
+ throw({error,{"asn1 illegal constraint",C1,C2}})
+ end;
+intersection_sv_vr(_S,[C1={'SingleValue',SV}],[C2])
+ when list(SV) ->
+ case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of
+ [] ->
+ %%error({type,{"asn1 illegal constraint",C1,C2},S});
+ throw({error,{"asn1 illegal constraint",C1,C2}});
+ [V] -> [{'SingleValue',V}];
+ L -> [{'SingleValue',L}]
+ end.
+
+
+
+intersection_of_size(_,[]) ->
+ [];
+intersection_of_size(_,C=[_SZ]) ->
+ C;
+intersection_of_size(S,[SZ,SZ|Rest]) ->
+ intersection_of_size(S,[SZ|Rest]);
+intersection_of_size(S,C=[C1={_,Int},{_,Range}|Rest])
+ when integer(Int),tuple(Range) ->
+ case Range of
+ {Lb,Ub} when Int >= Lb,
+ Int =< Ub ->
+ intersection_of_size(S,[C1|Rest]);
+ _ ->
+ throw({error,{asn1,{illegal_size_constraint,C}}})
+ end;
+intersection_of_size(S,[C1={_,Range},C2={_,Int}|Rest])
+ when integer(Int),tuple(Range) ->
+ intersection_of_size(S,[C2,C1|Rest]);
+intersection_of_size(S,[{_,{Lb1,Ub1}},{_,{Lb2,Ub2}}|Rest]) ->
+ Lb=greatest_LB(ordsets:from_list([Lb1,Lb2])),
+ Ub=smallest_UB(ordsets:from_list([Ub1,Ub2])),
+ intersection_of_size(S,[{'SizeConstraint',{Lb,Ub}}|Rest]);
+intersection_of_size(_,SZ) ->
+ throw({error,{asn1,{illegal_size_constraint,SZ}}}).
+
+intersection_of_vr(_,[]) ->
+ [];
+intersection_of_vr(_,VR=[_C]) ->
+ VR;
+intersection_of_vr(S,[{_,{Lb1,Ub1}},{_,{Lb2,Ub2}}|Rest]) ->
+ Lb=greatest_LB(ordsets:from_list([Lb1,Lb2])),
+ Ub=smallest_UB(ordsets:from_list([Ub1,Ub2])),
+ intersection_of_vr(S,[{'ValueRange',{Lb,Ub}}|Rest]);
+intersection_of_vr(_S,VR) ->
+ %%error({type,{asn1,{illegal_value_range_constraint,VR}},S});
+ throw({error,{asn1,{illegal_value_range_constraint,VR}}}).
+
+intersection_of_sv(_,[]) ->
+ [];
+intersection_of_sv(_,SV=[_C]) ->
+ SV;
+intersection_of_sv(S,[SV,SV|Rest]) ->
+ intersection_of_sv(S,[SV|Rest]);
+intersection_of_sv(S,[{_,Int},{_,SV}|Rest]) when integer(Int),
+ list(SV) ->
+ SV2=intersection_of_sv1(S,Int,SV),
+ intersection_of_sv(S,[SV2|Rest]);
+intersection_of_sv(S,[{_,SV},{_,Int}|Rest]) when integer(Int),
+ list(SV) ->
+ SV2=intersection_of_sv1(S,Int,SV),
+ intersection_of_sv(S,[SV2|Rest]);
+intersection_of_sv(S,[{_,SV1},{_,SV2}|Rest]) when list(SV1),
+ list(SV2) ->
+ SV3=common_set(SV1,SV2),
+ intersection_of_sv(S,[SV3|Rest]);
+intersection_of_sv(_S,SV) ->
+ %%error({type,{asn1,{illegal_single_value_constraint,SV}},S}).
+ throw({error,{asn1,{illegal_single_value_constraint,SV}}}).
+
+intersection_of_sv1(_S,Int,SV) when integer(Int),list(SV) ->
+ case lists:member(Int,SV) of
+ true -> {'SingleValue',Int};
+ _ ->
+ %%error({type,{asn1,{illegal_single_value_constraint,Int,SV}},S})
+ throw({error,{asn1,{illegal_single_value_constraint,Int,SV}}})
+ end;
+intersection_of_sv1(_S,SV1,SV2) ->
+ %%error({type,{asn1,{illegal_single_value_constraint,SV1,SV2}},S}).
+ throw({error,{asn1,{illegal_single_value_constraint,SV1,SV2}}}).
+
+greatest_LB([H]) ->
+ H;
+greatest_LB(L) ->
+ greatest_LB1(lists:reverse(L)).
+greatest_LB1(['MIN',H2|_T])->
+ H2;
+greatest_LB1([H|_T]) ->
+ H.
+smallest_UB(L) ->
+ hd(L).
+
+common_set(SV1,SV2) ->
+ lists:filter(fun(X)->lists:member(X,SV1) end,SV2).
+
+is_int_in_vr(Int,{_,{'MIN','MAX'}}) when integer(Int) ->
+ true;
+is_int_in_vr(Int,{_,{'MIN',Ub}}) when integer(Int),Int =< Ub ->
+ true;
+is_int_in_vr(Int,{_,{Lb,'MAX'}}) when integer(Int),Int >= Lb ->
+ true;
+is_int_in_vr(Int,{_,{Lb,Ub}}) when integer(Int),Int >= Lb,Int =< Ub ->
+ true;
+is_int_in_vr(_,_) ->
+ false.
+
+
+
+check_imported(_S,Imodule,Name) ->
+ case asn1_db:dbget(Imodule,'MODULE') of
+ undefined ->
+ io:format("~s.asn1db not found~n",[Imodule]),
+ io:format("Type ~s imported from non existing module ~s~n",[Name,Imodule]);
+ Im when record(Im,module) ->
+ case is_exported(Im,Name) of
+ false ->
+ io:format("Imported type ~s not exported from module ~s~n",[Name,Imodule]);
+ _ ->
+ ok
+ end
+ end,
+ ok.
+
+is_exported(Module,Name) when record(Module,module) ->
+ {exports,Exports} = Module#module.exports,
+ case Exports of
+ all ->
+ true;
+ [] ->
+ false;
+ L when list(L) ->
+ case lists:keysearch(Name,#'Externaltypereference'.type,Exports) of
+ false -> false;
+ _ -> true
+ end
+ end.
+
+
+
+check_externaltypereference(S,Etref=#'Externaltypereference'{module=Emod})->
+ Currmod = S#state.mname,
+ MergedMods = S#state.inputmodules,
+ case Emod of
+ Currmod ->
+ %% reference to current module or to imported reference
+ check_reference(S,Etref);
+ _ ->
+ %% io:format("Type ~s IMPORTED FROM ~s~n",[Etype,Emod]),
+ case lists:member(Emod,MergedMods) of
+ true ->
+ check_reference(S,Etref);
+ false ->
+ Etref
+ end
+ end.
+
+check_reference(S,#'Externaltypereference'{pos=Pos,module=Emod,type=Name}) ->
+ ModName = S#state.mname,
+ case asn1_db:dbget(ModName,Name) of
+ undefined ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ check_imported(S,Imodule,Name),
+ #'Externaltypereference'{module=Imodule,type=Name};
+ _ ->
+ %may be a renamed type in multi file compiling!
+ {_,T}=renamed_reference(S,Name,Emod),
+ NewName = asn1ct:get_name_of_def(T),
+ NewPos = asn1ct:get_pos_of_def(T),
+ #'Externaltypereference'{pos=NewPos,
+ module=ModName,
+ type=NewName}
+ end;
+ _ ->
+ %% cannot do check_type here due to recursive definitions, like
+ %% S ::= SEQUENCE {a INTEGER, b S}. This implies that references
+ %% that appear before the definition will be an
+ %% Externaltypereference in the abstract syntax tree
+ #'Externaltypereference'{pos=Pos,module=ModName,type=Name}
+ end.
+
+
+name2Extref(_Mod,Name) when record(Name,'Externaltypereference') ->
+ Name;
+name2Extref(Mod,Name) ->
+ #'Externaltypereference'{module=Mod,type=Name}.
+
+get_referenced_type(S,Ext) when record(Ext,'Externaltypereference') ->
+ case match_parameters(Ext, S#state.parameters) of
+ Ext ->
+ #'Externaltypereference'{pos=Pos,module=Emod,type=Etype} = Ext,
+ case S#state.mname of
+ Emod -> % a local reference in this module
+ get_referenced1(S,Emod,Etype,Pos);
+ _ ->% always when multi file compiling
+ case lists:member(Emod,S#state.inputmodules) of
+ true ->
+ get_referenced1(S,Emod,Etype,Pos);
+ false ->
+ get_referenced(S,Emod,Etype,Pos)
+ end
+ end;
+ Other ->
+ {undefined,Other}
+ end;
+get_referenced_type(S=#state{mname=Emod},
+ ERef=#'Externalvaluereference'{pos=P,module=Emod,
+ value=Eval}) ->
+ case match_parameters(ERef,S#state.parameters) of
+ ERef ->
+ get_referenced1(S,Emod,Eval,P);
+ OtherERef when record(OtherERef,'Externalvaluereference') ->
+ get_referenced_type(S,OtherERef);
+ Value ->
+ {Emod,Value}
+ end;
+get_referenced_type(S,ERef=#'Externalvaluereference'{pos=Pos,module=Emod,
+ value=Eval}) ->
+ case match_parameters(ERef,S#state.parameters) of
+ ERef ->
+ case lists:member(Emod,S#state.inputmodules) of
+ true ->
+ get_referenced1(S,Emod,Eval,Pos);
+ false ->
+ get_referenced(S,Emod,Eval,Pos)
+ end;
+ OtherERef ->
+ get_referenced_type(S,OtherERef)
+ end;
+get_referenced_type(S,#identifier{val=Name,pos=Pos}) ->
+ get_referenced1(S,undefined,Name,Pos);
+get_referenced_type(_S,Type) ->
+ {undefined,Type}.
+
+%% get_referenced/3
+%% The referenced entity Ename may in case of an imported parameterized
+%% type reference imported entities in the other module, which implies that
+%% asn1_db:dbget will fail even though the referenced entity exists. Thus
+%% Emod may be the module that imports the entity Ename and not holds the
+%% data about Ename.
+get_referenced(S,Emod,Ename,Pos) ->
+ case asn1_db:dbget(Emod,Ename) of
+ undefined ->
+ %% May be an imported entity in module Emod
+% throw({error,{asn1,{undefined_type_or_value,{Emod,Ename}}}});
+ NewS = S#state{module=asn1_db:dbget(Emod,'MODULE')},
+ get_imported(NewS,Ename,Emod,Pos);
+ T when record(T,typedef) ->
+ Spec = T#typedef.typespec,
+ case Spec#type.def of
+ Tref when record(Tref,typereference) ->
+ Def = #'Externaltypereference'{module=Emod,
+ type=Tref#typereference.val,
+ pos=Tref#typereference.pos},
+
+
+ {Emod,T#typedef{typespec=Spec#type{def=Def}}};
+ _ ->
+ {Emod,T} % should add check that T is exported here
+ end;
+ V -> {Emod,V}
+ end.
+
+get_referenced1(S,ModuleName,Name,Pos) ->
+ case asn1_db:dbget(S#state.mname,Name) of
+ undefined ->
+ %% ModuleName may be other than S#state.mname when
+ %% multi file compiling is used.
+ get_imported(S,Name,ModuleName,Pos);
+ T ->
+ {S#state.mname,T}
+ end.
+
+get_imported(S,Name,Module,Pos) ->
+ case imported(S,Name) of
+ {ok,Imodule} ->
+ case asn1_db:dbget(Imodule,'MODULE') of
+ undefined ->
+ throw({error,{asn1,{module_not_found,Imodule}}});
+ Im when record(Im,module) ->
+ case is_exported(Im,Name) of
+ false ->
+ throw({error,
+ {asn1,{not_exported,{Im,Name}}}});
+ _ ->
+ get_referenced_type(S,
+ #'Externaltypereference'
+ {module=Imodule,
+ type=Name,pos=Pos})
+ end
+ end;
+ _ ->
+ renamed_reference(S,Name,Module)
+ end.
+
+renamed_reference(S,Name,Module) ->
+ %% first check if there is a renamed type in this module
+ %% second check if any type was imported with this name
+ case ets:info(renamed_defs) of
+ undefined -> throw({error,{asn1,{undefined_type,Name}}});
+ _ ->
+ case ets:match(renamed_defs,{'$1',Name,Module}) of
+ [] ->
+ case ets:info(original_imports) of
+ undefined ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ _ ->
+ case ets:match(original_imports,{Module,'$1'}) of
+ [] ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ [[ImportsList]] ->
+ case get_importmoduleoftype(ImportsList,Name) of
+ undefined ->
+ throw({error,{asn1,{undefined_type,Name}}});
+ NextMod ->
+ renamed_reference(S,Name,NextMod)
+ end
+ end
+ end;
+ [[NewTypeName]] ->
+ get_referenced1(S,Module,NewTypeName,undefined)
+ end
+ end.
+
+get_importmoduleoftype([I|Is],Name) ->
+ Index = #'Externaltypereference'.type,
+ case lists:keysearch(Name,Index,I#'SymbolsFromModule'.symbols) of
+ {value,_Ref} ->
+ (I#'SymbolsFromModule'.module)#'Externaltypereference'.type;
+ _ ->
+ get_importmoduleoftype(Is,Name)
+ end;
+get_importmoduleoftype([],_) ->
+ undefined.
+
+
+match_parameters(Name,[]) ->
+ Name;
+
+match_parameters(#'Externaltypereference'{type=Name},[{#'Externaltypereference'{type=Name},NewName}|_T]) ->
+ NewName;
+match_parameters(#'Externaltypereference'{type=Name},[{{_,#'Externaltypereference'{type=Name}},NewName}|_T]) ->
+ NewName;
+% match_parameters(#'Externaltypereference'{type=Name},[{#typereference{val=Name},NewName}|T]) ->
+% NewName;
+% match_parameters(#'Externaltypereference'{type=Name},[{{_,#typereference{val=Name}},NewName}|T]) ->
+% NewName;
+%match_parameters(#typereference{val=Name},[{#typereference{val=Name},NewName}|T]) ->
+% NewName;
+match_parameters(#'Externalvaluereference'{value=Name},[{#'Externalvaluereference'{value=Name},NewName}|_T]) ->
+ NewName;
+match_parameters(#'Externalvaluereference'{value=Name},[{{_,#'Externalvaluereference'{value=Name}},NewName}|_T]) ->
+ NewName;
+% match_parameters(#identifier{val=Name},[{#identifier{val=Name},NewName}|T]) ->
+% NewName;
+% match_parameters(#identifier{val=Name},[{{_,#identifier{val=Name}},NewName}|T]) ->
+% NewName;
+match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},{valueset,#type{def=NewName}}}|_T]) ->
+ NewName;
+match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+ [{{_,#'Externaltypereference'{type=Name}},NewName}|_T]) ->
+ NewName;
+% match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+% [{{_,#typereference{val=Name}},{valueset,#type{def=NewName}}}|T]) ->
+% NewName;
+% match_parameters({valueset,#type{def=#'Externaltypereference'{type=Name}}},
+% [{{_,#typereference{val=Name}},NewName}|T]) ->
+% NewName;
+
+match_parameters(Name, [_H|T]) ->
+ %%io:format("match_parameters(~p,~p)~n",[Name,[H|T]]),
+ match_parameters(Name,T).
+
+imported(S,Name) ->
+ {imports,Ilist} = (S#state.module)#module.imports,
+ imported1(Name,Ilist).
+
+imported1(Name,
+ [#'SymbolsFromModule'{symbols=Symlist,
+ module=#'Externaltypereference'{type=ModuleName}}|T]) ->
+ case lists:keysearch(Name,#'Externaltypereference'.type,Symlist) of
+ {value,_V} ->
+ {ok,ModuleName};
+ _ ->
+ imported1(Name,T)
+ end;
+imported1(_Name,[]) ->
+ false.
+
+
+check_integer(_S,[],_C) ->
+ ok;
+check_integer(S,NamedNumberList,_C) ->
+ case check_unique(NamedNumberList,2) of
+ [] ->
+ check_int(S,NamedNumberList,[]);
+ L when list(L) ->
+ error({type,{duplicates,L},S}),
+ unchanged
+
+ end.
+
+check_int(S,[{'NamedNumber',Id,Num}|T],Acc) when integer(Num) ->
+ check_int(S,T,[{Id,Num}|Acc]);
+check_int(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
+ Val = dbget_ex(S,S#state.mname,Name),
+ check_int(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
+check_int(_S,[],Acc) ->
+ lists:keysort(2,Acc).
+
+
+
+check_bitstring(_S,[],_Constr) ->
+ [];
+check_bitstring(S,NamedNumberList,_Constr) ->
+ case check_unique(NamedNumberList,2) of
+ [] ->
+ check_bitstr(S,NamedNumberList,[]);
+ L when list(L) ->
+ error({type,{duplicates,L},S}),
+ unchanged
+ end.
+
+check_bitstr(S,[{'NamedNumber',Id,Num}|T],Acc)when integer(Num) ->
+ check_bitstr(S,T,[{Id,Num}|Acc]);
+check_bitstr(S,[{'NamedNumber',Id,Name}|T],Acc) when atom(Name) ->
+%%check_bitstr(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc) ->
+%% io:format("asn1ct_check:check_bitstr/3 hej hop ~w~n",[Name]),
+ Val = dbget_ex(S,S#state.mname,Name),
+%% io:format("asn1ct_check:check_bitstr/3: ~w~n",[Val]),
+ check_bitstr(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc);
+check_bitstr(S,[],Acc) ->
+ case check_unique(Acc,2) of
+ [] ->
+ lists:keysort(2,Acc);
+ L when list(L) ->
+ error({type,{duplicate_values,L},S}),
+ unchanged
+ end.
+
+%%check_bitstring(S,NamedNumberList,Constr) ->
+%% NamedNumberList.
+
+%% Check INSTANCE OF
+%% check that DefinedObjectClass is of TYPE-IDENTIFIER class
+%% If Constraint is empty make it the general INSTANCE OF type
+%% If Constraint is not empty make an inlined type
+%% convert INSTANCE OF to the associated type
+check_instance_of(S,DefinedObjectClass,Constraint) ->
+ check_type_identifier(S,DefinedObjectClass),
+ iof_associated_type(S,Constraint).
+
+
+check_type_identifier(_S,'TYPE-IDENTIFIER') ->
+ ok;
+check_type_identifier(S,Eref=#'Externaltypereference'{}) ->
+ case get_referenced_type(S,Eref) of
+ {_,#classdef{name='TYPE-IDENTIFIER'}} -> ok;
+ {_,TD=#typedef{typespec=#type{def=#'Externaltypereference'{}}}} ->
+ check_type_identifier(S,(TD#typedef.typespec)#type.def);
+ _ ->
+ error({type,{"object set in type INSTANCE OF "
+ "not of class TYPE-IDENTIFIER",Eref},S})
+ end.
+
+iof_associated_type(S,[]) ->
+ %% in this case encode/decode functions for INSTANCE OF must be
+ %% generated
+ case get(instance_of) of
+ undefined ->
+ AssociateSeq = iof_associated_type1(S,[]),
+ Tag =
+ case S#state.erule of
+ ber_bin_v2 ->
+ [?TAG_CONSTRUCTED(?N_INSTANCE_OF)];
+ _ -> []
+ end,
+ TypeDef=#typedef{checked=true,
+ name='INSTANCE OF',
+ typespec=#type{tag=Tag,
+ def=AssociateSeq}},
+ asn1_db:dbput(S#state.mname,'INSTANCE OF',TypeDef),
+ put(instance_of,generate);
+ _ ->
+ ok
+ end,
+ #'Externaltypereference'{module=S#state.mname,type='INSTANCE OF'};
+iof_associated_type(S,C) ->
+ iof_associated_type1(S,C).
+
+iof_associated_type1(S,C) ->
+ {TableCInf,Comp1Cnstr,Comp2Cnstr,Comp2tablecinf}=
+ instance_of_constraints(S,C),
+
+ ModuleName = S#state.mname,
+ Typefield_type=
+ case C of
+ [] -> 'ASN1_OPEN_TYPE';
+ _ -> {typefield,'Type'}
+ end,
+ {ObjIdTag,C1TypeTag}=
+ case S#state.erule of
+ ber_bin_v2 ->
+ {[{'UNIVERSAL',8}],
+ [#tag{class='UNIVERSAL',
+ number=6,
+ type='IMPLICIT',
+ form=0}]};
+ _ -> {[{'UNIVERSAL','INTEGER'}],[]}
+ end,
+ TypeIdentifierRef=#'Externaltypereference'{module=ModuleName,
+ type='TYPE-IDENTIFIER'},
+ ObjectIdentifier =
+ #'ObjectClassFieldType'{classname=TypeIdentifierRef,
+ class=[],
+ fieldname={id,[]},
+ type={fixedtypevaluefield,id,
+ #type{def='OBJECT IDENTIFIER'}}},
+ Typefield =
+ #'ObjectClassFieldType'{classname=TypeIdentifierRef,
+ class=[],
+ fieldname={'Type',[]},
+ type=Typefield_type},
+ IOFComponents =
+ [#'ComponentType'{name='type-id',
+ typespec=#type{tag=C1TypeTag,
+ def=ObjectIdentifier,
+ constraint=Comp1Cnstr},
+ prop=mandatory,
+ tags=ObjIdTag},
+ #'ComponentType'{name=value,
+ typespec=#type{tag=[#tag{class='CONTEXT',
+ number=0,
+ type='EXPLICIT',
+ form=32}],
+ def=Typefield,
+ constraint=Comp2Cnstr,
+ tablecinf=Comp2tablecinf},
+ prop=mandatory,
+ tags=[{'CONTEXT',0}]}],
+ #'SEQUENCE'{tablecinf=TableCInf,
+ components=IOFComponents}.
+
+
+%% returns the leading attribute, the constraint of the components and
+%% the tablecinf value for the second component.
+instance_of_constraints(_,[]) ->
+ {false,[],[],[]};
+instance_of_constraints(S,#constraint{c={simpletable,Type}}) ->
+ #type{def=#'Externaltypereference'{type=Name}} = Type,
+ ModuleName = S#state.mname,
+ ObjectSetRef=#'Externaltypereference'{module=ModuleName,
+ type=Name},
+ CRel=[{componentrelation,{objectset,
+ undefined, %% pos
+ ObjectSetRef},
+ [{innermost,
+ [#'Externalvaluereference'{module=ModuleName,
+ value=type}]}]}],
+ TableCInf=#simpletableattributes{objectsetname=Name,
+ c_name='type-id',
+ c_index=1,
+ usedclassfield=id,
+ uniqueclassfield=id,
+ valueindex=[]},
+ {TableCInf,[{simpletable,Name}],CRel,[{objfun,ObjectSetRef}]}.
+
+%% Check ENUMERATED
+%% ****************************************
+%% Check that all values are unique
+%% assign values to un-numbered identifiers
+%% check that the constraints are allowed and correct
+%% put the updated info back into database
+check_enumerated(_S,[{Name,Number}|Rest],_Constr) when atom(Name), integer(Number)->
+ %% already checked , just return the same list
+ [{Name,Number}|Rest];
+check_enumerated(S,NamedNumberList,_Constr) ->
+ check_enum(S,NamedNumberList,[],[]).
+
+%% identifiers are put in Acc2
+%% returns either [{Name,Number}] or {[{Name,Number}],[{ExtName,ExtNumber}]}
+%% the latter is returned if the ENUMERATION contains EXTENSIONMARK
+check_enum(S,[{'NamedNumber',Id,Num}|T],Acc1,Acc2) when integer(Num) ->
+ check_enum(S,T,[{Id,Num}|Acc1],Acc2);
+check_enum(S,[{'NamedNumber',Id,{identifier,_,Name}}|T],Acc1,Acc2) ->
+ Val = dbget_ex(S,S#state.mname,Name),
+ check_enum(S,[{'NamedNumber',Id,Val#valuedef.value}|T],Acc1,Acc2);
+check_enum(S,['EXTENSIONMARK'|T],Acc1,Acc2) ->
+ NewAcc2 = lists:keysort(2,Acc1),
+ NewList = enum_number(lists:reverse(Acc2),NewAcc2,0,[]),
+ { NewList, check_enum(S,T,[],[])};
+check_enum(S,[Id|T],Acc1,Acc2) when atom(Id) ->
+ check_enum(S,T,Acc1,[Id|Acc2]);
+check_enum(_S,[],Acc1,Acc2) ->
+ NewAcc2 = lists:keysort(2,Acc1),
+ enum_number(lists:reverse(Acc2),NewAcc2,0,[]).
+
+
+% assign numbers to identifiers , numbers from 0 ... but must not
+% be the same as already assigned to NamedNumbers
+enum_number([H|T],[{Id,Num}|T2],Cnt,Acc) when Num > Cnt ->
+ enum_number(T,[{Id,Num}|T2],Cnt+1,[{H,Cnt}|Acc]);
+enum_number([H|T],[{Id,Num}|T2],Cnt,Acc) when Num < Cnt -> % negative Num
+ enum_number(T,T2,Cnt+1,[{H,Cnt},{Id,Num}|Acc]);
+enum_number([],L2,_Cnt,Acc) ->
+ lists:concat([lists:reverse(Acc),L2]);
+enum_number(L,[{Id,Num}|T2],Cnt,Acc) -> % Num == Cnt
+ enum_number(L,T2,Cnt+1,[{Id,Num}|Acc]);
+enum_number([H|T],[],Cnt,Acc) ->
+ enum_number(T,[],Cnt+1,[{H,Cnt}|Acc]).
+
+
+check_boolean(_S,_Constr) ->
+ ok.
+
+check_octetstring(_S,_Constr) ->
+ ok.
+
+% check all aspects of a SEQUENCE
+% - that all component names are unique
+% - that all TAGS are ok (when TAG default is applied)
+% - that each component is of a valid type
+% - that the extension marks are valid
+
+check_sequence(S,Type,Comps) ->
+ Components = expand_components(S,Comps),
+ case check_unique([C||C <- Components ,record(C,'ComponentType')]
+ ,#'ComponentType'.name) of
+ [] ->
+ %% sort_canonical(Components),
+ Components2 = maybe_automatic_tags(S,Components),
+ %% check the table constraints from here. The outermost type
+ %% is Type, the innermost is Comps (the list of components)
+ NewComps =
+ case check_each_component(S,Type,Components2) of
+ NewComponents when list(NewComponents) ->
+ check_unique_sequence_tags(S,NewComponents),
+ NewComponents;
+ Ret = {NewComponents,NewEcomps} ->
+ TagComps = NewComponents ++
+ [Comp#'ComponentType'{prop='OPTIONAL'}|| Comp <- NewEcomps],
+ %% extension components are like optionals when it comes to tagging
+ check_unique_sequence_tags(S,TagComps),
+ Ret
+ end,
+ %% CRelInf is the "leading attribute" information
+ %% necessary for code generating of the look up in the
+ %% object set table,
+ %% i.e. getenc_ObjectSet/getdec_ObjectSet.
+ %% {objfun,ERef} tuple added in NewComps2 in tablecinf
+ %% field in type record of component relation constrained
+ %% type
+% io:format("NewComps: ~p~n",[NewComps]),
+ {CRelInf,NewComps2} = componentrelation_leadingattr(S,NewComps),
+% io:format("CRelInf: ~p~n",[CRelInf]),
+% io:format("NewComps2: ~p~n",[NewComps2]),
+ %% CompListWithTblInf has got a lot unecessary info about
+ %% the involved class removed, as the class of the object
+ %% set.
+ CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps2),
+% io:format("CompListWithTblInf: ~p~n",[CompListWithTblInf]),
+ {CRelInf,CompListWithTblInf};
+ Dupl ->
+ throw({error,{asn1,{duplicate_components,Dupl}}})
+ end.
+
+expand_components(S, [{'COMPONENTS OF',Type}|T]) ->
+ CompList =
+ case get_referenced_type(S,Type#type.def) of
+ {_,#typedef{typespec=#type{def=Seq}}} when record(Seq,'SEQUENCE') ->
+ case Seq#'SEQUENCE'.components of
+ {Root,_Ext} -> Root;
+ Root -> Root
+ end;
+ Err -> throw({error,{asn1,{illegal_COMPONENTS_OF,Err}}})
+ end,
+ expand_components(S,CompList) ++ expand_components(S,T);
+expand_components(S,[H|T]) ->
+ [H|expand_components(S,T)];
+expand_components(_,[]) ->
+ [].
+
+check_unique_sequence_tags(S,[#'ComponentType'{prop=mandatory}|Rest]) ->
+ check_unique_sequence_tags(S,Rest);
+check_unique_sequence_tags(S,[C|Rest]) when record(C,'ComponentType') ->
+ check_unique_sequence_tags1(S,Rest,[C]);% optional or default
+check_unique_sequence_tags(S,[_ExtensionMarker|Rest]) ->
+ check_unique_sequence_tags(S,Rest);
+check_unique_sequence_tags(_S,[]) ->
+ true.
+
+check_unique_sequence_tags1(S,[C|Rest],Acc) when record(C,'ComponentType') ->
+ case C#'ComponentType'.prop of
+ mandatory ->
+ check_unique_tags(S,lists:reverse([C|Acc])),
+ check_unique_sequence_tags(S,Rest);
+ _ ->
+ check_unique_sequence_tags1(S,Rest,[C|Acc]) % default or optional
+ end;
+check_unique_sequence_tags1(S,[H|Rest],Acc) ->
+ check_unique_sequence_tags1(S,Rest,[H|Acc]);
+check_unique_sequence_tags1(S,[],Acc) ->
+ check_unique_tags(S,lists:reverse(Acc)).
+
+check_sequenceof(S,Type,Component) when record(Component,type) ->
+ check_type(S,Type,Component).
+
+check_set(S,Type,Components) ->
+ {TableCInf,NewComponents} = check_sequence(S,Type,Components),
+ case lists:member(der,S#state.options) of
+ true when S#state.erule == ber;
+ S#state.erule == ber_bin ->
+ {Sorted,SortedComponents} =
+ sort_components(S#state.tname,
+ (S#state.module)#module.tagdefault,
+ NewComponents),
+ {Sorted,TableCInf,SortedComponents};
+ _ ->
+ {false,TableCInf,NewComponents}
+ end.
+
+sort_components(_TypeName,'AUTOMATIC',Components) ->
+ {true,Components};
+sort_components(TypeName,_TagDefault,Components) ->
+ case untagged_choice(Components) of
+ false ->
+ {true,sort_components1(TypeName,Components,[],[],[],[])};
+ true ->
+ {dynamic,Components} % sort in run-time
+ end.
+
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'UNIVERSAL',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,[C|UnivAcc],ApplAcc,ContAcc,PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'APPLICATION',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,[C|ApplAcc],ContAcc,PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'CONTEXT',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,ApplAcc,[C|ContAcc],PrivAcc);
+sort_components1(TypeName,[C=#'ComponentType'{tags=[{'PRIVATE',_}|_R]}|Cs],
+ UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ sort_components1(TypeName,Cs,UnivAcc,ApplAcc,ContAcc,[C|PrivAcc]);
+sort_components1(TypeName,[],UnivAcc,ApplAcc,ContAcc,PrivAcc) ->
+ I = #'ComponentType'.tags,
+ ascending_order_check(TypeName,sort_universal_type(UnivAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,ApplAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,ContAcc)) ++
+ ascending_order_check(TypeName,lists:keysort(I,PrivAcc)).
+
+ascending_order_check(TypeName,Components) ->
+ ascending_order_check1(TypeName,Components),
+ Components.
+
+ascending_order_check1(TypeName,
+ [C1 = #'ComponentType'{tags=[{_,T}|_]},
+ C2 = #'ComponentType'{tags=[{_,T}|_]}|Rest]) ->
+ io:format("WARNING: Indistinct tag ~p in SET ~p, components ~p and ~p~n",
+ [T,TypeName,C1#'ComponentType'.name,C2#'ComponentType'.name]),
+ ascending_order_check1(TypeName,[C2|Rest]);
+ascending_order_check1(TypeName,
+ [C1 = #'ComponentType'{tags=[{'UNIVERSAL',T1}|_]},
+ C2 = #'ComponentType'{tags=[{'UNIVERSAL',T2}|_]}|Rest]) ->
+ case (asn1ct_gen_ber:decode_type(T1) == asn1ct_gen_ber:decode_type(T2)) of
+ true ->
+ io:format("WARNING: Indistinct tags ~p and ~p in"
+ " SET ~p, components ~p and ~p~n",
+ [T1,T2,TypeName,C1#'ComponentType'.name,
+ C2#'ComponentType'.name]),
+ ascending_order_check1(TypeName,[C2|Rest]);
+ _ ->
+ ascending_order_check1(TypeName,[C2|Rest])
+ end;
+ascending_order_check1(N,[_|Rest]) ->
+ ascending_order_check1(N,Rest);
+ascending_order_check1(_,[_]) ->
+ ok;
+ascending_order_check1(_,[]) ->
+ ok.
+
+sort_universal_type(Components) ->
+ List = lists:map(fun(C) ->
+ #'ComponentType'{tags=[{_,T}|_]} = C,
+ {asn1ct_gen_ber:decode_type(T),C}
+ end,
+ Components),
+ SortedList = lists:keysort(1,List),
+ lists:map(fun(X)->element(2,X) end,SortedList).
+
+untagged_choice([#'ComponentType'{typespec=#type{tag=[],def={'CHOICE',_}}}|_Rest]) ->
+ true;
+untagged_choice([_|Rest]) ->
+ untagged_choice(Rest);
+untagged_choice([]) ->
+ false.
+
+check_setof(S,Type,Component) when record(Component,type) ->
+ check_type(S,Type,Component).
+
+check_restrictedstring(_S,_Def,_Constr) ->
+ ok.
+
+check_objectidentifier(_S,_Constr) ->
+ ok.
+
+% check all aspects of a CHOICE
+% - that all alternative names are unique
+% - that all TAGS are ok (when TAG default is applied)
+% - that each alternative is of a valid type
+% - that the extension marks are valid
+check_choice(S,Type,Components) when list(Components) ->
+ case check_unique([C||C <- Components,
+ record(C,'ComponentType')],#'ComponentType'.name) of
+ [] ->
+ %% sort_canonical(Components),
+ Components2 = maybe_automatic_tags(S,Components),
+ %NewComps =
+ case check_each_alternative(S,Type,Components2) of
+ {NewComponents,NewEcomps} ->
+ check_unique_tags(S,NewComponents ++ NewEcomps),
+ {NewComponents,NewEcomps};
+ NewComponents ->
+ check_unique_tags(S,NewComponents),
+ NewComponents
+ end;
+%% CompListWithTblInf = get_tableconstraint_info(S,Type,NewComps);
+ Dupl ->
+ throw({error,{asn1,{duplicate_choice_alternatives,Dupl}}})
+ end;
+check_choice(_S,_,[]) ->
+ [].
+
+%% probably dead code that should be removed
+%%maybe_automatic_tags(S,{Rc,Ec}) ->
+%% {maybe_automatic_tags1(S,Rc,0),maybe_automatic_tags1(S,Ec,length(Rc))};
+maybe_automatic_tags(#state{erule=per},C) ->
+ C;
+maybe_automatic_tags(#state{erule=per_bin},C) ->
+ C;
+maybe_automatic_tags(S,C) ->
+ maybe_automatic_tags1(S,C,0).
+
+maybe_automatic_tags1(S,C,TagNo) ->
+ case (S#state.module)#module.tagdefault of
+ 'AUTOMATIC' ->
+ generate_automatic_tags(S,C,TagNo);
+ _ ->
+ %% maybe is the module a multi file module were only some of
+ %% the modules have defaulttag AUTOMATIC TAGS then the names
+ %% of those types are saved in the table automatic_tags
+ Name= S#state.tname,
+ case is_automatic_tagged_in_multi_file(Name) of
+ true ->
+ generate_automatic_tags(S,C,TagNo);
+ false ->
+ C
+ end
+ end.
+
+is_automatic_tagged_in_multi_file(Name) ->
+ case ets:info(automatic_tags) of
+ undefined ->
+ %% this case when not multifile compilation
+ false;
+ _ ->
+ case ets:member(automatic_tags,Name) of
+ true ->
+ true;
+ _ ->
+ false
+ end
+ end.
+
+generate_automatic_tags(_S,C,TagNo) ->
+ case any_manual_tag(C) of
+ true ->
+ C;
+ false ->
+ generate_automatic_tags1(C,TagNo)
+ end.
+
+generate_automatic_tags1([H|T],TagNo) when record(H,'ComponentType') ->
+ #'ComponentType'{typespec=Ts} = H,
+ NewTs = Ts#type{tag=[#tag{class='CONTEXT',
+ number=TagNo,
+ type={default,'IMPLICIT'},
+ form= 0 }]}, % PRIMITIVE
+ [H#'ComponentType'{typespec=NewTs}|generate_automatic_tags1(T,TagNo+1)];
+generate_automatic_tags1([ExtMark|T],TagNo) -> % EXTENSIONMARK
+ [ExtMark | generate_automatic_tags1(T,TagNo)];
+generate_automatic_tags1([],_) ->
+ [].
+
+any_manual_tag([#'ComponentType'{typespec=#type{tag=[]}}|Rest]) ->
+ any_manual_tag(Rest);
+any_manual_tag([{'EXTENSIONMARK',_,_}|Rest]) ->
+ any_manual_tag(Rest);
+any_manual_tag([_|_Rest]) ->
+ true;
+any_manual_tag([]) ->
+ false.
+
+
+check_unique_tags(S,C) ->
+ case (S#state.module)#module.tagdefault of
+ 'AUTOMATIC' ->
+ case any_manual_tag(C) of
+ false -> true;
+ _ -> collect_and_sort_tags(C,[])
+ end;
+ _ ->
+ collect_and_sort_tags(C,[])
+ end.
+
+collect_and_sort_tags([C|Rest],Acc) when record(C,'ComponentType') ->
+ collect_and_sort_tags(Rest,C#'ComponentType'.tags ++ Acc);
+collect_and_sort_tags([_|Rest],Acc) ->
+ collect_and_sort_tags(Rest,Acc);
+collect_and_sort_tags([],Acc) ->
+ {Dupl,_}= lists:mapfoldl(fun(El,El)->{{dup,El},El};(El,_Prev)-> {El,El} end,notag,lists:sort(Acc)),
+ Dupl2 = [Dup|| {dup,Dup} <- Dupl],
+ if
+ length(Dupl2) > 0 ->
+ throw({error,{asn1,{duplicates_of_the_tags,Dupl2}}});
+ true ->
+ true
+ end.
+
+check_unique(L,Pos) ->
+ Slist = lists:keysort(Pos,L),
+ check_unique2(Slist,Pos,[]).
+
+check_unique2([A,B|T],Pos,Acc) when element(Pos,A) == element(Pos,B) ->
+ check_unique2([B|T],Pos,[element(Pos,B)|Acc]);
+check_unique2([_|T],Pos,Acc) ->
+ check_unique2(T,Pos,Acc);
+check_unique2([],_,Acc) ->
+ lists:reverse(Acc).
+
+check_each_component(S,Type,{Rlist,ExtList}) ->
+ {check_each_component(S,Type,Rlist),
+ check_each_component(S,Type,ExtList)};
+check_each_component(S,Type,Components) ->
+ check_each_component(S,Type,Components,[],[],noext).
+
+check_each_component(S = #state{abscomppath=Path,recordtopname=TopName},Type,
+ [C|Ct],Acc,Extacc,Ext) when record(C,'ComponentType') ->
+ #'ComponentType'{name=Cname,typespec=Ts,prop=Prop} = C,
+ NewAbsCPath =
+ case Ts#type.def of
+ #'Externaltypereference'{} -> [];
+ _ -> [Cname|Path]
+ end,
+ CheckedTs = check_type(S#state{abscomppath=NewAbsCPath,
+ recordtopname=[Cname|TopName]},Type,Ts),
+ NewTags = get_taglist(S,CheckedTs),
+
+ NewProp =
+% case lists:member(der,S#state.options) of
+% true ->
+% True ->
+ case normalize_value(S,CheckedTs,Prop,[Cname|TopName]) of
+ mandatory -> mandatory;
+ 'OPTIONAL' -> 'OPTIONAL';
+ DefaultValue -> {'DEFAULT',DefaultValue}
+ end,
+% _ ->
+% Prop
+% end,
+ NewC = C#'ComponentType'{typespec=CheckedTs,prop=NewProp,tags=NewTags},
+ case Ext of
+ noext ->
+ check_each_component(S,Type,Ct,[NewC|Acc],Extacc,Ext);
+ ext ->
+ check_each_component(S,Type,Ct,Acc,[NewC|Extacc],Ext)
+ end;
+check_each_component(S,Type,[_|Ct],Acc,Extacc,noext) -> % skip 'EXTENSIONMARK'
+ check_each_component(S,Type,Ct,Acc,Extacc,ext);
+check_each_component(_S,_,[_C|_Ct],_,_,ext) -> % skip 'EXTENSIONMARK'
+ throw({error,{asn1,{too_many_extension_marks}}});
+check_each_component(_S,_,[],Acc,Extacc,ext) ->
+ {lists:reverse(Acc),lists:reverse(Extacc)};
+check_each_component(_S,_,[],Acc,_,noext) ->
+ lists:reverse(Acc).
+
+check_each_alternative(S,Type,{Rlist,ExtList}) ->
+ {check_each_alternative(S,Type,Rlist),
+ check_each_alternative(S,Type,ExtList)};
+check_each_alternative(S,Type,[C|Ct]) ->
+ check_each_alternative(S,Type,[C|Ct],[],[],noext).
+
+check_each_alternative(S=#state{abscomppath=Path,recordtopname=TopName},Type,[C|Ct],
+ Acc,Extacc,Ext) when record(C,'ComponentType') ->
+ #'ComponentType'{name=Cname,typespec=Ts,prop=_Prop} = C,
+ NewAbsCPath =
+ case Ts#type.def of
+ #'Externaltypereference'{} -> [];
+ _ -> [Cname|Path]
+ end,
+ NewState =
+ S#state{abscomppath=NewAbsCPath,recordtopname=[Cname|TopName]},
+ CheckedTs = check_type(NewState,Type,Ts),
+ NewTags = get_taglist(S,CheckedTs),
+ NewC = C#'ComponentType'{typespec=CheckedTs,tags=NewTags},
+ case Ext of
+ noext ->
+ check_each_alternative(S,Type,Ct,[NewC|Acc],Extacc,Ext);
+ ext ->
+ check_each_alternative(S,Type,Ct,Acc,[NewC|Extacc],Ext)
+ end;
+
+check_each_alternative(S,Type,[_|Ct],Acc,Extacc,noext) -> % skip 'EXTENSIONMARK'
+ check_each_alternative(S,Type,Ct,Acc,Extacc,ext);
+check_each_alternative(_S,_,[_C|_Ct],_,_,ext) -> % skip 'EXTENSIONMARK'
+ throw({error,{asn1,{too_many_extension_marks}}});
+check_each_alternative(_S,_,[],Acc,Extacc,ext) ->
+ {lists:reverse(Acc),lists:reverse(Extacc)};
+check_each_alternative(_S,_,[],Acc,_,noext) ->
+ lists:reverse(Acc).
+
+%% componentrelation_leadingattr/2 searches the structure for table
+%% constraints, if any is found componentrelation_leadingattr/5 is
+%% called.
+componentrelation_leadingattr(S,CompList) ->
+% {Cs1,Cs2} =
+ Cs =
+ case CompList of
+ {Components,EComponents} when list(Components) ->
+% {Components,Components};
+ Components ++ EComponents;
+ CompList when list(CompList) ->
+% {CompList,CompList}
+ CompList
+ end,
+% case any_simple_table(S,Cs1,[]) of
+
+ %% get_simple_table_if_used/2 should find out whether there are any
+ %% component relation constraints in the entire tree of Cs1 that
+ %% relates to this level. It returns information about the simple
+ %% table constraint necessary for the the call to
+ %% componentrelation_leadingattr/6. The step when the leading
+ %% attribute and the syntax tree is modified to support the code
+ %% generating.
+ case get_simple_table_if_used(S,Cs) of
+ [] -> {false,CompList};
+ STList ->
+% componentrelation_leadingattr(S,Cs1,Cs2,STList,[],[])
+ componentrelation_leadingattr(S,Cs,Cs,STList,[],[])
+ end.
+
+%% componentrelation_leadingattr/6 when all components are searched
+%% the new modified components are returned together with the "leading
+%% attribute" information, which later is stored in the tablecinf
+%% field in the SEQUENCE/SET record. The "leading attribute"
+%% information is used to generate the lookup in the object set
+%% table. The other information gathered in the #type.tablecinf field
+%% is used in code generating phase too, to recognice the proper
+%% components for "open type" encoding and to propagate the result of
+%% the object set lookup when needed.
+componentrelation_leadingattr(_,[],_CompList,_,[],NewCompList) ->
+ {false,lists:reverse(NewCompList)};
+componentrelation_leadingattr(_,[],_CompList,_,LeadingAttr,NewCompList) ->
+ {lists:last(LeadingAttr),lists:reverse(NewCompList)}; %send all info in Ts later
+componentrelation_leadingattr(S,[C|Cs],CompList,STList,Acc,CompAcc) ->
+ {LAAcc,NewC} =
+ case catch componentrelation1(S,C#'ComponentType'.typespec,
+ [C#'ComponentType'.name]) of
+ {'EXIT',_} ->
+ {[],C};
+ {CRI=[{_A1,_B1,_C1,_D1}|_Rest],NewTSpec} ->
+ %% {ObjectSet,AtPath,ClassDef,Path}
+ %% _A1 is a reference to the object set of the
+ %% component relation constraint.
+ %% _B1 is the path of names in the at-list of the
+ %% component relation constraint.
+ %% _C1 is the class definition of the
+ %% ObjectClassFieldType.
+ %% _D1 is the path of components that was traversed to
+ %% find this constraint.
+ case leading_attr_index(S,CompList,CRI,
+ lists:reverse(S#state.abscomppath),[]) of
+ [] ->
+ {[],C};
+ [{ObjSet,Attr,N,ClassDef,_Path,ValueIndex}|_NewRest] ->
+ OS = object_set_mod_name(S,ObjSet),
+ UniqueFieldName =
+ case (catch get_unique_fieldname(#classdef{typespec=ClassDef})) of
+ {error,'__undefined_'} ->
+ no_unique;
+ {asn1,Msg,_} ->
+ error({type,Msg,S});
+ Other -> Other
+ end,
+% UsedFieldName = get_used_fieldname(S,Attr,STList),
+ %% Res should be done differently: even though
+ %% a unique field name exists it is not
+ %% certain that the ObjectClassFieldType of
+ %% the simple table constraint picks that
+ %% class field.
+ Res = #simpletableattributes{objectsetname=OS,
+%% c_name=asn1ct_gen:un_hyphen_var(Attr),
+ c_name=Attr,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex},
+ {[Res],C#'ComponentType'{typespec=NewTSpec}}
+ end;
+ _ ->
+ %% no constraint was found
+ {[],C}
+ end,
+ componentrelation_leadingattr(S,Cs,CompList,STList,LAAcc++Acc,
+ [NewC|CompAcc]).
+
+object_set_mod_name(_S,ObjSet) when atom(ObjSet) ->
+ ObjSet;
+object_set_mod_name(#state{mname=M},
+ #'Externaltypereference'{module=M,type=T}) ->
+ T;
+object_set_mod_name(S,#'Externaltypereference'{module=M,type=T}) ->
+ case lists:member(M,S#state.inputmodules) of
+ true ->
+ T;
+ false ->
+ {M,T}
+ end.
+
+%% get_used_fieldname gets the used field of the class referenced by
+%% the ObjectClassFieldType construct in the simple table constraint
+%% corresponding to the component relation constraint that depends on
+%% it.
+% get_used_fieldname(_S,CName,[{[CName|_Rest],_,ClFieldName}|_RestSimpleT]) ->
+% ClFieldName;
+% get_used_fieldname(S,CName,[_SimpleTC|Rest]) ->
+% get_used_fieldname(S,CName,Rest);
+% get_used_fieldname(S,_,[]) ->
+% error({type,"Error in Simple table constraint",S}).
+
+%% any_simple_table/3 checks if any of the components on this level is
+%% constrained by a simple table constraint. It returns a list of
+%% tuples with three elements. It is a name path to the place in the
+%% type structure where the constraint is, and the name of the object
+%% set and the referenced field in the class.
+% any_simple_table(S = #state{mname=M,abscomppath=Path},
+% [#'ComponentType'{name=Name,typespec=Type}|Cs],Acc) ->
+% Constraint = Type#type.constraint,
+% case lists:keysearch(simpletable,1,Constraint) of
+% {value,{_,#type{def=Ref}}} ->
+% %% This ObjectClassFieldType, which has a simple table
+% %% constraint, must pick a fixed type value, mustn't it ?
+% {ClassDef,[{_,ClassFieldName}]} = Type#type.def,
+% ST =
+% case Ref of
+% #'Externaltypereference'{module=M,type=ObjSetName} ->
+% {[Name|Path],ObjSetName,ClassFieldName};
+% _ ->
+% {[Name|Path],Ref,ClassFieldName}
+% end,
+% any_simple_table(S,Cs,[ST|Acc]);
+% false ->
+% any_simple_table(S,Cs,Acc)
+% end;
+% any_simple_table(_,[],Acc) ->
+% lists:reverse(Acc);
+% any_simple_table(S,[_|Cs],Acc) ->
+% any_simple_table(S,Cs,Acc).
+
+%% get_simple_table_if_used/2 searches the structure of Cs for any
+%% component relation constraints due to the present level of the
+%% structure. If there are any, the necessary information for code
+%% generation of the look up functionality in the object set table are
+%% returned.
+get_simple_table_if_used(S,Cs) ->
+ CNames = lists:map(fun(#'ComponentType'{name=Name}) -> Name;
+ (_) -> [] %% in case of extension marks
+ end,
+ Cs),
+ RefedSimpleTable=any_component_relation(S,Cs,CNames,[],[]),
+ get_simple_table_info(S,Cs,remove_doubles(RefedSimpleTable)).
+
+remove_doubles(L) ->
+ remove_doubles(L,[]).
+remove_doubles([H|T],Acc) ->
+ NewT = remove_doubles1(H,T),
+ remove_doubles(NewT,[H|Acc]);
+remove_doubles([],Acc) ->
+ Acc.
+
+remove_doubles1(El,L) ->
+ case lists:delete(El,L) of
+ L -> L;
+ NewL -> remove_doubles1(El,NewL)
+ end.
+
+%% get_simple_table_info searches the commponents Cs by the path from
+%% an at-list (third argument), and follows into a component of it if
+%% necessary, to get information needed for code generating.
+%%
+%% Returns a list of tuples with three elements. It holds a list of
+%% atoms that is the path, the name of the field of the class that are
+%% referred to in the ObjectClassFieldType, and the name of the unique
+%% field of the class of the ObjectClassFieldType.
+%%
+% %% The level information outermost/innermost must be kept. There are
+% %% at least two possibilities to cover here for an outermost case: 1)
+% %% Both the simple table and the component relation have a common path
+% %% at least one step below the outermost level, i.e. the leading
+% %% information shall be on a sub level. 2) They don't have any common
+% %% path.
+get_simple_table_info(S,Cs,[AtList|Rest]) ->
+%% [get_simple_table_info1(S,Cs,AtList,S#state.abscomppath)|get_simple_table_info(S,Cs,Rest)];
+ [get_simple_table_info1(S,Cs,AtList,[])|get_simple_table_info(S,Cs,Rest)];
+get_simple_table_info(_,_,[]) ->
+ [].
+get_simple_table_info1(S,Cs,[Cname|Cnames],Path) when list(Cs) ->
+ case lists:keysearch(Cname,#'ComponentType'.name,Cs) of
+ {value,C} ->
+ get_simple_table_info1(S,C,Cnames,[Cname|Path]);
+ _ ->
+ error({type,"Missing expected simple table constraint",S})
+ end;
+get_simple_table_info1(S,#'ComponentType'{typespec=TS},[],Path) ->
+ %% In this component there must be a simple table constraint
+ %% o.w. the asn1 code is wrong.
+ #type{def=OCFT,constraint=Cnstr} = TS,
+ case Cnstr of
+ [{simpletable,_OSRef}]�->
+ #'ObjectClassFieldType'{classname=ClRef,
+ class=ObjectClass,
+ fieldname=FieldName} = OCFT,
+% #'ObjectClassFieldType'{ObjectClass,FieldType} = ObjectClassFieldType,
+ ObjectClassFieldName =
+ case FieldName of
+ {LastFieldName,[]} -> LastFieldName;
+ {_FirstFieldName,FieldNames} ->
+ lists:last(FieldNames)
+ end,
+ %%ObjectClassFieldName is the last element in the dotted
+ %%list of the ObjectClassFieldType. The last element may
+ %%be of another class, that is referenced from the class
+ %%of the ObjectClassFieldType
+ ClassDef =
+ case ObjectClass of
+ [] ->
+ {_,CDef}=get_referenced_type(S,ClRef),
+ CDef;
+ _ -> #classdef{typespec=ObjectClass}
+ end,
+ UniqueName =
+ case (catch get_unique_fieldname(ClassDef)) of
+ {error,'__undefined_'} -> no_unique;
+ {asn1,Msg,_} ->
+ error({type,Msg,S});
+ Other -> Other
+ end,
+ {lists:reverse(Path),ObjectClassFieldName,UniqueName};
+ _ ->
+ error({type,{asn1,"missing expected simple table constraint",
+ Cnstr},S})
+ end;
+get_simple_table_info1(S,#'ComponentType'{typespec=TS},Cnames,Path) ->
+ Components = get_atlist_components(TS#type.def),
+ get_simple_table_info1(S,Components,Cnames,Path).
+
+%% any_component_relation searches for all component relation
+%% constraints that refers to the actual level and returns a list of
+%% the "name path" in the at-list to the component relation constraint
+%% that must refer to a simple table constraint. The list is empty if
+%% no component relation constraints were found.
+%%
+%% NamePath has the names of all components that are followed from the
+%% beginning of the search. CNames holds the names of all components
+%% of the start level, this info is used if an outermost at-notation
+%% is found to check the validity of the at-list.
+any_component_relation(S,[C|Cs],CNames,NamePath,Acc) ->
+ CName = C#'ComponentType'.name,
+ Type = C#'ComponentType'.typespec,
+ CRelPath =
+ case Type#type.constraint of
+ [{componentrelation,_,AtNotation}] ->
+ %% Found component relation constraint, now check
+ %% whether this constraint is relevant for the level
+ %% where the search started
+ AtNot = extract_at_notation(AtNotation),
+ %% evaluate_atpath returns the relative path to the
+ %% simple table constraint from where the component
+ %% relation is found.
+ evaluate_atpath(S#state.abscomppath,NamePath,CNames,AtNot);
+ _ ->
+ []
+ end,
+ InnerAcc =
+ case {Type#type.inlined,
+ asn1ct_gen:type(asn1ct_gen:get_inner(Type#type.def))} of
+ {no,{constructed,bif}} ->
+ InnerCs =
+ case get_components(Type#type.def) of
+ {IC1,_IC2} -> IC1 ++ IC1;
+ IC -> IC
+ end,
+ %% here we are interested in components of an
+ %% SEQUENCE/SET OF as well as SEQUENCE, SET and CHOICE
+ any_component_relation(S,InnerCs,CNames,[CName|NamePath],[]);
+ _ ->
+ []
+ end,
+ any_component_relation(S,Cs,CNames,NamePath,InnerAcc++CRelPath++Acc);
+any_component_relation(_,[],_,_,Acc) ->
+ Acc.
+
+%% evaluate_atpath/4 finds out whether the at notation refers to the
+%% search level. The list of referenced names in the AtNot list shall
+%% begin with a name that exists on the level it refers to. If the
+%% found AtPath is refering to the same sub-branch as the simple table
+%% has, then there shall not be any leading attribute info on this
+%% level.
+evaluate_atpath(_,[],Cnames,{innermost,AtPath=[Ref|_Refs]}) ->
+ %% any innermost constraint found deeper in the structure is
+ %% ignored.
+ case lists:member(Ref,Cnames) of
+ true -> [AtPath];
+ false -> []
+ end;
+%% In this case must check that the AtPath doesn't step any step of
+%% the NamePath, in that case the constraint will be handled in an
+%% inner level.
+evaluate_atpath(TopPath,NamePath,Cnames,{outermost,AtPath=[_Ref|_Refs]}) ->
+ AtPathBelowTop =
+ case TopPath of
+ [] -> AtPath;
+ _ ->
+ case lists:prefix(TopPath,AtPath) of
+ true ->
+ lists:subtract(AtPath,TopPath);
+ _ -> []
+ end
+ end,
+ case {NamePath,AtPathBelowTop} of
+ {[H|_T1],[H|_T2]} -> []; % this must be handled in lower level
+ {_,[]} -> [];% this must be handled in an above level
+ {_,[H|_T]} ->
+ case lists:member(H,Cnames) of
+ true -> [AtPathBelowTop];
+ _ -> error({type,{asn1,"failed to analyze at-path",AtPath}})
+ end
+ end;
+evaluate_atpath(_,_,_,_) ->
+ [].
+
+%% Type may be any of SEQUENCE, SET, CHOICE, SEQUENCE OF, SET OF but
+%% only the three first have valid components.
+get_atlist_components(Def) ->
+ get_components(atlist,Def).
+
+get_components(Def) ->
+ get_components(any,Def).
+
+get_components(_,#'SEQUENCE'{components=Cs}) ->
+ Cs;
+get_components(_,#'SET'{components=Cs}) ->
+ Cs;
+get_components(_,{'CHOICE',Cs}) ->
+ Cs;
+get_components(any,{'SEQUENCE OF',#type{def=Def}}) ->
+ get_components(any,Def);
+get_components(any,{'SET OF',#type{def=Def}}) ->
+ get_components(any,Def);
+get_components(_,_) ->
+ [].
+
+
+extract_at_notation([{Level,[#'Externalvaluereference'{value=Name}|Rest]}]) ->
+ {Level,[Name|extract_at_notation1(Rest)]};
+extract_at_notation(At) ->
+ exit({error,{asn1,{at_notation,At}}}).
+extract_at_notation1([#'Externalvaluereference'{value=Name}|Rest]) ->
+ [Name|extract_at_notation1(Rest)];
+extract_at_notation1([]) ->
+ [].
+
+%% componentrelation1/1 identifies all componentrelation constraints
+%% that exist in C or in the substructure of C. Info about the found
+%% constraints are returned in a list. It is ObjectSet, the reference
+%% to the object set, AttrPath, the name atoms extracted from the
+%% at-list in the component relation constraint, ClassDef, the
+%% objectclass record of the class of the ObjectClassFieldType, Path,
+%% that is the component name "path" from the searched level to this
+%% constraint.
+%%
+%% The function is called with one component of the type in turn and
+%% with the component name in Path at the first call. When called from
+%% within, the name of the inner component is added to Path.
+componentrelation1(S,C = #type{def=Def,constraint=Constraint,tablecinf=TCI},
+ Path) ->
+ Ret =
+ case Constraint of
+ [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
+ [{_,AL=[#'Externalvaluereference'{}|_R1]}|_R2] = AtList,
+ %% Note: if Path is longer than one,i.e. it is within
+ %% an inner type of the actual level, then the only
+ %% relevant at-list is of "outermost" type.
+%% #'ObjectClassFieldType'{class=ClassDef} = Def,
+ ClassDef = get_ObjectClassFieldType_classdef(S,Def),
+ AtPath =
+ lists:map(fun(#'Externalvaluereference'{value=V})->V end,
+ AL),
+ {[{ObjectSet,AtPath,ClassDef,Path}],Def};
+ _Other ->
+ %% check the inner type of component
+ innertype_comprel(S,Def,Path)
+ end,
+ case Ret of
+ nofunobj ->
+ nofunobj; %% ignored by caller
+ {CRelI=[{ObjSet,_,_,_}],NewDef} -> %%
+ TCItmp = lists:subtract(TCI,[{objfun,ObjSet}]),
+ {CRelI,C#type{tablecinf=[{objfun,ObjSet}|TCItmp],def=NewDef}};
+ {CompRelInf,NewDef} -> %% more than one tuple in CompRelInf
+ TCItmp = lists:subtract(TCI,[{objfun,anyset}]),
+ {CompRelInf,C#type{tablecinf=[{objfun,anyset}|TCItmp],def=NewDef}}
+ end.
+
+innertype_comprel(S,{'SEQUENCE OF',Type},Path) ->
+ case innertype_comprel1(S,Type,Path) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewType} ->
+ {CompRelInf,{'SEQUENCE OF',NewType}}
+ end;
+innertype_comprel(S,{'SET OF',Type},Path) ->
+ case innertype_comprel1(S,Type,Path) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewType} ->
+ {CompRelInf,{'SET OF',NewType}}
+ end;
+innertype_comprel(S,{'CHOICE',CTypeList},Path) ->
+ case componentlist_comprel(S,CTypeList,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,{'CHOICE',NewCs}}
+ end;
+innertype_comprel(S,Seq = #'SEQUENCE'{components=Cs},Path) ->
+ case componentlist_comprel(S,Cs,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,Seq#'SEQUENCE'{components=NewCs}}
+ end;
+innertype_comprel(S,Set = #'SET'{components=Cs},Path) ->
+ case componentlist_comprel(S,Cs,[],Path,[]) of
+ nofunobj ->
+ nofunobj;
+ {CompRelInf,NewCs} ->
+ {CompRelInf,Set#'SET'{components=NewCs}}
+ end;
+innertype_comprel(_,_,_) ->
+ nofunobj.
+
+componentlist_comprel(S,[C = #'ComponentType'{name=Name,typespec=Type}|Cs],
+ Acc,Path,NewCL) ->
+ case catch componentrelation1(S,Type,Path++[Name]) of
+ {'EXIT',_} ->
+ componentlist_comprel(S,Cs,Acc,Path,[C|NewCL]);
+ nofunobj ->
+ componentlist_comprel(S,Cs,Acc,Path,[C|NewCL]);
+ {CRelInf,NewType} ->
+ componentlist_comprel(S,Cs,CRelInf++Acc,Path,
+ [C#'ComponentType'{typespec=NewType}|NewCL])
+ end;
+componentlist_comprel(_,[],Acc,_,NewCL) ->
+ case Acc of
+ [] ->
+ nofunobj;
+ _ ->
+ {Acc,lists:reverse(NewCL)}
+ end.
+
+innertype_comprel1(S,T = #type{def=Def,constraint=Cons,tablecinf=TCI},Path) ->
+ Ret =
+ case Cons of
+ [{componentrelation,{_,_,ObjectSet},AtList}|_Rest] ->
+ %% This AtList must have an "outermost" at sign to be
+ %% relevent here.
+ [{_,AL=[#'Externalvaluereference'{value=_Attr}|_R1]}|_R2]
+ = AtList,
+%% #'ObjectClassFieldType'{class=ClassDef} = Def,
+ ClassDef = get_ObjectClassFieldType_classdef(S,Def),
+ AtPath =
+ lists:map(fun(#'Externalvaluereference'{value=V})->V end,
+ AL),
+ [{ObjectSet,AtPath,ClassDef,Path}];
+ _ ->
+ innertype_comprel(S,Def,Path)
+ end,
+ case Ret of
+ nofunobj -> nofunobj;
+ L = [{ObjSet,_,_,_}] ->
+ TCItmp = lists:subtract(TCI,[{objfun,ObjSet}]),
+ {L,T#type{tablecinf=[{objfun,ObjSet}|TCItmp]}};
+ {CRelInf,NewDef} ->
+ TCItmp = lists:subtract(TCI,[{objfun,anyset}]),
+ {CRelInf,T#type{def=NewDef,tablecinf=[{objfun,anyset}|TCItmp]}}
+ end.
+
+
+%% leading_attr_index counts the index and picks the name of the
+%% component that is at the actual level in the at-list of the
+%% component relation constraint (AttrP). AbsP is the path of
+%% component names from the top type level to the actual level. AttrP
+%% is a list with the atoms from the at-list.
+leading_attr_index(S,Cs,[H={_,AttrP,_,_}|T],AbsP,Acc) ->
+ AttrInfo =
+ case lists:prefix(AbsP,AttrP) of
+ %% why this ?? It is necessary when in same situation as
+ %% TConstrChoice, there is an inner structure with an
+ %% outermost at-list and the "leading attribute" code gen
+ %% may be at a level some steps below the outermost level.
+ true ->
+ RelativAttrP = lists:subtract(AttrP,AbsP),
+ %% The header is used to calculate the index of the
+ %% component and to give the fun, received from the
+ %% object set look up, an unique name. The tail is
+ %% used to match the proper value input to the fun.
+ {hd(RelativAttrP),tl(RelativAttrP)};
+ false ->
+ {hd(AttrP),tl(AttrP)}
+ end,
+ case leading_attr_index1(S,Cs,H,AttrInfo,1) of
+ 0 ->
+ leading_attr_index(S,Cs,T,AbsP,Acc);
+ Res ->
+ leading_attr_index(S,Cs,T,AbsP,[Res|Acc])
+ end;
+leading_attr_index(_,_Cs,[],_,Acc) ->
+ lists:reverse(Acc).
+
+leading_attr_index1(_,[],_,_,_) ->
+ 0;
+leading_attr_index1(S,[C|Cs],Arg={ObjectSet,_,CDef,P},
+ AttrInfo={Attr,SubAttr},N) ->
+ case C#'ComponentType'.name of
+ Attr ->
+ ValueMatch = value_match(S,C,Attr,SubAttr),
+ {ObjectSet,Attr,N,CDef,P,ValueMatch};
+ _ ->
+ leading_attr_index1(S,Cs,Arg,AttrInfo,N+1)
+ end.
+
+%% value_math gathers information for a proper value match in the
+%% generated encode function. For a SEQUENCE or a SET the index of the
+%% component is counted. For a CHOICE the index is 2.
+value_match(S,C,Name,SubAttr) ->
+ value_match(S,C,Name,SubAttr,[]). % C has name Name
+value_match(_S,#'ComponentType'{},_Name,[],Acc) ->
+ Acc;% do not reverse, indexes in reverse order
+value_match(S,#'ComponentType'{typespec=Type},Name,[At|Ats],Acc) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ Components =
+ case get_atlist_components(Type#type.def) of
+ [] -> error({type,{asn1,"element in at list must be a "
+ "SEQUENCE, SET or CHOICE.",Name},S});
+ Comps -> Comps
+ end,
+ {Index,ValueIndex} = component_value_index(S,InnerType,At,Components),
+ value_match(S,lists:nth(Index,Components),At,Ats,[ValueIndex|Acc]).
+
+component_value_index(S,'CHOICE',At,Components) ->
+ {component_index(S,At,Components),2};
+component_value_index(S,_,At,Components) ->
+ %% SEQUENCE or SET
+ Index = component_index(S,At,Components),
+ {Index,{Index+1,At}}.
+
+component_index(S,Name,Components) ->
+ component_index1(S,Name,Components,1).
+component_index1(_S,Name,[#'ComponentType'{name=Name}|_Cs],N) ->
+ N;
+component_index1(S,Name,[_C|Cs],N) ->
+ component_index1(S,Name,Cs,N+1);
+component_index1(S,Name,[],_) ->
+ error({type,{asn1,"component of at-list was not"
+ " found in substructure",Name},S}).
+
+get_unique_fieldname(ClassDef) ->
+%% {_,Fields,_} = ClassDef#classdef.typespec,
+ Fields = (ClassDef#classdef.typespec)#objectclass.fields,
+ get_unique_fieldname(Fields,[]).
+
+get_unique_fieldname([],[]) ->
+ throw({error,'__undefined_'});
+get_unique_fieldname([],[Name]) ->
+ Name;
+get_unique_fieldname([],Acc) ->
+ throw({asn1,'only one UNIQUE field is allowed in CLASS',Acc});
+get_unique_fieldname([{fixedtypevaluefield,Name,_,'UNIQUE',_}|Rest],Acc) ->
+ get_unique_fieldname(Rest,[Name|Acc]);
+get_unique_fieldname([_H|T],Acc) ->
+ get_unique_fieldname(T,Acc).
+
+get_tableconstraint_info(S,Type,{CheckedTs,EComps}) ->
+ {get_tableconstraint_info(S,Type,CheckedTs,[]),
+ get_tableconstraint_info(S,Type,EComps,[])};
+get_tableconstraint_info(S,Type,CheckedTs) ->
+ get_tableconstraint_info(S,Type,CheckedTs,[]).
+
+get_tableconstraint_info(_S,_Type,[],Acc) ->
+ lists:reverse(Acc);
+get_tableconstraint_info(S,Type,[C|Cs],Acc) ->
+ CheckedTs = C#'ComponentType'.typespec,
+ AccComp =
+ case CheckedTs#type.def of
+ %% ObjectClassFieldType
+ OCFT=#'ObjectClassFieldType'{class=#objectclass{},
+ type=_AType} ->
+% AType = get_ObjectClassFieldType(S,Fields,FieldRef),
+% RefedFieldName =
+% get_referencedclassfield(CheckedTs#type.def),%is probably obsolete
+ NewOCFT =
+ OCFT#'ObjectClassFieldType'{class=[]},
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+% def=AType,
+ def=NewOCFT
+ }};
+% constraint=[{tableconstraint_info,
+% FieldRef}]}};
+ {'SEQUENCE OF',SOType} when record(SOType,type),
+ (element(1,SOType#type.def)=='CHOICE') ->
+ CTypeList = element(2,SOType#type.def),
+ NewInnerCList =
+ get_tableconstraint_info(S,Type,CTypeList,[]),
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def={'SEQUENCE OF',
+ SOType#type{def={'CHOICE',
+ NewInnerCList}}}}};
+ {'SET OF',SOType} when record(SOType,type),
+ (element(1,SOType#type.def)=='CHOICE') ->
+ CTypeList = element(2,SOType#type.def),
+ NewInnerCList =
+ get_tableconstraint_info(S,Type,CTypeList,[]),
+ C#'ComponentType'{typespec=
+ CheckedTs#type{
+ def={'SET OF',
+ SOType#type{def={'CHOICE',
+ NewInnerCList}}}}};
+ _ ->
+ C
+ end,
+ get_tableconstraint_info(S,Type,Cs,[AccComp|Acc]).
+
+get_referenced_fieldname([{_,FirstFieldname}]) ->
+ {FirstFieldname,[]};
+get_referenced_fieldname([{_,FirstFieldname}|Rest]) ->
+ {FirstFieldname,lists:map(fun(X)->element(2,X) end,Rest)};
+get_referenced_fieldname(Def) ->
+ {no_type,Def}.
+
+%% get_ObjectClassFieldType extracts the type from the chain of
+%% objects that leads to a final type.
+get_ObjectClassFieldType(S,ERef,PrimFieldNameList) when
+ record(ERef,'Externaltypereference') ->
+ {_,Type} = get_referenced_type(S,ERef),
+ ClassSpec = check_class(S,Type),
+ Fields = ClassSpec#objectclass.fields,
+ get_ObjectClassFieldType(S,Fields,PrimFieldNameList);
+get_ObjectClassFieldType(S,Fields,L=[_PrimFieldName1|_Rest]) ->
+ check_PrimitiveFieldNames(S,Fields,L),
+ get_OCFType(S,Fields,L).
+
+check_PrimitiveFieldNames(_S,_Fields,_) ->
+ ok.
+
+%% get_ObjectClassFieldType_classdef gets the def of the class of the
+%% ObjectClassFieldType, i.e. the objectclass record. If the type has
+%% been checked (it may be a field type of an internal SEQUENCE) the
+%% class field = [], then the classdef has to be fetched by help of
+%% the class reference in the classname field.
+get_ObjectClassFieldType_classdef(S,#'ObjectClassFieldType'{classname=Name,
+ class=[]}) ->
+ {_,#classdef{typespec=TS}} = get_referenced_type(S,Name),
+ TS;
+get_ObjectClassFieldType_classdef(_,#'ObjectClassFieldType'{class=Cl}) ->
+ Cl.
+
+get_OCFType(S,Fields,[{_FieldType,PrimFieldName}|Rest]) ->
+ case lists:keysearch(PrimFieldName,2,Fields) of
+ {value,{fixedtypevaluefield,_,Type,_Unique,_OptSpec}} ->
+ {fixedtypevaluefield,PrimFieldName,Type};
+ {value,{objectfield,_,Type,_Unique,_OptSpec}} ->
+ {_,ClassDef} = get_referenced_type(S,Type#type.def),
+ CheckedCDef = check_class(S#state{type=ClassDef,
+ tname=ClassDef#classdef.name},
+ ClassDef#classdef.typespec),
+ get_OCFType(S,CheckedCDef#objectclass.fields,Rest);
+ {value,{objectsetfield,_,Type,_OptSpec}} ->
+ {_,ClassDef} = get_referenced_type(S,Type#type.def),
+ CheckedCDef = check_class(S#state{type=ClassDef,
+ tname=ClassDef#classdef.name},
+ ClassDef#classdef.typespec),
+ get_OCFType(S,CheckedCDef#objectclass.fields,Rest);
+
+ {value,Other} ->
+ {element(1,Other),PrimFieldName};
+ _ ->
+ error({type,"undefined FieldName in ObjectClassFieldType",S})
+ end.
+
+get_taglist(#state{erule=per},_) ->
+ [];
+get_taglist(#state{erule=per_bin},_) ->
+ [];
+get_taglist(S,Ext) when record(Ext,'Externaltypereference') ->
+ {_,T} = get_referenced_type(S,Ext),
+ get_taglist(S,T#typedef.typespec);
+get_taglist(S,Tref) when record(Tref,typereference) ->
+ {_,T} = get_referenced_type(S,Tref),
+ get_taglist(S,T#typedef.typespec);
+get_taglist(S,Type) when record(Type,type) ->
+ case Type#type.tag of
+ [] ->
+ get_taglist(S,Type#type.def);
+ [Tag|_] ->
+% case lists:member(S#state.erule,[ber,ber_bin]) of
+% true ->
+% lists:map(fun(Tx) -> asn1ct_gen:def_to_tag(Tx) end,Type#type.tag);
+% _ ->
+ [asn1ct_gen:def_to_tag(Tag)]
+% end
+ end;
+get_taglist(S,{'CHOICE',{Rc,Ec}}) ->
+ get_taglist(S,{'CHOICE',Rc ++ Ec});
+get_taglist(S,{'CHOICE',Components}) ->
+ get_taglist1(S,Components);
+%% ObjectClassFieldType OTP-4390
+get_taglist(_S,#'ObjectClassFieldType'{type={typefield,_}}) ->
+ [];
+get_taglist(S,#'ObjectClassFieldType'{type={fixedtypevaluefield,_,Type}}) ->
+ get_taglist(S,Type);
+get_taglist(S,{ERef=#'Externaltypereference'{},FieldNameList})
+ when list(FieldNameList) ->
+ case get_ObjectClassFieldType(S,ERef,FieldNameList) of
+ Type when record(Type,type) ->
+ get_taglist(S,Type);
+ {fixedtypevaluefield,_,Type} -> get_taglist(S,Type);
+ {TypeFieldName,_} when atom(TypeFieldName) -> []%should check if allowed
+ end;
+get_taglist(S,{ObjCl,FieldNameList}) when record(ObjCl,objectclass),
+ list(FieldNameList) ->
+ case get_ObjectClassFieldType(S,ObjCl#objectclass.fields,FieldNameList) of
+ Type when record(Type,type) ->
+ get_taglist(S,Type);
+ {fixedtypevaluefield,_,Type} -> get_taglist(S,Type);
+ {TypeFieldName,_} when atom(TypeFieldName) -> []%should check if allowed
+ end;
+get_taglist(S,Def) ->
+ case lists:member(S#state.erule,[ber_bin_v2]) of
+ false ->
+ case Def of
+ 'ASN1_OPEN_TYPE' -> % open_type has no UNIVERSAL tag as such
+ [];
+ _ ->
+ [asn1ct_gen:def_to_tag(Def)]
+ end;
+ _ ->
+ []
+ end.
+
+get_taglist1(S,[#'ComponentType'{name=_Cname,tags=TagL}|Rest]) when list(TagL) ->
+ %% tag_list has been here , just return TagL and continue with next alternative
+ TagL ++ get_taglist1(S,Rest);
+get_taglist1(S,[#'ComponentType'{typespec=Ts,tags=undefined}|Rest]) ->
+ get_taglist(S,Ts) ++ get_taglist1(S,Rest);
+get_taglist1(S,[_H|Rest]) -> % skip EXTENSIONMARK
+ get_taglist1(S,Rest);
+get_taglist1(_S,[]) ->
+ [].
+
+dbget_ex(_S,Module,Key) ->
+ case asn1_db:dbget(Module,Key) of
+ undefined ->
+
+ throw({error,{asn1,{undefined,{Module,Key}}}}); % this is catched on toplevel type or value
+ T -> T
+ end.
+
+merge_tags(T1, T2) when list(T2) ->
+ merge_tags2(T1 ++ T2, []);
+merge_tags(T1, T2) ->
+ merge_tags2(T1 ++ [T2], []).
+
+merge_tags2([T1= #tag{type='IMPLICIT'}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([T1= #tag{type={default,'IMPLICIT'}}, T2 |Rest], Acc) ->
+ merge_tags2([T1#tag{type=T2#tag.type, form=T2#tag.form}|Rest],Acc);
+merge_tags2([H|T],Acc) ->
+ merge_tags2(T, [H|Acc]);
+merge_tags2([], Acc) ->
+ lists:reverse(Acc).
+
+merge_constraints(C1, []) ->
+ C1;
+merge_constraints([], C2) ->
+ C2;
+merge_constraints(C1, C2) ->
+ {SList,VList,PAList,Rest} = splitlist(C1++C2,[],[],[],[]),
+ SizeC = merge_constraints(SList),
+ ValueC = merge_constraints(VList),
+ PermAlphaC = merge_constraints(PAList),
+ case Rest of
+ [] ->
+ SizeC ++ ValueC ++ PermAlphaC;
+ _ ->
+ throw({error,{asn1,{not_implemented,{merge_constraints,Rest}}}})
+ end.
+
+merge_constraints([]) -> [];
+merge_constraints([C1 = {_,{Low1,High1}},{_,{Low2,High2}}|Rest]) when Low1 >= Low2,
+ High1 =< High2 ->
+ merge_constraints([C1|Rest]);
+merge_constraints([C1={'PermittedAlphabet',_},C2|Rest]) ->
+ [C1|merge_constraints([C2|Rest])];
+merge_constraints([C1 = {_,{_Low1,_High1}},C2 = {_,{_Low2,_High2}}|_Rest]) ->
+ throw({error,asn1,{conflicting_constraints,{C1,C2}}});
+merge_constraints([C]) ->
+ [C].
+
+splitlist([C={'SizeConstraint',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,[C|Sacc],Vacc,PAacc,Restacc);
+splitlist([C={'ValueRange',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,Sacc,[C|Vacc],PAacc,Restacc);
+splitlist([C={'PermittedAlphabet',_}|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,Sacc,Vacc,[C|PAacc],Restacc);
+splitlist([C|Rest],Sacc,Vacc,PAacc,Restacc) ->
+ splitlist(Rest,Sacc,Vacc,PAacc,[C|Restacc]);
+splitlist([],Sacc,Vacc,PAacc,Restacc) ->
+ {lists:reverse(Sacc),
+ lists:reverse(Vacc),
+ lists:reverse(PAacc),
+ lists:reverse(Restacc)}.
+
+
+
+storeindb(M) when record(M,module) ->
+ TVlist = M#module.typeorval,
+ NewM = M#module{typeorval=findtypes_and_values(TVlist)},
+ asn1_db:dbnew(NewM#module.name),
+ asn1_db:dbput(NewM#module.name,'MODULE', NewM),
+ Res = storeindb(NewM#module.name,TVlist,[]),
+ include_default_class(NewM#module.name),
+ include_default_type(NewM#module.name),
+ Res.
+
+storeindb(Module,[H|T],ErrAcc) when record(H,typedef) ->
+ storeindb(Module,H#typedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,valuedef) ->
+ storeindb(Module,H#valuedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,ptypedef) ->
+ storeindb(Module,H#ptypedef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,classdef) ->
+ storeindb(Module,H#classdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,pvaluesetdef) ->
+ storeindb(Module,H#pvaluesetdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,pobjectdef) ->
+ storeindb(Module,H#pobjectdef.name,H,T,ErrAcc);
+storeindb(Module,[H|T],ErrAcc) when record(H,pvaluedef) ->
+ storeindb(Module,H#pvaluedef.name,H,T,ErrAcc);
+storeindb(_,[],[]) -> ok;
+storeindb(_,[],ErrAcc) ->
+ {error,ErrAcc}.
+
+storeindb(Module,Name,H,T,ErrAcc) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ asn1_db:dbput(Module,Name,H),
+ storeindb(Module,T,ErrAcc);
+ _ ->
+ case H of
+ _Type when record(H,typedef) ->
+ error({type,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,valuedef) ->
+ error({value,"already defined",
+ #state{mname=Module,value=H,vname=Name}});
+ _Type when record(H,ptypedef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,pobjectdef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,pvaluesetdef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,pvaluedef) ->
+ error({ptype,"already defined",
+ #state{mname=Module,type=H,tname=Name}});
+ _Type when record(H,classdef) ->
+ error({class,"already defined",
+ #state{mname=Module,value=H,vname=Name}})
+ end,
+ storeindb(Module,T,[H|ErrAcc])
+ end.
+
+findtypes_and_values(TVList) ->
+ findtypes_and_values(TVList,[],[],[],[],[],[]).%% Types,Values,
+%% Parameterizedtypes,Classes,Objects and ObjectSets
+
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,typedef),record(H#typedef.typespec,'Object') ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,[H#typedef.name|Oacc],OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,typedef),record(H#typedef.typespec,'ObjectSet') ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,Oacc,[H#typedef.name|OSacc]);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,typedef) ->
+ findtypes_and_values(T,[H#typedef.name|Tacc],Vacc,Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,valuedef) ->
+ findtypes_and_values(T,Tacc,[H#valuedef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,ptypedef) ->
+ findtypes_and_values(T,Tacc,Vacc,[H#ptypedef.name|Pacc],Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,classdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,[H#classdef.name|Cacc],Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pvaluedef) ->
+ findtypes_and_values(T,Tacc,[H#pvaluedef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pvaluesetdef) ->
+ findtypes_and_values(T,Tacc,[H#pvaluesetdef.name|Vacc],Pacc,Cacc,Oacc,OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pobjectdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,[H#pobjectdef.name|Oacc],OSacc);
+findtypes_and_values([H|T],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc)
+ when record(H,pobjectsetdef) ->
+ findtypes_and_values(T,Tacc,Vacc,Pacc,Cacc,Oacc,[H#pobjectsetdef.name|OSacc]);
+findtypes_and_values([],Tacc,Vacc,Pacc,Cacc,Oacc,OSacc) ->
+ {lists:reverse(Tacc),lists:reverse(Vacc),lists:reverse(Pacc),
+ lists:reverse(Cacc),lists:reverse(Oacc),lists:reverse(OSacc)}.
+
+
+
+error({export,Msg,#state{mname=Mname,type=Ref,tname=Typename}}) ->
+ Pos = Ref#'Externaltypereference'.pos,
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{export,Pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when record(Type,typedef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",
+ [Type#typedef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#typedef.pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when record(Type,ptypedef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",
+ [Type#ptypedef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#ptypedef.pos,Mname,Typename,Msg}};
+error({type,Msg,#state{mname=Mname,value=Value,vname=Valuename}})
+ when record(Value,valuedef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Value#valuedef.pos,Mname,Valuename,Msg]),
+ {error,{type,Value#valuedef.pos,Mname,Valuename,Msg}};
+error({type,Msg,#state{mname=Mname,type=Type,tname=Typename}})
+ when record(Type,pobjectdef) ->
+ io:format("asn1error:~p:~p:~p ~p~n",
+ [Type#pobjectdef.pos,Mname,Typename,Msg]),
+ {error,{type,Type#pobjectdef.pos,Mname,Typename,Msg}};
+error({value,Msg,#state{mname=Mname,value=Value,vname=Valuename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Value#valuedef.pos,Mname,Valuename,Msg]),
+ {error,{value,Value#valuedef.pos,Mname,Valuename,Msg}};
+error({Other,Msg,#state{mname=Mname,value=#valuedef{pos=Pos},vname=Valuename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Valuename,Msg]),
+ {error,{Other,Pos,Mname,Valuename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=#typedef{pos=Pos},tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{Other,Pos,Mname,Typename,Msg}};
+error({Other,Msg,#state{mname=Mname,type=#classdef{pos=Pos},tname=Typename}}) ->
+ io:format("asn1error:~p:~p:~p ~p~n",[Pos,Mname,Typename,Msg]),
+ {error,{Other,Pos,Mname,Typename,Msg}}.
+
+include_default_type(Module) ->
+ NameAbsList = default_type_list(),
+ include_default_type1(Module,NameAbsList).
+
+include_default_type1(_,[]) ->
+ ok;
+include_default_type1(Module,[{Name,TS}|Rest]) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ T = #typedef{name=Name,
+ typespec=TS},
+ asn1_db:dbput(Module,Name,T);
+ _ -> ok
+ end,
+ include_default_type1(Module,Rest).
+
+default_type_list() ->
+ %% The EXTERNAL type is represented, according to ASN.1 1997,
+ %% as a SEQUENCE with components: identification, data-value-descriptor
+ %% and data-value.
+ Syntax =
+ #'ComponentType'{name=syntax,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Presentation_Cid =
+ #'ComponentType'{name='presentation-context-id',
+ typespec=#type{def='INTEGER'},
+ prop=mandatory},
+ Transfer_syntax =
+ #'ComponentType'{name='transfer-syntax',
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Negotiation_items =
+ #type{def=
+ #'SEQUENCE'{components=
+ [Presentation_Cid,
+ Transfer_syntax#'ComponentType'{prop=mandatory}]}},
+ Context_negot =
+ #'ComponentType'{name='context-negotiation',
+ typespec=Negotiation_items,
+ prop=mandatory},
+
+ Data_value_descriptor =
+ #'ComponentType'{name='data-value-descriptor',
+ typespec=#type{def='ObjectDescriptor'},
+ prop='OPTIONAL'},
+ Data_value =
+ #'ComponentType'{name='data-value',
+ typespec=#type{def='OCTET STRING'},
+ prop=mandatory},
+
+ %% The EXTERNAL type is represented, according to ASN.1 1990,
+ %% as a SEQUENCE with components: direct-reference, indirect-reference,
+ %% data-value-descriptor and encoding.
+
+ Direct_reference =
+ #'ComponentType'{name='direct-reference',
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop='OPTIONAL'},
+
+ Indirect_reference =
+ #'ComponentType'{name='indirect-reference',
+ typespec=#type{def='INTEGER'},
+ prop='OPTIONAL'},
+
+ Single_ASN1_type =
+ #'ComponentType'{name='single-ASN1-type',
+ typespec=#type{tag=[{tag,'CONTEXT',0,
+ 'EXPLICIT',32}],
+ def='ANY'},
+ prop=mandatory},
+
+ Octet_aligned =
+ #'ComponentType'{name='octet-aligned',
+ typespec=#type{tag=[{tag,'CONTEXT',1,
+ 'IMPLICIT',32}],
+ def='OCTET STRING'},
+ prop=mandatory},
+
+ Arbitrary =
+ #'ComponentType'{name=arbitrary,
+ typespec=#type{tag=[{tag,'CONTEXT',2,
+ 'IMPLICIT',32}],
+ def={'BIT STRING',[]}},
+ prop=mandatory},
+
+ Encoding =
+ #'ComponentType'{name=encoding,
+ typespec=#type{def={'CHOICE',
+ [Single_ASN1_type,Octet_aligned,
+ Arbitrary]}},
+ prop=mandatory},
+
+ EXTERNAL_components1990 =
+ [Direct_reference,Indirect_reference,Data_value_descriptor,Encoding],
+
+ %% The EMBEDDED PDV type is represented by a SEQUENCE type
+ %% with components: identification and data-value
+ Abstract =
+ #'ComponentType'{name=abstract,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ Transfer =
+ #'ComponentType'{name=transfer,
+ typespec=#type{def='OBJECT IDENTIFIER'},
+ prop=mandatory},
+ AbstractTrSeq =
+ #'SEQUENCE'{components=[Abstract,Transfer]},
+ Syntaxes =
+ #'ComponentType'{name=syntaxes,
+ typespec=#type{def=AbstractTrSeq},
+ prop=mandatory},
+ Fixed = #'ComponentType'{name=fixed,
+ typespec=#type{def='NULL'},
+ prop=mandatory},
+ Negotiations =
+ [Syntaxes,Syntax,Presentation_Cid,Context_negot,
+ Transfer_syntax,Fixed],
+ Identification2 =
+ #'ComponentType'{name=identification,
+ typespec=#type{def={'CHOICE',Negotiations}},
+ prop=mandatory},
+ EmbeddedPdv_components =
+ [Identification2,Data_value],
+
+ %% The CHARACTER STRING type is represented by a SEQUENCE type
+ %% with components: identification and string-value
+ String_value =
+ #'ComponentType'{name='string-value',
+ typespec=#type{def='OCTET STRING'},
+ prop=mandatory},
+ CharacterString_components =
+ [Identification2,String_value],
+
+ [{'EXTERNAL',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=8,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=
+ EXTERNAL_components1990}}},
+ {'EMBEDDED PDV',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=11,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=EmbeddedPdv_components}}},
+ {'CHARACTER STRING',
+ #type{tag=[#tag{class='UNIVERSAL',
+ number=29,
+ type='IMPLICIT',
+ form=32}],
+ def=#'SEQUENCE'{components=CharacterString_components}}}
+ ].
+
+
+include_default_class(Module) ->
+ NameAbsList = default_class_list(),
+ include_default_class1(Module,NameAbsList).
+
+include_default_class1(_,[]) ->
+ ok;
+include_default_class1(Module,[{Name,TS}|_Rest]) ->
+ case asn1_db:dbget(Module,Name) of
+ undefined ->
+ C = #classdef{checked=true,name=Name,
+ typespec=TS},
+ asn1_db:dbput(Module,Name,C);
+ _ -> ok
+ end.
+
+default_class_list() ->
+ [{'TYPE-IDENTIFIER',
+ {objectclass,
+ [{fixedtypevaluefield,
+ id,
+ {type,[],'OBJECT IDENTIFIER',[]},
+ 'UNIQUE',
+ 'MANDATORY'},
+ {typefield,'Type','MANDATORY'}],
+ {'WITH SYNTAX',
+ [{typefieldreference,'Type'},
+ 'IDENTIFIED',
+ 'BY',
+ {valuefieldreference,id}]}}},
+ {'ABSTRACT-SYNTAX',
+ {objectclass,
+ [{fixedtypevaluefield,
+ id,
+ {type,[],'OBJECT IDENTIFIER',[]},
+ 'UNIQUE',
+ 'MANDATORY'},
+ {typefield,'Type','MANDATORY'},
+ {fixedtypevaluefield,
+ property,
+ {type,
+ [],
+ {'BIT STRING',[]},
+ []},
+ undefined,
+ {'DEFAULT',
+ [0,1,0]}}],
+ {'WITH SYNTAX',
+ [{typefieldreference,'Type'},
+ 'IDENTIFIED',
+ 'BY',
+ {valuefieldreference,id},
+ ['HAS',
+ 'PROPERTY',
+ {valuefieldreference,property}]]}}}].
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber.erl
new file mode 100644
index 0000000000..8a639de5bb
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber.erl
@@ -0,0 +1,1468 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_constructed_ber.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_constructed_ber).
+
+-export([gen_encode_sequence/3]).
+-export([gen_decode_sequence/3]).
+-export([gen_encode_set/3]).
+-export([gen_decode_set/3]).
+-export([gen_encode_sof/4]).
+-export([gen_decode_sof/4]).
+-export([gen_encode_choice/3]).
+-export([gen_decode_choice/3]).
+
+%%%% Application internal exports
+-export([match_tag/2]).
+
+-include("asn1_records.hrl").
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+
+ %% if EXTERNAL type the input value must be transformed to
+ %% ASN1 1990 format
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([" NewVal = asn1rt_check:transform_to_EXTERNAL1990(Val),",
+ nl]);
+ _ ->
+ ok
+ end,
+
+ {SeqOrSet,TableConsInfo,CompList} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {'SEQUENCE',TCI,CL};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {'SET',TCI,CL}
+ end,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ EncObj =
+ case TableConsInfo of
+ #simpletableattributes{usedclassfield=Used,
+ uniqueclassfield=Unique} when Used /= Unique ->
+ false;
+ %% ObjectSet, name of the object set in constraints
+ %%
+ %%{ObjectSet,AttrN,N,UniqueFieldName}
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex
+ } ->
+ OSDef =
+ case ObjectSet of
+ {Module,OSName} ->
+ asn1_db:dbget(Module,OSName);
+ OSName ->
+ asn1_db:dbget(get(currmod),OSName)
+ end,
+% io:format("currmod: ~p~nOSName: ~p~nAttrN: ~p~nN: ~p~nUniqueFieldName: ~p~n",
+% [get(currmod),OSName,AttrN,N,UniqueFieldName]),
+ case (OSDef#typedef.typespec)#'ObjectSet'.gen of
+ true ->
+% Val = lists:concat(["?RT_BER:cindex(",
+% N+1,",Val,"]),
+ ObjectEncode =
+ asn1ct_gen:un_hyphen_var(lists:concat(['Obj',
+ AttrN])),
+ emit({ObjectEncode," = ",nl}),
+ emit({" 'getenc_",ObjectSet,"'(",{asis,UniqueFieldName},
+ ", ",nl}),
+% emit({indent(35),"?RT_BER:cindex(",N+1,", Val,",
+% {asis,AttrN},")),",nl}),
+ emit([indent(10+length(atom_to_list(ObjectSet))),
+ "value_match(",{asis,ValueIndex},",",
+ "?RT_BER:cindex(",N+1,",Val,",
+ {asis,AttrN},"))),",nl]),
+ notice_value_match(),
+ {AttrN,ObjectEncode};
+ _ ->
+ false
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ %% when the simpletableattributes was at an
+ %% outer level and the objfun has been passed
+ %% through the function call
+ {"got objfun through args","ObjFun"};
+ _ ->
+ false
+ end
+ end,
+
+ gen_enc_sequence_call(Erules,Typename,CompList1,1,Ext,EncObj),
+
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type(SeqOrSet),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([nl," BytesSoFar = "]),
+ case SeqOrSet of
+ 'SET' when (D#type.def)#'SET'.sorted == dynamic ->
+ emit("?RT_BER:dynamicsort_SET_components(["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["]),",nl]);
+ _ ->
+ emit("["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["],",nl])
+ end,
+ emit(" LenSoFar = "),
+ case asn1ct_name:all(encLen) of
+ [] -> emit("0");
+ AllLengths ->
+ mkvplus(AllLengths)
+ end,
+ emit([",",nl]),
+% emit(["{TagBytes,Len} = ?RT_BER:encode_tags(TagIn ++ ",
+ emit([" ?RT_BER:encode_tags(TagIn ++ ",
+ {asis,MyTag},", BytesSoFar, LenSoFar).",nl]).
+
+
+gen_decode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+% asn1ct_name:new(term),
+ asn1ct_name:new(tag),
+ #'SEQUENCE'{tablecinf=TableConsInfo,components=CList} = D#type.def,
+ Ext = extensible(CList),
+ CompList = case CList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CList
+ end,
+
+ emit({" %%-------------------------------------------------",nl}),
+ emit({" %% decode tag and length ",nl}),
+ emit({" %%-------------------------------------------------",nl}),
+
+ asn1ct_name:new(rb),
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type('SEQUENCE'),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([" {{_,",asn1ct_gen_ber:unused_var("Len",D#type.def),"},",{next,bytes},",",{curr,rb},
+ "} = ?RT_BER:check_tags(TagIn ++ ",{asis,MyTag},", ",
+ {curr,bytes},", OptOrMand), ",nl]),
+ asn1ct_name:new(bytes),
+ asn1ct_name:new(len),
+
+ case CompList of
+ [] -> true;
+ _ ->
+ emit({"{",{next,bytes},
+ ",RemBytes} = ?RT_BER:split_list(",
+ {curr,bytes},
+ ",", {prev,len},"),",nl}),
+ asn1ct_name:new(bytes)
+ end,
+
+ {DecObjInf,UniqueFName,ValueIndex} =
+ case TableConsInfo of
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValIndex
+ } ->
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_R]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ %%AttributeName = asn1ct_gen:un_hyphen_var(AttrN),
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName,
+ ValIndex}},
+ UniqueFieldName,ValIndex};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName,ValIndex}
+ end;
+ _ ->
+ {false,false,false}
+ end,
+ case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
+ no_terms -> % an empty sequence
+ emit([nl,nl]),
+ demit({"Result = "}), %dbg
+ %% return value as record
+ asn1ct_name:new(rb),
+ emit([" {{'",asn1ct_gen:list2rname(Typename),"'}, ",{curr,bytes},",",nl," "]),
+ asn1ct_gen_ber:add_removed_bytes(),
+ emit(["}.",nl]);
+ {LeadingAttrTerm,PostponedDecArgs} ->
+ emit([com,nl,nl]),
+ case {LeadingAttrTerm,PostponedDecArgs} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],PostponedDecArgs} ->
+ DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
+ ValueMatch = value_match(ValueIndex,Term),
+ emit([DecObj," =",nl," 'getdec_",ObjSet,"'(",
+% {asis,UniqueFName},", ",Term,"),",nl}),
+ {asis,UniqueFName},", ",ValueMatch,"),",nl]),
+ gen_dec_postponed_decs(DecObj,PostponedDecArgs)
+ end,
+ demit({"Result = "}), %dbg
+ %% return value as record
+ asn1ct_name:new(rb),
+ asn1ct_name:new(bytes),
+ ExtStatus = case Ext of
+ {ext,_,_} -> ext;
+ noext -> noext
+ end,
+ emit([" {",{next,bytes},",",{curr,rb},"} = ?RT_BER:restbytes2(RemBytes, ",
+ {curr,bytes},",",ExtStatus,"),",nl]),
+ asn1ct_name:new(rb),
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([" OldFormat={'",asn1ct_gen:list2rname(Typename),
+ "', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["},",nl]),
+ emit([" ASN11994Format =",nl,
+ " asn1rt_check:transform_to_EXTERNAL1994",
+ "(OldFormat),",nl]),
+ emit([" {ASN11994Format,",{next,bytes},", "]);
+ _ ->
+ emit([" {{'",asn1ct_gen:list2rname(Typename),"', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["}, ",{next,bytes},", "])
+ end,
+ asn1ct_gen_ber:add_removed_bytes(),
+ emit(["}.",nl])
+ end.
+
+gen_dec_postponed_decs(_,[]) ->
+ emit(nl);
+gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term,TmpTerm,_Tag,OptOrMand}|Rest]) ->
+% asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+
+ emit({"{",Term,", _, _} = ",nl}),
+ N = case OptOrMand of
+ mandatory -> 0;
+ 'OPTIONAL' ->
+ emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
+ 6;
+ {'DEFAULT',Val} ->
+ emit_opt_or_mand_check(Val,TmpTerm),
+ 6
+ end,
+ emit({indent(N+3),"case (catch ",DecObj,"(",{asis,FirstPFN},
+% ", ",TmpTerm,", ", {asis,Tag},", ",{asis,PFNList},")) of",nl}),
+ ", ",TmpTerm,", [], ",{asis,PFNList},")) of",nl}),
+ emit({indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl}),
+ emit({indent(N+9),"exit({'Type not compatible with table constraint',",
+ {curr,reason},"});",nl}),
+ emit({indent(N+6),{curr,tmpterm}," ->",nl}),
+ emit({indent(N+9),{curr,tmpterm},nl}),
+
+ case OptOrMand of
+ mandatory -> emit([indent(N+3),"end,",nl]);
+ _ ->
+ emit([indent(N+3),"end",nl,
+ indent(3),"end,",nl])
+ end,
+% emit({indent(3),"end,",nl}),
+ gen_dec_postponed_decs(DecObj,Rest).
+
+
+emit_opt_or_mand_check(Value,TmpTerm) ->
+ emit([indent(3),"case ",TmpTerm," of",nl,
+ indent(6),{asis,Value}," -> {",{asis,Value},",[],[]};",nl,
+ indent(6),"_ ->",nl]).
+
+%%============================================================================
+%% Encode/decode SET
+%%
+%%============================================================================
+
+gen_encode_set(Erules,Typename,D) when record(D,type) ->
+ gen_encode_sequence(Erules,Typename,D).
+
+gen_decode_set(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(tag),
+ #'SET'{components=TCompList} = D#type.def,
+ Ext = extensible(TCompList),
+ CompList = case TCompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> TCompList
+ end,
+
+ emit([" %%-------------------------------------------------",nl]),
+ emit([" %% decode tag and length ",nl]),
+ emit([" %%-------------------------------------------------",nl]),
+
+ asn1ct_name:new(rb),
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type('SET'),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([" {{_,Len},",{next,bytes},",",{curr,rb},
+ "} = ?RT_BER:check_tags(TagIn ++ ",{asis,MyTag},", ",
+ {curr,bytes},", OptOrMand), ",nl]),
+ asn1ct_name:new(bytes),
+ asn1ct_name:new(len),
+ asn1ct_name:new(rb),
+
+ emit([" {SetTerm, SetBytes, ",{curr,rb},"} = ?RT_BER:decode_set(0, Len, ",
+ {curr,bytes},", OptOrMand, ",
+ "fun 'dec_",asn1ct_gen:list2name(Typename),"_fun'/2, []),",nl]),
+
+ asn1ct_name:new(rb),
+ emit([" 'dec_",asn1ct_gen:list2name(Typename),"_result'(lists:sort(SetTerm), SetBytes, "]),
+ asn1ct_gen_ber:add_removed_bytes(),
+ emit([").",nl,nl,nl]),
+
+ emit({"%%-------------------------------------------------",nl}),
+ emit({"%% Set loop fun for ",asn1ct_gen:list2name(Typename),nl}),
+ emit({"%%-------------------------------------------------",nl}),
+
+ asn1ct_name:clear(),
+ asn1ct_name:new(term),
+ emit(["'dec_",asn1ct_gen:list2name(Typename),"_fun'(",{curr,bytes},
+ ", OptOrMand) ->",nl]),
+
+ asn1ct_name:new(bytes),
+ gen_dec_set(Erules,Typename,CompList,1,Ext),
+
+ emit([" %% tag not found, if extensionmark we should skip bytes here",nl]),
+ emit([indent(6),"_ -> {[], Bytes,0}",nl]),
+ emit([indent(3),"end.",nl,nl,nl]),
+
+
+ emit({"%%-------------------------------------------------",nl}),
+ emit({"%% Result ",asn1ct_gen:list2name(Typename),nl}),
+ emit({"%%-------------------------------------------------",nl}),
+
+ asn1ct_name:clear(),
+ emit({"'dec_",asn1ct_gen:list2name(Typename),"_result'(",
+ asn1ct_gen_ber:unused_var("TermList",D#type.def),", Bytes, Rb) ->",nl}),
+
+ case gen_dec_set_result(Erules,Typename,CompList) of
+ no_terms ->
+ %% return value as record
+ asn1ct_name:new(rb),
+ emit({" {{'",asn1ct_gen:list2rname(Typename),"'}, Bytes, Rb}.",nl});
+ _ ->
+ emit({nl," case ",{curr,termList}," of",nl}),
+ emit({" [] -> {{'",asn1ct_gen:list2rname(Typename),"', "}),
+ mkvlist(asn1ct_name:all(term)),
+ emit({"}, Bytes, Rb};",nl}),
+ emit({" ExtraAtt -> exit({error,{asn1,{too_many_attributes, ExtraAtt}}})",nl}),
+ emit({" end.",nl}),
+ emit({nl,nl,nl})
+ end.
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE OF and SET OF
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sof(Erules,Typename,_InnerTypename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, Cont} = D#type.def,
+
+ Objfun = case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+
+ emit({" {EncBytes,EncLen} = 'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(Val",Objfun,",[],0),",nl}),
+
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type(SeqOrSetOf),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+% gen_encode_tags(Erules,MyTag,"EncLen","EncBytes"),
+ emit([" ?RT_BER:encode_tags(TagIn ++ ",
+ {asis,MyTag},", EncBytes, EncLen).",nl,nl]),
+
+ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont).
+% gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",0,
+% mandatory,"{EncBytes,EncLen} = "),
+
+
+gen_decode_sof(Erules,Typename,_InnerTypename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, TypeTag, Cont} =
+ case D#type.def of
+ {'SET OF',_Cont} -> {'SET OF','SET',_Cont};
+ {'SEQUENCE OF',_Cont} -> {'SEQUENCE OF','SEQUENCE',_Cont}
+ end,
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+
+ emit({" %%-------------------------------------------------",nl}),
+ emit({" %% decode tag and length ",nl}),
+ emit({" %%-------------------------------------------------",nl}),
+
+ asn1ct_name:new(rb),
+ MyTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- D#type.tag]
+ ++
+ [#tag{class = asn1ct_gen_ber:decode_class('UNIVERSAL'),
+ number = asn1ct_gen_ber:decode_type(TypeTag),
+ form = ?CONSTRUCTED,
+ type = 'IMPLICIT'}],
+ emit([" {{_,Len},",{next,bytes},",",{curr,rb},
+ "} = ?RT_BER:check_tags(TagIn ++ ",{asis,MyTag},", ",
+ {curr,bytes},", OptOrMand), ",nl]),
+
+ emit([" ?RT_BER:decode_components(",{curr,rb}]),
+ InnerType = asn1ct_gen:get_inner(Cont#type.def),
+ ContName = case asn1ct_gen:type(InnerType) of
+ Atom when atom(Atom) -> Atom;
+ _ -> TypeNameSuffix
+ end,
+ emit([", Len, ",{next,bytes},", "]),
+% NewCont =
+% case Cont#type.def of
+% {'ENUMERATED',_,Components}->
+% Cont#type{def={'ENUMERATED',Components}};
+% _ -> Cont
+% end,
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ []
+ end,
+ gen_dec_line_sof(Erules,Typename,ContName,Cont,ObjFun),
+ emit([", []).",nl,nl,nl]).
+
+
+gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
+ when record(Cont,type)->
+
+ {Objfun,ObjFun_novar,EncObj} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _",{no_attr,"ObjFun"}};
+ _ ->
+ {"","",false}
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([]",ObjFun_novar,", AccBytes, AccLen) -> ",nl]),
+
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit([indent(3),
+ "{?RT_BER:dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
+ _ ->
+ emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]),
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+ gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",3,
+ mandatory,"{EncBytes,EncLen} = ",EncObj),
+ emit([",",nl]),
+ emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(T",Objfun,","]),
+ emit(["[EncBytes|AccBytes], AccLen + EncLen).",nl,nl]).
+
+%%============================================================================
+%% Encode/decode CHOICE
+%%
+%%============================================================================
+
+gen_encode_choice(Erules,Typename,D) when record(D,type) ->
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_enc_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit({nl,nl}).
+
+gen_decode_choice(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(bytes),
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_dec_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit({".",nl}).
+
+
+%%============================================================================
+%% Encode SEQUENCE
+%%
+%%============================================================================
+
+gen_enc_sequence_call(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],Pos,Ext,EncObj) ->
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Element =
+ case TopType of
+ ['EXTERNAL'] ->
+ io_lib:format("?RT_BER:cindex(~w,NewVal,~w)",[Pos+1,Cname]);
+ _ ->
+ io_lib:format("?RT_BER:cindex(~w,Val,~w)",[Pos+1,Cname])
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ print_attribute_comment(InnerType,Pos,Prop),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,3,Prop,EncObj),
+ case Rest of
+ [] ->
+ emit({com,nl});
+ _ ->
+ emit({com,nl}),
+ gen_enc_sequence_call(Erules,TopType,Rest,Pos+1,Ext,EncObj)
+ end;
+
+gen_enc_sequence_call(_Erules,_TopType,[],_Num,_,_) ->
+ true.
+
+%%============================================================================
+%% Decode SEQUENCE
+%%
+%%============================================================================
+
+gen_dec_sequence_call(Erules,TopType,CompList,Ext,DecObjInf) ->
+ gen_dec_sequence_call1(Erules,TopType, CompList, 1, Ext,DecObjInf,[],[]).
+
+
+gen_dec_sequence_call1(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop,tags=Tags}|Rest],Num,Ext,DecObjInf,LeadingAttrAcc,ArgsAcc) ->
+ {LA,PostponedDec} =
+ gen_dec_component(Erules,TopType,Cname,Tags,Type,Num,Prop,
+ Ext,DecObjInf),
+ case Rest of
+ [] ->
+ {LA ++ LeadingAttrAcc,PostponedDec ++ ArgsAcc};
+ _ ->
+ emit({com,nl}),
+% asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+ gen_dec_sequence_call1(Erules,TopType,Rest,Num+1,Ext,DecObjInf,
+ LA++LeadingAttrAcc,PostponedDec++ArgsAcc)
+ end;
+
+gen_dec_sequence_call1(_Erules,_TopType,[],1,_,_,_,_) ->
+ no_terms.
+%%gen_dec_sequence_call1(Erules,_TopType,[],Num,_) ->
+%% true.
+
+
+
+%%----------------------------
+%%SEQUENCE mandatory
+%%----------------------------
+
+gen_dec_component(Erules,TopType,Cname,CTags,Type,Pos,Prop,Ext,DecObjInf) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} -> OCFTType;
+ _ -> asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ Prop1 = case {Prop,Ext} of
+ {mandatory,{ext,Epos,_}} when Pos >= Epos ->
+ 'OPTIONAL';
+ _ ->
+ Prop
+ end,
+ print_attribute_comment(InnerType,Pos,Prop1),
+ emit(" "),
+
+ case {InnerType,DecObjInf} of
+ {{typefield,_},NotFalse} when NotFalse /= false ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},",",{next,rb},"} = "});
+ {{objectfield,_,_},_} ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},",",{next,rb},"} = "});
+ _ ->
+ asn1ct_name:new(term),
+ emit({"{",{curr,term},",",{next,bytes},",",{next,rb},"} = "})
+ end,
+ asn1ct_name:new(rb),
+ PostponedDec =
+ gen_dec_line(Erules,TopType,Cname,CTags,Type,Prop1,DecObjInf),
+ asn1ct_name:new(form),
+ PostponedDec.
+
+
+%%-------------------------------------
+%% Decode SET
+%%-------------------------------------
+
+gen_dec_set(Erules,TopType,CompList,Pos,_Ext) ->
+ TagList = get_all_choice_tags(CompList),
+ emit({indent(3),
+ {curr,tagList}," = ",{asis,TagList},",",nl}),
+ emit({indent(3),
+ "case ?RT_BER:check_if_valid_tag(Bytes, ",
+ {curr,tagList},", OptOrMand) of",nl}),
+ asn1ct_name:new(tagList),
+ asn1ct_name:new(rbCho),
+ asn1ct_name:new(choTags),
+ gen_dec_set_cases(Erules,TopType,CompList,TagList,Pos),
+ asn1ct_name:new(tag),
+ asn1ct_name:new(bytes).
+
+
+
+gen_dec_set_cases(_,_,[],_,_) ->
+ ok;
+gen_dec_set_cases(Erules,TopType,[H|T],List,Pos) ->
+ case H of
+ {'EXTENSIONMARK', _, _} ->
+ gen_dec_set_cases(Erules,TopType,T,List,Pos);
+ _ ->
+ Name = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+
+ emit({indent(6),"'",Name,"' ->",nl}),
+ case Type#type.def of
+ {'CHOICE',_NewCompList} ->
+ gen_dec_set_cases_choice(Erules,TopType,H,Pos);
+ _ ->
+ gen_dec_set_cases_type(Erules,TopType,H,Pos)
+ end,
+ gen_dec_set_cases(Erules,TopType,T,List,Pos+1)
+ end.
+
+
+
+
+gen_dec_set_cases_choice(_Erules,TopType,H,Pos) ->
+ Cname = H#'ComponentType'.name,
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- (H#'ComponentType'.typespec)#type.tag],
+ asn1ct_name:new(rbCho),
+ emit({indent(9),"{Dec, Rest, ",{curr,rbCho},"} = "}),
+ emit({"'dec_",asn1ct_gen:list2name([Cname|TopType]),
+ "'(Bytes,OptOrMand,",{asis,Tag},"),",nl}),
+ emit([" {{",Pos,",Dec}, Rest, ",{curr,rbCho},"}"]),
+ emit([";",nl,nl]).
+
+
+gen_dec_set_cases_type(Erules,TopType,H,Pos) ->
+ Cname = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+ %% always use Prop = mandatory here Prop = H#'ComponentType'.prop,
+
+ asn1ct_name:new(rbCho),
+ emit({indent(9),"{Dec, Rest, ",{curr,rbCho},"} = "}),
+ asn1ct_name:delete(bytes),
+ %% we have already seen the tag so now we must find the value
+ %% that why we always use 'mandatory' here
+ gen_dec_line(Erules,TopType,Cname,[],Type,mandatory,decObjInf),
+ asn1ct_name:new(bytes),
+
+ emit([",",nl]),
+ emit(["{{",Pos,",Dec}, Rest, ",{curr,rbCho},"}"]),
+ emit([";",nl,nl]).
+
+
+%%---------------------------------
+%% Decode SET result
+%%---------------------------------
+
+gen_dec_set_result(Erules,TopType,{CompList,_ExtList}) ->
+ gen_dec_set_result1(Erules,TopType, CompList, 1);
+gen_dec_set_result(Erules,TopType,CompList) ->
+ gen_dec_set_result1(Erules,TopType, CompList, 1).
+
+gen_dec_set_result1(Erules,TopType,
+ [#'ComponentType'{name=Cname,
+ typespec=Type,
+ prop=Prop}|Rest],Num) ->
+ gen_dec_set_component(Erules,TopType,Cname,Type,Num,Prop),
+ case Rest of
+ [] ->
+ true;
+ _ ->
+ gen_dec_set_result1(Erules,TopType,Rest,Num+1)
+ end;
+
+gen_dec_set_result1(_Erules,_TopType,[],1) ->
+ no_terms;
+gen_dec_set_result1(_Erules,_TopType,[],_Num) ->
+ true.
+
+
+gen_dec_set_component(_Erules,_TopType,_Cname,Type,Pos,Prop) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ print_attribute_comment(InnerType,Pos,Prop),
+ emit({" {",{next,term},com,{next,termList},"} =",nl}),
+ emit({" case ",{curr,termList}," of",nl}),
+ emit({" [{",Pos,com,{curr,termTmp},"}|",
+ {curr,rest},"] -> "}),
+ emit({"{",{curr,termTmp},com,
+ {curr,rest},"};",nl}),
+ case Prop of
+ 'OPTIONAL' ->
+ emit([indent(10),"_ -> {asn1_NOVALUE, ",{curr,termList},"}",nl]);
+ {'DEFAULT', DefVal} ->
+ emit([indent(10),
+ "_ -> {",{asis,DefVal},", ",{curr,termList},"}",nl]);
+ mandatory ->
+ emit([indent(10),
+ "_ -> exit({error,{asn1,{mandatory_attribute_no, ",
+ Pos,", missing}}})",nl])
+ end,
+ emit([indent(6),"end,",nl]),
+ asn1ct_name:new(rest),
+ asn1ct_name:new(term),
+ asn1ct_name:new(termList),
+ asn1ct_name:new(termTmp).
+
+
+%%---------------------------------------------
+%% Encode CHOICE
+%%---------------------------------------------
+%% for BER we currently do care (a little) if the choice has an EXTENSIONMARKER
+
+
+gen_enc_choice(Erules,TopType,Tag,CompList,_Ext) ->
+ gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext).
+
+gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext) ->
+ asn1ct_name:clear(),
+ emit({" {EncBytes,EncLen} = case element(1,Val) of",nl}),
+ gen_enc_choice2(Erules,TopType,CompList),
+ emit([nl," end,",nl,nl]),
+ NewTag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- Tag],
+% gen_encode_tags(Erules,NewTag,"EncLen","EncBytes").
+ emit(["?RT_BER:encode_tags(TagIn ++",{asis,NewTag},", EncBytes, EncLen).",nl]).
+
+
+
+gen_enc_choice2(Erules,TopType,[H1|T]) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ emit({" ",{asis,Cname}," ->",nl}),
+ {Encobj,Assign} =
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+ case {Type#type.def,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+ {#'ObjectClassFieldType'{},{componentrelation,_,_}} ->
+ asn1ct_name:new(tmpBytes),
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Emit = ["{",{curr,tmpBytes},", _} = "],
+ {{no_attr,"ObjFun"},Emit};
+ _ ->
+ {false,[]}
+ end,
+ gen_enc_line(Erules,TopType,Cname,Type,"element(2,Val)",9,
+ mandatory,Assign,Encobj),
+ case Encobj of
+ false -> ok;
+ _ ->
+ emit({",",nl,indent(9),"{",{curr,encBytes},", ",
+ {curr,encLen},"}"})
+ end,
+ emit({";",nl}),
+ case T of
+ [] ->
+ emit([indent(6), "Else -> ",nl,
+ indent(9),"exit({error,{asn1,{invalid_choice_type,Else}}})"]);
+ _ ->
+ true
+ end,
+ gen_enc_choice2(Erules,TopType,T);
+
+gen_enc_choice2(_,_,[]) ->
+ true.
+
+
+
+
+%%--------------------------------------------
+%% Decode CHOICE
+%%--------------------------------------------
+
+gen_dec_choice(Erules,TopType, ChTag, CompList, Ext) ->
+ asn1ct_name:delete(bytes),
+ Tags = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}|| X <- ChTag],
+
+ emit([" {{_,Len},",{next,bytes},
+ ", RbExp} = ?RT_BER:check_tags(TagIn++",
+ {asis,Tags},", ",
+ {curr,bytes},", OptOrMand),",nl]),
+ asn1ct_name:new(bytes),
+ asn1ct_name:new(len),
+ gen_dec_choice_indef_funs(Erules),
+ case Erules of
+ ber_bin ->
+ emit([indent(3),"case ",{curr,bytes}," of",nl]);
+ ber ->
+ emit([indent(3),
+ "case (catch ?RT_BER:peek_tag(",{curr,bytes},")) of",nl])
+ end,
+ asn1ct_name:new(tagList),
+ asn1ct_name:new(choTags),
+ gen_dec_choice_cases(Erules,TopType,CompList),
+ case Ext of
+ noext ->
+ emit([indent(6), {curr,else}," -> ",nl]),
+ emit([indent(9),"case OptOrMand of",nl,
+ indent(12),"mandatory ->","exit({error,{asn1,",
+ "{invalid_choice_tag,",{curr,else},"}}});",nl,
+ indent(12),"_ ->","exit({error,{asn1,{no_optional_tag,",
+ {curr,else},"}}})",nl,
+ indent(9),"end",nl]);
+ _ ->
+ emit([indent(6),"_ -> ",nl]),
+ emit([indent(9),"{{asn1_ExtAlt,",{curr,bytes},"},",
+ empty_lb(Erules),", RbExp}",nl])
+ end,
+ emit([indent(3),"end"]),
+ asn1ct_name:new(tag),
+ asn1ct_name:new(else).
+
+gen_dec_choice_indef_funs(Erules) ->
+ emit({indent(3),"IndefEndBytes = fun(indefinite,",indefend_match(Erules,used_var),
+ ")-> R; (_,B)-> B end,",nl}),
+ emit({indent(3),"IndefEndRb = fun(indefinite,",indefend_match(Erules,unused_var),
+ ")-> 2; (_,_)-> 0 end,",nl}).
+
+
+gen_dec_choice_cases(_,_, []) ->
+ ok;
+gen_dec_choice_cases(Erules,TopType, [H|T]) ->
+ asn1ct_name:push(rbCho),
+ Name = H#'ComponentType'.name,
+ emit([nl,"%% '",Name,"'",nl]),
+ Fcases = fun([T1,T2|Tail],Fun) ->
+ emit([indent(6),match_tag(Erules,T1)," ->",nl]),
+ gen_dec_choice_cases_type(Erules,TopType, H),
+ Fun([T2|Tail],Fun);
+ ([T1],_) ->
+ emit([indent(6),match_tag(Erules,T1)," ->",nl]),
+ gen_dec_choice_cases_type(Erules,TopType, H)
+ end,
+ Fcases(H#'ComponentType'.tags,Fcases),
+ asn1ct_name:pop(rbCho),
+ gen_dec_choice_cases(Erules,TopType, T).
+
+
+
+gen_dec_choice_cases_type(Erules,TopType,H) ->
+ Cname = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+ Prop = H#'ComponentType'.prop,
+ emit({indent(9),"{Dec, Rest, ",{curr,rbCho},"} = "}),
+ gen_dec_line(Erules,TopType,Cname,[],Type,Prop,false),
+ emit([",",nl,indent(9),"{{",{asis,Cname},
+ ", Dec}, IndefEndBytes(Len,Rest), RbExp + ",
+ {curr,rbCho}," + IndefEndRb(Len,Rest)};",nl,nl]).
+
+encode_tag_val(Erules,{Class,TagNo}) when integer(TagNo) ->
+ Rtmod = rtmod(Erules),
+ Rtmod:encode_tag_val({asn1ct_gen_ber:decode_class(Class),
+ 0,TagNo});
+encode_tag_val(Erules,{Class,TypeName}) ->
+ Rtmod = rtmod(Erules),
+ Rtmod:encode_tag_val({asn1ct_gen_ber:decode_class(Class),
+ 0,asn1ct_gen_ber:decode_type(TypeName)}).
+
+
+match_tag(ber_bin,Arg) ->
+ match_tag_with_bitsyntax(Arg);
+match_tag(Erules,Arg) ->
+ io_lib:format("~p",[encode_tag_val(Erules,Arg)]).
+
+match_tag_with_bitsyntax({Class,TagNo}) when integer(TagNo) ->
+ match_tag_with_bitsyntax1({asn1ct_gen_ber:decode_class(Class),
+ 0,TagNo});
+match_tag_with_bitsyntax({Class,TypeName}) ->
+ match_tag_with_bitsyntax1({asn1ct_gen_ber:decode_class(Class),
+ 0,asn1ct_gen_ber:decode_type(TypeName)}).
+
+match_tag_with_bitsyntax1({Class, _Form, TagNo}) when (TagNo =< 30) ->
+ io_lib:format("<<~p:2,_:1,~p:5,_/binary>>",[Class bsr 6,TagNo]);
+
+match_tag_with_bitsyntax1({Class, _Form, TagNo}) ->
+ {Octets,Len} = mk_object_val(TagNo),
+ OctForm = case Len of
+ 1 -> "~p";
+ 2 -> "~p,~p";
+ 3 -> "~p,~p,~p";
+ 4 -> "~p,~p,~p,~p"
+ end,
+ io_lib:format("<<~p:2,_:1,31:5," ++ OctForm ++ ",_/binary>>",
+ [Class bsr 6] ++ Octets).
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+get_all_choice_tags(ComponentTypeList) ->
+ get_all_choice_tags(ComponentTypeList,[]).
+
+get_all_choice_tags([],TagList) ->
+ TagList;
+get_all_choice_tags([H|T],TagList) ->
+ Tags = H#'ComponentType'.tags,
+ get_all_choice_tags(T, TagList ++ [{H#'ComponentType'.name, Tags}]).
+
+
+
+%%---------------------------------------
+%% Generate the encode/decode code
+%%---------------------------------------
+
+gen_enc_line(Erules,TopType,Cname,
+ Type=#type{constraint=[{componentrelation,_,_}],
+ def=#'ObjectClassFieldType'{type={typefield,_}}},
+ Element,Indent,OptOrMand=mandatory,EncObj)
+ when list(Element) ->
+ asn1ct_name:new(tmpBytes),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,tmpBytes},",_} = "],EncObj);
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj)
+ when list(Element) ->
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,encBytes},",",{curr,encLen},"} = "],EncObj).
+
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
+ when list(Element) ->
+ IndDeep = indent(Indent),
+
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- Type#type.tag],
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ emit(IndDeep),
+ emit(Assign),
+ gen_optormand_case(OptOrMand,Erules,TopType,Cname,Type,InnerType,WhatKind,
+ Element),
+ case {Type,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+% #type{constraint=[{tableconstraint_info,RefedFieldName}],
+% def={typefield,_}} ->
+ {#type{def=#'ObjectClassFieldType'{type={typefield,_},
+ fieldname=RefedFieldName}},
+ {componentrelation,_,_}} ->
+ {_LeadingAttrName,Fun} = EncObj,
+ case RefedFieldName of
+ {notype,T} ->
+ throw({error,{notype,type_from_object,T}});
+ {Name,RestFieldNames} when atom(Name) ->
+ case OptOrMand of
+ mandatory -> ok;
+ _ ->
+% emit(["{",{curr,tmpBytes},",",{curr,tmpLen},
+ emit(["{",{curr,tmpBytes},", _} = "])
+%% asn1ct_name:new(tmpBytes),
+%% asn1ct_name:new(tmpLen)
+ end,
+ emit({Fun,"(",{asis,Name},", ",Element,", [], ",
+ {asis,RestFieldNames},"),",nl}),
+ emit(IndDeep),
+ case OptOrMand of
+ mandatory ->
+ emit({"{",{curr,encBytes},", ",{curr,encLen},"} = "}),
+ emit({"?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},")"});
+ _ ->
+% emit({"{",{next,tmpBytes},", _} = "}),
+ emit({"{",{next,tmpBytes},", ",{curr,tmpLen},
+ "} = "}),
+ emit({"?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},"),",nl}),
+ emit(IndDeep),
+ emit({"{",{next,tmpBytes},", ",{curr,tmpLen},"}"})
+ end;
+ _ ->
+ throw({asn1,{'internal error'}})
+ end;
+% #type{constraint=[{tableconstraint_info,_}],
+% def={objectfield,PrimFieldName1,PFNList}} ->
+ {{#'ObjectClassFieldType'{type={objectfield,PrimFieldName1,
+ PFNList}},_},
+ {componentrelation,_,_}} ->
+ %% this is when the dotted list in the FieldName has more
+ %% than one element
+ {_LeadingAttrName,Fun} = EncObj,
+ emit({"?RT_BER:encode_open_type(",Fun,"(",{asis,PrimFieldName1},
+ ", ",Element,", ",{asis,PFNList},"),",{asis,Tag},")"});
+ _ ->
+ case WhatKind of
+ {primitive,bif} ->
+ EncType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{
+ type={fixedtypevaluefield,
+ _,Btype}} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ asn1ct_gen_ber:gen_encode_prim(ber,EncType,{asis,Tag},
+ Element);
+ {notype,_} ->
+ emit({"'enc_",InnerType,"'(",Element,", ",{asis,Tag},")"});
+ 'ASN1_OPEN_TYPE' ->
+ asn1ct_gen_ber:gen_encode_prim(ber,Type#type{def='ASN1_OPEN_TYPE'},{asis,Tag},Element);
+ _ ->
+ {EncFunName, _, _} =
+ mkfuncname(TopType,Cname,WhatKind,enc),
+ case {WhatKind,Type#type.tablecinf,EncObj} of
+ {{constructed,bif},[{objfun,_}|_R],{_,Fun}} ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},
+ ", ",Fun,")"]);
+ _ ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},")"])
+ end
+ end
+ end,
+ case OptOrMand of
+ mandatory -> true;
+ _ ->
+ emit({nl,indent(7),"end"})
+ end.
+
+
+
+gen_optormand_case(mandatory,_,_,_,_,_,_, _) ->
+ ok;
+gen_optormand_case('OPTIONAL',Erules,_,_,_,_,_,Element) ->
+ emit({" case ",Element," of",nl}),
+ emit({indent(9),"asn1_NOVALUE -> {",
+ empty_lb(Erules),",0};",nl}),
+ emit({indent(9),"_ ->",nl,indent(12)});
+gen_optormand_case({'DEFAULT',DefaultValue},Erules,TopType,Cname,Type,
+ InnerType,WhatKind,Element) ->
+ CurrMod = get(currmod),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit(" case catch "),
+ asn1ct_gen:gen_check_call(TopType,Cname,Type,InnerType,
+ WhatKind,{asis,DefaultValue},
+ Element),
+ emit({" of",nl}),
+ emit({indent(12),"true -> {[],0};",nl});
+ _ ->
+ emit({" case ",Element," of",nl}),
+ emit({indent(9),"asn1_DEFAULT -> {",
+ empty_lb(Erules),
+ ",0};",nl}),
+ case DefaultValue of
+ #'Externalvaluereference'{module=CurrMod,
+ value=V} ->
+ emit({indent(9),"?",{asis,V}," -> {",
+ empty_lb(Erules),",0};",nl});
+ _ ->
+ emit({indent(9),{asis,
+ DefaultValue}," -> {",
+ empty_lb(Erules),",0};",nl})
+ end
+ end,
+ emit({indent(9),"_ ->",nl,indent(12)}).
+
+
+
+
+gen_dec_line_sof(_Erules,TopType,Cname,Type,ObjFun) ->
+
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- Type#type.tag],
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ case WhatKind of
+ {primitive,bif} ->
+ asn1ct_name:delete(len),
+
+ asn1ct_name:new(len),
+ emit(["fun(FBytes,_,_)->",nl]),
+ EncType = case Type#type.def of
+ #'ObjectClassFieldType'{
+ type={fixedtypevaluefield,
+ _,Btype}} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ asn1ct_gen_ber:gen_dec_prim(ber,EncType,"FBytes",Tag,
+ [],no_length,?PRIMITIVE,
+ mandatory),
+ emit([nl,"end, []"]);
+ _ ->
+ case ObjFun of
+ [] ->
+ {DecFunName, _, _} =
+ mkfunname(TopType,Cname,WhatKind,dec,3),
+ emit([DecFunName,", ",{asis,Tag}]);
+ _ ->
+ {DecFunName, _, _} =
+ mkfunname(TopType,Cname,WhatKind,dec,4),
+ emit([DecFunName,", ",{asis,Tag},", ObjFun"])
+ end
+ end.
+
+
+gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ Tag = [X#tag{class=asn1ct_gen_ber:decode_class(X#tag.class)}
+ || X <- Type#type.tag],
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+ PostpDec =
+ case OptOrMand of
+ mandatory ->
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,mandatory,", mandatory, ",
+ DecObjInf,OptOrMand);
+ _ -> %optional or default
+ case {CTags,Erules} of
+ {[CTag],ber_bin} ->
+ emit(["case ",{curr,bytes}," of",nl]),
+ emit([match_tag(Erules,CTag)," ->",nl]),
+ PostponedDec =
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,mandatory,
+ ", opt_or_default, ",DecObjInf,
+ OptOrMand),
+ emit([";",nl]),
+ emit(["_ ->",nl]),
+ case OptOrMand of
+ {'DEFAULT', Def} ->
+ emit(["{",{asis,Def},",",
+ BytesVar,", 0 }",nl]);
+ 'OPTIONAL' ->
+ emit(["{ asn1_NOVALUE, ",
+ BytesVar,", 0 }",nl])
+ end,
+ emit("end"),
+ PostponedDec;
+ _ ->
+ emit("case (catch "),
+ PostponedDec =
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,OptOrMand,
+ ", opt_or_default, ",DecObjInf,
+ OptOrMand),
+ emit([") of",nl]),
+ case OptOrMand of
+ {'DEFAULT', Def} ->
+ emit(["{'EXIT',{error,{asn1,{no_optional_tag,_}}}}",
+ " -> {",{asis,Def},",",
+ BytesVar,", 0 };",nl]);
+ 'OPTIONAL' ->
+ emit(["{'EXIT',{error,{asn1,{no_optional_tag,_}}}}",
+ " -> { asn1_NOVALUE, ",
+ BytesVar,", 0 };",nl])
+ end,
+ asn1ct_name:new(casetmp),
+ emit([{curr,casetmp},"-> ",{curr,casetmp},nl,"end"]),
+ PostponedDec
+ end
+ end,
+ case DecObjInf of
+ {Cname,ObjSet} -> % this must be the component were an object is
+ %% choosen from the object set according to the table
+ %% constraint.
+ {[{ObjSet,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ PostpDec};
+ _ -> {[],PostpDec}
+ end.
+
+
+gen_dec_call({typefield,_},Erules,_,_,Type,_,Tag,_,_,false,_) ->
+ %% this in case of a choice with typefield components
+ asn1ct_name:new(reason),
+ {FirstPFName,RestPFName} =
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ emit([nl,indent(6),"begin",nl]),
+ emit([indent(9),"{OpenDec,TmpRest,TmpRbCho} =",nl,indent(12),
+ "?RT_BER:decode_open_type(",Erules,",",{curr,bytes},",",
+ {asis,Tag},"),",nl]),
+ emit([indent(9),"case (catch ObjFun(",{asis,FirstPFName},
+ ", OpenDec, [], ",{asis,RestPFName},
+ ")) of", nl]),%% ??? What about Tag
+ emit([indent(12),"{'EXIT',",{curr,reason},"} ->",nl]),
+%% emit({indent(15),"throw({runtime_error,{'Type not ",
+%% "compatible with tableconstraint', OpenDec}});",nl}),
+ emit([indent(15),"exit({'Type not ",
+ "compatible with table constraint', ",{curr,reason},"});",nl]),
+ emit([indent(12),"{TmpDec,_ ,_} ->",nl]),
+ emit([indent(15),"{TmpDec, TmpRest, TmpRbCho}",nl]),
+ emit([indent(9),"end",nl,indent(6),"end",nl]),
+ [];
+gen_dec_call({typefield,_},_Erules,_,Cname,Type,_BytesVar,Tag,_,_,
+ _DecObjInf,OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",{curr,bytes},",",{asis,Tag},")"]),
+ RefedFieldName =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ [{Cname,RefedFieldName,
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+% asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),[],OptOrMandComp}];
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call({objectfield,PrimFieldName,PFNList},_Erules,_,Cname,_,_,Tag,_,_,_,
+ OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",{curr,bytes},",",{asis,Tag},")"]),
+ [{Cname,{PrimFieldName,PFNList},
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+% asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),[],OptOrMandComp}];
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand,
+ OptOrMand,DecObjInf,_) ->
+ WhatKind = asn1ct_gen:type(InnerType),
+ gen_dec_call1(WhatKind,InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,
+ PrimOptOrMand,OptOrMand),
+ case DecObjInf of
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ emit({",",nl,"ObjFun = 'getdec_",OSet,"'(",
+% {asis,UniqueFName},", ",{curr,term},")"});
+ {asis,UniqueFName},", ",ValueMatch,")"});
+ _ ->
+ ok
+ end,
+ [].
+gen_dec_call1({primitive,bif},InnerType,Erules,_,_,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ case InnerType of
+ {fixedtypevaluefield,_,Btype} ->
+ asn1ct_gen_ber:gen_dec_prim(Erules,Btype,BytesVar,Tag,[],no_length,
+ ?PRIMITIVE,OptOrMand);
+ _ ->
+ asn1ct_gen_ber:gen_dec_prim(Erules,Type,BytesVar,Tag,[],no_length,
+ ?PRIMITIVE,OptOrMand)
+ end;
+gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,_,_,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ asn1ct_gen_ber:gen_dec_prim(Erules,Type#type{def='ASN1_OPEN_TYPE'},
+ BytesVar,Tag,[],no_length,
+ ?PRIMITIVE,OptOrMand);
+gen_dec_call1(WhatKind,_,_Erules,TopType,Cname,Type,_,Tag,_,OptOrMand) ->
+ {DecFunName,_,_} =
+ mkfuncname(TopType,Cname,WhatKind,dec),
+ case {WhatKind,Type#type.tablecinf} of
+ {{constructed,bif},[{objfun,_}|_R]} ->
+ emit({DecFunName,"(",{curr,bytes},OptOrMand,{asis,Tag},", ObjFun)"});
+ _ ->
+ emit({DecFunName,"(",{curr,bytes},OptOrMand,{asis,Tag},")"})
+ end.
+
+
+%%------------------------------------------------------
+%% General and special help functions (not exported)
+%%------------------------------------------------------
+
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
+ emit([{var,H},Sep]),
+ mkvlist([T1|T], Sep);
+mkvlist([H|T], Sep) ->
+ emit([{var,H}]),
+ mkvlist(T, Sep);
+mkvlist([], _) ->
+ true.
+
+mkvlist(L) ->
+ mkvlist(L,", ").
+
+mkvplus(L) ->
+ mkvlist(L," + ").
+
+extensible(CompList) when list(CompList) ->
+ noext;
+extensible({RootList,ExtList}) ->
+ {ext,length(RootList)+1,length(ExtList)}.
+
+
+print_attribute_comment(InnerType,Pos,Prop) ->
+ CommentLine = "%%-------------------------------------------------",
+ emit([nl,CommentLine]),
+ case InnerType of
+ {typereference,_,Name} ->
+ emit([nl,"%% attribute number ",Pos," with type ",Name]);
+ {'Externaltypereference',_,XModule,Name} ->
+ emit([nl,"%% attribute number ",Pos," External ",XModule,":",Name]);
+ _ ->
+ emit([nl,"%% attribute number ",Pos," with type ",InnerType])
+ end,
+ case Prop of
+ mandatory ->
+ continue;
+ {'DEFAULT', Def} ->
+ emit([" DEFAULT = ",{asis,Def}]);
+ 'OPTIONAL' ->
+ emit([" OPTIONAL"])
+ end,
+ emit([nl,CommentLine,nl]).
+
+
+mkfuncname(TopType,Cname,WhatKind,DecOrEnc) ->
+ CurrMod = get(currmod),
+ case WhatKind of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ F = lists:concat(["'",DecOrEnc,"_",EType,"'"]),
+ {F, "?MODULE", F};
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ {lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"]),Mod,
+ lists:concat(["'",DecOrEnc,"_",EType,"'"])};
+ {constructed,bif} ->
+ F = lists:concat(["'",DecOrEnc,"_",asn1ct_gen:list2name([Cname|TopType]),"'"]),
+ {F, "?MODULE", F}
+ end.
+
+mkfunname(TopType,Cname,WhatKind,DecOrEnc,Arity) ->
+ CurrMod = get(currmod),
+ case WhatKind of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ F = lists:concat(["fun '",DecOrEnc,"_",EType,"'/",Arity]),
+ {F, "?MODULE", F};
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ {lists:concat(["{'",Mod,"','",DecOrEnc,"_",EType,"'}"]),Mod,
+ lists:concat(["'",DecOrEnc,"_",EType,"'"])};
+ {constructed,bif} ->
+ F =
+ lists:concat(["fun '",DecOrEnc,"_",
+ asn1ct_gen:list2name([Cname|TopType]),"'/",
+ Arity]),
+ {F, "?MODULE", F}
+ end.
+
+empty_lb(ber) ->
+ "[]";
+empty_lb(ber_bin) ->
+ "<<>>".
+
+rtmod(ber) ->
+ list_to_atom(?RT_BER);
+rtmod(ber_bin) ->
+ list_to_atom(?RT_BER_BIN).
+
+indefend_match(ber,used_var) ->
+ "[0,0|R]";
+indefend_match(ber,unused_var) ->
+ "[0,0|_R]";
+indefend_match(ber_bin,used_var) ->
+ "<<0,0,R/binary>>";
+indefend_match(ber_bin,unused_var) ->
+ "<<0,0,_R/binary>>".
+
+notice_value_match() ->
+ Module = get(currmod),
+ put(value_match,{true,Module}).
+
+value_match(Index,Value) when atom(Value) ->
+ value_match(Index,atom_to_list(Value));
+value_match([],Value) ->
+ Value;
+value_match([{VI,_Cname}|VIs],Value) ->
+ value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
+value_match1(Value,[],Acc,Depth) ->
+ Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
+value_match1(Value,[{VI,_Cname}|VIs],Acc,Depth) ->
+ value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl
new file mode 100644
index 0000000000..0684ffa084
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_ber_bin_v2.erl
@@ -0,0 +1,1357 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_constructed_ber_bin_v2.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_constructed_ber_bin_v2).
+
+-export([gen_encode_sequence/3]).
+-export([gen_decode_sequence/3]).
+-export([gen_encode_set/3]).
+-export([gen_decode_set/3]).
+-export([gen_encode_sof/4]).
+-export([gen_decode_sof/4]).
+-export([gen_encode_choice/3]).
+-export([gen_decode_choice/3]).
+
+
+-include("asn1_records.hrl").
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_constructed_ber,[match_tag/2]).
+
+-define(ASN1CT_GEN_BER,asn1ct_gen_ber_bin_v2).
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE (and SET)
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+
+ %% if EXTERNAL type the input value must be transformed to
+ %% ASN1 1990 format
+ ValName =
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([indent(4),
+ "NewVal = asn1rt_check:transform_to_EXTERNAL1990(Val),",
+ nl]),
+ "NewVal";
+ _ ->
+ "Val"
+ end,
+
+ {SeqOrSet,TableConsInfo,CompList} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {'SEQUENCE',TCI,CL};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {'SET',TCI,CL}
+ end,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+
+%% don't match recordname for now, because of compatibility reasons
+%% emit(["{'",asn1ct_gen:list2rname(Typename),"'"]),
+ emit(["{_"]),
+ case length(CompList1) of
+ 0 ->
+ true;
+ CompListLen ->
+ emit([","]),
+ mkcindexlist([Tc || Tc <- lists:seq(1,CompListLen)])
+ end,
+ emit(["} = ",ValName,",",nl]),
+ EncObj =
+ case TableConsInfo of
+ #simpletableattributes{usedclassfield=Used,
+ uniqueclassfield=Unique} when Used /= Unique ->
+ false;
+ %% ObjectSet, name of the object set in constraints
+ %%
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex} -> %% N is index of attribute that determines constraint
+ OSDef =
+ case ObjectSet of
+ {Module,OSName} ->
+ asn1_db:dbget(Module,OSName);
+ OSName ->
+ asn1_db:dbget(get(currmod),OSName)
+ end,
+% io:format("currmod: ~p~nOSName: ~p~nAttrN: ~p~nN: ~p~nUniqueFieldName: ~p~n",
+% [get(currmod),OSName,AttrN,N,UniqueFieldName]),
+ case (OSDef#typedef.typespec)#'ObjectSet'.gen of
+ true ->
+ ObjectEncode =
+ asn1ct_gen:un_hyphen_var(lists:concat(['Obj',
+ AttrN])),
+ emit([ObjectEncode," = ",nl]),
+ emit([" 'getenc_",ObjectSet,"'(",{asis,UniqueFieldName},
+ ", ",nl]),
+ ValueMatch = value_match(ValueIndex,
+ lists:concat(["Cindex",N])),
+ emit([indent(35),ValueMatch,"),",nl]),
+ {AttrN,ObjectEncode};
+ _ ->
+ false
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ %% when the simpletableattributes was at an outer
+ %% level and the objfun has been passed through the
+ %% function call
+ {"got objfun through args","ObjFun"};
+ _ ->
+ false
+ end
+ end,
+
+ gen_enc_sequence_call(Erules,Typename,CompList1,1,Ext,EncObj),
+
+ emit([nl," BytesSoFar = "]),
+ case SeqOrSet of
+ 'SET' when (D#type.def)#'SET'.sorted == dynamic ->
+ emit("?RT_BER:dynamicsort_SET_components(["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["]),",nl]);
+ _ ->
+ emit("["),
+ mkvlist(asn1ct_name:all(encBytes)),
+ emit(["],",nl])
+ end,
+ emit("LenSoFar = "),
+ case asn1ct_name:all(encLen) of
+ [] -> emit("0");
+ AllLengths ->
+ mkvplus(AllLengths)
+ end,
+ emit([",",nl]),
+ emit(["?RT_BER:encode_tags(TagIn, BytesSoFar, LenSoFar)."
+ ,nl]).
+
+gen_decode_sequence(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(tag),
+ #'SEQUENCE'{tablecinf=TableConsInfo,components=CList} = D#type.def,
+ Ext = extensible(CList),
+ CompList = case CList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CList
+ end,
+
+ emit([" %%-------------------------------------------------",nl]),
+ emit([" %% decode tag and length ",nl]),
+ emit([" %%-------------------------------------------------",nl]),
+
+ asn1ct_name:new(tlv),
+ case CompList of
+ EmptyCL when EmptyCL == [];EmptyCL == {[],[]}-> % empty sequence
+ true;
+ _ ->
+ emit([{curr,tlv}," = "])
+ end,
+ emit(["?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(tlv),
+ asn1ct_name:new(v),
+
+ {DecObjInf,UniqueFName,ValueIndex} =
+ case TableConsInfo of
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValIndex} ->
+% {ObjectSet,AttrN,_N,UniqueFieldName} ->%% N is index of attribute that determines constraint
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName,ValIndex}},
+ UniqueFieldName,ValIndex};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName,ValIndex}
+ end;
+ _ ->
+% case D#type.tablecinf of
+% [{objfun,_}|_] ->
+% {{"got objfun through args","ObjFun"},false,false};
+% _ ->
+ {false,false,false}
+% end
+ end,
+ case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
+ no_terms -> % an empty sequence
+ emit([nl,nl]),
+ demit(["Result = "]), %dbg
+ %% return value as record
+ asn1ct_name:new(rb),
+ emit([" {'",asn1ct_gen:list2rname(Typename),"'}.",nl,nl]);
+ {LeadingAttrTerm,PostponedDecArgs} ->
+ emit([com,nl,nl]),
+ case {LeadingAttrTerm,PostponedDecArgs} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],PostponedDecArgs} ->
+ DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
+ ValueMatch = value_match(ValueIndex,Term),
+ emit([DecObj," =",nl," 'getdec_",ObjSet,"'(",
+ {asis,UniqueFName},", ",ValueMatch,"),",nl]),
+ gen_dec_postponed_decs(DecObj,PostponedDecArgs)
+ end,
+ demit(["Result = "]), %dbg
+ %% return value as record
+ case Ext of
+ {ext,_,_} ->
+ emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
+ noext ->
+ emit(["case ",{prev,tlv}," of",nl,
+ "[] -> true;",
+ "_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
+ "}}}) % extra fields not allowed",nl,
+ "end,",nl])
+ end,
+ asn1ct_name:new(rb),
+ case Typename of
+ ['EXTERNAL'] ->
+ emit([" OldFormat={'",asn1ct_gen:list2rname(Typename),
+ "', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["},",nl]),
+ emit([" asn1rt_check:transform_to_EXTERNAL1994",
+ "(OldFormat).",nl]);
+ _ ->
+ emit([" {'",asn1ct_gen:list2rname(Typename),"', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["}.",nl,nl])
+ end
+ end.
+
+gen_dec_postponed_decs(_,[]) ->
+ emit(nl);
+gen_dec_postponed_decs(DecObj,[{_Cname,{FirstPFN,PFNList},Term,
+ TmpTerm,_Tag,OptOrMand}|Rest]) ->
+
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+ asn1ct_name:new(tmptlv),
+
+ emit([Term," = ",nl]),
+ N = case OptOrMand of
+ mandatory -> 0;
+ 'OPTIONAL' ->
+ emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
+ 6;
+ {'DEFAULT',Val} ->
+ emit_opt_or_mand_check(Val,TmpTerm),
+ 6
+ end,
+ emit([indent(N+3),"case (catch ",DecObj,"(",{asis,FirstPFN},
+ ", ",TmpTerm,", ",{asis,PFNList},")) of",nl]),
+ emit([indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl]),
+ emit([indent(N+9),"exit({'Type not compatible with table constraint',",
+ {curr,reason},"});",nl]),
+ emit([indent(N+6),{curr,tmpterm}," ->",nl]),
+ emit([indent(N+9),{curr,tmpterm},nl]),
+
+ case OptOrMand of
+ mandatory -> emit([indent(N+3),"end,",nl]);
+ _ ->
+ emit([indent(N+3),"end",nl,
+ indent(3),"end,",nl])
+ end,
+ gen_dec_postponed_decs(DecObj,Rest).
+
+emit_opt_or_mand_check(Value,TmpTerm) ->
+ emit([indent(3),"case ",TmpTerm," of",nl,
+ indent(6),{asis,Value}," ->",{asis,Value},";",nl,
+ indent(6),"_ ->",nl]).
+
+%%============================================================================
+%% Encode/decode SET
+%%
+%%============================================================================
+
+gen_encode_set(Erules,Typename,D) when record(D,type) ->
+ gen_encode_sequence(Erules,Typename,D).
+
+gen_decode_set(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(tag),
+ #'SET'{tablecinf=TableConsInfo,components=TCompList} = D#type.def,
+ Ext = extensible(TCompList),
+ CompList = case TCompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> TCompList
+ end,
+
+ asn1ct_name:clear(),
+ asn1ct_name:new(tlv),
+ case CompList of
+ EmptyCL when EmptyCL == [];EmptyCL == {[],[]}-> % empty sequence
+ true;
+ _ ->
+ emit([{curr,tlv}," = "])
+ end,
+ emit(["?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(v),
+
+
+ {DecObjInf,UniqueFName} =
+ case TableConsInfo of
+ {ObjectSet,AttrN,_N,UniqueFieldName} ->%% N is index of attribute that determines constraint
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName}},
+ UniqueFieldName};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName}
+ end;
+ _ ->
+ {false,false}
+ end,
+
+ case CompList of
+ [] -> % empty set
+ true;
+ _ ->
+ emit(["SetFun = fun(FunTlv) ->", nl]),
+ emit(["case FunTlv of ",nl]),
+ NextNum = gen_dec_set_cases(Erules,Typename,CompList,1),
+ emit([indent(6), {curr,else}," -> ",nl,
+ indent(9),"{",NextNum,", ",{curr,else},"}",nl]),
+ emit([indent(3),"end",nl]),
+ emit([indent(3),"end,",nl]),
+
+ emit(["PositionList = [SetFun(TempTlv)|| TempTlv <- ",{curr,tlv},"],",nl]),
+ asn1ct_name:new(tlv),
+ emit([{curr,tlv}," = [Stlv || {_,Stlv} <- lists:sort(PositionList)],",nl]),
+ asn1ct_name:new(tlv)
+
+ end,
+ case gen_dec_sequence_call(Erules,Typename,CompList,Ext,DecObjInf) of
+ no_terms -> % an empty sequence
+ emit([nl,nl]),
+ demit(["Result = "]), %dbg
+ %% return value as record
+ emit([" {'",asn1ct_gen:list2rname(Typename),"'}.",nl]);
+ {LeadingAttrTerm,PostponedDecArgs} ->
+ emit([com,nl,nl]),
+ case {LeadingAttrTerm,PostponedDecArgs} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],PostponedDecArgs} ->
+ DecObj = lists:concat(['DecObj',LeadingAttr,Term]),
+ emit([DecObj," =",nl," 'getdec_",ObjSet,"'(",
+ {asis,UniqueFName},", ",Term,"),",nl]),
+ gen_dec_postponed_decs(DecObj,PostponedDecArgs)
+ end,
+ demit(["Result = "]), %dbg
+ %% return value as record
+ case Ext of
+ {ext,_,_} ->
+ emit(["case ",{prev,tlv}," of [] -> true; _ -> true end, % ... extra fields skipped",nl]);
+ noext ->
+ emit(["case ",{prev,tlv}," of",nl,
+ "[] -> true;",
+ "_ -> exit({error,{asn1, {unexpected,",{prev,tlv},
+ "}}}) % extra fields not allowed",nl,
+ "end,",nl])
+ end,
+ emit([" {'",asn1ct_gen:list2rname(Typename),"', "]),
+ mkvlist(asn1ct_name:all(term)),
+ emit(["}.",nl])
+ end.
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Encode/decode SEQUENCE OF and SET OF
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+gen_encode_sof(Erules,Typename,_InnerTypename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, Cont} = D#type.def,
+
+ Objfun = case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+
+ emit([" {EncBytes,EncLen} = 'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(Val",Objfun,",[],0),",nl]),
+
+ emit([" ?RT_BER:encode_tags(TagIn, EncBytes, EncLen).",nl,nl]),
+
+ gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont).
+
+
+gen_decode_sof(Erules,TypeName,_InnerTypeName,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {SeqOrSetOf, _TypeTag, Cont} =
+ case D#type.def of
+ {'SET OF',_Cont} -> {'SET OF','SET',_Cont};
+ {'SEQUENCE OF',_Cont} -> {'SEQUENCE OF','SEQUENCE',_Cont}
+ end,
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+
+ emit([" %%-------------------------------------------------",nl]),
+ emit([" %% decode tag and length ",nl]),
+ emit([" %%-------------------------------------------------",nl]),
+
+ asn1ct_name:new(tlv),
+ emit([{curr,tlv},
+ " = ?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(v),
+
+ emit(["["]),
+
+ InnerType = asn1ct_gen:get_inner(Cont#type.def),
+ ContName = case asn1ct_gen:type(InnerType) of
+ Atom when atom(Atom) -> Atom;
+ _ -> TypeNameSuffix
+ end,
+%% fix me
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ []
+ end,
+ gen_dec_line(Erules,TypeName,ContName,[],Cont,mandatory,ObjFun),
+ %% gen_dec_line_sof(Erules,Typename,ContName,Cont,ObjFun),
+ emit([" || ",{curr,v}," <- ",{curr,tlv},"].",nl,nl,nl]).
+
+
+gen_encode_sof_components(Erules,Typename,SeqOrSetOf,Cont)
+ when record(Cont,type)->
+
+ {Objfun,Objfun_novar,EncObj} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _",{no_attr,"ObjFun"}};
+ _ ->
+ {"","",false}
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([]",Objfun_novar,", AccBytes, AccLen) -> ",nl]),
+
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit([indent(3),
+ "{?RT_BER:dynamicsort_SETOF(AccBytes),AccLen};",nl,nl]);
+ _ ->
+ emit([indent(3),"{lists:reverse(AccBytes),AccLen};",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "_components'([H|T]",Objfun,",AccBytes, AccLen) ->",nl]),
+ TypeNameSuffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,Cont#type.def),
+ gen_enc_line(Erules,Typename,TypeNameSuffix,Cont,"H",3,
+ mandatory,"{EncBytes,EncLen} = ",EncObj),
+ emit([",",nl]),
+ emit([indent(3),"'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(T",Objfun,","]),
+ emit(["[EncBytes|AccBytes], AccLen + EncLen).",nl,nl]).
+
+%%============================================================================
+%% Encode/decode CHOICE
+%%
+%%============================================================================
+
+gen_encode_choice(Erules,Typename,D) when record(D,type) ->
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_enc_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit([nl,nl]).
+
+gen_decode_choice(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(bytes),
+ ChoiceTag = D#type.tag,
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ CompList1 = case CompList of
+ {Rl,El} -> Rl ++ El;
+ _ -> CompList
+ end,
+ gen_dec_choice(Erules,Typename,ChoiceTag,CompList1,Ext),
+ emit([".",nl]).
+
+
+%%============================================================================
+%% Encode SEQUENCE
+%%
+%%============================================================================
+
+gen_enc_sequence_call(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],Pos,Ext,EncObj) ->
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Element =
+ case TopType of
+ ['EXTERNAL'] ->
+ io_lib:format("Cindex~w",[Pos]);
+ _ ->
+ io_lib:format("Cindex~w",[Pos])
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ print_attribute_comment(InnerType,Pos,Cname,Prop),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,3,Prop,EncObj),
+ emit([com,nl]),
+ gen_enc_sequence_call(Erules,TopType,Rest,Pos+1,Ext,EncObj);
+
+gen_enc_sequence_call(_Erules,_TopType,[],_Num,_,_) ->
+ true.
+
+%%============================================================================
+%% Decode SEQUENCE
+%%
+%%============================================================================
+
+gen_dec_sequence_call(Erules,TopType,CompList,Ext,DecObjInf) ->
+ gen_dec_sequence_call1(Erules,TopType, CompList, 1, Ext,DecObjInf,[],[]).
+
+
+gen_dec_sequence_call1(Erules,TopType,[#'ComponentType'{name=Cname,typespec=Type,prop=Prop,tags=Tags}|Rest],Num,Ext,DecObjInf,LeadingAttrAcc,ArgsAcc) ->
+ {LA,PostponedDec} =
+ gen_dec_component(Erules,TopType,Cname,Tags,Type,Num,Prop,
+ Ext,DecObjInf),
+ case Rest of
+ [] ->
+ {LA ++ LeadingAttrAcc,PostponedDec ++ ArgsAcc};
+ _ ->
+ emit([com,nl]),
+ asn1ct_name:new(bytes),
+ gen_dec_sequence_call1(Erules,TopType,Rest,Num+1,Ext,DecObjInf,
+ LA++LeadingAttrAcc,PostponedDec++ArgsAcc)
+ end;
+
+gen_dec_sequence_call1(_Erules,_TopType,[],1,_,_,_,_) ->
+ no_terms.
+
+
+%%----------------------------
+%%SEQUENCE mandatory
+%%----------------------------
+
+gen_dec_component(Erules,TopType,Cname,CTags,Type,Pos,Prop,Ext,DecObjInf) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} -> OCFTType;
+ _ -> asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ Prop1 = case {Prop,Ext} of
+ {mandatory,{ext,Epos,_}} when Pos >= Epos ->
+ 'OPTIONAL';
+ _ ->
+ Prop
+ end,
+ print_attribute_comment(InnerType,Pos,Cname,Prop1),
+ asn1ct_name:new(term),
+ emit_term_tlv(Prop1,InnerType,DecObjInf),
+ asn1ct_name:new(rb),
+ PostponedDec =
+ gen_dec_line(Erules,TopType,Cname,CTags,Type,Prop1,DecObjInf),
+ asn1ct_name:new(v),
+ asn1ct_name:new(tlv),
+ asn1ct_name:new(form),
+ PostponedDec.
+
+
+emit_term_tlv({'DEFAULT',_},InnerType,DecObjInf) ->
+ emit_term_tlv(opt_or_def,InnerType,DecObjInf);
+emit_term_tlv('OPTIONAL',InnerType,DecObjInf) ->
+ emit_term_tlv(opt_or_def,InnerType,DecObjInf);
+emit_term_tlv(Prop,{typefield,_},DecObjInf) ->
+ emit_term_tlv(Prop,type_or_object_field,DecObjInf);
+emit_term_tlv(Prop,{objectfield,_,_},DecObjInf) ->
+ emit_term_tlv(Prop,type_or_object_field,DecObjInf);
+emit_term_tlv(opt_or_def,type_or_object_field,_) ->
+ asn1ct_name:new(tmpterm),
+ emit(["{",{curr,tmpterm},",",{curr,tlv},"} = "]);
+emit_term_tlv(opt_or_def,_,_) ->
+ emit(["{",{curr,term},",",{curr,tlv},"} = "]);
+emit_term_tlv(_,type_or_object_field,false) ->
+ emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl,
+ {curr,term}," = "]);
+emit_term_tlv(_,type_or_object_field,_) ->
+ asn1ct_name:new(tmpterm),
+ emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl]),
+ emit([nl," ",{curr,tmpterm}," = "]);
+emit_term_tlv(mandatory,_,_) ->
+ emit(["[",{curr,v},"|",{curr,tlv},"] = ",{prev,tlv},", ",nl,
+ {curr,term}," = "]).
+
+
+gen_dec_set_cases(_Erules,_TopType,[],Pos) ->
+ Pos;
+gen_dec_set_cases(Erules,TopType,[Comp|RestComps],Pos) ->
+ Name = Comp#'ComponentType'.name,
+ Type = Comp#'ComponentType'.typespec,
+ CTags = Comp#'ComponentType'.tags,
+
+ emit([indent(6),"%",Name,nl]),
+ Tags = case Type#type.tag of
+ [] -> % this is a choice without explicit tag
+ [(?ASN1CT_GEN_BER:decode_class(T1class) bsl 10) + T1number||
+ {T1class,T1number} <- CTags];
+ [FirstTag|_] ->
+ [(?ASN1CT_GEN_BER:decode_class(FirstTag#tag.class) bsl 10) + FirstTag#tag.number]
+ end,
+% emit([indent(6),"%Tags: ",Tags,nl]),
+% emit([indent(6),"%Type#type.tag: ",Type#type.tag,nl]),
+ CaseFun = fun(TagList=[H|T],Fun,N) ->
+ Semicolon = case TagList of
+ [_Tag1,_|_] -> [";",nl];
+ _ -> ""
+ end,
+ emit(["TTlv = {",H,",_} ->",nl]),
+ emit([indent(4),"{",Pos,", TTlv}",Semicolon]),
+ Fun(T,Fun,N+1);
+ ([],_,0) ->
+ true;
+ ([],_,_) ->
+ emit([";",nl])
+ end,
+ CaseFun(Tags,CaseFun,0),
+%% emit([";",nl]),
+ gen_dec_set_cases(Erules,TopType,RestComps,Pos+1).
+
+
+
+%%---------------------------------------------
+%% Encode CHOICE
+%%---------------------------------------------
+%% for BER we currently do care (a little) if the choice has an EXTENSIONMARKER
+
+
+gen_enc_choice(Erules,TopType,Tag,CompList,_Ext) ->
+ gen_enc_choice1(Erules,TopType,Tag,CompList,_Ext).
+
+gen_enc_choice1(Erules,TopType,_Tag,CompList,_Ext) ->
+ asn1ct_name:clear(),
+ emit([" {EncBytes,EncLen} = case element(1,Val) of",nl]),
+ gen_enc_choice2(Erules,TopType,CompList),
+ emit([nl," end,",nl,nl]),
+
+ emit(["?RT_BER:encode_tags(TagIn, EncBytes, EncLen).",nl]).
+
+
+gen_enc_choice2(Erules,TopType,[H1|T]) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ emit([" ",{asis,Cname}," ->",nl]),
+ {Encobj,Assign} =
+ case {Type#type.def,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+ {#'ObjectClassFieldType'{},{componentrelation,_,_}} ->
+ asn1ct_name:new(tmpBytes),
+ asn1ct_name:new(encBytes),
+ asn1ct_name:new(encLen),
+ Emit = ["{",{curr,tmpBytes},", _} = "],
+ {{no_attr,"ObjFun"},Emit};
+ _ ->
+ {false,[]}
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% {false,[]};
+% _ ->
+% asn1ct_name:new(tmpBytes),
+% asn1ct_name:new(encBytes),
+% asn1ct_name:new(encLen),
+% Emit = ["{",{curr,tmpBytes},", _} = "],
+% {{no_attr,"ObjFun"},Emit}
+% end,
+ gen_enc_line(Erules,TopType,Cname,Type,"element(2,Val)",9,
+ mandatory,Assign,Encobj),
+ case Encobj of
+ false -> ok;
+ _ ->
+ emit([",",nl,indent(9),"{",{curr,encBytes},", ",
+ {curr,encLen},"}"])
+ end,
+ emit([";",nl]),
+ case T of
+ [] ->
+ emit([indent(6), "Else -> ",nl,
+ indent(9),"exit({error,{asn1,{invalid_choice_type,Else}}})"]);
+ _ ->
+ true
+ end,
+ gen_enc_choice2(Erules,TopType,T);
+
+gen_enc_choice2(_Erules,_TopType,[]) ->
+ true.
+
+
+
+
+%%--------------------------------------------
+%% Decode CHOICE
+%%--------------------------------------------
+
+gen_dec_choice(Erules,TopType, _ChTag, CompList, Ext) ->
+ asn1ct_name:clear(),
+ asn1ct_name:new(tlv),
+ emit([{curr,tlv},
+ " = ?RT_BER:match_tags(",{prev,tlv},",TagIn), ",nl]),
+ asn1ct_name:new(tlv),
+ asn1ct_name:new(v),
+ emit(["case (case ",{prev,tlv},
+ " of [Ctemp",{prev,tlv},"] -> Ctemp",{prev,tlv},
+ "; _ -> ",{prev,tlv}," end)"," of",nl]),
+ asn1ct_name:new(tagList),
+ asn1ct_name:new(choTags),
+ asn1ct_name:new(res),
+ gen_dec_choice_cases(Erules,TopType,CompList),
+ emit([indent(6), {curr,else}," -> ",nl]),
+ case Ext of
+ noext ->
+ emit([indent(9),"exit({error,{asn1,{invalid_choice_tag,",
+ {curr,else},"}}})",nl]);
+ _ ->
+ emit([indent(9),"{asn1_ExtAlt, ?RT_BER:encode(",{curr,else},")}",nl])
+ end,
+ emit([indent(3),"end",nl]),
+ asn1ct_name:new(tag),
+ asn1ct_name:new(else).
+
+
+gen_dec_choice_cases(_Erules,_TopType, []) ->
+ ok;
+gen_dec_choice_cases(Erules,TopType, [H|T]) ->
+ Cname = H#'ComponentType'.name,
+ Type = H#'ComponentType'.typespec,
+ Prop = H#'ComponentType'.prop,
+ Tags = Type#type.tag,
+ Fcases = fun([{T1class,T1number}|Tail],Fun) ->
+ emit([indent(4),{curr,v}," = {",
+ (?ASN1CT_GEN_BER:decode_class(T1class) bsl 10) +
+ T1number,",_} -> ",nl]),
+ emit([indent(8),"{",{asis,Cname},", "]),
+ gen_dec_line(Erules,TopType,Cname,[],Type,Prop,false),
+ emit(["};",nl,nl]),
+ Fun(Tail,Fun);
+ ([],_) ->
+ ok
+ end,
+ emit([nl,"%% '",Cname,"'",nl]),
+ case {Tags,asn1ct:get_gen_state_field(namelist)} of
+ {[],_} -> % choice without explicit tags
+ Fcases(H#'ComponentType'.tags,Fcases);
+ {[FirstT|_RestT],[{Cname,undecoded}|Names]} ->
+ DecTag=(?ASN1CT_GEN_BER:decode_class(FirstT#tag.class) bsl 10) +
+ FirstT#tag.number,
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ [DecTag],Type}),
+ asn1ct:update_gen_state(namelist,Names),
+ emit([indent(4),{curr,res}," = ",
+ match_tag(ber_bin,{FirstT#tag.class,FirstT#tag.number}),
+ " -> ",nl]),
+ emit([indent(8),"{",{asis,Cname},", {'",
+ asn1ct_gen:list2name([Cname|TopType]),"',",
+ {curr,res},"}};",nl,nl]);
+ {[FirstT|RestT],_} ->
+ emit([indent(4),"{",
+ (?ASN1CT_GEN_BER:decode_class(FirstT#tag.class) bsl 10) +
+ FirstT#tag.number,", ",{curr,v},"} -> ",nl]),
+ emit([indent(8),"{",{asis,Cname},", "]),
+ gen_dec_line(Erules,TopType,Cname,[],Type#type{tag=RestT},Prop,false),
+ emit(["};",nl,nl])
+ end,
+ gen_dec_choice_cases(Erules,TopType, T).
+
+
+
+%%---------------------------------------
+%% Generate the encode/decode code
+%%---------------------------------------
+
+gen_enc_line(Erules,TopType,Cname,
+ Type=#type{constraint=[{componentrelation,_,_}],
+ def=#'ObjectClassFieldType'{type={typefield,_}}},
+ Element,Indent,OptOrMand=mandatory,EncObj)
+ when list(Element) ->
+ asn1ct_name:new(tmpBytes),
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,tmpBytes},",_} = "],EncObj);
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,EncObj)
+ when list(Element) ->
+ gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,
+ ["{",{curr,encBytes},",",{curr,encLen},"} = "],EncObj).
+
+gen_enc_line(Erules,TopType,Cname,Type,Element,Indent,OptOrMand,Assign,EncObj)
+ when list(Element) ->
+ IndDeep = indent(Indent),
+ Tag = lists:reverse([?ASN1CT_GEN_BER:encode_tag_val(
+ ?ASN1CT_GEN_BER:decode_class(X#tag.class),
+ X#tag.form,
+ X#tag.number)
+ || X <- Type#type.tag]),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ emit(IndDeep),
+ emit(Assign),
+ gen_optormand_case(OptOrMand,Erules,TopType,Cname,Type,InnerType,WhatKind,
+ Element),
+ case {Type,asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation)} of
+% #type{constraint=[{tableconstraint_info,RefedFieldName}],
+% def={typefield,_}} ->
+ {#type{def=#'ObjectClassFieldType'{type={typefield,_},
+ fieldname=RefedFieldName}},
+ {componentrelation,_,_}} ->
+ {_LeadingAttrName,Fun} = EncObj,
+ case RefedFieldName of
+ {notype,T} ->
+ throw({error,{notype,type_from_object,T}});
+ {Name,RestFieldNames} when atom(Name) ->
+ case OptOrMand of
+ mandatory -> ok;
+ _ ->
+% emit(["{",{curr,tmpBytes},",",{curr,tmpLen},
+ emit(["{",{curr,tmpBytes},",_ } = "])
+% "} = "])
+ end,
+ emit([Fun,"(",{asis,Name},", ",Element,", ",
+ {asis,RestFieldNames},"),",nl]),
+ emit(IndDeep),
+ case OptOrMand of
+ mandatory ->
+ emit(["{",{curr,encBytes},",",{curr,encLen},
+ "} = "]),
+ emit(["?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},")"]);
+ _ ->
+% emit(["{",{next,tmpBytes},", _} = "]),
+ emit(["{",{next,tmpBytes},",",{curr,tmpLen},
+ "} = "]),
+ emit(["?RT_BER:encode_open_type(",{curr,tmpBytes},
+ ",",{asis,Tag},"),",nl]),
+ emit(IndDeep),
+ emit(["{",{next,tmpBytes},", ",{curr,tmpLen},"}"])
+ end;
+ _ ->
+ throw({asn1,{'internal error'}})
+ end;
+ {{#'ObjectClassFieldType'{type={objectfield,PrimFieldName1,
+ PFNList}},_},
+ {componentrelation,_,_}} ->
+ %% this is when the dotted list in the FieldName has more
+ %% than one element
+ {_LeadingAttrName,Fun} = EncObj,
+ emit(["?RT_BER:encode_open_type(",Fun,"(",{asis,PrimFieldName1},
+ ", ",Element,", ",{asis,PFNList},"))"]);
+ _ ->
+ case WhatKind of
+ {primitive,bif} ->
+ EncType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type={fixedtypevaluefield,_,Btype}} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ ?ASN1CT_GEN_BER:gen_encode_prim(ber,EncType,{asis,Tag},
+ Element);
+ {notype,_} ->
+ emit(["'enc_",InnerType,"'(",Element,", ",{asis,Tag},")"]);
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{} -> %Open Type
+ ?ASN1CT_GEN_BER:gen_encode_prim(ber,#type{def='ASN1_OPEN_TYPE'},{asis,Tag},Element);
+ _ ->
+ ?ASN1CT_GEN_BER:gen_encode_prim(ber,Type,
+ {asis,Tag},
+ Element)
+ end;
+ _ ->
+ {EncFunName, _EncMod, _EncFun} =
+ mkfuncname(TopType,Cname,WhatKind,"enc_"),
+ case {WhatKind,Type#type.tablecinf,EncObj} of
+ {{constructed,bif},[{objfun,_}|_R],{_,Fun}} ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},
+ ", ",Fun,")"]);
+ _ ->
+ emit([EncFunName,"(",Element,", ",{asis,Tag},")"])
+ end
+ end
+ end,
+ case OptOrMand of
+ mandatory -> true;
+ _ ->
+ emit([nl,indent(7),"end"])
+ end.
+
+gen_optormand_case(mandatory,_Erules,_TopType,_Cname,_Type,_InnerType,_WhatKind,
+ _Element) ->
+ ok;
+gen_optormand_case('OPTIONAL',Erules,_TopType,_Cname,_Type,_InnerType,_WhatKind,
+ Element) ->
+ emit([" case ",Element," of",nl]),
+ emit([indent(9),"asn1_NOVALUE -> {",
+ empty_lb(Erules),",0};",nl]),
+ emit([indent(9),"_ ->",nl,indent(12)]);
+gen_optormand_case({'DEFAULT',DefaultValue},Erules,TopType,Cname,Type,
+ InnerType,WhatKind,Element) ->
+ CurrMod = get(currmod),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ emit(" case catch "),
+ asn1ct_gen:gen_check_call(TopType,Cname,Type,InnerType,
+ WhatKind,{asis,DefaultValue},
+ Element),
+ emit([" of",nl]),
+ emit([indent(12),"true -> {[],0};",nl]);
+ _ ->
+ emit([" case ",Element," of",nl]),
+ emit([indent(9),"asn1_DEFAULT -> {",
+ empty_lb(Erules),
+ ",0};",nl]),
+ case DefaultValue of
+ #'Externalvaluereference'{module=CurrMod,
+ value=V} ->
+ emit([indent(9),"?",{asis,V}," -> {",
+ empty_lb(Erules),",0};",nl]);
+ _ ->
+ emit([indent(9),{asis,
+ DefaultValue}," -> {",
+ empty_lb(Erules),",0};",nl])
+ end
+ end,
+ emit([indent(9),"_ ->",nl,indent(12)]).
+
+
+
+gen_dec_line(Erules,TopType,Cname,CTags,Type,OptOrMand,DecObjInf) ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(v)),
+ Tag =
+ [(?ASN1CT_GEN_BER:decode_class(X#tag.class) bsl 10) + X#tag.number ||
+ X <- Type#type.tag],
+ ChoiceTags =
+ [(?ASN1CT_GEN_BER:decode_class(Class) bsl 10) + Number||
+ {Class,Number} <- CTags],
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+ PostpDec =
+ case OptOrMand of
+ mandatory ->
+ gen_dec_call(InnerType,Erules,TopType,Cname,Type,
+ BytesVar,Tag,
+ mandatory,", mandatory, ",DecObjInf,OptOrMand);
+ _ -> %optional or default or a mandatory component after an extensionmark
+ {FirstTag,RestTag} =
+ case Tag of
+ [] ->
+ {ChoiceTags,[]};
+ [Ft|Rt] ->
+ {Ft,Rt}
+ end,
+ emit(["case ",{prev,tlv}," of",nl]),
+ PostponedDec =
+ case Tag of
+ [] when length(ChoiceTags) > 0 -> % a choice without explicit tag
+ Fcases =
+ fun(FirstTag1) ->
+ emit(["[",{curr,v}," = {",{asis,FirstTag1},
+ ",_}|Temp",
+ {curr,tlv},
+ "] ->",nl]),
+ emit([indent(4),"{"]),
+ Pdec=
+ gen_dec_call(InnerType,Erules,
+ TopType,Cname,Type,
+ BytesVar,RestTag,
+ mandatory,
+ ", mandatory, ",
+ DecObjInf,OptOrMand),
+
+ emit([", Temp",{curr,tlv},"}"]),
+ emit([";",nl]),
+ Pdec
+ end,
+ hd([Fcases(TmpTag)|| TmpTag <- FirstTag]);
+
+ [] -> % an open type without explicit tag
+ emit(["[",{curr,v},"|Temp",{curr,tlv},"] ->",nl]),
+ emit([indent(4),"{"]),
+ Pdec=
+ gen_dec_call(InnerType,Erules,TopType,Cname,
+ Type,BytesVar,RestTag,mandatory,
+ ", mandatory, ",DecObjInf,
+ OptOrMand),
+
+ emit([", Temp",{curr,tlv},"}"]),
+ emit([";",nl]),
+ Pdec;
+
+ _ ->
+ emit(["[{",{asis,FirstTag},
+ ",",{curr,v},"}|Temp",
+ {curr,tlv},
+ "] ->",nl]),
+ emit([indent(4),"{"]),
+ Pdec=
+ gen_dec_call(InnerType,Erules,TopType,Cname,
+ Type,BytesVar,RestTag,mandatory,
+ ", mandatory, ",DecObjInf,
+ OptOrMand),
+
+ emit([", Temp",{curr,tlv},"}"]),
+ emit([";",nl]),
+ Pdec
+ end,
+
+ emit([indent(4),"_ ->",nl]),
+ case OptOrMand of
+ {'DEFAULT', Def} ->
+ emit([indent(8),"{",{asis,Def},",",{prev,tlv},"}",nl]);
+ 'OPTIONAL' ->
+ emit([indent(8),"{ asn1_NOVALUE, ",{prev,tlv},"}",nl])
+ end,
+ emit(["end"]),
+ PostponedDec
+ end,
+ case DecObjInf of
+ {Cname,ObjSet} -> % this must be the component were an object is
+ %% choosen from the object set according to the table
+ %% constraint.
+ {[{ObjSet,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ PostpDec};
+ _ -> {[],PostpDec}
+ end.
+
+gen_dec_call({typefield,_},_,_,_Cname,Type,BytesVar,Tag,_,_,false,_) ->
+ %% this in case of a choice with typefield components
+ asn1ct_name:new(reason),
+ asn1ct_name:new(opendec),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(tmptlv),
+
+ {FirstPFName,RestPFName} =
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ emit([nl,indent(6),"begin",nl]),
+% emit([indent(9),{curr,opendec}," = ?RT_BER:decode_open_type(",
+ emit([indent(9),{curr,tmptlv}," = ?RT_BER:decode_open_type(",
+ BytesVar,",",{asis,Tag},"),",nl]),
+% emit([indent(9),"{",{curr,tmptlv},",_} = ?RT_BER:decode(",
+% {curr,opendec},"),",nl]),
+
+ emit([indent(9),"case (catch ObjFun(",{asis,FirstPFName},
+ ", ",{curr,tmptlv},", ",{asis,RestPFName},
+ ")) of", nl]),%% ??? What about Tag
+ emit([indent(12),"{'EXIT',",{curr,reason},"} ->",nl]),
+ emit([indent(15),"exit({'Type not ",
+ "compatible with table constraint', ",{curr,reason},"});",nl]),
+ emit([indent(12),{curr,tmpterm}," ->",nl]),
+ emit([indent(15),{curr,tmpterm},nl]),
+ emit([indent(9),"end",nl,indent(6),"end",nl]),
+ [];
+gen_dec_call({typefield,_},_,_,Cname,Type,BytesVar,Tag,_,_,_DecObjInf,OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",BytesVar,",",{asis,Tag},")"]),
+ RefedFieldName =
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ [{Cname,RefedFieldName,asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call({objectfield,PrimFieldName,PFNList},_,_,Cname,_,BytesVar,Tag,_,_,_,OptOrMandComp) ->
+ emit(["?RT_BER:decode_open_type(",BytesVar,",",{asis,Tag},")"]),
+ [{Cname,{PrimFieldName,PFNList},asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),Tag,OptOrMandComp}];
+gen_dec_call(InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,PrimOptOrMand,
+ OptOrMand,DecObjInf,_) ->
+ WhatKind = asn1ct_gen:type(InnerType),
+ gen_dec_call1(WhatKind,InnerType,Erules,TopType,Cname,Type,BytesVar,Tag,
+ PrimOptOrMand,OptOrMand),
+ case DecObjInf of
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ emit([",",nl,"ObjFun = 'getdec_",OSet,"'(",
+% {asis,UniqueFName},", ",{curr,term},")"]);
+ {asis,UniqueFName},", ",ValueMatch,")"]);
+ _ ->
+ ok
+ end,
+ [].
+gen_dec_call1({primitive,bif},InnerType,Erules,TopType,Cname,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ case {asn1ct:get_gen_state_field(namelist),InnerType} of
+ {[{Cname,undecoded}|Rest],_} ->
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ Tag,Type}),
+ asn1ct:update_gen_state(namelist,Rest),
+% emit(["?RT_BER:match_tags(",BytesVar,",",{asis,Tag},")"]);
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
+ BytesVar,"}"]);
+ {_,{fixedtypevaluefield,_,Btype}} ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Btype,BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand);
+ _ ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand)
+ end;
+gen_dec_call1('ASN1_OPEN_TYPE',_InnerType,Erules,TopType,Cname,Type,BytesVar,
+ Tag,OptOrMand,_) ->
+ case {asn1ct:get_gen_state_field(namelist),Type#type.def} of
+ {[{Cname,undecoded}|Rest],_} ->
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ Tag,Type}),
+ asn1ct:update_gen_state(namelist,Rest),
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
+ BytesVar,"}"]);
+% emit(["?RT_BER:match_tags(",BytesVar,",",{asis,Tag},")"]);
+ {_,#'ObjectClassFieldType'{type=OpenType}} ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,#type{def=OpenType},
+ BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand);
+ _ ->
+ ?ASN1CT_GEN_BER:gen_dec_prim(Erules,Type,BytesVar,Tag,[],
+ ?PRIMITIVE,OptOrMand)
+ end;
+gen_dec_call1(WhatKind,_,_Erules,TopType,Cname,Type,BytesVar,
+ Tag,_,_OptOrMand) ->
+ case asn1ct:get_gen_state_field(namelist) of
+ [{Cname,undecoded}|Rest] ->
+ asn1ct:add_generated_refed_func({[Cname|TopType],undecoded,
+ Tag,Type}),
+ asn1ct:update_gen_state(namelist,Rest),
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',",
+ BytesVar,"}"]);
+ _ ->
+% {DecFunName, _DecMod, _DecFun} =
+% case {asn1ct:get_gen_state_field(namelist),WhatKind} of
+ EmitDecFunCall =
+ fun(FuncName) ->
+ case {WhatKind,Type#type.tablecinf} of
+ {{constructed,bif},[{objfun,_}|_Rest]} ->
+ emit([FuncName,"(",BytesVar,", ",{asis,Tag},
+ ", ObjFun)"]);
+ _ ->
+ emit([FuncName,"(",BytesVar,", ",{asis,Tag},")"])
+ end
+ end,
+ case asn1ct:get_gen_state_field(namelist) of
+ [{Cname,List}|Rest] when list(List) ->
+ case WhatKind of
+ #'Externaltypereference'{} ->
+ %%io:format("gen_dec_call1 1:~n~p~n~n",[WhatKind]),
+ asn1ct:add_tobe_refed_func({WhatKind,List});
+ _ ->
+ %%io:format("gen_dec_call1 2:~n~p~n~n",[[Cname|TopType]]),
+ asn1ct:add_tobe_refed_func({[Cname|TopType],
+ List})
+ end,
+ asn1ct:update_gen_state(namelist,Rest),
+ Prefix=asn1ct:get_gen_state_field(prefix),
+ {DecFunName,_,_}=
+ mkfuncname(TopType,Cname,WhatKind,Prefix),
+ EmitDecFunCall(DecFunName);
+ [{Cname,parts}|Rest] ->
+ asn1ct:update_gen_state(namelist,Rest),
+ asn1ct:get_gen_state_field(prefix),
+ %% This is to prepare SEQUENCE OF value in
+ %% partial incomplete decode for a later
+ %% part-decode, i.e. skip %% the tag.
+ asn1ct:add_generated_refed_func({[Cname|TopType],
+ parts,
+ [],Type}),
+ emit(["{'",asn1ct_gen:list2name([Cname|TopType]),"',"]),
+ EmitDecFunCall("?RT_BER:match_tags"),
+ emit("}");
+ _ ->
+ {DecFunName,_,_}=
+ mkfuncname(TopType,Cname,WhatKind,"dec_"),
+ EmitDecFunCall(DecFunName)
+ end
+% case {WhatKind,Type#type.tablecinf} of
+% {{constructed,bif},[{objfun,_}|_Rest]} ->
+% emit([DecFunName,"(",BytesVar,", ",{asis,Tag},
+% ", ObjFun)"]);
+% _ ->
+% emit([DecFunName,"(",BytesVar,", ",{asis,Tag},")"])
+% end
+ end.
+
+
+%%------------------------------------------------------
+%% General and special help functions (not exported)
+%%------------------------------------------------------
+
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+mkcindexlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
+ emit(["Cindex",H,Sep]),
+ mkcindexlist([T1|T], Sep);
+mkcindexlist([H|T], Sep) ->
+ emit(["Cindex",H]),
+ mkcindexlist(T, Sep);
+mkcindexlist([], _) ->
+ true.
+
+mkcindexlist(L) ->
+ mkcindexlist(L,", ").
+
+
+mkvlist([H,T1|T], Sep) -> % Sep is a string e.g ", " or "+ "
+ emit([{var,H},Sep]),
+ mkvlist([T1|T], Sep);
+mkvlist([H|T], Sep) ->
+ emit([{var,H}]),
+ mkvlist(T, Sep);
+mkvlist([], _) ->
+ true.
+
+mkvlist(L) ->
+ mkvlist(L,", ").
+
+mkvplus(L) ->
+ mkvlist(L," + ").
+
+extensible(CompList) when list(CompList) ->
+ noext;
+extensible({RootList,ExtList}) ->
+ {ext,length(RootList)+1,length(ExtList)}.
+
+
+print_attribute_comment(InnerType,Pos,Cname,Prop) ->
+ CommentLine = "%%-------------------------------------------------",
+ emit([nl,CommentLine]),
+ case InnerType of
+ {typereference,_,Name} ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",Name]);
+ {'Externaltypereference',_,XModule,Name} ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") External ",XModule,":",Name]);
+ _ ->
+ emit([nl,"%% attribute ",Cname,"(",Pos,") with type ",InnerType])
+ end,
+ case Prop of
+ mandatory ->
+ continue;
+ {'DEFAULT', Def} ->
+ emit([" DEFAULT = ",{asis,Def}]);
+ 'OPTIONAL' ->
+ emit([" OPTIONAL"])
+ end,
+ emit([nl,CommentLine,nl]).
+
+
+
+mkfuncname(TopType,Cname,WhatKind,Prefix) ->
+ CurrMod = get(currmod),
+ case WhatKind of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ F = lists:concat(["'",Prefix,EType,"'"]),
+ {F, "?MODULE", F};
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ {lists:concat(["'",Mod,"':'",Prefix,EType,"'"]),Mod,
+ lists:concat(["'",Prefix,EType,"'"])};
+ {constructed,bif} ->
+ F = lists:concat(["'",Prefix,asn1ct_gen:list2name([Cname|TopType]),"'"]),
+ {F, "?MODULE", F}
+ end.
+
+empty_lb(ber) ->
+ "[]";
+empty_lb(ber_bin) ->
+ "<<>>";
+empty_lb(ber_bin_v2) ->
+ "<<>>".
+
+value_match(Index,Value) when atom(Value) ->
+ value_match(Index,atom_to_list(Value));
+value_match([],Value) ->
+ Value;
+value_match([{VI,_}|VIs],Value) ->
+ value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
+value_match1(Value,[],Acc,Depth) ->
+ Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
+value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
+ value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_per.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_per.erl
new file mode 100644
index 0000000000..9b4e0063bb
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_constructed_per.erl
@@ -0,0 +1,1235 @@
+% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_constructed_per.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_constructed_per).
+
+-export([gen_encode_sequence/3]).
+-export([gen_decode_sequence/3]).
+-export([gen_encode_set/3]).
+-export([gen_decode_set/3]).
+-export([gen_encode_sof/4]).
+-export([gen_decode_sof/4]).
+-export([gen_encode_choice/3]).
+-export([gen_decode_choice/3]).
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+
+%% ENCODE GENERATOR FOR SEQUENCE TYPE ** **********
+
+
+gen_encode_set(Erules,TypeName,D) ->
+ gen_encode_constructed(Erules,TypeName,D).
+
+gen_encode_sequence(Erules,TypeName,D) ->
+ gen_encode_constructed(Erules,TypeName,D).
+
+gen_encode_constructed(Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(term),
+ asn1ct_name:new(bytes),
+ {CompList,TableConsInfo} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {CL,TCI};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {CL,TCI}
+ end,
+ case Typename of
+ ['EXTERNAL'] ->
+ emit({{var,asn1ct_name:next(val)},
+ " = asn1rt_check:transform_to_EXTERNAL1990(",
+ {var,asn1ct_name:curr(val)},"),",nl}),
+ asn1ct_name:new(val);
+ _ ->
+ ok
+ end,
+ case {Optionals = optionals(CompList),CompList} of
+ {[],EmptyCL} when EmptyCL == {[],[]};EmptyCL == [] ->
+ emit(["%%Variable setting just to eliminate ",
+ "compiler warning for unused vars!",nl,
+ "_Val = ",{var,asn1ct_name:curr(val)},",",nl]);
+ {[],_} ->
+ emit([{var,asn1ct_name:next(val)}," = ?RT_PER:list_to_record("]),
+ emit(["'",asn1ct_gen:list2rname(Typename),"'"]),
+ emit([", ",{var,asn1ct_name:curr(val)},"),",nl]);
+ _ ->
+ Fixoptcall =
+ case Erules of
+ per -> ",Opt} = ?RT_PER:fixoptionals2(";
+ _ -> ",Opt} = ?RT_PER:fixoptionals("
+ end,
+ emit({"{",{var,asn1ct_name:next(val)},Fixoptcall,
+ {asis,Optionals},",",length(Optionals),
+ ",",{var,asn1ct_name:curr(val)},"),",nl})
+ end,
+ asn1ct_name:new(val),
+ Ext = extensible(CompList),
+ case Ext of
+ {ext,_,NumExt} when NumExt > 0 ->
+ emit(["Extensions = ?RT_PER:fixextensions(",{asis,Ext},
+ ", ",{curr,val},"),",nl]);
+ _ -> true
+ end,
+ EncObj =
+ case TableConsInfo of
+ #simpletableattributes{usedclassfield=Used,
+ uniqueclassfield=Unique} when Used /= Unique ->
+ false;
+ %% ObjectSet, name of the object set in constraints
+ %%
+ %%{ObjectSet,AttrN,N,UniqueFieldName} -> %% N is index of attribute that determines constraint
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ c_index=N,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValueIndex
+ } -> %% N is index of attribute that determines constraint
+ OSDef =
+ case ObjectSet of
+ {Module,OSName} ->
+ asn1_db:dbget(Module,OSName);
+ OSName ->
+ asn1_db:dbget(get(currmod),OSName)
+ end,
+ case (OSDef#typedef.typespec)#'ObjectSet'.gen of
+ true ->
+ ObjectEncode =
+ asn1ct_gen:un_hyphen_var(lists:concat(['Obj',AttrN])),
+ emit([ObjectEncode," = ",nl]),
+ emit([" 'getenc_",ObjectSet,"'(",
+ {asis,UniqueFieldName},", ",nl]),
+ El = make_element(N+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),AttrN),
+ Indent = 12 + length(atom_to_list(ObjectSet)),
+ case ValueIndex of
+ [] ->
+ emit([indent(Indent),El,"),",nl]);
+ _ ->
+ emit([indent(Indent),"value_match(",
+ {asis,ValueIndex},",",El,")),",nl]),
+ notice_value_match()
+ end,
+ {AttrN,ObjectEncode};
+ _ ->
+ false
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ %% when the simpletableattributes was at an outer
+ %% level and the objfun has been passed through the
+ %% function call
+ {"got objfun through args","ObjFun"};
+ _ ->
+ false
+ end
+ end,
+ emit({"[",nl}),
+ MaybeComma1 =
+ case Ext of
+ {ext,_Pos,NumExt2} when NumExt2 > 0 ->
+ emit({"?RT_PER:setext(Extensions =/= [])"}),
+ ", ";
+ {ext,_Pos,_} ->
+ emit({"?RT_PER:setext(false)"}),
+ ", ";
+ _ ->
+ ""
+ end,
+ MaybeComma2 =
+ case optionals(CompList) of
+ [] -> MaybeComma1;
+ _ ->
+ emit(MaybeComma1),
+ emit("Opt"),
+ {",",nl}
+ end,
+ gen_enc_components_call(Typename,CompList,MaybeComma2,EncObj,Ext),
+ emit({"].",nl}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% generate decode function for SEQUENCE and SET
+%%
+gen_decode_set(Erules,Typename,D) ->
+ gen_decode_constructed(Erules,Typename,D).
+
+gen_decode_sequence(Erules,Typename,D) ->
+ gen_decode_constructed(Erules,Typename,D).
+
+gen_decode_constructed(_Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ {CompList,TableConsInfo} =
+ case D#type.def of
+ #'SEQUENCE'{tablecinf=TCI,components=CL} ->
+ {CL,TCI};
+ #'SET'{tablecinf=TCI,components=CL} ->
+ {CL,TCI}
+ end,
+ Ext = extensible(CompList),
+ MaybeComma1 = case Ext of
+ {ext,_Pos,_NumExt} ->
+ gen_dec_extension_value("Bytes"),
+ {",",nl};
+ _ ->
+ ""
+ end,
+ Optionals = optionals(CompList),
+ MaybeComma2 = case Optionals of
+ [] -> MaybeComma1;
+ _ ->
+ Bcurr = asn1ct_name:curr(bytes),
+ Bnext = asn1ct_name:next(bytes),
+ emit(MaybeComma1),
+ GetoptCall = "} = ?RT_PER:getoptionals2(",
+ emit({"{Opt,",{var,Bnext},GetoptCall,
+ {var,Bcurr},",",{asis,length(Optionals)},")"}),
+ asn1ct_name:new(bytes),
+ ", "
+ end,
+ {DecObjInf,UniqueFName,ValueIndex} =
+ case TableConsInfo of
+%% {ObjectSet,AttrN,N,UniqueFieldName} ->%% N is index of attribute that determines constraint
+ #simpletableattributes{objectsetname=ObjectSet,
+ c_name=AttrN,
+ usedclassfield=UniqueFieldName,
+ uniqueclassfield=UniqueFieldName,
+ valueindex=ValIndex} ->
+%% {AttrN,ObjectSet};
+ F = fun(#'ComponentType'{typespec=CT})->
+ case {CT#type.constraint,CT#type.tablecinf} of
+ {[],[{objfun,_}|_R]} -> true;
+ _ -> false
+ end
+ end,
+ case lists:any(F,CompList) of
+ true -> % when component relation constraint establish
+ %% relation from a component to another components
+ %% subtype component
+ {{AttrN,{deep,ObjectSet,UniqueFieldName,ValIndex}},
+ UniqueFieldName,ValIndex};
+ false ->
+ {{AttrN,ObjectSet},UniqueFieldName,ValIndex}
+ end;
+ _ ->
+ case D#type.tablecinf of
+ [{objfun,_}|_] ->
+ {{"got objfun through args","ObjFun"},false,false};
+ _ ->
+ {false,false,false}
+ end
+ end,
+ {AccTerm,AccBytes} =
+ gen_dec_components_call(Typename,CompList,MaybeComma2,DecObjInf,Ext,length(Optionals)),
+ case asn1ct_name:all(term) of
+ [] -> emit(MaybeComma2); % no components at all
+ _ -> emit({com,nl})
+ end,
+ case {AccTerm,AccBytes} of
+ {[],[]} ->
+ ok;
+ {_,[]} ->
+ ok;
+ {[{ObjSet,LeadingAttr,Term}],ListOfOpenTypes} ->
+ DecObj = asn1ct_gen:un_hyphen_var(lists:concat(['DecObj',LeadingAttr,Term])),
+ ValueMatch = value_match(ValueIndex,Term),
+ emit({DecObj," =",nl," 'getdec_",ObjSet,"'(",
+% {asis,UniqueFName},", ",Term,"),",nl}),
+ {asis,UniqueFName},", ",ValueMatch,"),",nl}),
+ gen_dec_listofopentypes(DecObj,ListOfOpenTypes,false)
+ end,
+ %% we don't return named lists any more Cnames = mkcnamelist(CompList),
+ demit({"Result = "}), %dbg
+ %% return value as record
+ case Typename of
+ ['EXTERNAL'] ->
+ emit({" OldFormat={'",asn1ct_gen:list2rname(Typename),
+ "'"}),
+ mkvlist(asn1ct_name:all(term)),
+ emit({"},",nl}),
+ emit({" ASN11994Format =",nl,
+ " asn1rt_check:transform_to_EXTERNAL1994",
+ "(OldFormat),",nl}),
+ emit(" {ASN11994Format,");
+ _ ->
+ emit(["{{'",asn1ct_gen:list2rname(Typename),"'"]),
+ mkvlist(asn1ct_name:all(term)),
+ emit("},")
+ end,
+ emit({{var,asn1ct_name:curr(bytes)},"}"}),
+ emit({".",nl,nl}).
+
+gen_dec_listofopentypes(_,[],_) ->
+ emit(nl);
+gen_dec_listofopentypes(DecObj,[{_Cname,{FirstPFN,PFNList},Term,TmpTerm,Prop}|Rest],_Update) ->
+
+% asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+
+ emit([Term," = ",nl]),
+
+ N = case Prop of
+ mandatory -> 0;
+ 'OPTIONAL' ->
+ emit_opt_or_mand_check(asn1_NOVALUE,TmpTerm),
+ 6;
+ {'DEFAULT',Val} ->
+ emit_opt_or_mand_check(Val,TmpTerm),
+ 6
+ end,
+
+ emit([indent(N+3),"case (catch ",DecObj,"(",
+ {asis,FirstPFN},", ",TmpTerm,", telltype,",{asis,PFNList},")) of",nl]),
+ emit([indent(N+6),"{'EXIT', ",{curr,reason},"} ->",nl]),
+%% emit({indent(9),"throw({runtime_error,{","'Type not compatible with table constraint'",",",Term,"}});",nl}),
+ emit([indent(N+9),"exit({'Type not compatible with table constraint',",
+ {curr,reason},"});",nl]),
+ emit([indent(N+6),"{",{curr,tmpterm},",_} ->",nl]),
+ emit([indent(N+9),{curr,tmpterm},nl]),
+
+ case Prop of
+ mandatory ->
+ emit([indent(N+3),"end,",nl]);
+ _ ->
+ emit([indent(N+3),"end",nl,
+ indent(3),"end,",nl])
+ end,
+ gen_dec_listofopentypes(DecObj,Rest,true).
+
+
+emit_opt_or_mand_check(Val,Term) ->
+ emit([indent(3),"case ",Term," of",nl,
+ indent(6),{asis,Val}," ->",{asis,Val},";",nl,
+ indent(6),"_ ->",nl]).
+
+%% ENCODE GENERATOR FOR THE CHOICE TYPE *******
+%% assume Val = {Alternative,AltType}
+%% generate
+%%[
+%% ?RT_PER:set_choice(element(1,Val),Altnum,Altlist,ext),
+%%case element(1,Val) of
+%% alt1 ->
+%% encode_alt1(element(2,Val));
+%% alt2 ->
+%% encode_alt2(element(2,Val))
+%%end
+%%].
+
+gen_encode_choice(_Erules,Typename,D) when record(D,type) ->
+ {'CHOICE',CompList} = D#type.def,
+ emit({"[",nl}),
+ Ext = extensible(CompList),
+ gen_enc_choice(Typename,CompList,Ext),
+ emit({nl,"].",nl}).
+
+gen_decode_choice(_Erules,Typename,D) when record(D,type) ->
+ asn1ct_name:start(),
+ asn1ct_name:new(bytes),
+ {'CHOICE',CompList} = D#type.def,
+ Ext = extensible(CompList),
+ gen_dec_choice(Typename,CompList,Ext),
+ emit({".",nl}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Encode generator for SEQUENCE OF type
+
+
+gen_encode_sof(_Erules,Typename,SeqOrSetOf,D) when record(D,type) ->
+ asn1ct_name:start(),
+% Val = [Component]
+% ?RT_PER:encode_length(length(Val)),
+% lists:
+ {_SeqOrSetOf,ComponentType} = D#type.def,
+ emit({"[",nl}),
+ SizeConstraint =
+ case asn1ct_gen:get_constraint(D#type.constraint,
+ 'SizeConstraint') of
+ no -> undefined;
+ Range -> Range
+ end,
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _->
+ ""
+ end,
+ emit({nl,indent(3),"?RT_PER:encode_length(",
+ {asis,SizeConstraint},
+ ",length(Val)),",nl}),
+ emit({indent(3),"'enc_",asn1ct_gen:list2name(Typename),
+ "_components'(Val",ObjFun,", [])"}),
+ emit({nl,"].",nl}),
+ NewComponentType =
+ case ComponentType#type.def of
+ {'ENUMERATED',_,Component}->
+ ComponentType#type{def={'ENUMERATED',Component}};
+ _ -> ComponentType
+ end,
+ gen_encode_sof_components(Typename,SeqOrSetOf,NewComponentType).
+
+gen_decode_sof(_Erules,Typename,SeqOrSetOf,D) when record(D,type) ->
+ asn1ct_name:start(),
+% Val = [Component]
+% ?RT_PER:encode_length(length(Val)),
+% lists:
+ {_SeqOrSetOf,ComponentType} = D#type.def,
+ SizeConstraint =
+ case asn1ct_gen:get_constraint(D#type.constraint,
+ 'SizeConstraint') of
+ no -> undefined;
+ Range -> Range
+ end,
+ ObjFun =
+ case D#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({nl,"{Num,Bytes1} = ?RT_PER:decode_length(Bytes,",{asis,SizeConstraint},"),",nl}),
+ emit({"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(Num, Bytes1, telltype",ObjFun,", []).",nl}),
+ NewComponentType =
+ case ComponentType#type.def of
+ {'ENUMERATED',_,Component}->
+ ComponentType#type{def={'ENUMERATED',Component}};
+ _ -> ComponentType
+ end,
+ gen_decode_sof_components(Typename,SeqOrSetOf,NewComponentType).
+
+gen_encode_sof_components(Typename,SeqOrSetOf,Cont) ->
+ {ObjFun,ObjFun_Var} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _"};
+ _ ->
+ {"",""}
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'([]",
+ ObjFun_Var,", Acc) -> lists:reverse(Acc);",nl,nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'([H|T]",
+ ObjFun,", Acc) ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"_components'(T"}),
+ emit({ObjFun,", ["}),
+ %% the component encoder
+ Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
+ Cont#type.def),
+
+ Conttype = asn1ct_gen:get_inner(Cont#type.def),
+ Currmod = get(currmod),
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ case asn1ct_gen:type(Conttype) of
+ {primitive,bif} ->
+ gen_encode_prim_wrapper(Ctgenmod,per,Cont,false,"H");
+% Ctgenmod:gen_encode_prim(per,Cont,false,"H");
+ {constructed,bif} ->
+ NewTypename = [Constructed_Suffix|Typename],
+ emit({"'enc_",asn1ct_gen:list2name(NewTypename),"'(H",
+ ObjFun,")",nl,nl});
+ #'Externaltypereference'{module=Currmod,type=Ename} ->
+ emit({"'enc_",Ename,"'(H)",nl,nl});
+ #'Externaltypereference'{module=EMod,type=EType} ->
+ emit({"'",EMod,"':'enc_",EType,"'(H)",nl,nl});
+ _ ->
+ emit({"'enc_",Conttype,"'(H)",nl,nl})
+ end,
+ emit({" | Acc]).",nl}).
+
+gen_decode_sof_components(Typename,SeqOrSetOf,Cont) ->
+ {ObjFun,ObjFun_Var} =
+ case Cont#type.tablecinf of
+ [{objfun,_}|_R] ->
+ {", ObjFun",", _"};
+ _ ->
+ {"",""}
+ end,
+ emit({"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(0, Bytes, _",ObjFun_Var,", Acc) ->",nl,
+ indent(3),"{lists:reverse(Acc), Bytes};",nl}),
+ emit({"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(Num, Bytes, _",ObjFun,", Acc) ->",nl}),
+ emit({indent(3),"{Term,Remain} = "}),
+ Constructed_Suffix = asn1ct_gen:constructed_suffix(SeqOrSetOf,
+ Cont#type.def),
+ Conttype = asn1ct_gen:get_inner(Cont#type.def),
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ case asn1ct_gen:type(Conttype) of
+ {primitive,bif} ->
+ Ctgenmod:gen_dec_prim(per,Cont,"Bytes"),
+ emit({com,nl});
+ {constructed,bif} ->
+ NewTypename = [Constructed_Suffix|Typename],
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(Bytes, telltype",ObjFun,"),",nl});
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(Bytes,telltype),",nl});
+ #'Externaltypereference'{module=EMod,type=EType} ->
+ emit({"'",EMod,"':'dec_",EType,"'(Bytes,telltype),",nl});
+ _ ->
+ emit({"'dec_",Conttype,"'(Bytes,telltype),",nl})
+ end,
+ emit({indent(3),"'dec_",asn1ct_gen:list2name(Typename),
+ "_components'(Num-1, Remain, telltype",ObjFun,", [Term|Acc]).",nl}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% General and special help functions (not exported)
+
+mkvlist([H|T]) ->
+ emit(","),
+ mkvlist2([H|T]);
+mkvlist([]) ->
+ true.
+mkvlist2([H,T1|T]) ->
+ emit({{var,H},","}),
+ mkvlist2([T1|T]);
+mkvlist2([H|T]) ->
+ emit({{var,H}}),
+ mkvlist2(T);
+mkvlist2([]) ->
+ true.
+
+extensible(CompList) when list(CompList) ->
+ noext;
+extensible({RootList,ExtList}) ->
+ {ext,length(RootList)+1,length(ExtList)}.
+
+gen_dec_extension_value(_) ->
+ emit({"{Ext,",{next,bytes},"} = ?RT_PER:getext(",{curr,bytes},")"}),
+ asn1ct_name:new(bytes).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Produce a list with positions (in the Value record) where
+%% there are optional components, start with 2 because first element
+%% is the record name
+
+optionals({L,_Ext}) -> optionals(L,[],2);
+optionals(L) -> optionals(L,[],2).
+
+optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
+optionals([#'ComponentType'{prop='OPTIONAL'}|Rest],Acc,Pos) ->
+ optionals(Rest,[Pos|Acc],Pos+1);
+optionals([#'ComponentType'{prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
+ optionals(Rest,[Pos|Acc],Pos+1);
+optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos+1);
+optionals([],Acc,_) ->
+ lists:reverse(Acc).
+
+
+gen_enc_components_call(TopType,{CompList,ExtList},MaybeComma,DynamicEnc,Ext) ->
+ %% The type has extensionmarker
+ Rpos = gen_enc_components_call1(TopType,CompList,1,MaybeComma,DynamicEnc,noext),
+ case Ext of
+ {ext,_,ExtNum} when ExtNum > 0 ->
+ emit([nl,
+ ",Extensions",nl]);
+ _ -> true
+ end,
+ %handle extensions
+ gen_enc_components_call1(TopType,ExtList,Rpos,MaybeComma,DynamicEnc,Ext);
+gen_enc_components_call(TopType, CompList, MaybeComma, DynamicEnc, Ext) ->
+ %% The type has no extensionmarker
+ gen_enc_components_call1(TopType,CompList,1,MaybeComma,DynamicEnc,Ext).
+
+gen_enc_components_call1(TopType,
+ [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],
+ Tpos,
+ MaybeComma, DynamicEnc, Ext) ->
+
+ put(component_type,{true,C}),
+ %% information necessary in asn1ct_gen_per_rt2ct:gen_encode_prim
+
+ Pos = case Ext of
+ noext -> Tpos;
+ {ext,Epos,_Enum} -> Tpos - Epos + 1
+ end,
+ emit(MaybeComma),
+ case Prop of
+ 'OPTIONAL' ->
+ gen_enc_component_optional(TopType,Cname,Type,Tpos,DynamicEnc,Ext);
+ {'DEFAULT',_DefVal} ->
+ gen_enc_component_default(TopType,Cname,Type,Tpos,DynamicEnc,Ext);
+ _ ->
+ case Ext of
+ {ext,ExtPos,_} when Tpos >= ExtPos ->
+ gen_enc_component_optional(TopType,Cname,Type,Tpos,DynamicEnc,Ext);
+ _ ->
+ gen_enc_component_mandatory(TopType,Cname,Type,Tpos,DynamicEnc,Ext)
+ end
+ end,
+
+ erase(component_type),
+
+ case Rest of
+ [] ->
+ Pos+1;
+ _ ->
+ emit({com,nl}),
+ gen_enc_components_call1(TopType,Rest,Tpos+1,"",DynamicEnc,Ext)
+ end;
+gen_enc_components_call1(_TopType,[],Pos,_,_,_) ->
+ Pos.
+
+gen_enc_component_default(TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
+% Element = io_lib:format("?RT_PER:cindex(~w,Val1,~w)",[Pos+1,Cname]),
+ Element = make_element(Pos+1,"Val1",Cname),
+ emit({"case ",Element," of",nl}),
+% case Ext of
+% {ext,ExtPos,_} when Pos >= ExtPos ->
+% emit({"asn1_NOEXTVALUE -> [];",nl});
+% _ ->
+ emit({"asn1_DEFAULT -> [];",nl}),
+% end,
+ asn1ct_name:new(tmpval),
+ emit({{curr,tmpval}," ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ gen_enc_line(TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
+ emit({nl,"end"}).
+
+gen_enc_component_optional(TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
+% Element = io_lib:format("?RT_PER:cindex(~w,Val1,~w)",[Pos+1,Cname]),
+ Element = make_element(Pos+1,"Val1",Cname),
+ emit({"case ",Element," of",nl}),
+% case Ext of
+% {ext,ExtPos,_} when Pos >= ExtPos ->
+% emit({"asn1_NOEXTVALUE -> [];",nl});
+% _ ->
+ emit({"asn1_NOVALUE -> [];",nl}),
+% end,
+ asn1ct_name:new(tmpval),
+ emit({{curr,tmpval}," ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ NextElement = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ gen_enc_line(TopType,Cname,Type,NextElement, Pos,DynamicEnc,Ext),
+ emit({nl,"end"}).
+
+gen_enc_component_mandatory(TopType,Cname,Type,Pos,DynamicEnc,Ext) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ emit({nl,"%% attribute number ",Pos," with type ",
+ InnerType,nl}),
+ gen_enc_line(TopType,Cname,Type,[],Pos,DynamicEnc,Ext).
+
+gen_enc_line(TopType, Cname, Type, [], Pos,DynamicEnc,Ext) ->
+% Element = io_lib:format("?RT_PER:cindex(~w,~s,~w)",[Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname]),
+ Element = make_element(Pos+1,asn1ct_gen:mk_var(asn1ct_name:curr(val)),Cname),
+ gen_enc_line(TopType,Cname,Type,Element, Pos,DynamicEnc,Ext);
+gen_enc_line(TopType,Cname,Type,Element, Pos,DynamicEnc,Ext) ->
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ Atype =
+ case Type of
+ #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
+ InnerType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ case Ext of
+ {ext,Ep1,_} when Pos >= Ep1 ->
+ emit(["?RT_PER:encode_open_type(dummy,?RT_PER:complete("]);
+ _ -> true
+ end,
+ case Atype of
+ {typefield,_} ->
+ case DynamicEnc of
+ {_LeadingAttrName,Fun} ->
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% componentrelation) of
+ case (Type#type.def)#'ObjectClassFieldType'.fieldname of
+ {notype,T} ->
+ throw({error,{notype,type_from_object,T}});
+ {Name,RestFieldNames} when atom(Name) ->
+ emit({"?RT_PER:encode_open_type([],?RT_PER:complete(",nl}),
+ emit({" ",Fun,"(",{asis,Name},", ",
+ Element,", ",{asis,RestFieldNames},")))"});
+ Other ->
+ throw({asn1,{'internal error',Other}})
+ end
+ end;
+ {objectfield,PrimFieldName1,PFNList} ->
+ case DynamicEnc of
+ {_LeadingAttrName,Fun} ->
+ emit({"?RT_PER:encode_open_type([],"
+ "?RT_PER:complete(",nl}),
+ emit({" ",Fun,"(",{asis,PrimFieldName1},
+ ", ",Element,", ",{asis,PFNList},")))"})
+ end;
+ _ ->
+ CurrMod = get(currmod),
+ case asn1ct_gen:type(Atype) of
+ #'Externaltypereference'{module=Mod,type=EType} when
+ (CurrMod==Mod) ->
+ emit({"'enc_",EType,"'(",Element,")"});
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ emit({"'",Mod,"':'enc_",
+ EType,"'(",Element,")"});
+ #typereference{val=Ename} ->
+ emit({"'enc_",Ename,"'(",Element,")"});
+ {notype,_} ->
+ emit({"'enc_",Atype,"'(",Element,")"});
+ {primitive,bif} ->
+ EncType =
+ case Atype of
+ {fixedtypevaluefield,_,Btype} ->
+ Btype;
+ _ ->
+ Type
+ end,
+ gen_encode_prim_wrapper(Ctgenmod,per,EncType,
+ false,Element);
+% Ctgenmod:gen_encode_prim(per,EncType,
+% false,Element);
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OpenType} ->
+ gen_encode_prim_wrapper(Ctgenmod,per,
+ #type{def=OpenType},
+ false,Element);
+ _ ->
+ gen_encode_prim_wrapper(Ctgenmod,per,Type,
+ false,Element)
+ end;
+% Ctgenmod:gen_encode_prim(per,Type,
+% false,Element);
+ {constructed,bif} ->
+ NewTypename = [Cname|TopType],
+ case {Type#type.tablecinf,DynamicEnc} of
+ {[{objfun,_}|_R],{_,EncFun}} ->
+%% emit({"?RT_PER:encode_open_type([],",
+%% "?RT_PER:complete(",nl}),
+ emit({"'enc_",
+ asn1ct_gen:list2name(NewTypename),
+ "'(",Element,", ",EncFun,")"});
+ _ ->
+ emit({"'enc_",
+ asn1ct_gen:list2name(NewTypename),
+ "'(",Element,")"})
+ end
+ end
+ end,
+ case Ext of
+ {ext,Ep2,_} when Pos >= Ep2 ->
+ emit(["))"]);
+ _ -> true
+ end.
+
+gen_dec_components_call(TopType,{CompList,ExtList},MaybeComma,DecInfObj,Ext,NumberOfOptionals) ->
+ %% The type has extensionmarker
+ {Rpos,AccTerm,AccBytes} =
+ gen_dec_components_call1(TopType, CompList, 1, 1, MaybeComma,DecInfObj,
+ noext,[],[],NumberOfOptionals),
+ emit([",",nl,"{Extensions,",{next,bytes},"} = "]),
+ emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]),
+ asn1ct_name:new(bytes),
+ {_Epos,AccTermE,AccBytesE} =
+ gen_dec_components_call1(TopType,ExtList,Rpos, 1, "",DecInfObj,Ext,[],[],NumberOfOptionals),
+ case ExtList of
+ [] -> true;
+ _ -> emit([",",nl])
+ end,
+ emit([{next,bytes},"= ?RT_PER:skipextensions(",{curr,bytes},",",
+ length(ExtList)+1,",Extensions)",nl]),
+ asn1ct_name:new(bytes),
+ {AccTerm++AccTermE,AccBytes++AccBytesE};
+
+gen_dec_components_call(TopType,CompList,MaybeComma,DecInfObj,Ext,NumberOfOptionals) ->
+ %% The type has no extensionmarker
+ {_,AccTerm,AccBytes} =
+ gen_dec_components_call1(TopType, CompList, 1, 1,MaybeComma,DecInfObj,Ext,[],[],NumberOfOptionals),
+ {AccTerm,AccBytes}.
+
+
+gen_dec_components_call1(TopType,
+ [C=#'ComponentType'{name=Cname,typespec=Type,prop=Prop}|Rest],
+ Tpos,OptPos,MaybeComma,DecInfObj,Ext,AccTerm,AccBytes,NumberOfOptionals) ->
+ Pos = case Ext of
+ noext -> Tpos;
+ {ext,Epos,_Enum} -> Tpos - Epos + 1
+ end,
+ emit(MaybeComma),
+%% asn1ct_name:new(term),
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=InType} ->
+ InType;
+ Def ->
+ asn1ct_gen:get_inner(Def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ case InnerType of
+ #'Externaltypereference'{type=T} ->
+ emit({nl,"%% attribute number ",Tpos," with type ",
+ T,nl});
+ IT when tuple(IT) ->
+ emit({nl,"%% attribute number ",Tpos," with type ",
+ element(2,IT),nl});
+ _ ->
+ emit({nl,"%% attribute number ",Tpos," with type ",
+ InnerType,nl})
+ end,
+
+ case InnerType of
+ {typefield,_} ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
+ {objectfield,_,_} ->
+ asn1ct_name:new(term),
+ asn1ct_name:new(tmpterm),
+ emit({"{",{curr,tmpterm},", ",{next,bytes},"} = "});
+ _ ->
+ asn1ct_name:new(term),
+ emit({"{",{curr,term},",",{next,bytes},"} = "})
+ end,
+
+ NewOptPos =
+ case {Ext,Prop} of
+ {noext,mandatory} -> OptPos; % generate nothing
+ {noext,_} ->
+ Element = io_lib:format("Opt band (1 bsl ~w)",[NumberOfOptionals - OptPos]),
+ emit({"case ",Element," of",nl}),
+ emit({"_Opt",OptPos," when _Opt",OptPos," > 0 ->"}),
+ OptPos+1;
+ _ ->
+ emit(["case Extensions of",nl]),
+ emit(["_ when size(Extensions) >= ",Pos,",element(",Pos,",Extensions) == 1 ->",nl])
+ end,
+ put(component_type,{true,C}),
+ {TermVar,BytesVar} = gen_dec_line(TopType,Cname,Type,Tpos,DecInfObj,Ext),
+ erase(component_type),
+ case {Ext,Prop} of
+ {noext,mandatory} -> true; % generate nothing
+ {noext,_} ->
+ emit([";",nl,"0 ->"]),
+ gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext);
+ _ ->
+ emit([";",nl,"_ ->",nl]),
+ gen_dec_component_no_val(TopType,Cname,Type,Prop,Tpos,Ext)
+ end,
+ case {Ext,Prop} of
+ {noext,mandatory} -> true; % generate nothing
+ {noext,_} ->
+ emit([nl,"end"]);
+ _ ->
+ emit([nl,"end"])
+
+ end,
+ asn1ct_name:new(bytes),
+ case Rest of
+ [] ->
+ {Pos+1,AccTerm++TermVar,AccBytes++BytesVar};
+ _ ->
+ emit({com,nl}),
+ gen_dec_components_call1(TopType,Rest,Tpos+1,NewOptPos,"",DecInfObj,Ext,
+ AccTerm++TermVar,AccBytes++BytesVar,NumberOfOptionals)
+ end;
+
+gen_dec_components_call1(_TopType,[],Pos,_OptPos,_,_,_,AccTerm,AccBytes,_NumberOfOptionals) ->
+ {Pos,AccTerm,AccBytes}.
+
+
+%%gen_dec_component_no_val(TopType,Cname,Type,_,Pos,{ext,Ep,Enum}) when Pos >= Ep ->
+%% emit({"{asn1_NOEXTVALUE,",{curr,bytes},"}",nl});
+gen_dec_component_no_val(_,_,_,{'DEFAULT',DefVal},_,_) ->
+ emit(["{",{asis,DefVal},",",{curr,bytes},"}",nl]);
+gen_dec_component_no_val(_,_,_,'OPTIONAL',_,_) ->
+ emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl});
+gen_dec_component_no_val(_,_,_,mandatory,_,{ext,_,_}) ->
+ emit({"{asn1_NOVALUE,",{curr,bytes},"}",nl}).
+
+
+gen_dec_line(TopType,Cname,Type,Pos,DecInfObj,Ext) ->
+ Ctgenmod = list_to_atom(lists:concat(["asn1ct_gen_",per,
+ asn1ct_gen:rt2ct_suffix()])),
+ Atype =
+ case Type of
+ #type{def=#'ObjectClassFieldType'{type=InnerType}} ->
+ InnerType;
+ _ ->
+ asn1ct_gen:get_inner(Type#type.def)
+ end,
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% asn1ct_gen:get_inner(Type#type.def);
+% _ ->
+% Type#type.def
+% end,
+ BytesVar0 = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ BytesVar = case Ext of
+ {ext,Ep,_} when Pos >= Ep ->
+ emit(["begin",nl,"{TmpVal",Pos,",Trem",Pos,
+ "}=?RT_PER:decode_open_type(",
+ {curr,bytes},",[]),",nl,
+ "{TmpValx",Pos,",_}="]),
+ io_lib:format("TmpVal~p",[Pos]);
+ _ -> BytesVar0
+ end,
+ SaveBytes =
+ case Atype of
+ {typefield,_} ->
+ case DecInfObj of
+ false -> % This is in a choice with typefield components
+ {Name,RestFieldNames} =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(reason),
+ emit([indent(2),"{",{curr,tmpterm},", ",{next,bytes},
+ "} = ?RT_PER:decode_open_type(",{curr,bytes},
+ ", []),",nl]),
+ emit([indent(2),"case (catch ObjFun(",
+ {asis,Name},
+ ",",{curr,tmpterm},",telltype,",
+ {asis,RestFieldNames},")) of", nl]),
+ emit([indent(4),"{'EXIT',",{curr,reason},"} ->",nl]),
+ emit([indent(6),"exit({'Type not ",
+ "compatible with table constraint', ",
+ {curr,reason},"});",nl]),
+ asn1ct_name:new(tmpterm),
+ emit([indent(4),"{",{curr,tmpterm},", _} ->",nl]),
+ emit([indent(6),"{",Cname,", {",{curr,tmpterm},", ",
+ {next,bytes},"}}",nl]),
+ emit([indent(2),"end"]),
+ [];
+ {"got objfun through args","ObjFun"} ->
+ %% this is when the generated code gots the
+ %% objfun though arguments on function
+ %% invocation.
+ {Name,RestFieldNames} =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+ emit(["?RT_PER:decode_open_type(",{curr,bytes},
+ ", []),",nl]),
+ emit([{curr,term}," =",nl,
+ " case (catch ObjFun(",{asis,Name},",",
+ {curr,tmpterm},",telltype,",
+ {asis,RestFieldNames},")) of", nl]),
+ emit([" {'EXIT',",{curr,reason},"} ->",nl]),
+ emit([indent(6),"exit({'Type not ",
+ "compatible with table constraint', ",
+ {curr,reason},"});",nl]),
+ asn1ct_name:new(tmpterm),
+ emit([indent(4),"{",{curr,tmpterm},", _} ->",nl]),
+ emit([indent(6),{curr,tmpterm},nl]),
+ emit([indent(2),"end"]),
+ [];
+ _ ->
+ emit({"?RT_PER:decode_open_type(",{curr,bytes},
+ ", [])"}),
+ RefedFieldName =
+ (Type#type.def)#'ObjectClassFieldType'.fieldname,
+% asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info),
+ [{Cname,RefedFieldName,
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ get_components_prop()}]
+ end;
+ {objectfield,PrimFieldName1,PFNList} ->
+ emit({"?RT_PER:decode_open_type(",{curr,bytes},", [])"}),
+ [{Cname,{PrimFieldName1,PFNList},
+ asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ get_components_prop()}];
+ _ ->
+ CurrMod = get(currmod),
+ case asn1ct_gen:type(Atype) of
+ #'Externaltypereference'{module=CurrMod,type=EType} ->
+ emit({"'dec_",EType,"'(",BytesVar,",telltype)"});
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ emit({"'",Mod,"':'dec_",EType,"'(",BytesVar,
+ ",telltype)"});
+ {primitive,bif} ->
+ case Atype of
+ {fixedtypevaluefield,_,Btype} ->
+ Ctgenmod:gen_dec_prim(per,Btype,
+ BytesVar);
+ _ ->
+ Ctgenmod:gen_dec_prim(per,Type,
+ BytesVar)
+ end;
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OpenType} ->
+ Ctgenmod:gen_dec_prim(per,#type{def=OpenType},
+ BytesVar);
+ _ ->
+ Ctgenmod:gen_dec_prim(per,Type,
+ BytesVar)
+ end;
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(",BytesVar,",telltype)"});
+ {notype,_} ->
+ emit({"'dec_",Atype,"'(",BytesVar,",telltype)"});
+ {constructed,bif} ->
+ NewTypename = [Cname|TopType],
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(",BytesVar,", telltype, ObjFun)"});
+ _ ->
+ emit({"'dec_",asn1ct_gen:list2name(NewTypename),
+ "'(",BytesVar,", telltype)"})
+ end
+ end,
+ case DecInfObj of
+ {Cname,{_,OSet,UniqueFName,ValIndex}} ->
+ Term = asn1ct_gen:mk_var(asn1ct_name:curr(term)),
+ ValueMatch = value_match(ValIndex,Term),
+ emit({",",nl,"ObjFun = 'getdec_",OSet,"'(",
+ {asis,UniqueFName},", ",ValueMatch,")"});
+ _ ->
+ ok
+ end,
+ []
+ end,
+ case Ext of
+ {ext,Ep2,_} when Pos >= Ep2 ->
+ emit([", {TmpValx",Pos,",Trem",Pos,"}",nl,"end"]);
+ _ -> true
+ end,
+ %% Prepare return value
+ case DecInfObj of
+ {Cname,ObjSet} ->
+ {[{ObjSet,Cname,asn1ct_gen:mk_var(asn1ct_name:curr(term))}],
+ SaveBytes};
+ _ ->
+ {[],SaveBytes}
+ end.
+
+gen_enc_choice(TopType,CompList,Ext) ->
+ gen_enc_choice_tag(CompList, [], Ext),
+ emit({com,nl}),
+ emit({"case element(1,Val) of",nl}),
+ gen_enc_choice2(TopType, CompList, Ext),
+ emit({nl,"end"}).
+
+gen_enc_choice_tag({C1,C2},_,_) ->
+ N1 = get_name_list(C1),
+ N2 = get_name_list(C2),
+ emit(["?RT_PER:set_choice(element(1,Val),",
+ {asis,{N1,N2}},", ",{asis,{length(N1),length(N2)}},")"]);
+gen_enc_choice_tag(C,_,_) ->
+ N = get_name_list(C),
+ emit(["?RT_PER:set_choice(element(1,Val),",
+ {asis,N},", ",{asis,length(N)},")"]).
+
+get_name_list(L) ->
+ get_name_list(L,[]).
+
+get_name_list([#'ComponentType'{name=Name}|T], Acc) ->
+ get_name_list(T,[Name|Acc]);
+get_name_list([], Acc) ->
+ lists:reverse(Acc).
+
+%gen_enc_choice_tag([H|T],Acc,Ext) when record(H,'ComponentType') ->
+% gen_enc_choice_tag(T,[H#'ComponentType'.name|Acc],Ext);
+%gen_enc_choice_tag([H|T],Acc,Ext) -> % skip EXTENSIONMARK
+% gen_enc_choice_tag(T,Acc,Ext);
+%gen_enc_choice_tag([],Acc,Ext) ->
+% Length = length(Acc),
+% emit({"?RT_PER:set_choice(element(1,Val),",{asis,Length},",",
+% {asis,lists:reverse(Acc)},",",{asis,Ext},")"}),
+% Length.
+
+gen_enc_choice2(TopType, {L1,L2}, Ext) ->
+ gen_enc_choice2(TopType, L1 ++ L2, 0, Ext);
+gen_enc_choice2(TopType, L, Ext) ->
+ gen_enc_choice2(TopType, L, 0, Ext).
+
+gen_enc_choice2(TopType,[H1,H2|T], Pos, Ext)
+when record(H1,'ComponentType'), record(H2,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ EncObj =
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% false;
+% _ ->
+% {no_attr,"ObjFun"}
+% end,
+ case asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation) of
+ no -> false;
+ _ -> {no_attr,"ObjFun"}
+ end,
+ emit({{asis,Cname}," ->",nl}),
+ gen_enc_line(TopType,Cname,Type,"element(2,Val)", Pos+1,EncObj,Ext),
+ emit({";",nl}),
+ gen_enc_choice2(TopType,[H2|T], Pos+1, Ext);
+gen_enc_choice2(TopType,[H1|T], Pos, Ext) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ EncObj =
+% case asn1ct_gen:get_constraint(Type#type.constraint,
+% tableconstraint_info) of
+% no ->
+% false;
+% _ ->
+% {no_attr,"ObjFun"}
+% end,
+ case asn1ct_gen:get_constraint(Type#type.constraint,
+ componentrelation) of
+ no -> false;
+ _ -> {no_attr,"ObjFun"}
+ end,
+ emit({{asis,H1#'ComponentType'.name}," ->",nl}),
+ gen_enc_line(TopType,Cname,Type,"element(2,Val)", Pos+1,EncObj,Ext),
+ gen_enc_choice2(TopType,T, Pos+1, Ext);
+gen_enc_choice2(_,[], _, _) ->
+ true.
+
+gen_dec_choice(TopType,CompList,{ext,Pos,NumExt}) ->
+ emit({"{Ext,",{curr,bytes},"} = ?RT_PER:getbit(Bytes),",nl}),
+ asn1ct_name:new(bytes),
+ gen_dec_choice1(TopType,CompList,{ext,Pos,NumExt});
+gen_dec_choice(TopType,CompList,noext) ->
+ gen_dec_choice1(TopType,CompList,noext).
+
+gen_dec_choice1(TopType,CompList,noext) ->
+ emit({"{Choice,",{curr,bytes},
+ "} = ?RT_PER:getchoice(",{prev,bytes},",",
+ length(CompList),", 0),",nl}),
+ emit({"{Cname,{Val,NewBytes}} = case Choice of",nl}),
+ gen_dec_choice2(TopType,CompList,noext),
+ emit({nl,"end,",nl}),
+ emit({nl,"{{Cname,Val},NewBytes}"});
+gen_dec_choice1(TopType,{RootList,ExtList},Ext) ->
+ NewList = RootList ++ ExtList,
+ gen_dec_choice1(TopType, NewList, Ext);
+gen_dec_choice1(TopType,CompList,{ext,ExtPos,ExtNum}) ->
+ emit({"{Choice,",{curr,bytes},
+ "} = ?RT_PER:getchoice(",{prev,bytes},",",
+ length(CompList)-ExtNum,",Ext ),",nl}),
+ emit({"{Cname,{Val,NewBytes}} = case Choice + Ext*",ExtPos-1," of",nl}),
+ gen_dec_choice2(TopType,CompList,{ext,ExtPos,ExtNum}),
+ emit([";",nl,"_ -> {asn1_ExtAlt, ?RT_PER:decode_open_type(",{curr,bytes},",[])}"]),
+ emit({nl,"end,",nl}),
+ emit({nl,"{{Cname,Val},NewBytes}"}).
+
+
+gen_dec_choice2(TopType,L,Ext) ->
+ gen_dec_choice2(TopType,L,0,Ext).
+
+gen_dec_choice2(TopType,[H1,H2|T],Pos,Ext)
+when record(H1,'ComponentType'), record(H2,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ case Type#type.def of
+ #'ObjectClassFieldType'{type={typefield,_}} ->
+ emit({Pos," -> ",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext),
+ emit({";",nl});
+ _ ->
+ emit({Pos," -> {",{asis,Cname},",",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext),
+ emit({"};",nl})
+ end,
+ gen_dec_choice2(TopType,[H2|T],Pos+1,Ext);
+gen_dec_choice2(TopType,[H1,_H2|T],Pos,Ext) when record(H1,'ComponentType') ->
+ gen_dec_choice2(TopType,[H1|T],Pos,Ext); % skip extensionmark
+gen_dec_choice2(TopType,[H1|T],Pos,Ext) when record(H1,'ComponentType') ->
+ Cname = H1#'ComponentType'.name,
+ Type = H1#'ComponentType'.typespec,
+ case Type#type.def of
+ #'ObjectClassFieldType'{type={typefield,_}} ->
+ emit({Pos," -> ",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext);
+ _ ->
+ emit({Pos," -> {",{asis,Cname},",",nl}),
+ wrap_gen_dec_line(H1,TopType,Cname,Type,Pos+1,false,Ext),
+ emit("}")
+ end,
+ gen_dec_choice2(TopType,[T],Pos+1);
+gen_dec_choice2(TopType,[_|T],Pos,Ext) ->
+ gen_dec_choice2(TopType,T,Pos,Ext);% skip extensionmark
+gen_dec_choice2(_,[],Pos,_) ->
+ Pos.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+gen_encode_prim_wrapper(CtgenMod,Erule,Cont,DoTag,Value) ->
+% put(component_type,true), % add more info in component_type
+ CtgenMod:gen_encode_prim(Erule,Cont,DoTag,Value).
+% erase(component_type).
+
+make_element(I,Val,Cname) ->
+ case lists:member(optimize,get(encoding_options)) of
+ false ->
+ io_lib:format("?RT_PER:cindex(~w,~s,~w)",[I,Val,Cname]);
+ _ ->
+ io_lib:format("element(~w,~s)",[I,Val])
+ end.
+
+wrap_gen_dec_line(C,TopType,Cname,Type,Pos,DIO,Ext) ->
+ put(component_type,{true,C}),
+ gen_dec_line(TopType,Cname,Type,Pos,DIO,Ext),
+ erase(component_type).
+
+get_components_prop() ->
+ case get(component_type) of
+ undefined ->
+ mandatory;
+ {true,#'ComponentType'{prop=Prop}} -> Prop
+ end.
+
+
+value_match(Index,Value) when atom(Value) ->
+ value_match(Index,atom_to_list(Value));
+value_match([],Value) ->
+ Value;
+value_match([{VI,_}|VIs],Value) ->
+ value_match1(Value,VIs,lists:concat(["element(",VI,","]),1).
+value_match1(Value,[],Acc,Depth) ->
+ Acc ++ Value ++ lists:concat(lists:duplicate(Depth,")"));
+value_match1(Value,[{VI,_}|VIs],Acc,Depth) ->
+ value_match1(Value,VIs,Acc++lists:concat(["element(",VI,","]),Depth+1).
+
+notice_value_match() ->
+ Module = get(currmod),
+ put(value_match,{true,Module}).
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen.erl
new file mode 100644
index 0000000000..e4a0b1fd9a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen.erl
@@ -0,0 +1,1664 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_gen).
+
+-include("asn1_records.hrl").
+%%-compile(export_all).
+-export([pgen_exports/3,
+ pgen_hrl/4,
+ gen_head/3,
+ demit/1,
+ emit/1,
+ fopen/2,
+ get_inner/1,type/1,def_to_tag/1,prim_bif/1,
+ type_from_object/1,
+ get_typefromobject/1,get_fieldcategory/2,
+ get_classfieldcategory/2,
+ list2name/1,
+ list2rname/1,
+ constructed_suffix/2,
+ unify_if_string/1,
+ gen_check_call/7,
+ get_constraint/2,
+ insert_once/2,
+ rt2ct_suffix/1,rt2ct_suffix/0]).
+-export([pgen/4,pgen_module/5,mk_var/1, un_hyphen_var/1]).
+-export([gen_encode_constructed/4,gen_decode_constructed/4]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber | ber_bin | per_bin
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+pgen_module(OutFile,Erules,Module,TypeOrVal,Indent) ->
+ put(outfile,OutFile),
+ HrlGenerated = asn1ct_gen:pgen_hrl(Erules,Module,TypeOrVal,Indent),
+ asn1ct_name:start(),
+ ErlFile = lists:concat([OutFile,".erl"]),
+ Fid = asn1ct_gen:fopen(ErlFile,write),
+ put(gen_file_out,Fid),
+ asn1ct_gen:gen_head(Erules,Module,HrlGenerated),
+ pgen_exports(Erules,Module,TypeOrVal),
+ pgen_dispatcher(Erules,Module,TypeOrVal),
+ pgen_info(Erules,Module),
+ pgen_typeorval(wrap_ber(Erules),Module,TypeOrVal),
+ pgen_partial_incomplete_decode(Erules),
+% gen_vars(asn1_db:mod_to_vars(Module)),
+% gen_tag_table(AllTypes),
+ file:close(Fid),
+ io:format("--~p--~n",[{generated,ErlFile}]).
+
+
+pgen_typeorval(Erules,Module,{Types,Values,_Ptypes,_Classes,Objects,ObjectSets}) ->
+ pgen_types(Erules,Module,Types),
+ pgen_values(Erules,Module,Values),
+ pgen_objects(Erules,Module,Objects),
+ pgen_objectsets(Erules,Module,ObjectSets),
+ case catch lists:member(der,get(encoding_options)) of
+ true ->
+ pgen_check_defaultval(Erules,Module);
+ _ -> ok
+ end,
+ pgen_partial_decode(Erules,Module).
+
+pgen_values(_,_,[]) ->
+ true;
+pgen_values(Erules,Module,[H|T]) ->
+ Valuedef = asn1_db:dbget(Module,H),
+ gen_value(Valuedef),
+ pgen_values(Erules,Module,T).
+
+pgen_types(_,Module,[]) ->
+ gen_value_match(Module),
+ true;
+pgen_types(Erules,Module,[H|T]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Typedef = asn1_db:dbget(Module,H),
+ Rtmod:gen_encode(Erules,Typedef),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Typedef),
+ pgen_types(Erules,Module,T).
+
+pgen_objects(_,_,[]) ->
+ true;
+pgen_objects(Erules,Module,[H|T]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Typedef = asn1_db:dbget(Module,H),
+ Rtmod:gen_obj_code(Erules,Module,Typedef),
+ pgen_objects(Erules,Module,T).
+
+pgen_objectsets(_,_,[]) ->
+ true;
+pgen_objectsets(Erules,Module,[H|T]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ TypeDef = asn1_db:dbget(Module,H),
+ Rtmod:gen_objectset_code(Erules,TypeDef),
+ pgen_objectsets(Erules,Module,T).
+
+pgen_check_defaultval(Erules,Module) ->
+ CheckObjects = ets:tab2list(check_functions),
+ case get(asndebug) of
+ true ->
+ FileName = lists:concat([Module,'.table']),
+ {ok,IoDevice} = file:open(FileName,[write]),
+ Fun =
+ fun(X)->
+ io:format(IoDevice,"~n~n************~n~n~p~n~n*****"
+ "********~n~n",[X])
+ end,
+ lists:foreach(Fun,CheckObjects),
+ file:close(IoDevice);
+ _ -> ok
+ end,
+ gen_check_defaultval(Erules,Module,CheckObjects).
+
+pgen_partial_decode(Erules,Module) ->
+ pgen_partial_inc_dec(Erules,Module),
+ pgen_partial_dec(Erules,Module).
+
+pgen_partial_inc_dec(Erules,Module) ->
+% io:format("Start partial incomplete decode gen?~n"),
+ case asn1ct:get_gen_state_field(inc_type_pattern) of
+ undefined ->
+% io:format("Partial incomplete decode gen not started:�~w~n",[asn1ct:get_gen_state_field(active)]),
+ ok;
+% [] ->
+% ok;
+ ConfList ->
+ PatternLists=lists:map(fun({_,P}) -> P end,ConfList),
+ pgen_partial_inc_dec1(Erules,Module,PatternLists),
+ gen_partial_inc_dec_refed_funcs(Erules)
+ end.
+
+%% pgen_partial_inc_dec1 generates a function of the toptype in each
+%% of the partial incomplete decoded types.
+pgen_partial_inc_dec1(Erules,Module,[P|Ps]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ TopTypeName = asn1ct:partial_inc_dec_toptype(P),
+ TypeDef=asn1_db:dbget(Module,TopTypeName),
+ asn1ct_name:clear(),
+ asn1ct:update_gen_state(namelist,P),
+ asn1ct:update_gen_state(active,true),
+ asn1ct:update_gen_state(prefix,"dec-inc-"),
+ Rtmod:gen_decode(Erules,TypeDef),
+%% asn1ct:update_gen_state(namelist,tl(P)), %%
+ gen_dec_part_inner_constr(Erules,TypeDef,[TopTypeName]),
+ pgen_partial_inc_dec1(Erules,Module,Ps);
+pgen_partial_inc_dec1(_,_,[]) ->
+ ok.
+
+gen_partial_inc_dec_refed_funcs(Erule) when Erule == ber_bin_v2 ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erule),
+ rt2ct_suffix(Erule)])),
+ case asn1ct:next_refed_func() of
+ [] ->
+ ok;
+ {#'Externaltypereference'{module=M,type=Name},Pattern} ->
+ TypeDef = asn1_db:dbget(M,Name),
+ asn1ct:update_gen_state(namelist,Pattern),
+ Rtmod:gen_inc_decode(Erule,TypeDef),
+ gen_dec_part_inner_constr(Erule,TypeDef,[Name]),
+ gen_partial_inc_dec_refed_funcs(Erule);
+ _ ->
+ gen_partial_inc_dec_refed_funcs(Erule)
+ end;
+gen_partial_inc_dec_refed_funcs(_) ->
+ ok.
+
+pgen_partial_dec(_Erules,_Module) ->
+ ok. %%%% implement later
+
+%% generate code for all inner types that are called from the top type
+%% of the partial incomplete decode
+gen_dec_part_inner_constr(Erules,TypeDef,TypeName) ->
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' ->
+ #'SET'{components=Components} = Def#type.def,
+ gen_dec_part_inner_types(Erules,Components,TypeName);
+ %% Continue generate the inner of each component
+ 'SEQUENCE' ->
+ #'SEQUENCE'{components=Components} = Def#type.def,
+ gen_dec_part_inner_types(Erules,Components,TypeName);
+ 'CHOICE' ->
+ {_,Components} = Def#type.def,
+ gen_dec_part_inner_types(Erules,Components,TypeName);
+ 'SEQUENCE OF' ->
+ %% this and next case must be the last component in the
+ %% partial decode chain here. Not likely that this occur.
+ {_,Type} = Def#type.def,
+ NameSuffix = constructed_suffix(InnerType,Type#type.def),
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
+%% gen_types(Erules,[NameSuffix|Typename],Type);
+ 'SET OF' ->
+ {_,Type} = Def#type.def,
+ NameSuffix = constructed_suffix(InnerType,Type#type.def),
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,[NameSuffix|TypeName],Type);
+ _ ->
+ ok
+ end.
+
+gen_dec_part_inner_types(Erules,[ComponentType|Rest],TypeName) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,TypeName,ComponentType),
+ gen_dec_part_inner_types(Erules,Rest,TypeName);
+gen_dec_part_inner_types(Erules,{Comps1,Comps2},TypeName)
+ when list(Comps1),list(Comps2) ->
+ gen_dec_part_inner_types(Erules,Comps1 ++ Comps2,TypeName);
+gen_dec_part_inner_types(_,[],_) ->
+ ok.
+
+
+pgen_partial_incomplete_decode(Erule) ->
+ case asn1ct:get_gen_state_field(active) of
+ true ->
+ pgen_partial_incomplete_decode1(Erule),
+ asn1ct:reset_gen_state();
+ _ ->
+ ok
+ end.
+pgen_partial_incomplete_decode1(ber_bin_v2) ->
+ case asn1ct:read_config_data(partial_incomplete_decode) of
+ undefined ->
+ ok;
+ Data ->
+ lists:foreach(fun emit_partial_incomplete_decode/1,Data)
+ end,
+ GeneratedFs= asn1ct:get_gen_state_field(gen_refed_funcs),
+% io:format("GeneratedFs :~n~p~n",[GeneratedFs]),
+ gen_part_decode_funcs(GeneratedFs,0);
+pgen_partial_incomplete_decode1(_) -> ok.
+
+emit_partial_incomplete_decode({FuncName,TopTypeName,Pattern}) ->
+ emit([{asis,FuncName},"(Bytes) ->",nl,
+ " decode_partial_incomplete(",{asis,TopTypeName},",Bytes,",{asis,Pattern},").",nl]);
+emit_partial_incomplete_decode(D) ->
+ throw({error,{asn1,{"bad data in asn1config file",D}}}).
+
+gen_part_decode_funcs([Data={Name,_,_,Type}|GeneratedFs],N) ->
+ InnerType =
+ case Type#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ get_inner(Type#type.def)
+ end,
+ WhatKind = type(InnerType),
+ TypeName=list2name(Name),
+ if
+ N > 0 -> emit([";",nl]);
+ true -> ok
+ end,
+ emit(["decode_inc_disp('",TypeName,"',Data) ->",nl]),
+ gen_part_decode_funcs(WhatKind,TypeName,Data),
+ gen_part_decode_funcs(GeneratedFs,N+1);
+gen_part_decode_funcs([_H|T],N) ->
+ gen_part_decode_funcs(T,N);
+gen_part_decode_funcs([],N) ->
+ if
+ N > 0 ->
+ .emit([".",nl]);
+ true ->
+ ok
+ end.
+
+gen_part_decode_funcs(#'Externaltypereference'{module=M,type=T},
+ _TypeName,Data) ->
+ #typedef{typespec=TS} = asn1_db:dbget(M,T),
+ InnerType =
+ case TS#type.def of
+ #'ObjectClassFieldType'{type=OCFTType} ->
+ OCFTType;
+ _ ->
+ get_inner(TS#type.def)
+ end,
+ WhatKind = type(InnerType),
+ gen_part_decode_funcs(WhatKind,[T],Data);
+gen_part_decode_funcs({constructed,bif},TypeName,
+ {_Name,parts,Tag,_Type}) ->
+ emit([" case Data of",nl,
+ " L when list(L) ->",nl,
+ " 'dec_",TypeName,"'(lists:map(fun(X)->element(1,?RT_BER:decode(X)) end,L),",{asis,Tag},");",nl,
+ " _ ->",nl,
+ " [Res] = 'dec_",TypeName,"'([Data],",{asis,Tag},"),",nl,
+ " Res",nl,
+ " end"]);
+gen_part_decode_funcs(WhatKind,_TypeName,{_Name,parts,_Tag,_Type}) ->
+ throw({error,{asn1,{"only SEQUENCE OF/SET OF may have the partial incomplete directive 'parts'.",WhatKind}}});
+gen_part_decode_funcs({constructed,bif},TypeName,
+ {_Name,undecoded,Tag,_Type}) ->
+ emit([" 'dec_",TypeName,"'(Data,",{asis,Tag},")"]);
+gen_part_decode_funcs({primitive,bif},_TypeName,
+ {_Name,undecoded,Tag,Type}) ->
+ % Argument no 6 is 0, i.e. bit 6 for primitive encoding.
+ asn1ct_gen_ber_bin_v2:gen_dec_prim(ber_bin_v2,Type,"Data",Tag,[],0,", mandatory, ");
+gen_part_decode_funcs(WhatKind,_TypeName,{_,Directive,_,_}) ->
+ throw({error,{asn1,{"Not implemented yet",WhatKind," partial incomplete directive:",Directive}}}).
+
+gen_types(Erules,Tname,{RootList,ExtList}) when list(RootList) ->
+ gen_types(Erules,Tname,RootList),
+ gen_types(Erules,Tname,ExtList);
+gen_types(Erules,Tname,[{'EXTENSIONMARK',_,_}|Rest]) ->
+ gen_types(Erules,Tname,Rest);
+gen_types(Erules,Tname,[ComponentType|Rest]) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_encode(Erules,Tname,ComponentType),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Tname,ComponentType),
+ gen_types(Erules,Tname,Rest);
+gen_types(_,_,[]) ->
+ true;
+gen_types(Erules,Tname,Type) when record(Type,type) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_gen_",erule(Erules),
+ rt2ct_suffix(Erules)])),
+ asn1ct_name:clear(),
+ Rtmod:gen_encode(Erules,Tname,Type),
+ asn1ct_name:clear(),
+ Rtmod:gen_decode(Erules,Tname,Type).
+
+gen_value_match(Module) ->
+ case get(value_match) of
+ {true,Module} ->
+ emit(["value_match([{Index,Cname}|Rest],Value) ->",nl,
+ " Value2 =",nl,
+ " case element(Index,Value) of",nl,
+ " {Cname,Val2} -> Val2;",nl,
+ " X -> X",nl,
+ " end,",nl,
+ " value_match(Rest,Value2);",nl,
+ "value_match([],Value) ->",nl,
+ " Value.",nl]);
+ _ -> ok
+ end,
+ put(value_match,undefined).
+
+gen_check_defaultval(Erules,Module,[{Name,Type}|Rest]) ->
+ gen_check_func(Name,Type),
+ gen_check_defaultval(Erules,Module,Rest);
+gen_check_defaultval(_,_,[]) ->
+ ok.
+
+gen_check_func(Name,FType = #type{def=Def}) ->
+ emit({Name,"(V,asn1_DEFAULT) ->",nl," true;",nl}),
+ emit({Name,"(V,V) ->",nl," true;",nl}),
+ emit({Name,"(V,{_,V}) ->",nl," true;",nl}),
+ case Def of
+ {'SEQUENCE OF',Type} ->
+ gen_check_sof(Name,'SEQOF',Type);
+ {'SET OF',Type} ->
+ gen_check_sof(Name,'SETOF',Type);
+ #'SEQUENCE'{components=Components} ->
+ gen_check_sequence(Name,Components);
+ #'SET'{components=Components} ->
+ gen_check_sequence(Name,Components);
+ {'CHOICE',Components} ->
+ gen_check_choice(Name,Components);
+ #'Externaltypereference'{type=T} ->
+ emit({Name,"(DefaultValue,Value) ->",nl}),
+ emit({" ",list2name([T,check]),"(DefaultValue,Value).",nl});
+ MaybePrim ->
+ InnerType = get_inner(MaybePrim),
+ case type(InnerType) of
+ {primitive,bif} ->
+ emit({Name,"(DefaultValue,Value) ->",nl," "}),
+ gen_prim_check_call(InnerType,"DefaultValue","Value",
+ FType),
+ emit({".",nl,nl});
+ _ ->
+ throw({asn1_error,{unknown,type,MaybePrim}})
+ end
+ end.
+
+gen_check_sof(Name,SOF,Type) ->
+ NewName = list2name([sorted,Name]),
+ emit({Name,"(V1,V2) ->",nl}),
+ emit({" ",NewName,"(lists:sort(V1),lists:sort(V2)).",nl,nl}),
+ emit({NewName,"([],[]) ->",nl," true;",nl}),
+ emit({NewName,"([DV|DVs],[V|Vs]) ->",nl," "}),
+ InnerType = get_inner(Type#type.def),
+ case type(InnerType) of
+ {primitive,bif} ->
+ gen_prim_check_call(InnerType,"DV","V",Type),
+ emit({",",nl});
+ {constructed,bif} ->
+ emit({list2name([SOF,Name]),"(DV, V),",nl});
+ #'Externaltypereference'{type=T} ->
+ emit({list2name([T,check]),"(DV,V),",nl})
+ end,
+ emit({" ",NewName,"(DVs,Vs).",nl,nl}).
+
+gen_check_sequence(Name,Components) ->
+ emit({Name,"(DefaultValue,Value) ->",nl}),
+ gen_check_sequence(Name,Components,1).
+gen_check_sequence(Name,[#'ComponentType'{name=N,typespec=Type}|Cs],Num) ->
+ InnerType = get_inner(Type#type.def),
+% NthDefV = lists:concat(["lists:nth(",Num,",DefaultValue)"]),
+ NthDefV = ["element(",Num+1,",DefaultValue)"],
+% NthV = lists:concat(["lists:nth(",Num,",Value)"]),
+ NthV = ["element(",Num+1,",Value)"],
+ gen_check_func_call(Name,Type,InnerType,NthDefV,NthV,N),
+ case Cs of
+ [] ->
+ emit({".",nl,nl});
+ _ ->
+ emit({",",nl}),
+ gen_check_sequence(Name,Cs,Num+1)
+ end;
+gen_check_sequence(_,[],_) ->
+ ok.
+
+gen_check_choice(Name,CList=[#'ComponentType'{}|_Cs]) ->
+ emit({Name,"({Id,DefaultValue},{Id,Value}) ->",nl}),
+ emit({" case Id of",nl}),
+ gen_check_choice_components(Name,CList,1).
+
+gen_check_choice_components(_,[],_)->
+ ok;
+gen_check_choice_components(Name,[#'ComponentType'{name=N,typespec=Type}|
+ Cs],Num) ->
+ Ind6 = " ",
+ InnerType = get_inner(Type#type.def),
+% DefVal = ["element(2,lists:nth(",Num,",DefaultValue))"],
+ emit({Ind6,N," ->",nl,Ind6}),
+ gen_check_func_call(Name,Type,InnerType,{var,"defaultValue"},
+ {var,"value"},N),
+ case Cs of
+ [] ->
+ emit({nl," end.",nl,nl});
+ _ ->
+ emit({";",nl}),
+ gen_check_choice_components(Name,Cs,Num+1)
+ end.
+
+gen_check_func_call(Name,Type,InnerType,DefVal,Val,N) ->
+ case type(InnerType) of
+ {primitive,bif} ->
+ emit(" "),
+ gen_prim_check_call(InnerType,DefVal,Val,Type);
+ #'Externaltypereference'{type=T} ->
+ emit({" ",list2name([T,check]),"(",DefVal,",",Val,")"});
+ _ ->
+ emit({" ",list2name([N,Name]),"(",DefVal,",",Val,")"})
+ end.
+
+
+%% VARIOUS GENERATOR STUFF
+%% *************************************************
+%%**************************************************
+
+mk_var(X) when atom(X) ->
+ list_to_atom(mk_var(atom_to_list(X)));
+
+mk_var([H|T]) ->
+ [H-32|T].
+
+%% Since hyphens are allowed in ASN.1 names, it may occur in a
+%% variable to. Turn a hyphen into a under-score sign.
+un_hyphen_var(X) when atom(X) ->
+ list_to_atom(un_hyphen_var(atom_to_list(X)));
+un_hyphen_var([45|T]) ->
+ [95|un_hyphen_var(T)];
+un_hyphen_var([H|T]) ->
+ [H|un_hyphen_var(T)];
+un_hyphen_var([]) ->
+ [].
+
+%% Generate value functions ***************
+%% ****************************************
+%% Generates a function 'V'/0 for each Value V defined in the ASN.1 module
+%% the function returns the value in an Erlang representation which can be
+%% used as input to the runtime encode functions
+
+gen_value(Value) when record(Value,valuedef) ->
+%% io:format(" ~w ",[Value#valuedef.name]),
+ emit({"'",Value#valuedef.name,"'() ->",nl}),
+ V = Value#valuedef.value,
+ emit([{asis,V},".",nl,nl]).
+
+gen_encode_constructed(Erules,Typename,InnerType,D) when record(D,type) ->
+
+ Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ case InnerType of
+ 'SET' ->
+ Rtmod:gen_encode_set(Erules,Typename,D),
+ #'SET'{components=Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'SEQUENCE' ->
+ Rtmod:gen_encode_sequence(Erules,Typename,D),
+ #'SEQUENCE'{components=Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'CHOICE' ->
+ Rtmod:gen_encode_choice(Erules,Typename,D),
+ {_,Components} = D#type.def,
+ gen_types(Erules,Typename,Components);
+ 'SEQUENCE OF' ->
+ Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ gen_types(Erules,[NameSuffix|Typename],Type);
+ 'SET OF' ->
+ Rtmod:gen_encode_sof(Erules,Typename,InnerType,D),
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ gen_types(Erules,[NameSuffix|Typename],Type);
+ _ ->
+ exit({nyi,InnerType})
+ end;
+gen_encode_constructed(Erules,Typename,InnerType,D)
+ when record(D,typedef) ->
+ gen_encode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
+
+gen_decode_constructed(Erules,Typename,InnerType,D) when record(D,type) ->
+ Rtmod = list_to_atom(lists:concat(["asn1ct_constructed_",erule(Erules)])),
+ asn1ct:step_in_constructed(), %% updates namelist for incomplete
+ %% partial decode
+ case InnerType of
+ 'SET' ->
+ Rtmod:gen_decode_set(Erules,Typename,D);
+ 'SEQUENCE' ->
+ Rtmod:gen_decode_sequence(Erules,Typename,D);
+ 'CHOICE' ->
+ Rtmod:gen_decode_choice(Erules,Typename,D);
+ 'SEQUENCE OF' ->
+ Rtmod:gen_decode_sof(Erules,Typename,InnerType,D);
+ 'SET OF' ->
+ Rtmod:gen_decode_sof(Erules,Typename,InnerType,D);
+ _ ->
+ exit({nyi,InnerType})
+ end;
+
+
+gen_decode_constructed(Erules,Typename,InnerType,D) when record(D,typedef) ->
+ gen_decode_constructed(Erules,Typename,InnerType,D#typedef.typespec).
+
+
+pgen_exports(Erules,_Module,{Types,Values,_,_,Objects,ObjectSets}) ->
+ emit({"-export([encoding_rule/0]).",nl}),
+ case Types of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ case Erules of
+ ber ->
+ gen_exports1(Types,"enc_",2);
+ ber_bin ->
+ gen_exports1(Types,"enc_",2);
+ ber_bin_v2 ->
+ gen_exports1(Types,"enc_",2);
+ _ ->
+ gen_exports1(Types,"enc_",1)
+ end,
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",2),
+ case Erules of
+ ber ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",3);
+ ber_bin ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",3);
+ ber_bin_v2 ->
+ emit({"-export([",nl}),
+ gen_exports1(Types,"dec_",2);
+ _ -> ok
+ end
+ end,
+ case Values of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(Values,"",0)
+ end,
+ case Objects of
+ [] -> ok;
+ _ ->
+ case erule(Erules) of
+ per ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",3),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",4);
+ ber_bin_v2 ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",3),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",3);
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"enc_",4),
+ emit({"-export([",nl}),
+ gen_exports1(Objects,"dec_",4)
+ end
+ end,
+ case ObjectSets of
+ [] -> ok;
+ _ ->
+ emit({"-export([",nl}),
+ gen_exports1(ObjectSets,"getenc_",2),
+ emit({"-export([",nl}),
+ gen_exports1(ObjectSets,"getdec_",2)
+ end,
+ emit({"-export([info/0]).",nl}),
+ gen_partial_inc_decode_exports(),
+ emit({nl,nl}).
+
+gen_exports1([F1,F2|T],Prefix,Arity) ->
+ emit({"'",Prefix,F1,"'/",Arity,com,nl}),
+ gen_exports1([F2|T],Prefix,Arity);
+gen_exports1([Flast|_T],Prefix,Arity) ->
+ emit({"'",Prefix,Flast,"'/",Arity,nl,"]).",nl,nl}).
+
+gen_partial_inc_decode_exports() ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ {Data,_} ->
+ gen_partial_inc_decode_exports(Data),
+ emit("-export([decode_part/2]).")
+ end.
+gen_partial_inc_decode_exports([]) ->
+ ok;
+gen_partial_inc_decode_exports([{Name,_,_}|Rest]) ->
+ emit(["-export([",Name,"/1"]),
+ gen_partial_inc_decode_exports1(Rest);
+gen_partial_inc_decode_exports([_|Rest]) ->
+ gen_partial_inc_decode_exports(Rest).
+
+gen_partial_inc_decode_exports1([]) ->
+ emit(["]).",nl]);
+gen_partial_inc_decode_exports1([{Name,_,_}|Rest]) ->
+ emit([", ",Name,"/1"]),
+ gen_partial_inc_decode_exports1(Rest);
+gen_partial_inc_decode_exports1([_|Rest]) ->
+ gen_partial_inc_decode_exports1(Rest).
+
+pgen_dispatcher(Erules,_Module,{[],_Values,_,_,_Objects,_ObjectSets}) ->
+ emit(["encoding_rule() ->",nl]),
+ emit([{asis,Erules},".",nl,nl]);
+pgen_dispatcher(Erules,_Module,{Types,_Values,_,_,_Objects,_ObjectSets}) ->
+ emit(["-export([encode/2,decode/2,encode_disp/2,decode_disp/2]).",nl,nl]),
+ emit(["encoding_rule() ->",nl]),
+ emit([" ",{asis,Erules},".",nl,nl]),
+ Call = case Erules of
+ per -> "?RT_PER:complete(encode_disp(Type,Data))";
+ per_bin -> "?RT_PER:complete(encode_disp(Type,Data))";
+ ber -> "encode_disp(Type,Data)";
+ ber_bin -> "encode_disp(Type,Data)";
+ ber_bin_v2 -> "encode_disp(Type,Data)"
+ end,
+ EncWrap = case Erules of
+ ber -> "wrap_encode(Bytes)";
+ _ -> "Bytes"
+ end,
+ emit(["encode(Type,Data) ->",nl,
+ "case catch ",Call," of",nl,
+ " {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " {Bytes,_Len} ->",nl,
+ " {ok,",EncWrap,"};",nl,
+ " Bytes ->",nl,
+ " {ok,",EncWrap,"}",nl,
+ "end.",nl,nl]),
+
+ case Erules of
+ ber_bin_v2 ->
+ emit(["decode(Type,Data0) ->",nl]),
+ emit(["{Data,_RestBin} = ?RT_BER:decode(Data0",driver_parameter(),"),",nl]);
+ _ ->
+ emit(["decode(Type,Data) ->",nl])
+ end,
+ DecWrap = case Erules of
+ ber -> "wrap_decode(Data)";
+ _ -> "Data"
+ end,
+
+ emit(["case catch decode_disp(Type,",DecWrap,") of",nl,
+ " {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl]),
+ case Erules of
+ ber_bin_v2 ->
+ emit([" Result ->",nl,
+ " {ok,Result}",nl]);
+ _ ->
+ emit([" {X,_Rest} ->",nl,
+ " {ok,X};",nl,
+ " {X,_Rest,_Len} ->",nl,
+ " {ok,X}",nl])
+ end,
+ emit(["end.",nl,nl]),
+
+ gen_decode_partial_incomplete(Erules),
+
+ case Types of
+ [] -> ok;
+ _ ->
+ case Erules of
+ ber ->
+ gen_dispatcher(Types,"encode_disp","enc_",",[]"),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory");
+ ber_bin ->
+ gen_dispatcher(Types,"encode_disp","enc_",",[]"),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory");
+ ber_bin_v2 ->
+ gen_dispatcher(Types,"encode_disp","enc_",""),
+ gen_dispatcher(Types,"decode_disp","dec_",""),
+ gen_partial_inc_dispatcher();
+ _PerOrPer_bin ->
+ gen_dispatcher(Types,"encode_disp","enc_",""),
+ gen_dispatcher(Types,"decode_disp","dec_",",mandatory")
+ end,
+ emit([nl])
+ end,
+ case Erules of
+ ber ->
+ gen_wrapper();
+ _ -> ok
+ end,
+ emit({nl,nl}).
+
+
+gen_decode_partial_incomplete(Erule) when Erule == ber;Erule==ber_bin;
+ Erule==ber_bin_v2 ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ _ ->
+ case Erule of
+ ber_bin_v2 ->
+ EmitCaseClauses =
+ fun() ->
+ emit([" {'EXIT',{error,Reason}} ->",nl,
+ " {error,Reason};",nl,
+ " {'EXIT',Reason} ->",nl,
+ " {error,{asn1,Reason}};",nl,
+ " Result ->",nl,
+ " {ok,Result}",nl,
+ " end.",nl,nl])
+ end,
+ emit(["decode_partial_incomplete(Type,Data0,",
+ "Pattern) ->",nl]),
+ emit([" {Data,_RestBin} =",nl,
+ " ?RT_BER:decode_primitive_",
+ "incomplete(Pattern,Data0),",nl,
+ " case catch decode_partial_inc_disp(Type,",
+ "Data) of",nl]),
+ EmitCaseClauses(),
+ emit(["decode_part(Type,Data0) ->",nl,
+ " {Data,_RestBin} = ?RT_BER:decode(Data0),",nl,
+ " case catch decode_inc_disp(Type,Data) of",nl]),
+ EmitCaseClauses();
+ _ -> ok % add later
+ end
+ end;
+gen_decode_partial_incomplete(_Erule) ->
+ ok.
+
+gen_partial_inc_dispatcher() ->
+ case {asn1ct:read_config_data(partial_incomplete_decode),
+ asn1ct:get_gen_state_field(inc_type_pattern)} of
+ {undefined,_} ->
+ ok;
+ {_,undefined} ->
+ ok;
+ {Data,_} ->
+ gen_partial_inc_dispatcher(Data)
+ end.
+gen_partial_inc_dispatcher([{_FuncName,TopType,_Pattern}|Rest]) ->
+ emit(["decode_partial_inc_disp(",{asis,TopType},",Data) ->",nl,
+ " ",{asis,list_to_atom(lists:concat([dec,"-inc-",TopType]))},
+ "(Data);",nl]),
+ gen_partial_inc_dispatcher(Rest);
+gen_partial_inc_dispatcher([]) ->
+ emit(["decode_partial_inc_disp(Type,_Data) ->",nl,
+ " exit({error,{asn1,{undefined_type,Type}}}).",nl]).
+
+driver_parameter() ->
+ Options = get(encoding_options),
+ case lists:member(driver,Options) of
+ true ->
+ ",driver";
+ _ -> ""
+ end.
+
+gen_wrapper() ->
+ emit(["wrap_encode(Bytes) when list(Bytes) ->",nl,
+ " binary_to_list(list_to_binary(Bytes));",nl,
+ "wrap_encode(Bytes) when binary(Bytes) ->",nl,
+ " binary_to_list(Bytes);",nl,
+ "wrap_encode(Bytes) -> Bytes.",nl,nl]),
+ emit(["wrap_decode(Bytes) when list(Bytes) ->",nl,
+ " list_to_binary(Bytes);",nl,
+ "wrap_decode(Bytes) -> Bytes.",nl]).
+
+gen_dispatcher([F1,F2|T],FuncName,Prefix,ExtraArg) ->
+ emit([FuncName,"('",F1,"',Data) -> '",Prefix,F1,"'(Data",ExtraArg,")",";",nl]),
+ gen_dispatcher([F2|T],FuncName,Prefix,ExtraArg);
+gen_dispatcher([Flast|_T],FuncName,Prefix,ExtraArg) ->
+ emit([FuncName,"('",Flast,"',Data) -> '",Prefix,Flast,"'(Data",ExtraArg,")",";",nl]),
+ emit([FuncName,"(","Type",",_Data) -> exit({error,{asn1,{undefined_type,Type}}}).",nl,nl,nl]).
+
+pgen_info(_Erules,Module) ->
+ Options = get(encoding_options),
+ emit({"info() ->",nl,
+ " [{vsn,'",asn1ct:vsn(),"'},",
+ " {module,'",Module,"'},",
+ " {options,",io_lib:format("~p",[Options]),"}].",nl}).
+
+open_hrl(OutFile,Module) ->
+ File = lists:concat([OutFile,".hrl"]),
+ Fid = fopen(File,write),
+ put(gen_file_out,Fid),
+ gen_hrlhead(Module).
+
+%% EMIT functions ************************
+%% ***************************************
+
+ % debug generation
+demit(Term) ->
+ case get(asndebug) of
+ true -> emit(Term);
+ _ ->true
+ end.
+
+ % always generation
+
+emit({external,_M,T}) ->
+ emit(T);
+
+emit({prev,Variable}) when atom(Variable) ->
+ emit({var,asn1ct_name:prev(Variable)});
+
+emit({next,Variable}) when atom(Variable) ->
+ emit({var,asn1ct_name:next(Variable)});
+
+emit({curr,Variable}) when atom(Variable) ->
+ emit({var,asn1ct_name:curr(Variable)});
+
+emit({var,Variable}) when atom(Variable) ->
+ [Head|V] = atom_to_list(Variable),
+ emit([Head-32|V]);
+
+emit({var,Variable}) ->
+ [Head|V] = Variable,
+ emit([Head-32|V]);
+
+emit({asis,What}) ->
+ format(get(gen_file_out),"~w",[What]);
+
+emit(nl) ->
+ nl(get(gen_file_out));
+
+emit(com) ->
+ emit(",");
+
+emit(tab) ->
+ put_chars(get(gen_file_out)," ");
+
+emit(What) when integer(What) ->
+ put_chars(get(gen_file_out),integer_to_list(What));
+
+emit(What) when list(What), integer(hd(What)) ->
+ put_chars(get(gen_file_out),What);
+
+emit(What) when atom(What) ->
+ put_chars(get(gen_file_out),atom_to_list(What));
+
+emit(What) when tuple(What) ->
+ emit_parts(tuple_to_list(What));
+
+emit(What) when list(What) ->
+ emit_parts(What);
+
+emit(X) ->
+ exit({'cant emit ',X}).
+
+emit_parts([]) -> true;
+emit_parts([H|T]) ->
+ emit(H),
+ emit_parts(T).
+
+format(undefined,X,Y) ->
+ io:format(X,Y);
+format(X,Y,Z) ->
+ io:format(X,Y,Z).
+
+nl(undefined) -> io:nl();
+nl(X) -> io:nl(X).
+
+put_chars(undefined,X) ->
+ io:put_chars(X);
+put_chars(Y,X) ->
+ io:put_chars(Y,X).
+
+fopen(F, Mode) ->
+ case file:open(F, [Mode]) of
+ {ok, Fd} ->
+ Fd;
+ {error, Reason} ->
+ io:format("** Can't open file ~p ~n", [F]),
+ exit({error,Reason})
+ end.
+
+pgen_hrl(Erules,Module,TypeOrVal,_Indent) ->
+ put(currmod,Module),
+ {Types,Values,Ptypes,_,_,_} = TypeOrVal,
+ Ret =
+ case pgen_hrltypes(Erules,Module,Ptypes++Types,0) of
+ 0 ->
+ case Values of
+ [] ->
+ 0;
+ _ ->
+ open_hrl(get(outfile),get(currmod)),
+ pgen_macros(Erules,Module,Values),
+ 1
+ end;
+ X ->
+ pgen_macros(Erules,Module,Values),
+ X
+ end,
+ case Ret of
+ 0 ->
+ 0;
+ Y ->
+ Fid = get(gen_file_out),
+ file:close(Fid),
+ io:format("--~p--~n",
+ [{generated,lists:concat([get(outfile),".hrl"])}]),
+ Y
+ end.
+
+pgen_macros(_,_,[]) ->
+ true;
+pgen_macros(Erules,Module,[H|T]) ->
+ Valuedef = asn1_db:dbget(Module,H),
+ gen_macro(Valuedef),
+ pgen_macros(Erules,Module,T).
+
+pgen_hrltypes(_,_,[],NumRecords) ->
+ NumRecords;
+pgen_hrltypes(Erules,Module,[H|T],NumRecords) ->
+% io:format("records = ~p~n",NumRecords),
+ Typedef = asn1_db:dbget(Module,H),
+ AddNumRecords = gen_record(Typedef,NumRecords),
+ pgen_hrltypes(Erules,Module,T,NumRecords+AddNumRecords).
+
+
+%% Generates a macro for value Value defined in the ASN.1 module
+gen_macro(Value) when record(Value,valuedef) ->
+ emit({"-define('",Value#valuedef.name,"', ",
+ {asis,Value#valuedef.value},").",nl}).
+
+%% Generate record functions **************
+%% Generates an Erlang record for each named and unnamed SEQUENCE and SET in the ASN.1
+%% module. If no SEQUENCE or SET is found there is no .hrl file generated
+
+
+gen_record(Tdef,NumRecords) when record(Tdef,typedef) ->
+ Name = [Tdef#typedef.name],
+ Type = Tdef#typedef.typespec,
+ gen_record(type,Name,Type,NumRecords);
+
+gen_record(Tdef,NumRecords) when record(Tdef,ptypedef) ->
+ Name = [Tdef#ptypedef.name],
+ Type = Tdef#ptypedef.typespec,
+ gen_record(ptype,Name,Type,NumRecords).
+
+gen_record(TorPtype,Name,[#'ComponentType'{name=Cname,typespec=Type}|T],Num) ->
+ Num2 = gen_record(TorPtype,[Cname|Name],Type,Num),
+ gen_record(TorPtype,Name,T,Num2);
+gen_record(TorPtype,Name,{Clist1,Clist2},Num) when list(Clist1), list(Clist2) ->
+ gen_record(TorPtype,Name,Clist1++Clist2,Num);
+gen_record(TorPtype,Name,[_|T],Num) -> % skip EXTENSIONMARK
+ gen_record(TorPtype,Name,T,Num);
+gen_record(_TorPtype,_Name,[],Num) ->
+ Num;
+
+gen_record(TorPtype,Name,Type,Num) when record(Type,type) ->
+ Def = Type#type.def,
+ Rec = case Def of
+ Seq when record(Seq,'SEQUENCE') ->
+ case Seq#'SEQUENCE'.pname of
+ false ->
+ {record,Seq#'SEQUENCE'.components};
+ _Pname when TorPtype == type ->
+ false;
+ _ ->
+ {record,Seq#'SEQUENCE'.components}
+ end;
+ Set when record(Set,'SET') ->
+ case Set#'SET'.pname of
+ false ->
+ {record,Set#'SET'.components};
+ _Pname when TorPtype == type ->
+ false;
+ _ ->
+ {record,Set#'SET'.components}
+ end;
+% {'SET',{_,_CompList}} ->
+% {record,_CompList};
+ {'CHOICE',_CompList} -> {inner,Def};
+ {'SEQUENCE OF',_CompList} -> {['SEQOF'|Name],Def};
+ {'SET OF',_CompList} -> {['SETOF'|Name],Def};
+ _ -> false
+ end,
+ case Rec of
+ false -> Num;
+ {record,CompList} ->
+ case Num of
+ 0 -> open_hrl(get(outfile),get(currmod));
+ _ -> true
+ end,
+ emit({"-record('",list2name(Name),"',{",nl}),
+ RootList = case CompList of
+ _ when list(CompList) ->
+ CompList;
+ {_Rl,_} -> _Rl
+ end,
+ gen_record2(Name,'SEQUENCE',RootList),
+ NewCompList =
+ case CompList of
+ {CompList1,[]} ->
+ emit({"}). % with extension mark",nl,nl}),
+ CompList1;
+ {Tr,ExtensionList2} ->
+ case Tr of
+ [] -> true;
+ _ -> emit({",",nl})
+ end,
+ emit({"%% with extensions",nl}),
+ gen_record2(Name, 'SEQUENCE', ExtensionList2,
+ "", ext),
+ emit({"}).",nl,nl}),
+ Tr ++ ExtensionList2;
+ _ ->
+ emit({"}).",nl,nl}),
+ CompList
+ end,
+ gen_record(TorPtype,Name,NewCompList,Num+1);
+ {inner,{'CHOICE', CompList}} ->
+ gen_record(TorPtype,Name,CompList,Num);
+ {NewName,{_, CompList}} ->
+ gen_record(TorPtype,NewName,CompList,Num)
+ end;
+gen_record(_,_,_,NumRecords) -> % skip CLASS etc for now.
+ NumRecords.
+
+gen_head(Erules,Mod,Hrl) ->
+ {Rtmac,Rtmod} = case Erules of
+ per ->
+ emit({"%% Generated by the Erlang ASN.1 PER-"
+ "compiler version:",asn1ct:vsn(),nl}),
+ {"RT_PER",?RT_PER};
+ ber ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version:",asn1ct:vsn(),nl}),
+ {"RT_BER",?RT_BER_BIN};
+ per_bin ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ %% temporary code to enable rt2ct optimization
+ Options = get(encoding_options),
+ case lists:member(optimize,Options) of
+ true -> {"RT_PER","asn1rt_per_bin_rt2ct"};
+ _ ->
+ {"RT_PER",?RT_PER_BIN}
+ end;
+ ber_bin ->
+ emit({"%% Generated by the Erlang ASN.1 BER-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ {"RT_BER",?RT_BER_BIN};
+ ber_bin_v2 ->
+ emit({"%% Generated by the Erlang ASN.1 BER_V2-"
+ "compiler version, utilizing bit-syntax:",
+ asn1ct:vsn(),nl}),
+ {"RT_BER","asn1rt_ber_bin_v2"}
+ end,
+ emit({"%% Purpose: encoder and decoder to the types in mod ",Mod,nl,nl}),
+ emit({"-module('",Mod,"').",nl}),
+ put(currmod,Mod),
+ %emit({"-compile(export_all).",nl}),
+ case Hrl of
+ 0 -> true;
+ _ ->
+ emit({"-include(\"",Mod,".hrl\").",nl})
+ end,
+ emit(["-define('",Rtmac,"',",Rtmod,").",nl]).
+
+
+gen_hrlhead(Mod) ->
+ emit({"%% Generated by the Erlang ASN.1 compiler version:",asn1ct:vsn(),nl}),
+ emit({"%% Purpose: Erlang record definitions for each named and unnamed",nl}),
+ emit({"%% SEQUENCE and SET, and macro definitions for each value",nl}),
+ emit({"%% definition,in module ",Mod,nl,nl}),
+ emit({nl,nl}).
+
+gen_record2(Name,SeqOrSet,Comps) ->
+ gen_record2(Name,SeqOrSet,Comps,"",noext).
+
+gen_record2(_Name,_SeqOrSet,[],_Com,_Extension) ->
+ true;
+gen_record2(Name,SeqOrSet,[{'EXTENSIONMARK',_,_}|T],Com,Extension) ->
+ gen_record2(Name,SeqOrSet,T,Com,Extension);
+gen_record2(_Name,_SeqOrSet,[H],Com,Extension) ->
+ #'ComponentType'{name=Cname} = H,
+ emit(Com),
+ emit({asis,Cname}),
+ gen_record_default(H, Extension);
+gen_record2(Name,SeqOrSet,[H|T],Com, Extension) ->
+ #'ComponentType'{name=Cname} = H,
+ emit(Com),
+ emit({asis,Cname}),
+ gen_record_default(H, Extension),
+% emit(", "),
+ gen_record2(Name,SeqOrSet,T,", ", Extension).
+
+%gen_record_default(C, ext) ->
+% emit(" = asn1_NOEXTVALUE");
+gen_record_default(#'ComponentType'{prop='OPTIONAL'}, _)->
+ emit(" = asn1_NOVALUE");
+gen_record_default(#'ComponentType'{prop={'DEFAULT',_}}, _)->
+ emit(" = asn1_DEFAULT");
+gen_record_default(_, _) ->
+ true.
+
+gen_check_call(TopType,Cname,Type,InnerType,WhatKind,DefaultValue,Element) ->
+ case WhatKind of
+ {primitive,bif} ->
+ gen_prim_check_call(InnerType,DefaultValue,Element,Type);
+ #'Externaltypereference'{module=M,type=T} ->
+ %% generate function call
+ Name = list2name([T,check]),
+ emit({"'",Name,"'(",DefaultValue,", ",Element,")"}),
+ %% insert in ets table and do look ahead check
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = asn1ct_gen:get_inner(RefType#type.def),
+ case insert_once(check_functions,{Name,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+% case asn1ct_gen:type(InType) of
+% {constructed,bif} ->
+% lookahead_innertype([T],InType,RefType);
+% #'Externaltypereference'{type=TNew} ->
+% lookahead_innertype([TNew],InType,RefType);
+% _ ->
+% ok
+% end;
+ _ ->
+ ok
+ end;
+ {constructed,bif} ->
+ NameList = [Cname|TopType],
+ Name = list2name(NameList ++ [check]),
+ emit({"'",Name,"'(",DefaultValue,", ",Element,")"}),
+ ets:insert(check_functions,{Name,Type}),
+ %% Must look for check functions in InnerType,
+ %% that may be referenced or internal defined
+ %% constructed types not used elsewhere.
+ lookahead_innertype(NameList,InnerType,Type)
+ end.
+
+gen_prim_check_call(PrimType,DefaultValue,Element,Type) ->
+ case unify_if_string(PrimType) of
+ 'BOOLEAN' ->
+ emit({"asn1rt_check:check_bool(",DefaultValue,", ",
+ Element,")"});
+ 'INTEGER' ->
+ NNL =
+ case Type#type.def of
+ {_,NamedNumberList} -> NamedNumberList;
+ _ -> []
+ end,
+ emit({"asn1rt_check:check_int(",DefaultValue,", ",
+ Element,", ",{asis,NNL},")"});
+ 'BIT STRING' ->
+ {_,NBL} = Type#type.def,
+ emit({"asn1rt_check:check_bitstring(",DefaultValue,", ",
+ Element,", ",{asis,NBL},")"});
+ 'OCTET STRING' ->
+ emit({"asn1rt_check:check_octetstring(",DefaultValue,", ",
+ Element,")"});
+ 'NULL' ->
+ emit({"asn1rt_check:check_null(",DefaultValue,", ",
+ Element,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"asn1rt_check:check_objectidentifier(",DefaultValue,
+ ", ",Element,")"});
+ 'ObjectDescriptor' ->
+ emit({"asn1rt_check:check_objectdescriptor(",DefaultValue,
+ ", ",Element,")"});
+ 'REAL' ->
+ emit({"asn1rt_check:check_real(",DefaultValue,
+ ", ",Element,")"});
+ 'ENUMERATED' ->
+ {_,Enumerations} = Type#type.def,
+ emit({"asn1rt_check:check_enum(",DefaultValue,
+ ", ",Element,", ",{asis,Enumerations},")"});
+ restrictedstring ->
+ emit({"asn1rt_check:check_restrictedstring(",DefaultValue,
+ ", ",Element,")"})
+ end.
+
+%% lokahead_innertype/3 traverses Type and checks if check functions
+%% have to be generated, i.e. for all constructed or referenced types.
+lookahead_innertype(Name,'SEQUENCE',Type) ->
+ Components = (Type#type.def)#'SEQUENCE'.components,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'SET',Type) ->
+ Components = (Type#type.def)#'SET'.components,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'CHOICE',Type) ->
+ {_,Components} = Type#type.def,
+ lookahead_components(Name,Components);
+lookahead_innertype(Name,'SEQUENCE OF',SeqOf) ->
+ lookahead_sof(Name,'SEQOF',SeqOf);
+lookahead_innertype(Name,'SET OF',SeqOf) ->
+ lookahead_sof(Name,'SETOF',SeqOf);
+lookahead_innertype(_Name,#'Externaltypereference'{module=M,type=T},_) ->
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = asn1ct_gen:get_inner(RefType#type.def),
+ case type(InType) of
+ {constructed,bif} ->
+ NewName = list2name([T,check]),
+ case insert_once(check_functions,{NewName,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ #'Externaltypereference'{} ->
+ NewName = list2name([T,check]),
+ case insert_once(check_functions,{NewName,RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end;
+% case insert_once(check_functions,{list2name(Name++[check]),Type}) of
+% true ->
+% InnerType = asn1ct_gen:get_inner(Type#type.def),
+% case asn1ct_gen:type(InnerType) of
+% {constructed,bif} ->
+% lookahead_innertype([T],InnerType,Type);
+% #'Externaltypereference'{type=TNew} ->
+% lookahead_innertype([TNew],InnerType,Type);
+% _ ->
+% ok
+% end;
+% _ ->
+% ok
+% end;
+lookahead_innertype(_,_,_) ->
+ ok.
+
+lookahead_components(_,[]) -> ok;
+lookahead_components(Name,[C|Cs]) ->
+ #'ComponentType'{name=Cname,typespec=Type} = C,
+ InType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InType) of
+ {constructed,bif} ->
+ case insert_once(check_functions,
+ {list2name([Cname|Name] ++ [check]),Type}) of
+ true ->
+ lookahead_innertype([Cname|Name],InType,Type);
+ _ ->
+ ok
+ end;
+ #'Externaltypereference'{module=RefMod,type=RefName} ->
+ Typedef = asn1_db:dbget(RefMod,RefName),
+ RefType = Typedef#typedef.typespec,
+ case insert_once(check_functions,{list2name([RefName,check]),
+ RefType}) of
+ true ->
+ lookahead_innertype([RefName],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end,
+ lookahead_components(Name,Cs).
+
+lookahead_sof(Name,SOF,SOFType) ->
+ Type = case SOFType#type.def of
+ {_,_Type} -> _Type;
+ _Type -> _Type
+ end,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ %% this is if a constructed type is defined in
+ %% the SEQUENCE OF type
+ NameList = [SOF|Name],
+ insert_once(check_functions,
+ {list2name(NameList ++ [check]),Type}),
+ lookahead_innertype(NameList,InnerType,Type);
+ #'Externaltypereference'{module=M,type=T} ->
+ Typedef = asn1_db:dbget(M,T),
+ RefType = Typedef#typedef.typespec,
+ InType = get_inner(RefType#type.def),
+ case insert_once(check_functions,
+ {list2name([T,check]),RefType}) of
+ true ->
+ lookahead_innertype([T],InType,RefType);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
+
+insert_once(Table,Object) ->
+ case ets:lookup(Table,element(1,Object)) of
+ [] ->
+ ets:insert(Table,Object); %returns true
+ _ -> false
+ end.
+
+unify_if_string(PrimType) ->
+ case PrimType of
+ 'NumericString' ->
+ restrictedstring;
+ 'PrintableString' ->
+ restrictedstring;
+ 'TeletexString' ->
+ restrictedstring;
+ 'VideotexString' ->
+ restrictedstring;
+ 'IA5String' ->
+ restrictedstring;
+ 'UTCTime' ->
+ restrictedstring;
+ 'GeneralizedTime' ->
+ restrictedstring;
+ 'GraphicString' ->
+ restrictedstring;
+ 'VisibleString' ->
+ restrictedstring;
+ 'GeneralString' ->
+ restrictedstring;
+ 'UniversalString' ->
+ restrictedstring;
+ 'BMPString' ->
+ restrictedstring;
+ Other -> Other
+ end.
+
+
+
+
+
+get_inner(A) when atom(A) -> A;
+get_inner(Ext) when record(Ext,'Externaltypereference') -> Ext;
+get_inner(Tref) when record(Tref,typereference) -> Tref;
+get_inner({fixedtypevaluefield,_,Type}) ->
+ if
+ record(Type,type) ->
+ get_inner(Type#type.def);
+ true ->
+ get_inner(Type)
+ end;
+get_inner({typefield,TypeName}) ->
+ TypeName;
+get_inner(#'ObjectClassFieldType'{type=Type}) ->
+% get_inner(Type);
+ Type;
+get_inner(T) when tuple(T) ->
+ case element(1,T) of
+ Tuple when tuple(Tuple),element(1,Tuple) == objectclass ->
+ case catch(lists:last(element(2,T))) of
+ {valuefieldreference,FieldName} ->
+ get_fieldtype(element(2,Tuple),FieldName);
+ {typefieldreference,FieldName} ->
+ get_fieldtype(element(2,Tuple),FieldName);
+ {'EXIT',Reason} ->
+ throw({asn1,{'internal error in get_inner/1',Reason}})
+ end;
+ _ -> element(1,T)
+ end.
+
+
+
+
+
+type(X) when record(X,'Externaltypereference') ->
+ X;
+type(X) when record(X,typereference) ->
+ X;
+type('ASN1_OPEN_TYPE') ->
+ 'ASN1_OPEN_TYPE';
+type({fixedtypevaluefield,_Name,Type}) when record(Type,type) ->
+ type(get_inner(Type#type.def));
+type({typefield,_}) ->
+ 'ASN1_OPEN_TYPE';
+type(X) ->
+ %% io:format("asn1_types:type(~p)~n",[X]),
+ case catch type2(X) of
+ {'EXIT',_} ->
+ {notype,X};
+ Normal ->
+ Normal
+ end.
+
+type2(X) ->
+ case prim_bif(X) of
+ true ->
+ {primitive,bif};
+ false ->
+ case construct_bif(X) of
+ true ->
+ {constructed,bif};
+ false ->
+ {undefined,user}
+ end
+ end.
+
+prim_bif(X) ->
+ lists:member(X,['INTEGER' ,
+ 'ENUMERATED',
+ 'OBJECT IDENTIFIER',
+ 'ANY',
+ 'NULL',
+ 'BIT STRING' ,
+ 'OCTET STRING' ,
+ 'ObjectDescriptor',
+ 'NumericString',
+ 'TeletexString',
+ 'VideotexString',
+ 'UTCTime',
+ 'GeneralizedTime',
+ 'GraphicString',
+ 'VisibleString',
+ 'GeneralString',
+ 'PrintableString',
+ 'IA5String',
+ 'UniversalString',
+ 'BMPString',
+ 'ENUMERATED',
+ 'BOOLEAN']).
+
+construct_bif(T) ->
+ lists:member(T,['SEQUENCE' ,
+ 'SEQUENCE OF' ,
+ 'CHOICE' ,
+ 'SET' ,
+ 'SET OF']).
+
+def_to_tag(#tag{class=Class,number=Number}) ->
+ {Class,Number};
+def_to_tag(#'ObjectClassFieldType'{type=Type}) ->
+ case Type of
+ T when tuple(T),element(1,T)==fixedtypevaluefield ->
+ {'UNIVERSAL',get_inner(Type)};
+ _ ->
+ []
+ end;
+def_to_tag(Def) ->
+ {'UNIVERSAL',get_inner(Def)}.
+
+
+%% Information Object Class
+
+type_from_object(X) ->
+ case (catch lists:last(element(2,X))) of
+ {'EXIT',_} ->
+ {notype,X};
+ Normal ->
+ Normal
+ end.
+
+
+get_fieldtype([],_FieldName)->
+ {no_type,no_name};
+get_fieldtype([Field|Rest],FieldName) ->
+ case element(2,Field) of
+ FieldName ->
+ case element(1,Field) of
+ fixedtypevaluefield ->
+ {element(1,Field),FieldName,element(3,Field)};
+ _ ->
+ {element(1,Field),FieldName}
+ end;
+ _ ->
+ get_fieldtype(Rest,FieldName)
+ end.
+
+get_fieldcategory([],_FieldName) ->
+ no_cat;
+get_fieldcategory([Field|Rest],FieldName) ->
+ case element(2,Field) of
+ FieldName ->
+ element(1,Field);
+ _ ->
+ get_fieldcategory(Rest,FieldName)
+ end.
+
+get_typefromobject(Type) when record(Type,type) ->
+ case Type#type.def of
+ {{objectclass,_,_},TypeFrObj} when list(TypeFrObj) ->
+ {_,FieldName} = lists:last(TypeFrObj),
+ FieldName;
+ _ ->
+ {no_field}
+ end.
+
+get_classfieldcategory(Type,FieldName) ->
+ case (catch Type#type.def) of
+ {{obejctclass,Fields,_},_} ->
+ get_fieldcategory(Fields,FieldName);
+ {'EXIT',_} ->
+ no_cat;
+ _ ->
+ no_cat
+ end.
+%% Information Object Class
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Convert a list of name parts to something that can be output by emit
+%%
+%% used to output function names in generated code.
+
+list2name(L) ->
+ NewL = list2name1(L),
+ lists:concat(lists:reverse(NewL)).
+
+list2name1([{ptype,H1},H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2name1([H1,H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2name1([{ptype,H}|_T]) ->
+ [H];
+list2name1([H|_T]) ->
+ [H];
+list2name1([]) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Convert a list of name parts to something that can be output by emit
+%% stops at {ptype,Pname} i.e Pname whill be the first part of the name
+%% used to output record names in generated code.
+
+list2rname(L) ->
+ NewL = list2rname1(L),
+ lists:concat(lists:reverse(NewL)).
+
+list2rname1([{ptype,H1},_H2|_T]) ->
+ [H1];
+list2rname1([H1,H2|T]) ->
+ [H1,"_",list2name([H2|T])];
+list2rname1([{ptype,H}|_T]) ->
+ [H];
+list2rname1([H|_T]) ->
+ [H];
+list2rname1([]) ->
+ [].
+
+
+
+constructed_suffix(_,#'SEQUENCE'{pname=Ptypename}) when Ptypename =/= false ->
+ {ptype, Ptypename};
+constructed_suffix(_,#'SET'{pname=Ptypename}) when Ptypename =/= false ->
+ {ptype,Ptypename};
+constructed_suffix('SEQUENCE OF',_) ->
+ 'SEQOF';
+constructed_suffix('SET OF',_) ->
+ 'SETOF'.
+
+erule(ber) ->
+ ber;
+erule(ber_bin) ->
+ ber;
+erule(ber_bin_v2) ->
+ ber_bin_v2;
+erule(per) ->
+ per;
+erule(per_bin) ->
+ per.
+
+wrap_ber(ber) ->
+ ber_bin;
+wrap_ber(Erule) ->
+ Erule.
+
+rt2ct_suffix() ->
+ Options = get(encoding_options),
+ case {lists:member(optimize,Options),lists:member(per_bin,Options)} of
+ {true,true} -> "_rt2ct";
+ _ -> ""
+ end.
+rt2ct_suffix(per_bin) ->
+ Options = get(encoding_options),
+ case lists:member(optimize,Options) of
+ true -> "_rt2ct";
+ _ -> ""
+ end;
+rt2ct_suffix(_) -> "".
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V;
+ {value,Cnstr} ->
+ Cnstr
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber.erl
new file mode 100644
index 0000000000..f063dff765
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber.erl
@@ -0,0 +1,1525 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_ber.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_gen_ber).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+
+-export([pgen/4]).
+-export([decode_class/1, decode_type/1]).
+-export([add_removed_bytes/0]).
+-export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]).
+-export([gen_encode_prim/4]).
+-export([gen_dec_prim/8]).
+-export([gen_objectset_code/2, gen_obj_code/3]).
+-export([re_wrap_erule/1]).
+-export([unused_var/2]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+ % the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+ % primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+-define(T_ObjectDescriptor, ?UNIVERSAL bor ?PRIMITIVE bor 7).
+ % restricted character string types
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList,PTypeList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate ENCODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+
+%%===============================================================================
+%% encode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+ ", ObjFun";
+ false ->
+ ""
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([nl,nl,nl,"%%================================"]),
+ emit([nl,"%% ",asn1ct_gen:list2name(Typename)]),
+ emit([nl,"%%================================",nl]),
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,
+ ") when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn",ObjFun,");",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}, TagIn",ObjFun,") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,");",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,") ->",nl," "]),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end;
+
+%%===============================================================================
+%% encode ComponentType
+%%===============================================================================
+
+gen_encode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_,_}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ gen_encode(Erules,NewTname,NewType).
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Type = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ emit([nl,nl,"%%================================"]),
+ emit([nl,"%% ",Typename]),
+ emit([nl,"%%================================",nl]),
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn) when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn);",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),"',Val}, TagIn) ->",nl}),
+ emit({" 'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(",
+ unused_var("Val",Type#type.def),", TagIn) ->",nl}),
+ CurrentMod = get(currmod),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ {primitive,bif} ->
+ asn1ct_gen_ber:gen_encode_prim(ber,Type,["TagIn ++ ",
+ {asis,Tag}],"Val"),
+ emit([".",nl]);
+ #typereference{val=Ename} ->
+ emit([" 'enc_",Ename,"'(Val, TagIn ++ ",{asis,Tag},").",nl]);
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, TagIn ++ ",
+ {asis,Tag},").",nl]);
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn ++ ",
+ {asis,Tag},").",nl]);
+ 'ASN1_OPEN_TYPE' ->
+ emit(["%% OPEN TYPE",nl]),
+ asn1ct_gen_ber:gen_encode_prim(ber,
+ Type#type{def='ASN1_OPEN_TYPE'},
+ ["TagIn ++ ",
+ {asis,Tag}],"Val"),
+ emit([".",nl])
+ end.
+
+unused_var(Var,#'SEQUENCE'{components=Cl}) ->
+ unused_var1(Var,Cl);
+unused_var(Var,#'SET'{components=Cl}) ->
+ unused_var1(Var,Cl);
+unused_var(Var,_) ->
+ Var.
+unused_var1(Var,Cs) when Cs == []; Cs == {[],[]} ->
+ lists:concat(["_",Var]);
+unused_var1(Var,_) ->
+ Var.
+
+unused_optormand_var(Var,Def) ->
+ case asn1ct_gen:type(asn1ct_gen:get_inner(Def)) of
+ 'ASN1_OPEN_TYPE' ->
+ lists:concat(["_",Var]);
+ _ ->
+ Var
+ end.
+
+
+gen_encode_prim(_Erules,D,DoTag,Value) when record(D,type) ->
+
+%%% Currently not used for BER (except for BitString) and therefore replaced
+%%% with [] as a placeholder
+ BitStringConstraint = D#type.constraint,
+ Constraint = [],
+ asn1ct_name:new(enumval),
+ case D#type.def of
+ 'BOOLEAN' ->
+ emit_encode_func('boolean',Value,DoTag);
+ 'INTEGER' ->
+ emit_encode_func('integer',Constraint,Value,DoTag);
+ {'INTEGER',NamedNumberList} ->
+ emit_encode_func('integer',Constraint,Value,
+ NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList={_,_}} ->
+
+ emit(["case (case ",Value," of {asn1_enum,_}->",Value,";{_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList} ->
+
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+
+ {'BIT STRING',NamedNumberList} ->
+ emit_encode_func('bit_string',BitStringConstraint,Value,
+ NamedNumberList,DoTag);
+ 'ANY' ->
+ emit_encode_func('open_type', Value,DoTag);
+ 'NULL' ->
+ emit_encode_func('null',Value,DoTag);
+ 'OBJECT IDENTIFIER' ->
+ emit_encode_func("object_identifier",Value,DoTag);
+ 'ObjectDescriptor' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_ObjectDescriptor,DoTag);
+ 'OCTET STRING' ->
+ emit_encode_func('octet_string',Constraint,Value,DoTag);
+ 'NumericString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_NumericString,DoTag);
+ 'TeletexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_TeletexString,DoTag);
+ 'VideotexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VideotexString,DoTag);
+ 'GraphicString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GraphicString,DoTag);
+ 'VisibleString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VisibleString,DoTag);
+ 'GeneralString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GeneralString,DoTag);
+ 'PrintableString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_PrintableString,DoTag);
+ 'IA5String' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_IA5String,DoTag);
+ 'UniversalString' ->
+ emit_encode_func('universal_string',Constraint,Value,DoTag);
+ 'BMPString' ->
+ emit_encode_func('BMP_string',Constraint,Value,DoTag);
+ 'UTCTime' ->
+ emit_encode_func('utc_time',Constraint,Value,DoTag);
+ 'GeneralizedTime' ->
+ emit_encode_func('generalized_time',Constraint,Value,DoTag);
+ 'ASN1_OPEN_TYPE' ->
+ emit_encode_func('open_type', Value,DoTag);
+ XX ->
+ exit({'can not encode' ,XX})
+ end.
+
+
+emit_encode_func(Name,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Value,Tags);
+emit_encode_func(Name,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Tags);
+emit_encode_func(Name,Constraint,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Asis,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Asis,Tags);
+emit_encode_func(Name,Constraint,Value,Asis,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,
+ ", ",{asis,Asis},
+ ", ",Tags,")"]).
+
+emit_enc_enumerated_cases({L1,L2}, Tags) ->
+ emit_enc_enumerated_cases(L1++L2, Tags, ext);
+emit_enc_enumerated_cases(L, Tags) ->
+ emit_enc_enumerated_cases(L, Tags, noext).
+
+emit_enc_enumerated_cases([{EnumName,EnumVal},H2|T], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+ emit_enc_enumerated_cases([H2|T], Tags, Ext);
+emit_enc_enumerated_cases([{EnumName,EnumVal}], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+ case Ext of
+ noext -> emit([";",nl]);
+ ext ->
+ emit([";",nl,"{asn1_enum,",{curr,enumval},"} -> ",
+ "?RT_BER:encode_enumerated(",{curr,enumval},",",Tags,");",nl]),
+ asn1ct_name:new(enumval)
+ end,
+ emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]),
+ emit([nl,"end"]).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate DECODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% decode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ D = Type,
+ emit({nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes, OptOrMand) ->",nl}),
+ emit({" 'dec_",Type#typedef.name,"'(Bytes, OptOrMand, []).",nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes, ",
+ unused_optormand_var("OptOrMand",(Type#typedef.typespec)#type.def),", TagIn) ->",nl}),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,D).
+
+
+%%===============================================================================
+%% decode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+gen_decode(Erules,Tname,Type) when record(Type,type) ->
+ Typename = Tname,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({"'dec_",asn1ct_gen:list2name(Typename),"'(Bytes, OptOrMand, TagIn",ObjFun,") ->",nl}),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end;
+
+
+%%===============================================================================
+%% decode ComponentType
+%%===============================================================================
+
+gen_decode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_,_}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ gen_decode(Erules,NewTname,NewType).
+
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ InnerTag = Def#type.tag ,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- InnerTag],
+ case asn1ct_gen:type(InnerType) of
+ 'ASN1_OPEN_TYPE' ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_name:new(len),
+ gen_dec_prim(Erules, Def#type{def='ASN1_OPEN_TYPE'},
+ BytesVar, Tag, "TagIn",no_length,
+ ?PRIMITIVE,"OptOrMand"),
+ emit({".",nl,nl});
+ {primitive,bif} ->
+ BytesVar = asn1ct_gen:mk_var(asn1ct_name:curr(bytes)),
+ asn1ct_name:new(len),
+ gen_dec_prim(Erules, Def, BytesVar, Tag, "TagIn",no_length,
+ ?PRIMITIVE,"OptOrMand"),
+ emit({".",nl,nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ TheType ->
+ DecFunName = mkfuncname(TheType,dec),
+ emit({DecFunName,"(",{curr,bytes},
+ ", OptOrMand, TagIn++",{asis,Tag},")"}),
+ emit({".",nl,nl})
+ end.
+
+
+gen_dec_prim(Erules,Att,BytesVar,DoTag,TagIn,Length,_Form,OptOrMand) ->
+ Typename = Att#type.def,
+%% Currently not used for BER replaced with [] as place holder
+%% Constraint = Att#type.constraint,
+%% Constraint = [],
+ Constraint =
+ case get_constraint(Att#type.constraint,'SizeConstraint') of
+ no -> [];
+ Tc -> Tc
+ end,
+ ValueRange =
+ case get_constraint(Att#type.constraint,'ValueRange') of
+ no -> [];
+ Tv -> Tv
+ end,
+ SingleValue =
+ case get_constraint(Att#type.constraint,'SingleValue') of
+ no -> [];
+ Sv -> Sv
+ end,
+ AsBin = case get(binary_strings) of
+ true -> "_as_bin";
+ _ -> ""
+ end,
+ NewTypeName = case Typename of
+ 'ANY' -> 'ASN1_OPEN_TYPE';
+ _ -> Typename
+ end,
+ DoLength =
+ case NewTypeName of
+ 'BOOLEAN'->
+ emit({"?RT_BER:decode_boolean(",BytesVar,","}),
+ false;
+ 'INTEGER' ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},","}),
+ false;
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},",",
+ {asis,NamedNumberList},","}),
+ false;
+ {'ENUMERATED',NamedNumberList} ->
+ emit({"?RT_BER:decode_enumerated(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ false;
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_BER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},","});
+ _ ->
+ emit({"?RT_BER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","})
+ end,
+ true;
+ 'NULL' ->
+ emit({"?RT_BER:decode_null(",BytesVar,","}),
+ false;
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_BER:decode_object_identifier(",BytesVar,","}),
+ false;
+ 'ObjectDescriptor' ->
+ emit({"?RT_BER:decode_restricted_string(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_ObjectDescriptor},","}),
+ true;
+ 'OCTET STRING' ->
+ emit({"?RT_BER:decode_octet_string",AsBin,"(",BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'NumericString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_NumericString},","}),true;
+ 'TeletexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_TeletexString},","}),
+ true;
+ 'VideotexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VideotexString},","}),
+ true;
+ 'GraphicString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GraphicString},","})
+ ,true;
+ 'VisibleString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VisibleString},","}),
+ true;
+ 'GeneralString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GeneralString},","}),
+ true;
+ 'PrintableString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_PrintableString},","}),
+ true;
+ 'IA5String' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_IA5String},","}),
+ true;
+ 'UniversalString' ->
+ emit({"?RT_BER:decode_universal_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'BMPString' ->
+ emit({"?RT_BER:decode_BMP_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'UTCTime' ->
+ emit({"?RT_BER:decode_utc_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'GeneralizedTime' ->
+ emit({"?RT_BER:decode_generalized_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ true;
+ 'ASN1_OPEN_TYPE' ->
+ emit(["?RT_BER:decode_open_type(",re_wrap_erule(Erules),",",
+ BytesVar,","]),
+ false;
+ Other ->
+ exit({'can not decode' ,Other})
+ end,
+
+ NewLength = case DoLength of
+ true -> [", ", Length];
+ false -> ""
+ end,
+ NewOptOrMand = case OptOrMand of
+ _ when list(OptOrMand) -> OptOrMand;
+ mandatory -> {asis,mandatory};
+ _ -> {asis,opt_or_default}
+ end,
+ case {TagIn,NewTypeName} of
+ {[],'ASN1_OPEN_TYPE'} ->
+ emit([{asis,DoTag},")"]);
+ {_,'ASN1_OPEN_TYPE'} ->
+ emit([TagIn,"++",{asis,DoTag},")"]);
+ {[],_} ->
+ emit([{asis,DoTag},NewLength,", ",NewOptOrMand,")"]);
+ _ when list(TagIn) ->
+ emit([TagIn,"++",{asis,DoTag},NewLength,", ",NewOptOrMand,")"])
+ end.
+
+
+int_constr([],[]) ->
+ [];
+int_constr([],ValueRange) ->
+ ValueRange;
+int_constr(SingleValue,[]) ->
+ SingleValue;
+int_constr(SV,VR) ->
+ [SV,VR].
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=M,type=ClName} = Def#'Object'.classname,
+ Class = asn1_db:dbget(M,ClName),
+
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+ gen_encode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+ gen_decode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed);
+gen_obj_code(_Erules,_Module,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Args,", _RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, TagIn, _RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_, _"),
+ emit([" {[],0}"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val, TagIn"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val, TagIn"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, TagIn, [H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause(" Val, TagIn, [H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, TagIn, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, TagIn, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_encode_objectfields(ClassName,[_|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+% MaybeConstr=
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% OTag = Def#type.tag,
+% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, TagIn, RestPrimFieldName) ->",nl}),
+% CAcc=
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_encode_prim(ber,Def,["TagIn ++ ",{asis,Tag}],
+% "Val"),
+% [];
+% {constructed,bif} ->
+% %%InnerType = asn1ct_gen:get_inner(Def#type.def),
+% %%asn1ct_gen:gen_encode_constructed(ber,[ObjName],
+% %% InnerType,Def);
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val, TagIn ++ ",{asis,Tag},")"}),
+% [{['enc_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,
+% "'(Val, TagIn ++ ",{asis,Tag},")"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val, TagIn ++ ",
+% {asis,Tag},")"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, TagIn, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, TagIn, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, TagIn, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_encode_objectfields(C,O,[H|T],Acc) ->
+% gen_encode_objectfields(C,O,T,Acc);
+% gen_encode_objectfields(_,_,[],Acc) ->
+% Acc.
+
+% gen_encode_constr_type([{Name,Def}|Rest]) ->
+% emit({Name,"(Val,TagIn) ->",nl}),
+% InnerType = asn1ct_gen:get_inner(Def#type.def),
+% asn1ct_gen:gen_encode_constructed(ber,Name,InnerType,Def),
+% gen_encode_constr_type(Rest);
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ -> gen_encode_user(Erules,TypeDef)
+ end,
+ gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+ gen_encode_prim(ber,Def,["TagIn ++ ",{asis,Tag}],
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val, TagIn ++",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val, TagIn ++ ",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val, TagIn ++ ",{asis,Tag},")"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Bytes, TagIn ++ ",
+ {asis,Tag},")"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, TagIn ++ ",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn ++ ",{asis,Tag},")",nl]),
+ []
+ end.
+
+
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Args,"_) ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes, TagIn, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_, _,"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes, TagIn,"),
+ gen_decode_default_call(ClassName,Name,"Bytes",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes, TagIn,"),
+ gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,TagIn,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,TagIn,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, TagIn, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, TagIn, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+
+
+% gen_decode_objectfields(Erules,Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Bytes, TagIn, RestPrimFieldName) ->",nl}),
+% OTag = Def#type.tag,
+% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% Prop =
+% case get_optionalityspec(Fields,FieldName) of
+% 'OPTIONAL' -> opt_or_default;
+% {'DEFAULT',_} -> opt_or_default;
+% _ -> mandatory
+% end,
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_dec_prim(Erules,Def,"Bytes",Tag,"TagIn",no_length,
+% ?PRIMITIVE,Prop),
+% [];
+% {constructed,bif} ->
+% emit({" 'dec_",ObjName,'_',FieldName,"'(Bytes,",
+% {asis,Prop},", TagIn ++ ",{asis,Tag},")"}),
+% [{['dec_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'dec_",TypeName,"'(Bytes, ",
+% {asis,Prop},", TagIn ++ ",{asis,Tag},")"}),
+% [];
+% TypeName ->
+% emit({" 'dec_",TypeName,"'(Bytes, ",{asis,Prop},
+% ", TagIn ++ ",{asis,Tag},")"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Bytes, TagIn, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+% "'(H, Bytes, TagIn, T)"});
+% TypeName ->
+% emit({indent(3),"'dec_",TypeName,
+% "'(H, Bytes, TagIn, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} ->
+% []
+% end,
+% gen_decode_objectfields(Erules,Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_decode_objectfields(Erules,C,O,[H|T],CAcc) ->
+% gen_decode_objectfields(Erules,C,O,T,CAcc);
+% gen_decode_objectfields(_,_,_,[],CAcc) ->
+% CAcc.
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+%% emit({Name,"(Bytes, OptOrMand) ->",nl}),
+%% emit({" ",Name,"(Bytes, OptOrMand, []).",nl,nl}),
+ emit({Name,"(Bytes, OptOrMand, TagIn) ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(ber,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+ gen_dec_prim(ber,Def,Bytes,Tag,"TagIn",no_length,
+ ?PRIMITIVE,opt_or_default),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",opt_or_default, TagIn ++ ",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,", opt_or_default,TagIn ++ ",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,
+ ", opt_or_default,TagIn ++ ",{asis,Tag},")"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,
+ ",opt_or_default, TagIn ++ ",{asis,Tag},")"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(ber,Type,Bytes,Tag,"TagIn",no_length,
+ ?PRIMITIVE,opt_or_default),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes,
+ " ,opt_or_default, TagIn ++ ",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,
+ ", opt_or_defualt, TagIn ++ ",{asis,Tag},")",nl]),
+ []
+ end.
+
+
+more_genfields([]) ->
+ false;
+more_genfields([Field|Fields]) ->
+ case element(1,Field) of
+ typefield ->
+ true;
+ objectfield ->
+ true;
+ _ ->
+ more_genfields(Fields)
+ end.
+
+
+
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = (ClassDef#classdef.typespec)#objectclass.fields,
+ InternalFuncs=gen_objset_enc(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1,[]),
+ gen_objset_dec(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+%% gen_objset_enc iterates over the objects of the object set
+gen_objset_enc(_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(ObjSName,UniqueName,
+ [{ObjName,Val,Fields},T|Rest],ClName,ClFields,NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/4"}),
+ {[],NthObj}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc ++ Acc);
+gen_objset_enc(ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
+ {InternalFunc,_}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/4"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc ++ Acc;
+%% See X.681 Annex E for the following case
+gen_objset_enc(ObjSetName,_UniqueName,['EXTENSIONMARK'],
+ _ClName,_ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_Attr, Val, _TagIn, _RestPrimFieldName) ->",nl}),
+ emit({indent(6),"Len = case Val of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Val)",nl,indent(6),"end,"}),
+ emit({indent(6),"{Val,Len}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, TagIn, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, TagIn, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_H|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+
+emit_inner_of_fun(TDef = #typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val,TagIn ++ ",
+ {asis,Tag},")"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val, TagIn ++ ",
+ {asis,Tag},")"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name,typespec=Type},_) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ emit({indent(12),"'enc_",Name,"'(Val, TagIn ++ ",{asis,Tag},")"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_encode_prim(ber,Type,["TagIn ++ ",{asis,Tag}],"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val, TagIn ++ ",{asis,Tag},")"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val, TagIn ++ ",{asis,Tag},")"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val, TagIn ++ ",{asis,Tag},")"})
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj)->
+ emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Erules,Fields,ClFields,ObjSName,
+ NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"}),
+ NthObj
+ end,
+ emit({";",nl}),
+ gen_objset_dec(Erules,ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj);
+gen_objset_dec(Erules,ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName,
+ ClFields,NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val},") ->",nl}),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Erules,Fields,ClFields,ObjSetName,
+ NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"})
+ end,
+ emit({".",nl,nl});
+gen_objset_dec(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields,
+ _NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Bytes, _, _) ->",nl}),
+ emit({indent(6),"Len = case Bytes of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Bytes)",nl,indent(6),"end,"}),
+ emit({indent(6),"{Bytes,[],Len}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ ok;
+gen_objset_dec(_,_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Erules,Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Bytes, TagIn, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl}),
+ N=emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Bytes, TagIn, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ N=emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Erules,Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Erules,Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Erules,Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Erules,Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ emit_inner_of_decfun(Erules,Type,DecProp,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Erules,Fields,[_H|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Erules,Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,_,[],_,NthObj) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ NthObj.
+
+emit_inner_of_decfun(Erules,#typedef{name={ExtName,Name},typespec=Type},
+ Prop,InternalDefFunName) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(Erules,Type,"Bytes",Tag,"TagIn",no_length,
+ ?PRIMITIVE,Prop),
+ 0;
+ {constructed,bif} ->
+ emit({indent(12),"'dec_",
+ asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",Prop,
+ ", TagIn ++ ",{asis,Tag},")"}),
+ 1;
+ _ ->
+ emit({indent(12),"'",ExtName,"':'dec_",Name,"'(Bytes, ",Prop,
+ ", TagIn ++ ",{asis,Tag},")"}),
+ 0
+ end;
+emit_inner_of_decfun(_,#typedef{name=Name,typespec=Type},Prop,_) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ emit({indent(12),"'dec_",Name,"'(Bytes, ",Prop,", TagIn ++ ",
+ {asis,Tag},")"}),
+ 0;
+emit_inner_of_decfun(Erules,Type,Prop,_) when record(Type,type) ->
+ OTag = Type#type.tag,
+ Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ CurrMod = get(currmod),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ case WhatKind of
+ {primitive,bif} ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_dec_prim(Erules,Type,"Bytes",Tag,"TagIn",no_length,
+ ?PRIMITIVE,Prop);
+% TRef when record(TRef,typereference) ->
+% T = TRef#typereference.val,
+% emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,
+ "'(Bytes, ",Prop,", TagIn ++ ",{asis,Tag},")"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+ T,"'(Bytes, ",Prop,", TagIn ++ ",{asis,Tag},")"})
+ end,
+ 0.
+
+
+gen_internal_funcs(_,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit({"'dec_",TypeDef#typedef.name,"'(Bytes, ",
+ unused_optormand_var("OptOrMand",(TypeDef#typedef.typespec)#type.def),", TagIn) ->",nl}),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+
+decode_class('UNIVERSAL') ->
+ ?UNIVERSAL;
+decode_class('APPLICATION') ->
+ ?APPLICATION;
+decode_class('CONTEXT') ->
+ ?CONTEXT;
+decode_class('PRIVATE') ->
+ ?PRIVATE.
+
+decode_type('BOOLEAN') -> 1;
+decode_type('INTEGER') -> 2;
+decode_type('BIT STRING') -> 3;
+decode_type('OCTET STRING') -> 4;
+decode_type('NULL') -> 5;
+decode_type('OBJECT IDENTIFIER') -> 6;
+decode_type('OBJECT DESCRIPTOR') -> 7;
+decode_type('EXTERNAL') -> 8;
+decode_type('REAL') -> 9;
+decode_type('ENUMERATED') -> 10;
+decode_type('EMBEDDED_PDV') -> 11;
+decode_type('SEQUENCE') -> 16;
+decode_type('SEQUENCE OF') -> 16;
+decode_type('SET') -> 17;
+decode_type('SET OF') -> 17;
+decode_type('NumericString') -> 18;
+decode_type('PrintableString') -> 19;
+decode_type('TeletexString') -> 20;
+decode_type('VideotexString') -> 21;
+decode_type('IA5String') -> 22;
+decode_type('UTCTime') -> 23;
+decode_type('GeneralizedTime') -> 24;
+decode_type('GraphicString') -> 25;
+decode_type('VisibleString') -> 26;
+decode_type('GeneralString') -> 27;
+decode_type('UniversalString') -> 28;
+decode_type('BMPString') -> 30;
+decode_type('CHOICE') -> 'CHOICE'; % choice gets the tag from the actual alternative
+decode_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}).
+
+add_removed_bytes() ->
+ asn1ct_name:delete(rb),
+ add_removed_bytes(asn1ct_name:all(rb)).
+
+add_removed_bytes([H,T1|T]) ->
+ emit({{var,H},"+"}),
+ add_removed_bytes([T1|T]);
+add_removed_bytes([H|T]) ->
+ emit({{var,H}}),
+ add_removed_bytes(T);
+add_removed_bytes([]) ->
+ true.
+
+mkfuncname(WhatKind,DecOrEnc) ->
+ case WhatKind of
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ CurrMod = get(currmod),
+ case CurrMod of
+ Mod ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ _ ->
+% io:format("CurrMod: ~p, Mod: ~p~n",[CurrMod,Mod]),
+ lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"])
+ end;
+ #'typereference'{val=EType} ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ 'ASN1_OPEN_TYPE' ->
+ lists:concat(["'",DecOrEnc,"_",WhatKind,"'"])
+
+ end.
+
+optionals(L) -> optionals(L,[],1).
+
+optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
+optionals([#'ComponentType'{name=Name,prop='OPTIONAL'}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{name=Name,prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos+1);
+optionals([],Acc,_) ->
+ lists:reverse(Acc).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%% if the original option was ber and it has been wrapped to ber_bin
+%% turn it back to ber
+re_wrap_erule(ber_bin) ->
+ case get(encoding_options) of
+ Options when list(Options) ->
+ case lists:member(ber,Options) of
+ true -> ber;
+ _ -> ber_bin
+ end;
+ _ -> ber_bin
+ end;
+re_wrap_erule(Erule) ->
+ Erule.
+
+is_already_generated(Operation,Name) ->
+ case get(class_default_type) of
+ undefined ->
+ put(class_default_type,[{Operation,Name}]),
+ false;
+ GeneratedList ->
+ case lists:member({Operation,Name},GeneratedList) of
+ true ->
+ true;
+ false ->
+ put(class_default_type,[{Operation,Name}|GeneratedList]),
+ false
+ end
+ end.
+
+get_class_fields(#classdef{typespec=ObjClass}) ->
+ ObjClass#objectclass.fields;
+get_class_fields(#objectclass{fields=Fields}) ->
+ Fields;
+get_class_fields(_) ->
+ [].
+
+get_object_field(Name,ObjectFields) ->
+ case lists:keysearch(Name,1,ObjectFields) of
+ {value,Field} -> Field;
+ false -> false
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl
new file mode 100644
index 0000000000..be8ae6f8a5
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_ber_bin_v2.erl
@@ -0,0 +1,1568 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_ber_bin_v2.erl,v 1.1 2008/12/17 09:53:29 mikpe Exp $
+%%
+-module(asn1ct_gen_ber_bin_v2).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+
+-export([pgen/4]).
+-export([decode_class/1, decode_type/1]).
+-export([add_removed_bytes/0]).
+-export([gen_encode/2,gen_encode/3,gen_decode/2,gen_decode/3]).
+-export([gen_encode_prim/4]).
+-export([gen_dec_prim/7]).
+-export([gen_objectset_code/2, gen_obj_code/3]).
+-export([encode_tag_val/3]).
+-export([gen_inc_decode/2]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+ % the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+ % primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+
+-define(T_ObjectDescriptor, ?UNIVERSAL bor ?PRIMITIVE bor 7).
+ % restricted character string types
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList,PTypeList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate ENCODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+
+%%===============================================================================
+%% encode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+ ", ObjFun";
+ false ->
+ ""
+ end,
+
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([nl,nl,nl,"%%================================"]),
+ emit([nl,"%% ",asn1ct_gen:list2name(Typename)]),
+ emit([nl,"%%================================",nl]),
+ case length(Typename) of
+ 1 -> % top level type
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",ObjFun,") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, ", {asis,lists:reverse(Type#type.tag)},ObjFun,").",nl,nl]);
+ _ -> % embedded type with constructed name
+ true
+ end,
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,
+ ") when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn",ObjFun,");",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}, TagIn",ObjFun,") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,");",nl,nl])
+ end,
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn",ObjFun,") ->",nl," "]),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end;
+
+%%===============================================================================
+%% encode ComponentType
+%%===============================================================================
+
+gen_encode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_Prop,_Tags}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ gen_encode(Erules,NewTname,NewType).
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Type = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ emit([nl,nl,"%%================================"]),
+ emit([nl,"%% ",Typename]),
+ emit([nl,"%%================================",nl]),
+ emit(["'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",") ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, ", {asis,lists:reverse(Tag)},").",nl,nl]),
+
+ case lists:member(InnerType,['SET','SEQUENCE']) of
+ true ->
+ case get(asn_keyed_list) of
+ true ->
+ CompList =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> Cl;
+ #'SET'{components=Cl} -> Cl
+ end,
+
+ emit([nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val, TagIn) when list(Val) ->",nl]),
+ emit([" 'enc_",asn1ct_gen:list2name(Typename),
+ "'(?RT_BER:fixoptionals(",
+ {asis,optionals(CompList)},
+ ",Val), TagIn);",nl,nl]);
+ _ -> true
+ end;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),"',Val}, TagIn) ->",nl}),
+ emit({" 'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val, TagIn) ->",nl}),
+ CurrentMod = get(currmod),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ {primitive,bif} ->
+ gen_encode_prim(ber,Type,"TagIn","Val"),
+ emit([".",nl]);
+ #typereference{val=Ename} ->
+ emit([" 'enc_",Ename,"'(Val, TagIn).",nl]);
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, TagIn).",nl]);
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, TagIn).",nl]);
+ 'ASN1_OPEN_TYPE' ->
+ emit(["%% OPEN TYPE",nl]),
+ gen_encode_prim(ber,
+ Type#type{def='ASN1_OPEN_TYPE'},
+ "TagIn","Val"),
+ emit([".",nl])
+ end.
+
+gen_encode_prim(_Erules,D,DoTag,Value) when record(D,type) ->
+
+%%% Constraint is currently not used for BER (except for BitString) and therefore replaced
+%%% with [] as a placeholder
+ BitStringConstraint = D#type.constraint,
+ Constraint = [],
+ asn1ct_name:new(enumval),
+ case D#type.def of
+ 'BOOLEAN' ->
+ emit_encode_func('boolean',Value,DoTag);
+ 'INTEGER' ->
+ emit_encode_func('integer',Constraint,Value,DoTag);
+ {'INTEGER',NamedNumberList} ->
+ emit_encode_func('integer',Constraint,Value,
+ NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList={_,_}} ->
+
+ emit(["case (case ",Value," of {asn1_enum,_}->",Value,";{_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+ {'ENUMERATED',NamedNumberList} ->
+
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NamedNumberList,DoTag);
+
+ {'BIT STRING',NamedNumberList} ->
+ emit_encode_func('bit_string',BitStringConstraint,Value,
+ NamedNumberList,DoTag);
+ 'ANY' ->
+ emit_encode_func('open_type', Value,DoTag);
+ 'NULL' ->
+ emit_encode_func('null',Value,DoTag);
+ 'OBJECT IDENTIFIER' ->
+ emit_encode_func("object_identifier",Value,DoTag);
+ 'ObjectDescriptor' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_ObjectDescriptor,DoTag);
+ 'OCTET STRING' ->
+ emit_encode_func('octet_string',Constraint,Value,DoTag);
+ 'NumericString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_NumericString,DoTag);
+ 'TeletexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_TeletexString,DoTag);
+ 'VideotexString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VideotexString,DoTag);
+ 'GraphicString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GraphicString,DoTag);
+ 'VisibleString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_VisibleString,DoTag);
+ 'GeneralString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_GeneralString,DoTag);
+ 'PrintableString' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_PrintableString,DoTag);
+ 'IA5String' ->
+ emit_encode_func('restricted_string',Constraint,Value,
+ ?T_IA5String,DoTag);
+ 'UniversalString' ->
+ emit_encode_func('universal_string',Constraint,Value,DoTag);
+ 'BMPString' ->
+ emit_encode_func('BMP_string',Constraint,Value,DoTag);
+ 'UTCTime' ->
+ emit_encode_func('utc_time',Constraint,Value,DoTag);
+ 'GeneralizedTime' ->
+ emit_encode_func('generalized_time',Constraint,Value,DoTag);
+ 'ASN1_OPEN_TYPE' ->
+ emit_encode_func('open_type', Value,DoTag);
+ XX ->
+ exit({'can not encode' ,XX})
+ end.
+
+
+emit_encode_func(Name,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Value,Tags);
+emit_encode_func(Name,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Tags);
+emit_encode_func(Name,Constraint,Value,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,", ",Tags,")"]).
+
+emit_encode_func(Name,Constraint,Value,Asis,Tags) when atom(Name) ->
+ emit_encode_func(atom_to_list(Name),Constraint,Value,Asis,Tags);
+emit_encode_func(Name,Constraint,Value,Asis,Tags) ->
+ Fname = "?RT_BER:encode_" ++ Name,
+ emit([Fname,"(",{asis,Constraint},", ",Value,
+ ", ",{asis,Asis},
+ ", ",Tags,")"]).
+
+emit_enc_enumerated_cases({L1,L2}, Tags) ->
+ emit_enc_enumerated_cases(L1++L2, Tags, ext);
+emit_enc_enumerated_cases(L, Tags) ->
+ emit_enc_enumerated_cases(L, Tags, noext).
+
+emit_enc_enumerated_cases([{EnumName,EnumVal},H2|T], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,");",nl]),
+ emit_enc_enumerated_cases([H2|T], Tags, Ext);
+emit_enc_enumerated_cases([{EnumName,EnumVal}], Tags, Ext) ->
+ emit([{asis,EnumName}," -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+%% emit(["'",{asis,EnumName},"' -> ?RT_BER:encode_enumerated(",EnumVal,",",Tags,")"]),
+ case Ext of
+ noext -> emit([";",nl]);
+ ext ->
+ emit([";",nl,"{asn1_enum,",{curr,enumval},"} -> ",
+ "?RT_BER:encode_enumerated(",{curr,enumval},",",Tags,");",nl]),
+ asn1ct_name:new(enumval)
+ end,
+ emit([{curr,enumval}," -> exit({error,{asn1, {enumerated_not_in_range,",{curr, enumval},"}}})"]),
+ emit([nl,"end"]).
+
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Generate DECODING
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% decode #{typedef, {pos, name, typespec}}
+%%===============================================================================
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ Def = Type#typedef.typespec,
+ InnerTag = Def#type.tag ,
+
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- InnerTag],
+
+ Prefix =
+ case {asn1ct:get_gen_state_field(active),
+ asn1ct:get_gen_state_field(prefix)} of
+ {true,Pref} -> Pref;
+ _ -> "dec_"
+ end,
+ emit({nl,nl}),
+ emit(["'",Prefix,Type#typedef.name,"'(Tlv) ->",nl]),
+ emit([" '",Prefix,Type#typedef.name,"'(Tlv, ",{asis,Tag},").",nl,nl]),
+ emit(["'",Prefix,Type#typedef.name,"'(Tlv, TagIn) ->",nl]),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,Type).
+
+gen_inc_decode(Erules,Type) when record(Type,typedef) ->
+ Prefix = asn1ct:get_gen_state_field(prefix),
+ emit({nl,nl}),
+ emit(["'",Prefix,Type#typedef.name,"'(Tlv, TagIn) ->",nl]),
+ gen_decode_user(Erules,Type).
+
+%%===============================================================================
+%% decode #{type, {tag, def, constraint}}
+%%===============================================================================
+
+%% This gen_decode is called by the gen_decode/3 that decodes
+%% ComponentType and the type of a SEQUENCE OF/SET OF.
+gen_decode(Erules,Tname,Type) when record(Type,type) ->
+ Typename = Tname,
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ Prefix =
+ case asn1ct:get_gen_state_field(active) of
+ true -> "'dec-inc-";
+ _ -> "'dec_"
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit([Prefix,asn1ct_gen:list2name(Typename),"'(Tlv, TagIn",ObjFun,") ->",nl]),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ Rec when record(Rec,'Externaltypereference') ->
+ case {Typename,asn1ct:get_gen_state_field(namelist)} of
+ {[Cname|_],[{Cname,_}|_]} -> %%
+ %% This referenced type must only be generated
+ %% once as incomplete partial decode. Therefore we
+ %% have to check whether this function already is
+ %% generated.
+ case asn1ct:is_function_generated(Typename) of
+ true ->
+ ok;
+ _ ->
+ asn1ct:generated_refed_func(Typename),
+ #'Externaltypereference'{module=M,type=Name}=Rec,
+ TypeDef = asn1_db:dbget(M,Name),
+ gen_decode(Erules,TypeDef)
+ end;
+ _ ->
+ true
+ end;
+ _ ->
+ true
+ end;
+
+
+%%===============================================================================
+%% decode ComponentType
+%%===============================================================================
+
+gen_decode(Erules,Tname,{'ComponentType',_Pos,Cname,Type,_Prop,_Tags}) ->
+ NewTname = [Cname|Tname],
+ %% The tag is set to [] to avoid that it is
+ %% taken into account twice, both as a component/alternative (passed as
+ %% argument to the encode decode function and within the encode decode
+ %% function it self.
+ NewType = Type#type{tag=[]},
+ case {asn1ct:get_gen_state_field(active),
+ asn1ct:get_tobe_refed_func(NewTname)} of
+ {true,{_,NameList}} ->
+ asn1ct:update_gen_state(namelist,NameList),
+ %% remove to gen_refed_funcs list from tobe_refed_funcs later
+ gen_decode(Erules,NewTname,NewType);
+ {No,_} when No == false; No == undefined ->
+ gen_decode(Erules,NewTname,NewType);
+ _ ->
+ ok
+ end.
+
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ BytesVar = "Tlv",
+ case asn1ct_gen:type(InnerType) of
+ 'ASN1_OPEN_TYPE' ->
+ asn1ct_name:new(len),
+ gen_dec_prim(ber, Def#type{def='ASN1_OPEN_TYPE'},
+ BytesVar,{string,"TagIn"}, [] ,
+ ?PRIMITIVE,"OptOrMand"),
+ emit({".",nl,nl});
+ {primitive,bif} ->
+ asn1ct_name:new(len),
+ gen_dec_prim(ber, Def, BytesVar,{string,"TagIn"},[] ,
+ ?PRIMITIVE,"OptOrMand"),
+ emit([".",nl,nl]);
+ {constructed,bif} ->
+ asn1ct:update_namelist(D#typedef.name),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ TheType ->
+ DecFunName = mkfuncname(TheType,dec),
+ emit([DecFunName,"(",BytesVar,
+ ", TagIn)"]),
+ emit([".",nl,nl])
+ end.
+
+
+gen_dec_prim(_Erules,Att,BytesVar,DoTag,_TagIn,_Form,_OptOrMand) ->
+ Typename = Att#type.def,
+%% Currently not used for BER replaced with [] as place holder
+%% Constraint = Att#type.constraint,
+%% Constraint = [],
+ Constraint =
+ case get_constraint(Att#type.constraint,'SizeConstraint') of
+ no -> [];
+ Tc -> Tc
+ end,
+ ValueRange =
+ case get_constraint(Att#type.constraint,'ValueRange') of
+ no -> [];
+ Tv -> Tv
+ end,
+ SingleValue =
+ case get_constraint(Att#type.constraint,'SingleValue') of
+ no -> [];
+ Sv -> Sv
+ end,
+ AsBin = case get(binary_strings) of
+ true -> "_as_bin";
+ _ -> ""
+ end,
+ NewTypeName = case Typename of
+ 'ANY' -> 'ASN1_OPEN_TYPE';
+ _ -> Typename
+ end,
+% DoLength =
+ case NewTypeName of
+ 'BOOLEAN'->
+ emit({"?RT_BER:decode_boolean(",BytesVar,","}),
+ add_func({decode_boolean,2});
+ 'INTEGER' ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},","}),
+ add_func({decode_integer,3});
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_BER:decode_integer(",BytesVar,",",
+ {asis,int_constr(SingleValue,ValueRange)},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_integer,4});
+ {'ENUMERATED',NamedNumberList} ->
+ emit({"?RT_BER:decode_enumerated(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_enumerated,4});
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_BER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_compact_bit_string,4});
+ _ ->
+ emit({"?RT_BER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},","}),
+ add_func({decode_bit_string,4})
+ end;
+ 'NULL' ->
+ emit({"?RT_BER:decode_null(",BytesVar,","}),
+ add_func({decode_null,2});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_BER:decode_object_identifier(",BytesVar,","}),
+ add_func({decode_object_identifier,2});
+ 'ObjectDescriptor' ->
+ emit({"?RT_BER:decode_restricted_string(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_ObjectDescriptor},","}),
+ add_func({decode_restricted_string,4});
+ 'OCTET STRING' ->
+ emit({"?RT_BER:decode_octet_string",AsBin,"(",BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_octet_string,3});
+ 'NumericString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_NumericString},","}),
+ add_func({decode_restricted_string,4});
+ 'TeletexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_TeletexString},","}),
+ add_func({decode_restricted_string,4});
+ 'VideotexString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VideotexString},","}),
+ add_func({decode_restricted_string,4});
+ 'GraphicString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GraphicString},","}),
+ add_func({decode_restricted_string,4});
+ 'VisibleString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_VisibleString},","}),
+ add_func({decode_restricted_string,4});
+ 'GeneralString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_GeneralString},","}),
+ add_func({decode_restricted_string,4});
+ 'PrintableString' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_PrintableString},","}),
+ add_func({decode_restricted_string,4});
+ 'IA5String' ->
+ emit({"?RT_BER:decode_restricted_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},",",{asis,?T_IA5String},","}),
+ add_func({decode_restricted_string,4}) ;
+ 'UniversalString' ->
+ emit({"?RT_BER:decode_universal_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_universal_string,3});
+ 'BMPString' ->
+ emit({"?RT_BER:decode_BMP_string",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_BMP_string,3});
+ 'UTCTime' ->
+ emit({"?RT_BER:decode_utc_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_utc_time,3});
+ 'GeneralizedTime' ->
+ emit({"?RT_BER:decode_generalized_time",AsBin,"(",
+ BytesVar,",",{asis,Constraint},","}),
+ add_func({decode_generalized_time,3});
+ 'ASN1_OPEN_TYPE' ->
+ emit(["?RT_BER:decode_open_type_as_binary(",
+ BytesVar,","]),
+ add_func({decode_open_type_as_binary,2});
+ Other ->
+ exit({'can not decode' ,Other})
+ end,
+
+ case {DoTag,NewTypeName} of
+ {{string,TagStr},'ASN1_OPEN_TYPE'} ->
+ emit([TagStr,")"]);
+ {_,'ASN1_OPEN_TYPE'} ->
+ emit([{asis,DoTag},")"]);
+ {{string,TagStr},_} ->
+ emit([TagStr,")"]);
+ _ when list(DoTag) ->
+ emit([{asis,DoTag},")"])
+ end.
+
+
+int_constr([],[]) ->
+ [];
+int_constr([],ValueRange) ->
+ ValueRange;
+int_constr(SingleValue,[]) ->
+ SingleValue;
+int_constr(SV,VR) ->
+ [SV,VR].
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=M,type=ClName} = Def#'Object'.classname,
+ Class = asn1_db:dbget(M,ClName),
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+ gen_encode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+ gen_decode_objectfields(ClName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed),
+ emit_tlv_format_function();
+gen_obj_code(_Erules,_Module,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Arg) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Arg,", _RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit([" {<<>>,0}"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause(" Val, [H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+% MaybeConstr=
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, RestPrimFieldName) ->",nl}),
+% CAcc=
+% case Type#typedef.name of
+% {primitive,bif} -> %%tag should be the primitive tag
+% OTag = Def#type.tag,
+% Tag = [encode_tag_val(decode_class(X#tag.class),
+% X#tag.form,X#tag.number)||
+% X <- OTag],
+% gen_encode_prim(ber,Def,{asis,lists:reverse(Tag)},
+% "Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val)"}),
+% [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,
+% "'(Val)"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val,[H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+% gen_encode_constr_type(Erules,[{Name,Def}|Rest]) ->
+% emit({Name,"(Val,TagIn) ->",nl}),
+% InnerType = asn1ct_gen:get_inner(Def#type.def),
+% asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+% gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ -> gen_encode_user(Erules,TypeDef)
+ end,
+ gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),
+ X#tag.form,X#tag.number)||
+ X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+% OTag = Def#type.tag,
+% Tag = [encode_tag_val(decode_class(X#tag.class),
+% X#tag.form,X#tag.number)||
+% X <- OTag],
+ gen_encode_prim(ber,Def,{asis,lists:reverse(Tag)},
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val,",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val,",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val,",{asis,Tag},")"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Bytes)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val, ",{asis,Tag},")",nl]),
+ []
+% 'ASN1_OPEN_TYPE' ->
+% emit(["%% OPEN TYPE",nl]),
+% gen_encode_prim(ber,
+% Type#type{def='ASN1_OPEN_TYPE'},
+% "TagIn","Val"),
+% emit([".",nl])
+ end.
+
+%%%%%%%%%%%%%%%%
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Arg) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Arg,",_) ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause(" _"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes"),
+ emit_tlv_format("Bytes"),
+ gen_decode_default_call(ClassName,Name,"Tlv",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes"),
+ emit_tlv_format("Bytes"),
+ gen_decode_field_call(ObjName,Name,"Tlv",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Args) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ", ",Args,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,[H|T]) ->",nl]),
+% emit_tlv_format("Bytes"),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,[H|T]"),
+% emit_tlv_format("Bytes"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+emit_tlv_format(Bytes) ->
+ notice_tlv_format_gen(), % notice for generating of tlv_format/1
+ emit([" Tlv = tlv_format(",Bytes,"),",nl]).
+
+notice_tlv_format_gen() ->
+ Module = get(currmod),
+% io:format("Noticed: ~p~n",[Module]),
+ case get(tlv_format) of
+ {done,Module} ->
+ ok;
+ _ -> % true or undefined
+ put(tlv_format,true)
+ end.
+
+emit_tlv_format_function() ->
+ Module = get(currmod),
+% io:format("Tlv formated: ~p",[Module]),
+ case get(tlv_format) of
+ true ->
+% io:format(" YES!~n"),
+ emit_tlv_format_function1(),
+ put(tlv_format,{done,Module});
+ _ ->
+% io:format(" NO!~n"),
+ ok
+ end.
+emit_tlv_format_function1() ->
+ emit(["tlv_format(Bytes) when binary(Bytes) ->",nl,
+ " {Tlv,_}=?RT_BER:decode(Bytes),",nl,
+ " Tlv;",nl,
+ "tlv_format(Bytes) ->",nl,
+ " Bytes.",nl]).
+
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+ emit([Name,"(Tlv, TagIn) ->",nl]),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(Erules,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+%%%%%%%%%%%
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ OTag = Def#type.tag,
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number ||
+ X <- OTag],
+ case Type#typedef.name of
+ {primitive,bif} -> %%tag should be the primitive tag
+ gen_dec_prim(ber,Def,Bytes,Tag,"TagIn",?PRIMITIVE,
+ opt_or_default),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",",{asis,Tag},")"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,",",{asis,Tag},")"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,",",{asis,Tag},")"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ OTag = Type#type.tag,
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,",",
+ {asis,Tag},")"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',
+ FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(ber,Type,Bytes,Tag,"TagIn",
+ ?PRIMITIVE,opt_or_default),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes, " ,",{asis,Tag},")",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", ",
+ {asis,Tag},")",nl]),
+ []
+% 'ASN1_OPEN_TYPE' ->
+% emit(["%% OPEN TYPE",nl]),
+% gen_encode_prim(ber,
+% Type#type{def='ASN1_OPEN_TYPE'},
+% "TagIn","Val"),
+% emit([".",nl])
+ end.
+%%%%%%%%%%%
+
+is_already_generated(Operation,Name) ->
+ case get(class_default_type) of
+ undefined ->
+ put(class_default_type,[{Operation,Name}]),
+ false;
+ GeneratedList ->
+ case lists:member({Operation,Name},GeneratedList) of
+ true ->
+ true;
+ false ->
+ put(class_default_type,[{Operation,Name}|GeneratedList]),
+ false
+ end
+ end.
+
+more_genfields([]) ->
+ false;
+more_genfields([Field|Fields]) ->
+ case element(1,Field) of
+ typefield ->
+ true;
+ objectfield ->
+ true;
+ _ ->
+ more_genfields(Fields)
+ end.
+
+
+
+
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = get_class_fields(ClassDef),
+ InternalFuncs=gen_objset_enc(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassFields,1,[]),
+ gen_objset_dec(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+%% gen_objset_enc iterates over the objects of the object set
+gen_objset_enc(_,_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(Erules,ObjSName,UniqueName,
+ [{ObjName,Val,Fields},T|Rest],ClName,ClFields,
+ NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(Erules,ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc ++ Acc);
+gen_objset_enc(_,ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,_} =
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc ++ Acc;
+%% See X.681 Annex E for the following case
+gen_objset_enc(_,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Val, _RestPrimFieldName) ->",nl}),
+ emit({indent(6),"Len = case Val of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Val)",nl,indent(6),"end,"}),
+ emit({indent(6),"{Val,Len}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _RestPrimFieldName) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(ber,Type,[{asis,lists:reverse(Tag)}],"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val)"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name},_) ->
+% OTag = Type#type.tag,
+% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ emit({indent(12),"'enc_",Name,"'(Val)"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+% OTag = Type#type.tag,
+% remove Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+% Tag = [encode_tag_val(decode_class(X#tag.class),X#tag.form,X#tag.number)|| X <- OTag],
+ case Type#type.def of
+ Def when atom(Def) ->
+ OTag = Type#type.tag,
+ Tag = [encode_tag_val(decode_class(X#tag.class),
+ X#tag.form,X#tag.number)||X <- OTag],
+ emit([indent(9),Def," ->",nl,indent(12)]),
+ gen_encode_prim(ber,Type,{asis,lists:reverse(Tag)},"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit([indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val)"]);
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),"'enc_",T,
+ "'(Val)"]);
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val)"])
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(Erules,ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj)->
+ emit(["'getdec_",ObjSName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl]),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit([" fun 'dec_",ObjName,"'/3"]),
+ NthObj
+ end,
+ emit([";",nl]),
+ gen_objset_dec(Erules,ObjSName,UniqueName,[T|Rest],ClName,
+ ClFields,NewNthObj);
+gen_objset_dec(_,ObjSetName,UniqueName,[{ObjName,Val,Fields}],
+ _ClName,ClFields,NthObj) ->
+ emit(["'getdec_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl]),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit([" fun 'dec_",ObjName,"'/3"])
+ end,
+ emit([".",nl,nl]),
+ ok;
+gen_objset_dec(Erules,ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj) ->
+ emit(["'getdec_",ObjSetName,"'(_, _) ->",nl]),
+ emit([indent(2),"fun(_,Bytes, _RestPrimFieldName) ->",nl]),
+ case Erules of
+ ber_bin_v2 ->
+ emit([indent(4),"case Bytes of",nl,
+ indent(6),"Bin when binary(Bin) -> ",nl,
+ indent(8),"Bin;",nl,
+ indent(6),"_ ->",nl,
+ indent(8),"?RT_BER:encode(Bytes)",nl,
+ indent(4),"end",nl]);
+ _ ->
+ emit([indent(6),"Len = case Bytes of",nl,indent(9),
+ "Bin when binary(Bin) -> size(Bin);",nl,indent(9),
+ "_ -> length(Bytes)",nl,indent(6),"end,"]),
+ emit([indent(4),"{Bytes,[],Len}",nl])
+ end,
+ emit([indent(2),"end.",nl,nl]),
+ ok;
+gen_objset_dec(_,_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl]),
+ N=emit_inner_of_decfun(Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit([indent(3),"fun(Type, Bytes, _RestPrimFieldName) ->",
+ nl,indent(6),"case Type of",nl]),
+ emit([indent(9),{asis,Name}," ->",nl]),
+ N=emit_inner_of_decfun(Type,DecProp,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Fields,[{typefield,Name,Prop}|Rest],
+ ObjSetName,NthObj) ->
+ DecProp = case Prop of
+ 'OPTIONAL' -> opt_or_default;
+ {'DEFAULT',_} -> opt_or_default;
+ _ -> mandatory
+ end,
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit([";",nl]),
+ emit_inner_of_decfun(Type,DecProp,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit([";",nl,indent(9),{asis,Name}," ->",nl]),
+ emit_inner_of_decfun(Type,DecProp,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,[],_,NthObj) ->
+ emit([nl,indent(6),"end",nl]),
+ emit([indent(3),"end"]),
+ NthObj.
+
+emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},Prop,
+ InternalDefFunName) ->
+ OTag = Type#type.tag,
+%% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(ber,Type,"Bytes",Tag,"TagIn",
+ ?PRIMITIVE,Prop),
+ 0;
+ {constructed,bif} ->
+ emit([indent(12),"'dec_",
+% asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",Prop,
+% ", ",{asis,Tag},")"]),
+ asn1ct_gen:list2name(InternalDefFunName),"'(Bytes, ",
+ {asis,Tag},")"]),
+ 1;
+ _ ->
+ emit([indent(12),"'",ExtName,"':'dec_",Name,"'(Bytes)"]),
+ 0
+ end;
+emit_inner_of_decfun(#typedef{name=Name},_Prop,_) ->
+ emit([indent(12),"'dec_",Name,"'(Bytes)"]),
+ 0;
+emit_inner_of_decfun(Type,Prop,_) when record(Type,type) ->
+ OTag = Type#type.tag,
+%% Tag = [X#tag{class=decode_class(X#tag.class)}|| X <- OTag],
+ Tag = [(decode_class(X#tag.class) bsl 10) + X#tag.number || X <- OTag],
+ CurrMod = get(currmod),
+ Def = Type#type.def,
+ InnerType = asn1ct_gen:get_inner(Def),
+ WhatKind = asn1ct_gen:type(InnerType),
+ case WhatKind of
+ {primitive,bif} ->
+ emit([indent(9),Def," ->",nl,indent(12)]),
+ gen_dec_prim(ber,Type,"Bytes",Tag,"TagIn",
+ ?PRIMITIVE,Prop);
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),"'dec_",T,
+% "'(Bytes, ",Prop,")"]);
+ "'(Bytes)"]);
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit([indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+% T,"'(Bytes, ",Prop,")"])
+ T,"'(Bytes)"])
+ end,
+ 0.
+
+gen_internal_funcs(_,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit([nl,nl,"'dec_",TypeDef#typedef.name,
+% "'(Tlv, OptOrMand, TagIn) ->",nl]),
+ "'(Tlv, TagIn) ->",nl]),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+
+decode_class('UNIVERSAL') ->
+ ?UNIVERSAL;
+decode_class('APPLICATION') ->
+ ?APPLICATION;
+decode_class('CONTEXT') ->
+ ?CONTEXT;
+decode_class('PRIVATE') ->
+ ?PRIVATE.
+
+decode_type('BOOLEAN') -> 1;
+decode_type('INTEGER') -> 2;
+decode_type('BIT STRING') -> 3;
+decode_type('OCTET STRING') -> 4;
+decode_type('NULL') -> 5;
+decode_type('OBJECT IDENTIFIER') -> 6;
+decode_type('OBJECT DESCRIPTOR') -> 7;
+decode_type('EXTERNAL') -> 8;
+decode_type('REAL') -> 9;
+decode_type('ENUMERATED') -> 10;
+decode_type('EMBEDDED_PDV') -> 11;
+decode_type('SEQUENCE') -> 16;
+decode_type('SEQUENCE OF') -> 16;
+decode_type('SET') -> 17;
+decode_type('SET OF') -> 17;
+decode_type('NumericString') -> 18;
+decode_type('PrintableString') -> 19;
+decode_type('TeletexString') -> 20;
+decode_type('VideotexString') -> 21;
+decode_type('IA5String') -> 22;
+decode_type('UTCTime') -> 23;
+decode_type('GeneralizedTime') -> 24;
+decode_type('GraphicString') -> 25;
+decode_type('VisibleString') -> 26;
+decode_type('GeneralString') -> 27;
+decode_type('UniversalString') -> 28;
+decode_type('BMPString') -> 30;
+decode_type('CHOICE') -> 'CHOICE'; % choice gets the tag from the actual alternative
+decode_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}).
+
+add_removed_bytes() ->
+ asn1ct_name:delete(rb),
+ add_removed_bytes(asn1ct_name:all(rb)).
+
+add_removed_bytes([H,T1|T]) ->
+ emit({{var,H},"+"}),
+ add_removed_bytes([T1|T]);
+add_removed_bytes([H|T]) ->
+ emit({{var,H}}),
+ add_removed_bytes(T);
+add_removed_bytes([]) ->
+ true.
+
+mkfuncname(WhatKind,DecOrEnc) ->
+ case WhatKind of
+ #'Externaltypereference'{module=Mod,type=EType} ->
+ CurrMod = get(currmod),
+ case CurrMod of
+ Mod ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ _ ->
+% io:format("CurrMod: ~p, Mod: ~p~n",[CurrMod,Mod]),
+ lists:concat(["'",Mod,"':'",DecOrEnc,"_",EType,"'"])
+ end;
+ #'typereference'{val=EType} ->
+ lists:concat(["'",DecOrEnc,"_",EType,"'"]);
+ 'ASN1_OPEN_TYPE' ->
+ lists:concat(["'",DecOrEnc,"_",WhatKind,"'"])
+
+ end.
+
+optionals(L) -> optionals(L,[],1).
+
+optionals([{'EXTENSIONMARK',_,_}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos); % optionals in extension are currently not handled
+optionals([#'ComponentType'{name=Name,prop='OPTIONAL'}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{name=Name,prop={'DEFAULT',_}}|Rest],Acc,Pos) ->
+ optionals(Rest,[{Name,Pos}|Acc],Pos+1);
+optionals([#'ComponentType'{}|Rest],Acc,Pos) ->
+ optionals(Rest,Acc,Pos+1);
+optionals([],Acc,_) ->
+ lists:reverse(Acc).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+
+get_class_fields(#classdef{typespec=ObjClass}) ->
+ ObjClass#objectclass.fields;
+get_class_fields(#objectclass{fields=Fields}) ->
+ Fields;
+get_class_fields(_) ->
+ [].
+
+get_object_field(Name,ObjectFields) ->
+ case lists:keysearch(Name,1,ObjectFields) of
+ {value,Field} -> Field;
+ false -> false
+ end.
+
+%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%% 8bit Int | binary
+encode_tag_val(Class, Form, TagNo) when (TagNo =< 30) ->
+ <<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
+
+encode_tag_val(Class, Form, TagNo) ->
+ {Octets,_Len} = mk_object_val(TagNo),
+ BinOct = list_to_binary(Octets),
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>.
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+add_func(F={_Func,_Arity}) ->
+ ets:insert(asn1_functab,{F}).
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per.erl
new file mode 100644
index 0000000000..8cd8d34918
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per.erl
@@ -0,0 +1,1190 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_per.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_gen_per).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-export([pgen/4,gen_dec_prim/3,gen_encode_prim/4]).
+-export([gen_obj_code/3,gen_objectset_code/2]).
+-export([gen_decode/2, gen_decode/3]).
+-export([gen_encode/2, gen_encode/3]).
+-export([is_already_generated/2,more_genfields/1,get_class_fields/1,
+ get_object_field/2]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%% Generate ENCODING ******************************
+%%****************************************x
+
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+%% case Type#typedef.typespec of
+%% Def when record(Def,type) ->
+%% gen_encode_user(Erules,Type);
+%% Def when tuple(Def),(element(1,Def) == 'Object') ->
+%% gen_encode_object(Erules,Type);
+%% Other ->
+%% exit({error,{asn1,{unknown,Other}}})
+%% end.
+
+gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTypename = [Cname|Typename],
+ gen_encode(Erules,NewTypename,Type);
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+%% lists:concat([", ObjFun",Name]);
+ ", ObjFun";
+ false ->
+ ""
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ case InnerType of
+ 'SET' ->
+ true;
+ 'SEQUENCE' ->
+ true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}",ObjFun,") ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",ObjFun,");",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun,
+ ") ->",nl}),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' -> true;
+ 'SEQUENCE' -> true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),"'({'",asn1ct_gen:list2name(Typename),"',Val}) ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_encode_prim(Erules,Def,"false"),
+ emit({".",nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_encode_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"false"),
+ emit({".",nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'enc_",Etype,"'(Val).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl});
+ #typereference{val=Ename} ->
+ emit({"'enc_",Ename,"'(Val).",nl,nl});
+ {notype,_} ->
+ emit({"'enc_",InnerType,"'(Val).",nl,nl})
+ end.
+
+
+gen_encode_prim(Erules,D,DoTag) ->
+ Value = case asn1ct_name:active(val) of
+ true ->
+ asn1ct_gen:mk_var(asn1ct_name:curr(val));
+ false ->
+ "Val"
+ end,
+ gen_encode_prim(Erules,D,DoTag,Value).
+
+gen_encode_prim(_Erules,D,_DoTag,Value) when record(D,type) ->
+ Constraint = D#type.constraint,
+ case D#type.def of
+ 'INTEGER' ->
+ emit({"?RT_PER:encode_integer(", %fel
+ {asis,Constraint},",",Value,")"});
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_PER:encode_integer(",
+ {asis,Constraint},",",Value,",",
+ {asis,NamedNumberList},")"});
+ {'ENUMERATED',{Nlist1,Nlist2}} ->
+ NewList = lists:concat([[{0,X}||{X,_} <- Nlist1],['EXT_MARK'],[{1,X}||{X,_} <- Nlist2]]),
+ NewC = [{'ValueRange',{0,length(Nlist1)-1}}],
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NewC, NewList++[{asn1_enum,length(Nlist1)-1}], 0);
+ {'ENUMERATED',NamedNumberList} ->
+ NewList = [X||{X,_} <- NamedNumberList],
+ NewC = [{'ValueRange',{0,length(NewList)-1}}],
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NewC, NewList, 0);
+ {'BIT STRING',NamedNumberList} ->
+ emit({"?RT_PER:encode_bit_string(",
+ {asis,Constraint},",",Value,",",
+ {asis,NamedNumberList},")"});
+ 'NULL' ->
+ emit({"?RT_PER:encode_null(",Value,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:encode_object_identifier(",Value,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:encode_ObjectDescriptor(",{asis,Constraint},
+ ",",Value,")"});
+ 'BOOLEAN' ->
+ emit({"?RT_PER:encode_boolean(",Value,")"});
+ 'OCTET STRING' ->
+ emit({"?RT_PER:encode_octet_string(",{asis,Constraint},",",Value,")"});
+ 'NumericString' ->
+ emit({"?RT_PER:encode_NumericString(",{asis,Constraint},",",Value,")"});
+ 'TeletexString' ->
+ emit({"?RT_PER:encode_TeletexString(",{asis,Constraint},",",Value,")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:encode_VideotexString(",{asis,Constraint},",",Value,")"});
+ 'UTCTime' ->
+ emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ 'GeneralizedTime' ->
+ emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ 'GraphicString' ->
+ emit({"?RT_PER:encode_GraphicString(",{asis,Constraint},",",Value,")"});
+ 'VisibleString' ->
+ emit({"?RT_PER:encode_VisibleString(",{asis,Constraint},",",Value,")"});
+ 'GeneralString' ->
+ emit({"?RT_PER:encode_GeneralString(",{asis,Constraint},",",Value,")"});
+ 'PrintableString' ->
+ emit({"?RT_PER:encode_PrintableString(",{asis,Constraint},",",Value,")"});
+ 'IA5String' ->
+ emit({"?RT_PER:encode_IA5String(",{asis,Constraint},",",Value,")"});
+ 'BMPString' ->
+ emit({"?RT_PER:encode_BMPString(",{asis,Constraint},",",Value,")"});
+ 'UniversalString' ->
+ emit({"?RT_PER:encode_UniversalString(",{asis,Constraint},",",Value,")"});
+ 'ANY' ->
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ Value, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ NewValue = case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ _ -> Value
+ end,
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ NewValue, ")"]);
+ XX ->
+ exit({asn1_error,nyi,XX})
+ end.
+
+emit_enc_enumerated_cases(C, [H], Count) ->
+ emit_enc_enumerated_case(C, H, Count),
+ emit([";",nl,"EnumVal -> exit({error,{asn1, {enumerated_not_in_range, EnumVal}}})"]),
+ emit([nl,"end"]);
+emit_enc_enumerated_cases(C, ['EXT_MARK'|T], _Count) ->
+ emit_enc_enumerated_cases(C, T, 0);
+emit_enc_enumerated_cases(C, [H1,H2|T], Count) ->
+ emit_enc_enumerated_case(C, H1, Count),
+ emit([";",nl]),
+ emit_enc_enumerated_cases(C, [H2|T], Count+1).
+
+
+
+emit_enc_enumerated_case(_C, {asn1_enum,High}, _) ->
+ emit([
+ "{asn1_enum,EnumV} when integer(EnumV), EnumV > ",High," -> ",
+ "[{bit,1},?RT_PER:encode_small_number(EnumV)]"]);
+emit_enc_enumerated_case(_C, 'EXT_MARK', _Count) ->
+ true;
+emit_enc_enumerated_case(_C, {1,EnumName}, Count) ->
+ emit(["'",EnumName,"' -> [{bit,1},?RT_PER:encode_small_number(",Count,")]"]);
+emit_enc_enumerated_case(C, {0,EnumName}, Count) ->
+ emit(["'",EnumName,"' -> [{bit,0},?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
+emit_enc_enumerated_case(C, EnumName, Count) ->
+ emit(["'",EnumName,"' -> ?RT_PER:encode_integer(",{asis,C},", ",Count,")"]).
+
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=Mod,type=ClassName} =
+ Def#'Object'.classname,
+ Class = asn1_db:dbget(Mod,ClassName),
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+ gen_encode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+ gen_decode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed),
+ emit(nl);
+gen_obj_code(_,_,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(V) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",V,",_RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, _RestPrimFieldName) ->",nl]),
+ MaybeConstr =
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit(" []"),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Val,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, Dummy) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_encode_prim(per,Def,"false","Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val)"}),
+% [{['enc_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,"'(Val)"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_encode_objectfields(C,O,[H|T],Acc) ->
+% gen_encode_objectfields(C,O,T,Acc);
+% gen_encode_objectfields(_,_,[],Acc) ->
+% Acc.
+
+% gen_encode_constr_type(Erules,[{Name,Def}|Rest]) ->
+% emit({Name,"(Val) ->",nl}),
+% InnerType = asn1ct_gen:get_inner(Def#type.def),
+% asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+% gen_encode_constr_type(Erules,Rest);
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ Name = lists:concat(["enc_",TypeDef#typedef.name]),
+ emit({Name,"(Val) ->",nl}),
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+ gen_encode_constr_type(Erules,Rest)
+ end;
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_encode_prim(per,Def,"false",
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val)"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val)"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Val)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(per,Type,"false","Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val)",nl]),
+ []
+ end.
+
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Bytes) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},",",Bytes,
+ ",_,_RestPrimFieldName) ->",nl])
+ end,
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes"),
+ gen_decode_default_call(ClassName,Name,"Bytes",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes"),
+ gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,_,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,_,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, telltype, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, telltype, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+
+% gen_decode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, RestPrimFieldName) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_dec_prim(per,Def,"Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'dec_",ObjName,'_',FieldName,
+% "'(Val, Telltype)"}),
+% [{['dec_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'dec_",TypeName,
+% "'(Val, Telltype)"}),
+% [];
+% TypeName ->
+% emit({" 'dec_",TypeName,"'(Val, Telltype)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+% "'(H, Val, Telltype, T)"});
+% TypeName ->
+% emit({indent(3),"'dec_",TypeName,
+% "'(H, Val, Telltype, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} ->
+% []
+% end,
+% gen_decode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_decode_objectfields(C,O,[H|T],CAcc) ->
+% gen_decode_objectfields(C,O,T,CAcc);
+% gen_decode_objectfields(_,_,[],CAcc) ->
+% CAcc.
+
+
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_dec_prim(per,Def,Bytes),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",telltype)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,", telltype)"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,", telltype)"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,", telltype)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(per,Type,Bytes),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ []
+ end.
+
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+ emit({Name,"(Bytes,_) ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(Erules,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+% more_genfields(Fields,[]) ->
+% false;
+% more_genfields(Fields,[{FieldName,_}|T]) ->
+% case is_typefield(Fields,FieldName) of
+% true -> true;
+% {false,objectfield} -> true;
+% {false,_} -> more_genfields(Fields,T)
+% end.
+
+more_genfields([]) ->
+ false;
+more_genfields([Field|Fields]) ->
+ case element(1,Field) of
+ typefield ->
+ true;
+ objectfield ->
+ true;
+ _ ->
+ more_genfields(Fields)
+ end.
+
+% is_typefield(Fields,FieldName) ->
+% case lists:keysearch(FieldName,2,Fields) of
+% {value,Field} ->
+% case element(1,Field) of
+% typefield ->
+% true;
+% Other ->
+% {false,Other}
+% end;
+% _ ->
+% false
+% end.
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+%% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = (ClassDef#classdef.typespec)#objectclass.fields,
+ InternalFuncs=
+ gen_objset_enc(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1,[]),
+ gen_objset_dec(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+%% gen_objset_enc iterates over the objects of the object set
+gen_objset_enc(_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],0}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc ++ Acc);
+gen_objset_enc(ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,_}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _Other ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc++Acc;
+gen_objset_enc(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Val, _) ->",nl}),
+ emit({indent(6),"[{octets,Val}]",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N} = emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_H|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_H|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(per,Type,dotag,"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val)"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name},_) ->
+ emit({indent(12),"'enc_",Name,"'(Val)"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_encode_prim(erules,Type,dotag,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val)"})
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName,
+ ClFields,NthObj)->
+
+ emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"}),
+ NthObj
+ end,
+ emit({";",nl}),
+ gen_objset_dec(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NewNthObj);
+gen_objset_dec(ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName,
+ ClFields,NthObj) ->
+
+ emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",{asis,Val},
+ ") ->",nl}),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj);
+ _Other ->
+ emit({" fun 'dec_",ObjName,"'/4"})
+ end,
+ emit({".",nl,nl}),
+ ok;
+gen_objset_dec(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,_ClFields,
+ _NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(Attr1, Bytes, _,_) ->",nl}),
+%% emit({indent(6),"?RT_PER:decode_open_type(Bytes,[])",nl}),
+ emit({indent(6),"{Bytes,Attr1}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ ok;
+gen_objset_dec(_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,[],_,NthObj) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ NthObj.
+
+emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(per,Type,"Val"),
+ 0;
+ {constructed,bif} ->
+ emit({indent(12),"'dec_",
+ asn1ct_gen:list2name(InternalDefFunName),"'(Val)"}),
+ 1;
+ _ ->
+ emit({indent(12),"'",ExtName,"':'dec_",Name,"'(Val, telltype)"}),
+ 0
+ end;
+emit_inner_of_decfun(#typedef{name=Name},_) ->
+ emit({indent(12),"'dec_",Name,"'(Val, telltype)"}),
+ 0;
+emit_inner_of_decfun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_dec_prim(erules,Type,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+ T,"'(Val)"})
+ end,
+ 0.
+
+
+gen_internal_funcs(_,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit([nl,nl,"'dec_",TypeDef#typedef.name,"'(Bytes) ->",nl]),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+
+%% DECODING *****************************
+%%***************************************
+
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ D = Type,
+ emit({nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes,_) ->",nl}),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,D).
+
+gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTname = [Cname|Tname],
+ gen_decode(Erules,NewTname,Type);
+
+gen_decode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({nl,"'dec_",asn1ct_gen:list2name(Typename),
+ "'(Bytes,_",ObjFun,") ->",nl}),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+dbdec(Type) when list(Type)->
+ demit({"io:format(\"decoding: ",asn1ct_gen:list2name(Type),"~w~n\",[Bytes]),",nl});
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_dec_prim(Erules,Def,"Bytes"),
+ emit({".",nl,nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"),
+ emit({".",nl,nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(Bytes,telltype)"}),
+ emit({".",nl,nl});
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ Other ->
+ exit({error,{asn1,{unknown,Other}}})
+ end.
+
+
+gen_dec_prim(_Erules,Att,BytesVar) ->
+ Typename = Att#type.def,
+ Constraint = Att#type.constraint,
+ case Typename of
+ 'INTEGER' ->
+ emit({"?RT_PER:decode_integer(",BytesVar,",",
+ {asis,Constraint},")"});
+ {'INTEGER',NamedNumberList} ->
+ emit({"?RT_PER:decode_integer(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},")"});
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_PER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},")"});
+ _ ->
+ emit({"?RT_PER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},")"})
+ end;
+ 'NULL' ->
+ emit({"?RT_PER:decode_null(",
+ BytesVar,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:decode_object_identifier(",
+ BytesVar,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:decode_ObjectDescriptor(",
+ BytesVar,")"});
+ {'ENUMERATED',{NamedNumberList1,NamedNumberList2}} ->
+ NewTup = {list_to_tuple([X||{X,_} <- NamedNumberList1]),
+ list_to_tuple([X||{X,_} <- NamedNumberList2])},
+ NewC = [{'ValueRange',{0,size(element(1,NewTup))-1}}],
+ emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+ {asis,NewC},",",
+ {asis,NewTup},")"});
+ {'ENUMERATED',NamedNumberList} ->
+ NewTup = list_to_tuple([X||{X,_} <- NamedNumberList]),
+ NewC = [{'ValueRange',{0,size(NewTup)-1}}],
+ emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+ {asis,NewC},",",
+ {asis,NewTup},")"});
+ 'BOOLEAN'->
+ emit({"?RT_PER:decode_boolean(",BytesVar,")"});
+ 'OCTET STRING' ->
+ emit({"?RT_PER:decode_octet_string(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'NumericString' ->
+ emit({"?RT_PER:decode_NumericString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'TeletexString' ->
+ emit({"?RT_PER:decode_TeletexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:decode_VideotexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'UTCTime' ->
+ emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'GeneralizedTime' ->
+ emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'GraphicString' ->
+ emit({"?RT_PER:decode_GraphicString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VisibleString' ->
+ emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'GeneralString' ->
+ emit({"?RT_PER:decode_GeneralString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'PrintableString' ->
+ emit({"?RT_PER:decode_PrintableString(",BytesVar,",",{asis,Constraint},")"});
+ 'IA5String' ->
+ emit({"?RT_PER:decode_IA5String(",BytesVar,",",{asis,Constraint},")"});
+ 'BMPString' ->
+ emit({"?RT_PER:decode_BMPString(",BytesVar,",",{asis,Constraint},")"});
+ 'UniversalString' ->
+ emit({"?RT_PER:decode_UniversalString(",BytesVar,",",{asis,Constraint},")"});
+ 'ANY' ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",",
+ {asis,Constraint}, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ _ ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",[])"])
+ end;
+ Other ->
+ exit({'cant decode' ,Other})
+ end.
+
+
+is_already_generated(Operation,Name) ->
+ case get(class_default_type) of
+ undefined ->
+ put(class_default_type,[{Operation,Name}]),
+ false;
+ GeneratedList ->
+ case lists:member({Operation,Name},GeneratedList) of
+ true ->
+ true;
+ false ->
+ put(class_default_type,[{Operation,Name}|GeneratedList]),
+ false
+ end
+ end.
+
+get_class_fields(#classdef{typespec=ObjClass}) ->
+ ObjClass#objectclass.fields;
+get_class_fields(#objectclass{fields=Fields}) ->
+ Fields;
+get_class_fields(_) ->
+ [].
+
+
+get_object_field(Name,ObjectFields) ->
+ case lists:keysearch(Name,1,ObjectFields) of
+ {value,Field} -> Field;
+ false -> false
+ end.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl
new file mode 100644
index 0000000000..70a017ac6a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_gen_per_rt2ct.erl
@@ -0,0 +1,1811 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_gen_per_rt2ct.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_gen_per_rt2ct).
+
+%% Generate erlang module which handles (PER) encode and decode for
+%% all types in an ASN.1 module
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-export([pgen/4,gen_dec_prim/3,gen_encode_prim/4]).
+-export([gen_obj_code/3,gen_objectset_code/2]).
+-export([gen_decode/2, gen_decode/3]).
+-export([gen_encode/2, gen_encode/3]).
+
+-import(asn1ct_gen, [emit/1,demit/1]).
+-import(asn1ct_gen_per, [is_already_generated/2,more_genfields/1,
+ get_class_fields/1,get_object_field/2]).
+
+%% pgen(Erules, Module, TypeOrVal)
+%% Generate Erlang module (.erl) and (.hrl) file corresponding to an ASN.1 module
+%% .hrl file is only generated if necessary
+%% Erules = per | ber
+%% Module = atom()
+%% TypeOrVal = {TypeList,ValueList}
+%% TypeList = ValueList = [atom()]
+
+pgen(OutFile,Erules,Module,TypeOrVal) ->
+ asn1ct_gen:pgen_module(OutFile,Erules,Module,TypeOrVal,true).
+
+
+%% Generate ENCODING ******************************
+%%****************************************x
+
+
+gen_encode(Erules,Type) when record(Type,typedef) ->
+ gen_encode_user(Erules,Type).
+
+gen_encode(Erules,Typename,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTypename = [Cname|Typename],
+ gen_encode(Erules,NewTypename,Type);
+
+gen_encode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ ObjFun =
+ case lists:keysearch(objfun,1,Type#type.tablecinf) of
+ {value,{_,_Name}} ->
+ ", ObjFun";
+ false ->
+ ""
+ end,
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ case InnerType of
+ 'SET' ->
+ true;
+ 'SEQUENCE' ->
+ true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),
+ "'({'",asn1ct_gen:list2name(Typename),
+ "',Val}",ObjFun,") ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),
+ "'(Val",ObjFun,");",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val",ObjFun,
+ ") ->",nl}),
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+
+gen_encode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case InnerType of
+ 'SET' -> true;
+ 'SEQUENCE' -> true;
+ _ ->
+ emit({nl,"'enc_",asn1ct_gen:list2name(Typename),"'({'",asn1ct_gen:list2name(Typename),"',Val}) ->",nl}),
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val);",nl,nl})
+ end,
+ emit({"'enc_",asn1ct_gen:list2name(Typename),"'(Val) ->",nl}),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_encode_prim(Erules,Def,"false"),
+ emit({".",nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_encode_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"false"),
+ emit({".",nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,D);
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'enc_",Etype,"'(Val).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'enc_",Etype,"'(Val).",nl,nl});
+ #typereference{val=Ename} ->
+ emit({"'enc_",Ename,"'(Val).",nl,nl});
+ {notype,_} ->
+ emit({"'enc_",InnerType,"'(Val).",nl,nl})
+ end.
+
+
+gen_encode_prim(Erules,D,DoTag) ->
+ Value = case asn1ct_name:active(val) of
+ true ->
+ asn1ct_gen:mk_var(asn1ct_name:curr(val));
+ false ->
+ "Val"
+ end,
+ gen_encode_prim(Erules,D,DoTag,Value).
+
+
+
+
+
+gen_encode_prim(_Erules,D,_DoTag,Value) when record(D,type) ->
+ Constraint = D#type.constraint,
+ case D#type.def of
+ 'INTEGER' ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ emit([" %%INTEGER with effective constraint: ",
+ {asis,EffectiveConstr},nl]),
+ emit_enc_integer(EffectiveConstr,Value);
+ {'INTEGER',NamedNumberList} ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ %% maybe an emit_enc_NNL_integer
+ emit([" %%INTEGER with effective constraint: ",
+ {asis,EffectiveConstr},nl]),
+ emit_enc_integer_NNL(EffectiveConstr,Value,NamedNumberList);
+ {'ENUMERATED',{Nlist1,Nlist2}} ->
+ NewList = lists:concat([[{0,X}||{X,_} <- Nlist1],['EXT_MARK'],[{1,X}||{X,_} <- Nlist2]]),
+ NewC = [{'ValueRange',{0,length(Nlist1)-1}}],
+ emit(["case (case ",Value," of {_,_}->element(2,",Value,");_->",
+ Value," end) of",nl]),
+ emit_enc_enumerated_cases(NewC, NewList++[{asn1_enum,length(Nlist1)-1}], 0);
+ {'ENUMERATED',NamedNumberList} ->
+ NewList = [X||{X,_} <- NamedNumberList],
+ NewC = effective_constraint(integer,
+ [{'ValueRange',
+ {0,length(NewList)-1}}]),
+ NewVal = enc_enum_cases(Value,NewList),
+ emit_enc_integer(NewC,NewVal);
+ {'BIT STRING',NamedNumberList} ->
+ EffectiveC = effective_constraint(bitstring,Constraint),
+ case EffectiveC of
+ 0 -> emit({"[]"});
+ _ ->
+ emit({"?RT_PER:encode_bit_string(",
+ {asis,EffectiveC},",",Value,",",
+ {asis,NamedNumberList},")"})
+ end;
+ 'NULL' ->
+ emit({"?RT_PER:encode_null(",Value,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:encode_object_identifier(",Value,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:encode_ObjectDescriptor(",{asis,Constraint},
+ ",",Value,")"});
+ 'BOOLEAN' ->
+% emit({"?RT_PER:encode_boolean(",Value,")"});
+ emit({"case ",Value," of",nl,
+% " true -> {bits,1,1};",nl,
+ " true -> [1];",nl,
+% " false -> {bits,1,0};",nl,
+ " false -> [0];",nl,
+ " _ -> exit({error,{asn1,{encode_boolean,",Value,"}}})",nl,
+ "end"});
+ 'OCTET STRING' ->
+ emit_enc_octet_string(Constraint,Value);
+
+ 'NumericString' ->
+ emit_enc_known_multiplier_string('NumericString',Constraint,Value);
+ 'TeletexString' ->
+ emit({"?RT_PER:encode_TeletexString(",{asis,Constraint},",",Value,")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:encode_VideotexString(",{asis,Constraint},",",Value,")"});
+ 'UTCTime' ->
+ emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
+ 'GeneralizedTime' ->
+ emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
+ 'GraphicString' ->
+ emit({"?RT_PER:encode_GraphicString(",{asis,Constraint},",",Value,")"});
+ 'VisibleString' ->
+ emit_enc_known_multiplier_string('VisibleString',Constraint,Value);
+ 'GeneralString' ->
+ emit({"?RT_PER:encode_GeneralString(",{asis,Constraint},",",Value,")"});
+ 'PrintableString' ->
+ emit_enc_known_multiplier_string('PrintableString',Constraint,Value);
+ 'IA5String' ->
+ emit_enc_known_multiplier_string('IA5String',Constraint,Value);
+ 'BMPString' ->
+ emit_enc_known_multiplier_string('BMPString',Constraint,Value);
+ 'UniversalString' ->
+ emit_enc_known_multiplier_string('UniversalString',Constraint,Value);
+ 'ANY' ->
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ Value, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ NewValue = case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ io_lib:format(
+ "?RT_PER:complete(enc_~s(~s))",[Tname,Value]);
+ _ -> Value
+ end,
+ emit(["?RT_PER:encode_open_type(", {asis,Constraint}, ",",
+ NewValue, ")"]);
+ XX ->
+ exit({asn1_error,nyi,XX})
+ end.
+
+emit_enc_known_multiplier_string(StringType,C,Value) ->
+ SizeC =
+ case get_constraint(C,'SizeConstraint') of
+ L when list(L) -> {lists:min(L),lists:max(L)};
+ L -> L
+ end,
+ PAlphabC = get_constraint(C,'PermittedAlphabet'),
+ case {StringType,PAlphabC} of
+ {'UniversalString',{_,_}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with "
+ "PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with "
+ "PermittedAlphabet constraint"}}});
+ _ -> ok
+ end,
+ NumBits = get_NumBits(C,StringType),
+ CharOutTab = get_CharOutTab(C,StringType),
+ %% NunBits and CharOutTab for chars_encode
+ emit_enc_k_m_string(StringType,SizeC,NumBits,CharOutTab,Value).
+
+emit_enc_k_m_string(_StringType,0,_NumBits,_CharOutTab,_Value) ->
+ emit({"[]"});
+emit_enc_k_m_string(StringType,SizeC,NumBits,CharOutTab,Value) ->
+ emit({"?RT_PER:encode_known_multiplier_string(",{asis,StringType},",",
+ {asis,SizeC},",",NumBits,",",{asis,CharOutTab},",",Value,")"}).
+
+emit_dec_known_multiplier_string(StringType,C,BytesVar) ->
+ SizeC = get_constraint(C,'SizeConstraint'),
+ PAlphabC = get_constraint(C,'PermittedAlphabet'),
+ case {StringType,PAlphabC} of
+ {'BMPString',{_,_}} ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet "
+ "constraint"}}});
+ _ ->
+ ok
+ end,
+ NumBits = get_NumBits(C,StringType),
+ CharInTab = get_CharInTab(C,StringType),
+ case SizeC of
+ 0 ->
+ emit({"{[],",BytesVar,"}"});
+ _ ->
+ emit({"?RT_PER:decode_known_multiplier_string(",
+ {asis,StringType},",",{asis,SizeC},",",NumBits,
+ ",",{asis,CharInTab},",",BytesVar,")"})
+ end.
+
+
+%% copied from run time module
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B =< 4 -> 4;
+ B when B =< 8 -> 8;
+ B when B =< 16 -> 16;
+ B when B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+%% copied from run time module
+
+emit_enc_octet_string(Constraint,Value) ->
+ case get_constraint(Constraint,'SizeConstraint') of
+ 0 ->
+ emit({" []"});
+ 1 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+ emit({" [",{curr,tmpval},"] = ",Value,",",nl}),
+% emit({" {bits,8,",{curr,tmpval},"}",nl}),
+ emit({" [10,8,",{curr,tmpval},"]",nl}),
+ emit(" end");
+ 2 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+ emit({" [",{curr,tmpval},",",{next,tmpval},"] = ",
+ Value,",",nl}),
+% emit({" [{bits,8,",{curr,tmpval},"},{bits,8,",
+% {next,tmpval},"}]",nl}),
+ emit({" [[10,8,",{curr,tmpval},"],[10,8,",
+ {next,tmpval},"]]",nl}),
+ emit(" end"),
+ asn1ct_name:new(tmpval);
+ Sv when integer(Sv),Sv =< 256 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+% emit({" case length(",Value,") == ",Sv," of",nl}),
+ emit({" case length(",Value,") of",nl}),
+ emit({" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," -> [2,20,",{curr,tmpval},",",Value,"];",nl}),
+ emit({" _ -> exit({error,{value_out_of_bounds,",Value,"}})",
+ nl," end",nl}),
+ emit(" end");
+ Sv when integer(Sv),Sv =< 65535 ->
+ asn1ct_name:new(tmpval),
+ emit({" begin",nl}),
+% emit({" case length(",Value,") == ",Sv," of",nl}),
+ emit({" case length(",Value,") of",nl}),
+% emit({" true -> [align,{octets,",Value,"}];",nl}),
+ emit({" ",{curr,tmpval}," when ",{curr,tmpval}," == ",Sv," -> [2,21,",{curr,tmpval},",",Value,"];",nl}),
+ emit({" _ -> exit({error,{value_out_of_bounds,",Value,"}})",
+ nl," end",nl}),
+ emit(" end");
+ C ->
+ emit({" ?RT_PER:encode_octet_string(",{asis,C},",false,",Value,")",nl})
+ end.
+
+emit_dec_octet_string(Constraint,BytesVar) ->
+ case get_constraint(Constraint,'SizeConstraint') of
+ 0 ->
+ emit({" {[],",BytesVar,"}",nl});
+ {_,0} ->
+ emit({" {[],",BytesVar,"}",nl});
+ C ->
+ emit({" ?RT_PER:decode_octet_string(",BytesVar,",",
+ {asis,C},",false)",nl})
+ end.
+
+emit_enc_integer_case(Value) ->
+ case get(component_type) of
+ {true,#'ComponentType'{prop=Prop}} ->
+ emit({" begin",nl}),
+ case Prop of
+ Opt when Opt=='OPTIONAL';
+ tuple(Opt),element(1,Opt)=='DEFAULT' ->
+ emit({" case ",Value," of",nl}),
+ ok;
+ _ ->
+ emit({" ",{curr,tmpval},"=",Value,",",nl}),
+ emit({" case ",{curr,tmpval}," of",nl}),
+ asn1ct_name:new(tmpval)
+ end;
+% asn1ct_name:new(tmpval);
+ _ ->
+ emit({" case ",Value," of ",nl})
+ end.
+emit_enc_integer_end_case() ->
+ case get(component_type) of
+ {true,_} ->
+ emit({nl," end"}); % end of begin ... end
+ _ -> ok
+ end.
+
+
+emit_enc_integer_NNL(C,Value,NNL) ->
+ EncVal = enc_integer_NNL_cases(Value,NNL),
+ emit_enc_integer(C,EncVal).
+
+enc_integer_NNL_cases(Value,NNL) ->
+ asn1ct_name:new(tmpval),
+ TmpVal = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ Cases=enc_integer_NNL_cases1(NNL),
+ lists:flatten(io_lib:format("(case ~s of "++Cases++
+ "~s when atom(~s)->exit({error,{asn1,{namednumber,~s}}});_->~s end)",[Value,TmpVal,TmpVal,TmpVal,Value])).
+
+enc_integer_NNL_cases1([{NNo,No}|Rest]) ->
+ io_lib:format("~w->~w;",[NNo,No])++enc_integer_NNL_cases1(Rest);
+enc_integer_NNL_cases1([]) ->
+ "".
+
+emit_enc_integer([{'SingleValue',Int}],Value) ->
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),% emit([" case ",Value," of",nl]),
+ emit([" ",Int," -> [];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+emit_enc_integer([{_,{Lb,Ub},_Range,{bits,NoBs}}],Value) -> % Range =< 255
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),
+ emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
+ {curr,tmpval},">=",Lb," ->",nl]),
+ emit([" [10,",NoBs,",",{curr,tmpval},"-",Lb,"];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+emit_enc_integer([{_,{Lb,Ub},Range,_}],Value) when Range =< 256 ->
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),
+ emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
+ {curr,tmpval},">=",Lb," ->",nl]),
+ emit([" [20,1,",{curr,tmpval},"-",Lb,"];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+emit_enc_integer([{_,{Lb,Ub},Range,_}],Value) when Range =< 65536 ->
+ asn1ct_name:new(tmpval),
+ emit_enc_integer_case(Value),
+ emit([" ",{curr,tmpval}," when ",{curr,tmpval},"=<",Ub,",",
+ {curr,tmpval},">=",Lb," ->",nl]),
+ emit([" [20,2,<<(",{curr,tmpval},"-",Lb,"):16>>];",nl]),
+ emit([" ",{curr,tmpval}," ->",nl]),
+ emit([" exit({error,{value_out_of_bounds,",{curr,tmpval},"}})",
+ nl," end",nl]),
+ emit_enc_integer_end_case();
+
+
+emit_enc_integer(C,Value) ->
+ emit({" ?RT_PER:encode_integer(",{asis,C},",",Value,")"}).
+
+
+
+
+enc_enum_cases(Value,NewList) ->
+ asn1ct_name:new(tmpval),
+ TmpVal = asn1ct_gen:mk_var(asn1ct_name:curr(tmpval)),
+ Cases=enc_enum_cases1(NewList),
+ lists:flatten(io_lib:format("(case ~s of "++Cases++
+ "~s ->exit({error,"
+ "{asn1,{enumerated,~s}}})"
+ " end)",
+ [Value,TmpVal,TmpVal])).
+enc_enum_cases1(NNL) ->
+ enc_enum_cases1(NNL,0).
+enc_enum_cases1([H|T],Index) ->
+ io_lib:format("~w->~w;",[H,Index])++enc_enum_cases1(T,Index+1);
+enc_enum_cases1([],_) ->
+ "".
+
+
+emit_enc_enumerated_cases(C, [H], Count) ->
+ emit_enc_enumerated_case(C, H, Count),
+ emit([";",nl,"EnumVal -> exit({error,{asn1, {enumerated_not_in_range, EnumVal}}})"]),
+ emit([nl,"end"]);
+emit_enc_enumerated_cases(C, ['EXT_MARK'|T], _Count) ->
+ emit_enc_enumerated_cases(C, T, 0);
+emit_enc_enumerated_cases(C, [H1,H2|T], Count) ->
+ emit_enc_enumerated_case(C, H1, Count),
+ emit([";",nl]),
+ emit_enc_enumerated_cases(C, [H2|T], Count+1).
+
+
+%% The function clauses matching on tuples with first element
+%% asn1_enum, 1 or 0 and the atom 'EXT_MARK' are for ENUMERATED
+%% with extension mark.
+emit_enc_enumerated_case(_C, {asn1_enum,High}, _) ->
+ %% ENUMERATED with extensionmark
+ %% value higher than the extension base and not
+ %% present in the extension range.
+ emit(["{asn1_enum,EnumV} when integer(EnumV), EnumV > ",High," -> ",
+ "[1,?RT_PER:encode_small_number(EnumV)]"]);
+emit_enc_enumerated_case(_C, 'EXT_MARK', _Count) ->
+ %% ENUMERATED with extensionmark
+ true;
+emit_enc_enumerated_case(_C, {1,EnumName}, Count) ->
+ %% ENUMERATED with extensionmark
+ %% values higher than extension root
+ emit(["'",EnumName,"' -> [1,?RT_PER:encode_small_number(",Count,")]"]);
+emit_enc_enumerated_case(C, {0,EnumName}, Count) ->
+ %% ENUMERATED with extensionmark
+ %% values within extension root
+ emit(["'",EnumName,"' -> [0,?RT_PER:encode_integer(",{asis,C},", ",Count,")]"]);
+
+%% This clause is invoked in case of an ENUMERATED without extension mark
+emit_enc_enumerated_case(_C, EnumName, Count) ->
+ emit(["'",EnumName,"' -> ",Count]).
+
+
+get_constraint([{Key,V}],Key) ->
+ V;
+get_constraint([],_) ->
+ no;
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+get_constraints(L=[{Key,_}],Key) ->
+ L;
+get_constraints([],_) ->
+ [];
+get_constraints(C,Key) ->
+ {value,L} = keysearch_allwithkey(Key,1,C,[]),
+ L.
+
+keysearch_allwithkey(Key,Ix,C,Acc) ->
+ case lists:keysearch(Key,Ix,C) of
+ false ->
+ {value,Acc};
+ {value,T} ->
+ RestC = lists:delete(T,C),
+ keysearch_allwithkey(Key,Ix,RestC,[T|Acc])
+ end.
+
+%% effective_constraint(Type,C)
+%% Type = atom()
+%% C = [C1,...]
+%% C1 = {'SingleValue',SV} | {'ValueRange',VR} | {atom(),term()}
+%% SV = integer() | [integer(),...]
+%% VR = {Lb,Ub}
+%% Lb = 'MIN' | integer()
+%% Ub = 'MAX' | integer()
+%% Returns a single value if C only has a single value constraint, and no
+%% value range constraints, that constrains to a single value, otherwise
+%% returns a value range that has the lower bound set to the lowest value
+%% of all single values and lower bound values in C and the upper bound to
+%% the greatest value.
+effective_constraint(integer,[C={{_,_},_}|_Rest]) -> % extension
+ [C]; %% [C|effective_constraint(integer,Rest)]; XXX what is possible ???
+effective_constraint(integer,C) ->
+ SVs = get_constraints(C,'SingleValue'),
+ SV = effective_constr('SingleValue',SVs),
+ VRs = get_constraints(C,'ValueRange'),
+ VR = effective_constr('ValueRange',VRs),
+ CRange = greatest_common_range(SV,VR),
+ pre_encode(integer,CRange);
+effective_constraint(bitstring,C) ->
+% Constr=get_constraints(C,'SizeConstraint'),
+% case Constr of
+% [] -> no;
+% [{'SizeConstraint',Val}] -> Val;
+% Other -> Other
+% end;
+ get_constraint(C,'SizeConstraint');
+effective_constraint(Type,C) ->
+ io:format("Effective constraint for ~p, not implemented yet.~n",[Type]),
+ C.
+
+effective_constr(_,[]) ->
+ [];
+effective_constr('SingleValue',List) ->
+ SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)),
+ case lists:usort(SVList) of
+ [N] ->
+ [{'SingleValue',N}];
+ L when list(L) ->
+ [{'ValueRange',{hd(L),lists:last(L)}}]
+ end;
+effective_constr('ValueRange',List) ->
+ LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List),
+ UBs = lists:map(fun({_,{_,Ub}})-> Ub end,List),
+ Lb = least_Lb(LBs),
+ [{'ValueRange',{Lb,lists:max(UBs)}}].
+
+greatest_common_range([],VR) ->
+ VR;
+greatest_common_range(SV,[]) ->
+ SV;
+greatest_common_range([{_,Int}],[{_,{'MIN',Ub}}]) when integer(Int),
+ Int > Ub ->
+ [{'ValueRange',{'MIN',Int}}];
+greatest_common_range([{_,Int}],[{_,{Lb,Ub}}]) when integer(Int),
+ Int < Lb ->
+ [{'ValueRange',{Int,Ub}}];
+greatest_common_range([{_,Int}],VR=[{_,{_Lb,_Ub}}]) when integer(Int) ->
+ VR;
+greatest_common_range([{_,L}],[{_,{Lb,Ub}}]) when list(L) ->
+ Min = least_Lb([Lb|L]),
+ Max = greatest_Ub([Ub|L]),
+ [{'ValueRange',{Min,Max}}].
+
+
+least_Lb(L) ->
+ case lists:member('MIN',L) of
+ true -> 'MIN';
+ _ -> lists:min(L)
+ end.
+
+greatest_Ub(L) ->
+ case lists:member('MAX',L) of
+ true -> 'MAX';
+ _ -> lists:max(L)
+ end.
+
+% effective_constraint1('SingleValue',List) ->
+% SVList = lists:map(fun(X)->element(2,X)end,List),
+% sv_effective_constraint(hd(SVList),tl(SVList));
+% effective_constraint1('ValueRange',List) ->
+% VRList = lists:map(fun(X)->element(2,X)end,List),
+% vr_effective_constraint(lists:map(fun(X)->element(1,X)end,VRList),
+% lists:map(fun(X)->element(2,X)end,VRList)).
+
+%% vr_effective_constraint/2
+%% Gets all LowerEndPoints and UpperEndPoints as arguments
+%% Returns {'ValueRange',{Lb,Ub}} where Lb is the highest value of
+%% the LowerEndPoints and Ub is the lowest value of the UpperEndPoints,
+%% i.e. the intersection of all value ranges.
+% vr_effective_constraint(Mins,Maxs) ->
+% Lb=lists:foldl(fun(X,'MIN') when integer(X) -> X;
+% (X,'MIN') -> 'MIN';
+% (X,AccIn) when integer(X),X >= AccIn -> X;
+% (X,AccIn) -> AccIn
+% end,hd(Mins),tl(Mins)),
+% Ub = lists:min(Maxs),
+% {'ValueRange',{Lb,Ub}}.
+
+
+% sv_effective_constraint(SV,[]) ->
+% {'SingleValue',SV};
+% sv_effective_constraint([],_) ->
+% exit({error,{asn1,{illegal_single_value_constraint}}});
+% sv_effective_constraint(SV,[SV|Rest]) ->
+% sv_effective_constraint(SV,Rest);
+% sv_effective_constraint(Int,[SV|Rest]) when integer(Int),list(SV) ->
+% case lists:member(Int,SV) of
+% true ->
+% sv_effective_constraint(Int,Rest);
+% _ ->
+% exit({error,{asn1,{illegal_single_value_constraint}}})
+% end;
+% sv_effective_constraint(SV,[Int|Rest]) when integer(Int),list(SV) ->
+% case lists:member(Int,SV) of
+% true ->
+% sv_effective_constraint(Int,Rest);
+% _ ->
+% exit({error,{asn1,{illegal_single_value_constraint}}})
+% end;
+% sv_effective_constraint(SV1,[SV2|Rest]) when list(SV1),list(SV2) ->
+% sv_effective_constraint(common_set(SV1,SV2),Rest);
+% sv_effective_constraint(_,_) ->
+% exit({error,{asn1,{illegal_single_value_constraint}}}).
+
+%% common_set/2
+%% Two lists as input
+%% Returns the list with all elements that are common for both
+%% input lists
+% common_set(SV1,SV2) ->
+% lists:filter(fun(X)->lists:member(X,SV1) end,SV2).
+
+
+
+pre_encode(integer,[]) ->
+ [];
+pre_encode(integer,C=[{'SingleValue',_}]) ->
+ C;
+pre_encode(integer,C=[{'ValueRange',VR={Lb,Ub}}]) when integer(Lb),integer(Ub)->
+ Range = Ub-Lb+1,
+ if
+ Range =< 255 ->
+ NoBits = no_bits(Range),
+ [{'ValueRange',VR,Range,{bits,NoBits}}];
+ Range =< 256 ->
+ [{'ValueRange',VR,Range,{octets,1}}];
+ Range =< 65536 ->
+ [{'ValueRange',VR,Range,{octets,2}}];
+ true ->
+ C
+ end;
+pre_encode(integer,C) ->
+ C.
+
+no_bits(2) -> 1;
+no_bits(N) when N=<4 -> 2;
+no_bits(N) when N=<8 -> 3;
+no_bits(N) when N=<16 -> 4;
+no_bits(N) when N=<32 -> 5;
+no_bits(N) when N=<64 -> 6;
+no_bits(N) when N=<128 -> 7;
+no_bits(N) when N=<255 -> 8.
+
+%% Object code generating for encoding and decoding
+%% ------------------------------------------------
+
+gen_obj_code(Erules,_Module,Obj) when record(Obj,typedef) ->
+ ObjName = Obj#typedef.name,
+ Def = Obj#typedef.typespec,
+ #'Externaltypereference'{module=Mod,type=ClassName} =
+ Def#'Object'.classname,
+ Class = asn1_db:dbget(Mod,ClassName),
+ {object,_,Fields} = Def#'Object'.def,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjName}),
+ emit({nl,"%%================================",nl}),
+ EncConstructed =
+% gen_encode_objectfields(Class#classdef.typespec,ObjName,Fields,[]),
+ gen_encode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_encode_constr_type(Erules,EncConstructed),
+ emit(nl),
+ DecConstructed =
+% gen_decode_objectfields(Class#classdef.typespec,ObjName,Fields,[]),
+ gen_decode_objectfields(ClassName,get_class_fields(Class),
+ ObjName,Fields,[]),
+ emit(nl),
+ gen_decode_constr_type(Erules,DecConstructed),
+ emit(nl);
+gen_obj_code(_Erules,_Module,Obj) when record(Obj,pobjectdef) ->
+ ok.
+
+gen_encode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(V) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",V,",_RestPrimFieldName) ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val, RestPrimFieldName) ->",nl]),
+ MaybeConstr =
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit(" <<>>"),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Val"),
+ gen_encode_default_call(ClassName,Name,DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Val"),
+ gen_encode_field_call(ObjName,Name,TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,
+ MaybeConstr++ConstrAcc);
+gen_encode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'enc_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'enc_",ObjName,"'(",{asis,Name},
+% ", Val,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_"),
+ emit([" exit({error,{'use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Val,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+ "'(H, Val, T)"});
+ TypeName ->
+ emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_encode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_encode_objectfields(ClassName,[_C|Cs],O,OF,Acc) ->
+ gen_encode_objectfields(ClassName,Cs,O,OF,Acc);
+gen_encode_objectfields(_,[],_,_,Acc) ->
+ Acc.
+
+% gen_encode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, Dummy) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_encode_prim(per,Def,"false","Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'enc_",ObjName,'_',FieldName,
+% "'(Val)"}),
+% [{['enc_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'enc_",TypeName,"'(Val)"}),
+% [];
+% TypeName ->
+% emit({" 'enc_",TypeName,"'(Val)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'enc_",ObjName,"'(",{asis,FieldName},
+% ", Val, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'enc_",TypeName,
+% "'(H, Val, T)"});
+% TypeName ->
+% emit({indent(3),"'enc_",TypeName,"'(H, Val, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} -> []
+% end,
+% gen_encode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_encode_objectfields(C,O,[_|T],Acc) ->
+% gen_encode_objectfields(C,O,T,Acc);
+% gen_encode_objectfields(_,_,[],Acc) ->
+% Acc.
+
+gen_encode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(enc,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ Name = lists:concat(["enc_",TypeDef#typedef.name]),
+ emit({Name,"(Val) ->",nl}),
+ Def = TypeDef#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_encode_constructed(Erules,Name,InnerType,Def),
+ gen_encode_constr_type(Erules,Rest)
+ end;
+gen_encode_constr_type(_,[]) ->
+ ok.
+
+gen_encode_field_call(ObjName,FieldName,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_encode_prim(per,Def,"false",
+ "Val"),
+ [];
+ {constructed,bif} ->
+ emit({" 'enc_",ObjName,'_',FieldName,
+ "'(Val)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'enc_",TypeName,
+ "'(Val)"}),
+ [];
+ TypeName ->
+ emit({" 'enc_",TypeName,"'(Val)"}),
+ []
+ end.
+
+gen_encode_default_call(ClassName,FieldName,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+%% asn1ct_gen:gen_encode_constructed(Erules,Typename,InnerType,Type);
+ emit([" 'enc_",ClassName,'_',FieldName,"'(Val)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_encode_prim(per,Type,"false","Val"),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'enc_",Etype,"'(Val)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'enc_",Etype,"'(Val)",nl]),
+ []
+ end.
+
+
+
+gen_decode_objectfields(ClassName,[{typefield,Name,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Bytes) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},",",Bytes,
+ ",_,_RestPrimFieldName) ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes, _, RestPrimFieldName) ->",nl]),
+ MaybeConstr=
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} -> %% this case is illegal
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_"),
+ emit([" asn1_NOVALUE"]),
+ [];
+ {false,{'DEFAULT',DefaultType}} ->
+ EmitFuncClause("Bytes"),
+ gen_decode_default_call(ClassName,Name,"Bytes",DefaultType);
+ {{Name,TypeSpec},_} ->
+ %% A specified field owerwrites any 'DEFAULT' or
+ %% 'OPTIONAL' field in the class
+ EmitFuncClause("Bytes"),
+ gen_decode_field_call(ObjName,Name,"Bytes",TypeSpec)
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,MaybeConstr++ConstrAcc);
+gen_decode_objectfields(ClassName,[{objectfield,Name,_,_,OptOrMand}|Rest],
+ ObjName,ObjectFields,ConstrAcc) ->
+ EmitFuncClause =
+ fun(Attrs) ->
+ emit(["'dec_",ObjName,"'(",{asis,Name},
+ ",",Attrs,") ->",nl])
+ end,
+% emit(["'dec_",ObjName,"'(",{asis,Name},
+% ", Bytes,_,[H|T]) ->",nl]),
+ case {get_object_field(Name,ObjectFields),OptOrMand} of
+ {false,'MANDATORY'} ->
+ exit({error,{asn1,{"missing mandatory field in object",
+ ObjName}}});
+ {false,'OPTIONAL'} ->
+ EmitFuncClause("_,_,_"),
+ emit([" exit({error,{'illegal use of missing field in object', ",Name,
+ "}})"]);
+ {false,{'DEFAULT',_DefaultObject}} ->
+ exit({error,{asn1,{"not implemented yet",Name}}});
+ {{Name,TypeSpec},_} ->
+ EmitFuncClause("Bytes,_,[H|T]"),
+ case TypeSpec#typedef.name of
+ {ExtMod,TypeName} ->
+ emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+ "'(H, Bytes, telltype, T)"});
+ TypeName ->
+ emit({indent(3),"'dec_",TypeName,"'(H, Bytes, telltype, T)"})
+ end
+ end,
+ case more_genfields(Rest) of
+ true ->
+ emit([";",nl]);
+ false ->
+ emit([".",nl])
+ end,
+ gen_decode_objectfields(ClassName,Rest,ObjName,ObjectFields,ConstrAcc);
+gen_decode_objectfields(CN,[_C|Cs],O,OF,CAcc) ->
+ gen_decode_objectfields(CN,Cs,O,OF,CAcc);
+gen_decode_objectfields(_,[],_,_,CAcc) ->
+ CAcc.
+
+
+gen_decode_field_call(ObjName,FieldName,Bytes,Type) ->
+ Def = Type#typedef.typespec,
+ case Type#typedef.name of
+ {primitive,bif} ->
+ gen_dec_prim(per,Def,Bytes),
+ [];
+ {constructed,bif} ->
+ emit({" 'dec_",ObjName,'_',FieldName,
+ "'(",Bytes,",telltype)"}),
+ [Type#typedef{name=list_to_atom(lists:concat([ObjName,'_',FieldName]))}];
+ {ExtMod,TypeName} ->
+ emit({" '",ExtMod,"':'dec_",TypeName,
+ "'(",Bytes,", telltype)"}),
+ [];
+ TypeName ->
+ emit({" 'dec_",TypeName,"'(",Bytes,", telltype)"}),
+ []
+ end.
+
+gen_decode_default_call(ClassName,FieldName,Bytes,Type) ->
+ CurrentMod = get(currmod),
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ emit([" 'dec_",ClassName,'_',FieldName,"'(",Bytes,", telltype)"]),
+ [#typedef{name=list_to_atom(lists:concat([ClassName,'_',FieldName])),
+ typespec=Type}];
+ {primitive,bif} ->
+ gen_dec_prim(per,Type,Bytes),
+ [];
+ #'Externaltypereference'{module=CurrentMod,type=Etype} ->
+ emit([" 'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ [];
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit([" '",Emod,"':'dec_",Etype,"'(",Bytes,", telltype)",nl]),
+ []
+ end.
+
+%%%%%%%%%%%%%%%
+
+% gen_decode_objectfields(Class,ObjName,[{FieldName,Type}|Rest],ConstrAcc) ->
+% Fields = Class#objectclass.fields,
+
+% MaybeConstr =
+% case is_typefield(Fields,FieldName) of
+% true ->
+% Def = Type#typedef.typespec,
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, RestPrimFieldName) ->",nl}),
+
+% CAcc =
+% case Type#typedef.name of
+% {primitive,bif} ->
+% gen_dec_prim(per,Def,"Val"),
+% [];
+% {constructed,bif} ->
+% emit({" 'dec_",ObjName,'_',FieldName,
+% "'(Val, Telltype)"}),
+% [{['dec_',ObjName,'_',FieldName],Def}];
+% {ExtMod,TypeName} ->
+% emit({" '",ExtMod,"':'dec_",TypeName,
+% "'(Val, Telltype)"}),
+% [];
+% TypeName ->
+% emit({" 'dec_",TypeName,"'(Val, Telltype)"}),
+% []
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% CAcc;
+% {false,objectfield} ->
+% emit({"'dec_",ObjName,"'(",{asis,FieldName},
+% ", Val, Telltype, [H|T]) ->",nl}),
+% case Type#typedef.name of
+% {ExtMod,TypeName} ->
+% emit({indent(3),"'",ExtMod,"':'dec_",TypeName,
+% "'(H, Val, Telltype, T)"});
+% TypeName ->
+% emit({indent(3),"'dec_",TypeName,
+% "'(H, Val, Telltype, T)"})
+% end,
+% case more_genfields(Fields,Rest) of
+% true ->
+% emit({";",nl});
+% false ->
+% emit({".",nl})
+% end,
+% [];
+% {false,_} ->
+% []
+% end,
+% gen_decode_objectfields(Class,ObjName,Rest,MaybeConstr ++ ConstrAcc);
+% gen_decode_objectfields(C,O,[_|T],CAcc) ->
+% gen_decode_objectfields(C,O,T,CAcc);
+% gen_decode_objectfields(_,_,[],CAcc) ->
+% CAcc.
+
+gen_decode_constr_type(Erules,[{Name,Def}|Rest]) ->
+ emit({Name,"(Bytes,_) ->",nl}),
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ asn1ct_gen:gen_decode_constructed(Erules,Name,InnerType,Def),
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(Erules,[TypeDef|Rest]) when record(TypeDef,typedef) ->
+ case is_already_generated(dec,TypeDef#typedef.name) of
+ true -> ok;
+ _ ->
+ gen_decode(Erules,TypeDef)
+ end,
+ gen_decode_constr_type(Erules,Rest);
+gen_decode_constr_type(_,[]) ->
+ ok.
+
+% is_typefield(Fields,FieldName) ->
+% case lists:keysearch(FieldName,2,Fields) of
+% {value,Field} ->
+% case element(1,Field) of
+% typefield ->
+% true;
+% Other ->
+% {false,Other}
+% end;
+% _ ->
+% false
+% end.
+%% Object Set code generating for encoding and decoding
+%% ----------------------------------------------------
+gen_objectset_code(Erules,ObjSet) ->
+ ObjSetName = ObjSet#typedef.name,
+ Def = ObjSet#typedef.typespec,
+%% {ClassName,ClassDef} = Def#'ObjectSet'.class,
+ #'Externaltypereference'{module=ClassModule,
+ type=ClassName} = Def#'ObjectSet'.class,
+ ClassDef = asn1_db:dbget(ClassModule,ClassName),
+ UniqueFName = Def#'ObjectSet'.uniquefname,
+ Set = Def#'ObjectSet'.set,
+ emit({nl,nl,nl,"%%================================"}),
+ emit({nl,"%% ",ObjSetName}),
+ emit({nl,"%%================================",nl}),
+ case ClassName of
+ {_Module,ExtClassName} ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ExtClassName,ClassDef);
+ _ ->
+ gen_objset_code(Erules,ObjSetName,UniqueFName,Set,
+ ClassName,ClassDef)
+ end,
+ emit(nl).
+
+gen_objset_code(Erules,ObjSetName,UniqueFName,Set,ClassName,ClassDef)->
+ ClassFields = (ClassDef#classdef.typespec)#objectclass.fields,
+ InternalFuncs=
+ gen_objset_enc(ObjSetName,UniqueFName,Set,ClassName,
+ ClassFields,1,[]),
+ gen_objset_dec(ObjSetName,UniqueFName,Set,ClassName,ClassFields,1),
+ gen_internal_funcs(Erules,InternalFuncs).
+
+gen_objset_enc(_,{unique,undefined},_,_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ [];
+gen_objset_enc(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],
+ ClName,ClFields,NthObj,Acc)->
+ emit({"'getenc_",ObjSName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,NewNthObj}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({";",nl}),
+ gen_objset_enc(ObjSName,UniqueName,[T|Rest],ClName,ClFields,
+ NewNthObj,InternalFunc++Acc);
+gen_objset_enc(ObjSetName,UniqueName,
+ [{ObjName,Val,Fields}],_ClName,ClFields,NthObj,Acc) ->
+
+ emit({"'getenc_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ {InternalFunc,_}=
+ case ObjName of
+ no_name ->
+ gen_inlined_enc_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit({" fun 'enc_",ObjName,"'/3"}),
+ {[],NthObj}
+ end,
+ emit({".",nl,nl}),
+ InternalFunc++Acc;
+gen_objset_enc(ObjSetName,_UniqueName,['EXTENSIONMARK'],_ClName,
+ _ClFields,_NthObj,Acc) ->
+ emit({"'getenc_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(_, Val, _) ->",nl}),
+ emit({indent(6),"Size = if",nl}),
+ emit({indent(9),"list(Val) -> length(Val);",nl}),
+ emit({indent(9),"true -> size(Val)",nl}),
+ emit({indent(6),"end,",nl}),
+ emit({indent(6),"if",nl}),
+ emit({indent(9),"Size < 256 ->",nl}),
+ emit({indent(12),"[20,Size,Val];",nl}),
+ emit({indent(9),"true ->",nl}),
+ emit({indent(12),"[21,<<Size:16>>,Val]",nl}),
+ emit({indent(6),"end",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ Acc;
+gen_objset_enc(_,_,[],_,_,_,Acc) ->
+ Acc.
+
+%% gen_inlined_enc_funs for each object iterates over all fields of a
+%% class, and for each typefield it checks if the object has that
+%% field and emits the proper code.
+gen_inlined_enc_funs(Fields,[{typefield,Name,_}|Rest],ObjSetName,NthObj) ->
+ InternalDefFunName=asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+N,Ret);
+ false ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_enc_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_enc_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_enc_funs(_,[],_,NthObj) ->
+ {[],NthObj}.
+
+gen_inlined_enc_funs1(Fields,[{typefield,Name,_}|Rest],ObjSetName,
+ NthObj,Acc) ->
+ InternalDefFunName = asn1ct_gen:list2name([NthObj,Name,ObjSetName]),
+ {Acc2,NAdd}=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ {Ret,N}=emit_inner_of_fun(Type,InternalDefFunName),
+ {Ret++Acc,N};
+ false ->
+ {Acc,0}
+ end,
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj+NAdd,Acc2);
+gen_inlined_enc_funs1(Fields,[_|Rest],ObjSetName,NthObj,Acc)->
+ gen_inlined_enc_funs1(Fields,Rest,ObjSetName,NthObj,Acc);
+gen_inlined_enc_funs1(_,[],_,NthObj,Acc) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ {Acc,NthObj}.
+
+emit_inner_of_fun(TDef=#typedef{name={ExtMod,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtMod,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_encode_prim(per,Type,dotag,"Val"),
+ {[],0};
+ {constructed,bif} ->
+ emit([indent(12),"'enc_",
+ InternalDefFunName,"'(Val)"]),
+ {[TDef#typedef{name=InternalDefFunName}],1};
+ _ ->
+ emit({indent(12),"'",ExtMod,"':'enc_",Name,"'(Val)"}),
+ {[],0}
+ end;
+emit_inner_of_fun(#typedef{name=Name},_) ->
+ emit({indent(12),"'enc_",Name,"'(Val)"}),
+ {[],0};
+emit_inner_of_fun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_encode_prim(erules,Type,dotag,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'enc_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'enc_",
+ T,"'(Val)"})
+ end,
+ {[],0}.
+
+indent(N) ->
+ lists:duplicate(N,32). % 32 = space
+
+
+gen_objset_dec(_,{unique,undefined},_,_,_,_) ->
+ %% There is no unique field in the class of this object set
+ %% don't bother about the constraint
+ ok;
+gen_objset_dec(ObjSName,UniqueName,[{ObjName,Val,Fields},T|Rest],ClName,
+ ClFields,NthObj)->
+
+ emit({"'getdec_",ObjSName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ NewNthObj=
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSName,NthObj);
+ _ ->
+ emit({" fun 'dec_",ObjName,"'/4"}),
+ NthObj
+ end,
+ emit({";",nl}),
+ gen_objset_dec(ObjSName,UniqueName,[T|Rest],ClName,ClFields,NewNthObj);
+gen_objset_dec(ObjSetName,UniqueName,[{ObjName,Val,Fields}],_ClName,
+ ClFields,NthObj) ->
+
+ emit({"'getdec_",ObjSetName,"'(",{asis,UniqueName},",",
+ {asis,Val},") ->",nl}),
+ case ObjName of
+ no_name ->
+ gen_inlined_dec_funs(Fields,ClFields,ObjSetName,NthObj);
+ _ ->
+ emit({" fun 'dec_",ObjName,"'/4"})
+ end,
+ emit({".",nl,nl}),
+ ok;
+gen_objset_dec(ObjSetName,_,['EXTENSIONMARK'],_ClName,_ClFields,
+ _NthObj) ->
+ emit({"'getdec_",ObjSetName,"'(_, _) ->",nl}),
+ emit({indent(3),"fun(Attr1, Bytes, _, _) ->",nl}),
+ %% emit({indent(6),"?RT_PER:decode_open_type(Bytes,[])",nl}),
+ emit({indent(6),"{Bytes,Attr1}",nl}),
+ emit({indent(3),"end.",nl,nl}),
+ ok;
+gen_objset_dec(_,_,[],_,_,_) ->
+ ok.
+
+gen_inlined_dec_funs(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({indent(3),"fun(Type, Val, _, _) ->",nl,
+ indent(6),"case Type of",nl}),
+ emit({indent(9),{asis,Name}," ->",nl}),
+ N=emit_inner_of_decfun(Type,InternalDefFunName),
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+ false ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj)
+ end;
+gen_inlined_dec_funs(Fields,[_|Rest],ObjSetName,NthObj) ->
+ gen_inlined_dec_funs(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs(_,[],_,NthObj) ->
+ NthObj.
+
+gen_inlined_dec_funs1(Fields,[{typefield,Name,_}|Rest],
+ ObjSetName,NthObj) ->
+ InternalDefFunName = [NthObj,Name,ObjSetName],
+ N=
+ case lists:keysearch(Name,1,Fields) of
+ {value,{_,Type}} when record(Type,type) ->
+ emit({";",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ {value,{_,Type}} when record(Type,typedef) ->
+ emit({";",nl,indent(9),{asis,Name}," ->",nl}),
+ emit_inner_of_decfun(Type,InternalDefFunName);
+ false ->
+ 0
+ end,
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj+N);
+gen_inlined_dec_funs1(Fields,[_|Rest],ObjSetName,NthObj)->
+ gen_inlined_dec_funs1(Fields,Rest,ObjSetName,NthObj);
+gen_inlined_dec_funs1(_,[],_,NthObj) ->
+ emit({nl,indent(6),"end",nl}),
+ emit({indent(3),"end"}),
+ NthObj.
+
+emit_inner_of_decfun(#typedef{name={ExtName,Name},typespec=Type},
+ InternalDefFunName) ->
+ case {ExtName,Name} of
+ {primitive,bif} ->
+ emit(indent(12)),
+ gen_dec_prim(per,Type,"Val"),
+ 0;
+ {constructed,bif} ->
+ emit({indent(12),"'dec_",
+ asn1ct_gen:list2name(InternalDefFunName),"'(Val)"}),
+ 1;
+ _ ->
+ emit({indent(12),"'",ExtName,"':'dec_",Name,
+ "'(Val, telltype)"}),
+ 0
+ end;
+emit_inner_of_decfun(#typedef{name=Name},_) ->
+ emit({indent(12),"'dec_",Name,"'(Val, telltype)"}),
+ 0;
+emit_inner_of_decfun(Type,_) when record(Type,type) ->
+ CurrMod = get(currmod),
+ case Type#type.def of
+ Def when atom(Def) ->
+ emit({indent(9),Def," ->",nl,indent(12)}),
+ gen_dec_prim(erules,Type,"Val");
+ TRef when record(TRef,typereference) ->
+ T = TRef#typereference.val,
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=CurrMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),"'dec_",T,"'(Val)"});
+ #'Externaltypereference'{module=ExtMod,type=T} ->
+ emit({indent(9),T," ->",nl,indent(12),ExtMod,":'dec_",
+ T,"'(Val)"})
+ end,
+ 0.
+
+
+gen_internal_funcs(_Erules,[]) ->
+ ok;
+gen_internal_funcs(Erules,[TypeDef|Rest]) ->
+ gen_encode_user(Erules,TypeDef),
+ emit([nl,nl,"'dec_",TypeDef#typedef.name,"'(Bytes) ->",nl]),
+ gen_decode_user(Erules,TypeDef),
+ gen_internal_funcs(Erules,Rest).
+
+
+
+%% DECODING *****************************
+%%***************************************
+
+
+gen_decode(Erules,Type) when record(Type,typedef) ->
+ D = Type,
+ emit({nl,nl}),
+ emit({"'dec_",Type#typedef.name,"'(Bytes,_) ->",nl}),
+ dbdec(Type#typedef.name),
+ gen_decode_user(Erules,D).
+
+gen_decode(Erules,Tname,#'ComponentType'{name=Cname,typespec=Type}) ->
+ NewTname = [Cname|Tname],
+ gen_decode(Erules,NewTname,Type);
+
+gen_decode(Erules,Typename,Type) when record(Type,type) ->
+ InnerType = asn1ct_gen:get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {constructed,bif} ->
+ ObjFun =
+ case Type#type.tablecinf of
+ [{objfun,_}|_R] ->
+ ", ObjFun";
+ _ ->
+ ""
+ end,
+ emit({nl,"'dec_",asn1ct_gen:list2name(Typename),
+ "'(Bytes,_",ObjFun,") ->",nl}),
+ dbdec(Typename),
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,Type);
+ _ ->
+ true
+ end.
+
+dbdec(Type) when list(Type)->
+ demit({"io:format(\"decoding: ",asn1ct_gen:list2name(Type),"~w~n\",[Bytes]),",nl});
+dbdec(Type) ->
+ demit({"io:format(\"decoding: ",{asis,Type},"~w~n\",[Bytes]),",nl}).
+
+gen_decode_user(Erules,D) when record(D,typedef) ->
+ CurrMod = get(currmod),
+ Typename = [D#typedef.name],
+ Def = D#typedef.typespec,
+ InnerType = asn1ct_gen:get_inner(Def#type.def),
+ case asn1ct_gen:type(InnerType) of
+ {primitive,bif} ->
+ gen_dec_prim(Erules,Def,"Bytes"),
+ emit({".",nl,nl});
+ 'ASN1_OPEN_TYPE' ->
+ gen_dec_prim(Erules,Def#type{def='ASN1_OPEN_TYPE'},"Bytes"),
+ emit({".",nl,nl});
+ {constructed,bif} ->
+ asn1ct_gen:gen_decode_constructed(Erules,Typename,InnerType,D);
+ #typereference{val=Dname} ->
+ emit({"'dec_",Dname,"'(Bytes,telltype)"}),
+ emit({".",nl,nl});
+ #'Externaltypereference'{module=CurrMod,type=Etype} ->
+ emit({"'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ emit({"'",Emod,"':'dec_",Etype,"'(Bytes,telltype).",nl,nl});
+ Other ->
+ exit({error,{asn1,{unknown,Other}}})
+ end.
+
+
+
+gen_dec_prim(_Erules,Att,BytesVar) ->
+ Typename = Att#type.def,
+ Constraint = Att#type.constraint,
+ case Typename of
+ 'INTEGER' ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ emit_dec_integer(EffectiveConstr,BytesVar);
+% emit({"?RT_PER:decode_integer(",BytesVar,",",
+% {asis,EffectiveConstr},")"});
+ {'INTEGER',NamedNumberList} ->
+ EffectiveConstr = effective_constraint(integer,Constraint),
+ emit_dec_integer(EffectiveConstr,BytesVar,NamedNumberList);
+% emit({"?RT_PER:decode_integer(",BytesVar,",",
+% {asis,EffectiveConstr},",",
+% {asis,NamedNumberList},")"});
+ {'BIT STRING',NamedNumberList} ->
+ case get(compact_bit_string) of
+ true ->
+ emit({"?RT_PER:decode_compact_bit_string(",
+ BytesVar,",",{asis,Constraint},",",
+ {asis,NamedNumberList},")"});
+ _ ->
+ emit({"?RT_PER:decode_bit_string(",BytesVar,",",
+ {asis,Constraint},",",
+ {asis,NamedNumberList},")"})
+ end;
+ 'NULL' ->
+ emit({"?RT_PER:decode_null(",
+ BytesVar,")"});
+ 'OBJECT IDENTIFIER' ->
+ emit({"?RT_PER:decode_object_identifier(",
+ BytesVar,")"});
+ 'ObjectDescriptor' ->
+ emit({"?RT_PER:decode_ObjectDescriptor(",
+ BytesVar,")"});
+ {'ENUMERATED',{NamedNumberList1,NamedNumberList2}} ->
+ NewTup = {list_to_tuple([X||{X,_} <- NamedNumberList1]),
+ list_to_tuple([X||{X,_} <- NamedNumberList2])},
+ NewC = [{'ValueRange',{0,size(element(1,NewTup))-1}}],
+ emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+ {asis,NewC},",",
+ {asis,NewTup},")"});
+ {'ENUMERATED',NamedNumberList} ->
+ %NewTup = list_to_tuple([X||{X,Y} <- NamedNumberList]),
+ NewNNL = [X||{X,_} <- NamedNumberList],
+ NewC = effective_constraint(integer,
+ [{'ValueRange',{0,length(NewNNL)-1}}]),
+ emit_dec_enumerated(BytesVar,NewC,NewNNL);
+% emit({"?RT_PER:decode_enumerated(",BytesVar,",",
+% {asis,NewC},",",
+% {asis,NewTup},")"});
+ 'BOOLEAN'->
+ emit({"?RT_PER:decode_boolean(",BytesVar,")"});
+ 'OCTET STRING' ->
+ emit_dec_octet_string(Constraint,BytesVar);
+% emit({"?RT_PER:decode_octet_string(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'NumericString' ->
+ emit_dec_known_multiplier_string('NumericString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_NumericString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'TeletexString' ->
+ emit({"?RT_PER:decode_TeletexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VideotexString' ->
+ emit({"?RT_PER:decode_VideotexString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'UTCTime' ->
+ emit_dec_known_multiplier_string('VisibleString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'GeneralizedTime' ->
+ emit_dec_known_multiplier_string('VisibleString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'GraphicString' ->
+ emit({"?RT_PER:decode_GraphicString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'VisibleString' ->
+ emit_dec_known_multiplier_string('VisibleString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_VisibleString(",BytesVar,",",
+% {asis,Constraint},")"});
+ 'GeneralString' ->
+ emit({"?RT_PER:decode_GeneralString(",BytesVar,",",
+ {asis,Constraint},")"});
+ 'PrintableString' ->
+ emit_dec_known_multiplier_string('PrintableString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_PrintableString(",BytesVar,",",{asis,Constraint},")"});
+ 'IA5String' ->
+ emit_dec_known_multiplier_string('IA5String',Constraint,BytesVar);
+% emit({"?RT_PER:decode_IA5String(",BytesVar,",",{asis,Constraint},")"});
+ 'BMPString' ->
+ emit_dec_known_multiplier_string('BMPString',Constraint,BytesVar);
+% emit({"?RT_PER:decode_BMPString(",BytesVar,",",{asis,Constraint},")"});
+ 'UniversalString' ->
+ emit_dec_known_multiplier_string('UniversalString',
+ Constraint,BytesVar);
+% emit({"?RT_PER:decode_UniversalString(",BytesVar,",",{asis,Constraint},")"});
+ 'ANY' ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",",
+ {asis,Constraint}, ")"]);
+ 'ASN1_OPEN_TYPE' ->
+ case Constraint of
+ [#'Externaltypereference'{type=Tname}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ [#type{def=#'Externaltypereference'{type=Tname}}] ->
+ emit(["fun(FBytes) ->",nl,
+ " {XTerm,XBytes} = "]),
+ emit(["?RT_PER:decode_open_type(FBytes,[]),",nl]),
+ emit([" {YTerm,_} = dec_",Tname,"(XTerm,mandatory),",nl]),
+ emit([" {YTerm,XBytes} end(",BytesVar,")"]);
+ _ ->
+ emit(["?RT_PER:decode_open_type(",BytesVar,",[])"])
+ end;
+ Other ->
+ exit({'cant decode' ,Other})
+ end.
+
+
+emit_dec_integer(C,BytesVar,NNL) ->
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(buffer),
+ Tmpterm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ Buffer = asn1ct_gen:mk_var(asn1ct_name:curr(buffer)),
+ emit({" begin {",{curr,tmpterm},",",{curr,buffer},"} = ",nl}),
+ emit_dec_integer(C,BytesVar),
+ emit({",",nl," case ",Tmpterm," of",nl}),
+ lists:map(fun({Name,Int})->emit({" ",Int," -> {",{asis,Name},",",
+ Buffer,"};",nl});
+ (_)-> exit({error,{asn1,{"error in named number list",NNL}}})
+ end,
+ NNL),
+ emit({" _ -> {",Tmpterm,",",Buffer,"}",nl}),
+ emit({" end",nl}), % end of case
+ emit(" end"). % end of begin
+
+emit_dec_integer([{'SingleValue',Int}],BytesVar) when integer(Int) ->
+ emit(["{",Int,",",BytesVar,"}"]);
+emit_dec_integer([{_,{Lb,_Ub},_Range,{BitsOrOctets,N}}],BytesVar) ->
+ GetBorO =
+ case BitsOrOctets of
+ bits -> "getbits";
+ _ -> "getoctets"
+ end,
+ asn1ct_name:new(tmpterm),
+ asn1ct_name:new(tmpremain),
+ emit({" begin",nl," {",{curr,tmpterm},",",{curr,tmpremain},"}=",
+ "?RT_PER:",GetBorO,"(",BytesVar,",",N,"),",nl}),
+ emit({" {",{curr,tmpterm},"+",Lb,",",{curr,tmpremain},"}",nl,
+ " end"});
+emit_dec_integer([{_,{'MIN',_}}],BytesVar) ->
+ emit({"?RT_PER:decode_unconstrained_number(",BytesVar,")"});
+emit_dec_integer([{_,{Lb,'MAX'}}],BytesVar) ->
+ emit({"?RT_PER:decode_semi_constrained_number(",BytesVar,",",Lb,")"});
+emit_dec_integer([{'ValueRange',VR={Lb,Ub}}],BytesVar) ->
+ Range = Ub-Lb+1,
+ emit({"?RT_PER:decode_constrained_number(",BytesVar,",",
+ {asis,VR},",",Range,")"});
+emit_dec_integer(C=[{Rc,_}],BytesVar) when tuple(Rc) ->
+ emit({"?RT_PER:decode_integer(",BytesVar,",",{asis,C},")"});
+emit_dec_integer(_,BytesVar) ->
+ emit({"?RT_PER:decode_unconstrained_number(",BytesVar,")"}).
+
+
+emit_dec_enumerated(BytesVar,C,NamedNumberList) ->
+ emit_dec_enumerated_begin(),% emits a begin if component
+ asn1ct_name:new(tmpterm),
+ Tmpterm = asn1ct_gen:mk_var(asn1ct_name:curr(tmpterm)),
+ asn1ct_name:new(tmpremain),
+ Tmpremain = asn1ct_gen:mk_var(asn1ct_name:curr(tmpremain)),
+ emit({" {",{curr,tmpterm},",",{curr,tmpremain},"} =",nl}),
+ emit_dec_integer(C,BytesVar),
+ emit({",",nl," case ",Tmpterm," of "}),
+% Cases=lists:flatten(dec_enumerated_cases(NamedNumberList,asn1ct_gen:mk_var(asn1ct_name:curr(tmpremain)),0)),
+ Cases=lists:flatten(dec_enumerated_cases(NamedNumberList,Tmpremain,0)),
+ emit({Cases++"_->exit({error,{asn1,{decode_enumerated,{",Tmpterm,
+ ",",{asis,NamedNumberList},"}}}}) end",nl}),
+ emit_dec_enumerated_end().
+
+emit_dec_enumerated_begin() ->
+ case get(component_type) of
+ {true,_} ->
+ emit({" begin",nl});
+ _ -> ok
+ end.
+
+emit_dec_enumerated_end() ->
+ case get(component_type) of
+ {true,_} ->
+ emit(" end");
+ _ -> ok
+ end.
+
+% dec_enumerated_cases(NNL,Tmpremain,No) ->
+% Cases=dec_enumerated_cases1(NNL,Tmpremain,0),
+% lists:flatten(io_lib:format("(case ~s "++Cases++
+% "~s when atom(~s)->exit({error,{asn1,{namednumber,~s}}});_->~s end)",[Value,"TmpVal","TmpVal","TmpVal",Value])).
+
+dec_enumerated_cases([Name|Rest],Tmpremain,No) ->
+ io_lib:format("~w->{~w,~s};",[No,Name,Tmpremain])++
+ dec_enumerated_cases(Rest,Tmpremain,No+1);
+dec_enumerated_cases([],_,_) ->
+ "".
+
+
+% more_genfields(_Fields,[]) ->
+% false;
+% more_genfields(Fields,[{FieldName,_}|T]) ->
+% case is_typefield(Fields,FieldName) of
+% true -> true;
+% {false,objectfield} -> true;
+% {false,_} -> more_genfields(Fields,T)
+% end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_name.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_name.erl
new file mode 100644
index 0000000000..03252bd7d9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_name.erl
@@ -0,0 +1,225 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_name.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_name).
+
+%%-compile(export_all).
+-export([name_server_loop/1,
+ start/0,
+ stop/0,
+ push/1,
+ pop/1,
+ curr/1,
+ clear/0,
+ delete/1,
+ active/1,
+ prev/1,
+ next/1,
+ all/1,
+ new/1]).
+
+start() ->
+ start_server(asn1_ns, asn1ct_name,name_server_loop,[[]]).
+
+stop() -> stop_server(asn1_ns).
+
+name_server_loop(Vars) ->
+%% io:format("name -- ~w~n",[Vars]),
+ receive
+ {From,{current,Variable}} ->
+ From ! {asn1_ns,get_curr(Vars,Variable)},
+ name_server_loop(Vars);
+ {From,{pop,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(pop_var(Vars,Variable));
+ {From,{push,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(push_var(Vars,Variable));
+ {From,{delete,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(delete_var(Vars,Variable));
+ {From,{new,Variable}} ->
+ From ! {asn1_ns,done},
+ name_server_loop(new_var(Vars,Variable));
+ {From,{prev,Variable}} ->
+ From ! {asn1_ns,get_prev(Vars,Variable)},
+ name_server_loop(Vars);
+ {From,{next,Variable}} ->
+ From ! {asn1_ns,get_next(Vars,Variable)},
+ name_server_loop(Vars);
+ {From,stop} ->
+ From ! {asn1_ns,stopped},
+ exit(normal)
+ end.
+
+active(V) ->
+ case curr(V) of
+ nil -> false;
+ _ -> true
+ end.
+
+req(Req) ->
+ asn1_ns ! {self(), Req},
+ receive {asn1_ns, Reply} -> Reply end.
+
+pop(V) -> req({pop,V}).
+push(V) -> req({push,V}).
+clear() -> req(stop), start().
+curr(V) -> req({current,V}).
+new(V) -> req({new,V}).
+delete(V) -> req({delete,V}).
+prev(V) ->
+ case req({prev,V}) of
+ none ->
+ exit('cant get prev of none');
+ Rep -> Rep
+ end.
+
+next(V) ->
+ case req({next,V}) of
+ none ->
+ exit('cant get next of none');
+ Rep -> Rep
+ end.
+
+all(V) ->
+ Curr = curr(V),
+ if Curr == V -> [];
+ true ->
+ lists:reverse(generate(V,last(Curr),[],0))
+ end.
+
+generate(V,Number,Res,Pos) ->
+ Ell = Pos+1,
+ if
+ Ell > Number ->
+ Res;
+ true ->
+ generate(V,Number,[list_to_atom(lists:concat([V,Ell]))|Res],Ell)
+ end.
+
+last(V) ->
+ last2(lists:reverse(atom_to_list(V))).
+
+last2(RevL) ->
+ list_to_integer(lists:reverse(get_digs(RevL))).
+
+
+get_digs([H|T]) ->
+ if
+ H < $9+1,
+ H > $0-1 ->
+ [H|get_digs(T)];
+ true ->
+ []
+ end.
+
+push_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ [{Variable,[0]}|Vars];
+ {value,{Variable,[Digit|Drest]}} ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,[Digit,Digit|Drest]}|NewVars]
+ end.
+
+pop_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ ok;
+ {value,{Variable,[_Dig]}} ->
+ lists:keydelete(Variable,1,Vars);
+ {value,{Variable,[_Dig|Digits]}} ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,Digits}|NewVars]
+ end.
+
+get_curr([],Variable) ->
+ Variable;
+get_curr([{Variable,[0|_Drest]}|_Tail],Variable) ->
+ Variable;
+get_curr([{Variable,[Digit|_Drest]}|_Tail],Variable) ->
+ list_to_atom(lists:concat([Variable,integer_to_list(Digit)]));
+
+get_curr([_|Tail],Variable) ->
+ get_curr(Tail,Variable).
+
+new_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ [{Variable,[1]}|Vars];
+ {value,{Variable,[Digit|Drest]}} ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,[Digit+1|Drest]}|NewVars]
+ end.
+
+delete_var(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ Vars;
+ {value,{Variable,[N]}} when N =< 1 ->
+ lists:keydelete(Variable,1,Vars);
+ {value,{Variable,[Digit|Drest]}} ->
+ case Digit of
+ 0 ->
+ Vars;
+ _ ->
+ NewVars = lists:keydelete(Variable,1,Vars),
+ [{Variable,[Digit-1|Drest]}|NewVars]
+ end
+ end.
+
+get_prev(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ none;
+ {value,{Variable,[Digit|_]}} when Digit =< 1 ->
+ Variable;
+ {value,{Variable,[Digit|_]}} when Digit > 1 ->
+ list_to_atom(lists:concat([Variable,
+ integer_to_list(Digit-1)]));
+ _ ->
+ none
+ end.
+
+get_next(Vars,Variable) ->
+ case lists:keysearch(Variable,1,Vars) of
+ false ->
+ list_to_atom(lists:concat([Variable,"1"]));
+ {value,{Variable,[Digit|_]}} when Digit >= 0 ->
+ list_to_atom(lists:concat([Variable,
+ integer_to_list(Digit+1)]));
+ _ ->
+ none
+ end.
+
+
+stop_server(Name) ->
+ stop_server(Name, whereis(Name)).
+stop_server(_Name, undefined) -> stopped;
+stop_server(Name, _Pid) ->
+ Name ! {self(), stop},
+ receive {Name, _} -> stopped end.
+
+
+start_server(Name,Mod,Fun,Args) ->
+ case whereis(Name) of
+ undefined ->
+ register(Name, spawn(Mod,Fun, Args));
+ _Pid ->
+ already_started
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser.yrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser.yrl
new file mode 100644
index 0000000000..df74685cb7
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser.yrl
@@ -0,0 +1,1175 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_parser.yrl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+Nonterminals
+ModuleDefinition ModuleIdentifier DefinitiveIdentifier DefinitiveObjIdComponentList
+DefinitiveObjIdComponent TagDefault ExtensionDefault
+ModuleBody Exports SymbolsExported Imports SymbolsImported
+SymbolsFromModuleList SymbolsFromModule GlobalModuleReference AssignedIdentifier SymbolList
+Symbol Reference AssignmentList Assignment
+ExtensionAndException
+ComponentTypeLists
+Externaltypereference Externalvaluereference DefinedType DefinedValue
+AbsoluteReference ItemSpec ItemId ComponentId TypeAssignment
+ValueAssignment
+% ValueSetTypeAssignment
+ValueSet
+Type BuiltinType NamedType ReferencedType
+Value ValueNotNull BuiltinValue ReferencedValue NamedValue
+% BooleanType
+BooleanValue IntegerType NamedNumberList NamedNumber SignedNumber
+% inlined IntegerValue
+EnumeratedType
+% inlined Enumerations
+Enumeration EnumerationItem
+% inlined EnumeratedValue
+% RealType
+RealValue NumericRealValue SpecialRealValue BitStringType
+% inlined BitStringValue
+IdentifierList
+% OctetStringType
+% inlined OctetStringValue
+% NullType NullValue
+SequenceType ComponentTypeList ComponentType
+% SequenceValue SequenceOfValue
+ComponentValueList SequenceOfType
+SAndSOfValue ValueList SetType
+% SetValue SetOfValue
+SetOfType
+ChoiceType
+% AlternativeTypeList made common with ComponentTypeList
+ChoiceValue
+AnyValue
+AnyDefBy
+SelectionType
+TaggedType Tag ClassNumber Class
+% redundant TaggedValue
+% EmbeddedPDVType EmbeddedPDVValue ExternalType ExternalValue ObjectIdentifierType
+ObjectIdentifierValue ObjIdComponentList ObjIdComponent
+% NameForm NumberForm NameAndNumberForm
+CharacterStringType
+RestrictedCharacterStringValue CharacterStringList
+% CharSyms CharsDefn
+Quadruple
+% Group Plane Row Cell
+Tuple
+% TableColumn TableRow
+% UnrestrictedCharacterString
+CharacterStringValue
+% UnrestrictedCharacterStringValue
+ConstrainedType Constraint ConstraintSpec TypeWithConstraint
+ElementSetSpecs ElementSetSpec
+%GeneralConstraint
+UserDefinedConstraint UserDefinedConstraintParameter
+UserDefinedConstraintParameters
+ExceptionSpec
+ExceptionIdentification
+Unions
+UnionMark
+UElems
+Intersections
+IntersectionElements
+IntersectionMark
+IElems
+Elements
+Elems
+SubTypeElements
+Exclusions
+LowerEndpoint
+UpperEndpoint
+LowerEndValue
+UpperEndValue
+TypeConstraints NamedConstraint PresenceConstraint
+
+ParameterizedTypeAssignment
+ParameterList
+Parameters
+Parameter
+ParameterizedType
+
+% X.681
+ObjectClassAssignment ObjectClass ObjectClassDefn
+FieldSpecs FieldSpec OptionalitySpec WithSyntaxSpec
+TokenOrGroupSpecs TokenOrGroupSpec
+SyntaxList OptionalGroup RequiredToken Word
+TypeOptionalitySpec
+ValueOrObjectOptSpec
+VSetOrOSetOptSpec
+ValueOptionalitySpec
+ObjectOptionalitySpec
+ValueSetOptionalitySpec
+ObjectSetOptionalitySpec
+% X.681 chapter 15
+InformationFromObjects
+ValueFromObject
+%ValueSetFromObjects
+TypeFromObject
+%ObjectFromObject
+%ObjectSetFromObjects
+ReferencedObjects
+FieldName
+PrimitiveFieldName
+
+ObjectAssignment
+ObjectSetAssignment
+ObjectSet
+ObjectSetElements
+Object
+ObjectDefn
+DefaultSyntax
+DefinedSyntax
+FieldSettings
+FieldSetting
+DefinedSyntaxTokens
+DefinedSyntaxToken
+Setting
+DefinedObject
+ObjectFromObject
+ObjectSetFromObjects
+ParameterizedObject
+ExternalObjectReference
+DefinedObjectSet
+DefinedObjectClass
+ExternalObjectClassReference
+
+% X.682
+TableConstraint
+ComponentRelationConstraint
+ComponentIdList
+
+% X.683
+ActualParameter
+.
+
+%UsefulType.
+
+Terminals
+'ABSENT' 'ABSTRACT-SYNTAX' 'ALL' 'ANY'
+'APPLICATION' 'AUTOMATIC' 'BEGIN' 'BIT'
+'BOOLEAN' 'BY' 'CHARACTER' 'CHOICE' 'CLASS' 'COMPONENT'
+'COMPONENTS' 'CONSTRAINED' 'DEFAULT' 'DEFINED' 'DEFINITIONS'
+'EMBEDDED' 'END' 'ENUMERATED' 'EXCEPT' 'EXPLICIT'
+'EXPORTS' 'EXTENSIBILITY' 'EXTERNAL' 'FALSE' 'FROM' 'GeneralizedTime'
+'TYPE-IDENTIFIER'
+'IDENTIFIER' 'IMPLICIT' 'IMPLIED' 'IMPORTS'
+'INCLUDES' 'INSTANCE' 'INTEGER' 'INTERSECTION'
+'MAX' 'MIN' 'MINUS-INFINITY' 'NULL'
+'OBJECT' 'ObjectDescriptor' 'OCTET' 'OF' 'OPTIONAL' 'PDV' 'PLUS-INFINITY'
+'PRESENT' 'PRIVATE' 'REAL' 'SEQUENCE' 'SET' 'SIZE'
+'STRING' 'SYNTAX' 'TAGS' 'TRUE' 'UNION'
+'UNIQUE' 'UNIVERSAL' 'UTCTime' 'WITH'
+'{' '}' '(' ')' '.' '::=' ';' ',' '@' '*' '-' '[' ']'
+'!' '..' '...' '|' '<' ':' '^'
+number identifier typereference restrictedcharacterstringtype
+bstring hstring cstring typefieldreference valuefieldreference
+objectclassreference word.
+
+Rootsymbol ModuleDefinition.
+Endsymbol '$end'.
+
+Left 300 'EXCEPT'.
+Left 200 '^'.
+Left 200 'INTERSECTION'.
+Left 100 '|'.
+Left 100 'UNION'.
+
+
+ModuleDefinition -> ModuleIdentifier
+ 'DEFINITIONS'
+ TagDefault
+ ExtensionDefault
+ '::='
+ 'BEGIN'
+ ModuleBody
+ 'END' :
+ {'ModuleBody',Ex,Im,Types} = '$7',
+ {{typereference,Pos,Name},Defid} = '$1',
+ #module{
+ pos= Pos,
+ name= Name,
+ defid= Defid,
+ tagdefault='$3',
+ extensiondefault='$4',
+ exports=Ex,
+ imports=Im,
+ typeorval=Types}.
+% {module, '$1','$3','$6'}.
+% Results always in a record of type module defined in asn_records.hlr
+
+ModuleIdentifier -> typereference DefinitiveIdentifier :
+ put(asn1_module,'$1'#typereference.val),
+ {'$1','$2'}.
+
+DefinitiveIdentifier -> '{' DefinitiveObjIdComponentList '}' : '$2' .
+DefinitiveIdentifier -> '$empty': [].
+
+DefinitiveObjIdComponentList -> DefinitiveObjIdComponent : ['$1'].
+DefinitiveObjIdComponentList -> DefinitiveObjIdComponent DefinitiveObjIdComponentList : ['$1'|'$2'].
+
+DefinitiveObjIdComponent -> identifier : '$1' . %expanded->
+% DefinitiveObjIdComponent -> NameForm : '$1' .
+DefinitiveObjIdComponent -> number : '$1' . %expanded->
+% DefinitiveObjIdComponent -> DefinitiveNumberForm : 'fix' .
+DefinitiveObjIdComponent -> identifier '(' number ')' : {'$1','$3'} . %expanded->
+% DefinitiveObjIdComponent -> DefinitiveNameAndNumberForm : {'$1','$3'} .
+
+% DefinitiveNumberForm -> number : 'fix' .
+
+% DefinitiveNameAndNumberForm -> identifier '(' DefinitiveNumberForm ')' : 'fix' .
+
+TagDefault -> 'EXPLICIT' 'TAGS' : put(tagdefault,'EXPLICIT'),'EXPLICIT' .
+TagDefault -> 'IMPLICIT' 'TAGS' : put(tagdefault,'IMPLICIT'),'IMPLICIT' .
+TagDefault -> 'AUTOMATIC' 'TAGS' : put(tagdefault,'AUTOMATIC'),'AUTOMATIC' .
+TagDefault -> '$empty': put(tagdefault,'EXPLICIT'),'EXPLICIT'. % because this is the default
+
+ExtensionDefault -> 'EXTENSIBILITY' 'IMPLIED' : 'IMPLIED'.
+ExtensionDefault -> '$empty' : 'false'. % because this is the default
+
+ModuleBody -> Exports Imports AssignmentList : {'ModuleBody','$1','$2','$3'}.
+ModuleBody -> '$empty' : {'ModuleBody',nil,nil,[]}.
+
+Exports -> 'EXPORTS' SymbolList ';' : {exports,'$2'}.
+Exports -> 'EXPORTS' ';' : {exports,[]}.
+Exports -> '$empty' : {exports,all} .
+
+% inlined above SymbolsExported -> SymbolList : '$1'.
+% inlined above SymbolsExported -> '$empty' : [].
+
+Imports -> 'IMPORTS' SymbolsFromModuleList ';' : {imports,'$2'}.
+Imports -> 'IMPORTS' ';' : {imports,[]}.
+Imports -> '$empty' : {imports,[]} .
+
+% inlined above SymbolsImported -> SymbolsFromModuleList : '$1'.
+% inlined above SymbolsImported -> '$empty' : [].
+
+SymbolsFromModuleList -> SymbolsFromModule :['$1'].
+% SymbolsFromModuleList -> SymbolsFromModuleList SymbolsFromModule :$1.%changed
+SymbolsFromModuleList -> SymbolsFromModule SymbolsFromModuleList :['$1'|'$2'].
+
+% expanded SymbolsFromModule -> SymbolList 'FROM' GlobalModuleReference : #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+SymbolsFromModule -> SymbolList 'FROM' typereference : #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+SymbolsFromModule -> SymbolList 'FROM' typereference '{' ValueList '}': #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+%SymbolsFromModule -> SymbolList 'FROM' typereference identifier: #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+%SymbolsFromModule -> SymbolList 'FROM' typereference Externalvaluereference: #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+%SymbolsFromModule -> SymbolList 'FROM' typereference DefinedValue: #'SymbolsFromModule'{symbols = '$1',module='$3'}.
+
+% inlined GlobalModuleReference -> typereference AssignedIdentifier : {'$1','$2'} .
+
+% inlined above AssignedIdentifier -> '{' ValueList '}' : '$2'.
+% replaced AssignedIdentifier -> '{' DefinedValue ObjIdComponentList '}' :{'$2','$3'}.
+% not necessary , replaced by SAndSOfValue AssignedIdentifier -> ObjectIdentifierValue :'$1'.
+% AssignedIdentifier -> DefinedValue : '$1'.
+% inlined AssignedIdentifier -> '$empty' : undefined.
+
+SymbolList -> Symbol : ['$1'].
+SymbolList -> Symbol ',' SymbolList :['$1'|'$3'].
+
+Symbol -> Reference :'$1'.
+% later Symbol -> ParameterizedReference :'$1'.
+
+Reference -> typereference :'$1'.
+Reference -> identifier:'$1'.
+Reference -> typereference '{' '}':'$1'.
+Reference -> Externaltypereference '{' '}':'$1'.
+
+% later Reference -> objectclassreference :'$1'.
+% later Reference -> objectreference :'$1'.
+% later Reference -> objectsetreference :'$1'.
+
+AssignmentList -> Assignment : ['$1'].
+% modified AssignmentList -> AssignmentList Assignment : '$1'.
+AssignmentList -> Assignment AssignmentList : ['$1'|'$2'].
+
+Assignment -> TypeAssignment : '$1'.
+Assignment -> ValueAssignment : '$1'.
+% later Assignment -> ValueSetTypeAssignment : '$1'.
+Assignment -> ObjectClassAssignment : '$1'.
+% later Assignment -> ObjectAssignment : '$1'.
+% combined with ValueAssignment Assignment -> ObjectAssignment : '$1'.
+Assignment -> ObjectSetAssignment : '$1'.
+Assignment -> ParameterizedTypeAssignment : '$1'.
+%Assignment -> ParameterizedValueAssignment : '$1'.
+%Assignment -> ParameterizedValueSetTypeAssignment : '$1'.
+%Assignment -> ParameterizedObjectClassAssignment : '$1'.
+
+ObjectClassAssignment -> typereference '::=' 'CLASS' '{' FieldSpecs '}' :
+%ObjectClassAssignment -> objectclassreference '::=' 'CLASS' '{' FieldSpecs '}' :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec={'CLASS','$5',[]}}.
+ObjectClassAssignment -> typereference '::=' 'CLASS' '{' FieldSpecs '}' WithSyntaxSpec :
+%ObjectClassAssignment -> objectclassreference '::=' 'CLASS' '{' FieldSpecs '}' WithSyntaxSpec :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec={'CLASS','$5','$7'}}.
+
+FieldSpecs -> FieldSpec : ['$1'].
+FieldSpecs -> FieldSpec ',' FieldSpecs : ['$1'|'$3'].
+
+FieldSpec -> typefieldreference TypeOptionalitySpec : {typefield,'$1','$2'}.
+
+FieldSpec -> valuefieldreference Type 'UNIQUE' ValueOrObjectOptSpec :
+ {fixedtypevaluefield,'$1','$2','UNIQUE','$4'}.
+FieldSpec -> valuefieldreference Type ValueOrObjectOptSpec :
+ {fixedtypevaluefield,'$1','$2',undefined,'$3'}.
+
+FieldSpec -> valuefieldreference typefieldreference ValueOrObjectOptSpec :
+ {variabletypevaluefield, '$1','$2','$3'}.
+
+FieldSpec -> typefieldreference typefieldreference VSetOrOSetOptSpec :
+ {variabletypevaluesetfield, '$1','$2','$3'}.
+
+FieldSpec -> typefieldreference Type VSetOrOSetOptSpec :
+ {fixedtypevaluesetfield, '$1','$2','$3'}.
+
+TypeOptionalitySpec -> 'DEFAULT' Type : {'DEFAULT','$2'}.
+TypeOptionalitySpec -> 'OPTIONAL' : 'OPTIONAL'.
+TypeOptionalitySpec -> '$empty' : 'MANDATORY'.
+
+ValueOrObjectOptSpec -> ValueOptionalitySpec : '$1'.
+ValueOrObjectOptSpec -> ObjectOptionalitySpec : '$1'.
+ValueOrObjectOptSpec -> 'OPTIONAL' : 'OPTIONAL'.
+ValueOrObjectOptSpec -> '$empty' : 'MANDATORY'.
+
+ValueOptionalitySpec -> 'DEFAULT' Value :
+ case '$2' of
+ {identifier,_,Id} -> {'DEFAULT',Id};
+ _ -> {'DEFAULT','$2'}
+ end.
+
+%ObjectOptionalitySpec -> 'DEFAULT' Object :{'DEFAULT','$1'}.
+ObjectOptionalitySpec -> 'DEFAULT' '{' FieldSetting ',' FieldSettings '}' :
+ {'DEFAULT',{object,['$2'|'$4']}}.
+ObjectOptionalitySpec -> 'DEFAULT' '{' FieldSetting '}' :
+ {'DEFAULT',{object, ['$2']}}.
+%ObjectOptionalitySpec -> 'DEFAULT' '{' DefinedSyntaxTokens '}' :
+% {'DEFAULT',{object, '$2'}}.
+ObjectOptionalitySpec -> 'DEFAULT' ObjectFromObject :
+ {'DEFAULT',{object, '$2'}}.
+
+
+VSetOrOSetOptSpec -> ValueSetOptionalitySpec : '$1'.
+%VSetOrOSetOptSpec -> ObjectSetOptionalitySpec : '$1'.
+VSetOrOSetOptSpec -> 'OPTIONAL' : 'OPTIONAL'.
+VSetOrOSetOptSpec -> '$empty' : 'MANDATORY'.
+
+ValueSetOptionalitySpec -> 'DEFAULT' ValueSet : {'DEFAULT','$1'}.
+
+%ObjectSetOptionalitySpec -> 'DEFAULT' ObjectSet : {'DEFAULT','$1'}.
+
+OptionalitySpec -> 'DEFAULT' Type : {'DEFAULT','$2'}.
+OptionalitySpec -> 'DEFAULT' ValueNotNull :
+ case '$2' of
+ {identifier,_,Id} -> {'DEFAULT',Id};
+ _ -> {'DEFAULT','$2'}
+ end.
+OptionalitySpec -> 'OPTIONAL' : 'OPTIONAL'.
+OptionalitySpec -> '$empty' : 'MANDATORY'.
+
+WithSyntaxSpec -> 'WITH' 'SYNTAX' SyntaxList : {'WITH SYNTAX','$3'}.
+
+SyntaxList -> '{' TokenOrGroupSpecs '}' : '$2'.
+SyntaxList -> '{' '}' : [].
+
+TokenOrGroupSpecs -> TokenOrGroupSpec : ['$1'].
+TokenOrGroupSpecs -> TokenOrGroupSpec TokenOrGroupSpecs : ['$1'|'$2'].
+
+TokenOrGroupSpec -> RequiredToken : '$1'.
+TokenOrGroupSpec -> OptionalGroup : '$1'.
+
+OptionalGroup -> '[' TokenOrGroupSpecs ']' : '$2'.
+
+RequiredToken -> typereference : '$1'.
+RequiredToken -> Word : '$1'.
+RequiredToken -> ',' : '$1'.
+RequiredToken -> PrimitiveFieldName : '$1'.
+
+Word -> 'BY' : 'BY'.
+
+ParameterizedTypeAssignment -> typereference ParameterList '::=' Type :
+ #ptypedef{pos=element(2,'$1'),name=element(3,'$1'),
+ args='$2', typespec='$4'}.
+
+ParameterList -> '{' Parameters '}':'$2'.
+
+Parameters -> Parameter: ['$1'].
+Parameters -> Parameter ',' Parameters: ['$1'|'$3'].
+
+Parameter -> typereference: '$1'.
+Parameter -> Value: '$1'.
+Parameter -> Type ':' typereference: {'$1','$3'}.
+Parameter -> Type ':' Value: {'$1','$3'}.
+Parameter -> '{' typereference '}': {objectset,'$2'}.
+
+
+% Externaltypereference -> modulereference '.' typereference : {'$1','$3'} .
+Externaltypereference -> typereference '.' typereference : #'Externaltypereference'{pos=element(2,'$1'),module=element(3,'$1'),type=element(3,'$3')}.
+
+% Externalvaluereference -> modulereference '.' valuereference : {'$1','$3'} .
+% inlined Externalvaluereference -> typereference '.' identifier : #'Externalvaluereference'{pos=element(2,'$1'),module=element(3,'$1'),value=element(3,'$3')}.
+
+
+DefinedType -> Externaltypereference : '$1' .
+DefinedType -> typereference :
+ #'Externaltypereference'{pos='$1'#typereference.pos,
+ module= get(asn1_module),
+ type= '$1'#typereference.val} .
+DefinedType -> typereference ParameterList : {pt,'$1','$2'}.
+DefinedType -> Externaltypereference ParameterList : {pt,'$1','$2'}.
+
+% ActualParameterList -> '{' ActualParameters '}' : '$1'.
+
+% ActualParameters -> ActualParameter : ['$1'].
+% ActualParameters -> ActualParameter ',' ActualParameters : ['$1'|'$3'].
+
+ActualParameter -> Type : '$1'.
+ActualParameter -> ValueNotNull : '$1'.
+ActualParameter -> ValueSet : '$1'.
+% later DefinedType -> ParameterizedType : '$1' .
+% later DefinedType -> ParameterizedValueSetType : '$1' .
+
+% inlined DefinedValue -> Externalvaluereference :'$1'.
+% inlined DefinedValue -> identifier :'$1'.
+% later DefinedValue -> ParameterizedValue :'$1'.
+
+% not referenced yet AbsoluteReference -> '@' GlobalModuleReference '.' ItemSpec :{'$2','$4'}.
+
+% not referenced yet ItemSpec -> typereference :'$1'.
+% not referenced yet ItemSpec -> ItemId '.' ComponentId : {'$1','$3'}.
+
+% not referenced yet ItemId -> ItemSpec : '$1'.
+
+% not referenced yet ComponentId -> identifier :'$1'.
+% not referenced yet ComponentId -> number :'$1'.
+% not referenced yet ComponentId -> '*' :'$1'.
+
+TypeAssignment -> typereference '::=' Type :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec='$3'}.
+
+ValueAssignment -> identifier Type '::=' Value :
+ #valuedef{pos=element(2,'$1'),name=element(3,'$1'),type='$2',value='$4'}.
+
+% later ValueSetTypeAssignment -> typereference Type '::=' ValueSet :{'ValueSetTypeAssignment','$1','$2','$4'}.
+
+
+ValueSet -> '{' ElementSetSpec '}' : {valueset,'$2'}.
+
+% record(type,{tag,def,constraint}).
+Type -> BuiltinType :#type{def='$1'}.
+Type -> 'NULL' :#type{def='NULL'}.
+Type -> TaggedType:'$1'.
+Type -> ReferencedType:#type{def='$1'}. % change notag later
+Type -> ConstrainedType:'$1'.
+
+%ANY is here for compatibility with the old ASN.1 standard from 1988
+BuiltinType -> 'ANY' AnyDefBy:
+ case '$2' of
+ [] -> 'ANY';
+ _ -> {'ANY DEFINED BY','$2'}
+ end.
+BuiltinType -> BitStringType :'$1'.
+BuiltinType -> 'BOOLEAN' :element(1,'$1').
+BuiltinType -> CharacterStringType :'$1'.
+BuiltinType -> ChoiceType :'$1'.
+BuiltinType -> 'EMBEDDED' 'PDV' :'EMBEDDED PDV'.
+BuiltinType -> EnumeratedType :'$1'.
+BuiltinType -> 'EXTERNAL' :element(1,'$1').
+% later BuiltinType -> InstanceOfType :'$1'.
+BuiltinType -> IntegerType :'$1'.
+% BuiltinType -> 'NULL' :element(1,'$1').
+% later BuiltinType -> ObjectClassFieldType :'$1'.
+BuiltinType -> 'OBJECT' 'IDENTIFIER' :'OBJECT IDENTIFIER'.
+BuiltinType -> 'OCTET' 'STRING' :'OCTET STRING'.
+BuiltinType -> 'REAL' :element(1,'$1').
+BuiltinType -> SequenceType :'$1'.
+BuiltinType -> SequenceOfType :'$1'.
+BuiltinType -> SetType :'$1'.
+BuiltinType -> SetOfType :'$1'.
+% The so called Useful types
+BuiltinType -> 'GeneralizedTime': 'GeneralizedTime'.
+BuiltinType -> 'UTCTime' :'UTCTime'.
+BuiltinType -> 'ObjectDescriptor' : 'ObjectDescriptor'.
+
+% moved BuiltinType -> TaggedType :'$1'.
+
+
+AnyDefBy -> 'DEFINED' 'BY' identifier: '$3'.
+AnyDefBy -> '$empty': [].
+
+NamedType -> identifier Type :
+%{_,Pos,Val} = '$1',
+%{'NamedType',Pos,{Val,'$2'}}.
+V1 = '$1',
+{'NamedType',V1#identifier.pos,{V1#identifier.val,'$2'}}.
+NamedType -> SelectionType :'$1'.
+
+ReferencedType -> DefinedType : '$1'.
+% redundant ReferencedType -> UsefulType : 'fix'.
+ReferencedType -> SelectionType : '$1'.
+ReferencedType -> TypeFromObject : '$1'.
+% later ReferencedType -> ValueSetFromObjects : 'fix'.
+
+% to much conflicts Value -> AnyValue :'$1'.
+Value -> ValueNotNull : '$1'.
+Value -> 'NULL' :element(1,'$1').
+
+ValueNotNull -> BuiltinValue :'$1'.
+% inlined Value -> DefinedValue :'$1'. % DefinedValue , identifier
+% inlined Externalvaluereference -> Externalvaluereference :'$1'.
+ValueNotNull -> typereference '.' identifier :
+ #'Externalvaluereference'{pos=element(2,'$1'),module=element(3,'$1'),
+ value=element(3,'$3')}.
+ValueNotNull -> identifier :'$1'.
+
+
+%tmp Value -> NamedNumber: '$1'. % not a value but part of ObjIdC
+% redundant BuiltinValue -> BitStringValue :'$1'.
+BuiltinValue -> BooleanValue :'$1'.
+BuiltinValue -> CharacterStringValue :'$1'.
+BuiltinValue -> ChoiceValue :'$1'.
+% BuiltinValue -> EmbeddedPDVValue :'$1'. ==SequenceValue
+% BuiltinValue -> EnumeratedValue :'$1'. identifier
+% BuiltinValue -> ExternalValue :'$1'. ==SequenceValue
+% later BuiltinValue -> InstanceOfValue :'$1'.
+BuiltinValue -> SignedNumber :'$1'.
+% BuiltinValue -> 'NULL' :'$1'.
+% later BuiltinValue -> ObjectClassFieldValue :'$1'.
+% replaced by SAndSOfValue BuiltinValue -> ObjectIdentifierValue :'$1'.
+BuiltinValue -> bstring :element(3,'$1').
+BuiltinValue -> hstring :element(3,'$1').
+% conflict BuiltinValue -> RealValue :'$1'.
+BuiltinValue -> SAndSOfValue :'$1'.
+% replaced BuiltinValue -> SequenceOfValue :'$1'.
+% replaced BuiltinValue -> SequenceValue :'$1'.
+% replaced BuiltinValue -> SetValue :'$1'.
+% replaced BuiltinValue -> SetOfValue :'$1'.
+% conflict redundant BuiltinValue -> TaggedValue :'$1'.
+
+% inlined ReferencedValue -> DefinedValue:'$1'.
+% ReferencedValue -> Externalvaluereference:'$1'.
+% ReferencedValue -> identifier :'$1'.
+% later ReferencedValue -> ValueFromObject:'$1'.
+
+% inlined BooleanType -> BOOLEAN :'BOOLEAN'.
+
+% to much conflicts AnyValue -> Type ':' Value : {'ANYVALUE',{'$1','$3'}}.
+
+BooleanValue -> TRUE :true.
+BooleanValue -> FALSE :false.
+
+IntegerType -> 'INTEGER' : 'INTEGER'.
+IntegerType -> 'INTEGER' '{' NamedNumberList '}' : {'INTEGER','$3'}.
+
+NamedNumberList -> NamedNumber :['$1'].
+% modified NamedNumberList -> NamedNumberList ',' NamedNumber :'fix'.
+NamedNumberList -> NamedNumber ',' NamedNumberList :['$1'|'$3'].
+
+NamedNumber -> identifier '(' SignedNumber ')' : {'NamedNumber',element(3,'$1'),'$3'}.
+NamedNumber -> identifier '(' typereference '.' identifier ')' : {'NamedNumber',element(3,'$1'),{'ExternalValue',element(3,'$3'),element(3,'$5')}}.
+NamedNumber -> identifier '(' identifier ')' : {'NamedNumber',element(3,'$1'),element(3,'$3')}.
+
+%NamedValue -> identifier Value :
+% {'NamedValue',element(2,'$1'),element(3,'$1'),'$2'}.
+
+
+SignedNumber -> number : element(3,'$1').
+SignedNumber -> '-' number : - element(3,'$1').
+
+% inlined IntegerValue -> SignedNumber :'$1'.
+% conflict moved to Value IntegerValue -> identifier:'$1'.
+
+EnumeratedType -> ENUMERATED '{' Enumeration '}' :{'ENUMERATED','$3'}.
+
+% inlined Enumerations -> Enumeration :{'$1','false',[]}.
+% inlined Enumerations -> Enumeration ',' '...' : {'$1','true',[]}.
+% inlined Enumerations -> Enumeration ',' '...' ',' Enumeration : {'$1','true','$5'}.
+
+Enumeration -> EnumerationItem :['$1'].
+% modified Enumeration -> EnumerationItem ',' Enumeration :'fix'.
+Enumeration -> EnumerationItem ',' Enumeration :['$1'|'$3'].
+
+EnumerationItem -> identifier:element(3,'$1').
+EnumerationItem -> NamedNumber :'$1'.
+EnumerationItem -> '...' :'EXTENSIONMARK'.
+
+% conflict moved to Value EnumeratedValue -> identifier:'$1'.
+
+% inlined RealType -> REAL:'REAL'.
+
+RealValue -> NumericRealValue :'$1'.
+RealValue -> SpecialRealValue:'$1'.
+
+% ?? NumericRealValue -> number:'$1'. % number MUST BE '0'
+NumericRealValue -> SAndSOfValue : '$1'. % Value of the associated sequence type
+
+SpecialRealValue -> 'PLUS-INFINITY' :'$1'.
+SpecialRealValue -> 'MINUS-INFINITY' :'$1'.
+
+BitStringType -> 'BIT' 'STRING' :{'BIT STRING',[]}.
+BitStringType -> 'BIT' 'STRING' '{' NamedNumberList '}' :{'BIT STRING','$4'}.
+% NamedBitList replaced by NamedNumberList to reduce the grammar
+% Must check later that all "numbers" are positive
+
+% inlined BitStringValue -> bstring:'$1'.
+% inlined BitStringValue -> hstring:'$1'.
+% redundant use SequenceValue BitStringValue -> '{' IdentifierList '}' :$2.
+% redundant use SequenceValue BitStringValue -> '{' '}' :'fix'.
+
+IdentifierList -> identifier :[element(3,'$1')].
+% modified IdentifierList -> IdentifierList ',' identifier :'$1'.
+IdentifierList -> identifier ',' IdentifierList :[element(3,'$1')|'$3'].
+
+% inlined OctetStringType -> 'OCTET' 'STRING' :'OCTET STRING'.
+
+% inlined OctetStringValue -> bstring:'$1'.
+% inlined OctetStringValue -> hstring:'$1'.
+
+% inlined NullType -> 'NULL':'NULL'.
+
+% inlined NullValue -> NULL:'NULL'.
+
+% result is {'SEQUENCE',Optionals,Extensionmark,Componenttypelist}.
+SequenceType -> SEQUENCE '{' ComponentTypeList '}' :{'SEQUENCE','$3'}.
+% SequenceType -> SEQUENCE '{' ComponentTypeLists '}' :{'SEQUENCE','$3'}.
+% SequenceType -> SEQUENCE '{' ExtensionAndException '}' :{'SEQUENCE','$3'}.
+SequenceType -> SEQUENCE '{' '}' :{'SEQUENCE',[]}.
+
+% result is {RootComponentList,ExtensionAndException,AdditionalComponentTypeList}.
+%ComponentTypeLists -> ComponentTypeList ',' ExtensionAndException :{'$1','$3',[]}.
+%ComponentTypeLists -> ComponentTypeList :{'$1','false',[]}.
+%ComponentTypeLists -> ComponentTypeList ',' ExtensionAndException
+% ',' ComponentTypeList :{'$1','$3', '$5'}.
+%ComponentTypeLists -> ExtensionAndException ',' ComponentTypeList :{[],'$1','$3'}.
+
+ComponentTypeList -> ComponentType :['$1'].
+% modified below ComponentTypeList -> ComponentTypeList ',' ComponentType :'$1'.
+ComponentTypeList -> ComponentType ',' ComponentTypeList :['$1'|'$3'].
+
+% -record('ComponentType',{pos,name,type,attrib}).
+ComponentType -> '...' ExceptionSpec :{'EXTENSIONMARK',element(2,'$1'),'$2'}.
+ComponentType -> NamedType :
+ {'NamedType',Pos,{Name,Type}} = '$1',
+ #'ComponentType'{pos=Pos,name=Name,typespec=Type,prop=mandatory}.
+ComponentType -> NamedType 'OPTIONAL' :
+ {'NamedType',Pos,{Name,Type}} = '$1',
+ #'ComponentType'{pos=Pos,name=Name,typespec=Type,prop='OPTIONAL'}.
+ComponentType -> NamedType 'DEFAULT' Value:
+ {'NamedType',Pos,{Name,Type}} = '$1',
+ #'ComponentType'{pos=Pos,name=Name,typespec=Type,prop={'DEFAULT','$3'}}.
+ComponentType -> 'COMPONENTS' 'OF' Type :{'COMPONENTS OF','$3'}.
+
+% redundant ExtensionAndException -> '...' : extensionmark.
+% ExtensionAndException -> '...' ExceptionSpec : {extensionmark,'$2'}.
+
+% replaced SequenceValue -> '{' ComponentValueList '}':'$2'.
+% replaced SequenceValue -> '{' '}':[].
+
+ValueList -> Value :['$1'].
+ValueList -> NamedNumber :['$1'].
+% modified ValueList -> ValueList ',' Value :'$1'.
+ValueList -> Value ',' ValueList :['$1'|'$3'].
+ValueList -> Value ',' '...' :['$1' |[]].
+ValueList -> Value ValueList : ['$1',space|'$2'].
+ValueList -> NamedNumber ValueList: ['$1',space|'$2'].
+
+%ComponentValueList -> identifier ObjIdComponent:[{'NamedValue','$1','$2'}].
+%ComponentValueList -> NamedValue :['$1'].
+%ComponentValueList -> NamedValue ',' ComponentValueList:['$1'|'$3'].
+%ComponentValueList -> identifier ObjIdComponent ',' ComponentValueList :[{'NamedValue', '$1','$2'}|'$4'].
+
+SequenceOfType -> SEQUENCE OF Type : {'SEQUENCE OF','$3'}.
+
+% replaced SequenceOfValue with SAndSOfValue
+
+SAndSOfValue -> '{' ValueList '}' :'$2'.
+%SAndSOfValue -> '{' ComponentValueList '}' :'$2'.
+SAndSOfValue -> '{' '}' :[].
+
+% save for later SetType ->
+% result is {'SET',Optionals,Extensionmark,Componenttypelist}.
+SetType -> SET '{' ComponentTypeList '}' :{'SET','$3'}.
+% SetType -> SET '{' ExtensionAndException '}' :{'SET','$3'}.
+SetType -> SET '{' '}' :{'SET',[]}.
+
+% replaced SetValue with SAndSOfValue
+
+SetOfType -> SET OF Type : {'SET OF','$3'}.
+
+% replaced SetOfValue with SAndSOfValue
+
+ChoiceType -> 'CHOICE' '{' ComponentTypeList '}' :{'CHOICE','$3'}.
+% AlternativeTypeList is replaced by ComponentTypeList
+ChoiceValue -> identifier ':' Value : {'ChoiceValue',element(3,'$1'),'$3'}.
+% save for later SelectionType ->
+
+TaggedType -> Tag Type : '$2'#type{tag=['$1'#tag{type={default,get(tagdefault)}}]}.
+TaggedType -> Tag IMPLICIT Type :'$3'#type{tag=['$1'#tag{type='IMPLICIT'}]}.
+TaggedType -> Tag EXPLICIT Type :'$3'#type{tag=['$1'#tag{type='EXPLICIT'}]}.
+
+Tag -> '[' Class ClassNumber ']': #tag{class='$2',number='$3'}.
+Tag -> '[' Class typereference '.' identifier ']':
+ #tag{class='$2',number=#'Externalvaluereference'{pos=element(2,'$3'),module=element(3,'$3'),
+ value=element(3,'$5')}}.
+Tag -> '[' Class number ']': #tag{class='$2',number=element(3,'$3')}.
+Tag -> '[' Class identifier ']': #tag{class='$2',number=element(3,'$3')}.
+
+ClassNumber -> number :element(3,'$1').
+% inlined above ClassNumber -> typereference '.' identifier :{'Externalvaluereference',element(3,'$1'),element(3,'$3')}.
+ClassNumber -> identifier :element(3,'$1').
+
+Class -> 'UNIVERSAL' :element(1,'$1').
+Class -> 'APPLICATION' :element(1,'$1').
+Class -> 'PRIVATE' :element(1,'$1').
+Class -> '$empty' :'CONTEXT'.
+
+% conflict redundant TaggedValue -> Value:'$1'.
+
+% inlined EmbeddedPDVType -> 'EMBEDDED' 'PDV' :'EMBEDDED PDV'.
+
+% inlined EmbeddedPDVValue -> SequenceValue:'$1'.
+
+% inlined ExternalType -> 'EXTERNAL' :'EXTERNAL'.
+
+% inlined ExternalValue -> SequenceValue :'$1'.
+
+% inlined ObjectIdentifierType -> 'OBJECT' 'IDENTIFIER' :'OBJECT IDENTIFIER'.
+
+ObjectIdentifierValue -> '{' ObjIdComponentList '}' :'$2'.
+% inlined ObjectIdentifierValue -> SequenceAndSequenceOfValue :'$1'.
+% ObjectIdentifierValue -> '{' identifier ObjIdComponentList '}' :{'ObjectIdentifierValue','$2','$3'}.
+% ObjectIdentifierValue -> '{' typereference '.' identifier ObjIdComponentList '}' :{'ObjectIdentifierValue',{'$2','$4'},'$5'}.
+
+ObjIdComponentList -> Value:'$1'.
+ObjIdComponentList -> Value ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> DefinedValue:'$1'.
+%ObjIdComponentList -> number:'$1'.
+%ObjIdComponentList -> DefinedValue ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> number ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> ObjIdComponent ObjIdComponentList :['$1'|'$2'].
+%ObjIdComponentList -> ObjIdComponent ObjIdComponentList :['$1'|'$2'].
+
+% redundant ObjIdComponent -> NameForm :'$1'. % expanded
+% replaced by 2 ObjIdComponent -> NumberForm :'$1'.
+% ObjIdComponent -> number :'$1'.
+% ObjIdComponent -> DefinedValue :'$1'. % means DefinedValue
+% ObjIdComponent -> NameAndNumberForm :'$1'.
+% ObjIdComponent -> NamedNumber :'$1'.
+% NamedBit replaced by NamedNumber to reduce grammar
+% must check later that "number" is positive
+
+% NameForm -> identifier:'$1'.
+
+% inlined NumberForm -> number :'$1'.
+% inlined NumberForm -> DefinedValue :'$1'.
+
+% replaced by NamedBit NameAndNumberForm -> identifier '(' NumberForm ')'.
+% NameAndNumberForm -> NamedBit:'$1'.
+
+
+CharacterStringType -> restrictedcharacterstringtype :element(3,'$1').
+CharacterStringType -> 'CHARACTER' 'STRING' :'CHARACTER STRING'.
+
+RestrictedCharacterStringValue -> cstring :element(3, '$1').
+% modified below RestrictedCharacterStringValue -> CharacterStringList :'$1'.
+% conflict vs BuiltinValue RestrictedCharacterStringValue -> SequenceAndSequenceOfValue :'$1'.
+RestrictedCharacterStringValue -> Quadruple :'$1'.
+RestrictedCharacterStringValue -> Tuple :'$1'.
+
+% redundant CharacterStringList -> '{' ValueList '}' :'$2'. % modified
+
+% redundant CharSyms -> CharsDefn :'$1'.
+% redundant CharSyms -> CharSyms ',' CharsDefn :['$1'|'$3'].
+
+% redundant CharsDefn -> cstring :'$1'.
+% temporary replaced see below CharsDefn -> DefinedValue :'$1'.
+% redundant CharsDefn -> Value :'$1'.
+
+Quadruple -> '{' number ',' number ',' number ',' number '}' :{'Quadruple','$2','$4','$6','$8'}.
+% {Group,Plane,Row,Cell}
+
+Tuple -> '{' number ',' number '}' :{'Tuple', '$2','$4'}.
+% {TableColumn,TableRow}
+
+% inlined UnrestrictedCharacterString -> 'CHARACTER' 'STRING' :'CHARACTER STRING'.
+
+CharacterStringValue -> RestrictedCharacterStringValue :'$1'.
+% conflict vs BuiltinValue CharacterStringValue -> SequenceValue :'$1'. % UnrestrictedCharacterStringValue
+
+% inlined UsefulType -> typereference :'$1'.
+
+SelectionType -> identifier '<' Type : {'SelectionType',element(3,'$1'),'$3'}.
+
+ConstrainedType -> Type Constraint :
+ '$1'#type{constraint=merge_constraints(['$2'])}.
+ConstrainedType -> Type Constraint Constraint :
+ '$1'#type{constraint=merge_constraints(['$2','$3'])}.
+ConstrainedType -> Type Constraint Constraint Constraint:
+ '$1'#type{constraint=merge_constraints(['$2','$3','$4'])}.
+ConstrainedType -> Type Constraint Constraint Constraint Constraint:
+ '$1'#type{constraint=merge_constraints(['$2','$3','$4','$5'])}.
+%ConstrainedType -> Type Constraint :'$1'#type{constraint='$2'}.
+%ConstrainedType -> Type Constraint :'$1'#type{constraint='$2'}.
+ConstrainedType -> TypeWithConstraint :'$1'.
+
+TypeWithConstraint -> 'SET' Constraint 'OF' Type :
+ #type{def = {'SET OF','$4'},constraint=merge_constraints(['$2'])}.
+TypeWithConstraint -> 'SET' 'SIZE' Constraint 'OF' Type :
+ #type{def = {'SET OF','$5'},constraint = merge_constraints([#constraint{c={'SizeConstraint','$3'#constraint.c}}])}.
+TypeWithConstraint -> 'SEQUENCE' Constraint 'OF' Type :
+ #type{def = {'SEQUENCE OF','$4'},constraint =
+ merge_constraints(['$2'])}.
+TypeWithConstraint -> 'SEQUENCE' 'SIZE' Constraint 'OF' Type :
+ #type{def = {'SEQUENCE OF','$5'},constraint = merge_constraints([#constraint{c={'SizeConstraint','$3'#constraint.c}}])}.
+
+
+Constraint -> '(' ConstraintSpec ExceptionSpec ')' :
+ #constraint{c='$2',e='$3'}.
+
+% inlined Constraint -> SubTypeConstraint :'$1'.
+ConstraintSpec -> ElementSetSpecs :'$1'.
+ConstraintSpec -> UserDefinedConstraint :'$1'.
+ConstraintSpec -> TableConstraint :'$1'.
+
+TableConstraint -> ComponentRelationConstraint : '$1'.
+TableConstraint -> ObjectSet : '$1'.
+%TableConstraint -> '{' typereference '}' :tableconstraint.
+
+ComponentRelationConstraint -> '{' typereference '}' '{' '@' ComponentIdList '}' : componentrelation.
+ComponentRelationConstraint -> '{' typereference '}' '{' '@' '.' ComponentIdList '}' : componentrelation.
+
+ComponentIdList -> identifier: ['$1'].
+ComponentIdList -> identifier '.' ComponentIdList: ['$1'| '$3'].
+
+
+% later ConstraintSpec -> GeneralConstraint :'$1'.
+
+% from X.682
+UserDefinedConstraint -> 'CONSTRAINED' 'BY' '{' '}' : {constrained_by,[]}.
+UserDefinedConstraint -> 'CONSTRAINED' 'BY'
+ '{' UserDefinedConstraintParameters '}' : {constrained_by,'$4'}.
+
+UserDefinedConstraintParameters -> UserDefinedConstraintParameter : ['$1'].
+UserDefinedConstraintParameters ->
+ UserDefinedConstraintParameter ','
+ UserDefinedConstraintParameters: ['$1'|'$3'].
+
+UserDefinedConstraintParameter -> Type '.' ActualParameter : {'$1','$3'}.
+UserDefinedConstraintParameter -> ActualParameter : '$1'.
+
+
+
+ExceptionSpec -> '!' ExceptionIdentification : '$1'.
+ExceptionSpec -> '$empty' : undefined.
+
+ExceptionIdentification -> SignedNumber : '$1'.
+% inlined ExceptionIdentification -> DefinedValue : '$1'.
+ExceptionIdentification -> typereference '.' identifier :
+ #'Externalvaluereference'{pos=element(2,'$1'),module=element(3,'$1'),
+ value=element(3,'$1')}.
+ExceptionIdentification -> identifier :'$1'.
+ExceptionIdentification -> Type ':' Value : {'$1','$3'}.
+
+% inlined SubTypeConstraint -> ElementSetSpec
+
+ElementSetSpecs -> ElementSetSpec : '$1'.
+ElementSetSpecs -> ElementSetSpec ',' '...': {'$1',[]}.
+ElementSetSpecs -> '...' ',' ElementSetSpec : {[],'$3'}.
+ElementSetSpecs -> ElementSetSpec ',' '...' ',' ElementSetSpec : {'$1','$5'}.
+
+ElementSetSpec -> Unions : '$1'.
+ElementSetSpec -> 'ALL' Exclusions : {'ALL','$2'}.
+
+Unions -> Intersections : '$1'.
+Unions -> UElems UnionMark IntersectionElements :
+ case {'$1','$3'} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {'SingleValue',ordsets:union(to_set(V1),to_set(V2))}
+ end.
+
+UElems -> Unions :'$1'.
+
+Intersections -> IntersectionElements :'$1'.
+Intersections -> IElems IntersectionMark IntersectionElements :
+ case {'$1','$3'} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {'SingleValue',ordsets:intersection(to_set(V1),to_set(V2))};
+ {V1,V2} when list(V1) ->
+ V1 ++ [V2];
+ {V1,V2} ->
+ [V1,V2]
+ end.
+%Intersections -> IElems '^' IntersectionElements :{'INTERSECTION','$1','$3'}.
+%Intersections -> IElems 'INTERSECTION' IntersectionElements :{'INTERSECTION','$1','$3'}.
+
+IElems -> Intersections :'$1'.
+
+IntersectionElements -> Elements :'$1'.
+IntersectionElements -> Elems Exclusions :{'$1','$2'}.
+
+Elems -> Elements :'$1'.
+
+Exclusions -> 'EXCEPT' Elements :{'EXCEPT','$2'}.
+
+IntersectionMark -> 'INTERSECTION':'$1'.
+IntersectionMark -> '^':'$1'.
+UnionMark -> 'UNION':'$1'.
+UnionMark -> '|':'$1'.
+
+
+Elements -> SubTypeElements : '$1'.
+%Elements -> ObjectSetElements : '$1'.
+Elements -> '(' ElementSetSpec ')' : '$2'.
+Elements -> ReferencedType : '$1'.
+
+SubTypeElements -> ValueList : {'SingleValue','$1'}. % NOTE it must be a Value
+% The rule above modifyed only because of conflicts
+SubTypeElements -> 'INCLUDES' Type : {'ContainedSubType','$2'}.
+%not lalr1 if this is activated SubTypeElements -> Type : {'TypeConstraint','$1'}.
+SubTypeElements -> LowerEndpoint '..' UpperEndpoint : {'ValueRange',{'$1','$3'}}.
+SubTypeElements -> 'FROM' Constraint : {'PermittedAlphabet','$2'#constraint.c}.
+SubTypeElements -> 'SIZE' Constraint: {'SizeConstraint','$2'#constraint.c}.
+% later will introduce conflicts related to NULL SubTypeElements -> Type : {'TypeConstraint','$1'}.
+SubTypeElements -> 'WITH' 'COMPONENT' Constraint:{'WITH COMPONENT','$3'}.
+SubTypeElements -> 'WITH' 'COMPONENTS' '{' TypeConstraints '}':{'WITH COMPONENTS',{'FullSpecification','$4'}}.
+SubTypeElements -> 'WITH' 'COMPONENTS' '{' '...' ',' TypeConstraints '}' :{'WITH COMPONENTS',{'PartialSpecification','$3'}}.
+
+% inlined above InnerTypeConstraints ::=
+% inlined above SingleTypeConstraint::= Constraint
+% inlined above MultipleTypeConstraints ::= FullSpecification | PartialSpecification
+% inlined above FullSpecification ::= "{" TypeConstraints "}"
+% inlined above PartialSpecification ::= "{" "..." "," TypeConstraints "}"
+% TypeConstraints -> identifier : [{'NamedConstraint',element(3,'$1'),undefined,undefined}]. % is this really meaningful or allowed
+TypeConstraints -> NamedConstraint : ['$1'].
+TypeConstraints -> NamedConstraint ',' TypeConstraints : ['$1'|'$3'].
+TypeConstraints -> identifier : ['$1'].
+TypeConstraints -> identifier ',' TypeConstraints : ['$1'|'$3'].
+
+NamedConstraint -> identifier Constraint PresenceConstraint :{'NamedConstraint',element(3,'$1'),'$2','$3'}.
+NamedConstraint -> identifier Constraint :{'NamedConstraint',element(3,'$1'),'$2',undefined}.
+NamedConstraint -> identifier PresenceConstraint :{'NamedConstraint',element(3,'$1'),undefined,'$2'}.
+
+PresenceConstraint -> 'PRESENT' : 'PRESENT'.
+PresenceConstraint -> 'ABSENT' : 'ABSENT'.
+PresenceConstraint -> 'OPTIONAL' : 'OPTIONAL'.
+
+
+
+LowerEndpoint -> LowerEndValue :'$1'.
+%LowerEndpoint -> LowerEndValue '<':{gt,'$1'}.
+LowerEndpoint -> LowerEndValue '<':('$1'+1).
+
+UpperEndpoint -> UpperEndValue :'$1'.
+%UpperEndpoint -> '<' UpperEndValue :{lt,'$2'}.
+UpperEndpoint -> '<' UpperEndValue :('$2'-1).
+
+LowerEndValue -> Value :'$1'.
+LowerEndValue -> 'MIN' :'MIN'.
+
+UpperEndValue -> Value :'$1'.
+UpperEndValue -> 'MAX' :'MAX'.
+
+
+% X.681
+
+
+% X.681 chap 15
+
+%TypeFromObject -> ReferencedObjects '.' FieldName : {'$1','$3'}.
+TypeFromObject -> typereference '.' FieldName : {'$1','$3'}.
+
+ReferencedObjects -> typereference : '$1'.
+%ReferencedObjects -> ParameterizedObject
+%ReferencedObjects -> DefinedObjectSet
+%ReferencedObjects -> ParameterizedObjectSet
+
+FieldName -> typefieldreference : ['$1'].
+FieldName -> valuefieldreference : ['$1'].
+FieldName -> FieldName '.' FieldName : ['$1' | '$3'].
+
+PrimitiveFieldName -> typefieldreference : '$1'.
+PrimitiveFieldName -> valuefieldreference : '$1'.
+
+%ObjectSetAssignment -> typereference DefinedObjectClass '::=' ObjectSet: null.
+ObjectSetAssignment -> typereference typereference '::=' ObjectSet :
+ #typedef{pos=element(2,'$1'),name=element(3,'$1'),typespec={'ObjectSet',element(3,'$2'), '$4'}}.
+ObjectSetAssignment -> typereference typereference '.' typereference '::=' ObjectSet.
+
+ObjectSet -> '{' ElementSetSpecs '}' : '$2'.
+ObjectSet -> '{' '...' '}' : ['EXTENSIONMARK'].
+
+%ObjectSetElements -> Object.
+% ObjectSetElements -> identifier : '$1'.
+%ObjectSetElements -> DefinedObjectSet.
+%ObjectSetElements -> ObjectSetFromObjects.
+%ObjectSetElements -> ParameterizedObjectSet.
+
+%ObjectAssignment -> identifier DefinedObjectClass '::=' Object.
+ObjectAssignment -> ValueAssignment.
+%ObjectAssignment -> identifier typereference '::=' Object.
+%ObjectAssignment -> identifier typereference '.' typereference '::=' Object.
+
+%Object -> DefinedObject: '$1'.
+%Object -> ExternalObjectReference: '$1'.%Object -> DefinedObject: '$1'.
+Object -> typereference '.' identifier: '$1'.%Object -> DefinedObject: '$1'.
+Object -> identifier: '$1'.%Object -> DefinedObject: '$1'.
+
+%Object -> ObjectDefn -> DefaultSyntax: '$1'.
+Object -> '{' FieldSetting ',' FieldSettings '}' : ['$2'|'$4'].
+Object -> '{' FieldSetting '}' :['$2'].
+
+%% For User-friendly notation
+%% Object -> ObjectDefn -> DefinedSyntax
+Object -> '{' '}'.
+Object -> '{' DefinedSyntaxTokens '}'.
+
+% later Object -> ParameterizedObject: '$1'. look in x.683
+
+%DefinedObject -> ExternalObjectReference: '$1'.
+%DefinedObject -> identifier: '$1'.
+
+DefinedObjectClass -> typereference.
+%DefinedObjectClass -> objectclassreference.
+DefinedObjectClass -> ExternalObjectClassReference.
+%DefinedObjectClass -> typereference '.' objectclassreference.
+%%DefinedObjectClass -> UsefulObjectClassReference.
+
+ExternalObjectReference -> typereference '.' identifier.
+ExternalObjectClassReference -> typereference '.' typereference.
+%%ExternalObjectClassReference -> typereference '.' objectclassreference.
+
+ObjectDefn -> DefaultSyntax: '$1'.
+%ObjectDefn -> DefinedSyntax: '$1'.
+
+ObjectFromObject -> ReferencedObjects '.' FieldName : {'ObjectFromObject','$1','$3'}.
+
+% later look in x.683 ParameterizedObject ->
+
+%DefaultSyntax -> '{' '}'.
+%DefaultSyntax -> '{' FieldSettings '}': '$2'.
+DefaultSyntax -> '{' FieldSetting ',' FieldSettings '}': '$2'.
+DefaultSyntax -> '{' FieldSetting '}': '$2'.
+
+FieldSetting -> PrimitiveFieldName Setting: {'$1','$2'}.
+
+FieldSettings -> FieldSetting ',' FieldSettings: ['$1'|'$3'].
+FieldSettings -> FieldSetting ',' FieldSettings: ['$1'|'$3'].
+FieldSettings -> FieldSetting: '$1'.
+
+%DefinedSyntax -> '{' '}'.
+DefinedSyntax -> '{' DefinedSyntaxTokens '}': '$2'.
+
+DefinedSyntaxTokens -> DefinedSyntaxToken: '$1'.
+DefinedSyntaxTokens -> DefinedSyntaxToken DefinedSyntaxTokens: ['$1'|'$2'].
+
+% expanded DefinedSyntaxToken -> Literal: '$1'.
+%DefinedSyntaxToken -> typereference: '$1'.
+DefinedSyntaxToken -> word: '$1'.
+DefinedSyntaxToken -> ',': '$1'.
+DefinedSyntaxToken -> Setting: '$1'.
+%DefinedSyntaxToken -> '$empty': nil .
+
+% Setting ::= Type|Value|ValueSet|Object|ObjectSet
+Setting -> Type: '$1'.
+%Setting -> Value: '$1'.
+%Setting -> ValueNotNull: '$1'.
+Setting -> BuiltinValue: '$1'.
+Setting -> ValueSet: '$1'.
+%Setting -> Object: '$1'.
+%Setting -> ExternalObjectReference.
+Setting -> typereference '.' identifier.
+Setting -> identifier.
+Setting -> ObjectDefn.
+
+Setting -> ObjectSet: '$1'.
+
+
+Erlang code.
+%%-author('[email protected]').
+-copyright('Copyright (c) 1991-99 Ericsson Telecom AB').
+-vsn('$Revision: 1.1 $').
+-include("asn1_records.hrl").
+
+to_set(V) when list(V) ->
+ ordsets:list_to_set(V);
+to_set(V) ->
+ ordsets:list_to_set([V]).
+
+merge_constraints({Rlist,ExtList}) -> % extensionmarker in constraint
+ {merge_constraints(Rlist,[],[]),
+ merge_constraints(ExtList,[],[])};
+
+merge_constraints(Clist) ->
+ merge_constraints(Clist, [], []).
+
+merge_constraints([Ch|Ct],Cacc, Eacc) ->
+ NewEacc = case Ch#constraint.e of
+ undefined -> Eacc;
+ E -> [E|Eacc]
+ end,
+ merge_constraints(Ct,[fixup_constraint(Ch#constraint.c)|Cacc],NewEacc);
+
+merge_constraints([],Cacc,[]) ->
+ lists:flatten(Cacc);
+merge_constraints([],Cacc,Eacc) ->
+ lists:flatten(Cacc) ++ [{'Errors',Eacc}].
+
+fixup_constraint(C) ->
+ case C of
+ {'SingleValue',V} when list(V) ->
+ [C,
+ {'ValueRange',{lists:min(V),lists:max(V)}}];
+ {'PermittedAlphabet',{'SingleValue',V}} when list(V) ->
+ V2 = {'SingleValue',
+ ordsets:list_to_set(lists:flatten(V))},
+ {'PermittedAlphabet',V2};
+ {'PermittedAlphabet',{'SingleValue',V}} ->
+ V2 = {'SingleValue',[V]},
+ {'PermittedAlphabet',V2};
+ {'SizeConstraint',Sc} ->
+ {'SizeConstraint',fixup_size_constraint(Sc)};
+
+ List when list(List) ->
+ [fixup_constraint(Xc)||Xc <- List];
+ Other ->
+ Other
+ end.
+
+fixup_size_constraint({'ValueRange',{Lb,Ub}}) ->
+ {Lb,Ub};
+fixup_size_constraint({{'ValueRange',R},[]}) ->
+ {R,[]};
+fixup_size_constraint({[],{'ValueRange',R}}) ->
+ {[],R};
+fixup_size_constraint({{'ValueRange',R1},{'ValueRange',R2}}) ->
+ {R1,R2};
+fixup_size_constraint({'SingleValue',[Sv]}) ->
+ fixup_size_constraint({'SingleValue',Sv});
+fixup_size_constraint({'SingleValue',L}) when list(L) ->
+ ordsets:list_to_set(L);
+fixup_size_constraint({'SingleValue',L}) ->
+ {L,L};
+fixup_size_constraint({C1,C2}) ->
+ {fixup_size_constraint(C1), fixup_size_constraint(C2)}.
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser2.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser2.erl
new file mode 100644
index 0000000000..639dcc6622
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_parser2.erl
@@ -0,0 +1,2764 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 2000, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_parser2.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_parser2).
+
+-export([parse/1]).
+-include("asn1_records.hrl").
+
+%% parse all types in module
+parse(Tokens) ->
+ case catch parse_ModuleDefinition(Tokens) of
+ {'EXIT',Reason} ->
+ {error,{{undefined,get(asn1_module),
+ [internal,error,'when',parsing,module,definition,Reason]},
+ hd(Tokens)}};
+ {asn1_error,Reason} ->
+ {error,{Reason,hd(Tokens)}};
+ {ModuleDefinition,Rest1} ->
+ {Types,Rest2} = parse_AssignmentList(Rest1),
+ case Rest2 of
+ [{'END',_}|_Rest3] ->
+ {ok,ModuleDefinition#module{typeorval = Types}};
+ _ ->
+ {error,{{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'END']},
+ hd(Rest2)}}
+ end
+ end.
+
+parse_ModuleDefinition([{typereference,L1,ModuleIdentifier}|Rest0]) ->
+ put(asn1_module,ModuleIdentifier),
+ {_DefinitiveIdentifier,Rest02} =
+ case Rest0 of
+ [{'{',_}|_Rest01] ->
+ parse_ObjectIdentifierValue(Rest0);
+ _ ->
+ {[],Rest0}
+ end,
+ Rest = case Rest02 of
+ [{'DEFINITIONS',_}|Rest03] ->
+ Rest03;
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest02)),get(asn1_module),
+ [got,get_token(hd(Rest02)),
+ expected,'DEFINITIONS']}})
+ end,
+ {TagDefault,Rest2} =
+ case Rest of
+ [{'EXPLICIT',_L3},{'TAGS',_L4}|Rest1] ->
+ put(tagdefault,'EXPLICIT'), {'EXPLICIT',Rest1};
+ [{'IMPLICIT',_L3},{'TAGS',_L4}|Rest1] ->
+ put(tagdefault,'IMPLICIT'), {'IMPLICIT',Rest1};
+ [{'AUTOMATIC',_L3},{'TAGS',_L4}|Rest1] ->
+ put(tagdefault,'AUTOMATIC'), {'AUTOMATIC',Rest1};
+ Rest1 ->
+ put(tagdefault,'EXPLICIT'), {'EXPLICIT',Rest1} % The default
+ end,
+ {ExtensionDefault,Rest3} =
+ case Rest2 of
+ [{'EXTENSIBILITY',_L5}, {'IMPLIED',_L6}|Rest21] ->
+ {'IMPLIED',Rest21};
+ _ -> {false,Rest2}
+ end,
+ case Rest3 of
+ [{'::=',_L7}, {'BEGIN',_L8}|Rest4] ->
+ {Exports, Rest5} = parse_Exports(Rest4),
+ {Imports, Rest6} = parse_Imports(Rest5),
+ {#module{ pos = L1,
+ name = ModuleIdentifier,
+ defid = [], % fix this
+ tagdefault = TagDefault,
+ extensiondefault = ExtensionDefault,
+ exports = Exports,
+ imports = Imports},Rest6};
+ _ -> throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,"::= BEGIN"]}})
+ end;
+parse_ModuleDefinition(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typereference]}}).
+
+parse_Exports([{'EXPORTS',_L1},{';',_L2}|Rest]) ->
+ {{exports,[]},Rest};
+parse_Exports([{'EXPORTS',_L1}|Rest]) ->
+ {SymbolList,Rest2} = parse_SymbolList(Rest),
+ case Rest2 of
+ [{';',_}|Rest3] ->
+ {{exports,SymbolList},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,';']}})
+ end;
+parse_Exports(Rest) ->
+ {{exports,all},Rest}.
+
+parse_SymbolList(Tokens) ->
+ parse_SymbolList(Tokens,[]).
+
+parse_SymbolList(Tokens,Acc) ->
+ {Symbol,Rest} = parse_Symbol(Tokens),
+ case Rest of
+ [{',',_L1}|Rest2] ->
+ parse_SymbolList(Rest2,[Symbol|Acc]);
+ Rest2 ->
+ {lists:reverse([Symbol|Acc]),Rest2}
+ end.
+
+parse_Symbol(Tokens) ->
+ parse_Reference(Tokens).
+
+parse_Reference([{typereference,L1,TrefName},{'{',_L2},{'}',_L3}|Rest]) ->
+% {Tref,Rest};
+ {tref2Exttref(L1,TrefName),Rest};
+parse_Reference([Tref1 = {typereference,_,_},{'.',_},Tref2 = {typereference,_,_},
+ {'{',_L2},{'}',_L3}|Rest]) ->
+% {{Tref1,Tref2},Rest};
+ {{tref2Exttref(Tref1),tref2Exttref(Tref2)},Rest};
+parse_Reference([Tref = {typereference,_L1,_TrefName}|Rest]) ->
+ {tref2Exttref(Tref),Rest};
+parse_Reference([Vref = {identifier,_L1,_VName}|Rest]) ->
+ {identifier2Extvalueref(Vref),Rest};
+parse_Reference(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,identifier]]}}).
+
+parse_Imports([{'IMPORTS',_L1},{';',_L2}|Rest]) ->
+ {{imports,[]},Rest};
+parse_Imports([{'IMPORTS',_L1}|Rest]) ->
+ {SymbolsFromModuleList,Rest2} = parse_SymbolsFromModuleList(Rest),
+ case Rest2 of
+ [{';',_L2}|Rest3] ->
+ {{imports,SymbolsFromModuleList},Rest3};
+ Rest3 ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,';']}})
+ end;
+parse_Imports(Tokens) ->
+ {{imports,[]},Tokens}.
+
+parse_SymbolsFromModuleList(Tokens) ->
+ parse_SymbolsFromModuleList(Tokens,[]).
+
+parse_SymbolsFromModuleList(Tokens,Acc) ->
+ {SymbolsFromModule,Rest} = parse_SymbolsFromModule(Tokens),
+ case (catch parse_SymbolsFromModule(Rest)) of
+ {Sl,_Rest2} when record(Sl,'SymbolsFromModule') ->
+ parse_SymbolsFromModuleList(Rest,[SymbolsFromModule|Acc]);
+ _ ->
+ {lists:reverse([SymbolsFromModule|Acc]),Rest}
+ end.
+
+parse_SymbolsFromModule(Tokens) ->
+ SetRefModuleName =
+ fun(N) ->
+ fun(X) when record(X,'Externaltypereference')->
+ X#'Externaltypereference'{module=N};
+ (X) when record(X,'Externalvaluereference')->
+ X#'Externalvaluereference'{module=N}
+ end
+ end,
+ {SymbolList,Rest} = parse_SymbolList(Tokens),
+ case Rest of
+ %%How does this case correspond to x.680 ?
+ [{'FROM',_L1},Tref = {typereference,_,_},Ref={identifier,_L2,_Id},C={',',_}|Rest2] ->
+ {#'SymbolsFromModule'{symbols=SymbolList,
+ module=tref2Exttref(Tref)},[Ref,C|Rest2]};
+ %%How does this case correspond to x.680 ?
+ [{'FROM',_L1},Tref = {typereference,_,_},{identifier,_L2,_Id}|Rest2] ->
+ {#'SymbolsFromModule'{symbols=SymbolList,
+ module=tref2Exttref(Tref)},Rest2};
+ [{'FROM',_L1},Tref = {typereference,_,Name},Brace = {'{',_}|Rest2] ->
+ {_ObjIdVal,Rest3} = parse_ObjectIdentifierValue([Brace|Rest2]), % value not used yet, fix me
+ NewSymbolList = lists:map(SetRefModuleName(Name),SymbolList),
+ {#'SymbolsFromModule'{symbols=NewSymbolList,
+ module=tref2Exttref(Tref)},Rest3};
+ [{'FROM',_L1},Tref = {typereference,_,Name}|Rest2] ->
+ NewSymbolList = lists:map(SetRefModuleName(Name),SymbolList),
+ {#'SymbolsFromModule'{symbols=NewSymbolList,
+ module=tref2Exttref(Tref)},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,
+ ['FROM typerefernece identifier ,',
+ 'FROM typereference identifier',
+ 'FROM typereference {',
+ 'FROM typereference']]}})
+ end.
+
+parse_ObjectIdentifierValue([{'{',_}|Rest]) ->
+ parse_ObjectIdentifierValue(Rest,[]).
+
+parse_ObjectIdentifierValue([{number,_,Num}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[Num|Acc]);
+parse_ObjectIdentifierValue([{identifier,_,Id},{'(',_}, {number,_,Num}, {')',_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[{'NamedNumber',Id,Num}|Acc]);
+parse_ObjectIdentifierValue([{identifier,_,Id},{'(',_}, {identifier,_,Id2}, {')',_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[{'NamedNumber',Id,Id2}|Acc]);
+parse_ObjectIdentifierValue([{identifier,_,Id},{'(',_}, {typereference,_,Tref},{'.',_},{identifier,_,Id2}, {')',_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[{'NamedNumber',Id,{'ExternalValue',Tref,Id2}}|Acc]);
+parse_ObjectIdentifierValue([Id = {identifier,_,_}|Rest],Acc) ->
+ parse_ObjectIdentifierValue(Rest,[identifier2Extvalueref(Id)|Acc]);
+parse_ObjectIdentifierValue([{'}',_}|Rest],Acc) ->
+ {lists:reverse(Acc),Rest};
+parse_ObjectIdentifierValue([H|_T],_Acc) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,
+ ['{ some of the following }',number,'identifier ( number )',
+ 'identifier ( identifier )',
+ 'identifier ( typereference.identifier)',identifier]]}}).
+
+parse_AssignmentList(Tokens = [{'END',_}|_Rest]) ->
+ {[],Tokens};
+parse_AssignmentList(Tokens = [{'$end',_}|_Rest]) ->
+ {[],Tokens};
+parse_AssignmentList(Tokens) ->
+ parse_AssignmentList(Tokens,[]).
+
+parse_AssignmentList(Tokens= [{'END',_}|_Rest],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AssignmentList(Tokens= [{'$end',_}|_Rest],Acc) ->
+ {lists:reverse(Acc),Tokens};
+parse_AssignmentList(Tokens,Acc) ->
+ case (catch parse_Assignment(Tokens)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,R} ->
+% [H|T] = Tokens,
+ throw({error,{R,hd(Tokens)}});
+ {Assignment,Rest} ->
+ parse_AssignmentList(Rest,[Assignment|Acc])
+ end.
+
+parse_Assignment(Tokens) ->
+ Flist = [fun parse_TypeAssignment/1,
+ fun parse_ValueAssignment/1,
+ fun parse_ObjectClassAssignment/1,
+ fun parse_ObjectAssignment/1,
+ fun parse_ObjectSetAssignment/1,
+ fun parse_ParameterizedAssignment/1,
+ fun parse_ValueSetTypeAssignment/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {asn1_assignment_error,Reason} ->
+ throw({asn1_error,Reason});
+ Result ->
+ Result
+ end.
+
+
+parse_or(Tokens,Flist) ->
+ parse_or(Tokens,Flist,[]).
+
+parse_or(_Tokens,[],ErrList) ->
+ case ErrList of
+ [] ->
+ throw({asn1_error,{parse_or,ErrList}});
+ L when list(L) ->
+%%% throw({asn1_error,{parse_or,hd(lists:reverse(ErrList))}});
+ %% chose to throw 1) the error with the highest line no,
+ %% 2) the last error which is not a asn1_assignment_error or
+ %% 3) the last error.
+ throw(prioritize_error(ErrList));
+ Other ->
+ throw({asn1_error,{parse_or,Other}})
+ end;
+parse_or(Tokens,[Fun|Frest],ErrList) ->
+ case (catch Fun(Tokens)) of
+ Exit = {'EXIT',_Reason} ->
+ parse_or(Tokens,Frest,[Exit|ErrList]);
+ AsnErr = {asn1_error,_} ->
+ parse_or(Tokens,Frest,[AsnErr|ErrList]);
+ AsnAssErr = {asn1_assignment_error,_} ->
+ parse_or(Tokens,Frest,[AsnAssErr|ErrList]);
+ Result = {_,L} when list(L) ->
+ Result;
+% Result ->
+% Result
+ Error ->
+ parse_or(Tokens,Frest,[Error|ErrList])
+ end.
+
+parse_TypeAssignment([{typereference,L1,Tref},{'::=',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#typedef{pos=L1,name=Tref,typespec=Type},Rest2};
+parse_TypeAssignment([H1,H2|_Rest]) ->
+ throw({asn1_assignment_error,{get_line(H1),get(asn1_module),
+ [got,[get_token(H1),get_token(H2)], expected,
+ typereference,'::=']}});
+parse_TypeAssignment([H|_T]) ->
+ throw({asn1_assignment_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,
+ typereference]}}).
+
+parse_Type(Tokens) ->
+ {Tag,Rest3} = case Tokens of
+ [Lbr= {'[',_}|Rest] ->
+ parse_Tag([Lbr|Rest]);
+ Rest-> {[],Rest}
+ end,
+ {Tag2,Rest4} = case Rest3 of
+ [{'IMPLICIT',_}|Rest31] when record(Tag,tag)->
+ {[Tag#tag{type='IMPLICIT'}],Rest31};
+ [{'EXPLICIT',_}|Rest31] when record(Tag,tag)->
+ {[Tag#tag{type='EXPLICIT'}],Rest31};
+ Rest31 when record(Tag,tag) ->
+ {[Tag#tag{type={default,get(tagdefault)}}],Rest31};
+ Rest31 ->
+ {Tag,Rest31}
+ end,
+ Flist = [fun parse_BuiltinType/1,fun parse_ReferencedType/1,fun parse_TypeWithConstraint/1],
+ {Type,Rest5} = case (catch parse_or(Rest4,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_Reason} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end,
+ case hd(Rest5) of
+ {'(',_} ->
+ {Constraints,Rest6} = parse_Constraints(Rest5),
+ if record(Type,type) ->
+ {Type#type{constraint=merge_constraints(Constraints),
+ tag=Tag2},Rest6};
+ true ->
+ {#type{def=Type,constraint=merge_constraints(Constraints),
+ tag=Tag2},Rest6}
+ end;
+ _ ->
+ if record(Type,type) ->
+ {Type#type{tag=Tag2},Rest5};
+ true ->
+ {#type{def=Type,tag=Tag2},Rest5}
+ end
+ end.
+
+parse_BuiltinType([{'BIT',_},{'STRING',_}|Rest]) ->
+ case Rest of
+ [{'{',_}|Rest2] ->
+ {NamedNumberList,Rest3} = parse_NamedNumberList(Rest2),
+ case Rest3 of
+ [{'}',_}|Rest4] ->
+ {#type{def={'BIT STRING',NamedNumberList}},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,'}']}})
+ end;
+ _ ->
+ {{'BIT STRING',[]},Rest}
+ end;
+parse_BuiltinType([{'BOOLEAN',_}|Rest]) ->
+ {#type{def='BOOLEAN'},Rest};
+%% CharacterStringType ::= RestrictedCharacterStringType |
+%% UnrestrictedCharacterStringType
+parse_BuiltinType([{restrictedcharacterstringtype,_,StringName}|Rest]) ->
+ {#type{def=StringName},Rest};
+parse_BuiltinType([{'CHARACTER',_},{'STRING',_}|Rest]) ->
+ {#type{def='CHARACTER STRING'},Rest};
+
+parse_BuiltinType([{'CHOICE',_},{'{',_}|Rest]) ->
+ {AlternativeTypeLists,Rest2} = parse_AlternativeTypeLists(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def={'CHOICE',AlternativeTypeLists}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'EMBEDDED',_},{'PDV',_}|Rest]) ->
+ {#type{def='EMBEDDED PDV'},Rest};
+parse_BuiltinType([{'ENUMERATED',_},{'{',_}|Rest]) ->
+ {Enumerations,Rest2} = parse_Enumerations(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def={'ENUMERATED',Enumerations}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'EXTERNAL',_}|Rest]) ->
+ {#type{def='EXTERNAL'},Rest};
+
+% InstanceOfType
+parse_BuiltinType([{'INSTANCE',_},{'OF',_}|Rest]) ->
+ {DefinedObjectClass,Rest2} = parse_DefinedObjectClass(Rest),
+ case Rest2 of
+ [{'(',_}|_] ->
+ {Constraint,Rest3} = parse_Constraint(Rest2),
+ {#type{def={'INSTANCE OF',DefinedObjectClass,Constraint}},Rest3};
+ _ ->
+ {#type{def={'INSTANCE OF',DefinedObjectClass,[]}},Rest2}
+ end;
+
+% parse_BuiltinType(Tokens) ->
+
+parse_BuiltinType([{'INTEGER',_}|Rest]) ->
+ case Rest of
+ [{'{',_}|Rest2] ->
+ {NamedNumberList,Rest3} = parse_NamedNumberList(Rest2),
+ case Rest3 of
+ [{'}',_}|Rest4] ->
+ {#type{def={'INTEGER',NamedNumberList}},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,'}']}})
+ end;
+ _ ->
+ {#type{def='INTEGER'},Rest}
+ end;
+parse_BuiltinType([{'NULL',_}|Rest]) ->
+ {#type{def='NULL'},Rest};
+
+% ObjectClassFieldType fix me later
+
+parse_BuiltinType([{'OBJECT',_},{'IDENTIFIER',_}|Rest]) ->
+ {#type{def='OBJECT IDENTIFIER'},Rest};
+parse_BuiltinType([{'OCTET',_},{'STRING',_}|Rest]) ->
+ {#type{def='OCTET STRING'},Rest};
+parse_BuiltinType([{'REAL',_}|Rest]) ->
+ {#type{def='REAL'},Rest};
+parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
+ {#type{def=#'SEQUENCE'{components=[{'EXTENSIONMARK',Line,undefined}]}},
+ Rest};
+parse_BuiltinType([{'SEQUENCE',_},{'{',_},{'...',Line},{'!',_}|Rest]) ->
+ {ExceptionIdentification,Rest2} = parse_ExceptionIdentification(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SEQUENCE'{components=[{'EXTENSIONMARK',
+ Line,
+ ExceptionIdentification}]}},
+ Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SEQUENCE',_},{'{',_}|Rest]) ->
+ {ComponentTypeLists,Rest2} = parse_ComponentTypeLists(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SEQUENCE'{components=ComponentTypeLists}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SEQUENCE',_},{'OF',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#type{def={'SEQUENCE OF',Type}},Rest2};
+
+
+parse_BuiltinType([{'SET',_},{'{',_},{'...',Line},{'}',_}|Rest]) ->
+ {#type{def=#'SET'{components=[{'EXTENSIONMARK',Line,undefined}]}},Rest};
+parse_BuiltinType([{'SET',_},{'{',_},{'...',Line},{'!',_}|Rest]) ->
+ {ExceptionIdentification,Rest2} = parse_ExceptionIdentification(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SET'{components=
+ [{'EXTENSIONMARK',Line,ExceptionIdentification}]}},
+ Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SET',_},{'{',_}|Rest]) ->
+ {ComponentTypeLists,Rest2} = parse_ComponentTypeLists(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {#type{def=#'SET'{components=ComponentTypeLists}},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_BuiltinType([{'SET',_},{'OF',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#type{def={'SET OF',Type}},Rest2};
+
+%% The so called Useful types
+parse_BuiltinType([{'GeneralizedTime',_}|Rest]) ->
+ {#type{def='GeneralizedTime'},Rest};
+parse_BuiltinType([{'UTCTime',_}|Rest]) ->
+ {#type{def='UTCTime'},Rest};
+parse_BuiltinType([{'ObjectDescriptor',_}|Rest]) ->
+ {#type{def='ObjectDescriptor'},Rest};
+
+%% For compatibility with old standard
+parse_BuiltinType([{'ANY',_},{'DEFINED',_},{'BY',_},{identifier,_,Id}|Rest]) ->
+ {#type{def={'ANY_DEFINED_BY',Id}},Rest};
+parse_BuiltinType([{'ANY',_}|Rest]) ->
+ {#type{def='ANY'},Rest};
+
+parse_BuiltinType(Tokens) ->
+ parse_ObjectClassFieldType(Tokens).
+% throw({asn1_error,unhandled_type}).
+
+
+parse_TypeWithConstraint([{'SEQUENCE',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SEQUENCE OF',Type}, constraint = merge_constraints([Constraint])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint([{'SEQUENCE',_},{'SIZE',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ Constraint2 =
+ case Constraint of
+ #constraint{c=C} ->
+ Constraint#constraint{c={'SizeConstraint',C}};
+ _ -> Constraint
+ end,
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SEQUENCE OF',Type}, constraint = merge_constraints([Constraint2])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint([{'SET',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SET OF',Type}, constraint = merge_constraints([Constraint])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint([{'SET',_},{'SIZE',_},Lpar = {'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_Constraint([Lpar|Rest]),
+ Constraint2 =
+ case Constraint of
+ #constraint{c=C} ->
+ Constraint#constraint{c={'SizeConstraint',C}};
+ _ -> Constraint
+ end,
+ case Rest2 of
+ [{'OF',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#type{def = {'SET OF',Type}, constraint = merge_constraints([Constraint2])},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'OF']}})
+ end;
+parse_TypeWithConstraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['SEQUENCE','SEQUENCE SIZE','SET','SET SIZE'],
+ followed,by,a,constraint]}}).
+
+
+%% --------------------------
+
+parse_ReferencedType(Tokens) ->
+ Flist = [fun parse_DefinedType/1,
+ fun parse_SelectionType/1,
+ fun parse_TypeFromObject/1,
+ fun parse_ValueSetFromObjects/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_DefinedType(Tokens=[{typereference,_,_},{'{',_}|_Rest]) ->
+ parse_ParameterizedType(Tokens);
+parse_DefinedType(Tokens=[{typereference,L1,TypeName},
+ T2={typereference,_,_},T3={'{',_}|Rest]) ->
+ case (catch parse_ParameterizedType(Tokens)) of
+ {'EXIT',_Reason} ->
+ Rest2 = [T2,T3|Rest],
+ {#type{def = #'Externaltypereference'{pos=L1,
+ module=get(asn1_module),
+ type=TypeName}},Rest2};
+ {asn1_error,_} ->
+ Rest2 = [T2,T3|Rest],
+ {#type{def = #'Externaltypereference'{pos=L1,
+ module=get(asn1_module),
+ type=TypeName}},Rest2};
+ Result ->
+ Result
+ end;
+parse_DefinedType([{typereference,L1,Module},{'.',_},{typereference,_,TypeName}|Rest]) ->
+ {#type{def = #'Externaltypereference'{pos=L1,module=Module,type=TypeName}},Rest};
+parse_DefinedType([{typereference,L1,TypeName}|Rest]) ->
+ {#type{def = #'Externaltypereference'{pos=L1,module=get(asn1_module),
+ type=TypeName}},Rest};
+parse_DefinedType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,'typereference.typereference',
+ 'typereference typereference']]}}).
+
+parse_SelectionType([{identifier,_,Name},{'<',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'SelectionType',Name,Type},Rest2};
+parse_SelectionType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'identifier <']}}).
+
+
+%% --------------------------
+
+
+%% This should probably be removed very soon
+% parse_ConstrainedType(Tokens) ->
+% case (catch parse_TypeWithConstraint(Tokens)) of
+% {'EXIT',Reason} ->
+% {Type,Rest} = parse_Type(Tokens),
+% {Constraint,Rest2} = parse_Constraint(Rest),
+% {Type#type{constraint=Constraint},Rest2};
+% {asn1_error,Reason2} ->
+% {Type,Rest} = parse_Type(Tokens),
+% {Constraint,Rest2} = parse_Constraint(Rest),
+% {Type#type{constraint=Constraint},Rest2};
+% Result ->
+% Result
+% end.
+
+parse_Constraints(Tokens) ->
+ parse_Constraints(Tokens,[]).
+
+parse_Constraints(Tokens,Acc) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ case Rest of
+ [{'(',_}|_Rest2] ->
+ parse_Constraints(Rest,[Constraint|Acc]);
+ _ ->
+ {lists:reverse([Constraint|Acc]),Rest}
+ end.
+
+parse_Constraint([{'(',_}|Rest]) ->
+ {Constraint,Rest2} = parse_ConstraintSpec(Rest),
+ {Exception,Rest3} = parse_ExceptionSpec(Rest2),
+ case Rest3 of
+ [{')',_}|Rest4] ->
+ {#constraint{c=Constraint,e=Exception},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,')']}})
+ end;
+parse_Constraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'(']}}).
+
+parse_ConstraintSpec(Tokens) ->
+ Flist = [fun parse_GeneralConstraint/1,
+ fun parse_SubtypeConstraint/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason2} ->
+ throw({asn1_error,Reason2});
+ Result ->
+ Result
+ end.
+
+parse_ExceptionSpec([LPar={')',_}|Rest]) ->
+ {undefined,[LPar|Rest]};
+parse_ExceptionSpec([{'!',_}|Rest]) ->
+ parse_ExceptionIdentification(Rest);
+parse_ExceptionSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,[')','!']]}}).
+
+parse_ExceptionIdentification(Tokens) ->
+ Flist = [fun parse_SignedNumber/1,
+ fun parse_DefinedValue/1,
+ fun parse_TypeColonValue/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason2} ->
+ throw({asn1_error,Reason2});
+ Result ->
+ Result
+ end.
+
+parse_TypeColonValue(Tokens) ->
+ {Type,Rest} = parse_Type(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Value,Rest3} = parse_Value(Rest2),
+ {{Type,Value},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+parse_SubtypeConstraint(Tokens) ->
+ parse_ElementSetSpecs(Tokens).
+
+parse_ElementSetSpecs([{'...',_}|Rest]) ->
+ {Elements,Rest2} = parse_ElementSetSpec(Rest),
+ {{[],Elements},Rest2};
+parse_ElementSetSpecs(Tokens) ->
+ {RootElems,Rest} = parse_ElementSetSpec(Tokens),
+ case Rest of
+ [{',',_},{'...',_},{',',_}|Rest2] ->
+ {AdditionalElems,Rest3} = parse_ElementSetSpec(Rest2),
+ {{RootElems,AdditionalElems},Rest3};
+ [{',',_},{'...',_}|Rest2] ->
+ {{RootElems,[]},Rest2};
+ _ ->
+ {RootElems,Rest}
+ end.
+
+parse_ElementSetSpec([{'ALL',_},{'EXCEPT',_}|Rest]) ->
+ {Exclusions,Rest2} = parse_Elements(Rest),
+ {{'ALL',{'EXCEPT',Exclusions}},Rest2};
+parse_ElementSetSpec(Tokens) ->
+ parse_Unions(Tokens).
+
+
+parse_Unions(Tokens) ->
+ {InterSec,Rest} = parse_Intersections(Tokens),
+ {Unions,Rest2} = parse_UnionsRec(Rest),
+ case {InterSec,Unions} of
+ {InterSec,[]} ->
+ {InterSec,Rest2};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest2};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ [union|V2],Rest2};
+ {V1,V2} ->
+ {[V1,union,V2],Rest2}
+% Other ->
+% throw(Other)
+ end.
+
+parse_UnionsRec([{'|',_}|Rest]) ->
+ {InterSec,Rest2} = parse_Intersections(Rest),
+ {URec,Rest3} = parse_UnionsRec(Rest2),
+ case {InterSec,URec} of
+ {V1,[]} ->
+ {V1,Rest3};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3}
+ end;
+parse_UnionsRec([{'UNION',_}|Rest]) ->
+ {InterSec,Rest2} = parse_Intersections(Rest),
+ {URec,Rest3} = parse_UnionsRec(Rest2),
+ case {InterSec,URec} of
+ {V1,[]} ->
+ {V1,Rest3};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',ordsets:union(to_set(V1),to_set(V2))},Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3}
+ end;
+parse_UnionsRec(Tokens) ->
+ {[],Tokens}.
+
+parse_Intersections(Tokens) ->
+ {InterSec,Rest} = parse_IntersectionElements(Tokens),
+ {IRec,Rest2} = parse_IElemsRec(Rest),
+ case {InterSec,IRec} of
+ {V1,[]} ->
+ {V1,Rest2};
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',
+ ordsets:intersection(to_set(V1),to_set(V2))},Rest2};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ [intersection|V2],Rest2};
+ {V1,V2} ->
+ {[V1,intersection,V2],Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'a Union']}})
+ end.
+
+parse_IElemsRec([{'^',_}|Rest]) ->
+ {InterSec,Rest2} = parse_IntersectionElements(Rest),
+ {IRec,Rest3} = parse_IElemsRec(Rest2),
+ case {InterSec,IRec} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',
+ ordsets:intersection(to_set(V1),to_set(V2))},Rest3};
+ {V1,[]} ->
+ {V1,Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'an Intersection']}})
+ end;
+parse_IElemsRec([{'INTERSECTION',_}|Rest]) ->
+ {InterSec,Rest2} = parse_IntersectionElements(Rest),
+ {IRec,Rest3} = parse_IElemsRec(Rest2),
+ case {InterSec,IRec} of
+ {{'SingleValue',V1},{'SingleValue',V2}} ->
+ {{'SingleValue',
+ ordsets:intersection(to_set(V1),to_set(V2))},Rest3};
+ {V1,[]} ->
+ {V1,Rest3};
+ {V1,V2} when list(V2) ->
+ {[V1] ++ V2,Rest3};
+ {V1,V2} ->
+ {[V1,V2],Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'an Intersection']}})
+ end;
+parse_IElemsRec(Tokens) ->
+ {[],Tokens}.
+
+parse_IntersectionElements(Tokens) ->
+ {InterSec,Rest} = parse_Elements(Tokens),
+ case Rest of
+ [{'EXCEPT',_}|Rest2] ->
+ {Exclusion,Rest3} = parse_Elements(Rest2),
+ {{InterSec,{'EXCEPT',Exclusion}},Rest3};
+ Rest ->
+ {InterSec,Rest}
+ end.
+
+parse_Elements([{'(',_}|Rest]) ->
+ {Elems,Rest2} = parse_ElementSetSpec(Rest),
+ case Rest2 of
+ [{')',_}|Rest3] ->
+ {Elems,Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,')']}})
+ end;
+parse_Elements(Tokens) ->
+ Flist = [fun parse_SubtypeElements/1,
+ fun parse_ObjectSetElements/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ Err = {asn1_error,_} ->
+ throw(Err);
+ Result ->
+ Result
+ end.
+
+
+
+
+%% --------------------------
+
+parse_DefinedObjectClass([{typereference,_,_ModName},{'.',_},Tr={typereference,_,_ObjClName}|Rest]) ->
+%% {{objectclassname,ModName,ObjClName},Rest};
+% {{objectclassname,tref2Exttref(Tr)},Rest};
+ {tref2Exttref(Tr),Rest};
+parse_DefinedObjectClass([Tr={typereference,_,_ObjClName}|Rest]) ->
+% {{objectclassname,tref2Exttref(Tr)},Rest};
+ {tref2Exttref(Tr),Rest};
+parse_DefinedObjectClass([{'TYPE-IDENTIFIER',_}|Rest]) ->
+ {'TYPE-IDENTIFIER',Rest};
+parse_DefinedObjectClass([{'ABSTRACT-SYNTAX',_}|Rest]) ->
+ {'ABSTRACT-SYNTAX',Rest};
+parse_DefinedObjectClass(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['typereference . typereference',
+ typereference,
+ 'TYPE-IDENTIFIER',
+ 'ABSTRACT-SYNTAX']]}}).
+
+parse_ObjectClassAssignment([{typereference,L1,ObjClName},{'::=',_}|Rest]) ->
+ {Type,Rest2} = parse_ObjectClass(Rest),
+ {#classdef{pos=L1,name=ObjClName,typespec=Type},Rest2};
+parse_ObjectClassAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ 'typereference ::=']}}).
+
+parse_ObjectClass(Tokens) ->
+ Flist = [fun parse_DefinedObjectClass/1,
+ fun parse_ObjectClassDefn/1,
+ fun parse_ParameterizedObjectClass/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason2} ->
+ throw({asn1_error,Reason2});
+ Result ->
+ Result
+ end.
+
+parse_ObjectClassDefn([{'CLASS',_},{'{',_}|Rest]) ->
+ {Type,Rest2} = parse_FieldSpec(Rest),
+ {WithSyntaxSpec,Rest3} = parse_WithSyntaxSpec(Rest2),
+ {#objectclass{fields=Type,syntax=WithSyntaxSpec},Rest3};
+parse_ObjectClassDefn(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'CLASS {']}}).
+
+parse_FieldSpec(Tokens) ->
+ parse_FieldSpec(Tokens,[]).
+
+parse_FieldSpec(Tokens,Acc) ->
+ Flist = [fun parse_FixedTypeValueFieldSpec/1,
+ fun parse_VariableTypeValueFieldSpec/1,
+ fun parse_ObjectFieldSpec/1,
+ fun parse_FixedTypeValueSetFieldSpec/1,
+ fun parse_VariableTypeValueSetFieldSpec/1,
+ fun parse_TypeFieldSpec/1,
+ fun parse_ObjectSetFieldSpec/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {Type,[{'}',_}|Rest]} ->
+ {lists:reverse([Type|Acc]),Rest};
+ {Type,[{',',_}|Rest2]} ->
+ parse_FieldSpec(Rest2,[Type|Acc]);
+ {_,[H|_T]} ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end.
+
+parse_PrimitiveFieldName([{typefieldreference,_,FieldName}|Rest]) ->
+ {{typefieldreference,FieldName},Rest};
+parse_PrimitiveFieldName([{valuefieldreference,_,FieldName}|Rest]) ->
+ {{valuefieldreference,FieldName},Rest};
+parse_PrimitiveFieldName(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typefieldreference,valuefieldreference]]}}).
+
+parse_FieldName(Tokens) ->
+ {Field,Rest} = parse_PrimitiveFieldName(Tokens),
+ parse_FieldName(Rest,[Field]).
+
+parse_FieldName([{'.',_}|Rest],Acc) ->
+ case (catch parse_PrimitiveFieldName(Rest)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {FieldName,Rest2} ->
+ parse_FieldName(Rest2,[FieldName|Acc])
+ end;
+parse_FieldName(Tokens,Acc) ->
+ {lists:reverse(Acc),Tokens}.
+
+parse_FixedTypeValueFieldSpec([{valuefieldreference,L1,VFieldName}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {Unique,Rest3} =
+ case Rest2 of
+ [{'UNIQUE',_}|Rest4] ->
+ {'UNIQUE',Rest4};
+ _ ->
+ {undefined,Rest2}
+ end,
+ {OptionalitySpec,Rest5} = parse_ValueOptionalitySpec(Rest3),
+ case Unique of
+ 'UNIQUE' ->
+ case OptionalitySpec of
+ {'DEFAULT',_} ->
+ throw({asn1_error,
+ {L1,get(asn1_module),
+ ['UNIQUE and DEFAULT in same field',VFieldName]}});
+ _ ->
+ {{fixedtypevaluefield,VFieldName,Type,Unique,OptionalitySpec},Rest5}
+ end;
+ _ ->
+ {{object_or_fixedtypevalue_field,VFieldName,Type,Unique,OptionalitySpec},Rest5}
+ end;
+parse_FixedTypeValueFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,valuefieldreference]}}).
+
+parse_VariableTypeValueFieldSpec([{valuefieldreference,_,VFieldName}|Rest]) ->
+ {FieldRef,Rest2} = parse_FieldName(Rest),
+ {OptionalitySpec,Rest3} = parse_ValueOptionalitySpec(Rest2),
+ {{variabletypevaluefield,VFieldName,FieldRef,OptionalitySpec},Rest3};
+parse_VariableTypeValueFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,valuefieldreference]}}).
+
+parse_ObjectFieldSpec([{valuefieldreference,_,VFieldName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ {OptionalitySpec,Rest3} = parse_ObjectOptionalitySpec(Rest2),
+ {{objectfield,VFieldName,Class,OptionalitySpec},Rest3};
+parse_ObjectFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,valuefieldreference]}}).
+
+parse_TypeFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {OptionalitySpec,Rest2} = parse_TypeOptionalitySpec(Rest),
+ {{typefield,TFieldName,OptionalitySpec},Rest2};
+parse_TypeFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_FixedTypeValueSetFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {OptionalitySpec,Rest3} = parse_ValueSetOptionalitySpec(Rest2),
+ {{objectset_or_fixedtypevalueset_field,TFieldName,Type,
+ OptionalitySpec},Rest3};
+parse_FixedTypeValueSetFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_VariableTypeValueSetFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {FieldRef,Rest2} = parse_FieldName(Rest),
+ {OptionalitySpec,Rest3} = parse_ValueSetOptionalitySpec(Rest2),
+ {{variabletypevaluesetfield,TFieldName,FieldRef,OptionalitySpec},Rest3};
+parse_VariableTypeValueSetFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_ObjectSetFieldSpec([{typefieldreference,_,TFieldName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ {OptionalitySpec,Rest3} = parse_ObjectSetOptionalitySpec(Rest2),
+ {{objectsetfield,TFieldName,Class,OptionalitySpec},Rest3};
+parse_ObjectSetFieldSpec(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,typefieldreference]}}).
+
+parse_ValueOptionalitySpec(Tokens)->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {Value,Rest2} = parse_Value(Rest),
+ {{'DEFAULT',Value},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_ObjectOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {Object,Rest2} = parse_Object(Rest),
+ {{'DEFAULT',Object},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_TypeOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'DEFAULT',Type},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_ValueSetOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {ValueSet,Rest2} = parse_ValueSet(Rest),
+ {{'DEFAULT',ValueSet},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_ObjectSetOptionalitySpec(Tokens) ->
+ case Tokens of
+ [{'OPTIONAL',_}|Rest] -> {'OPTIONAL',Rest};
+ [{'DEFAULT',_}|Rest] ->
+ {ObjectSet,Rest2} = parse_ObjectSet(Rest),
+ {{'DEFAULT',ObjectSet},Rest2};
+ _ -> {'MANDATORY',Tokens}
+ end.
+
+parse_WithSyntaxSpec([{'WITH',_},{'SYNTAX',_}|Rest]) ->
+ {SyntaxList,Rest2} = parse_SyntaxList(Rest),
+ {{'WITH SYNTAX',SyntaxList},Rest2};
+parse_WithSyntaxSpec(Tokens) ->
+ {[],Tokens}.
+
+parse_SyntaxList([{'{',_},{'}',_}|Rest]) ->
+ {[],Rest};
+parse_SyntaxList([{'{',_}|Rest]) ->
+ parse_SyntaxList(Rest,[]);
+parse_SyntaxList(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,['{}','{']]}}).
+
+parse_SyntaxList(Tokens,Acc) ->
+ {SyntaxList,Rest} = parse_TokenOrGroupSpec(Tokens),
+ case Rest of
+ [{'}',_}|Rest2] ->
+ {lists:reverse([SyntaxList|Acc]),Rest2};
+ _ ->
+ parse_SyntaxList(Rest,[SyntaxList|Acc])
+ end.
+
+parse_TokenOrGroupSpec(Tokens) ->
+ Flist = [fun parse_RequiredToken/1,
+ fun parse_OptionalGroup/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_RequiredToken([{typereference,L1,WordName}|Rest]) ->
+ case is_word(WordName) of
+ false ->
+ throw({asn1_error,{L1,get(asn1_module),
+ [got,WordName,expected,a,'Word']}});
+ true ->
+ {WordName,Rest}
+ end;
+parse_RequiredToken([{',',L1}|Rest]) ->
+ {{',',L1},Rest};
+parse_RequiredToken([{WordName,L1}|Rest]) ->
+ case is_word(WordName) of
+ false ->
+ throw({asn1_error,{L1,get(asn1_module),
+ [got,WordName,expected,a,'Word']}});
+ true ->
+ {WordName,Rest}
+ end;
+parse_RequiredToken(Tokens) ->
+ parse_PrimitiveFieldName(Tokens).
+
+parse_OptionalGroup([{'[',_}|Rest]) ->
+ {Spec,Rest2} = parse_TokenOrGroupSpec(Rest),
+ {SpecList,Rest3} = parse_OptionalGroup(Rest2,[Spec]),
+ {SpecList,Rest3}.
+
+parse_OptionalGroup([{']',_}|Rest],Acc) ->
+ {lists:reverse(Acc),Rest};
+parse_OptionalGroup(Tokens,Acc) ->
+ {Spec,Rest} = parse_TokenOrGroupSpec(Tokens),
+ parse_OptionalGroup(Rest,[Spec|Acc]).
+
+parse_DefinedObject([Id={identifier,_,_ObjName}|Rest]) ->
+ {{object,identifier2Extvalueref(Id)},Rest};
+parse_DefinedObject([{typereference,L1,ModName},{'.',_},{identifier,_,ObjName}|Rest]) ->
+ {{object, #'Externaltypereference'{pos=L1,module=ModName,type=ObjName}},Rest};
+parse_DefinedObject(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [identifier,'typereference.identifier']]}}).
+
+parse_ObjectAssignment([{identifier,L1,ObjName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Object,Rest4} = parse_Object(Rest3),
+ {#typedef{pos=L1,name=ObjName,
+ typespec=#'Object'{classname=Class,def=Object}},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}});
+ Other ->
+ throw({asn1_error,{L1,get(asn1_module),
+ [got,Other,expected,'::=']}})
+ end;
+parse_ObjectAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_Object(Tokens) ->
+ Flist=[fun parse_ObjectDefn/1,
+ fun parse_ObjectFromObject/1,
+ fun parse_ParameterizedObject/1,
+ fun parse_DefinedObject/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ObjectDefn(Tokens) ->
+ Flist=[fun parse_DefaultSyntax/1,
+ fun parse_DefinedSyntax/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_DefaultSyntax([{'{',_},{'}',_}|Rest]) ->
+ {{object,defaultsyntax,[]},Rest};
+parse_DefaultSyntax([{'{',_}|Rest]) ->
+ parse_DefaultSyntax(Rest,[]);
+parse_DefaultSyntax(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,['{}','{']]}}).
+
+parse_DefaultSyntax(Tokens,Acc) ->
+ {Setting,Rest} = parse_FieldSetting(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_DefaultSyntax(Rest2,[Setting|Acc]);
+ [{'}',_}|Rest3] ->
+ {{object,defaultsyntax,lists:reverse([Setting|Acc])},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,[',','}']]}})
+ end.
+
+parse_FieldSetting(Tokens) ->
+ {{_,PrimFieldName},Rest} = parse_PrimitiveFieldName(Tokens),
+ {Setting,Rest2} = parse_Setting(Rest),
+ {{PrimFieldName,Setting},Rest2}.
+
+parse_DefinedSyntax([{'{',_}|Rest]) ->
+ parse_DefinedSyntax(Rest,[]).
+
+parse_DefinedSyntax(Tokens,Acc) ->
+ case Tokens of
+ [{'}',_}|Rest2] ->
+ {{object,definedsyntax,lists:reverse(Acc)},Rest2};
+ _ ->
+ {DefSynTok,Rest3} = parse_DefinedSyntaxToken(Tokens),
+ parse_DefinedSyntax(Rest3,[DefSynTok|Acc])
+ end.
+
+parse_DefinedSyntaxToken([{',',L1}|Rest]) ->
+ {{',',L1},Rest};
+parse_DefinedSyntaxToken([{typereference,L1,Name}|Rest]) ->
+ case is_word(Name) of
+ false ->
+ {{setting,L1,Name},Rest};
+ true ->
+ {{word_or_setting,L1,Name},Rest}
+ end;
+parse_DefinedSyntaxToken(Tokens) ->
+ case catch parse_Setting(Tokens) of
+ {asn1_error,_} ->
+ parse_Word(Tokens);
+ {'EXIT',Reason} ->
+ exit(Reason);
+ Result ->
+ Result
+ end.
+
+parse_Word([{Name,Pos}|Rest]) ->
+ case is_word(Name) of
+ false ->
+ throw({asn1_error,{Pos,get(asn1_module),
+ [got,Name, expected,a,'Word']}});
+ true ->
+ {{word_or_setting,Pos,Name},Rest}
+ end.
+
+parse_Setting(Tokens) ->
+ Flist = [fun parse_Type/1,
+ fun parse_Value/1,
+ fun parse_Object/1,
+ fun parse_ObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_DefinedObjectSet([{typereference,L1,ModuleName},{'.',_},
+ {typereference,L2,ObjSetName}|Rest]) ->
+ {{objectset,L1,#'Externaltypereference'{pos=L2,module=ModuleName,
+ type=ObjSetName}},Rest};
+parse_DefinedObjectSet([{typereference,L1,ObjSetName}|Rest]) ->
+ {{objectset,L1,#'Externaltypereference'{pos=L1,module=get(asn1_module),
+ type=ObjSetName}},Rest};
+parse_DefinedObjectSet(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,'typereference.typereference']]}}).
+
+parse_ObjectSetAssignment([{typereference,L1,ObjSetName}|Rest]) ->
+ {Class,Rest2} = parse_DefinedObjectClass(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {ObjectSet,Rest4} = parse_ObjectSet(Rest3),
+ {#typedef{pos=L1,name=ObjSetName,
+ typespec=#'ObjectSet'{class=Class,
+ set=ObjectSet}},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ObjectSetAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ObjectSet([{'{',_}|Rest]) ->
+ {ObjSetSpec,Rest2} = parse_ObjectSetSpec(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {ObjSetSpec,Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+parse_ObjectSet(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ObjectSetSpec([{'...',_}|Rest]) ->
+ {['EXTENSIONMARK'],Rest};
+parse_ObjectSetSpec(Tokens) ->
+ parse_ElementSetSpecs(Tokens).
+
+parse_ObjectSetElements(Tokens) ->
+ Flist = [fun parse_Object/1,
+ fun parse_DefinedObjectSet/1,
+ fun parse_ObjectSetFromObjects/1,
+ fun parse_ParameterizedObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ObjectClassFieldType(Tokens) ->
+ {Class,Rest} = parse_DefinedObjectClass(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {FieldName,Rest3} = parse_FieldName(Rest2),
+ OCFT = #'ObjectClassFieldType'{
+ classname=Class,
+ class=Class,fieldname=FieldName},
+ {#type{def=OCFT},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw(Other)
+ end.
+
+%parse_ObjectClassFieldValue(Tokens) ->
+% Flist = [fun parse_OpenTypeFieldVal/1,
+% fun parse_FixedTypeFieldVal/1],
+% case (catch parse_or(Tokens,Flist)) of
+% {'EXIT',Reason} ->
+% throw(Reason);
+% AsnErr = {asn1_error,_} ->
+% throw(AsnErr);
+% Result ->
+% Result
+% end.
+
+parse_ObjectClassFieldValue(Tokens) ->
+ parse_OpenTypeFieldVal(Tokens).
+
+parse_OpenTypeFieldVal(Tokens) ->
+ {Type,Rest} = parse_Type(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Value,Rest3} = parse_Value(Rest2),
+ {{opentypefieldvalue,Type,Value},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+% parse_FixedTypeFieldVal(Tokens) ->
+% parse_Value(Tokens).
+
+% parse_InformationFromObjects(Tokens) ->
+% Flist = [fun parse_ValueFromObject/1,
+% fun parse_ValueSetFromObjects/1,
+% fun parse_TypeFromObject/1,
+% fun parse_ObjectFromObject/1],
+% case (catch parse_or(Tokens,Flist)) of
+% {'EXIT',Reason} ->
+% throw(Reason);
+% AsnErr = {asn1_error,_} ->
+% throw(AsnErr);
+% Result ->
+% Result
+% end.
+
+parse_ReferencedObjects(Tokens) ->
+ Flist = [fun parse_DefinedObject/1,
+ fun parse_DefinedObjectSet/1,
+ fun parse_ParameterizedObject/1,
+ fun parse_ParameterizedObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ValueFromObject(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ case lists:last(Name) of
+ {valuefieldreference,_} ->
+ {{'ValueFromObject',Objects,Name},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,typefieldreference,expected,
+ valuefieldreference]}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_ValueSetFromObjects(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ case lists:last(Name) of
+ {typefieldreference,_FieldName} ->
+ {{'ValueSetFromObjects',Objects,Name},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,
+ typefieldreference]}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_TypeFromObject(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ case lists:last(Name) of
+ {typefieldreference,_FieldName} ->
+ {{'TypeFromObject',Objects,Name},Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,
+ typefieldreference]}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_ObjectFromObject(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ {{'ObjectFromObject',Objects,Name},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+parse_ObjectSetFromObjects(Tokens) ->
+ {Objects,Rest} = parse_ReferencedObjects(Tokens),
+ case Rest of
+ [{'.',_}|Rest2] ->
+ {Name,Rest3} = parse_FieldName(Rest2),
+ {{'ObjectSetFromObjects',Objects,Name},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'.']}})
+%%% Other ->
+%%% throw({asn1_error,{got,Other,expected,'.'}})
+ end.
+
+% parse_InstanceOfType([{'INSTANCE',_},{'OF',_}|Rest]) ->
+% {Class,Rest2} = parse_DefinedObjectClass(Rest),
+% {{'InstanceOfType',Class},Rest2}.
+
+% parse_InstanceOfValue(Tokens) ->
+% parse_Value(Tokens).
+
+
+
+%% X.682 constraint specification
+
+parse_GeneralConstraint(Tokens) ->
+ Flist = [fun parse_UserDefinedConstraint/1,
+ fun parse_TableConstraint/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_UserDefinedConstraint([{'CONSTRAINED',_},{'BY',_},{'{',_},{'}',_}|Rest])->
+ {{constrained_by,[]},Rest};
+parse_UserDefinedConstraint([{'CONSTRAINED',_},
+ {'BY',_},
+ {'{',_}|Rest]) ->
+ {Param,Rest2} = parse_UserDefinedConstraintParameter(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {{constrained_by,Param},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+parse_UserDefinedConstraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['CONSTRAINED BY {}','CONSTRAINED BY {']]}}).
+
+parse_UserDefinedConstraintParameter(Tokens) ->
+ parse_UserDefinedConstraintParameter(Tokens,[]).
+parse_UserDefinedConstraintParameter(Tokens,Acc) ->
+ Flist = [fun parse_GovernorAndActualParameter/1,
+ fun parse_ActualParameter/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {Result,Rest} ->
+ case Rest of
+ [{',',_}|_Rest2] ->
+ parse_UserDefinedConstraintParameter(Tokens,[Result|Acc]);
+ _ ->
+ {lists:reverse([Result|Acc]),Rest}
+ end
+ end.
+
+parse_GovernorAndActualParameter(Tokens) ->
+ {Governor,Rest} = parse_Governor(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Params,Rest3} = parse_ActualParameter(Rest2),
+ {{'Governor_Params',Governor,Params},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+parse_TableConstraint(Tokens) ->
+ Flist = [fun parse_ComponentRelationConstraint/1,
+ fun parse_SimpleTableConstraint/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_SimpleTableConstraint(Tokens) ->
+ {ObjectSet,Rest} = parse_ObjectSet(Tokens),
+ {{simpletable,ObjectSet},Rest}.
+
+parse_ComponentRelationConstraint([{'{',_}|Rest]) ->
+ {ObjectSet,Rest2} = parse_DefinedObjectSet(Rest),
+ case Rest2 of
+ [{'}',_},{'{',_}|Rest3] ->
+ {AtNot,Rest4} = parse_AtNotationList(Rest3,[]),
+ case Rest4 of
+ [{'}',_}|Rest5] ->
+ {{componentrelation,ObjectSet,AtNot},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,
+ 'ComponentRelationConstraint',ended,with,'}']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ComponentRelationConstraint(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_AtNotationList(Tokens,Acc) ->
+ {AtNot,Rest} = parse_AtNotation(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_AtNotationList(Rest2,[AtNot|Acc]);
+ _ ->
+ {lists:reverse([AtNot|Acc]),Rest}
+ end.
+
+parse_AtNotation([{'@',_},{'.',_}|Rest]) ->
+ {CIdList,Rest2} = parse_ComponentIdList(Rest),
+ {{innermost,CIdList},Rest2};
+parse_AtNotation([{'@',_}|Rest]) ->
+ {CIdList,Rest2} = parse_ComponentIdList(Rest),
+ {{outermost,CIdList},Rest2};
+parse_AtNotation(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,['@','@.']]}}).
+
+parse_ComponentIdList(Tokens) ->
+ parse_ComponentIdList(Tokens,[]).
+
+parse_ComponentIdList([Id = {identifier,_,_},{'.',_}|Rest],Acc) ->
+ parse_ComponentIdList(Rest,[identifier2Extvalueref(Id)|Acc]);
+parse_ComponentIdList([Id = {identifier,_,_}|Rest],Acc) ->
+ {lists:reverse([identifier2Extvalueref(Id)|Acc]),Rest};
+parse_ComponentIdList(Tokens,_) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [identifier,'identifier.']]}}).
+
+
+
+
+
+% X.683 Parameterization of ASN.1 specifications
+
+parse_Governor(Tokens) ->
+ Flist = [fun parse_Type/1,
+ fun parse_DefinedObjectClass/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ActualParameter(Tokens) ->
+ Flist = [fun parse_Type/1,
+ fun parse_Value/1,
+ fun parse_ValueSet/1,
+ fun parse_DefinedObjectClass/1,
+ fun parse_Object/1,
+ fun parse_ObjectSet/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ParameterizedAssignment(Tokens) ->
+ Flist = [fun parse_ParameterizedTypeAssignment/1,
+ fun parse_ParameterizedValueAssignment/1,
+ fun parse_ParameterizedValueSetTypeAssignment/1,
+ fun parse_ParameterizedObjectClassAssignment/1,
+ fun parse_ParameterizedObjectAssignment/1,
+ fun parse_ParameterizedObjectSetAssignment/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ AsnAssErr = {asn1_assignment_error,_} ->
+ throw(AsnAssErr);
+ Result ->
+ Result
+ end.
+
+parse_ParameterizedTypeAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Type,Rest4} = parse_Type(Rest3),
+ {#ptypedef{pos=L1,name=Name,args=ParameterList,typespec=Type},
+ Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedTypeAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterizedValueAssignment([{identifier,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Type,Rest3} = parse_Type(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {Value,Rest5} = parse_Value(Rest4),
+ {#pvaluedef{pos=L1,name=Name,args=ParameterList,type=Type,
+ value=Value},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedValueAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_ParameterizedValueSetTypeAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Type,Rest3} = parse_Type(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {ValueSet,Rest5} = parse_ValueSet(Rest4),
+ {#pvaluesetdef{pos=L1,name=Name,args=ParameterList,
+ type=Type,valueset=ValueSet},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedValueSetTypeAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterizedObjectClassAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Class,Rest4} = parse_ObjectClass(Rest3),
+ {#ptypedef{pos=L1,name=Name,args=ParameterList,typespec=Class},
+ Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ParameterizedObjectClassAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterizedObjectAssignment([{identifier,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Class,Rest3} = parse_DefinedObjectClass(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {Object,Rest5} = parse_Object(Rest4),
+ {#pobjectdef{pos=L1,name=Name,args=ParameterList,
+ class=Class,def=Object},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ParameterizedObjectAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_ParameterizedObjectSetAssignment([{typereference,L1,Name}|Rest]) ->
+ {ParameterList,Rest2} = parse_ParameterList(Rest),
+ {Class,Rest3} = parse_DefinedObjectClass(Rest2),
+ case Rest3 of
+ [{'::=',_}|Rest4] ->
+ {ObjectSet,Rest5} = parse_ObjectSet(Rest4),
+ {#pobjectsetdef{pos=L1,name=Name,args=ParameterList,
+ class=Class,def=ObjectSet},Rest5};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+%%% Other ->
+%%% throw(Other)
+ end;
+parse_ParameterizedObjectSetAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ParameterList([{'{',_}|Rest]) ->
+ parse_ParameterList(Rest,[]);
+parse_ParameterList(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ParameterList(Tokens,Acc) ->
+ {Parameter,Rest} = parse_Parameter(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_ParameterList(Rest2,[Parameter|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([Parameter|Acc]),Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,[',','}']]}})
+ end.
+
+parse_Parameter(Tokens) ->
+ Flist = [fun parse_ParamGovAndRef/1,
+ fun parse_Reference/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_ParamGovAndRef(Tokens) ->
+ {ParamGov,Rest} = parse_ParamGovernor(Tokens),
+ case Rest of
+ [{':',_}|Rest2] ->
+ {Ref,Rest3} = parse_Reference(Rest2),
+ {{ParamGov,Ref},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,':']}})
+ end.
+
+parse_ParamGovernor(Tokens) ->
+ Flist = [fun parse_Governor/1,
+ fun parse_Reference/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+% parse_ParameterizedReference(Tokens) ->
+% {Ref,Rest} = parse_Reference(Tokens),
+% case Rest of
+% [{'{',_},{'}',_}|Rest2] ->
+% {{ptref,Ref},Rest2};
+% _ ->
+% {{ptref,Ref},Rest}
+% end.
+
+parse_SimpleDefinedType([{typereference,L1,ModuleName},{'.',_},
+ {typereference,_,TypeName}|Rest]) ->
+ {#'Externaltypereference'{pos=L1,module=ModuleName,
+ type=TypeName},Rest};
+parse_SimpleDefinedType([Tref={typereference,_,_}|Rest]) ->
+% {#'Externaltypereference'{pos=L2,module=get(asn1_module),
+% type=TypeName},Rest};
+ {tref2Exttref(Tref),Rest};
+parse_SimpleDefinedType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [typereference,'typereference.typereference']]}}).
+
+parse_SimpleDefinedValue([{typereference,L1,ModuleName},{'.',_},
+ {identifier,_,Value}|Rest]) ->
+ {{simpledefinedvalue,#'Externalvaluereference'{pos=L1,module=ModuleName,
+ value=Value}},Rest};
+parse_SimpleDefinedValue([{identifier,L2,Value}|Rest]) ->
+ {{simpledefinedvalue,L2,Value},Rest};
+parse_SimpleDefinedValue(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ ['typereference.identifier',identifier]]}}).
+
+parse_ParameterizedType(Tokens) ->
+ {Type,Rest} = parse_SimpleDefinedType(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{pt,Type,Params},Rest2}.
+
+parse_ParameterizedValue(Tokens) ->
+ {Value,Rest} = parse_SimpleDefinedValue(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{pv,Value,Params},Rest2}.
+
+parse_ParameterizedObjectClass(Tokens) ->
+ {Type,Rest} = parse_DefinedObjectClass(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{poc,Type,Params},Rest2}.
+
+parse_ParameterizedObjectSet(Tokens) ->
+ {ObjectSet,Rest} = parse_DefinedObjectSet(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{pos,ObjectSet,Params},Rest2}.
+
+parse_ParameterizedObject(Tokens) ->
+ {Object,Rest} = parse_DefinedObject(Tokens),
+ {Params,Rest2} = parse_ActualParameterList(Rest),
+ {{po,Object,Params},Rest2}.
+
+parse_ActualParameterList([{'{',_}|Rest]) ->
+ parse_ActualParameterList(Rest,[]);
+parse_ActualParameterList(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ActualParameterList(Tokens,Acc) ->
+ {Parameter,Rest} = parse_ActualParameter(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_ActualParameterList(Rest2,[Parameter|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([Parameter|Acc]),Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,[',','}']]}})
+%%% Other ->
+%%% throw(Other)
+ end.
+
+
+
+
+
+
+
+%-------------------------
+
+is_word(Token) ->
+ case not_allowed_word(Token) of
+ true -> false;
+ _ ->
+ if
+ atom(Token) ->
+ Item = atom_to_list(Token),
+ is_word(Item);
+ list(Token), length(Token) == 1 ->
+ check_one_char_word(Token);
+ list(Token) ->
+ [A|Rest] = Token,
+ case check_first(A) of
+ true ->
+ check_rest(Rest);
+ _ ->
+ false
+ end
+ end
+ end.
+
+not_allowed_word(Name) ->
+ lists:member(Name,["BIT",
+ "BOOLEAN",
+ "CHARACTER",
+ "CHOICE",
+ "EMBEDDED",
+ "END",
+ "ENUMERATED",
+ "EXTERNAL",
+ "FALSE",
+ "INSTANCE",
+ "INTEGER",
+ "INTERSECTION",
+ "MINUS-INFINITY",
+ "NULL",
+ "OBJECT",
+ "OCTET",
+ "PLUS-INFINITY",
+ "REAL",
+ "SEQUENCE",
+ "SET",
+ "TRUE",
+ "UNION"]).
+
+check_one_char_word([A]) when $A =< A, $Z >= A ->
+ true;
+check_one_char_word([_]) ->
+ false. %% unknown item in SyntaxList
+
+check_first(A) when $A =< A, $Z >= A ->
+ true;
+check_first(_) ->
+ false. %% unknown item in SyntaxList
+
+check_rest([R,R|_Rs]) when $- == R ->
+ false; %% two consecutive hyphens are not allowed in a word
+check_rest([R]) when $- == R ->
+ false; %% word cannot end with hyphen
+check_rest([R|Rs]) when $A=<R, $Z>=R; $-==R ->
+ check_rest(Rs);
+check_rest([]) ->
+ true;
+check_rest(_) ->
+ false.
+
+
+to_set(V) when list(V) ->
+ ordsets:list_to_set(V);
+to_set(V) ->
+ ordsets:list_to_set([V]).
+
+
+parse_AlternativeTypeLists(Tokens) ->
+ {AlternativeTypeList,Rest1} = parse_AlternativeTypeList(Tokens),
+ {ExtensionAndException,Rest2} =
+ case Rest1 of
+ [{',',_},{'...',L1},{'!',_}|Rest12] ->
+ {_,Rest13} = parse_ExceptionIdentification(Rest12),
+ %% Exception info is currently thrown away
+ {[#'EXTENSIONMARK'{pos=L1}],Rest13};
+ [{',',_},{'...',L1}|Rest12] ->
+ {[#'EXTENSIONMARK'{pos=L1}],Rest12};
+ _ ->
+ {[],Rest1}
+ end,
+ case ExtensionAndException of
+ [] ->
+ {AlternativeTypeList,Rest2};
+ _ ->
+ {ExtensionAddition,Rest3} =
+ case Rest2 of
+ [{',',_}|Rest23] ->
+ parse_ExtensionAdditionAlternativeList(Rest23);
+ _ ->
+ {[],Rest2}
+ end,
+ {OptionalExtensionMarker,Rest4} =
+ case Rest3 of
+ [{',',_},{'...',L3}|Rest31] ->
+ {[#'EXTENSIONMARK'{pos=L3}],Rest31};
+ _ ->
+ {[],Rest3}
+ end,
+ {AlternativeTypeList ++ ExtensionAndException ++ ExtensionAddition ++ OptionalExtensionMarker, Rest4}
+ end.
+
+
+parse_AlternativeTypeList(Tokens) ->
+ parse_AlternativeTypeList(Tokens,[]).
+
+parse_AlternativeTypeList(Tokens,Acc) ->
+ {NamedType,Rest} = parse_NamedType(Tokens),
+ case Rest of
+ [{',',_},Id = {identifier,_,_}|Rest2] ->
+ parse_AlternativeTypeList([Id|Rest2],[NamedType|Acc]);
+ _ ->
+ {lists:reverse([NamedType|Acc]),Rest}
+ end.
+
+
+
+parse_ExtensionAdditionAlternativeList(Tokens) ->
+ parse_ExtensionAdditionAlternativeList(Tokens,[]).
+
+parse_ExtensionAdditionAlternativeList(Tokens,Acc) ->
+ {Element,Rest0} =
+ case Tokens of
+ [{identifier,_,_}|_Rest] ->
+ parse_NamedType(Tokens);
+ [{'[[',_}|_] ->
+ parse_ExtensionAdditionAlternatives(Tokens)
+ end,
+ case Rest0 of
+ [{',',_}|Rest01] ->
+ parse_ExtensionAdditionAlternativeList(Rest01,[Element|Acc]);
+ _ ->
+ {lists:reverse([Element|Acc]),Rest0}
+ end.
+
+parse_ExtensionAdditionAlternatives([{'[[',_}|Rest]) ->
+ parse_ExtensionAdditionAlternatives(Rest,[]);
+parse_ExtensionAdditionAlternatives(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'[[']}}).
+
+parse_ExtensionAdditionAlternatives([Id = {identifier,_,_}|Rest],Acc) ->
+ {NamedType, Rest2} = parse_NamedType([Id|Rest]),
+ case Rest2 of
+ [{',',_}|Rest21] ->
+ parse_ExtensionAdditionAlternatives(Rest21,[NamedType|Acc]);
+ [{']]',_}|Rest21] ->
+ {lists:reverse(Acc),Rest21};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,[',',']]']]}})
+ end.
+
+parse_NamedType([{identifier,L1,Idname}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {#'ComponentType'{pos=L1,name=Idname,typespec=Type,prop=mandatory},Rest2};
+parse_NamedType(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+
+parse_ComponentTypeLists(Tokens) ->
+% Resulting tuple {ComponentTypeList,Rest1} is returned
+ case Tokens of
+ [{identifier,_,_}|_Rest0] ->
+ {Clist,Rest01} = parse_ComponentTypeList(Tokens),
+ case Rest01 of
+ [{',',_}|Rest02] ->
+ parse_ComponentTypeLists(Rest02,Clist);
+ _ ->
+ {Clist,Rest01}
+ end;
+ [{'COMPONENTS',_},{'OF',_}|_Rest] ->
+ {Clist,Rest01} = parse_ComponentTypeList(Tokens),
+ case Rest01 of
+ [{',',_}|Rest02] ->
+ parse_ComponentTypeLists(Rest02,Clist);
+ _ ->
+ {Clist,Rest01}
+ end;
+ _ ->
+ parse_ComponentTypeLists(Tokens,[])
+ end.
+
+parse_ComponentTypeLists([{'...',L1},{'!',_}|Rest],Clist1) ->
+ {_,Rest2} = parse_ExceptionIdentification(Rest),
+ %% Exception info is currently thrown away
+ parse_ComponentTypeLists2(Rest2,Clist1++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists([{'...',L1}|Rest],Clist1) ->
+ parse_ComponentTypeLists2(Rest,Clist1++[#'EXTENSIONMARK'{pos=L1}]);
+parse_ComponentTypeLists(Tokens,Clist1) ->
+ {Clist1,Tokens}.
+
+
+parse_ComponentTypeLists2(Tokens,Clist1) ->
+ {ExtensionAddition,Rest2} =
+ case Tokens of
+ [{',',_}|Rest1] ->
+ parse_ExtensionAdditionList(Rest1);
+ _ ->
+ {[],Tokens}
+ end,
+ {OptionalExtensionMarker,Rest3} =
+ case Rest2 of
+ [{',',_},{'...',L2}|Rest21] ->
+ {[#'EXTENSIONMARK'{pos=L2}],Rest21};
+ _ ->
+ {[],Rest2}
+ end,
+ {RootComponentTypeList,Rest4} =
+ case Rest3 of
+ [{',',_}|Rest31] ->
+ parse_ComponentTypeList(Rest31);
+ _ ->
+ {[],Rest3}
+ end,
+ {Clist1 ++ ExtensionAddition ++ OptionalExtensionMarker ++ RootComponentTypeList, Rest4}.
+
+
+parse_ComponentTypeList(Tokens) ->
+ parse_ComponentTypeList(Tokens,[]).
+
+parse_ComponentTypeList(Tokens,Acc) ->
+ {ComponentType,Rest} = parse_ComponentType(Tokens),
+ case Rest of
+ [{',',_},Id = {identifier,_,_}|Rest2] ->
+ parse_ComponentTypeList([Id|Rest2],[ComponentType|Acc]);
+ [{',',_},C1={'COMPONENTS',_},C2={'OF',_}|Rest2] ->
+ parse_ComponentTypeList([C1,C2|Rest2],[ComponentType|Acc]);
+% _ ->
+% {lists:reverse([ComponentType|Acc]),Rest}
+ [{'}',_}|_] ->
+ {lists:reverse([ComponentType|Acc]),Rest};
+ [{',',_},{'...',_}|_] ->
+ {lists:reverse([ComponentType|Acc]),Rest};
+ _ ->
+ throw({asn1_error,
+ {get_line(hd(Tokens)),get(asn1_module),
+ [got,[get_token(hd(Rest)),get_token(hd(tl(Rest)))],
+ expected,['}',', identifier']]}})
+ end.
+
+
+parse_ExtensionAdditionList(Tokens) ->
+ parse_ExtensionAdditionList(Tokens,[]).
+
+parse_ExtensionAdditionList(Tokens,Acc) ->
+ {Element,Rest0} =
+ case Tokens of
+ [{identifier,_,_}|_Rest] ->
+ parse_ComponentType(Tokens);
+ [{'[[',_}|_] ->
+ parse_ExtensionAdditions(Tokens);
+ _ ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [identifier,'[[']]}})
+ end,
+ case Rest0 of
+ [{',',_}|Rest01] ->
+ parse_ExtensionAdditionList(Rest01,[Element|Acc]);
+ _ ->
+ {lists:reverse([Element|Acc]),Rest0}
+ end.
+
+parse_ExtensionAdditions([{'[[',_}|Rest]) ->
+ parse_ExtensionAdditions(Rest,[]);
+parse_ExtensionAdditions(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'[[']}}).
+
+parse_ExtensionAdditions([Id = {identifier,_,_}|Rest],Acc) ->
+ {ComponentType, Rest2} = parse_ComponentType([Id|Rest]),
+ case Rest2 of
+ [{',',_}|Rest21] ->
+ parse_ExtensionAdditions(Rest21,[ComponentType|Acc]);
+ [{']]',_}|Rest21] ->
+ {lists:reverse(Acc),Rest21};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,[',',']]']]}})
+ end;
+parse_ExtensionAdditions(Tokens,_) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_ComponentType([{'COMPONENTS',_},{'OF',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'COMPONENTS OF',Type},Rest2};
+parse_ComponentType(Tokens) ->
+ {NamedType,Rest} = parse_NamedType(Tokens),
+ case Rest of
+ [{'OPTIONAL',_}|Rest2] ->
+ {NamedType#'ComponentType'{prop='OPTIONAL'},Rest2};
+ [{'DEFAULT',_}|Rest2] ->
+ {Value,Rest21} = parse_Value(Rest2),
+ {NamedType#'ComponentType'{prop={'DEFAULT',Value}},Rest21};
+ _ ->
+ {NamedType,Rest}
+ end.
+
+
+
+parse_SignedNumber([{number,_,Value}|Rest]) ->
+ {Value,Rest};
+parse_SignedNumber([{'-',_},{number,_,Value}|Rest]) ->
+ {-Value,Rest};
+parse_SignedNumber(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ [number,'-number']]}}).
+
+parse_Enumerations(Tokens=[{identifier,_,_}|_Rest]) ->
+ parse_Enumerations(Tokens,[]);
+parse_Enumerations([H|_T]) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,identifier]}}).
+
+parse_Enumerations(Tokens = [{identifier,_,_},{'(',_}|_Rest], Acc) ->
+ {NamedNumber,Rest2} = parse_NamedNumber(Tokens),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_Enumerations(Rest3,[NamedNumber|Acc]);
+ _ ->
+ {lists:reverse([NamedNumber|Acc]),Rest2}
+ end;
+parse_Enumerations([{identifier,_,Id}|Rest], Acc) ->
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_Enumerations(Rest2,[Id|Acc]);
+ _ ->
+ {lists:reverse([Id|Acc]),Rest}
+ end;
+parse_Enumerations([{'...',_}|Rest], Acc) ->
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_Enumerations(Rest2,['EXTENSIONMARK'|Acc]);
+ _ ->
+ {lists:reverse(['EXTENSIONMARK'|Acc]),Rest}
+ end;
+parse_Enumerations([H|_T],_) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,identifier]}}).
+
+parse_NamedNumberList(Tokens) ->
+ parse_NamedNumberList(Tokens,[]).
+
+parse_NamedNumberList(Tokens,Acc) ->
+ {NamedNum,Rest} = parse_NamedNumber(Tokens),
+ case Rest of
+ [{',',_}|Rest2] ->
+ parse_NamedNumberList(Rest2,[NamedNum|Acc]);
+ _ ->
+ {lists:reverse([NamedNum|Acc]),Rest}
+ end.
+
+parse_NamedNumber([{identifier,_,Name},{'(',_}|Rest]) ->
+ Flist = [fun parse_SignedNumber/1,
+ fun parse_DefinedValue/1],
+ case (catch parse_or(Rest,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {NamedNum,[{')',_}|Rest2]} ->
+ {{'NamedNumber',Name,NamedNum},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'NamedNumberList']}})
+ end;
+parse_NamedNumber(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+
+parse_Tag([{'[',_}|Rest]) ->
+ {Class,Rest2} = parse_Class(Rest),
+ {ClassNumber,Rest3} =
+ case Rest2 of
+ [{number,_,Num}|Rest21] ->
+ {Num,Rest21};
+ _ ->
+ parse_DefinedValue(Rest2)
+ end,
+ case Rest3 of
+ [{']',_}|Rest4] ->
+ {#tag{class=Class,number=ClassNumber},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest3)),get(asn1_module),
+ [got,get_token(hd(Rest3)),expected,']']}})
+ end;
+parse_Tag(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'[']}}).
+
+parse_Class([{'UNIVERSAL',_}|Rest]) ->
+ {'UNIVERSAL',Rest};
+parse_Class([{'APPLICATION',_}|Rest]) ->
+ {'APPLICATION',Rest};
+parse_Class([{'PRIVATE',_}|Rest]) ->
+ {'PRIVATE',Rest};
+parse_Class(Tokens) ->
+ {'CONTEXT',Tokens}.
+
+parse_Value(Tokens) ->
+ Flist = [fun parse_BuiltinValue/1,
+ fun parse_ValueFromObject/1,
+ fun parse_DefinedValue/1],
+
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end.
+
+parse_BuiltinValue([{bstring,_,Bstr}|Rest]) ->
+ {{bstring,Bstr},Rest};
+parse_BuiltinValue([{hstring,_,Hstr}|Rest]) ->
+ {{hstring,Hstr},Rest};
+parse_BuiltinValue([{'{',_},{'}',_}|Rest]) ->
+ {[],Rest};
+parse_BuiltinValue(Tokens = [{'{',_}|_Rest]) ->
+ Flist = [
+ fun parse_SequenceOfValue/1,
+ fun parse_SequenceValue/1,
+ fun parse_ObjectIdentifierValue/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ Result ->
+ Result
+ end;
+parse_BuiltinValue([{identifier,_,IdName},{':',_}|Rest]) ->
+ {Value,Rest2} = parse_Value(Rest),
+ {{'CHOICE',{IdName,Value}},Rest2};
+parse_BuiltinValue([{'NULL',_}|Rest]) ->
+ {'NULL',Rest};
+parse_BuiltinValue([{'TRUE',_}|Rest]) ->
+ {true,Rest};
+parse_BuiltinValue([{'FALSE',_}|Rest]) ->
+ {false,Rest};
+parse_BuiltinValue([{'PLUS-INFINITY',_}|Rest]) ->
+ {'PLUS-INFINITY',Rest};
+parse_BuiltinValue([{'MINUS-INFINITY',_}|Rest]) ->
+ {'MINUS-INFINITY',Rest};
+parse_BuiltinValue([{cstring,_,Cstr}|Rest]) ->
+ {Cstr,Rest};
+parse_BuiltinValue([{number,_,Num}|Rest]) ->
+ {Num,Rest};
+parse_BuiltinValue([{'-',_},{number,_,Num}|Rest]) ->
+ {- Num,Rest};
+parse_BuiltinValue(Tokens) ->
+ parse_ObjectClassFieldValue(Tokens).
+
+%% Externalvaluereference
+parse_DefinedValue([{typereference,L1,Tname},{'.',_},{identifier,_,Idname}|Rest]) ->
+ {#'Externalvaluereference'{pos=L1,module=Tname,value=Idname},Rest};
+%% valuereference
+parse_DefinedValue([Id = {identifier,_,_}|Rest]) ->
+ {identifier2Extvalueref(Id),Rest};
+%% ParameterizedValue
+parse_DefinedValue(Tokens) ->
+ parse_ParameterizedValue(Tokens).
+
+
+parse_SequenceValue([{'{',_}|Tokens]) ->
+ parse_SequenceValue(Tokens,[]);
+parse_SequenceValue(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_SequenceValue([{identifier,_,IdName}|Rest],Acc) ->
+ {Value,Rest2} = parse_Value(Rest),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_SequenceValue(Rest3,[{IdName,Value}|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([{IdName,Value}|Acc]),Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end;
+parse_SequenceValue(Tokens,_Acc) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+parse_SequenceOfValue([{'{',_}|Tokens]) ->
+ parse_SequenceOfValue(Tokens,[]);
+parse_SequenceOfValue(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_SequenceOfValue(Tokens,Acc) ->
+ {Value,Rest2} = parse_Value(Tokens),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_SequenceOfValue(Rest3,[Value|Acc]);
+ [{'}',_}|Rest3] ->
+ {lists:reverse([Value|Acc]),Rest3};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'}']}})
+ end.
+
+parse_ValueSetTypeAssignment([{typereference,L1,Name}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {ValueSet,Rest4} = parse_ValueSet(Rest3),
+ {#valuedef{pos=L1,name=Name,type=Type,value=ValueSet},Rest4};
+ [H|_T] ->
+ throw({asn1_error,{get_line(L1),get(asn1_module),
+ [got,get_token(H),expected,'::=']}})
+ end;
+parse_ValueSetTypeAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,
+ typereference]}}).
+
+parse_ValueSet([{'{',_}|Rest]) ->
+ {Elems,Rest2} = parse_ElementSetSpecs(Rest),
+ case Rest2 of
+ [{'}',_}|Rest3] ->
+ {{valueset,Elems},Rest3};
+ [H|_T] ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,'}']}})
+ end;
+parse_ValueSet(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'{']}}).
+
+parse_ValueAssignment([{identifier,L1,IdName}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ case Rest2 of
+ [{'::=',_}|Rest3] ->
+ {Value,Rest4} = parse_Value(Rest3),
+ case lookahead_assignment(Rest4) of
+ ok ->
+ {#valuedef{pos=L1,name=IdName,type=Type,value=Value},Rest4};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'::=']}})
+ end;
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest2)),get(asn1_module),
+ [got,get_token(hd(Rest2)),expected,'::=']}})
+ end;
+parse_ValueAssignment(Tokens) ->
+ throw({asn1_assignment_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,identifier]}}).
+
+%% SizeConstraint
+parse_SubtypeElements([{'SIZE',_}|Tokens]) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ {{'SizeConstraint',Constraint#constraint.c},Rest};
+%% PermittedAlphabet
+parse_SubtypeElements([{'FROM',_}|Tokens]) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ {{'PermittedAlphabet',Constraint#constraint.c},Rest};
+%% InnerTypeConstraints
+parse_SubtypeElements([{'WITH',_},{'COMPONENT',_}|Tokens]) ->
+ {Constraint,Rest} = parse_Constraint(Tokens),
+ {{'WITH COMPONENT',Constraint},Rest};
+parse_SubtypeElements([{'WITH',_},{'COMPONENTS',_},{'{',_},{'...',_},{',',_}|Tokens]) ->
+ {Constraint,Rest} = parse_TypeConstraints(Tokens),
+ case Rest of
+ [{'}',_}|Rest2] ->
+ {{'WITH COMPONENTS',{'PartialSpecification',Constraint}},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'}']}})
+ end;
+parse_SubtypeElements([{'WITH',_},{'COMPONENTS',_},{'{',_}|Tokens]) ->
+ {Constraint,Rest} = parse_TypeConstraints(Tokens),
+ case Rest of
+ [{'}',_}|Rest2] ->
+ {{'WITH COMPONENTS',{'FullSpecification',Constraint}},Rest2};
+ _ ->
+ throw({asn1_error,{get_line(hd(Rest)),get(asn1_module),
+ [got,get_token(hd(Rest)),expected,'}']}})
+ end;
+%% SingleValue
+%% ContainedSubtype
+%% ValueRange
+%% TypeConstraint
+parse_SubtypeElements(Tokens) ->
+ Flist = [fun parse_ContainedSubtype/1,
+ fun parse_Value/1,
+ fun([{'MIN',_}|T]) -> {'MIN',T} end,
+ fun parse_Type/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ {asn1_error,Reason} ->
+ throw(Reason);
+ Result = {Val,_} when record(Val,type) ->
+ Result;
+ {Lower,[{'..',_}|Rest]} ->
+ {Upper,Rest2} = parse_UpperEndpoint(Rest),
+ {{'ValueRange',{Lower,Upper}},Rest2};
+ {Lower,[{'<',_},{'..',_}|Rest]} ->
+ {Upper,Rest2} = parse_UpperEndpoint(Rest),
+ {{'ValueRange',{{gt,Lower},Upper}},Rest2};
+ {Res={'ContainedSubtype',_Type},Rest} ->
+ {Res,Rest};
+ {Value,Rest} ->
+ {{'SingleValue',Value},Rest}
+ end.
+
+parse_ContainedSubtype([{'INCLUDES',_}|Rest]) ->
+ {Type,Rest2} = parse_Type(Rest),
+ {{'ContainedSubtype',Type},Rest2};
+parse_ContainedSubtype(Tokens) ->
+ throw({asn1_error,{get_line(hd(Tokens)),get(asn1_module),
+ [got,get_token(hd(Tokens)),expected,'INCLUDES']}}).
+%%parse_ContainedSubtype(Tokens) -> %this option is moved to parse_SubtypeElements
+%% parse_Type(Tokens).
+
+parse_UpperEndpoint([{'<',_}|Rest]) ->
+ parse_UpperEndpoint(lt,Rest);
+parse_UpperEndpoint(Tokens) ->
+ parse_UpperEndpoint(false,Tokens).
+
+parse_UpperEndpoint(Lt,Tokens) ->
+ Flist = [ fun([{'MAX',_}|T]) -> {'MAX',T} end,
+ fun parse_Value/1],
+ case (catch parse_or(Tokens,Flist)) of
+ {'EXIT',Reason} ->
+ exit(Reason);
+ AsnErr = {asn1_error,_} ->
+ throw(AsnErr);
+ {Value,Rest2} when Lt == lt ->
+ {{lt,Value},Rest2};
+ {Value,Rest2} ->
+ {Value,Rest2}
+ end.
+
+parse_TypeConstraints(Tokens) ->
+ parse_TypeConstraints(Tokens,[]).
+
+parse_TypeConstraints([{identifier,_,_}|Rest],Acc) ->
+ {ComponentConstraint,Rest2} = parse_ComponentConstraint(Rest),
+ case Rest2 of
+ [{',',_}|Rest3] ->
+ parse_TypeConstraints(Rest3,[ComponentConstraint|Acc]);
+ _ ->
+ {lists:reverse([ComponentConstraint|Acc]),Rest2}
+ end;
+parse_TypeConstraints([H|_T],_) ->
+ throw({asn1_error,{get_line(H),get(asn1_module),
+ [got,get_token(H),expected,identifier]}}).
+
+parse_ComponentConstraint(Tokens = [{'(',_}|_Rest]) ->
+ {ValueConstraint,Rest2} = parse_Constraint(Tokens),
+ {PresenceConstraint,Rest3} = parse_PresenceConstraint(Rest2),
+ {{ValueConstraint,PresenceConstraint},Rest3};
+parse_ComponentConstraint(Tokens) ->
+ {PresenceConstraint,Rest} = parse_PresenceConstraint(Tokens),
+ {{asn1_empty,PresenceConstraint},Rest}.
+
+parse_PresenceConstraint([{'PRESENT',_}|Rest]) ->
+ {'PRESENT',Rest};
+parse_PresenceConstraint([{'ABSENT',_}|Rest]) ->
+ {'ABSENT',Rest};
+parse_PresenceConstraint([{'OPTIONAL',_}|Rest]) ->
+ {'OPTIONAL',Rest};
+parse_PresenceConstraint(Tokens) ->
+ {asn1_empty,Tokens}.
+
+
+merge_constraints({Rlist,ExtList}) -> % extensionmarker in constraint
+ {merge_constraints(Rlist,[],[]),
+ merge_constraints(ExtList,[],[])};
+
+merge_constraints(Clist) ->
+ merge_constraints(Clist, [], []).
+
+merge_constraints([Ch|Ct],Cacc, Eacc) ->
+ NewEacc = case Ch#constraint.e of
+ undefined -> Eacc;
+ E -> [E|Eacc]
+ end,
+ merge_constraints(Ct,[fixup_constraint(Ch#constraint.c)|Cacc],NewEacc);
+
+merge_constraints([],Cacc,[]) ->
+%% lists:flatten(Cacc);
+ lists:reverse(Cacc);
+merge_constraints([],Cacc,Eacc) ->
+%% lists:flatten(Cacc) ++ [{'Errors',Eacc}].
+ lists:reverse(Cacc) ++ [{'Errors',Eacc}].
+
+fixup_constraint(C) ->
+ case C of
+ {'SingleValue',SubType} when element(1,SubType) == 'ContainedSubtype' ->
+ SubType;
+ {'SingleValue',V} when list(V) ->
+ C;
+ %% [C,{'ValueRange',{lists:min(V),lists:max(V)}}];
+ %% bug, turns wrong when an element in V is a reference to a defined value
+ {'PermittedAlphabet',{'SingleValue',V}} when list(V) ->
+ %%sort and remove duplicates
+ V2 = {'SingleValue',
+ ordsets:list_to_set(lists:flatten(V))},
+ {'PermittedAlphabet',V2};
+ {'PermittedAlphabet',{'SingleValue',V}} ->
+ V2 = {'SingleValue',[V]},
+ {'PermittedAlphabet',V2};
+ {'SizeConstraint',Sc} ->
+ {'SizeConstraint',fixup_size_constraint(Sc)};
+
+ List when list(List) -> %% In This case maybe a union or intersection
+ [fixup_constraint(Xc)||Xc <- List];
+ Other ->
+ Other
+ end.
+
+fixup_size_constraint({'ValueRange',{Lb,Ub}}) ->
+ {Lb,Ub};
+fixup_size_constraint({{'ValueRange',R},[]}) ->
+ {R,[]};
+fixup_size_constraint({[],{'ValueRange',R}}) ->
+ {[],R};
+fixup_size_constraint({{'ValueRange',R1},{'ValueRange',R2}}) ->
+ {R1,R2};
+fixup_size_constraint({'SingleValue',[Sv]}) ->
+ fixup_size_constraint({'SingleValue',Sv});
+fixup_size_constraint({'SingleValue',L}) when list(L) ->
+ ordsets:list_to_set(L);
+fixup_size_constraint({'SingleValue',L}) ->
+ {L,L};
+fixup_size_constraint({C1,C2}) ->
+ {fixup_size_constraint(C1), fixup_size_constraint(C2)}.
+
+get_line({_,Pos,Token}) when integer(Pos),atom(Token) ->
+ Pos;
+get_line({Token,Pos}) when integer(Pos),atom(Token) ->
+ Pos;
+get_line(_) ->
+ undefined.
+
+get_token({_,Pos,Token}) when integer(Pos),atom(Token) ->
+ Token;
+get_token({'$end',Pos}) when integer(Pos) ->
+ undefined;
+get_token({Token,Pos}) when integer(Pos),atom(Token) ->
+ Token;
+get_token(_) ->
+ undefined.
+
+prioritize_error(ErrList) ->
+ case lists:keymember(asn1_error,1,ErrList) of
+ false -> % only asn1_assignment_error -> take the last
+ lists:last(ErrList);
+ true -> % contains errors from deeper in a Type
+ NewErrList = [_Err={_,_}|_RestErr] =
+ lists:filter(fun({asn1_error,_})->true;(_)->false end,
+ ErrList),
+ SplitErrs =
+ lists:splitwith(fun({_,X})->
+ case element(1,X) of
+ Int when integer(Int) -> true;
+ _ -> false
+ end
+ end,
+ NewErrList),
+ case SplitErrs of
+ {[],UndefPosErrs} -> % if no error with Positon exists
+ lists:last(UndefPosErrs);
+ {IntPosErrs,_} ->
+ IntPosReasons = lists:map(fun(X)->element(2,X) end,IntPosErrs),
+ SortedReasons = lists:keysort(1,IntPosReasons),
+ {asn1_error,lists:last(SortedReasons)}
+ end
+ end.
+
+%% most_prio_error([H={_,Reason}|T],Atom,Err) when atom(Atom) ->
+%% most_prio_error(T,element(1,Reason),H);
+%% most_prio_error([H={_,Reason}|T],Greatest,Err) ->
+%% case element(1,Reason) of
+%% Pos when integer(Pos),Pos>Greatest ->
+%% most_prio_error(
+
+
+tref2Exttref(#typereference{pos=Pos,val=Name}) ->
+ #'Externaltypereference'{pos=Pos,
+ module=get(asn1_module),
+ type=Name}.
+
+tref2Exttref(Pos,Name) ->
+ #'Externaltypereference'{pos=Pos,
+ module=get(asn1_module),
+ type=Name}.
+
+identifier2Extvalueref(#identifier{pos=Pos,val=Name}) ->
+ #'Externalvaluereference'{pos=Pos,
+ module=get(asn1_module),
+ value=Name}.
+
+%% lookahead_assignment/1 checks that the next sequence of tokens
+%% in Token contain a valid assignment or the
+%% 'END' token. Otherwise an exception is thrown.
+lookahead_assignment([{'END',_}|_Rest]) ->
+ ok;
+lookahead_assignment(Tokens) ->
+ parse_Assignment(Tokens),
+ ok.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_pretty_format.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_pretty_format.erl
new file mode 100644
index 0000000000..e0abcd36ec
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_pretty_format.erl
@@ -0,0 +1,199 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_pretty_format.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+
+%% usage: pretty_format:term(Term) -> PNF list of characters
+%%
+%% Note: this is usually used in expressions like:
+%% io:format('~s\n',[pretty_format:term(Term)]).
+%%
+%% Uses the following simple heuristics
+%%
+%% 1) Simple tuples are printed across the page
+%% (Simple means *all* the elements are "flat")
+%% 2) The Complex tuple {Arg1, Arg2, Arg3,....} is printed thus:
+%% {Arg1,
+%% Arg2,
+%% Arg3,
+%% ...}
+%% 3) Lists are treated as for tuples
+%% 4) Lists of printable characters are treated as strings
+%%
+%% This method seems to work reasonable well for {Tag, ...} type
+%% data structures
+
+-module(asn1ct_pretty_format).
+
+-export([term/1]).
+
+-import(io_lib, [write/1, write_string/1]).
+
+term(Term) ->
+ element(2, term(Term, 0)).
+
+%%______________________________________________________________________
+%% pretty_format:term(Term, Indent} -> {Indent', Chars}
+%% Format <Term> -- use <Indent> to indent the *next* line
+%% Note: Indent' is a new indentaion level (sometimes printing <Term>
+%% the next line to need an "extra" indent!).
+
+term([], Indent) ->
+ {Indent, [$[,$]]};
+term(L, Indent) when is_list(L) ->
+ case is_string(L) of
+ true ->
+ {Indent, write_string(L)};
+ false ->
+ case complex_list(L) of
+ true ->
+ write_complex_list(L, Indent);
+ false ->
+ write_simple_list(L, Indent)
+ end
+ end;
+term(T, Indent) when is_tuple(T) ->
+ case complex_tuple(T) of
+ true ->
+ write_complex_tuple(T, Indent);
+ false ->
+ write_simple_tuple(T, Indent)
+ end;
+term(A, Indent) ->
+ {Indent, write(A)}.
+
+%%______________________________________________________________________
+%% write_simple_list([H|T], Indent) -> {Indent', Chars}
+
+write_simple_list([H|T], Indent) ->
+ {_, S1} = term(H, Indent),
+ {_, S2} = write_simple_list_tail(T, Indent),
+ {Indent, [$[,S1|S2]}.
+
+write_simple_list_tail([H|T], Indent) ->
+ {_, S1} = term(H, Indent),
+ {_, S2} = write_simple_list_tail(T, Indent),
+ {Indent, [$,,S1| S2]};
+write_simple_list_tail([], Indent) ->
+ {Indent, "]"};
+write_simple_list_tail(Other, Indent) ->
+ {_, S} = term(Other, Indent),
+ {Indent, [$|,S,$]]}.
+
+%%______________________________________________________________________
+%% write_complex_list([H|T], Indent) -> {Indent', Chars}
+
+write_complex_list([H|T], Indent) ->
+ {I1, S1} = term(H, Indent+1),
+ {_, S2} = write_complex_list_tail(T, I1),
+ {Indent, [$[,S1|S2]}.
+
+write_complex_list_tail([H|T], Indent) ->
+ {I1, S1} = term(H, Indent),
+ {_, S2} = write_complex_list_tail(T, I1),
+ {Indent, [$,,nl_indent(Indent),S1,S2]};
+write_complex_list_tail([], Indent) ->
+ {Indent, "]"};
+write_complex_list_tail(Other, Indent) ->$,,
+ {_, S} = term(Other, Indent),
+ {Indent, [$|,S,$]]}.
+
+%%______________________________________________________________________
+%% complex_list(List) -> true | false
+%% returns true if the list is complex otherwise false
+
+complex_list([]) ->
+ false;
+complex_list([H|T]) when is_number(H); is_atom(H) ->
+ complex_list(T);
+complex_list([H|T]) ->
+ case is_string(H) of
+ true ->
+ complex_list(T);
+ false ->
+ true
+ end;
+complex_list(_) -> true.
+
+%%______________________________________________________________________
+%% complex_tuple(Tuple) -> true | false
+%% returns true if the tuple is complex otherwise false
+
+complex_tuple(T) ->
+ complex_list(tuple_to_list(T)).
+
+%%______________________________________________________________________
+%% write_simple_tuple(Tuple, Indent} -> {Indent', Chars}
+
+write_simple_tuple({}, Indent) ->
+ {Indent, "{}"};
+write_simple_tuple(Tuple, Indent) ->
+ {_, S} = write_simple_tuple_args(tuple_to_list(Tuple), Indent),
+ {Indent, [${, S, $}]}.
+
+write_simple_tuple_args([X], Indent) ->
+ term(X, Indent);
+write_simple_tuple_args([H|T], Indent) ->
+ {_, SH} = term(H, Indent),
+ {_, ST} = write_simple_tuple_args(T, Indent),
+ {Indent, [SH, $,, ST]}.
+
+%%______________________________________________________________________
+%% write_complex_tuple(Tuple, Indent} -> {Indent', Chars}
+
+write_complex_tuple(Tuple, Indent) ->
+ [H|T] = tuple_to_list(Tuple),
+ {I1, SH} = term(H, Indent+2),
+ {_, ST} = write_complex_tuple_args(T, I1),
+ {Indent, [${, SH, ST, $}]}.
+
+write_complex_tuple_args([X], Indent) ->
+ {_, S} = term(X, Indent),
+ {Indent, [$,, nl_indent(Indent), S]};
+write_complex_tuple_args([H|T], Indent) ->
+ {I1, SH} = term(H, Indent),
+ {_, ST} = write_complex_tuple_args(T, I1),
+ {Indent, [$,, nl_indent(Indent) , SH, ST]};
+write_complex_tuple_args([], Indent) ->
+ {Indent, []}.
+
+%%______________________________________________________________________
+%% utilities
+
+nl_indent(I) when I >= 0 ->
+ ["\n"|indent(I)];
+nl_indent(_) ->
+ [$\s].
+
+indent(I) when I >= 8 ->
+ [$\t|indent(I-8)];
+indent(I) when I > 0 ->
+ [$\s|indent(I-1)];
+indent(_) ->
+ [].
+
+is_string([9|T]) ->
+ is_string(T);
+is_string([10|T]) ->
+ is_string(T);
+is_string([H|T]) when H >31, H < 127 ->
+ is_string(T);
+is_string([]) ->
+ true;
+is_string(_) ->
+ false.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_tok.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_tok.erl
new file mode 100644
index 0000000000..3ac1b68b37
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_tok.erl
@@ -0,0 +1,351 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_tok.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_tok).
+
+%% Tokenize ASN.1 code (input to parser generated with yecc)
+
+-export([get_name/2,tokenise/2, file/1]).
+
+
+file(File) ->
+ case file:open(File, [read]) of
+ {error, Reason} ->
+ {error,{File,file:format_error(Reason)}};
+ {ok,Stream} ->
+ process0(Stream)
+ end.
+
+process0(Stream) ->
+ process(Stream,0,[]).
+
+process(Stream,Lno,R) ->
+ process(io:get_line(Stream, ''), Stream,Lno+1,R).
+
+process(eof, Stream,Lno,R) ->
+ file:close(Stream),
+ lists:flatten(lists:reverse([{'$end',Lno}|R]));
+
+
+process(L, Stream,Lno,R) when list(L) ->
+ %%io:format('read:~s',[L]),
+ case catch tokenise(L,Lno) of
+ {'ERR',Reason} ->
+ io:format("Tokeniser error on line: ~w ~w~n",[Lno,Reason]),
+ exit(0);
+ T ->
+ %%io:format('toks:~w~n',[T]),
+ process(Stream,Lno,[T|R])
+ end.
+
+
+tokenise([H|T],Lno) when $a =< H , H =< $z ->
+ {X, T1} = get_name(T, [H]),
+ [{identifier,Lno, list_to_atom(X)}|tokenise(T1,Lno)];
+
+tokenise([$&,H|T],Lno) when $A =< H , H =< $Z ->
+ {Y, T1} = get_name(T, [H]),
+ X = list_to_atom(Y),
+ [{typefieldreference, Lno, X} | tokenise(T1, Lno)];
+
+tokenise([$&,H|T],Lno) when $a =< H , H =< $z ->
+ {Y, T1} = get_name(T, [H]),
+ X = list_to_atom(Y),
+ [{valuefieldreference, Lno, X} | tokenise(T1, Lno)];
+
+tokenise([H|T],Lno) when $A =< H , H =< $Z ->
+ {Y, T1} = get_name(T, [H]),
+ X = list_to_atom(Y),
+ case reserved_word(X) of
+ true ->
+ [{X,Lno}|tokenise(T1,Lno)];
+ false ->
+ [{typereference,Lno,X}|tokenise(T1,Lno)];
+ rstrtype ->
+ [{restrictedcharacterstringtype,Lno,X}|tokenise(T1,Lno)]
+ end;
+
+tokenise([$-,H|T],Lno) when $0 =< H , H =< $9 ->
+ {X, T1} = get_number(T, [H]),
+ [{number,Lno,-1 * list_to_integer(X)}|tokenise(T1,Lno)];
+
+tokenise([H|T],Lno) when $0 =< H , H =< $9 ->
+ {X, T1} = get_number(T, [H]),
+ [{number,Lno,list_to_integer(X)}|tokenise(T1,Lno)];
+
+tokenise([$-,$-|T],Lno) ->
+ tokenise(skip_comment(T),Lno);
+tokenise([$:,$:,$=|T],Lno) ->
+ [{'::=',Lno}|tokenise(T,Lno)];
+
+tokenise([$'|T],Lno) ->
+ case catch collect_quoted(T,Lno,[]) of
+ {'ERR',_} ->
+ throw({'ERR','bad_quote'});
+ {Thing, T1} ->
+ [Thing|tokenise(T1,Lno)]
+ end;
+
+tokenise([$"|T],Lno) ->
+ collect_string(T,Lno);
+
+tokenise([${|T],Lno) ->
+ [{'{',Lno}|tokenise(T,Lno)];
+
+tokenise([$}|T],Lno) ->
+ [{'}',Lno}|tokenise(T,Lno)];
+
+tokenise([$]|T],Lno) ->
+ [{']',Lno}|tokenise(T,Lno)];
+
+tokenise([$[|T],Lno) ->
+ [{'[',Lno}|tokenise(T,Lno)];
+
+tokenise([$,|T],Lno) ->
+ [{',',Lno}|tokenise(T,Lno)];
+
+tokenise([$(|T],Lno) ->
+ [{'(',Lno}|tokenise(T,Lno)];
+tokenise([$)|T],Lno) ->
+ [{')',Lno}|tokenise(T,Lno)];
+
+tokenise([$.,$.,$.|T],Lno) ->
+ [{'...',Lno}|tokenise(T,Lno)];
+
+tokenise([$.,$.|T],Lno) ->
+ [{'..',Lno}|tokenise(T,Lno)];
+
+tokenise([$.|T],Lno) ->
+ [{'.',Lno}|tokenise(T,Lno)];
+tokenise([$^|T],Lno) ->
+ [{'^',Lno}|tokenise(T,Lno)];
+tokenise([$!|T],Lno) ->
+ [{'!',Lno}|tokenise(T,Lno)];
+tokenise([$||T],Lno) ->
+ [{'|',Lno}|tokenise(T,Lno)];
+
+
+tokenise([H|T],Lno) ->
+ case white_space(H) of
+ true ->
+ tokenise(T,Lno);
+ false ->
+ [{list_to_atom([H]),Lno}|tokenise(T,Lno)]
+ end;
+tokenise([],_) ->
+ [].
+
+
+collect_string(L,Lno) ->
+ collect_string(L,Lno,[]).
+
+collect_string([],_,_) ->
+ throw({'ERR','bad_quote found eof'});
+
+collect_string([H|T],Lno,Str) ->
+ case H of
+ $" ->
+ [{cstring,1,lists:reverse(Str)}|tokenise(T,Lno)];
+ Ch ->
+ collect_string(T,Lno,[Ch|Str])
+ end.
+
+
+
+% <name> is letters digits hyphens
+% hypen is not the last character. Hypen hyphen is NOT allowed
+%
+% <identifier> ::= <lowercase> <name>
+
+get_name([$-,Char|T], L) ->
+ case isalnum(Char) of
+ true ->
+ get_name(T,[Char,$-|L]);
+ false ->
+ {lists:reverse(L),[$-,Char|T]}
+ end;
+get_name([$-|T], L) ->
+ {lists:reverse(L),[$-|T]};
+get_name([Char|T], L) ->
+ case isalnum(Char) of
+ true ->
+ get_name(T,[Char|L]);
+ false ->
+ {lists:reverse(L),[Char|T]}
+ end;
+get_name([], L) ->
+ {lists:reverse(L), []}.
+
+
+isalnum(H) when $A =< H , H =< $Z ->
+ true;
+isalnum(H) when $a =< H , H =< $z ->
+ true;
+isalnum(H) when $0 =< H , H =< $9 ->
+ true;
+isalnum(_) ->
+ false.
+
+isdigit(H) when $0 =< H , H =< $9 ->
+ true;
+isdigit(_) ->
+ false.
+
+white_space(9) -> true;
+white_space(10) -> true;
+white_space(13) -> true;
+white_space(32) -> true;
+white_space(_) -> false.
+
+
+get_number([H|T], L) ->
+ case isdigit(H) of
+ true ->
+ get_number(T, [H|L]);
+ false ->
+ {lists:reverse(L), [H|T]}
+ end;
+get_number([], L) ->
+ {lists:reverse(L), []}.
+
+skip_comment([]) ->
+ [];
+skip_comment([$-,$-|T]) ->
+ T;
+skip_comment([_|T]) ->
+ skip_comment(T).
+
+collect_quoted([$',$B|T],Lno, L) ->
+ case check_bin(L) of
+ true ->
+ {{bstring,Lno, lists:reverse(L)}, T};
+ false ->
+ throw({'ERR',{invalid_binary_number, lists:reverse(L)}})
+ end;
+collect_quoted([$',$H|T],Lno, L) ->
+ case check_hex(L) of
+ true ->
+ {{hstring,Lno, lists:reverse(L)}, T};
+ false ->
+ throw({'ERR',{invalid_binary_number, lists:reverse(L)}})
+ end;
+collect_quoted([H|T], Lno, L) ->
+ collect_quoted(T, Lno,[H|L]);
+collect_quoted([], _, _) -> % This should be allowed FIX later
+ throw({'ERR',{eol_in_token}}).
+
+check_bin([$0|T]) ->
+ check_bin(T);
+check_bin([$1|T]) ->
+ check_bin(T);
+check_bin([]) ->
+ true;
+check_bin(_) ->
+ false.
+
+check_hex([H|T]) when $0 =< H , H =< $9 ->
+ check_hex(T);
+check_hex([H|T]) when $A =< H , H =< $F ->
+ check_hex(T);
+check_hex([]) ->
+ true;
+check_hex(_) ->
+ false.
+
+
+%% reserved_word(A) -> true|false|rstrtype
+%% A = atom()
+%% returns true if A is a reserved ASN.1 word
+%% returns false if A is not a reserved word
+%% returns rstrtype if A is a reserved word in the group
+%% RestrictedCharacterStringType
+reserved_word('ABSENT') -> true;
+%reserved_word('ABSTRACT-SYNTAX') -> true; % impl as predef item
+reserved_word('ALL') -> true;
+reserved_word('ANY') -> true;
+reserved_word('APPLICATION') -> true;
+reserved_word('AUTOMATIC') -> true;
+reserved_word('BEGIN') -> true;
+reserved_word('BIT') -> true;
+reserved_word('BMPString') -> rstrtype;
+reserved_word('BOOLEAN') -> true;
+reserved_word('BY') -> true;
+reserved_word('CHARACTER') -> true;
+reserved_word('CHOICE') -> true;
+reserved_word('CLASS') -> true;
+reserved_word('COMPONENT') -> true;
+reserved_word('COMPONENTS') -> true;
+reserved_word('CONSTRAINED') -> true;
+reserved_word('DEFAULT') -> true;
+reserved_word('DEFINED') -> true;
+reserved_word('DEFINITIONS') -> true;
+reserved_word('EMBEDDED') -> true;
+reserved_word('END') -> true;
+reserved_word('ENUMERATED') -> true;
+reserved_word('EXCEPT') -> true;
+reserved_word('EXPLICIT') -> true;
+reserved_word('EXPORTS') -> true;
+reserved_word('EXTERNAL') -> true;
+reserved_word('FALSE') -> true;
+reserved_word('FROM') -> true;
+reserved_word('GeneralizedTime') -> true;
+reserved_word('GeneralString') -> rstrtype;
+reserved_word('GraphicString') -> rstrtype;
+reserved_word('IA5String') -> rstrtype;
+% reserved_word('TYPE-IDENTIFIER') -> true; % impl as predef item
+reserved_word('IDENTIFIER') -> true;
+reserved_word('IMPLICIT') -> true;
+reserved_word('IMPORTS') -> true;
+reserved_word('INCLUDES') -> true;
+reserved_word('INSTANCE') -> true;
+reserved_word('INTEGER') -> true;
+reserved_word('INTERSECTION') -> true;
+reserved_word('ISO646String') -> rstrtype;
+reserved_word('MAX') -> true;
+reserved_word('MIN') -> true;
+reserved_word('MINUS-INFINITY') -> true;
+reserved_word('NULL') -> true;
+reserved_word('NumericString') -> rstrtype;
+reserved_word('OBJECT') -> true;
+reserved_word('ObjectDescriptor') -> true;
+reserved_word('OCTET') -> true;
+reserved_word('OF') -> true;
+reserved_word('OPTIONAL') -> true;
+reserved_word('PDV') -> true;
+reserved_word('PLUS-INFINITY') -> true;
+reserved_word('PRESENT') -> true;
+reserved_word('PrintableString') -> rstrtype;
+reserved_word('PRIVATE') -> true;
+reserved_word('REAL') -> true;
+reserved_word('SEQUENCE') -> true;
+reserved_word('SET') -> true;
+reserved_word('SIZE') -> true;
+reserved_word('STRING') -> true;
+reserved_word('SYNTAX') -> true;
+reserved_word('T61String') -> rstrtype;
+reserved_word('TAGS') -> true;
+reserved_word('TeletexString') -> rstrtype;
+reserved_word('TRUE') -> true;
+reserved_word('UNION') -> true;
+reserved_word('UNIQUE') -> true;
+reserved_word('UNIVERSAL') -> true;
+reserved_word('UniversalString') -> rstrtype;
+reserved_word('UTCTime') -> true;
+reserved_word('VideotexString') -> rstrtype;
+reserved_word('VisibleString') -> rstrtype;
+reserved_word('WITH') -> true;
+reserved_word(_) -> false.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_value.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_value.erl
new file mode 100644
index 0000000000..9510e4b341
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1ct_value.erl
@@ -0,0 +1,330 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1ct_value.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1ct_value).
+
+%% Generate Erlang values for ASN.1 types.
+%% The value is randomized within it's constraints
+
+-include("asn1_records.hrl").
+%-compile(export_all).
+
+-export([get_type/3]).
+
+
+
+%% Generate examples of values ******************************
+%%****************************************x
+
+
+get_type(M,Typename,Tellname) ->
+ case asn1_db:dbget(M,Typename) of
+ undefined ->
+ {asn1_error,{not_found,{M,Typename}}};
+ Tdef when record(Tdef,typedef) ->
+ Type = Tdef#typedef.typespec,
+ get_type(M,[Typename],Type,Tellname);
+ Err ->
+ {asn1_error,{other,Err}}
+ end.
+
+get_type(M,Typename,Type,Tellname) when record(Type,type) ->
+ InnerType = get_inner(Type#type.def),
+ case asn1ct_gen:type(InnerType) of
+ #'Externaltypereference'{module=Emod,type=Etype} ->
+ get_type(Emod,Etype,Tellname);
+ {_,user} ->
+ case Tellname of
+ yes -> {Typename,get_type(M,InnerType,no)};
+ no -> get_type(M,InnerType,no)
+ end;
+ {notype,_} ->
+ true;
+ {primitive,bif} ->
+ get_type_prim(Type);
+ 'ASN1_OPEN_TYPE' ->
+ case Type#type.constraint of
+ [#'Externaltypereference'{type=TrefConstraint}] ->
+ get_type(M,TrefConstraint,no);
+ _ ->
+ "open_type"
+ end;
+ {constructed,bif} ->
+ get_type_constructed(M,Typename,InnerType,Type)
+ end;
+get_type(M,Typename,#'ComponentType'{name = Name,typespec = Type},_) ->
+ get_type(M,[Name|Typename],Type,no);
+get_type(_,_,_,_) -> % 'EXTENSIONMARK'
+ undefined.
+
+get_inner(A) when atom(A) -> A;
+get_inner(Ext) when record(Ext,'Externaltypereference') -> Ext;
+get_inner({typereference,_Pos,Name}) -> Name;
+get_inner(T) when tuple(T) ->
+ case asn1ct_gen:get_inner(T) of
+ {fixedtypevaluefield,_,Type} ->
+ Type#type.def;
+ {typefield,_FieldName} ->
+ 'ASN1_OPEN_TYPE';
+ Other ->
+ Other
+ end.
+%%get_inner(T) when tuple(T) -> element(1,T).
+
+
+
+get_type_constructed(M,Typename,InnerType,D) when record(D,type) ->
+ case InnerType of
+ 'SET' ->
+ get_sequence(M,Typename,D);
+ 'SEQUENCE' ->
+ get_sequence(M,Typename,D);
+ 'CHOICE' ->
+ get_choice(M,Typename,D);
+ 'SEQUENCE OF' ->
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ get_sequence_of(M,Typename,D,NameSuffix);
+ 'SET OF' ->
+ {_,Type} = D#type.def,
+ NameSuffix = asn1ct_gen:constructed_suffix(InnerType,Type#type.def),
+ get_sequence_of(M,Typename,D,NameSuffix);
+ _ ->
+ exit({nyi,InnerType})
+ end.
+
+get_sequence(M,Typename,Type) ->
+ {_SEQorSET,CompList} =
+ case Type#type.def of
+ #'SEQUENCE'{components=Cl} -> {'SEQUENCE',Cl};
+ #'SET'{components=Cl} -> {'SET',Cl}
+ end,
+ case get_components(M,Typename,CompList) of
+ [] ->
+ {list_to_atom(asn1ct_gen:list2rname(Typename))};
+ C ->
+ list_to_tuple([list_to_atom(asn1ct_gen:list2rname(Typename))|C])
+ end.
+
+get_components(M,Typename,{Root,Ext}) ->
+ get_components(M,Typename,Root++Ext);
+
+%% Should enhance this *** HERE *** with proper handling of extensions
+
+get_components(M,Typename,[H|T]) ->
+ [get_type(M,Typename,H,no)|
+ get_components(M,Typename,T)];
+get_components(_,_,[]) ->
+ [].
+
+get_choice(M,Typename,Type) ->
+ {'CHOICE',TCompList} = Type#type.def,
+ case TCompList of
+ [] ->
+ {asn1_EMPTY,asn1_EMPTY};
+ {CompList,ExtList} -> % Should be enhanced to handle extensions too
+ CList = CompList ++ ExtList,
+ C = lists:nth(random(length(CList)),CList),
+ {C#'ComponentType'.name,get_type(M,Typename,C,no)};
+ CompList when list(CompList) ->
+ C = lists:nth(random(length(CompList)),CompList),
+ {C#'ComponentType'.name,get_type(M,Typename,C,no)}
+ end.
+
+get_sequence_of(M,Typename,Type,TypeSuffix) ->
+ %% should generate length according to constraints later
+ {_,Oftype} = Type#type.def,
+ C = Type#type.constraint,
+ S = size_random(C),
+ NewTypeName = [TypeSuffix|Typename],
+ gen_list(M,NewTypeName,Oftype,no,S).
+
+gen_list(_,_,_,_,0) ->
+ [];
+gen_list(M,Typename,Oftype,Tellname,N) ->
+ [get_type(M,Typename,Oftype,no)|gen_list(M,Typename,Oftype,Tellname,N-1)].
+
+get_type_prim(D) ->
+ C = D#type.constraint,
+ case D#type.def of
+ 'INTEGER' ->
+ i_random(C);
+ {'INTEGER',NamedNumberList} ->
+ NN = [X||{X,_} <- NamedNumberList],
+ case NN of
+ [] ->
+ i_random(C);
+ _ ->
+ lists:nth(random(length(NN)),NN)
+ end;
+ Enum when tuple(Enum),element(1,Enum)=='ENUMERATED' ->
+ NamedNumberList =
+ case Enum of
+ {_,_,NNL} -> NNL;
+ {_,NNL} -> NNL
+ end,
+ NNew=
+ case NamedNumberList of
+ {N1,N2} ->
+ N1 ++ N2;
+ _->
+ NamedNumberList
+ end,
+ NN = [X||{X,_} <- NNew],
+ case NN of
+ [] ->
+ asn1_EMPTY;
+ _ ->
+ lists:nth(random(length(NN)),NN)
+ end;
+ {'BIT STRING',NamedNumberList} ->
+%% io:format("get_type_prim 1: ~w~n",[NamedNumberList]),
+ NN = [X||{X,_} <- NamedNumberList],
+ case NN of
+ [] ->
+ Bl1 =lists:reverse(adjust_list(size_random(C),[1,0,1,1])),
+ lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,Bl1));
+ _ ->
+%% io:format("get_type_prim 2: ~w~n",[NN]),
+ [lists:nth(random(length(NN)),NN)]
+ end;
+ 'ANY' ->
+ exit({asn1_error,nyi,'ANY'});
+ 'NULL' ->
+ 'NULL';
+ 'OBJECT IDENTIFIER' ->
+ Len = random(3),
+ Olist = [(random(1000)-1)||_X <-lists:seq(1,Len)],
+ list_to_tuple([random(3)-1,random(40)-1|Olist]);
+ 'ObjectDescriptor' ->
+ object_descriptor_nyi;
+ 'BOOLEAN' ->
+ true;
+ 'OCTET STRING' ->
+ adjust_list(size_random(C),c_string(C,"OCTET STRING"));
+ 'NumericString' ->
+ adjust_list(size_random(C),c_string(C,"0123456789"));
+ 'TeletexString' ->
+ adjust_list(size_random(C),c_string(C,"TeletexString"));
+ 'VideotexString' ->
+ adjust_list(size_random(C),c_string(C,"VideotexString"));
+ 'UTCTime' ->
+ "97100211-0500";
+ 'GeneralizedTime' ->
+ "19971002103130.5";
+ 'GraphicString' ->
+ adjust_list(size_random(C),c_string(C,"GraphicString"));
+ 'VisibleString' ->
+ adjust_list(size_random(C),c_string(C,"VisibleString"));
+ 'GeneralString' ->
+ adjust_list(size_random(C),c_string(C,"GeneralString"));
+ 'PrintableString' ->
+ adjust_list(size_random(C),c_string(C,"PrintableString"));
+ 'IA5String' ->
+ adjust_list(size_random(C),c_string(C,"IA5String"));
+ 'BMPString' ->
+ adjust_list(size_random(C),c_string(C,"BMPString"));
+ 'UniversalString' ->
+ adjust_list(size_random(C),c_string(C,"UniversalString"));
+ XX ->
+ exit({asn1_error,nyi,XX})
+ end.
+
+c_string(undefined,Default) ->
+ Default;
+c_string(C,Default) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} when list(Sv) ->
+ Sv;
+ {'SingleValue',V} when integer(V) ->
+ [V];
+ no ->
+ Default
+ end.
+
+random(Upper) ->
+ {A1,A2,A3} = erlang:now(),
+ random:seed(A1,A2,A3),
+ random:uniform(Upper).
+
+size_random(C) ->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ c_random({0,5},no);
+ {Lb,Ub} when Ub-Lb =< 4 ->
+ c_random({Lb,Ub},no);
+ {Lb,_} ->
+ c_random({Lb,Lb+4},no);
+ Sv ->
+ c_random(no,Sv)
+ end.
+
+i_random(C) ->
+ c_random(get_constraint(C,'ValueRange'),get_constraint(C,'SingleValue')).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% c_random(Range,SingleValue)
+%% only called from other X_random functions
+
+c_random(VRange,Single) ->
+ case {VRange,Single} of
+ {no,no} ->
+ random(16#fffffff) - (16#fffffff bsr 1);
+ {R,no} ->
+ case R of
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ Range = Ub - Lb +1,
+ Lb + (random(Range)-1);
+ {Lb,'MAX'} ->
+ Lb + random(16#fffffff)-1;
+ {'MIN',Ub} ->
+ Ub - random(16#fffffff)-1;
+ {A,{'ASN1_OK',B}} ->
+ Range = B - A +1,
+ A + (random(Range)-1)
+ end;
+ {_,S} when integer(S) ->
+ S;
+ {_,S} when list(S) ->
+ lists:nth(random(length(S)),S)
+%% {S1,S2} ->
+%% io:format("asn1ct_value: hejsan hoppsan~n");
+%% _ ->
+%% io:format("asn1ct_value: hejsan hoppsan 2~n")
+%% io:format("asn1ct_value: c_random/2: S1 = ~w~n"
+%% "S2 = ~w,~n",[S1,S2])
+%% exit(self(),goodbye)
+ end.
+
+adjust_list(Len,Orig) ->
+ adjust_list1(Len,Orig,Orig,[]).
+
+adjust_list1(0,_Orig,[_Oh|_Ot],Acc) ->
+ lists:reverse(Acc);
+adjust_list1(Len,Orig,[],Acc) ->
+ adjust_list1(Len,Orig,Orig,Acc);
+adjust_list1(Len,Orig,[Oh|Ot],Acc) ->
+ adjust_list1(Len-1,Orig,Ot,[Oh|Acc]).
+
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt.erl
new file mode 100644
index 0000000000..1d73927052
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt.erl
@@ -0,0 +1,69 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt).
+
+%% Runtime functions for ASN.1 (i.e encode, decode)
+
+-export([encode/2,encode/3,decode/3,load_driver/0,unload_driver/0,info/1]).
+
+encode(Module,{Type,Term}) ->
+ encode(Module,Type,Term).
+
+encode(Module,Type,Term) ->
+ case catch apply(Module,encode,[Type,Term]) of
+ {'EXIT',undef} ->
+ {error,{asn1,{undef,Module,Type}}};
+ Result ->
+ Result
+ end.
+
+decode(Module,Type,Bytes) ->
+ case catch apply(Module,decode,[Type,Bytes]) of
+ {'EXIT',undef} ->
+ {error,{asn1,{undef,Module,Type}}};
+ Result ->
+ Result
+ end.
+
+load_driver() ->
+ asn1rt_driver_handler:load_driver(),
+ receive
+ driver_ready ->
+ ok;
+ Err={error,_Reason} ->
+ Err;
+ Error ->
+ {error,Error}
+ end.
+
+unload_driver() ->
+ case catch asn1rt_driver_handler:unload_driver() of
+ ok ->
+ ok;
+ Error ->
+ {error,Error}
+ end.
+
+
+info(Module) ->
+ case catch apply(Module,info,[]) of
+ {'EXIT',{undef,_Reason}} ->
+ {error,{asn1,{undef,Module,info}}};
+ Result ->
+ {ok,Result}
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin.erl
new file mode 100644
index 0000000000..4f4574513e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin.erl
@@ -0,0 +1,2310 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_ber_bin.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt_ber_bin).
+
+%% encoding / decoding of BER
+
+-export([decode/1]).
+-export([fixoptionals/2,split_list/2,cindex/3,restbytes2/3,
+ list_to_record/2,
+ encode_tag_val/1,decode_tag/1,peek_tag/1,
+ check_tags/3, encode_tags/3]).
+-export([encode_boolean/2,decode_boolean/3,
+ encode_integer/3,encode_integer/4,
+ decode_integer/4,decode_integer/5,encode_enumerated/2,
+ encode_enumerated/4,decode_enumerated/5,
+ encode_real/2,decode_real/4,
+ encode_bit_string/4,decode_bit_string/6,
+ decode_compact_bit_string/6,
+ encode_octet_string/3,decode_octet_string/5,
+ encode_null/2,decode_null/3,
+ encode_object_identifier/2,decode_object_identifier/3,
+ encode_restricted_string/4,decode_restricted_string/6,
+ encode_universal_string/3,decode_universal_string/5,
+ encode_BMP_string/3,decode_BMP_string/5,
+ encode_generalized_time/3,decode_generalized_time/5,
+ encode_utc_time/3,decode_utc_time/5,
+ encode_length/1,decode_length/1,
+ check_if_valid_tag/3,
+ decode_tag_and_length/1, decode_components/6,
+ decode_components/7, decode_set/6]).
+
+-export([encode_open_type/1,encode_open_type/2,decode_open_type/1,decode_open_type/2,decode_open_type/3]).
+-export([skipvalue/1, skipvalue/2]).
+
+-include("asn1_records.hrl").
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+%%% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_BMPString, 30).
+
+
+% the complete tag-word of built-in types
+-define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
+-define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
+-define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
+-define(T_OCTET_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 4). % can be CONSTRUCTED
+-define(T_NULL, ?UNIVERSAL bor ?PRIMITIVE bor 5).
+-define(T_OBJECT_IDENTIFIER,?UNIVERSAL bor ?PRIMITIVE bor 6).
+-define(T_OBJECT_DESCRIPTOR,?UNIVERSAL bor ?PRIMITIVE bor 7).
+-define(T_EXTERNAL, ?UNIVERSAL bor ?PRIMITIVE bor 8).
+-define(T_REAL, ?UNIVERSAL bor ?PRIMITIVE bor 9).
+-define(T_ENUMERATED, ?UNIVERSAL bor ?PRIMITIVE bor 10).
+-define(T_EMBEDDED_PDV, ?UNIVERSAL bor ?PRIMITIVE bor 11).
+-define(T_SEQUENCE, ?UNIVERSAL bor ?CONSTRUCTED bor 16).
+-define(T_SET, ?UNIVERSAL bor ?CONSTRUCTED bor 17).
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_UTCTime, ?UNIVERSAL bor ?PRIMITIVE bor 23).
+-define(T_GeneralizedTime, ?UNIVERSAL bor ?PRIMITIVE bor 24).
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+-define(T_UniversalString, ?UNIVERSAL bor ?PRIMITIVE bor 28). %can be constructed
+-define(T_BMPString, ?UNIVERSAL bor ?PRIMITIVE bor 30). %can be constructed
+
+
+decode(Bin) ->
+ decode_primitive(Bin).
+
+decode_primitive(Bin) ->
+ {Tlv = {Tag,Len,V},<<>>} = decode_tlv(Bin),
+ case element(2,Tag) of
+ ?CONSTRUCTED ->
+ {Tag,Len,decode_constructed(V)};
+ _ ->
+ Tlv
+ end.
+
+decode_constructed(<<>>) ->
+ [];
+decode_constructed(Bin) ->
+ {Tlv = {Tag,Len,V},Rest} = decode_tlv(Bin),
+ NewTlv =
+ case element(2,Tag) of
+ ?CONSTRUCTED ->
+ {Tag,Len,decode_constructed(V)};
+ _ ->
+ Tlv
+ end,
+ [NewTlv|decode_constructed(Rest)].
+
+decode_tlv(Bin) ->
+ {Tag,Bin1,_Rb1} = decode_tag(Bin),
+ {{Len,Bin2},_Rb2} = decode_length(Bin1),
+ <<V:Len/binary,Bin3/binary>> = Bin2,
+ {{Tag,Len,V},Bin3}.
+
+
+
+%%%%%%%%%%%%%
+% split_list(List,HeadLen) -> {HeadList,TailList}
+%
+% splits List into HeadList (Length=HeadLen) and TailList
+% if HeadLen == indefinite -> return {List,indefinite}
+split_list(List,indefinite) ->
+ {List, indefinite};
+split_list(Bin, Len) when binary(Bin) ->
+ split_binary(Bin,Len);
+split_list(List,Len) ->
+ {lists:sublist(List,Len),lists:nthtail(Len,List)}.
+
+
+%%% new function which fixes a bug regarding indefinite length decoding
+restbytes2(indefinite,<<0,0,RemBytes/binary>>,_) ->
+ {RemBytes,2};
+restbytes2(indefinite,RemBytes,ext) ->
+ skipvalue(indefinite,RemBytes);
+restbytes2(RemBytes,<<>>,_) ->
+ {RemBytes,0};
+restbytes2(_RemBytes,Bytes,noext) ->
+ exit({error,{asn1, {unexpected,Bytes}}});
+restbytes2(RemBytes,_Bytes,ext) ->
+ {RemBytes,0}.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% skipvalue(Length, Bytes) -> {RemainingBytes, RemovedNumberOfBytes}
+%%
+%% skips the one complete (could be nested) TLV from Bytes
+%% handles both definite and indefinite length encodings
+%%
+
+skipvalue(L, Bytes) ->
+ skipvalue(L, Bytes, 0).
+
+skipvalue(indefinite, Bytes, Rb) ->
+ {_T,Bytes2,R2} = decode_tag(Bytes),
+ {{L,Bytes3},R3} = decode_length(Bytes2),
+ {Bytes4,Rb4} = case L of
+ indefinite ->
+ skipvalue(indefinite,Bytes3,R2+R3);
+ _ ->
+ <<_:L/binary, RestBytes/binary>> = Bytes3,
+ {RestBytes, R2+R3+L}
+ end,
+ case Bytes4 of
+ <<0,0,Bytes5/binary>> ->
+ {Bytes5,Rb+Rb4+2};
+ _ -> skipvalue(indefinite,Bytes4,Rb+Rb4)
+ end;
+skipvalue(L, Bytes, Rb) ->
+% <<Skip:L/binary, RestBytes/binary>> = Bytes,
+ <<_:L/binary, RestBytes/binary>> = Bytes,
+ {RestBytes,Rb+L}.
+
+%%skipvalue(indefinite, Bytes, Rb) ->
+%% {T,Bytes2,R2} = decode_tag(Bytes),
+%% {L,Bytes3,R3} = decode_length(Bytes2),
+%% {Bytes4,Rb4} = case L of
+%% indefinite ->
+%% skipvalue(indefinite,Bytes3,R2+R3);
+%% _ ->
+%% lists:nthtail(L,Bytes3) %% konstigt !?
+%% end,
+%% case Bytes4 of
+%% [0,0|Bytes5] ->
+%% {Bytes5,Rb4+2};
+%% _ -> skipvalue(indefinite,Bytes4,Rb4)
+%% end;
+%%skipvalue(L, Bytes, Rb) ->
+%% {lists:nthtail(L,Bytes),Rb+L}.
+
+skipvalue(Bytes) ->
+ {_T,Bytes2,R2} = decode_tag(Bytes),
+ {{L,Bytes3},R3} = decode_length(Bytes2),
+ skipvalue(L,Bytes3,R2+R3).
+
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Optionals, preset not filled optionals with asn1_NOVALUE
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,_Acc1,Acc2) ->
+ % return Val as a record
+ list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]).
+
+
+%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%% 8bit Int | binary
+encode_tag_val({Class, Form, TagNo}) when (TagNo =< 30) ->
+ <<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
+
+encode_tag_val({Class, Form, TagNo}) ->
+ {Octets,_Len} = mk_object_val(TagNo),
+ BinOct = list_to_binary(Octets),
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>;
+
+%% asumes whole correct tag bitpattern, multiple of 8
+encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% anv�nds denna funktion??!!
+%% asumes correct bitpattern of 0-5
+encode_tag_val(Tag) -> encode_tag_val2(Tag,[]).
+
+encode_tag_val2(Tag, OctAck) when (Tag =< 255) ->
+ [Tag | OctAck];
+encode_tag_val2(Tag, OctAck) ->
+ encode_tag_val2(Tag bsr 8, [255 band Tag | OctAck]).
+
+
+%%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%%% 8bit Int | [list of octets]
+%encode_tag_val({Class, Form, TagNo}) when (TagNo =< 30) ->
+%%% <<Class:2,Form:1,TagNo:5>>;
+% [Class bor Form bor TagNo];
+%encode_tag_val({Class, Form, TagNo}) ->
+% {Octets,L} = mk_object_val(TagNo),
+% [Class bor Form bor 31 | Octets];
+
+
+%%============================================================================\%% Peek on the initial tag
+%% peek_tag(Bytes) -> TagBytes
+%% interprets the first byte and possible second, third and fourth byte as
+%% a tag and returns all the bytes comprising the tag, the constructed/primitive bit (6:th bit of first byte) is normalised to 0
+%%
+
+peek_tag(<<B7_6:2,_:1,31:5,Buffer/binary>>) ->
+ Bin = peek_tag(Buffer, <<>>),
+ <<B7_6:2,31:6,Bin/binary>>;
+%% single tag (tagno < 31)
+peek_tag(<<B7_6:2,_:1,B4_0:5,_Buffer/binary>>) ->
+ <<B7_6:2,B4_0:6>>.
+
+peek_tag(<<0:1,PartialTag:7,_Buffer/binary>>, TagAck) ->
+ <<TagAck/binary,PartialTag>>;
+peek_tag(<<PartialTag,Buffer/binary>>, TagAck) ->
+ peek_tag(Buffer,<<TagAck/binary,PartialTag>>);
+peek_tag(_,TagAck) ->
+ exit({error,{asn1, {invalid_tag,TagAck}}}).
+%%peek_tag([Tag|Buffer]) when (Tag band 31) == 31 ->
+%% [Tag band 2#11011111 | peek_tag(Buffer,[])];
+%%%% single tag (tagno < 31)
+%%peek_tag([Tag|Buffer]) ->
+%% [Tag band 2#11011111].
+
+%%peek_tag([PartialTag|Buffer], TagAck) when (PartialTag < 128 ) ->
+%% lists:reverse([PartialTag|TagAck]);
+%%peek_tag([PartialTag|Buffer], TagAck) ->
+%% peek_tag(Buffer,[PartialTag|TagAck]);
+%%peek_tag(Buffer,TagAck) ->
+%% exit({error,{asn1, {invalid_tag,lists:reverse(TagAck)}}}).
+
+
+%%===============================================================================
+%% Decode a tag
+%%
+%% decode_tag(OctetListBuffer) -> {{Class, Form, TagNo}, RestOfBuffer, RemovedBytes}
+%%===============================================================================
+
+%% multiple octet tag
+decode_tag(<<Class:2, Form:1, 31:5, Buffer/binary>>) ->
+ {TagNo, Buffer1, RemovedBytes} = decode_tag(Buffer, 0, 1),
+ {{(Class bsl 6), (Form bsl 5), TagNo}, Buffer1, RemovedBytes};
+
+%% single tag (< 31 tags)
+decode_tag(<<Class:2,Form:1,TagNo:5, Buffer/binary>>) ->
+ {{(Class bsl 6), (Form bsl 5), TagNo}, Buffer, 1}.
+
+%% last partial tag
+decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck, RemovedBytes) ->
+ TagNo = (TagAck bsl 7) bor PartialTag,
+ %%<<TagNo>> = <<TagAck:1, PartialTag:7>>,
+ {TagNo, Buffer, RemovedBytes+1};
+% more tags
+decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck, RemovedBytes) ->
+ TagAck1 = (TagAck bsl 7) bor PartialTag,
+ %%<<TagAck1:16>> = <<TagAck:1, PartialTag:7,0:8>>,
+ decode_tag(Buffer, TagAck1, RemovedBytes+1).
+
+%%------------------------------------------------------------------
+%% check_tags_i is the same as check_tags except that it stops and
+%% returns the remaining tags not checked when it encounters an
+%% indefinite length field
+%% only called internally within this module
+
+check_tags_i([Tag], Buffer, OptOrMand) -> % optimized very usual case
+ {[],check_one_tag(Tag, Buffer, OptOrMand)};
+check_tags_i(Tags, Buffer, OptOrMand) ->
+ check_tags_i(Tags, Buffer, 0, OptOrMand).
+
+check_tags_i([Tag1,Tag2|TagRest], Buffer, Rb, OptOrMand)
+ when Tag1#tag.type == 'IMPLICIT' ->
+ check_tags_i([Tag1#tag{type=Tag2#tag.type}|TagRest], Buffer, Rb, OptOrMand);
+
+check_tags_i([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
+ {Form_Length,Buffer2,Rb1} = check_one_tag(Tag1, Buffer, OptOrMand),
+ case TagRest of
+ [] -> {TagRest, {Form_Length, Buffer2, Rb + Rb1}};
+ _ ->
+ case Form_Length of
+ {?CONSTRUCTED,_} ->
+ {TagRest, {Form_Length, Buffer2, Rb + Rb1}};
+ _ ->
+ check_tags_i(TagRest, Buffer2, Rb + Rb1, mandatory)
+ end
+ end;
+
+check_tags_i([], Buffer, Rb, _) ->
+ {[],{{0,0},Buffer,Rb}}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This function is called from generated code
+
+check_tags([Tag], Buffer, OptOrMand) -> % optimized very usual case
+ check_one_tag(Tag, Buffer, OptOrMand);
+check_tags(Tags, Buffer, OptOrMand) ->
+ check_tags(Tags, Buffer, 0, OptOrMand).
+
+check_tags([Tag1,Tag2|TagRest], Buffer, Rb, OptOrMand)
+ when Tag1#tag.type == 'IMPLICIT' ->
+ check_tags([Tag1#tag{type=Tag2#tag.type}|TagRest], Buffer, Rb, OptOrMand);
+
+check_tags([Tag1|TagRest], Buffer, Rb, OptOrMand) ->
+ {Form_Length,Buffer2,Rb1} = check_one_tag(Tag1, Buffer, OptOrMand),
+ case TagRest of
+ [] -> {Form_Length, Buffer2, Rb + Rb1};
+ _ -> check_tags(TagRest, Buffer2, Rb + Rb1, mandatory)
+ end;
+
+check_tags([], Buffer, Rb, _) ->
+ {{0,0},Buffer,Rb}.
+
+check_one_tag(Tag=#tag{class=ExpectedClass,number=ExpectedNumber}, Buffer, OptOrMand) ->
+ case catch decode_tag(Buffer) of
+ {'EXIT',_Reason} ->
+ tag_error(no_data,Tag,Buffer,OptOrMand);
+ {{ExpectedClass,Form,ExpectedNumber},Buffer2,Rb} ->
+ {{L,Buffer3},RemBytes2} = decode_length(Buffer2),
+ {{Form,L}, Buffer3, RemBytes2+Rb};
+ {ErrorTag,_,_} ->
+ tag_error(ErrorTag, Tag, Buffer, OptOrMand)
+ end.
+
+tag_error(ErrorTag, Tag, Buffer, OptOrMand) ->
+ case OptOrMand of
+ mandatory ->
+ exit({error,{asn1, {invalid_tag,
+ {ErrorTag, Tag, Buffer}}}});
+ _ ->
+ exit({error,{asn1, {no_optional_tag,
+ {ErrorTag, Tag, Buffer}}}})
+ end.
+%%=======================================================================
+%%
+%% Encode all tags in the list Tags and return a possibly deep list of
+%% bytes with tag and length encoded
+%%
+%% prepend_tags(Tags, BytesSoFar, LenSoFar) -> {Bytes, Len}
+encode_tags(Tags, BytesSoFar, LenSoFar) ->
+ NewTags = encode_tags1(Tags, []),
+ %% NewTags contains the resulting tags in reverse order
+ encode_tags2(NewTags, BytesSoFar, LenSoFar).
+
+%encode_tags2([#tag{class=?UNIVERSAL,number=No}|Trest], BytesSoFar, LenSoFar) ->
+% {Bytes2,L2} = encode_length(LenSoFar),
+% encode_tags2(Trest,[[No|Bytes2],BytesSoFar], LenSoFar + 1 + L2);
+encode_tags2([Tag|Trest], BytesSoFar, LenSoFar) ->
+ {Bytes1,L1} = encode_one_tag(Tag),
+ {Bytes2,L2} = encode_length(LenSoFar),
+ encode_tags2(Trest, [Bytes1,Bytes2|BytesSoFar],
+ LenSoFar + L1 + L2);
+encode_tags2([], BytesSoFar, LenSoFar) ->
+ {BytesSoFar,LenSoFar}.
+
+encode_tags1([Tag1, Tag2| Trest], Acc)
+ when Tag1#tag.type == 'IMPLICIT' ->
+ encode_tags1([Tag1#tag{type=Tag2#tag.type,form=Tag2#tag.form}|Trest],Acc);
+encode_tags1([Tag1 | Trest], Acc) ->
+ encode_tags1(Trest, [Tag1|Acc]);
+encode_tags1([], Acc) ->
+ Acc. % the resulting tags are returned in reverse order
+
+encode_one_tag(Bin) when binary(Bin) ->
+ {Bin,size(Bin)};
+encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
+ NewForm = case Type of
+ 'EXPLICIT' ->
+ ?CONSTRUCTED;
+ _ ->
+ Form
+ end,
+ Bytes = encode_tag_val({Class,NewForm,No}),
+ {Bytes,size(Bytes)}.
+
+%%===============================================================================
+%% Change the tag (used when an implicit tagged type has a reference to something else)
+%% The constructed bit in the tag is taken from the tag to be replaced.
+%%
+%% change_tag(NewTag,[Tag,Buffer]) -> [NewTag,Buffer]
+%%===============================================================================
+
+%change_tag({NewClass,NewTagNr}, Buffer) ->
+% {{OldClass, OldForm, OldTagNo}, Buffer1, RemovedBytes} = decode_tag(lists:flatten(Buffer)),
+% [encode_tag_val({NewClass, OldForm, NewTagNr}) | Buffer1].
+
+
+
+
+
+
+
+%%===============================================================================
+%%
+%% This comment is valid for all the encode/decode functions
+%%
+%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
+%% used for PER-coding but not for BER-coding.
+%%
+%% Val = Value. If Val is an atom then it is a symbolic integer value
+%% (i.e the atom must be one of the names in the NamedNumberList).
+%% The NamedNumberList is used to translate the atom to an integer value
+%% before encoding.
+%%
+%%===============================================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+
+%% This version does not consider Explicit tagging of the open type. It
+%% is only left because of backward compatibility.
+encode_open_type(Val) when list(Val) ->
+ {Val,size(list_to_binary(Val))};
+encode_open_type(Val) ->
+ {Val, size(Val)}.
+
+%%
+encode_open_type(Val, []) when list(Val) ->
+ {Val,size(list_to_binary(Val))};
+encode_open_type(Val,[]) ->
+ {Val, size(Val)};
+encode_open_type(Val, Tag) when list(Val) ->
+ encode_tags(Tag,Val,size(list_to_binary(Val)));
+encode_open_type(Val,Tag) ->
+ encode_tags(Tag,Val, size(Val)).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer) -> Value
+%% Bytes = [byte] with BER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes) ->
+ {_Tag, Len, _RemainingBuffer, RemovedBytes} = decode_tag_and_length(Bytes),
+ N = Len + RemovedBytes,
+ <<Val:N/binary, RemainingBytes/binary>> = Bytes,
+ {Val, RemainingBytes, Len + RemovedBytes}.
+
+decode_open_type(Bytes,ExplTag) ->
+ {Tag, Len, RemainingBuffer, RemovedBytes} = decode_tag_and_length(Bytes),
+ case {Tag,ExplTag} of
+ {{Class,Form,No},[#tag{class=Class,number=No,form=Form}]} ->
+ {_Tag2, Len2, _RemainingBuffer2, RemovedBytes2} = decode_tag_and_length(RemainingBuffer),
+ N = Len2 + RemovedBytes2,
+ <<_:RemovedBytes/unit:8,Val:N/binary,RemainingBytes/binary>> = Bytes,
+ {Val, RemainingBytes, N + RemovedBytes};
+ _ ->
+ N = Len + RemovedBytes,
+ <<Val:N/binary, RemainingBytes/binary>> = Bytes,
+ {Val, RemainingBytes, Len + RemovedBytes}
+ end.
+
+decode_open_type(ber_bin,Bytes,ExplTag) ->
+ decode_open_type(Bytes,ExplTag);
+decode_open_type(ber,Bytes,ExplTag) ->
+ {Val,RemBytes,Len}=decode_open_type(Bytes,ExplTag),
+ {binary_to_list(Val),RemBytes,Len}.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Boolean, ITU_T X.690 Chapter 8.2
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode_boolean(Integer, tag | notag) -> [octet list]
+%%===============================================================================
+
+encode_boolean({Name, Val}, DoTag) when atom(Name) ->
+ dotag(DoTag, ?N_BOOLEAN, encode_boolean(Val));
+encode_boolean(true,[]) ->
+ {[1,1,16#FF],3};
+encode_boolean(false,[]) ->
+ {[1,1,0],3};
+encode_boolean(Val, DoTag) ->
+ dotag(DoTag, ?N_BOOLEAN, encode_boolean(Val)).
+
+%% encode_boolean(Boolean) -> [Len, Boolean] = [1, $FF | 0]
+encode_boolean(true) -> {[16#FF],1};
+encode_boolean(false) -> {[0],1};
+encode_boolean(X) -> exit({error,{asn1, {encode_boolean, X}}}).
+
+
+%%===============================================================================
+%% decode_boolean(BuffList, HasTag, TotalLen) -> {true, Remain, RemovedBytes} |
+%% {false, Remain, RemovedBytes}
+%%===============================================================================
+
+decode_boolean(Buffer, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_BOOLEAN}),
+ decode_boolean_notag(Buffer, NewTags, OptOrMand).
+
+decode_boolean_notag(Buffer, Tags, OptOrMand) ->
+ {RestTags, {FormLen,Buffer0,Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val,Buffer1,Rb1} = decode_boolean_notag(Buffer00, RestTags, OptOrMand),
+ {Buffer2, Rb2} = restbytes2(RestBytes,Buffer1,noext),
+ {Val, Buffer2, Rb0+Rb1+Rb2};
+ {_,_} ->
+ decode_boolean2(Buffer0, Rb0)
+ end.
+
+decode_boolean2(<<0:8, Buffer/binary>>, RemovedBytes) ->
+ {false, Buffer, RemovedBytes + 1};
+decode_boolean2(<<_:8, Buffer/binary>>, RemovedBytes) ->
+ {true, Buffer, RemovedBytes + 1};
+decode_boolean2(Buffer, _) ->
+ exit({error,{asn1, {decode_boolean, Buffer}}}).
+
+
+
+
+%%===========================================================================
+%% Integer, ITU_T X.690 Chapter 8.3
+
+%% encode_integer(Constraint, Value, Tag) -> [octet list]
+%% encode_integer(Constraint, Name, NamedNumberList, Tag) -> [octet list]
+%% Value = INTEGER | {Name,INTEGER}
+%% Tag = tag | notag
+%%===========================================================================
+
+encode_integer(C, Val, []) when integer(Val) ->
+ {EncVal,Len}=encode_integer(C, Val),
+ dotag_universal(?N_INTEGER,EncVal,Len);
+encode_integer(C, Val, Tag) when integer(Val) ->
+ dotag(Tag, ?N_INTEGER, encode_integer(C, Val));
+encode_integer(C,{Name,Val},Tag) when atom(Name) ->
+ encode_integer(C,Val,Tag);
+encode_integer(_, Val, _) ->
+ exit({error,{asn1, {encode_integer, Val}}}).
+
+
+
+encode_integer(C, Val, NamedNumberList, Tag) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value,{_, NewVal}} ->
+ dotag(Tag, ?N_INTEGER, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {encode_integer_namednumber, Val}}})
+ end;
+encode_integer(C,{_,Val},NamedNumberList,Tag) ->
+ encode_integer(C,Val,NamedNumberList,Tag);
+encode_integer(C, Val, _NamedNumberList, Tag) ->
+ dotag(Tag, ?N_INTEGER, encode_integer(C, Val)).
+
+
+
+
+encode_integer(_C, Val) ->
+ Bytes =
+ if
+ Val >= 0 ->
+ encode_integer_pos(Val, []);
+ true ->
+ encode_integer_neg(Val, [])
+ end,
+ {Bytes,length(Bytes)}.
+
+encode_integer_pos(0, L=[B|_Acc]) when B < 128 ->
+ L;
+encode_integer_pos(N, Acc) ->
+ encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
+
+encode_integer_neg(-1, L=[B1|_T]) when B1 > 127 ->
+ L;
+encode_integer_neg(N, Acc) ->
+ encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
+
+%%===============================================================================
+%% decode integer
+%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%===============================================================================
+
+
+decode_integer(Buffer, Range, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_INTEGER}),
+ decode_integer_notag(Buffer, Range, [], NewTags, OptOrMand).
+
+decode_integer(Buffer, Range, NamedNumberList, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_INTEGER}),
+ decode_integer_notag(Buffer, Range, NamedNumberList, NewTags, OptOrMand).
+
+decode_integer_notag(Buffer, Range, NamedNumberList, NewTags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(NewTags, Buffer, OptOrMand),
+% Result = {Val, Buffer2, RemovedBytes} =
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00, RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_integer_notag(Buffer00, Range, NamedNumberList,
+ RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_, Len} ->
+ Result =
+ decode_integer2(Len,Buffer0,Rb0+Len),
+ Result2 = check_integer_constraint(Result,Range),
+ resolve_named_value(Result2,NamedNumberList)
+ end.
+
+resolve_named_value(Result={Val,Buffer,RemBytes},NamedNumberList) ->
+ case NamedNumberList of
+ [] -> Result;
+ _ ->
+ NewVal = case lists:keysearch(Val, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ Val
+ end,
+ {NewVal, Buffer, RemBytes}
+ end.
+
+check_integer_constraint(Result={Val, _Buffer,_},Range) ->
+ case Range of
+ [] -> % No length constraint
+ Result;
+ {Lb,Ub} when Val >= Lb, Ub >= Val -> % variable length constraint
+ Result;
+ Val -> % fixed value constraint
+ Result;
+ {_,_} ->
+ exit({error,{asn1,{integer_range,Range,Val}}});
+ SingleValue when integer(SingleValue) ->
+ exit({error,{asn1,{integer_range,Range,Val}}});
+ _ -> % some strange constraint that we don't support yet
+ Result
+ end.
+
+%%============================================================================
+%% Enumerated value, ITU_T X.690 Chapter 8.4
+
+%% encode enumerated value
+%%============================================================================
+encode_enumerated(Val, []) when integer(Val)->
+ {EncVal,Len} = encode_integer(false,Val),
+ dotag_universal(?N_ENUMERATED,EncVal,Len);
+encode_enumerated(Val, DoTag) when integer(Val)->
+ dotag(DoTag, ?N_ENUMERATED, encode_integer(false,Val));
+encode_enumerated({Name,Val}, DoTag) when atom(Name) ->
+ encode_enumerated(Val, DoTag).
+
+%% The encode_enumerated functions below this line can be removed when the
+%% new code generation is stable. (the functions might have to be kept here
+%% a while longer for compatibility reasons)
+
+encode_enumerated(C, Val, {NamedNumberList,ExtList}, DoTag) when atom(Val) ->
+ case catch encode_enumerated(C, Val, NamedNumberList, DoTag) of
+ {'EXIT',_} -> encode_enumerated(C, Val, ExtList, DoTag);
+ Result -> Result
+ end;
+
+encode_enumerated(C, Val, NamedNumberList, DoTag) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value, {_, NewVal}} when DoTag == []->
+ {EncVal,Len} = encode_integer(C,NewVal),
+ dotag_universal(?N_ENUMERATED,EncVal,Len);
+ {value, {_, NewVal}} ->
+ dotag(DoTag, ?N_ENUMERATED, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {enumerated_not_in_range, Val}}})
+ end;
+
+encode_enumerated(C, {asn1_enum, Val}, {_,_}, DoTag) when integer(Val) ->
+ dotag(DoTag, ?N_ENUMERATED, encode_integer(C,Val));
+
+encode_enumerated(C, {Name,Val}, NamedNumberList, DoTag) when atom(Name) ->
+ encode_enumerated(C, Val, NamedNumberList, DoTag);
+
+encode_enumerated(_, Val, _, _) ->
+ exit({error,{asn1, {enumerated_not_namednumber, Val}}}).
+
+
+
+%%============================================================================
+%% decode enumerated value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) ->
+%% {Value, RemainingBuffer, RemovedBytes}
+%%===========================================================================
+decode_enumerated(Buffer, Range, NamedNumberList, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_ENUMERATED}),
+ decode_enumerated_notag(Buffer, Range, NamedNumberList,
+ NewTags, OptOrMand).
+
+decode_enumerated_notag(Buffer, Range, NNList = {NamedNumberList,ExtList}, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_enumerated_notag(Buffer00, Range, NNList, RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ {Val01, Buffer01, Rb01} =
+ decode_integer2(Len, Buffer0, Rb0+Len),
+ case decode_enumerated1(Val01, NamedNumberList) of
+ {asn1_enum,Val01} ->
+ {decode_enumerated1(Val01,ExtList), Buffer01, Rb01};
+ Result01 ->
+ {Result01, Buffer01, Rb01}
+ end
+ end;
+
+decode_enumerated_notag(Buffer, Range, NNList, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_enumerated_notag(Buffer00, Range, NNList, RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ {Val01, Buffer02, Rb02} =
+ decode_integer2(Len, Buffer0, Rb0+Len),
+ case decode_enumerated1(Val01, NNList) of
+ {asn1_enum,_} ->
+ exit({error,{asn1, {illegal_enumerated, Val01}}});
+ Result01 ->
+ {Result01, Buffer02, Rb02}
+ end
+ end.
+
+decode_enumerated1(Val, NamedNumberList) ->
+ %% it must be a named integer
+ case lists:keysearch(Val, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ {asn1_enum,Val}
+ end.
+
+
+%%============================================================================
+%%
+%% Real value, ITU_T X.690 Chapter 8.5
+%%============================================================================
+%%
+%% encode real value
+%%============================================================================
+
+%% only base 2 internally so far!!
+encode_real(0, DoTag) ->
+ dotag(DoTag, ?N_REAL, {[],0});
+encode_real('PLUS-INFINITY', DoTag) ->
+ dotag(DoTag, ?N_REAL, {[64],1});
+encode_real('MINUS-INFINITY', DoTag) ->
+ dotag(DoTag, ?N_REAL, {[65],1});
+encode_real(Val, DoTag) when tuple(Val)->
+ dotag(DoTag, ?N_REAL, encode_real(Val)).
+
+%%%%%%%%%%%%%%
+%% not optimal efficient..
+%% only base 2 of Mantissa encoding!
+%% only base 2 of ExpBase encoding!
+encode_real({Man, Base, Exp}) ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+
+ OctExp = if Exp >= 0 -> list_to_binary(encode_integer_pos(Exp, []));
+ true -> list_to_binary(encode_integer_neg(Exp, []))
+ end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+ SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval
+ true -> 1
+ end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+ InBase = if Base =:= 2 -> 0; % bit 6,5: only base 2 this far!
+ true ->
+ exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}})
+ end,
+ SFactor = 0, % bit 4,3: no scaling since only base 2
+ OctExpLen = size(OctExp),
+ if OctExpLen > 255 ->
+ exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+ true -> true %% make real assert later..
+ end,
+ {LenCode, EOctets} = case OctExpLen of % bit 2,1
+ 1 -> {0, OctExp};
+ 2 -> {1, OctExp};
+ 3 -> {2, OctExp};
+ _ -> {3, <<OctExpLen, OctExp/binary>>}
+ end,
+ FirstOctet = <<1:1,SignBit:1,InBase:2,SFactor:2,LenCode:2>>,
+ OctMantissa = if Man > 0 -> list_to_binary(minimum_octets(Man));
+ true -> list_to_binary(minimum_octets(-(Man))) % signbit keeps track of sign
+ end,
+ %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+ Bin = <<FirstOctet/binary, EOctets/binary, OctMantissa/binary>>,
+ {Bin, size(Bin)}.
+
+
+%encode_real({Man, Base, Exp}) ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+
+% OctExp = if Exp >= 0 -> encode_integer_pos(Exp, []);
+% true -> encode_integer_neg(Exp, [])
+% end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+% SignBitMask = if Man > 0 -> 2#00000000; % bit 7 is pos or neg, no Zeroval
+% true -> 2#01000000
+% end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+% InternalBaseMask = if Base =:= 2 -> 2#00000000; % bit 6,5: only base 2 this far!
+% true ->
+% exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}})
+% end,
+% ScalingFactorMask =2#00000000, % bit 4,3: no scaling since only base 2
+% OctExpLen = length(OctExp),
+% if OctExpLen > 255 ->
+% exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+% true -> true %% make real assert later..
+% end,
+% {LenMask, EOctets} = case OctExpLen of % bit 2,1
+% 1 -> {0, OctExp};
+% 2 -> {1, OctExp};
+% 3 -> {2, OctExp};
+% _ -> {3, [OctExpLen, OctExp]}
+% end,
+% FirstOctet = (SignBitMask bor InternalBaseMask bor
+% ScalingFactorMask bor LenMask bor
+% 2#10000000), % bit set for binary mantissa encoding!
+% OctMantissa = if Man > 0 -> minimum_octets(Man);
+% true -> minimum_octets(-(Man)) % signbit keeps track of sign
+% end,
+%% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+% {[FirstOctet, EOctets, OctMantissa],
+% length(OctMantissa) +
+% (if OctExpLen > 3 ->
+% OctExpLen + 2;
+% true ->
+% OctExpLen + 1
+% end)
+% }.
+
+
+%%============================================================================
+%% decode real value
+%%
+%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
+%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
+%% RestBuff}
+%%
+%% only for base 2 decoding sofar!!
+%%============================================================================
+
+decode_real(Buffer, Form, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_REAL}),
+ decode_real_notag(Buffer, Form, NewTags, OptOrMand).
+
+decode_real_notag(Buffer, Form, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_real_notag(Buffer00, Form, RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ decode_real2(Buffer0, Form, Len, Rb0)
+ end.
+
+decode_real2(Buffer0, Form, Len, RemBytes1) ->
+ <<First, Buffer2/binary>> = Buffer0,
+ if
+ First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
+ First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
+ First =:= 2#00000000 -> {0, Buffer2};
+ true ->
+ %% have some check here to verify only supported bases (2)
+ <<_B7:1,B6:1,B5_4:2,B3_2:2,B1_0:2>> = <<First>>,
+ Sign = B6,
+ Base =
+ case B5_4 of
+ 0 -> 2; % base 2, only one so far
+ _ -> exit({error,{asn1, {non_supported_base, First}}})
+ end,
+% ScalingFactor =
+ case B3_2 of
+ 0 -> 0; % no scaling so far
+ _ -> exit({error,{asn1, {non_supported_scaling, First}}})
+ end,
+ % ok = io:format("Buffer2: ~w~n",[Buffer2]),
+ {FirstLen, {Exp, Buffer3}, RemBytes2} =
+ case B1_0 of
+ 0 -> {2, decode_integer2(1, Buffer2, RemBytes1), RemBytes1+1};
+ 1 -> {3, decode_integer2(2, Buffer2, RemBytes1), RemBytes1+2};
+ 2 -> {4, decode_integer2(3, Buffer2, RemBytes1), RemBytes1+3};
+ 3 ->
+ <<ExpLen1,RestBuffer/binary>> = Buffer2,
+ { ExpLen1 + 2,
+ decode_integer2(ExpLen1, RestBuffer, RemBytes1),
+ RemBytes1+ExpLen1}
+ end,
+ % io:format("FirstLen: ~w, Exp: ~w, Buffer3: ~w ~n",
+ % [FirstLen, Exp, Buffer3]),
+ Length = Len - FirstLen,
+ <<LongInt:Length/unit:8,RestBuff/binary>> = Buffer3,
+ {{Mantissa, Buffer4}, RemBytes3} =
+ if Sign =:= 0 ->
+ % io:format("sign plus~n"),
+ {{LongInt, RestBuff}, 1 + Length};
+ true ->
+ % io:format("sign minus~n"),
+ {{-LongInt, RestBuff}, 1 + Length}
+ end,
+ % io:format("Form: ~w~n",[Form]),
+ case Form of
+ tuple ->
+ {Val,Buf,_RemB} = Exp,
+ {{Mantissa, Base, {Val,Buf}}, Buffer4, RemBytes2+RemBytes3};
+ _value ->
+ comming
+ end
+ end.
+
+
+%%============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.6
+%%
+%% encode bitstring value
+%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constrint Len, only valid when identifiers
+%%============================================================================
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,DoTag) when integer(Unused), binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList,DoTag);
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList, DoTag) when atom(FirstVal) ->
+ encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, DoTag);
+
+encode_bit_string(C, [{bit,X} | RestVal], NamedBitList, DoTag) ->
+ encode_bit_string_named(C, [{bit,X} | RestVal], NamedBitList, DoTag);
+
+encode_bit_string(C, [FirstVal| RestVal], NamedBitList, DoTag) when integer(FirstVal) ->
+ encode_bit_string_bits(C, [FirstVal | RestVal], NamedBitList, DoTag);
+
+encode_bit_string(_, 0, _, []) ->
+ {[?N_BIT_STRING,1,0],3};
+
+encode_bit_string(_, 0, _, DoTag) ->
+ dotag(DoTag, ?N_BIT_STRING, {<<0>>,1});
+
+encode_bit_string(_, [], _, []) ->
+ {[?N_BIT_STRING,1,0],3};
+
+encode_bit_string(_, [], _, DoTag) ->
+ dotag(DoTag, ?N_BIT_STRING, {<<0>>,1});
+
+encode_bit_string(C, IntegerVal, NamedBitList, DoTag) when integer(IntegerVal) ->
+ BitListVal = int_to_bitlist(IntegerVal),
+ encode_bit_string_bits(C, BitListVal, NamedBitList, DoTag);
+
+encode_bit_string(C, {Name,BitList}, NamedBitList, DoTag) when atom(Name) ->
+ encode_bit_string(C, BitList, NamedBitList, DoTag).
+
+
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+
+%%=================================================================
+%% Encode BIT STRING of the form {Unused,BinBits}.
+%% Unused is the number of unused bits in the last byte in BinBits
+%% and BinBits is a binary representing the BIT STRING.
+%%=================================================================
+encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList,DoTag)->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ remove_unused_then_dotag(DoTag,?N_BIT_STRING,Unused,BinBits);
+ {_Min,Max} ->
+ BBLen = (size(BinBits)*8)-Unused,
+ if
+ BBLen > Max ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBLen},{maximum,Max}}}}});
+ true ->
+ remove_unused_then_dotag(DoTag,?N_BIT_STRING,
+ Unused,BinBits)
+ end;
+ Size ->
+ case ((size(BinBits)*8)-Unused) of
+ BBSize when BBSize =< Size ->
+ remove_unused_then_dotag(DoTag,?N_BIT_STRING,
+ Unused,BinBits);
+ BBSize ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBSize},{should_be,Size}}}}})
+ end
+ end.
+
+remove_unused_then_dotag(DoTag,StringType,Unused,BinBits) ->
+ case Unused of
+ 0 when (size(BinBits) == 0),DoTag==[] ->
+ %% time optimization of next case
+ {[StringType,1,0],3};
+ 0 when (size(BinBits) == 0) ->
+ dotag(DoTag,StringType,{<<0>>,1});
+ 0 when DoTag==[]-> % time optimization of next case
+ dotag_universal(StringType,[Unused|BinBits],size(BinBits)+1);
+% {LenEnc,Len} = encode_legth(size(BinBits)+1),
+% {[StringType,LenEnc,[Unused|BinBits]],size(BinBits)+1+Len+1};
+ 0 ->
+ dotag(DoTag,StringType,<<Unused,BinBits/binary>>);
+ Num when DoTag == [] -> % time optimization of next case
+ N = (size(BinBits)-1),
+ <<BBits:N/binary,LastByte>> = BinBits,
+ dotag_universal(StringType,
+ [Unused,BBits,(LastByte bsr Num) bsl Num],
+ size(BinBits)+1);
+% {LenEnc,Len} = encode_legth(size(BinBits)+1),
+% {[StringType,LenEnc,[Unused,BBits,(LastByte bsr Num) bsl Num],
+% 1+Len+size(BinBits)+1};
+ Num ->
+ N = (size(BinBits)-1),
+ <<BBits:N/binary,LastByte>> = BinBits,
+ dotag(DoTag,StringType,{[Unused,binary_to_list(BBits) ++
+ [(LastByte bsr Num) bsl Num]],
+ 1+size(BinBits)})
+ end.
+
+
+%%=================================================================
+%% Encode named bits
+%%=================================================================
+
+encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, DoTag) ->
+ {Len,Unused,OctetList} =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal],
+ NamedBitList, []),
+ BitList = make_and_set_list(lists:max(ToSetPos)+1,
+ ToSetPos, 0),
+ encode_bitstring(BitList);
+ {_Min,Max} ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal],
+ NamedBitList, []),
+ BitList = make_and_set_list(Max, ToSetPos, 0),
+ encode_bitstring(BitList);
+ Size ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal],
+ NamedBitList, []),
+ BitList = make_and_set_list(Size, ToSetPos, 0),
+ encode_bitstring(BitList)
+ end,
+ case DoTag of
+ [] ->
+ dotag_universal(?N_BIT_STRING,[Unused|OctetList],Len+1);
+% {EncLen,LenLen} = encode_length(Len+1),
+% {[?N_BIT_STRING,EncLen,Unused,OctetList],1+LenLen+Len+1};
+ _ ->
+ dotag(DoTag, ?N_BIT_STRING, {[Unused|OctetList],Len+1})
+ end.
+
+
+%%----------------------------------------
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+%%----------------------------------------
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+get_all_bitposes([Val | Rest], NamedBitList, Ack) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+
+%%----------------------------------------
+%% make_and_set_list(Len of list to return, [list of positions to set to 1])->
+%% returns list of Len length, with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%% Len will make a list of length Len, not Len + 1.
+%% BitList = make_and_set_list(C, ToSetPos, 0),
+%%----------------------------------------
+
+make_and_set_list(0, [], _) -> [];
+make_and_set_list(0, _, _) ->
+ exit({error,{asn1,bitstring_sizeconstraint}});
+make_and_set_list(Len, [XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(Len - 1, SetPos, XPos + 1)];
+make_and_set_list(Len, [Pos|SetPos], XPos) ->
+ [0 | make_and_set_list(Len - 1, [Pos | SetPos], XPos + 1)];
+make_and_set_list(Len, [], XPos) ->
+ [0 | make_and_set_list(Len - 1, [], XPos + 1)].
+
+
+
+
+
+
+%%=================================================================
+%% Encode bit string for lists of ones and zeroes
+%%=================================================================
+encode_bit_string_bits(C, BitListVal, _NamedBitList, DoTag) when list(BitListVal) ->
+ {Len,Unused,OctetList} =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ encode_bitstring(BitListVal);
+ Constr={Min,Max} when integer(Min),integer(Max) ->
+ encode_constr_bit_str_bits(Constr,BitListVal,DoTag);
+ {Constr={_,_},[]} ->
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,DoTag);
+ Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,DoTag);
+ Size ->
+ case length(BitListVal) of
+ BitSize when BitSize == Size ->
+ encode_bitstring(BitListVal);
+ BitSize when BitSize < Size ->
+ PaddedList =
+ pad_bit_list(Size-BitSize,BitListVal),
+ encode_bitstring(PaddedList);
+ BitSize ->
+ exit({error,
+ {asn1,
+ {bitstring_length,
+ {{was,BitSize},
+ {should_be,Size}}}}})
+ end
+ end,
+ %%add unused byte to the Len
+ case DoTag of
+ [] ->
+ dotag_universal(?N_BIT_STRING,[Unused|OctetList],Len+1);
+% {EncLen,LenLen}=encode_length(Len+1),
+% {[?N_BIT_STRING,EncLen,Unused|OctetList],1+LenLen+Len+1};
+ _ ->
+ dotag(DoTag, ?N_BIT_STRING,
+ {[Unused | OctetList],Len+1})
+ end.
+
+
+encode_constr_bit_str_bits({_Min,Max},BitListVal,_DoTag) ->
+ BitLen = length(BitListVal),
+ if
+ BitLen > Max ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max}}}}});
+ true ->
+ encode_bitstring(BitListVal)
+ end;
+encode_constr_bit_str_bits({{_Min1,Max1},{Min2,Max2}},BitListVal,_DoTag) ->
+ BitLen = length(BitListVal),
+ case BitLen of
+ Len when Len > Max2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max2}}}}});
+ Len when Len > Max1, Len < Min2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {not_allowed_interval,
+ Max1,Min2}}}}});
+ _ ->
+ encode_bitstring(BitListVal)
+ end.
+
+%% returns a list of length Size + length(BitListVal), with BitListVal
+%% as the most significant elements followed by padded zero elements
+pad_bit_list(Size,BitListVal) ->
+ Tail = lists:duplicate(Size,0),
+ lists:append(BitListVal,Tail).
+
+%%=================================================================
+%% Do the actual encoding
+%% ([bitlist]) -> {ListLen, UnusedBits, OctetList}
+%%=================================================================
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest]) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Val], 1);
+encode_bitstring(Val) ->
+ {Unused, Octet} = unused_bitlist(Val, 7, 0),
+ {1, Unused, [Octet]}.
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest], Ack, Len) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Ack | [Val]], Len + 1);
+%%even multiple of 8 bits..
+encode_bitstring([], Ack, Len) ->
+ {Len, 0, Ack};
+%% unused bits in last octet
+encode_bitstring(Rest, Ack, Len) ->
+% io:format("uneven ~w ~w ~w~n",[Rest, Ack, Len]),
+ {Unused, Val} = unused_bitlist(Rest, 7, 0),
+ {Len + 1, Unused, [Ack | [Val]]}.
+
+%%%%%%%%%%%%%%%%%%
+%% unused_bitlist([list of ones and zeros <= 7], 7, []) ->
+%% {Unused bits, Last octet with bits moved to right}
+unused_bitlist([], Trail, Ack) ->
+ {Trail + 1, Ack};
+unused_bitlist([Bit | Rest], Trail, Ack) ->
+%% io:format("trail Bit: ~w Rest: ~w Trail: ~w Ack:~w~n",[Bit, Rest, Trail, Ack]),
+ unused_bitlist(Rest, Trail - 1, (Bit bsl Trail) bor Ack).
+
+
+%%============================================================================
+%% decode bitstring value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%============================================================================
+
+decode_compact_bit_string(Buffer, Range, NamedNumberList, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags, LenIn,
+ NamedNumberList, OptOrMand,bin).
+
+decode_bit_string(Buffer, Range, NamedNumberList, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags, LenIn,
+ NamedNumberList, OptOrMand,old).
+
+
+decode_bit_string2(1,<<0 ,Buffer/binary>>,_NamedNumberList,RemovedBytes,BinOrOld) ->
+ case BinOrOld of
+ bin ->
+ {{0,<<>>},Buffer,RemovedBytes};
+ _ ->
+ {[], Buffer, RemovedBytes}
+ end;
+decode_bit_string2(Len,<<Unused,Buffer/binary>>,NamedNumberList,
+ RemovedBytes,BinOrOld) ->
+ L = Len - 1,
+ <<Bits:L/binary,BufferTail/binary>> = Buffer,
+ case NamedNumberList of
+ [] ->
+ case BinOrOld of
+ bin ->
+ {{Unused,Bits},BufferTail,RemovedBytes};
+ _ ->
+ BitString = decode_bitstring2(L, Unused, Buffer),
+ {BitString,BufferTail, RemovedBytes}
+ end;
+ _ ->
+ BitString = decode_bitstring2(L, Unused, Buffer),
+ {decode_bitstring_NNL(BitString,NamedNumberList),
+ BufferTail,
+ RemovedBytes}
+ end.
+
+%%----------------------------------------
+%% Decode the in buffer to bits
+%%----------------------------------------
+decode_bitstring2(1,Unused,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,_/binary>>) ->
+ lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0],8-Unused);
+decode_bitstring2(Len, Unused,
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Buffer/binary>>) ->
+ [B7, B6, B5, B4, B3, B2, B1, B0 |
+ decode_bitstring2(Len - 1, Unused, Buffer)].
+
+%%decode_bitstring2(1, Unused, Buffer) ->
+%% make_bits_of_int(hd(Buffer), 128, 8-Unused);
+%%decode_bitstring2(Len, Unused, [BitVal | Buffer]) ->
+%% [B7, B6, B5, B4, B3, B2, B1, B0] = make_bits_of_int(BitVal, 128, 8),
+%% [B7, B6, B5, B4, B3, B2, B1, B0 |
+%% decode_bitstring2(Len - 1, Unused, Buffer)].
+
+
+%%make_bits_of_int(_, _, 0) ->
+%% [];
+%%make_bits_of_int(BitVal, MaskVal, Unused) when Unused > 0 ->
+%% X = case MaskVal band BitVal of
+%% 0 -> 0 ;
+%% _ -> 1
+%% end,
+%% [X | make_bits_of_int(BitVal, MaskVal bsr 1, Unused - 1)].
+
+
+
+%%----------------------------------------
+%% Decode the bitlist to names
+%%----------------------------------------
+
+
+decode_bitstring_NNL(BitList,NamedNumberList) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,0,[]).
+
+
+decode_bitstring_NNL([],_,_No,Result) ->
+ lists:reverse(Result);
+
+decode_bitstring_NNL([B|BitList],[{Name,No}|NamedNumberList],No,Result) ->
+ if
+ B == 0 ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result);
+ true ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[Name|Result])
+ end;
+decode_bitstring_NNL([1|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[{bit,No}|Result]);
+decode_bitstring_NNL([0|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result).
+
+
+%%============================================================================
+%% Octet string, ITU_T X.690 Chapter 8.7
+%%
+%% encode octet string
+%% The OctetList must be a flat list of integers in the range 0..255
+%% the function does not check this because it takes to much time
+%%============================================================================
+encode_octet_string(_C, OctetList, []) when binary(OctetList) ->
+ dotag_universal(?N_OCTET_STRING,OctetList,size(OctetList));
+encode_octet_string(_C, OctetList, DoTag) when binary(OctetList) ->
+ dotag(DoTag, ?N_OCTET_STRING, {OctetList,size(OctetList)});
+encode_octet_string(_C, OctetList, DoTag) when list(OctetList) ->
+ case length(OctetList) of
+ Len when DoTag == [] ->
+ dotag_universal(?N_OCTET_STRING,OctetList,Len);
+ Len ->
+ dotag(DoTag, ?N_OCTET_STRING, {OctetList,Len})
+ end;
+% encode_octet_string(C, OctetList, DoTag) when list(OctetList) ->
+% dotag(DoTag, ?N_OCTET_STRING, {OctetList,length(OctetList)});
+encode_octet_string(C, {Name,OctetList}, DoTag) when atom(Name) ->
+ encode_octet_string(C, OctetList, DoTag).
+
+
+%%============================================================================
+%% decode octet string
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%
+%% Octet string is decoded as a restricted string
+%%============================================================================
+decode_octet_string(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_OCTET_STRING,
+ Tags, TotalLen, [], OptOrMand,old).
+
+%%============================================================================
+%% Null value, ITU_T X.690 Chapter 8.8
+%%
+%% encode NULL value
+%%============================================================================
+
+encode_null(_, []) ->
+ {[?N_NULL,0],2};
+encode_null(_, DoTag) ->
+ dotag(DoTag, ?N_NULL, {[],0}).
+
+%%============================================================================
+%% decode NULL value
+%% (Buffer, HasTag, TotalLen) -> {NULL, Remain, RemovedBytes}
+%%============================================================================
+decode_null(Buffer, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_NULL}),
+ decode_null_notag(Buffer, NewTags, OptOrMand).
+
+decode_null_notag(Buffer, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {_Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} = decode_null_notag(Buffer0, RestTags,
+ OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,0} ->
+ {'NULL', Buffer0, Rb0};
+ {_,Len} ->
+ exit({error,{asn1,{invalid_length,'NULL',Len}}})
+ end.
+
+
+%%============================================================================
+%% Object identifier, ITU_T X.690 Chapter 8.19
+%%
+%% encode Object Identifier value
+%%============================================================================
+
+encode_object_identifier({Name,Val}, DoTag) when atom(Name) ->
+ encode_object_identifier(Val, DoTag);
+encode_object_identifier(Val, []) ->
+ {EncVal,Len} = e_object_identifier(Val),
+ dotag_universal(?N_OBJECT_IDENTIFIER,EncVal,Len);
+encode_object_identifier(Val, DoTag) ->
+ dotag(DoTag, ?N_OBJECT_IDENTIFIER, e_object_identifier(Val)).
+
+e_object_identifier({'OBJECT IDENTIFIER', V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname, V}) when atom(Cname), tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname, V}) when atom(Cname), list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%%%%%%%%%%%%%%%
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+e_object_identifier([E1, E2 | Tail]) ->
+ Head = 40*E1 + E2, % wow!
+ {H,Lh} = mk_object_val(Head),
+ {R,Lr} = enc_obj_id_tail(Tail, [], 0),
+ {[H|R], Lh+Lr}.
+
+enc_obj_id_tail([], Ack, Len) ->
+ {lists:reverse(Ack), Len};
+enc_obj_id_tail([H|T], Ack, Len) ->
+ {B, L} = mk_object_val(H),
+ enc_obj_id_tail(T, [B|Ack], Len+L).
+
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+%%e_object_identifier([E1, E2 | Tail]) ->
+%% Head = 40*E1 + E2, % wow!
+%% F = fun(Val, AckLen) ->
+%% {L, Ack} = mk_object_val(Val),
+%% {L, Ack + AckLen}
+%% end,
+%% {Octets, Len} = lists:mapfoldl(F, 0, [Head | Tail]).
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+
+%%============================================================================
+%% decode Object Identifier value
+%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
+%%============================================================================
+
+decode_object_identifier(Buffer, Tags, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,
+ number=?N_OBJECT_IDENTIFIER}),
+ decode_object_identifier_notag(Buffer, NewTags, OptOrMand).
+
+decode_object_identifier_notag(Buffer, Tags, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_object_identifier_notag(Buffer00,
+ RestTags, OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ {[AddedObjVal|ObjVals],Buffer01} =
+ dec_subidentifiers(Buffer0,0,[],Len),
+ {Val1, Val2} = if
+ AddedObjVal < 40 ->
+ {0, AddedObjVal};
+ AddedObjVal < 80 ->
+ {1, AddedObjVal - 40};
+ true ->
+ {2, AddedObjVal - 80}
+ end,
+ {list_to_tuple([Val1, Val2 | ObjVals]), Buffer01,
+ Rb0+Len}
+ end.
+
+dec_subidentifiers(Buffer,_Av,Al,0) ->
+ {lists:reverse(Al),Buffer};
+dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al,Len) ->
+ dec_subidentifiers(T,(Av bsl 7) + H,Al,Len-1);
+dec_subidentifiers(<<H,T/binary>>,Av,Al,Len) ->
+ dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al],Len-1).
+
+
+%%dec_subidentifiers(Buffer,Av,Al,0) ->
+%% {lists:reverse(Al),Buffer};
+%%dec_subidentifiers([H|T],Av,Al,Len) when H >=16#80 ->
+%% dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al,Len-1);
+%%dec_subidentifiers([H|T],Av,Al,Len) ->
+%% dec_subidentifiers(T,0,[(Av bsl 7) + H |Al],Len-1).
+
+
+%%============================================================================
+%% Restricted character string types, ITU_T X.690 Chapter 8.20
+%%
+%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%%============================================================================
+encode_restricted_string(_C, OctetList, StringType, [])
+ when binary(OctetList) ->
+ dotag_universal(StringType,OctetList,size(OctetList));
+encode_restricted_string(_C, OctetList, StringType, DoTag)
+ when binary(OctetList) ->
+ dotag(DoTag, StringType, {OctetList, size(OctetList)});
+encode_restricted_string(_C, OctetList, StringType, [])
+ when list(OctetList) ->
+ dotag_universal(StringType,OctetList,length(OctetList));
+encode_restricted_string(_C, OctetList, StringType, DoTag)
+ when list(OctetList) ->
+ dotag(DoTag, StringType, {OctetList, length(OctetList)});
+encode_restricted_string(C,{Name,OctetL},StringType,DoTag) when atom(Name)->
+ encode_restricted_string(C, OctetL, StringType, DoTag).
+
+%%============================================================================
+%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_restricted_string(Buffer, Range, StringType, Tags, LenIn, OptOrMand) ->
+ {Val,Buffer2,Rb} =
+ decode_restricted_string_tag(Buffer, Range, StringType, Tags,
+ LenIn, [], OptOrMand,old),
+ {check_and_convert_restricted_string(Val,StringType,Range,[],old),
+ Buffer2,Rb}.
+
+
+decode_restricted_string(Buffer, Range, StringType, Tags, LenIn, NNList, OptOrMand, BinOrOld ) ->
+ {Val,Buffer2,Rb} =
+ decode_restricted_string_tag(Buffer, Range, StringType, Tags,
+ LenIn, NNList, OptOrMand, BinOrOld),
+ {check_and_convert_restricted_string(Val,StringType,Range,NNList,BinOrOld),
+ Buffer2,Rb}.
+
+decode_restricted_string_tag(Buffer, Range, StringType, TagsIn, LenIn, NNList, OptOrMand, BinOrOld ) ->
+ NewTags = new_tags(TagsIn, #tag{class=?UNIVERSAL,number=StringType}),
+ decode_restricted_string_notag(Buffer, Range, StringType, NewTags,
+ LenIn, NNList, OptOrMand, BinOrOld).
+
+
+
+
+check_and_convert_restricted_string(Val,StringType,Range,NamedNumberList,_BinOrOld) ->
+ {StrLen,NewVal} = case StringType of
+ ?N_BIT_STRING when NamedNumberList /= [] ->
+ {no_check,Val};
+ ?N_BIT_STRING when list(Val) ->
+ {length(Val),Val};
+ ?N_BIT_STRING when tuple(Val) ->
+ {(size(element(2,Val))*8) - element(1,Val),Val};
+ _ when binary(Val) ->
+ {size(Val),binary_to_list(Val)};
+ _ when list(Val) ->
+ {length(Val), Val}
+ end,
+ case Range of
+ _ when StrLen == no_check ->
+ NewVal;
+ [] -> % No length constraint
+ NewVal;
+ {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint
+ NewVal;
+ {{Lb,_Ub},[]} when StrLen >= Lb ->
+ NewVal;
+ {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
+ StrLen =< Ub2, StrLen >= Lb2 ->
+ NewVal;
+ StrLen -> % fixed length constraint
+ NewVal;
+ {_,_} ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _Len when integer(_Len) ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _ -> % some strange constraint that we don't support yet
+ NewVal
+ end.
+
+
+%%=============================================================================
+%% Common routines for several string types including bit string
+%% handles indefinite length
+%%=============================================================================
+
+
+decode_restricted_string_notag(Buffer, _Range, StringType, TagsIn,
+ _, NamedNumberList, OptOrMand,BinOrOld) ->
+ %%-----------------------------------------------------------
+ %% Get inner (the implicit tag or no tag) and
+ %% outer (the explicit tag) lengths.
+ %%-----------------------------------------------------------
+ {RestTags, {FormLength={_,_Len01}, Buffer0, Rb0}} =
+ check_tags_i(TagsIn, Buffer, OptOrMand),
+
+ case FormLength of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00, RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_restricted_parts(Buffer00, RestBytes, [], StringType,
+ RestTags,
+ Len, NamedNumberList,
+ OptOrMand,
+ BinOrOld, 0, []),
+ {Val01, Buffer01, Rb0+Rb01};
+ {_, Len} ->
+ {Val01, Buffer01, Rb01} =
+ decode_restricted(Buffer0, Len, StringType,
+ NamedNumberList, BinOrOld),
+ {Val01, Buffer01, Rb0+Rb01}
+ end.
+
+
+decode_restricted_parts(Buffer, RestBytes, [], StringType, RestTags, Len, NNList,
+ OptOrMand, BinOrOld, AccRb, AccVal) ->
+ DecodeFun = case RestTags of
+ [] -> fun decode_restricted_string_tag/8;
+ _ -> fun decode_restricted_string_notag/8
+ end,
+ {Val, Buffer1, Rb} =
+ DecodeFun(Buffer, [], StringType, RestTags,
+ no_length, NNList,
+ OptOrMand, BinOrOld),
+ {Buffer2,More} =
+ case Buffer1 of
+ <<0,0,Buffer10/binary>> when Len == indefinite ->
+ {Buffer10,false};
+ <<>> ->
+ {RestBytes,false};
+ _ ->
+ {Buffer1,true}
+ end,
+ {NewVal, NewRb} =
+ case StringType of
+ ?N_BIT_STRING when BinOrOld == bin ->
+ {concat_bit_binaries(AccVal, Val), AccRb+Rb};
+ _ when binary(Val),binary(AccVal) ->
+ {<<AccVal/binary,Val/binary>>,AccRb+Rb};
+ _ when binary(Val), AccVal==[] ->
+ {Val,AccRb+Rb};
+ _ ->
+ {AccVal++Val, AccRb+Rb}
+ end,
+ case More of
+ false ->
+ {NewVal, Buffer2, NewRb};
+ true ->
+ decode_restricted_parts(Buffer2, RestBytes, [], StringType, RestTags, Len, NNList,
+ OptOrMand, BinOrOld, NewRb, NewVal)
+ end.
+
+
+
+decode_restricted(Buffer, InnerLen, StringType, NamedNumberList,BinOrOld) ->
+
+ case StringType of
+ ?N_BIT_STRING ->
+ decode_bit_string2(InnerLen,Buffer,NamedNumberList,InnerLen,BinOrOld);
+
+ ?N_UniversalString ->
+ <<PreBuff:InnerLen/binary,RestBuff/binary>> = Buffer,%%added for binary
+ UniString = mk_universal_string(binary_to_list(PreBuff)),
+ {UniString,RestBuff,InnerLen};
+ ?N_BMPString ->
+ <<PreBuff:InnerLen/binary,RestBuff/binary>> = Buffer,%%added for binary
+ BMP = mk_BMP_string(binary_to_list(PreBuff)),
+ {BMP,RestBuff,InnerLen};
+ _ ->
+ <<PreBuff:InnerLen/binary,RestBuff/binary>> = Buffer,%%added for binary
+ {PreBuff, RestBuff, InnerLen}
+ end.
+
+
+
+%%============================================================================
+%% encode Universal string
+%%============================================================================
+
+encode_universal_string(C, {Name, Universal}, DoTag) when atom(Name) ->
+ encode_universal_string(C, Universal, DoTag);
+encode_universal_string(_C, Universal, []) ->
+ OctetList = mk_uni_list(Universal),
+ dotag_universal(?N_UniversalString,OctetList,length(OctetList));
+encode_universal_string(_C, Universal, DoTag) ->
+ OctetList = mk_uni_list(Universal),
+ dotag(DoTag, ?N_UniversalString, {OctetList,length(OctetList)}).
+
+mk_uni_list(In) ->
+ mk_uni_list(In,[]).
+
+mk_uni_list([],List) ->
+ lists:reverse(List);
+mk_uni_list([{A,B,C,D}|T],List) ->
+ mk_uni_list(T,[D,C,B,A|List]);
+mk_uni_list([H|T],List) ->
+ mk_uni_list(T,[H,0,0,0|List]).
+
+%%===========================================================================
+%% decode Universal strings
+%% (Buffer, Range, StringType, HasTag, LenIn) ->
+%% {String, Remain, RemovedBytes}
+%%===========================================================================
+
+decode_universal_string(Buffer, Range, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag, #tag{class=?UNIVERSAL,number=?N_UniversalString}),
+ decode_restricted_string(Buffer, Range, ?N_UniversalString,
+ Tags, LenIn, [], OptOrMand,old).
+
+
+mk_universal_string(In) ->
+ mk_universal_string(In,[]).
+
+mk_universal_string([],Acc) ->
+ lists:reverse(Acc);
+mk_universal_string([0,0,0,D|T],Acc) ->
+ mk_universal_string(T,[D|Acc]);
+mk_universal_string([A,B,C,D|T],Acc) ->
+ mk_universal_string(T,[{A,B,C,D}|Acc]).
+
+
+%%============================================================================
+%% encode BMP string
+%%============================================================================
+
+encode_BMP_string(C, {Name,BMPString}, DoTag) when atom(Name)->
+ encode_BMP_string(C, BMPString, DoTag);
+encode_BMP_string(_C, BMPString, []) ->
+ OctetList = mk_BMP_list(BMPString),
+ dotag_universal(?N_BMPString,OctetList,length(OctetList));
+encode_BMP_string(_C, BMPString, DoTag) ->
+ OctetList = mk_BMP_list(BMPString),
+ dotag(DoTag, ?N_BMPString, {OctetList,length(OctetList)}).
+
+mk_BMP_list(In) ->
+ mk_BMP_list(In,[]).
+
+mk_BMP_list([],List) ->
+ lists:reverse(List);
+mk_BMP_list([{0,0,C,D}|T],List) ->
+ mk_BMP_list(T,[D,C|List]);
+mk_BMP_list([H|T],List) ->
+ mk_BMP_list(T,[H,0|List]).
+
+%%============================================================================
+%% decode (OctetList, Range(ignored), tag|notag) -> {ValList, RestList}
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+decode_BMP_string(Buffer, Range, Tags, LenIn, OptOrMand) ->
+% NewTags = new_tags(HasTag, #tag{class=?UNIVERSAL,number=?N_BMPString}),
+ decode_restricted_string(Buffer, Range, ?N_BMPString,
+ Tags, LenIn, [], OptOrMand,old).
+
+mk_BMP_string(In) ->
+ mk_BMP_string(In,[]).
+
+mk_BMP_string([],US) ->
+ lists:reverse(US);
+mk_BMP_string([0,B|T],US) ->
+ mk_BMP_string(T,[B|US]);
+mk_BMP_string([C,D|T],US) ->
+ mk_BMP_string(T,[{0,0,C,D}|US]).
+
+
+%%============================================================================
+%% Generalized time, ITU_T X.680 Chapter 39
+%%
+%% encode Generalized time
+%%============================================================================
+
+encode_generalized_time(C, {Name,OctetList}, DoTag) when atom(Name) ->
+ encode_generalized_time(C, OctetList, DoTag);
+encode_generalized_time(_C, OctetList, []) ->
+ dotag_universal(?N_GeneralizedTime,OctetList,length(OctetList));
+encode_generalized_time(_C, OctetList, DoTag) ->
+ dotag(DoTag, ?N_GeneralizedTime, {OctetList,length(OctetList)}).
+
+%%============================================================================
+%% decode Generalized time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_generalized_time(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,
+ number=?N_GeneralizedTime}),
+ decode_generalized_time_notag(Buffer, Range, NewTags, TotalLen, OptOrMand).
+
+decode_generalized_time_notag(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_generalized_time_notag(Buffer00, Range,
+ RestTags, TotalLen,
+ OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ <<PreBuff:Len/binary,RestBuff/binary>> = Buffer0,
+ {binary_to_list(PreBuff), RestBuff, Rb0+Len}
+ end.
+
+%%============================================================================
+%% Universal time, ITU_T X.680 Chapter 40
+%%
+%% encode UTC time
+%%============================================================================
+
+encode_utc_time(C, {Name,OctetList}, DoTag) when atom(Name) ->
+ encode_utc_time(C, OctetList, DoTag);
+encode_utc_time(_C, OctetList, []) ->
+ dotag_universal(?N_UTCTime, OctetList,length(OctetList));
+encode_utc_time(_C, OctetList, DoTag) ->
+ dotag(DoTag, ?N_UTCTime, {OctetList,length(OctetList)}).
+
+%%============================================================================
+%% decode UTC time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_utc_time(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ NewTags = new_tags(Tags,#tag{class=?UNIVERSAL,number=?N_UTCTime}),
+ decode_utc_time_notag(Buffer, Range, NewTags, TotalLen, OptOrMand).
+
+decode_utc_time_notag(Buffer, Range, Tags, TotalLen, OptOrMand) ->
+ {RestTags, {FormLen, Buffer0, Rb0}} =
+ check_tags_i(Tags, Buffer, OptOrMand),
+
+ case FormLen of
+ {?CONSTRUCTED,Len} ->
+ {Buffer00,RestBytes} = split_list(Buffer0,Len),
+ {Val01, Buffer01, Rb01} =
+ decode_utc_time_notag(Buffer00, Range,
+ RestTags, TotalLen,
+ OptOrMand),
+ {Buffer02, Rb02} = restbytes2(RestBytes,Buffer01,noext),
+ {Val01, Buffer02, Rb0+Rb01+Rb02};
+ {_,Len} ->
+ <<PreBuff:Len/binary,RestBuff/binary>> = Buffer0,
+ {binary_to_list(PreBuff), RestBuff, Rb0+Len}
+ end.
+
+
+%%============================================================================
+%% Length handling
+%%
+%% Encode length
+%%
+%% encode_length(Int | indefinite) ->
+%% [<127]| [128 + Int (<127),OctetList] | [16#80]
+%%============================================================================
+
+encode_length(indefinite) ->
+ {[16#80],1}; % 128
+encode_length(L) when L =< 16#7F ->
+ {[L],1};
+encode_length(L) ->
+ Oct = minimum_octets(L),
+ Len = length(Oct),
+ if
+ Len =< 126 ->
+ {[ (16#80+Len) | Oct ],Len+1};
+ true ->
+ exit({error,{asn1, to_long_length_oct, Len}})
+ end.
+
+
+%% Val must be >= 0
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(0,Acc) ->
+ Acc;
+minimum_octets(Val, Acc) ->
+ minimum_octets((Val bsr 8),[Val band 16#FF | Acc]).
+
+
+%%===========================================================================
+%% Decode length
+%%
+%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
+%% {{Length, RestOctetsL}, NoRemovedBytes}
+%%===========================================================================
+
+decode_length(<<1:1,0:7,T/binary>>) ->
+ {{indefinite, T}, 1};
+decode_length(<<0:1,Length:7,T/binary>>) ->
+ {{Length,T},1};
+decode_length(<<1:1,LL:7,T/binary>>) ->
+ <<Length:LL/unit:8,Rest/binary>> = T,
+ {{Length,Rest}, LL+1}.
+
+%decode_length([128 | T]) ->
+% {{indefinite, T},1};
+%decode_length([H | T]) when H =< 127 ->
+% {{H, T},1};
+%decode_length([H | T]) ->
+% dec_long_length(H band 16#7F, T, 0, 1).
+
+
+%%dec_long_length(0, Buffer, Acc, Len) ->
+%% {{Acc, Buffer},Len};
+%%dec_long_length(Bytes, [H | T], Acc, Len) ->
+%% dec_long_length(Bytes - 1, T, (Acc bsl 8) + H, Len+1).
+
+%%===========================================================================
+%% Decode tag and length
+%%
+%% decode_tag_and_length(Buffer) -> {Tag, Len, RemainingBuffer, RemovedBytes}
+%%
+%%===========================================================================
+
+decode_tag_and_length(Buffer) ->
+ {Tag, Buffer2, RemBytesTag} = decode_tag(Buffer),
+ {{Len, Buffer3}, RemBytesLen} = decode_length(Buffer2),
+ {Tag, Len, Buffer3, RemBytesTag+RemBytesLen}.
+
+
+%%============================================================================
+%% Check if valid tag
+%%
+%% check_if_valid_tag(Tag, List_of_valid_tags, OptOrMand) -> name of the tag
+%%===============================================================================
+
+check_if_valid_tag(<<0,0,_/binary>>,_,_) ->
+ asn1_EOC;
+check_if_valid_tag(<<>>, _, OptOrMand) ->
+ check_if_valid_tag2(false,[],[],OptOrMand);
+check_if_valid_tag(Bytes, ListOfTags, OptOrMand) when binary(Bytes) ->
+ {Tag, _, _} = decode_tag(Bytes),
+ check_if_valid_tag(Tag, ListOfTags, OptOrMand);
+
+%% This alternative should be removed in the near future
+%% Bytes as input should be the only necessary call
+check_if_valid_tag(Tag, ListOfTags, OptOrMand) ->
+ {Class, _Form, TagNo} = Tag,
+ C = code_class(Class),
+ T = case C of
+ 'UNIVERSAL' ->
+ code_type(TagNo);
+ _ ->
+ TagNo
+ end,
+ check_if_valid_tag2({C,T}, ListOfTags, Tag, OptOrMand).
+
+check_if_valid_tag2(_Class_TagNo, [], Tag, mandatory) ->
+ exit({error,{asn1,{invalid_tag,Tag}}});
+check_if_valid_tag2(_Class_TagNo, [], Tag, _) ->
+ exit({error,{asn1,{no_optional_tag,Tag}}});
+
+check_if_valid_tag2(Class_TagNo, [{TagName,TagList}|T], Tag, OptOrMand) ->
+ case check_if_valid_tag_loop(Class_TagNo, TagList) of
+ true ->
+ TagName;
+ false ->
+ check_if_valid_tag2(Class_TagNo, T, Tag, OptOrMand)
+ end.
+
+check_if_valid_tag_loop(_Class_TagNo,[]) ->
+ false;
+check_if_valid_tag_loop(Class_TagNo,[H|T]) ->
+ %% It is not possible to distinguish between SEQUENCE OF and SEQUENCE, and
+ %% between SET OF and SET because both are coded as 16 and 17, respectively.
+ H_without_OF = case H of
+ {C, 'SEQUENCE OF'} ->
+ {C, 'SEQUENCE'};
+ {C, 'SET OF'} ->
+ {C, 'SET'};
+ Else ->
+ Else
+ end,
+
+ case H_without_OF of
+ Class_TagNo ->
+ true;
+ {_,_} ->
+ check_if_valid_tag_loop(Class_TagNo,T);
+ _ ->
+ check_if_valid_tag_loop(Class_TagNo,H),
+ check_if_valid_tag_loop(Class_TagNo,T)
+ end.
+
+
+
+code_class(0) -> 'UNIVERSAL';
+code_class(16#40) -> 'APPLICATION';
+code_class(16#80) -> 'CONTEXT';
+code_class(16#C0) -> 'PRIVATE'.
+
+
+code_type(1) -> 'BOOLEAN';
+code_type(2) -> 'INTEGER';
+code_type(3) -> 'BIT STRING';
+code_type(4) -> 'OCTET STRING';
+code_type(5) -> 'NULL';
+code_type(6) -> 'OBJECT IDENTIFIER';
+code_type(7) -> 'OBJECT DESCRIPTOR';
+code_type(8) -> 'EXTERNAL';
+code_type(9) -> 'REAL';
+code_type(10) -> 'ENUMERATED';
+code_type(11) -> 'EMBEDDED_PDV';
+code_type(16) -> 'SEQUENCE';
+code_type(16) -> 'SEQUENCE OF';
+code_type(17) -> 'SET';
+code_type(17) -> 'SET OF';
+code_type(18) -> 'NumericString';
+code_type(19) -> 'PrintableString';
+code_type(20) -> 'TeletexString';
+code_type(21) -> 'VideotexString';
+code_type(22) -> 'IA5String';
+code_type(23) -> 'UTCTime';
+code_type(24) -> 'GeneralizedTime';
+code_type(25) -> 'GraphicString';
+code_type(26) -> 'VisibleString';
+code_type(27) -> 'GeneralString';
+code_type(28) -> 'UniversalString';
+code_type(30) -> 'BMPString';
+code_type(Else) -> exit({error,{asn1,{unrecognized_type,Else}}}).
+
+%%-------------------------------------------------------------------------
+%% decoding of the components of a SET
+%%-------------------------------------------------------------------------
+
+decode_set(Rb, indefinite, <<0,0,Bytes/binary>>, _OptOrMand, _Fun3, Acc) ->
+ {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_set(Rb, indefinite, Bytes, OptOrMand, Fun3, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, OptOrMand),
+ decode_set(Rb+Rb1, indefinite, Remain, OptOrMand, Fun3, [Term|Acc]);
+
+decode_set(Rb, Num, Bytes, _OptOrMand, _Fun3, Acc) when Num == 0 ->
+ {lists:reverse(Acc), Bytes, Rb};
+
+decode_set(_, Num, _, _, _, _) when Num < 0 ->
+ exit({error,{asn1,{length_error,'SET'}}});
+
+decode_set(Rb, Num, Bytes, OptOrMand, Fun3, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, OptOrMand),
+ decode_set(Rb+Rb1, Num-Rb1, Remain, OptOrMand, Fun3, [Term|Acc]).
+
+
+%%-------------------------------------------------------------------------
+%% decoding of SEQUENCE OF and SET OF
+%%-------------------------------------------------------------------------
+
+decode_components(Rb, indefinite, <<0,0,Bytes/binary>>, _Fun3, _TagIn, Acc) ->
+ {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_components(Rb, indefinite, Bytes, Fun3, TagIn, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, mandatory, TagIn),
+ decode_components(Rb+Rb1, indefinite, Remain, Fun3, TagIn, [Term|Acc]);
+
+decode_components(Rb, Num, Bytes, _Fun3, _TagIn, Acc) when Num == 0 ->
+ {lists:reverse(Acc), Bytes, Rb};
+
+decode_components(_, Num, _, _, _, _) when Num < 0 ->
+ exit({error,{asn1,{length_error,'SET/SEQUENCE OF'}}});
+
+decode_components(Rb, Num, Bytes, Fun3, TagIn, Acc) ->
+ {Term, Remain, Rb1} = Fun3(Bytes, mandatory, TagIn),
+ decode_components(Rb+Rb1, Num-Rb1, Remain, Fun3, TagIn, [Term|Acc]).
+
+%%decode_components(Rb, indefinite, [0,0|Bytes], _Fun3, _TagIn, Acc) ->
+%% {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_components(Rb, indefinite, <<0,0,Bytes/binary>>, _Fun4, _TagIn, _Fun, Acc) ->
+ {lists:reverse(Acc),Bytes,Rb+2};
+
+decode_components(Rb, indefinite, Bytes, _Fun4, TagIn, _Fun, Acc) ->
+ {Term, Remain, Rb1} = _Fun4(Bytes, mandatory, TagIn, _Fun),
+ decode_components(Rb+Rb1, indefinite, Remain, _Fun4, TagIn, _Fun, [Term|Acc]);
+
+decode_components(Rb, Num, Bytes, _Fun4, _TagIn, _Fun, Acc) when Num == 0 ->
+ {lists:reverse(Acc), Bytes, Rb};
+
+decode_components(_, Num, _, _, _, _, _) when Num < 0 ->
+ exit({error,{asn1,{length_error,'SET/SEQUENCE OF'}}});
+
+decode_components(Rb, Num, Bytes, _Fun4, TagIn, _Fun, Acc) ->
+ {Term, Remain, Rb1} = _Fun4(Bytes, mandatory, TagIn, _Fun),
+ decode_components(Rb+Rb1, Num-Rb1, Remain, _Fun4, TagIn, _Fun, [Term|Acc]).
+
+
+
+%%-------------------------------------------------------------------------
+%% INTERNAL HELPER FUNCTIONS (not exported)
+%%-------------------------------------------------------------------------
+
+
+%%==========================================================================
+%% Encode tag
+%%
+%% dotag(tag | notag, TagValpattern | TagValTuple, [Length, Value]) -> [Tag]
+%% TagValPattern is a correct bitpattern for a tag
+%% TagValTuple is a tuple of three bitpatterns, Class, Form and TagNo where
+%% Class = UNIVERSAL | APPLICATION | CONTEXT | PRIVATE
+%% Form = Primitive | Constructed
+%% TagNo = Number of tag
+%%==========================================================================
+
+
+dotag([], Tag, {Bytes,Len}) ->
+ dotag_universal(Tag,Bytes,Len);
+dotag(Tags, Tag, {Bytes,Len}) ->
+ encode_tags(Tags ++ [#tag{class=?UNIVERSAL,number=Tag,form=?PRIMITIVE}],
+ Bytes, Len);
+
+dotag(Tags, Tag, Bytes) ->
+ encode_tags(Tags ++ [#tag{class=?UNIVERSAL,number=Tag,form=?PRIMITIVE}],
+ Bytes, size(Bytes)).
+
+dotag_universal(UniversalTag,Bytes,Len) when Len =< 16#7F->
+ {[UniversalTag,Len,Bytes],2+Len};
+dotag_universal(UniversalTag,Bytes,Len) ->
+ {EncLen,LenLen}=encode_length(Len),
+ {[UniversalTag,EncLen,Bytes],1+LenLen+Len}.
+
+%% decoding postitive integer values.
+decode_integer2(Len,Bin = <<0:1,_:7,_Bs/binary>>,RemovedBytes) ->
+ <<Int:Len/unit:8,Buffer2/binary>> = Bin,
+ {Int,Buffer2,RemovedBytes};
+%% decoding negative integer values.
+decode_integer2(Len,<<1:1,B2:7,Bs/binary>>,RemovedBytes) ->
+ <<N:Len/unit:8,Buffer2/binary>> = <<B2,Bs/binary>>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ {Int,Buffer2,RemovedBytes}.
+
+%%decode_integer2(Len,Buffer,Acc,RemovedBytes) when (hd(Buffer) band 16#FF) =< 16#7F ->
+%% {decode_integer_pos(Buffer, 8 * (Len - 1)),skip(Buffer,Len),RemovedBytes};
+%%decode_integer2(Len,Buffer,Acc,RemovedBytes) ->
+%% {decode_integer_neg(Buffer, 8 * (Len - 1)),skip(Buffer,Len),RemovedBytes}.
+
+%%decode_integer_pos([Byte|Tail], Shift) ->
+%% (Byte bsl Shift) bor decode_integer_pos(Tail, Shift-8);
+%%decode_integer_pos([], _) -> 0.
+
+
+%%decode_integer_neg([Byte|Tail], Shift) ->
+%% (-128 + (Byte band 127) bsl Shift) bor decode_integer_pos(Tail, Shift-8).
+
+
+concat_bit_binaries([],Bin={_,_}) ->
+ Bin;
+concat_bit_binaries({0,B1},{U2,B2}) ->
+ {U2,<<B1/binary,B2/binary>>};
+concat_bit_binaries({U1,B1},{U2,B2}) ->
+ S1 = (size(B1) * 8) - U1,
+ S2 = (size(B2) * 8) - U2,
+ PadBits = 8 - ((S1+S2) rem 8),
+ {PadBits, <<B1:S1/binary-unit:1,B2:S2/binary-unit:1,0:PadBits>>};
+concat_bit_binaries(L1,L2) when list(L1),list(L2) ->
+ %% this case occur when decoding with NNL
+ L1 ++ L2.
+
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%skip(Buffer, 0) ->
+%% Buffer;
+%%skip([H | T], Len) ->
+%% skip(T, Len-1).
+
+new_tags([],LastTag) ->
+ [LastTag];
+new_tags(Tags=[#tag{type='IMPLICIT'}],_LastTag) ->
+ Tags;
+new_tags([T1 = #tag{type='IMPLICIT'},#tag{type=T2Type}|Rest],LastTag) ->
+ new_tags([T1#tag{type=T2Type}|Rest],LastTag);
+new_tags(Tags,LastTag) ->
+ case lists:last(Tags) of
+ #tag{type='IMPLICIT'} ->
+ Tags;
+ _ ->
+ Tags ++ [LastTag]
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
new file mode 100644
index 0000000000..7f7846184a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_ber_bin_v2.erl
@@ -0,0 +1,1869 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_ber_bin_v2.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt_ber_bin_v2).
+
+%% encoding / decoding of BER
+
+-export([decode/1, decode/2, match_tags/2, encode/1]).
+-export([fixoptionals/2, cindex/3,
+ list_to_record/2,
+ encode_tag_val/1,
+ encode_tags/3]).
+-export([encode_boolean/2,decode_boolean/2,
+ encode_integer/3,encode_integer/4,
+ decode_integer/3, decode_integer/4,
+ encode_enumerated/2,
+ encode_enumerated/4,decode_enumerated/4,
+ encode_real/2,decode_real/3,
+ encode_bit_string/4,decode_bit_string/4,
+ decode_compact_bit_string/4,
+ encode_octet_string/3,decode_octet_string/3,
+ encode_null/2,decode_null/2,
+ encode_object_identifier/2,decode_object_identifier/2,
+ encode_restricted_string/4,decode_restricted_string/4,
+ encode_universal_string/3,decode_universal_string/3,
+ encode_BMP_string/3,decode_BMP_string/3,
+ encode_generalized_time/3,decode_generalized_time/3,
+ encode_utc_time/3,decode_utc_time/3,
+ encode_length/1,decode_length/1,
+ decode_tag_and_length/1]).
+
+-export([encode_open_type/1,encode_open_type/2,
+ decode_open_type/2,decode_open_type_as_binary/2]).
+
+-export([decode_primitive_incomplete/2]).
+
+-include("asn1_records.hrl").
+
+% the encoding of class of tag bits 8 and 7
+-define(UNIVERSAL, 0).
+-define(APPLICATION, 16#40).
+-define(CONTEXT, 16#80).
+-define(PRIVATE, 16#C0).
+
+%%% primitive or constructed encoding % bit 6
+-define(PRIMITIVE, 0).
+-define(CONSTRUCTED, 2#00100000).
+
+%%% The tag-number for universal types
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
+-define(N_BIT_STRING, 3).
+-define(N_OCTET_STRING, 4).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
+-define(N_NumericString, 18).
+-define(N_PrintableString, 19).
+-define(N_TeletexString, 20).
+-define(N_VideotexString, 21).
+-define(N_IA5String, 22).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
+-define(N_GraphicString, 25).
+-define(N_VisibleString, 26).
+-define(N_GeneralString, 27).
+-define(N_UniversalString, 28).
+-define(N_BMPString, 30).
+
+
+% the complete tag-word of built-in types
+-define(T_BOOLEAN, ?UNIVERSAL bor ?PRIMITIVE bor 1).
+-define(T_INTEGER, ?UNIVERSAL bor ?PRIMITIVE bor 2).
+-define(T_BIT_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 3). % can be CONSTRUCTED
+-define(T_OCTET_STRING, ?UNIVERSAL bor ?PRIMITIVE bor 4). % can be CONSTRUCTED
+-define(T_NULL, ?UNIVERSAL bor ?PRIMITIVE bor 5).
+-define(T_OBJECT_IDENTIFIER,?UNIVERSAL bor ?PRIMITIVE bor 6).
+-define(T_OBJECT_DESCRIPTOR,?UNIVERSAL bor ?PRIMITIVE bor 7).
+-define(T_EXTERNAL, ?UNIVERSAL bor ?PRIMITIVE bor 8).
+-define(T_REAL, ?UNIVERSAL bor ?PRIMITIVE bor 9).
+-define(T_ENUMERATED, ?UNIVERSAL bor ?PRIMITIVE bor 10).
+-define(T_EMBEDDED_PDV, ?UNIVERSAL bor ?PRIMITIVE bor 11).
+-define(T_SEQUENCE, ?UNIVERSAL bor ?CONSTRUCTED bor 16).
+-define(T_SET, ?UNIVERSAL bor ?CONSTRUCTED bor 17).
+-define(T_NumericString, ?UNIVERSAL bor ?PRIMITIVE bor 18). %can be constructed
+-define(T_PrintableString, ?UNIVERSAL bor ?PRIMITIVE bor 19). %can be constructed
+-define(T_TeletexString, ?UNIVERSAL bor ?PRIMITIVE bor 20). %can be constructed
+-define(T_VideotexString, ?UNIVERSAL bor ?PRIMITIVE bor 21). %can be constructed
+-define(T_IA5String, ?UNIVERSAL bor ?PRIMITIVE bor 22). %can be constructed
+-define(T_UTCTime, ?UNIVERSAL bor ?PRIMITIVE bor 23).
+-define(T_GeneralizedTime, ?UNIVERSAL bor ?PRIMITIVE bor 24).
+-define(T_GraphicString, ?UNIVERSAL bor ?PRIMITIVE bor 25). %can be constructed
+-define(T_VisibleString, ?UNIVERSAL bor ?PRIMITIVE bor 26). %can be constructed
+-define(T_GeneralString, ?UNIVERSAL bor ?PRIMITIVE bor 27). %can be constructed
+-define(T_UniversalString, ?UNIVERSAL bor ?PRIMITIVE bor 28). %can be constructed
+-define(T_BMPString, ?UNIVERSAL bor ?PRIMITIVE bor 30). %can be constructed
+
+% encode(Tlv={_Tag={?PRIMITIVE,_},_VList}) ->
+% encode_primitive(Tlv);
+% encode(Tlv) ->
+% encode_constructed(Tlv).
+
+encode([Tlv]) ->
+ encode(Tlv);
+encode({TlvTag,TlvVal}) when list(TlvVal) ->
+ %% constructed form of value
+ encode_tlv(TlvTag,TlvVal,?CONSTRUCTED);
+encode({TlvTag,TlvVal}) ->
+ encode_tlv(TlvTag,TlvVal,?PRIMITIVE);
+encode(Bin) when binary(Bin) ->
+ Bin.
+
+encode_tlv(TlvTag,TlvVal,Form) ->
+ Tag = encode_tlv_tag(TlvTag,Form),
+ {Val,VLen} = encode_tlv_val(TlvVal),
+ {Len,_LLen} = encode_length(VLen),
+ BinLen = list_to_binary(Len),
+ <<Tag/binary,BinLen/binary,Val/binary>>.
+
+encode_tlv_tag(ClassTagNo,Form) ->
+ Class = ClassTagNo bsr 16,
+ case encode_tag_val({Class bsl 6,Form,(ClassTagNo - (Class bsl 16))}) of
+ T when list(T) ->
+ list_to_binary(T);
+ T ->
+ T
+ end.
+
+encode_tlv_val(TlvL) when list(TlvL) ->
+ encode_tlv_list(TlvL,[]);
+encode_tlv_val(Bin) ->
+ {Bin,size(Bin)}.
+
+encode_tlv_list([Tlv|Tlvs],Acc) ->
+ EncTlv = encode(Tlv),
+ encode_tlv_list(Tlvs,[EncTlv|Acc]);
+encode_tlv_list([],Acc) ->
+ Bin=list_to_binary(lists:reverse(Acc)),
+ {Bin,size(Bin)}.
+
+% encode_primitive({{_,ClassTagNo},V}) ->
+% Len = size(V), % not sufficient as length encode
+% Class = ClassTagNo bsr 16,
+% {TagLen,Tag} =
+% case encode_tag_val({Class,?PRIMITIVE,ClassTagNo - Class}) of
+% T when list(T) ->
+% {length(T),list_to_binary(T)};
+% T ->
+% {1,T}
+% end,
+
+
+decode(B,driver) ->
+ case catch port_control(drv_complete,2,B) of
+ Bin when binary(Bin) ->
+ binary_to_term(Bin);
+ List when list(List) -> handle_error(List,B);
+ {'EXIT',{badarg,Reason}} ->
+ asn1rt_driver_handler:load_driver(),
+ receive
+ driver_ready ->
+ case catch port_control(drv_complete,2,B) of
+ Bin2 when binary(Bin2) -> binary_to_term(Bin2);
+ List when list(List) -> handle_error(List,B);
+ Error -> exit(Error)
+ end;
+ {error,Error} -> % error when loading driver
+ %% the driver could not be loaded
+ exit(Error);
+ Error={port_error,Reason} ->
+ exit(Error)
+ end;
+ {'EXIT',Reason} ->
+ exit(Reason)
+ end.
+
+handle_error([],_)->
+ exit({error,{"memory allocation problem"}});
+handle_error([$1|_],L) -> % error in driver
+ exit({error,{asn1_error,L}});
+handle_error([$2|_],L) -> % error in driver due to wrong tag
+ exit({error,{asn1_error,{"bad tag",L}}});
+handle_error([$3|_],L) -> % error in driver due to length error
+ exit({error,{asn1_error,{"bad length field",L}}});
+handle_error([$4|_],L) -> % error in driver due to indefinite length error
+ exit({error,{asn1_error,{"indefinite length without end bytes",L}}});
+handle_error(ErrL,L) ->
+ exit({error,{unknown_error,ErrL,L}}).
+
+
+decode(Bin) when binary(Bin) ->
+ decode_primitive(Bin);
+decode(Tlv) -> % assume it is a tlv
+ {Tlv,<<>>}.
+
+
+decode_primitive(Bin) ->
+ {{Form,TagNo,Len,V},Rest} = decode_tlv(Bin),
+ case Form of
+ 1 when Len == indefinite -> % constructed
+ {Vlist,Rest2} = decode_constructed_indefinite(V,[]),
+ {{TagNo,Vlist},Rest2};
+ 1 -> % constructed
+ {{TagNo,decode_constructed(V)},Rest};
+ 0 -> % primitive
+ {{TagNo,V},Rest}
+ end.
+
+decode_constructed(<<>>) ->
+ [];
+decode_constructed(Bin) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ [Tlv|decode_constructed(Rest)].
+
+decode_constructed_indefinite(<<0,0,Rest/binary>>,Acc) ->
+ {lists:reverse(Acc),Rest};
+decode_constructed_indefinite(Bin,Acc) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ decode_constructed_indefinite(Rest, [Tlv|Acc]).
+
+decode_tlv(Bin) ->
+ {Form,TagNo,Len,Bin2} = decode_tag_and_length(Bin),
+ case Len of
+ indefinite ->
+ {{Form,TagNo,Len,Bin2},[]};
+ _ ->
+ <<V:Len/binary,Bin3/binary>> = Bin2,
+ {{Form,TagNo,Len,V},Bin3}
+ end.
+
+%% decode_primitive_incomplete/2 decodes an encoded message incomplete
+%% by help of the pattern attribute (first argument).
+decode_primitive_incomplete([[default,TagNo]],Bin) -> %default
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,[],Rest);
+ _ ->
+ %{asn1_DEFAULT,Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[default,TagNo,Directives]],Bin) -> %default, constructed type, Directives points into this type
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ %{asn1_DEFAULT,Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[opt,TagNo]],Bin) -> %optional
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,[],Rest);
+ _ ->
+ %{{TagNo,asn1_NOVALUE},Bin}
+ asn1_NOVALUE
+ end;
+decode_primitive_incomplete([[opt,TagNo,Directives]],Bin) -> %optional
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ %{{TagNo,asn1_NOVALUE},Bin}
+ asn1_NOVALUE
+ end;
+%% A choice alternative that shall be undecoded
+decode_primitive_incomplete([[alt_undec,TagNo]|RestAlts],Bin) ->
+% decode_incomplete_bin(Bin);
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,_V},_R} ->
+ decode_incomplete_bin(Bin);
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt,TagNo]|RestAlts],Bin) ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ {{TagNo,V},Rest};
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt,TagNo,Directives]|RestAlts],Bin) ->
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[alt_parts,TagNo]|RestAlts],Bin) ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ _ ->
+ decode_primitive_incomplete(RestAlts,Bin)
+ end;
+decode_primitive_incomplete([[undec,_TagNo]|_RestTag],Bin) -> %incomlete decode
+ decode_incomplete_bin(Bin); %% use this if changing handling of
+decode_primitive_incomplete([[parts,TagNo]|_RestTag],Bin) ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ {{TagNo,decode_parts_incomplete(V)},Rest};
+ Err ->
+ {error,{asn1,"tag failure",TagNo,Err}}
+ end;
+decode_primitive_incomplete([mandatory|RestTag],Bin) ->
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,RestTag,Rest);
+ _ ->
+ {error,{asn1,"partial incomplete decode failure"}}
+ end;
+%% A choice that is a toptype or a mandatory component of a
+%% SEQUENCE or SET.
+decode_primitive_incomplete([[mandatory,Directives]],Bin) ->
+ case decode_tlv(Bin) of
+ {{Form,TagNo,Len,V},Rest} ->
+ decode_incomplete2(Form,TagNo,Len,V,Directives,Rest);
+ _ ->
+ {error,{asn1,"partial incomplete decode failure"}}
+ end;
+decode_primitive_incomplete([],Bin) ->
+ decode_primitive(Bin).
+
+%% decode_parts_incomplete/1 receives a number of values encoded in
+%% sequence and returns the parts as unencoded binaries
+decode_parts_incomplete(<<>>) ->
+ [];
+decode_parts_incomplete(Bin) ->
+ {ok,Rest} = skip_tag(Bin),
+ {ok,Rest2} = skip_length_and_value(Rest),
+ LenPart = size(Bin) - size(Rest2),
+ <<Part:LenPart/binary,RestBin/binary>> = Bin,
+ [Part|decode_parts_incomplete(RestBin)].
+
+
+%% decode_incomplete2 checks if V is a value of a constructed or
+%% primitive type, and continues the decode propeerly.
+decode_incomplete2(1,TagNo,indefinite,V,TagMatch,_) ->
+ %% constructed indefinite length
+ {Vlist,Rest2} = decode_constr_indef_incomplete(TagMatch,V,[]),
+ {{TagNo,Vlist},Rest2};
+decode_incomplete2(1,TagNo,_Len,V,TagMatch,Rest) ->
+ {{TagNo,decode_constructed_incomplete(TagMatch,V)},Rest};
+decode_incomplete2(0,TagNo,_Len,V,_TagMatch,Rest) ->
+ {{TagNo,V},Rest}.
+
+decode_constructed_incomplete(_TagMatch,<<>>) ->
+ [];
+decode_constructed_incomplete([mandatory|RestTag],Bin) ->
+ {Tlv,Rest} = decode_primitive(Bin),
+ [Tlv|decode_constructed_incomplete(RestTag,Rest)];
+decode_constructed_incomplete(Directives=[[Alt,_]|_],Bin)
+ when Alt == alt_undec; Alt == alt ->
+ case decode_tlv(Bin) of
+ {{_Form,TagNo,_Len,V},Rest} ->
+ case incomplete_choice_alt(TagNo,Directives) of
+ alt_undec ->
+ LenA = size(Bin)-size(Rest),
+ <<A:LenA/binary,Rest/binary>> = Bin,
+ A;
+% {UndecBin,_}=decode_incomplete_bin(Bin),
+% UndecBin;
+% [{TagNo,V}];
+ alt ->
+ {Tlv,_} = decode_primitive(V),
+ [{TagNo,Tlv}];
+ alt_parts ->
+ %{{TagNo,decode_parts_incomplete(V)},Rest}; % maybe wrong
+ [{TagNo,decode_parts_incomplete(V)}];
+ Err ->
+ {error,{asn1,"partial incomplete decode failure",Err}}
+ end;
+ _ ->
+ {error,{asn1,"partial incomplete decode failure"}}
+ end;
+decode_constructed_incomplete([TagNo|RestTag],Bin) ->
+%% {Tlv,Rest} = decode_primitive_incomplete([TagNo],Bin),
+ case decode_primitive_incomplete([TagNo],Bin) of
+ {Tlv,Rest} ->
+ [Tlv|decode_constructed_incomplete(RestTag,Rest)];
+ asn1_NOVALUE ->
+ decode_constructed_incomplete(RestTag,Bin)
+ end;
+decode_constructed_incomplete([],Bin) ->
+ {Tlv,_Rest}=decode_primitive(Bin),
+ [Tlv].
+
+decode_constr_indef_incomplete(_TagMatch,<<0,0,Rest/binary>>,Acc) ->
+ {lists:reverse(Acc),Rest};
+decode_constr_indef_incomplete([Tag|RestTags],Bin,Acc) ->
+% {Tlv,Rest} = decode_primitive_incomplete([Tag],Bin),
+ case decode_primitive_incomplete([Tag],Bin) of
+ {Tlv,Rest} ->
+ decode_constr_indef_incomplete(RestTags,Rest,[Tlv|Acc]);
+ asn1_NOVALUE ->
+ decode_constr_indef_incomplete(RestTags,Bin,Acc)
+ end.
+
+
+decode_incomplete_bin(Bin) ->
+ {ok,Rest} = skip_tag(Bin),
+ {ok,Rest2} = skip_length_and_value(Rest),
+ IncLen = size(Bin) - size(Rest2),
+ <<IncBin:IncLen/binary,Ret/binary>> = Bin,
+ {IncBin,Ret}.
+
+incomplete_choice_alt(TagNo,[[Alt,TagNo]|_Directives]) ->
+ Alt;
+incomplete_choice_alt(TagNo,[_H|Directives]) ->
+ incomplete_choice_alt(TagNo,Directives);
+incomplete_choice_alt(_,[]) ->
+ error.
+
+
+%% skip_tag and skip_length_and_value are rutines used both by
+%% decode_partial_incomplete and decode_partial (decode/2).
+
+skip_tag(<<_:3,31:5,Rest/binary>>)->
+ skip_long_tag(Rest);
+skip_tag(<<_:3,_Tag:5,Rest/binary>>) ->
+ {ok,Rest}.
+
+skip_long_tag(<<1:1,_:7,Rest/binary>>) ->
+ skip_long_tag(Rest);
+skip_long_tag(<<0:1,_:7,Rest/binary>>) ->
+ {ok,Rest}.
+
+skip_length_and_value(Binary) ->
+ case decode_length(Binary) of
+ {indefinite,RestBinary} ->
+ skip_indefinite_value(RestBinary);
+ {Length,RestBinary} ->
+ <<_:Length/unit:8,Rest/binary>> = RestBinary,
+ {ok,Rest}
+ end.
+
+skip_indefinite_value(<<0,0,Rest/binary>>) ->
+ {ok,Rest};
+skip_indefinite_value(Binary) ->
+ {ok,RestBinary}=skip_tag(Binary),
+ {ok,RestBinary2} = skip_length_and_value(RestBinary),
+ skip_indefinite_value(RestBinary2).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% match_tags takes a Tlv (Tag, Length, Value) structure and matches
+%% it with the tags in TagList. If the tags does not match the function
+%% crashes otherwise it returns the remaining Tlv after that the tags have
+%% been removed.
+%%
+%% match_tags(Tlv, TagList)
+%%
+
+
+match_tags({T,V}, [T|Tt]) ->
+ match_tags(V,Tt);
+match_tags([{T,V}],[T|Tt]) ->
+ match_tags(V, Tt);
+match_tags(Vlist = [{T,_V}|_], [T]) ->
+ Vlist;
+match_tags(Tlv, []) ->
+ Tlv;
+match_tags({Tag,_V},[T|_Tt]) ->
+ {error,{asn1,{wrong_tag,{Tag,T}}}}.
+
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Optionals, preset not filled optionals with asn1_NOVALUE
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,_Acc1,Acc2) ->
+ % return Val as a record
+ list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]).
+
+
+%%encode_tag(TagClass(?UNI, APP etc), Form (?PRIM etx), TagInteger) ->
+%% 8bit Int | binary
+encode_tag_val({Class, Form, TagNo}) when (TagNo =< 30) ->
+ <<(Class bsr 6):2,(Form bsr 5):1,TagNo:5>>;
+
+encode_tag_val({Class, Form, TagNo}) ->
+ {Octets,_Len} = mk_object_val(TagNo),
+ BinOct = list_to_binary(Octets),
+ <<(Class bsr 6):2, (Form bsr 5):1, 31:5,BinOct/binary>>;
+
+%% asumes whole correct tag bitpattern, multiple of 8
+encode_tag_val(Tag) when (Tag =< 255) -> Tag; %% anv�nds denna funktion??!!
+%% asumes correct bitpattern of 0-5
+encode_tag_val(Tag) -> encode_tag_val2(Tag,[]).
+
+encode_tag_val2(Tag, OctAck) when (Tag =< 255) ->
+ [Tag | OctAck];
+encode_tag_val2(Tag, OctAck) ->
+ encode_tag_val2(Tag bsr 8, [255 band Tag | OctAck]).
+
+
+%%===============================================================================
+%% Decode a tag
+%%
+%% decode_tag(OctetListBuffer) -> {{Form, (Class bsl 16)+ TagNo}, RestOfBuffer, RemovedBytes}
+%%===============================================================================
+
+decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 0:1, Length:7, RestBuffer/binary>>) when TagNo < 31 ->
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 1:1, 0:7, T/binary>>) when TagNo < 31 ->
+ {Form, (Class bsl 16) + TagNo, indefinite, T};
+decode_tag_and_length(<<Class:2, Form:1, TagNo:5, 1:1, LL:7, T/binary>>) when TagNo < 31 ->
+ <<Length:LL/unit:8,RestBuffer/binary>> = T,
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 0:1, Length:7, RestBuffer/binary>>) ->
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 1:1, 0:7, T/binary>>) ->
+ {Form, (Class bsl 16) + TagNo, indefinite, T};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, 0:1, TagNo:7, 1:1, LL:7, T/binary>>) ->
+ <<Length:LL/unit:8,RestBuffer/binary>> = T,
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer};
+decode_tag_and_length(<<Class:2, Form:1, 31:5, Buffer/binary>>) ->
+ {TagNo, Buffer1} = decode_tag(Buffer, 0),
+ {Length, RestBuffer} = decode_length(Buffer1),
+ {Form, (Class bsl 16) + TagNo, Length, RestBuffer}.
+
+
+
+%% last partial tag
+decode_tag(<<0:1,PartialTag:7, Buffer/binary>>, TagAck) ->
+ TagNo = (TagAck bsl 7) bor PartialTag,
+ %%<<TagNo>> = <<TagAck:1, PartialTag:7>>,
+ {TagNo, Buffer};
+% more tags
+decode_tag(<<_:1,PartialTag:7, Buffer/binary>>, TagAck) ->
+ TagAck1 = (TagAck bsl 7) bor PartialTag,
+ %%<<TagAck1:16>> = <<TagAck:1, PartialTag:7,0:8>>,
+ decode_tag(Buffer, TagAck1).
+
+
+%%=======================================================================
+%%
+%% Encode all tags in the list Tags and return a possibly deep list of
+%% bytes with tag and length encoded
+%% The taglist must be in reverse order (fixed by the asn1 compiler)
+%% e.g [T1,T2] will result in
+%% {[EncodedT2,EncodedT1|BytesSoFar],LenSoFar+LenT2+LenT1}
+%%
+
+encode_tags([Tag|Trest], BytesSoFar, LenSoFar) ->
+% remove {Bytes1,L1} = encode_one_tag(Tag),
+ {Bytes2,L2} = encode_length(LenSoFar),
+ encode_tags(Trest, [Tag,Bytes2|BytesSoFar],
+ LenSoFar + size(Tag) + L2);
+encode_tags([], BytesSoFar, LenSoFar) ->
+ {BytesSoFar,LenSoFar}.
+
+encode_tags(TagIn, {BytesSoFar,LenSoFar}) ->
+ encode_tags(TagIn, BytesSoFar, LenSoFar).
+
+% encode_one_tag(#tag{class=Class,number=No,type=Type, form = Form}) ->
+% NewForm = case Type of
+% 'EXPLICIT' ->
+% ?CONSTRUCTED;
+% _ ->
+% Form
+% end,
+% Bytes = encode_tag_val({Class,NewForm,No}),
+% {Bytes,size(Bytes)}.
+
+
+%%===============================================================================
+%%
+%% This comment is valid for all the encode/decode functions
+%%
+%% C = Constraint -> typically {'ValueRange',LowerBound,UpperBound}
+%% used for PER-coding but not for BER-coding.
+%%
+%% Val = Value. If Val is an atom then it is a symbolic integer value
+%% (i.e the atom must be one of the names in the NamedNumberList).
+%% The NamedNumberList is used to translate the atom to an integer value
+%% before encoding.
+%%
+%%===============================================================================
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Value) -> io_list (i.e nested list with integers, binaries)
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+
+%%
+encode_open_type(Val) when list(Val) ->
+% {Val,length(Val)};
+ encode_open_type(list_to_binary(Val));
+encode_open_type(Val) ->
+ {Val, size(Val)}.
+
+%%
+encode_open_type(Val, T) when list(Val) ->
+ encode_open_type(list_to_binary(Val),T);
+encode_open_type(Val,[]) ->
+ {Val, size(Val)};
+encode_open_type(Val,Tag) ->
+ encode_tags(Tag,Val, size(Val)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Tlv, TagIn) -> Value
+%% Tlv = {Tag,V} | V where V -> binary()
+%% TagIn = [TagVal] where TagVal -> int()
+%% Value = binary with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Tlv, TagIn) ->
+ case match_tags(Tlv,TagIn) of
+ Bin when binary(Bin) ->
+ {InnerTlv,_} = decode(Bin),
+ InnerTlv;
+ TlvBytes -> TlvBytes
+ end.
+
+
+decode_open_type_as_binary(Tlv,TagIn)->
+ case match_tags(Tlv,TagIn) of
+ V when binary(V) ->
+ V;
+ [Tlv2] -> encode(Tlv2);
+ Tlv2 -> encode(Tlv2)
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Boolean, ITU_T X.690 Chapter 8.2
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode_boolean(Integer, ReversedTagList) -> {[Octet],Len}
+%%===============================================================================
+
+encode_boolean({Name, Val}, TagIn) when atom(Name) ->
+ encode_boolean(Val, TagIn);
+encode_boolean(true, TagIn) ->
+ encode_tags(TagIn, [16#FF],1);
+encode_boolean(false, TagIn) ->
+ encode_tags(TagIn, [0],1);
+encode_boolean(X,_) ->
+ exit({error,{asn1, {encode_boolean, X}}}).
+
+
+%%===============================================================================
+%% decode_boolean(BuffList, HasTag, TotalLen) -> {true, Remain, RemovedBytes} |
+%% {false, Remain, RemovedBytes}
+%%===============================================================================
+decode_boolean(Tlv,TagIn) ->
+ Val = match_tags(Tlv, TagIn),
+ case Val of
+ <<0:8>> ->
+ false;
+ <<_:8>> ->
+ true;
+ _ ->
+ exit({error,{asn1, {decode_boolean, Val}}})
+ end.
+
+
+%%===========================================================================
+%% Integer, ITU_T X.690 Chapter 8.3
+
+%% encode_integer(Constraint, Value, Tag) -> [octet list]
+%% encode_integer(Constraint, Name, NamedNumberList, Tag) -> [octet list]
+%% Value = INTEGER | {Name,INTEGER}
+%% Tag = tag | notag
+%%===========================================================================
+
+encode_integer(C, Val, Tag) when integer(Val) ->
+ encode_tags(Tag, encode_integer(C, Val));
+encode_integer(C,{Name,Val},Tag) when atom(Name) ->
+ encode_integer(C,Val,Tag);
+encode_integer(_C, Val, _Tag) ->
+ exit({error,{asn1, {encode_integer, Val}}}).
+
+
+
+encode_integer(C, Val, NamedNumberList, Tag) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value,{_, NewVal}} ->
+ encode_tags(Tag, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {encode_integer_namednumber, Val}}})
+ end;
+encode_integer(C,{_Name,Val},NamedNumberList,Tag) ->
+ encode_integer(C,Val,NamedNumberList,Tag);
+encode_integer(C, Val, _NamedNumberList, Tag) ->
+ encode_tags(Tag, encode_integer(C, Val)).
+
+
+encode_integer(_, Val) ->
+ Bytes =
+ if
+ Val >= 0 ->
+ encode_integer_pos(Val, []);
+ true ->
+ encode_integer_neg(Val, [])
+ end,
+ {Bytes,length(Bytes)}.
+
+encode_integer_pos(0, L=[B|_Acc]) when B < 128 ->
+ L;
+encode_integer_pos(N, Acc) ->
+ encode_integer_pos((N bsr 8), [N band 16#ff| Acc]).
+
+encode_integer_neg(-1, L=[B1|_T]) when B1 > 127 ->
+ L;
+encode_integer_neg(N, Acc) ->
+ encode_integer_neg(N bsr 8, [N band 16#ff|Acc]).
+
+%%===============================================================================
+%% decode integer
+%% (Buffer, Range, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%===============================================================================
+
+decode_integer(Tlv,Range,NamedNumberList,TagIn) ->
+ V = match_tags(Tlv,TagIn),
+ Int = decode_integer(V),
+ range_check_integer(Int,Range),
+ number2name(Int,NamedNumberList).
+
+decode_integer(Tlv,Range,TagIn) ->
+ V = match_tags(Tlv, TagIn),
+ Int = decode_integer(V),
+ range_check_integer(Int,Range),
+ Int.
+
+%% decoding postitive integer values.
+decode_integer(Bin = <<0:1,_:7,_/binary>>) ->
+ Len = size(Bin),
+% <<Int:Len/unit:8,Buffer2/binary>> = Bin,
+ <<Int:Len/unit:8>> = Bin,
+ Int;
+%% decoding negative integer values.
+decode_integer(Bin = <<1:1,B2:7,Bs/binary>>) ->
+ Len = size(Bin),
+% <<N:Len/unit:8,Buffer2/binary>> = <<B2,Bs/binary>>,
+ <<N:Len/unit:8>> = <<B2,Bs/binary>>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ Int.
+
+range_check_integer(Int,Range) ->
+ case Range of
+ [] -> % No length constraint
+ Int;
+ {Lb,Ub} when Int >= Lb, Ub >= Int -> % variable length constraint
+ Int;
+ Int -> % fixed value constraint
+ Int;
+ {_,_} ->
+ exit({error,{asn1,{integer_range,Range,Int}}});
+ SingleValue when integer(SingleValue) ->
+ exit({error,{asn1,{integer_range,Range,Int}}});
+ _ -> % some strange constraint that we don't support yet
+ Int
+ end.
+
+number2name(Int,[]) ->
+ Int;
+number2name(Int,NamedNumberList) ->
+ case lists:keysearch(Int, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ Int
+ end.
+
+
+%%============================================================================
+%% Enumerated value, ITU_T X.690 Chapter 8.4
+
+%% encode enumerated value
+%%============================================================================
+encode_enumerated(Val, TagIn) when integer(Val)->
+ encode_tags(TagIn, encode_integer(false,Val));
+encode_enumerated({Name,Val}, TagIn) when atom(Name) ->
+ encode_enumerated(Val, TagIn).
+
+%% The encode_enumerated functions below this line can be removed when the
+%% new code generation is stable. (the functions might have to be kept here
+%% a while longer for compatibility reasons)
+
+encode_enumerated(C, Val, {NamedNumberList,ExtList}, TagIn) when atom(Val) ->
+ case catch encode_enumerated(C, Val, NamedNumberList, TagIn) of
+ {'EXIT',_} -> encode_enumerated(C, Val, ExtList, TagIn);
+ Result -> Result
+ end;
+
+encode_enumerated(C, Val, NamedNumberList, TagIn) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedNumberList) of
+ {value, {_, NewVal}} ->
+ encode_tags(TagIn, encode_integer(C, NewVal));
+ _ ->
+ exit({error,{asn1, {enumerated_not_in_range, Val}}})
+ end;
+
+encode_enumerated(C, {asn1_enum, Val}, {_,_}, TagIn) when integer(Val) ->
+ encode_tags(TagIn, encode_integer(C,Val));
+
+encode_enumerated(C, {Name,Val}, NamedNumberList, TagIn) when atom(Name) ->
+ encode_enumerated(C, Val, NamedNumberList, TagIn);
+
+encode_enumerated(_C, Val, _NamedNumberList, _TagIn) ->
+ exit({error,{asn1, {enumerated_not_namednumber, Val}}}).
+
+
+
+%%============================================================================
+%% decode enumerated value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> Value
+%%===========================================================================
+decode_enumerated(Tlv, Range, NamedNumberList, Tags) ->
+ Buffer = match_tags(Tlv,Tags),
+ decode_enumerated_notag(Buffer, Range, NamedNumberList, Tags).
+
+decode_enumerated_notag(Buffer, _Range, {NamedNumberList,ExtList}, _Tags) ->
+
+ IVal = decode_integer2(size(Buffer), Buffer),
+ case decode_enumerated1(IVal, NamedNumberList) of
+ {asn1_enum,IVal} ->
+ decode_enumerated1(IVal,ExtList);
+ EVal ->
+ EVal
+ end;
+decode_enumerated_notag(Buffer, _Range, NNList, _Tags) ->
+ IVal = decode_integer2(size(Buffer), Buffer),
+ case decode_enumerated1(IVal, NNList) of
+ {asn1_enum,_} ->
+ exit({error,{asn1, {illegal_enumerated, IVal}}});
+ EVal ->
+ EVal
+ end.
+
+decode_enumerated1(Val, NamedNumberList) ->
+ %% it must be a named integer
+ case lists:keysearch(Val, 2, NamedNumberList) of
+ {value,{NamedVal, _}} ->
+ NamedVal;
+ _ ->
+ {asn1_enum,Val}
+ end.
+
+
+%%============================================================================
+%%
+%% Real value, ITU_T X.690 Chapter 8.5
+%%============================================================================
+%%
+%% encode real value
+%%============================================================================
+
+%% only base 2 internally so far!!
+encode_real(0, TagIn) ->
+ encode_tags(TagIn, {[],0});
+encode_real('PLUS-INFINITY', TagIn) ->
+ encode_tags(TagIn, {[64],1});
+encode_real('MINUS-INFINITY', TagIn) ->
+ encode_tags(TagIn, {[65],1});
+encode_real(Val, TagIn) when tuple(Val)->
+ encode_tags(TagIn, encode_real(Val)).
+
+%%%%%%%%%%%%%%
+%% not optimal efficient..
+%% only base 2 of Mantissa encoding!
+%% only base 2 of ExpBase encoding!
+encode_real({Man, Base, Exp}) ->
+%% io:format("Mantissa: ~w Base: ~w, Exp: ~w~n",[Man, Base, Exp]),
+
+ OctExp = if Exp >= 0 -> list_to_binary(encode_integer_pos(Exp, []));
+ true -> list_to_binary(encode_integer_neg(Exp, []))
+ end,
+%% ok = io:format("OctExp: ~w~n",[OctExp]),
+ SignBit = if Man > 0 -> 0; % bit 7 is pos or neg, no Zeroval
+ true -> 1
+ end,
+%% ok = io:format("SignBitMask: ~w~n",[SignBitMask]),
+ InBase = if Base =:= 2 -> 0; % bit 6,5: only base 2 this far!
+ true ->
+ exit({error,{asn1, {encode_real_non_supported_encodeing, Base}}})
+ end,
+ SFactor = 0, % bit 4,3: no scaling since only base 2
+ OctExpLen = size(OctExp),
+ if OctExpLen > 255 ->
+ exit({error,{asn1, {to_big_exp_in_encode_real, OctExpLen}}});
+ true -> true %% make real assert later..
+ end,
+ {LenCode, EOctets} = case OctExpLen of % bit 2,1
+ 1 -> {0, OctExp};
+ 2 -> {1, OctExp};
+ 3 -> {2, OctExp};
+ _ -> {3, <<OctExpLen, OctExp/binary>>}
+ end,
+ FirstOctet = <<1:1,SignBit:1,InBase:2,SFactor:2,LenCode:2>>,
+ OctMantissa = if Man > 0 -> list_to_binary(minimum_octets(Man));
+ true -> list_to_binary(minimum_octets(-(Man))) % signbit keeps track of sign
+ end,
+ %% ok = io:format("LenMask: ~w EOctets: ~w~nFirstOctet: ~w OctMantissa: ~w OctExpLen: ~w~n", [LenMask, EOctets, FirstOctet, OctMantissa, OctExpLen]),
+ Bin = <<FirstOctet/binary, EOctets/binary, OctMantissa/binary>>,
+ {Bin, size(Bin)}.
+
+
+%%============================================================================
+%% decode real value
+%%
+%% decode_real([OctetBufferList], tuple|value, tag|notag) ->
+%% {{Mantissa, Base, Exp} | realval | PLUS-INFINITY | MINUS-INFINITY | 0,
+%% RestBuff}
+%%
+%% only for base 2 decoding sofar!!
+%%============================================================================
+
+decode_real(Tlv, Form, Tags) ->
+ Buffer = match_tags(Tlv,Tags),
+ decode_real_notag(Buffer, Form).
+
+decode_real_notag(_Buffer, _Form) ->
+ exit({error,{asn1, {unimplemented,real}}}).
+%% decode_real2(Buffer, Form, size(Buffer)).
+
+% decode_real2(Buffer, Form, Len) ->
+% <<First, Buffer2/binary>> = Buffer,
+% if
+% First =:= 2#01000000 -> {'PLUS-INFINITY', Buffer2};
+% First =:= 2#01000001 -> {'MINUS-INFINITY', Buffer2};
+% First =:= 2#00000000 -> {0, Buffer2};
+% true ->
+% %% have some check here to verify only supported bases (2)
+% <<B7:1,B6:1,B5_4:2,B3_2:2,B1_0:2>> = <<First>>,
+% Sign = B6,
+% Base =
+% case B5_4 of
+% 0 -> 2; % base 2, only one so far
+% _ -> exit({error,{asn1, {non_supported_base, First}}})
+% end,
+% ScalingFactor =
+% case B3_2 of
+% 0 -> 0; % no scaling so far
+% _ -> exit({error,{asn1, {non_supported_scaling, First}}})
+% end,
+
+% {FirstLen,Exp,Buffer3} =
+% case B1_0 of
+% 0 ->
+% <<_:1/unit:8,Buffer21/binary>> = Buffer2,
+% {2, decode_integer2(1, Buffer2),Buffer21};
+% 1 ->
+% <<_:2/unit:8,Buffer21/binary>> = Buffer2,
+% {3, decode_integer2(2, Buffer2)};
+% 2 ->
+% <<_:3/unit:8,Buffer21/binary>> = Buffer2,
+% {4, decode_integer2(3, Buffer2)};
+% 3 ->
+% <<ExpLen1,RestBuffer/binary>> = Buffer2,
+% <<_:ExpLen1/unit:8,RestBuffer2/binary>> = RestBuffer,
+% { ExpLen1 + 2,
+% decode_integer2(ExpLen1, RestBuffer, RemBytes1),
+% RestBuffer2}
+% end,
+% Length = Len - FirstLen,
+% <<LongInt:Length/unit:8,RestBuff/binary>> = Buffer3,
+% {Mantissa, Buffer4} =
+% if Sign =:= 0 ->
+
+% {LongInt, RestBuff};% sign plus,
+% true ->
+
+% {-LongInt, RestBuff}% sign minus
+% end,
+% case Form of
+% tuple ->
+% {Val,Buf,RemB} = Exp,
+% {{Mantissa, Base, {Val,Buf}}, Buffer4, RemBytes2+RemBytes3};
+% _value ->
+% comming
+% end
+% end.
+
+
+%%============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.6
+%%
+%% encode bitstring value
+%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constrint Len, only valid when identifiers
+%%============================================================================
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList,TagIn) when integer(Unused), binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList,TagIn);
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList, TagIn) when atom(FirstVal) ->
+ encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(C, [{bit,X} | RestVal], NamedBitList, TagIn) ->
+ encode_bit_string_named(C, [{bit,X} | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(C, [FirstVal| RestVal], NamedBitList, TagIn) when integer(FirstVal) ->
+ encode_bit_string_bits(C, [FirstVal | RestVal], NamedBitList, TagIn);
+
+encode_bit_string(_C, 0, _NamedBitList, TagIn) ->
+ encode_tags(TagIn, <<0>>,1);
+
+encode_bit_string(_C, [], _NamedBitList, TagIn) ->
+ encode_tags(TagIn, <<0>>,1);
+
+encode_bit_string(C, IntegerVal, NamedBitList, TagIn) when integer(IntegerVal) ->
+ BitListVal = int_to_bitlist(IntegerVal),
+ encode_bit_string_bits(C, BitListVal, NamedBitList, TagIn);
+
+encode_bit_string(C, {Name,BitList}, NamedBitList, TagIn) when atom(Name) ->
+ encode_bit_string(C, BitList, NamedBitList, TagIn).
+
+
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+
+%%=================================================================
+%% Encode BIT STRING of the form {Unused,BinBits}.
+%% Unused is the number of unused bits in the last byte in BinBits
+%% and BinBits is a binary representing the BIT STRING.
+%%=================================================================
+encode_bin_bit_string(C,{Unused,BinBits},_NamedBitList,TagIn)->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits);
+ {_Min,Max} ->
+ BBLen = (size(BinBits)*8)-Unused,
+ if
+ BBLen > Max ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBLen},{maximum,Max}}}}});
+ true ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits)
+ end;
+ Size ->
+ case ((size(BinBits)*8)-Unused) of
+ BBSize when BBSize =< Size ->
+ remove_unused_then_dotag(TagIn, Unused, BinBits);
+ BBSize ->
+ exit({error,{asn1,
+ {bitstring_length,
+ {{was,BBSize},{should_be,Size}}}}})
+ end
+ end.
+
+remove_unused_then_dotag(TagIn,Unused,BinBits) ->
+ case Unused of
+ 0 when (size(BinBits) == 0) ->
+ encode_tags(TagIn,<<0>>,1);
+ 0 ->
+ Bin = <<Unused,BinBits/binary>>,
+ encode_tags(TagIn,Bin,size(Bin));
+ Num ->
+ N = (size(BinBits)-1),
+ <<BBits:N/binary,LastByte>> = BinBits,
+ encode_tags(TagIn,
+ [Unused,binary_to_list(BBits) ++[(LastByte bsr Num) bsl Num]],
+ 1+size(BinBits))
+ end.
+
+
+%%=================================================================
+%% Encode named bits
+%%=================================================================
+
+encode_bit_string_named(C, [FirstVal | RestVal], NamedBitList, TagIn) ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
+ Size =
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ lists:max(ToSetPos)+1;
+ {_Min,Max} ->
+ Max;
+ TSize ->
+ TSize
+ end,
+ BitList = make_and_set_list(Size, ToSetPos, 0),
+ {Len, Unused, OctetList} = encode_bitstring(BitList),
+ encode_tags(TagIn, [Unused|OctetList],Len+1).
+
+
+%%----------------------------------------
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+%%----------------------------------------
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+get_all_bitposes([Val | Rest], NamedBitList, Ack) when atom(Val) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+
+%%----------------------------------------
+%% make_and_set_list(Len of list to return, [list of positions to set to 1])->
+%% returns list of Len length, with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%% Len will make a list of length Len, not Len + 1.
+%% BitList = make_and_set_list(C, ToSetPos, 0),
+%%----------------------------------------
+
+make_and_set_list(0, [], _) -> [];
+make_and_set_list(0, _, _) ->
+ exit({error,{asn1,bitstring_sizeconstraint}});
+make_and_set_list(Len, [XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(Len - 1, SetPos, XPos + 1)];
+make_and_set_list(Len, [Pos|SetPos], XPos) ->
+ [0 | make_and_set_list(Len - 1, [Pos | SetPos], XPos + 1)];
+make_and_set_list(Len, [], XPos) ->
+ [0 | make_and_set_list(Len - 1, [], XPos + 1)].
+
+
+
+
+
+
+%%=================================================================
+%% Encode bit string for lists of ones and zeroes
+%%=================================================================
+encode_bit_string_bits(C, BitListVal, _NamedBitList, TagIn) when list(BitListVal) ->
+ case get_constraint(C,'SizeConstraint') of
+ no ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ Constr={Min,Max} when integer(Min),integer(Max) ->
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ {Constr={_,_},[]} ->%Constr={Min,Max}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ Constr={{_,_},{_,_}} ->%{{Min1,Max1},{Min2,Max2}}
+ %% constraint with extension mark
+ encode_constr_bit_str_bits(Constr,BitListVal,TagIn);
+ Size ->
+ case length(BitListVal) of
+ BitSize when BitSize == Size ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ BitSize when BitSize < Size ->
+ PaddedList = pad_bit_list(Size-BitSize,BitListVal),
+ {Len, Unused, OctetList} = encode_bitstring(PaddedList),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused | OctetList], Len+1);
+ BitSize ->
+ exit({error,{asn1,
+ {bitstring_length, {{was,BitSize},{should_be,Size}}}}})
+ end
+
+ end.
+
+encode_constr_bit_str_bits({_Min,Max},BitListVal,TagIn) ->
+ BitLen = length(BitListVal),
+ if
+ BitLen > Max ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max}}}}});
+ true ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused, OctetList], Len+1)
+ end;
+encode_constr_bit_str_bits({{_Min1,Max1},{Min2,Max2}},BitListVal,TagIn) ->
+ BitLen = length(BitListVal),
+ case BitLen of
+ Len when Len > Max2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {maximum,Max2}}}}});
+ Len when Len > Max1, Len < Min2 ->
+ exit({error,{asn1,{bitstring_length,{{was,BitLen},
+ {not_allowed_interval,
+ Max1,Min2}}}}});
+ _ ->
+ {Len, Unused, OctetList} = encode_bitstring(BitListVal),
+ %%add unused byte to the Len
+ encode_tags(TagIn, [Unused, OctetList], Len+1)
+ end.
+
+%% returns a list of length Size + length(BitListVal), with BitListVal
+%% as the most significant elements followed by padded zero elements
+pad_bit_list(Size,BitListVal) ->
+ Tail = lists:duplicate(Size,0),
+ lists:append(BitListVal,Tail).
+
+%%=================================================================
+%% Do the actual encoding
+%% ([bitlist]) -> {ListLen, UnusedBits, OctetList}
+%%=================================================================
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest]) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Val], 1);
+encode_bitstring(Val) ->
+ {Unused, Octet} = unused_bitlist(Val, 7, 0),
+ {1, Unused, [Octet]}.
+
+encode_bitstring([B8, B7, B6, B5, B4, B3, B2, B1 | Rest], Ack, Len) ->
+ Val = (B8 bsl 7) bor (B7 bsl 6) bor (B6 bsl 5) bor (B5 bsl 4) bor
+ (B4 bsl 3) bor (B3 bsl 2) bor (B2 bsl 1) bor B1,
+ encode_bitstring(Rest, [Ack | [Val]], Len + 1);
+%%even multiple of 8 bits..
+encode_bitstring([], Ack, Len) ->
+ {Len, 0, Ack};
+%% unused bits in last octet
+encode_bitstring(Rest, Ack, Len) ->
+% io:format("uneven ~w ~w ~w~n",[Rest, Ack, Len]),
+ {Unused, Val} = unused_bitlist(Rest, 7, 0),
+ {Len + 1, Unused, [Ack | [Val]]}.
+
+%%%%%%%%%%%%%%%%%%
+%% unused_bitlist([list of ones and zeros <= 7], 7, []) ->
+%% {Unused bits, Last octet with bits moved to right}
+unused_bitlist([], Trail, Ack) ->
+ {Trail + 1, Ack};
+unused_bitlist([Bit | Rest], Trail, Ack) ->
+%% io:format("trail Bit: ~w Rest: ~w Trail: ~w Ack:~w~n",[Bit, Rest, Trail, Ack]),
+ unused_bitlist(Rest, Trail - 1, (Bit bsl Trail) bor Ack).
+
+
+%%============================================================================
+%% decode bitstring value
+%% (Buffer, Range, NamedNumberList, HasTag, TotalLen) -> {Integer, Remain, RemovedBytes}
+%%============================================================================
+
+decode_compact_bit_string(Buffer, Range, NamedNumberList, Tags) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags,
+ NamedNumberList,bin).
+
+decode_bit_string(Buffer, Range, NamedNumberList, Tags) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_BIT_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_BIT_STRING, Tags,
+ NamedNumberList,old).
+
+
+decode_bit_string2(<<0>>,_NamedNumberList,BinOrOld) ->
+ case BinOrOld of
+ bin ->
+ {0,<<>>};
+ _ ->
+ []
+ end;
+decode_bit_string2(<<Unused,Bits/binary>>,NamedNumberList,BinOrOld) ->
+ case NamedNumberList of
+ [] ->
+ case BinOrOld of
+ bin ->
+ {Unused,Bits};
+ _ ->
+ decode_bitstring2(size(Bits), Unused, Bits)
+ end;
+ _ ->
+ BitString = decode_bitstring2(size(Bits), Unused, Bits),
+ decode_bitstring_NNL(BitString,NamedNumberList)
+ end.
+
+%%----------------------------------------
+%% Decode the in buffer to bits
+%%----------------------------------------
+decode_bitstring2(1,Unused,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,_/binary>>) ->
+ lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0],8-Unused);
+decode_bitstring2(Len, Unused,
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Buffer/binary>>) ->
+ [B7, B6, B5, B4, B3, B2, B1, B0 |
+ decode_bitstring2(Len - 1, Unused, Buffer)].
+
+%%decode_bitstring2(1, Unused, Buffer) ->
+%% make_bits_of_int(hd(Buffer), 128, 8-Unused);
+%%decode_bitstring2(Len, Unused, [BitVal | Buffer]) ->
+%% [B7, B6, B5, B4, B3, B2, B1, B0] = make_bits_of_int(BitVal, 128, 8),
+%% [B7, B6, B5, B4, B3, B2, B1, B0 |
+%% decode_bitstring2(Len - 1, Unused, Buffer)].
+
+
+%%make_bits_of_int(_, _, 0) ->
+%% [];
+%%make_bits_of_int(BitVal, MaskVal, Unused) when Unused > 0 ->
+%% X = case MaskVal band BitVal of
+%% 0 -> 0 ;
+%% _ -> 1
+%% end,
+%% [X | make_bits_of_int(BitVal, MaskVal bsr 1, Unused - 1)].
+
+
+
+%%----------------------------------------
+%% Decode the bitlist to names
+%%----------------------------------------
+
+
+decode_bitstring_NNL(BitList,NamedNumberList) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,0,[]).
+
+
+decode_bitstring_NNL([],_,_No,Result) ->
+ lists:reverse(Result);
+
+decode_bitstring_NNL([B|BitList],[{Name,No}|NamedNumberList],No,Result) ->
+ if
+ B == 0 ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result);
+ true ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[Name|Result])
+ end;
+decode_bitstring_NNL([1|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,[{bit,No}|Result]);
+decode_bitstring_NNL([0|BitList],NamedNumberList,No,Result) ->
+ decode_bitstring_NNL(BitList,NamedNumberList,No+1,Result).
+
+
+%%============================================================================
+%% Octet string, ITU_T X.690 Chapter 8.7
+%%
+%% encode octet string
+%% The OctetList must be a flat list of integers in the range 0..255
+%% the function does not check this because it takes to much time
+%%============================================================================
+encode_octet_string(_C, OctetList, TagIn) when binary(OctetList) ->
+ encode_tags(TagIn, OctetList, size(OctetList));
+encode_octet_string(_C, OctetList, TagIn) when list(OctetList) ->
+ encode_tags(TagIn, OctetList, length(OctetList));
+encode_octet_string(C, {Name,OctetList}, TagIn) when atom(Name) ->
+ encode_octet_string(C, OctetList, TagIn).
+
+
+%%============================================================================
+%% decode octet string
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%
+%% Octet string is decoded as a restricted string
+%%============================================================================
+decode_octet_string(Buffer, Range, Tags) ->
+% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}),
+ decode_restricted_string(Buffer, Range, ?N_OCTET_STRING,
+ Tags, [], old).
+
+%%============================================================================
+%% Null value, ITU_T X.690 Chapter 8.8
+%%
+%% encode NULL value
+%%============================================================================
+
+encode_null({Name, _Val}, TagIn) when atom(Name) ->
+ encode_tags(TagIn, [], 0);
+encode_null(_Val, TagIn) ->
+ encode_tags(TagIn, [], 0).
+
+%%============================================================================
+%% decode NULL value
+%% (Buffer, HasTag, TotalLen) -> {NULL, Remain, RemovedBytes}
+%%============================================================================
+
+decode_null(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ case Val of
+ <<>> ->
+ 'NULL';
+ _ ->
+ exit({error,{asn1,{decode_null,Val}}})
+ end.
+
+%%============================================================================
+%% Object identifier, ITU_T X.690 Chapter 8.19
+%%
+%% encode Object Identifier value
+%%============================================================================
+
+encode_object_identifier({Name,Val}, TagIn) when atom(Name) ->
+ encode_object_identifier(Val, TagIn);
+encode_object_identifier(Val, TagIn) ->
+ encode_tags(TagIn, e_object_identifier(Val)).
+
+e_object_identifier({'OBJECT IDENTIFIER', V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname, V}) when atom(Cname), tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname, V}) when atom(Cname), list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%%%%%%%%%%%%%%%
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+e_object_identifier([E1, E2 | Tail]) ->
+ Head = 40*E1 + E2, % wow!
+ {H,Lh} = mk_object_val(Head),
+ {R,Lr} = enc_obj_id_tail(Tail, [], 0),
+ {[H|R], Lh+Lr}.
+
+enc_obj_id_tail([], Ack, Len) ->
+ {lists:reverse(Ack), Len};
+enc_obj_id_tail([H|T], Ack, Len) ->
+ {B, L} = mk_object_val(H),
+ enc_obj_id_tail(T, [B|Ack], Len+L).
+
+%% e_object_identifier([List of Obect Identifiers]) ->
+%% {[Encoded Octetlist of ObjIds], IntLength}
+%%
+%%e_object_identifier([E1, E2 | Tail]) ->
+%% Head = 40*E1 + E2, % wow!
+%% F = fun(Val, AckLen) ->
+%% {L, Ack} = mk_object_val(Val),
+%% {L, Ack + AckLen}
+%% end,
+%% {Octets, Len} = lists:mapfoldl(F, 0, [Head | Tail]).
+
+%%%%%%%%%%%
+%% mk_object_val(Value) -> {OctetList, Len}
+%% returns a Val as a list of octets, the 8 bit is allways set to one except
+%% for the last octet, where its 0
+%%
+
+
+mk_object_val(Val) when Val =< 127 ->
+ {[255 band Val], 1};
+mk_object_val(Val) ->
+ mk_object_val(Val bsr 7, [Val band 127], 1).
+mk_object_val(0, Ack, Len) ->
+ {Ack, Len};
+mk_object_val(Val, Ack, Len) ->
+ mk_object_val(Val bsr 7, [((Val band 127) bor 128) | Ack], Len + 1).
+
+
+
+%%============================================================================
+%% decode Object Identifier value
+%% (Buffer, HasTag, TotalLen) -> {{ObjId}, Remain, RemovedBytes}
+%%============================================================================
+
+decode_object_identifier(Tlv, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ [AddedObjVal|ObjVals] = dec_subidentifiers(Val,0,[]),
+ {Val1, Val2} = if
+ AddedObjVal < 40 ->
+ {0, AddedObjVal};
+ AddedObjVal < 80 ->
+ {1, AddedObjVal - 40};
+ true ->
+ {2, AddedObjVal - 80}
+ end,
+ list_to_tuple([Val1, Val2 | ObjVals]).
+
+dec_subidentifiers(<<>>,_Av,Al) ->
+ lists:reverse(Al);
+dec_subidentifiers(<<1:1,H:7,T/binary>>,Av,Al) ->
+ dec_subidentifiers(T,(Av bsl 7) + H,Al);
+dec_subidentifiers(<<H,T/binary>>,Av,Al) ->
+ dec_subidentifiers(T,0,[((Av bsl 7) + H)|Al]).
+
+
+%%============================================================================
+%% Restricted character string types, ITU_T X.690 Chapter 8.20
+%%
+%% encode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%%============================================================================
+%% The StringType arg is kept for future use but might be removed
+encode_restricted_string(_C, OctetList, _StringType, TagIn)
+ when binary(OctetList) ->
+ encode_tags(TagIn, OctetList, size(OctetList));
+encode_restricted_string(_C, OctetList, _StringType, TagIn)
+ when list(OctetList) ->
+ encode_tags(TagIn, OctetList, length(OctetList));
+encode_restricted_string(C,{Name,OctetL}, StringType, TagIn) when atom(Name)->
+ encode_restricted_string(C, OctetL, StringType, TagIn).
+
+%%============================================================================
+%% decode Numeric Printable Teletex Videotex Visible IA5 Graphic General strings
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_restricted_string(Buffer, Range, StringType, Tags) ->
+ decode_restricted_string(Buffer, Range, StringType, Tags, [], old).
+
+
+decode_restricted_string(Tlv, Range, StringType, TagsIn,
+ NamedNumberList, BinOrOld) ->
+ Val = match_tags(Tlv, TagsIn),
+ Val2 =
+ case Val of
+ PartList = [_H|_T] -> % constructed val
+ Bin = collect_parts(PartList),
+ decode_restricted(Bin, StringType,
+ NamedNumberList, BinOrOld);
+ Bin ->
+ decode_restricted(Bin, StringType,
+ NamedNumberList, BinOrOld)
+ end,
+ check_and_convert_restricted_string(Val2,StringType,Range,NamedNumberList,BinOrOld).
+
+
+
+% case StringType of
+% ?N_BIT_STRING when BinOrOld == bin ->
+% {concat_bit_binaries(AccVal, Val), AccRb+Rb};
+% _ when binary(Val),binary(AccVal) ->
+% {<<AccVal/binary,Val/binary>>,AccRb+Rb};
+% _ when binary(Val), AccVal==[] ->
+% {Val,AccRb+Rb};
+% _ ->
+% {AccVal++Val, AccRb+Rb}
+% end,
+
+
+
+decode_restricted(Bin, StringType, NamedNumberList,BinOrOld) ->
+ case StringType of
+ ?N_BIT_STRING ->
+ decode_bit_string2(Bin, NamedNumberList, BinOrOld);
+ ?N_UniversalString ->
+ mk_universal_string(binary_to_list(Bin));
+ ?N_BMPString ->
+ mk_BMP_string(binary_to_list(Bin));
+ _ ->
+ Bin
+ end.
+
+
+check_and_convert_restricted_string(Val,StringType,Range,NamedNumberList,_BinOrOld) ->
+ {StrLen,NewVal} = case StringType of
+ ?N_BIT_STRING when NamedNumberList /= [] ->
+ {no_check,Val};
+ ?N_BIT_STRING when list(Val) ->
+ {length(Val),Val};
+ ?N_BIT_STRING when tuple(Val) ->
+ {(size(element(2,Val))*8) - element(1,Val),Val};
+ _ when binary(Val) ->
+ {size(Val),binary_to_list(Val)};
+ _ when list(Val) ->
+ {length(Val), Val}
+ end,
+ case Range of
+ _ when StrLen == no_check ->
+ NewVal;
+ [] -> % No length constraint
+ NewVal;
+ {Lb,Ub} when StrLen >= Lb, Ub >= StrLen -> % variable length constraint
+ NewVal;
+ {{Lb,_Ub},[]} when StrLen >= Lb ->
+ NewVal;
+ {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
+ StrLen =< Ub2, StrLen >= Lb2 ->
+ NewVal;
+ StrLen -> % fixed length constraint
+ NewVal;
+ {_,_} ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _Len when integer(_Len) ->
+ exit({error,{asn1,{length,Range,Val}}});
+ _ -> % some strange constraint that we don't support yet
+ NewVal
+ end.
+
+
+%%============================================================================
+%% encode Universal string
+%%============================================================================
+
+encode_universal_string(C, {Name, Universal}, TagIn) when atom(Name) ->
+ encode_universal_string(C, Universal, TagIn);
+encode_universal_string(_C, Universal, TagIn) ->
+ OctetList = mk_uni_list(Universal),
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+mk_uni_list(In) ->
+ mk_uni_list(In,[]).
+
+mk_uni_list([],List) ->
+ lists:reverse(List);
+mk_uni_list([{A,B,C,D}|T],List) ->
+ mk_uni_list(T,[D,C,B,A|List]);
+mk_uni_list([H|T],List) ->
+ mk_uni_list(T,[H,0,0,0|List]).
+
+%%===========================================================================
+%% decode Universal strings
+%% (Buffer, Range, StringType, HasTag, LenIn) ->
+%% {String, Remain, RemovedBytes}
+%%===========================================================================
+
+decode_universal_string(Buffer, Range, Tags) ->
+ decode_restricted_string(Buffer, Range, ?N_UniversalString,
+ Tags, [], old).
+
+
+mk_universal_string(In) ->
+ mk_universal_string(In,[]).
+
+mk_universal_string([],Acc) ->
+ lists:reverse(Acc);
+mk_universal_string([0,0,0,D|T],Acc) ->
+ mk_universal_string(T,[D|Acc]);
+mk_universal_string([A,B,C,D|T],Acc) ->
+ mk_universal_string(T,[{A,B,C,D}|Acc]).
+
+
+%%============================================================================
+%% encode BMP string
+%%============================================================================
+
+encode_BMP_string(C, {Name,BMPString}, TagIn) when atom(Name)->
+ encode_BMP_string(C, BMPString, TagIn);
+encode_BMP_string(_C, BMPString, TagIn) ->
+ OctetList = mk_BMP_list(BMPString),
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+mk_BMP_list(In) ->
+ mk_BMP_list(In,[]).
+
+mk_BMP_list([],List) ->
+ lists:reverse(List);
+mk_BMP_list([{0,0,C,D}|T],List) ->
+ mk_BMP_list(T,[D,C|List]);
+mk_BMP_list([H|T],List) ->
+ mk_BMP_list(T,[H,0|List]).
+
+%%============================================================================
+%% decode (OctetList, Range(ignored), tag|notag) -> {ValList, RestList}
+%% (Buffer, Range, StringType, HasTag, TotalLen) ->
+%% {String, Remain, RemovedBytes}
+%%============================================================================
+decode_BMP_string(Buffer, Range, Tags) ->
+ decode_restricted_string(Buffer, Range, ?N_BMPString,
+ Tags, [], old).
+
+mk_BMP_string(In) ->
+ mk_BMP_string(In,[]).
+
+mk_BMP_string([],US) ->
+ lists:reverse(US);
+mk_BMP_string([0,B|T],US) ->
+ mk_BMP_string(T,[B|US]);
+mk_BMP_string([C,D|T],US) ->
+ mk_BMP_string(T,[{0,0,C,D}|US]).
+
+
+%%============================================================================
+%% Generalized time, ITU_T X.680 Chapter 39
+%%
+%% encode Generalized time
+%%============================================================================
+
+encode_generalized_time(C, {Name,OctetList}, TagIn) when atom(Name) ->
+ encode_generalized_time(C, OctetList, TagIn);
+encode_generalized_time(_C, OctetList, TagIn) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode Generalized time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_generalized_time(Tlv, _Range, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ NewVal = case Val of
+ PartList = [_H|_T] -> % constructed
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end,
+ binary_to_list(NewVal).
+
+%%============================================================================
+%% Universal time, ITU_T X.680 Chapter 40
+%%
+%% encode UTC time
+%%============================================================================
+
+encode_utc_time(C, {Name,OctetList}, TagIn) when atom(Name) ->
+ encode_utc_time(C, OctetList, TagIn);
+encode_utc_time(_C, OctetList, TagIn) ->
+ encode_tags(TagIn, OctetList, length(OctetList)).
+
+%%============================================================================
+%% decode UTC time
+%% (Buffer, Range, HasTag, TotalLen) -> {String, Remain, RemovedBytes}
+%%============================================================================
+
+decode_utc_time(Tlv, _Range, Tags) ->
+ Val = match_tags(Tlv, Tags),
+ NewVal = case Val of
+ PartList = [_H|_T] -> % constructed
+ collect_parts(PartList);
+ Bin ->
+ Bin
+ end,
+ binary_to_list(NewVal).
+
+
+%%============================================================================
+%% Length handling
+%%
+%% Encode length
+%%
+%% encode_length(Int | indefinite) ->
+%% [<127]| [128 + Int (<127),OctetList] | [16#80]
+%%============================================================================
+
+encode_length(indefinite) ->
+ {[16#80],1}; % 128
+encode_length(L) when L =< 16#7F ->
+ {[L],1};
+encode_length(L) ->
+ Oct = minimum_octets(L),
+ Len = length(Oct),
+ if
+ Len =< 126 ->
+ {[ (16#80+Len) | Oct ],Len+1};
+ true ->
+ exit({error,{asn1, to_long_length_oct, Len}})
+ end.
+
+
+%% Val must be >= 0
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(0,Acc) ->
+ Acc;
+minimum_octets(Val, Acc) ->
+ minimum_octets((Val bsr 8),[Val band 16#FF | Acc]).
+
+
+%%===========================================================================
+%% Decode length
+%%
+%% decode_length(OctetList) -> {{indefinite, RestOctetsL}, NoRemovedBytes} |
+%% {{Length, RestOctetsL}, NoRemovedBytes}
+%%===========================================================================
+
+decode_length(<<1:1,0:7,T/binary>>) ->
+ {indefinite, T};
+decode_length(<<0:1,Length:7,T/binary>>) ->
+ {Length,T};
+decode_length(<<1:1,LL:7,T/binary>>) ->
+ <<Length:LL/unit:8,Rest/binary>> = T,
+ {Length,Rest}.
+
+
+
+%%-------------------------------------------------------------------------
+%% INTERNAL HELPER FUNCTIONS (not exported)
+%%-------------------------------------------------------------------------
+
+
+%% decoding postitive integer values.
+decode_integer2(Len,Bin = <<0:1,_:7,_Bs/binary>>) ->
+ <<Int:Len/unit:8>> = Bin,
+ Int;
+%% decoding negative integer values.
+decode_integer2(Len,<<1:1,B2:7,Bs/binary>>) ->
+ <<N:Len/unit:8>> = <<B2,Bs/binary>>,
+ Int = N - (1 bsl (8 * Len - 1)),
+ Int.
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+collect_parts(TlvList) ->
+ collect_parts(TlvList,[]).
+
+collect_parts([{_,L}|Rest],Acc) when list(L) ->
+ collect_parts(Rest,[collect_parts(L)|Acc]);
+collect_parts([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],_Acc) ->
+ collect_parts_bit(Rest,[Bits],Unused);
+collect_parts([{_T,V}|Rest],Acc) ->
+ collect_parts(Rest,[V|Acc]);
+collect_parts([],Acc) ->
+ list_to_binary(lists:reverse(Acc)).
+
+collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
+ collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
+collect_parts_bit([],Acc,Uacc) ->
+ list_to_binary([Uacc|lists:reverse(Acc)]).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_check.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_check.erl
new file mode 100644
index 0000000000..bd3d5e6d8b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_check.erl
@@ -0,0 +1,333 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_check.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+-module(asn1rt_check).
+
+-include("asn1_records.hrl").
+
+-export([check_bool/2,
+ check_int/3,
+ check_bitstring/3,
+ check_octetstring/2,
+ check_null/2,
+ check_objectidentifier/2,
+ check_objectdescriptor/2,
+ check_real/2,
+ check_enum/3,
+ check_restrictedstring/2]).
+
+-export([transform_to_EXTERNAL1990/1,
+ transform_to_EXTERNAL1994/1]).
+
+
+check_bool(_Bool,asn1_DEFAULT) ->
+ true;
+check_bool(Bool,Bool) when Bool == true; Bool == false ->
+ true;
+check_bool(_Bool1,Bool2) ->
+ throw({error,Bool2}).
+
+check_int(_,asn1_DEFAULT,_) ->
+ true;
+check_int(Value,Value,_) when integer(Value) ->
+ true;
+check_int(DefValue,Value,NNL) when atom(Value) ->
+ case lists:keysearch(Value,1,NNL) of
+ {value,{_,DefValue}} ->
+ true;
+ _ ->
+ throw({error,DefValue})
+ end;
+check_int(DefaultValue,_Value,_) ->
+ throw({error,DefaultValue}).
+
+% check_bitstring([H|T],[H|T],_) when integer(H) ->
+% true;
+% check_bitstring(V,V,_) when integer(V) ->
+% true;
+%% Two equal lists or integers
+check_bitstring(_,asn1_DEFAULT,_) ->
+ true;
+check_bitstring(V,V,_) ->
+ true;
+%% Default value as a list of 1 and 0 and user value as an integer
+check_bitstring(L=[H|T],Int,_) when integer(Int),integer(H) ->
+ case bit_list_to_int(L,length(T)) of
+ Int -> true;
+ _ -> throw({error,L,Int})
+ end;
+%% Default value as an integer, val as list
+check_bitstring(Int,Val,NBL) when integer(Int),list(Val) ->
+ BL = int_to_bit_list(Int,[],length(Val)),
+ check_bitstring(BL,Val,NBL);
+%% Default value and user value as lists of ones and zeros
+check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL=[_H|_T]) when integer(H1),integer(H2) ->
+ L2new = remove_trailing_zeros(L2),
+ check_bitstring(L1,L2new,NBL);
+%% Default value as a list of 1 and 0 and user value as a list of atoms
+check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL) when integer(H1),atom(H2) ->
+ case bit_list_to_nbl(L1,NBL,0,[]) of
+ L3 -> check_bitstring(L3,L2,NBL);
+ _ -> throw({error,L2})
+ end;
+%% Both default value and user value as a list of atoms
+check_bitstring(L1=[H1|T1],L2=[H2|_T2],_) when atom(H1),atom(H2) ->
+ length(L1) == length(L2),
+ case lists:member(H1,L2) of
+ true ->
+ check_bitstring1(T1,L2);
+ false -> throw({error,L2})
+ end;
+%% Default value as a list of atoms and user value as a list of 1 and 0
+check_bitstring(L1=[H1|_T1],L2=[H2|_T2],NBL) when atom(H1),integer(H2) ->
+ case bit_list_to_nbl(L2,NBL,0,[]) of
+ L3 ->
+ check_bitstring(L1,L3,NBL);
+ _ -> throw({error,L2})
+ end;
+%% User value in compact format
+check_bitstring(DefVal,CBS={_,_},NBL) ->
+ NewVal = cbs_to_bit_list(CBS),
+ check_bitstring(DefVal,NewVal,NBL);
+check_bitstring(DV,V,_) ->
+ throw({error,DV,V}).
+
+
+bit_list_to_int([0|Bs],ShL)->
+ bit_list_to_int(Bs,ShL-1) + 0;
+bit_list_to_int([1|Bs],ShL) ->
+ bit_list_to_int(Bs,ShL-1) + (1 bsl ShL);
+bit_list_to_int([],_) ->
+ 0.
+
+int_to_bit_list(0,Acc,0) ->
+ Acc;
+int_to_bit_list(Int,Acc,Len) ->
+ int_to_bit_list(Int bsr 1,[Int band 1|Acc],Len - 1).
+
+bit_list_to_nbl([0|T],NBL,Pos,Acc) ->
+ bit_list_to_nbl(T,NBL,Pos+1,Acc);
+bit_list_to_nbl([1|T],NBL,Pos,Acc) ->
+ case lists:keysearch(Pos,2,NBL) of
+ {value,{N,_}} ->
+ bit_list_to_nbl(T,NBL,Pos+1,[N|Acc]);
+ _ ->
+ throw({error,{no,named,element,at,pos,Pos}})
+ end;
+bit_list_to_nbl([],_,_,Acc) ->
+ Acc.
+
+remove_trailing_zeros(L2) ->
+ remove_trailing_zeros1(lists:reverse(L2)).
+remove_trailing_zeros1(L) ->
+ lists:reverse(lists:dropwhile(fun(0)->true;
+ (_) ->false
+ end,
+ L)).
+
+check_bitstring1([H|T],NBL) ->
+ case lists:member(H,NBL) of
+ true ->
+ check_bitstring1(T,NBL);
+ V -> throw({error,V})
+ end;
+check_bitstring1([],_) ->
+ true.
+
+cbs_to_bit_list({Unused,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Rest/binary>>}) when size(Rest) >= 1 ->
+ [B7,B6,B5,B4,B3,B2,B1,B0|cbs_to_bit_list({Unused,Rest})];
+cbs_to_bit_list({0,<<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1>>}) ->
+ [B7,B6,B5,B4,B3,B2,B1,B0];
+cbs_to_bit_list({Unused,Bin}) when size(Bin) == 1 ->
+ Used = 8-Unused,
+ <<Int:Used,_:Unused>> = Bin,
+ int_to_bit_list(Int,[],Used).
+
+
+check_octetstring(_,asn1_DEFAULT) ->
+ true;
+check_octetstring(L,L) ->
+ true;
+check_octetstring(L,Int) when list(L),integer(Int) ->
+ case integer_to_octetlist(Int) of
+ L -> true;
+ V -> throw({error,V})
+ end;
+check_octetstring(_,V) ->
+ throw({error,V}).
+
+integer_to_octetlist(Int) ->
+ integer_to_octetlist(Int,[]).
+integer_to_octetlist(0,Acc) ->
+ Acc;
+integer_to_octetlist(Int,Acc) ->
+ integer_to_octetlist(Int bsr 8,[(Int band 255)|Acc]).
+
+check_null(_,asn1_DEFAULT) ->
+ true;
+check_null('NULL','NULL') ->
+ true;
+check_null(_,V) ->
+ throw({error,V}).
+
+check_objectidentifier(_,asn1_DEFAULT) ->
+ true;
+check_objectidentifier(OI,OI) ->
+ true;
+check_objectidentifier(DOI,OI) when tuple(DOI),tuple(OI) ->
+ check_objectidentifier1(tuple_to_list(DOI),tuple_to_list(OI));
+check_objectidentifier(_,OI) ->
+ throw({error,OI}).
+
+check_objectidentifier1([V|Rest1],[V|Rest2]) ->
+ check_objectidentifier1(Rest1,Rest2,V);
+check_objectidentifier1([V1|Rest1],[V2|Rest2]) ->
+ case reserved_objectid(V2,[]) of
+ V1 ->
+ check_objectidentifier1(Rest1,Rest2,[V1]);
+ V ->
+ throw({error,V})
+ end.
+check_objectidentifier1([V|Rest1],[V|Rest2],Above) ->
+ check_objectidentifier1(Rest1,Rest2,[V|Above]);
+check_objectidentifier1([V1|Rest1],[V2|Rest2],Above) ->
+ case reserved_objectid(V2,Above) of
+ V1 ->
+ check_objectidentifier1(Rest1,Rest2,[V1|Above]);
+ V ->
+ throw({error,V})
+ end;
+check_objectidentifier1([],[],_) ->
+ true;
+check_objectidentifier1(_,V,_) ->
+ throw({error,object,identifier,V}).
+
+%% ITU-T Rec. X.680 Annex B - D
+reserved_objectid('itu-t',[]) -> 0;
+reserved_objectid('ccitt',[]) -> 0;
+%% arcs below "itu-t"
+reserved_objectid('recommendation',[0]) -> 0;
+reserved_objectid('question',[0]) -> 1;
+reserved_objectid('administration',[0]) -> 2;
+reserved_objectid('network-operator',[0]) -> 3;
+reserved_objectid('identified-organization',[0]) -> 4;
+
+reserved_objectid(iso,[]) -> 1;
+%% arcs below "iso", note that number 1 is not used
+reserved_objectid('standard',[1]) -> 0;
+reserved_objectid('member-body',[1]) -> 2;
+reserved_objectid('identified-organization',[1]) -> 3;
+
+reserved_objectid('joint-iso-itu-t',[]) -> 2;
+reserved_objectid('joint-iso-ccitt',[]) -> 2;
+
+reserved_objectid(_,_) -> false.
+
+
+check_objectdescriptor(_,asn1_DEFAULT) ->
+ true;
+check_objectdescriptor(OD,OD) ->
+ true;
+check_objectdescriptor(OD,OD) ->
+ throw({error,{not_implemented_yet,check_objectdescriptor}}).
+
+check_real(_,asn1_DEFAULT) ->
+ true;
+check_real(R,R) ->
+ true;
+check_real(_,_) ->
+ throw({error,{not_implemented_yet,check_real}}).
+
+check_enum(_,asn1_DEFAULT,_) ->
+ true;
+check_enum(Val,Val,_) ->
+ true;
+check_enum(Int,Atom,Enumerations) when integer(Int),atom(Atom) ->
+ case lists:keysearch(Atom,1,Enumerations) of
+ {value,{_,Int}} -> true;
+ _ -> throw({error,{enumerated,Int,Atom}})
+ end;
+check_enum(DefVal,Val,_) ->
+ throw({error,{enumerated,DefVal,Val}}).
+
+
+check_restrictedstring(_,asn1_DEFAULT) ->
+ true;
+check_restrictedstring(Val,Val) ->
+ true;
+check_restrictedstring([V|Rest1],[V|Rest2]) ->
+ check_restrictedstring(Rest1,Rest2);
+check_restrictedstring([V1|Rest1],[V2|Rest2]) ->
+ check_restrictedstring(V1,V2),
+ check_restrictedstring(Rest1,Rest2);
+%% tuple format of value
+check_restrictedstring({V1,V2},[V1,V2]) ->
+ true;
+check_restrictedstring([V1,V2],{V1,V2}) ->
+ true;
+%% quadruple format of value
+check_restrictedstring({V1,V2,V3,V4},[V1,V2,V3,V4]) ->
+ true;
+check_restrictedstring([V1,V2,V3,V4],{V1,V2,V3,V4}) ->
+ true;
+%% character string list
+check_restrictedstring(V1,V2) when list(V1),tuple(V2) ->
+ check_restrictedstring(V1,tuple_to_list(V2));
+check_restrictedstring(V1,V2) ->
+ throw({error,{restricted,string,V1,V2}}).
+
+transform_to_EXTERNAL1990(Val) when tuple(Val),size(Val) == 4 ->
+ transform_to_EXTERNAL1990(tuple_to_list(Val),[]);
+transform_to_EXTERNAL1990(Val) when tuple(Val) ->
+ %% Data already in ASN1 1990 format
+ Val.
+
+transform_to_EXTERNAL1990(['EXTERNAL'|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,['EXTERNAL'|Acc]);
+transform_to_EXTERNAL1990([{syntax,Syntax}|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,[asn1_NOVALUE,Syntax|Acc]);
+transform_to_EXTERNAL1990([{'presentation-context-id',PCid}|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,[PCid,asn1_NOVALUE|Acc]);
+transform_to_EXTERNAL1990([{'context-negotiation',Context_negot}|Rest],Acc) ->
+ {_,Presentation_Cid,Transfer_syntax} = Context_negot,
+ transform_to_EXTERNAL1990(Rest,[Transfer_syntax,Presentation_Cid|Acc]);
+transform_to_EXTERNAL1990([asn1_NOVALUE|Rest],Acc) ->
+ transform_to_EXTERNAL1990(Rest,[asn1_NOVALUE|Acc]);
+transform_to_EXTERNAL1990([Data_val_desc,Data_value],Acc) when list(Data_value)->
+ list_to_tuple(lists:reverse([{'octet-aligned',Data_value},
+ Data_val_desc|Acc]));
+transform_to_EXTERNAL1990([Data_value],Acc) when list(Data_value)->
+ list_to_tuple(lists:reverse([{'octet-aligned',Data_value}|Acc])).
+
+
+transform_to_EXTERNAL1994(V={'EXTERNAL',DRef,IndRef,Data_v_desc,Encoding}) ->
+ Identification =
+ case {DRef,IndRef} of
+ {DRef,asn1_NOVALUE} ->
+ {syntax,DRef};
+ {asn1_NOVALUE,IndRef} ->
+ {'presentation-context-id',IndRef};
+ _ ->
+ {'context-negotiation',
+ {'EXTERNAL_identification_context-negotiation',IndRef,DRef}}
+ end,
+ case Encoding of
+ {_,Val} when list(Val) ->
+ {'EXTERNAL',Identification,Data_v_desc,Val};
+ _ ->
+ V
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_driver_handler.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_driver_handler.erl
new file mode 100644
index 0000000000..7a986b5376
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_driver_handler.erl
@@ -0,0 +1,108 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_driver_handler.erl,v 1.1 2008/12/17 09:53:30 mikpe Exp $
+%%
+
+-module(asn1rt_driver_handler).
+
+-export([init/1,load_driver/0,unload_driver/0]).
+
+
+load_driver() ->
+ spawn(asn1rt_driver_handler, init, [self()]).
+
+init(From) ->
+ Port=
+ case load_driver("asn1_erl_drv") of
+ ok ->
+ open_named_port(From);
+ already_done ->
+ From ! driver_ready;
+ Error -> % if erl_ddll:load_driver fails
+ erl_ddll:unload_driver("asn1_erl_drv"),
+ From ! Error
+ end,
+ register_and_loop(Port).
+
+load_driver(DriverName) ->
+ case is_driver_loaded(DriverName) of
+ false ->
+ Dir = filename:join([code:priv_dir(asn1),"lib"]),
+ erl_ddll:load_driver(Dir,DriverName);
+ true ->
+ ok
+ end.
+
+
+is_driver_loaded(_Name) ->
+ case whereis(asn1_driver_owner) of
+ undefined ->
+ false;
+ _ ->
+ true
+ end.
+
+open_named_port(From) ->
+ case is_port_open(drv_complete) of
+ false ->
+ case catch open_port({spawn,"asn1_erl_drv"},[]) of
+ {'EXIT',Reason} ->
+ From ! {port_error,Reason};
+ Port ->
+ register(drv_complete,Port),
+ From ! driver_ready,
+ Port
+ end;
+ _ ->
+ From ! driver_ready,
+ ok
+ end.
+
+is_port_open(Name) ->
+ case whereis(Name) of
+ Port when port(Port) ->
+ true;
+ _ -> false
+ end.
+
+register_and_loop(Port) when port(Port) ->
+ register(asn1_driver_owner,self()),
+ loop();
+register_and_loop(_) ->
+ ok.
+
+loop() ->
+ receive
+ unload ->
+ case whereis(drv_complete) of
+ Port when port(Port) ->
+ port_close(Port);
+ _ -> ok
+ end,
+ erl_ddll:unload_driver("asn1_erl_drv"),
+ ok;
+ _ ->
+ loop()
+ end.
+
+unload_driver() ->
+ case whereis(asn1_driver_owner) of
+ Pid when pid(Pid) ->
+ Pid ! unload,
+ ok;
+ _ ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per.erl
new file mode 100644
index 0000000000..d531a165ae
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per.erl
@@ -0,0 +1,1609 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/2, fixextensions/2, setoptionals/1,
+ getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/3, set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_boolean/1,
+ decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1]).
+-export([encode_enumerated/3, decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_restricted_string/4, encode_restricted_string/5,
+ decode_restricted_string/4, decode_restricted_string/5,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([encode_UniversalString/2, decode_UniversalString/2,
+ encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ encode_VisibleString/2, decode_VisibleString/2,
+ encode_BMPString/2, decode_BMPString/2,
+ encode_IA5String/2, decode_IA5String/2,
+ encode_NumericString/2, decode_NumericString/2
+ ]).
+
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+ [{debug,choiceext},{bit,0}];
+setchoiceext(false) ->
+ [{debug,choiceext},{bit,1}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(true) ->
+ [{debug,ext},{bit,1}];
+setext(false) ->
+ [{debug,ext},{bit,0}].
+
+fixoptionals(OptList,Val) when tuple(Val) ->
+ fixoptionals(OptList,Val,[]);
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([],Val,Acc) ->
+ % return {Val,Opt}
+ {Val,lists:reverse(Acc)};
+fixoptionals([{_,Pos}|Ot],Val,Acc) ->
+ case element(Pos+1,Val) of
+ asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
+ _ -> fixoptionals(Ot,Val,[1|Acc])
+ end.
+
+
+%setoptionals(OptList,Val) ->
+% Vlist = tuple_to_list(Val),
+% setoptionals(OptList,Vlist,1,[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,Acc1,Acc2) ->
+ % return {Val,Opt}
+ {list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]),lists:reverse(Acc1)}.
+
+setoptionals([H|T]) ->
+ [{bit,H}|setoptionals(T)];
+setoptionals([]) ->
+ [{debug,optionals}].
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_NumChoices,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_integer(Bytes,[{'ValueRange',{0,NumChoices-1}}]).
+
+getoptionals(Bytes,L,NumComp) when list(L) ->
+ {Blist,Bytes1} = getbits_as_list(length(L),Bytes),
+ {list_to_tuple(comptuple(Blist,L,NumComp,1)),Bytes1}.
+
+comptuple([Bh|Bt],[{_Name,Nr}|T],NumComp,Nr) ->
+ [Bh|comptuple(Bt,T,NumComp-1,Nr+1)];
+comptuple(Bl,[{Name,Tnr}|Tl],NumComp,Nr) ->
+ [0|comptuple(Bl,[{Name,Tnr}|Tl],NumComp-1,Nr+1)];
+comptuple(_B,_L,0,_Nr) ->
+ [];
+comptuple(B,O,N,Nr) ->
+ [0|comptuple(B,O,N-1,Nr+1)].
+
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+getbits_as_list(0,Bytes,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getbits_as_list(Num,Bytes,Acc) ->
+ {Bit,NewBytes} = getbit(Bytes),
+ getbits_as_list(Num-1,NewBytes,[Bit|Acc]).
+
+getbit(Bytes) ->
+% io:format("getbit:~p~n",[Bytes]),
+ getbit1(Bytes).
+
+getbit1({7,[H|T]}) ->
+ {H band 1,{0,T}};
+getbit1({Pos,[H|T]}) ->
+ {(H bsr (7-Pos)) band 1,{(Pos+1) rem 8,[H|T]}};
+getbit1(Bytes) when list(Bytes) ->
+ getbit1({0,Bytes}).
+
+%% This could be optimized
+getbits(Buffer,Num) ->
+% io:format("getbits:Buffer = ~p~nNum=~p~n",[Buffer,Num]),
+ getbits(Buffer,Num,0).
+
+getbits(Buffer,0,Acc) ->
+ {Acc,Buffer};
+getbits(Buffer,Num,Acc) ->
+ {B,NewBuffer} = getbit(Buffer),
+ getbits(NewBuffer,Num-1,B + (Acc bsl 1)).
+
+
+getoctet(Bytes) when list(Bytes) ->
+ getoctet({0,Bytes});
+getoctet(Bytes) ->
+% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+ getoctet1(Bytes).
+
+getoctet1({0,[H|T]}) ->
+ {H,{0,T}};
+getoctet1({_Pos,[_,H|T]}) ->
+ {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,[_H|T]}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+getoctets(Buffer,Num) ->
+% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+ getoctets(Buffer,Num,0).
+
+getoctets(Buffer,0,Acc) ->
+ {Acc,Buffer};
+getoctets(Buffer,Num,Acc) ->
+ {Oct,NewBuffer} = getoctet(Buffer),
+ getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+getoctets_as_list(Buffer,Num) ->
+ getoctets_as_list(Buffer,Num,[]).
+
+getoctets_as_list(Buffer,0,Acc) ->
+ {lists:reverse(Acc),Buffer};
+getoctets_as_list(Buffer,Num,Acc) ->
+ {Oct,NewBuffer} = getoctet(Buffer),
+ getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+ [{bit,0}, % the value is in the root set
+ encode_integer([{'ValueRange',{0,Len1-1}}],N)];
+ N when integer(N) ->
+ [{bit,0}]; % no encoding if only 0 or 1 alternative
+ false ->
+ [{bit,1}, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_integer([{'ValueRange',{0,Len-1}}],N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_,[],_) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_Constraint, Val) when list(Val) ->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}];
+encode_open_type(_Constraint, Val) when binary(Val) ->
+ [encode_length(undefined,size(Val)),align,
+ {octets,binary_to_list(Val)}].
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _Constraint) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_NamedNumberList) when integer(V) ->
+ encode_integer(C,V).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer({Rc,_Ec},Val) ->
+ case (catch encode_integer(Rc,Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+ [{bit,1},encode_unconstrained_number(Val)];
+ Encoded ->
+ [{bit,0},Encoded]
+ end;
+encode_integer(C,Val ) when list(C) ->
+ case get_constraint(C,'SingleValue') of
+ no ->
+ encode_integer1(C,Val);
+ V when integer(V),V == Val ->
+ []; % a type restricted to a single value encodes to nothing
+ V when list(V) ->
+ case lists:member(Val,V) of
+ true ->
+ encode_integer1(C,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end.
+
+encode_integer1(C, Val) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ encode_unconstrained_number(Val);
+ {Lb,'MAX'} ->
+ encode_semi_constrained_number(Lb,Val);
+ %% positive with range
+ {Lb,Ub} when Val >= Lb,
+ Ub >= Val ->
+ encode_constrained_number(VR,Val)
+ end.
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,{Rc,_Ec}) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,Rc);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ V when list(V) ->
+ {Val,Buffer2} = decode_integer1(Buffer,C),
+ case lists:member(Val,V) of
+ true ->
+ {Val,Buffer2};
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_,_} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+% X.691:10.6 Encoding of a normally small non-negative whole number
+% Use this for encoding of CHOICE index if there is an extension marker in
+% the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+ [{bit,0},{bits,6,Val}];
+encode_small_number(Val) ->
+ [{bit,1},encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,{0,'MAX'})
+ end.
+
+% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Octs = eint_positive(Val2),
+ [encode_length(undefined,length(Octs)),{octets,Octs}].
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+ {bits,1,Val2};
+ Range =< 4 ->
+ {bits,2,Val2};
+ Range =< 8 ->
+ {bits,3,Val2};
+ Range =< 16 ->
+ {bits,4,Val2};
+ Range =< 32 ->
+ {bits,5,Val2};
+ Range =< 64 ->
+ {bits,6,Val2};
+ Range =< 128 ->
+ {bits,7,Val2};
+ Range =< 255 ->
+ {bits,8,Val2};
+ Range =< 256 ->
+ {octets,1,Val2};
+ Range =< 65536 ->
+ {octets,2,Val2};
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,3},length(Octs)),{octets,Octs}];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,4},length(Octs)),{octets,Octs}];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,5},length(Octs)),{octets,Octs}];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end.
+
+decode_constrained_number(Buffer,{Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+% Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ [{debug,unconstrained_number},
+ encode_length({0,'MAX'},length(Oct)),
+ {octets,Oct}];
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ [{debug,unconstrained_number},
+ encode_length({0,'MAX'},length(Oct)),
+ {octets,Oct}].
+
+%% used for positive Values which don't need a sign bit
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+%% used for signed positive values
+
+%eint(Val, Ack) ->
+% X = Val band 255,
+% Next = Val bsr 8,
+% if
+% Next == 0, X >= 127 ->
+% [0,X|Ack];
+% Next == 0 ->
+% [X|Ack];
+% true ->
+% eint(Next,[X|Ack])
+% end.
+
+%%% used for signed negative values
+%enint(Val, Acc) ->
+% NumOctets = if
+% -Val < 16#80 -> 1;
+% -Val < 16#8000 ->2;
+% -Val < 16#800000 ->3;
+% -Val < 16#80000000 ->4;
+% -Val < 16#8000000000 ->5;
+% -Val < 16#800000000000 ->6;
+% -Val < 16#80000000000000 ->7;
+% -Val < 16#8000000000000000 ->8;
+% -Val < 16#800000000000000000 ->9
+% end,
+% enint(Val,Acc,NumOctets).
+
+%enint(Val, Acc,0) ->
+% Acc;
+%enint(Val, Acc,NumOctets) ->
+% enint(Val bsr 8,[Val band 255|Acc],NumOctets-1).
+
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(Val,Acc) when Val > 0 ->
+ minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+minimum_octets(0,Acc) ->
+ Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+ {octet,Len band 16#7F};
+ Len < 16384 ->
+ {octets,2,2#1000000000000000 bor Len};
+ true ->
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length({Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number({Lb,Ub},Len);
+encode_length(SingleValue,_Len) when integer(SingleValue) ->
+ [].
+
+encode_small_length(Len) when Len =< 64 ->
+ [{bit,0},{bits,6,Len-1}];
+encode_small_length(Len) ->
+ [{bit,1},encode_length(undefined,Len)].
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ Buffer2 = align(Buffer),
+ {Bits,_} = getbits(Buffer2,2),
+ case Bits of
+ 2 ->
+ {Val,Bytes3} = getoctets(Buffer2,2),
+ {(Val band 16#3FFF),Bytes3};
+ 3 ->
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+ _ ->
+ {Val,Bytes3} = getoctet(Buffer2),
+ {Val band 16#7F,Bytes3}
+ end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+ % X.691:10.9.3.5
+decode_length(Buffer,{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+ case getbit(Buffer) of
+ {0,Remain} ->
+ getbits(Remain,7);
+ {1,_Remain} ->
+ {Val,Remain2} = getoctets(Buffer,2),
+ {Val band 2#0111111111111111, Remain2}
+ end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+% X.691:11
+encode_boolean({Name,Val}) when atom(Name) ->
+ encode_boolean(Val);
+encode_boolean(true) ->
+ {bit,1};
+encode_boolean(false) ->
+ {bit,0};
+encode_boolean(Val) ->
+ exit({error,{asn1,{encode_boolean,Val}}}).
+
+
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:12
+%% ENUMERATED
+%%
+%% encode_enumerated(C,Value,NamedNumberTup) -> CompleteList
+%%
+%%
+
+encode_enumerated(C,{Name,Value},NamedNumberList) when
+ atom(Name),list(NamedNumberList) ->
+ encode_enumerated(C,Value,NamedNumberList);
+
+%% ENUMERATED with extension mark
+encode_enumerated(_C,{asn1_enum,Value},{_Nlist1,Nlist2}) when Value >= length(Nlist2) ->
+ [{bit,1},encode_small_number(Value)];
+encode_enumerated(C,Value,{Nlist1,Nlist2}) ->
+ case enum_search(Value,Nlist1,0) of
+ NewV when integer(NewV) ->
+ [{bit,0},encode_integer(C,NewV)];
+ false ->
+ case enum_search(Value,Nlist2,0) of
+ ExtV when integer(ExtV) ->
+ [{bit,1},encode_small_number(ExtV)];
+ false ->
+ exit({error,{asn1,{encode_enumerated,Value}}})
+ end
+ end;
+
+encode_enumerated(C,Value,NamedNumberList) when list(NamedNumberList) ->
+ case enum_search(Value,NamedNumberList,0) of
+ NewV when integer(NewV) ->
+ encode_integer(C,NewV);
+ false ->
+ exit({error,{asn1,{encode_enumerated,Value}}})
+ end.
+
+%% returns the ordinal number from 0 ,1 ... in the list where Name is found
+%% or false if not found
+%%
+enum_search(Name,[Name|_NamedNumberList],Acc) ->
+ Acc;
+enum_search(Name,[_H|T],Acc) ->
+ enum_search(Name,T,Acc+1);
+enum_search(_,[],_) ->
+ false. % name not found !error
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+%% when the value is a list of named bits
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+encode_bit_string(C, [{bit,No} | RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes([{bit,No} | RestVal], NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+
+encode_bit_string(C, BitListValue, _NamedBitList) when list(BitListValue) ->
+ %% first remove any trailing zeroes
+ Bl1 = lists:dropwhile(fun(0)->true;(1)->false end,lists:reverse(BitListValue)),
+ BitList = [{bit,X} || X <- lists:reverse(Bl1)],
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ []; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ pad_list(V,BitList);
+ V when integer(V) -> % fixed length more than 16 bits
+ [align,pad_list(V,BitList)];
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ [encode_length({Lb,Ub},length(BitList)),align,BitList];
+ no ->
+ [encode_length(undefined,length(BitList)),align,BitList]
+ end;
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) ->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList).
+
+
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_to_named(Buffer,V,NamedNumberList);
+ V when integer(V) -> % fixed length 16 bits or less
+ Bytes2 = align(Buffer),
+ bit_list_to_named(Bytes2,V,NamedNumberList);
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList)
+ end.
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_to_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_to_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_to_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_to_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_to_named1(Pos+1,Bt,Names,Acc);
+bit_list_to_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_to_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_to_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_to_named1(_Pos,[],_Names,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+pad_list(0,BitList) ->
+ case BitList of
+ [] -> [];
+ _ -> exit({error,{asn1,{range_error,{bit_string,BitList}}}})
+ end;
+pad_list(N,[Bh|Bt]) ->
+ [Bh|pad_list(N-1,Bt)];
+pad_list(N,[]) ->
+ [{bit,0},pad_list(N-1,[])].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,{Name,Val}) when atom(Name) ->
+ encode_octet_string(C,false,Val);
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(_C,true,_Val) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(C,false,Val) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ [];
+ 1 ->
+ [V] = Val,
+ {bits,8,V};
+ 2 ->
+ [V1,V2] = Val,
+ [{bits,8,V1},{bits,8,V2}];
+ Sv when Sv =<65535, Sv == length(Val) -> % fixed length
+ [align,{octets,Val}];
+ {Lb,Ub} ->
+ [encode_length({Lb,Ub},length(Val)),align,
+ {octets,Val}];
+ Sv when list(Sv) ->
+ [encode_length({hd(Sv),lists:max(Sv)},length(Val)),align,
+ {octets,Val}];
+ no ->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}]
+ end.
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,C,false) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ {[],Bytes};
+ 1 ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+ 2 ->
+ {B1,Bytes2}= getbits(Bytes,8),
+ {B2,Bytes3}= getbits(Bytes2,8),
+ {[B1,B2],Bytes3};
+ {_,0} ->
+ {[],Bytes};
+ Sv when integer(Sv), Sv =<65535 -> % fixed length
+ Bytes2 = align(Bytes),
+ getoctets_as_list(Bytes2,Sv);
+ {Lb,Ub} ->
+ {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+ Sv when list(Sv) ->
+ {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+ no ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val)
+
+encode_restricted_string(aligned,StringType,C,Val) ->
+encode_restricted_string(aligned,StringType,C,false,Val).
+
+
+encode_restricted_string(aligned,StringType,C,_Ext,{Name,Val}) when atom(Name) ->
+ encode_restricted_string(aligned,StringType,C,false,Val);
+encode_restricted_string(aligned,StringType,C,_Ext,Val) ->
+ Result = chars_encode(C,StringType,Val),
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ case {StringType,Result} of
+ {'BMPString',{octets,Ol}} ->
+ [{bits,8,Oct}||Oct <- Ol];
+ _ ->
+ Result
+ end;
+ 0 ->
+ [];
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ [align,Result];
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb},length(Val)),align,Result];
+ Vl when list(Vl) ->
+ [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),align,Result];
+ no ->
+ [encode_length(undefined,length(Val)),align,Result]
+ end.
+
+decode_restricted_string(Bytes,aligned,StringType,C) ->
+ decode_restricted_string(Bytes,aligned,StringType,C,false).
+
+decode_restricted_string(Bytes,aligned,StringType,C,_Ext) ->
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ chars_decode(Bytes,NumBits,StringType,C,Ub);
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ Bytes1 = align(Bytes),
+ chars_decode(Bytes1,NumBits,StringType,C,Ub);
+ 0 ->
+ {[],Bytes};
+ Vl when list(Vl) ->
+ {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ no ->
+ {Len,Bytes1} = decode_length(Bytes,undefined),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ {Lb,Ub}->
+ {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len)
+ end.
+
+
+
+encode_BMPString(C,Val) ->
+ encode_restricted_string(aligned,'BMPString',C,false,Val).
+decode_BMPString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'BMPString',C,false).
+
+encode_GeneralString(C,Val) ->
+ encode_restricted_string(aligned,'GeneralString',C,false,Val).
+decode_GeneralString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'GeneralString',C,false).
+
+encode_GraphicString(C,Val) ->
+ encode_restricted_string(aligned,'GraphicString',C,false,Val).
+decode_GraphicString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'GraphicString',C,false).
+
+encode_IA5String(C,Val) ->
+ encode_restricted_string(aligned,'IA5String',C,false,Val).
+decode_IA5String(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'IA5String',C,false).
+
+encode_NumericString(C,Val) ->
+ encode_restricted_string(aligned,'NumericString',C,false,Val).
+decode_NumericString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'NumericString',C,false).
+
+encode_PrintableString(C,Val) ->
+ encode_restricted_string(aligned,'PrintableString',C,false,Val).
+decode_PrintableString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'PrintableString',C,false).
+
+encode_TeletexString(C,Val) -> % equivalent with T61String
+ encode_restricted_string(aligned,'TeletexString',C,false,Val).
+decode_TeletexString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'TeletexString',C,false).
+
+encode_UniversalString(C,Val) ->
+ encode_restricted_string(aligned,'UniversalString',C,false,Val).
+decode_UniversalString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'UniversalString',C,false).
+
+encode_VideotexString(C,Val) ->
+ encode_restricted_string(aligned,'VideotexString',C,false,Val).
+decode_VideotexString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'VideotexString',C,false).
+
+encode_VisibleString(C,Val) -> % equivalent with ISO646String
+ encode_restricted_string(aligned,'VisibleString',C,false,Val).
+decode_VisibleString(Bytes,C) ->
+ decode_restricted_string(Bytes,aligned,'VisibleString',C,false).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
+%%
+getBMPChars(Bytes,1) ->
+ {O1,Bytes2} = getbits(Bytes,8),
+ {O2,Bytes3} = getbits(Bytes2,8),
+ if
+ O1 == 0 ->
+ {[O2],Bytes3};
+ true ->
+ {[{O1,O2}],Bytes3}
+ end;
+getBMPChars(Bytes,Len) ->
+ getBMPChars(Bytes,Len,[]).
+
+getBMPChars(Bytes,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getBMPChars(Bytes,Len,Acc) ->
+ {Octs,Bytes1} = getoctets_as_list(Bytes,2),
+ case Octs of
+ [0,O2] ->
+ getBMPChars(Bytes1,Len-1,[O2|Acc]);
+ [O1,O2]->
+ getBMPChars(Bytes1,Len-1,[{O1,O2}|Acc])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint PermittedAlphabet
+%% into account.
+%% This function does only encode the value part and NOT the length
+
+chars_encode(C,StringType,Value) ->
+ case {StringType,get_constraint(C,'PermittedAlphabet')} of
+ {'UniversalString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
+ _ ->
+ {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
+ chars_encode2(Value,NumBits,CharOutTab)
+ end.
+
+chars_encode2([H|T],NumBits,{Min,Max,notab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,H-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,element(H-Min+1,Tab)}|chars_encode2(T,NumBits,{Min,Max,Tab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [{bits,NumBits,element((A*B*C*D)-Min,Tab)}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_NumBits,{_Min,_Max,_Tab}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'GeneralString' ->
+ exit({error,{asn1,{not implemented,'GeneralString'}}});
+ 'GraphicString' ->
+ exit({error,{asn1,{not implemented,'GraphicString'}}});
+ 'TeletexString' ->
+ exit({error,{asn1,{not implemented,'TeletexString'}}});
+ 'VideotexString' ->
+ exit({error,{asn1,{not implemented,'VideotexString'}}});
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+%%Maybe used later
+%%get_MaxChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% lists:nth(length(Sv),Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#7F; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#7E; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $9; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#ffffffff;
+%% 'BMPString' ->
+%% 16#ffff
+%% end
+%% end.
+
+%%Maybe used later
+%%get_MinChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% hd(Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#00; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#20; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $\s; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#00;
+%% 'BMPString' ->
+%% 16#00
+%% end
+%% end.
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+%% This very inefficient and should be moved to compiletime
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B > 2, B =< 4 -> 4;
+ B when B > 4, B =< 8 -> 8;
+ B when B > 8, B =< 16 -> 16;
+ B when B > 16, B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+
+chars_decode(Bytes,_,'BMPString',C,Len) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ no ->
+ getBMPChars(Bytes,Len);
+ _ ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet constraint"}}})
+ end;
+chars_decode(Bytes,NumBits,StringType,C,Len) ->
+ CharInTab = get_CharInTab(C,StringType),
+ chars_decode2(Bytes,CharInTab,NumBits,Len).
+
+
+chars_decode2(Bytes,CharInTab,NumBits,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
+
+chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ Result = case minimum_octets(Char+Min) of
+ [NewChar] -> NewChar;
+ [C1,C2] -> {0,0,C1,C2};
+ [C1,C2,C3] -> {0,C1,C2,C3};
+ [C1,C2,C3,C4] -> {C1,C2,C3,C4}
+ end,
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
+
+%% BMPString and UniversalString with PermittedAlphabet is currently not supported
+chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
+
+
+ % X.691:17
+encode_null({Name,Val}) when atom(Name) ->
+ encode_null(Val);
+encode_null(_) -> []. % encodes to nothing
+
+decode_null(Bytes) ->
+ {'NULL',Bytes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier(Val) ->
+ Octets = e_object_identifier(Val,notag),
+ [{debug,object_identifier},encode_length(undefined,length(Octets)),{octets,Octets}].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V},DoTag) ->
+ e_object_identifier(V,DoTag);
+e_object_identifier({Cname,V},DoTag) when atom(Cname),tuple(V) ->
+ e_object_identifier(tuple_to_list(V),DoTag);
+e_object_identifier({Cname,V},DoTag) when atom(Cname),list(V) ->
+ e_object_identifier(V,DoTag);
+e_object_identifier(V,DoTag) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V),DoTag);
+
+% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
+e_object_identifier([E1,E2|Tail],_DoTag) when E1 =< 2 ->
+ Head = 40*E1 + E2, % weird
+ Res = e_object_elements([Head|Tail]),
+% dotag(DoTag,[6],elength(length(Res)+1),[Head|Res]),
+ Res.
+
+e_object_elements([]) ->
+ [];
+e_object_elements([H|T]) ->
+ lists:append(e_object_element(H),e_object_elements(T)).
+
+e_object_element(Num) when Num < 128 ->
+ [Num];
+% must be changed to handle more than 2 octets
+e_object_element(Num) -> %% when Num < ???
+ Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000,
+ Right = Num band 2#1111111 ,
+ [Left,Right].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes}
+%% ObjId -> {integer(),integer(),...} % at least 2 integers
+%% RemainingBytes -> [integer()] when integer() (0..255)
+decode_object_identifier(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ [First|Rest] = dec_subidentifiers(Octs,0,[]),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ {list_to_tuple(Idlist),Bytes3}.
+
+dec_subidentifiers([H|T],Av,Al) when H >=16#80 ->
+ dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al);
+dec_subidentifiers([H|T],Av,Al) ->
+ dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]);
+dec_subidentifiers([],_Av,Al) ->
+ lists:reverse(Al).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+complete(InList) when list(InList) ->
+ complete(InList,[],0);
+complete(InList) ->
+ complete([InList],[],0).
+
+complete([{debug,_}|T], Acc, Acclen) ->
+ complete(T,Acc,Acclen);
+complete([H|T],Acc,Acclen) when list(H) ->
+ complete(lists:concat([H,T]),Acc,Acclen);
+
+
+complete([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+ Newval = case N of
+ 1 ->
+ Val4 = Val band 16#FF,
+ [Val4];
+ 2 ->
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val3,Val4];
+ 3 ->
+ Val2 = (Val bsr 16) band 16#FF,
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val2,Val3,Val4];
+ 4 ->
+ Val1 = (Val bsr 24) band 16#FF,
+ Val2 = (Val bsr 16) band 16#FF,
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val1,Val2,Val3,Val4]
+ end,
+ complete([{octets,Newval}|T],Acc,Acclen);
+
+complete([{octets,Oct}|T],[],_Acclen) when list(Oct) ->
+ complete(T,lists:reverse(Oct),0);
+complete([{octets,Oct}|T],[Hacc|Tacc],Acclen) when list(Oct) ->
+ Rest = 8 - Acclen,
+ if
+ Rest == 8 ->
+ complete(T,lists:concat([lists:reverse(Oct),[Hacc|Tacc]]),0);
+ true ->
+ complete(T,lists:concat([lists:reverse(Oct),[Hacc bsl Rest|Tacc]]),0)
+ end;
+
+complete([{bit,Val}|T], Acc, Acclen) ->
+ complete([{bits,1,Val}|T],Acc,Acclen);
+complete([{octet,Val}|T], Acc, Acclen) ->
+ complete([{octets,1,Val}|T],Acc,Acclen);
+
+complete([{bits,N,Val}|T], Acc, 0) when N =< 8 ->
+ complete(T,[Val|Acc],N);
+complete([{bits,N,Val}|T], [Hacc|Tacc], Acclen) when N =< 8 ->
+ Rest = 8 - Acclen,
+ if
+ Rest >= N ->
+ complete(T,[(Hacc bsl N) + Val|Tacc],(Acclen+N) rem 8);
+ true ->
+ Diff = N - Rest,
+ NewHacc = (Hacc bsl Rest) + (Val bsr Diff),
+ Mask = element(Diff,{1,3,7,15,31,63,127,255}),
+ complete(T,[(Val band Mask),NewHacc|Tacc],(Acclen+N) rem 8)
+ end;
+complete([{bits,N,Val}|T], Acc, Acclen) -> % N > 8
+ complete([{bits,N-8,Val bsr 8},{bits,8,Val band 255}|T],Acc,Acclen);
+
+complete([align|T],Acc,0) ->
+ complete(T,Acc,0);
+complete([align|T],[Hacc|Tacc],Acclen) ->
+ Rest = 8 - Acclen,
+ complete(T,[Hacc bsl Rest|Tacc],0);
+complete([{octets,_N,Val}|T],Acc,Acclen) when list(Val) -> % no security check here
+ complete([{octets,Val}|T],Acc,Acclen);
+complete([],Acc,0) ->
+ lists:reverse(Acc);
+complete([],[Hacc|Tacc],Acclen) when Acclen > 0->
+ Rest = 8 - Acclen,
+ NewHacc = Hacc bsl Rest,
+ lists:reverse([NewHacc|Tacc]).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin.erl
new file mode 100644
index 0000000000..08a78165a2
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin.erl
@@ -0,0 +1,2182 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per_bin.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per_bin).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/2, fixoptionals/3,
+ fixextensions/2,
+ getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/2, getoptionals2/2, set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_small_number/1, encode_boolean/1,
+ decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1,
+ decode_compact_bit_string/3]).
+-export([decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([encode_UniversalString/2, decode_UniversalString/2,
+ encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ encode_VisibleString/2, decode_VisibleString/2,
+ encode_BMPString/2, decode_BMPString/2,
+ encode_IA5String/2, decode_IA5String/2,
+ encode_NumericString/2, decode_NumericString/2,
+ encode_ObjectDescriptor/2, decode_ObjectDescriptor/1
+ ]).
+-export([complete_bytes/1]).
+
+-define('16K',16384).
+-define('32K',32768).
+-define('64K',65536).
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%% converts a list to a record if necessary
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple;
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]).
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+ [{debug,choiceext},{bits,1,0}];
+setchoiceext(false) ->
+ [{debug,choiceext},{bits,1,1}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(false) ->
+ [{debug,ext},{bits,1,0}];
+setext(true) ->
+ [{debug,ext},{bits,1,1}].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This version of fixoptionals/2 are left only because of
+%% backward compatibility with older generates
+
+fixoptionals(OptList,Val) when tuple(Val) ->
+ fixoptionals1(OptList,Val,[]);
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals1(OptList,Val,1,[],[]).
+
+fixoptionals1([],Val,Acc) ->
+ %% return {Val,Opt}
+ {Val,lists:reverse(Acc)};
+fixoptionals1([{_,Pos}|Ot],Val,Acc) ->
+ case element(Pos+1,Val) of
+ asn1_NOVALUE -> fixoptionals1(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals1(Ot,Val,[0|Acc]);
+ _ -> fixoptionals1(Ot,Val,[1|Acc])
+ end.
+
+
+fixoptionals1([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals1(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals1([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals1(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals1(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals1(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals1([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals1([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals1([],[],_,Acc1,Acc2) ->
+ % return {Val,Opt}
+ {list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]),lists:reverse(Acc1)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This is the new fixoptionals/3 which is used by the new generates
+%%
+fixoptionals(OptList,OptLength,Val) when tuple(Val) ->
+ Bits = fixoptionals(OptList,Val,0),
+ {Val,{bits,OptLength,Bits}};
+
+fixoptionals([],_Val,Acc) ->
+ %% Optbits
+ Acc;
+fixoptionals([Pos|Ot],Val,Acc) ->
+ case element(Pos,Val) of
+ asn1_NOVALUE -> fixoptionals(Ot,Val,Acc bsl 1);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,Acc bsl 1);
+ _ -> fixoptionals(Ot,Val,(Acc bsl 1) + 1)
+ end.
+
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when binary(Bytes) ->
+ getbit({0,Bytes});
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_constrained_number(Bytes,{0,NumChoices-1}).
+
+%% old version kept for backward compatibility with generates from R7B
+getoptionals(Bytes,NumOpt) ->
+ {Blist,Bytes1} = getbits_as_list(NumOpt,Bytes),
+ {list_to_tuple(Blist),Bytes1}.
+
+%% new version used in generates from r8b_patch/3 and later
+getoptionals2(Bytes,NumOpt) ->
+ getbits(Bytes,NumOpt).
+
+
+%% getbits_as_binary(Num,Bytes) -> {{Unused,BinBits},RestBytes},
+%% Num = integer(),
+%% Bytes = list() | tuple(),
+%% Unused = integer(),
+%% BinBits = binary(),
+%% RestBytes = tuple()
+getbits_as_binary(Num,Bytes) when binary(Bytes) ->
+ getbits_as_binary(Num,{0,Bytes});
+getbits_as_binary(0,Buffer) ->
+ {{0,<<>>},Buffer};
+getbits_as_binary(Num,{0,Bin}) when Num > 16 ->
+ Used = Num rem 8,
+ Pad = (8 - Used) rem 8,
+% Nbytes = Num div 8,
+ <<Bits:Num,_:Pad,RestBin/binary>> = Bin,
+ {{Pad,<<Bits:Num,0:Pad>>},RestBin};
+getbits_as_binary(Num,Buffer={_Used,_Bin}) -> % Unaligned buffer
+ %% Num =< 16,
+ {Bits2,Buffer2} = getbits(Buffer,Num),
+ Pad = (8 - (Num rem 8)) rem 8,
+ {{Pad,<<Bits2:Num,0:Pad>>},Buffer2}.
+
+
+% integer_from_list(Int,[],BigInt) ->
+% BigInt;
+% integer_from_list(Int,[H|T],BigInt) when Int < 8 ->
+% (BigInt bsl Int) bor (H bsr (8-Int));
+% integer_from_list(Int,[H|T],BigInt) ->
+% integer_from_list(Int-8,T,(BigInt bsl 8) bor H).
+
+getbits_as_list(Num,Bytes) when binary(Bytes) ->
+ getbits_as_list(Num,{0,Bytes},[]);
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+%% If buffer is empty and nothing more will be picked.
+getbits_as_list(0, B, Acc) ->
+ {lists:reverse(Acc),B};
+%% If first byte in buffer is full and at least one byte will be picked,
+%% then pick one byte.
+getbits_as_list(N,{0,Bin},Acc) when N >= 8 ->
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Rest/binary>> = Bin,
+ getbits_as_list(N-8,{0,Rest},[B0,B1,B2,B3,B4,B5,B6,B7|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 4, Used =< 4 ->
+ NewUsed = Used + 4,
+ Rem = 8 - NewUsed,
+ <<_:Used,B3:1,B2:1,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-4,{NewUsed rem 8,NewRest},[B0,B1,B2,B3|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 2, Used =< 6 ->
+ NewUsed = Used + 2,
+ Rem = 8 - NewUsed,
+ <<_:Used,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-2,{NewUsed rem 8,NewRest},[B0,B1|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when Used =< 7 ->
+ NewUsed = Used + 1,
+ Rem = 8 - NewUsed,
+ <<_:Used,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-1,{NewUsed rem 8,NewRest},[B0|Acc]).
+
+
+getbit({7,<<_:7,B:1,Rest/binary>>}) ->
+ {B,{0,Rest}};
+getbit({0,Buffer = <<B:1,_:7,_/binary>>}) ->
+ {B,{1,Buffer}};
+getbit({Used,Buffer}) ->
+ Unused = (8 - Used) - 1,
+ <<_:Used,B:1,_:Unused,_/binary>> = Buffer,
+ {B,{Used+1,Buffer}};
+getbit(Buffer) when binary(Buffer) ->
+ getbit({0,Buffer}).
+
+
+getbits({0,Buffer},Num) when (Num rem 8) == 0 ->
+ <<Bits:Num,Rest/binary>> = Buffer,
+ {Bits,{0,Rest}};
+getbits({Used,Bin},Num) ->
+ NumPlusUsed = Num + Used,
+ NewUsed = NumPlusUsed rem 8,
+ Unused = (8-NewUsed) rem 8,
+ case Unused of
+ 0 ->
+ <<_:Used,Bits:Num,Rest/binary>> = Bin,
+ {Bits,{0,Rest}};
+ _ ->
+ Bytes = NumPlusUsed div 8,
+ <<_:Used,Bits:Num,_UBits:Unused,_/binary>> = Bin,
+ <<_:Bytes/binary,Rest/binary>> = Bin,
+ {Bits,{NewUsed,Rest}}
+ end;
+getbits(Bin,Num) when binary(Bin) ->
+ getbits({0,Bin},Num).
+
+
+
+% getoctet(Bytes) when list(Bytes) ->
+% getoctet({0,Bytes});
+% getoctet(Bytes) ->
+% %% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+% getoctet1(Bytes).
+
+% getoctet1({0,[H|T]}) ->
+% {H,{0,T}};
+% getoctet1({Pos,[_,H|T]}) ->
+% {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,<<_H,T/binary>>}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as an integer with bit significance as in buffer.
+getoctets({0,Buffer},Num) ->
+ <<Val:Num/integer-unit:8,RestBin/binary>> = Buffer,
+ {Val,{0,RestBin}};
+getoctets({U,<<_Padding,Rest/binary>>},Num) when U /= 0 ->
+ getoctets({0,Rest},Num);
+getoctets(Buffer,Num) when binary(Buffer) ->
+ getoctets({0,Buffer},Num).
+% getoctets(Buffer,Num) ->
+% %% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+% getoctets(Buffer,Num,0).
+
+% getoctets(Buffer,0,Acc) ->
+% {Acc,Buffer};
+% getoctets(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+% getoctets_as_list(Buffer,Num) ->
+% getoctets_as_list(Buffer,Num,[]).
+
+% getoctets_as_list(Buffer,0,Acc) ->
+% {lists:reverse(Acc),Buffer};
+% getoctets_as_list(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as a binary
+getoctets_as_bin({0,Bin},Num)->
+ <<Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin({_U,Bin},Num) ->
+ <<_Padding,Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin(Bin,Num) when binary(Bin) ->
+ getoctets_as_bin({0,Bin},Num).
+
+%% same as above but returns octets as a List
+getoctets_as_list(Buffer,Num) ->
+ {Bin,Buffer2} = getoctets_as_bin(Buffer,Num),
+ {binary_to_list(Bin),Buffer2}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+ [{bits,1,0}, % the value is in the root set
+ encode_integer([{'ValueRange',{0,Len1-1}}],N)];
+ N when integer(N) ->
+ [{bits,1,0}]; % no encoding if only 0 or 1 alternative
+ false ->
+ [{bits,1,1}, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_integer([{'ValueRange',{0,Len-1}}],N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_Alt,[],_Tag) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_fragmented_XXX; decode of values encoded fragmented according
+%% to ITU-T X.691 clause 10.9.3.8. The unit (XXX) is either bits, octets,
+%% characters or number of components (in a choice,sequence or similar).
+%% Buffer is a buffer {Used, Bin}.
+%% C is the constrained length.
+%% If the buffer is not aligned, this function does that.
+decode_fragmented_bits({0,Buffer},C) ->
+ decode_fragmented_bits(Buffer,C,[]);
+decode_fragmented_bits({_N,<<_,Bs/binary>>},C) ->
+ decode_fragmented_bits(Bs,C,[]).
+
+decode_fragmented_bits(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin, Len * ?'16K'),
+ decode_fragmented_bits(Bin2,C,[Value,Acc]);
+decode_fragmented_bits(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ BinBits = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int),C == size(BinBits) ->
+ {BinBits,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ {BinBits,{0,Bin}}
+ end;
+decode_fragmented_bits(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ Result = {BinBits,{Used,_Rest}} =
+ case (Len rem 8) of
+ 0 ->
+ <<Value:Len/binary-unit:1,Bin2/binary>> = Bin,
+ {list_to_binary(lists:reverse([Value|Acc])),{0,Bin2}};
+ Rem ->
+ Bytes = Len div 8,
+ U = 8 - Rem,
+ <<Value:Bytes/binary-unit:8,Bits1:Rem,Bits2:U,Bin2/binary>> = Bin,
+ {list_to_binary(lists:reverse([Bits1 bsl U,Value|Acc])),
+ {Rem,<<Bits2,Bin2/binary>>}}
+ end,
+ case C of
+ Int when integer(Int),C == (size(BinBits) - ((8 - Used) rem 8)) ->
+ Result;
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ Result
+ end.
+
+
+decode_fragmented_octets({0,Bin},C) ->
+ decode_fragmented_octets(Bin,C,[]);
+decode_fragmented_octets({_N,<<_,Bs/binary>>},C) ->
+ decode_fragmented_octets(Bs,C,[]).
+
+decode_fragmented_octets(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin,Len * ?'16K'),
+ decode_fragmented_octets(Bin2,C,[Value,Acc]);
+decode_fragmented_octets(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ Octets = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int), C == size(Octets) ->
+ {Octets,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,Octets}}});
+ _ ->
+ {Octets,{0,Bin}}
+ end;
+decode_fragmented_octets(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ <<Value:Len/binary-unit:8,Bin2/binary>> = Bin,
+ BinOctets = list_to_binary(lists:reverse([Value|Acc])),
+ case C of
+ Int when integer(Int),size(BinOctets) == Int ->
+ {BinOctets,Bin2};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinOctets}}});
+ _ ->
+ {BinOctets,Bin2}
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_C, Val) when list(Val) ->
+ Bin = list_to_binary(Val),
+ [encode_length(undefined,size(Bin)),{octets,Bin}]; % octets implies align
+encode_open_type(_C, Val) when binary(Val) ->
+ [encode_length(undefined,size(Val)),{octets,Val}]. % octets implies align
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _C) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_bin(Bytes2,Len).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_NamedNumberList) when integer(V) ->
+ encode_integer(C,V);
+encode_integer(C,{Name,V},NamedNumberList) when atom(Name) ->
+ encode_integer(C,V,NamedNumberList).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer([{Rc,_Ec}],Val) when tuple(Rc) -> % XXX when is this invoked? First argument most often a list,...Ok this is the extension case...but it doesn't work.
+ case (catch encode_integer([Rc],Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+ [{bits,1,1},encode_unconstrained_number(Val)];
+ Encoded ->
+ [{bits,1,0},Encoded]
+ end;
+encode_integer(C,Val ) when list(C) ->
+ case get_constraint(C,'SingleValue') of
+ no ->
+ encode_integer1(C,Val);
+ V when integer(V),V == Val ->
+ []; % a type restricted to a single value encodes to nothing
+ V when list(V) ->
+ case lists:member(Val,V) of
+ true ->
+ encode_integer1(C,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end.
+
+encode_integer1(C, Val) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ encode_unconstrained_number(Val);
+ {Lb,'MAX'} ->
+ encode_semi_constrained_number(Lb,Val);
+ %% positive with range
+ {Lb,Ub} when Val >= Lb,
+ Ub >= Val ->
+ encode_constrained_number(VR,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,VR,Val}}})
+ end.
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,[{Rc,_Ec}]) when tuple(Rc) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,[Rc]);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ V when list(V) ->
+ {Val,Buffer2} = decode_integer1(Buffer,C),
+ case lists:member(Val,V) of
+ true ->
+ {Val,Buffer2};
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_,_} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+ % X.691:10.6 Encoding of a normally small non-negative whole number
+ % Use this for encoding of CHOICE index if there is an extension marker in
+ % the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+% [{bits,1,0},{bits,6,Val}];
+ [{bits,7,Val}]; % same as above but more efficient
+encode_small_number(Val) ->
+ [{bits,1,1},encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,0)
+ end.
+
+%% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Oct = eint_positive(Val2),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ true ->
+ [encode_length(undefined,Len),{octets,Oct}]
+ end.
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+ {bits,1,Val2};
+ Range =< 4 ->
+ {bits,2,Val2};
+ Range =< 8 ->
+ {bits,3,Val2};
+ Range =< 16 ->
+ {bits,4,Val2};
+ Range =< 32 ->
+ {bits,5,Val2};
+ Range =< 64 ->
+ {bits,6,Val2};
+ Range =< 128 ->
+ {bits,7,Val2};
+ Range =< 255 ->
+ {bits,8,Val2};
+ Range =< 256 ->
+ {octets,[Val2]};
+ Range =< 65536 ->
+ {octets,<<Val2:16>>};
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+ [{bits,2,length(Octs)-1},{octets,Octs}];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ [{bits,2,length(Octs)-1},{octets,Octs}];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ [{bits,3,length(Octs)-1},{octets,Octs}];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end;
+encode_constrained_number(Range,Val) ->
+ exit({error,{asn1,{integer_range,Range,value,Val}}}).
+
+
+decode_constrained_number(Buffer,{Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+ % Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+%% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ true ->
+ [encode_length(undefined,Len),{octets,Oct}]
+ end;
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ true ->
+ [encode_length(undefined,Len),{octets,Oct}]
+ end.
+
+
+%% used for positive Values which don't need a sign bit
+%% returns a binary
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+% minimum_octets(Val) ->
+% minimum_octets(Val,[]).
+
+% minimum_octets(Val,Acc) when Val > 0 ->
+% minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+% minimum_octets(0,Acc) ->
+% Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+ {octets,[Len]};
+ Len < 16384 ->
+ {octets,<<2:2,Len:14>>};
+ true -> % should be able to endode length >= 16384
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length(Vr={Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number(Vr,Len);
+encode_length({Lb,_Ub},Len) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ encode_length(undefined,Len);
+encode_length({Vr={Lb,Ub},[]},Len) when Ub =< 65535 ,Lb >= 0 ->
+ %% constrained extensible
+ [{bits,1,0},encode_constrained_number(Vr,Len)];
+encode_length(SingleValue,_Len) when integer(SingleValue) ->
+ [].
+
+%% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension
+%% additions in a sequence or set
+encode_small_length(Len) when Len =< 64 ->
+%% [{bits,1,0},{bits,6,Len-1}];
+ {bits,7,Len-1}; % the same as above but more efficient
+encode_small_length(Len) ->
+ [{bits,1,1},encode_length(undefined,Len)].
+
+% decode_small_length({Used,<<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>>}) ->
+% case Buffer of
+% <<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>> ->
+% {Num,
+% case getbit(Buffer) of
+% {0,Remain} ->
+% {Bits,Remain2} = getbits(Remain,6),
+% {Bits+1,Remain2};
+% {1,Remain} ->
+% decode_length(Remain,undefined)
+% end.
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ {0,Buffer2} = align(Buffer),
+ case Buffer2 of
+ <<0:1,Oct:7,Rest/binary>> ->
+ {Oct,{0,Rest}};
+ <<2:2,Val:14,Rest/binary>> ->
+ {Val,{0,Rest}};
+ <<3:2,_:14,_Rest/binary>> ->
+ %% this case should be fixed
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}})
+ end;
+%% {Bits,_} = getbits(Buffer2,2),
+% case Bits of
+% 2 ->
+% {Val,Bytes3} = getoctets(Buffer2,2),
+% {(Val band 16#3FFF),Bytes3};
+% 3 ->
+% exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+% _ ->
+% {Val,Bytes3} = getoctet(Buffer2),
+% {Val band 16#7F,Bytes3}
+% end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+decode_length(_,{Lb,_}) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ exit({error,{asn1,{decode_length,{nyi,above_64K}}}});
+decode_length(Buffer,{{Lb,Ub},[]}) ->
+ case getbit(Buffer) of
+ {0,Buffer2} ->
+ decode_length(Buffer2, {Lb,Ub})
+ end;
+
+
+%When does this case occur with {_,_Lb,Ub} ??
+% X.691:10.9.3.5
+decode_length({Used,Bin},{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535
+ Unused = (8-Used) rem 8,
+ case Bin of
+ <<_:Used,0:1,Val:7,R:Unused,Rest/binary>> ->
+ {Val,{Used,<<R,Rest/binary>>}};
+ <<_:Used,_:Unused,2:2,Val:14,Rest/binary>> ->
+ {Val, {0,Rest}};
+ <<_:Used,_:Unused,3:2,_:14,_Rest/binary>> ->
+ exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}})
+ end;
+% decode_length(Buffer,{_,_Lb,Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+% case getbit(Buffer) of
+% {0,Remain} ->
+% getbits(Remain,7);
+% {1,Remain} ->
+% {Val,Remain2} = getoctets(Buffer,2),
+% {Val band 2#0111111111111111, Remain2}
+% end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+ % X.691:11
+encode_boolean(true) ->
+ {bits,1,1};
+encode_boolean(false) ->
+ {bits,1,0};
+encode_boolean({Name,Val}) when atom(Name) ->
+ encode_boolean(Val);
+encode_boolean(Val) ->
+ exit({error,{asn1,{encode_boolean,Val}}}).
+
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+
+%% when the value is a list of {Unused,BinBits}, where
+%% Unused = integer(),
+%% BinBits = binary().
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList) when integer(Unused),
+ binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList);
+
+%% when the value is a list of named bits
+encode_bit_string(C, LoNB=[FirstVal | _RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes(LoNB, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+encode_bit_string(C, BL=[{bit,_No} | _RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes(BL, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+
+% encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+% Bl1 =
+% case NamedBitList of
+% [] -> % dont remove trailing zeroes
+% BitListValue;
+% _ -> % first remove any trailing zeroes
+% lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+% lists:reverse(BitListValue)))
+% end,
+% BitList = [{bit,X} || X <- Bl1],
+% %% BListLen = length(BitList),
+% case get_constraint(C,'SizeConstraint') of
+% 0 -> % fixed length
+% []; % nothing to encode
+% V when integer(V),V=<16 -> % fixed length 16 bits or less
+% pad_list(V,BitList);
+% V when integer(V) -> % fixed length 16 bits or more
+% [align,pad_list(V,BitList)]; % should be another case for V >= 65537
+% {Lb,Ub} when integer(Lb),integer(Ub) ->
+% [encode_length({Lb,Ub},length(BitList)),align,BitList];
+% no ->
+% [encode_length(undefined,length(BitList)),align,BitList];
+% Sc -> % extension marker
+% [encode_length(Sc,length(BitList)),align,BitList]
+% end;
+encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+ BitListToBinary =
+ %% fun that transforms a list of 1 and 0 to a tuple:
+ %% {UnusedBitsInLastByte, Binary}
+ fun([H|T],Acc,N,Fun) ->
+ Fun(T,(Acc bsl 1)+H,N+1,Fun);
+ ([],Acc,N,_) ->
+ Unused = (8 - (N rem 8)) rem 8,
+ {Unused,<<Acc:N,0:Unused>>}
+ end,
+ UnusedAndBin =
+ case NamedBitList of
+ [] -> % dont remove trailing zeroes
+ BitListToBinary(BitListValue,0,0,BitListToBinary);
+ _ ->
+ BitListToBinary(lists:reverse(
+ lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue))),
+ 0,0,BitListToBinary)
+ end,
+ encode_bin_bit_string(C,UnusedAndBin,NamedBitList);
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) when integer(IntegerVal)->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a tuple
+encode_bit_string(C,{Name,Val}, NamedBitList) when atom(Name) ->
+ encode_bit_string(C,Val,NamedBitList).
+
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),i.e. number unused bits in least sign. byte of
+%% BinBits = binary().
+
+
+encode_bin_bit_string(C,UnusedAndBin={_Unused,_BinBits},NamedBitList) ->
+ Constr = get_constraint(C,'SizeConstraint'),
+ UnusedAndBin1 = {Unused1,Bin1} =
+ remove_trailing_bin(NamedBitList,UnusedAndBin,lower_bound(Constr)),
+ case Constr of
+ 0 ->
+ [];
+ V when integer(V),V=<16 ->
+ {Unused2,Bin2} = pad_list(V,UnusedAndBin1),
+ <<BitVal:V,_:Unused2>> = Bin2,
+ {bits,V,BitVal};
+ V when integer(V) ->
+ [align, pad_list(V, UnusedAndBin1)];
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ [encode_length({Lb,Ub},size(Bin1)*8 - Unused1),
+ align,UnusedAndBin1];
+ no ->
+ [encode_length(undefined,size(Bin1)*8 - Unused1),
+ align,UnusedAndBin1];
+ Sc ->
+ [encode_length(Sc,size(Bin1)*8 - Unused1),
+ align,UnusedAndBin1]
+ end.
+
+remove_trailing_bin([], {Unused,Bin},_) ->
+ {Unused,Bin};
+remove_trailing_bin(NamedNumberList, {_Unused,Bin},C) ->
+ Size = size(Bin)-1,
+ <<Bfront:Size/binary, LastByte:8>> = Bin,
+ %% clear the Unused bits to be sure
+% LastByte1 = LastByte band (((1 bsl Unused) -1) bxor 255),
+ Unused1 = trailingZeroesInNibble(LastByte band 15),
+ Unused2 =
+ case Unused1 of
+ 4 ->
+ 4 + trailingZeroesInNibble(LastByte bsr 4);
+ _ -> Unused1
+ end,
+ case Unused2 of
+ 8 ->
+ remove_trailing_bin(NamedNumberList,{0,Bfront},C);
+ _ ->
+ case C of
+ Int when integer(Int),Int > ((size(Bin)*8)-Unused2) ->
+ %% this padding see OTP-4353
+ pad_list(Int,{Unused2,Bin});
+ _ -> {Unused2,Bin}
+ end
+ end.
+
+
+trailingZeroesInNibble(0) ->
+ 4;
+trailingZeroesInNibble(1) ->
+ 0;
+trailingZeroesInNibble(2) ->
+ 1;
+trailingZeroesInNibble(3) ->
+ 0;
+trailingZeroesInNibble(4) ->
+ 2;
+trailingZeroesInNibble(5) ->
+ 0;
+trailingZeroesInNibble(6) ->
+ 1;
+trailingZeroesInNibble(7) ->
+ 0;
+trailingZeroesInNibble(8) ->
+ 3;
+trailingZeroesInNibble(9) ->
+ 0;
+trailingZeroesInNibble(10) ->
+ 1;
+trailingZeroesInNibble(11) ->
+ 0;
+trailingZeroesInNibble(12) -> %#1100
+ 2;
+trailingZeroesInNibble(13) ->
+ 0;
+trailingZeroesInNibble(14) ->
+ 1;
+trailingZeroesInNibble(15) ->
+ 0.
+
+lower_bound({{Lb,_},_}) when integer(Lb) ->
+ Lb;
+lower_bound({Lb,_}) when integer(Lb) ->
+ Lb;
+lower_bound(C) ->
+ C.
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a tuple {Unused,Bits}. Unused is the number of unused
+%% bits, least significant bits in the last byte of Bits. Bits is
+%% the BIT STRING represented as a binary.
+%%
+decode_compact_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {{8,0},Buffer};
+ V when integer(V),V=<16 -> %fixed length 16 bits or less
+ compact_bit_string(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 -> %fixed length > 16 bits
+ Bytes2 = align(Buffer),
+ compact_bit_string(Bytes2,V,NamedNumberList);
+ V when integer(V) -> % V > 65536 => fragmented value
+ {Bin,Buffer2} = decode_fragmented_bits(Buffer,V),
+ case Buffer2 of
+ {0,_} -> {{0,Bin},Buffer2};
+ {U,_} -> {{8-U,Bin},Buffer2}
+ end;
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ no ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ Sc ->
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_or_named(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 ->
+ Bytes2 = align(Buffer),
+ bit_list_or_named(Bytes2,V,NamedNumberList);
+ V when integer(V) ->
+ Bytes2 = align(Buffer),
+ {BinBits,_} = decode_fragmented_bits(Bytes2,V),
+ bit_list_or_named(BinBits,V,NamedNumberList);
+ Sc -> % extension marker
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%% if no named bits are declared we will return a
+%% {Unused,Bits}. Unused = integer(),
+%% Bits = binary().
+compact_bit_string(Buffer,Len,[]) ->
+ getbits_as_binary(Len,Buffer); % {{Unused,BinBits},NewBuffer}
+compact_bit_string(Buffer,Len,NamedNumberList) ->
+ bit_list_or_named(Buffer,Len,NamedNumberList).
+
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_or_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_or_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_or_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_or_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_or_named1(Pos+1,Bt,Names,Acc);
+bit_list_or_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_or_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_or_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_or_named1(_,[],_,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(Int) when integer(Int), Int > 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)];
+int_to_bitlist(0) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+pad_list(N,In={Unused,Bin}) ->
+ pad_list(N, size(Bin)*8 - Unused, In).
+
+pad_list(N,Size,In={_,_}) when N < Size ->
+ exit({error,{asn1,{range_error,{bit_string,In}}}});
+pad_list(N,Size,{Unused,Bin}) when N > Size, Unused > 0 ->
+ pad_list(N,Size+1,{Unused-1,Bin});
+pad_list(N,Size,{_Unused,Bin}) when N > Size ->
+ pad_list(N,Size+1,{7,<<Bin/binary,0>>});
+pad_list(N,N,In={_,_}) ->
+ In.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(C,Bool,{_Name,Val}) ->
+ encode_octet_string(C,Bool,Val);
+encode_octet_string(_,true,_) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(C,false,Val) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ [];
+ 1 ->
+ [V] = Val,
+ {bits,8,V};
+ 2 ->
+ [V1,V2] = Val,
+ [{bits,8,V1},{bits,8,V2}];
+ Sv when Sv =<65535, Sv == length(Val) -> % fixed length
+ {octets,Val};
+ {Lb,Ub} ->
+ [encode_length({Lb,Ub},length(Val)),{octets,Val}];
+ Sv when list(Sv) ->
+ [encode_length({hd(Sv),lists:max(Sv)},length(Val)),{octets,Val}];
+ no ->
+ [encode_length(undefined,length(Val)),{octets,Val}]
+ end.
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,C,false) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ {[],Bytes};
+ 1 ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+ 2 ->
+ {Bs,Bytes2}= getbits(Bytes,16),
+ {binary_to_list(<<Bs:16>>),Bytes2};
+ {_,0} ->
+ {[],Bytes};
+ Sv when integer(Sv), Sv =<65535 -> % fixed length
+ getoctets_as_list(Bytes,Sv);
+ Sv when integer(Sv) -> % fragmented encoding
+ Bytes2 = align(Bytes),
+ decode_fragmented_octets(Bytes2,Sv);
+ {Lb,Ub} ->
+ {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
+ getoctets_as_list(Bytes2,Len);
+ Sv when list(Sv) ->
+ {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
+ getoctets_as_list(Bytes2,Len);
+ no ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_list(Bytes2,Len)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val)
+
+
+encode_restricted_string(aligned,{Name,Val}) when atom(Name) ->
+ encode_restricted_string(aligned,Val);
+
+encode_restricted_string(aligned,Val) when list(Val)->
+ [encode_length(undefined,length(Val)),{octets,Val}].
+
+encode_known_multiplier_string(aligned,StringType,C,_Ext,{Name,Val}) when atom(Name) ->
+ encode_known_multiplier_string(aligned,StringType,C,false,Val);
+
+encode_known_multiplier_string(aligned,StringType,C,_Ext,Val) ->
+ Result = chars_encode(C,StringType,Val),
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ case {StringType,Result} of
+ {'BMPString',{octets,Ol}} ->
+ [{bits,8,Oct}||Oct <- Ol];
+ _ ->
+ Result
+ end;
+ 0 ->
+ [];
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ [align,Result];
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb},length(Val)),align,Result];
+ Vl when list(Vl) ->
+ [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),align,Result];
+ no ->
+ [encode_length(undefined,length(Val)),align,Result]
+ end.
+
+decode_restricted_string(Bytes,aligned) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_list(Bytes2,Len).
+
+decode_known_multiplier_string(Bytes,aligned,StringType,C,_Ext) ->
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ chars_decode(Bytes,NumBits,StringType,C,Ub);
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ Bytes1 = align(Bytes),
+ chars_decode(Bytes1,NumBits,StringType,C,Ub);
+ 0 ->
+ {[],Bytes};
+ Vl when list(Vl) ->
+ {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ no ->
+ {Len,Bytes1} = decode_length(Bytes,undefined),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ {Lb,Ub}->
+ {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len)
+ end.
+
+
+encode_NumericString(C,Val) ->
+ encode_known_multiplier_string(aligned,'NumericString',C,false,Val).
+decode_NumericString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'NumericString',C,false).
+
+encode_PrintableString(C,Val) ->
+ encode_known_multiplier_string(aligned,'PrintableString',C,false,Val).
+decode_PrintableString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'PrintableString',C,false).
+
+encode_VisibleString(C,Val) -> % equivalent with ISO646String
+ encode_known_multiplier_string(aligned,'VisibleString',C,false,Val).
+decode_VisibleString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'VisibleString',C,false).
+
+encode_IA5String(C,Val) ->
+ encode_known_multiplier_string(aligned,'IA5String',C,false,Val).
+decode_IA5String(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'IA5String',C,false).
+
+encode_BMPString(C,Val) ->
+ encode_known_multiplier_string(aligned,'BMPString',C,false,Val).
+decode_BMPString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'BMPString',C,false).
+
+encode_UniversalString(C,Val) ->
+ encode_known_multiplier_string(aligned,'UniversalString',C,false,Val).
+decode_UniversalString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'UniversalString',C,false).
+
+%% end of known-multiplier strings for which PER visible constraints are
+%% applied
+
+encode_GeneralString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GeneralString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_GraphicString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GraphicString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_ObjectDescriptor(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_ObjectDescriptor(Bytes) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_TeletexString(_C,Val) -> % equivalent with T61String
+ encode_restricted_string(aligned,Val).
+decode_TeletexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_VideotexString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_VideotexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
+%%
+getBMPChars(Bytes,1) ->
+ {O1,Bytes2} = getbits(Bytes,8),
+ {O2,Bytes3} = getbits(Bytes2,8),
+ if
+ O1 == 0 ->
+ {[O2],Bytes3};
+ true ->
+ {[{0,0,O1,O2}],Bytes3}
+ end;
+getBMPChars(Bytes,Len) ->
+ getBMPChars(Bytes,Len,[]).
+
+getBMPChars(Bytes,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getBMPChars(Bytes,Len,Acc) ->
+ {Octs,Bytes1} = getoctets_as_list(Bytes,2),
+ case Octs of
+ [0,O2] ->
+ getBMPChars(Bytes1,Len-1,[O2|Acc]);
+ [O1,O2]->
+ getBMPChars(Bytes1,Len-1,[{0,0,O1,O2}|Acc])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint PermittedAlphabet
+%% into account.
+%% This function does only encode the value part and NOT the length
+
+chars_encode(C,StringType,Value) ->
+ case {StringType,get_constraint(C,'PermittedAlphabet')} of
+ {'UniversalString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
+ _ ->
+ {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
+ chars_encode2(Value,NumBits,CharOutTab)
+ end.
+
+chars_encode2([H|T],NumBits,{Min,Max,notab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,H-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,exit_if_false(H,element(H-Min+1,Tab))}|chars_encode2(T,NumBits,{Min,Max,Tab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+ [{bits,NumBits,((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,element((A*B*C*D)-Min,Tab)}|chars_encode2(T,NumBits,{Min,Max,notab})];
+ [{bits,NumBits,exit_if_false({A,B,C,D},element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab))}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_,{_,_,_}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+exit_if_false(V,false)->
+ exit({error,{asn1,{"illegal value according to Permitted alphabet constraint",V}}});
+exit_if_false(_,V) ->V.
+
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+%%Maybe used later
+%%get_MaxChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% lists:nth(length(Sv),Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#7F; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#7E; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $9; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#ffffffff;
+%% 'BMPString' ->
+%% 16#ffff
+%% end
+%% end.
+
+%%Maybe used later
+%%get_MinChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% hd(Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#00; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#20; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $\s; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#00;
+%% 'BMPString' ->
+%% 16#00
+%% end
+%% end.
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+%% This very inefficient and should be moved to compiletime
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B =< 4 -> 4;
+ B when B =< 8 -> 8;
+ B when B =< 16 -> 16;
+ B when B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+
+chars_decode(Bytes,_,'BMPString',C,Len) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ no ->
+ getBMPChars(Bytes,Len);
+ _ ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet constraint"}}})
+ end;
+chars_decode(Bytes,NumBits,StringType,C,Len) ->
+ CharInTab = get_CharInTab(C,StringType),
+ chars_decode2(Bytes,CharInTab,NumBits,Len).
+
+
+chars_decode2(Bytes,CharInTab,NumBits,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
+
+chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ Result =
+ if
+ Char < 256 -> Char;
+ true ->
+ list_to_tuple(binary_to_list(<<Char:32>>))
+ end,
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+% chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+% {Char,Bytes2} = getbits(Bytes,NumBits),
+% Result = case minimum_octets(Char+Min) of
+% [NewChar] -> NewChar;
+% [C1,C2] -> {0,0,C1,C2};
+% [C1,C2,C3] -> {0,C1,C2,C3};
+% [C1,C2,C3,C4] -> {C1,C2,C3,C4}
+% end,
+% chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
+
+%% BMPString and UniversalString with PermittedAlphabet is currently not supported
+chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
+
+
+ % X.691:17
+encode_null(_) -> []; % encodes to nothing
+encode_null({Name,Val}) when atom(Name) ->
+ encode_null(Val).
+
+decode_null(Bytes) ->
+ {'NULL',Bytes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier({Name,Val}) when atom(Name) ->
+ encode_object_identifier(Val);
+encode_object_identifier(Val) ->
+ OctetList = e_object_identifier(Val),
+ Octets = list_to_binary(OctetList), % performs a flatten at the same time
+ [{debug,object_identifier},encode_length(undefined,size(Octets)),{octets,Octets}].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname,V}) when atom(Cname),tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname,V}) when atom(Cname),list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
+e_object_identifier([E1,E2|Tail]) when E1 >= 0, E1 < 2, E2 < 40 ; E1==2 ->
+ Head = 40*E1 + E2, % weird
+ e_object_elements([Head|Tail],[]);
+e_object_identifier(Oid=[_,_|_Tail]) ->
+ exit({error,{asn1,{'illegal_value',Oid}}}).
+
+e_object_elements([],Acc) ->
+ lists:reverse(Acc);
+e_object_elements([H|T],Acc) ->
+ e_object_elements(T,[e_object_element(H)|Acc]).
+
+e_object_element(Num) when Num < 128 ->
+ Num;
+%% must be changed to handle more than 2 octets
+e_object_element(Num) -> %% when Num < ???
+ Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000,
+ Right = Num band 2#1111111 ,
+ [Left,Right].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes}
+%% ObjId -> {integer(),integer(),...} % at least 2 integers
+%% RemainingBytes -> [integer()] when integer() (0..255)
+decode_object_identifier(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ [First|Rest] = dec_subidentifiers(Octs,0,[]),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ {list_to_tuple(Idlist),Bytes3}.
+
+dec_subidentifiers([H|T],Av,Al) when H >=16#80 ->
+ dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al);
+dec_subidentifiers([H|T],Av,Al) ->
+ dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]);
+dec_subidentifiers([],_Av,Al) ->
+ lists:reverse(Al).
+
+get_constraint([{Key,V}],Key) ->
+ V;
+get_constraint([],_Key) ->
+ no;
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+
+% complete(L) ->
+% case complete1(L) of
+% {[],0} ->
+% <<0>>;
+% {Acc,0} ->
+% lists:reverse(Acc);
+% {[Hacc|Tacc],Acclen} -> % Acclen >0
+% Rest = 8 - Acclen,
+% NewHacc = Hacc bsl Rest,
+% lists:reverse([NewHacc|Tacc])
+% end.
+
+
+% complete1(InList) when list(InList) ->
+% complete1(InList,[]);
+% complete1(InList) ->
+% complete1([InList],[]).
+
+% complete1([{debug,_}|T], Acc) ->
+% complete1(T,Acc);
+% complete1([H|T],Acc) when list(H) ->
+% {NewH,NewAcclen} = complete1(H,Acc),
+% complete1(T,NewH,NewAcclen);
+
+% complete1([{0,Bin}|T],Acc,0) when binary(Bin) ->
+% complete1(T,[Bin|Acc],0);
+% complete1([{Unused,Bin}|T],Acc,0) when integer(Unused),binary(Bin) ->
+% Size = size(Bin)-1,
+% <<Bs:Size/binary,B>> = Bin,
+% complete1(T,[(B bsr Unused),Bs|Acc],8-Unused);
+% complete1([{Unused,Bin}|T],[Hacc|Tacc],Acclen) when integer(Unused),binary(Bin) ->
+% Rest = 8 - Acclen,
+% Used = 8 - Unused,
+% case size(Bin) of
+% 1 ->
+% if
+% Rest >= Used ->
+% <<B:Used,_:Unused>> = Bin,
+% complete1(T,[(Hacc bsl Used) + B|Tacc],
+% (Acclen+Used) rem 8);
+% true ->
+% LeftOver = 8 - Rest - Unused,
+% <<Val2:Rest,Val1:LeftOver,_:Unused>> = Bin,
+% complete1(T,[Val1,(Hacc bsl Rest) + Val2|Tacc],
+% (Acclen+Used) rem 8)
+% end;
+% N ->
+% if
+% Rest == Used ->
+% N1 = N - 1,
+% <<B:Rest,Bs:N1/binary,_:Unused>> = Bin,
+% complete1(T,[Bs,(Hacc bsl Rest) + B|Tacc],0);
+% Rest > Used ->
+% N1 = N - 2,
+% N2 = (8 - Rest) + Used,
+% <<B1:Rest,Bytes:N1/binary,B2:N2,_:Unused>> = Bin,
+% complete1(T,[B2,Bytes,(Hacc bsl Rest) + B1|Tacc],
+% (Acclen + Used) rem 8);
+% true -> % Rest < Used
+% N1 = N - 1,
+% N2 = Used - Rest,
+% <<B1:Rest,Bytes:N1/binary,B2:N2,_:Unused>> = Bin,
+% complete1(T,[B2,Bytes,(Hacc bsl Rest) + B1|Tacc],
+% (Acclen + Used) rem 8)
+% end
+% end;
+
+% %complete1([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+% % complete1([{octets,<<Val:N/unit:8>>}|T],Acc,Acclen);
+% complete1([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+% Newval = case N of
+% 1 ->
+% Val4 = Val band 16#FF,
+% [Val4];
+% 2 ->
+% Val3 = (Val bsr 8) band 16#FF,
+% Val4 = Val band 16#FF,
+% [Val3,Val4];
+% 3 ->
+% Val2 = (Val bsr 16) band 16#FF,
+% Val3 = (Val bsr 8) band 16#FF,
+% Val4 = Val band 16#FF,
+% [Val2,Val3,Val4];
+% 4 ->
+% Val1 = (Val bsr 24) band 16#FF,
+% Val2 = (Val bsr 16) band 16#FF,
+% Val3 = (Val bsr 8) band 16#FF,
+% Val4 = Val band 16#FF,
+% [Val1,Val2,Val3,Val4]
+% end,
+% complete1([{octets,Newval}|T],Acc,Acclen);
+
+% complete1([{octets,Bin}|T],Acc,Acclen) when binary(Bin) ->
+% Rest = 8 - Acclen,
+% if
+% Rest == 8 ->
+% complete1(T,[Bin|Acc],0);
+% true ->
+% [Hacc|Tacc]=Acc,
+% complete1(T,[Bin, Hacc bsl Rest|Tacc],0)
+% end;
+
+% complete1([{octets,Oct}|T],Acc,Acclen) when list(Oct) ->
+% Rest = 8 - Acclen,
+% if
+% Rest == 8 ->
+% complete1(T,[list_to_binary(Oct)|Acc],0);
+% true ->
+% [Hacc|Tacc]=Acc,
+% complete1(T,[list_to_binary(Oct), Hacc bsl Rest|Tacc],0)
+% end;
+
+% complete1([{bit,Val}|T], Acc, Acclen) ->
+% complete1([{bits,1,Val}|T],Acc,Acclen);
+% complete1([{octet,Val}|T], Acc, Acclen) ->
+% complete1([{octets,1,Val}|T],Acc,Acclen);
+
+% complete1([{bits,N,Val}|T], Acc, 0) when N =< 8 ->
+% complete1(T,[Val|Acc],N);
+% complete1([{bits,N,Val}|T], [Hacc|Tacc], Acclen) when N =< 8 ->
+% Rest = 8 - Acclen,
+% if
+% Rest >= N ->
+% complete1(T,[(Hacc bsl N) + Val|Tacc],(Acclen+N) rem 8);
+% true ->
+% Diff = N - Rest,
+% NewHacc = (Hacc bsl Rest) + (Val bsr Diff),
+% Mask = element(Diff,{1,3,7,15,31,63,127,255}),
+% complete1(T,[(Val band Mask),NewHacc|Tacc],(Acclen+N) rem 8)
+% end;
+% complete1([{bits,N,Val}|T], Acc, Acclen) -> % N > 8
+% complete1([{bits,N-8,Val bsr 8},{bits,8,Val band 255}|T],Acc,Acclen);
+
+% complete1([align|T],Acc,0) ->
+% complete1(T,Acc,0);
+% complete1([align|T],[Hacc|Tacc],Acclen) ->
+% Rest = 8 - Acclen,
+% complete1(T,[Hacc bsl Rest|Tacc],0);
+% complete1([{octets,N,Val}|T],Acc,Acclen) when list(Val) -> % no security check here
+% complete1([{octets,Val}|T],Acc,Acclen);
+
+% complete1([],Acc,Acclen) ->
+% {Acc,Acclen}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+
+complete(L) ->
+ case complete1(L) of
+ {[],[]} ->
+ <<0>>;
+ {Acc,[]} ->
+ Acc;
+ {Acc,Bacc} ->
+ [Acc|complete_bytes(Bacc)]
+ end.
+
+%% this function builds the ugly form of lists [E1|E2] to avoid having to reverse it at the end.
+%% this is done because it is efficient and that the result always will be sent on a port or
+%% converted by means of list_to_binary/1
+complete1(InList) when list(InList) ->
+ complete1(InList,[],[]);
+complete1(InList) ->
+ complete1([InList],[],[]).
+
+complete1([],Acc,Bacc) ->
+ {Acc,Bacc};
+complete1([H|T],Acc,Bacc) when list(H) ->
+ {NewH,NewBacc} = complete1(H,Acc,Bacc),
+ complete1(T,NewH,NewBacc);
+
+complete1([{octets,Bin}|T],Acc,[]) ->
+ complete1(T,[Acc|Bin],[]);
+
+complete1([{octets,Bin}|T],Acc,Bacc) ->
+ complete1(T,[Acc|[complete_bytes(Bacc),Bin]],[]);
+
+complete1([{debug,_}|T], Acc,Bacc) ->
+ complete1(T,Acc,Bacc);
+
+complete1([{bits,N,Val}|T],Acc,Bacc) ->
+ complete1(T,Acc,complete_update_byte(Bacc,Val,N));
+
+complete1([{bit,Val}|T],Acc,Bacc) ->
+ complete1(T,Acc,complete_update_byte(Bacc,Val,1));
+
+complete1([align|T],Acc,[]) ->
+ complete1(T,Acc,[]);
+complete1([align|T],Acc,Bacc) ->
+ complete1(T,[Acc|complete_bytes(Bacc)],[]);
+complete1([{0,Bin}|T],Acc,[]) when binary(Bin) ->
+ complete1(T,[Acc|Bin],[]);
+complete1([{Unused,Bin}|T],Acc,[]) when integer(Unused),binary(Bin) ->
+ Size = size(Bin)-1,
+ <<Bs:Size/binary,B>> = Bin,
+ NumBits = 8-Unused,
+ complete1(T,[Acc|Bs],[[B bsr Unused]|NumBits]);
+complete1([{Unused,Bin}|T],Acc,Bacc) when integer(Unused),binary(Bin) ->
+ Size = size(Bin)-1,
+ <<Bs:Size/binary,B>> = Bin,
+ NumBits = 8 - Unused,
+ Bf = complete_bytes(Bacc),
+ complete1(T,[Acc|[Bf,Bs]],[[B bsr Unused]|NumBits]).
+
+
+complete_update_byte([],Val,Len) ->
+ complete_update_byte([[0]|0],Val,Len);
+complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) when NumBits + Len == 8 ->
+ [[0,((Byte bsl Len) + Val) band 255|Bacc]|0];
+complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) when NumBits + Len > 8 ->
+ Rem = 8 - NumBits,
+ Rest = Len - Rem,
+ complete_update_byte([[0,((Byte bsl Rem) + (Val bsr Rest)) band 255 |Bacc]|0],Val,Rest);
+complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) ->
+ [[((Byte bsl Len) + Val) band 255|Bacc]|NumBits+Len].
+
+
+complete_bytes([[_Byte|Bacc]|0]) ->
+ lists:reverse(Bacc);
+complete_bytes([[Byte|Bacc]|NumBytes]) ->
+ lists:reverse([(Byte bsl (8-NumBytes)) band 255|Bacc]);
+complete_bytes([]) ->
+ [].
+
+% complete_bytes(L) ->
+% complete_bytes1(lists:reverse(L),[],[],0,0).
+
+% complete_bytes1([H={V,B}|T],Acc,ReplyAcc,NumBits,NumFields) when ((NumBits+B) rem 8) == 0 ->
+% NewReplyAcc = [complete_bytes2([H|Acc],0)|ReplyAcc],
+% complete_bytes1(T,[],NewReplyAcc,0,0);
+% complete_bytes1([H={V,B}|T],Acc,ReplyAcc,NumBits,NumFields) when NumFields == 7; (NumBits+B) div 8 > 0 ->
+% Rem = (NumBits+B) rem 8,
+% NewReplyAcc = [complete_bytes2([{V bsr Rem,B - Rem}|Acc],0)|ReplyAcc],
+% complete_bytes1([{V,Rem}|T],[],NewReplyAcc,0,0);
+% complete_bytes1([H={V,B}|T],Acc,ReplyAcc,NumBits,NumFields) ->
+% complete_bytes1(T,[H|Acc],ReplyAcc,NumBits+B,NumFields+1);
+% complete_bytes1([],[],ReplyAcc,_,_) ->
+% lists:reverse(ReplyAcc);
+% complete_bytes1([],Acc,ReplyAcc,NumBits,_) ->
+% PadBits = case NumBits rem 8 of
+% 0 -> 0;
+% Rem -> 8 - Rem
+% end,
+% lists:reverse([complete_bytes2(Acc,PadBits)|ReplyAcc]).
+
+
+% complete_bytes2([{V1,B1}],PadBits) ->
+% <<V1:B1,0:PadBits>>;
+% complete_bytes2([{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,0:PadBits>>;
+% complete_bytes2([{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,V3:B3,0:PadBits>>;
+% complete_bytes2([{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,V3:B3,V4:B4,0:PadBits>>;
+% complete_bytes2([{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,V3:B3,V4:B4,V5:B5,0:PadBits>>;
+% complete_bytes2([{V6,B6},{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,V3:B3,V4:B4,V5:B5,V6:B6,0:PadBits>>;
+% complete_bytes2([{V7,B7},{V6,B6},{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,V3:B3,V4:B4,V5:B5,V6:B6,V7:B7,0:PadBits>>;
+% complete_bytes2([{V8,B8},{V7,B7},{V6,B6},{V5,B5},{V4,B4},{V3,B3},{V2,B2},{V1,B1}],PadBits) ->
+% <<V1:B1,V2:B2,V3:B3,V4:B4,V5:B5,V6:B6,V7:B7,V8:B8,0:PadBits>>.
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
new file mode 100644
index 0000000000..0647650ea6
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_bin_rt2ct.erl
@@ -0,0 +1,2102 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per_bin_rt2ct.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per_bin_rt2ct).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/3, fixextensions/2,
+ getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/2, getoptionals2/2,
+ set_choice/3, encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_small_number/1,
+ decode_boolean/1, encode_length/2, decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1,
+ decode_compact_bit_string/3]).
+-export([decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([%encode_UniversalString/2, decode_UniversalString/2,
+ %encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ %encode_VisibleString/2, decode_VisibleString/2,
+ %encode_BMPString/2, decode_BMPString/2,
+ %encode_IA5String/2, decode_IA5String/2,
+ %encode_NumericString/2, decode_NumericString/2,
+ encode_ObjectDescriptor/2, decode_ObjectDescriptor/1
+ ]).
+
+-export([decode_constrained_number/2,
+ decode_constrained_number/3,
+ decode_unconstrained_number/1,
+ decode_semi_constrained_number/2,
+ encode_unconstrained_number/1,
+ decode_constrained_number/4,
+ encode_octet_string/3,
+ decode_octet_string/3,
+ encode_known_multiplier_string/5,
+ decode_known_multiplier_string/5,
+ getoctets/2, getbits/2
+% start_drv/1,start_drv2/1,init_drv/1
+ ]).
+
+
+-export([eint_positive/1]).
+-export([pre_complete_bits/2]).
+
+-define('16K',16384).
+-define('32K',32768).
+-define('64K',65536).
+
+%%-define(nodriver,true).
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+%% converts a list to a record if necessary
+list_to_record(_,Tuple) when tuple(Tuple) ->
+ Tuple;
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]).
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+% [{debug,choiceext},{bits,1,0}];
+ [0];
+setchoiceext(false) ->
+% [{debug,choiceext},{bits,1,1}].
+ [1].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(false) ->
+% [{debug,ext},{bits,1,0}];
+ [0];
+setext(true) ->
+% [{debug,ext},{bits,1,1}];
+ [1].
+
+fixoptionals(OptList,_OptLength,Val) when tuple(Val) ->
+% Bits = fixoptionals(OptList,Val,0),
+% {Val,{bits,OptLength,Bits}};
+% {Val,[10,OptLength,Bits]};
+ {Val,fixoptionals(OptList,Val,[])};
+
+fixoptionals([],_,Acc) ->
+ %% Optbits
+ lists:reverse(Acc);
+fixoptionals([Pos|Ot],Val,Acc) ->
+ case element(Pos,Val) of
+% asn1_NOVALUE -> fixoptionals(Ot,Val,Acc bsl 1);
+% asn1_DEFAULT -> fixoptionals(Ot,Val,Acc bsl 1);
+% _ -> fixoptionals(Ot,Val,(Acc bsl 1) + 1)
+ asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
+ _ -> fixoptionals(Ot,Val,[1|Acc])
+ end.
+
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when binary(Bytes) ->
+ getbit({0,Bytes});
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+% [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+% [encode_small_length(ExtNum),[10,ExtNum,ExtBits]]
+ [encode_small_length(ExtNum),pre_complete_bits(ExtNum,ExtBits)]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_constrained_number(Bytes,{0,NumChoices-1}).
+
+%% old version kept for backward compatibility with generates from R7B01
+getoptionals(Bytes,NumOpt) ->
+ {Blist,Bytes1} = getbits_as_list(NumOpt,Bytes),
+ {list_to_tuple(Blist),Bytes1}.
+
+%% new version used in generates from r8b_patch/3 and later
+getoptionals2(Bytes,NumOpt) ->
+ {_,_} = getbits(Bytes,NumOpt).
+
+
+%% getbits_as_binary(Num,Bytes) -> {{Unused,BinBits},RestBytes},
+%% Num = integer(),
+%% Bytes = list() | tuple(),
+%% Unused = integer(),
+%% BinBits = binary(),
+%% RestBytes = tuple()
+getbits_as_binary(Num,Bytes) when binary(Bytes) ->
+ getbits_as_binary(Num,{0,Bytes});
+getbits_as_binary(0,Buffer) ->
+ {{0,<<>>},Buffer};
+getbits_as_binary(Num,{0,Bin}) when Num > 16 ->
+ Used = Num rem 8,
+ Pad = (8 - Used) rem 8,
+%% Nbytes = Num div 8,
+ <<Bits:Num,_:Pad,RestBin/binary>> = Bin,
+ {{Pad,<<Bits:Num,0:Pad>>},RestBin};
+getbits_as_binary(Num,Buffer={_Used,_Bin}) -> % Unaligned buffer
+ %% Num =< 16,
+ {Bits2,Buffer2} = getbits(Buffer,Num),
+ Pad = (8 - (Num rem 8)) rem 8,
+ {{Pad,<<Bits2:Num,0:Pad>>},Buffer2}.
+
+
+% integer_from_list(Int,[],BigInt) ->
+% BigInt;
+% integer_from_list(Int,[H|T],BigInt) when Int < 8 ->
+% (BigInt bsl Int) bor (H bsr (8-Int));
+% integer_from_list(Int,[H|T],BigInt) ->
+% integer_from_list(Int-8,T,(BigInt bsl 8) bor H).
+
+getbits_as_list(Num,Bytes) when binary(Bytes) ->
+ getbits_as_list(Num,{0,Bytes},[]);
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+%% If buffer is empty and nothing more will be picked.
+getbits_as_list(0, B, Acc) ->
+ {lists:reverse(Acc),B};
+%% If first byte in buffer is full and at least one byte will be picked,
+%% then pick one byte.
+getbits_as_list(N,{0,Bin},Acc) when N >= 8 ->
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1,Rest/binary>> = Bin,
+ getbits_as_list(N-8,{0,Rest},[B0,B1,B2,B3,B4,B5,B6,B7|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 4, Used =< 4 ->
+ NewUsed = Used + 4,
+ Rem = 8 - NewUsed,
+ <<_:Used,B3:1,B2:1,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-4,{NewUsed rem 8,NewRest},[B0,B1,B2,B3|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when N >= 2, Used =< 6 ->
+ NewUsed = Used + 2,
+ Rem = 8 - NewUsed,
+ <<_:Used,B1:1,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-2,{NewUsed rem 8,NewRest},[B0,B1|Acc]);
+getbits_as_list(N,{Used,Bin},Acc) when Used =< 7 ->
+ NewUsed = Used + 1,
+ Rem = 8 - NewUsed,
+ <<_:Used,B0:1,_:Rem, Rest/binary>> = Bin,
+ NewRest = case Rem of 0 -> Rest; _ -> Bin end,
+ getbits_as_list(N-1,{NewUsed rem 8,NewRest},[B0|Acc]).
+
+
+getbit({7,<<_:7,B:1,Rest/binary>>}) ->
+ {B,{0,Rest}};
+getbit({0,Buffer = <<B:1,_:7,_/binary>>}) ->
+ {B,{1,Buffer}};
+getbit({Used,Buffer}) ->
+ Unused = (8 - Used) - 1,
+ <<_:Used,B:1,_:Unused,_/binary>> = Buffer,
+ {B,{Used+1,Buffer}};
+getbit(Buffer) when binary(Buffer) ->
+ getbit({0,Buffer}).
+
+
+getbits({0,Buffer},Num) when (Num rem 8) == 0 ->
+ <<Bits:Num,Rest/binary>> = Buffer,
+ {Bits,{0,Rest}};
+getbits({Used,Bin},Num) ->
+ NumPlusUsed = Num + Used,
+ NewUsed = NumPlusUsed rem 8,
+ Unused = (8-NewUsed) rem 8,
+ case Unused of
+ 0 ->
+ <<_:Used,Bits:Num,Rest/binary>> = Bin,
+ {Bits,{0,Rest}};
+ _ ->
+ Bytes = NumPlusUsed div 8,
+ <<_:Used,Bits:Num,_:Unused,_/binary>> = Bin,
+ <<_:Bytes/binary,Rest/binary>> = Bin,
+ {Bits,{NewUsed,Rest}}
+ end;
+getbits(Bin,Num) when binary(Bin) ->
+ getbits({0,Bin},Num).
+
+
+
+% getoctet(Bytes) when list(Bytes) ->
+% getoctet({0,Bytes});
+% getoctet(Bytes) ->
+% %% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+% getoctet1(Bytes).
+
+% getoctet1({0,[H|T]}) ->
+% {H,{0,T}};
+% getoctet1({Pos,[_,H|T]}) ->
+% {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,<<_H,T/binary>>}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as an integer with bit significance as in buffer.
+getoctets({0,Buffer},Num) ->
+ <<Val:Num/integer-unit:8,RestBin/binary>> = Buffer,
+ {Val,{0,RestBin}};
+getoctets({U,<<_Padding,Rest/binary>>},Num) when U /= 0 ->
+ getoctets({0,Rest},Num);
+getoctets(Buffer,Num) when binary(Buffer) ->
+ getoctets({0,Buffer},Num).
+% getoctets(Buffer,Num) ->
+% %% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+% getoctets(Buffer,Num,0).
+
+% getoctets(Buffer,0,Acc) ->
+% {Acc,Buffer};
+% getoctets(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+% getoctets_as_list(Buffer,Num) ->
+% getoctets_as_list(Buffer,Num,[]).
+
+% getoctets_as_list(Buffer,0,Acc) ->
+% {lists:reverse(Acc),Buffer};
+% getoctets_as_list(Buffer,Num,Acc) ->
+% {Oct,NewBuffer} = getoctet(Buffer),
+% getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%% First align buffer, then pick the first Num octets.
+%% Returns octets as a binary
+getoctets_as_bin({0,Bin},Num)->
+ <<Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin({_U,Bin},Num) ->
+ <<_Padding,Octets:Num/binary,RestBin/binary>> = Bin,
+ {Octets,{0,RestBin}};
+getoctets_as_bin(Bin,Num) when binary(Bin) ->
+ getoctets_as_bin({0,Bin},Num).
+
+%% same as above but returns octets as a List
+getoctets_as_list(Buffer,Num) ->
+ {Bin,Buffer2} = getoctets_as_bin(Buffer,Num),
+ {binary_to_list(Bin),Buffer2}.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+% [{bits,1,0}, % the value is in the root set
+% encode_constrained_number({0,Len1-1},N)];
+ [0, % the value is in the root set
+ encode_constrained_number({0,Len1-1},N)];
+ N when integer(N) ->
+% [{bits,1,0}]; % no encoding if only 0 or 1 alternative
+ [0]; % no encoding if only 0 or 1 alternative
+ false ->
+% [{bits,1,1}, % extension value
+ [1, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_constrained_number({0,Len-1},N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_Alt,[],_Tag) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_fragmented_XXX; decode of values encoded fragmented according
+%% to ITU-T X.691 clause 10.9.3.8. The unit (XXX) is either bits, octets,
+%% characters or number of components (in a choice,sequence or similar).
+%% Buffer is a buffer {Used, Bin}.
+%% C is the constrained length.
+%% If the buffer is not aligned, this function does that.
+decode_fragmented_bits({0,Buffer},C) ->
+ decode_fragmented_bits(Buffer,C,[]);
+decode_fragmented_bits({_N,<<_B,Bs/binary>>},C) ->
+ decode_fragmented_bits(Bs,C,[]).
+
+decode_fragmented_bits(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin, Len * ?'16K'),
+ decode_fragmented_bits(Bin2,C,[Value,Acc]);
+decode_fragmented_bits(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ BinBits = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int),C == size(BinBits) ->
+ {BinBits,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ {BinBits,{0,Bin}}
+ end;
+decode_fragmented_bits(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ Result = {BinBits,{Used,_Rest}} =
+ case (Len rem 8) of
+ 0 ->
+ <<Value:Len/binary-unit:1,Bin2/binary>> = Bin,
+ {list_to_binary(lists:reverse([Value|Acc])),{0,Bin2}};
+ Rem ->
+ Bytes = Len div 8,
+ U = 8 - Rem,
+ <<Value:Bytes/binary-unit:8,Bits1:Rem,Bits2:U,Bin2/binary>> = Bin,
+ {list_to_binary(lists:reverse([Bits1 bsl U,Value|Acc])),
+ {Rem,<<Bits2,Bin2/binary>>}}
+ end,
+ case C of
+ Int when integer(Int),C == (size(BinBits) - ((8 - Used) rem 8)) ->
+ Result;
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinBits}}});
+ _ ->
+ Result
+ end.
+
+
+decode_fragmented_octets({0,Bin},C) ->
+ decode_fragmented_octets(Bin,C,[]);
+decode_fragmented_octets({_N,<<_B,Bs/binary>>},C) ->
+ decode_fragmented_octets(Bs,C,[]).
+
+decode_fragmented_octets(<<3:2,Len:6,Bin/binary>>,C,Acc) ->
+ {Value,Bin2} = split_binary(Bin,Len * ?'16K'),
+ decode_fragmented_octets(Bin2,C,[Value,Acc]);
+decode_fragmented_octets(<<0:1,0:7,Bin/binary>>,C,Acc) ->
+ Octets = list_to_binary(lists:reverse(Acc)),
+ case C of
+ Int when integer(Int), C == size(Octets) ->
+ {Octets,{0,Bin}};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,Octets}}});
+ _ ->
+ {Octets,{0,Bin}}
+ end;
+decode_fragmented_octets(<<0:1,Len:7,Bin/binary>>,C,Acc) ->
+ <<Value:Len/binary-unit:8,Bin2/binary>> = Bin,
+ BinOctets = list_to_binary(lists:reverse([Value|Acc])),
+ case C of
+ Int when integer(Int),size(BinOctets) == Int ->
+ {BinOctets,Bin2};
+ Int when integer(Int) ->
+ exit({error,{asn1,{illegal_value,C,BinOctets}}});
+ _ ->
+ {BinOctets,Bin2}
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_Constraint, Val) when list(Val) ->
+ Bin = list_to_binary(Val),
+ case size(Bin) of
+ Size when Size>255 ->
+ [encode_length(undefined,Size),[21,<<Size:16>>,Bin]];
+ Size ->
+ [encode_length(undefined,Size),[20,Size,Bin]]
+ end;
+% [encode_length(undefined,size(Bin)),{octets,Bin}]; % octets implies align
+encode_open_type(_Constraint, Val) when binary(Val) ->
+% [encode_length(undefined,size(Val)),{octets,Val}]. % octets implies align
+ case size(Val) of
+ Size when Size>255 ->
+ [encode_length(undefined,size(Val)),[21,<<Size:16>>,Val]]; % octets implies align
+ Size ->
+ [encode_length(undefined,Size),[20,Size,Val]]
+ end.
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _Constraint) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_bin(Bytes2,Len).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_NamedNumberList) when integer(V) ->
+ encode_integer(C,V);
+encode_integer(C,{Name,V},NamedNumberList) when atom(Name) ->
+ encode_integer(C,V,NamedNumberList).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer([{Rc,_Ec}],Val) when tuple(Rc) -> % XXX when is this invoked? First argument most often a list,...Ok this is the extension case...but it doesn't work.
+ case (catch encode_integer([Rc],Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+% [{bits,1,1},encode_unconstrained_number(Val)];
+ [1,encode_unconstrained_number(Val)];
+ Encoded ->
+% [{bits,1,0},Encoded]
+ [0,Encoded]
+ end;
+
+encode_integer([],Val) ->
+ encode_unconstrained_number(Val);
+%% The constraint is the effective constraint, and in this case is a number
+encode_integer([{'SingleValue',V}],V) ->
+ [];
+encode_integer([{'ValueRange',VR={Lb,Ub},Range,PreEnc}],Val) when Val >= Lb,
+ Ub >= Val ->
+ %% this case when NamedNumberList
+ encode_constrained_number(VR,Range,PreEnc,Val);
+encode_integer([{'ValueRange',{Lb,'MAX'}}],Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_integer([{'ValueRange',{'MIN',_}}],Val) ->
+ encode_unconstrained_number(Val);
+encode_integer([{'ValueRange',VR={_Lb,_Ub}}],Val) ->
+ encode_constrained_number(VR,Val);
+encode_integer(_,Val) ->
+ exit({error,{asn1,{illegal_value,Val}}}).
+
+
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,[{Rc,_Ec}]) when tuple(Rc) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,[Rc]);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_Lb,_Ub} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+%% X.691:10.6 Encoding of a normally small non-negative whole number
+%% Use this for encoding of CHOICE index if there is an extension marker in
+%% the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+% [{bits,1,0},{bits,6,Val}];
+% [{bits,7,Val}]; % same as above but more efficient
+ [10,7,Val]; % same as above but more efficient
+encode_small_number(Val) ->
+% [{bits,1,1},encode_semi_constrained_number(0,Val)].
+ [1,encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,0)
+ end.
+
+%% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Oct = eint_positive(Val2),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ %{octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ [20,Len+1,[Len|Oct]];
+ Len < 256 ->
+ [encode_length(undefined,Len),[20,Len,Oct]];
+ true ->
+ [encode_length(undefined,Len),[21,<<Len:16>>,Oct]]
+ end.
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) ->
+ Val2 = Val-Lb,
+% {bits,N,Val2};
+ [10,N,Val2];
+encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) when N < 256->
+ %% N is 8 or 16 (1 or 2 octets)
+ Val2 = Val-Lb,
+% {octets,<<Val2:N/unit:8>>};
+ [20,N,Val2];
+encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) -> % N>255
+ %% N is 8 or 16 (1 or 2 octets)
+ Val2 = Val-Lb,
+% {octets,<<Val2:N/unit:8>>};
+ [21,<<N:16>>,Val2];
+encode_constrained_number({Lb,_Ub},Range,_,Val) ->
+ Val2 = Val-Lb,
+ if
+ Range =< 16#1000000 -> % max 3 octets
+ Octs = eint_positive(Val2),
+% [encode_length({1,3},size(Octs)),{octets,Octs}];
+ L = length(Octs),
+ [encode_length({1,3},L),[20,L,Octs]];
+ Range =< 16#100000000 -> % max 4 octets
+ Octs = eint_positive(Val2),
+% [encode_length({1,4},size(Octs)),{octets,Octs}];
+ L = length(Octs),
+ [encode_length({1,4},L),[20,L,Octs]];
+ Range =< 16#10000000000 -> % max 5 octets
+ Octs = eint_positive(Val2),
+% [encode_length({1,5},size(Octs)),{octets,Octs}];
+ L = length(Octs),
+ [encode_length({1,5},L),[20,L,Octs]];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+% Size = {bits,1,Val2};
+ [Val2];
+ Range =< 4 ->
+% Size = {bits,2,Val2};
+ [10,2,Val2];
+ Range =< 8 ->
+ [10,3,Val2];
+ Range =< 16 ->
+ [10,4,Val2];
+ Range =< 32 ->
+ [10,5,Val2];
+ Range =< 64 ->
+ [10,6,Val2];
+ Range =< 128 ->
+ [10,7,Val2];
+ Range =< 255 ->
+ [10,8,Val2];
+ Range =< 256 ->
+% Size = {octets,[Val2]};
+ [20,1,Val2];
+ Range =< 65536 ->
+% Size = {octets,<<Val2:16>>};
+ [20,2,<<Val2:16>>];
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+% [{bits,2,length(Octs)-1},{octets,Octs}];
+ Len = length(Octs),
+ [10,2,Len-1,20,Len,Octs];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ Len = length(Octs),
+ [10,2,Len-1,20,Len,Octs];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ Len = length(Octs),
+ [10,3,Len-1,20,Len,Octs];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end;
+encode_constrained_number({_,_},Val) ->
+ exit({error,{asn1,{illegal_value,Val}}}).
+
+decode_constrained_number(Buffer,VR={Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+ decode_constrained_number(Buffer,VR,Range).
+
+decode_constrained_number(Buffer,{Lb,_Ub},_Range,{bits,N}) ->
+ {Val,Remain} = getbits(Buffer,N),
+ {Val+Lb,Remain};
+decode_constrained_number(Buffer,{Lb,_Ub},_Range,{octets,N}) ->
+ {Val,Remain} = getoctets(Buffer,N),
+ {Val+Lb,Remain}.
+
+decode_constrained_number(Buffer,{Lb,_Ub},Range) ->
+ % Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+%% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+ %{octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ [20,Len+1,[Len|Oct]];
+ Len < 256 ->
+% [encode_length(undefined,Len),20,Len,Oct];
+ [20,Len+2,<<2:2,Len:14>>,Oct];% equiv with encode_length(undefined,Len) but faster
+ true ->
+% [encode_length(undefined,Len),{octets,Oct}]
+ [encode_length(undefined,Len),[21,<<Len:16>>,Oct]]
+ end;
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ Len = length(Oct),
+ if
+ Len < 128 ->
+% {octets,[Len|Oct]}; % equiv with encode_length(undefined,Len) but faster
+ [20,Len+1,[Len|Oct]];% equiv with encode_length(undefined,Len) but faster
+ Len < 256 ->
+% [encode_length(undefined,Len),20,Len,Oct];
+ [20,Len+2,<<2:2,Len:14>>,Oct];% equiv with encode_length(undefined,Len) but faster
+ true ->
+ %[encode_length(undefined,Len),{octets,Oct}]
+ [encode_length(undefined,Len),[21,<<Len:16>>,Oct]]
+ end.
+
+
+%% used for positive Values which don't need a sign bit
+%% returns a list
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+% minimum_octets(Val) ->
+% minimum_octets(Val,[]).
+
+% minimum_octets(Val,Acc) when Val > 0 ->
+% minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+% minimum_octets(0,Acc) ->
+% Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+% {octets,[Len]};
+ [20,1,Len];
+ Len < 16384 ->
+ %{octets,<<2:2,Len:14>>};
+ [20,2,<<2:2,Len:14>>];
+ true -> % should be able to endode length >= 16384 i.e. fragmented length
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length(Vr={Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number(Vr,Len);
+encode_length({Lb,_Ub},Len) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ encode_length(undefined,Len);
+encode_length({Vr={Lb,Ub},[]},Len) when Ub =< 65535 ,Lb >= 0,Len=<Ub ->
+ %% constrained extensible
+% [{bits,1,0},encode_constrained_number(Vr,Len)];
+ [0,encode_constrained_number(Vr,Len)];
+encode_length({{Lb,_},[]},Len) ->
+ [1,encode_semi_constrained_number(Lb,Len)];
+encode_length(SingleValue,_Len) when integer(SingleValue) ->
+ [].
+
+%% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension
+%% additions in a sequence or set
+encode_small_length(Len) when Len =< 64 ->
+%% [{bits,1,0},{bits,6,Len-1}];
+% {bits,7,Len-1}; % the same as above but more efficient
+ [10,7,Len-1];
+encode_small_length(Len) ->
+% [{bits,1,1},encode_length(undefined,Len)].
+ [1,encode_length(undefined,Len)].
+
+% decode_small_length({Used,<<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>>}) ->
+% case Buffer of
+% <<_:Used,0:1,Num:6,_:((8-Used+1) rem 8),Rest/binary>> ->
+% {Num,
+% case getbit(Buffer) of
+% {0,Remain} ->
+% {Bits,Remain2} = getbits(Remain,6),
+% {Bits+1,Remain2};
+% {1,Remain} ->
+% decode_length(Remain,undefined)
+% end.
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ {0,Buffer2} = align(Buffer),
+ case Buffer2 of
+ <<0:1,Oct:7,Rest/binary>> ->
+ {Oct,{0,Rest}};
+ <<2:2,Val:14,Rest/binary>> ->
+ {Val,{0,Rest}};
+ <<3:2,_Val:14,_Rest/binary>> ->
+ %% this case should be fixed
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}})
+ end;
+%% {Bits,_} = getbits(Buffer2,2),
+% case Bits of
+% 2 ->
+% {Val,Bytes3} = getoctets(Buffer2,2),
+% {(Val band 16#3FFF),Bytes3};
+% 3 ->
+% exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+% _ ->
+% {Val,Bytes3} = getoctet(Buffer2),
+% {Val band 16#7F,Bytes3}
+% end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+decode_length(_Buffer,{Lb,_Ub}) when integer(Lb), Lb >= 0 -> % Ub > 65535
+ exit({error,{asn1,{decode_length,{nyi,above_64K}}}});
+decode_length(Buffer,{{Lb,Ub},[]}) ->
+ case getbit(Buffer) of
+ {0,Buffer2} ->
+ decode_length(Buffer2, {Lb,Ub})
+ end;
+
+
+%When does this case occur with {_,_Lb,Ub} ??
+% X.691:10.9.3.5
+decode_length({Used,Bin},{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535
+ Unused = (8-Used) rem 8,
+ case Bin of
+ <<_:Used,0:1,Val:7,R:Unused,Rest/binary>> ->
+ {Val,{Used,<<R,Rest/binary>>}};
+ <<_:Used,_:Unused,2:2,Val:14,Rest/binary>> ->
+ {Val, {0,Rest}};
+ <<_:Used,_:Unused,3:2,_:14,_Rest/binary>> ->
+ exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}})
+ end;
+% decode_length(Buffer,{_,_Lb,Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+% case getbit(Buffer) of
+% {0,Remain} ->
+% getbits(Remain,7);
+% {1,Remain} ->
+% {Val,Remain2} = getoctets(Buffer,2),
+% {Val band 2#0111111111111111, Remain2}
+% end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+ % X.691:11
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+
+%% when the value is a list of {Unused,BinBits}, where
+%% Unused = integer(),
+%% BinBits = binary().
+
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList) when integer(Unused),
+ binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList);
+
+%% when the value is a list of named bits
+
+encode_bit_string(C, LoNB=[FirstVal | _RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes(LoNB, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);% consider the constraint
+
+encode_bit_string(C, BL=[{bit,_} | _RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes(BL, NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+encode_bit_string(Int, BitListValue, _)
+ when list(BitListValue),integer(Int) ->
+ %% The type is constrained by a single value size constraint
+ [40,Int,length(BitListValue),BitListValue];
+% encode_bit_string(C, BitListValue,NamedBitList)
+% when list(BitListValue) ->
+% [encode_bit_str_length(C,BitListValue),
+% 2,45,BitListValue];
+encode_bit_string(no, BitListValue,[])
+ when list(BitListValue) ->
+ [encode_length(undefined,length(BitListValue)),
+ 2,BitListValue];
+encode_bit_string(C, BitListValue,[])
+ when list(BitListValue) ->
+ [encode_length(C,length(BitListValue)),
+ 2,BitListValue];
+encode_bit_string(no, BitListValue,_NamedBitList)
+ when list(BitListValue) ->
+ %% this case with an unconstrained BIT STRING can be made more efficient
+ %% if the complete driver can take a special code so the length field
+ %% is encoded there.
+ NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue))),
+ [encode_length(undefined,length(NewBitLVal)),
+ 2,NewBitLVal];
+encode_bit_string(C,BitListValue,_NamedBitList)
+ when list(BitListValue) ->% C = {_,'MAX'}
+% NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+% lists:reverse(BitListValue))),
+ NewBitLVal = bit_string_trailing_zeros(BitListValue,C),
+ [encode_length(C,length(NewBitLVal)),
+ 2,NewBitLVal];
+
+% encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+% BitListToBinary =
+% %% fun that transforms a list of 1 and 0 to a tuple:
+% %% {UnusedBitsInLastByte, Binary}
+% fun([H|T],Acc,N,Fun) ->
+% Fun(T,(Acc bsl 1)+H,N+1,Fun);
+% ([],Acc,N,_) -> % length fits in one byte
+% Unused = (8 - (N rem 8)) rem 8,
+% % case N/8 of
+% % _Len =< 255 ->
+% % [30,Unused,(Unused+N)/8,<<Acc:N,0:Unused>>];
+% % _Len ->
+% % Len = (Unused+N)/8,
+% % [31,Unused,<<Len:16>>,<<Acc:N,0:Unused>>]
+% % end
+% {Unused,<<Acc:N,0:Unused>>}
+% end,
+% UnusedAndBin =
+% case NamedBitList of
+% [] -> % dont remove trailing zeroes
+% BitListToBinary(BitListValue,0,0,BitListToBinary);
+% _ ->
+% BitListToBinary(lists:reverse(
+% lists:dropwhile(fun(0)->true;(1)->false end,
+% lists:reverse(BitListValue))),
+% 0,0,BitListToBinary)
+% end,
+% encode_bin_bit_string(C,UnusedAndBin,NamedBitList);
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) when integer(IntegerVal)->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a tuple
+encode_bit_string(C,{Name,Val}, NamedBitList) when atom(Name) ->
+ encode_bit_string(C,Val,NamedBitList).
+
+bit_string_trailing_zeros(BitList,C) when integer(C) ->
+ bit_string_trailing_zeros1(BitList,C,C);
+bit_string_trailing_zeros(BitList,{Lb,Ub}) when integer(Lb) ->
+ bit_string_trailing_zeros1(BitList,Lb,Ub);
+bit_string_trailing_zeros(BitList,{{Lb,Ub},_}) when integer(Lb) ->
+ bit_string_trailing_zeros1(BitList,Lb,Ub);
+bit_string_trailing_zeros(BitList,_) ->
+ BitList.
+
+bit_string_trailing_zeros1(BitList,Lb,Ub) ->
+ case length(BitList) of
+ Lb -> BitList;
+ B when B<Lb -> BitList++lists:duplicate(Lb-B,0);
+ D -> F = fun(L,LB,LB,_,_)->lists:reverse(L);
+ ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun);
+ (L,L1,_,UB,_)when L1 =< UB -> lists:reverse(L);
+ (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING,
+ BitList}}) end,
+ F(lists:reverse(BitList),D,Lb,Ub,F)
+ end.
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),i.e. number unused bits in least sign. byte of
+%% BinBits = binary().
+encode_bin_bit_string(C,{_,BinBits},_NamedBitList)
+ when integer(C),C=<16 ->
+ [45,C,size(BinBits),BinBits];
+encode_bin_bit_string(C,{_Unused,BinBits},_NamedBitList)
+ when integer(C) ->
+ [2,45,C,size(BinBits),BinBits];
+encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) ->
+% UnusedAndBin1 = {Unused1,Bin1} =
+ {Unused1,Bin1} =
+ %% removes all trailing bits if NamedBitList is not empty
+ remove_trailing_bin(NamedBitList,UnusedAndBin),
+ case C of
+% case get_constraint(C,'SizeConstraint') of
+
+% 0 ->
+% []; % borde avg�ras i compile-time
+% V when integer(V),V=<16 ->
+% {Unused2,Bin2} = pad_list(V,UnusedAndBin1),
+% <<BitVal:V,_:Unused2>> = Bin2,
+% % {bits,V,BitVal};
+% [10,V,BitVal];
+% V when integer(V) ->
+% %[align, pad_list(V, UnusedAndBin1)];
+% {Unused2,Bin2} = pad_list(V, UnusedAndBin1),
+% <<BitVal:V,_:Unused2>> = Bin2,
+% [2,octets_unused_to_complete(Unused2,size(Bin2),Bin2)];
+
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+% [encode_length({Lb,Ub},size(Bin1)*8 - Unused1),
+% align,UnusedAndBin1];
+ Size=size(Bin1),
+ [encode_length({Lb,Ub},Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)];
+ no ->
+ Size=size(Bin1),
+ [encode_length(undefined,Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)];
+ Sc ->
+ Size=size(Bin1),
+ [encode_length(Sc,Size*8 - Unused1),
+ 2,octets_unused_to_complete(Unused1,Size,Bin1)]
+ end.
+
+remove_trailing_bin([], {Unused,Bin}) ->
+ {Unused,Bin};
+remove_trailing_bin(NamedNumberList, {_Unused,Bin}) ->
+ Size = size(Bin)-1,
+ <<Bfront:Size/binary, LastByte:8>> = Bin,
+ %% clear the Unused bits to be sure
+% LastByte1 = LastByte band (((1 bsl Unused) -1) bxor 255),% why this???
+ Unused1 = trailingZeroesInNibble(LastByte band 15),
+ Unused2 =
+ case Unused1 of
+ 4 ->
+ 4 + trailingZeroesInNibble(LastByte bsr 4);
+ _ -> Unused1
+ end,
+ case Unused2 of
+ 8 ->
+ remove_trailing_bin(NamedNumberList,{0,Bfront});
+ _ ->
+ {Unused2,Bin}
+ end.
+
+
+trailingZeroesInNibble(0) ->
+ 4;
+trailingZeroesInNibble(1) ->
+ 0;
+trailingZeroesInNibble(2) ->
+ 1;
+trailingZeroesInNibble(3) ->
+ 0;
+trailingZeroesInNibble(4) ->
+ 2;
+trailingZeroesInNibble(5) ->
+ 0;
+trailingZeroesInNibble(6) ->
+ 1;
+trailingZeroesInNibble(7) ->
+ 0;
+trailingZeroesInNibble(8) ->
+ 3;
+trailingZeroesInNibble(9) ->
+ 0;
+trailingZeroesInNibble(10) ->
+ 1;
+trailingZeroesInNibble(11) ->
+ 0;
+trailingZeroesInNibble(12) -> %#1100
+ 2;
+trailingZeroesInNibble(13) ->
+ 0;
+trailingZeroesInNibble(14) ->
+ 1;
+trailingZeroesInNibble(15) ->
+ 0.
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a tuple {Unused,Bits}. Unused is the number of unused
+%% bits, least significant bits in the last byte of Bits. Bits is
+%% the BIT STRING represented as a binary.
+%%
+decode_compact_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {{8,0},Buffer};
+ V when integer(V),V=<16 -> %fixed length 16 bits or less
+ compact_bit_string(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 -> %fixed length > 16 bits
+ Bytes2 = align(Buffer),
+ compact_bit_string(Bytes2,V,NamedNumberList);
+ V when integer(V) -> % V > 65536 => fragmented value
+ {Bin,Buffer2} = decode_fragmented_bits(Buffer,V),
+ case Buffer2 of
+ {0,_} -> {{0,Bin},Buffer2};
+ {U,_} -> {{8-U,Bin},Buffer2}
+ end;
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ no ->
+ %% This case may demand decoding of fragmented length/value
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ Sc ->
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList);
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_or_named(Buffer,V,NamedNumberList);
+ V when integer(V),V=<65536 ->
+ Bytes2 = align(Buffer),
+ bit_list_or_named(Bytes2,V,NamedNumberList);
+ V when integer(V) ->
+ Bytes2 = align(Buffer),
+ {BinBits,_Bytes3} = decode_fragmented_bits(Bytes2,V),
+ bit_list_or_named(BinBits,V,NamedNumberList);
+ Sc -> % extension marker
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ bit_list_or_named(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%% if no named bits are declared we will return a
+%% {Unused,Bits}. Unused = integer(),
+%% Bits = binary().
+compact_bit_string(Buffer,Len,[]) ->
+ getbits_as_binary(Len,Buffer); % {{Unused,BinBits},NewBuffer}
+compact_bit_string(Buffer,Len,NamedNumberList) ->
+ bit_list_or_named(Buffer,Len,NamedNumberList).
+
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_or_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_or_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_or_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_or_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_or_named1(Pos+1,Bt,Names,Acc);
+bit_list_or_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_or_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_or_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_or_named1(_Pos,[],_Names,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(Int) when integer(Int), Int > 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)];
+int_to_bitlist(0) ->
+ [].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+% pad_list(N,In={Unused,Bin}) ->
+% pad_list(N, size(Bin)*8 - Unused, In).
+
+% pad_list(N,Size,In={Unused,Bin}) when N < Size ->
+% exit({error,{asn1,{range_error,{bit_string,In}}}});
+% pad_list(N,Size,{Unused,Bin}) when N > Size, Unused > 0 ->
+% pad_list(N,Size+1,{Unused-1,Bin});
+% pad_list(N,Size,{Unused,Bin}) when N > Size ->
+% pad_list(N,Size+1,{7,<<Bin/binary,0>>});
+% pad_list(N,N,In={Unused,Bin}) ->
+% In.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(C,Bool,{_Name,Val}) ->
+ encode_octet_string(C,Bool,Val);
+encode_octet_string(_C,true,_Val) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(SZ={_,_},false,Val) ->
+% [encode_length(SZ,length(Val)),align,
+% {octets,Val}];
+ Len = length(Val),
+ [encode_length(SZ,Len),2,
+ octets_to_complete(Len,Val)];
+encode_octet_string(SZ,false,Val) when list(SZ) ->
+ Len = length(Val),
+ [encode_length({hd(SZ),lists:max(SZ)},Len),2,
+ octets_to_complete(Len,Val)];
+encode_octet_string(no,false,Val) ->
+ Len = length(Val),
+ [encode_length(undefined,Len),2,
+ octets_to_complete(Len,Val)];
+encode_octet_string(C,_,_) ->
+ exit({error,{not_implemented,C}}).
+
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,1,false) ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+decode_octet_string(Bytes,2,false) ->
+ {Bs,Bytes2}= getbits(Bytes,16),
+ {binary_to_list(<<Bs:16>>),Bytes2};
+decode_octet_string(Bytes,Sv,false) when integer(Sv),Sv=<65535 ->
+ Bytes2 = align(Bytes),
+ getoctets_as_list(Bytes2,Sv);
+decode_octet_string(Bytes,Sv,false) when integer(Sv) ->
+ Bytes2 = align(Bytes),
+ decode_fragmented_octets(Bytes2,Sv);
+decode_octet_string(Bytes,{Lb,Ub},false) ->
+ {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+decode_octet_string(Bytes,Sv,false) when list(Sv) ->
+ {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+decode_octet_string(Bytes,no,false) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val)
+
+
+encode_restricted_string(aligned,{Name,Val}) when atom(Name) ->
+ encode_restricted_string(aligned,Val);
+
+encode_restricted_string(aligned,Val) when list(Val)->
+ Len = length(Val),
+% [encode_length(undefined,length(Val)),{octets,Val}].
+ [encode_length(undefined,Len),octets_to_complete(Len,Val)].
+
+
+encode_known_multiplier_string(StringType,SizeC,NumBits,CharOutTab,{Name,Val}) when atom(Name) ->
+ encode_known_multiplier_string(StringType,SizeC,NumBits,CharOutTab,Val);
+encode_known_multiplier_string(StringType,SizeC,NumBits,CharOutTab,Val) ->
+ Result = chars_encode2(Val,NumBits,CharOutTab),
+ case SizeC of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ case {StringType,Result} of
+ {'BMPString',{octets,Ol}} -> %% this case cannot happen !!??
+ [{bits,8,Oct}||Oct <- Ol];
+ _ ->
+ Result
+ end;
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+%% [align,Result];
+ [2,Result];
+ {Ub,Lb} ->
+% [encode_length({Ub,Lb},length(Val)),align,Result];
+ [encode_length({Ub,Lb},length(Val)),2,Result];
+ no ->
+% [encode_length(undefined,length(Val)),align,Result]
+ [encode_length(undefined,length(Val)),2,Result]
+ end.
+
+decode_restricted_string(Bytes,aligned) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ getoctets_as_list(Bytes2,Len).
+
+decode_known_multiplier_string(StringType,SizeC,NumBits,CharInTab,Bytes) ->
+ case SizeC of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ chars_decode(Bytes,NumBits,StringType,CharInTab,Ub);
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ Bytes1 = align(Bytes),
+ chars_decode(Bytes1,NumBits,StringType,CharInTab,Ub);
+ Vl when list(Vl) ->
+ {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,CharInTab,Len);
+ no ->
+ {Len,Bytes1} = decode_length(Bytes,undefined),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,CharInTab,Len);
+ {Lb,Ub}->
+ {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,CharInTab,Len)
+ end.
+
+encode_GeneralString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GeneralString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_GraphicString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GraphicString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_ObjectDescriptor(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_ObjectDescriptor(Bytes) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_TeletexString(_C,Val) -> % equivalent with T61String
+ encode_restricted_string(aligned,Val).
+decode_TeletexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_VideotexString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_VideotexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
+%%
+getBMPChars(Bytes,1) ->
+ {O1,Bytes2} = getbits(Bytes,8),
+ {O2,Bytes3} = getbits(Bytes2,8),
+ if
+ O1 == 0 ->
+ {[O2],Bytes3};
+ true ->
+ {[{0,0,O1,O2}],Bytes3}
+ end;
+getBMPChars(Bytes,Len) ->
+ getBMPChars(Bytes,Len,[]).
+
+getBMPChars(Bytes,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getBMPChars(Bytes,Len,Acc) ->
+ {Octs,Bytes1} = getoctets_as_list(Bytes,2),
+ case Octs of
+ [0,O2] ->
+ getBMPChars(Bytes1,Len-1,[O2|Acc]);
+ [O1,O2]->
+ getBMPChars(Bytes1,Len-1,[{0,0,O1,O2}|Acc])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint PermittedAlphabet
+%% into account.
+%% This function does only encode the value part and NOT the length
+
+% chars_encode(C,StringType,Value) ->
+% case {StringType,get_constraint(C,'PermittedAlphabet')} of
+% {'UniversalString',{_,Sv}} ->
+% exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
+% {'BMPString',{_,Sv}} ->
+% exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
+% _ ->
+% {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
+% chars_encode2(Value,NumBits,CharOutTab)
+% end.
+
+
+chars_encode2([H|T],NumBits,T1={Min,Max,notab}) when H =< Max, H >= Min ->
+% [[10,NumBits,H-Min]|chars_encode2(T,NumBits,T1)];
+ [pre_complete_bits(NumBits,H-Min)|chars_encode2(T,NumBits,T1)];
+chars_encode2([H|T],NumBits,T1={Min,Max,Tab}) when H =< Max, H >= Min ->
+% [[10,NumBits,element(H-Min+1,Tab)]|chars_encode2(T,NumBits,T1)];
+ [pre_complete_bits(NumBits,exit_if_false(H,element(H-Min+1,Tab)))|
+ chars_encode2(T,NumBits,T1)];
+chars_encode2([{A,B,C,D}|T],NumBits,T1={Min,_Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+% [[10,NumBits,((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min]|chars_encode2(T,NumBits,T1)];
+ [pre_complete_bits(NumBits,
+ ((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min)|
+ chars_encode2(T,NumBits,T1)];
+chars_encode2([H={A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+ [pre_complete_bits(NumBits,exit_if_false(H,element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)))|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_NumBits,{_Min,_Max,_Tab}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+exit_if_false(V,false)->
+ exit({error,{asn1,{"illegal value according to Permitted alphabet constraint",V}}});
+exit_if_false(_,V) ->V.
+
+pre_complete_bits(NumBits,Val) when NumBits =< 8 ->
+ [10,NumBits,Val];
+pre_complete_bits(NumBits,Val) when NumBits =< 16 ->
+ [10,NumBits-8,Val bsr 8,10,8,(Val band 255)];
+pre_complete_bits(NumBits,Val) when NumBits =< 2040 -> % 255 * 8
+% LBUsed = NumBits rem 8,
+% {Unused,Len} = case (8 - LBUsed) of
+% 8 -> {0,NumBits div 8};
+% U -> {U,(NumBits div 8) + 1}
+% end,
+% NewVal = Val bsr LBUsed,
+% [30,Unused,Len,<<NewVal:Len/unit:8,Val:LBUsed,0:Unused>>].
+ Unused = (8 - (NumBits rem 8)) rem 8,
+ Len = NumBits + Unused,
+ [30,Unused,Len div 8,<<(Val bsl Unused):Len>>].
+
+% get_NumBits(C,StringType) ->
+% case get_constraint(C,'PermittedAlphabet') of
+% {'SingleValue',Sv} ->
+% charbits(length(Sv),aligned);
+% no ->
+% case StringType of
+% 'IA5String' ->
+% charbits(128,aligned); % 16#00..16#7F
+% 'VisibleString' ->
+% charbits(95,aligned); % 16#20..16#7E
+% 'PrintableString' ->
+% charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+% 'NumericString' ->
+% charbits(11,aligned); % $ ,"0123456789"
+% 'UniversalString' ->
+% 32;
+% 'BMPString' ->
+% 16
+% end
+% end.
+
+%%Maybe used later
+%%get_MaxChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% lists:nth(length(Sv),Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#7F; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#7E; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $9; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#ffffffff;
+%% 'BMPString' ->
+%% 16#ffff
+%% end
+%% end.
+
+%%Maybe used later
+%%get_MinChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% hd(Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#00; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#20; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $\s; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#00;
+%% 'BMPString' ->
+%% 16#00
+%% end
+%% end.
+
+% get_CharOutTab(C,StringType) ->
+% get_CharTab(C,StringType,out).
+
+% get_CharInTab(C,StringType) ->
+% get_CharTab(C,StringType,in).
+
+% get_CharTab(C,StringType,InOut) ->
+% case get_constraint(C,'PermittedAlphabet') of
+% {'SingleValue',Sv} ->
+% get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+% no ->
+% case StringType of
+% 'IA5String' ->
+% {0,16#7F,notab};
+% 'VisibleString' ->
+% get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+% 'PrintableString' ->
+% Chars = lists:sort(
+% " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+% get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+% 'NumericString' ->
+% get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+% 'UniversalString' ->
+% {0,16#FFFFFFFF,notab};
+% 'BMPString' ->
+% {0,16#FFFF,notab}
+% end
+% end.
+
+% get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+% BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+% if
+% Max =< BitValMax ->
+% {0,Max,notab};
+% true ->
+% case InOut of
+% out ->
+% {Min,Max,create_char_tab(Min,Chars)};
+% in ->
+% {Min,Max,list_to_tuple(Chars)}
+% end
+% end.
+
+% create_char_tab(Min,L) ->
+% list_to_tuple(create_char_tab(Min,L,0)).
+% create_char_tab(Min,[Min|T],V) ->
+% [V|create_char_tab(Min+1,T,V+1)];
+% create_char_tab(_Min,[],_V) ->
+% [];
+% create_char_tab(Min,L,V) ->
+% [false|create_char_tab(Min+1,L,V)].
+
+%% This very inefficient and should be moved to compiletime
+% charbits(NumOfChars,aligned) ->
+% case charbits(NumOfChars) of
+% 1 -> 1;
+% 2 -> 2;
+% B when B =< 4 -> 4;
+% B when B =< 8 -> 8;
+% B when B =< 16 -> 16;
+% B when B =< 32 -> 32
+% end.
+
+% charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+% charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+% charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+% charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+% charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+% charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+% charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+% charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+% charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+% charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+% charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+% charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+% charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+% charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+% charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+% charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+% charbits(NumOfChars) when integer(NumOfChars) ->
+% 16 + charbits1(NumOfChars bsr 16).
+
+% charbits1(0) ->
+% 0;
+% charbits1(NumOfChars) ->
+% 1 + charbits1(NumOfChars bsr 1).
+
+
+chars_decode(Bytes,_,'BMPString',_,Len) ->
+ getBMPChars(Bytes,Len);
+chars_decode(Bytes,NumBits,_StringType,CharInTab,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len).
+
+
+chars_decode2(Bytes,CharInTab,NumBits,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
+
+chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ Result =
+ if
+ Char < 256 -> Char;
+ true ->
+ list_to_tuple(binary_to_list(<<Char:32>>))
+ end,
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
+
+%% BMPString and UniversalString with PermittedAlphabet is currently not supported
+chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
+
+
+ % X.691:17
+encode_null(_Val) -> []; % encodes to nothing
+encode_null({Name,Val}) when atom(Name) ->
+ encode_null(Val).
+
+decode_null(Bytes) ->
+ {'NULL',Bytes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier({Name,Val}) when atom(Name) ->
+ encode_object_identifier(Val);
+encode_object_identifier(Val) ->
+ OctetList = e_object_identifier(Val),
+ Octets = list_to_binary(OctetList), % performs a flatten at the same time
+% [{debug,object_identifier},encode_length(undefined,size(Octets)),{octets,Octets}].
+ [encode_length(undefined,size(Octets)),
+ octets_to_complete(size(Octets),Octets)].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V}) ->
+ e_object_identifier(V);
+e_object_identifier({Cname,V}) when atom(Cname),tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+e_object_identifier({Cname,V}) when atom(Cname),list(V) ->
+ e_object_identifier(V);
+e_object_identifier(V) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V));
+
+%% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
+e_object_identifier([E1,E2|Tail]) when E1 >= 0, E1 < 2, E2 < 40 ; E1==2 ->
+ Head = 40*E1 + E2, % weird
+ e_object_elements([Head|Tail],[]);
+e_object_identifier(Oid=[_,_|_Tail]) ->
+ exit({error,{asn1,{'illegal_value',Oid}}}).
+
+e_object_elements([],Acc) ->
+ lists:reverse(Acc);
+e_object_elements([H|T],Acc) ->
+ e_object_elements(T,[e_object_element(H)|Acc]).
+
+e_object_element(Num) when Num < 128 ->
+ Num;
+%% must be changed to handle more than 2 octets
+e_object_element(Num) -> %% when Num < ???
+ Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000,
+ Right = Num band 2#1111111 ,
+ [Left,Right].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes}
+%% ObjId -> {integer(),integer(),...} % at least 2 integers
+%% RemainingBytes -> [integer()] when integer() (0..255)
+decode_object_identifier(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ [First|Rest] = dec_subidentifiers(Octs,0,[]),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ {list_to_tuple(Idlist),Bytes3}.
+
+dec_subidentifiers([H|T],Av,Al) when H >=16#80 ->
+ dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al);
+dec_subidentifiers([H|T],Av,Al) ->
+ dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]);
+dec_subidentifiers([],_Av,Al) ->
+ lists:reverse(Al).
+
+get_constraint([{Key,V}],Key) ->
+ V;
+get_constraint([],_) ->
+ no;
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+
+-ifdef(nodriver).
+
+complete(L) ->
+ case complete1(L) of
+ {[],[]} ->
+ <<0>>;
+ {Acc,[]} ->
+ Acc;
+ {Acc,Bacc} ->
+ [Acc|complete_bytes(Bacc)]
+ end.
+
+
+% this function builds the ugly form of lists [E1|E2] to avoid having to reverse it at the end.
+% this is done because it is efficient and that the result always will be sent on a port or
+% converted by means of list_to_binary/1
+ complete1(InList) when list(InList) ->
+ complete1(InList,[],[]);
+ complete1(InList) ->
+ complete1([InList],[],[]).
+
+ complete1([],Acc,Bacc) ->
+ {Acc,Bacc};
+ complete1([H|T],Acc,Bacc) when list(H) ->
+ {NewH,NewBacc} = complete1(H,Acc,Bacc),
+ complete1(T,NewH,NewBacc);
+
+ complete1([{octets,Bin}|T],Acc,[]) ->
+ complete1(T,[Acc|Bin],[]);
+
+ complete1([{octets,Bin}|T],Acc,Bacc) ->
+ complete1(T,[Acc|[complete_bytes(Bacc),Bin]],[]);
+
+ complete1([{debug,_}|T], Acc,Bacc) ->
+ complete1(T,Acc,Bacc);
+
+ complete1([{bits,N,Val}|T],Acc,Bacc) ->
+ complete1(T,Acc,complete_update_byte(Bacc,Val,N));
+
+ complete1([{bit,Val}|T],Acc,Bacc) ->
+ complete1(T,Acc,complete_update_byte(Bacc,Val,1));
+
+ complete1([align|T],Acc,[]) ->
+ complete1(T,Acc,[]);
+ complete1([align|T],Acc,Bacc) ->
+ complete1(T,[Acc|complete_bytes(Bacc)],[]);
+ complete1([{0,Bin}|T],Acc,[]) when binary(Bin) ->
+ complete1(T,[Acc|Bin],[]);
+ complete1([{Unused,Bin}|T],Acc,[]) when integer(Unused),binary(Bin) ->
+ Size = size(Bin)-1,
+ <<Bs:Size/binary,B>> = Bin,
+ NumBits = 8-Unused,
+ complete1(T,[Acc|Bs],[[B bsr Unused]|NumBits]);
+ complete1([{Unused,Bin}|T],Acc,Bacc) when integer(Unused),binary(Bin) ->
+ Size = size(Bin)-1,
+ <<Bs:Size/binary,B>> = Bin,
+ NumBits = 8 - Unused,
+ Bf = complete_bytes(Bacc),
+ complete1(T,[Acc|[Bf,Bs]],[[B bsr Unused]|NumBits]).
+
+
+ complete_update_byte([],Val,Len) ->
+ complete_update_byte([[0]|0],Val,Len);
+ complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) when NumBits + Len == 8 ->
+ [[0,((Byte bsl Len) + Val) band 255|Bacc]|0];
+ complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) when NumBits + Len > 8 ->
+ Rem = 8 - NumBits,
+ Rest = Len - Rem,
+ complete_update_byte([[0,((Byte bsl Rem) + (Val bsr Rest)) band 255 |Bacc]|0],Val,Rest);
+ complete_update_byte([[Byte|Bacc]|NumBits],Val,Len) ->
+ [[((Byte bsl Len) + Val) band 255|Bacc]|NumBits+Len].
+
+
+ complete_bytes([[Byte|Bacc]|0]) ->
+ lists:reverse(Bacc);
+ complete_bytes([[Byte|Bacc]|NumBytes]) ->
+ lists:reverse([(Byte bsl (8-NumBytes)) band 255|Bacc]);
+ complete_bytes([]) ->
+ [].
+
+-else.
+
+
+ complete(L) ->
+ case catch port_control(drv_complete,1,L) of
+ Bin when binary(Bin) ->
+ Bin;
+ List when list(List) -> handle_error(List,L);
+ {'EXIT',{badarg,Reason}} ->
+ asn1rt_driver_handler:load_driver(),
+ receive
+ driver_ready ->
+ case catch port_control(drv_complete,1,L) of
+ Bin2 when binary(Bin2) -> Bin2;
+ List when list(List) -> handle_error(List,L);
+ Error -> exit(Error)
+ end;
+ {error,Error} -> % error when loading driver
+ %% the driver could not be loaded
+ exit(Error);
+ Error={port_error,Reason} ->
+ exit(Error)
+ end;
+ {'EXIT',Reason} ->
+ exit(Reason)
+ end.
+
+handle_error([],_)->
+ exit({error,{"memory allocation problem"}});
+handle_error("1",L) -> % error in complete in driver
+ exit({error,{asn1_error,L}});
+handle_error(ErrL,L) ->
+ exit({error,{unknown_error,ErrL,L}}).
+
+-endif.
+
+
+octets_to_complete(Len,Val) when Len < 256 ->
+ [20,Len,Val];
+octets_to_complete(Len,Val) ->
+ [21,<<Len:16>>,Val].
+
+octets_unused_to_complete(Unused,Len,Val) when Len < 256 ->
+ [30,Unused,Len,Val];
+octets_unused_to_complete(Unused,Len,Val) ->
+ [31,Unused,<<Len:16>>,Val].
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_v1.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_v1.erl
new file mode 100644
index 0000000000..ebab269f5d
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/asn1rt_per_v1.erl
@@ -0,0 +1,1843 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: asn1rt_per_v1.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+%%
+-module(asn1rt_per_v1).
+
+%% encoding / decoding of PER aligned
+
+-include("asn1_records.hrl").
+
+-export([dec_fixup/3, cindex/3, list_to_record/2]).
+-export([setchoiceext/1, setext/1, fixoptionals/2, fixextensions/2,
+ setoptionals/1, fixoptionals2/3, getext/1, getextension/2,
+ skipextensions/3, getbit/1, getchoice/3 ]).
+-export([getoptionals/2, getoptionals/3, set_choice/3,
+ getoptionals2/2,
+ encode_integer/2, encode_integer/3 ]).
+-export([decode_integer/2, decode_integer/3, encode_small_number/1,
+ encode_boolean/1, decode_boolean/1, encode_length/2,
+ decode_length/1, decode_length/2,
+ encode_small_length/1, decode_small_length/1,
+ decode_compact_bit_string/3]).
+-export([encode_enumerated/3, decode_enumerated/3,
+ encode_bit_string/3, decode_bit_string/3 ]).
+-export([encode_octet_string/2, decode_octet_string/2,
+ encode_null/1, decode_null/1,
+ encode_object_identifier/1, decode_object_identifier/1,
+ complete/1]).
+
+-export([encode_open_type/2, decode_open_type/2]).
+
+-export([encode_UniversalString/2, decode_UniversalString/2,
+ encode_PrintableString/2, decode_PrintableString/2,
+ encode_GeneralString/2, decode_GeneralString/2,
+ encode_GraphicString/2, decode_GraphicString/2,
+ encode_TeletexString/2, decode_TeletexString/2,
+ encode_VideotexString/2, decode_VideotexString/2,
+ encode_VisibleString/2, decode_VisibleString/2,
+ encode_BMPString/2, decode_BMPString/2,
+ encode_IA5String/2, decode_IA5String/2,
+ encode_NumericString/2, decode_NumericString/2,
+ encode_ObjectDescriptor/2, decode_ObjectDescriptor/1
+ ]).
+
+
+dec_fixup(Terms,Cnames,RemBytes) ->
+ dec_fixup(Terms,Cnames,RemBytes,[]).
+
+dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,Acc);
+dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) ->
+ dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]);
+dec_fixup([],_Cnames,RemBytes,Acc) ->
+ {lists:reverse(Acc),RemBytes}.
+
+cindex(Ix,Val,Cname) ->
+ case element(Ix,Val) of
+ {Cname,Val2} -> Val2;
+ X -> X
+ end.
+
+% converts a list to a record if necessary
+list_to_record(Name,List) when list(List) ->
+ list_to_tuple([Name|List]);
+list_to_record(_Name,Tuple) when tuple(Tuple) ->
+ Tuple.
+
+%%--------------------------------------------------------
+%% setchoiceext(InRootSet) -> [{bit,X}]
+%% X is set to 1 when InRootSet==false
+%% X is set to 0 when InRootSet==true
+%%
+setchoiceext(true) ->
+ [{debug,choiceext},{bit,0}];
+setchoiceext(false) ->
+ [{debug,choiceext},{bit,1}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% setext(true|false) -> CompleteList
+%%
+
+setext(true) ->
+ [{debug,ext},{bit,1}];
+setext(false) ->
+ [{debug,ext},{bit,0}].
+
+%%
+
+fixoptionals2(OptList,OptLength,Val) when tuple(Val) ->
+ Bits = fixoptionals2(OptList,Val,0),
+ {Val,{bits,OptLength,Bits}};
+
+fixoptionals2([],_Val,Acc) ->
+ %% Optbits
+ Acc;
+fixoptionals2([Pos|Ot],Val,Acc) ->
+ case element(Pos,Val) of
+ asn1_NOVALUE -> fixoptionals2(Ot,Val,Acc bsl 1);
+ asn1_DEFAULT -> fixoptionals2(Ot,Val,Acc bsl 1);
+ _ -> fixoptionals2(Ot,Val,(Acc bsl 1) + 1)
+ end.
+
+
+%%
+%% fixoptionals remains only for backward compatibility purpose
+fixoptionals(OptList,Val) when tuple(Val) ->
+ fixoptionals(OptList,Val,[]);
+
+fixoptionals(OptList,Val) when list(Val) ->
+ fixoptionals(OptList,Val,1,[],[]).
+
+fixoptionals([],Val,Acc) ->
+ % return {Val,Opt}
+ {Val,lists:reverse(Acc)};
+fixoptionals([{_,Pos}|Ot],Val,Acc) ->
+ case element(Pos+1,Val) of
+ asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]);
+ asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]);
+ _ -> fixoptionals(Ot,Val,[1|Acc])
+ end.
+
+
+%setoptionals(OptList,Val) ->
+% Vlist = tuple_to_list(Val),
+% setoptionals(OptList,Vlist,1,[]).
+
+fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) ->
+ fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]);
+fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) ->
+ fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]);
+fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) ->
+ fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]);
+fixoptionals([],[],_,Acc1,Acc2) ->
+ % return {Val,Opt}
+ {list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]),lists:reverse(Acc1)}.
+
+setoptionals([H|T]) ->
+ [{bit,H}|setoptionals(T)];
+setoptionals([]) ->
+ [{debug,optionals}].
+
+getext(Bytes) when tuple(Bytes) ->
+ getbit(Bytes);
+getext(Bytes) when list(Bytes) ->
+ getbit({0,Bytes}).
+
+getextension(0, Bytes) ->
+ {{},Bytes};
+getextension(1, Bytes) ->
+ {Len,Bytes2} = decode_small_length(Bytes),
+ {Blist, Bytes3} = getbits_as_list(Len,Bytes2),
+ {list_to_tuple(Blist),Bytes3}.
+
+fixextensions({ext,ExtPos,ExtNum},Val) ->
+ case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of
+ 0 -> [];
+ ExtBits ->
+ [encode_small_length(ExtNum),{bits,ExtNum,ExtBits}]
+ end.
+
+fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos ->
+ Acc;
+fixextensions(Pos,ExtPos,Val,Acc) ->
+ Bit = case catch(element(Pos+1,Val)) of
+ asn1_NOVALUE ->
+ 0;
+ asn1_NOEXTVALUE ->
+ 0;
+ {'EXIT',_} ->
+ 0;
+ _ ->
+ 1
+ end,
+ fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit).
+
+skipextensions(Bytes,Nr,ExtensionBitPattern) ->
+ case (catch element(Nr,ExtensionBitPattern)) of
+ 1 ->
+ {_,Bytes2} = decode_open_type(Bytes,[]),
+ skipextensions(Bytes2, Nr+1, ExtensionBitPattern);
+ 0 ->
+ skipextensions(Bytes, Nr+1, ExtensionBitPattern);
+ {'EXIT',_} -> % badarg, no more extensions
+ Bytes
+ end.
+
+
+getchoice(Bytes,1,0) -> % only 1 alternative is not encoded
+ {0,Bytes};
+getchoice(Bytes,_NumChoices,1) ->
+ decode_small_number(Bytes);
+getchoice(Bytes,NumChoices,0) ->
+ decode_integer(Bytes,[{'ValueRange',{0,NumChoices-1}}]).
+
+getoptionals2(Bytes,NumOpt) ->
+ getbits(Bytes,NumOpt).
+
+%% getoptionals is kept only for bakwards compatibility
+getoptionals(Bytes,NumOpt) ->
+ {Blist,Bytes1} = getbits_as_list(NumOpt,Bytes),
+ {list_to_tuple(Blist),Bytes1}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getoptionals/3 is only here for compatibility from 1.3.2
+%% the codegenerator uses getoptionals/2
+
+getoptionals(Bytes,L,NumComp) when list(L) ->
+ {Blist,Bytes1} = getbits_as_list(length(L),Bytes),
+ {list_to_tuple(comptuple(Blist,L,NumComp,1)),Bytes1}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% comptuple is only here for compatibility not used from 1.3.2
+comptuple([Bh|Bt],[{_Name,Nr}|T],NumComp,Nr) ->
+ [Bh|comptuple(Bt,T,NumComp-1,Nr+1)];
+comptuple(Bl,[{Name,Tnr}|Tl],NumComp,Nr) ->
+ [0|comptuple(Bl,[{Name,Tnr}|Tl],NumComp-1,Nr+1)];
+comptuple(_B,_L,0,_Nr) ->
+ [];
+comptuple(B,O,N,Nr) ->
+ [0|comptuple(B,O,N-1,Nr+1)].
+
+%% getbits_as_binary(Num,Bytes) -> {{Unused,BinBits},RestBytes},
+%% Num = integer(),
+%% Bytes = list() | tuple(),
+%% Unused = integer(),
+%% BinBits = binary(),
+%% RestBytes = tuple()
+getbits_as_binary(Num,Bytes) when list(Bytes) ->
+ getbits_as_binary(Num,{0,Bytes});
+getbits_as_binary(_Num,{Used,[]}) ->
+ {{0,<<>>},{Used,[]}};
+getbits_as_binary(Num,{Used,Bits=[H|T]}) ->
+ B1 = case (Num+Used) =< 8 of
+ true -> Num;
+ _ -> 8-Used
+ end,
+ B2 = Num - B1,
+ Pad = (8 - ((B1+B2) rem 8)) rem 8,% Pad /= 8
+ RestBits = lists:nthtail((B1+B2) div 8,Bits),
+ Int = integer_from_list(B2,T,0),
+ NewUsed = (Used + Num) rem 8,
+ {{Pad,<<(H bsr (8-(Used+B1))):B1,Int:B2,0:Pad>>},{NewUsed,RestBits}}.
+
+integer_from_list(_Int,[],BigInt) ->
+ BigInt;
+integer_from_list(Int,[H|_T],BigInt) when Int < 8 ->
+ (BigInt bsl Int) bor (H bsr (8-Int));
+integer_from_list(Int,[H|T],BigInt) ->
+ integer_from_list(Int-8,T,(BigInt bsl 8) bor H).
+
+getbits_as_list(Num,Bytes) ->
+ getbits_as_list(Num,Bytes,[]).
+
+getbits_as_list(0,Bytes,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getbits_as_list(Num,Bytes,Acc) ->
+ {Bit,NewBytes} = getbit(Bytes),
+ getbits_as_list(Num-1,NewBytes,[Bit|Acc]).
+
+getbit(Bytes) ->
+% io:format("getbit:~p~n",[Bytes]),
+ getbit1(Bytes).
+
+getbit1({7,[H|T]}) ->
+ {H band 1,{0,T}};
+getbit1({Pos,[H|T]}) ->
+ {(H bsr (7-Pos)) band 1,{(Pos+1) rem 8,[H|T]}};
+getbit1(Bytes) when list(Bytes) ->
+ getbit1({0,Bytes}).
+
+%% This could be optimized
+getbits(Buffer,Num) ->
+% io:format("getbits:Buffer = ~p~nNum=~p~n",[Buffer,Num]),
+ getbits(Buffer,Num,0).
+
+getbits(Buffer,0,Acc) ->
+ {Acc,Buffer};
+getbits(Buffer,Num,Acc) ->
+ {B,NewBuffer} = getbit(Buffer),
+ getbits(NewBuffer,Num-1,B + (Acc bsl 1)).
+
+
+getoctet(Bytes) when list(Bytes) ->
+ getoctet({0,Bytes});
+getoctet(Bytes) ->
+% io:format("getoctet:Buffer = ~p~n",[Bytes]),
+ getoctet1(Bytes).
+
+getoctet1({0,[H|T]}) ->
+ {H,{0,T}};
+getoctet1({_Pos,[_,H|T]}) ->
+ {H,{0,T}}.
+
+align({0,L}) ->
+ {0,L};
+align({_Pos,[_H|T]}) ->
+ {0,T};
+align(Bytes) ->
+ {0,Bytes}.
+
+getoctets(Buffer,Num) ->
+% io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]),
+ getoctets(Buffer,Num,0).
+
+getoctets(Buffer,0,Acc) ->
+ {Acc,Buffer};
+getoctets(Buffer,Num,Acc) ->
+ {Oct,NewBuffer} = getoctet(Buffer),
+ getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct).
+
+getoctets_as_list(Buffer,Num) ->
+ getoctets_as_list(Buffer,Num,[]).
+
+getoctets_as_list(Buffer,0,Acc) ->
+ {lists:reverse(Acc),Buffer};
+getoctets_as_list(Buffer,Num,Acc) ->
+ {Oct,NewBuffer} = getoctet(Buffer),
+ getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% set_choice(Alt,Choices,Altnum) -> ListofBitSettings
+%% Alt = atom()
+%% Altnum = integer() | {integer(),integer()}% number of alternatives
+%% Choices = [atom()] | {[atom()],[atom()]}
+%% When Choices is a tuple the first list is the Rootset and the
+%% second is the Extensions and then Altnum must also be a tuple with the
+%% lengths of the 2 lists
+%%
+set_choice(Alt,{L1,L2},{Len1,_Len2}) ->
+ case set_choice_tag(Alt,L1) of
+ N when integer(N), Len1 > 1 ->
+ [{bit,0}, % the value is in the root set
+ encode_integer([{'ValueRange',{0,Len1-1}}],N)];
+ N when integer(N) ->
+ [{bit,0}]; % no encoding if only 0 or 1 alternative
+ false ->
+ [{bit,1}, % extension value
+ case set_choice_tag(Alt,L2) of
+ N2 when integer(N2) ->
+ encode_small_number(N2);
+ false ->
+ unknown_choice_alt
+ end]
+ end;
+set_choice(Alt,L,Len) ->
+ case set_choice_tag(Alt,L) of
+ N when integer(N), Len > 1 ->
+ encode_integer([{'ValueRange',{0,Len-1}}],N);
+ N when integer(N) ->
+ []; % no encoding if only 0 or 1 alternative
+ false ->
+ [unknown_choice_alt]
+ end.
+
+set_choice_tag(Alt,Choices) ->
+ set_choice_tag(Alt,Choices,0).
+
+set_choice_tag(Alt,[Alt|_Rest],Tag) ->
+ Tag;
+set_choice_tag(Alt,[_H|Rest],Tag) ->
+ set_choice_tag(Alt,Rest,Tag+1);
+set_choice_tag(_Alt,[],_Tag) ->
+ false.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_open_type(Constraint, Value) -> CompleteList
+%% Value = list of bytes of an already encoded value (the list must be flat)
+%% | binary
+%% Contraint = not used in this version
+%%
+encode_open_type(_Constraint, Val) when list(Val) ->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}];
+encode_open_type(_Constraint, Val) when binary(Val) ->
+ [encode_length(undefined,size(Val)),align,
+ {octets,binary_to_list(Val)}].
+%% the binary_to_list is not optimal but compatible with the current solution
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_open_type(Buffer,Constraint) -> Value
+%% Constraint is not used in this version
+%% Buffer = [byte] with PER encoded data
+%% Value = [byte] with decoded data (which must be decoded again as some type)
+%%
+decode_open_type(Bytes, _Constraint) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList
+%% encode_integer(Constraint,Value) -> CompleteList
+%% encode_integer(Constraint,{Name,Value}) -> CompleteList
+%%
+%%
+encode_integer(C,V,NamedNumberList) when atom(V) ->
+ case lists:keysearch(V,1,NamedNumberList) of
+ {value,{_,NewV}} ->
+ encode_integer(C,NewV);
+ _ ->
+ exit({error,{asn1,{namednumber,V}}})
+ end;
+encode_integer(C,V,_) when integer(V) ->
+ encode_integer(C,V);
+encode_integer(C,{Name,V},NamedNumberList) when atom(Name) ->
+ encode_integer(C,V,NamedNumberList).
+
+encode_integer(C,{Name,Val}) when atom(Name) ->
+ encode_integer(C,Val);
+
+encode_integer({Rc,_Ec},Val) ->
+ case (catch encode_integer(Rc,Val)) of
+ {'EXIT',{error,{asn1,_}}} ->
+ [{bit,1},encode_unconstrained_number(Val)];
+ Encoded ->
+ [{bit,0},Encoded]
+ end;
+encode_integer(C,Val ) when list(C) ->
+ case get_constraint(C,'SingleValue') of
+ no ->
+ encode_integer1(C,Val);
+ V when integer(V),V == Val ->
+ []; % a type restricted to a single value encodes to nothing
+ V when list(V) ->
+ case lists:member(Val,V) of
+ true ->
+ encode_integer1(C,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end.
+
+encode_integer1(C, Val) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ encode_unconstrained_number(Val);
+ {Lb,'MAX'} ->
+ encode_semi_constrained_number(Lb,Val);
+ %% positive with range
+ {Lb,Ub} when Val >= Lb,
+ Ub >= Val ->
+ encode_constrained_number(VR,Val);
+ _ ->
+ exit({error,{asn1,{illegal_value,VR,Val}}})
+ end.
+
+decode_integer(Buffer,Range,NamedNumberList) ->
+ {Val,Buffer2} = decode_integer(Buffer,Range),
+ case lists:keysearch(Val,2,NamedNumberList) of
+ {value,{NewVal,_}} -> {NewVal,Buffer2};
+ _ -> {Val,Buffer2}
+ end.
+
+decode_integer(Buffer,{Rc,_Ec}) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> decode_integer(Buffer2,Rc);
+ 1 -> decode_unconstrained_number(Buffer2)
+ end;
+decode_integer(Buffer,undefined) ->
+ decode_unconstrained_number(Buffer);
+decode_integer(Buffer,C) ->
+ case get_constraint(C,'SingleValue') of
+ V when integer(V) ->
+ {V,Buffer};
+ V when list(V) ->
+ {Val,Buffer2} = decode_integer1(Buffer,C),
+ case lists:member(Val,V) of
+ true ->
+ {Val,Buffer2};
+ _ ->
+ exit({error,{asn1,{illegal_value,Val}}})
+ end;
+ _ ->
+ decode_integer1(Buffer,C)
+ end.
+
+decode_integer1(Buffer,C) ->
+ case VR = get_constraint(C,'ValueRange') of
+ no ->
+ decode_unconstrained_number(Buffer);
+ {Lb, 'MAX'} ->
+ decode_semi_constrained_number(Buffer,Lb);
+ {_,_} ->
+ decode_constrained_number(Buffer,VR)
+ end.
+
+% X.691:10.6 Encoding of a normally small non-negative whole number
+% Use this for encoding of CHOICE index if there is an extension marker in
+% the CHOICE
+encode_small_number({Name,Val}) when atom(Name) ->
+ encode_small_number(Val);
+encode_small_number(Val) when Val =< 63 ->
+ [{bit,0},{bits,6,Val}];
+encode_small_number(Val) ->
+ [{bit,1},encode_semi_constrained_number(0,Val)].
+
+decode_small_number(Bytes) ->
+ {Bit,Bytes2} = getbit(Bytes),
+ case Bit of
+ 0 ->
+ getbits(Bytes2,6);
+ 1 ->
+ decode_semi_constrained_number(Bytes2,{0,'MAX'})
+ end.
+
+% X.691:10.7 Encoding of a semi-constrained whole number
+%% might be an optimization encode_semi_constrained_number(0,Val) ->
+encode_semi_constrained_number(C,{Name,Val}) when atom(Name) ->
+ encode_semi_constrained_number(C,Val);
+encode_semi_constrained_number({Lb,'MAX'},Val) ->
+ encode_semi_constrained_number(Lb,Val);
+encode_semi_constrained_number(Lb,Val) ->
+ Val2 = Val - Lb,
+ Octs = eint_positive(Val2),
+ [encode_length(undefined,length(Octs)),{octets,Octs}].
+
+decode_semi_constrained_number(Bytes,{Lb,_}) ->
+ decode_semi_constrained_number(Bytes,Lb);
+decode_semi_constrained_number(Bytes,Lb) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {V,Bytes3} = getoctets(Bytes2,Len),
+ {V+Lb,Bytes3}.
+
+encode_constrained_number(Range,{Name,Val}) when atom(Name) ->
+ encode_constrained_number(Range,Val);
+encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val ->
+ Range = Ub - Lb + 1,
+ Val2 = Val - Lb,
+ if
+ Range == 2 ->
+ {bits,1,Val2};
+ Range =< 4 ->
+ {bits,2,Val2};
+ Range =< 8 ->
+ {bits,3,Val2};
+ Range =< 16 ->
+ {bits,4,Val2};
+ Range =< 32 ->
+ {bits,5,Val2};
+ Range =< 64 ->
+ {bits,6,Val2};
+ Range =< 128 ->
+ {bits,7,Val2};
+ Range =< 255 ->
+ {bits,8,Val2};
+ Range =< 256 ->
+ {octets,1,Val2};
+ Range =< 65536 ->
+ {octets,2,Val2};
+ Range =< 16#1000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,3},length(Octs)),{octets,Octs}];
+ Range =< 16#100000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,4},length(Octs)),{octets,Octs}];
+ Range =< 16#10000000000 ->
+ Octs = eint_positive(Val2),
+ [encode_length({1,5},length(Octs)),{octets,Octs}];
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end.
+
+decode_constrained_number(Buffer,{Lb,Ub}) ->
+ Range = Ub - Lb + 1,
+% Val2 = Val - Lb,
+ {Val,Remain} =
+ if
+ Range == 2 ->
+ getbits(Buffer,1);
+ Range =< 4 ->
+ getbits(Buffer,2);
+ Range =< 8 ->
+ getbits(Buffer,3);
+ Range =< 16 ->
+ getbits(Buffer,4);
+ Range =< 32 ->
+ getbits(Buffer,5);
+ Range =< 64 ->
+ getbits(Buffer,6);
+ Range =< 128 ->
+ getbits(Buffer,7);
+ Range =< 255 ->
+ getbits(Buffer,8);
+ Range =< 256 ->
+ getoctets(Buffer,1);
+ Range =< 65536 ->
+ getoctets(Buffer,2);
+ Range =< 16#1000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,3}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#100000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,4}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ Range =< 16#10000000000 ->
+ {Len,Bytes2} = decode_length(Buffer,{1,5}),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_pos_integer(Octs),Bytes3};
+ true ->
+ exit({not_supported,{integer_range,Range}})
+ end,
+ {Val+Lb,Remain}.
+
+% X.691:10.8 Encoding of an unconstrained whole number
+
+encode_unconstrained_number(Val) when Val >= 0 ->
+ Oct = eint(Val,[]),
+ [{debug,unconstrained_number},
+ encode_length({0,'MAX'},length(Oct)),
+ {octets,Oct}];
+encode_unconstrained_number(Val) -> % negative
+ Oct = enint(Val,[]),
+ [{debug,unconstrained_number},
+ encode_length({0,'MAX'},length(Oct)),
+ {octets,Oct}].
+
+%% used for positive Values which don't need a sign bit
+eint_positive(Val) ->
+ case eint(Val,[]) of
+ [0,B1|T] ->
+ [B1|T];
+ T ->
+ T
+ end.
+
+eint(0, [B|Acc]) when B < 128 ->
+ [B|Acc];
+eint(N, Acc) ->
+ eint(N bsr 8, [N band 16#ff| Acc]).
+
+enint(-1, [B1|T]) when B1 > 127 ->
+ [B1|T];
+enint(N, Acc) ->
+ enint(N bsr 8, [N band 16#ff|Acc]).
+
+%% used for signed positive values
+
+%eint(Val, Ack) ->
+% X = Val band 255,
+% Next = Val bsr 8,
+% if
+% Next == 0, X >= 127 ->
+% [0,X|Ack];
+% Next == 0 ->
+% [X|Ack];
+% true ->
+% eint(Next,[X|Ack])
+% end.
+
+%%% used for signed negative values
+%enint(Val, Acc) ->
+% NumOctets = if
+% -Val < 16#80 -> 1;
+% -Val < 16#8000 ->2;
+% -Val < 16#800000 ->3;
+% -Val < 16#80000000 ->4;
+% -Val < 16#8000000000 ->5;
+% -Val < 16#800000000000 ->6;
+% -Val < 16#80000000000000 ->7;
+% -Val < 16#8000000000000000 ->8;
+% -Val < 16#800000000000000000 ->9
+% end,
+% enint(Val,Acc,NumOctets).
+
+%enint(Val, Acc,0) ->
+% Acc;
+%enint(Val, Acc,NumOctets) ->
+% enint(Val bsr 8,[Val band 255|Acc],NumOctets-1).
+
+
+decode_unconstrained_number(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Ints,Bytes3} = getoctets_as_list(Bytes2,Len),
+ {dec_integer(Ints),Bytes3}.
+
+dec_pos_integer(Ints) ->
+ decpint(Ints, 8 * (length(Ints) - 1)).
+dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number
+ decpint(Ints, 8 * (length(Ints) - 1));
+dec_integer(Ints) -> %% Negative
+ decnint(Ints, 8 * (length(Ints) - 1)).
+
+decpint([Byte|Tail], Shift) ->
+ (Byte bsl Shift) bor decpint(Tail, Shift-8);
+decpint([], _) -> 0.
+
+decnint([Byte|Tail], Shift) ->
+ (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8).
+
+minimum_octets(Val) ->
+ minimum_octets(Val,[]).
+
+minimum_octets(Val,Acc) when Val > 0 ->
+ minimum_octets((Val bsr 8),[Val band 16#FF|Acc]);
+minimum_octets(0,Acc) ->
+ Acc.
+
+
+%% X.691:10.9 Encoding of a length determinant
+%%encode_small_length(undefined,Len) -> % null means no UpperBound
+%% encode_small_number(Len).
+
+%% X.691:10.9.3.5
+%% X.691:10.9.3.7
+encode_length(undefined,Len) -> % un-constrained
+ if
+ Len < 128 ->
+ {octet,Len band 16#7F};
+ Len < 16384 ->
+ {octets,2,2#1000000000000000 bor Len};
+ true ->
+ exit({error,{asn1,{encode_length,{nyi,above_16k}}}})
+ end;
+
+encode_length({0,'MAX'},Len) ->
+ encode_length(undefined,Len);
+encode_length({Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ encode_constrained_number({Lb,Ub},Len);
+encode_length({{Lb,Ub},[]},Len) when Ub =< 65535 ,Lb >= 0 ->
+ %% constrained extensible
+ [{bit,0},encode_constrained_number({Lb,Ub},Len)];
+encode_length(SingleValue,_) when integer(SingleValue) ->
+ [].
+
+encode_small_length(Len) when Len =< 64 ->
+ [{bit,0},{bits,6,Len-1}];
+encode_small_length(Len) ->
+ [{bit,1},encode_length(undefined,Len)].
+
+decode_small_length(Buffer) ->
+ case getbit(Buffer) of
+ {0,Remain} ->
+ {Bits,Remain2} = getbits(Remain,6),
+ {Bits+1,Remain2};
+ {1,Remain} ->
+ decode_length(Remain,undefined)
+ end.
+
+decode_length(Buffer) ->
+ decode_length(Buffer,undefined).
+
+decode_length(Buffer,undefined) -> % un-constrained
+ Buffer2 = align(Buffer),
+ {Bits,_} = getbits(Buffer2,2),
+ case Bits of
+ 2 ->
+ {Val,Bytes3} = getoctets(Buffer2,2),
+ {(Val band 16#3FFF),Bytes3};
+ 3 ->
+ exit({error,{asn1,{decode_length,{nyi,above_16k}}}});
+ _ ->
+ {Val,Bytes3} = getoctet(Buffer2),
+ {Val band 16#7F,Bytes3}
+ end;
+
+decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained
+ decode_constrained_number(Buffer,{Lb,Ub});
+
+decode_length(Buffer,{{Lb,Ub},[]}) ->
+ case getbit(Buffer) of
+ {0,Buffer2} ->
+ decode_length(Buffer2, {Lb,Ub})
+ end;
+ % X.691:10.9.3.5
+decode_length(Buffer,{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub
+ case getbit(Buffer) of
+ {0,Remain} ->
+ getbits(Remain,7);
+ {1,_Remain} ->
+ {Val,Remain2} = getoctets(Buffer,2),
+ {Val band 2#0111111111111111, Remain2}
+ end;
+decode_length(Buffer,SingleValue) when integer(SingleValue) ->
+ {SingleValue,Buffer}.
+
+
+% X.691:11
+encode_boolean({Name,Val}) when atom(Name) ->
+ encode_boolean(Val);
+encode_boolean(true) ->
+ {bit,1};
+encode_boolean(false) ->
+ {bit,0};
+encode_boolean(Val) ->
+ exit({error,{asn1,{encode_boolean,Val}}}).
+
+
+decode_boolean(Buffer) -> %when record(Buffer,buffer)
+ case getbit(Buffer) of
+ {1,Remain} -> {true,Remain};
+ {0,Remain} -> {false,Remain}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:12
+%% ENUMERATED
+%%
+%% encode_enumerated(C,Value,NamedNumberTup) -> CompleteList
+%%
+%%
+
+encode_enumerated(C,{Name,Value},NamedNumberList) when
+ atom(Name),list(NamedNumberList) ->
+ encode_enumerated(C,Value,NamedNumberList);
+
+%% ENUMERATED with extension mark
+encode_enumerated(_C,{asn1_enum,Value},{_Nlist1,Nlist2}) when Value >= length(Nlist2) ->
+ [{bit,1},encode_small_number(Value)];
+encode_enumerated(C,Value,{Nlist1,Nlist2}) ->
+ case enum_search(Value,Nlist1,0) of
+ NewV when integer(NewV) ->
+ [{bit,0},encode_integer(C,NewV)];
+ false ->
+ case enum_search(Value,Nlist2,0) of
+ ExtV when integer(ExtV) ->
+ [{bit,1},encode_small_number(ExtV)];
+ false ->
+ exit({error,{asn1,{encode_enumerated,Value}}})
+ end
+ end;
+
+encode_enumerated(C,Value,NamedNumberList) when list(NamedNumberList) ->
+ case enum_search(Value,NamedNumberList,0) of
+ NewV when integer(NewV) ->
+ encode_integer(C,NewV);
+ false ->
+ exit({error,{asn1,{encode_enumerated,Value}}})
+ end.
+
+%% returns the ordinal number from 0 ,1 ... in the list where Name is found
+%% or false if not found
+%%
+enum_search(Name,[Name|_NamedNumberList],Acc) ->
+ Acc;
+enum_search(Name,[_H|T],Acc) ->
+ enum_search(Name,T,Acc+1);
+enum_search(_,[],_) ->
+ false. % name not found !error
+
+%% ENUMERATED with extension marker
+decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) ->
+ {Ext,Buffer2} = getext(Buffer),
+ case Ext of
+ 0 -> % not an extension value
+ {Val,Buffer3} = decode_integer(Buffer2,C),
+ case catch (element(Val+1,Ntup1)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}})
+ end;
+ 1 -> % this an extension value
+ {Val,Buffer3} = decode_small_number(Buffer2),
+ case catch (element(Val+1,Ntup2)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer3};
+ _ -> {{asn1_enum,Val},Buffer3}
+ end
+ end;
+
+decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) ->
+ {Val,Buffer2} = decode_integer(Buffer,C),
+ case catch (element(Val+1,NamedNumberTup)) of
+ NewVal when atom(NewVal) -> {NewVal,Buffer2};
+ _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}})
+ end.
+
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+%% Bitstring value, ITU_T X.690 Chapter 8.5
+%%===============================================================================
+%%===============================================================================
+%%===============================================================================
+
+%%===============================================================================
+%% encode bitstring value
+%%===============================================================================
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% bitstring NamedBitList
+%% Val can be of:
+%% - [identifiers] where only named identifers are set to one,
+%% the Constraint must then have some information of the
+%% bitlength.
+%% - [list of ones and zeroes] all bits
+%% - integer value representing the bitlist
+%% C is constraint Len, only valid when identifiers
+
+
+%% when the value is a list of {Unused,BinBits}, where
+%% Unused = integer(),
+%% BinBits = binary().
+encode_bit_string(C,Bin={Unused,BinBits},NamedBitList) when integer(Unused),
+ binary(BinBits) ->
+ encode_bin_bit_string(C,Bin,NamedBitList);
+
+%% when the value is a list of named bits
+encode_bit_string(C, [FirstVal | RestVal], NamedBitList) when atom(FirstVal) ->
+ ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+encode_bit_string(C, [{bit,No} | RestVal], NamedBitList) ->
+ ToSetPos = get_all_bitposes([{bit,No} | RestVal], NamedBitList, []),
+ BitList = make_and_set_list(ToSetPos,0),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a list of ones and zeroes
+
+encode_bit_string(C, BitListValue, NamedBitList) when list(BitListValue) ->
+ Bl1 =
+ case NamedBitList of
+ [] -> % dont remove trailing zeroes
+ BitListValue;
+ _ -> % first remove any trailing zeroes
+ lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitListValue)))
+ end,
+ BitList = [{bit,X} || X <- Bl1],
+ BListLen = length(BitList),
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ []; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ pad_list(V,BitList);
+ V when integer(V) -> % fixed length 16 bits or less
+ [align,pad_list(V,BitList)];
+ {Lb,Ub} when integer(Lb),integer(Ub),BListLen<Lb ->
+ %% padding due to OTP-4353
+ [encode_length({Lb,Ub},Lb),align,pad_list(Lb,BitList)];
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ [encode_length({Lb,Ub},length(BitList)),align,BitList];
+ no ->
+ [encode_length(undefined,length(BitList)),align,BitList];
+ Sc={{Lb,Ub},_} when integer(Lb),integer(Ub),BListLen<Lb ->
+ %% padding due to OTP-4353
+ [encode_length(Sc,Lb),align,pad_list(Lb,BitList)];
+ Sc -> % extension marker
+ [encode_length(Sc,length(BitList)),align,BitList]
+ end;
+
+%% when the value is an integer
+encode_bit_string(C, IntegerVal, NamedBitList) when integer(IntegerVal)->
+ BitList = int_to_bitlist(IntegerVal),
+ encode_bit_string(C,BitList,NamedBitList);
+
+%% when the value is a tuple
+encode_bit_string(C,{Name,Val}, NamedBitList) when atom(Name) ->
+ encode_bit_string(C,Val,NamedBitList).
+
+
+%% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits.
+%% Unused = integer(),
+%% BinBits = binary().
+
+encode_bin_bit_string(C,{Unused,BinBits},NamedBitList) ->
+ RemoveZerosIfNNL =
+ fun({NNL,BitList}) ->
+ case NNL of
+ [] -> BitList;
+ _ ->
+ lists:reverse(
+ lists:dropwhile(fun(0)->true;(1)->false end,
+ lists:reverse(BitList)))
+ end
+ end,
+ {OctetList,OLSize,LastBits} =
+ case size(BinBits) of
+ N when N>1 ->
+ IntList = binary_to_list(BinBits),
+ [H|T] = lists:reverse(IntList),
+ Bl1 = RemoveZerosIfNNL({NamedBitList,lists:reverse(int_to_bitlist(H,8-Unused))}),% lists:sublist obsolete if trailing bits are zero !
+ {[{octet,X} || X <- lists:reverse(T)],size(BinBits)-1,
+ [{bit,X} || X <- Bl1]};
+ 1 ->
+ <<B7:1,B6:1,B5:1,B4:1,B3:1,B2:1,B1:1,B0:1>> = BinBits,
+ {[],0,[{bit,X} || X <- lists:sublist([B7,B6,B5,B4,B3,B2,B1,B0],8-Unused)]};
+ _ ->
+ {[],0,[]}
+ end,
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ [];
+ V when integer(V),V=<16 ->
+ [OctetList, pad_list(V,LastBits)];
+ V when integer(V) ->
+% [OctetList, align, pad_list(V rem 8,LastBits)];
+ [align,OctetList, pad_list(V rem 8,LastBits)];
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ NewLastBits = maybe_pad(Lb,length(LastBits)+(OLSize*8),LastBits,NamedBitList),
+ [encode_length({Lb,Ub},length(NewLastBits)+(OLSize*8)),
+% OctetList,align,LastBits];
+ align,OctetList,NewLastBits];
+ no ->
+ [encode_length(undefined,length(LastBits)+(OLSize*8)),
+% OctetList,align,LastBits];
+ align,OctetList,LastBits];
+ Sc={{Lb,_},_} when integer(Lb) ->
+ NewLastBits = maybe_pad(Lb,length(LastBits)+(OLSize*8),LastBits,NamedBitList),
+ [encode_length(Sc,length(NewLastBits)+(OLSize*8)),
+ align,OctetList,NewLastBits];
+ Sc ->
+ [encode_length(Sc,length(LastBits)+(OLSize*8)),
+% OctetList,align,LastBits]
+ align,OctetList,LastBits]
+ end.
+
+maybe_pad(_,_,Bits,[]) ->
+ Bits;
+maybe_pad(Lb,LenBits,Bits,_) when Lb>LenBits ->
+ pad_list(Lb,Bits);
+maybe_pad(_,_,Bits,_) ->
+ Bits.
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a tuple {Unused,Bits}. Unused is the number of unused
+%% bits, least significant bits in the last byte of Bits. Bits is
+%% the BIT STRING represented as a binary.
+%%
+decode_compact_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {{0,<<>>},Buffer};
+ V when integer(V),V=<16 -> %fixed length 16 bits or less
+ compact_bit_string(Buffer,V,NamedNumberList);
+ V when integer(V) -> %fixed length > 16 bits
+ Bytes2 = align(Buffer),
+ compact_bit_string(Bytes2,V,NamedNumberList);
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList);
+ Sc ->
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ compact_bit_string(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%%%%%%%%%%%%%%%
+%% The result is presented as a list of named bits (if possible)
+%% else as a list of 0 and 1.
+%%
+decode_bit_string(Buffer, C, NamedNumberList) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 -> % fixed length
+ {[],Buffer}; % nothing to encode
+ V when integer(V),V=<16 -> % fixed length 16 bits or less
+ bit_list_to_named(Buffer,V,NamedNumberList);
+ V when integer(V) -> % fixed length 16 bits or less
+ Bytes2 = align(Buffer),
+ bit_list_to_named(Bytes2,V,NamedNumberList);
+ {Lb,Ub} when integer(Lb),integer(Ub) ->
+ {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList);
+ no ->
+ {Len,Bytes2} = decode_length(Buffer,undefined),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList);
+ Sc -> % extension marker
+ {Len,Bytes2} = decode_length(Buffer,Sc),
+ Bytes3 = align(Bytes2),
+ bit_list_to_named(Bytes3,Len,NamedNumberList)
+ end.
+
+
+%% if no named bits are declared we will return a
+%% {Unused,Bits}. Unused = integer(),
+%% Bits = binary().
+compact_bit_string(Buffer,Len,[]) ->
+ getbits_as_binary(Len,Buffer); % {{Unused,BinBits},NewBuffer}
+compact_bit_string(Buffer,Len,NamedNumberList) ->
+ bit_list_to_named(Buffer,Len,NamedNumberList).
+
+
+%% if no named bits are declared we will return a
+%% BitList = [0 | 1]
+
+bit_list_to_named(Buffer,Len,[]) ->
+ getbits_as_list(Len,Buffer);
+
+%% if there are named bits declared we will return a named
+%% BitList where the names are atoms and unnamed bits represented
+%% as {bit,Pos}
+%% BitList = [atom() | {bit,Pos}]
+%% Pos = integer()
+
+bit_list_to_named(Buffer,Len,NamedNumberList) ->
+ {BitList,Rest} = getbits_as_list(Len,Buffer),
+ {bit_list_to_named1(0,BitList,NamedNumberList,[]), Rest}.
+
+bit_list_to_named1(Pos,[0|Bt],Names,Acc) ->
+ bit_list_to_named1(Pos+1,Bt,Names,Acc);
+bit_list_to_named1(Pos,[1|Bt],Names,Acc) ->
+ case lists:keysearch(Pos,2,Names) of
+ {value,{Name,_}} ->
+ bit_list_to_named1(Pos+1,Bt,Names,[Name|Acc]);
+ _ ->
+ bit_list_to_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc])
+ end;
+bit_list_to_named1(_Pos,[],_Names,Acc) ->
+ lists:reverse(Acc).
+
+
+
+%%%%%%%%%%%%%%%
+%%
+
+int_to_bitlist(0) ->
+ [];
+int_to_bitlist(Int) when integer(Int), Int >= 0 ->
+ [Int band 1 | int_to_bitlist(Int bsr 1)].
+
+int_to_bitlist(_Int,0) ->
+ [];
+int_to_bitlist(0,N) ->
+ [0|int_to_bitlist(0,N-1)];
+int_to_bitlist(Int,N) ->
+ [Int band 1 | int_to_bitlist(Int bsr 1, N-1)].
+
+
+%%%%%%%%%%%%%%%%%%
+%% get_all_bitposes([list of named bits to set], named_bit_db, []) ->
+%% [sorted_list_of_bitpositions_to_set]
+
+get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]);
+
+get_all_bitposes([Val | Rest], NamedBitList, Ack) ->
+ case lists:keysearch(Val, 1, NamedBitList) of
+ {value, {_ValName, ValPos}} ->
+ get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]);
+ _ ->
+ exit({error,{asn1, {bitstring_namedbit, Val}}})
+ end;
+get_all_bitposes([], _NamedBitList, Ack) ->
+ lists:sort(Ack).
+
+%%%%%%%%%%%%%%%%%%
+%% make_and_set_list([list of positions to set to 1])->
+%% returns list with all in SetPos set.
+%% in positioning in list the first element is 0, the second 1 etc.., but
+%%
+
+make_and_set_list([XPos|SetPos], XPos) ->
+ [1 | make_and_set_list(SetPos, XPos + 1)];
+make_and_set_list([Pos|SetPos], XPos) ->
+ [0 | make_and_set_list([Pos | SetPos], XPos + 1)];
+make_and_set_list([], _XPos) ->
+ [].
+
+%%%%%%%%%%%%%%%%%
+%% pad_list(N,BitList) -> PaddedList
+%% returns a padded (with trailing {bit,0} elements) list of length N
+%% if Bitlist contains more than N significant bits set an exit asn1_error
+%% is generated
+
+pad_list(0,BitList) ->
+ case BitList of
+ [] -> [];
+ _ -> exit({error,{asn1,{range_error,{bit_string,BitList}}}})
+ end;
+pad_list(N,[Bh|Bt]) ->
+ [Bh|pad_list(N-1,Bt)];
+pad_list(N,[]) ->
+ [{bit,0},pad_list(N-1,[])].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% X.691:16
+%% encode_octet_string(Constraint,ExtensionMarker,Val)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+encode_octet_string(C,{Name,Val}) when atom(Name) ->
+ encode_octet_string(C,false,Val);
+encode_octet_string(C,Val) ->
+ encode_octet_string(C,false,Val).
+
+encode_octet_string(C,Bool,{_Name,Val}) ->
+ encode_octet_string(C,Bool,Val);
+encode_octet_string(_,true,_) ->
+ exit({error,{asn1,{'not_supported',extensionmarker}}});
+encode_octet_string(C,false,Val) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ [];
+ 1 ->
+ [V] = Val,
+ {bits,8,V};
+ 2 ->
+ [V1,V2] = Val,
+ [{bits,8,V1},{bits,8,V2}];
+ Sv when Sv =<65535, Sv == length(Val) -> % fixed length
+ [align,{octets,Val}];
+ {Lb,Ub} ->
+ [encode_length({Lb,Ub},length(Val)),align,
+ {octets,Val}];
+ Sv when list(Sv) ->
+ [encode_length({hd(Sv),lists:max(Sv)},length(Val)),align,
+ {octets,Val}];
+ no ->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}]
+ end.
+
+decode_octet_string(Bytes,Range) ->
+ decode_octet_string(Bytes,Range,false).
+
+decode_octet_string(Bytes,C,false) ->
+ case get_constraint(C,'SizeConstraint') of
+ 0 ->
+ {[],Bytes};
+ 1 ->
+ {B1,Bytes2} = getbits(Bytes,8),
+ {[B1],Bytes2};
+ 2 ->
+ {B1,Bytes2}= getbits(Bytes,8),
+ {B2,Bytes3}= getbits(Bytes2,8),
+ {[B1,B2],Bytes3};
+ {_,0} ->
+ {[],Bytes};
+ Sv when integer(Sv), Sv =<65535 -> % fixed length
+ Bytes2 = align(Bytes),
+ getoctets_as_list(Bytes2,Sv);
+ {Lb,Ub} ->
+ {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+ Sv when list(Sv) ->
+ {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len);
+ no ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restricted char string types
+%% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString)
+%% X.691:26 and X.680:34-36
+%%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val)
+
+
+encode_restricted_string(aligned,{Name,Val}) when atom(Name) ->
+ encode_restricted_string(aligned,Val);
+
+encode_restricted_string(aligned,Val) when list(Val)->
+ [encode_length(undefined,length(Val)),align,
+ {octets,Val}].
+
+encode_known_multiplier_string(aligned,StringType,C,_Ext,{Name,Val}) when atom(Name) ->
+ encode_known_multiplier_string(aligned,StringType,C,false,Val);
+
+encode_known_multiplier_string(aligned,StringType,C,_Ext,Val) ->
+ Result = chars_encode(C,StringType,Val),
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ case {StringType,Result} of
+ {'BMPString',{octets,Ol}} ->
+ [{bits,8,Oct}||Oct <- Ol];
+ _ ->
+ Result
+ end;
+ 0 ->
+ [];
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ [align,Result];
+ {Ub,Lb} ->
+ [encode_length({Ub,Lb},length(Val)),align,Result];
+ Vl when list(Vl) ->
+ [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),align,Result];
+ no ->
+ [encode_length(undefined,length(Val)),align,Result]
+ end.
+
+decode_restricted_string(Bytes,aligned) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ Bytes3 = align(Bytes2),
+ getoctets_as_list(Bytes3,Len).
+
+decode_known_multiplier_string(Bytes,aligned,StringType,C,_Ext) ->
+ NumBits = get_NumBits(C,StringType),
+ case get_constraint(C,'SizeConstraint') of
+ Ub when integer(Ub), Ub*NumBits =< 16 ->
+ chars_decode(Bytes,NumBits,StringType,C,Ub);
+ Ub when integer(Ub),Ub =<65535 -> % fixed length
+ Bytes1 = align(Bytes),
+ chars_decode(Bytes1,NumBits,StringType,C,Ub);
+ 0 ->
+ {[],Bytes};
+ Vl when list(Vl) ->
+ {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ no ->
+ {Len,Bytes1} = decode_length(Bytes,undefined),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len);
+ {Lb,Ub}->
+ {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}),
+ Bytes2 = align(Bytes1),
+ chars_decode(Bytes2,NumBits,StringType,C,Len)
+ end.
+
+
+encode_NumericString(C,Val) ->
+ encode_known_multiplier_string(aligned,'NumericString',C,false,Val).
+decode_NumericString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'NumericString',C,false).
+
+encode_PrintableString(C,Val) ->
+ encode_known_multiplier_string(aligned,'PrintableString',C,false,Val).
+decode_PrintableString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'PrintableString',C,false).
+
+encode_VisibleString(C,Val) -> % equivalent with ISO646String
+ encode_known_multiplier_string(aligned,'VisibleString',C,false,Val).
+decode_VisibleString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'VisibleString',C,false).
+
+encode_IA5String(C,Val) ->
+ encode_known_multiplier_string(aligned,'IA5String',C,false,Val).
+decode_IA5String(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'IA5String',C,false).
+
+encode_BMPString(C,Val) ->
+ encode_known_multiplier_string(aligned,'BMPString',C,false,Val).
+decode_BMPString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'BMPString',C,false).
+
+encode_UniversalString(C,Val) ->
+ encode_known_multiplier_string(aligned,'UniversalString',C,false,Val).
+decode_UniversalString(Bytes,C) ->
+ decode_known_multiplier_string(Bytes,aligned,'UniversalString',C,false).
+
+%% end of known-multiplier strings for which PER visible constraints are
+%% applied
+
+encode_GeneralString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GeneralString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_GraphicString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_GraphicString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_ObjectDescriptor(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_ObjectDescriptor(Bytes) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_TeletexString(_C,Val) -> % equivalent with T61String
+ encode_restricted_string(aligned,Val).
+decode_TeletexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+encode_VideotexString(_C,Val) ->
+ encode_restricted_string(aligned,Val).
+decode_VideotexString(Bytes,_C) ->
+ decode_restricted_string(Bytes,aligned).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes}
+%%
+getBMPChars(Bytes,1) ->
+ {O1,Bytes2} = getbits(Bytes,8),
+ {O2,Bytes3} = getbits(Bytes2,8),
+ if
+ O1 == 0 ->
+ {[O2],Bytes3};
+ true ->
+ {[{0,0,O1,O2}],Bytes3}
+ end;
+getBMPChars(Bytes,Len) ->
+ getBMPChars(Bytes,Len,[]).
+
+getBMPChars(Bytes,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+getBMPChars(Bytes,Len,Acc) ->
+ {Octs,Bytes1} = getoctets_as_list(Bytes,2),
+ case Octs of
+ [0,O2] ->
+ getBMPChars(Bytes1,Len-1,[O2|Acc]);
+ [O1,O2]->
+ getBMPChars(Bytes1,Len-1,[{0,0,O1,O2}|Acc])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% chars_encode(C,StringType,Value) -> ValueList
+%%
+%% encodes chars according to the per rules taking the constraint PermittedAlphabet
+%% into account.
+%% This function does only encode the value part and NOT the length
+
+chars_encode(C,StringType,Value) ->
+ case {StringType,get_constraint(C,'PermittedAlphabet')} of
+ {'UniversalString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}});
+ {'BMPString',{_,_Sv}} ->
+ exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}});
+ _ ->
+ {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)},
+ chars_encode2(Value,NumBits,CharOutTab)
+ end.
+
+chars_encode2([H|T],NumBits,{Min,Max,notab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,H-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min ->
+ [{bits,NumBits,element(H-Min+1,Tab)}|chars_encode2(T,NumBits,{Min,Max,Tab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,notab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+ [{bits,NumBits,((((((A bsl 8) + B) bsl 8) + C) bsl 8) + D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,Tab}) ->
+ %% no value range check here (ought to be, but very expensive)
+% [{bits,NumBits,element((A*B*C*D)-Min,Tab)}|chars_encode2(T,NumBits,{Min,Max,notab})];
+ [{bits,NumBits,element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)}|chars_encode2(T,NumBits,{Min,Max,notab})];
+chars_encode2([H|_T],_,{_,_,_}) ->
+ exit({error,{asn1,{illegal_char_value,H}}});
+chars_encode2([],_,_) ->
+ [].
+
+
+get_NumBits(C,StringType) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ charbits(length(Sv),aligned);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ charbits(128,aligned); % 16#00..16#7F
+ 'VisibleString' ->
+ charbits(95,aligned); % 16#20..16#7E
+ 'PrintableString' ->
+ charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+ 'NumericString' ->
+ charbits(11,aligned); % $ ,"0123456789"
+ 'UniversalString' ->
+ 32;
+ 'BMPString' ->
+ 16
+ end
+ end.
+
+%%Maybe used later
+%%get_MaxChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% lists:nth(length(Sv),Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#7F; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#7E; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $9; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#ffffffff;
+%% 'BMPString' ->
+%% 16#ffff
+%% end
+%% end.
+
+%%Maybe used later
+%%get_MinChar(C,StringType) ->
+%% case get_constraint(C,'PermittedAlphabet') of
+%% {'SingleValue',Sv} ->
+%% hd(Sv);
+%% no ->
+%% case StringType of
+%% 'IA5String' ->
+%% 16#00; % 16#00..16#7F
+%% 'VisibleString' ->
+%% 16#20; % 16#20..16#7E
+%% 'PrintableString' ->
+%% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z
+%% 'NumericString' ->
+%% $\s; % $ ,"0123456789"
+%% 'UniversalString' ->
+%% 16#00;
+%% 'BMPString' ->
+%% 16#00
+%% end
+%% end.
+
+get_CharOutTab(C,StringType) ->
+ get_CharTab(C,StringType,out).
+
+get_CharInTab(C,StringType) ->
+ get_CharTab(C,StringType,in).
+
+get_CharTab(C,StringType,InOut) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ {'SingleValue',Sv} ->
+ get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut);
+ no ->
+ case StringType of
+ 'IA5String' ->
+ {0,16#7F,notab};
+ 'VisibleString' ->
+ get_CharTab2(C,StringType,16#20,16#7F,notab,InOut);
+ 'PrintableString' ->
+ Chars = lists:sort(
+ " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"),
+ get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut);
+ 'NumericString' ->
+ get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut);
+ 'UniversalString' ->
+ {0,16#FFFFFFFF,notab};
+ 'BMPString' ->
+ {0,16#FFFF,notab}
+ end
+ end.
+
+get_CharTab2(C,StringType,Min,Max,Chars,InOut) ->
+ BitValMax = (1 bsl get_NumBits(C,StringType))-1,
+ if
+ Max =< BitValMax ->
+ {0,Max,notab};
+ true ->
+ case InOut of
+ out ->
+ {Min,Max,create_char_tab(Min,Chars)};
+ in ->
+ {Min,Max,list_to_tuple(Chars)}
+ end
+ end.
+
+create_char_tab(Min,L) ->
+ list_to_tuple(create_char_tab(Min,L,0)).
+create_char_tab(Min,[Min|T],V) ->
+ [V|create_char_tab(Min+1,T,V+1)];
+create_char_tab(_Min,[],_V) ->
+ [];
+create_char_tab(Min,L,V) ->
+ [false|create_char_tab(Min+1,L,V)].
+
+%% This very inefficient and should be moved to compiletime
+charbits(NumOfChars,aligned) ->
+ case charbits(NumOfChars) of
+ 1 -> 1;
+ 2 -> 2;
+ B when B > 2, B =< 4 -> 4;
+ B when B > 4, B =< 8 -> 8;
+ B when B > 8, B =< 16 -> 16;
+ B when B > 16, B =< 32 -> 32
+ end.
+
+charbits(NumOfChars) when NumOfChars =< 2 -> 1;
+charbits(NumOfChars) when NumOfChars =< 4 -> 2;
+charbits(NumOfChars) when NumOfChars =< 8 -> 3;
+charbits(NumOfChars) when NumOfChars =< 16 -> 4;
+charbits(NumOfChars) when NumOfChars =< 32 -> 5;
+charbits(NumOfChars) when NumOfChars =< 64 -> 6;
+charbits(NumOfChars) when NumOfChars =< 128 -> 7;
+charbits(NumOfChars) when NumOfChars =< 256 -> 8;
+charbits(NumOfChars) when NumOfChars =< 512 -> 9;
+charbits(NumOfChars) when NumOfChars =< 1024 -> 10;
+charbits(NumOfChars) when NumOfChars =< 2048 -> 11;
+charbits(NumOfChars) when NumOfChars =< 4096 -> 12;
+charbits(NumOfChars) when NumOfChars =< 8192 -> 13;
+charbits(NumOfChars) when NumOfChars =< 16384 -> 14;
+charbits(NumOfChars) when NumOfChars =< 32768 -> 15;
+charbits(NumOfChars) when NumOfChars =< 65536 -> 16;
+charbits(NumOfChars) when integer(NumOfChars) ->
+ 16 + charbits1(NumOfChars bsr 16).
+
+charbits1(0) ->
+ 0;
+charbits1(NumOfChars) ->
+ 1 + charbits1(NumOfChars bsr 1).
+
+
+chars_decode(Bytes,_,'BMPString',C,Len) ->
+ case get_constraint(C,'PermittedAlphabet') of
+ no ->
+ getBMPChars(Bytes,Len);
+ _ ->
+ exit({error,{asn1,
+ {'not implemented',
+ "BMPString with PermittedAlphabet constraint"}}})
+ end;
+chars_decode(Bytes,NumBits,StringType,C,Len) ->
+ CharInTab = get_CharInTab(C,StringType),
+ chars_decode2(Bytes,CharInTab,NumBits,Len).
+
+
+chars_decode2(Bytes,CharInTab,NumBits,Len) ->
+ chars_decode2(Bytes,CharInTab,NumBits,Len,[]).
+
+chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) ->
+ {lists:reverse(Acc),Bytes};
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ Result = case minimum_octets(Char+Min) of
+ [NewChar] -> NewChar;
+ [C1,C2] -> {0,0,C1,C2};
+ [C1,C2,C3] -> {0,C1,C2,C3};
+ [C1,C2,C3,C4] -> {C1,C2,C3,C4}
+ end,
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]);
+chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]);
+
+%% BMPString and UniversalString with PermittedAlphabet is currently not supported
+chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) ->
+ {Char,Bytes2} = getbits(Bytes,NumBits),
+ chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]).
+
+
+ % X.691:17
+encode_null({Name,Val}) when atom(Name) ->
+ encode_null(Val);
+encode_null(_) -> []. % encodes to nothing
+
+decode_null(Bytes) ->
+ {'NULL',Bytes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_object_identifier(Val) -> CompleteList
+%% encode_object_identifier({Name,Val}) -> CompleteList
+%% Val -> {Int1,Int2,...,IntN} % N >= 2
+%% Name -> atom()
+%% Int1 -> integer(0..2)
+%% Int2 -> integer(0..39) when Int1 (0..1) else integer()
+%% Int3-N -> integer()
+%% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...]
+%%
+encode_object_identifier({Name,Val}) when atom(Name) ->
+ encode_object_identifier(Val);
+encode_object_identifier(Val) ->
+ Octets = e_object_identifier(Val,notag),
+ [{debug,object_identifier},encode_length(undefined,length(Octets)),{octets,Octets}].
+
+%% This code is copied from asn1_encode.erl (BER) and corrected and modified
+
+e_object_identifier({'OBJECT IDENTIFIER',V},DoTag) ->
+ e_object_identifier(V,DoTag);
+e_object_identifier({Cname,V},DoTag) when atom(Cname),tuple(V) ->
+ e_object_identifier(tuple_to_list(V),DoTag);
+e_object_identifier({Cname,V},DoTag) when atom(Cname),list(V) ->
+ e_object_identifier(V,DoTag);
+e_object_identifier(V,DoTag) when tuple(V) ->
+ e_object_identifier(tuple_to_list(V),DoTag);
+
+% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
+e_object_identifier([E1,E2|Tail],_DoTag) when E1 =< 2 ->
+ Head = 40*E1 + E2, % weird
+ Res = e_object_elements([Head|Tail]),
+% dotag(DoTag,[6],elength(length(Res)+1),[Head|Res]),
+ Res.
+
+e_object_elements([]) ->
+ [];
+e_object_elements([H|T]) ->
+ lists:append(e_object_element(H),e_object_elements(T)).
+
+e_object_element(Num) when Num < 128 ->
+ [Num];
+% must be changed to handle more than 2 octets
+e_object_element(Num) -> %% when Num < ???
+ Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000,
+ Right = Num band 2#1111111 ,
+ [Left,Right].
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes}
+%% ObjId -> {integer(),integer(),...} % at least 2 integers
+%% RemainingBytes -> [integer()] when integer() (0..255)
+decode_object_identifier(Bytes) ->
+ {Len,Bytes2} = decode_length(Bytes,undefined),
+ {Octs,Bytes3} = getoctets_as_list(Bytes2,Len),
+ [First|Rest] = dec_subidentifiers(Octs,0,[]),
+ Idlist = if
+ First < 40 ->
+ [0,First|Rest];
+ First < 80 ->
+ [1,First - 40|Rest];
+ true ->
+ [2,First - 80|Rest]
+ end,
+ {list_to_tuple(Idlist),Bytes3}.
+
+dec_subidentifiers([H|T],Av,Al) when H >=16#80 ->
+ dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al);
+dec_subidentifiers([H|T],Av,Al) ->
+ dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]);
+dec_subidentifiers([],_Av,Al) ->
+ lists:reverse(Al).
+
+get_constraint(C,Key) ->
+ case lists:keysearch(Key,1,C) of
+ false ->
+ no;
+ {value,{_,V}} ->
+ V
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% complete(InList) -> ByteList
+%% Takes a coded list with bits and bytes and converts it to a list of bytes
+%% Should be applied as the last step at encode of a complete ASN.1 type
+%%
+complete(InList) when list(InList) ->
+ complete(InList,[],0);
+complete(InList) ->
+ complete([InList],[],0).
+
+complete([{debug,_}|T], Acc, Acclen) ->
+ complete(T,Acc,Acclen);
+complete([H|T],Acc,Acclen) when list(H) ->
+ complete(lists:concat([H,T]),Acc,Acclen);
+
+
+complete([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) ->
+ Newval = case N of
+ 1 ->
+ Val4 = Val band 16#FF,
+ [Val4];
+ 2 ->
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val3,Val4];
+ 3 ->
+ Val2 = (Val bsr 16) band 16#FF,
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val2,Val3,Val4];
+ 4 ->
+ Val1 = (Val bsr 24) band 16#FF,
+ Val2 = (Val bsr 16) band 16#FF,
+ Val3 = (Val bsr 8) band 16#FF,
+ Val4 = Val band 16#FF,
+ [Val1,Val2,Val3,Val4]
+ end,
+ complete([{octets,Newval}|T],Acc,Acclen);
+
+complete([{octets,Oct}|T],[],_Acclen) when list(Oct) ->
+ complete(T,lists:reverse(Oct),0);
+complete([{octets,Oct}|T],[Hacc|Tacc],Acclen) when list(Oct) ->
+ Rest = 8 - Acclen,
+ if
+ Rest == 8 ->
+ complete(T,lists:concat([lists:reverse(Oct),[Hacc|Tacc]]),0);
+ true ->
+ complete(T,lists:concat([lists:reverse(Oct),[Hacc bsl Rest|Tacc]]),0)
+ end;
+
+complete([{bit,Val}|T], Acc, Acclen) ->
+ complete([{bits,1,Val}|T],Acc,Acclen);
+complete([{octet,Val}|T], Acc, Acclen) ->
+ complete([{octets,1,Val}|T],Acc,Acclen);
+
+complete([{bits,N,Val}|T], Acc, 0) when N =< 8 ->
+ complete(T,[Val|Acc],N);
+complete([{bits,N,Val}|T], [Hacc|Tacc], Acclen) when N =< 8 ->
+ Rest = 8 - Acclen,
+ if
+ Rest >= N ->
+ complete(T,[(Hacc bsl N) + Val|Tacc],(Acclen+N) rem 8);
+ true ->
+ Diff = N - Rest,
+ NewHacc = (Hacc bsl Rest) + (Val bsr Diff),
+ Mask = element(Diff,{1,3,7,15,31,63,127,255}),
+ complete(T,[(Val band Mask),NewHacc|Tacc],(Acclen+N) rem 8)
+ end;
+complete([{bits,N,Val}|T], Acc, Acclen) -> % N > 8
+ complete([{bits,N-8,Val bsr 8},{bits,8,Val band 255}|T],Acc,Acclen);
+
+complete([align|T],Acc,0) ->
+ complete(T,Acc,0);
+complete([align|T],[Hacc|Tacc],Acclen) ->
+ Rest = 8 - Acclen,
+ complete(T,[Hacc bsl Rest|Tacc],0);
+complete([{octets,_N,Val}|T],Acc,Acclen) when list(Val) -> % no security check here
+ complete([{octets,Val}|T],Acc,Acclen);
+
+complete([],[],0) ->
+ [0]; % a complete encoding must always be at least 1 byte
+complete([],Acc,0) ->
+ lists:reverse(Acc);
+complete([],[Hacc|Tacc],Acclen) when Acclen > 0->
+ Rest = 8 - Acclen,
+ NewHacc = Hacc bsl Rest,
+ lists:reverse([NewHacc|Tacc]).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_history.sgml b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_history.sgml
new file mode 100644
index 0000000000..f63b3360eb
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_history.sgml
@@ -0,0 +1,100 @@
+<!doctype chapter PUBLIC "-//Stork//DTD chapter//EN">
+<!--
+ ``The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved via the world wide web at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ AB. All Rights Reserved.''
+
+ $Id: notes_history.sgml,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+-->
+<chapter>
+ <header>
+ <title>ASN1 Release Notes (Old)</title>
+ <prepared>Kenneth Lundin</prepared>
+ <responsible>Kenneth Lundin</responsible>
+ <docno></docno>
+ <approved>Kenneth Lundin</approved>
+ <checked>Kenneth Lundin</checked>
+ <date>98-02-02</date>
+ <rev>A</rev>
+ <file>notes_history.sgml</file>
+ </header>
+
+ <p>This document describes the changes made to old versions of the <c>asn1</c> application.
+
+ <section>
+ <title>ASN1 0.8.1</title>
+ <p>This is the first release of the ASN1 application. This version is released
+ for beta-testing. Some functionality will be added until the 1.0 version is
+ released. A list of missing features and restrictions can be found in the
+ chapter below.
+
+ <section>
+ <title>Missing features and other restrictions</title>
+ <list>
+ <item>
+ <p>The encoding rules BER and PER (aligned) is supported. <em>PER (unaligned)
+ IS NOT SUPPORTED</em>.
+ <item>
+ <p>NOT SUPPORTED types <c>ANY</c> and <c>ANY DEFINED BY</c>
+ (is not in the standard any more).
+ <item>
+ <p>NOT SUPPORTED types <c>EXTERNAL</c> and <c>EMBEDDED-PDV</c>.
+ <item>
+ <p>NOT SUPPORTED type <c>REAL</c> (planned to be implemented).
+ <item>
+ <p>The code generation support for value definitions in the ASN.1 notation is very limited
+ (planned to be enhanced).
+ <item>
+ <p>The support for constraints is limited to:
+ <list>
+ <item><p>
+ SizeConstraint SIZE(X)
+ <item><p>
+ SingleValue (1)
+ <item><p>
+ ValueRange (X..Y)
+ <item><p>
+ PermittedAlpabet FROM (but not for BMPString and UniversalString when generating PER).
+ </list>
+ <p>Complex expressions in constraints is not supported (planned to be extended).
+ <item>
+ <p>The current version of the compiler has very limited error checking:
+ <list>
+ <item><p>Stops at first syntax error.
+ <item><p>Does not stop when a reference to an undefined type is found ,
+ but prints an error message. Compilation of the generated
+ Erlang module will then fail.
+ <item><p>A whole number of other semantical controls is currently missing. This
+ means that the compiler will give little or bad help to detect what's wrong
+ with an ASN.1 specification, but will mostly work very well when the
+ ASN.1 specification is correct.
+ </list>
+ <item>
+ <p>The maximum INTEGER supported in this version is a signed 64 bit integer. This
+ limitation is probably quite reasonable. (Planned to be extended).
+ <item>
+ <p>Only AUTOMATIC TAGS supported for PER.
+ <item>
+ <p>Only EXPLICIT and IMPLICIT TAGS supported for BER.
+ <item>
+ <p>The compiler supports decoding of BER-data with indefinite length but it is
+ not possible to produce data with indefinite length with the encoder.
+ </list>
+ </section>
+
+ </section>
+</chapter>
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_latest.sgml b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_latest.sgml
new file mode 100644
index 0000000000..7accc797a6
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/asn1/notes_latest.sgml
@@ -0,0 +1,100 @@
+<!doctype chapter PUBLIC "-//Stork//DTD chapter//EN">
+<!--
+ ``The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved via the world wide web at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ AB. All Rights Reserved.''
+
+ $Id: notes_latest.sgml,v 1.1 2008/12/17 09:53:31 mikpe Exp $
+-->
+<chapter>
+ <header>
+ <title>ASN1 Release Notes</title>
+ <prepared>Kenneth Lundin</prepared>
+ <responsible>Kenneth Lundin</responsible>
+ <docno></docno>
+ <approved>Kenneth Lundin</approved>
+ <checked>Kenneth Lundin</checked>
+ <date>97-10-07</date>
+ <rev>A</rev>
+ <file>notes_latest.sgml</file>
+ </header>
+
+ <p>This document describes the changes made to the asn1 application.
+
+ <section>
+ <title>ASN1 0.8.1</title>
+ <p>This is the first release of the ASN1 application. This version is released
+ for beta-testing. Some functionality will be added until the 1.0 version is
+ released. A list of missing features and restrictions can be found in the
+ chapter below.
+
+ <section>
+ <title>Missing features and other restrictions</title>
+ <list>
+ <item>
+ <p>The encoding rules BER and PER (aligned) is supported. <em>PER (unaligned)
+ IS NOT SUPPORTED</em>.
+ <item>
+ <p>NOT SUPPORTED types <c>ANY</c> and <c>ANY DEFINED BY</c>
+ (is not in the standard any more).
+ <item>
+ <p>NOT SUPPORTED types <c>EXTERNAL</c> and <c>EMBEDDED-PDV</c>.
+ <item>
+ <p>NOT SUPPORTED type <c>REAL</c> (planned to be implemented).
+ <item>
+ <p>The code generation support for value definitions in the ASN.1 notation is very limited
+ (planned to be enhanced).
+ <item>
+ <p>The support for constraints is limited to:
+ <list>
+ <item><p>
+ SizeConstraint SIZE(X)
+ <item><p>
+ SingleValue (1)
+ <item><p>
+ ValueRange (X..Y)
+ <item><p>
+ PermittedAlpabet FROM (but not for BMPString and UniversalString when generating PER).
+ </list>
+ <p>Complex expressions in constraints is not supported (planned to be extended).
+ <item>
+ <p>The current version of the compiler has very limited error checking:
+ <list>
+ <item><p>Stops at first syntax error.
+ <item><p>Does not stop when a reference to an undefined type is found ,
+ but prints an error message. Compilation of the generated
+ Erlang module will then fail.
+ <item><p>A whole number of other semantical controls is currently missing. This
+ means that the compiler will give little or bad help to detect what's wrong
+ with an ASN.1 specification, but will mostly work very well when the
+ ASN.1 specification is correct.
+ </list>
+ <item>
+ <p>The maximum INTEGER supported in this version is a signed 64 bit integer. This
+ limitation is probably quite reasonable. (Planned to be extended).
+ <item>
+ <p>Only AUTOMATIC TAGS supported for PER.
+ <item>
+ <p>Only EXPLICIT and IMPLICIT TAGS supported for BER.
+ <item>
+ <p>The compiler supports decoding of BER-data with indefinite length but it is
+ not possible to produce data with indefinite length with the encoder.
+ </list>
+ </section>
+
+ </section>
+</chapter>
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/Makefile b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/Makefile
new file mode 100644
index 0000000000..ab0d7c0a63
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/Makefile
@@ -0,0 +1,178 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id: Makefile,v 1.1 2008/12/17 09:53:33 mikpe Exp $
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+
+VSN = $(INETS_VSN)
+APP_VSN = "inets-$(VSN)"
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES = \
+ ftp \
+ http \
+ http_lib \
+ httpc_handler \
+ httpc_manager \
+ uri \
+ httpd \
+ httpd_acceptor \
+ httpd_acceptor_sup \
+ httpd_conf \
+ httpd_example \
+ httpd_manager \
+ httpd_misc_sup \
+ httpd_parse \
+ httpd_request_handler \
+ httpd_response \
+ httpd_socket \
+ httpd_sup \
+ httpd_util \
+ httpd_verbosity \
+ inets_sup \
+ mod_actions \
+ mod_alias \
+ mod_auth \
+ mod_auth_plain \
+ mod_auth_dets \
+ mod_auth_mnesia \
+ mod_auth_server \
+ mod_browser \
+ mod_cgi \
+ mod_dir \
+ mod_disk_log \
+ mod_esi \
+ mod_get \
+ mod_head \
+ mod_htaccess \
+ mod_include \
+ mod_log \
+ mod_range \
+ mod_responsecontrol \
+ mod_trace \
+ mod_security \
+ mod_security_server
+
+HRL_FILES = httpd.hrl httpd_verbosity.hrl mod_auth.hrl \
+ http.hrl jnets_httpd.hrl
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+APP_FILE= inets.app
+APPUP_FILE= inets.appup
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# INETS FLAGS
+# ----------------------------------------------------
+# DONT_USE_VERBOSITY = -Ddont_use_verbosity=true
+INETS_FLAGS = -D'SERVER_SOFTWARE="inets/$(VSN)"' \
+ -Ddefault_verbosity=silence \
+ $(DONT_USE_VERBOSITY)
+
+# INETS_DEBUG_DEFAULT = d
+ifeq ($(INETS_DEBUG),)
+ INETS_DEBUG = $(INETS_DEBUG_DEFAULT)
+endif
+
+ifeq ($(INETS_DEBUG),c)
+ INETS_FLAGS += -Dinets_cdebug -Dinets_debug -Dinets_log -Dinets_error
+endif
+ifeq ($(INETS_DEBUG),d)
+ INETS_FLAGS += -Dinets_debug -Dinets_log -Dinets_error
+endif
+ifeq ($(INETS_DEBUG),l)
+ INETS_FLAGS += -Dinets_log -Dinets_error
+endif
+ifeq ($(INETS_DEBUG),e)
+ INETS_FLAGS += -Dinets_error
+endif
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_FLAGS +=
+
+ifeq ($(WARN_UNUSED_WARS),true)
+ERL_COMPILE_FLAGS += +warn_unused_vars
+endif
+
+ERL_COMPILE_FLAGS += $(INETS_FLAGS) \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}'
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
+info:
+ @echo "INETS_DEBUG = $(INETS_DEBUG)"
+ @echo "INETS_FLAGS = $(INETS_FLAGS)"
+ @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)"
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl
new file mode 100644
index 0000000000..be06ec654c
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/ftp.erl
@@ -0,0 +1,1582 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: ftp.erl,v 1.2 2009/03/03 01:55:01 kostis Exp $
+%%
+-module(ftp).
+
+-behaviour(gen_server).
+
+%% This module implements an ftp client based on socket(3)/gen_tcp(3),
+%% file(3) and filename(3).
+%%
+
+
+-define(OPEN_TIMEOUT, 60*1000).
+-define(BYTE_TIMEOUT, 1000). % Timeout for _ONE_ byte to arrive. (ms)
+-define(OPER_TIMEOUT, 300). % Operation timeout (seconds)
+-define(FTP_PORT, 21).
+
+%% Client interface
+-export([cd/2, close/1, delete/2, formaterror/1, help/0,
+ lcd/2, lpwd/1, ls/1, ls/2,
+ mkdir/2, nlist/1, nlist/2,
+ open/1, open/2, open/3,
+ pwd/1,
+ recv/2, recv/3, recv_bin/2,
+ recv_chunk_start/2, recv_chunk/1,
+ rename/3, rmdir/2,
+ send/2, send/3, send_bin/3,
+ send_chunk_start/2, send_chunk/2, send_chunk_end/1,
+ type/2, user/3,user/4,account/2,
+ append/3, append/2, append_bin/3,
+ append_chunk/2, append_chunk_end/1, append_chunk_start/2]).
+
+%% Internal
+-export([init/1, handle_call/3, handle_cast/2,
+ handle_info/2, terminate/2,code_change/3]).
+
+
+%%
+%% CLIENT FUNCTIONS
+%%
+
+%% open(Host)
+%% open(Host, Flags)
+%%
+%% Purpose: Start an ftp client and connect to a host.
+%% Args: Host = string(),
+%% Port = integer(),
+%% Flags = [Flag],
+%% Flag = verbose | debug
+%% Returns: {ok, Pid} | {error, ehost}
+
+%%Tho only option was the host in textual form
+open({option_list,Option_list})->
+ %% Dbg = {debug,[trace,log,statistics]},
+ %% Options = [Dbg],
+ Options = [],
+ {ok,Pid1}=case lists:keysearch(flags,1,Option_list) of
+ {value,{flags,Flags}}->
+ {ok, Pid} = gen_server:start_link(?MODULE, [Flags], Options);
+ false ->
+ {ok, Pid} = gen_server:start_link(?MODULE, [], Options)
+ end,
+ gen_server:call(Pid1, {open, ip_comm,Option_list}, infinity);
+
+
+%%The only option was the tuple form of the ip-number
+open(Host)when tuple(Host) ->
+ open(Host, ?FTP_PORT, []);
+
+%%Host is the string form of the hostname
+open(Host)->
+ open(Host,?FTP_PORT,[]).
+
+
+
+open(Host, Port) when integer(Port) ->
+ open(Host,Port,[]);
+
+open(Host, Flags) when list(Flags) ->
+ open(Host,?FTP_PORT, Flags).
+
+open(Host,Port,Flags) when integer(Port), list(Flags) ->
+ %% Dbg = {debug,[trace,log,statistics]},
+ %% Options = [Dbg],
+ Options = [],
+ {ok, Pid} = gen_server:start_link(?MODULE, [Flags], Options),
+ gen_server:call(Pid, {open, ip_comm, Host, Port}, infinity).
+
+%% user(Pid, User, Pass)
+%% Purpose: Login.
+%% Args: Pid = pid(), User = Pass = string()
+%% Returns: ok | {error, euser} | {error, econn}
+user(Pid, User, Pass) ->
+ gen_server:call(Pid, {user, User, Pass}, infinity).
+
+%% user(Pid, User, Pass,Acc)
+%% Purpose: Login whith a supplied account name
+%% Args: Pid = pid(), User = Pass = Acc = string()
+%% Returns: ok | {error, euser} | {error, econn} | {error, eacct}
+user(Pid, User, Pass,Acc) ->
+ gen_server:call(Pid, {user, User, Pass,Acc}, infinity).
+
+%% account(Pid,Acc)
+%% Purpose: Set a user Account.
+%% Args: Pid = pid(), Acc= string()
+%% Returns: ok | {error, eacct}
+account(Pid,Acc) ->
+ gen_server:call(Pid, {account,Acc}, infinity).
+
+%% pwd(Pid)
+%%
+%% Purpose: Get the current working directory at remote server.
+%% Args: Pid = pid()
+%% Returns: {ok, Dir} | {error, elogin} | {error, econn}
+pwd(Pid) ->
+ gen_server:call(Pid, pwd, infinity).
+
+%% lpwd(Pid)
+%%
+%% Purpose: Get the current working directory at local server.
+%% Args: Pid = pid()
+%% Returns: {ok, Dir} | {error, elogin}
+lpwd(Pid) ->
+ gen_server:call(Pid, lpwd, infinity).
+
+%% cd(Pid, Dir)
+%%
+%% Purpose: Change current working directory at remote server.
+%% Args: Pid = pid(), Dir = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+cd(Pid, Dir) ->
+ gen_server:call(Pid, {cd, Dir}, infinity).
+
+%% lcd(Pid, Dir)
+%%
+%% Purpose: Change current working directory for the local client.
+%% Args: Pid = pid(), Dir = string()
+%% Returns: ok | {error, epath}
+lcd(Pid, Dir) ->
+ gen_server:call(Pid, {lcd, Dir}, infinity).
+
+%% ls(Pid)
+%% ls(Pid, Dir)
+%%
+%% Purpose: List the contents of current directory (ls/1) or directory
+%% Dir (ls/2) at remote server.
+%% Args: Pid = pid(), Dir = string()
+%% Returns: {ok, Listing} | {error, epath} | {error, elogin} | {error, econn}
+ls(Pid) ->
+ ls(Pid, "").
+ls(Pid, Dir) ->
+ gen_server:call(Pid, {dir, long, Dir}, infinity).
+
+%% nlist(Pid)
+%% nlist(Pid, Dir)
+%%
+%% Purpose: List the contents of current directory (ls/1) or directory
+%% Dir (ls/2) at remote server. The returned list is a stream
+%% of file names.
+%% Args: Pid = pid(), Dir = string()
+%% Returns: {ok, Listing} | {error, epath} | {error, elogin} | {error, econn}
+nlist(Pid) ->
+ nlist(Pid, "").
+nlist(Pid, Dir) ->
+ gen_server:call(Pid, {dir, short, Dir}, infinity).
+
+%% rename(Pid, CurrFile, NewFile)
+%%
+%% Purpose: Rename a file at remote server.
+%% Args: Pid = pid(), CurrFile = NewFile = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+rename(Pid, CurrFile, NewFile) ->
+ gen_server:call(Pid, {rename, CurrFile, NewFile}, infinity).
+
+%% delete(Pid, File)
+%%
+%% Purpose: Remove file at remote server.
+%% Args: Pid = pid(), File = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+delete(Pid, File) ->
+ gen_server:call(Pid, {delete, File}, infinity).
+
+%% mkdir(Pid, Dir)
+%%
+%% Purpose: Make directory at remote server.
+%% Args: Pid = pid(), Dir = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+mkdir(Pid, Dir) ->
+ gen_server:call(Pid, {mkdir, Dir}, infinity).
+
+%% rmdir(Pid, Dir)
+%%
+%% Purpose: Remove directory at remote server.
+%% Args: Pid = pid(), Dir = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+rmdir(Pid, Dir) ->
+ gen_server:call(Pid, {rmdir, Dir}, infinity).
+
+%% type(Pid, Type)
+%%
+%% Purpose: Set transfer type.
+%% Args: Pid = pid(), Type = ascii | binary
+%% Returns: ok | {error, etype} | {error, elogin} | {error, econn}
+type(Pid, Type) ->
+ gen_server:call(Pid, {type, Type}, infinity).
+
+%% recv(Pid, RFile [, LFile])
+%%
+%% Purpose: Transfer file from remote server.
+%% Args: Pid = pid(), RFile = LFile = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+recv(Pid, RFile) ->
+ recv(Pid, RFile, "").
+
+recv(Pid, RFile, LFile) ->
+ gen_server:call(Pid, {recv, RFile, LFile}, infinity).
+
+%% recv_bin(Pid, RFile)
+%%
+%% Purpose: Transfer file from remote server into binary.
+%% Args: Pid = pid(), RFile = string()
+%% Returns: {ok, Bin} | {error, epath} | {error, elogin} | {error, econn}
+recv_bin(Pid, RFile) ->
+ gen_server:call(Pid, {recv_bin, RFile}, infinity).
+
+%% recv_chunk_start(Pid, RFile)
+%%
+%% Purpose: Start receive of chunks of remote file.
+%% Args: Pid = pid(), RFile = string().
+%% Returns: ok | {error, elogin} | {error, epath} | {error, econn}
+recv_chunk_start(Pid, RFile) ->
+ gen_server:call(Pid, {recv_chunk_start, RFile}, infinity).
+
+
+%% recv_chunk(Pid, RFile)
+%%
+%% Purpose: Transfer file from remote server into binary in chunks
+%% Args: Pid = pid(), RFile = string()
+%% Returns: Reference
+recv_chunk(Pid) ->
+ gen_server:call(Pid, recv_chunk, infinity).
+
+%% send(Pid, LFile [, RFile])
+%%
+%% Purpose: Transfer file to remote server.
+%% Args: Pid = pid(), LFile = RFile = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+send(Pid, LFile) ->
+ send(Pid, LFile, "").
+
+send(Pid, LFile, RFile) ->
+ gen_server:call(Pid, {send, LFile, RFile}, infinity).
+
+%% send_bin(Pid, Bin, RFile)
+%%
+%% Purpose: Transfer a binary to a remote file.
+%% Args: Pid = pid(), Bin = binary(), RFile = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, enotbinary}
+%% | {error, econn}
+send_bin(Pid, Bin, RFile) when binary(Bin) ->
+ gen_server:call(Pid, {send_bin, Bin, RFile}, infinity);
+send_bin(Pid, Bin, RFile) ->
+ {error, enotbinary}.
+
+%% send_chunk_start(Pid, RFile)
+%%
+%% Purpose: Start transfer of chunks to remote file.
+%% Args: Pid = pid(), RFile = string().
+%% Returns: ok | {error, elogin} | {error, epath} | {error, econn}
+send_chunk_start(Pid, RFile) ->
+ gen_server:call(Pid, {send_chunk_start, RFile}, infinity).
+
+
+%% append_chunk_start(Pid, RFile)
+%%
+%% Purpose: Start append chunks of data to remote file.
+%% Args: Pid = pid(), RFile = string().
+%% Returns: ok | {error, elogin} | {error, epath} | {error, econn}
+append_chunk_start(Pid, RFile) ->
+ gen_server:call(Pid, {append_chunk_start, RFile}, infinity).
+
+
+%% send_chunk(Pid, Bin)
+%%
+%% Purpose: Send chunk to remote file.
+%% Args: Pid = pid(), Bin = binary().
+%% Returns: ok | {error, elogin} | {error, enotbinary} | {error, echunk}
+%% | {error, econn}
+send_chunk(Pid, Bin) when binary(Bin) ->
+ gen_server:call(Pid, {send_chunk, Bin}, infinity);
+send_chunk(Pid, Bin) ->
+ {error, enotbinary}.
+
+%%append_chunk(Pid, Bin)
+%%
+%% Purpose: Append chunk to remote file.
+%% Args: Pid = pid(), Bin = binary().
+%% Returns: ok | {error, elogin} | {error, enotbinary} | {error, echunk}
+%% | {error, econn}
+append_chunk(Pid, Bin) when binary(Bin) ->
+ gen_server:call(Pid, {append_chunk, Bin}, infinity);
+append_chunk(Pid, Bin) ->
+ {error, enotbinary}.
+
+%% send_chunk_end(Pid)
+%%
+%% Purpose: End sending of chunks to remote file.
+%% Args: Pid = pid().
+%% Returns: ok | {error, elogin} | {error, echunk} | {error, econn}
+send_chunk_end(Pid) ->
+ gen_server:call(Pid, send_chunk_end, infinity).
+
+%% append_chunk_end(Pid)
+%%
+%% Purpose: End appending of chunks to remote file.
+%% Args: Pid = pid().
+%% Returns: ok | {error, elogin} | {error, echunk} | {error, econn}
+append_chunk_end(Pid) ->
+ gen_server:call(Pid, append_chunk_end, infinity).
+
+%% append(Pid, LFile,RFile)
+%%
+%% Purpose: Append the local file to the remote file
+%% Args: Pid = pid(), LFile = RFile = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, econn}
+append(Pid, LFile) ->
+ append(Pid, LFile, "").
+
+append(Pid, LFile, RFile) ->
+ gen_server:call(Pid, {append, LFile, RFile}, infinity).
+
+%% append_bin(Pid, Bin, RFile)
+%%
+%% Purpose: Append a binary to a remote file.
+%% Args: Pid = pid(), Bin = binary(), RFile = string()
+%% Returns: ok | {error, epath} | {error, elogin} | {error, enotbinary}
+%% | {error, econn}
+append_bin(Pid, Bin, RFile) when binary(Bin) ->
+ gen_server:call(Pid, {append_bin, Bin, RFile}, infinity);
+append_bin(Pid, Bin, RFile) ->
+ {error, enotbinary}.
+
+
+%% close(Pid)
+%%
+%% Purpose: End the ftp session.
+%% Args: Pid = pid()
+%% Returns: ok
+close(Pid) ->
+ case (catch gen_server:call(Pid, close, 30000)) of
+ ok ->
+ ok;
+ {'EXIT',{noproc,_}} ->
+ %% Already gone...
+ ok;
+ Res ->
+ Res
+ end.
+
+%% formaterror(Tag)
+%%
+%% Purpose: Return diagnostics.
+%% Args: Tag = atom() | {error, atom()}
+%% Returns: string().
+formaterror(Tag) ->
+ errstr(Tag).
+
+%% help()
+%%
+%% Purpose: Print list of valid commands.
+%%
+%% Undocumented.
+%%
+help() ->
+ io:format("\n Commands:\n"
+ " ---------\n"
+ " cd(Pid, Dir)\n"
+ " close(Pid)\n"
+ " delete(Pid, File)\n"
+ " formaterror(Tag)\n"
+ " help()\n"
+ " lcd(Pid, Dir)\n"
+ " lpwd(Pid)\n"
+ " ls(Pid [, Dir])\n"
+ " mkdir(Pid, Dir)\n"
+ " nlist(Pid [, Dir])\n"
+ " open(Host [Port, Flags])\n"
+ " pwd(Pid)\n"
+ " recv(Pid, RFile [, LFile])\n"
+ " recv_bin(Pid, RFile)\n"
+ " recv_chunk_start(Pid, RFile)\n"
+ " recv_chunk(Pid)\n"
+ " rename(Pid, CurrFile, NewFile)\n"
+ " rmdir(Pid, Dir)\n"
+ " send(Pid, LFile [, RFile])\n"
+ " send_chunk(Pid, Bin)\n"
+ " send_chunk_start(Pid, RFile)\n"
+ " send_chunk_end(Pid)\n"
+ " send_bin(Pid, Bin, RFile)\n"
+ " append(Pid, LFile [, RFile])\n"
+ " append_chunk(Pid, Bin)\n"
+ " append_chunk_start(Pid, RFile)\n"
+ " append_chunk_end(Pid)\n"
+ " append_bin(Pid, Bin, RFile)\n"
+ " type(Pid, Type)\n"
+ " account(Pid,Account)\n"
+ " user(Pid, User, Pass)\n"
+ " user(Pid, User, Pass,Account)\n").
+
+%%
+%% INIT
+%%
+
+-record(state, {csock = undefined, dsock = undefined, flags = undefined,
+ ldir = undefined, type = undefined, chunk = false,
+ pending = undefined}).
+
+init([Flags]) ->
+ sock_start(),
+ put(debug,get_debug(Flags)),
+ put(verbose,get_verbose(Flags)),
+ process_flag(priority, low),
+ {ok, LDir} = file:get_cwd(),
+ {ok, #state{flags = Flags, ldir = LDir}}.
+
+%%
+%% HANDLERS
+%%
+
+%% First group of reply code digits
+-define(POS_PREL, 1).
+-define(POS_COMPL, 2).
+-define(POS_INTERM, 3).
+-define(TRANS_NEG_COMPL, 4).
+-define(PERM_NEG_COMPL, 5).
+
+%% Second group of reply code digits
+-define(SYNTAX,0).
+-define(INFORMATION,1).
+-define(CONNECTION,2).
+-define(AUTH_ACC,3).
+-define(UNSPEC,4).
+-define(FILE_SYSTEM,5).
+
+
+-define(STOP_RET(E),{stop, normal, {error, E},
+ State#state{csock = undefined}}).
+
+
+rescode(?POS_PREL,_,_) -> pos_prel; %%Positive Preleminary Reply
+rescode(?POS_COMPL,_,_) -> pos_compl; %%Positive Completion Reply
+rescode(?POS_INTERM,?AUTH_ACC,2) -> pos_interm_acct; %%Positive Intermediate Reply nedd account
+rescode(?POS_INTERM,_,_) -> pos_interm; %%Positive Intermediate Reply
+rescode(?TRANS_NEG_COMPL,?FILE_SYSTEM,2) -> trans_no_space; %%No storage area no action taken
+rescode(?TRANS_NEG_COMPL,_,_) -> trans_neg_compl;%%Temporary Error, no action taken
+rescode(?PERM_NEG_COMPL,?FILE_SYSTEM,2) -> perm_no_space; %%Permanent disk space error, the user shall not try again
+rescode(?PERM_NEG_COMPL,?FILE_SYSTEM,3) -> perm_fname_not_allowed;
+rescode(?PERM_NEG_COMPL,_,_) -> perm_neg_compl.
+
+retcode(trans_no_space,_) -> etnospc;
+retcode(perm_no_space,_) -> epnospc;
+retcode(perm_fname_not_allowed,_) -> efnamena;
+retcode(_,Otherwise) -> Otherwise.
+
+handle_call({open,ip_comm,Conn_data},From,State) ->
+ case lists:keysearch(host,1,Conn_data) of
+ {value,{host,Host}}->
+ Port=get_key1(port,Conn_data,?FTP_PORT),
+ Timeout=get_key1(timeout,Conn_data,?OPEN_TIMEOUT),
+ open(Host,Port,Timeout,State);
+ false ->
+ ehost
+ end;
+
+handle_call({open,ip_comm,Host,Port},From,State) ->
+ open(Host,Port,?OPEN_TIMEOUT,State);
+
+handle_call({user, User, Pass}, _From, State) ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "USER ~s", [User]) of
+ pos_interm ->
+ case ctrl_cmd(CSock, "PASS ~s", [Pass]) of
+ pos_compl ->
+ set_type(binary, CSock),
+ {reply, ok, State#state{type = binary}};
+ {error,enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, euser}, State}
+ end;
+ pos_compl ->
+ set_type(binary, CSock),
+ {reply, ok, State#state{type = binary}};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, euser}, State}
+ end;
+
+handle_call({user, User, Pass,Acc}, _From, State) ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "USER ~s", [User]) of
+ pos_interm ->
+ case ctrl_cmd(CSock, "PASS ~s", [Pass]) of
+ pos_compl ->
+ set_type(binary, CSock),
+ {reply, ok, State#state{type = binary}};
+ pos_interm_acct->
+ case ctrl_cmd(CSock,"ACCT ~s",[Acc]) of
+ pos_compl->
+ set_type(binary, CSock),
+ {reply, ok, State#state{type = binary}};
+ {error,enotconn}->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, eacct}, State}
+ end;
+ {error,enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, euser}, State}
+ end;
+ pos_compl ->
+ set_type(binary, CSock),
+ {reply, ok, State#state{type = binary}};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, euser}, State}
+ end;
+
+%%set_account(Acc,State)->Reply
+%%Reply={reply, {error, euser}, State} | {error,enotconn}->
+handle_call({account,Acc},_From,State)->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock,"ACCT ~s",[Acc]) of
+ pos_compl->
+ {reply, ok,State};
+ {error,enotconn}->
+ ?STOP_RET(econn);
+ Error ->
+ debug(" error: ~p",[Error]),
+ {reply, {error, eacct}, State}
+ end;
+
+handle_call(pwd, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ %%
+ %% NOTE: The directory string comes over the control connection.
+ case sock_write(CSock, mk_cmd("PWD", [])) of
+ ok ->
+ {_, Line} = result_line(CSock),
+ {_, Cs} = split($", Line), % XXX Ugly
+ {Dir0, _} = split($", Cs),
+ Dir = lists:delete($", Dir0),
+ {reply, {ok, Dir}, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn)
+ end;
+
+handle_call(lpwd, _From, State) ->
+ #state{csock = CSock, ldir = LDir} = State,
+ {reply, {ok, LDir}, State};
+
+handle_call({cd, Dir}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "CWD ~s", [Dir]) of
+ pos_compl ->
+ {reply, ok, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({lcd, Dir}, _From, State) ->
+ #state{csock = CSock, ldir = LDir0} = State,
+ LDir = absname(LDir0, Dir),
+ case file:read_file_info(LDir) of
+ {ok, _ } ->
+ {reply, ok, State#state{ldir = LDir}};
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({dir, Len, Dir}, _From, State) when State#state.chunk == false ->
+ debug(" dir : ~p: ~s~n",[Len,Dir]),
+ #state{csock = CSock, type = Type} = State,
+ set_type(ascii, Type, CSock),
+ LSock = listen_data(CSock, raw),
+ Cmd = case Len of
+ short -> "NLST";
+ long -> "LIST"
+ end,
+ Result = case Dir of
+ "" ->
+ ctrl_cmd(CSock, Cmd, "");
+ _ ->
+ ctrl_cmd(CSock, Cmd ++ " ~s", [Dir])
+ end,
+ debug(" ctrl : command result: ~p~n",[Result]),
+ case Result of
+ pos_prel ->
+ debug(" dbg : await the data connection", []),
+ DSock = accept_data(LSock),
+ debug(" dbg : await the data", []),
+ Reply0 =
+ case recv_data(DSock) of
+ {ok, DirData} ->
+ debug(" data : DirData: ~p~n",[DirData]),
+ case result(CSock) of
+ pos_compl ->
+ {ok, DirData};
+ _ ->
+ {error, epath}
+ end;
+ {error, Reason} ->
+ sock_close(DSock),
+ verbose(" data : error: ~p, ~p~n",[Reason, result(CSock)]),
+ {error, epath}
+ end,
+
+ debug(" ctrl : reply: ~p~n",[Reply0]),
+ reset_type(ascii, Type, CSock),
+ {reply, Reply0, State};
+ {closed, _Why} ->
+ ?STOP_RET(econn);
+ _ ->
+ sock_close(LSock),
+ {reply, {error, epath}, State}
+ end;
+
+
+handle_call({rename, CurrFile, NewFile}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "RNFR ~s", [CurrFile]) of
+ pos_interm ->
+ case ctrl_cmd(CSock, "RNTO ~s", [NewFile]) of
+ pos_compl ->
+ {reply, ok, State};
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({delete, File}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "DELE ~s", [File]) of
+ pos_compl ->
+ {reply, ok, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({mkdir, Dir}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "MKD ~s", [Dir]) of
+ pos_compl ->
+ {reply, ok, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({rmdir, Dir}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ case ctrl_cmd(CSock, "RMD ~s", [Dir]) of
+ pos_compl ->
+ {reply, ok, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({type, Type}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ case Type of
+ ascii ->
+ set_type(ascii, CSock),
+ {reply, ok, State#state{type = ascii}};
+ binary ->
+ set_type(binary, CSock),
+ {reply, ok, State#state{type = binary}};
+ _ ->
+ {reply, {error, etype}, State}
+ end;
+
+handle_call({recv, RFile, LFile}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock, ldir = LDir} = State,
+ ALFile = case LFile of
+ "" ->
+ absname(LDir, RFile);
+ _ ->
+ absname(LDir, LFile)
+ end,
+ case file_open(ALFile, write) of
+ {ok, Fd} ->
+ LSock = listen_data(CSock, binary),
+ Ret = case ctrl_cmd(CSock, "RETR ~s", [RFile]) of
+ pos_prel ->
+ DSock = accept_data(LSock),
+ recv_file(DSock, Fd),
+ Reply0 = case result(CSock) of
+ pos_compl ->
+ ok;
+ _ ->
+ {error, epath}
+ end,
+ sock_close(DSock),
+ {reply, Reply0, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end,
+ file_close(Fd),
+ Ret;
+ {error, _What} ->
+ {reply, {error, epath}, State}
+ end;
+
+handle_call({recv_bin, RFile}, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock, ldir = LDir} = State,
+ LSock = listen_data(CSock, binary),
+ case ctrl_cmd(CSock, "RETR ~s", [RFile]) of
+ pos_prel ->
+ DSock = accept_data(LSock),
+ Reply = recv_binary(DSock,CSock),
+ sock_close(DSock),
+ {reply, Reply, State};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ _ ->
+ {reply, {error, epath}, State}
+ end;
+
+
+handle_call({recv_chunk_start, RFile}, _From, State)
+ when State#state.chunk == false ->
+ start_chunk_transfer("RETR",RFile,State);
+
+handle_call(recv_chunk, _From, State)
+ when State#state.chunk == true ->
+ do_recv_chunk(State);
+
+
+handle_call({send, LFile, RFile}, _From, State)
+ when State#state.chunk == false ->
+ transfer_file("STOR",LFile,RFile,State);
+
+handle_call({append, LFile, RFile}, _From, State)
+ when State#state.chunk == false ->
+ transfer_file("APPE",LFile,RFile,State);
+
+
+handle_call({send_bin, Bin, RFile}, _From, State)
+ when State#state.chunk == false ->
+ transfer_data("STOR",Bin,RFile,State);
+
+handle_call({append_bin, Bin, RFile}, _From, State)
+ when State#state.chunk == false ->
+ transfer_data("APPE",Bin,RFile,State);
+
+
+
+handle_call({send_chunk_start, RFile}, _From, State)
+ when State#state.chunk == false ->
+ start_chunk_transfer("STOR",RFile,State);
+
+handle_call({append_chunk_start,RFile},_From,State)
+ when State#state.chunk==false->
+ start_chunk_transfer("APPE",RFile,State);
+
+handle_call({send_chunk, Bin}, _From, State)
+ when State#state.chunk == true ->
+ chunk_transfer(Bin,State);
+
+handle_call({append_chunk, Bin}, _From, State)
+ when State#state.chunk == true ->
+ chunk_transfer(Bin,State);
+
+handle_call(append_chunk_end, _From, State)
+ when State#state.chunk == true ->
+ end_chunk_transfer(State);
+
+handle_call(send_chunk_end, _From, State)
+ when State#state.chunk == true ->
+ end_chunk_transfer(State);
+
+
+
+handle_call(close, _From, State) when State#state.chunk == false ->
+ #state{csock = CSock} = State,
+ ctrl_cmd(CSock, "QUIT", []),
+ sock_close(CSock),
+ {stop, normal, ok, State};
+
+handle_call(_, _From, State) when State#state.chunk == true ->
+ {reply, {error, echunk}, State}.
+
+
+handle_cast(Msg, State) ->
+ {noreply, State}.
+
+
+handle_info({Sock, {fromsocket, Bytes}}, State) when Sock == State#state.csock ->
+ put(leftovers, Bytes ++ leftovers()),
+ {noreply, State};
+
+%% Data connection closed (during chunk sending)
+handle_info({Sock, {socket_closed, _Reason}}, State) when Sock == State#state.dsock ->
+ {noreply, State#state{dsock = undefined}};
+
+%% Control connection closed.
+handle_info({Sock, {socket_closed, _Reason}}, State) when Sock == State#state.csock ->
+ debug(" sc : ~s~n",[leftovers()]),
+ {stop, ftp_server_close, State#state{csock = undefined}};
+
+handle_info(Info, State) ->
+ error_logger:info_msg("ftp : ~w : Unexpected message: ~w\n", [self(),Info]),
+ {noreply, State}.
+
+code_change(OldVsn,State,Extra)->
+ {ok,State}.
+
+terminate(Reason, State) ->
+ ok.
+%%
+%% OPEN CONNECTION
+%%
+open(Host,Port,Timeout,State)->
+ case sock_connect(Host,Port,Timeout) of
+ {error, What} ->
+ {stop, normal, {error, What}, State};
+ CSock ->
+ case result(CSock, State#state.flags) of
+ {error,Reason} ->
+ sock_close(CSock),
+ {stop,normal,{error,Reason},State};
+ _ -> % We should really check this...
+ {reply, {ok, self()}, State#state{csock = CSock}}
+ end
+ end.
+
+
+
+%%
+%% CONTROL CONNECTION
+%%
+
+ctrl_cmd(CSock, Fmt, Args) ->
+ Cmd = mk_cmd(Fmt, Args),
+ case sock_write(CSock, Cmd) of
+ ok ->
+ debug(" cmd : ~s",[Cmd]),
+ result(CSock);
+ {error, enotconn} ->
+ {error, enotconn};
+ Other ->
+ Other
+ end.
+
+mk_cmd(Fmt, Args) ->
+ [io_lib:format(Fmt, Args)| "\r\n"]. % Deep list ok.
+
+%%
+%% TRANSFER TYPE
+%%
+
+%%
+%% set_type(NewType, CurrType, CSock)
+%% reset_type(NewType, CurrType, CSock)
+%%
+set_type(Type, Type, CSock) ->
+ ok;
+set_type(NewType, _OldType, CSock) ->
+ set_type(NewType, CSock).
+
+reset_type(Type, Type, CSock) ->
+ ok;
+reset_type(_NewType, OldType, CSock) ->
+ set_type(OldType, CSock).
+
+set_type(ascii, CSock) ->
+ ctrl_cmd(CSock, "TYPE A", []);
+set_type(binary, CSock) ->
+ ctrl_cmd(CSock, "TYPE I", []).
+
+%%
+%% DATA CONNECTION
+%%
+
+%% Create a listen socket for a data connection and send a PORT command
+%% containing the IP address and port number. Mode is binary or raw.
+%%
+listen_data(CSock, Mode) ->
+ {IP, _} = sock_name(CSock), % IP address of control conn.
+ LSock = sock_listen(Mode, IP),
+ Port = sock_listen_port(LSock),
+ {A1, A2, A3, A4} = IP,
+ {P1, P2} = {Port div 256, Port rem 256},
+ ctrl_cmd(CSock, "PORT ~w,~w,~w,~w,~w,~w", [A1, A2, A3, A4, P1, P2]),
+ LSock.
+
+%%
+%% Accept the data connection and close the listen socket.
+%%
+accept_data(LSock) ->
+ Sock = sock_accept(LSock),
+ sock_close(LSock),
+ Sock.
+
+%%
+%% DATA COLLECTION (ls, dir)
+%%
+%% Socket is a byte stream in ASCII mode.
+%%
+
+%% Receive data (from data connection).
+recv_data(Sock) ->
+ recv_data(Sock, [], 0).
+recv_data(Sock, Sofar, ?OPER_TIMEOUT) ->
+ sock_close(Sock),
+ {ok, lists:flatten(lists:reverse(Sofar))};
+recv_data(Sock, Sofar, Retry) ->
+ case sock_read(Sock) of
+ {ok, Data} ->
+ debug(" dbg : received some data: ~n~s", [Data]),
+ recv_data(Sock, [Data| Sofar], 0);
+ {error, timeout} ->
+ %% Retry..
+ recv_data(Sock, Sofar, Retry+1);
+ {error, Reason} ->
+ SoFar1 = lists:flatten(lists:reverse(Sofar)),
+ {error, {socket_error, Reason, SoFar1, Retry}};
+ {closed, _} ->
+ {ok, lists:flatten(lists:reverse(Sofar))}
+ end.
+
+%%
+%% BINARY TRANSFER
+%%
+
+%% --------------------------------------------------
+
+%% recv_binary(DSock,CSock) = {ok,Bin} | {error,Reason}
+%%
+recv_binary(DSock,CSock) ->
+ recv_binary1(recv_binary2(DSock,[],0),CSock).
+
+recv_binary1(Reply,Sock) ->
+ case result(Sock) of
+ pos_compl -> Reply;
+ _ -> {error, epath}
+ end.
+
+recv_binary2(Sock, _Bs, ?OPER_TIMEOUT) ->
+ sock_close(Sock),
+ {error,eclosed};
+recv_binary2(Sock, Bs, Retry) ->
+ case sock_read(Sock) of
+ {ok, Bin} ->
+ recv_binary2(Sock, [Bs, Bin], 0);
+ {error, timeout} ->
+ recv_binary2(Sock, Bs, Retry+1);
+ {closed, _Why} ->
+ {ok,list_to_binary(Bs)}
+ end.
+
+%% --------------------------------------------------
+
+%%
+%% recv_chunk
+%%
+
+do_recv_chunk(#state{dsock = undefined} = State) ->
+ {reply, {error,econn}, State};
+do_recv_chunk(State) ->
+ recv_chunk1(recv_chunk2(State, 0), State).
+
+recv_chunk1({ok, _Bin} = Reply, State) ->
+ {reply, Reply, State};
+%% Reply = ok | {error, Reason}
+recv_chunk1(Reply, #state{csock = CSock} = State) ->
+ State1 = State#state{dsock = undefined, chunk = false},
+ case result(CSock) of
+ pos_compl ->
+ {reply, Reply, State1};
+ _ ->
+ {reply, {error, epath}, State1}
+ end.
+
+recv_chunk2(#state{dsock = DSock} = State, ?OPER_TIMEOUT) ->
+ sock_close(DSock),
+ {error, eclosed};
+recv_chunk2(#state{dsock = DSock} = State, Retry) ->
+ case sock_read(DSock) of
+ {ok, Bin} ->
+ {ok, Bin};
+ {error, timeout} ->
+ recv_chunk2(State, Retry+1);
+ {closed, Reason} ->
+ debug(" dbg : socket closed: ~p", [Reason]),
+ ok
+ end.
+
+
+%% --------------------------------------------------
+
+%%
+%% FILE TRANSFER
+%%
+
+recv_file(Sock, Fd) ->
+ recv_file(Sock, Fd, 0).
+
+recv_file(Sock, Fd, ?OPER_TIMEOUT) ->
+ sock_close(Sock),
+ {closed, timeout};
+recv_file(Sock, Fd, Retry) ->
+ case sock_read(Sock) of
+ {ok, Bin} ->
+ file_write(Fd, Bin),
+ recv_file(Sock, Fd);
+ {error, timeout} ->
+ recv_file(Sock, Fd, Retry+1);
+% {error, Reason} ->
+% SoFar1 = lists:flatten(lists:reverse(Sofar)),
+% exit({socket_error, Reason, Sock, SoFar1, Retry});
+ {closed, How} ->
+ {closed, How}
+ end.
+
+%%
+%% send_file(Fd, Sock) = ok | {error, Why}
+%%
+
+send_file(Fd, Sock) ->
+ {N, Bin} = file_read(Fd),
+ if
+ N > 0 ->
+ case sock_write(Sock, Bin) of
+ ok ->
+ send_file(Fd, Sock);
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ true ->
+ ok
+ end.
+
+
+
+%%
+%% PARSING OF RESULT LINES
+%%
+
+%% Excerpt from RFC 959:
+%%
+%% "A reply is defined to contain the 3-digit code, followed by Space
+%% <SP>, followed by one line of text (where some maximum line length
+%% has been specified), and terminated by the Telnet end-of-line
+%% code. There will be cases however, where the text is longer than
+%% a single line. In these cases the complete text must be bracketed
+%% so the User-process knows when it may stop reading the reply (i.e.
+%% stop processing input on the control connection) and go do other
+%% things. This requires a special format on the first line to
+%% indicate that more than one line is coming, and another on the
+%% last line to designate it as the last. At least one of these must
+%% contain the appropriate reply code to indicate the state of the
+%% transaction. To satisfy all factions, it was decided that both
+%% the first and last line codes should be the same.
+%%
+%% Thus the format for multi-line replies is that the first line
+%% will begin with the exact required reply code, followed
+%% immediately by a Hyphen, "-" (also known as Minus), followed by
+%% text. The last line will begin with the same code, followed
+%% immediately by Space <SP>, optionally some text, and the Telnet
+%% end-of-line code.
+%%
+%% For example:
+%% 123-First line
+%% Second line
+%% 234 A line beginning with numbers
+%% 123 The last line
+%%
+%% The user-process then simply needs to search for the second
+%% occurrence of the same reply code, followed by <SP> (Space), at
+%% the beginning of a line, and ignore all intermediary lines. If
+%% an intermediary line begins with a 3-digit number, the Server
+%% must pad the front to avoid confusion.
+%%
+%% This scheme allows standard system routines to be used for
+%% reply information (such as for the STAT reply), with
+%% "artificial" first and last lines tacked on. In rare cases
+%% where these routines are able to generate three digits and a
+%% Space at the beginning of any line, the beginning of each
+%% text line should be offset by some neutral text, like Space.
+%%
+%% This scheme assumes that multi-line replies may not be nested."
+
+%% We have to collect the stream of result characters into lines (ending
+%% in "\r\n"; we check for "\n"). When a line is assembled, left-over
+%% characters are saved in the process dictionary.
+%%
+
+%% result(Sock) = rescode()
+%%
+result(Sock) ->
+ result(Sock, false).
+
+result_line(Sock) ->
+ result(Sock, true).
+
+%% result(Sock, Bool) = {error,Reason} | rescode() | {rescode(), Lines}
+%% Printout if Bool = true.
+%%
+result(Sock, RetForm) ->
+ case getline(Sock) of
+ Line when length(Line) > 3 ->
+ [D1, D2, D3| Tail] = Line,
+ case Tail of
+ [$-| _] ->
+ parse_to_end(Sock, [D1, D2, D3, $ ]); % 3 digits + space
+ _ ->
+ ok
+ end,
+ result(D1,D2,D3,Line,RetForm);
+ _ ->
+ retform(rescode(?PERM_NEG_COMPL,-1,-1),[],RetForm)
+ end.
+
+result(D1,_D2,_D3,Line,_RetForm) when D1 - $0 > 10 ->
+ {error,{invalid_server_response,Line}};
+result(D1,_D2,_D3,Line,_RetForm) when D1 - $0 < 0 ->
+ {error,{invalid_server_response,Line}};
+result(D1,D2,D3,Line,RetForm) ->
+ Res1 = D1 - $0,
+ Res2 = D2 - $0,
+ Res3 = D3 - $0,
+ verbose(" ~w : ~s", [Res1, Line]),
+ retform(rescode(Res1,Res2,Res3),Line,RetForm).
+
+retform(ResCode,Line,true) ->
+ {ResCode,Line};
+retform(ResCode,_,_) ->
+ ResCode.
+
+leftovers() ->
+ case get(leftovers) of
+ undefined -> [];
+ X -> X
+ end.
+
+%% getline(Sock) = Line
+%%
+getline(Sock) ->
+ getline(Sock, leftovers()).
+
+getline(Sock, Rest) ->
+ getline1(Sock, split($\n, Rest), 0).
+
+getline1(Sock, {[], Rest}, ?OPER_TIMEOUT) ->
+ sock_close(Sock),
+ put(leftovers, Rest),
+ [];
+getline1(Sock, {[], Rest}, Retry) ->
+ case sock_read(Sock) of
+ {ok, More} ->
+ debug(" read : ~s~n",[More]),
+ getline(Sock, Rest ++ More);
+ {error, timeout} ->
+ %% Retry..
+ getline1(Sock, {[], Rest}, Retry+1);
+ Error ->
+ put(leftovers, Rest),
+ []
+ end;
+getline1(Sock, {Line, Rest}, Retry) ->
+ put(leftovers, Rest),
+ Line.
+
+parse_to_end(Sock, Prefix) ->
+ Line = getline(Sock),
+ case lists:prefix(Prefix, Line) of
+ false ->
+ parse_to_end(Sock, Prefix);
+ true ->
+ ok
+ end.
+
+
+%% Split list after first occurence of S.
+%% Returns {Prefix, Suffix} ({[], Cs} if S not found).
+split(S, Cs) ->
+ split(S, Cs, []).
+
+split(S, [S| Cs], As) ->
+ {lists:reverse([S|As]), Cs};
+split(S, [C| Cs], As) ->
+ split(S, Cs, [C| As]);
+split(_, [], As) ->
+ {[], lists:reverse(As)}.
+
+%%
+%% FILE INTERFACE
+%%
+%% All files are opened raw in binary mode.
+%%
+-define(BUFSIZE, 4096).
+
+file_open(File, Option) ->
+ file:open(File, [raw, binary, Option]).
+
+file_close(Fd) ->
+ file:close(Fd).
+
+
+file_read(Fd) -> % Compatible with pre R2A.
+ case file:read(Fd, ?BUFSIZE) of
+ {ok, {N, Bytes}} ->
+ {N, Bytes};
+ {ok, Bytes} ->
+ {size(Bytes), Bytes};
+ eof ->
+ {0, []}
+ end.
+
+file_write(Fd, Bytes) ->
+ file:write(Fd, Bytes).
+
+absname(Dir, File) -> % Args swapped.
+ filename:absname(File, Dir).
+
+
+
+%% sock_start()
+%%
+
+%%
+%% USE GEN_TCP
+%%
+
+sock_start() ->
+ inet_db:start().
+
+%%
+%% Connect to FTP server at Host (default is TCP port 21) in raw mode,
+%% in order to establish a control connection.
+%%
+
+sock_connect(Host,Port,TimeOut) ->
+ debug(" info : connect to server on ~p:~p~n",[Host,Port]),
+ Opts = [{packet, 0}, {active, false}],
+ case (catch gen_tcp:connect(Host, Port, Opts,TimeOut)) of
+ {'EXIT', R1} -> % XXX Probably no longer needed.
+ debug(" error: socket connectionn failed with exit reason:"
+ "~n ~p",[R1]),
+ {error, ehost};
+ {error, R2} ->
+ debug(" error: socket connectionn failed with exit reason:"
+ "~n ~p",[R2]),
+ {error, ehost};
+ {ok, Sock} ->
+ Sock
+ end.
+
+%%
+%% Create a listen socket (any port) in binary or raw non-packet mode for
+%% data connection.
+%%
+sock_listen(Mode, IP) ->
+ Opts = case Mode of
+ binary ->
+ [binary, {packet, 0}];
+ raw ->
+ [{packet, 0}]
+ end,
+ {ok, Sock} = gen_tcp:listen(0, [{ip, IP}, {active, false} | Opts]),
+ Sock.
+
+sock_accept(LSock) ->
+ {ok, Sock} = gen_tcp:accept(LSock),
+ Sock.
+
+sock_close(undefined) ->
+ ok;
+sock_close(Sock) ->
+ gen_tcp:close(Sock).
+
+sock_read(Sock) ->
+ case gen_tcp:recv(Sock, 0, ?BYTE_TIMEOUT) of
+ {ok, Bytes} ->
+ {ok, Bytes};
+
+ {error, closed} ->
+ {closed, closed}; % Yes
+
+ %% --- OTP-4770 begin ---
+ %%
+ %% This seems to happen on windows
+ %% "Someone" tried to close an already closed socket...
+ %%
+
+ {error, enotsock} ->
+ {closed, enotsock};
+
+ %%
+ %% --- OTP-4770 end ---
+
+ {error, etimedout} ->
+ {error, timeout};
+
+ Other ->
+ Other
+ end.
+
+%% receive
+%% {tcp, Sock, Bytes} ->
+%% {ok, Bytes};
+%% {tcp_closed, Sock} ->
+%% {closed, closed}
+%% end.
+
+sock_write(Sock, Bytes) ->
+ gen_tcp:send(Sock, Bytes).
+
+sock_name(Sock) ->
+ {ok, {IP, Port}} = inet:sockname(Sock),
+ {IP, Port}.
+
+sock_listen_port(LSock) ->
+ {ok, Port} = inet:port(LSock),
+ Port.
+
+
+%%
+%% ERROR STRINGS
+%%
+errstr({error, Reason}) ->
+ errstr(Reason);
+
+errstr(echunk) -> "Synchronisation error during chung sending.";
+errstr(eclosed) -> "Session has been closed.";
+errstr(econn) -> "Connection to remote server prematurely closed.";
+errstr(eexists) ->"File or directory already exists.";
+errstr(ehost) -> "Host not found, FTP server not found, "
+"or connection rejected.";
+errstr(elogin) -> "User not logged in.";
+errstr(enotbinary) -> "Term is not a binary.";
+errstr(epath) -> "No such file or directory, already exists, "
+"or permission denied.";
+errstr(etype) -> "No such type.";
+errstr(euser) -> "User name or password not valid.";
+errstr(etnospc) -> "Insufficient storage space in system.";
+errstr(epnospc) -> "Exceeded storage allocation "
+"(for current directory or dataset).";
+errstr(efnamena) -> "File name not allowed.";
+errstr(Reason) ->
+ lists:flatten(io_lib:format("Unknown error: ~w", [Reason])).
+
+
+
+%% ----------------------------------------------------------
+
+get_verbose(Params) -> check_param(verbose,Params).
+
+get_debug(Flags) -> check_param(debug,Flags).
+
+check_param(P,Ps) -> lists:member(P,Ps).
+
+
+%% verbose -> ok
+%%
+%% Prints the string if the Flags list is non-epmty
+%%
+%% Params: F Format string
+%% A Arguments to the format string
+%%
+verbose(F,A) -> verbose(get(verbose),F,A).
+
+verbose(true,F,A) -> print(F,A);
+verbose(_,_F,_A) -> ok.
+
+
+
+
+%% debug -> ok
+%%
+%% Prints the string if debug enabled
+%%
+%% Params: F Format string
+%% A Arguments to the format string
+%%
+debug(F,A) -> debug(get(debug),F,A).
+
+debug(true,F,A) -> print(F,A);
+debug(_,_F,_A) -> ok.
+
+
+print(F,A) -> io:format(F,A).
+
+
+
+transfer_file(Cmd,LFile,RFile,State)->
+ #state{csock = CSock, ldir = LDir} = State,
+ ARFile = case RFile of
+ "" ->
+ LFile;
+ _ ->
+ RFile
+ end,
+ ALFile = absname(LDir, LFile),
+ case file_open(ALFile, read) of
+ {ok, Fd} ->
+ LSock = listen_data(CSock, binary),
+ case ctrl_cmd(CSock, "~s ~s", [Cmd,ARFile]) of
+ pos_prel ->
+ DSock = accept_data(LSock),
+ SFreply = send_file(Fd, DSock),
+ file_close(Fd),
+ sock_close(DSock),
+ case {SFreply,result(CSock)} of
+ {ok,pos_compl} ->
+ {reply, ok, State};
+ {ok,Other} ->
+ debug(" error: unknown reply: ~p~n",[Other]),
+ {reply, {error, epath}, State};
+ {{error,Why},Result} ->
+ ?STOP_RET(retcode(Result,econn))
+ end;
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ Other ->
+ debug(" error: ctrl failed: ~p~n",[Other]),
+ {reply, {error, epath}, State}
+ end;
+ {error, Reason} ->
+ debug(" error: file open: ~p~n",[Reason]),
+ {reply, {error, epath}, State}
+ end.
+
+transfer_data(Cmd,Bin,RFile,State)->
+ #state{csock = CSock, ldir = LDir} = State,
+ LSock = listen_data(CSock, binary),
+ case ctrl_cmd(CSock, "~s ~s", [Cmd,RFile]) of
+ pos_prel ->
+ DSock = accept_data(LSock),
+ SReply = sock_write(DSock, Bin),
+ sock_close(DSock),
+ case {SReply,result(CSock)} of
+ {ok,pos_compl} ->
+ {reply, ok, State};
+ {ok,trans_no_space} ->
+ ?STOP_RET(etnospc);
+ {ok,perm_no_space} ->
+ ?STOP_RET(epnospc);
+ {ok,perm_fname_not_allowed} ->
+ ?STOP_RET(efnamena);
+ {ok,Other} ->
+ debug(" error: unknown reply: ~p~n",[Other]),
+ {reply, {error, epath}, State};
+ {{error,Why},Result} ->
+ ?STOP_RET(retcode(Result,econn))
+ %% {{error,_Why},_Result} ->
+ %% ?STOP_RET(econn)
+ end;
+
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+
+ Other ->
+ debug(" error: ctrl failed: ~p~n",[Other]),
+ {reply, {error, epath}, State}
+ end.
+
+
+start_chunk_transfer(Cmd, RFile, #state{csock = CSock} = State) ->
+ LSock = listen_data(CSock, binary),
+ case ctrl_cmd(CSock, "~s ~s", [Cmd,RFile]) of
+ pos_prel ->
+ DSock = accept_data(LSock),
+ {reply, ok, State#state{dsock = DSock, chunk = true}};
+ {error, enotconn} ->
+ ?STOP_RET(econn);
+ Otherwise ->
+ debug(" error: ctrl failed: ~p~n",[Otherwise]),
+ {reply, {error, epath}, State}
+ end.
+
+
+chunk_transfer(Bin,State)->
+ #state{dsock = DSock, csock = CSock} = State,
+ case DSock of
+ undefined ->
+ {reply,{error,econn},State};
+ _ ->
+ case sock_write(DSock, Bin) of
+ ok ->
+ {reply, ok, State};
+ Other ->
+ debug(" error: chunk write error: ~p~n",[Other]),
+ {reply, {error, econn}, State#state{dsock = undefined}}
+ end
+ end.
+
+
+
+end_chunk_transfer(State)->
+ #state{csock = CSock, dsock = DSock} = State,
+ case DSock of
+ undefined ->
+ Result = result(CSock),
+ case Result of
+ pos_compl ->
+ {reply,ok,State#state{dsock = undefined,
+ chunk = false}};
+ trans_no_space ->
+ ?STOP_RET(etnospc);
+ perm_no_space ->
+ ?STOP_RET(epnospc);
+ perm_fname_not_allowed ->
+ ?STOP_RET(efnamena);
+ Result ->
+ debug(" error: send chunk end (1): ~p~n",
+ [Result]),
+ {reply,{error,epath},State#state{dsock = undefined,
+ chunk = false}}
+ end;
+ _ ->
+ sock_close(DSock),
+ Result = result(CSock),
+ case Result of
+ pos_compl ->
+ {reply,ok,State#state{dsock = undefined,
+ chunk = false}};
+ trans_no_space ->
+ sock_close(CSock),
+ ?STOP_RET(etnospc);
+ perm_no_space ->
+ sock_close(CSock),
+ ?STOP_RET(epnospc);
+ perm_fname_not_allowed ->
+ sock_close(CSock),
+ ?STOP_RET(efnamena);
+ Result ->
+ debug(" error: send chunk end (2): ~p~n",
+ [Result]),
+ {reply,{error,epath},State#state{dsock = undefined,
+ chunk = false}}
+ end
+ end.
+
+get_key1(Key,List,Default)->
+ case lists:keysearch(Key,1,List)of
+ {value,{_,Val}}->
+ Val;
+ false->
+ Default
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.erl
new file mode 100644
index 0000000000..764e7fb092
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.erl
@@ -0,0 +1,260 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+
+%%% This version of the HTTP/1.1 client implements:
+%%% - RFC 2616 HTTP 1.1 client part
+%%% - RFC 2817 Upgrading to TLS Within HTTP/1.1 (not yet!)
+%%% - RFC 2818 HTTP Over TLS
+%%% - RFC 3229 Delta encoding in HTTP (not yet!)
+%%% - RFC 3230 Instance Digests in HTTP (not yet!)
+%%% - RFC 3310 Authentication and Key Agreement (AKA) (not yet!)
+%%% - HTTP/1.1 Specification Errata found at
+%%% http://world.std.com/~lawrence/http_errata.html
+%%% Additionaly follows the following recommendations:
+%%% - RFC 3143 Known HTTP Proxy/Caching Problems (not yet!)
+%%% - draft-nottingham-hdrreg-http-00.txt (not yet!)
+%%%
+%%% Depends on
+%%% - uri.erl for all URL parsing (except what is handled by the C driver)
+%%% - http_lib.erl for all parsing of body and headers
+%%%
+%%% Supported Settings are:
+%%% http_timeout % (int) Milliseconds before a request times out
+%%% http_useproxy % (bool) True if a proxy should be used
+%%% http_proxy % (string) Proxy
+%%% http_noproxylist % (list) List with hosts not requiring proxy
+%%% http_autoredirect % (bool) True if automatic redirection on 30X responses
+%%% http_ssl % (list) SSL settings. A non-empty list enables SSL/TLS
+%%% support in the HTTP client
+%%% http_pipelinesize % (int) Length of pipeline. 1 means no pipeline.
+%%% Only has effect when initiating a new session.
+%%% http_sessions % (int) Max number of open sessions for {Addr,Port}
+%%%
+%%% TODO: (Known bugs!)
+%% - Cache handling
+%% - Doesn't handle a bunch of entity headers properly
+%% - Better handling of status codes different from 200,30X and 50X
+%% - Many of the settings above are not implemented!
+%% - close_session/2 and cancel_request/1 doesn't work
+%% - Variable pipe size.
+%% - Due to the fact that inet_drv only has a single timer, the timeouts given
+%% for pipelined requests are not ok (too long)
+%%
+%% Note:
+%% - Some servers (e.g. Microsoft-IIS/5.0) may sometimes not return a proper
+%% 'Location' header on a redirect.
+%% The client will fail with {error,no_scheme} in these cases.
+
+-module(http).
+-author("[email protected]").
+
+-export([start/0,
+ request/3,request/4,cancel_request/1,
+ request_sync/2,request_sync/3]).
+
+-include("http.hrl").
+-include("jnets_httpd.hrl").
+
+-define(START_OPTIONS,[]).
+
+%%% HTTP Client manager. Used to store open connections.
+%%% Will be started automatically unless started explicitly.
+start() ->
+ application:start(ssl),
+ httpc_manager:start().
+
+%%% Asynchronous HTTP request that spawns a handler.
+%%% Method HTTPReq
+%%% options,get,head,delete,trace = {Url,Headers}
+%%% post,put = {Url,Headers,ContentType,Body}
+%%% where Url is a {Scheme,Host,Port,PathQuery} tuple, as returned by uri.erl
+%%%
+%%% Returns: {ok,ReqId} |
+%%% {error,Reason}
+%%% If {ok,Pid} was returned, the handler will return with
+%%% gen_server:cast(From,{Ref,ReqId,{error,Reason}}) |
+%%% gen_server:cast(From,{Ref,ReqId,{Status,Headers,Body}})
+%%% where Reason is an atom and Headers a #res_headers{} record
+%%% http:format_error(Reason) gives a more informative description.
+%%%
+%%% Note:
+%%% - Always try to find an open connection to a given host and port, and use
+%%% the associated socket.
+%%% - Unless a 'Connection: close' header is provided don't close the socket
+%%% after a response is given
+%%% - A given Pid, found in the database, might be terminated before the
+%%% message is sent to the Pid. This will happen e.g., if the connection is
+%%% closed by the other party and there are no pending requests.
+%%% - The HTTP connection process is spawned, if necessary, in
+%%% httpc_manager:add_connection/4
+request(Ref,Method,HTTPReqCont) ->
+ request(Ref,Method,HTTPReqCont,[],self()).
+
+request(Ref,Method,HTTPReqCont,Settings) ->
+ request(Ref,Method,HTTPReqCont,Settings,self()).
+
+request(Ref,Method,{{Scheme,Host,Port,PathQuery},
+ Headers,ContentType,Body},Settings,From) ->
+ case create_settings(Settings,#client_settings{}) of
+ {error,Reason} ->
+ {error,Reason};
+ CS ->
+ case create_headers(Headers,#req_headers{}) of
+ {error,Reason} ->
+ {error,Reason};
+ H ->
+ Req=#request{ref=Ref,from=From,
+ scheme=Scheme,address={Host,Port},
+ pathquery=PathQuery,method=Method,
+ headers=H,content={ContentType,Body},
+ settings=CS},
+ httpc_manager:request(Req)
+ end
+ end;
+request(Ref,Method,{Url,Headers},Settings,From) ->
+ request(Ref,Method,{Url,Headers,[],[]},Settings,From).
+
+%%% Cancels requests identified with ReqId.
+%%% FIXME! Doesn't work...
+cancel_request(ReqId) ->
+ httpc_manager:cancel_request(ReqId).
+
+%%% Close all sessions currently open to Host:Port
+%%% FIXME! Doesn't work...
+close_session(Host,Port) ->
+ httpc_manager:close_session(Host,Port).
+
+
+%%% Synchronous HTTP request that waits until a response is created
+%%% (e.g. successfull reply or timeout)
+%%% Method HTTPReq
+%%% options,get,head,delete,trace = {Url,Headers}
+%%% post,put = {Url,Headers,ContentType,Body}
+%%% where Url is a string() or a {Scheme,Host,Port,PathQuery} tuple
+%%%
+%%% Returns: {Status,Headers,Body} |
+%%% {error,Reason}
+%%% where Reason is an atom.
+%%% http:format_error(Reason) gives a more informative description.
+request_sync(Method,HTTPReqCont) ->
+ request_sync(Method,HTTPReqCont,[]).
+
+request_sync(Method,{Url,Headers},Settings)
+ when Method==options;Method==get;Method==head;Method==delete;Method==trace ->
+ case uri:parse(Url) of
+ {error,Reason} ->
+ {error,Reason};
+ ParsedUrl ->
+ request_sync(Method,{ParsedUrl,Headers,[],[]},Settings,0)
+ end;
+request_sync(Method,{Url,Headers,ContentType,Body},Settings)
+ when Method==post;Method==put ->
+ case uri:parse(Url) of
+ {error,Reason} ->
+ {error,Reason};
+ ParsedUrl ->
+ request_sync(Method,{ParsedUrl,Headers,ContentType,Body},Settings,0)
+ end;
+request_sync(Method,Request,Settings) ->
+ {error,bad_request}.
+
+request_sync(Method,HTTPCont,Settings,_Redirects) ->
+ case request(request_sync,Method,HTTPCont,Settings,self()) of
+ {ok,_ReqId} ->
+ receive
+ {'$gen_cast',{request_sync,_ReqId2,{Status,Headers,Body}}} ->
+ {Status,pp_headers(Headers),binary_to_list(Body)};
+ {'$gen_cast',{request_sync,_ReqId2,{error,Reason}}} ->
+ {error,Reason};
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+
+create_settings([],Out) ->
+ Out;
+create_settings([{http_timeout,Val}|Settings],Out) ->
+ create_settings(Settings,Out#client_settings{timeout=Val});
+create_settings([{http_useproxy,Val}|Settings],Out) ->
+ create_settings(Settings,Out#client_settings{useproxy=Val});
+create_settings([{http_proxy,Val}|Settings],Out) ->
+ create_settings(Settings,Out#client_settings{proxy=Val});
+create_settings([{http_noproxylist,Val}|Settings],Out) ->
+ create_settings(Settings,Out#client_settings{noproxylist=Val});
+create_settings([{http_autoredirect,Val}|Settings],Out) ->
+ create_settings(Settings,Out#client_settings{autoredirect=Val});
+create_settings([{http_ssl,Val}|Settings],Out) ->
+ create_settings(Settings,Out#client_settings{ssl=Val});
+create_settings([{http_pipelinesize,Val}|Settings],Out)
+ when integer(Val),Val>0 ->
+ create_settings(Settings,Out#client_settings{max_quelength=Val});
+create_settings([{http_sessions,Val}|Settings],Out)
+ when integer(Val),Val>0 ->
+ create_settings(Settings,Out#client_settings{max_sessions=Val});
+create_settings([{Key,_Val}|_Settings],_Out) ->
+ io:format("ERROR bad settings, got ~p~n",[Key]),
+ {error,bad_settings}.
+
+
+create_headers([],Req) ->
+ Req;
+create_headers([{Key,Val}|Rest],Req) ->
+ case httpd_util:to_lower(Key) of
+ "expect" ->
+ create_headers(Rest,Req#req_headers{expect=Val});
+ OtherKey ->
+ create_headers(Rest,
+ Req#req_headers{other=[{OtherKey,Val}|
+ Req#req_headers.other]})
+ end.
+
+
+pp_headers(#res_headers{connection=Connection,
+ transfer_encoding=Transfer_encoding,
+ retry_after=Retry_after,
+ content_length=Content_length,
+ content_type=Content_type,
+ location=Location,
+ other=Other}) ->
+ H1=case Connection of
+ undefined -> [];
+ _ -> [{'Connection',Connection}]
+ end,
+ H2=case Transfer_encoding of
+ undefined -> [];
+ _ -> [{'Transfer-Encoding',Transfer_encoding}]
+ end,
+ H3=case Retry_after of
+ undefined -> [];
+ _ -> [{'Retry-After',Retry_after}]
+ end,
+ H4=case Location of
+ undefined -> [];
+ _ -> [{'Location',Location}]
+ end,
+ HCL=case Content_length of
+ "0" -> [];
+ _ -> [{'Content-Length',Content_length}]
+ end,
+ HCT=case Content_type of
+ undefined -> [];
+ _ -> [{'Content-Type',Content_type}]
+ end,
+ H1++H2++H3++H4++HCL++HCT++Other.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.hrl
new file mode 100644
index 0000000000..f10ca47a9a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http.hrl
@@ -0,0 +1,127 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+
+-define(HTTP_REQUEST_TIMEOUT, 5000).
+-define(PIPELINE_LENGTH,3).
+-define(OPEN_SESSIONS,400).
+
+
+%%% FIXME! These definitions should probably be possible to defined via
+%%% user settings
+-define(MAX_REDIRECTS, 4).
+
+
+%%% Note that if not persitent the connection can be closed immediately on a
+%%% response, because new requests are not sent to this connection process.
+%%% address, % ({Host,Port}) Destination Host and Port
+-record(session,{
+ id, % (int) Session Id identifies session in http_manager
+ clientclose, % (bool) true if client requested "close" connection
+ scheme, % (atom) http (HTTP/TCP) or https (TCP/SSL/TCP)
+ socket, % (socket) Open socket, used by connection
+ pipeline=[], % (list) Sent requests, not yet taken care of by the
+ % associated http_responder.
+ quelength=1, % (int) Current length of pipeline (1 when created)
+ max_quelength% (int) Max pipeline length
+ }).
+
+%%% [{Pid,RequestQue,QueLength},...] list where
+%%% - RequestQue (implemented with a list) contains sent requests that
+%%% has not yet received a response (pipelined) AND is not currently
+%%% handled (awaiting data) by the session process.
+%%% - QueLength is the length of this que, but
+
+%%% Response headers
+-record(res_headers,{
+%%% --- Standard "General" headers
+% cache_control,
+ connection,
+% date,
+% pragma,
+% trailer,
+ transfer_encoding,
+% upgrade,
+% via,
+% warning,
+%%% --- Standard "Request" headers
+% accept_ranges,
+% age,
+% etag,
+ location,
+% proxy_authenticate,
+ retry_after,
+% server,
+% vary,
+% www_authenticate,
+%%% --- Standard "Entity" headers
+% allow,
+% content_encoding,
+% content_language,
+ content_length="0",
+% content_location,
+% content_md5,
+% content_range,
+ content_type,
+% expires,
+% last_modified,
+ other=[] % (list) Key/Value list with other headers
+ }).
+
+%%% All data associated to a specific HTTP request
+-record(request,{
+ id, % (int) Request Id
+ ref, % Caller specific
+ from, % (pid) Caller
+ redircount=0,% (int) Number of redirects made for this request
+ scheme, % (http|https) (HTTP/TCP) or (TCP/SSL/TCP) connection
+ address, % ({Host,Port}) Destination Host and Port
+ pathquery, % (string) Rest of parsed URL
+ method, % (atom) HTTP request Method
+ headers, % (list) Key/Value list with Headers
+ content, % ({ContentType,Body}) Current HTTP request
+ settings % (#client_settings{}) User defined settings
+ }).
+
+-record(response,{
+ scheme, % (atom) http (HTTP/TCP) or https (TCP/SSL/TCP)
+ socket, % (socket) Open socket, used by connection
+ status,
+ http_version,
+ headers=#res_headers{},
+ body = <<>>
+ }).
+
+
+
+
+%%% HTTP Client settings
+-record(client_settings,{
+ timeout=?HTTP_REQUEST_TIMEOUT,
+ % (int) Milliseconds before a request times out
+ useproxy=false, % (bool) True if the proxy should be used
+ proxy=undefined, % (tuple) Parsed Proxy URL
+ noproxylist=[], % (list) List with hosts not requiring proxy
+ autoredirect=true, % (bool) True if automatic redirection on 30X
+ % responses.
+ max_sessions=?OPEN_SESSIONS,% (int) Max open sessions for any Adr,Port
+ max_quelength=?PIPELINE_LENGTH, % (int) Max pipeline length
+% ssl=[{certfile,"/jb/server_root/ssl/ssl_client.pem"},
+% {keyfile,"/jb/server_root/ssl/ssl_client.pem"},
+% {verify,0}]
+ ssl=false % (list) SSL settings. A non-empty list enables SSL/TLS
+ % support in the HTTP client
+ }).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http_lib.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http_lib.erl
new file mode 100644
index 0000000000..eb8d7d66b1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/http_lib.erl
@@ -0,0 +1,745 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+%%% File : http_lib.erl
+%%% Author : Johan Blom <[email protected]>
+%%% Description : Generic, HTTP specific helper functions
+%%% Created : 4 Mar 2002 by Johan Blom
+
+%%% TODO
+%%% - Check if I need to anything special when parsing
+%%% "Content-Type:multipart/form-data"
+
+-module(http_lib).
+-author("[email protected]").
+
+-include("http.hrl").
+-include("jnets_httpd.hrl").
+
+-export([connection_close/1,
+ accept/3,deliver/3,recv/4,recv0/3,
+ connect/1,send/3,close/2,controlling_process/3,setopts/3,
+ getParameterValue/2,
+% get_var/2,
+ create_request_line/3]).
+
+-export([read_client_headers/2,read_server_headers/2,
+ get_auth_data/1,create_header_list/1,
+ read_client_body/2,read_client_multipartrange_body/3,
+ read_server_body/2]).
+
+
+%%% Server response:
+%%% Check "Connection" header if server requests session to be closed.
+%%% No 'close' means returns false
+%%% Client Request:
+%%% Check if 'close' in request headers
+%%% Only care about HTTP 1.1 clients!
+connection_close(Headers) when record(Headers,req_headers) ->
+ case Headers#req_headers.connection of
+ "close" ->
+ true;
+ "keep-alive" ->
+ false;
+ Value when list(Value) ->
+ true;
+ _ ->
+ false
+ end;
+connection_close(Headers) when record(Headers,res_headers) ->
+ case Headers#res_headers.connection of
+ "close" ->
+ true;
+ "keep-alive" ->
+ false;
+ Value when list(Value) ->
+ true;
+ _ ->
+ false
+ end.
+
+
+%% =============================================================================
+%%% Debugging:
+
+% format_time(TS) ->
+% {_,_,MicroSecs}=TS,
+% {{Y,Mon,D},{H,M,S}}=calendar:now_to_universal_time(TS),
+% lists:flatten(io_lib:format("~4.4.0w-~2.2.0w-~2.2.0w,~2.2.0w:~2.2.0w:~6.3.0f",
+% [Y,Mon,D,H,M,S+(MicroSecs/1000000)])).
+
+%% Time in milli seconds
+% t() ->
+% {A,B,C} = erlang:now(),
+% A*1000000000+B*1000+(C div 1000).
+
+% sz(L) when list(L) ->
+% length(L);
+% sz(B) when binary(B) ->
+% size(B);
+% sz(O) ->
+% {unknown_size,O}.
+
+
+%% =============================================================================
+
+getHeaderValue(_Attr,[]) ->
+ [];
+getHeaderValue(Attr,[{Attr,Value}|_Rest]) ->
+ Value;
+getHeaderValue(Attr,[_|Rest]) ->
+ getHeaderValue(Attr,Rest).
+
+getParameterValue(_Attr,undefined) ->
+ undefined;
+getParameterValue(Attr,List) ->
+ case lists:keysearch(Attr,1,List) of
+ {value,{Attr,Val}} ->
+ Val;
+ _ ->
+ undefined
+ end.
+
+create_request_line(Method,Path,{Major,Minor}) ->
+ [atom_to_list(Method)," ",Path,
+ " HTTP/",integer_to_list(Major),".",integer_to_list(Minor)];
+create_request_line(Method,Path,Minor) ->
+ [atom_to_list(Method)," ",Path," HTTP/1.",integer_to_list(Minor)].
+
+
+%%% ============================================================================
+read_client_headers(Info,Timeout) ->
+ Headers=read_response_h(Info#response.scheme,Info#response.socket,Timeout,
+ Info#response.headers),
+ Info#response{headers=Headers}.
+
+read_server_headers(Info,Timeout) ->
+ Headers=read_request_h(Info#mod.socket_type,Info#mod.socket,Timeout,
+ Info#mod.headers),
+ Info#mod{headers=Headers}.
+
+
+%% Parses the header of a HTTP request and returns a key,value tuple
+%% list containing Name and Value of each header directive as of:
+%%
+%% Content-Type: multipart/mixed -> {"Content-Type", "multipart/mixed"}
+%%
+%% But in http/1.1 the field-names are case insencitive so now it must be
+%% Content-Type: multipart/mixed -> {"content-type", "multipart/mixed"}
+%% The standard furthermore says that leading and traling white space
+%% is not a part of the fieldvalue and shall therefore be removed.
+read_request_h(SType,S,Timeout,H) ->
+ case recv0(SType,S,Timeout) of
+ {ok,{http_header,_,'Connection',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{connection=Value});
+ {ok,{http_header,_,'Content-Type',_,Val}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{content_type=Val});
+ {ok,{http_header,_,'Host',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{host=Value});
+ {ok,{http_header,_,'Content-Length',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{content_length=Value});
+% {ok,{http_header,_,'Expect',_,Value}} -> % FIXME! Update inet_drv.c!!
+% read_request_h(SType,S,Timeout,H#req_headers{expect=Value});
+ {ok,{http_header,_,'Transfer-Encoding',_,V}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{transfer_encoding=V});
+ {ok,{http_header,_,'Authorization',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{authorization=Value});
+ {ok,{http_header,_,'User-Agent',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{user_agent=Value});
+ {ok,{http_header,_,'Range',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{range=Value});
+ {ok,{http_header,_,'If-Range',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{if_range=Value});
+ {ok,{http_header,_,'If-Match',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{if_match=Value});
+ {ok,{http_header,_,'If-None-Match',_,Value}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{if_none_match=Value});
+ {ok,{http_header,_,'If-Modified-Since',_,V}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{if_modified_since=V});
+ {ok,{http_header,_,'If-Unmodified-Since',_,V}} ->
+ read_request_h(SType,S,Timeout,H#req_headers{if_unmodified_since=V});
+ {ok,{http_header,_,K,_,V}} ->
+ read_request_h(SType,S,Timeout,
+ H#req_headers{other=H#req_headers.other++[{K,V}]});
+ {ok,http_eoh} ->
+ H;
+ {error, timeout} when SType==http ->
+ throw({error, session_local_timeout});
+ {error, etimedout} when SType==https ->
+ throw({error, session_local_timeout});
+ {error, Reason} when Reason==closed;Reason==enotconn ->
+ throw({error, session_remotely_closed});
+ {error, Reason} ->
+ throw({error,Reason})
+ end.
+
+
+read_response_h(SType,S,Timeout,H) ->
+ case recv0(SType,S,Timeout) of
+ {ok,{http_header,_,'Connection',_,Val}} ->
+ read_response_h(SType,S,Timeout,H#res_headers{connection=Val});
+ {ok,{http_header,_,'Content-Length',_,Val}} ->
+ read_response_h(SType,S,Timeout,H#res_headers{content_length=Val});
+ {ok,{http_header,_,'Content-Type',_,Val}} ->
+ read_response_h(SType,S,Timeout,H#res_headers{content_type=Val});
+ {ok,{http_header,_,'Transfer-Encoding',_,V}} ->
+ read_response_h(SType,S,Timeout,H#res_headers{transfer_encoding=V});
+ {ok,{http_header,_,'Location',_,V}} ->
+ read_response_h(SType,S,Timeout,H#res_headers{location=V});
+ {ok,{http_header,_,'Retry-After',_,V}} ->
+ read_response_h(SType,S,Timeout,H#res_headers{retry_after=V});
+ {ok,{http_header,_,K,_,V}} ->
+ read_response_h(SType,S,Timeout,
+ H#res_headers{other=H#res_headers.other++[{K,V}]});
+ {ok,http_eoh} ->
+ H;
+ {error, timeout} when SType==http ->
+ throw({error, session_local_timeout});
+ {error, etimedout} when SType==https ->
+ throw({error, session_local_timeout});
+ {error, Reason} when Reason==closed;Reason==enotconn ->
+ throw({error, session_remotely_closed});
+ {error, Reason} ->
+ throw({error,Reason})
+ end.
+
+
+%%% Got the headers, and maybe a part of the body, now read in the rest
+%%% Note:
+%%% - No need to check for Expect header if client
+%%% - Currently no support for setting MaxHeaderSize in client, set to
+%%% unlimited.
+%%% - Move to raw packet mode as we are finished with HTTP parsing
+read_client_body(Info,Timeout) ->
+ Headers=Info#response.headers,
+ case Headers#res_headers.transfer_encoding of
+ "chunked" ->
+ ?DEBUG("read_entity_body2()->"
+ "Transfer-encoding:Chunked Data:",[]),
+ read_client_chunked_body(Info,Timeout,?MAXBODYSIZE);
+ Encoding when list(Encoding) ->
+ ?DEBUG("read_entity_body2()->"
+ "Transfer-encoding:Unknown",[]),
+ throw({error,unknown_coding});
+ _ ->
+ ContLen=list_to_integer(Headers#res_headers.content_length),
+ if
+ ContLen>?MAXBODYSIZE ->
+ throw({error,body_too_big});
+ true ->
+ ?DEBUG("read_entity_body2()->"
+ "Transfer-encoding:none ",[]),
+ Info#response{body=read_plain_body(Info#response.scheme,
+ Info#response.socket,
+ ContLen,
+ Info#response.body,
+ Timeout)}
+ end
+ end.
+
+
+%%% ----------------------------------------------------------------------
+read_server_body(Info,Timeout) ->
+ MaxBodySz=httpd_util:lookup(Info#mod.config_db,max_body_size,?MAXBODYSIZE),
+ ContLen=list_to_integer((Info#mod.headers)#req_headers.content_length),
+ %% ?vtrace("ContentLength: ~p", [ContLen]),
+ if
+ integer(ContLen),integer(MaxBodySz),ContLen>MaxBodySz ->
+ throw({error,body_too_big});
+ true ->
+ read_server_body2(Info,Timeout,ContLen,MaxBodySz)
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Control if the body is transfer encoded, if so decode it.
+%% Note:
+%% - MaxBodySz has an integer value or 'nolimit'
+%% - ContLen has an integer value or 'undefined'
+%% All applications MUST be able to receive and decode the "chunked"
+%% transfer-coding, see RFC 2616 Section 3.6.1
+read_server_body2(Info,Timeout,ContLen,MaxBodySz) ->
+ ?DEBUG("read_entity_body2()->Max: ~p ~nLength:~p ~nSocket: ~p ~n",
+ [MaxBodySz,ContLen,Info#mod.socket]),
+ case (Info#mod.headers)#req_headers.transfer_encoding of
+ "chunked" ->
+ ?DEBUG("read_entity_body2()->"
+ "Transfer-encoding:Chunked Data:",[]),
+ read_server_chunked_body(Info,Timeout,MaxBodySz);
+ Encoding when list(Encoding) ->
+ ?DEBUG("read_entity_body2()->"
+ "Transfer-encoding:Unknown",[]),
+ httpd_response:send_status(Info,501,"Unknown Transfer-Encoding"),
+ http_lib:close(Info#mod.socket_type,Info#mod.socket),
+ throw({error,{status_sent,"Unknown Transfer-Encoding "++Encoding}});
+ _ when integer(ContLen),integer(MaxBodySz),ContLen>MaxBodySz ->
+ throw({error,body_too_big});
+ _ when integer(ContLen) ->
+ ?DEBUG("read_entity_body2()->"
+ "Transfer-encoding:none ",[]),
+ Info#mod{entity_body=read_plain_body(Info#mod.socket_type,
+ Info#mod.socket,
+ ContLen,Info#mod.entity_body,
+ Timeout)}
+ end.
+
+
+%%% ----------------------------------------------------------------------------
+%%% The body was plain, just read it from the socket.
+read_plain_body(_SocketType,Socket,0,Cont,_Timeout) ->
+ Cont;
+read_plain_body(SocketType,Socket,ContLen,Cont,Timeout) ->
+ Body=read_more_data(SocketType,Socket,ContLen,Timeout),
+ <<Cont/binary,Body/binary>>.
+
+%%% ----------------------------------------------------------------------------
+%%% The body was chunked, decode it.
+%%% From RFC2616, Section 3.6.1
+%% Chunked-Body = *chunk
+%% last-chunk
+%% trailer
+%% CRLF
+%%
+%% chunk = chunk-size [ chunk-extension ] CRLF
+%% chunk-data CRLF
+%% chunk-size = 1*HEX
+%% last-chunk = 1*("0") [ chunk-extension ] CRLF
+%%
+%% chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+%% chunk-ext-name = token
+%% chunk-ext-val = token | quoted-string
+%% chunk-data = chunk-size(OCTET)
+%% trailer = *(entity-header CRLF)
+%%
+%%% "All applications MUST ignore chunk-extension extensions they do not
+%%% understand.", see RFC 2616 Section 3.6.1
+%%% We don't understand any extension...
+read_client_chunked_body(Info,Timeout,MaxChunkSz) ->
+ case read_chunk(Info#response.scheme,Info#response.socket,
+ Timeout,0,MaxChunkSz) of
+ {last_chunk,_ExtensionList} -> % Ignore extension
+ TrailH=read_headers_old(Info#response.scheme,Info#response.socket,
+ Timeout),
+ H=Info#response.headers,
+ OtherHeaders=H#res_headers.other++TrailH,
+ Info#response{headers=H#res_headers{other=OtherHeaders}};
+ {Chunk,ChunkSize,_ExtensionList} -> % Ignore extension
+ Info1=Info#response{body= <<(Info#response.body)/binary,
+ Chunk/binary>>},
+ read_client_chunked_body(Info1,Timeout,MaxChunkSz-ChunkSize);
+ {error,Reason} ->
+ throw({error,Reason})
+ end.
+
+
+read_server_chunked_body(Info,Timeout,MaxChunkSz) ->
+ case read_chunk(Info#mod.socket_type,Info#mod.socket,
+ Timeout,0,MaxChunkSz) of
+ {last_chunk,_ExtensionList} -> % Ignore extension
+ TrailH=read_headers_old(Info#mod.socket_type,Info#mod.socket,
+ Timeout),
+ H=Info#mod.headers,
+ OtherHeaders=H#req_headers.other++TrailH,
+ Info#mod{headers=H#req_headers{other=OtherHeaders}};
+ {Chunk,ChunkSize,_ExtensionList} -> % Ignore extension
+ Info1=Info#mod{entity_body= <<(Info#mod.entity_body)/binary,
+ Chunk/binary>>},
+ read_server_chunked_body(Info1,Timeout,MaxChunkSz-ChunkSize);
+ {error,Reason} ->
+ throw({error,Reason})
+ end.
+
+
+read_chunk(Scheme,Socket,Timeout,Int,MaxChunkSz) when MaxChunkSz>Int ->
+ case read_more_data(Scheme,Socket,1,Timeout) of
+ <<C>> when $0=<C,C=<$9 ->
+ read_chunk(Scheme,Socket,Timeout,16*Int+(C-$0),MaxChunkSz);
+ <<C>> when $a=<C,C=<$f ->
+ read_chunk(Scheme,Socket,Timeout,16*Int+10+(C-$a),MaxChunkSz);
+ <<C>> when $A=<C,C=<$F ->
+ read_chunk(Scheme,Socket,Timeout,16*Int+10+(C-$A),MaxChunkSz);
+ <<$;>> when Int>0 ->
+ ExtensionList=read_chunk_ext_name(Scheme,Socket,Timeout,[],[]),
+ read_chunk_data(Scheme,Socket,Int+1,ExtensionList,Timeout);
+ <<$;>> when Int==0 ->
+ ExtensionList=read_chunk_ext_name(Scheme,Socket,Timeout,[],[]),
+ read_data_lf(Scheme,Socket,Timeout),
+ {last_chunk,ExtensionList};
+ <<?CR>> when Int>0 ->
+ read_chunk_data(Scheme,Socket,Int+1,[],Timeout);
+ <<?CR>> when Int==0 ->
+ read_data_lf(Scheme,Socket,Timeout),
+ {last_chunk,[]};
+ <<C>> when C==$ -> % Some servers (e.g., Apache 1.3.6) throw in
+ % additional whitespace...
+ read_chunk(Scheme,Socket,Timeout,Int,MaxChunkSz);
+ _Other ->
+ {error,unexpected_chunkdata}
+ end;
+read_chunk(_Scheme,_Socket,_Timeout,_Int,_MaxChunkSz) ->
+ {error,body_too_big}.
+
+
+%%% Note:
+%%% - Got the initial ?CR already!
+%%% - Bitsyntax does not allow matching of ?CR,?LF in the end of the first read
+read_chunk_data(Scheme,Socket,Int,ExtensionList,Timeout) ->
+ case read_more_data(Scheme,Socket,Int,Timeout) of
+ <<?LF,Chunk/binary>> ->
+ case read_more_data(Scheme,Socket,2,Timeout) of
+ <<?CR,?LF>> ->
+ {Chunk,size(Chunk),ExtensionList};
+ _ ->
+ {error,bad_chunkdata}
+ end;
+ _ ->
+ {error,bad_chunkdata}
+ end.
+
+read_chunk_ext_name(Scheme,Socket,Timeout,Name,Acc) ->
+ Len=length(Name),
+ case read_more_data(Scheme,Socket,1,Timeout) of
+ $= when Len>0 ->
+ read_chunk_ext_val(Scheme,Socket,Timeout,Name,[],Acc);
+ $; when Len>0 ->
+ read_chunk_ext_name(Scheme,Socket,Timeout,[],
+ [{lists:reverse(Name),""}|Acc]);
+ ?CR when Len>0 ->
+ lists:reverse([{lists:reverse(Name,"")}|Acc]);
+ Token -> % FIXME Check that it is "token"
+ read_chunk_ext_name(Scheme,Socket,Timeout,[Token|Name],Acc);
+ _ ->
+ {error,bad_chunk_extension_name}
+ end.
+
+read_chunk_ext_val(Scheme,Socket,Timeout,Name,Val,Acc) ->
+ Len=length(Val),
+ case read_more_data(Scheme,Socket,1,Timeout) of
+ $; when Len>0 ->
+ read_chunk_ext_name(Scheme,Socket,Timeout,[],
+ [{Name,lists:reverse(Val)}|Acc]);
+ ?CR when Len>0 ->
+ lists:reverse([{Name,lists:reverse(Val)}|Acc]);
+ Token -> % FIXME Check that it is "token" or "quoted-string"
+ read_chunk_ext_val(Scheme,Socket,Timeout,Name,[Token|Val],Acc);
+ _ ->
+ {error,bad_chunk_extension_value}
+ end.
+
+read_data_lf(Scheme,Socket,Timeout) ->
+ case read_more_data(Scheme,Socket,1,Timeout) of
+ ?LF ->
+ ok;
+ _ ->
+ {error,bad_chunkdata}
+ end.
+
+%%% ----------------------------------------------------------------------------
+%%% The body was "multipart/byteranges", decode it.
+%%% Example from RFC 2616, Appendix 19.2
+%%% HTTP/1.1 206 Partial Content
+%%% Date: Wed, 15 Nov 1995 06:25:24 GMT
+%%% Last-Modified: Wed, 15 Nov 1995 04:58:08 GMT
+%%% Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES
+%%%
+%%% --THIS_STRING_SEPARATES
+%%% Content-type: application/pdf
+%%% Content-range: bytes 500-999/8000
+%%%
+%%% ...the first range...
+%%% --THIS_STRING_SEPARATES
+%%% Content-type: application/pdf
+%%% Content-range: bytes 7000-7999/8000
+%%%
+%%% ...the second range
+%%% --THIS_STRING_SEPARATES--
+%%%
+%%% Notes:
+%%%
+%%% 1) Additional CRLFs may precede the first boundary string in the
+%%% entity.
+%%% FIXME!!
+read_client_multipartrange_body(Info,Parstr,Timeout) ->
+ Boundary=get_boundary(Parstr),
+ scan_boundary(Info,Boundary),
+ Info#response{body=read_multipart_body(Info,Boundary,Timeout)}.
+
+read_multipart_body(Info,Boundary,Timeout) ->
+ Info.
+
+% Headers=read_headers_old(Info#response.scheme,Info#response.socket,Timeout),
+% H=Info#response.headers,
+% OtherHeaders=H#res_headers.other++TrailH,
+% Info#response{headers=H#res_headers{other=OtherHeaders}}.
+
+
+scan_boundary(Info,Boundary) ->
+ Info.
+
+
+get_boundary(Parstr) ->
+ case skip_lwsp(Parstr) of
+ [] ->
+ throw({error,missing_range_boundary_parameter});
+ Val ->
+ get_boundary2(string:tokens(Val, ";"))
+ end.
+
+get_boundary2([]) ->
+ undefined;
+get_boundary2([Param|Rest]) ->
+ case string:tokens(skip_lwsp(Param), "=") of
+ ["boundary"++Attribute,Value] ->
+ Value;
+ _ ->
+ get_boundary2(Rest)
+ end.
+
+
+%% skip space & tab
+skip_lwsp([$ | Cs]) -> skip_lwsp(Cs);
+skip_lwsp([$\t | Cs]) -> skip_lwsp(Cs);
+skip_lwsp(Cs) -> Cs.
+
+%%% ----------------------------------------------------------------------------
+
+%%% Read the incoming data from the open socket.
+read_more_data(http,Socket,Len,Timeout) ->
+ case gen_tcp:recv(Socket,Len,Timeout) of
+ {ok,Val} ->
+ Val;
+ {error, timeout} ->
+ throw({error, session_local_timeout});
+ {error, Reason} when Reason==closed;Reason==enotconn ->
+ throw({error, session_remotely_closed});
+ {error, Reason} ->
+% httpd_response:send_status(Info,400,none),
+ throw({error, Reason})
+ end;
+read_more_data(https,Socket,Len,Timeout) ->
+ case ssl:recv(Socket,Len,Timeout) of
+ {ok,Val} ->
+ Val;
+ {error, etimedout} ->
+ throw({error, session_local_timeout});
+ {error, Reason} when Reason==closed;Reason==enotconn ->
+ throw({error, session_remotely_closed});
+ {error, Reason} ->
+% httpd_response:send_status(Info,400,none),
+ throw({error, Reason})
+ end.
+
+
+%% =============================================================================
+%%% Socket handling
+
+accept(http,ListenSocket, Timeout) ->
+ gen_tcp:accept(ListenSocket, Timeout);
+accept(https,ListenSocket, Timeout) ->
+ ssl:accept(ListenSocket, Timeout).
+
+
+close(http,Socket) ->
+ gen_tcp:close(Socket);
+close(https,Socket) ->
+ ssl:close(Socket).
+
+
+connect(#request{scheme=http,settings=Settings,address=Addr}) ->
+ case proxyusage(Addr,Settings) of
+ {error,Reason} ->
+ {error,Reason};
+ {Host,Port} ->
+ Opts=[binary,{active,false},{reuseaddr,true}],
+ gen_tcp:connect(Host,Port,Opts)
+ end;
+connect(#request{scheme=https,settings=Settings,address=Addr}) ->
+ case proxyusage(Addr,Settings) of
+ {error,Reason} ->
+ {error,Reason};
+ {Host,Port} ->
+ Opts=case Settings#client_settings.ssl of
+ false ->
+ [binary,{active,false}];
+ SSLSettings ->
+ [binary,{active,false}]++SSLSettings
+ end,
+ ssl:connect(Host,Port,Opts)
+ end.
+
+
+%%% Check to see if the given {Host,Port} tuple is in the NoProxyList
+%%% Returns an eventually updated {Host,Port} tuple, with the proxy address
+proxyusage(HostPort,Settings) ->
+ case Settings#client_settings.useproxy of
+ true ->
+ case noProxy(HostPort,Settings#client_settings.noproxylist) of
+ true ->
+ HostPort;
+ _ ->
+ case Settings#client_settings.proxy of
+ undefined ->
+ {error,no_proxy_defined};
+ ProxyHostPort ->
+ ProxyHostPort
+ end
+ end;
+ _ ->
+ HostPort
+ end.
+
+noProxy(_HostPort,[]) ->
+ false;
+noProxy({Host,Port},[{Host,Port}|Rest]) ->
+ true;
+noProxy(HostPort,[_|Rest]) ->
+ noProxy(HostPort,Rest).
+
+
+controlling_process(http,Socket,Pid) ->
+ gen_tcp:controlling_process(Socket,Pid);
+controlling_process(https,Socket,Pid) ->
+ ssl:controlling_process(Socket,Pid).
+
+
+deliver(SocketType, Socket, Message) ->
+ case send(SocketType, Socket, Message) of
+ {error, einval} ->
+ close(SocketType, Socket),
+ socket_closed;
+ {error, _Reason} ->
+% ?vlog("deliver(~p) failed for reason:"
+% "~n Reason: ~p",[SocketType,_Reason]),
+ close(SocketType, Socket),
+ socket_closed;
+ _Other ->
+ ok
+ end.
+
+
+recv0(http,Socket,Timeout) ->
+ gen_tcp:recv(Socket,0,Timeout);
+recv0(https,Socket,Timeout) ->
+ ssl:recv(Socket,0,Timeout).
+
+recv(http,Socket,Len,Timeout) ->
+ gen_tcp:recv(Socket,Len,Timeout);
+recv(https,Socket,Len,Timeout) ->
+ ssl:recv(Socket,Len,Timeout).
+
+
+setopts(http,Socket,Options) ->
+ inet:setopts(Socket,Options);
+setopts(https,Socket,Options) ->
+ ssl:setopts(Socket,Options).
+
+
+send(http,Socket,Message) ->
+ gen_tcp:send(Socket,Message);
+send(https,Socket,Message) ->
+ ssl:send(Socket,Message).
+
+
+%%% ============================================================================
+%%% HTTP Server only
+
+%%% Returns the Authenticating data in the HTTP request
+get_auth_data("Basic "++EncodedString) ->
+ UnCodedString=httpd_util:decode_base64(EncodedString),
+ case catch string:tokens(UnCodedString,":") of
+ [User,PassWord] ->
+ {User,PassWord};
+ {error,Error}->
+ {error,Error}
+ end;
+get_auth_data(BadCredentials) when list(BadCredentials) ->
+ {error,BadCredentials};
+get_auth_data(_) ->
+ {error,nouser}.
+
+
+create_header_list(H) ->
+ lookup(connection,H#req_headers.connection)++
+ lookup(host,H#req_headers.host)++
+ lookup(content_length,H#req_headers.content_length)++
+ lookup(transfer_encoding,H#req_headers.transfer_encoding)++
+ lookup(authorization,H#req_headers.authorization)++
+ lookup(user_agent,H#req_headers.user_agent)++
+ lookup(user_agent,H#req_headers.range)++
+ lookup(user_agent,H#req_headers.if_range)++
+ lookup(user_agent,H#req_headers.if_match)++
+ lookup(user_agent,H#req_headers.if_none_match)++
+ lookup(user_agent,H#req_headers.if_modified_since)++
+ lookup(user_agent,H#req_headers.if_unmodified_since)++
+ H#req_headers.other.
+
+lookup(_Key,undefined) ->
+ [];
+lookup(Key,Val) ->
+ [{Key,Val}].
+
+
+
+%%% ============================================================================
+%%% This code is for parsing trailer headers in chunked messages.
+%%% Will be deprecated whenever I have found an alternative working solution!
+%%% Note:
+%%% - The header names are returned slighly different from what the what
+%%% inet_drv returns
+read_headers_old(Scheme,Socket,Timeout) ->
+ read_headers_old(<<>>,Scheme,Socket,Timeout,[],[]).
+
+read_headers_old(<<>>,Scheme,Socket,Timeout,Acc,AccHdrs) ->
+ read_headers_old(read_more_data(Scheme,Socket,1,Timeout),
+ Scheme,Socket,Timeout,Acc,AccHdrs);
+read_headers_old(<<$\r>>,Scheme,Socket,Timeout,Acc,AccHdrs) ->
+ read_headers_old(<<$\r,(read_more_data(Scheme,Socket,1,Timeout))/binary>>,
+ Scheme,Socket,Timeout,Acc,AccHdrs);
+read_headers_old(<<$\r,$\n>>,Scheme,Socket,Timeout,Acc,AccHdrs) ->
+ if
+ Acc==[] -> % Done!
+ tagup_header(lists:reverse(AccHdrs));
+ true ->
+ read_headers_old(read_more_data(Scheme,Socket,1,Timeout),
+ Scheme,Socket,
+ Timeout,[],[lists:reverse(Acc)|AccHdrs])
+ end;
+read_headers_old(<<C>>,Scheme,Socket,Timeout,Acc,AccHdrs) ->
+ read_headers_old(read_more_data(Scheme,Socket,1,Timeout),
+ Scheme,Socket,Timeout,[C|Acc],AccHdrs);
+read_headers_old(Bin,_Scheme,_Socket,_Timeout,_Acc,_AccHdrs) ->
+ io:format("ERROR: Unexpected data from inet driver: ~p",[Bin]),
+ throw({error,this_is_a_bug}).
+
+
+%% Parses the header of a HTTP request and returns a key,value tuple
+%% list containing Name and Value of each header directive as of:
+%%
+%% Content-Type: multipart/mixed -> {"Content-Type", "multipart/mixed"}
+%%
+%% But in http/1.1 the field-names are case insencitive so now it must be
+%% Content-Type: multipart/mixed -> {"content-type", "multipart/mixed"}
+%% The standard furthermore says that leading and traling white space
+%% is not a part of the fieldvalue and shall therefore be removed.
+tagup_header([]) -> [];
+tagup_header([Line|Rest]) -> [tag(Line, [])|tagup_header(Rest)].
+
+tag([], Tag) ->
+ {httpd_util:to_lower(lists:reverse(Tag)), ""};
+tag([$:|Rest], Tag) ->
+ {httpd_util:to_lower(lists:reverse(Tag)), httpd_util:strip(Rest)};
+tag([Chr|Rest], Tag) ->
+ tag(Rest, [Chr|Tag]).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_handler.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_handler.erl
new file mode 100644
index 0000000000..5076a12aaa
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_handler.erl
@@ -0,0 +1,724 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+
+%%% TODO:
+%%% - If an error is returned when sending a request, don't use this
+%%% session anymore.
+%%% - Closing of sessions not properly implemented for some cases
+
+%%% File : httpc_handler.erl
+%%% Author : Johan Blom <[email protected]>
+%%% Description : Handles HTTP client responses, for a single TCP session
+%%% Created : 4 Mar 2002 by Johan Blom
+
+-module(httpc_handler).
+
+-include("http.hrl").
+-include("jnets_httpd.hrl").
+
+-export([init_connection/2,http_request/2]).
+
+%%% ==========================================================================
+%%% "Main" function in the spawned process for the session.
+init_connection(Req,Session) when record(Req,request) ->
+ case catch http_lib:connect(Req) of
+ {ok,Socket} ->
+ case catch http_request(Req,Socket) of
+ ok ->
+ case Session#session.clientclose of
+ true ->
+ ok;
+ false ->
+ httpc_manager:register_socket(Req#request.address,
+ Session#session.id,
+ Socket)
+ end,
+ next_response_with_request(Req,
+ Session#session{socket=Socket});
+ {error,Reason} -> % Not possible to use new session
+ gen_server:cast(Req#request.from,
+ {Req#request.ref,Req#request.id,{error,Reason}}),
+ exit_session_ok(Req#request.address,
+ Session#session{socket=Socket})
+ end;
+ {error,Reason} -> % Not possible to set up new session
+ gen_server:cast(Req#request.from,
+ {Req#request.ref,Req#request.id,{error,Reason}}),
+ exit_session_ok2(Req#request.address,
+ Session#session.clientclose,Session#session.id)
+ end.
+
+next_response_with_request(Req,Session) ->
+ Timeout=(Req#request.settings)#client_settings.timeout,
+ case catch read(Timeout,Session#session.scheme,Session#session.socket) of
+ {Status,Headers,Body} ->
+ NewReq=handle_response({Status,Headers,Body},Timeout,Req,Session),
+ next_response_with_request(NewReq,Session);
+ {error,Reason} ->
+ gen_server:cast(Req#request.from,
+ {Req#request.ref,Req#request.id,{error,Reason}}),
+ exit_session(Req#request.address,Session,aborted_request);
+ {'EXIT',Reason} ->
+ gen_server:cast(Req#request.from,
+ {Req#request.ref,Req#request.id,{error,Reason}}),
+ exit_session(Req#request.address,Session,aborted_request)
+ end.
+
+handle_response(Response,Timeout,Req,Session) ->
+ case http_response(Response,Req,Session) of
+ ok ->
+ next_response(Timeout,Req#request.address,Session);
+ stop ->
+ exit(normal);
+ {error,Reason} ->
+ gen_server:cast(Req#request.from,
+ {Req#request.ref,Req#request.id,{error,Reason}}),
+ exit_session(Req#request.address,Session,aborted_request)
+ end.
+
+
+
+%%% Wait for the next respond until
+%%% - session is closed by the other side
+%%% => set up a new a session, if there are pending requests in the que
+%%% - "Connection:close" header is received
+%%% => close the connection (release socket) then
+%%% set up a new a session, if there are pending requests in the que
+%%%
+%%% Note:
+%%% - When invoked there are no pending responses on received requests.
+%%% - Never close the session explicitly, let it timeout instead!
+next_response(Timeout,Address,Session) ->
+ case httpc_manager:next_request(Address,Session#session.id) of
+ no_more_requests ->
+ %% There are no more pending responses, now just wait for
+ %% timeout or a new response.
+ case catch read(Timeout,
+ Session#session.scheme,Session#session.socket) of
+ {error,Reason} when Reason==session_remotely_closed;
+ Reason==session_local_timeout ->
+ exit_session_ok(Address,Session);
+ {error,Reason} ->
+ exit_session(Address,Session,aborted_request);
+ {'EXIT',Reason} ->
+ exit_session(Address,Session,aborted_request);
+ {Status2,Headers2,Body2} ->
+ case httpc_manager:next_request(Address,
+ Session#session.id) of
+ no_more_requests -> % Should not happen!
+ exit_session(Address,Session,aborted_request);
+ {error,Reason} -> % Should not happen!
+ exit_session(Address,Session,aborted_request);
+ NewReq ->
+ handle_response({Status2,Headers2,Body2},
+ Timeout,NewReq,Session)
+ end
+ end;
+ {error,Reason} -> % The connection has been closed by httpc_manager
+ exit_session(Address,Session,aborted_request);
+ NewReq ->
+ NewReq
+ end.
+
+%% ===========================================================================
+%% Internals
+
+%%% Read in and parse response data from the socket
+read(Timeout,SockType,Socket) ->
+ Info=#response{scheme=SockType,socket=Socket},
+ http_lib:setopts(SockType,Socket,[{packet, http}]),
+ Info1=read_response(SockType,Socket,Info,Timeout),
+ http_lib:setopts(SockType,Socket,[binary,{packet, raw}]),
+ case (Info1#response.headers)#res_headers.content_type of
+ "multipart/byteranges"++Param ->
+ range_response_body(Info1,Timeout,Param);
+ _ ->
+ #response{status=Status2,headers=Headers2,body=Body2}=
+ http_lib:read_client_body(Info1,Timeout),
+ {Status2,Headers2,Body2}
+ end.
+
+
+%%% From RFC 2616:
+%%% Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
+%%% HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
+%%% Status-Code = 3DIGIT
+%%% Reason-Phrase = *<TEXT, excluding CR, LF>
+read_response(SockType,Socket,Info,Timeout) ->
+ case http_lib:recv0(SockType,Socket,Timeout) of
+ {ok,{http_response,{1,VerMin}, Status, _Phrase}} when VerMin==0;
+ VerMin==1 ->
+ Info1=Info#response{status=Status,http_version=VerMin},
+ http_lib:read_client_headers(Info1,Timeout);
+ {ok,{http_response,_Version, _Status, _Phrase}} ->
+ throw({error,bad_status_line});
+ {error, timeout} ->
+ throw({error,session_local_timeout});
+ {error, Reason} when Reason==closed;Reason==enotconn ->
+ throw({error,session_remotely_closed});
+ {error, Reason} ->
+ throw({error,Reason})
+ end.
+
+%%% From RFC 2616, Section 4.4, Page 34
+%% 4.If the message uses the media type "multipart/byteranges", and the
+%% transfer-length is not otherwise specified, then this self-
+%% delimiting media type defines the transfer-length. This media type
+%% MUST NOT be used unless the sender knows that the recipient can parse
+%% it; the presence in a request of a Range header with multiple byte-
+%% range specifiers from a 1.1 client implies that the client can parse
+%% multipart/byteranges responses.
+%%% FIXME !!
+range_response_body(Info,Timeout,Param) ->
+ Headers=Info#response.headers,
+ case {Headers#res_headers.content_length,
+ Headers#res_headers.transfer_encoding} of
+ {undefined,undefined} ->
+ #response{status=Status2,headers=Headers2,body=Body2}=
+ http_lib:read_client_multipartrange_body(Info,Param,Timeout),
+ {Status2,Headers2,Body2};
+ _ ->
+ #response{status=Status2,headers=Headers2,body=Body2}=
+ http_lib:read_client_body(Info,Timeout),
+ {Status2,Headers2,Body2}
+ end.
+
+
+%%% ----------------------------------------------------------------------------
+%%% Host: field is required when addressing multi-homed sites ...
+%%% It must not be present when the request is being made to a proxy.
+http_request(#request{method=Method,id=Id,
+ scheme=Scheme,address={Host,Port},pathquery=PathQuery,
+ headers=Headers, content={ContentType,Body},
+ settings=Settings},
+ Socket) ->
+ PostData=
+ if
+ Method==post;Method==put ->
+ case Headers#req_headers.expect of
+ "100-continue" ->
+ content_type_header(ContentType) ++
+ content_length_header(length(Body)) ++
+ "\r\n";
+ _ ->
+ content_type_header(ContentType) ++
+ content_length_header(length(Body)) ++
+ "\r\n" ++ Body
+ end;
+ true ->
+ "\r\n"
+ end,
+ Message=
+ case useProxy(Settings#client_settings.useproxy,
+ {Scheme,Host,Port,PathQuery}) of
+ false ->
+ method(Method)++" "++PathQuery++" HTTP/1.1\r\n"++
+ host_header(Host)++te_header()++
+ headers(Headers) ++ PostData;
+ AbsURI ->
+ method(Method)++" "++AbsURI++" HTTP/1.1\r\n"++
+ te_header()++
+ headers(Headers)++PostData
+ end,
+ http_lib:send(Scheme,Socket,Message).
+
+useProxy(false,_) ->
+ false;
+useProxy(true,{Scheme,Host,Port,PathQuery}) ->
+ [atom_to_list(Scheme),"://",Host,":",integer_to_list(Port),PathQuery].
+
+
+
+headers(#req_headers{expect=Expect,
+ other=Other}) ->
+ H1=case Expect of
+ undefined ->[];
+ _ -> "Expect: "++Expect++"\r\n"
+ end,
+ H1++headers_other(Other).
+
+
+headers_other([]) ->
+ [];
+headers_other([{Key,Value}|Rest]) when atom(Key) ->
+ Head = atom_to_list(Key)++": "++Value++"\r\n",
+ Head ++ headers_other(Rest);
+headers_other([{Key,Value}|Rest]) ->
+ Head = Key++": "++Value++"\r\n",
+ Head ++ headers_other(Rest).
+
+host_header(Host) ->
+ "Host: "++lists:concat([Host])++"\r\n".
+content_type_header(ContentType) ->
+ "Content-Type: " ++ ContentType ++ "\r\n".
+content_length_header(ContentLength) ->
+ "Content-Length: "++integer_to_list(ContentLength) ++ "\r\n".
+te_header() ->
+ "TE: \r\n".
+
+method(Method) ->
+ httpd_util:to_upper(atom_to_list(Method)).
+
+
+%%% ----------------------------------------------------------------------------
+http_response({Status,Headers,Body},Req,Session) ->
+ case Status of
+ 100 ->
+ status_continue(Req,Session);
+ 200 ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {Status,Headers,Body}}),
+ ServerClose=http_lib:connection_close(Headers),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ 300 -> status_multiple_choices(Headers,Body,Req,Session);
+ 301 -> status_moved_permanently(Req#request.method,
+ Headers,Body,Req,Session);
+ 302 -> status_found(Headers,Body,Req,Session);
+ 303 -> status_see_other(Headers,Body,Req,Session);
+ 304 -> status_not_modified(Headers,Body,Req,Session);
+ 305 -> status_use_proxy(Headers,Body,Req,Session);
+ %% 306 This Status code is not used in HTTP 1.1
+ 307 -> status_temporary_redirect(Headers,Body,Req,Session);
+ 503 -> status_service_unavailable({Status,Headers,Body},Req,Session);
+ Status50x when Status50x==500;Status50x==501;Status50x==502;
+ Status50x==504;Status50x==505 ->
+ status_server_error_50x({Status,Headers,Body},Req,Session);
+ _ -> % FIXME May want to take some action on other Status codes as well
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {Status,Headers,Body}}),
+ ServerClose=http_lib:connection_close(Headers),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session)
+ end.
+
+
+%%% Status code dependent functions.
+
+%%% Received a 100 Status code ("Continue")
+%%% From RFC2616
+%%% The client SHOULD continue with its request. This interim response is
+%%% used to inform the client that the initial part of the request has
+%%% been received and has not yet been rejected by the server. The client
+%%% SHOULD continue by sending the remainder of the request or, if the
+%%% request has already been completed, ignore this response. The server
+%%% MUST send a final response after the request has been completed. See
+%%% section 8.2.3 for detailed discussion of the use and handling of this
+%%% status code.
+status_continue(Req,Session) ->
+ {_,Body}=Req#request.content,
+ http_lib:send(Session#session.scheme,Session#session.socket,Body),
+ next_response_with_request(Req,Session).
+
+
+%%% Received a 300 Status code ("Multiple Choices")
+%%% The resource is located in any one of a set of locations
+%%% - If a 'Location' header is present (preserved server choice), use that
+%%% to automatically redirect to the given URL
+%%% - else if the Content-Type/Body both are non-empty let the user agent make
+%%% the choice and thus return a response with status 300
+%%% Note:
+%%% - If response to a HEAD request, the Content-Type/Body both should be empty.
+%%% - The behaviour on an empty Content-Type or Body is unspecified.
+%%% However, e.g. "Apache/1.3" servers returns both empty if the header
+%%% 'if-modified-since: Date' was sent in the request and the content is
+%%% "not modified" (instead of 304). Thus implicitly giving the cache as the
+%%% only choice.
+status_multiple_choices(Headers,Body,Req,Session)
+ when ((Req#request.settings)#client_settings.autoredirect)==true ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {300,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_multiple_choices(Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {300,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+
+%%% Received a 301 Status code ("Moved Permanently")
+%%% The resource has been assigned a new permanent URI
+%%% - If a 'Location' header is present, use that to automatically redirect to
+%%% the given URL if GET or HEAD request
+%%% - else return
+%%% Note:
+%%% - The Body should contain a short hypertext note with a hyperlink to the
+%%% new URI. Return this if Content-Type acceptable (some HTTP servers doesn't
+%%% deal properly with Accept headers)
+status_moved_permanently(Method,Headers,Body,Req,Session)
+ when (((Req#request.settings)#client_settings.autoredirect)==true) and
+ (Method==get) or (Method==head) ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {301,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_moved_permanently(_Method,Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {301,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+
+%%% Received a 302 Status code ("Found")
+%%% The requested resource resides temporarily under a different URI.
+%%% Note:
+%%% - Only cacheable if indicated by a Cache-Control or Expires header
+status_found(Headers,Body,Req,Session)
+ when ((Req#request.settings)#client_settings.autoredirect)==true ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {302,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_found(Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {302,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+%%% Received a 303 Status code ("See Other")
+%%% The request found under a different URI and should be retrieved using GET
+%%% Note:
+%%% - Must not be cached
+status_see_other(Headers,Body,Req,Session)
+ when ((Req#request.settings)#client_settings.autoredirect)==true ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {303,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ method=get,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_see_other(Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {303,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+
+%%% Received a 304 Status code ("Not Modified")
+%%% Note:
+%%% - The response MUST NOT contain a body.
+%%% - The response MUST include the following header fields:
+%%% - Date, unless its omission is required
+%%% - ETag and/or Content-Location, if the header would have been sent
+%%% in a 200 response to the same request
+%%% - Expires, Cache-Control, and/or Vary, if the field-value might
+%%% differ from that sent in any previous response for the same
+%%% variant
+status_not_modified(Headers,Body,Req,Session)
+ when ((Req#request.settings)#client_settings.autoredirect)==true ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {304,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_not_modified(Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {304,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+
+
+%%% Received a 305 Status code ("Use Proxy")
+%%% The requested resource MUST be accessed through the proxy given by the
+%%% Location field
+status_use_proxy(Headers,Body,Req,Session)
+ when ((Req#request.settings)#client_settings.autoredirect)==true ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {305,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_use_proxy(Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {305,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+
+%%% Received a 307 Status code ("Temporary Redirect")
+status_temporary_redirect(Headers,Body,Req,Session)
+ when ((Req#request.settings)#client_settings.autoredirect)==true ->
+ ServerClose=http_lib:connection_close(Headers),
+ case Headers#res_headers.location of
+ undefined ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {307,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,
+ Req,Session);
+ RedirUrl ->
+ Scheme=Session#session.scheme,
+ case uri:parse(RedirUrl) of
+ {error,Reason} ->
+ {error,Reason};
+ {Scheme,Host,Port,PathQuery} -> % Automatic redirection
+ NewReq=Req#request{redircount=Req#request.redircount+1,
+ address={Host,Port},pathquery=PathQuery},
+ handle_redirect(Session#session.clientclose,ServerClose,
+ NewReq,Session)
+ end
+ end;
+status_temporary_redirect(Headers,Body,Req,Session) ->
+ ServerClose=http_lib:connection_close(Headers),
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {307,Headers,Body}}),
+ handle_connection(Session#session.clientclose,ServerClose,Req,Session).
+
+
+
+%%% Received a 503 Status code ("Service Unavailable")
+%%% The server is currently unable to handle the request due to a
+%%% temporary overloading or maintenance of the server. The implication
+%%% is that this is a temporary condition which will be alleviated after
+%%% some delay. If known, the length of the delay MAY be indicated in a
+%%% Retry-After header. If no Retry-After is given, the client SHOULD
+%%% handle the response as it would for a 500 response.
+%% Note:
+%% - This session is now considered busy, thus cancel any requests in the
+%% pipeline and close the session.
+%% FIXME! Implement a user option to automatically retry if the 'Retry-After'
+%% header is given.
+status_service_unavailable(Resp,Req,Session) ->
+% RetryAfter=Headers#res_headers.retry_after,
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,Resp}),
+ close_session(server_connection_close,Req,Session).
+
+
+%%% Received a 50x Status code (~ "Service Error")
+%%% Response status codes beginning with the digit "5" indicate cases in
+%%% which the server is aware that it has erred or is incapable of
+%%% performing the request.
+status_server_error_50x(Resp,Req,Session) ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,Resp}),
+ close_session(server_connection_close,Req,Session).
+
+
+%%% Handles requests for redirects
+%%% The redirected request might be:
+%%% - FIXME! on another TCP session, another scheme
+%%% - on the same TCP session, same scheme
+%%% - on another TCP session , same scheme
+%%% However, in all cases treat it as a new request, with redircount updated.
+%%%
+%%% The redirect may fail, but this not a reason to close this session.
+%%% Instead return a error for this request, and continue as ok.
+handle_redirect(ClientClose,ServerClose,Req,Session) ->
+ case httpc_manager:request(Req) of
+ {ok,_ReqId} -> % FIXME Should I perhaps reuse the Reqid?
+ handle_connection(ClientClose,ServerClose,Req,Session);
+ {error,Reason} ->
+ gen_server:cast(Req#request.from,{Req#request.ref,Req#request.id,
+ {error,Reason}}),
+ handle_connection(ClientClose,ServerClose,Req,Session)
+ end.
+
+%%% Check if the persistent connection flag is false (ie client request
+%%% non-persistive connection), or if the server requires a closed connection
+%%% (by sending a "Connection: close" header). If the connection required
+%%% non-persistent, we may close the connection immediately.
+handle_connection(ClientClose,ServerClose,Req,Session) ->
+ case {ClientClose,ServerClose} of
+ {false,false} ->
+ ok;
+ {false,true} -> % The server requests this session to be closed.
+ close_session(server_connection_close,Req,Session);
+ {true,_} -> % The client requested a non-persistent connection
+ close_session(client_connection_close,Req,Session)
+ end.
+
+
+%%% Close the session.
+%%% We now have three cases:
+%%% - Client request a non-persistent connection when initiating the request.
+%%% Session info not stored in httpc_manager
+%%% - Server requests a non-persistent connection when answering a request.
+%%% No need to resend request, but there might be a pipeline.
+%%% - Some kind of error
+%%% Close the session, we may then try resending all requests in the pipeline
+%%% including the current depending on the error.
+%%% FIXME! Should not always abort the session (see close_session in
+%%% httpc_manager for more details)
+close_session(client_connection_close,_Req,Session) ->
+ http_lib:close(Session#session.scheme,Session#session.socket),
+ stop;
+close_session(server_connection_close,Req,Session) ->
+ http_lib:close(Session#session.scheme,Session#session.socket),
+ httpc_manager:abort_session(Req#request.address,Session#session.id,
+ aborted_request),
+ stop.
+
+exit_session(Address,Session,Reason) ->
+ http_lib:close(Session#session.scheme,Session#session.socket),
+ httpc_manager:abort_session(Address,Session#session.id,Reason),
+ exit(normal).
+
+%%% This is the "normal" case to close a persistent connection. I.e., there are
+%%% no more requests waiting and the session was closed by the client, or
+%%% server because of a timeout or user request.
+exit_session_ok(Address,Session) ->
+ http_lib:close(Session#session.scheme,Session#session.socket),
+ exit_session_ok2(Address,Session#session.clientclose,Session#session.id).
+
+exit_session_ok2(Address,ClientClose,Sid) ->
+ case ClientClose of
+ false ->
+ httpc_manager:close_session(Address,Sid);
+ true ->
+ ok
+ end,
+ exit(normal).
+
+%%% ============================================================================
+%%% This is deprecated code, to be removed
+
+format_time() ->
+ {_,_,MicroSecs}=TS=now(),
+ {{Y,Mon,D},{H,M,S}}=calendar:now_to_universal_time(TS),
+ lists:flatten(io_lib:format("~4.4.0w-~2.2.0w-~2.2.0w,~2.2.0w:~2.2.0w:~6.3.0f",
+ [Y,Mon,D,H,M,S+(MicroSecs/1000000)])).
+
+%%% Read more data from the open socket.
+%%% Two different read functions is used because for the {active, once} socket
+%%% option is (currently) not available for SSL...
+%%% FIXME
+% read_more_data(http,Socket,Timeout) ->
+% io:format("read_more_data(ip_comm) -> "
+% "~n set active = 'once' and "
+% "await a chunk data", []),
+% http_lib:setopts(Socket, [{active,once}]),
+% read_more_data_ipcomm(Socket,Timeout);
+% read_more_data(https,Socket,Timeout) ->
+% case ssl:recv(Socket,0,Timeout) of
+% {ok,MoreData} ->
+% MoreData;
+% {error,closed} ->
+% throw({error, session_remotely_closed});
+% {error,etimedout} ->
+% throw({error, session_local_timeout});
+% {error,Reason} ->
+% throw({error, Reason});
+% Other ->
+% throw({error, Other})
+% end.
+
+% %%% Send any incoming requests on the open session immediately
+% read_more_data_ipcomm(Socket,Timeout) ->
+% receive
+% {tcp,Socket,MoreData} ->
+% % ?vtrace("read_more_data(ip_comm) -> got some data:~p",
+% % [MoreData]),
+% MoreData;
+% {tcp_closed,Socket} ->
+% % ?vtrace("read_more_data(ip_comm) -> socket closed",[]),
+% throw({error,session_remotely_closed});
+% {tcp_error,Socket,Reason} ->
+% % ?vtrace("read_more_data(ip_comm) -> ~p socket error: ~p",
+% % [self(),Reason]),
+% throw({error, Reason});
+% stop ->
+% throw({error, user_req})
+% after Timeout ->
+% throw({error, session_local_timeout})
+% end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_manager.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_manager.erl
new file mode 100644
index 0000000000..4659749270
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpc_manager.erl
@@ -0,0 +1,542 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+%% Created : 18 Dec 2001 by Johan Blom <[email protected]>
+%%
+
+-module(httpc_manager).
+
+-behaviour(gen_server).
+
+-include("http.hrl").
+
+-define(HMACALL, ?MODULE).
+-define(HMANAME, ?MODULE).
+
+%%--------------------------------------------------------------------
+%% External exports
+-export([start_link/0,start/0,
+ request/1,cancel_request/1,
+ next_request/2,
+ register_socket/3,
+ abort_session/3,close_session/2,close_session/3
+ ]).
+
+%% Debugging only
+-export([status/0]).
+
+%% gen_server callbacks
+-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,
+ code_change/3]).
+
+%%% address_db - ets() Contains mappings from a tuple {Host,Port} to a tuple
+%%% {LastSID,OpenSessions,ets()} where
+%%% LastSid is the last allocated session id,
+%%% OpenSessions is the number of currently open sessions and
+%%% ets() contains mappings from Session Id to #session{}.
+%%%
+%%% Note:
+%%% - Only persistent connections are stored in address_db
+%%% - When automatically redirecting, multiple requests are performed.
+-record(state,{
+ address_db, % ets()
+ reqid % int() Next Request id to use (identifies request).
+ }).
+
+%%====================================================================
+%% External functions
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link/0
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start() ->
+ ensure_started().
+
+start_link() ->
+ gen_server:start_link({local,?HMACALL}, ?HMANAME, [], []).
+
+
+%% Find available session process and store in address_db. If no
+%% available, start new handler process.
+request(Req) ->
+ ensure_started(),
+ ClientClose=http_lib:connection_close(Req#request.headers),
+ gen_server:call(?HMACALL,{request,ClientClose,Req},infinity).
+
+cancel_request(ReqId) ->
+ gen_server:call(?HMACALL,{cancel_request,ReqId},infinity).
+
+
+%%% Close Session
+close_session(Addr,Sid) ->
+ gen_server:call(?HMACALL,{close_session,Addr,Sid},infinity).
+close_session(Req,Addr,Sid) ->
+ gen_server:call(?HMACALL,{close_session,Req,Addr,Sid},infinity).
+
+abort_session(Addr,Sid,Msg) ->
+ gen_server:call(?HMACALL,{abort_session,Addr,Sid,Msg},infinity).
+
+
+%%% Pick next in request que
+next_request(Addr,Sid) ->
+ gen_server:call(?HMACALL,{next_request,Addr,Sid},infinity).
+
+%%% Session handler has succeded to set up a new session, now register
+%%% the socket
+register_socket(Addr,Sid,Socket) ->
+ gen_server:cast(?HMACALL,{register_socket,Addr,Sid,Socket}).
+
+
+%%% Debugging
+status() ->
+ gen_server:cast(?HMACALL,status).
+
+
+%%--------------------------------------------------------------------
+%% Function: init/1
+%% Description: Initiates the server
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%%--------------------------------------------------------------------
+init([]) ->
+ process_flag(trap_exit, true),
+ {ok,#state{address_db=ets:new(address_db,[private]),
+ reqid=0}}.
+
+
+%%--------------------------------------------------------------------
+%% Function: handle_call/3
+%% Description: Handling call messages
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+%%% Note:
+%%% - We may have multiple non-persistent connections, each will be handled in
+%%% separate processes, thus don't add such connections to address_db
+handle_call({request,false,Req},_From,State) ->
+ case ets:lookup(State#state.address_db,Req#request.address) of
+ [] ->
+ STab=ets:new(session_db,[private,{keypos,2},set]),
+ case persistent_new_session_request(0,Req,STab,State) of
+ {Reply,LastSid,State2} ->
+ ets:insert(State2#state.address_db,
+ {Req#request.address,{LastSid,1,STab}}),
+ {reply,Reply,State2};
+ {ErrorReply,State2} ->
+ {reply,ErrorReply,State2}
+ end;
+ [{_,{LastSid,OpenS,STab}}] ->
+ case lookup_session_entry(STab) of
+ {ok,Session} ->
+ old_session_request(Session,Req,STab,State);
+ need_new_session when OpenS<(Req#request.settings)#client_settings.max_sessions ->
+ case persistent_new_session_request(LastSid,Req,
+ STab,State) of
+ {Reply,LastSid2,State2} ->
+ ets:insert(State2#state.address_db,
+ {Req#request.address,
+ {LastSid2,OpenS+1,STab}}),
+ {reply,Reply,State2};
+ {ErrorReply,State2} ->
+ {reply,ErrorReply,State2}
+ end;
+ need_new_session ->
+ {reply,{error,too_many_sessions},State}
+ end
+ end;
+handle_call({request,true,Req},_From,State) ->
+ {Reply,State2}=not_persistent_new_session_request(Req,State),
+ {reply,Reply,State2};
+handle_call({cancel_request,true,_ReqId},_From,State) ->
+%% FIXME Should be possible to scan through all requests made, but perhaps
+%% better to give some more hints (such as Addr etc)
+ Reply=ok,
+ {reply,Reply,State};
+handle_call({next_request,Addr,Sid},_From,State) ->
+ case ets:lookup(State#state.address_db,Addr) of
+ [] ->
+ {reply,{error,no_connection},State};
+ [{_,{_,_,STab}}] ->
+ case ets:lookup(STab,Sid) of
+ [] ->
+ {reply,{error,session_not_registered},State};
+ [S=#session{pipeline=[],quelength=QueLen}] ->
+ if
+ QueLen==1 ->
+ ets:insert(STab,S#session{quelength=0});
+ true ->
+ ok
+ end,
+ {reply,no_more_requests,State};
+ [S=#session{pipeline=Que}] ->
+ [Req|RevQue]=lists:reverse(Que),
+ ets:insert(STab,S#session{pipeline=lists:reverse(RevQue),
+ quelength=S#session.quelength-1}),
+ {reply,Req,State}
+ end
+ end;
+handle_call({close_session,Addr,Sid},_From,State) ->
+ case ets:lookup(State#state.address_db,Addr) of
+ [] ->
+ {reply,{error,no_connection},State};
+ [{_,{LastSid,OpenS,STab}}] ->
+ case ets:lookup(STab,Sid) of
+ [#session{pipeline=Que}] ->
+ R=handle_close_session(lists:reverse(Que),STab,Sid,State),
+ ets:insert(State#state.address_db,
+ {Addr,{LastSid,OpenS-1,STab}}),
+ {reply,R,State};
+ [] ->
+ {reply,{error,session_not_registered},State}
+ end
+ end;
+handle_call({close_session,Req,Addr,Sid},_From,State) ->
+ case ets:lookup(State#state.address_db,Addr) of
+ [] ->
+ {reply,{error,no_connection},State};
+ [{_,{LastSid,OpenS,STab}}] ->
+ case ets:lookup(STab,Sid) of
+ [#session{pipeline=Que}] ->
+ R=handle_close_session([Req|lists:reverse(Que)],
+ STab,Sid,State),
+ ets:insert(State#state.address_db,
+ {Addr,{LastSid,OpenS-1,STab}}),
+ {reply,R,State};
+ [] ->
+ {reply,{error,session_not_registered},State}
+ end
+ end;
+handle_call({abort_session,Addr,Sid,Msg},_From,State) ->
+ case ets:lookup(State#state.address_db,Addr) of
+ [] ->
+ {reply,{error,no_connection},State};
+ [{_,{LastSid,OpenS,STab}}] ->
+ case ets:lookup(STab,Sid) of
+ [#session{pipeline=Que}] ->
+ R=abort_request_que(Que,{error,Msg}),
+ ets:delete(STab,Sid),
+ ets:insert(State#state.address_db,
+ {Addr,{LastSid,OpenS-1,STab}}),
+ {reply,R,State};
+ [] ->
+ {reply,{error,session_not_registered},State}
+ end
+ end.
+
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast/2
+%% Description: Handling cast messages
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_cast(status, State) ->
+ io:format("Status:~n"),
+ print_all(lists:sort(ets:tab2list(State#state.address_db))),
+ {noreply, State};
+handle_cast({register_socket,Addr,Sid,Socket},State) ->
+ case ets:lookup(State#state.address_db,Addr) of
+ [] ->
+ {noreply,State};
+ [{_,{_,_,STab}}] ->
+ case ets:lookup(STab,Sid) of
+ [Session] ->
+ ets:insert(STab,Session#session{socket=Socket}),
+ {noreply,State};
+ [] ->
+ {noreply,State}
+ end
+ end.
+
+print_all([]) ->
+ ok;
+print_all([{Addr,{LastSid,OpenSessions,STab}}|Rest]) ->
+ io:format(" Address:~p LastSid=~p OpenSessions=~p~n",[Addr,LastSid,OpenSessions]),
+ SortedList=lists:sort(fun(A,B) ->
+ if
+ A#session.id<B#session.id ->
+ true;
+ true ->
+ false
+ end
+ end,ets:tab2list(STab)),
+ print_all2(SortedList),
+ print_all(Rest).
+
+print_all2([]) ->
+ ok;
+print_all2([Session|Rest]) ->
+ io:format(" Session:~p~n",[Session#session.id]),
+ io:format(" Client close:~p~n",[Session#session.clientclose]),
+ io:format(" Socket:~p~n",[Session#session.socket]),
+ io:format(" Pipe: length=~p Que=~p~n",[Session#session.quelength,Session#session.pipeline]),
+ print_all2(Rest).
+
+%%--------------------------------------------------------------------
+%% Function: handle_info/2
+%% Description: Handling all non call/cast messages
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%--------------------------------------------------------------------
+handle_info({'EXIT',_Pid,normal}, State) ->
+ {noreply, State};
+handle_info(Info, State) ->
+ io:format("ERROR httpc_manager:handle_info ~p~n",[Info]),
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate/2
+%% Description: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%--------------------------------------------------------------------
+terminate(_Reason, State) ->
+ ets:delete(State#state.address_db).
+
+%%--------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Convert process state when code is changed
+%% Returns: {ok, NewState}
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+%%% From RFC 2616, Section 8.1.4
+%%% A client, server, or proxy MAY close the transport connection at any
+%%% time. For example, a client might have started to send a new request
+%%% at the same time that the server has decided to close the "idle"
+%%% connection. From the server's point of view, the connection is being
+%%% closed while it was idle, but from the client's point of view, a
+%%% request is in progress.
+%%%
+%%% This means that clients, servers, and proxies MUST be able to recover
+%%% from asynchronous close events. Client software SHOULD reopen the
+%%% transport connection and retransmit the aborted sequence of requests
+%%% without user interaction so long as the request sequence is
+%%% idempotent (see section 9.1.2). Non-idempotent methods or sequences
+%%%
+%%% FIXME
+%%% Note:
+%%% - If this happen (server close because of idle) there can't be any requests
+%%% in the que.
+%%% - This is the main function for closing of sessions
+handle_close_session([],STab,Sid,_State) ->
+ ets:delete(STab,Sid);
+handle_close_session(Que,STab,Sid,_State) ->
+ ets:delete(STab,Sid),
+ abort_request_que(Que,{error,aborted_request}).
+
+
+%%% From RFC 2616, Section 8.1.2.2
+%%% Clients which assume persistent connections and pipeline immediately
+%%% after connection establishment SHOULD be prepared to retry their
+%%% connection if the first pipelined attempt fails. If a client does
+%%% such a retry, it MUST NOT pipeline before it knows the connection is
+%%% persistent. Clients MUST also be prepared to resend their requests if
+%%% the server closes the connection before sending all of the
+%%% corresponding responses.
+%%% FIXME! I'm currently not checking if tis is the first attempt on the session
+%%% FIXME! Pipeline size must be dynamically variable (e.g. 0 if resend, 2 else)
+%%% The que contains requests that have been sent ok previously, but the session
+%%% was closed prematurely when reading the response.
+%%% Try setup a new session and resend these requests.
+%%% Note:
+%%% - This MUST be a persistent session
+% handle_closed_pipelined_session_que([],_State) ->
+% ok;
+% handle_closed_pipelined_session_que(_Que,_State) ->
+% ok.
+
+
+%%% From RFC 2616, Section 8.2.4
+%%% If an HTTP/1.1 client sends a request which includes a request body,
+%%% but which does not include an Expect request-header field with the
+%%% "100-continue" expectation, and if the client is not directly
+%%% connected to an HTTP/1.1 origin server, and if the client sees the
+%%% connection close before receiving any status from the server, the
+%%% client SHOULD retry the request. If the client does retry this
+%%% request, it MAY use the following "binary exponential backoff"
+%%% algorithm to be assured of obtaining a reliable response:
+%%% ...
+%%% FIXME! I'm currently not checking if a "Expect: 100-continue" has been sent.
+% handle_remotely_closed_session_que([],_State) ->
+% ok;
+% handle_remotely_closed_session_que(_Que,_State) ->
+% % resend_que(Que,Socket),
+% ok.
+
+%%% Resend all requests in the request que
+% resend_que([],_) ->
+% ok;
+% resend_que([Req|Que],Socket) ->
+% case catch httpc_handler:http_request(Req,Socket) of
+% ok ->
+% resend_que(Que,Socket);
+% {error,Reason} ->
+% {error,Reason}
+% end.
+
+
+%%% From RFC 2616,
+%%% Section 8.1.2.2:
+%%% Clients SHOULD NOT pipeline requests using non-idempotent methods or
+%%% non-idempotent sequences of methods (see section 9.1.2). Otherwise, a
+%%% premature termination of the transport connection could lead to
+%%% indeterminate results. A client wishing to send a non-idempotent
+%%% request SHOULD wait to send that request until it has received the
+%%% response status for the previous request.
+%%% Section 9.1.2:
+%%% Methods can also have the property of "idempotence" in that (aside
+%%% from error or expiration issues) the side-effects of N > 0 identical
+%%% requests is the same as for a single request. The methods GET, HEAD,
+%%% PUT and DELETE share this property. Also, the methods OPTIONS and
+%%% TRACE SHOULD NOT have side effects, and so are inherently idempotent.
+%%%
+%%% Note that POST and CONNECT are idempotent methods.
+%%%
+%%% Tries to find an open, free session i STab. Such a session has quelength
+%%% less than ?MAX_PIPELINE_LENGTH
+%%% Don't care about non-standard, user defined methods.
+%%%
+%%% Returns {ok,Session} or need_new_session where
+%%% Session is the session that may be used
+lookup_session_entry(STab) ->
+ MS=[{#session{quelength='$1',max_quelength='$2',
+ id='_',clientclose='_',socket='$3',scheme='_',pipeline='_'},
+ [{'<','$1','$2'},{is_port,'$3'}],
+ ['$_']}],
+ case ets:select(STab,MS) of
+ [] ->
+ need_new_session;
+ SessionList -> % Now check if any of these has an empty pipeline.
+ case lists:keysearch(0,2,SessionList) of
+ {value,Session} ->
+ {ok,Session};
+ false ->
+ {ok,hd(SessionList)}
+ end
+ end.
+
+
+%%% Returns a tuple {Reply,State} where
+%%% Reply is the response sent back to the application
+%%%
+%%% Note:
+%%% - An {error,einval} from a send should sometimes rather be {error,closed}
+%%% - Don't close the session from here, let httpc_handler take care of that.
+%old_session_request(Session,Req,STab,State)
+% when (Req#request.settings)#client_settings.max_quelength==0 ->
+% Session1=Session#session{pipeline=[Req]},
+% ets:insert(STab,Session1),
+% {reply,{ok,ReqId},State#state{reqid=ReqId+1}};
+old_session_request(Session,Req,STab,State) ->
+ ReqId=State#state.reqid,
+ Req1=Req#request{id=ReqId},
+ case catch httpc_handler:http_request(Req1,Session#session.socket) of
+ ok ->
+ Session1=Session#session{pipeline=[Req1|Session#session.pipeline],
+ quelength=Session#session.quelength+1},
+ ets:insert(STab,Session1),
+ {reply,{ok,ReqId},State#state{reqid=ReqId+1}};
+ {error,Reason} ->
+ ets:insert(STab,Session#session{socket=undefined}),
+% http_lib:close(Session#session.sockettype,Session#session.socket),
+ {reply,{error,Reason},State#state{reqid=ReqId+1}}
+ end.
+
+%%% Returns atuple {Reply,Sid,State} where
+%%% Reply is the response sent back to the application, and
+%%% Sid is the last used Session Id
+persistent_new_session_request(Sid,Req,STab,State) ->
+ ReqId=State#state.reqid,
+ case setup_new_session(Req#request{id=ReqId},false,Sid) of
+ {error,Reason} ->
+ {{error,Reason},State#state{reqid=ReqId+1}};
+ {NewSid,Session} ->
+ ets:insert(STab,Session),
+ {{ok,ReqId},NewSid,State#state{reqid=ReqId+1}}
+ end.
+
+%%% Returns a tuple {Reply,State} where
+%%% Reply is the response sent back to the application
+not_persistent_new_session_request(Req,State) ->
+ ReqId=State#state.reqid,
+ case setup_new_session(Req#request{id=ReqId},true,undefined) of
+ {error,Reason} ->
+ {{error,Reason},State#state{reqid=ReqId+1}};
+ ok ->
+ {{ok,ReqId},State#state{reqid=ReqId+1}}
+ end.
+
+%%% As there are no sessions available, setup a new session and send the request
+%%% on it.
+setup_new_session(Req,ClientClose,Sid) ->
+ S=#session{id=Sid,clientclose=ClientClose,
+ scheme=Req#request.scheme,
+ max_quelength=(Req#request.settings)#client_settings.max_quelength},
+ spawn_link(httpc_handler,init_connection,[Req,S]),
+ case ClientClose of
+ false ->
+ {Sid+1,S};
+ true ->
+ ok
+ end.
+
+
+%%% ----------------------------------------------------------------------------
+%%% Abort all requests in the request que.
+abort_request_que([],_Msg) ->
+ ok;
+abort_request_que([#request{from=From,ref=Ref,id=Id}|Que],Msg) ->
+ gen_server:cast(From,{Ref,Id,Msg}),
+ abort_request_que(Que,Msg);
+abort_request_que(#request{from=From,ref=Ref,id=Id},Msg) ->
+ gen_server:cast(From,{Ref,Id,Msg}).
+
+
+%%% --------------------------------
+% C={httpc_manager,{?MODULE,start_link,[]},permanent,1000,
+% worker,[?MODULE]},
+% supervisor:start_child(inets_sup, C),
+ensure_started() ->
+ case whereis(?HMANAME) of
+ undefined ->
+ start_link();
+ _ ->
+ ok
+ end.
+
+
+%%% ============================================================================
+%%% This is deprecated code, to be removed
+
+% format_time() ->
+% {_,_,MicroSecs}=TS=now(),
+% {{Y,Mon,D},{H,M,S}}=calendar:now_to_universal_time(TS),
+% lists:flatten(io_lib:format("~4.4.0w-~2.2.0w-~2.2.0w,~2.2.0w:~2.2.0w:~6.3.0f",
+% [Y,Mon,D,H,M,S+(MicroSecs/1000000)])).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.erl
new file mode 100644
index 0000000000..8cc1c133e9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.erl
@@ -0,0 +1,596 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
+%%
+-module(httpd).
+-export([multi_start/1, multi_start_link/1,
+ start/0, start/1, start/2,
+ start_link/0, start_link/1, start_link/2,
+ start_child/0,start_child/1,
+ multi_stop/1,
+ stop/0,stop/1,stop/2,
+ stop_child/0,stop_child/1,stop_child/2,
+ multi_restart/1,
+ restart/0,restart/1,restart/2,
+ parse_query/1]).
+
+%% Optional start related stuff...
+-export([load/1, load_mime_types/1,
+ start2/1, start2/2,
+ start_link2/1, start_link2/2,
+ stop2/1]).
+
+%% Management stuff
+-export([block/0,block/1,block/2,block/3,block/4,
+ unblock/0,unblock/1,unblock/2]).
+
+%% Debugging and status info stuff...
+-export([verbosity/3,verbosity/4]).
+-export([get_status/1,get_status/2,get_status/3,
+ get_admin_state/0,get_admin_state/1,get_admin_state/2,
+ get_usage_state/0,get_usage_state/1,get_usage_state/2]).
+
+-include("httpd.hrl").
+
+-define(D(F, A), io:format("~p:" ++ F ++ "~n", [?MODULE|A])).
+
+
+%% start
+
+start() ->
+ start("/var/tmp/server_root/conf/8888.conf").
+
+start(ConfigFile) ->
+ %% ?D("start(~s) -> entry", [ConfigFile]),
+ start(ConfigFile, []).
+
+start(ConfigFile, Verbosity) when list(ConfigFile), list(Verbosity) ->
+ httpd_sup:start(ConfigFile, Verbosity).
+
+
+%% start_link
+
+start_link() ->
+ start("/var/tmp/server_root/conf/8888.conf").
+
+start_link(ConfigFile) ->
+ start_link(ConfigFile, []).
+
+start_link(ConfigFile, Verbosity) when list(ConfigFile), list(Verbosity) ->
+ httpd_sup:start_link(ConfigFile, Verbosity).
+
+
+%% start2 & start_link2
+
+start2(Config) ->
+ start2(Config, []).
+
+start2(Config, Verbosity) when list(Config), list(Verbosity) ->
+ httpd_sup:start2(Config, Verbosity).
+
+start_link2(Config) ->
+ start_link2(Config, []).
+
+start_link2(Config, Verbosity) when list(Config), list(Verbosity) ->
+ httpd_sup:start_link2(Config, Verbosity).
+
+
+%% stop
+
+stop() ->
+ stop(8888).
+
+stop(Port) when integer(Port) ->
+ stop(undefined, Port);
+stop(Pid) when pid(Pid) ->
+ httpd_sup:stop(Pid);
+stop(ConfigFile) when list(ConfigFile) ->
+ %% ?D("stop(~s) -> entry", [ConfigFile]),
+ httpd_sup:stop(ConfigFile).
+
+stop(Addr, Port) when integer(Port) ->
+ httpd_sup:stop(Addr, Port).
+
+stop2(Config) when list(Config) ->
+ httpd_sup:stop2(Config).
+
+%% start_child
+
+start_child() ->
+ start_child("/var/tmp/server_root/conf/8888.conf").
+
+start_child(ConfigFile) ->
+ start_child(ConfigFile, []).
+
+start_child(ConfigFile, Verbosity) ->
+ inets_sup:start_child(ConfigFile, Verbosity).
+
+
+%% stop_child
+
+stop_child() ->
+ stop_child(8888).
+
+stop_child(Port) ->
+ stop_child(undefined,Port).
+
+stop_child(Addr, Port) when integer(Port) ->
+ inets_sup:stop_child(Addr, Port).
+
+
+%% multi_start
+
+multi_start(MultiConfigFile) ->
+ case read_multi_file(MultiConfigFile) of
+ {ok,ConfigFiles} ->
+ mstart(ConfigFiles);
+ Error ->
+ Error
+ end.
+
+mstart(ConfigFiles) ->
+ mstart(ConfigFiles,[]).
+mstart([],Results) ->
+ {ok,lists:reverse(Results)};
+mstart([H|T],Results) ->
+ Res = start(H),
+ mstart(T,[Res|Results]).
+
+
+%% multi_start_link
+
+multi_start_link(MultiConfigFile) ->
+ case read_multi_file(MultiConfigFile) of
+ {ok,ConfigFiles} ->
+ mstart_link(ConfigFiles);
+ Error ->
+ Error
+ end.
+
+mstart_link(ConfigFiles) ->
+ mstart_link(ConfigFiles,[]).
+mstart_link([],Results) ->
+ {ok,lists:reverse(Results)};
+mstart_link([H|T],Results) ->
+ Res = start_link(H),
+ mstart_link(T,[Res|Results]).
+
+
+%% multi_stop
+
+multi_stop(MultiConfigFile) ->
+ case read_multi_file(MultiConfigFile) of
+ {ok,ConfigFiles} ->
+ mstop(ConfigFiles);
+ Error ->
+ Error
+ end.
+
+mstop(ConfigFiles) ->
+ mstop(ConfigFiles,[]).
+mstop([],Results) ->
+ {ok,lists:reverse(Results)};
+mstop([H|T],Results) ->
+ Res = stop(H),
+ mstop(T,[Res|Results]).
+
+
+%% multi_restart
+
+multi_restart(MultiConfigFile) ->
+ case read_multi_file(MultiConfigFile) of
+ {ok,ConfigFiles} ->
+ mrestart(ConfigFiles);
+ Error ->
+ Error
+ end.
+
+mrestart(ConfigFiles) ->
+ mrestart(ConfigFiles,[]).
+mrestart([],Results) ->
+ {ok,lists:reverse(Results)};
+mrestart([H|T],Results) ->
+ Res = restart(H),
+ mrestart(T,[Res|Results]).
+
+
+%% restart
+
+restart() -> restart(undefined,8888).
+
+restart(Port) when integer(Port) ->
+ restart(undefined,Port);
+restart(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ restart(Addr,Port);
+ Error ->
+ Error
+ end.
+
+
+restart(Addr,Port) when integer(Port) ->
+ do_restart(Addr,Port).
+
+do_restart(Addr,Port) when integer(Port) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:restart(Pid);
+ _ ->
+ {error,not_started}
+ end.
+
+
+%%% =========================================================
+%%% Function: block/0, block/1, block/2, block/3, block/4
+%%% block()
+%%% block(Port)
+%%% block(ConfigFile)
+%%% block(Addr,Port)
+%%% block(Port,Mode)
+%%% block(ConfigFile,Mode)
+%%% block(Addr,Port,Mode)
+%%% block(ConfigFile,Mode,Timeout)
+%%% block(Addr,Port,Mode,Timeout)
+%%%
+%%% Returns: ok | {error,Reason}
+%%%
+%%% Description: This function is used to block an HTTP server.
+%%% The blocking can be done in two ways,
+%%% disturbing or non-disturbing. Default is disturbing.
+%%% When a HTTP server is blocked, all requests are rejected
+%%% (status code 503).
+%%%
+%%% disturbing:
+%%% By performing a disturbing block, the server
+%%% is blocked forcefully and all ongoing requests
+%%% are terminated. No new connections are accepted.
+%%% If a timeout time is given then, on-going requests
+%%% are given this much time to complete before the
+%%% server is forcefully blocked. In this case no new
+%%% connections is accepted.
+%%%
+%%% non-disturbing:
+%%% A non-disturbing block is more gracefull. No
+%%% new connections are accepted, but the ongoing
+%%% requests are allowed to complete.
+%%% If a timeout time is given, it waits this long before
+%%% giving up (the block operation is aborted and the
+%%% server state is once more not-blocked).
+%%%
+%%% Types: Port -> integer()
+%%% Addr -> {A,B,C,D} | string() | undefined
+%%% ConfigFile -> string()
+%%% Mode -> disturbing | non_disturbing
+%%% Timeout -> integer()
+%%%
+block() -> block(undefined,8888,disturbing).
+
+block(Port) when integer(Port) ->
+ block(undefined,Port,disturbing);
+
+block(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ block(Addr,Port,disturbing);
+ Error ->
+ Error
+ end.
+
+block(Addr,Port) when integer(Port) ->
+ block(Addr,Port,disturbing);
+
+block(Port,Mode) when integer(Port), atom(Mode) ->
+ block(undefined,Port,Mode);
+
+block(ConfigFile,Mode) when list(ConfigFile), atom(Mode) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ block(Addr,Port,Mode);
+ Error ->
+ Error
+ end.
+
+
+block(Addr,Port,disturbing) when integer(Port) ->
+ do_block(Addr,Port,disturbing);
+block(Addr,Port,non_disturbing) when integer(Port) ->
+ do_block(Addr,Port,non_disturbing);
+
+block(ConfigFile,Mode,Timeout) when list(ConfigFile), atom(Mode), integer(Timeout) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ block(Addr,Port,Mode,Timeout);
+ Error ->
+ Error
+ end.
+
+
+block(Addr,Port,non_disturbing,Timeout) when integer(Port), integer(Timeout) ->
+ do_block(Addr,Port,non_disturbing,Timeout);
+block(Addr,Port,disturbing,Timeout) when integer(Port), integer(Timeout) ->
+ do_block(Addr,Port,disturbing,Timeout).
+
+do_block(Addr,Port,Mode) when integer(Port), atom(Mode) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:block(Pid,Mode);
+ _ ->
+ {error,not_started}
+ end.
+
+
+do_block(Addr,Port,Mode,Timeout) when integer(Port), atom(Mode) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:block(Pid,Mode,Timeout);
+ _ ->
+ {error,not_started}
+ end.
+
+
+%%% =========================================================
+%%% Function: unblock/0, unblock/1, unblock/2
+%%% unblock()
+%%% unblock(Port)
+%%% unblock(ConfigFile)
+%%% unblock(Addr,Port)
+%%%
+%%% Description: This function is used to reverse a previous block
+%%% operation on the HTTP server.
+%%%
+%%% Types: Port -> integer()
+%%% Addr -> {A,B,C,D} | string() | undefined
+%%% ConfigFile -> string()
+%%%
+unblock() -> unblock(undefined,8888).
+unblock(Port) when integer(Port) -> unblock(undefined,Port);
+
+unblock(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ unblock(Addr,Port);
+ Error ->
+ Error
+ end.
+
+unblock(Addr,Port) when integer(Port) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:unblock(Pid);
+ _ ->
+ {error,not_started}
+ end.
+
+
+verbosity(Port,Who,Verbosity) ->
+ verbosity(undefined,Port,Who,Verbosity).
+
+verbosity(Addr,Port,Who,Verbosity) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:verbosity(Pid,Who,Verbosity);
+ _ ->
+ not_started
+ end.
+
+
+%%% =========================================================
+%%% Function: get_admin_state/0, get_admin_state/1, get_admin_state/2
+%%% get_admin_state()
+%%% get_admin_state(Port)
+%%% get_admin_state(Addr,Port)
+%%%
+%%% Returns: {ok,State} | {error,Reason}
+%%%
+%%% Description: This function is used to retrieve the administrative
+%%% state of the HTTP server.
+%%%
+%%% Types: Port -> integer()
+%%% Addr -> {A,B,C,D} | string() | undefined
+%%% State -> unblocked | shutting_down | blocked
+%%% Reason -> term()
+%%%
+get_admin_state() -> get_admin_state(undefined,8888).
+get_admin_state(Port) when integer(Port) -> get_admin_state(undefined,Port);
+
+get_admin_state(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ unblock(Addr,Port);
+ Error ->
+ Error
+ end.
+
+get_admin_state(Addr,Port) when integer(Port) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:get_admin_state(Pid);
+ _ ->
+ {error,not_started}
+ end.
+
+
+
+%%% =========================================================
+%%% Function: get_usage_state/0, get_usage_state/1, get_usage_state/2
+%%% get_usage_state()
+%%% get_usage_state(Port)
+%%% get_usage_state(Addr,Port)
+%%%
+%%% Returns: {ok,State} | {error,Reason}
+%%%
+%%% Description: This function is used to retrieve the usage
+%%% state of the HTTP server.
+%%%
+%%% Types: Port -> integer()
+%%% Addr -> {A,B,C,D} | string() | undefined
+%%% State -> idle | active | busy
+%%% Reason -> term()
+%%%
+get_usage_state() -> get_usage_state(undefined,8888).
+get_usage_state(Port) when integer(Port) -> get_usage_state(undefined,Port);
+
+get_usage_state(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ unblock(Addr,Port);
+ Error ->
+ Error
+ end.
+
+get_usage_state(Addr,Port) when integer(Port) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:get_usage_state(Pid);
+ _ ->
+ {error,not_started}
+ end.
+
+
+
+%%% =========================================================
+%% Function: get_status(ConfigFile) -> Status
+%% get_status(Port) -> Status
+%% get_status(Addr,Port) -> Status
+%% get_status(Port,Timeout) -> Status
+%% get_status(Addr,Port,Timeout) -> Status
+%%
+%% Arguments: ConfigFile -> string()
+%% Configuration file from which Port and
+%% BindAddress will be extracted.
+%% Addr -> {A,B,C,D} | string()
+%% Bind Address of the http server
+%% Port -> integer()
+%% Port number of the http server
+%% Timeout -> integer()
+%% Timeout time for the call
+%%
+%% Returns: Status -> list()
+%%
+%% Description: This function is used when the caller runs in the
+%% same node as the http server or if calling with a
+%% program such as erl_call (see erl_interface).
+%%
+
+get_status(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok,Addr,Port} ->
+ get_status(Addr,Port);
+ Error ->
+ Error
+ end;
+
+get_status(Port) when integer(Port) ->
+ get_status(undefined,Port,5000).
+
+get_status(Port,Timeout) when integer(Port), integer(Timeout) ->
+ get_status(undefined,Port,Timeout);
+
+get_status(Addr,Port) when list(Addr), integer(Port) ->
+ get_status(Addr,Port,5000).
+
+get_status(Addr,Port,Timeout) when integer(Port) ->
+ Name = make_name(Addr,Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ httpd_manager:get_status(Pid,Timeout);
+ _ ->
+ not_started
+ end.
+
+
+%% load config
+
+load(ConfigFile) ->
+ httpd_conf:load(ConfigFile).
+
+load_mime_types(MimeTypesFile) ->
+ httpd_conf:load_mime_types(MimeTypesFile).
+
+
+%% parse_query
+
+parse_query(String) ->
+ {ok, SplitString} = regexp:split(String,"[&;]"),
+ foreach(SplitString).
+
+foreach([]) ->
+ [];
+foreach([KeyValue|Rest]) ->
+ {ok, Plus2Space, _} = regexp:gsub(KeyValue,"[\+]"," "),
+ case regexp:split(Plus2Space,"=") of
+ {ok,[Key|Value]} ->
+ [{httpd_util:decode_hex(Key),
+ httpd_util:decode_hex(lists:flatten(Value))}|foreach(Rest)];
+ {ok,_} ->
+ foreach(Rest)
+ end.
+
+
+%% get_addr_and_port
+
+get_addr_and_port(ConfigFile) ->
+ case httpd_conf:load(ConfigFile) of
+ {ok,ConfigList} ->
+ Port = httpd_util:key1search(ConfigList,port,80),
+ Addr = httpd_util:key1search(ConfigList,bind_address),
+ {ok,Addr,Port};
+ Error ->
+ Error
+ end.
+
+
+%% make_name
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd",Addr,Port).
+
+
+%% Multi stuff
+%%
+
+read_multi_file(File) ->
+ read_mfile(file:open(File,[read])).
+
+read_mfile({ok,Fd}) ->
+ read_mfile(read_line(Fd),Fd,[]);
+read_mfile(Error) ->
+ Error.
+
+read_mfile(eof,_Fd,SoFar) ->
+ {ok,lists:reverse(SoFar)};
+read_mfile({error,Reason},_Fd,SoFar) ->
+ {error,Reason};
+read_mfile([$#|Comment],Fd,SoFar) ->
+ read_mfile(read_line(Fd),Fd,SoFar);
+read_mfile([],Fd,SoFar) ->
+ read_mfile(read_line(Fd),Fd,SoFar);
+read_mfile(Line,Fd,SoFar) ->
+ read_mfile(read_line(Fd),Fd,[Line|SoFar]).
+
+read_line(Fd) -> read_line1(io:get_line(Fd,[])).
+read_line1(eof) -> eof;
+read_line1(String) -> httpd_conf:clean(String).
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.hrl
new file mode 100644
index 0000000000..ba21bdf638
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd.hrl
@@ -0,0 +1,77 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd.hrl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
+%%
+
+-include_lib("kernel/include/file.hrl").
+
+-ifndef(SERVER_SOFTWARE).
+-define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile!
+-endif.
+-define(SERVER_PROTOCOL,"HTTP/1.1").
+-define(SOCKET_CHUNK_SIZE,8192).
+-define(SOCKET_MAX_POLL,25).
+-define(FILE_CHUNK_SIZE,64*1024).
+-define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
+-define(DEFAULT_CONTEXT,
+ [{errmsg,"[an error occurred while processing this directive]"},
+ {timefmt,"%A, %d-%b-%y %T %Z"},
+ {sizefmt,"abbrev"}]).
+
+
+-ifdef(inets_error).
+-define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(ERROR(F,A),[]).
+-endif.
+
+-ifdef(inets_log).
+-define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(LOG(F,A),[]).
+-endif.
+
+-ifdef(inets_debug).
+-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(DEBUG(F,A),[]).
+-endif.
+
+-ifdef(inets_cdebug).
+-define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(CDEBUG(F,A),[]).
+-endif.
+
+
+-record(init_data,{peername,resolve}).
+-record(mod,{init_data,
+ data=[],
+ socket_type=ip_comm,
+ socket,
+ config_db,
+ method,
+ absolute_uri=[],
+ request_uri,
+ http_version,
+ request_line,
+ parsed_header=[],
+ entity_body,
+ connection}).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor.erl
new file mode 100644
index 0000000000..9b88f84865
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor.erl
@@ -0,0 +1,176 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_acceptor.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
+%%
+-module(httpd_acceptor).
+
+-include("httpd.hrl").
+-include("httpd_verbosity.hrl").
+
+
+%% External API
+-export([start_link/6]).
+
+%% Other exports (for spawn's etc.)
+-export([acceptor/4, acceptor/7]).
+
+
+%%
+%% External API
+%%
+
+%% start_link
+
+start_link(Manager, SocketType, Addr, Port, ConfigDb, Verbosity) ->
+ Args = [self(), Manager, SocketType, Addr, Port, ConfigDb, Verbosity],
+ proc_lib:start_link(?MODULE, acceptor, Args).
+
+
+acceptor(Parent, Manager, SocketType, Addr, Port, ConfigDb, Verbosity) ->
+ put(sname,acc),
+ put(verbosity,Verbosity),
+ ?vlog("starting",[]),
+ case (catch do_init(SocketType, Addr, Port)) of
+ {ok, ListenSocket} ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ acceptor(Manager, SocketType, ListenSocket, ConfigDb);
+ Error ->
+ proc_lib:init_ack(Parent, Error),
+ error
+ end.
+
+do_init(SocketType, Addr, Port) ->
+ do_socket_start(SocketType),
+ ListenSocket = do_socket_listen(SocketType, Addr, Port),
+ {ok, ListenSocket}.
+
+
+do_socket_start(SocketType) ->
+ case httpd_socket:start(SocketType) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ ?vinfo("failed socket start: ~p",[Reason]),
+ throw({error, {socket_start_failed, Reason}})
+ end.
+
+
+do_socket_listen(SocketType, Addr, Port) ->
+ case httpd_socket:listen(SocketType, Addr, Port) of
+ {error, Reason} ->
+ ?vinfo("failed socket listen operation: ~p", [Reason]),
+ throw({error, {listen, Reason}});
+ ListenSocket ->
+ ListenSocket
+ end.
+
+
+%% acceptor
+
+acceptor(Manager, SocketType, ListenSocket, ConfigDb) ->
+ ?vdebug("await connection",[]),
+ case (catch httpd_socket:accept(SocketType, ListenSocket, 30000)) of
+ {error, Reason} ->
+ handle_error(Reason, ConfigDb, SocketType),
+ ?MODULE:acceptor(Manager, SocketType, ListenSocket, ConfigDb);
+
+ {'EXIT', Reason} ->
+ handle_error({'EXIT', Reason}, ConfigDb, SocketType),
+ ?MODULE:acceptor(Manager, SocketType, ListenSocket, ConfigDb);
+
+ Socket ->
+ handle_connection(Manager, ConfigDb, SocketType, Socket),
+ ?MODULE:acceptor(Manager, SocketType, ListenSocket, ConfigDb)
+ end.
+
+
+handle_connection(Manager, ConfigDb, SocketType, Socket) ->
+ case httpd_request_handler:start_link(Manager, ConfigDb) of
+ {ok, Pid} ->
+ httpd_socket:controlling_process(SocketType, Socket, Pid),
+ httpd_request_handler:synchronize(Pid, SocketType, Socket);
+ {error, Reason} ->
+ handle_connection_err(SocketType, Socket, ConfigDb, Reason)
+ end.
+
+
+handle_connection_err(SocketType, Socket, ConfigDb, Reason) ->
+ String =
+ lists:flatten(
+ io_lib:format("failed starting request handler:~n ~p", [Reason])),
+ report_error(ConfigDb, String),
+ httpd_socket:close(SocketType, Socket).
+
+
+handle_error(timeout, _, _) ->
+ ?vtrace("Accept timeout",[]),
+ ok;
+
+handle_error({enfile, _}, _, _) ->
+ ?vinfo("Accept error: enfile",[]),
+ %% Out of sockets...
+ sleep(200);
+
+handle_error(emfile, _, _) ->
+ ?vinfo("Accept error: emfile",[]),
+ %% Too many open files -> Out of sockets...
+ sleep(200);
+
+handle_error(closed, _, _) ->
+ ?vlog("Accept error: closed",[]),
+ %% This propably only means that the application is stopping,
+ %% but just in case
+ exit(closed);
+
+handle_error(econnaborted, _, _) ->
+ ?vlog("Accept aborted",[]),
+ ok;
+
+handle_error(esslaccept, _, _) ->
+ %% The user has selected to cancel the installation of
+ %% the certifikate, This is not a real error, so we do
+ %% not write an error message.
+ ok;
+
+handle_error({'EXIT', Reason}, ConfigDb, SocketType) ->
+ ?vinfo("Accept exit:~n ~p",[Reason]),
+ String = lists:flatten(io_lib:format("Accept exit: ~p", [Reason])),
+ accept_failed(SocketType, ConfigDb, String);
+
+handle_error(Reason, ConfigDb, SocketType) ->
+ ?vinfo("Accept error:~n ~p",[Reason]),
+ String = lists:flatten(io_lib:format("Accept error: ~p", [Reason])),
+ accept_failed(SocketType, ConfigDb, String).
+
+
+accept_failed(SocketType, ConfigDb, String) ->
+ error_logger:error_report(String),
+ mod_log:error_log(SocketType, undefined, ConfigDb,
+ {0, "unknown"}, String),
+ mod_disk_log:error_log(SocketType, undefined, ConfigDb,
+ {0, "unknown"}, String),
+ exit({accept_failed, String}).
+
+
+report_error(Db, String) ->
+ error_logger:error_report(String),
+ mod_log:report_error(Db, String),
+ mod_disk_log:report_error(Db, String).
+
+
+sleep(T) -> receive after T -> ok end.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor_sup.erl
new file mode 100644
index 0000000000..e408614f1c
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_acceptor_sup.erl
@@ -0,0 +1,118 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_acceptor_sup.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The top supervisor for the Megaco/H.248 application
+%%----------------------------------------------------------------------
+
+-module(httpd_acceptor_sup).
+
+-behaviour(supervisor).
+
+-include("httpd_verbosity.hrl").
+
+%% public
+-export([start/3, stop/1, init/1]).
+
+-export([start_acceptor/4, stop_acceptor/2]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% supervisor callback functions
+
+
+start(Addr, Port, AccSupVerbosity) ->
+ SupName = make_name(Addr, Port),
+ supervisor:start_link({local, SupName}, ?MODULE, [AccSupVerbosity]).
+
+stop(StartArgs) ->
+ ok.
+
+init([Verbosity]) -> % Supervisor
+ do_init(Verbosity);
+init(BadArg) ->
+ {error, {badarg, BadArg}}.
+
+do_init(Verbosity) ->
+ put(verbosity,?vvalidate(Verbosity)),
+ put(sname,acc_sup),
+ ?vlog("starting", []),
+ Flags = {one_for_one, 500, 100},
+ KillAfter = timer:seconds(1),
+ Workers = [],
+ {ok, {Flags, Workers}}.
+
+
+%%----------------------------------------------------------------------
+%% Function: [start|stop]_acceptor/5
+%% Description: Starts a [auth | security] worker (child) process
+%%----------------------------------------------------------------------
+
+start_acceptor(SocketType, Addr, Port, ConfigDb) ->
+ Verbosity = get_acc_verbosity(),
+ start_worker(httpd_acceptor, SocketType, Addr, Port,
+ ConfigDb, Verbosity, self(), []).
+
+stop_acceptor(Addr, Port) ->
+ stop_worker(httpd_acceptor, Addr, Port).
+
+
+%%----------------------------------------------------------------------
+%% Function: start_worker/5
+%% Description: Starts a (permanent) worker (child) process
+%%----------------------------------------------------------------------
+
+start_worker(M, SocketType, Addr, Port, ConfigDB, Verbosity, Manager,
+ Modules) ->
+ SupName = make_name(Addr, Port),
+ Args = [Manager, SocketType, Addr, Port, ConfigDB, Verbosity],
+ Spec = {{M, Addr, Port},
+ {M, start_link, Args},
+ permanent, timer:seconds(1), worker, [M] ++ Modules},
+ supervisor:start_child(SupName, Spec).
+
+
+%%----------------------------------------------------------------------
+%% Function: stop_permanent_worker/3
+%% Description: Stops a permanent worker (child) process
+%%----------------------------------------------------------------------
+
+stop_worker(M, Addr, Port) ->
+ SupName = make_name(Addr, Port),
+ Name = {M, Addr, Port},
+ case supervisor:terminate_child(SupName, Name) of
+ ok ->
+ supervisor:delete_child(SupName, Name);
+ Error ->
+ Error
+ end.
+
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd_acc_sup",Addr,Port).
+
+
+
+get_acc_verbosity() ->
+ get_verbosity(get(acceptor_verbosity)).
+
+get_verbosity(undefined) ->
+ ?default_verbosity;
+get_verbosity(V) ->
+ ?vvalidate(V).
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_conf.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_conf.erl
new file mode 100644
index 0000000000..2c7a747d42
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_conf.erl
@@ -0,0 +1,688 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_conf.erl,v 1.1 2008/12/17 09:53:33 mikpe Exp $
+%%
+-module(httpd_conf).
+-export([load/1, load_mime_types/1,
+ load/2, store/1, store/2,
+ remove_all/1, remove/1,
+ is_directory/1, is_file/1,
+ make_integer/1, clean/1, custom_clean/3, check_enum/2]).
+
+
+-define(VMODULE,"CONF").
+-include("httpd_verbosity.hrl").
+
+%% The configuration data is handled in three (3) phases:
+%% 1. Parse the config file and put all directives into a key-vale
+%% tuple list (load/1).
+%% 2. Traverse the key-value tuple list store it into an ETS table.
+%% Directives depending on other directives are taken care of here
+%% (store/1).
+%% 3. Traverse the ETS table and do a complete clean-up (remove/1).
+
+-include("httpd.hrl").
+
+%%
+%% Phase 1: Load
+%%
+
+%% load
+
+load(ConfigFile) ->
+ ?CDEBUG("load -> ConfigFile: ~p",[ConfigFile]),
+ case read_config_file(ConfigFile) of
+ {ok, Config} ->
+ case bootstrap(Config) of
+ {error, Reason} ->
+ {error, Reason};
+ {ok, Modules} ->
+ load_config(Config, lists:append(Modules, [?MODULE]))
+ end;
+ {error, Reason} ->
+ {error, ?NICE("Error while reading config file: "++Reason)}
+ end.
+
+
+bootstrap([]) ->
+ {error, ?NICE("Modules must be specified in the config file")};
+bootstrap([Line|Config]) ->
+ case Line of
+ [$M,$o,$d,$u,$l,$e,$s,$ |Modules] ->
+ {ok, ModuleList} = regexp:split(Modules," "),
+ TheMods = [list_to_atom(X) || X <- ModuleList],
+ case verify_modules(TheMods) of
+ ok ->
+ {ok, TheMods};
+ {error, Reason} ->
+ ?ERROR("bootstrap -> : validation failed: ~p",[Reason]),
+ {error, Reason}
+ end;
+ _ ->
+ bootstrap(Config)
+ end.
+
+
+%%
+%% verify_modules/1 -> ok | {error, Reason}
+%%
+%% Verifies that all specified modules are available.
+%%
+verify_modules([]) ->
+ ok;
+verify_modules([Mod|Rest]) ->
+ case code:which(Mod) of
+ non_existing ->
+ {error, ?NICE(atom_to_list(Mod)++" does not exist")};
+ Path ->
+ verify_modules(Rest)
+ end.
+
+%%
+%% read_config_file/1 -> {ok, [line(), line()..]} | {error, Reason}
+%%
+%% Reads the entire configuration file and returns list of strings or
+%% and error.
+%%
+
+
+read_config_file(FileName) ->
+ case file:open(FileName, [read]) of
+ {ok, Stream} ->
+ read_config_file(Stream, []);
+ {error, Reason} ->
+ {error, ?NICE("Cannot open "++FileName)}
+ end.
+
+read_config_file(Stream, SoFar) ->
+ case io:get_line(Stream, []) of
+ eof ->
+ {ok, lists:reverse(SoFar)};
+ {error, Reason} ->
+ {error, Reason};
+ [$#|Rest] ->
+ %% Ignore commented lines for efficiency later ..
+ read_config_file(Stream, SoFar);
+ Line ->
+ {ok, NewLine, _}=regexp:sub(clean(Line),"[\t\r\f ]"," "),
+ case NewLine of
+ [] ->
+ %% Also ignore empty lines ..
+ read_config_file(Stream, SoFar);
+ Other ->
+ read_config_file(Stream, [NewLine|SoFar])
+ end
+ end.
+
+is_exported(Module, ToFind) ->
+ Exports = Module:module_info(exports),
+ lists:member(ToFind, Exports).
+
+%%
+%% load/4 -> {ok, ConfigList} | {error, Reason}
+%%
+%% This loads the config file into each module specified by Modules
+%% Each module has its own context that is passed to and (optionally)
+%% returned by the modules load function. The module can also return
+%% a ConfigEntry, which will be added to the global configuration
+%% list.
+%% All configuration directives are guaranteed to be passed to all
+%% modules. Each module only implements the function clauses of
+%% the load function for the configuration directives it supports,
+%% it's ok if an apply returns {'EXIT', {function_clause, ..}}.
+%%
+load_config(Config, Modules) ->
+ %% Create default contexts for all modules
+ Contexts = lists:duplicate(length(Modules), []),
+ load_config(Config, Modules, Contexts, []).
+
+
+load_config([], _Modules, _Contexts, ConfigList) ->
+ case a_must(ConfigList, [server_name,port,server_root,document_root]) of
+ ok ->
+ {ok, ConfigList};
+ {missing, Directive} ->
+ {error, ?NICE(atom_to_list(Directive)++
+ " must be specified in the config file")}
+ end;
+
+load_config([Line|Config], Modules, Contexts, ConfigList) ->
+ ?CDEBUG("load_config -> Line: ~p",[Line]),
+ case load_traverse(Line, Contexts, Modules, [], ConfigList, no) of
+ {ok, NewContexts, NewConfigList} ->
+ load_config(Config, Modules, NewContexts, NewConfigList);
+ {error, Reason} ->
+ ?ERROR("load_config -> traverse failed: ~p",[Reason]),
+ {error, Reason}
+ end.
+
+
+load_traverse(Line, [], [], NewContexts, ConfigList, no) ->
+ ?CDEBUG("load_traverse/no -> ~n"
+ " Line: ~p~n"
+ " NewContexts: ~p~n"
+ " ConfigList: ~p",
+ [Line,NewContexts,ConfigList]),
+ {error, ?NICE("Configuration directive not recognized: "++Line)};
+load_traverse(Line, [], [], NewContexts, ConfigList, yes) ->
+ ?CDEBUG("load_traverse/yes -> ~n"
+ " Line: ~p~n"
+ " NewContexts: ~p~n"
+ " ConfigList: ~p",
+ [Line,NewContexts,ConfigList]),
+ {ok, lists:reverse(NewContexts), ConfigList};
+load_traverse(Line, [Context|Contexts], [Module|Modules], NewContexts, ConfigList, State) ->
+ ?CDEBUG("load_traverse/~p -> ~n"
+ " Line: ~p~n"
+ " Module: ~p~n"
+ " Context: ~p~n"
+ " Contexts: ~p~n"
+ " NewContexts: ~p",
+ [State,Line,Module,Context,Contexts,NewContexts]),
+ case is_exported(Module, {load, 2}) of
+ true ->
+ ?CDEBUG("load_traverse -> ~p:load/2 exported",[Module]),
+ case catch apply(Module, load, [Line, Context]) of
+ {'EXIT', {function_clause, _}} ->
+ ?CDEBUG("load_traverse -> exit: function_clause"
+ "~n Module: ~p"
+ "~n Line: ~s",[Module,Line]),
+ load_traverse(Line, Contexts, Modules, [Context|NewContexts], ConfigList, State);
+ {'EXIT', Reason} ->
+ ?CDEBUG("load_traverse -> exit: ~p",[Reason]),
+ error_logger:error_report({'EXIT', Reason}),
+ load_traverse(Line, Contexts, Modules, [Context|NewContexts], ConfigList, State);
+ {ok, NewContext} ->
+ ?CDEBUG("load_traverse -> ~n"
+ " NewContext: ~p",[NewContext]),
+ load_traverse(Line, Contexts, Modules, [NewContext|NewContexts], ConfigList,yes);
+ {ok, NewContext, ConfigEntry} when tuple(ConfigEntry) ->
+ ?CDEBUG("load_traverse (tuple) -> ~n"
+ " NewContext: ~p~n"
+ " ConfigEntry: ~p",[NewContext,ConfigEntry]),
+ load_traverse(Line, Contexts, Modules, [NewContext|NewContexts],
+ [ConfigEntry|ConfigList], yes);
+ {ok, NewContext, ConfigEntry} when list(ConfigEntry) ->
+ ?CDEBUG("load_traverse (list) -> ~n"
+ " NewContext: ~p~n"
+ " ConfigEntry: ~p",[NewContext,ConfigEntry]),
+ load_traverse(Line, Contexts, Modules, [NewContext|NewContexts],
+ lists:append(ConfigEntry, ConfigList), yes);
+ {error, Reason} ->
+ ?CDEBUG("load_traverse -> error: ~p",[Reason]),
+ {error, Reason}
+ end;
+ false ->
+ ?CDEBUG("load_traverse -> ~p:load/2 not exported",[Module]),
+ load_traverse(Line, Contexts, Modules, [Context|NewContexts],
+ ConfigList,yes)
+ end.
+
+
+load(eof, []) ->
+ eof;
+
+load([$M,$a,$x,$H,$e,$a,$d,$e,$r,$S,$i,$z,$e,$ |MaxHeaderSize], []) ->
+ ?DEBUG("load -> MaxHeaderSize: ~p",[MaxHeaderSize]),
+ case make_integer(MaxHeaderSize) of
+ {ok, Integer} ->
+ {ok, [], {max_header_size,Integer}};
+ {error, _} ->
+ {error, ?NICE(clean(MaxHeaderSize)++
+ " is an invalid number of MaxHeaderSize")}
+ end;
+load([$M,$a,$x,$H,$e,$a,$d,$e,$r,$A,$c,$t,$i,$o,$n,$ |Action], []) ->
+ ?DEBUG("load -> MaxHeaderAction: ~p",[Action]),
+ {ok, [], {max_header_action,list_to_atom(clean(Action))}};
+load([$M,$a,$x,$B,$o,$d,$y,$S,$i,$z,$e,$ |MaxBodySize], []) ->
+ ?DEBUG("load -> MaxBodySize: ~p",[MaxBodySize]),
+ case make_integer(MaxBodySize) of
+ {ok, Integer} ->
+ {ok, [], {max_body_size,Integer}};
+ {error, _} ->
+ {error, ?NICE(clean(MaxBodySize)++
+ " is an invalid number of MaxBodySize")}
+ end;
+load([$M,$a,$x,$B,$o,$d,$y,$A,$c,$t,$i,$o,$n,$ |Action], []) ->
+ ?DEBUG("load -> MaxBodyAction: ~p",[Action]),
+ {ok, [], {max_body_action,list_to_atom(clean(Action))}};
+load([$S,$e,$r,$v,$e,$r,$N,$a,$m,$e,$ |ServerName], []) ->
+ ?DEBUG("load -> ServerName: ~p",[ServerName]),
+ {ok,[],{server_name,clean(ServerName)}};
+load([$S,$o,$c,$k,$e,$t,$T,$y,$p,$e,$ |SocketType], []) ->
+ ?DEBUG("load -> SocketType: ~p",[SocketType]),
+ case check_enum(clean(SocketType),["ssl","ip_comm"]) of
+ {ok, ValidSocketType} ->
+ {ok, [], {com_type,ValidSocketType}};
+ {error,_} ->
+ {error, ?NICE(clean(SocketType) ++ " is an invalid SocketType")}
+ end;
+load([$P,$o,$r,$t,$ |Port], []) ->
+ ?DEBUG("load -> Port: ~p",[Port]),
+ case make_integer(Port) of
+ {ok, Integer} ->
+ {ok, [], {port,Integer}};
+ {error, _} ->
+ {error, ?NICE(clean(Port)++" is an invalid Port")}
+ end;
+load([$B,$i,$n,$d,$A,$d,$d,$r,$e,$s,$s,$ |Address], []) ->
+ ?DEBUG("load -> Address: ~p",[Address]),
+ case clean(Address) of
+ "*" ->
+ {ok, [], {bind_address,any}};
+ CAddress ->
+ ?CDEBUG("load -> CAddress: ~p",[CAddress]),
+ case inet:getaddr(CAddress,inet) of
+ {ok, IPAddr} ->
+ ?CDEBUG("load -> IPAddr: ~p",[IPAddr]),
+ {ok, [], {bind_address,IPAddr}};
+ {error, _} ->
+ {error, ?NICE(CAddress++" is an invalid address")}
+ end
+ end;
+load([$K,$e,$e,$p,$A,$l,$i,$v,$e,$ |OnorOff], []) ->
+ case list_to_atom(clean(OnorOff)) of
+ off ->
+ {ok, [], {persistent_conn, false}};
+ _ ->
+ {ok, [], {persistent_conn, true}}
+ end;
+load([$M,$a,$x,$K,$e,$e,$p,$A,$l,$i,$v,$e,$R,$e,$q,$u,$e,$s,$t,$ |MaxRequests], []) ->
+ case make_integer(MaxRequests) of
+ {ok, Integer} ->
+ {ok, [], {max_keep_alive_request, Integer}};
+ {error, _} ->
+ {error, ?NICE(clean(MaxRequests)++" is an invalid MaxKeepAliveRequest")}
+ end;
+load([$K,$e,$e,$p,$A,$l,$i,$v,$e,$T,$i,$m,$e,$o,$u,$t,$ |Timeout], []) ->
+ case make_integer(Timeout) of
+ {ok, Integer} ->
+ {ok, [], {keep_alive_timeout, Integer*1000}};
+ {error, _} ->
+ {error, ?NICE(clean(Timeout)++" is an invalid KeepAliveTimeout")}
+ end;
+load([$M,$o,$d,$u,$l,$e,$s,$ |Modules], []) ->
+ {ok, ModuleList} = regexp:split(Modules," "),
+ {ok, [], {modules,[list_to_atom(X) || X <- ModuleList]}};
+load([$S,$e,$r,$v,$e,$r,$A,$d,$m,$i,$n,$ |ServerAdmin], []) ->
+ {ok, [], {server_admin,clean(ServerAdmin)}};
+load([$S,$e,$r,$v,$e,$r,$R,$o,$o,$t,$ |ServerRoot], []) ->
+ case is_directory(clean(ServerRoot)) of
+ {ok, Directory} ->
+ MimeTypesFile =
+ filename:join([clean(ServerRoot),"conf", "mime.types"]),
+ case load_mime_types(MimeTypesFile) of
+ {ok, MimeTypesList} ->
+ {ok, [], [{server_root,string:strip(Directory,right,$/)},
+ {mime_types,MimeTypesList}]};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, _} ->
+ {error, ?NICE(clean(ServerRoot)++" is an invalid ServerRoot")}
+ end;
+load([$M,$a,$x,$C,$l,$i,$e,$n,$t,$s,$ |MaxClients], []) ->
+ ?DEBUG("load -> MaxClients: ~p",[MaxClients]),
+ case make_integer(MaxClients) of
+ {ok, Integer} ->
+ {ok, [], {max_clients,Integer}};
+ {error, _} ->
+ {error, ?NICE(clean(MaxClients)++" is an invalid number of MaxClients")}
+ end;
+load([$D,$o,$c,$u,$m,$e,$n,$t,$R,$o,$o,$t,$ |DocumentRoot],[]) ->
+ case is_directory(clean(DocumentRoot)) of
+ {ok, Directory} ->
+ {ok, [], {document_root,string:strip(Directory,right,$/)}};
+ {error, _} ->
+ {error, ?NICE(clean(DocumentRoot)++"is an invalid DocumentRoot")}
+ end;
+load([$D,$e,$f,$a,$u,$l,$t,$T,$y,$p,$e,$ |DefaultType], []) ->
+ {ok, [], {default_type,clean(DefaultType)}};
+load([$S,$S,$L,$C,$e,$r,$t,$i,$f,$i,$c,$a,$t,$e,$F,$i,$l,$e,$ | SSLCertificateFile], []) ->
+ ?DEBUG("load -> SSLCertificateFile: ~p",[SSLCertificateFile]),
+ case is_file(clean(SSLCertificateFile)) of
+ {ok, File} ->
+ {ok, [], {ssl_certificate_file,File}};
+ {error, _} ->
+ {error, ?NICE(clean(SSLCertificateFile)++
+ " is an invalid SSLCertificateFile")}
+ end;
+load([$S,$S,$L,$C,$e,$r,$t,$i,$f,$i,$c,$a,$t,$e,$K,$e,$y,$F,$i,$l,$e,$ |
+ SSLCertificateKeyFile], []) ->
+ ?DEBUG("load -> SSLCertificateKeyFile: ~p",[SSLCertificateKeyFile]),
+ case is_file(clean(SSLCertificateKeyFile)) of
+ {ok, File} ->
+ {ok, [], {ssl_certificate_key_file,File}};
+ {error, _} ->
+ {error, ?NICE(clean(SSLCertificateKeyFile)++
+ " is an invalid SSLCertificateKeyFile")}
+ end;
+load([$S,$S,$L,$V,$e,$r,$i,$f,$y,$C,$l,$i,$e,$n,$t,$ |SSLVerifyClient], []) ->
+ ?DEBUG("load -> SSLVerifyClient: ~p",[SSLVerifyClient]),
+ case make_integer(clean(SSLVerifyClient)) of
+ {ok, Integer} when Integer >=0,Integer =< 2 ->
+ {ok, [], {ssl_verify_client,Integer}};
+ {ok, Integer} ->
+ {error,?NICE(clean(SSLVerifyClient)++" is an invalid SSLVerifyClient")};
+ {error, nomatch} ->
+ {error,?NICE(clean(SSLVerifyClient)++" is an invalid SSLVerifyClient")}
+ end;
+load([$S,$S,$L,$V,$e,$r,$i,$f,$y,$D,$e,$p,$t,$h,$ |
+ SSLVerifyDepth], []) ->
+ ?DEBUG("load -> SSLVerifyDepth: ~p",[SSLVerifyDepth]),
+ case make_integer(clean(SSLVerifyDepth)) of
+ {ok, Integer} when Integer > 0 ->
+ {ok, [], {ssl_verify_client_depth,Integer}};
+ {ok, Integer} ->
+ {error,?NICE(clean(SSLVerifyDepth) ++
+ " is an invalid SSLVerifyDepth")};
+ {error, nomatch} ->
+ {error,?NICE(clean(SSLVerifyDepth) ++
+ " is an invalid SSLVerifyDepth")}
+ end;
+load([$S,$S,$L,$C,$i,$p,$h,$e,$r,$s,$ | SSLCiphers], []) ->
+ ?DEBUG("load -> SSLCiphers: ~p",[SSLCiphers]),
+ {ok, [], {ssl_ciphers, clean(SSLCiphers)}};
+load([$S,$S,$L,$C,$A,$C,$e,$r,$t,$i,$f,$i,$c,$a,$t,$e,$F,$i,$l,$e,$ |
+ SSLCACertificateFile], []) ->
+ case is_file(clean(SSLCACertificateFile)) of
+ {ok, File} ->
+ {ok, [], {ssl_ca_certificate_file,File}};
+ {error, _} ->
+ {error, ?NICE(clean(SSLCACertificateFile)++
+ " is an invalid SSLCACertificateFile")}
+ end;
+load([$S,$S,$L,$P,$a,$s,$s,$w,$o,$r,$d,$C,$a,$l,$l,$b,$a,$c,$k,$M,$o,$d,$u,$l,$e,$ | SSLPasswordCallbackModule], []) ->
+ ?DEBUG("load -> SSLPasswordCallbackModule: ~p",
+ [SSLPasswordCallbackModule]),
+ {ok, [], {ssl_password_callback_module,
+ list_to_atom(clean(SSLPasswordCallbackModule))}};
+load([$S,$S,$L,$P,$a,$s,$s,$w,$o,$r,$d,$C,$a,$l,$l,$b,$a,$c,$k,$F,$u,$n,$c,$t,$i,$o,$n,$ | SSLPasswordCallbackFunction], []) ->
+ ?DEBUG("load -> SSLPasswordCallbackFunction: ~p",
+ [SSLPasswordCallbackFunction]),
+ {ok, [], {ssl_password_callback_function,
+ list_to_atom(clean(SSLPasswordCallbackFunction))}}.
+
+
+%%
+%% load_mime_types/1 -> {ok, MimeTypes} | {error, Reason}
+%%
+load_mime_types(MimeTypesFile) ->
+ case file:open(MimeTypesFile, [read]) of
+ {ok, Stream} ->
+ parse_mime_types(Stream, []);
+ {error, _} ->
+ {error, ?NICE("Can't open " ++ MimeTypesFile)}
+ end.
+
+parse_mime_types(Stream,MimeTypesList) ->
+ Line=
+ case io:get_line(Stream,'') of
+ eof ->
+ eof;
+ String ->
+ clean(String)
+ end,
+ parse_mime_types(Stream, MimeTypesList, Line).
+
+parse_mime_types(Stream, MimeTypesList, eof) ->
+ file:close(Stream),
+ {ok, MimeTypesList};
+parse_mime_types(Stream, MimeTypesList, "") ->
+ parse_mime_types(Stream, MimeTypesList);
+parse_mime_types(Stream, MimeTypesList, [$#|_]) ->
+ parse_mime_types(Stream, MimeTypesList);
+parse_mime_types(Stream, MimeTypesList, Line) ->
+ case regexp:split(Line, " ") of
+ {ok, [NewMimeType|Suffixes]} ->
+ parse_mime_types(Stream,lists:append(suffixes(NewMimeType,Suffixes),
+ MimeTypesList));
+ {ok, _} ->
+ {error, ?NICE(Line)}
+ end.
+
+suffixes(MimeType,[]) ->
+ [];
+suffixes(MimeType,[Suffix|Rest]) ->
+ [{Suffix,MimeType}|suffixes(MimeType,Rest)].
+
+%%
+%% Phase 2: Store
+%%
+
+%% store
+
+store(ConfigList) ->
+ Modules = httpd_util:key1search(ConfigList, modules, []),
+ Port = httpd_util:key1search(ConfigList, port),
+ Addr = httpd_util:key1search(ConfigList,bind_address),
+ Name = httpd_util:make_name("httpd_conf",Addr,Port),
+ ?CDEBUG("store -> Name = ~p",[Name]),
+ ConfigDB = ets:new(Name, [named_table, bag, protected]),
+ ?CDEBUG("store -> ConfigDB = ~p",[ConfigDB]),
+ store(ConfigDB, ConfigList, lists:append(Modules,[?MODULE]),ConfigList).
+
+store(ConfigDB, ConfigList, Modules,[]) ->
+ ?vtrace("store -> done",[]),
+ ?CDEBUG("store -> done",[]),
+ {ok, ConfigDB};
+store(ConfigDB, ConfigList, Modules, [ConfigListEntry|Rest]) ->
+ ?vtrace("store -> entry with"
+ "~n ConfigListEntry: ~p",[ConfigListEntry]),
+ ?CDEBUG("store -> "
+ "~n ConfigListEntry: ~p",[ConfigListEntry]),
+ case store_traverse(ConfigListEntry,ConfigList,Modules) of
+ {ok, ConfigDBEntry} when tuple(ConfigDBEntry) ->
+ ?vtrace("store -> ConfigDBEntry(tuple): "
+ "~n ~p",[ConfigDBEntry]),
+ ?CDEBUG("store -> ConfigDBEntry(tuple): "
+ "~n ~p",[ConfigDBEntry]),
+ ets:insert(ConfigDB,ConfigDBEntry),
+ store(ConfigDB,ConfigList,Modules,Rest);
+ {ok, ConfigDBEntry} when list(ConfigDBEntry) ->
+ ?vtrace("store -> ConfigDBEntry(list): "
+ "~n ~p",[ConfigDBEntry]),
+ ?CDEBUG("store -> ConfigDBEntry(list): "
+ "~n ~p",[ConfigDBEntry]),
+ lists:foreach(fun(Entry) ->
+ ets:insert(ConfigDB,Entry)
+ end,ConfigDBEntry),
+ store(ConfigDB,ConfigList,Modules,Rest);
+ {error, Reason} ->
+ ?vlog("store -> error: ~p",[Reason]),
+ ?ERROR("store -> error: ~p",[Reason]),
+ {error,Reason}
+ end.
+
+store_traverse(ConfigListEntry,ConfigList,[]) ->
+ {error,?NICE("Unable to store configuration...")};
+store_traverse(ConfigListEntry, ConfigList, [Module|Rest]) ->
+ case is_exported(Module, {store, 2}) of
+ true ->
+ ?CDEBUG("store_traverse -> call ~p:store/2",[Module]),
+ case catch apply(Module,store,[ConfigListEntry, ConfigList]) of
+ {'EXIT',{function_clause,_}} ->
+ ?CDEBUG("store_traverse -> exit: function_clause",[]),
+ store_traverse(ConfigListEntry,ConfigList,Rest);
+ {'EXIT',Reason} ->
+ ?ERROR("store_traverse -> exit: ~p",[Reason]),
+ error_logger:error_report({'EXIT',Reason}),
+ store_traverse(ConfigListEntry,ConfigList,Rest);
+ Result ->
+ ?CDEBUG("store_traverse -> ~n"
+ " Result: ~p",[Result]),
+ Result
+ end;
+ false ->
+ store_traverse(ConfigListEntry,ConfigList,Rest)
+ end.
+
+store({mime_types,MimeTypesList},ConfigList) ->
+ Port = httpd_util:key1search(ConfigList, port),
+ Addr = httpd_util:key1search(ConfigList, bind_address),
+ Name = httpd_util:make_name("httpd_mime",Addr,Port),
+ ?CDEBUG("store(mime_types) -> Name: ~p",[Name]),
+ {ok, MimeTypesDB} = store_mime_types(Name,MimeTypesList),
+ ?CDEBUG("store(mime_types) -> ~n"
+ " MimeTypesDB: ~p~n"
+ " MimeTypesDB info: ~p",
+ [MimeTypesDB,ets:info(MimeTypesDB)]),
+ {ok, {mime_types,MimeTypesDB}};
+store(ConfigListEntry,ConfigList) ->
+ ?CDEBUG("store/2 -> ~n"
+ " ConfigListEntry: ~p~n"
+ " ConfigList: ~p",
+ [ConfigListEntry,ConfigList]),
+ {ok, ConfigListEntry}.
+
+
+%% store_mime_types
+store_mime_types(Name,MimeTypesList) ->
+ ?CDEBUG("store_mime_types -> Name: ~p",[Name]),
+ MimeTypesDB = ets:new(Name, [set, protected]),
+ ?CDEBUG("store_mime_types -> MimeTypesDB: ~p",[MimeTypesDB]),
+ store_mime_types1(MimeTypesDB, MimeTypesList).
+
+store_mime_types1(MimeTypesDB,[]) ->
+ {ok, MimeTypesDB};
+store_mime_types1(MimeTypesDB,[Type|Rest]) ->
+ ?CDEBUG("store_mime_types1 -> Type: ~p",[Type]),
+ ets:insert(MimeTypesDB, Type),
+ store_mime_types1(MimeTypesDB, Rest).
+
+
+%%
+%% Phase 3: Remove
+%%
+
+remove_all(ConfigDB) ->
+ Modules = httpd_util:lookup(ConfigDB,modules,[]),
+ remove_traverse(ConfigDB, lists:append(Modules,[?MODULE])).
+
+remove_traverse(ConfigDB,[]) ->
+ ?vtrace("remove_traverse -> done", []),
+ ok;
+remove_traverse(ConfigDB,[Module|Rest]) ->
+ ?vtrace("remove_traverse -> call ~p:remove", [Module]),
+ case (catch apply(Module,remove,[ConfigDB])) of
+ {'EXIT',{undef,_}} ->
+ ?vtrace("remove_traverse -> undef", []),
+ remove_traverse(ConfigDB,Rest);
+ {'EXIT',{function_clause,_}} ->
+ ?vtrace("remove_traverse -> function_clause", []),
+ remove_traverse(ConfigDB,Rest);
+ {'EXIT',Reason} ->
+ ?vtrace("remove_traverse -> exit: ~p", [Reason]),
+ error_logger:error_report({'EXIT',Reason}),
+ remove_traverse(ConfigDB,Rest);
+ {error,Reason} ->
+ ?vtrace("remove_traverse -> error: ~p", [Reason]),
+ error_logger:error_report(Reason),
+ remove_traverse(ConfigDB,Rest);
+ _ ->
+ remove_traverse(ConfigDB,Rest)
+ end.
+
+remove(ConfigDB) ->
+ ets:delete(ConfigDB),
+ ok.
+
+
+%%
+%% Utility functions
+%%
+
+%% is_directory
+
+is_directory(Directory) ->
+ case file:read_file_info(Directory) of
+ {ok,FileInfo} ->
+ #file_info{type = Type, access = Access} = FileInfo,
+ is_directory(Type,Access,FileInfo,Directory);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+is_directory(directory,read,_FileInfo,Directory) ->
+ {ok,Directory};
+is_directory(directory,read_write,_FileInfo,Directory) ->
+ {ok,Directory};
+is_directory(_Type,_Access,FileInfo,_Directory) ->
+ {error,FileInfo}.
+
+
+%% is_file
+
+is_file(File) ->
+ case file:read_file_info(File) of
+ {ok,FileInfo} ->
+ #file_info{type = Type, access = Access} = FileInfo,
+ is_file(Type,Access,FileInfo,File);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+is_file(regular,read,_FileInfo,File) ->
+ {ok,File};
+is_file(regular,read_write,_FileInfo,File) ->
+ {ok,File};
+is_file(_Type,_Access,FileInfo,_File) ->
+ {error,FileInfo}.
+
+%% make_integer
+
+make_integer(String) ->
+ case regexp:match(clean(String),"[0-9]+") of
+ {match, _, _} ->
+ {ok, list_to_integer(clean(String))};
+ nomatch ->
+ {error, nomatch}
+ end.
+
+
+%% clean
+
+clean(String) ->
+ {ok,CleanedString,_} = regexp:gsub(String, "^[ \t\n\r\f]*|[ \t\n\r\f]*\$",""),
+ CleanedString.
+
+%% custom_clean
+
+custom_clean(String,MoreBefore,MoreAfter) ->
+ {ok,CleanedString,_}=regexp:gsub(String,"^[ \t\n\r\f"++MoreBefore++
+ "]*|[ \t\n\r\f"++MoreAfter++"]*\$",""),
+ CleanedString.
+
+%% check_enum
+
+check_enum(Enum,[]) ->
+ {error, not_valid};
+check_enum(Enum,[Enum|Rest]) ->
+ {ok, list_to_atom(Enum)};
+check_enum(Enum, [NotValid|Rest]) ->
+ check_enum(Enum, Rest).
+
+%% a_must
+
+a_must(ConfigList,[]) ->
+ ok;
+a_must(ConfigList,[Directive|Rest]) ->
+ case httpd_util:key1search(ConfigList,Directive) of
+ undefined ->
+ {missing,Directive};
+ _ ->
+ a_must(ConfigList,Rest)
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_example.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_example.erl
new file mode 100644
index 0000000000..1819650963
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_example.erl
@@ -0,0 +1,134 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_example.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_example).
+-export([print/1]).
+-export([get/2, post/2, yahoo/2, test1/2]).
+
+-export([newformat/3]).
+%% These are used by the inets test-suite
+-export([delay/1]).
+
+
+print(String) ->
+ [header(),
+ top("Print"),
+ String++"\n",
+ footer()].
+
+
+test1(Env, []) ->
+ io:format("Env:~p~n",[Env]),
+ ["<html>",
+ "<head>",
+ "<title>Test1</title>",
+ "</head>",
+ "<body>",
+ "<h1>Erlang Body</h1>",
+ "<h2>Stuff</h2>",
+ "</body>",
+ "</html>"].
+
+
+get(Env,[]) ->
+ [header(),
+ top("GET Example"),
+ "<FORM ACTION=\"/cgi-bin/erl/httpd_example:get\" METHOD=GET>
+<B>Input:</B> <INPUT TYPE=\"text\" NAME=\"input1\">
+<INPUT TYPE=\"text\" NAME=\"input2\">
+<INPUT TYPE=\"submit\"><BR>
+</FORM>" ++ "\n",
+ footer()];
+
+get(Env,Input) ->
+ default(Env,Input).
+
+post(Env,[]) ->
+ [header(),
+ top("POST Example"),
+ "<FORM ACTION=\"/cgi-bin/erl/httpd_example:post\" METHOD=POST>
+<B>Input:</B> <INPUT TYPE=\"text\" NAME=\"input1\">
+<INPUT TYPE=\"text\" NAME=\"input2\">
+<INPUT TYPE=\"submit\"><BR>
+</FORM>" ++ "\n",
+ footer()];
+
+post(Env,Input) ->
+ default(Env,Input).
+
+yahoo(Env,Input) ->
+ "Location: http://www.yahoo.com\r\n\r\n".
+
+default(Env,Input) ->
+ [header(),
+ top("Default Example"),
+ "<B>Environment:</B> ",io_lib:format("~p",[Env]),"<BR>\n",
+ "<B>Input:</B> ",Input,"<BR>\n",
+ "<B>Parsed Input:</B> ",
+ io_lib:format("~p",[httpd:parse_query(Input)]),"\n",
+ footer()].
+
+header() ->
+ header("text/html").
+header(MimeType) ->
+ "Content-type: " ++ MimeType ++ "\r\n\r\n".
+
+top(Title) ->
+ "<HTML>
+<HEAD>
+<TITLE>" ++ Title ++ "</TITLE>
+</HEAD>
+<BODY>\n".
+
+footer() ->
+ "</BODY>
+</HTML>\n".
+
+
+newformat(SessionID,Env,Input)->
+ mod_esi:deliver(SessionID,"Content-Type:text/html\r\n\r\n"),
+ mod_esi:deliver(SessionID,top("new esi format test")),
+ mod_esi:deliver(SessionID,"This new format is nice<BR>"),
+ mod_esi:deliver(SessionID,"This new format is nice<BR>"),
+ mod_esi:deliver(SessionID,"This new format is nice<BR>"),
+ mod_esi:deliver(SessionID,footer()).
+
+%% ------------------------------------------------------
+
+delay(Time) when integer(Time) ->
+ i("httpd_example:delay(~p) -> do the delay",[Time]),
+ sleep(Time),
+ i("httpd_example:delay(~p) -> done, now reply",[Time]),
+ delay_reply("delay ok");
+delay(Time) when list(Time) ->
+ delay(httpd_conf:make_integer(Time));
+delay({ok,Time}) when integer(Time) ->
+ delay(Time);
+delay({error,_Reason}) ->
+ i("delay -> called with invalid time"),
+ delay_reply("delay failed: invalid delay time").
+
+delay_reply(Reply) ->
+ [header(),
+ top("delay"),
+ Reply,
+ footer()].
+
+i(F) -> i(F,[]).
+i(F,A) -> io:format(F ++ "~n",A).
+
+sleep(T) -> receive after T -> ok end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_manager.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_manager.erl
new file mode 100644
index 0000000000..78750c32c9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_manager.erl
@@ -0,0 +1,1030 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_manager.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+
+-module(httpd_manager).
+
+-include("httpd.hrl").
+-include("httpd_verbosity.hrl").
+
+-behaviour(gen_server).
+
+%% External API
+-export([start/2, start/3, start_link/2, start_link/3, stop/1, restart/1]).
+
+%% Internal API
+-export([new_connection/1, done_connection/1]).
+
+%% Module API
+-export([config_lookup/2, config_lookup/3,
+ config_multi_lookup/2, config_multi_lookup/3,
+ config_match/2, config_match/3]).
+
+%% gen_server exports
+-export([init/1,
+ handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+
+%% Management exports
+-export([block/2, block/3, unblock/1]).
+-export([get_admin_state/1, get_usage_state/1]).
+-export([is_busy/1,is_busy/2,is_busy_or_blocked/1,is_blocked/1]). %% ???????
+-export([get_status/1, get_status/2]).
+-export([verbosity/2, verbosity/3]).
+
+
+-export([c/1]).
+
+-record(state,{socket_type = ip_comm,
+ config_file,
+ config_db = null,
+ connections, %% Current request handlers
+ admin_state = unblocked,
+ blocker_ref = undefined,
+ blocking_tmr = undefined,
+ status = []}).
+
+
+c(Port) ->
+ Ref = httpd_util:make_name("httpd",undefined,Port),
+ gen_server:call(Ref, fake_close).
+
+
+%%
+%% External API
+%%
+
+start(ConfigFile, ConfigList) ->
+ start(ConfigFile, ConfigList, []).
+
+start(ConfigFile, ConfigList, Verbosity) ->
+ Port = httpd_util:key1search(ConfigList,port,80),
+ Addr = httpd_util:key1search(ConfigList,bind_address),
+ Name = make_name(Addr,Port),
+ ?LOG("start -> Name = ~p",[Name]),
+ gen_server:start({local,Name},?MODULE,
+ [ConfigFile, ConfigList, Addr, Port, Verbosity],[]).
+
+start_link(ConfigFile, ConfigList) ->
+ start_link(ConfigFile, ConfigList, []).
+
+start_link(ConfigFile, ConfigList, Verbosity) ->
+ Port = httpd_util:key1search(ConfigList,port,80),
+ Addr = httpd_util:key1search(ConfigList,bind_address),
+ Name = make_name(Addr,Port),
+ ?LOG("start_link -> Name = ~p",[Name]),
+ gen_server:start_link({local, Name},?MODULE,
+ [ConfigFile, ConfigList, Addr, Port, Verbosity],[]).
+
+%% stop
+
+stop(ServerRef) ->
+ gen_server:call(ServerRef, stop).
+
+%% restart
+
+restart(ServerRef) ->
+ gen_server:call(ServerRef, restart).
+
+
+%%%----------------------------------------------------------------
+
+block(ServerRef, disturbing) ->
+ call(ServerRef,block);
+
+block(ServerRef, non_disturbing) ->
+ do_block(ServerRef, non_disturbing, infinity).
+
+block(ServerRef, Method, Timeout) ->
+ do_block(ServerRef, Method, Timeout).
+
+
+%% The reason for not using call here, is that the manager cannot
+%% _wait_ for completion of the requests. It must be able to do
+%% do other things at the same time as the blocking goes on.
+do_block(ServerRef, Method, infinity) ->
+ Ref = make_ref(),
+ cast(ServerRef, {block, Method, infinity, self(), Ref}),
+ receive
+ {block_reply, Reply, Ref} ->
+ Reply
+ end;
+do_block(ServerRef,Method,Timeout) when Timeout > 0 ->
+ Ref = make_ref(),
+ cast(ServerRef,{block,Method,Timeout,self(),Ref}),
+ receive
+ {block_reply,Reply,Ref} ->
+ Reply
+ end.
+
+
+%%%----------------------------------------------------------------
+
+%% unblock
+
+unblock(ServerRef) ->
+ call(ServerRef,unblock).
+
+%% get admin/usage state
+
+get_admin_state(ServerRef) ->
+ call(ServerRef,get_admin_state).
+
+get_usage_state(ServerRef) ->
+ call(ServerRef,get_usage_state).
+
+
+%% get_status
+
+get_status(ServerRef) ->
+ gen_server:call(ServerRef,get_status).
+
+get_status(ServerRef,Timeout) ->
+ gen_server:call(ServerRef,get_status,Timeout).
+
+
+verbosity(ServerRef,Verbosity) ->
+ verbosity(ServerRef,all,Verbosity).
+
+verbosity(ServerRef,all,Verbosity) ->
+ gen_server:call(ServerRef,{verbosity,all,Verbosity});
+verbosity(ServerRef,manager,Verbosity) ->
+ gen_server:call(ServerRef,{verbosity,manager,Verbosity});
+verbosity(ServerRef,request,Verbosity) ->
+ gen_server:call(ServerRef,{verbosity,request,Verbosity});
+verbosity(ServerRef,acceptor,Verbosity) ->
+ gen_server:call(ServerRef,{verbosity,acceptor,Verbosity});
+verbosity(ServerRef,security,Verbosity) ->
+ gen_server:call(ServerRef,{verbosity,security,Verbosity});
+verbosity(ServerRef,auth,Verbosity) ->
+ gen_server:call(ServerRef,{verbosity,auth,Verbosity}).
+
+%%
+%% Internal API
+%%
+
+
+%% new_connection
+
+new_connection(Manager) ->
+ gen_server:call(Manager, {new_connection, self()}).
+
+%% done
+
+done_connection(Manager) ->
+ gen_server:cast(Manager, {done_connection, self()}).
+
+
+%% is_busy(ServerRef) -> true | false
+%%
+%% Tests if the server is (in usage state) busy,
+%% i.e. has rached the heavy load limit.
+%%
+
+is_busy(ServerRef) ->
+ gen_server:call(ServerRef,is_busy).
+
+is_busy(ServerRef,Timeout) ->
+ gen_server:call(ServerRef,is_busy,Timeout).
+
+
+%% is_busy_or_blocked(ServerRef) -> busy | blocked | false
+%%
+%% Tests if the server is busy (usage state), i.e. has rached,
+%% the heavy load limit, or blocked (admin state) .
+%%
+
+is_busy_or_blocked(ServerRef) ->
+ gen_server:call(ServerRef,is_busy_or_blocked).
+
+
+%% is_blocked(ServerRef) -> true | false
+%%
+%% Tests if the server is blocked (admin state) .
+%%
+
+is_blocked(ServerRef) ->
+ gen_server:call(ServerRef,is_blocked).
+
+
+%%
+%% Module API. Theese functions are intended for use from modules only.
+%%
+
+config_lookup(Port, Query) ->
+ config_lookup(undefined, Port, Query).
+config_lookup(Addr, Port, Query) ->
+ Name = httpd_util:make_name("httpd",Addr,Port),
+ gen_server:call(whereis(Name), {config_lookup, Query}).
+
+config_multi_lookup(Port, Query) ->
+ config_multi_lookup(undefined,Port,Query).
+config_multi_lookup(Addr,Port, Query) ->
+ Name = httpd_util:make_name("httpd",Addr,Port),
+ gen_server:call(whereis(Name), {config_multi_lookup, Query}).
+
+config_match(Port, Pattern) ->
+ config_match(undefined,Port,Pattern).
+config_match(Addr, Port, Pattern) ->
+ Name = httpd_util:make_name("httpd",Addr,Port),
+ gen_server:call(whereis(Name), {config_match, Pattern}).
+
+
+%%
+%% Server call-back functions
+%%
+
+%% init
+
+init([ConfigFile, ConfigList, Addr, Port, Verbosity]) ->
+ process_flag(trap_exit, true),
+ case (catch do_init(ConfigFile, ConfigList, Addr, Port, Verbosity)) of
+ {error, Reason} ->
+ ?vlog("failed starting server: ~p", [Reason]),
+ {stop, Reason};
+ {ok, State} ->
+ {ok, State}
+ end.
+
+
+do_init(ConfigFile, ConfigList, Addr, Port, Verbosity) ->
+ put(sname,man),
+ set_verbosity(Verbosity),
+ ?vlog("starting",[]),
+ ConfigDB = do_initial_store(ConfigList),
+ ?vtrace("config db: ~p", [ConfigDB]),
+ SocketType = httpd_socket:config(ConfigDB),
+ ?vtrace("socket type: ~p, now start acceptor", [SocketType]),
+ case httpd_acceptor_sup:start_acceptor(SocketType, Addr, Port, ConfigDB) of
+ {ok, Pid} ->
+ ?vtrace("acceptor started: ~p", [Pid]),
+ Status = [{max_conn,0}, {last_heavy_load,never},
+ {last_connection,never}],
+ State = #state{socket_type = SocketType,
+ config_file = ConfigFile,
+ config_db = ConfigDB,
+ connections = [],
+ status = Status},
+ ?vdebug("started",[]),
+ {ok, State};
+ Else ->
+ Else
+ end.
+
+
+do_initial_store(ConfigList) ->
+ case httpd_conf:store(ConfigList) of
+ {ok, ConfigDB} ->
+ ConfigDB;
+ {error, Reason} ->
+ ?vinfo("failed storing configuration: ~p",[Reason]),
+ throw({error, Reason})
+ end.
+
+
+
+%% handle_call
+
+handle_call(stop, _From, State) ->
+ ?vlog("stop",[]),
+ {stop, normal, ok, State};
+
+handle_call({config_lookup, Query}, _From, State) ->
+ ?vlog("config lookup: Query = ~p",[Query]),
+ Res = httpd_util:lookup(State#state.config_db, Query),
+ ?vdebug("config lookup result: ~p",[Res]),
+ {reply, Res, State};
+
+handle_call({config_multi_lookup, Query}, _From, State) ->
+ ?vlog("multi config lookup: Query = ~p",[Query]),
+ Res = httpd_util:multi_lookup(State#state.config_db, Query),
+ ?vdebug("multi config lookup result: ~p",[Res]),
+ {reply, Res, State};
+
+handle_call({config_match, Query}, _From, State) ->
+ ?vlog("config match: Query = ~p",[Query]),
+ Res = ets:match_object(State#state.config_db, Query),
+ ?vdebug("config match result: ~p",[Res]),
+ {reply, Res, State};
+
+handle_call(get_status, _From, State) ->
+ ?vdebug("get status",[]),
+ ManagerStatus = manager_status(self()),
+ %% AuthStatus = auth_status(get(auth_server)),
+ %% SecStatus = sec_status(get(sec_server)),
+ %% AccStatus = sec_status(get(acceptor_server)),
+ S1 = [{current_conn,length(State#state.connections)}|State#state.status]++
+ [ManagerStatus],
+ ?vtrace("status = ~p",[S1]),
+ {reply,S1,State};
+
+handle_call(is_busy, From, State) ->
+ Reply = case get_ustate(State) of
+ busy ->
+ true;
+ _ ->
+ false
+ end,
+ ?vlog("is busy: ~p",[Reply]),
+ {reply,Reply,State};
+
+handle_call(is_busy_or_blocked, From, State) ->
+ Reply =
+ case get_astate(State) of
+ unblocked ->
+ case get_ustate(State) of
+ busy ->
+ busy;
+ _ ->
+ false
+ end;
+ _ ->
+ blocked
+ end,
+ ?vlog("is busy or blocked: ~p",[Reply]),
+ {reply,Reply,State};
+
+handle_call(is_blocked, From, State) ->
+ Reply =
+ case get_astate(State) of
+ unblocked ->
+ false;
+ _ ->
+ true
+ end,
+ ?vlog("is blocked: ~p",[Reply]),
+ {reply,Reply,State};
+
+handle_call(get_admin_state, From, State) ->
+ Reply = get_astate(State),
+ ?vlog("admin state: ~p",[Reply]),
+ {reply,Reply,State};
+
+handle_call(get_usage_state, From, State) ->
+ Reply = get_ustate(State),
+ ?vlog("usage state: ~p",[Reply]),
+ {reply,Reply,State};
+
+handle_call({verbosity,Who,Verbosity}, From, State) ->
+ V = ?vvalidate(Verbosity),
+ ?vlog("~n Set new verbosity to ~p for ~p",[V,Who]),
+ Reply = set_verbosity(Who,V,State),
+ {reply,Reply,State};
+
+handle_call(restart, From, State) when State#state.admin_state == blocked ->
+ ?vlog("restart",[]),
+ case handle_restart(State) of
+ {stop, Reply,S1} ->
+ {stop, Reply, S1};
+ {_, Reply, S1} ->
+ {reply,Reply,S1}
+ end;
+
+handle_call(restart, From, State) ->
+ ?vlog("restart(~p)",[State#state.admin_state]),
+ {reply,{error,{invalid_admin_state,State#state.admin_state}},State};
+
+handle_call(block, From, State) ->
+ ?vlog("block(disturbing)",[]),
+ {Reply,S1} = handle_block(State),
+ {reply,Reply,S1};
+
+handle_call(unblock, {From,_Tag}, State) ->
+ ?vlog("unblock",[]),
+ {Reply,S1} = handle_unblock(State,From),
+ {reply, Reply, S1};
+
+handle_call({new_connection, Pid}, From, State) ->
+ ?vlog("~n New connection (~p) when connection count = ~p",
+ [Pid,length(State#state.connections)]),
+ {S, S1} = handle_new_connection(State, Pid),
+ Reply = {S, get(request_handler_verbosity)},
+ {reply, Reply, S1};
+
+handle_call(Request, From, State) ->
+ ?vinfo("~n unknown request '~p' from ~p", [Request,From]),
+ String =
+ lists:flatten(
+ io_lib:format("Unknown request "
+ "~n ~p"
+ "~nto manager (~p)"
+ "~nfrom ~p",
+ [Request, self(), From])),
+ report_error(State,String),
+ {reply, ok, State}.
+
+
+%% handle_cast
+
+handle_cast({done_connection, Pid}, State) ->
+ ?vlog("~n Done connection (~p)", [Pid]),
+ S1 = handle_done_connection(State, Pid),
+ {noreply, S1};
+
+handle_cast({block, disturbing, Timeout, From, Ref}, State) ->
+ ?vlog("block(disturbing,~p)",[Timeout]),
+ S1 = handle_block(State, Timeout, From, Ref),
+ {noreply,S1};
+
+handle_cast({block, non_disturbing, Timeout, From, Ref}, State) ->
+ ?vlog("block(non-disturbing,~p)",[Timeout]),
+ S1 = handle_nd_block(State, Timeout, From, Ref),
+ {noreply,S1};
+
+handle_cast(Message, State) ->
+ ?vinfo("~n received unknown message '~p'",[Message]),
+ String =
+ lists:flatten(
+ io_lib:format("Unknown message "
+ "~n ~p"
+ "~nto manager (~p)",
+ [Message, self()])),
+ report_error(State, String),
+ {noreply, State}.
+
+%% handle_info
+
+handle_info({block_timeout, Method}, State) ->
+ ?vlog("received block_timeout event",[]),
+ S1 = handle_block_timeout(State,Method),
+ {noreply, S1};
+
+handle_info({'DOWN', Ref, process, _Object, Info}, State) ->
+ ?vlog("~n down message for ~p",[Ref]),
+ S1 =
+ case State#state.blocker_ref of
+ Ref ->
+ handle_blocker_exit(State);
+ _ ->
+ %% Not our blocker, so ignore
+ State
+ end,
+ {noreply, S1};
+
+handle_info({'EXIT', Pid, normal}, State) ->
+ ?vdebug("~n Normal exit message from ~p", [Pid]),
+ {noreply, State};
+
+handle_info({'EXIT', Pid, blocked}, S) ->
+ ?vdebug("blocked exit signal from request handler (~p)", [Pid]),
+ {noreply, S};
+
+handle_info({'EXIT', Pid, Reason}, State) ->
+ ?vlog("~n Exit message from ~p for reason ~p",[Pid, Reason]),
+ S1 = check_connections(State, Pid, Reason),
+ {noreply, S1};
+
+handle_info(Info, State) ->
+ ?vinfo("~n received unknown info '~p'",[Info]),
+ String =
+ lists:flatten(
+ io_lib:format("Unknown info "
+ "~n ~p"
+ "~nto manager (~p)",
+ [Info, self()])),
+ report_error(State, String),
+ {noreply, State}.
+
+
+%% terminate
+
+terminate(R, #state{config_db = Db}) ->
+ ?vlog("Terminating for reason: ~n ~p", [R]),
+ httpd_conf:remove_all(Db),
+ ok.
+
+
+%% code_change({down,ToVsn}, State, Extra)
+%%
+%% NOTE:
+%% Actually upgrade from 2.5.1 to 2.5.3 and downgrade from
+%% 2.5.3 to 2.5.1 is done with an application restart, so
+%% these function is actually never used. The reason for keeping
+%% this stuff is only for future use.
+%%
+code_change({down,ToVsn},State,Extra) ->
+ {ok,State};
+
+%% code_change(FromVsn, State, Extra)
+%%
+code_change(FromVsn,State,Extra) ->
+ {ok,State}.
+
+
+
+%% -------------------------------------------------------------------------
+%% check_connection
+%%
+%%
+%%
+%%
+
+check_connections(#state{connections = []} = State, _Pid, _Reason) ->
+ State;
+check_connections(#state{admin_state = shutting_down,
+ connections = Connections} = State, Pid, Reason) ->
+ %% Could be a crashing request handler
+ case lists:delete(Pid, Connections) of
+ [] -> % Crashing request handler => block complete
+ String =
+ lists:flatten(
+ io_lib:format("request handler (~p) crashed:"
+ "~n ~p", [Pid, Reason])),
+ report_error(State, String),
+ ?vlog("block complete",[]),
+ demonitor_blocker(State#state.blocker_ref),
+ {Tmr,From,Ref} = State#state.blocking_tmr,
+ ?vlog("(possibly) stop block timer",[]),
+ stop_block_tmr(Tmr),
+ ?vlog("and send the reply",[]),
+ From ! {block_reply,ok,Ref},
+ State#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined};
+ Connections1 ->
+ State#state{connections = Connections1}
+ end;
+check_connections(#state{connections = Connections} = State, Pid, Reason) ->
+ case lists:delete(Pid, Connections) of
+ Connections -> % Not a request handler, so ignore
+ State;
+ Connections1 ->
+ String =
+ lists:flatten(
+ io_lib:format("request handler (~p) crashed:"
+ "~n ~p", [Pid, Reason])),
+ report_error(State, String),
+ State#state{connections = lists:delete(Pid, Connections)}
+ end.
+
+
+%% -------------------------------------------------------------------------
+%% handle_[new | done]_connection
+%%
+%%
+%%
+%%
+
+handle_new_connection(State, Handler) ->
+ UsageState = get_ustate(State),
+ AdminState = get_astate(State),
+ handle_new_connection(UsageState, AdminState, State, Handler).
+
+handle_new_connection(busy, unblocked, State, Handler) ->
+ Status = update_heavy_load_status(State#state.status),
+ {{reject, busy},
+ State#state{status = Status}};
+
+handle_new_connection(_UsageState, unblocked, State, Handler) ->
+ Connections = State#state.connections,
+ Status = update_connection_status(State#state.status,
+ length(Connections)+1),
+ link(Handler),
+ {accept,
+ State#state{connections = [Handler|Connections], status = Status}};
+
+handle_new_connection(_UsageState, _AdminState, State, _Handler) ->
+ {{reject, blocked},
+ State}.
+
+
+handle_done_connection(#state{admin_state = shutting_down,
+ connections = Connections} = State, Handler) ->
+ unlink(Handler),
+ case lists:delete(Handler, Connections) of
+ [] -> % Ok, block complete
+ ?vlog("block complete",[]),
+ demonitor_blocker(State#state.blocker_ref),
+ {Tmr,From,Ref} = State#state.blocking_tmr,
+ ?vlog("(possibly) stop block timer",[]),
+ stop_block_tmr(Tmr),
+ ?vlog("and send the reply",[]),
+ From ! {block_reply,ok,Ref},
+ State#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined};
+ Connections1 ->
+ State#state{connections = Connections1}
+ end;
+
+handle_done_connection(#state{connections = Connections} = State, Handler) ->
+ State#state{connections = lists:delete(Handler, Connections)}.
+
+
+%% -------------------------------------------------------------------------
+%% handle_block
+%%
+%%
+%%
+%%
+handle_block(#state{admin_state = AdminState} = S) ->
+ handle_block(S, AdminState).
+
+handle_block(S,unblocked) ->
+ %% Kill all connections
+ ?vtrace("handle_block(unblocked) -> kill all request handlers",[]),
+%% [exit(Pid,blocked) || Pid <- S#state.connections],
+ [kill_handler(Pid) || Pid <- S#state.connections],
+ {ok,S#state{connections = [], admin_state = blocked}};
+handle_block(S,blocked) ->
+ ?vtrace("handle_block(blocked) -> already blocked",[]),
+ {ok,S};
+handle_block(S,shutting_down) ->
+ ?vtrace("handle_block(shutting_down) -> ongoing...",[]),
+ {{error,shutting_down},S}.
+
+
+kill_handler(Pid) ->
+ ?vtrace("kill request handler: ~p",[Pid]),
+ exit(Pid, blocked).
+%% exit(Pid, kill).
+
+handle_block(S,Timeout,From,Ref) when Timeout >= 0 ->
+ do_block(S,Timeout,From,Ref);
+
+handle_block(S,Timeout,From,Ref) ->
+ Reply = {error,{invalid_block_request,Timeout}},
+ From ! {block_reply,Reply,Ref},
+ S.
+
+do_block(S,Timeout,From,Ref) ->
+ case S#state.connections of
+ [] ->
+ %% Already in idle usage state => go directly to blocked
+ ?vdebug("do_block -> already in idle usage state",[]),
+ From ! {block_reply,ok,Ref},
+ S#state{admin_state = blocked};
+ _ ->
+ %% Active or Busy usage state => go to shutting_down
+ ?vdebug("do_block -> active or busy usage state",[]),
+ %% Make sure we get to know if blocker dies...
+ ?vtrace("do_block -> create blocker monitor",[]),
+ MonitorRef = monitor_blocker(From),
+ ?vtrace("do_block -> (possibly) start block timer",[]),
+ Tmr = {start_block_tmr(Timeout,disturbing),From,Ref},
+ S#state{admin_state = shutting_down,
+ blocker_ref = MonitorRef, blocking_tmr = Tmr}
+ end.
+
+handle_nd_block(S,infinity,From,Ref) ->
+ do_nd_block(S,infinity,From,Ref);
+
+handle_nd_block(S,Timeout,From,Ref) when Timeout >= 0 ->
+ do_nd_block(S,Timeout,From,Ref);
+
+handle_nd_block(S,Timeout,From,Ref) ->
+ Reply = {error,{invalid_block_request,Timeout}},
+ From ! {block_reply,Reply,Ref},
+ S.
+
+do_nd_block(S,Timeout,From,Ref) ->
+ case S#state.connections of
+ [] ->
+ %% Already in idle usage state => go directly to blocked
+ ?vdebug("do_nd_block -> already in idle usage state",[]),
+ From ! {block_reply,ok,Ref},
+ S#state{admin_state = blocked};
+ _ ->
+ %% Active or Busy usage state => go to shutting_down
+ ?vdebug("do_nd_block -> active or busy usage state",[]),
+ %% Make sure we get to know if blocker dies...
+ ?vtrace("do_nd_block -> create blocker monitor",[]),
+ MonitorRef = monitor_blocker(From),
+ ?vtrace("do_nd_block -> (possibly) start block timer",[]),
+ Tmr = {start_block_tmr(Timeout,non_disturbing),From,Ref},
+ S#state{admin_state = shutting_down,
+ blocker_ref = MonitorRef, blocking_tmr = Tmr}
+ end.
+
+handle_block_timeout(S,Method) ->
+ %% Time to take this to the road...
+ demonitor_blocker(S#state.blocker_ref),
+ handle_block_timeout1(S,Method,S#state.blocking_tmr).
+
+handle_block_timeout1(S,non_disturbing,{_,From,Ref}) ->
+ ?vdebug("handle_block_timeout1(non-disturbing) -> send reply: timeout",[]),
+ From ! {block_reply,{error,timeout},Ref},
+ S#state{admin_state = unblocked,
+ blocker_ref = undefined, blocking_tmr = undefined};
+
+handle_block_timeout1(S,disturbing,{_,From,Ref}) ->
+ ?vdebug("handle_block_timeout1(disturbing) -> kill all connections",[]),
+ [exit(Pid,blocked) || Pid <- S#state.connections],
+
+ ?vdebug("handle_block_timeout1 -> send reply: ok",[]),
+ From ! {block_reply,ok,Ref},
+ S#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined, blocking_tmr = undefined};
+
+handle_block_timeout1(S,Method,{_,From,Ref}) ->
+ ?vinfo("received block timeout with unknown block method:"
+ "~n Method: ~p",[Method]),
+ From ! {block_reply,{error,{unknown_block_method,Method}},Ref},
+ S#state{admin_state = blocked, connections = [],
+ blocker_ref = undefined, blocking_tmr = undefined};
+
+handle_block_timeout1(S,Method,TmrInfo) ->
+ ?vinfo("received block timeout with erroneous timer info:"
+ "~n Method: ~p"
+ "~n TmrInfo: ~p",[Method,TmrInfo]),
+ S#state{admin_state = unblocked,
+ blocker_ref = undefined, blocking_tmr = undefined}.
+
+handle_unblock(S,FromA) ->
+ handle_unblock(S,FromA,S#state.admin_state).
+
+handle_unblock(S,_FromA,unblocked) ->
+ {ok,S};
+handle_unblock(S,FromA,_AdminState) ->
+ ?vtrace("handle_unblock -> (possibly) stop block timer",[]),
+ stop_block_tmr(S#state.blocking_tmr),
+ case S#state.blocking_tmr of
+ {Tmr,FromB,Ref} ->
+ %% Another process is trying to unblock
+ %% Inform the blocker
+ FromB ! {block_reply, {error,{unblocked,FromA}},Ref};
+ _ ->
+ ok
+ end,
+ {ok,S#state{admin_state = unblocked, blocking_tmr = undefined}}.
+
+%% The blocker died so we give up on the block.
+handle_blocker_exit(S) ->
+ {Tmr,_From,_Ref} = S#state.blocking_tmr,
+ ?vtrace("handle_blocker_exit -> (possibly) stop block timer",[]),
+ stop_block_tmr(Tmr),
+ S#state{admin_state = unblocked,
+ blocker_ref = undefined, blocking_tmr = undefined}.
+
+
+
+%% -------------------------------------------------------------------------
+%% handle_restart
+%%
+%%
+%%
+%%
+handle_restart(#state{config_file = undefined} = State) ->
+ {continue, {error, undefined_config_file}, State};
+handle_restart(#state{config_db = Db, config_file = ConfigFile} = State) ->
+ ?vtrace("load new configuration",[]),
+ {ok, Config} = httpd_conf:load(ConfigFile),
+ ?vtrace("check for illegal changes (addr, port and socket-type)",[]),
+ case (catch check_constant_values(Db, Config)) of
+ ok ->
+ %% If something goes wrong between the remove
+ %% and the store where fu-ed
+ ?vtrace("remove old configuration, now hold you breath...",[]),
+ httpd_conf:remove_all(Db),
+ ?vtrace("store new configuration",[]),
+ case httpd_conf:store(Config) of
+ {ok, NewConfigDB} ->
+ ?vlog("restart done, puh!",[]),
+ {continue, ok, State#state{config_db = NewConfigDB}};
+ Error ->
+ ?vlog("failed store new config: ~n ~p",[Error]),
+ {stop, Error, State}
+ end;
+ Error ->
+ ?vlog("restart NOT performed due to:"
+ "~n ~p",[Error]),
+ {continue, Error, State}
+ end.
+
+
+check_constant_values(Db, Config) ->
+ %% Check port number
+ ?vtrace("check_constant_values -> check port number",[]),
+ Port = httpd_util:lookup(Db,port),
+ case httpd_util:key1search(Config,port) of %% MUST be equal
+ Port ->
+ ok;
+ OtherPort ->
+ throw({error,{port_number_changed,Port,OtherPort}})
+ end,
+
+ %% Check bind address
+ ?vtrace("check_constant_values -> check bind address",[]),
+ Addr = httpd_util:lookup(Db,bind_address),
+ case httpd_util:key1search(Config,bind_address) of %% MUST be equal
+ Addr ->
+ ok;
+ OtherAddr ->
+ throw({error,{addr_changed,Addr,OtherAddr}})
+ end,
+
+ %% Check socket type
+ ?vtrace("check_constant_values -> check socket type",[]),
+ SockType = httpd_util:lookup(Db, com_type),
+ case httpd_util:key1search(Config, com_type) of %% MUST be equal
+ SockType ->
+ ok;
+ OtherSockType ->
+ throw({error,{sock_type_changed,SockType,OtherSockType}})
+ end,
+ ?vtrace("check_constant_values -> done",[]),
+ ok.
+
+
+%% get_ustate(State) -> idle | active | busy
+%%
+%% Retrieve the usage state of the HTTP server:
+%% 0 active connection -> idle
+%% max_clients active connections -> busy
+%% Otherwise -> active
+%%
+get_ustate(State) ->
+ get_ustate(length(State#state.connections),State).
+
+get_ustate(0,_State) ->
+ idle;
+get_ustate(ConnectionCnt,State) ->
+ ConfigDB = State#state.config_db,
+ case httpd_util:lookup(ConfigDB, max_clients, 150) of
+ ConnectionCnt ->
+ busy;
+ _ ->
+ active
+ end.
+
+
+get_astate(S) -> S#state.admin_state.
+
+
+%% Timer handling functions
+start_block_tmr(infinity,_) ->
+ undefined;
+start_block_tmr(T,M) ->
+ erlang:send_after(T,self(),{block_timeout,M}).
+
+stop_block_tmr(undefined) ->
+ ok;
+stop_block_tmr(Ref) ->
+ erlang:cancel_timer(Ref).
+
+
+%% Monitor blocker functions
+monitor_blocker(Pid) when pid(Pid) ->
+ case (catch erlang:monitor(process,Pid)) of
+ MonitorRef ->
+ MonitorRef;
+ {'EXIT',Reason} ->
+ undefined
+ end;
+monitor_blocker(_) ->
+ undefined.
+
+demonitor_blocker(undefined) ->
+ ok;
+demonitor_blocker(Ref) ->
+ (catch erlang:demonitor(Ref)).
+
+
+%% Some status utility functions
+
+update_heavy_load_status(Status) ->
+ update_status_with_time(Status,last_heavy_load).
+
+update_connection_status(Status,ConnCount) ->
+ S1 = case lists:keysearch(max_conn,1,Status) of
+ {value,{max_conn,C1}} when ConnCount > C1 ->
+ lists:keyreplace(max_conn,1,Status,{max_conn,ConnCount});
+ {value,{max_conn,C2}} ->
+ Status;
+ false ->
+ [{max_conn,ConnCount}|Status]
+ end,
+ update_status_with_time(S1,last_connection).
+
+update_status_with_time(Status,Key) ->
+ lists:keyreplace(Key,1,Status,{Key,universal_time()}).
+
+universal_time() -> calendar:universal_time().
+
+
+auth_status(P) when pid(P) ->
+ Items = [status, message_queue_len, reductions,
+ heap_size, stack_size, current_function],
+ {auth_status, process_status(P,Items,[])};
+auth_status(_) ->
+ {auth_status, undefined}.
+
+sec_status(P) when pid(P) ->
+ Items = [status, message_queue_len, reductions,
+ heap_size, stack_size, current_function],
+ {security_status, process_status(P,Items,[])};
+sec_status(_) ->
+ {security_status, undefined}.
+
+acceptor_status(P) when pid(P) ->
+ Items = [status, message_queue_len, reductions,
+ heap_size, stack_size, current_function],
+ {acceptor_status, process_status(P,Items,[])};
+acceptor_status(_) ->
+ {acceptor_status, undefined}.
+
+
+manager_status(P) ->
+ Items = [status, message_queue_len, reductions,
+ heap_size, stack_size],
+ {manager_status, process_status(P,Items,[])}.
+
+
+process_status(P,[],L) ->
+ [{pid,P}|lists:reverse(L)];
+process_status(P,[H|T],L) ->
+ case (catch process_info(P,H)) of
+ {H, Value} ->
+ process_status(P,T,[{H,Value}|L]);
+ _ ->
+ process_status(P,T,[{H,undefined}|L])
+ end.
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd",Addr,Port).
+
+
+report_error(State,String) ->
+ Cdb = State#state.config_db,
+ error_logger:error_report(String),
+ mod_log:report_error(Cdb,String),
+ mod_disk_log:report_error(Cdb,String).
+
+
+set_verbosity(V) ->
+ Units = [manager_verbosity,
+ acceptor_verbosity, request_handler_verbosity,
+ security_verbosity, auth_verbosity],
+ case httpd_util:key1search(V, all) of
+ undefined ->
+ set_verbosity(V, Units);
+ Verbosity when atom(Verbosity) ->
+ V1 = [{Unit, Verbosity} || Unit <- Units],
+ set_verbosity(V1, Units)
+ end.
+
+set_verbosity(_V, []) ->
+ ok;
+set_verbosity(V, [manager_verbosity = Unit|Units]) ->
+ Verbosity = httpd_util:key1search(V, Unit, ?default_verbosity),
+ put(verbosity, ?vvalidate(Verbosity)),
+ set_verbosity(V, Units);
+set_verbosity(V, [Unit|Units]) ->
+ Verbosity = httpd_util:key1search(V, Unit, ?default_verbosity),
+ put(Unit, ?vvalidate(Verbosity)),
+ set_verbosity(V, Units).
+
+
+set_verbosity(manager,V,_S) ->
+ put(verbosity,V);
+set_verbosity(acceptor,V,_S) ->
+ put(acceptor_verbosity,V);
+set_verbosity(request,V,_S) ->
+ put(request_handler_verbosity,V);
+set_verbosity(security,V,S) ->
+ OldVerbosity = put(security_verbosity,V),
+ Addr = httpd_util:lookup(S#state.config_db, bind_address),
+ Port = httpd_util:lookup(S#state.config_db, port),
+ mod_security_server:verbosity(Addr,Port,V),
+ OldVerbosity;
+set_verbosity(auth,V,S) ->
+ OldVerbosity = put(auth_verbosity,V),
+ Addr = httpd_util:lookup(S#state.config_db, bind_address),
+ Port = httpd_util:lookup(S#state.config_db, port),
+ mod_auth_server:verbosity(Addr,Port,V),
+ OldVerbosity;
+
+set_verbosity(all,V,S) ->
+ OldMv = put(verbosity,V),
+ OldAv = put(acceptor_verbosity,V),
+ OldRv = put(request_handler_verbosity,V),
+ OldSv = put(security_verbosity,V),
+ OldAv = put(auth_verbosity,V),
+ Addr = httpd_util:lookup(S#state.config_db, bind_address),
+ Port = httpd_util:lookup(S#state.config_db, port),
+ mod_security_server:verbosity(Addr,Port,V),
+ mod_auth_server:verbosity(Addr,Port,V),
+ [{manager,OldMv}, {request,OldRv}, {security,OldSv}, {auth, OldAv}].
+
+
+%%
+call(ServerRef,Request) ->
+ gen_server:call(ServerRef,Request).
+
+cast(ServerRef,Message) ->
+ gen_server:cast(ServerRef,Message).
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_misc_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_misc_sup.erl
new file mode 100644
index 0000000000..5921c5db60
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_misc_sup.erl
@@ -0,0 +1,116 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_misc_sup.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The top supervisor for the Megaco/H.248 application
+%%----------------------------------------------------------------------
+
+-module(httpd_misc_sup).
+
+-behaviour(supervisor).
+
+-include("httpd_verbosity.hrl").
+
+%% public
+-export([start/3, stop/1, init/1]).
+
+-export([start_auth_server/3, stop_auth_server/2,
+ start_sec_server/3, stop_sec_server/2]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% supervisor callback functions
+
+
+start(Addr, Port, MiscSupVerbosity) ->
+ SupName = make_name(Addr, Port),
+ supervisor:start_link({local, SupName}, ?MODULE, [MiscSupVerbosity]).
+
+stop(StartArgs) ->
+ ok.
+
+init([Verbosity]) -> % Supervisor
+ do_init(Verbosity);
+init(BadArg) ->
+ {error, {badarg, BadArg}}.
+
+do_init(Verbosity) ->
+ put(verbosity,?vvalidate(Verbosity)),
+ put(sname,misc_sup),
+ ?vlog("starting", []),
+ Flags = {one_for_one, 0, 1},
+ KillAfter = timer:seconds(1),
+ Workers = [],
+ {ok, {Flags, Workers}}.
+
+
+%%----------------------------------------------------------------------
+%% Function: [start|stop]_[auth|sec]_server/3
+%% Description: Starts a [auth | security] worker (child) process
+%%----------------------------------------------------------------------
+
+start_auth_server(Addr, Port, Verbosity) ->
+ start_permanent_worker(mod_auth_server, Addr, Port,
+ Verbosity, [gen_server]).
+
+stop_auth_server(Addr, Port) ->
+ stop_permanent_worker(mod_auth_server, Addr, Port).
+
+
+start_sec_server(Addr, Port, Verbosity) ->
+ start_permanent_worker(mod_security_server, Addr, Port,
+ Verbosity, [gen_server]).
+
+stop_sec_server(Addr, Port) ->
+ stop_permanent_worker(mod_security_server, Addr, Port).
+
+
+
+%%----------------------------------------------------------------------
+%% Function: start_permanent_worker/5
+%% Description: Starts a permanent worker (child) process
+%%----------------------------------------------------------------------
+
+start_permanent_worker(Mod, Addr, Port, Verbosity, Modules) ->
+ SupName = make_name(Addr, Port),
+ Spec = {{Mod, Addr, Port},
+ {Mod, start_link, [Addr, Port, Verbosity]},
+ permanent, timer:seconds(1), worker, [Mod] ++ Modules},
+ supervisor:start_child(SupName, Spec).
+
+
+%%----------------------------------------------------------------------
+%% Function: stop_permanent_worker/3
+%% Description: Stops a permanent worker (child) process
+%%----------------------------------------------------------------------
+
+stop_permanent_worker(Mod, Addr, Port) ->
+ SupName = make_name(Addr, Port),
+ Name = {Mod, Addr, Port},
+ case supervisor:terminate_child(SupName, Name) of
+ ok ->
+ supervisor:delete_child(SupName, Name);
+ Error ->
+ Error
+ end.
+
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd_misc_sup",Addr,Port).
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_parse.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_parse.erl
new file mode 100644
index 0000000000..3f8f0837f9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_parse.erl
@@ -0,0 +1,348 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_parse.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_parse).
+-export([
+ request_header/1,
+ hsplit/2,
+ get_request_record/10,
+ split_lines/1,
+ tagup_header/1]).
+-include("httpd.hrl").
+
+
+%%----------------------------------------------------------------------
+%% request_header
+%%
+%% Input: The request as sent from the client (list of characters)
+%% (may include part of the entity body)
+%%
+%% Returns:
+%% {ok, Info#mod}
+%% {not_implemented,Info#mod}
+%% {bad_request, Reason}
+%%----------------------------------------------------------------------
+
+request_header(Header)->
+ [RequestLine|HeaderFields] = split_lines(Header),
+ ?DEBUG("request ->"
+ "~n RequestLine: ~p"
+ "~n Header: ~p",[RequestLine,Header]),
+ ParsedHeader = tagup_header(HeaderFields),
+ ?DEBUG("request ->"
+ "~n ParseHeader: ~p",[ParsedHeader]),
+ case verify_request(string:tokens(RequestLine," ")) of
+ ["HEAD", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] ->
+ {ok, ["HEAD", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+ ParsedHeader]};
+ ["GET", RequestURI, "HTTP/0.9"] ->
+ {ok, ["GET", RequestURI, "HTTP/0.9", RequestLine, ParsedHeader]};
+ ["GET", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] ->
+ {ok, ["GET", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+ ParsedHeader]};
+ ["POST", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] ->
+ {ok, ["POST", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+ ParsedHeader]};
+ %%HTTP must be 1.1 or higher
+ ["TRACE", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] when N>48->
+ {ok, ["TRACE", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+ ParsedHeader]};
+ [Method, RequestURI] ->
+ {not_implemented, RequestLine, Method, RequestURI,ParsedHeader,"HTTP/0.9"};
+ [Method, RequestURI, HTTPVersion] ->
+ {not_implemented, RequestLine, Method, RequestURI,ParsedHeader, HTTPVersion};
+ {bad_request, Reason} ->
+ {bad_request, Reason};
+ Reason ->
+ {bad_request, "Unknown request method"}
+ end.
+
+
+
+
+
+
+%%----------------------------------------------------------------------
+%% The request is passed through the server as a record of type mod get it
+%% ----------------------------------------------------------------------
+
+get_request_record(Socket,SocketType,ConfigDB,Method,RequestURI,
+ HTTPVersion,RequestLine,ParsedHeader,EntityBody,InitData)->
+ PersistentConn=get_persistens(HTTPVersion,ParsedHeader,ConfigDB),
+ Info=#mod{init_data=InitData,
+ data=[],
+ socket_type=SocketType,
+ socket=Socket,
+ config_db=ConfigDB,
+ method=Method,
+ absolute_uri=formatAbsoluteURI(RequestURI,ParsedHeader),
+ request_uri=formatRequestUri(RequestURI),
+ http_version=HTTPVersion,
+ request_line=RequestLine,
+ parsed_header=ParsedHeader,
+ entity_body=maybe_remove_nl(ParsedHeader,EntityBody),
+ connection=PersistentConn},
+ {ok,Info}.
+
+%%----------------------------------------------------------------------
+%% Conmtrol wheater we shall maintain a persistent connection or not
+%%----------------------------------------------------------------------
+get_persistens(HTTPVersion,ParsedHeader,ConfigDB)->
+ case httpd_util:lookup(ConfigDB,persistent_conn,true) of
+ true->
+ case HTTPVersion of
+ %%If it is version prio to 1.1 kill the conneciton
+ [$H, $T, $T, $P, $\/, $1, $.,N] ->
+ case httpd_util:key1search(ParsedHeader,"connection","keep-alive")of
+ %%if the connection isnt ordered to go down let it live
+ %%The keep-alive value is the older http/1.1 might be older
+ %%Clients that use it.
+ "keep-alive" when N >= 49 ->
+ ?DEBUG("CONNECTION MODE: ~p",[true]),
+ true;
+ "close" ->
+ ?DEBUG("CONNECTION MODE: ~p",[false]),
+ false;
+ Connect ->
+ ?DEBUG("CONNECTION MODE: ~p VALUE: ~p",[false,Connect]),
+ false
+ end;
+ _ ->
+ ?DEBUG("CONNECTION MODE: ~p VERSION: ~p",[false,HTTPVersion]),
+ false
+
+ end;
+ _ ->
+ false
+ end.
+
+
+
+
+%%----------------------------------------------------------------------
+%% Control whether the last newline of the body is a part of the message or
+%%it is a part of the multipart message.
+%%----------------------------------------------------------------------
+maybe_remove_nl(Header,Rest) ->
+ case find_content_type(Header) of
+ false ->
+ {ok,EntityBody,_}=regexp:sub(Rest,"\r\n\$",""),
+ EntityBody;
+ {ok, Value} ->
+ case string:str(Value, "multipart/form-data") of
+ 0 ->
+ {ok,EntityBody,_}=regexp:sub(Rest,"\r\n\$",""),
+ EntityBody;
+ _ ->
+ Rest
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%% Cet the content type of the incomming request
+%%----------------------------------------------------------------------
+
+
+find_content_type([]) ->
+ false;
+find_content_type([{Name,Value}|Tail]) ->
+ case httpd_util:to_lower(Name) of
+ "content-type" ->
+ {ok, Value};
+ _ ->
+ find_content_type(Tail)
+ end.
+
+%%----------------------------------------------------------------------
+%% Split the header to a list of strings where each string represents a
+%% HTTP header-field
+%%----------------------------------------------------------------------
+split_lines(Request) ->
+ split_lines(Request, [], []).
+split_lines([], CAcc, Acc) ->
+ lists:reverse([lists:reverse(CAcc)|Acc]);
+
+%%White space in the header fields are allowed but the new line must begin with LWS se
+%%rfc2616 chap 4.2. The rfc do not say what to
+split_lines([$\r, $\n, $\t |Rest], CAcc, Acc) ->
+ split_lines(Rest, [$\r, $\n |CAcc], Acc);
+
+split_lines([$\r, $\n, $\s |Rest], CAcc, Acc) ->
+ split_lines(Rest, [$\r, $\n |CAcc], Acc);
+
+split_lines([$\r, $\n|Rest], CAcc, Acc) ->
+ split_lines(Rest, [], [lists:reverse(CAcc)|Acc]);
+split_lines([Chr|Rest], CAcc, Acc) ->
+ split_lines(Rest, [Chr|CAcc], Acc).
+
+
+%%----------------------------------------------------------------------
+%% This is a 'hack' to stop people from trying to access directories/files
+%% relative to the ServerRoot.
+%%----------------------------------------------------------------------
+
+
+verify_request([Request, RequestURI]) ->
+ verify_request([Request, RequestURI, "HTTP/0.9"]);
+
+verify_request([Request, RequestURI, Protocol]) ->
+ NewRequestURI =
+ case string:str(RequestURI, "?") of
+ 0 ->
+ RequestURI;
+ Ndx ->
+ string:left(RequestURI, Ndx)
+ end,
+ case string:str(NewRequestURI, "..") of
+ 0 ->
+ [Request, RequestURI, Protocol];
+ _ ->
+ {bad_request, {forbidden, RequestURI}}
+ end;
+verify_request(Request) ->
+ Request.
+
+%%----------------------------------------------------------------------
+%% tagup_header
+%%
+%% Parses the header of a HTTP request and returns a key,value tuple
+%% list containing Name and Value of each header directive as of:
+%%
+%% Content-Type: multipart/mixed -> {"Content-Type", "multipart/mixed"}
+%%
+%% But in http/1.1 the field-names are case insencitive so now it must be
+%% Content-Type: multipart/mixed -> {"content-type", "multipart/mixed"}
+%% The standard furthermore says that leading and traling white space
+%% is not a part of the fieldvalue and shall therefore be removed.
+%%----------------------------------------------------------------------
+
+tagup_header([]) -> [];
+tagup_header([Line|Rest]) -> [tag(Line, [])|tagup_header(Rest)].
+
+tag([], Tag) ->
+ {httpd_util:to_lower(lists:reverse(Tag)), ""};
+tag([$:|Rest], Tag) ->
+ {httpd_util:to_lower(lists:reverse(Tag)), httpd_util:strip(Rest)};
+tag([Chr|Rest], Tag) ->
+ tag(Rest, [Chr|Tag]).
+
+
+%%----------------------------------------------------------------------
+%% There are 3 possible forms of the reuqest URI
+%%
+%% 1. * When the request is not for a special assset. is is instead
+%% to the server itself
+%%
+%% 2. absoluteURI the whole servername port and asset is in the request
+%%
+%% 3. The most common form that http/1.0 used abs path that is a path
+%% to the requested asset.
+%5----------------------------------------------------------------------
+formatRequestUri("*")->
+ "*";
+formatRequestUri([$h,$t,$t,$p,$:,$\/,$\/|ServerAndPath]) ->
+ removeServer(ServerAndPath);
+
+formatRequestUri([$H,$T,$T,$P,$:,$\/,$\/|ServerAndPath]) ->
+ removeServer(ServerAndPath);
+
+formatRequestUri(ABSPath) ->
+ ABSPath.
+
+removeServer([$\/|Url])->
+ case Url of
+ []->
+ "/";
+ _->
+ [$\/|Url]
+ end;
+removeServer([N|Url]) ->
+ removeServer(Url).
+
+
+formatAbsoluteURI([$h,$t,$t,$p,$:,$\/,$\/|Uri],ParsedHeader)->
+ [$H,$T,$T,$P,$:,$\/,$\/|Uri];
+
+formatAbsoluteURI([$H,$T,$T,$P,$:,$\/,$\/|Uri],ParsedHeader)->
+ [$H,$T,$T,$P,$:,$\/,$\/|Uri];
+
+formatAbsoluteURI(Uri,ParsedHeader)->
+ case httpd_util:key1search(ParsedHeader,"host") of
+ undefined ->
+ nohost;
+ Host ->
+ Host++Uri
+ end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%Code below is crap from an older version shall be removed when
+%%transformation to http/1.1 is finished
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+%request(Request) ->
+% ?DEBUG("request -> entry with:"
+% "~n Request: ~s",[Request]),
+ % {BeforeEntityBody, Rest} = hsplit([], Request),
+ % ?DEBUG("request ->"
+% "~n BeforeEntityBody: ~p"
+% "~n Rest: ~p",[BeforeEntityBody, Rest]),
+% [RequestLine|Header] = split_lines(BeforeEntityBody),
+% ?DEBUG("request ->"
+% "~n RequestLine: ~p"
+% "~n Header: ~p",[RequestLine,Header]),
+% ParsedHeader = tagup_header(Header),
+% ?DEBUG("request ->"
+% "~n ParseHeader: ~p",[ParsedHeader]),
+% EntityBody = maybe_remove_nl(ParsedHeader,Rest),
+% ?DEBUG("request ->"
+% "~n EntityBody: ~p",[EntityBody]),
+% case verify_request(string:tokens(RequestLine," ")) of
+% ["HEAD", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] ->
+% {ok, ["HEAD", formatRequestUri(RequestURI), [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+% ParsedHeader, EntityBody]};
+% ["GET", RequestURI, "HTTP/0.9"] ->
+% {ok, ["GET", RequestURI, "HTTP/0.9", RequestLine, ParsedHeader,
+% EntityBody]};
+% ["GET", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] ->
+% {ok, ["GET", formatRequestUri(RequestURI), [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+% ParsedHeader,EntityBody]};
+%% ["POST", RequestURI, [$H,$T,$T,$P,$/,$1,$.,N]] ->
+% {ok, ["POST", formatRequestUri(RequestURI), [$H,$T,$T,$P,$/,$1,$.,N], RequestLine,
+% ParsedHeader, EntityBody]};
+% [Method, RequestURI] ->
+% {not_implemented, RequestLine, Method, RequestURI,ParsedHeader,"HTTP/0.9"};
+% [Method, RequestURI, HTTPVersion] ->
+% {not_implemented, RequestLine, Method, RequestURI,ParsedHeader, HTTPVersion};
+% {bad_request, Reason} ->
+% {bad_request, Reason};
+% Reason ->
+% {bad_request, "Unknown request method"}
+% end.
+
+hsplit(Accu,[]) ->
+ {lists:reverse(Accu), []};
+hsplit(Accu, [ $\r, $\n, $\r, $\n | Tail]) ->
+ {lists:reverse(Accu), Tail};
+hsplit(Accu, [H|T]) ->
+ hsplit([H|Accu],T).
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_request_handler.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_request_handler.erl
new file mode 100644
index 0000000000..5008e6022e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_request_handler.erl
@@ -0,0 +1,995 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_request_handler.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_request_handler).
+
+%% app internal api
+-export([start_link/2, synchronize/3]).
+
+%% module internal api
+-export([connection/2, do_next_connection/6, read_header/7]).
+-export([parse_trailers/1, newline/1]).
+
+-include("httpd.hrl").
+-include("httpd_verbosity.hrl").
+
+
+%% start_link
+
+start_link(Manager, ConfigDB) ->
+ Pid = proc_lib:spawn(?MODULE, connection, [Manager, ConfigDB]),
+ {ok, Pid}.
+
+
+%% synchronize
+
+synchronize(Pid, SocketType, Socket) ->
+ Pid ! {synchronize, SocketType, Socket}.
+
+% connection
+
+connection(Manager, ConfigDB) ->
+ {SocketType, Socket, {Status, Verbosity}} = await_synchronize(Manager),
+ put(sname,self()),
+ put(verbosity,?vvalidate(Verbosity)),
+ connection1(Status, Manager, ConfigDB, SocketType, Socket).
+
+
+connection1({reject, busy}, Manager, ConfigDB, SocketType, Socket) ->
+ handle_busy(Manager, ConfigDB, SocketType, Socket);
+
+connection1({reject, blocked}, Manager, ConfigDB, SocketType, Socket) ->
+ handle_blocked(Manager, ConfigDB, SocketType, Socket);
+
+connection1(accept, Manager, ConfigDB, SocketType, Socket) ->
+ handle_connection(Manager, ConfigDB, SocketType, Socket).
+
+
+%% await_synchronize
+
+await_synchronize(Manager) ->
+ receive
+ {synchronize, SocketType, Socket} ->
+ ?vlog("received syncronize: "
+ "~n SocketType: ~p"
+ "~n Socket: ~p", [SocketType, Socket]),
+ {SocketType, Socket, httpd_manager:new_connection(Manager)}
+ after 5000 ->
+ exit(synchronize_timeout)
+ end.
+
+
+% handle_busy
+
+handle_busy(Manager, ConfigDB, SocketType, Socket) ->
+ ?vlog("handle busy: ~p", [Socket]),
+ MaxClients = httpd_util:lookup(ConfigDB, max_clients, 150),
+ String = io_lib:format("heavy load (>~w processes)", [MaxClients]),
+ reject_connection(Manager, ConfigDB, SocketType, Socket, String).
+
+
+% handle_blocked
+
+handle_blocked(Manager, ConfigDB, SocketType, Socket) ->
+ ?vlog("handle blocked: ~p", [Socket]),
+ String = "Server maintenance performed, try again later",
+ reject_connection(Manager, ConfigDB, SocketType, Socket, String).
+
+
+% reject_connection
+
+reject_connection(Manager, ConfigDB, SocketType, Socket, Info) ->
+ String = lists:flatten(Info),
+ ?vtrace("send status (503) message", []),
+ httpd_response:send_status(SocketType, Socket, 503, String, ConfigDB),
+ %% This ugly thing is to make ssl deliver the message, before the close...
+ close_sleep(SocketType, 1000),
+ ?vtrace("close the socket", []),
+ close(SocketType, Socket, ConfigDB).
+
+
+% handle_connection
+
+handle_connection(Manager, ConfigDB, SocketType, Socket) ->
+ ?vlog("handle connection: ~p", [Socket]),
+ Resolve = httpd_socket:resolve(SocketType),
+ Peername = httpd_socket:peername(SocketType, Socket),
+ InitData = #init_data{peername=Peername, resolve=Resolve},
+ TimeOut = httpd_util:lookup(ConfigDB, keep_alive_timeout, 150000),
+ NrOfRequest = httpd_util:lookup(ConfigDB, max_keep_alive_request, forever),
+ ?MODULE:do_next_connection(ConfigDB, InitData,
+ SocketType, Socket,NrOfRequest,TimeOut),
+ ?vlog("handle connection: done", []),
+ httpd_manager:done_connection(Manager),
+ ?vlog("handle connection: close socket", []),
+ close(SocketType, Socket, ConfigDB).
+
+
+% do_next_connection
+do_next_connection(_ConfigDB, _InitData, _SocketType, _Socket, NrOfRequests,
+ _Timeout) when NrOfRequests < 1 ->
+ ?vtrace("do_next_connection: done", []),
+ ok;
+do_next_connection(ConfigDB, InitData, SocketType, Socket, NrOfRequests,
+ Timeout) ->
+ Peername = InitData#init_data.peername,
+ case (catch read(ConfigDB, SocketType, Socket, InitData, Timeout)) of
+ {'EXIT', Reason} ->
+ ?vlog("exit reading from socket: ~p",[Reason]),
+ error_logger:error_report({'EXIT',Reason}),
+ String =
+ lists:flatten(
+ io_lib:format("exit reading from socket: ~p => ~n~p~n",
+ [Socket, Reason])),
+ error_log(mod_log,
+ SocketType, Socket, ConfigDB, Peername, String),
+ error_log(mod_disk_log,
+ SocketType, Socket, ConfigDB, Peername, String);
+ {error, Reason} ->
+ handle_read_error(Reason,SocketType,Socket,ConfigDB,Peername);
+ Info when record(Info, mod) ->
+ case Info#mod.connection of
+ true ->
+ ReqTimeout = httpd_util:lookup(ConfigDB,
+ keep_alive_timeout, 150000),
+ ?MODULE:do_next_connection(ConfigDB, InitData,
+ SocketType, Socket,
+ dec(NrOfRequests), ReqTimeout);
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
+
+
+%% read
+read(ConfigDB, SocketType, Socket, InitData, Timeout) ->
+ ?vdebug("read from socket ~p with Timeout ~p",[Socket, Timeout]),
+ MaxHdrSz = httpd_util:lookup(ConfigDB, max_header_size, 10240),
+ case ?MODULE:read_header(SocketType, Socket, Timeout, MaxHdrSz,
+ ConfigDB, InitData, []) of
+ {socket_closed, Reason} ->
+ ?vlog("Socket closed while reading request header: "
+ "~n ~p", [Reason]),
+ socket_close;
+ {error, Error} ->
+ {error, Error};
+ {ok, Info, EntityBodyPart} ->
+ read1(SocketType, Socket, ConfigDB, InitData, Timeout, Info,
+ EntityBodyPart)
+ end.
+
+%% Got the head and maybe a part of the body: read in the rest
+read1(SocketType, Socket, ConfigDB, InitData, Timeout, Info, BodyPart)->
+ MaxBodySz = httpd_util:lookup(ConfigDB, max_body_size, nolimit),
+ ContentLength = content_length(Info),
+ ?vtrace("ContentLength: ~p", [ContentLength]),
+ case read_entity_body(SocketType, Socket, Timeout, MaxBodySz,
+ ContentLength, BodyPart, Info, ConfigDB) of
+ {socket_closed, Reason} ->
+ ?vlog("Socket closed while reading request body: "
+ "~n ~p", [Reason]),
+ socket_close;
+ {ok, EntityBody} ->
+ finish_request(EntityBody, [], Info);
+ {ok, ExtraHeader, EntityBody} ->
+ finish_request(EntityBody, ExtraHeader, Info);
+ Response ->
+ httpd_socket:close(SocketType, Socket),
+ socket_closed
+ %% Catch up all bad return values
+ end.
+
+
+%% The request is read in send it forward to the module that
+%% generates the response
+
+finish_request(EntityBody, ExtraHeader,
+ #mod{parsed_header = ParsedHeader} = Info)->
+ ?DEBUG("finish_request -> ~n"
+ " EntityBody: ~p~n"
+ " ExtraHeader: ~p~n"
+ " ParsedHeader: ~p~n",
+ [EntityBody, ExtraHeader, ParsedHeader]),
+ httpd_response:send(Info#mod{parsed_header = ParsedHeader ++ ExtraHeader,
+ entity_body = EntityBody}).
+
+
+%% read_header
+
+%% This algorithm rely on the buffer size of the inet driver together
+%% with the {active, once} socket option. Atmost one message of this
+%% size will be received at a given time. When a full header has been
+%% read, the body is read with the recv function (the body size is known).
+%%
+read_header(SocketType, Socket, Timeout, MaxHdrSz, ConfigDB,
+ InitData, SoFar0) ->
+ T = t(),
+ %% remove any newlines at the begining, they might be crap from ?
+ SoFar = remove_newline(SoFar0),
+
+ case terminated_header(MaxHdrSz, SoFar) of
+ {true, Header, EntityBodyPart} ->
+ ?vdebug("read_header -> done reading header: "
+ "~n length(Header): ~p"
+ "~n length(EntityBodyPart): ~p",
+ [length(Header), length(EntityBodyPart)]),
+ transform_header(SocketType, Socket, Header, ConfigDB, InitData,
+ EntityBodyPart);
+ false ->
+ ?vtrace("read_header -> "
+ "~n set active = 'once' and "
+ "await a chunk of the header", []),
+
+ case httpd_socket:active_once(SocketType, Socket) of
+ ok ->
+ receive
+ %%
+ %% TCP
+ %%
+ {tcp, Socket, Data} ->
+ ?vtrace("read_header(ip) -> got some data: ~p",
+ [sz(Data)]),
+ ?MODULE:read_header(SocketType, Socket,
+ Timeout - (t()-T),
+ MaxHdrSz, ConfigDB,
+ InitData, SoFar ++ Data);
+ {tcp_closed, Socket} ->
+ ?vtrace("read_header(ip) -> socket closed",[]),
+ {socket_closed,normal};
+ {tcp_error, Socket, Reason} ->
+ ?vtrace("read_header(ip) -> socket error: ~p",
+ [Reason]),
+ {socket_closed, Reason};
+
+ %%
+ %% SSL
+ %%
+ {ssl, Socket, Data} ->
+ ?vtrace("read_header(ssl) -> got some data: ~p",
+ [sz(Data)]),
+ ?MODULE:read_header(SocketType, Socket,
+ Timeout - (t()-T),
+ MaxHdrSz, ConfigDB,
+ InitData, SoFar ++ Data);
+ {ssl_closed, Socket} ->
+ ?vtrace("read_header(ssl) -> socket closed", []),
+ {socket_closed, normal};
+ {ssl_error, Socket, Reason} ->
+ ?vtrace("read_header(ssl) -> socket error: ~p",
+ [Reason]),
+ {socket_closed, Reason}
+
+ after Timeout ->
+ ?vlog("read_header -> timeout", []),
+ {socket_closed, timeout}
+ end;
+
+ Error ->
+ httpd_response:send_status(SocketType, Socket,
+ 500, none, ConfigDB),
+ Error
+ end
+ end.
+
+
+terminated_header(MaxHdrSz, Data) ->
+ D1 = lists:flatten(Data),
+ ?vtrace("terminated_header -> Data size: ~p",[sz(D1)]),
+ case hsplit(MaxHdrSz,[],D1) of
+ not_terminated ->
+ false;
+ [Header, EntityBodyPart] ->
+ {true, Header++"\r\n\r\n",EntityBodyPart}
+ end.
+
+
+transform_header(SocketType, Socket, Request, ConfigDB, InitData, BodyPart) ->
+ case httpd_parse:request_header(Request) of
+ {not_implemented, RequestLine, Method, RequestURI, ParsedHeader,
+ HTTPVersion} ->
+ httpd_response:send_status(SocketType, Socket, 501,
+ {Method, RequestURI, HTTPVersion},
+ ConfigDB),
+ {error,"Not Implemented"};
+ {bad_request, {forbidden, URI}} ->
+ httpd_response:send_status(SocketType, Socket, 403, URI, ConfigDB),
+ {error,"Forbidden Request"};
+ {bad_request, Reason} ->
+ httpd_response:send_status(SocketType, Socket, 400, none,
+ ConfigDB),
+ {error,"Malformed request"};
+ {ok,[Method, RequestURI, HTTPVersion, RequestLine, ParsedHeader]} ->
+ ?DEBUG("send -> ~n"
+ " Method: ~p~n"
+ " RequestURI: ~p~n"
+ " HTTPVersion: ~p~n"
+ " RequestLine: ~p~n",
+ [Method, RequestURI, HTTPVersion, RequestLine]),
+ {ok, Info} =
+ httpd_parse:get_request_record(Socket, SocketType, ConfigDB,
+ Method, RequestURI, HTTPVersion,
+ RequestLine, ParsedHeader,
+ [], InitData),
+ %% Control that the Host header field is provided
+ case Info#mod.absolute_uri of
+ nohost ->
+ case Info#mod.http_version of
+ "HTTP/1.1" ->
+ httpd_response:send_status(Info, 400, none),
+ {error,"No host specified"};
+ _ ->
+ {ok, Info, BodyPart}
+ end;
+ _ ->
+ {ok, Info, BodyPart}
+ end
+ end.
+
+
+hsplit(_MaxHdrSz, Accu,[]) ->
+ not_terminated;
+hsplit(_MaxHdrSz, Accu, [ $\r, $\n, $\r, $\n | Tail]) ->
+ [lists:reverse(Accu), Tail];
+hsplit(nolimit, Accu, [H|T]) ->
+ hsplit(nolimit,[H|Accu],T);
+hsplit(MaxHdrSz, Accu, [H|T]) when length(Accu) < MaxHdrSz ->
+ hsplit(MaxHdrSz,[H|Accu],T);
+hsplit(MaxHdrSz, Accu, D) ->
+ throw({error,{header_too_long,length(Accu),length(D)}}).
+
+
+
+%%----------------------------------------------------------------------
+%% The http/1.1 standard chapter 8.2.3 says that a request containing
+%% An Except header-field must be responded to by 100 (Continue) by
+%% the server before the client sends the body.
+%%----------------------------------------------------------------------
+
+read_entity_body(SocketType, Socket, Timeout, Max, Length, BodyPart, Info,
+ ConfigDB) when integer(Max) ->
+ case expect(Info#mod.http_version, Info#mod.parsed_header, ConfigDB) of
+ continue when Max > Length ->
+ ?DEBUG("read_entity_body()->100 Continue ~n", []),
+ httpd_response:send_status(Info, 100, ""),
+ read_entity_body2(SocketType, Socket, Timeout, Max, Length,
+ BodyPart, Info, ConfigDB);
+ continue when Max < Length ->
+ httpd_response:send_status(Info, 417, "Body to big"),
+ httpd_socket:close(SocketType, Socket),
+ {socket_closed,"Expect denied according to size"};
+ break ->
+ httpd_response:send_status(Info, 417, "Method not allowed"),
+ httpd_socket:close(SocketType, Socket),
+ {socket_closed,"Expect conditions was not fullfilled"};
+ no_expect_header ->
+ read_entity_body2(SocketType, Socket, Timeout, Max, Length,
+ BodyPart, Info, ConfigDB);
+ http_1_0_expect_header ->
+ httpd_response:send_status(Info, 400,
+ "Only HTTP/1.1 Clients "
+ "may use the Expect Header"),
+ httpd_socket:close(SocketType, Socket),
+ {socket_closed,"Due to a HTTP/1.0 expect header"}
+ end;
+
+read_entity_body(SocketType, Socket, Timeout, Max, Length, BodyPart,
+ Info, ConfigDB) ->
+ case expect(Info#mod.http_version, Info#mod.parsed_header, ConfigDB) of
+ continue ->
+ ?DEBUG("read_entity_body() -> 100 Continue ~n", []),
+ httpd_response:send_status(Info, 100, ""),
+ read_entity_body2(SocketType, Socket, Timeout, Max, Length,
+ BodyPart, Info, ConfigDB);
+ break->
+ httpd_response:send_status(Info, 417, "Method not allowed"),
+ httpd_socket:close(SocketType, Socket),
+ {socket_closed,"Expect conditions was not fullfilled"};
+ no_expect_header ->
+ read_entity_body2(SocketType, Socket, Timeout, Max, Length,
+ BodyPart, Info, ConfigDB);
+ http_1_0_expect_header ->
+ httpd_response:send_status(Info, 400,
+ "HTTP/1.0 Clients are not allowed "
+ "to use the Expect Header"),
+ httpd_socket:close(SocketType, Socket),
+ {socket_closed,"Expect header field in an HTTP/1.0 request"}
+ end.
+
+%%----------------------------------------------------------------------
+%% control if the body is transfer encoded
+%%----------------------------------------------------------------------
+read_entity_body2(SocketType, Socket, Timeout, Max, Length, BodyPart,
+ Info, ConfigDB) ->
+ ?DEBUG("read_entity_body2() -> "
+ "~n Max: ~p"
+ "~n Length: ~p"
+ "~n Socket: ~p", [Max, Length, Socket]),
+
+ case transfer_coding(Info) of
+ {chunked, ChunkedData} ->
+ ?DEBUG("read_entity_body2() -> "
+ "Transfer-encoding: Chunked Data: BodyPart ~s", [BodyPart]),
+ read_chunked_entity(Info, Timeout, Max, Length, ChunkedData, [],
+ BodyPart);
+ unknown_coding ->
+ ?DEBUG("read_entity_body2() -> Transfer-encoding: Unknown",[]),
+ httpd_response:send_status(Info, 501, "Unknown Transfer-Encoding"),
+ httpd_socket:close(SocketType, Socket),
+ {socket_closed,"Expect conditions was not fullfilled"};
+ none ->
+ ?DEBUG("read_entity_body2() -> Transfer-encoding: none",[]),
+ read_entity_body(SocketType, Socket, Timeout, Max, Length,
+ BodyPart)
+ end.
+
+
+%%----------------------------------------------------------------------
+%% The body was plain read it from the socket
+%% ----------------------------------------------------------------------
+read_entity_body(_SocketType, _Socket, _Timeout, _Max, 0, _BodyPart) ->
+ {ok, []};
+
+read_entity_body(_SocketType, _Socket, _Timeout, Max, Len, _BodyPart)
+ when Max < Len ->
+ ?vlog("body to long: "
+ "~n Max: ~p"
+ "~n Len: ~p", [Max,Len]),
+ throw({error,{body_too_long,Max,Len}});
+
+%% OTP-4409: Fixing POST problem
+read_entity_body(_,_,_,_, Len, BodyPart) when Len == length(BodyPart) ->
+ ?vtrace("read_entity_body -> done when"
+ "~n Len = length(BodyPart): ~p", [Len]),
+ {ok, BodyPart};
+
+%% OTP-4550: Fix problem with trailing garbage produced by some clients.
+read_entity_body(_, _, _, _, Len, BodyPart) when Len < length(BodyPart) ->
+ ?vtrace("read_entity_body -> done when"
+ "~n Len: ~p"
+ "~n length(BodyPart): ~p", [Len, length(BodyPart)]),
+ {ok, lists:sublist(BodyPart,Len)};
+
+read_entity_body(SocketType, Socket, Timeout, Max, Len, BodyPart) ->
+ ?vtrace("read_entity_body -> entry when"
+ "~n Len: ~p"
+ "~n length(BodyPart): ~p", [Len, length(BodyPart)]),
+ %% OTP-4548:
+ %% The length calculation was previously (inets-2.*) done in the
+ %% read function. As of 3.0 it was removed from read but not
+ %% included here.
+ L = Len - length(BodyPart),
+ case httpd_socket:recv(SocketType, Socket, L, Timeout) of
+ {ok, Body} ->
+ ?vtrace("read_entity_body -> received some data:"
+ "~n length(Body): ~p", [length(Body)]),
+ {ok, BodyPart ++ Body};
+ {error,closed} ->
+ {socket_closed,normal};
+ {error,etimedout} ->
+ {socket_closed, timeout};
+ {error,Reason} ->
+ {socket_closed, Reason};
+ Other ->
+ {socket_closed, Other}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% If the body of the message is encoded used the chunked transfer encoding
+%% it looks somethin like this:
+%% METHOD URI HTTP/VSN
+%% Transfer-Encoding: chunked
+%% CRLF
+%% ChunkSize
+%% Chunk
+%% ChunkSize
+%% Chunk
+%% 0
+%% Trailer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+read_chunked_entity(Info, Timeout, Max, Length, ChunkedData, Body, []) ->
+ ?DEBUG("read_chunked_entity()->:no_chunks ~n", []),
+ read_chunked_entity(Info#mod.socket_type, Info#mod.socket,
+ Timeout, Max, Length, ChunkedData, Body,
+ Info#mod.config_db, Info);
+
+read_chunked_entity(Info, Timeout, Max, Length, ChunkedData, Body, BodyPart) ->
+ %% Get the size
+ ?DEBUG("read_chunked_entity() -> PrefetchedBodyPart: ~p ~n",[BodyPart]),
+ case parse_chunk_size(Info, Timeout, BodyPart) of
+ {ok, Size, NewBodyPart} when Size > 0 ->
+ ?DEBUG("read_chunked_entity() -> Size: ~p ~n", [Size]),
+ case parse_chunked_entity_body(Info, Timeout, Max, length(Body),
+ Size, NewBodyPart) of
+ {ok, Chunk, NewBodyPart1} ->
+ ?DEBUG("read_chunked_entity()->Size: ~p ~n", [Size]),
+ read_chunked_entity(Info, Timeout, Max, Length,
+ ChunkedData, Body ++ Chunk,
+ NewBodyPart1);
+ OK ->
+ httpd_socket:close(Info#mod.socket_type, Info#mod.socket),
+ {socket_closed, error}
+ end;
+ {ok, 0, Trailers} ->
+ ?DEBUG("read_chunked_entity()->Size: 0, Trailers: ~s Body: ~s ~n",
+ [Trailers, Body]),
+ case parse_chunk_trailer(Info, Timeout, Info#mod.config_db,
+ Trailers) of
+ {ok, TrailerFields} ->
+ {ok, TrailerFields, Body};
+ _->
+ {ok, []}
+ end;
+ Error ->
+ Error
+ end.
+
+
+parse_chunk_size(Info, Timeout, BodyPart) ->
+ case httpd_util:split(remove_newline(BodyPart), "\r\n", 2) of
+ {ok, [Size, Body]} ->
+ ?DEBUG("parse_chunk_size()->Size: ~p ~n", [Size]),
+ {ok, httpd_util:hexlist_to_integer(Size), Body};
+ {ok, [Size]} ->
+ ?DEBUG("parse_chunk_size()->Size: ~p ~n", [Size]),
+ Sz = get_chunk_size(Info#mod.socket_type,
+ Info#mod.socket, Timeout,
+ lists:reverse(Size)),
+ {ok, Sz, []}
+ end.
+
+%%----------------------------------------------------------------------
+%% We got the chunk size get the chunk
+%%
+%% Max: Max numbers of bytes to read may also be undefined
+%% Length: Numbers of bytes already read
+%% Size Numbers of byte to read for the chunk
+%%----------------------------------------------------------------------
+
+%% body to big
+parse_chunked_entity_body(Info, Timeout, Max, Length, Size, BodyPart)
+ when Max =< (Length + Size) ->
+ {error, body_to_big};
+
+%% Prefetched body part is bigger than the current chunk
+%% (i.e. BodyPart includes more than one chunk)
+parse_chunked_entity_body(Info, Timeout, Max, Length, Size, BodyPart)
+ when (Size+2) =< length(BodyPart) ->
+ Chunk = string:substr(BodyPart, 1, Size),
+ Rest = string:substr(BodyPart, Size+3),
+ ?DEBUG("parse_chunked_entity_body() -> ~nChunk: ~s ~nRest: ~s ~n",
+ [Chunk, Rest]),
+ {ok, Chunk, Rest};
+
+
+%% We just got a part of the current chunk
+parse_chunked_entity_body(Info, Timeout, Max, Length, Size, BodyPart) ->
+ %% OTP-4551:
+ %% Subtracting BodyPart from Size does not produce an integer
+ %% when BodyPart is a list...
+ Remaining = Size - length(BodyPart),
+ LastPartOfChunk = read_chunked_entity_body(Info#mod.socket_type,
+ Info#mod.socket,
+ Timeout, Max,
+ Length, Remaining),
+ %% Remove newline
+ httpd_socket:recv(Info#mod.socket_type, Info#mod.socket, 2, Timeout),
+ ?DEBUG("parse_chunked_entity_body() -> "
+ "~nBodyPart: ~s"
+ "~nLastPartOfChunk: ~s ~n",
+ [BodyPart, LastPartOfChunk]),
+ {ok, BodyPart ++ LastPartOfChunk, []}.
+
+
+%%----------------------------------------------------------------------
+%% If the data we got along with the header contained the whole chunked body
+%% It may aswell contain the trailer :-(
+%%----------------------------------------------------------------------
+%% Either trailer begins with \r\n and then all data is there or
+%% The trailer has data then read upto \r\n\r\n
+parse_chunk_trailer(Info,Timeout,ConfigDB,"\r\n")->
+ {ok,[]};
+parse_chunk_trailer(Info,Timeout,ConfigDB,Trailers) ->
+ ?DEBUG("parse_chunk_trailer()->Trailers: ~s ~n", [Trailers]),
+ case string:rstr(Trailers,"\r\n\r\n") of
+ 0 ->
+ MaxHdrSz=httpd_util:lookup(ConfigDB, max_header_size, 10240),
+ read_trailer_end(Info,Timeout,MaxHdrSz,Trailers);
+ _->
+ %%We got the whole header parse it up
+ parse_trailers(Trailers)
+ end.
+
+parse_trailers(Trailer)->
+ ?DEBUG("parse_trailer()->Trailer: ~s",[Trailer]),
+ {ok,[Fields0|Crap]}=httpd_util:split(Trailer,"\r\n\r\n",2),
+ Fields=string:tokens(Fields0,"\r\n"),
+ [getTrailerField(X)||X<-Fields,lists:member($:,X)].
+
+
+read_trailer_end(Info,Timeout,MaxHdrSz,[])->
+ ?DEBUG("read_trailer_end()->[]",[]),
+ case read_trailer(Info#mod.socket_type,Info#mod.socket,
+ Timeout,MaxHdrSz,[],[],
+ httpd_util:key1search(Info#mod.parsed_header,"trailer",[])) of
+ {ok,Trailers}->
+ Trailers;
+ _->
+ []
+ end;
+read_trailer_end(Info,Timeout,MaxHdrSz,Trailers)->
+ ?DEBUG("read_trailer_end()->Trailers: ~s ~n ",[Trailers]),
+ %% Get the last paart of the the last headerfield
+ End=lists:reverse(lists:takewhile(fun(X)->case X of 10 ->false;13->false;_ ->true end end,lists:reverse(Trailers))),
+ Fields0=regexp:split(Trailers,"\r\n"),
+ %%Get rid of the last header field
+ [_Last|Fields]=lists:reverse(Fields0),
+ Headers=[getTrailerField(X)||X<-Fields,lists:member($:,X)],
+ case read_trailer(Info#mod.socket_type,Info#mod.socket,
+ Timeout,MaxHdrSz,Headers,End,
+ httpd_util:key1search(Info#mod.parsed_header,"trailer",[])) of
+ {ok,Trailers}->
+ Trailers;
+ _->
+ []
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% The code below is a a good way to read in chunked encoding but
+%% that require that the encoding comes from a stream and not from a list
+%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
+
+%%----------------------------------------------------------------------
+%% The body is encoded by chubnked encoding read it in
+%% ChunkedData= Chunked extensions
+%% Body= the inread chunked body
+%% Max: Max numbers of bytes to read
+%% Length: Numbers of bytes already readed
+%% Size Numbers of byte to read for the chunk
+%%----------------------------------------------------------------------
+
+
+
+read_chunked_entity(SocketType, Socket, Timeout, Max, Length, ChunkedData,
+ Body, ConfigDB, Info) ->
+ T = t(),
+ case get_chunk_size(SocketType,Socket,Timeout,[]) of
+ Size when integer(Size), Size>0 ->
+ case read_chunked_entity_body(SocketType, Socket,
+ Timeout-(t()-T),
+ Max, length(Body), Size) of
+ {ok,Chunk} ->
+ ?DEBUG("read_chunked_entity/9 Got a chunk: ~p " ,[Chunk]),
+ %% Two bytes are left of the chunk, that is the CRLF
+ %% at the end that is not a part of the message
+ %% So we read it and do nothing with it.
+ httpd_socket:recv(SocketType,Socket,2,Timeout-(t()-T)),
+ read_chunked_entity(SocketType, Socket, Timeout-(t()-T),
+ Max, Length, ChunkedData, Body++Chunk,
+ ConfigDB, Info);
+ Error ->
+ ?DEBUG("read_chunked_entity/9 Error: ~p " ,[Error]),
+ httpd_socket:close(SocketType,Socket),
+ {socket_closed,error}
+ end;
+ Size when integer(Size), Size == 0 ->
+ %% Must read in any trailer fields here
+ read_chunk_trailer(SocketType, Socket, Timeout,
+ Max, Info, ChunkedData, Body, ConfigDB);
+ Error ->
+ Error
+ end.
+
+
+%% If a user wants to send header data after the chunked data we
+%% must pick it out
+read_chunk_trailer(SocketType, Socket, Timeout, Max, Info, ChunkedData,
+ Body, ConfigDB) ->
+ ?DEBUG("read_chunk_trailer/8: ~p " ,[Body]),
+ MaxHdrSz = httpd_util:lookup(ConfigDB,max_header_size,10240),
+ case httpd_util:key1search(Info#mod.parsed_header,"trailer")of
+ undefined ->
+ {ok,Body};
+ Fields ->
+ case read_trailer(SocketType, Socket, Timeout,
+ MaxHdrSz, [], [],
+ string:tokens(
+ httpd_util:to_lower(Fields),",")) of
+ {ok,[]} ->
+ {ok,Body};
+ {ok,HeaderFields} ->
+ % ParsedExtraHeaders =
+ % httpd_parse:tagup_header(httpd_parse:split_lines(HeaderFields)),
+ {ok,HeaderFields,Body};
+ Error ->
+ Error
+ end
+ end.
+
+read_chunked_entity_body(SocketType, Socket, Timeout, Max, Length, Size)
+ when integer(Max) ->
+ read_entity_body(SocketType, Socket, Timeout, Max-Length, Size, []);
+
+read_chunked_entity_body(SocketType, Socket, Timeout, Max, _Length, Size) ->
+ read_entity_body(SocketType, Socket, Timeout, Max, Size, []).
+
+%% If we read in the \r\n the httpd_util:hexlist_to_integer
+%% Will remove it and we get rid of it emmediatly :-)
+get_chunk_size(SocketType, Socket, Timeout, Size) ->
+ T = t(),
+ ?DEBUG("get_chunk_size: ~p " ,[Size]),
+ case httpd_socket:recv(SocketType,Socket,1,Timeout) of
+ {ok,[Digit]} when Digit==$\n ->
+ httpd_util:hexlist_to_integer(lists:reverse(Size));
+ {ok,[Digit]} ->
+ get_chunk_size(SocketType,Socket,Timeout-(t()-T),[Digit|Size]);
+ {error,closed} ->
+ {socket_closed,normal};
+ {error,etimedout} ->
+ {socket_closed, timeout};
+ {error,Reason} ->
+ {socket_closed, Reason};
+ Other ->
+ {socket_closed,Other}
+ end.
+
+
+
+
+%%----------------------------------------------------------------------
+%% Reads the HTTP-trailer
+%% Would be easy to tweak the read_head to do this but in this way
+%% the chunked encoding can be updated better.
+%%----------------------------------------------------------------------
+
+
+%% When end is reached
+%% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,Last,[]) ->
+%% {ok,Headers};
+
+%% When header to big
+read_trailer(_,_,_,MaxHdrSz,Headers,Bs,_Fields)
+ when MaxHdrSz < length(Headers) ->
+ ?vlog("header to long: "
+ "~n MaxHdrSz: ~p"
+ "~n length(Bs): ~p", [MaxHdrSz,length(Bs)]),
+ throw({error,{header_too_long,MaxHdrSz,length(Bs)}});
+
+%% The last Crlf is there
+read_trailer(_, _, _, _, Headers, [$\n, $\r], _) ->
+ {ok,Headers};
+
+read_trailer(SocketType, Socket, Timeout, MaxHdrSz, Headers,
+ [$\n, $\r|Rest], Fields) ->
+ case getTrailerField(lists:reverse(Rest))of
+ {error,Reason}->
+ {error,"Bad trailer"};
+ {HeaderField,Value}->
+ case lists:member(HeaderField,Fields) of
+ true ->
+ read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
+ [{HeaderField,Value} |Headers],[],
+ lists:delete(HeaderField,Fields));
+ false ->
+ read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
+ Headers,[],Fields)
+ end
+ end;
+
+% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,[$\n, $\r|Rest],Fields) ->
+% case Rest of
+% [] ->
+% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,Rest,Fields);
+% Field ->
+% case getTrailerField(lists:reverse(Rest))of
+% {error,Reason}->
+% {error,"Bad trailer"};
+% {HeaderField,Value}->
+% case lists:member(HeaderField,Fields) of
+% true ->
+% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
+% [{HeaderField,Value} |Headers],[],
+% lists:delete(HeaderField,Fields));
+% false ->
+% read_trailer(SocketType,Socket,Timeout,MaxHdrSz,
+% Headers,[],Fields)
+% end
+% end
+% end;
+
+read_trailer(SocketType,Socket,Timeout,MaxHdrSz,Headers,Bs,Fields) ->
+ %% ?vlog("read_header -> entry with Timeout: ~p",[Timeout]),
+ T = t(),
+ case (catch httpd_socket:recv(SocketType,Socket,1,Timeout)) of
+ {ok,[B]} ->
+ read_trailer(SocketType, Socket, Timeout-(t()-T),
+ MaxHdrSz, Headers, [B|Bs], Fields);
+ {error,closed} ->
+ {socket_closed,normal};
+ {error,etimedout} ->
+ {socket_closed, timeout};
+ {error,Reason} ->
+ {socket_closed, Reason};
+ Other ->
+ {socket_closed,Other}
+ end.
+
+getTrailerField(HeaderField)->
+ case string:str(HeaderField,":") of
+ 0->
+ {error,"badheaderfield"};
+ Number ->
+ {httpd_util:to_lower(string:substr(HeaderField,1,Number-1)),
+ httpd_util:to_lower(string:substr(HeaderField,Number+1))}
+ end.
+
+
+
+
+%% Time in milli seconds
+t() ->
+ {A,B,C} = erlang:now(),
+ A*1000000000+B*1000+(C div 1000).
+
+%%----------------------------------------------------------------------
+%% If the user sends an expect header-field with the value 100-continue
+%% We must send a 100 status message if he is a HTTP/1.1 client.
+
+%% If it is an HTTP/1.0 client it's little more difficult.
+%% If expect is not defined it is easy but in the other case shall we
+%% Break or the transmission or let it continue the standard is not clear
+%% if to break connection or wait for data.
+%%----------------------------------------------------------------------
+expect(HTTPVersion,ParsedHeader,ConfigDB)->
+ case HTTPVersion of
+ [$H,$T,$T,$P,$\/,$1,$.,N|_Whatever]when N>=1->
+ case httpd_util:key1search(ParsedHeader,"expect") of
+ "100-continue" ->
+ continue;
+ undefined ->
+ no_expect_header;
+ NewValue ->
+ break
+ end;
+ _OldVersion ->
+ case httpd_util:key1search(ParsedHeader,"expect") of
+ undefined ->
+ no_expect_header;
+ NewValue ->
+ case httpd_util:lookup(ConfigDB,expect,continue) of
+ continue->
+ no_expect_header;
+ _ ->
+ http_1_0_expect_header
+ end
+ end
+ end.
+
+
+%%----------------------------------------------------------------------
+%% According to the http/1.1 standard all applications must understand
+%% Chunked encoded data. (Last line chapter 3.6.1).
+transfer_coding(#mod{parsed_header = Ph}) ->
+ case httpd_util:key1search(Ph, "transfer-encoding", none) of
+ none ->
+ none;
+ [$c,$h,$u,$n,$k,$e,$d|Data]->
+ {chunked,Data};
+ _ ->
+ unknown_coding
+ end.
+
+
+
+handle_read_error({header_too_long,Max,Rem},
+ SocketType,Socket,ConfigDB,Peername) ->
+ String = io_lib:format("header too long: ~p : ~p",[Max,Rem]),
+ handle_read_error(ConfigDB,String,SocketType,Socket,Peername,
+ max_header_action,close);
+handle_read_error({body_too_long,Max,Actual},
+ SocketType,Socket,ConfigDB,Peername) ->
+ String = io_lib:format("body too long: ~p : ~p",[Max,Actual]),
+ handle_read_error(ConfigDB,String,SocketType,Socket,Peername,
+ max_body_action,close);
+handle_read_error(Error,SocketType,Socket,ConfigDB,Peername) ->
+ ok.
+
+
+handle_read_error(ConfigDB, ReasonString, SocketType, Socket, Peername,
+ Item, Default) ->
+ ?vlog("error reading request: ~s",[ReasonString]),
+ E = lists:flatten(
+ io_lib:format("Error reading request: ~s",[ReasonString])),
+ error_log(mod_log, SocketType, Socket, ConfigDB, Peername, E),
+ error_log(mod_disk_log, SocketType, Socket, ConfigDB, Peername, E),
+ case httpd_util:lookup(ConfigDB,Item,Default) of
+ reply414 ->
+ send_read_status(SocketType, Socket, 414, ReasonString, ConfigDB);
+ _ ->
+ ok
+ end.
+
+send_read_status(SocketType, Socket, Code, ReasonString, ConfigDB) ->
+ httpd_response:send_status(SocketType, Socket, Code, ReasonString,
+ ConfigDB).
+
+
+error_log(Mod, SocketType, Socket, ConfigDB, Peername, String) ->
+ Modules = httpd_util:lookup(ConfigDB, modules,
+ [mod_get, mod_head, mod_log]),
+ case lists:member(Mod, Modules) of
+ true ->
+ Mod:error_log(SocketType, Socket, ConfigDB, Peername, String);
+ _ ->
+ ok
+ end.
+
+
+sz(L) when list(L) ->
+ length(L);
+sz(B) when binary(B) ->
+ size(B);
+sz(O) ->
+ {unknown_size,O}.
+
+
+%% Socket utility functions:
+
+close(SocketType, Socket, ConfigDB) ->
+ case httpd_socket:close(SocketType, Socket) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ ?vlog("error while closing socket: ~p",[Reason]),
+ ok
+ end.
+
+close_sleep({ssl, _}, Time) ->
+ sleep(Time);
+close_sleep(_, _) ->
+ ok.
+
+
+sleep(T) -> receive after T -> ok end.
+
+
+dec(N) when integer(N) ->
+ N-1;
+dec(N) ->
+ N.
+
+
+content_length(#mod{parsed_header = Ph}) ->
+ list_to_integer(httpd_util:key1search(Ph, "content-length","0")).
+
+
+remove_newline(List)->
+ lists:dropwhile(fun newline/1,List).
+
+newline($\r) ->
+ true;
+newline($\n) ->
+ true;
+newline(_Sign) ->
+ false.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_response.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_response.erl
new file mode 100644
index 0000000000..4c7f8e0c8f
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_response.erl
@@ -0,0 +1,437 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_response.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_response).
+-export([send/1, send_status/3, send_status/5]).
+
+%%code is the key for the statuscode ex: 200 404 ...
+-define(HTTP11HEADERFIELDS,[content_length, accept_ranges, cache_control, date,
+ pragma, trailer, transfer_encoding, etag, location,
+ retry_after, server, allow,
+ content_encoding, content_language,
+ content_location, content_MD5, content_range,
+ content_type, expires, last_modified]).
+
+-define(HTTP10HEADERFIELDS,[content_length, date, pragma, transfer_encoding,
+ location, server, allow, content_encoding,
+ content_type, last_modified]).
+
+-define(PROCEED_RESPONSE(StatusCode, Info),
+ {proceed,
+ [{response,{already_sent, StatusCode,
+ httpd_util:key1search(Info#mod.data,content_lenght)}}]}).
+
+
+-include("httpd.hrl").
+
+-define(VMODULE,"RESPONSE").
+-include("httpd_verbosity.hrl").
+
+%% send
+
+send(#mod{config_db = ConfigDB} = Info) ->
+ ?vtrace("send -> Request line: ~p", [Info#mod.request_line]),
+ Modules = httpd_util:lookup(ConfigDB,modules,[mod_get, mod_head, mod_log]),
+ case traverse_modules(Info, Modules) of
+ done ->
+ Info;
+ {proceed, Data} ->
+ case httpd_util:key1search(Data, status) of
+ {StatusCode, PhraseArgs, Reason} ->
+ ?vdebug("send -> proceed/status: ~n"
+ "~n StatusCode: ~p"
+ "~n PhraseArgs: ~p"
+ "~n Reason: ~p",
+ [StatusCode, PhraseArgs, Reason]),
+ send_status(Info, StatusCode, PhraseArgs),
+ Info;
+
+ undefined ->
+ case httpd_util:key1search(Data, response) of
+ {already_sent, StatusCode, Size} ->
+ ?vtrace("send -> already sent: "
+ "~n StatusCode: ~p"
+ "~n Size: ~p",
+ [StatusCode, Size]),
+ Info;
+ {response, Header, Body} -> %% New way
+ send_response(Info, Header, Body),
+ Info;
+ {StatusCode, Response} -> %% Old way
+ send_response_old(Info, StatusCode, Response),
+ Info;
+ undefined ->
+ ?vtrace("send -> undefined response", []),
+ send_status(Info, 500, none),
+ Info
+ end
+ end
+ end.
+
+
+%% traverse_modules
+
+traverse_modules(Info,[]) ->
+ {proceed,Info#mod.data};
+traverse_modules(Info,[Module|Rest]) ->
+ case (catch apply(Module,do,[Info])) of
+ {'EXIT', Reason} ->
+ ?vlog("traverse_modules -> exit reason: ~p",[Reason]),
+ String =
+ lists:flatten(
+ io_lib:format("traverse exit from apply: ~p:do => ~n~p",
+ [Module, Reason])),
+ report_error(mod_log, Info#mod.config_db, String),
+ report_error(mod_disk_log, Info#mod.config_db, String),
+ done;
+ done ->
+ done;
+ {break,NewData} ->
+ {proceed,NewData};
+ {proceed,NewData} ->
+ traverse_modules(Info#mod{data=NewData},Rest)
+ end.
+
+%% send_status %%
+
+
+send_status(#mod{socket_type = SocketType,
+ socket = Socket,
+ connection = Conn} = Info, 100, _PhraseArgs) ->
+ ?DEBUG("send_status -> StatusCode: ~p~n",[100]),
+ Header = httpd_util:header(100, Conn),
+ httpd_socket:deliver(SocketType, Socket,
+ [Header, "Content-Length:0\r\n\r\n"]);
+
+send_status(#mod{socket_type = SocketType,
+ socket = Socket,
+ config_db = ConfigDB} = Info, StatusCode, PhraseArgs) ->
+ send_status(SocketType, Socket, StatusCode, PhraseArgs, ConfigDB).
+
+send_status(SocketType, Socket, StatusCode, PhraseArgs, ConfigDB) ->
+ ?DEBUG("send_status -> ~n"
+ " StatusCode: ~p~n"
+ " PhraseArgs: ~p",
+ [StatusCode, PhraseArgs]),
+ Header = httpd_util:header(StatusCode, "text/html", false),
+ ReasonPhrase = httpd_util:reason_phrase(StatusCode),
+ Message = httpd_util:message(StatusCode, PhraseArgs, ConfigDB),
+ Body = get_body(ReasonPhrase, Message),
+ Header1 =
+ Header ++
+ "Content-Length:" ++
+ integer_to_list(length(Body)) ++
+ "\r\n\r\n",
+ httpd_socket:deliver(SocketType, Socket, [Header1, Body]).
+
+
+get_body(ReasonPhrase, Message)->
+ "<HTML>
+ <HEAD>
+ <TITLE>"++ReasonPhrase++"</TITLE>
+ </HEAD>
+ <BODY>
+ <H1>"++ReasonPhrase++"</H1>\n"++Message++"\n</BODY>
+ </HTML>\n".
+
+
+%%% Create a response from the Key/Val tuples In the Head List
+%%% Body is a tuple {body,Fun(),Args}
+
+%% send_response
+%% Allowed Fields
+
+% HTTP-Version StatusCode Reason-Phrase
+% *((general-headers
+% response-headers
+% entity-headers)CRLF)
+% CRLF
+% ?(BODY)
+
+% General Header fields
+% ======================
+% Cache-Control cache_control
+% Connection %%Is set dependiong on the request
+% Date
+% Pramga
+% Trailer
+% Transfer-Encoding
+
+% Response Header field
+% =====================
+% Accept-Ranges
+% (Age) Mostly for proxys
+% Etag
+% Location
+% (Proxy-Authenticate) Only for proxies
+% Retry-After
+% Server
+% Vary
+% WWW-Authenticate
+%
+% Entity Header Fields
+% ====================
+% Allow
+% Content-Encoding
+% Content-Language
+% Content-Length
+% Content-Location
+% Content-MD5
+% Content-Range
+% Content-Type
+% Expires
+% Last-Modified
+
+
+send_response(Info, Header, Body) ->
+ ?vtrace("send_response -> (new) entry with"
+ "~n Header: ~p", [Header]),
+ case httpd_util:key1search(Header, code) of
+ undefined ->
+ %% No status code
+ %% Ooops this must be very bad:
+ %% generate a 404 content not availible
+ send_status(Info, 404, "The file is not availible");
+ StatusCode ->
+ case send_header(Info, StatusCode, Header) of
+ ok ->
+ send_body(Info, StatusCode, Body);
+ Error ->
+ ?vlog("head delivery failure: ~p", [Error]),
+ done
+ end
+ end.
+
+
+send_header(#mod{socket_type = Type, socket = Sock,
+ http_version = Ver, connection = Conn} = Info,
+ StatusCode, Head0) ->
+ ?vtrace("send_haeder -> entry with"
+ "~n Ver: ~p"
+ "~n Conn: ~p", [Ver, Conn]),
+ Head1 = create_header(Ver, Head0),
+ StatusLine = [Ver, " ",
+ io_lib:write(StatusCode), " ",
+ httpd_util:reason_phrase(StatusCode), "\r\n"],
+ Connection = get_connection(Conn, Ver),
+ Head = list_to_binary([StatusLine, Head1, Connection,"\r\n"]),
+ ?vtrace("deliver head", []),
+ httpd_socket:deliver(Type, Sock, Head).
+
+
+send_body(_, _, nobody) ->
+ ?vtrace("send_body -> no body", []),
+ ok;
+
+send_body(#mod{socket_type = Type, socket = Sock},
+ StatusCode, Body) when list(Body) ->
+ ?vtrace("deliver body of size ~p", [length(Body)]),
+ httpd_socket:deliver(Type, Sock, Body);
+
+send_body(#mod{socket_type = Type, socket = Sock} = Info,
+ StatusCode, {Fun, Args}) ->
+ case (catch apply(Fun, Args)) of
+ close ->
+ httpd_socket:close(Type, Sock),
+ done;
+
+ sent ->
+ ?PROCEED_RESPONSE(StatusCode, Info);
+
+ {ok, Body} ->
+ ?vtrace("deliver body", []),
+ case httpd_socket:deliver(Type, Sock, Body) of
+ ok ->
+ ?PROCEED_RESPONSE(StatusCode, Info);
+ Error ->
+ ?vlog("body delivery failure: ~p", [Error]),
+ done
+ end;
+
+ Error ->
+ ?vlog("failure of apply(~p,~p): ~p", [Fun, Args, Error]),
+ done
+ end;
+send_body(I, S, B) ->
+ ?vinfo("BAD ARGS: "
+ "~n I: ~p"
+ "~n S: ~p"
+ "~n B: ~p", [I, S, B]),
+ exit({bad_args, {I, S, B}}).
+
+
+%% Return a HTTP-header field that indicates that the
+%% connection will be inpersistent
+get_connection(true,"HTTP/1.0")->
+ "Connection:close\r\n";
+get_connection(false,"HTTP/1.1") ->
+ "Connection:close\r\n";
+get_connection(_,_) ->
+ "".
+
+
+create_header("HTTP/1.1", Data) ->
+ create_header1(?HTTP11HEADERFIELDS, Data);
+create_header(_, Data) ->
+ create_header1(?HTTP10HEADERFIELDS, Data).
+
+create_header1(Fields, Data) ->
+ ?DEBUG("create_header() -> "
+ "~n Fields :~p~n Data: ~p ~n", [Fields, Data]),
+ mapfilter(fun(Field)->
+ transform({Field, httpd_util:key1search(Data, Field)})
+ end, Fields, undefined).
+
+
+%% Do a map and removes the values that evaluates to RemoveVal
+mapfilter(Fun,List,RemoveVal)->
+ mapfilter(Fun,List,[],RemoveVal).
+
+mapfilter(Fun,[],[RemoveVal|Acc],RemoveVal)->
+ Acc;
+mapfilter(Fun,[],Acc,_RemoveVal)->
+ Acc;
+
+mapfilter(Fun,[Elem|Rest],[RemoveVal|Acc],RemoveVal)->
+ mapfilter(Fun,Rest,[Fun(Elem)|Acc],RemoveVal);
+mapfilter(Fun,[Elem|Rest],Acc,RemoveVal)->
+ mapfilter(Fun,Rest,[Fun(Elem)|Acc],RemoveVal).
+
+
+transform({content_type,undefined})->
+ ["Content-Type:text/plain\r\n"];
+
+transform({date,undefined})->
+ ["Date:",httpd_util:rfc1123_date(),"\r\n"];
+
+transform({date,RFCDate})->
+ ["Date:",RFCDate,"\r\n"];
+
+
+transform({_Key,undefined})->
+ undefined;
+transform({accept_ranges,Value})->
+ ["Accept-Ranges:",Value,"\r\n"];
+transform({cache_control,Value})->
+ ["Cache-Control:",Value,"\r\n"];
+transform({pragma,Value})->
+ ["Pragma:",Value,"\r\n"];
+transform({trailer,Value})->
+ ["Trailer:",Value,"\r\n"];
+transform({transfer_encoding,Value})->
+ ["Pragma:",Value,"\r\n"];
+transform({etag,Value})->
+ ["ETag:",Value,"\r\n"];
+transform({location,Value})->
+ ["Retry-After:",Value,"\r\n"];
+transform({server,Value})->
+ ["Server:",Value,"\r\n"];
+transform({allow,Value})->
+ ["Allow:",Value,"\r\n"];
+transform({content_encoding,Value})->
+ ["Content-Encoding:",Value,"\r\n"];
+transform({content_language,Value})->
+ ["Content-Language:",Value,"\r\n"];
+transform({retry_after,Value})->
+ ["Retry-After:",Value,"\r\n"];
+transform({server,Value})->
+ ["Server:",Value,"\r\n"];
+transform({allow,Value})->
+ ["Allow:",Value,"\r\n"];
+transform({content_encoding,Value})->
+ ["Content-Encoding:",Value,"\r\n"];
+transform({content_language,Value})->
+ ["Content-Language:",Value,"\r\n"];
+transform({content_location,Value})->
+ ["Content-Location:",Value,"\r\n"];
+transform({content_length,Value})->
+ ["Content-Length:",Value,"\r\n"];
+transform({content_MD5,Value})->
+ ["Content-MD5:",Value,"\r\n"];
+transform({content_range,Value})->
+ ["Content-Range:",Value,"\r\n"];
+transform({content_type,Value})->
+ ["Content-Type:",Value,"\r\n"];
+transform({expires,Value})->
+ ["Expires:",Value,"\r\n"];
+transform({last_modified,Value})->
+ ["Last-Modified:",Value,"\r\n"].
+
+
+
+%%----------------------------------------------------------------------
+%% This is the old way of sending data it is strongly encouraged to
+%% Leave this method and go on to the newer form of response
+%% OTP-4408
+%%----------------------------------------------------------------------
+
+send_response_old(#mod{socket_type = Type,
+ socket = Sock,
+ method = "HEAD"} = Info,
+ StatusCode, Response) ->
+ ?vtrace("send_response_old(HEAD) -> entry with"
+ "~n StatusCode: ~p"
+ "~n Response: ~p",
+ [StatusCode,Response]),
+ case httpd_util:split(lists:flatten(Response),"\r\n\r\n|\n\n",2) of
+ {ok, [Head, Body]} ->
+ Header =
+ httpd_util:header(StatusCode,Info#mod.connection) ++
+ "Content-Length:" ++ content_length(Body),
+ httpd_socket:deliver(Type, Sock, [Header,Head,"\r\n"]);
+
+ Error ->
+ send_status(Info, 500, "Internal Server Error")
+ end;
+
+send_response_old(#mod{socket_type = Type,
+ socket = Sock} = Info,
+ StatusCode, Response) ->
+ ?vtrace("send_response_old -> entry with"
+ "~n StatusCode: ~p"
+ "~n Response: ~p",
+ [StatusCode,Response]),
+ case httpd_util:split(lists:flatten(Response),"\r\n\r\n|\n\n",2) of
+ {ok, [_Head, Body]} ->
+ Header =
+ httpd_util:header(StatusCode,Info#mod.connection) ++
+ "Content-Length:" ++ content_length(Body),
+ httpd_socket:deliver(Type, Sock, [Header, Response]);
+
+ {ok, Body} ->
+ Header =
+ httpd_util:header(StatusCode,Info#mod.connection) ++
+ "Content-Length:" ++ content_length(Body) ++ "\r\n",
+ httpd_socket:deliver(Type, Sock, [Header, Response]);
+
+ {error, Reason} ->
+ send_status(Info, 500, "Internal Server Error")
+ end.
+
+content_length(Body)->
+ integer_to_list(httpd_util:flatlength(Body))++"\r\n".
+
+
+report_error(Mod, ConfigDB, Error) ->
+ Modules = httpd_util:lookup(ConfigDB, modules,
+ [mod_get, mod_head, mod_log]),
+ case lists:member(Mod, Modules) of
+ true ->
+ Mod:report_error(ConfigDB, Error);
+ _ ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_socket.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_socket.erl
new file mode 100644
index 0000000000..95dfc5e824
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_socket.erl
@@ -0,0 +1,381 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_socket.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_socket).
+-export([start/1,
+ listen/2, listen/3, accept/2, accept/3,
+ deliver/3, send/3, recv/4,
+ close/2,
+ peername/2, resolve/1, config/1,
+ controlling_process/3,
+ active_once/2]).
+
+-include("httpd.hrl").
+
+-define(VMODULE,"SOCKET").
+-include("httpd_verbosity.hrl").
+
+-include_lib("kernel/include/inet.hrl").
+
+%% start -> ok | {error,Reason}
+
+start(ip_comm) ->
+ case inet_db:start() of
+ {ok,_Pid} ->
+ ok;
+ {error,{already_started,_Pid}} ->
+ ok;
+ Error ->
+ Error
+ end;
+start({ssl,_SSLConfig}) ->
+ case ssl:start() of
+ ok ->
+ ok;
+ {ok, _} ->
+ ok;
+ {error,{already_started,_}} ->
+ ok;
+ Error ->
+ Error
+ end.
+
+%% listen
+
+listen(SocketType,Port) ->
+ listen(SocketType,undefined,Port).
+
+listen(ip_comm,Addr,Port) ->
+ ?DEBUG("listening(ip_comm) to port ~p", [Port]),
+ Opt = sock_opt(Addr,[{backlog,128},{reuseaddr,true}]),
+ case gen_tcp:listen(Port,Opt) of
+ {ok,ListenSocket} ->
+ ListenSocket;
+ Error ->
+ Error
+ end;
+listen({ssl,SSLConfig},Addr,Port) ->
+ ?DEBUG("listening(ssl) to port ~p"
+ "~n SSLConfig: ~p", [Port,SSLConfig]),
+ Opt = sock_opt(Addr,SSLConfig),
+ case ssl:listen(Port, Opt) of
+ {ok,ListenSocket} ->
+ ListenSocket;
+ Error ->
+ Error
+ end.
+
+
+sock_opt(undefined,Opt) -> [{packet,0},{active,false}|Opt];
+sock_opt(Addr,Opt) -> [{ip, Addr},{packet,0},{active,false}|Opt].
+
+%% -define(packet_type_http,true).
+%% -define(packet_type_httph,true).
+
+%% -ifdef(packet_type_http).
+%% sock_opt(undefined,Opt) -> [{packet,http},{active,false}|Opt];
+%% sock_opt(Addr,Opt) -> [{ip, Addr},{packet,http},{active,false}|Opt].
+%% -elif(packet_type_httph).
+%% sock_opt(undefined,Opt) -> [{packet,httph},{active,false}|Opt];
+%% sock_opt(Addr,Opt) -> [{ip, Addr},{packet,httph},{active,false}|Opt].
+%% -else.
+%% sock_opt(undefined,Opt) -> [{packet,0},{active,false}|Opt];
+%% sock_opt(Addr,Opt) -> [{ip, Addr},{packet,0},{active,false}|Opt].
+%% -endif.
+
+
+%% active_once
+
+active_once(Type, Sock) ->
+ active(Type, Sock, once).
+
+active(ip_comm, Sock, Active) ->
+ inet:setopts(Sock, [{active, Active}]);
+active({ssl, _SSLConfig}, Sock, Active) ->
+ ssl:setopts(Sock, [{active, Active}]).
+
+%% accept
+
+accept(A, B) ->
+ accept(A, B, infinity).
+
+
+accept(ip_comm,ListenSocket, T) ->
+ ?DEBUG("accept(ip_comm) on socket ~p", [ListenSocket]),
+ case gen_tcp:accept(ListenSocket, T) of
+ {ok,Socket} ->
+ Socket;
+ Error ->
+ ?vtrace("accept(ip_comm) failed for reason:"
+ "~n Error: ~p",[Error]),
+ Error
+ end;
+accept({ssl,_SSLConfig},ListenSocket, T) ->
+ ?DEBUG("accept(ssl) on socket ~p", [ListenSocket]),
+ case ssl:accept(ListenSocket, T) of
+ {ok,Socket} ->
+ Socket;
+ Error ->
+ ?vtrace("accept(ssl) failed for reason:"
+ "~n Error: ~p",[Error]),
+ Error
+ end.
+
+
+%% controlling_process
+
+controlling_process(ip_comm, Socket, Pid) ->
+ gen_tcp:controlling_process(Socket, Pid);
+controlling_process({ssl, _}, Socket, Pid) ->
+ ssl:controlling_process(Socket, Pid).
+
+
+%% deliver
+
+deliver(SocketType, Socket, IOListOrBinary) ->
+ case send(SocketType, Socket, IOListOrBinary) of
+% {error, einval} ->
+% ?vlog("deliver failed for reason: einval"
+% "~n SocketType: ~p"
+% "~n Socket: ~p"
+% "~n Data: ~p",
+% [SocketType, Socket, type(IOListOrBinary)]),
+% (catch close(SocketType, Socket)),
+% socket_closed;
+ {error, _Reason} ->
+ ?vlog("deliver(~p) failed for reason:"
+ "~n Reason: ~p",[SocketType,_Reason]),
+ (catch close(SocketType, Socket)),
+ socket_closed;
+ _ ->
+ ok
+ end.
+
+% type(L) when list(L) ->
+% {list, L};
+% type(B) when binary(B) ->
+% Decoded =
+% case (catch binary_to_term(B)) of
+% {'EXIT', _} ->
+% %% Oups, not a term, try list
+% case (catch binary_to_list(B)) of
+% %% Oups, not a list either, give up
+% {'EXIT', _} ->
+% {size, size(B)};
+% L ->
+% {list, L}
+% end;
+
+% T ->
+% {term, T}
+% end,
+% {binary, Decoded};
+% type(T) when tuple(T) ->
+% {tuple, T};
+% type(I) when integer(I) ->
+% {integer, I};
+% type(F) when float(F) ->
+% {float, F};
+% type(P) when pid(P) ->
+% {pid, P};
+% type(P) when port(P) ->
+% {port, P};
+% type(R) when reference(R) ->
+% {reference, R};
+% type(T) ->
+% {term, T}.
+
+
+
+send(ip_comm,Socket,Data) ->
+ ?DEBUG("send(ip_comm) -> ~p bytes on socket ~p",[data_size(Data),Socket]),
+ gen_tcp:send(Socket,Data);
+send({ssl,SSLConfig},Socket,Data) ->
+ ?DEBUG("send(ssl) -> ~p bytes on socket ~p",[data_size(Data),Socket]),
+ ssl:send(Socket, Data).
+
+recv(ip_comm,Socket,Length,Timeout) ->
+ ?DEBUG("recv(ip_comm) -> read from socket ~p",[Socket]),
+ gen_tcp:recv(Socket,Length,Timeout);
+recv({ssl,SSLConfig},Socket,Length,Timeout) ->
+ ?DEBUG("recv(ssl) -> read from socket ~p",[Socket]),
+ ssl:recv(Socket,Length,Timeout).
+
+-ifdef(inets_debug).
+data_size(L) when list(L) ->
+ httpd_util:flatlength(L);
+data_size(B) when binary(B) ->
+ size(B);
+data_size(O) ->
+ {unknown_size,O}.
+-endif.
+
+
+%% peername
+
+peername(ip_comm, Socket) ->
+ case inet:peername(Socket) of
+ {ok,{{A,B,C,D},Port}} ->
+ PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
+ integer_to_list(C)++"."++integer_to_list(D),
+ ?DEBUG("peername(ip_comm) on socket ~p: ~p",
+ [Socket,{Port,PeerName}]),
+ {Port,PeerName};
+ {error,Reason} ->
+ ?vlog("failed getting peername:"
+ "~n Reason: ~p"
+ "~n Socket: ~p",
+ [Reason,Socket]),
+ {-1,"unknown"}
+ end;
+peername({ssl,_SSLConfig},Socket) ->
+ case ssl:peername(Socket) of
+ {ok,{{A,B,C,D},Port}} ->
+ PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
+ integer_to_list(C)++"."++integer_to_list(D),
+ ?DEBUG("peername(ssl) on socket ~p: ~p",
+ [Socket, {Port,PeerName}]),
+ {Port,PeerName};
+ {error,_Reason} ->
+ {-1,"unknown"}
+ end.
+
+%% resolve
+
+resolve(_) ->
+ {ok,Name} = inet:gethostname(),
+ Name.
+
+%% close
+
+close(ip_comm,Socket) ->
+ Res =
+ case (catch gen_tcp:close(Socket)) of
+ ok -> ok;
+ {error,Reason} -> {error,Reason};
+ {'EXIT',{noproc,_}} -> {error,closed};
+ {'EXIT',Reason} -> {error,Reason};
+ Otherwise -> {error,Otherwise}
+ end,
+ ?vtrace("close(ip_comm) result: ~p",[Res]),
+ Res;
+close({ssl,_SSLConfig},Socket) ->
+ Res =
+ case (catch ssl:close(Socket)) of
+ ok -> ok;
+ {error,Reason} -> {error,Reason};
+ {'EXIT',{noproc,_}} -> {error,closed};
+ {'EXIT',Reason} -> {error,Reason};
+ Otherwise -> {error,Otherwise}
+ end,
+ ?vtrace("close(ssl) result: ~p",[Res]),
+ Res.
+
+%% config (debug: {certfile, "/var/tmp/server_root/conf/ssl_server.pem"})
+
+config(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,com_type,ip_comm) of
+ ssl ->
+ case ssl_certificate_file(ConfigDB) of
+ undefined ->
+ {error,
+ ?NICE("Directive SSLCertificateFile "
+ "not found in the config file")};
+ SSLCertificateFile ->
+ {ssl,
+ SSLCertificateFile++
+ ssl_certificate_key_file(ConfigDB)++
+ ssl_verify_client(ConfigDB)++
+ ssl_ciphers(ConfigDB)++
+ ssl_password(ConfigDB)++
+ ssl_verify_depth(ConfigDB)++
+ ssl_ca_certificate_file(ConfigDB)}
+ end;
+ ip_comm ->
+ ip_comm
+ end.
+
+ssl_certificate_file(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_certificate_file) of
+ undefined ->
+ undefined;
+ SSLCertificateFile ->
+ [{certfile,SSLCertificateFile}]
+ end.
+
+ssl_certificate_key_file(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_certificate_key_file) of
+ undefined ->
+ [];
+ SSLCertificateKeyFile ->
+ [{keyfile,SSLCertificateKeyFile}]
+ end.
+
+ssl_verify_client(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_verify_client) of
+ undefined ->
+ [];
+ SSLVerifyClient ->
+ [{verify,SSLVerifyClient}]
+ end.
+
+ssl_ciphers(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_ciphers) of
+ undefined ->
+ [];
+ Ciphers ->
+ [{ciphers, Ciphers}]
+ end.
+
+ssl_password(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_password_callback_module) of
+ undefined ->
+ [];
+ Module ->
+ case httpd_util:lookup(ConfigDB, ssl_password_callback_function) of
+ undefined ->
+ [];
+ Function ->
+ case catch apply(Module, Function, []) of
+ Password when list(Password) ->
+ [{password, Password}];
+ Error ->
+ error_report(ssl_password,Module,Function,Error),
+ []
+ end
+ end
+ end.
+
+ssl_verify_depth(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB, ssl_verify_client_depth) of
+ undefined ->
+ [];
+ Depth ->
+ [{depth, Depth}]
+ end.
+
+ssl_ca_certificate_file(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB, ssl_ca_certificate_file) of
+ undefined ->
+ [];
+ File ->
+ [{cacertfile, File}]
+ end.
+
+
+error_report(Where,M,F,Error) ->
+ error_logger:error_report([{?MODULE, Where}, {apply, {M, F, []}}, Error]).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_sup.erl
new file mode 100644
index 0000000000..fd557c30db
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_sup.erl
@@ -0,0 +1,203 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_sup.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The top supervisor for the inets application
+%%----------------------------------------------------------------------
+
+-module(httpd_sup).
+
+-behaviour(supervisor).
+
+-include("httpd_verbosity.hrl").
+
+%% public
+-export([start/2, start_link/2, start2/2, start_link2/2, stop/1, stop/2, stop2/1]).
+-export([init/1]).
+
+
+-define(D(F, A), io:format("~p:" ++ F ++ "~n", [?MODULE|A])).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% supervisor callback functions
+
+start(ConfigFile, Verbosity) ->
+ case start_link(ConfigFile, Verbosity) of
+ {ok, Pid} ->
+ unlink(Pid),
+ {ok, Pid};
+
+ Else ->
+ Else
+ end.
+
+
+start_link(ConfigFile, Verbosity) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok, ConfigList, Addr, Port} ->
+ Name = make_name(Addr, Port),
+ SupName = {local, Name},
+ supervisor:start_link(SupName, ?MODULE,
+ [ConfigFile, ConfigList,
+ Verbosity, Addr, Port]);
+
+ {error, Reason} ->
+ error_logger:error_report(Reason),
+ {stop, Reason};
+
+ Else ->
+ error_logger:error_report(Else),
+ {stop, Else}
+ end.
+
+
+start2(ConfigList, Verbosity) ->
+ case start_link2(ConfigList, Verbosity) of
+ {ok, Pid} ->
+ unlink(Pid),
+ {ok, Pid};
+
+ Else ->
+ Else
+ end.
+
+
+start_link2(ConfigList, Verbosity) ->
+ case get_addr_and_port2(ConfigList) of
+ {ok, Addr, Port} ->
+ Name = make_name(Addr, Port),
+ SupName = {local, Name},
+ supervisor:start_link(SupName, ?MODULE,
+ [undefined, ConfigList, Verbosity, Addr, Port]);
+
+ {error, Reason} ->
+ error_logger:error_report(Reason),
+ {stop, Reason};
+
+ Else ->
+ error_logger:error_report(Else),
+ {stop, Else}
+ end.
+
+
+
+stop(Pid) when pid(Pid) ->
+ do_stop(Pid);
+stop(ConfigFile) when list(ConfigFile) ->
+ case get_addr_and_port(ConfigFile) of
+ {ok, _, Addr, Port} ->
+ stop(Addr, Port);
+
+ Error ->
+ Error
+ end;
+stop(StartArgs) ->
+ ok.
+
+
+stop(Addr, Port) when integer(Port) ->
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ Pid when pid(Pid) ->
+ do_stop(Pid),
+ ok;
+ _ ->
+ not_started
+ end.
+
+stop2(ConfigList) when list(ConfigList) ->
+ {ok, Addr, Port} = get_addr_and_port2(ConfigList),
+ stop(Addr, Port).
+
+
+do_stop(Pid) ->
+ exit(Pid, shutdown).
+
+
+init([ConfigFile, ConfigList, Verbosity, Addr, Port]) ->
+ init(ConfigFile, ConfigList, Verbosity, Addr, Port);
+init(BadArg) ->
+ {error, {badarg, BadArg}}.
+
+init(ConfigFile, ConfigList, Verbosity, Addr, Port) ->
+ Flags = {one_for_one, 0, 1},
+ AccSupVerbosity = get_acc_sup_verbosity(Verbosity),
+ MiscSupVerbosity = get_misc_sup_verbosity(Verbosity),
+ Sups = [sup_spec(httpd_acceptor_sup, Addr, Port, AccSupVerbosity),
+ sup_spec(httpd_misc_sup, Addr, Port, MiscSupVerbosity),
+ worker_spec(httpd_manager, Addr, Port, ConfigFile, ConfigList,
+ Verbosity, [gen_server])],
+ {ok, {Flags, Sups}}.
+
+
+sup_spec(Name, Addr, Port, Verbosity) ->
+ {{Name, Addr, Port},
+ {Name, start, [Addr, Port, Verbosity]},
+ permanent, 2000, supervisor, [Name, supervisor]}.
+
+worker_spec(Name, Addr, Port, ConfigFile, ConfigList, Verbosity, Modules) ->
+ {{Name, Addr, Port},
+ {Name, start_link, [ConfigFile, ConfigList, Verbosity]},
+ permanent, 2000, worker, [Name] ++ Modules}.
+
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd_sup",Addr,Port).
+
+
+%% get_addr_and_port
+
+get_addr_and_port(ConfigFile) ->
+ case httpd_conf:load(ConfigFile) of
+ {ok, ConfigList} ->
+ {ok, Addr, Port} = get_addr_and_port2(ConfigList),
+ {ok, ConfigList, Addr, Port};
+ Error ->
+ Error
+ end.
+
+
+get_addr_and_port2(ConfigList) ->
+ Port = httpd_util:key1search(ConfigList, port, 80),
+ Addr = httpd_util:key1search(ConfigList, bind_address),
+ {ok, Addr, Port}.
+
+get_acc_sup_verbosity(V) ->
+ case key1search(V, all) of
+ undefined ->
+ key1search(V, acceptor_sup_verbosity, ?default_verbosity);
+ Verbosity ->
+ Verbosity
+ end.
+
+
+get_misc_sup_verbosity(V) ->
+ case key1search(V, all) of
+ undefined ->
+ key1search(V, misc_sup_verbosity, ?default_verbosity);
+ Verbosity ->
+ Verbosity
+ end.
+
+
+key1search(L, K) ->
+ httpd_util:key1search(L, K).
+
+key1search(L, K, D) ->
+ httpd_util:key1search(L, K, D).
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_util.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_util.erl
new file mode 100644
index 0000000000..05064a8d38
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_util.erl
@@ -0,0 +1,777 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_util.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_util).
+-export([key1search/2, key1search/3, lookup/2, lookup/3, multi_lookup/2,
+ lookup_mime/2, lookup_mime/3, lookup_mime_default/2,
+ lookup_mime_default/3, reason_phrase/1, message/3, rfc1123_date/0,
+ rfc1123_date/1, day/1, month/1, decode_hex/1, decode_base64/1, encode_base64/1,
+ flatlength/1, split_path/1, split_script_path/1, suffix/1, to_upper/1,
+ to_lower/1, split/3, header/2, header/3, header/4, uniq/1,
+ make_name/2,make_name/3,make_name/4,strip/1,
+ hexlist_to_integer/1,integer_to_hexlist/1,
+ convert_request_date/1,create_etag/1,create_etag/2,getSize/1,
+ response_generated/1]).
+
+%%Since hexlist_to_integer is a lousy name make a name convert
+-export([encode_hex/1]).
+-include("httpd.hrl").
+
+%% key1search
+
+key1search(TupleList,Key) ->
+ key1search(TupleList,Key,undefined).
+
+key1search(TupleList,Key,Undefined) ->
+ case lists:keysearch(Key,1,TupleList) of
+ {value,{Key,Value}} ->
+ Value;
+ false ->
+ Undefined
+ end.
+
+%% lookup
+
+lookup(Table,Key) ->
+ lookup(Table,Key,undefined).
+
+lookup(Table,Key,Undefined) ->
+ case catch ets:lookup(Table,Key) of
+ [{Key,Value}|_] ->
+ Value;
+ _->
+ Undefined
+ end.
+
+%% multi_lookup
+
+multi_lookup(Table,Key) ->
+ remove_key(ets:lookup(Table,Key)).
+
+remove_key([]) ->
+ [];
+remove_key([{_Key,Value}|Rest]) ->
+ [Value|remove_key(Rest)].
+
+%% lookup_mime
+
+lookup_mime(ConfigDB,Suffix) ->
+ lookup_mime(ConfigDB,Suffix,undefined).
+
+lookup_mime(ConfigDB,Suffix,Undefined) ->
+ [{mime_types,MimeTypesDB}|_]=ets:lookup(ConfigDB,mime_types),
+ case ets:lookup(MimeTypesDB,Suffix) of
+ [] ->
+ Undefined;
+ [{Suffix,MimeType}|_] ->
+ MimeType
+ end.
+
+%% lookup_mime_default
+
+lookup_mime_default(ConfigDB,Suffix) ->
+ lookup_mime_default(ConfigDB,Suffix,undefined).
+
+lookup_mime_default(ConfigDB,Suffix,Undefined) ->
+ [{mime_types,MimeTypesDB}|_]=ets:lookup(ConfigDB,mime_types),
+ case ets:lookup(MimeTypesDB,Suffix) of
+ [] ->
+ case ets:lookup(ConfigDB,default_type) of
+ [] ->
+ Undefined;
+ [{default_type,DefaultType}|_] ->
+ DefaultType
+ end;
+ [{Suffix,MimeType}|_] ->
+ MimeType
+ end.
+
+%% reason_phrase
+reason_phrase(100) -> "Continue";
+reason_phrase(101) -> "Swithing protocol";
+reason_phrase(200) -> "OK";
+reason_phrase(201) -> "Created";
+reason_phrase(202) -> "Accepted";
+reason_phrase(204) -> "No Content";
+reason_phrase(205) -> "Reset Content";
+reason_phrase(206) -> "Partial Content";
+reason_phrase(301) -> "Moved Permanently";
+reason_phrase(302) -> "Moved Temporarily";
+reason_phrase(304) -> "Not Modified";
+reason_phrase(400) -> "Bad Request";
+reason_phrase(401) -> "Unauthorized";
+reason_phrase(402) -> "Payment Required";
+reason_phrase(403) -> "Forbidden";
+reason_phrase(404) -> "Not Found";
+reason_phrase(405) -> "Method Not Allowed";
+reason_phrase(408) -> "Request Timeout";
+reason_phrase(411) -> "Length Required";
+reason_phrase(414) -> "Request-URI Too Long";
+reason_phrase(412) -> "Precondition Failed";
+reason_phrase(416) -> "request Range Not Satisfiable";
+reason_phrase(417) -> "Expectation failed";
+reason_phrase(500) -> "Internal Server Error";
+reason_phrase(501) -> "Not Implemented";
+reason_phrase(502) -> "Bad Gateway";
+reason_phrase(503) -> "Service Unavailable";
+reason_phrase(_) -> "Internal Server Error".
+
+%% message
+
+message(301,URL,_) ->
+ "The document has moved <A HREF=\""++URL++"\">here</A>.";
+message(304,_URL,_) ->
+ "The document has not been changed.";
+message(400,none,_) ->
+ "Your browser sent a query that this server could not understand.";
+message(401,none,_) ->
+ "This server could not verify that you
+are authorized to access the document you
+requested. Either you supplied the wrong
+credentials (e.g., bad password), or your
+browser does not understand how to supply
+the credentials required.";
+message(403,RequestURI,_) ->
+ "You do not have permission to access "++RequestURI++" on this server.";
+message(404,RequestURI,_) ->
+ "The requested URL "++RequestURI++" was not found on this server.";
+message(412,none,_) ->
+ "The requested preconditions where false";
+message(414,ReasonPhrase,_) ->
+ "Message "++ReasonPhrase++".";
+message(416,ReasonPhrase,_) ->
+ ReasonPhrase;
+
+message(500,none,ConfigDB) ->
+ ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"),
+ "The server encountered an internal error or
+misconfiguration and was unable to complete
+your request.
+<P>Please contact the server administrator "++ServerAdmin++",
+and inform them of the time the error occurred
+and anything you might have done that may have
+caused the error.";
+message(501,{Method,RequestURI,HTTPVersion},_ConfigDB) ->
+ Method++" to "++RequestURI++" ("++HTTPVersion++") not supported.";
+message(503,String,_ConfigDB) ->
+ "This service in unavailable due to: "++String.
+
+%%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}}
+
+convert_request_date([D,A,Y,DateType|Rest]) ->
+ Func=case DateType of
+ $\, ->
+ fun convert_rfc1123_date/1;
+ $\ ->
+ fun convert_ascii_date/1;
+ _ ->
+ fun convert_rfc850_date/1
+ end,
+ case catch Func([D,A,Y,DateType|Rest])of
+ {ok,Date} ->
+ Date;
+ _Error ->
+ bad_date
+ end.
+
+convert_rfc850_date(DateStr) ->
+ case string:tokens(DateStr," ") of
+ [_WeekDay,Date,Time,_TimeZone|_Rest] ->
+ convert_rfc850_date(Date,Time);
+ _Error ->
+ bad_date
+ end.
+
+convert_rfc850_date([D1,D2,_,M,O,N,_,Y1,Y2|_Rest],[H1,H2,_Col,M1,M2,_Col,S1,S2|_Rest2])->
+ Year=list_to_integer([50,48,Y1,Y2]),
+ Day=list_to_integer([D1,D2]),
+ Month=convert_month([M,O,N]),
+ Hour=list_to_integer([H1,H2]),
+ Min=list_to_integer([M1,M2]),
+ Sec=list_to_integer([S1,S2]),
+ {ok,{{Year,Month,Day},{Hour,Min,Sec}}};
+convert_rfc850_date(_BadDate,_BadTime)->
+ bad_date.
+
+convert_ascii_date([_D,_A,_Y,_SP,M,O,N,_SP,D1,D2,_SP,H1,H2,_Col,M1,M2,_Col,S1,S2,_SP,Y1,Y2,Y3,Y4|_Rest])->
+ Year=list_to_integer([Y1,Y2,Y3,Y4]),
+ Day=case D1 of
+ $\ ->
+ list_to_integer([D2]);
+ _->
+ list_to_integer([D1,D2])
+ end,
+ Month=convert_month([M,O,N]),
+ Hour=list_to_integer([H1,H2]),
+ Min=list_to_integer([M1,M2]),
+ Sec=list_to_integer([S1,S2]),
+ {ok,{{Year,Month,Day},{Hour,Min,Sec}}};
+convert_ascii_date(BadDate)->
+ bad_date.
+convert_rfc1123_date([_D,_A,_Y,_C,_SP,D1,D2,_SP,M,O,N,_SP,Y1,Y2,Y3,Y4,_SP,H1,H2,_Col,M1,M2,_Col,S1,S2|Rest])->
+ Year=list_to_integer([Y1,Y2,Y3,Y4]),
+ Day=list_to_integer([D1,D2]),
+ Month=convert_month([M,O,N]),
+ Hour=list_to_integer([H1,H2]),
+ Min=list_to_integer([M1,M2]),
+ Sec=list_to_integer([S1,S2]),
+ {ok,{{Year,Month,Day},{Hour,Min,Sec}}};
+convert_rfc1123_date(BadDate)->
+ bad_date.
+
+convert_month("Jan")->1;
+convert_month("Feb") ->2;
+convert_month("Mar") ->3;
+convert_month("Apr") ->4;
+convert_month("May") ->5;
+convert_month("Jun") ->6;
+convert_month("Jul") ->7;
+convert_month("Aug") ->8;
+convert_month("Sep") ->9;
+convert_month("Oct") ->10;
+convert_month("Nov") ->11;
+convert_month("Dec") ->12.
+
+
+%% rfc1123_date
+
+rfc1123_date() ->
+ {{YYYY,MM,DD},{Hour,Min,Sec}}=calendar:universal_time(),
+ DayNumber=calendar:day_of_the_week({YYYY,MM,DD}),
+ lists:flatten(io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
+ [day(DayNumber),DD,month(MM),YYYY,Hour,Min,Sec])).
+
+rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) ->
+ DayNumber=calendar:day_of_the_week({YYYY,MM,DD}),
+ lists:flatten(io_lib:format("~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
+ [day(DayNumber),DD,month(MM),YYYY,Hour,Min,Sec])).
+
+%% uniq
+
+uniq([]) ->
+ [];
+uniq([First,First|Rest]) ->
+ uniq([First|Rest]);
+uniq([First|Rest]) ->
+ [First|uniq(Rest)].
+
+
+%% day
+
+day(1) -> "Mon";
+day(2) -> "Tue";
+day(3) -> "Wed";
+day(4) -> "Thu";
+day(5) -> "Fri";
+day(6) -> "Sat";
+day(7) -> "Sun".
+
+%% month
+
+month(1) -> "Jan";
+month(2) -> "Feb";
+month(3) -> "Mar";
+month(4) -> "Apr";
+month(5) -> "May";
+month(6) -> "Jun";
+month(7) -> "Jul";
+month(8) -> "Aug";
+month(9) -> "Sep";
+month(10) -> "Oct";
+month(11) -> "Nov";
+month(12) -> "Dec".
+
+%% decode_hex
+
+decode_hex([$%,Hex1,Hex2|Rest]) ->
+ [hex2dec(Hex1)*16+hex2dec(Hex2)|decode_hex(Rest)];
+decode_hex([First|Rest]) ->
+ [First|decode_hex(Rest)];
+decode_hex([]) ->
+ [].
+
+hex2dec(X) when X>=$0,X=<$9 -> X-$0;
+hex2dec(X) when X>=$A,X=<$F -> X-$A+10;
+hex2dec(X) when X>=$a,X=<$f -> X-$a+10.
+
+%% decode_base64 (DEBUG STRING: QWxhZGRpbjpvcGVuIHNlc2FtZQ==)
+
+decode_base64([]) ->
+ [];
+decode_base64([Sextet1,Sextet2,$=,$=|Rest]) ->
+ Bits2x6=
+ (d(Sextet1) bsl 18) bor
+ (d(Sextet2) bsl 12),
+ Octet1=Bits2x6 bsr 16,
+ [Octet1|decode_base64(Rest)];
+decode_base64([Sextet1,Sextet2,Sextet3,$=|Rest]) ->
+ Bits3x6=
+ (d(Sextet1) bsl 18) bor
+ (d(Sextet2) bsl 12) bor
+ (d(Sextet3) bsl 6),
+ Octet1=Bits3x6 bsr 16,
+ Octet2=(Bits3x6 bsr 8) band 16#ff,
+ [Octet1,Octet2|decode_base64(Rest)];
+decode_base64([Sextet1,Sextet2,Sextet3,Sextet4|Rest]) ->
+ Bits4x6=
+ (d(Sextet1) bsl 18) bor
+ (d(Sextet2) bsl 12) bor
+ (d(Sextet3) bsl 6) bor
+ d(Sextet4),
+ Octet1=Bits4x6 bsr 16,
+ Octet2=(Bits4x6 bsr 8) band 16#ff,
+ Octet3=Bits4x6 band 16#ff,
+ [Octet1,Octet2,Octet3|decode_base64(Rest)];
+decode_base64(CatchAll) ->
+ "BAD!".
+
+d(X) when X >= $A, X =<$Z ->
+ X-65;
+d(X) when X >= $a, X =<$z ->
+ X-71;
+d(X) when X >= $0, X =<$9 ->
+ X+4;
+d($+) -> 62;
+d($/) -> 63;
+d(_) -> 63.
+
+
+encode_base64([]) ->
+ [];
+encode_base64([A]) ->
+ [e(A bsr 2), e((A band 3) bsl 4), $=, $=];
+encode_base64([A,B]) ->
+ [e(A bsr 2), e(((A band 3) bsl 4) bor (B bsr 4)), e((B band 15) bsl 2), $=];
+encode_base64([A,B,C|Ls]) ->
+ encode_base64_do(A,B,C, Ls).
+encode_base64_do(A,B,C, Rest) ->
+ BB = (A bsl 16) bor (B bsl 8) bor C,
+ [e(BB bsr 18), e((BB bsr 12) band 63),
+ e((BB bsr 6) band 63), e(BB band 63)|encode_base64(Rest)].
+
+e(X) when X >= 0, X < 26 -> X+65;
+e(X) when X>25, X<52 -> X+71;
+e(X) when X>51, X<62 -> X-4;
+e(62) -> $+;
+e(63) -> $/;
+e(X) -> exit({bad_encode_base64_token, X}).
+
+
+%% flatlength
+
+flatlength(List) ->
+ flatlength(List, 0).
+
+flatlength([H|T],L) when list(H) ->
+ flatlength(H,flatlength(T,L));
+flatlength([H|T],L) when binary(H) ->
+ flatlength(T,L+size(H));
+flatlength([H|T],L) ->
+ flatlength(T,L+1);
+flatlength([],L) ->
+ L.
+
+%% split_path
+
+split_path(Path) ->
+ case regexp:match(Path,"[\?].*\$") of
+ %% A QUERY_STRING exists!
+ {match,Start,Length} ->
+ {httpd_util:decode_hex(string:substr(Path,1,Start-1)),
+ string:substr(Path,Start,Length)};
+ %% A possible PATH_INFO exists!
+ nomatch ->
+ split_path(Path,[])
+ end.
+
+split_path([],SoFar) ->
+ {httpd_util:decode_hex(lists:reverse(SoFar)),[]};
+split_path([$/|Rest],SoFar) ->
+ Path=httpd_util:decode_hex(lists:reverse(SoFar)),
+ case file:read_file_info(Path) of
+ {ok,FileInfo} when FileInfo#file_info.type == regular ->
+ {Path,[$/|Rest]};
+ {ok,FileInfo} ->
+ split_path(Rest,[$/|SoFar]);
+ {error,Reason} ->
+ split_path(Rest,[$/|SoFar])
+ end;
+split_path([C|Rest],SoFar) ->
+ split_path(Rest,[C|SoFar]).
+
+%% split_script_path
+
+split_script_path(Path) ->
+ case split_script_path(Path, []) of
+ {Script, AfterPath} ->
+ {PathInfo, QueryString} = pathinfo_querystring(AfterPath),
+ {Script, {PathInfo, QueryString}};
+ not_a_script ->
+ not_a_script
+ end.
+
+pathinfo_querystring(Str) ->
+ pathinfo_querystring(Str, []).
+pathinfo_querystring([], SoFar) ->
+ {lists:reverse(SoFar), []};
+pathinfo_querystring([$?|Rest], SoFar) ->
+ {lists:reverse(SoFar), Rest};
+pathinfo_querystring([C|Rest], SoFar) ->
+ pathinfo_querystring(Rest, [C|SoFar]).
+
+split_script_path([$?|QueryString], SoFar) ->
+ Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ case file:read_file_info(Path) of
+ {ok,FileInfo} when FileInfo#file_info.type == regular ->
+ {Path, [$?|QueryString]};
+ {ok,FileInfo} ->
+ not_a_script;
+ {error,Reason} ->
+ not_a_script
+ end;
+split_script_path([], SoFar) ->
+ Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ case file:read_file_info(Path) of
+ {ok,FileInfo} when FileInfo#file_info.type == regular ->
+ {Path, []};
+ {ok,FileInfo} ->
+ not_a_script;
+ {error,Reason} ->
+ not_a_script
+ end;
+split_script_path([$/|Rest], SoFar) ->
+ Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ case file:read_file_info(Path) of
+ {ok, FileInfo} when FileInfo#file_info.type == regular ->
+ {Path, [$/|Rest]};
+ {ok, _FileInfo} ->
+ split_script_path(Rest, [$/|SoFar]);
+ {error, _Reason} ->
+ split_script_path(Rest, [$/|SoFar])
+ end;
+split_script_path([C|Rest], SoFar) ->
+ split_script_path(Rest,[C|SoFar]).
+
+%% suffix
+
+suffix(Path) ->
+ case filename:extension(Path) of
+ [] ->
+ [];
+ Extension ->
+ tl(Extension)
+ end.
+
+%% to_upper
+
+to_upper([C|Cs]) when C >= $a, C =< $z ->
+ [C-($a-$A)|to_upper(Cs)];
+to_upper([C|Cs]) ->
+ [C|to_upper(Cs)];
+to_upper([]) ->
+ [].
+
+%% to_lower
+
+to_lower([C|Cs]) when C >= $A, C =< $Z ->
+ [C+($a-$A)|to_lower(Cs)];
+to_lower([C|Cs]) ->
+ [C|to_lower(Cs)];
+to_lower([]) ->
+ [].
+
+
+%% strip
+strip(Value)->
+ lists:reverse(remove_ws(lists:reverse(remove_ws(Value)))).
+
+remove_ws([$\s|Rest])->
+ remove_ws(Rest);
+remove_ws([$\t|Rest]) ->
+ remove_ws(Rest);
+remove_ws(Rest) ->
+ Rest.
+
+%% split
+
+split(String,RegExp,Limit) ->
+ case regexp:parse(RegExp) of
+ {error,Reason} ->
+ {error,Reason};
+ {ok,_} ->
+ {ok,do_split(String,RegExp,Limit)}
+ end.
+
+do_split(String,RegExp,1) ->
+ [String];
+
+do_split(String,RegExp,Limit) ->
+ case regexp:first_match(String,RegExp) of
+ {match,Start,Length} ->
+ [string:substr(String,1,Start-1)|
+ do_split(lists:nthtail(Start+Length-1,String),RegExp,Limit-1)];
+ nomatch ->
+ [String]
+ end.
+
+%% header
+header(StatusCode,Date)when list(Date)->
+ header(StatusCode,"text/plain",false);
+
+header(StatusCode, PersistentConnection) when integer(StatusCode)->
+ Date = rfc1123_date(),
+ Connection =
+ case PersistentConnection of
+ true ->
+ "";
+ _ ->
+ "Connection: close \r\n"
+ end,
+ io_lib:format("HTTP/1.1 ~w ~s \r\nDate: ~s\r\nServer: ~s\r\n~s",
+ [StatusCode, httpd_util:reason_phrase(StatusCode),
+ Date, ?SERVER_SOFTWARE, Connection]).
+
+%%----------------------------------------------------------------------
+
+header(StatusCode, MimeType, Date) when list(Date) ->
+ header(StatusCode, MimeType, false,rfc1123_date());
+
+
+header(StatusCode, MimeType, PersistentConnection) when integer(StatusCode) ->
+ header(StatusCode, MimeType, PersistentConnection,rfc1123_date()).
+
+
+%%----------------------------------------------------------------------
+
+header(416, MimeType,PersistentConnection,Date)->
+ Connection =
+ case PersistentConnection of
+ true ->
+ "";
+ _ ->
+ "Connection: close \r\n"
+ end,
+ io_lib:format("HTTP/1.1 ~w ~s \r\nDate: ~s\r\nServer: ~s\r\n"
+ "Content-Range:bytes *"
+ "Content-Type: ~s\r\n~s",
+ [416, httpd_util:reason_phrase(416),
+ Date, ?SERVER_SOFTWARE, MimeType, Connection]);
+
+
+header(StatusCode, MimeType,PersistentConnection,Date) when integer(StatusCode)->
+ Connection =
+ case PersistentConnection of
+ true ->
+ "";
+ _ ->
+ "Connection: close \r\n"
+ end,
+ io_lib:format("HTTP/1.1 ~w ~s \r\nDate: ~s\r\nServer: ~s\r\n"
+ "Content-Type: ~s\r\n~s",
+ [StatusCode, httpd_util:reason_phrase(StatusCode),
+ Date, ?SERVER_SOFTWARE, MimeType, Connection]).
+
+
+
+%% make_name/2, make_name/3
+%% Prefix -> string()
+%% First part of the name, e.g. "httpd"
+%% Addr -> {A,B,C,D} | string() | undefined
+%% The address part of the name.
+%% e.g. "123.234.55.66" or {123,234,55,66} or "otp.ericsson.se"
+%% for a host address or undefined if local host.
+%% Port -> integer()
+%% Last part of the name, such as the HTTPD server port
+%% number (80).
+%% Postfix -> Any string that will be added last to the name
+%%
+%% Example:
+%% make_name("httpd","otp.ericsson.se",80) => httpd__otp_ericsson_se__80
+%% make_name("httpd",undefined,8088) => httpd_8088
+
+make_name(Prefix,Port) ->
+ make_name(Prefix,undefined,Port,"").
+
+make_name(Prefix,Addr,Port) ->
+ make_name(Prefix,Addr,Port,"").
+
+make_name(Prefix,"*",Port,Postfix) ->
+ make_name(Prefix,undefined,Port,Postfix);
+
+make_name(Prefix,any,Port,Postfix) ->
+ make_name1(io_lib:format("~s_~w~s",[Prefix,Port,Postfix]));
+
+make_name(Prefix,undefined,Port,Postfix) ->
+ make_name1(io_lib:format("~s_~w~s",[Prefix,Port,Postfix]));
+
+make_name(Prefix,Addr,Port,Postfix) ->
+ NameString =
+ Prefix ++ "__" ++ make_name2(Addr) ++ "__" ++
+ integer_to_list(Port) ++ Postfix,
+ make_name1(NameString).
+
+make_name1(String) ->
+ list_to_atom(lists:flatten(String)).
+
+make_name2({A,B,C,D}) ->
+ io_lib:format("~w_~w_~w_~w",[A,B,C,D]);
+make_name2(Addr) ->
+ search_and_replace(Addr,$.,$_).
+
+search_and_replace(S,A,B) ->
+ Fun = fun(What) ->
+ case What of
+ A -> B;
+ O -> O
+ end
+ end,
+ lists:map(Fun,S).
+
+
+
+%%----------------------------------------------------------------------
+%% Converts a string that constists of 0-9,A-F,a-f to a
+%% integer
+%%----------------------------------------------------------------------
+
+hexlist_to_integer([])->
+ empty;
+
+
+%%When the string only contains one value its eaasy done.
+%% 0-9
+hexlist_to_integer([Size]) when Size>=48 , Size=<57 ->
+ Size-48;
+%% A-F
+hexlist_to_integer([Size]) when Size>=65 , Size=<70 ->
+ Size-55;
+%% a-f
+hexlist_to_integer([Size]) when Size>=97 , Size=<102 ->
+ Size-87;
+hexlist_to_integer([Size]) ->
+ not_a_num;
+
+hexlist_to_integer(Size) ->
+ Len=string:span(Size,"1234567890abcdefABCDEF"),
+ hexlist_to_integer2(Size,16 bsl (4 *(Len-2)),0).
+
+hexlist_to_integer2([],_Pos,Sum)->
+ Sum;
+hexlist_to_integer2([HexVal|HexString],Pos,Sum)when HexVal>=48,HexVal=<57->
+ hexlist_to_integer2(HexString,Pos bsr 4,Sum+((HexVal-48)*Pos));
+
+hexlist_to_integer2([HexVal|HexString],Pos,Sum)when HexVal>=65,HexVal=<70->
+ hexlist_to_integer2(HexString,Pos bsr 4,Sum+((HexVal-55)*Pos));
+
+hexlist_to_integer2([HexVal|HexString],Pos,Sum)when HexVal>=97,HexVal=<102->
+ hexlist_to_integer2(HexString,Pos bsr 4,Sum+((HexVal-87)*Pos));
+
+hexlist_to_integer2(_AfterHexString,_Pos,Sum)->
+ Sum.
+
+%%----------------------------------------------------------------------
+%%Converts an integer to an hexlist
+%%----------------------------------------------------------------------
+encode_hex(Num)->
+ integer_to_hexlist(Num).
+
+
+integer_to_hexlist(Num)->
+ integer_to_hexlist(Num,getSize(Num),[]).
+
+integer_to_hexlist(Num,Pot,Res) when Pot<0 ->
+ convert_to_ascii([Num|Res]);
+
+integer_to_hexlist(Num,Pot,Res) ->
+ Position=(16 bsl (Pot*4)),
+ PosVal=Num div Position,
+ integer_to_hexlist(Num-(PosVal*Position),Pot-1,[PosVal|Res]).
+convert_to_ascii(RevesedNum)->
+ convert_to_ascii(RevesedNum,[]).
+
+convert_to_ascii([],Num)->
+ Num;
+convert_to_ascii([Num|Reversed],Number)when Num>-1, Num<10 ->
+ convert_to_ascii(Reversed,[Num+48|Number]);
+convert_to_ascii([Num|Reversed],Number)when Num>9, Num<16 ->
+ convert_to_ascii(Reversed,[Num+55|Number]);
+convert_to_ascii(NumReversed,Number) ->
+ error.
+
+
+
+getSize(Num)->
+ getSize(Num,0).
+
+getSize(Num,Pot)when Num<(16 bsl(Pot *4)) ->
+ Pot-1;
+
+getSize(Num,Pot) ->
+ getSize(Num,Pot+1).
+
+
+
+
+
+create_etag(FileInfo)->
+ create_etag(FileInfo#file_info.mtime,FileInfo#file_info.size).
+
+create_etag({{Year,Month,Day},{Hour,Min,Sec}},Size)->
+ create_part([Year,Month,Day,Hour,Min,Sec])++io_lib:write(Size);
+
+create_etag(FileInfo,Size)->
+ create_etag(FileInfo#file_info.mtime,Size).
+
+create_part(Values)->
+ lists:map(fun(Val0)->
+ Val=Val0 rem 60,
+ if
+ Val=<25 ->
+ 65+Val; % A-Z
+ Val=<50 ->
+ 72+Val; % a-z
+ %%Since no date s
+ true ->
+ Val-3
+ end
+ end,Values).
+
+
+
+%%----------------------------------------------------------------------
+%%Function that controls whether a response is generated or not
+%%----------------------------------------------------------------------
+response_generated(Info)->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason}->
+ true;
+ %%No status code control repsonsxe
+ undefined ->
+ case httpd_util:key1search(Info#mod.data, response) of
+ %% No response has been generated!
+ undefined ->
+ false;
+ %% A response has been generated or sent!
+ Response ->
+ true
+ end
+ end.
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.erl
new file mode 100644
index 0000000000..c772a11dd1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.erl
@@ -0,0 +1,94 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_verbosity.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(httpd_verbosity).
+
+-include_lib("stdlib/include/erl_compile.hrl").
+
+-export([print/4,print/5,printc/4,validate/1]).
+
+print(silence,_Severity,_Format,_Arguments) ->
+ ok;
+print(Verbosity,Severity,Format,Arguments) ->
+ print1(printable(Verbosity,Severity),Format,Arguments).
+
+
+print(silence,_Severity,_Module,_Format,_Arguments) ->
+ ok;
+print(Verbosity,Severity,Module,Format,Arguments) ->
+ print1(printable(Verbosity,Severity),Module,Format,Arguments).
+
+
+printc(silence,Severity,Format,Arguments) ->
+ ok;
+printc(Verbosity,Severity,Format,Arguments) ->
+ print2(printable(Verbosity,Severity),Format,Arguments).
+
+
+print1(false,_Format,_Arguments) -> ok;
+print1(Verbosity,Format,Arguments) ->
+ V = image_of_verbosity(Verbosity),
+ S = image_of_sname(get(sname)),
+ io:format("** HTTPD ~s ~s: " ++ Format ++ "~n",[S,V]++Arguments).
+
+print1(false,_Module,_Format,_Arguments) -> ok;
+print1(Verbosity,Module,Format,Arguments) ->
+ V = image_of_verbosity(Verbosity),
+ S = image_of_sname(get(sname)),
+ io:format("** HTTPD ~s ~s ~s: " ++ Format ++ "~n",[S,Module,V]++Arguments).
+
+
+print2(false,_Format,_Arguments) -> ok;
+print2(_Verbosity,Format,Arguments) ->
+ io:format(Format ++ "~n",Arguments).
+
+
+%% printable(Verbosity,Severity)
+printable(info,info) -> info;
+printable(log,info) -> info;
+printable(log,log) -> log;
+printable(debug,info) -> info;
+printable(debug,log) -> log;
+printable(debug,debug) -> debug;
+printable(trace,V) -> V;
+printable(_Verb,_Sev) -> false.
+
+
+image_of_verbosity(info) -> "INFO";
+image_of_verbosity(log) -> "LOG";
+image_of_verbosity(debug) -> "DEBUG";
+image_of_verbosity(trace) -> "TRACE";
+image_of_verbosity(_) -> "".
+
+%% ShortName
+image_of_sname(acc) -> "ACCEPTOR";
+image_of_sname(acc_sup) -> "ACCEPTOR_SUP";
+image_of_sname(auth) -> "AUTH";
+image_of_sname(man) -> "MANAGER";
+image_of_sname(misc_sup) -> "MISC_SUP";
+image_of_sname(sec) -> "SECURITY";
+image_of_sname(P) when pid(P) -> io_lib:format("REQUEST_HANDLER(~p)",[P]);
+image_of_sname(undefined) -> "";
+image_of_sname(V) -> io_lib:format("~p",[V]).
+
+
+validate(info) -> info;
+validate(log) -> log;
+validate(debug) -> debug;
+validate(trace) -> trace;
+validate(_) -> silence.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.hrl
new file mode 100644
index 0000000000..caafd8ef18
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/httpd_verbosity.hrl
@@ -0,0 +1,65 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: httpd_verbosity.hrl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+
+-ifndef(dont_use_verbosity).
+
+-ifndef(default_verbosity).
+-define(default_verbosity,silence).
+-endif.
+
+-define(vvalidate(V), httpd_verbosity:validate(V)).
+
+-ifdef(VMODULE).
+
+-define(vinfo(F,A), httpd_verbosity:print(get(verbosity),info, ?VMODULE,F,A)).
+-define(vlog(F,A), httpd_verbosity:print(get(verbosity),log, ?VMODULE,F,A)).
+-define(vdebug(F,A),httpd_verbosity:print(get(verbosity),debug,?VMODULE,F,A)).
+-define(vtrace(F,A),httpd_verbosity:print(get(verbosity),trace,?VMODULE,F,A)).
+
+-else.
+
+-define(vinfo(F,A), httpd_verbosity:print(get(verbosity),info, F,A)).
+-define(vlog(F,A), httpd_verbosity:print(get(verbosity),log, F,A)).
+-define(vdebug(F,A),httpd_verbosity:print(get(verbosity),debug,F,A)).
+-define(vtrace(F,A),httpd_verbosity:print(get(verbosity),trace,F,A)).
+
+-endif.
+
+-define(vinfoc(F,A), httpd_verbosity:printc(get(verbosity),info, F,A)).
+-define(vlogc(F,A), httpd_verbosity:printc(get(verbosity),log, F,A)).
+-define(vdebugc(F,A),httpd_verbosity:printc(get(verbosity),debug,F,A)).
+-define(vtracec(F,A),httpd_verbosity:printc(get(verbosity),trace,F,A)).
+
+-else.
+
+-define(vvalidate(V),ok).
+
+-define(vinfo(F,A),ok).
+-define(vlog(F,A),ok).
+-define(vdebug(F,A),ok).
+-define(vtrace(F,A),ok).
+
+-define(vinfoc(F,A),ok).
+-define(vlogc(F,A),ok).
+-define(vdebugc(F,A),ok).
+-define(vtracec(F,A),ok).
+
+-endif.
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.app.src b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.app.src
new file mode 100644
index 0000000000..1bf5fcc56e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.app.src
@@ -0,0 +1,56 @@
+{application,inets,
+ [{description,"INETS CXC 138 49"},
+ {vsn,"%VSN%"},
+ {modules,[
+ %% FTP
+ ftp,
+
+ %% HTTP client:
+ http,
+ http_lib,
+ httpc_handler,
+ httpc_manager,
+ uri,
+
+ %% HTTP server:
+ httpd,
+ httpd_acceptor,
+ httpd_acceptor_sup,
+ httpd_conf,
+ httpd_example,
+ httpd_manager,
+ httpd_misc_sup,
+ httpd_parse,
+ httpd_request_handler,
+ httpd_response,
+ httpd_socket,
+ httpd_sup,
+ httpd_util,
+ httpd_verbosity,
+ inets_sup,
+ mod_actions,
+ mod_alias,
+ mod_auth,
+ mod_auth_dets,
+ mod_auth_mnesia,
+ mod_auth_plain,
+ mod_auth_server,
+ mod_browser,
+ mod_cgi,
+ mod_dir,
+ mod_disk_log,
+ mod_esi,
+ mod_get,
+ mod_head,
+ mod_htaccess,
+ mod_include,
+ mod_log,
+ mod_range,
+ mod_responsecontrol,
+ mod_security,
+ mod_security_server,
+ mod_trace
+ ]},
+ {registered,[inets_sup]},
+ {applications,[kernel,stdlib]},
+ {mod,{inets_sup,[]}}]}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.appup.src b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.appup.src
new file mode 100644
index 0000000000..f612dc5b91
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.appup.src
@@ -0,0 +1,135 @@
+{"%VSN%",
+ [{"3.0.5",
+ [
+ {load_module, ftp, soft_purge, soft_purge, []}
+ ]
+ },
+ {"3.0.4",
+ [
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []}
+ ]
+ },
+ {"3.0.3",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, [mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, [httpd_manager]},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [mod_disk_log, httpd_conf, httpd_socket]}]
+ },
+ {"3.0.2",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, [mod_disk_log]},
+ {update, httpd_sup, soft, soft_purge, soft_purge, [httpd_manager]},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [httpd_request_handler, httpd_conf, httpd_socket]},
+ {update, httpd_request_handler, soft, soft_purge, soft_purge,
+ [httpd_response]}]
+ },
+ {"3.0.1",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge,
+ [mod_auth, mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {load_module, mod_auth, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, [httpd_manager]},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [httpd_request_handler, httpd_conf, httpd_socket]},
+ {update, httpd_request_handler, soft, soft_purge, soft_purge,
+ [httpd_response]}]
+ },
+ {"3.0",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge,
+ [mod_auth, mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {load_module, mod_auth, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge,
+ [httpd_manager, httpd_misc_sup]},
+ {update, httpd_misc_sup, soft, soft_purge, soft_purge, []},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [httpd_request_handler, httpd_conf, httpd_socket]},
+ {update, httpd_request_handler, soft, soft_purge, soft_purge,
+ [httpd_response]}]
+ }
+ ],
+ [{"3.0.5",
+ [
+ {load_module, ftp, soft_purge, soft_purge, []}
+ ]
+ },
+ {"3.0.4",
+ [{update, httpd_acceptor, soft, soft_purge, soft_purge, []}]
+ },
+ {"3.0.3",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, [mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, [httpd_manager]},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [mod_disk_log, httpd_conf, httpd_socket]}]
+ },
+ {"3.0.2",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge, [mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, [httpd_manager]},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [httpd_request_handler, httpd_conf, httpd_socket]},
+ {update, httpd_request_handler, soft, soft_purge, soft_purge,
+ [httpd_response]}]
+ },
+ {"3.0.1",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge,
+ [mod_auth, mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {load_module, mod_auth, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge, [httpd_manager]},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [httpd_request_handler, httpd_conf, httpd_socket]},
+ {update, httpd_request_handler, soft, soft_purge, soft_purge,
+ [httpd_response]}]
+ },
+ {"3.0",
+ [{load_module, httpd, soft_purge, soft_purge, [httpd_conf, httpd_sup]},
+ {load_module, httpd_conf, soft_purge, soft_purge, []},
+ {load_module, httpd_socket, soft_purge, soft_purge, []},
+ {load_module, httpd_response, soft_purge, soft_purge,
+ [mod_auth, mod_disk_log]},
+ {load_module, mod_disk_log, soft_purge, soft_purge, []},
+ {load_module, mod_auth, soft_purge, soft_purge, []},
+ {update, httpd_sup, soft, soft_purge, soft_purge,
+ [httpd_manager, httpd_misc_sup]},
+ {update, httpd_misc_sup, soft, soft_purge, soft_purge, []},
+ {update, httpd_acceptor, soft, soft_purge, soft_purge, []},
+ {update, httpd_manager, soft, soft_purge, soft_purge,
+ [httpd_request_handler, httpd_conf, httpd_socket]},
+ {update, httpd_request_handler, soft, soft_purge, soft_purge,
+ [httpd_response]}]
+ }
+ ]
+}.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.config b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.config
new file mode 100644
index 0000000000..adf0e3ecf1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets.config
@@ -0,0 +1,2 @@
+[{inets,[{services,[{httpd,"/var/tmp/server_root/conf/8888.conf"},
+ {httpd,"/var/tmp/server_root/conf/8080.conf"}]}]}].
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets_sup.erl
new file mode 100644
index 0000000000..6bda87148c
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/inets_sup.erl
@@ -0,0 +1,158 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: inets_sup.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(inets_sup).
+
+-export([crock/0]).
+-export([start/2, stop/1, init/1]).
+-export([start_child/2, stop_child/2, which_children/0]).
+
+
+%% crock (Used for debugging!)
+
+crock() ->
+ application:start(sasl),
+ application:start(inets).
+
+
+%% start
+
+start(Type, State) ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+
+%% stop
+
+stop(State) ->
+ ok.
+
+
+%% start_child
+
+start_child(ConfigFile, Verbosity) ->
+ {ok, Spec} = httpd_child_spec(ConfigFile, Verbosity),
+ supervisor:start_child(?MODULE, Spec).
+
+
+%% stop_child
+
+stop_child(Addr, Port) ->
+ Name = {httpd_sup, Addr, Port},
+ case supervisor:terminate_child(?MODULE, Name) of
+ ok ->
+ supervisor:delete_child(?MODULE, Name);
+ Error ->
+ Error
+ end.
+
+
+%% which_children
+
+which_children() ->
+ supervisor:which_children(?MODULE).
+
+
+%% init
+
+init([]) ->
+ case get_services() of
+ {error, Reason} ->
+ {error,Reason};
+ Services ->
+ SupFlags = {one_for_one, 10, 3600},
+ {ok, {SupFlags, child_spec(Services, [])}}
+ end.
+
+get_services() ->
+ case (catch application:get_env(inets, services)) of
+ {ok, Services} ->
+ Services;
+ _ ->
+ []
+ end.
+
+
+child_spec([], Acc) ->
+ Acc;
+child_spec([{httpd, ConfigFile, Verbosity}|Rest], Acc) ->
+ case httpd_child_spec(ConfigFile, Verbosity) of
+ {ok, Spec} ->
+ child_spec(Rest, [Spec | Acc]);
+ {error, Reason} ->
+ error_msg("Failed creating child spec "
+ "using ~p for reason: ~p", [ConfigFile, Reason]),
+ child_spec(Rest, Acc)
+ end;
+child_spec([{httpd, ConfigFile}|Rest], Acc) ->
+ case httpd_child_spec(ConfigFile, []) of
+ {ok, Spec} ->
+ child_spec(Rest, [Spec | Acc]);
+ {error, Reason} ->
+ error_msg("Failed creating child spec "
+ "using ~p for reason: ~p", [ConfigFile, Reason]),
+ child_spec(Rest, Acc)
+ end.
+
+
+httpd_child_spec(ConfigFile, Verbosity) ->
+ case httpd_conf:load(ConfigFile) of
+ {ok, ConfigList} ->
+ Port = httpd_util:key1search(ConfigList, port, 80),
+ Addr = httpd_util:key1search(ConfigList, bind_address),
+ {ok, httpd_child_spec(ConfigFile, Addr, Port, Verbosity)};
+ Error ->
+ Error
+ end.
+
+
+httpd_child_spec(ConfigFile, Addr, Port, Verbosity) ->
+ {{httpd_sup, Addr, Port},{httpd_sup, start_link,[ConfigFile, Verbosity]},
+ permanent, 20000, supervisor,
+ [ftp,
+ httpd,
+ httpd_conf,
+ httpd_example,
+ httpd_manager,
+ httpd_misc_sup,
+ httpd_listener,
+ httpd_parse,
+ httpd_request,
+ httpd_response,
+ httpd_socket,
+ httpd_sup,
+ httpd_util,
+ httpd_verbosity,
+ inets_sup,
+ mod_actions,
+ mod_alias,
+ mod_auth,
+ mod_cgi,
+ mod_dir,
+ mod_disk_log,
+ mod_esi,
+ mod_get,
+ mod_head,
+ mod_include,
+ mod_log,
+ mod_auth_mnesia,
+ mod_auth_plain,
+ mod_auth_dets,
+ mod_security]}.
+
+
+error_msg(F, A) ->
+ error_logger:error_msg(F ++ "~n", A).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/jnets_httpd.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/jnets_httpd.hrl
new file mode 100644
index 0000000000..721a6b991d
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/jnets_httpd.hrl
@@ -0,0 +1,138 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+
+-include_lib("kernel/include/file.hrl").
+
+-define(SOCKET_CHUNK_SIZE,8192).
+-define(SOCKET_MAX_POLL,25).
+-define(FILE_CHUNK_SIZE,64*1024).
+-define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
+-define(DEFAULT_CONTEXT,
+ [{errmsg,"[an error occurred while processing this directive]"},
+ {timefmt,"%A, %d-%b-%y %T %Z"},
+ {sizefmt,"abbrev"}]).
+
+
+-ifdef(inets_debug).
+-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(DEBUG(F,A),[]).
+-endif.
+
+-define(MAXBODYSIZE,16#ffffffff).
+
+-define(HTTP_VERSION_10,0).
+-define(HTTP_VERSION_11,1).
+
+-define(CR,13).
+-define(LF,10).
+
+
+-record(init_data,{peername,resolve}).
+
+
+-record(mod,{
+ init_data, %
+ data= [], % list() Used to propagate data between modules
+ socket_type=ip_comm, % socket_type() IP or SSL socket
+ socket, % socket() Actual socket
+ config_db, % ets() {key,val} db with config entries
+ method, % atom() HTTP method, e.g. 'GET'
+% request_uri, % string() Request URI
+ path, % string() Absolute path. May include query etc
+ http_version, % int() HTTP minor version number, e.g. 0 or 1
+% request_line, % string() Request Line
+ headers, % #req_headers{} Parsed request headers
+ entity_body= <<>>, % binary() Body of request
+ connection, % boolean() true if persistant connection
+ status_code, % int() Status code
+ logging % int() 0=No logging
+ % 1=Only mod_log present
+ % 2=Only mod_disk_log present
+ % 3=Both mod_log and mod_disk_log present
+ }).
+
+% -record(ssl,{
+% certfile, %
+% keyfile, %
+% verify= 0, %
+% ciphers, %
+% password, %
+% depth = 1, %
+% cacertfile, %
+
+% cachetimeout % Found in yaws....
+% }).
+
+
+-record(http_request,{
+ method, % atom() if known else string() HTTP methd
+ path, % {abs_path,string()} URL path
+ version % {int(),int()} {Major,Minor} HTTP version
+ }).
+
+-record(http_response,{
+ version, % {int(),int()} {Major,Minor} HTTP version
+ status, % int() Status code
+ phrase % string() HTTP Reason phrase
+ }).
+
+
+%%% Request headers
+-record(req_headers,{
+%%% --- Standard "General" headers
+% cache_control,
+ connection="keep-alive",
+% date,
+% pragma,
+% trailer,
+ transfer_encoding,
+% upgrade,
+% via,
+% warning,
+%%% --- Standard "Request" headers
+% accept,
+% accept_charset,
+% accept_encoding,
+% accept_language,
+ authorization,
+ expect, %% FIXME! Update inet_drv.c!!
+% from,
+ host,
+ if_match,
+ if_modified_since,
+ if_none_match,
+ if_range,
+ if_unmodified_since,
+% max_forwards,
+% proxy_authorization,
+ range,
+% referer,
+% te, %% FIXME! Update inet_drv.c!!
+ user_agent,
+%%% --- Standard "Entity" headers
+% content_encoding,
+% content_language,
+ content_length="0",
+% content_location,
+% content_md5,
+% content_range,
+ content_type,
+% last_modified,
+ other=[] % (list) Key/Value list with other headers
+ }).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_actions.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_actions.erl
new file mode 100644
index 0000000000..93bdb9fb40
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_actions.erl
@@ -0,0 +1,92 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_actions.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(mod_actions).
+-export([do/1,load/2]).
+
+-include("httpd.hrl").
+
+%% do
+
+do(Info) ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ Path=mod_alias:path(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),
+ Suffix=httpd_util:suffix(Path),
+ MimeType=httpd_util:lookup_mime(Info#mod.config_db,Suffix,
+ "text/plain"),
+ Actions=httpd_util:multi_lookup(Info#mod.config_db,action),
+ case action(Info#mod.request_uri,MimeType,Actions) of
+ {yes,RequestURI} ->
+ {proceed,[{new_request_uri,RequestURI}|Info#mod.data]};
+ no ->
+ Scripts=httpd_util:multi_lookup(Info#mod.config_db,script),
+ case script(Info#mod.request_uri,Info#mod.method,Scripts) of
+ {yes,RequestURI} ->
+ {proceed,[{new_request_uri,RequestURI}|Info#mod.data]};
+ no ->
+ {proceed,Info#mod.data}
+ end
+ end;
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end.
+
+action(RequestURI,MimeType,[]) ->
+ no;
+action(RequestURI,MimeType,[{MimeType,CGIScript}|Rest]) ->
+ {yes,CGIScript++RequestURI};
+action(RequestURI,MimeType,[_|Rest]) ->
+ action(RequestURI,MimeType,Rest).
+
+script(RequestURI,Method,[]) ->
+ no;
+script(RequestURI,Method,[{Method,CGIScript}|Rest]) ->
+ {yes,CGIScript++RequestURI};
+script(RequestURI,Method,[_|Rest]) ->
+ script(RequestURI,Method,Rest).
+
+%%
+%% Configuration
+%%
+
+%% load
+
+load([$A,$c,$t,$i,$o,$n,$ |Action],[]) ->
+ case regexp:split(Action," ") of
+ {ok,[MimeType,CGIScript]} ->
+ {ok,[],{action,{MimeType,CGIScript}}};
+ {ok,_} ->
+ {error,?NICE(httpd_conf:clean(Action)++" is an invalid Action")}
+ end;
+load([$S,$c,$r,$i,$p,$t,$ |Script],[]) ->
+ case regexp:split(Script," ") of
+ {ok,[Method,CGIScript]} ->
+ {ok,[],{script,{Method,CGIScript}}};
+ {ok,_} ->
+ {error,?NICE(httpd_conf:clean(Script)++" is an invalid Script")}
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_alias.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_alias.erl
new file mode 100644
index 0000000000..e01c18b3d6
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_alias.erl
@@ -0,0 +1,175 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_alias.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(mod_alias).
+-export([do/1,real_name/3,real_script_name/3,default_index/2,load/2,path/3]).
+
+-include("httpd.hrl").
+
+%% do
+
+do(Info) ->
+ ?DEBUG("do -> entry",[]),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ do_alias(Info);
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end.
+
+do_alias(Info) ->
+ ?DEBUG("do_alias -> Request URI: ~p",[Info#mod.request_uri]),
+ {ShortPath,Path,AfterPath} =
+ real_name(Info#mod.config_db,Info#mod.request_uri,
+ httpd_util:multi_lookup(Info#mod.config_db,alias)),
+ %% Relocate if a trailing slash is missing else proceed!
+ LastChar = lists:last(ShortPath),
+ case file:read_file_info(ShortPath) of
+ {ok,FileInfo} when FileInfo#file_info.type == directory,LastChar /= $/ ->
+ ?LOG("do_alias -> ~n"
+ " ShortPath: ~p~n"
+ " LastChar: ~p~n"
+ " FileInfo: ~p",
+ [ShortPath,LastChar,FileInfo]),
+ ServerName = httpd_util:lookup(Info#mod.config_db,server_name),
+ Port = port_string(httpd_util:lookup(Info#mod.config_db,port,80)),
+ URL = "http://"++ServerName++Port++Info#mod.request_uri++"/",
+ ReasonPhrase = httpd_util:reason_phrase(301),
+ Message = httpd_util:message(301,URL,Info#mod.config_db),
+ {proceed,
+ [{response,
+ {301, ["Location: ", URL, "\r\n"
+ "Content-Type: text/html\r\n",
+ "\r\n",
+ "<HTML>\n<HEAD>\n<TITLE>",ReasonPhrase,
+ "</TITLE>\n</HEAD>\n"
+ "<BODY>\n<H1>",ReasonPhrase,
+ "</H1>\n", Message,
+ "\n</BODY>\n</HTML>\n"]}}|
+ [{real_name,{Path,AfterPath}}|Info#mod.data]]};
+ NoFile ->
+ {proceed,[{real_name,{Path,AfterPath}}|Info#mod.data]}
+ end.
+
+port_string(80) ->
+ "";
+port_string(Port) ->
+ ":"++integer_to_list(Port).
+
+%% real_name
+
+real_name(ConfigDB, RequestURI,[]) ->
+ DocumentRoot = httpd_util:lookup(ConfigDB, document_root, ""),
+ RealName = DocumentRoot++RequestURI,
+ {ShortPath, _AfterPath} = httpd_util:split_path(RealName),
+ {Path, AfterPath}=httpd_util:split_path(default_index(ConfigDB,RealName)),
+ {ShortPath, Path, AfterPath};
+real_name(ConfigDB, RequestURI, [{FakeName,RealName}|Rest]) ->
+ case regexp:match(RequestURI, "^"++FakeName) of
+ {match, _, _} ->
+ {ok, ActualName, _} = regexp:sub(RequestURI,
+ "^"++FakeName, RealName),
+ {ShortPath, _AfterPath} = httpd_util:split_path(ActualName),
+ {Path, AfterPath} =
+ httpd_util:split_path(default_index(ConfigDB, ActualName)),
+ {ShortPath, Path, AfterPath};
+ nomatch ->
+ real_name(ConfigDB,RequestURI,Rest)
+ end.
+
+%% real_script_name
+
+real_script_name(ConfigDB,RequestURI,[]) ->
+ not_a_script;
+real_script_name(ConfigDB,RequestURI,[{FakeName,RealName}|Rest]) ->
+ case regexp:match(RequestURI,"^"++FakeName) of
+ {match,_,_} ->
+ {ok,ActualName,_}=regexp:sub(RequestURI,"^"++FakeName,RealName),
+ httpd_util:split_script_path(default_index(ConfigDB,ActualName));
+ nomatch ->
+ real_script_name(ConfigDB,RequestURI,Rest)
+ end.
+
+%% default_index
+
+default_index(ConfigDB, Path) ->
+ case file:read_file_info(Path) of
+ {ok, FileInfo} when FileInfo#file_info.type == directory ->
+ DirectoryIndex = httpd_util:lookup(ConfigDB, directory_index, []),
+ append_index(Path, DirectoryIndex);
+ _ ->
+ Path
+ end.
+
+append_index(RealName, []) ->
+ RealName;
+append_index(RealName, [Index|Rest]) ->
+ case file:read_file_info(filename:join(RealName, Index)) of
+ {error,Reason} ->
+ append_index(RealName, Rest);
+ _ ->
+ filename:join(RealName,Index)
+ end.
+
+%% path
+
+path(Data, ConfigDB, RequestURI) ->
+ case httpd_util:key1search(Data,real_name) of
+ undefined ->
+ DocumentRoot = httpd_util:lookup(ConfigDB, document_root, ""),
+ {Path,AfterPath} =
+ httpd_util:split_path(DocumentRoot++RequestURI),
+ Path;
+ {Path,AfterPath} ->
+ Path
+ end.
+
+%%
+%% Configuration
+%%
+
+%% load
+
+load([$D,$i,$r,$e,$c,$t,$o,$r,$y,$I,$n,$d,$e,$x,$ |DirectoryIndex],[]) ->
+ {ok, DirectoryIndexes} = regexp:split(DirectoryIndex," "),
+ {ok,[], {directory_index, DirectoryIndexes}};
+load([$A,$l,$i,$a,$s,$ |Alias],[]) ->
+ case regexp:split(Alias," ") of
+ {ok, [FakeName, RealName]} ->
+ {ok,[],{alias,{FakeName,RealName}}};
+ {ok, _} ->
+ {error,?NICE(httpd_conf:clean(Alias)++" is an invalid Alias")}
+ end;
+load([$S,$c,$r,$i,$p,$t,$A,$l,$i,$a,$s,$ |ScriptAlias],[]) ->
+ case regexp:split(ScriptAlias," ") of
+ {ok, [FakeName, RealName]} ->
+ %% Make sure the path always has a trailing slash..
+ RealName1 = filename:join(filename:split(RealName)),
+ {ok, [], {script_alias,{FakeName, RealName1++"/"}}};
+ {ok, _} ->
+ {error, ?NICE(httpd_conf:clean(ScriptAlias)++
+ " is an invalid ScriptAlias")}
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.erl
new file mode 100644
index 0000000000..dadb64e3c1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.erl
@@ -0,0 +1,750 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_auth.erl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+-module(mod_auth).
+
+
+%% The functions that the webbserver call on startup stop
+%% and when the server traverse the modules.
+-export([do/1, load/2, store/2, remove/1]).
+
+%% User entries to the gen-server.
+-export([add_user/2, add_user/5, add_user/6,
+ add_group_member/3, add_group_member/4, add_group_member/5,
+ list_users/1, list_users/2, list_users/3,
+ delete_user/2, delete_user/3, delete_user/4,
+ delete_group_member/3, delete_group_member/4, delete_group_member/5,
+ list_groups/1, list_groups/2, list_groups/3,
+ delete_group/2, delete_group/3, delete_group/4,
+ get_user/2, get_user/3, get_user/4,
+ list_group_members/2, list_group_members/3, list_group_members/4,
+ update_password/6, update_password/5]).
+
+-include("httpd.hrl").
+-include("mod_auth.hrl").
+
+-define(VMODULE,"AUTH").
+-include("httpd_verbosity.hrl").
+
+-define(NOPASSWORD,"NoPassword").
+
+
+%% do
+do(Info) ->
+ ?vtrace("do", []),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed, Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ Path = mod_alias:path(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),
+ %% Is it a secret area?
+ case secretp(Path,Info#mod.config_db) of
+ {yes, Directory, DirectoryData} ->
+ %% Authenticate (allow)
+ case allow((Info#mod.init_data)#init_data.peername,
+ Info#mod.socket_type,Info#mod.socket,
+ DirectoryData) of
+ allowed ->
+ case deny((Info#mod.init_data)#init_data.peername,
+ Info#mod.socket_type, Info#mod.socket,
+ DirectoryData) of
+ not_denied ->
+ case httpd_util:key1search(DirectoryData,
+ auth_type) of
+ undefined ->
+ {proceed, Info#mod.data};
+ none ->
+ {proceed, Info#mod.data};
+ AuthType ->
+ do_auth(Info,
+ Directory,
+ DirectoryData,
+ AuthType)
+ end;
+ {denied, Reason} ->
+ {proceed,
+ [{status,{403,Info#mod.request_uri,Reason}}|
+ Info#mod.data]}
+ end;
+ {not_allowed, Reason} ->
+ {proceed,[{status,{403,Info#mod.request_uri,Reason}}|
+ Info#mod.data]}
+ end;
+ no ->
+ {proceed, Info#mod.data}
+ end;
+ %% A response has been generated or sent!
+ Response ->
+ {proceed, Info#mod.data}
+ end
+ end.
+
+
+do_auth(Info, Directory, DirectoryData, AuthType) ->
+ %% Authenticate (require)
+ case require(Info, Directory, DirectoryData) of
+ authorized ->
+ {proceed,Info#mod.data};
+ {authorized, User} ->
+ {proceed, [{remote_user,User}|Info#mod.data]};
+ {authorization_failed, Reason} ->
+ ?vtrace("do_auth -> authorization_failed: ~p",[Reason]),
+ {proceed, [{status,{401,none,Reason}}|Info#mod.data]};
+ {authorization_required, Realm} ->
+ ?vtrace("do_auth -> authorization_required: ~p",[Realm]),
+ ReasonPhrase = httpd_util:reason_phrase(401),
+ Message = httpd_util:message(401,none,Info#mod.config_db),
+ {proceed,
+ [{response,
+ {401,
+ ["WWW-Authenticate: Basic realm=\"",Realm,
+ "\"\r\n\r\n","<HTML>\n<HEAD>\n<TITLE>",
+ ReasonPhrase,"</TITLE>\n",
+ "</HEAD>\n<BODY>\n<H1>",ReasonPhrase,
+ "</H1>\n",Message,"\n</BODY>\n</HTML>\n"]}}|
+ Info#mod.data]};
+ {status, {StatusCode,PhraseArgs,Reason}} ->
+ {proceed, [{status,{StatusCode,PhraseArgs,Reason}}|
+ Info#mod.data]}
+ end.
+
+
+%% require
+
+require(Info, Directory, DirectoryData) ->
+ ParsedHeader = Info#mod.parsed_header,
+ ValidUsers = httpd_util:key1search(DirectoryData, require_user),
+ ValidGroups = httpd_util:key1search(DirectoryData, require_group),
+
+ %% Any user or group restrictions?
+ case ValidGroups of
+ undefined when ValidUsers == undefined ->
+ authorized;
+ _ ->
+ case httpd_util:key1search(ParsedHeader, "authorization") of
+ %% Authorization required!
+ undefined ->
+ case httpd_util:key1search(DirectoryData, auth_name) of
+ undefined ->
+ {status,{500,none,?NICE("AuthName directive not specified")}};
+ Realm ->
+ {authorization_required, Realm}
+ end;
+ %% Check credentials!
+ [$B,$a,$s,$i,$c,$ | EncodedString] ->
+ DecodedString = httpd_util:decode_base64(EncodedString),
+ case a_valid_user(Info, DecodedString,
+ ValidUsers, ValidGroups,
+ Directory, DirectoryData) of
+ {yes, User} ->
+ {authorized, User};
+ {no, Reason} ->
+ {authorization_failed, Reason};
+ {status, {StatusCode,PhraseArgs,Reason}} ->
+ {status,{StatusCode,PhraseArgs,Reason}}
+ end;
+ %% Bad credentials!
+ BadCredentials ->
+ {status,{401,none,?NICE("Bad credentials "++BadCredentials)}}
+ end
+ end.
+
+a_valid_user(Info,DecodedString,ValidUsers,ValidGroups,Dir,DirData) ->
+ case httpd_util:split(DecodedString,":",2) of
+ {ok,[SupposedUser, Password]} ->
+ case user_accepted(SupposedUser, ValidUsers) of
+ true ->
+ check_password(SupposedUser, Password, Dir, DirData);
+ false ->
+ case group_accepted(Info,SupposedUser,ValidGroups,Dir,DirData) of
+ true ->
+ check_password(SupposedUser,Password,Dir,DirData);
+ false ->
+ {no,?NICE("No such user exists")}
+ end
+ end;
+ {ok,BadCredentials} ->
+ {status,{401,none,?NICE("Bad credentials "++BadCredentials)}}
+ end.
+
+user_accepted(SupposedUser, undefined) ->
+ false;
+user_accepted(SupposedUser, ValidUsers) ->
+ lists:member(SupposedUser, ValidUsers).
+
+
+group_accepted(Info, User, undefined, Dir, DirData) ->
+ false;
+group_accepted(Info, User, [], Dir, DirData) ->
+ false;
+group_accepted(Info, User, [Group|Rest], Dir, DirData) ->
+ Ret = int_list_group_members(Group, Dir, DirData),
+ case Ret of
+ {ok, UserList} ->
+ case lists:member(User, UserList) of
+ true ->
+ true;
+ false ->
+ group_accepted(Info, User, Rest, Dir, DirData)
+ end;
+ Other ->
+ false
+ end.
+
+check_password(User, Password, Dir, DirData) ->
+ case int_get_user(DirData, User) of
+ {ok, UStruct} ->
+ case UStruct#httpd_user.password of
+ Password ->
+ %% FIXME
+ {yes, UStruct#httpd_user.username};
+ Other ->
+ {no, "No such user"} % Don't say 'Bad Password' !!!
+ end;
+ _ ->
+ {no, "No such user"}
+ end.
+
+
+%% Middle API. Theese functions call the appropriate authentication module.
+int_get_user(DirData, User) ->
+ AuthMod = auth_mod_name(DirData),
+ apply(AuthMod, get_user, [DirData, User]).
+
+int_list_group_members(Group, Dir, DirData) ->
+ AuthMod = auth_mod_name(DirData),
+ apply(AuthMod, list_group_members, [DirData, Group]).
+
+auth_mod_name(DirData) ->
+ case httpd_util:key1search(DirData, auth_type, plain) of
+ plain -> mod_auth_plain;
+ mnesia -> mod_auth_mnesia;
+ dets -> mod_auth_dets
+ end.
+
+
+%%
+%% Is it a secret area?
+%%
+
+%% secretp
+
+secretp(Path,ConfigDB) ->
+ Directories = ets:match(ConfigDB,{directory,'$1','_'}),
+ case secret_path(Path, Directories) of
+ {yes,Directory} ->
+ {yes,Directory,
+ lists:flatten(ets:match(ConfigDB,{directory,Directory,'$1'}))};
+ no ->
+ no
+ end.
+
+secret_path(Path,Directories) ->
+ secret_path(Path, httpd_util:uniq(lists:sort(Directories)),to_be_found).
+
+secret_path(Path,[],to_be_found) ->
+ no;
+secret_path(Path,[],Directory) ->
+ {yes,Directory};
+secret_path(Path,[[NewDirectory]|Rest],Directory) ->
+ case regexp:match(Path,NewDirectory) of
+ {match,_,_} when Directory == to_be_found ->
+ secret_path(Path,Rest,NewDirectory);
+ {match,_,Length} when Length > length(Directory)->
+ secret_path(Path,Rest,NewDirectory);
+ {match,_,Length} ->
+ secret_path(Path,Rest,Directory);
+ nomatch ->
+ secret_path(Path,Rest,Directory)
+ end.
+
+%%
+%% Authenticate
+%%
+
+%% allow
+
+allow({_,RemoteAddr},SocketType,Socket,DirectoryData) ->
+ Hosts = httpd_util:key1search(DirectoryData, allow_from, all),
+ case validate_addr(RemoteAddr,Hosts) of
+ true ->
+ allowed;
+ false ->
+ {not_allowed, ?NICE("Connection from your host is not allowed")}
+ end.
+
+validate_addr(RemoteAddr,all) -> % When called from 'allow'
+ true;
+validate_addr(RemoteAddr,none) -> % When called from 'deny'
+ false;
+validate_addr(RemoteAddr,[]) ->
+ false;
+validate_addr(RemoteAddr,[HostRegExp|Rest]) ->
+ ?DEBUG("validate_addr -> RemoteAddr: ~p HostRegExp: ~p",
+ [RemoteAddr, HostRegExp]),
+ case regexp:match(RemoteAddr, HostRegExp) of
+ {match,_,_} ->
+ true;
+ nomatch ->
+ validate_addr(RemoteAddr,Rest)
+ end.
+
+%% deny
+
+deny({_,RemoteAddr},SocketType,Socket,DirectoryData) ->
+ ?DEBUG("deny -> RemoteAddr: ~p",[RemoteAddr]),
+ Hosts = httpd_util:key1search(DirectoryData, deny_from, none),
+ ?DEBUG("deny -> Hosts: ~p",[Hosts]),
+ case validate_addr(RemoteAddr,Hosts) of
+ true ->
+ {denied, ?NICE("Connection from your host is not allowed")};
+ false ->
+ not_denied
+ end.
+
+%%
+%% Configuration
+%%
+
+%% load/2
+%%
+
+%% mod_auth recognizes the following Configuration Directives:
+%% <Directory /path/to/directory>
+%% AuthDBType
+%% AuthName
+%% AuthUserFile
+%% AuthGroupFile
+%% AuthAccessPassword
+%% require
+%% allow
+%% </Directory>
+
+%% When a <Directory> directive is found, a new context is set to
+%% [{directory, Directory, DirData}|OtherContext]
+%% DirData in this case is a key-value list of data belonging to the
+%% directory in question.
+%%
+%% When the </Directory> statement is found, the Context created earlier
+%% will be returned as a ConfigList and the context will return to the
+%% state it was previously.
+
+load([$<,$D,$i,$r,$e,$c,$t,$o,$r,$y,$ |Directory],[]) ->
+ Dir = httpd_conf:custom_clean(Directory,"",">"),
+ {ok,[{directory, Dir, [{path, Dir}]}]};
+load(eof,[{directory,Directory, DirData}|_]) ->
+ {error, ?NICE("Premature end-of-file in "++Directory)};
+
+load([$A,$u,$t,$h,$N,$a,$m,$e,$ |AuthName], [{directory,Directory, DirData}|Rest]) ->
+ {ok, [{directory,Directory,
+ [ {auth_name, httpd_conf:clean(AuthName)}|DirData]} | Rest ]};
+
+load([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e,$ |AuthUserFile0],
+ [{directory, Directory, DirData}|Rest]) ->
+ AuthUserFile = httpd_conf:clean(AuthUserFile0),
+ {ok,[{directory,Directory,
+ [ {auth_user_file, AuthUserFile}|DirData]} | Rest ]};
+
+load([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$ |AuthGroupFile0],
+ [{directory,Directory, DirData}|Rest]) ->
+ AuthGroupFile = httpd_conf:clean(AuthGroupFile0),
+ {ok,[{directory,Directory,
+ [ {auth_group_file, AuthGroupFile}|DirData]} | Rest]};
+
+%AuthAccessPassword
+load([$A,$u,$t,$h,$A,$c,$c,$e,$s,$s,$P,$a,$s,$s,$w,$o,$r,$d,$ |AuthAccessPassword0],
+ [{directory,Directory, DirData}|Rest]) ->
+ AuthAccessPassword = httpd_conf:clean(AuthAccessPassword0),
+ {ok,[{directory,Directory,
+ [{auth_access_password, AuthAccessPassword}|DirData]} | Rest]};
+
+
+
+
+load([$A,$u,$t,$h,$D,$B,$T,$y,$p,$e,$ |Type],
+ [{directory, Dir, DirData}|Rest]) ->
+ case httpd_conf:clean(Type) of
+ "plain" ->
+ {ok, [{directory, Dir, [{auth_type, plain}|DirData]} | Rest ]};
+ "mnesia" ->
+ {ok, [{directory, Dir, [{auth_type, mnesia}|DirData]} | Rest ]};
+ "dets" ->
+ {ok, [{directory, Dir, [{auth_type, dets}|DirData]} | Rest ]};
+ _ ->
+ {error, ?NICE(httpd_conf:clean(Type)++" is an invalid AuthDBType")}
+ end;
+
+load([$r,$e,$q,$u,$i,$r,$e,$ |Require],[{directory,Directory, DirData}|Rest]) ->
+ case regexp:split(Require," ") of
+ {ok,["user"|Users]} ->
+ {ok,[{directory,Directory,
+ [{require_user,Users}|DirData]} | Rest]};
+ {ok,["group"|Groups]} ->
+ {ok,[{directory,Directory,
+ [{require_group,Groups}|DirData]} | Rest]};
+ {ok,_} ->
+ {error,?NICE(httpd_conf:clean(Require)++" is an invalid require")}
+ end;
+
+load([$a,$l,$l,$o,$w,$ |Allow],[{directory,Directory, DirData}|Rest]) ->
+ case regexp:split(Allow," ") of
+ {ok,["from","all"]} ->
+ {ok,[{directory,Directory,
+ [{allow_from,all}|DirData]} | Rest]};
+ {ok,["from"|Hosts]} ->
+ {ok,[{directory,Directory,
+ [{allow_from,Hosts}|DirData]} | Rest]};
+ {ok,_} ->
+ {error,?NICE(httpd_conf:clean(Allow)++" is an invalid allow")}
+ end;
+
+load([$d,$e,$n,$y,$ |Deny],[{directory,Directory, DirData}|Rest]) ->
+ case regexp:split(Deny," ") of
+ {ok, ["from", "all"]} ->
+ {ok,[{directory, Directory,
+ [{deny_from, all}|DirData]} | Rest]};
+ {ok, ["from"|Hosts]} ->
+ {ok,[{directory, Directory,
+ [{deny_from, Hosts}|DirData]} | Rest]};
+ {ok, _} ->
+ {error,?NICE(httpd_conf:clean(Deny)++" is an invalid deny")}
+ end;
+
+load("</Directory>",[{directory,Directory, DirData}|Rest]) ->
+ {ok, Rest, {directory, Directory, DirData}};
+
+load([$A,$u,$t,$h,$M,$n,$e,$s,$i,$a,$D,$B,$ |AuthMnesiaDB],
+ [{directory, Dir, DirData}|Rest]) ->
+ case httpd_conf:clean(AuthMnesiaDB) of
+ "On" ->
+ {ok,[{directory,Dir,[{auth_type,mnesia}|DirData]}|Rest]};
+ "Off" ->
+ {ok,[{directory,Dir,[{auth_type,plain}|DirData]}|Rest]};
+ _ ->
+ {error, ?NICE(httpd_conf:clean(AuthMnesiaDB)++" is an invalid AuthMnesiaDB")}
+ end.
+
+%% store
+
+store({directory,Directory0, DirData0}, ConfigList) ->
+ Port = httpd_util:key1search(ConfigList, port),
+ DirData = case httpd_util:key1search(ConfigList, bind_address) of
+ undefined ->
+ [{port, Port}|DirData0];
+ Addr ->
+ [{port, Port},{bind_address,Addr}|DirData0]
+ end,
+ Directory =
+ case filename:pathtype(Directory0) of
+ relative ->
+ SR = httpd_util:key1search(ConfigList, server_root),
+ filename:join(SR, Directory0);
+ _ ->
+ Directory0
+ end,
+ AuthMod =
+ case httpd_util:key1search(DirData0, auth_type) of
+ mnesia -> mod_auth_mnesia;
+ dets -> mod_auth_dets;
+ plain -> mod_auth_plain;
+ _ -> no_module_at_all
+ end,
+ case AuthMod of
+ no_module_at_all ->
+ {ok, {directory, Directory, DirData}};
+ _ ->
+ %% Control that there are a password or add a standard password:
+ %% "NoPassword"
+ %% In this way a user must select to use a noPassword
+ Pwd = case httpd_util:key1search(DirData,auth_access_password)of
+ undefined->
+ ?NOPASSWORD;
+ PassW->
+ PassW
+ end,
+ DirDataLast = lists:keydelete(auth_access_password,1,DirData),
+ case catch AuthMod:store_directory_data(Directory, DirDataLast) of
+ ok ->
+ add_auth_password(Directory,Pwd,ConfigList),
+ {ok, {directory, Directory, DirDataLast}};
+ {ok, NewDirData} ->
+ add_auth_password(Directory,Pwd,ConfigList),
+ {ok, {directory, Directory, NewDirData}};
+ {error, Reason} ->
+ {error, Reason};
+ Other ->
+ ?ERROR("unexpected result: ~p",[Other]),
+ {error, Other}
+ end
+ end.
+
+
+add_auth_password(Dir, Pwd0, ConfigList) ->
+ Addr = httpd_util:key1search(ConfigList, bind_address),
+ Port = httpd_util:key1search(ConfigList, port),
+ mod_auth_server:start(Addr, Port),
+ mod_auth_server:add_password(Addr, Port, Dir, Pwd0).
+
+%% remove
+
+
+remove(ConfigDB) ->
+ lists:foreach(fun({directory, Dir, DirData}) ->
+ AuthMod = auth_mod_name(DirData),
+ (catch apply(AuthMod, remove, [DirData]))
+ end,
+ ets:match_object(ConfigDB,{directory,'_','_'})),
+ Addr = case lookup(ConfigDB, bind_address) of
+ [] ->
+ undefined;
+ [{bind_address, Address}] ->
+ Address
+ end,
+ [{port, Port}] = lookup(ConfigDB, port),
+ mod_auth_server:stop(Addr, Port),
+ ok.
+
+
+
+
+%% --------------------------------------------------------------------
+
+%% update_password
+
+update_password(Port, Dir, Old, New, New)->
+ update_password(undefined, Port, Dir, Old, New, New).
+
+update_password(Addr, Port, Dir, Old, New, New) when list(New) ->
+ mod_auth_server:update_password(Addr, Port, Dir, Old, New);
+
+update_password(_Addr, _Port, _Dir, _Old, New, New) ->
+ {error, badtype};
+update_password(_Addr, _Port, _Dir, _Old, New, New1) ->
+ {error, notqeual}.
+
+
+%% add_user
+
+add_user(UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd}->
+ case get_options(Opt, userData) of
+ {error, Reason}->
+ {error, Reason};
+ {UserData, Password}->
+ User = [#httpd_user{username = UserName,
+ password = Password,
+ user_data = UserData}],
+ mod_auth_server:add_user(Addr, Port, Dir, User, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end.
+
+
+add_user(UserName, Password, UserData, Port, Dir) ->
+ add_user(UserName, Password, UserData, undefined, Port, Dir).
+add_user(UserName, Password, UserData, Addr, Port, Dir) ->
+ User = [#httpd_user{username = UserName,
+ password = Password,
+ user_data = UserData}],
+ mod_auth_server:add_user(Addr, Port, Dir, User, ?NOPASSWORD).
+
+
+%% get_user
+
+get_user(UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:get_user(Addr, Port, Dir, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+get_user(UserName, Port, Dir) ->
+ get_user(UserName, undefined, Port, Dir).
+get_user(UserName, Addr, Port, Dir) ->
+ mod_auth_server:get_user(Addr, Port, Dir, UserName, ?NOPASSWORD).
+
+
+%% add_group_member
+
+add_group_member(GroupName, UserName, Opt)->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd}->
+ mod_auth_server:add_group_member(Addr, Port, Dir,
+ GroupName, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+add_group_member(GroupName, UserName, Port, Dir) ->
+ add_group_member(GroupName, UserName, undefined, Port, Dir).
+
+add_group_member(GroupName, UserName, Addr, Port, Dir) ->
+ mod_auth_server:add_group_member(Addr, Port, Dir,
+ GroupName, UserName, ?NOPASSWORD).
+
+
+%% delete_group_member
+
+delete_group_member(GroupName, UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:delete_group_member(Addr, Port, Dir,
+ GroupName, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+delete_group_member(GroupName, UserName, Port, Dir) ->
+ delete_group_member(GroupName, UserName, undefined, Port, Dir).
+delete_group_member(GroupName, UserName, Addr, Port, Dir) ->
+ mod_auth_server:delete_group_member(Addr, Port, Dir,
+ GroupName, UserName, ?NOPASSWORD).
+
+
+%% list_users
+
+list_users(Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:list_users(Addr, Port, Dir, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+list_users(Port, Dir) ->
+ list_users(undefined, Port, Dir).
+list_users(Addr, Port, Dir) ->
+ mod_auth_server:list_users(Addr, Port, Dir, ?NOPASSWORD).
+
+
+%% delete_user
+
+delete_user(UserName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:delete_user(Addr, Port, Dir, UserName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+delete_user(UserName, Port, Dir) ->
+ delete_user(UserName, undefined, Port, Dir).
+delete_user(UserName, Addr, Port, Dir) ->
+ mod_auth_server:delete_user(Addr, Port, Dir, UserName, ?NOPASSWORD).
+
+
+%% delete_group
+
+delete_group(GroupName, Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd}->
+ mod_auth_server:delete_group(Addr, Port, Dir, GroupName, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+delete_group(GroupName, Port, Dir) ->
+ delete_group(GroupName, undefined, Port, Dir).
+delete_group(GroupName, Addr, Port, Dir) ->
+ mod_auth_server:delete_group(Addr, Port, Dir, GroupName, ?NOPASSWORD).
+
+
+%% list_groups
+
+list_groups(Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd}->
+ mod_auth_server:list_groups(Addr, Port, Dir, AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+list_groups(Port, Dir) ->
+ list_groups(undefined, Port, Dir).
+list_groups(Addr, Port, Dir) ->
+ mod_auth_server:list_groups(Addr, Port, Dir, ?NOPASSWORD).
+
+
+%% list_group_members
+
+list_group_members(GroupName,Opt) ->
+ case get_options(Opt, mandatory) of
+ {Addr, Port, Dir, AuthPwd} ->
+ mod_auth_server:list_group_members(Addr, Port, Dir, GroupName,
+ AuthPwd);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+list_group_members(GroupName, Port, Dir) ->
+ list_group_members(GroupName, undefined, Port, Dir).
+list_group_members(GroupName, Addr, Port, Dir) ->
+ mod_auth_server:list_group_members(Addr, Port, Dir, GroupName, ?NOPASSWORD).
+
+
+
+%% Opt = [{port, Port},
+%% {addr, Addr},
+%% {dir, Dir},
+%% {authPassword, AuthPassword} | FunctionSpecificData]
+get_options(Opt, mandatory)->
+ case httpd_util:key1search(Opt, port, undefined) of
+ Port when integer(Port) ->
+ case httpd_util:key1search(Opt, dir, undefined) of
+ Dir when list(Dir) ->
+ Addr = httpd_util:key1search(Opt,
+ addr,
+ undefined),
+ AuthPwd = httpd_util:key1search(Opt,
+ authPassword,
+ ?NOPASSWORD),
+ {Addr, Port, Dir, AuthPwd};
+ _->
+ {error, bad_dir}
+ end;
+ _ ->
+ {error, bad_dir}
+ end;
+
+%% FunctionSpecificData = {userData, UserData} | {password, Password}
+get_options(Opt, userData)->
+ case httpd_util:key1search(Opt, userData, undefined) of
+ undefined ->
+ {error, no_userdata};
+ UserData ->
+ case httpd_util:key1search(Opt, password, undefined) of
+ undefined->
+ {error, no_password};
+ Pwd ->
+ {UserData, Pwd}
+ end
+ end.
+
+
+lookup(Db, Key) ->
+ ets:lookup(Db, Key).
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.hrl
new file mode 100644
index 0000000000..ed3f437e60
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth.hrl
@@ -0,0 +1,27 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_auth.hrl,v 1.1 2008/12/17 09:53:34 mikpe Exp $
+%%
+
+-record(httpd_user,
+ {username,
+ password,
+ user_data}).
+
+-record(httpd_group,
+ {name,
+ userlist}).
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_dets.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_dets.erl
new file mode 100644
index 0000000000..89d8574e83
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_dets.erl
@@ -0,0 +1,222 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_auth_dets.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_auth_dets).
+
+%% dets authentication storage
+
+-export([get_user/2,
+ list_group_members/2,
+ add_user/2,
+ add_group_member/3,
+ list_users/1,
+ delete_user/2,
+ list_groups/1,
+ delete_group_member/3,
+ delete_group/2,
+ remove/1]).
+
+-export([store_directory_data/2]).
+
+-include("httpd.hrl").
+-include("mod_auth.hrl").
+
+store_directory_data(Directory, DirData) ->
+ ?CDEBUG("store_directory_data -> ~n"
+ " Directory: ~p~n"
+ " DirData: ~p",
+ [Directory, DirData]),
+
+ PWFile = httpd_util:key1search(DirData, auth_user_file),
+ GroupFile = httpd_util:key1search(DirData, auth_group_file),
+ Addr = httpd_util:key1search(DirData, bind_address),
+ Port = httpd_util:key1search(DirData, port),
+
+ PWName = httpd_util:make_name("httpd_dets_pwdb",Addr,Port),
+ case dets:open_file(PWName,[{type,set},{file,PWFile},{repair,true}]) of
+ {ok, PWDB} ->
+ GDBName = httpd_util:make_name("httpd_dets_groupdb",Addr,Port),
+ case dets:open_file(GDBName,[{type,set},{file,GroupFile},{repair,true}]) of
+ {ok, GDB} ->
+ NDD1 = lists:keyreplace(auth_user_file, 1, DirData,
+ {auth_user_file, PWDB}),
+ NDD2 = lists:keyreplace(auth_group_file, 1, NDD1,
+ {auth_group_file, GDB}),
+ {ok, NDD2};
+ {error, Err}->
+ {error, {{file, GroupFile},Err}}
+ end;
+ {error, Err2} ->
+ {error, {{file, PWFile},Err2}}
+ end.
+
+%%
+%% Storage format of users in the dets table:
+%% {{UserName, Addr, Port, Dir}, Password, UserData}
+%%
+
+add_user(DirData, UStruct) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ Record = {{UStruct#httpd_user.username, Addr, Port, Dir},
+ UStruct#httpd_user.password, UStruct#httpd_user.user_data},
+ case dets:lookup(PWDB, UStruct#httpd_user.username) of
+ [Record] ->
+ {error, user_already_in_db};
+ _ ->
+ dets:insert(PWDB, Record),
+ true
+ end.
+
+get_user(DirData, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ User = {UserName, Addr, Port, Dir},
+ case dets:lookup(PWDB, User) of
+ [{User, Password, UserData}] ->
+ {ok, #httpd_user{username=UserName, password=Password, user_data=UserData}};
+ Other ->
+ {error, no_such_user}
+ end.
+
+list_users(DirData) ->
+ ?DEBUG("list_users -> ~n"
+ " DirData: ~p", [DirData]),
+ {Addr, Port, Dir} = lookup_common(DirData),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ case dets:traverse(PWDB, fun(X) -> {continue, X} end) of %% SOOOO Ugly !
+ Records when list(Records) ->
+ ?DEBUG("list_users -> ~n"
+ " Records: ~p", [Records]),
+ {ok, [UserName || {{UserName, AnyAddr, AnyPort, AnyDir}, Password, _Data} <- Records,
+ AnyAddr == Addr, AnyPort == Port, AnyDir == Dir]};
+ O ->
+ ?DEBUG("list_users -> ~n"
+ " O: ~p", [O]),
+ {ok, []}
+ end.
+
+delete_user(DirData, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ User = {UserName, Addr, Port, Dir},
+ case dets:lookup(PWDB, User) of
+ [{User, SomePassword, UserData}] ->
+ dets:delete(PWDB, User),
+ lists:foreach(fun(Group) -> delete_group_member(DirData, Group, UserName) end,
+ list_groups(DirData)),
+ true;
+ _ ->
+ {error, no_such_user}
+ end.
+
+%%
+%% Storage of groups in the dets table:
+%% {Group, UserList} where UserList is a list of strings.
+%%
+add_group_member(DirData, GroupName, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ Group = {GroupName, Addr, Port, Dir},
+ case dets:lookup(GDB, Group) of
+ [{Group, Users}] ->
+ case lists:member(UserName, Users) of
+ true ->
+ true;
+ false ->
+ dets:insert(GDB, {Group, [UserName|Users]}),
+ true
+ end;
+ [] ->
+ dets:insert(GDB, {Group, [UserName]}),
+ true;
+ Other ->
+ {error, Other}
+ end.
+
+list_group_members(DirData, GroupName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ Group = {GroupName, Addr, Port, Dir},
+ case dets:lookup(GDB, Group) of
+ [{Group, Users}] ->
+ {ok, Users};
+ Other ->
+ {error, no_such_group}
+ end.
+
+list_groups(DirData) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ case dets:match(GDB, {'$1', '_'}) of
+ [] ->
+ {ok, []};
+ List when list(List) ->
+ Groups = lists:flatten(List),
+ {ok, [GroupName || {GroupName, AnyAddr, AnyPort, AnyDir} <- Groups,
+ AnyAddr == Addr, AnyPort == Port, AnyDir == Dir]};
+ _ ->
+ {ok, []}
+ end.
+
+delete_group_member(DirData, GroupName, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ Group = {GroupName, Addr, Port, Dir},
+ case dets:lookup(GDB, GroupName) of
+ [{Group, Users}] ->
+ case lists:member(UserName, Users) of
+ true ->
+ dets:delete(GDB, Group),
+ dets:insert(GDB, {Group,
+ lists:delete(UserName, Users)}),
+ true;
+ false ->
+ {error, no_such_group_member}
+ end;
+ _ ->
+ {error, no_such_group}
+ end.
+
+delete_group(DirData, GroupName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ Group = {GroupName, Addr, Port, Dir},
+ case dets:lookup(GDB, Group) of
+ [{Group, Users}] ->
+ dets:delete(GDB, Group),
+ true;
+ _ ->
+ {error, no_such_group}
+ end.
+
+lookup_common(DirData) ->
+ Dir = httpd_util:key1search(DirData, path),
+ Port = httpd_util:key1search(DirData, port),
+ Addr = httpd_util:key1search(DirData, bind_address),
+ {Addr, Port, Dir}.
+
+%% remove/1
+%%
+%% Closes dets tables used by this auth mod.
+%%
+remove(DirData) ->
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ dets:close(GDB),
+ dets:close(PWDB),
+ ok.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_mnesia.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_mnesia.erl
new file mode 100644
index 0000000000..ec29022da0
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_mnesia.erl
@@ -0,0 +1,276 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_auth_mnesia.erl,v 1.2 2010/03/04 13:54:19 maria Exp $
+%%
+-module(mod_auth_mnesia).
+-export([get_user/2,
+ list_group_members/2,
+ add_user/2,
+ add_group_member/3,
+ list_users/1,
+ delete_user/2,
+ list_groups/1,
+ delete_group_member/3,
+ delete_group/2]).
+
+-export([store_user/5, store_user/6,
+ store_group_member/5, store_group_member/6,
+ list_group_members/3, list_group_members/4,
+ list_groups/2, list_groups/3,
+ list_users/2, list_users/3,
+ remove_user/4, remove_user/5,
+ remove_group_member/5, remove_group_member/6,
+ remove_group/4, remove_group/5]).
+
+-export([store_directory_data/2]).
+
+-include("httpd.hrl").
+-include("mod_auth.hrl").
+
+
+
+store_directory_data(Directory, DirData) ->
+ %% We don't need to do anything here, we could ofcourse check that the appropriate
+ %% mnesia tables has been created prior to starting the http server.
+ ok.
+
+
+%%
+%% API
+%%
+
+%% Compability API
+
+
+store_user(UserName, Password, Port, Dir, AccessPassword) ->
+ %% AccessPassword is ignored - was not used in previous version
+ DirData = [{path,Dir},{port,Port}],
+ UStruct = #httpd_user{username = UserName,
+ password = Password},
+ add_user(DirData, UStruct).
+
+store_user(UserName, Password, Addr, Port, Dir, AccessPassword) ->
+ %% AccessPassword is ignored - was not used in previous version
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ UStruct = #httpd_user{username = UserName,
+ password = Password},
+ add_user(DirData, UStruct).
+
+store_group_member(GroupName, UserName, Port, Dir, AccessPassword) ->
+ DirData = [{path,Dir},{port,Port}],
+ add_group_member(DirData, GroupName, UserName).
+
+store_group_member(GroupName, UserName, Addr, Port, Dir, AccessPassword) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ add_group_member(DirData, GroupName, UserName).
+
+list_group_members(GroupName, Port, Dir) ->
+ DirData = [{path,Dir},{port,Port}],
+ list_group_members(DirData, GroupName).
+
+list_group_members(GroupName, Addr, Port, Dir) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ list_group_members(DirData, GroupName).
+
+list_groups(Port, Dir) ->
+ DirData = [{path,Dir},{port,Port}],
+ list_groups(DirData).
+
+list_groups(Addr, Port, Dir) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ list_groups(DirData).
+
+list_users(Port, Dir) ->
+ DirData = [{path,Dir},{port,Port}],
+ list_users(DirData).
+
+list_users(Addr, Port, Dir) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ list_users(DirData).
+
+remove_user(UserName, Port, Dir, _AccessPassword) ->
+ DirData = [{path,Dir},{port,Port}],
+ delete_user(DirData, UserName).
+
+remove_user(UserName, Addr, Port, Dir, _AccessPassword) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ delete_user(DirData, UserName).
+
+remove_group_member(GroupName,UserName,Port,Dir,_AccessPassword) ->
+ DirData = [{path,Dir},{port,Port}],
+ delete_group_member(DirData, GroupName, UserName).
+
+remove_group_member(GroupName,UserName,Addr,Port,Dir,_AccessPassword) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ delete_group_member(DirData, GroupName, UserName).
+
+remove_group(GroupName,Port,Dir,_AccessPassword) ->
+ DirData = [{path,Dir},{port,Port}],
+ delete_group(DirData, GroupName).
+
+remove_group(GroupName,Addr,Port,Dir,_AccessPassword) ->
+ DirData = [{path,Dir},{bind_address,Addr},{port,Port}],
+ delete_group(DirData, GroupName).
+
+%%
+%% Storage format of users in the mnesia table:
+%% httpd_user records
+%%
+
+add_user(DirData, UStruct) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ UserName = UStruct#httpd_user.username,
+ Password = UStruct#httpd_user.password,
+ Data = UStruct#httpd_user.user_data,
+ User=#httpd_user{username={UserName,Addr,Port,Dir},
+ password=Password,
+ user_data=Data},
+ case mnesia:transaction(fun() -> mnesia:write(User) end) of
+ {aborted,Reason} ->
+ {error,Reason};
+ _ ->
+ true
+ end.
+
+get_user(DirData, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ case mnesia:transaction(fun() ->
+ mnesia:read({httpd_user,
+ {UserName,Addr,Port,Dir}})
+ end) of
+ {aborted,Reason} ->
+ {error, Reason};
+ {'atomic',[]} ->
+ {error, no_such_user};
+ {'atomic', [Record]} when record(Record, httpd_user) ->
+ {ok, Record#httpd_user{username=UserName}};
+ Other ->
+ {error, no_such_user}
+ end.
+
+list_users(DirData) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ case mnesia:transaction(fun() ->
+ mnesia:match_object({httpd_user,
+ {'_',Addr,Port,Dir},'_','_'})
+ end) of
+ {aborted,Reason} ->
+ {error,Reason};
+ {'atomic',Users} ->
+ {ok,
+ lists:foldr(fun({httpd_user, {UserName, AnyAddr, AnyPort, AnyDir},
+ Password, Data}, Acc) ->
+ [UserName|Acc]
+ end,
+ [], Users)}
+ end.
+
+delete_user(DirData, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ case mnesia:transaction(fun() ->
+ mnesia:delete({httpd_user,
+ {UserName,Addr,Port,Dir}})
+ end) of
+ {aborted,Reason} ->
+ {error,Reason};
+ _ ->
+ true
+ end.
+
+%%
+%% Storage of groups in the mnesia table:
+%% Multiple instances of {#httpd_group, User}
+%%
+
+add_group_member(DirData, GroupName, User) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ Group=#httpd_group{name={GroupName, Addr, Port, Dir}, userlist=User},
+ case mnesia:transaction(fun() -> mnesia:write(Group) end) of
+ {aborted,Reason} ->
+ {error,Reason};
+ _ ->
+ true
+ end.
+
+list_group_members(DirData, GroupName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ case mnesia:transaction(fun() ->
+ mnesia:read({httpd_group,
+ {GroupName,Addr,Port,Dir}})
+ end) of
+ {aborted, Reason} ->
+ {error,Reason};
+ {'atomic', Members} ->
+ {ok,[UserName || {httpd_group,{AnyGroupName,AnyAddr,AnyPort,AnyDir},UserName} <- Members,
+ AnyGroupName == GroupName, AnyAddr == Addr,
+ AnyPort == Port, AnyDir == Dir]}
+ end.
+
+list_groups(DirData) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ case mnesia:transaction(fun() ->
+ mnesia:match_object({httpd_group,
+ {'_',Addr,Port,Dir},'_'})
+ end) of
+ {aborted, Reason} ->
+ {error, Reason};
+ {'atomic', Groups} ->
+ GroupNames=
+ [GroupName || {httpd_group,{GroupName,AnyAddr,AnyPort,AnyDir}, UserName} <- Groups,
+ AnyAddr == Addr, AnyPort == AnyPort, AnyDir == Dir],
+ {ok, httpd_util:uniq(lists:sort(GroupNames))}
+ end.
+
+delete_group_member(DirData, GroupName, UserName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ Group = #httpd_group{name={GroupName, Addr, Port, Dir}, userlist=UserName},
+ case mnesia:transaction(fun() -> mnesia:delete_object(Group) end) of
+ {aborted,Reason} ->
+ {error,Reason};
+ _ ->
+ true
+ end.
+
+%% THIS IS WRONG (?) !
+%% Should first match out all httpd_group records for this group and then
+%% do mnesia:delete on those. Or ?
+
+delete_group(DirData, GroupName) ->
+ {Addr, Port, Dir} = lookup_common(DirData),
+ case mnesia:transaction(fun() ->
+ mnesia:delete({httpd_group,
+ {GroupName,Addr,Port,Dir}})
+ end) of
+ {aborted,Reason} ->
+ {error,Reason};
+ _ ->
+ true
+ end.
+
+%% Utility functions.
+
+lookup_common(DirData) ->
+ Dir = httpd_util:key1search(DirData, path),
+ Port = httpd_util:key1search(DirData, port),
+ Addr = httpd_util:key1search(DirData, bind_address),
+ {Addr, Port, Dir}.
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_plain.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_plain.erl
new file mode 100644
index 0000000000..2f92dcb446
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_plain.erl
@@ -0,0 +1,344 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_auth_plain.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_auth_plain).
+
+-include("httpd.hrl").
+-include("mod_auth.hrl").
+
+-define(VMODULE,"AUTH_PLAIN").
+-include("httpd_verbosity.hrl").
+
+
+%% Internal API
+-export([store_directory_data/2]).
+
+
+-export([get_user/2,
+ list_group_members/2,
+ add_user/2,
+ add_group_member/3,
+ list_users/1,
+ delete_user/2,
+ list_groups/1,
+ delete_group_member/3,
+ delete_group/2,
+ remove/1]).
+
+%%
+%% API
+%%
+
+%%
+%% Storage format of users in the ets table:
+%% {UserName, Password, UserData}
+%%
+
+add_user(DirData, #httpd_user{username = User} = UStruct) ->
+ ?vtrace("add_user -> entry with:"
+ "~n User: ~p",[User]),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ Record = {User,
+ UStruct#httpd_user.password,
+ UStruct#httpd_user.user_data},
+ case ets:lookup(PWDB, User) of
+ [{User, _SomePassword, _SomeData}] ->
+ {error, user_already_in_db};
+ _ ->
+ ets:insert(PWDB, Record),
+ true
+ end.
+
+get_user(DirData, User) ->
+ ?vtrace("get_user -> entry with:"
+ "~n User: ~p",[User]),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ case ets:lookup(PWDB, User) of
+ [{User, PassWd, Data}] ->
+ {ok, #httpd_user{username=User, password=PassWd, user_data=Data}};
+ _ ->
+ {error, no_such_user}
+ end.
+
+list_users(DirData) ->
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ case ets:match(PWDB, '$1') of
+ Records when list(Records) ->
+ {ok, lists:foldr(fun({User,PassWd,Data}, A) -> [User|A] end,
+ [], lists:flatten(Records))};
+ O ->
+ {ok, []}
+ end.
+
+delete_user(DirData, UserName) ->
+ ?vtrace("delete_user -> entry with:"
+ "~n UserName: ~p",[UserName]),
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ case ets:lookup(PWDB, UserName) of
+ [{UserName, SomePassword, SomeData}] ->
+ ets:delete(PWDB, UserName),
+ case list_groups(DirData) of
+ {ok,Groups}->
+ lists:foreach(fun(Group) ->
+ delete_group_member(DirData, Group, UserName)
+ end,Groups),
+ true;
+ _->
+ true
+ end;
+ _ ->
+ {error, no_such_user}
+ end.
+
+%%
+%% Storage of groups in the ets table:
+%% {Group, UserList} where UserList is a list of strings.
+%%
+
+add_group_member(DirData, Group, UserName) ->
+ ?DEBUG("add_group_members -> ~n"
+ " Group: ~p~n"
+ " UserName: ~p",[Group,UserName]),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ case ets:lookup(GDB, Group) of
+ [{Group, Users}] ->
+ case lists:member(UserName, Users) of
+ true ->
+ ?DEBUG("add_group_members -> already member in group",[]),
+ true;
+ false ->
+ ?DEBUG("add_group_members -> add",[]),
+ ets:insert(GDB, {Group, [UserName|Users]}),
+ true
+ end;
+ [] ->
+ ?DEBUG("add_group_members -> create grouo",[]),
+ ets:insert(GDB, {Group, [UserName]}),
+ true;
+ Other ->
+ ?ERROR("add_group_members -> Other: ~p",[Other]),
+ {error, Other}
+ end.
+
+list_group_members(DirData, Group) ->
+ ?DEBUG("list_group_members -> Group: ~p",[Group]),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ case ets:lookup(GDB, Group) of
+ [{Group, Users}] ->
+ ?DEBUG("list_group_members -> Users: ~p",[Users]),
+ {ok, Users};
+ _ ->
+ {error, no_such_group}
+ end.
+
+list_groups(DirData) ->
+ ?DEBUG("list_groups -> entry",[]),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ case ets:match(GDB, '$1') of
+ [] ->
+ ?DEBUG("list_groups -> []",[]),
+ {ok, []};
+ Groups0 when list(Groups0) ->
+ ?DEBUG("list_groups -> Groups0: ~p",[Groups0]),
+ {ok, httpd_util:uniq(lists:foldr(fun({G, U}, A) -> [G|A] end,
+ [], lists:flatten(Groups0)))};
+ _ ->
+ {ok, []}
+ end.
+
+delete_group_member(DirData, Group, User) ->
+ ?DEBUG("list_group_members -> ~n"
+ " Group: ~p~n"
+ " User: ~p",[Group,User]),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ UDB = httpd_util:key1search(DirData, auth_user_file),
+ case ets:lookup(GDB, Group) of
+ [{Group, Users}] when list(Users) ->
+ case lists:member(User, Users) of
+ true ->
+ ?DEBUG("list_group_members -> deleted from group",[]),
+ ets:delete(GDB, Group),
+ ets:insert(GDB, {Group, lists:delete(User, Users)}),
+ true;
+ false ->
+ ?DEBUG("list_group_members -> not member",[]),
+ {error, no_such_group_member}
+ end;
+ _ ->
+ ?ERROR("list_group_members -> no such group",[]),
+ {error, no_such_group}
+ end.
+
+delete_group(DirData, Group) ->
+ ?DEBUG("list_group_members -> Group: ~p",[Group]),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ case ets:lookup(GDB, Group) of
+ [{Group, Users}] ->
+ ?DEBUG("list_group_members -> delete",[]),
+ ets:delete(GDB, Group),
+ true;
+ _ ->
+ ?ERROR("delete_group -> no such group",[]),
+ {error, no_such_group}
+ end.
+
+
+store_directory_data(Directory, DirData) ->
+ PWFile = httpd_util:key1search(DirData, auth_user_file),
+ GroupFile = httpd_util:key1search(DirData, auth_group_file),
+ case load_passwd(PWFile) of
+ {ok, PWDB} ->
+ case load_group(GroupFile) of
+ {ok, GRDB} ->
+ %% Address and port is included in the file names...
+ Addr = httpd_util:key1search(DirData, bind_address),
+ Port = httpd_util:key1search(DirData, port),
+ {ok, PasswdDB} = store_passwd(Addr,Port,PWDB),
+ {ok, GroupDB} = store_group(Addr,Port,GRDB),
+ NDD1 = lists:keyreplace(auth_user_file, 1, DirData,
+ {auth_user_file, PasswdDB}),
+ NDD2 = lists:keyreplace(auth_group_file, 1, NDD1,
+ {auth_group_file, GroupDB}),
+ {ok, NDD2};
+ Err ->
+ ?ERROR("failed storing directory data: "
+ "load group error: ~p",[Err]),
+ {error, Err}
+ end;
+ Err2 ->
+ ?ERROR("failed storing directory data: "
+ "load passwd error: ~p",[Err2]),
+ {error, Err2}
+ end.
+
+
+
+%% load_passwd
+
+load_passwd(AuthUserFile) ->
+ case file:open(AuthUserFile, [read]) of
+ {ok,Stream} ->
+ parse_passwd(Stream, []);
+ {error, _} ->
+ {error, ?NICE("Can't open "++AuthUserFile)}
+ end.
+
+parse_passwd(Stream,PasswdList) ->
+ Line =
+ case io:get_line(Stream, '') of
+ eof ->
+ eof;
+ String ->
+ httpd_conf:clean(String)
+ end,
+ parse_passwd(Stream, PasswdList, Line).
+
+parse_passwd(Stream, PasswdList, eof) ->
+ file:close(Stream),
+ {ok, PasswdList};
+parse_passwd(Stream, PasswdList, "") ->
+ parse_passwd(Stream, PasswdList);
+parse_passwd(Stream, PasswdList, [$#|_]) ->
+ parse_passwd(Stream, PasswdList);
+parse_passwd(Stream, PasswdList, Line) ->
+ case regexp:split(Line,":") of
+ {ok, [User,Password]} ->
+ parse_passwd(Stream, [{User,Password, []}|PasswdList]);
+ {ok,_} ->
+ {error, ?NICE(Line)}
+ end.
+
+%% load_group
+
+load_group(AuthGroupFile) ->
+ case file:open(AuthGroupFile, [read]) of
+ {ok, Stream} ->
+ parse_group(Stream,[]);
+ {error, _} ->
+ {error, ?NICE("Can't open "++AuthGroupFile)}
+ end.
+
+parse_group(Stream, GroupList) ->
+ Line=
+ case io:get_line(Stream,'') of
+ eof ->
+ eof;
+ String ->
+ httpd_conf:clean(String)
+ end,
+ parse_group(Stream, GroupList, Line).
+
+parse_group(Stream, GroupList, eof) ->
+ file:close(Stream),
+ {ok, GroupList};
+parse_group(Stream, GroupList, "") ->
+ parse_group(Stream, GroupList);
+parse_group(Stream, GroupList, [$#|_]) ->
+ parse_group(Stream, GroupList);
+parse_group(Stream, GroupList, Line) ->
+ case regexp:split(Line, ":") of
+ {ok, [Group,Users]} ->
+ {ok, UserList} = regexp:split(Users," "),
+ parse_group(Stream, [{Group,UserList}|GroupList]);
+ {ok, _} ->
+ {error, ?NICE(Line)}
+ end.
+
+
+%% store_passwd
+
+store_passwd(Addr,Port,PasswdList) ->
+ Name = httpd_util:make_name("httpd_passwd",Addr,Port),
+ PasswdDB = ets:new(Name, [set, public]),
+ store_passwd(PasswdDB, PasswdList).
+
+store_passwd(PasswdDB, []) ->
+ {ok, PasswdDB};
+store_passwd(PasswdDB, [User|Rest]) ->
+ ets:insert(PasswdDB, User),
+ store_passwd(PasswdDB, Rest).
+
+%% store_group
+
+store_group(Addr,Port,GroupList) ->
+ Name = httpd_util:make_name("httpd_group",Addr,Port),
+ GroupDB = ets:new(Name, [set, public]),
+ store_group(GroupDB, GroupList).
+
+
+store_group(GroupDB,[]) ->
+ {ok, GroupDB};
+store_group(GroupDB,[User|Rest]) ->
+ ets:insert(GroupDB, User),
+ store_group(GroupDB, Rest).
+
+
+%% remove/1
+%%
+%% Deletes ets tables used by this auth mod.
+%%
+remove(DirData) ->
+ PWDB = httpd_util:key1search(DirData, auth_user_file),
+ GDB = httpd_util:key1search(DirData, auth_group_file),
+ ets:delete(PWDB),
+ ets:delete(GDB).
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_server.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_server.erl
new file mode 100644
index 0000000000..6694ed7eac
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_auth_server.erl
@@ -0,0 +1,424 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_auth_server.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+
+-module(mod_auth_server).
+
+-include("httpd.hrl").
+%% -include("mod_auth.hrl").
+-include("httpd_verbosity.hrl").
+
+-behaviour(gen_server).
+
+
+%% mod_auth exports
+-export([start/2, stop/2,
+ add_password/4, update_password/5,
+ add_user/5, delete_user/5, get_user/5, list_users/4,
+ add_group_member/6, delete_group_member/6, list_group_members/5,
+ delete_group/5, list_groups/4]).
+
+%% Management exports
+-export([verbosity/3]).
+
+%% gen_server exports
+-export([start_link/3,
+ init/1,
+ handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+
+-record(state,{tab}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% External API %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% start_link/3
+%%
+%% NOTE: This is called by httpd_misc_sup when the process is started
+%%
+start_link(Addr, Port, Verbosity)->
+ ?vlog("start_link -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Addr, Port]),
+ Name = make_name(Addr, Port),
+ gen_server:start_link({local, Name}, ?MODULE, [Verbosity],
+ [{timeout, infinity}]).
+
+
+%% start/2
+
+start(Addr, Port)->
+ ?vtrace("start -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Addr, Port]),
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ undefined ->
+ Verbosity = get(auth_verbosity),
+ case (catch httpd_misc_sup:start_auth_server(Addr, Port,
+ Verbosity)) of
+ {ok, Pid} ->
+ put(auth_server, Pid),
+ ok;
+ {error, Reason} ->
+ exit({failed_start_auth_server, Reason});
+ Error ->
+ exit({failed_start_auth_server, Error})
+ end;
+ _ -> %% Already started...
+ ok
+ end.
+
+
+%% stop/2
+
+stop(Addr, Port)->
+ ?vtrace("stop -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Addr, Port]),
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ undefined -> %% Already stopped
+ ok;
+ _ ->
+ (catch httpd_misc_sup:stop_auth_server(Addr, Port))
+ end.
+
+
+%% verbosity/3
+
+verbosity(Addr, Port, Verbosity) ->
+ Name = make_name(Addr, Port),
+ Req = {verbosity, Verbosity},
+ call(Name, Req).
+
+
+%% add_password/4
+
+add_password(Addr, Port, Dir, Password)->
+ Name = make_name(Addr, Port),
+ Req = {add_password, Dir, Password},
+ call(Name, Req).
+
+
+%% update_password/6
+
+update_password(Addr, Port, Dir, Old, New) when list(New) ->
+ Name = make_name(Addr, Port),
+ Req = {update_password, Dir, Old, New},
+ call(Name, Req).
+
+
+%% add_user/5
+
+add_user(Addr, Port, Dir, User, Password) ->
+ Name = make_name(Addr, Port),
+ Req = {add_user, Addr, Port, Dir, User, Password},
+ call(Name, Req).
+
+
+%% delete_user/5
+
+delete_user(Addr, Port, Dir, UserName, Password) ->
+ Name = make_name(Addr, Port),
+ Req = {delete_user, Addr, Port, Dir, UserName, Password},
+ call(Name, Req).
+
+
+%% get_user/5
+
+get_user(Addr, Port, Dir, UserName, Password) ->
+ Name = make_name(Addr, Port),
+ Req = {get_user, Addr, Port, Dir, UserName, Password},
+ call(Name, Req).
+
+
+%% list_users/4
+
+list_users(Addr, Port, Dir, Password) ->
+ Name = make_name(Addr,Port),
+ Req = {list_users, Addr, Port, Dir, Password},
+ call(Name, Req).
+
+
+%% add_group_member/6
+
+add_group_member(Addr, Port, Dir, GroupName, UserName, Password) ->
+ Name = make_name(Addr,Port),
+ Req = {add_group_member, Addr, Port, Dir, GroupName, UserName, Password},
+ call(Name, Req).
+
+
+%% delete_group_member/6
+
+delete_group_member(Addr, Port, Dir, GroupName, UserName, Password) ->
+ Name = make_name(Addr,Port),
+ Req = {delete_group_member, Addr, Port, Dir, GroupName, UserName, Password},
+ call(Name, Req).
+
+
+%% list_group_members/4
+
+list_group_members(Addr, Port, Dir, Group, Password) ->
+ Name = make_name(Addr, Port),
+ Req = {list_group_members, Addr, Port, Dir, Group, Password},
+ call(Name, Req).
+
+
+%% delete_group/5
+
+delete_group(Addr, Port, Dir, GroupName, Password) ->
+ Name = make_name(Addr, Port),
+ Req = {delete_group, Addr, Port, Dir, GroupName, Password},
+ call(Name, Req).
+
+
+%% list_groups/4
+
+list_groups(Addr, Port, Dir, Password) ->
+ Name = make_name(Addr, Port),
+ Req = {list_groups, Addr, Port, Dir, Password},
+ call(Name, Req).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Server call-back functions %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% init
+
+init([undefined]) ->
+ init([?default_verbosity]);
+
+init([Verbosity]) ->
+ put(sname,auth),
+ put(verbosity,Verbosity),
+ ?vlog("starting",[]),
+ {ok,#state{tab = ets:new(auth_pwd,[set,protected])}}.
+
+
+%% handle_call
+
+%% Add a user
+handle_call({add_user, Addr, Port, Dir, User, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Dir, add_user, User, AuthPwd, State),
+ {reply, Reply, State};
+
+%% Get data about a user
+handle_call({get_user, Addr, Port, Dir, User, AuthPwd}, _From, State) ->
+ Reply = api_call(Addr, Port, Dir, get_user, [User], AuthPwd, State),
+ {reply, Reply, State};
+
+%% Add a group member
+handle_call({add_group_member, Addr, Port, Dir, Group, User, AuthPwd},
+ _From, State) ->
+ Reply = api_call(Addr, Port, Dir, add_group_member, [Group, User],
+ AuthPwd, State),
+ {reply, Reply, State};
+
+%% delete a group
+handle_call({delete_group_member, Addr, Port, Dir, Group, User, AuthPwd},
+ _From, State)->
+ Reply = api_call(Addr, Port, Dir, delete_group_member, [Group, User],
+ AuthPwd, State),
+ {reply, Reply, State};
+
+%% List all users thats standalone users
+handle_call({list_users, Addr, Port, Dir, AuthPwd}, _From, State)->
+ Reply = api_call(Addr, Port, Dir, list_users, [], AuthPwd, State),
+ {reply, Reply, State};
+
+%% Delete a user
+handle_call({delete_user, Addr, Port, Dir, User, AuthPwd}, _From, State)->
+ Reply = api_call(Addr, Port, Dir, delete_user, [User], AuthPwd, State),
+ {reply, Reply, State};
+
+%% Delete a group
+handle_call({delete_group, Addr, Port, Dir, Group, AuthPwd}, _From, State)->
+ Reply = api_call(Addr, Port, Dir, delete_group, [Group], AuthPwd, State),
+ {reply, Reply, State};
+
+%% List the current groups
+handle_call({list_groups, Addr, Port, Dir, AuthPwd}, _From, State)->
+ Reply = api_call(Addr, Port, Dir, list_groups, [], AuthPwd, State),
+ {reply, Reply, State};
+
+%% List the members of the given group
+handle_call({list_group_members, Addr, Port, Dir, Group, AuthPwd},
+ _From, State)->
+ Reply = api_call(Addr, Port, Dir, list_group_members, [Group],
+ AuthPwd, State),
+ {reply, Reply, State};
+
+
+%% Add password for a directory
+handle_call({add_password, Dir, Password}, _From, State)->
+ Reply = do_add_password(Dir, Password, State),
+ {reply, Reply, State};
+
+
+%% Update the password for a directory
+
+handle_call({update_password, Dir, Old, New},_From,State)->
+ Reply =
+ case getPassword(State, Dir) of
+ OldPwd when binary(OldPwd)->
+ case erlang:md5(Old) of
+ OldPwd ->
+ %% The old password is right =>
+ %% update the password to the new
+ do_update_password(Dir,New,State),
+ ok;
+ _->
+ {error, error_new}
+ end;
+ _->
+ {error, error_old}
+ end,
+ {reply, Reply, State};
+
+handle_call(stop, _From, State)->
+ {stop, normal, State};
+
+handle_call({verbosity,Verbosity},_From,State)->
+ OldVerbosity = put(verbosity,Verbosity),
+ ?vlog("set verbosity: ~p -> ~p",[Verbosity,OldVerbosity]),
+ {reply,OldVerbosity,State}.
+
+handle_info(Info,State)->
+ {noreply,State}.
+
+handle_cast(Request,State)->
+ {noreply,State}.
+
+
+terminate(Reason,State) ->
+ ets:delete(State#state.tab),
+ ok.
+
+
+%% code_change({down, ToVsn}, State, Extra)
+%%
+code_change({down, _}, #state{tab = Tab}, downgrade_to_2_6_0) ->
+ ?vlog("downgrade to 2.6.0", []),
+ {ok, {state, Tab, undefined}};
+
+
+%% code_change(FromVsn, State, Extra)
+%%
+code_change(_, {state, Tab, _}, upgrade_from_2_6_0) ->
+ ?vlog("upgrade from 2.6.0", []),
+ {ok, #state{tab = Tab}}.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The functions that really changes the data in the database %%
+%% of users to different directories %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% API gateway
+
+api_call(Addr, Port, Dir, Func, Args,Password,State) ->
+ case controlPassword(Password,State,Dir) of
+ ok->
+ ConfigName = httpd_util:make_name("httpd_conf",Addr,Port),
+ case ets:match_object(ConfigName, {directory, Dir, '$1'}) of
+ [{directory, Dir, DirData}] ->
+ AuthMod = auth_mod_name(DirData),
+ ?DEBUG("api_call -> call ~p:~p",[AuthMod,Func]),
+ Ret = (catch apply(AuthMod, Func, [DirData|Args])),
+ ?DEBUG("api_call -> Ret: ~p",[ret]),
+ Ret;
+ O ->
+ ?DEBUG("api_call -> O: ~p",[O]),
+ {error, no_such_directory}
+ end;
+ bad_password ->
+ {error,bad_password}
+ end.
+
+controlPassword(Password,State,Dir)when Password=:="DummyPassword"->
+ bad_password;
+
+controlPassword(Password,State,Dir)->
+ case getPassword(State,Dir) of
+ Pwd when binary(Pwd)->
+ case erlang:md5(Password) of
+ Pwd ->
+ ok;
+ _->
+ bad_password
+ end;
+ _ ->
+ bad_password
+ end.
+
+
+getPassword(State,Dir)->
+ case lookup(State#state.tab, Dir) of
+ [{_,Pwd}]->
+ Pwd;
+ _ ->
+ {error,bad_password}
+ end.
+
+do_update_password(Dir, New, State) ->
+ ets:insert(State#state.tab, {Dir, erlang:md5(New)}).
+
+do_add_password(Dir, Password, State) ->
+ case getPassword(State,Dir) of
+ PwdExists when binary(PwdExists) ->
+ {error, dir_protected};
+ {error, _} ->
+ do_update_password(Dir, Password, State)
+ end.
+
+
+auth_mod_name(DirData) ->
+ case httpd_util:key1search(DirData, auth_type, plain) of
+ plain -> mod_auth_plain;
+ mnesia -> mod_auth_mnesia;
+ dets -> mod_auth_dets
+ end.
+
+
+lookup(Db, Key) ->
+ ets:lookup(Db, Key).
+
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd_auth",Addr,Port).
+
+
+call(Name, Req) ->
+ case (catch gen_server:call(Name, Req)) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Reply ->
+ Reply
+ end.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_browser.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_browser.erl
new file mode 100644
index 0000000000..62ffba0e5b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_browser.erl
@@ -0,0 +1,214 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_browser.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+%% ----------------------------------------------------------------------
+%%
+%% Browsers sends a string to the webbserver
+%% to identify themsevles. They are a bit nasty
+%% since the only thing that the specification really
+%% is strict about is that they shall be short
+%% tree axamples:
+%%
+%% Netscape Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)
+%% IE5 Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11)
+%% Lynx Lynx/2.8.3rel.1 libwww-FM/2.142
+%%
+%% ----------------------------------------------------------------------
+
+-module(mod_browser).
+
+%% Remember that the order of the mozilla browsers are
+%% important since some browsers include others to behave
+%% as they were something else
+-define(MOZILLA_BROWSERS,[{opera,"opera"},{msie,"msie"}]).
+
+
+%% If your operatingsystem is not recognized add it to this list.
+-define(OPERATIVE_SYSTEMS,[{win3x,["win16","windows 3","windows 16-bit"]},
+ {win95,["win95","windows 95"]},
+ {win98,["win98", "windows 98"]},
+ {winnt,["winnt", "windows nt"]},
+ {win2k,["nt 5"]},
+ {sunos4,["sunos 4"]},
+ {sunos5,["sunos 5"]},
+ {sun,["sunos"]},
+ {aix,["aix"]},
+ {linux,["linux"]},
+ {sco,["sco","unix_sv"]},
+ {freebsd,["freebsd"]},
+ {bsd,["bsd"]}]).
+
+-define(LYNX,lynx).
+-define(MOZILLA,mozilla).
+-define(EMACS,emacs).
+-define(STAROFFICE,soffice).
+-define(MOSAIC,mosaic).
+-define(NETSCAPE,netscape).
+-define(UNKOWN,unknown).
+
+-include("httpd.hrl").
+
+-export([do/1, test/0, getBrowser/1]).
+
+
+do(Info) ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ {Status_code,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ undefined ->
+ {proceed,[{'user-agent',getBrowser1(Info)}|Info#mod.data]}
+ end.
+
+getBrowser1(Info) ->
+ PHead=Info#mod.parsed_header,
+ case httpd_util:key1search(PHead,"User-Agent") of
+ undefined->
+ undefined;
+ AgentString ->
+ getBrowser(AgentString)
+ end.
+
+getBrowser(AgentString) ->
+ LAgentString = httpd_util:to_lower(AgentString),
+ case regexp:first_match(LAgentString,"^[^ ]*") of
+ {match,Start,Length} ->
+ Browser=lists:sublist(LAgentString,Start,Length),
+ case browserType(Browser) of
+ {mozilla,Vsn} ->
+ {getMozilla(LAgentString,
+ ?MOZILLA_BROWSERS,{?NETSCAPE,Vsn}),
+ operativeSystem(LAgentString)};
+ AnyBrowser ->
+ {AnyBrowser,operativeSystem(LAgentString)}
+ end;
+ nomatch ->
+ browserType(LAgentString)
+ end.
+
+browserType([$l,$y,$n,$x|Version]) ->
+ {?LYNX,browserVersion(Version)};
+browserType([$m,$o,$z,$i,$l,$l,$a|Version]) ->
+ {?MOZILLA,browserVersion(Version)};
+browserType([$e,$m,$a,$c,$s|Version]) ->
+ {?EMACS,browserVersion(Version)};
+browserType([$e,$t,$a,$r,$o,$f,$f,$i,$c,$e|Version]) ->
+ {?STAROFFICE,browserVersion(Version)};
+browserType([$m,$o,$s,$a,$i,$c|Version]) ->
+ {?MOSAIC,browserVersion(Version)};
+browserType(Unknown)->
+ unknown.
+
+
+browserVersion([$/|VsnString]) ->
+ case catch list_to_float(VsnString) of
+ Number when float(Number) ->
+ Number;
+ Whatever ->
+ case string:span(VsnString,"1234567890.") of
+ 0 ->
+ unknown;
+ VLength ->
+ Vsn = string:substr(VsnString,1,VLength),
+ case string:tokens(Vsn,".") of
+ [Number] ->
+ list_to_float(Number++".0");
+ [Major,Minor|_MinorMinor] ->
+ list_to_float(Major++"."++Minor)
+ end
+ end
+ end;
+browserVersion(VsnString) ->
+ browserVersion([$/|VsnString]).
+
+operativeSystem(OpString) ->
+ operativeSystem(OpString, ?OPERATIVE_SYSTEMS).
+
+operativeSystem(OpString,[]) ->
+ unknown;
+operativeSystem(OpString,[{RetVal,RegExps}|Rest]) ->
+ case controlOperativeSystem(OpString,RegExps) of
+ true->
+ RetVal;
+ _ ->
+ operativeSystem(OpString,Rest)
+ end.
+
+controlOperativeSystem(OpString,[]) ->
+ false;
+controlOperativeSystem(OpString,[Regexp|Regexps]) ->
+ case regexp:match(OpString,Regexp) of
+ {match,_,_}->
+ true;
+ nomatch->
+ controlOperativeSystem(OpString,Regexps)
+ end.
+
+
+%% OK this is ugly but thats the only way since
+%% all browsers dont conform to the name/vsn standard
+%% First we check if it is one of the browsers that
+%% not are the default mozillaborwser against the regexp
+%% for the different browsers. if no match it a mozilla
+%% browser i.e opera netscape or internet explorer
+
+getMozilla(AgentString,[],Default) ->
+ Default;
+getMozilla(AgentString,[{Agent,AgentRegExp}|Rest],Default) ->
+ case regexp:match(AgentString,AgentRegExp) of
+ {match,_,_} ->
+ {Agent,getVersion(AgentString,AgentRegExp)};
+ nomatch ->
+ getMozilla(AgentString,Rest,Default)
+ end.
+
+getVersion(AgentString,AgentRegExp) ->
+ case regexp:match(AgentString,AgentRegExp++"[0-9\.\ ]*") of
+ {match,Start,Length} when length(AgentRegExp) < Length ->
+ %% Ok we got the number split it out
+ RealStart=Start+length(AgentRegExp),
+ RealLength=Length-length(AgentRegExp),
+ VsnString=string:substr(AgentString,RealStart,RealLength),
+ case string:strip(VsnString,both,$\ ) of
+ [] ->
+ unknown;
+ Vsn ->
+ case string:tokens(Vsn,".") of
+ [Number]->
+ list_to_float(Number++".0");
+ [Major,Minor|_MinorMinor]->
+ list_to_float(Major++"."++Minor)
+ end
+ end;
+ nomatch ->
+ unknown
+ end.
+
+
+test()->
+ io:format("~n--------------------------------------------------------~n"),
+ Res1=getBrowser("Mozilla/4.75 [en] (X11; U; SunOS 5.8 sun4u)"),
+ io:format("~p",[Res1]),
+ io:format("~n--------------------------------------------------------~n"),
+ io:format("~n--------------------------------------------------------~n"),
+ Res2=getBrowser("Mozilla/4.0 (compatible; MSIE 5.0; SP1B; SunOS 5.8 sun4u; X11)"),
+ io:format("~p",[Res2]),
+ io:format("~n--------------------------------------------------------~n"),
+ io:format("~n--------------------------------------------------------~n"),
+ Res3=getBrowser("Lynx/2.8.3rel.1 libwww-FM/2.142"),
+ io:format("~p",[Res3]),
+ io:format("~n--------------------------------------------------------~n").
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_cgi.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_cgi.erl
new file mode 100644
index 0000000000..d9070b8860
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_cgi.erl
@@ -0,0 +1,694 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_cgi.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_cgi).
+-export([do/1,env/3,status_code/1,load/2]).
+
+%%Exports to the interface for sending chunked data
+%% to http/1.1 users and full responses to http/1.0
+-export([send/5,final_send/4, update_status_code/2,get_new_size/2]).
+-include("httpd.hrl").
+
+-define(VMODULE,"CGI").
+-include("httpd_verbosity.hrl").
+
+-define(GATEWAY_INTERFACE,"CGI/1.1").
+-define(DEFAULT_CGI_TIMEOUT,15000).
+
+%% do
+
+do(Info) ->
+ ?vtrace("do",[]),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode, PhraseArgs, Reason} ->
+ {proceed, Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ ?vtrace("do -> no status code has been generated", []),
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ ?vtrace("do -> no response has been generated", []),
+ RequestURI =
+ case httpd_util:key1search(Info#mod.data,
+ new_request_uri) of
+ undefined ->
+ Info#mod.request_uri;
+ Value ->
+ Value
+ end,
+ ?vtrace("do -> RequestURI: ~p", [RequestURI]),
+ ScriptAliases =
+ httpd_util:multi_lookup(Info#mod.config_db,
+ script_alias),
+ ?vtrace("do -> ScriptAliases: ~p", [ScriptAliases]),
+ case mod_alias:real_script_name(Info#mod.config_db,
+ RequestURI,
+ ScriptAliases) of
+ {Script, AfterScript} ->
+ exec_script(Info, Script, AfterScript, RequestURI);
+ not_a_script ->
+ {proceed,Info#mod.data}
+ end;
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end.
+
+
+%% is_executable(File) ->
+%% ?DEBUG("is_executable -> entry with~n"
+%% " File: ~s",[File]),
+%% Dir = filename:dirname(File),
+%% FileName = filename:basename(File),
+%% is_executable(FileName,Dir).
+%%
+%% is_executable(FileName,Dir) ->
+%% ?DEBUG("is_executable -> entry with~n"
+%% " Dir: ~s~n"
+%% " FileName: ~s",[Dir,FileName]),
+%% case os:find_executable(FileName, Dir) of
+%% false ->
+%% false;
+%% _ ->
+%% true
+%% end.
+
+
+%% -------------------------
+%% Start temporary (hopefully) fix for win32
+%% OTP-3627
+%%
+
+is_executable(File) ->
+ Dir = filename:dirname(File),
+ FileName = filename:basename(File),
+ case os:type() of
+ {win32,_} ->
+ is_win32_executable(Dir,FileName);
+ _ ->
+ is_other_executable(Dir,FileName)
+ end.
+
+
+is_win32_executable(D,F) ->
+ case ends_with(F,[".bat",".exe",".com"]) of
+ false ->
+ %% This is why we cant use 'os:find_executable' directly.
+ %% It assumes that executable files is given without extension
+ case os:find_executable(F,D) of
+ false ->
+ false;
+ _ ->
+ true
+ end;
+ true ->
+ case file:read_file_info(D ++ "/" ++ F) of
+ {ok,_} ->
+ true;
+ _ ->
+ false
+ end
+ end.
+
+
+is_other_executable(D,F) ->
+ case os:find_executable(F,D) of
+ false ->
+ false;
+ _ ->
+ true
+ end.
+
+
+ends_with(File,[]) ->
+ false;
+ends_with(File,[Ext|Rest]) ->
+ case ends_with1(File,Ext) of
+ true ->
+ true;
+ false ->
+ ends_with(File,Rest)
+ end.
+
+ends_with1(S,E) when length(S) >= length(E) ->
+ case to_lower(string:right(S,length(E))) of
+ E ->
+ true;
+ _ ->
+ false
+ end;
+ends_with1(_S,_E) ->
+ false.
+
+
+to_lower(S) -> to_lower(S,[]).
+
+to_lower([],L) -> lists:reverse(L);
+to_lower([H|T],L) -> to_lower(T,[to_lower1(H)|L]).
+
+to_lower1(C) when C >= $A, C =< $Z ->
+ C + ($a - $A);
+to_lower1(C) ->
+ C.
+
+%%
+%% End fix
+%% ---------------------------------
+
+
+env(VarName, Value) ->
+ {VarName, Value}.
+
+env(Info, Script, AfterScript) ->
+ ?vtrace("env -> entry with"
+ "~n Script: ~p"
+ "~n AfterScript: ~p",
+ [Script, AfterScript]),
+ {_, RemoteAddr} = (Info#mod.init_data)#init_data.peername,
+ ServerName = (Info#mod.init_data)#init_data.resolve,
+ PH = parsed_header(Info#mod.parsed_header),
+ Env =
+ [env("SERVER_SOFTWARE",?SERVER_SOFTWARE),
+ env("SERVER_NAME",ServerName),
+ env("GATEWAY_INTERFACE",?GATEWAY_INTERFACE),
+ env("SERVER_PROTOCOL",?SERVER_PROTOCOL),
+ env("SERVER_PORT",
+ integer_to_list(httpd_util:lookup(Info#mod.config_db,port,80))),
+ env("REQUEST_METHOD",Info#mod.method),
+ env("REMOTE_ADDR",RemoteAddr),
+ env("SCRIPT_NAME",Script)],
+ Env1 =
+ case Info#mod.method of
+ "GET" ->
+ case AfterScript of
+ {[], QueryString} ->
+ [env("QUERY_STRING", QueryString)|Env];
+ {PathInfo, []} ->
+ Aliases = httpd_util:multi_lookup(
+ Info#mod.config_db,alias),
+ {_, PathTranslated, _} =
+ mod_alias:real_name(
+ Info#mod.config_db, PathInfo, Aliases),
+ [Env|
+ [env("PATH_INFO","/"++httpd_util:decode_hex(PathInfo)),
+ env("PATH_TRANSLATED",PathTranslated)]];
+ {PathInfo, QueryString} ->
+ Aliases = httpd_util:multi_lookup(
+ Info#mod.config_db,alias),
+ {_, PathTranslated, _} =
+ mod_alias:real_name(
+ Info#mod.config_db, PathInfo, Aliases),
+ [Env|
+ [env("PATH_INFO",
+ httpd_util:decode_hex(PathInfo)),
+ env("PATH_TRANSLATED",PathTranslated),
+ env("QUERY_STRING", QueryString)]];
+ [] ->
+ Env
+ end;
+ "POST" ->
+ [env("CONTENT_LENGTH",
+ integer_to_list(httpd_util:flatlength(
+ Info#mod.entity_body)))|Env];
+ _ ->
+ Env
+ end,
+ Env2 =
+ case httpd_util:key1search(Info#mod.data,remote_user) of
+ undefined ->
+ Env1;
+ RemoteUser ->
+ [env("REMOTE_USER",RemoteUser)|Env1] %% OTP-4416
+ end,
+ lists:flatten([Env2|PH]).
+
+
+parsed_header(List) ->
+ parsed_header(List, []).
+
+parsed_header([], SoFar) ->
+ SoFar;
+parsed_header([{Name,[Value|R1]}|R2], SoFar) when list(Value)->
+ NewName=lists:map(fun(X) -> if X == $- -> $_; true -> X end end,Name),
+ Env = env("HTTP_"++httpd_util:to_upper(NewName),
+ multi_value([Value|R1])),
+ parsed_header(R2, [Env|SoFar]);
+
+parsed_header([{Name,Value}|Rest], SoFar) ->
+ {ok,NewName,_} = regexp:gsub(Name, "-", "_"),
+ Env=env("HTTP_"++httpd_util:to_upper(NewName),Value),
+ parsed_header(Rest, [Env|SoFar]).
+
+
+multi_value([]) ->
+ [];
+multi_value([Value]) ->
+ Value;
+multi_value([Value|Rest]) ->
+ Value++", "++multi_value(Rest).
+
+
+exec_script(Info, Script, AfterScript, RequestURI) ->
+ ?vdebug("exec_script -> entry with"
+ "~n Script: ~p"
+ "~n AfterScript: ~p",
+ [Script,AfterScript]),
+ exec_script(is_executable(Script),Info,Script,AfterScript,RequestURI).
+
+exec_script(true, Info, Script, AfterScript, RequestURI) ->
+ ?vtrace("exec_script -> entry when script is executable",[]),
+ process_flag(trap_exit,true),
+ Dir = filename:dirname(Script),
+ [Script_Name|_] = string:tokens(RequestURI, "?"),
+ Env = env(Info, Script_Name, AfterScript),
+ Port = (catch open_port({spawn,Script},[stream,{cd, Dir},{env, Env}])),
+ ?vtrace("exec_script -> Port: ~w",[Port]),
+ case Port of
+ P when port(P) ->
+ %% Send entity_body to port.
+ Res = case Info#mod.entity_body of
+ [] ->
+ true;
+ EntityBody ->
+ (catch port_command(Port, EntityBody))
+ end,
+ case Res of
+ {'EXIT',Reason} ->
+ ?vlog("port send failed:"
+ "~n Port: ~p"
+ "~n URI: ~p"
+ "~n Reason: ~p",
+ [Port,Info#mod.request_uri,Reason]),
+ exit({open_cmd_failed,Reason,
+ [{mod,?MODULE},{port,Port},
+ {uri,Info#mod.request_uri},
+ {script,Script},{env,Env},{dir,Dir},
+ {ebody_size,sz(Info#mod.entity_body)}]});
+ true ->
+ proxy(Info, Port)
+ end;
+ {'EXIT',Reason} ->
+ ?vlog("open port failed: exit"
+ "~n URI: ~p"
+ "~n Reason: ~p",
+ [Info#mod.request_uri,Reason]),
+ exit({open_port_failed,Reason,
+ [{mod,?MODULE},{uri,Info#mod.request_uri},{script,Script},
+ {env,Env},{dir,Dir}]});
+ O ->
+ ?vlog("open port failed: unknown result"
+ "~n URI: ~p"
+ "~n O: ~p",
+ [Info#mod.request_uri,O]),
+ exit({open_port_failed,O,
+ [{mod,?MODULE},{uri,Info#mod.request_uri},{script,Script},
+ {env,Env},{dir,Dir}]})
+ end;
+
+exec_script(false,Info,Script,_AfterScript,_RequestURI) ->
+ ?vlog("script ~s not executable",[Script]),
+ {proceed,
+ [{status,
+ {404,Info#mod.request_uri,
+ ?NICE("You don't have permission to execute " ++
+ Info#mod.request_uri ++ " on this server")}}|
+ Info#mod.data]}.
+
+
+
+%%
+%% Socket <-> Port communication
+%%
+
+proxy(#mod{config_db = ConfigDb} = Info, Port) ->
+ Timeout = httpd_util:lookup(ConfigDb, cgi_timeout, ?DEFAULT_CGI_TIMEOUT),
+ proxy(Info, Port, 0, undefined,[], Timeout).
+
+proxy(Info, Port, Size, StatusCode, AccResponse, Timeout) ->
+ ?vdebug("proxy -> entry with"
+ "~n Size: ~p"
+ "~n StatusCode ~p"
+ "~n Timeout: ~p",
+ [Size, StatusCode, Timeout]),
+ receive
+ {Port, {data, Response}} when port(Port) ->
+ ?vtrace("proxy -> got some data from the port",[]),
+
+ NewStatusCode = update_status_code(StatusCode, Response),
+
+ ?vtrace("proxy -> NewStatusCode: ~p",[NewStatusCode]),
+ case send(Info, NewStatusCode, Response, Size, AccResponse) of
+ socket_closed ->
+ ?vtrace("proxy -> socket closed: kill port",[]),
+ (catch port_close(Port)), % KILL the port !!!!
+ process_flag(trap_exit,false),
+ {proceed,
+ [{response,{already_sent,200,Size}}|Info#mod.data]};
+
+ head_sent ->
+ ?vtrace("proxy -> head sent: kill port",[]),
+ (catch port_close(Port)), % KILL the port !!!!
+ process_flag(trap_exit,false),
+ {proceed,
+ [{response,{already_sent,200,Size}}|Info#mod.data]};
+
+ {http_response, NewAccResponse} ->
+ ?vtrace("proxy -> head response: continue",[]),
+ NewSize = get_new_size(Size, Response),
+ proxy(Info, Port, NewSize, NewStatusCode,
+ NewAccResponse, Timeout);
+
+ _ ->
+ ?vtrace("proxy -> continue",[]),
+ %% The data is sent and the socket is not closed, continue
+ NewSize = get_new_size(Size, Response),
+ proxy(Info, Port, NewSize, NewStatusCode,
+ "nonempty", Timeout)
+ end;
+
+ {'EXIT', Port, normal} when port(Port) ->
+ ?vtrace("proxy -> exit signal from port: normal",[]),
+ NewStatusCode = update_status_code(StatusCode,AccResponse),
+ final_send(Info,NewStatusCode,Size,AccResponse),
+ process_flag(trap_exit,false),
+ {proceed, [{response,{already_sent,200,Size}}|Info#mod.data]};
+
+ {'EXIT', Port, Reason} when port(Port) ->
+ ?vtrace("proxy -> exit signal from port: ~p",[Reason]),
+ process_flag(trap_exit, false),
+ {proceed, [{status,{400,none,reason(Reason)}}|Info#mod.data]};
+
+ {'EXIT', Pid, Reason} when pid(Pid) ->
+ %% This is the case that a linked process has died,
+ %% It would be nice to response with a server error
+ %% but since the heade alredy is sent
+ ?vtrace("proxy -> exit signal from ~p: ~p",[Pid, Reason]),
+ proxy(Info, Port, Size, StatusCode, AccResponse, Timeout);
+
+ %% This should not happen
+ WhatEver ->
+ ?vinfo("proxy -> received garbage: ~n~p", [WhatEver]),
+ NewStatusCode = update_status_code(StatusCode, AccResponse),
+ final_send(Info, StatusCode, Size, AccResponse),
+ process_flag(trap_exit, false),
+ {proceed, [{response,{already_sent,200,Size}}|Info#mod.data]}
+
+ after Timeout ->
+ ?vlog("proxy -> timeout",[]),
+ (catch port_close(Port)), % KILL the port !!!!
+ httpd_socket:close(Info#mod.socket_type, Info#mod.socket),
+ process_flag(trap_exit,false),
+ {proceed,[{response,{already_sent,200,Size}}|Info#mod.data]}
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The functions that handles the sending of the data to the client %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%----------------------------------------------------------------------
+%% Send the header the first time the size of the body is Zero
+%%----------------------------------------------------------------------
+
+send(#mod{method = "HEAD"} = Info, StatusCode, Response, 0, []) ->
+ first_handle_head_request(Info, StatusCode, Response);
+send(Info, StatusCode, Response, 0, []) ->
+ first_handle_other_request(Info, StatusCode, Response);
+
+%%----------------------------------------------------------------------
+%% The size of the body is bigger than zero =>
+%% we have a part of the body to send
+%%----------------------------------------------------------------------
+send(Info, StatusCode, Response, Size, AccResponse) ->
+ handle_other_request(Info, StatusCode, Response).
+
+
+%%----------------------------------------------------------------------
+%% The function is called the last time when the port has closed
+%%----------------------------------------------------------------------
+
+final_send(Info, StatusCode, Size, AccResponse)->
+ final_handle_other_request(Info, StatusCode).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The code that handles the head requests %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%----------------------------------------------------------------------
+%% The request is a head request if its a HTPT/1.1 request answer to it
+%% otherwise we must collect the size of hte body before we can answer.
+%% Return Values:
+%% head_sent
+%%----------------------------------------------------------------------
+first_handle_head_request(Info, StatusCode, Response)->
+ case Info#mod.http_version of
+ "HTTP/1.1" ->
+ %% Since we have all we need to create the header create it
+ %% send it and return head_sent.
+ case httpd_util:split(Response,"\r\n\r\n|\n\n",2) of
+ {ok, [HeadEnd, Rest]} ->
+ HeadEnd1 = removeStatus(HeadEnd),
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket,
+ [create_header(Info,StatusCode),
+ HeadEnd1,"\r\n\r\n"]);
+ _ ->
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket,
+ [create_header(Info, StatusCode),
+ "Content-Type:text/html\r\n\r\n"])
+ end;
+ _ ->
+ Response1= case regexp:split(Response,"\r\n\r\n|\n\n") of
+ {ok,[HeadEnd|Rest]} ->
+ removeStatus(HeadEnd);
+ _ ->
+ ["Content-Type:text/html"]
+ end,
+ H1 = httpd_util:header(StatusCode,Info#mod.connection),
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket,
+ [H1,Response1,"\r\n\r\n"])
+ end,
+ head_sent.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Handle the requests that is to the other methods %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%----------------------------------------------------------------------
+%% Create the http-response header and send it to the user if it is
+%% a http/1.1 request otherwise we must accumulate it
+%%----------------------------------------------------------------------
+first_handle_other_request(Info,StatusCode,Response)->
+ Header = create_header(Info,StatusCode),
+ Response1 =
+ case httpd_util:split(Response,"\r\n\r\n|\n\n",2) of
+ {ok,[HeadPart,[]]} ->
+ [Header, removeStatus(HeadPart),"\r\n\r\n"];
+
+ {ok,[HeadPart,BodyPart]} ->
+ [Header, removeStatus(HeadPart), "\r\n\r\n",
+ httpd_util:integer_to_hexlist(length(BodyPart)),
+ "\r\n", BodyPart];
+ _WhatEver ->
+ %% No response header field from the cgi-script,
+ %% Just a body
+ [Header, "Content-Type:text/html","\r\n\r\n",
+ httpd_util:integer_to_hexlist(length(Response)),
+ "\r\n", Response]
+ end,
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket, Response1).
+
+
+handle_other_request(#mod{http_version = "HTTP/1.1",
+ socket_type = Type, socket = Sock} = Info,
+ StatusCode, Response0) ->
+ Response = create_chunk(Info, Response0),
+ httpd_socket:deliver(Type, Sock, Response);
+handle_other_request(#mod{socket_type = Type, socket = Sock} = Info,
+ StatusCode, Response) ->
+ httpd_socket:deliver(Type, Sock, Response).
+
+
+final_handle_other_request(#mod{http_version = "HTTP/1.1",
+ socket_type = Type, socket = Sock},
+ StatusCode) ->
+ httpd_socket:deliver(Type, Sock, "0\r\n");
+final_handle_other_request(#mod{socket_type = Type, socket = Sock},
+ StatusCode) ->
+ httpd_socket:close(Type, Sock),
+ socket_closed.
+
+
+create_chunk(_Info, Response) ->
+ HEXSize = httpd_util:integer_to_hexlist(length(lists:flatten(Response))),
+ HEXSize++"\r\n"++Response++"\r\n".
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The various helper functions %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+update_status_code(undefined, Response) ->
+ case status_code(Response) of
+ {ok, StatusCode1} ->
+ StatusCode1;
+ _ ->
+ ?vlog("invalid response from script:~n~p", [Response]),
+ 500
+ end;
+update_status_code(StatusCode,_Response)->
+ StatusCode.
+
+
+get_new_size(0,Response)->
+ case httpd_util:split(Response,"\r\n\r\n|\n\n",2) of
+ {ok,[Head,Body]}->
+ length(lists:flatten(Body));
+ _ ->
+ %%No header in the respone
+ length(lists:flatten(Response))
+ end;
+
+get_new_size(Size,Response)->
+ Size+length(lists:flatten(Response)).
+
+%%----------------------------------------------------------------------
+%% Creates the http-header for a response
+%%----------------------------------------------------------------------
+create_header(Info,StatusCode)->
+ Cache=case httpd_util:lookup(Info#mod.config_db,script_nocache,false) of
+ true->
+ Date=httpd_util:rfc1123_date(),
+ "Cache-Control:no-cache\r\nPragma:no-cache\r\nExpires:"++ Date ++ "\r\n";
+ false ->
+ []
+ end,
+ case Info#mod.http_version of
+ "HTTP/1.1" ->
+ Header=httpd_util:header(StatusCode, Info#mod.connection),
+ Header++"Transfer-encoding:chunked\r\n"++Cache;
+ _ ->
+ httpd_util:header(StatusCode,Info#mod.connection)++Cache
+ end.
+
+
+
+%% status_code
+
+status_code(Response) ->
+ case httpd_util:split(Response,"\n\n|\r\n\r\n",2) of
+ {ok,[Header,Body]} ->
+ case regexp:split(Header,"\n|\r\n") of
+ {ok,HeaderFields} ->
+ {ok,extract_status_code(HeaderFields)};
+ {error,_} ->
+ {error, bad_script_output(Response)}
+ end;
+ _ ->
+ %% No header field in the returned data return 200 the standard code
+ {ok, 200}
+ end.
+
+bad_script_output(Bad) ->
+ lists:flatten(io_lib:format("Bad script output ~s",[Bad])).
+
+
+extract_status_code([]) ->
+ 200;
+extract_status_code([[$L,$o,$c,$a,$t,$i,$o,$n,$:,$ |_]|_]) ->
+ 302;
+extract_status_code([[$S,$t,$a,$t,$u,$s,$:,$ |CodeAndReason]|_]) ->
+ case httpd_util:split(CodeAndReason," ",2) of
+ {ok,[Code,_]} ->
+ list_to_integer(Code);
+ {ok,_} ->
+ 200
+ end;
+extract_status_code([_|Rest]) ->
+ extract_status_code(Rest).
+
+
+sz(B) when binary(B) -> {binary,size(B)};
+sz(L) when list(L) -> {list,length(L)};
+sz(_) -> undefined.
+
+
+%% Convert error to printable string
+%%
+reason({error,emfile}) -> ": To many open files";
+reason({error,{enfile,_}}) -> ": File/port table overflow";
+reason({error,enomem}) -> ": Not enough memory";
+reason({error,eagain}) -> ": No more available OS processes";
+reason(_) -> "".
+
+removeStatus(Head)->
+ case httpd_util:split(Head,"Status:.\r\n",2) of
+ {ok,[HeadPart,HeadEnd]}->
+ HeadPart++HeadEnd;
+ _ ->
+ Head
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% There are 2 config directives for mod_cgi: %%
+%% ScriptNoCache true|false, defines whether the server shall add %%
+%% header fields to stop proxies and %%
+%% clients from saving the page in history %%
+%% or cache %%
+%% %%
+%% ScriptTimeout Seconds, The number of seconds that the server %%
+%% maximum will wait for the script to %%
+%% generate a part of the document %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+load([$S,$c,$r,$i,$p,$t,$N,$o,$C,$a,$c,$h,$e |CacheArg],[])->
+ case catch list_to_atom(httpd_conf:clean(CacheArg)) of
+ true ->
+ {ok, [], {script_nocache,true}};
+ false ->
+ {ok, [], {script_nocache,false}};
+ _ ->
+ {error, ?NICE(httpd_conf:clean(CacheArg)++
+ " is an invalid ScriptNoCache directive")}
+ end;
+
+load([$S,$c,$r,$i,$p,$t,$T,$i,$m,$e,$o,$u,$t,$ |Timeout],[])->
+ case catch list_to_integer(httpd_conf:clean(Timeout)) of
+ TimeoutSec when integer(TimeoutSec) ->
+ {ok, [], {script_timeout,TimeoutSec*1000}};
+ _ ->
+ {error, ?NICE(httpd_conf:clean(Timeout)++
+ " is an invalid ScriptTimeout")}
+ end.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_dir.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_dir.erl
new file mode 100644
index 0000000000..449b088055
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_dir.erl
@@ -0,0 +1,266 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_dir.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_dir).
+-export([do/1]).
+
+-include("httpd.hrl").
+
+%% do
+
+do(Info) ->
+ ?DEBUG("do -> entry",[]),
+ case Info#mod.method of
+ "GET" ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ do_dir(Info);
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end;
+ %% Not a GET method!
+ _ ->
+ {proceed,Info#mod.data}
+ end.
+
+do_dir(Info) ->
+ ?DEBUG("do_dir -> Request URI: ~p",[Info#mod.request_uri]),
+ Path = mod_alias:path(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),
+ DefaultPath = mod_alias:default_index(Info#mod.config_db,Path),
+ %% Is it a directory?
+ case file:read_file_info(DefaultPath) of
+ {ok,FileInfo} when FileInfo#file_info.type == directory ->
+ DecodedRequestURI =
+ httpd_util:decode_hex(Info#mod.request_uri),
+ ?DEBUG("do_dir -> ~n"
+ " Path: ~p~n"
+ " DefaultPath: ~p~n"
+ " DecodedRequestURI: ~p",
+ [Path,DefaultPath,DecodedRequestURI]),
+ case dir(DefaultPath,string:strip(DecodedRequestURI,right,$/),Info#mod.config_db) of
+ {ok, Dir} ->
+ Head=[{content_type,"text/html"},
+ {content_length,integer_to_list(httpd_util:flatlength(Dir))},
+ {date,httpd_util:rfc1123_date(FileInfo#file_info.mtime)},
+ {code,200}],
+ {proceed,[{response,{response,Head,Dir}},
+ {mime_type,"text/html"}|Info#mod.data]};
+ {error, Reason} ->
+ ?ERROR("do_dir -> dir operation failed: ~p",[Reason]),
+ {proceed,
+ [{status,{404,Info#mod.request_uri,Reason}}|
+ Info#mod.data]}
+ end;
+ {ok,FileInfo} ->
+ ?DEBUG("do_dir -> ~n"
+ " Path: ~p~n"
+ " DefaultPath: ~p~n"
+ " FileInfo: ~p",
+ [Path,DefaultPath,FileInfo]),
+ {proceed,Info#mod.data};
+ {error,Reason} ->
+ ?LOG("do_dir -> failed reading file info (~p) for: ~p",
+ [Reason,DefaultPath]),
+ {proceed,
+ [{status,read_file_info_error(Reason,Info,DefaultPath)}|
+ Info#mod.data]}
+ end.
+
+dir(Path,RequestURI,ConfigDB) ->
+ case file:list_dir(Path) of
+ {ok,FileList} ->
+ SortedFileList=lists:sort(FileList),
+ {ok,[header(Path,RequestURI),
+ body(Path,RequestURI,ConfigDB,SortedFileList),
+ footer(Path,SortedFileList)]};
+ {error,Reason} ->
+ {error,?NICE("Can't open directory "++Path++": "++Reason)}
+ end.
+
+%% header
+
+header(Path,RequestURI) ->
+ Header=
+ "<HTML>\n<HEAD>\n<TITLE>Index of "++RequestURI++"</TITLE>\n</HEAD>\n<BODY>\n<H1>Index of "++
+ RequestURI++"</H1>\n<PRE><IMG SRC=\""++icon(blank)++
+ "\" ALT=" "> Name Last modified Size Description
+<HR>\n",
+ case regexp:sub(RequestURI,"[^/]*\$","") of
+ {ok,"/",_} ->
+ Header;
+ {ok,ParentRequestURI,_} ->
+ {ok,ParentPath,_}=regexp:sub(string:strip(Path,right,$/),"[^/]*\$",""),
+ Header++format(ParentPath,ParentRequestURI)
+ end.
+
+format(Path,RequestURI) ->
+ {ok,FileInfo}=file:read_file_info(Path),
+ {{Year,Month,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
+ io_lib:format("<IMG SRC=\"~s\" ALT=\"[~s]\"> <A HREF=\"~s\">Parent directory</A> ~2.2.0w-~s-~w ~2.2.0w:~2.2.0w -\n",
+ [icon(back),"DIR",RequestURI,Day,
+ httpd_util:month(Month),Year,Hour,Minute]).
+
+%% body
+
+body(Path,RequestURI,ConfigDB,[]) ->
+ [];
+body(Path,RequestURI,ConfigDB,[Entry|Rest]) ->
+ [format(Path,RequestURI,ConfigDB,Entry)|body(Path,RequestURI,ConfigDB,Rest)].
+
+format(Path,RequestURI,ConfigDB,Entry) ->
+ case file:read_file_info(Path++"/"++Entry) of
+ {ok,FileInfo} when FileInfo#file_info.type == directory ->
+ {{Year,Month,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
+ EntryLength=length(Entry),
+ if
+ EntryLength > 21 ->
+ io_lib:format("<IMG SRC=\"~s\" ALT=\"[~s]\"> <A HREF=\"~s\">~-21.s..</A>~2.2.0w-~s-~w ~2.2.0w:~2.2.0w -\n",
+ [icon(folder),"DIR",RequestURI++"/"++Entry++"/",Entry,
+ Day,httpd_util:month(Month),Year,Hour,Minute]);
+ true ->
+ io_lib:format("<IMG SRC=\"~s\" ALT=\"[~s]\"> <A HREF=\"~s\">~s</A>~*.*c~2.2.0w-~s-~w ~2.2.0w:~2.2.0w -\n",
+ [icon(folder),"DIR",RequestURI++"/"++Entry++"/",Entry,
+ 23-EntryLength,23-EntryLength,$ ,Day,
+ httpd_util:month(Month),Year,Hour,Minute])
+ end;
+ {ok,FileInfo} ->
+ {{Year,Month,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
+ Suffix=httpd_util:suffix(Entry),
+ MimeType=httpd_util:lookup_mime(ConfigDB,Suffix,""),
+ EntryLength=length(Entry),
+ if
+ EntryLength > 21 ->
+ io_lib:format("<IMG SRC=\"~s\" ALT=\"[~s]\"> <A HREF=\"~s\">~-21.s..</A>~2.2.0w-~s-~w ~2.2.0w:~2.2.0w~8wk ~s\n",
+ [icon(Suffix,MimeType),Suffix,RequestURI++"/"++Entry,
+ Entry,Day,httpd_util:month(Month),Year,Hour,Minute,
+ trunc(FileInfo#file_info.size/1024+1),MimeType]);
+ true ->
+ io_lib:format("<IMG SRC=\"~s\" ALT=\"[~s]\"> <A HREF=\"~s\">~s</A>~*.*c~2.2.0w-~s-~w ~2.2.0w:~2.2.0w~8wk ~s\n",
+ [icon(Suffix,MimeType),Suffix,RequestURI++"/"++Entry,
+ Entry,23-EntryLength,23-EntryLength,$ ,Day,
+ httpd_util:month(Month),Year,Hour,Minute,
+ trunc(FileInfo#file_info.size/1024+1),MimeType])
+ end;
+ {error,Reason} ->
+ ""
+ end.
+
+%% footer
+
+footer(Path,FileList) ->
+ case lists:member("README",FileList) of
+ true ->
+ {ok,Body}=file:read_file(Path++"/README"),
+ "</PRE>\n<HR>\n<PRE>\n"++binary_to_list(Body)++
+ "\n</PRE>\n</BODY>\n</HTML>\n";
+ false ->
+ "</PRE>\n</BODY>\n</HTML>\n"
+ end.
+
+%%
+%% Icon mappings are hard-wired ala default Apache (Ugly!)
+%%
+
+icon(Suffix,MimeType) ->
+ case icon(Suffix) of
+ undefined ->
+ case MimeType of
+ [$t,$e,$x,$t,$/|_] ->
+ "/icons/text.gif";
+ [$i,$m,$a,$g,$e,$/|_] ->
+ "/icons/image2.gif";
+ [$a,$u,$d,$i,$o,$/|_] ->
+ "/icons/sound2.gif";
+ [$v,$i,$d,$e,$o,$/|_] ->
+ "/icons/movie.gif";
+ _ ->
+ "/icons/unknown.gif"
+ end;
+ Icon ->
+ Icon
+ end.
+
+icon(blank) -> "/icons/blank.gif";
+icon(back) -> "/icons/back.gif";
+icon(folder) -> "/icons/folder.gif";
+icon("bin") -> "/icons/binary.gif";
+icon("exe") -> "/icons/binary.gif";
+icon("hqx") -> "/icons/binhex.gif";
+icon("tar") -> "/icons/tar.gif";
+icon("wrl") -> "/icons/world2.gif";
+icon("wrl.gz") -> "/icons/world2.gif";
+icon("vrml") -> "/icons/world2.gif";
+icon("vrm") -> "/icons/world2.gif";
+icon("iv") -> "/icons/world2.gif";
+icon("Z") -> "/icons/compressed.gif";
+icon("z") -> "/icons/compressed.gif";
+icon("tgz") -> "/icons/compressed.gif";
+icon("gz") -> "/icons/compressed.gif";
+icon("zip") -> "/icons/compressed.gif";
+icon("ps") -> "/icons/a.gif";
+icon("ai") -> "/icons/a.gif";
+icon("eps") -> "/icons/a.gif";
+icon("html") -> "/icons/layout.gif";
+icon("shtml") -> "/icons/layout.gif";
+icon("htm") -> "/icons/layout.gif";
+icon("pdf") -> "/icons/layout.gif";
+icon("txt") -> "/icons/text.gif";
+icon("erl") -> "/icons/burst.gif";
+icon("c") -> "/icons/c.gif";
+icon("pl") -> "/icons/p.gif";
+icon("py") -> "/icons/p.gif";
+icon("for") -> "/icons/f.gif";
+icon("dvi") -> "/icons/dvi.gif";
+icon("uu") -> "/icons/uuencoded.gif";
+icon("conf") -> "/icons/script.gif";
+icon("sh") -> "/icons/script.gif";
+icon("shar") -> "/icons/script.gif";
+icon("csh") -> "/icons/script.gif";
+icon("ksh") -> "/icons/script.gif";
+icon("tcl") -> "/icons/script.gif";
+icon("tex") -> "/icons/tex.gif";
+icon("core") -> "/icons/tex.gif";
+icon(_) -> undefined.
+
+
+read_file_info_error(eacces,Info,Path) ->
+ read_file_info_error(403,Info,Path,
+ ": Missing search permissions for one "
+ "of the parent directories");
+read_file_info_error(enoent,Info,Path) ->
+ read_file_info_error(404,Info,Path,"");
+read_file_info_error(enotdir,Info,Path) ->
+ read_file_info_error(404,Info,Path,
+ ": A component of the file name is not a directory");
+read_file_info_error(_,Info,Path) ->
+ read_file_info_error(500,none,Path,"").
+
+read_file_info_error(StatusCode,none,Path,Reason) ->
+ {StatusCode,none,?NICE("Can't access "++Path++Reason)};
+read_file_info_error(StatusCode,Info,Path,Reason) ->
+ {StatusCode,Info#mod.request_uri,
+ ?NICE("Can't access "++Path++Reason)}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_disk_log.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_disk_log.erl
new file mode 100644
index 0000000000..c5d110ee4b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_disk_log.erl
@@ -0,0 +1,405 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_disk_log.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_disk_log).
+-export([do/1,error_log/5,security_log/2,load/2,store/2,remove/1]).
+
+-export([report_error/2]).
+
+-define(VMODULE,"DISK_LOG").
+-include("httpd_verbosity.hrl").
+
+-include("httpd.hrl").
+
+%% do
+
+do(Info) ->
+ AuthUser = auth_user(Info#mod.data),
+ Date = custom_date(),
+ log_internal_info(Info,Date,Info#mod.data),
+ LogFormat = get_log_format(Info#mod.config_db),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ transfer_log(Info, "-", AuthUser, Date, StatusCode, 0, LogFormat),
+ if
+ StatusCode >= 400 ->
+ error_log(Info, Date, Reason, LogFormat);
+ true ->
+ not_an_error
+ end,
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ {already_sent,StatusCode,Size} ->
+ transfer_log(Info, "-", AuthUser, Date, StatusCode,
+ Size, LogFormat),
+ {proceed,Info#mod.data};
+
+ {response, Head, Body} ->
+ Size = httpd_util:key1search(Head, content_length, 0),
+ Code = httpd_util:key1search(Head, code, 200),
+ transfer_log(Info, "-", AuthUser, Date, Code,
+ Size, LogFormat),
+ {proceed,Info#mod.data};
+
+ {StatusCode,Response} ->
+ transfer_log(Info, "-", AuthUser, Date, 200,
+ httpd_util:flatlength(Response), LogFormat),
+ {proceed,Info#mod.data};
+ undefined ->
+ transfer_log(Info, "-", AuthUser, Date, 200,
+ 0, LogFormat),
+ {proceed,Info#mod.data}
+ end
+ end.
+
+custom_date() ->
+ LocalTime = calendar:local_time(),
+ UniversalTime = calendar:universal_time(),
+ Minutes = round(diff_in_minutes(LocalTime,UniversalTime)),
+ {{YYYY,MM,DD},{Hour,Min,Sec}} = LocalTime,
+ Date =
+ io_lib:format("~.2.0w/~.3s/~.4w:~.2.0w:~.2.0w:~.2.0w ~c~.2.0w~.2.0w",
+ [DD,httpd_util:month(MM),YYYY,Hour,Min,Sec,sign(Minutes),
+ abs(Minutes) div 60,abs(Minutes) rem 60]),
+ lists:flatten(Date).
+
+diff_in_minutes(L,U) ->
+ (calendar:datetime_to_gregorian_seconds(L) -
+ calendar:datetime_to_gregorian_seconds(U))/60.
+
+sign(Minutes) when Minutes > 0 ->
+ $+;
+sign(Minutes) ->
+ $-.
+
+auth_user(Data) ->
+ case httpd_util:key1search(Data,remote_user) of
+ undefined ->
+ "-";
+ RemoteUser ->
+ RemoteUser
+ end.
+
+%% log_internal_info
+
+log_internal_info(Info,Date,[]) ->
+ ok;
+log_internal_info(Info,Date,[{internal_info,Reason}|Rest]) ->
+ Format = get_log_format(Info#mod.config_db),
+ error_log(Info,Date,Reason,Format),
+ log_internal_info(Info,Date,Rest);
+log_internal_info(Info,Date,[_|Rest]) ->
+ log_internal_info(Info,Date,Rest).
+
+
+%% transfer_log
+
+transfer_log(Info,RFC931,AuthUser,Date,StatusCode,Bytes,Format) ->
+ case httpd_util:lookup(Info#mod.config_db,transfer_disk_log) of
+ undefined ->
+ no_transfer_log;
+ TransferDiskLog ->
+ {PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
+ Entry = io_lib:format("~s ~s ~s [~s] \"~s\" ~w ~w~n",
+ [RemoteHost,RFC931,AuthUser,Date,
+ Info#mod.request_line,StatusCode,Bytes]),
+ write(TransferDiskLog, Entry, Format)
+ end.
+
+
+%% error_log
+
+error_log(Info, Date, Reason, Format) ->
+ Format=get_log_format(Info#mod.config_db),
+ case httpd_util:lookup(Info#mod.config_db,error_disk_log) of
+ undefined ->
+ no_error_log;
+ ErrorDiskLog ->
+ {PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
+ Entry =
+ io_lib:format("[~s] access to ~s failed for ~s, reason: ~p~n",
+ [Date, Info#mod.request_uri,
+ RemoteHost, Reason]),
+ write(ErrorDiskLog, Entry, Format)
+ end.
+
+error_log(SocketType, Socket, ConfigDB, {PortNumber, RemoteHost}, Reason) ->
+ Format = get_log_format(ConfigDB),
+ case httpd_util:lookup(ConfigDB,error_disk_log) of
+ undefined ->
+ no_error_log;
+ ErrorDiskLog ->
+ Date = custom_date(),
+ Entry =
+ io_lib:format("[~s] server crash for ~s, reason: ~p~n",
+ [Date,RemoteHost,Reason]),
+ write(ErrorDiskLog, Entry, Format),
+ ok
+ end.
+
+
+%% security_log
+
+security_log(ConfigDB, Event) ->
+ Format = get_log_format(ConfigDB),
+ case httpd_util:lookup(ConfigDB,security_disk_log) of
+ undefined ->
+ no_error_log;
+ DiskLog ->
+ Date = custom_date(),
+ Entry = io_lib:format("[~s] ~s ~n", [Date, Event]),
+ write(DiskLog, Entry, Format),
+ ok
+ end.
+
+report_error(ConfigDB, Error) ->
+ Format = get_log_format(ConfigDB),
+ case httpd_util:lookup(ConfigDB, error_disk_log) of
+ undefined ->
+ no_error_log;
+ ErrorDiskLog ->
+ Date = custom_date(),
+ Entry = io_lib:format("[~s] reporting error: ~s",[Date,Error]),
+ write(ErrorDiskLog, Entry, Format),
+ ok
+ end.
+
+%%----------------------------------------------------------------------
+%% Get the current format of the disklog
+%%----------------------------------------------------------------------
+get_log_format(ConfigDB)->
+ httpd_util:lookup(ConfigDB,disk_log_format,external).
+
+
+%%
+%% Configuration
+%%
+
+%% load
+
+load([$T,$r,$a,$n,$s,$f,$e,$r,$D,$i,$s,$k,$L,$o,$g,$S,$i,$z,$e,$ |
+ TransferDiskLogSize],[]) ->
+ case regexp:split(TransferDiskLogSize," ") of
+ {ok,[MaxBytes,MaxFiles]} ->
+ case httpd_conf:make_integer(MaxBytes) of
+ {ok,MaxBytesInteger} ->
+ case httpd_conf:make_integer(MaxFiles) of
+ {ok,MaxFilesInteger} ->
+ {ok,[],{transfer_disk_log_size,
+ {MaxBytesInteger,MaxFilesInteger}}};
+ {error,_} ->
+ {error,
+ ?NICE(httpd_conf:clean(TransferDiskLogSize)++
+ " is an invalid TransferDiskLogSize")}
+ end;
+ {error,_} ->
+ {error,?NICE(httpd_conf:clean(TransferDiskLogSize)++
+ " is an invalid TransferDiskLogSize")}
+ end
+ end;
+load([$T,$r,$a,$n,$s,$f,$e,$r,$D,$i,$s,$k,$L,$o,$g,$ |TransferDiskLog],[]) ->
+ {ok,[],{transfer_disk_log,httpd_conf:clean(TransferDiskLog)}};
+
+load([$E,$r,$r,$o,$r,$D,$i,$s,$k,$L,$o,$g,$S,$i,$z,$e,$ | ErrorDiskLogSize],[]) ->
+ case regexp:split(ErrorDiskLogSize," ") of
+ {ok,[MaxBytes,MaxFiles]} ->
+ case httpd_conf:make_integer(MaxBytes) of
+ {ok,MaxBytesInteger} ->
+ case httpd_conf:make_integer(MaxFiles) of
+ {ok,MaxFilesInteger} ->
+ {ok,[],{error_disk_log_size,
+ {MaxBytesInteger,MaxFilesInteger}}};
+ {error,_} ->
+ {error,?NICE(httpd_conf:clean(ErrorDiskLogSize)++
+ " is an invalid ErrorDiskLogSize")}
+ end;
+ {error,_} ->
+ {error,?NICE(httpd_conf:clean(ErrorDiskLogSize)++
+ " is an invalid ErrorDiskLogSize")}
+ end
+ end;
+load([$E,$r,$r,$o,$r,$D,$i,$s,$k,$L,$o,$g,$ |ErrorDiskLog],[]) ->
+ {ok, [], {error_disk_log, httpd_conf:clean(ErrorDiskLog)}};
+
+load([$S,$e,$c,$u,$r,$i,$t,$y,$D,$i,$s,$k,$L,$o,$g,$S,$i,$z,$e,$ |SecurityDiskLogSize],[]) ->
+ case regexp:split(SecurityDiskLogSize, " ") of
+ {ok, [MaxBytes, MaxFiles]} ->
+ case httpd_conf:make_integer(MaxBytes) of
+ {ok, MaxBytesInteger} ->
+ case httpd_conf:make_integer(MaxFiles) of
+ {ok, MaxFilesInteger} ->
+ {ok, [], {security_disk_log_size,
+ {MaxBytesInteger, MaxFilesInteger}}};
+ {error,_} ->
+ {error, ?NICE(httpd_conf:clean(SecurityDiskLogSize)++
+ " is an invalid SecurityDiskLogSize")}
+ end;
+ {error, _} ->
+ {error, ?NICE(httpd_conf:clean(SecurityDiskLogSize)++
+ " is an invalid SecurityDiskLogSize")}
+ end
+ end;
+load([$S,$e,$c,$u,$r,$i,$t,$y,$D,$i,$s,$k,$L,$o,$g,$ |SecurityDiskLog],[]) ->
+ {ok, [], {security_disk_log, httpd_conf:clean(SecurityDiskLog)}};
+
+load([$D,$i,$s,$k,$L,$o,$g,$F,$o,$r,$m,$a,$t,$ |Format],[]) ->
+ case httpd_conf:clean(Format) of
+ "internal" ->
+ {ok, [], {disk_log_format,internal}};
+ "external" ->
+ {ok, [], {disk_log_format,external}};
+ _Default ->
+ {ok, [], {disk_log_format,external}}
+ end.
+
+%% store
+
+store({transfer_disk_log,TransferDiskLog},ConfigList) ->
+ case create_disk_log(TransferDiskLog, transfer_disk_log_size, ConfigList) of
+ {ok,TransferDB} ->
+ {ok,{transfer_disk_log,TransferDB}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+store({security_disk_log,SecurityDiskLog},ConfigList) ->
+ case create_disk_log(SecurityDiskLog, security_disk_log_size, ConfigList) of
+ {ok,SecurityDB} ->
+ {ok,{security_disk_log,SecurityDB}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+store({error_disk_log,ErrorDiskLog},ConfigList) ->
+ case create_disk_log(ErrorDiskLog, error_disk_log_size, ConfigList) of
+ {ok,ErrorDB} ->
+ {ok,{error_disk_log,ErrorDB}};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Open or creates the disklogs
+%%----------------------------------------------------------------------
+log_size(ConfigList, Tag) ->
+ httpd_util:key1search(ConfigList, Tag, {500*1024,8}).
+
+create_disk_log(LogFile, SizeTag, ConfigList) ->
+ Filename = httpd_conf:clean(LogFile),
+ {MaxBytes, MaxFiles} = log_size(ConfigList, SizeTag),
+ case filename:pathtype(Filename) of
+ absolute ->
+ create_disk_log(Filename, MaxBytes, MaxFiles, ConfigList);
+ volumerelative ->
+ create_disk_log(Filename, MaxBytes, MaxFiles, ConfigList);
+ relative ->
+ case httpd_util:key1search(ConfigList,server_root) of
+ undefined ->
+ {error,
+ ?NICE(Filename++
+ " is an invalid ErrorLog beacuse ServerRoot is not defined")};
+ ServerRoot ->
+ AbsoluteFilename = filename:join(ServerRoot,Filename),
+ create_disk_log(AbsoluteFilename, MaxBytes, MaxFiles,
+ ConfigList)
+ end
+ end.
+
+create_disk_log(Filename, MaxBytes, MaxFiles, ConfigList) ->
+ Format = httpd_util:key1search(ConfigList, disk_log_format, external),
+ open(Filename, MaxBytes, MaxFiles, Format).
+
+
+
+%% remove
+remove(ConfigDB) ->
+ lists:foreach(fun([DiskLog]) -> close(DiskLog) end,
+ ets:match(ConfigDB,{transfer_disk_log,'$1'})),
+ lists:foreach(fun([DiskLog]) -> close(DiskLog) end,
+ ets:match(ConfigDB,{error_disk_log,'$1'})),
+ ok.
+
+
+%%
+%% Some disk_log wrapper functions:
+%%
+
+%%----------------------------------------------------------------------
+%% Function: open/4
+%% Description: Open a disk log file.
+%% Control which format the disk log will be in. The external file
+%% format is used as default since that format was used by older
+%% implementations of inets.
+%%
+%% When the internal disk log format is used, we will do some extra
+%% controls. If the files are valid, try to repair them and if
+%% thats not possible, truncate.
+%%----------------------------------------------------------------------
+
+open(Filename, MaxBytes, MaxFiles, internal) ->
+ Opts = [{format, internal}, {repair, truncate}],
+ open1(Filename, MaxBytes, MaxFiles, Opts);
+open(Filename, MaxBytes, MaxFiles, _) ->
+ Opts = [{format, external}],
+ open1(Filename, MaxBytes, MaxFiles, Opts).
+
+open1(Filename, MaxBytes, MaxFiles, Opts0) ->
+ Opts1 = [{name, Filename}, {file, Filename}, {type, wrap}] ++ Opts0,
+ case open2(Opts1, {MaxBytes, MaxFiles}) of
+ {ok, LogDB} ->
+ {ok, LogDB};
+ {error, Reason} ->
+ ?vlog("failed opening disk log with args:"
+ "~n Filename: ~p"
+ "~n MaxBytes: ~p"
+ "~n MaxFiles: ~p"
+ "~n Opts0: ~p"
+ "~nfor reason:"
+ "~n ~p", [Filename, MaxBytes, MaxFiles, Opts0, Reason]),
+ {error,
+ ?NICE("Can't create " ++ Filename ++
+ lists:flatten(io_lib:format(", ~p",[Reason])))};
+ _ ->
+ {error, ?NICE("Can't create "++Filename)}
+ end.
+
+open2(Opts, Size) ->
+ case disk_log:open(Opts) of
+ {error, {badarg, size}} ->
+ %% File did not exist, add the size option and try again
+ disk_log:open([{size, Size} | Opts]);
+ Else ->
+ Else
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Actually writes the entry to the disk_log. If the log is an
+%% internal disk_log write it with log otherwise with blog.
+%%----------------------------------------------------------------------
+write(Log, Entry, internal) ->
+ disk_log:log(Log, Entry);
+
+write(Log, Entry, _) ->
+ disk_log:blog(Log, Entry).
+
+%% Close the log file
+close(Log) ->
+ disk_log:close(Log).
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_esi.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_esi.erl
new file mode 100644
index 0000000000..d527f36788
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_esi.erl
@@ -0,0 +1,490 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_esi.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_esi).
+-export([do/1,load/2]).
+
+%%Functions provided to help erl scheme alias programmer to
+%%Create dynamic webpages that are sent back to the user during
+%%Generation
+-export([deliver/2]).
+
+
+-include("httpd.hrl").
+
+-define(VMODULE,"ESI").
+-include("httpd_verbosity.hrl").
+
+-define(GATEWAY_INTERFACE,"CGI/1.1").
+-define(DEFAULT_ERL_TIMEOUT,15000).
+%% do
+
+do(Info) ->
+ ?vtrace("do",[]),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ case erl_or_eval(Info#mod.request_uri,
+ Info#mod.config_db) of
+ {eval,CGIBody,Modules} ->
+ eval(Info,Info#mod.method,CGIBody,Modules);
+ {erl,CGIBody,Modules} ->
+ erl(Info,Info#mod.method,CGIBody,Modules);
+ proceed ->
+ {proceed,Info#mod.data}
+ end;
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end.
+
+
+
+%% erl_or_eval
+
+erl_or_eval(RequestURI, ConfigDB) ->
+ case erlp(RequestURI, ConfigDB) of
+ false ->
+ case evalp(RequestURI, ConfigDB) of
+ false ->
+ ?vtrace("neither erl nor eval",[]),
+ proceed;
+ Other ->
+ Other
+ end;
+ Other ->
+ Other
+ end.
+
+erlp(RequestURI, ConfigDB) ->
+ case httpd_util:multi_lookup(ConfigDB, erl_script_alias) of
+ [] ->
+ false;
+ AliasMods ->
+ erlp_find_alias(RequestURI,AliasMods)
+ end.
+
+erlp_find_alias(_RequestURI,[]) ->
+ ?vtrace("erlp_find_alias -> no match",[]),
+ false;
+erlp_find_alias(RequestURI,[{Alias,Modules}|Rest]) ->
+ case regexp:first_match(RequestURI,"^"++Alias++"/") of
+ {match,1,Length} ->
+ ?vtrace("erlp -> match with Length: ~p",[Length]),
+ {erl,string:substr(RequestURI,Length+1),Modules};
+ nomatch ->
+ erlp_find_alias(RequestURI,Rest)
+ end.
+
+evalp(RequestURI, ConfigDB) ->
+ case httpd_util:multi_lookup(ConfigDB, eval_script_alias) of
+ [] ->
+ false;
+ AliasMods ->
+ evalp_find_alias(RequestURI,AliasMods)
+ end.
+
+evalp_find_alias(_RequestURI,[]) ->
+ ?vtrace("evalp_find_alias -> no match",[]),
+ false;
+evalp_find_alias(RequestURI,[{Alias,Modules}|Rest]) ->
+ case regexp:first_match(RequestURI,"^"++Alias++"\\?") of
+ {match, 1, Length} ->
+ ?vtrace("evalp_find_alias -> match with Length: ~p",[Length]),
+ {eval, string:substr(RequestURI,Length+1),Modules};
+ nomatch ->
+ evalp_find_alias(RequestURI,Rest)
+ end.
+
+
+%%
+%% Erl mechanism
+%%
+
+%%This is exactly the same as the GET method the difference is that
+%%The response must not contain any data expect the response header
+
+
+erl(Info,"HEAD",CGIBody,Modules) ->
+ erl(Info,"GET",CGIBody,Modules);
+
+erl(Info,"GET",CGIBody,Modules) ->
+ ?vtrace("erl GET request",[]),
+ case httpd_util:split(CGIBody,":|%3A|/",2) of
+ {ok, [Mod,FuncAndInput]} ->
+ ?vtrace("~n Mod: ~p"
+ "~n FuncAndInput: ~p",[Mod,FuncAndInput]),
+ case httpd_util:split(FuncAndInput,"[\?/]",2) of
+ {ok, [Func,Input]} ->
+ ?vtrace("~n Func: ~p"
+ "~n Input: ~p",[Func,Input]),
+ exec(Info,"GET",CGIBody,Modules,Mod,Func,
+ {input_type(FuncAndInput),Input});
+ {ok, [Func]} ->
+ exec(Info,"GET",CGIBody,Modules,Mod,Func,{no_input,""});
+ {ok, BadRequest} ->
+ {proceed,[{status,{400,none,BadRequest}}|Info#mod.data]}
+ end;
+ {ok, BadRequest} ->
+ ?vlog("erl BAD (GET-) request",[]),
+ {proceed, [{status,{400,none,BadRequest}}|Info#mod.data]}
+ end;
+
+erl(Info, "POST", CGIBody, Modules) ->
+ ?vtrace("erl POST request",[]),
+ case httpd_util:split(CGIBody,":|%3A|/",2) of
+ {ok,[Mod,Func]} ->
+ ?vtrace("~n Mod: ~p"
+ "~n Func: ~p",[Mod,Func]),
+ exec(Info,"POST",CGIBody,Modules,Mod,Func,
+ {entity_body,Info#mod.entity_body});
+ {ok,BadRequest} ->
+ ?vlog("erl BAD (POST-) request",[]),
+ {proceed,[{status,{400,none,BadRequest}}|Info#mod.data]}
+ end.
+
+input_type([]) ->
+ no_input;
+input_type([$/|Rest]) ->
+ path_info;
+input_type([$?|Rest]) ->
+ query_string;
+input_type([First|Rest]) ->
+ input_type(Rest).
+
+
+%% exec
+
+exec(Info,Method,CGIBody,["all"],Mod,Func,{Type,Input}) ->
+ ?vtrace("exec ~s 'all'",[Method]),
+ exec(Info,Method,CGIBody,[Mod],Mod,Func,{Type,Input});
+exec(Info,Method,CGIBody,Modules,Mod,Func,{Type,Input}) ->
+ ?vtrace("exec ~s request with:"
+ "~n Modules: ~p"
+ "~n Mod: ~p"
+ "~n Func: ~p"
+ "~n Type: ~p"
+ "~n Input: ~p",
+ [Method,Modules,Mod,Func,Type,Input]),
+ case lists:member(Mod,Modules) of
+ true ->
+ {_,RemoteAddr}=(Info#mod.init_data)#init_data.peername,
+ ServerName=(Info#mod.init_data)#init_data.resolve,
+ Env=get_environment(Info,ServerName,Method,RemoteAddr,Type,Input),
+ ?vtrace("and now call the module",[]),
+ case try_new_erl_scheme_method(Info,Env,Input,list_to_atom(Mod),list_to_atom(Func)) of
+ {error,not_new_method}->
+ case catch apply(list_to_atom(Mod),list_to_atom(Func),[Env,Input]) of
+ {'EXIT',Reason} ->
+ ?vlog("exit with Reason: ~p",[Reason]),
+ {proceed,[{status,{500,none,Reason}}|Info#mod.data]};
+ Response ->
+ control_response_header(Info,Mod,Func,Response)
+ end;
+ ResponseResult->
+ ResponseResult
+ end;
+ false ->
+ ?vlog("unknown module",[]),
+ {proceed,[{status,{403,Info#mod.request_uri,
+ ?NICE("Client not authorized to evaluate: "++CGIBody)}}|Info#mod.data]}
+ end.
+
+control_response_header(Info,Mod,Func,Response)->
+ case control_response(Response,Info,Mod,Func) of
+ {proceed,[{response,{StatusCode,Response}}|Rest]} ->
+ case httpd_util:lookup(Info#mod.config_db,erl_script_nocache,false) of
+ true ->
+ case httpd_util:split(Response,"\r\n\r\n|\n\n",2) of
+ {ok,[Head,Body]}->
+ Date=httpd_util:rfc1123_date(),
+ Cache="Cache-Control:no-cache\r\nPragma:no-cache\r\nExpires:"++ Date ++ "\r\n",
+ {proceed,[{response,{StatusCode,[Head,"\r\n",Cache,"\r\n",Body]}}|Rest]};
+ _->
+ {proceed,[{response,{StatusCode,Response}}|Rest]}
+ end;
+ WhatEver->
+ {proceed,[{response,{StatusCode,Response}}|Rest]}
+ end;
+ WhatEver->
+ WhatEver
+ end.
+
+control_response(Response,Info,Mod,Func)->
+ ?vdebug("Response: ~n~p",[Response]),
+ case mod_cgi:status_code(lists:flatten(Response)) of
+ {ok,StatusCode} ->
+ {proceed,[{response,{StatusCode,Response}}|Info#mod.data]};
+ {error,Reason} ->
+ {proceed,
+ [{status,{400,none,
+ ?NICE("Error in "++Mod++":"++Func++"/2: "++
+ lists:flatten(io_lib:format("~p",[Reason])))}}|
+ Info#mod.data]}
+ end.
+
+parsed_header([]) ->
+ [];
+parsed_header([{Name,[Value|R1]}|R2]) when list(Value) ->
+ NewName=lists:map(fun(X) -> if X == $- -> $_; true -> X end end,Name),
+ [{list_to_atom("http_"++httpd_util:to_lower(NewName)),
+ multi_value([Value|R1])}|parsed_header(R2)];
+parsed_header([{Name,Value}|Rest]) when list(Value)->
+ {ok,NewName,_}=regexp:gsub(Name,"-","_"),
+ [{list_to_atom("http_"++httpd_util:to_lower(NewName)),Value}|
+ parsed_header(Rest)].
+
+multi_value([]) ->
+ [];
+multi_value([Value]) ->
+ Value;
+multi_value([Value|Rest]) ->
+ Value++", "++multi_value(Rest).
+
+%%
+%% Eval mechanism
+%%
+
+
+eval(Info,"POST",CGIBody,Modules) ->
+ ?vtrace("eval(POST) -> method not supported",[]),
+ {proceed,[{status,{501,{"POST",Info#mod.request_uri,Info#mod.http_version},
+ ?NICE("Eval mechanism doesn't support method POST")}}|
+ Info#mod.data]};
+
+eval(Info,"HEAD",CGIBody,Modules) ->
+ %%The function that sends the data in httpd_response handles HEAD reqest by not
+ %% Sending the body
+ eval(Info,"GET",CGIBody,Modules);
+
+
+eval(Info,"GET",CGIBody,Modules) ->
+ ?vtrace("eval(GET) -> entry when"
+ "~n Modules: ~p",[Modules]),
+ case auth(CGIBody,Modules) of
+ true ->
+ case lib:eval_str(string:concat(CGIBody,". ")) of
+ {error,Reason} ->
+ ?vlog("eval -> error:"
+ "~n Reason: ~p",[Reason]),
+ {proceed,[{status,{500,none,Reason}}|Info#mod.data]};
+ {ok,Response} ->
+ ?vtrace("eval -> ok:"
+ "~n Response: ~p",[Response]),
+ case mod_cgi:status_code(lists:flatten(Response)) of
+ {ok,StatusCode} ->
+ {proceed,[{response,{StatusCode,Response}}|Info#mod.data]};
+ {error,Reason} ->
+ {proceed,[{status,{400,none,Reason}}|Info#mod.data]}
+ end
+ end;
+ false ->
+ ?vlog("eval -> auth failed",[]),
+ {proceed,[{status,
+ {403,Info#mod.request_uri,
+ ?NICE("Client not authorized to evaluate: "++CGIBody)}}|
+ Info#mod.data]}
+ end.
+
+auth(CGIBody,["all"]) ->
+ true;
+auth(CGIBody,Modules) ->
+ case regexp:match(CGIBody,"^[^\:(%3A)]*") of
+ {match,Start,Length} ->
+ lists:member(string:substr(CGIBody,Start,Length),Modules);
+ nomatch ->
+ false
+ end.
+
+%%----------------------------------------------------------------------
+%%Creates the environment list that will be the first arg to the
+%%Functions that is called through the ErlScript Schema
+%%----------------------------------------------------------------------
+
+get_environment(Info,ServerName,Method,RemoteAddr,Type,Input)->
+ Env=[{server_software,?SERVER_SOFTWARE},
+ {server_name,ServerName},
+ {gateway_interface,?GATEWAY_INTERFACE},
+ {server_protocol,?SERVER_PROTOCOL},
+ {server_port,httpd_util:lookup(Info#mod.config_db,port,80)},
+ {request_method,Method},
+ {remote_addr,RemoteAddr},
+ {script_name,Info#mod.request_uri}|
+ parsed_header(Info#mod.parsed_header)],
+ get_environment(Type,Input,Env,Info).
+
+
+get_environment(Type,Input,Env,Info)->
+ Env1=case Type of
+ query_string ->
+ [{query_string,Input}|Env];
+ path_info ->
+ Aliases=httpd_util:multi_lookup(Info#mod.config_db,alias),
+ {_,PathTranslated,_}=mod_alias:real_name(Info#mod.config_db,[$/|Input],Aliases),
+ [{path_info,"/"++httpd_util:decode_hex(Input)},
+ {path_translated,PathTranslated}|Env];
+ entity_body ->
+ [{content_length,httpd_util:flatlength(Input)}|Env];
+ no_input ->
+ Env
+ end,
+ get_environment(Info,Env1).
+
+get_environment(Info,Env)->
+ case httpd_util:key1search(Info#mod.data,remote_user) of
+ undefined ->
+ Env;
+ RemoteUser ->
+ [{remote_user,RemoteUser}|Env]
+ end.
+%%
+%% Configuration
+%%
+
+%% load
+
+load([$E,$r,$l,$S,$c,$r,$i,$p,$t,$A,$l,$i,$a,$s,$ |ErlScriptAlias],[]) ->
+ case regexp:split(ErlScriptAlias," ") of
+ {ok, [ErlName|Modules]} ->
+ {ok, [], {erl_script_alias, {ErlName,Modules}}};
+ {ok, _} ->
+ {error,?NICE(httpd_conf:clean(ErlScriptAlias)++
+ " is an invalid ErlScriptAlias")}
+ end;
+load([$E,$v,$a,$l,$S,$c,$r,$i,$p,$t,$A,$l,$i,$a,$s,$ |EvalScriptAlias],[]) ->
+ case regexp:split(EvalScriptAlias, " ") of
+ {ok, [EvalName|Modules]} ->
+ {ok, [], {eval_script_alias, {EvalName,Modules}}};
+ {ok, _} ->
+ {error, ?NICE(httpd_conf:clean(EvalScriptAlias)++
+ " is an invalid EvalScriptAlias")}
+ end;
+load([$E,$r,$l,$S,$c,$r,$i,$p,$t,$T,$i,$m,$e,$o,$u,$t,$ |Timeout],[])->
+ case catch list_to_integer(httpd_conf:clean(Timeout)) of
+ TimeoutSec when integer(TimeoutSec) ->
+ {ok, [], {erl_script_timeout,TimeoutSec*1000}};
+ _ ->
+ {error, ?NICE(httpd_conf:clean(Timeout)++
+ " is an invalid ErlScriptTimeout")}
+ end;
+load([$E,$r,$l,$S,$c,$r,$i,$p,$t,$N,$o,$C,$a,$c,$h,$e |CacheArg],[])->
+ case catch list_to_atom(httpd_conf:clean(CacheArg)) of
+ true ->
+ {ok, [], {erl_script_nocache,true}};
+ false ->
+ {ok, [], {erl_script_nocache,false}};
+ _ ->
+ {error, ?NICE(httpd_conf:clean(CacheArg)++
+ " is an invalid ErlScriptNoCache directive")}
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Functions below handles the data from the dynamic webpages %%
+%% That sends data back to the user part by part %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%----------------------------------------------------------------------
+%%Deliver is the callback function users can call to deliver back data to the
+%%client
+%%----------------------------------------------------------------------
+
+deliver(SessionID,Data)when pid(SessionID) ->
+ SessionID ! {ok,Data},
+ ok;
+deliver(SessionID,Data) ->
+ {error,bad_sessionID}.
+
+
+%%----------------------------------------------------------------------
+%% The method that tries to execute the new format
+%%----------------------------------------------------------------------
+
+%%It would be nicer to use erlang:function_exported/3 but if the
+%%Module isn't loaded the function says that it is not loaded
+
+
+try_new_erl_scheme_method(Info,Env,Input,Mod,Func)->
+ process_flag(trap_exit,true),
+ Pid=spawn_link(Mod,Func,[self(),Env,Input]),
+ Timeout=httpd_util:lookup(Info#mod.config_db,erl_script_timeout,?DEFAULT_ERL_TIMEOUT),
+ RetVal=receive_response_data(Info,Pid,0,undefined,[],Timeout),
+ process_flag(trap_exit,false),
+ RetVal.
+
+
+%%----------------------------------------------------------------------
+%%The function recieves the data from the process that generates the page
+%%and send the data to the client through the mod_cgi:send function
+%%----------------------------------------------------------------------
+
+receive_response_data(Info,Pid,Size,StatusCode,AccResponse,Timeout) ->
+ ?DEBUG("receive_response_data()-> Script Size: ~p,StatusCode ~p ,Timeout: ~p ~n",[Size,StatusCode,Timeout]),
+ receive
+ {ok, Response} ->
+ NewStatusCode=mod_cgi:update_status_code(StatusCode,Response),
+
+ ?DEBUG("receive_response_data/2 NewStatusCode: ~p~n",[NewStatusCode]),
+ case mod_cgi:send(Info, NewStatusCode,Response, Size,AccResponse) of
+ socket_closed ->
+ (catch exit(Pid,final)),
+ {proceed,[{response,{already_sent,200,Size}}|Info#mod.data]};
+ head_sent->
+ (catch exit(Pid,final)),
+ {proceed,[{response,{already_sent,200,Size}}|Info#mod.data]};
+ _ ->
+ %%The data is sent and the socket is not closed contine
+ NewSize = mod_cgi:get_new_size(Size,Response),
+ receive_response_data(Info,Pid,NewSize,NewStatusCode,"notempty",Timeout)
+ end;
+ {'EXIT', Pid, Reason} when AccResponse==[] ->
+ {error,not_new_method};
+ {'EXIT', Pid, Reason} when pid(Pid) ->
+ NewStatusCode=mod_cgi:update_status_code(StatusCode,AccResponse),
+ mod_cgi:final_send(Info,NewStatusCode,Size,AccResponse),
+ {proceed, [{response,{already_sent,200,Size}}|Info#mod.data]};
+ %% This should not happen!
+ WhatEver ->
+ NewStatusCode=mod_cgi:update_status_code(StatusCode,AccResponse),
+ mod_cgi:final_send(Info,StatusCode,Size,AccResponse),
+ {proceed, [{response,{already_sent,200,Size}}|Info#mod.data]}
+ after
+ Timeout ->
+ (catch exit(Pid,timeout)), % KILL the port !!!!
+ httpd_socket:close(Info#mod.socket_type,Info#mod.socket),
+ {proceed,[{response,{already_sent,200,Size}}|Info#mod.data]}
+ end.
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_get.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_get.erl
new file mode 100644
index 0000000000..02f708f85b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_get.erl
@@ -0,0 +1,179 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_get.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_get).
+-export([do/1]).
+-include("httpd.hrl").
+
+%% do
+
+do(Info) ->
+ ?DEBUG("do -> entry",[]),
+ case Info#mod.method of
+ "GET" ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ do_get(Info);
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end;
+ %% Not a GET method!
+ _ ->
+ {proceed,Info#mod.data}
+ end.
+
+
+do_get(Info) ->
+ ?DEBUG("do_get -> Request URI: ~p",[Info#mod.request_uri]),
+ Path = mod_alias:path(Info#mod.data, Info#mod.config_db,
+ Info#mod.request_uri),
+ {FileInfo, LastModified} =get_modification_date(Path),
+
+ send_response(Info#mod.socket,Info#mod.socket_type,Path,Info,FileInfo,LastModified).
+
+
+%%The common case when no range is specified
+send_response(Socket,SocketType,Path,Info,FileInfo,LastModified)->
+ %% Send the file!
+ %% Find the modification date of the file
+ case file:open(Path,[raw,binary]) of
+ {ok, FileDescriptor} ->
+ ?DEBUG("do_get -> FileDescriptor: ~p",[FileDescriptor]),
+ Suffix = httpd_util:suffix(Path),
+ MimeType = httpd_util:lookup_mime_default(Info#mod.config_db,
+ Suffix,"text/plain"),
+ %FileInfo=file:read_file_info(Path),
+ Date = httpd_util:rfc1123_date(),
+ Size = integer_to_list(FileInfo#file_info.size),
+ Header=case Info#mod.http_version of
+ "HTTP/1.1" ->
+ [httpd_util:header(200, MimeType, Info#mod.connection),
+ "Last-Modified: ", LastModified, "\r\n",
+ "Etag: ",httpd_util:create_etag(FileInfo),"\r\n",
+ "Content-Length: ",Size,"\r\n\r\n"];
+ "HTTP/1.0" ->
+ [httpd_util:header(200, MimeType, Info#mod.connection),
+ "Last-Modified: ", LastModified, "\r\n",
+ "Content-Length: ",Size,"\r\n\r\n"]
+ end,
+
+ send(Info#mod.socket_type, Info#mod.socket,
+ Header, FileDescriptor),
+ file:close(FileDescriptor),
+ {proceed,[{response,{already_sent,200,
+ FileInfo#file_info.size}},
+ {mime_type,MimeType}|Info#mod.data]};
+ {error, Reason} ->
+
+ {proceed,
+ [{status,open_error(Reason,Info,Path)}|Info#mod.data]}
+ end.
+
+%% send
+
+send(SocketType,Socket,Header,FileDescriptor) ->
+ ?DEBUG("send -> send header",[]),
+ case httpd_socket:deliver(SocketType,Socket,Header) of
+ socket_closed ->
+ ?LOG("send -> socket closed while sending header",[]),
+ socket_close;
+ _ ->
+ send_body(SocketType,Socket,FileDescriptor)
+ end.
+
+send_body(SocketType,Socket,FileDescriptor) ->
+ case file:read(FileDescriptor,?FILE_CHUNK_SIZE) of
+ {ok,Binary} ->
+ ?DEBUG("send_body -> send another chunk: ~p",[size(Binary)]),
+ case httpd_socket:deliver(SocketType,Socket,Binary) of
+ socket_closed ->
+ ?LOG("send_body -> socket closed while sending",[]),
+ socket_close;
+ _ ->
+ send_body(SocketType,Socket,FileDescriptor)
+ end;
+ eof ->
+ ?DEBUG("send_body -> done with this file",[]),
+ eof
+ end.
+
+
+%% open_error - Handle file open failure
+%%
+open_error(eacces,Info,Path) ->
+ open_error(403,Info,Path,"");
+open_error(enoent,Info,Path) ->
+ open_error(404,Info,Path,"");
+open_error(enotdir,Info,Path) ->
+ open_error(404,Info,Path,
+ ": A component of the file name is not a directory");
+open_error(emfile,_Info,Path) ->
+ open_error(500,none,Path,": To many open files");
+open_error({enfile,_},_Info,Path) ->
+ open_error(500,none,Path,": File table overflow");
+open_error(_Reason,_Info,Path) ->
+ open_error(500,none,Path,"").
+
+open_error(StatusCode,none,Path,Reason) ->
+ {StatusCode,none,?NICE("Can't open "++Path++Reason)};
+open_error(StatusCode,Info,Path,Reason) ->
+ {StatusCode,Info#mod.request_uri,?NICE("Can't open "++Path++Reason)}.
+
+get_modification_date(Path)->
+ case file:read_file_info(Path) of
+ {ok, FileInfo0} ->
+ {FileInfo0, httpd_util:rfc1123_date(FileInfo0#file_info.mtime)};
+ _ ->
+ {#file_info{},""}
+ end.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_head.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_head.erl
new file mode 100644
index 0000000000..542604e092
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_head.erl
@@ -0,0 +1,89 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_head.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_head).
+-export([do/1]).
+
+-include("httpd.hrl").
+
+%% do
+
+do(Info) ->
+ ?DEBUG("do -> entry",[]),
+ case Info#mod.method of
+ "HEAD" ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ _undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ do_head(Info);
+ %% A response has been sent! Nothing to do about it!
+ {already_sent,StatusCode,Size} ->
+ {proceed,Info#mod.data};
+ %% A response has been generated!
+ {StatusCode,Response} ->
+ {proceed,Info#mod.data}
+ end
+ end;
+ %% Not a HEAD method!
+ _ ->
+ {proceed,Info#mod.data}
+ end.
+
+do_head(Info) ->
+ ?DEBUG("do_head -> Request URI: ~p",[Info#mod.request_uri]),
+ Path = mod_alias:path(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),
+ Suffix = httpd_util:suffix(Path),
+ %% Does the file exists?
+ case file:read_file_info(Path) of
+ {ok,FileInfo} ->
+ MimeType=httpd_util:lookup_mime_default(Info#mod.config_db,Suffix,"text/plain"),
+ Length=io_lib:write(FileInfo#file_info.size),
+ Head=[{content_type,MimeType},{content_length,Length},{code,200}],
+ {proceed,[{response,{response,Head,nobody}}|Info#mod.data]};
+ {error,Reason} ->
+ {proceed,
+ [{status,read_file_info_error(Reason,Info,Path)}|Info#mod.data]}
+ end.
+
+%% read_file_info_error - Handle file info read failure
+%%
+read_file_info_error(eacces,Info,Path) ->
+ read_file_info_error(403,Info,Path,"");
+read_file_info_error(enoent,Info,Path) ->
+ read_file_info_error(404,Info,Path,"");
+read_file_info_error(enotdir,Info,Path) ->
+ read_file_info_error(404,Info,Path,
+ ": A component of the file name is not a directory");
+read_file_info_error(emfile,_Info,Path) ->
+ read_file_info_error(500,none,Path,": To many open files");
+read_file_info_error({enfile,_},_Info,Path) ->
+ read_file_info_error(500,none,Path,": File table overflow");
+read_file_info_error(_Reason,_Info,Path) ->
+ read_file_info_error(500,none,Path,"").
+
+read_file_info_error(StatusCode,none,Path,Reason) ->
+ {StatusCode,none,?NICE("Can't access "++Path++Reason)};
+read_file_info_error(StatusCode,Info,Path,Reason) ->
+ {StatusCode,Info#mod.request_uri,
+ ?NICE("Can't access "++Path++Reason)}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_htaccess.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_htaccess.erl
new file mode 100644
index 0000000000..069e4ad3a9
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_htaccess.erl
@@ -0,0 +1,1150 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_htaccess.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+
+-module(mod_htaccess).
+
+-export([do/1, load/2]).
+-export([debug/0]).
+
+-include("httpd.hrl").
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Public methods that interface the eswapi %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%----------------------------------------------------------------------
+% Public method called by the webbserver to insert the data about
+% Names on accessfiles
+%----------------------------------------------------------------------
+load([$A,$c,$c,$e,$s,$s,$F,$i,$l,$e,$N,$a,$m,$e|FileNames],Context)->
+ CleanFileNames=httpd_conf:clean(FileNames),
+ %%io:format("\n The filenames is:" ++ FileNames ++ "\n"),
+ {ok,[],{access_files,string:tokens(CleanFileNames," ")}}.
+
+
+%----------------------------------------------------------------------
+% Public method that the webbserver calls to control the page
+%----------------------------------------------------------------------
+do(Info)->
+ case httpd_util:key1search(Info#mod.data,status) of
+ {Status_code,PhraseArgs,Reason}->
+ {proceed,Info#mod.data};
+ undefined ->
+ control_path(Info)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The functions that start the control if there is a accessfile %%
+%% and if so controls if the dir is allowed or not %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%----------------------------------------------------------------------
+%Info = record mod as specified in httpd.hrl
+%returns either {proceed,Info#mod.data}
+%{proceed,[{status,403....}|Info#mod.data]}
+%{proceed,[{status,401....}|Info#mod.data]}
+%{proceed,[{status,500....}|Info#mod.data]}
+%----------------------------------------------------------------------
+control_path(Info) ->
+ Path = mod_alias:path(Info#mod.data,
+ Info#mod.config_db,
+ Info#mod.request_uri),
+ case isErlScriptOrNotAccessibleFile(Path,Info) of
+ true->
+ {proceed,Info#mod.data};
+ false->
+ case getHtAccessData(Path,Info)of
+ {ok,public}->
+ %%There was no restrictions on the page continue
+ {proceed,Info#mod.data};
+ {error,Reason} ->
+ %Something got wrong continue or quit??????????????????/
+ {proceed,Info#mod.data};
+ {accessData,AccessData}->
+ controlAllowedMethod(Info,AccessData)
+ end
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% These methods controls that the method the client used in the %%
+%% request is one of the limited %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%----------------------------------------------------------------------
+%Control that if the accessmethod used is in the list of modes to challenge
+%
+%Info is the mod record as specified in httpd.hrl
+%AccessData is an ets table whit the data in the .htaccessfiles
+%----------------------------------------------------------------------
+controlAllowedMethod(Info,AccessData)->
+ case allowedRequestMethod(Info,AccessData) of
+ allow->
+ %%The request didnt use one of the limited methods
+ ets:delete(AccessData),
+ {proceed,Info#mod.data};
+ challenge->
+ authenticateUser(Info,AccessData)
+ end.
+
+%----------------------------------------------------------------------
+%Check the specified access method in the .htaccessfile
+%----------------------------------------------------------------------
+allowedRequestMethod(Info,AccessData)->
+ case ets:lookup(AccessData,limit) of
+ [{limit,all}]->
+ challenge;
+ [{limit,Methods}]->
+ isLimitedRequestMethod(Info,Methods)
+ end.
+
+
+%----------------------------------------------------------------------
+%Check the specified accessmethods in the .htaccesfile against the users
+%accessmethod
+%
+%Info is the record from the do call
+%Methods is a list of the methods specified in the .htaccessfile
+%----------------------------------------------------------------------
+isLimitedRequestMethod(Info,Methods)->
+ case lists:member(Info#mod.method,Methods) of
+ true->
+ challenge;
+ false ->
+ allow
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% These methods controls that the user comes from an allowwed net %%
+%% and if so wheather its a valid user or a challenge shall be %%
+%% generated %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%----------------------------------------------------------------------
+%The first thing to control is that the user is from a network
+%that has access to the page
+%----------------------------------------------------------------------
+authenticateUser(Info,AccessData)->
+ case controlNet(Info,AccessData) of
+ allow->
+ %the network is ok control that it is an allowed user
+ authenticateUser2(Info,AccessData);
+ deny->
+ %The user isnt allowed to access the pages from that network
+ ets:delete(AccessData),
+ {proceed,[{status,{403,Info#mod.request_uri,
+ "Restricted area not allowed from your network"}}|Info#mod.data]}
+ end.
+
+
+%----------------------------------------------------------------------
+%The network the user comes from is allowed to view the resources
+%control whether the user needsto supply a password or not
+%----------------------------------------------------------------------
+authenticateUser2(Info,AccessData)->
+ case ets:lookup(AccessData,require) of
+ [{require,AllowedUsers}]->
+ case ets:lookup(AccessData,auth_name) of
+ [{auth_name,Realm}]->
+ authenticateUser2(Info,AccessData,Realm,AllowedUsers);
+ _NoAuthName->
+ ets:delete(AccessData),
+ {break,[{status,{500,none,
+ ?NICE("mod_htaccess:AuthName directive not specified")}}]}
+ end;
+ [] ->
+ %%No special user is required the network is ok so let
+ %%the user in
+ ets:delete(AccessData),
+ {proceed,Info#mod.data}
+ end.
+
+
+%----------------------------------------------------------------------
+%The user must send a userId and a password to get the resource
+%Control if its already in the http-request
+%if the file with users is bad send an 500 response
+%----------------------------------------------------------------------
+authenticateUser2(Info,AccessData,Realm,AllowedUsers)->
+ case authenticateUser(Info,AccessData,AllowedUsers) of
+ allow ->
+ ets:delete(AccessData),
+ {user,Name,Pwd}=getAuthenticatingDataFromHeader(Info),
+ {proceed, [{remote_user_name,Name}|Info#mod.data]};
+ challenge->
+ ets:delete(AccessData),
+ ReasonPhrase = httpd_util:reason_phrase(401),
+ Message = httpd_util:message(401,none,Info#mod.config_db),
+ {proceed,
+ [{response,
+ {401,
+ ["WWW-Authenticate: Basic realm=\"",Realm,
+ "\"\r\n\r\n","<HTML>\n<HEAD>\n<TITLE>",
+ ReasonPhrase,"</TITLE>\n",
+ "</HEAD>\n<BODY>\n<H1>",ReasonPhrase,
+ "</H1>\n",Message,"\n</BODY>\n</HTML>\n"]}}|
+ Info#mod.data]};
+ deny->
+ ets:delete(AccessData),
+ {break,[{status,{500,none,
+ ?NICE("mod_htaccess:Bad path to user or group file")}}]}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Methods that validate the netwqork the user comes from %%
+%% according to the allowed networks %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%---------------------------------------------------------------------
+%Controls the users networkaddress agains the specifed networks to
+%allow or deny
+%
+%returns either allow or deny
+%----------------------------------------------------------------------
+controlNet(Info,AccessData)->
+ UserNetwork=getUserNetworkAddress(Info),
+ case getAllowDenyOrder(AccessData) of
+ {_deny,[],_allow,[]}->
+ allow;
+ {deny,[],allow,AllowedNetworks}->
+ controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
+ {allow,AllowedNetworks,deny,[]}->
+ controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny);
+
+ {deny,DeniedNetworks,allow,[]}->
+ controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
+ {allow,[],deny,DeniedNetworks}->
+ controlIfAllowed(DeniedNetworks,UserNetwork,allow,deny);
+
+ {deny,DeniedNetworks,allow,AllowedNetworks}->
+ controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork);
+ {allow,AllowedNetworks,deny,DeniedNetworks}->
+ controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)
+ end.
+
+
+%----------------------------------------------------------------------
+%Returns the users IP-Number
+%----------------------------------------------------------------------
+getUserNetworkAddress(Info)->
+ {_Socket,Address}=(Info#mod.init_data)#init_data.peername,
+ Address.
+
+
+%----------------------------------------------------------------------
+%Control the users Ip-number against the ip-numbers in the .htaccessfile
+%----------------------------------------------------------------------
+controlIfAllowed(AllowedNetworks,UserNetwork,IfAllowed,IfDenied)->
+ case AllowedNetworks of
+ [{allow,all}]->
+ IfAllowed;
+ [{deny,all}]->
+ IfDenied;
+ [{deny,Networks}]->
+ memberNetwork(Networks,UserNetwork,IfDenied,IfAllowed);
+ [{allow,Networks}]->
+ memberNetwork(Networks,UserNetwork,IfAllowed,IfDenied);
+ _Error->
+ IfDenied
+ end.
+
+
+%---------------------------------------------------------------------%
+%The Denycontrol isn't neccessary to preform since the allow control %
+%override the deny control %
+%---------------------------------------------------------------------%
+controlDenyAllow(DeniedNetworks,AllowedNetworks,UserNetwork)->
+ case AllowedNetworks of
+ [{allow,all}]->
+ allow;
+ [{allow,Networks}]->
+ case memberNetwork(Networks,UserNetwork) of
+ true->
+ allow;
+ false->
+ deny
+ end
+ end.
+
+
+%----------------------------------------------------------------------%
+%Control that the user is in the allowed list if so control that the %
+%network is in the denied list
+%----------------------------------------------------------------------%
+controlAllowDeny(AllowedNetworks,DeniedNetworks,UserNetwork)->
+ case controlIfAllowed(AllowedNetworks,UserNetwork,allow,deny) of
+ allow->
+ controlIfAllowed(DeniedNetworks,UserNetwork,deny,allow);
+ deny ->
+ deny
+ end.
+
+%----------------------------------------------------------------------
+%Controls if the users Ipnumber is in the list of either denied or
+%allowed networks
+%----------------------------------------------------------------------
+memberNetwork(Networks,UserNetwork,IfTrue,IfFalse)->
+ case memberNetwork(Networks,UserNetwork) of
+ true->
+ IfTrue;
+ false->
+ IfFalse
+ end.
+
+
+%----------------------------------------------------------------------
+%regexp match the users ip-address against the networks in the list of
+%ipadresses or subnet addresses.
+memberNetwork(Networks,UserNetwork)->
+ case lists:filter(fun(Net)->
+ case regexp:match(UserNetwork,
+ formatRegexp(Net)) of
+ {match,1,_}->
+ true;
+ _NotSubNet ->
+ false
+ end
+ end,Networks) of
+ []->
+ false;
+ MemberNetWork ->
+ true
+ end.
+
+
+%----------------------------------------------------------------------
+%Creates a regexp from an ip-number i.e "127.0.0-> "^127[.]0[.]0.*"
+%"127.0.0.-> "^127[.]0[.]0[.].*"
+%----------------------------------------------------------------------
+formatRegexp(Net)->
+ [SubNet1|SubNets]=string:tokens(Net,"."),
+ NetRegexp=lists:foldl(fun(SubNet,Newnet)->
+ Newnet ++ "[.]" ++SubNet
+ end,"^"++SubNet1,SubNets),
+ case string:len(Net)-string:rchr(Net,$.) of
+ 0->
+ NetRegexp++"[.].*";
+ _->
+ NetRegexp++".*"
+ end.
+
+
+%----------------------------------------------------------------------
+%If the user has specified if the allow or deny check shall be preformed
+%first get that order if no order is specified take
+%allow - deny since its harder that deny - allow
+%----------------------------------------------------------------------
+getAllowDenyOrder(AccessData)->
+ case ets:lookup(AccessData,order) of
+ [{order,{deny,allow}}]->
+ {deny,ets:lookup(AccessData,deny),
+ allow,ets:lookup(AccessData,allow)};
+ _DefaultOrder->
+ {allow,ets:lookup(AccessData,allow),
+ deny,ets:lookup(AccessData,deny)}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% The methods that validates the user %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%----------------------------------------------------------------------
+%Control if there is anyu autheticating data in threquest header
+%if so it controls it against the users in the list Allowed Users
+%----------------------------------------------------------------------
+authenticateUser(Info,AccessData,AllowedUsers)->
+ case getAuthenticatingDataFromHeader(Info) of
+ {user,User,PassWord}->
+ authenticateUser(Info,AccessData,AllowedUsers,
+ {user,User,PassWord});
+ {error,nouser}->
+ challenge;
+ {error,BadData}->
+ challenge
+ end.
+
+
+%----------------------------------------------------------------------
+%Returns the Autheticating data in the http-request
+%----------------------------------------------------------------------
+getAuthenticatingDataFromHeader(Info)->
+ PrsedHeader=Info#mod.parsed_header,
+ case httpd_util:key1search(PrsedHeader,"authorization" ) of
+ undefined->
+ {error,nouser};
+ [$B,$a,$s,$i,$c,$\ |EncodedString]->
+ UnCodedString=httpd_util:decode_base64(EncodedString),
+ case httpd_util:split(UnCodedString,":",2) of
+ {ok,[User,PassWord]}->
+ {user,User,PassWord};
+ {error,Error}->
+ {error,Error}
+ end;
+ BadCredentials ->
+ {error,BadCredentials}
+ end.
+
+
+%----------------------------------------------------------------------
+%Returns a list of all members of the allowed groups
+%----------------------------------------------------------------------
+getGroupMembers(Groups,AllowedGroups)->
+ Allowed=lists:foldl(fun({group,Name,Members},AllowedMembers)->
+ case lists:member(Name,AllowedGroups) of
+ true->
+ AllowedMembers++Members;
+ false ->
+ AllowedMembers
+ end
+ end,[],Groups),
+ {ok,Allowed}.
+
+authenticateUser(Info,AccessData,{{users,[]},{groups,Groups}},User)->
+ authenticateUser(Info,AccessData,{groups,Groups},User);
+authenticateUser(Info,AccessData,{{users,Users},{groups,[]}},User)->
+ authenticateUser(Info,AccessData,{users,Users},User);
+
+authenticateUser(Info,AccessData,{{users,Users},{groups,Groups}},User)->
+ AllowUser=authenticateUser(Info,AccessData,{users,Users},User),
+ AllowGroup=authenticateUser(Info,AccessData,{groups,Groups},User),
+ case {AllowGroup,AllowUser} of
+ {_,allow}->
+ allow;
+ {allow,_}->
+ allow;
+ {challenge,_}->
+ challenge;
+ {_,challenge}->
+ challenge;
+ {_deny,_deny}->
+ deny
+ end;
+
+
+%----------------------------------------------------------------------
+%Controls that the user is a member in one of the allowed group
+%----------------------------------------------------------------------
+authenticateUser(Info,AccessData,{groups,AllowedGroups},{user,User,PassWord})->
+ case getUsers(AccessData,group_file) of
+ {group_data,Groups}->
+ case getGroupMembers(Groups,AllowedGroups) of
+ {ok,Members}->
+ authenticateUser(Info,AccessData,{users,Members},
+ {user,User,PassWord});
+ {error,BadData}->
+ deny
+ end;
+ {error,BadData}->
+ deny
+ end;
+
+
+%----------------------------------------------------------------------
+%Control that the user is one of the allowed users and that the passwd is ok
+%----------------------------------------------------------------------
+authenticateUser(Info,AccessData,{users,AllowedUsers},{user,User,PassWord})->
+ case lists:member(User,AllowedUsers) of
+ true->
+ %Get the usernames and passwords from the file
+ case getUsers(AccessData,user_file) of
+ {error,BadData}->
+ deny;
+ {user_data,Users}->
+ %Users is a list of the users in
+ %the userfile [{user,User,Passwd}]
+ checkPassWord(Users,{user,User,PassWord})
+ end;
+ false ->
+ challenge
+ end.
+
+
+%----------------------------------------------------------------------
+%Control that the user User={user,"UserName","PassWd"} is
+%member of the list of Users
+%----------------------------------------------------------------------
+checkPassWord(Users,User)->
+ case lists:member(User,Users) of
+ true->
+ allow;
+ false->
+ challenge
+ end.
+
+
+%----------------------------------------------------------------------
+%Get the users in the specified file
+%UserOrGroup is an atom that specify if its a group file or a user file
+%i.e. group_file or user_file
+%----------------------------------------------------------------------
+getUsers({file,FileName},UserOrGroup)->
+ case file:open(FileName,[read]) of
+ {ok,AccessFileHandle} ->
+ getUsers({stream,AccessFileHandle},[],UserOrGroup);
+ {error,Reason} ->
+ {error,{Reason,FileName}}
+ end;
+
+
+%----------------------------------------------------------------------
+%The method that starts the lokkong for user files
+%----------------------------------------------------------------------
+
+getUsers(AccessData,UserOrGroup)->
+ case ets:lookup(AccessData,UserOrGroup) of
+ [{UserOrGroup,File}]->
+ getUsers({file,File},UserOrGroup);
+ _ ->
+ {error,noUsers}
+ end.
+
+
+%----------------------------------------------------------------------
+%Reads data from the filehandle File to the list FileData and when its
+%reach the end it returns the list in a tuple {user_file|group_file,FileData}
+%----------------------------------------------------------------------
+getUsers({stream,File},FileData,UserOrGroup)->
+ case io:get_line(File,[]) of
+ eof when UserOrGroup==user_file->
+ {user_data,FileData};
+ eof when UserOrGroup ==group_file->
+ {group_data,FileData};
+ Line ->
+ getUsers({stream,File},
+ formatUser(Line,FileData,UserOrGroup),UserOrGroup)
+ end.
+
+
+%----------------------------------------------------------------------
+%If the line is a comment remove it
+%----------------------------------------------------------------------
+formatUser([$#|UserDataComment],FileData,_UserOrgroup)->
+ FileData;
+
+
+%----------------------------------------------------------------------
+%The user name in the file is Username:Passwd\n
+%Remove the newline sign and split the user name in
+%UserName and Password
+%----------------------------------------------------------------------
+formatUser(UserData,FileData,UserOrGroup)->
+ case string:tokens(UserData," \r\n")of
+ [User|Whitespace] when UserOrGroup==user_file->
+ case string:tokens(User,":") of
+ [Name,PassWord]->
+ [{user,Name,PassWord}|FileData];
+ _Error->
+ FileData
+ end;
+ GroupData when UserOrGroup==group_file ->
+ parseGroupData(GroupData,FileData);
+ _Error ->
+ FileData
+ end.
+
+
+%----------------------------------------------------------------------
+%if everything is right GroupData is on the form
+% ["groupName:", "Member1", "Member2", "Member2"
+%----------------------------------------------------------------------
+parseGroupData([GroupName|GroupData],FileData)->
+ [{group,formatGroupName(GroupName),GroupData}|FileData].
+
+
+%----------------------------------------------------------------------
+%the line in the file is GroupName: Member1 Member2 .....MemberN
+%Remove the : from the group name
+%----------------------------------------------------------------------
+formatGroupName(GroupName)->
+ string:strip(GroupName,right,$:).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Functions that parses the accessfiles %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%----------------------------------------------------------------------
+%Control that the asset is a real file and not a request for an virtual
+%asset
+%----------------------------------------------------------------------
+isErlScriptOrNotAccessibleFile(Path,Info)->
+ case file:read_file_info(Path) of
+ {ok,_fileInfo}->
+ false;
+ {error,_Reason} ->
+ true
+ end.
+
+
+%----------------------------------------------------------------------
+%Path=PathToTheRequestedFile=String
+%Innfo=record#mod
+%----------------------------------------------------------------------
+getHtAccessData(Path,Info)->
+ HtAccessFileNames=getHtAccessFileNames(Info),
+ case getData(Path,Info,HtAccessFileNames) of
+ {ok,public}->
+ {ok,public};
+ {accessData,AccessData}->
+ {accessData,AccessData};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+
+%----------------------------------------------------------------------
+%returns the names of the accessfiles
+%----------------------------------------------------------------------
+getHtAccessFileNames(Info)->
+ case httpd_util:lookup(Info#mod.config_db,access_files) of
+ undefined->
+ [".htaccess"];
+ Files->
+ Files
+ end.
+%----------------------------------------------------------------------
+%HtAccessFileNames=["accessfileName1",..."AccessFileName2"]
+%----------------------------------------------------------------------
+getData(Path,Info,HtAccessFileNames)->
+ case regexp:split(Path,"/") of
+ {error,Error}->
+ {error,Error};
+ {ok,SplittedPath}->
+ getData2(HtAccessFileNames,SplittedPath,Info)
+ end.
+
+
+%----------------------------------------------------------------------
+%Add to together the data in the Splittedpath up to the path
+%that is the alias or the document root
+%Since we do not need to control after any accessfiles before here
+%----------------------------------------------------------------------
+getData2(HtAccessFileNames,SplittedPath,Info)->
+ case getRootPath(SplittedPath,Info) of
+ {error,Path}->
+ {error,Path};
+ {ok,StartPath,RestOfSplittedPath} ->
+ getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info)
+ end.
+
+
+%----------------------------------------------------------------------
+%HtAccessFilenames is a list the names the accesssfiles can have
+%Path is the shortest match agains all alias and documentroot
+%rest of splitted path is a list of the parts of the path
+%Info is the mod recod from the server
+%----------------------------------------------------------------------
+getData2(HtAccessFileNames,StartPath,RestOfSplittedPath,Info)->
+ case getHtAccessFiles(HtAccessFileNames,StartPath,RestOfSplittedPath) of
+ []->
+ %No accessfile qiut its a public directory
+ {ok,public};
+ Files ->
+ loadAccessFilesData(Files)
+ end.
+
+
+%----------------------------------------------------------------------
+%Loads the data in the accessFiles specifiied by
+% AccessFiles=["/hoem/public/html/accefile",
+% "/home/public/html/priv/accessfile"]
+%----------------------------------------------------------------------
+loadAccessFilesData(AccessFiles)->
+ loadAccessFilesData(AccessFiles,ets:new(accessData,[])).
+
+
+%----------------------------------------------------------------------
+%Returns the found data
+%----------------------------------------------------------------------
+contextToValues(AccessData)->
+ case ets:lookup(AccessData,context) of
+ [{context,Values}]->
+ ets:delete(AccessData,context),
+ insertContext(AccessData,Values),
+ {accessData,AccessData};
+ _Error->
+ {error,errorInAccessFile}
+ end.
+
+
+insertContext(AccessData,[])->
+ ok;
+
+insertContext(AccessData,[{allow,From}|Values])->
+ insertDenyAllowContext(AccessData,{allow,From}),
+ insertContext(AccessData,Values);
+
+insertContext(AccessData,[{deny,From}|Values])->
+ insertDenyAllowContext(AccessData,{deny,From}),
+ insertContext(AccessData,Values);
+
+insertContext(AccessData,[{require,{GrpOrUsr,Members}}|Values])->
+ case ets:lookup(AccessData,require) of
+ []when GrpOrUsr==users->
+ ets:insert(AccessData,{require,{{users,Members},{groups,[]}}});
+
+ [{require,{{users,Users},{groups,Groups}}}]when GrpOrUsr==users ->
+ ets:insert(AccessData,{require,{{users,Users++Members},
+ {groups,Groups}}});
+ []when GrpOrUsr==groups->
+ ets:insert(AccessData,{require,{{users,[]},{groups,Members}}});
+
+ [{require,{{users,Users},{groups,Groups}}}]when GrpOrUsr==groups ->
+ ets:insert(AccessData,{require,{{users,Users},
+ {groups,Groups++Members}}})
+ end,
+ insertContext(AccessData,Values);
+
+
+
+%%limit and order directive need no transforming they areis just to insert
+insertContext(AccessData,[Elem|Values])->
+ ets:insert(AccessData,Elem),
+ insertContext(AccessData,Values).
+
+
+insertDenyAllowContext(AccessData,{AllowDeny,From})->
+ case From of
+ all->
+ ets:insert(AccessData,{AllowDeny,all});
+ AllowedSubnets->
+ case ets:lookup(AccessData,AllowDeny) of
+ []->
+ ets:insert(AccessData,{AllowDeny,From});
+ [{AllowDeny,all}]->
+ ok;
+ [{AllowDeny,Networks}]->
+ ets:insert(AccessData,{allow,Networks++From})
+ end
+ end.
+
+loadAccessFilesData([],AccessData)->
+ %preform context to limits
+ contextToValues(AccessData),
+ {accessData,AccessData};
+
+%----------------------------------------------------------------------
+%Takes each file in the list and load the data to the ets table
+%AccessData
+%----------------------------------------------------------------------
+loadAccessFilesData([FileName|FileNames],AccessData)->
+ case loadAccessFileData({file,FileName},AccessData) of
+ overRide->
+ loadAccessFilesData(FileNames,AccessData);
+ noOverRide ->
+ {accessData,AccessData};
+ error->
+ ets:delete(AccessData),
+ {error,errorInAccessFile}
+ end.
+
+%----------------------------------------------------------------------
+%opens the filehandle to the specified file
+%----------------------------------------------------------------------
+loadAccessFileData({file,FileName},AccessData)->
+ case file:open(FileName,[read]) of
+ {ok,AccessFileHandle}->
+ loadAccessFileData({stream,AccessFileHandle},AccessData,[]);
+ {error,Reason} ->
+ overRide
+ end.
+
+%----------------------------------------------------------------------
+%%look att each line in the file and add them to the database
+%%When end of file is reached control i overrride is allowed
+%% if so return
+%----------------------------------------------------------------------
+loadAccessFileData({stream,File},AccessData,FileData)->
+ case io:get_line(File,[]) of
+ eof->
+ insertData(AccessData,FileData),
+ case ets:match_object(AccessData,{'_',error}) of
+ []->
+ %Case we got no error control that we can override a
+ %at least some of the values
+ case ets:match_object(AccessData,
+ {allow_over_ride,none}) of
+ []->
+ overRide;
+ _NoOverride->
+ noOverRide
+ end;
+ Errors->
+ error
+ end;
+ Line ->
+ loadAccessFileData({stream,File},AccessData,
+ insertLine(string:strip(Line,left),FileData))
+ end.
+
+%----------------------------------------------------------------------
+%AccessData is a ets table where the previous found data is inserted
+%FileData is a list of the directives in the last parsed file
+%before insertion a control is done that the directive is allowed to
+%override
+%----------------------------------------------------------------------
+insertData(AccessData,{{context,Values},FileData})->
+ insertData(AccessData,[{context,Values}|FileData]);
+
+insertData(AccessData,FileData)->
+ case ets:lookup(AccessData,allow_over_ride) of
+ [{allow_over_ride,all}]->
+ lists:foreach(fun(Elem)->
+ ets:insert(AccessData,Elem)
+ end,FileData);
+ []->
+ lists:foreach(fun(Elem)->
+ ets:insert(AccessData,Elem)
+ end,FileData);
+ [{allow_over_ride,Directives}]when list(Directives)->
+ lists:foreach(fun({Key,Value})->
+ case lists:member(Key,Directives) of
+ true->
+ ok;
+ false ->
+ ets:insert(AccessData,{Key,Value})
+ end
+ end,FileData);
+ [{allow_over_ride,_}]->
+ %Will never appear if the user
+ %aint doing very strang econfig files
+ ok
+ end.
+%----------------------------------------------------------------------
+%Take a line in the accessfile and transform it into a tuple that
+%later can be inserted in to the ets:table
+%----------------------------------------------------------------------
+%%%Here is the alternatives that resides inside the limit context
+
+insertLine([$o,$r,$d,$e,$r|Order],{{context,Values},FileData})->
+ {{context,[{order,getOrder(Order)}|Values]},FileData};
+%%Let the user place a tab in the beginning
+insertLine([$\t,$o,$r,$d,$e,$r|Order],{{context,Values},FileData})->
+ {{context,[{order,getOrder(Order)}|Values]},FileData};
+
+insertLine([$a,$l,$l,$o,$w|Allow],{{context,Values},FileData})->
+ {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData};
+insertLine([$\t,$a,$l,$l,$o,$w|Allow],{{context,Values},FileData})->
+ {{context,[{allow,getAllowDenyData(Allow)}|Values]},FileData};
+
+insertLine([$d,$e,$n,$y|Deny],{{context,Values},FileData})->
+ {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData};
+insertLine([$\t,$d,$e,$n,$y|Deny],{{context,Values},FileData})->
+ {{context,[{deny,getAllowDenyData(Deny)}|Values]},FileData};
+
+
+insertLine([$r,$e,$q,$u,$i,$r,$e|Require],{{context,Values},FileData})->
+ {{context,[{require,getRequireData(Require)}|Values]},FileData};
+insertLine([$\t,$r,$e,$q,$u,$i,$r,$e|Require],{{context,Values},FileData})->
+ {{context,[{require,getRequireData(Require)}|Values]},FileData};
+
+
+insertLine([$<,$/,$L,$i,$m,$i,$t|EndLimit],{Context,FileData})->
+ [Context|FileData];
+
+insertLine([$<,$L,$i,$m,$i,$t|Limit],FileData)->
+ {{context,[{limit,getLimits(Limit)}]}, FileData};
+
+
+
+insertLine([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e,$\ |AuthUserFile],FileData)->
+ [{user_file,string:strip(AuthUserFile,right,$\n)}|FileData];
+
+insertLine([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e,$\ |AuthGroupFile],
+ FileData)->
+ [{group_file,string:strip(AuthGroupFile,right,$\n)}|FileData];
+
+insertLine([$A,$l,$l,$o,$w,$O,$v,$e,$r,$R,$i,$d,$e|AllowOverRide],FileData)->
+ [{allow_over_ride,getAllowOverRideData(AllowOverRide)}
+ |FileData];
+
+insertLine([$A,$u,$t,$h,$N,$a,$m,$e,$\ |AuthName],FileData)->
+ [{auth_name,string:strip(AuthName,right,$\n)}|FileData];
+
+insertLine([$A,$u,$t,$h,$T,$y,$p,$e|AuthType],FileData)->
+ [{auth_type,getAuthorizationType(AuthType)}|FileData];
+
+insertLine(_BadDirectiveOrComment,FileData)->
+ FileData.
+
+%----------------------------------------------------------------------
+%transform the Data specified about override to a form that is ieasier
+%handled later
+%Override data="all"|"md5"|"Directive1 .... DirectioveN"
+%----------------------------------------------------------------------
+
+getAllowOverRideData(OverRideData)->
+ case string:tokens(OverRideData," \r\n") of
+ [[$a,$l,$l]|_]->
+ all;
+ [[$n,$o,$n,$e]|_]->
+ none;
+ Directives ->
+ getOverRideDirectives(Directives)
+ end.
+
+getOverRideDirectives(Directives)->
+ lists:map(fun(Directive)->
+ transformDirective(Directive)
+ end,Directives).
+transformDirective([$A,$u,$t,$h,$U,$s,$e,$r,$F,$i,$l,$e|_])->
+ user_file;
+transformDirective([$A,$u,$t,$h,$G,$r,$o,$u,$p,$F,$i,$l,$e|_]) ->
+ group_file;
+transformDirective([$A,$u,$t,$h,$N,$a,$m,$e|_])->
+ auth_name;
+transformDirective([$A,$u,$t,$h,$T,$y,$p,$e|_])->
+ auth_type;
+transformDirective(_UnAllowedOverRideDirective) ->
+ unallowed.
+%----------------------------------------------------------------------
+%Replace the string that specify which method to use for authentication
+%and replace it with the atom for easier mathing
+%----------------------------------------------------------------------
+getAuthorizationType(AuthType)->
+ [Arg|Crap]=string:tokens(AuthType,"\n\r\ "),
+ case Arg of
+ [$B,$a,$s,$i,$c]->
+ basic;
+ [$M,$D,$5] ->
+ md5;
+ _What ->
+ error
+ end.
+%----------------------------------------------------------------------
+%Returns a list of the specified methods to limit or the atom all
+%----------------------------------------------------------------------
+getLimits(Limits)->
+ case regexp:split(Limits,">")of
+ {ok,[_NoEndOnLimit]}->
+ error;
+ {ok,[Methods|Crap]}->
+ case regexp:split(Methods," ")of
+ {ok,[]}->
+ all;
+ {ok,SplittedMethods}->
+ SplittedMethods;
+ {error,Error}->
+ error
+ end;
+ {error,_Error}->
+ error
+ end.
+
+
+%----------------------------------------------------------------------
+% Transform the order to prefrom deny allow control to a tuple of atoms
+%----------------------------------------------------------------------
+getOrder(Order)->
+ [First|Rest]=lists:map(fun(Part)->
+ list_to_atom(Part)
+ end,string:tokens(Order," \n\r")),
+ case First of
+ deny->
+ {deny,allow};
+ allow->
+ {allow,deny};
+ _Error->
+ error
+ end.
+
+%----------------------------------------------------------------------
+% The string AllowDeny is "from all" or "from Subnet1 Subnet2...SubnetN"
+%----------------------------------------------------------------------
+getAllowDenyData(AllowDeny)->
+ case string:tokens(AllowDeny," \n\r") of
+ [_From|AllowDenyData] when length(AllowDenyData)>=1->
+ case lists:nth(1,AllowDenyData) of
+ [$a,$l,$l]->
+ all;
+ Hosts->
+ AllowDenyData
+ end;
+ Error->
+ errror
+ end.
+%----------------------------------------------------------------------
+% Fix the string that describes who is allowed to se the page
+%----------------------------------------------------------------------
+getRequireData(Require)->
+ [UserOrGroup|UserData]=string:tokens(Require," \n\r"),
+ case UserOrGroup of
+ [$u,$s,$e,$r]->
+ {users,UserData};
+ [$g,$r,$o,$u,$p] ->
+ {groups,UserData};
+ _Whatever ->
+ error
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Methods that collects the searchways to the accessfiles %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%----------------------------------------------------------------------
+% Get the whole path to the different accessfiles
+%----------------------------------------------------------------------
+getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath)->
+ getHtAccessFiles(HtAccessFileNames,Path,RestOfSplittedPath,[]).
+
+getHtAccessFiles(HtAccessFileNames,Path,[[]],HtAccessFiles)->
+ HtAccessFiles ++ accessFilesOfPath(HtAccessFileNames,Path++"/");
+
+getHtAccessFiles(HtAccessFileNames,Path,[],HtAccessFiles)->
+ HtAccessFiles;
+getHtAccessFiles(HtAccessFileNames,Path,[NextDir|RestOfSplittedPath],
+ AccessFiles)->
+ getHtAccessFiles(HtAccessFileNames,Path++"/"++NextDir,RestOfSplittedPath,
+ AccessFiles ++
+ accessFilesOfPath(HtAccessFileNames,Path++"/")).
+
+
+%----------------------------------------------------------------------
+%Control if therer are any accessfies in the path
+%----------------------------------------------------------------------
+accessFilesOfPath(HtAccessFileNames,Path)->
+ lists:foldl(fun(HtAccessFileName,Files)->
+ case file:read_file_info(Path++HtAccessFileName) of
+ {ok,FileInfo}->
+ [Path++HtAccessFileName|Files];
+ {error,_Error} ->
+ Files
+ end
+ end,[],HtAccessFileNames).
+
+
+%----------------------------------------------------------------------
+%Sake the splitted path and joins it up to the documentroot or the alias
+%that match first
+%----------------------------------------------------------------------
+
+getRootPath(SplittedPath,Info)->
+ DocRoot=httpd_util:lookup(Info#mod.config_db,document_root,"/"),
+ PresumtiveRootPath=
+ [DocRoot|lists:map(fun({Alias,RealPath})->
+ RealPath
+ end,
+ httpd_util:multi_lookup(Info#mod.config_db,alias))],
+ getRootPath(PresumtiveRootPath,SplittedPath,Info).
+
+
+getRootPath(PresumtiveRootPath,[[],Splittedpath],Info)->
+ getRootPath(PresumtiveRootPath,["/",Splittedpath],Info);
+
+
+getRootPath(PresumtiveRootPath,[Part,NextPart|SplittedPath],Info)->
+ case lists:member(Part,PresumtiveRootPath)of
+ true->
+ {ok,Part,[NextPart|SplittedPath]};
+ false ->
+ getRootPath(PresumtiveRootPath,
+ [Part++"/"++NextPart|SplittedPath],Info)
+ end;
+
+getRootPath(PresumtiveRootPath,[Part],Info)->
+ case lists:member(Part,PresumtiveRootPath)of
+ true->
+ {ok,Part,[]};
+ false ->
+ {error,Part}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%Debug methods %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%----------------------------------------------------------------------
+% Simulate the webserver by calling do/1 with apropiate parameters
+%----------------------------------------------------------------------
+debug()->
+ Conf=getConfigData(),
+ Uri=getUri(),
+ {_Proceed,Data}=getDataFromAlias(Conf,Uri),
+ Init_data=#init_data{peername={socket,"127.0.0.1"}},
+ ParsedHeader=headerparts(),
+ do(#mod{init_data=Init_data,
+ data=Data,
+ config_db=Conf,
+ request_uri=Uri,
+ parsed_header=ParsedHeader,
+ method="GET"}).
+
+%----------------------------------------------------------------------
+%Add authenticate data to the fake http-request header
+%----------------------------------------------------------------------
+headerparts()->
+ [{"authorization","Basic " ++ httpd_util:encode_base64("lotta:potta")}].
+
+getDataFromAlias(Conf,Uri)->
+ mod_alias:do(#mod{config_db=Conf,request_uri=Uri}).
+
+getUri()->
+ "/appmon/test/test.html".
+
+getConfigData()->
+ Tab=ets:new(test_inets,[bag,public]),
+ ets:insert(Tab,{server_name,"localhost"}),
+ ets:insert(Tab,{bind_addresss,{127,0,0,1}}),
+ ets:insert(Tab,{erl_script_alias,{"/webcover/erl",["webcover"]}}),
+ ets:insert(Tab,{erl_script_alias,{"/erl",["webappmon"]}}),
+ ets:insert(Tab,{com_type,ip_comm}),
+ ets:insert(Tab,{modules,[mod_alias,mod_auth,mod_header]}),
+ ets:insert(Tab,{default_type,"text/plain"}),
+ ets:insert(Tab,{server_root,
+ "/home/gandalf/marting/exjobb/webtool-1.0/priv/root"}),
+ ets:insert(Tab,{port,8888}),
+ ets:insert(Tab,{document_root,
+ "/home/gandalf/marting/exjobb/webtool-1.0/priv/root"}),
+ ets:insert(Tab,
+ {alias,
+ {"/appmon"
+ ,"/home/gandalf/marting/exjobb/webappmon-1.0/priv"}}),
+ ets:insert(Tab,{alias,
+ {"/webcover"
+ ,"/home/gandalf/marting/exjobb/webcover-1.0/priv"}}),
+ ets:insert(Tab,{access_file,[".htaccess","kalle","pelle"]}),
+ Tab.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_include.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_include.erl
new file mode 100644
index 0000000000..c93e0a4f59
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_include.erl
@@ -0,0 +1,726 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_include.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_include).
+-export([do/1,parse/2,config/6,include/6,echo/6,fsize/6,flastmod/6,exec/6]).
+
+-include("httpd.hrl").
+
+-define(VMODULE,"INCLUDE").
+-include("httpd_verbosity.hrl").
+
+%% do
+
+do(Info) ->
+ ?vtrace("do",[]),
+ case Info#mod.method of
+ "GET" ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data, response) of
+ %% No response has been generated!
+ undefined ->
+ do_include(Info);
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end;
+ %% Not a GET method!
+ _ ->
+ {proceed,Info#mod.data}
+ end.
+
+do_include(Info) ->
+ ?vtrace("do_include -> entry with"
+ "~n URI: ~p",[Info#mod.request_uri]),
+ Path = mod_alias:path(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),
+ Suffix = httpd_util:suffix(Path),
+ case httpd_util:lookup_mime_default(Info#mod.config_db,Suffix) of
+ "text/x-server-parsed-html" ->
+ HeaderStart =
+ httpd_util:header(200, "text/html", Info#mod.connection),
+ ?vtrace("do_include -> send ~p", [Path]),
+ case send_in(Info,Path,HeaderStart,file:read_file_info(Path)) of
+ {ok, ErrorLog, Size} ->
+ ?vtrace("do_include -> sent ~w bytes", [Size]),
+ {proceed,[{response,{already_sent,200,Size}},
+ {mime_type,"text/html"}|
+ lists:append(ErrorLog,Info#mod.data)]};
+ {error, Reason} ->
+ ?vlog("send in failed:"
+ "~n Reason: ~p"
+ "~n Path: ~p"
+ "~n Info: ~p",
+ [Reason,Info,Path]),
+ {proceed,
+ [{status,send_error(Reason,Info,Path)}|Info#mod.data]}
+ end;
+ _ -> %% Unknown mime type, ignore
+ {proceed,Info#mod.data}
+ end.
+
+
+%%
+%% config directive
+%%
+
+config(Info, Context, ErrorLog, TagList, ValueList, R) ->
+ case verify_tags("config",[errmsg,timefmt,sizefmt],
+ TagList,ValueList) of
+ ok ->
+ {ok,update_context(TagList,ValueList,Context),ErrorLog,"",R};
+ {error,Reason} ->
+ {ok,Context,[{internal_info,Reason}|ErrorLog],
+ httpd_util:key1search(Context,errmsg,""),R}
+ end.
+
+update_context([],[],Context) ->
+ Context;
+update_context([Tag|R1],[Value|R2],Context) ->
+ update_context(R1,R2,[{Tag,Value}|Context]).
+
+verify_tags(Command,ValidTags,TagList,ValueList) when length(TagList)==length(ValueList) ->
+ verify_tags(Command,ValidTags,TagList);
+verify_tags(Command,ValidTags,TagList,ValueList) ->
+ {error,?NICE(Command++" directive has spurious tags")}.
+
+verify_tags(Command, ValidTags, []) ->
+ ok;
+verify_tags(Command, ValidTags, [Tag|Rest]) ->
+ case lists:member(Tag, ValidTags) of
+ true ->
+ verify_tags(Command, ValidTags, Rest);
+ false ->
+ {error,?NICE(Command++" directive has a spurious tag ("++
+ atom_to_list(Tag)++")")}
+ end.
+
+%%
+%% include directive
+%%
+
+include(Info,Context,ErrorLog,[virtual],[VirtualPath],R) ->
+ Aliases = httpd_util:multi_lookup(Info#mod.config_db,alias),
+ {_, Path, _AfterPath} =
+ mod_alias:real_name(Info#mod.config_db, VirtualPath, Aliases),
+ include(Info,Context,ErrorLog,R,Path);
+include(Info, Context, ErrorLog, [file], [FileName], R) ->
+ Path = file(Info#mod.config_db, Info#mod.request_uri, FileName),
+ include(Info, Context, ErrorLog, R, Path);
+include(Info, Context, ErrorLog, TagList, ValueList, R) ->
+ {ok, Context,
+ [{internal_info,?NICE("include directive has a spurious tag")}|
+ ErrorLog], httpd_util:key1search(Context, errmsg, ""), R}.
+
+include(Info, Context, ErrorLog, R, Path) ->
+ ?DEBUG("include -> read file: ~p",[Path]),
+ case file:read_file(Path) of
+ {ok, Body} ->
+ ?DEBUG("include -> size(Body): ~p",[size(Body)]),
+ {ok, NewContext, NewErrorLog, Result} =
+ parse(Info, binary_to_list(Body), Context, ErrorLog, []),
+ {ok, Context, NewErrorLog, Result, R};
+ {error, Reason} ->
+ {ok, Context,
+ [{internal_info, ?NICE("Can't open "++Path)}|ErrorLog],
+ httpd_util:key1search(Context, errmsg, ""), R}
+ end.
+
+file(ConfigDB, RequestURI, FileName) ->
+ Aliases = httpd_util:multi_lookup(ConfigDB, alias),
+ {_, Path, _AfterPath}
+ = mod_alias:real_name(ConfigDB, RequestURI, Aliases),
+ Pwd = filename:dirname(Path),
+ filename:join(Pwd, FileName).
+
+%%
+%% echo directive
+%%
+
+echo(Info,Context,ErrorLog,[var],["DOCUMENT_NAME"],R) ->
+ {ok,Context,ErrorLog,document_name(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),R};
+echo(Info,Context,ErrorLog,[var],["DOCUMENT_URI"],R) ->
+ {ok,Context,ErrorLog,document_uri(Info#mod.config_db,
+ Info#mod.request_uri),R};
+echo(Info,Context,ErrorLog,[var],["QUERY_STRING_UNESCAPED"],R) ->
+ {ok,Context,ErrorLog,query_string_unescaped(Info#mod.request_uri),R};
+echo(Info,Context,ErrorLog,[var],["DATE_LOCAL"],R) ->
+ {ok,Context,ErrorLog,date_local(),R};
+echo(Info,Context,ErrorLog,[var],["DATE_GMT"],R) ->
+ {ok,Context,ErrorLog,date_gmt(),R};
+echo(Info,Context,ErrorLog,[var],["LAST_MODIFIED"],R) ->
+ {ok,Context,ErrorLog,last_modified(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri),R};
+echo(Info,Context,ErrorLog,TagList,ValueList,R) ->
+ {ok,Context,
+ [{internal_info,?NICE("echo directive has a spurious tag")}|
+ ErrorLog],"(none)",R}.
+
+document_name(Data,ConfigDB,RequestURI) ->
+ Path = mod_alias:path(Data,ConfigDB,RequestURI),
+ case regexp:match(Path,"[^/]*\$") of
+ {match,Start,Length} ->
+ string:substr(Path,Start,Length);
+ nomatch ->
+ "(none)"
+ end.
+
+document_uri(ConfigDB, RequestURI) ->
+ Aliases = httpd_util:multi_lookup(ConfigDB, alias),
+ {Path, AfterPath} =
+ case mod_alias:real_name(ConfigDB, RequestURI, Aliases) of
+ {_, Name, {[], []}} ->
+ {Name, ""};
+ {_, Name, {PathInfo, []}} ->
+ {Name, "/"++PathInfo};
+ {_, Name, {PathInfo, QueryString}} ->
+ {Name, "/"++PathInfo++"?"++QueryString};
+ {_, Name, _} ->
+ {Name, ""};
+ Gurka ->
+ io:format("Gurka: ~p~n", [Gurka])
+ end,
+ VirtualPath = string:substr(RequestURI, 1,
+ length(RequestURI)-length(AfterPath)),
+ {match, Start, Length} = regexp:match(Path,"[^/]*\$"),
+ FileName = string:substr(Path,Start,Length),
+ case regexp:match(VirtualPath, FileName++"\$") of
+ {match, _, _} ->
+ httpd_util:decode_hex(VirtualPath)++AfterPath;
+ nomatch ->
+ string:strip(httpd_util:decode_hex(VirtualPath),right,$/)++
+ "/"++FileName++AfterPath
+ end.
+
+query_string_unescaped(RequestURI) ->
+ case regexp:match(RequestURI,"[\?].*\$") of
+ {match,Start,Length} ->
+ %% Escape all shell-special variables with \
+ escape(string:substr(RequestURI,Start+1,Length-1));
+ nomatch ->
+ "(none)"
+ end.
+
+escape([]) -> [];
+escape([$;|R]) -> [$\\,$;|escape(R)];
+escape([$&|R]) -> [$\\,$&|escape(R)];
+escape([$(|R]) -> [$\\,$(|escape(R)];
+escape([$)|R]) -> [$\\,$)|escape(R)];
+escape([$||R]) -> [$\\,$||escape(R)];
+escape([$^|R]) -> [$\\,$^|escape(R)];
+escape([$<|R]) -> [$\\,$<|escape(R)];
+escape([$>|R]) -> [$\\,$>|escape(R)];
+escape([$\n|R]) -> [$\\,$\n|escape(R)];
+escape([$ |R]) -> [$\\,$ |escape(R)];
+escape([$\t|R]) -> [$\\,$\t|escape(R)];
+escape([C|R]) -> [C|escape(R)].
+
+date_local() ->
+ {{Year,Month,Day},{Hour,Minute,Second}}=calendar:local_time(),
+ %% Time format hard-wired to: "%a %b %e %T %Y" according to strftime(3)
+ io_lib:format("~s ~s ~2w ~2.2.0w:~2.2.0w:~2.2.0w ~w",
+ [httpd_util:day(calendar:day_of_the_week(Year,Month,Day)),
+ httpd_util:month(Month),Day,Hour,Minute,Second,Year]).
+
+date_gmt() ->
+ {{Year,Month,Day},{Hour,Minute,Second}}=calendar:universal_time(),
+ %% Time format hard-wired to: "%a %b %e %T %Z %Y" according to strftime(3)
+ io_lib:format("~s ~s ~2w ~2.2.0w:~2.2.0w:~2.2.0w GMT ~w",
+ [httpd_util:day(calendar:day_of_the_week(Year,Month,Day)),
+ httpd_util:month(Month),Day,Hour,Minute,Second,Year]).
+
+last_modified(Data,ConfigDB,RequestURI) ->
+ {ok,FileInfo}=file:read_file_info(mod_alias:path(Data,ConfigDB,RequestURI)),
+ {{Year,Month,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
+ io_lib:format("~s ~s ~2w ~2.2.0w:~2.2.0w:~2.2.0w ~w",
+ [httpd_util:day(calendar:day_of_the_week(Year,Month,Day)),
+ httpd_util:month(Month),Day,Hour,Minute,Second,Year]).
+
+%%
+%% fsize directive
+%%
+
+fsize(Info,Context,ErrorLog,[virtual],[VirtualPath],R) ->
+ Aliases=httpd_util:multi_lookup(Info#mod.config_db,alias),
+ {_,Path,AfterPath}=
+ mod_alias:real_name(Info#mod.config_db,VirtualPath,Aliases),
+ fsize(Info, Context, ErrorLog, R, Path);
+fsize(Info,Context,ErrorLog,[file],[FileName],R) ->
+ Path=file(Info#mod.config_db,Info#mod.request_uri,FileName),
+ fsize(Info,Context,ErrorLog,R,Path);
+fsize(Info,Context,ErrorLog,TagList,ValueList,R) ->
+ {ok,Context,[{internal_info,?NICE("fsize directive has a spurious tag")}|
+ ErrorLog],httpd_util:key1search(Context,errmsg,""),R}.
+
+fsize(Info,Context,ErrorLog,R,Path) ->
+ case file:read_file_info(Path) of
+ {ok,FileInfo} ->
+ case httpd_util:key1search(Context,sizefmt) of
+ "bytes" ->
+ {ok,Context,ErrorLog,
+ integer_to_list(FileInfo#file_info.size),R};
+ "abbrev" ->
+ Size = integer_to_list(trunc(FileInfo#file_info.size/1024+1))++"k",
+ {ok,Context,ErrorLog,Size,R};
+ Value->
+ {ok,Context,
+ [{internal_info,
+ ?NICE("fsize directive has a spurious tag value ("++
+ Value++")")}|
+ ErrorLog],
+ httpd_util:key1search(Context, errmsg, ""), R}
+ end;
+ {error,Reason} ->
+ {ok,Context,[{internal_info,?NICE("Can't open "++Path)}|ErrorLog],
+ httpd_util:key1search(Context,errmsg,""),R}
+ end.
+
+%%
+%% flastmod directive
+%%
+
+flastmod(Info, Context, ErrorLog, [virtual], [VirtualPath],R) ->
+ Aliases=httpd_util:multi_lookup(Info#mod.config_db,alias),
+ {_,Path,AfterPath}=
+ mod_alias:real_name(Info#mod.config_db,VirtualPath,Aliases),
+ flastmod(Info,Context,ErrorLog,R,Path);
+flastmod(Info, Context, ErrorLog, [file], [FileName], R) ->
+ Path = file(Info#mod.config_db, Info#mod.request_uri, FileName),
+ flastmod(Info, Context, ErrorLog, R, Path);
+flastmod(Info,Context,ErrorLog,TagList,ValueList,R) ->
+ {ok,Context,[{internal_info,?NICE("flastmod directive has a spurious tag")}|
+ ErrorLog],httpd_util:key1search(Context,errmsg,""),R}.
+
+flastmod(Info,Context,ErrorLog,R,File) ->
+ case file:read_file_info(File) of
+ {ok,FileInfo} ->
+ {{Yr,Mon,Day},{Hour,Minute,Second}}=FileInfo#file_info.mtime,
+ Result=
+ io_lib:format("~s ~s ~2w ~w:~w:~w ~w",
+ [httpd_util:day(
+ calendar:day_of_the_week(Yr,Mon, Day)),
+ httpd_util:month(Mon),Day,Hour,Minute,Second, Yr]),
+ {ok,Context,ErrorLog,Result,R};
+ {error,Reason} ->
+ {ok,Context,[{internal_info,?NICE("Can't open "++File)}|ErrorLog],
+ httpd_util:key1search(Context,errmsg,""),R}
+ end.
+
+%%
+%% exec directive
+%%
+
+exec(Info,Context,ErrorLog,[cmd],[Command],R) ->
+ ?vtrace("exec cmd:~n Command: ~p",[Command]),
+ cmd(Info,Context,ErrorLog,R,Command);
+exec(Info,Context,ErrorLog,[cgi],[RequestURI],R) ->
+ ?vtrace("exec cgi:~n RequestURI: ~p",[RequestURI]),
+ cgi(Info,Context,ErrorLog,R,RequestURI);
+exec(Info,Context,ErrorLog,TagList,ValueList,R) ->
+ ?vtrace("exec with spurious tag:"
+ "~n TagList: ~p"
+ "~n ValueList: ~p",
+ [TagList,ValueList]),
+ {ok, Context,
+ [{internal_info,?NICE("exec directive has a spurious tag")}|
+ ErrorLog], httpd_util:key1search(Context,errmsg,""),R}.
+
+%% cmd
+
+cmd(Info, Context, ErrorLog, R, Command) ->
+ process_flag(trap_exit,true),
+ Env = env(Info),
+ Dir = filename:dirname(Command),
+ Port = (catch open_port({spawn,Command},[stream,{cd,Dir},{env,Env}])),
+ case Port of
+ P when port(P) ->
+ {NewErrorLog, Result} = proxy(Port, ErrorLog),
+ {ok, Context, NewErrorLog, Result, R};
+ {'EXIT', Reason} ->
+ ?vlog("open port failed: exit"
+ "~n URI: ~p"
+ "~n Reason: ~p",
+ [Info#mod.request_uri,Reason]),
+ exit({open_port_failed,Reason,
+ [{uri,Info#mod.request_uri},{script,Command},
+ {env,Env},{dir,Dir}]});
+ O ->
+ ?vlog("open port failed: unknown result"
+ "~n URI: ~p"
+ "~n O: ~p",
+ [Info#mod.request_uri,O]),
+ exit({open_port_failed,O,
+ [{uri,Info#mod.request_uri},{script,Command},
+ {env,Env},{dir,Dir}]})
+ end.
+
+env(Info) ->
+ [{"DOCUMENT_NAME",document_name(Info#mod.data,Info#mod.config_db,
+ Info#mod.request_uri)},
+ {"DOCUMENT_URI", document_uri(Info#mod.config_db, Info#mod.request_uri)},
+ {"QUERY_STRING_UNESCAPED", query_string_unescaped(Info#mod.request_uri)},
+ {"DATE_LOCAL", date_local()},
+ {"DATE_GMT", date_gmt()},
+ {"LAST_MODIFIED", last_modified(Info#mod.data, Info#mod.config_db,
+ Info#mod.request_uri)}
+ ].
+
+%% cgi
+
+cgi(Info, Context, ErrorLog, R, RequestURI) ->
+ ScriptAliases = httpd_util:multi_lookup(Info#mod.config_db, script_alias),
+ case mod_alias:real_script_name(Info#mod.config_db, RequestURI,
+ ScriptAliases) of
+ {Script, AfterScript} ->
+ exec_script(Info,Script,AfterScript,ErrorLog,Context,R);
+ not_a_script ->
+ {ok, Context,
+ [{internal_info, ?NICE(RequestURI++" is not a script")}|
+ ErrorLog], httpd_util:key1search(Context, errmsg, ""),R}
+ end.
+
+remove_header([]) ->
+ [];
+remove_header([$\n,$\n|Rest]) ->
+ Rest;
+remove_header([C|Rest]) ->
+ remove_header(Rest).
+
+
+exec_script(Info,Script,AfterScript,ErrorLog,Context,R) ->
+ process_flag(trap_exit,true),
+ Aliases = httpd_util:multi_lookup(Info#mod.config_db, alias),
+ {_, Path, AfterPath} = mod_alias:real_name(Info#mod.config_db,
+ Info#mod.request_uri,
+ Aliases),
+ Env = env(Info)++mod_cgi:env(Info, Path, AfterPath),
+ Dir = filename:dirname(Path),
+ Port = (catch open_port({spawn,Script},[stream,{env, Env},{cd, Dir}])),
+ case Port of
+ P when port(P) ->
+ %% Send entity body to port.
+ Res = case Info#mod.entity_body of
+ [] ->
+ true;
+ EntityBody ->
+ (catch port_command(Port,EntityBody))
+ end,
+ case Res of
+ {'EXIT', Reason} ->
+ ?vlog("port send failed:"
+ "~n Port: ~p"
+ "~n URI: ~p"
+ "~n Reason: ~p",
+ [Port,Info#mod.request_uri,Reason]),
+ exit({open_cmd_failed,Reason,
+ [{mod,?MODULE},{port,Port},
+ {uri,Info#mod.request_uri},
+ {script,Script},{env,Env},{dir,Dir},
+ {ebody_size,sz(Info#mod.entity_body)}]});
+ true ->
+ {NewErrorLog, Result} = proxy(Port, ErrorLog),
+ {ok, Context, NewErrorLog, remove_header(Result), R}
+ end;
+ {'EXIT', Reason} ->
+ ?vlog("open port failed: exit"
+ "~n URI: ~p"
+ "~n Reason: ~p",
+ [Info#mod.request_uri,Reason]),
+ exit({open_port_failed,Reason,
+ [{mod,?MODULE},{uri,Info#mod.request_uri},{script,Script},
+ {env,Env},{dir,Dir}]});
+ O ->
+ ?vlog("open port failed: unknown result"
+ "~n URI: ~p"
+ "~n O: ~p",
+ [Info#mod.request_uri,O]),
+ exit({open_port_failed,O,
+ [{mod,?MODULE},{uri,Info#mod.request_uri},{script,Script},
+ {env,Env},{dir,Dir}]})
+ end.
+
+
+%%
+%% Port communication
+%%
+
+proxy(Port,ErrorLog) ->
+ process_flag(trap_exit, true),
+ proxy(Port, ErrorLog, []).
+
+proxy(Port, ErrorLog, Result) ->
+ receive
+ {Port, {data, Response}} ->
+ proxy(Port, ErrorLog, lists:append(Result,Response));
+ {'EXIT', Port, normal} when port(Port) ->
+ process_flag(trap_exit, false),
+ {ErrorLog, Result};
+ {'EXIT', Port, Reason} when port(Port) ->
+ process_flag(trap_exit, false),
+ {[{internal_info,
+ ?NICE("Scrambled output from CGI-script")}|ErrorLog],
+ Result};
+ {'EXIT', Pid, Reason} when pid(Pid) ->
+ process_flag(trap_exit, false),
+ {'EXIT', Pid, Reason};
+ %% This should not happen!
+ WhatEver ->
+ process_flag(trap_exit, false),
+ {ErrorLog, Result}
+ end.
+
+
+%% ------
+%% Temporary until I figure out a way to fix send_in_chunks
+%% (comments and directives that start in one chunk but end
+%% in another is not handled).
+%%
+
+send_in(Info, Path,Head, {ok,FileInfo}) ->
+ case file:read_file(Path) of
+ {ok, Bin} ->
+ send_in1(Info, binary_to_list(Bin), Head, FileInfo);
+ {error, Reason} ->
+ ?vlog("failed reading file: ~p",[Reason]),
+ {error, {open,Reason}}
+ end;
+send_in(Info,Path,Head,{error,Reason}) ->
+ ?vlog("failed open file: ~p",[Reason]),
+ {error, {open,Reason}}.
+
+send_in1(Info, Data,Head,FileInfo) ->
+ {ok, _Context, Err, ParsedBody} = parse(Info,Data,?DEFAULT_CONTEXT,[],[]),
+ Size = length(ParsedBody),
+ ?vdebug("send_in1 -> Size: ~p",[Size]),
+ Head1 = case Info#mod.http_version of
+ "HTTP/1.1"->
+ Head ++
+ "Content-Length: " ++
+ integer_to_list(Size) ++
+ "\r\nEtag:" ++
+ httpd_util:create_etag(FileInfo,Size) ++"\r\n" ++
+ "Last-Modified: " ++
+ httpd_util:rfc1123_date(FileInfo#file_info.mtime) ++
+ "\r\n\r\n";
+ _->
+ %% i.e http/1.0 and http/0.9
+ Head ++
+ "Content-Length: " ++
+ integer_to_list(Size) ++
+ "\r\nLast-Modified: " ++
+ httpd_util:rfc1123_date(FileInfo#file_info.mtime) ++
+ "\r\n\r\n"
+ end,
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket,
+ [Head1,ParsedBody]),
+ {ok, Err, Size}.
+
+
+
+%%
+%% Addition to "Fuzzy" HTML parser. This is actually a ugly hack to
+%% avoid putting to much data on the heap. To be rewritten...
+%%
+
+% -define(CHUNK_SIZE, 4096).
+
+% send_in_chunks(Info, Path) ->
+% ?DEBUG("send_in_chunks -> Path: ~p",[Path]),
+% case file:open(Path, [read, raw]) of
+% {ok, Stream} ->
+% send_in_chunks(Info, Stream, ?DEFAULT_CONTEXT,[]);
+% {error, Reason} ->
+% ?ERROR("Failed open file: ~p",[Reason]),
+% {error, {open,Reason}}
+% end.
+
+% send_in_chunks(Info, Stream, Context, ErrorLog) ->
+% case file:read(Stream, ?CHUNK_SIZE) of
+% {ok, Data} ->
+% ?DEBUG("send_in_chunks -> read ~p bytes",[length(Data)]),
+% {ok, NewContext, NewErrorLog, ParsedBody}=
+% parse(Info, Data, Context, ErrorLog, []),
+% httpd_socket:deliver(Info#mod.socket_type,
+% Info#mod.socket, ParsedBody),
+% send_in_chunks(Info,Stream,NewContext,NewErrorLog);
+% eof ->
+% {ok, ErrorLog};
+% {error, Reason} ->
+% ?ERROR("Failed read from file: ~p",[Reason]),
+% {error, {read,Reason}}
+% end.
+
+
+%%
+%% "Fuzzy" HTML parser
+%%
+
+parse(Info,Body) ->
+ parse(Info, Body, ?DEFAULT_CONTEXT, [], []).
+
+parse(Info, [], Context, ErrorLog, Result) ->
+ {ok, Context, lists:reverse(ErrorLog), lists:reverse(Result)};
+parse(Info,[$<,$!,$-,$-,$#|R1],Context,ErrorLog,Result) ->
+ ?DEBUG("parse -> start command directive when length(R1): ~p",[length(R1)]),
+ case catch parse0(R1,Context) of
+ {parse_error,Reason} ->
+ parse(Info,R1,Context,[{internal_info,?NICE(Reason)}|ErrorLog],
+ [$#,$-,$-,$!,$<|Result]);
+ {ok,Context,Command,TagList,ValueList,R2} ->
+ ?DEBUG("parse -> Command: ~p",[Command]),
+ {ok,NewContext,NewErrorLog,MoreResult,R3}=
+ handle(Info,Context,ErrorLog,Command,TagList,ValueList,R2),
+ parse(Info,R3,NewContext,NewErrorLog,lists:reverse(MoreResult)++Result)
+ end;
+parse(Info,[$<,$!,$-,$-|R1],Context,ErrorLog,Result) ->
+ ?DEBUG("parse -> start comment when length(R1) = ~p",[length(R1)]),
+ case catch parse5(R1,[],0) of
+ {parse_error,Reason} ->
+ ?ERROR("parse -> parse error: ~p",[Reason]),
+ parse(Info,R1,Context,[{internal_info,?NICE(Reason)}|ErrorLog],Result);
+ {Comment,R2} ->
+ ?DEBUG("parse -> length(Comment) = ~p, length(R2) = ~p",
+ [length(Comment),length(R2)]),
+ parse(Info,R2,Context,ErrorLog,Comment++Result)
+ end;
+parse(Info,[C|R],Context,ErrorLog,Result) ->
+ parse(Info,R,Context,ErrorLog,[C|Result]).
+
+handle(Info,Context,ErrorLog,Command,TagList,ValueList,R) ->
+ case catch apply(?MODULE,Command,[Info,Context,ErrorLog,TagList,ValueList,
+ R]) of
+ {'EXIT',{undef,_}} ->
+ throw({parse_error,"Unknown command "++atom_to_list(Command)++
+ " in parsed doc"});
+ Result ->
+ Result
+ end.
+
+parse0([],Context) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse0([$-,$-,$>|R],Context) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse0([$ |R],Context) ->
+ parse0(R,Context);
+parse0(String,Context) ->
+ parse1(String,Context,"").
+
+parse1([],Context,Command) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse1([$-,$-,$>|R],Context,Command) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse1([$ |R],Context,Command) ->
+ parse2(R,Context,list_to_atom(lists:reverse(Command)),[],[],"");
+parse1([C|R],Context,Command) ->
+ parse1(R,Context,[C|Command]).
+
+parse2([],Context,Command,TagList,ValueList,Tag) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse2([$-,$-,$>|R],Context,Command,TagList,ValueList,Tag) ->
+ {ok,Context,Command,TagList,ValueList,R};
+parse2([$ |R],Context,Command,TagList,ValueList,Tag) ->
+ parse2(R,Context,Command,TagList,ValueList,Tag);
+parse2([$=|R],Context,Command,TagList,ValueList,Tag) ->
+ parse3(R,Context,Command,[list_to_atom(lists:reverse(Tag))|TagList],
+ ValueList);
+parse2([C|R],Context,Command,TagList,ValueList,Tag) ->
+ parse2(R,Context,Command,TagList,ValueList,[C|Tag]).
+
+parse3([],Context,Command,TagList,ValueList) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse3([$-,$-,$>|R],Context,Command,TagList,ValueList) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse3([$ |R],Context,Command,TagList,ValueList) ->
+ parse3(R,Context,Command,TagList,ValueList);
+parse3([$"|R],Context,Command,TagList,ValueList) ->
+ parse4(R,Context,Command,TagList,ValueList,"");
+parse3(String,Context,Command,TagList,ValueList) ->
+ throw({parse_error,"Premature EOF in parsed file"}).
+
+parse4([],Context,Command,TagList,ValueList,Value) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse4([$-,$-,$>|R],Context,Command,TagList,ValueList,Value) ->
+ throw({parse_error,"Premature EOF in parsed file"});
+parse4([$"|R],Context,Command,TagList,ValueList,Value) ->
+ parse2(R,Context,Command,TagList,[lists:reverse(Value)|ValueList],"");
+parse4([C|R],Context,Command,TagList,ValueList,Value) ->
+ parse4(R,Context,Command,TagList,ValueList,[C|Value]).
+
+parse5([],Comment,Depth) ->
+ ?ERROR("parse5 -> unterminated comment of ~p bytes when Depth = ~p",
+ [length(Comment),Depth]),
+ throw({parse_error,"Premature EOF in parsed file"});
+parse5([$<,$!,$-,$-|R],Comment,Depth) ->
+ parse5(R,[$-,$-,$!,$<|Comment],Depth+1);
+parse5([$-,$-,$>|R],Comment,0) ->
+ {">--"++Comment++"--!<",R};
+parse5([$-,$-,$>|R],Comment,Depth) ->
+ parse5(R,[$>,$-,$-|Comment],Depth-1);
+parse5([C|R],Comment,Depth) ->
+ parse5(R,[C|Comment],Depth).
+
+
+sz(B) when binary(B) -> {binary,size(B)};
+sz(L) when list(L) -> {list,length(L)};
+sz(_) -> undefined.
+
+
+%% send_error - Handle failure to send the file
+%%
+send_error({open,Reason},Info,Path) -> open_error(Reason,Info,Path);
+send_error({read,Reason},Info,Path) -> read_error(Reason,Info,Path).
+
+
+%% open_error - Handle file open failure
+%%
+open_error(eacces,Info,Path) ->
+ open_error(403,Info,Path,"");
+open_error(enoent,Info,Path) ->
+ open_error(404,Info,Path,"");
+open_error(enotdir,Info,Path) ->
+ open_error(404,Info,Path,
+ ": A component of the file name is not a directory");
+open_error(emfile,_Info,Path) ->
+ open_error(500,none,Path,": To many open files");
+open_error({enfile,_},_Info,Path) ->
+ open_error(500,none,Path,": File table overflow");
+open_error(_Reason,_Info,Path) ->
+ open_error(500,none,Path,"").
+
+open_error(StatusCode,none,Path,Reason) ->
+ {StatusCode,none,?NICE("Can't open "++Path++Reason)};
+open_error(StatusCode,Info,Path,Reason) ->
+ {StatusCode,Info#mod.request_uri,?NICE("Can't open "++Path++Reason)}.
+
+read_error(_Reason,_Info,Path) ->
+ read_error(500,none,Path,"").
+
+read_error(StatusCode,none,Path,Reason) ->
+ {StatusCode,none,?NICE("Can't read "++Path++Reason)};
+read_error(StatusCode,Info,Path,Reason) ->
+ {StatusCode,Info#mod.request_uri,?NICE("Can't read "++Path++Reason)}.
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_log.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_log.erl
new file mode 100644
index 0000000000..29fa2cfd11
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_log.erl
@@ -0,0 +1,250 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_log.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_log).
+-export([do/1,error_log/5,security_log/2,load/2,store/2,remove/1]).
+
+-export([report_error/2]).
+
+-include("httpd.hrl").
+
+-define(VMODULE,"LOG").
+-include("httpd_verbosity.hrl").
+
+%% do
+
+do(Info) ->
+ AuthUser = auth_user(Info#mod.data),
+ Date = custom_date(),
+ log_internal_info(Info,Date,Info#mod.data),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ transfer_log(Info,"-",AuthUser,Date,StatusCode,0),
+ if
+ StatusCode >= 400 ->
+ error_log(Info,Date,Reason);
+ true ->
+ not_an_error
+ end,
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ {already_sent,StatusCode,Size} ->
+ transfer_log(Info,"-",AuthUser,Date,StatusCode,Size),
+ {proceed,Info#mod.data};
+ {response,Head,Body} ->
+ Size=httpd_util:key1search(Head,content_length,unknown),
+ Code=httpd_util:key1search(Head,code,unknown),
+ transfer_log(Info,"-",AuthUser,Date,Code,Size),
+ {proceed,Info#mod.data};
+ {StatusCode,Response} ->
+ transfer_log(Info,"-",AuthUser,Date,200,
+ httpd_util:flatlength(Response)),
+ {proceed,Info#mod.data};
+ undefined ->
+ transfer_log(Info,"-",AuthUser,Date,200,0),
+ {proceed,Info#mod.data}
+ end
+ end.
+
+custom_date() ->
+ LocalTime=calendar:local_time(),
+ UniversalTime=calendar:universal_time(),
+ Minutes=round(diff_in_minutes(LocalTime,UniversalTime)),
+ {{YYYY,MM,DD},{Hour,Min,Sec}}=LocalTime,
+ Date =
+ io_lib:format("~.2.0w/~.3s/~.4w:~.2.0w:~.2.0w:~.2.0w ~c~.2.0w~.2.0w",
+ [DD, httpd_util:month(MM), YYYY, Hour, Min, Sec,
+ sign(Minutes),
+ abs(Minutes) div 60, abs(Minutes) rem 60]),
+ lists:flatten(Date).
+
+diff_in_minutes(L,U) ->
+ (calendar:datetime_to_gregorian_seconds(L) -
+ calendar:datetime_to_gregorian_seconds(U))/60.
+
+sign(Minutes) when Minutes > 0 ->
+ $+;
+sign(Minutes) ->
+ $-.
+
+auth_user(Data) ->
+ case httpd_util:key1search(Data,remote_user) of
+ undefined ->
+ "-";
+ RemoteUser ->
+ RemoteUser
+ end.
+
+%% log_internal_info
+
+log_internal_info(Info,Date,[]) ->
+ ok;
+log_internal_info(Info,Date,[{internal_info,Reason}|Rest]) ->
+ error_log(Info,Date,Reason),
+ log_internal_info(Info,Date,Rest);
+log_internal_info(Info,Date,[_|Rest]) ->
+ log_internal_info(Info,Date,Rest).
+
+%% transfer_log
+
+transfer_log(Info,RFC931,AuthUser,Date,StatusCode,Bytes) ->
+ case httpd_util:lookup(Info#mod.config_db,transfer_log) of
+ undefined ->
+ no_transfer_log;
+ TransferLog ->
+ {PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
+ case (catch io:format(TransferLog, "~s ~s ~s [~s] \"~s\" ~w ~w~n",
+ [RemoteHost, RFC931, AuthUser,
+ Date, Info#mod.request_line,
+ StatusCode, Bytes])) of
+ ok ->
+ ok;
+ Error ->
+ error_logger:error_report(Error)
+ end
+ end.
+
+%% security log
+
+security_log(Info, Reason) ->
+ case httpd_util:lookup(Info#mod.config_db, security_log) of
+ undefined ->
+ no_security_log;
+ SecurityLog ->
+ io:format(SecurityLog,"[~s] ~s~n", [custom_date(), Reason])
+ end.
+
+%% error_log
+
+error_log(Info,Date,Reason) ->
+ case httpd_util:lookup(Info#mod.config_db, error_log) of
+ undefined ->
+ no_error_log;
+ ErrorLog ->
+ {PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
+ io:format(ErrorLog,"[~s] access to ~s failed for ~s, reason: ~p~n",
+ [Date,Info#mod.request_uri,RemoteHost,Reason])
+ end.
+
+error_log(SocketType,Socket,ConfigDB,{PortNumber,RemoteHost},Reason) ->
+ case httpd_util:lookup(ConfigDB,error_log) of
+ undefined ->
+ no_error_log;
+ ErrorLog ->
+ Date=custom_date(),
+ io:format(ErrorLog,"[~s] server crash for ~s, reason: ~p~n",
+ [Date,RemoteHost,Reason]),
+ ok
+ end.
+
+report_error(ConfigDB,Error) ->
+ case httpd_util:lookup(ConfigDB,error_log) of
+ undefined ->
+ no_error_log;
+ ErrorLog ->
+ Date=custom_date(),
+ io:format(ErrorLog,"[~s] reporting error: ~s~n",[Date,Error]),
+ ok
+ end.
+
+%%
+%% Configuration
+%%
+
+%% load
+
+load([$T,$r,$a,$n,$s,$f,$e,$r,$L,$o,$g,$ |TransferLog],[]) ->
+ {ok,[],{transfer_log,httpd_conf:clean(TransferLog)}};
+load([$E,$r,$r,$o,$r,$L,$o,$g,$ |ErrorLog],[]) ->
+ {ok,[],{error_log,httpd_conf:clean(ErrorLog)}};
+load([$S,$e,$c,$u,$r,$i,$t,$y,$L,$o,$g,$ |SecurityLog], []) ->
+ {ok, [], {security_log, httpd_conf:clean(SecurityLog)}}.
+
+%% store
+
+store({transfer_log,TransferLog},ConfigList) ->
+ case create_log(TransferLog,ConfigList) of
+ {ok,TransferLogStream} ->
+ {ok,{transfer_log,TransferLogStream}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+store({error_log,ErrorLog},ConfigList) ->
+ case create_log(ErrorLog,ConfigList) of
+ {ok,ErrorLogStream} ->
+ {ok,{error_log,ErrorLogStream}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+store({security_log, SecurityLog},ConfigList) ->
+ case create_log(SecurityLog, ConfigList) of
+ {ok, SecurityLogStream} ->
+ {ok, {security_log, SecurityLogStream}};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+create_log(LogFile,ConfigList) ->
+ Filename = httpd_conf:clean(LogFile),
+ case filename:pathtype(Filename) of
+ absolute ->
+ case file:open(Filename, [read,write]) of
+ {ok,LogStream} ->
+ file:position(LogStream,{eof,0}),
+ {ok,LogStream};
+ {error,_} ->
+ {error,?NICE("Can't create "++Filename)}
+ end;
+ volumerelative ->
+ case file:open(Filename, [read,write]) of
+ {ok,LogStream} ->
+ file:position(LogStream,{eof,0}),
+ {ok,LogStream};
+ {error,_} ->
+ {error,?NICE("Can't create "++Filename)}
+ end;
+ relative ->
+ case httpd_util:key1search(ConfigList,server_root) of
+ undefined ->
+ {error,
+ ?NICE(Filename++
+ " is an invalid logfile name beacuse ServerRoot is not defined")};
+ ServerRoot ->
+ AbsoluteFilename=filename:join(ServerRoot,Filename),
+ case file:open(AbsoluteFilename, [read,write]) of
+ {ok,LogStream} ->
+ file:position(LogStream,{eof,0}),
+ {ok,LogStream};
+ {error,Reason} ->
+ {error,?NICE("Can't create "++AbsoluteFilename)}
+ end
+ end
+ end.
+
+%% remove
+
+remove(ConfigDB) ->
+ lists:foreach(fun([Stream]) -> file:close(Stream) end,
+ ets:match(ConfigDB,{transfer_log,'$1'})),
+ lists:foreach(fun([Stream]) -> file:close(Stream) end,
+ ets:match(ConfigDB,{error_log,'$1'})),
+ lists:foreach(fun([Stream]) -> file:close(Stream) end,
+ ets:match(ConfigDB,{security_log,'$1'})),
+ ok.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_range.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_range.erl
new file mode 100644
index 0000000000..0728bd2d91
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_range.erl
@@ -0,0 +1,397 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_range.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_range).
+-export([do/1]).
+-include("httpd.hrl").
+
+%% do
+
+
+
+do(Info) ->
+ ?DEBUG("do -> entry",[]),
+ case Info#mod.method of
+ "GET" ->
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.parsed_header,"range") of
+ undefined ->
+ %Not a range response
+ {proceed,Info#mod.data};
+ Range ->
+ %%Control that there weren't a if-range field that stopped
+ %%The range request in favor for the whole file
+ case httpd_util:key1search(Info#mod.data,if_range) of
+ send_file ->
+ {proceed,Info#mod.data};
+ _undefined ->
+ do_get_range(Info,Range)
+ end
+ end;
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end;
+ %% Not a GET method!
+ _ ->
+ {proceed,Info#mod.data}
+ end.
+
+do_get_range(Info,Ranges) ->
+ ?DEBUG("do_get_range -> Request URI: ~p",[Info#mod.request_uri]),
+ Path = mod_alias:path(Info#mod.data, Info#mod.config_db,
+ Info#mod.request_uri),
+ {FileInfo, LastModified} =get_modification_date(Path),
+ send_range_response(Path,Info,Ranges,FileInfo,LastModified).
+
+
+send_range_response(Path,Info,Ranges,FileInfo,LastModified)->
+ case parse_ranges(Ranges) of
+ error->
+ ?ERROR("send_range_response-> Unparsable range request",[]),
+ {proceed,Info#mod.data};
+ {multipart,RangeList}->
+ send_multi_range_response(Path,Info,RangeList);
+ {Start,Stop}->
+ send_range_response(Path,Info,Start,Stop,FileInfo,LastModified)
+ end.
+%%More than one range specified
+%%Send a multipart reponse to the user
+%
+%%An example of an multipart range response
+
+% HTTP/1.1 206 Partial Content
+% Date:Wed 15 Nov 1995 04:08:23 GMT
+% Last-modified:Wed 14 Nov 1995 04:08:23 GMT
+% Content-type: multipart/byteranges; boundary="SeparatorString"
+%
+% --"SeparatorString"
+% Content-Type: application/pdf
+% Content-Range: bytes 500-600/1010
+% .... The data..... 101 bytes
+%
+% --"SeparatorString"
+% Content-Type: application/pdf
+% Content-Range: bytes 700-1009/1010
+% .... The data.....
+
+
+
+send_multi_range_response(Path,Info,RangeList)->
+ case file:open(Path, [raw,binary]) of
+ {ok, FileDescriptor} ->
+ file:close(FileDescriptor),
+ ?DEBUG("send_multi_range_response -> FileDescriptor: ~p",[FileDescriptor]),
+ Suffix = httpd_util:suffix(Path),
+ PartMimeType = httpd_util:lookup_mime_default(Info#mod.config_db,Suffix,"text/plain"),
+ Date = httpd_util:rfc1123_date(),
+ {FileInfo,LastModified}=get_modification_date(Path),
+ case valid_ranges(RangeList,Path,FileInfo) of
+ {ValidRanges,true}->
+ ?DEBUG("send_multi_range_response -> Ranges are valid:",[]),
+ %Apache breaks the standard by sending the size field in the Header.
+ Header = [{code,206},
+ {content_type,"multipart/byteranges;boundary=RangeBoundarySeparator"},
+ {etag,httpd_util:create_etag(FileInfo)},
+ {last_modified,LastModified}
+ ],
+ ?DEBUG("send_multi_range_response -> Valid Ranges: ~p",[RagneList]),
+ Body={fun send_multiranges/4,[ValidRanges,Info,PartMimeType,Path]},
+ {proceed,[{response,{response,Header,Body}}|Info#mod.data]};
+ _ ->
+ {proceed, [{status, {416,"Range not valid",bad_range_boundaries }}]}
+ end;
+ {error, Reason} ->
+ ?ERROR("do_get -> failed open file: ~p",[Reason]),
+ {proceed,Info#mod.data}
+ end.
+
+send_multiranges(ValidRanges,Info,PartMimeType,Path)->
+ ?DEBUG("send_multiranges -> Start sending the ranges",[]),
+ case file:open(Path, [raw,binary]) of
+ {ok,FileDescriptor} ->
+ lists:foreach(fun(Range)->
+ send_multipart_start(Range,Info,PartMimeType,FileDescriptor)
+ end,ValidRanges),
+ file:close(FileDescriptor),
+ %%Sends an end of the multipart
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket,"\r\n--RangeBoundarySeparator--"),
+ sent;
+ _ ->
+ close
+ end.
+
+send_multipart_start({{Start,End},{StartByte,EndByte,Size}},Info,PartMimeType,FileDescriptor)when StartByte<Size->
+ PartHeader=["\r\n--RangeBoundarySeparator\r\n","Content-type: ",PartMimeType,"\r\n",
+ "Content-Range:bytes=",integer_to_list(StartByte),"-",integer_to_list(EndByte),"/",
+ integer_to_list(Size),"\r\n\r\n"],
+ send_part_start(Info#mod.socket_type,Info#mod.socket,PartHeader,FileDescriptor,Start,End);
+
+
+send_multipart_start({{Start,End},{StartByte,EndByte,Size}},Info,PartMimeType,FileDescriptor)->
+ PartHeader=["\r\n--RangeBoundarySeparator\r\n","Content-type: ",PartMimeType,"\r\n",
+ "Content-Range:bytes=",integer_to_list(Size-(StartByte-Size)),"-",integer_to_list(EndByte),"/",
+ integer_to_list(Size),"\r\n\r\n"],
+ send_part_start(Info#mod.socket_type,Info#mod.socket,PartHeader,FileDescriptor,Start,End).
+
+send_part_start(SocketType,Socket,PartHeader,FileDescriptor,Start,End)->
+ case httpd_socket:deliver(SocketType,Socket,PartHeader) of
+ ok ->
+ send_part_start(SocketType,Socket,FileDescriptor,Start,End);
+ _ ->
+ close
+ end.
+
+send_range_response(Path,Info,Start,Stop,FileInfo,LastModified)->
+ case file:open(Path, [raw,binary]) of
+ {ok, FileDescriptor} ->
+ file:close(FileDescriptor),
+ ?DEBUG("send_range_response -> FileDescriptor: ~p",[FileDescriptor]),
+ Suffix = httpd_util:suffix(Path),
+ MimeType = httpd_util:lookup_mime_default(Info#mod.config_db,Suffix,"text/plain"),
+ Date = httpd_util:rfc1123_date(),
+ Size = get_range_size(Start,Stop,FileInfo),
+ case valid_range(Start,Stop,FileInfo) of
+ {true,StartByte,EndByte,TotByte}->
+ Head=[{code,206},{content_type, MimeType},
+ {last_modified, LastModified},
+ {etag,httpd_util:create_etag(FileInfo)},
+ {content_range,["bytes=",integer_to_list(StartByte),"-",
+ integer_to_list(EndByte),"/",integer_to_list(TotByte)]},
+ {content_length,Size}],
+ BodyFunc=fun send_range_body/5,
+ Arg=[Info#mod.socket_type, Info#mod.socket,Path,Start,Stop],
+ {proceed,[{response,{response,Head,{BodyFunc,Arg}}}|Info#mod.data]};
+ {false,Reason} ->
+ {proceed, [{status, {416,Reason,bad_range_boundaries }}]}
+ end;
+ {error, Reason} ->
+ ?ERROR("send_range_response -> failed open file: ~p",[Reason]),
+ {proceed,Info#mod.data}
+ end.
+
+
+send_range_body(SocketType,Socket,Path,Start,End) ->
+ ?DEBUG("mod_range -> send_range_body",[]),
+ case file:open(Path, [raw,binary]) of
+ {ok,FileDescriptor} ->
+ send_part_start(SocketType,Socket,FileDescriptor,Start,End),
+ file:close(FileDescriptor);
+ _ ->
+ close
+ end.
+
+send_part_start(SocketType,Socket,FileDescriptor,Start,End) ->
+ case Start of
+ from_end ->
+ file:position(FileDescriptor,{eof,End}),
+ send_body(SocketType,Socket,FileDescriptor);
+ from_start ->
+ file:position(FileDescriptor,{bof,End}),
+ send_body(SocketType,Socket,FileDescriptor);
+ Byte when integer(Byte) ->
+ file:position(FileDescriptor,{bof,Start}),
+ send_part(SocketType,Socket,FileDescriptor,End)
+ end,
+ sent.
+
+
+%%This function could replace send_body by calling it with Start=0 end =FileSize
+%% But i gues it would be stupid when we look at performance
+send_part(SocketType,Socket,FileDescriptor,End)->
+ case file:position(FileDescriptor,{cur,0}) of
+ {ok,NewPos} ->
+ if
+ NewPos > End ->
+ ok;
+ true ->
+ Size=get_file_chunk_size(NewPos,End,?FILE_CHUNK_SIZE),
+ case file:read(FileDescriptor,Size) of
+ eof ->
+ ok;
+ {error,Reason} ->
+ ok;
+ {ok,Binary} ->
+ case httpd_socket:deliver(SocketType,Socket,Binary) of
+ socket_closed ->
+ ?LOG("send_range of body -> socket closed while sending",[]),
+ socket_close;
+ _ ->
+ send_part(SocketType,Socket,FileDescriptor,End)
+ end
+ end
+ end;
+ _->
+ ok
+ end.
+
+%% validate that the range is in the limits of the file
+valid_ranges(RangeList,Path,FileInfo)->
+ lists:mapfoldl(fun({Start,End},Acc)->
+ case Acc of
+ true ->
+ case valid_range(Start,End,FileInfo) of
+ {true,StartB,EndB,Size}->
+ {{{Start,End},{StartB,EndB,Size}},true};
+ _ ->
+ false
+ end;
+ _ ->
+ {false,false}
+ end
+ end,true,RangeList).
+
+
+
+valid_range(from_end,End,FileInfo)->
+ Size=FileInfo#file_info.size,
+ if
+ End < Size ->
+ {true,(Size+End),Size-1,Size};
+ true ->
+ false
+ end;
+valid_range(from_start,End,FileInfo)->
+ Size=FileInfo#file_info.size,
+ if
+ End < Size ->
+ {true,End,Size-1,Size};
+ true ->
+ false
+ end;
+
+valid_range(Start,End,FileInfo)when Start=<End->
+ case FileInfo#file_info.size of
+ FileSize when Start< FileSize ->
+ case FileInfo#file_info.size of
+ Size when End<Size ->
+ {true,Start,End,FileInfo#file_info.size};
+ Size ->
+ {true,Start,Size-1,Size}
+ end;
+ _->
+ {false,"The size of the range is negative"}
+ end;
+
+valid_range(Start,End,FileInfo)->
+ {false,"Range starts out of file boundaries"}.
+%% Find the modification date of the file
+get_modification_date(Path)->
+ case file:read_file_info(Path) of
+ {ok, FileInfo0} ->
+ {FileInfo0, httpd_util:rfc1123_date(FileInfo0#file_info.mtime)};
+ _ ->
+ {#file_info{},""}
+ end.
+
+%Calculate the size of the chunk to read
+
+get_file_chunk_size(Position,End,DefaultChunkSize)when (Position+DefaultChunkSize) =< End->
+ DefaultChunkSize;
+get_file_chunk_size(Position,End,DefaultChunkSize)->
+ (End-Position) +1.
+
+
+
+%Get the size of the range to send. Remember that
+%A range is from startbyte up to endbyte which means that
+%the nuber of byte in a range is (StartByte-EndByte)+1
+
+get_range_size(from_end,Stop,FileInfo)->
+ integer_to_list(-1*Stop);
+
+get_range_size(from_start,StartByte,FileInfo) ->
+ integer_to_list((((FileInfo#file_info.size)-StartByte)));
+
+get_range_size(StartByte,EndByte,FileInfo) ->
+ integer_to_list((EndByte-StartByte)+1).
+
+parse_ranges([$\ ,$b,$y,$t,$e,$s,$\=|Ranges])->
+ parse_ranges([$b,$y,$t,$e,$s,$\=|Ranges]);
+parse_ranges([$b,$y,$t,$e,$s,$\=|Ranges])->
+ case string:tokens(Ranges,", ") of
+ [Range] ->
+ parse_range(Range);
+ [Range1|SplittedRanges]->
+ {multipart,lists:map(fun parse_range/1,[Range1|SplittedRanges])}
+ end;
+%Bad unit
+parse_ranges(Ranges)->
+ io:format("Bad Ranges : ~p",[Ranges]),
+ error.
+%Parse the range specification from the request to {Start,End}
+%Start=End : Numreric string | []
+
+parse_range(Range)->
+ format_range(split_range(Range,[],[])).
+format_range({[],BytesFromEnd})->
+ {from_end,-1*(list_to_integer(BytesFromEnd))};
+format_range({StartByte,[]})->
+ {from_start,list_to_integer(StartByte)};
+format_range({StartByte,EndByte})->
+ {list_to_integer(StartByte),list_to_integer(EndByte)}.
+%Last case return the splitted range
+split_range([],Current,Other)->
+ {lists:reverse(Other),lists:reverse(Current)};
+
+split_range([$-|Rest],Current,Other)->
+ split_range(Rest,Other,Current);
+
+split_range([N|Rest],Current,End) ->
+ split_range(Rest,[N|Current],End).
+
+send_body(SocketType,Socket,FileDescriptor) ->
+ case file:read(FileDescriptor,?FILE_CHUNK_SIZE) of
+ {ok,Binary} ->
+ ?DEBUG("send_body -> send another chunk: ~p",[size(Binary)]),
+ case httpd_socket:deliver(SocketType,Socket,Binary) of
+ socket_closed ->
+ ?LOG("send_body -> socket closed while sending",[]),
+ socket_close;
+ _ ->
+ send_body(SocketType,Socket,FileDescriptor)
+ end;
+ eof ->
+ ?DEBUG("send_body -> done with this file",[]),
+ eof
+ end.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_responsecontrol.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_responsecontrol.erl
new file mode 100644
index 0000000000..c946098120
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_responsecontrol.erl
@@ -0,0 +1,337 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_responsecontrol.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+
+-module(mod_responsecontrol).
+-export([do/1]).
+
+-include("httpd.hrl").
+
+
+do(Info) ->
+ ?DEBUG("do -> response_control",[]),
+ case httpd_util:key1search(Info#mod.data,status) of
+ %% A status code has been generated!
+ {StatusCode,PhraseArgs,Reason} ->
+ {proceed,Info#mod.data};
+ %% No status code has been generated!
+ undefined ->
+ case httpd_util:key1search(Info#mod.data,response) of
+ %% No response has been generated!
+ undefined ->
+ case do_responsecontrol(Info) of
+ continue ->
+ {proceed,Info#mod.data};
+ Response ->
+ {proceed,[Response|Info#mod.data]}
+ end;
+ %% A response has been generated or sent!
+ Response ->
+ {proceed,Info#mod.data}
+ end
+ end.
+
+
+%%----------------------------------------------------------------------
+%%Control that the request header did not contians any limitations
+%%wheather a response shall be createed or not
+%%----------------------------------------------------------------------
+
+do_responsecontrol(Info) ->
+ ?DEBUG("do_response_control -> Request URI: ~p",[Info#mod.request_uri]),
+ Path = mod_alias:path(Info#mod.data, Info#mod.config_db,
+ Info#mod.request_uri),
+ case file:read_file_info(Path) of
+ {ok, FileInfo} ->
+ control(Path,Info,FileInfo);
+ _ ->
+ %% The requested asset is not a plain file and then it must
+ %% be generated everytime its requested
+ continue
+ end.
+
+%%----------------------------------------------------------------------
+%%Control the If-Match, If-None-Match, and If-Modified-Since
+%%----------------------------------------------------------------------
+
+
+%% If a client sends more then one of the if-XXXX fields in a request
+%% The standard says it does not specify the behaviuor so I specified it :-)
+%% The priority between the fields is
+%% 1.If-modified
+%% 2.If-Unmodified
+%% 3.If-Match
+%% 4.If-Nomatch
+
+%% This means if more than one of the fields are in the request the
+%% field with highest priority will be used
+
+%%If the request is a range request the If-Range field will be the winner.
+
+control(Path,Info,FileInfo)->
+ case control_range(Path,Info,FileInfo) of
+ undefined ->
+ case control_Etag(Path,Info,FileInfo) of
+ undefined ->
+ case control_modification(Path,Info,FileInfo) of
+ continue ->
+ continue;
+ ReturnValue ->
+ send_return_value(ReturnValue,FileInfo)
+ end;
+ continue ->
+ continue;
+ ReturnValue ->
+ send_return_value(ReturnValue,FileInfo)
+ end;
+ Response->
+ Response
+ end.
+
+%%----------------------------------------------------------------------
+%%If there are both a range and an if-range field control if
+%%----------------------------------------------------------------------
+control_range(Path,Info,FileInfo) ->
+ case httpd_util:key1search(Info#mod.parsed_header,"range") of
+ undefined->
+ undefined;
+ _Range ->
+ case httpd_util:key1search(Info#mod.parsed_header,"if-range") of
+ undefined ->
+ undefined;
+ EtagOrDate ->
+ control_if_range(Path,Info,FileInfo,EtagOrDate)
+ end
+ end.
+
+control_if_range(Path,Info,FileInfo,EtagOrDate) ->
+ case httpd_util:convert_request_date(strip_date(EtagOrDate)) of
+ bad_date ->
+ FileEtag=httpd_util:create_etag(FileInfo),
+ case FileEtag of
+ EtagOrDate ->
+ continue;
+ _ ->
+ {if_range,send_file}
+ end;
+ ErlDate ->
+ %%We got the date in the request if it is
+ case control_modification_data(Info,FileInfo#file_info.mtime,"if-range") of
+ modified ->
+ {if_range,send_file};
+ _UnmodifiedOrUndefined->
+ continue
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%%Controls the values of the If-Match and I-None-Mtch
+%%----------------------------------------------------------------------
+control_Etag(Path,Info,FileInfo)->
+ FileEtag=httpd_util:create_etag(FileInfo),
+ %%Control if the E-Tag for the resource matches one of the Etags in
+ %%the -if-match header field
+ case control_match(Info,FileInfo,"if-match",FileEtag) of
+ nomatch ->
+ %%None of the Etags in the if-match field matched the current
+ %%Etag for the resource return a 304
+ {412,Info,Path};
+ match ->
+ continue;
+ undefined ->
+ case control_match(Info,FileInfo,"if-none-match",FileEtag) of
+ nomatch ->
+ continue;
+ match ->
+ case Info#mod.method of
+ "GET" ->
+ {304,Info,Path};
+ "HEAD" ->
+ {304,Info,Path};
+ _OtherrequestMethod ->
+ {412,Info,Path}
+ end;
+ undefined ->
+ undefined
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%%Control if there are any Etags for HeaderField in the request if so
+%%Control if they match the Etag for the requested file
+%%----------------------------------------------------------------------
+control_match(Info,FileInfo,HeaderField,FileEtag)->
+ case split_etags(httpd_util:key1search(Info#mod.parsed_header,HeaderField)) of
+ undefined->
+ undefined;
+ Etags->
+ %%Control that the match any star not is availible
+ case lists:member("*",Etags) of
+ true->
+ match;
+ false->
+ compare_etags(FileEtag,Etags)
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%%Split the etags from the request
+%%----------------------------------------------------------------------
+split_etags(undefined)->
+ undefined;
+split_etags(Tags) ->
+ string:tokens(Tags,", ").
+
+%%----------------------------------------------------------------------
+%%Control if the etag for the file is in the list
+%%----------------------------------------------------------------------
+compare_etags(Tag,Etags) ->
+ case lists:member(Tag,Etags) of
+ true ->
+ match;
+ _ ->
+ nomatch
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%%Control if the file is modificated %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%----------------------------------------------------------------------
+%%Control the If-Modified-Since and If-Not-Modified-Since header fields
+%%----------------------------------------------------------------------
+control_modification(Path,Info,FileInfo)->
+ ?DEBUG("control_modification() -> entry",[]),
+ case control_modification_data(Info,FileInfo#file_info.mtime,"if-modified-since") of
+ modified->
+ continue;
+ unmodified->
+ {304,Info,Path};
+ undefined ->
+ case control_modification_data(Info,FileInfo#file_info.mtime,"if-unmodified-since") of
+ modified ->
+ {412,Info,Path};
+ _ContinueUndefined ->
+ continue
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%%Controls the date from the http-request if-modified-since and
+%%if-not-modified-since against the modification data of the
+%%File
+%%----------------------------------------------------------------------
+%%Info is the record about the request
+%%ModificationTime is the time the file was edited last
+%%Header Field is the name of the field to control
+
+control_modification_data(Info,ModificationTime,HeaderField)->
+ case strip_date(httpd_util:key1search(Info#mod.parsed_header,HeaderField)) of
+ undefined->
+ undefined;
+ LastModified0 ->
+ LastModified=httpd_util:convert_request_date(LastModified0),
+ ?DEBUG("control_modification_data() -> "
+ "~n Request-Field: ~s"
+ "~n FileLastModified: ~p"
+ "~n FieldValue: ~p",
+ [HeaderField,ModificationTime,LastModified]),
+ case LastModified of
+ bad_date ->
+ undefined;
+ _ ->
+ FileTime=calendar:datetime_to_gregorian_seconds(ModificationTime),
+ FieldTime=calendar:datetime_to_gregorian_seconds(LastModified),
+ if
+ FileTime=<FieldTime ->
+ ?DEBUG("File unmodified~n", []),
+ unmodified;
+ FileTime>=FieldTime ->
+ ?DEBUG("File modified~n", []),
+ modified
+ end
+ end
+ end.
+
+%%----------------------------------------------------------------------
+%%Compare to dates on the form {{YYYY,MM,DD},{HH,MIN,SS}}
+%%If the first date is the biggest returns biggest1 (read biggestFirst)
+%%If the first date is smaller
+% compare_date(Date,bad_date)->
+% bad_date;
+
+% compare_date({D1,T1},{D2,T2})->
+% case compare_date1(D1,D2) of
+% equal ->
+% compare_date1(T1,T2);
+% GTorLT->
+% GTorLT
+% end.
+
+% compare_date1({T1,T2,T3},{T12,T22,T32}) when T1>T12 ->
+% bigger1;
+% compare_date1({T1,T2,T3},{T1,T22,T32}) when T2>T22 ->
+% bigger1;
+% compare_date1({T1,T2,T3},{T1,T2,T32}) when T3>T32 ->
+% bigger1;
+% compare_date1({T1,T2,T3},{T1,T2,T3})->
+% equal;
+% compare_date1(_D1,_D2)->
+% smaller1.
+
+
+%% IE4 & NS4 sends an extra '; length=xxxx' string at the end of the If-Modified-Since
+%% header, we detect this and ignore it (the RFCs does not mention this).
+strip_date(undefined) ->
+ undefined;
+strip_date([]) ->
+ [];
+strip_date([$;,$ |Rest]) ->
+ [];
+strip_date([C|Rest]) ->
+ [C|strip_date(Rest)].
+
+send_return_value({412,_,_},FileInfo)->
+ {status,{412,none,"Precondition Failed"}};
+
+send_return_value({304,Info,Path},FileInfo)->
+ Suffix=httpd_util:suffix(Path),
+ MimeType = httpd_util:lookup_mime_default(Info#mod.config_db,Suffix,"text/plain"),
+ Header = [{code,304},
+ {etag,httpd_util:create_etag(FileInfo)},
+ {content_length,0},
+ {last_modified,httpd_util:rfc1123_date(FileInfo#file_info.mtime)}],
+ {response,{response,Header,nobody}}.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security.erl
new file mode 100644
index 0000000000..14197979d1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security.erl
@@ -0,0 +1,307 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_security.erl,v 1.1 2008/12/17 09:53:35 mikpe Exp $
+%%
+-module(mod_security).
+
+%% Security Audit Functionality
+
+%% User API exports
+-export([list_blocked_users/1, list_blocked_users/2, list_blocked_users/3,
+ block_user/4, block_user/5,
+ unblock_user/2, unblock_user/3, unblock_user/4,
+ list_auth_users/1, list_auth_users/2, list_auth_users/3]).
+
+%% module API exports
+-export([do/1, load/2, store/2, remove/1]).
+
+-include("httpd.hrl").
+
+-define(VMODULE,"SEC").
+-include("httpd_verbosity.hrl").
+
+
+%% do/1
+do(Info) ->
+ ?vdebug("~n do with ~n Info: ~p",[Info]),
+ %% Check and see if any user has been authorized.
+ case httpd_util:key1search(Info#mod.data,remote_user,not_defined_user) of
+ not_defined_user ->
+ %% No user has been authorized.
+ case httpd_util:key1search(Info#mod.data, status) of
+ %% A status code has been generated!
+ {401, PhraseArgs, Reason} ->
+ case httpd_util:key1search(Info#mod.parsed_header,
+ "authorization") of
+ undefined ->
+ %% Not an authorization attempt (server just replied to
+ %% challenge for authentication)
+ {proceed, Info#mod.data};
+ [$B,$a,$s,$i,$c,$ |EncodedString] ->
+ %% Someone tried to authenticate, and obviously failed!
+ ?vlog("~n Authentication failed: ~s",
+ [EncodedString]),
+ report_failed(Info, EncodedString,"Failed authentication"),
+ take_failed_action(Info, EncodedString),
+ {proceed, Info#mod.data}
+ end;
+ _ ->
+ {proceed, Info#mod.data}
+ end;
+ User ->
+ %% A user has been authenticated, now is he blocked ?
+ ?vtrace("user '~p' authentication",[User]),
+ Path = mod_alias:path(Info#mod.data,
+ Info#mod.config_db,
+ Info#mod.request_uri),
+ {Dir, SDirData} = secretp(Path, Info#mod.config_db),
+ Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
+ Port = httpd_util:lookup(Info#mod.config_db, port),
+ DF = httpd_util:key1search(SDirData, data_file),
+ case mod_security_server:check_blocked_user(Info, User,
+ SDirData,
+ Addr, Port) of
+ true ->
+ ?vtrace("user blocked",[]),
+ report_failed(Info,httpd_util:decode_base64(User) ,"User Blocked"),
+ {proceed, [{status, {403, Info#mod.request_uri, ""}}|Info#mod.data]};
+ false ->
+ ?vtrace("user not blocked",[]),
+ EncodedUser=httpd_util:decode_base64(User),
+ report_failed(Info, EncodedUser,"Authentication Succedded"),
+ mod_security_server:store_successful_auth(Addr, Port,
+ User, SDirData),
+ {proceed, Info#mod.data}
+ end
+ end.
+
+
+
+report_failed(Info, EncodedString,Event) ->
+ Request = Info#mod.request_line,
+ Decoded = httpd_util:decode_base64(EncodedString),
+ {PortNumber,RemoteHost}=(Info#mod.init_data)#init_data.peername,
+ String = RemoteHost++" : " ++ Event ++ " : "++Request++" : "++Decoded,
+ mod_disk_log:security_log(Info,String),
+ mod_log:security_log(Info, String).
+
+take_failed_action(Info, EncodedString) ->
+ Path = mod_alias:path(Info#mod.data,Info#mod.config_db, Info#mod.request_uri),
+ {Dir, SDirData} = secretp(Path, Info#mod.config_db),
+ Addr = httpd_util:lookup(Info#mod.config_db, bind_address),
+ Port = httpd_util:lookup(Info#mod.config_db, port),
+ DecodedString = httpd_util:decode_base64(EncodedString),
+ mod_security_server:store_failed_auth(Info, Addr, Port,
+ DecodedString, SDirData).
+
+secretp(Path, ConfigDB) ->
+ Directories = ets:match(ConfigDB,{directory,'$1','_'}),
+ case secret_path(Path, Directories) of
+ {yes, Directory} ->
+ SDirs0 = httpd_util:multi_lookup(ConfigDB, security_directory),
+ SDir = lists:filter(fun(X) ->
+ lists:member({path, Directory}, X)
+ end, SDirs0),
+ {Directory, lists:flatten(SDir)};
+ no ->
+ error_report({internal_error_secretp, ?MODULE}),
+ {[], []}
+ end.
+
+secret_path(Path,Directories) ->
+ secret_path(Path, httpd_util:uniq(lists:sort(Directories)), to_be_found).
+
+secret_path(Path, [], to_be_found) ->
+ no;
+secret_path(Path, [], Directory) ->
+ {yes, Directory};
+secret_path(Path, [[NewDirectory]|Rest], Directory) ->
+ case regexp:match(Path, NewDirectory) of
+ {match, _, _} when Directory == to_be_found ->
+ secret_path(Path, Rest, NewDirectory);
+ {match, _, Length} when Length > length(Directory)->
+ secret_path(Path, Rest, NewDirectory);
+ {match, _, Length} ->
+ secret_path(Path, Rest, Directory);
+ nomatch ->
+ secret_path(Path, Rest, Directory)
+ end.
+
+
+load([$<,$D,$i,$r,$e,$c,$t,$o,$r,$y,$ |Directory],[]) ->
+ Dir = httpd_conf:custom_clean(Directory,"",">"),
+ {ok, [{security_directory, Dir, [{path, Dir}]}]};
+load(eof,[{security_directory,Directory, DirData}|_]) ->
+ {error, ?NICE("Premature end-of-file in "++Directory)};
+load([$S,$e,$c,$u,$r,$i,$t,$y,$D,$a,$t,$a,$F,$i,$l,$e,$ |FileName],
+ [{security_directory, Dir, DirData}]) ->
+ File = httpd_conf:clean(FileName),
+ {ok, [{security_directory, Dir, [{data_file, File}|DirData]}]};
+load([$S,$e,$c,$u,$r,$i,$t,$y,$C,$a,$l,$l,$b,$a,$c,$k,$M,$o,$d,$u,$l,$e,$ |ModuleName],
+ [{security_directory, Dir, DirData}]) ->
+ Mod = list_to_atom(httpd_conf:clean(ModuleName)),
+ {ok, [{security_directory, Dir, [{callback_module, Mod}|DirData]}]};
+load([$S,$e,$c,$u,$r,$i,$t,$y,$M,$a,$x,$R,$e,$t,$r,$i,$e,$s,$ |Retries],
+ [{security_directory, Dir, DirData}]) ->
+ MaxRetries = httpd_conf:clean(Retries),
+ load_return_int_tag("SecurityMaxRetries", max_retries,
+ httpd_conf:clean(Retries), Dir, DirData);
+load([$S,$e,$c,$u,$r,$i,$t,$y,$B,$l,$o,$c,$k,$T,$i,$m,$e,$ |Time],
+ [{security_directory, Dir, DirData}]) ->
+ load_return_int_tag("SecurityBlockTime", block_time,
+ httpd_conf:clean(Time), Dir, DirData);
+load([$S,$e,$c,$u,$r,$i,$t,$y,$F,$a,$i,$l,$E,$x,$p,$i,$r,$e,$T,$i,$m,$e,$ |Time],
+ [{security_directory, Dir, DirData}]) ->
+ load_return_int_tag("SecurityFailExpireTime", fail_expire_time,
+ httpd_conf:clean(Time), Dir, DirData);
+load([$S,$e,$c,$u,$r,$i,$t,$y,$A,$u,$t,$h,$T,$i,$m,$e,$o,$u,$t,$ |Time0],
+ [{security_directory, Dir, DirData}]) ->
+ Time = httpd_conf:clean(Time0),
+ load_return_int_tag("SecurityAuthTimeout", auth_timeout,
+ httpd_conf:clean(Time), Dir, DirData);
+load([$A,$u,$t,$h,$N,$a,$m,$e,$ |Name0],
+ [{security_directory, Dir, DirData}]) ->
+ Name = httpd_conf:clean(Name0),
+ {ok, [{security_directory, Dir, [{auth_name, Name}|DirData]}]};
+load("</Directory>",[{security_directory,Directory, DirData}]) ->
+ {ok, [], {security_directory, Directory, DirData}}.
+
+load_return_int_tag(Name, Atom, Time, Dir, DirData) ->
+ case Time of
+ "infinity" ->
+ {ok, [{security_directory, Dir, [{Atom, 99999999999999999999999999999}|DirData]}]};
+ Int ->
+ case catch list_to_integer(Time) of
+ {'EXIT', _} ->
+ {error, Time++" is an invalid "++Name};
+ Val ->
+ {ok, [{security_directory, Dir, [{Atom, Val}|DirData]}]}
+ end
+ end.
+
+store({security_directory, Dir0, DirData}, ConfigList) ->
+ ?CDEBUG("store(security_directory) -> ~n"
+ " Dir0: ~p~n"
+ " DirData: ~p",
+ [Dir0, DirData]),
+ Addr = httpd_util:key1search(ConfigList, bind_address),
+ Port = httpd_util:key1search(ConfigList, port),
+ mod_security_server:start(Addr, Port),
+ SR = httpd_util:key1search(ConfigList, server_root),
+ Dir =
+ case filename:pathtype(Dir0) of
+ relative ->
+ filename:join(SR, Dir0);
+ _ ->
+ Dir0
+ end,
+ case httpd_util:key1search(DirData, data_file, no_data_file) of
+ no_data_file ->
+ {error, no_security_data_file};
+ DataFile0 ->
+ DataFile =
+ case filename:pathtype(DataFile0) of
+ relative ->
+ filename:join(SR, DataFile0);
+ _ ->
+ DataFile0
+ end,
+ case mod_security_server:new_table(Addr, Port, DataFile) of
+ {ok, TwoTables} ->
+ NewDirData0 = lists:keyreplace(data_file, 1, DirData,
+ {data_file, TwoTables}),
+ NewDirData1 = case Addr of
+ undefined ->
+ [{port,Port}|NewDirData0];
+ _ ->
+ [{port,Port},{bind_address,Addr}|
+ NewDirData0]
+ end,
+ {ok, {security_directory,NewDirData1}};
+ {error, Err} ->
+ {error, {{open_data_file, DataFile}, Err}}
+ end
+ end.
+
+
+remove(ConfigDB) ->
+ Addr = case ets:lookup(ConfigDB, bind_address) of
+ [] ->
+ undefined;
+ [{bind_address, Address}] ->
+ Address
+ end,
+ [{port, Port}] = ets:lookup(ConfigDB, port),
+ mod_security_server:delete_tables(Addr, Port),
+ mod_security_server:stop(Addr, Port).
+
+
+%%
+%% User API
+%%
+
+%% list_blocked_users
+
+list_blocked_users(Port) ->
+ list_blocked_users(undefined, Port).
+
+list_blocked_users(Port, Dir) when integer(Port) ->
+ list_blocked_users(undefined,Port,Dir);
+list_blocked_users(Addr, Port) when integer(Port) ->
+ mod_security_server:list_blocked_users(Addr, Port).
+
+list_blocked_users(Addr, Port, Dir) ->
+ mod_security_server:list_blocked_users(Addr, Port, Dir).
+
+
+%% block_user
+
+block_user(User, Port, Dir, Time) ->
+ block_user(User, undefined, Port, Dir, Time).
+block_user(User, Addr, Port, Dir, Time) ->
+ mod_security_server:block_user(User, Addr, Port, Dir, Time).
+
+
+%% unblock_user
+
+unblock_user(User, Port) ->
+ unblock_user(User, undefined, Port).
+
+unblock_user(User, Port, Dir) when integer(Port) ->
+ unblock_user(User, undefined, Port, Dir);
+unblock_user(User, Addr, Port) when integer(Port) ->
+ mod_security_server:unblock_user(User, Addr, Port).
+
+unblock_user(User, Addr, Port, Dir) ->
+ mod_security_server:unblock_user(User, Addr, Port, Dir).
+
+
+%% list_auth_users
+
+list_auth_users(Port) ->
+ list_auth_users(undefined,Port).
+
+list_auth_users(Port, Dir) when integer(Port) ->
+ list_auth_users(undefined, Port, Dir);
+list_auth_users(Addr, Port) when integer(Port) ->
+ mod_security_server:list_auth_users(Addr, Port).
+
+list_auth_users(Addr, Port, Dir) ->
+ mod_security_server:list_auth_users(Addr, Port, Dir).
+
+
+error_report(M) ->
+ error_logger:error_report(M).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security_server.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security_server.erl
new file mode 100644
index 0000000000..7df61df63e
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_security_server.erl
@@ -0,0 +1,728 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_security_server.erl,v 1.1 2008/12/17 09:53:36 mikpe Exp $
+%%
+%% Security Audit Functionality
+
+%%
+%% The gen_server code.
+%%
+%% A gen_server is needed in this module to take care of shared access to the
+%% data file used to store failed and successful authentications aswell as
+%% user blocks.
+%%
+%% The storage model is a write-through model with both an ets and a dets
+%% table. Writes are done to both the ets and then the dets table, but reads
+%% are only done from the ets table.
+%%
+%% This approach also enables parallelism when using dets by returning the
+%% same dets table identifier when opening several files with the same
+%% physical location.
+%%
+%% NOTE: This could be implemented using a single dets table, as it is
+%% possible to open a dets file with the ram_file flag, but this
+%% would require periodical sync's to disk, and it would be hard
+%% to decide when such an operation should occur.
+%%
+
+
+-module(mod_security_server).
+
+-include("httpd.hrl").
+-include("httpd_verbosity.hrl").
+
+
+-behaviour(gen_server).
+
+
+%% User API exports (called via mod_security)
+-export([list_blocked_users/2, list_blocked_users/3,
+ block_user/5,
+ unblock_user/3, unblock_user/4,
+ list_auth_users/2, list_auth_users/3]).
+
+%% Internal exports (for mod_security only)
+-export([start/2, stop/1, stop/2,
+ new_table/3, delete_tables/2,
+ store_failed_auth/5, store_successful_auth/4,
+ check_blocked_user/5]).
+
+%% gen_server exports
+-export([start_link/3,
+ init/1,
+ handle_info/2, handle_call/3, handle_cast/2,
+ terminate/2,
+ code_change/3]).
+
+-export([verbosity/3]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% External API %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% start_link/3
+%%
+%% NOTE: This is called by httpd_misc_sup when the process is started
+%%
+
+start_link(Addr, Port, Verbosity) ->
+ ?vtrace("start_link -> entry with"
+ "~n Addr: ~p"
+ "~n Port: ~p", [Addr, Port]),
+ Name = make_name(Addr, Port),
+ gen_server:start_link({local, Name}, ?MODULE, [Verbosity],
+ [{timeout, infinity}]).
+
+
+%% start/2
+%% Called by the mod_security module.
+
+start(Addr, Port) ->
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ undefined ->
+ Verbosity = get(security_verbosity),
+ case httpd_misc_sup:start_sec_server(Addr, Port, Verbosity) of
+ {ok, Pid} ->
+ put(security_server, Pid),
+ ok;
+ Error ->
+ exit({failed_start_security_server, Error})
+ end;
+ _ -> %% Already started...
+ ok
+ end.
+
+
+%% stop
+
+stop(Port) ->
+ stop(undefined, Port).
+stop(Addr, Port) ->
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ undefined ->
+ ok;
+ _ ->
+ httpd_misc_sup:stop_sec_server(Addr, Port)
+ end.
+
+
+%% verbosity
+
+verbosity(Addr, Port, Verbosity) ->
+ Name = make_name(Addr, Port),
+ Req = {verbosity, Verbosity},
+ call(Name, Req).
+
+
+%% list_blocked_users
+
+list_blocked_users(Addr, Port) ->
+ Name = make_name(Addr,Port),
+ Req = {list_blocked_users, Addr, Port, '_'},
+ call(Name, Req).
+
+list_blocked_users(Addr, Port, Dir) ->
+ Name = make_name(Addr, Port),
+ Req = {list_blocked_users, Addr, Port, Dir},
+ call(Name, Req).
+
+
+%% block_user
+
+block_user(User, Addr, Port, Dir, Time) ->
+ Name = make_name(Addr, Port),
+ Req = {block_user, User, Addr, Port, Dir, Time},
+ call(Name, Req).
+
+
+%% unblock_user
+
+unblock_user(User, Addr, Port) ->
+ Name = make_name(Addr, Port),
+ Req = {unblock_user, User, Addr, Port, '_'},
+ call(Name, Req).
+
+unblock_user(User, Addr, Port, Dir) ->
+ Name = make_name(Addr, Port),
+ Req = {unblock_user, User, Addr, Port, Dir},
+ call(Name, Req).
+
+
+%% list_auth_users
+
+list_auth_users(Addr, Port) ->
+ Name = make_name(Addr, Port),
+ Req = {list_auth_users, Addr, Port, '_'},
+ call(Name, Req).
+
+list_auth_users(Addr, Port, Dir) ->
+ Name = make_name(Addr,Port),
+ Req = {list_auth_users, Addr, Port, Dir},
+ call(Name, Req).
+
+
+%% new_table
+
+new_table(Addr, Port, TabName) ->
+ Name = make_name(Addr,Port),
+ Req = {new_table, Addr, Port, TabName},
+ call(Name, Req).
+
+
+%% delete_tables
+
+delete_tables(Addr, Port) ->
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ undefined ->
+ ok;
+ _ ->
+ call(Name, delete_tables)
+ end.
+
+
+%% store_failed_auth
+
+store_failed_auth(Info, Addr, Port, DecodedString, SDirData) ->
+ Name = make_name(Addr,Port),
+ Msg = {store_failed_auth,[Info,DecodedString,SDirData]},
+ cast(Name, Msg).
+
+
+%% store_successful_auth
+
+store_successful_auth(Addr, Port, User, SDirData) ->
+ Name = make_name(Addr,Port),
+ Msg = {store_successful_auth, [User,Addr,Port,SDirData]},
+ cast(Name, Msg).
+
+
+%% check_blocked_user
+
+check_blocked_user(Info, User, SDirData, Addr, Port) ->
+ Name = make_name(Addr, Port),
+ Req = {check_blocked_user, [Info, User, SDirData]},
+ call(Name, Req).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Server call-back functions %%
+%% %%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% init
+
+init([undefined]) ->
+ init([?default_verbosity]);
+init([Verbosity]) ->
+ ?DEBUG("init -> entry with Verbosity: ~p",[Verbosity]),
+ process_flag(trap_exit, true),
+ put(sname, sec),
+ put(verbosity, Verbosity),
+ ?vlog("starting",[]),
+ {ok, []}.
+
+
+%% handle_call
+
+handle_call(stop, _From, Tables) ->
+ ?vlog("stop",[]),
+ {stop, normal, ok, []};
+
+
+handle_call({verbosity,Verbosity}, _From, Tables) ->
+ ?vlog("set verbosity to ~p",[Verbosity]),
+ OldVerbosity = get(verbosity),
+ put(verbosity,Verbosity),
+ ?vdebug("old verbosity: ~p",[OldVerbosity]),
+ {reply,OldVerbosity,Tables};
+
+
+handle_call({block_user, User, Addr, Port, Dir, Time}, _From, Tables) ->
+ ?vlog("block user '~p' for ~p",[User,Dir]),
+ Ret = block_user_int({User, Addr, Port, Dir, Time}),
+ ?vdebug("block user result: ~p",[Ret]),
+ {reply, Ret, Tables};
+
+
+handle_call({list_blocked_users, Addr, Port, Dir}, _From, Tables) ->
+ ?vlog("list blocked users for ~p",[Dir]),
+ Blocked = list_blocked(Tables, Addr, Port, Dir, []),
+ ?vdebug("list blocked users: ~p",[Blocked]),
+ {reply, Blocked, Tables};
+
+
+handle_call({unblock_user, User, Addr, Port, Dir}, _From, Tables) ->
+ ?vlog("unblock user '~p' for ~p",[User,Dir]),
+ Ret = unblock_user_int({User, Addr, Port, Dir}),
+ ?vdebug("unblock user result: ~p",[Ret]),
+ {reply, Ret, Tables};
+
+
+handle_call({list_auth_users, Addr, Port, Dir}, _From, Tables) ->
+ ?vlog("list auth users for ~p",[Dir]),
+ Auth = list_auth(Tables, Addr, Port, Dir, []),
+ ?vdebug("list auth users result: ~p",[Auth]),
+ {reply, Auth, Tables};
+
+
+handle_call({new_table, Addr, Port, Name}, _From, Tables) ->
+ case lists:keysearch(Name, 1, Tables) of
+ {value, {Name, {Ets, Dets}}} ->
+ ?DEBUG("handle_call(new_table) -> we already have this table: ~p",
+ [Name]),
+ ?vdebug("new table; we already have this one: ~p",[Name]),
+ {reply, {ok, {Ets, Dets}}, Tables};
+ false ->
+ ?LOG("handle_call(new_table) -> new_table: Name = ~p",[Name]),
+ ?vlog("new table: ~p",[Name]),
+ TName = make_name(Addr,Port,length(Tables)),
+ ?DEBUG("handle_call(new_table) -> TName: ~p",[TName]),
+ ?vdebug("new table: ~p",[TName]),
+ case dets:open_file(TName, [{type, bag}, {file, Name},
+ {repair, true},
+ {access, read_write}]) of
+ {ok, DFile} ->
+ ETS = ets:new(TName, [bag, private]),
+ sync_dets_to_ets(DFile, ETS),
+ NewTables = [{Name, {ETS, DFile}}|Tables],
+ ?DEBUG("handle_call(new_table) -> ~n"
+ " NewTables: ~p",[NewTables]),
+ ?vtrace("new tables: ~p",[NewTables]),
+ {reply, {ok, {ETS, DFile}}, NewTables};
+ {error, Err} ->
+ ?LOG("handle_call -> Err: ~p",[Err]),
+ ?vinfo("failed open dets file: ~p",[Err]),
+ {reply, {error, {create_dets, Err}}, Tables}
+ end
+ end;
+
+handle_call(delete_tables, _From, Tables) ->
+ ?vlog("delete tables",[]),
+ lists:foreach(fun({Name, {ETS, DETS}}) ->
+ dets:close(DETS),
+ ets:delete(ETS)
+ end, Tables),
+ {reply, ok, []};
+
+handle_call({check_blocked_user, [Info, User, SDirData]}, _From, Tables) ->
+ ?vlog("check blocked user '~p'",[User]),
+ {ETS, DETS} = httpd_util:key1search(SDirData, data_file),
+ Dir = httpd_util:key1search(SDirData, path),
+ Addr = httpd_util:key1search(SDirData, bind_address),
+ Port = httpd_util:key1search(SDirData, port),
+ CBModule = httpd_util:key1search(SDirData, callback_module, no_module_at_all),
+ ?vdebug("call back module: ~p",[CBModule]),
+ Ret = check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule),
+ ?vdebug("check result: ~p",[Ret]),
+ {reply, Ret, Tables};
+handle_call(Request,From,Tables) ->
+ ?vinfo("~n unknown call '~p' from ~p",[Request,From]),
+ {reply,ok,Tables}.
+
+
+%% handle_cast
+
+handle_cast({store_failed_auth, [Info, DecodedString, SDirData]}, Tables) ->
+ ?vlog("store failed auth",[]),
+ {ETS, DETS} = httpd_util:key1search(SDirData, data_file),
+ Dir = httpd_util:key1search(SDirData, path),
+ Addr = httpd_util:key1search(SDirData, bind_address),
+ Port = httpd_util:key1search(SDirData, port),
+ {ok, [User,Password]} = httpd_util:split(DecodedString,":",2),
+ ?vdebug("user '~p' and password '~p'",[User,Password]),
+ Seconds = universal_time(),
+ Key = {User, Dir, Addr, Port},
+
+ %% Event
+ CBModule = httpd_util:key1search(SDirData, callback_module, no_module_at_all),
+ ?vtrace("call back module: ~p",[CBModule]),
+ auth_fail_event(CBModule,Addr,Port,Dir,User,Password),
+
+ %% Find out if any of this user's other failed logins are too old to keep..
+ ?vtrace("remove old login failures",[]),
+ case ets:match_object(ETS, {failed, {Key, '_', '_'}}) of
+ [] ->
+ ?vtrace("no old login failures",[]),
+ no;
+ List when list(List) ->
+ ?vtrace("~p old login failures",[length(List)]),
+ ExpireTime = httpd_util:key1search(SDirData, fail_expire_time, 30)*60,
+ ?vtrace("expire time ~p",[ExpireTime]),
+ lists:map(fun({failed, {TheKey, LS, Gen}}) ->
+ Diff = Seconds-LS,
+ if
+ Diff > ExpireTime ->
+ ?vtrace("~n '~p' is to old to keep: ~p",
+ [TheKey,Gen]),
+ ets:match_delete(ETS, {failed, {TheKey, LS, Gen}}),
+ dets:match_delete(DETS, {failed, {TheKey, LS, Gen}});
+ true ->
+ ?vtrace("~n '~p' is not old enough: ~p",
+ [TheKey,Gen]),
+ ok
+ end
+ end,
+ List);
+ O ->
+ ?vlog("~n unknown login failure search resuylt: ~p",[O]),
+ no
+ end,
+
+ %% Insert the new failure..
+ Generation = length(ets:match_object(ETS, {failed, {Key, '_', '_'}})),
+ ?vtrace("insert ('~p') new login failure: ~p",[Key,Generation]),
+ ets:insert(ETS, {failed, {Key, Seconds, Generation}}),
+ dets:insert(DETS, {failed, {Key, Seconds, Generation}}),
+
+ %% See if we should block this user..
+ MaxRetries = httpd_util:key1search(SDirData, max_retries, 3),
+ BlockTime = httpd_util:key1search(SDirData, block_time, 60),
+ ?vtrace("~n Max retries ~p, block time ~p",[MaxRetries,BlockTime]),
+ case ets:match_object(ETS, {failed, {Key, '_', '_'}}) of
+ List1 ->
+ ?vtrace("~n ~p tries so far",[length(List1)]),
+ if
+ length(List1) >= MaxRetries ->
+ %% Block this user until Future
+ ?vtrace("block user '~p'",[User]),
+ Future = Seconds+BlockTime*60,
+ ?vtrace("future: ~p",[Future]),
+ Reason = io_lib:format("Blocking user ~s from dir ~s "
+ "for ~p minutes",
+ [User, Dir, BlockTime]),
+ mod_log:security_log(Info, lists:flatten(Reason)),
+
+ %% Event
+ user_block_event(CBModule,Addr,Port,Dir,User),
+
+ ets:match_delete(ETS,{blocked_user,
+ {User, Addr, Port, Dir, '$1'}}),
+ dets:match_delete(DETS, {blocked_user,
+ {User, Addr, Port, Dir, '$1'}}),
+ BlockRecord = {blocked_user,
+ {User, Addr, Port, Dir, Future}},
+ ets:insert(ETS, BlockRecord),
+ dets:insert(DETS, BlockRecord),
+ %% Remove previous failed requests.
+ ets:match_delete(ETS, {failed, {Key, '_', '_'}}),
+ dets:match_delete(DETS, {failed, {Key, '_', '_'}});
+ true ->
+ ?vtrace("still some tries to go",[]),
+ no
+ end;
+ Other ->
+ no
+ end,
+ {noreply, Tables};
+
+handle_cast({store_successful_auth, [User, Addr, Port, SDirData]}, Tables) ->
+ ?vlog("store successfull auth",[]),
+ {ETS, DETS} = httpd_util:key1search(SDirData, data_file),
+ AuthTimeOut = httpd_util:key1search(SDirData, auth_timeout, 30),
+ Dir = httpd_util:key1search(SDirData, path),
+ Key = {User, Dir, Addr, Port},
+
+ %% Remove failed entries for this Key
+ dets:match_delete(DETS, {failed, {Key, '_', '_'}}),
+ ets:match_delete(ETS, {failed, {Key, '_', '_'}}),
+
+ %% Keep track of when the last successful login took place.
+ Seconds = universal_time()+AuthTimeOut,
+ ets:match_delete(ETS, {success, {Key, '_'}}),
+ dets:match_delete(DETS, {success, {Key, '_'}}),
+ ets:insert(ETS, {success, {Key, Seconds}}),
+ dets:insert(DETS, {success, {Key, Seconds}}),
+ {noreply, Tables};
+
+handle_cast(Req, Tables) ->
+ ?vinfo("~n unknown cast '~p'",[Req]),
+ error_msg("security server got unknown cast: ~p",[Req]),
+ {noreply, Tables}.
+
+
+%% handle_info
+
+handle_info(Info, State) ->
+ ?vinfo("~n unknown info '~p'",[Info]),
+ {noreply, State}.
+
+
+%% terminate
+
+terminate(Reason, _Tables) ->
+ ?vlog("~n Terminating for reason: ~p",[Reason]),
+ ok.
+
+
+%% code_change({down, ToVsn}, State, Extra)
+%%
+code_change({down, _}, State, _Extra) ->
+ ?vlog("downgrade", []),
+ {ok, State};
+
+
+%% code_change(FromVsn, State, Extra)
+%%
+code_change(_, State, Extra) ->
+ ?vlog("upgrade", []),
+ {ok, State}.
+
+
+
+
+%% block_user_int/2
+block_user_int({User, Addr, Port, Dir, Time}) ->
+ Dirs = httpd_manager:config_match(Addr, Port, {security_directory, '_'}),
+ ?vtrace("block '~p' for ~p during ~p",[User,Dir,Time]),
+ case find_dirdata(Dirs, Dir) of
+ {ok, DirData, {ETS, DETS}} ->
+ Time1 =
+ case Time of
+ infinity ->
+ 99999999999999999999999999999;
+ _ ->
+ Time
+ end,
+ Future = universal_time()+Time1,
+ ets:match_delete(ETS, {blocked_user, {User,Addr,Port,Dir,'_'}}),
+ dets:match_delete(DETS, {blocked_user, {User,Addr,Port,Dir,'_'}}),
+ ets:insert(ETS, {blocked_user, {User,Addr,Port,Dir,Future}}),
+ dets:insert(DETS, {blocked_user, {User,Addr,Port,Dir,Future}}),
+ CBModule = httpd_util:key1search(DirData, callback_module,
+ no_module_at_all),
+ ?vtrace("call back module ~p",[CBModule]),
+ user_block_event(CBModule,Addr,Port,Dir,User),
+ true;
+ _ ->
+ {error, no_such_directory}
+ end.
+
+
+find_dirdata([], _Dir) ->
+ false;
+find_dirdata([{security_directory, DirData}|SDirs], Dir) ->
+ case lists:keysearch(path, 1, DirData) of
+ {value, {path, Dir}} ->
+ {value, {data_file, {ETS, DETS}}} =
+ lists:keysearch(data_file, 1, DirData),
+ {ok, DirData, {ETS, DETS}};
+ _ ->
+ find_dirdata(SDirs, Dir)
+ end.
+
+%% unblock_user_int/2
+
+unblock_user_int({User, Addr, Port, Dir}) ->
+ ?vtrace("unblock user '~p' for ~p",[User,Dir]),
+ Dirs = httpd_manager:config_match(Addr, Port, {security_directory, '_'}),
+ ?vtrace("~n dirs: ~p",[Dirs]),
+ case find_dirdata(Dirs, Dir) of
+ {ok, DirData, {ETS, DETS}} ->
+ case ets:match_object(ETS,{blocked_user,{User,Addr,Port,Dir,'_'}}) of
+ [] ->
+ ?vtrace("not blocked",[]),
+ {error, not_blocked};
+ Objects ->
+ ets:match_delete(ETS, {blocked_user,
+ {User, Addr, Port, Dir, '_'}}),
+ dets:match_delete(DETS, {blocked_user,
+ {User, Addr, Port, Dir, '_'}}),
+ CBModule = httpd_util:key1search(DirData, callback_module,
+ no_module_at_all),
+ user_unblock_event(CBModule,Addr,Port,Dir,User),
+ true
+ end;
+ _ ->
+ ?vlog("~n cannot unblock: no such directory '~p'",[Dir]),
+ {error, no_such_directory}
+ end.
+
+
+
+%% list_auth/2
+
+list_auth([], _Addr, _Port, Dir, Acc) ->
+ Acc;
+list_auth([{Name, {ETS, DETS}}|Tables], Addr, Port, Dir, Acc) ->
+ case ets:match_object(ETS, {success, {{'_', Dir, Addr, Port}, '_'}}) of
+ [] ->
+ list_auth(Tables, Addr, Port, Dir, Acc);
+ List when list(List) ->
+ TN = universal_time(),
+ NewAcc = lists:foldr(fun({success,{{U,Ad,P,D},T}},Ac) ->
+ if
+ T-TN > 0 ->
+ [U|Ac];
+ true ->
+ Rec = {success,{{U,Ad,P,D},T}},
+ ets:match_delete(ETS,Rec),
+ dets:match_delete(DETS,Rec),
+ Ac
+ end
+ end,
+ Acc, List),
+ list_auth(Tables, Addr, Port, Dir, NewAcc);
+ _ ->
+ list_auth(Tables, Addr, Port, Dir, Acc)
+ end.
+
+
+%% list_blocked/2
+
+list_blocked([], Addr, Port, Dir, Acc) ->
+ TN = universal_time(),
+ lists:foldl(fun({U,Ad,P,D,T}, Ac) ->
+ if
+ T-TN > 0 ->
+ [{U,Ad,P,D,local_time(T)}|Ac];
+ true ->
+ Ac
+ end
+ end,
+ [], Acc);
+list_blocked([{Name, {ETS, DETS}}|Tables], Addr, Port, Dir, Acc) ->
+ NewBlocked =
+ case ets:match_object(ETS, {blocked_user, {'_',Addr,Port,Dir,'_'}}) of
+ List when list(List) ->
+ lists:foldl(fun({blocked_user, X}, A) -> [X|A] end, Acc, List);
+ _ ->
+ Acc
+ end,
+ list_blocked(Tables, Addr, Port, Dir, NewBlocked).
+
+
+%%
+%% sync_dets_to_ets/2
+%%
+%% Reads dets-table DETS and syncronizes it with the ets-table ETS.
+%%
+sync_dets_to_ets(DETS, ETS) ->
+ dets:traverse(DETS, fun(X) ->
+ ets:insert(ETS, X),
+ continue
+ end).
+
+%%
+%% check_blocked_user/7 -> true | false
+%%
+%% Check if a specific user is blocked from access.
+%%
+%% The sideeffect of this routine is that it unblocks also other users
+%% whos blocking time has expired. This to keep the tables as small
+%% as possible.
+%%
+check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule) ->
+ TN = universal_time(),
+ case ets:match_object(ETS, {blocked_user, {User, '_', '_', '_', '_'}}) of
+ List when list(List) ->
+ Blocked = lists:foldl(fun({blocked_user, X}, A) ->
+ [X|A] end, [], List),
+ check_blocked_user(Info,User,Dir,Addr,Port,ETS,DETS,TN,Blocked,CBModule);
+ _ ->
+ false
+ end.
+check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, TN, [], CBModule) ->
+ false;
+check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, TN,
+ [{User,Addr,Port,Dir,T}|Ls], CBModule) ->
+ TD = T-TN,
+ if
+ TD =< 0 ->
+ %% Blocking has expired, remove and grant access.
+ unblock_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule),
+ false;
+ true ->
+ true
+ end;
+check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, TN,
+ [{OUser,ODir,OAddr,OPort,T}|Ls], CBModule) ->
+ TD = T-TN,
+ if
+ TD =< 0 ->
+ %% Blocking has expired, remove.
+ unblock_user(Info, OUser, ODir, OAddr, OPort, ETS, DETS, CBModule);
+ true ->
+ true
+ end,
+ check_blocked_user(Info, User, Dir, Addr, Port, ETS, DETS, TN, Ls, CBModule).
+
+unblock_user(Info, User, Dir, Addr, Port, ETS, DETS, CBModule) ->
+ Reason=io_lib:format("User ~s was removed from the block list for dir ~s",
+ [User, Dir]),
+ mod_log:security_log(Info, lists:flatten(Reason)),
+ user_unblock_event(CBModule,Addr,Port,Dir,User),
+ dets:match_delete(DETS, {blocked_user, {User, Addr, Port, Dir, '_'}}),
+ ets:match_delete(ETS, {blocked_user, {User, Addr, Port, Dir, '_'}}).
+
+
+make_name(Addr,Port) ->
+ httpd_util:make_name("httpd_security",Addr,Port).
+
+make_name(Addr,Port,Num) ->
+ httpd_util:make_name("httpd_security",Addr,Port,
+ "__" ++ integer_to_list(Num)).
+
+
+auth_fail_event(Mod,Addr,Port,Dir,User,Passwd) ->
+ event(auth_fail,Mod,Addr,Port,Dir,[{user,User},{password,Passwd}]).
+
+user_block_event(Mod,Addr,Port,Dir,User) ->
+ event(user_block,Mod,Addr,Port,Dir,[{user,User}]).
+
+user_unblock_event(Mod,Addr,Port,Dir,User) ->
+ event(user_unblock,Mod,Addr,Port,Dir,[{user,User}]).
+
+event(Event,Mod,undefined,Port,Dir,Info) ->
+ (catch Mod:event(Event,Port,Dir,Info));
+event(Event,Mod,Addr,Port,Dir,Info) ->
+ (catch Mod:event(Event,Addr,Port,Dir,Info)).
+
+universal_time() ->
+ calendar:datetime_to_gregorian_seconds(calendar:universal_time()).
+
+local_time(T) ->
+ calendar:universal_time_to_local_time(
+ calendar:gregorian_seconds_to_datetime(T)).
+
+
+error_msg(F, A) ->
+ error_logger:error_msg(F, A).
+
+
+call(Name, Req) ->
+ case (catch gen_server:call(Name, Req)) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Reply ->
+ Reply
+ end.
+
+
+cast(Name, Msg) ->
+ case (catch gen_server:cast(Name, Msg)) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Result ->
+ Result
+ end.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_trace.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_trace.erl
new file mode 100644
index 0000000000..51fe6d283a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/mod_trace.erl
@@ -0,0 +1,69 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mod_trace.erl,v 1.1 2008/12/17 09:53:36 mikpe Exp $
+%%
+-module(mod_trace).
+
+-export([do/1]).
+
+-include("httpd.hrl").
+
+
+do(Info) ->
+ %%?vtrace("do",[]),
+ case Info#mod.method of
+ "TRACE" ->
+ case httpd_util:response_generated(Info) of
+ false->
+ generate_trace_response(Info);
+ true->
+ {proceed,Info#mod.data}
+ end;
+ _ ->
+ {proceed,Info#mod.data}
+ end.
+
+
+%%---------------------------------------------------------------------
+%%Generate the trace response the trace response consists of a
+%%http-header and the body will be the request.
+%5----------------------------------------------------------------------
+
+generate_trace_response(Info)->
+ RequestHead=Info#mod.parsed_header,
+ Body=generate_trace_response_body(RequestHead),
+ Len=length(Body),
+ Response=["HTTP/1.1 200 OK\r\n",
+ "Content-Type:message/http\r\n",
+ "Content-Length:",integer_to_list(Len),"\r\n\r\n",
+ Info#mod.request_line,Body],
+ httpd_socket:deliver(Info#mod.socket_type,Info#mod.socket,Response),
+ {proceed,[{response,{already_sent,200,Len}}|Info#mod.data]}.
+
+generate_trace_response_body(Parsed_header)->
+ generate_trace_response_body(Parsed_header,[]).
+
+generate_trace_response_body([],Head)->
+ lists:flatten(Head);
+generate_trace_response_body([{[],[]}|Rest],Head) ->
+ generate_trace_response_body(Rest,Head);
+generate_trace_response_body([{Field,Value}|Rest],Head) ->
+ generate_trace_response_body(Rest,[Field ++ ":" ++ Value ++ "\r\n"|Head]).
+
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/uri.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/uri.erl
new file mode 100644
index 0000000000..e1acd62a31
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/inets/uri.erl
@@ -0,0 +1,349 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Mobile Arts AB
+%% Portions created by Mobile Arts are Copyright 2002, Mobile Arts AB
+%% All Rights Reserved.''
+%%
+%%
+%% Author : Johan Blom <[email protected]>
+%% Description :
+%% Implements various scheme dependent subsets (e.g. HTTP, FTP etc) based on
+%% RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax
+%% Created : 27 Jul 2001 by Johan Blom <[email protected]>
+%%
+
+-module(uri).
+
+-author('[email protected]').
+
+-export([parse/1,resolve/2]).
+
+
+%%% Parse URI and return {Scheme,Path}
+%%% Note that Scheme specific parsing/validation is not handled here!
+resolve(Root,Rel) ->
+ ok.
+
+%%% See "http://www.isi.edu/in-notes/iana/assignments/url-schemes" for a list of
+%%% defined URL schemes and references to its sources.
+
+parse(URI) ->
+ case parse_scheme(URI) of
+ {http,Cont} -> parse_http(Cont,http);
+ {https,Cont} -> parse_http(Cont,https);
+ {ftp,Cont} -> parse_ftp(Cont,ftp);
+ {sip,Cont} -> parse_sip(Cont,sip);
+ {sms,Cont} -> parse_sms(Cont,sip);
+ {error,Error} -> {error,Error};
+ {Scheme,Cont} -> {Scheme,Cont}
+ end.
+
+
+%%% Parse the scheme.
+parse_scheme(URI) ->
+ parse_scheme(URI,[]).
+
+parse_scheme([H|URI],Acc) when $a=<H,H=<$z; $A=<H,H=<$Z ->
+ parse_scheme2(URI,[H|Acc]);
+parse_scheme(_,_) ->
+ {error,no_scheme}.
+
+parse_scheme2([H|URI],Acc)
+ when $a=<H,H=<$z; $A=<H,H=<$Z; $0=<H,H=<$9; H==$-;H==$+;H==$. ->
+ parse_scheme2(URI,[H|Acc]);
+parse_scheme2([$:|URI],Acc) ->
+ {list_to_atom(lists:reverse(Acc)),URI};
+parse_scheme2(_,_) ->
+ {error,no_scheme}.
+
+
+%%% ............................................................................
+-define(HTTP_DEFAULT_PORT, 80).
+-define(HTTPS_DEFAULT_PORT, 443).
+
+%%% HTTP (Source RFC 2396, RFC 2616)
+%%% http_URL = "*" | absoluteURI | abs_path [ "?" query ] | authority
+
+%%% http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
+%%% Returns a tuple {http,Host,Port,PathQuery} where
+%%% Host = string() Host value
+%%% Port = string() Port value
+%%% PathQuery= string() Combined absolute path and query value
+parse_http("//"++C0,Scheme) ->
+ case scan_hostport(C0,Scheme) of
+ {C1,Host,Port} ->
+ case scan_pathquery(C1) of
+ {error,Error} ->
+ {error,Error};
+ PathQuery ->
+ {Scheme,Host,Port,PathQuery}
+ end;
+ {error,Error} ->
+ {error,Error}
+ end;
+parse_http(_,_) ->
+ {error,invalid_url}.
+
+scan_pathquery(C0) ->
+ case scan_abspath(C0) of
+ {error,Error} ->
+ {error,Error};
+ {[],[]} -> % Add implicit path
+ "/";
+ {"?"++C1,Path} ->
+ case scan_query(C1,[]) of
+ {error,Error} ->
+ {error,Error};
+ Query ->
+ Path++"?"++Query
+ end;
+ {[],Path} ->
+ Path
+ end.
+
+
+%%% ............................................................................
+%%% FIXME!!! This is just a quick hack that doesn't work!
+-define(FTP_DEFAULT_PORT, 80).
+
+%%% FTP (Source RFC 2396, RFC 1738, RFC 959)
+%%% Note: This BNF has been modified to better fit with RFC 2396
+%%% ftp_URL = "ftp:" "//" [ ftp_userinfo ] host [ ":" port ] ftp_abs_path
+%%% ftp_userinfo = ftp_user [ ":" ftp_password ]
+%%% ftp_abs_path = "/" ftp_path_segments [ ";type=" ftp_type ]
+%%% ftp_path_segments = ftp_segment *( "/" ftp_segment)
+%%% ftp_segment = *[ ftp_uchar | "?" | ":" | "@" | "&" | "=" ]
+%%% ftp_type = "A" | "I" | "D" | "a" | "i" | "d"
+%%% ftp_user = *[ ftp_uchar | ";" | "?" | "&" | "=" ]
+%%% ftp_password = *[ ftp_uchar | ";" | "?" | "&" | "=" ]
+%%% ftp_uchar = ftp_unreserved | escaped
+%%% ftp_unreserved = alphanum | mark | "$" | "+" | ","
+parse_ftp("//"++C0,Scheme) ->
+ case ftp_userinfo(C0) of
+ {C1,Creds} ->
+ case scan_hostport(C1,Scheme) of
+ {C2,Host,Port} ->
+ case scan_abspath(C2) of
+ {error,Error} ->
+ {error,Error};
+ {[],[]} -> % Add implicit path
+ {Scheme,Creds,Host,Port,"/"};
+ {[],Path} ->
+ {Scheme,Creds,Host,Port,Path}
+ end;
+ {error,Error} ->
+ {error,Error}
+ end;
+ {error,Error} ->
+ {error,Error}
+ end.
+
+ftp_userinfo(C0) ->
+ User="",
+ Password="",
+ {C0,{User,Password}}.
+
+
+%%% ............................................................................
+%%% SIP (Source RFC 2396, RFC 2543)
+%%% sip_URL = "sip:" [ sip_userinfo "@" ] host [ ":" port ]
+%%% sip_url-parameters [ sip_headers ]
+%%% sip_userinfo = sip_user [ ":" sip_password ]
+%%% sip_user = *( unreserved | escaped | "&" | "=" | "+" | "$" | "," )
+%%% sip_password = *( unreserved | escaped | "&" | "=" | "+" | "$" | "," )
+%%% sip_url-parameters = *( ";" sip_url-parameter )
+%%% sip_url-parameter = sip_transport-param | sip_user-param |
+%%% sip_method-param | sip_ttl-param |
+%%% sip_maddr-param | sip_other-param
+%%% sip_transport-param = "transport=" ( "udp" | "tcp" )
+%%% sip_ttl-param = "ttl=" sip_ttl
+%%% sip_ttl = 1*3DIGIT ; 0 to 255
+%%% sip_maddr-param = "maddr=" host
+%%% sip_user-param = "user=" ( "phone" | "ip" )
+%%% sip_method-param = "method=" sip_Method
+%%% sip_tag-param = "tag=" sip_UUID
+%%% sip_UUID = 1*( hex | "-" )
+%%% sip_other-param = ( token | ( token "=" ( token | quoted-string )))
+%%% sip_Method = "INVITE" | "ACK" | "OPTIONS" | "BYE" |
+%%% "CANCEL" | "REGISTER"
+%%% sip_token = 1*< any CHAR except CTL's or separators>
+%%% sip_quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+%%% sip_qdtext = <any TEXT-UTF8 except <">>
+%%% sip_quoted-pair = " \ " CHAR
+parse_sip(Cont,Scheme) ->
+ {Scheme,Cont}.
+
+
+
+
+%%% ............................................................................
+%%% SMS (Source draft-wilde-sms-uri-01, January 24 2002 and
+%%% draft-allocchio-gstn-01, November 2001)
+%%% The syntax definition for "gstn-phone" is taken from
+%%% [draft-allocchio-gstn-01], allowing global as well as local telephone
+%%% numbers.
+%%% Note: This BNF has been modified to better fit with RFC 2396
+%%% sms_URI = sms ":" 1*( sms-recipient ) [ sms-body ]
+%%% sms-recipient = gstn-phone sms-qualifier
+%%% [ "," sms-recipient ]
+%%% sms-qualifier = *( smsc-qualifier / pid-qualifier )
+%%% smsc-qualifier = ";smsc=" SMSC-sub-addr
+%%% pid-qualifier = ";pid=" PID-sub-addr
+%%% sms-body = ";body=" *urlc
+%%% gstn-phone = ( global-phone / local-phone )
+%%% global-phone = "+" 1*( DIGIT / written-sep )
+%%% local-phone = [ exit-code ] dial-number / exit-code [ dial-number ]
+%%% exit-code = phone-string
+%%% dial-number = phone-string
+%%% subaddr-string = phone-string
+%%% post-dial = phone-string
+%%% phone-string = 1*( DTMF / pause / tonewait / written-sep )
+%%% DTMF = ( DIGIT / "#" / "*" / "A" / "B" / "C" / "D" )
+%%% written-sep = ( "-" / "." )
+%%% pause = "p"
+%%% tonewait = "w"
+parse_sms(Cont,Scheme) ->
+ {Scheme,Cont}.
+
+
+%%% ============================================================================
+%%% Generic URI parsing. BNF rules from RFC 2396
+
+%%% hostport = host [ ":" port ]
+scan_hostport(C0,Scheme) ->
+ case scan_host(C0) of
+ {error,Error} ->
+ {error,Error};
+ {":"++C1,Host} ->
+ {C2,Port}=scan_port(C1,[]),
+ {C2,Host,list_to_integer(Port)};
+ {C1,Host} when Scheme==http ->
+ {C1,Host,?HTTP_DEFAULT_PORT};
+ {C1,Host} when Scheme==https ->
+ {C1,Host,?HTTPS_DEFAULT_PORT};
+ {C1,Host} when Scheme==ftp ->
+ {C1,Host,?FTP_DEFAULT_PORT}
+ end.
+
+
+%%% host = hostname | IPv4address
+%%% hostname = *( domainlabel "." ) toplabel [ "." ]
+%%% domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+%%% toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+%%% IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
+
+-define(ALPHA, 1).
+-define(DIGIT, 2).
+
+scan_host(C0) ->
+ case scan_host2(C0,[],0,[],[]) of
+ {C1,IPv4address,[?DIGIT,?DIGIT,?DIGIT,?DIGIT]} ->
+ {C1,lists:reverse(lists:append(IPv4address))};
+ {C1,Hostname,[?ALPHA|HostF]} ->
+ {C1,lists:reverse(lists:append(Hostname))};
+ _ ->
+ {error,no_host}
+ end.
+
+scan_host2([H|C0],Acc,CurF,Host,HostF) when $0=<H,H=<$9 ->
+ scan_host2(C0,[H|Acc],CurF bor ?DIGIT,Host,HostF);
+scan_host2([H|C0],Acc,CurF,Host,HostF) when $a=<H,H=<$z; $A=<H,H=<$Z ->
+ scan_host2(C0,[H|Acc],CurF bor ?ALPHA,Host,HostF);
+scan_host2([$-|C0],Acc,CurF,Host,HostF) when CurF=/=0 ->
+ scan_host2(C0,[$-|Acc],CurF,Host,HostF);
+scan_host2([$.|C0],Acc,CurF,Host,HostF) when CurF=/=0 ->
+ scan_host2(C0,[],0,[".",Acc|Host],[CurF|HostF]);
+scan_host2(C0,Acc,CurF,Host,HostF) ->
+ {C0,[Acc|Host],[CurF|HostF]}.
+
+
+%%% port = *digit
+scan_port([H|C0],Acc) when $0=<H,H=<$9 ->
+ scan_port(C0,[H|Acc]);
+scan_port(C0,Acc) ->
+ {C0,lists:reverse(Acc)}.
+
+%%% abs_path = "/" path_segments
+scan_abspath([]) ->
+ {[],[]};
+scan_abspath("/"++C0) ->
+ scan_pathsegments(C0,["/"]);
+scan_abspath(_) ->
+ {error,no_abspath}.
+
+%%% path_segments = segment *( "/" segment )
+scan_pathsegments(C0,Acc) ->
+ case scan_segment(C0,[]) of
+ {"/"++C1,Segment} ->
+ scan_pathsegments(C1,["/",Segment|Acc]);
+ {C1,Segment} ->
+ {C1,lists:reverse(lists:append([Segment|Acc]))}
+ end.
+
+
+%%% segment = *pchar *( ";" param )
+%%% param = *pchar
+scan_segment(";"++C0,Acc) ->
+ {C1,ParamAcc}=scan_pchars(C0,";"++Acc),
+ scan_segment(C1,ParamAcc);
+scan_segment(C0,Acc) ->
+ case scan_pchars(C0,Acc) of
+ {";"++C1,Segment} ->
+ {C2,ParamAcc}=scan_pchars(C1,";"++Segment),
+ scan_segment(C2,ParamAcc);
+ {C1,Segment} ->
+ {C1,Segment}
+ end.
+
+%%% query = *uric
+%%% uric = reserved | unreserved | escaped
+%%% reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+%%% "$" | ","
+%%% unreserved = alphanum | mark
+%%% mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+%%% "(" | ")"
+%%% escaped = "%" hex hex
+scan_query([],Acc) ->
+ lists:reverse(Acc);
+scan_query([$%,H1,H2|C0],Acc) -> % escaped
+ scan_query(C0,[hex2dec(H1)*16+hex2dec(H2)|Acc]);
+scan_query([H|C0],Acc) when $a=<H,H=<$z;$A=<H,H=<$Z;$0=<H,H=<$9 -> % alphanum
+ scan_query(C0,[H|Acc]);
+scan_query([H|C0],Acc) when H==$;; H==$/; H==$?; H==$:; H==$@;
+ H==$&; H==$=; H==$+; H==$$; H==$, -> % reserved
+ scan_query(C0,[H|Acc]);
+scan_query([H|C0],Acc) when H==$-; H==$_; H==$.; H==$!; H==$~;
+ H==$*; H==$'; H==$(; H==$) -> % mark
+ scan_query(C0,[H|Acc]);
+scan_query([H|C0],Acc) ->
+ {error,no_query}.
+
+
+%%% pchar = unreserved | escaped |
+%%% ":" | "@" | "&" | "=" | "+" | "$" | ","
+scan_pchars([],Acc) ->
+ {[],Acc};
+scan_pchars([$%,H1,H2|C0],Acc) -> % escaped
+ scan_pchars(C0,[hex2dec(H1)*16+hex2dec(H2)|Acc]);
+scan_pchars([H|C0],Acc) when $a=<H,H=<$z;$A=<H,H=<$Z;$0=<H,H=<$9 -> % alphanum
+ scan_pchars(C0,[H|Acc]);
+scan_pchars([H|C0],Acc) when H==$-; H==$_; H==$.; H==$!; H==$~;
+ H==$*; H==$'; H==$(; H==$) -> % mark
+ scan_pchars(C0,[H|Acc]);
+scan_pchars([H|C0],Acc) when H==$:; H==$@; H==$&; H==$=; H==$+; H==$$; H==$, ->
+ scan_pchars(C0,[H|Acc]);
+scan_pchars(C0,Acc) ->
+ {C0,Acc}.
+
+hex2dec(X) when X>=$0,X=<$9 -> X-$0;
+hex2dec(X) when X>=$A,X=<$F -> X-$A+10;
+hex2dec(X) when X>=$a,X=<$f -> X-$a+10.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/Makefile b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/Makefile
new file mode 100644
index 0000000000..461dc82155
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/Makefile
@@ -0,0 +1,137 @@
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id: Makefile,v 1.1 2008/12/17 09:53:37 mikpe Exp $
+#
+include $(ERL_TOP)/make/target.mk
+
+ifeq ($(TYPE),debug)
+ERL_COMPILE_FLAGS += -Ddebug -W
+endif
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(MNESIA_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/mnesia-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES= \
+ mnesia \
+ mnesia_backup \
+ mnesia_bup \
+ mnesia_checkpoint \
+ mnesia_checkpoint_sup \
+ mnesia_controller \
+ mnesia_dumper\
+ mnesia_event \
+ mnesia_frag \
+ mnesia_frag_hash \
+ mnesia_frag_old_hash \
+ mnesia_index \
+ mnesia_kernel_sup \
+ mnesia_late_loader \
+ mnesia_lib\
+ mnesia_loader \
+ mnesia_locker \
+ mnesia_log \
+ mnesia_monitor \
+ mnesia_recover \
+ mnesia_registry \
+ mnesia_schema\
+ mnesia_snmp_hook \
+ mnesia_snmp_sup \
+ mnesia_subscr \
+ mnesia_sup \
+ mnesia_sp \
+ mnesia_text \
+ mnesia_tm
+
+HRL_FILES= mnesia.hrl
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+APP_FILE= mnesia.app
+
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_FILE= mnesia.appup
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_FLAGS +=
+ERL_COMPILE_FLAGS += \
+ +warn_unused_vars \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,vsn,"mnesia_$(MNESIA_VSN)"}' \
+ -W
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+opt: $(TARGET_FILES)
+
+debug:
+ @${MAKE} TYPE=debug
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.app.src b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.app.src
new file mode 100644
index 0000000000..3715488ec2
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.app.src
@@ -0,0 +1,52 @@
+{application, mnesia,
+ [{description, "MNESIA CXC 138 12"},
+ {vsn, "%VSN%"},
+ {modules, [
+ mnesia,
+ mnesia_backup,
+ mnesia_bup,
+ mnesia_checkpoint,
+ mnesia_checkpoint_sup,
+ mnesia_controller,
+ mnesia_dumper,
+ mnesia_event,
+ mnesia_frag,
+ mnesia_frag_hash,
+ mnesia_frag_old_hash,
+ mnesia_index,
+ mnesia_kernel_sup,
+ mnesia_late_loader,
+ mnesia_lib,
+ mnesia_loader,
+ mnesia_locker,
+ mnesia_log,
+ mnesia_monitor,
+ mnesia_recover,
+ mnesia_registry,
+ mnesia_schema,
+ mnesia_snmp_hook,
+ mnesia_snmp_sup,
+ mnesia_subscr,
+ mnesia_sup,
+ mnesia_sp,
+ mnesia_text,
+ mnesia_tm
+ ]},
+ {registered, [
+ mnesia_dumper_load_regulator,
+ mnesia_event,
+ mnesia_fallback,
+ mnesia_controller,
+ mnesia_kernel_sup,
+ mnesia_late_loader,
+ mnesia_locker,
+ mnesia_monitor,
+ mnesia_recover,
+ mnesia_substr,
+ mnesia_sup,
+ mnesia_tm
+ ]},
+ {applications, [kernel, stdlib]},
+ {mod, {mnesia_sup, []}}]}.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.appup.src b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.appup.src
new file mode 100644
index 0000000000..502ddb02fc
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.appup.src
@@ -0,0 +1,6 @@
+{"%VSN%",
+ [
+ ],
+ [
+ ]
+}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.erl
new file mode 100644
index 0000000000..956f4f5395
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.erl
@@ -0,0 +1,2191 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia.erl,v 1.2 2010/03/04 13:54:19 maria Exp $
+%%
+%% This module exports the public interface of the Mnesia DBMS engine
+
+-module(mnesia).
+%-behaviour(mnesia_access).
+
+-export([
+ %% Start, stop and debugging
+ start/0, start/1, stop/0, % Not for public use
+ set_debug_level/1, lkill/0, kill/0, % Not for public use
+ ms/0, nc/0, nc/1, ni/0, ni/1, % Not for public use
+ change_config/2,
+
+ %% Activity mgt
+ abort/1, transaction/1, transaction/2, transaction/3,
+ sync_transaction/1, sync_transaction/2, sync_transaction/3,
+ async_dirty/1, async_dirty/2, sync_dirty/1, sync_dirty/2, ets/1, ets/2,
+ activity/2, activity/3, activity/4, % Not for public use
+
+ %% Access within an activity - Lock acquisition
+ lock/2, lock/4,
+ read_lock_table/1,
+ write_lock_table/1,
+
+ %% Access within an activity - Updates
+ write/1, s_write/1, write/3, write/5,
+ delete/1, s_delete/1, delete/3, delete/5,
+ delete_object/1, s_delete_object/1, delete_object/3, delete_object/5,
+
+ %% Access within an activity - Reads
+ read/1, wread/1, read/3, read/5,
+ match_object/1, match_object/3, match_object/5,
+ select/2, select/3, select/5,
+ all_keys/1, all_keys/4,
+ index_match_object/2, index_match_object/4, index_match_object/6,
+ index_read/3, index_read/6,
+
+ %% Iterators within an activity
+ foldl/3, foldl/4, foldr/3, foldr/4,
+
+ %% Dirty access regardless of activities - Updates
+ dirty_write/1, dirty_write/2,
+ dirty_delete/1, dirty_delete/2,
+ dirty_delete_object/1, dirty_delete_object/2,
+ dirty_update_counter/2, dirty_update_counter/3,
+
+ %% Dirty access regardless of activities - Read
+ dirty_read/1, dirty_read/2,
+ dirty_select/2,
+ dirty_match_object/1, dirty_match_object/2, dirty_all_keys/1,
+ dirty_index_match_object/2, dirty_index_match_object/3,
+ dirty_index_read/3, dirty_slot/2,
+ dirty_first/1, dirty_next/2, dirty_last/1, dirty_prev/2,
+
+ %% Info
+ table_info/2, table_info/4, schema/0, schema/1,
+ error_description/1, info/0, system_info/1,
+ system_info/0, % Not for public use
+
+ %% Database mgt
+ create_schema/1, delete_schema/1,
+ backup/1, backup/2, traverse_backup/4, traverse_backup/6,
+ install_fallback/1, install_fallback/2,
+ uninstall_fallback/0, uninstall_fallback/1,
+ activate_checkpoint/1, deactivate_checkpoint/1,
+ backup_checkpoint/2, backup_checkpoint/3, restore/2,
+
+ %% Table mgt
+ create_table/1, create_table/2, delete_table/1,
+ add_table_copy/3, del_table_copy/2, move_table_copy/3,
+ add_table_index/2, del_table_index/2,
+ transform_table/3, transform_table/4,
+ change_table_copy_type/3,
+ read_table_property/2, write_table_property/2, delete_table_property/2,
+ change_table_frag/2,
+ clear_table/1,
+
+ %% Table load
+ dump_tables/1, wait_for_tables/2, force_load_table/1,
+ change_table_access_mode/2, change_table_load_order/2,
+ set_master_nodes/1, set_master_nodes/2,
+
+ %% Misc admin
+ dump_log/0, subscribe/1, unsubscribe/1, report_event/1,
+
+ %% Snmp
+ snmp_open_table/2, snmp_close_table/1,
+ snmp_get_row/2, snmp_get_next_index/2, snmp_get_mnesia_key/2,
+
+ %% Textfile access
+ load_textfile/1, dump_to_textfile/1,
+
+ %% Mnemosyne exclusive
+ get_activity_id/0, put_activity_id/1, % Not for public use
+
+ %% Mnesia internal functions
+ dirty_rpc/4, % Not for public use
+ has_var/1, fun_select/7,
+ foldl/6, foldr/6,
+
+ %% Module internal callback functions
+ remote_dirty_match_object/2, % Not for public use
+ remote_dirty_select/2 % Not for public use
+ ]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [verbose/2]).
+
+-define(DEFAULT_ACCESS, ?MODULE).
+
+%% Select
+-define(PATTERN_TO_OBJECT_MATCH_SPEC(Pat), [{Pat,[],['$_']}]).
+-define(PATTERN_TO_BINDINGS_MATCH_SPEC(Pat), [{Pat,[],['$$']}]).
+
+%% Local function in order to avoid external function call
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
+
+is_dollar_digits(Var) ->
+ case atom_to_list(Var) of
+ [$$ | Digs] ->
+ is_digits(Digs);
+ _ ->
+ false
+ end.
+
+is_digits([Dig | Tail]) ->
+ if
+ $0 =< Dig, Dig =< $9 ->
+ is_digits(Tail);
+ true ->
+ false
+ end;
+is_digits([]) ->
+ true.
+
+has_var(X) when atom(X) ->
+ if
+ X == '_' ->
+ true;
+ atom(X) ->
+ is_dollar_digits(X);
+ true ->
+ false
+ end;
+has_var(X) when tuple(X) ->
+ e_has_var(X, size(X));
+has_var([H|T]) ->
+ case has_var(H) of
+ false -> has_var(T);
+ Other -> Other
+ end;
+has_var(_) -> false.
+
+e_has_var(_, 0) -> false;
+e_has_var(X, Pos) ->
+ case has_var(element(Pos, X))of
+ false -> e_has_var(X, Pos-1);
+ Other -> Other
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Start and stop
+
+start() ->
+ {Time , Res} = timer:tc(application, start, [?APPLICATION, temporary]),
+
+ Secs = Time div 1000000,
+ case Res of
+ ok ->
+ verbose("Mnesia started, ~p seconds~n",[ Secs]),
+ ok;
+ {error, {already_started, mnesia}} ->
+ verbose("Mnesia already started, ~p seconds~n",[ Secs]),
+ ok;
+ {error, R} ->
+ verbose("Mnesia failed to start, ~p seconds: ~p~n",[ Secs, R]),
+ {error, R}
+ end.
+
+start(ExtraEnv) when list(ExtraEnv) ->
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ patched_start(ExtraEnv);
+ Error ->
+ Error
+ end;
+start(ExtraEnv) ->
+ {error, {badarg, ExtraEnv}}.
+
+patched_start([{Env, Val} | Tail]) when atom(Env) ->
+ case mnesia_monitor:patch_env(Env, Val) of
+ {error, Reason} ->
+ {error, Reason};
+ _NewVal ->
+ patched_start(Tail)
+ end;
+patched_start([Head | _]) ->
+ {error, {bad_type, Head}};
+patched_start([]) ->
+ start().
+
+stop() ->
+ case application:stop(?APPLICATION) of
+ ok -> stopped;
+ {error, {not_started, ?APPLICATION}} -> stopped;
+ Other -> Other
+ end.
+
+change_config(extra_db_nodes, Ns) when list(Ns) ->
+ mnesia_controller:connect_nodes(Ns);
+change_config(BadKey, _BadVal) ->
+ {error, {badarg, BadKey}}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Debugging
+
+set_debug_level(Level) ->
+ mnesia_subscr:set_debug_level(Level).
+
+lkill() ->
+ mnesia_sup:kill().
+
+kill() ->
+ rpc:multicall(mnesia_sup, kill, []).
+
+ms() ->
+ [
+ mnesia,
+ mnesia_backup,
+ mnesia_bup,
+ mnesia_checkpoint,
+ mnesia_checkpoint_sup,
+ mnesia_controller,
+ mnesia_dumper,
+ mnesia_loader,
+ mnesia_frag,
+ mnesia_frag_hash,
+ mnesia_frag_old_hash,
+ mnesia_index,
+ mnesia_kernel_sup,
+ mnesia_late_loader,
+ mnesia_lib,
+ mnesia_log,
+ mnesia_registry,
+ mnesia_schema,
+ mnesia_snmp_hook,
+ mnesia_snmp_sup,
+ mnesia_subscr,
+ mnesia_sup,
+ mnesia_text,
+ mnesia_tm,
+ mnesia_recover,
+ mnesia_locker,
+
+ %% Keep these last in the list, so
+ %% mnesia_sup kills these last
+ mnesia_monitor,
+ mnesia_event
+ ].
+
+nc() ->
+ Mods = ms(),
+ nc(Mods).
+
+nc(Mods) when list(Mods)->
+ [Mod || Mod <- Mods, ok /= load(Mod, compile)].
+
+ni() ->
+ Mods = ms(),
+ ni(Mods).
+
+ni(Mods) when list(Mods) ->
+ [Mod || Mod <- Mods, ok /= load(Mod, interpret)].
+
+load(Mod, How) when atom(Mod) ->
+ case try_load(Mod, How) of
+ ok ->
+ ok;
+ _ ->
+ mnesia_lib:show( "~n RETRY ~p FROM: ", [Mod]),
+ Abs = mod2abs(Mod),
+ load(Abs, How)
+ end;
+load(Abs, How) ->
+ case try_load(Abs, How) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ mnesia_lib:show( " *** ERROR *** ~p~n", [Reason]),
+ {error, Reason}
+ end.
+
+try_load(Mod, How) ->
+ mnesia_lib:show( " ~p ", [Mod]),
+ Flags = [{d, debug}],
+ case How of
+ compile ->
+ case catch c:nc(Mod, Flags) of
+ {ok, _} -> ok;
+ Other -> {error, Other}
+ end;
+ interpret ->
+ case catch int:ni(Mod, Flags) of
+ {module, _} -> ok;
+ Other -> {error, Other}
+ end
+ end.
+
+mod2abs(Mod) ->
+ ModString = atom_to_list(Mod),
+ SubDir =
+ case lists:suffix("test", ModString) of
+ true -> test;
+ false -> src
+ end,
+ filename:join([code:lib_dir(?APPLICATION), SubDir, ModString]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Activity mgt
+
+abort(Reason) ->
+ exit({aborted, Reason}).
+
+transaction(Fun) ->
+ transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, async).
+transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
+ transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
+transaction(Fun, Retries) when Retries == infinity ->
+ transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async);
+transaction(Fun, Args) ->
+ transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, async).
+transaction(Fun, Args, Retries) ->
+ transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, async).
+
+sync_transaction(Fun) ->
+ transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, sync).
+sync_transaction(Fun, Retries) when integer(Retries), Retries >= 0 ->
+ transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
+sync_transaction(Fun, Retries) when Retries == infinity ->
+ transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync);
+sync_transaction(Fun, Args) ->
+ transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, sync).
+sync_transaction(Fun, Args, Retries) ->
+ transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, sync).
+
+
+transaction(State, Fun, Args, Retries, Mod, Kind)
+ when function(Fun), list(Args), Retries == infinity, atom(Mod) ->
+ mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
+transaction(State, Fun, Args, Retries, Mod, Kind)
+ when function(Fun), list(Args), integer(Retries), Retries >= 0, atom(Mod) ->
+ mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind);
+transaction(_State, Fun, Args, Retries, Mod, _Kind) ->
+ {aborted, {badarg, Fun, Args, Retries, Mod}}.
+
+non_transaction(State, Fun, Args, ActivityKind, Mod)
+ when function(Fun), list(Args), atom(Mod) ->
+ mnesia_tm:non_transaction(State, Fun, Args, ActivityKind, Mod);
+non_transaction(_State, Fun, Args, _ActivityKind, _Mod) ->
+ {aborted, {badarg, Fun, Args}}.
+
+async_dirty(Fun) ->
+ async_dirty(Fun, []).
+async_dirty(Fun, Args) ->
+ non_transaction(get(mnesia_activity_state), Fun, Args, async_dirty, ?DEFAULT_ACCESS).
+
+sync_dirty(Fun) ->
+ sync_dirty(Fun, []).
+sync_dirty(Fun, Args) ->
+ non_transaction(get(mnesia_activity_state), Fun, Args, sync_dirty, ?DEFAULT_ACCESS).
+
+ets(Fun) ->
+ ets(Fun, []).
+ets(Fun, Args) ->
+ non_transaction(get(mnesia_activity_state), Fun, Args, ets, ?DEFAULT_ACCESS).
+
+activity(Kind, Fun) ->
+ activity(Kind, Fun, []).
+activity(Kind, Fun, Args) when list(Args) ->
+ activity(Kind, Fun, Args, mnesia_monitor:get_env(access_module));
+activity(Kind, Fun, Mod) ->
+ activity(Kind, Fun, [], Mod).
+
+activity(Kind, Fun, Args, Mod) ->
+ State = get(mnesia_activity_state),
+ case Kind of
+ ets -> non_transaction(State, Fun, Args, Kind, Mod);
+ async_dirty -> non_transaction(State, Fun, Args, Kind, Mod);
+ sync_dirty -> non_transaction(State, Fun, Args, Kind, Mod);
+ transaction -> wrap_trans(State, Fun, Args, infinity, Mod, async);
+ {transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, async);
+ sync_transaction -> wrap_trans(State, Fun, Args, infinity, Mod, sync);
+ {sync_transaction, Retries} -> wrap_trans(State, Fun, Args, Retries, Mod, sync);
+ _ -> {aborted, {bad_type, Kind}}
+ end.
+
+wrap_trans(State, Fun, Args, Retries, Mod, Kind) ->
+ case transaction(State, Fun, Args, Retries, Mod, Kind) of
+ {'atomic', GoodRes} -> GoodRes;
+ BadRes -> exit(BadRes)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Access within an activity - lock acquisition
+
+%% Grab a lock on an item in the global lock table
+%% Item may be any term. Lock may be write or read.
+%% write lock is set on all the given nodes
+%% read lock is only set on the first node
+%% Nodes may either be a list of nodes or one node as an atom
+%% Mnesia on all Nodes must be connected to each other, but
+%% it is not neccessary that they are up and running.
+
+lock(LockItem, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ lock(Tid, Ts, LockItem, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:lock(Tid, Ts, LockItem, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+lock(Tid, Ts, LockItem, LockKind) ->
+ case element(1, Tid) of
+ tid ->
+ case LockItem of
+ {record, Tab, Key} ->
+ lock_record(Tid, Ts, Tab, Key, LockKind);
+ {table, Tab} ->
+ lock_table(Tid, Ts, Tab, LockKind);
+ {global, GlobalKey, Nodes} ->
+ global_lock(Tid, Ts, GlobalKey, LockKind, Nodes);
+ _ ->
+ abort({bad_type, LockItem})
+ end;
+ _Protocol ->
+ []
+ end.
+
+%% Grab a read lock on a whole table
+read_lock_table(Tab) ->
+ lock({table, Tab}, read),
+ ok.
+
+%% Grab a write lock on a whole table
+write_lock_table(Tab) ->
+ lock({table, Tab}, write),
+ ok.
+
+lock_record(Tid, Ts, Tab, Key, LockKind) when atom(Tab) ->
+ Store = Ts#tidstore.store,
+ Oid = {Tab, Key},
+ case LockKind of
+ read ->
+ mnesia_locker:rlock(Tid, Store, Oid);
+ write ->
+ mnesia_locker:wlock(Tid, Store, Oid);
+ sticky_write ->
+ mnesia_locker:sticky_wlock(Tid, Store, Oid);
+ none ->
+ [];
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end;
+lock_record(_Tid, _Ts, Tab, _Key, _LockKind) ->
+ abort({bad_type, Tab}).
+
+lock_table(Tid, Ts, Tab, LockKind) when atom(Tab) ->
+ Store = Ts#tidstore.store,
+ case LockKind of
+ read ->
+ mnesia_locker:rlock_table(Tid, Store, Tab);
+ write ->
+ mnesia_locker:wlock_table(Tid, Store, Tab);
+ sticky_write ->
+ mnesia_locker:sticky_wlock_table(Tid, Store, Tab);
+ none ->
+ [];
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end;
+lock_table(_Tid, _Ts, Tab, _LockKind) ->
+ abort({bad_type, Tab}).
+
+global_lock(Tid, Ts, Item, Kind, Nodes) when list(Nodes) ->
+ case element(1, Tid) of
+ tid ->
+ Store = Ts#tidstore.store,
+ GoodNs = good_global_nodes(Nodes),
+ if
+ Kind /= read, Kind /= write ->
+ abort({bad_type, Kind});
+ true ->
+ mnesia_locker:global_lock(Tid, Store, Item, Kind, GoodNs)
+ end;
+ _Protocol ->
+ []
+ end;
+global_lock(_Tid, _Ts, _Item, _Kind, Nodes) ->
+ abort({bad_type, Nodes}).
+
+good_global_nodes(Nodes) ->
+ Recover = [node() | val(recover_nodes)],
+ mnesia_lib:intersect(Nodes, Recover).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Access within an activity - updates
+
+write(Val) when tuple(Val), size(Val) > 2 ->
+ Tab = element(1, Val),
+ write(Tab, Val, write);
+write(Val) ->
+ abort({bad_type, Val}).
+
+s_write(Val) when tuple(Val), size(Val) > 2 ->
+ Tab = element(1, Val),
+ write(Tab, Val, sticky_write).
+
+write(Tab, Val, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ write(Tid, Ts, Tab, Val, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:write(Tid, Ts, Tab, Val, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+write(Tid, Ts, Tab, Val, LockKind)
+ when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
+ case element(1, Tid) of
+ ets ->
+ ?ets_insert(Tab, Val),
+ ok;
+ tid ->
+ Store = Ts#tidstore.store,
+ Oid = {Tab, element(2, Val)},
+ case LockKind of
+ write ->
+ mnesia_locker:wlock(Tid, Store, Oid);
+ sticky_write ->
+ mnesia_locker:sticky_wlock(Tid, Store, Oid);
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end,
+ write_to_store(Tab, Store, Oid, Val);
+ Protocol ->
+ do_dirty_write(Protocol, Tab, Val)
+ end;
+write(_Tid, _Ts, Tab, Val, LockKind) ->
+ abort({bad_type, Tab, Val, LockKind}).
+
+write_to_store(Tab, Store, Oid, Val) ->
+ case ?catch_val({Tab, record_validation}) of
+ {RecName, Arity, Type}
+ when size(Val) == Arity, RecName == element(1, Val) ->
+ case Type of
+ bag ->
+ ?ets_insert(Store, {Oid, Val, write});
+ _ ->
+ ?ets_delete(Store, Oid),
+ ?ets_insert(Store, {Oid, Val, write})
+ end,
+ ok;
+ {'EXIT', _} ->
+ abort({no_exists, Tab});
+ _ ->
+ abort({bad_type, Val})
+ end.
+
+delete({Tab, Key}) ->
+ delete(Tab, Key, write);
+delete(Oid) ->
+ abort({bad_type, Oid}).
+
+s_delete({Tab, Key}) ->
+ delete(Tab, Key, sticky_write);
+s_delete(Oid) ->
+ abort({bad_type, Oid}).
+
+delete(Tab, Key, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ delete(Tid, Ts, Tab, Key, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:delete(Tid, Ts, Tab, Key, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+delete(Tid, Ts, Tab, Key, LockKind)
+ when atom(Tab), Tab /= schema ->
+ case element(1, Tid) of
+ ets ->
+ ?ets_delete(Tab, Key),
+ ok;
+ tid ->
+ Store = Ts#tidstore.store,
+ Oid = {Tab, Key},
+ case LockKind of
+ write ->
+ mnesia_locker:wlock(Tid, Store, Oid);
+ sticky_write ->
+ mnesia_locker:sticky_wlock(Tid, Store, Oid);
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end,
+ ?ets_delete(Store, Oid),
+ ?ets_insert(Store, {Oid, Oid, delete}),
+ ok;
+ Protocol ->
+ do_dirty_delete(Protocol, Tab, Key)
+ end;
+delete(_Tid, _Ts, Tab, _Key, _LockKind) ->
+ abort({bad_type, Tab}).
+
+delete_object(Val) when tuple(Val), size(Val) > 2 ->
+ Tab = element(1, Val),
+ delete_object(Tab, Val, write);
+delete_object(Val) ->
+ abort({bad_type, Val}).
+
+s_delete_object(Val) when tuple(Val), size(Val) > 2 ->
+ Tab = element(1, Val),
+ delete_object(Tab, Val, sticky_write);
+s_delete_object(Val) ->
+ abort({bad_type, Val}).
+
+delete_object(Tab, Val, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ delete_object(Tid, Ts, Tab, Val, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:delete_object(Tid, Ts, Tab, Val, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+delete_object(Tid, Ts, Tab, Val, LockKind)
+ when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
+ case element(1, Tid) of
+ ets ->
+ ?ets_match_delete(Tab, Val),
+ ok;
+ tid ->
+ Store = Ts#tidstore.store,
+ Oid = {Tab, element(2, Val)},
+ case LockKind of
+ write ->
+ mnesia_locker:wlock(Tid, Store, Oid);
+ sticky_write ->
+ mnesia_locker:sticky_wlock(Tid, Store, Oid);
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end,
+ case val({Tab, setorbag}) of
+ bag ->
+ ?ets_match_delete(Store, {Oid, Val, '_'}),
+ ?ets_insert(Store, {Oid, Val, delete_object});
+ _ ->
+ case ?ets_match_object(Store, {Oid, '_', write}) of
+ [] ->
+ ?ets_match_delete(Store, {Oid, Val, '_'}),
+ ?ets_insert(Store, {Oid, Val, delete_object});
+ _ ->
+ ?ets_delete(Store, Oid),
+ ?ets_insert(Store, {Oid, Oid, delete})
+ end
+ end,
+ ok;
+ Protocol ->
+ do_dirty_delete_object(Protocol, Tab, Val)
+ end;
+delete_object(_Tid, _Ts, Tab, _Key, _LockKind) ->
+ abort({bad_type, Tab}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Access within an activity - read
+
+read({Tab, Key}) ->
+ read(Tab, Key, read);
+read(Oid) ->
+ abort({bad_type, Oid}).
+
+wread({Tab, Key}) ->
+ read(Tab, Key, write);
+wread(Oid) ->
+ abort({bad_type, Oid}).
+
+read(Tab, Key, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ read(Tid, Ts, Tab, Key, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:read(Tid, Ts, Tab, Key, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+read(Tid, Ts, Tab, Key, LockKind)
+ when atom(Tab), Tab /= schema ->
+ case element(1, Tid) of
+ ets ->
+ ?ets_lookup(Tab, Key);
+ tid ->
+ Store = Ts#tidstore.store,
+ Oid = {Tab, Key},
+ Objs =
+ case LockKind of
+ read ->
+ mnesia_locker:rlock(Tid, Store, Oid);
+ write ->
+ mnesia_locker:rwlock(Tid, Store, Oid);
+ sticky_write ->
+ mnesia_locker:sticky_rwlock(Tid, Store, Oid);
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end,
+ add_written(?ets_lookup(Store, Oid), Tab, Objs);
+ _Protocol ->
+ dirty_read(Tab, Key)
+ end;
+read(_Tid, _Ts, Tab, _Key, _LockKind) ->
+ abort({bad_type, Tab}).
+
+%%%%%%%%%%%%%%%%%%%%%
+%% Iterators
+
+foldl(Fun, Acc, Tab) ->
+ foldl(Fun, Acc, Tab, read).
+
+foldl(Fun, Acc, Tab, LockKind) when function(Fun) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:foldl(Tid, Ts, Fun, Acc, Tab, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
+ {Type, Prev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
+ Res = (catch do_foldl(ActivityId, Opaque, Tab, dirty_first(Tab), Fun, Acc, Type, Prev)),
+ close_iteration(Res, Tab).
+
+do_foldl(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
+ lists:foldl(fun(Key, Acc) ->
+ lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
+ end, RAcc, Stored);
+do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
+ do_foldl(A, O, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, Stored);
+do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
+ do_foldl(A, O, Tab, Key, Fun, NewAcc, ordered_set, Stored);
+do_foldl(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
+ do_foldl(A, O, Tab, dirty_next(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
+do_foldl(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
+ NewStored = ordsets:del_element(Key, Stored),
+ do_foldl(A, O, Tab, dirty_next(Tab, Key), Fun, NewAcc, Type, NewStored).
+
+foldr(Fun, Acc, Tab) ->
+ foldr(Fun, Acc, Tab, read).
+foldr(Fun, Acc, Tab, LockKind) when function(Fun) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:foldr(Tid, Ts, Fun, Acc, Tab, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
+ {Type, TempPrev} = init_iteration(ActivityId, Opaque, Tab, LockKind),
+ Prev =
+ if
+ Type == ordered_set ->
+ lists:reverse(TempPrev);
+ true -> %% Order doesn't matter for set and bag
+ TempPrev %% Keep the order so we can use ordsets:del_element
+ end,
+ Res = (catch do_foldr(ActivityId, Opaque, Tab, dirty_last(Tab), Fun, Acc, Type, Prev)),
+ close_iteration(Res, Tab).
+
+do_foldr(A, O, Tab, '$end_of_table', Fun, RAcc, _Type, Stored) ->
+ lists:foldl(fun(Key, Acc) ->
+ lists:foldl(Fun, Acc, read(A, O, Tab, Key, read))
+ end, RAcc, Stored);
+do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H == Key ->
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
+ do_foldr(A, O, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, Stored);
+do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H > Key ->
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, H, read)),
+ do_foldr(A, O, Tab, Key, Fun, NewAcc, ordered_set, Stored);
+do_foldr(A, O, Tab, Key, Fun, Acc, ordered_set, [H | Stored]) when H < Key ->
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
+ do_foldr(A, O, Tab, dirty_prev(Tab, Key), Fun, NewAcc, ordered_set, [H |Stored]);
+do_foldr(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag
+ NewAcc = lists:foldl(Fun, Acc, read(A, O, Tab, Key, read)),
+ NewStored = ordsets:del_element(Key, Stored),
+ do_foldr(A, O, Tab, dirty_prev(Tab, Key), Fun, NewAcc, Type, NewStored).
+
+init_iteration(ActivityId, Opaque, Tab, LockKind) ->
+ lock(ActivityId, Opaque, {table, Tab}, LockKind),
+ Type = val({Tab, setorbag}),
+ Previous = add_previous(ActivityId, Opaque, Type, Tab),
+ St = val({Tab, storage_type}),
+ if
+ St == unknown ->
+ ignore;
+ true ->
+ mnesia_lib:db_fixtable(St, Tab, true)
+ end,
+ {Type, Previous}.
+
+close_iteration(Res, Tab) ->
+ case val({Tab, storage_type}) of
+ unknown ->
+ ignore;
+ St ->
+ mnesia_lib:db_fixtable(St, Tab, false)
+ end,
+ case Res of
+ {'EXIT', {aborted, What}} ->
+ abort(What);
+ {'EXIT', What} ->
+ abort(What);
+ _ ->
+ Res
+ end.
+
+add_previous(_ActivityId, non_transaction, _Type, _Tab) ->
+ [];
+add_previous(_Tid, Ts, _Type, Tab) ->
+ Previous = ?ets_match(Ts#tidstore.store, {{Tab, '$1'}, '_', write}),
+ lists:sort(lists:concat(Previous)).
+
+%% This routine fixes up the return value from read/1 so that
+%% it is correct with respect to what this particular transaction
+%% has already written, deleted .... etc
+
+add_written([], _Tab, Objs) ->
+ Objs; % standard normal fast case
+add_written(Written, Tab, Objs) ->
+ case val({Tab, setorbag}) of
+ bag ->
+ add_written_to_bag(Written, Objs, []);
+ _ ->
+ add_written_to_set(Written)
+ end.
+
+add_written_to_set(Ws) ->
+ case lists:last(Ws) of
+ {_, _, delete} -> [];
+ {_, Val, write} -> [Val];
+ {_, _, delete_object} -> []
+ end.
+
+add_written_to_bag([{_, Val, write} | Tail], Objs, Ack) ->
+ add_written_to_bag(Tail, lists:delete(Val, Objs), [Val | Ack]);
+add_written_to_bag([], Objs, Ack) ->
+ Objs ++ lists:reverse(Ack); %% Oldest write first as in ets
+add_written_to_bag([{_, _ , delete} | Tail], _Objs, _Ack) ->
+ %% This transaction just deleted all objects
+ %% with this key
+ add_written_to_bag(Tail, [], []);
+add_written_to_bag([{_, Val, delete_object} | Tail], Objs, Ack) ->
+ add_written_to_bag(Tail, lists:delete(Val, Objs), lists:delete(Val, Ack)).
+
+match_object(Pat) when tuple(Pat), size(Pat) > 2 ->
+ Tab = element(1, Pat),
+ match_object(Tab, Pat, read);
+match_object(Pat) ->
+ abort({bad_type, Pat}).
+
+match_object(Tab, Pat, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ match_object(Tid, Ts, Tab, Pat, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:match_object(Tid, Ts, Tab, Pat, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+match_object(Tid, Ts, Tab, Pat, LockKind)
+ when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
+ case element(1, Tid) of
+ ets ->
+ mnesia_lib:db_match_object(ram_copies, Tab, Pat);
+ tid ->
+ Key = element(2, Pat),
+ case has_var(Key) of
+ false -> lock_record(Tid, Ts, Tab, Key, LockKind);
+ true -> lock_table(Tid, Ts, Tab, LockKind)
+ end,
+ Objs = dirty_match_object(Tab, Pat),
+ add_written_match(Ts#tidstore.store, Pat, Tab, Objs);
+ _Protocol ->
+ dirty_match_object(Tab, Pat)
+ end;
+match_object(_Tid, _Ts, Tab, Pat, _LockKind) ->
+ abort({bad_type, Tab, Pat}).
+
+add_written_match(S, Pat, Tab, Objs) ->
+ Ops = find_ops(S, Tab, Pat),
+ add_match(Ops, Objs, val({Tab, setorbag})).
+
+find_ops(S, Tab, Pat) ->
+ GetWritten = [{{{Tab, '_'}, Pat, write}, [], ['$_']},
+ {{{Tab, '_'}, '_', delete}, [], ['$_']},
+ {{{Tab, '_'}, Pat, delete_object}, [], ['$_']}],
+ ets:select(S, GetWritten).
+
+add_match([], Objs, _Type) ->
+ Objs;
+add_match(Written, Objs, ordered_set) ->
+ %% Must use keysort which is stable
+ add_ordered_match(lists:keysort(1,Written), Objs, []);
+add_match([{Oid, _, delete}|R], Objs, Type) ->
+ add_match(R, deloid(Oid, Objs), Type);
+add_match([{_Oid, Val, delete_object}|R], Objs, Type) ->
+ add_match(R, lists:delete(Val, Objs), Type);
+add_match([{_Oid, Val, write}|R], Objs, bag) ->
+ add_match(R, [Val | lists:delete(Val, Objs)], bag);
+add_match([{Oid, Val, write}|R], Objs, set) ->
+ add_match(R, [Val | deloid(Oid,Objs)],set).
+
+%% For ordered_set only !!
+add_ordered_match(Written = [{{_, Key}, _, _}|_], [Obj|Objs], Acc)
+ when Key > element(2, Obj) ->
+ add_ordered_match(Written, Objs, [Obj|Acc]);
+add_ordered_match([{{_, Key}, Val, write}|Rest], Objs =[Obj|_], Acc)
+ when Key < element(2, Obj) ->
+ add_ordered_match(Rest, [Val|Objs],Acc);
+add_ordered_match([{{_, Key}, _, _DelOP}|Rest], Objs =[Obj|_], Acc)
+ when Key < element(2, Obj) ->
+ add_ordered_match(Rest,Objs,Acc);
+%% Greater than last object
+add_ordered_match([{_, Val, write}|Rest], [], Acc) ->
+ add_ordered_match(Rest, [Val], Acc);
+add_ordered_match([_|Rest], [], Acc) ->
+ add_ordered_match(Rest, [], Acc);
+%% Keys are equal from here
+add_ordered_match([{_, Val, write}|Rest], [_Obj|Objs], Acc) ->
+ add_ordered_match(Rest, [Val|Objs], Acc);
+add_ordered_match([{_, _Val, delete}|Rest], [_Obj|Objs], Acc) ->
+ add_ordered_match(Rest, Objs, Acc);
+add_ordered_match([{_, Val, delete_object}|Rest], [Val|Objs], Acc) ->
+ add_ordered_match(Rest, Objs, Acc);
+add_ordered_match([{_, _, delete_object}|Rest], Objs, Acc) ->
+ add_ordered_match(Rest, Objs, Acc);
+add_ordered_match([], Objs, Acc) ->
+ lists:reverse(Acc, Objs).
+
+
+%%%%%%%%%%%%%%%%%%
+% select
+
+select(Tab, Pat) ->
+ select(Tab, Pat, read).
+select(Tab, Pat, LockKind)
+ when atom(Tab), Tab /= schema, list(Pat) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ select(Tid, Ts, Tab, Pat, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:select(Tid, Ts, Tab, Pat, LockKind);
+ _ ->
+ abort(no_transaction)
+ end;
+select(Tab, Pat, _Lock) ->
+ abort({badarg, Tab, Pat}).
+
+select(Tid, Ts, Tab, Spec, LockKind) ->
+ SelectFun = fun(FixedSpec) -> dirty_select(Tab, FixedSpec) end,
+ fun_select(Tid, Ts, Tab, Spec, LockKind, Tab, SelectFun).
+
+fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, SelectFun) ->
+ case element(1, Tid) of
+ ets ->
+ mnesia_lib:db_select(ram_copies, Tab, Spec);
+ tid ->
+ Store = Ts#tidstore.store,
+ Written = ?ets_match_object(Store, {{TabPat, '_'}, '_', '_'}),
+ %% Avoid table lock if possible
+ case Spec of
+ [{HeadPat,_, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
+ Key = element(2, HeadPat),
+ case has_var(Key) of
+ false -> lock_record(Tid, Ts, Tab, Key, LockKind);
+ true -> lock_table(Tid, Ts, Tab, LockKind)
+ end;
+ _ ->
+ lock_table(Tid, Ts, Tab, LockKind)
+ end,
+ case Written of
+ [] ->
+ %% Nothing changed in the table during this transaction,
+ %% Simple case get results from [d]ets
+ SelectFun(Spec);
+ _ ->
+ %% Hard (slow case) records added or deleted earlier
+ %% in the transaction, have to cope with that.
+ Type = val({Tab, setorbag}),
+ FixedSpec = get_record_pattern(Spec),
+ TabRecs = SelectFun(FixedSpec),
+ FixedRes = add_match(Written, TabRecs, Type),
+ CMS = ets:match_spec_compile(Spec),
+% case Type of
+% ordered_set ->
+% ets:match_spec_run(lists:sort(FixedRes), CMS);
+% _ ->
+% ets:match_spec_run(FixedRes, CMS)
+% end
+ ets:match_spec_run(FixedRes, CMS)
+ end;
+ _Protocol ->
+ SelectFun(Spec)
+ end.
+
+get_record_pattern([]) ->
+ [];
+get_record_pattern([{M,C,_B}|R]) ->
+ [{M,C,['$_']} | get_record_pattern(R)].
+
+deloid(_Oid, []) ->
+ [];
+deloid({Tab, Key}, [H | T]) when element(2, H) == Key ->
+ deloid({Tab, Key}, T);
+deloid(Oid, [H | T]) ->
+ [H | deloid(Oid, T)].
+
+all_keys(Tab) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ all_keys(Tid, Ts, Tab, read);
+ {Mod, Tid, Ts} ->
+ Mod:all_keys(Tid, Ts, Tab, read);
+ _ ->
+ abort(no_transaction)
+ end.
+
+all_keys(Tid, Ts, Tab, LockKind)
+ when atom(Tab), Tab /= schema ->
+ Pat0 = val({Tab, wild_pattern}),
+ Pat = setelement(2, Pat0, '$1'),
+ Keys = select(Tid, Ts, Tab, [{Pat, [], ['$1']}], LockKind),
+ case val({Tab, setorbag}) of
+ bag ->
+ mnesia_lib:uniq(Keys);
+ _ ->
+ Keys
+ end;
+all_keys(_Tid, _Ts, Tab, _LockKind) ->
+ abort({bad_type, Tab}).
+
+index_match_object(Pat, Attr) when tuple(Pat), size(Pat) > 2 ->
+ Tab = element(1, Pat),
+ index_match_object(Tab, Pat, Attr, read);
+index_match_object(Pat, _Attr) ->
+ abort({bad_type, Pat}).
+
+index_match_object(Tab, Pat, Attr, LockKind) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
+ {Mod, Tid, Ts} ->
+ Mod:index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind);
+ _ ->
+ abort(no_transaction)
+ end.
+
+index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind)
+ when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
+ case element(1, Tid) of
+ ets ->
+ dirty_index_match_object(Tab, Pat, Attr); % Should be optimized?
+ tid ->
+ case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
+ Pos when Pos =< size(Pat) ->
+ case LockKind of
+ read ->
+ Store = Ts#tidstore.store,
+ mnesia_locker:rlock_table(Tid, Store, Tab),
+ Objs = dirty_index_match_object(Tab, Pat, Attr),
+ add_written_match(Store, Pat, Tab, Objs);
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end;
+ BadPos ->
+ abort({bad_type, Tab, BadPos})
+ end;
+ _Protocol ->
+ dirty_index_match_object(Tab, Pat, Attr)
+ end;
+index_match_object(_Tid, _Ts, Tab, Pat, _Attr, _LockKind) ->
+ abort({bad_type, Tab, Pat}).
+
+index_read(Tab, Key, Attr) ->
+ case get(mnesia_activity_state) of
+ {?DEFAULT_ACCESS, Tid, Ts} ->
+ index_read(Tid, Ts, Tab, Key, Attr, read);
+ {Mod, Tid, Ts} ->
+ Mod:index_read(Tid, Ts, Tab, Key, Attr, read);
+ _ ->
+ abort(no_transaction)
+ end.
+
+index_read(Tid, Ts, Tab, Key, Attr, LockKind)
+ when atom(Tab), Tab /= schema ->
+ case element(1, Tid) of
+ ets ->
+ dirty_index_read(Tab, Key, Attr); % Should be optimized?
+ tid ->
+ Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
+ case LockKind of
+ read ->
+ case has_var(Key) of
+ false ->
+ Store = Ts#tidstore.store,
+ Objs = mnesia_index:read(Tid, Store, Tab, Key, Pos),
+ Pat = setelement(Pos, val({Tab, wild_pattern}), Key),
+ add_written_match(Store, Pat, Tab, Objs);
+ true ->
+ abort({bad_type, Tab, Attr, Key})
+ end;
+ _ ->
+ abort({bad_type, Tab, LockKind})
+ end;
+ _Protocol ->
+ dirty_index_read(Tab, Key, Attr)
+ end;
+index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind) ->
+ abort({bad_type, Tab}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Dirty access regardless of activities - updates
+
+dirty_write(Val) when tuple(Val), size(Val) > 2 ->
+ Tab = element(1, Val),
+ dirty_write(Tab, Val);
+dirty_write(Val) ->
+ abort({bad_type, Val}).
+
+dirty_write(Tab, Val) ->
+ do_dirty_write(async_dirty, Tab, Val).
+
+do_dirty_write(SyncMode, Tab, Val)
+ when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
+ case ?catch_val({Tab, record_validation}) of
+ {RecName, Arity, _Type}
+ when size(Val) == Arity, RecName == element(1, Val) ->
+ Oid = {Tab, element(2, Val)},
+ mnesia_tm:dirty(SyncMode, {Oid, Val, write});
+ {'EXIT', _} ->
+ abort({no_exists, Tab});
+ _ ->
+ abort({bad_type, Val})
+ end;
+do_dirty_write(_SyncMode, Tab, Val) ->
+ abort({bad_type, Tab, Val}).
+
+dirty_delete({Tab, Key}) ->
+ dirty_delete(Tab, Key);
+dirty_delete(Oid) ->
+ abort({bad_type, Oid}).
+
+dirty_delete(Tab, Key) ->
+ do_dirty_delete(async_dirty, Tab, Key).
+
+do_dirty_delete(SyncMode, Tab, Key) when atom(Tab), Tab /= schema ->
+ Oid = {Tab, Key},
+ mnesia_tm:dirty(SyncMode, {Oid, Oid, delete});
+do_dirty_delete(_SyncMode, Tab, _Key) ->
+ abort({bad_type, Tab}).
+
+dirty_delete_object(Val) when tuple(Val), size(Val) > 2 ->
+ Tab = element(1, Val),
+ dirty_delete_object(Tab, Val);
+dirty_delete_object(Val) ->
+ abort({bad_type, Val}).
+
+dirty_delete_object(Tab, Val) ->
+ do_dirty_delete_object(async_dirty, Tab, Val).
+
+do_dirty_delete_object(SyncMode, Tab, Val)
+ when atom(Tab), Tab /= schema, tuple(Val), size(Val) > 2 ->
+ Oid = {Tab, element(2, Val)},
+ mnesia_tm:dirty(SyncMode, {Oid, Val, delete_object});
+do_dirty_delete_object(_SyncMode, Tab, Val) ->
+ abort({bad_type, Tab, Val}).
+
+%% A Counter is an Oid being {CounterTab, CounterName}
+
+dirty_update_counter({Tab, Key}, Incr) ->
+ dirty_update_counter(Tab, Key, Incr);
+dirty_update_counter(Counter, _Incr) ->
+ abort({bad_type, Counter}).
+
+dirty_update_counter(Tab, Key, Incr) ->
+ do_dirty_update_counter(async_dirty, Tab, Key, Incr).
+
+do_dirty_update_counter(SyncMode, Tab, Key, Incr)
+ when atom(Tab), Tab /= schema, integer(Incr) ->
+ case ?catch_val({Tab, record_validation}) of
+ {RecName, 3, set} ->
+ Oid = {Tab, Key},
+ mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
+ _ ->
+ abort({combine_error, Tab, update_counter})
+ end;
+do_dirty_update_counter(_SyncMode, Tab, _Key, Incr) ->
+ abort({bad_type, Tab, Incr}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Dirty access regardless of activities - read
+
+dirty_read({Tab, Key}) ->
+ dirty_read(Tab, Key);
+dirty_read(Oid) ->
+ abort({bad_type, Oid}).
+
+dirty_read(Tab, Key)
+ when atom(Tab), Tab /= schema ->
+%% case catch ?ets_lookup(Tab, Key) of
+%% {'EXIT', _} ->
+ %% Bad luck, we have to perform a real lookup
+ dirty_rpc(Tab, mnesia_lib, db_get, [Tab, Key]);
+%% Val ->
+%% Val
+%% end;
+dirty_read(Tab, _Key) ->
+ abort({bad_type, Tab}).
+
+dirty_match_object(Pat) when tuple(Pat), size(Pat) > 2 ->
+ Tab = element(1, Pat),
+ dirty_match_object(Tab, Pat);
+dirty_match_object(Pat) ->
+ abort({bad_type, Pat}).
+
+dirty_match_object(Tab, Pat)
+ when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
+ dirty_rpc(Tab, ?MODULE, remote_dirty_match_object, [Tab, Pat]);
+dirty_match_object(Tab, Pat) ->
+ abort({bad_type, Tab, Pat}).
+
+remote_dirty_match_object(Tab, Pat) ->
+ Key = element(2, Pat),
+ case has_var(Key) of
+ false ->
+ mnesia_lib:db_match_object(Tab, Pat);
+ true ->
+ PosList = val({Tab, index}),
+ remote_dirty_match_object(Tab, Pat, PosList)
+ end.
+
+remote_dirty_match_object(Tab, Pat, [Pos | Tail]) when Pos =< size(Pat) ->
+ IxKey = element(Pos, Pat),
+ case has_var(IxKey) of
+ false ->
+ mnesia_index:dirty_match_object(Tab, Pat, Pos);
+ true ->
+ remote_dirty_match_object(Tab, Pat, Tail)
+ end;
+remote_dirty_match_object(Tab, Pat, []) ->
+ mnesia_lib:db_match_object(Tab, Pat);
+remote_dirty_match_object(Tab, Pat, _PosList) ->
+ abort({bad_type, Tab, Pat}).
+
+dirty_select(Tab, Spec) when atom(Tab), Tab /= schema, list(Spec) ->
+ dirty_rpc(Tab, ?MODULE, remote_dirty_select, [Tab, Spec]);
+dirty_select(Tab, Spec) ->
+ abort({bad_type, Tab, Spec}).
+
+remote_dirty_select(Tab, Spec) ->
+ case Spec of
+ [{HeadPat, _, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
+ Key = element(2, HeadPat),
+ case has_var(Key) of
+ false ->
+ mnesia_lib:db_select(Tab, Spec);
+ true ->
+ PosList = val({Tab, index}),
+ remote_dirty_select(Tab, Spec, PosList)
+ end;
+ _ ->
+ mnesia_lib:db_select(Tab, Spec)
+ end.
+
+remote_dirty_select(Tab, [{HeadPat,_, _}] = Spec, [Pos | Tail])
+ when tuple(HeadPat), size(HeadPat) > 2, Pos =< size(Spec) ->
+ Key = element(Pos, HeadPat),
+ case has_var(Key) of
+ false ->
+ Recs = mnesia_index:dirty_select(Tab, Spec, Pos),
+ %% Returns the records without applying the match spec
+ %% The actual filtering is handled by the caller
+ CMS = ets:match_spec_compile(Spec),
+ case val({Tab, setorbag}) of
+ ordered_set ->
+ ets:match_spec_run(lists:sort(Recs), CMS);
+ _ ->
+ ets:match_spec_run(Recs, CMS)
+ end;
+ true ->
+ remote_dirty_select(Tab, Spec, Tail)
+ end;
+remote_dirty_select(Tab, Spec, _) ->
+ mnesia_lib:db_select(Tab, Spec).
+
+dirty_all_keys(Tab) when atom(Tab), Tab /= schema ->
+ case ?catch_val({Tab, wild_pattern}) of
+ {'EXIT', _} ->
+ abort({no_exists, Tab});
+ Pat0 ->
+ Pat = setelement(2, Pat0, '$1'),
+ Keys = dirty_select(Tab, [{Pat, [], ['$1']}]),
+ case val({Tab, setorbag}) of
+ bag -> mnesia_lib:uniq(Keys);
+ _ -> Keys
+ end
+ end;
+dirty_all_keys(Tab) ->
+ abort({bad_type, Tab}).
+
+dirty_index_match_object(Pat, Attr) when tuple(Pat), size(Pat) > 2 ->
+ Tab = element(1, Pat),
+ dirty_index_match_object(Tab, Pat, Attr);
+dirty_index_match_object(Pat, _Attr) ->
+ abort({bad_type, Pat}).
+
+dirty_index_match_object(Tab, Pat, Attr)
+ when atom(Tab), Tab /= schema, tuple(Pat), size(Pat) > 2 ->
+ case mnesia_schema:attr_tab_to_pos(Tab, Attr) of
+ Pos when Pos =< size(Pat) ->
+ case has_var(element(2, Pat)) of
+ false ->
+ dirty_match_object(Tab, Pat);
+ true ->
+ Elem = element(Pos, Pat),
+ case has_var(Elem) of
+ false ->
+ dirty_rpc(Tab, mnesia_index, dirty_match_object,
+ [Tab, Pat, Pos]);
+ true ->
+ abort({bad_type, Tab, Attr, Elem})
+ end
+ end;
+ BadPos ->
+ abort({bad_type, Tab, BadPos})
+ end;
+dirty_index_match_object(Tab, Pat, _Attr) ->
+ abort({bad_type, Tab, Pat}).
+
+dirty_index_read(Tab, Key, Attr) when atom(Tab), Tab /= schema ->
+ Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr),
+ case has_var(Key) of
+ false ->
+ mnesia_index:dirty_read(Tab, Key, Pos);
+ true ->
+ abort({bad_type, Tab, Attr, Key})
+ end;
+dirty_index_read(Tab, _Key, _Attr) ->
+ abort({bad_type, Tab}).
+
+dirty_slot(Tab, Slot) when atom(Tab), Tab /= schema, integer(Slot) ->
+ dirty_rpc(Tab, mnesia_lib, db_slot, [Tab, Slot]);
+dirty_slot(Tab, Slot) ->
+ abort({bad_type, Tab, Slot}).
+
+dirty_first(Tab) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_lib, db_first, [Tab]);
+dirty_first(Tab) ->
+ abort({bad_type, Tab}).
+
+dirty_last(Tab) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_lib, db_last, [Tab]);
+dirty_last(Tab) ->
+ abort({bad_type, Tab}).
+
+dirty_next(Tab, Key) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_lib, db_next_key, [Tab, Key]);
+dirty_next(Tab, _Key) ->
+ abort({bad_type, Tab}).
+
+dirty_prev(Tab, Key) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_lib, db_prev_key, [Tab, Key]);
+dirty_prev(Tab, _Key) ->
+ abort({bad_type, Tab}).
+
+
+dirty_rpc(Tab, M, F, Args) ->
+ Node = val({Tab, where_to_read}),
+ do_dirty_rpc(Tab, Node, M, F, Args).
+
+do_dirty_rpc(_Tab, nowhere, _, _, Args) ->
+ mnesia:abort({no_exists, Args});
+do_dirty_rpc(Tab, Node, M, F, Args) ->
+ case rpc:call(Node, M, F, Args) of
+ {badrpc,{'EXIT', {undef, [{ M, F, _} | _]}}}
+ when M == ?MODULE, F == remote_dirty_select ->
+ %% Oops, the other node has not been upgraded
+ %% to 4.0.3 yet. Lets do it the old way.
+ %% Remove this in next release.
+ do_dirty_rpc(Tab, Node, mnesia_lib, db_select, Args);
+ {badrpc, Reason} ->
+ erlang:yield(), %% Do not be too eager
+ case mnesia_controller:call({check_w2r, Node, Tab}) of % Sync
+ NewNode when NewNode == Node ->
+ ErrorTag = mnesia_lib:dirty_rpc_error_tag(Reason),
+ mnesia:abort({ErrorTag, Args});
+ NewNode ->
+ case get(mnesia_activity_state) of
+ {_Mod, Tid, _Ts} when record(Tid, tid) ->
+ %% In order to perform a consistent
+ %% retry of a transaction we need
+ %% to acquire the lock on the NewNode.
+ %% In this context we do neither know
+ %% the kind or granularity of the lock.
+ %% --> Abort the transaction
+ mnesia:abort({node_not_running, Node});
+ _ ->
+ %% Splendid! A dirty retry is safe
+ %% 'Node' probably went down now
+ %% Let mnesia_controller get broken link message first
+ do_dirty_rpc(Tab, NewNode, M, F, Args)
+ end
+ end;
+ Other ->
+ Other
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Info
+
+%% Info about one table
+table_info(Tab, Item) ->
+ case get(mnesia_activity_state) of
+ undefined ->
+ any_table_info(Tab, Item);
+ {?DEFAULT_ACCESS, _Tid, _Ts} ->
+ any_table_info(Tab, Item);
+ {Mod, Tid, Ts} ->
+ Mod:table_info(Tid, Ts, Tab, Item);
+ _ ->
+ abort(no_transaction)
+ end.
+
+table_info(_Tid, _Ts, Tab, Item) ->
+ any_table_info(Tab, Item).
+
+
+any_table_info(Tab, Item) when atom(Tab) ->
+ case Item of
+ master_nodes ->
+ mnesia_recover:get_master_nodes(Tab);
+% checkpoints ->
+% case ?catch_val({Tab, commit_work}) of
+% [{checkpoints, List} | _] -> List;
+% No_chk when list(No_chk) -> [];
+% Else -> info_reply(Else, Tab, Item)
+% end;
+ size ->
+ raw_table_info(Tab, Item);
+ memory ->
+ raw_table_info(Tab, Item);
+ type ->
+ case ?catch_val({Tab, setorbag}) of
+ {'EXIT', _} ->
+ bad_info_reply(Tab, Item);
+ Val ->
+ Val
+ end;
+ all ->
+ case mnesia_schema:get_table_properties(Tab) of
+ [] ->
+ abort({no_exists, Tab, Item});
+ Props ->
+ lists:map(fun({setorbag, Type}) -> {type, Type};
+ (Prop) -> Prop end,
+ Props)
+ end;
+ _ ->
+ case ?catch_val({Tab, Item}) of
+ {'EXIT', _} ->
+ bad_info_reply(Tab, Item);
+ Val ->
+ Val
+ end
+ end;
+any_table_info(Tab, _Item) ->
+ abort({bad_type, Tab}).
+
+raw_table_info(Tab, Item) ->
+ case ?catch_val({Tab, storage_type}) of
+ ram_copies ->
+ info_reply(catch ?ets_info(Tab, Item), Tab, Item);
+ disc_copies ->
+ info_reply(catch ?ets_info(Tab, Item), Tab, Item);
+ disc_only_copies ->
+ info_reply(catch dets:info(Tab, Item), Tab, Item);
+ unknown ->
+ bad_info_reply(Tab, Item);
+ {'EXIT', _} ->
+ bad_info_reply(Tab, Item)
+ end.
+
+info_reply({'EXIT', _Reason}, Tab, Item) ->
+ bad_info_reply(Tab, Item);
+info_reply({error, _Reason}, Tab, Item) ->
+ bad_info_reply(Tab, Item);
+info_reply(Val, _Tab, _Item) ->
+ Val.
+
+bad_info_reply(_Tab, size) -> 0;
+bad_info_reply(_Tab, memory) -> 0;
+bad_info_reply(Tab, Item) -> abort({no_exists, Tab, Item}).
+
+%% Raw info about all tables
+schema() ->
+ mnesia_schema:info().
+
+%% Raw info about one tables
+schema(Tab) ->
+ mnesia_schema:info(Tab).
+
+error_description(Err) ->
+ mnesia_lib:error_desc(Err).
+
+info() ->
+ case mnesia_lib:is_running() of
+ yes ->
+ TmInfo = mnesia_tm:get_info(10000),
+ Held = system_info(held_locks),
+ Queued = system_info(lock_queue),
+
+ io:format("---> Processes holding locks <--- ~n", []),
+ lists:foreach(fun(L) -> io:format("Lock: ~p~n", [L]) end,
+ Held),
+
+ io:format( "---> Processes waiting for locks <--- ~n", []),
+ lists:foreach(fun({Oid, Op, _Pid, Tid, OwnerTid}) ->
+ io:format("Tid ~p waits for ~p lock "
+ "on oid ~p owned by ~p ~n",
+ [Tid, Op, Oid, OwnerTid])
+ end, Queued),
+ mnesia_tm:display_info(group_leader(), TmInfo),
+
+ Pat = {'_', unclear, '_'},
+ Uncertain = ets:match_object(mnesia_decision, Pat),
+
+ io:format( "---> Uncertain transactions <--- ~n", []),
+ lists:foreach(fun({Tid, _, Nodes}) ->
+ io:format("Tid ~w waits for decision "
+ "from ~w~n",
+ [Tid, Nodes])
+ end, Uncertain),
+
+ mnesia_controller:info(),
+ display_system_info(Held, Queued, TmInfo, Uncertain);
+ _ ->
+ mini_info()
+ end,
+ ok.
+
+mini_info() ->
+ io:format("===> System info in version ~p, debug level = ~p <===~n",
+ [system_info(version), system_info(debug)]),
+ Not =
+ case system_info(use_dir) of
+ true -> "";
+ false -> "NOT "
+ end,
+
+ io:format("~w. Directory ~p is ~sused.~n",
+ [system_info(schema_location), system_info(directory), Not]),
+ io:format("use fallback at restart = ~w~n",
+ [system_info(fallback_activated)]),
+ Running = system_info(running_db_nodes),
+ io:format("running db nodes = ~w~n", [Running]),
+ All = mnesia_lib:all_nodes(),
+ io:format("stopped db nodes = ~w ~n", [All -- Running]).
+
+display_system_info(Held, Queued, TmInfo, Uncertain) ->
+ mini_info(),
+ display_tab_info(),
+ S = fun(Items) -> [system_info(I) || I <- Items] end,
+
+ io:format("~w transactions committed, ~w aborted, "
+ "~w restarted, ~w logged to disc~n",
+ S([transaction_commits, transaction_failures,
+ transaction_restarts, transaction_log_writes])),
+
+ {Active, Pending} =
+ case TmInfo of
+ {timeout, _} -> {infinity, infinity};
+ {info, P, A} -> {length(A), length(P)}
+ end,
+ io:format("~w held locks, ~w in queue; "
+ "~w local transactions, ~w remote~n",
+ [length(Held), length(Queued), Active, Pending]),
+
+ Ufold = fun({_, _, Ns}, {C, Old}) ->
+ New = [N || N <- Ns, not lists:member(N, Old)],
+ {C + 1, New ++ Old}
+ end,
+ {Ucount, Unodes} = lists:foldl(Ufold, {0, []}, Uncertain),
+ io:format("~w transactions waits for other nodes: ~p~n",
+ [Ucount, Unodes]).
+
+display_tab_info() ->
+ MasterTabs = mnesia_recover:get_master_node_tables(),
+ io:format("master node tables = ~p~n", [lists:sort(MasterTabs)]),
+
+ Tabs = system_info(tables),
+
+ {Unknown, Ram, Disc, DiscOnly} =
+ lists:foldl(fun storage_count/2, {[], [], [], []}, Tabs),
+
+ io:format("remote = ~p~n", [lists:sort(Unknown)]),
+ io:format("ram_copies = ~p~n", [lists:sort(Ram)]),
+ io:format("disc_copies = ~p~n", [lists:sort(Disc)]),
+ io:format("disc_only_copies = ~p~n", [lists:sort(DiscOnly)]),
+
+ Rfoldl = fun(T, Acc) ->
+ Rpat =
+ case val({T, access_mode}) of
+ read_only ->
+ lists:sort([{A, read_only} || A <- val({T, active_replicas})]);
+ read_write ->
+ table_info(T, where_to_commit)
+ end,
+ case lists:keysearch(Rpat, 1, Acc) of
+ {value, {_Rpat, Rtabs}} ->
+ lists:keyreplace(Rpat, 1, Acc, {Rpat, [T | Rtabs]});
+ false ->
+ [{Rpat, [T]} | Acc]
+ end
+ end,
+ Repl = lists:foldl(Rfoldl, [], Tabs),
+ Rdisp = fun({Rpat, Rtabs}) -> io:format("~p = ~p~n", [Rpat, Rtabs]) end,
+ lists:foreach(Rdisp, lists:sort(Repl)).
+
+storage_count(T, {U, R, D, DO}) ->
+ case table_info(T, storage_type) of
+ unknown -> {[T | U], R, D, DO};
+ ram_copies -> {U, [T | R], D, DO};
+ disc_copies -> {U, R, [T | D], DO};
+ disc_only_copies -> {U, R, D, [T | DO]}
+ end.
+
+system_info(Item) ->
+ case catch system_info2(Item) of
+ {'EXIT',Error} -> abort(Error);
+ Other -> Other
+ end.
+
+system_info2(all) ->
+ Items = system_info_items(mnesia_lib:is_running()),
+ [{I, system_info(I)} || I <- Items];
+
+system_info2(db_nodes) ->
+ DiscNs = ?catch_val({schema, disc_copies}),
+ RamNs = ?catch_val({schema, ram_copies}),
+ if
+ list(DiscNs), list(RamNs) ->
+ DiscNs ++ RamNs;
+ true ->
+ case mnesia_schema:read_nodes() of
+ {ok, Nodes} -> Nodes;
+ {error,Reason} -> exit(Reason)
+ end
+ end;
+system_info2(running_db_nodes) ->
+ case ?catch_val({current, db_nodes}) of
+ {'EXIT',_} ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ load_mnesia_or_abort(),
+ mnesia_lib:running_nodes();
+ Other ->
+ Other
+ end;
+
+system_info2(extra_db_nodes) ->
+ case ?catch_val(extra_db_nodes) of
+ {'EXIT',_} ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ load_mnesia_or_abort(),
+ mnesia_monitor:get_env(extra_db_nodes);
+ Other ->
+ Other
+ end;
+
+system_info2(directory) ->
+ case ?catch_val(directory) of
+ {'EXIT',_} ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ load_mnesia_or_abort(),
+ mnesia_monitor:get_env(dir);
+ Other ->
+ Other
+ end;
+
+system_info2(use_dir) ->
+ case ?catch_val(use_dir) of
+ {'EXIT',_} ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ load_mnesia_or_abort(),
+ mnesia_monitor:use_dir();
+ Other ->
+ Other
+ end;
+
+system_info2(schema_location) ->
+ case ?catch_val(schema_location) of
+ {'EXIT',_} ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ load_mnesia_or_abort(),
+ mnesia_monitor:get_env(schema_location);
+ Other ->
+ Other
+ end;
+
+system_info2(fallback_activated) ->
+ case ?catch_val(fallback_activated) of
+ {'EXIT',_} ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ load_mnesia_or_abort(),
+ mnesia_bup:fallback_exists();
+ Other ->
+ Other
+ end;
+
+system_info2(version) ->
+ case ?catch_val(version) of
+ {'EXIT', _} ->
+ Apps = application:loaded_applications(),
+ case lists:keysearch(?APPLICATION, 1, Apps) of
+ {value, {_Name, _Desc, Version}} ->
+ Version;
+ false ->
+ %% Ensure that it does not match
+ {mnesia_not_loaded, node(), now()}
+ end;
+ Version ->
+ Version
+ end;
+
+system_info2(access_module) -> mnesia_monitor:get_env(access_module);
+system_info2(auto_repair) -> mnesia_monitor:get_env(auto_repair);
+system_info2(is_running) -> mnesia_lib:is_running();
+system_info2(backup_module) -> mnesia_monitor:get_env(backup_module);
+system_info2(event_module) -> mnesia_monitor:get_env(event_module);
+system_info2(debug) -> mnesia_monitor:get_env(debug);
+system_info2(dump_log_load_regulation) -> mnesia_monitor:get_env(dump_log_load_regulation);
+system_info2(dump_log_write_threshold) -> mnesia_monitor:get_env(dump_log_write_threshold);
+system_info2(dump_log_time_threshold) -> mnesia_monitor:get_env(dump_log_time_threshold);
+system_info2(dump_log_update_in_place) ->
+ mnesia_monitor:get_env(dump_log_update_in_place);
+system_info2(dump_log_update_in_place) ->
+ mnesia_monitor:get_env(dump_log_update_in_place);
+system_info2(max_wait_for_decision) -> mnesia_monitor:get_env(max_wait_for_decision);
+system_info2(embedded_mnemosyne) -> mnesia_monitor:get_env(embedded_mnemosyne);
+system_info2(ignore_fallback_at_startup) -> mnesia_monitor:get_env(ignore_fallback_at_startup);
+system_info2(fallback_error_function) -> mnesia_monitor:get_env(fallback_error_function);
+system_info2(log_version) -> mnesia_log:version();
+system_info2(protocol_version) -> mnesia_monitor:protocol_version();
+system_info2(schema_version) -> mnesia_schema:version(); %backward compatibility
+system_info2(tables) -> val({schema, tables});
+system_info2(local_tables) -> val({schema, local_tables});
+system_info2(master_node_tables) -> mnesia_recover:get_master_node_tables();
+system_info2(subscribers) -> mnesia_subscr:subscribers();
+system_info2(checkpoints) -> mnesia_checkpoint:checkpoints();
+system_info2(held_locks) -> mnesia_locker:get_held_locks();
+system_info2(lock_queue) -> mnesia_locker:get_lock_queue();
+system_info2(transactions) -> mnesia_tm:get_transactions();
+system_info2(transaction_failures) -> mnesia_lib:read_counter(trans_failures);
+system_info2(transaction_commits) -> mnesia_lib:read_counter(trans_commits);
+system_info2(transaction_restarts) -> mnesia_lib:read_counter(trans_restarts);
+system_info2(transaction_log_writes) -> mnesia_dumper:get_log_writes();
+
+system_info2(Item) -> exit({badarg, Item}).
+
+system_info_items(yes) ->
+ [
+ access_module,
+ auto_repair,
+ backup_module,
+ checkpoints,
+ db_nodes,
+ debug,
+ directory,
+ dump_log_load_regulation,
+ dump_log_time_threshold,
+ dump_log_update_in_place,
+ dump_log_write_threshold,
+ embedded_mnemosyne,
+ event_module,
+ extra_db_nodes,
+ fallback_activated,
+ held_locks,
+ ignore_fallback_at_startup,
+ fallback_error_function,
+ is_running,
+ local_tables,
+ lock_queue,
+ log_version,
+ master_node_tables,
+ max_wait_for_decision,
+ protocol_version,
+ running_db_nodes,
+ schema_location,
+ schema_version,
+ subscribers,
+ tables,
+ transaction_commits,
+ transaction_failures,
+ transaction_log_writes,
+ transaction_restarts,
+ transactions,
+ use_dir,
+ version
+ ];
+system_info_items(no) ->
+ [
+ auto_repair,
+ backup_module,
+ db_nodes,
+ debug,
+ directory,
+ dump_log_load_regulation,
+ dump_log_time_threshold,
+ dump_log_update_in_place,
+ dump_log_write_threshold,
+ event_module,
+ extra_db_nodes,
+ ignore_fallback_at_startup,
+ fallback_error_function,
+ is_running,
+ log_version,
+ max_wait_for_decision,
+ protocol_version,
+ running_db_nodes,
+ schema_location,
+ schema_version,
+ use_dir,
+ version
+ ].
+
+system_info() ->
+ IsRunning = mnesia_lib:is_running(),
+ case IsRunning of
+ yes ->
+ TmInfo = mnesia_tm:get_info(10000),
+ Held = system_info(held_locks),
+ Queued = system_info(lock_queue),
+ Pat = {'_', unclear, '_'},
+ Uncertain = ets:match_object(mnesia_decision, Pat),
+ display_system_info(Held, Queued, TmInfo, Uncertain);
+ _ ->
+ mini_info()
+ end,
+ IsRunning.
+
+load_mnesia_or_abort() ->
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ abort(Reason)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Database mgt
+
+create_schema(Ns) ->
+ mnesia_bup:create_schema(Ns).
+
+delete_schema(Ns) ->
+ mnesia_schema:delete_schema(Ns).
+
+backup(Opaque) ->
+ mnesia_log:backup(Opaque).
+
+backup(Opaque, Mod) ->
+ mnesia_log:backup(Opaque, Mod).
+
+traverse_backup(S, T, Fun, Acc) ->
+ mnesia_bup:traverse_backup(S, T, Fun, Acc).
+
+traverse_backup(S, SM, T, TM, F, A) ->
+ mnesia_bup:traverse_backup(S, SM, T, TM, F, A).
+
+install_fallback(Opaque) ->
+ mnesia_bup:install_fallback(Opaque).
+
+install_fallback(Opaque, Mod) ->
+ mnesia_bup:install_fallback(Opaque, Mod).
+
+uninstall_fallback() ->
+ mnesia_bup:uninstall_fallback().
+
+uninstall_fallback(Args) ->
+ mnesia_bup:uninstall_fallback(Args).
+
+activate_checkpoint(Args) ->
+ mnesia_checkpoint:activate(Args).
+
+deactivate_checkpoint(Name) ->
+ mnesia_checkpoint:deactivate(Name).
+
+backup_checkpoint(Name, Opaque) ->
+ mnesia_log:backup_checkpoint(Name, Opaque).
+
+backup_checkpoint(Name, Opaque, Mod) ->
+ mnesia_log:backup_checkpoint(Name, Opaque, Mod).
+
+restore(Opaque, Args) ->
+ mnesia_schema:restore(Opaque, Args).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Table mgt
+
+create_table(Arg) ->
+ mnesia_schema:create_table(Arg).
+create_table(Name, Arg) when list(Arg) ->
+ mnesia_schema:create_table([{name, Name}| Arg]);
+create_table(Name, Arg) ->
+ {aborted, badarg, Name, Arg}.
+
+delete_table(Tab) ->
+ mnesia_schema:delete_table(Tab).
+
+add_table_copy(Tab, N, S) ->
+ mnesia_schema:add_table_copy(Tab, N, S).
+del_table_copy(Tab, N) ->
+ mnesia_schema:del_table_copy(Tab, N).
+
+move_table_copy(Tab, From, To) ->
+ mnesia_schema:move_table(Tab, From, To).
+
+add_table_index(Tab, Ix) ->
+ mnesia_schema:add_table_index(Tab, Ix).
+del_table_index(Tab, Ix) ->
+ mnesia_schema:del_table_index(Tab, Ix).
+
+transform_table(Tab, Fun, NewA) ->
+ case catch val({Tab, record_name}) of
+ {'EXIT', Reason} ->
+ mnesia:abort(Reason);
+ OldRN ->
+ mnesia_schema:transform_table(Tab, Fun, NewA, OldRN)
+ end.
+
+transform_table(Tab, Fun, NewA, NewRN) ->
+ mnesia_schema:transform_table(Tab, Fun, NewA, NewRN).
+
+change_table_copy_type(T, N, S) ->
+ mnesia_schema:change_table_copy_type(T, N, S).
+
+clear_table(Tab) ->
+ mnesia_schema:clear_table(Tab).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Table mgt - user properties
+
+read_table_property(Tab, PropKey) ->
+ val({Tab, user_property, PropKey}).
+
+write_table_property(Tab, Prop) ->
+ mnesia_schema:write_table_property(Tab, Prop).
+
+delete_table_property(Tab, PropKey) ->
+ mnesia_schema:delete_table_property(Tab, PropKey).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Table mgt - user properties
+
+change_table_frag(Tab, FragProp) ->
+ mnesia_schema:change_table_frag(Tab, FragProp).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Table mgt - table load
+
+%% Dump a ram table to disc
+dump_tables(Tabs) ->
+ mnesia_schema:dump_tables(Tabs).
+
+%% allow the user to wait for some tables to be loaded
+wait_for_tables(Tabs, Timeout) ->
+ mnesia_controller:wait_for_tables(Tabs, Timeout).
+
+force_load_table(Tab) ->
+ case mnesia_controller:force_load_table(Tab) of
+ ok -> yes; % Backwards compatibility
+ Other -> Other
+ end.
+
+change_table_access_mode(T, Access) ->
+ mnesia_schema:change_table_access_mode(T, Access).
+
+change_table_load_order(T, O) ->
+ mnesia_schema:change_table_load_order(T, O).
+
+set_master_nodes(Nodes) when list(Nodes) ->
+ UseDir = system_info(use_dir),
+ IsRunning = system_info(is_running),
+ case IsRunning of
+ yes ->
+ CsPat = {{'_', cstruct}, '_'},
+ Cstructs0 = ?ets_match_object(mnesia_gvar, CsPat),
+ Cstructs = [Cs || {_, Cs} <- Cstructs0],
+ log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning);
+ _NotRunning ->
+ case UseDir of
+ true ->
+ mnesia_lib:lock_table(schema),
+ Res =
+ case mnesia_schema:read_cstructs_from_disc() of
+ {ok, Cstructs} ->
+ log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning);
+ {error, Reason} ->
+ {error, Reason}
+ end,
+ mnesia_lib:unlock_table(schema),
+ Res;
+ false ->
+ ok
+ end
+ end;
+set_master_nodes(Nodes) ->
+ {error, {bad_type, Nodes}}.
+
+log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning) ->
+ Fun = fun(Cs) ->
+ Copies = mnesia_lib:copy_holders(Cs),
+ Valid = mnesia_lib:intersect(Nodes, Copies),
+ {Cs#cstruct.name, Valid}
+ end,
+ Args = lists:map(Fun, Cstructs),
+ mnesia_recover:log_master_nodes(Args, UseDir, IsRunning).
+
+set_master_nodes(Tab, Nodes) when list(Nodes) ->
+ UseDir = system_info(use_dir),
+ IsRunning = system_info(is_running),
+ case IsRunning of
+ yes ->
+ case ?catch_val({Tab, cstruct}) of
+ {'EXIT', _} ->
+ {error, {no_exists, Tab}};
+ Cs ->
+ case Nodes -- mnesia_lib:copy_holders(Cs) of
+ [] ->
+ Args = [{Tab , Nodes}],
+ mnesia_recover:log_master_nodes(Args, UseDir, IsRunning);
+ BadNodes ->
+ {error, {no_exists, Tab, BadNodes}}
+ end
+ end;
+ _NotRunning ->
+ case UseDir of
+ true ->
+ mnesia_lib:lock_table(schema),
+ Res =
+ case mnesia_schema:read_cstructs_from_disc() of
+ {ok, Cstructs} ->
+ case lists:keysearch(Tab, 2, Cstructs) of
+ {value, Cs} ->
+ case Nodes -- mnesia_lib:copy_holders(Cs) of
+ [] ->
+ Args = [{Tab , Nodes}],
+ mnesia_recover:log_master_nodes(Args, UseDir, IsRunning);
+ BadNodes ->
+ {error, {no_exists, Tab, BadNodes}}
+ end;
+ false ->
+ {error, {no_exists, Tab}}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end,
+ mnesia_lib:unlock_table(schema),
+ Res;
+ false ->
+ ok
+ end
+ end;
+set_master_nodes(Tab, Nodes) ->
+ {error, {bad_type, Tab, Nodes}}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Misc admin
+
+dump_log() ->
+ mnesia_controller:sync_dump_log(user).
+
+subscribe(What) ->
+ mnesia_subscr:subscribe(self(), What).
+
+unsubscribe(What) ->
+ mnesia_subscr:unsubscribe(self(), What).
+
+report_event(Event) ->
+ mnesia_lib:report_system_event({mnesia_user, Event}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Snmp
+
+snmp_open_table(Tab, Us) ->
+ mnesia_schema:add_snmp(Tab, Us).
+
+snmp_close_table(Tab) ->
+ mnesia_schema:del_snmp(Tab).
+
+snmp_get_row(Tab, RowIndex) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_snmp_hook, get_row, [Tab, RowIndex]);
+snmp_get_row(Tab, _RowIndex) ->
+ abort({bad_type, Tab}).
+
+snmp_get_next_index(Tab, RowIndex) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_snmp_hook, get_next_index, [Tab, RowIndex]);
+snmp_get_next_index(Tab, _RowIndex) ->
+ abort({bad_type, Tab}).
+
+snmp_get_mnesia_key(Tab, RowIndex) when atom(Tab), Tab /= schema ->
+ dirty_rpc(Tab, mnesia_snmp_hook, get_mnesia_key, [Tab, RowIndex]);
+snmp_get_mnesia_key(Tab, _RowIndex) ->
+ abort({bad_type, Tab}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Textfile access
+
+load_textfile(F) ->
+ mnesia_text:load_textfile(F).
+dump_to_textfile(F) ->
+ mnesia_text:dump_to_textfile(F).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Mnemosyne exclusive
+
+get_activity_id() ->
+ get(mnesia_activity_state).
+
+put_activity_id(Activity) ->
+ mnesia_tm:put_activity_id(Activity).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.hrl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.hrl
new file mode 100644
index 0000000000..b9715ad927
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia.hrl
@@ -0,0 +1,118 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia.hrl,v 1.1 2008/12/17 09:53:37 mikpe Exp $
+%%
+
+-define(APPLICATION, mnesia).
+
+-define(ets_lookup(Tab, Key), ets:lookup(Tab, Key)).
+-define(ets_lookup_element(Tab, Key, Pos), ets:lookup_element(Tab, Key, Pos)).
+-define(ets_insert(Tab, Rec), ets:insert(Tab, Rec)).
+-define(ets_delete(Tab, Key), ets:delete(Tab, Key)).
+-define(ets_match_delete(Tab, Pat), ets:match_delete(Tab, Pat)).
+-define(ets_match_object(Tab, Pat), ets:match_object(Tab, Pat)).
+-define(ets_match(Tab, Pat), ets:match(Tab, Pat)).
+-define(ets_info(Tab, Item), ets:info(Tab, Item)).
+-define(ets_update_counter(Tab, Key, Incr), ets:update_counter(Tab, Key, Incr)).
+-define(ets_first(Tab), ets:first(Tab)).
+-define(ets_next(Tab, Key), ets:next(Tab, Key)).
+-define(ets_last(Tab), ets:last(Tab)).
+-define(ets_prev(Tab, Key), ets:prev(Tab, Key)).
+-define(ets_slot(Tab, Pos), ets:slot(Tab, Pos)).
+-define(ets_new_table(Tab, Props), ets:new(Tab, Props)).
+-define(ets_delete_table(Tab), ets:delete(Tab)).
+-define(ets_fixtable(Tab, Bool), ets:fixtable(Tab, Bool)).
+
+-define(catch_val(Var), (catch ?ets_lookup_element(mnesia_gvar, Var, 2))).
+
+%% It's important that counter is first, since we compare tid's
+
+-record(tid,
+ {counter, %% serial no for tid
+ pid}). %% owner of tid
+
+
+-record(tidstore,
+ {store, %% current ets table for tid
+ up_stores = [], %% list of upper layer stores for nested trans
+ level = 1}). %% transaction level
+
+-define(unique_cookie, {erlang:now(), node()}).
+
+-record(cstruct, {name, % Atom
+ type = set, % set | bag
+ ram_copies = [], % [Node]
+ disc_copies = [], % [Node]
+ disc_only_copies = [], % [Node]
+ load_order = 0, % Integer
+ access_mode = read_write, % read_write | read_only
+ index = [], % [Integer]
+ snmp = [], % Snmp Ustruct
+ local_content = false, % true | false
+ record_name = {bad_record_name}, % Atom (Default = Name)
+ attributes = [key, val], % [Atom]
+ user_properties = [], % [Record]
+ frag_properties = [], % [{Key, Val]
+ cookie = ?unique_cookie, % Term
+ version = {{2, 0}, []}}). % {{Integer, Integer}, [Node]}
+
+%% Record for the head structure in Mnesia's log files
+%%
+%% The definition of this record may *NEVER* be changed
+%% since it may be written to very old backup files.
+%% By holding this record definition stable we can be
+%% able to comprahend backups from timepoint 0. It also
+%% allows us to use the backup format as an interchange
+%% format between Mnesia releases.
+
+-record(log_header,{log_kind,
+ log_version,
+ mnesia_version,
+ node,
+ now}).
+
+%% Commit records stored in the transaction log
+-record(commit, {node,
+ decision, % presume_commit | Decision
+ ram_copies = [],
+ disc_copies = [],
+ disc_only_copies = [],
+ snmp = [],
+ schema_ops = []
+ }).
+
+-record(decision, {tid,
+ outcome, % presume_abort | committed
+ disc_nodes,
+ ram_nodes}).
+
+%% Maybe cyclic wait
+-record(cyclic, {node = node(),
+ oid, % {Tab, Key}
+ op, % read | write
+ lock, % read | write
+ lucky
+ }).
+
+%% Managing conditional debug functions
+
+-ifdef(debug).
+ -define(eval_debug_fun(I, C),
+ mnesia_lib:eval_debug_fun(I, C, ?FILE, ?LINE)).
+-else.
+ -define(eval_debug_fun(I, C), ok).
+-endif.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_backup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_backup.erl
new file mode 100644
index 0000000000..a1fbb21d94
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_backup.erl
@@ -0,0 +1,195 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_backup.erl,v 1.1 2008/12/17 09:53:37 mikpe Exp $
+%%
+%0
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% This module contains one implementation of callback functions
+%% used by Mnesia at backup and restore. The user may however
+%% write an own module the same interface as mnesia_backup and
+%% configure Mnesia so the alternate module performs the actual
+%% accesses to the backup media. This means that the user may put
+%% the backup on medias that Mnesia does not know about, possibly
+%% on hosts where Erlang is not running.
+%%
+%% The OpaqueData argument is never interpreted by other parts of
+%% Mnesia. It is the property of this module. Alternate implementations
+%% of this module may have different interpretations of OpaqueData.
+%% The OpaqueData argument given to open_write/1 and open_read/1
+%% are forwarded directly from the user.
+%%
+%% All functions must return {ok, NewOpaqueData} or {error, Reason}.
+%%
+%% The NewOpaqueData arguments returned by backup callback functions will
+%% be given as input when the next backup callback function is invoked.
+%% If any return value does not match {ok, _} the backup will be aborted.
+%%
+%% The NewOpaqueData arguments returned by restore callback functions will
+%% be given as input when the next restore callback function is invoked
+%% If any return value does not match {ok, _} the restore will be aborted.
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(mnesia_backup).
+-behaviour(mnesia_backup).
+
+-include_lib("kernel/include/file.hrl").
+
+-export([
+ %% Write access
+ open_write/1,
+ write/2,
+ commit_write/1,
+ abort_write/1,
+
+ %% Read access
+ open_read/1,
+ read/1,
+ close_read/1
+ ]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Backup callback interface
+-record(backup, {tmp_file, file, file_desc}).
+
+%% Opens backup media for write
+%%
+%% Returns {ok, OpaqueData} or {error, Reason}
+open_write(OpaqueData) ->
+ File = OpaqueData,
+ Tmp = lists:concat([File,".BUPTMP"]),
+ file:delete(Tmp),
+ file:delete(File),
+ case disk_log:open([{name, make_ref()},
+ {file, Tmp},
+ {repair, false},
+ {linkto, self()}]) of
+ {ok, Fd} ->
+ {ok, #backup{tmp_file = Tmp, file = File, file_desc = Fd}};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%% Writes BackupItems to the backup media
+%%
+%% Returns {ok, OpaqueData} or {error, Reason}
+write(OpaqueData, BackupItems) ->
+ B = OpaqueData,
+ case disk_log:log_terms(B#backup.file_desc, BackupItems) of
+ ok ->
+ {ok, B};
+ {error, Reason} ->
+ abort_write(B),
+ {error, Reason}
+ end.
+
+%% Closes the backup media after a successful backup
+%%
+%% Returns {ok, ReturnValueToUser} or {error, Reason}
+commit_write(OpaqueData) ->
+ B = OpaqueData,
+ case disk_log:sync(B#backup.file_desc) of
+ ok ->
+ case disk_log:close(B#backup.file_desc) of
+ ok ->
+ case file:rename(B#backup.tmp_file, B#backup.file) of
+ ok ->
+ {ok, B#backup.file};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%% Closes the backup media after an interrupted backup
+%%
+%% Returns {ok, ReturnValueToUser} or {error, Reason}
+abort_write(BackupRef) ->
+ Res = disk_log:close(BackupRef#backup.file_desc),
+ file:delete(BackupRef#backup.tmp_file),
+ case Res of
+ ok ->
+ {ok, BackupRef#backup.file};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restore callback interface
+
+-record(restore, {file, file_desc, cont}).
+
+%% Opens backup media for read
+%%
+%% Returns {ok, OpaqueData} or {error, Reason}
+open_read(OpaqueData) ->
+ File = OpaqueData,
+ case file:read_file_info(File) of
+ {error, Reason} ->
+ {error, Reason};
+ _FileInfo -> %% file exists
+ case disk_log:open([{file, File},
+ {name, make_ref()},
+ {repair, false},
+ {mode, read_only},
+ {linkto, self()}]) of
+ {ok, Fd} ->
+ {ok, #restore{file = File, file_desc = Fd, cont = start}};
+ {repaired, Fd, _, {badbytes, 0}} ->
+ {ok, #restore{file = File, file_desc = Fd, cont = start}};
+ {repaired, Fd, _, _} ->
+ {ok, #restore{file = File, file_desc = Fd, cont = start}};
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end.
+
+%% Reads BackupItems from the backup media
+%%
+%% Returns {ok, OpaqueData, BackupItems} or {error, Reason}
+%%
+%% BackupItems == [] is interpreted as eof
+read(OpaqueData) ->
+ R = OpaqueData,
+ Fd = R#restore.file_desc,
+ case disk_log:chunk(Fd, R#restore.cont) of
+ {error, Reason} ->
+ {error, {"Possibly truncated", Reason}};
+ eof ->
+ {ok, R, []};
+ {Cont, []} ->
+ read(R#restore{cont = Cont});
+ {Cont, BackupItems} ->
+ {ok, R#restore{cont = Cont}, BackupItems}
+ end.
+
+%% Closes the backup media after restore
+%%
+%% Returns {ok, ReturnValueToUser} or {error, Reason}
+close_read(OpaqueData) ->
+ R = OpaqueData,
+ case disk_log:close(R#restore.file_desc) of
+ ok -> {ok, R#restore.file};
+ {error, Reason} -> {error, Reason}
+ end.
+%0
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl
new file mode 100644
index 0000000000..f03dc029cc
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_bup.erl
@@ -0,0 +1,1169 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_bup.erl,v 1.1 2008/12/17 09:53:37 mikpe Exp $
+%%
+-module(mnesia_bup).
+-export([
+ %% Public interface
+ iterate/4,
+ read_schema/2,
+ fallback_bup/0,
+ fallback_exists/0,
+ tm_fallback_start/1,
+ create_schema/1,
+ install_fallback/1,
+ install_fallback/2,
+ uninstall_fallback/0,
+ uninstall_fallback/1,
+ traverse_backup/4,
+ traverse_backup/6,
+ make_initial_backup/3,
+ fallback_to_schema/0,
+ lookup_schema/2,
+ schema2bup/1,
+ refresh_cookie/2,
+
+ %% Internal
+ fallback_receiver/2,
+ install_fallback_master/2,
+ uninstall_fallback_master/2,
+ local_uninstall_fallback/2,
+ do_traverse_backup/7,
+ trav_apply/4
+ ]).
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [verbose/2, dbg_out/2]).
+
+-record(restore, {mode, bup_module, bup_data}).
+
+-record(fallback_args, {opaque,
+ scope = global,
+ module = mnesia_monitor:get_env(backup_module),
+ use_default_dir = true,
+ mnesia_dir,
+ fallback_bup,
+ fallback_tmp,
+ skip_tables = [],
+ keep_tables = [],
+ default_op = keep_tables
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Backup iterator
+
+%% Reads schema section and iterates over all records in a backup.
+%%
+%% Fun(BunchOfRecords, Header, Schema, Acc) is applied when a suitable amount
+%% of records has been collected.
+%%
+%% BunchOfRecords will be [] when the iteration is done.
+iterate(Mod, Fun, Opaque, Acc) ->
+ R = #restore{bup_module = Mod, bup_data = Opaque},
+ case catch read_schema_section(R) of
+ {error, Reason} ->
+ {error, Reason};
+ {R2, {Header, Schema, Rest}} ->
+ case catch iter(R2, Header, Schema, Fun, Acc, Rest) of
+ {ok, R3, Res} ->
+ catch safe_apply(R3, close_read, [R3#restore.bup_data]),
+ {ok, Res};
+ {error, Reason} ->
+ catch safe_apply(R2, close_read, [R2#restore.bup_data]),
+ {error, Reason};
+ {'EXIT', Pid, Reason} ->
+ catch safe_apply(R2, close_read, [R2#restore.bup_data]),
+ {error, {'EXIT', Pid, Reason}};
+ {'EXIT', Reason} ->
+ catch safe_apply(R2, close_read, [R2#restore.bup_data]),
+ {error, {'EXIT', Reason}}
+ end
+ end.
+
+iter(R, Header, Schema, Fun, Acc, []) ->
+ case safe_apply(R, read, [R#restore.bup_data]) of
+ {R2, []} ->
+ Res = Fun([], Header, Schema, Acc),
+ {ok, R2, Res};
+ {R2, BupItems} ->
+ iter(R2, Header, Schema, Fun, Acc, BupItems)
+ end;
+iter(R, Header, Schema, Fun, Acc, BupItems) ->
+ Acc2 = Fun(BupItems, Header, Schema, Acc),
+ iter(R, Header, Schema, Fun, Acc2, []).
+
+safe_apply(R, write, [_, Items]) when Items == [] ->
+ R;
+safe_apply(R, What, Args) ->
+ Abort = fun(Re) -> abort_restore(R, What, Args, Re) end,
+ receive
+ {'EXIT', Pid, Re} -> Abort({'EXIT', Pid, Re})
+ after 0 ->
+ Mod = R#restore.bup_module,
+ case catch apply(Mod, What, Args) of
+ {ok, Opaque, Items} when What == read ->
+ {R#restore{bup_data = Opaque}, Items};
+ {ok, Opaque} when What /= read->
+ R#restore{bup_data = Opaque};
+ {error, Re} ->
+ Abort(Re);
+ Re ->
+ Abort(Re)
+ end
+ end.
+
+abort_restore(R, What, Args, Reason) ->
+ Mod = R#restore.bup_module,
+ Opaque = R#restore.bup_data,
+ dbg_out("Restore aborted. ~p:~p~p -> ~p~n",
+ [Mod, What, Args, Reason]),
+ catch apply(Mod, close_read, [Opaque]),
+ throw({error, Reason}).
+
+fallback_to_schema() ->
+ Fname = fallback_bup(),
+ fallback_to_schema(Fname).
+
+fallback_to_schema(Fname) ->
+ Mod = mnesia_backup,
+ case read_schema(Mod, Fname) of
+ {error, Reason} ->
+ {error, Reason};
+ Schema ->
+ case catch lookup_schema(schema, Schema) of
+ {error, _} ->
+ {error, "No schema in fallback"};
+ List ->
+ {ok, fallback, List}
+ end
+ end.
+
+%% Opens Opaque reads schema and then close
+read_schema(Mod, Opaque) ->
+ R = #restore{bup_module = Mod, bup_data = Opaque},
+ case catch read_schema_section(R) of
+ {error, Reason} ->
+ {error, Reason};
+ {R2, {_Header, Schema, _}} ->
+ catch safe_apply(R2, close_read, [R2#restore.bup_data]),
+ Schema
+ end.
+
+%% Open backup media and extract schema
+%% rewind backup media and leave it open
+%% Returns {R, {Header, Schema}}
+read_schema_section(R) ->
+ case catch do_read_schema_section(R) of
+ {'EXIT', Reason} ->
+ catch safe_apply(R, close_read, [R#restore.bup_data]),
+ {error, {'EXIT', Reason}};
+ {error, Reason} ->
+ catch safe_apply(R, close_read, [R#restore.bup_data]),
+ {error, Reason};
+ {R2, {H, Schema, Rest}} ->
+ Schema2 = convert_schema(H#log_header.log_version, Schema),
+ {R2, {H, Schema2, Rest}}
+ end.
+
+do_read_schema_section(R) ->
+ R2 = safe_apply(R, open_read, [R#restore.bup_data]),
+ {R3, RawSchema} = safe_apply(R2, read, [R2#restore.bup_data]),
+ do_read_schema_section(R3, verify_header(RawSchema), []).
+
+do_read_schema_section(R, {ok, B, C, []}, Acc) ->
+ case safe_apply(R, read, [R#restore.bup_data]) of
+ {R2, []} ->
+ {R2, {B, Acc, []}};
+ {R2, RawSchema} ->
+ do_read_schema_section(R2, {ok, B, C, RawSchema}, Acc)
+ end;
+
+do_read_schema_section(R, {ok, B, C, [Head | Tail]}, Acc)
+ when element(1, Head) == schema ->
+ do_read_schema_section(R, {ok, B, C, Tail}, Acc ++ [Head]);
+
+do_read_schema_section(R, {ok, B, _C, Rest}, Acc) ->
+ {R, {B, Acc, Rest}};
+
+do_read_schema_section(_R, {error, Reason}, _Acc) ->
+ {error, Reason}.
+
+verify_header([H | RawSchema]) when record(H, log_header) ->
+ Current = mnesia_log:backup_log_header(),
+ if
+ H#log_header.log_kind == Current#log_header.log_kind ->
+ Versions = ["0.1", "1.1", Current#log_header.log_version],
+ case lists:member(H#log_header.log_version, Versions) of
+ true ->
+ {ok, H, Current, RawSchema};
+ false ->
+ {error, {"Bad header version. Cannot be used as backup.", H}}
+ end;
+ true ->
+ {error, {"Bad kind of header. Cannot be used as backup.", H}}
+ end;
+verify_header(RawSchema) ->
+ {error, {"Missing header. Cannot be used as backup.", catch hd(RawSchema)}}.
+
+refresh_cookie(Schema, NewCookie) ->
+ case lists:keysearch(schema, 2, Schema) of
+ {value, {schema, schema, List}} ->
+ Cs = mnesia_schema:list2cs(List),
+ Cs2 = Cs#cstruct{cookie = NewCookie},
+ Item = {schema, schema, mnesia_schema:cs2list(Cs2)},
+ lists:keyreplace(schema, 2, Schema, Item);
+
+ false ->
+ Reason = "No schema found. Cannot be used as backup.",
+ throw({error, {Reason, Schema}})
+ end.
+
+%% Convert schema items from an external backup
+%% If backup format is the latest, no conversion is needed
+%% All supported backup formats should have their converters
+%% here as separate function clauses.
+convert_schema("0.1", Schema) ->
+ convert_0_1(Schema);
+convert_schema("1.1", Schema) ->
+ %% The new backup format is a pure extension of the old one
+ Current = mnesia_log:backup_log_header(),
+ convert_schema(Current#log_header.log_version, Schema);
+convert_schema(Latest, Schema) ->
+ H = mnesia_log:backup_log_header(),
+ if
+ H#log_header.log_version == Latest ->
+ Schema;
+ true ->
+ Reason = "Bad backup header version. Cannot convert schema.",
+ throw({error, {Reason, H}})
+ end.
+
+%% Backward compatibility for 0.1
+convert_0_1(Schema) ->
+ case lists:keysearch(schema, 2, Schema) of
+ {value, {schema, schema, List}} ->
+ Schema2 = lists:keydelete(schema, 2, Schema),
+ Cs = mnesia_schema:list2cs(List),
+ convert_0_1(Schema2, [], Cs);
+ false ->
+ List = mnesia_schema:get_initial_schema(disc_copies, [node()]),
+ Cs = mnesia_schema:list2cs(List),
+ convert_0_1(Schema, [], Cs)
+ end.
+
+convert_0_1([{schema, cookie, Cookie} | Schema], Acc, Cs) ->
+ convert_0_1(Schema, Acc, Cs#cstruct{cookie = Cookie});
+convert_0_1([{schema, db_nodes, DbNodes} | Schema], Acc, Cs) ->
+ convert_0_1(Schema, Acc, Cs#cstruct{disc_copies = DbNodes});
+convert_0_1([{schema, version, Version} | Schema], Acc, Cs) ->
+ convert_0_1(Schema, Acc, Cs#cstruct{version = Version});
+convert_0_1([{schema, Tab, Def} | Schema], Acc, Cs) ->
+ Head =
+ case lists:keysearch(index, 1, Def) of
+ {value, {index, PosList}} ->
+ %% Remove the snmp "index"
+ P = PosList -- [snmp],
+ Def2 = lists:keyreplace(index, 1, Def, {index, P}),
+ {schema, Tab, Def2};
+ false ->
+ {schema, Tab, Def}
+ end,
+ convert_0_1(Schema, [Head | Acc], Cs);
+convert_0_1([Head | Schema], Acc, Cs) ->
+ convert_0_1(Schema, [Head | Acc], Cs);
+convert_0_1([], Acc, Cs) ->
+ [schema2bup({schema, schema, Cs}) | Acc].
+
+%% Returns Val or throw error
+lookup_schema(Key, Schema) ->
+ case lists:keysearch(Key, 2, Schema) of
+ {value, {schema, Key, Val}} -> Val;
+ false -> throw({error, {"Cannot lookup", Key}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Backup compatibility
+
+%% Convert internal schema items to backup dito
+schema2bup({schema, Tab}) ->
+ {schema, Tab};
+schema2bup({schema, Tab, TableDef}) ->
+ {schema, Tab, mnesia_schema:cs2list(TableDef)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Create schema on the given nodes
+%% Requires that old schemas has been deleted
+%% Returns ok | {error, Reason}
+create_schema([]) ->
+ create_schema([node()]);
+create_schema(Ns) when list(Ns) ->
+ case is_set(Ns) of
+ true ->
+ create_schema(Ns, mnesia_schema:ensure_no_schema(Ns));
+ false ->
+ {error, {combine_error, Ns}}
+ end;
+create_schema(Ns) ->
+ {error, {badarg, Ns}}.
+
+is_set(List) when list(List) ->
+ ordsets:is_set(lists:sort(List));
+is_set(_) ->
+ false.
+
+create_schema(Ns, ok) ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ case mnesia_monitor:get_env(schema_location) of
+ ram ->
+ {error, {has_no_disc, node()}};
+ _ ->
+ case mnesia_schema:opt_create_dir(true, mnesia_lib:dir()) of
+ {error, What} ->
+ {error, What};
+ ok ->
+ Mod = mnesia_backup,
+ Str = mk_str(),
+ File = mnesia_lib:dir(Str),
+ file:delete(File),
+ case catch make_initial_backup(Ns, File, Mod) of
+ {ok, _Res} ->
+ case do_install_fallback(File, Mod) of
+ ok ->
+ file:delete(File),
+ ok;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+create_schema(_Ns, {error, Reason}) ->
+ {error, Reason};
+create_schema(_Ns, Reason) ->
+ {error, Reason}.
+
+mk_str() ->
+ Now = [integer_to_list(I) || I <- tuple_to_list(now())],
+ lists:concat([node()] ++ Now ++ ".TMP").
+
+make_initial_backup(Ns, Opaque, Mod) ->
+ Schema = [{schema, schema, mnesia_schema:get_initial_schema(disc_copies, Ns)}],
+ O2 = do_apply(Mod, open_write, [Opaque], Opaque),
+ O3 = do_apply(Mod, write, [O2, [mnesia_log:backup_log_header()]], O2),
+ O4 = do_apply(Mod, write, [O3, Schema], O3),
+ O5 = do_apply(Mod, commit_write, [O4], O4),
+ {ok, O5}.
+
+do_apply(_, write, [_, Items], Opaque) when Items == [] ->
+ Opaque;
+do_apply(Mod, What, Args, _Opaque) ->
+ case catch apply(Mod, What, Args) of
+ {ok, Opaque2} -> Opaque2;
+ {error, Reason} -> throw({error, Reason});
+ {'EXIT', Reason} -> throw({error, {'EXIT', Reason}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Restore
+
+%% Restore schema and possibly other tables from a backup
+%% and replicate them to the necessary nodes
+%% Requires that old schemas has been deleted
+%% Returns ok | {error, Reason}
+install_fallback(Opaque) ->
+ install_fallback(Opaque, []).
+
+install_fallback(Opaque, Args) ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ do_install_fallback(Opaque, Args);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+do_install_fallback(Opaque, Mod) when atom(Mod) ->
+ do_install_fallback(Opaque, [{module, Mod}]);
+do_install_fallback(Opaque, Args) when list(Args) ->
+ case check_fallback_args(Args, #fallback_args{opaque = Opaque}) of
+ {ok, FA} ->
+ do_install_fallback(FA);
+ {error, Reason} ->
+ {error, Reason}
+ end;
+do_install_fallback(_Opaque, Args) ->
+ {error, {badarg, Args}}.
+
+check_fallback_args([Arg | Tail], FA) ->
+ case catch check_fallback_arg_type(Arg, FA) of
+ {'EXIT', _Reason} ->
+ {error, {badarg, Arg}};
+ FA2 ->
+ check_fallback_args(Tail, FA2)
+ end;
+check_fallback_args([], FA) ->
+ {ok, FA}.
+
+check_fallback_arg_type(Arg, FA) ->
+ case Arg of
+ {scope, global} ->
+ FA#fallback_args{scope = global};
+ {scope, local} ->
+ FA#fallback_args{scope = local};
+ {module, Mod} ->
+ Mod2 = mnesia_monitor:do_check_type(backup_module, Mod),
+ FA#fallback_args{module = Mod2};
+ {mnesia_dir, Dir} ->
+ FA#fallback_args{mnesia_dir = Dir,
+ use_default_dir = false};
+ {keep_tables, Tabs} ->
+ atom_list(Tabs),
+ FA#fallback_args{keep_tables = Tabs};
+ {skip_tables, Tabs} ->
+ atom_list(Tabs),
+ FA#fallback_args{skip_tables = Tabs};
+ {default_op, keep_tables} ->
+ FA#fallback_args{default_op = keep_tables};
+ {default_op, skip_tables} ->
+ FA#fallback_args{default_op = skip_tables}
+ end.
+
+atom_list([H | T]) when atom(H) ->
+ atom_list(T);
+atom_list([]) ->
+ ok.
+
+do_install_fallback(FA) ->
+ Pid = spawn_link(?MODULE, install_fallback_master, [self(), FA]),
+ Res =
+ receive
+ {'EXIT', Pid, Reason} -> % if appl has trapped exit
+ {error, {'EXIT', Reason}};
+ {Pid, Res2} ->
+ case Res2 of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ {error, {"Cannot install fallback", Reason}}
+ end
+ end,
+ Res.
+
+install_fallback_master(ClientPid, FA) ->
+ process_flag(trap_exit, true),
+ State = {start, FA},
+ Opaque = FA#fallback_args.opaque,
+ Mod = FA#fallback_args.module,
+ Res = (catch iterate(Mod, fun restore_recs/4, Opaque, State)),
+ unlink(ClientPid),
+ ClientPid ! {self(), Res},
+ exit(shutdown).
+
+restore_recs(_, _, _, stop) ->
+ throw({error, "restore_recs already stopped"});
+
+restore_recs(Recs, Header, Schema, {start, FA}) ->
+ %% No records in backup
+ Schema2 = convert_schema(Header#log_header.log_version, Schema),
+ CreateList = lookup_schema(schema, Schema2),
+ case catch mnesia_schema:list2cs(CreateList) of
+ {'EXIT', Reason} ->
+ throw({error, {"Bad schema in restore_recs", Reason}});
+ Cs ->
+ Ns = get_fallback_nodes(FA, Cs#cstruct.disc_copies),
+ global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity),
+ Args = [self(), FA],
+ Pids = [spawn_link(N, ?MODULE, fallback_receiver, Args) || N <- Ns],
+ send_fallback(Pids, {start, Header, Schema2}),
+ Res = restore_recs(Recs, Header, Schema2, Pids),
+ global:del_lock({{mnesia_table_lock, schema}, self()}, Ns),
+ Res
+ end;
+
+restore_recs([], _Header, _Schema, Pids) ->
+ send_fallback(Pids, swap),
+ send_fallback(Pids, stop),
+ stop;
+
+restore_recs(Recs, _, _, Pids) ->
+ send_fallback(Pids, {records, Recs}),
+ Pids.
+
+get_fallback_nodes(FA, Ns) ->
+ This = node(),
+ case lists:member(This, Ns) of
+ true ->
+ case FA#fallback_args.scope of
+ global -> Ns;
+ local -> [This]
+ end;
+ false ->
+ throw({error, {"No disc resident schema on local node", Ns}})
+ end.
+
+send_fallback(Pids, Msg) when list(Pids), Pids /= [] ->
+ lists:foreach(fun(Pid) -> Pid ! {self(), Msg} end, Pids),
+ rec_answers(Pids, []).
+
+rec_answers([], Acc) ->
+ case {lists:keysearch(error, 1, Acc), mnesia_lib:uniq(Acc)} of
+ {{value, {error, Val}}, _} -> throw({error, Val});
+ {_, [SameAnswer]} -> SameAnswer;
+ {_, Other} -> throw({error, {"Different answers", Other}})
+ end;
+rec_answers(Pids, Acc) ->
+ receive
+ {'EXIT', Pid, stopped} ->
+ Pids2 = lists:delete(Pid, Pids),
+ rec_answers(Pids2, [stopped|Acc]);
+ {'EXIT', Pid, Reason} ->
+ Pids2 = lists:delete(Pid, Pids),
+ rec_answers(Pids2, [{error, {'EXIT', Pid, Reason}}|Acc]);
+ {Pid, Reply} ->
+ Pids2 = lists:delete(Pid, Pids),
+ rec_answers(Pids2, [Reply|Acc])
+ end.
+
+fallback_exists() ->
+ Fname = fallback_bup(),
+ fallback_exists(Fname).
+
+fallback_exists(Fname) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_lib:exists(Fname);
+ false ->
+ case ?catch_val(active_fallback) of
+ {'EXIT', _} -> false;
+ Bool -> Bool
+ end
+ end.
+
+fallback_name() -> "FALLBACK.BUP".
+fallback_bup() -> mnesia_lib:dir(fallback_name()).
+
+fallback_tmp_name() -> "FALLBACK.TMP".
+%% fallback_full_tmp_name() -> mnesia_lib:dir(fallback_tmp_name()).
+
+fallback_receiver(Master, FA) ->
+ process_flag(trap_exit, true),
+
+ case catch register(mnesia_fallback, self()) of
+ {'EXIT', _} ->
+ Reason = {already_exists, node()},
+ local_fallback_error(Master, Reason);
+ true ->
+ FA2 = check_fallback_dir(Master, FA),
+ Bup = FA2#fallback_args.fallback_bup,
+ case mnesia_lib:exists(Bup) of
+ true ->
+ Reason2 = {already_exists, node()},
+ local_fallback_error(Master, Reason2);
+ false ->
+ Mod = mnesia_backup,
+ Tmp = FA2#fallback_args.fallback_tmp,
+ R = #restore{mode = replace,
+ bup_module = Mod,
+ bup_data = Tmp},
+ file:delete(Tmp),
+ case catch fallback_receiver_loop(Master, R, FA2, schema) of
+ {error, Reason} ->
+ local_fallback_error(Master, Reason);
+ Other ->
+ exit(Other)
+ end
+ end
+ end.
+
+local_fallback_error(Master, Reason) ->
+ Master ! {self(), {error, Reason}},
+ unlink(Master),
+ exit(Reason).
+
+check_fallback_dir(Master, FA) ->
+ case mnesia:system_info(schema_location) of
+ ram ->
+ Reason = {has_no_disc, node()},
+ local_fallback_error(Master, Reason);
+ _ ->
+ Dir = check_fallback_dir_arg(Master, FA),
+ Bup = filename:join([Dir, fallback_name()]),
+ Tmp = filename:join([Dir, fallback_tmp_name()]),
+ FA#fallback_args{fallback_bup = Bup,
+ fallback_tmp = Tmp,
+ mnesia_dir = Dir}
+ end.
+
+check_fallback_dir_arg(Master, FA) ->
+ case FA#fallback_args.use_default_dir of
+ true ->
+ mnesia_lib:dir();
+ false when FA#fallback_args.scope == local ->
+ Dir = FA#fallback_args.mnesia_dir,
+ case catch mnesia_monitor:do_check_type(dir, Dir) of
+ {'EXIT', _R} ->
+ Reason = {badarg, {dir, Dir}, node()},
+ local_fallback_error(Master, Reason);
+ AbsDir->
+ AbsDir
+ end;
+ false when FA#fallback_args.scope == global ->
+ Reason = {combine_error, global, dir, node()},
+ local_fallback_error(Master, Reason)
+ end.
+
+fallback_receiver_loop(Master, R, FA, State) ->
+ receive
+ {Master, {start, Header, Schema}} when State == schema ->
+ Dir = FA#fallback_args.mnesia_dir,
+ throw_bad_res(ok, mnesia_schema:opt_create_dir(true, Dir)),
+ R2 = safe_apply(R, open_write, [R#restore.bup_data]),
+ R3 = safe_apply(R2, write, [R2#restore.bup_data, [Header]]),
+ BupSchema = [schema2bup(S) || S <- Schema],
+ R4 = safe_apply(R3, write, [R3#restore.bup_data, BupSchema]),
+ Master ! {self(), ok},
+ fallback_receiver_loop(Master, R4, FA, records);
+
+ {Master, {records, Recs}} when State == records ->
+ R2 = safe_apply(R, write, [R#restore.bup_data, Recs]),
+ Master ! {self(), ok},
+ fallback_receiver_loop(Master, R2, FA, records);
+
+ {Master, swap} when State /= schema ->
+ ?eval_debug_fun({?MODULE, fallback_receiver_loop, pre_swap}, []),
+ safe_apply(R, commit_write, [R#restore.bup_data]),
+ Bup = FA#fallback_args.fallback_bup,
+ Tmp = FA#fallback_args.fallback_tmp,
+ throw_bad_res(ok, file:rename(Tmp, Bup)),
+ catch mnesia_lib:set(active_fallback, true),
+ ?eval_debug_fun({?MODULE, fallback_receiver_loop, post_swap}, []),
+ Master ! {self(), ok},
+ fallback_receiver_loop(Master, R, FA, stop);
+
+ {Master, stop} when State == stop ->
+ stopped;
+
+ Msg ->
+ safe_apply(R, abort_write, [R#restore.bup_data]),
+ Tmp = FA#fallback_args.fallback_tmp,
+ file:delete(Tmp),
+ throw({error, "Unexpected msg fallback_receiver_loop", Msg})
+ end.
+
+throw_bad_res(Expected, Expected) -> Expected;
+throw_bad_res(_Expected, {error, Actual}) -> throw({error, Actual});
+throw_bad_res(_Expected, Actual) -> throw({error, Actual}).
+
+-record(local_tab, {name, storage_type, dets_args, open, close, add, record_name}).
+
+tm_fallback_start(IgnoreFallback) ->
+ mnesia_schema:lock_schema(),
+ Res = do_fallback_start(fallback_exists(), IgnoreFallback),
+ mnesia_schema: unlock_schema(),
+ case Res of
+ ok -> ok;
+ {error, Reason} -> exit(Reason)
+ end.
+
+do_fallback_start(false, _IgnoreFallback) ->
+ ok;
+do_fallback_start(true, true) ->
+ verbose("Ignoring fallback at startup, but leaving it active...~n", []),
+ mnesia_lib:set(active_fallback, true),
+ ok;
+do_fallback_start(true, false) ->
+ verbose("Starting from fallback...~n", []),
+
+ Fname = fallback_bup(),
+ Mod = mnesia_backup,
+ Ets = ?ets_new_table(mnesia_local_tables, [set, public, {keypos, 2}]),
+ case catch iterate(Mod, fun restore_tables/4, Fname, {start, Ets}) of
+ {ok, Res} ->
+ case Res of
+ {local, _, LT} -> %% Close the last file
+ (LT#local_tab.close)(LT);
+ _ ->
+ ignore
+ end,
+ List = ?ets_match_object(Ets, '_'),
+ Tabs = [L#local_tab.name || L <- List, L#local_tab.name /= schema],
+ ?ets_delete_table(Ets),
+ mnesia_lib:swap_tmp_files(Tabs),
+ catch dets:close(schema),
+ Tmp = mnesia_lib:tab2tmp(schema),
+ Dat = mnesia_lib:tab2dat(schema),
+ case file:rename(Tmp, Dat) of
+ ok ->
+ file:delete(Fname),
+ ok;
+ {error, Reason} ->
+ file:delete(Tmp),
+ {error, {"Cannot start from fallback. Rename error.", Reason}}
+ end;
+ {error, Reason} ->
+ {error, {"Cannot start from fallback", Reason}};
+ {'EXIT', Reason} ->
+ {error, {"Cannot start from fallback", Reason}}
+ end.
+
+restore_tables(Recs, Header, Schema, {start, LocalTabs}) ->
+ Dir = mnesia_lib:dir(),
+ OldDir = filename:join([Dir, "OLD_DIR"]),
+ mnesia_schema:purge_dir(OldDir, []),
+ mnesia_schema:purge_dir(Dir, [fallback_name()]),
+ init_dat_files(Schema, LocalTabs),
+ State = {new, LocalTabs},
+ restore_tables(Recs, Header, Schema, State);
+restore_tables([Rec | Recs], Header, Schema, {new, LocalTabs}) ->
+ Tab = element(1, Rec),
+ case ?ets_lookup(LocalTabs, Tab) of
+ [] ->
+ State = {not_local, LocalTabs, Tab},
+ restore_tables(Recs, Header, Schema, State);
+ [L] when record(L, local_tab) ->
+ (L#local_tab.open)(Tab, L),
+ State = {local, LocalTabs, L},
+ restore_tables([Rec | Recs], Header, Schema, State)
+ end;
+restore_tables([Rec | Recs], Header, Schema, S = {not_local, LocalTabs, PrevTab}) ->
+ Tab = element(1, Rec),
+ if
+ Tab == PrevTab ->
+ restore_tables(Recs, Header, Schema, S);
+ true ->
+ State = {new, LocalTabs},
+ restore_tables([Rec | Recs], Header, Schema, State)
+ end;
+restore_tables([Rec | Recs], Header, Schema, State = {local, LocalTabs, L}) ->
+ Tab = element(1, Rec),
+ if
+ Tab == L#local_tab.name ->
+ Key = element(2, Rec),
+ (L#local_tab.add)(Tab, Key, Rec, L),
+ restore_tables(Recs, Header, Schema, State);
+ true ->
+ (L#local_tab.close)(L),
+ NState = {new, LocalTabs},
+ restore_tables([Rec | Recs], Header, Schema, NState)
+ end;
+restore_tables([], _Header, _Schema, State) ->
+ State.
+
+%% Creates all neccessary dat files and inserts
+%% the table definitions in the schema table
+%%
+%% Returns a list of local_tab tuples for all local tables
+init_dat_files(Schema, LocalTabs) ->
+ Fname = mnesia_lib:tab2tmp(schema),
+ Args = [{file, Fname}, {keypos, 2}, {type, set}],
+ case dets:open_file(schema, Args) of % Assume schema lock
+ {ok, _} ->
+ create_dat_files(Schema, LocalTabs),
+ dets:close(schema),
+ LocalTab = #local_tab{name = schema,
+ storage_type = disc_copies,
+ dets_args = Args,
+ open = fun open_media/2,
+ close = fun close_media/1,
+ add = fun add_to_media/4,
+ record_name = schema},
+ ?ets_insert(LocalTabs, LocalTab);
+ {error, Reason} ->
+ throw({error, {"Cannot open file", schema, Args, Reason}})
+ end.
+
+create_dat_files([{schema, schema, TabDef} | Tail], LocalTabs) ->
+ ok = dets:insert(schema, {schema, schema, TabDef}),
+ create_dat_files(Tail, LocalTabs);
+create_dat_files([{schema, Tab, TabDef} | Tail], LocalTabs) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ ok = dets:insert(schema, {schema, Tab, TabDef}),
+ RecName = Cs#cstruct.record_name,
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ cleanup_dat_file(Tab),
+ create_dat_files(Tail, LocalTabs);
+ disc_only_copies ->
+ Fname = mnesia_lib:tab2tmp(Tab),
+ Args = [{file, Fname}, {keypos, 2},
+ {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)}],
+ case mnesia_lib:dets_sync_open(Tab, Args) of
+ {ok, _} ->
+ mnesia_lib:dets_sync_close(Tab),
+ LocalTab = #local_tab{name = Tab,
+ storage_type = disc_only_copies,
+ dets_args = Args,
+ open = fun open_media/2,
+ close = fun close_media/1,
+ add = fun add_to_media/4,
+ record_name = RecName},
+ ?ets_insert(LocalTabs, LocalTab),
+ create_dat_files(Tail, LocalTabs);
+ {error, Reason} ->
+ throw({error, {"Cannot open file", Tab, Args, Reason}})
+ end;
+ ram_copies ->
+ %% Create .DCD if needed in open_media in case any ram_copies
+ %% are backed up.
+ LocalTab = #local_tab{name = Tab,
+ storage_type = ram_copies,
+ dets_args = ignore,
+ open = fun open_media/2,
+ close = fun close_media/1,
+ add = fun add_to_media/4,
+ record_name = RecName},
+ ?ets_insert(LocalTabs, LocalTab),
+ create_dat_files(Tail, LocalTabs);
+ Storage ->
+ %% Create DCD
+ Fname = mnesia_lib:tab2dcd(Tab),
+ file:delete(Fname),
+ Log = mnesia_log:open_log(fallback_tab, mnesia_log:dcd_log_header(),
+ Fname, false),
+ LocalTab = #local_tab{name = Tab,
+ storage_type = Storage,
+ dets_args = ignore,
+ open = fun open_media/2,
+ close = fun close_media/1,
+ add = fun add_to_media/4,
+ record_name = RecName},
+ mnesia_log:close_log(Log),
+ ?ets_insert(LocalTabs, LocalTab),
+ create_dat_files(Tail, LocalTabs)
+ end;
+create_dat_files([{schema, Tab} | Tail], LocalTabs) ->
+ cleanup_dat_file(Tab),
+ create_dat_files(Tail, LocalTabs);
+create_dat_files([], _LocalTabs) ->
+ ok.
+
+cleanup_dat_file(Tab) ->
+ ok = dets:delete(schema, {schema, Tab}),
+ mnesia_lib:cleanup_tmp_files([Tab]).
+
+open_media(Tab, LT) ->
+ case LT#local_tab.storage_type of
+ disc_only_copies ->
+ Args = LT#local_tab.dets_args,
+ case mnesia_lib:dets_sync_open(Tab, Args) of
+ {ok, _} -> ok;
+ {error, Reason} ->
+ throw({error, {"Cannot open file", Tab, Args, Reason}})
+ end;
+ ram_copies ->
+ %% Create .DCD as ram_copies backed up.
+ FnameDCD = mnesia_lib:tab2dcd(Tab),
+ file:delete(FnameDCD),
+ Log = mnesia_log:open_log(fallback_tab,
+ mnesia_log:dcd_log_header(),
+ FnameDCD, false),
+ mnesia_log:close_log(Log),
+
+ %% Create .DCL
+ Fname = mnesia_lib:tab2dcl(Tab),
+ file:delete(Fname),
+ mnesia_log:open_log({?MODULE,Tab},
+ mnesia_log:dcl_log_header(),
+ Fname, false, false,
+ read_write);
+ _ ->
+ Fname = mnesia_lib:tab2dcl(Tab),
+ file:delete(Fname),
+ mnesia_log:open_log({?MODULE,Tab},
+ mnesia_log:dcl_log_header(),
+ Fname, false, false,
+ read_write)
+ end.
+close_media(L) ->
+ Tab = L#local_tab.name,
+ case L#local_tab.storage_type of
+ disc_only_copies ->
+ mnesia_lib:dets_sync_close(Tab);
+ _ ->
+ mnesia_log:close_log({?MODULE,Tab})
+ end.
+
+add_to_media(Tab, Key, Rec, L) ->
+ RecName = L#local_tab.record_name,
+ case L#local_tab.storage_type of
+ disc_only_copies ->
+ case Rec of
+ {Tab, Key} ->
+ ok = dets:delete(Tab, Key);
+ (Rec) when Tab == RecName ->
+ ok = dets:insert(Tab, Rec);
+ (Rec) ->
+ Rec2 = setelement(1, Rec, RecName),
+ ok = dets:insert(Tab, Rec2)
+ end;
+ _ ->
+ Log = {?MODULE, Tab},
+ case Rec of
+ {Tab, Key} ->
+ mnesia_log:append(Log, {{Tab, Key}, {Tab, Key}, delete});
+ (Rec) when Tab == RecName ->
+ mnesia_log:append(Log, {{Tab, Key}, Rec, write});
+ (Rec) ->
+ Rec2 = setelement(1, Rec, RecName),
+ mnesia_log:append(Log, {{Tab, Key}, Rec2, write})
+ end
+ end.
+
+uninstall_fallback() ->
+ uninstall_fallback([{scope, global}]).
+
+uninstall_fallback(Args) ->
+ case check_fallback_args(Args, #fallback_args{}) of
+ {ok, FA} ->
+ do_uninstall_fallback(FA);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+do_uninstall_fallback(FA) ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ Pid = spawn_link(?MODULE, uninstall_fallback_master, [self(), FA]),
+ receive
+ {'EXIT', Pid, Reason} -> % if appl has trapped exit
+ {error, {'EXIT', Reason}};
+ {Pid, Res} ->
+ Res
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+uninstall_fallback_master(ClientPid, FA) ->
+ process_flag(trap_exit, true),
+
+ FA2 = check_fallback_dir(ClientPid, FA), % May exit
+ Bup = FA2#fallback_args.fallback_bup,
+ case fallback_to_schema(Bup) of
+ {ok, fallback, List} ->
+ Cs = mnesia_schema:list2cs(List),
+ case catch get_fallback_nodes(FA, Cs#cstruct.disc_copies) of
+ Ns when list(Ns) ->
+ do_uninstall(ClientPid, Ns, FA);
+ {error, Reason} ->
+ local_fallback_error(ClientPid, Reason)
+ end;
+ {error, Reason} ->
+ local_fallback_error(ClientPid, Reason)
+ end.
+
+do_uninstall(ClientPid, Ns, FA) ->
+ Args = [self(), FA],
+ global:set_lock({{mnesia_table_lock, schema}, self()}, Ns, infinity),
+ Pids = [spawn_link(N, ?MODULE, local_uninstall_fallback, Args) || N <- Ns],
+ Res = do_uninstall(ClientPid, Pids, [], [], ok),
+ global:del_lock({{mnesia_table_lock, schema}, self()}, Ns),
+ ClientPid ! {self(), Res},
+ unlink(ClientPid),
+ exit(shutdown).
+
+do_uninstall(ClientPid, [Pid | Pids], GoodPids, BadNodes, Res) ->
+ receive
+ %% {'EXIT', ClientPid, _} ->
+ %% client_exit;
+ {'EXIT', Pid, Reason} ->
+ BadNode = node(Pid),
+ BadRes = {error, {"Uninstall fallback", BadNode, Reason}},
+ do_uninstall(ClientPid, Pids, GoodPids, [BadNode | BadNodes], BadRes);
+ {Pid, {error, Reason}} ->
+ BadNode = node(Pid),
+ BadRes = {error, {"Uninstall fallback", BadNode, Reason}},
+ do_uninstall(ClientPid, Pids, GoodPids, [BadNode | BadNodes], BadRes);
+ {Pid, started} ->
+ do_uninstall(ClientPid, Pids, [Pid | GoodPids], BadNodes, Res)
+ end;
+do_uninstall(ClientPid, [], GoodPids, [], ok) ->
+ lists:foreach(fun(Pid) -> Pid ! {self(), do_uninstall} end, GoodPids),
+ rec_uninstall(ClientPid, GoodPids, ok);
+do_uninstall(_ClientPid, [], GoodPids, BadNodes, BadRes) ->
+ lists:foreach(fun(Pid) -> exit(Pid, shutdown) end, GoodPids),
+ {error, {node_not_running, BadNodes, BadRes}}.
+
+local_uninstall_fallback(Master, FA) ->
+ %% Don't trap exit
+
+ register(mnesia_fallback, self()), % May exit
+ FA2 = check_fallback_dir(Master, FA), % May exit
+ Master ! {self(), started},
+
+ receive
+ {Master, do_uninstall} ->
+ ?eval_debug_fun({?MODULE, uninstall_fallback2, pre_delete}, []),
+ catch mnesia_lib:set(active_fallback, false),
+ Tmp = FA2#fallback_args.fallback_tmp,
+ Bup = FA2#fallback_args.fallback_bup,
+ file:delete(Tmp),
+ Res =
+ case fallback_exists(Bup) of
+ true -> file:delete(Bup);
+ false -> ok
+ end,
+ ?eval_debug_fun({?MODULE, uninstall_fallback2, post_delete}, []),
+ Master ! {self(), Res},
+ unlink(Master),
+ exit(normal)
+ end.
+
+rec_uninstall(ClientPid, [Pid | Pids], AccRes) ->
+ receive
+ %% {'EXIT', ClientPid, _} ->
+ %% exit(shutdown);
+ {'EXIT', Pid, R} ->
+ Reason = {node_not_running, {node(Pid), R}},
+ rec_uninstall(ClientPid, Pids, {error, Reason});
+ {Pid, ok} ->
+ rec_uninstall(ClientPid, Pids, AccRes);
+ {Pid, BadRes} ->
+ rec_uninstall(ClientPid, Pids, BadRes)
+ end;
+rec_uninstall(ClientPid, [], Res) ->
+ ClientPid ! {self(), Res},
+ unlink(ClientPid),
+ exit(normal).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Backup traversal
+
+%% Iterate over a backup and produce a new backup.
+%% Fun(BackupItem, Acc) is applied for each BackupItem.
+%%
+%% Valid BackupItems are:
+%%
+%% {schema, Tab} Table to be deleted
+%% {schema, Tab, CreateList} Table to be created, CreateList may be empty
+%% {schema, db_nodes, DbNodes}List of nodes, defaults to [node()] OLD
+%% {schema, version, Version} Schema version OLD
+%% {schema, cookie, Cookie} Unique schema cookie OLD
+%% {Tab, Key} Oid for record to be deleted
+%% Record Record to be inserted.
+%%
+%% The Fun must return a tuple {BackupItems, NewAcc}
+%% where BackupItems is a list of valid BackupItems and
+%% NewAcc is a new accumulator value. Once BackupItems
+%% that not are schema related has been returned, no more schema
+%% items may be returned. The schema related items must always be
+%% first in the backup.
+%%
+%% If TargetMod == read_only, no new backup will be created.
+%%
+%% Opening of the source media will be performed by
+%% to SourceMod:open_read(Source)
+%%
+%% Opening of the target media will be performed by
+%% to TargetMod:open_write(Target)
+traverse_backup(Source, Target, Fun, Acc) ->
+ Mod = mnesia_monitor:get_env(backup_module),
+ traverse_backup(Source, Mod, Target, Mod, Fun, Acc).
+
+traverse_backup(Source, SourceMod, Target, TargetMod, Fun, Acc) ->
+ Args = [self(), Source, SourceMod, Target, TargetMod, Fun, Acc],
+ Pid = spawn_link(?MODULE, do_traverse_backup, Args),
+ receive
+ {'EXIT', Pid, Reason} ->
+ {error, {"Backup traversal crashed", Reason}};
+ {iter_done, Pid, Res} ->
+ Res
+ end.
+
+do_traverse_backup(ClientPid, Source, SourceMod, Target, TargetMod, Fun, Acc) ->
+ process_flag(trap_exit, true),
+ Iter =
+ if
+ TargetMod /= read_only ->
+ case catch do_apply(TargetMod, open_write, [Target], Target) of
+ {error, Error} ->
+ unlink(ClientPid),
+ ClientPid ! {iter_done, self(), {error, Error}},
+ exit(Error);
+ Else -> Else
+ end;
+ true ->
+ ignore
+ end,
+ A = {start, Fun, Acc, TargetMod, Iter},
+ Res =
+ case iterate(SourceMod, fun trav_apply/4, Source, A) of
+ {ok, {iter, _, Acc2, _, Iter2}} when TargetMod /= read_only ->
+ case catch do_apply(TargetMod, commit_write, [Iter2], Iter2) of
+ {error, Reason} ->
+ {error, Reason};
+ _ ->
+ {ok, Acc2}
+ end;
+ {ok, {iter, _, Acc2, _, _}} ->
+ {ok, Acc2};
+ {error, Reason} when TargetMod /= read_only->
+ catch do_apply(TargetMod, abort_write, [Iter], Iter),
+ {error, {"Backup traversal failed", Reason}};
+ {error, Reason} ->
+ {error, {"Backup traversal failed", Reason}}
+ end,
+ unlink(ClientPid),
+ ClientPid ! {iter_done, self(), Res}.
+
+trav_apply(Recs, _Header, _Schema, {iter, Fun, Acc, Mod, Iter}) ->
+ {NewRecs, Acc2} = filter_foldl(Fun, Acc, Recs),
+ if
+ Mod /= read_only, NewRecs /= [] ->
+ Iter2 = do_apply(Mod, write, [Iter, NewRecs], Iter),
+ {iter, Fun, Acc2, Mod, Iter2};
+ true ->
+ {iter, Fun, Acc2, Mod, Iter}
+ end;
+trav_apply(Recs, Header, Schema, {start, Fun, Acc, Mod, Iter}) ->
+ Iter2 =
+ if
+ Mod /= read_only ->
+ do_apply(Mod, write, [Iter, [Header]], Iter);
+ true ->
+ Iter
+ end,
+ TravAcc = trav_apply(Schema, Header, Schema, {iter, Fun, Acc, Mod, Iter2}),
+ trav_apply(Recs, Header, Schema, TravAcc).
+
+filter_foldl(Fun, Acc, [Head|Tail]) ->
+ case Fun(Head, Acc) of
+ {HeadItems, HeadAcc} when list(HeadItems) ->
+ {TailItems, TailAcc} = filter_foldl(Fun, HeadAcc, Tail),
+ {HeadItems ++ TailItems, TailAcc};
+ Other ->
+ throw({error, {"Fun must return a list", Other}})
+ end;
+filter_foldl(_Fun, Acc, []) ->
+ {[], Acc}.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint.erl
new file mode 100644
index 0000000000..aa2e99642b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint.erl
@@ -0,0 +1,1284 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_checkpoint.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_checkpoint).
+
+%% TM callback interface
+-export([
+ tm_add_copy/2,
+ tm_change_table_copy_type/3,
+ tm_del_copy/2,
+ tm_mnesia_down/1,
+ tm_prepare/1,
+ tm_retain/4,
+ tm_retain/5,
+ tm_enter_pending/1,
+ tm_enter_pending/3,
+ tm_exit_pending/1,
+ convert_cp_record/1
+ ]).
+
+%% Public interface
+-export([
+ activate/1,
+ checkpoints/0,
+ deactivate/1,
+ deactivate/2,
+ iterate/6,
+ most_local_node/2,
+ really_retain/2,
+ stop/0,
+ stop_iteration/1,
+ tables_and_cookie/1
+ ]).
+
+%% Internal
+-export([
+ call/2,
+ cast/2,
+ init/1,
+ remote_deactivate/1,
+ start/1
+ ]).
+
+%% sys callback interface
+-export([
+ system_code_change/4,
+ system_continue/3,
+ system_terminate/4
+ ]).
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [add/2, del/2, set/2, unset/1]).
+-import(mnesia_lib, [dbg_out/2]).
+
+-record(tm, {log, pending, transactions, checkpoints}).
+
+-record(checkpoint_args, {name = {now(), node()},
+ allow_remote = true,
+ ram_overrides_dump = false,
+ nodes = [],
+ node = node(),
+ now = now(),
+ cookie = ?unique_cookie,
+ min = [],
+ max = [],
+ pending_tab,
+ wait_for_old, % Initially undefined then List
+ is_activated = false,
+ ignore_new = [],
+ retainers = [],
+ iterators = [],
+ supervisor,
+ pid
+ }).
+
+%% Old record definition
+-record(checkpoint, {name,
+ allow_remote,
+ ram_overrides_dump,
+ nodes,
+ node,
+ now,
+ min,
+ max,
+ pending_tab,
+ wait_for_old,
+ is_activated,
+ ignore_new,
+ retainers,
+ iterators,
+ supervisor,
+ pid
+ }).
+
+-record(retainer, {cp_name, tab_name, store, writers = [], really_retain = true}).
+
+-record(iter, {tab_name, oid_tab, main_tab, retainer_tab, source, val, pid}).
+
+-record(pending, {tid, disc_nodes = [], ram_nodes = []}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% TM callback functions
+
+stop() ->
+ lists:foreach(fun(Name) -> call(Name, stop) end,
+ checkpoints()),
+ ok.
+
+tm_prepare(Cp) when record(Cp, checkpoint_args) ->
+ Name = Cp#checkpoint_args.name,
+ case lists:member(Name, checkpoints()) of
+ false ->
+ start_retainer(Cp);
+ true ->
+ {error, {already_exists, Name, node()}}
+ end;
+tm_prepare(Cp) when record(Cp, checkpoint) ->
+ %% Node with old protocol sent an old checkpoint record
+ %% and we have to convert it
+ case convert_cp_record(Cp) of
+ {ok, NewCp} ->
+ tm_prepare(NewCp);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+tm_mnesia_down(Node) ->
+ lists:foreach(fun(Name) -> cast(Name, {mnesia_down, Node}) end,
+ checkpoints()).
+
+%% Returns pending
+tm_enter_pending(Tid, DiscNs, RamNs) ->
+ Pending = #pending{tid = Tid, disc_nodes = DiscNs, ram_nodes = RamNs},
+ tm_enter_pending(Pending).
+
+tm_enter_pending(Pending) ->
+ PendingTabs = val(pending_checkpoints),
+ tm_enter_pending(PendingTabs, Pending).
+
+tm_enter_pending([], Pending) ->
+ Pending;
+tm_enter_pending([Tab | Tabs], Pending) ->
+ catch ?ets_insert(Tab, Pending),
+ tm_enter_pending(Tabs, Pending).
+
+tm_exit_pending(Tid) ->
+ Pids = val(pending_checkpoint_pids),
+ tm_exit_pending(Pids, Tid).
+
+tm_exit_pending([], Tid) ->
+ Tid;
+tm_exit_pending([Pid | Pids], Tid) ->
+ Pid ! {self(), {exit_pending, Tid}},
+ tm_exit_pending(Pids, Tid).
+
+enter_still_pending([Tid | Tids], Tab) ->
+ ?ets_insert(Tab, #pending{tid = Tid}),
+ enter_still_pending(Tids, Tab);
+enter_still_pending([], _Tab) ->
+ ok.
+
+
+%% Looks up checkpoints for functions in mnesia_tm.
+tm_retain(Tid, Tab, Key, Op) ->
+ case val({Tab, commit_work}) of
+ [{checkpoints, Checkpoints} | _ ] ->
+ tm_retain(Tid, Tab, Key, Op, Checkpoints);
+ _ ->
+ undefined
+ end.
+
+tm_retain(Tid, Tab, Key, Op, Checkpoints) ->
+ case Op of
+ clear_table ->
+ OldRecs = mnesia_lib:db_match_object(Tab, '_'),
+ send_group_retain(OldRecs, Checkpoints, Tid, Tab, []),
+ OldRecs;
+ _ ->
+ OldRecs = mnesia_lib:db_get(Tab, Key),
+ send_retain(Checkpoints, {retain, Tid, Tab, Key, OldRecs}),
+ OldRecs
+ end.
+
+send_group_retain([Rec | Recs], Checkpoints, Tid, Tab, [PrevRec | PrevRecs])
+ when element(2, Rec) /= element(2, PrevRec) ->
+ Key = element(2, PrevRec),
+ OldRecs = lists:reverse([PrevRec | PrevRecs]),
+ send_retain(Checkpoints, {retain, Tid, Tab, Key, OldRecs}),
+ send_group_retain(Recs, Checkpoints, Tid, Tab, [Rec]);
+send_group_retain([Rec | Recs], Checkpoints, Tid, Tab, Acc) ->
+ send_group_retain(Recs, Checkpoints, Tid, Tab, [Rec | Acc]);
+send_group_retain([], Checkpoints, Tid, Tab, [PrevRec | PrevRecs]) ->
+ Key = element(2, PrevRec),
+ OldRecs = lists:reverse([PrevRec | PrevRecs]),
+ send_retain(Checkpoints, {retain, Tid, Tab, Key, OldRecs}),
+ ok;
+send_group_retain([], _Checkpoints, _Tid, _Tab, []) ->
+ ok.
+
+send_retain([Name | Names], Msg) ->
+ cast(Name, Msg),
+ send_retain(Names, Msg);
+send_retain([], _Msg) ->
+ ok.
+
+tm_add_copy(Tab, Node) when Node /= node() ->
+ case val({Tab, commit_work}) of
+ [{checkpoints, Checkpoints} | _ ] ->
+ Fun = fun(Name) -> call(Name, {add_copy, Tab, Node}) end,
+ map_call(Fun, Checkpoints, ok);
+ _ ->
+ ok
+ end.
+
+tm_del_copy(Tab, Node) when Node == node() ->
+ mnesia_subscr:unsubscribe_table(Tab),
+ case val({Tab, commit_work}) of
+ [{checkpoints, Checkpoints} | _ ] ->
+ Fun = fun(Name) -> call(Name, {del_copy, Tab, Node}) end,
+ map_call(Fun, Checkpoints, ok);
+ _ ->
+ ok
+ end.
+
+tm_change_table_copy_type(Tab, From, To) ->
+ case val({Tab, commit_work}) of
+ [{checkpoints, Checkpoints} | _ ] ->
+ Fun = fun(Name) -> call(Name, {change_copy, Tab, From, To}) end,
+ map_call(Fun, Checkpoints, ok);
+ _ ->
+ ok
+ end.
+
+map_call(Fun, [Name | Names], Res) ->
+ case Fun(Name) of
+ ok ->
+ map_call(Fun, Names, Res);
+ {error, {no_exists, Name}} ->
+ map_call(Fun, Names, Res);
+ {error, Reason} ->
+ %% BUGBUG: We may end up with some checkpoint retainers
+ %% too much in the add_copy case. How do we remove them?
+ map_call(Fun, Names, {error, Reason})
+ end;
+map_call(_Fun, [], Res) ->
+ Res.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Public functions
+
+deactivate(Name) ->
+ case call(Name, get_checkpoint) of
+ {error, Reason} ->
+ {error, Reason};
+ Cp ->
+ deactivate(Cp#checkpoint_args.nodes, Name)
+ end.
+
+deactivate(Nodes, Name) ->
+ rpc:multicall(Nodes, ?MODULE, remote_deactivate, [Name]),
+ ok.
+
+remote_deactivate(Name) ->
+ call(Name, deactivate).
+
+checkpoints() -> val(checkpoints).
+
+tables_and_cookie(Name) ->
+ case call(Name, get_checkpoint) of
+ {error, Reason} ->
+ {error, Reason};
+ Cp ->
+ Tabs = Cp#checkpoint_args.min ++ Cp#checkpoint_args.max,
+ Cookie = Cp#checkpoint_args.cookie,
+ {ok, Tabs, Cookie}
+ end.
+
+most_local_node(Name, Tab) ->
+ case ?catch_val({Tab, {retainer, Name}}) of
+ {'EXIT', _} ->
+ {error, {"No retainer attached to table", [Tab, Name]}};
+ R ->
+ Writers = R#retainer.writers,
+ LocalWriter = lists:member(node(), Writers),
+ if
+ LocalWriter == true ->
+ {ok, node()};
+ Writers /= [] ->
+ {ok, hd(Writers)};
+ true ->
+ {error, {"No retainer attached to table", [Tab, Name]}}
+ end
+ end.
+
+really_retain(Name, Tab) ->
+ R = val({Tab, {retainer, Name}}),
+ R#retainer.really_retain.
+
+%% Activate a checkpoint.
+%%
+%% A checkpoint is a transaction consistent state that may be used to
+%% perform a distributed backup or to rollback the involved tables to
+%% their old state. Backups may also be used to restore tables to
+%% their old state. Args is a list of the following tuples:
+%%
+%% {name, Name}
+%% Name of checkpoint. Each checkpoint must have a name which
+%% is unique on the reachable nodes. The name may be reused when
+%% the checkpoint has been deactivated.
+%% By default a probably unique name is generated.
+%% Multiple checkpoints may be set on the same table.
+%%
+%% {allow_remote, Bool}
+%% false means that all retainers must be local. If the
+%% table does not reside locally, the checkpoint fails.
+%% true allows retainers on other nodes.
+%%
+%% {min, MinTabs}
+%% Minimize redundancy and only keep checkpoint info together with
+%% one replica, preferrably at the local node. If any node involved
+%% the checkpoint goes down, the checkpoint is deactivated.
+%%
+%% {max, MaxTabs}
+%% Maximize redundancy and keep checkpoint info together with all
+%% replicas. The checkpoint becomes more fault tolerant if the
+%% tables has several replicas. When new replicas are added, they
+%% will also get a retainer attached to them.
+%%
+%% {ram_overrides_dump, Bool}
+%% {ram_overrides_dump, Tabs}
+%% Only applicable for ram_copies. Bool controls which versions of
+%% the records that should be included in the checkpoint state.
+%% true means that the latest comitted records in ram (i.e. the
+%% records that the application accesses) should be included
+%% in the checkpoint. false means that the records dumped to
+%% dat-files (the records that will be loaded at startup) should
+%% be included in the checkpoint. Tabs is a list of tables.
+%% Default is false.
+%%
+%% {ignore_new, TidList}
+%% Normally we wait for all pending transactions to complete
+%% before we allow iteration over the checkpoint. But in order
+%% to cope with checkpoint activation inside a transaction that
+%% currently prepares commit (mnesia_init:get_net_work_copy) we
+%% need to have the ability to ignore the enclosing transaction.
+%% We do not wait for the transactions in TidList to end. The
+%% transactions in TidList are regarded as newer than the checkpoint.
+
+activate(Args) ->
+ case args2cp(Args) of
+ {ok, Cp} ->
+ do_activate(Cp);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+args2cp(Args) when list(Args)->
+ case catch lists:foldl(fun check_arg/2, #checkpoint_args{}, Args) of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Cp ->
+ case check_tables(Cp) of
+ {error, Reason} ->
+ {error, Reason};
+ {ok, Overriders, AllTabs} ->
+ arrange_retainers(Cp, Overriders, AllTabs)
+ end
+ end;
+args2cp(Args) ->
+ {error, {badarg, Args}}.
+
+check_arg({name, Name}, Cp) ->
+ case lists:member(Name, checkpoints()) of
+ true ->
+ exit({already_exists, Name});
+ false ->
+ case catch tab2retainer({foo, Name}) of
+ List when list(List) ->
+ Cp#checkpoint_args{name = Name};
+ _ ->
+ exit({badarg, Name})
+ end
+ end;
+check_arg({allow_remote, true}, Cp) ->
+ Cp#checkpoint_args{allow_remote = true};
+check_arg({allow_remote, false}, Cp) ->
+ Cp#checkpoint_args{allow_remote = false};
+check_arg({ram_overrides_dump, true}, Cp) ->
+ Cp#checkpoint_args{ram_overrides_dump = true};
+check_arg({ram_overrides_dump, false}, Cp) ->
+ Cp#checkpoint_args{ram_overrides_dump = false};
+check_arg({ram_overrides_dump, Tabs}, Cp) when list(Tabs) ->
+ Cp#checkpoint_args{ram_overrides_dump = Tabs};
+check_arg({min, Tabs}, Cp) when list(Tabs) ->
+ Cp#checkpoint_args{min = Tabs};
+check_arg({max, Tabs}, Cp) when list(Tabs) ->
+ Cp#checkpoint_args{max = Tabs};
+check_arg({ignore_new, Tids}, Cp) when list(Tids) ->
+ Cp#checkpoint_args{ignore_new = Tids};
+check_arg(Arg, _) ->
+ exit({badarg, Arg}).
+
+check_tables(Cp) ->
+ Min = Cp#checkpoint_args.min,
+ Max = Cp#checkpoint_args.max,
+ AllTabs = Min ++ Max,
+ DoubleTabs = [T || T <- Min, lists:member(T, Max)],
+ Overriders = Cp#checkpoint_args.ram_overrides_dump,
+ if
+ DoubleTabs /= [] ->
+ {error, {combine_error, Cp#checkpoint_args.name,
+ [{min, DoubleTabs}, {max, DoubleTabs}]}};
+ Min == [], Max == [] ->
+ {error, {combine_error, Cp#checkpoint_args.name,
+ [{min, Min}, {max, Max}]}};
+ Overriders == false ->
+ {ok, [], AllTabs};
+ Overriders == true ->
+ {ok, AllTabs, AllTabs};
+ list(Overriders) ->
+ case [T || T <- Overriders, not lists:member(T, Min)] of
+ [] ->
+ case [T || T <- Overriders, not lists:member(T, Max)] of
+ [] ->
+ {ok, Overriders, AllTabs};
+ Outsiders ->
+ {error, {combine_error, Cp#checkpoint_args.name,
+ [{ram_overrides_dump, Outsiders},
+ {max, Outsiders}]}}
+ end;
+ Outsiders ->
+ {error, {combine_error, Cp#checkpoint_args.name,
+ [{ram_overrides_dump, Outsiders},
+ {min, Outsiders}]}}
+ end
+ end.
+
+arrange_retainers(Cp, Overriders, AllTabs) ->
+ R = #retainer{cp_name = Cp#checkpoint_args.name},
+ case catch [R#retainer{tab_name = Tab,
+ writers = select_writers(Cp, Tab)}
+ || Tab <- AllTabs] of
+ {'EXIT', Reason} ->
+ {error, Reason};
+ Retainers ->
+ {ok, Cp#checkpoint_args{ram_overrides_dump = Overriders,
+ retainers = Retainers,
+ nodes = writers(Retainers)}}
+ end.
+
+select_writers(Cp, Tab) ->
+ case filter_remote(Cp, val({Tab, active_replicas})) of
+ [] ->
+ exit({"Cannot prepare checkpoint (replica not available)",
+ [Tab, Cp#checkpoint_args.name]});
+ Writers ->
+ This = node(),
+ case {lists:member(Tab, Cp#checkpoint_args.max),
+ lists:member(This, Writers)} of
+ {true, _} -> Writers; % Max
+ {false, true} -> [This];
+ {false, false} -> [hd(Writers)]
+ end
+ end.
+
+filter_remote(Cp, Writers) when Cp#checkpoint_args.allow_remote == true ->
+ Writers;
+filter_remote(_Cp, Writers) ->
+ This = node(),
+ case lists:member(This, Writers) of
+ true -> [This];
+ false -> []
+ end.
+
+writers(Retainers) ->
+ Fun = fun(R, Acc) -> R#retainer.writers ++ Acc end,
+ Writers = lists:foldl(Fun, [], Retainers),
+ mnesia_lib:uniq(Writers).
+
+do_activate(Cp) ->
+ Name = Cp#checkpoint_args.name,
+ Nodes = Cp#checkpoint_args.nodes,
+ case mnesia_tm:prepare_checkpoint(Nodes, Cp) of
+ {Replies, []} ->
+ check_prep(Replies, Name, Nodes, Cp#checkpoint_args.ignore_new);
+ {_, BadNodes} ->
+ {error, {"Cannot prepare checkpoint (bad nodes)",
+ [Name, BadNodes]}}
+ end.
+
+check_prep([{ok, Name, IgnoreNew, _Node} | Replies], Name, Nodes, IgnoreNew) ->
+ check_prep(Replies, Name, Nodes, IgnoreNew);
+check_prep([{error, Reason} | _Replies], Name, _Nodes, _IgnoreNew) ->
+ {error, {"Cannot prepare checkpoint (bad reply)",
+ [Name, Reason]}};
+check_prep([{badrpc, Reason} | _Replies], Name, _Nodes, _IgnoreNew) ->
+ {error, {"Cannot prepare checkpoint (badrpc)",
+ [Name, Reason]}};
+check_prep([], Name, Nodes, IgnoreNew) ->
+ collect_pending(Name, Nodes, IgnoreNew).
+
+collect_pending(Name, Nodes, IgnoreNew) ->
+ case rpc:multicall(Nodes, ?MODULE, call, [Name, collect_pending]) of
+ {Replies, []} ->
+ case catch ?ets_new_table(mnesia_union, [bag]) of
+ {'EXIT', Reason} -> %% system limit
+ Msg = "Cannot create an ets table pending union",
+ {error, {system_limit, Msg, Reason}};
+ UnionTab ->
+ compute_union(Replies, Nodes, Name, UnionTab, IgnoreNew)
+ end;
+ {_, BadNodes} ->
+ deactivate(Nodes, Name),
+ {error, {"Cannot collect from pending checkpoint", Name, BadNodes}}
+ end.
+
+compute_union([{ok, Pending} | Replies], Nodes, Name, UnionTab, IgnoreNew) ->
+ add_pending(Pending, UnionTab),
+ compute_union(Replies, Nodes, Name, UnionTab, IgnoreNew);
+compute_union([{error, Reason} | _Replies], Nodes, Name, UnionTab, _IgnoreNew) ->
+ deactivate(Nodes, Name),
+ ?ets_delete_table(UnionTab),
+ {error, Reason};
+compute_union([{badrpc, Reason} | _Replies], Nodes, Name, UnionTab, _IgnoreNew) ->
+ deactivate(Nodes, Name),
+ ?ets_delete_table(UnionTab),
+ {error, {badrpc, Reason}};
+compute_union([], Nodes, Name, UnionTab, IgnoreNew) ->
+ send_activate(Nodes, Nodes, Name, UnionTab, IgnoreNew).
+
+add_pending([P | Pending], UnionTab) ->
+ add_pending_node(P#pending.disc_nodes, P#pending.tid, UnionTab),
+ add_pending_node(P#pending.ram_nodes, P#pending.tid, UnionTab),
+ add_pending(Pending, UnionTab);
+add_pending([], _UnionTab) ->
+ ok.
+
+add_pending_node([Node | Nodes], Tid, UnionTab) ->
+ ?ets_insert(UnionTab, {Node, Tid}),
+ add_pending_node(Nodes, Tid, UnionTab);
+add_pending_node([], _Tid, _UnionTab) ->
+ ok.
+
+send_activate([Node | Nodes], AllNodes, Name, UnionTab, IgnoreNew) ->
+ Pending = [Tid || {_, Tid} <- ?ets_lookup(UnionTab, Node),
+ not lists:member(Tid, IgnoreNew)],
+ case rpc:call(Node, ?MODULE, call, [Name, {activate, Pending}]) of
+ activated ->
+ send_activate(Nodes, AllNodes, Name, UnionTab, IgnoreNew);
+ {badrpc, Reason} ->
+ deactivate(Nodes, Name),
+ ?ets_delete_table(UnionTab),
+ {error, {"Activation failed (bad node)", Name, Node, Reason}};
+ {error, Reason} ->
+ deactivate(Nodes, Name),
+ ?ets_delete_table(UnionTab),
+ {error, {"Activation failed", Name, Node, Reason}}
+ end;
+send_activate([], AllNodes, Name, UnionTab, _IgnoreNew) ->
+ ?ets_delete_table(UnionTab),
+ {ok, Name, AllNodes}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Checkpoint server
+
+cast(Name, Msg) ->
+ case ?catch_val({checkpoint, Name}) of
+ {'EXIT', _} ->
+ {error, {no_exists, Name}};
+
+ Pid when pid(Pid) ->
+ Pid ! {self(), Msg},
+ {ok, Pid}
+ end.
+
+call(Name, Msg) ->
+ case cast(Name, Msg) of
+ {ok, Pid} ->
+ catch link(Pid), % Always local
+ Self = self(),
+ receive
+ {'EXIT', Pid, Reason} ->
+ {error, {"Got exit", [Name, Reason]}};
+ {Name, Self, Reply} ->
+ unlink(Pid),
+ Reply
+ end;
+ Error ->
+ Error
+ end.
+
+abcast(Nodes, Name, Msg) ->
+ rpc:eval_everywhere(Nodes, ?MODULE, cast, [Name, Msg]).
+
+reply(nopid, _Name, _Reply) ->
+ ignore;
+reply(ReplyTo, Name, Reply) ->
+ ReplyTo ! {Name, ReplyTo, Reply}.
+
+%% Returns {ok, NewCp} or {error, Reason}
+start_retainer(Cp) ->
+ % Will never be restarted
+ Name = Cp#checkpoint_args.name,
+ case supervisor:start_child(mnesia_checkpoint_sup, [Cp]) of
+ {ok, _Pid} ->
+ {ok, Name, Cp#checkpoint_args.ignore_new, node()};
+ {error, Reason} ->
+ {error, {"Cannot create checkpoint retainer",
+ Name, node(), Reason}}
+ end.
+
+start(Cp) ->
+ Name = Cp#checkpoint_args.name,
+ Args = [Cp#checkpoint_args{supervisor = self()}],
+ mnesia_monitor:start_proc({?MODULE, Name}, ?MODULE, init, Args).
+
+init(Cp) ->
+ process_flag(trap_exit, true),
+ Name = Cp#checkpoint_args.name,
+ Props = [set, public, {keypos, 2}],
+ case catch ?ets_new_table(mnesia_pending_checkpoint, Props) of
+ {'EXIT', Reason} -> %% system limit
+ Msg = "Cannot create an ets table for pending transactions",
+ Error = {error, {system_limit, Name, Msg, Reason}},
+ proc_lib:init_ack(Cp#checkpoint_args.supervisor, Error);
+ PendingTab ->
+ Rs = [prepare_tab(Cp, R) || R <- Cp#checkpoint_args.retainers],
+ Cp2 = Cp#checkpoint_args{retainers = Rs,
+ pid = self(),
+ pending_tab = PendingTab},
+ add(pending_checkpoint_pids, self()),
+ add(pending_checkpoints, PendingTab),
+ set({checkpoint, Name}, self()),
+ add(checkpoints, Name),
+ dbg_out("Checkpoint ~p (~p) started~n", [Name, self()]),
+ proc_lib:init_ack(Cp2#checkpoint_args.supervisor, {ok, self()}),
+ retainer_loop(Cp2)
+ end.
+
+prepare_tab(Cp, R) ->
+ Tab = R#retainer.tab_name,
+ prepare_tab(Cp, R, val({Tab, storage_type})).
+
+prepare_tab(Cp, R, Storage) ->
+ Tab = R#retainer.tab_name,
+ Name = R#retainer.cp_name,
+ case lists:member(node(), R#retainer.writers) of
+ true ->
+ R2 = retainer_create(Cp, R, Tab, Name, Storage),
+ set({Tab, {retainer, Name}}, R2),
+ add({Tab, checkpoints}, Name), %% Keep checkpoint info for table_info & mnesia_session
+ add_chkp_info(Tab, Name),
+ R2;
+ false ->
+ set({Tab, {retainer, Name}}, R#retainer{store = undefined}),
+ R
+ end.
+
+add_chkp_info(Tab, Name) ->
+ case val({Tab, commit_work}) of
+ [{checkpoints, OldList} | CommitList] ->
+ case lists:member(Name, OldList) of
+ true ->
+ ok;
+ false ->
+ NewC = [{checkpoints, [Name | OldList]} | CommitList],
+ mnesia_lib:set({Tab, commit_work}, NewC)
+ end;
+ CommitList ->
+ Chkp = {checkpoints, [Name]},
+ %% OBS checkpoints needs to be first in the list!
+ mnesia_lib:set({Tab, commit_work}, [Chkp | CommitList])
+ end.
+
+tab2retainer({Tab, Name}) ->
+ FlatName = lists:flatten(io_lib:write(Name)),
+ mnesia_lib:dir(lists:concat([?MODULE, "_", Tab, "_", FlatName, ".RET"])).
+
+retainer_create(_Cp, R, Tab, Name, disc_only_copies) ->
+ Fname = tab2retainer({Tab, Name}),
+ file:delete(Fname),
+ Args = [{file, Fname}, {type, set}, {keypos, 2}, {repair, false}],
+ {ok, _} = mnesia_lib:dets_sync_open({Tab, Name}, Args),
+ dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
+ R#retainer{store = {dets, {Tab, Name}}, really_retain = true};
+retainer_create(Cp, R, Tab, Name, Storage) ->
+ T = ?ets_new_table(mnesia_retainer, [set, public, {keypos, 2}]),
+ Overriders = Cp#checkpoint_args.ram_overrides_dump,
+ ReallyR = R#retainer.really_retain,
+ ReallyCp = lists:member(Tab, Overriders),
+ ReallyR2 = prepare_ram_tab(Tab, T, Storage, ReallyR, ReallyCp),
+ dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
+ R#retainer{store = {ets, T}, really_retain = ReallyR2}.
+
+%% Copy the dumped table into retainer if needed
+%% If the really_retain flag already has been set to false,
+%% it should remain false even if we change storage type
+%% while the checkpoint is activated.
+prepare_ram_tab(Tab, T, ram_copies, true, false) ->
+ Fname = mnesia_lib:tab2dcd(Tab),
+ case mnesia_lib:exists(Fname) of
+ true ->
+ Log = mnesia_log:open_log(prepare_ram_tab,
+ mnesia_log:dcd_log_header(),
+ Fname, true,
+ mnesia_monitor:get_env(auto_repair),
+ read_only),
+ Add = fun(Rec) ->
+ Key = element(2, Rec),
+ Recs =
+ case ?ets_lookup(T, Key) of
+ [] -> [];
+ [{_, _, Old}] -> Old
+ end,
+ ?ets_insert(T, {Tab, Key, [Rec | Recs]}),
+ continue
+ end,
+ traverse_dcd(mnesia_log:chunk_log(Log, start), Log, Add),
+ mnesia_log:close_log(Log);
+ false ->
+ ok
+ end,
+ false;
+prepare_ram_tab(_, _, _, ReallyRetain, _) ->
+ ReallyRetain.
+
+traverse_dcd({Cont, [LogH | Rest]}, Log, Fun)
+ when record(LogH, log_header),
+ LogH#log_header.log_kind == dcd_log,
+ LogH#log_header.log_version >= "1.0" ->
+ traverse_dcd({Cont, Rest}, Log, Fun); %% BUGBUG Error handling repaired files
+traverse_dcd({Cont, Recs}, Log, Fun) -> %% trashed data??
+ lists:foreach(Fun, Recs),
+ traverse_dcd(mnesia_log:chunk_log(Log, Cont), Log, Fun);
+traverse_dcd(eof, _Log, _Fun) ->
+ ok.
+
+retainer_get({ets, Store}, Key) -> ?ets_lookup(Store, Key);
+retainer_get({dets, Store}, Key) -> dets:lookup(Store, Key).
+
+retainer_put({ets, Store}, Val) -> ?ets_insert(Store, Val);
+retainer_put({dets, Store}, Val) -> dets:insert(Store, Val).
+
+retainer_first({ets, Store}) -> ?ets_first(Store);
+retainer_first({dets, Store}) -> dets:first(Store).
+
+retainer_next({ets, Store}, Key) -> ?ets_next(Store, Key);
+retainer_next({dets, Store}, Key) -> dets:next(Store, Key).
+
+%% retainer_next_slot(Tab, Pos) ->
+%% case retainer_slot(Tab, Pos) of
+%% '$end_of_table' ->
+%% '$end_of_table';
+%% [] ->
+%% retainer_next_slot(Tab, Pos + 1);
+%% Recs when list(Recs) ->
+%% {Pos, Recs}
+%% end.
+%%
+%% retainer_slot({ets, Store}, Pos) -> ?ets_next(Store, Pos);
+%% retainer_slot({dets, Store}, Pos) -> dets:slot(Store, Pos).
+
+retainer_fixtable(Tab, Bool) when atom(Tab) ->
+ mnesia_lib:db_fixtable(val({Tab, storage_type}), Tab, Bool);
+retainer_fixtable({ets, Tab}, Bool) ->
+ mnesia_lib:db_fixtable(ram_copies, Tab, Bool);
+retainer_fixtable({dets, Tab}, Bool) ->
+ mnesia_lib:db_fixtable(disc_only_copies, Tab, Bool).
+
+retainer_delete({ets, Store}) ->
+ ?ets_delete_table(Store);
+retainer_delete({dets, Store}) ->
+ mnesia_lib:dets_sync_close(Store),
+ Fname = tab2retainer(Store),
+ file:delete(Fname).
+
+retainer_loop(Cp) ->
+ Name = Cp#checkpoint_args.name,
+ receive
+ {_From, {retain, Tid, Tab, Key, OldRecs}}
+ when Cp#checkpoint_args.wait_for_old == [] ->
+ R = val({Tab, {retainer, Name}}),
+ case R#retainer.really_retain of
+ true ->
+ PendingTab = Cp#checkpoint_args.pending_tab,
+ case catch ?ets_lookup_element(PendingTab, Tid, 1) of
+ {'EXIT', _} ->
+ Store = R#retainer.store,
+ case retainer_get(Store, Key) of
+ [] ->
+ retainer_put(Store, {Tab, Key, OldRecs});
+ _ ->
+ already_retained
+ end;
+ pending ->
+ ignore
+ end;
+ false ->
+ ignore
+ end,
+ retainer_loop(Cp);
+
+ %% Adm
+ {From, deactivate} ->
+ do_stop(Cp),
+ reply(From, Name, deactivated),
+ unlink(From),
+ exit(shutdown);
+
+ {'EXIT', Parent, _} when Parent == Cp#checkpoint_args.supervisor ->
+ %% do_stop(Cp),
+ %% assume that entire Mnesia is terminating
+ exit(shutdown);
+
+ {_From, {mnesia_down, Node}} ->
+ Cp2 = do_del_retainers(Cp, Node),
+ retainer_loop(Cp2);
+ {From, get_checkpoint} ->
+ reply(From, Name, Cp),
+ retainer_loop(Cp);
+ {From, {add_copy, Tab, Node}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {Res, Cp2} = do_add_copy(Cp, Tab, Node),
+ reply(From, Name, Res),
+ retainer_loop(Cp2);
+ {From, {del_copy, Tab, Node}} when Cp#checkpoint_args.wait_for_old == [] ->
+ Cp2 = do_del_copy(Cp, Tab, Node),
+ reply(From, Name, ok),
+ retainer_loop(Cp2);
+ {From, {change_copy, Tab, From, To}} when Cp#checkpoint_args.wait_for_old == [] ->
+ Cp2 = do_change_copy(Cp, Tab, From, To),
+ reply(From, Name, ok),
+ retainer_loop(Cp2);
+ {_From, {add_retainer, R, Node}} ->
+ Cp2 = do_add_retainer(Cp, R, Node),
+ retainer_loop(Cp2);
+ {_From, {del_retainer, R, Node}} when Cp#checkpoint_args.wait_for_old == [] ->
+ Cp2 = do_del_retainer(Cp, R, Node),
+ retainer_loop(Cp2);
+
+ %% Iteration
+ {From, {iter_begin, Iter}} when Cp#checkpoint_args.wait_for_old == [] ->
+ Cp2 = iter_begin(Cp, From, Iter),
+ retainer_loop(Cp2);
+
+ {From, {iter_end, Iter}} when Cp#checkpoint_args.wait_for_old == [] ->
+ retainer_fixtable(Iter#iter.oid_tab, false),
+ Iters = Cp#checkpoint_args.iterators -- [Iter],
+ reply(From, Name, ok),
+ retainer_loop(Cp#checkpoint_args{iterators = Iters});
+
+ {_From, {exit_pending, Tid}}
+ when list(Cp#checkpoint_args.wait_for_old) ->
+ StillPending = lists:delete(Tid, Cp#checkpoint_args.wait_for_old),
+ Cp2 = Cp#checkpoint_args{wait_for_old = StillPending},
+ Cp3 = maybe_activate(Cp2),
+ retainer_loop(Cp3);
+
+ {From, collect_pending} ->
+ PendingTab = Cp#checkpoint_args.pending_tab,
+ del(pending_checkpoints, PendingTab),
+ Pending = ?ets_match_object(PendingTab, '_'),
+ reply(From, Name, {ok, Pending}),
+ retainer_loop(Cp);
+
+ {From, {activate, Pending}} ->
+ StillPending = mnesia_recover:still_pending(Pending),
+ enter_still_pending(StillPending, Cp#checkpoint_args.pending_tab),
+ Cp2 = maybe_activate(Cp#checkpoint_args{wait_for_old = StillPending}),
+ reply(From, Name, activated),
+ retainer_loop(Cp2);
+
+ {'EXIT', From, _Reason} ->
+ Iters = [Iter || Iter <- Cp#checkpoint_args.iterators,
+ check_iter(From, Iter)],
+ retainer_loop(Cp#checkpoint_args{iterators = Iters});
+
+ {system, From, Msg} ->
+ dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ sys:handle_system_msg(Msg, From, no_parent, ?MODULE, [], Cp)
+ end.
+
+maybe_activate(Cp)
+ when Cp#checkpoint_args.wait_for_old == [],
+ Cp#checkpoint_args.is_activated == false ->
+ Cp#checkpoint_args{pending_tab = undefined, is_activated = true};
+maybe_activate(Cp) ->
+ Cp.
+
+iter_begin(Cp, From, Iter) ->
+ Name = Cp#checkpoint_args.name,
+ R = val({Iter#iter.tab_name, {retainer, Name}}),
+ Iter2 = init_tabs(R, Iter),
+ Iter3 = Iter2#iter{pid = From},
+ retainer_fixtable(Iter3#iter.oid_tab, true),
+ Iters = [Iter3 | Cp#checkpoint_args.iterators],
+ reply(From, Name, {ok, Iter3, self()}),
+ Cp#checkpoint_args{iterators = Iters}.
+
+do_stop(Cp) ->
+ Name = Cp#checkpoint_args.name,
+ del(pending_checkpoints, Cp#checkpoint_args.pending_tab),
+ del(pending_checkpoint_pids, self()),
+ del(checkpoints, Name),
+ unset({checkpoint, Name}),
+ lists:foreach(fun deactivate_tab/1, Cp#checkpoint_args.retainers),
+ Iters = Cp#checkpoint_args.iterators,
+ lists:foreach(fun(I) -> retainer_fixtable(I#iter.oid_tab, false) end, Iters).
+
+deactivate_tab(R) ->
+ Name = R#retainer.cp_name,
+ Tab = R#retainer.tab_name,
+ del({Tab, checkpoints}, Name), %% Keep checkpoint info for table_info & mnesia_session
+ del_chkp_info(Tab, Name),
+ unset({Tab, {retainer, Name}}),
+ Active = lists:member(node(), R#retainer.writers),
+ case R#retainer.store of
+ undefined ->
+ ignore;
+ Store when Active == true ->
+ retainer_delete(Store);
+ _ ->
+ ignore
+ end.
+
+del_chkp_info(Tab, Name) ->
+ case val({Tab, commit_work}) of
+ [{checkpoints, ChkList} | Rest] ->
+ case lists:delete(Name, ChkList) of
+ [] ->
+ %% The only checkpoint was deleted
+ mnesia_lib:set({Tab, commit_work}, Rest);
+ NewList ->
+ mnesia_lib:set({Tab, commit_work},
+ [{checkpoints, NewList} | Rest])
+ end;
+ _ -> ignore
+ end.
+
+do_del_retainers(Cp, Node) ->
+ Rs = [do_del_retainer2(Cp, R, Node) || R <- Cp#checkpoint_args.retainers],
+ Cp#checkpoint_args{retainers = Rs, nodes = writers(Rs)}.
+
+do_del_retainer2(Cp, R, Node) ->
+ Writers = R#retainer.writers -- [Node],
+ R2 = R#retainer{writers = Writers},
+ set({R2#retainer.tab_name, {retainer, R2#retainer.cp_name}}, R2),
+ if
+ Writers == [] ->
+ Event = {mnesia_checkpoint_deactivated, Cp#checkpoint_args.name},
+ mnesia_lib:report_system_event(Event),
+ do_stop(Cp),
+ exit(shutdown);
+ Node == node() ->
+ deactivate_tab(R), % Avoids unnecessary tm_retain accesses
+ set({R2#retainer.tab_name, {retainer, R2#retainer.cp_name}}, R2),
+ R2;
+ true ->
+ R2
+ end.
+
+do_del_retainer(Cp, R0, Node) ->
+ {R, Rest} = find_retainer(R0, Cp#checkpoint_args.retainers, []),
+ R2 = do_del_retainer2(Cp, R, Node),
+ Rs = [R2|Rest],
+ Cp#checkpoint_args{retainers = Rs, nodes = writers(Rs)}.
+
+do_del_copy(Cp, Tab, ThisNode) when ThisNode == node() ->
+ Name = Cp#checkpoint_args.name,
+ Others = Cp#checkpoint_args.nodes -- [ThisNode],
+ R = val({Tab, {retainer, Name}}),
+ abcast(Others, Name, {del_retainer, R, ThisNode}),
+ do_del_retainer(Cp, R, ThisNode).
+
+do_add_copy(Cp, Tab, Node) when Node /= node()->
+ case lists:member(Tab, Cp#checkpoint_args.max) of
+ false ->
+ {ok, Cp};
+ true ->
+ Name = Cp#checkpoint_args.name,
+ R0 = val({Tab, {retainer, Name}}),
+ W = R0#retainer.writers,
+ R = R0#retainer{writers = W ++ [Node]},
+
+ case lists:member(Node, Cp#checkpoint_args.nodes) of
+ true ->
+ send_retainer(Cp, R, Node);
+ false ->
+ case tm_remote_prepare(Node, Cp) of
+ {ok, Name, _IgnoreNew, Node} ->
+ case lists:member(schema, Cp#checkpoint_args.max) of
+ true ->
+ %% We need to send schema retainer somewhere
+ RS0 = val({schema, {retainer, Name}}),
+ W = RS0#retainer.writers,
+ RS1 = RS0#retainer{writers = W ++ [Node]},
+ case send_retainer(Cp, RS1, Node) of
+ {ok, Cp1} ->
+ send_retainer(Cp1, R, Node);
+ Error ->
+ Error
+ end;
+ false ->
+ send_retainer(Cp, R, Node)
+ end;
+ {badrpc, Reason} ->
+ {{error, {badrpc, Reason}}, Cp};
+ {error, Reason} ->
+ {{error, Reason}, Cp}
+ end
+ end
+ end.
+
+tm_remote_prepare(Node, Cp) ->
+ rpc:call(Node, ?MODULE, tm_prepare, [Cp]).
+
+do_add_retainer(Cp, R0, Node) ->
+ Writers = R0#retainer.writers,
+ {R, Rest} = find_retainer(R0, Cp#checkpoint_args.retainers, []),
+ NewRet =
+ if
+ Node == node() ->
+ prepare_tab(Cp, R#retainer{writers = Writers});
+ true ->
+ R#retainer{writers = Writers}
+ end,
+ Rs = [NewRet | Rest],
+ set({NewRet#retainer.tab_name, {retainer, NewRet#retainer.cp_name}}, NewRet),
+ Cp#checkpoint_args{retainers = Rs, nodes = writers(Rs)}.
+
+find_retainer(#retainer{cp_name = CP, tab_name = Tab},
+ [Ret = #retainer{cp_name = CP, tab_name = Tab} | R], Acc) ->
+ {Ret, R ++ Acc};
+find_retainer(Ret, [H|R], Acc) ->
+ find_retainer(Ret, R, [H|Acc]).
+
+send_retainer(Cp, R, Node) ->
+ Name = Cp#checkpoint_args.name,
+ Nodes0 = Cp#checkpoint_args.nodes -- [Node],
+ Nodes1 = Nodes0 ++ [Node],
+ Nodes = Nodes1 -- [node()],
+ abcast(Nodes, Name, {add_retainer, R, Node}),
+ Store = R#retainer.store,
+%% send_retainer2(Node, Name, Store, retainer_next_slot(Store, 0)),
+ send_retainer2(Node, Name, Store, retainer_first(Store)),
+ Cp2 = do_add_retainer(Cp, R, Node),
+ {ok, Cp2}.
+
+send_retainer2(_, _, _, '$end_of_table') ->
+ ok;
+%%send_retainer2(Node, Name, Store, {Slot, Records}) ->
+send_retainer2(Node, Name, Store, Key) ->
+ [{Tab, _, Records}] = retainer_get(Store, Key),
+ abcast([Node], Name, {retain, {dirty, send_retainer}, Tab, Key, Records}),
+ send_retainer2(Node, Name, Store, retainer_next(Store, Key)).
+
+do_change_copy(Cp, Tab, FromType, ToType) ->
+ Name = Cp#checkpoint_args.name,
+ R = val({Tab, {retainer, Name}}),
+ R2 = prepare_tab(Cp, R, ToType),
+ {_, Old} = R#retainer.store,
+ {_, New} = R2#retainer.store,
+
+ Fname = tab2retainer({Tab, Name}),
+ if
+ FromType == disc_only_copies ->
+ mnesia_lib:dets_sync_close(Old),
+ loaded = mnesia_lib:dets_to_ets(Old, New, Fname, set, no, yes),
+ ok = file:delete(Fname);
+ ToType == disc_only_copies ->
+ TabSize = ?ets_info(Old, size),
+ Props = [{file, Fname},
+ {type, set},
+ {keypos, 2},
+%% {ram_file, true},
+ {estimated_no_objects, TabSize + 256},
+ {repair, false}],
+ {ok, _} = mnesia_lib:dets_sync_open(New, Props),
+ ok = mnesia_dumper:raw_dump_table(New, Old),
+ ?ets_delete_table(Old);
+ true ->
+ ignore
+ end,
+ Pos = #retainer.tab_name,
+ Rs = lists:keyreplace(Tab, Pos, Cp#checkpoint_args.retainers, R2),
+ Cp#checkpoint_args{retainers = Rs, nodes = writers(Rs)}.
+
+check_iter(From, Iter) when Iter#iter.pid == From ->
+ retainer_fixtable(Iter#iter.oid_tab, false),
+ false;
+check_iter(_From, _Iter) ->
+ true.
+
+init_tabs(R, Iter) ->
+ {Kind, _} = Store = R#retainer.store,
+ Main = {Kind, Iter#iter.tab_name},
+ Ret = Store,
+ Iter2 = Iter#iter{main_tab = Main, retainer_tab = Ret},
+ case Iter#iter.source of
+ table -> Iter2#iter{oid_tab = Main};
+ retainer -> Iter2#iter{oid_tab = Ret}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Iteration
+%%
+%% Iterates over a table and applies Fun(ListOfRecords)
+%% with a suitable amount of records, e.g. 1000 or so.
+%% ListOfRecords is [] when the iteration is over.
+%%
+%% OidKind affects which internal table to be iterated over and
+%% ValKind affects which table to pick the actual records from. Legal
+%% values for OidKind and ValKind is the atom table or the atom
+%% retainer.
+%%
+%% The iteration may either be performed over the main table (which
+%% contains the latest values of the records, i.e. the values that
+%% are visible to the applications) or over the checkpoint retainer
+%% (which contains the values as the looked like the timepoint when
+%% the checkpoint was activated).
+%%
+%% It is possible to iterate over the main table and pick values
+%% from the retainer and vice versa.
+
+iterate(Name, Tab, Fun, Acc, Source, Val) ->
+ Iter0 = #iter{tab_name = Tab, source = Source, val = Val},
+ case call(Name, {iter_begin, Iter0}) of
+ {error, Reason} ->
+ {error, Reason};
+ {ok, Iter, Pid} ->
+ link(Pid), % We don't want any pending fixtable's
+ Res = (catch iter(Fun, Acc, Iter)),
+ unlink(Pid),
+ call(Name, {iter_end, Iter}),
+ case Res of
+ {'EXIT', Reason} -> {error, Reason};
+ {error, Reason} -> {error, Reason};
+ Acc2 -> {ok, Acc2}
+ end
+ end.
+
+iter(Fun, Acc, Iter)->
+ iter(Fun, Acc, Iter, retainer_first(Iter#iter.oid_tab)).
+
+iter(Fun, Acc, Iter, Key) ->
+ case get_records(Iter, Key) of
+ {'$end_of_table', []} ->
+ Fun([], Acc);
+ {'$end_of_table', Records} ->
+ Acc2 = Fun(Records, Acc),
+ Fun([], Acc2);
+ {Next, Records} ->
+ Acc2 = Fun(Records, Acc),
+ iter(Fun, Acc2, Iter, Next)
+ end.
+
+stop_iteration(Reason) ->
+ throw({error, {stopped, Reason}}).
+
+get_records(Iter, Key) ->
+ get_records(Iter, Key, 500, []). % 500 keys
+
+get_records(_Iter, Key, 0, Acc) ->
+ {Key, lists:append(lists:reverse(Acc))};
+get_records(_Iter, '$end_of_table', _I, Acc) ->
+ {'$end_of_table', lists:append(lists:reverse(Acc))};
+get_records(Iter, Key, I, Acc) ->
+ Recs = get_val(Iter, Key),
+ Next = retainer_next(Iter#iter.oid_tab, Key),
+ get_records(Iter, Next, I-1, [Recs | Acc]).
+
+get_val(Iter, Key) when Iter#iter.val == latest ->
+ get_latest_val(Iter, Key);
+get_val(Iter, Key) when Iter#iter.val == checkpoint ->
+ get_checkpoint_val(Iter, Key).
+
+get_latest_val(Iter, Key) when Iter#iter.source == table ->
+ retainer_get(Iter#iter.main_tab, Key);
+get_latest_val(Iter, Key) when Iter#iter.source == retainer ->
+ DeleteOid = {Iter#iter.tab_name, Key},
+ [DeleteOid | retainer_get(Iter#iter.main_tab, Key)].
+
+get_checkpoint_val(Iter, Key) when Iter#iter.source == table ->
+ retainer_get(Iter#iter.main_tab, Key);
+get_checkpoint_val(Iter, Key) when Iter#iter.source == retainer ->
+ DeleteOid = {Iter#iter.tab_name, Key},
+ case retainer_get(Iter#iter.retainer_tab, Key) of
+ [{_, _, []}] -> [DeleteOid];
+ [{_, _, Records}] -> [DeleteOid | Records]
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% System upgrade
+
+system_continue(_Parent, _Debug, Cp) ->
+ retainer_loop(Cp).
+
+system_terminate(_Reason, _Parent,_Debug, Cp) ->
+ do_stop(Cp).
+
+system_code_change(Cp, _Module, _OldVsn, _Extra) ->
+ {ok, Cp}.
+
+convert_cp_record(Cp) when record(Cp, checkpoint) ->
+ ROD =
+ case Cp#checkpoint.ram_overrides_dump of
+ true -> Cp#checkpoint.min ++ Cp#checkpoint.max;
+ false -> []
+ end,
+
+ {ok, #checkpoint_args{name = Cp#checkpoint.name,
+ allow_remote = Cp#checkpoint.name,
+ ram_overrides_dump = ROD,
+ nodes = Cp#checkpoint.nodes,
+ node = Cp#checkpoint.node,
+ now = Cp#checkpoint.now,
+ cookie = ?unique_cookie,
+ min = Cp#checkpoint.min,
+ max = Cp#checkpoint.max,
+ pending_tab = Cp#checkpoint.pending_tab,
+ wait_for_old = Cp#checkpoint.wait_for_old,
+ is_activated = Cp#checkpoint.is_activated,
+ ignore_new = Cp#checkpoint.ignore_new,
+ retainers = Cp#checkpoint.retainers,
+ iterators = Cp#checkpoint.iterators,
+ supervisor = Cp#checkpoint.supervisor,
+ pid = Cp#checkpoint.pid
+ }};
+convert_cp_record(Cp) when record(Cp, checkpoint_args) ->
+ AllTabs = Cp#checkpoint_args.min ++ Cp#checkpoint_args.max,
+ ROD = case Cp#checkpoint_args.ram_overrides_dump of
+ [] ->
+ false;
+ AllTabs ->
+ true;
+ _ ->
+ error
+ end,
+ if
+ ROD == error ->
+ {error, {"Old node cannot handle new checkpoint protocol",
+ ram_overrides_dump}};
+ true ->
+ {ok, #checkpoint{name = Cp#checkpoint_args.name,
+ allow_remote = Cp#checkpoint_args.name,
+ ram_overrides_dump = ROD,
+ nodes = Cp#checkpoint_args.nodes,
+ node = Cp#checkpoint_args.node,
+ now = Cp#checkpoint_args.now,
+ min = Cp#checkpoint_args.min,
+ max = Cp#checkpoint_args.max,
+ pending_tab = Cp#checkpoint_args.pending_tab,
+ wait_for_old = Cp#checkpoint_args.wait_for_old,
+ is_activated = Cp#checkpoint_args.is_activated,
+ ignore_new = Cp#checkpoint_args.ignore_new,
+ retainers = Cp#checkpoint_args.retainers,
+ iterators = Cp#checkpoint_args.iterators,
+ supervisor = Cp#checkpoint_args.supervisor,
+ pid = Cp#checkpoint_args.pid
+ }}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ _VaLuE_ -> _VaLuE_
+ end.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint_sup.erl
new file mode 100644
index 0000000000..29e31f15a6
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_checkpoint_sup.erl
@@ -0,0 +1,39 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_checkpoint_sup.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_checkpoint_sup).
+
+-behaviour(supervisor).
+
+-export([start/0, init/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% top supervisor callback functions
+
+start() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% sub supervisor callback functions
+
+init([]) ->
+ Flags = {simple_one_for_one, 0, timer:hours(24)}, % Trust the top supervisor
+ MFA = {mnesia_checkpoint, start, []},
+ Modules = [?MODULE, mnesia_checkpoint, supervisor],
+ KillAfter = mnesia_kernel_sup:supervisor_timeout(timer:seconds(3)),
+ Workers = [{?MODULE, MFA, transient, KillAfter, worker, Modules}],
+ {ok, {Flags, Workers}}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_controller.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_controller.erl
new file mode 100644
index 0000000000..b6f865f0d4
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_controller.erl
@@ -0,0 +1,2012 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_controller.erl,v 1.3 2010/03/04 13:54:19 maria Exp $
+%%
+%% The mnesia_init process loads tables from local disc or from
+%% another nodes. It also coordinates updates of the info about
+%% where we can read and write tables.
+%%
+%% Tables may need to be loaded initially at startup of the local
+%% node or when other nodes announces that they already have loaded
+%% tables that we also want.
+%%
+%% Initially we set the load request queue to those tables that we
+%% safely can load locally, i.e. tables where we have the last
+%% consistent replica and we have received mnesia_down from all
+%% other nodes holding the table. Then we let the mnesia_init
+%% process enter its normal working state.
+%%
+%% When we need to load a table we append a request to the load
+%% request queue. All other requests are regarded as high priority
+%% and are processed immediately (e.g. update table whereabouts).
+%% We processes the load request queue as a "background" job..
+
+-module(mnesia_controller).
+
+-behaviour(gen_server).
+
+%% Mnesia internal stuff
+-export([
+ start/0,
+ i_have_tab/1,
+ info/0,
+ get_info/1,
+ get_workers/1,
+ force_load_table/1,
+ async_dump_log/1,
+ sync_dump_log/1,
+ connect_nodes/1,
+ wait_for_schema_commit_lock/0,
+ release_schema_commit_lock/0,
+ create_table/1,
+ get_disc_copy/1,
+ get_cstructs/0,
+ sync_and_block_table_whereabouts/4,
+ sync_del_table_copy_whereabouts/2,
+ block_table/1,
+ unblock_table/1,
+ block_controller/0,
+ unblock_controller/0,
+ unannounce_add_table_copy/2,
+ master_nodes_updated/2,
+ mnesia_down/1,
+ add_active_replica/2,
+ add_active_replica/3,
+ add_active_replica/4,
+ change_table_access_mode/1,
+ del_active_replica/2,
+ wait_for_tables/2,
+ get_network_copy/2,
+ merge_schema/0,
+ start_remote_sender/4,
+ schedule_late_disc_load/2
+ ]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+%% Module internal stuff
+-export([call/1,
+ cast/1,
+ dump_and_reply/2,
+ load_and_reply/2,
+ send_and_reply/2,
+ wait_for_tables_init/2
+ ]).
+
+-import(mnesia_lib, [set/2, add/2]).
+-import(mnesia_lib, [fatal/2, error/2, verbose/2, dbg_out/2]).
+
+-include("mnesia.hrl").
+
+-define(SERVER_NAME, ?MODULE).
+
+-record(state, {supervisor,
+ schema_is_merged = false,
+ early_msgs = [],
+ loader_pid,
+ loader_queue = [],
+ sender_pid,
+ sender_queue = [],
+ late_loader_queue = [],
+ dumper_pid, % Dumper or schema commit pid
+ dumper_queue = [], % Dumper or schema commit queue
+ dump_log_timer_ref,
+ is_stopping = false
+ }).
+
+-record(worker_reply, {what,
+ pid,
+ result
+ }).
+
+-record(schema_commit_lock, {owner}).
+-record(block_controller, {owner}).
+
+-record(dump_log, {initiated_by,
+ opt_reply_to
+ }).
+
+-record(net_load, {table,
+ reason,
+ opt_reply_to,
+ cstruct = unknown
+ }).
+
+-record(send_table, {table,
+ receiver_pid,
+ remote_storage
+ }).
+
+-record(disc_load, {table,
+ reason,
+ opt_reply_to
+ }).
+
+-record(late_load, {table,
+ reason,
+ opt_reply_to,
+ loaders
+ }).
+
+-record(loader_done, {worker_pid,
+ is_loaded,
+ table_name,
+ needs_announce,
+ needs_sync,
+ needs_reply,
+ reply_to,
+ reply}).
+
+-record(sender_done, {worker_pid,
+ worker_res,
+ table_name
+ }).
+
+-record(dumper_done, {worker_pid,
+ worker_res
+ }).
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
+
+start() ->
+ gen_server:start_link({local, ?SERVER_NAME}, ?MODULE, [self()],
+ [{timeout, infinity}
+ %% ,{debug, [trace]}
+ ]).
+
+sync_dump_log(InitBy) ->
+ call({sync_dump_log, InitBy}).
+
+async_dump_log(InitBy) ->
+ ?SERVER_NAME ! {async_dump_log, InitBy}.
+
+%% Wait for tables to be active
+%% If needed, we will wait for Mnesia to start
+%% If Mnesia stops, we will wait for Mnesia to restart
+%% We will wait even if the list of tables is empty
+%%
+wait_for_tables(Tabs, Timeout) when list(Tabs), Timeout == infinity ->
+ do_wait_for_tables(Tabs, Timeout);
+wait_for_tables(Tabs, Timeout) when list(Tabs),
+ integer(Timeout), Timeout >= 0 ->
+ do_wait_for_tables(Tabs, Timeout);
+wait_for_tables(Tabs, Timeout) ->
+ {error, {badarg, Tabs, Timeout}}.
+
+do_wait_for_tables(Tabs, 0) ->
+ reply_wait(Tabs);
+do_wait_for_tables(Tabs, Timeout) ->
+ Pid = spawn_link(?MODULE, wait_for_tables_init, [self(), Tabs]),
+ receive
+ {?SERVER_NAME, Pid, Res} ->
+ Res;
+
+ {'EXIT', Pid, _} ->
+ reply_wait(Tabs)
+
+ after Timeout ->
+ unlink(Pid),
+ exit(Pid, timeout),
+ reply_wait(Tabs)
+ end.
+
+reply_wait(Tabs) ->
+ case catch mnesia_lib:active_tables() of
+ {'EXIT', _} ->
+ {error, {node_not_running, node()}};
+ Active when list(Active) ->
+ case Tabs -- Active of
+ [] ->
+ ok;
+ BadTabs ->
+ {timeout, BadTabs}
+ end
+ end.
+
+wait_for_tables_init(From, Tabs) ->
+ process_flag(trap_exit, true),
+ Res = wait_for_init(From, Tabs, whereis(?SERVER_NAME)),
+ From ! {?SERVER_NAME, self(), Res},
+ unlink(From),
+ exit(normal).
+
+wait_for_init(From, Tabs, Init) ->
+ case catch link(Init) of
+ {'EXIT', _} ->
+ %% Mnesia is not started
+ {error, {node_not_running, node()}};
+ true when pid(Init) ->
+ cast({sync_tabs, Tabs, self()}),
+ rec_tabs(Tabs, Tabs, From, Init)
+ end.
+
+sync_reply(Waiter, Tab) ->
+ Waiter ! {?SERVER_NAME, {tab_synced, Tab}}.
+
+rec_tabs([Tab | Tabs], AllTabs, From, Init) ->
+ receive
+ {?SERVER_NAME, {tab_synced, Tab}} ->
+ rec_tabs(Tabs, AllTabs, From, Init);
+
+ {'EXIT', From, _} ->
+ %% This will trigger an exit signal
+ %% to mnesia_init
+ exit(wait_for_tables_timeout);
+
+ {'EXIT', Init, _} ->
+ %% Oops, mnesia_init stopped,
+ exit(mnesia_stopped)
+ end;
+rec_tabs([], _, _, Init) ->
+ unlink(Init),
+ ok.
+
+get_cstructs() ->
+ call(get_cstructs).
+
+mnesia_down(Node) ->
+ case cast({mnesia_down, Node}) of
+ {error, _} -> mnesia_monitor:mnesia_down(?SERVER_NAME, Node);
+ _Pid -> ok
+ end.
+wait_for_schema_commit_lock() ->
+ link(whereis(?SERVER_NAME)),
+ unsafe_call(wait_for_schema_commit_lock).
+
+block_controller() ->
+ call(block_controller).
+
+unblock_controller() ->
+ cast(unblock_controller).
+
+release_schema_commit_lock() ->
+ cast({release_schema_commit_lock, self()}),
+ unlink(whereis(?SERVER_NAME)).
+
+%% Special for preparation of add table copy
+get_network_copy(Tab, Cs) ->
+ Work = #net_load{table = Tab,
+ reason = {dumper, add_table_copy},
+ cstruct = Cs
+ },
+ Res = (catch load_table(Work)),
+ if Res#loader_done.is_loaded == true ->
+ Tab = Res#loader_done.table_name,
+ case Res#loader_done.needs_announce of
+ true ->
+ i_have_tab(Tab);
+ false ->
+ ignore
+ end;
+ true -> ignore
+ end,
+
+ receive %% Flush copier done message
+ {copier_done, _Node} ->
+ ok
+ after 500 -> %% avoid hanging if something is wrong and we shall fail.
+ ignore
+ end,
+ Res#loader_done.reply.
+
+%% This functions is invoked from the dumper
+%%
+%% There are two cases here:
+%% startup ->
+%% no need for sync, since mnesia_controller not started yet
+%% schema_trans ->
+%% already synced with mnesia_controller since the dumper
+%% is syncronously started from mnesia_controller
+
+create_table(Tab) ->
+ {loaded, ok} = mnesia_loader:disc_load_table(Tab, {dumper,create_table}).
+
+get_disc_copy(Tab) ->
+ disc_load_table(Tab, {dumper,change_table_copy_type}, undefined).
+
+%% Returns ok instead of yes
+force_load_table(Tab) when atom(Tab), Tab /= schema ->
+ case ?catch_val({Tab, storage_type}) of
+ ram_copies ->
+ do_force_load_table(Tab);
+ disc_copies ->
+ do_force_load_table(Tab);
+ disc_only_copies ->
+ do_force_load_table(Tab);
+ unknown ->
+ set({Tab, load_by_force}, true),
+ cast({force_load_updated, Tab}),
+ wait_for_tables([Tab], infinity);
+ {'EXIT', _} ->
+ {error, {no_exists, Tab}}
+ end;
+force_load_table(Tab) ->
+ {error, {bad_type, Tab}}.
+
+do_force_load_table(Tab) ->
+ Loaded = ?catch_val({Tab, load_reason}),
+ case Loaded of
+ unknown ->
+ set({Tab, load_by_force}, true),
+ mnesia_late_loader:async_late_disc_load(node(), [Tab], forced_by_user),
+ wait_for_tables([Tab], infinity);
+ {'EXIT', _} ->
+ set({Tab, load_by_force}, true),
+ mnesia_late_loader:async_late_disc_load(node(), [Tab], forced_by_user),
+ wait_for_tables([Tab], infinity);
+ _ ->
+ ok
+ end.
+master_nodes_updated(schema, _Masters) ->
+ ignore;
+master_nodes_updated(Tab, Masters) ->
+ cast({master_nodes_updated, Tab, Masters}).
+
+schedule_late_disc_load(Tabs, Reason) ->
+ MsgTag = late_disc_load,
+ try_schedule_late_disc_load(Tabs, Reason, MsgTag).
+
+try_schedule_late_disc_load(Tabs, _Reason, MsgTag)
+ when Tabs == [], MsgTag /= schema_is_merged ->
+ ignore;
+try_schedule_late_disc_load(Tabs, Reason, MsgTag) ->
+ GetIntents =
+ fun() ->
+ Item = mnesia_late_disc_load,
+ Nodes = val({current, db_nodes}),
+ mnesia:lock({global, Item, Nodes}, write),
+ case multicall(Nodes -- [node()], disc_load_intents) of
+ {Replies, []} ->
+ call({MsgTag, Tabs, Reason, Replies}),
+ done;
+ {_, BadNodes} ->
+ %% Some nodes did not respond, lets try again
+ {retry, BadNodes}
+ end
+ end,
+ case mnesia:transaction(GetIntents) of
+ {'atomic', done} ->
+ done;
+ {'atomic', {retry, BadNodes}} ->
+ verbose("Retry late_load_tables because bad nodes: ~p~n",
+ [BadNodes]),
+ try_schedule_late_disc_load(Tabs, Reason, MsgTag);
+ {aborted, AbortReason} ->
+ fatal("Cannot late_load_tables~p: ~p~n",
+ [[Tabs, Reason, MsgTag], AbortReason])
+ end.
+
+connect_nodes(Ns) ->
+ case mnesia:system_info(is_running) of
+ no ->
+ {error, {node_not_running, node()}};
+ yes ->
+ {NewC, OldC} = mnesia_recover:connect_nodes(Ns),
+ Connected = NewC ++OldC,
+ New1 = mnesia_lib:intersect(Ns, Connected),
+ New = New1 -- val({current, db_nodes}),
+
+ case try_merge_schema(New) of
+ ok ->
+ mnesia_lib:add_list(extra_db_nodes, New),
+ {ok, New};
+ {aborted, {throw, Str}} when list(Str) ->
+ %%mnesia_recover:disconnect_nodes(New),
+ {error, {merge_schema_failed, lists:flatten(Str)}};
+ Else ->
+ %% Unconnect nodes where merge failed!!
+ %% mnesia_recover:disconnect_nodes(New),
+ {error, Else}
+ end
+ end.
+
+%% Merge the local schema with the schema on other nodes.
+%% But first we must let all processes that want to force
+%% load tables wait until the schema merge is done.
+
+merge_schema() ->
+ AllNodes = mnesia_lib:all_nodes(),
+ case try_merge_schema(AllNodes) of
+ ok ->
+ schema_is_merged();
+ {aborted, {throw, Str}} when list(Str) ->
+ fatal("Failed to merge schema: ~s~n", [Str]);
+ Else ->
+ fatal("Failed to merge schema: ~p~n", [Else])
+ end.
+
+try_merge_schema(Nodes) ->
+ case mnesia_schema:merge_schema() of
+ {'atomic', not_merged} ->
+ %% No more nodes that we need to merge the schema with
+ ok;
+ {'atomic', {merged, OldFriends, NewFriends}} ->
+ %% Check if new nodes has been added to the schema
+ Diff = mnesia_lib:all_nodes() -- [node() | Nodes],
+ mnesia_recover:connect_nodes(Diff),
+
+ %% Tell everybody to adopt orphan tables
+ im_running(OldFriends, NewFriends),
+ im_running(NewFriends, OldFriends),
+
+ try_merge_schema(Nodes);
+ {'atomic', {"Cannot get cstructs", Node, Reason}} ->
+ dbg_out("Cannot get cstructs, Node ~p ~p~n", [Node, Reason]),
+ timer:sleep(1000), % Avoid a endless loop look alike
+ try_merge_schema(Nodes);
+ Other ->
+ Other
+ end.
+
+im_running(OldFriends, NewFriends) ->
+ abcast(OldFriends, {im_running, node(), NewFriends}).
+
+schema_is_merged() ->
+ MsgTag = schema_is_merged,
+ SafeLoads = initial_safe_loads(),
+
+ %% At this point we do not know anything about
+ %% which tables that the other nodes already
+ %% has loaded and therefore we let the normal
+ %% processing of the loader_queue take care
+ %% of it, since we at that time point will
+ %% know the whereabouts. We rely on the fact
+ %% that all nodes tells each other directly
+ %% when they have loaded a table and are
+ %% willing to share it.
+
+ try_schedule_late_disc_load(SafeLoads, initial, MsgTag).
+
+
+cast(Msg) ->
+ case whereis(?SERVER_NAME) of
+ undefined ->{error, {node_not_running, node()}};
+ Pid -> gen_server:cast(Pid, Msg)
+ end.
+
+abcast(Nodes, Msg) ->
+ gen_server:abcast(Nodes, ?SERVER_NAME, Msg).
+
+unsafe_call(Msg) ->
+ case whereis(?SERVER_NAME) of
+ undefined -> {error, {node_not_running, node()}};
+ Pid -> gen_server:call(Pid, Msg, infinity)
+ end.
+
+call(Msg) ->
+ case whereis(?SERVER_NAME) of
+ undefined ->
+ {error, {node_not_running, node()}};
+ Pid ->
+ link(Pid),
+ Res = gen_server:call(Pid, Msg, infinity),
+ unlink(Pid),
+
+ %% We get an exit signal if server dies
+ receive
+ {'EXIT', Pid, _Reason} ->
+ {error, {node_not_running, node()}}
+ after 0 ->
+ ignore
+ end,
+ Res
+ end.
+
+remote_call(Node, Func, Args) ->
+ case catch gen_server:call({?MODULE, Node}, {Func, Args, self()}, infinity) of
+ {'EXIT', Error} ->
+ {error, Error};
+ Else ->
+ Else
+ end.
+
+multicall(Nodes, Msg) ->
+ {Good, Bad} = gen_server:multi_call(Nodes, ?MODULE, Msg, infinity),
+ PatchedGood = [Reply || {_Node, Reply} <- Good],
+ {PatchedGood, Bad}. %% Make the replies look like rpc:multicalls..
+%% rpc:multicall(Nodes, ?MODULE, call, [Msg]).
+
+%%%----------------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%----------------------------------------------------------------------
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+init([Parent]) ->
+ process_flag(trap_exit, true),
+ mnesia_lib:verbose("~p starting: ~p~n", [?SERVER_NAME, self()]),
+
+ %% Handshake and initialize transaction recovery
+ %% for new nodes detected in the schema
+ All = mnesia_lib:all_nodes(),
+ Diff = All -- [node() | val(original_nodes)],
+ mnesia_lib:unset(original_nodes),
+ mnesia_recover:connect_nodes(Diff),
+
+ Interval = mnesia_monitor:get_env(dump_log_time_threshold),
+ Msg = {async_dump_log, time_threshold},
+ {ok, Ref} = timer:send_interval(Interval, Msg),
+ mnesia_dumper:start_regulator(),
+
+ {ok, #state{supervisor = Parent, dump_log_timer_ref = Ref}}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%% {stop, Reason, Reply, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_call({sync_dump_log, InitBy}, From, State) ->
+ Worker = #dump_log{initiated_by = InitBy,
+ opt_reply_to = From
+ },
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+handle_call(wait_for_schema_commit_lock, From, State) ->
+ Worker = #schema_commit_lock{owner = From},
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+handle_call(block_controller, From, State) ->
+ Worker = #block_controller{owner = From},
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+
+handle_call(get_cstructs, From, State) ->
+ Tabs = val({schema, tables}),
+ Cstructs = [val({T, cstruct}) || T <- Tabs],
+ Running = val({current, db_nodes}),
+ reply(From, {cstructs, Cstructs, Running}),
+ noreply(State);
+
+handle_call({schema_is_merged, TabsR, Reason, RemoteLoaders}, From, State) ->
+ State2 = late_disc_load(TabsR, Reason, RemoteLoaders, From, State),
+
+ %% Handle early messages
+ Msgs = State2#state.early_msgs,
+ State3 = State2#state{early_msgs = [], schema_is_merged = true},
+ Ns = val({current, db_nodes}),
+ dbg_out("Schema is merged ~w, State ~w~n", [Ns, State3]),
+%% dbg_out("handle_early_msgs ~p ~n", [Msgs]), % qqqq
+ handle_early_msgs(lists:reverse(Msgs), State3);
+
+handle_call(disc_load_intents, From, State) ->
+ Tabs = disc_load_intents(State#state.loader_queue) ++
+ disc_load_intents(State#state.late_loader_queue),
+ ActiveTabs = mnesia_lib:local_active_tables(),
+ reply(From, {ok, node(), mnesia_lib:union(Tabs, ActiveTabs)}),
+ noreply(State);
+
+handle_call({update_where_to_write, [add, Tab, AddNode], _From}, _Dummy, State) ->
+%%% dbg_out("update_w2w ~p", [[add, Tab, AddNode]]), %%% qqqq
+ Current = val({current, db_nodes}),
+ Res =
+ case lists:member(AddNode, Current) and
+ State#state.schema_is_merged == true of
+ true ->
+ mnesia_lib:add({Tab, where_to_write}, AddNode);
+ false ->
+ ignore
+ end,
+ {reply, Res, State};
+
+handle_call({add_active_replica, [Tab, ToNode, RemoteS, AccessMode], From},
+ ReplyTo, State) ->
+ KnownNode = lists:member(ToNode, val({current, db_nodes})),
+ Merged = State#state.schema_is_merged,
+ if
+ KnownNode == false ->
+ reply(ReplyTo, ignore),
+ noreply(State);
+ Merged == true ->
+ Res = add_active_replica(Tab, ToNode, RemoteS, AccessMode),
+ reply(ReplyTo, Res),
+ noreply(State);
+ true -> %% Schema is not merged
+ Msg = {add_active_replica, [Tab, ToNode, RemoteS, AccessMode], From},
+ Msgs = State#state.early_msgs,
+ reply(ReplyTo, ignore), %% Reply ignore and add data after schema merge
+ noreply(State#state{early_msgs = [{call, Msg, undefined} | Msgs]})
+ end;
+
+handle_call({unannounce_add_table_copy, [Tab, Node], From}, ReplyTo, State) ->
+ KnownNode = lists:member(node(From), val({current, db_nodes})),
+ Merged = State#state.schema_is_merged,
+ if
+ KnownNode == false ->
+ reply(ReplyTo, ignore),
+ noreply(State);
+ Merged == true ->
+ Res = unannounce_add_table_copy(Tab, Node),
+ reply(ReplyTo, Res),
+ noreply(State);
+ true -> %% Schema is not merged
+ Msg = {unannounce_add_table_copy, [Tab, Node], From},
+ Msgs = State#state.early_msgs,
+ reply(ReplyTo, ignore), %% Reply ignore and add data after schema merge
+ %% Set ReplyTO to undefined so we don't reply twice
+ noreply(State#state{early_msgs = [{call, Msg, undefined} | Msgs]})
+ end;
+
+handle_call(Msg, From, State) when State#state.schema_is_merged == false ->
+ %% Buffer early messages
+%% dbg_out("Buffered early msg ~p ~n", [Msg]), %% qqqq
+ Msgs = State#state.early_msgs,
+ noreply(State#state{early_msgs = [{call, Msg, From} | Msgs]});
+
+handle_call({net_load, Tab, Cs}, From, State) ->
+ Worker = #net_load{table = Tab,
+ opt_reply_to = From,
+ reason = add_table_copy,
+ cstruct = Cs
+ },
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+handle_call({late_disc_load, Tabs, Reason, RemoteLoaders}, From, State) ->
+ State2 = late_disc_load(Tabs, Reason, RemoteLoaders, From, State),
+ noreply(State2);
+
+handle_call({block_table, [Tab], From}, _Dummy, State) ->
+ case lists:member(node(From), val({current, db_nodes})) of
+ true ->
+ block_table(Tab);
+ false ->
+ ignore
+ end,
+ {reply, ok, State};
+
+handle_call({check_w2r, _Node, Tab}, _From, State) ->
+ {reply, val({Tab, where_to_read}), State};
+
+handle_call(Msg, _From, State) ->
+ error("~p got unexpected call: ~p~n", [?SERVER_NAME, Msg]),
+ noreply(State).
+
+disc_load_intents([H | T]) when record(H, disc_load) ->
+ [H#disc_load.table | disc_load_intents(T)];
+disc_load_intents([H | T]) when record(H, late_load) ->
+ [H#late_load.table | disc_load_intents(T)];
+disc_load_intents( [H | T]) when record(H, net_load) ->
+ disc_load_intents(T);
+disc_load_intents([]) ->
+ [].
+
+late_disc_load(TabsR, Reason, RemoteLoaders, From, State) ->
+ verbose("Intend to load tables: ~p~n", [TabsR]),
+ ?eval_debug_fun({?MODULE, late_disc_load},
+ [{tabs, TabsR},
+ {reason, Reason},
+ {loaders, RemoteLoaders}]),
+
+ reply(From, queued),
+ %% RemoteLoaders is a list of {ok, Node, Tabs} tuples
+
+ %% Remove deleted tabs
+ LocalTabs = mnesia_lib:val({schema, local_tables}),
+ Filter = fun({Tab, Reas}, Acc) ->
+ case lists:member(Tab, LocalTabs) of
+ true -> [{Tab, Reas} | Acc];
+ false -> Acc
+ end;
+ (Tab, Acc) ->
+ case lists:member(Tab, LocalTabs) of
+ true -> [Tab | Acc];
+ false -> Acc
+ end
+ end,
+
+ Tabs = lists:foldl(Filter, [], TabsR),
+
+ Nodes = val({current, db_nodes}),
+ LateLoaders = late_loaders(Tabs, Reason, RemoteLoaders, Nodes),
+ LateQueue = State#state.late_loader_queue ++ LateLoaders,
+ State#state{late_loader_queue = LateQueue}.
+
+late_loaders([{Tab, Reason} | Tabs], DefaultReason, RemoteLoaders, Nodes) ->
+ LoadNodes = late_load_filter(RemoteLoaders, Tab, Nodes, []),
+ case LoadNodes of
+ [] ->
+ cast({disc_load, Tab, Reason}); % Ugly cast
+ _ ->
+ ignore
+ end,
+ LateLoad = #late_load{table = Tab, loaders = LoadNodes, reason = Reason},
+ [LateLoad | late_loaders(Tabs, DefaultReason, RemoteLoaders, Nodes)];
+
+late_loaders([Tab | Tabs], Reason, RemoteLoaders, Nodes) ->
+ Loaders = late_load_filter(RemoteLoaders, Tab, Nodes, []),
+ case Loaders of
+ [] ->
+ cast({disc_load, Tab, Reason}); % Ugly cast
+ _ ->
+ ignore
+ end,
+ LateLoad = #late_load{table = Tab, loaders = Loaders, reason = Reason},
+ [LateLoad | late_loaders(Tabs, Reason, RemoteLoaders, Nodes)];
+late_loaders([], _Reason, _RemoteLoaders, _Nodes) ->
+ [].
+
+late_load_filter([{error, _} | RemoteLoaders], Tab, Nodes, Acc) ->
+ late_load_filter(RemoteLoaders, Tab, Nodes, Acc);
+late_load_filter([{badrpc, _} | RemoteLoaders], Tab, Nodes, Acc) ->
+ late_load_filter(RemoteLoaders, Tab, Nodes, Acc);
+late_load_filter([RL | RemoteLoaders], Tab, Nodes, Acc) ->
+ {ok, Node, Intents} = RL,
+ Access = val({Tab, access_mode}),
+ LocalC = val({Tab, local_content}),
+ StillActive = lists:member(Node, Nodes),
+ RemoteIntent = lists:member(Tab, Intents),
+ if
+ Access == read_write,
+ LocalC == false,
+ StillActive == true,
+ RemoteIntent == true ->
+ Masters = mnesia_recover:get_master_nodes(Tab),
+ case lists:member(Node, Masters) of
+ true ->
+ %% The other node is master node for
+ %% the table, accept his load intent
+ late_load_filter(RemoteLoaders, Tab, Nodes, [Node | Acc]);
+ false when Masters == [] ->
+ %% The table has no master nodes
+ %% accept his load intent
+ late_load_filter(RemoteLoaders, Tab, Nodes, [Node | Acc]);
+ false ->
+ %% Some one else is master node for
+ %% the table, ignore his load intent
+ late_load_filter(RemoteLoaders, Tab, Nodes, Acc)
+ end;
+ true ->
+ late_load_filter(RemoteLoaders, Tab, Nodes, Acc)
+ end;
+late_load_filter([], _Tab, _Nodes, Acc) ->
+ Acc.
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_cast({release_schema_commit_lock, _Owner}, State) ->
+ if
+ State#state.is_stopping == true ->
+ {stop, shutdown, State};
+ true ->
+ case State#state.dumper_queue of
+ [#schema_commit_lock{}|Rest] ->
+ [_Worker | Rest] = State#state.dumper_queue,
+ State2 = State#state{dumper_pid = undefined,
+ dumper_queue = Rest},
+ State3 = opt_start_worker(State2),
+ noreply(State3);
+ _ ->
+ noreply(State)
+ end
+ end;
+
+handle_cast(unblock_controller, State) ->
+ if
+ State#state.is_stopping == true ->
+ {stop, shutdown, State};
+ record(hd(State#state.dumper_queue), block_controller) ->
+ [_Worker | Rest] = State#state.dumper_queue,
+ State2 = State#state{dumper_pid = undefined,
+ dumper_queue = Rest},
+ State3 = opt_start_worker(State2),
+ noreply(State3)
+ end;
+
+handle_cast({mnesia_down, Node}, State) ->
+ maybe_log_mnesia_down(Node),
+ mnesia_lib:del({current, db_nodes}, Node),
+ mnesia_checkpoint:tm_mnesia_down(Node),
+ Alltabs = val({schema, tables}),
+ State2 = reconfigure_tables(Node, State, Alltabs),
+ case State#state.sender_pid of
+ undefined -> ignore;
+ Pid when pid(Pid) -> Pid ! {copier_done, Node}
+ end,
+ case State#state.loader_pid of
+ undefined -> ignore;
+ Pid2 when pid(Pid2) -> Pid2 ! {copier_done, Node}
+ end,
+ NewSenders =
+ case State#state.sender_queue of
+ [OldSender | RestSenders] ->
+ Remove = fun(ST) ->
+ node(ST#send_table.receiver_pid) /= Node
+ end,
+ NewS = lists:filter(Remove, RestSenders),
+ %% Keep old sender it will be removed by sender_done
+ [OldSender | NewS];
+ [] ->
+ []
+ end,
+ Early = remove_early_messages(State2#state.early_msgs, Node),
+ mnesia_monitor:mnesia_down(?SERVER_NAME, Node),
+ noreply(State2#state{sender_queue = NewSenders, early_msgs = Early});
+
+handle_cast({im_running, _Node, NewFriends}, State) ->
+ Tabs = mnesia_lib:local_active_tables() -- [schema],
+ Ns = mnesia_lib:intersect(NewFriends, val({current, db_nodes})),
+ abcast(Ns, {adopt_orphans, node(), Tabs}),
+ noreply(State);
+
+handle_cast(Msg, State) when State#state.schema_is_merged == false ->
+ %% Buffer early messages
+ Msgs = State#state.early_msgs,
+ noreply(State#state{early_msgs = [{cast, Msg} | Msgs]});
+
+handle_cast({disc_load, Tab, Reason}, State) ->
+ Worker = #disc_load{table = Tab, reason = Reason},
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+handle_cast(Worker, State) when record(Worker, send_table) ->
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+handle_cast({sync_tabs, Tabs, From}, State) ->
+ %% user initiated wait_for_tables
+ handle_sync_tabs(Tabs, From),
+ noreply(State);
+
+handle_cast({i_have_tab, Tab, Node}, State) ->
+ case lists:member(Node, val({current, db_nodes})) of
+ true ->
+ State2 = node_has_tabs([Tab], Node, State),
+ noreply(State2);
+ false ->
+ noreply(State)
+ end;
+
+handle_cast({force_load_updated, Tab}, State) ->
+ case val({Tab, active_replicas}) of
+ [] ->
+ %% No valid replicas
+ noreply(State);
+ [SomeNode | _] ->
+ State2 = node_has_tabs([Tab], SomeNode, State),
+ noreply(State2)
+ end;
+
+handle_cast({master_nodes_updated, Tab, Masters}, State) ->
+ Active = val({Tab, active_replicas}),
+ Valid =
+ case val({Tab, load_by_force}) of
+ true ->
+ Active;
+ false ->
+ if
+ Masters == [] ->
+ Active;
+ true ->
+ mnesia_lib:intersect(Masters, Active)
+ end
+ end,
+ case Valid of
+ [] ->
+ %% No valid replicas
+ noreply(State);
+ [SomeNode | _] ->
+ State2 = node_has_tabs([Tab], SomeNode, State),
+ noreply(State2)
+ end;
+
+handle_cast({adopt_orphans, Node, Tabs}, State) ->
+
+ State2 = node_has_tabs(Tabs, Node, State),
+
+ %% Register the other node as up and running
+ mnesia_recover:log_mnesia_up(Node),
+ verbose("Logging mnesia_up ~w~n", [Node]),
+ mnesia_lib:report_system_event({mnesia_up, Node}),
+
+ %% Load orphan tables
+ LocalTabs = val({schema, local_tables}) -- [schema],
+ Nodes = val({current, db_nodes}),
+ {LocalOrphans, RemoteMasters} =
+ orphan_tables(LocalTabs, Node, Nodes, [], []),
+ Reason = {adopt_orphan, node()},
+ mnesia_late_loader:async_late_disc_load(node(), LocalOrphans, Reason),
+
+ Fun =
+ fun(N) ->
+ RemoteOrphans =
+ [Tab || {Tab, Ns} <- RemoteMasters,
+ lists:member(N, Ns)],
+ mnesia_late_loader:maybe_async_late_disc_load(N, RemoteOrphans, Reason)
+ end,
+ lists:foreach(Fun, Nodes),
+
+ Queue = State2#state.loader_queue,
+ State3 = State2#state{loader_queue = Queue},
+ noreply(State3);
+
+handle_cast(Msg, State) ->
+ error("~p got unexpected cast: ~p~n", [?SERVER_NAME, Msg]),
+ noreply(State).
+
+handle_sync_tabs([Tab | Tabs], From) ->
+ case val({Tab, where_to_read}) of
+ nowhere ->
+ case get({sync_tab, Tab}) of
+ undefined ->
+ put({sync_tab, Tab}, [From]);
+ Pids ->
+ put({sync_tab, Tab}, [From | Pids])
+ end;
+ _ ->
+ sync_reply(From, Tab)
+ end,
+ handle_sync_tabs(Tabs, From);
+handle_sync_tabs([], _From) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_info({async_dump_log, InitBy}, State) ->
+ Worker = #dump_log{initiated_by = InitBy},
+ State2 = add_worker(Worker, State),
+ noreply(State2);
+
+handle_info(Done, State) when record(Done, dumper_done) ->
+ Pid = Done#dumper_done.worker_pid,
+ Res = Done#dumper_done.worker_res,
+ if
+ State#state.is_stopping == true ->
+ {stop, shutdown, State};
+ Res == dumped, Pid == State#state.dumper_pid ->
+ [Worker | Rest] = State#state.dumper_queue,
+ reply(Worker#dump_log.opt_reply_to, Res),
+ State2 = State#state{dumper_pid = undefined,
+ dumper_queue = Rest},
+ State3 = opt_start_worker(State2),
+ noreply(State3);
+ true ->
+ fatal("Dumper failed: ~p~n state: ~p~n", [Res, State]),
+ {stop, fatal, State}
+ end;
+
+handle_info(Done, State) when record(Done, loader_done) ->
+ if
+ %% Assertion
+ Done#loader_done.worker_pid == State#state.loader_pid -> ok
+ end,
+
+ [_Worker | Rest] = LoadQ0 = State#state.loader_queue,
+ LateQueue0 = State#state.late_loader_queue,
+ {LoadQ, LateQueue} =
+ case Done#loader_done.is_loaded of
+ true ->
+ Tab = Done#loader_done.table_name,
+
+ %% Optional user sync
+ case Done#loader_done.needs_sync of
+ true -> user_sync_tab(Tab);
+ false -> ignore
+ end,
+
+ %% Optional table announcement
+ case Done#loader_done.needs_announce of
+ true ->
+ i_have_tab(Tab),
+ case Tab of
+ schema ->
+ ignore;
+ _ ->
+ %% Local node needs to perform user_sync_tab/1
+ Ns = val({current, db_nodes}),
+ abcast(Ns, {i_have_tab, Tab, node()})
+ end;
+ false ->
+ case Tab of
+ schema ->
+ ignore;
+ _ ->
+ %% Local node needs to perform user_sync_tab/1
+ Ns = val({current, db_nodes}),
+ AlreadyKnows = val({Tab, active_replicas}),
+ abcast(Ns -- AlreadyKnows, {i_have_tab, Tab, node()})
+ end
+ end,
+
+ %% Optional client reply
+ case Done#loader_done.needs_reply of
+ true ->
+ reply(Done#loader_done.reply_to,
+ Done#loader_done.reply);
+ false ->
+ ignore
+ end,
+ {Rest, reply_late_load(Tab, LateQueue0)};
+ false ->
+ case Done#loader_done.reply of
+ restart ->
+ {LoadQ0, LateQueue0};
+ _ ->
+ {Rest, LateQueue0}
+ end
+ end,
+
+ State2 = State#state{loader_pid = undefined,
+ loader_queue = LoadQ,
+ late_loader_queue = LateQueue},
+
+ State3 = opt_start_worker(State2),
+ noreply(State3);
+
+handle_info(Done, State) when record(Done, sender_done) ->
+ Pid = Done#sender_done.worker_pid,
+ Res = Done#sender_done.worker_res,
+ if
+ Res == ok, Pid == State#state.sender_pid ->
+ [Worker | Rest] = State#state.sender_queue,
+ Worker#send_table.receiver_pid ! {copier_done, node()},
+ State2 = State#state{sender_pid = undefined,
+ sender_queue = Rest},
+ State3 = opt_start_worker(State2),
+ noreply(State3);
+ true ->
+ %% No need to send any message to the table receiver
+ %% since it will soon get a mnesia_down anyway
+ fatal("Sender failed: ~p~n state: ~p~n", [Res, State]),
+ {stop, fatal, State}
+ end;
+
+handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor ->
+ catch set(mnesia_status, stopping),
+ case State#state.dumper_pid of
+ undefined ->
+ dbg_out("~p was ~p~n", [?SERVER_NAME, R]),
+ {stop, shutdown, State};
+ _ ->
+ noreply(State#state{is_stopping = true})
+ end;
+
+handle_info({'EXIT', Pid, R}, State) when Pid == State#state.dumper_pid ->
+ case State#state.dumper_queue of
+ [#schema_commit_lock{}|Workers] -> %% Schema trans crashed or was killed
+ State2 = State#state{dumper_queue = Workers, dumper_pid = undefined},
+ State3 = opt_start_worker(State2),
+ noreply(State3);
+ _Other ->
+ fatal("Dumper or schema commit crashed: ~p~n state: ~p~n", [R, State]),
+ {stop, fatal, State}
+ end;
+
+handle_info({'EXIT', Pid, R}, State) when Pid == State#state.loader_pid ->
+ fatal("Loader crashed: ~p~n state: ~p~n", [R, State]),
+ {stop, fatal, State};
+
+handle_info({'EXIT', Pid, R}, State) when Pid == State#state.sender_pid ->
+ %% No need to send any message to the table receiver
+ %% since it will soon get a mnesia_down anyway
+ fatal("Sender crashed: ~p~n state: ~p~n", [R, State]),
+ {stop, fatal, State};
+
+handle_info({From, get_state}, State) ->
+ From ! {?SERVER_NAME, State},
+ noreply(State);
+
+%% No real need for buffering
+handle_info(Msg, State) when State#state.schema_is_merged == false ->
+ %% Buffer early messages
+ Msgs = State#state.early_msgs,
+ noreply(State#state{early_msgs = [{info, Msg} | Msgs]});
+
+handle_info({'EXIT', Pid, wait_for_tables_timeout}, State) ->
+ sync_tab_timeout(Pid, get()),
+ noreply(State);
+
+handle_info(Msg, State) ->
+ error("~p got unexpected info: ~p~n", [?SERVER_NAME, Msg]),
+ noreply(State).
+
+reply_late_load(Tab, [H | T]) when H#late_load.table == Tab ->
+ reply(H#late_load.opt_reply_to, ok),
+ reply_late_load(Tab, T);
+reply_late_load(Tab, [H | T]) ->
+ [H | reply_late_load(Tab, T)];
+reply_late_load(_Tab, []) ->
+ [].
+
+sync_tab_timeout(Pid, [{{sync_tab, Tab}, Pids} | Tail]) ->
+ case lists:delete(Pid, Pids) of
+ [] ->
+ erase({sync_tab, Tab});
+ Pids2 ->
+ put({sync_tab, Tab}, Pids2)
+ end,
+ sync_tab_timeout(Pid, Tail);
+sync_tab_timeout(Pid, [_ | Tail]) ->
+ sync_tab_timeout(Pid, Tail);
+sync_tab_timeout(_Pid, []) ->
+ ok.
+
+%% Pick the load record that has the highest load order
+%% Returns {BestLoad, RemainingQueue} or {none, []} if queue is empty
+pick_next(Queue) ->
+ pick_next(Queue, none, none, []).
+
+pick_next([Head | Tail], Load, Order, Rest) when record(Head, net_load) ->
+ Tab = Head#net_load.table,
+ select_best(Head, Tail, val({Tab, load_order}), Load, Order, Rest);
+pick_next([Head | Tail], Load, Order, Rest) when record(Head, disc_load) ->
+ Tab = Head#disc_load.table,
+ select_best(Head, Tail, val({Tab, load_order}), Load, Order, Rest);
+pick_next([], Load, _Order, Rest) ->
+ {Load, Rest}.
+
+select_best(Load, Tail, Order, none, none, Rest) ->
+ pick_next(Tail, Load, Order, Rest);
+select_best(Load, Tail, Order, OldLoad, OldOrder, Rest) when Order > OldOrder ->
+ pick_next(Tail, Load, Order, [OldLoad | Rest]);
+select_best(Load, Tail, _Order, OldLoad, OldOrder, Rest) ->
+ pick_next(Tail, OldLoad, OldOrder, [Load | Rest]).
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+terminate(Reason, State) ->
+ mnesia_monitor:terminate_proc(?SERVER_NAME, Reason, State).
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+maybe_log_mnesia_down(N) ->
+ %% We use mnesia_down when deciding which tables to load locally,
+ %% so if we are not running (i.e haven't decided which tables
+ %% to load locally), don't log mnesia_down yet.
+ case mnesia_lib:is_running() of
+ yes ->
+ verbose("Logging mnesia_down ~w~n", [N]),
+ mnesia_recover:log_mnesia_down(N),
+ ok;
+ _ ->
+ Filter = fun(Tab) ->
+ inactive_copy_holders(Tab, N)
+ end,
+ HalfLoadedTabs = lists:any(Filter, val({schema, local_tables}) -- [schema]),
+ if
+ HalfLoadedTabs == true ->
+ verbose("Logging mnesia_down ~w~n", [N]),
+ mnesia_recover:log_mnesia_down(N),
+ ok;
+ true ->
+ %% Unfortunately we have not loaded some common
+ %% tables yet, so we cannot rely on the nodedown
+ log_later %% BUGBUG handle this case!!!
+ end
+ end.
+
+inactive_copy_holders(Tab, Node) ->
+ Cs = val({Tab, cstruct}),
+ case mnesia_lib:cs_to_storage_type(Node, Cs) of
+ unknown ->
+ false;
+ _Storage ->
+ mnesia_lib:not_active_here(Tab)
+ end.
+
+orphan_tables([Tab | Tabs], Node, Ns, Local, Remote) ->
+ Cs = val({Tab, cstruct}),
+ CopyHolders = mnesia_lib:copy_holders(Cs),
+ RamCopyHolders = Cs#cstruct.ram_copies,
+ DiscCopyHolders = CopyHolders -- RamCopyHolders,
+ DiscNodes = val({schema, disc_copies}),
+ LocalContent = Cs#cstruct.local_content,
+ RamCopyHoldersOnDiscNodes = mnesia_lib:intersect(RamCopyHolders, DiscNodes),
+ Active = val({Tab, active_replicas}),
+ case lists:member(Node, DiscCopyHolders) of
+ true when Active == [] ->
+ case DiscCopyHolders -- Ns of
+ [] ->
+ %% We're last up and the other nodes have not
+ %% loaded the table. Lets load it if we are
+ %% the smallest node.
+ case lists:min(DiscCopyHolders) of
+ Min when Min == node() ->
+ case mnesia_recover:get_master_nodes(Tab) of
+ [] ->
+ L = [Tab | Local],
+ orphan_tables(Tabs, Node, Ns, L, Remote);
+ Masters ->
+ R = [{Tab, Masters} | Remote],
+ orphan_tables(Tabs, Node, Ns, Local, R)
+ end;
+ _ ->
+ orphan_tables(Tabs, Node, Ns, Local, Remote)
+ end;
+ _ ->
+ orphan_tables(Tabs, Node, Ns, Local, Remote)
+ end;
+ false when Active == [], DiscCopyHolders == [], RamCopyHoldersOnDiscNodes == [] ->
+ %% Special case when all replicas resides on disc less nodes
+ orphan_tables(Tabs, Node, Ns, [Tab | Local], Remote);
+ _ when LocalContent == true ->
+ orphan_tables(Tabs, Node, Ns, [Tab | Local], Remote);
+ _ ->
+ orphan_tables(Tabs, Node, Ns, Local, Remote)
+ end;
+orphan_tables([], _, _, LocalOrphans, RemoteMasters) ->
+ {LocalOrphans, RemoteMasters}.
+
+node_has_tabs([Tab | Tabs], Node, State) when Node /= node() ->
+ State2 = update_whereabouts(Tab, Node, State),
+ node_has_tabs(Tabs, Node, State2);
+node_has_tabs([Tab | Tabs], Node, State) ->
+ user_sync_tab(Tab),
+ node_has_tabs(Tabs, Node, State);
+node_has_tabs([], _Node, State) ->
+ State.
+
+update_whereabouts(Tab, Node, State) ->
+ Storage = val({Tab, storage_type}),
+ Read = val({Tab, where_to_read}),
+ LocalC = val({Tab, local_content}),
+ BeingCreated = (?catch_val({Tab, create_table}) == true),
+ Masters = mnesia_recover:get_master_nodes(Tab),
+ ByForce = val({Tab, load_by_force}),
+ GoGetIt =
+ if
+ ByForce == true ->
+ true;
+ Masters == [] ->
+ true;
+ true ->
+ lists:member(Node, Masters)
+ end,
+
+ dbg_out("Table ~w is loaded on ~w. s=~w, r=~w, lc=~w, f=~w, m=~w~n",
+ [Tab, Node, Storage, Read, LocalC, ByForce, GoGetIt]),
+ if
+ LocalC == true ->
+ %% Local contents, don't care about other node
+ State;
+ Storage == unknown, Read == nowhere ->
+ %% No own copy, time to read remotely
+ %% if the other node is a good node
+ add_active_replica(Tab, Node),
+ case GoGetIt of
+ true ->
+ set({Tab, where_to_read}, Node),
+ user_sync_tab(Tab),
+ State;
+ false ->
+ State
+ end;
+ Storage == unknown ->
+ %% No own copy, continue to read remotely
+ add_active_replica(Tab, Node),
+ NodeST = mnesia_lib:storage_type_at_node(Node, Tab),
+ ReadST = mnesia_lib:storage_type_at_node(Read, Tab),
+ if %% Avoid reading from disc_only_copies
+ NodeST == disc_only_copies ->
+ ignore;
+ ReadST == disc_only_copies ->
+ mnesia_lib:set_remote_where_to_read(Tab);
+ true ->
+ ignore
+ end,
+ user_sync_tab(Tab),
+ State;
+ BeingCreated == true ->
+ %% The table is currently being created
+ %% and we shall have an own copy of it.
+ %% We will load the (empty) table locally.
+ add_active_replica(Tab, Node),
+ State;
+ Read == nowhere ->
+ %% Own copy, go and get a copy of the table
+ %% if the other node is master or if there
+ %% are no master at all
+ add_active_replica(Tab, Node),
+ case GoGetIt of
+ true ->
+ Worker = #net_load{table = Tab,
+ reason = {active_remote, Node}},
+ add_worker(Worker, State);
+ false ->
+ State
+ end;
+ true ->
+ %% We already have an own copy
+ add_active_replica(Tab, Node),
+ user_sync_tab(Tab),
+ State
+ end.
+
+initial_safe_loads() ->
+ case val({schema, storage_type}) of
+ ram_copies ->
+ Downs = [],
+ Tabs = val({schema, local_tables}) -- [schema],
+ LastC = fun(T) -> last_consistent_replica(T, Downs) end,
+ lists:zf(LastC, Tabs);
+
+ disc_copies ->
+ Downs = mnesia_recover:get_mnesia_downs(),
+ dbg_out("mnesia_downs = ~p~n", [Downs]),
+
+ Tabs = val({schema, local_tables}) -- [schema],
+ LastC = fun(T) -> last_consistent_replica(T, Downs) end,
+ lists:zf(LastC, Tabs)
+ end.
+
+last_consistent_replica(Tab, Downs) ->
+ Cs = val({Tab, cstruct}),
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ Ram = Cs#cstruct.ram_copies,
+ Disc = Cs#cstruct.disc_copies,
+ DiscOnly = Cs#cstruct.disc_only_copies,
+ BetterCopies0 = mnesia_lib:remote_copy_holders(Cs) -- Downs,
+ BetterCopies = BetterCopies0 -- Ram,
+ AccessMode = Cs#cstruct.access_mode,
+ Copies = mnesia_lib:copy_holders(Cs),
+ Masters = mnesia_recover:get_master_nodes(Tab),
+ LocalMaster0 = lists:member(node(), Masters),
+ LocalContent = Cs#cstruct.local_content,
+ RemoteMaster =
+ if
+ Masters == [] -> false;
+ true -> not LocalMaster0
+ end,
+ LocalMaster =
+ if
+ Masters == [] -> false;
+ true -> LocalMaster0
+ end,
+ if
+ Copies == [node()] ->
+ %% Only one copy holder and it is local.
+ %% It may also be a local contents table
+ {true, {Tab, local_only}};
+ LocalContent == true ->
+ {true, {Tab, local_content}};
+ LocalMaster == true ->
+ %% We have a local master
+ {true, {Tab, local_master}};
+ RemoteMaster == true ->
+ %% Wait for remote master copy
+ false;
+ Storage == ram_copies ->
+ if
+ Disc == [], DiscOnly == [] ->
+ %% Nobody has copy on disc
+ {true, {Tab, ram_only}};
+ true ->
+ %% Some other node has copy on disc
+ false
+ end;
+ AccessMode == read_only ->
+ %% No one has been able to update the table,
+ %% i.e. all disc resident copies are equal
+ {true, {Tab, read_only}};
+ BetterCopies /= [], Masters /= [node()] ->
+ %% There are better copies on other nodes
+ %% and we do not have the only master copy
+ false;
+ true ->
+ {true, {Tab, initial}}
+ end.
+
+reconfigure_tables(N, State, [Tab |Tail]) ->
+ del_active_replica(Tab, N),
+ case val({Tab, where_to_read}) of
+ N -> mnesia_lib:set_remote_where_to_read(Tab);
+ _ -> ignore
+ end,
+ LateQ = drop_loaders(Tab, N, State#state.late_loader_queue),
+ reconfigure_tables(N, State#state{late_loader_queue = LateQ}, Tail);
+
+reconfigure_tables(_, State, []) ->
+ State.
+
+remove_early_messages([], _Node) ->
+ [];
+remove_early_messages([{call, {add_active_replica, [_, Node, _, _], _}, _}|R], Node) ->
+ remove_early_messages(R, Node); %% Does a reply before queuing
+remove_early_messages([{call, {block_table, _, From}, ReplyTo}|R], Node)
+ when node(From) == Node ->
+ reply(ReplyTo, ok), %% Remove gen:server waits..
+ remove_early_messages(R, Node);
+remove_early_messages([{cast, {i_have_tab, _Tab, Node}}|R], Node) ->
+ remove_early_messages(R, Node);
+remove_early_messages([{cast, {adopt_orphans, Node, _Tabs}}|R], Node) ->
+ remove_early_messages(R, Node);
+remove_early_messages([M|R],Node) ->
+ [M|remove_early_messages(R,Node)].
+
+%% Drop loader from late load queue and possibly trigger a disc_load
+drop_loaders(Tab, Node, [H | T]) when H#late_load.table == Tab ->
+ %% Check if it is time to issue a disc_load request
+ case H#late_load.loaders of
+ [Node] ->
+ Reason = {H#late_load.reason, last_loader_down, Node},
+ cast({disc_load, Tab, Reason}); % Ugly cast
+ _ ->
+ ignore
+ end,
+ %% Drop the node from the list of loaders
+ H2 = H#late_load{loaders = H#late_load.loaders -- [Node]},
+ [H2 | drop_loaders(Tab, Node, T)];
+drop_loaders(Tab, Node, [H | T]) ->
+ [H | drop_loaders(Tab, Node, T)];
+drop_loaders(_, _, []) ->
+ [].
+
+add_active_replica(Tab, Node) ->
+ add_active_replica(Tab, Node, val({Tab, cstruct})).
+
+add_active_replica(Tab, Node, Cs) when record(Cs, cstruct) ->
+ Storage = mnesia_lib:schema_cs_to_storage_type(Node, Cs),
+ AccessMode = Cs#cstruct.access_mode,
+ add_active_replica(Tab, Node, Storage, AccessMode).
+
+%% Block table primitives
+
+block_table(Tab) ->
+ Var = {Tab, where_to_commit},
+ Old = val(Var),
+ New = {blocked, Old},
+ set(Var, New). % where_to_commit
+
+unblock_table(Tab) ->
+ Var = {Tab, where_to_commit},
+ New =
+ case val(Var) of
+ {blocked, List} ->
+ List;
+ List ->
+ List
+ end,
+ set(Var, New). % where_to_commit
+
+is_tab_blocked(W2C) when list(W2C) ->
+ {false, W2C};
+is_tab_blocked({blocked, W2C}) when list(W2C) ->
+ {true, W2C}.
+
+mark_blocked_tab(true, Value) ->
+ {blocked, Value};
+mark_blocked_tab(false, Value) ->
+ Value.
+
+%%
+
+add_active_replica(Tab, Node, Storage, AccessMode) ->
+ Var = {Tab, where_to_commit},
+ {Blocked, Old} = is_tab_blocked(val(Var)),
+ Del = lists:keydelete(Node, 1, Old),
+ case AccessMode of
+ read_write ->
+ New = lists:sort([{Node, Storage} | Del]),
+ set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit
+ add({Tab, where_to_write}, Node);
+ read_only ->
+ set(Var, mark_blocked_tab(Blocked, Del)),
+ mnesia_lib:del({Tab, where_to_write}, Node)
+ end,
+ add({Tab, active_replicas}, Node).
+
+del_active_replica(Tab, Node) ->
+ Var = {Tab, where_to_commit},
+ {Blocked, Old} = is_tab_blocked(val(Var)),
+ Del = lists:keydelete(Node, 1, Old),
+ New = lists:sort(Del),
+ set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit
+ mnesia_lib:del({Tab, active_replicas}, Node),
+ mnesia_lib:del({Tab, where_to_write}, Node).
+
+change_table_access_mode(Cs) ->
+ Tab = Cs#cstruct.name,
+ lists:foreach(fun(N) -> add_active_replica(Tab, N, Cs) end,
+ val({Tab, active_replicas})).
+
+%% node To now has tab loaded, but this must be undone
+%% This code is rpc:call'ed from the tab_copier process
+%% when it has *not* released it's table lock
+unannounce_add_table_copy(Tab, To) ->
+ del_active_replica(Tab, To),
+ case val({Tab , where_to_read}) of
+ To ->
+ mnesia_lib:set_remote_where_to_read(Tab);
+ _ ->
+ ignore
+ end.
+
+user_sync_tab(Tab) ->
+ case val(debug) of
+ trace ->
+ mnesia_subscr:subscribe(whereis(mnesia_event), {table, Tab});
+ _ ->
+ ignore
+ end,
+
+ case erase({sync_tab, Tab}) of
+ undefined ->
+ ok;
+ Pids ->
+ lists:foreach(fun(Pid) -> sync_reply(Pid, Tab) end, Pids)
+ end.
+
+i_have_tab(Tab) ->
+ case val({Tab, local_content}) of
+ true ->
+ mnesia_lib:set_local_content_whereabouts(Tab);
+ false ->
+ set({Tab, where_to_read}, node())
+ end,
+ add_active_replica(Tab, node()).
+
+sync_and_block_table_whereabouts(Tab, ToNode, RemoteS, AccessMode) when Tab /= schema ->
+ Current = val({current, db_nodes}),
+ Ns =
+ case lists:member(ToNode, Current) of
+ true -> Current -- [ToNode];
+ false -> Current
+ end,
+ remote_call(ToNode, block_table, [Tab]),
+ [remote_call(Node, add_active_replica, [Tab, ToNode, RemoteS, AccessMode]) ||
+ Node <- [ToNode | Ns]],
+ ok.
+
+sync_del_table_copy_whereabouts(Tab, ToNode) when Tab /= schema ->
+ Current = val({current, db_nodes}),
+ Ns =
+ case lists:member(ToNode, Current) of
+ true -> Current;
+ false -> [ToNode | Current]
+ end,
+ Args = [Tab, ToNode],
+ [remote_call(Node, unannounce_add_table_copy, Args) || Node <- Ns],
+ ok.
+
+get_info(Timeout) ->
+ case whereis(?SERVER_NAME) of
+ undefined ->
+ {timeout, Timeout};
+ Pid ->
+ Pid ! {self(), get_state},
+ receive
+ {?SERVER_NAME, State} when record(State, state) ->
+ {info,State}
+ after Timeout ->
+ {timeout, Timeout}
+ end
+ end.
+
+get_workers(Timeout) ->
+ case whereis(?SERVER_NAME) of
+ undefined ->
+ {timeout, Timeout};
+ Pid ->
+ Pid ! {self(), get_state},
+ receive
+ {?SERVER_NAME, State} when record(State, state) ->
+ {workers, State#state.loader_pid, State#state.sender_pid, State#state.dumper_pid}
+ after Timeout ->
+ {timeout, Timeout}
+ end
+ end.
+
+info() ->
+ Tabs = mnesia_lib:local_active_tables(),
+ io:format( "---> Active tables <--- ~n", []),
+ info(Tabs).
+
+info([Tab | Tail]) ->
+ case val({Tab, storage_type}) of
+ disc_only_copies ->
+ info_format(Tab,
+ dets:info(Tab, size),
+ dets:info(Tab, file_size),
+ "bytes on disc");
+ _ ->
+ info_format(Tab,
+ ?ets_info(Tab, size),
+ ?ets_info(Tab, memory),
+ "words of mem")
+ end,
+ info(Tail);
+info([]) -> ok;
+info(Tab) -> info([Tab]).
+
+info_format(Tab, Size, Mem, Media) ->
+ StrT = mnesia_lib:pad_name(atom_to_list(Tab), 15, []),
+ StrS = mnesia_lib:pad_name(integer_to_list(Size), 8, []),
+ StrM = mnesia_lib:pad_name(integer_to_list(Mem), 8, []),
+ io:format("~s: with ~s records occupying ~s ~s~n",
+ [StrT, StrS, StrM, Media]).
+
+%% Handle early arrived messages
+handle_early_msgs([Msg | Msgs], State) ->
+ %% The messages are in reverse order
+ case handle_early_msg(Msg, State) of
+ {stop, Reason, Reply, State2} ->
+ {stop, Reason, Reply, State2};
+ {stop, Reason, State2} ->
+ {stop, Reason, State2};
+ {noreply, State2} ->
+ handle_early_msgs(Msgs, State2);
+ {noreply, State2, _Timeout} ->
+ handle_early_msgs(Msgs, State2);
+ Else ->
+ dbg_out("handle_early_msgs case clause ~p ~n", [Else]),
+ erlang:error(Else, [[Msg | Msgs], State])
+ end;
+handle_early_msgs([], State) ->
+ noreply(State).
+
+handle_early_msg({call, Msg, From}, State) ->
+ handle_call(Msg, From, State);
+handle_early_msg({cast, Msg}, State) ->
+ handle_cast(Msg, State);
+handle_early_msg({info, Msg}, State) ->
+ handle_info(Msg, State).
+
+noreply(State) ->
+ {noreply, State}.
+
+reply(undefined, Reply) ->
+ Reply;
+reply(ReplyTo, Reply) ->
+ gen_server:reply(ReplyTo, Reply),
+ Reply.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Worker management
+
+%% Returns new State
+add_worker(Worker, State) when record(Worker, dump_log) ->
+ InitBy = Worker#dump_log.initiated_by,
+ Queue = State#state.dumper_queue,
+ case lists:keymember(InitBy, #dump_log.initiated_by, Queue) of
+ false ->
+ ignore;
+ true when Worker#dump_log.opt_reply_to == undefined ->
+ %% The same threshold has been exceeded again,
+ %% before we have had the possibility to
+ %% process the older one.
+ DetectedBy = {dump_log, InitBy},
+ Event = {mnesia_overload, DetectedBy},
+ mnesia_lib:report_system_event(Event)
+ end,
+ Queue2 = Queue ++ [Worker],
+ State2 = State#state{dumper_queue = Queue2},
+ opt_start_worker(State2);
+add_worker(Worker, State) when record(Worker, schema_commit_lock) ->
+ Queue = State#state.dumper_queue,
+ Queue2 = Queue ++ [Worker],
+ State2 = State#state{dumper_queue = Queue2},
+ opt_start_worker(State2);
+add_worker(Worker, State) when record(Worker, net_load) ->
+ Queue = State#state.loader_queue,
+ State2 = State#state{loader_queue = Queue ++ [Worker]},
+ opt_start_worker(State2);
+add_worker(Worker, State) when record(Worker, send_table) ->
+ Queue = State#state.sender_queue,
+ State2 = State#state{sender_queue = Queue ++ [Worker]},
+ opt_start_worker(State2);
+add_worker(Worker, State) when record(Worker, disc_load) ->
+ Queue = State#state.loader_queue,
+ State2 = State#state{loader_queue = Queue ++ [Worker]},
+ opt_start_worker(State2);
+% Block controller should be used for upgrading mnesia.
+add_worker(Worker, State) when record(Worker, block_controller) ->
+ Queue = State#state.dumper_queue,
+ Queue2 = [Worker | Queue],
+ State2 = State#state{dumper_queue = Queue2},
+ opt_start_worker(State2).
+
+%% Optionally start a worker
+%%
+%% Dumpers and loaders may run simultaneously
+%% but neither of them may run during schema commit.
+%% Loaders may not start if a schema commit is enqueued.
+opt_start_worker(State) when State#state.is_stopping == true ->
+ State;
+opt_start_worker(State) ->
+ %% Prioritize dumper and schema commit
+ %% by checking them first
+ case State#state.dumper_queue of
+ [Worker | _Rest] when State#state.dumper_pid == undefined ->
+ %% Great, a worker in queue and neither
+ %% a schema transaction is being
+ %% committed and nor a dumper is running
+
+ %% Start worker but keep him in the queue
+ if
+ record(Worker, schema_commit_lock) ->
+ ReplyTo = Worker#schema_commit_lock.owner,
+ reply(ReplyTo, granted),
+ {Owner, _Tag} = ReplyTo,
+ State#state{dumper_pid = Owner};
+
+ record(Worker, dump_log) ->
+ Pid = spawn_link(?MODULE, dump_and_reply, [self(), Worker]),
+ State2 = State#state{dumper_pid = Pid},
+
+ %% If the worker was a dumper we may
+ %% possibly be able to start a loader
+ %% or sender
+ State3 = opt_start_sender(State2),
+ opt_start_loader(State3);
+
+ record(Worker, block_controller) ->
+ case {State#state.sender_pid, State#state.loader_pid} of
+ {undefined, undefined} ->
+ ReplyTo = Worker#block_controller.owner,
+ reply(ReplyTo, granted),
+ {Owner, _Tag} = ReplyTo,
+ State#state{dumper_pid = Owner};
+ _ ->
+ State
+ end
+ end;
+ _ ->
+ %% Bad luck, try with a loader or sender instead
+ State2 = opt_start_sender(State),
+ opt_start_loader(State2)
+ end.
+
+opt_start_sender(State) ->
+ case State#state.sender_queue of
+ []->
+ %% No need
+ State;
+
+ _ when State#state.sender_pid /= undefined ->
+ %% Bad luck, a sender is already running
+ State;
+
+ [Sender | _SenderRest] ->
+ case State#state.loader_queue of
+ [Loader | _LoaderRest]
+ when State#state.loader_pid /= undefined,
+ Loader#net_load.table == Sender#send_table.table ->
+ %% A conflicting loader is running
+ State;
+ _ ->
+ SchemaQueue = State#state.dumper_queue,
+ case lists:keymember(schema_commit, 1, SchemaQueue) of
+ false ->
+
+ %% Start worker but keep him in the queue
+ Pid = spawn_link(?MODULE, send_and_reply,
+ [self(), Sender]),
+ State#state{sender_pid = Pid};
+ true ->
+ %% Bad luck, we must wait for the schema commit
+ State
+ end
+ end
+ end.
+
+opt_start_loader(State) ->
+ LoaderQueue = State#state.loader_queue,
+ if
+ LoaderQueue == [] ->
+ %% No need
+ State;
+
+ State#state.loader_pid /= undefined ->
+ %% Bad luck, an loader is already running
+ State;
+
+ true ->
+ SchemaQueue = State#state.dumper_queue,
+ case lists:keymember(schema_commit, 1, SchemaQueue) of
+ false ->
+ {Worker, Rest} = pick_next(LoaderQueue),
+
+ %% Start worker but keep him in the queue
+ Pid = spawn_link(?MODULE, load_and_reply, [self(), Worker]),
+ State#state{loader_pid = Pid,
+ loader_queue = [Worker | Rest]};
+ true ->
+ %% Bad luck, we must wait for the schema commit
+ State
+ end
+ end.
+
+start_remote_sender(Node, Tab, Receiver, Storage) ->
+ Msg = #send_table{table = Tab,
+ receiver_pid = Receiver,
+ remote_storage = Storage},
+ gen_server:cast({?SERVER_NAME, Node}, Msg).
+
+dump_and_reply(ReplyTo, Worker) ->
+ %% No trap_exit, die intentionally instead
+ Res = mnesia_dumper:opt_dump_log(Worker#dump_log.initiated_by),
+ ReplyTo ! #dumper_done{worker_pid = self(),
+ worker_res = Res},
+ unlink(ReplyTo),
+ exit(normal).
+
+send_and_reply(ReplyTo, Worker) ->
+ %% No trap_exit, die intentionally instead
+ Res = mnesia_loader:send_table(Worker#send_table.receiver_pid,
+ Worker#send_table.table,
+ Worker#send_table.remote_storage),
+ ReplyTo ! #sender_done{worker_pid = self(),
+ worker_res = Res},
+ unlink(ReplyTo),
+ exit(normal).
+
+
+load_and_reply(ReplyTo, Worker) ->
+ process_flag(trap_exit, true),
+ Done = load_table(Worker),
+ ReplyTo ! Done#loader_done{worker_pid = self()},
+ unlink(ReplyTo),
+ exit(normal).
+
+%% Now it is time to load the table
+%% but first we must check if it still is neccessary
+load_table(Load) when record(Load, net_load) ->
+ Tab = Load#net_load.table,
+ ReplyTo = Load#net_load.opt_reply_to,
+ Reason = Load#net_load.reason,
+ LocalC = val({Tab, local_content}),
+ AccessMode = val({Tab, access_mode}),
+ ReadNode = val({Tab, where_to_read}),
+ Active = filter_active(Tab),
+ Done = #loader_done{is_loaded = true,
+ table_name = Tab,
+ needs_announce = false,
+ needs_sync = false,
+ needs_reply = true,
+ reply_to = ReplyTo,
+ reply = {loaded, ok}
+ },
+ if
+ ReadNode == node() ->
+ %% Already loaded locally
+ Done;
+ LocalC == true ->
+ Res = mnesia_loader:disc_load_table(Tab, load_local_content),
+ Done#loader_done{reply = Res, needs_announce = true, needs_sync = true};
+ AccessMode == read_only ->
+ disc_load_table(Tab, Reason, ReplyTo);
+ true ->
+ %% Either we cannot read the table yet
+ %% or someone is moving a replica between
+ %% two nodes
+ Cs = Load#net_load.cstruct,
+ Res = mnesia_loader:net_load_table(Tab, Reason, Active, Cs),
+ case Res of
+ {loaded, ok} ->
+ Done#loader_done{needs_sync = true,
+ reply = Res};
+ {not_loaded, storage_unknown} ->
+ Done#loader_done{reply = Res};
+ {not_loaded, _} ->
+ Done#loader_done{is_loaded = false,
+ needs_reply = false,
+ reply = Res}
+ end
+ end;
+
+load_table(Load) when record(Load, disc_load) ->
+ Tab = Load#disc_load.table,
+ Reason = Load#disc_load.reason,
+ ReplyTo = Load#disc_load.opt_reply_to,
+ ReadNode = val({Tab, where_to_read}),
+ Active = filter_active(Tab),
+ Done = #loader_done{is_loaded = true,
+ table_name = Tab,
+ needs_announce = false,
+ needs_sync = false,
+ needs_reply = false
+ },
+ if
+ Active == [], ReadNode == nowhere ->
+ %% Not loaded anywhere, lets load it from disc
+ disc_load_table(Tab, Reason, ReplyTo);
+ ReadNode == nowhere ->
+ %% Already loaded on other node, lets get it
+ Cs = val({Tab, cstruct}),
+ case mnesia_loader:net_load_table(Tab, Reason, Active, Cs) of
+ {loaded, ok} ->
+ Done#loader_done{needs_sync = true};
+ {not_loaded, storage_unknown} ->
+ Done#loader_done{is_loaded = false};
+ {not_loaded, ErrReason} ->
+ Done#loader_done{is_loaded = false,
+ reply = {not_loaded,ErrReason}}
+ end;
+ true ->
+ %% Already readable, do not worry be happy
+ Done
+ end.
+
+disc_load_table(Tab, Reason, ReplyTo) ->
+ Done = #loader_done{is_loaded = true,
+ table_name = Tab,
+ needs_announce = false,
+ needs_sync = false,
+ needs_reply = true,
+ reply_to = ReplyTo,
+ reply = {loaded, ok}
+ },
+ Res = mnesia_loader:disc_load_table(Tab, Reason),
+ if
+ Res == {loaded, ok} ->
+ Done#loader_done{needs_announce = true,
+ needs_sync = true,
+ reply = Res};
+ ReplyTo /= undefined ->
+ Done#loader_done{is_loaded = false,
+ reply = Res};
+ true ->
+ fatal("Cannot load table ~p from disc: ~p~n", [Tab, Res])
+ end.
+
+filter_active(Tab) ->
+ ByForce = val({Tab, load_by_force}),
+ Active = val({Tab, active_replicas}),
+ Masters = mnesia_recover:get_master_nodes(Tab),
+ do_filter_active(ByForce, Active, Masters).
+
+do_filter_active(true, Active, _Masters) ->
+ Active;
+do_filter_active(false, Active, []) ->
+ Active;
+do_filter_active(false, Active, Masters) ->
+ mnesia_lib:intersect(Active, Masters).
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_dumper.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_dumper.erl
new file mode 100644
index 0000000000..bbdb04589b
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_dumper.erl
@@ -0,0 +1,1092 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_dumper.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_dumper).
+
+%% The InitBy arg may be one of the following:
+%% scan_decisions Initial scan for decisions
+%% startup Initial dump during startup
+%% schema_prepare Dump initiated during schema transaction preparation
+%% schema_update Dump initiated during schema transaction commit
+%% fast_schema_update A schema_update, but ignores the log file
+%% user Dump initiated by user
+%% write_threshold Automatic dump caused by too many log writes
+%% time_threshold Automatic dump caused by timeout
+
+%% Public interface
+-export([
+ get_log_writes/0,
+ incr_log_writes/0,
+ raw_dump_table/2,
+ raw_named_dump_table/2,
+ start_regulator/0,
+ opt_dump_log/1,
+ update/3
+ ]).
+
+ %% Internal stuff
+-export([regulator_init/1]).
+
+-include("mnesia.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-import(mnesia_lib, [fatal/2, dbg_out/2]).
+
+-define(REGULATOR_NAME, mnesia_dumper_load_regulator).
+-define(DumpToEtsMultiplier, 4).
+
+-record(state, {initiated_by = nobody,
+ dumper = nopid,
+ regulator_pid,
+ supervisor_pid,
+ queue = [],
+ timeout}).
+
+get_log_writes() ->
+ Max = mnesia_monitor:get_env(dump_log_write_threshold),
+ Prev = mnesia_lib:read_counter(trans_log_writes),
+ Left = mnesia_lib:read_counter(trans_log_writes_left),
+ Diff = Max - Left,
+ Prev + Diff.
+
+incr_log_writes() ->
+ Left = mnesia_lib:incr_counter(trans_log_writes_left, -1),
+ if
+ Left > 0 ->
+ ignore;
+ true ->
+ adjust_log_writes(true)
+ end.
+
+adjust_log_writes(DoCast) ->
+ Token = {mnesia_adjust_log_writes, self()},
+ case global:set_lock(Token, [node()], 1) of
+ false ->
+ ignore; %% Somebody else is sending a dump request
+ true ->
+ case DoCast of
+ false ->
+ ignore;
+ true ->
+ mnesia_controller:async_dump_log(write_threshold)
+ end,
+ Max = mnesia_monitor:get_env(dump_log_write_threshold),
+ Left = mnesia_lib:read_counter(trans_log_writes_left),
+ %% Don't care if we lost a few writes
+ mnesia_lib:set_counter(trans_log_writes_left, Max),
+ Diff = Max - Left,
+ mnesia_lib:incr_counter(trans_log_writes, Diff),
+ global:del_lock(Token, [node()])
+ end.
+
+%% Returns 'ok' or exits
+opt_dump_log(InitBy) ->
+ Reg = case whereis(?REGULATOR_NAME) of
+ undefined ->
+ nopid;
+ Pid when pid(Pid) ->
+ Pid
+ end,
+ perform_dump(InitBy, Reg).
+
+%% Scan for decisions
+perform_dump(InitBy, Regulator) when InitBy == scan_decisions ->
+ ?eval_debug_fun({?MODULE, perform_dump}, [InitBy]),
+
+ dbg_out("Transaction log dump initiated by ~w~n", [InitBy]),
+ scan_decisions(mnesia_log:previous_log_file(), InitBy, Regulator),
+ scan_decisions(mnesia_log:latest_log_file(), InitBy, Regulator);
+
+%% Propagate the log into the DAT-files
+perform_dump(InitBy, Regulator) ->
+ ?eval_debug_fun({?MODULE, perform_dump}, [InitBy]),
+ LogState = mnesia_log:prepare_log_dump(InitBy),
+ dbg_out("Transaction log dump initiated by ~w: ~w~n",
+ [InitBy, LogState]),
+ adjust_log_writes(false),
+ mnesia_recover:allow_garb(),
+ case LogState of
+ already_dumped ->
+ dumped;
+ {needs_dump, Diff} ->
+ U = mnesia_monitor:get_env(dump_log_update_in_place),
+ Cont = mnesia_log:init_log_dump(),
+ case catch do_perform_dump(Cont, U, InitBy, Regulator, undefined) of
+ ok ->
+ ?eval_debug_fun({?MODULE, post_dump}, [InitBy]),
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_recover:dump_decision_tab();
+ false ->
+ mnesia_log:purge_some_logs()
+ end,
+ %% And now to the crucial point...
+ mnesia_log:confirm_log_dump(Diff);
+ {error, Reason} ->
+ {error, Reason};
+ {'EXIT', {Desc, Reason}} ->
+ case mnesia_monitor:get_env(auto_repair) of
+ true ->
+ mnesia_lib:important(Desc, Reason),
+ %% Ignore rest of the log
+ mnesia_log:confirm_log_dump(Diff);
+ false ->
+ fatal(Desc, Reason)
+ end
+ end;
+ {error, Reason} ->
+ {error, {"Cannot prepare log dump", Reason}}
+ end.
+
+scan_decisions(Fname, InitBy, Regulator) ->
+ Exists = mnesia_lib:exists(Fname),
+ case Exists of
+ false ->
+ ok;
+ true ->
+ Header = mnesia_log:trans_log_header(),
+ Name = previous_log,
+ mnesia_log:open_log(Name, Header, Fname, Exists,
+ mnesia_monitor:get_env(auto_repair), read_only),
+ Cont = start,
+ Res = (catch do_perform_dump(Cont, false, InitBy, Regulator, undefined)),
+ mnesia_log:close_log(Name),
+ case Res of
+ ok -> ok;
+ {'EXIT', Reason} -> {error, Reason}
+ end
+ end.
+
+do_perform_dump(Cont, InPlace, InitBy, Regulator, OldVersion) ->
+ case mnesia_log:chunk_log(Cont) of
+ {C2, Recs} ->
+ case catch insert_recs(Recs, InPlace, InitBy, Regulator, OldVersion) of
+ {'EXIT', R} ->
+ Reason = {"Transaction log dump error: ~p~n", [R]},
+ close_files(InPlace, {error, Reason}, InitBy),
+ exit(Reason);
+ Version ->
+ do_perform_dump(C2, InPlace, InitBy, Regulator, Version)
+ end;
+ eof ->
+ close_files(InPlace, ok, InitBy),
+ ok
+ end.
+
+insert_recs([Rec | Recs], InPlace, InitBy, Regulator, LogV) ->
+ regulate(Regulator),
+ case insert_rec(Rec, InPlace, InitBy, LogV) of
+ LogH when record(LogH, log_header) ->
+ insert_recs(Recs, InPlace, InitBy, Regulator, LogH#log_header.log_version);
+ _ ->
+ insert_recs(Recs, InPlace, InitBy, Regulator, LogV)
+ end;
+
+insert_recs([], _InPlace, _InitBy, _Regulator, Version) ->
+ Version.
+
+insert_rec(Rec, _InPlace, scan_decisions, _LogV) ->
+ if
+ record(Rec, commit) ->
+ ignore;
+ record(Rec, log_header) ->
+ ignore;
+ true ->
+ mnesia_recover:note_log_decision(Rec, scan_decisions)
+ end;
+insert_rec(Rec, InPlace, InitBy, LogV) when record(Rec, commit) ->
+ %% Determine the Outcome of the transaction and recover it
+ D = Rec#commit.decision,
+ case mnesia_recover:wait_for_decision(D, InitBy) of
+ {Tid, committed} ->
+ do_insert_rec(Tid, Rec, InPlace, InitBy, LogV);
+ {Tid, aborted} ->
+ mnesia_schema:undo_prepare_commit(Tid, Rec)
+ end;
+insert_rec(H, _InPlace, _InitBy, _LogV) when record(H, log_header) ->
+ CurrentVersion = mnesia_log:version(),
+ if
+ H#log_header.log_kind /= trans_log ->
+ exit({"Bad kind of transaction log", H});
+ H#log_header.log_version == CurrentVersion ->
+ ok;
+ H#log_header.log_version == "4.2" ->
+ ok;
+ H#log_header.log_version == "4.1" ->
+ ok;
+ H#log_header.log_version == "4.0" ->
+ ok;
+ true ->
+ fatal("Bad version of transaction log: ~p~n", [H])
+ end,
+ H;
+
+insert_rec(_Rec, _InPlace, _InitBy, _LogV) ->
+ ok.
+
+do_insert_rec(Tid, Rec, InPlace, InitBy, LogV) ->
+ case Rec#commit.schema_ops of
+ [] ->
+ ignore;
+ SchemaOps ->
+ case val({schema, storage_type}) of
+ ram_copies ->
+ insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, LogV);
+ Storage ->
+ true = open_files(schema, Storage, InPlace, InitBy),
+ insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy, LogV)
+ end
+ end,
+ D = Rec#commit.disc_copies,
+ insert_ops(Tid, disc_copies, D, InPlace, InitBy, LogV),
+ case InitBy of
+ startup ->
+ DO = Rec#commit.disc_only_copies,
+ insert_ops(Tid, disc_only_copies, DO, InPlace, InitBy, LogV);
+ _ ->
+ ignore
+ end.
+
+
+update(_Tid, [], _DumperMode) ->
+ dumped;
+update(Tid, SchemaOps, DumperMode) ->
+ UseDir = mnesia_monitor:use_dir(),
+ Res = perform_update(Tid, SchemaOps, DumperMode, UseDir),
+ mnesia_controller:release_schema_commit_lock(),
+ Res.
+
+perform_update(_Tid, _SchemaOps, mandatory, true) ->
+ %% Force a dump of the transaction log in order to let the
+ %% dumper perform needed updates
+
+ InitBy = schema_update,
+ ?eval_debug_fun({?MODULE, dump_schema_op}, [InitBy]),
+ opt_dump_log(InitBy);
+perform_update(Tid, SchemaOps, _DumperMode, _UseDir) ->
+ %% No need for a full transaction log dump.
+ %% Ignore the log file and perform only perform
+ %% the corresponding updates.
+
+ InitBy = fast_schema_update,
+ InPlace = mnesia_monitor:get_env(dump_log_update_in_place),
+ ?eval_debug_fun({?MODULE, dump_schema_op}, [InitBy]),
+ case catch insert_ops(Tid, schema_ops, SchemaOps, InPlace, InitBy,
+ mnesia_log:version()) of
+ {'EXIT', Reason} ->
+ Error = {error, {"Schema update error", Reason}},
+ close_files(InPlace, Error, InitBy),
+ fatal("Schema update error ~p ~p", [Reason, SchemaOps]);
+ _ ->
+ ?eval_debug_fun({?MODULE, post_dump}, [InitBy]),
+ close_files(InPlace, ok, InitBy),
+ ok
+ end.
+
+insert_ops(_Tid, _Storage, [], _InPlace, _InitBy, _) -> ok;
+insert_ops(Tid, Storage, [Op], InPlace, InitBy, Ver) when Ver >= "4.3"->
+ insert_op(Tid, Storage, Op, InPlace, InitBy),
+ ok;
+insert_ops(Tid, Storage, [Op | Ops], InPlace, InitBy, Ver) when Ver >= "4.3"->
+ insert_op(Tid, Storage, Op, InPlace, InitBy),
+ insert_ops(Tid, Storage, Ops, InPlace, InitBy, Ver);
+insert_ops(Tid, Storage, [Op | Ops], InPlace, InitBy, Ver) when Ver < "4.3" ->
+ insert_ops(Tid, Storage, Ops, InPlace, InitBy, Ver),
+ insert_op(Tid, Storage, Op, InPlace, InitBy).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Normal ops
+
+disc_insert(_Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->
+ case open_files(Tab, Storage, InPlace, InitBy) of
+ true ->
+ case Storage of
+ disc_copies when Tab /= schema ->
+ mnesia_log:append({?MODULE,Tab}, {{Tab, Key}, Val, Op}),
+ ok;
+ _ ->
+ case Op of
+ write ->
+ ok = dets:insert(Tab, Val);
+ delete ->
+ ok = dets:delete(Tab, Key);
+ update_counter ->
+ {RecName, Incr} = Val,
+ case catch dets:update_counter(Tab, Key, Incr) of
+ CounterVal when integer(CounterVal) ->
+ ok;
+ _ ->
+ Zero = {RecName, Key, 0},
+ ok = dets:insert(Tab, Zero)
+ end;
+ delete_object ->
+ ok = dets:delete_object(Tab, Val);
+ clear_table ->
+ ok = dets:match_delete(Tab, '_')
+ end
+ end;
+ false ->
+ ignore
+ end.
+
+insert(Tid, Storage, Tab, Key, [Val | Tail], Op, InPlace, InitBy) ->
+ insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy),
+ insert(Tid, Storage, Tab, Key, Tail, Op, InPlace, InitBy);
+
+insert(_Tid, _Storage, _Tab, _Key, [], _Op, _InPlace, _InitBy) ->
+ ok;
+
+insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy) ->
+ Item = {{Tab, Key}, Val, Op},
+ case InitBy of
+ startup ->
+ disc_insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy);
+
+ _ when Storage == ram_copies ->
+ mnesia_tm:do_update_op(Tid, Storage, Item),
+ Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
+ mnesia_tm:do_snmp(Tid, Snmp);
+
+ _ when Storage == disc_copies ->
+ disc_insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy),
+ mnesia_tm:do_update_op(Tid, Storage, Item),
+ Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
+ mnesia_tm:do_snmp(Tid, Snmp);
+
+ _ when Storage == disc_only_copies ->
+ mnesia_tm:do_update_op(Tid, Storage, Item),
+ Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
+ mnesia_tm:do_snmp(Tid, Snmp);
+
+ _ when Storage == unknown ->
+ ignore
+ end.
+
+disc_delete_table(Tab, Storage) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ if
+ Storage == disc_only_copies; Tab == schema ->
+ mnesia_monitor:unsafe_close_dets(Tab),
+ Dat = mnesia_lib:tab2dat(Tab),
+ file:delete(Dat);
+ true ->
+ DclFile = mnesia_lib:tab2dcl(Tab),
+ case get({?MODULE,Tab}) of
+ {opened_dumper, dcl} ->
+ del_opened_tab(Tab),
+ mnesia_log:unsafe_close_log(Tab);
+ _ ->
+ ok
+ end,
+ file:delete(DclFile),
+ DcdFile = mnesia_lib:tab2dcd(Tab),
+ file:delete(DcdFile),
+ ok
+ end,
+ erase({?MODULE, Tab});
+ false ->
+ ignore
+ end.
+
+disc_delete_indecies(_Tab, _Cs, Storage) when Storage /= disc_only_copies ->
+ ignore;
+disc_delete_indecies(Tab, Cs, disc_only_copies) ->
+ Indecies = Cs#cstruct.index,
+ mnesia_index:del_transient(Tab, Indecies, disc_only_copies).
+
+insert_op(Tid, Storage, {{Tab, Key}, Val, Op}, InPlace, InitBy) ->
+ %% Propagate to disc only
+ disc_insert(Tid, Storage, Tab, Key, Val, Op, InPlace, InitBy);
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% NOTE that all operations below will only
+%% be performed if the dump is initiated by
+%% startup or fast_schema_update
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+insert_op(_Tid, schema_ops, _OP, _InPlace, Initby)
+ when Initby /= startup,
+ Initby /= fast_schema_update,
+ Initby /= schema_update ->
+ ignore;
+
+insert_op(Tid, _, {op, rec, Storage, Item}, InPlace, InitBy) ->
+ {{Tab, Key}, ValList, Op} = Item,
+ insert(Tid, Storage, Tab, Key, ValList, Op, InPlace, InitBy);
+
+insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Val = mnesia_schema:insert_cstruct(Tid, Cs, true), % Update ram only
+ {schema, Tab, _} = Val,
+ if
+ InitBy /= startup ->
+ mnesia_controller:add_active_replica(Tab, N, Cs);
+ true ->
+ ignore
+ end,
+ if
+ N == node() ->
+ Dmp = mnesia_lib:tab2dmp(Tab),
+ Dat = mnesia_lib:tab2dat(Tab),
+ Dcd = mnesia_lib:tab2dcd(Tab),
+ Dcl = mnesia_lib:tab2dcl(Tab),
+ case {FromS, ToS} of
+ {ram_copies, disc_copies} when Tab == schema ->
+ ok = ensure_rename(Dmp, Dat);
+ {ram_copies, disc_copies} ->
+ file:delete(Dcl),
+ ok = ensure_rename(Dmp, Dcd);
+ {disc_copies, ram_copies} when Tab == schema ->
+ mnesia_lib:set(use_dir, false),
+ mnesia_monitor:unsafe_close_dets(Tab),
+ file:delete(Dat);
+ {disc_copies, ram_copies} ->
+ file:delete(Dcl),
+ file:delete(Dcd);
+ {ram_copies, disc_only_copies} ->
+ ok = ensure_rename(Dmp, Dat),
+ true = open_files(Tab, disc_only_copies, InPlace, InitBy),
+ %% ram_delete_table must be done before init_indecies,
+ %% it uses info which is reset in init_indecies,
+ %% it doesn't matter, because init_indecies don't use
+ %% the ram replica of the table when creating the disc
+ %% index; Could be improved :)
+ mnesia_schema:ram_delete_table(Tab, FromS),
+ PosList = Cs#cstruct.index,
+ mnesia_index:init_indecies(Tab, disc_only_copies, PosList);
+ {disc_only_copies, ram_copies} ->
+ mnesia_monitor:unsafe_close_dets(Tab),
+ disc_delete_indecies(Tab, Cs, disc_only_copies),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ mnesia_controller:get_disc_copy(Tab)
+ end,
+ disc_delete_table(Tab, disc_only_copies);
+ {disc_copies, disc_only_copies} ->
+ ok = ensure_rename(Dmp, Dat),
+ true = open_files(Tab, disc_only_copies, InPlace, InitBy),
+ mnesia_schema:ram_delete_table(Tab, FromS),
+ PosList = Cs#cstruct.index,
+ mnesia_index:init_indecies(Tab, disc_only_copies, PosList),
+ file:delete(Dcl),
+ file:delete(Dcd);
+ {disc_only_copies, disc_copies} ->
+ mnesia_monitor:unsafe_close_dets(Tab),
+ disc_delete_indecies(Tab, Cs, disc_only_copies),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ mnesia_log:ets2dcd(Tab),
+ mnesia_controller:get_disc_copy(Tab),
+ disc_delete_table(Tab, disc_only_copies)
+ end
+ end;
+ true ->
+ ignore
+ end,
+ S = val({schema, storage_type}),
+ disc_insert(Tid, S, schema, Tab, Val, write, InPlace, InitBy);
+
+insert_op(Tid, _, {op, transform, _Fun, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ disc_copies ->
+ open_dcl(Cs#cstruct.name);
+ _ ->
+ ignore
+ end,
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+%%% Operations below this are handled without using the logg.
+
+insert_op(Tid, _, {op, restore_recreate, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ Type = Cs#cstruct.type,
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ %% Delete all possbibly existing files and tables
+ disc_delete_table(Tab, Storage),
+ disc_delete_indecies(Tab, Cs, Storage),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ mnesia_schema:ram_delete_table(Tab, Storage),
+ mnesia_checkpoint:tm_del_copy(Tab, node())
+ end,
+ %% delete_cstruct(Tid, Cs, InPlace, InitBy),
+ %% And create new ones..
+ if
+ (InitBy == startup) or (Storage == unknown) ->
+ ignore;
+ Storage == ram_copies ->
+ Args = [{keypos, 2}, public, named_table, Type],
+ mnesia_monitor:mktab(Tab, Args);
+ Storage == disc_copies ->
+ Args = [{keypos, 2}, public, named_table, Type],
+ mnesia_monitor:mktab(Tab, Args),
+ File = mnesia_lib:tab2dcd(Tab),
+ FArg = [{file, File}, {name, {mnesia,create}},
+ {repair, false}, {mode, read_write}],
+ {ok, Log} = mnesia_monitor:open_log(FArg),
+ mnesia_monitor:unsafe_close_log(Log);
+ Storage == disc_only_copies ->
+ File = mnesia_lib:tab2dat(Tab),
+ file:delete(File),
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Type)},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)}],
+ mnesia_monitor:open_dets(Tab, Args)
+ end,
+ insert_op(Tid, ignore, {op, create_table, TabDef}, InPlace, InitBy);
+
+insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, false, InPlace, InitBy),
+ Tab = Cs#cstruct.name,
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ case InitBy of
+ startup ->
+ case Storage of
+ unknown ->
+ ignore;
+ ram_copies ->
+ ignore;
+ disc_copies ->
+ Dcd = mnesia_lib:tab2dcd(Tab),
+ case mnesia_lib:exists(Dcd) of
+ true -> ignore;
+ false ->
+ mnesia_log:open_log(temp,
+ mnesia_log:dcl_log_header(),
+ Dcd,
+ false,
+ false,
+ read_write),
+ mnesia_log:unsafe_close_log(temp)
+ end;
+ _ ->
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)}],
+ case mnesia_monitor:open_dets(Tab, Args) of
+ {ok, _} ->
+ mnesia_monitor:unsafe_close_dets(Tab);
+ {error, Error} ->
+ exit({"Failed to create dets table", Error})
+ end
+ end;
+ _ ->
+ Copies = mnesia_lib:copy_holders(Cs),
+ Active = mnesia_lib:intersect(Copies, val({current, db_nodes})),
+ [mnesia_controller:add_active_replica(Tab, N, Cs) || N <- Active],
+
+ case Storage of
+ unknown ->
+ case Cs#cstruct.local_content of
+ true ->
+ ignore;
+ false ->
+ mnesia_lib:set_remote_where_to_read(Tab)
+ end;
+ _ ->
+ case Cs#cstruct.local_content of
+ true ->
+ mnesia_lib:set_local_content_whereabouts(Tab);
+ false ->
+ mnesia_lib:set({Tab, where_to_read}, node())
+ end,
+ case Storage of
+ ram_copies ->
+ ignore;
+ _ ->
+ %% Indecies are still created by loader
+ disc_delete_indecies(Tab, Cs, Storage)
+ %% disc_delete_table(Tab, Storage)
+ end,
+
+ %% Update whereabouts and create table
+ mnesia_controller:create_table(Tab)
+ end
+ end;
+
+insert_op(_Tid, _, {op, dump_table, Size, TabDef}, _InPlace, _InitBy) ->
+ case Size of
+ unknown ->
+ ignore;
+ _ ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ Dmp = mnesia_lib:tab2dmp(Tab),
+ Dat = mnesia_lib:tab2dcd(Tab),
+ case Size of
+ 0 ->
+ %% Assume that table files already are closed
+ file:delete(Dmp),
+ file:delete(Dat);
+ _ ->
+ ok = ensure_rename(Dmp, Dat)
+ end
+ end;
+
+insert_op(Tid, _, {op, delete_table, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ ignore;
+ Storage ->
+ disc_delete_table(Tab, Storage),
+ disc_delete_indecies(Tab, Cs, Storage),
+ case InitBy of
+ startup ->
+ ignore;
+ _ ->
+ mnesia_schema:ram_delete_table(Tab, Storage),
+ mnesia_checkpoint:tm_del_copy(Tab, node())
+ end
+ end,
+ delete_cstruct(Tid, Cs, InPlace, InitBy);
+
+insert_op(Tid, _, {op, clear_table, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ ignore;
+ Storage ->
+ Oid = '_', %%val({Tab, wild_pattern}),
+ if Storage == disc_copies ->
+ open_dcl(Cs#cstruct.name);
+ true ->
+ ignore
+ end,
+ insert(Tid, Storage, Tab, '_', Oid, clear_table, InPlace, InitBy)
+ end;
+
+insert_op(Tid, _, {op, merge_schema, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, false, InPlace, InitBy);
+
+insert_op(Tid, _, {op, del_table_copy, Storage, Node, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ if
+ Tab == schema, Storage == ram_copies ->
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+ Tab /= schema ->
+ mnesia_controller:del_active_replica(Tab, Node),
+ mnesia_lib:del({Tab, Storage}, Node),
+ if
+ Node == node() ->
+ case Cs#cstruct.local_content of
+ true -> mnesia_lib:set({Tab, where_to_read}, nowhere);
+ false -> mnesia_lib:set_remote_where_to_read(Tab)
+ end,
+ mnesia_lib:del({schema, local_tables}, Tab),
+ mnesia_lib:set({Tab, storage_type}, unknown),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy),
+ disc_delete_table(Tab, Storage),
+ disc_delete_indecies(Tab, Cs, Storage),
+ mnesia_schema:ram_delete_table(Tab, Storage),
+ mnesia_checkpoint:tm_del_copy(Tab, Node);
+ true ->
+ case val({Tab, where_to_read}) of
+ Node ->
+ mnesia_lib:set_remote_where_to_read(Tab);
+ _ ->
+ ignore
+ end,
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy)
+ end
+ end;
+
+insert_op(Tid, _, {op, add_table_copy, _Storage, _Node, TabDef}, InPlace, InitBy) ->
+ %% During prepare commit, the files was created
+ %% and the replica was announced
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, add_snmp, _Us, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, del_snmp, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ if
+ InitBy /= startup,
+ Storage /= unknown ->
+ case ?catch_val({Tab, {index, snmp}}) of
+ {'EXIT', _} ->
+ ignore;
+ Stab ->
+ mnesia_snmp_hook:delete_table(Tab, Stab),
+ mnesia_lib:unset({Tab, {index, snmp}})
+ end;
+ true ->
+ ignore
+ end,
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, add_index, Pos, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = insert_cstruct(Tid, Cs, true, InPlace, InitBy),
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ case InitBy of
+ startup when Storage == disc_only_copies ->
+ mnesia_index:init_indecies(Tab, Storage, [Pos]);
+ startup ->
+ ignore;
+ _ ->
+ mnesia_index:init_indecies(Tab, Storage, [Pos])
+ end;
+
+insert_op(Tid, _, {op, del_index, Pos, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ case InitBy of
+ startup when Storage == disc_only_copies ->
+ mnesia_index:del_index_table(Tab, Storage, Pos);
+ startup ->
+ ignore;
+ _ ->
+ mnesia_index:del_index_table(Tab, Storage, Pos)
+ end,
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, change_table_access_mode,TabDef, _OldAccess, _Access}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ case InitBy of
+ startup -> ignore;
+ _ -> mnesia_controller:change_table_access_mode(Cs)
+ end,
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, change_table_load_order, TabDef, _OldLevel, _Level}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, delete_property, TabDef, PropKey}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ mnesia_lib:unset({Tab, user_property, PropKey}),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, write_property, TabDef, _Prop}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+insert_op(Tid, _, {op, change_table_frag, _Change, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy).
+
+open_files(Tab, Storage, UpdateInPlace, InitBy)
+ when Storage /= unknown, Storage /= ram_copies ->
+ case get({?MODULE, Tab}) of
+ undefined ->
+ case ?catch_val({Tab, setorbag}) of
+ {'EXIT', _} ->
+ false;
+ Type ->
+ case Storage of
+ disc_copies when Tab /= schema ->
+ Bool = open_disc_copies(Tab, InitBy),
+ Bool;
+ _ ->
+ Fname = prepare_open(Tab, UpdateInPlace),
+ Args = [{file, Fname},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)},
+ {type, mnesia_lib:disk_type(Tab, Type)}],
+ {ok, _} = mnesia_monitor:open_dets(Tab, Args),
+ put({?MODULE, Tab}, {opened_dumper, dat}),
+ true
+ end
+ end;
+ already_dumped ->
+ false;
+ {opened_dumper, _} ->
+ true
+ end;
+open_files(_Tab, _Storage, _UpdateInPlace, _InitBy) ->
+ false.
+
+open_disc_copies(Tab, InitBy) ->
+ DclF = mnesia_lib:tab2dcl(Tab),
+ DumpEts =
+ case file:read_file_info(DclF) of
+ {error, enoent} ->
+ false;
+ {ok, DclInfo} ->
+ DcdF = mnesia_lib:tab2dcd(Tab),
+ case file:read_file_info(DcdF) of
+ {error, Reason} ->
+ mnesia_lib:dbg_out("File ~p info_error ~p ~n",
+ [DcdF, Reason]),
+ true;
+ {ok, DcdInfo} ->
+ DcdInfo#file_info.size =<
+ (DclInfo#file_info.size *
+ ?DumpToEtsMultiplier)
+ end
+ end,
+ if
+ DumpEts == false; InitBy == startup ->
+ mnesia_log:open_log({?MODULE,Tab},
+ mnesia_log:dcl_log_header(),
+ DclF,
+ mnesia_lib:exists(DclF),
+ mnesia_monitor:get_env(auto_repair),
+ read_write),
+ put({?MODULE, Tab}, {opened_dumper, dcl}),
+ true;
+ true ->
+ mnesia_log:ets2dcd(Tab),
+ put({?MODULE, Tab}, already_dumped),
+ false
+ end.
+
+%% Always opens the dcl file for writing overriding already_dumped
+%% mechanismen, used for schema transactions.
+open_dcl(Tab) ->
+ case get({?MODULE, Tab}) of
+ {opened_dumper, _} ->
+ true;
+ _ -> %% undefined or already_dumped
+ DclF = mnesia_lib:tab2dcl(Tab),
+ mnesia_log:open_log({?MODULE,Tab},
+ mnesia_log:dcl_log_header(),
+ DclF,
+ mnesia_lib:exists(DclF),
+ mnesia_monitor:get_env(auto_repair),
+ read_write),
+ put({?MODULE, Tab}, {opened_dumper, dcl}),
+ true
+ end.
+
+prepare_open(Tab, UpdateInPlace) ->
+ Dat = mnesia_lib:tab2dat(Tab),
+ case UpdateInPlace of
+ true ->
+ Dat;
+ false ->
+ Tmp = mnesia_lib:tab2tmp(Tab),
+ case catch mnesia_lib:copy_file(Dat, Tmp) of
+ ok ->
+ Tmp;
+ Error ->
+ fatal("Cannot copy dets file ~p to ~p: ~p~n",
+ [Dat, Tmp, Error])
+ end
+ end.
+
+del_opened_tab(Tab) ->
+ erase({?MODULE, Tab}).
+
+close_files(UpdateInPlace, Outcome, InitBy) -> % Update in place
+ close_files(UpdateInPlace, Outcome, InitBy, get()).
+
+close_files(InPlace, Outcome, InitBy, [{{?MODULE, Tab}, already_dumped} | Tail]) ->
+ erase({?MODULE, Tab}),
+ close_files(InPlace, Outcome, InitBy, Tail);
+close_files(InPlace, Outcome, InitBy, [{{?MODULE, Tab}, {opened_dumper, Type}} | Tail]) ->
+ erase({?MODULE, Tab}),
+ case val({Tab, storage_type}) of
+ disc_only_copies when InitBy /= startup ->
+ ignore;
+ disc_copies when Tab /= schema ->
+ mnesia_log:close_log({?MODULE,Tab});
+ Storage ->
+ do_close(InPlace, Outcome, Tab, Type, Storage)
+ end,
+ close_files(InPlace, Outcome, InitBy, Tail);
+
+close_files(InPlace, Outcome, InitBy, [_ | Tail]) ->
+ close_files(InPlace, Outcome, InitBy, Tail);
+close_files(_, _, _InitBy, []) ->
+ ok.
+
+%% If storage is unknown during close clean up files, this can happen if timing
+%% is right and dirty_write conflicts with schema operations.
+do_close(_, _, Tab, dcl, unknown) ->
+ mnesia_log:close_log({?MODULE,Tab}),
+ file:delete(mnesia_lib:tab2dcl(Tab));
+do_close(_, _, Tab, dcl, _) -> %% To be safe, can it happen?
+ mnesia_log:close_log({?MODULE,Tab});
+
+do_close(InPlace, Outcome, Tab, dat, Storage) ->
+ mnesia_monitor:close_dets(Tab),
+ if
+ Storage == unknown, InPlace == true ->
+ file:delete(mnesia_lib:tab2dat(Tab));
+ InPlace == true ->
+ %% Update in place
+ ok;
+ Outcome == ok, Storage /= unknown ->
+ %% Success: swap tmp files with dat files
+ TabDat = mnesia_lib:tab2dat(Tab),
+ ok = file:rename(mnesia_lib:tab2tmp(Tab), TabDat);
+ true ->
+ file:delete(mnesia_lib:tab2tmp(Tab))
+ end.
+
+
+ensure_rename(From, To) ->
+ case mnesia_lib:exists(From) of
+ true ->
+ file:rename(From, To);
+ false ->
+ case mnesia_lib:exists(To) of
+ true ->
+ ok;
+ false ->
+ {error, {rename_failed, From, To}}
+ end
+ end.
+
+insert_cstruct(Tid, Cs, KeepWhereabouts, InPlace, InitBy) ->
+ Val = mnesia_schema:insert_cstruct(Tid, Cs, KeepWhereabouts),
+ {schema, Tab, _} = Val,
+ S = val({schema, storage_type}),
+ disc_insert(Tid, S, schema, Tab, Val, write, InPlace, InitBy),
+ Tab.
+
+delete_cstruct(Tid, Cs, InPlace, InitBy) ->
+ Val = mnesia_schema:delete_cstruct(Tid, Cs),
+ {schema, Tab, _} = Val,
+ S = val({schema, storage_type}),
+ disc_insert(Tid, S, schema, Tab, Val, delete, InPlace, InitBy),
+ Tab.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Raw dump of table. Dumper must have unique access to the ets table.
+
+raw_named_dump_table(Tab, Ftype) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_lib:lock_table(Tab),
+ TmpFname = mnesia_lib:tab2tmp(Tab),
+ Fname =
+ case Ftype of
+ dat -> mnesia_lib:tab2dat(Tab);
+ dmp -> mnesia_lib:tab2dmp(Tab)
+ end,
+ file:delete(TmpFname),
+ file:delete(Fname),
+ TabSize = ?ets_info(Tab, size),
+ TabRef = Tab,
+ DiskType = mnesia_lib:disk_type(Tab),
+ Args = [{file, TmpFname},
+ {keypos, 2},
+ %% {ram_file, true},
+ {estimated_no_objects, TabSize + 256},
+ {repair, mnesia_monitor:get_env(auto_repair)},
+ {type, DiskType}],
+ case mnesia_lib:dets_sync_open(TabRef, Args) of
+ {ok, TabRef} ->
+ Storage = ram_copies,
+ mnesia_lib:db_fixtable(Storage, Tab, true),
+
+ case catch raw_dump_table(TabRef, Tab) of
+ {'EXIT', Reason} ->
+ mnesia_lib:db_fixtable(Storage, Tab, false),
+ mnesia_lib:dets_sync_close(Tab),
+ file:delete(TmpFname),
+ mnesia_lib:unlock_table(Tab),
+ exit({"Dump of table to disc failed", Reason});
+ ok ->
+ mnesia_lib:db_fixtable(Storage, Tab, false),
+ mnesia_lib:dets_sync_close(Tab),
+ mnesia_lib:unlock_table(Tab),
+ ok = file:rename(TmpFname, Fname)
+ end;
+ {error, Reason} ->
+ mnesia_lib:unlock_table(Tab),
+ exit({"Open of file before dump to disc failed", Reason})
+ end;
+ false ->
+ exit({has_no_disc, node()})
+ end.
+
+raw_dump_table(DetsRef, EtsRef) ->
+ dets:from_ets(DetsRef, EtsRef).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Load regulator
+%%
+%% This is a poor mans substitute for a fair scheduler algorithm
+%% in the Erlang emulator. The mnesia_dumper process performs many
+%% costly BIF invokations and must pay for this. But since the
+%% Emulator does not handle this properly we must compensate for
+%% this with some form of load regulation of ourselves in order to
+%% not steal all computation power in the Erlang Emulator ans make
+%% other processes starve. Hopefully this is a temporary solution.
+
+start_regulator() ->
+ case mnesia_monitor:get_env(dump_log_load_regulation) of
+ false ->
+ nopid;
+ true ->
+ N = ?REGULATOR_NAME,
+ case mnesia_monitor:start_proc(N, ?MODULE, regulator_init, [self()]) of
+ {ok, Pid} ->
+ Pid;
+ {error, Reason} ->
+ fatal("Failed to start ~n: ~p~n", [N, Reason])
+ end
+ end.
+
+regulator_init(Parent) ->
+ %% No need for trapping exits.
+ %% Using low priority causes the regulation
+ process_flag(priority, low),
+ register(?REGULATOR_NAME, self()),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ regulator_loop().
+
+regulator_loop() ->
+ receive
+ {regulate, From} ->
+ From ! {regulated, self()},
+ regulator_loop();
+ {stop, From} ->
+ From ! {stopped, self()},
+ exit(normal)
+ end.
+
+regulate(nopid) ->
+ ok;
+regulate(RegulatorPid) ->
+ RegulatorPid ! {regulate, self()},
+ receive
+ {regulated, RegulatorPid} -> ok
+ end.
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_event.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_event.erl
new file mode 100644
index 0000000000..fc0638e1ad
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_event.erl
@@ -0,0 +1,263 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_event.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_event).
+
+-behaviour(gen_event).
+%-behaviour(mnesia_event).
+
+%% gen_event callback interface
+-export([init/1,
+ handle_event/2,
+ handle_call/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+-record(state, {nodes = [],
+ dumped_core = false, %% only dump fatal core once
+ args}).
+
+%%%----------------------------------------------------------------
+%%% Callback functions from gen_server
+%%%----------------------------------------------------------------
+
+%%-----------------------------------------------------------------
+%% init(Args) ->
+%% {ok, State} | Error
+%%-----------------------------------------------------------------
+
+init(Args) ->
+ {ok, #state{args = Args}}.
+
+%%-----------------------------------------------------------------
+%% handle_event(Event, State) ->
+%% {ok, NewState} | remove_handler |
+%% {swap_handler, Args1, State1, Mod2, Args2}
+%%-----------------------------------------------------------------
+
+handle_event(Event, State) ->
+ handle_any_event(Event, State).
+
+%%-----------------------------------------------------------------
+%% handle_info(Msg, State) ->
+%% {ok, NewState} | remove_handler |
+%% {swap_handler, Args1, State1, Mod2, Args2}
+%%-----------------------------------------------------------------
+
+handle_info(Msg, State) ->
+ handle_any_event(Msg, State),
+ {ok, State}.
+
+%%-----------------------------------------------------------------
+%% handle_call(Event, State) ->
+%% {ok, Reply, NewState} | {remove_handler, Reply} |
+%% {swap_handler, Reply, Args1, State1, Mod2, Args2}
+%%-----------------------------------------------------------------
+
+handle_call(Msg, State) ->
+ Reply = ok,
+ case handle_any_event(Msg, State) of
+ {ok, NewState} ->
+ {ok, Reply, NewState};
+ remove_handler ->
+ {remove_handler, Reply};
+ {swap_handler,Args1, State1, Mod2, Args2} ->
+ {swap_handler, Reply, Args1, State1, Mod2, Args2}
+ end.
+
+%%-----------------------------------------------------------------
+%% terminate(Reason, State) ->
+%% AnyVal
+%%-----------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%-----------------------------------------------------------------
+%% Internal functions
+%%-----------------------------------------------------------------
+
+handle_any_event({mnesia_system_event, Event}, State) ->
+ handle_system_event(Event, State);
+handle_any_event({mnesia_table_event, Event}, State) ->
+ handle_table_event(Event, State);
+handle_any_event(Msg, State) ->
+ report_error("~p got unexpected event: ~p~n", [?MODULE, Msg]),
+ {ok, State}.
+
+handle_table_event({Oper, Record, TransId}, State) ->
+ report_info("~p performed by ~p on record:~n\t~p~n",
+ [Oper, TransId, Record]),
+ {ok, State}.
+
+handle_system_event({mnesia_checkpoint_activated, _Checkpoint}, State) ->
+ {ok, State};
+
+handle_system_event({mnesia_checkpoint_deactivated, _Checkpoint}, State) ->
+ {ok, State};
+
+handle_system_event({mnesia_up, Node}, State) ->
+ Nodes = [Node | State#state.nodes],
+ {ok, State#state{nodes = Nodes}};
+
+handle_system_event({mnesia_down, Node}, State) ->
+ case mnesia:system_info(fallback_activated) of
+ true ->
+ case mnesia_monitor:get_env(fallback_error_function) of
+ {mnesia, lkill} ->
+ Msg = "A fallback is installed and Mnesia "
+ "must be restarted. Forcing shutdown "
+ "after mnesia_down from ~p...~n",
+ report_fatal(Msg, [Node], nocore, State#state.dumped_core),
+ mnesia:lkill(),
+ exit(fatal);
+ {UserMod, UserFunc} ->
+ Msg = "Warning: A fallback is installed and Mnesia got mnesia_down "
+ "from ~p. ~n",
+ report_info(Msg, [Node]),
+ case catch apply(UserMod, UserFunc, [Node]) of
+ {'EXIT', {undef, _Reason}} ->
+ %% Backward compatibility
+ apply(UserMod, UserFunc, []);
+ {'EXIT', Reason} ->
+ exit(Reason);
+ _ ->
+ ok
+ end,
+ Nodes = lists:delete(Node, State#state.nodes),
+ {ok, State#state{nodes = Nodes}}
+ end;
+ false ->
+ Nodes = lists:delete(Node, State#state.nodes),
+ {ok, State#state{nodes = Nodes}}
+ end;
+
+handle_system_event({mnesia_overload, Details}, State) ->
+ report_warning("Mnesia is overloaded: ~p~n", [Details]),
+ {ok, State};
+
+handle_system_event({mnesia_info, Format, Args}, State) ->
+ report_info(Format, Args),
+ {ok, State};
+
+handle_system_event({mnesia_warning, Format, Args}, State) ->
+ report_warning(Format, Args),
+ {ok, State};
+
+handle_system_event({mnesia_error, Format, Args}, State) ->
+ report_error(Format, Args),
+ {ok, State};
+
+handle_system_event({mnesia_fatal, Format, Args, BinaryCore}, State) ->
+ report_fatal(Format, Args, BinaryCore, State#state.dumped_core),
+ {ok, State#state{dumped_core = true}};
+
+handle_system_event({inconsistent_database, Reason, Node}, State) ->
+ report_error("mnesia_event got {inconsistent_database, ~w, ~w}~n",
+ [Reason, Node]),
+ {ok, State};
+
+handle_system_event({mnesia_user, Event}, State) ->
+ report_info("User event: ~p~n", [Event]),
+ {ok, State};
+
+handle_system_event(Msg, State) ->
+ report_error("mnesia_event got unexpected system event: ~p~n", [Msg]),
+ {ok, State}.
+
+report_info(Format0, Args0) ->
+ Format = "Mnesia(~p): " ++ Format0,
+ Args = [node() | Args0],
+ case global:whereis_name(mnesia_global_logger) of
+ undefined ->
+ io:format(Format, Args);
+ Pid ->
+ io:format(Pid, Format, Args)
+ end.
+
+report_warning(Format0, Args0) ->
+ Format = "Mnesia(~p): ** WARNING ** " ++ Format0,
+ Args = [node() | Args0],
+ case erlang:function_exported(error_logger, warning_msg, 2) of
+ true ->
+ error_logger:warning_msg(Format, Args);
+ false ->
+ error_logger:format(Format, Args)
+ end,
+ case global:whereis_name(mnesia_global_logger) of
+ undefined ->
+ ok;
+ Pid ->
+ io:format(Pid, Format, Args)
+ end.
+
+report_error(Format0, Args0) ->
+ Format = "Mnesia(~p): ** ERROR ** " ++ Format0,
+ Args = [node() | Args0],
+ error_logger:format(Format, Args),
+ case global:whereis_name(mnesia_global_logger) of
+ undefined ->
+ ok;
+ Pid ->
+ io:format(Pid, Format, Args)
+ end.
+
+report_fatal(Format, Args, BinaryCore, CoreDumped) ->
+ UseDir = mnesia_monitor:use_dir(),
+ CoreDir = mnesia_monitor:get_env(core_dir),
+ if
+ list(CoreDir),CoreDumped == false,binary(BinaryCore) ->
+ core_file(CoreDir,BinaryCore,Format,Args);
+ (UseDir == true),CoreDumped == false,binary(BinaryCore) ->
+ core_file(CoreDir,BinaryCore,Format,Args);
+ true ->
+ report_error("(ignoring core) ** FATAL ** " ++ Format, Args)
+ end.
+
+core_file(CoreDir,BinaryCore,Format,Args) ->
+ %% Integers = tuple_to_list(date()) ++ tuple_to_list(time()),
+ Integers = tuple_to_list(now()),
+ Fun = fun(I) when I < 10 -> ["_0",I];
+ (I) -> ["_",I]
+ end,
+ List = lists:append([Fun(I) || I <- Integers]),
+ CoreFile = if list(CoreDir) ->
+ filename:absname(lists:concat(["MnesiaCore.", node()] ++ List),
+ CoreDir);
+ true ->
+ filename:absname(lists:concat(["MnesiaCore.", node()] ++ List))
+ end,
+ case file:write_file(CoreFile, BinaryCore) of
+ ok ->
+ report_error("(core dumped to file: ~p)~n ** FATAL ** " ++ Format,
+ [CoreFile] ++ Args);
+ {error, Reason} ->
+ report_error("(could not write core file: ~p)~n ** FATAL ** " ++ Format,
+ [Reason] ++ Args)
+ end.
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag.erl
new file mode 100644
index 0000000000..e1f4e96a95
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag.erl
@@ -0,0 +1,1201 @@
+%%% ``The contents of this file are subject to the Erlang Public License,
+%%% Version 1.1, (the "License"); you may not use this file except in
+%%% compliance with the License. You should have received a copy of the
+%%% Erlang Public License along with this software. If not, it can be
+%%% retrieved via the world wide web at http://www.erlang.org/.
+%%%
+%%% Software distributed under the License is distributed on an "AS IS"
+%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%%% the License for the specific language governing rights and limitations
+%%% under the License.
+%%%
+%%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%%% AB. All Rights Reserved.''
+%%%
+%%% $Id: mnesia_frag.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%%
+%%%----------------------------------------------------------------------
+%%% Purpose : Support tables so large that they need
+%%% to be divided into several fragments.
+%%%----------------------------------------------------------------------
+
+%header_doc_include
+
+-module(mnesia_frag).
+-behaviour(mnesia_access).
+
+%% Callback functions when accessed within an activity
+-export([
+ lock/4,
+ write/5, delete/5, delete_object/5,
+ read/5, match_object/5, all_keys/4,
+ select/5,
+ index_match_object/6, index_read/6,
+ foldl/6, foldr/6,
+ table_info/4
+ ]).
+
+%header_doc_include
+
+-export([
+ change_table_frag/2,
+ remove_node/2,
+ expand_cstruct/1,
+ lookup_frag_hash/1,
+ lookup_foreigners/1,
+ frag_names/1,
+ set_frag_hash/2,
+ local_select/4,
+ remote_select/4
+ ]).
+
+-include("mnesia.hrl").
+
+-define(OLD_HASH_MOD, mnesia_frag_old_hash).
+-define(DEFAULT_HASH_MOD, mnesia_frag_hash).
+%%-define(DEFAULT_HASH_MOD, ?OLD_HASH_MOD). %% BUGBUG: New should be default
+
+-record(frag_state,
+ {foreign_key,
+ n_fragments,
+ hash_module,
+ hash_state}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Access functions
+
+%impl_doc_include
+
+%% Callback functions which provides transparent
+%% access of fragmented tables from any activity
+%% access context.
+
+lock(ActivityId, Opaque, {table , Tab}, LockKind) ->
+ case frag_names(Tab) of
+ [Tab] ->
+ mnesia:lock(ActivityId, Opaque, {table, Tab}, LockKind);
+ Frags ->
+ DeepNs = [mnesia:lock(ActivityId, Opaque, {table, F}, LockKind) ||
+ F <- Frags],
+ mnesia_lib:uniq(lists:append(DeepNs))
+ end;
+
+lock(ActivityId, Opaque, LockItem, LockKind) ->
+ mnesia:lock(ActivityId, Opaque, LockItem, LockKind).
+
+write(ActivityId, Opaque, Tab, Rec, LockKind) ->
+ Frag = record_to_frag_name(Tab, Rec),
+ mnesia:write(ActivityId, Opaque, Frag, Rec, LockKind).
+
+delete(ActivityId, Opaque, Tab, Key, LockKind) ->
+ Frag = key_to_frag_name(Tab, Key),
+ mnesia:delete(ActivityId, Opaque, Frag, Key, LockKind).
+
+delete_object(ActivityId, Opaque, Tab, Rec, LockKind) ->
+ Frag = record_to_frag_name(Tab, Rec),
+ mnesia:delete_object(ActivityId, Opaque, Frag, Rec, LockKind).
+
+read(ActivityId, Opaque, Tab, Key, LockKind) ->
+ Frag = key_to_frag_name(Tab, Key),
+ mnesia:read(ActivityId, Opaque, Frag, Key, LockKind).
+
+match_object(ActivityId, Opaque, Tab, HeadPat, LockKind) ->
+ MatchSpec = [{HeadPat, [], ['$_']}],
+ select(ActivityId, Opaque, Tab, MatchSpec, LockKind).
+
+select(ActivityId, Opaque, Tab, MatchSpec, LockKind) ->
+ do_select(ActivityId, Opaque, Tab, MatchSpec, LockKind).
+
+all_keys(ActivityId, Opaque, Tab, LockKind) ->
+ Match = [mnesia:all_keys(ActivityId, Opaque, Frag, LockKind)
+ || Frag <- frag_names(Tab)],
+ lists:append(Match).
+
+index_match_object(ActivityId, Opaque, Tab, Pat, Attr, LockKind) ->
+ Match =
+ [mnesia:index_match_object(ActivityId, Opaque, Frag, Pat, Attr, LockKind)
+ || Frag <- frag_names(Tab)],
+ lists:append(Match).
+
+index_read(ActivityId, Opaque, Tab, Key, Attr, LockKind) ->
+ Match =
+ [mnesia:index_read(ActivityId, Opaque, Frag, Key, Attr, LockKind)
+ || Frag <- frag_names(Tab)],
+ lists:append(Match).
+
+foldl(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
+ Fun2 = fun(Frag, A) ->
+ mnesia:foldl(ActivityId, Opaque, Fun, A, Frag, LockKind)
+ end,
+ lists:foldl(Fun2, Acc, frag_names(Tab)).
+
+foldr(ActivityId, Opaque, Fun, Acc, Tab, LockKind) ->
+ Fun2 = fun(Frag, A) ->
+ mnesia:foldr(ActivityId, Opaque, Fun, A, Frag, LockKind)
+ end,
+ lists:foldr(Fun2, Acc, frag_names(Tab)).
+
+table_info(ActivityId, Opaque, {Tab, Key}, Item) ->
+ Frag = key_to_frag_name(Tab, Key),
+ table_info2(ActivityId, Opaque, Tab, Frag, Item);
+table_info(ActivityId, Opaque, Tab, Item) ->
+ table_info2(ActivityId, Opaque, Tab, Tab, Item).
+
+table_info2(ActivityId, Opaque, Tab, Frag, Item) ->
+ case Item of
+ size ->
+ SumFun = fun({_, Size}, Acc) -> Acc + Size end,
+ lists:foldl(SumFun, 0, frag_size(ActivityId, Opaque, Tab));
+ memory ->
+ SumFun = fun({_, Size}, Acc) -> Acc + Size end,
+ lists:foldl(SumFun, 0, frag_memory(ActivityId, Opaque, Tab));
+ base_table ->
+ lookup_prop(Tab, base_table);
+ node_pool ->
+ lookup_prop(Tab, node_pool);
+ n_fragments ->
+ FH = lookup_frag_hash(Tab),
+ FH#frag_state.n_fragments;
+ foreign_key ->
+ FH = lookup_frag_hash(Tab),
+ FH#frag_state.foreign_key;
+ foreigners ->
+ lookup_foreigners(Tab);
+ n_ram_copies ->
+ length(val({Tab, ram_copies}));
+ n_disc_copies ->
+ length(val({Tab, disc_copies}));
+ n_disc_only_copies ->
+ length(val({Tab, disc_only_copies}));
+
+ frag_names ->
+ frag_names(Tab);
+ frag_dist ->
+ frag_dist(Tab);
+ frag_size ->
+ frag_size(ActivityId, Opaque, Tab);
+ frag_memory ->
+ frag_memory(ActivityId, Opaque, Tab);
+ _ ->
+ mnesia:table_info(ActivityId, Opaque, Frag, Item)
+ end.
+%impl_doc_include
+
+frag_size(ActivityId, Opaque, Tab) ->
+ [{F, remote_table_info(ActivityId, Opaque, F, size)} || F <- frag_names(Tab)].
+
+frag_memory(ActivityId, Opaque, Tab) ->
+ [{F, remote_table_info(ActivityId, Opaque, F, memory)} || F <- frag_names(Tab)].
+
+
+
+remote_table_info(ActivityId, Opaque, Tab, Item) ->
+ N = val({Tab, where_to_read}),
+ case rpc:call(N, mnesia, table_info, [ActivityId, Opaque, Tab, Item]) of
+ {badrpc, _} ->
+ mnesia:abort({no_exists, Tab, Item});
+ Info ->
+ Info
+ end.
+
+do_select(ActivityId, Opaque, Tab, MatchSpec, LockKind) ->
+ case ?catch_val({Tab, frag_hash}) of
+ {'EXIT', _} ->
+ mnesia:select(ActivityId, Opaque, Tab, MatchSpec, LockKind);
+ FH ->
+ HashState = FH#frag_state.hash_state,
+ FragNumbers =
+ case FH#frag_state.hash_module of
+ HashMod when HashMod == ?DEFAULT_HASH_MOD ->
+ ?DEFAULT_HASH_MOD:match_spec_to_frag_numbers(HashState, MatchSpec);
+ HashMod ->
+ HashMod:match_spec_to_frag_numbers(HashState, MatchSpec)
+ end,
+ N = FH#frag_state.n_fragments,
+ VerifyFun = fun(F) when integer(F), F >= 1, F =< N -> false;
+ (_F) -> true
+ end,
+ case catch lists:filter(VerifyFun, FragNumbers) of
+ [] ->
+ Fun = fun(Num) ->
+ Name = n_to_frag_name(Tab, Num),
+ Node = val({Name, where_to_read}),
+ mnesia:lock(ActivityId, Opaque, {table, Name}, LockKind),
+ {Name, Node}
+ end,
+ NameNodes = lists:map(Fun, FragNumbers),
+ SelectAllFun =
+ fun(PatchedMatchSpec) ->
+ Match = [mnesia:dirty_select(Name, PatchedMatchSpec)
+ || {Name, _Node} <- NameNodes],
+ lists:append(Match)
+ end,
+ case [{Name, Node} || {Name, Node} <- NameNodes, Node /= node()] of
+ [] ->
+ %% All fragments are local
+ mnesia:fun_select(ActivityId, Opaque, Tab, MatchSpec, none, '_', SelectAllFun);
+ RemoteNameNodes ->
+ SelectFun =
+ fun(PatchedMatchSpec) ->
+ Ref = make_ref(),
+ Args = [self(), Ref, RemoteNameNodes, PatchedMatchSpec],
+ Pid = spawn_link(?MODULE, local_select, Args),
+ LocalMatch = [mnesia:dirty_select(Name, PatchedMatchSpec)
+ || {Name, Node} <- NameNodes, Node == node()],
+ OldSelectFun = fun() -> SelectAllFun(PatchedMatchSpec) end,
+ local_collect(Ref, Pid, lists:append(LocalMatch), OldSelectFun)
+ end,
+ mnesia:fun_select(ActivityId, Opaque, Tab, MatchSpec, none, '_', SelectFun)
+ end;
+ BadFrags ->
+ mnesia:abort({"match_spec_to_frag_numbers: Fragment numbers out of range",
+ BadFrags, {range, 1, N}})
+ end
+ end.
+
+local_select(ReplyTo, Ref, RemoteNameNodes, MatchSpec) ->
+ RemoteNodes = mnesia_lib:uniq([Node || {_Name, Node} <- RemoteNameNodes]),
+ Args = [ReplyTo, Ref, RemoteNameNodes, MatchSpec],
+ {Replies, BadNodes} = rpc:multicall(RemoteNodes, ?MODULE, remote_select, Args),
+ case mnesia_lib:uniq(Replies) -- [ok] of
+ [] when BadNodes == [] ->
+ ReplyTo ! {local_select, Ref, ok};
+ _ when BadNodes /= [] ->
+ ReplyTo ! {local_select, Ref, {error, {node_not_running, hd(BadNodes)}}};
+ [{badrpc, {'EXIT', Reason}} | _] ->
+ ReplyTo ! {local_select, Ref, {error, Reason}};
+ [Reason | _] ->
+ ReplyTo ! {local_select, Ref, {error, Reason}}
+ end,
+ unlink(ReplyTo),
+ exit(normal).
+
+remote_select(ReplyTo, Ref, NameNodes, MatchSpec) ->
+ do_remote_select(ReplyTo, Ref, NameNodes, MatchSpec).
+
+do_remote_select(ReplyTo, Ref, [{Name, Node} | NameNodes], MatchSpec) ->
+ if
+ Node == node() ->
+ Res = (catch {ok, mnesia:dirty_select(Name, MatchSpec)}),
+ ReplyTo ! {remote_select, Ref, Node, Res},
+ do_remote_select(ReplyTo, Ref, NameNodes, MatchSpec);
+ true ->
+ do_remote_select(ReplyTo, Ref, NameNodes, MatchSpec)
+ end;
+do_remote_select(_ReplyTo, _Ref, [], _MatchSpec) ->
+ ok.
+
+local_collect(Ref, Pid, LocalMatch, OldSelectFun) ->
+ receive
+ {local_select, Ref, LocalRes} ->
+ remote_collect(Ref, LocalRes, LocalMatch, OldSelectFun);
+ {'EXIT', Pid, Reason} ->
+ remote_collect(Ref, {error, Reason}, [], OldSelectFun)
+ end.
+
+remote_collect(Ref, LocalRes = ok, Acc, OldSelectFun) ->
+ receive
+ {remote_select, Ref, Node, RemoteRes} ->
+ case RemoteRes of
+ {ok, RemoteMatch} ->
+ remote_collect(Ref, LocalRes, RemoteMatch ++ Acc, OldSelectFun);
+ _ ->
+ remote_collect(Ref, {error, {node_not_running, Node}}, [], OldSelectFun)
+ end
+ after 0 ->
+ Acc
+ end;
+remote_collect(Ref, LocalRes = {error, Reason}, _Acc, OldSelectFun) ->
+ receive
+ {remote_select, Ref, _Node, _RemoteRes} ->
+ remote_collect(Ref, LocalRes, [], OldSelectFun)
+ after 0 ->
+ mnesia:abort(Reason)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Returns a list of cstructs
+
+expand_cstruct(Cs) ->
+ expand_cstruct(Cs, create).
+
+expand_cstruct(Cs, Mode) ->
+ Tab = Cs#cstruct.name,
+ Props = Cs#cstruct.frag_properties,
+ mnesia_schema:verify({alt, [nil, list]}, mnesia_lib:etype(Props),
+ {badarg, Tab, Props}),
+ %% Verify keys
+ ValidKeys = [foreign_key, n_fragments, node_pool,
+ n_ram_copies, n_disc_copies, n_disc_only_copies,
+ hash_module, hash_state],
+ Keys = mnesia_schema:check_keys(Tab, Props, ValidKeys),
+ mnesia_schema:check_duplicates(Tab, Keys),
+
+ %% Pick fragmentation props
+ ForeignKey = mnesia_schema:pick(Tab, foreign_key, Props, undefined),
+ {ForeignKey2, N, Pool, DefaultNR, DefaultND, DefaultNDO} =
+ pick_props(Tab, Cs, ForeignKey),
+
+ %% Verify node_pool
+ BadPool = {bad_type, Tab, {node_pool, Pool}},
+ mnesia_schema:verify(list, mnesia_lib:etype(Pool), BadPool),
+ NotAtom = fun(A) when atom(A) -> false;
+ (_A) -> true
+ end,
+ mnesia_schema:verify([], [P || P <- Pool, NotAtom(P)], BadPool),
+
+ NR = mnesia_schema:pick(Tab, n_ram_copies, Props, 0),
+ ND = mnesia_schema:pick(Tab, n_disc_copies, Props, 0),
+ NDO = mnesia_schema:pick(Tab, n_disc_only_copies, Props, 0),
+
+ PosInt = fun(I) when integer(I), I >= 0 -> true;
+ (_I) -> false
+ end,
+ mnesia_schema:verify(true, PosInt(NR),
+ {bad_type, Tab, {n_ram_copies, NR}}),
+ mnesia_schema:verify(true, PosInt(ND),
+ {bad_type, Tab, {n_disc_copies, ND}}),
+ mnesia_schema:verify(true, PosInt(NDO),
+ {bad_type, Tab, {n_disc_only_copies, NDO}}),
+
+ %% Verify n_fragments
+ Cs2 = verify_n_fragments(N, Cs, Mode),
+
+ %% Verify hash callback
+ HashMod = mnesia_schema:pick(Tab, hash_module, Props, ?DEFAULT_HASH_MOD),
+ HashState = mnesia_schema:pick(Tab, hash_state, Props, undefined),
+ HashState2 = HashMod:init_state(Tab, HashState), %% BUGBUG: Catch?
+
+ FH = #frag_state{foreign_key = ForeignKey2,
+ n_fragments = 1,
+ hash_module = HashMod,
+ hash_state = HashState2},
+ if
+ NR == 0, ND == 0, NDO == 0 ->
+ do_expand_cstruct(Cs2, FH, N, Pool, DefaultNR, DefaultND, DefaultNDO, Mode);
+ true ->
+ do_expand_cstruct(Cs2, FH, N, Pool, NR, ND, NDO, Mode)
+ end.
+
+do_expand_cstruct(Cs, FH, N, Pool, NR, ND, NDO, Mode) ->
+ Tab = Cs#cstruct.name,
+
+ LC = Cs#cstruct.local_content,
+ mnesia_schema:verify(false, LC,
+ {combine_error, Tab, {local_content, LC}}),
+
+ Snmp = Cs#cstruct.snmp,
+ mnesia_schema:verify([], Snmp,
+ {combine_error, Tab, {snmp, Snmp}}),
+
+ %% Add empty fragments
+ CommonProps = [{base_table, Tab}],
+ Cs2 = Cs#cstruct{frag_properties = lists:sort(CommonProps)},
+ expand_frag_cstructs(N, NR, ND, NDO, Cs2, Pool, Pool, FH, Mode).
+
+verify_n_fragments(N, Cs, Mode) when integer(N), N >= 1 ->
+ case Mode of
+ create ->
+ Cs#cstruct{ram_copies = [],
+ disc_copies = [],
+ disc_only_copies = []};
+ activate ->
+ Reason = {combine_error, Cs#cstruct.name, {n_fragments, N}},
+ mnesia_schema:verify(1, N, Reason),
+ Cs
+ end;
+verify_n_fragments(N, Cs, _Mode) ->
+ mnesia:abort({bad_type, Cs#cstruct.name, {n_fragments, N}}).
+
+pick_props(Tab, Cs, {ForeignTab, Attr}) ->
+ mnesia_schema:verify(true, ForeignTab /= Tab,
+ {combine_error, Tab, {ForeignTab, Attr}}),
+ Props = Cs#cstruct.frag_properties,
+ Attrs = Cs#cstruct.attributes,
+
+ ForeignKey = lookup_prop(ForeignTab, foreign_key),
+ ForeignN = lookup_prop(ForeignTab, n_fragments),
+ ForeignPool = lookup_prop(ForeignTab, node_pool),
+ N = mnesia_schema:pick(Tab, n_fragments, Props, ForeignN),
+ Pool = mnesia_schema:pick(Tab, node_pool, Props, ForeignPool),
+
+ mnesia_schema:verify(ForeignN, N,
+ {combine_error, Tab, {n_fragments, N},
+ ForeignTab, {n_fragments, ForeignN}}),
+
+ mnesia_schema:verify(ForeignPool, Pool,
+ {combine_error, Tab, {node_pool, Pool},
+ ForeignTab, {node_pool, ForeignPool}}),
+
+ mnesia_schema:verify(undefined, ForeignKey,
+ {combine_error, Tab,
+ "Multiple levels of foreign_key dependencies",
+ {ForeignTab, Attr}, ForeignKey}),
+
+ Key = {ForeignTab, mnesia_schema:attr_to_pos(Attr, Attrs)},
+ DefaultNR = length(val({ForeignTab, ram_copies})),
+ DefaultND = length(val({ForeignTab, disc_copies})),
+ DefaultNDO = length(val({ForeignTab, disc_only_copies})),
+ {Key, N, Pool, DefaultNR, DefaultND, DefaultNDO};
+pick_props(Tab, Cs, undefined) ->
+ Props = Cs#cstruct.frag_properties,
+ DefaultN = 1,
+ DefaultPool = mnesia:system_info(db_nodes),
+ N = mnesia_schema:pick(Tab, n_fragments, Props, DefaultN),
+ Pool = mnesia_schema:pick(Tab, node_pool, Props, DefaultPool),
+ DefaultNR = 1,
+ DefaultND = 0,
+ DefaultNDO = 0,
+ {undefined, N, Pool, DefaultNR, DefaultND, DefaultNDO};
+pick_props(Tab, _Cs, BadKey) ->
+ mnesia:abort({bad_type, Tab, {foreign_key, BadKey}}).
+
+expand_frag_cstructs(N, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode)
+ when N > 1, Mode == create ->
+ Frag = n_to_frag_name(CommonCs#cstruct.name, N),
+ Cs = CommonCs#cstruct{name = Frag},
+ {Cs2, RevModDist, RestDist} = set_frag_nodes(NR, ND, NDO, Cs, Dist, []),
+ ModDist = lists:reverse(RevModDist),
+ Dist2 = rearrange_dist(Cs, ModDist, RestDist, Pool),
+ %% Adjusts backwards, but it doesn't matter.
+ {FH2, _FromFrags, _AdditionalWriteFrags} = adjust_before_split(FH),
+ CsList = expand_frag_cstructs(N - 1, NR, ND, NDO, CommonCs, Dist2, Pool, FH2, Mode),
+ [Cs2 | CsList];
+expand_frag_cstructs(1, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode) ->
+ BaseProps = CommonCs#cstruct.frag_properties ++
+ [{foreign_key, FH#frag_state.foreign_key},
+ {hash_module, FH#frag_state.hash_module},
+ {hash_state, FH#frag_state.hash_state},
+ {n_fragments, FH#frag_state.n_fragments},
+ {node_pool, Pool}
+ ],
+ BaseCs = CommonCs#cstruct{frag_properties = lists:sort(BaseProps)},
+ case Mode of
+ activate ->
+ [BaseCs];
+ create ->
+ {BaseCs2, _, _} = set_frag_nodes(NR, ND, NDO, BaseCs, Dist, []),
+ [BaseCs2]
+ end.
+
+set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when NR > 0 ->
+ Pos = #cstruct.ram_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+ set_frag_nodes(NR - 1, ND, NDO, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when ND > 0 ->
+ Pos = #cstruct.disc_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+ set_frag_nodes(NR, ND - 1, NDO, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when NDO > 0 ->
+ Pos = #cstruct.disc_only_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+ set_frag_nodes(NR, ND, NDO - 1, Cs2, Tail, [Head2 | Acc]);
+set_frag_nodes(0, 0, 0, Cs, RestDist, ModDist) ->
+ {Cs, ModDist, RestDist};
+set_frag_nodes(_, _, _, Cs, [], _) ->
+ mnesia:abort({combine_error, Cs#cstruct.name, "Too few nodes in node_pool"}).
+
+set_frag_node(Cs, Pos, Head) ->
+ Ns = element(Pos, Cs),
+ {Node, Count2} =
+ case Head of
+ {N, Count} when atom(N), integer(Count), Count >= 0 ->
+ {N, Count + 1};
+ N when atom(N) ->
+ {N, 1};
+ BadNode ->
+ mnesia:abort({bad_type, Cs#cstruct.name, BadNode})
+ end,
+ Cs2 = setelement(Pos, Cs, [Node | Ns]),
+ {Cs2, {Node, Count2}}.
+
+rearrange_dist(Cs, [{Node, Count} | ModDist], Dist, Pool) ->
+ Dist2 = insert_dist(Cs, Node, Count, Dist, Pool),
+ rearrange_dist(Cs, ModDist, Dist2, Pool);
+rearrange_dist(_Cs, [], Dist, _) ->
+ Dist.
+
+insert_dist(Cs, Node, Count, [Head | Tail], Pool) ->
+ case Head of
+ {Node2, Count2} when atom(Node2), integer(Count2), Count2 >= 0 ->
+ case node_diff(Node, Count, Node2, Count2, Pool) of
+ less ->
+ [{Node, Count}, Head | Tail];
+ greater ->
+ [Head | insert_dist(Cs, Node, Count, Tail, Pool)]
+ end;
+ Node2 when atom(Node2) ->
+ insert_dist(Cs, Node, Count, [{Node2, 0} | Tail], Pool);
+ BadNode ->
+ mnesia:abort({bad_type, Cs#cstruct.name, BadNode})
+ end;
+insert_dist(_Cs, Node, Count, [], _Pool) ->
+ [{Node, Count}];
+insert_dist(_Cs, _Node, _Count, Dist, _Pool) ->
+ mnesia:abort({bad_type, Dist}).
+
+node_diff(_Node, Count, _Node2, Count2, _Pool) when Count < Count2 ->
+ less;
+node_diff(Node, Count, Node2, Count2, Pool) when Count == Count2 ->
+ Pos = list_pos(Node, Pool, 1),
+ Pos2 = list_pos(Node2, Pool, 1),
+ if
+ Pos < Pos2 ->
+ less;
+ Pos > Pos2 ->
+ greater
+ end;
+node_diff(_Node, Count, _Node2, Count2, _Pool) when Count > Count2 ->
+ greater.
+
+%% Returns position of element in list
+list_pos(H, [H | _T], Pos) ->
+ Pos;
+list_pos(E, [_H | T], Pos) ->
+ list_pos(E, T, Pos + 1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Switch function for changing of table fragmentation
+%%
+%% Returns a list of lists of schema ops
+
+change_table_frag(Tab, {activate, FragProps}) ->
+ make_activate(Tab, FragProps);
+change_table_frag(Tab, deactivate) ->
+ make_deactivate(Tab);
+change_table_frag(Tab, {add_frag, SortedNodes}) ->
+ make_multi_add_frag(Tab, SortedNodes);
+change_table_frag(Tab, del_frag) ->
+ make_multi_del_frag(Tab);
+change_table_frag(Tab, {add_node, Node}) ->
+ make_multi_add_node(Tab, Node);
+change_table_frag(Tab, {del_node, Node}) ->
+ make_multi_del_node(Tab, Node);
+change_table_frag(Tab, Change) ->
+ mnesia:abort({bad_type, Tab, Change}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Turn a normal table into a fragmented table
+%%
+%% The storage type must be the same on all nodes
+
+make_activate(Tab, Props) ->
+ Cs = mnesia_schema:incr_version(val({Tab, cstruct})),
+ mnesia_schema:ensure_active(Cs),
+ case Cs#cstruct.frag_properties of
+ [] ->
+ Cs2 = Cs#cstruct{frag_properties = Props},
+ [Cs3] = expand_cstruct(Cs2, activate),
+ TabDef = mnesia_schema:cs2list(Cs3),
+ Op = {op, change_table_frag, activate, TabDef},
+ [[Op]];
+ BadProps ->
+ mnesia:abort({already_exists, Tab, {frag_properties, BadProps}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Turn a table into a normal defragmented table
+
+make_deactivate(Tab) ->
+ Cs = mnesia_schema:incr_version(val({Tab, cstruct})),
+ mnesia_schema:ensure_active(Cs),
+ Foreigners = lookup_foreigners(Tab),
+ BaseTab = lookup_prop(Tab, base_table),
+ FH = lookup_frag_hash(Tab),
+ if
+ BaseTab /= Tab ->
+ mnesia:abort({combine_error, Tab, "Not a base table"});
+ Foreigners /= [] ->
+ mnesia:abort({combine_error, Tab, "Too many foreigners", Foreigners});
+ FH#frag_state.n_fragments > 1 ->
+ mnesia:abort({combine_error, Tab, "Too many fragments"});
+ true ->
+ Cs2 = Cs#cstruct{frag_properties = []},
+ TabDef = mnesia_schema:cs2list(Cs2),
+ Op = {op, change_table_frag, deactivate, TabDef},
+ [[Op]]
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Add a fragment to a fragmented table and fill it with half of
+%% the records from one of the old fragments
+
+make_multi_add_frag(Tab, SortedNs) when list(SortedNs) ->
+ verify_multi(Tab),
+ Ops = make_add_frag(Tab, SortedNs),
+
+ %% Propagate to foreigners
+ MoreOps = [make_add_frag(T, SortedNs) || T <- lookup_foreigners(Tab)],
+ [Ops | MoreOps];
+make_multi_add_frag(Tab, SortedNs) ->
+ mnesia:abort({bad_type, Tab, SortedNs}).
+
+verify_multi(Tab) ->
+ FH = lookup_frag_hash(Tab),
+ ForeignKey = FH#frag_state.foreign_key,
+ mnesia_schema:verify(undefined, ForeignKey,
+ {combine_error, Tab,
+ "Op only allowed via foreign table",
+ {foreign_key, ForeignKey}}).
+
+make_frag_names_and_acquire_locks(Tab, N, FragIndecies, DoNotLockN) ->
+ mnesia_schema:get_tid_ts_and_lock(Tab, write),
+ Fun = fun(Index, FN) ->
+ if
+ DoNotLockN == true, Index == N ->
+ Name = n_to_frag_name(Tab, Index),
+ setelement(Index, FN, Name);
+ true ->
+ Name = n_to_frag_name(Tab, Index),
+ mnesia_schema:get_tid_ts_and_lock(Name, write),
+ setelement(Index , FN, Name)
+ end
+ end,
+ FragNames = erlang:make_tuple(N, undefined),
+ lists:foldl(Fun, FragNames, FragIndecies).
+
+make_add_frag(Tab, SortedNs) ->
+ Cs = mnesia_schema:incr_version(val({Tab, cstruct})),
+ mnesia_schema:ensure_active(Cs),
+ FH = lookup_frag_hash(Tab),
+ {FH2, FromIndecies, WriteIndecies} = adjust_before_split(FH),
+ N = FH2#frag_state.n_fragments,
+ FragNames = make_frag_names_and_acquire_locks(Tab, N, WriteIndecies, true),
+ NewFrag = element(N, FragNames),
+
+ NR = length(Cs#cstruct.ram_copies),
+ ND = length(Cs#cstruct.disc_copies),
+ NDO = length(Cs#cstruct.disc_only_copies),
+ NewCs = Cs#cstruct{name = NewFrag,
+ frag_properties = [{base_table, Tab}],
+ ram_copies = [],
+ disc_copies = [],
+ disc_only_copies = []},
+ {NewCs2, _, _} = set_frag_nodes(NR, ND, NDO, NewCs, SortedNs, []),
+ [NewOp] = mnesia_schema:make_create_table(NewCs2),
+
+ SplitOps = split(Tab, FH2, FromIndecies, FragNames, []),
+
+ Cs2 = replace_frag_hash(Cs, FH2),
+ TabDef = mnesia_schema:cs2list(Cs2),
+ BaseOp = {op, change_table_frag, {add_frag, SortedNs}, TabDef},
+
+ [BaseOp, NewOp | SplitOps].
+
+replace_frag_hash(Cs, FH) when record(FH, frag_state) ->
+ Fun = fun(Prop) ->
+ case Prop of
+ {n_fragments, _} ->
+ {true, {n_fragments, FH#frag_state.n_fragments}};
+ {hash_module, _} ->
+ {true, {hash_module, FH#frag_state.hash_module}};
+ {hash_state, _} ->
+ {true, {hash_state, FH#frag_state.hash_state}};
+ {next_n_to_split, _} ->
+ false;
+ {n_doubles, _} ->
+ false;
+ _ ->
+ true
+ end
+ end,
+ Props = lists:zf(Fun, Cs#cstruct.frag_properties),
+ Cs#cstruct{frag_properties = Props}.
+
+%% Adjust table info before split
+adjust_before_split(FH) ->
+ HashState = FH#frag_state.hash_state,
+ {HashState2, FromFrags, AdditionalWriteFrags} =
+ case FH#frag_state.hash_module of
+ HashMod when HashMod == ?DEFAULT_HASH_MOD ->
+ ?DEFAULT_HASH_MOD:add_frag(HashState);
+ HashMod ->
+ HashMod:add_frag(HashState)
+ end,
+ N = FH#frag_state.n_fragments + 1,
+ FromFrags2 = (catch lists:sort(FromFrags)),
+ UnionFrags = (catch lists:merge(FromFrags2, lists:sort(AdditionalWriteFrags))),
+ VerifyFun = fun(F) when integer(F), F >= 1, F =< N -> false;
+ (_F) -> true
+ end,
+ case catch lists:filter(VerifyFun, UnionFrags) of
+ [] ->
+ FH2 = FH#frag_state{n_fragments = N,
+ hash_state = HashState2},
+ {FH2, FromFrags2, UnionFrags};
+ BadFrags ->
+ mnesia:abort({"add_frag: Fragment numbers out of range",
+ BadFrags, {range, 1, N}})
+ end.
+
+split(Tab, FH, [SplitN | SplitNs], FragNames, Ops) ->
+ SplitFrag = element(SplitN, FragNames),
+ Pat = mnesia:table_info(SplitFrag, wild_pattern),
+ {_Mod, Tid, Ts} = mnesia_schema:get_tid_ts_and_lock(Tab, none),
+ Recs = mnesia:match_object(Tid, Ts, SplitFrag, Pat, read),
+ Ops2 = do_split(FH, SplitN, FragNames, Recs, Ops),
+ split(Tab, FH, SplitNs, FragNames, Ops2);
+split(_Tab, _FH, [], _FragNames, Ops) ->
+ Ops.
+
+%% Perform the split of the table
+do_split(FH, OldN, FragNames, [Rec | Recs], Ops) ->
+ Pos = key_pos(FH),
+ HashKey = element(Pos, Rec),
+ case key_to_n(FH, HashKey) of
+ NewN when NewN == OldN ->
+ %% Keep record in the same fragment. No need to move it.
+ do_split(FH, OldN, FragNames, Recs, Ops);
+ NewN ->
+ case element(NewN, FragNames) of
+ NewFrag when NewFrag /= undefined ->
+ OldFrag = element(OldN, FragNames),
+ Key = element(2, Rec),
+ NewOid = {NewFrag, Key},
+ OldOid = {OldFrag, Key},
+ Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}},
+ {op, rec, unknown, {OldOid, [OldOid], delete}} | Ops],
+ do_split(FH, OldN, FragNames, Recs, Ops2);
+ _NewFrag ->
+ %% Tried to move record to fragment that not is locked
+ mnesia:abort({"add_frag: Fragment not locked", NewN})
+ end
+ end;
+do_split(_FH, _OldN, _FragNames, [], Ops) ->
+ Ops.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Delete a fragment from a fragmented table
+%% and merge its records with an other fragment
+
+make_multi_del_frag(Tab) ->
+ verify_multi(Tab),
+ Ops = make_del_frag(Tab),
+
+ %% Propagate to foreigners
+ MoreOps = [make_del_frag(T) || T <- lookup_foreigners(Tab)],
+ [Ops | MoreOps].
+
+make_del_frag(Tab) ->
+ FH = lookup_frag_hash(Tab),
+ case FH#frag_state.n_fragments of
+ N when N > 1 ->
+ Cs = mnesia_schema:incr_version(val({Tab, cstruct})),
+ mnesia_schema:ensure_active(Cs),
+ {FH2, FromIndecies, WriteIndecies} = adjust_before_merge(FH),
+ FragNames = make_frag_names_and_acquire_locks(Tab, N, WriteIndecies, false),
+
+ MergeOps = merge(Tab, FH2, FromIndecies, FragNames, []),
+ LastFrag = element(N, FragNames),
+ [LastOp] = mnesia_schema:make_delete_table(LastFrag, single_frag),
+ Cs2 = replace_frag_hash(Cs, FH2),
+ TabDef = mnesia_schema:cs2list(Cs2),
+ BaseOp = {op, change_table_frag, del_frag, TabDef},
+ [BaseOp, LastOp | MergeOps];
+ _ ->
+ %% Cannot remove the last fragment
+ mnesia:abort({no_exists, Tab})
+ end.
+
+%% Adjust tab info before merge
+adjust_before_merge(FH) ->
+ HashState = FH#frag_state.hash_state,
+ {HashState2, FromFrags, AdditionalWriteFrags} =
+ case FH#frag_state.hash_module of
+ HashMod when HashMod == ?DEFAULT_HASH_MOD ->
+ ?DEFAULT_HASH_MOD:del_frag(HashState);
+ HashMod ->
+ HashMod:del_frag(HashState)
+ end,
+ N = FH#frag_state.n_fragments,
+ FromFrags2 = (catch lists:sort(FromFrags)),
+ UnionFrags = (catch lists:merge(FromFrags2, lists:sort(AdditionalWriteFrags))),
+ VerifyFun = fun(F) when integer(F), F >= 1, F =< N -> false;
+ (_F) -> true
+ end,
+ case catch lists:filter(VerifyFun, UnionFrags) of
+ [] ->
+ case lists:member(N, FromFrags2) of
+ true ->
+ FH2 = FH#frag_state{n_fragments = N - 1,
+ hash_state = HashState2},
+ {FH2, FromFrags2, UnionFrags};
+ false ->
+ mnesia:abort({"del_frag: Last fragment number not included", N})
+ end;
+ BadFrags ->
+ mnesia:abort({"del_frag: Fragment numbers out of range",
+ BadFrags, {range, 1, N}})
+ end.
+
+merge(Tab, FH, [FromN | FromNs], FragNames, Ops) ->
+ FromFrag = element(FromN, FragNames),
+ Pat = mnesia:table_info(FromFrag, wild_pattern),
+ {_Mod, Tid, Ts} = mnesia_schema:get_tid_ts_and_lock(Tab, none),
+ Recs = mnesia:match_object(Tid, Ts, FromFrag, Pat, read),
+ Ops2 = do_merge(FH, FromN, FragNames, Recs, Ops),
+ merge(Tab, FH, FromNs, FragNames, Ops2);
+merge(_Tab, _FH, [], _FragNames, Ops) ->
+ Ops.
+
+%% Perform the merge of the table
+do_merge(FH, OldN, FragNames, [Rec | Recs], Ops) ->
+ Pos = key_pos(FH),
+ LastN = FH#frag_state.n_fragments + 1,
+ HashKey = element(Pos, Rec),
+ case key_to_n(FH, HashKey) of
+ NewN when NewN == LastN ->
+ %% Tried to leave a record in the fragment that is to be deleted
+ mnesia:abort({"del_frag: Fragment number out of range",
+ NewN, {range, 1, LastN}});
+ NewN when NewN == OldN ->
+ %% Keep record in the same fragment. No need to move it.
+ do_merge(FH, OldN, FragNames, Recs, Ops);
+ NewN when OldN == LastN ->
+ %% Move record from the fragment that is to be deleted
+ %% No need to create a delete op for each record.
+ case element(NewN, FragNames) of
+ NewFrag when NewFrag /= undefined ->
+ Key = element(2, Rec),
+ NewOid = {NewFrag, Key},
+ Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}} | Ops],
+ do_merge(FH, OldN, FragNames, Recs, Ops2);
+ _NewFrag ->
+ %% Tried to move record to fragment that not is locked
+ mnesia:abort({"del_frag: Fragment not locked", NewN})
+ end;
+ NewN ->
+ case element(NewN, FragNames) of
+ NewFrag when NewFrag /= undefined ->
+ OldFrag = element(OldN, FragNames),
+ Key = element(2, Rec),
+ NewOid = {NewFrag, Key},
+ OldOid = {OldFrag, Key},
+ Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}},
+ {op, rec, unknown, {OldOid, [OldOid], delete}} | Ops],
+ do_merge(FH, OldN, FragNames, Recs, Ops2);
+ _NewFrag ->
+ %% Tried to move record to fragment that not is locked
+ mnesia:abort({"del_frag: Fragment not locked", NewN})
+ end
+ end;
+ do_merge(_FH, _OldN, _FragNames, [], Ops) ->
+ Ops.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Add a node to the node pool of a fragmented table
+
+make_multi_add_node(Tab, Node) ->
+ verify_multi(Tab),
+ Ops = make_add_node(Tab, Node),
+
+ %% Propagate to foreigners
+ MoreOps = [make_add_node(T, Node) || T <- lookup_foreigners(Tab)],
+ [Ops | MoreOps].
+
+make_add_node(Tab, Node) when atom(Node) ->
+ Pool = lookup_prop(Tab, node_pool),
+ case lists:member(Node, Pool) of
+ false ->
+ Cs = mnesia_schema:incr_version(val({Tab, cstruct})),
+ Pool2 = Pool ++ [Node],
+ Props = Cs#cstruct.frag_properties,
+ Props2 = lists:keyreplace(node_pool, 1, Props, {node_pool, Pool2}),
+ Cs2 = Cs#cstruct{frag_properties = Props2},
+ TabDef = mnesia_schema:cs2list(Cs2),
+ Op = {op, change_table_frag, {add_node, Node}, TabDef},
+ [Op];
+ true ->
+ mnesia:abort({already_exists, Tab, Node})
+ end;
+make_add_node(Tab, Node) ->
+ mnesia:abort({bad_type, Tab, Node}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Delet a node from the node pool of a fragmented table
+
+make_multi_del_node(Tab, Node) ->
+ verify_multi(Tab),
+ Ops = make_del_node(Tab, Node),
+
+ %% Propagate to foreigners
+ MoreOps = [make_del_node(T, Node) || T <- lookup_foreigners(Tab)],
+ [Ops | MoreOps].
+
+make_del_node(Tab, Node) when atom(Node) ->
+ Cs = mnesia_schema:incr_version(val({Tab, cstruct})),
+ mnesia_schema:ensure_active(Cs),
+ Pool = lookup_prop(Tab, node_pool),
+ case lists:member(Node, Pool) of
+ true ->
+ Pool2 = Pool -- [Node],
+ Props = lists:keyreplace(node_pool, 1, Cs#cstruct.frag_properties, {node_pool, Pool2}),
+ Cs2 = Cs#cstruct{frag_properties = Props},
+ TabDef = mnesia_schema:cs2list(Cs2),
+ Op = {op, change_table_frag, {del_node, Node}, TabDef},
+ [Op];
+ false ->
+ mnesia:abort({no_exists, Tab, Node})
+ end;
+make_del_node(Tab, Node) ->
+ mnesia:abort({bad_type, Tab, Node}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Special case used to remove all references to a node during
+%% mnesia:del_table_copy(schema, Node)
+
+remove_node(Node, Cs) ->
+ Tab = Cs#cstruct.name,
+ case is_top_frag(Tab) of
+ false ->
+ {Cs, false};
+ true ->
+ Pool = lookup_prop(Tab, node_pool),
+ case lists:member(Node, Pool) of
+ true ->
+ Pool2 = Pool -- [Node],
+ Props = lists:keyreplace(node_pool, 1,
+ Cs#cstruct.frag_properties,
+ {node_pool, Pool2}),
+ {Cs#cstruct{frag_properties = Props}, true};
+ false ->
+ {Cs, false}
+ end
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Helpers
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
+
+set_frag_hash(Tab, Props) ->
+ case props_to_frag_hash(Tab, Props) of
+ FH when record(FH, frag_state) ->
+ mnesia_lib:set({Tab, frag_hash}, FH);
+ no_hash ->
+ mnesia_lib:unset({Tab, frag_hash})
+ end.
+
+props_to_frag_hash(_Tab, []) ->
+ no_hash;
+props_to_frag_hash(Tab, Props) ->
+ case mnesia_schema:pick(Tab, base_table, Props, undefined) of
+ T when T == Tab ->
+ Foreign = mnesia_schema:pick(Tab, foreign_key, Props, must),
+ N = mnesia_schema:pick(Tab, n_fragments, Props, must),
+
+ case mnesia_schema:pick(Tab, hash_module, Props, undefined) of
+ undefined ->
+ Split = mnesia_schema:pick(Tab, next_n_to_split, Props, must),
+ Doubles = mnesia_schema:pick(Tab, n_doubles, Props, must),
+ FH = {frag_hash, Foreign, N, Split, Doubles},
+ HashState = ?OLD_HASH_MOD:init_state(Tab, FH),
+ #frag_state{foreign_key = Foreign,
+ n_fragments = N,
+ hash_module = ?OLD_HASH_MOD,
+ hash_state = HashState};
+ HashMod ->
+ HashState = mnesia_schema:pick(Tab, hash_state, Props, must),
+ #frag_state{foreign_key = Foreign,
+ n_fragments = N,
+ hash_module = HashMod,
+ hash_state = HashState}
+ %% Old style. Kept for backwards compatibility.
+ end;
+ _ ->
+ no_hash
+ end.
+
+lookup_prop(Tab, Prop) ->
+ Props = val({Tab, frag_properties}),
+ case lists:keysearch(Prop, 1, Props) of
+ {value, {Prop, Val}} ->
+ Val;
+ false ->
+ mnesia:abort({no_exists, Tab, Prop, {frag_properties, Props}})
+ end.
+
+lookup_frag_hash(Tab) ->
+ case ?catch_val({Tab, frag_hash}) of
+ FH when record(FH, frag_state) ->
+ FH;
+ {frag_hash, K, N, _S, _D} = FH ->
+ %% Old style. Kept for backwards compatibility.
+ HashState = ?OLD_HASH_MOD:init_state(Tab, FH),
+ #frag_state{foreign_key = K,
+ n_fragments = N,
+ hash_module = ?OLD_HASH_MOD,
+ hash_state = HashState};
+ {'EXIT', _} ->
+ mnesia:abort({no_exists, Tab, frag_properties, frag_hash})
+ end.
+
+is_top_frag(Tab) ->
+ case ?catch_val({Tab, frag_hash}) of
+ {'EXIT', _} ->
+ false;
+ _ ->
+ [] == lookup_foreigners(Tab)
+ end.
+
+%% Returns a list of tables
+lookup_foreigners(Tab) ->
+ %% First field in HashPat is either frag_hash or frag_state
+ HashPat = {'_', {Tab, '_'}, '_', '_', '_'},
+ [T || [T] <- ?ets_match(mnesia_gvar, {{'$1', frag_hash}, HashPat})].
+
+%% Returns name of fragment table
+record_to_frag_name(Tab, Rec) ->
+ case ?catch_val({Tab, frag_hash}) of
+ {'EXIT', _} ->
+ Tab;
+ FH ->
+ Pos = key_pos(FH),
+ Key = element(Pos, Rec),
+ N = key_to_n(FH, Key),
+ n_to_frag_name(Tab, N)
+ end.
+
+key_pos(FH) ->
+ case FH#frag_state.foreign_key of
+ undefined ->
+ 2;
+ {_ForeignTab, Pos} ->
+ Pos
+ end.
+
+%% Returns name of fragment table
+key_to_frag_name({BaseTab, _} = Tab, Key) ->
+ N = key_to_frag_number(Tab, Key),
+ n_to_frag_name(BaseTab, N);
+key_to_frag_name(Tab, Key) ->
+ N = key_to_frag_number(Tab, Key),
+ n_to_frag_name(Tab, N).
+
+%% Returns name of fragment table
+n_to_frag_name(Tab, 1) ->
+ Tab;
+n_to_frag_name(Tab, N) when atom(Tab), integer(N) ->
+ list_to_atom(atom_to_list(Tab) ++ "_frag" ++ integer_to_list(N));
+n_to_frag_name(Tab, N) ->
+ mnesia:abort({bad_type, Tab, N}).
+
+%% Returns name of fragment table
+key_to_frag_number({Tab, ForeignKey}, _Key) ->
+ FH = val({Tab, frag_hash}),
+ case FH#frag_state.foreign_key of
+ {_ForeignTab, _Pos} ->
+ key_to_n(FH, ForeignKey);
+ undefined ->
+ mnesia:abort({combine_error, Tab, frag_properties,
+ {foreign_key, undefined}})
+ end;
+key_to_frag_number(Tab, Key) ->
+ case ?catch_val({Tab, frag_hash}) of
+ {'EXIT', _} ->
+ 1;
+ FH ->
+ key_to_n(FH, Key)
+ end.
+
+%% Returns fragment number
+key_to_n(FH, Key) ->
+ HashState = FH#frag_state.hash_state,
+ N =
+ case FH#frag_state.hash_module of
+ HashMod when HashMod == ?DEFAULT_HASH_MOD ->
+ ?DEFAULT_HASH_MOD:key_to_frag_number(HashState, Key);
+ HashMod ->
+ HashMod:key_to_frag_number(HashState, Key)
+ end,
+ if
+ integer(N), N >= 1, N =< FH#frag_state.n_fragments ->
+ N;
+ true ->
+ mnesia:abort({"key_to_frag_number: Fragment number out of range",
+ N, {range, 1, FH#frag_state.n_fragments}})
+ end.
+
+%% Returns a list of frament table names
+frag_names(Tab) ->
+ case ?catch_val({Tab, frag_hash}) of
+ {'EXIT', _} ->
+ [Tab];
+ FH ->
+ N = FH#frag_state.n_fragments,
+ frag_names(Tab, N, [])
+ end.
+
+frag_names(Tab, 1, Acc) ->
+ [Tab | Acc];
+frag_names(Tab, N, Acc) ->
+ Frag = n_to_frag_name(Tab, N),
+ frag_names(Tab, N - 1, [Frag | Acc]).
+
+%% Returns a list of {Node, FragCount} tuples
+%% sorted on FragCounts
+frag_dist(Tab) ->
+ Pool = lookup_prop(Tab, node_pool),
+ Dist = [{good, Node, 0} || Node <- Pool],
+ Dist2 = count_frag(frag_names(Tab), Dist),
+ sort_dist(Dist2).
+
+count_frag([Frag | Frags], Dist) ->
+ Dist2 = incr_nodes(val({Frag, ram_copies}), Dist),
+ Dist3 = incr_nodes(val({Frag, disc_copies}), Dist2),
+ Dist4 = incr_nodes(val({Frag, disc_only_copies}), Dist3),
+ count_frag(Frags, Dist4);
+count_frag([], Dist) ->
+ Dist.
+
+incr_nodes([Node | Nodes], Dist) ->
+ Dist2 = incr_node(Node, Dist),
+ incr_nodes(Nodes, Dist2);
+incr_nodes([], Dist) ->
+ Dist.
+
+incr_node(Node, [{Kind, Node, Count} | Tail]) ->
+ [{Kind, Node, Count + 1} | Tail];
+incr_node(Node, [Head | Tail]) ->
+ [Head | incr_node(Node, Tail)];
+incr_node(Node, []) ->
+ [{bad, Node, 1}].
+
+%% Sorts dist according in decreasing count order
+sort_dist(Dist) ->
+ Dist2 = deep_dist(Dist, []),
+ Dist3 = lists:keysort(1, Dist2),
+ shallow_dist(Dist3).
+
+deep_dist([Head | Tail], Deep) ->
+ {Kind, _Node, Count} = Head,
+ {Tag, Same, Other} = pick_count(Kind, Count, [Head | Tail]),
+ deep_dist(Other, [{Tag, Same} | Deep]);
+deep_dist([], Deep) ->
+ Deep.
+
+pick_count(Kind, Count, [{Kind2, Node2, Count2} | Tail]) ->
+ Head = {Node2, Count2},
+ {_, Same, Other} = pick_count(Kind, Count, Tail),
+ if
+ Kind == bad ->
+ {bad, [Head | Same], Other};
+ Kind2 == bad ->
+ {Count, Same, [{Kind2, Node2, Count2} | Other]};
+ Count == Count2 ->
+ {Count, [Head | Same], Other};
+ true ->
+ {Count, Same, [{Kind2, Node2, Count2} | Other]}
+ end;
+pick_count(_Kind, Count, []) ->
+ {Count, [], []}.
+
+shallow_dist([{_Tag, Shallow} | Deep]) ->
+ Shallow ++ shallow_dist(Deep);
+shallow_dist([]) ->
+ [].
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_hash.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_hash.erl
new file mode 100644
index 0000000000..19b97f8d61
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_hash.erl
@@ -0,0 +1,118 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_frag_hash.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : Implements hashing functionality for fragmented tables
+%%%----------------------------------------------------------------------
+
+%header_doc_include
+-module(mnesia_frag_hash).
+-behaviour(mnesia_frag_hash).
+
+%% Fragmented Table Hashing callback functions
+-export([
+ init_state/2,
+ add_frag/1,
+ del_frag/1,
+ key_to_frag_number/2,
+ match_spec_to_frag_numbers/2
+ ]).
+
+%header_doc_include
+
+%impl_doc_include
+-record(hash_state, {n_fragments, next_n_to_split, n_doubles}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_state(_Tab, State) when State == undefined ->
+ #hash_state{n_fragments = 1,
+ next_n_to_split = 1,
+ n_doubles = 0}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+add_frag(State) when record(State, hash_state) ->
+ SplitN = State#hash_state.next_n_to_split,
+ P = SplitN + 1,
+ L = State#hash_state.n_doubles,
+ NewN = State#hash_state.n_fragments + 1,
+ State2 = case trunc(math:pow(2, L)) + 1 of
+ P2 when P2 == P ->
+ State#hash_state{n_fragments = NewN,
+ n_doubles = L + 1,
+ next_n_to_split = 1};
+ _ ->
+ State#hash_state{n_fragments = NewN,
+ next_n_to_split = P}
+ end,
+ {State2, [SplitN], [NewN]}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+del_frag(State) when record(State, hash_state) ->
+ P = State#hash_state.next_n_to_split - 1,
+ L = State#hash_state.n_doubles,
+ N = State#hash_state.n_fragments,
+ if
+ P < 1 ->
+ L2 = L - 1,
+ MergeN = trunc(math:pow(2, L2)),
+ State2 = State#hash_state{n_fragments = N - 1,
+ next_n_to_split = MergeN,
+ n_doubles = L2},
+ {State2, [N], [MergeN]};
+ true ->
+ MergeN = P,
+ State2 = State#hash_state{n_fragments = N - 1,
+ next_n_to_split = MergeN},
+ {State2, [N], [MergeN]}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+key_to_frag_number(State, Key) when record(State, hash_state) ->
+ L = State#hash_state.n_doubles,
+ A = erlang:phash(Key, trunc(math:pow(2, L))),
+ P = State#hash_state.next_n_to_split,
+ if
+ A < P ->
+ erlang:phash(Key, trunc(math:pow(2, L + 1)));
+ true ->
+ A
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+match_spec_to_frag_numbers(State, MatchSpec) when record(State, hash_state) ->
+ case MatchSpec of
+ [{HeadPat, _, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
+ KeyPat = element(2, HeadPat),
+ case has_var(KeyPat) of
+ false ->
+ [key_to_frag_number(State, KeyPat)];
+ true ->
+ lists:seq(1, State#hash_state.n_fragments)
+ end;
+ _ ->
+ lists:seq(1, State#hash_state.n_fragments)
+ end.
+
+%impl_doc_include
+
+has_var(Pat) ->
+ mnesia:has_var(Pat).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_old_hash.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_old_hash.erl
new file mode 100644
index 0000000000..6560613302
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_frag_old_hash.erl
@@ -0,0 +1,127 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_frag_old_hash.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : Implements hashing functionality for fragmented tables
+%%%----------------------------------------------------------------------
+
+-module(mnesia_frag_old_hash).
+-behaviour(mnesia_frag_hash).
+
+%% Hashing callback functions
+-export([
+ init_state/2,
+ add_frag/1,
+ del_frag/1,
+ key_to_frag_number/2,
+ match_spec_to_frag_numbers/2
+ ]).
+
+-record(old_hash_state,
+ {n_fragments,
+ next_n_to_split,
+ n_doubles}).
+
+%% Old style. Kept for backwards compatibility.
+-record(frag_hash,
+ {foreign_key,
+ n_fragments,
+ next_n_to_split,
+ n_doubles}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_state(_Tab, InitialState) when InitialState == undefined ->
+ #old_hash_state{n_fragments = 1,
+ next_n_to_split = 1,
+ n_doubles = 0};
+init_state(_Tab, FH) when record(FH, frag_hash) ->
+ %% Old style. Kept for backwards compatibility.
+ #old_hash_state{n_fragments = FH#frag_hash.n_fragments,
+ next_n_to_split = FH#frag_hash.next_n_to_split,
+ n_doubles = FH#frag_hash.n_doubles}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+add_frag(State) when record(State, old_hash_state) ->
+ SplitN = State#old_hash_state.next_n_to_split,
+ P = SplitN + 1,
+ L = State#old_hash_state.n_doubles,
+ NewN = State#old_hash_state.n_fragments + 1,
+ State2 = case trunc(math:pow(2, L)) + 1 of
+ P2 when P2 == P ->
+ State#old_hash_state{n_fragments = NewN,
+ next_n_to_split = 1,
+ n_doubles = L + 1};
+ _ ->
+ State#old_hash_state{n_fragments = NewN,
+ next_n_to_split = P}
+ end,
+ {State2, [SplitN], [NewN]}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+del_frag(State) when record(State, old_hash_state) ->
+ P = State#old_hash_state.next_n_to_split - 1,
+ L = State#old_hash_state.n_doubles,
+ N = State#old_hash_state.n_fragments,
+ if
+ P < 1 ->
+ L2 = L - 1,
+ MergeN = trunc(math:pow(2, L2)),
+ State2 = State#old_hash_state{n_fragments = N - 1,
+ next_n_to_split = MergeN,
+ n_doubles = L2},
+ {State2, [N], [MergeN]};
+ true ->
+ MergeN = P,
+ State2 = State#old_hash_state{n_fragments = N - 1,
+ next_n_to_split = MergeN},
+ {State2, [N], [MergeN]}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+key_to_frag_number(State, Key) when record(State, old_hash_state) ->
+ L = State#old_hash_state.n_doubles,
+ A = erlang:hash(Key, trunc(math:pow(2, L))),
+ P = State#old_hash_state.next_n_to_split,
+ if
+ A < P ->
+ erlang:hash(Key, trunc(math:pow(2, L + 1)));
+ true ->
+ A
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+match_spec_to_frag_numbers(State, MatchSpec) when record(State, old_hash_state) ->
+ case MatchSpec of
+ [{HeadPat, _, _}] when tuple(HeadPat), size(HeadPat) > 2 ->
+ KeyPat = element(2, HeadPat),
+ case has_var(KeyPat) of
+ false ->
+ [key_to_frag_number(State, KeyPat)];
+ true ->
+ lists:seq(1, State#old_hash_state.n_fragments)
+ end;
+ _ ->
+ lists:seq(1, State#old_hash_state.n_fragments)
+ end.
+
+has_var(Pat) ->
+ mnesia:has_var(Pat).
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_index.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_index.erl
new file mode 100644
index 0000000000..3455a4808a
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_index.erl
@@ -0,0 +1,380 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_index.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+%% Purpose: Handles index functionality in mnesia
+
+-module(mnesia_index).
+-export([read/5,
+ add_index/5,
+ delete_index/3,
+ del_object_index/5,
+ clear_index/4,
+ dirty_match_object/3,
+ dirty_select/3,
+ dirty_read/3,
+ dirty_read2/3,
+
+ db_put/2,
+ db_get/2,
+ db_match_erase/2,
+ get_index_table/2,
+ get_index_table/3,
+
+ tab2filename/2,
+ tab2tmp_filename/2,
+ init_index/2,
+ init_indecies/3,
+ del_transient/2,
+ del_transient/3,
+ del_index_table/3]).
+
+-import(mnesia_lib, [verbose/2]).
+-include("mnesia.hrl").
+
+-record(index, {setorbag, pos_list}).
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ _VaLuE_ -> _VaLuE_
+ end.
+
+%% read an object list throuh its index table
+%% we assume that table Tab has index on attribute number Pos
+
+read(Tid, Store, Tab, IxKey, Pos) ->
+ ResList = mnesia_locker:ixrlock(Tid, Store, Tab, IxKey, Pos),
+ %% Remove all tuples which don't include Ixkey, happens when Tab is a bag
+ case val({Tab, setorbag}) of
+ bag ->
+ mnesia_lib:key_search_all(IxKey, Pos, ResList);
+ _ ->
+ ResList
+ end.
+
+add_index(Index, Tab, Key, Obj, Old) ->
+ add_index2(Index#index.pos_list, Index#index.setorbag, Tab, Key, Obj, Old).
+
+add_index2([{Pos, Ixt} |Tail], bag, Tab, K, Obj, OldRecs) ->
+ db_put(Ixt, {element(Pos, Obj), K}),
+ add_index2(Tail, bag, Tab, K, Obj, OldRecs);
+add_index2([{Pos, Ixt} |Tail], Type, Tab, K, Obj, OldRecs) ->
+ %% Remove old tuples in index if Tab is updated
+ case OldRecs of
+ undefined ->
+ Old = mnesia_lib:db_get(Tab, K),
+ del_ixes(Ixt, Old, Pos, K);
+ Old ->
+ del_ixes(Ixt, Old, Pos, K)
+ end,
+ db_put(Ixt, {element(Pos, Obj), K}),
+ add_index2(Tail, Type, Tab, K, Obj, OldRecs);
+add_index2([], _, _Tab, _K, _Obj, _) -> ok.
+
+delete_index(Index, Tab, K) ->
+ delete_index2(Index#index.pos_list, Tab, K).
+
+delete_index2([{Pos, Ixt} | Tail], Tab, K) ->
+ DelObjs = mnesia_lib:db_get(Tab, K),
+ del_ixes(Ixt, DelObjs, Pos, K),
+ delete_index2(Tail, Tab, K);
+delete_index2([], _Tab, _K) -> ok.
+
+
+del_ixes(_Ixt, [], _Pos, _L) -> ok;
+del_ixes(Ixt, [Obj | Tail], Pos, Key) ->
+ db_match_erase(Ixt, {element(Pos, Obj), Key}),
+ del_ixes(Ixt, Tail, Pos, Key).
+
+del_object_index(Index, Tab, K, Obj, Old) ->
+ del_object_index2(Index#index.pos_list, Index#index.setorbag, Tab, K, Obj, Old).
+
+del_object_index2([], _, _Tab, _K, _Obj, _Old) -> ok;
+del_object_index2([{Pos, Ixt} | Tail], SoB, Tab, K, Obj, Old) ->
+ case SoB of
+ bag ->
+ del_object_bag(Tab, K, Obj, Pos, Ixt, Old);
+ _ -> %% If set remove the tuple in index table
+ del_ixes(Ixt, [Obj], Pos, K)
+ end,
+ del_object_index2(Tail, SoB, Tab, K, Obj, Old).
+
+del_object_bag(Tab, Key, Obj, Pos, Ixt, undefined) ->
+ Old = mnesia_lib:db_get(Tab, Key),
+ del_object_bag(Tab, Key, Obj, Pos, Ixt, Old);
+%% If Tab type is bag we need remove index identifier if Tab
+%% contains less than 2 elements.
+del_object_bag(_Tab, Key, Obj, Pos, Ixt, Old) when length(Old) < 2 ->
+ del_ixes(Ixt, [Obj], Pos, Key);
+del_object_bag(_Tab, _Key, _Obj, _Pos, _Ixt, _Old) -> ok.
+
+clear_index(Index, Tab, K, Obj) ->
+ clear_index2(Index#index.pos_list, Tab, K, Obj).
+
+clear_index2([], _Tab, _K, _Obj) -> ok;
+clear_index2([{_Pos, Ixt} | Tail], Tab, K, Obj) ->
+ db_match_erase(Ixt, Obj),
+ clear_index2(Tail, Tab, K, Obj).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dirty_match_object(Tab, Pat, Pos) ->
+ %% Assume that we are on the node where the replica is
+ case element(2, Pat) of
+ '_' ->
+ IxKey = element(Pos, Pat),
+ RealKeys = realkeys(Tab, Pos, IxKey),
+ merge(RealKeys, Tab, Pat, []);
+ _Else ->
+ mnesia_lib:db_match_object(Tab, Pat)
+ end.
+
+merge([{_IxKey, RealKey} | Tail], Tab, Pat, Ack) ->
+ %% Assume that we are on the node where the replica is
+ Pat2 = setelement(2, Pat, RealKey),
+ Recs = mnesia_lib:db_match_object(Tab, Pat2),
+ merge(Tail, Tab, Pat, Recs ++ Ack);
+merge([], _, _, Ack) ->
+ Ack.
+
+realkeys(Tab, Pos, IxKey) ->
+ Index = get_index_table(Tab, Pos),
+ db_get(Index, IxKey). % a list on the form [{IxKey, RealKey1} , ....
+
+dirty_select(Tab, Spec, Pos) ->
+ %% Assume that we are on the node where the replica is
+ %% Returns the records without applying the match spec
+ %% The actual filtering is handled by the caller
+ IxKey = element(Pos, Spec),
+ RealKeys = realkeys(Tab, Pos, IxKey),
+ StorageType = val({Tab, storage_type}),
+ lists:append([mnesia_lib:db_get(StorageType, Tab, Key) || Key <- RealKeys]).
+
+dirty_read(Tab, IxKey, Pos) ->
+ ResList = mnesia:dirty_rpc(Tab, ?MODULE, dirty_read2,
+ [Tab, IxKey, Pos]),
+ case val({Tab, setorbag}) of
+ bag ->
+ %% Remove all tuples which don't include Ixkey
+ mnesia_lib:key_search_all(IxKey, Pos, ResList);
+ _ ->
+ ResList
+ end.
+
+dirty_read2(Tab, IxKey, Pos) ->
+ Ix = get_index_table(Tab, Pos),
+ Keys = db_match(Ix, {IxKey, '$1'}),
+ r_keys(Keys, Tab, []).
+
+r_keys([[H]|T],Tab,Ack) ->
+ V = mnesia_lib:db_get(Tab, H),
+ r_keys(T, Tab, V ++ Ack);
+r_keys([], _, Ack) ->
+ Ack.
+
+
+%%%%%%% Creation, Init and deletion routines for index tables
+%% We can have several indexes on the same table
+%% this can be a fairly costly operation if table is *very* large
+
+tab2filename(Tab, Pos) ->
+ mnesia_lib:dir(Tab) ++ "_" ++ integer_to_list(Pos) ++ ".DAT".
+
+tab2tmp_filename(Tab, Pos) ->
+ mnesia_lib:dir(Tab) ++ "_" ++ integer_to_list(Pos) ++ ".TMP".
+
+init_index(Tab, Storage) ->
+ PosList = val({Tab, index}),
+ init_indecies(Tab, Storage, PosList).
+
+init_indecies(Tab, Storage, PosList) ->
+ case Storage of
+ unknown ->
+ ignore;
+ disc_only_copies ->
+ init_disc_index(Tab, PosList);
+ ram_copies ->
+ make_ram_index(Tab, PosList);
+ disc_copies ->
+ make_ram_index(Tab, PosList)
+ end.
+
+%% works for both ram and disc indexes
+
+del_index_table(_, unknown, _) ->
+ ignore;
+del_index_table(Tab, Storage, Pos) ->
+ delete_transient_index(Tab, Pos, Storage),
+ mnesia_lib:del({Tab, index}, Pos).
+
+del_transient(Tab, Storage) ->
+ PosList = val({Tab, index}),
+ del_transient(Tab, PosList, Storage).
+
+del_transient(_, [], _) -> done;
+del_transient(Tab, [Pos | Tail], Storage) ->
+ delete_transient_index(Tab, Pos, Storage),
+ del_transient(Tab, Tail, Storage).
+
+delete_transient_index(Tab, Pos, disc_only_copies) ->
+ Tag = {Tab, index, Pos},
+ mnesia_monitor:unsafe_close_dets(Tag),
+ file:delete(tab2filename(Tab, Pos)),
+ del_index_info(Tab, Pos), %% Uses val(..)
+ mnesia_lib:unset({Tab, {index, Pos}});
+
+delete_transient_index(Tab, Pos, _Storage) ->
+ Ixt = val({Tab, {index, Pos}}),
+ ?ets_delete_table(Ixt),
+ del_index_info(Tab, Pos),
+ mnesia_lib:unset({Tab, {index, Pos}}).
+
+%%%%% misc functions for the index create/init/delete functions above
+
+%% assuming that the file exists.
+init_disc_index(_Tab, []) ->
+ done;
+init_disc_index(Tab, [Pos | Tail]) when integer(Pos) ->
+ Fn = tab2filename(Tab, Pos),
+ IxTag = {Tab, index, Pos},
+ file:delete(Fn),
+ Args = [{file, Fn}, {keypos, 1}, {type, bag}],
+ mnesia_monitor:open_dets(IxTag, Args),
+ Storage = disc_only_copies,
+ Key = mnesia_lib:db_first(Storage, Tab),
+ Recs = mnesia_lib:db_get(Storage, Tab, Key),
+ BinSize = size(term_to_binary(Recs)),
+ KeysPerChunk = (4000 div BinSize) + 1,
+ Init = {start, KeysPerChunk},
+ mnesia_lib:db_fixtable(Storage, Tab, true),
+ ok = dets:init_table(IxTag, create_fun(Init, Tab, Pos)),
+ mnesia_lib:db_fixtable(Storage, Tab, false),
+ mnesia_lib:set({Tab, {index, Pos}}, IxTag),
+ add_index_info(Tab, val({Tab, setorbag}), {Pos, {dets, IxTag}}),
+ init_disc_index(Tab, Tail).
+
+create_fun(Cont, Tab, Pos) ->
+ fun(read) ->
+ Data =
+ case Cont of
+ {start, KeysPerChunk} ->
+ mnesia_lib:db_init_chunk(disc_only_copies, Tab, KeysPerChunk);
+ '$end_of_table' ->
+ '$end_of_table';
+ _Else ->
+ mnesia_lib:db_chunk(disc_only_copies, Cont)
+ end,
+ case Data of
+ '$end_of_table' ->
+ end_of_input;
+ {Recs, Next} ->
+ IdxElems = [{element(Pos, Obj), element(2, Obj)} || Obj <- Recs],
+ {IdxElems, create_fun(Next, Tab, Pos)}
+ end;
+ (close) ->
+ ok
+ end.
+
+make_ram_index(_, []) ->
+ done;
+make_ram_index(Tab, [Pos | Tail]) ->
+ add_ram_index(Tab, Pos),
+ make_ram_index(Tab, Tail).
+
+add_ram_index(Tab, Pos) when integer(Pos) ->
+ verbose("Creating index for ~w ~n", [Tab]),
+ Index = mnesia_monitor:mktab(mnesia_index, [bag, public]),
+ Insert = fun(Rec, _Acc) ->
+ true = ?ets_insert(Index, {element(Pos, Rec), element(2, Rec)})
+ end,
+ mnesia_lib:db_fixtable(ram_copies, Tab, true),
+ true = ets:foldl(Insert, true, Tab),
+ mnesia_lib:db_fixtable(ram_copies, Tab, false),
+ mnesia_lib:set({Tab, {index, Pos}}, Index),
+ add_index_info(Tab, val({Tab, setorbag}), {Pos, {ram, Index}});
+add_ram_index(_Tab, snmp) ->
+ ok.
+
+add_index_info(Tab, Type, IxElem) ->
+ Commit = val({Tab, commit_work}),
+ case lists:keysearch(index, 1, Commit) of
+ false ->
+ Index = #index{setorbag = Type,
+ pos_list = [IxElem]},
+ %% Check later if mnesia_tm is sensative about the order
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit([Index | Commit]));
+ {value, Old} ->
+ %% We could check for consistency here
+ Index = Old#index{pos_list = [IxElem | Old#index.pos_list]},
+ NewC = lists:keyreplace(index, 1, Commit, Index),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit(NewC))
+ end.
+
+del_index_info(Tab, Pos) ->
+ Commit = val({Tab, commit_work}),
+ case lists:keysearch(index, 1, Commit) of
+ false ->
+ %% Something is wrong ignore
+ skip;
+ {value, Old} ->
+ case lists:keydelete(Pos, 1, Old#index.pos_list) of
+ [] ->
+ NewC = lists:keydelete(index, 1, Commit),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit(NewC));
+ New ->
+ Index = Old#index{pos_list = New},
+ NewC = lists:keyreplace(index, 1, Commit, Index),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit(NewC))
+ end
+ end.
+
+db_put({ram, Ixt}, V) ->
+ true = ?ets_insert(Ixt, V);
+db_put({dets, Ixt}, V) ->
+ ok = dets:insert(Ixt, V).
+
+db_get({ram, Ixt}, K) ->
+ ?ets_lookup(Ixt, K);
+db_get({dets, Ixt}, K) ->
+ dets:lookup(Ixt, K).
+
+db_match_erase({ram, Ixt}, Pat) ->
+ true = ?ets_match_delete(Ixt, Pat);
+db_match_erase({dets, Ixt}, Pat) ->
+ ok = dets:match_delete(Ixt, Pat).
+
+db_match({ram, Ixt}, Pat) ->
+ ?ets_match(Ixt, Pat);
+db_match({dets, Ixt}, Pat) ->
+ dets:match(Ixt, Pat).
+
+get_index_table(Tab, Pos) ->
+ get_index_table(Tab, val({Tab, storage_type}), Pos).
+
+get_index_table(Tab, ram_copies, Pos) ->
+ {ram, val({Tab, {index, Pos}})};
+get_index_table(Tab, disc_copies, Pos) ->
+ {ram, val({Tab, {index, Pos}})};
+get_index_table(Tab, disc_only_copies, Pos) ->
+ {dets, val({Tab, {index, Pos}})};
+get_index_table(_Tab, unknown, _Pos) ->
+ unknown.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_kernel_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_kernel_sup.erl
new file mode 100644
index 0000000000..899d434fdd
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_kernel_sup.erl
@@ -0,0 +1,62 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_kernel_sup.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_kernel_sup).
+
+-behaviour(supervisor).
+
+-export([start/0, init/1, supervisor_timeout/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% top supervisor callback functions
+
+start() ->
+ supervisor:start_link({local, mnesia_kernel_sup}, ?MODULE, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% sub supervisor callback functions
+
+init([]) ->
+ ProcLib = [mnesia_monitor, proc_lib],
+ Flags = {one_for_all, 0, timer:hours(24)}, % Trust the top supervisor
+ Workers = [worker_spec(mnesia_monitor, timer:seconds(3), [gen_server]),
+ worker_spec(mnesia_subscr, timer:seconds(3), [gen_server]),
+ worker_spec(mnesia_locker, timer:seconds(3), ProcLib),
+ worker_spec(mnesia_recover, timer:minutes(3), [gen_server]),
+ worker_spec(mnesia_tm, timer:seconds(30), ProcLib),
+ supervisor_spec(mnesia_checkpoint_sup),
+ supervisor_spec(mnesia_snmp_sup),
+ worker_spec(mnesia_controller, timer:seconds(3), [gen_server]),
+ worker_spec(mnesia_late_loader, timer:seconds(3), ProcLib)
+ ],
+ {ok, {Flags, Workers}}.
+
+worker_spec(Name, KillAfter, Modules) ->
+ KA = supervisor_timeout(KillAfter),
+ {Name, {Name, start, []}, permanent, KA, worker, [Name] ++ Modules}.
+
+supervisor_spec(Name) ->
+ {Name, {Name, start, []}, permanent, infinity, supervisor,
+ [Name, supervisor]}.
+
+-ifdef(debug_shutdown).
+supervisor_timeout(_KillAfter) -> timer:hours(24).
+-else.
+supervisor_timeout(KillAfter) -> KillAfter.
+-endif.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_late_loader.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_late_loader.erl
new file mode 100644
index 0000000000..96d00f6e81
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_late_loader.erl
@@ -0,0 +1,95 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_late_loader.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_late_loader).
+
+-export([
+ async_late_disc_load/3,
+ maybe_async_late_disc_load/3,
+ init/1,
+ start/0
+ ]).
+
+%% sys callback functions
+-export([
+ system_continue/3,
+ system_terminate/4,
+ system_code_change/4
+ ]).
+
+-define(SERVER_NAME, ?MODULE).
+
+-record(state, {supervisor}).
+
+async_late_disc_load(Node, Tabs, Reason) ->
+ Msg = {async_late_disc_load, Tabs, Reason},
+ catch ({?SERVER_NAME, Node} ! {self(), Msg}).
+
+maybe_async_late_disc_load(Node, Tabs, Reason) ->
+ Msg = {maybe_async_late_disc_load, Tabs, Reason},
+ catch ({?SERVER_NAME, Node} ! {self(), Msg}).
+
+start() ->
+ mnesia_monitor:start_proc(?SERVER_NAME, ?MODULE, init, [self()]).
+
+init(Parent) ->
+ %% Trap exit omitted intentionally
+ register(?SERVER_NAME, self()),
+ link(whereis(mnesia_controller)), %% We may not hang
+ mnesia_controller:merge_schema(),
+ unlink(whereis(mnesia_controller)),
+ mnesia_lib:set(mnesia_status, running),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ loop(#state{supervisor = Parent}).
+
+loop(State) ->
+ receive
+ {_From, {async_late_disc_load, Tabs, Reason}} ->
+ mnesia_controller:schedule_late_disc_load(Tabs, Reason),
+ loop(State);
+
+ {_From, {maybe_async_late_disc_load, Tabs, Reason}} ->
+ GoodTabs =
+ [T || T <- Tabs,
+ lists:member(node(),
+ mnesia_recover:get_master_nodes(T))],
+ mnesia_controller:schedule_late_disc_load(GoodTabs, Reason),
+ loop(State);
+
+ {system, From, Msg} ->
+ mnesia_lib:dbg_out("~p got {system, ~p, ~p}~n",
+ [?SERVER_NAME, From, Msg]),
+ Parent = State#state.supervisor,
+ sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], State);
+
+ Msg ->
+ mnesia_lib:error("~p got unexpected message: ~p~n",
+ [?SERVER_NAME, Msg]),
+ loop(State)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% System upgrade
+
+system_continue(_Parent, _Debug, State) ->
+ loop(State).
+
+system_terminate(Reason, _Parent, _Debug, _State) ->
+ exit(Reason).
+
+system_code_change(State, _Module, _OldVsn, _Extra) ->
+ {ok, State}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_lib.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_lib.erl
new file mode 100644
index 0000000000..2c9e4d4fcf
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_lib.erl
@@ -0,0 +1,1278 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_lib.erl,v 1.3 2009/07/01 15:45:40 kostis Exp $
+%%
+%% This module contains all sorts of various which doesn't fit
+%% anywhere else. Basically everything is exported.
+
+-module(mnesia_lib).
+
+-include("mnesia.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-export([core_file/0]).
+
+-export([
+ active_tables/0,
+ add/2,
+ add_list/2,
+ all_nodes/0,
+%% catch_val/1,
+ cleanup_tmp_files/1,
+ copy_file/2,
+ copy_holders/1,
+ coredump/0,
+ coredump/1,
+ create_counter/1,
+ cs_to_nodes/1,
+ cs_to_storage_type/2,
+ dets_to_ets/6,
+ db_chunk/2,
+ db_init_chunk/1,
+ db_init_chunk/2,
+ db_init_chunk/3,
+ db_erase/2,
+ db_erase/3,
+ db_erase_tab/1,
+ db_erase_tab/2,
+ db_first/1,
+ db_first/2,
+ db_last/1,
+ db_last/2,
+ db_fixtable/3,
+ db_get/2,
+ db_get/3,
+ db_match_erase/2,
+ db_match_erase/3,
+ db_match_object/2,
+ db_match_object/3,
+ db_next_key/2,
+ db_next_key/3,
+ db_prev_key/2,
+ db_prev_key/3,
+ db_put/2,
+ db_put/3,
+ db_select/2,
+ db_select/3,
+ db_slot/2,
+ db_slot/3,
+ db_update_counter/3,
+ db_update_counter/4,
+ dbg_out/2,
+ del/2,
+ dets_sync_close/1,
+ dets_sync_open/2,
+ dets_sync_open/3,
+ dir/0,
+ dir/1,
+ dir_info/0,
+ dirty_rpc_error_tag/1,
+ dist_coredump/0,
+ disk_type/1,
+ disk_type/2,
+ elems/2,
+ ensure_loaded/1,
+ error/2,
+ error_desc/1,
+ etype/1,
+ exists/1,
+ fatal/2,
+ get_node_number/0,
+ fix_error/1,
+ important/2,
+ incr_counter/1,
+ incr_counter/2,
+ intersect/2,
+ is_running/0,
+ is_running/1,
+ is_running_remote/0,
+ is_string/1,
+ key_search_delete/3,
+ key_search_all/3,
+ last_error/0,
+ local_active_tables/0,
+ lock_table/1,
+ mkcore/1,
+ not_active_here/1,
+ other_val/2,
+ pad_name/3,
+ random_time/2,
+ read_counter/1,
+ readable_indecies/1,
+ remote_copy_holders/1,
+ report_fatal/2,
+ report_system_event/1,
+ running_nodes/0,
+ running_nodes/1,
+ schema_cs_to_storage_type/2,
+ search_delete/2,
+ set/2,
+ set_counter/2,
+ set_local_content_whereabouts/1,
+ set_remote_where_to_read/1,
+ set_remote_where_to_read/2,
+ show/1,
+ show/2,
+ sort_commit/1,
+ storage_type_at_node/2,
+ swap_tmp_files/1,
+ tab2dat/1,
+ tab2dmp/1,
+ tab2tmp/1,
+ tab2dcd/1,
+ tab2dcl/1,
+ to_list/1,
+ union/2,
+ uniq/1,
+ unlock_table/1,
+ unset/1,
+ update_counter/2,
+ val/1,
+ vcore/0,
+ vcore/1,
+ verbose/2,
+ view/0,
+ view/1,
+ view/2,
+ warning/2,
+
+ is_debug_compiled/0,
+ activate_debug_fun/5,
+ deactivate_debug_fun/3,
+ eval_debug_fun/4,
+ scratch_debug_fun/0
+ ]).
+
+
+search_delete(Obj, List) ->
+ search_delete(Obj, List, [], none).
+search_delete(Obj, [Obj|Tail], Ack, _Res) ->
+ search_delete(Obj, Tail, Ack, Obj);
+search_delete(Obj, [H|T], Ack, Res) ->
+ search_delete(Obj, T, [H|Ack], Res);
+search_delete(_, [], Ack, Res) ->
+ {Res, Ack}.
+
+key_search_delete(Key, Pos, TupleList) ->
+ key_search_delete(Key, Pos, TupleList, none, []).
+key_search_delete(Key, Pos, [H|T], _Obj, Ack) when element(Pos, H) == Key ->
+ key_search_delete(Key, Pos, T, H, Ack);
+key_search_delete(Key, Pos, [H|T], Obj, Ack) ->
+ key_search_delete(Key, Pos, T, Obj, [H|Ack]);
+key_search_delete(_, _, [], Obj, Ack) ->
+ {Obj, Ack}.
+
+key_search_all(Key, Pos, TupleList) ->
+ key_search_all(Key, Pos, TupleList, []).
+key_search_all(Key, N, [H|T], Ack) when element(N, H) == Key ->
+ key_search_all(Key, N, T, [H|Ack]);
+key_search_all(Key, N, [_|T], Ack) ->
+ key_search_all(Key, N, T, Ack);
+key_search_all(_, _, [], Ack) -> Ack.
+
+intersect(L1, L2) ->
+ L2 -- (L2 -- L1).
+
+elems(I, [H|T]) ->
+ [element(I, H) | elems(I, T)];
+elems(_, []) ->
+ [].
+
+%% sort_commit see to that checkpoint info is always first in
+%% commit_work structure the other info don't need to be sorted.
+sort_commit(List) ->
+ sort_commit2(List, []).
+
+sort_commit2([{checkpoints, ChkpL}| Rest], Acc) ->
+ [{checkpoints, ChkpL}| Rest] ++ Acc;
+sort_commit2([H | R], Acc) ->
+ sort_commit2(R, [H | Acc]);
+sort_commit2([], Acc) -> Acc.
+
+is_string([H|T]) ->
+ if
+ 0 =< H, H < 256, integer(H) -> is_string(T);
+ true -> false
+ end;
+is_string([]) -> true.
+
+%%%
+
+union([H|L1], L2) ->
+ case lists:member(H, L2) of
+ true -> union(L1, L2);
+ false -> [H | union(L1, L2)]
+ end;
+union([], L2) -> L2.
+
+uniq([]) ->
+ [];
+uniq(List) ->
+ [H|T] = lists:sort(List),
+ uniq1(H, T, []).
+
+uniq1(H, [H|R], Ack) ->
+ uniq1(H, R, Ack);
+uniq1(Old, [H|R], Ack) ->
+ uniq1(H, R, [Old|Ack]);
+uniq1(Old, [], Ack) ->
+ [Old| Ack].
+
+to_list(X) when list(X) -> X;
+to_list(X) -> atom_to_list(X).
+
+all_nodes() ->
+ Ns = mnesia:system_info(db_nodes) ++
+ mnesia:system_info(extra_db_nodes),
+ mnesia_lib:uniq(Ns).
+
+running_nodes() ->
+ running_nodes(all_nodes()).
+
+running_nodes(Ns) ->
+ {Replies, _BadNs} = rpc:multicall(Ns, ?MODULE, is_running_remote, []),
+ [N || {GoodState, N} <- Replies, GoodState == true].
+
+is_running_remote() ->
+ IsRunning = is_running(),
+ {IsRunning == yes, node()}.
+
+is_running(Node) when atom(Node) ->
+ case rpc:call(Node, ?MODULE, is_running, []) of
+ {badrpc, _} -> no;
+ X -> X
+ end.
+
+is_running() ->
+ case ?catch_val(mnesia_status) of
+ {'EXIT', _} -> no;
+ running -> yes;
+ starting -> starting;
+ stopping -> stopping
+ end.
+
+show(X) ->
+ show(X, []).
+show(F, A) ->
+ io:format(user, F, A).
+
+
+pad_name([Char | Chars], Len, Tail) ->
+ [Char | pad_name(Chars, Len - 1, Tail)];
+pad_name([], Len, Tail) when Len =< 0 ->
+ Tail;
+pad_name([], Len, Tail) ->
+ [$ | pad_name([], Len - 1, Tail)].
+
+%% Some utility functions .....
+active_here(Tab) ->
+ case val({Tab, where_to_read}) of
+ Node when Node == node() -> true;
+ _ -> false
+ end.
+
+not_active_here(Tab) ->
+ not active_here(Tab).
+
+exists(Fname) ->
+ case file:open(Fname, [raw,read]) of
+ {ok, F} ->file:close(F), true;
+ _ -> false
+ end.
+
+dir() -> mnesia_monitor:get_env(dir).
+
+dir(Fname) ->
+ filename:join([dir(), to_list(Fname)]).
+
+tab2dat(Tab) -> %% DETS files
+ dir(lists:concat([Tab, ".DAT"])).
+
+tab2tmp(Tab) ->
+ dir(lists:concat([Tab, ".TMP"])).
+
+tab2dmp(Tab) -> %% Dumped ets tables
+ dir(lists:concat([Tab, ".DMP"])).
+
+tab2dcd(Tab) -> %% Disc copies data
+ dir(lists:concat([Tab, ".DCD"])).
+
+tab2dcl(Tab) -> %% Disc copies log
+ dir(lists:concat([Tab, ".DCL"])).
+
+storage_type_at_node(Node, Tab) ->
+ search_key(Node, [{disc_copies, val({Tab, disc_copies})},
+ {ram_copies, val({Tab, ram_copies})},
+ {disc_only_copies, val({Tab, disc_only_copies})}]).
+
+cs_to_storage_type(Node, Cs) ->
+ search_key(Node, [{disc_copies, Cs#cstruct.disc_copies},
+ {ram_copies, Cs#cstruct.ram_copies},
+ {disc_only_copies, Cs#cstruct.disc_only_copies}]).
+
+schema_cs_to_storage_type(Node, Cs) ->
+ case cs_to_storage_type(Node, Cs) of
+ unknown when Cs#cstruct.name == schema -> ram_copies;
+ Other -> Other
+ end.
+
+
+search_key(Key, [{Val, List} | Tail]) ->
+ case lists:member(Key, List) of
+ true -> Val;
+ false -> search_key(Key, Tail)
+ end;
+search_key(_Key, []) ->
+ unknown.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% ops, we've got some global variables here :-)
+
+%% They are
+%%
+%% {Tab, setorbag}, -> set | bag
+%% {Tab, storage_type} -> disc_copies |ram_copies | unknown (**)
+%% {Tab, disc_copies} -> node list (from schema)
+%% {Tab, ram_copies}, -> node list (from schema)
+%% {Tab, arity}, -> number
+%% {Tab, attributes}, -> atom list
+%% {Tab, wild_pattern}, -> record tuple with '_'s
+%% {Tab, {index, Pos}} -> ets table
+%% {Tab, index} -> integer list
+%% {Tab, cstruct} -> cstruct structure
+%%
+
+%% The following fields are dynamic according to the
+%% the current node/table situation
+
+%% {Tab, where_to_write} -> node list
+%% {Tab, where_to_read} -> node | nowhere
+%%
+%% {schema, tables} -> tab list
+%% {schema, local_tables} -> tab list (**)
+%%
+%% {current, db_nodes} -> node list
+%%
+%% dir -> directory path (**)
+%% mnesia_status -> status | running | stopping (**)
+%% (**) == (Different on all nodes)
+%%
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ _VaLuE_ -> _VaLuE_
+ end.
+
+set(Var, Val) ->
+ ?ets_insert(mnesia_gvar, {Var, Val}).
+
+unset(Var) ->
+ ?ets_delete(mnesia_gvar, Var).
+
+other_val(Var, Other) ->
+ case Var of
+ {_, where_to_read} -> nowhere;
+ {_, where_to_write} -> [];
+ {_, active_replicas} -> [];
+ _ ->
+ pr_other(Var, Other)
+ end.
+
+pr_other(Var, Other) ->
+ Why =
+ case is_running() of
+ no -> {node_not_running, node()};
+ _ -> {no_exists, Var}
+ end,
+ verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~p ~n",
+ [self(), process_info(self(), registered_name),
+ Var, Other, Why]),
+ case Other of
+ {badarg, [{ets, lookup_element, _}|_]} ->
+ exit(Why);
+ _ ->
+ erlang:error(Why)
+ end.
+
+%% Some functions for list valued variables
+add(Var, Val) ->
+ L = val(Var),
+ set(Var, [Val | lists:delete(Val, L)]).
+
+add_list(Var, List) ->
+ L = val(Var),
+ set(Var, union(L, List)).
+
+del(Var, Val) ->
+ L = val(Var),
+ set(Var, lists:delete(Val, L)).
+
+%% This function is needed due to the fact
+%% that the application_controller enters
+%% a deadlock now and then. ac is implemented
+%% as a rather naive server.
+ensure_loaded(Appl) ->
+ case application_controller:get_loaded(Appl) of
+ {true, _} ->
+ ok;
+ false ->
+ case application:load(Appl) of
+ ok ->
+ ok;
+ {error, {already_loaded, Appl}} ->
+ ok;
+ {error, Reason} ->
+ {error, {application_load_error, Reason}}
+ end
+ end.
+
+local_active_tables() ->
+ Tabs = val({schema, local_tables}),
+ lists:zf(fun(Tab) -> active_here(Tab) end, Tabs).
+
+active_tables() ->
+ Tabs = val({schema, tables}),
+ F = fun(Tab) ->
+ case val({Tab, where_to_read}) of
+ nowhere -> false;
+ _ -> {true, Tab}
+ end
+ end,
+ lists:zf(F, Tabs).
+
+etype(X) when integer(X) -> integer;
+etype([]) -> nil;
+etype(X) when list(X) -> list;
+etype(X) when tuple(X) -> tuple;
+etype(X) when atom(X) -> atom;
+etype(_) -> othertype.
+
+remote_copy_holders(Cs) ->
+ copy_holders(Cs) -- [node()].
+
+copy_holders(Cs) when Cs#cstruct.local_content == false ->
+ cs_to_nodes(Cs);
+copy_holders(Cs) when Cs#cstruct.local_content == true ->
+ case lists:member(node(), cs_to_nodes(Cs)) of
+ true -> [node()];
+ false -> []
+ end.
+
+
+set_remote_where_to_read(Tab) ->
+ set_remote_where_to_read(Tab, []).
+
+set_remote_where_to_read(Tab, Ignore) ->
+ Active = val({Tab, active_replicas}),
+ Valid =
+ case mnesia_recover:get_master_nodes(Tab) of
+ [] -> Active;
+ Masters -> mnesia_lib:intersect(Masters, Active)
+ end,
+ Available = mnesia_lib:intersect(val({current, db_nodes}), Valid -- Ignore),
+ DiscOnlyC = val({Tab, disc_only_copies}),
+ Prefered = Available -- DiscOnlyC,
+ if
+ Prefered /= [] ->
+ set({Tab, where_to_read}, hd(Prefered));
+ Available /= [] ->
+ set({Tab, where_to_read}, hd(Available));
+ true ->
+ set({Tab, where_to_read}, nowhere)
+ end.
+
+%%% Local only
+set_local_content_whereabouts(Tab) ->
+ add({schema, local_tables}, Tab),
+ add({Tab, active_replicas}, node()),
+ set({Tab, where_to_write}, [node()]),
+ set({Tab, where_to_read}, node()).
+
+%%% counter routines
+
+create_counter(Name) ->
+ set_counter(Name, 0).
+
+set_counter(Name, Val) ->
+ ?ets_insert(mnesia_gvar, {Name, Val}).
+
+incr_counter(Name) ->
+ ?ets_update_counter(mnesia_gvar, Name, 1).
+
+incr_counter(Name, I) ->
+ ?ets_update_counter(mnesia_gvar, Name, I).
+
+update_counter(Name, Val) ->
+ ?ets_update_counter(mnesia_gvar, Name, Val).
+
+read_counter(Name) ->
+ ?ets_lookup_element(mnesia_gvar, Name, 2).
+
+cs_to_nodes(Cs) ->
+ Cs#cstruct.disc_only_copies ++
+ Cs#cstruct.disc_copies ++
+ Cs#cstruct.ram_copies.
+
+dist_coredump() ->
+ dist_coredump(all_nodes()).
+dist_coredump(Ns) ->
+ {Replies, _} = rpc:multicall(Ns, ?MODULE, coredump, []),
+ Replies.
+
+coredump() ->
+ coredump({crashinfo, {"user initiated~n", []}}).
+coredump(CrashInfo) ->
+ Core = mkcore(CrashInfo),
+ Out = core_file(),
+ important("Writing Mnesia core to file: ~p...~p~n", [Out, CrashInfo]),
+ file:write_file(Out, Core),
+ Out.
+
+core_file() ->
+ Integers = tuple_to_list(date()) ++ tuple_to_list(time()),
+ Fun = fun(I) when I < 10 -> ["_0", I];
+ (I) -> ["_", I]
+ end,
+ List = lists:append([Fun(I) || I <- Integers]),
+ filename:absname(lists:concat(["MnesiaCore.", node()] ++ List)).
+
+mkcore(CrashInfo) ->
+% dbg_out("Making a Mnesia core dump...~p~n", [CrashInfo]),
+ Nodes = [node() |nodes()],
+ TidLocks = (catch ets:tab2list(mnesia_tid_locks)),
+ Core = [
+ CrashInfo,
+ {time, {date(), time()}},
+ {self, catch process_info(self())},
+ {nodes, catch rpc:multicall(Nodes, ?MODULE, get_node_number, [])},
+ {applications, catch lists:sort(application:loaded_applications())},
+ {flags, catch init:get_arguments()},
+ {code_path, catch code:get_path()},
+ {code_loaded, catch lists:sort(code:all_loaded())},
+ {etsinfo, catch ets_info(ets:all())},
+
+ {version, catch mnesia:system_info(version)},
+ {schema, catch ets:tab2list(schema)},
+ {gvar, catch ets:tab2list(mnesia_gvar)},
+ {master_nodes, catch mnesia_recover:get_master_node_info()},
+
+ {processes, catch procs()},
+ {relatives, catch relatives()},
+ {workers, catch workers(mnesia_controller:get_workers(2000))},
+ {locking_procs, catch locking_procs(TidLocks)},
+
+ {held_locks, catch mnesia:system_info(held_locks)},
+ {tid_locks, TidLocks},
+ {lock_queue, catch mnesia:system_info(lock_queue)},
+ {load_info, catch mnesia_controller:get_info(2000)},
+ {trans_info, catch mnesia_tm:get_info(2000)},
+
+ {schema_file, catch file:read_file(tab2dat(schema))},
+ {dir_info, catch dir_info()},
+ {logfile, catch {ok, read_log_files()}}
+ ],
+ term_to_binary(Core).
+
+procs() ->
+ Fun = fun(P) -> {P, (catch lists:zf(fun proc_info/1, process_info(P)))} end,
+ lists:map(Fun, processes()).
+
+proc_info({registered_name, Val}) -> {true, Val};
+proc_info({message_queue_len, Val}) -> {true, Val};
+proc_info({status, Val}) -> {true, Val};
+proc_info({current_function, Val}) -> {true, Val};
+proc_info(_) -> false.
+
+get_node_number() ->
+ {node(), self()}.
+
+read_log_files() ->
+ [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()].
+
+dir_info() ->
+ {ok, Cwd} = file:get_cwd(),
+ Dir = dir(),
+ [{cwd, Cwd, file:read_file_info(Cwd)},
+ {mnesia_dir, Dir, file:read_file_info(Dir)}] ++
+ case file:list_dir(Dir) of
+ {ok, Files} ->
+ [{mnesia_file, F, catch file:read_file_info(dir(F))} || F <- Files];
+ Other ->
+ [Other]
+ end.
+
+ets_info([H|T]) ->
+ [{table, H, ets:info(H)} | ets_info(T)];
+ets_info([]) -> [].
+
+relatives() ->
+ Info = fun(Name) ->
+ case whereis(Name) of
+ undefined -> false;
+ Pid -> {true, {Name, Pid, catch process_info(Pid)}}
+ end
+ end,
+ lists:zf(Info, mnesia:ms()).
+
+workers({workers, Loader, Sender, Dumper}) ->
+ Info = fun({Name, Pid}) ->
+ case Pid of
+ undefined -> false;
+ Pid -> {true, {Name, Pid, catch process_info(Pid)}}
+ end
+ end,
+ lists:zf(Info, [{loader, Loader}, {sender, Sender}, {dumper, Dumper}]).
+
+locking_procs(LockList) when list(LockList) ->
+ Tids = [element(1, Lock) || Lock <- LockList],
+ UT = uniq(Tids),
+ Info = fun(Tid) ->
+ Pid = Tid#tid.pid,
+ case node(Pid) == node() of
+ true ->
+ {true, {Pid, catch process_info(Pid)}};
+ _ ->
+ false
+ end
+ end,
+ lists:zf(Info, UT).
+
+view() ->
+ Bin = mkcore({crashinfo, {"view only~n", []}}),
+ vcore(Bin).
+
+%% Displays a Mnesia file on the tty. The file may be repaired.
+view(File) ->
+ case suffix([".DAT", ".RET", ".DMP", ".TMP"], File) of
+ true ->
+ view(File, dat);
+ false ->
+ case suffix([".LOG", ".BUP", ".ETS"], File) of
+ true ->
+ view(File, log);
+ false ->
+ case lists:prefix("MnesiaCore.", File) of
+ true ->
+ view(File, core);
+ false ->
+ {error, "Unknown file name"}
+ end
+ end
+ end.
+
+view(File, dat) ->
+ dets:view(File);
+view(File, log) ->
+ mnesia_log:view(File);
+view(File, core) ->
+ vcore(File).
+
+suffix(Suffixes, File) ->
+ Fun = fun(S) -> lists:suffix(S, File) end,
+ lists:any(Fun, Suffixes).
+
+%% View a core file
+
+vcore() ->
+ Prefix = lists:concat(["MnesiaCore.", node()]),
+ Filter = fun(F) -> lists:prefix(Prefix, F) end,
+ {ok, Cwd} = file:get_cwd(),
+ case file:list_dir(Cwd) of
+ {ok, Files}->
+ CoreFiles = lists:sort(lists:zf(Filter, Files)),
+ show("Mnesia core files: ~p~n", [CoreFiles]),
+ vcore(lists:last(CoreFiles));
+ Error ->
+ Error
+ end.
+
+vcore(Bin) when binary(Bin) ->
+ Core = binary_to_term(Bin),
+ Fun = fun({Item, Info}) ->
+ show("***** ~p *****~n", [Item]),
+ case catch vcore_elem({Item, Info}) of
+ {'EXIT', Reason} ->
+ show("{'EXIT', ~p}~n", [Reason]);
+ _ -> ok
+ end
+ end,
+ lists:foreach(Fun, Core);
+
+vcore(File) ->
+ show("~n***** Mnesia core: ~p *****~n", [File]),
+ case file:read_file(File) of
+ {ok, Bin} ->
+ vcore(Bin);
+ _ ->
+ nocore
+ end.
+
+vcore_elem({schema_file, {ok, B}}) ->
+ Fname = "/tmp/schema.DAT",
+ file:write_file(Fname, B),
+ dets:view(Fname),
+ file:delete(Fname);
+
+vcore_elem({logfile, {ok, BinList}}) ->
+ Fun = fun({F, Info}) ->
+ show("----- logfile: ~p -----~n", [F]),
+ case Info of
+ {ok, B} ->
+ Fname = "/tmp/mnesia_vcore_elem.TMP",
+ file:write_file(Fname, B),
+ mnesia_log:view(Fname),
+ file:delete(Fname);
+ _ ->
+ show("~p~n", [Info])
+ end
+ end,
+ lists:foreach(Fun, BinList);
+
+vcore_elem({crashinfo, {Format, Args}}) ->
+ show(Format, Args);
+vcore_elem({gvar, L}) ->
+ show("~p~n", [lists:sort(L)]);
+vcore_elem({transactions, Info}) ->
+ mnesia_tm:display_info(user, Info);
+
+vcore_elem({_Item, Info}) ->
+ show("~p~n", [Info]).
+
+fix_error(X) ->
+ set(last_error, X), %% for debugabililty
+ case X of
+ {aborted, Reason} -> Reason;
+ {abort, Reason} -> Reason;
+ Y when atom(Y) -> Y;
+ {'EXIT', {_Reason, {Mod, _, _}}} when atom(Mod) ->
+ save(X),
+ case atom_to_list(Mod) of
+ [$m, $n, $e|_] -> badarg;
+ _ -> X
+ end;
+ _ -> X
+ end.
+
+last_error() ->
+ val(last_error).
+
+%% The following is a list of possible mnesia errors and what they
+%% actually mean
+
+error_desc(nested_transaction) -> "Nested transactions are not allowed";
+error_desc(badarg) -> "Bad or invalid argument, possibly bad type";
+error_desc(no_transaction) -> "Operation not allowed outside transactions";
+error_desc(combine_error) -> "Table options were ilegally combined";
+error_desc(bad_index) -> "Index already exists or was out of bounds";
+error_desc(already_exists) -> "Some schema option we try to set is already on";
+error_desc(index_exists)-> "Some ops can not be performed on tabs with index";
+error_desc(no_exists)-> "Tried to perform op on non-existing (non alive) item";
+error_desc(system_limit) -> "Some system_limit was exhausted";
+error_desc(mnesia_down) -> "A transaction involving objects at some remote "
+ "node which died while transaction was executing"
+ "*and* object(s) are no longer available elsewhere"
+ "in the network";
+error_desc(not_a_db_node) -> "A node which is non existant in "
+ "the schema was mentioned";
+error_desc(bad_type) -> "Bad type on some provided arguments";
+error_desc(node_not_running) -> "Node not running";
+error_desc(truncated_binary_file) -> "Truncated binary in file";
+error_desc(active) -> "Some delete ops require that "
+ "all active objects are removed";
+error_desc(illegal) -> "Operation not supported on object";
+error_desc({'EXIT', Reason}) ->
+ error_desc(Reason);
+error_desc({error, Reason}) ->
+ error_desc(Reason);
+error_desc({aborted, Reason}) ->
+ error_desc(Reason);
+error_desc(Reason) when tuple(Reason), size(Reason) > 0 ->
+ setelement(1, Reason, error_desc(element(1, Reason)));
+error_desc(Reason) ->
+ Reason.
+
+dirty_rpc_error_tag(Reason) ->
+ case Reason of
+ {'EXIT', _} -> badarg;
+ no_variable -> badarg;
+ _ -> no_exists
+ end.
+
+fatal(Format, Args) ->
+ catch set(mnesia_status, stopping),
+ Core = mkcore({crashinfo, {Format, Args}}),
+ report_fatal(Format, Args, Core),
+ timer:sleep(10000), % Enough to write the core dump to disc?
+ mnesia:lkill(),
+ exit(fatal).
+
+report_fatal(Format, Args) ->
+ report_fatal(Format, Args, nocore).
+
+report_fatal(Format, Args, Core) ->
+ report_system_event({mnesia_fatal, Format, Args, Core}),
+ catch exit(whereis(mnesia_monitor), fatal).
+
+%% We sleep longer and longer the more we try
+%% Made some testing and came up with the following constants
+random_time(Retries, _Counter0) ->
+% UpperLimit = 2000,
+% MaxIntv = trunc(UpperLimit * (1-(4/((Retries*Retries)+4)))),
+ UpperLimit = 500,
+ Dup = Retries * Retries,
+ MaxIntv = trunc(UpperLimit * (1-(50/((Dup)+50)))),
+
+ case get(random_seed) of
+ undefined ->
+ {X, Y, Z} = erlang:now(), %% time()
+ random:seed(X, Y, Z),
+ Time = Dup + random:uniform(MaxIntv),
+ %% dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
+ Time;
+ _ ->
+ Time = Dup + random:uniform(MaxIntv),
+ %% dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
+ Time
+ end.
+
+report_system_event(Event0) ->
+ Event = {mnesia_system_event, Event0},
+ report_system_event(catch_notify(Event), Event),
+ case ?catch_val(subscribers) of
+ {'EXIT', _} -> ignore;
+ Pids -> lists:foreach(fun(Pid) -> Pid ! Event end, Pids)
+ end,
+ ok.
+
+catch_notify(Event) ->
+ case whereis(mnesia_event) of
+ undefined ->
+ {'EXIT', {badarg, {mnesia_event, Event}}};
+ Pid ->
+ gen_event:notify(Pid, Event)
+ end.
+
+report_system_event({'EXIT', Reason}, Event) ->
+ Mod = mnesia_monitor:get_env(event_module),
+ case mnesia_sup:start_event() of
+ {ok, Pid} ->
+ link(Pid),
+ gen_event:call(mnesia_event, Mod, Event, infinity),
+ unlink(Pid),
+
+ %% We get an exit signal if server dies
+ receive
+ {'EXIT', Pid, _Reason} ->
+ {error, {node_not_running, node()}}
+ after 0 ->
+ gen_event:stop(mnesia_event),
+ ok
+ end;
+
+ Error ->
+ Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n",
+ error_logger:format(Msg, [node(), Event, Reason, Error])
+ end;
+report_system_event(_Res, _Event) ->
+ ignore.
+
+%% important messages are reported regardless of debug level
+important(Format, Args) ->
+ save({Format, Args}),
+ report_system_event({mnesia_info, Format, Args}).
+
+%% Warning messages are reported regardless of debug level
+warning(Format, Args) ->
+ save({Format, Args}),
+ report_system_event({mnesia_warning, Format, Args}).
+
+%% error messages are reported regardless of debug level
+error(Format, Args) ->
+ save({Format, Args}),
+ report_system_event({mnesia_error, Format, Args}).
+
+%% verbose messages are reported if debug level == debug or verbose
+verbose(Format, Args) ->
+ case mnesia_monitor:get_env(debug) of
+ none -> save({Format, Args});
+ verbose -> important(Format, Args);
+ debug -> important(Format, Args);
+ trace -> important(Format, Args)
+ end.
+
+%% debug message are display if debug level == 2
+dbg_out(Format, Args) ->
+ case mnesia_monitor:get_env(debug) of
+ none -> ignore;
+ verbose -> save({Format, Args});
+ _ -> report_system_event({mnesia_info, Format, Args})
+ end.
+
+%% Keep the last 10 debug print outs
+save(DbgInfo) ->
+ catch save2(DbgInfo).
+
+save2(DbgInfo) ->
+ Key = {'$$$_report', current_pos},
+ P =
+ case ?ets_lookup_element(mnesia_gvar, Key, 2) of
+ 30 -> -1;
+ I -> I
+ end,
+ set({'$$$_report', current_pos}, P+1),
+ set({'$$$_report', P+1}, {date(), time(), DbgInfo}).
+
+copy_file(From, To) ->
+ case file:open(From, [raw, binary, read]) of
+ {ok, F} ->
+ case file:open(To, [raw, binary, write]) of
+ {ok, T} ->
+ Res = copy_file_loop(F, T, 8000),
+ file:close(F),
+ file:close(T),
+ Res;
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+copy_file_loop(F, T, ChunkSize) ->
+ case file:read(F, ChunkSize) of
+ {ok, {0, _}} ->
+ ok;
+ {ok, {_, Bin}} ->
+ file:write(T, Bin),
+ copy_file_loop(F, T, ChunkSize);
+ {ok, Bin} ->
+ file:write(T, Bin),
+ copy_file_loop(F, T, ChunkSize);
+ eof ->
+ ok;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+
+%%%%%%%%%%%%
+%% versions of all the lowlevel db funcs that determine whether we
+%% shall go to disc or ram to do the actual operation.
+
+db_get(Tab, Key) ->
+ db_get(val({Tab, storage_type}), Tab, Key).
+db_get(ram_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
+db_get(disc_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
+db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key).
+
+db_init_chunk(Tab) ->
+ db_init_chunk(val({Tab, storage_type}), Tab, 1000).
+db_init_chunk(Tab, N) ->
+ db_init_chunk(val({Tab, storage_type}), Tab, N).
+
+db_init_chunk(disc_only_copies, Tab, N) ->
+ dets:select(Tab, [{'_', [], ['$_']}], N);
+db_init_chunk(_, Tab, N) ->
+ ets:select(Tab, [{'_', [], ['$_']}], N).
+
+db_chunk(disc_only_copies, State) ->
+ dets:select(State);
+db_chunk(_, State) ->
+ ets:select(State).
+
+db_put(Tab, Val) ->
+ db_put(val({Tab, storage_type}), Tab, Val).
+
+db_put(ram_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
+db_put(disc_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
+db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val).
+
+db_match_object(Tab, Pat) ->
+ db_match_object(val({Tab, storage_type}), Tab, Pat).
+db_match_object(Storage, Tab, Pat) ->
+ db_fixtable(Storage, Tab, true),
+ Res = catch_match_object(Storage, Tab, Pat),
+ db_fixtable(Storage, Tab, false),
+ case Res of
+ {'EXIT', Reason} -> exit(Reason);
+ _ -> Res
+ end.
+
+catch_match_object(disc_only_copies, Tab, Pat) ->
+ catch dets:match_object(Tab, Pat);
+catch_match_object(_, Tab, Pat) ->
+ catch ets:match_object(Tab, Pat).
+
+db_select(Tab, Pat) ->
+ db_select(val({Tab, storage_type}), Tab, Pat).
+
+db_select(Storage, Tab, Pat) ->
+ db_fixtable(Storage, Tab, true),
+ Res = catch_select(Storage, Tab, Pat),
+ db_fixtable(Storage, Tab, false),
+ case Res of
+ {'EXIT', Reason} -> exit(Reason);
+ _ -> Res
+ end.
+
+catch_select(disc_only_copies, Tab, Pat) ->
+ dets:select(Tab, Pat);
+catch_select(_, Tab, Pat) ->
+ ets:select(Tab, Pat).
+
+db_fixtable(ets, Tab, Bool) ->
+ ets:safe_fixtable(Tab, Bool);
+db_fixtable(ram_copies, Tab, Bool) ->
+ ets:safe_fixtable(Tab, Bool);
+db_fixtable(disc_copies, Tab, Bool) ->
+ ets:safe_fixtable(Tab, Bool);
+db_fixtable(dets, Tab, Bool) ->
+ dets:safe_fixtable(Tab, Bool);
+db_fixtable(disc_only_copies, Tab, Bool) ->
+ dets:safe_fixtable(Tab, Bool).
+
+db_erase(Tab, Key) ->
+ db_erase(val({Tab, storage_type}), Tab, Key).
+db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
+db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
+db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key).
+
+db_match_erase(Tab, Pat) ->
+ db_match_erase(val({Tab, storage_type}), Tab, Pat).
+db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
+db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
+db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat).
+
+db_first(Tab) ->
+ db_first(val({Tab, storage_type}), Tab).
+db_first(ram_copies, Tab) -> ?ets_first(Tab);
+db_first(disc_copies, Tab) -> ?ets_first(Tab);
+db_first(disc_only_copies, Tab) -> dets:first(Tab).
+
+db_next_key(Tab, Key) ->
+ db_next_key(val({Tab, storage_type}), Tab, Key).
+db_next_key(ram_copies, Tab, Key) -> ?ets_next(Tab, Key);
+db_next_key(disc_copies, Tab, Key) -> ?ets_next(Tab, Key);
+db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key).
+
+db_last(Tab) ->
+ db_last(val({Tab, storage_type}), Tab).
+db_last(ram_copies, Tab) -> ?ets_last(Tab);
+db_last(disc_copies, Tab) -> ?ets_last(Tab);
+db_last(disc_only_copies, Tab) -> dets:first(Tab). %% Dets don't have order
+
+db_prev_key(Tab, Key) ->
+ db_prev_key(val({Tab, storage_type}), Tab, Key).
+db_prev_key(ram_copies, Tab, Key) -> ?ets_prev(Tab, Key);
+db_prev_key(disc_copies, Tab, Key) -> ?ets_prev(Tab, Key);
+db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key). %% Dets don't have order
+
+db_slot(Tab, Pos) ->
+ db_slot(val({Tab, storage_type}), Tab, Pos).
+db_slot(ram_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
+db_slot(disc_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
+db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos).
+
+db_update_counter(Tab, C, Val) ->
+ db_update_counter(val({Tab, storage_type}), Tab, C, Val).
+db_update_counter(ram_copies, Tab, C, Val) ->
+ ?ets_update_counter(Tab, C, Val);
+db_update_counter(disc_copies, Tab, C, Val) ->
+ ?ets_update_counter(Tab, C, Val);
+db_update_counter(disc_only_copies, Tab, C, Val) ->
+ dets:update_counter(Tab, C, Val).
+
+db_erase_tab(Tab) ->
+ db_erase_tab(val({Tab, storage_type}), Tab).
+db_erase_tab(ram_copies, Tab) -> ?ets_delete_table(Tab);
+db_erase_tab(disc_copies, Tab) -> ?ets_delete_table(Tab);
+db_erase_tab(disc_only_copies, _Tab) -> ignore.
+
+%% assuming that Tab is a valid ets-table
+dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) ->
+ {Open, Close} = mkfuns(Lock),
+ case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)},
+ {keypos, 2}, {repair, Rep}]) of
+ {ok, Tabname} ->
+ Res = dets:to_ets(Tabname, Tab),
+ Close(Tabname),
+ trav_ret(Res, Tab);
+ Other ->
+ Other
+ end.
+
+trav_ret(Tabname, Tabname) -> loaded;
+trav_ret(Other, _Tabname) -> Other.
+
+mkfuns(yes) ->
+ {fun(Tab, Args) -> dets_sync_open(Tab, Args) end,
+ fun(Tab) -> dets_sync_close(Tab) end};
+mkfuns(no) ->
+ {fun(Tab, Args) -> dets:open_file(Tab, Args) end,
+ fun(Tab) -> dets:close(Tab) end}.
+
+disk_type(Tab) ->
+ disk_type(Tab, val({Tab, setorbag})).
+
+disk_type(_Tab, ordered_set) ->
+ set;
+disk_type(_, Type) ->
+ Type.
+
+dets_sync_open(Tab, Ref, File) ->
+ Args = [{file, File},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)},
+ {type, disk_type(Tab)}],
+ dets_sync_open(Ref, Args).
+
+lock_table(Tab) ->
+ global:set_lock({{mnesia_table_lock, Tab}, self()}, [node()], infinity).
+% dbg_out("dets_sync_open: ~p ~p~n", [T, self()]),
+
+unlock_table(Tab) ->
+ global:del_lock({{mnesia_table_lock, Tab}, self()}, [node()]).
+% dbg_out("unlock_table: ~p ~p~n", [T, self()]),
+
+dets_sync_open(Tab, Args) ->
+ lock_table(Tab),
+ case dets:open_file(Tab, Args) of
+ {ok, Tab} ->
+ {ok, Tab};
+ Other ->
+ dets_sync_close(Tab),
+ Other
+ end.
+
+dets_sync_close(Tab) ->
+ catch dets:close(Tab),
+ unlock_table(Tab),
+ ok.
+
+cleanup_tmp_files([Tab | Tabs]) ->
+ dets_sync_close(Tab),
+ file:delete(tab2tmp(Tab)),
+ cleanup_tmp_files(Tabs);
+cleanup_tmp_files([]) ->
+ ok.
+
+%% Returns a list of bad tables
+swap_tmp_files([Tab | Tabs]) ->
+ dets_sync_close(Tab),
+ Tmp = tab2tmp(Tab),
+ Dat = tab2dat(Tab),
+ case file:rename(Tmp, Dat) of
+ ok ->
+ swap_tmp_files(Tabs);
+ _ ->
+ file:delete(Tmp),
+ [Tab | swap_tmp_files(Tabs)]
+ end;
+swap_tmp_files([]) ->
+ [].
+
+readable_indecies(Tab) ->
+ val({Tab, index}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Managing conditional debug functions
+%%
+%% The main idea with the debug_fun's is to allow test programs
+%% to control the internal behaviour of Mnesia. This is needed
+%% to make the test programs independent of system load, swapping
+%% and other circumstances that may affect the behaviour of Mnesia.
+%%
+%% First should calls to ?eval_debug_fun be inserted at well
+%% defined places in Mnesia's code. E.g. in critical situations
+%% of startup, transaction commit, backups etc.
+%%
+%% Then compile Mnesia with the compiler option 'debug'.
+%%
+%% In test programs ?activate_debug_fun should be called
+%% in order to bind a fun to the debug identifier stated
+%% in the call to ?eval_debug_fun.
+%%
+%% If eval_debug_fun finds that the fun is activated it
+%% invokes the fun as NewContext = Fun(PreviousContext, EvalContext)
+%% and replaces the PreviousContext with the NewContext.
+%% The initial context of a debug_fun is given as argument to
+%% activate_debug_fun.
+
+-define(DEBUG_TAB, mnesia_debug).
+-record(debug_info, {id, function, context, file, line}).
+
+scratch_debug_fun() ->
+ dbg_out("scratch_debug_fun(): ~p~n", [?DEBUG_TAB]),
+ (catch ?ets_delete_table(?DEBUG_TAB)),
+ ?ets_new_table(?DEBUG_TAB, [set, public, named_table, {keypos, 2}]).
+
+activate_debug_fun(FunId, Fun, InitialContext, File, Line) ->
+ Info = #debug_info{id = FunId,
+ function = Fun,
+ context = InitialContext,
+ file = File,
+ line = Line
+ },
+ update_debug_info(Info).
+
+update_debug_info(Info) ->
+ case catch ?ets_insert(?DEBUG_TAB, Info) of
+ {'EXIT', _} ->
+ scratch_debug_fun(),
+ ?ets_insert(?DEBUG_TAB, Info);
+ _ ->
+ ok
+ end,
+ dbg_out("update_debug_info(~p)~n", [Info]),
+ ok.
+
+deactivate_debug_fun(FunId, _File, _Line) ->
+ catch ?ets_delete(?DEBUG_TAB, FunId),
+ ok.
+
+eval_debug_fun(FunId, EvalContext, EvalFile, EvalLine) ->
+ case catch ?ets_lookup(?DEBUG_TAB, FunId) of
+ [] ->
+ ok;
+ [Info] ->
+ OldContext = Info#debug_info.context,
+ dbg_out("~s(~p): ~w "
+ "activated in ~s(~p)~n "
+ "eval_debug_fun(~w, ~w)~n",
+ [filename:basename(EvalFile), EvalLine, Info#debug_info.id,
+ filename:basename(Info#debug_info.file), Info#debug_info.line,
+ OldContext, EvalContext]),
+ Fun = Info#debug_info.function,
+ NewContext = Fun(OldContext, EvalContext),
+
+ case catch ?ets_lookup(?DEBUG_TAB, FunId) of
+ [Info] when NewContext /= OldContext ->
+ NewInfo = Info#debug_info{context = NewContext},
+ update_debug_info(NewInfo);
+ _ ->
+ ok
+ end;
+ {'EXIT', _} -> ok
+ end.
+
+-ifdef(debug).
+ is_debug_compiled() -> true.
+-else.
+ is_debug_compiled() -> false.
+-endif.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_loader.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_loader.erl
new file mode 100644
index 0000000000..df3309cfa6
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_loader.erl
@@ -0,0 +1,805 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_loader.erl,v 1.2 2010/03/04 13:54:19 maria Exp $
+%%
+%%% Purpose : Loads tables from local disc or from remote node
+
+-module(mnesia_loader).
+
+%% Mnesia internal stuff
+-export([disc_load_table/2,
+ net_load_table/4,
+ send_table/3]).
+
+-export([old_node_init_table/6]). %% Spawned old node protocol conversion hack
+-export([spawned_receiver/8]). %% Spawned lock taking process
+
+-import(mnesia_lib, [set/2, fatal/2, verbose/2, dbg_out/2]).
+
+-include("mnesia.hrl").
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Load a table from local disc
+
+disc_load_table(Tab, Reason) ->
+ Storage = val({Tab, storage_type}),
+ Type = val({Tab, setorbag}),
+ dbg_out("Getting table ~p (~p) from disc: ~p~n",
+ [Tab, Storage, Reason]),
+ ?eval_debug_fun({?MODULE, do_get_disc_copy},
+ [{tab, Tab},
+ {reason, Reason},
+ {storage, Storage},
+ {type, Type}]),
+ do_get_disc_copy2(Tab, Reason, Storage, Type).
+
+do_get_disc_copy2(Tab, _Reason, Storage, _Type) when Storage == unknown ->
+ verbose("Local table copy of ~p has recently been deleted, ignored.~n",
+ [Tab]),
+ {loaded, ok}; %% ?
+do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies ->
+ %% NOW we create the actual table
+ Repair = mnesia_monitor:get_env(auto_repair),
+ Args = [{keypos, 2}, public, named_table, Type],
+ case Reason of
+ {dumper, _} -> %% Resources allready allocated
+ ignore;
+ _ ->
+ mnesia_monitor:mktab(Tab, Args),
+ Count = mnesia_log:dcd2ets(Tab, Repair),
+ case ets:info(Tab, size) of
+ X when X < Count * 4 ->
+ ok = mnesia_log:ets2dcd(Tab);
+ _ ->
+ ignore
+ end
+ end,
+ mnesia_index:init_index(Tab, Storage),
+ snmpify(Tab, Storage),
+ set({Tab, load_node}, node()),
+ set({Tab, load_reason}, Reason),
+ {loaded, ok};
+
+do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == ram_copies ->
+ Args = [{keypos, 2}, public, named_table, Type],
+ case Reason of
+ {dumper, _} -> %% Resources allready allocated
+ ignore;
+ _ ->
+ mnesia_monitor:mktab(Tab, Args),
+ Fname = mnesia_lib:tab2dcd(Tab),
+ Datname = mnesia_lib:tab2dat(Tab),
+ Repair = mnesia_monitor:get_env(auto_repair),
+ case mnesia_monitor:use_dir() of
+ true ->
+ case mnesia_lib:exists(Fname) of
+ true -> mnesia_log:dcd2ets(Tab, Repair);
+ false ->
+ case mnesia_lib:exists(Datname) of
+ true ->
+ mnesia_lib:dets_to_ets(Tab, Tab, Datname,
+ Type, Repair, no);
+ false ->
+ false
+ end
+ end;
+ false ->
+ false
+ end
+ end,
+ mnesia_index:init_index(Tab, Storage),
+ snmpify(Tab, Storage),
+ set({Tab, load_node}, node()),
+ set({Tab, load_reason}, Reason),
+ {loaded, ok};
+
+do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_only_copies ->
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Type)},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)}],
+ case Reason of
+ {dumper, _} ->
+ mnesia_index:init_index(Tab, Storage),
+ snmpify(Tab, Storage),
+ set({Tab, load_node}, node()),
+ set({Tab, load_reason}, Reason),
+ {loaded, ok};
+ _ ->
+ case mnesia_monitor:open_dets(Tab, Args) of
+ {ok, _} ->
+ mnesia_index:init_index(Tab, Storage),
+ snmpify(Tab, Storage),
+ set({Tab, load_node}, node()),
+ set({Tab, load_reason}, Reason),
+ {loaded, ok};
+ {error, Error} ->
+ {not_loaded, {"Failed to create dets table", Error}}
+ end
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Load a table from a remote node
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Receiver Sender
+%% -------- ------
+%% Grab schema lock on table
+%% Determine table size
+%% Create empty pre-grown table
+%% Grab read lock on table
+%% Let receiver subscribe on updates done on sender node
+%% Disable rehashing of table
+%% Release read lock on table
+%% Send table to receiver in chunks
+%%
+%% Grab read lock on table
+%% Block dirty updates
+%% Update wherabouts
+%%
+%% Cancel the update subscription
+%% Process the subscription events
+%% Optionally dump to disc
+%% Unblock dirty updates
+%% Release read lock on table
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(MAX_TRANSFER_SIZE, 7500).
+-define(MAX_RAM_FILE_SIZE, 1000000).
+-define(MAX_RAM_TRANSFERS, (?MAX_RAM_FILE_SIZE div ?MAX_TRANSFER_SIZE) + 1).
+-define(MAX_NOPACKETS, 20).
+
+net_load_table(Tab, Reason, Ns, Cs)
+ when Reason == {dumper,add_table_copy} ->
+ try_net_load_table(Tab, Reason, Ns, Cs);
+net_load_table(Tab, Reason, Ns, _Cs) ->
+ try_net_load_table(Tab, Reason, Ns, val({Tab, cstruct})).
+
+try_net_load_table(Tab, _Reason, [], _Cs) ->
+ verbose("Copy failed. No active replicas of ~p are available.~n", [Tab]),
+ {not_loaded, none_active};
+try_net_load_table(Tab, Reason, Ns, Cs) ->
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ do_get_network_copy(Tab, Reason, Ns, Storage, Cs).
+
+do_get_network_copy(Tab, _Reason, _Ns, unknown, _Cs) ->
+ verbose("Local table copy of ~p has recently been deleted, ignored.~n", [Tab]),
+ {not_loaded, storage_unknown};
+do_get_network_copy(Tab, Reason, Ns, Storage, Cs) ->
+ [Node | Tail] = Ns,
+ dbg_out("Getting table ~p (~p) from node ~p: ~p~n",
+ [Tab, Storage, Node, Reason]),
+ ?eval_debug_fun({?MODULE, do_get_network_copy},
+ [{tab, Tab}, {reason, Reason},
+ {nodes, Ns}, {storage, Storage}]),
+ mnesia_controller:start_remote_sender(Node, Tab, self(), Storage),
+ put(mnesia_table_sender_node, {Tab, Node}),
+ case init_receiver(Node, Tab, Storage, Cs, Reason) of
+ ok ->
+ set({Tab, load_node}, Node),
+ set({Tab, load_reason}, Reason),
+ mnesia_controller:i_have_tab(Tab),
+ dbg_out("Table ~p copied from ~p to ~p~n", [Tab, Node, node()]),
+ {loaded, ok};
+ Err = {error, _} when element(1, Reason) == dumper ->
+ {not_loaded,Err};
+ restart ->
+ try_net_load_table(Tab, Reason, Tail, Cs);
+ down ->
+ try_net_load_table(Tab, Reason, Tail, Cs)
+ end.
+
+snmpify(Tab, Storage) ->
+ do_snmpify(Tab, val({Tab, snmp}), Storage).
+
+do_snmpify(_Tab, [], _Storage) ->
+ ignore;
+do_snmpify(Tab, Us, Storage) ->
+ Snmp = mnesia_snmp_hook:create_table(Us, Tab, Storage),
+ set({Tab, {index, snmp}}, Snmp).
+
+%% Start the recieiver
+%% Sender should be started first, so we don't have the schema-read
+%% lock to long (or get stuck in a deadlock)
+init_receiver(Node, Tab, Storage, Cs, Reason) ->
+ receive
+ {SenderPid, {first, TabSize}} ->
+ spawn_receiver(Tab,Storage,Cs,SenderPid,
+ TabSize,false,Reason);
+ {SenderPid, {first, TabSize, DetsData}} ->
+ spawn_receiver(Tab,Storage,Cs,SenderPid,
+ TabSize,DetsData,Reason);
+ %% Protocol conversion hack
+ {copier_done, Node} ->
+ dbg_out("Sender of table ~p crashed on node ~p ~n", [Tab, Node]),
+ down(Tab, Storage)
+ end.
+
+
+table_init_fun(SenderPid) ->
+ PConv = mnesia_monitor:needs_protocol_conversion(node(SenderPid)),
+ MeMyselfAndI = self(),
+ fun(read) ->
+ Receiver =
+ if
+ PConv == true ->
+ MeMyselfAndI ! {actual_tabrec, self()},
+ MeMyselfAndI; %% Old mnesia
+ PConv == false -> self()
+ end,
+ SenderPid ! {Receiver, more},
+ get_data(SenderPid, Receiver)
+ end.
+
+
+%% Add_table_copy get's it's own locks.
+spawn_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,{dumper,add_table_copy}) ->
+ Init = table_init_fun(SenderPid),
+ case do_init_table(Tab,Storage,Cs,SenderPid,TabSize,DetsData,self(), Init) of
+ Err = {error, _} ->
+ SenderPid ! {copier_done, node()},
+ Err;
+ Else ->
+ Else
+ end;
+
+spawn_receiver(Tab,Storage,Cs,SenderPid,
+ TabSize,DetsData,Reason) ->
+ %% Grab a schema lock to avoid deadlock between table_loader and schema_commit dumping.
+ %% Both may grab tables-locks in different order.
+ Load = fun() ->
+ {_,Tid,Ts} = get(mnesia_activity_state),
+ mnesia_locker:rlock(Tid, Ts#tidstore.store,
+ {schema, Tab}),
+ Init = table_init_fun(SenderPid),
+ Pid = spawn_link(?MODULE, spawned_receiver,
+ [self(),Tab,Storage,Cs,
+ SenderPid,TabSize,DetsData,
+ Init]),
+ put(mnesia_real_loader, Pid),
+ wait_on_load_complete(Pid)
+ end,
+ Res = case mnesia:transaction(Load, 20) of
+ {'atomic', {error,Result}} when element(1,Reason) == dumper ->
+ SenderPid ! {copier_done, node()},
+ {error,Result};
+ {'atomic', {error,Result}} ->
+ SenderPid ! {copier_done, node()},
+ fatal("Cannot create table ~p: ~p~n",
+ [[Tab, Storage], Result]);
+ {'atomic', Result} -> Result;
+ {aborted, nomore} ->
+ SenderPid ! {copier_done, node()},
+ restart;
+ {aborted, _ } ->
+ SenderPid ! {copier_done, node()},
+ down %% either this node or sender is dying
+ end,
+ unlink(whereis(mnesia_tm)), %% Avoid late unlink from tm
+ Res.
+
+spawned_receiver(ReplyTo,Tab,Storage,Cs,
+ SenderPid,TabSize,DetsData, Init) ->
+ process_flag(trap_exit, true),
+ Done = do_init_table(Tab,Storage,Cs,
+ SenderPid,TabSize,DetsData,
+ ReplyTo, Init),
+ ReplyTo ! {self(),Done},
+ unlink(ReplyTo),
+ unlink(whereis(mnesia_controller)),
+ exit(normal).
+
+wait_on_load_complete(Pid) ->
+ receive
+ {Pid, Res} ->
+ Res;
+ {'EXIT', Pid, Reason} ->
+ exit(Reason);
+ Else ->
+ Pid ! Else,
+ wait_on_load_complete(Pid)
+ end.
+
+tab_receiver(Node, Tab, Storage, Cs, PConv, OrigTabRec) ->
+ receive
+ {SenderPid, {no_more, DatBin}} when PConv == false ->
+ finish_copy(Storage,Tab,Cs,SenderPid,DatBin,OrigTabRec);
+
+ %% Protocol conversion hack
+ {SenderPid, {no_more, DatBin}} when pid(PConv) ->
+ PConv ! {SenderPid, no_more},
+ receive
+ {old_init_table_complete, ok} ->
+ finish_copy(Storage, Tab, Cs, SenderPid, DatBin,OrigTabRec);
+ {old_init_table_complete, Reason} ->
+ Msg = "OLD: [d]ets:init table failed",
+ dbg_out("~s: ~p: ~p~n", [Msg, Tab, Reason]),
+ down(Tab, Storage)
+ end;
+
+ {actual_tabrec, Pid} ->
+ tab_receiver(Node, Tab, Storage, Cs, Pid,OrigTabRec);
+
+ {SenderPid, {more, [Recs]}} when pid(PConv) ->
+ PConv ! {SenderPid, {more, Recs}}, %% Forward Msg to OldNodes
+ tab_receiver(Node, Tab, Storage, Cs, PConv,OrigTabRec);
+
+ {'EXIT', PConv, Reason} -> %% [d]ets:init process crashed
+ Msg = "Receiver crashed",
+ dbg_out("~s: ~p: ~p~n", [Msg, Tab, Reason]),
+ down(Tab, Storage);
+
+ %% Protocol conversion hack
+ {copier_done, Node} ->
+ dbg_out("Sender of table ~p crashed on node ~p ~n", [Tab, Node]),
+ down(Tab, Storage);
+
+ {'EXIT', Pid, Reason} ->
+ handle_exit(Pid, Reason),
+ tab_receiver(Node, Tab, Storage, Cs, PConv,OrigTabRec)
+ end.
+
+create_table(Tab, TabSize, Storage, Cs) ->
+ if
+ Storage == disc_only_copies ->
+ mnesia_lib:lock_table(Tab),
+ Tmp = mnesia_lib:tab2tmp(Tab),
+ Size = lists:max([TabSize, 256]),
+ Args = [{file, Tmp},
+ {keypos, 2},
+%% {ram_file, true},
+ {estimated_no_objects, Size},
+ {repair, mnesia_monitor:get_env(auto_repair)},
+ {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)}],
+ file:delete(Tmp),
+ case mnesia_lib:dets_sync_open(Tab, Args) of
+ {ok, _} ->
+ mnesia_lib:unlock_table(Tab),
+ {Storage, Tab};
+ Else ->
+ mnesia_lib:unlock_table(Tab),
+ Else
+ end;
+ (Storage == ram_copies) or (Storage == disc_copies) ->
+ Args = [{keypos, 2}, public, named_table, Cs#cstruct.type],
+ case mnesia_monitor:unsafe_mktab(Tab, Args) of
+ Tab ->
+ {Storage, Tab};
+ Else ->
+ Else
+ end
+ end.
+
+do_init_table(Tab,Storage,Cs,SenderPid,
+ TabSize,DetsInfo,OrigTabRec,Init) ->
+ case create_table(Tab, TabSize, Storage, Cs) of
+ {Storage,Tab} ->
+ %% Debug info
+ Node = node(SenderPid),
+ put(mnesia_table_receiver, {Tab, Node, SenderPid}),
+ mnesia_tm:block_tab(Tab),
+ PConv = mnesia_monitor:needs_protocol_conversion(Node),
+
+ case init_table(Tab,Storage,Init,PConv,DetsInfo,SenderPid) of
+ ok ->
+ tab_receiver(Node,Tab,Storage,Cs,PConv,OrigTabRec);
+ Reason ->
+ Msg = "[d]ets:init table failed",
+ dbg_out("~s: ~p: ~p~n", [Msg, Tab, Reason]),
+ down(Tab, Storage)
+ end;
+ Error ->
+ Error
+ end.
+
+make_table_fun(Pid, TabRec) ->
+ fun(close) ->
+ ok;
+ (read) ->
+ get_data(Pid, TabRec)
+ end.
+
+get_data(Pid, TabRec) ->
+ receive
+ {Pid, {more, Recs}} ->
+ Pid ! {TabRec, more},
+ {Recs, make_table_fun(Pid,TabRec)};
+ {Pid, no_more} ->
+ end_of_input;
+ {copier_done, Node} ->
+ case node(Pid) of
+ Node ->
+ {copier_done, Node};
+ _ ->
+ get_data(Pid, TabRec)
+ end;
+ {'EXIT', Pid, Reason} ->
+ handle_exit(Pid, Reason),
+ get_data(Pid, TabRec)
+ end.
+
+init_table(Tab, disc_only_copies, Fun, false, DetsInfo,Sender) ->
+ ErtsVer = erlang:system_info(version),
+ case DetsInfo of
+ {ErtsVer, DetsData} ->
+ Res = (catch dets:is_compatible_bchunk_format(Tab, DetsData)),
+ case Res of
+ {'EXIT',{undef,[{dets,_,_}|_]}} ->
+ Sender ! {self(), {old_protocol, Tab}},
+ dets:init_table(Tab, Fun); %% Old dets version
+ {'EXIT', What} ->
+ exit(What);
+ false ->
+ Sender ! {self(), {old_protocol, Tab}},
+ dets:init_table(Tab, Fun); %% Old dets version
+ true ->
+ dets:init_table(Tab, Fun, [{format, bchunk}])
+ end;
+ Old when Old /= false ->
+ Sender ! {self(), {old_protocol, Tab}},
+ dets:init_table(Tab, Fun); %% Old dets version
+ _ ->
+ dets:init_table(Tab, Fun)
+ end;
+init_table(Tab, _, Fun, false, _DetsInfo,_) ->
+ case catch ets:init_table(Tab, Fun) of
+ true ->
+ ok;
+ {'EXIT', Else} -> Else
+ end;
+init_table(Tab, Storage, Fun, true, _DetsInfo, Sender) -> %% Old Nodes
+ spawn_link(?MODULE, old_node_init_table,
+ [Tab, Storage, Fun, self(), false, Sender]),
+ ok.
+
+old_node_init_table(Tab, Storage, Fun, TabReceiver, DetsInfo,Sender) ->
+ Res = init_table(Tab, Storage, Fun, false, DetsInfo,Sender),
+ TabReceiver ! {old_init_table_complete, Res},
+ unlink(TabReceiver),
+ ok.
+
+finish_copy(Storage,Tab,Cs,SenderPid,DatBin,OrigTabRec) ->
+ TabRef = {Storage, Tab},
+ subscr_receiver(TabRef, Cs#cstruct.record_name),
+ case handle_last(TabRef, Cs#cstruct.type, DatBin) of
+ ok ->
+ mnesia_index:init_index(Tab, Storage),
+ snmpify(Tab, Storage),
+ %% OrigTabRec must not be the spawned tab-receiver
+ %% due to old protocol.
+ SenderPid ! {OrigTabRec, no_more},
+ mnesia_tm:unblock_tab(Tab),
+ ok;
+ {error, Reason} ->
+ Msg = "Failed to handle last",
+ dbg_out("~s: ~p: ~p~n", [Msg, Tab, Reason]),
+ down(Tab, Storage)
+ end.
+
+subscr_receiver(TabRef = {_, Tab}, RecName) ->
+ receive
+ {mnesia_table_event, {Op, Val, _Tid}} ->
+ if
+ Tab == RecName ->
+ handle_event(TabRef, Op, Val);
+ true ->
+ handle_event(TabRef, Op, setelement(1, Val, RecName))
+ end,
+ subscr_receiver(TabRef, RecName);
+
+ {'EXIT', Pid, Reason} ->
+ handle_exit(Pid, Reason),
+ subscr_receiver(TabRef, RecName)
+ after 0 ->
+ ok
+ end.
+
+handle_event(TabRef, write, Rec) ->
+ db_put(TabRef, Rec);
+handle_event(TabRef, delete, {_Tab, Key}) ->
+ db_erase(TabRef, Key);
+handle_event(TabRef, delete_object, OldRec) ->
+ db_match_erase(TabRef, OldRec);
+handle_event(TabRef, clear_table, {_Tab, _Key}) ->
+ db_match_erase(TabRef, '_').
+
+handle_last({disc_copies, Tab}, _Type, nobin) ->
+ Ret = mnesia_log:ets2dcd(Tab),
+ Fname = mnesia_lib:tab2dat(Tab),
+ case mnesia_lib:exists(Fname) of
+ true -> %% Remove old .DAT files.
+ file:delete(Fname);
+ false ->
+ ok
+ end,
+ Ret;
+
+handle_last({disc_only_copies, Tab}, Type, nobin) ->
+ case mnesia_lib:swap_tmp_files([Tab]) of
+ [] ->
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Type)},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)}],
+ mnesia_monitor:open_dets(Tab, Args),
+ ok;
+ L when list(L) ->
+ {error, {"Cannot swap tmp files", Tab, L}}
+ end;
+
+handle_last({ram_copies, _Tab}, _Type, nobin) ->
+ ok;
+handle_last({ram_copies, Tab}, _Type, DatBin) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_lib:lock_table(Tab),
+ Tmp = mnesia_lib:tab2tmp(Tab),
+ ok = file:write_file(Tmp, DatBin),
+ ok = file:rename(Tmp, mnesia_lib:tab2dcd(Tab)),
+ mnesia_lib:unlock_table(Tab),
+ ok;
+ false ->
+ ok
+ end.
+
+down(Tab, Storage) ->
+ case Storage of
+ ram_copies ->
+ catch ?ets_delete_table(Tab);
+ disc_copies ->
+ catch ?ets_delete_table(Tab);
+ disc_only_copies ->
+ mnesia_lib:cleanup_tmp_files([Tab])
+ end,
+ mnesia_checkpoint:tm_del_copy(Tab, node()),
+ mnesia_controller:sync_del_table_copy_whereabouts(Tab, node()),
+ mnesia_tm:unblock_tab(Tab),
+ flush_subcrs(),
+ down.
+
+flush_subcrs() ->
+ receive
+ {mnesia_table_event, _} ->
+ flush_subcrs();
+
+ {'EXIT', Pid, Reason} ->
+ handle_exit(Pid, Reason),
+ flush_subcrs()
+ after 0 ->
+ done
+ end.
+
+db_erase({ram_copies, Tab}, Key) ->
+ true = ?ets_delete(Tab, Key);
+db_erase({disc_copies, Tab}, Key) ->
+ true = ?ets_delete(Tab, Key);
+db_erase({disc_only_copies, Tab}, Key) ->
+ ok = dets:delete(Tab, Key).
+
+db_match_erase({ram_copies, Tab} , Pat) ->
+ true = ?ets_match_delete(Tab, Pat);
+db_match_erase({disc_copies, Tab} , Pat) ->
+ true = ?ets_match_delete(Tab, Pat);
+db_match_erase({disc_only_copies, Tab}, Pat) ->
+ ok = dets:match_delete(Tab, Pat).
+
+db_put({ram_copies, Tab}, Val) ->
+ true = ?ets_insert(Tab, Val);
+db_put({disc_copies, Tab}, Val) ->
+ true = ?ets_insert(Tab, Val);
+db_put({disc_only_copies, Tab}, Val) ->
+ ok = dets:insert(Tab, Val).
+
+%% This code executes at the remote site where the data is
+%% executes in a special copier process.
+
+calc_nokeys(Storage, Tab) ->
+ %% Calculate #keys per transfer
+ Key = mnesia_lib:db_first(Storage, Tab),
+ Recs = mnesia_lib:db_get(Storage, Tab, Key),
+ BinSize = size(term_to_binary(Recs)),
+ (?MAX_TRANSFER_SIZE div BinSize) + 1.
+
+send_table(Pid, Tab, RemoteS) ->
+ case ?catch_val({Tab, storage_type}) of
+ {'EXIT', _} ->
+ {error, {no_exists, Tab}};
+ unknown ->
+ {error, {no_exists, Tab}};
+ Storage ->
+ %% Send first
+ TabSize = mnesia:table_info(Tab, size),
+ Pconvert = mnesia_monitor:needs_protocol_conversion(node(Pid)),
+ KeysPerTransfer = calc_nokeys(Storage, Tab),
+ ChunkData = dets:info(Tab, bchunk_format),
+
+ UseDetsChunk =
+ Storage == RemoteS andalso
+ Storage == disc_only_copies andalso
+ ChunkData /= undefined andalso
+ Pconvert == false,
+ if
+ UseDetsChunk == true ->
+ DetsInfo = erlang:system_info(version),
+ Pid ! {self(), {first, TabSize, {DetsInfo, ChunkData}}};
+ true ->
+ Pid ! {self(), {first, TabSize}}
+ end,
+
+ %% Debug info
+ put(mnesia_table_sender, {Tab, node(Pid), Pid}),
+ {Init, Chunk} = reader_funcs(UseDetsChunk, Tab, Storage, KeysPerTransfer),
+
+ SendIt = fun() ->
+ prepare_copy(Pid, Tab, Storage),
+ send_more(Pid, 1, Chunk, Init(), Tab, Pconvert),
+ finish_copy(Pid, Tab, Storage, RemoteS)
+ end,
+
+ case catch SendIt() of
+ receiver_died ->
+ cleanup_tab_copier(Pid, Storage, Tab),
+ unlink(whereis(mnesia_tm)),
+ ok;
+ {_, receiver_died} ->
+ unlink(whereis(mnesia_tm)),
+ ok;
+ {'atomic', no_more} ->
+ unlink(whereis(mnesia_tm)),
+ ok;
+ Reason ->
+ cleanup_tab_copier(Pid, Storage, Tab),
+ unlink(whereis(mnesia_tm)),
+ {error, Reason}
+ end
+ end.
+
+prepare_copy(Pid, Tab, Storage) ->
+ Trans =
+ fun() ->
+ mnesia:write_lock_table(Tab),
+ mnesia_subscr:subscribe(Pid, {table, Tab}),
+ update_where_to_write(Tab, node(Pid)),
+ mnesia_lib:db_fixtable(Storage, Tab, true),
+ ok
+ end,
+ case mnesia:transaction(Trans) of
+ {'atomic', ok} ->
+ ok;
+ {aborted, Reason} ->
+ exit({tab_copier_prepare, Tab, Reason})
+ end.
+
+update_where_to_write(Tab, Node) ->
+ case val({Tab, access_mode}) of
+ read_only ->
+ ignore;
+ read_write ->
+ Current = val({current, db_nodes}),
+ Ns =
+ case lists:member(Node, Current) of
+ true -> Current;
+ false -> [Node | Current]
+ end,
+ update_where_to_write(Ns, Tab, Node)
+ end.
+
+update_where_to_write([], _, _) ->
+ ok;
+update_where_to_write([H|T], Tab, AddNode) ->
+ rpc:call(H, mnesia_controller, call,
+ [{update_where_to_write, [add, Tab, AddNode], self()}]),
+ update_where_to_write(T, Tab, AddNode).
+
+send_more(Pid, N, Chunk, DataState, Tab, OldNode) ->
+ receive
+ {NewPid, more} ->
+ case send_packet(N - 1, NewPid, Chunk, DataState, OldNode) of
+ New when integer(New) ->
+ New - 1;
+ NewData ->
+ send_more(NewPid, ?MAX_NOPACKETS, Chunk, NewData, Tab, OldNode)
+ end;
+ {_NewPid, {old_protocol, Tab}} ->
+ Storage = val({Tab, storage_type}),
+ {Init, NewChunk} =
+ reader_funcs(false, Tab, Storage, calc_nokeys(Storage, Tab)),
+ send_more(Pid, 1, NewChunk, Init(), Tab, OldNode);
+
+ {copier_done, Node} when Node == node(Pid)->
+ verbose("Receiver of table ~p crashed on ~p (more)~n", [Tab, Node]),
+ throw(receiver_died)
+ end.
+
+reader_funcs(UseDetsChunk, Tab, Storage, KeysPerTransfer) ->
+ case UseDetsChunk of
+ false ->
+ {fun() -> mnesia_lib:db_init_chunk(Storage, Tab, KeysPerTransfer) end,
+ fun(Cont) -> mnesia_lib:db_chunk(Storage, Cont) end};
+ true ->
+ {fun() -> dets_bchunk(Tab, start) end,
+ fun(Cont) -> dets_bchunk(Tab, Cont) end}
+ end.
+
+dets_bchunk(Tab, Chunk) -> %% Arrg
+ case dets:bchunk(Tab, Chunk) of
+ {Cont, Data} -> {Data, Cont};
+ Else -> Else
+ end.
+
+send_packet(N, Pid, _Chunk, '$end_of_table', OldNode) ->
+ case OldNode of
+ true -> ignore; %% Old nodes can't handle the new no_more
+ false -> Pid ! {self(), no_more}
+ end,
+ N;
+send_packet(N, Pid, Chunk, {[], Cont}, OldNode) ->
+ send_packet(N, Pid, Chunk, Chunk(Cont), OldNode);
+send_packet(N, Pid, Chunk, {Recs, Cont}, OldNode) when N < ?MAX_NOPACKETS ->
+ case OldNode of
+ true -> Pid ! {self(), {more, [Recs]}}; %% Old need's wrapping list
+ false -> Pid ! {self(), {more, Recs}}
+ end,
+ send_packet(N+1, Pid, Chunk, Chunk(Cont), OldNode);
+send_packet(_N, _Pid, _Chunk, DataState, _OldNode) ->
+ DataState.
+
+finish_copy(Pid, Tab, Storage, RemoteS) ->
+ RecNode = node(Pid),
+ DatBin = dat2bin(Tab, Storage, RemoteS),
+ Trans =
+ fun() ->
+ mnesia:read_lock_table(Tab),
+ A = val({Tab, access_mode}),
+ mnesia_controller:sync_and_block_table_whereabouts(Tab, RecNode, RemoteS, A),
+ cleanup_tab_copier(Pid, Storage, Tab),
+ mnesia_checkpoint:tm_add_copy(Tab, RecNode),
+ Pid ! {self(), {no_more, DatBin}},
+ receive
+ {Pid, no_more} -> % Dont bother about the spurious 'more' message
+ no_more;
+ {copier_done, Node} when Node == node(Pid)->
+ verbose("Tab receiver ~p crashed (more): ~p~n", [Tab, Node]),
+ receiver_died
+ end
+ end,
+ mnesia:transaction(Trans).
+
+cleanup_tab_copier(Pid, Storage, Tab) ->
+ mnesia_lib:db_fixtable(Storage, Tab, false),
+ mnesia_subscr:unsubscribe(Pid, {table, Tab}).
+
+dat2bin(Tab, ram_copies, ram_copies) ->
+ mnesia_lib:lock_table(Tab),
+ Res = file:read_file(mnesia_lib:tab2dcd(Tab)),
+ mnesia_lib:unlock_table(Tab),
+ case Res of
+ {ok, DatBin} -> DatBin;
+ _ -> nobin
+ end;
+dat2bin(_Tab, _LocalS, _RemoteS) ->
+ nobin.
+
+handle_exit(Pid, Reason) when node(Pid) == node() ->
+ exit(Reason);
+handle_exit(_Pid, _Reason) -> %% Not from our node, this will be handled by
+ ignore. %% mnesia_down soon.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_locker.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_locker.erl
new file mode 100644
index 0000000000..8fe08414d0
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_locker.erl
@@ -0,0 +1,1022 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_locker.erl,v 1.2 2009/07/01 15:45:40 kostis Exp $
+%%
+-module(mnesia_locker).
+
+-export([
+ get_held_locks/0,
+ get_lock_queue/0,
+ global_lock/5,
+ ixrlock/5,
+ init/1,
+ mnesia_down/2,
+ release_tid/1,
+ async_release_tid/2,
+ send_release_tid/2,
+ receive_release_tid_acc/2,
+ rlock/3,
+ rlock_table/3,
+ rwlock/3,
+ sticky_rwlock/3,
+ start/0,
+ sticky_wlock/3,
+ sticky_wlock_table/3,
+ wlock/3,
+ wlock_no_exist/4,
+ wlock_table/3
+ ]).
+
+%% sys callback functions
+-export([system_continue/3,
+ system_terminate/4,
+ system_code_change/4
+ ]).
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [dbg_out/2, error/2, verbose/2]).
+
+-define(dbg(S,V), ok).
+%-define(dbg(S,V), dbg_out("~p:~p: " ++ S, [?MODULE, ?LINE] ++ V)).
+
+-define(ALL, '______WHOLETABLE_____').
+-define(STICK, '______STICK_____').
+-define(GLOBAL, '______GLOBAL_____').
+
+-record(state, {supervisor}).
+
+-record(queue, {oid, tid, op, pid, lucky}).
+
+%% mnesia_held_locks: contain {Oid, Op, Tid} entries (bag)
+-define(match_oid_held_locks(Oid), {Oid, '_', '_'}).
+%% mnesia_tid_locks: contain {Tid, Oid, Op} entries (bag)
+-define(match_oid_tid_locks(Tid), {Tid, '_', '_'}).
+%% mnesia_sticky_locks: contain {Oid, Node} entries and {Tab, Node} entries (set)
+-define(match_oid_sticky_locks(Oid),{Oid, '_'}).
+%% mnesia_lock_queue: contain {queue, Oid, Tid, Op, ReplyTo, WaitForTid} entries (ordered_set)
+-define(match_oid_lock_queue(Oid), #queue{oid=Oid, tid='_', op = '_', pid = '_', lucky = '_'}).
+%% mnesia_lock_counter: {{write, Tab}, Number} &&
+%% {{read, Tab}, Number} entries (set)
+
+start() ->
+ mnesia_monitor:start_proc(?MODULE, ?MODULE, init, [self()]).
+
+init(Parent) ->
+ register(?MODULE, self()),
+ process_flag(trap_exit, true),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ loop(#state{supervisor = Parent}).
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ _VaLuE_ -> _VaLuE_
+ end.
+
+reply(From, R) ->
+ From ! {?MODULE, node(), R}.
+
+l_request(Node, X, Store) ->
+ {?MODULE, Node} ! {self(), X},
+ l_req_rec(Node, Store).
+
+l_req_rec(Node, Store) ->
+ ?ets_insert(Store, {nodes, Node}),
+ receive
+ {?MODULE, Node, {switch, Node2, Req}} ->
+ ?ets_insert(Store, {nodes, Node2}),
+ {?MODULE, Node2} ! Req,
+ {switch, Node2, Req};
+ {?MODULE, Node, Reply} ->
+ Reply;
+ {mnesia_down, Node} ->
+ {not_granted, {node_not_running, Node}}
+ end.
+
+release_tid(Tid) ->
+ ?MODULE ! {release_tid, Tid}.
+
+async_release_tid(Nodes, Tid) ->
+ rpc:abcast(Nodes, ?MODULE, {release_tid, Tid}).
+
+send_release_tid(Nodes, Tid) ->
+ rpc:abcast(Nodes, ?MODULE, {self(), {sync_release_tid, Tid}}).
+
+receive_release_tid_acc([Node | Nodes], Tid) ->
+ receive
+ {?MODULE, Node, {tid_released, Tid}} ->
+ receive_release_tid_acc(Nodes, Tid);
+ {mnesia_down, Node} ->
+ receive_release_tid_acc(Nodes, Tid)
+ end;
+receive_release_tid_acc([], _Tid) ->
+ ok.
+
+loop(State) ->
+ receive
+ {From, {write, Tid, Oid}} ->
+ try_sticky_lock(Tid, write, From, Oid),
+ loop(State);
+
+ %% If Key == ?ALL it's a request to lock the entire table
+ %%
+
+ {From, {read, Tid, Oid}} ->
+ try_sticky_lock(Tid, read, From, Oid),
+ loop(State);
+
+ %% Really do a read, but get hold of a write lock
+ %% used by mnesia:wread(Oid).
+
+ {From, {read_write, Tid, Oid}} ->
+ try_sticky_lock(Tid, read_write, From, Oid),
+ loop(State);
+
+ %% Tid has somehow terminated, clear up everything
+ %% and pass locks on to queued processes.
+ %% This is the purpose of the mnesia_tid_locks table
+
+ {release_tid, Tid} ->
+ do_release_tid(Tid),
+ loop(State);
+
+ %% stick lock, first tries this to the where_to_read Node
+ {From, {test_set_sticky, Tid, {Tab, _} = Oid, Lock}} ->
+ case ?ets_lookup(mnesia_sticky_locks, Tab) of
+ [] ->
+ reply(From, not_stuck),
+ loop(State);
+ [{_,Node}] when Node == node() ->
+ %% Lock is stuck here, see now if we can just set
+ %% a regular write lock
+ try_lock(Tid, Lock, From, Oid),
+ loop(State);
+ [{_,Node}] ->
+ reply(From, {stuck_elsewhere, Node}),
+ loop(State)
+ end;
+
+ %% If test_set_sticky fails, we send this to all nodes
+ %% after aquiring a real write lock on Oid
+
+ {stick, {Tab, _}, N} ->
+ ?ets_insert(mnesia_sticky_locks, {Tab, N}),
+ loop(State);
+
+ %% The caller which sends this message, must have first
+ %% aquired a write lock on the entire table
+ {unstick, Tab} ->
+ ?ets_delete(mnesia_sticky_locks, Tab),
+ loop(State);
+
+ {From, {ix_read, Tid, Tab, IxKey, Pos}} ->
+ case catch mnesia_index:get_index_table(Tab, Pos) of
+ {'EXIT', _} ->
+ reply(From, {not_granted, {no_exists, Tab, {index, [Pos]}}}),
+ loop(State);
+ Index ->
+ Rk = mnesia_lib:elems(2,mnesia_index:db_get(Index, IxKey)),
+ %% list of real keys
+ case ?ets_lookup(mnesia_sticky_locks, Tab) of
+ [] ->
+ set_read_lock_on_all_keys(Tid, From,Tab,Rk,Rk,
+ []),
+ loop(State);
+ [{_,N}] when N == node() ->
+ set_read_lock_on_all_keys(Tid, From,Tab,Rk,Rk,
+ []),
+ loop(State);
+ [{_,N}] ->
+ Req = {From, {ix_read, Tid, Tab, IxKey, Pos}},
+ From ! {?MODULE, node(), {switch, N, Req}},
+ loop(State)
+ end
+ end;
+
+ {From, {sync_release_tid, Tid}} ->
+ do_release_tid(Tid),
+ reply(From, {tid_released, Tid}),
+ loop(State);
+
+ {release_remote_non_pending, Node, Pending} ->
+ release_remote_non_pending(Node, Pending),
+ mnesia_monitor:mnesia_down(?MODULE, Node),
+ loop(State);
+
+ {'EXIT', Pid, _} when Pid == State#state.supervisor ->
+ do_stop();
+
+ {system, From, Msg} ->
+ verbose("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ Parent = State#state.supervisor,
+ sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], State);
+
+ Msg ->
+ error("~p got unexpected message: ~p~n", [?MODULE, Msg]),
+ loop(State)
+ end.
+
+set_lock(Tid, Oid, Op) ->
+ ?dbg("Granted ~p ~p ~p~n", [Tid,Oid,Op]),
+ ?ets_insert(mnesia_held_locks, {Oid, Op, Tid}),
+ ?ets_insert(mnesia_tid_locks, {Tid, Oid, Op}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Acquire locks
+
+try_sticky_lock(Tid, Op, Pid, {Tab, _} = Oid) ->
+ case ?ets_lookup(mnesia_sticky_locks, Tab) of
+ [] ->
+ try_lock(Tid, Op, Pid, Oid);
+ [{_,N}] when N == node() ->
+ try_lock(Tid, Op, Pid, Oid);
+ [{_,N}] ->
+ Req = {Pid, {Op, Tid, Oid}},
+ Pid ! {?MODULE, node(), {switch, N, Req}}
+ end.
+
+try_lock(Tid, read_write, Pid, Oid) ->
+ try_lock(Tid, read_write, read, write, Pid, Oid);
+try_lock(Tid, Op, Pid, Oid) ->
+ try_lock(Tid, Op, Op, Op, Pid, Oid).
+
+try_lock(Tid, Op, SimpleOp, Lock, Pid, Oid) ->
+ case can_lock(Tid, Lock, Oid, {no, bad_luck}) of
+ yes ->
+ Reply = grant_lock(Tid, SimpleOp, Lock, Oid),
+ reply(Pid, Reply);
+ {no, Lucky} ->
+ C = #cyclic{op = SimpleOp, lock = Lock, oid = Oid, lucky = Lucky},
+ ?dbg("Rejected ~p ~p ~p ~p ~n", [Tid, Oid, Lock, Lucky]),
+ reply(Pid, {not_granted, C});
+ {queue, Lucky} ->
+ ?dbg("Queued ~p ~p ~p ~p ~n", [Tid, Oid, Lock, Lucky]),
+ %% Append to queue: Nice place for trace output
+ ?ets_insert(mnesia_lock_queue,
+ #queue{oid = Oid, tid = Tid, op = Op,
+ pid = Pid, lucky = Lucky}),
+ ?ets_insert(mnesia_tid_locks, {Tid, Oid, {queued, Op}})
+ end.
+
+grant_lock(Tid, read, Lock, {Tab, Key})
+ when Key /= ?ALL, Tab /= ?GLOBAL ->
+ case node(Tid#tid.pid) == node() of
+ true ->
+ set_lock(Tid, {Tab, Key}, Lock),
+ {granted, lookup_in_client};
+ false ->
+ case catch mnesia_lib:db_get(Tab, Key) of %% lookup as well
+ {'EXIT', _Reason} ->
+ %% Table has been deleted from this node,
+ %% restart the transaction.
+ C = #cyclic{op = read, lock = Lock, oid = {Tab, Key},
+ lucky = nowhere},
+ {not_granted, C};
+ Val ->
+ set_lock(Tid, {Tab, Key}, Lock),
+ {granted, Val}
+ end
+ end;
+grant_lock(Tid, read, Lock, Oid) ->
+ set_lock(Tid, Oid, Lock),
+ {granted, ok};
+grant_lock(Tid, write, Lock, Oid) ->
+ set_lock(Tid, Oid, Lock),
+ granted.
+
+%% 1) Impose an ordering on all transactions favour old (low tid) transactions
+%% newer (higher tid) transactions may never wait on older ones,
+%% 2) When releasing the tids from the queue always begin with youngest (high tid)
+%% because of 1) it will avoid the deadlocks.
+%% 3) TabLocks is the problem :-) They should not starve and not deadlock
+%% handle tablocks in queue as they had locks on unlocked records.
+
+can_lock(Tid, read, {Tab, Key}, AlreadyQ) when Key /= ?ALL ->
+ %% The key is bound, no need for the other BIF
+ Oid = {Tab, Key},
+ ObjLocks = ?ets_match_object(mnesia_held_locks, {Oid, write, '_'}),
+ TabLocks = ?ets_match_object(mnesia_held_locks, {{Tab, ?ALL}, write, '_'}),
+ check_lock(Tid, Oid, ObjLocks, TabLocks, yes, AlreadyQ, read);
+
+can_lock(Tid, read, Oid, AlreadyQ) -> % Whole tab
+ Tab = element(1, Oid),
+ ObjLocks = ?ets_match_object(mnesia_held_locks, {{Tab, '_'}, write, '_'}),
+ check_lock(Tid, Oid, ObjLocks, [], yes, AlreadyQ, read);
+
+can_lock(Tid, write, {Tab, Key}, AlreadyQ) when Key /= ?ALL ->
+ Oid = {Tab, Key},
+ ObjLocks = ?ets_lookup(mnesia_held_locks, Oid),
+ TabLocks = ?ets_lookup(mnesia_held_locks, {Tab, ?ALL}),
+ check_lock(Tid, Oid, ObjLocks, TabLocks, yes, AlreadyQ, write);
+
+can_lock(Tid, write, Oid, AlreadyQ) -> % Whole tab
+ Tab = element(1, Oid),
+ ObjLocks = ?ets_match_object(mnesia_held_locks, ?match_oid_held_locks({Tab, '_'})),
+ check_lock(Tid, Oid, ObjLocks, [], yes, AlreadyQ, write).
+
+%% Check held locks for conflicting locks
+check_lock(Tid, Oid, [Lock | Locks], TabLocks, X, AlreadyQ, Type) ->
+ case element(3, Lock) of
+ Tid ->
+ check_lock(Tid, Oid, Locks, TabLocks, X, AlreadyQ, Type);
+ WaitForTid when WaitForTid > Tid -> % Important order
+ check_lock(Tid, Oid, Locks, TabLocks, {queue, WaitForTid}, AlreadyQ, Type);
+ WaitForTid when Tid#tid.pid == WaitForTid#tid.pid ->
+ dbg_out("Spurious lock conflict ~w ~w: ~w -> ~w~n",
+ [Oid, Lock, Tid, WaitForTid]),
+%% check_lock(Tid, Oid, Locks, TabLocks, {queue, WaitForTid}, AlreadyQ);
+ %% BUGBUG Fix this if possible
+ {no, WaitForTid};
+ WaitForTid ->
+ {no, WaitForTid}
+ end;
+
+check_lock(_, _, [], [], X, {queue, bad_luck}, _) ->
+ X; %% The queue should be correct already no need to check it again
+
+check_lock(_, _, [], [], X = {queue, _Tid}, _AlreadyQ, _) ->
+ X;
+
+check_lock(Tid, Oid, [], [], X, AlreadyQ, Type) ->
+ {Tab, Key} = Oid,
+ if
+ Type == write ->
+ check_queue(Tid, Tab, X, AlreadyQ);
+ Key == ?ALL ->
+ %% hmm should be solvable by a clever select expr but not today...
+ check_queue(Tid, Tab, X, AlreadyQ);
+ true ->
+ %% If there is a queue on that object, read_lock shouldn't be granted
+ ObjLocks = ets:lookup(mnesia_lock_queue, Oid),
+ Greatest = max(ObjLocks),
+ case Greatest of
+ empty ->
+ check_queue(Tid, Tab, X, AlreadyQ);
+ ObjL when Tid > ObjL ->
+ {no, ObjL}; %% Starvation Preemption (write waits for read)
+ ObjL ->
+ check_queue(Tid, Tab, {queue, ObjL}, AlreadyQ)
+ end
+ end;
+
+check_lock(Tid, Oid, [], TabLocks, X, AlreadyQ, Type) ->
+ check_lock(Tid, Oid, TabLocks, [], X, AlreadyQ, Type).
+
+%% Check queue for conflicting locks
+%% Assume that all queued locks belongs to other tid's
+
+check_queue(Tid, Tab, X, AlreadyQ) ->
+ TabLocks = ets:lookup(mnesia_lock_queue, {Tab,?ALL}),
+ Greatest = max(TabLocks),
+ case Greatest of
+ empty ->
+ X;
+ Tid ->
+ X;
+ WaitForTid when WaitForTid#queue.tid > Tid -> % Important order
+ {queue, WaitForTid};
+ WaitForTid ->
+ case AlreadyQ of
+ {no, bad_luck} -> {no, WaitForTid};
+ _ ->
+ erlang:error({mnesia_locker, assert, AlreadyQ})
+ end
+ end.
+
+max([]) ->
+ empty;
+max([H|R]) ->
+ max(R, H#queue.tid).
+
+max([H|R], Tid) when H#queue.tid > Tid ->
+ max(R, H#queue.tid);
+max([_|R], Tid) ->
+ max(R, Tid);
+max([], Tid) ->
+ Tid.
+
+%% We can't queue the ixlock requests since it
+%% becomes to complivated for little me :-)
+%% If we encounter an object with a wlock we reject the
+%% entire lock request
+%%
+%% BUGBUG: this is actually a bug since we may starve
+
+set_read_lock_on_all_keys(Tid, From, Tab, [RealKey | Tail], Orig, Ack) ->
+ Oid = {Tab, RealKey},
+ case can_lock(Tid, read, Oid, {no, bad_luck}) of
+ yes ->
+ {granted, Val} = grant_lock(Tid, read, read, Oid),
+ case opt_lookup_in_client(Val, Oid, read) of % Ought to be invoked
+ C when record(C, cyclic) -> % in the client
+ reply(From, {not_granted, C});
+ Val2 ->
+ Ack2 = lists:append(Val2, Ack),
+ set_read_lock_on_all_keys(Tid, From, Tab, Tail, Orig, Ack2)
+ end;
+ {no, Lucky} ->
+ C = #cyclic{op = read, lock = read, oid = Oid, lucky = Lucky},
+ reply(From, {not_granted, C});
+ {queue, Lucky} ->
+ C = #cyclic{op = read, lock = read, oid = Oid, lucky = Lucky},
+ reply(From, {not_granted, C})
+ end;
+set_read_lock_on_all_keys(_Tid, From, _Tab, [], Orig, Ack) ->
+ reply(From, {granted, Ack, Orig}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Release of locks
+
+%% Release remote non-pending nodes
+release_remote_non_pending(Node, Pending) ->
+ %% Clear the mnesia_sticky_locks table first, to avoid
+ %% unnecessary requests to the failing node
+ ?ets_match_delete(mnesia_sticky_locks, {'_' , Node}),
+
+ %% Then we have to release all locks held by processes
+ %% running at the failed node and also simply remove all
+ %% queue'd requests back to the failed node
+
+ AllTids = ?ets_match(mnesia_tid_locks, {'$1', '_', '_'}),
+ Tids = [T || [T] <- AllTids, Node == node(T#tid.pid), not lists:member(T, Pending)],
+ do_release_tids(Tids).
+
+do_release_tids([Tid | Tids]) ->
+ do_release_tid(Tid),
+ do_release_tids(Tids);
+do_release_tids([]) ->
+ ok.
+
+do_release_tid(Tid) ->
+ Locks = ?ets_lookup(mnesia_tid_locks, Tid),
+ ?dbg("Release ~p ~p ~n", [Tid, Locks]),
+ ?ets_delete(mnesia_tid_locks, Tid),
+ release_locks(Locks),
+ %% Removed queued locks which has had locks
+ UniqueLocks = keyunique(lists:sort(Locks),[]),
+ rearrange_queue(UniqueLocks).
+
+keyunique([{_Tid, Oid, _Op}|R], Acc = [{_, Oid, _}|_]) ->
+ keyunique(R, Acc);
+keyunique([H|R], Acc) ->
+ keyunique(R, [H|Acc]);
+keyunique([], Acc) ->
+ Acc.
+
+release_locks([Lock | Locks]) ->
+ release_lock(Lock),
+ release_locks(Locks);
+release_locks([]) ->
+ ok.
+
+release_lock({Tid, Oid, {queued, _}}) ->
+ ?ets_match_delete(mnesia_lock_queue,
+ #queue{oid=Oid, tid = Tid, op = '_',
+ pid = '_', lucky = '_'});
+release_lock({Tid, Oid, Op}) ->
+ if
+ Op == write ->
+ ?ets_delete(mnesia_held_locks, Oid);
+ Op == read ->
+ ?ets_match_delete(mnesia_held_locks, {Oid, Op, Tid})
+ end.
+
+rearrange_queue([{_Tid, {Tab, Key}, _} | Locks]) ->
+ if
+ Key /= ?ALL->
+ Queue =
+ ets:lookup(mnesia_lock_queue, {Tab, ?ALL}) ++
+ ets:lookup(mnesia_lock_queue, {Tab, Key}),
+ case Queue of
+ [] ->
+ ok;
+ _ ->
+ Sorted = lists:reverse(lists:keysort(#queue.tid, Queue)),
+ try_waiters_obj(Sorted)
+ end;
+ true ->
+ Pat = ?match_oid_lock_queue({Tab, '_'}),
+ Queue = ?ets_match_object(mnesia_lock_queue, Pat),
+ Sorted = lists:reverse(lists:keysort(#queue.tid, Queue)),
+ try_waiters_tab(Sorted)
+ end,
+ ?dbg("RearrQ ~p~n", [Queue]),
+ rearrange_queue(Locks);
+rearrange_queue([]) ->
+ ok.
+
+try_waiters_obj([W | Waiters]) ->
+ case try_waiter(W) of
+ queued ->
+ no;
+ _ ->
+ try_waiters_obj(Waiters)
+ end;
+try_waiters_obj([]) ->
+ ok.
+
+try_waiters_tab([W | Waiters]) ->
+ case W#queue.oid of
+ {_Tab, ?ALL} ->
+ case try_waiter(W) of
+ queued ->
+ no;
+ _ ->
+ try_waiters_tab(Waiters)
+ end;
+ Oid ->
+ case try_waiter(W) of
+ queued ->
+ Rest = key_delete_all(Oid, #queue.oid, Waiters),
+ try_waiters_tab(Rest);
+ _ ->
+ try_waiters_tab(Waiters)
+ end
+ end;
+try_waiters_tab([]) ->
+ ok.
+
+try_waiter({queue, Oid, Tid, read_write, ReplyTo, _}) ->
+ try_waiter(Oid, read_write, read, write, ReplyTo, Tid);
+try_waiter({queue, Oid, Tid, Op, ReplyTo, _}) ->
+ try_waiter(Oid, Op, Op, Op, ReplyTo, Tid).
+
+try_waiter(Oid, Op, SimpleOp, Lock, ReplyTo, Tid) ->
+ case can_lock(Tid, Lock, Oid, {queue, bad_luck}) of
+ yes ->
+ %% Delete from queue: Nice place for trace output
+ ?ets_match_delete(mnesia_lock_queue,
+ #queue{oid=Oid, tid = Tid, op = Op,
+ pid = ReplyTo, lucky = '_'}),
+ Reply = grant_lock(Tid, SimpleOp, Lock, Oid),
+ ReplyTo ! {?MODULE, node(), Reply},
+ locked;
+ {queue, _Why} ->
+ ?dbg("Keep ~p ~p ~p ~p~n", [Tid, Oid, Lock, _Why]),
+ queued; % Keep waiter in queue
+ {no, Lucky} ->
+ C = #cyclic{op = SimpleOp, lock = Lock, oid = Oid, lucky = Lucky},
+ verbose("** WARNING ** Restarted transaction, possible deadlock in lock queue ~w: cyclic = ~w~n",
+ [Tid, C]),
+ ?ets_match_delete(mnesia_lock_queue,
+ #queue{oid=Oid, tid = Tid, op = Op,
+ pid = ReplyTo, lucky = '_'}),
+ Reply = {not_granted, C},
+ ReplyTo ! {?MODULE, node(), Reply},
+ removed
+ end.
+
+key_delete_all(Key, Pos, TupleList) ->
+ key_delete_all(Key, Pos, TupleList, []).
+key_delete_all(Key, Pos, [H|T], Ack) when element(Pos, H) == Key ->
+ key_delete_all(Key, Pos, T, Ack);
+key_delete_all(Key, Pos, [H|T], Ack) ->
+ key_delete_all(Key, Pos, T, [H|Ack]);
+key_delete_all(_, _, [], Ack) ->
+ lists:reverse(Ack).
+
+
+%% ********************* end server code ********************
+%% The following code executes at the client side of a transactions
+
+mnesia_down(N, Pending) ->
+ case whereis(?MODULE) of
+ undefined ->
+ %% Takes care of mnesia_down's in early startup
+ mnesia_monitor:mnesia_down(?MODULE, N);
+ Pid ->
+ %% Syncronously call needed in order to avoid
+ %% race with mnesia_tm's coordinator processes
+ %% that may restart and acquire new locks.
+ %% mnesia_monitor ensures the sync.
+ Pid ! {release_remote_non_pending, N, Pending}
+ end.
+
+%% Aquire a write lock, but do a read, used by
+%% mnesia:wread/1
+
+rwlock(Tid, Store, Oid) ->
+ {Tab, Key} = Oid,
+ case val({Tab, where_to_read}) of
+ nowhere ->
+ mnesia:abort({no_exists, Tab});
+ Node ->
+ Lock = write,
+ case need_lock(Store, Tab, Key, Lock) of
+ yes ->
+ Ns = w_nodes(Tab),
+ Res = get_rwlocks_on_nodes(Ns, Ns, Node, Store, Tid, Oid),
+ ?ets_insert(Store, {{locks, Tab, Key}, Lock}),
+ Res;
+ no ->
+ if
+ Key == ?ALL ->
+ w_nodes(Tab);
+ Tab == ?GLOBAL ->
+ w_nodes(Tab);
+ true ->
+ dirty_rpc(Node, Tab, Key, Lock)
+ end
+ end
+ end.
+
+get_rwlocks_on_nodes([Node | Tail], Orig, Node, Store, Tid, Oid) ->
+ Op = {self(), {read_write, Tid, Oid}},
+ {?MODULE, Node} ! Op,
+ ?ets_insert(Store, {nodes, Node}),
+ add_debug(Node),
+ get_rwlocks_on_nodes(Tail, Orig, Node, Store, Tid, Oid);
+get_rwlocks_on_nodes([Node | Tail], Orig, OtherNode, Store, Tid, Oid) ->
+ Op = {self(), {write, Tid, Oid}},
+ {?MODULE, Node} ! Op,
+ add_debug(Node),
+ ?ets_insert(Store, {nodes, Node}),
+ get_rwlocks_on_nodes(Tail, Orig, OtherNode, Store, Tid, Oid);
+get_rwlocks_on_nodes([], Orig, _Node, Store, _Tid, Oid) ->
+ receive_wlocks(Orig, read_write_lock, Store, Oid).
+
+%% Return a list of nodes or abort transaction
+%% WE also insert any additional where_to_write nodes
+%% in the local store under the key == nodes
+
+w_nodes(Tab) ->
+ Nodes = ?catch_val({Tab, where_to_write}),
+ case Nodes of
+ [_ | _] -> Nodes;
+ _ -> mnesia:abort({no_exists, Tab})
+ end.
+
+%% aquire a sticky wlock, a sticky lock is a lock
+%% which remains at this node after the termination of the
+%% transaction.
+
+sticky_wlock(Tid, Store, Oid) ->
+ sticky_lock(Tid, Store, Oid, write).
+
+sticky_rwlock(Tid, Store, Oid) ->
+ sticky_lock(Tid, Store, Oid, read_write).
+
+sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) ->
+ N = val({Tab, where_to_read}),
+ if
+ node() == N ->
+ case need_lock(Store, Tab, Key, write) of
+ yes ->
+ do_sticky_lock(Tid, Store, Oid, Lock);
+ no ->
+ dirty_sticky_lock(Tab, Key, [N], Lock)
+ end;
+ true ->
+ mnesia:abort({not_local, Tab})
+ end.
+
+do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) ->
+ ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}},
+ receive
+ {?MODULE, _N, granted} ->
+ ?ets_insert(Store, {{locks, Tab, Key}, write}),
+ granted;
+ {?MODULE, _N, {granted, Val}} -> %% for rwlocks
+ case opt_lookup_in_client(Val, Oid, write) of
+ C when record(C, cyclic) ->
+ exit({aborted, C});
+ Val2 ->
+ ?ets_insert(Store, {{locks, Tab, Key}, write}),
+ Val2
+ end;
+ {?MODULE, _N, {not_granted, Reason}} ->
+ exit({aborted, Reason});
+ {?MODULE, N, not_stuck} ->
+ not_stuck(Tid, Store, Tab, Key, Oid, Lock, N),
+ dirty_sticky_lock(Tab, Key, [N], Lock);
+ {mnesia_down, N} ->
+ exit({aborted, {node_not_running, N}});
+ {?MODULE, N, {stuck_elsewhere, _N2}} ->
+ stuck_elsewhere(Tid, Store, Tab, Key, Oid, Lock),
+ dirty_sticky_lock(Tab, Key, [N], Lock)
+ end.
+
+not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) ->
+ rlock(Tid, Store, {Tab, ?ALL}), %% needed?
+ wlock(Tid, Store, Oid), %% perfect sync
+ wlock(Tid, Store, {Tab, ?STICK}), %% max one sticker/table
+ Ns = val({Tab, where_to_write}),
+ rpc:abcast(Ns, ?MODULE, {stick, Oid, N}).
+
+stuck_elsewhere(Tid, Store, Tab, _Key, Oid, _Lock) ->
+ rlock(Tid, Store, {Tab, ?ALL}), %% needed?
+ wlock(Tid, Store, Oid), %% perfect sync
+ wlock(Tid, Store, {Tab, ?STICK}), %% max one sticker/table
+ Ns = val({Tab, where_to_write}),
+ rpc:abcast(Ns, ?MODULE, {unstick, Tab}).
+
+dirty_sticky_lock(Tab, Key, Nodes, Lock) ->
+ if
+ Lock == read_write ->
+ mnesia_lib:db_get(Tab, Key);
+ Key == ?ALL ->
+ Nodes;
+ Tab == ?GLOBAL ->
+ Nodes;
+ true ->
+ ok
+ end.
+
+sticky_wlock_table(Tid, Store, Tab) ->
+ sticky_lock(Tid, Store, {Tab, ?ALL}, write).
+
+%% aquire a wlock on Oid
+%% We store a {Tabname, write, Tid} in all locktables
+%% on all nodes containing a copy of Tabname
+%% We also store an item {{locks, Tab, Key}, write} in the
+%% local store when we have aquired the lock.
+%%
+wlock(Tid, Store, Oid) ->
+ {Tab, Key} = Oid,
+ case need_lock(Store, Tab, Key, write) of
+ yes ->
+ Ns = w_nodes(Tab),
+ Op = {self(), {write, Tid, Oid}},
+ ?ets_insert(Store, {{locks, Tab, Key}, write}),
+ get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid);
+ no when Key /= ?ALL, Tab /= ?GLOBAL ->
+ [];
+ no ->
+ w_nodes(Tab)
+ end.
+
+wlock_table(Tid, Store, Tab) ->
+ wlock(Tid, Store, {Tab, ?ALL}).
+
+%% Write lock even if the table does not exist
+
+wlock_no_exist(Tid, Store, Tab, Ns) ->
+ Oid = {Tab, ?ALL},
+ Op = {self(), {write, Tid, Oid}},
+ get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid).
+
+need_lock(Store, Tab, Key, LockPattern) ->
+ TabL = ?ets_match_object(Store, {{locks, Tab, ?ALL}, LockPattern}),
+ if
+ TabL == [] ->
+ KeyL = ?ets_match_object(Store, {{locks, Tab, Key}, LockPattern}),
+ if
+ KeyL == [] ->
+ yes;
+ true ->
+ no
+ end;
+ true ->
+ no
+ end.
+
+add_debug(Node) -> % Use process dictionary for debug info
+ case get(mnesia_wlock_nodes) of
+ undefined ->
+ put(mnesia_wlock_nodes, [Node]);
+ NodeList ->
+ put(mnesia_wlock_nodes, [Node|NodeList])
+ end.
+
+del_debug(Node) ->
+ case get(mnesia_wlock_nodes) of
+ undefined -> % Shouldn't happen
+ ignore;
+ [Node] ->
+ erase(mnesia_wlock_nodes);
+ List ->
+ put(mnesia_wlock_nodes, lists:delete(Node, List))
+ end.
+
+%% We first send lock requests to the lockmanagers on all
+%% nodes holding a copy of the table
+
+get_wlocks_on_nodes([Node | Tail], Orig, Store, Request, Oid) ->
+ {?MODULE, Node} ! Request,
+ ?ets_insert(Store, {nodes, Node}),
+ add_debug(Node),
+ get_wlocks_on_nodes(Tail, Orig, Store, Request, Oid);
+get_wlocks_on_nodes([], Orig, Store, _Request, Oid) ->
+ receive_wlocks(Orig, Orig, Store, Oid).
+
+receive_wlocks([Node | Tail], Res, Store, Oid) ->
+ receive
+ {?MODULE, Node, granted} ->
+ del_debug(Node),
+ receive_wlocks(Tail, Res, Store, Oid);
+ {?MODULE, Node, {granted, Val}} -> %% for rwlocks
+ del_debug(Node),
+ case opt_lookup_in_client(Val, Oid, write) of
+ C when record(C, cyclic) ->
+ flush_remaining(Tail, Node, {aborted, C});
+ Val2 ->
+ receive_wlocks(Tail, Val2, Store, Oid)
+ end;
+ {?MODULE, Node, {not_granted, Reason}} ->
+ del_debug(Node),
+ Reason1 = {aborted, Reason},
+ flush_remaining(Tail, Node, Reason1);
+ {mnesia_down, Node} ->
+ del_debug(Node),
+ Reason1 = {aborted, {node_not_running, Node}},
+ flush_remaining(Tail, Node, Reason1);
+ {?MODULE, Node, {switch, Node2, Req}} -> %% for rwlocks
+ del_debug(Node),
+ add_debug(Node2),
+ ?ets_insert(Store, {nodes, Node2}),
+ {?MODULE, Node2} ! Req,
+ receive_wlocks([Node2 | Tail], Res, Store, Oid)
+ end;
+
+receive_wlocks([], Res, _Store, _Oid) ->
+ Res.
+
+flush_remaining([], _SkipNode, Res) ->
+ exit(Res);
+flush_remaining([SkipNode | Tail ], SkipNode, Res) ->
+ del_debug(SkipNode),
+ flush_remaining(Tail, SkipNode, Res);
+flush_remaining([Node | Tail], SkipNode, Res) ->
+ receive
+ {?MODULE, Node, _} ->
+ del_debug(Node),
+ flush_remaining(Tail, SkipNode, Res);
+ {mnesia_down, Node} ->
+ del_debug(Node),
+ flush_remaining(Tail, SkipNode, {aborted, {node_not_running, Node}})
+ end.
+
+opt_lookup_in_client(lookup_in_client, Oid, Lock) ->
+ {Tab, Key} = Oid,
+ case catch mnesia_lib:db_get(Tab, Key) of
+ {'EXIT', _} ->
+ %% Table has been deleted from this node,
+ %% restart the transaction.
+ #cyclic{op = read, lock = Lock, oid = Oid, lucky = nowhere};
+ Val ->
+ Val
+ end;
+opt_lookup_in_client(Val, _Oid, _Lock) ->
+ Val.
+
+return_granted_or_nodes({_, ?ALL} , Nodes) -> Nodes;
+return_granted_or_nodes({?GLOBAL, _}, Nodes) -> Nodes;
+return_granted_or_nodes(_ , _Nodes) -> granted.
+
+%% We store a {Tab, read, From} item in the
+%% locks table on the node where we actually do pick up the object
+%% and we also store an item {lock, Oid, read} in our local store
+%% so that we can release any locks we hold when we commit.
+%% This function not only aquires a read lock, but also reads the object
+
+%% Oid's are always {Tab, Key} tuples
+rlock(Tid, Store, Oid) ->
+ {Tab, Key} = Oid,
+ case val({Tab, where_to_read}) of
+ nowhere ->
+ mnesia:abort({no_exists, Tab});
+ Node ->
+ case need_lock(Store, Tab, Key, '_') of
+ yes ->
+ R = l_request(Node, {read, Tid, Oid}, Store),
+ rlock_get_reply(Node, Store, Oid, R);
+ no ->
+ if
+ Key == ?ALL ->
+ [Node];
+ Tab == ?GLOBAL ->
+ [Node];
+ true ->
+ dirty_rpc(Node, Tab, Key, read)
+ end
+ end
+ end.
+
+dirty_rpc(nowhere, Tab, Key, _Lock) ->
+ mnesia:abort({no_exists, {Tab, Key}});
+dirty_rpc(Node, _Tab, ?ALL, _Lock) ->
+ [Node];
+dirty_rpc(Node, ?GLOBAL, _Key, _Lock) ->
+ [Node];
+dirty_rpc(Node, Tab, Key, Lock) ->
+ Args = [Tab, Key],
+ case rpc:call(Node, mnesia_lib, db_get, Args) of
+ {badrpc, Reason} ->
+ case val({Tab, where_to_read}) of
+ Node ->
+ ErrorTag = mnesia_lib:dirty_rpc_error_tag(Reason),
+ mnesia:abort({ErrorTag, Args});
+ _NewNode ->
+ %% Table has been deleted from the node,
+ %% restart the transaction.
+ C = #cyclic{op = read, lock = Lock, oid = {Tab, Key}, lucky = nowhere},
+ exit({aborted, C})
+ end;
+ Other ->
+ Other
+ end.
+
+rlock_get_reply(Node, Store, Oid, {granted, V}) ->
+ {Tab, Key} = Oid,
+ ?ets_insert(Store, {{locks, Tab, Key}, read}),
+ ?ets_insert(Store, {nodes, Node}),
+ case opt_lookup_in_client(V, Oid, read) of
+ C when record(C, cyclic) ->
+ mnesia:abort(C);
+ Val ->
+ Val
+ end;
+rlock_get_reply(Node, Store, Oid, granted) ->
+ {Tab, Key} = Oid,
+ ?ets_insert(Store, {{locks, Tab, Key}, read}),
+ ?ets_insert(Store, {nodes, Node}),
+ return_granted_or_nodes(Oid, [Node]);
+rlock_get_reply(Node, Store, Tab, {granted, V, RealKeys}) ->
+ L = fun(K) -> ?ets_insert(Store, {{locks, Tab, K}, read}) end,
+ lists:foreach(L, RealKeys),
+ ?ets_insert(Store, {nodes, Node}),
+ V;
+rlock_get_reply(_Node, _Store, _Oid, {not_granted , Reason}) ->
+ exit({aborted, Reason});
+
+rlock_get_reply(_Node, Store, Oid, {switch, N2, Req}) ->
+ ?ets_insert(Store, {nodes, N2}),
+ {?MODULE, N2} ! Req,
+ rlock_get_reply(N2, Store, Oid, l_req_rec(N2, Store)).
+
+
+rlock_table(Tid, Store, Tab) ->
+ rlock(Tid, Store, {Tab, ?ALL}).
+
+ixrlock(Tid, Store, Tab, IxKey, Pos) ->
+ case val({Tab, where_to_read}) of
+ nowhere ->
+ mnesia:abort({no_exists, Tab});
+ Node ->
+ R = l_request(Node, {ix_read, Tid, Tab, IxKey, Pos}, Store),
+ rlock_get_reply(Node, Store, Tab, R)
+ end.
+
+%% Grabs the locks or exits
+global_lock(Tid, Store, Item, write, Ns) ->
+ Oid = {?GLOBAL, Item},
+ Op = {self(), {write, Tid, Oid}},
+ get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid);
+global_lock(Tid, Store, Item, read, Ns) ->
+ Oid = {?GLOBAL, Item},
+ send_requests(Ns, {read, Tid, Oid}),
+ rec_requests(Ns, Oid, Store),
+ Ns.
+
+send_requests([Node | Nodes], X) ->
+ {?MODULE, Node} ! {self(), X},
+ send_requests(Nodes, X);
+send_requests([], _X) ->
+ ok.
+
+rec_requests([Node | Nodes], Oid, Store) ->
+ Res = l_req_rec(Node, Store),
+ case catch rlock_get_reply(Node, Store, Oid, Res) of
+ {'EXIT', Reason} ->
+ flush_remaining(Nodes, Node, Reason);
+ _ ->
+ rec_requests(Nodes, Oid, Store)
+ end;
+rec_requests([], _Oid, _Store) ->
+ ok.
+
+get_held_locks() ->
+ ?ets_match_object(mnesia_held_locks, '_').
+
+get_lock_queue() ->
+ Q = ?ets_match_object(mnesia_lock_queue, '_'),
+ [{Oid, Op, Pid, Tid, WFT} || {queue, Oid, Tid, Op, Pid, WFT} <- Q].
+
+do_stop() ->
+ exit(shutdown).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% System upgrade
+
+system_continue(_Parent, _Debug, State) ->
+ loop(State).
+
+system_terminate(_Reason, _Parent, _Debug, _State) ->
+ do_stop().
+
+system_code_change(State, _Module, _OldVsn, _Extra) ->
+ {ok, State}.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_log.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_log.erl
new file mode 100644
index 0000000000..79bd8d3812
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_log.erl
@@ -0,0 +1,1019 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_log.erl,v 1.2 2009/07/01 15:45:40 kostis Exp $
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% This module administers three kinds of log files:
+%%
+%% 1 The transaction log
+%% mnesia_tm appends to the log (via mnesia_log) at the
+%% end of each transaction (or dirty write) and
+%% mnesia_dumper reads the log and performs the ops in
+%% the dat files. The dump_log is done at startup and
+%% at intervals controlled by the user.
+%%
+%% 2 The mnesia_down log
+%% mnesia_tm appends to the log (via mnesia_log) when it
+%% realizes that mnesia goes up or down on another node.
+%% mnesia_init reads the log (via mnesia_log) at startup.
+%%
+%% 3 The backup log
+%% mnesia_schema produces one tiny log when the schema is
+%% initially created. mnesia_schema also reads the log
+%% when the user wants tables (possibly incl the schema)
+%% to be restored. mnesia_log appends to the log when the
+%% user wants to produce a real backup.
+%%
+%% The actual access to the backup media is performed via the
+%% mnesia_backup module for both read and write. mnesia_backup
+%% uses the disk_log (*), BUT the user may write an own module
+%% with the same interface as mnesia_backup and configure
+%% Mnesia so the alternate module performs the actual accesses
+%% to the backup media. This means that the user may put the
+%% backup on medias that Mnesia does not know about possibly on
+%% hosts where Erlang is not running.
+%%
+%% All these logs have to some extent a common structure.
+%% They are all using the disk_log module (*) for the basic
+%% file structure. The disk_log has a repair feature that
+%% can be used to skip erroneous log records if one comes to
+%% the conclusion that it is more important to reuse some
+%% of the log records than the risque of obtaining inconsistent
+%% data. If the data becomes inconsistent it is solely up to the
+%% application to make it consistent again. The automatic
+%% reparation of the disk_log is very powerful, but use it
+%% with extreme care.
+%%
+%% First in all Mnesia's log file is a mnesia log header.
+%% It contains a list with a log_header record as single
+%% element. The structure of the log_header may never be
+%% changed since it may be written to very old backup files.
+%% By holding this record definition stable we can be
+%% able to comprahend backups from timepoint 0. It also
+%% allows us to use the backup format as an interchange
+%% format between Mnesia releases.
+%%
+%% An op-list is a list of tuples with arity 3. Each tuple
+%% has this structure: {Oid, Recs, Op} where Oid is the tuple
+%% {Tab, Key}, Recs is a (possibly empty) list of records and
+%% Op is an atom.
+%%
+%% The log file structure for the transaction log is as follows.
+%%
+%% After the mnesia log section follows an extended record section
+%% containing op-lists. There are several values that Op may
+%% have, such as write, delete, update_counter, delete_object,
+%% and replace. There is no special end of section marker.
+%%
+%% +-----------------+
+%% | mnesia log head |
+%% +-----------------+
+%% | extended record |
+%% | section |
+%% +-----------------+
+%%
+%% The log file structure for the mnesia_down log is as follows.
+%%
+%% After the mnesia log section follows a mnesia_down section
+%% containg lists with yoyo records as single element.
+%%
+%% +-----------------+
+%% | mnesia log head |
+%% +-----------------+
+%% | mnesia_down |
+%% | section |
+%% +-----------------+
+%%
+%% The log file structure for the backup log is as follows.
+%%
+%% After the mnesia log section follows a schema section
+%% containing record lists. A record list is a list of tuples
+%% where {schema, Tab} is interpreted as a delete_table(Tab) and
+%% {schema, Tab, CreateList} are interpreted as create_table.
+%%
+%% The record section also contains record lists. In this section
+%% {Tab, Key} is interpreted as delete({Tab, Key}) and other tuples
+%% as write(Tuple). There is no special end of section marker.
+%%
+%% +-----------------+
+%% | mnesia log head |
+%% +-----------------+
+%% | schema section |
+%% +-----------------+
+%% | record section |
+%% +-----------------+
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(mnesia_log).
+
+-export([
+ append/2,
+ backup/1,
+ backup/2,
+ backup_checkpoint/2,
+ backup_checkpoint/3,
+ backup_log_header/0,
+ backup_master/2,
+ chunk_decision_log/1,
+ chunk_decision_tab/1,
+ chunk_log/1,
+ chunk_log/2,
+ close_decision_log/0,
+ close_decision_tab/0,
+ close_log/1,
+ unsafe_close_log/1,
+ confirm_log_dump/1,
+ confirm_decision_log_dump/0,
+ previous_log_file/0,
+ previous_decision_log_file/0,
+ latest_log_file/0,
+ decision_log_version/0,
+ decision_log_file/0,
+ decision_tab_file/0,
+ decision_tab_version/0,
+ dcl_version/0,
+ dcd_version/0,
+ ets2dcd/1,
+ ets2dcd/2,
+ dcd2ets/1,
+ dcd2ets/2,
+ init/0,
+ init_log_dump/0,
+ log/1,
+ slog/1,
+ log_decision/1,
+ log_files/0,
+ open_decision_log/0,
+ trans_log_header/0,
+ open_decision_tab/0,
+ dcl_log_header/0,
+ dcd_log_header/0,
+ open_log/4,
+ open_log/6,
+ prepare_decision_log_dump/0,
+ prepare_log_dump/1,
+ save_decision_tab/1,
+ purge_all_logs/0,
+ purge_some_logs/0,
+ stop/0,
+ tab_copier/3,
+ version/0,
+ view/0,
+ view/1,
+ write_trans_log_header/0
+ ]).
+
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [val/1, dir/1]).
+-import(mnesia_lib, [exists/1, fatal/2, error/2, dbg_out/2]).
+
+trans_log_header() -> log_header(trans_log, version()).
+backup_log_header() -> log_header(backup_log, "1.2").
+decision_log_header() -> log_header(decision_log, decision_log_version()).
+decision_tab_header() -> log_header(decision_tab, decision_tab_version()).
+dcl_log_header() -> log_header(dcl_log, dcl_version()).
+dcd_log_header() -> log_header(dcd_log, dcd_version()).
+
+log_header(Kind, Version) ->
+ #log_header{log_version=Version,
+ log_kind=Kind,
+ mnesia_version=mnesia:system_info(version),
+ node=node(),
+ now=now()}.
+
+version() -> "4.3".
+
+decision_log_version() -> "3.0".
+
+decision_tab_version() -> "1.0".
+
+dcl_version() -> "1.0".
+dcd_version() -> "1.0".
+
+append(Log, Bin) when binary(Bin) ->
+ disk_log:balog(Log, Bin);
+append(Log, Term) ->
+ disk_log:alog(Log, Term).
+
+%% Synced append
+sappend(Log, Bin) when binary(Bin) ->
+ ok = disk_log:blog(Log, Bin);
+sappend(Log, Term) ->
+ ok = disk_log:log(Log, Term).
+
+%% Write commit records to the latest_log
+log(C) when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [],
+ C#commit.schema_ops == [] ->
+ ignore;
+log(C) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ if
+ record(C, commit) ->
+ C2 = C#commit{ram_copies = [], snmp = []},
+ append(latest_log, C2);
+ true ->
+ %% Either a commit record as binary
+ %% or some decision related info
+ append(latest_log, C)
+ end,
+ mnesia_dumper:incr_log_writes();
+ false ->
+ ignore
+ end.
+
+%% Synced
+
+slog(C) when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [],
+ C#commit.schema_ops == [] ->
+ ignore;
+slog(C) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ if
+ record(C, commit) ->
+ C2 = C#commit{ram_copies = [], snmp = []},
+ sappend(latest_log, C2);
+ true ->
+ %% Either a commit record as binary
+ %% or some decision related info
+ sappend(latest_log, C)
+ end,
+ mnesia_dumper:incr_log_writes();
+ false ->
+ ignore
+ end.
+
+
+%% Stuff related to the file LOG
+
+%% Returns a list of logfiles. The oldest is first.
+log_files() -> [previous_log_file(),
+ latest_log_file(),
+ decision_tab_file()
+ ].
+
+latest_log_file() -> dir(latest_log_name()).
+
+previous_log_file() -> dir("PREVIOUS.LOG").
+
+decision_log_file() -> dir(decision_log_name()).
+
+decision_tab_file() -> dir(decision_tab_name()).
+
+previous_decision_log_file() -> dir("PDECISION.LOG").
+
+latest_log_name() -> "LATEST.LOG".
+
+decision_log_name() -> "DECISION.LOG".
+
+decision_tab_name() -> "DECISION_TAB.LOG".
+
+init() ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ Prev = previous_log_file(),
+ verify_no_exists(Prev),
+
+ Latest = latest_log_file(),
+ verify_no_exists(Latest),
+
+ Header = trans_log_header(),
+ open_log(latest_log, Header, Latest);
+ false ->
+ ok
+ end.
+
+verify_no_exists(Fname) ->
+ case exists(Fname) of
+ false ->
+ ok;
+ true ->
+ fatal("Log file exists: ~p~n", [Fname])
+ end.
+
+open_log(Name, Header, Fname) ->
+ Exists = exists(Fname),
+ open_log(Name, Header, Fname, Exists).
+
+open_log(Name, Header, Fname, Exists) ->
+ Repair = mnesia_monitor:get_env(auto_repair),
+ open_log(Name, Header, Fname, Exists, Repair).
+
+open_log(Name, Header, Fname, Exists, Repair) ->
+ case Name == previous_log of
+ true ->
+ open_log(Name, Header, Fname, Exists, Repair, read_only);
+ false ->
+ open_log(Name, Header, Fname, Exists, Repair, read_write)
+ end.
+
+open_log(Name, Header, Fname, Exists, Repair, Mode) ->
+ Args = [{file, Fname}, {name, Name}, {repair, Repair}, {mode, Mode}],
+%% io:format("~p:open_log: ~p ~p~n", [?MODULE, Name, Fname]),
+ case mnesia_monitor:open_log(Args) of
+ {ok, Log} when Exists == true ->
+ Log;
+ {ok, Log} ->
+ write_header(Log, Header),
+ Log;
+ {repaired, Log, _, {badbytes, 0}} when Exists == true ->
+ Log;
+ {repaired, Log, _, {badbytes, 0}} ->
+ write_header(Log, Header),
+ Log;
+ {repaired, Log, _Recover, BadBytes} ->
+ mnesia_lib:important("Data may be missing, log ~p repaired: Lost ~p bytes~n",
+ [Fname, BadBytes]),
+ Log;
+ {error, Reason} when Repair == true ->
+ file:delete(Fname),
+ mnesia_lib:important("Data may be missing, Corrupt logfile deleted: ~p, ~p ~n",
+ [Fname, Reason]),
+ %% Create a new
+ open_log(Name, Header, Fname, false, false, read_write);
+ {error, Reason} ->
+ fatal("Cannot open log file ~p: ~p~n", [Fname, Reason])
+ end.
+
+write_header(Log, Header) ->
+ append(Log, Header).
+
+write_trans_log_header() ->
+ write_header(latest_log, trans_log_header()).
+
+stop() ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ close_log(latest_log);
+ false ->
+ ok
+ end.
+
+close_log(Log) ->
+%% io:format("mnesia_log:close_log ~p~n", [Log]),
+%% io:format("mnesia_log:close_log ~p~n", [Log]),
+ case disk_log:sync(Log) of
+ ok -> ok;
+ {error, {read_only_mode, Log}} ->
+ ok;
+ {error, Reason} ->
+ mnesia_lib:important("Failed syncing ~p to_disk reason ~p ~n",
+ [Log, Reason])
+ end,
+ mnesia_monitor:close_log(Log).
+
+unsafe_close_log(Log) ->
+%% io:format("mnesia_log:close_log ~p~n", [Log]),
+ mnesia_monitor:unsafe_close_log(Log).
+
+
+purge_some_logs() ->
+ mnesia_monitor:unsafe_close_log(latest_log),
+ file:delete(latest_log_file()),
+ file:delete(decision_tab_file()).
+
+purge_all_logs() ->
+ file:delete(previous_log_file()),
+ file:delete(latest_log_file()),
+ file:delete(decision_tab_file()).
+
+%% Prepare dump by renaming the open logfile if possible
+%% Returns a tuple on the following format: {Res, OpenLog}
+%% where OpenLog is the file descriptor to log file, ready for append
+%% and Res is one of the following: already_dumped, needs_dump or {error, Reason}
+prepare_log_dump(InitBy) ->
+ Diff = mnesia_dumper:get_log_writes() -
+ mnesia_lib:read_counter(trans_log_writes_prev),
+ if
+ Diff == 0, InitBy /= startup ->
+ already_dumped;
+ true ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ Prev = previous_log_file(),
+ prepare_prev(Diff, InitBy, Prev, exists(Prev));
+ false ->
+ already_dumped
+ end
+ end.
+
+prepare_prev(Diff, _, _, true) ->
+ {needs_dump, Diff};
+prepare_prev(Diff, startup, Prev, false) ->
+ Latest = latest_log_file(),
+ case exists(Latest) of
+ true ->
+ case file:rename(Latest, Prev) of
+ ok ->
+ {needs_dump, Diff};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ false ->
+ already_dumped
+ end;
+prepare_prev(Diff, _InitBy, Prev, false) ->
+ Head = trans_log_header(),
+ case mnesia_monitor:reopen_log(latest_log, Prev, Head) of
+ ok ->
+ {needs_dump, Diff};
+ {error, Reason} ->
+ Latest = latest_log_file(),
+ {error, {"Cannot rename log file",
+ [Latest, Prev, Reason]}}
+ end.
+
+%% Init dump and return PrevLogFileDesc or exit.
+init_log_dump() ->
+ Fname = previous_log_file(),
+ open_log(previous_log, trans_log_header(), Fname),
+ start.
+
+
+chunk_log(Cont) ->
+ chunk_log(previous_log, Cont).
+
+chunk_log(_Log, eof) ->
+ eof;
+chunk_log(Log, Cont) ->
+ case catch disk_log:chunk(Log, Cont) of
+ {error, Reason} ->
+ fatal("Possibly truncated ~p file: ~p~n",
+ [Log, Reason]);
+ {C2, Chunk, _BadBytes} ->
+ %% Read_only case, should we warn about the bad log file?
+ %% BUGBUG Should we crash if Repair == false ??
+ %% We got to check this !!
+ mnesia_lib:important("~p repaired, lost ~p bad bytes~n", [Log, _BadBytes]),
+ {C2, Chunk};
+ Other ->
+ Other
+ end.
+
+%% Confirms the dump by closing prev log and delete the file
+confirm_log_dump(Updates) ->
+ case mnesia_monitor:close_log(previous_log) of
+ ok ->
+ file:delete(previous_log_file()),
+ mnesia_lib:incr_counter(trans_log_writes_prev, Updates),
+ dumped;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Decision log
+
+open_decision_log() ->
+ Latest = decision_log_file(),
+ open_log(decision_log, decision_log_header(), Latest),
+ start.
+
+prepare_decision_log_dump() ->
+ Prev = previous_decision_log_file(),
+ prepare_decision_log_dump(exists(Prev), Prev).
+
+prepare_decision_log_dump(false, Prev) ->
+ Head = decision_log_header(),
+ case mnesia_monitor:reopen_log(decision_log, Prev, Head) of
+ ok ->
+ prepare_decision_log_dump(true, Prev);
+ {error, Reason} ->
+ fatal("Cannot rename decision log file ~p -> ~p: ~p~n",
+ [decision_log_file(), Prev, Reason])
+ end;
+prepare_decision_log_dump(true, Prev) ->
+ open_log(previous_decision_log, decision_log_header(), Prev),
+ start.
+
+chunk_decision_log(Cont) ->
+ %% dbg_out("chunk log ~p~n", [Cont]),
+ chunk_log(previous_decision_log, Cont).
+
+%% Confirms dump of the decision log
+confirm_decision_log_dump() ->
+ case mnesia_monitor:close_log(previous_decision_log) of
+ ok ->
+ file:delete(previous_decision_log_file());
+ {error, Reason} ->
+ fatal("Cannot confirm decision log dump: ~p~n",
+ [Reason])
+ end.
+
+save_decision_tab(Decisions) ->
+ Log = decision_tab,
+ Tmp = mnesia_lib:dir("DECISION_TAB.TMP"),
+ file:delete(Tmp),
+ open_log(Log, decision_tab_header(), Tmp),
+ append(Log, Decisions),
+ close_log(Log),
+ TabFile = decision_tab_file(),
+ ok = file:rename(Tmp, TabFile).
+
+open_decision_tab() ->
+ TabFile = decision_tab_file(),
+ open_log(decision_tab, decision_tab_header(), TabFile),
+ start.
+
+close_decision_tab() ->
+ close_log(decision_tab).
+
+chunk_decision_tab(Cont) ->
+ %% dbg_out("chunk tab ~p~n", [Cont]),
+ chunk_log(decision_tab, Cont).
+
+close_decision_log() ->
+ close_log(decision_log).
+
+log_decision(Decision) ->
+ append(decision_log, Decision).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Debug functions
+
+view() ->
+ lists:foreach(fun(F) -> view(F) end, log_files()).
+
+view(File) ->
+ mnesia_lib:show("***** ~p ***** ~n", [File]),
+ case exists(File) of
+ false ->
+ nolog;
+ true ->
+ N = view_only,
+ Args = [{file, File}, {name, N}, {mode, read_only}],
+ case disk_log:open(Args) of
+ {ok, N} ->
+ view_file(start, N);
+ {repaired, _, _, _} ->
+ view_file(start, N);
+ {error, Reason} ->
+ error("Cannot open log ~p: ~p~n", [File, Reason])
+ end
+ end.
+
+view_file(C, Log) ->
+ case disk_log:chunk(Log, C) of
+ {error, Reason} ->
+ error("** Possibly truncated FILE ~p~n", [Reason]),
+ error;
+ eof ->
+ disk_log:close(Log),
+ eof;
+ {C2, Terms, _BadBytes} ->
+ dbg_out("Lost ~p bytes in ~p ~n", [_BadBytes, Log]),
+ lists:foreach(fun(X) -> mnesia_lib:show("~p~n", [X]) end,
+ Terms),
+ view_file(C2, Log);
+ {C2, Terms} ->
+ lists:foreach(fun(X) -> mnesia_lib:show("~p~n", [X]) end,
+ Terms),
+ view_file(C2, Log)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Backup
+
+-record(backup_args, {name, module, opaque, scope, prev_name, tables, cookie}).
+
+backup(Opaque) ->
+ backup(Opaque, []).
+
+backup(Opaque, Mod) when atom(Mod) ->
+ backup(Opaque, [{module, Mod}]);
+backup(Opaque, Args) when list(Args) ->
+ %% Backup all tables with max redundancy
+ CpArgs = [{ram_overrides_dump, false}, {max, val({schema, tables})}],
+ case mnesia_checkpoint:activate(CpArgs) of
+ {ok, Name, _Nodes} ->
+ Res = backup_checkpoint(Name, Opaque, Args),
+ mnesia_checkpoint:deactivate(Name),
+ Res;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+backup_checkpoint(Name, Opaque) ->
+ backup_checkpoint(Name, Opaque, []).
+
+backup_checkpoint(Name, Opaque, Mod) when atom(Mod) ->
+ backup_checkpoint(Name, Opaque, [{module, Mod}]);
+backup_checkpoint(Name, Opaque, Args) when list(Args) ->
+ DefaultMod = mnesia_monitor:get_env(backup_module),
+ B = #backup_args{name = Name,
+ module = DefaultMod,
+ opaque = Opaque,
+ scope = global,
+ tables = all,
+ prev_name = Name},
+ case check_backup_args(Args, B) of
+ {ok, B2} ->
+ %% Decentralized backup
+ %% Incremental
+
+ Self = self(),
+ Pid = spawn_link(?MODULE, backup_master, [Self, B2]),
+ receive
+ {Pid, Self, Res} -> Res
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+check_backup_args([Arg | Tail], B) ->
+ case catch check_backup_arg_type(Arg, B) of
+ {'EXIT', _Reason} ->
+ {error, {badarg, Arg}};
+ B2 ->
+ check_backup_args(Tail, B2)
+ end;
+
+check_backup_args([], B) ->
+ {ok, B}.
+
+check_backup_arg_type(Arg, B) ->
+ case Arg of
+ {scope, global} ->
+ B#backup_args{scope = global};
+ {scope, local} ->
+ B#backup_args{scope = local};
+ {module, Mod} ->
+ Mod2 = mnesia_monitor:do_check_type(backup_module, Mod),
+ B#backup_args{module = Mod2};
+ {incremental, Name} ->
+ B#backup_args{prev_name = Name};
+ {tables, Tabs} when list(Tabs) ->
+ B#backup_args{tables = Tabs}
+ end.
+
+backup_master(ClientPid, B) ->
+ process_flag(trap_exit, true),
+ case catch do_backup_master(B) of
+ {'EXIT', Reason} ->
+ ClientPid ! {self(), ClientPid, {error, {'EXIT', Reason}}};
+ Res ->
+ ClientPid ! {self(), ClientPid, Res}
+ end,
+ unlink(ClientPid),
+ exit(normal).
+
+do_backup_master(B) ->
+ Name = B#backup_args.name,
+ B2 = safe_apply(B, open_write, [B#backup_args.opaque]),
+ B3 = safe_write(B2, [backup_log_header()]),
+ case mnesia_checkpoint:tables_and_cookie(Name) of
+ {ok, AllTabs, Cookie} ->
+ Tabs = select_tables(AllTabs, B3),
+ B4 = B3#backup_args{cookie = Cookie},
+ %% Always put schema first in backup file
+ B5 = backup_schema(B4, Tabs),
+ B6 = lists:foldl(fun backup_tab/2, B5, Tabs -- [schema]),
+ safe_apply(B6, commit_write, [B6#backup_args.opaque]),
+ ok;
+ {error, Reason} ->
+ abort_write(B3, {?MODULE, backup_master}, [B], {error, Reason})
+ end.
+
+select_tables(AllTabs, B) ->
+ Tabs =
+ case B#backup_args.tables of
+ all -> AllTabs;
+ SomeTabs when list(SomeTabs) -> SomeTabs
+ end,
+ case B#backup_args.scope of
+ global ->
+ Tabs;
+ local ->
+ Name = B#backup_args.name,
+ [T || T <- Tabs, mnesia_checkpoint:most_local_node(Name, T) == node()]
+ end.
+
+safe_write(B, []) ->
+ B;
+safe_write(B, Recs) ->
+ safe_apply(B, write, [B#backup_args.opaque, Recs]).
+
+backup_schema(B, Tabs) ->
+ case lists:member(schema, Tabs) of
+ true ->
+ backup_tab(schema, B);
+ false ->
+ Defs = [{schema, T, mnesia_schema:get_create_list(T)} || T <- Tabs],
+ safe_write(B, Defs)
+ end.
+
+safe_apply(B, write, [_, Items]) when Items == [] ->
+ B;
+safe_apply(B, What, Args) ->
+ Abort = fun(R) -> abort_write(B, What, Args, R) end,
+ receive
+ {'EXIT', Pid, R} -> Abort({'EXIT', Pid, R})
+ after 0 ->
+ Mod = B#backup_args.module,
+ case catch apply(Mod, What, Args) of
+ {ok, Opaque} -> B#backup_args{opaque=Opaque};
+ {error, R} -> Abort(R);
+ R -> Abort(R)
+ end
+ end.
+
+abort_write(B, What, Args, Reason) ->
+ Mod = B#backup_args.module,
+ Opaque = B#backup_args.opaque,
+ dbg_out("Failed to perform backup. M=~p:F=~p:A=~p -> ~p~n",
+ [Mod, What, Args, Reason]),
+ case catch apply(Mod, abort_write, [Opaque]) of
+ {ok, _Res} ->
+ throw({error, Reason});
+ Other ->
+ error("Failed to abort backup. ~p:~p~p -> ~p~n",
+ [Mod, abort_write, [Opaque], Other]),
+ throw({error, Reason})
+ end.
+
+backup_tab(Tab, B) ->
+ Name = B#backup_args.name,
+ case mnesia_checkpoint:most_local_node(Name, Tab) of
+ {ok, Node} when Node == node() ->
+ tab_copier(self(), B, Tab);
+ {ok, Node} ->
+ RemoteB = B,
+ Pid = spawn_link(Node, ?MODULE, tab_copier, [self(), RemoteB, Tab]),
+ RecName = val({Tab, record_name}),
+ tab_receiver(Pid, B, Tab, RecName, 0);
+ {error, Reason} ->
+ abort_write(B, {?MODULE, backup_tab}, [Tab, B], {error, Reason})
+ end.
+
+tab_copier(Pid, B, Tab) when record(B, backup_args) ->
+ %% Intentional crash at exit
+ Name = B#backup_args.name,
+ PrevName = B#backup_args.prev_name,
+ {FirstName, FirstSource} = select_source(Tab, Name, PrevName),
+
+ ?eval_debug_fun({?MODULE, tab_copier, pre}, [{name, Name}, {tab, Tab}]),
+ Res = handle_more(Pid, B, Tab, FirstName, FirstSource, Name),
+ ?eval_debug_fun({?MODULE, tab_copier, post}, [{name, Name}, {tab, Tab}]),
+
+ handle_last(Pid, Res).
+
+select_source(Tab, Name, PrevName) ->
+ if
+ Tab == schema ->
+ %% Always full backup of schema
+ {Name, table};
+ Name == PrevName ->
+ %% Full backup
+ {Name, table};
+ true ->
+ %% Wants incremental backup
+ case mnesia_checkpoint:most_local_node(PrevName, Tab) of
+ {ok, Node} when Node == node() ->
+ %% Accept incremental backup
+ {PrevName, retainer};
+ _ ->
+ %% Do a full backup anyway
+ dbg_out("Incremental backup escalated to full backup: ~p~n", [Tab]),
+ {Name, table}
+ end
+ end.
+
+handle_more(Pid, B, Tab, FirstName, FirstSource, Name) ->
+ Acc = {0, B},
+ case {mnesia_checkpoint:really_retain(Name, Tab),
+ mnesia_checkpoint:really_retain(FirstName, Tab)} of
+ {true, true} ->
+ Acc2 = iterate(B, FirstName, Tab, Pid, FirstSource, latest, first, Acc),
+ iterate(B, Name, Tab, Pid, retainer, checkpoint, last, Acc2);
+ {false, false}->
+ %% Put the dumped file in the backup
+ %% instead of the ram table. Does
+ %% only apply to ram_copies.
+ iterate(B, Name, Tab, Pid, retainer, checkpoint, last, Acc);
+ Bad ->
+ Reason = {"Checkpoints for incremental backup must have same "
+ "setting of ram_overrides_dump",
+ Tab, Name, FirstName, Bad},
+ abort_write(B, {?MODULE, backup_tab}, [Tab, B], {error, Reason})
+ end.
+
+handle_last(Pid, {_Count, B}) when Pid == self() ->
+ B;
+handle_last(Pid, _Acc) ->
+ unlink(Pid),
+ Pid ! {self(), {last, {ok, dummy}}},
+ exit(normal).
+
+iterate(B, Name, Tab, Pid, Source, Age, Pass, Acc) ->
+ Fun =
+ if
+ Pid == self() ->
+ RecName = val({Tab, record_name}),
+ fun(Recs, A) -> copy_records(RecName, Tab, Recs, A) end;
+ true ->
+ fun(Recs, A) -> send_records(Pid, Tab, Recs, Pass, A) end
+ end,
+ case mnesia_checkpoint:iterate(Name, Tab, Fun, Acc, Source, Age) of
+ {ok, Acc2} ->
+ Acc2;
+ {error, Reason} ->
+ R = {error, {"Tab copier iteration failed", Reason}},
+ abort_write(B, {?MODULE, iterate}, [self(), B, Tab], R)
+ end.
+
+copy_records(_RecName, _Tab, [], Acc) ->
+ Acc;
+copy_records(RecName, Tab, Recs, {Count, B}) ->
+ Recs2 = rec_filter(B, Tab, RecName, Recs),
+ B2 = safe_write(B, Recs2),
+ {Count + 1, B2}.
+
+send_records(Pid, Tab, Recs, Pass, {Count, B}) ->
+ receive
+ {Pid, more, Count} ->
+ if
+ Pass == last, Recs == [] ->
+ {Count, B};
+ true ->
+ Next = Count + 1,
+ Pid ! {self(), {more, Next, Recs}},
+ {Next, B}
+ end;
+ Msg ->
+ exit({send_records_unexpected_msg, Tab, Msg})
+ end.
+
+tab_receiver(Pid, B, Tab, RecName, Slot) ->
+ Pid ! {self(), more, Slot},
+ receive
+ {Pid, {more, Next, Recs}} ->
+ Recs2 = rec_filter(B, Tab, RecName, Recs),
+ B2 = safe_write(B, Recs2),
+ tab_receiver(Pid, B2, Tab, RecName, Next);
+
+ {Pid, {last, {ok,_}}} ->
+ B;
+
+ {'EXIT', Pid, {error, R}} ->
+ Reason = {error, {"Tab copier crashed", R}},
+ abort_write(B, {?MODULE, remote_tab_sender}, [self(), B, Tab], Reason);
+ {'EXIT', Pid, R} ->
+ Reason = {error, {"Tab copier crashed", {'EXIT', R}}},
+ abort_write(B, {?MODULE, remote_tab_sender}, [self(), B, Tab], Reason);
+ Msg ->
+ R = {error, {"Tab receiver got unexpected msg", Msg}},
+ abort_write(B, {?MODULE, remote_tab_sender}, [self(), B, Tab], R)
+ end.
+
+rec_filter(B, schema, _RecName, Recs) ->
+ case catch mnesia_bup:refresh_cookie(Recs, B#backup_args.cookie) of
+ Recs2 when list(Recs2) ->
+ Recs2;
+ {error, _Reason} ->
+ %% No schema table cookie
+ Recs
+ end;
+rec_filter(_B, Tab, Tab, Recs) ->
+ Recs;
+rec_filter(_B, Tab, _RecName, Recs) ->
+ [setelement(1, Rec, Tab) || Rec <- Recs].
+
+ets2dcd(Tab) ->
+ ets2dcd(Tab, dcd).
+
+ets2dcd(Tab, Ftype) ->
+ Fname =
+ case Ftype of
+ dcd -> mnesia_lib:tab2dcd(Tab);
+ dmp -> mnesia_lib:tab2dmp(Tab)
+ end,
+ TmpF = mnesia_lib:tab2tmp(Tab),
+ file:delete(TmpF),
+ Log = open_log({Tab, ets2dcd}, dcd_log_header(), TmpF, false),
+ mnesia_lib:db_fixtable(ram_copies, Tab, true),
+ ok = ets2dcd(mnesia_lib:db_init_chunk(ram_copies, Tab, 1000), Tab, Log),
+ mnesia_lib:db_fixtable(ram_copies, Tab, false),
+ close_log(Log),
+ ok = file:rename(TmpF, Fname),
+ %% Remove old log data which is now in the new dcd.
+ %% No one else should be accessing this file!
+ file:delete(mnesia_lib:tab2dcl(Tab)),
+ ok.
+
+ets2dcd('$end_of_table', _Tab, _Log) ->
+ ok;
+ets2dcd({Recs, Cont}, Tab, Log) ->
+ ok = disk_log:alog_terms(Log, Recs),
+ ets2dcd(mnesia_lib:db_chunk(ram_copies, Cont), Tab, Log).
+
+dcd2ets(Tab) ->
+ dcd2ets(Tab, mnesia_monitor:get_env(auto_repair)).
+
+dcd2ets(Tab, Rep) ->
+ Dcd = mnesia_lib:tab2dcd(Tab),
+ case mnesia_lib:exists(Dcd) of
+ true ->
+ Log = open_log({Tab, dcd2ets}, dcd_log_header(), Dcd,
+ true, Rep, read_only),
+ Data = chunk_log(Log, start),
+ ok = insert_dcdchunk(Data, Log, Tab),
+ close_log(Log),
+ load_dcl(Tab, Rep);
+ false -> %% Handle old dets files, and conversion from disc_only to disc.
+ Fname = mnesia_lib:tab2dat(Tab),
+ Type = val({Tab, setorbag}),
+ case mnesia_lib:dets_to_ets(Tab, Tab, Fname, Type, Rep, yes) of
+ loaded ->
+ ets2dcd(Tab),
+ file:delete(Fname),
+ 0;
+ {error, Error} ->
+ erlang:error({"Failed to load table from disc", [Tab, Error]})
+ end
+ end.
+
+insert_dcdchunk({Cont, [LogH | Rest]}, Log, Tab)
+ when record(LogH, log_header),
+ LogH#log_header.log_kind == dcd_log,
+ LogH#log_header.log_version >= "1.0" ->
+ insert_dcdchunk({Cont, Rest}, Log, Tab);
+
+insert_dcdchunk({Cont, Recs}, Log, Tab) ->
+ true = ets:insert(Tab, Recs),
+ insert_dcdchunk(chunk_log(Log, Cont), Log, Tab);
+insert_dcdchunk(eof, _Log, _Tab) ->
+ ok.
+
+load_dcl(Tab, Rep) ->
+ FName = mnesia_lib:tab2dcl(Tab),
+ case mnesia_lib:exists(FName) of
+ true ->
+ Name = {load_dcl,Tab},
+ open_log(Name,
+ dcl_log_header(),
+ FName,
+ true,
+ Rep,
+ read_only),
+ FirstChunk = chunk_log(Name, start),
+ N = insert_logchunk(FirstChunk, Name, 0),
+ close_log(Name),
+ N;
+ false ->
+ 0
+ end.
+
+insert_logchunk({C2, Recs}, Tab, C) ->
+ N = add_recs(Recs, C),
+ insert_logchunk(chunk_log(Tab, C2), Tab, C+N);
+insert_logchunk(eof, _Tab, C) ->
+ C.
+
+add_recs([{{Tab, _Key}, Val, write} | Rest], N) ->
+ true = ets:insert(Tab, Val),
+ add_recs(Rest, N+1);
+add_recs([{{Tab, Key}, _Val, delete} | Rest], N) ->
+ true = ets:delete(Tab, Key),
+ add_recs(Rest, N+1);
+add_recs([{{Tab, _Key}, Val, delete_object} | Rest], N) ->
+ true = ets:match_delete(Tab, Val),
+ add_recs(Rest, N+1);
+add_recs([{{Tab, Key}, Val, update_counter} | Rest], N) ->
+ {RecName, Incr} = Val,
+ case catch ets:update_counter(Tab, Key, Incr) of
+ CounterVal when integer(CounterVal) ->
+ ok;
+ _ ->
+ Zero = {RecName, Key, 0},
+ true = ets:insert(Tab, Zero)
+ end,
+ add_recs(Rest, N+1);
+add_recs([LogH|Rest], N)
+ when record(LogH, log_header),
+ LogH#log_header.log_kind == dcl_log,
+ LogH#log_header.log_version >= "1.0" ->
+ add_recs(Rest, N);
+add_recs([{{Tab, _Key}, _Val, clear_table} | Rest], N) ->
+ true = ets:match_delete(Tab, '_'),
+ add_recs(Rest, N+ets:info(Tab, size));
+add_recs([], N) ->
+ N.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_monitor.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_monitor.erl
new file mode 100644
index 0000000000..554f020ffb
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_monitor.erl
@@ -0,0 +1,776 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_monitor.erl,v 1.1 2008/12/17 09:53:38 mikpe Exp $
+%%
+-module(mnesia_monitor).
+
+-behaviour(gen_server).
+
+%% Public exports
+-export([
+ close_dets/1,
+ close_log/1,
+ detect_inconcistency/2,
+ get_env/1,
+ init/0,
+ mktab/2,
+ unsafe_mktab/2,
+ mnesia_down/2,
+ needs_protocol_conversion/1,
+ negotiate_protocol/1,
+ disconnect/1,
+ open_dets/2,
+ unsafe_open_dets/2,
+ open_log/1,
+ patch_env/2,
+ protocol_version/0,
+ reopen_log/3,
+ set_env/2,
+ start/0,
+ start_proc/4,
+ terminate_proc/3,
+ unsafe_close_dets/1,
+ unsafe_close_log/1,
+ use_dir/0,
+ do_check_type/2
+ ]).
+
+%% gen_server callbacks
+-export([
+ init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3
+ ]).
+
+%% Internal exports
+-export([
+ call/1,
+ cast/1,
+ detect_partitioned_network/2,
+ has_remote_mnesia_down/1
+ ]).
+
+-import(mnesia_lib, [dbg_out/2, verbose/2, error/2, fatal/2, set/2]).
+
+-include("mnesia.hrl").
+
+-record(state, {supervisor, pending_negotiators = [],
+ going_down = [], tm_started = false, early_connects = []}).
+
+-define(current_protocol_version, {7,6}).
+
+-define(previous_protocol_version, {7,5}).
+
+start() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE,
+ [self()], [{timeout, infinity}
+ %% ,{debug, [trace]}
+ ]).
+
+init() ->
+ call(init).
+
+mnesia_down(From, Node) ->
+ cast({mnesia_down, From, Node}).
+
+mktab(Tab, Args) ->
+ unsafe_call({mktab, Tab, Args}).
+unsafe_mktab(Tab, Args) ->
+ unsafe_call({unsafe_mktab, Tab, Args}).
+
+open_dets(Tab, Args) ->
+ unsafe_call({open_dets, Tab, Args}).
+unsafe_open_dets(Tab, Args) ->
+ unsafe_call({unsafe_open_dets, Tab, Args}).
+
+close_dets(Tab) ->
+ unsafe_call({close_dets, Tab}).
+
+unsafe_close_dets(Name) ->
+ unsafe_call({unsafe_close_dets, Name}).
+
+open_log(Args) ->
+ unsafe_call({open_log, Args}).
+
+reopen_log(Name, Fname, Head) ->
+ unsafe_call({reopen_log, Name, Fname, Head}).
+
+close_log(Name) ->
+ unsafe_call({close_log, Name}).
+
+unsafe_close_log(Name) ->
+ unsafe_call({unsafe_close_log, Name}).
+
+
+disconnect(Node) ->
+ cast({disconnect, Node}).
+
+%% Returns GoodNoodes
+%% Creates a link to each compatible monitor and
+%% protocol_version to agreed version upon success
+
+negotiate_protocol(Nodes) ->
+ Version = mnesia:system_info(version),
+ Protocols = acceptable_protocol_versions(),
+ MonitorPid = whereis(?MODULE),
+ Msg = {negotiate_protocol, MonitorPid, Version, Protocols},
+ {Replies, _BadNodes} = multicall(Nodes, Msg),
+ check_protocol(Replies, Protocols).
+
+check_protocol([{Node, {accept, Mon, _Version, Protocol}} | Tail], Protocols) ->
+ case lists:member(Protocol, Protocols) of
+ true ->
+ case Protocol == protocol_version() of
+ true ->
+ set({protocol, Node}, {Protocol, false});
+ false ->
+ set({protocol, Node}, {Protocol, true})
+ end,
+ [node(Mon) | check_protocol(Tail, Protocols)];
+ false ->
+ unlink(Mon), % Get rid of unneccessary link
+ check_protocol(Tail, Protocols)
+ end;
+check_protocol([{Node, {reject, _Mon, Version, Protocol}} | Tail], Protocols) ->
+ verbose("Failed to connect with ~p. ~p protocols rejected. "
+ "expected version = ~p, expected protocol = ~p~n",
+ [Node, Protocols, Version, Protocol]),
+ check_protocol(Tail, Protocols);
+check_protocol([{error, _Reason} | Tail], Protocols) ->
+ check_protocol(Tail, Protocols);
+check_protocol([{badrpc, _Reason} | Tail], Protocols) ->
+ check_protocol(Tail, Protocols);
+check_protocol([], [Protocol | _Protocols]) ->
+ set(protocol_version, Protocol),
+ [];
+check_protocol([], []) ->
+ set(protocol_version, protocol_version()),
+ [].
+
+protocol_version() ->
+ case ?catch_val(protocol_version) of
+ {'EXIT', _} -> ?current_protocol_version;
+ Version -> Version
+ end.
+
+%% A sorted list of acceptable protocols the
+%% preferred protocols are first in the list
+acceptable_protocol_versions() ->
+ [protocol_version(), ?previous_protocol_version].
+
+needs_protocol_conversion(Node) ->
+ case {?catch_val({protocol, Node}), protocol_version()} of
+ {{'EXIT', _}, _} ->
+ false;
+ {{_, Bool}, ?current_protocol_version} ->
+ Bool;
+ {{_, Bool}, _} ->
+ not Bool
+ end.
+
+cast(Msg) ->
+ case whereis(?MODULE) of
+ undefined -> ignore;
+ Pid -> gen_server:cast(Pid, Msg)
+ end.
+
+unsafe_call(Msg) ->
+ case whereis(?MODULE) of
+ undefined -> {error, {node_not_running, node()}};
+ Pid -> gen_server:call(Pid, Msg, infinity)
+ end.
+
+call(Msg) ->
+ case whereis(?MODULE) of
+ undefined ->
+ {error, {node_not_running, node()}};
+ Pid ->
+ link(Pid),
+ Res = gen_server:call(Pid, Msg, infinity),
+ unlink(Pid),
+
+ %% We get an exit signal if server dies
+ receive
+ {'EXIT', Pid, _Reason} ->
+ {error, {node_not_running, node()}}
+ after 0 ->
+ ignore
+ end,
+ Res
+ end.
+
+multicall(Nodes, Msg) ->
+ rpc:multicall(Nodes, ?MODULE, call, [Msg]).
+
+start_proc(Who, Mod, Fun, Args) ->
+ Args2 = [Who, Mod, Fun, Args],
+ proc_lib:start_link(mnesia_sp, init_proc, Args2, infinity).
+
+terminate_proc(Who, R, State) when R /= shutdown, R /= killed ->
+ fatal("~p crashed: ~p state: ~p~n", [Who, R, State]);
+
+terminate_proc(Who, Reason, _State) ->
+ mnesia_lib:verbose("~p terminated: ~p~n", [Who, Reason]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Callback functions from gen_server
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+init([Parent]) ->
+ process_flag(trap_exit, true),
+ ?ets_new_table(mnesia_gvar, [set, public, named_table]),
+ set(subscribers, []),
+ mnesia_lib:verbose("~p starting: ~p~n", [?MODULE, self()]),
+ Version = mnesia:system_info(version),
+ set(version, Version),
+ dbg_out("Version: ~p~n", [Version]),
+
+ case catch process_config_args(env()) of
+ ok ->
+ mnesia_lib:set({'$$$_report', current_pos}, 0),
+ Level = mnesia_lib:val(debug),
+ mnesia_lib:verbose("Mnesia debug level set to ~p\n", [Level]),
+ set(mnesia_status, starting), %% set start status
+ set({current, db_nodes}, [node()]),
+ set(use_dir, use_dir()),
+ mnesia_lib:create_counter(trans_aborts),
+ mnesia_lib:create_counter(trans_commits),
+ mnesia_lib:create_counter(trans_log_writes),
+ Left = get_env(dump_log_write_threshold),
+ mnesia_lib:set_counter(trans_log_writes_left, Left),
+ mnesia_lib:create_counter(trans_log_writes_prev),
+ mnesia_lib:create_counter(trans_restarts),
+ mnesia_lib:create_counter(trans_failures),
+ ?ets_new_table(mnesia_held_locks, [bag, public, named_table]),
+ ?ets_new_table(mnesia_tid_locks, [bag, public, named_table]),
+ ?ets_new_table(mnesia_sticky_locks, [set, public, named_table]),
+ ?ets_new_table(mnesia_lock_queue,
+ [bag, public, named_table, {keypos, 2}]),
+ ?ets_new_table(mnesia_lock_counter, [set, public, named_table]),
+ set(checkpoints, []),
+ set(pending_checkpoints, []),
+ set(pending_checkpoint_pids, []),
+
+ {ok, #state{supervisor = Parent}};
+ {'EXIT', Reason} ->
+ mnesia_lib:report_fatal("Bad configuration: ~p~n", [Reason]),
+ {stop, {bad_config, Reason}}
+ end.
+
+use_dir() ->
+ case ?catch_val(use_dir) of
+ {'EXIT', _} ->
+ case get_env(schema_location) of
+ disc -> true;
+ opt_disc -> non_empty_dir();
+ ram -> false
+ end;
+ Bool ->
+ Bool
+ end.
+
+%% Returns true if the Mnesia directory contains
+%% important files
+non_empty_dir() ->
+ mnesia_lib:exists(mnesia_bup:fallback_bup()) or
+ mnesia_lib:exists(mnesia_lib:tab2dmp(schema)) or
+ mnesia_lib:exists(mnesia_lib:tab2dat(schema)).
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_call({mktab, Tab, Args}, _From, State) ->
+ case catch ?ets_new_table(Tab, Args) of
+ {'EXIT', ExitReason} ->
+ Msg = "Cannot create ets table",
+ Reason = {system_limit, Msg, Tab, Args, ExitReason},
+ fatal("~p~n", [Reason]),
+ {noreply, State};
+ Reply ->
+ {reply, Reply, State}
+ end;
+
+handle_call({unsafe_mktab, Tab, Args}, _From, State) ->
+ case catch ?ets_new_table(Tab, Args) of
+ {'EXIT', ExitReason} ->
+ {reply, {error, ExitReason}, State};
+ Reply ->
+ {reply, Reply, State}
+ end;
+
+
+handle_call({open_dets, Tab, Args}, _From, State) ->
+ case mnesia_lib:dets_sync_open(Tab, Args) of
+ {ok, Tab} ->
+ {reply, {ok, Tab}, State};
+
+ {error, Reason} ->
+ Msg = "Cannot open dets table",
+ Error = {error, {Msg, Tab, Args, Reason}},
+ fatal("~p~n", [Error]),
+ {noreply, State}
+ end;
+
+handle_call({unsafe_open_dets, Tab, Args}, _From, State) ->
+ case mnesia_lib:dets_sync_open(Tab, Args) of
+ {ok, Tab} ->
+ {reply, {ok, Tab}, State};
+ {error, Reason} ->
+ {reply, {error,Reason}, State}
+ end;
+
+handle_call({close_dets, Tab}, _From, State) ->
+ case mnesia_lib:dets_sync_close(Tab) of
+ ok ->
+ {reply, ok, State};
+ {error, Reason} ->
+ Msg = "Cannot close dets table",
+ Error = {error, {Msg, Tab, Reason}},
+ fatal("~p~n", [Error]),
+ {noreply, State}
+ end;
+
+handle_call({unsafe_close_dets, Tab}, _From, State) ->
+ mnesia_lib:dets_sync_close(Tab),
+ {reply, ok, State};
+
+handle_call({open_log, Args}, _From, State) ->
+ Res = disk_log:open([{notify, true}|Args]),
+ {reply, Res, State};
+
+handle_call({reopen_log, Name, Fname, Head}, _From, State) ->
+ case disk_log:reopen(Name, Fname, Head) of
+ ok ->
+ {reply, ok, State};
+
+ {error, Reason} ->
+ Msg = "Cannot rename disk_log file",
+ Error = {error, {Msg, Name, Fname, Head, Reason}},
+ fatal("~p~n", [Error]),
+ {noreply, State}
+ end;
+
+handle_call({close_log, Name}, _From, State) ->
+ case disk_log:close(Name) of
+ ok ->
+ {reply, ok, State};
+
+ {error, Reason} ->
+ Msg = "Cannot close disk_log file",
+ Error = {error, {Msg, Name, Reason}},
+ fatal("~p~n", [Error]),
+ {noreply, State}
+ end;
+
+handle_call({unsafe_close_log, Name}, _From, State) ->
+ disk_log:close(Name),
+ {reply, ok, State};
+
+handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State)
+ when State#state.tm_started == false ->
+ State2 = State#state{early_connects = [node(Mon) | State#state.early_connects]},
+ {reply, {node(), {reject, self(), uninitialized, uninitialized}}, State2};
+
+handle_call({negotiate_protocol, Mon, Version, Protocols}, From, State)
+ when node(Mon) /= node() ->
+ Protocol = protocol_version(),
+ MyVersion = mnesia:system_info(version),
+ case lists:member(Protocol, Protocols) of
+ true ->
+ accept_protocol(Mon, MyVersion, Protocol, From, State);
+ false ->
+ %% in this release we should be able to handle the previous
+ %% protocol
+ case hd(Protocols) of
+ ?previous_protocol_version ->
+ accept_protocol(Mon, MyVersion, ?previous_protocol_version, From, State);
+ _ ->
+ verbose("Connection with ~p rejected. "
+ "version = ~p, protocols = ~p, "
+ "expected version = ~p, expected protocol = ~p~n",
+ [node(Mon), Version, Protocols, MyVersion, Protocol]),
+ {reply, {node(), {reject, self(), MyVersion, Protocol}}, State}
+ end
+ end;
+
+handle_call(init, _From, State) ->
+ net_kernel:monitor_nodes(true),
+ EarlyNodes = State#state.early_connects,
+ State2 = State#state{tm_started = true},
+ {reply, EarlyNodes, State2};
+
+handle_call(Msg, _From, State) ->
+ error("~p got unexpected call: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+accept_protocol(Mon, Version, Protocol, From, State) ->
+ Reply = {node(), {accept, self(), Version, Protocol}},
+ Node = node(Mon),
+ Pending0 = State#state.pending_negotiators,
+ Pending = lists:keydelete(Node, 1, Pending0),
+ case lists:member(Node, State#state.going_down) of
+ true ->
+ %% Wait for the mnesia_down to be processed,
+ %% before we reply
+ P = Pending ++ [{Node, Mon, From, Reply}],
+ {noreply, State#state{pending_negotiators = P}};
+ false ->
+ %% No need for wait
+ link(Mon), %% link to remote Monitor
+ case Protocol == protocol_version() of
+ true ->
+ set({protocol, Node}, {Protocol, false});
+ false ->
+ set({protocol, Node}, {Protocol, true})
+ end,
+ {reply, Reply, State#state{pending_negotiators = Pending}}
+ end.
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_cast({mnesia_down, mnesia_controller, Node}, State) ->
+ mnesia_tm:mnesia_down(Node),
+ {noreply, State};
+
+handle_cast({mnesia_down, mnesia_tm, {Node, Pending}}, State) ->
+ mnesia_locker:mnesia_down(Node, Pending),
+ {noreply, State};
+
+handle_cast({mnesia_down, mnesia_locker, Node}, State) ->
+ Down = {mnesia_down, Node},
+ mnesia_lib:report_system_event(Down),
+ GoingDown = lists:delete(Node, State#state.going_down),
+ State2 = State#state{going_down = GoingDown},
+ Pending = State#state.pending_negotiators,
+ case lists:keysearch(Node, 1, Pending) of
+ {value, {Node, Mon, ReplyTo, Reply}} ->
+ %% Late reply to remote monitor
+ link(Mon), %% link to remote Monitor
+ gen_server:reply(ReplyTo, Reply),
+ P2 = lists:keydelete(Node, 1,Pending),
+ State3 = State2#state{pending_negotiators = P2},
+ {noreply, State3};
+ false ->
+ %% No pending remote monitors
+ {noreply, State2}
+ end;
+
+handle_cast({disconnect, Node}, State) ->
+ case rpc:call(Node, erlang, whereis, [?MODULE]) of
+ {badrpc, _} ->
+ ignore;
+ RemoteMon when pid(RemoteMon) ->
+ unlink(RemoteMon)
+ end,
+ {noreply, State};
+
+handle_cast({inconsistent_database, Context, Node}, State) ->
+ Msg = {inconsistent_database, Context, Node},
+ mnesia_lib:report_system_event(Msg),
+ {noreply, State};
+
+handle_cast(Msg, State) ->
+ error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor ->
+ dbg_out("~p was ~p by supervisor~n",[?MODULE, R]),
+ {stop, R, State};
+
+handle_info({'EXIT', Pid, fatal}, State) when node(Pid) == node() ->
+ dbg_out("~p got FATAL ERROR from: ~p~n",[?MODULE, Pid]),
+ exit(State#state.supervisor, shutdown),
+ {noreply, State};
+
+handle_info({'EXIT', Pid, Reason}, State) ->
+ Node = node(Pid),
+ if
+ Node /= node() ->
+ %% Remotly linked process died, assume that it was a mnesia_monitor
+ mnesia_recover:mnesia_down(Node),
+ mnesia_controller:mnesia_down(Node),
+ {noreply, State#state{going_down = [Node | State#state.going_down]}};
+ true ->
+ %% We have probably got an exit signal from from
+ %% disk_log or dets
+ Hint = "Hint: check that the disk still is writable",
+ Msg = {'EXIT', Pid, Reason},
+ fatal("~p got unexpected info: ~p; ~p~n",
+ [?MODULE, Msg, Hint])
+ end;
+
+handle_info({nodeup, Node}, State) ->
+ %% Ok, we are connected to yet another Erlang node
+ %% Let's check if Mnesia is running there in order
+ %% to detect if the network has been partitioned
+ %% due to communication failure.
+
+ HasDown = mnesia_recover:has_mnesia_down(Node),
+ ImRunning = mnesia_lib:is_running(),
+
+ if
+ %% If I'm not running the test will be made later.
+ HasDown == true, ImRunning == yes ->
+ spawn_link(?MODULE, detect_partitioned_network, [self(), Node]);
+ true ->
+ ignore
+ end,
+ {noreply, State};
+
+handle_info({nodedown, _Node}, State) ->
+ %% Ignore, we are only caring about nodeup's
+ {noreply, State};
+
+handle_info({disk_log, _Node, Log, Info}, State) ->
+ case Info of
+ {truncated, _No} ->
+ ok;
+ _ ->
+ mnesia_lib:important("Warning Log file ~p error reason ~s~n",
+ [Log, disk_log:format_error(Info)])
+ end,
+ {noreply, State};
+
+handle_info(Msg, State) ->
+ error("~p got unexpected info (~p): ~p~n", [?MODULE, State, Msg]).
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+terminate(Reason, State) ->
+ terminate_proc(?MODULE, Reason, State).
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+process_config_args([]) ->
+ ok;
+process_config_args([C|T]) ->
+ V = get_env(C),
+ dbg_out("Env ~p: ~p~n", [C, V]),
+ mnesia_lib:set(C, V),
+ process_config_args(T).
+
+set_env(E,Val) ->
+ mnesia_lib:set(E, check_type(E,Val)),
+ ok.
+
+get_env(E) ->
+ case ?catch_val(E) of
+ {'EXIT', _} ->
+ case application:get_env(mnesia, E) of
+ {ok, Val} ->
+ check_type(E, Val);
+ undefined ->
+ check_type(E, default_env(E))
+ end;
+ Val ->
+ Val
+ end.
+
+env() ->
+ [
+ access_module,
+ auto_repair,
+ backup_module,
+ debug,
+ dir,
+ dump_log_load_regulation,
+ dump_log_time_threshold,
+ dump_log_update_in_place,
+ dump_log_write_threshold,
+ embedded_mnemosyne,
+ event_module,
+ extra_db_nodes,
+ ignore_fallback_at_startup,
+ fallback_error_function,
+ max_wait_for_decision,
+ schema_location,
+ core_dir
+ ].
+
+default_env(access_module) ->
+ mnesia;
+default_env(auto_repair) ->
+ true;
+default_env(backup_module) ->
+ mnesia_backup;
+default_env(debug) ->
+ none;
+default_env(dir) ->
+ Name = lists:concat(["Mnesia.", node()]),
+ filename:absname(Name);
+default_env(dump_log_load_regulation) ->
+ false;
+default_env(dump_log_time_threshold) ->
+ timer:minutes(3);
+default_env(dump_log_update_in_place) ->
+ true;
+default_env(dump_log_write_threshold) ->
+ 1000;
+default_env(embedded_mnemosyne) ->
+ false;
+default_env(event_module) ->
+ mnesia_event;
+default_env(extra_db_nodes) ->
+ [];
+default_env(ignore_fallback_at_startup) ->
+ false;
+default_env(fallback_error_function) ->
+ {mnesia, lkill};
+default_env(max_wait_for_decision) ->
+ infinity;
+default_env(schema_location) ->
+ opt_disc;
+default_env(core_dir) ->
+ false.
+
+check_type(Env, Val) ->
+ case catch do_check_type(Env, Val) of
+ {'EXIT', _Reason} ->
+ exit({bad_config, Env, Val});
+ NewVal ->
+ NewVal
+ end.
+
+do_check_type(access_module, A) when atom(A) -> A;
+do_check_type(auto_repair, B) -> bool(B);
+do_check_type(backup_module, B) when atom(B) -> B;
+do_check_type(debug, debug) -> debug;
+do_check_type(debug, false) -> none;
+do_check_type(debug, none) -> none;
+do_check_type(debug, trace) -> trace;
+do_check_type(debug, true) -> debug;
+do_check_type(debug, verbose) -> verbose;
+do_check_type(dir, V) -> filename:absname(V);
+do_check_type(dump_log_load_regulation, B) -> bool(B);
+do_check_type(dump_log_time_threshold, I) when integer(I), I > 0 -> I;
+do_check_type(dump_log_update_in_place, B) -> bool(B);
+do_check_type(dump_log_write_threshold, I) when integer(I), I > 0 -> I;
+do_check_type(event_module, A) when atom(A) -> A;
+do_check_type(ignore_fallback_at_startup, B) -> bool(B);
+do_check_type(fallback_error_function, {Mod, Func})
+ when atom(Mod), atom(Func) -> {Mod, Func};
+do_check_type(embedded_mnemosyne, B) -> bool(B);
+do_check_type(extra_db_nodes, L) when list(L) ->
+ Fun = fun(N) when N == node() -> false;
+ (A) when atom(A) -> true
+ end,
+ lists:filter(Fun, L);
+do_check_type(max_wait_for_decision, infinity) -> infinity;
+do_check_type(max_wait_for_decision, I) when integer(I), I > 0 -> I;
+do_check_type(schema_location, M) -> media(M);
+do_check_type(core_dir, "false") -> false;
+do_check_type(core_dir, false) -> false;
+do_check_type(core_dir, Dir) when list(Dir) -> Dir.
+
+
+bool(true) -> true;
+bool(false) -> false.
+
+media(disc) -> disc;
+media(opt_disc) -> opt_disc;
+media(ram) -> ram.
+
+patch_env(Env, Val) ->
+ case catch do_check_type(Env, Val) of
+ {'EXIT', _Reason} ->
+ {error, {bad_type, Env, Val}};
+ NewVal ->
+ application_controller:set_env(mnesia, Env, NewVal),
+ NewVal
+ end.
+
+detect_partitioned_network(Mon, Node) ->
+ GoodNodes = negotiate_protocol([Node]),
+ detect_inconcistency(GoodNodes, running_partitioned_network),
+ unlink(Mon),
+ exit(normal).
+
+detect_inconcistency([], _Context) ->
+ ok;
+detect_inconcistency(Nodes, Context) ->
+ Downs = [N || N <- Nodes, mnesia_recover:has_mnesia_down(N)],
+ {Replies, _BadNodes} =
+ rpc:multicall(Downs, ?MODULE, has_remote_mnesia_down, [node()]),
+ report_inconsistency(Replies, Context, ok).
+
+has_remote_mnesia_down(Node) ->
+ HasDown = mnesia_recover:has_mnesia_down(Node),
+ Master = mnesia_recover:get_master_nodes(schema),
+ if
+ HasDown == true, Master == [] ->
+ {true, node()};
+ true ->
+ {false, node()}
+ end.
+
+report_inconsistency([{true, Node} | Replies], Context, _Status) ->
+ %% Oops, Mnesia is already running on the
+ %% other node AND we both regard each
+ %% other as down. The database is
+ %% potentially inconsistent and we has to
+ %% do tell the applications about it, so
+ %% they may perform some clever recovery
+ %% action.
+ Msg = {inconsistent_database, Context, Node},
+ mnesia_lib:report_system_event(Msg),
+ report_inconsistency(Replies, Context, inconsistent_database);
+report_inconsistency([{false, _Node} | Replies], Context, Status) ->
+ report_inconsistency(Replies, Context, Status);
+report_inconsistency([{badrpc, _Reason} | Replies], Context, Status) ->
+ report_inconsistency(Replies, Context, Status);
+report_inconsistency([], _Context, Status) ->
+ Status.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl
new file mode 100644
index 0000000000..b3e8f1c386
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_recover.erl
@@ -0,0 +1,1175 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_recover.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
+%%
+-module(mnesia_recover).
+
+-behaviour(gen_server).
+
+-export([
+ allow_garb/0,
+ call/1,
+ connect_nodes/1,
+ disconnect/1,
+ dump_decision_tab/0,
+ get_master_node_info/0,
+ get_master_node_tables/0,
+ get_master_nodes/1,
+ get_mnesia_downs/0,
+ has_mnesia_down/1,
+ incr_trans_tid_serial/0,
+ init/0,
+ log_decision/1,
+ log_master_nodes/3,
+ log_mnesia_down/1,
+ log_mnesia_up/1,
+ mnesia_down/1,
+ note_decision/2,
+ note_log_decision/2,
+ outcome/2,
+ start/0,
+ start_garb/0,
+ still_pending/1,
+ sync_trans_tid_serial/1,
+ wait_for_decision/2,
+ what_happened/3
+ ]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3
+ ]).
+
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [set/2, verbose/2, error/2, fatal/2]).
+
+-record(state, {supervisor,
+ unclear_pid,
+ unclear_decision,
+ unclear_waitfor,
+ tm_queue_len = 0,
+ initiated = false,
+ early_msgs = []
+ }).
+
+%%-define(DBG(F, A), mnesia:report_event(list_to_atom(lists:flatten(io_lib:format(F, A))))).
+%%-define(DBG(F, A), io:format("DBG: " ++ F, A)).
+
+-record(transient_decision, {tid, outcome}).
+
+start() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [self()],
+ [{timeout, infinity}
+ %%, {debug, [trace]}
+ ]).
+
+init() ->
+ call(init).
+
+start_garb() ->
+ Pid = whereis(mnesia_recover),
+ {ok, _} = timer:send_interval(timer:minutes(2), Pid, garb_decisions),
+ {ok, _} = timer:send_interval(timer:seconds(10), Pid, check_overload).
+
+allow_garb() ->
+ cast(allow_garb).
+
+
+%% The transaction log has either been swiched (latest -> previous) or
+%% there is nothing to be dumped. This means that the previous
+%% transaction log only may contain commit records which refers to
+%% transactions noted in the last two of the 'Prev' tables. All other
+%% tables may now be garbed by 'garb_decisions' (after 2 minutes).
+%% Max 10 tables are kept.
+do_allow_garb() ->
+ %% The order of the following stuff is important!
+ Curr = val(latest_transient_decision),
+ Old = val(previous_transient_decisions),
+ Next = create_transient_decision(),
+ {Prev, ReallyOld} = sublist([Curr | Old], 10, []),
+ [?ets_delete_table(Tab) || Tab <- ReallyOld],
+ set(previous_transient_decisions, Prev),
+ set(latest_transient_decision, Next).
+
+sublist([H|R], N, Acc) when N > 0 ->
+ sublist(R, N-1, [H| Acc]);
+sublist(List, _N, Acc) ->
+ {lists:reverse(Acc), List}.
+
+do_garb_decisions() ->
+ case val(previous_transient_decisions) of
+ [First, Second | Rest] ->
+ set(previous_transient_decisions, [First, Second]),
+ [?ets_delete_table(Tab) || Tab <- Rest];
+ _ ->
+ ignore
+ end.
+
+connect_nodes([]) ->
+ [];
+connect_nodes(Ns) ->
+ %% Determine which nodes we should try to connect
+ AlreadyConnected = val(recover_nodes),
+ {_, Nodes} = mnesia_lib:search_delete(node(), Ns),
+ Check = Nodes -- AlreadyConnected,
+ GoodNodes = mnesia_monitor:negotiate_protocol(Check),
+ if
+ GoodNodes == [] ->
+ %% No good noodes to connect to
+ ignore;
+ true ->
+ %% Now we have agreed upon a protocol with some new nodes
+ %% and we may use them when we recover transactions
+ mnesia_lib:add_list(recover_nodes, GoodNodes),
+ cast({announce_all, GoodNodes}),
+ case get_master_nodes(schema) of
+ [] ->
+ Context = starting_partitioned_network,
+ mnesia_monitor:detect_inconcistency(GoodNodes, Context);
+ _ -> %% If master_nodes is set ignore old inconsistencies
+ ignore
+ end
+ end,
+ {GoodNodes, AlreadyConnected}.
+
+disconnect(Node) ->
+ mnesia_monitor:disconnect(Node),
+ mnesia_lib:del(recover_nodes, Node).
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
+
+call(Msg) ->
+ Pid = whereis(?MODULE),
+ case Pid of
+ undefined ->
+ {error, {node_not_running, node()}};
+ Pid ->
+ link(Pid),
+ Res = gen_server:call(Pid, Msg, infinity),
+ unlink(Pid),
+
+ %% We get an exit signal if server dies
+ receive
+ {'EXIT', Pid, _Reason} ->
+ {error, {node_not_running, node()}}
+ after 0 ->
+ ignore
+ end,
+ Res
+ end.
+
+multicall(Nodes, Msg) ->
+ rpc:multicall(Nodes, ?MODULE, call, [Msg]).
+
+cast(Msg) ->
+ case whereis(?MODULE) of
+ undefined -> ignore;
+ Pid -> gen_server:cast(Pid, Msg)
+ end.
+
+abcast(Nodes, Msg) ->
+ gen_server:abcast(Nodes, ?MODULE, Msg).
+
+note_decision(Tid, Outcome) ->
+ Tab = val(latest_transient_decision),
+ ?ets_insert(Tab, #transient_decision{tid = Tid, outcome = Outcome}).
+
+note_up(Node, _Date, _Time) ->
+ ?ets_delete(mnesia_decision, Node).
+
+note_down(Node, Date, Time) ->
+ ?ets_insert(mnesia_decision, {mnesia_down, Node, Date, Time}).
+
+note_master_nodes(Tab, []) ->
+ ?ets_delete(mnesia_decision, Tab);
+note_master_nodes(Tab, Nodes) when list(Nodes) ->
+ Master = {master_nodes, Tab, Nodes},
+ ?ets_insert(mnesia_decision, Master).
+
+note_outcome(D) when D#decision.disc_nodes == [] ->
+%% ?DBG("~w: note_tmp_decision: ~w~n", [node(), D]),
+ note_decision(D#decision.tid, filter_outcome(D#decision.outcome)),
+ ?ets_delete(mnesia_decision, D#decision.tid);
+note_outcome(D) when D#decision.disc_nodes /= [] ->
+%% ?DBG("~w: note_decision: ~w~n", [node(), D]),
+ ?ets_insert(mnesia_decision, D).
+
+log_decision(D) when D#decision.outcome /= unclear ->
+ OldD = decision(D#decision.tid),
+ MergedD = merge_decisions(node(), OldD, D),
+ do_log_decision(MergedD, true);
+log_decision(D) ->
+ do_log_decision(D, false).
+
+do_log_decision(D, DoTell) ->
+ RamNs = D#decision.ram_nodes,
+ DiscNs = D#decision.disc_nodes -- [node()],
+ Outcome = D#decision.outcome,
+ D2 =
+ case Outcome of
+ aborted -> D#decision{disc_nodes = DiscNs};
+ committed -> D#decision{disc_nodes = DiscNs};
+ _ -> D
+ end,
+ note_outcome(D2),
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_log:append(latest_log, D2),
+ if
+ DoTell == true, Outcome /= unclear ->
+ tell_im_certain(DiscNs, D2),
+ tell_im_certain(RamNs, D2);
+ true ->
+ ignore
+ end;
+ false ->
+ ignore
+ end.
+
+tell_im_certain([], _D) ->
+ ignore;
+tell_im_certain(Nodes, D) ->
+ Msg = {im_certain, node(), D},
+%% ?DBG("~w: ~w: tell: ~w~n", [node(), Msg, Nodes]),
+ abcast(Nodes, Msg).
+
+log_mnesia_up(Node) ->
+ call({log_mnesia_up, Node}).
+
+log_mnesia_down(Node) ->
+ call({log_mnesia_down, Node}).
+
+get_mnesia_downs() ->
+ Tab = mnesia_decision,
+ Pat = {mnesia_down, '_', '_', '_'},
+ Downs = ?ets_match_object(Tab, Pat),
+ [Node || {mnesia_down, Node, _Date, _Time} <- Downs].
+
+%% Check if we have got a mnesia_down from Node
+has_mnesia_down(Node) ->
+ case ?ets_lookup(mnesia_decision, Node) of
+ [{mnesia_down, Node, _Date, _Time}] ->
+ true;
+ [] ->
+ false
+ end.
+
+mnesia_down(Node) ->
+ case ?catch_val(recover_nodes) of
+ {'EXIT', _} ->
+ %% Not started yet
+ ignore;
+ _ ->
+ mnesia_lib:del(recover_nodes, Node),
+ cast({mnesia_down, Node})
+ end.
+
+log_master_nodes(Args, UseDir, IsRunning) ->
+ if
+ IsRunning == yes ->
+ log_master_nodes2(Args, UseDir, IsRunning, ok);
+ UseDir == false ->
+ ok;
+ true ->
+ Name = latest_log,
+ Fname = mnesia_log:latest_log_file(),
+ Exists = mnesia_lib:exists(Fname),
+ Repair = mnesia:system_info(auto_repair),
+ OpenArgs = [{file, Fname}, {name, Name}, {repair, Repair}],
+ case disk_log:open(OpenArgs) of
+ {ok, Name} ->
+ log_master_nodes2(Args, UseDir, IsRunning, ok);
+ {repaired, Name, {recovered, _R}, {badbytes, _B}}
+ when Exists == true ->
+ log_master_nodes2(Args, UseDir, IsRunning, ok);
+ {repaired, Name, {recovered, _R}, {badbytes, _B}}
+ when Exists == false ->
+ mnesia_log:write_trans_log_header(),
+ log_master_nodes2(Args, UseDir, IsRunning, ok);
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end.
+
+log_master_nodes2([{Tab, Nodes} | Tail], UseDir, IsRunning, WorstRes) ->
+ Res =
+ case IsRunning of
+ yes ->
+ R = call({log_master_nodes, Tab, Nodes, UseDir, IsRunning}),
+ mnesia_controller:master_nodes_updated(Tab, Nodes),
+ R;
+ _ ->
+ do_log_master_nodes(Tab, Nodes, UseDir, IsRunning)
+ end,
+ case Res of
+ ok ->
+ log_master_nodes2(Tail, UseDir, IsRunning, WorstRes);
+ {error, Reason} ->
+ log_master_nodes2(Tail, UseDir, IsRunning, {error, Reason})
+ end;
+log_master_nodes2([], _UseDir, IsRunning, WorstRes) ->
+ case IsRunning of
+ yes ->
+ WorstRes;
+ _ ->
+ disk_log:close(latest_log),
+ WorstRes
+ end.
+
+get_master_node_info() ->
+ Tab = mnesia_decision,
+ Pat = {master_nodes, '_', '_'},
+ case catch mnesia_lib:db_match_object(ram_copies,Tab, Pat) of
+ {'EXIT', _} ->
+ [];
+ Masters ->
+ Masters
+ end.
+
+get_master_node_tables() ->
+ Masters = get_master_node_info(),
+ [Tab || {master_nodes, Tab, _Nodes} <- Masters].
+
+get_master_nodes(Tab) ->
+ case catch ?ets_lookup_element(mnesia_decision, Tab, 3) of
+ {'EXIT', _} -> [];
+ Nodes -> Nodes
+ end.
+
+%% Determine what has happened to the transaction
+what_happened(Tid, Protocol, Nodes) ->
+ Default =
+ case Protocol of
+ asym_trans -> aborted;
+ _ -> unclear %% sym_trans and sync_sym_trans
+ end,
+ This = node(),
+ case lists:member(This, Nodes) of
+ true ->
+ {ok, Outcome} = call({what_happened, Default, Tid}),
+ Others = Nodes -- [This],
+ case filter_outcome(Outcome) of
+ unclear -> what_happened_remotely(Tid, Default, Others);
+ aborted -> aborted;
+ committed -> committed
+ end;
+ false ->
+ what_happened_remotely(Tid, Default, Nodes)
+ end.
+
+what_happened_remotely(Tid, Default, Nodes) ->
+ {Replies, _} = multicall(Nodes, {what_happened, Default, Tid}),
+ check_what_happened(Replies, 0, 0).
+
+check_what_happened([H | T], Aborts, Commits) ->
+ case H of
+ {ok, R} ->
+ case filter_outcome(R) of
+ committed ->
+ check_what_happened(T, Aborts, Commits + 1);
+ aborted ->
+ check_what_happened(T, Aborts + 1, Commits);
+ unclear ->
+ check_what_happened(T, Aborts, Commits)
+ end;
+ {error, _} ->
+ check_what_happened(T, Aborts, Commits);
+ {badrpc, _} ->
+ check_what_happened(T, Aborts, Commits)
+ end;
+check_what_happened([], Aborts, Commits) ->
+ if
+ Aborts == 0, Commits == 0 -> aborted; % None of the active nodes knows
+ Aborts > 0 -> aborted; % Someody has aborted
+ Aborts == 0, Commits > 0 -> committed % All has committed
+ end.
+
+%% Determine what has happened to the transaction
+%% and possibly wait forever for the decision.
+wait_for_decision(presume_commit, _InitBy) ->
+ %% sym_trans
+ {{presume_commit, self()}, committed};
+
+wait_for_decision(D, InitBy) when D#decision.outcome == presume_abort ->
+ %% asym_trans
+ Tid = D#decision.tid,
+ Outcome = filter_outcome(outcome(Tid, D#decision.outcome)),
+ if
+ Outcome /= unclear ->
+ {Tid, Outcome};
+
+ InitBy /= startup ->
+ %% Wait a while for active transactions
+ %% to end and try again
+ timer:sleep(200),
+ wait_for_decision(D, InitBy);
+
+ InitBy == startup ->
+ {ok, Res} = call({wait_for_decision, D}),
+ {Tid, Res}
+ end.
+
+still_pending([Tid | Pending]) ->
+ case filter_outcome(outcome(Tid, unclear)) of
+ unclear -> [Tid | still_pending(Pending)];
+ _ -> still_pending(Pending)
+ end;
+still_pending([]) ->
+ [].
+
+load_decision_tab() ->
+ Cont = mnesia_log:open_decision_tab(),
+ load_decision_tab(Cont, load_decision_tab),
+ mnesia_log:close_decision_tab().
+
+load_decision_tab(eof, _InitBy) ->
+ ok;
+load_decision_tab(Cont, InitBy) ->
+ case mnesia_log:chunk_decision_tab(Cont) of
+ {Cont2, Decisions} ->
+ note_log_decisions(Decisions, InitBy),
+ load_decision_tab(Cont2, InitBy);
+ eof ->
+ ok
+ end.
+
+%% Dumps DECISION.LOG and PDECISION.LOG and removes them.
+%% From now on all decisions are logged in the transaction log file
+convert_old() ->
+ HasOldStuff =
+ mnesia_lib:exists(mnesia_log:previous_decision_log_file()) or
+ mnesia_lib:exists(mnesia_log:decision_log_file()),
+ case HasOldStuff of
+ true ->
+ mnesia_log:open_decision_log(),
+ dump_decision_log(startup),
+ dump_decision_log(startup),
+ mnesia_log:close_decision_log(),
+ Latest = mnesia_log:decision_log_file(),
+ ok = file:delete(Latest);
+ false ->
+ ignore
+ end.
+
+dump_decision_log(InitBy) ->
+ %% Assumed to be run in transaction log dumper process
+ Cont = mnesia_log:prepare_decision_log_dump(),
+ perform_dump_decision_log(Cont, InitBy).
+
+perform_dump_decision_log(eof, _InitBy) ->
+ confirm_decision_log_dump();
+perform_dump_decision_log(Cont, InitBy) when InitBy == startup ->
+ case mnesia_log:chunk_decision_log(Cont) of
+ {Cont2, Decisions} ->
+ note_log_decisions(Decisions, InitBy),
+ perform_dump_decision_log(Cont2, InitBy);
+ eof ->
+ confirm_decision_log_dump()
+ end;
+perform_dump_decision_log(_Cont, _InitBy) ->
+ confirm_decision_log_dump().
+
+confirm_decision_log_dump() ->
+ dump_decision_tab(),
+ mnesia_log:confirm_decision_log_dump().
+
+dump_decision_tab() ->
+ Tab = mnesia_decision,
+ All = mnesia_lib:db_match_object(ram_copies,Tab, '_'),
+ mnesia_log:save_decision_tab({decision_list, All}).
+
+note_log_decisions([What | Tail], InitBy) ->
+ note_log_decision(What, InitBy),
+ note_log_decisions(Tail, InitBy);
+note_log_decisions([], _InitBy) ->
+ ok.
+
+note_log_decision(NewD, InitBy) when NewD#decision.outcome == pre_commit ->
+ note_log_decision(NewD#decision{outcome = unclear}, InitBy);
+
+note_log_decision(NewD, _InitBy) when record(NewD, decision) ->
+ Tid = NewD#decision.tid,
+ sync_trans_tid_serial(Tid),
+ OldD = decision(Tid),
+ MergedD = merge_decisions(node(), OldD, NewD),
+ note_outcome(MergedD);
+
+note_log_decision({trans_tid, serial, _Serial}, startup) ->
+ ignore;
+
+note_log_decision({trans_tid, serial, Serial}, _InitBy) ->
+ sync_trans_tid_serial(Serial);
+
+note_log_decision({mnesia_up, Node, Date, Time}, _InitBy) ->
+ note_up(Node, Date, Time);
+
+note_log_decision({mnesia_down, Node, Date, Time}, _InitBy) ->
+ note_down(Node, Date, Time);
+
+note_log_decision({master_nodes, Tab, Nodes}, _InitBy) ->
+ note_master_nodes(Tab, Nodes);
+
+note_log_decision(H, _InitBy) when H#log_header.log_kind == decision_log ->
+ V = mnesia_log:decision_log_version(),
+ if
+ H#log_header.log_version == V->
+ ok;
+ H#log_header.log_version == "2.0" ->
+ verbose("Accepting an old version format of decision log: ~p~n",
+ [V]),
+ ok;
+ true ->
+ fatal("Bad version of decision log: ~p~n", [H])
+ end;
+
+note_log_decision(H, _InitBy) when H#log_header.log_kind == decision_tab ->
+ V = mnesia_log:decision_tab_version(),
+ if
+ V == H#log_header.log_version ->
+ ok;
+ true ->
+ fatal("Bad version of decision tab: ~p~n", [H])
+ end;
+note_log_decision({decision_list, ItemList}, InitBy) ->
+ note_log_decisions(ItemList, InitBy);
+note_log_decision(BadItem, InitBy) ->
+ exit({"Bad decision log item", BadItem, InitBy}).
+
+trans_tid_serial() ->
+ ?ets_lookup_element(mnesia_decision, serial, 3).
+
+set_trans_tid_serial(Val) ->
+ ?ets_insert(mnesia_decision, {trans_tid, serial, Val}).
+
+incr_trans_tid_serial() ->
+ ?ets_update_counter(mnesia_decision, serial, 1).
+
+sync_trans_tid_serial(ThatCounter) when integer(ThatCounter) ->
+ ThisCounter = trans_tid_serial(),
+ if
+ ThatCounter > ThisCounter ->
+ set_trans_tid_serial(ThatCounter + 1);
+ true ->
+ ignore
+ end;
+sync_trans_tid_serial(Tid) ->
+ sync_trans_tid_serial(Tid#tid.counter).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Callback functions from gen_server
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+init([Parent]) ->
+ process_flag(trap_exit, true),
+ mnesia_lib:verbose("~p starting: ~p~n", [?MODULE, self()]),
+ set(latest_transient_decision, create_transient_decision()),
+ set(previous_transient_decisions, []),
+ set(recover_nodes, []),
+ State = #state{supervisor = Parent},
+ {ok, State}.
+
+create_transient_decision() ->
+ ?ets_new_table(mnesia_transient_decision, [{keypos, 2}, set, public]).
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_call(init, From, State) when State#state.initiated == false ->
+ Args = [{keypos, 2}, set, public, named_table],
+ case mnesia_monitor:use_dir() of
+ true ->
+ ?ets_new_table(mnesia_decision, Args),
+ set_trans_tid_serial(0),
+ TabFile = mnesia_log:decision_tab_file(),
+ case mnesia_lib:exists(TabFile) of
+ true ->
+ load_decision_tab();
+ false ->
+ ignore
+ end,
+ convert_old(),
+ mnesia_dumper:opt_dump_log(scan_decisions);
+ false ->
+ ?ets_new_table(mnesia_decision, Args),
+ set_trans_tid_serial(0)
+ end,
+ handle_early_msgs(State, From);
+
+handle_call(Msg, From, State) when State#state.initiated == false ->
+ %% Buffer early messages
+ Msgs = State#state.early_msgs,
+ {noreply, State#state{early_msgs = [{call, Msg, From} | Msgs]}};
+
+handle_call({what_happened, Default, Tid}, _From, State) ->
+ sync_trans_tid_serial(Tid),
+ Outcome = outcome(Tid, Default),
+ {reply, {ok, Outcome}, State};
+
+handle_call({wait_for_decision, D}, From, State) ->
+ Recov = val(recover_nodes),
+ AliveRam = (mnesia_lib:intersect(D#decision.ram_nodes, Recov) -- [node()]),
+ RemoteDisc = D#decision.disc_nodes -- [node()],
+ if
+ AliveRam == [], RemoteDisc == [] ->
+ %% No more else to wait for and we may safely abort
+ {reply, {ok, aborted}, State};
+ true ->
+ verbose("Transaction ~p is unclear. "
+ "Wait for disc nodes: ~w ram: ~w~n",
+ [D#decision.tid, RemoteDisc, AliveRam]),
+ AliveDisc = mnesia_lib:intersect(RemoteDisc, Recov),
+ Msg = {what_decision, node(), D},
+ abcast(AliveRam, Msg),
+ abcast(AliveDisc, Msg),
+ case val(max_wait_for_decision) of
+ infinity ->
+ ignore;
+ MaxWait ->
+ ForceMsg = {force_decision, D#decision.tid},
+ {ok, _} = timer:send_after(MaxWait, ForceMsg)
+ end,
+ State2 = State#state{unclear_pid = From,
+ unclear_decision = D,
+ unclear_waitfor = (RemoteDisc ++ AliveRam)},
+ {noreply, State2}
+ end;
+
+handle_call({log_mnesia_up, Node}, _From, State) ->
+ do_log_mnesia_up(Node),
+ {reply, ok, State};
+
+handle_call({log_mnesia_down, Node}, _From, State) ->
+ do_log_mnesia_down(Node),
+ {reply, ok, State};
+
+handle_call({log_master_nodes, Tab, Nodes, UseDir, IsRunning}, _From, State) ->
+ do_log_master_nodes(Tab, Nodes, UseDir, IsRunning),
+ {reply, ok, State};
+
+handle_call(Msg, _From, State) ->
+ error("~p got unexpected call: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+do_log_mnesia_up(Node) ->
+ Yoyo = {mnesia_up, Node, Date = date(), Time = time()},
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_log:append(latest_log, Yoyo),
+ disk_log:sync(latest_log);
+ false ->
+ ignore
+ end,
+ note_up(Node, Date, Time).
+
+do_log_mnesia_down(Node) ->
+ Yoyo = {mnesia_down, Node, Date = date(), Time = time()},
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_log:append(latest_log, Yoyo),
+ disk_log:sync(latest_log);
+ false ->
+ ignore
+ end,
+ note_down(Node, Date, Time).
+
+do_log_master_nodes(Tab, Nodes, UseDir, IsRunning) ->
+ Master = {master_nodes, Tab, Nodes},
+ Res =
+ case UseDir of
+ true ->
+ LogRes = mnesia_log:append(latest_log, Master),
+ disk_log:sync(latest_log),
+ LogRes;
+ false ->
+ ok
+ end,
+ case IsRunning of
+ yes ->
+ note_master_nodes(Tab, Nodes);
+ _NotRunning ->
+ ignore
+ end,
+ Res.
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_cast(Msg, State) when State#state.initiated == false ->
+ %% Buffer early messages
+ Msgs = State#state.early_msgs,
+ {noreply, State#state{early_msgs = [{cast, Msg} | Msgs]}};
+
+handle_cast({im_certain, Node, NewD}, State) ->
+ OldD = decision(NewD#decision.tid),
+ MergedD = merge_decisions(Node, OldD, NewD),
+ do_log_decision(MergedD, false),
+ {noreply, State};
+
+handle_cast(allow_garb, State) ->
+ do_allow_garb(),
+ {noreply, State};
+
+handle_cast({decisions, Node, Decisions}, State) ->
+ mnesia_lib:add(recover_nodes, Node),
+ State2 = add_remote_decisions(Node, Decisions, State),
+ {noreply, State2};
+
+handle_cast({what_decision, Node, OtherD}, State) ->
+ Tid = OtherD#decision.tid,
+ sync_trans_tid_serial(Tid),
+ Decision =
+ case decision(Tid) of
+ no_decision -> OtherD;
+ MyD when record(MyD, decision) -> MyD
+ end,
+ announce([Node], [Decision], [], true),
+ {noreply, State};
+
+handle_cast({mnesia_down, Node}, State) ->
+ case State#state.unclear_decision of
+ undefined ->
+ {noreply, State};
+ D ->
+ case lists:member(Node, D#decision.ram_nodes) of
+ false ->
+ {noreply, State};
+ true ->
+ State2 = add_remote_decision(Node, D, State),
+ {noreply, State2}
+ end
+ end;
+
+handle_cast({announce_all, Nodes}, State) ->
+ announce_all(Nodes, tabs()),
+ {noreply, State};
+
+handle_cast(Msg, State) ->
+ error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+%% No need for buffering
+%% handle_info(Msg, State) when State#state.initiated == false ->
+%% %% Buffer early messages
+%% Msgs = State#state.early_msgs,
+%% {noreply, State#state{early_msgs = [{info, Msg} | Msgs]}};
+
+handle_info(check_overload, S) ->
+ %% Time to check if mnesia_tm is overloaded
+ case whereis(mnesia_tm) of
+ Pid when pid(Pid) ->
+
+ Threshold = 100,
+ Prev = S#state.tm_queue_len,
+ {message_queue_len, Len} =
+ process_info(Pid, message_queue_len),
+ if
+ Len > Threshold, Prev > Threshold ->
+ What = {mnesia_tm, message_queue_len, [Prev, Len]},
+ mnesia_lib:report_system_event({mnesia_overload, What}),
+ {noreply, S#state{tm_queue_len = 0}};
+
+ Len > Threshold ->
+ {noreply, S#state{tm_queue_len = Len}};
+
+ true ->
+ {noreply, S#state{tm_queue_len = 0}}
+ end;
+ undefined ->
+ {noreply, S}
+ end;
+
+handle_info(garb_decisions, State) ->
+ do_garb_decisions(),
+ {noreply, State};
+
+handle_info({force_decision, Tid}, State) ->
+ %% Enforce a transaction recovery decision,
+ %% if we still are waiting for the outcome
+
+ case State#state.unclear_decision of
+ U when U#decision.tid == Tid ->
+ verbose("Decided to abort transaction ~p since "
+ "max_wait_for_decision has been exceeded~n",
+ [Tid]),
+ D = U#decision{outcome = aborted},
+ State2 = add_remote_decision(node(), D, State),
+ {noreply, State2};
+ _ ->
+ {noreply, State}
+ end;
+
+handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor ->
+ mnesia_lib:dbg_out("~p was ~p~n",[?MODULE, R]),
+ {stop, shutdown, State};
+
+handle_info(Msg, State) ->
+ error("~p got unexpected info: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+
+terminate(Reason, State) ->
+ mnesia_monitor:terminate_proc(?MODULE, Reason, State).
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+handle_early_msgs(State, From) ->
+ Res = do_handle_early_msgs(State#state.early_msgs,
+ State#state{early_msgs = [],
+ initiated = true}),
+ gen_server:reply(From, ok),
+ Res.
+
+do_handle_early_msgs([Msg | Msgs], State) ->
+ %% The messages are in reverted order
+ case do_handle_early_msgs(Msgs, State) of
+ {stop, Reason, Reply, State2} ->
+ {stop, Reason, Reply, State2};
+ {stop, Reason, State2} ->
+ {stop, Reason, State2};
+ {noreply, State2} ->
+ handle_early_msg(Msg, State2)
+ end;
+
+do_handle_early_msgs([], State) ->
+ {noreply, State}.
+
+handle_early_msg({call, Msg, From}, State) ->
+ case handle_call(Msg, From, State) of
+ {reply, R, S} ->
+ gen_server:reply(From, R),
+ {noreply, S};
+ Other ->
+ Other
+ end;
+handle_early_msg({cast, Msg}, State) ->
+ handle_cast(Msg, State);
+handle_early_msg({info, Msg}, State) ->
+ handle_info(Msg, State).
+
+tabs() ->
+ Curr = val(latest_transient_decision), % Do not miss any trans even
+ Prev = val(previous_transient_decisions), % if the tabs are switched
+ [Curr, mnesia_decision | Prev]. % Ordered by hit probability
+
+decision(Tid) ->
+ decision(Tid, tabs()).
+
+decision(Tid, [Tab | Tabs]) ->
+ case catch ?ets_lookup(Tab, Tid) of
+ [D] when record(D, decision) ->
+ D;
+ [C] when record(C, transient_decision) ->
+ #decision{tid = C#transient_decision.tid,
+ outcome = C#transient_decision.outcome,
+ disc_nodes = [],
+ ram_nodes = []
+ };
+ [] ->
+ decision(Tid, Tabs);
+ {'EXIT', _} ->
+ %% Recently switched transient decision table
+ decision(Tid, Tabs)
+ end;
+decision(_Tid, []) ->
+ no_decision.
+
+outcome(Tid, Default) ->
+ outcome(Tid, Default, tabs()).
+
+outcome(Tid, Default, [Tab | Tabs]) ->
+ case catch ?ets_lookup_element(Tab, Tid, 3) of
+ {'EXIT', _} ->
+ outcome(Tid, Default, Tabs);
+ Val ->
+ Val
+ end;
+outcome(_Tid, Default, []) ->
+ Default.
+
+filter_outcome(Val) ->
+ case Val of
+ unclear -> unclear;
+ aborted -> aborted;
+ presume_abort -> aborted;
+ committed -> committed;
+ pre_commit -> unclear
+ end.
+
+filter_aborted(D) when D#decision.outcome == presume_abort ->
+ D#decision{outcome = aborted};
+filter_aborted(D) ->
+ D.
+
+%% Merge old decision D with new (probably remote) decision
+merge_decisions(Node, D, NewD0) ->
+ NewD = filter_aborted(NewD0),
+ if
+ D == no_decision, node() /= Node ->
+ %% We did not know anything about this txn
+ NewD#decision{disc_nodes = []};
+ D == no_decision ->
+ NewD;
+ record(D, decision) ->
+ DiscNs = D#decision.disc_nodes -- ([node(), Node]),
+ OldD = filter_aborted(D#decision{disc_nodes = DiscNs}),
+%% mnesia_lib:dbg_out("merge ~w: NewD = ~w~n D = ~w~n OldD = ~w~n",
+%% [Node, NewD, D, OldD]),
+ if
+ OldD#decision.outcome == unclear,
+ NewD#decision.outcome == unclear ->
+ D;
+
+ OldD#decision.outcome == NewD#decision.outcome ->
+ %% We have come to the same decision
+ OldD;
+
+ OldD#decision.outcome == committed,
+ NewD#decision.outcome == aborted ->
+ %% Interesting! We have already committed,
+ %% but someone else has aborted. Now we
+ %% have a nice little inconcistency. The
+ %% other guy (or some one else) has
+ %% enforced a recovery decision when
+ %% max_wait_for_decision was exceeded.
+ %% We will pretend that we have obeyed
+ %% the forced recovery decision, but we
+ %% will also generate an event in case the
+ %% application wants to do something clever.
+ Msg = {inconsistent_database, bad_decision, Node},
+ mnesia_lib:report_system_event(Msg),
+ OldD#decision{outcome = aborted};
+
+ OldD#decision.outcome == aborted ->
+ %% aborted overrrides anything
+ OldD#decision{outcome = aborted};
+
+ NewD#decision.outcome == aborted ->
+ %% aborted overrrides anything
+ OldD#decision{outcome = aborted};
+
+ OldD#decision.outcome == committed,
+ NewD#decision.outcome == unclear ->
+ %% committed overrides unclear
+ OldD#decision{outcome = committed};
+
+ OldD#decision.outcome == unclear,
+ NewD#decision.outcome == committed ->
+ %% committed overrides unclear
+ OldD#decision{outcome = committed}
+ end
+ end.
+
+add_remote_decisions(Node, [D | Tail], State) when record(D, decision) ->
+ State2 = add_remote_decision(Node, D, State),
+ add_remote_decisions(Node, Tail, State2);
+
+add_remote_decisions(Node, [C | Tail], State)
+ when record(C, transient_decision) ->
+ D = #decision{tid = C#transient_decision.tid,
+ outcome = C#transient_decision.outcome,
+ disc_nodes = [],
+ ram_nodes = []},
+ State2 = add_remote_decision(Node, D, State),
+ add_remote_decisions(Node, Tail, State2);
+
+add_remote_decisions(Node, [{mnesia_down, _, _, _} | Tail], State) ->
+ add_remote_decisions(Node, Tail, State);
+
+add_remote_decisions(Node, [{trans_tid, serial, Serial} | Tail], State) ->
+ sync_trans_tid_serial(Serial),
+ case State#state.unclear_decision of
+ undefined ->
+ ignored;
+ D ->
+ case lists:member(Node, D#decision.ram_nodes) of
+ true ->
+ ignore;
+ false ->
+ abcast([Node], {what_decision, node(), D})
+ end
+ end,
+ add_remote_decisions(Node, Tail, State);
+
+add_remote_decisions(_Node, [], State) ->
+ State.
+
+add_remote_decision(Node, NewD, State) ->
+ Tid = NewD#decision.tid,
+ OldD = decision(Tid),
+ D = merge_decisions(Node, OldD, NewD),
+ do_log_decision(D, false),
+ Outcome = D#decision.outcome,
+ if
+ OldD == no_decision ->
+ ignore;
+ Outcome == unclear ->
+ ignore;
+ true ->
+ case lists:member(node(), NewD#decision.disc_nodes) or
+ lists:member(node(), NewD#decision.ram_nodes) of
+ true ->
+ tell_im_certain([Node], D);
+ false ->
+ ignore
+ end
+ end,
+ case State#state.unclear_decision of
+ U when U#decision.tid == Tid ->
+ WaitFor = State#state.unclear_waitfor -- [Node],
+ if
+ Outcome == unclear, WaitFor == [] ->
+ %% Everybody are uncertain, lets abort
+ NewOutcome = aborted,
+ CertainD = D#decision{outcome = NewOutcome,
+ disc_nodes = [],
+ ram_nodes = []},
+ tell_im_certain(D#decision.disc_nodes, CertainD),
+ tell_im_certain(D#decision.ram_nodes, CertainD),
+ do_log_decision(CertainD, false),
+ verbose("Decided to abort transaction ~p "
+ "since everybody are uncertain ~p~n",
+ [Tid, CertainD]),
+ gen_server:reply(State#state.unclear_pid, {ok, NewOutcome}),
+ State#state{unclear_pid = undefined,
+ unclear_decision = undefined,
+ unclear_waitfor = undefined};
+ Outcome /= unclear ->
+ verbose("~p told us that transaction ~p was ~p~n",
+ [Node, Tid, Outcome]),
+ gen_server:reply(State#state.unclear_pid, {ok, Outcome}),
+ State#state{unclear_pid = undefined,
+ unclear_decision = undefined,
+ unclear_waitfor = undefined};
+ Outcome == unclear ->
+ State#state{unclear_waitfor = WaitFor}
+ end;
+ _ ->
+ State
+ end.
+
+announce_all([], _Tabs) ->
+ ok;
+announce_all(ToNodes, [Tab | Tabs]) ->
+ case catch mnesia_lib:db_match_object(ram_copies, Tab, '_') of
+ {'EXIT', _} ->
+ %% Oops, we are in the middle of a 'garb_decisions'
+ announce_all(ToNodes, Tabs);
+ List ->
+ announce(ToNodes, List, [], false),
+ announce_all(ToNodes, Tabs)
+ end;
+announce_all(_ToNodes, []) ->
+ ok.
+
+announce(ToNodes, [Head | Tail], Acc, ForceSend) ->
+ Acc2 = arrange(ToNodes, Head, Acc, ForceSend),
+ announce(ToNodes, Tail, Acc2, ForceSend);
+
+announce(_ToNodes, [], Acc, _ForceSend) ->
+ send_decisions(Acc).
+
+send_decisions([{Node, Decisions} | Tail]) ->
+ abcast([Node], {decisions, node(), Decisions}),
+ send_decisions(Tail);
+send_decisions([]) ->
+ ok.
+
+arrange([To | ToNodes], D, Acc, ForceSend) when record(D, decision) ->
+ NeedsAdd = (ForceSend or
+ lists:member(To, D#decision.disc_nodes) or
+ lists:member(To, D#decision.ram_nodes)),
+ case NeedsAdd of
+ true ->
+ Acc2 = add_decision(To, D, Acc),
+ arrange(ToNodes, D, Acc2, ForceSend);
+ false ->
+ arrange(ToNodes, D, Acc, ForceSend)
+ end;
+
+arrange([To | ToNodes], C, Acc, ForceSend) when record(C, transient_decision) ->
+ Acc2 = add_decision(To, C, Acc),
+ arrange(ToNodes, C, Acc2, ForceSend);
+
+arrange([_To | _ToNodes], {mnesia_down, _Node, _Date, _Time}, Acc, _ForceSend) ->
+ %% The others have their own info about this
+ Acc;
+
+arrange([_To | _ToNodes], {master_nodes, _Tab, _Nodes}, Acc, _ForceSend) ->
+ %% The others have their own info about this
+ Acc;
+
+arrange([To | ToNodes], {trans_tid, serial, Serial}, Acc, ForceSend) ->
+ %% Do the lamport thing plus release the others
+ %% from uncertainity.
+ Acc2 = add_decision(To, {trans_tid, serial, Serial}, Acc),
+ arrange(ToNodes, {trans_tid, serial, Serial}, Acc2, ForceSend);
+
+arrange([], _Decision, Acc, _ForceSend) ->
+ Acc.
+
+add_decision(Node, Decision, [{Node, Decisions} | Tail]) ->
+ [{Node, [Decision | Decisions]} | Tail];
+add_decision(Node, Decision, [Head | Tail]) ->
+ [Head | add_decision(Node, Decision, Tail)];
+add_decision(Node, Decision, []) ->
+ [{Node, [Decision]}].
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_registry.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_registry.erl
new file mode 100644
index 0000000000..c16603f344
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_registry.erl
@@ -0,0 +1,277 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_registry.erl,v 1.2 2010/03/04 13:54:19 maria Exp $
+%%
+-module(mnesia_registry).
+
+%%%----------------------------------------------------------------------
+%%% File : mnesia_registry.erl
+%%% Purpose : Support dump and restore of a registry on a C-node
+%%% This is an OTP internal module and is not public available.
+%%%
+%%% Example : Dump some hardcoded records into the Mnesia table Tab
+%%%
+%%% case rpc:call(Node, mnesia_registry, start_dump, [Tab, self()]) of
+%%% Pid when pid(Pid) ->
+%%% Pid ! {write, key1, key_size1, val_type1, val_size1, val1},
+%%% Pid ! {delete, key3},
+%%% Pid ! {write, key2, key_size2, val_type2, val_size2, val2},
+%%% Pid ! {write, key4, key_size4, val_type4, val_size4, val4},
+%%% Pid ! {commit, self()},
+%%% receive
+%%% {ok, Pid} ->
+%%% ok;
+%%% {'EXIT', Pid, Reason} ->
+%%% exit(Reason)
+%%% end;
+%%% {badrpc, Reason} ->
+%%% exit(Reason)
+%%% end.
+%%%
+%%% Example : Restore the corresponding Mnesia table Tab
+%%%
+%%% case rpc:call(Node, mnesia_registry, start_restore, [Tab, self()]) of
+%%% {size, Pid, N, LargestKey, LargestVal} ->
+%%% Pid ! {send_records, self()},
+%%% Fun = fun() ->
+%%% receive
+%%% {restore, KeySize, ValSize, ValType, Key, Val} ->
+%%% {Key, Val};
+%%% {'EXIT', Pid, Reason} ->
+%%% exit(Reason)
+%%% end
+%%% end,
+%%% lists:map(Fun, lists:seq(1, N));
+%%% {badrpc, Reason} ->
+%%% exit(Reason)
+%%% end.
+%%%
+%%%----------------------------------------------------------------------
+
+%% External exports
+-export([start_dump/2, start_restore/2]).
+-export([create_table/1, create_table/2]).
+
+%% Internal exports
+-export([init/4]).
+
+-record(state, {table, ops = [], link_to}).
+
+-record(registry_entry, {key, key_size, val_type, val_size, val}).
+
+-record(size, {pid = self(), n_values = 0, largest_key = 0, largest_val = 0}).
+
+%%%----------------------------------------------------------------------
+%%% Client
+%%%----------------------------------------------------------------------
+
+start(Type, Tab, LinkTo) ->
+ Starter = self(),
+ Args = [Type, Starter, LinkTo, Tab],
+ Pid = spawn_link(?MODULE, init, Args),
+ %% The receiver process may unlink the current process
+ receive
+ {ok, Res} ->
+ Res;
+ {'EXIT', Pid, Reason} when LinkTo == Starter ->
+ exit(Reason)
+ end.
+
+%% Starts a receiver process and optionally creates a Mnesia table
+%% with suitable default values. Returns the Pid of the receiver process
+%%
+%% The receiver process accumulates Mnesia operations and performs
+%% all operations or none at commit. The understood messages are:
+%%
+%% {write, Key, KeySize, ValType, ValSize, Val} ->
+%% accumulates mnesia:write({Tab, Key, KeySize, ValType, ValSize, Val})
+%% (no reply)
+%% {delete, Key} ->
+%% accumulates mnesia:delete({Tab, Key}) (no reply)
+%% {commit, ReplyTo} ->
+%% commits all accumulated operations
+%% and stops the process (replies {ok, Pid})
+%% abort ->
+%% stops the process (no reply)
+%%
+%% The receiver process is linked to the process with the process identifier
+%% LinkTo. If some error occurs the receiver process will invoke exit(Reason)
+%% and it is up to he LinkTo process to act properly when it receives an exit
+%% signal.
+
+start_dump(Tab, LinkTo) ->
+ start(dump, Tab, LinkTo).
+
+%% Starts a sender process which sends restore messages back to the
+%% LinkTo process. But first are some statistics about the table
+%% determined and returned as a 5-tuple:
+%%
+%% {size, SenderPid, N, LargestKeySize, LargestValSize}
+%%
+%% where N is the number of records in the table. Then the sender process
+%% waits for a 2-tuple message:
+%%
+%% {send_records, ReplyTo}
+%%
+%% At last N 6-tuple messages is sent to the ReplyTo process:
+%%
+%% ReplyTo ! {restore, KeySize, ValSize, ValType, Key, Val}
+%%
+%% If some error occurs the receiver process will invoke exit(Reason)
+%% and it is up to he LinkTo process to act properly when it receives an
+%% exit signal.
+
+start_restore(Tab, LinkTo) ->
+ start(restore, Tab, LinkTo).
+
+
+%% Optionally creates the Mnesia table Tab with suitable default values.
+%% Returns ok or EXIT's
+create_table(Tab) ->
+ Storage = mnesia:table_info(schema, storage_type),
+ create_table(Tab, [{Storage, [node()]}]).
+
+create_table(Tab, TabDef) ->
+ Attrs = record_info(fields, registry_entry),
+ case mnesia:create_table(Tab, [{attributes, Attrs} | TabDef]) of
+ {'atomic', ok} ->
+ ok;
+ {aborted, {already_exists, Tab}} ->
+ ok;
+ {aborted, Reason} ->
+ exit(Reason)
+ end.
+
+%%%----------------------------------------------------------------------
+%%% Server
+%%%----------------------------------------------------------------------
+
+init(Type, Starter, LinkTo, Tab) ->
+ if
+ LinkTo /= Starter ->
+ link(LinkTo),
+ unlink(Starter);
+ true ->
+ ignore
+ end,
+ case Type of
+ dump ->
+ Starter ! {ok, self()},
+ dump_loop(#state{table = Tab, link_to = LinkTo});
+ restore ->
+ restore_table(Tab, Starter, LinkTo)
+ end.
+
+%%%----------------------------------------------------------------------
+%%% Dump loop
+%%%----------------------------------------------------------------------
+
+dump_loop(S) ->
+ Tab = S#state.table,
+ Ops = S#state.ops,
+ receive
+ {write, Key, KeySize, ValType, ValSize, Val} ->
+ RE = #registry_entry{key = Key,
+ key_size = KeySize,
+ val_type = ValType,
+ val_size = ValSize,
+ val = Val},
+ dump_loop(S#state{ops = [{write, RE} | Ops]});
+ {delete, Key} ->
+ dump_loop(S#state{ops = [{delete, Key} | Ops]});
+ {commit, ReplyTo} ->
+ create_table(Tab),
+ RecName = mnesia:table_info(Tab, record_name),
+ %% The Ops are in reverse order, but there is no need
+ %% for reversing the list of accumulated operations
+ case mnesia:transaction(fun handle_ops/3, [Tab, RecName, Ops]) of
+ {'atomic', ok} ->
+ ReplyTo ! {ok, self()},
+ stop(S#state.link_to);
+ {aborted, Reason} ->
+ exit({aborted, Reason})
+ end;
+ abort ->
+ stop(S#state.link_to);
+ BadMsg ->
+ exit({bad_message, BadMsg})
+ end.
+
+stop(LinkTo) ->
+ unlink(LinkTo),
+ exit(normal).
+
+%% Grab a write lock for the entire table
+%% and iterate over all accumulated operations
+handle_ops(Tab, RecName, Ops) ->
+ mnesia:write_lock_table(Tab),
+ do_handle_ops(Tab, RecName, Ops).
+
+do_handle_ops(Tab, RecName, [{write, RegEntry} | Ops]) ->
+ Record = setelement(1, RegEntry, RecName),
+ mnesia:write(Tab, Record, write),
+ do_handle_ops(Tab, RecName, Ops);
+do_handle_ops(Tab, RecName, [{delete, Key} | Ops]) ->
+ mnesia:delete(Tab, Key, write),
+ do_handle_ops(Tab, RecName, Ops);
+do_handle_ops(_Tab, _RecName, []) ->
+ ok.
+
+%%%----------------------------------------------------------------------
+%%% Restore table
+%%%----------------------------------------------------------------------
+
+restore_table(Tab, Starter, LinkTo) ->
+ Pat = mnesia:table_info(Tab, wild_pattern),
+ Fun = fun() -> mnesia:match_object(Tab, Pat, read) end,
+ case mnesia:transaction(Fun) of
+ {'atomic', AllRecords} ->
+ Size = calc_size(AllRecords, #size{}),
+ Starter ! {ok, Size},
+ receive
+ {send_records, ReplyTo} ->
+ send_records(AllRecords, ReplyTo),
+ unlink(LinkTo),
+ exit(normal);
+ BadMsg ->
+ exit({bad_message, BadMsg})
+ end;
+ {aborted, Reason} ->
+ exit(Reason)
+ end.
+
+calc_size([H | T], S) ->
+ KeySize = max(element(#registry_entry.key_size, H), S#size.largest_key),
+ ValSize = max(element(#registry_entry.val_size, H), S#size.largest_val),
+ N = S#size.n_values + 1,
+ calc_size(T, S#size{n_values = N, largest_key = KeySize, largest_val = ValSize});
+calc_size([], Size) ->
+ Size.
+
+max(New, Old) when New > Old -> New;
+max(_New, Old) -> Old.
+
+send_records([H | T], ReplyTo) ->
+ KeySize = element(#registry_entry.key_size, H),
+ ValSize = element(#registry_entry.val_size, H),
+ ValType = element(#registry_entry.val_type, H),
+ Key = element(#registry_entry.key, H),
+ Val = element(#registry_entry.val, H),
+ ReplyTo ! {restore, KeySize, ValSize, ValType, Key, Val},
+ send_records(T, ReplyTo);
+send_records([], _ReplyTo) ->
+ ok.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_schema.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_schema.erl
new file mode 100644
index 0000000000..cceb6bf0d1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_schema.erl
@@ -0,0 +1,2899 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_schema.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
+%%
+%% In this module we provide a number of explicit functions
+%% to maninpulate the schema. All these functions are called
+%% within a special schema transaction.
+%%
+%% We also have an init/1 function defined here, this func is
+%% used by mnesia:start() to initialize the entire schema.
+
+-module(mnesia_schema).
+
+-export([
+ add_snmp/2,
+ add_table_copy/3,
+ add_table_index/2,
+ arrange_restore/3,
+ attr_tab_to_pos/2,
+ attr_to_pos/2,
+ change_table_copy_type/3,
+ change_table_access_mode/2,
+ change_table_load_order/2,
+ change_table_frag/2,
+ clear_table/1,
+ create_table/1,
+ cs2list/1,
+ del_snmp/1,
+ del_table_copy/2,
+ del_table_index/2,
+ delete_cstruct/2,
+ delete_schema/1,
+ delete_schema2/0,
+ delete_table/1,
+ delete_table_property/2,
+ dump_tables/1,
+ ensure_no_schema/1,
+ get_create_list/1,
+ get_initial_schema/2,
+ get_table_properties/1,
+ info/0,
+ info/1,
+ init/1,
+ insert_cstruct/3,
+ is_remote_member/1,
+ list2cs/1,
+ lock_schema/0,
+ lock_del_table/4, % Spawned
+ merge_schema/0,
+ move_table/3,
+ opt_create_dir/2,
+ prepare_commit/3,
+ purge_dir/2,
+ purge_tmp_files/0,
+ ram_delete_table/2,
+% ram_delete_table/3,
+ read_cstructs_from_disc/0,
+ read_nodes/0,
+ remote_read_schema/0,
+ restore/1,
+ restore/2,
+ restore/3,
+ schema_coordinator/3,
+ set_where_to_read/3,
+ transform_table/4,
+ undo_prepare_commit/2,
+ unlock_schema/0,
+ version/0,
+ write_table_property/2
+ ]).
+
+%% Exports for mnesia_frag
+-export([
+ get_tid_ts_and_lock/2,
+ make_create_table/1,
+ ensure_active/1,
+ pick/4,
+ verify/3,
+ incr_version/1,
+ check_keys/3,
+ check_duplicates/2,
+ make_delete_table/2
+ ]).
+
+%% Needed outside to be able to use/set table_properties
+%% from user (not supported)
+-export([schema_transaction/1,
+ insert_schema_ops/2,
+ do_create_table/1,
+ do_delete_table/1,
+ do_delete_table_property/2,
+ do_write_table_property/2]).
+
+-include("mnesia.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-import(mnesia_lib, [set/2, del/2, verbose/2, dbg_out/2]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Here comes the init function which also resides in
+%% this module, it is called upon by the trans server
+%% at startup of the system
+%%
+%% We have a meta table which looks like
+%% {table, schema,
+%% {type, set},
+%% {disc_copies, all},
+%% {arity, 2}
+%% {attributes, [key, val]}
+%%
+%% This means that we have a series of {schema, Name, Cs} tuples
+%% in a table called schema !!
+
+init(IgnoreFallback) ->
+ Res = read_schema(true, false, IgnoreFallback),
+ {ok, Source, _CreateList} = exit_on_error(Res),
+ verbose("Schema initiated from: ~p~n", [Source]),
+ set({schema, tables}, []),
+ set({schema, local_tables}, []),
+ Tabs = set_schema(?ets_first(schema)),
+ lists:foreach(fun(Tab) -> clear_whereabouts(Tab) end, Tabs),
+ set({schema, where_to_read}, node()),
+ set({schema, load_node}, node()),
+ set({schema, load_reason}, initial),
+ mnesia_controller:add_active_replica(schema, node()).
+
+exit_on_error({error, Reason}) ->
+ exit(Reason);
+exit_on_error(GoodRes) ->
+ GoodRes.
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
+ Value -> Value
+ end.
+
+%% This function traverses all cstructs in the schema and
+%% sets all values in mnesia_gvar accordingly for each table/cstruct
+
+set_schema('$end_of_table') ->
+ [];
+set_schema(Tab) ->
+ do_set_schema(Tab),
+ [Tab | set_schema(?ets_next(schema, Tab))].
+
+get_create_list(Tab) ->
+ ?ets_lookup_element(schema, Tab, 3).
+
+do_set_schema(Tab) ->
+ List = get_create_list(Tab),
+ Cs = list2cs(List),
+ do_set_schema(Tab, Cs).
+
+do_set_schema(Tab, Cs) ->
+ Type = Cs#cstruct.type,
+ set({Tab, setorbag}, Type),
+ set({Tab, local_content}, Cs#cstruct.local_content),
+ set({Tab, ram_copies}, Cs#cstruct.ram_copies),
+ set({Tab, disc_copies}, Cs#cstruct.disc_copies),
+ set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies),
+ set({Tab, load_order}, Cs#cstruct.load_order),
+ set({Tab, access_mode}, Cs#cstruct.access_mode),
+ set({Tab, snmp}, Cs#cstruct.snmp),
+ set({Tab, user_properties}, Cs#cstruct.user_properties),
+ [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties],
+ set({Tab, frag_properties}, Cs#cstruct.frag_properties),
+ mnesia_frag:set_frag_hash(Tab, Cs#cstruct.frag_properties),
+ set({Tab, attributes}, Cs#cstruct.attributes),
+ Arity = length(Cs#cstruct.attributes) + 1,
+ set({Tab, arity}, Arity),
+ RecName = Cs#cstruct.record_name,
+ set({Tab, record_name}, RecName),
+ set({Tab, record_validation}, {RecName, Arity, Type}),
+ set({Tab, wild_pattern}, wild(RecName, Arity)),
+ set({Tab, index}, Cs#cstruct.index),
+ %% create actual index tabs later
+ set({Tab, cookie}, Cs#cstruct.cookie),
+ set({Tab, version}, Cs#cstruct.version),
+ set({Tab, cstruct}, Cs),
+ Storage = mnesia_lib:schema_cs_to_storage_type(node(), Cs),
+ set({Tab, storage_type}, Storage),
+ mnesia_lib:add({schema, tables}, Tab),
+ Ns = mnesia_lib:cs_to_nodes(Cs),
+ case lists:member(node(), Ns) of
+ true ->
+ mnesia_lib:add({schema, local_tables}, Tab);
+ false when Tab == schema ->
+ mnesia_lib:add({schema, local_tables}, Tab);
+ false ->
+ ignore
+ end.
+
+wild(RecName, Arity) ->
+ Wp0 = list_to_tuple(lists:duplicate(Arity, '_')),
+ setelement(1, Wp0, RecName).
+
+%% Temporarily read the local schema and return a list
+%% of all nodes mentioned in the schema.DAT file
+read_nodes() ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ case read_schema(false, false) of
+ {ok, _Source, CreateList} ->
+ Cs = list2cs(CreateList),
+ {ok, Cs#cstruct.disc_copies ++ Cs#cstruct.ram_copies};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%% Returns Version from the tuple {Version,MasterNodes}
+version() ->
+ case read_schema(false, false) of
+ {ok, Source, CreateList} when Source /= default ->
+ Cs = list2cs(CreateList),
+ {Version, _Details} = Cs#cstruct.version,
+ Version;
+ _ ->
+ case dir_exists(mnesia_lib:dir()) of
+ true -> {1,0};
+ false -> {0,0}
+ end
+ end.
+
+%% Calculate next table version from old cstruct
+incr_version(Cs) ->
+ {{Major, Minor}, _} = Cs#cstruct.version,
+ Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
+ mnesia_lib:cs_to_nodes(Cs)),
+ V =
+ case Nodes -- val({Cs#cstruct.name, active_replicas}) of
+ [] -> {Major + 1, 0}; % All replicas are active
+ _ -> {Major, Minor + 1} % Some replicas are inactive
+ end,
+ Cs#cstruct{version = {V, {node(), now()}}}.
+
+%% Returns table name
+insert_cstruct(Tid, Cs, KeepWhereabouts) ->
+ Tab = Cs#cstruct.name,
+ TabDef = cs2list(Cs),
+ Val = {schema, Tab, TabDef},
+ mnesia_checkpoint:tm_retain(Tid, schema, Tab, write),
+ mnesia_subscr:report_table_event(schema, Tid, Val, write),
+ Active = val({Tab, active_replicas}),
+
+ case KeepWhereabouts of
+ true ->
+ ignore;
+ false when Active == [] ->
+ clear_whereabouts(Tab);
+ false ->
+ %% Someone else has initiated table
+ ignore
+ end,
+ set({Tab, cstruct}, Cs),
+ ?ets_insert(schema, Val),
+ do_set_schema(Tab, Cs),
+ Val.
+
+clear_whereabouts(Tab) ->
+ set({Tab, checkpoints}, []),
+ set({Tab, subscribers}, []),
+ set({Tab, where_to_read}, nowhere),
+ set({Tab, active_replicas}, []),
+ set({Tab, commit_work}, []),
+ set({Tab, where_to_write}, []),
+ set({Tab, where_to_commit}, []),
+ set({Tab, load_by_force}, false),
+ set({Tab, load_node}, unknown),
+ set({Tab, load_reason}, unknown).
+
+%% Returns table name
+delete_cstruct(Tid, Cs) ->
+ Tab = Cs#cstruct.name,
+ TabDef = cs2list(Cs),
+ Val = {schema, Tab, TabDef},
+ mnesia_checkpoint:tm_retain(Tid, schema, Tab, delete),
+ mnesia_subscr:report_table_event(schema, Tid, Val, delete),
+ ?ets_match_delete(mnesia_gvar, {{Tab, '_'}, '_'}),
+ ?ets_match_delete(mnesia_gvar, {{Tab, '_', '_'}, '_'}),
+ del({schema, local_tables}, Tab),
+ del({schema, tables}, Tab),
+ ?ets_delete(schema, Tab),
+ Val.
+
+%% Delete the Mnesia directory on all given nodes
+%% Requires that Mnesia is not running anywhere
+%% Returns ok | {error,Reason}
+delete_schema(Ns) when list(Ns), Ns /= [] ->
+ RunningNs = mnesia_lib:running_nodes(Ns),
+ Reason = "Cannot delete schema on all nodes",
+ if
+ RunningNs == [] ->
+ case rpc:multicall(Ns, ?MODULE, delete_schema2, []) of
+ {Replies, []} ->
+ case [R || R <- Replies, R /= ok] of
+ [] ->
+ ok;
+ BadReplies ->
+ verbose("~s: ~p~n", [Reason, BadReplies]),
+ {error, {"All nodes not running", BadReplies}}
+ end;
+ {_Replies, BadNs} ->
+ verbose("~s: ~p~n", [Reason, BadNs]),
+ {error, {"All nodes not running", BadNs}}
+ end;
+ true ->
+ verbose("~s: ~p~n", [Reason, RunningNs]),
+ {error, {"Mnesia is not stopped everywhere", RunningNs}}
+ end;
+delete_schema(Ns) ->
+ {error, {badarg, Ns}}.
+
+delete_schema2() ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ case mnesia_lib:is_running() of
+ no ->
+ Dir = mnesia_lib:dir(),
+ purge_dir(Dir, []),
+ ok;
+ _ ->
+ {error, {"Mnesia still running", node()}}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+ensure_no_schema([H|T]) when atom(H) ->
+ case rpc:call(H, ?MODULE, remote_read_schema, []) of
+ {badrpc, Reason} ->
+ {H, {"All nodes not running", H, Reason}};
+ {ok,Source, _} when Source /= default ->
+ {H, {already_exists, H}};
+ _ ->
+ ensure_no_schema(T)
+ end;
+ensure_no_schema([H|_]) ->
+ {error,{badarg, H}};
+ensure_no_schema([]) ->
+ ok.
+
+remote_read_schema() ->
+ %% Ensure that we access the intended Mnesia
+ %% directory. This function may not be called
+ %% during startup since it will cause the
+ %% application_controller to get into deadlock
+ case mnesia_lib:ensure_loaded(?APPLICATION) of
+ ok ->
+ case mnesia_monitor:get_env(schema_location) of
+ opt_disc ->
+ read_schema(false, true);
+ _ ->
+ read_schema(false, false)
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+dir_exists(Dir) ->
+ dir_exists(Dir, mnesia_monitor:use_dir()).
+dir_exists(Dir, true) ->
+ case file:read_file_info(Dir) of
+ {ok, _} -> true;
+ _ -> false
+ end;
+dir_exists(_Dir, false) ->
+ false.
+
+opt_create_dir(UseDir, Dir) when UseDir == true->
+ case dir_exists(Dir, UseDir) of
+ true ->
+ check_can_write(Dir);
+ false ->
+ case file:make_dir(Dir) of
+ ok ->
+ verbose("Create Directory ~p~n", [Dir]),
+ ok;
+ {error, Reason} ->
+ verbose("Cannot create mnesia dir ~p~n", [Reason]),
+ {error, {"Cannot create Mnesia dir", Dir, Reason}}
+ end
+ end;
+opt_create_dir(false, _) ->
+ {error, {has_no_disc, node()}}.
+
+check_can_write(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, FI} when FI#file_info.type == directory,
+ FI#file_info.access == read_write ->
+ ok;
+ {ok, _} ->
+ {error, "Not allowed to write in Mnesia dir", Dir};
+ _ ->
+ {error, "Non existent Mnesia dir", Dir}
+ end.
+
+lock_schema() ->
+ mnesia_lib:lock_table(schema).
+
+unlock_schema() ->
+ mnesia_lib:unlock_table(schema).
+
+read_schema(Keep, _UseDirAnyway) ->
+ read_schema(Keep, false, false).
+
+%% The schema may be read for several reasons.
+%% If Mnesia is not already started the read intention
+%% we normally do not want the ets table named schema
+%% be left around.
+%% If Keep == true, the ets table schema is kept
+%% If Keep == false, the ets table schema is removed
+%%
+%% Returns {ok, Source, SchemaCstruct} or {error, Reason}
+%% Source may be: default | ram | disc | fallback
+
+read_schema(Keep, UseDirAnyway, IgnoreFallback) ->
+ lock_schema(),
+ Res =
+ case mnesia:system_info(is_running) of
+ yes ->
+ {ok, ram, get_create_list(schema)};
+ _IsRunning ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ read_disc_schema(Keep, IgnoreFallback);
+ false when UseDirAnyway == true ->
+ read_disc_schema(Keep, IgnoreFallback);
+ false when Keep == true ->
+ Args = [{keypos, 2}, public, named_table, set],
+ mnesia_monitor:mktab(schema, Args),
+ CreateList = get_initial_schema(ram_copies, []),
+ ?ets_insert(schema,{schema, schema, CreateList}),
+ {ok, default, CreateList};
+ false when Keep == false ->
+ CreateList = get_initial_schema(ram_copies, []),
+ {ok, default, CreateList}
+ end
+ end,
+ unlock_schema(),
+ Res.
+
+read_disc_schema(Keep, IgnoreFallback) ->
+ Running = mnesia:system_info(is_running),
+ case mnesia_bup:fallback_exists() of
+ true when IgnoreFallback == false, Running /= yes ->
+ mnesia_bup:fallback_to_schema();
+ _ ->
+ %% If we're running, we read the schema file even
+ %% if fallback exists
+ Dat = mnesia_lib:tab2dat(schema),
+ case mnesia_lib:exists(Dat) of
+ true ->
+ do_read_disc_schema(Dat, Keep);
+ false ->
+ Dmp = mnesia_lib:tab2dmp(schema),
+ case mnesia_lib:exists(Dmp) of
+ true ->
+ %% May only happen when toggling of
+ %% schema storage type has been
+ %% interrupted
+ do_read_disc_schema(Dmp, Keep);
+ false ->
+ {error, "No schema file exists"}
+ end
+ end
+ end.
+
+do_read_disc_schema(Fname, Keep) ->
+ T =
+ case Keep of
+ false ->
+ Args = [{keypos, 2}, public, set],
+ ?ets_new_table(schema, Args);
+ true ->
+ Args = [{keypos, 2}, public, named_table, set],
+ mnesia_monitor:mktab(schema, Args)
+ end,
+ Repair = mnesia_monitor:get_env(auto_repair),
+ Res = % BUGBUG Fixa till dcl!
+ case mnesia_lib:dets_to_ets(schema, T, Fname, set, Repair, no) of
+ loaded -> {ok, disc, ?ets_lookup_element(T, schema, 3)};
+ Other -> {error, {"Cannot read schema", Fname, Other}}
+ end,
+ case Keep of
+ true -> ignore;
+ false -> ?ets_delete_table(T)
+ end,
+ Res.
+
+get_initial_schema(SchemaStorage, Nodes) ->
+ Cs = #cstruct{name = schema,
+ record_name = schema,
+ attributes = [table, cstruct]},
+ Cs2 =
+ case SchemaStorage of
+ ram_copies -> Cs#cstruct{ram_copies = Nodes};
+ disc_copies -> Cs#cstruct{disc_copies = Nodes}
+ end,
+ cs2list(Cs2).
+
+read_cstructs_from_disc() ->
+ %% Assumptions:
+ %% - local schema lock in global
+ %% - use_dir is true
+ %% - Mnesia is not running
+ %% - Ignore fallback
+
+ Fname = mnesia_lib:tab2dat(schema),
+ case mnesia_lib:exists(Fname) of
+ true ->
+ Args = [{file, Fname},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)},
+ {type, set}],
+ case dets:open_file(make_ref(), Args) of
+ {ok, Tab} ->
+ Fun = fun({_, _, List}) ->
+ {continue, list2cs(List)}
+ end,
+ Cstructs = dets:traverse(Tab, Fun),
+ dets:close(Tab),
+ {ok, Cstructs};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ false ->
+ {error, "No schema file exists"}
+ end.
+
+%% We run a very special type of transactions when we
+%% we want to manipulate the schema.
+
+get_tid_ts_and_lock(Tab, Intent) ->
+ TidTs = get(mnesia_activity_state),
+ case TidTs of
+ {_Mod, Tid, Ts} when record(Ts, tidstore)->
+ Store = Ts#tidstore.store,
+ case Intent of
+ read -> mnesia_locker:rlock_table(Tid, Store, Tab);
+ write -> mnesia_locker:wlock_table(Tid, Store, Tab);
+ none -> ignore
+ end,
+ TidTs;
+ _ ->
+ mnesia:abort(no_transaction)
+ end.
+
+schema_transaction(Fun) ->
+ case get(mnesia_activity_state) of
+ undefined ->
+ Args = [self(), Fun, whereis(mnesia_controller)],
+ Pid = spawn_link(?MODULE, schema_coordinator, Args),
+ receive
+ {transaction_done, Res, Pid} -> Res;
+ {'EXIT', Pid, R} -> {aborted, {transaction_crashed, R}}
+ end;
+ _ ->
+ {aborted, nested_transaction}
+ end.
+
+%% This process may dump the transaction log, and should
+%% therefore not be run in an application process
+%%
+schema_coordinator(Client, _Fun, undefined) ->
+ Res = {aborted, {node_not_running, node()}},
+ Client ! {transaction_done, Res, self()},
+ unlink(Client);
+
+schema_coordinator(Client, Fun, Controller) when pid(Controller) ->
+ %% Do not trap exit in order to automatically die
+ %% when the controller dies
+
+ link(Controller),
+ unlink(Client),
+
+ %% Fulfull the transaction even if the client dies
+ Res = mnesia:transaction(Fun),
+ Client ! {transaction_done, Res, self()},
+ unlink(Controller), % Avoids spurious exit message
+ unlink(whereis(mnesia_tm)), % Avoids spurious exit message
+ exit(normal).
+
+%% The make* rotines return a list of ops, this function
+%% inserts em all in the Store and maintains the local order
+%% of ops.
+
+insert_schema_ops({_Mod, _Tid, Ts}, SchemaIOps) ->
+ do_insert_schema_ops(Ts#tidstore.store, SchemaIOps).
+
+do_insert_schema_ops(Store, [Head | Tail]) ->
+ ?ets_insert(Store, Head),
+ do_insert_schema_ops(Store, Tail);
+do_insert_schema_ops(_Store, []) ->
+ ok.
+
+cs2list(Cs) when record(Cs, cstruct) ->
+ Tags = record_info(fields, cstruct),
+ rec2list(Tags, 2, Cs);
+cs2list(CreateList) when list(CreateList) ->
+ CreateList.
+
+rec2list([Tag | Tags], Pos, Rec) ->
+ Val = element(Pos, Rec),
+ [{Tag, Val} | rec2list(Tags, Pos + 1, Rec)];
+rec2list([], _Pos, _Rec) ->
+ [].
+
+list2cs(List) when list(List) ->
+ Name = pick(unknown, name, List, must),
+ Type = pick(Name, type, List, set),
+ Rc0 = pick(Name, ram_copies, List, []),
+ Dc = pick(Name, disc_copies, List, []),
+ Doc = pick(Name, disc_only_copies, List, []),
+ Rc = case {Rc0, Dc, Doc} of
+ {[], [], []} -> [node()];
+ _ -> Rc0
+ end,
+ LC = pick(Name, local_content, List, false),
+ RecName = pick(Name, record_name, List, Name),
+ Attrs = pick(Name, attributes, List, [key, val]),
+ Snmp = pick(Name, snmp, List, []),
+ LoadOrder = pick(Name, load_order, List, 0),
+ AccessMode = pick(Name, access_mode, List, read_write),
+ UserProps = pick(Name, user_properties, List, []),
+ verify({alt, [nil, list]}, mnesia_lib:etype(UserProps),
+ {bad_type, Name, {user_properties, UserProps}}),
+ Cookie = pick(Name, cookie, List, ?unique_cookie),
+ Version = pick(Name, version, List, {{2, 0}, []}),
+ Ix = pick(Name, index, List, []),
+ verify({alt, [nil, list]}, mnesia_lib:etype(Ix),
+ {bad_type, Name, {index, [Ix]}}),
+ Ix2 = [attr_to_pos(I, Attrs) || I <- Ix],
+
+ Frag = pick(Name, frag_properties, List, []),
+ verify({alt, [nil, list]}, mnesia_lib:etype(Frag),
+ {badarg, Name, {frag_properties, Frag}}),
+
+ Keys = check_keys(Name, List, record_info(fields, cstruct)),
+ check_duplicates(Name, Keys),
+ #cstruct{name = Name,
+ ram_copies = Rc,
+ disc_copies = Dc,
+ disc_only_copies = Doc,
+ type = Type,
+ index = Ix2,
+ snmp = Snmp,
+ load_order = LoadOrder,
+ access_mode = AccessMode,
+ local_content = LC,
+ record_name = RecName,
+ attributes = Attrs,
+ user_properties = lists:sort(UserProps),
+ frag_properties = lists:sort(Frag),
+ cookie = Cookie,
+ version = Version};
+list2cs(Other) ->
+ mnesia:abort({badarg, Other}).
+
+pick(Tab, Key, List, Default) ->
+ case lists:keysearch(Key, 1, List) of
+ false when Default == must ->
+ mnesia:abort({badarg, Tab, "Missing key", Key, List});
+ false ->
+ Default;
+ {value, {Key, Value}} ->
+ Value;
+ {value, BadArg} ->
+ mnesia:abort({bad_type, Tab, BadArg})
+ end.
+
+%% Convert attribute name to integer if neccessary
+attr_tab_to_pos(_Tab, Pos) when integer(Pos) ->
+ Pos;
+attr_tab_to_pos(Tab, Attr) ->
+ attr_to_pos(Attr, val({Tab, attributes})).
+
+%% Convert attribute name to integer if neccessary
+attr_to_pos(Pos, _Attrs) when integer(Pos) ->
+ Pos;
+attr_to_pos(Attr, Attrs) when atom(Attr) ->
+ attr_to_pos(Attr, Attrs, 2);
+attr_to_pos(Attr, _) ->
+ mnesia:abort({bad_type, Attr}).
+
+attr_to_pos(Attr, [Attr | _Attrs], Pos) ->
+ Pos;
+attr_to_pos(Attr, [_ | Attrs], Pos) ->
+ attr_to_pos(Attr, Attrs, Pos + 1);
+attr_to_pos(Attr, _, _) ->
+ mnesia:abort({bad_type, Attr}).
+
+check_keys(Tab, [{Key, _Val} | Tail], Items) ->
+ case lists:member(Key, Items) of
+ true -> [Key | check_keys(Tab, Tail, Items)];
+ false -> mnesia:abort({badarg, Tab, Key})
+ end;
+check_keys(_, [], _) ->
+ [];
+check_keys(Tab, Arg, _) ->
+ mnesia:abort({badarg, Tab, Arg}).
+
+check_duplicates(Tab, Keys) ->
+ case has_duplicates(Keys) of
+ false -> ok;
+ true -> mnesia:abort({badarg, Tab, "Duplicate keys", Keys})
+ end.
+
+has_duplicates([H | T]) ->
+ case lists:member(H, T) of
+ true -> true;
+ false -> has_duplicates(T)
+ end;
+has_duplicates([]) ->
+ false.
+
+%% This is the only place where we check the validity of data
+verify_cstruct(Cs) when record(Cs, cstruct) ->
+ verify_nodes(Cs),
+
+ Tab = Cs#cstruct.name,
+ verify(atom, mnesia_lib:etype(Tab), {bad_type, Tab}),
+ Type = Cs#cstruct.type,
+ verify(true, lists:member(Type, [set, bag, ordered_set]),
+ {bad_type, Tab, {type, Type}}),
+
+ %% Currently ordered_set is not supported for disk_only_copies.
+ if
+ Type == ordered_set, Cs#cstruct.disc_only_copies /= [] ->
+ mnesia:abort({bad_type, Tab, {not_supported, Type, disc_only_copies}});
+ true ->
+ ok
+ end,
+
+ RecName = Cs#cstruct.record_name,
+ verify(atom, mnesia_lib:etype(RecName),
+ {bad_type, Tab, {record_name, RecName}}),
+
+ Attrs = Cs#cstruct.attributes,
+ verify(list, mnesia_lib:etype(Attrs),
+ {bad_type, Tab, {attributes, Attrs}}),
+
+ Arity = length(Attrs) + 1,
+ verify(true, Arity > 2, {bad_type, Tab, {attributes, Attrs}}),
+
+ lists:foldl(fun(Attr,_Other) when Attr == snmp ->
+ mnesia:abort({bad_type, Tab, {attributes, [Attr]}});
+ (Attr,Other) ->
+ verify(atom, mnesia_lib:etype(Attr),
+ {bad_type, Tab, {attributes, [Attr]}}),
+ verify(false, lists:member(Attr, Other),
+ {combine_error, Tab, {attributes, [Attr | Other]}}),
+ [Attr | Other]
+ end,
+ [],
+ Attrs),
+
+ Index = Cs#cstruct.index,
+ verify({alt, [nil, list]}, mnesia_lib:etype(Index),
+ {bad_type, Tab, {index, Index}}),
+
+ IxFun =
+ fun(Pos) ->
+ verify(true, fun() ->
+ if
+ integer(Pos),
+ Pos > 2,
+ Pos =< Arity ->
+ true;
+ true -> false
+ end
+ end,
+ {bad_type, Tab, {index, [Pos]}})
+ end,
+ lists:foreach(IxFun, Index),
+
+ LC = Cs#cstruct.local_content,
+ verify({alt, [true, false]}, LC,
+ {bad_type, Tab, {local_content, LC}}),
+ Access = Cs#cstruct.access_mode,
+ verify({alt, [read_write, read_only]}, Access,
+ {bad_type, Tab, {access_mode, Access}}),
+
+ Snmp = Cs#cstruct.snmp,
+ verify(true, mnesia_snmp_hook:check_ustruct(Snmp),
+ {badarg, Tab, {snmp, Snmp}}),
+
+ CheckProp = fun(Prop) when tuple(Prop), size(Prop) >= 1 -> ok;
+ (Prop) -> mnesia:abort({bad_type, Tab, {user_properties, [Prop]}})
+ end,
+ lists:foreach(CheckProp, Cs#cstruct.user_properties),
+
+ case Cs#cstruct.cookie of
+ {{MegaSecs, Secs, MicroSecs}, _Node}
+ when integer(MegaSecs), integer(Secs),
+ integer(MicroSecs), atom(node) ->
+ ok;
+ Cookie ->
+ mnesia:abort({bad_type, Tab, {cookie, Cookie}})
+ end,
+ case Cs#cstruct.version of
+ {{Major, Minor}, _Detail}
+ when integer(Major), integer(Minor) ->
+ ok;
+ Version ->
+ mnesia:abort({bad_type, Tab, {version, Version}})
+ end.
+
+verify_nodes(Cs) ->
+ Tab = Cs#cstruct.name,
+ Ram = Cs#cstruct.ram_copies,
+ Disc = Cs#cstruct.disc_copies,
+ DiscOnly = Cs#cstruct.disc_only_copies,
+ LoadOrder = Cs#cstruct.load_order,
+
+ verify({alt, [nil, list]}, mnesia_lib:etype(Ram),
+ {bad_type, Tab, {ram_copies, Ram}}),
+ verify({alt, [nil, list]}, mnesia_lib:etype(Disc),
+ {bad_type, Tab, {disc_copies, Disc}}),
+ case Tab of
+ schema ->
+ verify([], DiscOnly, {bad_type, Tab, {disc_only_copies, DiscOnly}});
+ _ ->
+ verify({alt, [nil, list]},
+ mnesia_lib:etype(DiscOnly),
+ {bad_type, Tab, {disc_only_copies, DiscOnly}})
+ end,
+ verify(integer, mnesia_lib:etype(LoadOrder),
+ {bad_type, Tab, {load_order, LoadOrder}}),
+
+ Nodes = Ram ++ Disc ++ DiscOnly,
+ verify(list, mnesia_lib:etype(Nodes),
+ {combine_error, Tab,
+ [{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []}]}),
+ verify(false, has_duplicates(Nodes), {combine_error, Tab, Nodes}),
+ AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end,
+ lists:foreach(AtomCheck, Nodes).
+
+verify(Expected, Fun, Error) when function(Fun) ->
+ do_verify(Expected, catch Fun(), Error);
+verify(Expected, Actual, Error) ->
+ do_verify(Expected, Actual, Error).
+
+do_verify({alt, Values}, Value, Error) ->
+ case lists:member(Value, Values) of
+ true -> ok;
+ false -> mnesia:abort(Error)
+ end;
+do_verify(Value, Value, _) ->
+ ok;
+do_verify(_Value, _, Error) ->
+ mnesia:abort(Error).
+
+ensure_writable(Tab) ->
+ case val({Tab, where_to_write}) of
+ [] -> mnesia:abort({read_only, Tab});
+ _ -> ok
+ end.
+
+%% Ensure that all replicas on disk full nodes are active
+ensure_active(Cs) ->
+ ensure_active(Cs, active_replicas).
+
+ensure_active(Cs, What) ->
+ Tab = Cs#cstruct.name,
+ case val({Tab, What}) of
+ [] -> mnesia:abort({no_exists, Tab});
+ _ -> ok
+ end,
+ Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
+ mnesia_lib:cs_to_nodes(Cs)),
+ W = {Tab, What},
+ case Nodes -- val(W) of
+ [] ->
+ ok;
+ Ns ->
+ Expl = "All replicas on diskfull nodes are not active yet",
+ case val({Tab, local_content}) of
+ true ->
+ case rpc:multicall(Ns, ?MODULE, is_remote_member, [W]) of
+ {Replies, []} ->
+ check_active(Replies, Expl, Tab);
+ {_Replies, BadNs} ->
+ mnesia:abort({not_active, Expl, Tab, BadNs})
+ end;
+ false ->
+ mnesia:abort({not_active, Expl, Tab, Ns})
+ end
+ end.
+
+ensure_not_active(schema, Node) ->
+ case lists:member(Node, val({schema, active_replicas})) of
+ false ->
+ ok;
+ true ->
+ Expl = "Mnesia is running",
+ mnesia:abort({active, Expl, Node})
+ end.
+
+is_remote_member(Key) ->
+ IsActive = lists:member(node(), val(Key)),
+ {IsActive, node()}.
+
+check_active([{true, _Node} | Replies], Expl, Tab) ->
+ check_active(Replies, Expl, Tab);
+check_active([{false, Node} | _Replies], Expl, Tab) ->
+ mnesia:abort({not_active, Expl, Tab, [Node]});
+check_active([{badrpc, Reason} | _Replies], Expl, Tab) ->
+ mnesia:abort({not_active, Expl, Tab, Reason});
+check_active([], _Expl, _Tab) ->
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Here's the real interface function to create a table
+
+create_table(TabDef) ->
+ schema_transaction(fun() -> do_multi_create_table(TabDef) end).
+
+%% And the corresponding do routines ....
+
+do_multi_create_table(TabDef) ->
+ get_tid_ts_and_lock(schema, write),
+ ensure_writable(schema),
+ Cs = list2cs(TabDef),
+ case Cs#cstruct.frag_properties of
+ [] ->
+ do_create_table(Cs);
+ _Props ->
+ CsList = mnesia_frag:expand_cstruct(Cs),
+ lists:foreach(fun do_create_table/1, CsList)
+ end,
+ ok.
+
+do_create_table(Cs) ->
+ {_Mod, _Tid, Ts} = get_tid_ts_and_lock(schema, none),
+ Store = Ts#tidstore.store,
+ do_insert_schema_ops(Store, make_create_table(Cs)).
+
+make_create_table(Cs) ->
+ Tab = Cs#cstruct.name,
+ verify('EXIT', element(1, ?catch_val({Tab, cstruct})),
+ {already_exists, Tab}),
+ unsafe_make_create_table(Cs).
+
+% unsafe_do_create_table(Cs) ->
+% {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
+% Store = Ts#tidstore.store,
+% do_insert_schema_ops(Store, unsafe_make_create_table(Cs)).
+
+unsafe_make_create_table(Cs) ->
+ {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
+ verify_cstruct(Cs),
+ Tab = Cs#cstruct.name,
+
+ %% Check that we have all disc replica nodes running
+ DiscNodes = Cs#cstruct.disc_copies ++ Cs#cstruct.disc_only_copies,
+ RunningNodes = val({current, db_nodes}),
+ CheckDisc = fun(N) ->
+ verify(true, lists:member(N, RunningNodes),
+ {not_active, Tab, N})
+ end,
+ lists:foreach(CheckDisc, DiscNodes),
+
+ Nodes = mnesia_lib:intersect(mnesia_lib:cs_to_nodes(Cs), RunningNodes),
+ Store = Ts#tidstore.store,
+ mnesia_locker:wlock_no_exist(Tid, Store, Tab, Nodes),
+ [{op, create_table, cs2list(Cs)}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Delete a table entirely on all nodes.
+
+delete_table(Tab) ->
+ schema_transaction(fun() -> do_delete_table(Tab) end).
+
+do_delete_table(schema) ->
+ mnesia:abort({bad_type, schema});
+do_delete_table(Tab) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ ensure_writable(schema),
+ insert_schema_ops(TidTs, make_delete_table(Tab, whole_table)).
+
+make_delete_table(Tab, Mode) ->
+ case Mode of
+ whole_table ->
+ case val({Tab, frag_properties}) of
+ [] ->
+ [make_delete_table2(Tab)];
+ _Props ->
+ %% Check if it is a base table
+ mnesia_frag:lookup_frag_hash(Tab),
+
+ %% Check for foreigners
+ F = mnesia_frag:lookup_foreigners(Tab),
+ verify([], F, {combine_error, Tab, "Too many foreigners", F}),
+ [make_delete_table2(T) || T <- mnesia_frag:frag_names(Tab)]
+ end;
+ single_frag ->
+ [make_delete_table2(Tab)]
+ end.
+
+make_delete_table2(Tab) ->
+ get_tid_ts_and_lock(Tab, write),
+ Cs = val({Tab, cstruct}),
+ ensure_active(Cs),
+ ensure_writable(Tab),
+ {op, delete_table, cs2list(Cs)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Change fragmentation of a table
+
+change_table_frag(Tab, Change) ->
+ schema_transaction(fun() -> do_change_table_frag(Tab, Change) end).
+
+do_change_table_frag(Tab, Change) when atom(Tab), Tab /= schema ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ Ops = mnesia_frag:change_table_frag(Tab, Change),
+ [insert_schema_ops(TidTs, Op) || Op <- Ops],
+ ok;
+do_change_table_frag(Tab, _Change) ->
+ mnesia:abort({bad_type, Tab}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Clear a table
+
+clear_table(Tab) ->
+ schema_transaction(fun() -> do_clear_table(Tab) end).
+
+do_clear_table(schema) ->
+ mnesia:abort({bad_type, schema});
+do_clear_table(Tab) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, write),
+ insert_schema_ops(TidTs, make_clear_table(Tab)).
+
+make_clear_table(Tab) ->
+ ensure_writable(schema),
+ Cs = val({Tab, cstruct}),
+ ensure_active(Cs),
+ ensure_writable(Tab),
+ [{op, clear_table, cs2list(Cs)}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+add_table_copy(Tab, Node, Storage) ->
+ schema_transaction(fun() -> do_add_table_copy(Tab, Node, Storage) end).
+
+do_add_table_copy(Tab, Node, Storage) when atom(Tab), atom(Node) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ insert_schema_ops(TidTs, make_add_table_copy(Tab, Node, Storage));
+do_add_table_copy(Tab,Node,_) ->
+ mnesia:abort({badarg, Tab, Node}).
+
+make_add_table_copy(Tab, Node, Storage) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ Ns = mnesia_lib:cs_to_nodes(Cs),
+ verify(false, lists:member(Node, Ns), {already_exists, Tab, Node}),
+ Cs2 = new_cs(Cs, Node, Storage, add),
+ verify_cstruct(Cs2),
+
+ %% Check storage and if node is running
+ IsRunning = lists:member(Node, val({current, db_nodes})),
+ if
+ Storage == unknown ->
+ mnesia:abort({badarg, Tab, Storage});
+ Tab == schema ->
+ if
+ Storage /= ram_copies ->
+ mnesia:abort({badarg, Tab, Storage});
+ IsRunning == true ->
+ mnesia:abort({already_exists, Tab, Node});
+ true ->
+ ignore
+ end;
+ Storage == ram_copies ->
+ ignore;
+ IsRunning == true ->
+ ignore;
+ IsRunning == false ->
+ mnesia:abort({not_active, schema, Node})
+ end,
+ [{op, add_table_copy, Storage, Node, cs2list(Cs2)}].
+
+del_table_copy(Tab, Node) ->
+ schema_transaction(fun() -> do_del_table_copy(Tab, Node) end).
+
+do_del_table_copy(Tab, Node) when atom(Node) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+%% get_tid_ts_and_lock(Tab, write),
+ insert_schema_ops(TidTs, make_del_table_copy(Tab, Node));
+do_del_table_copy(Tab, Node) ->
+ mnesia:abort({badarg, Tab, Node}).
+
+make_del_table_copy(Tab, Node) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ Storage = mnesia_lib:schema_cs_to_storage_type(Node, Cs),
+ Cs2 = new_cs(Cs, Node, Storage, del),
+ case mnesia_lib:cs_to_nodes(Cs2) of
+ [] when Tab == schema ->
+ mnesia:abort({combine_error, Tab, "Last replica"});
+ [] ->
+ ensure_active(Cs),
+ dbg_out("Last replica deleted in table ~p~n", [Tab]),
+ make_delete_table(Tab, whole_table);
+ _ when Tab == schema ->
+ ensure_active(Cs2),
+ ensure_not_active(Tab, Node),
+ verify_cstruct(Cs2),
+ Ops = remove_node_from_tabs(val({schema, tables}), Node),
+ [{op, del_table_copy, ram_copies, Node, cs2list(Cs2)} | Ops];
+ _ ->
+ ensure_active(Cs),
+ verify_cstruct(Cs2),
+ [{op, del_table_copy, Storage, Node, cs2list(Cs2)}]
+ end.
+
+remove_node_from_tabs([], _Node) ->
+ [];
+remove_node_from_tabs([schema|Rest], Node) ->
+ remove_node_from_tabs(Rest, Node);
+remove_node_from_tabs([Tab|Rest], Node) ->
+ {Cs, IsFragModified} =
+ mnesia_frag:remove_node(Node, incr_version(val({Tab, cstruct}))),
+ case mnesia_lib:schema_cs_to_storage_type(Node, Cs) of
+ unknown ->
+ case IsFragModified of
+ true ->
+ [{op, change_table_frag, {del_node, Node}, cs2list(Cs)} |
+ remove_node_from_tabs(Rest, Node)];
+ false ->
+ remove_node_from_tabs(Rest, Node)
+ end;
+ Storage ->
+ Cs2 = new_cs(Cs, Node, Storage, del),
+ case mnesia_lib:cs_to_nodes(Cs2) of
+ [] ->
+ [{op, delete_table, cs2list(Cs)} |
+ remove_node_from_tabs(Rest, Node)];
+ _Ns ->
+ verify_cstruct(Cs2),
+ [{op, del_table_copy, ram_copies, Node, cs2list(Cs2)}|
+ remove_node_from_tabs(Rest, Node)]
+ end
+ end.
+
+new_cs(Cs, Node, ram_copies, add) ->
+ Cs#cstruct{ram_copies = opt_add(Node, Cs#cstruct.ram_copies)};
+new_cs(Cs, Node, disc_copies, add) ->
+ Cs#cstruct{disc_copies = opt_add(Node, Cs#cstruct.disc_copies)};
+new_cs(Cs, Node, disc_only_copies, add) ->
+ Cs#cstruct{disc_only_copies = opt_add(Node, Cs#cstruct.disc_only_copies)};
+new_cs(Cs, Node, ram_copies, del) ->
+ Cs#cstruct{ram_copies = lists:delete(Node , Cs#cstruct.ram_copies)};
+new_cs(Cs, Node, disc_copies, del) ->
+ Cs#cstruct{disc_copies = lists:delete(Node , Cs#cstruct.disc_copies)};
+new_cs(Cs, Node, disc_only_copies, del) ->
+ Cs#cstruct{disc_only_copies =
+ lists:delete(Node , Cs#cstruct.disc_only_copies)};
+new_cs(Cs, _Node, Storage, _Op) ->
+ mnesia:abort({badarg, Cs#cstruct.name, Storage}).
+
+
+opt_add(N, L) -> [N | lists:delete(N, L)].
+
+move_table(Tab, FromNode, ToNode) ->
+ schema_transaction(fun() -> do_move_table(Tab, FromNode, ToNode) end).
+
+do_move_table(schema, _FromNode, _ToNode) ->
+ mnesia:abort({bad_type, schema});
+do_move_table(Tab, FromNode, ToNode) when atom(FromNode), atom(ToNode) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ insert_schema_ops(TidTs, make_move_table(Tab, FromNode, ToNode));
+do_move_table(Tab, FromNode, ToNode) ->
+ mnesia:abort({badarg, Tab, FromNode, ToNode}).
+
+make_move_table(Tab, FromNode, ToNode) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ Ns = mnesia_lib:cs_to_nodes(Cs),
+ verify(false, lists:member(ToNode, Ns), {already_exists, Tab, ToNode}),
+ verify(true, lists:member(FromNode, val({Tab, where_to_write})),
+ {not_active, Tab, FromNode}),
+ verify(false, val({Tab,local_content}),
+ {"Cannot move table with local content", Tab}),
+ ensure_active(Cs),
+ Running = val({current, db_nodes}),
+ Storage = mnesia_lib:schema_cs_to_storage_type(FromNode, Cs),
+ verify(true, lists:member(ToNode, Running), {not_active, schema, ToNode}),
+
+ Cs2 = new_cs(Cs, ToNode, Storage, add),
+ Cs3 = new_cs(Cs2, FromNode, Storage, del),
+ verify_cstruct(Cs3),
+ [{op, add_table_copy, Storage, ToNode, cs2list(Cs2)},
+ {op, sync_trans},
+ {op, del_table_copy, Storage, FromNode, cs2list(Cs3)}].
+
+%% end of functions to add and delete nodes to tables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+
+change_table_copy_type(Tab, Node, ToS) ->
+ schema_transaction(fun() -> do_change_table_copy_type(Tab, Node, ToS) end).
+
+do_change_table_copy_type(Tab, Node, ToS) when atom(Node) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, write), % ensure global sync
+ %% get_tid_ts_and_lock(Tab, read),
+ insert_schema_ops(TidTs, make_change_table_copy_type(Tab, Node, ToS));
+do_change_table_copy_type(Tab, Node, _ToS) ->
+ mnesia:abort({badarg, Tab, Node}).
+
+make_change_table_copy_type(Tab, Node, unknown) ->
+ make_del_table_copy(Tab, Node);
+make_change_table_copy_type(Tab, Node, ToS) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ FromS = mnesia_lib:storage_type_at_node(Node, Tab),
+
+ case compare_storage_type(false, FromS, ToS) of
+ {same, _} ->
+ mnesia:abort({already_exists, Tab, Node, ToS});
+ {diff, _} ->
+ ignore;
+ incompatible ->
+ ensure_active(Cs)
+ end,
+
+ Cs2 = new_cs(Cs, Node, FromS, del),
+ Cs3 = new_cs(Cs2, Node, ToS, add),
+ verify_cstruct(Cs3),
+
+ if
+ FromS == unknown ->
+ make_add_table_copy(Tab, Node, ToS);
+ true ->
+ ignore
+ end,
+
+ [{op, change_table_copy_type, Node, FromS, ToS, cs2list(Cs3)}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% change index functions ....
+%% Pos is allready added by 1 in both of these functions
+
+add_table_index(Tab, Pos) ->
+ schema_transaction(fun() -> do_add_table_index(Tab, Pos) end).
+
+do_add_table_index(schema, _Attr) ->
+ mnesia:abort({bad_type, schema});
+do_add_table_index(Tab, Attr) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, read),
+ Pos = attr_tab_to_pos(Tab, Attr),
+ insert_schema_ops(TidTs, make_add_table_index(Tab, Pos)).
+
+make_add_table_index(Tab, Pos) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ Ix = Cs#cstruct.index,
+ verify(false, lists:member(Pos, Ix), {already_exists, Tab, Pos}),
+ Ix2 = lists:sort([Pos | Ix]),
+ Cs2 = Cs#cstruct{index = Ix2},
+ verify_cstruct(Cs2),
+ [{op, add_index, Pos, cs2list(Cs2)}].
+
+del_table_index(Tab, Pos) ->
+ schema_transaction(fun() -> do_del_table_index(Tab, Pos) end).
+
+do_del_table_index(schema, _Attr) ->
+ mnesia:abort({bad_type, schema});
+do_del_table_index(Tab, Attr) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, read),
+ Pos = attr_tab_to_pos(Tab, Attr),
+ insert_schema_ops(TidTs, make_del_table_index(Tab, Pos)).
+
+make_del_table_index(Tab, Pos) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ Ix = Cs#cstruct.index,
+ verify(true, lists:member(Pos, Ix), {no_exists, Tab, Pos}),
+ Cs2 = Cs#cstruct{index = lists:delete(Pos, Ix)},
+ verify_cstruct(Cs2),
+ [{op, del_index, Pos, cs2list(Cs2)}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+add_snmp(Tab, Ustruct) ->
+ schema_transaction(fun() -> do_add_snmp(Tab, Ustruct) end).
+
+do_add_snmp(schema, _Ustruct) ->
+ mnesia:abort({bad_type, schema});
+do_add_snmp(Tab, Ustruct) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, read),
+ insert_schema_ops(TidTs, make_add_snmp(Tab, Ustruct)).
+
+make_add_snmp(Tab, Ustruct) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ verify([], Cs#cstruct.snmp, {already_exists, Tab, snmp}),
+ Error = {badarg, Tab, snmp, Ustruct},
+ verify(true, mnesia_snmp_hook:check_ustruct(Ustruct), Error),
+ Cs2 = Cs#cstruct{snmp = Ustruct},
+ verify_cstruct(Cs2),
+ [{op, add_snmp, Ustruct, cs2list(Cs2)}].
+
+del_snmp(Tab) ->
+ schema_transaction(fun() -> do_del_snmp(Tab) end).
+
+do_del_snmp(schema) ->
+ mnesia:abort({bad_type, schema});
+do_del_snmp(Tab) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, read),
+ insert_schema_ops(TidTs, make_del_snmp(Tab)).
+
+make_del_snmp(Tab) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ Cs2 = Cs#cstruct{snmp = []},
+ verify_cstruct(Cs2),
+ [{op, del_snmp, cs2list(Cs2)}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+
+transform_table(Tab, Fun, NewAttrs, NewRecName)
+ when function(Fun), list(NewAttrs), atom(NewRecName) ->
+ schema_transaction(fun() -> do_transform_table(Tab, Fun, NewAttrs, NewRecName) end);
+
+transform_table(Tab, ignore, NewAttrs, NewRecName)
+ when list(NewAttrs), atom(NewRecName) ->
+ schema_transaction(fun() -> do_transform_table(Tab, ignore, NewAttrs, NewRecName) end);
+
+transform_table(Tab, Fun, NewAttrs, NewRecName) ->
+ {aborted,{bad_type, Tab, Fun, NewAttrs, NewRecName}}.
+
+do_transform_table(schema, _Fun, _NewAttrs, _NewRecName) ->
+ mnesia:abort({bad_type, schema});
+do_transform_table(Tab, Fun, NewAttrs, NewRecName) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, write),
+ insert_schema_ops(TidTs, make_transform(Tab, Fun, NewAttrs, NewRecName)).
+
+make_transform(Tab, Fun, NewAttrs, NewRecName) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ ensure_writable(Tab),
+ case mnesia_lib:val({Tab, index}) of
+ [] ->
+ Cs2 = Cs#cstruct{attributes = NewAttrs, record_name = NewRecName},
+ verify_cstruct(Cs2),
+ [{op, transform, Fun, cs2list(Cs2)}];
+ PosList ->
+ DelIdx = fun(Pos, Ncs) ->
+ Ix = Ncs#cstruct.index,
+ Ncs1 = Ncs#cstruct{index = lists:delete(Pos, Ix)},
+ Op = {op, del_index, Pos, cs2list(Ncs1)},
+ {Op, Ncs1}
+ end,
+ AddIdx = fun(Pos, Ncs) ->
+ Ix = Ncs#cstruct.index,
+ Ix2 = lists:sort([Pos | Ix]),
+ Ncs1 = Ncs#cstruct{index = Ix2},
+ Op = {op, add_index, Pos, cs2list(Ncs1)},
+ {Op, Ncs1}
+ end,
+ {DelOps, Cs1} = lists:mapfoldl(DelIdx, Cs, PosList),
+ Cs2 = Cs1#cstruct{attributes = NewAttrs, record_name = NewRecName},
+ {AddOps, Cs3} = lists:mapfoldl(AddIdx, Cs2, PosList),
+ verify_cstruct(Cs3),
+ lists:flatten([DelOps, {op, transform, Fun, cs2list(Cs2)}, AddOps])
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+
+change_table_access_mode(Tab, Mode) ->
+ schema_transaction(fun() -> do_change_table_access_mode(Tab, Mode) end).
+
+do_change_table_access_mode(Tab, Mode) ->
+ {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
+ Store = Ts#tidstore.store,
+ mnesia_locker:wlock_no_exist(Tid, Store, schema, val({schema, active_replicas})),
+ mnesia_locker:wlock_no_exist(Tid, Store, Tab, val({Tab, active_replicas})),
+ do_insert_schema_ops(Store, make_change_table_access_mode(Tab, Mode)).
+
+make_change_table_access_mode(Tab, Mode) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ OldMode = Cs#cstruct.access_mode,
+ verify(false, OldMode == Mode, {already_exists, Tab, Mode}),
+ Cs2 = Cs#cstruct{access_mode = Mode},
+ verify_cstruct(Cs2),
+ [{op, change_table_access_mode, cs2list(Cs2), OldMode, Mode}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+change_table_load_order(Tab, LoadOrder) ->
+ schema_transaction(fun() -> do_change_table_load_order(Tab, LoadOrder) end).
+
+do_change_table_load_order(schema, _LoadOrder) ->
+ mnesia:abort({bad_type, schema});
+do_change_table_load_order(Tab, LoadOrder) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ get_tid_ts_and_lock(Tab, none),
+ insert_schema_ops(TidTs, make_change_table_load_order(Tab, LoadOrder)).
+
+make_change_table_load_order(Tab, LoadOrder) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ OldLoadOrder = Cs#cstruct.load_order,
+ Cs2 = Cs#cstruct{load_order = LoadOrder},
+ verify_cstruct(Cs2),
+ [{op, change_table_load_order, cs2list(Cs2), OldLoadOrder, LoadOrder}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+write_table_property(Tab, Prop) when tuple(Prop), size(Prop) >= 1 ->
+ schema_transaction(fun() -> do_write_table_property(Tab, Prop) end);
+write_table_property(Tab, Prop) ->
+ {aborted, {bad_type, Tab, Prop}}.
+do_write_table_property(Tab, Prop) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ {_, _, Ts} = TidTs,
+ Store = Ts#tidstore.store,
+ case change_prop_in_existing_op(Tab, Prop, write_property, Store) of
+ true ->
+ dbg_out("change_prop_in_existing_op"
+ "(~p,~p,write_property,Store) -> true~n",
+ [Tab,Prop]),
+ %% we have merged the table prop into the create_table op
+ ok;
+ false ->
+ dbg_out("change_prop_in_existing_op"
+ "(~p,~p,write_property,Store) -> false~n",
+ [Tab,Prop]),
+ %% this must be an existing table
+ get_tid_ts_and_lock(Tab, none),
+ insert_schema_ops(TidTs, make_write_table_properties(Tab, [Prop]))
+ end.
+
+make_write_table_properties(Tab, Props) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ make_write_table_properties(Tab, Props, Cs).
+
+make_write_table_properties(Tab, [Prop | Props], Cs) ->
+ OldProps = Cs#cstruct.user_properties,
+ PropKey = element(1, Prop),
+ DelProps = lists:keydelete(PropKey, 1, OldProps),
+ MergedProps = lists:merge(DelProps, [Prop]),
+ Cs2 = Cs#cstruct{user_properties = MergedProps},
+ verify_cstruct(Cs2),
+ [{op, write_property, cs2list(Cs2), Prop} |
+ make_write_table_properties(Tab, Props, Cs2)];
+make_write_table_properties(_Tab, [], _Cs) ->
+ [].
+
+change_prop_in_existing_op(Tab, Prop, How, Store) ->
+ Ops = ets:match_object(Store, '_'),
+ case update_existing_op(Ops, Tab, Prop, How, []) of
+ {true, Ops1} ->
+ ets:match_delete(Store, '_'),
+ [ets:insert(Store, Op) || Op <- Ops1],
+ true;
+ false ->
+ false
+ end.
+
+update_existing_op([{op, Op, L = [{name,Tab}|_], _OldProp}|Ops],
+ Tab, Prop, How, Acc) when Op == write_property;
+ Op == delete_property ->
+ %% Apparently, mnesia_dumper doesn't care about OldProp here -- just L,
+ %% so we will throw away OldProp (not that it matters...) and insert Prop.
+ %% as element 3.
+ L1 = insert_prop(Prop, L, How),
+ NewOp = {op, How, L1, Prop},
+ {true, lists:reverse(Acc) ++ [NewOp|Ops]};
+update_existing_op([Op = {op, create_table, L}|Ops], Tab, Prop, How, Acc) ->
+ case lists:keysearch(name, 1, L) of
+ {value, {_, Tab}} ->
+ %% Tab is being created here -- insert Prop into L
+ L1 = insert_prop(Prop, L, How),
+ {true, lists:reverse(Acc) ++ [{op, create_table, L1}|Ops]};
+ _ ->
+ update_existing_op(Ops, Tab, Prop, How, [Op|Acc])
+ end;
+update_existing_op([Op|Ops], Tab, Prop, How, Acc) ->
+ update_existing_op(Ops, Tab, Prop, How, [Op|Acc]);
+update_existing_op([], _, _, _, _) ->
+ false.
+
+%% perhaps a misnomer. How could also be delete_property... never mind.
+%% Returns the modified L.
+insert_prop(Prop, L, How) ->
+ Prev = find_props(L),
+ MergedProps = merge_with_previous(How, Prop, Prev),
+ replace_props(L, MergedProps).
+
+
+find_props([{user_properties, P}|_]) -> P;
+find_props([_H|T]) -> find_props(T).
+%% we shouldn't reach []
+
+replace_props([{user_properties, _}|T], P) -> [{user_properties, P}|T];
+replace_props([H|T], P) -> [H|replace_props(T, P)].
+%% again, we shouldn't reach []
+
+merge_with_previous(write_property, Prop, Prev) ->
+ Key = element(1, Prop),
+ Prev1 = lists:keydelete(Key, 1, Prev),
+ lists:sort([Prop|Prev1]);
+merge_with_previous(delete_property, PropKey, Prev) ->
+ lists:keydelete(PropKey, 1, Prev).
+
+delete_table_property(Tab, PropKey) ->
+ schema_transaction(fun() -> do_delete_table_property(Tab, PropKey) end).
+
+do_delete_table_property(Tab, PropKey) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ {_, _, Ts} = TidTs,
+ Store = Ts#tidstore.store,
+ case change_prop_in_existing_op(Tab, PropKey, delete_property, Store) of
+ true ->
+ dbg_out("change_prop_in_existing_op"
+ "(~p,~p,delete_property,Store) -> true~n",
+ [Tab,PropKey]),
+ %% we have merged the table prop into the create_table op
+ ok;
+ false ->
+ dbg_out("change_prop_in_existing_op"
+ "(~p,~p,delete_property,Store) -> false~n",
+ [Tab,PropKey]),
+ %% this must be an existing table
+ get_tid_ts_and_lock(Tab, none),
+ insert_schema_ops(TidTs,
+ make_delete_table_properties(Tab, [PropKey]))
+ end.
+
+make_delete_table_properties(Tab, PropKeys) ->
+ ensure_writable(schema),
+ Cs = incr_version(val({Tab, cstruct})),
+ ensure_active(Cs),
+ make_delete_table_properties(Tab, PropKeys, Cs).
+
+make_delete_table_properties(Tab, [PropKey | PropKeys], Cs) ->
+ OldProps = Cs#cstruct.user_properties,
+ Props = lists:keydelete(PropKey, 1, OldProps),
+ Cs2 = Cs#cstruct{user_properties = Props},
+ verify_cstruct(Cs2),
+ [{op, delete_property, cs2list(Cs2), PropKey} |
+ make_delete_table_properties(Tab, PropKeys, Cs2)];
+make_delete_table_properties(_Tab, [], _Cs) ->
+ [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Ensure that the transaction can be committed even
+%% if the node crashes and Mnesia is restarted
+prepare_commit(Tid, Commit, WaitFor) ->
+ case Commit#commit.schema_ops of
+ [] ->
+ {false, Commit, optional};
+ OrigOps ->
+ {Modified, Ops, DumperMode} =
+ prepare_ops(Tid, OrigOps, WaitFor, false, [], optional),
+ InitBy = schema_prepare,
+ GoodRes = {Modified,
+ Commit#commit{schema_ops = lists:reverse(Ops)},
+ DumperMode},
+ case DumperMode of
+ optional ->
+ dbg_out("Transaction log dump skipped (~p): ~w~n",
+ [DumperMode, InitBy]);
+ mandatory ->
+ case mnesia_controller:sync_dump_log(InitBy) of
+ dumped ->
+ GoodRes;
+ {error, Reason} ->
+ mnesia:abort(Reason)
+ end
+ end,
+ case Ops of
+ [] ->
+ ignore;
+ _ ->
+ %% We need to grab a dumper lock here, the log may not
+ %% be dumped by others, during the schema commit phase.
+ mnesia_controller:wait_for_schema_commit_lock()
+ end,
+ GoodRes
+ end.
+
+prepare_ops(Tid, [Op | Ops], WaitFor, Changed, Acc, DumperMode) ->
+ case prepare_op(Tid, Op, WaitFor) of
+ {true, mandatory} ->
+ prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], mandatory);
+ {true, optional} ->
+ prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], DumperMode);
+ {true, Ops2, mandatory} ->
+ prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, mandatory);
+ {true, Ops2, optional} ->
+ prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, DumperMode);
+ {false, mandatory} ->
+ prepare_ops(Tid, Ops, WaitFor, true, Acc, mandatory);
+ {false, optional} ->
+ prepare_ops(Tid, Ops, WaitFor, true, Acc, DumperMode)
+ end;
+prepare_ops(_Tid, [], _WaitFor, Changed, Acc, DumperMode) ->
+ {Changed, Acc, DumperMode}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Prepare for commit
+%% returns true if Op should be included, i.e. unmodified
+%% {true, Operation} if NewRecs should be included, i.e. modified
+%% false if Op should NOT be included, i.e. modified
+%%
+prepare_op(_Tid, {op, rec, unknown, Rec}, _WaitFor) ->
+ {{Tab, Key}, Items, _Op} = Rec,
+ case val({Tab, storage_type}) of
+ unknown ->
+ {false, optional};
+ Storage ->
+ mnesia_tm:prepare_snmp(Tab, Key, Items), % May exit
+ {true, [{op, rec, Storage, Rec}], optional}
+ end;
+
+prepare_op(_Tid, {op, announce_im_running, _Node, SchemaDef, Running, RemoteRunning}, _WaitFor) ->
+ SchemaCs = list2cs(SchemaDef),
+ case lists:member(node(), Running) of
+ true ->
+ announce_im_running(RemoteRunning -- Running, SchemaCs);
+ false ->
+ announce_im_running(Running -- RemoteRunning, SchemaCs)
+ end,
+ {false, optional};
+
+prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) ->
+ CoordPid ! {sync_trans, self()},
+ receive
+ {sync_trans, CoordPid} ->
+ {false, optional};
+ Else ->
+ mnesia_lib:verbose("sync_op terminated due to ~p~n", [Else]),
+ mnesia:abort(Else)
+ end;
+
+prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) ->
+ case receive_sync(Nodes, []) of
+ {abort, Reason} ->
+ mnesia_lib:verbose("sync_op terminated due to ~p~n", [Reason]),
+ mnesia:abort(Reason);
+ Pids ->
+ [Pid ! {sync_trans, self()} || Pid <- Pids],
+ {false, optional}
+ end;
+prepare_op(Tid, {op, create_table, TabDef}, _WaitFor) ->
+ Cs = list2cs(TabDef),
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ UseDir = mnesia_monitor:use_dir(),
+ Tab = Cs#cstruct.name,
+ case Storage of
+ disc_copies when UseDir == false ->
+ UseDirReason = {bad_type, Tab, Storage, node()},
+ mnesia:abort(UseDirReason);
+ disc_only_copies when UseDir == false ->
+ UseDirReason = {bad_type, Tab, Storage, node()},
+ mnesia:abort(UseDirReason);
+ ram_copies ->
+ create_ram_table(Tab, Cs#cstruct.type),
+ insert_cstruct(Tid, Cs, false),
+ {true, optional};
+ disc_copies ->
+ create_ram_table(Tab, Cs#cstruct.type),
+ create_disc_table(Tab),
+ insert_cstruct(Tid, Cs, false),
+ {true, optional};
+ disc_only_copies ->
+ create_disc_only_table(Tab,Cs#cstruct.type),
+ insert_cstruct(Tid, Cs, false),
+ {true, optional};
+ unknown -> %% No replica on this node
+ insert_cstruct(Tid, Cs, false),
+ {true, optional}
+ end;
+
+prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}, _WaitFor) ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+
+ if
+ Tab == schema ->
+ {true, optional}; % Nothing to prepare
+ Node == node() ->
+ case mnesia_lib:val({schema, storage_type}) of
+ ram_copies when Storage /= ram_copies ->
+ Error = {combine_error, Tab, "has no disc", Node},
+ mnesia:abort(Error);
+ _ ->
+ ok
+ end,
+ %% Tables are created by mnesia_loader get_network code
+ insert_cstruct(Tid, Cs, true),
+ case mnesia_controller:get_network_copy(Tab, Cs) of
+ {loaded, ok} ->
+ {true, optional};
+ {not_loaded, ErrReason} ->
+ Reason = {system_limit, Tab, {Node, ErrReason}},
+ mnesia:abort(Reason)
+ end;
+ Node /= node() ->
+ %% Verify that ram table not has been dumped to disc
+ if
+ Storage /= ram_copies ->
+ case mnesia_lib:schema_cs_to_storage_type(node(), Cs) of
+ ram_copies ->
+ Dat = mnesia_lib:tab2dcd(Tab),
+ case mnesia_lib:exists(Dat) of
+ true ->
+ mnesia:abort({combine_error, Tab, Storage,
+ "Table dumped to disc", node()});
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end;
+ true ->
+ ok
+ end,
+ insert_cstruct(Tid, Cs, true),
+ {true, optional}
+ end;
+
+prepare_op(Tid, {op, del_table_copy, _Storage, Node, TabDef}, _WaitFor) ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+
+ if
+ %% Schema table lock is always required to run a schema op.
+ %% No need to look it.
+ node(Tid#tid.pid) == node(), Tab /= schema ->
+ Pid = spawn_link(?MODULE, lock_del_table, [Tab, Node, Cs, self()]),
+ receive
+ {Pid, updated} ->
+ {true, optional};
+ {Pid, FailReason} ->
+ mnesia:abort(FailReason);
+ {'EXIT', Pid, Reason} ->
+ mnesia:abort(Reason)
+ end;
+ true ->
+ {true, optional}
+ end;
+
+prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
+ when N == node() ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+
+ NotActive = mnesia_lib:not_active_here(Tab),
+
+ if
+ NotActive == true ->
+ mnesia:abort({not_active, Tab, node()});
+
+ Tab == schema ->
+ case {FromS, ToS} of
+ {ram_copies, disc_copies} ->
+ case mnesia:system_info(schema_location) of
+ opt_disc ->
+ ignore;
+ _ ->
+ mnesia:abort({combine_error, Tab, node(),
+ "schema_location must be opt_disc"})
+ end,
+ Dir = mnesia_lib:dir(),
+ case opt_create_dir(true, Dir) of
+ ok ->
+ purge_dir(Dir, []),
+ mnesia_log:purge_all_logs(),
+ set(use_dir, true),
+ mnesia_log:init(),
+ Ns = val({current, db_nodes}), %mnesia_lib:running_nodes(),
+ F = fun(U) -> mnesia_recover:log_mnesia_up(U) end,
+ lists:foreach(F, Ns),
+
+ mnesia_dumper:raw_named_dump_table(Tab, dmp),
+ mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS);
+ {error, Reason} ->
+ mnesia:abort(Reason)
+ end;
+ {disc_copies, ram_copies} ->
+ Ltabs = val({schema, local_tables}) -- [schema],
+ Dtabs = [L || L <- Ltabs,
+ val({L, storage_type}) /= ram_copies],
+ verify([], Dtabs, {"Disc resident tables", Dtabs, N});
+ _ ->
+ mnesia:abort({combine_error, Tab, ToS})
+ end;
+
+ FromS == ram_copies ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ Dat = mnesia_lib:tab2dcd(Tab),
+ case mnesia_lib:exists(Dat) of
+ true ->
+ mnesia:abort({combine_error, Tab, node(),
+ "Table dump exists"});
+ false ->
+ case ToS of
+ disc_copies ->
+ mnesia_log:ets2dcd(Tab, dmp);
+ disc_only_copies ->
+ mnesia_dumper:raw_named_dump_table(Tab, dmp)
+ end,
+ mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS)
+ end;
+ false ->
+ mnesia:abort({has_no_disc, node()})
+ end;
+
+ FromS == disc_copies, ToS == disc_only_copies ->
+ mnesia_dumper:raw_named_dump_table(Tab, dmp);
+ FromS == disc_only_copies ->
+ Type = Cs#cstruct.type,
+ create_ram_table(Tab, Type),
+ Datname = mnesia_lib:tab2dat(Tab),
+ Repair = mnesia_monitor:get_env(auto_repair),
+ case mnesia_lib:dets_to_ets(Tab, Tab, Datname, Type, Repair, no) of
+ loaded -> ok;
+ Reason ->
+ Err = "Failed to copy disc data to ram",
+ mnesia:abort({system_limit, Tab, {Err,Reason}})
+ end;
+ true ->
+ ignore
+ end,
+ {true, mandatory};
+
+prepare_op(_Tid, {op, change_table_copy_type, N, _FromS, _ToS, _TabDef}, _WaitFor)
+ when N /= node() ->
+ {true, mandatory};
+
+prepare_op(_Tid, {op, delete_table, _TabDef}, _WaitFor) ->
+ {true, mandatory};
+
+prepare_op(_Tid, {op, dump_table, unknown, TabDef}, _WaitFor) ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ case lists:member(node(), Cs#cstruct.ram_copies) of
+ true ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ mnesia_log:ets2dcd(Tab, dmp),
+ Size = mnesia:table_info(Tab, size),
+ {true, [{op, dump_table, Size, TabDef}], optional};
+ false ->
+ mnesia:abort({has_no_disc, node()})
+ end;
+ false ->
+ {false, optional}
+ end;
+
+prepare_op(_Tid, {op, add_snmp, Ustruct, TabDef}, _WaitFor) ->
+ Cs = list2cs(TabDef),
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ {true, optional};
+ Storage ->
+ Tab = Cs#cstruct.name,
+ Stab = mnesia_snmp_hook:create_table(Ustruct, Tab, Storage),
+ mnesia_lib:set({Tab, {index, snmp}}, Stab),
+ {true, optional}
+ end;
+
+prepare_op(_Tid, {op, transform, ignore, _TabDef}, _WaitFor) ->
+ {true, mandatory}; %% Apply schema changes only.
+prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) ->
+ Cs = list2cs(TabDef),
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ {true, mandatory};
+ Storage ->
+ Tab = Cs#cstruct.name,
+ RecName = Cs#cstruct.record_name,
+ Type = Cs#cstruct.type,
+ NewArity = length(Cs#cstruct.attributes) + 1,
+ mnesia_lib:db_fixtable(Storage, Tab, true),
+ Key = mnesia_lib:db_first(Tab),
+ Op = {op, transform, Fun, TabDef},
+ case catch transform_objs(Fun, Tab, RecName,
+ Key, NewArity, Storage, Type, [Op]) of
+ {'EXIT', Reason} ->
+ mnesia_lib:db_fixtable(Storage, Tab, false),
+ exit({"Bad transform function", Tab, Fun, node(), Reason});
+ Objs ->
+ mnesia_lib:db_fixtable(Storage, Tab, false),
+ {true, Objs, mandatory}
+ end
+ end;
+
+prepare_op(_Tid, _Op, _WaitFor) ->
+ {true, optional}.
+
+
+create_ram_table(Tab, Type) ->
+ Args = [{keypos, 2}, public, named_table, Type],
+ case mnesia_monitor:unsafe_mktab(Tab, Args) of
+ Tab ->
+ ok;
+ {error,Reason} ->
+ Err = "Failed to create ets table",
+ mnesia:abort({system_limit, Tab, {Err,Reason}})
+ end.
+create_disc_table(Tab) ->
+ File = mnesia_lib:tab2dcd(Tab),
+ file:delete(File),
+ FArg = [{file, File}, {name, {mnesia,create}},
+ {repair, false}, {mode, read_write}],
+ case mnesia_monitor:open_log(FArg) of
+ {ok,Log} ->
+ mnesia_monitor:unsafe_close_log(Log),
+ ok;
+ {error,Reason} ->
+ Err = "Failed to create disc table",
+ mnesia:abort({system_limit, Tab, {Err,Reason}})
+ end.
+create_disc_only_table(Tab,Type) ->
+ File = mnesia_lib:tab2dat(Tab),
+ file:delete(File),
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Type)},
+ {keypos, 2},
+ {repair, mnesia_monitor:get_env(auto_repair)}],
+ case mnesia_monitor:unsafe_open_dets(Tab, Args) of
+ {ok, _} ->
+ ok;
+ {error,Reason} ->
+ Err = "Failed to create disc table",
+ mnesia:abort({system_limit, Tab, {Err,Reason}})
+ end.
+
+
+receive_sync([], Pids) ->
+ Pids;
+receive_sync(Nodes, Pids) ->
+ receive
+ {sync_trans, Pid} ->
+ Node = node(Pid),
+ receive_sync(lists:delete(Node, Nodes), [Pid | Pids]);
+ Else ->
+ {abort, Else}
+ end.
+
+lock_del_table(Tab, Node, Cs, Father) ->
+ Ns = val({schema, active_replicas}),
+ Lock = fun() ->
+ mnesia:write_lock_table(Tab),
+ {Res, []} = rpc:multicall(Ns, ?MODULE, set_where_to_read, [Tab, Node, Cs]),
+ Filter = fun(ok) ->
+ false;
+ ({badrpc, {'EXIT', {undef, _}}}) ->
+ %% This will be the case we talks with elder nodes
+ %% than 3.8.2, they will set where_to_read without
+ %% getting a lock.
+ false;
+ (_) ->
+ true
+ end,
+ [] = lists:filter(Filter, Res),
+ ok
+ end,
+ case mnesia:transaction(Lock) of
+ {'atomic', ok} ->
+ Father ! {self(), updated};
+ {aborted, R} ->
+ Father ! {self(), R}
+ end,
+ unlink(Father),
+ exit(normal).
+
+set_where_to_read(Tab, Node, Cs) ->
+ case mnesia_lib:val({Tab, where_to_read}) of
+ Node ->
+ case Cs#cstruct.local_content of
+ true ->
+ ok;
+ false ->
+ mnesia_lib:set_remote_where_to_read(Tab, [Node]),
+ ok
+ end;
+ _ ->
+ ok
+ end.
+
+%% Build up the list in reverse order.
+transform_objs(_Fun, _Tab, _RT, '$end_of_table', _NewArity, _Storage, _Type, Acc) ->
+ Acc;
+transform_objs(Fun, Tab, RecName, Key, A, Storage, Type, Acc) ->
+ Objs = mnesia_lib:db_get(Tab, Key),
+ NextKey = mnesia_lib:db_next_key(Tab, Key),
+ Oid = {Tab, Key},
+ NewObjs = {Ws, Ds} = transform_obj(Tab, RecName, Key, Fun, Objs, A, Type, [], []),
+ if
+ NewObjs == {[], []} ->
+ transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, Acc);
+ Type == bag ->
+ transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
+ [{op, rec, Storage, {Oid, Ws, write}},
+ {op, rec, Storage, {Oid, [Oid], delete}} | Acc]);
+ Ds == [] ->
+ %% Type is set or ordered_set, no need to delete the record first
+ transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
+ [{op, rec, Storage, {Oid, Ws, write}} | Acc]);
+ Ws == [] ->
+ transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
+ [{op, rec, Storage, {Oid, Ds, write}} | Acc]);
+ true ->
+ transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
+ [{op, rec, Storage, {Oid, Ws, write}},
+ {op, rec, Storage, {Oid, Ds, delete}} | Acc])
+ end.
+
+transform_obj(Tab, RecName, Key, Fun, [Obj|Rest], NewArity, Type, Ws, Ds) ->
+ NewObj = Fun(Obj),
+ if
+ size(NewObj) /= NewArity ->
+ exit({"Bad arity", Obj, NewObj});
+ NewObj == Obj ->
+ transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, Type, Ws, Ds);
+ RecName == element(1, NewObj), Key == element(2, NewObj) ->
+ transform_obj(Tab, RecName, Key, Fun, Rest, NewArity,
+ Type, [NewObj | Ws], Ds);
+ NewObj == delete ->
+ case Type of
+ bag -> %% Just don't write that object
+ transform_obj(Tab, RecName, Key, Fun, Rest,
+ NewArity, Type, Ws, Ds);
+ _ ->
+ transform_obj(Tab, RecName, Key, Fun, Rest, NewArity,
+ Type, Ws, [NewObj | Ds])
+ end;
+ true ->
+ exit({"Bad key or Record Name", Obj, NewObj})
+ end;
+transform_obj(_Tab, _RecName, _Key, _Fun, [], _NewArity, _Type, Ws, Ds) ->
+ {lists:reverse(Ws), lists:reverse(Ds)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Undo prepare of commit
+undo_prepare_commit(Tid, Commit) ->
+ case Commit#commit.schema_ops of
+ [] ->
+ ignore;
+ Ops ->
+ %% Catch to allow failure mnesia_controller may not be started
+ catch mnesia_controller:release_schema_commit_lock(),
+ undo_prepare_ops(Tid, Ops)
+ end,
+ Commit.
+
+%% Undo in reverse order
+undo_prepare_ops(Tid, [Op | Ops]) ->
+ case element(1, Op) of
+ TheOp when TheOp /= op, TheOp /= restore_op ->
+ undo_prepare_ops(Tid, Ops);
+ _ ->
+ undo_prepare_ops(Tid, Ops),
+ undo_prepare_op(Tid, Op)
+ end;
+undo_prepare_ops(_Tid, []) ->
+ [].
+
+undo_prepare_op(_Tid, {op, announce_im_running, _, _, Running, RemoteRunning}) ->
+ case lists:member(node(), Running) of
+ true ->
+ unannounce_im_running(RemoteRunning -- Running);
+ false ->
+ unannounce_im_running(Running -- RemoteRunning)
+ end;
+
+undo_prepare_op(_Tid, {op, sync_trans}) ->
+ ok;
+
+undo_prepare_op(Tid, {op, create_table, TabDef}) ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ mnesia_lib:unset({Tab, create_table}),
+ delete_cstruct(Tid, Cs),
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ ok;
+ ram_copies ->
+ ram_delete_table(Tab, ram_copies);
+ disc_copies ->
+ ram_delete_table(Tab, disc_copies),
+ DcdFile = mnesia_lib:tab2dcd(Tab),
+ %% disc_delete_table(Tab, Storage),
+ file:delete(DcdFile);
+ disc_only_copies ->
+ mnesia_monitor:unsafe_close_dets(Tab),
+ Dat = mnesia_lib:tab2dat(Tab),
+ %% disc_delete_table(Tab, Storage),
+ file:delete(Dat)
+ end;
+
+undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ if
+ Tab == schema ->
+ true; % Nothing to prepare
+ Node == node() ->
+ mnesia_checkpoint:tm_del_copy(Tab, Node),
+ mnesia_controller:unannounce_add_table_copy(Tab, Node),
+ if
+ Storage == disc_only_copies; Tab == schema ->
+ mnesia_monitor:close_dets(Tab),
+ file:delete(mnesia_lib:tab2dat(Tab));
+ true ->
+ file:delete(mnesia_lib:tab2dcd(Tab))
+ end,
+ ram_delete_table(Tab, Storage),
+ Cs2 = new_cs(Cs, Node, Storage, del),
+ insert_cstruct(Tid, Cs2, true); % Don't care about the version
+ Node /= node() ->
+ mnesia_controller:unannounce_add_table_copy(Tab, Node),
+ Cs2 = new_cs(Cs, Node, Storage, del),
+ insert_cstruct(Tid, Cs2, true) % Don't care about the version
+ end;
+
+undo_prepare_op(_Tid, {op, del_table_copy, _, Node, TabDef})
+ when Node == node() ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ mnesia_lib:set({Tab, where_to_read}, Node);
+
+
+undo_prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef})
+ when N == node() ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+ mnesia_checkpoint:tm_change_table_copy_type(Tab, ToS, FromS),
+ Dmp = mnesia_lib:tab2dmp(Tab),
+
+ case {FromS, ToS} of
+ {ram_copies, disc_copies} when Tab == schema ->
+ file:delete(Dmp),
+ mnesia_log:purge_some_logs(),
+ set(use_dir, false);
+ {ram_copies, disc_copies} ->
+ file:delete(Dmp);
+ {ram_copies, disc_only_copies} ->
+ file:delete(Dmp);
+ {disc_only_copies, _} ->
+ ram_delete_table(Tab, ram_copies);
+ _ ->
+ ignore
+ end;
+
+undo_prepare_op(_Tid, {op, dump_table, _Size, TabDef}) ->
+ Cs = list2cs(TabDef),
+ case lists:member(node(), Cs#cstruct.ram_copies) of
+ true ->
+ Tab = Cs#cstruct.name,
+ Dmp = mnesia_lib:tab2dmp(Tab),
+ file:delete(Dmp);
+ false ->
+ ignore
+ end;
+
+undo_prepare_op(_Tid, {op, add_snmp, _Ustruct, TabDef}) ->
+ Cs = list2cs(TabDef),
+ case mnesia_lib:cs_to_storage_type(node(), Cs) of
+ unknown ->
+ true;
+ _Storage ->
+ Tab = Cs#cstruct.name,
+ case ?catch_val({Tab, {index, snmp}}) of
+ {'EXIT',_} ->
+ ignore;
+ Stab ->
+ mnesia_snmp_hook:delete_table(Tab, Stab),
+ mnesia_lib:unset({Tab, {index, snmp}})
+ end
+ end;
+
+undo_prepare_op(_Tid, _Op) ->
+ ignore.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ram_delete_table(Tab, Storage) ->
+ case Storage of
+ unknown ->
+ ignore;
+ disc_only_copies ->
+ ignore;
+ _Else ->
+ %% delete possible index files and data .....
+ %% Got to catch this since if no info has been set in the
+ %% mnesia_gvar it will crash
+ catch mnesia_index:del_transient(Tab, Storage),
+ case ?catch_val({Tab, {index, snmp}}) of
+ {'EXIT', _} ->
+ ignore;
+ Etab ->
+ catch mnesia_snmp_hook:delete_table(Tab, Etab)
+ end,
+ catch ?ets_delete_table(Tab)
+ end.
+
+purge_dir(Dir, KeepFiles) ->
+ Suffixes = known_suffixes(),
+ purge_dir(Dir, KeepFiles, Suffixes).
+
+purge_dir(Dir, KeepFiles, Suffixes) ->
+ case dir_exists(Dir) of
+ true ->
+ {ok, AllFiles} = file:list_dir(Dir),
+ purge_known_files(AllFiles, KeepFiles, Dir, Suffixes);
+ false ->
+ ok
+ end.
+
+purge_tmp_files() ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ Dir = mnesia_lib:dir(),
+ KeepFiles = [],
+ Exists = mnesia_lib:exists(mnesia_lib:tab2dat(schema)),
+ case Exists of
+ true ->
+ Suffixes = tmp_suffixes(),
+ purge_dir(Dir, KeepFiles, Suffixes);
+ false ->
+ %% Interrupted change of storage type
+ %% for schema table
+ Suffixes = known_suffixes(),
+ purge_dir(Dir, KeepFiles, Suffixes),
+ mnesia_lib:set(use_dir, false)
+ end;
+
+ false ->
+ ok
+ end.
+
+purge_known_files([File | Tail], KeepFiles, Dir, Suffixes) ->
+ case lists:member(File, KeepFiles) of
+ true ->
+ ignore;
+ false ->
+ case has_known_suffix(File, Suffixes, false) of
+ false ->
+ ignore;
+ true ->
+ AbsFile = filename:join([Dir, File]),
+ file:delete(AbsFile)
+ end
+ end,
+ purge_known_files(Tail, KeepFiles, Dir, Suffixes);
+purge_known_files([], _KeepFiles, _Dir, _Suffixes) ->
+ ok.
+
+has_known_suffix(_File, _Suffixes, true) ->
+ true;
+has_known_suffix(File, [Suffix | Tail], false) ->
+ has_known_suffix(File, Tail, lists:suffix(Suffix, File));
+has_known_suffix(_File, [], Bool) ->
+ Bool.
+
+known_suffixes() -> real_suffixes() ++ tmp_suffixes().
+
+real_suffixes() -> [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"].
+
+tmp_suffixes() -> [".TMP", ".BUPTMP", ".RET", ".DMP"].
+
+info() ->
+ Tabs = lists:sort(val({schema, tables})),
+ lists:foreach(fun(T) -> info(T) end, Tabs),
+ ok.
+
+info(Tab) ->
+ Props = get_table_properties(Tab),
+ io:format("-- Properties for ~w table --- ~n",[Tab]),
+ info2(Tab, Props).
+info2(Tab, [{cstruct, _V} | Tail]) -> % Ignore cstruct
+ info2(Tab, Tail);
+info2(Tab, [{frag_hash, _V} | Tail]) -> % Ignore frag_hash
+ info2(Tab, Tail);
+info2(Tab, [{P, V} | Tail]) ->
+ io:format("~-20w -> ~p~n",[P,V]),
+ info2(Tab, Tail);
+info2(_, []) ->
+ io:format("~n", []).
+
+get_table_properties(Tab) ->
+ case catch mnesia_lib:db_match_object(ram_copies,
+ mnesia_gvar, {{Tab, '_'}, '_'}) of
+ {'EXIT', _} ->
+ mnesia:abort({no_exists, Tab, all});
+ RawGvar ->
+ case [{Item, Val} || {{_Tab, Item}, Val} <- RawGvar] of
+ [] ->
+ [];
+ Gvar ->
+ Size = {size, mnesia:table_info(Tab, size)},
+ Memory = {memory, mnesia:table_info(Tab, memory)},
+ Master = {master_nodes, mnesia:table_info(Tab, master_nodes)},
+ lists:sort([Size, Memory, Master | Gvar])
+ end
+ end.
+
+%%%%%%%%%%% RESTORE %%%%%%%%%%%
+
+-record(r, {iter = schema,
+ module,
+ table_options = [],
+ default_op = clear_tables,
+ tables = [],
+ opaque,
+ insert_op = error_fun,
+ recs = error_recs
+ }).
+
+restore(Opaque) ->
+ restore(Opaque, [], mnesia_monitor:get_env(backup_module)).
+restore(Opaque, Args) when list(Args) ->
+ restore(Opaque, Args, mnesia_monitor:get_env(backup_module));
+restore(_Opaque, BadArg) ->
+ {aborted, {badarg, BadArg}}.
+restore(Opaque, Args, Module) when list(Args), atom(Module) ->
+ InitR = #r{opaque = Opaque, module = Module},
+ case catch lists:foldl(fun check_restore_arg/2, InitR, Args) of
+ R when record(R, r) ->
+ case mnesia_bup:read_schema(Module, Opaque) of
+ {error, Reason} ->
+ {aborted, Reason};
+ BupSchema ->
+ schema_transaction(fun() -> do_restore(R, BupSchema) end)
+ end;
+ {'EXIT', Reason} ->
+ {aborted, Reason}
+ end;
+restore(_Opaque, Args, Module) ->
+ {aborted, {badarg, Args, Module}}.
+
+check_restore_arg({module, Mod}, R) when atom(Mod) ->
+ R#r{module = Mod};
+
+check_restore_arg({clear_tables, List}, R) when list(List) ->
+ case lists:member(schema, List) of
+ false ->
+ TableList = [{Tab, clear_tables} || Tab <- List],
+ R#r{table_options = R#r.table_options ++ TableList};
+ true ->
+ exit({badarg, {clear_tables, schema}})
+ end;
+check_restore_arg({recreate_tables, List}, R) when list(List) ->
+ case lists:member(schema, List) of
+ false ->
+ TableList = [{Tab, recreate_tables} || Tab <- List],
+ R#r{table_options = R#r.table_options ++ TableList};
+ true ->
+ exit({badarg, {recreate_tables, schema}})
+ end;
+check_restore_arg({keep_tables, List}, R) when list(List) ->
+ TableList = [{Tab, keep_tables} || Tab <- List],
+ R#r{table_options = R#r.table_options ++ TableList};
+check_restore_arg({skip_tables, List}, R) when list(List) ->
+ TableList = [{Tab, skip_tables} || Tab <- List],
+ R#r{table_options = R#r.table_options ++ TableList};
+check_restore_arg({default_op, Op}, R) ->
+ case Op of
+ clear_tables -> ok;
+ recreate_tables -> ok;
+ keep_tables -> ok;
+ skip_tables -> ok;
+ Else ->
+ exit({badarg, {bad_default_op, Else}})
+ end,
+ R#r{default_op = Op};
+
+check_restore_arg(BadArg,_) ->
+ exit({badarg, BadArg}).
+
+do_restore(R, BupSchema) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ R2 = restore_schema(BupSchema, R),
+ insert_schema_ops(TidTs, [{restore_op, R2}]),
+ [element(1, TabStruct) || TabStruct <- R2#r.tables].
+
+arrange_restore(R, Fun, Recs) ->
+ R2 = R#r{insert_op = Fun, recs = Recs},
+ case mnesia_bup:iterate(R#r.module, fun restore_items/4, R#r.opaque, R2) of
+ {ok, R3} -> R3#r.recs;
+ {error, Reason} -> mnesia:abort(Reason);
+ Reason -> mnesia:abort(Reason)
+ end.
+
+restore_items([Rec | Recs], Header, Schema, R) ->
+ Tab = element(1, Rec),
+ case lists:keysearch(Tab, 1, R#r.tables) of
+ {value, {Tab, Where, Snmp, RecName}} ->
+ {Rest, NRecs} =
+ restore_tab_items([Rec | Recs], Tab, RecName, Where, Snmp,
+ R#r.recs, R#r.insert_op),
+ restore_items(Rest, Header, Schema, R#r{recs = NRecs});
+ false ->
+ Rest = skip_tab_items(Recs, Tab),
+ restore_items(Rest, Header, Schema, R)
+ end;
+
+restore_items([], _Header, _Schema, R) ->
+ R.
+
+restore_func(Tab, R) ->
+ case lists:keysearch(Tab, 1, R#r.table_options) of
+ {value, {Tab, OP}} ->
+ OP;
+ false ->
+ R#r.default_op
+ end.
+
+where_to_commit(Tab, CsList) ->
+ Ram = [{N, ram_copies} || N <- pick(Tab, ram_copies, CsList, [])],
+ Disc = [{N, disc_copies} || N <- pick(Tab, disc_copies, CsList, [])],
+ DiscO = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])],
+ Ram ++ Disc ++ DiscO.
+
+%% Changes of the Meta info of schema itself is not allowed
+restore_schema([{schema, schema, _List} | Schema], R) ->
+ restore_schema(Schema, R);
+restore_schema([{schema, Tab, List} | Schema], R) ->
+ case restore_func(Tab, R) of
+ clear_tables ->
+ do_clear_table(Tab),
+ Where = val({Tab, where_to_commit}),
+ Snmp = val({Tab, snmp}),
+ RecName = val({Tab, record_name}),
+ R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
+ restore_schema(Schema, R2);
+ recreate_tables ->
+ TidTs = get_tid_ts_and_lock(Tab, write),
+ NC = {cookie, ?unique_cookie},
+ List2 = lists:keyreplace(cookie, 1, List, NC),
+ Where = where_to_commit(Tab, List2),
+ Snmp = pick(Tab, snmp, List2, []),
+ RecName = pick(Tab, record_name, List2, Tab),
+% case ?catch_val({Tab, cstruct}) of
+% {'EXIT', _} ->
+% ignore;
+% OldCs when record(OldCs, cstruct) ->
+% do_delete_table(Tab)
+% end,
+% unsafe_do_create_table(list2cs(List2)),
+ insert_schema_ops(TidTs, [{op, restore_recreate, List2}]),
+ R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
+ restore_schema(Schema, R2);
+ keep_tables ->
+ get_tid_ts_and_lock(Tab, write),
+ Where = val({Tab, where_to_commit}),
+ Snmp = val({Tab, snmp}),
+ RecName = val({Tab, record_name}),
+ R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
+ restore_schema(Schema, R2);
+ skip_tables ->
+ restore_schema(Schema, R)
+ end;
+
+restore_schema([{schema, Tab} | Schema], R) ->
+ do_delete_table(Tab),
+ Tabs = lists:delete(Tab,R#r.tables),
+ restore_schema(Schema, R#r{tables = Tabs});
+restore_schema([], R) ->
+ R.
+
+restore_tab_items([Rec | Rest], Tab, RecName, Where, Snmp, Recs, Op)
+ when element(1, Rec) == Tab ->
+ NewRecs = Op(Rec, Recs, RecName, Where, Snmp),
+ restore_tab_items(Rest, Tab, RecName, Where, Snmp, NewRecs, Op);
+
+restore_tab_items(Rest, _Tab, _RecName, _Where, _Snmp, Recs, _Op) ->
+ {Rest, Recs}.
+
+skip_tab_items([Rec| Rest], Tab)
+ when element(1, Rec) == Tab ->
+ skip_tab_items(Rest, Tab);
+skip_tab_items(Recs, _) ->
+ Recs.
+
+%%%%%%%%% Dump tables %%%%%%%%%%%%%
+dump_tables(Tabs) when list(Tabs) ->
+ schema_transaction(fun() -> do_dump_tables(Tabs) end);
+dump_tables(Tabs) ->
+ {aborted, {bad_type, Tabs}}.
+
+do_dump_tables(Tabs) ->
+ TidTs = get_tid_ts_and_lock(schema, write),
+ insert_schema_ops(TidTs, make_dump_tables(Tabs)).
+
+make_dump_tables([schema | _Tabs]) ->
+ mnesia:abort({bad_type, schema});
+make_dump_tables([Tab | Tabs]) ->
+ get_tid_ts_and_lock(Tab, read),
+ TabDef = get_create_list(Tab),
+ DiscResident = val({Tab, disc_copies}) ++ val({Tab, disc_only_copies}),
+ verify([], DiscResident,
+ {"Only allowed on ram_copies", Tab, DiscResident}),
+ [{op, dump_table, unknown, TabDef} | make_dump_tables(Tabs)];
+make_dump_tables([]) ->
+ [].
+
+%% Merge the local schema with the schema on other nodes
+merge_schema() ->
+ schema_transaction(fun() -> do_merge_schema() end).
+
+do_merge_schema() ->
+ {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
+ Connected = val(recover_nodes),
+ Running = val({current, db_nodes}),
+ Store = Ts#tidstore.store,
+ case Connected -- Running of
+ [Node | _] ->
+ %% Time for a schema merging party!
+ mnesia_locker:wlock_no_exist(Tid, Store, schema, [Node]),
+
+ case rpc:call(Node, mnesia_controller, get_cstructs, []) of
+ {cstructs, Cstructs, RemoteRunning1} ->
+ LockedAlready = Running ++ [Node],
+ {New, Old} = mnesia_recover:connect_nodes(RemoteRunning1),
+ RemoteRunning = mnesia_lib:intersect(New ++ Old, RemoteRunning1),
+ if
+ RemoteRunning /= RemoteRunning1 ->
+ mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n",
+ [node(), RemoteRunning1 -- RemoteRunning]);
+ true -> ok
+ end,
+ NeedsLock = RemoteRunning -- LockedAlready,
+ mnesia_locker:wlock_no_exist(Tid, Store, schema, NeedsLock),
+
+ {value, SchemaCs} =
+ lists:keysearch(schema, #cstruct.name, Cstructs),
+
+ %% Announce that Node is running
+ A = [{op, announce_im_running, node(),
+ cs2list(SchemaCs), Running, RemoteRunning}],
+ do_insert_schema_ops(Store, A),
+
+ %% Introduce remote tables to local node
+ do_insert_schema_ops(Store, make_merge_schema(Node, Cstructs)),
+
+ %% Introduce local tables to remote nodes
+ Tabs = val({schema, tables}),
+ Ops = [{op, merge_schema, get_create_list(T)}
+ || T <- Tabs,
+ not lists:keymember(T, #cstruct.name, Cstructs)],
+ do_insert_schema_ops(Store, Ops),
+
+ %% Ensure that the txn will be committed on all nodes
+ announce_im_running(RemoteRunning, SchemaCs),
+ {merged, Running, RemoteRunning};
+ {error, Reason} ->
+ {"Cannot get cstructs", Node, Reason};
+ {badrpc, Reason} ->
+ {"Cannot get cstructs", Node, {badrpc, Reason}}
+ end;
+ [] ->
+ %% No more nodes to merge schema with
+ not_merged
+ end.
+
+make_merge_schema(Node, [Cs | Cstructs]) ->
+ Ops = do_make_merge_schema(Node, Cs),
+ Ops ++ make_merge_schema(Node, Cstructs);
+make_merge_schema(_Node, []) ->
+ [].
+
+%% Merge definitions of schema table
+do_make_merge_schema(Node, RemoteCs)
+ when RemoteCs#cstruct.name == schema ->
+ Cs = val({schema, cstruct}),
+ Masters = mnesia_recover:get_master_nodes(schema),
+ HasRemoteMaster = lists:member(Node, Masters),
+ HasLocalMaster = lists:member(node(), Masters),
+ Force = HasLocalMaster or HasRemoteMaster,
+ %% What is the storage types opinions?
+ StCsLocal = mnesia_lib:cs_to_storage_type(node(), Cs),
+ StRcsLocal = mnesia_lib:cs_to_storage_type(node(), RemoteCs),
+ StCsRemote = mnesia_lib:cs_to_storage_type(Node, Cs),
+ StRcsRemote = mnesia_lib:cs_to_storage_type(Node, RemoteCs),
+
+ if
+ Cs#cstruct.cookie == RemoteCs#cstruct.cookie,
+ Cs#cstruct.version == RemoteCs#cstruct.version ->
+ %% Great, we have the same cookie and version
+ %% and do not need to merge cstructs
+ [];
+
+ Cs#cstruct.cookie /= RemoteCs#cstruct.cookie,
+ Cs#cstruct.disc_copies /= [],
+ RemoteCs#cstruct.disc_copies /= [] ->
+ %% Both cstructs involves disc nodes
+ %% and we cannot merge them
+ if
+ HasLocalMaster == true,
+ HasRemoteMaster == false ->
+ %% Choose local cstruct,
+ %% since it's the master
+ [{op, merge_schema, cs2list(Cs)}];
+
+ HasRemoteMaster == true,
+ HasLocalMaster == false ->
+ %% Choose remote cstruct,
+ %% since it's the master
+ [{op, merge_schema, cs2list(RemoteCs)}];
+
+ true ->
+ Str = io_lib:format("Incompatible schema cookies. "
+ "Please, restart from old backup."
+ "~w = ~w, ~w = ~w~n",
+ [Node, cs2list(RemoteCs), node(), cs2list(Cs)]),
+ throw(Str)
+ end;
+
+ StCsLocal /= StRcsLocal, StRcsLocal /= unknown ->
+ Str = io_lib:format("Incompatible schema storage types. "
+ "on ~w storage ~w, on ~w storage ~w~n",
+ [node(), StCsLocal, Node, StRcsLocal]),
+ throw(Str);
+ StCsRemote /= StRcsRemote, StCsRemote /= unknown ->
+ Str = io_lib:format("Incompatible schema storage types. "
+ "on ~w storage ~w, on ~w storage ~w~n",
+ [node(), StCsRemote, Node, StRcsRemote]),
+ throw(Str);
+
+ Cs#cstruct.disc_copies /= [] ->
+ %% Choose local cstruct,
+ %% since it involves disc nodes
+ MergedCs = merge_cstructs(Cs, RemoteCs, Force),
+ [{op, merge_schema, cs2list(MergedCs)}];
+
+ RemoteCs#cstruct.disc_copies /= [] ->
+ %% Choose remote cstruct,
+ %% since it involves disc nodes
+ MergedCs = merge_cstructs(RemoteCs, Cs, Force),
+ [{op, merge_schema, cs2list(MergedCs)}];
+
+ Cs > RemoteCs ->
+ %% Choose remote cstruct
+ MergedCs = merge_cstructs(RemoteCs, Cs, Force),
+ [{op, merge_schema, cs2list(MergedCs)}];
+
+ true ->
+ %% Choose local cstruct
+ MergedCs = merge_cstructs(Cs, RemoteCs, Force),
+ [{op, merge_schema, cs2list(MergedCs)}]
+ end;
+
+%% Merge definitions of normal table
+do_make_merge_schema(Node, RemoteCs) ->
+ Tab = RemoteCs#cstruct.name,
+ Masters = mnesia_recover:get_master_nodes(schema),
+ HasRemoteMaster = lists:member(Node, Masters),
+ HasLocalMaster = lists:member(node(), Masters),
+ Force = HasLocalMaster or HasRemoteMaster,
+ case ?catch_val({Tab, cstruct}) of
+ {'EXIT', _} ->
+ %% A completely new table, created while Node was down
+ [{op, merge_schema, cs2list(RemoteCs)}];
+ Cs when Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
+ if
+ Cs#cstruct.version == RemoteCs#cstruct.version ->
+ %% We have exactly the same version of the
+ %% table def
+ [];
+
+ Cs#cstruct.version > RemoteCs#cstruct.version ->
+ %% Oops, we have different versions
+ %% of the table def, lets merge them.
+ %% The only changes that may have occurred
+ %% is that new replicas may have been added.
+ MergedCs = merge_cstructs(Cs, RemoteCs, Force),
+ [{op, merge_schema, cs2list(MergedCs)}];
+
+ Cs#cstruct.version < RemoteCs#cstruct.version ->
+ %% Oops, we have different versions
+ %% of the table def, lets merge them
+ MergedCs = merge_cstructs(RemoteCs, Cs, Force),
+ [{op, merge_schema, cs2list(MergedCs)}]
+ end;
+ Cs ->
+ %% Different cookies, not possible to merge
+ if
+ HasLocalMaster == true,
+ HasRemoteMaster == false ->
+ %% Choose local cstruct,
+ %% since it's the master
+ [{op, merge_schema, cs2list(Cs)}];
+
+ HasRemoteMaster == true,
+ HasLocalMaster == false ->
+ %% Choose remote cstruct,
+ %% since it's the master
+ [{op, merge_schema, cs2list(RemoteCs)}];
+
+ true ->
+ Str = io_lib:format("Bad cookie in table definition"
+ " ~w: ~w = ~w, ~w = ~w~n",
+ [Tab, node(), Cs, Node, RemoteCs]),
+ throw(Str)
+ end
+ end.
+
+%% Change of table definitions (cstructs) requires all replicas
+%% of the table to be active. New replicas, db_nodes and tables
+%% may however be added even if some replica is inactive. These
+%% invariants must be enforced in order to allow merge of cstructs.
+%%
+%% Returns a new cstruct or issues a fatal error
+merge_cstructs(Cs, RemoteCs, Force) ->
+ verify_cstruct(Cs),
+ case catch do_merge_cstructs(Cs, RemoteCs, Force) of
+ {'EXIT', {aborted, _Reason}} when Force == true ->
+ Cs;
+ {'EXIT', Reason} ->
+ exit(Reason);
+ MergedCs when record(MergedCs, cstruct) ->
+ MergedCs;
+ Other ->
+ throw(Other)
+ end.
+
+do_merge_cstructs(Cs, RemoteCs, Force) ->
+ verify_cstruct(RemoteCs),
+ Ns = mnesia_lib:uniq(mnesia_lib:cs_to_nodes(Cs) ++
+ mnesia_lib:cs_to_nodes(RemoteCs)),
+ {AnythingNew, MergedCs} =
+ merge_storage_type(Ns, false, Cs, RemoteCs, Force),
+ MergedCs2 = merge_versions(AnythingNew, MergedCs, RemoteCs, Force),
+ verify_cstruct(MergedCs2),
+ MergedCs2.
+
+merge_storage_type([N | Ns], AnythingNew, Cs, RemoteCs, Force) ->
+ Local = mnesia_lib:cs_to_storage_type(N, Cs),
+ Remote = mnesia_lib:cs_to_storage_type(N, RemoteCs),
+ case compare_storage_type(true, Local, Remote) of
+ {same, _Storage} ->
+ merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
+ {diff, Storage} ->
+ Cs2 = change_storage_type(N, Storage, Cs),
+ merge_storage_type(Ns, true, Cs2, RemoteCs, Force);
+ incompatible when Force == true ->
+ merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
+ Other ->
+ Str = io_lib:format("Cannot merge storage type for node ~w "
+ "in cstruct ~w with remote cstruct ~w (~w)~n",
+ [N, Cs, RemoteCs, Other]),
+ throw(Str)
+ end;
+merge_storage_type([], AnythingNew, MergedCs, _RemoteCs, _Force) ->
+ {AnythingNew, MergedCs}.
+
+compare_storage_type(_Retry, Any, Any) ->
+ {same, Any};
+compare_storage_type(_Retry, unknown, Any) ->
+ {diff, Any};
+compare_storage_type(_Retry, ram_copies, disc_copies) ->
+ {diff, disc_copies};
+compare_storage_type(_Retry, disc_copies, disc_only_copies) ->
+ {diff, disc_only_copies};
+compare_storage_type(true, One, Another) ->
+ compare_storage_type(false, Another, One);
+compare_storage_type(false, _One, _Another) ->
+ incompatible.
+
+change_storage_type(N, ram_copies, Cs) ->
+ Nodes = [N | Cs#cstruct.ram_copies],
+ Cs#cstruct{ram_copies = mnesia_lib:uniq(Nodes)};
+change_storage_type(N, disc_copies, Cs) ->
+ Nodes = [N | Cs#cstruct.disc_copies],
+ Cs#cstruct{disc_copies = mnesia_lib:uniq(Nodes)};
+change_storage_type(N, disc_only_copies, Cs) ->
+ Nodes = [N | Cs#cstruct.disc_only_copies],
+ Cs#cstruct{disc_only_copies = mnesia_lib:uniq(Nodes)}.
+
+%% BUGBUG: Verify match of frag info; equalit demanded for all but add_node
+
+merge_versions(AnythingNew, Cs, RemoteCs, Force) ->
+ if
+ Cs#cstruct.name == schema ->
+ ok;
+ Cs#cstruct.name /= schema,
+ Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
+ ok;
+ Force == true ->
+ ok;
+ true ->
+ Str = io_lib:format("Bad cookies. Cannot merge definitions of "
+ "table ~w. Local = ~w, Remote = ~w~n",
+ [Cs#cstruct.name, Cs, RemoteCs]),
+ throw(Str)
+ end,
+ if
+ Cs#cstruct.name == RemoteCs#cstruct.name,
+ Cs#cstruct.type == RemoteCs#cstruct.type,
+ Cs#cstruct.local_content == RemoteCs#cstruct.local_content,
+ Cs#cstruct.attributes == RemoteCs#cstruct.attributes,
+ Cs#cstruct.index == RemoteCs#cstruct.index,
+ Cs#cstruct.snmp == RemoteCs#cstruct.snmp,
+ Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode,
+ Cs#cstruct.load_order == RemoteCs#cstruct.load_order,
+ Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties ->
+ do_merge_versions(AnythingNew, Cs, RemoteCs);
+ Force == true ->
+ do_merge_versions(AnythingNew, Cs, RemoteCs);
+ true ->
+ Str1 = io_lib:format("Cannot merge definitions of "
+ "table ~w. Local = ~w, Remote = ~w~n",
+ [Cs#cstruct.name, Cs, RemoteCs]),
+ throw(Str1)
+ end.
+
+do_merge_versions(AnythingNew, MergedCs, RemoteCs) ->
+ {{Major1, Minor1}, _Detail1} = MergedCs#cstruct.version,
+ {{Major2, Minor2}, _Detail2} = RemoteCs#cstruct.version,
+ if
+ MergedCs#cstruct.version == RemoteCs#cstruct.version ->
+ MergedCs;
+ AnythingNew == false ->
+ MergedCs;
+ Major1 == Major2 ->
+ Minor = lists:max([Minor1, Minor2]),
+ V = {{Major1, Minor}, dummy},
+ incr_version(MergedCs#cstruct{version = V});
+ Major1 /= Major2 ->
+ Major = lists:max([Major1, Major2]),
+ V = {{Major, 0}, dummy},
+ incr_version(MergedCs#cstruct{version = V})
+ end.
+
+announce_im_running([N | Ns], SchemaCs) ->
+ {L1, L2} = mnesia_recover:connect_nodes([N]),
+ case lists:member(N, L1) or lists:member(N, L2) of
+ true ->
+%% dbg_out("Adding ~p to {current db_nodes} ~n", [N]), %% qqqq
+ mnesia_lib:add({current, db_nodes}, N),
+ mnesia_controller:add_active_replica(schema, N, SchemaCs);
+ false ->
+ ignore
+ end,
+ announce_im_running(Ns, SchemaCs);
+announce_im_running([], _) ->
+ [].
+
+unannounce_im_running([N | Ns]) ->
+ mnesia_lib:del({current, db_nodes}, N),
+ mnesia_controller:del_active_replica(schema, N),
+ mnesia_recover:disconnect(N),
+ unannounce_im_running(Ns);
+unannounce_im_running([]) ->
+ [].
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_hook.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_hook.erl
new file mode 100644
index 0000000000..458323c0e4
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_hook.erl
@@ -0,0 +1,271 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_snmp_hook.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
+%%
+-module(mnesia_snmp_hook).
+
+%% Hooks (called from mnesia)
+-export([check_ustruct/1, create_table/3, delete_table/2,
+ key_to_oid/3, update/1, start/2,
+ get_row/2, get_next_index/2, get_mnesia_key/2]).
+
+%% sys callback functions
+-export([system_continue/3,
+ system_terminate/4,
+ system_code_change/4
+ ]).
+
+%% Internal exports
+-export([b_init/2]).
+
+check_ustruct([]) ->
+ true; %% default value, not SNMP'ified
+check_ustruct([{key, Types}]) ->
+ is_snmp_type(to_list(Types));
+check_ustruct(_) -> false.
+
+to_list(Tuple) when tuple(Tuple) -> tuple_to_list(Tuple);
+to_list(X) -> [X].
+
+is_snmp_type([integer | T]) -> is_snmp_type(T);
+is_snmp_type([string | T]) -> is_snmp_type(T);
+is_snmp_type([fix_string | T]) -> is_snmp_type(T);
+is_snmp_type([]) -> true;
+is_snmp_type(_) -> false.
+
+create_table([], MnesiaTab, _Storage) ->
+ mnesia:abort({badarg, MnesiaTab, {snmp, empty_snmpstruct}});
+
+create_table([{key, Us}], MnesiaTab, Storage) ->
+ Tree = b_new(MnesiaTab, Us),
+ mnesia_lib:db_fixtable(Storage, MnesiaTab, true),
+ First = mnesia_lib:db_first(Storage, MnesiaTab),
+ build_table(First, MnesiaTab, Tree, Us, Storage),
+ mnesia_lib:db_fixtable(Storage, MnesiaTab, false),
+ Tree.
+
+build_table(MnesiaKey, MnesiaTab, Tree, Us, Storage)
+ when MnesiaKey /= '$end_of_table' ->
+%% SnmpKey = key_to_oid(MnesiaTab, MnesiaKey, Us),
+%% update(write, Tree, MnesiaKey, SnmpKey),
+ update(write, Tree, MnesiaKey, MnesiaKey),
+ Next = mnesia_lib:db_next_key(Storage, MnesiaTab, MnesiaKey),
+ build_table(Next, MnesiaTab, Tree, Us, Storage);
+build_table('$end_of_table', _MnesiaTab, _Tree, _Us, _Storage) ->
+ ok.
+
+delete_table(_MnesiaTab, Tree) ->
+ exit(Tree, shutdown),
+ ok.
+
+%%-----------------------------------------------------------------
+%% update({Op, MnesiaTab, MnesiaKey, SnmpKey})
+%%-----------------------------------------------------------------
+
+update({clear_table, MnesiaTab}) ->
+ Tree = mnesia_lib:val({MnesiaTab, {index, snmp}}),
+ b_clear(Tree);
+
+update({Op, MnesiaTab, MnesiaKey, SnmpKey}) ->
+ Tree = mnesia_lib:val({MnesiaTab, {index, snmp}}),
+ update(Op, Tree, MnesiaKey, SnmpKey).
+
+update(Op, Tree, MnesiaKey, _) ->
+ case Op of
+ write ->
+ b_insert(Tree, MnesiaKey, MnesiaKey);
+ update_counter ->
+ ignore;
+ delete ->
+ b_delete(Tree, MnesiaKey);
+ delete_object ->
+ b_delete(Tree, MnesiaKey)
+ end,
+ ok.
+
+%%-----------------------------------------------------------------
+%% Func: key_to_oid(Tab, Key, Ustruct)
+%% Args: Key ::= key()
+%% key() ::= int() | string() | {int() | string()}
+%% Type ::= {fix_string | term()}
+%% Make an OBJECT IDENTIFIER out of it.
+%% Variable length objects are prepended by their length.
+%% Ex. Key = {"pelle", 42} AND Type = {string, integer} =>
+%% OID [5, $p, $e, $l, $l, $e, 42]
+%% Key = {"pelle", 42} AND Type = {fix_string, integer} =>
+%% OID [$p, $e, $l, $l, $e, 42]
+%%-----------------------------------------------------------------
+key_to_oid(Tab, Key, [{key, Types}]) ->
+ MnesiaOid = {Tab, Key},
+ if
+ tuple(Key), tuple(Types) ->
+ case {size(Key), size(Types)} of
+ {Size, Size} ->
+ keys_to_oid(MnesiaOid, Size, Key, [], Types);
+ _ ->
+ exit({bad_snmp_key, MnesiaOid})
+ end;
+ true ->
+ key_to_oid_i(MnesiaOid, Key, Types)
+ end.
+
+key_to_oid_i(_MnesiaOid, Key, integer) when integer(Key) -> [Key];
+key_to_oid_i(_MnesiaOid, Key, fix_string) when list(Key) -> Key;
+key_to_oid_i(_MnesiaOid, Key, string) when list(Key) -> [length(Key) | Key];
+key_to_oid_i(MnesiaOid, Key, Type) ->
+ exit({bad_snmp_key, [MnesiaOid, Key, Type]}).
+
+keys_to_oid(_MnesiaOid, 0, _Key, Oid, _Types) -> Oid;
+keys_to_oid(MnesiaOid, N, Key, Oid, Types) ->
+ Type = element(N, Types),
+ KeyPart = element(N, Key),
+ Oid2 = key_to_oid_i(MnesiaOid, KeyPart, Type) ++ Oid,
+ keys_to_oid(MnesiaOid, N-1, Key, Oid2, Types).
+
+%%-----------------------------------------------------------------
+%% Func: get_row/2
+%% Args: Name is the name of the table (atom)
+%% RowIndex is an Oid
+%% Returns: {ok, Row} | undefined
+%% Note that the Row returned might contain columns that
+%% are not visible via SNMP. e.g. the first column may be
+%% ifIndex, and the last MFA ({ifIndex, col1, col2, MFA}).
+%% where ifIndex is used only as index (not as a real col),
+%% and MFA as extra info, used by the application.
+%%-----------------------------------------------------------------
+get_row(Name, RowIndex) ->
+ Tree = mnesia_lib:val({Name, {index, snmp}}),
+ case b_lookup(Tree, RowIndex) of
+ {ok, {_RowIndex, Key}} ->
+ [Row] = mnesia:dirty_read({Name, Key}),
+ {ok, Row};
+ _ ->
+ undefined
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: get_next_index/2
+%% Args: Name is the name of the table (atom)
+%% RowIndex is an Oid
+%% Returns: {ok, NextIndex} | endOfTable
+%%-----------------------------------------------------------------
+get_next_index(Name, RowIndex) ->
+ Tree = mnesia_lib:val({Name, {index, snmp}}),
+ case b_lookup_next(Tree, RowIndex) of
+ {ok, {NextIndex, _Key}} ->
+ {ok, NextIndex};
+ _ ->
+ endOfTable
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: get_mnesia_key/2
+%% Purpose: Get the mnesia key corresponding to the RowIndex.
+%% Args: Name is the name of the table (atom)
+%% RowIndex is an Oid
+%% Returns: {ok, Key} | undefiend
+%%-----------------------------------------------------------------
+get_mnesia_key(Name, RowIndex) ->
+ Tree = mnesia_lib:val({Name, {index, snmp}}),
+ case b_lookup(Tree, RowIndex) of
+ {ok, {_RowIndex, Key}} ->
+ {ok, Key};
+ _ ->
+ undefined
+ end.
+
+%%-----------------------------------------------------------------
+%% Encapsulate a bplus_tree in a process.
+%%-----------------------------------------------------------------
+
+b_new(MnesiaTab, Us) ->
+ case supervisor:start_child(mnesia_snmp_sup, [MnesiaTab, Us]) of
+ {ok, Tree} ->
+ Tree;
+ {error, Reason} ->
+ exit({badsnmp, MnesiaTab, Reason})
+ end.
+
+start(MnesiaTab, Us) ->
+ Name = {mnesia_snmp, MnesiaTab},
+ mnesia_monitor:start_proc(Name, ?MODULE, b_init, [self(), Us]).
+
+b_insert(Tree, Key, Val) -> Tree ! {insert, Key, Val}.
+b_delete(Tree, Key) -> Tree ! {delete, Key}.
+b_lookup(Tree, Key) ->
+ Tree ! {lookup, self(), Key},
+ receive
+ {bplus_res, Res} ->
+ Res
+ end.
+b_lookup_next(Tree, Key) ->
+ Tree ! {lookup_next, self(), Key},
+ receive
+ {bplus_res, Res} ->
+ Res
+ end.
+
+b_clear(Tree) ->
+ Tree ! clear,
+ ok.
+
+b_init(Parent, Us) ->
+ %% Do not trap exit
+ Tree = snmp_index:new(Us),
+ proc_lib:init_ack(Parent, {ok, self()}),
+ b_loop(Parent, Tree, Us).
+
+b_loop(Parent, Tree, Us) ->
+ receive
+ {insert, Key, Val} ->
+ NTree = snmp_index:insert(Tree, Key, Val),
+ b_loop(Parent, NTree, Us);
+ {delete, Key} ->
+ NTree = snmp_index:delete(Tree, Key),
+ b_loop(Parent, NTree, Us);
+ {lookup, From, Key} ->
+ Res = snmp_index:get(Tree, Key),
+ From ! {bplus_res, Res},
+ b_loop(Parent, Tree, Us);
+ {lookup_next, From, Key} ->
+ Res = snmp_index:get_next(Tree, Key),
+ From ! {bplus_res, Res},
+ b_loop(Parent, Tree, Us);
+ clear ->
+ catch snmp_index:delete(Tree), %% Catch because delete/1 is not
+ NewTree = snmp_index:new(Us), %% available in old snmp (before R5)
+ b_loop(Parent, NewTree, Us);
+
+ {'EXIT', Parent, Reason} ->
+ exit(Reason);
+
+ {system, From, Msg} ->
+ mnesia_lib:dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ sys:handle_system_msg(Msg, From, Parent, ?MODULE, [], {Tree, Us})
+
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% System upgrade
+
+system_continue(Parent, _Debug, {Tree, Us}) ->
+ b_loop(Parent, Tree, Us).
+
+system_terminate(Reason, _Parent, _Debug, _Tree) ->
+ exit(Reason).
+
+system_code_change(State, _Module, _OldVsn, _Extra) ->
+ {ok, State}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_sup.erl
new file mode 100644
index 0000000000..1cbac23e9d
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_snmp_sup.erl
@@ -0,0 +1,39 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_snmp_sup.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
+%%
+-module(mnesia_snmp_sup).
+
+-behaviour(supervisor).
+
+-export([start/0, init/1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% top supervisor callback functions
+
+start() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% sub supervisor callback functions
+
+init([]) ->
+ Flags = {simple_one_for_one, 0, timer:hours(24)}, % Trust the top supervisor
+ MFA = {mnesia_snmp_hook, start, []},
+ Modules = [?MODULE, mnesia_snmp_hook, supervisor],
+ KillAfter = mnesia_kernel_sup:supervisor_timeout(timer:seconds(3)),
+ Workers = [{?MODULE, MFA, transient, KillAfter, worker, Modules}],
+ {ok, {Flags, Workers}}.
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sp.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sp.erl
new file mode 100644
index 0000000000..ad29d3cc78
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sp.erl
@@ -0,0 +1,39 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_sp.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
+%%
+
+%% To able to generate nice crash reports we need a catch on the highest level.
+%% This code can't be purged so a code change is not possible.
+%% And hence this a simple module.
+
+-module(mnesia_sp).
+
+-export([init_proc/4]).
+
+init_proc(Who, Mod, Fun, Args) ->
+ mnesia_lib:verbose("~p starting: ~p~n", [Who, self()]),
+ case catch apply(Mod, Fun, Args) of
+ {'EXIT', Reason} ->
+ mnesia_monitor:terminate_proc(Who, Reason, Args),
+ exit(Reason);
+ Other ->
+ Other
+ end.
+
+
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_subscr.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_subscr.erl
new file mode 100644
index 0000000000..f077291bc6
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_subscr.erl
@@ -0,0 +1,492 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_subscr.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
+%%
+-module(mnesia_subscr).
+
+-behaviour(gen_server).
+
+-export([start/0,
+ set_debug_level/1,
+ subscribe/2,
+ unsubscribe/2,
+ unsubscribe_table/1,
+ subscribers/0,
+ report_table_event/4,
+ report_table_event/5,
+ report_table_event/6
+ ]).
+
+%% gen_server callbacks
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3
+ ]).
+
+-include("mnesia.hrl").
+
+-import(mnesia_lib, [error/2]).
+-record(state, {supervisor, pid_tab}).
+
+start() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [self()],
+ [{timeout, infinity}]).
+
+set_debug_level(Level) ->
+ OldEnv = application:get_env(mnesia, debug),
+ case mnesia_monitor:patch_env(debug, Level) of
+ {error, Reason} ->
+ {error, Reason};
+ NewLevel ->
+ set_debug_level(NewLevel, OldEnv)
+ end.
+
+set_debug_level(Level, OldEnv) ->
+ case mnesia:system_info(is_running) of
+ no when OldEnv == undefined ->
+ none;
+ no ->
+ {ok, E} = OldEnv,
+ E;
+ _ ->
+ Old = mnesia_lib:val(debug),
+ Local = mnesia:system_info(local_tables),
+ E = whereis(mnesia_event),
+ Sub = fun(Tab) -> subscribe(E, {table, Tab}) end,
+ UnSub = fun(Tab) -> unsubscribe(E, {table, Tab}) end,
+
+ case Level of
+ none ->
+ lists:foreach(UnSub, Local);
+ verbose ->
+ lists:foreach(UnSub, Local);
+ debug ->
+ lists:foreach(UnSub, Local -- [schema]),
+ Sub(schema);
+ trace ->
+ lists:foreach(Sub, Local)
+ end,
+ mnesia_lib:set(debug, Level),
+ Old
+ end.
+
+subscribe(ClientPid, system) ->
+ change_subscr(activate, ClientPid, system);
+subscribe(ClientPid, {table, Tab}) ->
+ change_subscr(activate, ClientPid, {table, Tab, simple});
+subscribe(ClientPid, {table, Tab, simple}) ->
+ change_subscr(activate, ClientPid, {table, Tab, simple});
+subscribe(ClientPid, {table, Tab, detailed}) ->
+ change_subscr(activate, ClientPid, {table, Tab, detailed});
+subscribe(_ClientPid, What) ->
+ {error, {badarg, What}}.
+
+unsubscribe(ClientPid, system) ->
+ change_subscr(deactivate, ClientPid, system);
+unsubscribe(ClientPid, {table, Tab}) ->
+ change_subscr(deactivate, ClientPid, {table, Tab, simple});
+unsubscribe(ClientPid, {table, Tab, simple}) ->
+ change_subscr(deactivate, ClientPid, {table, Tab, simple});
+unsubscribe(ClientPid, {table, Tab, detailed}) ->
+ change_subscr(deactivate, ClientPid, {table, Tab, detailed});
+unsubscribe(_ClientPid, What) ->
+ {error, {badarg, What}}.
+
+unsubscribe_table(Tab) ->
+ call({change, {deactivate_table, Tab}}).
+
+change_subscr(Kind, ClientPid, What) ->
+ call({change, {Kind, ClientPid, What}}).
+
+subscribers() ->
+ [whereis(mnesia_event) | mnesia_lib:val(subscribers)].
+
+report_table_event(Tab, Tid, Obj, Op) ->
+ case ?catch_val({Tab, commit_work}) of
+ {'EXIT', _} -> ok;
+ Commit ->
+ case lists:keysearch(subscribers, 1, Commit) of
+ false -> ok;
+ {value, Subs} ->
+ report_table_event(Subs, Tab, Tid, Obj, Op, undefined)
+ end
+ end.
+
+%% Backwards compatible for the moment when mnesia_tm get's updated!
+report_table_event(Subscr, Tab, Tid, Obj, Op) ->
+ report_table_event(Subscr, Tab, Tid, Obj, Op, undefined).
+
+report_table_event({subscribers, S1, S2}, Tab, Tid, _Obj, clear_table, _Old) ->
+ What = {delete, {schema, Tab}, Tid},
+ deliver(S1, {mnesia_table_event, What}),
+ TabDef = mnesia_schema:cs2list(?catch_val({Tab, cstruct})),
+ What2 = {write, {schema, Tab, TabDef}, Tid},
+ deliver(S1, {mnesia_table_event, What2}),
+ What3 = {delete, schema, {schema, Tab}, [{schema, Tab, TabDef}], Tid},
+ deliver(S2, {mnesia_table_event, What3}),
+ What4 = {write, schema, {schema, Tab, TabDef}, [], Tid},
+ deliver(S2, {mnesia_table_event, What4});
+
+report_table_event({subscribers, Subscr, []}, Tab, Tid, Obj, Op, _Old) ->
+ What = {Op, patch_record(Tab, Obj), Tid},
+ deliver(Subscr, {mnesia_table_event, What});
+
+report_table_event({subscribers, S1, S2}, Tab, Tid, Obj, Op, Old) ->
+ Standard = {Op, patch_record(Tab, Obj), Tid},
+ deliver(S1, {mnesia_table_event, Standard}),
+ Extended = what(Tab, Tid, Obj, Op, Old),
+ deliver(S2, Extended);
+
+%% Backwards compatible for the moment when mnesia_tm get's updated!
+report_table_event({subscribers, Subscr}, Tab, Tid, Obj, Op, Old) ->
+ report_table_event({subscribers, Subscr, []}, Tab, Tid, Obj, Op, Old).
+
+
+patch_record(Tab, Obj) ->
+ case Tab == element(1, Obj) of
+ true ->
+ Obj;
+ false ->
+ setelement(1, Obj, Tab)
+ end.
+
+what(Tab, Tid, {RecName, Key}, delete, undefined) ->
+ case catch mnesia_lib:db_get(Tab, Key) of
+ Old when list(Old) -> %% Op only allowed for set table.
+ {mnesia_table_event, {delete, Tab, {RecName, Key}, Old, Tid}};
+ _ ->
+ %% Record just deleted by a dirty_op or
+ %% the whole table has been deleted
+ ignore
+ end;
+what(Tab, Tid, Obj, delete, Old) ->
+ {mnesia_table_event, {delete, Tab, Obj, Old, Tid}};
+what(Tab, Tid, Obj, delete_object, _Old) ->
+ {mnesia_table_event, {delete, Tab, Obj, [Obj], Tid}};
+what(Tab, Tid, Obj, write, undefined) ->
+ case catch mnesia_lib:db_get(Tab, element(2, Obj)) of
+ Old when list(Old) ->
+ {mnesia_table_event, {write, Tab, Obj, Old, Tid}};
+ {'EXIT', _} ->
+ ignore
+ end.
+
+deliver(_, ignore) ->
+ ok;
+deliver([Pid | Pids], Msg) ->
+ Pid ! Msg,
+ deliver(Pids, Msg);
+deliver([], _Msg) ->
+ ok.
+
+call(Msg) ->
+ Pid = whereis(?MODULE),
+ case Pid of
+ undefined ->
+ {error, {node_not_running, node()}};
+ Pid ->
+ Res = gen_server:call(Pid, Msg, infinity),
+ %% We get an exit signal if server dies
+ receive
+ {'EXIT', _Pid, _Reason} ->
+ {error, {node_not_running, node()}}
+ after 0 ->
+ ignore
+ end,
+ Res
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Callback functions from gen_server
+
+%%----------------------------------------------------------------------
+%% Func: init/1
+%% Returns: {ok, State} |
+%% {ok, State, Timeout} |
+%% {stop, Reason}
+%%----------------------------------------------------------------------
+init([Parent]) ->
+ process_flag(trap_exit, true),
+ ClientPid = whereis(mnesia_event),
+ link(ClientPid),
+ mnesia_lib:verbose("~p starting: ~p~n", [?MODULE, self()]),
+ Tab = ?ets_new_table(mnesia_subscr, [duplicate_bag, private]),
+ ?ets_insert(Tab, {ClientPid, system}),
+ {ok, #state{supervisor = Parent, pid_tab = Tab}}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_call/3
+%% Returns: {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} | (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_call({change, How}, _From, State) ->
+ Reply = do_change(How, State#state.pid_tab),
+ {reply, Reply, State};
+
+handle_call(Msg, _From, State) ->
+ error("~p got unexpected call: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_cast/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+handle_cast(Msg, State) ->
+ error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: handle_info/2
+%% Returns: {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State} (terminate/2 is called)
+%%----------------------------------------------------------------------
+
+handle_info({'EXIT', Pid, _R}, State) when Pid == State#state.supervisor ->
+ {stop, shutdown, State};
+
+handle_info({'EXIT', Pid, _Reason}, State) ->
+ handle_exit(Pid, State#state.pid_tab),
+ {noreply, State};
+
+handle_info(Msg, State) ->
+ error("~p got unexpected info: ~p~n", [?MODULE, Msg]),
+ {noreply, State}.
+
+%%----------------------------------------------------------------------
+%% Func: terminate/2
+%% Purpose: Shutdown the server
+%% Returns: any (ignored by gen_server)
+%%----------------------------------------------------------------------
+terminate(Reason, State) ->
+ prepare_stop(State#state.pid_tab),
+ mnesia_monitor:terminate_proc(?MODULE, Reason, State).
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%----------------------------------------------------------------------
+%%% Internal functions
+%%%----------------------------------------------------------------------
+
+do_change({activate, ClientPid, system}, SubscrTab) when pid(ClientPid) ->
+ Var = subscribers,
+ activate(ClientPid, system, Var, subscribers(), SubscrTab);
+do_change({activate, ClientPid, {table, Tab, How}}, SubscrTab) when pid(ClientPid) ->
+ case ?catch_val({Tab, where_to_read}) of
+ Node when Node == node() ->
+ Var = {Tab, commit_work},
+ activate(ClientPid, {table, Tab, How}, Var, mnesia_lib:val(Var), SubscrTab);
+ {'EXIT', _} ->
+ {error, {no_exists, Tab}};
+ _Node ->
+ {error, {not_active_local, Tab}}
+ end;
+do_change({deactivate, ClientPid, system}, SubscrTab) ->
+ Var = subscribers,
+ deactivate(ClientPid, system, Var, SubscrTab);
+do_change({deactivate, ClientPid, {table, Tab, How}}, SubscrTab) ->
+ Var = {Tab, commit_work},
+ deactivate(ClientPid, {table, Tab, How}, Var, SubscrTab);
+do_change({deactivate_table, Tab}, SubscrTab) ->
+ Var = {Tab, commit_work},
+ case ?catch_val(Var) of
+ {'EXIT', _} ->
+ {error, {no_exists, Tab}};
+ CommitWork ->
+ case lists:keysearch(subscribers, 1, CommitWork) of
+ false ->
+ ok;
+ {value, Subs} ->
+ Simple = {table, Tab, simple},
+ Detailed = {table, Tab, detailed},
+ Fs = fun(C) -> deactivate(C, Simple, Var, SubscrTab) end,
+ Fd = fun(C) -> deactivate(C, Detailed, Var, SubscrTab) end,
+ case Subs of
+ {subscribers, L1, L2} ->
+ lists:foreach(Fs, L1),
+ lists:foreach(Fd, L2);
+ {subscribers, L1} ->
+ lists:foreach(Fs, L1)
+ end
+ end,
+ {ok, node()}
+ end;
+do_change(_, _) ->
+ {error, badarg}.
+
+activate(ClientPid, What, Var, OldSubscribers, SubscrTab) ->
+ Old =
+ if Var == subscribers ->
+ OldSubscribers;
+ true ->
+ case lists:keysearch(subscribers, 1, OldSubscribers) of
+ false -> [];
+ {value, Subs} ->
+ case Subs of
+ {subscribers, L1, L2} ->
+ L1 ++ L2;
+ {subscribers, L1} ->
+ L1
+ end
+ end
+ end,
+ case lists:member(ClientPid, Old) of
+ false ->
+ %% Don't care about checking old links
+ case catch link(ClientPid) of
+ true ->
+ ?ets_insert(SubscrTab, {ClientPid, What}),
+ add_subscr(Var, What, ClientPid),
+ {ok, node()};
+ {'EXIT', _Reason} ->
+ {error, {no_exists, ClientPid}}
+ end;
+ true ->
+ {error, {already_exists, What}}
+ end.
+
+%%-record(subscribers, {pids = []}). Old subscriber record removed
+%% To solve backward compatibility, this code is a cludge..
+add_subscr(subscribers, _What, Pid) ->
+ mnesia_lib:add(subscribers, Pid),
+ {ok, node()};
+add_subscr({Tab, commit_work}, What, Pid) ->
+ Commit = mnesia_lib:val({Tab, commit_work}),
+ case lists:keysearch(subscribers, 1, Commit) of
+ false ->
+ Subscr =
+ case What of
+ {table, _, simple} ->
+ {subscribers, [Pid], []};
+ {table, _, detailed} ->
+ {subscribers, [], [Pid]}
+ end,
+ mnesia_lib:add({Tab, subscribers}, Pid),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit([Subscr | Commit]));
+ {value, Old} ->
+ {L1, L2} =
+ case Old of
+ {subscribers, L} -> %% Old Way
+ {L, []};
+ {subscribers, SL1, SL2} ->
+ {SL1, SL2}
+ end,
+ Subscr =
+ case What of
+ {table, _, simple} ->
+ {subscribers, [Pid | L1], L2};
+ {table, _, detailed} ->
+ {subscribers, L1, [Pid | L2]}
+ end,
+ NewC = lists:keyreplace(subscribers, 1, Commit, Subscr),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit(NewC)),
+ mnesia_lib:add({Tab, subscribers}, Pid)
+ end.
+
+deactivate(ClientPid, What, Var, SubscrTab) ->
+ ?ets_match_delete(SubscrTab, {ClientPid, What}),
+ case catch ?ets_lookup_element(SubscrTab, ClientPid, 1) of
+ List when list(List) ->
+ ignore;
+ {'EXIT', _} ->
+ unlink(ClientPid)
+ end,
+ del_subscr(Var, What, ClientPid),
+ {ok, node()}.
+
+del_subscr(subscribers, _What, Pid) ->
+ mnesia_lib:del(subscribers, Pid);
+del_subscr({Tab, commit_work}, What, Pid) ->
+ Commit = mnesia_lib:val({Tab, commit_work}),
+ case lists:keysearch(subscribers, 1, Commit) of
+ false ->
+ false;
+ {value, Old} ->
+ {L1, L2} =
+ case Old of
+ {subscribers, L} -> %% Old Way
+ {L, []};
+ {subscribers, SL1, SL2} ->
+ {SL1, SL2}
+ end,
+ Subscr =
+ case What of %% Ignore user error delete subscr from any list
+ {table, _, simple} ->
+ NewL1 = lists:delete(Pid, L1),
+ NewL2 = lists:delete(Pid, L2),
+ {subscribers, NewL1, NewL2};
+ {table, _, detailed} ->
+ NewL1 = lists:delete(Pid, L1),
+ NewL2 = lists:delete(Pid, L2),
+ {subscribers, NewL1, NewL2}
+ end,
+ case Subscr of
+ {subscribers, [], []} ->
+ NewC = lists:keydelete(subscribers, 1, Commit),
+ mnesia_lib:del({Tab, subscribers}, Pid),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit(NewC));
+ _ ->
+ NewC = lists:keyreplace(subscribers, 1, Commit, Subscr),
+ mnesia_lib:del({Tab, subscribers}, Pid),
+ mnesia_lib:set({Tab, commit_work},
+ mnesia_lib:sort_commit(NewC))
+ end
+ end.
+
+handle_exit(ClientPid, SubscrTab) ->
+ do_handle_exit(?ets_lookup(SubscrTab, ClientPid)),
+ ?ets_delete(SubscrTab, ClientPid).
+
+do_handle_exit([{ClientPid, What} | Tail]) ->
+ case What of
+ system ->
+ del_subscr(subscribers, What, ClientPid);
+ {_, Tab, _Level} ->
+ del_subscr({Tab, commit_work}, What, ClientPid)
+ end,
+ do_handle_exit(Tail);
+do_handle_exit([]) ->
+ ok.
+
+prepare_stop(SubscrTab) ->
+ mnesia_lib:report_system_event({mnesia_down, node()}),
+ do_prepare_stop(?ets_first(SubscrTab), SubscrTab).
+
+do_prepare_stop('$end_of_table', _SubscrTab) ->
+ ok;
+do_prepare_stop(ClientPid, SubscrTab) ->
+ Next = ?ets_next(SubscrTab, ClientPid),
+ handle_exit(ClientPid, SubscrTab),
+ unlink(ClientPid),
+ do_prepare_stop(Next, SubscrTab).
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sup.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sup.erl
new file mode 100644
index 0000000000..a8a1df885f
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_sup.erl
@@ -0,0 +1,137 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_sup.erl,v 1.1 2008/12/17 09:53:39 mikpe Exp $
+%%
+%% Supervisor for the entire Mnesia application
+
+-module(mnesia_sup).
+
+-behaviour(application).
+-behaviour(supervisor).
+
+-export([start/0, start/2, init/1, stop/1, start_event/0, kill/0]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% application and suprvisor callback functions
+
+start(normal, Args) ->
+ SupName = {local,?MODULE},
+ case supervisor:start_link(SupName, ?MODULE, [Args]) of
+ {ok, Pid} ->
+ {ok, Pid, {normal, Args}};
+ Error ->
+ Error
+ end;
+start(_, _) ->
+ {error, badarg}.
+
+start() ->
+ SupName = {local,?MODULE},
+ supervisor:start_link(SupName, ?MODULE, []).
+
+stop(_StartArgs) ->
+ ok.
+
+init([]) -> % Supervisor
+ init();
+init([[]]) -> % Application
+ init();
+init(BadArg) ->
+ {error, {badarg, BadArg}}.
+
+init() ->
+ Flags = {one_for_all, 0, 3600}, % Should be rest_for_one policy
+
+ Event = event_procs(),
+ Kernel = kernel_procs(),
+ Mnemosyne = mnemosyne_procs(),
+
+ {ok, {Flags, Event ++ Kernel ++ Mnemosyne}}.
+
+event_procs() ->
+ KillAfter = timer:seconds(30),
+ KA = mnesia_kernel_sup:supervisor_timeout(KillAfter),
+ E = mnesia_event,
+ [{E, {?MODULE, start_event, []}, permanent, KA, worker, [E, gen_event]}].
+
+kernel_procs() ->
+ K = mnesia_kernel_sup,
+ KA = infinity,
+ [{K, {K, start, []}, permanent, KA, supervisor, [K, supervisor]}].
+
+mnemosyne_procs() ->
+ case mnesia_monitor:get_env(embedded_mnemosyne) of
+ true ->
+ Q = mnemosyne_sup,
+ KA = infinity,
+ [{Q, {Q, start, []}, permanent, KA, supervisor, [Q, supervisor]}];
+ false ->
+ []
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% event handler
+
+start_event() ->
+ case gen_event:start_link({local, mnesia_event}) of
+ {ok, Pid} ->
+ case add_event_handler() of
+ ok ->
+ {ok, Pid};
+ Error ->
+ Error
+ end;
+ Error ->
+ Error
+ end.
+
+add_event_handler() ->
+ Handler = mnesia_monitor:get_env(event_module),
+ gen_event:add_handler(mnesia_event, Handler, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% debug functions
+
+kill() ->
+ Mnesia = [mnesia_fallback | mnesia:ms()],
+ Mnemosyne = mnemosyne_ms(),
+ Kill = fun(Name) -> catch exit(whereis(Name), kill) end,
+ lists:foreach(Kill, Mnemosyne),
+ lists:foreach(Kill, Mnesia),
+ lists:foreach(fun ensure_dead/1, Mnemosyne),
+ lists:foreach(fun ensure_dead/1, Mnesia),
+ timer:sleep(10),
+ case lists:keymember(mnesia, 1, application:which_applications()) of
+ true -> kill();
+ false -> ok
+ end.
+
+ensure_dead(Name) ->
+ case whereis(Name) of
+ undefined ->
+ ok;
+ Pid when pid(Pid) ->
+ exit(Pid, kill),
+ timer:sleep(10),
+ ensure_dead(Name)
+ end.
+
+mnemosyne_ms() ->
+ case mnesia_monitor:get_env(embedded_mnemosyne) of
+ true -> mnemosyne:ms();
+ false -> []
+ end.
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_text.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_text.erl
new file mode 100644
index 0000000000..e6084efbb1
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_text.erl
@@ -0,0 +1,191 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_text.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
+%%
+-module(mnesia_text).
+
+-export([parse/1, file/1, load_textfile/1, dump_to_textfile/1]).
+
+load_textfile(File) ->
+ ensure_started(),
+ case parse(File) of
+ {ok, {Tabs, Data}} ->
+ Badtabs = make_tabs(lists:map(fun validate_tab/1, Tabs)),
+ load_data(del_data(Badtabs, Data, []));
+ Other ->
+ Other
+ end.
+
+dump_to_textfile(File) ->
+ dump_to_textfile(mnesia_lib:is_running(), file:open(File, [write])).
+dump_to_textfile(yes, {ok, F}) ->
+ Tabs = lists:delete(schema, mnesia_lib:local_active_tables()),
+ Defs = lists:map(fun(T) -> {T, [{record_name, mnesia_lib:val({T, record_name})},
+ {attributes, mnesia_lib:val({T, attributes})}]}
+ end,
+ Tabs),
+ io:format(F, "~p.~n", [{tables, Defs}]),
+ lists:foreach(fun(T) -> dump_tab(F, T) end, Tabs),
+ file:close(F);
+dump_to_textfile(_,_) -> error.
+
+
+dump_tab(F, T) ->
+ W = mnesia_lib:val({T, wild_pattern}),
+ {'atomic',All} = mnesia:transaction(fun() -> mnesia:match_object(T, W, read) end),
+ lists:foreach(fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
+
+
+ensure_started() ->
+ case mnesia_lib:is_running() of
+ yes ->
+ yes;
+ no ->
+ case mnesia_lib:exists(mnesia_lib:dir("schema.DAT")) of
+ true ->
+ mnesia:start();
+ false ->
+ mnesia:create_schema([node()]),
+ mnesia:start()
+ end
+ end.
+
+del_data(Bad, [H|T], Ack) ->
+ case lists:member(element(1, H), Bad) of
+ true -> del_data(Bad, T, Ack);
+ false -> del_data(Bad, T, [H|Ack])
+ end;
+del_data(_Bad, [], Ack) ->
+ lists:reverse(Ack).
+
+%% Tis the place to call the validate func in mnesia_schema
+validate_tab({Tabname, List}) ->
+ {Tabname, List};
+validate_tab({Tabname, RecName, List}) ->
+ {Tabname, RecName, List};
+validate_tab(_) -> error(badtab).
+
+make_tabs([{Tab, Def} | Tail]) ->
+ case catch mnesia:table_info(Tab, where_to_read) of
+ {'EXIT', _} -> %% non-existing table
+ case mnesia:create_table(Tab, Def) of
+ {aborted, Reason} ->
+ io:format("** Failed to create table ~w ~n"
+ "** Reason = ~w, Args = ~p~n",
+ [Tab, Reason, Def]),
+ [Tab | make_tabs(Tail)];
+ _ ->
+ io:format("New table ~w~n", [Tab]),
+ make_tabs(Tail)
+ end;
+ Node ->
+ io:format("** Table ~w already exists on ~p, just entering data~n",
+ [Tab, Node]),
+ make_tabs(Tail)
+ end;
+
+make_tabs([]) ->
+ [].
+
+load_data(L) ->
+ mnesia:transaction(fun() ->
+ F = fun(X) ->
+ Tab = element(1, X),
+ RN = mnesia:table_info(Tab, record_name),
+ Rec = setelement(1, X, RN),
+ mnesia:write(Tab, Rec, write) end,
+ lists:foreach(F, L)
+ end).
+
+parse(File) ->
+ case file(File) of
+ {ok, Terms} ->
+ case catch collect(Terms) of
+ {error, X} ->
+ {error, X};
+ Other ->
+ {ok, Other}
+ end;
+ Other ->
+ Other
+ end.
+
+collect([{_, {tables, Tabs}}|L]) ->
+ {Tabs, collect_data(Tabs, L)};
+
+collect(_) ->
+ io:format("No tables found\n", []),
+ error(bad_header).
+
+collect_data(Tabs, [{Line, Term} | Tail]) when tuple(Term) ->
+ case lists:keysearch(element(1, Term), 1, Tabs) of
+ {value, _} ->
+ [Term | collect_data(Tabs, Tail)];
+ _Other ->
+ io:format("Object:~p at line ~w unknown\n", [Term,Line]),
+ error(undefined_object)
+ end;
+collect_data(_Tabs, []) -> [];
+collect_data(_Tabs, [H|_T]) ->
+ io:format("Object:~p unknown\n", [H]),
+ error(undefined_object).
+
+error(What) -> throw({error, What}).
+
+file(File) ->
+ case file:open(File, [read]) of
+ {ok, Stream} ->
+ Res = read_terms(Stream, File, 1, []),
+ file:close(Stream),
+ Res;
+ _Other ->
+ {error, open}
+ end.
+
+read_terms(Stream, File, Line, L) ->
+ case read_term_from_stream(Stream, File, Line) of
+ {ok, Term, NextLine} ->
+ read_terms(Stream, File, NextLine, [Term|L]);
+ error ->
+ {error, read};
+ eof ->
+ {ok, lists:reverse(L)}
+ end.
+
+read_term_from_stream(Stream, File, Line) ->
+ R = io:request(Stream, {get_until,'',erl_scan,tokens,[Line]}),
+ case R of
+ {ok,Toks,EndLine} ->
+ case erl_parse:parse_term(Toks) of
+ {ok, Term} ->
+ {ok, {Line, Term}, EndLine};
+ {error, {NewLine,Mod,What}} ->
+ Str = Mod:format_error(What),
+ io:format("Error in line:~p of:~p ~s\n",
+ [NewLine, File, Str]),
+ error;
+ T ->
+ io:format("Error2 **~p~n",[T]),
+ error
+ end;
+ {eof,_EndLine} ->
+ eof;
+ Other ->
+ io:format("Error1 **~p~n",[Other]),
+ error
+ end.
+
+
diff --git a/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_tm.erl b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_tm.erl
new file mode 100644
index 0000000000..7bee382a89
--- /dev/null
+++ b/lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_tm.erl
@@ -0,0 +1,2173 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: mnesia_tm.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
+%%
+-module(mnesia_tm).
+
+-export([
+ start/0,
+ init/1,
+ non_transaction/5,
+ transaction/6,
+ commit_participant/5,
+ dirty/2,
+ display_info/2,
+ do_update_op/3,
+ get_info/1,
+ get_transactions/0,
+ info/1,
+ mnesia_down/1,
+ prepare_checkpoint/2,
+ prepare_checkpoint/1, % Internal
+ prepare_snmp/3,
+ do_snmp/2,
+ put_activity_id/1,
+ block_tab/1,
+ unblock_tab/1
+ ]).
+
+%% sys callback functions
+-export([system_continue/3,
+ system_terminate/4,
+ system_code_change/4
+ ]).
+
+-include("mnesia.hrl").
+-import(mnesia_lib, [set/2]).
+-import(mnesia_lib, [fatal/2, verbose/2, dbg_out/2]).
+
+-record(state, {coordinators = [], participants = [], supervisor,
+ blocked_tabs = [], dirty_queue = []}).
+%% Format on coordinators is [{Tid, EtsTabList} .....
+
+-record(prep, {protocol = sym_trans,
+ %% async_dirty | sync_dirty | sym_trans | sync_sym_trans | asym_trans
+ records = [],
+ prev_tab = [], % initiate to a non valid table name
+ prev_types,
+ prev_snmp,
+ types
+ }).
+
+-record(participant, {tid, pid, commit, disc_nodes = [],
+ ram_nodes = [], protocol = sym_trans}).
+
+start() ->
+ mnesia_monitor:start_proc(?MODULE, ?MODULE, init, [self()]).
+
+init(Parent) ->
+ register(?MODULE, self()),
+ process_flag(trap_exit, true),
+
+ %% Initialize the schema
+ IgnoreFallback = mnesia_monitor:get_env(ignore_fallback_at_startup),
+ mnesia_bup:tm_fallback_start(IgnoreFallback),
+ mnesia_schema:init(IgnoreFallback),
+
+ %% Handshake and initialize transaction recovery
+ mnesia_recover:init(),
+ Early = mnesia_monitor:init(),
+ AllOthers = mnesia_lib:uniq(Early ++ mnesia_lib:all_nodes()) -- [node()],
+ set(original_nodes, AllOthers),
+ mnesia_recover:connect_nodes(AllOthers),
+
+ %% Recover transactions, may wait for decision
+ case mnesia_monitor:use_dir() of
+ true ->
+ P = mnesia_dumper:opt_dump_log(startup), % previous log
+ L = mnesia_dumper:opt_dump_log(startup), % latest log
+ Msg = "Initial dump of log during startup: ~p~n",
+ mnesia_lib:verbose(Msg, [[P, L]]),
+ mnesia_log:init();
+ false ->
+ ignore
+ end,
+
+ mnesia_schema:purge_tmp_files(),
+ mnesia_recover:start_garb(),
+
+ ?eval_debug_fun({?MODULE, init}, [{nodes, AllOthers}]),
+
+ case val(debug) of
+ Debug when Debug /= debug, Debug /= trace ->
+ ignore;
+ _ ->
+ mnesia_subscr:subscribe(whereis(mnesia_event), {table, schema})
+ end,
+ proc_lib:init_ack(Parent, {ok, self()}),
+ doit_loop(#state{supervisor = Parent}).
+
+val(Var) ->
+ case ?catch_val(Var) of
+ {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
+ _VaLuE_ -> _VaLuE_
+ end.
+
+reply({From,Ref}, R) ->
+ From ! {?MODULE, Ref, R};
+reply(From, R) ->
+ From ! {?MODULE, node(), R}.
+
+reply(From, R, State) ->
+ reply(From, R),
+ doit_loop(State).
+
+req(R) ->
+ case whereis(?MODULE) of
+ undefined ->
+ {error, {node_not_running, node()}};
+ Pid ->
+ Ref = make_ref(),
+ Pid ! {{self(), Ref}, R},
+ rec(Pid, Ref)
+ end.
+
+rec() ->
+ rec(whereis(?MODULE)).
+
+rec(Pid) when pid(Pid) ->
+ receive
+ {?MODULE, _, Reply} ->
+ Reply;
+
+ {'EXIT', Pid, _} ->
+ {error, {node_not_running, node()}}
+ end;
+rec(undefined) ->
+ {error, {node_not_running, node()}}.
+
+rec(Pid, Ref) ->
+ receive
+ {?MODULE, Ref, Reply} ->
+ Reply;
+ {'EXIT', Pid, _} ->
+ {error, {node_not_running, node()}}
+ end.
+
+tmlink({From, Ref}) when reference(Ref) ->
+ link(From);
+tmlink(From) ->
+ link(From).
+tmpid({Pid, _Ref}) when pid(Pid) ->
+ Pid;
+tmpid(Pid) ->
+ Pid.
+
+%% Returns a list of participant transaction Tid's
+mnesia_down(Node) ->
+ %% Syncronously call needed in order to avoid
+ %% race with mnesia_tm's coordinator processes
+ %% that may restart and acquire new locks.
+ %% mnesia_monitor takes care of the sync
+ case whereis(?MODULE) of
+ undefined ->
+ mnesia_monitor:mnesia_down(?MODULE, {Node, []});
+ Pid ->
+ Pid ! {mnesia_down, Node}
+ end.
+
+prepare_checkpoint(Nodes, Cp) ->
+ rpc:multicall(Nodes, ?MODULE, prepare_checkpoint, [Cp]).
+
+prepare_checkpoint(Cp) ->
+ req({prepare_checkpoint,Cp}).
+
+block_tab(Tab) ->
+ req({block_tab, Tab}).
+
+unblock_tab(Tab) ->
+ req({unblock_tab, Tab}).
+
+doit_loop(#state{coordinators = Coordinators, participants = Participants, supervisor = Sup}
+ = State) ->
+ receive
+ {_From, {async_dirty, Tid, Commit, Tab}} ->
+ case lists:member(Tab, State#state.blocked_tabs) of
+ false ->
+ do_async_dirty(Tid, Commit, Tab),
+ doit_loop(State);
+ true ->
+ Item = {async_dirty, Tid, Commit, Tab},
+ State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},
+ doit_loop(State2)
+ end;
+
+ {From, {sync_dirty, Tid, Commit, Tab}} ->
+ case lists:member(Tab, State#state.blocked_tabs) of
+ false ->
+ do_sync_dirty(From, Tid, Commit, Tab),
+ doit_loop(State);
+ true ->
+ Item = {sync_dirty, From, Tid, Commit, Tab},
+ State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},
+ doit_loop(State2)
+ end;
+
+ {From, start_outer} -> %% Create and associate ets_tab with Tid
+ case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of
+ {'EXIT', Reason} -> %% system limit
+ Msg = "Cannot create an ets table for the "
+ "local transaction store",
+ reply(From, {error, {system_limit, Msg, Reason}}, State);
+ Etab ->
+ tmlink(From),
+ C = mnesia_recover:incr_trans_tid_serial(),
+ ?ets_insert(Etab, {nodes, node()}),
+ Tid = #tid{pid = tmpid(From), counter = C},
+ A2 = [{Tid , [Etab]} | Coordinators],
+ S2 = State#state{coordinators = A2},
+ reply(From, {new_tid, Tid, Etab}, S2)
+ end;
+
+ {From, {ask_commit, Protocol, Tid, Commit, DiscNs, RamNs}} ->
+ ?eval_debug_fun({?MODULE, doit_ask_commit},
+ [{tid, Tid}, {prot, Protocol}]),
+ mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
+ Pid =
+ case Protocol of
+ asym_trans when node(Tid#tid.pid) /= node() ->
+ Args = [tmpid(From), Tid, Commit, DiscNs, RamNs],
+ spawn_link(?MODULE, commit_participant, Args);
+ _ when node(Tid#tid.pid) /= node() -> %% *_sym_trans
+ reply(From, {vote_yes, Tid}),
+ nopid
+ end,
+ P = #participant{tid = Tid,
+ pid = Pid,
+ commit = Commit,
+ disc_nodes = DiscNs,
+ ram_nodes = RamNs,
+ protocol = Protocol},
+ State2 = State#state{participants = [P | Participants]},
+ doit_loop(State2);
+
+ {Tid, do_commit} ->
+ case mnesia_lib:key_search_delete(Tid, #participant.tid, Participants) of
+ {none, _} ->
+ verbose("Tried to commit a non participant transaction ~p~n",
+ [Tid]),
+ doit_loop(State);
+ {P, Participants2} ->
+ ?eval_debug_fun({?MODULE, do_commit, pre},
+ [{tid, Tid}, {participant, P}]),
+ case P#participant.pid of
+ nopid ->
+ Commit = P#participant.commit,
+ Member = lists:member(node(), P#participant.disc_nodes),
+ if Member == false ->
+ ignore;
+ P#participant.protocol == sym_trans ->
+ mnesia_log:log(Commit);
+ P#participant.protocol == sync_sym_trans ->
+ mnesia_log:slog(Commit)
+ end,
+ mnesia_recover:note_decision(Tid, committed),
+ do_commit(Tid, Commit),
+ if
+ P#participant.protocol == sync_sym_trans ->
+ Tid#tid.pid ! {?MODULE, node(), {committed, Tid}};
+ true ->
+ ignore
+ end,
+ mnesia_locker:release_tid(Tid),
+ transaction_terminated(Tid),
+ ?eval_debug_fun({?MODULE, do_commit, post}, [{tid, Tid}, {pid, nopid}]),
+ doit_loop(State#state{participants = Participants2});
+ Pid when pid(Pid) ->
+ Pid ! {Tid, committed},
+ ?eval_debug_fun({?MODULE, do_commit, post}, [{tid, Tid}, {pid, Pid}]),
+ doit_loop(State)
+ end
+ end;
+
+ {Tid, simple_commit} ->
+ mnesia_recover:note_decision(Tid, committed),
+ mnesia_locker:release_tid(Tid),
+ transaction_terminated(Tid),
+ doit_loop(State);
+
+ {Tid, {do_abort, Reason}} ->
+ ?eval_debug_fun({?MODULE, do_abort, pre}, [{tid, Tid}]),
+ mnesia_locker:release_tid(Tid),
+ case mnesia_lib:key_search_delete(Tid, #participant.tid, Participants) of
+ {none, _} ->
+ verbose("Tried to abort a non participant transaction ~p: ~p~n",
+ [Tid, Reason]),
+ doit_loop(State);
+ {P, Participants2} ->
+ case P#participant.pid of
+ nopid ->
+ Commit = P#participant.commit,
+ mnesia_recover:note_decision(Tid, aborted),
+ do_abort(Tid, Commit),
+ if
+ P#participant.protocol == sync_sym_trans ->
+ Tid#tid.pid ! {?MODULE, node(), {aborted, Tid}};
+ true ->
+ ignore
+ end,
+ transaction_terminated(Tid),
+ ?eval_debug_fun({?MODULE, do_abort, post}, [{tid, Tid}, {pid, nopid}]),
+ doit_loop(State#state{participants = Participants2});
+ Pid when pid(Pid) ->
+ Pid ! {Tid, {do_abort, Reason}},
+ ?eval_debug_fun({?MODULE, do_abort, post},
+ [{tid, Tid}, {pid, Pid}]),
+ doit_loop(State)
+ end
+ end;
+
+ {From, {add_store, Tid}} -> %% new store for nested transaction
+ case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of
+ {'EXIT', Reason} -> %% system limit
+ Msg = "Cannot create an ets table for a nested "
+ "local transaction store",
+ reply(From, {error, {system_limit, Msg, Reason}}, State);
+ Etab ->
+ A2 = add_coord_store(Coordinators, Tid, Etab),
+ reply(From, {new_store, Etab},
+ State#state{coordinators = A2})
+ end;
+
+ {From, {del_store, Tid, Current, Obsolete, PropagateStore}} ->
+ opt_propagate_store(Current, Obsolete, PropagateStore),
+ A2 = del_coord_store(Coordinators, Tid, Current, Obsolete),
+ reply(From, store_erased, State#state{coordinators = A2});
+
+ {'EXIT', Pid, Reason} ->
+ handle_exit(Pid, Reason, State);
+
+ {From, {restart, Tid, Store}} ->
+ A2 = restore_stores(Coordinators, Tid, Store),
+ ?ets_match_delete(Store, '_'),
+ ?ets_insert(Store, {nodes, node()}),
+ reply(From, {restarted, Tid}, State#state{coordinators = A2});
+
+ {delete_transaction, Tid} ->
+ %% used to clear transactions which are committed
+ %% in coordinator or participant processes
+ case mnesia_lib:key_search_delete(Tid, #participant.tid, Participants) of
+ {none, _} ->
+ case mnesia_lib:key_search_delete(Tid, 1, Coordinators) of
+ {none, _} ->
+ verbose("** ERROR ** Tried to delete a non transaction ~p~n",
+ [Tid]),
+ doit_loop(State);
+ {{_Tid, Etabs}, A2} ->
+ erase_ets_tabs(Etabs),
+ transaction_terminated(Tid),
+ doit_loop(State#state{coordinators = A2})
+ end;
+ {_P, Participants2} ->
+ transaction_terminated(Tid),
+ State2 = State#state{participants = Participants2},
+ doit_loop(State2)
+ end;
+
+ {sync_trans_serial, Tid} ->
+ %% Do the Lamport thing here
+ mnesia_recover:sync_trans_tid_serial(Tid),
+ doit_loop(State);
+
+ {From, info} ->
+ reply(From, {info, Participants, Coordinators}, State);
+
+ {mnesia_down, N} ->
+ verbose("Got mnesia_down from ~p, reconfiguring...~n", [N]),
+ reconfigure_coordinators(N, Coordinators),
+
+ Tids = [P#participant.tid || P <- Participants],
+ reconfigure_participants(N, Participants),
+ mnesia_monitor:mnesia_down(?MODULE, {N, Tids}),
+ doit_loop(State);
+
+ {From, {unblock_me, Tab}} ->
+ case lists:member(Tab, State#state.blocked_tabs) of
+ false ->
+ verbose("Wrong dirty Op blocked on ~p ~p ~p",
+ [node(), Tab, From]),
+ reply(From, unblocked),
+ doit_loop(State);
+ true ->
+ Item = {Tab, unblock_me, From},
+ State2 = State#state{dirty_queue = [Item | State#state.dirty_queue]},
+ doit_loop(State2)
+ end;
+
+ {From, {block_tab, Tab}} ->
+ State2 = State#state{blocked_tabs = [Tab | State#state.blocked_tabs]},
+ reply(From, ok, State2);
+
+ {From, {unblock_tab, Tab}} ->
+ BlockedTabs2 = State#state.blocked_tabs -- [Tab],
+ case lists:member(Tab, BlockedTabs2) of
+ false ->
+ mnesia_controller:unblock_table(Tab),
+ Queue = process_dirty_queue(Tab, State#state.dirty_queue),
+ State2 = State#state{blocked_tabs = BlockedTabs2,
+ dirty_queue = Queue},
+ reply(From, ok, State2);
+ true ->
+ State2 = State#state{blocked_tabs = BlockedTabs2},
+ reply(From, ok, State2)
+ end;
+
+ {From, {prepare_checkpoint, Cp}} ->
+ Res = mnesia_checkpoint:tm_prepare(Cp),
+ case Res of
+ {ok, _Name, IgnoreNew, _Node} ->
+ prepare_pending_coordinators(Coordinators, IgnoreNew),
+ prepare_pending_participants(Participants, IgnoreNew);
+ {error, _Reason} ->
+ ignore
+ end,
+ reply(From, Res, State);
+
+ {system, From, Msg} ->
+ dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ sys:handle_system_msg(Msg, From, Sup, ?MODULE, [], State);
+
+ Msg ->
+ verbose("** ERROR ** ~p got unexpected message: ~p~n", [?MODULE, Msg]),
+ doit_loop(State)
+ end.
+
+do_sync_dirty(From, Tid, Commit, _Tab) ->
+ ?eval_debug_fun({?MODULE, sync_dirty, pre}, [{tid, Tid}]),
+ Res = (catch do_dirty(Tid, Commit)),
+ ?eval_debug_fun({?MODULE, sync_dirty, post}, [{tid, Tid}]),
+ From ! {?MODULE, node(), {dirty_res, Res}}.
+
+do_async_dirty(Tid, Commit, _Tab) ->
+ ?eval_debug_fun({?MODULE, async_dirty, pre}, [{tid, Tid}]),
+ catch do_dirty(Tid, Commit),
+ ?eval_debug_fun({?MODULE, async_dirty, post}, [{tid, Tid}]).
+
+%% Process items in fifo order
+process_dirty_queue(Tab, [Item | Queue]) ->
+ Queue2 = process_dirty_queue(Tab, Queue),
+ case Item of
+ {async_dirty, Tid, Commit, Tab} ->
+ do_async_dirty(Tid, Commit, Tab),
+ Queue2;
+ {sync_dirty, From, Tid, Commit, Tab} ->
+ do_sync_dirty(From, Tid, Commit, Tab),
+ Queue2;
+ {Tab, unblock_me, From} ->
+ reply(From, unblocked),
+ Queue2;
+ _ ->
+ [Item | Queue2]
+ end;
+process_dirty_queue(_Tab, []) ->
+ [].
+
+prepare_pending_coordinators([{Tid, [Store | _Etabs]} | Coords], IgnoreNew) ->
+ case catch ?ets_lookup(Store, pending) of
+ [] ->
+ prepare_pending_coordinators(Coords, IgnoreNew);
+ [Pending] ->
+ case lists:member(Tid, IgnoreNew) of
+ false ->
+ mnesia_checkpoint:tm_enter_pending(Pending);
+ true ->
+ ignore
+ end,
+ prepare_pending_coordinators(Coords, IgnoreNew);
+ {'EXIT', _} ->
+ prepare_pending_coordinators(Coords, IgnoreNew)
+ end;
+prepare_pending_coordinators([], _IgnoreNew) ->
+ ok.
+
+prepare_pending_participants([Part | Parts], IgnoreNew) ->
+ Tid = Part#participant.tid,
+ D = Part#participant.disc_nodes,
+ R = Part#participant.ram_nodes,
+ case lists:member(Tid, IgnoreNew) of
+ false ->
+ mnesia_checkpoint:tm_enter_pending(Tid, D, R);
+ true ->
+ ignore
+ end,
+ prepare_pending_participants(Parts, IgnoreNew);
+prepare_pending_participants([], _IgnoreNew) ->
+ ok.
+
+handle_exit(Pid, Reason, State) when node(Pid) /= node() ->
+ %% We got exit from a remote fool
+ dbg_out("~p got remote EXIT from unknown ~p~n",
+ [?MODULE, {Pid, Reason}]),
+ doit_loop(State);
+
+handle_exit(Pid, _Reason, State) when Pid == State#state.supervisor ->
+ %% Our supervisor has died, time to stop
+ do_stop(State);
+
+handle_exit(Pid, Reason, State) ->
+ %% Check if it is a coordinator
+ case pid_search_delete(Pid, State#state.coordinators) of
+ {none, _} ->
+ %% Check if it is a participant
+ case mnesia_lib:key_search_delete(Pid, #participant.pid, State#state.participants) of
+ {none, _} ->
+ %% We got exit from a local fool
+ verbose("** ERROR ** ~p got local EXIT from unknown process: ~p~n",
+ [?MODULE, {Pid, Reason}]),
+ doit_loop(State);
+
+ {P, RestP} when record(P, participant) ->
+ fatal("Participant ~p in transaction ~p died ~p~n",
+ [P#participant.pid, P#participant.tid, Reason]),
+ doit_loop(State#state{participants = RestP})
+ end;
+
+ {{Tid, Etabs}, RestC} ->
+ %% A local coordinator has died and
+ %% we must determine the outcome of the
+ %% transaction and tell mnesia_tm on the
+ %% other nodes about it and then recover
+ %% locally.
+ recover_coordinator(Tid, Etabs),
+ doit_loop(State#state{coordinators = RestC})
+ end.
+
+recover_coordinator(Tid, Etabs) ->
+ verbose("Coordinator ~p in transaction ~p died.~n", [Tid#tid.pid, Tid]),
+
+ Store = hd(Etabs),
+ CheckNodes = get_nodes(Store),
+ TellNodes = CheckNodes -- [node()],
+ case catch arrange(Tid, Store, async) of
+ {'EXIT', Reason} ->
+ dbg_out("Recovery of coordinator ~p failed:~n", [Tid, Reason]),
+ Protocol = asym_trans,
+ tell_outcome(Tid, Protocol, node(), CheckNodes, TellNodes);
+ {_N, Prep} ->
+ %% Tell the participants about the outcome
+ Protocol = Prep#prep.protocol,
+ Outcome = tell_outcome(Tid, Protocol, node(), CheckNodes, TellNodes),
+
+ %% Recover locally
+ CR = Prep#prep.records,
+ {DiscNs, RamNs} = commit_nodes(CR, [], []),
+ {value, Local} = lists:keysearch(node(), #commit.node, CR),
+
+ ?eval_debug_fun({?MODULE, recover_coordinator, pre},
+ [{tid, Tid}, {outcome, Outcome}, {prot, Protocol}]),
+ recover_coordinator(Tid, Protocol, Outcome, Local, DiscNs, RamNs),
+ ?eval_debug_fun({?MODULE, recover_coordinator, post},
+ [{tid, Tid}, {outcome, Outcome}, {prot, Protocol}])
+
+ end,
+ erase_ets_tabs(Etabs),
+ transaction_terminated(Tid),
+ mnesia_locker:release_tid(Tid).
+
+recover_coordinator(Tid, sym_trans, committed, Local, _, _) ->
+ mnesia_recover:note_decision(Tid, committed),
+ do_dirty(Tid, Local);
+recover_coordinator(Tid, sym_trans, aborted, _Local, _, _) ->
+ mnesia_recover:note_decision(Tid, aborted);
+recover_coordinator(Tid, sync_sym_trans, committed, Local, _, _) ->
+ mnesia_recover:note_decision(Tid, committed),
+ do_dirty(Tid, Local);
+recover_coordinator(Tid, sync_sym_trans, aborted, _Local, _, _) ->
+ mnesia_recover:note_decision(Tid, aborted);
+
+recover_coordinator(Tid, asym_trans, committed, Local, DiscNs, RamNs) ->
+ D = #decision{tid = Tid, outcome = committed,
+ disc_nodes = DiscNs, ram_nodes = RamNs},
+ mnesia_recover:log_decision(D),
+ do_commit(Tid, Local);
+recover_coordinator(Tid, asym_trans, aborted, Local, DiscNs, RamNs) ->
+ D = #decision{tid = Tid, outcome = aborted,
+ disc_nodes = DiscNs, ram_nodes = RamNs},
+ mnesia_recover:log_decision(D),
+ do_abort(Tid, Local).
+
+restore_stores([{Tid, Etstabs} | Tail], Tid, Store) ->
+ Remaining = lists:delete(Store, Etstabs),
+ erase_ets_tabs(Remaining),
+ [{Tid, [Store]} | Tail];
+restore_stores([H | T], Tid, Store) ->
+ [H | restore_stores(T, Tid, Store)].
+%% No NIL case on purpose
+
+add_coord_store([{Tid, Stores} | Coordinators], Tid, Etab) ->
+ [{Tid, [Etab | Stores]} | Coordinators];
+add_coord_store([H | T], Tid, Etab) ->
+ [H | add_coord_store(T, Tid, Etab)].
+%% no NIL case on purpose
+
+del_coord_store([{Tid, Stores} | Coordinators], Tid, Current, Obsolete) ->
+ Rest =
+ case Stores of
+ [Obsolete, Current | Tail] -> Tail;
+ [Current, Obsolete | Tail] -> Tail
+ end,
+ ?ets_delete_table(Obsolete),
+ [{Tid, [Current | Rest]} | Coordinators];
+del_coord_store([H | T], Tid, Current, Obsolete) ->
+ [H | del_coord_store(T, Tid, Current, Obsolete)].
+%% no NIL case on purpose
+
+erase_ets_tabs([H | T]) ->
+ ?ets_delete_table(H),
+ erase_ets_tabs(T);
+erase_ets_tabs([]) ->
+ ok.
+
+%% Deletes a pid from a list of participants
+%% or from a list of coordinators and returns
+%% {none, All} or {Tr, Rest}
+pid_search_delete(Pid, Trs) ->
+ pid_search_delete(Pid, Trs, none, []).
+pid_search_delete(Pid, [Tr = {Tid, _Ts} | Trs], _Val, Ack) when Tid#tid.pid == Pid ->
+ pid_search_delete(Pid, Trs, Tr, Ack);
+pid_search_delete(Pid, [Tr | Trs], Val, Ack) ->
+ pid_search_delete(Pid, Trs, Val, [Tr | Ack]);
+
+pid_search_delete(_Pid, [], Val, Ack) ->
+ {Val, Ack}.
+
+%% When TM gets an EXIT sig, we must also check to see
+%% if the crashing transaction is in the Participant list
+%%
+%% search_participant_for_pid([Participant | Tail], Pid) ->
+%% Tid = Participant#participant.tid,
+%% if
+%% Tid#tid.pid == Pid ->
+%% {coordinator, Participant};
+%% Participant#participant.pid == Pid ->
+%% {participant, Participant};
+%% true ->
+%% search_participant_for_pid(Tail, Pid)
+%% end;
+%% search_participant_for_pid([], _) ->
+%% fool.
+
+transaction_terminated(Tid) ->
+ mnesia_checkpoint:tm_exit_pending(Tid),
+ Pid = Tid#tid.pid,
+ if
+ node(Pid) == node() ->
+ unlink(Pid);
+ true -> %% Do the Lamport thing here
+ mnesia_recover:sync_trans_tid_serial(Tid)
+ end.
+
+non_transaction(OldState, Fun, Args, ActivityKind, Mod) ->
+ Id = {ActivityKind, self()},
+ NewState = {Mod, Id, non_transaction},
+ put(mnesia_activity_state, NewState),
+ %% I Want something uniqe here, references are expensive
+ Ref = mNeSia_nOn_TrAnSacTioN,
+ RefRes = (catch {Ref, apply(Fun, Args)}),
+ case OldState of
+ undefined -> erase(mnesia_activity_state);
+ _ -> put(mnesia_activity_state, OldState)
+ end,
+ case RefRes of
+ {Ref, Res} ->
+ case Res of
+ {'EXIT', Reason} -> exit(Reason);
+ {aborted, Reason} -> mnesia:abort(Reason);
+ _ -> Res
+ end;
+ {'EXIT', Reason} ->
+ exit(Reason);
+ Throw ->
+ throw(Throw)
+ end.
+
+transaction(OldTidTs, Fun, Args, Retries, Mod, Type) ->
+ Factor = 1,
+ case OldTidTs of
+ undefined -> % Outer
+ execute_outer(Mod, Fun, Args, Factor, Retries, Type);
+ {_OldMod, Tid, Ts} -> % Nested
+ execute_inner(Mod, Tid, Ts, Fun, Args, Factor, Retries, Type);
+ _ -> % Bad nesting
+ {aborted, nested_transaction}
+ end.
+
+execute_outer(Mod, Fun, Args, Factor, Retries, Type) ->
+ case req(start_outer) of
+ {error, Reason} ->
+ {aborted, Reason};
+ {new_tid, Tid, Store} ->
+ Ts = #tidstore{store = Store},
+ NewTidTs = {Mod, Tid, Ts},
+ put(mnesia_activity_state, NewTidTs),
+ execute_transaction(Fun, Args, Factor, Retries, Type)
+ end.
+
+execute_inner(Mod, Tid, Ts, Fun, Args, Factor, Retries, Type) ->
+ case req({add_store, Tid}) of
+ {error, Reason} ->
+ {aborted, Reason};
+ {new_store, Ets} ->
+ copy_ets(Ts#tidstore.store, Ets),
+ Up = [Ts#tidstore.store | Ts#tidstore.up_stores],
+ NewTs = Ts#tidstore{level = 1 + Ts#tidstore.level,
+ store = Ets,
+ up_stores = Up},
+ NewTidTs = {Mod, Tid, NewTs},
+ put(mnesia_activity_state, NewTidTs),
+ execute_transaction(Fun, Args, Factor, Retries, Type)
+ end.
+
+copy_ets(From, To) ->
+ do_copy_ets(?ets_first(From), From, To).
+do_copy_ets('$end_of_table', _,_) ->
+ ok;
+do_copy_ets(K, From, To) ->
+ Objs = ?ets_lookup(From, K),
+ insert_objs(Objs, To),
+ do_copy_ets(?ets_next(From, K), From, To).
+
+insert_objs([H|T], Tab) ->
+ ?ets_insert(Tab, H),
+ insert_objs(T, Tab);
+insert_objs([], _Tab) ->
+ ok.
+
+execute_transaction(Fun, Args, Factor, Retries, Type) ->
+ case catch apply_fun(Fun, Args, Type) of
+ {'EXIT', Reason} ->
+ check_exit(Fun, Args, Factor, Retries, Reason, Type);
+ {'atomic', Value} ->
+ mnesia_lib:incr_counter(trans_commits),
+ erase(mnesia_activity_state),
+ %% no need to clear locks, already done by commit ...
+ %% Flush any un processed mnesia_down messages we might have
+ flush_downs(),
+ {'atomic', Value};
+ {nested_atomic, Value} ->
+ mnesia_lib:incr_counter(trans_commits),
+ {'atomic', Value};
+ Value -> %% User called throw
+ Reason = {aborted, {throw, Value}},
+ return_abort(Fun, Args, Reason)
+ end.
+
+apply_fun(Fun, Args, Type) ->
+ Result = apply(Fun, Args),
+ case t_commit(Type) of
+ do_commit ->
+ {'atomic', Result};
+ do_commit_nested ->
+ {nested_atomic, Result};
+ {do_abort, {aborted, Reason}} ->
+ {'EXIT', {aborted, Reason}};
+ {do_abort, Reason} ->
+ {'EXIT', {aborted, Reason}}
+ end.
+
+check_exit(Fun, Args, Factor, Retries, Reason, Type) ->
+ case Reason of
+ {aborted, C} when record(C, cyclic) ->
+ maybe_restart(Fun, Args, Factor, Retries, Type, C);
+ {aborted, {node_not_running, N}} ->
+ maybe_restart(Fun, Args, Factor, Retries, Type, {node_not_running, N});
+ {aborted, {bad_commit, N}} ->
+ maybe_restart(Fun, Args, Factor, Retries, Type, {bad_commit, N});
+ _ ->
+ return_abort(Fun, Args, Reason)
+ end.
+
+maybe_restart(Fun, Args, Factor, Retries, Type, Why) ->
+ {Mod, Tid, Ts} = get(mnesia_activity_state),
+ case try_again(Retries) of
+ yes when Ts#tidstore.level == 1 ->
+ restart(Mod, Tid, Ts, Fun, Args, Factor, Retries, Type, Why);
+ yes ->
+ return_abort(Fun, Args, Why);
+ no ->
+ return_abort(Fun, Args, {aborted, nomore})
+ end.
+
+try_again(infinity) -> yes;
+try_again(X) when number(X) , X > 1 -> yes;
+try_again(_) -> no.
+
+%% We can only restart toplevel transactions.
+%% If a deadlock situation occurs in a nested transaction
+%% The whole thing including all nested transactions need to be
+%% restarted. The stack is thus popped by a consequtive series of
+%% exit({aborted, #cyclic{}}) calls
+
+restart(Mod, Tid, Ts, Fun, Args, Factor0, Retries0, Type, Why) ->
+ mnesia_lib:incr_counter(trans_restarts),
+ Retries = decr(Retries0),
+ case Why of
+ {bad_commit, _N} ->
+ return_abort(Fun, Args, Why),
+ Factor = 1,
+ SleepTime = mnesia_lib:random_time(Factor, Tid#tid.counter),
+ dbg_out("Restarting transaction ~w: in ~wms ~w~n", [Tid, SleepTime, Why]),
+ timer:sleep(SleepTime),
+ execute_outer(Mod, Fun, Args, Factor, Retries, Type);
+ {node_not_running, _N} -> %% Avoids hanging in receive_release_tid_ack
+ return_abort(Fun, Args, Why),
+ Factor = 1,
+ SleepTime = mnesia_lib:random_time(Factor, Tid#tid.counter),
+ dbg_out("Restarting transaction ~w: in ~wms ~w~n", [Tid, SleepTime, Why]),
+ timer:sleep(SleepTime),
+ execute_outer(Mod, Fun, Args, Factor, Retries, Type);
+ _ ->
+ SleepTime = mnesia_lib:random_time(Factor0, Tid#tid.counter),
+ dbg_out("Restarting transaction ~w: in ~wms ~w~n", [Tid, SleepTime, Why]),
+
+ if
+ Factor0 /= 10 ->
+ ignore;
+ true ->
+ %% Our serial may be much larger than other nodes ditto
+ AllNodes = val({current, db_nodes}),
+ verbose("Sync serial ~p~n", [Tid]),
+ rpc:abcast(AllNodes, ?MODULE, {sync_trans_serial, Tid})
+ end,
+ intercept_friends(Tid, Ts),
+ Store = Ts#tidstore.store,
+ Nodes = get_nodes(Store),
+ ?MODULE ! {self(), {restart, Tid, Store}},
+ mnesia_locker:send_release_tid(Nodes, Tid),
+ timer:sleep(SleepTime),
+ mnesia_locker:receive_release_tid_acc(Nodes, Tid),
+ case rec() of
+ {restarted, Tid} ->
+ execute_transaction(Fun, Args, Factor0 + 1,
+ Retries, Type);
+ {error, Reason} ->
+ mnesia:abort(Reason)
+ end
+ end.
+
+decr(infinity) -> infinity;
+decr(X) when integer(X), X > 1 -> X - 1;
+decr(_X) -> 0.
+
+return_abort(Fun, Args, Reason) ->
+ {Mod, Tid, Ts} = get(mnesia_activity_state),
+ OldStore = Ts#tidstore.store,
+ Nodes = get_nodes(OldStore),
+ intercept_friends(Tid, Ts),
+ catch mnesia_lib:incr_counter(trans_failures),
+ Level = Ts#tidstore.level,
+ if
+ Level == 1 ->
+ mnesia_locker:async_release_tid(Nodes, Tid),
+ ?MODULE ! {delete_transaction, Tid},
+ erase(mnesia_activity_state),
+ dbg_out("Transaction ~p calling ~p with ~p, failed ~p~n",
+ [Tid, Fun, Args, Reason]),
+ flush_downs(),
+ {aborted, mnesia_lib:fix_error(Reason)};
+ true ->
+ %% Nested transaction
+ [NewStore | Tail] = Ts#tidstore.up_stores,
+ req({del_store, Tid, NewStore, OldStore, true}),
+ Ts2 = Ts#tidstore{store = NewStore,
+ up_stores = Tail,
+ level = Level - 1},
+ NewTidTs = {Mod, Tid, Ts2},
+ put(mnesia_activity_state, NewTidTs),
+ case Reason of
+ #cyclic{} ->
+ exit({aborted, Reason});
+ {node_not_running, _N} ->
+ exit({aborted, Reason});
+ {bad_commit, _N}->
+ exit({aborted, Reason});
+ _ ->
+ {aborted, mnesia_lib:fix_error(Reason)}
+ end
+ end.
+
+flush_downs() ->
+ receive
+ {?MODULE, _, _} -> flush_downs(); % Votes
+ {mnesia_down, _} -> flush_downs()
+ after 0 -> flushed
+ end.
+
+put_activity_id(undefined) ->
+ erase_activity_id();
+put_activity_id({Mod, Tid, Ts}) when record(Tid, tid), record(Ts, tidstore) ->
+ flush_downs(),
+ Store = Ts#tidstore.store,
+ ?ets_insert(Store, {friends, self()}),
+ NewTidTs = {Mod, Tid, Ts},
+ put(mnesia_activity_state, NewTidTs);
+put_activity_id(SimpleState) ->
+ put(mnesia_activity_state, SimpleState).
+
+erase_activity_id() ->
+ flush_downs(),
+ erase(mnesia_activity_state).
+
+get_nodes(Store) ->
+ case catch ?ets_lookup_element(Store, nodes, 2) of
+ {'EXIT', _} -> [node()];
+ Nodes -> Nodes
+ end.
+
+get_friends(Store) ->
+ case catch ?ets_lookup_element(Store, friends, 2) of
+ {'EXIT', _} -> [];
+ Friends -> Friends
+ end.
+
+opt_propagate_store(_Current, _Obsolete, false) ->
+ ok;
+opt_propagate_store(Current, Obsolete, true) ->
+ propagate_store(Current, nodes, get_nodes(Obsolete)),
+ propagate_store(Current, friends, get_friends(Obsolete)).
+
+propagate_store(Store, Var, [Val | Vals]) ->
+ ?ets_insert(Store, {Var, Val}),
+ propagate_store(Store, Var, Vals);
+propagate_store(_Store, _Var, []) ->
+ ok.
+
+%% Tell all processes that are cooperating with the current transaction
+intercept_friends(_Tid, Ts) ->
+ Friends = get_friends(Ts#tidstore.store),
+ Message = {activity_ended, undefined, self()},
+ intercept_best_friend(Friends, Message).
+
+intercept_best_friend([], _Message) ->
+ ok;
+intercept_best_friend([Pid | _], Message) ->
+ Pid ! Message,
+ wait_for_best_friend(Pid, 0).
+
+wait_for_best_friend(Pid, Timeout) ->
+ receive
+ {'EXIT', Pid, _} -> ok;
+ {activity_ended, _, Pid} -> ok
+ after Timeout ->
+ case my_process_is_alive(Pid) of
+ true -> wait_for_best_friend(Pid, 1000);
+ false -> ok
+ end
+ end.
+
+my_process_is_alive(Pid) ->
+ case catch erlang:is_process_alive(Pid) of % New BIF in R5
+ true ->
+ true;
+ false ->
+ false;
+ {'EXIT', _} -> % Pre R5 backward compatibility
+ case process_info(Pid, message_queue_len) of
+ undefined -> false;
+ _ -> true
+ end
+ end.
+
+dirty(Protocol, Item) ->
+ {{Tab, Key}, _Val, _Op} = Item,
+ Tid = {dirty, self()},
+ Prep = prepare_items(Tid, Tab, Key, [Item], #prep{protocol= Protocol}),
+ CR = Prep#prep.records,
+ case Protocol of
+ async_dirty ->
+ %% Send commit records to the other involved nodes,
+ %% but do only wait for one node to complete.
+ %% Preferrably, the local node if possible.
+
+ ReadNode = val({Tab, where_to_read}),
+ {WaitFor, FirstRes} = async_send_dirty(Tid, CR, Tab, ReadNode),
+ rec_dirty(WaitFor, FirstRes);
+
+ sync_dirty ->
+ %% Send commit records to the other involved nodes,
+ %% and wait for all nodes to complete
+ {WaitFor, FirstRes} = sync_send_dirty(Tid, CR, Tab, []),
+ rec_dirty(WaitFor, FirstRes);
+ _ ->
+ mnesia:abort({bad_activity, Protocol})
+ end.
+
+%% This is the commit function, The first thing it does,
+%% is to find out which nodes that have been participating
+%% in this particular transaction, all of the mnesia_locker:lock*
+%% functions insert the names of the nodes where it aquires locks
+%% into the local shadow Store
+%% This function exacutes in the context of the user process
+t_commit(Type) ->
+ {Mod, Tid, Ts} = get(mnesia_activity_state),
+ Store = Ts#tidstore.store,
+ if
+ Ts#tidstore.level == 1 ->
+ intercept_friends(Tid, Ts),
+ %% N is number of updates
+ case arrange(Tid, Store, Type) of
+ {N, Prep} when N > 0 ->
+ multi_commit(Prep#prep.protocol,
+ Tid, Prep#prep.records, Store);
+ {0, Prep} ->
+ multi_commit(read_only, Tid, Prep#prep.records, Store)
+ end;
+ true ->
+ %% nested commit
+ Level = Ts#tidstore.level,
+ [Obsolete | Tail] = Ts#tidstore.up_stores,
+ req({del_store, Tid, Store, Obsolete, false}),
+ NewTs = Ts#tidstore{store = Store,
+ up_stores = Tail,
+ level = Level - 1},
+ NewTidTs = {Mod, Tid, NewTs},
+ put(mnesia_activity_state, NewTidTs),
+ do_commit_nested
+ end.
+
+%% This function arranges for all objects we shall write in S to be
+%% in a list of {Node, CommitRecord}
+%% Important function for the performance of mnesia.
+
+arrange(Tid, Store, Type) ->
+ %% The local node is always included
+ Nodes = get_nodes(Store),
+ Recs = prep_recs(Nodes, []),
+ Key = ?ets_first(Store),
+ N = 0,
+ Prep =
+ case Type of
+ async -> #prep{protocol = sym_trans, records = Recs};
+ sync -> #prep{protocol = sync_sym_trans, records = Recs}
+ end,
+ case catch do_arrange(Tid, Store, Key, Prep, N) of
+ {'EXIT', Reason} ->
+ dbg_out("do_arrange failed ~p ~p~n", [Reason, Tid]),
+ case Reason of
+ {aborted, R} ->
+ mnesia:abort(R);
+ _ ->
+ mnesia:abort(Reason)
+ end;
+ {New, Prepared} ->
+ {New, Prepared#prep{records = reverse(Prepared#prep.records)}}
+ end.
+
+reverse([]) ->
+ [];
+reverse([H|R]) when record(H, commit) ->
+ [
+ H#commit{
+ ram_copies = lists:reverse(H#commit.ram_copies),
+ disc_copies = lists:reverse(H#commit.disc_copies),
+ disc_only_copies = lists:reverse(H#commit.disc_only_copies),
+ snmp = lists:reverse(H#commit.snmp)
+ }
+ | reverse(R)].
+
+prep_recs([N | Nodes], Recs) ->
+ prep_recs(Nodes, [#commit{decision = presume_commit, node = N} | Recs]);
+prep_recs([], Recs) ->
+ Recs.
+
+%% storage_types is a list of {Node, Storage} tuples
+%% where each tuple represents an active replica
+do_arrange(Tid, Store, {Tab, Key}, Prep, N) ->
+ Oid = {Tab, Key},
+ Items = ?ets_lookup(Store, Oid), %% Store is a bag
+ P2 = prepare_items(Tid, Tab, Key, Items, Prep),
+ do_arrange(Tid, Store, ?ets_next(Store, Oid), P2, N + 1);
+do_arrange(Tid, Store, SchemaKey, Prep, N) when SchemaKey == op ->
+ Items = ?ets_lookup(Store, SchemaKey), %% Store is a bag
+ P2 = prepare_schema_items(Tid, Items, Prep),
+ do_arrange(Tid, Store, ?ets_next(Store, SchemaKey), P2, N + 1);
+do_arrange(Tid, Store, RestoreKey, Prep, N) when RestoreKey == restore_op ->
+ [{restore_op, R}] = ?ets_lookup(Store, RestoreKey),
+ Fun = fun({Tab, Key}, CommitRecs, _RecName, Where, Snmp) ->
+ Item = [{{Tab, Key}, {Tab, Key}, delete}],
+ do_prepare_items(Tid, Tab, Key, Where, Snmp, Item, CommitRecs);
+ (BupRec, CommitRecs, RecName, Where, Snmp) ->
+ Tab = element(1, BupRec),
+ Key = element(2, BupRec),
+ Item =
+ if
+ Tab == RecName ->
+ [{{Tab, Key}, BupRec, write}];
+ true ->
+ BupRec2 = setelement(1, BupRec, RecName),
+ [{{Tab, Key}, BupRec2, write}]
+ end,
+ do_prepare_items(Tid, Tab, Key, Where, Snmp, Item, CommitRecs)
+ end,
+ Recs2 = mnesia_schema:arrange_restore(R, Fun, Prep#prep.records),
+ P2 = Prep#prep{protocol = asym_trans, records = Recs2},
+ do_arrange(Tid, Store, ?ets_next(Store, RestoreKey), P2, N + 1);
+do_arrange(_Tid, _Store, '$end_of_table', Prep, N) ->
+ {N, Prep};
+do_arrange(Tid, Store, IgnoredKey, Prep, N) -> %% locks, nodes ... local atoms...
+ do_arrange(Tid, Store, ?ets_next(Store, IgnoredKey), Prep, N).
+
+%% Returns a prep record with all items in reverse order
+prepare_schema_items(Tid, Items, Prep) ->
+ Types = [{N, schema_ops} || N <- val({current, db_nodes})],
+ Recs = prepare_nodes(Tid, Types, Items, Prep#prep.records, schema),
+ Prep#prep{protocol = asym_trans, records = Recs}.
+
+%% Returns a prep record with all items in reverse order
+prepare_items(Tid, Tab, Key, Items, Prep) when Prep#prep.prev_tab == Tab ->
+ Types = Prep#prep.prev_types,
+ Snmp = Prep#prep.prev_snmp,
+ Recs = Prep#prep.records,
+ Recs2 = do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Recs),
+ Prep#prep{records = Recs2};
+
+prepare_items(Tid, Tab, Key, Items, Prep) ->
+ Types = val({Tab, where_to_commit}),
+ case Types of
+ [] -> mnesia:abort({no_exists, Tab});
+ {blocked, _} ->
+ unblocked = req({unblock_me, Tab}),
+ prepare_items(Tid, Tab, Key, Items, Prep);
+ _ ->
+ Snmp = val({Tab, snmp}),
+ Recs2 = do_prepare_items(Tid, Tab, Key, Types,
+ Snmp, Items, Prep#prep.records),
+ Prep2 = Prep#prep{records = Recs2, prev_tab = Tab,
+ prev_types = Types, prev_snmp = Snmp},
+ check_prep(Prep2, Types)
+ end.
+
+do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Recs) ->
+ Recs2 = prepare_snmp(Tid, Tab, Key, Types, Snmp, Items, Recs), % May exit
+ prepare_nodes(Tid, Types, Items, Recs2, normal).
+
+prepare_snmp(Tab, Key, Items) ->
+ case val({Tab, snmp}) of
+ [] ->
+ [];
+ Ustruct when Key /= '_' ->
+ {_Oid, _Val, Op} = hd(Items),
+ %% Still making snmp oid (not used) because we want to catch errors here
+ %% And also it keeps backwards comp. with old nodes.
+ SnmpOid = mnesia_snmp_hook:key_to_oid(Tab, Key, Ustruct), % May exit
+ [{Op, Tab, Key, SnmpOid}];
+ _ ->
+ [{clear_table, Tab}]
+ end.
+
+prepare_snmp(_Tid, _Tab, _Key, _Types, [], _Items, Recs) ->
+ Recs;
+
+prepare_snmp(Tid, Tab, Key, Types, Us, Items, Recs) ->
+ if Key /= '_' ->
+ {_Oid, _Val, Op} = hd(Items),
+ SnmpOid = mnesia_snmp_hook:key_to_oid(Tab, Key, Us), % May exit
+ prepare_nodes(Tid, Types, [{Op, Tab, Key, SnmpOid}], Recs, snmp);
+ Key == '_' ->
+ prepare_nodes(Tid, Types, [{clear_table, Tab}], Recs, snmp)
+ end.
+
+check_prep(Prep, Types) when Prep#prep.types == Types ->
+ Prep;
+check_prep(Prep, Types) when Prep#prep.types == undefined ->
+ Prep#prep{types = Types};
+check_prep(Prep, _Types) ->
+ Prep#prep{protocol = asym_trans}.
+
+%% Returns a list of commit records
+prepare_nodes(Tid, [{Node, Storage} | Rest], Items, C, Kind) ->
+ {Rec, C2} = pick_node(Tid, Node, C, []),
+ Rec2 = prepare_node(Node, Storage, Items, Rec, Kind),
+ [Rec2 | prepare_nodes(Tid, Rest, Items, C2, Kind)];
+prepare_nodes(_Tid, [], _Items, CommitRecords, _Kind) ->
+ CommitRecords.
+
+pick_node(Tid, Node, [Rec | Rest], Done) ->
+ if
+ Rec#commit.node == Node ->
+ {Rec, Done ++ Rest};
+ true ->
+ pick_node(Tid, Node, Rest, [Rec | Done])
+ end;
+pick_node(_Tid, Node, [], Done) ->
+ {#commit{decision = presume_commit, node = Node}, Done}.
+
+prepare_node(Node, Storage, [Item | Items], Rec, Kind) when Kind == snmp ->
+ Rec2 = Rec#commit{snmp = [Item | Rec#commit.snmp]},
+ prepare_node(Node, Storage, Items, Rec2, Kind);
+prepare_node(Node, Storage, [Item | Items], Rec, Kind) when Kind /= schema ->
+ Rec2 =
+ case Storage of
+ ram_copies ->
+ Rec#commit{ram_copies = [Item | Rec#commit.ram_copies]};
+ disc_copies ->
+ Rec#commit{disc_copies = [Item | Rec#commit.disc_copies]};
+ disc_only_copies ->
+ Rec#commit{disc_only_copies =
+ [Item | Rec#commit.disc_only_copies]}
+ end,
+ prepare_node(Node, Storage, Items, Rec2, Kind);
+prepare_node(_Node, _Storage, Items, Rec, Kind)
+ when Kind == schema, Rec#commit.schema_ops == [] ->
+ Rec#commit{schema_ops = Items};
+prepare_node(_Node, _Storage, [], Rec, _Kind) ->
+ Rec.
+
+%% multi_commit((Protocol, Tid, CommitRecords, Store)
+%% Local work is always performed in users process
+multi_commit(read_only, Tid, CR, _Store) ->
+ %% This featherweight commit protocol is used when no
+ %% updates has been performed in the transaction.
+
+ {DiscNs, RamNs} = commit_nodes(CR, [], []),
+ Msg = {Tid, simple_commit},
+ rpc:abcast(DiscNs -- [node()], ?MODULE, Msg),
+ rpc:abcast(RamNs -- [node()], ?MODULE, Msg),
+ mnesia_recover:note_decision(Tid, committed),
+ mnesia_locker:release_tid(Tid),
+ ?MODULE ! {delete_transaction, Tid},
+ do_commit;
+
+multi_commit(sym_trans, Tid, CR, Store) ->
+ %% This lightweight commit protocol is used when all
+ %% the involved tables are replicated symetrically.
+ %% Their storage types must match on each node.
+ %%
+ %% 1 Ask the other involved nodes if they want to commit
+ %% All involved nodes votes yes if they are up
+ %% 2a Somebody has voted no
+ %% Tell all yes voters to do_abort
+ %% 2b Everybody has voted yes
+ %% Tell everybody to do_commit. I.e. that they should
+ %% prepare the commit, log the commit record and
+ %% perform the updates.
+ %%
+ %% The outcome is kept 3 minutes in the transient decision table.
+ %%
+ %% Recovery:
+ %% If somebody dies before the coordinator has
+ %% broadcasted do_commit, the transaction is aborted.
+ %%
+ %% If a participant dies, the table load algorithm
+ %% ensures that the contents of the involved tables
+ %% are picked from another node.
+ %%
+ %% If the coordinator dies, each participants checks
+ %% the outcome with all the others. If all are uncertain
+ %% about the outcome, the transaction is aborted. If
+ %% somebody knows the outcome the others will follow.
+
+ {DiscNs, RamNs} = commit_nodes(CR, [], []),
+ Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
+ ?ets_insert(Store, Pending),
+
+ {WaitFor, Local} = ask_commit(sym_trans, Tid, CR, DiscNs, RamNs),
+ {Outcome, []} = rec_all(WaitFor, Tid, do_commit, []),
+ ?eval_debug_fun({?MODULE, multi_commit_sym},
+ [{tid, Tid}, {outcome, Outcome}]),
+ rpc:abcast(DiscNs -- [node()], ?MODULE, {Tid, Outcome}),
+ rpc:abcast(RamNs -- [node()], ?MODULE, {Tid, Outcome}),
+ case Outcome of
+ do_commit ->
+ mnesia_recover:note_decision(Tid, committed),
+ do_dirty(Tid, Local),
+ mnesia_locker:release_tid(Tid),
+ ?MODULE ! {delete_transaction, Tid};
+ {do_abort, _Reason} ->
+ mnesia_recover:note_decision(Tid, aborted)
+ end,
+ ?eval_debug_fun({?MODULE, multi_commit_sym, post},
+ [{tid, Tid}, {outcome, Outcome}]),
+ Outcome;
+
+multi_commit(sync_sym_trans, Tid, CR, Store) ->
+ %% This protocol is the same as sym_trans except that it
+ %% uses syncronized calls to disk_log and syncronized commits
+ %% when several nodes are involved.
+
+ {DiscNs, RamNs} = commit_nodes(CR, [], []),
+ Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
+ ?ets_insert(Store, Pending),
+
+ {WaitFor, Local} = ask_commit(sync_sym_trans, Tid, CR, DiscNs, RamNs),
+ {Outcome, []} = rec_all(WaitFor, Tid, do_commit, []),
+ ?eval_debug_fun({?MODULE, multi_commit_sym_sync},
+ [{tid, Tid}, {outcome, Outcome}]),
+ rpc:abcast(DiscNs -- [node()], ?MODULE, {Tid, Outcome}),
+ rpc:abcast(RamNs -- [node()], ?MODULE, {Tid, Outcome}),
+ case Outcome of
+ do_commit ->
+ mnesia_recover:note_decision(Tid, committed),
+ mnesia_log:slog(Local),
+ do_commit(Tid, Local),
+ %% Just wait for completion result is ignore.
+ rec_all(WaitFor, Tid, ignore, []),
+ mnesia_locker:release_tid(Tid),
+ ?MODULE ! {delete_transaction, Tid};
+ {do_abort, _Reason} ->
+ mnesia_recover:note_decision(Tid, aborted)
+ end,
+ ?eval_debug_fun({?MODULE, multi_commit_sym, post},
+ [{tid, Tid}, {outcome, Outcome}]),
+ Outcome;
+
+multi_commit(asym_trans, Tid, CR, Store) ->
+ %% This more expensive commit protocol is used when
+ %% table definitions are changed (schema transactions).
+ %% It is also used when the involved tables are
+ %% replicated asymetrically. If the storage type differs
+ %% on at least one node this protocol is used.
+ %%
+ %% 1 Ask the other involved nodes if they want to commit.
+ %% All involved nodes prepares the commit, logs a presume_abort
+ %% commit record and votes yes or no depending of the
+ %% outcome of the prepare. The preparation is also performed
+ %% by the coordinator.
+ %%
+ %% 2a Somebody has died or voted no
+ %% Tell all yes voters to do_abort
+ %% 2b Everybody has voted yes
+ %% Put a unclear marker in the log.
+ %% Tell the others to pre_commit. I.e. that they should
+ %% put a unclear marker in the log and reply
+ %% acc_pre_commit when they are done.
+ %%
+ %% 3a Somebody died
+ %% Tell the remaining participants to do_abort
+ %% 3b Everybody has replied acc_pre_commit
+ %% Tell everybody to committed. I.e that they should
+ %% put a committed marker in the log, perform the updates
+ %% and reply done_commit when they are done. The coordinator
+ %% must wait with putting his committed marker inte the log
+ %% until the committed has been sent to all the others.
+ %% Then he performs local commit before collecting replies.
+ %%
+ %% 4 Everybody has either died or replied done_commit
+ %% Return to the caller.
+ %%
+ %% Recovery:
+ %% If the coordinator dies, the participants (and
+ %% the coordinator when he starts again) must do
+ %% the following:
+ %%
+ %% If we have no unclear marker in the log we may
+ %% safely abort, since we know that nobody may have
+ %% decided to commit yet.
+ %%
+ %% If we have a committed marker in the log we may
+ %% safely commit since we know that everybody else
+ %% also will come to this conclusion.
+ %%
+ %% If we have a unclear marker but no committed
+ %% in the log we are uncertain about the real outcome
+ %% of the transaction and must ask the others before
+ %% we can decide what to do. If someone knows the
+ %% outcome we will do the same. If nobody knows, we
+ %% will wait for the remaining involved nodes to come
+ %% up. When all involved nodes are up and uncertain,
+ %% we decide to commit (first put a committed marker
+ %% in the log, then do the updates).
+
+ D = #decision{tid = Tid, outcome = presume_abort},
+ {D2, CR2} = commit_decision(D, CR, [], []),
+ DiscNs = D2#decision.disc_nodes,
+ RamNs = D2#decision.ram_nodes,
+ Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
+ ?ets_insert(Store, Pending),
+ {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs),
+ SchemaPrep = (catch mnesia_schema:prepare_commit(Tid, Local, {coord, WaitFor})),
+ {Votes, Pids} = rec_all(WaitFor, Tid, do_commit, []),
+
+ ?eval_debug_fun({?MODULE, multi_commit_asym_got_votes},
+ [{tid, Tid}, {votes, Votes}]),
+ case Votes of
+ do_commit ->
+ case SchemaPrep of
+ {_Modified, C, DumperMode} when record(C, commit) ->
+ mnesia_log:log(C), % C is not a binary
+ ?eval_debug_fun({?MODULE, multi_commit_asym_log_commit_rec},
+ [{tid, Tid}]),
+
+ D3 = C#commit.decision,
+ D4 = D3#decision{outcome = unclear},
+ mnesia_recover:log_decision(D4),
+ ?eval_debug_fun({?MODULE, multi_commit_asym_log_commit_dec},
+ [{tid, Tid}]),
+ tell_participants(Pids, {Tid, pre_commit}),
+ %% Now we are uncertain and we do not know
+ %% if all participants have logged that
+ %% they are uncertain or not
+ rec_acc_pre_commit(Pids, Tid, Store, C,
+ do_commit, DumperMode, [], []);
+ {'EXIT', Reason} ->
+ %% The others have logged the commit
+ %% record but they are not uncertain
+ mnesia_recover:note_decision(Tid, aborted),
+ ?eval_debug_fun({?MODULE, multi_commit_asym_prepare_exit},
+ [{tid, Tid}]),
+ tell_participants(Pids, {Tid, {do_abort, Reason}}),
+ do_abort(Tid, Local),
+ {do_abort, Reason}
+ end;
+
+ {do_abort, Reason} ->
+ %% The others have logged the commit
+ %% record but they are not uncertain
+ mnesia_recover:note_decision(Tid, aborted),
+ ?eval_debug_fun({?MODULE, multi_commit_asym_do_abort}, [{tid, Tid}]),
+ tell_participants(Pids, {Tid, {do_abort, Reason}}),
+ do_abort(Tid, Local),
+ {do_abort, Reason}
+ end.
+
+%% Returns do_commit or {do_abort, Reason}
+rec_acc_pre_commit([Pid | Tail], Tid, Store, Commit, Res, DumperMode,
+ GoodPids, SchemaAckPids) ->
+ receive
+ {?MODULE, _, {acc_pre_commit, Tid, Pid, true}} ->
+ rec_acc_pre_commit(Tail, Tid, Store, Commit, Res, DumperMode,
+ [Pid | GoodPids], [Pid | SchemaAckPids]);
+
+ {?MODULE, _, {acc_pre_commit, Tid, Pid, false}} ->
+ rec_acc_pre_commit(Tail, Tid, Store, Commit, Res, DumperMode,
+ [Pid | GoodPids], SchemaAckPids);
+
+ {?MODULE, _, {acc_pre_commit, Tid, Pid}} ->
+ %% Kept for backwards compatibility. Remove after Mnesia 4.x
+ rec_acc_pre_commit(Tail, Tid, Store, Commit, Res, DumperMode,
+ [Pid | GoodPids], [Pid | SchemaAckPids]);
+
+ {mnesia_down, Node} when Node == node(Pid) ->
+ AbortRes = {do_abort, {bad_commit, Node}},
+ rec_acc_pre_commit(Tail, Tid, Store, Commit, AbortRes, DumperMode,
+ GoodPids, SchemaAckPids)
+ end;
+rec_acc_pre_commit([], Tid, Store, Commit, Res, DumperMode, GoodPids, SchemaAckPids) ->
+ D = Commit#commit.decision,
+ case Res of
+ do_commit ->
+ %% Now everybody knows that the others
+ %% has voted yes. We also know that
+ %% everybody are uncertain.
+ prepare_sync_schema_commit(Store, SchemaAckPids),
+ tell_participants(GoodPids, {Tid, committed}),
+ D2 = D#decision{outcome = committed},
+ mnesia_recover:log_decision(D2),
+ ?eval_debug_fun({?MODULE, rec_acc_pre_commit_log_commit},
+ [{tid, Tid}]),
+
+ %% Now we have safely logged committed
+ %% and we can recover without asking others
+ do_commit(Tid, Commit, DumperMode),
+ ?eval_debug_fun({?MODULE, rec_acc_pre_commit_done_commit},
+ [{tid, Tid}]),
+ sync_schema_commit(Tid, Store, SchemaAckPids),
+ mnesia_locker:release_tid(Tid),
+ ?MODULE ! {delete_transaction, Tid};
+
+ {do_abort, Reason} ->
+ tell_participants(GoodPids, {Tid, {do_abort, Reason}}),
+ D2 = D#decision{outcome = aborted},
+ mnesia_recover:log_decision(D2),
+ ?eval_debug_fun({?MODULE, rec_acc_pre_commit_log_abort},
+ [{tid, Tid}]),
+ do_abort(Tid, Commit),
+ ?eval_debug_fun({?MODULE, rec_acc_pre_commit_done_abort},
+ [{tid, Tid}])
+ end,
+ Res.
+
+%% Note all nodes in case of mnesia_down mgt
+prepare_sync_schema_commit(_Store, []) ->
+ ok;
+prepare_sync_schema_commit(Store, [Pid | Pids]) ->
+ ?ets_insert(Store, {waiting_for_commit_ack, node(Pid)}),
+ prepare_sync_schema_commit(Store, Pids).
+
+sync_schema_commit(_Tid, _Store, []) ->
+ ok;
+sync_schema_commit(Tid, Store, [Pid | Tail]) ->
+ receive
+ {?MODULE, _, {schema_commit, Tid, Pid}} ->
+ ?ets_match_delete(Store, {waiting_for_commit_ack, node(Pid)}),
+ sync_schema_commit(Tid, Store, Tail);
+
+ {mnesia_down, Node} when Node == node(Pid) ->
+ ?ets_match_delete(Store, {waiting_for_commit_ack, Node}),
+ sync_schema_commit(Tid, Store, Tail)
+ end.
+
+tell_participants([Pid | Pids], Msg) ->
+ Pid ! Msg,
+ tell_participants(Pids, Msg);
+tell_participants([], _Msg) ->
+ ok.
+
+%% No need for trapping exits. We are only linked
+%% to mnesia_tm and if it dies we should also die.
+%% The same goes for disk_log and dets.
+commit_participant(Coord, Tid, Bin, DiscNs, RamNs) when binary(Bin) ->
+ Commit = binary_to_term(Bin),
+ commit_participant(Coord, Tid, Bin, Commit, DiscNs, RamNs);
+commit_participant(Coord, Tid, C, DiscNs, RamNs) when record(C, commit) ->
+ commit_participant(Coord, Tid, C, C, DiscNs, RamNs).
+
+commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
+ ?eval_debug_fun({?MODULE, commit_participant, pre}, [{tid, Tid}]),
+ case catch mnesia_schema:prepare_commit(Tid, C0, {part, Coord}) of
+ {Modified, C, DumperMode} when record(C, commit) ->
+ %% If we can not find any local unclear decision
+ %% we should presume abort at startup recovery
+ case lists:member(node(), DiscNs) of
+ false ->
+ ignore;
+ true ->
+ case Modified of
+ false -> mnesia_log:log(Bin);
+ true -> mnesia_log:log(C)
+ end
+ end,
+ ?eval_debug_fun({?MODULE, commit_participant, vote_yes},
+ [{tid, Tid}]),
+ reply(Coord, {vote_yes, Tid, self()}),
+
+ receive
+ {Tid, pre_commit} ->
+ D = C#commit.decision,
+ mnesia_recover:log_decision(D#decision{outcome = unclear}),
+ ?eval_debug_fun({?MODULE, commit_participant, pre_commit},
+ [{tid, Tid}]),
+ Expect_schema_ack = C#commit.schema_ops /= [],
+ reply(Coord, {acc_pre_commit, Tid, self(), Expect_schema_ack}),
+
+ %% Now we are vulnerable for failures, since
+ %% we cannot decide without asking others
+ receive
+ {Tid, committed} ->
+ mnesia_recover:log_decision(D#decision{outcome = committed}),
+ ?eval_debug_fun({?MODULE, commit_participant, log_commit},
+ [{tid, Tid}]),
+ do_commit(Tid, C, DumperMode),
+ case Expect_schema_ack of
+ false -> ignore;
+ true -> reply(Coord, {schema_commit, Tid, self()})
+ end,
+ ?eval_debug_fun({?MODULE, commit_participant, do_commit},
+ [{tid, Tid}]);
+
+ {Tid, {do_abort, _Reason}} ->
+ mnesia_recover:log_decision(D#decision{outcome = aborted}),
+ ?eval_debug_fun({?MODULE, commit_participant, log_abort},
+ [{tid, Tid}]),
+ mnesia_schema:undo_prepare_commit(Tid, C),
+ ?eval_debug_fun({?MODULE, commit_participant, undo_prepare},
+ [{tid, Tid}]);
+
+ {'EXIT', _, _} ->
+ mnesia_recover:log_decision(D#decision{outcome = aborted}),
+ ?eval_debug_fun({?MODULE, commit_participant, exit_log_abort},
+ [{tid, Tid}]),
+ mnesia_schema:undo_prepare_commit(Tid, C),
+ ?eval_debug_fun({?MODULE, commit_participant, exit_undo_prepare},
+ [{tid, Tid}]);
+
+ Msg ->
+ verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n",
+ [Tid, Msg])
+ end;
+ {Tid, {do_abort, _Reason}} ->
+ mnesia_schema:undo_prepare_commit(Tid, C),
+ ?eval_debug_fun({?MODULE, commit_participant, pre_commit_undo_prepare},
+ [{tid, Tid}]);
+
+ {'EXIT', _, _} ->
+ mnesia_schema:undo_prepare_commit(Tid, C),
+ ?eval_debug_fun({?MODULE, commit_participant, pre_commit_undo_prepare}, [{tid, Tid}]);
+
+ Msg ->
+ verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n",
+ [Tid, Msg])
+ end;
+
+ {'EXIT', Reason} ->
+ ?eval_debug_fun({?MODULE, commit_participant, vote_no},
+ [{tid, Tid}]),
+ reply(Coord, {vote_no, Tid, Reason}),
+ mnesia_schema:undo_prepare_commit(Tid, C0)
+ end,
+ mnesia_locker:release_tid(Tid),
+ ?MODULE ! {delete_transaction, Tid},
+ unlink(whereis(?MODULE)),
+ exit(normal).
+
+do_abort(Tid, Bin) when binary(Bin) ->
+ %% Possible optimization:
+ %% If we want we could pass arround a flag
+ %% that tells us whether the binary contains
+ %% schema ops or not. Only if the binary
+ %% contains schema ops there are meningful
+ %% unpack the binary and perform
+ %% mnesia_schema:undo_prepare_commit/1.
+ do_abort(Tid, binary_to_term(Bin));
+do_abort(Tid, Commit) ->
+ mnesia_schema:undo_prepare_commit(Tid, Commit),
+ Commit.
+
+do_dirty(Tid, Commit) when Commit#commit.schema_ops == [] ->
+ mnesia_log:log(Commit),
+ do_commit(Tid, Commit).
+
+%% do_commit(Tid, CommitRecord)
+do_commit(Tid, Bin) when binary(Bin) ->
+ do_commit(Tid, binary_to_term(Bin));
+do_commit(Tid, C) ->
+ do_commit(Tid, C, optional).
+do_commit(Tid, Bin, DumperMode) when binary(Bin) ->
+ do_commit(Tid, binary_to_term(Bin), DumperMode);
+do_commit(Tid, C, DumperMode) ->
+ mnesia_dumper:update(Tid, C#commit.schema_ops, DumperMode),
+ R = do_snmp(Tid, C#commit.snmp),
+ R2 = do_update(Tid, ram_copies, C#commit.ram_copies, R),
+ R3 = do_update(Tid, disc_copies, C#commit.disc_copies, R2),
+ do_update(Tid, disc_only_copies, C#commit.disc_only_copies, R3).
+
+%% Update the items
+do_update(Tid, Storage, [Op | Ops], OldRes) ->
+ case catch do_update_op(Tid, Storage, Op) of
+ ok ->
+ do_update(Tid, Storage, Ops, OldRes);
+ {'EXIT', Reason} ->
+ %% This may only happen when we recently have
+ %% deleted our local replica, changed storage_type
+ %% or transformed table
+ %% BUGBUG: Updates may be lost if storage_type is changed.
+ %% Determine actual storage type and try again.
+ %% BUGBUG: Updates may be lost if table is transformed.
+
+ verbose("do_update in ~w failed: ~p -> {'EXIT', ~p}~n",
+ [Tid, Op, Reason]),
+ do_update(Tid, Storage, Ops, OldRes);
+ NewRes ->
+ do_update(Tid, Storage, Ops, NewRes)
+ end;
+do_update(_Tid, _Storage, [], Res) ->
+ Res.
+
+do_update_op(Tid, Storage, {{Tab, K}, Obj, write}) ->
+ commit_write(?catch_val({Tab, commit_work}), Tid,
+ Tab, K, Obj, undefined),
+ mnesia_lib:db_put(Storage, Tab, Obj);
+
+do_update_op(Tid, Storage, {{Tab, K}, Val, delete}) ->
+ commit_delete(?catch_val({Tab, commit_work}), Tid, Tab, K, Val, undefined),
+ mnesia_lib:db_erase(Storage, Tab, K);
+
+do_update_op(Tid, Storage, {{Tab, K}, {RecName, Incr}, update_counter}) ->
+ {NewObj, OldObjs} =
+ case catch mnesia_lib:db_update_counter(Storage, Tab, K, Incr) of
+ NewVal when integer(NewVal), NewVal >= 0 ->
+ {{RecName, K, NewVal}, [{RecName, K, NewVal - Incr}]};
+ _ ->
+ Zero = {RecName, K, 0},
+ mnesia_lib:db_put(Storage, Tab, Zero),
+ {Zero, []}
+ end,
+ commit_update(?catch_val({Tab, commit_work}), Tid, Tab,
+ K, NewObj, OldObjs),
+ element(3, NewObj);
+
+do_update_op(Tid, Storage, {{Tab, Key}, Obj, delete_object}) ->
+ commit_del_object(?catch_val({Tab, commit_work}),
+ Tid, Tab, Key, Obj, undefined),
+ mnesia_lib:db_match_erase(Storage, Tab, Obj);
+
+do_update_op(Tid, Storage, {{Tab, Key}, Obj, clear_table}) ->
+ commit_clear(?catch_val({Tab, commit_work}), Tid, Tab, Key, Obj),
+ mnesia_lib:db_match_erase(Storage, Tab, Obj).
+
+commit_write([], _, _, _, _, _) -> ok;
+commit_write([{checkpoints, CpList}|R], Tid, Tab, K, Obj, Old) ->
+ mnesia_checkpoint:tm_retain(Tid, Tab, K, write, CpList),
+ commit_write(R, Tid, Tab, K, Obj, Old);
+commit_write([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == subscribers ->
+ mnesia_subscr:report_table_event(H, Tab, Tid, Obj, write, Old),
+ commit_write(R, Tid, Tab, K, Obj, Old);
+commit_write([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == index ->
+ mnesia_index:add_index(H, Tab, K, Obj, Old),
+ commit_write(R, Tid, Tab, K, Obj, Old).
+
+commit_update([], _, _, _, _, _) -> ok;
+commit_update([{checkpoints, CpList}|R], Tid, Tab, K, Obj, _) ->
+ Old = mnesia_checkpoint:tm_retain(Tid, Tab, K, write, CpList),
+ commit_update(R, Tid, Tab, K, Obj, Old);
+commit_update([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == subscribers ->
+ mnesia_subscr:report_table_event(H, Tab, Tid, Obj, write, Old),
+ commit_update(R, Tid, Tab, K, Obj, Old);
+commit_update([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == index ->
+ mnesia_index:add_index(H, Tab, K, Obj, Old),
+ commit_update(R, Tid, Tab, K, Obj, Old).
+
+commit_delete([], _, _, _, _, _) -> ok;
+commit_delete([{checkpoints, CpList}|R], Tid, Tab, K, Obj, _) ->
+ Old = mnesia_checkpoint:tm_retain(Tid, Tab, K, delete, CpList),
+ commit_delete(R, Tid, Tab, K, Obj, Old);
+commit_delete([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == subscribers ->
+ mnesia_subscr:report_table_event(H, Tab, Tid, Obj, delete, Old),
+ commit_delete(R, Tid, Tab, K, Obj, Old);
+commit_delete([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == index ->
+ mnesia_index:delete_index(H, Tab, K),
+ commit_delete(R, Tid, Tab, K, Obj, Old).
+
+commit_del_object([], _, _, _, _, _) -> ok;
+commit_del_object([{checkpoints, CpList}|R], Tid, Tab, K, Obj, _) ->
+ Old = mnesia_checkpoint:tm_retain(Tid, Tab, K, delete_object, CpList),
+ commit_del_object(R, Tid, Tab, K, Obj, Old);
+commit_del_object([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == subscribers ->
+ mnesia_subscr:report_table_event(H, Tab, Tid, Obj, delete_object, Old),
+ commit_del_object(R, Tid, Tab, K, Obj, Old);
+commit_del_object([H|R], Tid, Tab, K, Obj, Old)
+ when element(1, H) == index ->
+ mnesia_index:del_object_index(H, Tab, K, Obj, Old),
+ commit_del_object(R, Tid, Tab, K, Obj, Old).
+
+commit_clear([], _, _, _, _) -> ok;
+commit_clear([{checkpoints, CpList}|R], Tid, Tab, K, Obj) ->
+ mnesia_checkpoint:tm_retain(Tid, Tab, K, clear_table, CpList),
+ commit_clear(R, Tid, Tab, K, Obj);
+commit_clear([H|R], Tid, Tab, K, Obj)
+ when element(1, H) == subscribers ->
+ mnesia_subscr:report_table_event(H, Tab, Tid, Obj, clear_table, undefined),
+ commit_clear(R, Tid, Tab, K, Obj);
+commit_clear([H|R], Tid, Tab, K, Obj)
+ when element(1, H) == index ->
+ mnesia_index:clear_index(H, Tab, K, Obj),
+ commit_clear(R, Tid, Tab, K, Obj).
+
+do_snmp(_, []) -> ok;
+do_snmp(Tid, [Head | Tail]) ->
+ case catch mnesia_snmp_hook:update(Head) of
+ {'EXIT', Reason} ->
+ %% This should only happen when we recently have
+ %% deleted our local replica or recently deattached
+ %% the snmp table
+
+ verbose("do_snmp in ~w failed: ~p -> {'EXIT', ~p}~n",
+ [Tid, Head, Reason]);
+ ok ->
+ ignore
+ end,
+ do_snmp(Tid, Tail).
+
+commit_nodes([C | Tail], AccD, AccR)
+ when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [],
+ C#commit.schema_ops == [] ->
+ commit_nodes(Tail, AccD, [C#commit.node | AccR]);
+commit_nodes([C | Tail], AccD, AccR) ->
+ commit_nodes(Tail, [C#commit.node | AccD], AccR);
+commit_nodes([], AccD, AccR) ->
+ {AccD, AccR}.
+
+commit_decision(D, [C | Tail], AccD, AccR) ->
+ N = C#commit.node,
+ {D2, Tail2} =
+ case C#commit.schema_ops of
+ [] when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [] ->
+ commit_decision(D, Tail, AccD, [N | AccR]);
+ [] ->
+ commit_decision(D, Tail, [N | AccD], AccR);
+ Ops ->
+ case ram_only_ops(N, Ops) of
+ true ->
+ commit_decision(D, Tail, AccD, [N | AccR]);
+ false ->
+ commit_decision(D, Tail, [N | AccD], AccR)
+ end
+ end,
+ {D2, [C#commit{decision = D2} | Tail2]};
+commit_decision(D, [], AccD, AccR) ->
+ {D#decision{disc_nodes = AccD, ram_nodes = AccR}, []}.
+
+ram_only_ops(N, [{op, change_table_copy_type, N, _FromS, _ToS, Cs} | _Ops ]) ->
+ case lists:member({name, schema}, Cs) of
+ true ->
+ %% We always use disk if change type of the schema
+ false;
+ false ->
+ not lists:member(N, val({schema, disc_copies}))
+ end;
+
+ram_only_ops(N, _Ops) ->
+ not lists:member(N, val({schema, disc_copies})).
+
+%% Returns {WaitFor, Res}
+sync_send_dirty(Tid, [Head | Tail], Tab, WaitFor) ->
+ Node = Head#commit.node,
+ if
+ Node == node() ->
+ {WF, _} = sync_send_dirty(Tid, Tail, Tab, WaitFor),
+ Res = do_dirty(Tid, Head),
+ {WF, Res};
+ true ->
+ {?MODULE, Node} ! {self(), {sync_dirty, Tid, Head, Tab}},
+ sync_send_dirty(Tid, Tail, Tab, [Node | WaitFor])
+ end;
+sync_send_dirty(_Tid, [], _Tab, WaitFor) ->
+ {WaitFor, {'EXIT', {aborted, {node_not_running, WaitFor}}}}.
+
+%% Returns {WaitFor, Res}
+async_send_dirty(_Tid, _Nodes, Tab, nowhere) ->
+ {[], {'EXIT', {aborted, {no_exists, Tab}}}};
+async_send_dirty(Tid, Nodes, Tab, ReadNode) ->
+ async_send_dirty(Tid, Nodes, Tab, ReadNode, [], ok).
+
+async_send_dirty(Tid, [Head | Tail], Tab, ReadNode, WaitFor, Res) ->
+ Node = Head#commit.node,
+ if
+ ReadNode == Node, Node == node() ->
+ NewRes = do_dirty(Tid, Head),
+ async_send_dirty(Tid, Tail, Tab, ReadNode, WaitFor, NewRes);
+ ReadNode == Node ->
+ {?MODULE, Node} ! {self(), {sync_dirty, Tid, Head, Tab}},
+ NewRes = {'EXIT', {aborted, {node_not_running, Node}}},
+ async_send_dirty(Tid, Tail, Tab, ReadNode, [Node | WaitFor], NewRes);
+ true ->
+ {?MODULE, Node} ! {self(), {async_dirty, Tid, Head, Tab}},
+ async_send_dirty(Tid, Tail, Tab, ReadNode, WaitFor, Res)
+ end;
+async_send_dirty(_Tid, [], _Tab, _ReadNode, WaitFor, Res) ->
+ {WaitFor, Res}.
+
+rec_dirty([Node | Tail], Res) when Node /= node() ->
+ NewRes = get_dirty_reply(Node, Res),
+ rec_dirty(Tail, NewRes);
+rec_dirty([], Res) ->
+ Res.
+
+get_dirty_reply(Node, Res) ->
+ receive
+ {?MODULE, Node, {'EXIT', Reason}} ->
+ {'EXIT', {aborted, {badarg, Reason}}};
+ {?MODULE, Node, {dirty_res, ok}} ->
+ case Res of
+ {'EXIT', {aborted, {node_not_running, _Node}}} ->
+ ok;
+ _ ->
+ %% Prioritize bad results, but node_not_running
+ Res
+ end;
+ {?MODULE, Node, {dirty_res, Reply}} ->
+ Reply;
+ {mnesia_down, Node} ->
+ %% It's ok to ignore mnesia_down's
+ %% since we will make the replicas
+ %% consistent again when Node is started
+ Res
+ after 1000 ->
+ case lists:member(Node, val({current, db_nodes})) of
+ true ->
+ get_dirty_reply(Node, Res);
+ false ->
+ Res
+ end
+ end.
+
+%% Assume that CommitRecord is no binary
+%% Return {Res, Pids}
+ask_commit(Protocol, Tid, CR, DiscNs, RamNs) ->
+ ask_commit(Protocol, Tid, CR, DiscNs, RamNs, [], no_local).
+
+ask_commit(Protocol, Tid, [Head | Tail], DiscNs, RamNs, WaitFor, Local) ->
+ Node = Head#commit.node,
+ if
+ Node == node() ->
+ ask_commit(Protocol, Tid, Tail, DiscNs, RamNs, WaitFor, Head);
+ true ->
+ Bin = opt_term_to_binary(Protocol, Head, DiscNs++RamNs),
+ Msg = {ask_commit, Protocol, Tid, Bin, DiscNs, RamNs},
+ {?MODULE, Node} ! {self(), Msg},
+ ask_commit(Protocol, Tid, Tail, DiscNs, RamNs, [Node | WaitFor], Local)
+ end;
+ask_commit(_Protocol, _Tid, [], _DiscNs, _RamNs, WaitFor, Local) ->
+ {WaitFor, Local}.
+
+opt_term_to_binary(asym_trans, Head, Nodes) ->
+ opt_term_to_binary(Nodes, Head);
+opt_term_to_binary(_Protocol, Head, _Nodes) ->
+ Head.
+
+opt_term_to_binary([], Head) ->
+ term_to_binary(Head);
+opt_term_to_binary([H|R], Head) ->
+ case mnesia_monitor:needs_protocol_conversion(H) of
+ true -> Head;
+ false ->
+ opt_term_to_binary(R, Head)
+ end.
+
+rec_all([Node | Tail], Tid, Res, Pids) ->
+ receive
+ {?MODULE, Node, {vote_yes, Tid}} ->
+ rec_all(Tail, Tid, Res, Pids);
+ {?MODULE, Node, {vote_yes, Tid, Pid}} ->
+ rec_all(Tail, Tid, Res, [Pid | Pids]);
+ {?MODULE, Node, {vote_no, Tid, Reason}} ->
+ rec_all(Tail, Tid, {do_abort, Reason}, Pids);
+ {?MODULE, Node, {committed, Tid}} ->
+ rec_all(Tail, Tid, Res, Pids);
+ {?MODULE, Node, {aborted, Tid}} ->
+ rec_all(Tail, Tid, Res, Pids);
+
+ {mnesia_down, Node} ->
+ rec_all(Tail, Tid, {do_abort, {bad_commit, Node}}, Pids)
+ end;
+rec_all([], _Tid, Res, Pids) ->
+ {Res, Pids}.
+
+get_transactions() ->
+ {info, Participant, Coordinator} = req(info),
+ lists:map(fun({Tid, _Tabs}) ->
+ Status = tr_status(Tid,Participant),
+ {Tid#tid.counter, Tid#tid.pid, Status}
+ end,Coordinator).
+
+tr_status(Tid,Participant) ->
+ case lists:keymember(Tid, 1, Participant) of
+ true -> participant;
+ false -> coordinator
+ end.
+
+get_info(Timeout) ->
+ case whereis(?MODULE) of
+ undefined ->
+ {timeout, Timeout};
+ Pid ->
+ Pid ! {self(), info},
+ receive
+ {?MODULE, _, {info, Part, Coord}} ->
+ {info, Part, Coord}
+ after Timeout ->
+ {timeout, Timeout}
+ end
+ end.
+
+display_info(Stream, {timeout, T}) ->
+ io:format(Stream, "---> No info about coordinator and participant transactions, "
+ "timeout ~p <--- ~n", [T]);
+
+display_info(Stream, {info, Part, Coord}) ->
+ io:format(Stream, "---> Participant transactions <--- ~n", []),
+ lists:foreach(fun(P) -> pr_participant(Stream, P) end, Part),
+ io:format(Stream, "---> Coordinator transactions <---~n", []),
+ lists:foreach(fun({Tid, _Tabs}) -> pr_tid(Stream, Tid) end, Coord).
+
+pr_participant(Stream, P) ->
+ Commit0 = P#participant.commit,
+ Commit =
+ if
+ binary(Commit0) -> binary_to_term(Commit0);
+ true -> Commit0
+ end,
+ pr_tid(Stream, P#participant.tid),
+ io:format(Stream, "with participant objects ~p~n", [Commit]).
+
+
+pr_tid(Stream, Tid) ->
+ io:format(Stream, "Tid: ~p (owned by ~p) ~n",
+ [Tid#tid.counter, Tid#tid.pid]).
+
+info(Serial) ->
+ io:format( "Info about transaction with serial == ~p~n", [Serial]),
+ {info, Participant, Trs} = req(info),
+ search_pr_participant(Serial, Participant),
+ search_pr_coordinator(Serial, Trs).
+
+
+search_pr_coordinator(_S, []) -> no;
+search_pr_coordinator(S, [{Tid, _Ts}|Tail]) ->
+ case Tid#tid.counter of
+ S ->
+ io:format( "Tid is coordinator, owner == \n", []),
+ display_pid_info(Tid#tid.pid),
+ search_pr_coordinator(S, Tail);
+ _ ->
+ search_pr_coordinator(S, Tail)
+ end.
+
+search_pr_participant(_S, []) ->
+ false;
+search_pr_participant(S, [ P | Tail]) ->
+ Tid = P#participant.tid,
+ Commit0 = P#participant.commit,
+ if
+ Tid#tid.counter == S ->
+ io:format( "Tid is participant to commit, owner == \n", []),
+ Pid = Tid#tid.pid,
+ display_pid_info(Pid),
+ io:format( "Tid wants to write objects \n",[]),
+ Commit =
+ if
+ binary(Commit0) -> binary_to_term(Commit0);
+ true -> Commit0
+ end,
+
+ io:format("~p~n", [Commit]),
+ search_pr_participant(S,Tail); %% !!!!!
+ true ->
+ search_pr_participant(S, Tail)
+ end.
+
+display_pid_info(Pid) ->
+ case rpc:pinfo(Pid) of
+ undefined ->
+ io:format( "Dead process \n");
+ Info ->
+ Call = fetch(initial_call, Info),
+ Curr = case fetch(current_function, Info) of
+ {Mod,F,Args} when list(Args) ->
+ {Mod,F,length(Args)};
+ Other ->
+ Other
+ end,
+ Reds = fetch(reductions, Info),
+ LM = length(fetch(messages, Info)),
+ pformat(io_lib:format("~p", [Pid]),
+ io_lib:format("~p", [Call]),
+ io_lib:format("~p", [Curr]), Reds, LM)
+ end.
+
+pformat(A1, A2, A3, A4, A5) ->
+ io:format( "~-12s ~-21s ~-21s ~9w ~4w~n", [A1,A2,A3,A4,A5]).
+
+fetch(Key, Info) ->
+ case lists:keysearch(Key, 1, Info) of
+ {value, {_, Val}} ->
+ Val;
+ _ ->
+ 0
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%% reconfigure stuff comes here ......
+%%%%%%%%%%%%%%%%%%%%%
+
+reconfigure_coordinators(N, [{Tid, [Store | _]} | Coordinators]) ->
+ case mnesia_recover:outcome(Tid, unknown) of
+ committed ->
+ WaitingNodes = ?ets_lookup(Store, waiting_for_commit_ack),
+ case lists:keymember(N, 2, WaitingNodes) of
+ false ->
+ ignore; % avoid spurious mnesia_down messages
+ true ->
+ send_mnesia_down(Tid, Store, N)
+ end;
+ aborted ->
+ ignore; % avoid spurious mnesia_down messages
+ _ ->
+ %% Tell the coordinator about the mnesia_down
+ send_mnesia_down(Tid, Store, N)
+ end,
+ reconfigure_coordinators(N, Coordinators);
+reconfigure_coordinators(_N, []) ->
+ ok.
+
+send_mnesia_down(Tid, Store, Node) ->
+ Msg = {mnesia_down, Node},
+ send_to_pids([Tid#tid.pid | get_friends(Store)], Msg).
+
+send_to_pids([Pid | Pids], Msg) ->
+ Pid ! Msg,
+ send_to_pids(Pids, Msg);
+send_to_pids([], _Msg) ->
+ ok.
+
+reconfigure_participants(N, [P | Tail]) ->
+ case lists:member(N, P#participant.disc_nodes) or
+ lists:member(N, P#participant.ram_nodes) of
+ false ->
+ %% Ignore, since we are not a participant
+ %% in the transaction.
+ reconfigure_participants(N, Tail);
+
+ true ->
+ %% We are on a participant node, lets
+ %% check if the dead one was a
+ %% participant or a coordinator.
+ Tid = P#participant.tid,
+ if
+ node(Tid#tid.pid) /= N ->
+ %% Another participant node died. Ignore.
+ reconfigure_participants(N, Tail);
+
+ true ->
+ %% The coordinator node has died and
+ %% we must determine the outcome of the
+ %% transaction and tell mnesia_tm on all
+ %% nodes (including the local node) about it
+ verbose("Coordinator ~p in transaction ~p died~n",
+ [Tid#tid.pid, Tid]),
+
+ Nodes = P#participant.disc_nodes ++
+ P#participant.ram_nodes,
+ AliveNodes = Nodes -- [N],
+ Protocol = P#participant.protocol,
+ tell_outcome(Tid, Protocol, N, AliveNodes, AliveNodes),
+ reconfigure_participants(N, Tail)
+ end
+ end;
+reconfigure_participants(_, []) ->
+ [].
+
+%% We need to determine the outcome of the transaction and
+%% tell mnesia_tm on all involved nodes (including the local node)
+%% about the outcome.
+tell_outcome(Tid, Protocol, Node, CheckNodes, TellNodes) ->
+ Outcome = mnesia_recover:what_happened(Tid, Protocol, CheckNodes),
+ case Outcome of
+ aborted ->
+ rpc:abcast(TellNodes, ?MODULE, {Tid,{do_abort, {mnesia_down, Node}}});
+ committed ->
+ rpc:abcast(TellNodes, ?MODULE, {Tid, do_commit})
+ end,
+ Outcome.
+
+do_stop(#state{coordinators = Coordinators}) ->
+ Msg = {mnesia_down, node()},
+ lists:foreach(fun({Tid, _}) -> Tid#tid.pid ! Msg end, Coordinators),
+ mnesia_checkpoint:stop(),
+ mnesia_log:stop(),
+ exit(shutdown).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% System upgrade
+
+system_continue(_Parent, _Debug, State) ->
+ doit_loop(State).
+
+system_terminate(_Reason, _Parent, _Debug, State) ->
+ do_stop(State).
+
+system_code_change(State, _Module, _OldVsn, _Extra) ->
+ {ok, State}.
diff --git a/lib/dialyzer/test/race_tests_SUITE.erl b/lib/dialyzer/test/race_tests_SUITE.erl
new file mode 100644
index 0000000000..cfc898d464
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE.erl
@@ -0,0 +1,799 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(race_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([race_tests_SUITE_consistency/1, ets_insert_args1/1,
+ ets_insert_args2/1, ets_insert_args3/1, ets_insert_args4/1,
+ ets_insert_args5/1, ets_insert_args6/1, ets_insert_args7/1,
+ ets_insert_args8/1, ets_insert_control_flow1/1,
+ ets_insert_control_flow2/1, ets_insert_control_flow3/1,
+ ets_insert_control_flow4/1, ets_insert_control_flow5/1,
+ ets_insert_diff_atoms_race1/1, ets_insert_diff_atoms_race2/1,
+ ets_insert_diff_atoms_race3/1, ets_insert_diff_atoms_race4/1,
+ ets_insert_diff_atoms_race5/1, ets_insert_diff_atoms_race6/1,
+ ets_insert_double1/1, ets_insert_double2/1, ets_insert_funs1/1,
+ ets_insert_funs2/1, ets_insert_new/1, ets_insert_param/1,
+ extract_translations/1, mnesia_diff_atoms_race1/1,
+ mnesia_diff_atoms_race2/1, mnesia_dirty_read_one_write_two/1,
+ mnesia_dirty_read_two_write_one/1,
+ mnesia_dirty_read_write_double1/1,
+ mnesia_dirty_read_write_double2/1,
+ mnesia_dirty_read_write_double3/1,
+ mnesia_dirty_read_write_double4/1, mnesia_dirty_read_write_one/1,
+ mnesia_dirty_read_write_two/1, whereis_control_flow1/1,
+ whereis_control_flow2/1, whereis_control_flow3/1,
+ whereis_control_flow4/1, whereis_control_flow5/1,
+ whereis_control_flow6/1, whereis_diff_atoms_no_race/1,
+ whereis_diff_atoms_race/1, whereis_diff_functions1/1,
+ whereis_diff_functions1_nested/1,
+ whereis_diff_functions1_pathsens/1,
+ whereis_diff_functions1_twice/1, whereis_diff_functions2/1,
+ whereis_diff_functions2_nested/1,
+ whereis_diff_functions2_pathsens/1,
+ whereis_diff_functions2_twice/1, whereis_diff_functions3/1,
+ whereis_diff_functions3_nested/1,
+ whereis_diff_functions3_pathsens/1, whereis_diff_functions4/1,
+ whereis_diff_functions5/1, whereis_diff_functions6/1,
+ whereis_diff_modules1/1, whereis_diff_modules1_pathsens/1,
+ whereis_diff_modules1_rec/1, whereis_diff_modules2/1,
+ whereis_diff_modules2_pathsens/1, whereis_diff_modules2_rec/1,
+ whereis_diff_modules3/1, whereis_diff_modules_nested/1,
+ whereis_diff_modules_twice/1, whereis_diff_vars_no_race/1,
+ whereis_diff_vars_race/1, whereis_intra_inter_module1/1,
+ whereis_intra_inter_module2/1, whereis_intra_inter_module3/1,
+ whereis_intra_inter_module4/1, whereis_intra_inter_module5/1,
+ whereis_intra_inter_module6/1, whereis_intra_inter_module7/1,
+ whereis_intra_inter_module8/1, whereis_param/1,
+ whereis_param_inter_module/1, whereis_rec_function1/1,
+ whereis_rec_function2/1, whereis_rec_function3/1,
+ whereis_rec_function4/1, whereis_rec_function5/1,
+ whereis_rec_function6/1, whereis_rec_function7/1,
+ whereis_rec_function8/1, whereis_try_catch/1, whereis_vars1/1,
+ whereis_vars10/1, whereis_vars11/1, whereis_vars12/1,
+ whereis_vars13/1, whereis_vars14/1, whereis_vars15/1,
+ whereis_vars16/1, whereis_vars17/1, whereis_vars18/1,
+ whereis_vars19/1, whereis_vars2/1, whereis_vars20/1,
+ whereis_vars21/1, whereis_vars22/1, whereis_vars3/1,
+ whereis_vars4/1, whereis_vars5/1, whereis_vars6/1,
+ whereis_vars7/1, whereis_vars8/1, whereis_vars9/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, [{warnings,[race_conditions]}]}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [race_tests_SUITE_consistency,ets_insert_args1,ets_insert_args2,
+ ets_insert_args3,ets_insert_args4,ets_insert_args5,ets_insert_args6,
+ ets_insert_args7,ets_insert_args8,ets_insert_control_flow1,
+ ets_insert_control_flow2,ets_insert_control_flow3,ets_insert_control_flow4,
+ ets_insert_control_flow5,ets_insert_diff_atoms_race1,
+ ets_insert_diff_atoms_race2,ets_insert_diff_atoms_race3,
+ ets_insert_diff_atoms_race4,ets_insert_diff_atoms_race5,
+ ets_insert_diff_atoms_race6,ets_insert_double1,ets_insert_double2,
+ ets_insert_funs1,ets_insert_funs2,ets_insert_new,ets_insert_param,
+ extract_translations,mnesia_diff_atoms_race1,mnesia_diff_atoms_race2,
+ mnesia_dirty_read_one_write_two,mnesia_dirty_read_two_write_one,
+ mnesia_dirty_read_write_double1,mnesia_dirty_read_write_double2,
+ mnesia_dirty_read_write_double3,mnesia_dirty_read_write_double4,
+ mnesia_dirty_read_write_one,mnesia_dirty_read_write_two,
+ whereis_control_flow1,whereis_control_flow2,whereis_control_flow3,
+ whereis_control_flow4,whereis_control_flow5,whereis_control_flow6,
+ whereis_diff_atoms_no_race,whereis_diff_atoms_race,whereis_diff_functions1,
+ whereis_diff_functions1_nested,whereis_diff_functions1_pathsens,
+ whereis_diff_functions1_twice,whereis_diff_functions2,
+ whereis_diff_functions2_nested,whereis_diff_functions2_pathsens,
+ whereis_diff_functions2_twice,whereis_diff_functions3,
+ whereis_diff_functions3_nested,whereis_diff_functions3_pathsens,
+ whereis_diff_functions4,whereis_diff_functions5,whereis_diff_functions6,
+ whereis_diff_modules1,whereis_diff_modules1_pathsens,
+ whereis_diff_modules1_rec,whereis_diff_modules2,
+ whereis_diff_modules2_pathsens,whereis_diff_modules2_rec,
+ whereis_diff_modules3,whereis_diff_modules_nested,
+ whereis_diff_modules_twice,whereis_diff_vars_no_race,
+ whereis_diff_vars_race,whereis_intra_inter_module1,
+ whereis_intra_inter_module2,whereis_intra_inter_module3,
+ whereis_intra_inter_module4,whereis_intra_inter_module5,
+ whereis_intra_inter_module6,whereis_intra_inter_module7,
+ whereis_intra_inter_module8,whereis_param,whereis_param_inter_module,
+ whereis_rec_function1,whereis_rec_function2,whereis_rec_function3,
+ whereis_rec_function4,whereis_rec_function5,whereis_rec_function6,
+ whereis_rec_function7,whereis_rec_function8,whereis_try_catch,
+ whereis_vars1,whereis_vars10,whereis_vars11,whereis_vars12,whereis_vars13,
+ whereis_vars14,whereis_vars15,whereis_vars16,whereis_vars17,whereis_vars18,
+ whereis_vars19,whereis_vars2,whereis_vars20,whereis_vars21,whereis_vars22,
+ whereis_vars3,whereis_vars4,whereis_vars5,whereis_vars6,whereis_vars7,
+ whereis_vars8,whereis_vars9].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+race_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+ets_insert_args1(Config) ->
+ case dialyze(Config, ets_insert_args1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args2(Config) ->
+ case dialyze(Config, ets_insert_args2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args3(Config) ->
+ case dialyze(Config, ets_insert_args3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args4(Config) ->
+ case dialyze(Config, ets_insert_args4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args5(Config) ->
+ case dialyze(Config, ets_insert_args5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args6(Config) ->
+ case dialyze(Config, ets_insert_args6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args7(Config) ->
+ case dialyze(Config, ets_insert_args7) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_args8(Config) ->
+ case dialyze(Config, ets_insert_args8) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_control_flow1(Config) ->
+ case dialyze(Config, ets_insert_control_flow1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_control_flow2(Config) ->
+ case dialyze(Config, ets_insert_control_flow2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_control_flow3(Config) ->
+ case dialyze(Config, ets_insert_control_flow3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_control_flow4(Config) ->
+ case dialyze(Config, ets_insert_control_flow4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_control_flow5(Config) ->
+ case dialyze(Config, ets_insert_control_flow5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_diff_atoms_race1(Config) ->
+ case dialyze(Config, ets_insert_diff_atoms_race1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_diff_atoms_race2(Config) ->
+ case dialyze(Config, ets_insert_diff_atoms_race2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_diff_atoms_race3(Config) ->
+ case dialyze(Config, ets_insert_diff_atoms_race3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_diff_atoms_race4(Config) ->
+ case dialyze(Config, ets_insert_diff_atoms_race4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_diff_atoms_race5(Config) ->
+ case dialyze(Config, ets_insert_diff_atoms_race5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_diff_atoms_race6(Config) ->
+ case dialyze(Config, ets_insert_diff_atoms_race6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_double1(Config) ->
+ case dialyze(Config, ets_insert_double1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_double2(Config) ->
+ case dialyze(Config, ets_insert_double2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_funs1(Config) ->
+ case dialyze(Config, ets_insert_funs1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_funs2(Config) ->
+ case dialyze(Config, ets_insert_funs2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_new(Config) ->
+ case dialyze(Config, ets_insert_new) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_insert_param(Config) ->
+ case dialyze(Config, ets_insert_param) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+extract_translations(Config) ->
+ case dialyze(Config, extract_translations) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_diff_atoms_race1(Config) ->
+ case dialyze(Config, mnesia_diff_atoms_race1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_diff_atoms_race2(Config) ->
+ case dialyze(Config, mnesia_diff_atoms_race2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_one_write_two(Config) ->
+ case dialyze(Config, mnesia_dirty_read_one_write_two) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_two_write_one(Config) ->
+ case dialyze(Config, mnesia_dirty_read_two_write_one) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_write_double1(Config) ->
+ case dialyze(Config, mnesia_dirty_read_write_double1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_write_double2(Config) ->
+ case dialyze(Config, mnesia_dirty_read_write_double2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_write_double3(Config) ->
+ case dialyze(Config, mnesia_dirty_read_write_double3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_write_double4(Config) ->
+ case dialyze(Config, mnesia_dirty_read_write_double4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_write_one(Config) ->
+ case dialyze(Config, mnesia_dirty_read_write_one) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mnesia_dirty_read_write_two(Config) ->
+ case dialyze(Config, mnesia_dirty_read_write_two) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_control_flow1(Config) ->
+ case dialyze(Config, whereis_control_flow1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_control_flow2(Config) ->
+ case dialyze(Config, whereis_control_flow2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_control_flow3(Config) ->
+ case dialyze(Config, whereis_control_flow3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_control_flow4(Config) ->
+ case dialyze(Config, whereis_control_flow4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_control_flow5(Config) ->
+ case dialyze(Config, whereis_control_flow5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_control_flow6(Config) ->
+ case dialyze(Config, whereis_control_flow6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_atoms_no_race(Config) ->
+ case dialyze(Config, whereis_diff_atoms_no_race) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_atoms_race(Config) ->
+ case dialyze(Config, whereis_diff_atoms_race) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions1(Config) ->
+ case dialyze(Config, whereis_diff_functions1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions1_nested(Config) ->
+ case dialyze(Config, whereis_diff_functions1_nested) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions1_pathsens(Config) ->
+ case dialyze(Config, whereis_diff_functions1_pathsens) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions1_twice(Config) ->
+ case dialyze(Config, whereis_diff_functions1_twice) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions2(Config) ->
+ case dialyze(Config, whereis_diff_functions2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions2_nested(Config) ->
+ case dialyze(Config, whereis_diff_functions2_nested) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions2_pathsens(Config) ->
+ case dialyze(Config, whereis_diff_functions2_pathsens) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions2_twice(Config) ->
+ case dialyze(Config, whereis_diff_functions2_twice) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions3(Config) ->
+ case dialyze(Config, whereis_diff_functions3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions3_nested(Config) ->
+ case dialyze(Config, whereis_diff_functions3_nested) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions3_pathsens(Config) ->
+ case dialyze(Config, whereis_diff_functions3_pathsens) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions4(Config) ->
+ case dialyze(Config, whereis_diff_functions4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions5(Config) ->
+ case dialyze(Config, whereis_diff_functions5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_functions6(Config) ->
+ case dialyze(Config, whereis_diff_functions6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules1(Config) ->
+ case dialyze(Config, whereis_diff_modules1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules1_pathsens(Config) ->
+ case dialyze(Config, whereis_diff_modules1_pathsens) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules1_rec(Config) ->
+ case dialyze(Config, whereis_diff_modules1_rec) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules2(Config) ->
+ case dialyze(Config, whereis_diff_modules2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules2_pathsens(Config) ->
+ case dialyze(Config, whereis_diff_modules2_pathsens) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules2_rec(Config) ->
+ case dialyze(Config, whereis_diff_modules2_rec) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules3(Config) ->
+ case dialyze(Config, whereis_diff_modules3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules_nested(Config) ->
+ case dialyze(Config, whereis_diff_modules_nested) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_modules_twice(Config) ->
+ case dialyze(Config, whereis_diff_modules_twice) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_vars_no_race(Config) ->
+ case dialyze(Config, whereis_diff_vars_no_race) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_diff_vars_race(Config) ->
+ case dialyze(Config, whereis_diff_vars_race) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module1(Config) ->
+ case dialyze(Config, whereis_intra_inter_module1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module2(Config) ->
+ case dialyze(Config, whereis_intra_inter_module2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module3(Config) ->
+ case dialyze(Config, whereis_intra_inter_module3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module4(Config) ->
+ case dialyze(Config, whereis_intra_inter_module4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module5(Config) ->
+ case dialyze(Config, whereis_intra_inter_module5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module6(Config) ->
+ case dialyze(Config, whereis_intra_inter_module6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module7(Config) ->
+ case dialyze(Config, whereis_intra_inter_module7) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_intra_inter_module8(Config) ->
+ case dialyze(Config, whereis_intra_inter_module8) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_param(Config) ->
+ case dialyze(Config, whereis_param) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_param_inter_module(Config) ->
+ case dialyze(Config, whereis_param_inter_module) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function1(Config) ->
+ case dialyze(Config, whereis_rec_function1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function2(Config) ->
+ case dialyze(Config, whereis_rec_function2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function3(Config) ->
+ case dialyze(Config, whereis_rec_function3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function4(Config) ->
+ case dialyze(Config, whereis_rec_function4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function5(Config) ->
+ case dialyze(Config, whereis_rec_function5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function6(Config) ->
+ case dialyze(Config, whereis_rec_function6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function7(Config) ->
+ case dialyze(Config, whereis_rec_function7) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_rec_function8(Config) ->
+ case dialyze(Config, whereis_rec_function8) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_try_catch(Config) ->
+ case dialyze(Config, whereis_try_catch) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars1(Config) ->
+ case dialyze(Config, whereis_vars1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars10(Config) ->
+ case dialyze(Config, whereis_vars10) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars11(Config) ->
+ case dialyze(Config, whereis_vars11) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars12(Config) ->
+ case dialyze(Config, whereis_vars12) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars13(Config) ->
+ case dialyze(Config, whereis_vars13) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars14(Config) ->
+ case dialyze(Config, whereis_vars14) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars15(Config) ->
+ case dialyze(Config, whereis_vars15) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars16(Config) ->
+ case dialyze(Config, whereis_vars16) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars17(Config) ->
+ case dialyze(Config, whereis_vars17) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars18(Config) ->
+ case dialyze(Config, whereis_vars18) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars19(Config) ->
+ case dialyze(Config, whereis_vars19) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars2(Config) ->
+ case dialyze(Config, whereis_vars2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars20(Config) ->
+ case dialyze(Config, whereis_vars20) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars21(Config) ->
+ case dialyze(Config, whereis_vars21) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars22(Config) ->
+ case dialyze(Config, whereis_vars22) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars3(Config) ->
+ case dialyze(Config, whereis_vars3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars4(Config) ->
+ case dialyze(Config, whereis_vars4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars5(Config) ->
+ case dialyze(Config, whereis_vars5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars6(Config) ->
+ case dialyze(Config, whereis_vars6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars7(Config) ->
+ case dialyze(Config, whereis_vars7) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars8(Config) ->
+ case dialyze(Config, whereis_vars8) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+whereis_vars9(Config) ->
+ case dialyze(Config, whereis_vars9) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/race_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..44e1720715
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, [{warnings, [race_conditions]}]}.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args1 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args1
new file mode 100644
index 0000000000..3bbe99d4af
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args1
@@ -0,0 +1,2 @@
+
+ets_insert_args1.erl:9: The call ets:insert(T::'foo',[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(T::'foo','counter') call in ets_insert_args1.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args2 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args2
new file mode 100644
index 0000000000..34176c66ac
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args2
@@ -0,0 +1,2 @@
+
+ets_insert_args2.erl:9: The call ets:insert(T::'foo',[{'counter',number()} | {'kostis',number()} | {'maria',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(T::'foo','counter') call in ets_insert_args2.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args3 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args3
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args3
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args4 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args4
new file mode 100644
index 0000000000..8c45de08c2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args4
@@ -0,0 +1,2 @@
+
+ets_insert_args4.erl:9: The call ets:insert(T::'foo',{'counter',number()}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(T::'foo','counter') call in ets_insert_args4.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args5 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args5
new file mode 100644
index 0000000000..a4a0c021c2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args5
@@ -0,0 +1,2 @@
+
+ets_insert_args5.erl:9: The call ets:insert(T::'foo',{'counter',number(),number()}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(T::'foo','counter') call in ets_insert_args5.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args6 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args6
new file mode 100644
index 0000000000..10fa4c27e3
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args6
@@ -0,0 +1,2 @@
+
+ets_insert_args6.erl:9: The call ets:insert(T::'foo',[{'counter',number(),number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(T::'foo','counter') call in ets_insert_args6.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args7 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args7
new file mode 100644
index 0000000000..af43145c17
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args7
@@ -0,0 +1,2 @@
+
+ets_insert_args7.erl:17: The call ets:insert(Table::'foo',[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo','counter') call in ets_insert_args7.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args8 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args8
new file mode 100644
index 0000000000..5a2b41ed8c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_args8
@@ -0,0 +1,2 @@
+
+ets_insert_args8.erl:16: The call ets:insert(Table::atom(),[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::atom(),'counter') call in ets_insert_args8.erl on line 12
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow1 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow1
new file mode 100644
index 0000000000..d7df214939
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow1
@@ -0,0 +1,2 @@
+
+ets_insert_control_flow1.erl:15: The call ets:insert('foo',{'random',integer()}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','random') call in ets_insert_control_flow1.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow2 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow2
new file mode 100644
index 0000000000..cdaeafb0ed
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow2
@@ -0,0 +1,3 @@
+
+ets_insert_control_flow2.erl:15: The call ets:insert('foo',[{'pass',[pos_integer()]} | {'random',integer()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','random') call in ets_insert_control_flow2.erl on line 10
+ets_insert_control_flow2.erl:19: The call ets:insert('foo',[{'pass',[pos_integer()]} | {'random',integer()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','random') call in ets_insert_control_flow2.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow3 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow3
new file mode 100644
index 0000000000..d640f564cd
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow3
@@ -0,0 +1,3 @@
+
+ets_insert_control_flow3.erl:21: The call ets:insert(Table::atom() | tid(),{'root',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'root') call in ets_insert_control_flow3.erl on line 12
+ets_insert_control_flow3.erl:23: The call ets:insert(Table::atom() | tid(),{'user',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'user') call in ets_insert_control_flow3.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow4 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow4
new file mode 100644
index 0000000000..6f34e75902
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow4
@@ -0,0 +1,3 @@
+
+ets_insert_control_flow4.erl:21: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 13
+ets_insert_control_flow4.erl:23: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow4.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow5 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow5
new file mode 100644
index 0000000000..5af592f43f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_control_flow5
@@ -0,0 +1,5 @@
+
+ets_insert_control_flow5.erl:22: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16
+ets_insert_control_flow5.erl:23: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 13
+ets_insert_control_flow5.erl:25: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_control_flow5.erl on line 16
+ets_insert_control_flow5.erl:26: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 12, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_control_flow5.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race1 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race1
new file mode 100644
index 0000000000..98ccf34e7d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race1
@@ -0,0 +1,2 @@
+
+ets_insert_diff_atoms_race1.erl:22: The call ets:insert(Table::'bar' | 'foo',[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo','counter') call in ets_insert_diff_atoms_race1.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race2 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race2
new file mode 100644
index 0000000000..b6af99b4cc
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race2
@@ -0,0 +1,2 @@
+
+ets_insert_diff_atoms_race2.erl:22: The call ets:insert(Table::'bar' | 'foo',[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo',Counter::'counter') call in ets_insert_diff_atoms_race2.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race3 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race3
new file mode 100644
index 0000000000..d79182c289
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race3
@@ -0,0 +1,2 @@
+
+ets_insert_diff_atoms_race3.erl:22: The call ets:insert(Table::'bar' | 'foo',{'counter',number()}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo','counter') call in ets_insert_diff_atoms_race3.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race4 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race4
new file mode 100644
index 0000000000..5bb1b9f781
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race4
@@ -0,0 +1,2 @@
+
+ets_insert_diff_atoms_race4.erl:22: The call ets:insert(Table::'bar' | 'foo',{'counter',number()}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo',Counter::'counter') call in ets_insert_diff_atoms_race4.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race5 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race5
new file mode 100644
index 0000000000..7db320e758
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race5
@@ -0,0 +1,2 @@
+
+ets_insert_diff_atoms_race5.erl:22: The call ets:insert(Table::'foo',[{'counter',number()} | {'index',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo',Counter::'counter') call in ets_insert_diff_atoms_race5.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race6 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race6
new file mode 100644
index 0000000000..c029f79ed5
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_diff_atoms_race6
@@ -0,0 +1,2 @@
+
+ets_insert_diff_atoms_race6.erl:22: The call ets:insert(Table::'foo',{'counter',number()} | {'index',number()}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Tab::'foo',Counter::'counter') call in ets_insert_diff_atoms_race6.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double1 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double1
new file mode 100644
index 0000000000..b640b91271
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double1
@@ -0,0 +1,4 @@
+
+ets_insert_double1.erl:15: The call ets:insert('foo',[{'pass',[number()]} | {'random',integer()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','random') call in ets_insert_double1.erl on line 10, the ets:lookup('foo','pass') call in ets_insert_double1.erl on line 27
+ets_insert_double1.erl:19: The call ets:insert('foo',[{'pass',[number()]} | {'random',integer()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','random') call in ets_insert_double1.erl on line 10, the ets:lookup('foo','pass') call in ets_insert_double1.erl on line 27
+ets_insert_double1.erl:24: The call ets:insert('foo',{'pass','empty'}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','pass') call in ets_insert_double1.erl on line 22
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double2 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double2
new file mode 100644
index 0000000000..cf61cb5ec3
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_double2
@@ -0,0 +1,4 @@
+
+ets_insert_double2.erl:15: The call ets:insert('foo',[{_,[number()] | integer()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo',Random::any()) call in ets_insert_double2.erl on line 10, the ets:lookup('foo',Pass::any()) call in ets_insert_double2.erl on line 27
+ets_insert_double2.erl:19: The call ets:insert('foo',[{_,[number()] | integer()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo',Random::any()) call in ets_insert_double2.erl on line 10, the ets:lookup('foo',Pass::any()) call in ets_insert_double2.erl on line 27
+ets_insert_double2.erl:24: The call ets:insert('foo',{_,'empty'}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo',Pass::any()) call in ets_insert_double2.erl on line 22
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs1 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs1
new file mode 100644
index 0000000000..540a0cf388
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs1
@@ -0,0 +1,2 @@
+
+ets_insert_funs1.erl:15: The call ets:insert('foo',[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(T::'foo','counter') call in ets_insert_funs1.erl on line 9
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs2 b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs2
new file mode 100644
index 0000000000..6b618f72b6
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_funs2
@@ -0,0 +1,2 @@
+
+ets_insert_funs2.erl:9: The call ets:insert(T::'foo',[{'counter',number()},...]) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('foo','counter') call in ets_insert_funs2.erl on line 14
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_new b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_new
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_new
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_param b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_param
new file mode 100644
index 0000000000..58f934a190
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/ets_insert_param
@@ -0,0 +1,5 @@
+
+ets_insert_param.erl:13: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_param.erl on line 10
+ets_insert_param.erl:14: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 14, the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 15
+ets_insert_param.erl:17: The call ets:insert(Table::atom() | tid(),{'welcome_msg',[any(),...]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'welcome_msg') call in ets_insert_param.erl on line 10
+ets_insert_param.erl:18: The call ets:insert(Table::atom() | tid(),{'pass',[pos_integer()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup(Table::atom() | tid(),'pass') call in ets_insert_param.erl on line 18
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/extract_translations b/lib/dialyzer/test/race_tests_SUITE_data/results/extract_translations
new file mode 100644
index 0000000000..f7d5abc6f5
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/extract_translations
@@ -0,0 +1,5 @@
+
+extract_translations.erl:140: The call ets:insert('files',{atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]) call in extract_translations.erl on line 135
+extract_translations.erl:146: The call ets:insert('translations',{_,[]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('translations',Str::any()) call in extract_translations.erl on line 126
+extract_translations.erl:152: The call ets:insert('files',{atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]) call in extract_translations.erl on line 148
+extract_translations.erl:154: The call ets:insert('translations',{_,[]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('translations',Str::any()) call in extract_translations.erl on line 126
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race1 b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race1
new file mode 100644
index 0000000000..f5e544dc2a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race1
@@ -0,0 +1,2 @@
+
+mnesia_diff_atoms_race1.erl:33: The call mnesia:dirty_write(Table::'employee' | 'employer',Record::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read(Tab::'employee',Eno::any()) call in mnesia_diff_atoms_race1.erl on line 19
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race2 b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race2
new file mode 100644
index 0000000000..0ad0bc0afd
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_diff_atoms_race2
@@ -0,0 +1,2 @@
+
+mnesia_diff_atoms_race2.erl:37: The call mnesia:dirty_write(Record::#employee{salary::number()} | #employer{}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read(Tab::'employee',Eno::any()) call in mnesia_diff_atoms_race2.erl on line 26
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_one_write_two b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_one_write_two
new file mode 100644
index 0000000000..a4f3c269f1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_one_write_two
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_one_write_two.erl:20: The call mnesia:dirty_write('employee',New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read({'employee',_}) call in mnesia_dirty_read_one_write_two.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_two_write_one b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_two_write_one
new file mode 100644
index 0000000000..6e666d755f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_two_write_one
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_two_write_one.erl:20: The call mnesia:dirty_write(New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read('employee',Eno::any()) call in mnesia_dirty_read_two_write_one.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double1 b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double1
new file mode 100644
index 0000000000..e953c6948b
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double1
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_write_double1.erl:20: The call mnesia:dirty_write('employee',New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read('employee',Eno::any()) call in mnesia_dirty_read_write_double1.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double2 b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double2
new file mode 100644
index 0000000000..2a0b4eddd0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double2
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_write_double2.erl:20: The call mnesia:dirty_write(New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read({'employee',_}) call in mnesia_dirty_read_write_double2.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double3 b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double3
new file mode 100644
index 0000000000..fe51a5e838
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double3
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_write_double3.erl:20: The call mnesia:dirty_write('employee',New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read({'employee',_}) call in mnesia_dirty_read_write_double3.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double4 b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double4
new file mode 100644
index 0000000000..d6a60d847a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_double4
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_write_double4.erl:20: The call mnesia:dirty_write(New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read('employee',Eno::any()) call in mnesia_dirty_read_write_double4.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_one b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_one
new file mode 100644
index 0000000000..b47f66eb79
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_one
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_write_one.erl:20: The call mnesia:dirty_write(New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read({'employee',_}) call in mnesia_dirty_read_write_one.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_two b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_two
new file mode 100644
index 0000000000..2faf55fe72
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/mnesia_dirty_read_write_two
@@ -0,0 +1,2 @@
+
+mnesia_dirty_read_write_two.erl:20: The call mnesia:dirty_write('employee',New::#employee{salary::number()}) might have an unintended effect due to a possible race condition caused by its combination with the mnesia:dirty_read('employee',Eno::any()) call in mnesia_dirty_read_write_two.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow1 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow1
new file mode 100644
index 0000000000..0fcf13c50a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow1
@@ -0,0 +1,2 @@
+
+whereis_control_flow1.erl:13: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_control_flow1.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow2 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow2
new file mode 100644
index 0000000000..d0c048701d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow2
@@ -0,0 +1,3 @@
+
+whereis_control_flow2.erl:14: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_control_flow2.erl on line 8
+whereis_control_flow2.erl:15: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_control_flow2.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow3 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow3
new file mode 100644
index 0000000000..0d93428758
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow3
@@ -0,0 +1,2 @@
+
+whereis_control_flow3.erl:25: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_control_flow3.erl on line 11, the erlang:whereis(AnAtom::any()) call in whereis_control_flow3.erl on line 18
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow4 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow4
new file mode 100644
index 0000000000..f0ce12d0a4
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow4
@@ -0,0 +1,3 @@
+
+whereis_control_flow4.erl:18: The call erlang:register('maria',Pid1::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis('maria') call in whereis_control_flow4.erl on line 8
+whereis_control_flow4.erl:19: The call erlang:register('kostis',Pid2::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis('kostis') call in whereis_control_flow4.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow5 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow5
new file mode 100644
index 0000000000..fd809139e4
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow5
@@ -0,0 +1,2 @@
+
+whereis_control_flow5.erl:11: The call erlang:unregister(AnAtom::atom()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_control_flow5.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow6 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow6
new file mode 100644
index 0000000000..ba89cc5624
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_control_flow6
@@ -0,0 +1,2 @@
+
+whereis_control_flow6.erl:11: The call erlang:unregister('kostis') might fail due to a possible race condition caused by its combination with the erlang:whereis('kostis') call in whereis_control_flow6.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_no_race b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_no_race
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_no_race
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_race b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_race
new file mode 100644
index 0000000000..76c746e2f4
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_atoms_race
@@ -0,0 +1,2 @@
+
+whereis_diff_atoms_race.erl:34: The call erlang:register(Atom::'kostis' | 'maria',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::'maria') call in whereis_diff_atoms_race.erl on line 14
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1
new file mode 100644
index 0000000000..14c157885f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1
@@ -0,0 +1,3 @@
+
+whereis_diff_functions1.erl:10: The call erlang:register('master',pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis('master') call in whereis_diff_functions1.erl on line 8
+whereis_diff_functions1.erl:18: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions1.erl on line 15
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_nested b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_nested
new file mode 100644
index 0000000000..c791d4b347
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_nested
@@ -0,0 +1,2 @@
+
+whereis_diff_functions1_nested.erl:23: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions1_nested.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_pathsens b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_pathsens
new file mode 100644
index 0000000000..d22e696196
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_pathsens
@@ -0,0 +1,2 @@
+
+whereis_diff_functions1_pathsens.erl:32: The call erlang:register(Atom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::'kostis') call in whereis_diff_functions1_pathsens.erl on line 15, the erlang:whereis(AnAtom::'kostis') call in whereis_diff_functions1_pathsens.erl on line 22
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_twice b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_twice
new file mode 100644
index 0000000000..3024c77d91
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions1_twice
@@ -0,0 +1,3 @@
+
+whereis_diff_functions1_twice.erl:27: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions1_twice.erl on line 11
+whereis_diff_functions1_twice.erl:30: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::atom()) call in whereis_diff_functions1_twice.erl on line 15
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2
new file mode 100644
index 0000000000..9a22eb7e17
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2
@@ -0,0 +1,2 @@
+
+whereis_diff_functions2.erl:25: The call erlang:register(Atom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::'kostis') call in whereis_diff_functions2.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_nested b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_nested
new file mode 100644
index 0000000000..0e757fbccc
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_nested
@@ -0,0 +1,2 @@
+
+whereis_diff_functions2_nested.erl:20: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions2_nested.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_pathsens b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_pathsens
new file mode 100644
index 0000000000..c102b39243
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_pathsens
@@ -0,0 +1,2 @@
+
+whereis_diff_functions2_pathsens.erl:29: The call erlang:register(Atom::atom(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions2_pathsens.erl on line 12, the erlang:whereis(AnAtom::any()) call in whereis_diff_functions2_pathsens.erl on line 19
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_twice b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_twice
new file mode 100644
index 0000000000..b048bc6bed
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions2_twice
@@ -0,0 +1,3 @@
+
+whereis_diff_functions2_twice.erl:24: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions2_twice.erl on line 8
+whereis_diff_functions2_twice.erl:27: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::atom()) call in whereis_diff_functions2_twice.erl on line 12
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3
new file mode 100644
index 0000000000..6d5154b411
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3
@@ -0,0 +1,2 @@
+
+whereis_diff_functions3.erl:8: The call erlang:register(AnAtom::atom(),'undefined' | pid() | port()) might fail due to a possible race condition caused by its combination with the erlang:whereis(Atom::any()) call in whereis_diff_functions3.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_nested b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_nested
new file mode 100644
index 0000000000..298c4c7178
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_nested
@@ -0,0 +1,2 @@
+
+whereis_diff_functions3_nested.erl:21: The call erlang:unregister(Atom::atom()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions3_nested.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_pathsens b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_pathsens
new file mode 100644
index 0000000000..5d1ea5bda5
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions3_pathsens
@@ -0,0 +1,2 @@
+
+whereis_diff_functions3_pathsens.erl:29: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions3_pathsens.erl on line 12, the erlang:whereis(AnAtom::any()) call in whereis_diff_functions3_pathsens.erl on line 19
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions4 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions4
new file mode 100644
index 0000000000..cb51301f1e
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions4
@@ -0,0 +1,2 @@
+
+whereis_diff_functions4.erl:32: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions4.erl on line 13, the erlang:whereis(AnAtom::atom()) call in whereis_diff_functions4.erl on line 17
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions5 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions5
new file mode 100644
index 0000000000..34c477e05a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions5
@@ -0,0 +1,2 @@
+
+whereis_diff_functions5.erl:22: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions5.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions6 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions6
new file mode 100644
index 0000000000..8840ef4ca7
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_functions6
@@ -0,0 +1,2 @@
+
+whereis_diff_functions6.erl:29: The call erlang:register(Atom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_functions6.erl on line 10, the erlang:whereis(AnAtom::atom()) call in whereis_diff_functions6.erl on line 14
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1
new file mode 100644
index 0000000000..8f7d0b7a17
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1
@@ -0,0 +1,2 @@
+
+whereis_diff_modules2.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_modules1.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_pathsens b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_pathsens
new file mode 100644
index 0000000000..40d36eb7d2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_pathsens
@@ -0,0 +1,2 @@
+
+whereis_diff_modules2_pathsens.erl:12: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_modules1_pathsens.erl on line 12, the erlang:whereis(AnAtom::any()) call in whereis_diff_modules1_pathsens.erl on line 19
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_rec b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_rec
new file mode 100644
index 0000000000..278b679aba
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules1_rec
@@ -0,0 +1,2 @@
+
+whereis_diff_modules1_rec.erl:10: The call erlang:register(AnAtom::any(),Id::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_diff_modules1_rec.erl on line 12
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2
new file mode 100644
index 0000000000..a4e5a000e2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2
@@ -0,0 +1,2 @@
+
+whereis_diff_modules3.erl:8: The call erlang:register(AnAtom::atom(),'undefined' | pid() | port()) might fail due to a possible race condition caused by its combination with the erlang:whereis(Atom::any()) call in whereis_diff_modules4.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_pathsens b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_pathsens
new file mode 100644
index 0000000000..cc93133019
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_pathsens
@@ -0,0 +1,2 @@
+
+whereis_diff_modules4_pathsens.erl:13: The call erlang:register(Atom::atom(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_modules3_pathsens.erl on line 12, the erlang:whereis(AnAtom::any()) call in whereis_diff_modules3_pathsens.erl on line 19
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_rec b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_rec
new file mode 100644
index 0000000000..8874ab3553
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules2_rec
@@ -0,0 +1,2 @@
+
+whereis_diff_modules3_rec.erl:13: The call erlang:register(AnAtom::any(),Id::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_diff_modules3_rec.erl on line 15
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules3 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules3
new file mode 100644
index 0000000000..8e839a53dc
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules3
@@ -0,0 +1,2 @@
+
+whereis_diff_modules6.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_modules5.erl on line 10, the erlang:whereis(AnAtom::atom()) call in whereis_diff_modules5.erl on line 14
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_nested b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_nested
new file mode 100644
index 0000000000..9192dc0708
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_nested
@@ -0,0 +1,2 @@
+
+whereis_diff_modules3_nested.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_modules1_nested.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_twice b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_twice
new file mode 100644
index 0000000000..3758347255
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_modules_twice
@@ -0,0 +1,3 @@
+
+whereis_diff_modules2_twice.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::atom()) call in whereis_diff_modules1_twice.erl on line 12
+whereis_diff_modules2_twice.erl:8: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_diff_modules1_twice.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_no_race b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_no_race
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_no_race
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_race b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_race
new file mode 100644
index 0000000000..e34b4d2138
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_diff_vars_race
@@ -0,0 +1,2 @@
+
+whereis_diff_vars_race.erl:16: The call erlang:register(Atom2::any(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(Atom1::any()) call in whereis_diff_vars_race.erl on line 13
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module1 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module1
new file mode 100644
index 0000000000..3ed6f50d8d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module1
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module2.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module1.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module2 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module2
new file mode 100644
index 0000000000..737054fe67
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module2
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module4.erl:14: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module3.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module3 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module3
new file mode 100644
index 0000000000..4111498efe
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module3
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module6.erl:14: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module5.erl on line 10
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module4 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module4
new file mode 100644
index 0000000000..4e70a8efa1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module4
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module7.erl:8: The call erlang:register(AnAtom::atom(),pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module8.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module5 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module5
new file mode 100644
index 0000000000..f6a10f52fd
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module5
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module9.erl:8: The call erlang:register(AnAtom::atom(),pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module10.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module6 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module6
new file mode 100644
index 0000000000..a8623ee985
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module6
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module12.erl:14: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module11.erl on line 10, the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module11.erl on line 21
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module7 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module7
new file mode 100644
index 0000000000..e39d630c75
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module7
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module14.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module13.erl on line 10, the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module14.erl on line 16
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module8 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module8
new file mode 100644
index 0000000000..58ae498bd4
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_intra_inter_module8
@@ -0,0 +1,2 @@
+
+whereis_intra_inter_module16.erl:11: The call erlang:register(Atom::any(),Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module15.erl on line 10, the erlang:whereis(AnAtom::any()) call in whereis_intra_inter_module16.erl on line 16
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param
new file mode 100644
index 0000000000..fb7563b1c7
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param
@@ -0,0 +1,2 @@
+
+whereis_param.erl:8: The call erlang:register(AnAtom::atom(),pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_param.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param_inter_module b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param_inter_module
new file mode 100644
index 0000000000..fc3e9ca59d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_param_inter_module
@@ -0,0 +1,2 @@
+
+whereis_param_inter_module1.erl:8: The call erlang:register(AnAtom::atom(),pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_param_inter_module2.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function1 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function1
new file mode 100644
index 0000000000..2cf1960d65
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function1
@@ -0,0 +1,2 @@
+
+whereis_rec_function1.erl:14: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_rec_function1.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function2 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function2
new file mode 100644
index 0000000000..4b55bc61ad
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function2
@@ -0,0 +1,2 @@
+
+whereis_rec_function2.erl:13: The call erlang:register(AnAtom::atom(),Id::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_rec_function2.erl on line 15
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function3 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function3
new file mode 100644
index 0000000000..638e9b0f4b
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function3
@@ -0,0 +1,2 @@
+
+whereis_rec_function3.erl:13: The call erlang:register(AnAtom::atom(),Id::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_rec_function3.erl on line 16, the erlang:whereis(NextAtom::atom()) call in whereis_rec_function3.erl on line 20
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function4 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function4
new file mode 100644
index 0000000000..f255cb8170
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function4
@@ -0,0 +1,2 @@
+
+whereis_rec_function4.erl:13: The call erlang:register(AnAtom::atom(),Id::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_rec_function4.erl on line 15
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function5 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function5
new file mode 100644
index 0000000000..78d81b9a57
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function5
@@ -0,0 +1,2 @@
+
+whereis_rec_function5.erl:10: The call erlang:register(AnAtom::any(),Id::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_rec_function5.erl on line 12
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function6 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function6
new file mode 100644
index 0000000000..6df6de1922
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function6
@@ -0,0 +1,2 @@
+
+whereis_rec_function6.erl:10: The call erlang:register(AnAtom::any(),Id::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(NextAtom::any()) call in whereis_rec_function6.erl on line 12
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function7 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function7
new file mode 100644
index 0000000000..f3ddb0b537
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function7
@@ -0,0 +1,2 @@
+
+whereis_rec_function7.erl:15: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_rec_function7.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function8 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function8
new file mode 100644
index 0000000000..9d731ada29
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_rec_function8
@@ -0,0 +1,2 @@
+
+whereis_rec_function8.erl:18: The call erlang:register(AnAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_rec_function8.erl on line 11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_try_catch b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_try_catch
new file mode 100644
index 0000000000..fecb0756bd
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_try_catch
@@ -0,0 +1,3 @@
+
+whereis_try_catch.erl:13: The call erlang:register('master',Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis('master') call in whereis_try_catch.erl on line 8
+whereis_try_catch.erl:21: The call erlang:register('master',Pid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis('master') call in whereis_try_catch.erl on line 18
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars1 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars1
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars1
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars10 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars10
new file mode 100644
index 0000000000..36a59096e0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars10
@@ -0,0 +1,2 @@
+
+whereis_vars10.erl:17: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars10.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars11 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars11
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars11
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars12 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars12
new file mode 100644
index 0000000000..d34e1b1c7e
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars12
@@ -0,0 +1,2 @@
+
+whereis_vars12.erl:16: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars12.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars13 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars13
new file mode 100644
index 0000000000..e6ae40cee0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars13
@@ -0,0 +1,2 @@
+
+whereis_vars13.erl:16: The call erlang:register(OtherAtom::'kostis',APid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars13.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars14 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars14
new file mode 100644
index 0000000000..cdd23a7471
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars14
@@ -0,0 +1,2 @@
+
+whereis_vars14.erl:16: The call erlang:register(OtherAtom::'kostis',APid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars14.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars15 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars15
new file mode 100644
index 0000000000..7f79852978
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars15
@@ -0,0 +1,2 @@
+
+whereis_vars15.erl:17: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars15.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars16 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars16
new file mode 100644
index 0000000000..0f28dff25d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars16
@@ -0,0 +1,2 @@
+
+whereis_vars16.erl:17: The call erlang:register(OtherAtom::any(),APid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars16.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars17 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars17
new file mode 100644
index 0000000000..3681c1aa9f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars17
@@ -0,0 +1,2 @@
+
+whereis_vars17.erl:17: The call erlang:register(OtherAtom::any(),APid::any()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars17.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars18 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars18
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars18
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars19 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars19
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars19
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars2 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars2
new file mode 100644
index 0000000000..1636a6e908
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars2
@@ -0,0 +1,2 @@
+
+whereis_vars2.erl:14: The call erlang:register(OtherAtom::atom(),Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars2.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars20 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars20
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars20
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars21 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars21
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars21
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars22 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars22
new file mode 100644
index 0000000000..0f258cc097
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars22
@@ -0,0 +1,2 @@
+
+whereis_vars22.erl:21: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars22.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars3 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars3
new file mode 100644
index 0000000000..4f43b9adca
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars3
@@ -0,0 +1,2 @@
+
+whereis_vars3.erl:14: The call erlang:register(OtherAtom::atom(),APid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars3.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars4 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars4
new file mode 100644
index 0000000000..9eb833c42a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars4
@@ -0,0 +1,2 @@
+
+whereis_vars4.erl:14: The call erlang:register(OtherAtom::atom() | pid(),APid::atom() | pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars4.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars5 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars5
new file mode 100644
index 0000000000..b1c269c020
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars5
@@ -0,0 +1,2 @@
+
+whereis_vars5.erl:16: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars5.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars6 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars6
new file mode 100644
index 0000000000..88c58cfdf2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars6
@@ -0,0 +1,2 @@
+
+whereis_vars6.erl:16: The call erlang:register(OtherAtom::'kostis',APid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars6.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars7 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars7
new file mode 100644
index 0000000000..8924869634
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars7
@@ -0,0 +1,2 @@
+
+whereis_vars7.erl:16: The call erlang:register(OtherAtom::'kostis',APid::atom() | pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars7.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars8 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars8
new file mode 100644
index 0000000000..d9d8f3872f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars8
@@ -0,0 +1,2 @@
+
+whereis_vars8.erl:16: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars8.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars9 b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars9
new file mode 100644
index 0000000000..da52ca1f82
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/results/whereis_vars9
@@ -0,0 +1,2 @@
+
+whereis_vars9.erl:16: The call erlang:register(OtherAtom::'kostis',Pid::pid()) might fail due to a possible race condition caused by its combination with the erlang:whereis(AnAtom::any()) call in whereis_vars9.erl on line 8
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args1.erl
new file mode 100644
index 0000000000..78b586f097
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args1.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args1).
+-export([start/0]).
+
+start() ->
+ F = fun(T) -> [{_, N}] = ets:lookup(T, counter),
+ ets:insert(T, [{counter, N+1}])
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0}),
+ io:format("Inserted ~w\n", [{counter, 0}]),
+ F(foo),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args2.erl
new file mode 100644
index 0000000000..7e53b1e8bf
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args2.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args2).
+-export([start/0]).
+
+start() ->
+ F = fun(T)-> [{_, N}] = ets:lookup(T, counter),
+ ets:insert(T, [{counter, N+1}, {maria, N+1}, {kostis, N+1}])
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0}),
+ io:format("Inserted ~w\n", [{counter, 0}]),
+ F(foo),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args3.erl
new file mode 100644
index 0000000000..b99bde14fa
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args3.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args3).
+-export([start/0]).
+
+start() ->
+ F = fun(T)-> [{_, N}] = ets:lookup(T, counter),
+ ets:insert(T, [{maria, N+1}, {kostis, N+1}])
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0}),
+ io:format("Inserted ~w\n", [{counter, 0}]),
+ F(foo),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args4.erl
new file mode 100644
index 0000000000..7bf3599c65
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args4.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args4).
+-export([start/0]).
+
+start() ->
+ F = fun(T)-> [{_, N}] = ets:lookup(T, counter),
+ ets:insert(T, {counter, N+1})
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0}),
+ io:format("Inserted ~w\n", [{counter, 0}]),
+ F(foo),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args5.erl
new file mode 100644
index 0000000000..93fef43cf1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args5.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args5).
+-export([start/0]).
+
+start() ->
+ F = fun(T)-> [{_, N}] = ets:lookup(T, counter),
+ ets:insert(T, {counter, N+1, N+2})
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0, 0}),
+ io:format("Inserted ~w\n", [{counter, 0, 0}]),
+ F(foo),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args6.erl
new file mode 100644
index 0000000000..2a803ccaac
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args6.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args6).
+-export([start/0]).
+
+start() ->
+ F = fun(T)-> [{_, N}] = ets:lookup(T, counter),
+ ets:insert(T, [{counter, N+1, N+2}])
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0, 0}),
+ io:format("Inserted ~w\n", [{counter, 0, 0}]),
+ F(foo),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args7.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args7.erl
new file mode 100644
index 0000000000..adc13703a7
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args7.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args7).
+-export([test/0]).
+
+test() ->
+ Foo = foo,
+ ets:new(Foo, [named_table, public]),
+ race(Foo).
+
+race(Tab) ->
+ [{_, N}] = ets:lookup(Tab, counter),
+ aux(Tab, N).
+
+aux(Table, N) ->
+ ets:insert(Table, [{counter, N+1}]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args8.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args8.erl
new file mode 100644
index 0000000000..832fc2eef1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_args8.erl
@@ -0,0 +1,16 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the argument types of the calls.
+
+-module(ets_insert_args8).
+-export([test/1]).
+
+test(Foo) ->
+ ets:new(Foo, [named_table, public]),
+ race(Foo).
+
+race(Tab) ->
+ [{_, N}] = ets:lookup(Tab, counter),
+ aux(Tab, N).
+
+aux(Table, N) ->
+ ets:insert(Table, [{counter, N+1}]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow1.erl
new file mode 100644
index 0000000000..7b56495e47
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow1.erl
@@ -0,0 +1,20 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account control flow that might exist.
+
+-module(ets_insert_control_flow1).
+-export([start/0]).
+
+start() ->
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {random, random:uniform(maria:get_int())}),
+ io:format("Inserted ~w\n", [{_, N}] = ets:lookup(foo, random)),
+ case (N rem 2 == 0) of
+ true ->
+ io:format("\nInserted an even number\n", []),
+ io:format("\nWill make it odd\n", []),
+ ets:insert(foo, {random, N+1});
+ false -> ok
+ end,
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, random),
+ io:format("Random odd integer: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow2.erl
new file mode 100644
index 0000000000..434ca113ee
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow2.erl
@@ -0,0 +1,26 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account control flow that might exist.
+
+-module(ets_insert_control_flow2).
+-export([start/0]).
+
+start() ->
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {random, random:uniform(150)}),
+ io:format("Inserted ~w\n", [{_, N}] = ets:lookup(foo, random)),
+ case (N rem 2 == 0) of
+ true ->
+ io:format("\nInserted an even integer\n", []),
+ io:format("\nWill make it odd and generate password\n", []),
+ ets:insert(foo, [{random, N+1}, {pass, generate_password(N)}]);
+ false ->
+ io:format("\nInserted an odd integer\n", []),
+ io:format("\nWill make it even and generate password\n", []),
+ ets:insert(foo, [{random, N+1}, {pass, generate_password(N)}])
+ end,
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, pass),
+ io:format("New password: ~w\n", [ObjectList]).
+
+generate_password(N) ->
+ lists:map(fun (_) -> random:uniform(90)+$\s+1 end, lists:seq(1,N)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow3.erl
new file mode 100644
index 0000000000..9c6a22eb05
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow3.erl
@@ -0,0 +1,31 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account control flow that might exist.
+
+-module(ets_insert_control_flow3).
+-export([start/1]).
+
+start(User) ->
+ Table = ets:new(table, [public]),
+ mod:process(Table),
+ [{_, N}] =
+ case User of
+ root -> ets:lookup(Table, root);
+ user -> ets:lookup(Table, user);
+ Other -> [{undefined, -1}]
+ end,
+ case N of
+ -1 -> io:format("\nUnknown User\n", []);
+ 0 ->
+ case User of
+ root ->
+ ets:insert(Table, {User, Pass = generate_password(N) ++ generate_password(N+1)});
+ user ->
+ ets:insert(Table, {User, Pass = generate_password(N)})
+ end,
+ io:format("\nYour new pass is ~w\n", [Pass]);
+ P ->
+ io:format("\nYour pass is ~w\n", [P])
+ end.
+
+generate_password(N) ->
+ lists:map(fun (_) -> random:uniform(90)+$\s+1 end, lists:seq(1,N)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow4.erl
new file mode 100644
index 0000000000..caa3804614
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow4.erl
@@ -0,0 +1,31 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account control flow that might exist.
+
+-module(ets_insert_control_flow4).
+-export([start/1]).
+
+start(User) ->
+ Table = ets:new(table, [public]),
+ mod:process(Table),
+ [{_, N}] =
+ case User of
+ root -> ets:lookup(Table, pass);
+ user -> ets:lookup(Table, pass);
+ _Other -> [{undefined, -1}]
+ end,
+ case N of
+ -1 -> io:format("\nUnknown User\n", []);
+ 0 ->
+ case User of
+ root ->
+ ets:insert(Table, {pass, Pass = generate_password(N) ++ generate_password(N+1)});
+ user ->
+ ets:insert(Table, {pass, Pass = generate_password(N)})
+ end,
+ io:format("\nYour new pass is ~w\n", [Pass]);
+ P ->
+ io:format("\nYour pass is ~w\n", [P])
+ end.
+
+generate_password(N) ->
+ lists:map(fun (_) -> random:uniform(90)+$\s+1 end, lists:seq(1,N)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow5.erl
new file mode 100644
index 0000000000..b19fd776ec
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_control_flow5.erl
@@ -0,0 +1,34 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account control flow that might exist.
+
+-module(ets_insert_control_flow5).
+-export([start/1]).
+
+start(User) ->
+ Table = ets:new(table, [public]),
+ mod:process(Table),
+ [{_, N}] =
+ case User of
+ root -> ets:lookup(Table, pass);
+ user -> ets:lookup(Table, pass);
+ Other -> [{undefined, -1}]
+ end,
+ [{_, Msg}] = ets:lookup(Table, welcome_msg),
+ case N of
+ -1 -> io:format("\nUnknown User\n", []);
+ 0 ->
+ case User of
+ root ->
+ ets:insert(Table, {welcome_msg, Msg ++ "root"}),
+ ets:insert(Table, {pass, Pass = generate_password(N) ++ generate_password(N+1)});
+ user ->
+ ets:insert(Table, {welcome_msg, Msg ++ "user"}),
+ ets:insert(Table, {pass, Pass = generate_password(N)})
+ end,
+ io:format("\nYour new pass is ~w\n", [Pass]);
+ P ->
+ io:format("\nYour pass is ~w\n", [P])
+ end.
+
+generate_password(N) ->
+ lists:map(fun (_) -> random:uniform(90)+$\s+1 end, lists:seq(1,N)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race1.erl
new file mode 100644
index 0000000000..57022c86d4
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race1.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between ets:lookup/
+%% ets:insert is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(ets_insert_diff_atoms_race1).
+-export([test/0]).
+
+test() ->
+ ets:new(foo, [named_table, public]),
+ {race(foo), no_race(foo)}.
+
+race(Tab) ->
+ [{_, N}] = ets:lookup(Tab, counter),
+ aux(Tab, N).
+
+no_race(Tab) ->
+ [{_, N}] = ets:lookup(Tab, counter),
+ AnotherTab = bar,
+ aux(AnotherTab, N).
+
+aux(Table, N) ->
+ ets:insert(Table, [{counter, N+1}]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race2.erl
new file mode 100644
index 0000000000..233a19087e
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race2.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between ets:lookup/
+%% ets:insert is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(ets_insert_diff_atoms_race2).
+-export([test/0]).
+
+test() ->
+ ets:new(foo, [named_table, public]),
+ {race(foo, counter), no_race(foo, counter)}.
+
+race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ aux(Tab, Counter, N).
+
+no_race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ AnotherTab = bar,
+ aux(AnotherTab, Counter, N).
+
+aux(Table, Counter, N) ->
+ ets:insert(Table, [{Counter, N+1}]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race3.erl
new file mode 100644
index 0000000000..a09e4644f8
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race3.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between ets:lookup/
+%% ets:insert is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(ets_insert_diff_atoms_race3).
+-export([test/0]).
+
+test() ->
+ ets:new(foo, [named_table, public]),
+ {race(foo), no_race(foo)}.
+
+race(Tab) ->
+ [{_, N}] = ets:lookup(Tab, counter),
+ aux(Tab, N).
+
+no_race(Tab) ->
+ [{_, N}] = ets:lookup(Tab, counter),
+ AnotherTab = bar,
+ aux(AnotherTab, N).
+
+aux(Table, N) ->
+ ets:insert(Table, {counter, N+1}).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race4.erl
new file mode 100644
index 0000000000..d0a3f0a1d1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race4.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between ets:lookup/
+%% ets:insert is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(ets_insert_diff_atoms_race4).
+-export([test/0]).
+
+test() ->
+ ets:new(foo, [named_table, public]),
+ {race(foo, counter), no_race(foo, counter)}.
+
+race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ aux(Tab, Counter, N).
+
+no_race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ AnotherTab = bar,
+ aux(AnotherTab, Counter, N).
+
+aux(Table, Counter, N) ->
+ ets:insert(Table, {Counter, N+1}).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race5.erl
new file mode 100644
index 0000000000..bbccaab94d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race5.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between ets:lookup/
+%% ets:insert is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(ets_insert_diff_atoms_race5).
+-export([test/0]).
+
+test() ->
+ ets:new(foo, [named_table, public]),
+ {race(foo, counter), no_race(foo, counter)}.
+
+race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ aux(Tab, Counter, N).
+
+no_race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ AnotherCounter = index,
+ aux(Tab, AnotherCounter, N).
+
+aux(Table, Counter, N) ->
+ ets:insert(Table, [{Counter, N+1}]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race6.erl
new file mode 100644
index 0000000000..17457e2b44
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_diff_atoms_race6.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between ets:lookup/
+%% ets:insert is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(ets_insert_diff_atoms_race6).
+-export([test/0]).
+
+test() ->
+ ets:new(foo, [named_table, public]),
+ {race(foo, counter), no_race(foo, counter)}.
+
+race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ aux(Tab, Counter, N).
+
+no_race(Tab, Counter) ->
+ [{_, N}] = ets:lookup(Tab, Counter),
+ AnotherCounter = index,
+ aux(Tab, AnotherCounter, N).
+
+aux(Table, Counter, N) ->
+ ets:insert(Table, {Counter, N+1}).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double1.erl
new file mode 100644
index 0000000000..92fa945b73
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double1.erl
@@ -0,0 +1,28 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account multiple ets:inserts that might exist.
+
+-module(ets_insert_double1).
+-export([start/0]).
+
+start() ->
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {random, random:uniform(150)}),
+ io:format("Inserted ~w\n", [{_, N}] = ets:lookup(foo, random)),
+ case (N rem 2 == 0) of
+ true ->
+ io:format("\nInserted an even integer\n", []),
+ io:format("\nWill make it odd and generate new password\n", []),
+ ets:insert(foo, [{random, N+1}, {pass, generate_password(N)}]);
+ false ->
+ io:format("\nInserted an odd integer\n", []),
+ io:format("\nWill make it even and generate new password\n", []),
+ ets:insert(foo, [{random, N+1}, {pass, generate_password(N)}])
+ end,
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, pass),
+ io:format("New password: ~w\n", [ObjectList]),
+ ets:insert(foo, {pass, 'empty'}).
+
+generate_password(N) ->
+ [{_, P}] = ets:lookup(foo, pass),
+ lists:map(fun (_) -> random:uniform(90)+P+$\s+1 end, lists:seq(1,N)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double2.erl
new file mode 100644
index 0000000000..dc2b14ada0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_double2.erl
@@ -0,0 +1,28 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account multiple ets:inserts that might exist.
+
+-module(ets_insert_double2).
+-export([start/2]).
+
+start(Random, Pass) ->
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {Random, random:uniform(150)}),
+ io:format("Inserted ~w\n", [{_, N}] = ets:lookup(foo, Random)),
+ case (N rem 2 == 0) of
+ true ->
+ io:format("\nInserted an even integer\n", []),
+ io:format("\nWill make it odd and generate new password\n", []),
+ ets:insert(foo, [{Random, N+1}, {Pass, generate_password(Pass, N)}]);
+ false ->
+ io:format("\nInserted an odd integer\n", []),
+ io:format("\nWill make it even and generate new password\n", []),
+ ets:insert(foo, [{Random, N+1}, {Pass, generate_password(Pass, N)}])
+ end,
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, Pass),
+ io:format("New password: ~w\n", [ObjectList]),
+ ets:insert(foo, {Pass, 'empty'}).
+
+generate_password(Pass, N) ->
+ [{_, P}] = ets:lookup(foo, Pass),
+ lists:map(fun (_) -> random:uniform(90)+P+$\s+1 end, lists:seq(1,N)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs1.erl
new file mode 100644
index 0000000000..4a0a012fe3
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs1.erl
@@ -0,0 +1,18 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the anonymous functions.
+
+-module(ets_insert_funs1).
+-export([start/0]).
+
+start() ->
+ F = fun(T) ->
+ ets:lookup(T, counter)
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0}),
+ io:format("Inserted ~w\n", [{counter, 0}]),
+ [{_, N}] = F(foo),
+ ets:insert(foo, [{counter, N+1}]),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs2.erl
new file mode 100644
index 0000000000..3abb9f2fca
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_funs2.erl
@@ -0,0 +1,18 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account the anonymous functions.
+
+-module(ets_insert_funs2).
+-export([start/0]).
+
+start() ->
+ F = fun(T, N) ->
+ ets:insert(T, [{counter, N+1}])
+ end,
+ io:format("Created ~w\n", [ets:new(foo, [named_table, public])]),
+ ets:insert(foo, {counter, 0}),
+ io:format("Inserted ~w\n", [{counter, 0}]),
+ [{_, N}] = ets:lookup(foo, counter),
+ F(foo, N),
+ io:format("Update complete\n", []),
+ ObjectList = ets:lookup(foo, counter),
+ io:format("Counter: ~w\n", [ObjectList]).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_new.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_new.erl
new file mode 100644
index 0000000000..63f3272912
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_new.erl
@@ -0,0 +1,15 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination. It takes into account multiple ets:new calls that might exist.
+
+-module(ets_insert_new).
+-export([test/0]).
+
+test() ->
+ T1 = ets:new(foo, [public]),
+ T2 = ets:new(bar, []),
+ ets:lookup(T2, counter),
+ aux(T1),
+ aux(T2).
+
+aux(Tab) ->
+ ets:insert(Tab, {counter, 1}).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_param.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_param.erl
new file mode 100644
index 0000000000..a479a31792
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/ets_insert_param.erl
@@ -0,0 +1,26 @@
+%% This tests the presence of possible races due to an ets:lookup/ets:insert
+%% combination in higher order functions.
+
+-module(ets_insert_param).
+-export([start/1]).
+
+start(User) ->
+ Table = ets:new(table, [public]),
+ mod:process(Table),
+ [{_, Msg}] = ets:lookup(Table, welcome_msg),
+ case User of
+ root ->
+ ets:insert(Table, {welcome_msg, Msg ++ "root"}),
+ ets:insert(Table, {pass, Pass = generate_password(ets:lookup(Table, pass))
+ ++ generate_strong_password(ets:lookup(Table, pass))});
+ user ->
+ ets:insert(Table, {welcome_msg, Msg ++ "user"}),
+ ets:insert(Table, {pass, Pass = generate_password(ets:lookup(Table, pass))})
+ end,
+ io:format("\nYour new pass is ~w\n", [Pass]).
+
+generate_password([{_, N}]) ->
+ lists:map(fun (_) -> random:uniform(90)+$\s+1 end, lists:seq(1,N)).
+
+generate_strong_password([{_, N}]) ->
+ lists:map(fun (_) -> random:uniform(90)+$\s+1 end, lists:seq(1,(N rem 2) * 5)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/extract_translations.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/extract_translations.erl
new file mode 100644
index 0000000000..4bf6f1b198
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/extract_translations.erl
@@ -0,0 +1,294 @@
+%%%----------------------------------------------------------------------
+%%% File : extract_translations.erl
+%%% Author : Sergei Golovan <[email protected]>
+%%% Purpose : Auxiliary tool for interface/messages translators
+%%% Created : 23 Apr 2005 by Sergei Golovan <[email protected]>
+%%% Id : $Id: extract_translations.erl,v 1.1 2009/08/17 09:18:59 maria Exp $
+%%%----------------------------------------------------------------------
+
+-module(extract_translations).
+-author('[email protected]').
+
+-export([start/0]).
+
+-define(STATUS_SUCCESS, 0).
+-define(STATUS_ERROR, 1).
+-define(STATUS_USAGE, 2).
+
+-include_lib("kernel/include/file.hrl").
+
+
+start() ->
+ ets:new(translations, [named_table, public]),
+ ets:new(translations_obsolete, [named_table, public]),
+ ets:new(files, [named_table, public]),
+ ets:new(vars, [named_table, public]),
+ case init:get_plain_arguments() of
+ ["-srcmsg2po", Dir, File] ->
+ print_po_header(File),
+ Status = process(Dir, File, srcmsg2po),
+ halt(Status);
+ ["-unused", Dir, File] ->
+ Status = process(Dir, File, unused),
+ halt(Status);
+ [Dir, File] ->
+ Status = process(Dir, File, used),
+ halt(Status);
+ _ ->
+ print_usage(),
+ halt(?STATUS_USAGE)
+ end.
+
+
+process(Dir, File, Used) ->
+ case load_file(File) of
+ {error, Reason} ->
+ io:format("~s: ~s~n", [File, file:format_error(Reason)]),
+ ?STATUS_ERROR;
+ _ ->
+ FileList = find_src_files(Dir),
+ lists:foreach(
+ fun(F) ->
+ parse_file(Dir, F, Used)
+ end, FileList),
+ case Used of
+ unused ->
+ ets:foldl(fun({Key, _}, _) ->
+ io:format("~p~n", [Key])
+ end, ok, translations);
+ srcmsg2po ->
+ ets:foldl(fun({Key, Trans}, _) ->
+ print_translation_obsolete(Key, Trans)
+ end, ok, translations_obsolete);
+ _ ->
+ ok
+ end,
+ ?STATUS_SUCCESS
+ end.
+
+parse_file(Dir, File, Used) ->
+ ets:delete_all_objects(vars),
+ case epp:parse_file(File, [Dir, filename:dirname(File) | code:get_path()], []) of
+ {ok, Forms} ->
+ lists:foreach(
+ fun(F) ->
+ parse_form(Dir, File, F, Used)
+ end, Forms);
+ _ ->
+ ok
+ end.
+
+parse_form(Dir, File, Form, Used) ->
+ case Form of
+ %%{undefined, Something} ->
+ %% io:format("Undefined: ~p~n", [Something]);
+ {call,
+ _,
+ {remote, _, {atom, _, translate}, {atom, _, translate}},
+ [_, {string, Line, Str}]
+ } ->
+ process_string(Dir, File, Line, Str, Used);
+ {call,
+ _,
+ {remote, _, {atom, _, translate}, {atom, _, translate}},
+ [_, {var, _, Name}]
+ } ->
+ case ets:lookup(vars, Name) of
+ [{_Name, Value, Line}] ->
+ process_string(Dir, File, Line, Value, Used);
+ _ ->
+ ok
+ end;
+ {match,
+ _,
+ {var, _, Name},
+ {string, Line, Value}
+ } ->
+ ets:insert(vars, {Name, Value, Line});
+ L when is_list(L) ->
+ lists:foreach(
+ fun(F) ->
+ parse_form(Dir, File, F, Used)
+ end, L);
+ T when is_tuple(T) ->
+ lists:foreach(
+ fun(F) ->
+ parse_form(Dir, File, F, Used)
+ end, tuple_to_list(T));
+ _ ->
+ ok
+ end.
+
+process_string(_Dir, _File, _Line, "", _Used) ->
+ ok;
+
+process_string(_Dir, File, Line, Str, Used) ->
+ case {ets:lookup(translations, Str), Used} of
+ {[{_Key, _Trans}], unused} ->
+ ets:delete(translations, Str);
+ {[{_Key, _Trans}], used} ->
+ ok;
+ {[{_Key, Trans}], srcmsg2po} ->
+ ets:delete(translations_obsolete, Str),
+ print_translation(File, Line, Str, Trans);
+ {_, used} ->
+ case ets:lookup(files, File) of
+ [{_}] ->
+ ok;
+ _ ->
+ io:format("~n% ~s~n", [File]),
+ ets:insert(files, {File})
+ end,
+ case Str of
+ [] -> ok;
+ _ -> io:format("{~p, \"\"}.~n", [Str])
+ end,
+ ets:insert(translations, {Str, ""});
+ {_, srcmsg2po} ->
+ case ets:lookup(files, File) of
+ [{_}] ->
+ ok;
+ _ ->
+ ets:insert(files, {File})
+ end,
+ ets:insert(translations, {Str, ""}),
+ print_translation(File, Line, Str, "");
+ _ ->
+ ok
+ end.
+
+load_file(File) ->
+ case file:consult(File) of
+ {ok, Terms} ->
+ lists:foreach(
+ fun({Orig, Trans}) ->
+ case Trans of
+ "" ->
+ ok;
+ _ ->
+ ets:insert(translations, {Orig, Trans}),
+ ets:insert(translations_obsolete, {Orig, Trans})
+ end
+ end, Terms);
+ Err ->
+ Err
+ end.
+
+find_src_files(Dir) ->
+ case file:list_dir(Dir) of
+ {ok, FileList} ->
+ recurse_filelist(
+ lists:map(
+ fun(F) ->
+ filename:join(Dir, F)
+ end, FileList));
+ _ ->
+ []
+ end.
+
+recurse_filelist(FileList) ->
+ recurse_filelist(FileList, []).
+
+recurse_filelist([], Acc) ->
+ lists:reverse(Acc);
+
+recurse_filelist([H | T], Acc) ->
+ case file:read_file_info(H) of
+ {ok, #file_info{type = directory}} ->
+ recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc);
+ {ok, #file_info{type = regular}} ->
+ case string:substr(H, string:len(H) - 3) of
+ ".erl" ->
+ recurse_filelist(T, [H | Acc]);
+ ".hrl" ->
+ recurse_filelist(T, [H | Acc]);
+ _ ->
+ recurse_filelist(T, Acc)
+ end;
+ _ ->
+ recurse_filelist(T, Acc)
+ end.
+
+
+print_usage() ->
+ io:format(
+ "Usage: extract_translations [-unused] dir file~n"
+ "~n"
+ "Example:~n"
+ " extract_translations . ./msgs/ru.msg~n"
+ ).
+
+
+%%%
+%%% Gettext
+%%%
+
+print_po_header(File) ->
+ MsgProps = get_msg_header_props(File),
+ {Language, [LastT | AddT]} = prepare_props(MsgProps),
+ application:load(ejabberd),
+ {ok, Version} = application:get_key(ejabberd, vsn),
+ print_po_header(Version, Language, LastT, AddT).
+
+get_msg_header_props(File) ->
+ {ok, F} = file:open(File, [read]),
+ Lines = get_msg_header_props(F, []),
+ file:close(F),
+ Lines.
+
+get_msg_header_props(F, Lines) ->
+ String = io:get_line(F, ""),
+ case io_lib:fread("% ", String) of
+ {ok, [], RemString} ->
+ case io_lib:fread("~s", RemString) of
+ {ok, [Key], Value} when Value /= "\n" ->
+ %% The first character in Value is a blankspace:
+ %% And the last characters are 'slash n'
+ ValueClean = string:substr(Value, 2, string:len(Value)-2),
+ get_msg_header_props(F, Lines ++ [{Key, ValueClean}]);
+ _ ->
+ get_msg_header_props(F, Lines)
+ end;
+ _ ->
+ Lines
+ end.
+
+prepare_props(MsgProps) ->
+ Language = proplists:get_value("Language:", MsgProps),
+ Authors = proplists:get_all_values("Author:", MsgProps),
+ {Language, Authors}.
+
+print_po_header(Version, Language, LastTranslator, AdditionalTranslatorsList) ->
+ AdditionalTranslatorsString = build_additional_translators(AdditionalTranslatorsList),
+ HeaderString =
+ "msgid \"\"\n"
+ "msgstr \"\"\n"
+ "\"Project-Id-Version: " ++ Version ++ "\\n\"\n"
+ ++ "\"X-Language: " ++ Language ++ "\\n\"\n"
+ "\"Last-Translator: " ++ LastTranslator ++ "\\n\"\n"
+ ++ AdditionalTranslatorsString ++
+ "\"MIME-Version: 1.0\\n\"\n"
+ "\"Content-Type: text/plain; charset=UTF-8\\n\"\n"
+ "\"Content-Transfer-Encoding: 8bit\\n\"\n",
+ io:format("~s~n", [HeaderString]).
+
+build_additional_translators(List) ->
+ lists:foldl(
+ fun(T, Str) ->
+ Str ++ "\"X-Additional-Translator: " ++ T ++ "\\n\"\n"
+ end,
+ "",
+ List).
+
+print_translation(File, Line, Str, StrT) ->
+ {ok, StrQ, _} = regexp:gsub(Str, "\"", "\\\""),
+ {ok, StrTQ, _} = regexp:gsub(StrT, "\"", "\\\""),
+ io:format("#: ~s:~p~nmsgid \"~s\"~nmsgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]).
+
+print_translation_obsolete(Str, StrT) ->
+ File = "unknown.erl",
+ Line = 1,
+ {ok, StrQ, _} = regexp:gsub(Str, "\"", "\\\""),
+ {ok, StrTQ, _} = regexp:gsub(StrT, "\"", "\\\""),
+ io:format("#: ~s:~p~n#~~ msgid \"~s\"~n#~~ msgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]).
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race1.erl
new file mode 100644
index 0000000000..74d17aab0c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race1.erl
@@ -0,0 +1,33 @@
+%% This tests that the race condition detection between mnesia:dirty_read/
+%% mnesia:dirty_write is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(mnesia_diff_atoms_race1).
+-export([test/2]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+test(Eno, Raise) ->
+ {race(employee, Eno, Raise), no_race(employee, Eno, Raise)}.
+
+race(Tab, Eno, Raise) ->
+ [E] = mnesia:dirty_read(Tab, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ aux(Tab, New).
+
+no_race(Tab, Eno, Raise) ->
+ [E] = mnesia:dirty_read(Tab, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ AnotherTab = employer,
+ aux(AnotherTab, New).
+
+
+aux(Table, Record) ->
+ mnesia:dirty_write(Table, Record).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race2.erl
new file mode 100644
index 0000000000..e92405a673
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_diff_atoms_race2.erl
@@ -0,0 +1,37 @@
+%% This tests that the race condition detection between mnesia:dirty_read/
+%% mnesia:dirty_write is robust even when the functions are called with
+%% different atoms as arguments.
+
+-module(mnesia_diff_atoms_race2).
+-export([test/2]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+-record(employer, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+test(Eno, Raise) ->
+ {race(employee, Eno, Raise), no_race(employee, Eno, Raise)}.
+
+race(Tab, Eno, Raise) ->
+ [E] = mnesia:dirty_read(Tab, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ aux(New).
+
+no_race(Tab, Eno, Raise) ->
+ [E] = mnesia:dirty_read(Tab, Eno),
+ AnotherRecord = #employer{},
+ aux(AnotherRecord).
+
+aux(Record) ->
+ mnesia:dirty_write(Record).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_one_write_two.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_one_write_two.erl
new file mode 100644
index 0000000000..81e460be45
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_one_write_two.erl
@@ -0,0 +1,22 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account the argument types
+%% of the calls.
+
+-module(mnesia_dirty_read_one_write_two).
+-export([raise/2]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise) ->
+ [E] = mnesia:dirty_read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(employee, New).
+
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_two_write_one.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_two_write_one.erl
new file mode 100644
index 0000000000..515e9f11de
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_two_write_one.erl
@@ -0,0 +1,22 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account the argument types
+%% of the calls.
+
+-module(mnesia_dirty_read_two_write_one).
+-export([raise/2]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise) ->
+ [E] = mnesia:dirty_read(employee, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(New).
+
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double1.erl
new file mode 100644
index 0000000000..2bd18e4772
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double1.erl
@@ -0,0 +1,25 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account multiple
+%% mnesia:dirty_writes that might exist.
+
+-module(mnesia_dirty_read_write_double1).
+-export([raise/3]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise, Room) ->
+ [E] = mnesia:dirty_read(employee, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(employee, New),
+ move(E, Room).
+
+move(E, Room) ->
+ New = E#employee{room_no = Room},
+ mnesia:dirty_write(employee, New).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double2.erl
new file mode 100644
index 0000000000..cdbfdc700a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double2.erl
@@ -0,0 +1,25 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account multiple
+%% mnesia:dirty_writes that might exist.
+
+-module(mnesia_dirty_read_write_double2).
+-export([raise/3]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise, Room) ->
+ [E] = mnesia:dirty_read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(New),
+ move(E, Room).
+
+move(E, Room) ->
+ New = E#employee{room_no = Room},
+ mnesia:dirty_write(New).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double3.erl
new file mode 100644
index 0000000000..051524917e
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double3.erl
@@ -0,0 +1,25 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account multiple
+%% mnesia:dirty_writes that might exist.
+
+-module(mnesia_dirty_read_write_double3).
+-export([raise/3]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise, Room) ->
+ [E] = mnesia:dirty_read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(employee, New),
+ move(E, Room).
+
+move(E, Room) ->
+ New = E#employee{room_no = Room},
+ mnesia:dirty_write(employee, New).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double4.erl
new file mode 100644
index 0000000000..96752a6045
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_double4.erl
@@ -0,0 +1,25 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account multiple
+%% mnesia:dirty_writes that might exist.
+
+-module(mnesia_dirty_read_write_double4).
+-export([raise/3]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise, Room) ->
+ [E] = mnesia:dirty_read(employee, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(New),
+ move(E, Room).
+
+move(E, Room) ->
+ New = E#employee{room_no = Room},
+ mnesia:dirty_write(New).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_one.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_one.erl
new file mode 100644
index 0000000000..7ff546a9ea
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_one.erl
@@ -0,0 +1,22 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account the argument types
+%% of the calls.
+
+-module(mnesia_dirty_read_write_one).
+-export([raise/2]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise) ->
+ [E] = mnesia:dirty_read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(New).
+
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_two.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_two.erl
new file mode 100644
index 0000000000..10952ac86d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/mnesia_dirty_read_write_two.erl
@@ -0,0 +1,22 @@
+%% This tests the presence of possible races due to an mnesia:dirty_read/
+%% mnesia:dirty_write combination. It takes into account the argument types
+%% of the calls.
+
+-module(mnesia_dirty_read_write_two).
+-export([raise/2]).
+
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+
+raise(Eno, Raise) ->
+ [E] = mnesia:dirty_read(employee, Eno),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:dirty_write(employee, New).
+
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow1.erl
new file mode 100644
index 0000000000..e65f6c3e23
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow1.erl
@@ -0,0 +1,17 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination. It takes into account control flow that might exist.
+
+-module(whereis_control_flow1).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false -> register(AnAtom, Pid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow2.erl
new file mode 100644
index 0000000000..41039482c9
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow2.erl
@@ -0,0 +1,19 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination. It takes into account control flow that might exist.
+
+-module(whereis_control_flow2).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true ->
+ io:format("self",[]),
+ register(AnAtom, Pid);
+ false -> register(AnAtom, Pid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow3.erl
new file mode 100644
index 0000000000..87b2976165
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow3.erl
@@ -0,0 +1,25 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination. It takes into account control flow that might exist.
+
+-module(whereis_control_flow3).
+-export([start/3]).
+
+start(AnAtom, Fun, FunName) ->
+ Pid =
+ case FunName of
+ master ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end;
+ slave ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end
+ end,
+ register(AnAtom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow4.erl
new file mode 100644
index 0000000000..9292006fa8
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow4.erl
@@ -0,0 +1,29 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination. It takes into account control flow that might exist.
+
+-module(whereis_control_flow4).
+-export([start/1]).
+
+start(Fun) ->
+ case whereis(maria) of
+ undefined ->
+ Pid1 = spawn(Fun),
+ case Pid1 =:= self() of
+ true ->
+ case whereis(kostis) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ case Pid2 =:= self() of
+ true ->
+ register(maria, Pid1),
+ register(kostis, Pid2);
+ false -> ok
+ end;
+ P when is_pid(P) ->
+ ok
+ end;
+ false -> ok
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow5.erl
new file mode 100644
index 0000000000..8de9cb2dad
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow5.erl
@@ -0,0 +1,12 @@
+%% This tests the presence of possible races due to a whereis/unregister
+%% combination. It takes into account control flow that might exist.
+
+-module(whereis_control_flow5).
+-export([start/1]).
+
+start(AnAtom) ->
+ case whereis(AnAtom) of
+ undefined -> ok;
+ P when is_pid(P) ->
+ unregister(AnAtom)
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow6.erl
new file mode 100644
index 0000000000..03c5095a50
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_control_flow6.erl
@@ -0,0 +1,12 @@
+%% This tests the presence of possible races due to a whereis/unregister
+%% combination. It takes into account control flow that might exist.
+
+-module(whereis_control_flow6).
+-export([start/0]).
+
+start() ->
+ case whereis(kostis) of
+ undefined -> ok;
+ P when is_pid(P) ->
+ unregister(kostis)
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_no_race.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_no_race.erl
new file mode 100644
index 0000000000..dcadcb3683
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_no_race.erl
@@ -0,0 +1,24 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust even when the functions are called with different atoms
+%% as arguments.
+
+-module(whereis_diff_atoms_no_race).
+-export([test/0]).
+
+test() ->
+ Fun = fun () -> foo end,
+ {no_race(maria, Fun)}.
+
+no_race(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ AnotherAtom = kostis,
+ aux(AnotherAtom, Pid);
+ P when is_pid(P) ->
+ ok
+ end.
+
+aux(Atom, Pid) ->
+ register(Atom, Pid).
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_race.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_race.erl
new file mode 100644
index 0000000000..7e302247f8
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_atoms_race.erl
@@ -0,0 +1,35 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust even when the functions are called with different atoms
+%% as arguments.
+
+-module(whereis_diff_atoms_race).
+-export([test/0]). %, race/1, no_race/1]).
+
+test() ->
+ Fun = fun () -> foo end,
+ {race(maria, Fun), no_race(maria, Fun)}.
+
+race(AnAtom, Fun) ->
+ %AnAtom = maria,
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ aux(AnAtom, Pid);
+ P when is_pid(P) ->
+ ok
+ end.
+
+no_race(AnAtom, Fun) ->
+ %AnAtom = maria,
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ AnotherAtom = kostis,
+ aux(AnotherAtom, Pid);
+ P when is_pid(P) ->
+ ok
+ end.
+
+aux(Atom, Pid) ->
+ register(Atom, Pid).
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1.erl
new file mode 100644
index 0000000000..6a1c197c06
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions1).
+-export([start/2]).
+
+continue(Fun) ->
+ case whereis(master) of
+ undefined ->
+ register(master, spawn(Fun));
+ _ -> ok
+ end.
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ register(AnAtom, Pid);
+ _ ->
+ ok
+ end,
+ continue(Fun).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_nested.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_nested.erl
new file mode 100644
index 0000000000..0a77c78ba3
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_nested.erl
@@ -0,0 +1,23 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions1_nested).
+-export([test/2]).
+
+test(AnAtom, Fun) ->
+ start(AnAtom, Fun).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ race1(AnAtom, Pid);
+ P when is_pid(P) ->
+ true
+ end.
+
+race1(Atom, Pid) ->
+ race2(Atom, Pid).
+
+race2(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_pathsens.erl
new file mode 100644
index 0000000000..53955a7fa1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_pathsens.erl
@@ -0,0 +1,32 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_functions1_pathsens).
+-export([test/1]).
+
+test(FunName) ->
+ start(kostis, mod:function(), FunName).
+
+start(AnAtom, Fun, FunName) ->
+ Pid =
+ case FunName of
+ master ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end;
+ slave ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end
+ end,
+ race(AnAtom, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_twice.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_twice.erl
new file mode 100644
index 0000000000..2e87caff4f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions1_twice.erl
@@ -0,0 +1,30 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having multiple calls in separate functions.
+
+-module(whereis_diff_functions1_twice).
+-export([test/2]).
+
+test(AnAtom, Fun) ->
+ start(AnAtom, Fun).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid1 = spawn(Fun),
+ race(AnAtom, Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race_again(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end;
+ P when is_pid(P) ->
+ true
+ end.
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
+
+race_again(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2.erl
new file mode 100644
index 0000000000..1ec8d194be
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2.erl
@@ -0,0 +1,25 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions2).
+-export([test/0]).
+
+test() ->
+ start(kostis, mod:function()).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_nested.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_nested.erl
new file mode 100644
index 0000000000..415f73d555
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_nested.erl
@@ -0,0 +1,20 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions2_nested).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ race1(AnAtom, Pid);
+ P when is_pid(P) ->
+ true
+ end.
+
+race1(Atom, Pid) ->
+ race2(Atom, Pid).
+
+race2(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_pathsens.erl
new file mode 100644
index 0000000000..cbd9a7d016
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_pathsens.erl
@@ -0,0 +1,29 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_functions2_pathsens).
+-export([race/4]).
+
+start(AnAtom, Fun, FunName) ->
+ Pid =
+ case FunName of
+ master ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end;
+ slave ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end
+ end.
+
+race(Atom, Fun, FunName, Pid) ->
+ start(Atom, Fun, FunName),
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_twice.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_twice.erl
new file mode 100644
index 0000000000..d8e4987758
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions2_twice.erl
@@ -0,0 +1,27 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having multiple calls in separate functions.
+
+-module(whereis_diff_functions2_twice).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid1 = spawn(Fun),
+ race(AnAtom, Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race_again(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end;
+ P when is_pid(P) ->
+ true
+ end.
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
+
+race_again(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3.erl
new file mode 100644
index 0000000000..7d4e0905ef
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions3).
+-export([start/1]).
+
+start(AnAtom) ->
+ register(AnAtom, race(AnAtom)).
+
+race(Atom) ->
+ whereis(Atom).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_nested.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_nested.erl
new file mode 100644
index 0000000000..b4129dc83b
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_nested.erl
@@ -0,0 +1,21 @@
+%% This tests that the race condition detection between whereis/unregister
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions3_nested).
+-export([test/1]).
+
+test(AnAtom) ->
+ start(AnAtom).
+
+start(AnAtom) ->
+ case whereis(AnAtom) of
+ undefined -> true;
+ P when is_pid(P) ->
+ race1(AnAtom)
+ end.
+
+race1(Atom) ->
+ race2(Atom).
+
+race2(Atom) ->
+ unregister(Atom).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_pathsens.erl
new file mode 100644
index 0000000000..f06e43024b
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions3_pathsens.erl
@@ -0,0 +1,29 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_functions3_pathsens).
+-export([start/3]).
+
+start(AnAtom, Fun, FunName) ->
+ Pid =
+ case FunName of
+ master ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end;
+ slave ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end
+ end,
+ race(AnAtom, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions4.erl
new file mode 100644
index 0000000000..334485921c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions4.erl
@@ -0,0 +1,32 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions4).
+-export([test/2]).
+
+test(AnAtom, Fun) ->
+ start(AnAtom, Fun).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race(AnAtom, Pid2),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid3 = spawn(Fun),
+ race(AnAtom, Pid3);
+ P when is_pid(P) ->
+ true
+ end;
+ P when is_pid(P) ->
+ true
+ end.
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions5.erl
new file mode 100644
index 0000000000..b4459273f9
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions5.erl
@@ -0,0 +1,22 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions5).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions6.erl
new file mode 100644
index 0000000000..ccf0f5e127
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_functions6.erl
@@ -0,0 +1,29 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions.
+
+-module(whereis_diff_functions6).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race(AnAtom, Pid2),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid3 = spawn(Fun),
+ race(AnAtom, Pid3);
+ P when is_pid(P) ->
+ true
+ end;
+ P when is_pid(P) ->
+ true
+ end.
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules1.erl
new file mode 100644
index 0000000000..00cb29cec0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules1.erl
@@ -0,0 +1,16 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules1).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_diff_modules2:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_diff_modules2:race(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules2.erl
new file mode 100644
index 0000000000..dabb7fd2da
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1/whereis_diff_modules2.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules2).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules1_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules1_pathsens.erl
new file mode 100644
index 0000000000..3dbb645e65
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules1_pathsens.erl
@@ -0,0 +1,26 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules (backward analysis).
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_modules1_pathsens).
+-export([start/3]).
+
+start(AnAtom, Fun, FunName) ->
+ Pid =
+ case FunName of
+ master ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end;
+ slave ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end
+ end,
+ whereis_diff_modules2_pathsens:race(AnAtom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules2_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules2_pathsens.erl
new file mode 100644
index 0000000000..99331b81b1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_pathsens/whereis_diff_modules2_pathsens.erl
@@ -0,0 +1,12 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules (backward analysis).
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_modules2_pathsens).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules1_rec.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules1_rec.erl
new file mode 100644
index 0000000000..a397954eea
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules1_rec.erl
@@ -0,0 +1,22 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in an indirectly recursive inter-modular function.
+
+-module(whereis_diff_modules1_rec).
+-export([start/4]).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ whereis_diff_modules2_rec:continue(NextAtom, mod:next(), Pid, Id)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules2_rec.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules2_rec.erl
new file mode 100644
index 0000000000..4b46b4a8e5
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules1_rec/whereis_diff_modules2_rec.erl
@@ -0,0 +1,8 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in a recursive function.
+
+-module(whereis_diff_modules2_rec).
+-export([continue/4]).
+
+continue(Atom, NextAtom, Fun, Id) ->
+ whereis_diff_modules1_rec:start(Atom, NextAtom, Fun, Id).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules3.erl
new file mode 100644
index 0000000000..60b5a1d378
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules3.erl
@@ -0,0 +1,8 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules3).
+-export([start/1]).
+
+start(AnAtom) ->
+ register(AnAtom, whereis_diff_modules4:race(AnAtom)).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules4.erl
new file mode 100644
index 0000000000..6ab9a4d824
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2/whereis_diff_modules4.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules4).
+-export([no_race/1, race/1]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom) ->
+ whereis(Atom).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules3_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules3_pathsens.erl
new file mode 100644
index 0000000000..1eaa954fa1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules3_pathsens.erl
@@ -0,0 +1,25 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules (forward analysis).
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_modules3_pathsens).
+-export([start/3]).
+
+start(AnAtom, Fun, FunName) ->
+ Pid =
+ case FunName of
+ master ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end;
+ slave ->
+ case whereis(AnAtom) of
+ undefined ->
+ spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules4_pathsens.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules4_pathsens.erl
new file mode 100644
index 0000000000..f23a63c8f0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_pathsens/whereis_diff_modules4_pathsens.erl
@@ -0,0 +1,13 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules (forward analysis).
+%% It takes into account control flow that might exist.
+
+-module(whereis_diff_modules4_pathsens).
+-export([no_race/1, race/4]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Fun, FunName, Pid) ->
+ whereis_diff_modules3_pathsens:start(Atom, Fun, FunName),
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules3_rec.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules3_rec.erl
new file mode 100644
index 0000000000..0320140768
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules3_rec.erl
@@ -0,0 +1,25 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in an indirectly recursive inter-modular function.
+
+-module(whereis_diff_modules3_rec).
+-export([test/0, start/4]).
+
+test() ->
+ start(undefined, second, mod:f(), self()).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ whereis_diff_modules4_rec:continue(NextAtom, mod:next(), Pid, Id)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules4_rec.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules4_rec.erl
new file mode 100644
index 0000000000..d49c59ed5c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules2_rec/whereis_diff_modules4_rec.erl
@@ -0,0 +1,8 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in a recursive function.
+
+-module(whereis_diff_modules4_rec).
+-export([continue/4]).
+
+continue(Atom, NextAtom, Fun, Id) ->
+ whereis_diff_modules3_rec:start(Atom, NextAtom, Fun, Id).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules5.erl
new file mode 100644
index 0000000000..591732aa31
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules5.erl
@@ -0,0 +1,23 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules5).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_diff_modules6:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_diff_modules6:race(AnAtom, Pid2),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid3 = spawn(Fun),
+ whereis_diff_modules6:race(AnAtom, Pid3);
+ P when is_pid(P) ->
+ true
+ end;
+ P when is_pid(P) ->
+ true
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules6.erl
new file mode 100644
index 0000000000..ec6c245c9a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules3/whereis_diff_modules6.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules6).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules1_nested.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules1_nested.erl
new file mode 100644
index 0000000000..a25d2f8784
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules1_nested.erl
@@ -0,0 +1,14 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules1_nested).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ whereis_diff_modules2_nested:race(AnAtom, Pid);
+ P when is_pid(P) ->
+ true
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules2_nested.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules2_nested.erl
new file mode 100644
index 0000000000..4b4c058884
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules2_nested.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules2_nested).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ whereis_diff_modules3_nested:race(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules3_nested.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules3_nested.erl
new file mode 100644
index 0000000000..5412660b16
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_nested/whereis_diff_modules3_nested.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules3_nested).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules1_twice.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules1_twice.erl
new file mode 100644
index 0000000000..92f2cb1fbc
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules1_twice.erl
@@ -0,0 +1,21 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having multiple calls in separate modules.
+
+-module(whereis_diff_modules1_twice).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid1 = spawn(Fun),
+ whereis_diff_modules2_twice:race(AnAtom, Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_diff_modules2_twice:race_again(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end;
+ P when is_pid(P) ->
+ true
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules2_twice.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules2_twice.erl
new file mode 100644
index 0000000000..afe5214648
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_modules_twice/whereis_diff_modules2_twice.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate modules.
+
+-module(whereis_diff_modules2_twice).
+-export([race/2, race_again/2]).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
+
+race_again(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_no_race.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_no_race.erl
new file mode 100644
index 0000000000..16f1d91490
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_no_race.erl
@@ -0,0 +1,13 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust even when the functions are called with different variables
+%% as arguments.
+
+-module(whereis_diff_vars_no_race).
+-export([test/3]).
+
+test(AnAtom, AnotherAtom, Pid) ->
+ {aux(AnAtom, Pid), aux(AnotherAtom, Pid)}.
+
+aux(Atom, Pid) ->
+ register(Atom, Pid),
+ whereis(Atom).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_race.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_race.erl
new file mode 100644
index 0000000000..7382d184dc
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_diff_vars_race.erl
@@ -0,0 +1,19 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust even when the functions are called with different variables
+%% as arguments.
+
+-module(whereis_diff_vars_race).
+-export([test/2]).
+
+test(AnAtom, AnotherAtom) ->
+ Fun = fun () -> foo end,
+ {aux(AnAtom, AnotherAtom, Fun), aux(AnotherAtom, AnAtom, Fun)}.
+
+aux(Atom1, Atom2, Fun) ->
+ case whereis(Atom1) of
+ undefined ->
+ Pid = spawn(Fun),
+ register(Atom2, Pid);
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module1.erl
new file mode 100644
index 0000000000..677551c99d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module1.erl
@@ -0,0 +1,19 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module1).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module2:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ continue(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+continue(Atom, Pid) ->
+ whereis_intra_inter_module2:race(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module2.erl
new file mode 100644
index 0000000000..cc2efbecd0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module1/whereis_intra_inter_module2.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module2).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module3.erl
new file mode 100644
index 0000000000..c8103db122
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module3.erl
@@ -0,0 +1,16 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module3).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module4:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_intra_inter_module4:race(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module4.erl
new file mode 100644
index 0000000000..9769f312a8
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module2/whereis_intra_inter_module4.erl
@@ -0,0 +1,14 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module4).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ continue(Atom, Pid).
+
+continue(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module5.erl
new file mode 100644
index 0000000000..2a29779153
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module5.erl
@@ -0,0 +1,19 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module5).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module6:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ continue(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+continue(Atom, Pid) ->
+ whereis_intra_inter_module6:race(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module6.erl
new file mode 100644
index 0000000000..92a589f97f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module3/whereis_intra_inter_module6.erl
@@ -0,0 +1,14 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module6).
+-export([no_race/1, race/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ continue(Atom, Pid).
+
+continue(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module7.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module7.erl
new file mode 100644
index 0000000000..1f702e7af3
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module7.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module7).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ register(AnAtom, continue(AnAtom, Fun)).
+
+continue(AnAtom, Fun) ->
+ whereis_intra_inter_module8:continue(AnAtom, Fun).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module8.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module8.erl
new file mode 100644
index 0000000000..581817308b
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module4/whereis_intra_inter_module8.erl
@@ -0,0 +1,13 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module8).
+-export([continue/2]).
+
+continue(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module10.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module10.erl
new file mode 100644
index 0000000000..7ed50ea742
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module10.erl
@@ -0,0 +1,16 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module10).
+-export([continue/2]).
+
+continue(AnAtom, Fun) ->
+ aux(AnAtom, Fun).
+
+aux(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module9.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module9.erl
new file mode 100644
index 0000000000..5c5d92b770
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module5/whereis_intra_inter_module9.erl
@@ -0,0 +1,11 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module9).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ register(AnAtom, continue(AnAtom, Fun)).
+
+continue(AnAtom, Fun) ->
+ whereis_intra_inter_module10:continue(AnAtom, Fun).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module11.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module11.erl
new file mode 100644
index 0000000000..82abe2f4a8
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module11.erl
@@ -0,0 +1,27 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module11).
+-export([start/2, start_again/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module12:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_intra_inter_module12:race(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+start_again(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module12:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_intra_inter_module12:continue(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module12.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module12.erl
new file mode 100644
index 0000000000..2160780d8e
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module6/whereis_intra_inter_module12.erl
@@ -0,0 +1,14 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module12).
+-export([no_race/1, race/2, continue/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ continue(Atom, Pid).
+
+continue(Atom, Pid) ->
+ register(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module13.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module13.erl
new file mode 100644
index 0000000000..3cd5cc6fa6
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module13.erl
@@ -0,0 +1,19 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module13).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module14:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ continue(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+continue(Atom, Pid) ->
+ whereis_intra_inter_module14:race(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module14.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module14.erl
new file mode 100644
index 0000000000..2de6c91985
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module7/whereis_intra_inter_module14.erl
@@ -0,0 +1,23 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module14).
+-export([no_race/1, race/2, start/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ race(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module15.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module15.erl
new file mode 100644
index 0000000000..c60d166fa9
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module15.erl
@@ -0,0 +1,19 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module15).
+-export([start/2, continue/2]).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ whereis_intra_inter_module16:no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ continue(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
+continue(Atom, Pid) ->
+ whereis_intra_inter_module16:race(Atom, Pid).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module16.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module16.erl
new file mode 100644
index 0000000000..6c170dc851
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_intra_inter_module8/whereis_intra_inter_module16.erl
@@ -0,0 +1,23 @@
+%% This tests that the race condition detection between whereis/register
+%% is robust w.r.t. having the calls in separate functions and modules.
+
+-module(whereis_intra_inter_module16).
+-export([no_race/1, race/2, start/2]).
+
+no_race(Pid) ->
+ register(master, Pid).
+
+race(Atom, Pid) ->
+ register(Atom, Pid).
+
+start(AnAtom, Fun) ->
+ Pid1 = spawn(Fun),
+ no_race(Pid1),
+ case whereis(AnAtom) of
+ undefined ->
+ Pid2 = spawn(Fun),
+ whereis_intra_inter_module15:continue(AnAtom, Pid2);
+ P when is_pid(P) ->
+ true
+ end.
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param.erl
new file mode 100644
index 0000000000..7bcde321a1
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param.erl
@@ -0,0 +1,16 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination in higher order functions.
+
+-module(whereis_param).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ register(AnAtom, continue(AnAtom, Fun)).
+
+continue(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module1.erl
new file mode 100644
index 0000000000..ab7c9b4cf9
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module1.erl
@@ -0,0 +1,9 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination in higher order functions and inter-module calls.
+
+-module(whereis_param_inter_module1).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ register(AnAtom, whereis_param_inter_module2:continue(AnAtom, Fun)).
+
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module2.erl
new file mode 100644
index 0000000000..61252add9a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_param_inter_module/whereis_param_inter_module2.erl
@@ -0,0 +1,13 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination in higher order functions and inter-module calls.
+
+-module(whereis_param_inter_module2).
+-export([continue/2]).
+
+continue(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun);
+ P when is_pid(P) ->
+ P
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function1.erl
new file mode 100644
index 0000000000..c8095fbf4c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function1.erl
@@ -0,0 +1,19 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination in a recursive function.
+
+-module(whereis_rec_function1).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ register(AnAtom, Pid),
+ start(AnAtom, Fun)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function2.erl
new file mode 100644
index 0000000000..2721c9e19c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function2.erl
@@ -0,0 +1,24 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in a recursive function.
+
+-module(whereis_rec_function2).
+-export([test/0]).
+
+test() ->
+ start(undefined, second, mod:f(), self()).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false -> start(NextAtom, mod:next(), Pid, Id)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function3.erl
new file mode 100644
index 0000000000..e101f34fba
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function3.erl
@@ -0,0 +1,27 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in a recursive function.
+
+-module(whereis_rec_function3).
+-export([test/0]).
+
+test() ->
+ start(undefined, second, mod:f(), self()).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ Pid =
+ case whereis(NextAtom) of
+ undefined -> spawn(Fun);
+ P1 when is_pid(P1) -> P1
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ case Pid =:= self() of
+ true -> ok;
+ false -> start(NextAtom, mod:next(), Pid, Id), io:format("", [])
+ end;
+ P2 when is_pid(P2) -> ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function4.erl
new file mode 100644
index 0000000000..4894d3397b
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function4.erl
@@ -0,0 +1,27 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in an indirectly recursive function.
+
+-module(whereis_rec_function4).
+-export([test/0]).
+
+test() ->
+ start(undefined, second, mod:f(), self()).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false -> continue(NextAtom, mod:next(), Pid, Id)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
+
+continue(Atom, NextAtom, Fun, Id) ->
+ start(Atom, NextAtom, Fun, Id).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function5.erl
new file mode 100644
index 0000000000..d821f829a2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function5.erl
@@ -0,0 +1,21 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in a recursive function.
+
+-module(whereis_rec_function5).
+-export([start/4]).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false -> start(NextAtom, mod:next(), Pid, Id)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function6.erl
new file mode 100644
index 0000000000..4ec4baf0be
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function6.erl
@@ -0,0 +1,24 @@
+%% This tests the presence of possible races due to a register/whereis
+%% combination in an indirectly recursive function.
+
+-module(whereis_rec_function6).
+-export([start/4]).
+
+start(AnAtom, NextAtom, Fun, Id) ->
+ case AnAtom of
+ undefined -> register(start, Id);
+ _ -> register(AnAtom, Id)
+ end,
+ case whereis(NextAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false -> continue(NextAtom, mod:next(), Pid, Id)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
+
+continue(Atom, NextAtom, Fun, Id) ->
+ start(Atom, NextAtom, Fun, Id).
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function7.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function7.erl
new file mode 100644
index 0000000000..7667443117
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function7.erl
@@ -0,0 +1,19 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination in a recursive function.
+
+-module(whereis_rec_function7).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ start(AnAtom, Fun),
+ register(AnAtom, Pid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function8.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function8.erl
new file mode 100644
index 0000000000..a06fb75f64
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_rec_function8.erl
@@ -0,0 +1,22 @@
+%% This tests the presence of possible races due to a whereis/register
+%% combination in a recursive function.
+
+-module(whereis_rec_function8).
+-export([test/2]).
+
+test(AnAtom, Fun) ->
+ start(AnAtom, Fun).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ start(AnAtom, Fun),
+ register(AnAtom, Pid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_try_catch.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_try_catch.erl
new file mode 100644
index 0000000000..9c8daf8d8c
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_try_catch.erl
@@ -0,0 +1,25 @@
+% This tests that warnings do appear when a whereis/register combination
+% is handled by try/catch.
+
+-module(whereis_try_catch).
+-export([race/1, no_race/1]).
+
+race(Pid) ->
+ case whereis(master) of
+ undefined ->
+ try
+ io:format("exception", [])
+ catch
+ _ -> register(master, Pid)
+ end
+ end.
+
+no_race(Pid) ->
+ case whereis(master) of
+ undefined ->
+ try
+ register(master, Pid)
+ catch
+ _ -> io:format("exception", [])
+ end
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars1.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars1.erl
new file mode 100644
index 0000000000..9b249e72be
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars1.erl
@@ -0,0 +1,17 @@
+%% This tests that no warnings appear when there is no specific
+%% information about the types and the variables are not bound.
+
+-module(whereis_vars1).
+-export([start/3]).
+
+start(AnAtom, OtherAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false -> register(OtherAtom, Pid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars10.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars10.erl
new file mode 100644
index 0000000000..5c1896d6b4
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars10.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars10).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom =/= OtherAtom of
+ true -> ok;
+ false -> register(OtherAtom, Pid)
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars11.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars11.erl
new file mode 100644
index 0000000000..dc8551b3f2
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars11.erl
@@ -0,0 +1,22 @@
+%% This tests that no warnings appear when there is no specific
+%% information about the types and the variables are not bound.
+
+-module(whereis_vars11).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ OtherAtom -> ok;
+ _Other -> register(OtherAtom, Pid)
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars12.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars12.erl
new file mode 100644
index 0000000000..38b0dc5d04
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars12.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars12).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ if
+ AnAtom =:= OtherAtom -> register(OtherAtom, Pid);
+ AnAtom =/= OtherAtom -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars13.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars13.erl
new file mode 100644
index 0000000000..3a04bba02f
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars13.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars13).
+-export([start/3]).
+
+start(AnAtom, APid, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ if
+ {AnAtom, Pid} =:= {OtherAtom, APid} -> register(OtherAtom, APid);
+ {AnAtom, Pid} =/= {OtherAtom, APid} -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars14.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars14.erl
new file mode 100644
index 0000000000..c688847551
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars14.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars14).
+-export([start/3]).
+
+start(AnAtom, APid, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ if
+ [AnAtom, Pid] =:= [OtherAtom, APid] -> register(OtherAtom, APid);
+ [AnAtom, Pid] =/= [OtherAtom, APid] -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars15.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars15.erl
new file mode 100644
index 0000000000..4b3a72537e
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars15.erl
@@ -0,0 +1,23 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars15).
+-export([start/3]).
+
+start(AnAtom, OtherAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ maria -> ok;
+ kostis when AnAtom =:= OtherAtom ->
+ register(OtherAtom, Pid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars16.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars16.erl
new file mode 100644
index 0000000000..7badb8df22
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars16.erl
@@ -0,0 +1,23 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars16).
+-export([start/4]).
+
+start(AnAtom, OtherAtom, APid, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ maria -> ok;
+ kostis when {AnAtom, Pid} =:= {OtherAtom, APid} ->
+ register(OtherAtom, APid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars17.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars17.erl
new file mode 100644
index 0000000000..bc7ef5e980
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars17.erl
@@ -0,0 +1,23 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars17).
+-export([start/4]).
+
+start(AnAtom, OtherAtom, APid, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ maria -> ok;
+ kostis when [AnAtom, Pid] =:= [OtherAtom, APid] ->
+ register(OtherAtom, APid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars18.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars18.erl
new file mode 100644
index 0000000000..06416fa987
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars18.erl
@@ -0,0 +1,22 @@
+%% This tests that no warnings appear when there is no specific
+%% information about the types and the variables are not bound.
+
+-module(whereis_vars18).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom =:= OtherAtom of
+ true -> ok;
+ false -> register(OtherAtom, Pid)
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars19.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars19.erl
new file mode 100644
index 0000000000..ae5b28e42d
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars19.erl
@@ -0,0 +1,23 @@
+%% This tests that no warnings appear when there is no specific
+%% information about the types and the variables are not bound.
+
+-module(whereis_vars19).
+-export([start/3]).
+
+start(AnAtom, OtherAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ maria -> ok;
+ kostis when AnAtom =/= OtherAtom ->
+ register(OtherAtom, Pid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars2.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars2.erl
new file mode 100644
index 0000000000..bafb5d4644
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars2.erl
@@ -0,0 +1,18 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars2).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = AnAtom,
+ case Pid =:= self() of
+ true -> ok;
+ false -> register(OtherAtom, Pid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars20.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars20.erl
new file mode 100644
index 0000000000..87c6caadf0
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars20.erl
@@ -0,0 +1,22 @@
+%% This tests that no warnings appear when there is no specific
+%% information about the types and the variables are not bound.
+
+-module(whereis_vars20).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ if
+ AnAtom =:= OtherAtom -> ok;
+ AnAtom =/= OtherAtom -> register(OtherAtom, Pid)
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars21.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars21.erl
new file mode 100644
index 0000000000..73d22d3467
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars21.erl
@@ -0,0 +1,23 @@
+%% This tests that no warnings appear when there is no specific
+%% information about the types and the variables are not bound.
+
+-module(whereis_vars21).
+-export([start/3]).
+
+start(AnAtom, OtherAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ maria when AnAtom =/= OtherAtom -> ok;
+ kostis when AnAtom =/= OtherAtom ->
+ register(OtherAtom, Pid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars22.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars22.erl
new file mode 100644
index 0000000000..dd16928e33
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars22.erl
@@ -0,0 +1,27 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars22).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ Same =
+ case AnAtom of
+ OtherAtom -> true;
+ _Other -> false
+ end,
+ case Same of
+ true -> register(OtherAtom, Pid);
+ false -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars3.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars3.erl
new file mode 100644
index 0000000000..16c9a6c8bc
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars3.erl
@@ -0,0 +1,18 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars3).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ {OtherAtom, APid} = {AnAtom, Pid},
+ case Pid =:= self() of
+ true -> ok;
+ false -> register(OtherAtom, APid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars4.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars4.erl
new file mode 100644
index 0000000000..da5b329ca9
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars4.erl
@@ -0,0 +1,18 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars4).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ [OtherAtom, APid] = [AnAtom, Pid],
+ case Pid =:= self() of
+ true -> ok;
+ false -> register(OtherAtom, APid)
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars5.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars5.erl
new file mode 100644
index 0000000000..dff8646ea8
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars5.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars5).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom of
+ OtherAtom -> register(OtherAtom, Pid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars6.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars6.erl
new file mode 100644
index 0000000000..cf22ab1883
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars6.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars6).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case {AnAtom, Pid} of
+ {OtherAtom, APid} -> register(OtherAtom, APid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars7.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars7.erl
new file mode 100644
index 0000000000..4bce53982a
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars7.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars7).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case [AnAtom, Pid] of
+ [OtherAtom, APid] -> register(OtherAtom, APid);
+ _Other -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars8.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars8.erl
new file mode 100644
index 0000000000..937b83cf02
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars8.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars8).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom =:= OtherAtom of
+ true -> register(OtherAtom, Pid);
+ false -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars9.erl b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars9.erl
new file mode 100644
index 0000000000..9beb67ca38
--- /dev/null
+++ b/lib/dialyzer/test/race_tests_SUITE_data/src/whereis_vars9.erl
@@ -0,0 +1,22 @@
+%% This tests that warnings do appear when there is no specific
+%% information about the types and the variables are bound.
+
+-module(whereis_vars9).
+-export([start/2]).
+
+start(AnAtom, Fun) ->
+ case whereis(AnAtom) of
+ undefined ->
+ Pid = spawn(Fun),
+ OtherAtom = kostis,
+ case Pid =:= self() of
+ true -> ok;
+ false ->
+ case AnAtom == OtherAtom of
+ true -> register(OtherAtom, Pid);
+ false -> ok
+ end
+ end;
+ P when is_pid(P) ->
+ ok
+ end.
diff --git a/lib/dialyzer/test/remake b/lib/dialyzer/test/remake
new file mode 100755
index 0000000000..654bdd9e88
--- /dev/null
+++ b/lib/dialyzer/test/remake
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+erlc +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec dialyzer_common.erl file_utils.erl
+if [ -n "$1" ]; then
+ erl -noshell -run dialyzer_common create_suite "$1" -s erlang halt
+else
+ erl -noshell -run dialyzer_common create_all_suites -s erlang halt
+fi
+rm dialyzer_common.beam file_utils.beam \ No newline at end of file
diff --git a/lib/dialyzer/test/small_tests_SUITE.erl b/lib/dialyzer/test/small_tests_SUITE.erl
new file mode 100644
index 0000000000..21a2c76160
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE.erl
@@ -0,0 +1,483 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(small_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([small_tests_SUITE_consistency/1, app_call/1, appmon_place/1,
+ areq/1, atom_call/1, atom_guard/1, atom_widen/1,
+ bs_fail_constr/1, bs_utf8/1, cerl_hipeify/1, comm_layer/1,
+ compare1/1, confusing_warning/1, contract2/1, contract3/1,
+ contract5/1, disj_norm_form/1, eqeq/1, ets_select/1,
+ exhaust_case/1, failing_guard1/1, flatten/1, fun_app/1,
+ fun_ref_match/1, fun_ref_record/1, gencall/1, gs_make/1,
+ inf_loop2/1, letrec1/1, list_match/1, lzip/1, make_tuple/1,
+ minus_minus/1, mod_info/1, my_filter/1, my_sofs/1, no_match/1,
+ no_unused_fun/1, no_unused_fun2/1, non_existing/1,
+ not_guard_crash/1, or_bug/1, orelsebug/1, orelsebug2/1,
+ overloaded1/1, port_info_test/1, process_info_test/1, pubsub/1,
+ receive1/1, record_construct/1, record_pat/1,
+ record_send_test/1, record_test/1, recursive_types1/1,
+ recursive_types2/1, recursive_types3/1, recursive_types4/1,
+ recursive_types5/1, recursive_types6/1, recursive_types7/1,
+ refine_bug1/1, toth/1, trec/1, try1/1, tuple1/1,
+ unsafe_beamcode_bug/1, unused_cases/1, unused_clauses/1,
+ zero_tuple/1]).
+
+suite() ->
+ [{timetrap, {minutes, 1}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, []}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [small_tests_SUITE_consistency,app_call,appmon_place,areq,atom_call,
+ atom_guard,atom_widen,bs_fail_constr,bs_utf8,cerl_hipeify,comm_layer,
+ compare1,confusing_warning,contract2,contract3,contract5,disj_norm_form,
+ eqeq,ets_select,exhaust_case,failing_guard1,flatten,fun_app,fun_ref_match,
+ fun_ref_record,gencall,gs_make,inf_loop2,letrec1,list_match,lzip,
+ make_tuple,minus_minus,mod_info,my_filter,my_sofs,no_match,no_unused_fun,
+ no_unused_fun2,non_existing,not_guard_crash,or_bug,orelsebug,orelsebug2,
+ overloaded1,port_info_test,process_info_test,pubsub,receive1,
+ record_construct,record_pat,record_send_test,record_test,recursive_types1,
+ recursive_types2,recursive_types3,recursive_types4,recursive_types5,
+ recursive_types6,recursive_types7,refine_bug1,toth,trec,try1,tuple1,
+ unsafe_beamcode_bug,unused_cases,unused_clauses,zero_tuple].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+small_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+app_call(Config) ->
+ case dialyze(Config, app_call) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+appmon_place(Config) ->
+ case dialyze(Config, appmon_place) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+areq(Config) ->
+ case dialyze(Config, areq) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+atom_call(Config) ->
+ case dialyze(Config, atom_call) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+atom_guard(Config) ->
+ case dialyze(Config, atom_guard) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+atom_widen(Config) ->
+ case dialyze(Config, atom_widen) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+bs_fail_constr(Config) ->
+ case dialyze(Config, bs_fail_constr) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+bs_utf8(Config) ->
+ case dialyze(Config, bs_utf8) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+cerl_hipeify(Config) ->
+ case dialyze(Config, cerl_hipeify) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+comm_layer(Config) ->
+ case dialyze(Config, comm_layer) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+compare1(Config) ->
+ case dialyze(Config, compare1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+confusing_warning(Config) ->
+ case dialyze(Config, confusing_warning) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+contract2(Config) ->
+ case dialyze(Config, contract2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+contract3(Config) ->
+ case dialyze(Config, contract3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+contract5(Config) ->
+ case dialyze(Config, contract5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+disj_norm_form(Config) ->
+ case dialyze(Config, disj_norm_form) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+eqeq(Config) ->
+ case dialyze(Config, eqeq) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+ets_select(Config) ->
+ case dialyze(Config, ets_select) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+exhaust_case(Config) ->
+ case dialyze(Config, exhaust_case) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+failing_guard1(Config) ->
+ case dialyze(Config, failing_guard1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+flatten(Config) ->
+ case dialyze(Config, flatten) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+fun_app(Config) ->
+ case dialyze(Config, fun_app) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+fun_ref_match(Config) ->
+ case dialyze(Config, fun_ref_match) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+fun_ref_record(Config) ->
+ case dialyze(Config, fun_ref_record) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+gencall(Config) ->
+ case dialyze(Config, gencall) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+gs_make(Config) ->
+ case dialyze(Config, gs_make) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+inf_loop2(Config) ->
+ case dialyze(Config, inf_loop2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+letrec1(Config) ->
+ case dialyze(Config, letrec1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+list_match(Config) ->
+ case dialyze(Config, list_match) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+lzip(Config) ->
+ case dialyze(Config, lzip) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+make_tuple(Config) ->
+ case dialyze(Config, make_tuple) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+minus_minus(Config) ->
+ case dialyze(Config, minus_minus) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+mod_info(Config) ->
+ case dialyze(Config, mod_info) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+my_filter(Config) ->
+ case dialyze(Config, my_filter) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+my_sofs(Config) ->
+ case dialyze(Config, my_sofs) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+no_match(Config) ->
+ case dialyze(Config, no_match) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+no_unused_fun(Config) ->
+ case dialyze(Config, no_unused_fun) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+no_unused_fun2(Config) ->
+ case dialyze(Config, no_unused_fun2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+non_existing(Config) ->
+ case dialyze(Config, non_existing) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+not_guard_crash(Config) ->
+ case dialyze(Config, not_guard_crash) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+or_bug(Config) ->
+ case dialyze(Config, or_bug) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+orelsebug(Config) ->
+ case dialyze(Config, orelsebug) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+orelsebug2(Config) ->
+ case dialyze(Config, orelsebug2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+overloaded1(Config) ->
+ case dialyze(Config, overloaded1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+port_info_test(Config) ->
+ case dialyze(Config, port_info_test) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+process_info_test(Config) ->
+ case dialyze(Config, process_info_test) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+pubsub(Config) ->
+ case dialyze(Config, pubsub) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+receive1(Config) ->
+ case dialyze(Config, receive1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+record_construct(Config) ->
+ case dialyze(Config, record_construct) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+record_pat(Config) ->
+ case dialyze(Config, record_pat) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+record_send_test(Config) ->
+ case dialyze(Config, record_send_test) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+record_test(Config) ->
+ case dialyze(Config, record_test) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types1(Config) ->
+ case dialyze(Config, recursive_types1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types2(Config) ->
+ case dialyze(Config, recursive_types2) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types3(Config) ->
+ case dialyze(Config, recursive_types3) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types4(Config) ->
+ case dialyze(Config, recursive_types4) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types5(Config) ->
+ case dialyze(Config, recursive_types5) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types6(Config) ->
+ case dialyze(Config, recursive_types6) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+recursive_types7(Config) ->
+ case dialyze(Config, recursive_types7) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+refine_bug1(Config) ->
+ case dialyze(Config, refine_bug1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+toth(Config) ->
+ case dialyze(Config, toth) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+trec(Config) ->
+ case dialyze(Config, trec) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+try1(Config) ->
+ case dialyze(Config, try1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+tuple1(Config) ->
+ case dialyze(Config, tuple1) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+unsafe_beamcode_bug(Config) ->
+ case dialyze(Config, unsafe_beamcode_bug) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+unused_cases(Config) ->
+ case dialyze(Config, unused_cases) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+unused_clauses(Config) ->
+ case dialyze(Config, unused_clauses) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+zero_tuple(Config) ->
+ case dialyze(Config, zero_tuple) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/small_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..50991c9bc5
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/dialyzer_options
@@ -0,0 +1 @@
+{dialyzer_options, []}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/andalso_test b/lib/dialyzer/test/small_tests_SUITE_data/results/andalso_test
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/andalso_test
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/app_call b/lib/dialyzer/test/small_tests_SUITE_data/results/app_call
new file mode 100644
index 0000000000..cc1a63f944
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/app_call
@@ -0,0 +1,3 @@
+
+app_call.erl:6: The call M:'foo'() requires that M is of type atom() | tuple() not 42
+app_call.erl:9: The call 'mod':F() requires that F is of type atom() not {'gazonk',[]}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/appmon_place b/lib/dialyzer/test/small_tests_SUITE_data/results/appmon_place
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/appmon_place
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/areq b/lib/dialyzer/test/small_tests_SUITE_data/results/areq
new file mode 100644
index 0000000000..dd91f2d2bf
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/areq
@@ -0,0 +1,2 @@
+
+areq.erl:11: The test float() =:= 3 can never evaluate to 'true'
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/atom_call b/lib/dialyzer/test/small_tests_SUITE_data/results/atom_call
new file mode 100644
index 0000000000..851bb7ab12
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/atom_call
@@ -0,0 +1,3 @@
+
+atom_call.erl:14: Fun application will fail since F :: 'f' is not a function of arity 0
+atom_call.erl:14: Function g/0 has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/atom_widen b/lib/dialyzer/test/small_tests_SUITE_data/results/atom_widen
new file mode 100644
index 0000000000..6d0a7b2737
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/atom_widen
@@ -0,0 +1,3 @@
+
+atom_widen.erl:10: The call atom_widen:foo('z') will never return since it differs in the 1st argument from the success typing arguments: ('a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'k' | 'l' | 'm' | 'n')
+atom_widen.erl:9: Function test/0 has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/bs_fail_constr b/lib/dialyzer/test/small_tests_SUITE_data/results/bs_fail_constr
new file mode 100644
index 0000000000..dbc8241971
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/bs_fail_constr
@@ -0,0 +1,9 @@
+
+bs_fail_constr.erl:11: Function w3/1 has no local return
+bs_fail_constr.erl:12: Binary construction will fail since the size field S in segment 42:S/integer-unit:1 has type neg_integer()
+bs_fail_constr.erl:14: Function w4/1 has no local return
+bs_fail_constr.erl:15: Binary construction will fail since the value field V in segment V/utf32 has type float()
+bs_fail_constr.erl:5: Function w1/1 has no local return
+bs_fail_constr.erl:6: Binary construction will fail since the value field V in segment V:8/integer-unit:1 has type float()
+bs_fail_constr.erl:8: Function w2/1 has no local return
+bs_fail_constr.erl:9: Binary construction will fail since the value field V in segment V/binary-unit:8 has type atom()
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/bs_utf8 b/lib/dialyzer/test/small_tests_SUITE_data/results/bs_utf8
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/bs_utf8
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/cerl_hipeify b/lib/dialyzer/test/small_tests_SUITE_data/results/cerl_hipeify
new file mode 100644
index 0000000000..87bf6f309f
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/cerl_hipeify
@@ -0,0 +1,4 @@
+
+cerl_hipeify.erl:370: Function will never be called
+cerl_hipeify.erl:370: Guard test fun((none()) -> none()) =:= F::{_,_,_} | {_,_,_,_} | {_,_,_,_,_} | {_,_,_,_,_,_} | {_,_,_,_,_,_,_} can never succeed
+cerl_hipeify.erl:641: Function env__new_function_name/2 will never be called
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/comm_layer b/lib/dialyzer/test/small_tests_SUITE_data/results/comm_layer
new file mode 100644
index 0000000000..cb4bf14eb4
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/comm_layer
@@ -0,0 +1,2 @@
+
+comm_layer.erl:76: Invalid type specification for function 'comm_layer_dir.comm_layer':this/0. The success typing is () -> {_,integer(),pid()}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/compare1 b/lib/dialyzer/test/small_tests_SUITE_data/results/compare1
new file mode 100644
index 0000000000..f0d696ffcb
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/compare1
@@ -0,0 +1,4 @@
+
+compare1.erl:15: Guard test X::42 > 42 can never succeed
+compare1.erl:17: Guard test X::42 < 42 can never succeed
+compare1.erl:19: Guard test X::42 =/= 42 can never succeed
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/confusing_warning b/lib/dialyzer/test/small_tests_SUITE_data/results/confusing_warning
new file mode 100644
index 0000000000..d2d0c91fff
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/confusing_warning
@@ -0,0 +1,2 @@
+
+confusing_warning.erl:16: The pattern {'a', {_, L}} can never match the type {'b','aaa' | 'bbb'}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/contract1 b/lib/dialyzer/test/small_tests_SUITE_data/results/contract1
new file mode 100644
index 0000000000..fb8ba5f72b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/contract1
@@ -0,0 +1,3 @@
+
+contract1.erl:23: Function test/0 has no local return
+contract1.erl:24: The pattern 42 can never match the type 'a' | 'b' | 'c'
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/contract2 b/lib/dialyzer/test/small_tests_SUITE_data/results/contract2
new file mode 100644
index 0000000000..6809e528c4
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/contract2
@@ -0,0 +1,2 @@
+
+contract2.erl:13: The call contract2:test(T::any(),nonempty_maybe_improper_list()) will never return since it differs in the 2nd argument from the success typing arguments: (['true'],[])
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/contract3 b/lib/dialyzer/test/small_tests_SUITE_data/results/contract3
new file mode 100644
index 0000000000..44b49e745a
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/contract3
@@ -0,0 +1,3 @@
+
+contract3.erl:17: Overloaded contract has overlapping domains; such contracts are currently unsupported and are simply ignored
+contract3.erl:29: Overloaded contract has overlapping domains; such contracts are currently unsupported and are simply ignored
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/contract5 b/lib/dialyzer/test/small_tests_SUITE_data/results/contract5
new file mode 100644
index 0000000000..116c4f4d4d
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/contract5
@@ -0,0 +1,2 @@
+
+contract5.erl:13: Invalid type specification for function contract5:t/0. The success typing is () -> #bar{baz::'not_a_boolean'}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/eqeq b/lib/dialyzer/test/small_tests_SUITE_data/results/eqeq
new file mode 100644
index 0000000000..dabd38ebe3
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/eqeq
@@ -0,0 +1,2 @@
+
+eqeq.erl:15: The test float() =:= 'foo' can never evaluate to 'true'
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/ets_select b/lib/dialyzer/test/small_tests_SUITE_data/results/ets_select
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/ets_select
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/exhaust_case b/lib/dialyzer/test/small_tests_SUITE_data/results/exhaust_case
new file mode 100644
index 0000000000..45cdd80b64
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/exhaust_case
@@ -0,0 +1,3 @@
+
+exhaust_case.erl:17: The pattern 42 can never match the type 'bar' | 'foo'
+exhaust_case.erl:18: The variable _other can never match since previous clauses completely covered the type 'bar' | 'foo'
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/failing_guard1 b/lib/dialyzer/test/small_tests_SUITE_data/results/failing_guard1
new file mode 100644
index 0000000000..5bdd13093a
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/failing_guard1
@@ -0,0 +1,4 @@
+
+failing_guard1.erl:12: Guard test float() =:= 2 can never succeed
+failing_guard1.erl:13: Guard test integer() =:= float() can never succeed
+failing_guard1.erl:14: Guard test -2 | -1 | 0 | 1 | 2 =:= float() can never succeed
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/flatten b/lib/dialyzer/test/small_tests_SUITE_data/results/flatten
new file mode 100644
index 0000000000..4571214e49
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/flatten
@@ -0,0 +1,2 @@
+
+flatten.erl:17: The call lists:flatten(nonempty_improper_list(atom() | binary() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()])
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/fun_app b/lib/dialyzer/test/small_tests_SUITE_data/results/fun_app
new file mode 100644
index 0000000000..b28baad43b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/fun_app
@@ -0,0 +1,7 @@
+
+fun_app.erl:37: Fun application will fail since F :: fun((_,_,_) -> 'ok' | 'true') is not a function of arity 1
+fun_app.erl:37: The created fun has no local return
+fun_app.erl:38: Fun application will fail since F :: fun((_,_,_) -> 'ok' | 'true') is not a function of arity 2
+fun_app.erl:38: The created fun has no local return
+fun_app.erl:40: Fun application will fail since F :: fun((_,_,_) -> 'ok' | 'true') is not a function of arity 4
+fun_app.erl:40: The created fun has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/fun_ref_match b/lib/dialyzer/test/small_tests_SUITE_data/results/fun_ref_match
new file mode 100644
index 0000000000..60b34530b4
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/fun_ref_match
@@ -0,0 +1,2 @@
+
+fun_ref_match.erl:14: Function will never be called
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/gencall b/lib/dialyzer/test/small_tests_SUITE_data/results/gencall
new file mode 100644
index 0000000000..d0479ed738
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/gencall
@@ -0,0 +1,4 @@
+
+gencall.erl:11: Call to missing or unexported function gencall:foo/0
+gencall.erl:12: Call to missing or unexported function gen_server:handle_cast/2
+gencall.erl:9: Call to missing or unexported function ets:lookup/3
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/gs_make b/lib/dialyzer/test/small_tests_SUITE_data/results/gs_make
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/gs_make
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/inf_loop2 b/lib/dialyzer/test/small_tests_SUITE_data/results/inf_loop2
new file mode 100644
index 0000000000..7e9972ad98
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/inf_loop2
@@ -0,0 +1,4 @@
+
+inf_loop2.erl:18: Function test/0 has no local return
+inf_loop2.erl:19: The call lists:reverse('gazonk') will never return since it differs in the 1st argument from the success typing arguments: ([any()])
+inf_loop2.erl:22: Function loop/0 has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/letrec1 b/lib/dialyzer/test/small_tests_SUITE_data/results/letrec1
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/letrec1
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/list_match b/lib/dialyzer/test/small_tests_SUITE_data/results/list_match
new file mode 100644
index 0000000000..95007da604
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/list_match
@@ -0,0 +1,2 @@
+
+list_match.erl:19: The pattern [_ | T] can never match since previous clauses completely covered the type [1 | 2 | 3 | 4]
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/lzip b/lib/dialyzer/test/small_tests_SUITE_data/results/lzip
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/lzip
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/make_tuple b/lib/dialyzer/test/small_tests_SUITE_data/results/make_tuple
new file mode 100644
index 0000000000..4d51586e35
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/make_tuple
@@ -0,0 +1,3 @@
+
+make_tuple.erl:4: Function test/0 has no local return
+make_tuple.erl:5: The pattern {_, _} can never match the type {_,_,_}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/minus_minus b/lib/dialyzer/test/small_tests_SUITE_data/results/minus_minus
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/minus_minus
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/mod_info b/lib/dialyzer/test/small_tests_SUITE_data/results/mod_info
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/mod_info
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/my_filter b/lib/dialyzer/test/small_tests_SUITE_data/results/my_filter
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/my_filter
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/my_sofs b/lib/dialyzer/test/small_tests_SUITE_data/results/my_sofs
new file mode 100644
index 0000000000..bfee0bce0d
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/my_sofs
@@ -0,0 +1,3 @@
+
+my_sofs.erl:34: The pattern {'Set', _, _} can never match the type #OrdSet{}
+my_sofs.erl:54: The pattern {'Set', _, _} can never match the type #OrdSet{}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/no_match b/lib/dialyzer/test/small_tests_SUITE_data/results/no_match
new file mode 100644
index 0000000000..9760b980a2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/no_match
@@ -0,0 +1,4 @@
+
+no_match.erl:5: Function t1/1 has no clauses that will ever match
+no_match.erl:7: Function t2/1 has no clauses that will ever match
+no_match.erl:9: Function t3/1 has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun b/lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun2 b/lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun2
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/no_unused_fun2
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/non_existing b/lib/dialyzer/test/small_tests_SUITE_data/results/non_existing
new file mode 100644
index 0000000000..58da2bfc8b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/non_existing
@@ -0,0 +1,2 @@
+
+non_existing.erl:9: Call to missing or unexported function lists:non_existing_call/1
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/not_guard_crash b/lib/dialyzer/test/small_tests_SUITE_data/results/not_guard_crash
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/not_guard_crash
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/or_bug b/lib/dialyzer/test/small_tests_SUITE_data/results/or_bug
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/or_bug
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug b/lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug2 b/lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug2
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/orelsebug2
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/overloaded1 b/lib/dialyzer/test/small_tests_SUITE_data/results/overloaded1
new file mode 100644
index 0000000000..ab57ec03ff
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/overloaded1
@@ -0,0 +1,3 @@
+
+overloaded1.erl:10: The pattern {'ok', 'gazonk'} can never match the type {'error',_} | {'ok',{atom(),atom(),byte()}}
+overloaded1.erl:9: Function test1/0 has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/port_info_test b/lib/dialyzer/test/small_tests_SUITE_data/results/port_info_test
new file mode 100644
index 0000000000..9ee863f9eb
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/port_info_test
@@ -0,0 +1,6 @@
+
+port_info_test.erl:10: The pattern {'connected', 42} can never match the type 'undefined' | {'connected',pid()}
+port_info_test.erl:14: The pattern {'registered_name', "42"} can never match the type 'undefined' | {'registered_name',atom()}
+port_info_test.erl:19: The pattern {'output', 42} can never match the type 'undefined' | {'connected',pid()}
+port_info_test.erl:24: Guard test 'links' =:= Atom::'connected' can never succeed
+port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/process_info_test b/lib/dialyzer/test/small_tests_SUITE_data/results/process_info_test
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/process_info_test
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/pubsub b/lib/dialyzer/test/small_tests_SUITE_data/results/pubsub
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/pubsub
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/receive1 b/lib/dialyzer/test/small_tests_SUITE_data/results/receive1
new file mode 100644
index 0000000000..abf6eec0ca
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/receive1
@@ -0,0 +1,2 @@
+
+receive1.erl:12: Function t/1 has no local return
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/record_construct b/lib/dialyzer/test/small_tests_SUITE_data/results/record_construct
new file mode 100644
index 0000000000..c0110b144f
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/record_construct
@@ -0,0 +1,7 @@
+
+record_construct.erl:15: Function t_opa/0 has no local return
+record_construct.erl:16: Record construction #r_opa{b::gb_set(),c::42,e::'false'} violates the declared type of field c::boolean()
+record_construct.erl:20: Function t_rem/0 has no local return
+record_construct.erl:21: Record construction #r_rem{a::'gazonk'} violates the declared type of field a::string()
+record_construct.erl:6: Function t_loc/0 has no local return
+record_construct.erl:7: Record construction #r_loc{a::'gazonk',b::42} violates the declared type of field a::integer() and b::atom()
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/record_pat b/lib/dialyzer/test/small_tests_SUITE_data/results/record_pat
new file mode 100644
index 0000000000..9a3f925e42
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/record_pat
@@ -0,0 +1,2 @@
+
+record_pat.erl:14: The pattern {'foo', 'baz'} violates the declared type for #foo{}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/record_send_test b/lib/dialyzer/test/small_tests_SUITE_data/results/record_send_test
new file mode 100644
index 0000000000..6a08d44179
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/record_send_test
@@ -0,0 +1,2 @@
+
+record_send_test.erl:30: The call erlang:'!'(Rec1::#rec1{a::'a',b::'b',c::'c'},'hello_again') will never return since it differs in the 1st argument from the success typing arguments: (atom() | pid() | port() | {atom(),atom()},any())
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/record_test b/lib/dialyzer/test/small_tests_SUITE_data/results/record_test
new file mode 100644
index 0000000000..9715f0dcfb
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/record_test
@@ -0,0 +1,3 @@
+
+record_test.erl:19: The pattern {'foo', _} can never match the type 'foo'
+record_test.erl:21: The variable _ can never match since previous clauses completely covered the type 'foo'
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types1 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types1
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types1
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types2 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types2
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types2
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types3 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types3
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types3
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types4 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types4
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types4
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types5 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types5
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types5
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types6 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types6
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types6
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types7 b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types7
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/recursive_types7
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/toth b/lib/dialyzer/test/small_tests_SUITE_data/results/toth
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/toth
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/trec b/lib/dialyzer/test/small_tests_SUITE_data/results/trec
new file mode 100644
index 0000000000..01ccc63761
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/trec
@@ -0,0 +1,7 @@
+
+trec.erl:26: Function test/0 has no local return
+trec.erl:27: The call trec:mk_foo_loc(42,any()) will never return since it differs in the 1st argument from the success typing arguments: ('undefined',atom())
+trec.erl:29: Function mk_foo_loc/2 has no local return
+trec.erl:30: Record construction violates the declared type for #foo{} since variable A cannot be of type atom()
+trec.erl:36: Function mk_foo_exp/2 has no local return
+trec.erl:37: Record construction violates the declared type for #foo{} since variable A cannot be of type atom()
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/try1 b/lib/dialyzer/test/small_tests_SUITE_data/results/try1
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/try1
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/tuple1 b/lib/dialyzer/test/small_tests_SUITE_data/results/tuple1
new file mode 100644
index 0000000000..1b5ed49b56
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/tuple1
@@ -0,0 +1,5 @@
+
+tuple1.erl:13: Function t1/2 has no local return
+tuple1.erl:14: The call lists:mapfoldl(fun((_,_) -> 'a' | 'b'),X::any(),List::nonempty_maybe_improper_list()) will never return since the success typing arguments are (fun((_,_) -> {_,_}),any(),[any()])
+tuple1.erl:19: Function t3/2 has no local return
+tuple1.erl:20: The call lists:mapfoldl(fun((_) -> 1),X::any(),List::nonempty_maybe_improper_list()) will never return since it differs in the 1st argument from the success typing arguments: (fun((_,_) -> {_,_}),any(),[any()])
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/unsafe_beamcode_bug b/lib/dialyzer/test/small_tests_SUITE_data/results/unsafe_beamcode_bug
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/unsafe_beamcode_bug
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/unused_cases b/lib/dialyzer/test/small_tests_SUITE_data/results/unused_cases
new file mode 100644
index 0000000000..cafe1c042b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/unused_cases
@@ -0,0 +1,4 @@
+
+unused_cases.erl:21: The variable OTHER can never match since previous clauses completely covered the type {42,42}
+unused_cases.erl:27: The pattern 'weird' can never match the type 'false'
+unused_cases.erl:35: The variable OTHER can never match since previous clauses completely covered the type boolean()
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/unused_clauses b/lib/dialyzer/test/small_tests_SUITE_data/results/unused_clauses
new file mode 100644
index 0000000000..4603e888c1
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/unused_clauses
@@ -0,0 +1,3 @@
+
+unused_clauses.erl:16: Guard test is_integer(X::{42}) can never succeed
+unused_clauses.erl:18: The variable X can never match since previous clauses completely covered the type 'atom' | {42}
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/results/zero_tuple b/lib/dialyzer/test/small_tests_SUITE_data/results/zero_tuple
new file mode 100644
index 0000000000..bf5ec5cd6e
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/results/zero_tuple
@@ -0,0 +1,5 @@
+
+zero_tuple.erl:4: Function t1/0 has no local return
+zero_tuple.erl:5: The pattern {} can never match the type 'a'
+zero_tuple.erl:8: Function t2/0 has no local return
+zero_tuple.erl:9: The pattern 'b' can never match the type 'a'
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/app_call.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/app_call.erl
new file mode 100644
index 0000000000..54d178d29a
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/app_call.erl
@@ -0,0 +1,17 @@
+-module(app_call).
+-export([test/1]).
+
+test(m) ->
+ M = get_mod(),
+ M:foo();
+test(f) ->
+ F = get_fun(),
+ mod:F();
+test(_) ->
+ ok.
+
+get_mod() ->
+ 42.
+
+get_fun() ->
+ {gazonk, []}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/appmon_place.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/appmon_place.erl
new file mode 100644
index 0000000000..8371cab233
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/appmon_place.erl
@@ -0,0 +1,71 @@
+%%---------------------------------------------------------------------
+%% This is added as a test because it was giving a false positive
+%% (function move/4 will nevr be called) due to the strange use of
+%% self-recursive fun construction in placex/3.
+%%
+%% The analysis was getting confused that the foldl call will never
+%% terminate (due to a wrong hard-coded type for foldl) and inferred
+%% that the remaining calls in the body of placex/3 will not be
+%% reached. Fixed 11 March 2005.
+%%---------------------------------------------------------------------
+
+-module(appmon_place).
+-export([place/2]).
+
+place(DG, Root) ->
+ case appmon_dg:get(data, DG, Root) of
+ false -> [0];
+ _Other ->
+ placey(DG, Root, 1),
+ placex(DG, Root, [])
+ end.
+
+placey(DG, V, Y) ->
+ appmon_dg:set(y, DG, V, Y),
+ Y1 = Y+1,
+ lists:foreach(fun(C) -> placey(DG, C, Y1) end, appmon_dg:get(out, DG, V)).
+
+placex(DG, V, LastX) ->
+ Ch = appmon_dg:get(out, DG, V),
+ ChLX = lists:foldl(fun(C, Accu) -> placex(DG, C, Accu) end,
+ tll(LastX),
+ Ch),
+ Width = appmon_dg:get(w, DG, V),
+ MyX = calc_mid(DG, Width, Ch),
+ DeltaX = calc_delta(MyX, hdd(LastX)+20),
+ appmon_dg:set(x, DG, V, MyX),
+ move(DG, V, [MyX+Width | ChLX], DeltaX).
+
+move(_DG, _L, LastX, 0) -> LastX;
+move(DG, V, LastX, DeltaX) -> move2(DG, V, LastX, DeltaX).
+
+move2(DG, V, LastX, DeltaX) ->
+ NewX = appmon_dg:get(x, DG, V)+DeltaX,
+ appmon_dg:set(x, DG, V, NewX),
+ ChLX = lists:foldl(fun(C, LX) -> move2(DG, C, LX, DeltaX) end,
+ tll(LastX),
+ appmon_dg:get(out, DG, V)),
+ [max(NewX+appmon_dg:get(w, DG, V), hdd(LastX)) | ChLX].
+
+max(A, B) when A>B -> A;
+max(_, B) -> B.
+
+calc_mid(_DG, _Width, []) -> 0;
+calc_mid(DG, Width, ChList) ->
+ LeftMostX = appmon_dg:get(x, DG, hd(ChList)),
+ Z2 = lists:last(ChList),
+ RightMostX = appmon_dg:get(x, DG, Z2)+appmon_dg:get(w, DG, Z2),
+ trunc((LeftMostX+RightMostX)/2)-trunc(Width/2).
+
+calc_delta(Mid, Right) ->
+ if Right>Mid -> Right-Mid;
+ true -> 0
+ end.
+
+%% Special head and tail
+%% Handles empty list in a non-standard way
+tll([]) -> [];
+tll([_|T]) -> T.
+hdd([]) -> 0;
+hdd([H|_]) -> H.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/areq.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/areq.erl
new file mode 100644
index 0000000000..1b4eea8511
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/areq.erl
@@ -0,0 +1,12 @@
+-module(areq).
+
+-export([t/0]).
+
+t() ->
+ ar_comp(3.0, 3),
+ ex_comp(3.0, 3).
+
+ar_comp(X, Y) -> X == Y.
+
+ex_comp(X, Y) -> X =:= Y.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/atom_call.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/atom_call.erl
new file mode 100644
index 0000000000..bf0646eadc
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/atom_call.erl
@@ -0,0 +1,14 @@
+%%%-------------------------------------------------------------------
+%%% File : atom_call.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 10 Dec 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(atom_call).
+
+-export([f/0,g/0]).
+
+f() -> ok.
+
+g() -> F = f, F().
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/atom_guard.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/atom_guard.erl
new file mode 100644
index 0000000000..67d97f8e29
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/atom_guard.erl
@@ -0,0 +1,9 @@
+-module(atom_guard).
+-export([test/0]).
+
+test() ->
+ foo(42).
+
+foo(X) when is_atom(x) ->
+ X.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/atom_widen.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/atom_widen.erl
new file mode 100644
index 0000000000..81bfac9d56
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/atom_widen.erl
@@ -0,0 +1,24 @@
+%%---------------------------------------------------------------------
+%% Tests that the set widening limit is at least as big as 13,
+%% which allows for the following discrepancy to be detected.
+%%---------------------------------------------------------------------
+
+-module(atom_widen).
+-export([test/0, foo/1]).
+
+test() ->
+ foo(z).
+
+foo(a) -> 1;
+foo(b) -> 2;
+foo(c) -> 3;
+foo(d) -> 4;
+foo(e) -> 5;
+foo(f) -> 6;
+foo(g) -> 7;
+foo(h) -> 8;
+foo(i) -> 9;
+foo(k) -> 10;
+foo(l) -> 11;
+foo(m) -> 12;
+foo(n) -> 13.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/bs_fail_constr.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/bs_fail_constr.erl
new file mode 100644
index 0000000000..20fd1cbf64
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/bs_fail_constr.erl
@@ -0,0 +1,16 @@
+-module(bs_fail_constr).
+
+-export([w1/1, w2/1, w3/1, w4/1]).
+
+w1(V) when is_float(V) ->
+ <<V/integer>>.
+
+w2(V) when is_atom(V) ->
+ <<V/binary>>.
+
+w3(S) when is_integer(S), S < 0 ->
+ <<42:S/integer>>.
+
+w4(V) when is_float(V) ->
+ <<V/utf32>>.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/bs_utf8.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/bs_utf8.erl
new file mode 100644
index 0000000000..5fe28f1da1
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/bs_utf8.erl
@@ -0,0 +1,27 @@
+%%--------------------------------------------------------------------
+%% Test case that exposed a bug (bogus warning) in dialyzer_dataflow
+%% when refining binaries containing UTF-based segments. Reported by
+%% Patrik Nyblom on 4/3/2009 and fixed by Kostis Sagonas on 31/3/2009.
+%%--------------------------------------------------------------------
+
+-module(bs_utf8).
+
+-export([doit/2]).
+
+doit(N, Bin) when is_integer(N), N > 0 ->
+ count_and_find(Bin, N).
+
+count_and_find(Bin, N) when is_binary(Bin) ->
+ cafu(Bin, N, 0, 0, no_pos).
+
+cafu(<<>>, _N, Count, _ByteCount, SavePos) ->
+ {Count, SavePos};
+cafu(<<_/utf8, Rest/binary>>, 0, Count, ByteCount, _SavePos) ->
+ cafu(Rest, -1, Count+1, 0, ByteCount);
+cafu(<<_/utf8, Rest/binary>>, N, Count, _ByteCount, SavePos) when N < 0 ->
+ cafu(Rest, -1, Count+1, 0, SavePos);
+cafu(<<_/utf8, Rest/binary>> = Whole, N, Count, ByteCount, SavePos) ->
+ Delta = byte_size(Whole) - byte_size(Rest),
+ cafu(Rest, N-1, Count+1, ByteCount+Delta, SavePos);
+cafu(_Other, _N, Count, ByteCount, _SavePos) -> % Non Unicode character at end
+ {Count, ByteCount}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/cerl_hipeify.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/cerl_hipeify.erl
new file mode 100644
index 0000000000..3ccadec4d0
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/cerl_hipeify.erl
@@ -0,0 +1,684 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id: cerl_hipeify.erl,v 1.1 2008/12/17 09:53:49 mikpe Exp $
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @copyright 2000-2004 Richard Carlsson
+%% @doc HiPE-ification of Core Erlang code. Prepares Core Erlang code
+%% for translation to ICode.
+%% @see cerl_to_icode
+
+-module(cerl_hipeify).
+
+-export([transform/2]).
+
+-define(PRIMOP_IDENTITY, identity). % arity 1
+-define(PRIMOP_NOT, 'not'). % arity 1
+-define(PRIMOP_AND, 'and'). % arity 2
+-define(PRIMOP_OR, 'or'). % arity 2
+-define(PRIMOP_XOR, 'xor'). % arity 2
+-define(PRIMOP_ADD, '+'). % arity 2
+-define(PRIMOP_SUB, '-'). % arity 2
+-define(PRIMOP_NEG, neg). % arity 1
+-define(PRIMOP_MUL, '*'). % arity 2
+-define(PRIMOP_DIV, '/'). % arity 2
+-define(PRIMOP_INTDIV, 'div'). % arity 2
+-define(PRIMOP_REM, 'rem'). % arity 2
+-define(PRIMOP_BAND, 'band'). % arity 2
+-define(PRIMOP_BOR, 'bor'). % arity 2
+-define(PRIMOP_BXOR, 'bxor'). % arity 2
+-define(PRIMOP_BNOT, 'bnot'). % arity 1
+-define(PRIMOP_BSL, 'bsl'). % arity 2
+-define(PRIMOP_BSR, 'bsr'). % arity 2
+-define(PRIMOP_EQ, '=='). % arity 2
+-define(PRIMOP_NE, '/='). % arity 2
+-define(PRIMOP_EXACT_EQ, '=:='). % arity 2
+-define(PRIMOP_EXACT_NE, '=/='). % arity 2
+-define(PRIMOP_LT, '<'). % arity 2
+-define(PRIMOP_GT, '>'). % arity 2
+-define(PRIMOP_LE, '=<'). % arity 2
+-define(PRIMOP_GE, '>='). % arity 2
+-define(PRIMOP_IS_ATOM, 'is_atom'). % arity 1
+-define(PRIMOP_IS_BIGNUM, 'is_bignum'). % arity 1
+-define(PRIMOP_IS_BINARY, 'is_binary'). % arity 1
+-define(PRIMOP_IS_CONSTANT, 'is_constant'). % arity 1
+-define(PRIMOP_IS_FIXNUM, 'is_fixnum'). % arity 1
+-define(PRIMOP_IS_FLOAT, 'is_float'). % arity 1
+-define(PRIMOP_IS_FUNCTION, 'is_function'). % arity 1
+-define(PRIMOP_IS_INTEGER, 'is_integer'). % arity 1
+-define(PRIMOP_IS_LIST, 'is_list'). % arity 1
+-define(PRIMOP_IS_NUMBER, 'is_number'). % arity 1
+-define(PRIMOP_IS_PID, 'is_pid'). % arity 1
+-define(PRIMOP_IS_PORT, 'is_port'). % arity 1
+-define(PRIMOP_IS_REFERENCE, 'is_reference'). % arity 1
+-define(PRIMOP_IS_TUPLE, 'is_tuple'). % arity 1
+-define(PRIMOP_IS_RECORD, 'is_record'). % arity 3
+-define(PRIMOP_EXIT, exit). % arity 1
+-define(PRIMOP_THROW, throw). % arity 1
+-define(PRIMOP_ERROR, error). % arity 1,2
+-define(PRIMOP_RETHROW, raise). % arity 2
+-define(PRIMOP_RECEIVE_SELECT, receive_select). % arity 0
+-define(PRIMOP_RECEIVE_NEXT, receive_next). % arity 0
+-define(PRIMOP_ELEMENT, element). % arity 2
+-define(PRIMOP_DSETELEMENT, dsetelement). % arity 3
+-define(PRIMOP_MAKE_FUN, make_fun). % arity 6
+-define(PRIMOP_APPLY_FUN, apply_fun). % arity 2
+-define(PRIMOP_FUN_ELEMENT, closure_element). % arity 2
+-define(PRIMOP_SET_LABEL, set_label). % arity 1
+-define(PRIMOP_GOTO_LABEL, goto_label). % arity 1
+-define(PRIMOP_REDUCTION_TEST, reduction_test). % arity 0
+
+-record(ctxt, {class = expr}).
+
+
+%% @spec transform(Module::cerl(), Options::[term()]) -> cerl()
+%%
+%% cerl() = cerl:cerl()
+%%
+%% @doc Rewrites a Core Erlang module to a form suitable for further
+%% translation to HiPE Icode. See module <code>cerl_to_icode</code> for
+%% details.
+%%
+%% @see cerl_to_icode
+%% @see cerl_cconv
+
+transform(E, Opts) ->
+ %% Start by closure converting the code
+ module(cerl_cconv:transform(E, Opts), Opts).
+
+module(E, Opts) ->
+ {Ds, Env, Ren} = add_defs(cerl:module_defs(E), env__new(),
+ ren__new()),
+ M = cerl:module_name(E),
+ S0 = s__new(cerl:atom_val(M)),
+ S = s__set_pmatch(proplists:get_value(pmatch, Opts), S0),
+ {Ds1, _} = defs(Ds, true, Env, Ren, S),
+ cerl:update_c_module(E, M, cerl:module_exports(E),
+ cerl:module_attrs(E), Ds1).
+
+%% Note that the environment is defined on the renamed variables.
+
+expr(E0, Env, Ren, Ctxt, S0) ->
+ %% Do peephole optimizations as we traverse the code.
+ E = cerl_lib:reduce_expr(E0),
+ case cerl:type(E) of
+ literal ->
+ {E, S0};
+ var ->
+ variable(E, Env, Ren, Ctxt, S0);
+ values ->
+ {Es, S1} = expr_list(cerl:values_es(E), Env, Ren, Ctxt, S0),
+ {cerl:update_c_values(E, Es), S1};
+ cons ->
+ {E1, S1} = expr(cerl:cons_hd(E), Env, Ren, Ctxt, S0),
+ {E2, S2} = expr(cerl:cons_tl(E), Env, Ren, Ctxt, S1),
+ {cerl:update_c_cons(E, E1, E2), S2};
+ tuple ->
+ {Es, S1} = expr_list(cerl:tuple_es(E), Env, Ren, Ctxt, S0),
+ {cerl:update_c_tuple(E, Es), S1};
+ 'let' ->
+ let_expr(E, Env, Ren, Ctxt, S0);
+ seq ->
+ {A, S1} = expr(cerl:seq_arg(E), Env, Ren, Ctxt, S0),
+ {B, S2} = expr(cerl:seq_body(E), Env, Ren, Ctxt, S1),
+ {cerl:update_c_seq(E, A, B), S2};
+ apply ->
+ {Op, S1} = expr(cerl:apply_op(E), Env, Ren, Ctxt, S0),
+ {As, S2} = expr_list(cerl:apply_args(E), Env, Ren, Ctxt, S1),
+ {cerl:update_c_apply(E, Op, As), S2};
+ call ->
+ {M, S1} = expr(cerl:call_module(E), Env, Ren, Ctxt, S0),
+ {N, S2} = expr(cerl:call_name(E), Env, Ren, Ctxt, S1),
+ {As, S3} = expr_list(cerl:call_args(E), Env, Ren, Ctxt, S2),
+ {rewrite_call(E, M, N, As, S3), S3};
+ primop ->
+ {As, S1} = expr_list(cerl:primop_args(E), Env, Ren, Ctxt, S0),
+ N = cerl:primop_name(E),
+ {rewrite_primop(E, N, As, S1), S1};
+ 'case' ->
+ {A, S1} = expr(cerl:case_arg(E), Env, Ren, Ctxt, S0),
+ {E1, Vs, S2} = clauses(cerl:case_clauses(E), Env, Ren, Ctxt, S1),
+ {cerl:c_let(Vs, A, E1), S2};
+ 'fun' ->
+ Vs = cerl:fun_vars(E),
+ {Vs1, Env1, Ren1} = add_vars(Vs, Env, Ren),
+ {B, S1} = expr(cerl:fun_body(E), Env1, Ren1, Ctxt, S0),
+ {cerl:update_c_fun(E, Vs1, B), S1};
+ 'receive' ->
+ receive_expr(E, Env, Ren, Ctxt, S0);
+ 'try' ->
+ {A, S1} = expr(cerl:try_arg(E), Env, Ren, Ctxt, S0),
+ Vs = cerl:try_vars(E),
+ {Vs1, Env1, Ren1} = add_vars(Vs, Env, Ren),
+ {B, S2} = expr(cerl:try_body(E), Env1, Ren1, Ctxt, S1),
+ Evs = cerl:try_evars(E),
+ {Evs1, Env2, Ren2} = add_vars(Evs, Env, Ren),
+ {H, S3} = expr(cerl:try_handler(E), Env2, Ren2, Ctxt, S2),
+ {cerl:update_c_try(E, A, Vs1, B, Evs1, H), S3};
+ 'catch' ->
+ catch_expr(E, Env, Ren, Ctxt, S0);
+ letrec ->
+ {Ds, Env1, Ren1} = add_defs(cerl:letrec_defs(E), Env, Ren),
+ {Ds1, S1} = defs(Ds, false, Env1, Ren1, S0),
+ {B, S2} = expr(cerl:letrec_body(E), Env1, Ren1, Ctxt, S1),
+ {cerl:update_c_letrec(E, Ds1, B), S2};
+ binary ->
+ {Segs, S1}=expr_list(cerl:binary_segments(E), Env, Ren,
+ Ctxt, S0),
+ {cerl:update_c_binary(E, Segs), S1};
+ bitstr ->
+ {E1,S1} = expr(cerl:bitstr_val(E), Env, Ren, Ctxt, S0),
+ {E2,S2} = expr(cerl:bitstr_size(E), Env, Ren, Ctxt, S1),
+ E3 = cerl:bitstr_unit(E),
+ E4 = cerl:bitstr_type(E),
+ E5 = cerl:bitstr_flags(E),
+ {cerl:update_c_bitstr(E, E1, E2, E3, E4, E5), S2}
+ end.
+
+guard_expr(E, Env, Ren, Ctxt, S) ->
+ expr(E, Env, Ren, Ctxt#ctxt{class = guard}, S).
+
+expr_list(Es, Env, Ren, Ctxt, S0) ->
+ list(Es, Env, Ren, Ctxt, S0, fun expr/5).
+
+list([E | Es], Env, Ren, Ctxt, S0, F) ->
+ {E1, S1} = F(E, Env, Ren, Ctxt, S0),
+ {Es1, S2} = list(Es, Env, Ren, Ctxt, S1, F),
+ {[E1 | Es1], S2};
+list([], _, _, _, S, _) ->
+ {[], S}.
+
+pattern(E, Env, Ren) ->
+ case cerl:type(E) of
+ literal ->
+ E;
+ var ->
+ cerl:update_c_var(E, ren__map(cerl:var_name(E), Ren));
+ values ->
+ Es = pattern_list(cerl:values_es(E), Env, Ren),
+ cerl:update_c_values(E, Es);
+ cons ->
+ E1 = pattern(cerl:cons_hd(E), Env, Ren),
+ E2 = pattern(cerl:cons_tl(E), Env, Ren),
+ cerl:update_c_cons(E, E1, E2);
+ tuple ->
+ Es = pattern_list(cerl:tuple_es(E), Env, Ren),
+ cerl:update_c_tuple(E, Es);
+ alias ->
+ V = pattern(cerl:alias_var(E), Env, Ren),
+ P = pattern(cerl:alias_pat(E), Env, Ren),
+ cerl:update_c_alias(E, V, P);
+ binary ->
+ Segs=pattern_list(cerl:binary_segments(E), Env, Ren),
+ cerl:update_c_binary(E, Segs);
+ bitstr ->
+ E1 = pattern(cerl:bitstr_val(E), Env, Ren),
+ E2 = pattern(cerl:bitstr_size(E), Env, Ren),
+ E3 = cerl:bitstr_unit(E),
+ E4 = cerl:bitstr_type(E),
+ E5 = cerl:bitstr_flags(E),
+ cerl:update_c_bitstr(E, E1, E2, E3, E4, E5)
+ end.
+
+
+
+pattern_list([E | Es], Env, Ren) ->
+ [pattern(E, Env, Ren) | pattern_list(Es, Env, Ren)];
+pattern_list([], _, _) ->
+ [].
+
+%% Visit the function body of each definition. We insert an explicit
+%% reduction test at the start of each function.
+
+defs(Ds, Top, Env, Ren, S) ->
+ defs(Ds, [], Top, Env, Ren, S).
+
+defs([{V, F} | Ds], Ds1, Top, Env, Ren, S0) ->
+ S1 = case Top of
+ true -> s__enter_function(cerl:var_name(V), S0);
+ false -> S0
+ end,
+ {B, S2} = expr(cerl:fun_body(F), Env, Ren, #ctxt{}, S1),
+ B1 = cerl:c_seq(cerl:c_primop(cerl:c_atom(?PRIMOP_REDUCTION_TEST),
+ []),
+ B),
+ F1 = cerl:update_c_fun(F, cerl:fun_vars(F), B1),
+ defs(Ds, [{V, F1} | Ds1], Top, Env, Ren, S2);
+defs([], Ds, _Top, _Env, _Ren, S) ->
+ {lists:reverse(Ds), S}.
+
+clauses([C|_]=Cs, Env, Ren, Ctxt, S) ->
+ {Cs1, S1} = clause_list(Cs, Env, Ren, Ctxt, S),
+ %% Perform pattern matching compilation on the clauses.
+ {E, Vs} = case s__get_pmatch(S) of
+ true ->
+ cerl_pmatch:clauses(Cs1, Env);
+ no_duplicates ->
+ put('cerl_pmatch_duplicate_code', never),
+ cerl_pmatch:clauses(Cs1, Env);
+ duplicate_all ->
+ put('cerl_pmatch_duplicate_code', always),
+ cerl_pmatch:clauses(Cs1, Env);
+ Other when Other == false; Other == undefined ->
+ Vs0 = new_vars(cerl:clause_arity(C), Env),
+ {cerl:c_case(cerl:c_values(Vs0), Cs1), Vs0}
+ end,
+ %% We must make sure that we also visit any clause guards generated
+ %% by the pattern matching compilation. We pass an empty renaming,
+ %% so we do not rename any variables twice.
+ {E1, S2} = revisit_expr(E, Env, ren__new(), Ctxt, S1),
+ {E1, Vs, S2}.
+
+clause_list(Cs, Env, Ren, Ctxt, S) ->
+ list(Cs, Env, Ren, Ctxt, S, fun clause/5).
+
+clause(E, Env, Ren, Ctxt, S0) ->
+ Vs = cerl:clause_vars(E),
+ {_, Env1, Ren1} = add_vars(Vs, Env, Ren),
+ %% Visit patterns to rename variables.
+ Ps = pattern_list(cerl:clause_pats(E), Env1, Ren1),
+ {G, S1} = guard_expr(cerl:clause_guard(E), Env1, Ren1, Ctxt, S0),
+ {B, S2} = expr(cerl:clause_body(E), Env1, Ren1, Ctxt, S1),
+ {cerl:update_c_clause(E, Ps, G, B), S2}.
+
+%% This does what 'expr' does, but only recurses into clause guard
+%% expressions, 'case'-expressions, and the bodies of lets and letrecs.
+%% Note that revisiting should not add further renamings, and we simply
+%% ignore making any bindings at all at this level.
+
+revisit_expr(E, Env, Ren, Ctxt, S0) ->
+ %% Also enable peephole optimizations here.
+ revisit_expr_1(cerl_lib:reduce_expr(E), Env, Ren, Ctxt, S0).
+
+revisit_expr_1(E, Env, Ren, Ctxt, S0) ->
+ case cerl:type(E) of
+ 'case' ->
+ {Cs, S1} = revisit_clause_list(cerl:case_clauses(E), Env,
+ Ren, Ctxt, S0),
+ {cerl:update_c_case(E, cerl:case_arg(E), Cs), S1};
+ 'let' ->
+ {B, S1} = revisit_expr(cerl:let_body(E), Env, Ren, Ctxt, S0),
+ {cerl:update_c_let(E, cerl:let_vars(E), cerl:let_arg(E), B),
+ S1};
+ 'letrec' ->
+ {B, S1} = revisit_expr(cerl:letrec_body(E), Env, Ren, Ctxt, S0),
+ {cerl:update_c_letrec(E, cerl:letrec_defs(E), B), S1};
+ _ ->
+ {E, S0}
+ end.
+
+revisit_clause_list(Cs, Env, Ren, Ctxt, S) ->
+ list(Cs, Env, Ren, Ctxt, S, fun revisit_clause/5).
+
+revisit_clause(E, Env, Ren, Ctxt, S0) ->
+ %% Ignore the bindings.
+ {G, S1} = guard_expr(cerl:clause_guard(E), Env, Ren, Ctxt, S0),
+ {B, S2} = revisit_expr(cerl:clause_body(E), Env, Ren, Ctxt, S1),
+ {cerl:update_c_clause(E, cerl:clause_pats(E), G, B), S2}.
+
+%% We use the no-shadowing strategy, renaming variables on the fly and
+%% only when necessary to uphold the invariant.
+
+add_vars(Vs, Env, Ren) ->
+ add_vars(Vs, [], Env, Ren).
+
+add_vars([V | Vs], Vs1, Env, Ren) ->
+ Name = cerl:var_name(V),
+ {Name1, Ren1} = rename(Name, Env, Ren),
+ add_vars(Vs, [cerl:update_c_var(V, Name1) | Vs1],
+ env__bind(Name1, variable, Env), Ren1);
+add_vars([], Vs, Env, Ren) ->
+ {lists:reverse(Vs), Env, Ren}.
+
+rename(Name, Env, Ren) ->
+ case env__is_defined(Name, Env) of
+ false ->
+ {Name, Ren};
+ true ->
+ New = env__new_name(Env),
+ {New, ren__add(Name, New, Ren)}
+ end.
+
+%% Setting up the environment for a list of letrec-bound definitions.
+
+add_defs(Ds, Env, Ren) ->
+ add_defs(Ds, [], Env, Ren).
+
+add_defs([{V, F} | Ds], Ds1, Env, Ren) ->
+ Name = cerl:var_name(V),
+ {Name1, Ren1} =
+ case env__is_defined(Name, Env) of
+ false ->
+ {Name, Ren};
+ true ->
+ {N, A} = Name,
+ S = atom_to_list(N) ++ "_",
+ F = fun (Num) -> %% XXX: BUG: This should be F1
+ {list_to_atom(S ++ integer_to_list(Num)), A}
+ end,
+ New = env__new_function_name(F, Env),
+ {New, ren__add(Name, New, Ren)}
+ end,
+ add_defs(Ds, [{cerl:update_c_var(V, Name1), F} | Ds1],
+ env__bind(Name1, function, Env), Ren1);
+add_defs([], Ds, Env, Ren) ->
+ {lists:reverse(Ds), Env, Ren}.
+
+%% We change remote calls to important built-in functions into primop
+%% calls. In some cases (e.g., for the boolean operators), this is
+%% mainly to allow the cerl_to_icode module to handle them more
+%% straightforwardly. In most cases however, it is simply because they
+%% are supposed to be represented as primop calls on the Icode level.
+
+rewrite_call(E, M, F, As, S) ->
+ case cerl:is_c_atom(M) and cerl:is_c_atom(F) of
+ true ->
+ case call_to_primop(cerl:atom_val(M),
+ cerl:atom_val(F),
+ length(As))
+ of
+ {yes, N} ->
+ %% The primop might need further handling
+ N1 = cerl:c_atom(N),
+ E1 = cerl:update_c_primop(E, N1, As),
+ rewrite_primop(E1, N1, As, S);
+ no ->
+ cerl:update_c_call(E, M, F, As)
+ end;
+ false ->
+ cerl:update_c_call(E, M, F, As)
+ end.
+
+call_to_primop(erlang, 'not', 1) -> {yes, ?PRIMOP_NOT};
+call_to_primop(erlang, 'and', 2) -> {yes, ?PRIMOP_AND};
+call_to_primop(erlang, 'or', 2) -> {yes, ?PRIMOP_OR};
+call_to_primop(erlang, 'xor', 2) -> {yes, ?PRIMOP_XOR};
+call_to_primop(erlang, '+', 2) -> {yes, ?PRIMOP_ADD};
+call_to_primop(erlang, '+', 1) -> {yes, ?PRIMOP_IDENTITY};
+call_to_primop(erlang, '-', 2) -> {yes, ?PRIMOP_SUB};
+call_to_primop(erlang, '-', 1) -> {yes, ?PRIMOP_NEG};
+call_to_primop(erlang, '*', 2) -> {yes, ?PRIMOP_MUL};
+call_to_primop(erlang, '/', 2) -> {yes, ?PRIMOP_DIV};
+call_to_primop(erlang, 'div', 2) -> {yes, ?PRIMOP_INTDIV};
+call_to_primop(erlang, 'rem', 2) -> {yes, ?PRIMOP_REM};
+call_to_primop(erlang, 'band', 2) -> {yes, ?PRIMOP_BAND};
+call_to_primop(erlang, 'bor', 2) -> {yes, ?PRIMOP_BOR};
+call_to_primop(erlang, 'bxor', 2) -> {yes, ?PRIMOP_BXOR};
+call_to_primop(erlang, 'bnot', 1) -> {yes, ?PRIMOP_BNOT};
+call_to_primop(erlang, 'bsl', 2) -> {yes, ?PRIMOP_BSL};
+call_to_primop(erlang, 'bsr', 2) -> {yes, ?PRIMOP_BSR};
+call_to_primop(erlang, '==', 2) -> {yes, ?PRIMOP_EQ};
+call_to_primop(erlang, '/=', 2) -> {yes, ?PRIMOP_NE};
+call_to_primop(erlang, '=:=', 2) -> {yes, ?PRIMOP_EXACT_EQ};
+call_to_primop(erlang, '=/=', 2) -> {yes, ?PRIMOP_EXACT_NE};
+call_to_primop(erlang, '<', 2) -> {yes, ?PRIMOP_LT};
+call_to_primop(erlang, '>', 2) -> {yes, ?PRIMOP_GT};
+call_to_primop(erlang, '=<', 2) -> {yes, ?PRIMOP_LE};
+call_to_primop(erlang, '>=', 2) -> {yes, ?PRIMOP_GE};
+call_to_primop(erlang, is_atom, 1) -> {yes, ?PRIMOP_IS_ATOM};
+call_to_primop(erlang, is_binary, 1) -> {yes, ?PRIMOP_IS_BINARY};
+call_to_primop(erlang, is_constant, 1) -> {yes, ?PRIMOP_IS_CONSTANT};
+call_to_primop(erlang, is_float, 1) -> {yes, ?PRIMOP_IS_FLOAT};
+call_to_primop(erlang, is_function, 1) -> {yes, ?PRIMOP_IS_FUNCTION};
+call_to_primop(erlang, is_integer, 1) -> {yes, ?PRIMOP_IS_INTEGER};
+call_to_primop(erlang, is_list, 1) -> {yes, ?PRIMOP_IS_LIST};
+call_to_primop(erlang, is_number, 1) -> {yes, ?PRIMOP_IS_NUMBER};
+call_to_primop(erlang, is_pid, 1) -> {yes, ?PRIMOP_IS_PID};
+call_to_primop(erlang, is_port, 1) -> {yes, ?PRIMOP_IS_PORT};
+call_to_primop(erlang, is_reference, 1) -> {yes, ?PRIMOP_IS_REFERENCE};
+call_to_primop(erlang, is_tuple, 1) -> {yes, ?PRIMOP_IS_TUPLE};
+call_to_primop(erlang, internal_is_record, 3) -> {yes, ?PRIMOP_IS_RECORD};
+call_to_primop(erlang, element, 2) -> {yes, ?PRIMOP_ELEMENT};
+call_to_primop(erlang, exit, 1) -> {yes, ?PRIMOP_EXIT};
+call_to_primop(erlang, throw, 1) -> {yes, ?PRIMOP_THROW};
+call_to_primop(erlang, error, 1) -> {yes, ?PRIMOP_ERROR};
+call_to_primop(erlang, error, 2) -> {yes, ?PRIMOP_ERROR};
+call_to_primop(erlang, fault, 1) -> {yes, ?PRIMOP_ERROR};
+call_to_primop(erlang, fault, 2) -> {yes, ?PRIMOP_ERROR};
+call_to_primop(_, _, _) -> no.
+
+%% Also, some primops (introduced by Erlang to Core Erlang translation
+%% and possibly other stages) must be recognized and rewritten.
+
+rewrite_primop(E, N, As, S) ->
+ case {cerl:atom_val(N), As} of
+ {match_fail, [R]} ->
+ M = s__get_module_name(S),
+ {F, A} = s__get_function_name(S),
+ Stack = cerl:abstract([{M, F, A}]),
+ case cerl:type(R) of
+ tuple ->
+ %% Function clause failures have a special encoding
+ %% as '{function_clause, Arg1, ..., ArgN}'.
+ case cerl:tuple_es(R) of
+ [X | Xs] ->
+ case cerl:is_c_atom(X) of
+ true ->
+ case cerl:atom_val(X) of
+ function_clause ->
+ FStack = cerl:make_list(
+ [cerl:c_tuple(
+ [cerl:c_atom(M),
+ cerl:c_atom(F),
+ cerl:make_list(Xs)])]),
+ match_fail(E, X, FStack);
+ _ ->
+ match_fail(E, R, Stack)
+ end;
+ false ->
+ match_fail(E, R, Stack)
+ end;
+ _ ->
+ match_fail(E, R, Stack)
+ end;
+ _ ->
+ match_fail(E, R, Stack)
+ end;
+ _ ->
+ cerl:update_c_primop(E, N, As)
+ end.
+
+match_fail(E, R, Stack) ->
+ cerl:update_c_primop(E, cerl:c_atom(?PRIMOP_ERROR), [R, Stack]).
+
+%% Simple let-definitions (of degree 1) in guard context are always
+%% inline expanded. This is allowable, since they cannot have side
+%% effects, and it makes it easy to generate good code for boolean
+%% expressions. It could cause repeated evaluations, but typically,
+%% local definitions within guards are used exactly once.
+
+let_expr(E, Env, Ren, Ctxt, S) ->
+ if Ctxt#ctxt.class == guard ->
+ case cerl:let_vars(E) of
+ [V] ->
+ {Name, Ren1} = rename(cerl:var_name(V), Env, Ren),
+ Env1 = env__bind(Name, {expr, cerl:let_arg(E)}, Env),
+ expr(cerl:let_body(E), Env1, Ren1, Ctxt, S);
+ _ ->
+ let_expr_1(E, Env, Ren, Ctxt, S)
+ end;
+ true ->
+ let_expr_1(E, Env, Ren, Ctxt, S)
+ end.
+
+let_expr_1(E, Env, Ren, Ctxt, S0) ->
+ {A, S1} = expr(cerl:let_arg(E), Env, Ren, Ctxt, S0),
+ Vs = cerl:let_vars(E),
+ {Vs1, Env1, Ren1} = add_vars(Vs, Env, Ren),
+ {B, S2} = expr(cerl:let_body(E), Env1, Ren1, Ctxt, S1),
+ {cerl:update_c_let(E, Vs1, A, B), S2}.
+
+variable(E, Env, Ren, Ctxt, S) ->
+ V = ren__map(cerl:var_name(E), Ren),
+ if Ctxt#ctxt.class == guard ->
+ case env__lookup(V, Env) of
+ {ok, {expr, E1}} ->
+ expr(E1, Env, Ren, Ctxt, S); % inline
+ _ ->
+ %% Since we don't track all bindings when we revisit
+ %% guards, some names will not be in the environment.
+ variable_1(E, V, S)
+ end;
+ true ->
+ variable_1(E, V, S)
+ end.
+
+variable_1(E, V, S) ->
+ {cerl:update_c_var(E, V), S}.
+
+%% A catch-expression 'catch Expr' is rewritten as:
+%%
+%% try Expr
+%% of (V) -> V
+%% catch (T, V, E) ->
+%% letrec 'wrap'/1 = fun (V) -> {'EXIT', V}
+%% in case T of
+%% 'throw' when 'true' -> V
+%% 'exit' when 'true' -> 'wrap'/1(V)
+%% V when 'true' ->
+%% 'wrap'/1({V, erlang:get_stacktrace()})
+%% end
+
+catch_expr(E, Env, Ren, Ctxt, S) ->
+ T = cerl:c_var('T'),
+ V = cerl:c_var('V'),
+ X = cerl:c_var('X'),
+ W = cerl:c_var({wrap,1}),
+ G = cerl:c_call(cerl:c_atom('erlang'),cerl:c_atom('get_stacktrace'),[]),
+ Cs = [cerl:c_clause([cerl:c_atom('throw')], V),
+ cerl:c_clause([cerl:c_atom('exit')], cerl:c_apply(W, [V])),
+ cerl:c_clause([T], cerl:c_apply(W, [cerl:c_tuple([V,G])]))
+ ],
+ C = cerl:c_case(T, Cs),
+ F = cerl:c_fun([V], cerl:c_tuple([cerl:c_atom('EXIT'), V])),
+ H = cerl:c_letrec([{W,F}], C),
+ As = cerl:get_ann(E),
+ {B, S1} = expr(cerl:catch_body(E),Env, Ren, Ctxt, S),
+ {cerl:ann_c_try(As, B, [V], V, [T,V,X], H), S1}.
+
+%% Receive-expressions are rewritten as follows:
+%%
+%% receive
+%% P1 when G1 -> B1
+%% ...
+%% Pn when Gn -> Bn
+%% after T -> A end
+%% becomes:
+%% receive
+%% M when 'true' ->
+%% case M of
+%% P1 when G1 -> do primop RECEIVE_SELECT B1
+%% ...
+%% Pn when Gn -> do primop RECEIVE_SELECT Bn
+%% Pn+1 when 'true' -> primop RECEIVE_NEXT()
+%% end
+%% after T -> A end
+
+receive_expr(E, Env, Ren, Ctxt, S0) ->
+ Cs = cerl:receive_clauses(E),
+ {B, Vs, S1} = clauses(receive_clauses(Cs), Env, Ren, Ctxt, S0),
+ {T, S2} = expr(cerl:receive_timeout(E), Env, Ren, Ctxt, S1),
+ {A, S3} = expr(cerl:receive_action(E), Env, Ren, Ctxt, S2),
+ Cs1 = [cerl:c_clause(Vs, B)],
+ {cerl:update_c_receive(E, Cs1, T, A), S3}.
+
+receive_clauses([C | Cs]) ->
+ Call = cerl:c_primop(cerl:c_atom(?PRIMOP_RECEIVE_SELECT),
+ []),
+ B = cerl:c_seq(Call, cerl:clause_body(C)),
+ C1 = cerl:update_c_clause(C, cerl:clause_pats(C),
+ cerl:clause_guard(C), B),
+ [C1 | receive_clauses(Cs)];
+receive_clauses([]) ->
+ Call = cerl:c_primop(cerl:c_atom(?PRIMOP_RECEIVE_NEXT),
+ []),
+ V = cerl:c_var('X'), % any name is ok
+ [cerl:c_clause([V], Call)].
+
+
+new_vars(N, Env) ->
+ [cerl:c_var(V) || V <- env__new_names(N, Env)].
+
+
+%% ---------------------------------------------------------------------
+%% Environment
+
+env__new() ->
+ rec_env:empty().
+
+env__bind(Key, Value, Env) ->
+ rec_env:bind(Key, Value, Env).
+
+%% env__get(Key, Env) ->
+%% rec_env:get(Key, Env).
+
+env__lookup(Key, Env) ->
+ rec_env:lookup(Key, Env).
+
+env__is_defined(Key, Env) ->
+ rec_env:is_defined(Key, Env).
+
+env__new_name(Env) ->
+ rec_env:new_key(Env).
+
+env__new_names(N, Env) ->
+ rec_env:new_keys(N, Env).
+
+env__new_function_name(F, Env) ->
+ rec_env:new_key(F, Env).
+
+
+%% ---------------------------------------------------------------------
+%% Renaming
+
+ren__new() ->
+ dict:new().
+
+ren__add(Key, Value, Ren) ->
+ dict:store(Key, Value, Ren).
+
+ren__map(Key, Ren) ->
+ case dict:find(Key, Ren) of
+ {ok, Value} ->
+ Value;
+ error ->
+ Key
+ end.
+
+
+%% ---------------------------------------------------------------------
+%% State
+
+-record(state, {module, function, pmatch=true}).
+
+s__new(Module) ->
+ #state{module = Module}.
+
+s__get_module_name(S) ->
+ S#state.module.
+
+s__enter_function(F, S) ->
+ S#state{function = F}.
+
+s__get_function_name(S) ->
+ S#state.function.
+
+s__set_pmatch(V, S) ->
+ S#state{pmatch = V}.
+
+s__get_pmatch(S) ->
+ S#state.pmatch.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_acceptor.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_acceptor.erl
new file mode 100644
index 0000000000..2aef625dc6
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_acceptor.erl
@@ -0,0 +1,120 @@
+% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_acceptor.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description : Acceptor
+%%% This module accepts new connections and starts corresponding
+%%% comm_connection processes.
+%%%
+%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id $
+-module(comm_layer_dir.comm_acceptor).
+
+-export([start_link/1, init/2]).
+
+-import(config).
+-import(gen_tcp).
+-import(inet).
+-import(log).
+-import(lists).
+-import(process_dictionary).
+
+start_link(InstanceId) ->
+ Pid = spawn_link(comm_layer_dir.comm_acceptor, init, [InstanceId, self()]),
+ receive
+ {started} ->
+ {ok, Pid}
+ end.
+
+init(InstanceId, Supervisor) ->
+ process_dictionary:register_process(InstanceId, acceptor, self()),
+ erlang:register(comm_layer_acceptor, self()),
+ log:log(info,"[ CC ] listening on ~p:~p", [config:listenIP(), config:listenPort()]),
+ LS = case config:listenIP() of
+ undefined ->
+ open_listen_port(config:listenPort(), first_ip());
+ _ ->
+ open_listen_port(config:listenPort(), config:listenIP())
+ end,
+ {ok, {_LocalAddress, LocalPort}} = inet:sockname(LS),
+ comm_port:set_local_address(undefined, LocalPort),
+ %io:format("this() == ~w~n", [{LocalAddress, LocalPort}]),
+ Supervisor ! {started},
+ server(LS).
+
+server(LS) ->
+ case gen_tcp:accept(LS) of
+ {ok, S} ->
+ case comm_port:get_local_address_port() of
+ {undefined, LocalPort} ->
+ {ok, {MyIP, _LocalPort}} = inet:sockname(S),
+ comm_port:set_local_address(MyIP, LocalPort);
+ _ ->
+ ok
+ end,
+ receive
+ {tcp, S, Msg} ->
+ {endpoint, Address, Port} = binary_to_term(Msg),
+ % auto determine remote address, when not sent correctly
+ NewAddress = if Address =:= {0,0,0,0} orelse Address =:= {127,0,0,1} ->
+ case inet:peername(S) of
+ {ok, {PeerAddress, _Port}} ->
+ % io:format("Sent Address ~p\n",[Address]),
+ % io:format("Peername is ~p\n",[PeerAddress]),
+ PeerAddress;
+ {error, _Why} ->
+ % io:format("Peername error ~p\n",[Why]).
+ Address
+ end;
+ true ->
+ % io:format("Address is ~p\n",[Address]),
+ Address
+ end,
+ NewPid = comm_connection:new(NewAddress, Port, S),
+ gen_tcp:controlling_process(S, NewPid),
+ inet:setopts(S, [{active, once}, {send_timeout, config:read(tcp_send_timeout)}]),
+ comm_port:register_connection(NewAddress, Port, NewPid, S)
+ end,
+ server(LS);
+ Other ->
+ log:log(warn,"[ CC ] unknown message ~p", [Other])
+ end.
+
+open_listen_port({From, To}, IP) ->
+ open_listen_port(lists:seq(From, To), IP);
+open_listen_port([Port | Rest], IP) ->
+ case gen_tcp:listen(Port, [binary, {packet, 4}, {reuseaddr, true},
+ {active, once}, {ip, IP}]) of
+ {ok, Socket} ->
+ Socket;
+ {error, Reason} ->
+ log:log(error,"[ CC ] can't listen on ~p: ~p~n", [Port, Reason]),
+ open_listen_port(Rest, IP)
+ end;
+open_listen_port([], _) ->
+ abort;
+open_listen_port(Port, IP) ->
+ open_listen_port([Port], IP).
+
+-include_lib("kernel/include/inet.hrl").
+
+first_ip() ->
+ {ok, Hostname} = inet:gethostname(),
+ {ok, HostEntry} = inet:gethostbyname(Hostname),
+ erlang:hd(HostEntry#hostent.h_addr_list).
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_connection.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_connection.erl
new file mode 100644
index 0000000000..8dca647f6d
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_connection.erl
@@ -0,0 +1,206 @@
+% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_connection.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description : creates and destroys connections and represents the
+%%% endpoint of a connection where messages are received and
+%% send from/to the network.
+%%%
+%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%% @version $Id $
+-module(comm_layer_dir.comm_connection).
+
+-export([send/3, open_new/4, new/3, open_new_async/4]).
+
+-import(config).
+-import(gen_tcp).
+-import(inet).
+-import(io).
+-import(io_lib).
+-import(log).
+-import(timer).
+
+-include("comm_layer.hrl").
+
+%% @doc new accepted connection. called by comm_acceptor
+%% @spec new(inet:ip_address(), int(), socket()) -> pid()
+new(Address, Port, Socket) ->
+ spawn(fun () -> loop(Socket, Address, Port) end).
+
+%% @doc open new connection
+%% @spec open_new(inet:ip_address(), int(), inet:ip_address(), int()) ->
+%% {local_ip, inet:ip_address(), int(), pid(), inet:socket()}
+%% | fail
+%% | {connection, pid(), inet:socket()}
+open_new(Address, Port, undefined, MyPort) ->
+ Myself = self(),
+ LocalPid = spawn(fun () ->
+ case new_connection(Address, Port, MyPort) of
+ fail ->
+ Myself ! {new_connection_failed};
+ Socket ->
+ {ok, {MyIP, _MyPort}} = inet:sockname(Socket),
+ Myself ! {new_connection_started, MyIP, MyPort, Socket},
+ loop(Socket, Address, Port)
+ end
+ end),
+ receive
+ {new_connection_failed} ->
+ fail;
+ {new_connection_started, MyIP, MyPort, S} ->
+ {local_ip, MyIP, MyPort, LocalPid, S}
+ end;
+open_new(Address, Port, _MyAddress, MyPort) ->
+ Owner = self(),
+ LocalPid = spawn(fun () ->
+ case new_connection(Address, Port, MyPort) of
+ fail ->
+ Owner ! {new_connection_failed};
+ Socket ->
+ Owner ! {new_connection_started, Socket},
+ loop(Socket, Address, Port)
+ end
+ end),
+ receive
+ {new_connection_failed} ->
+ fail;
+ {new_connection_started, Socket} ->
+ {connection, LocalPid, Socket}
+ end.
+
+% ===============================================================================
+% @doc open a new connection asynchronously
+% ===============================================================================
+-spec(open_new_async/4 :: (any(), any(), any(), any()) -> pid()).
+open_new_async(Address, Port, _MyAddr, MyPort) ->
+ Pid = spawn(fun () ->
+ case new_connection(Address, Port, MyPort) of
+ fail ->
+ comm_port:unregister_connection(Address, Port),
+ ok;
+ Socket ->
+ loop(Socket, Address, Port)
+ end
+ end),
+ Pid.
+
+
+send({Address, Port, Socket}, Pid, Message) ->
+ BinaryMessage = term_to_binary({deliver, Pid, Message}),
+ SendTimeout = config:read(tcp_send_timeout),
+ {Time, Result} = timer:tc(gen_tcp, send, [Socket, BinaryMessage]),
+ if
+ Time > 1200 * SendTimeout ->
+ log:log(error,"[ CC ] send to ~p took ~p: ~p",
+ [Address, Time, inet:getopts(Socket, [keep_alive, send_timeout])]);
+ true ->
+ ok
+ end,
+ case Result of
+ ok ->
+ ?LOG_MESSAGE(erlang:element(1, Message), byte_size(BinaryMessage)),
+ ok;
+ {error, closed} ->
+ comm_port:unregister_connection(Address, Port),
+ close_connection(Socket);
+ {error, _Reason} ->
+ %log:log(error,"[ CC ] couldn't send to ~p:~p (~p)", [Address, Port, Reason]),
+ comm_port:unregister_connection(Address, Port),
+ close_connection(Socket)
+ end.
+
+loop(fail, Address, Port) ->
+ comm_port:unregister_connection(Address, Port),
+ ok;
+loop(Socket, Address, Port) ->
+ receive
+ {send, Pid, Message} ->
+ case send({Address, Port, Socket}, Pid, Message) of
+ ok -> loop(Socket, Address, Port);
+ _ -> ok
+ end;
+ {tcp_closed, Socket} ->
+ comm_port:unregister_connection(Address, Port),
+ gen_tcp:close(Socket);
+ {tcp, Socket, Data} ->
+ case binary_to_term(Data) of
+ {deliver, Process, Message} ->
+ Process ! Message,
+ inet:setopts(Socket, [{active, once}]),
+ loop(Socket, Address, Port);
+ {user_close} ->
+ comm_port:unregister_connection(Address, Port),
+ gen_tcp:close(Socket);
+ {youare, _Address, _Port} ->
+ %% @TODO what do we get from this information?
+ inet:setopts(Socket, [{active, once}]),
+ loop(Socket, Address, Port);
+ Unknown ->
+ log:log(warn,"[ CC ] unknown message ~p", [Unknown]),
+ inet:setopts(Socket, [{active, once}]),
+ loop(Socket, Address, Port)
+ end;
+
+ {youare, _IP, _Port} ->
+ loop(Socket, Address, Port);
+
+ Unknown ->
+ log:log(warn,"[ CC ] unknown message2 ~p", [Unknown]) ,
+ loop(Socket, Address, Port)
+ end.
+
+% ===============================================================================
+
+-spec(new_connection(inet:ip_address(), integer(), integer()) -> inet:socket() | fail).
+new_connection(Address, Port, MyPort) ->
+ case gen_tcp:connect(Address, Port, [binary, {packet, 4}, {nodelay, true}, {active, once},
+ {send_timeout, config:read(tcp_send_timeout)}],
+ config:read(tcp_connect_timeout)) of
+ {ok, Socket} ->
+ % send end point data
+ case inet:sockname(Socket) of
+ {ok, {MyAddress, _MyPort}} ->
+ Message = term_to_binary({endpoint, MyAddress, MyPort}),
+ gen_tcp:send(Socket, Message),
+ case inet:peername(Socket) of
+ {ok, {RemoteIP, RemotePort}} ->
+ YouAre = term_to_binary({youare, RemoteIP, RemotePort}),
+ gen_tcp:send(Socket, YouAre),
+ Socket;
+ {error, _Reason} ->
+ %log:log(error,"[ CC ] reconnect to ~p because socket is ~p",
+ % [Address, Reason]),
+ close_connection(Socket),
+ new_connection(Address, Port, MyPort)
+ end;
+ {error, _Reason} ->
+ %log:log(error,"[ CC ] reconnect to ~p because socket is ~p",
+ % [Address, Reason]),
+ close_connection(Socket),
+ new_connection(Address, Port, MyPort)
+ end;
+ {error, _Reason} ->
+ %log:log(error,"[ CC ] couldn't connect to ~p:~p (~p)",
+ %[Address, Port, Reason]),
+ fail
+ end.
+
+close_connection(Socket) ->
+ spawn( fun () ->
+ gen_tcp:close(Socket)
+ end ).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.erl
new file mode 100644
index 0000000000..f48324e49c
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.erl
@@ -0,0 +1,83 @@
+% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_layer.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description : Public interface to Communication Layer.
+%%% Generic functions to send messages.
+%%% Distinguishes on runtime whether the destination is in the
+%%% same Erlang virtual machine (use ! for sending) or on a remote
+%%% site (use comm_port:send()).
+%%%
+%%% Created : 04 Feb 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id $
+-module(comm_layer_dir.comm_layer).
+
+-author('[email protected]').
+-vsn('$Id: comm_layer.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
+
+-export([start_link/0, send/2, this/0, here/1]).
+
+-import(io).
+-import(util).
+-import(log).
+
+-include("comm_layer.hrl").
+
+
+% @TODO: should be ip
+-type(process_id() :: {any(), integer(), pid()}).
+%%====================================================================
+%% public functions
+%%====================================================================
+
+%% @doc starts the communication port (for supervisor)
+%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
+start_link() ->
+ comm_port_sup:start_link().
+
+%% @doc a process descriptor has to specify the erlang vm
+%% + the process inside. {IP address, port, pid}
+%% @type process_id() = {inet:ip_address(), int(), pid()}.
+%% @spec send(process_id(), term()) -> ok
+
+send({{_IP1, _IP2, _IP3, _IP4} = _IP, _Port, _Pid} = Target, Message) ->
+ {MyIP,MyPort} = comm_port:get_local_address_port(),
+ %io:format("send: ~p:~p -> ~p:~p(~p) : ~p\n", [MyIP, MyPort, _IP, _Port, _Pid, Message]),
+ IsLocal = (MyIP == _IP) and (MyPort == _Port),
+ if
+ IsLocal ->
+ ?LOG_MESSAGE(erlang:element(1, Message), byte_size(term_to_binary(Message))),
+ _Pid ! Message;
+ true ->
+ comm_port:send(Target, Message)
+ end;
+
+send(Target, Message) ->
+ log:log(error,"[ CC ] wrong call to cs_send:send: ~w ! ~w", [Target, Message]),
+ log:log(error,"[ CC ] stacktrace: ~w", [util:get_stacktrace()]),
+ ok.
+
+%% @doc returns process descriptor for the calling process
+-spec(this/0 :: () -> atom()).%process_id()).
+this() ->
+ here(self()).
+
+-spec(here/1 :: (pid()) -> process_id()).
+here(Pid) ->
+ {LocalIP, LocalPort} = comm_port:get_local_address_port(),
+ {LocalIP, LocalPort, Pid}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.hrl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.hrl
new file mode 100644
index 0000000000..f4e4d560f7
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_layer.hrl
@@ -0,0 +1,30 @@
+% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_layer.hrl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description :
+%%%
+%%% Created : 31 Jul 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id: comm_layer.hrl,v 1.1 2009/11/06 12:41:36 maria Exp $
+-author('[email protected]').
+-vsn('$Id: comm_layer.hrl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
+
+% enable logging of message statistics
+%-define(LOG_MESSAGE(TAG, SIZE), comm_layer.comm_logger:log(TAG, SIZE)).
+-define(LOG_MESSAGE(TAG, SIZE), ok).
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_logger.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_logger.erl
new file mode 100644
index 0000000000..c70b0d3438
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_logger.erl
@@ -0,0 +1,143 @@
+% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_logger.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description :
+%%%
+%%% Created : 31 Jul 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id: comm_logger.erl,v 1.1 2009/11/06 12:41:36 maria Exp $
+-module(comm_layer_dir.comm_logger).
+
+-author('[email protected]').
+-vsn('$Id: comm_logger.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
+
+-behaviour(gen_server).
+
+-import(gb_trees).
+-import(gen_server).
+
+%% API
+-export([start_link/0]).
+
+-export([log/2, dump/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, {start, map}).
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+%%--------------------------------------------------------------------
+%% Function: log(Tag, Size) -> ok
+%% Description: logs a message type with its size
+%%--------------------------------------------------------------------
+log(Tag, Size) ->
+ gen_server:cast(?MODULE, {log, Tag, Size}).
+
+%%--------------------------------------------------------------------
+%% Function: dump() -> {gb_tree:gb_trees(), {Date, Time}}
+%% Description: gets the logging state
+%%--------------------------------------------------------------------
+dump() ->
+ gen_server:call(?MODULE, {dump}).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([]) ->
+ {ok, #state{start=erlang:now(), map=gb_trees:empty()}}.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call({dump}, _From, State) ->
+ Reply = {State#state.map, State#state.start},
+ {reply, Reply, State};
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast({log, Tag, Size}, State) ->
+ case gb_trees:lookup(Tag, State#state.map) of
+ none ->
+ {noreply, State#state{map=gb_trees:insert(Tag, {Size, 1}, State#state.map)}};
+ {value, {OldSize, OldCount}} ->
+ {noreply, State#state{map=gb_trees:update(Tag, {Size + OldSize, OldCount + 1}, State#state.map)}}
+ end;
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port.erl
new file mode 100644
index 0000000000..5eded48750
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port.erl
@@ -0,0 +1,240 @@
+% Copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_port.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description : Main CommLayer Interface
+%%% Maps remote addresses to comm_connection PIDs.
+%%%
+%%% Created : 18 Apr 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum f�r Informationstechnik Berlin
+%% @version $Id $
+-module(comm_layer_dir.comm_port).
+
+-author('[email protected]').
+-vsn('$Id: comm_port.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
+
+-behaviour(gen_server).
+
+-import(ets).
+-import(gen_server).
+-import(io).
+-import(log).
+
+-define(ASYNC, true).
+%-define(SYNC, true).
+
+%% API
+-export([start_link/0,
+ send/2,
+ unregister_connection/2, register_connection/4,
+ set_local_address/2, get_local_address_port/0]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%% @doc
+%% @spec send({inet:ip_address(), int(), pid()}, term()) -> ok
+-ifdef(ASYNC).
+send({Address, Port, Pid}, Message) ->
+ gen_server:call(?MODULE, {send, Address, Port, Pid, Message}, 20000).
+-endif.
+-ifdef(SYNC).
+send({Address, Port, Pid}, Message) ->
+ case ets:lookup(?MODULE, {Address, Port}) of
+ [{{Address, Port}, {_LPid, Socket}}] ->
+ comm_connection:send({Address, Port, Socket}, Pid, Message),
+ ok;
+ [] ->
+ gen_server:call(?MODULE, {send, Address, Port, Pid, Message}, 20000)
+ end.
+-endif.
+
+
+%% @doc
+%% @spec unregister_connection(inet:ip_address(), int()) -> ok
+unregister_connection(Adress, Port) ->
+ gen_server:call(?MODULE, {unregister_conn, Adress, Port}, 20000).
+
+%% @doc
+%% @spec register_connection(inet:ip_address(), int(), pid(), gen_tcp:socket()) -> ok | duplicate
+register_connection(Adress, Port, Pid, Socket) ->
+ gen_server:call(?MODULE, {register_conn, Adress, Port, Pid, Socket}, 20000).
+
+%% @doc
+%% @spec set_local_address(inet:ip_address(), int()) -> ok
+set_local_address(Address, Port) ->
+ gen_server:call(?MODULE, {set_local_address, Address, Port}, 20000).
+
+
+%% @doc
+%% @spec get_local_address_port() -> {inet:ip_address(),int()}
+get_local_address_port() ->
+ case ets:lookup(?MODULE, local_address_port) of
+ [{local_address_port, Value}] ->
+ Value;
+ [] ->
+ undefined
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the server
+%%--------------------------------------------------------------------
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: init(Args) -> {ok, State} |
+%% {ok, State, Timeout} |
+%% ignore |
+%% {stop, Reason}
+%% Description: Initiates the server
+%%--------------------------------------------------------------------
+init([]) ->
+ ets:new(?MODULE, [set, protected, named_table]),
+ {ok, ok}. % empty state.
+
+%%--------------------------------------------------------------------
+%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
+%% {reply, Reply, State, Timeout} |
+%% {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, Reply, State} |
+%% {stop, Reason, State}
+%% Description: Handling call messages
+%%--------------------------------------------------------------------
+handle_call({send, Address, Port, Pid, Message}, _From, State) ->
+ send(Address, Port, Pid, Message, State);
+
+handle_call({unregister_conn, Address, Port}, _From, State) ->
+ ets:delete(?MODULE, {Address, Port}),
+ {reply, ok, State};
+
+handle_call({register_conn, Address, Port, Pid, Socket}, _From, State) ->
+ case ets:lookup(?MODULE, {Address, Port}) of
+ [{{Address, Port}, _}] ->
+ {reply, duplicate, State};
+ [] ->
+ ets:insert(?MODULE, {{Address, Port}, {Pid, Socket}}),
+ {reply, ok, State}
+ end;
+
+handle_call({set_local_address, Address, Port}, _From, State) ->
+ ets:insert(?MODULE, {local_address_port, {Address,Port}}),
+ {reply, ok, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_cast(Msg, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling cast messages
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: handle_info(Info, State) -> {noreply, State} |
+%% {noreply, State, Timeout} |
+%% {stop, Reason, State}
+%% Description: Handling all non call/cast messages
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% Function: terminate(Reason, State) -> void()
+%% Description: This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% Description: Convert process state when code is changed
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+-ifdef(ASYNC).
+send(Address, Port, Pid, Message, State) ->
+ {DepAddr,DepPort} = get_local_address_port(),
+ if
+ DepAddr == undefined ->
+ open_sync_connection(Address, Port, Pid, Message, State);
+ true ->
+ case ets:lookup(?MODULE, {Address, Port}) of
+ [{{Address, Port}, {ConnPid, _Socket}}] ->
+ ConnPid ! {send, Pid, Message},
+ {reply, ok, State};
+ [] ->
+ ConnPid = comm_connection:open_new_async(Address, Port,
+ DepAddr, DepPort),
+ ets:insert(?MODULE, {{Address, Port}, {ConnPid, undef}}),
+ ConnPid ! {send, Pid, Message},
+ {reply, ok, State}
+ end
+ end.
+-endif.
+
+-ifdef(SYNC).
+send(Address, Port, Pid, Message, State) ->
+ case ets:lookup(?MODULE, {Address, Port}) of
+ [{{Address, Port}, {_LPid, Socket}}] ->
+ comm_connection:send({Address, Port, Socket}, Pid, Message),
+ {reply, ok, State};
+ [] ->
+ open_sync_connection(Address, Port, Pid, Message, State)
+ end.
+-endif.
+
+
+open_sync_connection(Address, Port, Pid, Message, State) ->
+ {DepAddr,DepPort} = get_local_address_port(),
+ case comm_connection:open_new(Address, Port, DepAddr, DepPort) of
+ {local_ip, MyIP, MyPort, MyPid, MySocket} ->
+ comm_connection:send({Address, Port, MySocket}, Pid, Message),
+ log:log(info,"[ CC ] this() == ~w", [{MyIP, MyPort}]),
+ % set_local_address(t, {MyIP,MyPort}}),
+ % register_connection(Address, Port, MyPid, MySocket),
+ ets:insert(?MODULE, {local_address_port, {MyIP,MyPort}}),
+ ets:insert(?MODULE, {{Address, Port}, {MyPid, MySocket}}),
+ {reply, ok, State};
+ fail ->
+ % drop message (remote node not reachable, failure detector will notice)
+ {reply, ok, State};
+ {connection, LocalPid, NewSocket} ->
+ comm_connection:send({Address, Port, NewSocket}, Pid, Message),
+ ets:insert(?MODULE, {{Address, Port}, {LocalPid, NewSocket}}),
+ % register_connection(Address, Port, LPid, NewSocket),
+ {reply, ok, State}
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port_sup.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port_sup.erl
new file mode 100644
index 0000000000..622d0a8c06
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/comm_layer/comm_port_sup.erl
@@ -0,0 +1,90 @@
+% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : comm_port_sup.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description :
+%%%
+%%% Created : 04 Feb 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id: comm_port_sup.erl,v 1.1 2009/11/06 12:41:36 maria Exp $
+-module(comm_layer_dir.comm_port_sup).
+
+-author('[email protected]').
+-vsn('$Id: comm_port_sup.erl,v 1.1 2009/11/06 12:41:36 maria Exp $ ').
+
+-behaviour(supervisor).
+
+-import(supervisor).
+-import(randoms).
+-import(string).
+-import(config).
+
+-export([start_link/0, init/1]).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+%% Description: Starts the supervisor
+%%--------------------------------------------------------------------
+start_link() ->
+ supervisor:start_link(?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+%%--------------------------------------------------------------------
+%% Func: init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
+%% ignore |
+%% {error, Reason}
+%% Description: Whenever a supervisor is started using
+%% supervisor:start_link/[2,3], this function is called by the new process
+%% to find out about restart strategy, maximum restart frequency and child
+%% specifications.
+%%--------------------------------------------------------------------
+init([]) ->
+ InstanceId = string:concat("comm_port_", randoms:getRandomId()),
+ CommPort =
+ {comm_port,
+ {comm_layer_dir.comm_port, start_link, []},
+ permanent,
+ brutal_kill,
+ worker,
+ []},
+ CommAcceptor =
+ {comm_acceptor,
+ {comm_layer_dir.comm_acceptor, start_link, [InstanceId]},
+ permanent,
+ brutal_kill,
+ worker,
+ []},
+ CommLogger =
+ {comm_logger,
+ {comm_layer_dir.comm_logger, start_link, []},
+ permanent,
+ brutal_kill,
+ worker,
+ []},
+ {ok, {{one_for_all, 10, 1},
+ [
+ CommPort,
+ CommLogger,
+ CommAcceptor
+ ]}}.
+
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/compare1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/compare1.erl
new file mode 100644
index 0000000000..2626d2ebea
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/compare1.erl
@@ -0,0 +1,21 @@
+%%%-------------------------------------------------------------------
+%%% File : compare1.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 20 Apr 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(compare1).
+
+-export([t/0]).
+
+t() ->
+ t(42).
+
+t(X) when X > 42 ->
+ error;
+t(X) when X < 42 ->
+ error;
+t(X) when X =/= 42 ->
+ error;
+t(X) -> ok.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/confusing_warning.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/confusing_warning.erl
new file mode 100644
index 0000000000..c82df0f056
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/confusing_warning.erl
@@ -0,0 +1,22 @@
+%% Test case that results in a confusing warning -- created from a
+%% very stripped down actual application. The second case clause of
+%% test/1 cannot possibly match because all a-pairs match with the
+%% first clause. Dialyzer complains that the second argument of the
+%% second 2-tuple has type 'aaa' | 'bbb'. This is mucho confusing
+%% since there is no 'a'-pair whose second element is 'aaa' | 'bbb'.
+%% Pattern matching compilation is of course what's to blame here.
+
+-module(confusing_warning).
+-export([test/1]).
+
+test(N) when is_integer(N) ->
+ case foo(N) of
+ {a, I} when is_integer(I) ->
+ I;
+ {a, {_, L}} -> % this clause cannot possibly match
+ L
+ end.
+
+foo(1) -> {a, 42};
+foo(2) -> {b, aaa}; % this is really unused
+foo(3) -> {b, bbb}. % this is really unused
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/contract2.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/contract2.erl
new file mode 100644
index 0000000000..83ee5910f2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/contract2.erl
@@ -0,0 +1,18 @@
+-module(contract2).
+-export([test/2]).
+
+-spec test(list(), list()) -> ok.
+
+test([], []) ->
+ ok;
+test([], L) ->
+ raise(L);
+test([H|T], L) ->
+ case H of
+ true -> test(T, L);
+ false -> test(T, [H|L])
+ end.
+
+-spec raise(_) -> no_return().
+raise(X) ->
+ throw(X).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/contract3.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/contract3.erl
new file mode 100644
index 0000000000..c135b72d45
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/contract3.erl
@@ -0,0 +1,34 @@
+%%%-------------------------------------------------------------------
+%%% File : contract3.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description : Check overloaded domains
+%%%
+%%% Created : 2 Nov 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(contract3).
+
+-export([t/3]).
+
+t(X, Y, Z) ->
+ t1(X),
+ t2(X, Y),
+ t3(X, Y, Z).
+
+-spec t1(atom()|integer()) -> integer();
+ (atom()|list()) -> atom().
+
+t1(X) ->
+ foo:bar(X).
+
+-spec t2(atom(), integer()) -> integer();
+ (atom(), list()) -> atom().
+
+t2(X, Y) ->
+ foo:bar(X, Y).
+
+-spec t3(atom(), integer(), list()) -> integer();
+ (X, integer(), list()) -> X.
+
+t3(X, Y, Z) ->
+ X.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/contract5.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/contract5.erl
new file mode 100644
index 0000000000..6385473c20
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/contract5.erl
@@ -0,0 +1,15 @@
+%%%-------------------------------------------------------------------
+%%% File : contract5.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description : Excercise modified record types.
+%%%
+%%% Created : 15 Apr 2008 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(contract5).
+-export([t/0]).
+
+-record(bar, {baz}).
+
+-spec t() -> #bar{baz :: boolean()}.
+
+t() -> #bar{baz = not_a_boolean}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/disj_norm_form.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/disj_norm_form.erl
new file mode 100644
index 0000000000..313c2e8b86
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/disj_norm_form.erl
@@ -0,0 +1,23 @@
+%%%-------------------------------------------------------------------
+%%% File : disj_norm_form.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description : Exposes a bad behavior in expansion to
+%%% disjunctive normal form of guards.
+%%%
+%%% Created : 24 Aug 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(disj_norm_form).
+
+-export([t/1]).
+
+-record(foo, {bar}).
+
+t(R) ->
+ if R#foo.bar =:= 1;
+ R#foo.bar =:= 2;
+ R#foo.bar =:= 3;
+ R#foo.bar =:= 4;
+ R#foo.bar =:= 5;
+ R#foo.bar =:= 6 -> ok;
+ true -> error
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/eqeq.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/eqeq.erl
new file mode 100644
index 0000000000..6767023e3a
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/eqeq.erl
@@ -0,0 +1,16 @@
+%%%-------------------------------------------------------------------
+%%% File : eqeq.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 12 Nov 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(eqeq).
+
+-export([t/0]).
+
+t() ->
+ comp(3.14, foo).
+
+comp(X, Y) -> X =:= Y.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/ets_select.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/ets_select.erl
new file mode 100644
index 0000000000..2b3c38cd59
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/ets_select.erl
@@ -0,0 +1,12 @@
+-module(ets_select).
+-export([test/0]).
+
+test() ->
+ Table = ets:new(table, [set,{keypos,1}]),
+ ets:insert(Table, {foo, bar, baz}),
+ foo(Table). % ets:select(Table, [{{'_', '$1', '$2'}, [], ['$$']}]).
+
+foo(Table) ->
+ Tuples = ets:select(Table, [{{'_', '$1', '$2'}, [], ['$$']}]),
+ [list_to_tuple(Tuple) || Tuple <- Tuples].
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/exhaust_case.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/exhaust_case.erl
new file mode 100644
index 0000000000..6b20c7c98c
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/exhaust_case.erl
@@ -0,0 +1,24 @@
+%%-------------------------------------------------------------------
+%% File : exhaust_case.erl
+%% Author : Kostis Sagonas <[email protected]>
+%% Description : Tests that Dialyzer warns when it finds an unreachable
+%% case clause (independently of whether ground vs. var).
+%%
+%% Created : 15 Dec 2004 by Kostis Sagonas <[email protected]>
+%%-------------------------------------------------------------------
+
+-module(exhaust_case).
+-export([t/1]).
+
+t(X) when is_integer(X) ->
+ case ret(X) of
+ foo -> ok;
+ bar -> ok;
+ 42 -> ok;
+ _other -> error %% unreachable clause (currently no warning)
+ %% other -> error %% but contrast this with this clause... hmm
+ end.
+
+ret(1) -> foo;
+ret(2) -> bar.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/failing_guard1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/failing_guard1.erl
new file mode 100644
index 0000000000..8fa1ce9ce0
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/failing_guard1.erl
@@ -0,0 +1,16 @@
+%%-----------------------------------------------------------------------
+%% Author: Kostis Sagonas (Wed Aug 23 14:54:25 CEST 2006)
+%%
+%% Program to test failing arithmetic comparisons with a number of the
+%% wrong type. The first case is handled properly; the second one is not.
+%% Why?
+%%-----------------------------------------------------------------------
+
+-module(failing_guard1).
+-export([n/1]).
+
+n(N) when (N / 2) =:= 2 -> multiple_of_four;
+n(N) when (N div 3) =:= 2.0 -> multiple_of_six;
+n(N) when (N rem 3) =:= 2.0 -> multiple_of_six;
+n(N) when is_number(N) -> other_number.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/flatten.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/flatten.erl
new file mode 100644
index 0000000000..ac28fe27c9
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/flatten.erl
@@ -0,0 +1,18 @@
+%%%-------------------------------------------------------------------
+%%% File : flatten.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 4 Nov 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(flatten).
+
+-export([t/1]).
+
+t(Dir) ->
+ case file:list_dir(Dir) of
+ {ok,FileList} ->
+ FileList;
+ {error,Reason} ->
+ {error,lists:flatten("Can't open directory "++Dir++": "++Reason)}
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/fun_app.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/fun_app.erl
new file mode 100644
index 0000000000..605b0799d1
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/fun_app.erl
@@ -0,0 +1,42 @@
+%% This is taken from the code of distel.
+
+-module(fun_app).
+-export([html_index/2]). % , lines/3, curry/2]).
+
+html_index(file,Dir) ->
+ fold_file(curry(fun lines/3,Dir),[],filename:join([Dir,"doc","man_index.html"])).
+
+fold_file(Fun,Acc0,File) ->
+ {ok, FD} = file:open(File, [read]),
+ Acc = fold_file_lines(FD,Fun,Acc0),
+ file:close(FD),
+ Acc.
+
+fold_file_lines(FD,Fun,Acc) ->
+ case io:get_line(FD, "") of
+ eof -> Acc;
+ Line -> fold_file_lines(FD,Fun,Fun(trim_nl(Line),Acc))
+ end.
+
+trim_nl(Str) -> lists:reverse(tl(lists:reverse(Str))).
+
+lines(Line,_,Dir) ->
+ case string:tokens(Line, "<> \"") of
+ ["TD", "A", "HREF=", "../"++Href, M|_] ->
+ case filename:basename(Href, ".html") of
+ "index" -> ok;
+ M -> e_set({file,M}, filename:join([Dir,Href]))
+ end;
+ _ -> ok
+ end.
+
+e_set(Key,Val) -> ets:insert(?MODULE, {Key,Val}).
+
+curry(F, Arg) ->
+ case erlang:fun_info(F,arity) of
+ {_,1} -> fun() -> F(Arg) end;
+ {_,2} -> fun(A) -> F(A,Arg) end;
+ {_,3} -> fun(A,B) -> F(A,B,Arg) end;
+ {_,4} -> fun(A,B,C) -> F(A,B,C,Arg) end
+ end.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_match.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_match.erl
new file mode 100644
index 0000000000..c15226ba6e
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_match.erl
@@ -0,0 +1,21 @@
+%%%-------------------------------------------------------------------
+%%% File : fun_ref_match.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description : Find that newly created funs and references cannot
+%%% match on earlier bound variables.
+%%%
+%%% Created : 10 Mar 2005 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(fun_ref_match).
+
+-export([t1/1, t2/1]).
+
+t1(X) ->
+ X = fun(Y) -> Y end,
+ ok.
+
+t2(X) ->
+ case make_ref() of
+ X -> error;
+ _ -> ok
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_record.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_record.erl
new file mode 100644
index 0000000000..eace7a4332
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/fun_ref_record.erl
@@ -0,0 +1,17 @@
+%%%-------------------------------------------------------------------
+%%% File : fun_ref_record.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description : Exposes a bug when referring to a fun in a record.
+%%%
+%%% Created : 25 Sep 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(fun_ref_record).
+
+-export([t1/0, t2/0]).
+
+-record(foo, {bar}).
+
+t1() ->
+ #foo{bar=fun t2/0}.
+
+t2() -> ok.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/gencall.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/gencall.erl
new file mode 100644
index 0000000000..d2875c9df1
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/gencall.erl
@@ -0,0 +1,12 @@
+%% Error: gen_server:handle_cast/2 is not logged as an unexported func
+%% but unknown function.
+-module(gencall).
+
+-export([f/0]).
+
+f() ->
+ gen_server:call(1,2,3),
+ ets:lookup(1,2,3),
+ gencall2:foo(),
+ gencall:foo(),
+ gen_server:handle_cast(1,2).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/gs_make.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/gs_make.erl
new file mode 100644
index 0000000000..cbf3ef5dcb
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/gs_make.erl
@@ -0,0 +1,261 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id: gs_make.erl,v 1.1 2008/12/17 09:53:50 mikpe Exp $
+%%
+-module(gs_make).
+
+-export([start/0]).
+
+start() ->
+ Terms = the_config(),
+ DB=fill_ets(Terms),
+ {ok,OutFd} = file:open("gstk_generic.hrl", [write]),
+ put(stdout,OutFd),
+% io:format("terms: ~p ~n ets:~p~n",[Terms,ets:tab2list(DB)]),
+ p("% Don't edit this file. It was generated by gs_make:start/0 "),
+ p("at ~p-~p-~p, ~p:~p:~p.\n\n",
+ lists:append(tuple_to_list(date()),tuple_to_list(time()))),
+ gen_out_opts(DB),
+ gen_read(DB),
+ file:close(OutFd),
+ {ok,"gstk_generic.hrl",DB}.
+
+fill_ets(Terms) ->
+ DB = ets:new(gs_mapping,[bag,public]),
+ fill_ets(DB,Terms).
+
+fill_ets(DB,[]) -> DB;
+fill_ets(DB,[{Objs,Opt,Fun,Access}|Terms]) ->
+ fill_ets(DB,lists:flatten(Objs),Opt,Fun,Access),
+ fill_ets(DB,Terms).
+
+fill_ets(_DB,[],_,_,_) -> done;
+fill_ets(DB,[Obj|Objs],Opt,Fun,rw) ->
+ ets:insert(DB,{Obj,Opt,Fun,read}),
+ ets:insert(DB,{Obj,Opt,Fun,write}),
+ fill_ets(DB,Objs,Opt,Fun,rw);
+fill_ets(DB,[Obj|Objs],Opt,Fun,r) ->
+ ets:insert(DB,{Obj,Opt,Fun,read}),
+ fill_ets(DB,Objs,Opt,Fun,r);
+fill_ets(DB,[Obj|Objs],Opt,Fun,w) ->
+ ets:insert(DB,{Obj,Opt,Fun,write}),
+ fill_ets(DB,Objs,Opt,Fun,w).
+
+
+
+gen_out_opts(DB) ->
+ ObjTypes = lists:flatten(ordsets:from_list(ets:match(DB,{'$1','_','_',write}))),
+ p("out_opts([Option|Options],Gstkid,TkW,DB,ExtraArg,S,P,C) ->\n"),
+ p(" {Opt,Val} =\n"),
+ p(" case Option of \n"),
+ p(" {{default,Cat,Key},V} -> {default,{Cat,{Key,V}}};\n"),
+ p(" {_Key,_V} -> Option;\n"),
+ p(" {default,Cat,Opti} -> {default,{Cat,Opti}};\n"),
+ p(" Atom when atom(Atom) -> {Atom,undefined};\n"),
+ p(" _ -> {error, {invalid_option,Option}}\n"),
+ p(" end,\n"),
+ p(" case Gstkid#gstkid.objtype of\n"),
+ gen_out_type_case_clauses(merge_types(ObjTypes),DB),
+ p(" Q -> exit({internal_error,unknown_objtype,Q})\n"),
+ p(" end;\n"),
+ p("out_opts([],_Gstkid,_TkW,_DB,_ExtraArg,S,P,C) -> \n"),
+ p(" {S,P,C}.\n").
+
+
+gen_out_type_case_clauses([],_DB) -> done;
+gen_out_type_case_clauses([Objtype|Objtypes],DB) ->
+ OptsFuns = lists:map(fun (L) -> list_to_tuple(L) end,
+ ets:match(DB,{Objtype,'$1','$2',write})),
+ p(" ~p -> \ncase Opt of\n",[Objtype]),
+ gen_opt_case_clauses(merge_opts(opt_prio(),OptsFuns)),
+ p(" _ -> \n"),
+ p(" handle_external_opt_call([Option|Options],Gstkid,TkW,DB,ExtraArg,"
+ " gstk_~p:option(Option,Gstkid,TkW,DB,ExtraArg),S,P,C)\n",
+ [Objtype]),
+ p(" end;\n"),
+ gen_out_type_case_clauses(Objtypes,DB).
+
+gen_opt_case_clauses([]) ->
+ done;
+gen_opt_case_clauses([{Opt,Fun}|OptFuncs]) ->
+ p(" ~p ->\n",[Opt]),
+ p(" ~p(Val,Options,Gstkid,TkW,DB,ExtraArg,S,P,C);\n",[Fun]),
+ gen_opt_case_clauses(OptFuncs).
+
+gen_read(DB) ->
+ ObjTypes = lists:flatten(ordsets:from_list(ets:match(DB,{'$1','_','_',read}))),
+ p("read_option(DB,Gstkid,TkW,Option,ExtraArg) ->\n"),
+ p(" Key = case Option of\n"),
+ p(" Atom when atom(Atom) -> Atom;\n"),
+ p(" Opt when tuple(Opt) -> element(1,Opt)\n"),
+ p(" end,\n"),
+ p(" case Gstkid#gstkid.objtype of\n"),
+ gen_read_type_clauses(merge_types(ObjTypes),DB),
+ p(" Q -> exit({internal_error,unknown_objtype,Q})\n"),
+ p(" end.\n").
+
+
+gen_read_type_clauses([],_) -> done;
+gen_read_type_clauses([Objtype|Objtypes],DB) ->
+ OptsFuns = lists:map(fun (L) -> list_to_tuple(L) end,
+ ets:match(DB,{Objtype,'$1','$2',read})),
+ p(" ~p -> \ncase Key of\n",[Objtype]),
+ gen_readopt_case_clauses(merge_opts(opt_prio(),OptsFuns)),
+ p(" _ -> \nhandle_external_read(gstk_~p:read_option(Option,Gstkid,TkW,DB,ExtraArg))\n",[Objtype]),
+ p(" end;\n"),
+ gen_read_type_clauses(Objtypes,DB).
+
+gen_readopt_case_clauses([]) ->
+ done;
+gen_readopt_case_clauses([{Opt,Fun}|OptFuncs]) ->
+ p(" ~p -> \n~p(Option,Gstkid,TkW,DB,ExtraArg);\n",[Opt,Fun]),
+ gen_readopt_case_clauses(OptFuncs).
+
+
+p(Str) ->
+ ok = io:format(get(stdout),Str,[]).
+
+p(Format,Data) ->
+ ok = io:format(get(stdout),Format,Data).
+
+%%----------------------------------------------------------------------
+%% There items should be placed early in a case statement.
+%%----------------------------------------------------------------------
+obj_prio() -> [rectangle,line,gridline,image,button,canvas,checkbutton,radiobutton].
+opt_prio() -> [x,y,width,height,move,coords,data].
+
+merge_types(Types) ->
+ T2 = ordsets:from_list(Types),
+ P2 = ordsets:from_list(obj_prio()),
+ obj_prio() ++ ordsets:subtract(T2, P2).
+
+merge_opts([],L) -> L;
+merge_opts([Opt|Opts],Dict) ->
+ case gs:assq(Opt,Dict) of
+ {value,V} -> [{Opt,V}|merge_opts(Opts,lists:keydelete(Opt,1,Dict))];
+ false -> merge_opts(Opts,Dict)
+ end.
+
+the_config() ->
+ Buttons=[button,checkbutton,radiobutton],
+ AllPureTk = [Buttons,canvas,editor,entry,frame,label,listbox,
+ menubar,menubutton,scale,window],
+ CanvasObj = [arc,image,line,oval,polygon,rectangle,text],
+ All = [AllPureTk,CanvasObj,grid,gridline,menu,menuitem,gs],
+ Containers = [canvas,frame,grid,menu,menubar,menubutton,menuitem,window],
+ Ob1 = [Buttons,canvas,grid,frame,label,entry,editor,listbox,scale],
+ Ob2 = [button,checkbutton,radiobutton,label,menubutton],
+ Ob3 = [Buttons,frame,label,entry,editor,listbox,scale,menubutton,
+ menubar,menu],
+ Ob4 = [canvas,editor,listbox],
+ [{[Buttons,entry,scale,menubutton],enable,gen_enable,rw},
+ {[Buttons,label,entry,scale,menubutton,menu],fg,gen_fg,rw},
+ {[Buttons,label,entry,scale,menubutton,menu],bg,gen_bg,rw},
+ {Ob1,anchor,gen_anchor,rw},
+ {Ob1,height,gen_height,r},
+ {Ob1--[frame],height,gen_height,w},
+ {Ob1,width,gen_width,r},
+ {Ob1--[frame],width,gen_width,w},
+ {Ob1,pack_x,gen_pack_x,rw},
+ {Ob1,pack_y,gen_pack_y,rw},
+ {Ob1,pack_xy,gen_pack_xy,w},
+ {Ob1,x,gen_x,rw},
+ {Ob1,y,gen_y,rw},
+ {Ob1,raise,gen_raise,w},
+ {Ob1,lower,gen_lower,w},
+ {Ob2,align,gen_align,rw},
+ {Ob2,font,gen_font,rw},
+ {Ob2,justify,gen_justify,rw},
+ {Ob2,padx,gen_padx,rw},
+ {Ob2,pady,gen_pady,rw},
+ {Containers,default,gen_default,w},
+ {[AllPureTk,menu],relief,gen_relief,rw},
+ {[AllPureTk,menu],bw,gen_bw,rw},
+ {[Buttons,canvas,frame,label,entry,scale,menubutton,menu,menubar],
+ setfocus,gen_setfocus,rw},
+ {Ob3,buttonpress,gen_buttonpress,rw},
+ {Ob3,buttonrelease,gen_buttonrelease,rw},
+ {Ob3,configure,gen_configure,rw},
+ {[Ob3,window],destroy,gen_destroy,rw},
+ {[Ob3,window],enter,gen_enter,rw},
+ {[Ob3,window],leave,gen_leave,rw},
+ {[Ob3,window],focus,gen_focus_ev,rw},
+ {[Ob3,window],keypress,gen_keypress,rw},
+ {[Ob3,window],keyrelease,gen_keyrelease,rw},
+ {Ob3,motion,gen_motion,rw},
+ %% events containing x,y are special
+ {[window],buttonpress,gen_buttonpress,r},
+ {[window],buttonrelease,gen_buttonrelease,r},
+ {[window],motion,gen_motion,r},
+ {All,font_wh,gen_font_wh,r},
+ {All,choose_font,gen_choose_font,r},
+ {All,data,gen_data,rw},
+ {All,children,gen_children,r},
+ {All,id,gen_id,r},
+ {All,parent,gen_parent,r},
+ {All,type,gen_type,r},
+ {All,beep,gen_beep,w},
+ {All,keep_opt,gen_keep_opt,w},
+ {All,flush,gen_flush,rw},
+ {AllPureTk,highlightbw,gen_highlightbw,rw},
+ {AllPureTk,highlightbg,gen_highlightbg,rw},
+ {AllPureTk,highlightfg,gen_highlightfg,rw},
+ {AllPureTk,cursor,gen_cursor,rw}, % bug
+ {[Buttons,label,menubutton],label,gen_label,rw},
+ {[Buttons,menubutton,menu],activebg,gen_activebg,rw},
+ {[Buttons,menubutton,menu],activefg,gen_activefg,rw},
+ {[entry],selectbg,gen_selectbg,rw},
+ {[entry],selectbw,gen_selectbw,rw},
+ {[entry],selectfg,gen_selectfg,rw},
+ {Ob4,activebg,gen_so_activebg,rw},
+ {Ob4,bc,gen_so_bc,rw},
+ {Ob4,bg,gen_so_bg,rw},
+ {Ob4,hscroll,gen_so_hscroll,r},
+ {Ob4,scrollbg,gen_so_scrollbg,rw},
+ {Ob4,scrollfg,gen_so_scrollfg,rw},
+ {Ob4,scrolls,gen_so_scrolls,w},
+ {Ob4,selectbg,gen_so_selectbg,rw},
+ {Ob4,selectbg,gen_so_selectbg,rw},
+ {Ob4,selectbw,gen_so_selectbw,rw},
+ {Ob4,selectbw,gen_so_selectbw,rw},
+ {Ob4,selectfg,gen_so_selectfg,rw},
+ {Ob4,selectfg,gen_so_selectfg,rw},
+ {Ob4,vscroll,gen_so_vscroll,r},
+ {CanvasObj,coords,gen_citem_coords,rw},
+ {CanvasObj,lower,gen_citem_lower,w},
+ {CanvasObj,raise,gen_citem_raise,w},
+ {CanvasObj,move,gen_citem_move,w},
+ {CanvasObj,setfocus,gen_citem_setfocus,rw},
+ {CanvasObj,buttonpress,gen_citem_buttonpress,w}, % should be rw
+ {CanvasObj,buttonrelease,gen_citem_buttonrelease,w},
+ {CanvasObj,enter,gen_citem_enter,w},
+ {CanvasObj,focus,gen_citem_setfocus,w},
+ {CanvasObj,keypress,gen_citem_keypress,w},
+ {CanvasObj,keyrelease,gen_citem_keyrelease,w},
+ {CanvasObj,leave,gen_citem_leave,w},
+ {CanvasObj,motion,gen_citem_motion,w},
+ {CanvasObj,buttonpress,gen_buttonpress,r},
+ {CanvasObj,buttonrelease,gen_buttonrelease,r},
+ {CanvasObj,configure,gen_configure,r},
+ {CanvasObj,destroy,gen_destroy,r},
+ {CanvasObj,enter,gen_enter,r},
+ {CanvasObj,leave,gen_leave,r},
+ {CanvasObj,focus,gen_focus_ev,r},
+ {CanvasObj,keypress,gen_keypress,r},
+ {CanvasObj,keyrelease,gen_keyrelease,r},
+ {CanvasObj,motion,gen_motion,r},
+ {[arc,oval,polygon,rectangle],fill,gen_citem_fill,rw}].
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/inf_loop2.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/inf_loop2.erl
new file mode 100644
index 0000000000..fbbec10a55
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/inf_loop2.erl
@@ -0,0 +1,23 @@
+%%---------------------------------------------------------------------
+%% Module that went into an infinite loop when trying to assign types.
+%%
+%% What was happening is that for functions which are in an SCC but all
+%% return none(), a second chance was given to them by the analysis to
+%% see whether they return none() because they are involved in an loop
+%% (presumably server-related) and could be assigned the type unit()
+%% instead. The problem is that when the really return none() for some
+%% other reason (an error such in this case) then we will again find
+%% none() and try again for unit(), thereby entering an infinite loop.
+%% The issue was resolved on May 17th by adding an appropriate boolean
+%% parameter to dialyzer_typesig:solve_scc() function.
+%%---------------------------------------------------------------------
+-module(inf_loop2).
+
+-export([test/0]).
+
+test() ->
+ lists:reverse(gazonk),
+ loop().
+
+loop() ->
+ test().
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/letrec1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/letrec1.erl
new file mode 100644
index 0000000000..f5c265cc60
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/letrec1.erl
@@ -0,0 +1,13 @@
+%%%-------------------------------------------------------------------
+%%% File : letrec1.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 9 Mar 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(letrec1).
+
+-export([t/1]).
+
+t(Opts) ->
+ [Opt || Opt <- Opts, Opt =/= compressed].
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/list_match.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/list_match.erl
new file mode 100644
index 0000000000..77de6d7dee
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/list_match.erl
@@ -0,0 +1,20 @@
+%%%-------------------------------------------------------------------
+%%% File : list_match.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 12 Mar 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(list_match).
+
+-export([t/0]).
+
+t() ->
+ t([1,2,3,4]).
+
+t([]) ->
+ ok;
+t([H|T]) when is_integer(H) ->
+ t(T);
+t([_|T]) ->
+ t(T).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/lzip.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/lzip.erl
new file mode 100644
index 0000000000..753d2939d8
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/lzip.erl
@@ -0,0 +1,8 @@
+-module(lzip).
+-export([test/0, test/1]).
+
+test() ->
+ lists:zip([],[]).
+
+test(L) ->
+ lists:zip(L, []).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/make_tuple.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/make_tuple.erl
new file mode 100644
index 0000000000..0a5edf8c24
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/make_tuple.erl
@@ -0,0 +1,5 @@
+-module(make_tuple).
+-export([test/0]).
+
+test() ->
+ {_,_} = erlang:make_tuple(3, []).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/minus_minus.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/minus_minus.erl
new file mode 100644
index 0000000000..f1e9483c40
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/minus_minus.erl
@@ -0,0 +1,8 @@
+%%------------------------------------------------------------------------
+%% Test file which gave a bogus warning when analyzed with Dialyzer 1.6.1.
+%%------------------------------------------------------------------------
+-module(minus_minus).
+-export([test/0]).
+
+test() ->
+ [] -- [].
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/mod_info.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/mod_info.erl
new file mode 100644
index 0000000000..a24e4276ad
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/mod_info.erl
@@ -0,0 +1,5 @@
+-module(mod_info).
+-export([test/0]).
+
+test() ->
+ {module_info(), module_info(compile)}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/my_filter.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/my_filter.erl
new file mode 100644
index 0000000000..a67c4bd432
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/my_filter.erl
@@ -0,0 +1,17 @@
+-module(my_filter).
+-export([test/0]).
+
+test() ->
+ filter(fun mystery/1, [1,2,3,4]).
+
+filter(Pred, List) when is_function(Pred, 1) ->
+ [ E || E <- List, Pred(E) ].
+
+mystery(X) ->
+ case (X rem 3) of
+ 0 -> true;
+ 1 -> false;
+ 2 -> gazonk
+ end.
+
+%% mystery(_X,_Y) -> true.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/my_sofs.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/my_sofs.erl
new file mode 100644
index 0000000000..32252071d2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/my_sofs.erl
@@ -0,0 +1,83 @@
+%% Program showing the problems with record field accesses.
+
+-module(my_sofs).
+-export([ordset_of_sets/3, is_equal/2]).
+
+-define(TAG, 'Set').
+-define(ORDTAG, 'OrdSet').
+
+-record(?TAG, {data = [], type = type}).
+-record(?ORDTAG, {orddata = {}, ordtype = type}).
+
+-define(LIST(S), (S)#?TAG.data).
+-define(TYPE(S), (S)#?TAG.type).
+-define(SET(L, T), #?TAG{data = L, type = T}).
+-define(IS_SET(S), record(S, ?TAG)).
+
+%% Ordered sets and atoms:
+-define(ORDDATA(S), (S)#?ORDTAG.orddata).
+-define(ORDTYPE(S), (S)#?ORDTAG.ordtype).
+-define(ORDSET(L, T), #?ORDTAG{orddata = L, ordtype = T}).
+-define(IS_ORDSET(S), record(S, ?ORDTAG)).
+
+%% When IS_SET is true:
+-define(ANYTYPE, '_').
+-define(REL_TYPE(I, R), element(I, R)).
+-define(SET_OF(X), [X]).
+
+is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
+ case match_types(?TYPE(S1), ?TYPE(S2)) of
+ true -> ?LIST(S1) == ?LIST(S2);
+ false -> erlang:error(type_mismatch, [S1, S2])
+ end;
+is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_ORDSET(S2) ->
+ case match_types(?TYPE(S1), ?TYPE(S2)) of
+ true -> ?ORDDATA(S1) == ?ORDDATA(S2);
+ false -> erlang:error(type_mismatch, [S1, S2])
+ end;
+is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) ->
+ erlang:error(type_mismatch, [S1, S2]);
+is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) ->
+ erlang:error(type_mismatch, [S1, S2]).
+
+%% Type = OrderedSetType
+%% | SetType
+%% | atom() except '_'
+%% OrderedSetType = {Type, ..., Type}
+%% SetType = [ElementType] % list of exactly one element
+%% ElementType = '_' % any type (implies empty set)
+%% | Type
+
+ordset_of_sets([S | Ss], L, T) when ?IS_SET(S) ->
+ ordset_of_sets(Ss, [?LIST(S) | L], [[?TYPE(S)] | T]);
+ordset_of_sets([S | Ss], L, T) when ?IS_ORDSET(S) ->
+ ordset_of_sets(Ss, [?LIST(S) | L], [?ORDTYPE(S) | T]);
+ordset_of_sets([], L, T) ->
+ ?ORDSET(list_to_tuple(lists:reverse(L)), list_to_tuple(lists:reverse(T)));
+ordset_of_sets(_, _L, _T) ->
+ error.
+
+%% inlined.
+match_types(T, T) -> true;
+match_types(Type1, Type2) -> match_types1(Type1, Type2).
+
+match_types1(Atom, Atom) when is_atom(Atom) ->
+ true;
+match_types1(?ANYTYPE, _) ->
+ true;
+match_types1(_, ?ANYTYPE) ->
+ true;
+match_types1(?SET_OF(Type1), ?SET_OF(Type2)) ->
+ match_types1(Type1, Type2);
+match_types1(T1, T2) when tuple(T1), tuple(T2), size(T1) =:= size(T2) ->
+ match_typesl(size(T1), T1, T2);
+match_types1(_T1, _T2) ->
+ false.
+
+match_typesl(0, _T1, _T2) ->
+ true;
+match_typesl(N, T1, T2) ->
+ case match_types1(?REL_TYPE(N, T1), ?REL_TYPE(N, T2)) of
+ true -> match_typesl(N-1, T1, T2);
+ false -> false
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/no_match.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/no_match.erl
new file mode 100644
index 0000000000..e3e7a4b2d1
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/no_match.erl
@@ -0,0 +1,9 @@
+-module(no_match).
+-export([t1/1, t2/1, t3/1]).
+-record(rec, {field}).
+
+t1(#rec{} = {_}) -> no_match1.
+
+t2(42 = gazonk) -> no_match2.
+
+t3(X) when false -> X.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun.erl
new file mode 100644
index 0000000000..0bd8ba402c
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun.erl
@@ -0,0 +1,20 @@
+-module(no_unused_fun).
+-export([main/2]).
+
+main(X, Bool) ->
+ case Bool of
+ true ->
+ F = fun foo/1;
+ false ->
+ F = fun foobar/1
+ end,
+ calc(X, F).
+
+calc(X, Fun) ->
+ Fun(X).
+
+foo(A) ->
+ A+42.
+
+foobar(A) ->
+ A-42.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun2.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun2.erl
new file mode 100644
index 0000000000..e287c4de5f
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/no_unused_fun2.erl
@@ -0,0 +1,20 @@
+-module(no_unused_fun2).
+-export([main/2]).
+
+main(X, Bool) ->
+ case Bool of
+ true ->
+ F = fun foo/1;
+ false ->
+ F = fun foobar/1
+ end,
+ spawn(fun()->calc(X, F)end).
+
+calc(X, Fun) ->
+ Fun(X).
+
+foo(A) ->
+ A+42.
+
+foobar(A) ->
+ A-42.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/non_existing.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/non_existing.erl
new file mode 100644
index 0000000000..5701b8a745
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/non_existing.erl
@@ -0,0 +1,13 @@
+%%--------------------------------------------------------------------------
+%% Module which contains direct and indirect calls to remote functions
+%% which do not exist. Their treatment should be the same.
+%%--------------------------------------------------------------------------
+-module(non_existing).
+-export([t_call/0, t_fun/0]).
+
+t_call() ->
+ lists:non_existing_call(42).
+
+t_fun() ->
+ Fun = fun lists:non_existing_fun/1,
+ Fun(42).
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/not_guard_crash.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/not_guard_crash.erl
new file mode 100644
index 0000000000..0350864dce
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/not_guard_crash.erl
@@ -0,0 +1,49 @@
+%% From: Matthias Radestock <[email protected]>
+%% Date: 19 August 2007
+%%
+%% when I run dialyzer on my code it throws the following error:
+%%
+%% Analysis failed with error report:
+%% {{case_clause,any},
+%% [{dialyzer_dataflow,bind_guard,5},
+%% {dialyzer_dataflow,bind_guard_case_clauses,6},
+%% {dialyzer_dataflow,bind_guard,5},
+%% {dialyzer_dataflow,bind_guard_case_clauses,6},
+%% {dialyzer_dataflow,bind_guard,5},
+%% {dialyzer_dataflow,bind_eqeq_guard_lit_other,6},
+%% {dialyzer_dataflow,bind_guard,...},
+%% {dialyzer_dataflow,...}]}
+%%
+%% This is happening with the R11B-5 version of dialyzer when
+%% analyzing the attached file.
+%%--------------------------------------------------------------------
+
+-module(not_guard_crash).
+
+-export([match_ticket/2]).
+
+-record(ticket, {passive_flag, active_flag, write_flag, read_flag}).
+
+%%--------------------------------------------------------------------
+
+match_ticket(#ticket{passive_flag = PP,
+ active_flag = PA,
+ write_flag = PW,
+ read_flag = PR},
+ #ticket{passive_flag = TP,
+ active_flag = TA,
+ write_flag = TW,
+ read_flag = TR}) ->
+ if
+ %% Matches if either we're not requesting passive access, or
+ %% passive access is permitted, and ...
+ (not(TP) orelse PP) andalso
+ (not(TA) orelse PA) andalso
+ (not(TW) orelse PW) andalso
+ (not(TR) orelse PR) ->
+ match;
+ true ->
+ no_match
+ end.
+
+%%--------------------------------------------------------------------
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/or_bug.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/or_bug.erl
new file mode 100644
index 0000000000..fb8f6558b8
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/or_bug.erl
@@ -0,0 +1,24 @@
+%%---------------------------------------------------------------------------
+%% From: Per Hedeland <[email protected]>
+%% Date: 11 Feb 2010
+%%
+%% The code below demonstrates a bug in dialyzer - it produces the warning:
+%% Clause guard cannot succeed.
+%% The variable Cs was matched against the type any()
+%% for the first test/1 clause, but of course the claim can easily be easily
+%% refuted by calling test(#cs{}).
+%%---------------------------------------------------------------------------
+
+-module(or_bug).
+
+-export([test/1]).
+
+-record(cs, {children = [], actions = []}).
+
+-define(is_internal(X), ((X#cs.children =/= []) or
+ (X#cs.actions =/= []))).
+-define(has_children(X), (X#cs.children /= [])).
+
+test(Cs) when not ?is_internal(Cs) -> foo;
+test(Cs) when not ?has_children(Cs) -> bar;
+test(Cs) when Cs#cs.children =/= [] -> baz.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug.erl
new file mode 100644
index 0000000000..626f2b7f03
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug.erl
@@ -0,0 +1,17 @@
+%%%-------------------------------------------------------------------
+%%% File : orelsebug.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 14 Nov 2006 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(orelsebug).
+
+-export([t/1, t1/1]).
+
+t(Format) when is_list(Format) ->
+ t1(Format).
+
+t1(Format) when is_list(Format) orelse is_binary(Format) ->
+ Format.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug2.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug2.erl
new file mode 100644
index 0000000000..52b1b3b5a9
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/orelsebug2.erl
@@ -0,0 +1,23 @@
+%%%-------------------------------------------------------------------
+%%% File : orelsebug2.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 21 Nov 2006 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(orelsebug2).
+
+-export([t/1]).
+
+-record(eventdata, {
+ expires
+ }).
+
+t(L) ->
+ L2 = [E1 || E1 <- L, E1#eventdata.expires == x
+ orelse E1#eventdata.expires == y],
+
+ case L2 of
+ [_E] -> x;
+ [] -> y
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/overloaded1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/overloaded1.erl
new file mode 100644
index 0000000000..0af4f7446f
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/overloaded1.erl
@@ -0,0 +1,31 @@
+%%-----------------------------------------------------------------------------
+%% Test that tests overloaded contratcs.
+%% In December 2008 it works as far as intersection types are concerned (test1)
+%% However, it does NOT work as far as type variables are concerned (test2)
+%%-----------------------------------------------------------------------------
+-module(overloaded1).
+-export([test1/0, test2/0, foo/2]).
+
+test1() ->
+ {ok, gazonk} = foo({a,b,1}, atom_to_list(gazonk)),
+ ok.
+
+test2() ->
+ {ok, gazonk} = foo(baz, []),
+ ok.
+
+-type mod() :: atom().
+
+-spec foo(ATM, list()) -> {'ok', ATM} | {'error', _} when is_subtype(ATM, mod())
+ ; (MFA, list()) -> {'ok', MFA} | {'error', _} when is_subtype(MFA, mfa()).
+
+foo(F, _) when is_atom(F) ->
+ case atom_to_list(F) of
+ [42|_] -> {ok, F};
+ _Other -> {error, mod:bar(F)}
+ end;
+foo({M,F,A}, _) ->
+ case A =:= 0 of
+ false -> {ok, {M,F,A}};
+ true -> {error, M}
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/port_info_test.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/port_info_test.erl
new file mode 100644
index 0000000000..d8a5e15caf
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/port_info_test.erl
@@ -0,0 +1,34 @@
+%%
+%% Tests hardcoded dependent type info
+%% and the quality of the warnings that Dialyzer spits out
+%%
+-module(port_info_test).
+-export([t1/1, t2/1, t3/1, t4/1, t5/2, buggy/1]).
+
+%% The following errors are correctly caught, but the messages are a bit weird
+t1(X) when is_port(X) ->
+ {connected, 42} = erlang:port_info(X, connected);
+t1(_) -> ok.
+
+t2(X) when is_port(X) ->
+ {registered_name, "42"} = erlang:port_info(X, registered_name);
+t2(_) -> ok.
+
+%% Here only one od the two errors is reported...
+t3(X) when is_atom(X) ->
+ {output, 42} = erlang:port_info(X, connected);
+t3(_) -> ok.
+
+t4(X) when is_atom(X) ->
+ {Atom, _} = erlang:port_info(X, connected),
+ Atom = links;
+t4(_) -> ok.
+
+t5(X, Atom) when is_port(X) ->
+ {gazonk, _} = erlang:port_info(X, Atom);
+t5(_, _) -> ok.
+
+%% The type system is not strong enough to catch the following errors
+buggy(X) when is_atom(X) ->
+ {links, X} = erlang:port_info(foo, X).
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/process_info_test.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/process_info_test.erl
new file mode 100644
index 0000000000..d098884f4d
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/process_info_test.erl
@@ -0,0 +1,21 @@
+%%
+%% Tests hardcoded dependent type info for process_info/1
+%%
+-module(process_info_test).
+-export([pinfo/1]).
+
+pinfo(P) when node(P) == node() -> % On same node
+ case process_info(P) of
+ undefined ->
+ exit(dead);
+ Info -> Info
+ end;
+pinfo(P) -> % On different node
+ case rpc:call(node(P), erlang, process_info, [P]) of
+ {badrpc, _} ->
+ exit(badrpc);
+ undefined -> % This does happen
+ exit(dead);
+ Info -> Info
+ end.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_api.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_api.erl
new file mode 100644
index 0000000000..c30233b8f5
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_api.erl
@@ -0,0 +1,99 @@
+% Copyright 2007-2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : pubsub_api.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description : Publish API function
+%%%
+%%% Created : 17 Sep 2007 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2007-2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id $
+-module(pubsub_dir.pubsub_api).
+
+-author('[email protected]').
+-vsn('$Id: pubsub_api.erl,v 1.1 2009/11/06 12:39:55 maria Exp $ ').
+
+-export([publish/2, subscribe/2, unsubscribe/2, get_subscribers/1]).
+
+-import(transstore.transaction_api).
+-import(io).
+-import(lists).
+
+%%====================================================================
+%% public functions
+%%====================================================================
+
+%% @doc publishs an event under a given topic.
+%% called e.g. from the java-interface
+%% @spec publish(string(), string()) -> ok
+publish(Topic, Content) ->
+ Subscribers = get_subscribers(Topic),
+ io:format("calling subscribers ~p~n", [Subscribers]),
+ lists:foreach(fun (Subscriber) ->
+ io:format("calling ~p~n", [Subscriber]),
+ pubsub_publish:publish(Subscriber, Topic, Content)
+ end,
+ Subscribers),
+ ok.
+
+%% @doc subscribes a url for a topic.
+%% called e.g. from the java-interface
+%% @spec subscribe(string(), string()) -> ok | {fail, term()}
+subscribe(Topic, URL) ->
+ TFun = fun(TransLog) ->
+ {{Success, _ValueOrReason} = Result, TransLog1} = transaction_api:read(Topic, TransLog),
+ {Result2, TransLog2} = if
+ Success == fail ->
+ transaction_api:write(Topic, [URL], TransLog); %obacht: muss TransLog sein!
+ true ->
+ {value, Subscribers} = Result,
+ transaction_api:write(Topic, [URL | Subscribers], TransLog1)
+ end,
+ if
+ Result2 == ok ->
+ {{ok, ok}, TransLog2};
+ true ->
+ {Result2, TransLog2}
+ end
+ end,
+ transaction_api:do_transaction(TFun, fun (_) -> ok end, fun (X) -> {fail, X} end).
+
+%% @doc unsubscribes a url for a topic.
+-spec(unsubscribe/2 :: (string(), string()) -> ok | {fail, any()}).
+unsubscribe(Topic, URL) ->
+ TFun = fun(TransLog) ->
+ {Subscribers, TransLog1} = transaction_api:read2(TransLog, Topic),
+ case lists:member(URL, Subscribers) of
+ true ->
+ NewSubscribers = lists:delete(URL, Subscribers),
+ TransLog2 = transaction_api:write2(TransLog1, Topic, NewSubscribers),
+ {{ok, ok}, TransLog2};
+ false ->
+ {{fail, not_found}, TransLog}
+ end
+ end,
+ transaction_api:do_transaction(TFun, fun (_) -> ok end, fun (X) -> {fail, X} end).
+
+%% @doc queries the subscribers of a query
+%% @spec get_subscribers(string()) -> [string()]
+get_subscribers(Topic) ->
+ {Fl, _Value} = transaction_api:quorum_read(Topic),
+ if
+ Fl == fail -> %% Fl is either Fail or the Value/Subscribers
+ [];
+ true ->
+ Fl
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_publish.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_publish.erl
new file mode 100644
index 0000000000..97c993e576
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/pubsub/pubsub_publish.erl
@@ -0,0 +1,50 @@
+% Copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%
+% 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.
+%%%-------------------------------------------------------------------
+%%% File : pubsub_publish.erl
+%%% Author : Thorsten Schuett <[email protected]>
+%%% Description : Publish function
+%%%
+%%% Created : 26 Mar 2008 by Thorsten Schuett <[email protected]>
+%%%-------------------------------------------------------------------
+%% @author Thorsten Schuett <[email protected]>
+%% @copyright 2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin
+%% @version $Id $
+-module(pubsub_dir.pubsub_publish).
+
+-author('[email protected]').
+-vsn('$Id: pubsub_publish.erl,v 1.1 2009/11/06 12:39:55 maria Exp $ ').
+
+-export([publish/3, publish_internal/3]).
+
+-import(json).
+-import(io).
+-import(http).
+-import(jsonrpc).
+
+%%====================================================================
+%% public functions
+%%====================================================================
+
+%% @doc publishs an event to a given url.
+%% @spec publish(string(), string(), string()) -> ok
+%% @todo use pool:pspawn
+publish(URL, Topic, Content) ->
+ spawn(fun () -> pubsub_publish:publish_internal(URL, Topic, Content) end),
+ ok.
+
+publish_internal(URL, Topic, Content) ->
+ Res = jsonrpc:call(URL, [], {call, notify, [Topic, Content]}),
+ io:format("~p ~p~n", [Res, URL]).
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/receive1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/receive1.erl
new file mode 100644
index 0000000000..2699a6da51
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/receive1.erl
@@ -0,0 +1,17 @@
+%%%-------------------------------------------------------------------
+%%% File : receive1.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 27 Mar 2007 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(receive1).
+
+-export([t/1]).
+
+t(X) ->
+ receive
+ after
+ infinity -> X
+ end.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/record_construct.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/record_construct.erl
new file mode 100644
index 0000000000..627e23956b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/record_construct.erl
@@ -0,0 +1,22 @@
+-module(record_construct).
+-export([t_loc/0, t_opa/0, t_rem/0]).
+
+-record(r_loc, {a = gazonk :: integer(), b = 42 :: atom()}).
+
+t_loc() ->
+ #r_loc{}.
+
+-record(r_opa, {a :: atom(),
+ b = gb_sets:new() :: gb_set(),
+ c = 42 :: boolean(),
+ d, % untyped on purpose
+ e = false :: boolean()}).
+
+t_opa() ->
+ #r_opa{}.
+
+-record(r_rem, {a = gazonk :: string()}).
+
+t_rem() ->
+ #r_rem{}.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/record_pat.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/record_pat.erl
new file mode 100644
index 0000000000..89228b8357
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/record_pat.erl
@@ -0,0 +1,19 @@
+%%%-------------------------------------------------------------------
+%%% File : record_pat.erl
+%%% Author : Tobias Lindahl <>
+%%% Description : Emit warning if a pattern violates the record type
+%%%
+%%% Created : 21 Oct 2008 by Tobias Lindahl <>
+%%%-------------------------------------------------------------------
+-module(record_pat).
+
+-export([t/1]).
+
+-record(foo, {bar :: integer()}).
+
+t(#foo{bar=baz}) -> no_way;
+t(#foo{bar=1}) -> ok.
+
+
+
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/record_send_test.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/record_send_test.erl
new file mode 100644
index 0000000000..742519e54e
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/record_send_test.erl
@@ -0,0 +1,33 @@
+%%-------------------------------------------------------------------
+%% File : record_send_test.erl
+%% Author : Kostis Sagonas <[email protected]>
+%% Description : A test inspired by a post of Mkcael Remond to the
+%% Erlang mailing list suggesting thst Dialyzer should
+%% be reporting sends to records rather than to pids.
+%% Dialyzer v1.3.0 indeed reports one of the dicrepancies
+%% (the one with the 4-tuple) but not the one where the
+%% message is sent to a pair which is a record.
+%% This should be fixed.
+%%
+%% Created : 10 Apr 2005 by Kostis Sagonas <[email protected]>
+%%-------------------------------------------------------------------
+-module(record_send_test).
+
+-export([t/0]).
+
+-record(rec1, {a=a, b=b, c=c}).
+-record(rec2, {a}).
+
+t() ->
+ t(#rec1{}).
+
+t(Rec1 = #rec1{b=B}) ->
+ Rec2 = some_mod:some_function(),
+ if
+ is_record(Rec2, rec2) ->
+ Rec2 ! hello; %% currently this one is not found
+ true ->
+ Rec1 ! hello_again
+ end,
+ B.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/record_test.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/record_test.erl
new file mode 100644
index 0000000000..8151e595a0
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/record_test.erl
@@ -0,0 +1,24 @@
+%%%-------------------------------------------------------------------
+%%% File : record_test.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description :
+%%%
+%%% Created : 22 Oct 2004 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(record_test).
+
+-export([t/0]).
+
+-record(foo, {bar}).
+
+t() ->
+ doit(foo).
+
+doit(X) ->
+ case X of
+ #foo{} -> error1;
+ foo -> ok;
+ _ -> error2
+ end.
+
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types1.erl
new file mode 100644
index 0000000000..657d11653b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types1.erl
@@ -0,0 +1,10 @@
+-module(recursive_types1).
+
+-export([test/0]).
+
+-type tree() :: 'nil' | {non_neg_integer(), tree(), tree()}.
+
+-spec test() -> {42, tree(), tree()}.
+
+test() ->
+ {42, {42, nil, nil}, {42, {42, nil, nil}, {42, nil, nil}}}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types2.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types2.erl
new file mode 100644
index 0000000000..3a22bbf5d2
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types2.erl
@@ -0,0 +1,12 @@
+-module(recursive_types2).
+
+-export([test/0]).
+
+-type tree() :: 'nil' | {non_neg_integer(), child(), child()}.
+
+-type child() :: tree().
+
+-spec test() -> {42, tree(), tree()}.
+
+test() ->
+ {42, {42, nil, nil}, {42, {42, nil, nil}, {42, nil, nil}}}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types3.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types3.erl
new file mode 100644
index 0000000000..997678ac92
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types3.erl
@@ -0,0 +1,15 @@
+-module(recursive_types3).
+
+-export([test/1]).
+
+-record(tree, {node :: atom(),
+ kid = nil :: 'nil' | tree()}).
+
+-type tree() :: #tree{}.
+
+-spec test(tree()) -> tree().
+
+test(Tree) ->
+ case Tree of
+ #tree{node = root, kid=#tree{}} -> Tree
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types4.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types4.erl
new file mode 100644
index 0000000000..118bab57a1
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types4.erl
@@ -0,0 +1,13 @@
+-module(recursive_types4).
+
+-export([test/0]).
+
+-record(tree, {node :: atom(),
+ kid = nil :: 'nil' | tree()}).
+
+-type tree() :: #tree{}.
+
+-spec test() -> tree().
+
+test() ->
+ #tree{node = root, kid = #tree{}}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types5.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types5.erl
new file mode 100644
index 0000000000..a71e613cf0
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types5.erl
@@ -0,0 +1,13 @@
+-module(recursive_types5).
+
+-export([test/0]).
+
+-type tree() :: 'nil' | {non_neg_integer(), tree(), tree()}.
+
+-record(tree, {node :: atom(),
+ kid = 'nil' :: tree()}).
+
+-spec test() -> #tree{}.
+
+test() ->
+ #tree{node = root, kid = {42, {42, nil, nil}, {42, nil, nil}}}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types6.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types6.erl
new file mode 100644
index 0000000000..ff61976736
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types6.erl
@@ -0,0 +1,17 @@
+-module(recursive_types6).
+
+-export([test/0]).
+
+-record(tree, {node :: non_neg_integer(),
+ kid = nil :: child()}).
+
+-type tree() :: #tree{}.
+
+-record(child, {tree :: 'nil' | tree()}).
+
+-type child() :: #child{}.
+
+-spec test() -> tree().
+
+test() ->
+ #tree{node = 42, kid = #child{tree = #tree{node = 42, kid = #child{tree = nil}}}}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types7.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types7.erl
new file mode 100644
index 0000000000..92106e9694
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/recursive_types7.erl
@@ -0,0 +1,13 @@
+-module(recursive_types7).
+
+-export([test/0]).
+
+-type tree() :: 'nil' | {non_neg_integer(), recursive_types7:tree(),
+ recursive_types7:tree()}.
+
+-export_type([tree/0]).
+
+-spec test() -> {42, tree(), tree()}.
+
+test() ->
+ {42, {42, nil, nil}, {42, {42, nil, nil}, {42, nil, nil}}}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/refine_bug1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/refine_bug1.erl
new file mode 100644
index 0000000000..1b299e782a
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/refine_bug1.erl
@@ -0,0 +1,11 @@
+-module(refine_bug1).
+-export([f/1]).
+
+f(gazonk = X) ->
+ foo(X), % this call is currently not considered when refining foo's
+ throw(error); % type since it appears in a clause that throws an exception
+f(foo = X) ->
+ foo(X).
+
+foo(X) ->
+ X.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/toth.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/toth.erl
new file mode 100644
index 0000000000..bd7fa4982e
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/toth.erl
@@ -0,0 +1,99 @@
+-module(toth).
+-export([sys_table_view/1]).
+
+%%% Constants
+-define(sysTabETS,1).
+-define(sysTabMnesia,2).
+-define(sysTabBoth,3).
+
+sys_table_view([CpId,{match,Pattern},TableType, ViewType]) ->
+ AllTableList =
+ case TableType of
+ ?sysTabMnesia ->
+ lists:sort(mnesia:system_info(tables));
+ ?sysTabBoth ->
+ lists:sort(rpc:call(CpId,ets,all,[]));
+ ?sysTabETS ->
+ lists:sort(rpc:call(CpId,ets,all,[]) --
+ mnesia:system_info(tables));
+ _ -> %%% Happens at registration only
+ [ok]
+ end,
+ %% Filter the matching table names, skip unnamed tables first:
+ NamedTableList = lists:filter(fun (X) -> is_atom(X) end, AllTableList),
+ TablesShown =
+ case Pattern of
+ "" ->
+ NamedTableList;
+ _ ->
+ %% Filter the ones whose name begins with the Pattern:
+ Filter = fun(T) ->
+ lists:prefix(Pattern, atom_to_list(T))
+ end,
+ lists:filter(Filter, NamedTableList)
+ end,
+
+ Fields = [{text, [{value,"CpId: " ++ atom_to_list(CpId)}]},
+ {text, [{value,"TabSpec=" ++ Pattern},
+ {value_format, term}]},
+ {text, [{value,"Table type: " ++ formatTableType(TableType)},
+ {value_format, term}]}],
+
+ Template = [[{type, index},
+ {link, {?MODULE, sys_table_browse,
+ [{"CpId",CpId},{"TableType",TableType},
+ {"View", ViewType},
+ {"FirstKey",1}, {"KeyPattern",""}]}}],
+
+ [{type, data},
+ {title, "Table name"},
+ {display_value, {erlang, atom_to_list}}], %%% else crash
+
+ [{type,data},
+ {title, "No of rows"},
+ {display_value, term}],
+
+ [{type,data},
+ {title, "Memory"},
+ {display_value, term}]
+ ],
+
+ TableAttr = [{rows, [[T,T|tableSize(T,TableType,CpId)] ||
+ T <- TablesShown]},
+ {template,Template}],
+
+ Page = [{header, {"Filter tables", "Selected tables"}},
+ {buttons, [reload, back]},
+ {layout, [{form, Fields},
+ {table, TableAttr}]}
+ ],
+ Page.
+
+%%--------------------------------------------------------------------
+%% tableSize/3
+%% @spec tableSize(T::atom(),TableType::integer(),CpId::atom()) ->
+%% list(integer())
+%% @doc Return the table size and memory size of the table.
+%% @end
+%%---------------------------------------------------------------------
+
+tableSize(T, TableType, CpId) ->
+ case TableType of
+ ?sysTabETS ->
+ [rpc:call(CpId, ets, info, [T, size]),
+ rpc:call(CpId, ets, info, [T, memory])];
+ ?sysTabMnesia ->
+ [mnesia:table_info(T, size),mnesia:table_info(T, memory)];
+ _ -> %%% Registration
+ [0,0]
+ end.
+
+formatTableType(T) ->
+ case T of
+ ?sysTabETS ->
+ "ETS";
+ ?sysTabMnesia ->
+ "mnesia";
+ _ -> %%% Registration !
+ "ETS + mnesia"
+ end.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/trec.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/trec.erl
new file mode 100644
index 0000000000..b36b0cafba
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/trec.erl
@@ -0,0 +1,37 @@
+%%
+%% The current treatment of typed records leaves much to be desired.
+%% These are not made up examples; I have cases like that the branch
+%% of the HiPE compiler with types in records. I get very confusing
+%% warnings which require a lot of effort to find their cause and why
+%% a function has no local return.
+%%
+-module(trec).
+-export([test/0, mk_foo_exp/2]).
+
+-record(foo, {a :: integer(), b :: [atom()]}).
+
+%%
+%% For these functions we currently get the following warnings:
+%% 1. Function test/0 has no local return
+%% 2. The call trec:mk_foo_loc(42,any()) will fail since it differs
+%% in argument position 1 from the success typing arguments:
+%% ('undefined',atom())
+%% 3. Function mk_foo_loc/2 has no local return
+%%
+%% Arguably, the second warning is not what most users have in mind
+%% when they wrote the type declarations in the 'foo' record, so no
+%% doubt they'll find it confusing. But note that it is also inconsistent!
+%% How come there is a success typing for a function that has no local return?
+%%
+test() ->
+ mk_foo_loc(42, bar:f()).
+
+mk_foo_loc(A, B) ->
+ #foo{a = A, b = [A,B]}.
+
+%%
+%% For this function we currently get "has no local return" but we get
+%% no reason; I want us to get a reason.
+%%
+mk_foo_exp(A, B) when is_integer(A) ->
+ #foo{a = A, b = [A,B]}.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/try1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/try1.erl
new file mode 100644
index 0000000000..d07380295b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/try1.erl
@@ -0,0 +1,27 @@
+%%%-------------------------------------------------------------------
+%%% File : try1.erl
+%%% Author : <[email protected]>
+%%% Description :
+%%%
+%%% Created : 23 Aug 2005 by <[email protected]>
+%%%-------------------------------------------------------------------
+-module(try1).
+
+-export([t/1]).
+
+t(X) ->
+ case wierd_is_bool(X) of
+ true -> ok;
+ false -> ok
+ end.
+
+wierd_is_bool(X) ->
+ try bool(X) of
+ Y -> Y
+ catch
+ _:_ -> false
+ end.
+
+bool(true) -> true;
+bool(false) -> true.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/tuple1.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/tuple1.erl
new file mode 100644
index 0000000000..c58aac9646
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/tuple1.erl
@@ -0,0 +1,29 @@
+%%%-------------------------------------------------------------------
+%%% File : tuple1.erl
+%%% Author : Tobias Lindahl <[email protected]>
+%%% Description : Exposed two bugs in the analysis;
+%%% one supressed warning and one crash.
+%%%
+%%% Created : 13 Nov 2006 by Tobias Lindahl <[email protected]>
+%%%-------------------------------------------------------------------
+-module(tuple1).
+
+-export([t1/2, t2/2, t3/2, bar/2]).
+
+t1(List = [_|_], X) ->
+ lists:mapfoldl(fun foo/2, X, List).
+
+t2(List = [_|_], X) ->
+ lists:mapfoldl(fun bar/2, X, List).
+
+t3(List = [_|_], X) ->
+ lists:mapfoldl(fun baz/1, X, List).
+
+
+foo(1, 1) -> a;
+foo(a, 1) -> b.
+
+bar(1, 1) -> {b, b};
+bar(a, 1) -> {a, a}.
+
+baz(1) -> 1.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/unsafe_beamcode_bug.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/unsafe_beamcode_bug.erl
new file mode 100644
index 0000000000..889f94014e
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/unsafe_beamcode_bug.erl
@@ -0,0 +1,15 @@
+-module(unsafe_beamcode_bug).
+-export([test/1]).
+
+test(N) -> i(r(N)).
+
+%% this function cannot be exported, or the error does not occur
+i({one}) -> ok1;
+i({two, _}) -> ok2;
+i({three, {_,R}, _}) -> R.
+
+r(1) -> {one};
+r(2) -> {two, 2};
+r(42)-> {dummy, 42}; % without this clause, no problem ... hmm
+r(3) -> {three, {rec,ok3}, 2}.
+
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/unused_cases.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/unused_cases.erl
new file mode 100644
index 0000000000..e6e6693963
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/unused_cases.erl
@@ -0,0 +1,41 @@
+%%-------------------------------------------------------------------
+%% File : unused_cases.erl
+%% Author : Kostis Sagonas <[email protected]>
+%% Description : Tests that Dialyzer warns whenever it finds unused
+%% case clauses -- even those that are catch all.
+%%
+%% Created : 21 Jan 2007 by Kostis Sagonas <[email protected]>
+%%-------------------------------------------------------------------
+
+-module(unused_cases).
+-export([test/0]).
+
+test() -> % dummy function to avoid exporting stuff
+ ok = unreachable_catchall(42),
+ ok = unreachable_middle(42),
+ ok = unreachable_final(42).
+
+unreachable_catchall(X) ->
+ case mk_pair(X) of
+ {_,_} -> ok;
+ OTHER -> {unreachable_catchall, OTHER}
+ end.
+
+unreachable_middle(X) ->
+ case is_positive(X) of
+ true -> ok;
+ weird -> {unreachable_middle, weird};
+ false -> ok
+ end.
+
+unreachable_final(X) ->
+ case is_positive(X) of
+ true -> ok;
+ false -> ok;
+ OTHER-> {unreachable_final, OTHER}
+ end.
+
+mk_pair(X) -> {X, X}.
+
+is_positive(X) when is_integer(X), X > 0 -> true;
+is_positive(X) when is_integer(X) -> false.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/unused_clauses.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/unused_clauses.erl
new file mode 100644
index 0000000000..a98b227a6b
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/unused_clauses.erl
@@ -0,0 +1,18 @@
+%%-------------------------------------------------------------------
+%% File : unused_clauses.erl
+%% Author : Kostis Sagonas <[email protected]>
+%% Description : Tests that Dialyzer warns when it finds an unused
+%% clause.
+%%
+%% Created : 16 Mar 2006 by Kostis Sagonas <[email protected]>
+%%-------------------------------------------------------------------
+
+-module(unused_clauses).
+-export([test/0]).
+
+test() -> {t(atom), t({42})}.
+
+t(X) when is_atom(X) -> X;
+t(X) when is_integer(X) -> X;
+t(X) when is_tuple(X) -> element(1, X);
+t(X) when is_binary(X) -> X.
diff --git a/lib/dialyzer/test/small_tests_SUITE_data/src/zero_tuple.erl b/lib/dialyzer/test/small_tests_SUITE_data/src/zero_tuple.erl
new file mode 100644
index 0000000000..90dc366fe7
--- /dev/null
+++ b/lib/dialyzer/test/small_tests_SUITE_data/src/zero_tuple.erl
@@ -0,0 +1,13 @@
+-module(zero_tuple).
+-export([t1/0, t2/0]).
+
+t1() ->
+ {} = a(),
+ ok.
+
+t2() ->
+ b = a(),
+ ok.
+
+a() -> a.
+
diff --git a/lib/dialyzer/test/user_tests_SUITE.erl b/lib/dialyzer/test/user_tests_SUITE.erl
new file mode 100644
index 0000000000..9654114725
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE.erl
@@ -0,0 +1,78 @@
+%% ATTENTION!
+%% This is an automatically generated file. Do not edit.
+%% Use './remake' script to refresh it if needed.
+%% All Dialyzer options should be defined in dialyzer_options
+%% file.
+
+-module(user_tests_SUITE).
+
+-include("ct.hrl").
+-include("dialyzer_test_constants.hrl").
+
+-export([suite/0, init_per_suite/0, init_per_suite/1,
+ end_per_suite/1, all/0]).
+-export([user_tests_SUITE_consistency/1, broken_dialyzer/1,
+ gcpFlowControl/1, qlc_error/1, spvcOrig/1, wsp_pdu/1]).
+
+suite() ->
+ [{timetrap, {minutes, 3}}].
+
+init_per_suite() ->
+ [{timetrap, ?plt_timeout}].
+init_per_suite(Config) ->
+ OutDir = ?config(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, []}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+all() ->
+ [user_tests_SUITE_consistency,broken_dialyzer,gcpFlowControl,qlc_error,
+ spvcOrig,wsp_pdu].
+
+dialyze(Config, TestCase) ->
+ Opts = ?config(dialyzer_options, Config),
+ Dir = ?config(data_dir, Config),
+ OutDir = ?config(priv_dir, Config),
+ dialyzer_common:check(TestCase, Opts, Dir, OutDir).
+
+user_tests_SUITE_consistency(Config) ->
+ Dir = ?config(data_dir, Config),
+ case dialyzer_common:new_tests(Dir, all()) of
+ [] -> ok;
+ New -> ct:fail({missing_tests,New})
+ end.
+
+broken_dialyzer(Config) ->
+ case dialyze(Config, broken_dialyzer) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+gcpFlowControl(Config) ->
+ case dialyze(Config, gcpFlowControl) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+qlc_error(Config) ->
+ case dialyze(Config, qlc_error) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+spvcOrig(Config) ->
+ case dialyze(Config, spvcOrig) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
+wsp_pdu(Config) ->
+ case dialyze(Config, wsp_pdu) of
+ 'same' -> 'same';
+ Error -> ct:fail(Error)
+ end.
+
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/dialyzer_options b/lib/dialyzer/test/user_tests_SUITE_data/dialyzer_options
new file mode 100644
index 0000000000..513ed7752b
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/dialyzer_options
@@ -0,0 +1,2 @@
+{dialyzer_options, []}.
+{time_limit, 3}. \ No newline at end of file
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/results/broken_dialyzer b/lib/dialyzer/test/user_tests_SUITE_data/results/broken_dialyzer
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/results/broken_dialyzer
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/results/gcpFlowControl b/lib/dialyzer/test/user_tests_SUITE_data/results/gcpFlowControl
new file mode 100644
index 0000000000..7938c53fc6
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/results/gcpFlowControl
@@ -0,0 +1,2 @@
+
+gcpFlowControl.erl:171: The pattern <Key, 'errors', X> can never match the type <_,'available' | 'bucket' | 'rejectable' | 'rejects' | 'window',0 | 1 | 20>
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/results/qlc_error b/lib/dialyzer/test/user_tests_SUITE_data/results/qlc_error
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/results/qlc_error
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/results/spvcOrig b/lib/dialyzer/test/user_tests_SUITE_data/results/spvcOrig
new file mode 100644
index 0000000000..8c57358af0
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/results/spvcOrig
@@ -0,0 +1,193 @@
+
+spvcOrig.erl:1238: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVcc',25) can never succeed
+spvcOrig.erl:1241: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVpc',20) can never succeed
+spvcOrig.erl:1244: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVpcPerm',12) can never succeed
+spvcOrig.erl:1247: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVccPerm',17) can never succeed
+spvcOrig.erl:1250: Guard test is_record(Spvc::[] | #spvcObj{},'spvcTargetVc',6) can never succeed
+spvcOrig.erl:1253: Guard test is_record(Spvc::[] | #spvcObj{},'spvcTargetVp',6) can never succeed
+spvcOrig.erl:1256: Guard test is_record(Spvc::[] | #spvcObj{},'pchVc',32) can never succeed
+spvcOrig.erl:1259: Guard test is_record(Spvc::[] | #spvcObj{},'pchVp',33) can never succeed
+spvcOrig.erl:1262: Guard test is_record(Spvc::[] | #spvcObj{},'spvcFr',21) can never succeed
+spvcOrig.erl:1265: Guard test is_record(Spvc::[] | #spvcObj{},'spvcFrPerm',6) can never succeed
+spvcOrig.erl:1268: The pattern {If_Value, _, _, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:1270: The pattern {If_Value, _, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:1272: The pattern {If_Value, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:1274: The pattern [If_Value | _] can never match the type [] | #spvcObj{}
+spvcOrig.erl:1380: The variable _ can never match since previous clauses completely covered the type any()
+spvcOrig.erl:1389: The variable _ can never match since previous clauses completely covered the type any()
+spvcOrig.erl:1576: Guard test is_record(Row::[any(),...],'spvcObj',24) can never succeed
+spvcOrig.erl:1583: Guard test is_record(Row::[any(),...],'spvcVcc',25) can never succeed
+spvcOrig.erl:1586: Guard test is_record(Row::[any(),...],'spvcVpc',20) can never succeed
+spvcOrig.erl:1589: Guard test is_record(Row::[any(),...],'spvcVpcPerm',12) can never succeed
+spvcOrig.erl:1592: Guard test is_record(Row::[any(),...],'spvcVccPerm',17) can never succeed
+spvcOrig.erl:1595: Guard test is_record(Row::[any(),...],'spvcTargetVc',6) can never succeed
+spvcOrig.erl:1598: Guard test is_record(Row::[any(),...],'spvcTargetVp',6) can never succeed
+spvcOrig.erl:1601: Guard test is_record(Row::[any(),...],'pchVc',32) can never succeed
+spvcOrig.erl:1604: Guard test is_record(Row::[any(),...],'pchVp',33) can never succeed
+spvcOrig.erl:1607: Guard test is_record(Row::[any(),...],'spvcFr',21) can never succeed
+spvcOrig.erl:1610: Guard test is_record(Row::[any(),...],'spvcFrPerm',6) can never succeed
+spvcOrig.erl:1613: The pattern {If_Value, _, _, _} can never match the type [any(),...]
+spvcOrig.erl:1615: The pattern {If_Value, _, _} can never match the type [any(),...]
+spvcOrig.erl:1617: The pattern {If_Value, _} can never match the type [any(),...]
+spvcOrig.erl:1621: The variable _ can never match since previous clauses completely covered the type [any(),...]
+spvcOrig.erl:1731: The pattern [_, _, _, _] can never match the type tuple()
+spvcOrig.erl:1733: The pattern [_, _, _] can never match the type tuple()
+spvcOrig.erl:1735: The pattern [_, _] can never match the type tuple()
+spvcOrig.erl:264: The pattern {If_Value, Vpi_Value} can never match the type {_,_,_}
+spvcOrig.erl:271: Guard test is_integer(Vci_Value::'no_vc') can never succeed
+spvcOrig.erl:275: The pattern {If_Value, Vpi_Value} can never match the type {_,_,'no_vc'}
+spvcOrig.erl:305: The pattern {'spvcVcc', 'targetAddress'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:307: The pattern {'spvcVcc', 'selectType'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:309: The pattern {'spvcVcc', 'targetVpi'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:311: The pattern {'spvcVcc', 'targetVci'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:313: The pattern {'spvcVcc', 'releaseCause'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:315: The pattern {'spvcVcc', 'releaseDiagnostic'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:317: The pattern {'spvcVcc', 'retryInterval'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:319: The pattern {'spvcVcc', 'retryTimer'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:321: The pattern {'spvcVcc', 'retryThreshold'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:323: The pattern {'spvcVcc', 'retryFailures'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:325: The pattern {'spvcVcc', 'retryLimit'} can never match the type {'spvcFr','rowStatus'} | {'spvcVcc','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:329: The pattern {'spvcVcc', 'restart'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:331: The pattern {'spvcVcc', 'targetSelectType_any'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:333: The pattern {'spvcVcc', 'targetSelectType_required'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:335: The pattern {'spvcVpc', 'targetAddress'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:337: The pattern {'spvcVpc', 'selectType'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:339: The pattern {'spvcVpc', 'targetVpi'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:341: The pattern {'spvcVpc', 'releaseCause'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:343: The pattern {'spvcVpc', 'releaseDiagnostic'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:345: The pattern {'spvcVpc', 'retryInterval'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:347: The pattern {'spvcVpc', 'retryTimer'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:349: The pattern {'spvcVpc', 'retryThreshold'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:351: The pattern {'spvcVpc', 'retryFailures'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:353: The pattern {'spvcVpc', 'retryLimit'} can never match the type {'spvcFr','rowStatus'} | {'spvcVpc','rowStatus'}
+spvcOrig.erl:357: The pattern {'spvcVpc', 'restart'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:359: The pattern {'spvcVpc', 'targetSelectType_any'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:361: The pattern {'spvcVpc', 'targetSelectType_required'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:363: The pattern {'spvcFr', 'targetAddress'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:365: The pattern {'spvcFr', 'selectType'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:367: The pattern {'spvcFr', 'identifier'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:369: The pattern {'spvcFr', 'targetVpi'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:371: The pattern {'spvcFr', 'targetVci'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:373: The pattern {'spvcFr', 'translation'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:375: The pattern {'spvcFr', 'releaseCause'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:377: The pattern {'spvcFr', 'releaseDiagnostic'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:379: The pattern {'spvcFr', 'operStatus'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:381: The pattern {'spvcFr', 'adminStatus'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:383: The pattern {'spvcFr', 'restart'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:385: The pattern {'spvcFr', 'retryInterval'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:387: The pattern {'spvcFr', 'retryTimer'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:389: The pattern {'spvcFr', 'retryThreshold'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:391: The pattern {'spvcFr', 'retryFailures'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:393: The pattern {'spvcFr', 'retryLimit'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:395: The pattern {'spvcFr', 'lastChange'} can never match the type {'spvcFr','rowStatus'}
+spvcOrig.erl:404: Guard test is_record(Row::[any(),...],'spvcObj',24) can never succeed
+spvcOrig.erl:411: Guard test is_record(Row::[any(),...],'spvcVcc',25) can never succeed
+spvcOrig.erl:414: Guard test is_record(Row::[any(),...],'spvcVpc',20) can never succeed
+spvcOrig.erl:417: Guard test is_record(Row::[any(),...],'spvcVpcPerm',12) can never succeed
+spvcOrig.erl:420: Guard test is_record(Row::[any(),...],'spvcVccPerm',17) can never succeed
+spvcOrig.erl:423: Guard test is_record(Row::[any(),...],'spvcTargetVc',6) can never succeed
+spvcOrig.erl:426: Guard test is_record(Row::[any(),...],'spvcTargetVp',6) can never succeed
+spvcOrig.erl:429: Guard test is_record(Row::[any(),...],'pchVc',32) can never succeed
+spvcOrig.erl:432: Guard test is_record(Row::[any(),...],'pchVp',33) can never succeed
+spvcOrig.erl:435: Guard test is_record(Row::[any(),...],'spvcFr',21) can never succeed
+spvcOrig.erl:438: Guard test is_record(Row::[any(),...],'spvcFrPerm',6) can never succeed
+spvcOrig.erl:441: The pattern {If_Value, _, _, _} can never match the type [any(),...]
+spvcOrig.erl:443: The pattern {If_Value, _, _} can never match the type [any(),...]
+spvcOrig.erl:445: The pattern {If_Value, _} can never match the type [any(),...]
+spvcOrig.erl:449: The variable _ can never match since previous clauses completely covered the type [any(),...]
+spvcOrig.erl:468: Guard test is_record(Row::[any(),...],'spvcObj',24) can never succeed
+spvcOrig.erl:475: Guard test is_record(Row::[any(),...],'spvcVcc',25) can never succeed
+spvcOrig.erl:478: Guard test is_record(Row::[any(),...],'spvcVpc',20) can never succeed
+spvcOrig.erl:481: Guard test is_record(Row::[any(),...],'spvcVpcPerm',12) can never succeed
+spvcOrig.erl:484: Guard test is_record(Row::[any(),...],'spvcVccPerm',17) can never succeed
+spvcOrig.erl:487: Guard test is_record(Row::[any(),...],'spvcTargetVc',6) can never succeed
+spvcOrig.erl:490: Guard test is_record(Row::[any(),...],'spvcTargetVp',6) can never succeed
+spvcOrig.erl:493: Guard test is_record(Row::[any(),...],'pchVc',32) can never succeed
+spvcOrig.erl:496: Guard test is_record(Row::[any(),...],'pchVp',33) can never succeed
+spvcOrig.erl:499: Guard test is_record(Row::[any(),...],'spvcFr',21) can never succeed
+spvcOrig.erl:502: Guard test is_record(Row::[any(),...],'spvcFrPerm',6) can never succeed
+spvcOrig.erl:505: The pattern {If_Value, _, _, _} can never match the type [any(),...]
+spvcOrig.erl:507: The pattern {If_Value, _, _} can never match the type [any(),...]
+spvcOrig.erl:509: The pattern {If_Value, _} can never match the type [any(),...]
+spvcOrig.erl:513: The variable _ can never match since previous clauses completely covered the type [any(),...]
+spvcOrig.erl:546: The pattern {_, _, _, _} can never match the type [any(),...]
+spvcOrig.erl:548: The pattern {_, _, _} can never match the type [any(),...]
+spvcOrig.erl:550: The pattern {_, _} can never match the type [any(),...]
+spvcOrig.erl:559: The pattern {'spvcVcc', 'targetAddress'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:561: The pattern {'spvcVcc', 'selectType'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:563: The pattern {'spvcVcc', 'targetVpi'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:565: The pattern {'spvcVcc', 'targetVci'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:567: The pattern {'spvcVcc', 'releaseCause'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:569: The pattern {'spvcVcc', 'releaseDiagnostic'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:571: The pattern {'spvcVcc', 'retryInterval'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:573: The pattern {'spvcVcc', 'retryTimer'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:575: The pattern {'spvcVcc', 'retryThreshold'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:577: The pattern {'spvcVcc', 'retryFailures'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:579: The pattern {'spvcVcc', 'retryLimit'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:581: The pattern {'spvcVcc', 'rowStatus'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:585: The pattern {'spvcVcc', 'targetSelectType_any'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:587: The pattern {'spvcVcc', 'targetSelectType_required'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:589: The pattern {'spvcVpc', 'targetAddress'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:591: The pattern {'spvcVpc', 'selectType'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:593: The pattern {'spvcVpc', 'targetVpi'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:595: The pattern {'spvcVpc', 'releaseCause'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:597: The pattern {'spvcVpc', 'releaseDiagnostic'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:599: The pattern {'spvcVpc', 'retryInterval'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:601: The pattern {'spvcVpc', 'retryTimer'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:603: The pattern {'spvcVpc', 'retryThreshold'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:605: The pattern {'spvcVpc', 'retryFailures'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:607: The pattern {'spvcVpc', 'retryLimit'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:609: The pattern {'spvcVpc', 'rowStatus'} can never match the type {'spvcFr','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:613: The pattern {'spvcVpc', 'targetSelectType_any'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:615: The pattern {'spvcVpc', 'targetSelectType_required'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:617: The pattern {'spvcFr', 'targetAddress'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:619: The pattern {'spvcFr', 'selectType'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:621: The pattern {'spvcFr', 'identifier'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:623: The pattern {'spvcFr', 'targetVpi'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:625: The pattern {'spvcFr', 'targetVci'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:627: The pattern {'spvcFr', 'translation'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:629: The pattern {'spvcFr', 'releaseCause'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:631: The pattern {'spvcFr', 'releaseDiagnostic'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:633: The pattern {'spvcFr', 'operStatus'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:635: The pattern {'spvcFr', 'adminStatus'} can never match the type {'spvcFr','restart'}
+spvcOrig.erl:639: The pattern {'spvcFr', 'retryInterval'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:641: The pattern {'spvcFr', 'retryTimer'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:643: The pattern {'spvcFr', 'retryThreshold'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:645: The pattern {'spvcFr', 'retryFailures'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:647: The pattern {'spvcFr', 'retryLimit'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:649: The pattern {'spvcFr', 'lastChange'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:651: The pattern {'spvcFr', 'rowStatus'} can never match the type {'spvcFr','restart'} | {'spvcVcc','restart'} | {'spvcVpc','restart'}
+spvcOrig.erl:730: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVcc',25) can never succeed
+spvcOrig.erl:733: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVpc',20) can never succeed
+spvcOrig.erl:736: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVpcPerm',12) can never succeed
+spvcOrig.erl:739: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVccPerm',17) can never succeed
+spvcOrig.erl:742: Guard test is_record(Spvc::[] | #spvcObj{},'spvcTargetVc',6) can never succeed
+spvcOrig.erl:745: Guard test is_record(Spvc::[] | #spvcObj{},'spvcTargetVp',6) can never succeed
+spvcOrig.erl:748: Guard test is_record(Spvc::[] | #spvcObj{},'pchVc',32) can never succeed
+spvcOrig.erl:751: Guard test is_record(Spvc::[] | #spvcObj{},'pchVp',33) can never succeed
+spvcOrig.erl:754: Guard test is_record(Spvc::[] | #spvcObj{},'spvcFr',21) can never succeed
+spvcOrig.erl:757: Guard test is_record(Spvc::[] | #spvcObj{},'spvcFrPerm',6) can never succeed
+spvcOrig.erl:760: The pattern {If_Value, _, _, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:762: The pattern {If_Value, _, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:764: The pattern {If_Value, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:766: The pattern [If_Value | _] can never match the type [] | #spvcObj{}
+spvcOrig.erl:802: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVcc',25) can never succeed
+spvcOrig.erl:805: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVpc',20) can never succeed
+spvcOrig.erl:808: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVpcPerm',12) can never succeed
+spvcOrig.erl:811: Guard test is_record(Spvc::[] | #spvcObj{},'spvcVccPerm',17) can never succeed
+spvcOrig.erl:814: Guard test is_record(Spvc::[] | #spvcObj{},'spvcTargetVc',6) can never succeed
+spvcOrig.erl:817: Guard test is_record(Spvc::[] | #spvcObj{},'spvcTargetVp',6) can never succeed
+spvcOrig.erl:820: Guard test is_record(Spvc::[] | #spvcObj{},'pchVc',32) can never succeed
+spvcOrig.erl:823: Guard test is_record(Spvc::[] | #spvcObj{},'pchVp',33) can never succeed
+spvcOrig.erl:826: Guard test is_record(Spvc::[] | #spvcObj{},'spvcFr',21) can never succeed
+spvcOrig.erl:829: Guard test is_record(Spvc::[] | #spvcObj{},'spvcFrPerm',6) can never succeed
+spvcOrig.erl:832: The pattern {If_Value, _, _, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:834: The pattern {If_Value, _, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:836: The pattern {If_Value, _} can never match the type [] | #spvcObj{}
+spvcOrig.erl:838: The pattern [If_Value | _] can never match the type [] | #spvcObj{}
+spvcOrig.erl:951: The pattern [IfIndex_Value, Vpi_Value, Vci_Value, _] can never match the type tuple()
+spvcOrig.erl:953: The pattern [IfIndex_Value, Vpi_Value, _] can never match the type tuple()
+spvcOrig.erl:974: The pattern [IfIndex_Value, Vpi_Value, Vci_Value, _] can never match the type tuple()
+spvcOrig.erl:976: The pattern [IfIndex_Value, Vpi_Value, _] can never match the type tuple()
+spvcOrig.erl:996: The pattern [IfIndex_Value, Vpi_Value, Vci_Value, _] can never match the type tuple()
+spvcOrig.erl:998: The pattern [IfIndex_Value, Vpi_Value, _] can never match the type tuple()
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/results/wsp_pdu b/lib/dialyzer/test/user_tests_SUITE_data/results/wsp_pdu
new file mode 100644
index 0000000000..a47b1f1f2c
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/results/wsp_pdu
@@ -0,0 +1,25 @@
+
+wsp_pdu.erl:1063: The pattern [H | Hs] can never match the type []
+wsp_pdu.erl:1162: The call wsp_pdu:parse_push_flag(Value::[any()]) will never return since it differs in the 1st argument from the success typing arguments: (integer())
+wsp_pdu.erl:2400: Function decode_retry_after/2 has no local return
+wsp_pdu.erl:2403: The call wsp_pdu:d_date(Data1::binary()) will never return since it differs in the 1st argument from the success typing arguments: (integer() | {'short',binary()})
+wsp_pdu.erl:2406: Guard test is_integer(Sec::{[byte()] | byte() | {'long',binary()} | {'short',binary()},binary()}) can never succeed
+wsp_pdu.erl:2408: The pattern {'short', Data2} can never match the type {[byte()] | byte() | {'long',binary()} | {'short',binary()},binary()}
+wsp_pdu.erl:2755: Function parse_push_flag/1 has no local return
+wsp_pdu.erl:2756: The call erlang:integer_to_list(Value::[any()]) will never return since it differs in the 1st argument from the success typing arguments: (integer())
+wsp_pdu.erl:2875: The call wsp_pdu:d_text_string(Data::byte()) will never return since it differs in the 1st argument from the success typing arguments: (binary())
+wsp_pdu.erl:2976: The call wsp_pdu:d_q_value(QData::byte()) will never return since it differs in the 1st argument from the success typing arguments: (<<_:8,_:_*8>>)
+wsp_pdu.erl:3336: The call wsp_pdu:encode_typed_field(Ver::any(),'Q-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
+wsp_pdu.erl:3342: The call wsp_pdu:encode_typed_field(Ver::any(),'Ver-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
+wsp_pdu.erl:3349: The call wsp_pdu:encode_typed_field(Ver::any(),'Integer-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
+wsp_pdu.erl:3367: The call wsp_pdu:encode_typed_field(Ver::any(),'Field-name',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
+wsp_pdu.erl:3405: The call wsp_pdu:encode_typed_field(Ver::any(),'Delta-seconds-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
+wsp_pdu.erl:3437: The call wsp_pdu:encode_typed_field(Ver::any(),'Integer-value',ParamValue::any()) will never return since it differs in the 2nd argument from the success typing arguments: (any(),'Constrained-encoding' | 'Date-value' | 'No-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',any())
+wsp_pdu.erl:3455: The call wsp_pdu:decode_typed_field('Version-value',Data::binary(),Version::any()) will never return since it differs in the 1st argument from the success typing arguments: ('Constrained-encoding' | 'Date-value' | 'Delta-seconds-value' | 'Field-name' | 'No-value' | 'Q-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',binary(),any())
+wsp_pdu.erl:3459: The call wsp_pdu:decode_typed_field('Integer-value',Data::binary(),Version::any()) will never return since it differs in the 1st argument from the success typing arguments: ('Constrained-encoding' | 'Date-value' | 'Delta-seconds-value' | 'Field-name' | 'No-value' | 'Q-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',binary(),any())
+wsp_pdu.erl:3531: The call wsp_pdu:decode_typed_field('Integer-value',Data::binary(),Version::any()) will never return since it differs in the 1st argument from the success typing arguments: ('Constrained-encoding' | 'Date-value' | 'Delta-seconds-value' | 'Field-name' | 'No-value' | 'Q-value' | 'Short-integer' | 'Text-string' | 'Text-value' | 'Well-known-charset',binary(),any())
+wsp_pdu.erl:3593: The pattern 'Delta-Seconds-value' can never match the type 'Delta-seconds-value' | 'Field-name' | 'Integer-value' | 'No-value' | 'Q-value' | 'Ver-value'
+wsp_pdu.erl:4844: The call wsp_pdu:d_long('data') will never return since it differs in the 1st argument from the success typing arguments: (binary())
+wsp_pdu.erl:510: The variable _ can never match since previous clauses completely covered the type 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+wsp_pdu.erl:512: The variable _ can never match since previous clauses completely covered the type 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
+wsp_pdu.erl:5265: Call to missing or unexported function inet:ip_to_bytes/1
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/broken_dialyzer.erl b/lib/dialyzer/test/user_tests_SUITE_data/src/broken_dialyzer.erl
new file mode 100644
index 0000000000..fd9a6ada1a
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/broken_dialyzer.erl
@@ -0,0 +1,130 @@
+-module(broken_dialyzer).
+
+-export([do_move_next/1]).
+
+-define(ap_indices, 512).
+-define(dp_indices, 504).
+
+
+-record(apR,{a,c=[],n=[],nc=0,nn=0,nl=[]}).
+-define(apL(L), [#apR{a=A} || A <- L]).
+
+-define(gr, get(my_return_value)).
+-define(pr(PR), put(my_return_value, PR)).
+-record(bit,{i,c,n,s}). % index, current, next, state
+
+
+do_move_next({BL,AL}) ->
+ Max = max(length(BL), length(AL)),
+ Max2 = max(length(BL)*2, length(AL)),
+ MoveTo = [A || A <- AL, A#apR.nn < Max, A#apR.nn+A#apR.nc < Max2],
+ MoveFrom = [A || A <- AL,
+ (A#apR.nn > Max) orelse (A#apR.nn+A#apR.nc > Max2)],
+ Unchanged = (AL--MoveTo)--MoveFrom,
+ {BL1,{AL1,{AL2,AL3}}} =
+ lists:mapfoldl(
+ fun(B=#bit{i=I,c=C,s=S,n=Next}, {From,{To,FilledUp}})
+ when S==ok;S==lost_replica;S==moved_replica ->
+ case lists:keysearch(Next,#apR.a,From) of
+ {value, F=#apR{n=N1,nn=NN1,nc=NC1}}
+ when (NN1>Max) or (NN1+NC1>Max2) ->
+ case C of
+ [] ->
+ {B, {From,{To,FilledUp}}};
+ ShortList ->
+ T=#apR{a=NewNext,n=N2,nn=NN2} =
+ find_next(Next,ShortList),
+ {value, {C,NL_from}} =
+ lists:keysearch(C,1,F#apR.nl),
+ {value, {C,NL_to}} =
+ lists:keysearch(C,1,T#apR.nl),
+ NewNL_from = lists:keyreplace(
+ C,1,F#apR.nl,{C,NL_from--[I]}),
+ NewNL_to = lists:keyreplace(
+ C,1,T#apR.nl,{C,[I|NL_to]}),
+
+ NewT = T#apR{n=[I|N2],nn=NN2+1,
+ nl=NewNL_to},
+
+ {B#bit{n=NewNext,
+ s = if
+ S == lost_replica ->
+ lost_replica;
+ true ->
+ moved_replica
+ end},
+ {lists:keyreplace(
+ Next,#apR.a,From,
+ F#apR{n=N1--[I],nn=NN1-1,nl=NewNL_from}),
+ if
+ (NewT#apR.nn+NewT#apR.nc >= Max2)
+ or (NewT#apR.nn >= Max) ->
+ {lists:keydelete(NewNext,#apR.a,To),
+ [NewT|FilledUp]};
+ true ->
+ {lists:keyreplace(
+ NewNext,#apR.a,To,NewT),
+ FilledUp}
+ end}}
+ end;
+ _ ->
+ {B, {From,{To,FilledUp}}}
+ end;
+ (B, A) ->
+ {B, A}
+ end, {MoveFrom,{MoveTo,[]}},BL),
+ {BL1,Unchanged++AL1++AL2++AL3}.
+
+%%% -----------------------------------------------------------------
+%%% find_next/2
+%%%
+%%% ------------------------------------------------------------------
+
+find_next(Ap,L) ->
+ hd(catch
+ lists:foreach(
+ fun(SelVal) ->
+ case [ApR ||
+ ApR <- L,
+ begin
+ {value,{Ap,NL}} =
+ lists:keysearch(Ap,1,ApR#apR.nl),
+ length(NL) =< SelVal
+ end] of
+ [] ->
+ ok;
+ ShortList ->
+ throw(ShortList)
+ end
+ end,
+ lists:seq(0,?ap_indices))).
+
+%%% -----------------------------------------------------------------
+%%% max/2
+%%%
+%%% Calculates max number of indices per AP, given number of indices
+%%% and number of APs.
+%%% -----------------------------------------------------------------
+max(F,S) ->
+ (F div S) + if
+ (F rem S) == 0 ->
+ 0;
+ true ->
+ 1
+ end.
+
+%%% ==============================================================
+%%% ADMINISTRATIVE INFORMATION
+%%% ==============================================================
+%%% #Copyright (C) 2005
+%%% by ERICSSON TELECOM AB
+%%% S - 125 26 STOCKHOLM
+%%% SWEDEN, tel int + 46 8 719 0000
+%%%
+%%% The program may be used and/or copied only with the written
+%%% permission from ERICSSON TELECOM AB, or in accordance with
+%%% the terms and conditions stipulated in the agreement/contract
+%%% under which the program has been supplied.
+%%%
+%%% All rights reserved
+%%%
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/gcp.hrl b/lib/dialyzer/test/user_tests_SUITE_data/src/gcp.hrl
new file mode 100644
index 0000000000..aac87d8b6b
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/gcp.hrl
@@ -0,0 +1,166 @@
+%%% #0. BASIC INFORMATION
+%%% ----------------------------------------------------------
+%%% %CCaseFile: gcp.hrl %
+%%% Author: EAB/UPD/AV
+%%% Description: Internal include file.
+%%% ----------------------------------------------------------
+-hrl_id('9/190 55-CNA 113 033 Ux').
+-hrl_vsn('/main/R1A/21').
+-hrl_date('2005-05-31').
+-hrl_author('uabasve').
+%%% %CCaseTemplateFile: module.hrl %
+%%% %CCaseTemplateId: 17/002 01-FEA 202 714 Ux, Rev: /main/4 %
+%%%
+%%% Copyright (C) 2000-2005 by Ericsson Telecom AB
+%%% SE-126 25 STOCKHOLM
+%%% SWEDEN, tel int + 46 8 719 0000
+%%%
+%%% The program may be used and/or copied only with the written
+%%% permission from Ericsson Telecom AB, or in accordance with
+%%% the terms and conditions stipulated in the agreement/contract
+%%% under which the program has been supplied.
+%%%
+%%% All rights reserved
+%%%
+%%% ----------------------------------------------------------
+%%% #1. REVISION LOG
+%%% ----------------------------------------------------------
+%%% Rev Date Name What
+%%% ----- ------- -------- ------------------------
+%%% R1A/1 05-02-07 uabasve Copied from EAS R7A/9
+%%% R1A/2 05-02-08 ejojmjn Removed SAAL
+%%% R1A/3- 05-03-18 uabasve Clean.
+%%% ----------------------------------------------------------
+%%%
+%%% #2. CODE
+%%% #---------------------------------------------------------
+%%% #2.1 DEFINITION OF CONSTANTS
+%%% #---------------------------------------------------------
+
+%% Keys into gcpVariables for various options/values.
+-define(TRAFFIC_DESCRIPTOR_KEY, traffic_descriptor).
+
+%% H.248 version at link creation.
+-define(INITIAL_H248_VERSION, 1).
+
+%% Exceptions for use within a module. ?MODULE is just extra protection
+%% against catching something unexpected.
+-define(THROW(Reason), throw({error, ?MODULE, ?LINE, Reason})).
+-define(CATCH(Expr), try Expr
+ catch throw: ?FAILURE(Reason) -> {error, Reason}
+ end).
+-define(FAILURE(T), {error, ?MODULE, _, T}).
+
+%% The SendHandle used by a GCP transport process must be a tuple
+%% of length >= 2 whose first two elements are the pid of the
+%% transport process and index (aka #gcpLinkTable.key) of the link
+%% upon which incoming data has arrived.
+-define(SH_PID(SendHandle), element(1, SendHandle)).
+-define(SH_LINK(SendHandle), element(2, SendHandle)).
+-define(SH_SET_PID(SendHandle, Pid), setelement(1, SendHandle, Pid)).
+
+%% Megaco process that CH and OM servers monitor. This needs to be
+%% replaced by a documented method.
+-define(MEGACO_APP, megaco_config).
+
+%% The message that gcpI:send_reply sends to the process that's waiting
+%% for an action reply.
+-define(ACTION_REPLY_MESSAGE(ActionReplies, Result),
+ {reply, ActionReplies, Result}).
+
+%%% #---------------------------------------------------------
+%%% #2.2 DEFINITION OF RECORDS
+%%% #---------------------------------------------------------
+
+-record(mg, {pref}).
+-record(mgc, {mgid}).
+
+%% User configuration that gets mapped into megaco user info by
+%% gcpLib:make_user_info/1. GCP exposes only a subset of what's
+%% possible to set in megaco.
+-record(user_config,
+ {reply_timer = 30000, %% ms to wait for reply ack
+ %% Incoming transactions:
+ pending_timer = 10000, %% ms until outgoing transaction pending
+ sent_pending_limit = 5, %% nr of outgoing pendings before 506
+ %% Outgoing transactions:
+ recv_pending_limit = infinity,%% nr of incoming pendings before fail
+ request_timer = 3000, %% ms to wait for response before resend
+ request_retries = 5, %% nr unanswered sends before fail
+ long_request_timer = 15000, %% ms to wait for reply after pending
+ long_request_retries = 5}). %% nr of pendings/timeouts before fail
+
+%% Record passed into transport implementations at transport start.
+%% Expected to be passed back to gcpTransportI.
+-record(receive_handle,
+ {megaco_receive_handle, %% passed to megaco:receive_message
+ receive_message}). %% gcpLinkTable.receive_message
+
+%%% ---------------------------------------------------------------------------
+%%% # gcpRegistrationTable
+%%%
+%%% Record containing defined MGC's/MG's (aka megaco users).
+%%% ---------------------------------------------------------------------------
+
+-record(gcpRegistrationTable,
+ {key, %% user reference (aka MG/MGC id)
+ role, %% mg | mgc
+ mid, %% H.248 mid of the MGC/MG
+ version, %% of H.248
+ callback, %% {Module, ExtraArgs}
+ config = #user_config{}}).
+
+%%% ----------------------------------------------------------
+%%% # gcpLinkTable
+%%% ----------------------------------------------------------
+
+-record(gcpLinkTable,
+ {key, %% link reference
+ endpoint, %% #mgc{} | #mg{}
+ user, %% registration table key
+ chid, %% call handler of transport
+ admin_state, %% up | down
+ op_state, %% up | down | pending | disabled
+ restart = auto, %% auto | user
+ encoding_mod, %% module implementing megaco_encoder
+ encoding_config, %% as passed to encoding_mod
+ transport_start, %% {M,F,ExtraArgs} for transport start
+ transport_data, %% arbitrary, passed to transport_mod
+ send_message, %% {default|sysrpc|transport|module, Module}
+ receive_message, %% local | {M,F,ExtraArgs} for decode node
+ tried = false, %% Only for links owned by a MG.
+ %% Used to indicate that a setup attempt
+ %% has been performed on this link.
+ t95_period = 350000}).
+
+%%% ----------------------------------------------------------
+%%% # gcpActiveLinkTable
+%%% ----------------------------------------------------------
+
+-record(gcpActiveLinkTable,
+ {key, %% {mg|mgc, MgId}
+ link, %% link reference
+ chid, %% CH the link is tied to
+ node, %% node the link is on
+ conn_handle, %% record megaco_conn_handle
+ send_handle, %% {TransportPid, LinkIdx, ...}
+ version = ?INITIAL_H248_VERSION}).
+
+%%% ----------------------------------------------------------
+%%% # gcpVariables
+%%% ----------------------------------------------------------
+
+-record(gcpVariables,
+ {key,
+ value}).
+
+%%% ----------------------------------------------------------
+%%% # gcpReplyData
+%%% ----------------------------------------------------------
+
+-record(gcpReplyData,
+ {callback, %% {Module, Args}
+ mgid,
+ user_data, %% As passed by the user on send
+ prio,
+ timestamp}).
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/gcpFlowControl.erl b/lib/dialyzer/test/user_tests_SUITE_data/src/gcpFlowControl.erl
new file mode 100644
index 0000000000..1653220352
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/gcpFlowControl.erl
@@ -0,0 +1,397 @@
+%%%-------------------------------------------------------------------
+%%% File : gcpFlowControl.erl
+%%% Author : EAB/UPD/AV
+%%% Description : Implements overload protection.
+%%%-------------------------------------------------------------------
+-module(gcpFlowControl).
+-id('24/190 55-CNA 113 033 Ux').
+-vsn('/main/R1A/14').
+-date('2005-05-04').
+-author('uabasve').
+%%% ----------------------------------------------------------
+%%% %CCaseTemplateFile: module.erl %
+%%% %CCaseTemplateId: 16/002 01-FEA 202 714 Ux, Rev: /main/4 %
+%%%
+%%% Copyright (C) 2001-2005 by Ericsson Telecom AB
+%%% SE-126 25 STOCKHOLM
+%%% SWEDEN, tel int + 46 8 719 0000
+%%%
+%%% The program may be used and/or copied only with the written
+%%% permission from Ericsson Telecom AB, or in accordance with
+%%% the terms and conditions stipulated in the agreement/contract
+%%% under which the program has been supplied.
+%%%
+%%% All rights reserved
+%%%
+%%%
+%%% ----------------------------------------------------------
+%%% #1. REVISION LOG
+%%% ----------------------------------------------------------
+%%% Rev Date Name What
+%%% -------- -------- -------- ------------------------
+%%% R1A/1-2 05-02-07 ejojmjn Copied from EAS R7A/11.
+%%% R1A/3-14 05-03-14 uabasve Clean.
+%%%--------------------------------------------------------------------
+
+-include_lib("megaco/include/megaco.hrl").
+-include_lib("megaco/include/megaco_message_v1.hrl").
+-include("gcp.hrl").
+
+-export([send_request/4, %% user send from gcpInterface
+ receive_reply/2, %% from callback in gcpTransaction
+ init_ets_tables/1,
+ init_data/2]).
+
+-define(PRIO_INFINITY, 16).
+-define(MIN_WINDOW, 10).
+-define(MAX_WINDOW, 100).
+
+-define(BUCKET_MAX, 100).
+-define(BUCKET_THRESH_HIGH, 80).
+-define(BUCKET_THRESH_LOW, 20).
+
+-define(ALLOW_TIMEOUT, 1000).
+
+%% Holds counters for flow control in GCP
+-record(gcpFlowControlTable,
+ {key,
+ window = 50,
+ available = 50,
+ bucket = 0,
+ q = 0,
+ sent = 0, %% Counts all attempts
+ rejectable = 0, %% Counts rejectable attempts
+ t95,
+ errors = 0,
+ rejects = 0,
+ replies = 0}).
+
+-record(gcpFlowControlBitmap,
+ {key,
+ count = 0}).
+
+%%====================================================================
+%% External functions
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: send_request/4
+%%
+%% Output: ok | {error, Reason}
+%%--------------------------------------------------------------------
+
+send_request(ActiveLink, TimerOptions, ActionRequests, UserData) ->
+ #gcpActiveLinkTable{key = Key,
+ conn_handle = ConnHandle}
+ = ActiveLink,
+ Prio = prio(ActionRequests),
+ incr(Key, sent),
+ case allow(Key, Prio) of
+ {true, Timestamp} ->
+ grant_request(user_data(ConnHandle),
+ Key,
+ Prio,
+ Timestamp,
+ ConnHandle,
+ TimerOptions,
+ ActionRequests,
+ UserData);
+ false ->
+ {error, rejected}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: receive_reply/2
+%% Description:
+%%--------------------------------------------------------------------
+
+receive_reply(Key, Timestamp) ->
+ incr(Key, available),
+ incr(Key, replies),
+ release(Key),
+ report_time(Key, Timestamp).
+
+%%--------------------------------------------------------------------
+%% Func: init_ets_tables/1
+%%
+%% Returns: ok
+%%--------------------------------------------------------------------
+
+init_ets_tables(Role) ->
+ create_ets(Role, gcpFlowControlTable, #gcpFlowControlTable.key),
+ create_ets(Role, gcpFlowControlBitmap, #gcpFlowControlBitmap.key),
+ ok.
+
+create_ets(Role, Table, Pos) when integer(Pos) ->
+ create_ets(Role,
+ Table,
+ [named_table, ordered_set, public, {keypos, Pos}]);
+
+create_ets(test, Table, ArgList) ->
+ ets:new(Table, ArgList);
+create_ets(Role, Table, ArgList) ->
+ case ets:info(Table) of
+ undefined ->
+ sysCmd:ets_new(Table, ArgList);
+ _ when Role == ch ->
+ sysCmd:inherit_tables([Table]);
+ _ when Role == om ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+%% Func: init_data/2
+%%--------------------------------------------------------------------
+
+init_data(Key, T95) ->
+ ets:insert(gcpFlowControlTable, #gcpFlowControlTable{key = Key,
+ t95 = T95}).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+%%% ----------------------------------------------------------
+%%% incr
+%%% ----------------------------------------------------------
+
+cntr(Key, Field) ->
+ incr(Key, Field, 0).
+
+incr(Key, Field) ->
+ incr(Key, Field, 1).
+
+-define(INCR(Field),
+ incr(Key, Field, X) -> upd_c(Key, {#gcpFlowControlTable.Field, X})).
+
+?INCR(sent);
+?INCR(replies);
+?INCR(q);
+?INCR(t95);
+?INCR(errors);
+?INCR(rejects);
+?INCR(rejectable);
+?INCR(window);
+?INCR(available);
+
+incr(Key, bucket, X)->
+ upd_c(Key, {#gcpFlowControlTable.bucket, X, ?BUCKET_MAX, ?BUCKET_MAX}).
+
+upd_c(Key, N) ->
+ ets:update_counter(gcpFlowControlTable, Key, N).
+
+%%% ----------------------------------------------------------
+%%% decr
+%%%
+%%% Beware that decr is implemented as incr, care has to be taken
+%%% not to bungle things when max/min values are used.
+%%% ----------------------------------------------------------
+
+decr(Key, available, X) ->
+ upd_c(Key, {#gcpFlowControlTable.available, -X});
+decr(Key, window, X) ->
+ upd_c(Key, {#gcpFlowControlTable.window, -X});
+decr(Key, bucket, X) ->
+ upd_c(Key, {#gcpFlowControlTable.bucket, -X, 0, 0}).
+
+decr(Key, Field) ->
+ decr(Key, Field, 1).
+
+%%% ----------------------------------------------------------
+%%% allow
+%%% ----------------------------------------------------------
+
+allow(Key, ?PRIO_INFINITY) ->
+ decr(Key, available),
+ {true, now()};
+
+allow(Key, Prio) ->
+ incr(Key, rejectable),
+ case decr(Key, available) of
+ N when N > 0 ->
+ {true, no_stamp};
+ _ ->
+ %% We did not send it, therefore incr available again
+ incr(Key, available),
+ queue(Key, Prio)
+ end.
+
+%%% ----------------------------------------------------------
+%%% queue
+%%% ----------------------------------------------------------
+
+queue(Key, Prio) ->
+ incr(Key, q),
+ T = {Key, Prio, now(), self()},
+ ets:insert(gcpFlowControlBitmap, #gcpFlowControlBitmap{key = T}),
+ wait(T).
+
+%%% ----------------------------------------------------------
+%%% wait
+%%% ----------------------------------------------------------
+
+wait({Key, _Prio, _When, _Self} = T) ->
+ receive
+ allow ->
+ ets:delete(gcpFlowControlBitmap, T),
+ decr(Key, available),
+ {true, no_stamp}
+ after ?ALLOW_TIMEOUT ->
+ timeout(T),
+ adjust_window(Key),
+ incr(Key, rejects),
+ false
+ end.
+
+timeout(T) ->
+ case ets:update_counter(gcpFlowControlBitmap, T, 1) of
+ 1 ->
+ %% Got the lock: no one has released Key and sent 'allow'.
+ ets:delete(gcpFlowControlBitmap, T),
+ ok;
+ _ ->
+ %% A releasing process got the lock: 'allow' has been
+ %% sent. Try to remove the message before proceeding.
+ %% (This is to keep mdisp from complaining apparently.)
+ ets:delete(gcpFlowControlBitmap, T),
+ receive
+ allow ->
+ ok
+ after ?ALLOW_TIMEOUT ->
+ io:format("~p: errant allow: ~p~n", [?MODULE, T])
+ end
+ end.
+
+%% Now, if we reject and our general response time is low
+%% (i.e. low bucket) then we increase the window size.
+adjust_window(Key) ->
+ adjust_window(Key,
+ cntr(Key, bucket) < ?BUCKET_THRESH_LOW
+ andalso cntr(Key, window) < ?MAX_WINDOW).
+
+adjust_window(Key, true) ->
+ incr(Key, window),
+ incr(Key, available),
+ incr(Key, bucket, 20);
+adjust_window(_, false) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Func: report_time/2
+%%--------------------------------------------------------------------
+
+report_time(_, no_stamp) ->
+ ok;
+report_time(Key, {MS, S, Ms})->
+ {MegaSecs, Secs, MicroSecs} = now(),
+ p(Key,
+ MicroSecs - Ms + 1000000*(Secs - S + 1000000*(MegaSecs - MS)),
+ cntr(Key, t95)).
+
+%%% ----------------------------------------------------------
+%%% p
+%%% ----------------------------------------------------------
+
+p(Key, Time, T95) when Time =< T95 ->
+ decr(Key, bucket);
+p(Key, _Time, _T95) ->
+ %% If we have a long response time, then increase the leaky
+ %% bucket. If the bucket is over the high watermark and the window
+ %% is not already at its minimum size, then decrease the window
+ %% and available.
+ case {cntr(Key, window), incr(Key, bucket, 20)} of
+ {Window, Bucket} when Window > ?MIN_WINDOW,
+ Bucket > ?BUCKET_THRESH_HIGH ->
+ decr(Key, window),
+ decr(Key, available);
+ _ ->
+ ok
+ end.
+
+%%% ----------------------------------------------------------
+%%% release
+%%% ----------------------------------------------------------
+
+release(Key) ->
+ %% The choice of the key below will cause ets:prev/2 to return
+ %% the key with the highest priority which was queued most
+ %% recently. This relies on the fact that integers sort before
+ %% atoms, the atom 'prio' in this case. The atoms 'queued' and
+ %% 'pid' are of no significance.
+ release(Key, {Key, prio, queued, pid}).
+
+%% This isn't a (FIFO) queue within each priority, but a (LIFO) stack.
+
+release(Key, T) ->
+ release(Key, cntr(Key, available), ets:prev(gcpFlowControlBitmap, T)).
+
+%% Note that only keys on the same Key are matched.
+release(Key, N, {Key, _Prio, _When, Pid} = T) when N > 0 ->
+ case catch ets:update_counter(gcpFlowControlBitmap, T, 1) of
+ 1 ->
+ Pid ! allow;
+ _ ->
+ %% Another process has released this key.
+ release(Key, T)
+ end;
+
+release(_, _, _)->
+ ok.
+
+%%% ----------------------------------------------------------
+%%% user_data
+%%% ----------------------------------------------------------
+
+user_data(ConnHandle) ->
+ case catch megaco:conn_info(ConnHandle, reply_data) of
+ {'EXIT', _Reason} ->
+ false;
+ Rec ->
+ {value, Rec}
+ end.
+
+%%% ----------------------------------------------------------
+%%% grant_request
+%%% ----------------------------------------------------------
+
+grant_request({value, Rec},
+ Key, Prio, Time,
+ ConnHandle, Options, ActionRequests, UserData) ->
+ ReplyData = Rec#gcpReplyData{user_data = UserData,
+ prio = Prio,
+ timestamp = Time},
+ cast_rc(megaco:cast(ConnHandle,
+ ActionRequests,
+ [{reply_data, ReplyData} | Options]),
+ Key,
+ ActionRequests);
+
+grant_request(false, Key, _, _, _, _, _, _) ->
+ incr(Key, available),
+ {error, reply_data}.
+
+cast_rc(ok = Ok, _, _) ->
+ Ok;
+cast_rc({error, Reason}, Key, ActionRequests) ->
+ incr(Key, available),
+ gcpLib:error_report(?MODULE, send_request, [ActionRequests],
+ "send failed",
+ Reason),
+ {error, {encode, Reason}}.
+
+%%--------------------------------------------------------------------
+%% Func: prio/1
+%% Returns: The priority of the request
+%%--------------------------------------------------------------------
+
+prio([ActionRequest | _]) ->
+ #'ActionRequest'{contextId = ContextId,
+ contextRequest = ContextRequest}
+ = ActionRequest,
+ prio(ContextId, ContextRequest).
+
+prio(?megaco_choose_context_id, #'ContextRequest'{priority = Prio})
+ when integer(Prio) ->
+ Prio;
+prio(_, _) ->
+ ?PRIO_INFINITY.
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/qlc_error.erl b/lib/dialyzer/test/user_tests_SUITE_data/src/qlc_error.erl
new file mode 100644
index 0000000000..a6865c4562
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/qlc_error.erl
@@ -0,0 +1,15 @@
+%% -*- erlang-indent-level: 2 -*-
+%% $Id: qlc_error.erl,v 1.1 2008/12/17 09:53:52 mikpe Exp $
+
+%% @author Daniel Luna <[email protected]>
+%% @copyright 2006 Daniel Luna
+%%
+%% @doc
+%%
+
+-module(qlc_error).
+-export([fix/0]).
+-include_lib("stdlib/include/qlc.hrl").
+
+fix() ->
+ qlc:eval(qlc:q([I || I <- []])).
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/spvcOrig.erl b/lib/dialyzer/test/user_tests_SUITE_data/src/spvcOrig.erl
new file mode 100644
index 0000000000..70a3c4c7e2
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/spvcOrig.erl
@@ -0,0 +1,3523 @@
+%%%=======================================================================
+%%%
+%%% Test from Mats Cronqvist <[email protected]>. The
+%%% analysis crasched due to the handling of tuples-as-funs in
+%%% hipe_icode_type.erl, and it also exposed a bug when a control flow
+%%% path is first analyzed and then shown to be infeasible.
+%%%
+
+-file("./spvcOrig.erl", 1).
+
+-module(spvcOrig).
+
+-author(qamarma).
+
+-id('3/190 55-CNA 121 64').
+
+-vsn('/main/Inc4/R2A/R4A/R6A/R7A/R7D/R8B/R10A/R11A/2').
+
+-date('2004-10-26').
+
+-export([gen_set/3,gen_set/4,connect/3,release_comp_nu/3,release_nu/3,timeout/2,restart_spvc/1,restart_multi_spvcs/1,forced_release/1,error_handler/3,get_backoff_table/2,timeout_event/1]).
+
+-export([release_incumbent/2,switch_over/2]).
+
+-export([call_failure/1,get_backoff_table/2]).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/pchTables.hrl", 1).
+
+-hrl_id('2/190 55-CNA 121 08').
+
+-hrl_vsn('/main/Inc3/Inc4/R2A/R3A/R3B/R5A/R6A/R7A/R7D/R8B/13').
+
+-hrl_date('2003-01-24').
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../derived_hrl/mib/AXD301-PCH-MIB.hrl", 1).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/pchTables.hrl", 58).
+
+-record(pchVp, {vplEntry,
+ vplLastChange,
+ vplReceiveTrafficDescrIndex = 0,
+ vplTransmitTrafficDescrIndex = 0,
+ vplCcIdentifier,
+ vplConnId,
+ vplMpId,
+ vplLeafId,
+ vplChargingIndicator = 1,
+ vplRemoteChargingInd = 1,
+ vplChargablePartyIdentifier,
+ vplSegmentEndPoint = 2,
+ vplRowStatus,
+ vplCastType = 1,
+ vplConnKind = 1,
+ vplServiceType = 2,
+ vplEndPointData,
+ vplContinuityCheck = 1,
+ vplUpcNpcMode = 2,
+ vplPreventInbandCc = 1,
+ vplMonAisRdi = 2,
+ vpcAdminStatus = 2,
+ vplSpvcAutoTarget = 2,
+ vplSchedulingFlag = 2,
+ vplApplication,
+ vplRemoteData,
+ vpccAdminStatus = 2,
+ vplContCheckSearch = 1,
+ vplPmSearch = 1,
+ vplLastBuffFlagRead,
+ vplShapingMode = 1,
+ vplGroupShapingId}).
+
+-record(pchVpDb, {vplEntry,
+ vplLastChange,
+ vplReceiveTrafficDescrIndex = 0,
+ vplTransmitTrafficDescrIndex = 0,
+ vplCcIdentifier,
+ vplConnId,
+ vplMpId,
+ vplLeafId,
+ vplAttributes,
+ vplChargablePartyIdentifier,
+ vplRowStatus,
+ vplEndPointData,
+ vplApplication,
+ vplRemoteData,
+ vplLastBuffFlagRead,
+ vplShapingMode,
+ vplGroupShapingId}).
+
+-record(pchVpExt, {vplExtEntry,
+ vplExtReceiveTdIndex,
+ vplExtTransmitTdIndex,
+ vplExtUserName = [],
+ vplExtProviderName = [],
+ vplExtUserOperator}).
+
+-record(pchVc, {vclEntry,
+ vclLastChange,
+ vclReceiveTrafficDescrIndex = 0,
+ vclTransmitTrafficDescrIndex = 0,
+ vclCcIdentifier,
+ vclConnId,
+ vclMpId,
+ vclLeafId,
+ vclChargingIndicator = 1,
+ vclRemoteChargingInd = 1,
+ vclChargablePartyIdentifier,
+ vclPacketDiscard = 2,
+ vclSegmentEndPoint = 2,
+ vclRowStatus,
+ vclCastType = 1,
+ vclConnKind = 1,
+ vclContinuityCheck = 1,
+ vclUpcNpcMode = 2,
+ vclEndPointData,
+ vclPreventInbandCc = 1,
+ vclMonAisRdi = 2,
+ vclSpvcAutoTarget = 2,
+ vclSchedulingFlag = 2,
+ vclApplication,
+ vclRemoteData,
+ vcccAdminStatus = 2,
+ vclContCheckSearch = 1,
+ vclPmSearch = 1,
+ vclLastBuffFlagRead,
+ vclChargingIfChanid,
+ vclShapingMode = 1}).
+
+-record(pchVcDb, {vclEntry,
+ vclLastChange,
+ vclReceiveTrafficDescrIndex = 0,
+ vclTransmitTrafficDescrIndex = 0,
+ vclCcIdentifier,
+ vclConnId,
+ vclMpId,
+ vclLeafId,
+ vclAttributes,
+ vclChargablePartyIdentifier,
+ vclRowStatus,
+ vclEndPointData,
+ vclApplication,
+ vclRemoteData,
+ vclLastBuffFlagRead,
+ vclChargingIfChanid,
+ vclShapingMode}).
+
+-record(pchAtd, {tdIndex,
+ tdType,
+ tdParam1 = 0,
+ tdParam2 = 0,
+ tdParam3 = 0,
+ tdParam4 = 0,
+ tdParam5 = 0,
+ tdTrafficQoSClass = 0,
+ tdRowStatus = 1,
+ tdServiceCategory = 6,
+ tdVcCapability = 1,
+ tdName = [],
+ tdUserCounter = 0,
+ tdUser = []}).
+
+-record(pchAbr, {abrIndex,
+ abrIcr,
+ abrTbe = 16277215,
+ abrFrtt = 0,
+ abrRdf = 11,
+ abrRif = 11,
+ abrNrm = 4,
+ abrTrm = 7,
+ abrCdf = 3,
+ abrAdtf = 50,
+ abrRowStatus = 1}).
+
+-record(pchIndexNext, {key,
+ tdIndexNext,
+ vpccIndexNext,
+ vcccIndexNext,
+ scheduledVpCcIndexNext,
+ scheduledVcCcIndexNext}).
+
+-record(pchSchedVpCc, {schedVpCcIndex,
+ schedVpCcTarget,
+ schedVpCcReceiveTdIndex,
+ schedVpCcTransmitTdIndex,
+ schedVpCcOpTime,
+ schedVpCcOpInd,
+ schedVpCcOpStatus,
+ schedVpCcTimerRef,
+ schedVpCcRowStatus,
+ schedVpCcErrorCode,
+ schedVpCcUserName = [],
+ schedVpCcProviderName = []}).
+
+-record(pchVpCc, {vpccId,
+ vpccUserName = [],
+ vpccAdminStatus,
+ vpccApplication,
+ vpccProviderName = []}).
+
+-record(pchSchedVcCc, {schedVcCcIndex,
+ schedVcCcTarget,
+ schedVcCcReceiveTdIndex,
+ schedVcCcTransmitTdIndex,
+ schedVcCcOpTime,
+ schedVcCcOpInd,
+ schedVcCcOpStatus,
+ schedVcCcTimerRef,
+ schedVcCcRowStatus,
+ schedVcCcErrorCode,
+ schedVcCcUserName = [],
+ schedVcCcProviderName = []}).
+
+-record(pchVcCc, {vcccId,
+ vcccUserName = [],
+ vcccAdminStatus,
+ vcccApplication,
+ vcccProviderName = []}).
+
+-record(pchSigChannels, {et_entry,
+ cp_entry,
+ sb_cp_entry,
+ membership,
+ status,
+ sb_status,
+ application = {0,[]}}).
+
+-record(pchSigChannelExt, {et_entry,
+ user_name,
+ provider_name}).
+
+-record(pchApplication, {key,
+ application,
+ rights}).
+
+-record(pchCurrAlarm, {key,
+ type_of_fault,
+ fault_id}).
+
+-record(pchIfAddress, {ifAddressEntry,
+ ifAddressRowStatus}).
+
+-record(pchAddressToIf, {address,
+ if_index}).
+
+-record(pchPreferences, {key,
+ if_format}).
+
+-record(pchSigChannelCallback, {key,
+ callback,
+ function,
+ args,
+ data}).
+
+-record(pchTermHcId, {hcId,
+ vclEntry}).
+
+-record(pchChg, {chgEntry,
+ chgStatus}).
+
+-record(pchCommState, {key,
+ ccid,
+ request,
+ low_cp_state,
+ high_cp_state,
+ et_side,
+ application,
+ data,
+ timestamp,
+ timer_id,
+ callback}).
+
+-record(pchBufferedCmd, {key,
+ resource,
+ module,
+ function,
+ arguments,
+ data}).
+
+-record(pchAnswerCh, {conn_id,
+ chg_data,
+ call_back_cp,
+ old_rtd,
+ old_ttd,
+ old_EpData,
+ action,
+ resource,
+ data,
+ fail_cause}).
+
+-record(pchAnswerOm, {conn_id}).
+
+-record(ccPch, {rowInd,
+ admState = 2}).
+
+-record(pchIf, {ilmiVpi = 0,
+ ilmiVci = 0,
+ ilmiS = 1,
+ ilmiT = 5,
+ ilmiK = 4,
+ neighborIfName = [],
+ neighborIpAddr = [0,0,0,0],
+ maxVciSvc,
+ overbookingFactor = {0,0},
+ shapingMode = 0,
+ maxVpiSvc,
+ cdvtMultFactor = 100,
+ scBandwidth1 = 0,
+ scBandwidth2 = 0,
+ scBandwidth3 = 0,
+ scBandwidth4 = 0}).
+
+-record(pchMpTemp, {key,
+ data}).
+
+-record(pchLatestErrorCode, {key,
+ errorCode}).
+
+-record(pchRangeTable, {node,
+ tdIndexRange,
+ vpccIndexRange,
+ vcccIndexRange}).
+
+-record(pchIndexBitmaps, {key,
+ available,
+ bitmap}).
+
+-record(pchLinkState, {key,
+ op_state,
+ last_change}).
+
+-record(pchFailedVpl, {vplEntry,
+ vplLastChange}).
+
+-record(pchFailedVcl, {vclEntry,
+ vclLastChange}).
+
+-record(pchStatCounters, {key,
+ ingress,
+ egress}).
+
+-record(pchEtStatTable, {index,
+ value = 0}).
+
+-record(pchAuditResult, {key,
+ passed,
+ not_passed,
+ sizes,
+ obj_keys}).
+
+-record(pch_fault_reqc, {fault_type,
+ fault_location}).
+
+-record(pch_cid, {conn_id,
+ mp_id,
+ leaf_id}).
+
+-file("./spvcOrig.erl", 207).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/pchI.hrl", 1).
+
+-hrl_id('52/190 55-CNA 121 08 Ux').
+
+-hrl_vsn('/main/R6A/R7A/R7D/R8B/3').
+
+-hrl_date('2002-10-14').
+
+-hrl_author(uabdomo).
+
+-record(pch_vc_rec, {ifIndex,
+ vpi,
+ vci,
+ application}).
+
+-record(pch_vp_rec, {ifIndex,
+ vpi}).
+
+-record(pch_td_index, {rtd_index,
+ ttd_index}).
+
+-record(pch_td, {service_cat,
+ pcr,
+ scr,
+ mbs,
+ mcr,
+ cdvt,
+ tagging,
+ clp_significance}).
+
+-record(pch_call_back_req, {module,
+ function,
+ user_data}).
+
+-record(pch_chg_rec, {chg_type,
+ chg_interface,
+ chg_chan_id,
+ chg_party_name}).
+
+-record(pch_polic_rec, {policing,
+ packet_discard}).
+
+-record(pch_user_name_rec, {user_name}).
+
+-record(pch_shaping_rec, {shaping}).
+
+-record(pch_audit_callback, {mod,
+ arg}).
+
+-file("./spvcOrig.erl", 208).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/plc.hrl", 1).
+
+-hrl_id('12/190 55-CNA 121 45 Ux').
+
+-hrl_vsn('/main/R6A/R6B/R7A/R7D/R8B/R9A/R11A/4').
+
+-hrl_date('2004-12-07').
+
+-hrl_author(ethrba).
+
+-record(plcQueues, {name,
+ type,
+ weight,
+ maxlength,
+ owner}).
+
+-record(plcSettings, {flag,
+ value}).
+
+-record(plcAlarm, {flag,
+ value}).
+
+-file("./spvcOrig.erl", 209).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/spvcTables.hrl", 1).
+
+-hrl_id('10/190 55-CNA 121 64').
+
+-hrl_vsn('/main/Inc4/R2A/R3A/R3B/R5A/R6A/R7A/R7D/R8B/4').
+
+-hrl_date('2003-02-12').
+
+-hrl_author(etxovp).
+
+-record(spvcVpc, {spvcVpcEntry,
+ spvcVpcTargetAddress,
+ spvcVpcTargetSelectType,
+ spvcVpcTargetVpi,
+ spvcVpcLastReleaseCause,
+ spvcVpcLastReleaseDiagnostic,
+ spvcVpcRetryInterval = 1000,
+ spvcVpcRetryTimer = 0,
+ spvcVpcRetryThreshold = 1,
+ spvcVpcRetryFailures = 0,
+ spvcVpcRetryLimit = 15,
+ spvcVpcRowStatus,
+ spvcVpcUserName = [],
+ spvcVpcProviderName = [],
+ currentState,
+ crankBackCounter = 0,
+ spvcVpcApplication,
+ spvcRerCap = false,
+ spvcRerStatus = false}).
+
+-record(spvcVpcOpState, {state,
+ timeOfChange}).
+
+-record(spvcVpcPerm, {spvcVpcEntry,
+ spvcVpcTargetAddress,
+ spvcVpcTargetSelectType,
+ spvcVpcTargetVpi,
+ spvcVpcRetryInterval = 1000,
+ spvcVpcRetryThreshold = 1,
+ spvcVpcRetryLimit = 15,
+ spvcVpcRowStatus,
+ spvcVpcUserName,
+ spvcVpcProviderName,
+ spvcVpcApplication}).
+
+-record(spvcVpcDyn, {spvcVpcEntry,
+ spvcVpcLastReleaseCause,
+ spvcVpcLastReleaseDiagnostic,
+ spvcVpcRetryTimer = 0,
+ spvcVpcRetryFailures = 0,
+ currentState,
+ crankBackCounter = 0}).
+
+-record(spvcVcc, {spvcVccEntry,
+ spvcVccTargetAddress,
+ spvcVccTargetSelectType,
+ spvcVccTargetVpi,
+ spvcVccTargetVci,
+ spvcVccLastReleaseCause,
+ spvcVccLastReleaseDiagnostic,
+ spvcVccRetryInterval = 1000,
+ spvcVccRetryTimer = 0,
+ spvcVccRetryThreshold = 1,
+ spvcVccRetryFailures = 0,
+ spvcVccRetryLimit = 15,
+ spvcVccRowStatus,
+ spvcVccUserName = [],
+ spvcVccProviderName = [],
+ currentState,
+ crankBackCounter = 0,
+ spvcVccTargetDlci,
+ spvcVccTargetType,
+ spvcVccApplication,
+ spvcVccFrKey,
+ spvcVccTranslationMode,
+ spvcRerCap = false,
+ spvcRerStatus = false}).
+
+-record(spvcVccOpState, {state,
+ timeOfChange}).
+
+-record(spvcVccPerm, {spvcVccEntry,
+ spvcVccTargetAddress,
+ spvcVccTargetSelectType,
+ spvcVccTargetVpi,
+ spvcVccTargetVci,
+ spvcVccRetryInterval = 1000,
+ spvcVccRetryThreshold = 1,
+ spvcVccRetryLimit = 15,
+ spvcVccRowStatus,
+ spvcVccUserName,
+ spvcVccProviderName,
+ spvcVccTargetDlci,
+ spvcVccTargetType,
+ spvcVccApplication,
+ spvcVccFrKey,
+ spvcVccTranslationMode = 2}).
+
+-record(spvcVccDyn, {spvcVccEntry,
+ spvcVccLastReleaseCause,
+ spvcVccLastReleaseDiagnostic,
+ spvcVccRetryTimer = 0,
+ spvcVccRetryFailures = 0,
+ currentState,
+ crankBackCounter = 0}).
+
+-record(spvcFailures, {dummy_key,
+ spvcCallFailuresTrapEnable = 2,
+ spvcNotificationInterval = 30,
+ backoff_interval = 0.100000,
+ delay_factor = 2,
+ max_delay = 200000}).
+
+-record(spvcCounters, {key,
+ value}).
+
+-record(spvcEventIndicator, {dummy_key,
+ spvcTimerInd = 2,
+ spvcSendEventInd = 2}).
+
+-record(spvcIndexNext, {dummy_key,
+ schedVccIndexNext = 1,
+ schedVpcIndexNext = 1}).
+
+-record(spvcHcIdToTp, {hcId,
+ tpEntry}).
+
+-record(spvcTpToHcId, {tpEntry,
+ hcId,
+ orig_number,
+ orig_vpi,
+ orig_vci,
+ orig_dlci,
+ frKey}).
+
+-record(spvcSchedVpc, {schedVpcIndex,
+ schedVpcSource,
+ schedVpcTargetAddr,
+ schedVpcTargetSelType,
+ schedVpcTargetVpi,
+ schedVpcRetryInt,
+ schedVpcRetryThres,
+ schedVpcRetryLimit,
+ schedVpcOpTime,
+ schedVpcOpInd,
+ schedVpcOpStatus,
+ schedVpcTimerRef,
+ schedVpcRowStatus,
+ schedVpcUserName,
+ schedVpcProviderName,
+ schedVpcFaultCause,
+ schedVpcRerCap = false}).
+
+-record(spvcSchedVcc, {schedVccIndex,
+ schedVccSource,
+ schedVccTargetAddr,
+ schedVccTargetSelType,
+ schedVccTargetVpi,
+ schedVccTargetVci,
+ schedVccRetryInt,
+ schedVccRetryThres,
+ schedVccRetryLimit,
+ schedVccOpTime,
+ schedVccOpInd,
+ schedVccOpStatus,
+ schedVccTimerRef,
+ schedVccRowStatus,
+ schedVccUserName,
+ schedVccProviderName,
+ schedVccFaultCause,
+ schedVccRerCap = false}).
+
+-record(spvcCurrAlarm, {key,
+ fault_id,
+ data}).
+
+-record(spvcChg, {key,
+ data}).
+
+-record(spvcBackoff, {key,
+ delay_time,
+ flag}).
+
+-record(spvcAutoVp, {entry,
+ lastChange,
+ receiveTrafficDescrIndex,
+ transmitTrafficDescrIndex,
+ ccIdentifier,
+ connId,
+ mpId,
+ leafId,
+ chargingIndicator = 1,
+ remoteChargingInd = 1,
+ chargablePartyIdentifier,
+ segmentEndPoint = 2,
+ rowStatus,
+ castType = 1,
+ connKind,
+ serviceType = 2,
+ endPointData,
+ continuityCheck = 1,
+ upcNpcMode = 2,
+ preventInbandCc = 1,
+ monAisRdi = 2,
+ adminStatus,
+ autoTarget = 1,
+ schedulingFlag = 2,
+ application = [],
+ remoteData,
+ vpccAdminStatus = 2,
+ contCheckSearch = 1,
+ pmSearch = 1,
+ lastBuffFlagRead,
+ shapingMode = 1,
+ groupShapingId}).
+
+-record(spvcAutoVc, {entry,
+ lastChange,
+ receiveTrafficDescrIndex,
+ transmitTrafficDescrIndex,
+ ccIdentifier,
+ connId,
+ mpId,
+ leafId,
+ chargingIndicator = 1,
+ remoteChargingInd = 1,
+ chargablePartyIdentifier,
+ packetDiscard = 2,
+ segmentEndPoint = 2,
+ rowStatus,
+ castType = 1,
+ connKind,
+ continuityCheck = 1,
+ upcNpcMode = 2,
+ endPointData,
+ preventInbandCc = 1,
+ monAisRdi = 2,
+ autoTarget = 1,
+ schedulingFlag = 2,
+ application = [],
+ remoteData,
+ vcccAdminStatus = 2,
+ contCheckSearch = 1,
+ pmSearch = 1,
+ lastBuffFlagRead,
+ chargingIfChanid,
+ shapingMode = 1}).
+
+-record(spvcAutoAtd, {index,
+ type,
+ param1 = 0,
+ param2 = 0,
+ param3 = 0,
+ param4 = 0,
+ param5 = 0,
+ trafficQoSClass = 0,
+ rowStatus = 1,
+ serviceCategory = 6,
+ vcCapability = 1,
+ name = [],
+ userCounter = 0}).
+
+-record(spvcAutoAbr, {index,
+ icr,
+ tbe = 16277215,
+ frtt = 0,
+ rdf = 11,
+ rif = 11,
+ nrm = 4,
+ trm = 7,
+ cdf = 3,
+ adtf = 50,
+ rowStatus = 1}).
+
+-record(spvcLatestErrorCode, {key,
+ errorCode}).
+
+-record(spvcVcDyn, {vclEntry,
+ vclCcIdentifier,
+ vclConnId,
+ vclMpId,
+ vclLeafId}).
+
+-record(spvcVpDyn, {vplEntry,
+ vplCcIdentifier,
+ vplConnId,
+ vplMpId,
+ vplLeafId}).
+
+-record(spvcObj, {spvcEntry,
+ spvcTargetAddress,
+ spvcTargetSelectType,
+ spvcTargetVpi,
+ spvcTargetVci,
+ spvcLastReleaseCause,
+ spvcLastReleaseDiagnostic,
+ spvcRetryInterval = 1000,
+ spvcRetryTimer = 0,
+ spvcRetryThreshold = 1,
+ spvcRetryFailures = 0,
+ spvcRetryLimit = 15,
+ spvcRowStatus,
+ spvcUserName,
+ spvcProviderName,
+ currentState,
+ spvcTargetDlci,
+ spvcTargetType,
+ spvcApplication,
+ spvcFrKey,
+ spvcVccTranslationMode = 2,
+ spvcRerCap = false,
+ spvcRerStatus = false}).
+
+-record(spvcTargetVc, {entry,
+ userName = [],
+ providerName = [],
+ opState,
+ rowStatus}).
+
+-record(spvcTargetVp, {entry,
+ userName = [],
+ providerName = [],
+ opState,
+ rowStatus}).
+
+-record(spvcReestablishTimer, {time,
+ timer_id,
+ module,
+ function,
+ args}).
+
+-record(spvcRerVp, {entry,
+ rerCap,
+ rerData}).
+
+-record(spvcRerVc, {entry,
+ rerCap,
+ rerData}).
+
+-record(spvcHcEtStat, {key,
+ counter = 0}).
+
+-record(spvcSaEtStat, {key,
+ counter = 0}).
+
+-file("./spvcOrig.erl", 210).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/spvcDefines.hrl", 1).
+
+-hrl_id('41/190 55-CNA 121 64 Ux').
+
+-hrl_vsn('/main/R6A/R7A/R7D/R8B/3').
+
+-hrl_date('2003-02-21').
+
+-hrl_author(etxhebl).
+
+-file("./spvcOrig.erl", 211).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/spvcFr.hrl", 1).
+
+-hrl_id('48/190 55-CNA 121 64 Ux').
+
+-hrl_vsn('/main/R7A/R7D/2').
+
+-hrl_date('2001-12-06').
+
+-hrl_author(etxhtb).
+
+-record(spvcFr, {spvcFrEntry,
+ spvcFrAtmEntry,
+ spvcFrTargetAddress,
+ spvcFrTargetSelectType,
+ spvcFrTargetIdentifier,
+ spvcFrTargetVpi,
+ spvcFrTargetVci,
+ spvcFrAtmTranslation,
+ spvcFrLastReleaseCause,
+ spvcFrLastReleaseDiagnostic,
+ spvcFrAdminStatus,
+ spvcFrRetryInterval = 1000,
+ spvcFrRetryTimer = 0,
+ spvcFrRetryThreshold = 1,
+ spvcFrRetryFailures = 0,
+ spvcFrRetryLimit = 15,
+ spvcFrRowStatus,
+ spvcFrUserName,
+ spvcFrProviderName,
+ currentState}).
+
+-record(spvcFrPerm, {spvcFrEntry,
+ spvcFrAtmEntry,
+ spvcFrAtmTranslation,
+ spvcFrAdminStatus,
+ spvcFrConnect}).
+
+-record(spvcFrAddress, {addressEntry,
+ addressRowStatus}).
+
+-record(spvcFrAddressToIf, {address,
+ if_index}).
+
+-record(fr_end_point, {ifIndex,
+ dlci}).
+
+-record(fr_atm_translation, {routedIp = off,
+ routedOsi = off,
+ otherRouted = off,
+ arpTranslation = off}).
+
+-record(link_layer_core_parameters, {outgoing_max_ifs,
+ incoming_max_ifs}).
+
+-record(priority_and_service_class, {outgoing_transfer_priority,
+ incoming_transfer_priority,
+ outgoing_discard_priority,
+ incoming_discard_priority}).
+
+-file("./spvcOrig.erl", 212).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../derived_hrl/mib/AXD301-PCH-MIB.hrl", 1).
+
+-file("./spvcOrig.erl", 213).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../derived_hrl/mib/AXD301-SPVC-MIB.hrl", 1).
+
+-file("./spvcOrig.erl", 214).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../derived_hrl/mib/AXD301-FRSPVC-MIB.hrl", 1).
+
+-file("./spvcOrig.erl", 215).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/sysDefines.hrl", 1).
+
+-hrl_id('3/190 55-CNA 121 70').
+
+-hrl_vsn('/main/Inc3/Inc4/Inc5/R3B/R4A/R5B/R6A/R7A/R8B/2').
+
+-hrl_date('2002-06-07').
+
+-hrl_author(etxjotj).
+
+-file("./spvcOrig.erl", 216).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/hciMsg.hrl", 1).
+
+-hrl_id('4/190 55-CNA 121 159 Ux').
+
+-hrl_vsn('/main/R7A/R8B/10').
+
+-hrl_date('2003-02-21').
+
+-hrl_author(etxmexa).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/hciComp.hrl", 1).
+
+-hrl_id('3/190 55-CNA 121 159 Ux').
+
+-hrl_vsn('/main/R7A/1').
+
+-hrl_date('00-03-22').
+
+-hrl_author(etxmexa).
+
+-record(hci_comp_info, {required_FC = 0,
+ desired_FC = 0}).
+
+-record(hci_comp_res, {not_supported_required_FCs,
+ not_supported_desired_FCs,
+ all_supported_FCs}).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/hciMsg.hrl", 14).
+
+-record(hci_add_party, {hci_cpn,
+ hci_aal,
+ hci_bhli,
+ hci_blli,
+ hci_blli_bici,
+ hci_bsco,
+ hci_epr,
+ hci_e2etd,
+ hci_noti,
+ hci_cpsa,
+ hci_clpn,
+ hci_clpsa,
+ hci_cpn_soft,
+ hci_clpn_soft,
+ hci_geidt_list = [],
+ hci_dtl_bin_list = [],
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_add_party_ack, {hci_epr,
+ hci_aal,
+ hci_blli,
+ hci_blli_bici,
+ hci_e2etd,
+ hci_noti,
+ hci_cpn_soft,
+ hci_cnosa,
+ hci_cno,
+ hci_geidt_list = [],
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_add_party_rej, {hci_cause,
+ hci_epr,
+ hci_geidt_list = [],
+ hci_cb,
+ hci_pa_list = [],
+ hci_internal_rel_info,
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_alerting, {hci_mci,
+ hci_unrps,
+ hci_cdpi,
+ hci_epr,
+ hci_prog_list = [],
+ hci_nbc,
+ hci_nbhlc,
+ hci_noti,
+ hci_geidt_list = [],
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_ssie,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_b_resources, {hci_rem_dataB,
+ hci_vpiB,
+ hci_vciB,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_connect, {hci_mci,
+ hci_unrps,
+ hci_aal,
+ hci_blli,
+ hci_blli_bici,
+ hci_epr,
+ hci_atd,
+ hci_e2etd,
+ hci_noti,
+ hci_abrs,
+ hci_abra,
+ hci_nbc,
+ hci_nbhlc,
+ hci_nbllc,
+ hci_prog_list = [],
+ hci_geidt_list = [],
+ hci_eqos,
+ hci_cpn_soft,
+ hci_cnosa,
+ hci_cno,
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_rem_dataB,
+ hci_con_dir = both,
+ hci_ssie,
+ hci_rer_services,
+ hci_rer,
+ hci_opt_traf,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_drop_party, {hci_cause,
+ hci_epr,
+ hci_noti,
+ hci_geidt_list = [],
+ hci_pa_list = [],
+ hci_internal_rel_info,
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_local_connect, {hci_rem_data,
+ hci_con_dir,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_local_connected, {hci_rem_data,
+ hci_con_dir,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_local_disconnect, {hci_discon_dir,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_local_disconnected, {hci_data,
+ hci_prot_comp}).
+
+-record(hci_notify, {hci_epr,
+ hci_noti,
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_party_alerting, {hci_epr,
+ hci_noti,
+ hci_geidt_list = [],
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_progress, {hci_mci,
+ hci_unrps,
+ hci_cdpi,
+ hci_prog_list = [],
+ hci_nbc,
+ hci_nbhlc,
+ hci_noti,
+ hci_pa_list = [],
+ hci_gat_list = [],
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_release, {hci_mci,
+ hci_unrps,
+ hci_cause_list = [],
+ hci_noti,
+ hci_prog_list = [],
+ hci_geidt_list = [],
+ hci_cb,
+ hci_pa_list = [],
+ hci_internal_rel_info,
+ hci_gat_list = [],
+ hci_ssie,
+ hci_rer_cause,
+ hci_data,
+ hci_prot_comp,
+ hci_internal_dbg_cc,
+ hci_internal_dbg_l3}).
+
+-record(hci_setup, {hci_mci,
+ hci_unrps,
+ hci_atd,
+ hci_bbc,
+ hci_qos,
+ hci_cpn,
+ hci_aal,
+ hci_bhli,
+ hci_blli_brep,
+ hci_blli_bici,
+ hci_bsco,
+ hci_epr,
+ hci_lpt,
+ hci_e2etd,
+ hci_noti,
+ hci_abrs,
+ hci_abra,
+ hci_prog_list = [],
+ hci_eqos,
+ hci_cpsa_list = [],
+ hci_clpn,
+ hci_bici_clpn,
+ hci_clpsa_list = [],
+ hci_cgpc,
+ hci_nbc_brep,
+ hci_nbhlc_list = [],
+ hci_nbllc_brep,
+ hci_conss,
+ hci_geidt_list = [],
+ hci_cpn_soft,
+ hci_clpn_soft,
+ hci_dtl_bin_list = [],
+ hci_pa_list = [],
+ hci_ncci,
+ hci_routing_address,
+ hci_protocol_internal_info,
+ hci_gat_list = [],
+ hci_con_dir = both,
+ hci_ssie,
+ hci_rer_services,
+ hci_rer,
+ hci_opt_traf,
+ hci_data_setup,
+ hci_prot_comp}).
+
+-record(hci_setup_ack, {hci_assign,
+ hci_rem_dataB,
+ hci_con_dir = both,
+ hci_vpiB,
+ hci_vciB,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_status, {hci_state,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_status_enq, {hci_state,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_remote_data, {hci_prot_type,
+ hci_data,
+ hci_dummy1,
+ hci_dummy2}).
+
+-record(hci_unrec, {hci_mci,
+ hci_head,
+ hci_binary,
+ hci_data,
+ hci_prot_comp}).
+
+-record(hci_atd, {hci_pci,
+ hci_apci,
+ hci_fwd_pcr_clp_0,
+ hci_bwd_pcr_clp_0,
+ hci_fwd_pcr_clp_0_1,
+ hci_bwd_pcr_clp_0_1,
+ hci_fwd_scr_clp_0,
+ hci_bwd_scr_clp_0,
+ hci_fwd_scr_clp_0_1,
+ hci_bwd_scr_clp_0_1,
+ hci_fwd_mbs_clp_0,
+ hci_bwd_mbs_clp_0,
+ hci_fwd_mbs_clp_0_1,
+ hci_bwd_mbs_clp_0_1,
+ hci_best_effort_ind = 0,
+ hci_fwd_frame_discard = 0,
+ hci_bwd_frame_discard = 0,
+ hci_tagging_bwd = 0,
+ hci_tagging_fwd = 0,
+ hci_fwd_abr_mcr,
+ hci_bwd_abr_mcr,
+ hci_binary}).
+
+-record(hci_bbc, {hci_pci,
+ hci_bearer_class,
+ hci_atm_transfer_capability,
+ hci_user_plane_connection_configuration,
+ hci_susceptibility_to_clipping,
+ hci_binary}).
+
+-record(hci_cause, {hci_pci,
+ hci_location,
+ hci_cause_value,
+ hci_diagnostics_list = [],
+ hci_binary}).
+
+-record(hci_cpn, {hci_pci,
+ hci_type_of_number,
+ hci_intern_netw_numb_indic,
+ hci_numbering_plan_indicator,
+ hci_number_digits,
+ hci_orig_native = false}).
+
+-record(hci_clpn, {hci_pci,
+ hci_type_of_number,
+ hci_numbering_plan_indicator,
+ hci_presentation_indicator,
+ hci_screening_indicator,
+ hci_number_digits,
+ hci_incomplete_indicator = 0,
+ hci_binary}).
+
+-record(hci_cno, {hci_type_of_number,
+ hci_numbering_plan_indicator,
+ hci_presentation_indicator,
+ hci_screening_indicator,
+ hci_number_digits,
+ hci_binary}).
+
+-record(hci_cnosa, {hci_binary}).
+
+-record(hci_cpn_soft, {hci_select_type,
+ hci_soft_vpi,
+ hci_soft_vci,
+ hci_soft_dlci,
+ hci_binary}).
+
+-record(hci_clpn_soft, {hci_soft_vpi,
+ hci_soft_vci,
+ hci_soft_dlci,
+ hci_binary}).
+
+-record(hci_rer_services, {hci_inter_req_hard,
+ hci_inter_cap_hard,
+ hci_intra_req_soft,
+ hci_intra_req_hard,
+ hci_intra_cap_asym,
+ hci_intra_cap_sym,
+ hci_intra_cap_hard,
+ hci_binary}).
+
+-record(hci_rer, {hci_func_addr,
+ hci_endpoint_key,
+ hci_switchover,
+ hci_incarnation,
+ hci_pnni_cumul_fw_max_cell_td,
+ hci_cumul_fw_p2p_cdv,
+ hci_cumul_bw_p2p_cdv,
+ hci_binary}).
+
+-record(hci_rer_cause, {hci_rer_rel_cause,
+ hci_binary}).
+
+-record(hci_opt_traf, {hci_origin,
+ hci_cumul_fw_aw,
+ hci_cumul_bw_aw,
+ hci_binary}).
+
+-record(hci_qos, {hci_pci,
+ hci_qos_class_fwd,
+ hci_qos_class_bwd,
+ hci_binary}).
+
+-record(hci_aal, {hci_pci,
+ hci_binary}).
+
+-record(hci_bhli, {hci_pci,
+ hci_binary}).
+
+-record(hci_blli_brep, {hci_brep,
+ hci_blli_list = []}).
+
+-record(hci_blli, {hci_binary}).
+
+-record(hci_blli_bici, {hci_repeated,
+ hci_priority,
+ hci_pci,
+ hci_binary}).
+
+-record(hci_cpsa, {hci_pci,
+ hci_binary}).
+
+-record(hci_clpsa, {hci_pci,
+ hci_binary}).
+
+-record(hci_gat, {hci_binary}).
+
+-record(hci_epr, {hci_epr_type,
+ hci_epr_value,
+ hci_epr_flag,
+ hci_binary}).
+
+-record(hci_eqos, {hci_origin,
+ hci_acc_fwd_p2p_cdv,
+ hci_acc_bwd_p2p_cdv,
+ hci_cum_fwd_p2p_cdv,
+ hci_cum_bwd_p2p_cdv,
+ hci_acc_fwd_clr,
+ hci_acc_bwd_clr,
+ hci_binary}).
+
+-record(hci_brep, {hci_binary}).
+
+-record(hci_bsco, {hci_binary}).
+
+-record(hci_noti, {hci_binary}).
+
+-record(hci_abrs, {hci_fwd_abr_icr,
+ hci_bwd_abr_icr,
+ hci_fwd_abr_tbe,
+ hci_bwd_abr_tbe,
+ hci_cum_rm_fix_round_trip,
+ hci_fwd_rif,
+ hci_bwd_rif,
+ hci_fwd_rdf,
+ hci_bwd_rdf,
+ hci_binary}).
+
+-record(hci_abra, {hci_fwd_nrm,
+ hci_fwd_trm,
+ hci_fwd_cdf,
+ hci_fwd_atdf,
+ hci_bwd_nrm,
+ hci_bwd_trm,
+ hci_bwd_cdf,
+ hci_bwd_atdf,
+ hci_binary}).
+
+-record(hci_prog, {hci_coding_std,
+ hci_location,
+ hci_prog_desc,
+ hci_binary}).
+
+-record(hci_nbc_brep, {hci_brep,
+ hci_nbc_list = []}).
+
+-record(hci_nbc, {hci_binary}).
+
+-record(hci_nbhlc, {hci_binary}).
+
+-record(hci_nbllc_brep, {hci_brep,
+ hci_nbllc_list = []}).
+
+-record(hci_nbllc, {hci_binary}).
+
+-record(hci_geidt, {hci_binary}).
+
+-record(hci_conss, {hci_type_of_conn_scope,
+ hci_conn_scope,
+ hci_binary}).
+
+-record(hci_e2etd, {hci_pci,
+ hci_cumul_td,
+ hci_max_td,
+ hci_pnni_cumul_td,
+ hci_pnni_accept_fwd_max_td,
+ hci_netw_gen}).
+
+-record(hci_cdpi, {hci_pci,
+ hci_cdpci,
+ hci_cdpsi,
+ hci_binary}).
+
+-record(hci_cgpc, {hci_pci,
+ hci_binary}).
+
+-record(hci_lpt, {hci_pci,
+ hci_ptype}).
+
+-record(hci_cb, {hci_cb_level,
+ hci_bl_transit_type,
+ hci_bl_node_id,
+ hci_bl_link_proc_node_id,
+ hci_bl_link_port_id,
+ hci_bl_link_succ_node_id,
+ cause_value,
+ hci_cb_diagnostics,
+ hci_binary}).
+
+-record(hci_pa, {hci_ie_id,
+ hci_coding,
+ hci_action,
+ hci_length,
+ hci_binary,
+ hci_error_type}).
+
+-record(hci_ncci, {hci_pci,
+ hci_ni,
+ hci_point_code,
+ hci_call_id}).
+
+-record(hci_ssie, {hci_ssie_sas = [],
+ hci_binary}).
+
+-record(hci_sas, {hci_sas_vsn,
+ hci_sas_transp_ind,
+ hci_sas_flow_ind,
+ hci_sas_discard,
+ hci_sas_scope,
+ hci_sas_relative_id,
+ hci_binary}).
+
+-record(hci_data, {hci_hcid,
+ hci_sender_ifindex,
+ hci_sender_hcid}).
+
+-record(hci_data_setup, {hci_hcidA,
+ hci_pidA,
+ hci_protA,
+ hci_protB,
+ hci_portB,
+ hci_hcidB,
+ hci_rem_dataA,
+ hci_assign,
+ hci_ifindexB,
+ hci_node_id,
+ hci_succ_node_id,
+ hci_ifindexA,
+ hci_vpiA,
+ hci_vciA,
+ hci_cpA,
+ hci_cpB}).
+
+-record(hci_prot_comp, {hci_requiredFC = 0,
+ hci_desiredFC = 0}).
+
+-file("./spvcOrig.erl", 217).
+
+-file("/export/localhome/locmacr/wrk/axd_r11/ATS_CRA12002/SPVC_CNA12164/src/../../../inc/ccCd.hrl", 1).
+
+-hrl_id('13/190 55-CNA 121 101 Ux').
+
+-hrl_vsn('/main/R6A/R7A/R8A/R8B/8').
+
+-hrl_date('2003-02-21').
+
+-hrl_author(etxmexa).
+
+-record(ccCdRR, {hcid,
+ vpi,
+ vci,
+ ifindexA,
+ call_type,
+ spvc = false,
+ reserve = yes,
+ etA,
+ destdata,
+ leafdata,
+ loopdata,
+ l3,
+ l3_loop,
+ cc}).
+
+-record(ccCdRD, {destid,
+ loopdata,
+ cc}).
+
+-record(ccCdRL, {leafid,
+ protTypeB,
+ loopdata,
+ l3,
+ l3_loop,
+ cc}).
+
+-record(ccCdDD, {hcid,
+ hcidA,
+ vpi,
+ vci,
+ ifindexB,
+ portB,
+ call_type,
+ spvc = false,
+ reserve = yes,
+ protTypeA,
+ etB,
+ leafdata,
+ loopdata,
+ l3,
+ l3_loop,
+ cc}).
+
+-record(ccCdDL, {leafid,
+ loopdata,
+ l3,
+ l3_loop,
+ cc}).
+
+-record(ccRR, {protTypeA,
+ remote_dataA,
+ remote_dataB,
+ chg_counters,
+ sc,
+ chg_decision = on,
+ cc_loop}).
+
+-record(ccRL, {hcidB,
+ charging,
+ cc_loop}).
+
+-record(ccRD, {portB,
+ ifindexB,
+ cpB,
+ vpiB,
+ vciB,
+ cc_loop}).
+
+-record(ccDD, {protTypeB,
+ remote_dataA,
+ remote_dataB,
+ ifindexA,
+ cpA,
+ vpiA,
+ vciA,
+ chg_counters,
+ sc,
+ chg_decision = on,
+ cc_loop}).
+
+-record(ccDL, {cc_loop}).
+
+-record(loopRR, {vpList,
+ nodeid,
+ succ_nodeid,
+ connection_type,
+ policing,
+ delay_contrib,
+ charging = on,
+ prev_routing_data}).
+
+-record(loopRD, {}).
+
+-record(loopRL, {msg_rec,
+ providerName,
+ userName,
+ partyId,
+ serviceIfA,
+ serviceIdA,
+ serviceIfB,
+ serviceIdB,
+ estAw,
+ dtlLevels}).
+
+-record(loopDD, {nodeid,
+ succ_nodeid,
+ vpList,
+ connection_type,
+ policing,
+ assign,
+ delay_contrib,
+ charging = on}).
+
+-record(loopDL, {msg_rec,
+ providerName,
+ userName,
+ partyId,
+ serviceIfA,
+ serviceIdA,
+ serviceIfB,
+ serviceIdB}).
+
+-record(ccLoopRR, {pidB,
+ qos,
+ atd,
+ bbc,
+ cscope,
+ e2etd,
+ eqos,
+ con_state = none,
+ con_order = both,
+ mr_flag,
+ catch_up_id,
+ cpA}).
+
+-record(ccLoopRD, {}).
+
+-record(ccLoopRL, {route,
+ linklist,
+ routelist,
+ failurelist = [],
+ nodeidlist,
+ cb,
+ cpn,
+ dtl,
+ routing_state,
+ assign,
+ timer_counter = 0,
+ timer_ref,
+ status_enq_ind,
+ link_CB,
+ node_CB,
+ pnnir_rlp,
+ pnni_only}).
+
+-record(ccLoopDD, {pidA,
+ con_state = none,
+ con_order = both,
+ mr_flag,
+ catch_up_id,
+ cpB}).
+
+-record(ccLoopDL, {timer_counter = 0,
+ timer_ref,
+ status_enq_ind}).
+
+-file("./spvcOrig.erl", 218).
+
+-file("/export/localhome/locmacr/built/lib/erlang/lib/snmp-4.1.2/include/STANDARD-MIB.hrl", 1).
+
+-file("./spvcOrig.erl", 219).
+
+error_handler({From,Tag},{M,F,Args},EXITReason) ->
+ spvcLib:do_report(sccm,M,F,Args,"",EXITReason).
+
+connect(HcId,Connect,Key) ->
+ debug_disabled,
+ Obj = spvcDataBase:db_read({spvcObj,Key}),
+ orig_state_machine(Obj#spvcObj.currentState,connect_nu,Obj,[HcId,Connect]).
+
+release_nu(HcId,Release,Key) ->
+ debug_disabled,
+ Obj = spvcDataBase:db_read({spvcObj,Key}),
+ spvcDataBase:db_delete({spvcHcIdToTp,HcId}),
+ orig_state_machine(Obj#spvcObj.currentState,release_nu,Obj,[HcId,Release]).
+
+release_comp_nu(HcId,Release_comp,Key) ->
+ debug_disabled,
+ Obj = spvcDataBase:db_read({spvcObj,Key}),
+ spvcDataBase:db_delete({spvcHcIdToTp,HcId}),
+ orig_state_machine(Obj#spvcObj.currentState,release_comp_nu,Obj,[HcId,Release_comp]).
+
+release_incumbent(HcId,Release) ->
+ debug_disabled,
+ release_incumbent2(spvcDataBase:db_read({spvcHcIdToTp,HcId}),Release).
+
+release_incumbent2(SpvcHcIdToTp,Release) ->
+ release_incumbent3(SpvcHcIdToTp#spvcHcIdToTp.tpEntry,Release).
+
+release_incumbent3({orig,If,Vpi,Vci,Leaf},Release) ->
+ release_incumbent4({If,Vpi,Vci,Leaf},Release);
+release_incumbent3({orig,If,Vpi,Leaf},Release) ->
+ release_incumbent4({If,Vpi,Leaf},Release).
+
+release_incumbent4(TpKey,Release) ->
+ Spvc = spvcDataBase:db_read({spvcObj,TpKey}),
+ active = Spvc#spvcObj.currentState,
+ orig_state_machine(active,release_incumbent,Spvc,[Release]).
+
+switch_over(HcId,{If,Vpi,Vci}) ->
+ Key = case {If,Vpi,Vci} of
+ {If_Value,Vpi_Value,Vci_Value} when integer(Vci_Value) ->
+ {If_Value,Vpi_Value,Vci_Value,1};
+ {If_Value,Vpi_Value,_} ->
+ {If_Value,Vpi_Value,1};
+ {If_Value,Vpi_Value} ->
+ {If_Value,Vpi_Value,1}
+ end,
+ Spvc = spvcDataBase:db_read({spvcObj,Key}),
+ do_switch_over(HcId,Spvc);
+switch_over(HcId,{If,Vpi}) ->
+ Key = case {If,Vpi,no_vc} of
+ {If_Value,Vpi_Value,Vci_Value} when integer(Vci_Value) ->
+ {If_Value,Vpi_Value,Vci_Value,1};
+ {If_Value,Vpi_Value,_} ->
+ {If_Value,Vpi_Value,1};
+ {If_Value,Vpi_Value} ->
+ {If_Value,Vpi_Value,1}
+ end,
+ Spvc = spvcDataBase:db_read({spvcObj,Key}),
+ do_switch_over(HcId,Spvc).
+
+do_switch_over(HcId,Spvc) ->
+ State = Spvc#spvcObj.currentState,
+ orig_state_machine(State,switch_over,Spvc,[HcId]).
+
+gen_set(Type,Row,Cols) ->
+ debug_disabled,
+ gen_set(Type,Row,Cols,undefined).
+
+gen_set(Type,Row,Cols,FrKey) ->
+ debug_disabled,
+ case lists:keysearch(case {case Row of
+ {_,_,_,_} ->
+ spvcVcc;
+ {_,_,_} ->
+ spvcVpc;
+ {_,_} ->
+ spvcFr;
+ [_,_,_,_] ->
+ spvcVcc;
+ [_,_,_] ->
+ spvcVpc;
+ [_,_] ->
+ spvcFr
+ end,rowStatus} of
+ {spvcVcc,targetAddress} ->
+ 2;
+ {spvcVcc,selectType} ->
+ 3;
+ {spvcVcc,targetVpi} ->
+ 18;
+ {spvcVcc,targetVci} ->
+ 5;
+ {spvcVcc,releaseCause} ->
+ 6;
+ {spvcVcc,releaseDiagnostic} ->
+ 7;
+ {spvcVcc,retryInterval} ->
+ 10;
+ {spvcVcc,retryTimer} ->
+ 11;
+ {spvcVcc,retryThreshold} ->
+ 12;
+ {spvcVcc,retryFailures} ->
+ 13;
+ {spvcVcc,retryLimit} ->
+ 14;
+ {spvcVcc,rowStatus} ->
+ 15;
+ {spvcVcc,restart} ->
+ 9;
+ {spvcVcc,targetSelectType_any} ->
+ 2;
+ {spvcVcc,targetSelectType_required} ->
+ 1;
+ {spvcVpc,targetAddress} ->
+ 2;
+ {spvcVpc,selectType} ->
+ 3;
+ {spvcVpc,targetVpi} ->
+ 15;
+ {spvcVpc,releaseCause} ->
+ 5;
+ {spvcVpc,releaseDiagnostic} ->
+ 6;
+ {spvcVpc,retryInterval} ->
+ 9;
+ {spvcVpc,retryTimer} ->
+ 10;
+ {spvcVpc,retryThreshold} ->
+ 11;
+ {spvcVpc,retryFailures} ->
+ 12;
+ {spvcVpc,retryLimit} ->
+ 13;
+ {spvcVpc,rowStatus} ->
+ 14;
+ {spvcVpc,restart} ->
+ 8;
+ {spvcVpc,targetSelectType_any} ->
+ 2;
+ {spvcVpc,targetSelectType_required} ->
+ 1;
+ {spvcFr,targetAddress} ->
+ 3;
+ {spvcFr,selectType} ->
+ 5;
+ {spvcFr,identifier} ->
+ 6;
+ {spvcFr,targetVpi} ->
+ 7;
+ {spvcFr,targetVci} ->
+ 8;
+ {spvcFr,translation} ->
+ 9;
+ {spvcFr,releaseCause} ->
+ 10;
+ {spvcFr,releaseDiagnostic} ->
+ 11;
+ {spvcFr,operStatus} ->
+ 12;
+ {spvcFr,adminStatus} ->
+ 13;
+ {spvcFr,restart} ->
+ 14;
+ {spvcFr,retryInterval} ->
+ 15;
+ {spvcFr,retryTimer} ->
+ 16;
+ {spvcFr,retryThreshold} ->
+ 17;
+ {spvcFr,retryFailures} ->
+ 18;
+ {spvcFr,retryLimit} ->
+ 19;
+ {spvcFr,lastChange} ->
+ 20;
+ {spvcFr,rowStatus} ->
+ 21
+ end,1,Cols) of
+ {value,{_,4}} ->
+ debug_disabled,
+ mnesia:dirty_update_counter(spvcHcEtStat,spvcLib:get_board(hd(Row)),1),
+ case get_link_state(case Row of
+ Row when record(Row,spvcObj) ->
+ case Row#spvcObj.spvcEntry of
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value
+ end;
+ Row when record(Row,spvcVcc) ->
+ {If_Value,_,_,_} = Row#spvcVcc.spvcVccEntry,
+ If_Value;
+ Row when record(Row,spvcVpc) ->
+ {If_Value,_,_} = Row#spvcVpc.spvcVpcEntry,
+ If_Value;
+ Row when record(Row,spvcVpcPerm) ->
+ {If_Value,_,_} = Row#spvcVpcPerm.spvcVpcEntry,
+ If_Value;
+ Row when record(Row,spvcVccPerm) ->
+ {If_Value,_,_,_} = Row#spvcVccPerm.spvcVccEntry,
+ If_Value;
+ Row when record(Row,spvcTargetVc) ->
+ {If_Value,_,_} = Row#spvcTargetVc.entry,
+ If_Value;
+ Row when record(Row,spvcTargetVp) ->
+ {If_Value,_} = Row#spvcTargetVp.entry,
+ If_Value;
+ Row when record(Row,pchVc) ->
+ {If_Value,_,_} = Row#pchVc.vclEntry,
+ If_Value;
+ Row when record(Row,pchVp) ->
+ {If_Value,_} = Row#pchVp.vplEntry,
+ If_Value;
+ Row when record(Row,spvcFr) ->
+ {If_Value,_} = Row#spvcFr.spvcFrEntry,
+ If_Value;
+ Row when record(Row,spvcFrPerm) ->
+ {If_Value,_} = Row#spvcFrPerm.spvcFrEntry,
+ If_Value;
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value;
+ {If_Value,_} ->
+ If_Value;
+ [If_Value|_] ->
+ If_Value;
+ _ ->
+ error
+ end) of
+ disabled ->
+ orig_state_machine(null,createAndGo_disabled,[],[Row,Cols,Type,FrKey]);
+ enabled ->
+ orig_state_machine(null,createAndGo_enabled,[],[Row,Cols,Type,FrKey])
+ end;
+ {value,{_,5}} ->
+ debug_disabled,
+ mnesia:dirty_update_counter(spvcHcEtStat,spvcLib:get_board(hd(Row)),1),
+ orig_state_machine(null,createAndWait,[],[Row,Cols,Type,FrKey]);
+ {value,{_,1}} ->
+ debug_disabled,
+ case spvcDataBase:db_read({spvcObj,list_to_tuple(Row)}) of
+ [] ->
+ ok;
+ Spvc ->
+ case get_link_state(case Row of
+ Row when record(Row,spvcObj) ->
+ case Row#spvcObj.spvcEntry of
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value
+ end;
+ Row when record(Row,spvcVcc) ->
+ {If_Value,_,_,_} = Row#spvcVcc.spvcVccEntry,
+ If_Value;
+ Row when record(Row,spvcVpc) ->
+ {If_Value,_,_} = Row#spvcVpc.spvcVpcEntry,
+ If_Value;
+ Row when record(Row,spvcVpcPerm) ->
+ {If_Value,_,_} = Row#spvcVpcPerm.spvcVpcEntry,
+ If_Value;
+ Row when record(Row,spvcVccPerm) ->
+ {If_Value,_,_,_} = Row#spvcVccPerm.spvcVccEntry,
+ If_Value;
+ Row when record(Row,spvcTargetVc) ->
+ {If_Value,_,_} = Row#spvcTargetVc.entry,
+ If_Value;
+ Row when record(Row,spvcTargetVp) ->
+ {If_Value,_} = Row#spvcTargetVp.entry,
+ If_Value;
+ Row when record(Row,pchVc) ->
+ {If_Value,_,_} = Row#pchVc.vclEntry,
+ If_Value;
+ Row when record(Row,pchVp) ->
+ {If_Value,_} = Row#pchVp.vplEntry,
+ If_Value;
+ Row when record(Row,spvcFr) ->
+ {If_Value,_} = Row#spvcFr.spvcFrEntry,
+ If_Value;
+ Row when record(Row,spvcFrPerm) ->
+ {If_Value,_} = Row#spvcFrPerm.spvcFrEntry,
+ If_Value;
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value;
+ {If_Value,_} ->
+ If_Value;
+ [If_Value|_] ->
+ If_Value;
+ _ ->
+ error
+ end) of
+ disabled ->
+ orig_state_machine(Spvc#spvcObj.currentState,activate_disabled,Spvc,Cols);
+ enabled ->
+ orig_state_machine(Spvc#spvcObj.currentState,activate_enabled,Spvc,Cols)
+ end
+ end;
+ {value,{_,6}} ->
+ debug_disabled,
+ case spvcDataBase:db_read({spvcObj,list_to_tuple(Row)}) of
+ [] ->
+ ok;
+ Spvc ->
+ mnesia:dirty_update_counter(spvcHcEtStat,spvcLib:get_board(hd(Row)),- 1),
+ orig_state_machine(Spvc#spvcObj.currentState,destroy,Spvc,Cols)
+ end;
+ {value,{_,2}} ->
+ debug_disabled,
+ case spvcDataBase:db_read({spvcObj,list_to_tuple(Row)}) of
+ [] ->
+ mnesia:dirty_update_counter(spvcHcEtStat,spvcLib:get_board(hd(Row)),1),
+ ok;
+ Spvc ->
+ orig_state_machine(Spvc#spvcObj.currentState,not_in_service,Spvc,Cols)
+ end;
+ false ->
+ debug_disabled,
+ Spvc = spvcDataBase:db_read({spvcObj,list_to_tuple(Row)}),
+ CurrentState = Spvc#spvcObj.currentState,
+ NewSpvc = set_attrs(Spvc,Cols),
+ Restart = case {case Row of
+ {_,_,_,_} ->
+ spvcVcc;
+ {_,_,_} ->
+ spvcVpc;
+ {_,_} ->
+ spvcFr;
+ [_,_,_,_] ->
+ spvcVcc;
+ [_,_,_] ->
+ spvcVpc;
+ [_,_] ->
+ spvcFr
+ end,restart} of
+ {spvcVcc,targetAddress} ->
+ 2;
+ {spvcVcc,selectType} ->
+ 3;
+ {spvcVcc,targetVpi} ->
+ 18;
+ {spvcVcc,targetVci} ->
+ 5;
+ {spvcVcc,releaseCause} ->
+ 6;
+ {spvcVcc,releaseDiagnostic} ->
+ 7;
+ {spvcVcc,retryInterval} ->
+ 10;
+ {spvcVcc,retryTimer} ->
+ 11;
+ {spvcVcc,retryThreshold} ->
+ 12;
+ {spvcVcc,retryFailures} ->
+ 13;
+ {spvcVcc,retryLimit} ->
+ 14;
+ {spvcVcc,rowStatus} ->
+ 15;
+ {spvcVcc,restart} ->
+ 9;
+ {spvcVcc,targetSelectType_any} ->
+ 2;
+ {spvcVcc,targetSelectType_required} ->
+ 1;
+ {spvcVpc,targetAddress} ->
+ 2;
+ {spvcVpc,selectType} ->
+ 3;
+ {spvcVpc,targetVpi} ->
+ 15;
+ {spvcVpc,releaseCause} ->
+ 5;
+ {spvcVpc,releaseDiagnostic} ->
+ 6;
+ {spvcVpc,retryInterval} ->
+ 9;
+ {spvcVpc,retryTimer} ->
+ 10;
+ {spvcVpc,retryThreshold} ->
+ 11;
+ {spvcVpc,retryFailures} ->
+ 12;
+ {spvcVpc,retryLimit} ->
+ 13;
+ {spvcVpc,rowStatus} ->
+ 14;
+ {spvcVpc,restart} ->
+ 8;
+ {spvcVpc,targetSelectType_any} ->
+ 2;
+ {spvcVpc,targetSelectType_required} ->
+ 1;
+ {spvcFr,targetAddress} ->
+ 3;
+ {spvcFr,selectType} ->
+ 5;
+ {spvcFr,identifier} ->
+ 6;
+ {spvcFr,targetVpi} ->
+ 7;
+ {spvcFr,targetVci} ->
+ 8;
+ {spvcFr,translation} ->
+ 9;
+ {spvcFr,releaseCause} ->
+ 10;
+ {spvcFr,releaseDiagnostic} ->
+ 11;
+ {spvcFr,operStatus} ->
+ 12;
+ {spvcFr,adminStatus} ->
+ 13;
+ {spvcFr,restart} ->
+ 14;
+ {spvcFr,retryInterval} ->
+ 15;
+ {spvcFr,retryTimer} ->
+ 16;
+ {spvcFr,retryThreshold} ->
+ 17;
+ {spvcFr,retryFailures} ->
+ 18;
+ {spvcFr,retryLimit} ->
+ 19;
+ {spvcFr,lastChange} ->
+ 20;
+ {spvcFr,rowStatus} ->
+ 21
+ end,
+ case lists:keysearch(Restart,1,Cols) of
+ {value,{Restart,1}} ->
+ orig_state_machine(CurrentState,restart,NewSpvc,Cols);
+ _ ->
+ spvcDataBase:db_write(NewSpvc),
+ ok
+ end
+ end,
+ {noError,0}.
+
+restart_spvc(Key) ->
+ debug_disabled,
+ Spvc = spvcDataBase:db_read({spvcObj,Key}),
+ handle_restart_spvc(Spvc#spvcObj.currentState,Spvc),
+ ok.
+
+handle_restart_spvc(rest_in_peace,Spvc) ->
+ debug_disabled,
+ rest_in_peace(restart,Spvc,undefined);
+handle_restart_spvc(_,_) ->
+ ok.
+
+restart_multi_spvcs(Key) ->
+ debug_disabled,
+ Spvc = spvcDataBase:db_read({spvcObj,Key}),
+ handle_restart_multi_spvcs(Spvc#spvcObj.currentState,Spvc),
+ ok.
+
+handle_restart_multi_spvcs(rest_in_peace,Spvc) ->
+ debug_disabled,
+ handle_restart_spvc(rest_in_peace,Spvc);
+handle_restart_multi_spvcs(active,Spvc) ->
+ debug_disabled,
+ active(restart,Spvc,undefined);
+handle_restart_multi_spvcs(outgoing_callproceeding,Spvc) ->
+ debug_disabled,
+ outgoing_callproceeding(restart,Spvc,undefined);
+handle_restart_multi_spvcs(release_at_restart,Spvc) ->
+ debug_disabled,
+ release_at_restart(restart,Spvc,undefined);
+handle_restart_multi_spvcs(wait,Spvc) ->
+ debug_disabled,
+ wait(restart,Spvc,undefined);
+handle_restart_multi_spvcs(rest_in_peace,Spvc) ->
+ debug_disabled,
+ rest_in_peace(restart,Spvc,undefined);
+handle_restart_multi_spvcs(_,_) ->
+ ok.
+
+orig_state_machine(null,createAndGo_enabled,Spvc,Attrs) ->
+ null(createAndGo_enabled,Spvc,Attrs);
+orig_state_machine(null,createAndGo_disabled,Spvc,Attrs) ->
+ null(createAndGo_disabled,Spvc,Attrs);
+orig_state_machine(null,createAndWait,Spvc,Attrs) ->
+ null(createAndWait,Spvc,Attrs);
+orig_state_machine(created,activate_disabled,Spvc,Attrs) ->
+ created(activate_disabled,Spvc,Attrs);
+orig_state_machine(created,activate_enabled,Spvc,Attrs) ->
+ created(activate_enabled,Spvc,Attrs);
+orig_state_machine(created,destroy,Spvc,Attrs) ->
+ created(destroy,Spvc,Attrs);
+orig_state_machine(outgoing_callproceeding,connect_nu,Spvc,Attrs) ->
+ outgoing_callproceeding(connect_nu,Spvc,Attrs);
+orig_state_machine(outgoing_callproceeding,destroy,Spvc,Attrs) ->
+ outgoing_callproceeding(destroy,Spvc,Attrs);
+orig_state_machine(outgoing_callproceeding,restart,Spvc,Attrs) ->
+ outgoing_callproceeding(restart,Spvc,Attrs);
+orig_state_machine(outgoing_callproceeding,release_nu,Spvc,Attrs) ->
+ case get_link_state_intf(case Spvc of
+ Spvc when record(Spvc,spvcObj) ->
+ case Spvc#spvcObj.spvcEntry of
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value
+ end;
+ Spvc when record(Spvc,spvcVcc) ->
+ {If_Value,_,_,_} = Spvc#spvcVcc.spvcVccEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVpc) ->
+ {If_Value,_,_} = Spvc#spvcVpc.spvcVpcEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVpcPerm) ->
+ {If_Value,_,_} = Spvc#spvcVpcPerm.spvcVpcEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVccPerm) ->
+ {If_Value,_,_,_} = Spvc#spvcVccPerm.spvcVccEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcTargetVc) ->
+ {If_Value,_,_} = Spvc#spvcTargetVc.entry,
+ If_Value;
+ Spvc when record(Spvc,spvcTargetVp) ->
+ {If_Value,_} = Spvc#spvcTargetVp.entry,
+ If_Value;
+ Spvc when record(Spvc,pchVc) ->
+ {If_Value,_,_} = Spvc#pchVc.vclEntry,
+ If_Value;
+ Spvc when record(Spvc,pchVp) ->
+ {If_Value,_} = Spvc#pchVp.vplEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcFr) ->
+ {If_Value,_} = Spvc#spvcFr.spvcFrEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcFrPerm) ->
+ {If_Value,_} = Spvc#spvcFrPerm.spvcFrEntry,
+ If_Value;
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value;
+ {If_Value,_} ->
+ If_Value;
+ [If_Value|_] ->
+ If_Value;
+ _ ->
+ error
+ end,release_nu) of
+ disabled ->
+ outgoing_callproceeding(release_nu_disabled,Spvc,Attrs);
+ enabled ->
+ outgoing_callproceeding(release_nu_enabled,Spvc,Attrs)
+ end;
+orig_state_machine(outgoing_callproceeding,release_comp_nu,Spvc,Attrs) ->
+ case get_link_state_intf(tuple_to_list(Spvc#spvcObj.spvcEntry),release_comp_nu) of
+ disabled ->
+ outgoing_callproceeding(release_comp_nu_disabled,Spvc,Attrs);
+ enabled ->
+ outgoing_callproceeding(release_comp_nu_enabled,Spvc,Attrs)
+ end;
+orig_state_machine(outgoing_callproceeding,not_in_service,Spvc,Attrs) ->
+ outgoing_callproceeding(not_in_service,Spvc,Attrs);
+orig_state_machine(outgoing_callproceeding,activate_enabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(outgoing_callproceeding,activate_disabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(active,destroy,Spvc,Attrs) ->
+ active(destroy,Spvc,Attrs);
+orig_state_machine(active,restart,Spvc,Attrs) ->
+ active(restart,Spvc,Attrs);
+orig_state_machine(active,release_nu,Spvc,Attrs) ->
+ case cnhChi:get_link_opstate(case Spvc of
+ Spvc when record(Spvc,spvcObj) ->
+ case Spvc#spvcObj.spvcEntry of
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value
+ end;
+ Spvc when record(Spvc,spvcVcc) ->
+ {If_Value,_,_,_} = Spvc#spvcVcc.spvcVccEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVpc) ->
+ {If_Value,_,_} = Spvc#spvcVpc.spvcVpcEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVpcPerm) ->
+ {If_Value,_,_} = Spvc#spvcVpcPerm.spvcVpcEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVccPerm) ->
+ {If_Value,_,_,_} = Spvc#spvcVccPerm.spvcVccEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcTargetVc) ->
+ {If_Value,_,_} = Spvc#spvcTargetVc.entry,
+ If_Value;
+ Spvc when record(Spvc,spvcTargetVp) ->
+ {If_Value,_} = Spvc#spvcTargetVp.entry,
+ If_Value;
+ Spvc when record(Spvc,pchVc) ->
+ {If_Value,_,_} = Spvc#pchVc.vclEntry,
+ If_Value;
+ Spvc when record(Spvc,pchVp) ->
+ {If_Value,_} = Spvc#pchVp.vplEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcFr) ->
+ {If_Value,_} = Spvc#spvcFr.spvcFrEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcFrPerm) ->
+ {If_Value,_} = Spvc#spvcFrPerm.spvcFrEntry,
+ If_Value;
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value;
+ {If_Value,_} ->
+ If_Value;
+ [If_Value|_] ->
+ If_Value;
+ _ ->
+ error
+ end) of
+ disabled ->
+ active(release_nu_disabled,Spvc,Attrs);
+ enabled ->
+ active(release_nu_enabled,Spvc,Attrs)
+ end;
+orig_state_machine(active,release_comp_nu,Spvc,Attrs) ->
+ release_at_restart(release_comp_nu,Spvc,Attrs);
+orig_state_machine(active,not_in_service,Spvc,Attrs) ->
+ active(not_in_service,Spvc,Attrs);
+orig_state_machine(active,activate_enabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(active,activate_disabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(active,release_incumbent,Spvc,Attrs) ->
+ active(release_incumbent,Spvc,Attrs);
+orig_state_machine(wait,destroy,Spvc,Attrs) ->
+ wait(destroy,Spvc,Attrs);
+orig_state_machine(wait,timeout,Spvc,Attrs) ->
+ wait(timeout,Spvc,Attrs);
+orig_state_machine(wait,restart,Spvc,Attrs) ->
+ wait(restart,Spvc,Attrs);
+orig_state_machine(wait,release_nu,Spvc,Attrs) ->
+ ok;
+orig_state_machine(wait,not_in_service,Spvc,Attrs) ->
+ wait(not_in_service,Spvc,Attrs);
+orig_state_machine(wait,activate_enabled,Spvc,Attrs) ->
+ wait(timeout,Spvc,Attrs);
+orig_state_machine(wait,activate_disabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(release_at_restart,release_comp_nu,Spvc,Attrs) ->
+ release_at_restart(release_comp_nu,Spvc,Attrs);
+orig_state_machine(release_at_restart,release_nu,Spvc,Attrs) ->
+ release_at_restart(release_nu,Spvc,Attrs);
+orig_state_machine(release_at_restart,connect_nu,Spvc,Attrs) ->
+ release_at_restart(connect_nu,Spvc,Attrs);
+orig_state_machine(release_at_restart,destroy,Spvc,Attrs) ->
+ release_at_restart(destroy,Spvc,Attrs);
+orig_state_machine(release_at_restart,not_in_service,Spvc,Attrs) ->
+ release_at_restart(not_in_service,Spvc,Attrs);
+orig_state_machine(release_at_restart,activate_enabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(release_at_restart,activate_disabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(release_request,release_comp_nu,Spvc,Attrs) ->
+ release_request(release_comp_nu,Spvc,Attrs);
+orig_state_machine(release_request,release_nu,Spvc,Attrs) ->
+ release_request(release_nu,Spvc,Attrs);
+orig_state_machine(release_request,destroy,Spvc,Attrs) ->
+ release_request(destroy,Spvc,Attrs);
+orig_state_machine(release_request,not_in_service,Spvc,Attrs) ->
+ release_request(not_in_service,Spvc,Attrs);
+orig_state_machine(release_request,activate_enabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(release_request,activate_disabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(rest_in_peace,restart,Spvc,Attrs) ->
+ rest_in_peace(restart,Spvc,Attrs);
+orig_state_machine(rest_in_peace,destroy,Spvc,Attrs) ->
+ rest_in_peace(destroy,Spvc,Attrs);
+orig_state_machine(rest_in_peace,not_in_service,Spvc,Attrs) ->
+ rest_in_peace(not_in_service,Spvc,Attrs);
+orig_state_machine(rest_in_peace,connect_nu,Spvc,Attrs) ->
+ rest_in_peace(connect_nu,Spvc,Attrs);
+orig_state_machine(rest_in_peace,activate_enabled,Spvc,Attrs) ->
+ rest_in_peace(restart,Spvc,Attrs);
+orig_state_machine(rest_in_peace,activate_disabled,Spvc,Attrs) ->
+ ok;
+orig_state_machine(rest_in_peace,release_nu,Spvc,Attrs) ->
+ ok;
+orig_state_machine(rest_in_peace,release_comp_nu,Spvc,Attrs) ->
+ ok;
+orig_state_machine(not_in_service,activate_enabled,Spvc,Attrs) ->
+ not_in_service(activate_enabled,Spvc,Attrs);
+orig_state_machine(not_in_service,activate_disabled,Spvc,Attrs) ->
+ not_in_service(activate_disabled,Spvc,Attrs);
+orig_state_machine(not_in_service,destroy,Spvc,Attrs) ->
+ not_in_service(destroy,Spvc,Attrs);
+orig_state_machine(not_in_service,connect_nu,Spvc,Attrs) ->
+ not_in_service(connect_nu,Spvc,Attrs);
+orig_state_machine(not_in_service,_,Spvc,Attrs) ->
+ ok;
+orig_state_machine(awaiting_switch_over,switch_over,Spvc,[HcId]) ->
+ awaiting_switch_over(switch_over,Spvc,[HcId]);
+orig_state_machine(awaiting_switch_over,activate_disabled,Spvc,Attrs) ->
+ awaiting_switch_over(activate_disabled,Spvc,Attrs);
+orig_state_machine(awaiting_switch_over,destroy,Spvc,Attrs) ->
+ awaiting_switch_over(destroy,Spvc,Attrs);
+orig_state_machine(awaiting_switch_over,restart,Spvc,Attrs) ->
+ awaiting_switch_over(restart,Spvc,Attrs);
+orig_state_machine(awaiting_switch_over,_,Spvc,Attrs) ->
+ ok;
+orig_state_machine(undefined,destroy,Spvc,Attrs) ->
+ rest_in_peace(destroy,Spvc,Attrs).
+
+null(createAndGo_enabled,[],[Row,Cols,Type,FrKey]) ->
+ debug_disabled,
+ Key = list_to_tuple(Row),
+ Spvc = #spvcObj{spvcEntry = Key,
+ spvcApplication = Type,
+ spvcRowStatus = 1,
+ spvcFrKey = FrKey},
+ Spvc1 = set_attrs(Spvc,Cols),
+ {Spvc2,HcId,Setup} = new_state_outgoing_call_proceeding(Spvc1),
+ pchTpUpdate(case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end),
+ spvcDataBase:db_write(Spvc2),
+ setup(HcId,Setup,Spvc2);
+null(createAndGo_disabled,[],[Row,Cols,Type,FrKey]) ->
+ debug_disabled,
+ case get_link_state_intf(Row,null_createAndGo_disabled) of
+ disabled ->
+ Key = list_to_tuple(Row),
+ Spvc = #spvcObj{spvcEntry = Key,
+ spvcRowStatus = 1,
+ currentState = rest_in_peace,
+ spvcApplication = Type,
+ spvcFrKey = FrKey},
+ Spvc1 = set_attrs(Spvc,Cols),
+ pchTpUpdate(case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end),
+ set_call_failure_data_and_send_spvcFailingAlarm(Key),
+ spvcDataBase:db_write(Spvc1);
+ enabled ->
+ null(createAndGo_enabled,[],[Row,Cols,Type,FrKey])
+ end;
+null(createAndWait,[],[Row,Cols,Type,FrKey]) ->
+ debug_disabled,
+ Key = list_to_tuple(Row),
+ Spvc = #spvcObj{spvcEntry = Key,
+ spvcApplication = Type,
+ spvcFrKey = FrKey},
+ Spvc1 = new_state_created(Spvc,Cols),
+ pchTpUpdate(case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end),
+ spvcDataBase:db_write(Spvc1).
+
+pchTpUpdate({If,Vpi,Vci}) ->
+ spvcDataBase:db_write(#spvcVcDyn{vclEntry = {If,Vpi,Vci},
+ vclCcIdentifier = 0});
+pchTpUpdate({If,Vpi}) ->
+ spvcDataBase:db_write(#spvcVpDyn{vplEntry = {If,Vpi},
+ vplCcIdentifier = 0}).
+
+created(activate_enabled,Spvc,Attrs) ->
+ debug_disabled,
+ Spvc1 = set_attrs(Spvc,Attrs),
+ Spvc2 = Spvc1#spvcObj{spvcRowStatus = 1},
+ {Spvc3,HcId,HciMsg} = new_state_outgoing_call_proceeding(Spvc1),
+ spvcDataBase:db_write(Spvc3),
+ setup(HcId,HciMsg,Spvc3);
+created(activate_disabled,Spvc,Attrs) ->
+ debug_disabled,
+ Spvc1 = set_attrs(Spvc,Attrs),
+ Spvc2 = Spvc1#spvcObj{currentState = rest_in_peace,
+ spvcRowStatus = 1},
+ update_state(Spvc,4),
+ spvcDataBase:db_write(Spvc2);
+created(destroy,Spvc,Attrs) ->
+ debug_disabled,
+ clear(Spvc).
+
+outgoing_callproceeding(connect_nu,Spvc,[HcId,Connect]) ->
+ debug_disabled,
+ Spvc1 = new_state_active(Spvc),
+ case Spvc#spvcObj.spvcTargetSelectType of
+ 2 ->
+ Cpn = Connect#hci_connect.hci_cpn_soft,
+ TargetVpi = Cpn#hci_cpn_soft.hci_soft_vpi,
+ TargetVci = Cpn#hci_cpn_soft.hci_soft_vci,
+ TargetDlci = Cpn#hci_cpn_soft.hci_soft_dlci,
+ Spvc2 = Spvc1#spvcObj{spvcTargetSelectType = 1,
+ spvcTargetVpi = TargetVpi,
+ spvcTargetVci = TargetVci,
+ spvcTargetDlci = TargetDlci},
+ spvcDataBase:db_write(Spvc2);
+ 1 ->
+ spvcDataBase:db_write(ets,Spvc1);
+ 2 ->
+ Cpn = Connect#hci_connect.hci_cpn_soft,
+ TargetVpi = Cpn#hci_cpn_soft.hci_soft_vpi,
+ TargetDlci = Cpn#hci_cpn_soft.hci_soft_dlci,
+ Spvc2 = Spvc1#spvcObj{spvcTargetSelectType = 1,
+ spvcTargetVpi = TargetVpi,
+ spvcTargetDlci = TargetDlci},
+ spvcDataBase:db_write(Spvc2);
+ 1 ->
+ spvcDataBase:db_write(ets,Spvc1)
+ end,
+ Key = Spvc#spvcObj.spvcEntry,
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ SpvcDyn = case PchKey of
+ {_,_,_} ->
+ case spvcDataBase:db_read({spvcVcDyn,PchKey}) of
+ [] ->
+ #spvcVcDyn{vclEntry = PchKey,
+ vclCcIdentifier = 0,
+ vclConnId = HcId};
+ SpvcVcDyn ->
+ SpvcVcDyn#spvcVcDyn{vclEntry = PchKey,
+ vclConnId = HcId}
+ end;
+ {_,_} ->
+ case spvcDataBase:db_read({spvcVpDyn,PchKey}) of
+ [] ->
+ #spvcVpDyn{vplEntry = PchKey,
+ vplCcIdentifier = 0,
+ vplConnId = HcId};
+ SpvcVpDyn ->
+ SpvcVpDyn#spvcVpDyn{vplEntry = PchKey,
+ vplConnId = HcId}
+ end
+ end,
+ spvcDataBase:db_write(SpvcDyn),
+ CbCValue = get(no_of_rerouting),
+ CbC = case CbCValue of
+ undefined ->
+ debug_disabled,
+ 0;
+ _ ->
+ CbCValue
+ end,
+ SpvcDyn2 = case Key of
+ {_,_,_,_} ->
+ case spvcDataBase:db_read({spvcVccDyn,Key}) of
+ [] ->
+ #spvcVccDyn{spvcVccEntry = Key,
+ crankBackCounter = CbC};
+ SpvcVccDyn ->
+ SpvcVccDyn#spvcVccDyn{spvcVccEntry = Key,
+ crankBackCounter = CbC}
+ end;
+ {_,_,_} ->
+ case spvcDataBase:db_read({spvcVpcDyn,Key}) of
+ [] ->
+ #spvcVpcDyn{spvcVpcEntry = Key,
+ crankBackCounter = CbC};
+ SpvcVpcDyn ->
+ SpvcVpcDyn#spvcVpcDyn{spvcVpcEntry = Key,
+ crankBackCounter = CbC}
+ end
+ end,
+ spvcDataBase:db_write(SpvcDyn2),
+ NewPch = spvcDataBase:db_read({pch,PchKey}),
+ spvcLib:clear_spvcStillTryingAlarm(Key),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ spvcLib:ilmi_change(PchKey,1),
+ ok;
+ FrEndPoint ->
+ SpvcFrObj = spvcDataBase:db_read({spvcFrPerm,FrEndPoint}),
+ NewSpvcFrObj = SpvcFrObj#spvcFrPerm{spvcFrConnect = 3},
+ spvcDataBase:db_write(NewSpvcFrObj),
+ spvcLib:ilmi_change(PchKey,1),
+ set_fr_atm_iw_admin_state(FrEndPoint,up,Spvc)
+ end;
+outgoing_callproceeding(restart,Spvc,_) ->
+ Key = Spvc#spvcObj.spvcEntry,
+ debug_disabled,
+ Spvc1 = new_state_release_at_restart(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ spvcLib:clear_spvcStillTryingAlarm(Key);
+outgoing_callproceeding(release_nu_enabled,Spvc,[HcId,HciMsg]) ->
+ debug_disabled,
+ Spvc1 = new_state_rest_in_peace_or_wait(Spvc,[HcId,HciMsg]),
+ [CcCause|_] = HciMsg#hci_release.hci_cause_list,
+ Spvc2 = Spvc1#spvcObj{spvcLastReleaseCause = CcCause#hci_cause.hci_cause_value,
+ spvcLastReleaseDiagnostic = CcCause#hci_cause.hci_diagnostics_list},
+ spvcDataBase:db_write(ets,Spvc2);
+outgoing_callproceeding(release_nu_disabled,Spvc,[HcId,Release]) ->
+ debug_disabled,
+ Spvc1 = new_state_rest_in_peace(Spvc),
+ [CcCause|_] = Release#hci_release.hci_cause_list,
+ Spvc2 = Spvc1#spvcObj{spvcLastReleaseCause = CcCause#hci_cause.hci_cause_value,
+ spvcLastReleaseDiagnostic = CcCause#hci_cause.hci_diagnostics_list},
+ spvcDataBase:db_write(ets,Spvc2),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry);
+outgoing_callproceeding(release_comp_nu_enabled,Spvc,[HcId,Release_complete]) ->
+ debug_disabled,
+ Spvc1 = new_state_rest_in_peace_or_wait(Spvc,[HcId,Release_complete]),
+ spvcDataBase:db_write(ets,Spvc1);
+outgoing_callproceeding(release_comp_nu_disabled,Spvc,[HcId,Release_complete]) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_rest_in_peace(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ spvcLib:clear_spvcStillTryingAlarm(Key);
+outgoing_callproceeding(destroy,Spvc,_) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_release_request(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc1),
+ spvcLib:clear_spvcStillTryingAlarm(Key);
+outgoing_callproceeding(not_in_service,Spvc,_) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_not_in_service(Spvc),
+ spvcDataBase:db_write(Spvc1),
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc1),
+ spvcLib:clear_spvcStillTryingAlarm(Key).
+
+active(restart,Spvc,_) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_release_at_restart(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcLib:ilmi_change(PchKey,2),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ ok;
+ FrEndPoint ->
+ set_fr_atm_iw_admin_state(FrEndPoint,down,Spvc)
+ end;
+active(release_nu_enabled,Spvc,[HcId,Release]) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_rest_in_peace_or_wait(Spvc,[HcId,Release]),
+ [CcCause|_] = Release#hci_release.hci_cause_list,
+ Spvc2 = Spvc1#spvcObj{spvcLastReleaseCause = CcCause#hci_cause.hci_cause_value,
+ spvcLastReleaseDiagnostic = CcCause#hci_cause.hci_diagnostics_list},
+ spvcDataBase:db_write(ets,Spvc2),
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcLib:ilmi_change(PchKey,2),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ ok;
+ FrEndPoint ->
+ set_fr_atm_iw_admin_state(FrEndPoint,down,Spvc)
+ end;
+active(release_nu_disabled,Spvc,[HcId,Release]) ->
+ debug_disabled,
+ case get_link_state_intf(case Spvc of
+ Spvc when record(Spvc,spvcObj) ->
+ case Spvc#spvcObj.spvcEntry of
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value
+ end;
+ Spvc when record(Spvc,spvcVcc) ->
+ {If_Value,_,_,_} = Spvc#spvcVcc.spvcVccEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVpc) ->
+ {If_Value,_,_} = Spvc#spvcVpc.spvcVpcEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVpcPerm) ->
+ {If_Value,_,_} = Spvc#spvcVpcPerm.spvcVpcEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcVccPerm) ->
+ {If_Value,_,_,_} = Spvc#spvcVccPerm.spvcVccEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcTargetVc) ->
+ {If_Value,_,_} = Spvc#spvcTargetVc.entry,
+ If_Value;
+ Spvc when record(Spvc,spvcTargetVp) ->
+ {If_Value,_} = Spvc#spvcTargetVp.entry,
+ If_Value;
+ Spvc when record(Spvc,pchVc) ->
+ {If_Value,_,_} = Spvc#pchVc.vclEntry,
+ If_Value;
+ Spvc when record(Spvc,pchVp) ->
+ {If_Value,_} = Spvc#pchVp.vplEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcFr) ->
+ {If_Value,_} = Spvc#spvcFr.spvcFrEntry,
+ If_Value;
+ Spvc when record(Spvc,spvcFrPerm) ->
+ {If_Value,_} = Spvc#spvcFrPerm.spvcFrEntry,
+ If_Value;
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value;
+ {If_Value,_} ->
+ If_Value;
+ [If_Value|_] ->
+ If_Value;
+ _ ->
+ error
+ end,active_release_nu_disabled) of
+ disabled ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = Spvc#spvcObj{currentState = rest_in_peace},
+ [CcCause|_] = Release#hci_release.hci_cause_list,
+ Spvc2 = Spvc1#spvcObj{spvcLastReleaseCause = CcCause#hci_cause.hci_cause_value,
+ spvcLastReleaseDiagnostic = CcCause#hci_cause.hci_diagnostics_list},
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcLib:ilmi_change(PchKey,2),
+ update_state(Spvc,4),
+ spvcDataBase:db_write(ets,Spvc2),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ ok;
+ FrEndPoint ->
+ set_fr_atm_iw_admin_state(FrEndPoint,down,Spvc)
+ end;
+ enabled ->
+ active(release_nu_enabled,Spvc,[HcId,Release])
+ end;
+active(destroy,Spvc,_) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_release_request(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcLib:ilmi_change(PchKey,2),
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc);
+active(not_in_service,Spvc,_) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_not_in_service(Spvc),
+ spvcDataBase:db_write(Spvc1),
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcLib:ilmi_change(PchKey,2),
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc1),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ ok;
+ FrEndPoint ->
+ set_fr_atm_iw_admin_state(FrEndPoint,down,Spvc)
+ end;
+active(release_incumbent,Spvc,[Release]) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_awaiting_switch_over(Spvc),
+ spvcDataBase:db_write(Spvc1),
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc1).
+
+read_spvcTpToHcId({If,Vpi,Vci,Leaf}) ->
+ spvcDataBase:db_read({spvcTpToHcId,{orig,If,Vpi,Vci,Leaf}});
+read_spvcTpToHcId({If,Vpi,Leaf}) ->
+ spvcDataBase:db_read({spvcTpToHcId,{orig,If,Vpi,Leaf}}).
+
+release_request(release_nu,Spvc,[HcId,Release]) ->
+ debug_disabled,
+ clear(Spvc);
+release_request(release_comp_nu,Spvc,[HcId,Release_comp]) ->
+ debug_disabled,
+ clear(Spvc);
+release_request(destroy,Spvc,_) ->
+ debug_disabled,
+ case Spvc#spvcObj.spvcEntry of
+ {If,Vpi,Vci,Leaf} ->
+ case spvcDataBase:db_read({spvcTpToHcId,{orig,If,Vpi,Vci,Leaf}}) of
+ SpvcTpToHcId ->
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc),
+ clear(Spvc);
+ _ ->
+ ok
+ end;
+ {If,Vpi,Leaf} ->
+ case spvcDataBase:db_read({spvcTpToHcId,{orig,If,Vpi,Leaf}}) of
+ SpvcTpToHcId ->
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc),
+ clear(Spvc);
+ _ ->
+ ok
+ end
+ end,
+ ok;
+release_request(not_in_service,Spvc,_) ->
+ debug_disabled,
+ ok.
+
+release_at_restart(release_nu,Spvc,[HcId,Release]) ->
+ debug_disabled,
+ {Spvc1,NewHcId,Setup} = new_state_outgoing_call_proceeding(Spvc),
+ [CcCause|_] = Release#hci_release.hci_cause_list,
+ Spvc2 = Spvc1#spvcObj{spvcLastReleaseCause = CcCause#hci_cause.hci_cause_value,
+ spvcLastReleaseDiagnostic = CcCause#hci_cause.hci_diagnostics_list},
+ spvcDataBase:db_write(ets,Spvc2),
+ timer:sleep(500),
+ setup(NewHcId,Setup,Spvc2);
+release_at_restart(release_comp_nu,Spvc,[HcId,Release_complete]) ->
+ debug_disabled,
+ {Spvc1,NewHcId,Setup} = new_state_outgoing_call_proceeding(Spvc),
+ Spvc2 = Spvc1#spvcObj{spvcLastReleaseCause = 31,
+ spvcLastReleaseDiagnostic = []},
+ spvcDataBase:db_write(ets,Spvc2),
+ timer:sleep(500),
+ setup(NewHcId,Setup,Spvc1);
+release_at_restart(connect_nu,Spvc,_) ->
+ debug_disabled,
+ ok;
+release_at_restart(destroy,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_release_request(Spvc),
+ spvcDataBase:db_write(ets,Spvc1);
+release_at_restart(restart,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_release_at_restart(Spvc);
+release_at_restart(not_in_service,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_not_in_service(Spvc),
+ spvcDataBase:db_write(Spvc1).
+
+wait(timeout,Spvc,_) ->
+ debug_disabled,
+ {Spvc1,HcId,Setup} = new_state_outgoing_call_proceeding(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ setup(HcId,Setup,Spvc1);
+wait(destroy,Spvc,_) ->
+ debug_disabled,
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry),
+ clear(Spvc);
+wait(restart,Spvc,_) ->
+ debug_disabled,
+ {Spvc1,HcId,Setup} = new_state_outgoing_call_proceeding(Spvc#spvcObj{spvcRetryFailures = 0}),
+ spvcDataBase:db_write(ets,Spvc1),
+ spvcReestablishTimer:cancel(Spvc#spvcObj.spvcEntry),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry),
+ setup(HcId,Setup,Spvc1);
+wait(not_in_service,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_not_in_service(Spvc),
+ spvcDataBase:db_write(Spvc1),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry).
+
+rest_in_peace(restart,Spvc,_) ->
+ debug_disabled,
+ {Spvc1,HcId,Setup} = new_state_outgoing_call_proceeding(Spvc#spvcObj{spvcRetryFailures = 0}),
+ spvcDataBase:db_write(ets,Spvc1),
+ setup(HcId,Setup,Spvc1),
+ sccmManager:cast_to_sccm(spvcLib:get_cp(om_node),spvcLib,clear_spvcFailingAlarm,[spvcLib:get_membership(node())]);
+rest_in_peace(destroy,Spvc,_) ->
+ debug_disabled,
+ sccmManager:cast_to_sccm(spvcLib:get_cp(om_node),spvcLib,clear_spvcFailingAlarm,[spvcLib:get_membership(node())]),
+ clear(Spvc);
+rest_in_peace(connect_nu,Spvc,_) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(b_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc);
+rest_in_peace(not_in_service,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_not_in_service(Spvc),
+ spvcDataBase:db_write(Spvc1),
+ sccmManager:cast_to_sccm(spvcLib:get_cp(om_node),spvcLib,clear_spvcFailingAlarm,[spvcLib:get_membership(node())]).
+
+not_in_service(activate_enabled,Spvc,_) ->
+ debug_disabled,
+ {Spvc1,HcId,Setup} = new_state_outgoing_call_proceeding(Spvc#spvcObj{spvcRetryFailures = 0}),
+ spvcDataBase:db_write(Spvc1#spvcObj{spvcRowStatus = 1}),
+ setup(HcId,Setup,Spvc1);
+not_in_service(activate_disabled,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_rest_in_peace(Spvc),
+ spvcDataBase:db_write(Spvc1#spvcObj{spvcRowStatus = 1});
+not_in_service(connect_nu,Spvc,_) ->
+ debug_disabled,
+ Spvc1 = new_state_rest_in_peace(Spvc),
+ spvcDataBase:db_write(Spvc1#spvcObj{spvcRowStatus = 1}),
+ Key = Spvc#spvcObj.spvcEntry,
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(b_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc1);
+not_in_service(destroy,Spvc,_) ->
+ debug_disabled,
+ clear(Spvc).
+
+awaiting_switch_over(switch_over,Spvc,[HcId]) ->
+ debug_disabled,
+ Spvc1 = Spvc#spvcObj{currentState = active},
+ Index = Spvc#spvcObj.spvcEntry,
+ TpIndex = create_tp_index(Index),
+ spvcDataBase:db_write(Spvc1),
+ ets:insert(spvcTpToHcId,#spvcTpToHcId{tpEntry = TpIndex,
+ hcId = HcId}),
+ ets:insert(spvcHcIdToTp,#spvcHcIdToTp{tpEntry = TpIndex,
+ hcId = HcId}),
+ update_dyn_table_hcid(Index,HcId),
+ ok;
+awaiting_switch_over(activate_disabled,Spvc,Attrs) ->
+ Spvc1 = new_state_rest_in_peace(Spvc),
+ spvcDataBase:db_write(Spvc1),
+ ok;
+awaiting_switch_over(restart,Spvc,Attrs) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Spvc1 = new_state_release_at_restart(Spvc),
+ spvcDataBase:db_write(ets,Spvc1),
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcLib:ilmi_change(PchKey,2),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ ok;
+ FrEndPoint ->
+ set_fr_atm_iw_admin_state(FrEndPoint,down,Spvc)
+ end;
+awaiting_switch_over(destroy,Spvc,Attrs) ->
+ clear(Spvc).
+
+create_tp_index({If,Vpi,Vci,Leaf}) ->
+ list_to_tuple([orig,If,Vpi,Vci,Leaf]);
+create_tp_index({If,Vpi,Leaf}) ->
+ list_to_tuple([orig,If,Vpi,Leaf]).
+
+update_dyn_table_hcid({If,Vpi,Vci,Leaf},HcId) ->
+ [VcDyn] = ets:lookup(spvcVcDyn,{If,Vpi,Vci}),
+ ets:insert(spvcVcDyn,VcDyn#spvcVcDyn{vclConnId = HcId});
+update_dyn_table_hcid({If,Vpi,Leaf},HcId) ->
+ [VpDyn] = ets:lookup(spvcVpDyn,{If,Vpi}),
+ ets:insert(spvcVpDyn,VpDyn#spvcVpDyn{vplConnId = HcId}).
+
+new_state_outgoing_call_proceeding(Spvc) ->
+ debug_disabled,
+ Spvc1 = Spvc#spvcObj{spvcRowStatus = 1,
+ currentState = outgoing_callproceeding},
+ Key = Spvc1#spvcObj.spvcEntry,
+ update_state(Spvc,outgoing_callproceeding),
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ {FwdAtdIndex,BwdAtdIndex} = case PchKey of
+ {_,_,_} ->
+ Vc = spvcDataBase:db_read({pchVc,PchKey}),
+ {Vc#pchVc.vclReceiveTrafficDescrIndex,Vc#pchVc.vclTransmitTrafficDescrIndex};
+ {_,_} ->
+ Vp = spvcDataBase:db_read({pchVp,PchKey}),
+ {Vp#pchVp.vplReceiveTrafficDescrIndex,Vp#pchVp.vplTransmitTrafficDescrIndex}
+ end,
+ FwdPchAtd = spvcDataBase:db_read({pchAtd,FwdAtdIndex}),
+ BwdPchAtd = spvcDataBase:db_read({pchAtd,BwdAtdIndex}),
+ Row = tuple_to_list(Key),
+ HcId = spvcLib:create_hcid(Row,case Row of
+ Row when record(Row,spvcObj) ->
+ case Row#spvcObj.spvcEntry of
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value
+ end;
+ Row when record(Row,spvcVcc) ->
+ {If_Value,_,_,_} = Row#spvcVcc.spvcVccEntry,
+ If_Value;
+ Row when record(Row,spvcVpc) ->
+ {If_Value,_,_} = Row#spvcVpc.spvcVpcEntry,
+ If_Value;
+ Row when record(Row,spvcVpcPerm) ->
+ {If_Value,_,_} = Row#spvcVpcPerm.spvcVpcEntry,
+ If_Value;
+ Row when record(Row,spvcVccPerm) ->
+ {If_Value,_,_,_} = Row#spvcVccPerm.spvcVccEntry,
+ If_Value;
+ Row when record(Row,spvcTargetVc) ->
+ {If_Value,_,_} = Row#spvcTargetVc.entry,
+ If_Value;
+ Row when record(Row,spvcTargetVp) ->
+ {If_Value,_} = Row#spvcTargetVp.entry,
+ If_Value;
+ Row when record(Row,pchVc) ->
+ {If_Value,_,_} = Row#pchVc.vclEntry,
+ If_Value;
+ Row when record(Row,pchVp) ->
+ {If_Value,_} = Row#pchVp.vplEntry,
+ If_Value;
+ Row when record(Row,spvcFr) ->
+ {If_Value,_} = Row#spvcFr.spvcFrEntry,
+ If_Value;
+ Row when record(Row,spvcFrPerm) ->
+ {If_Value,_} = Row#spvcFrPerm.spvcFrEntry,
+ If_Value;
+ {If_Value,_,_,_} ->
+ If_Value;
+ {If_Value,_,_} ->
+ If_Value;
+ {If_Value,_} ->
+ If_Value;
+ [If_Value|_] ->
+ If_Value;
+ _ ->
+ error
+ end),
+ Setup = spvcEncode:encode_cc_setup(Row,Spvc1,FwdPchAtd,BwdPchAtd),
+ debug_disabled,
+ debug_disabled,
+ debug_disabled,
+ {Spvc1,HcId,Setup}.
+
+new_state_release_request(Spvc) ->
+ debug_disabled,
+ update_state(Spvc,release_request),
+ Spvc#spvcObj{currentState = release_request}.
+
+new_state_release_at_restart(Spvc) ->
+ debug_disabled,
+ Spvc1 = Spvc#spvcObj{spvcRetryFailures = 0,
+ currentState = release_at_restart},
+ update_state(Spvc,release_at_restart),
+ HcId = spvcEncode:encode_cc_hcid(Spvc1#spvcObj.spvcEntry),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(a_side,HcId,Release,Spvc1),
+ Spvc1.
+
+new_state_rest_in_peace_or_wait(Spvc,[HcId,HciMsg]) ->
+ debug_disabled,
+ Spvc1 = Spvc#spvcObj{spvcRetryFailures = Spvc#spvcObj.spvcRetryFailures + 1},
+ case check_limits(Spvc1) of
+ {ok,ok,no_retries} ->
+ send_spvcFailingAlarm(Spvc#spvcObj.spvcEntry),
+ update_state(Spvc,4),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry),
+ Spvc1#spvcObj{currentState = rest_in_peace};
+ {ok,ok,_} ->
+ Spvc2 = Spvc1#spvcObj{spvcRetryTimer = time(),
+ currentState = wait},
+ update_state(Spvc,wait),
+ start_timer(wait,Spvc2),
+ Spvc2;
+ {retry_threshold,ok,no_retries} ->
+ Spvc2 = Spvc1#spvcObj{currentState = rest_in_peace},
+ update_state(Spvc,4),
+ send_call_failure(Spvc),
+ send_spvcFailingAlarm(Spvc#spvcObj.spvcEntry),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry),
+ Spvc2;
+ {retry_threshold,ok,_} ->
+ Spvc2 = Spvc1#spvcObj{spvcRetryTimer = time(),
+ currentState = wait},
+ update_state(Spvc,wait),
+ send_call_failure(Spvc2),
+ start_timer(wait,Spvc2),
+ Spvc2;
+ {ok,retry_limit,_} ->
+ send_spvcFailingAlarm(Spvc#spvcObj.spvcEntry),
+ update_state(Spvc,4),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry),
+ Spvc1#spvcObj{currentState = rest_in_peace};
+ {retry_threshold,retry_limit,_} ->
+ Spvc2 = Spvc1#spvcObj{currentState = rest_in_peace},
+ update_state(Spvc,4),
+ send_call_failure(Spvc2),
+ send_spvcFailingAlarm(Spvc#spvcObj.spvcEntry),
+ spvcLib:clear_spvcStillTryingAlarm(Spvc#spvcObj.spvcEntry),
+ Spvc2
+ end.
+
+send_call_failure(Spvc) ->
+ case Spvc#spvcObj.spvcRetryThreshold of
+ 0 ->
+ ok;
+ _ ->
+ sccmManager:cast_to_sccm(spvcLib:get_cp(om_node),spvcOrig,call_failure,[Spvc])
+ end.
+
+new_state_rest_in_peace(Spvc) ->
+ debug_disabled,
+ update_state(Spvc,4),
+ Spvc1 = Spvc#spvcObj{spvcRetryFailures = Spvc#spvcObj.spvcRetryFailures + 1},
+ send_spvcFailingAlarm(Spvc#spvcObj.spvcEntry),
+ case check_limits(Spvc1) of
+ {ok,_,_} ->
+ Spvc1#spvcObj{currentState = rest_in_peace};
+ {retry_threshold,_,_} ->
+ Spvc2 = Spvc1#spvcObj{currentState = rest_in_peace},
+ case Spvc2#spvcObj.spvcRetryThreshold of
+ 0 ->
+ ok;
+ _ ->
+ sccmManager:cast_to_sccm(spvcLib:get_cp(om_node),spvcOrig,call_failure,[Spvc2])
+ end,
+ Spvc2
+ end.
+
+new_state_active(Spvc) ->
+ debug_disabled,
+ update_state(Spvc,3),
+ Spvc#spvcObj{spvcRetryFailures = 0,
+ currentState = active}.
+
+new_state_created(Spvc,SetCols) ->
+ debug_disabled,
+ update_state(Spvc,created),
+ case spvcSNMP:is_all_values(case Spvc#spvcObj.spvcEntry of
+ {_,_,_,_} ->
+ spvcVcc;
+ {_,_,_} ->
+ spvcVpc;
+ {_,_} ->
+ spvcFr;
+ [_,_,_,_] ->
+ spvcVcc;
+ [_,_,_] ->
+ spvcVpc;
+ [_,_] ->
+ spvcFr
+ end,SetCols) of
+ true ->
+ Spvc1 = Spvc#spvcObj{spvcRowStatus = 2,
+ currentState = created},
+ set_attrs(Spvc1,SetCols);
+ false ->
+ Spvc1 = Spvc#spvcObj{spvcRowStatus = 3,
+ currentState = created},
+ set_attrs(Spvc1,SetCols)
+ end.
+
+new_state_not_in_service(Spvc) ->
+ debug_disabled,
+ update_state(Spvc,not_in_service),
+ Spvc#spvcObj{currentState = not_in_service,
+ spvcRowStatus = 2}.
+
+new_state_awaiting_switch_over(Spvc) ->
+ debug_disabled,
+ Spvc#spvcObj{currentState = awaiting_switch_over}.
+
+update_state(Spvc,NewState) ->
+ State = Spvc#spvcObj.currentState,
+ SpvcEntry = Spvc#spvcObj.spvcEntry,
+ debug_disabled,
+ spvcLib:update_state({State,SpvcEntry},NewState).
+
+send_spvcFailingAlarm(Key) ->
+ debug_disabled,
+ rpc:cast(spvcLib:get_cp(om_node),spvcLib,send_spvcFailingAlarm,[Key]).
+
+set_call_failure_data_and_send_spvcFailingAlarm({If,Vpi,Leaf}) ->
+ debug_disabled,
+ Spvc = spvcDataBase:db_read({spvcObj,{If,Vpi,Leaf}}),
+ if
+ Spvc == [] ->
+ ok;
+ true ->
+ spvcLib:update_state({Spvc#spvcObj.currentState,{If,Vpi,Leaf}},4)
+ end;
+set_call_failure_data_and_send_spvcFailingAlarm({If,Vpi,Vci,Leaf}) ->
+ debug_disabled,
+ Spvc = spvcDataBase:db_read({spvcObj,{If,Vpi,Vci,Leaf}}),
+ if
+ Spvc == [] ->
+ ok;
+ true ->
+ spvcLib:update_state({Spvc#spvcObj.currentState,{If,Vpi,Vci,Leaf}},4)
+ end.
+
+set_attrs(Spvc,SetCols) ->
+ case Spvc#spvcObj.spvcEntry of
+ {_,_,_,_} ->
+ set_attrs_spvcc(Spvc,SetCols);
+ {_,_,_} ->
+ set_attrs_spvpc(Spvc,SetCols)
+ end.
+
+set_attrs_spvcc(Spvc,[{2,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetAddress = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{3,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetSelectType = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{18,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetVpi = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{4,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetVpi = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{5,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetVci = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{6,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcLastReleaseCause = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{7,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcLastReleaseDiagnostic = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{10,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryInterval = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{11,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryTimer = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{12,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryThreshold = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{13,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryFailures = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{14,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryLimit = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{16,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetDlci = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[{17,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetType = Value},
+ set_attrs_spvcc(Spvc1,T);
+set_attrs_spvcc(Spvc,[_|T]) ->
+ set_attrs_spvcc(Spvc,T);
+set_attrs_spvcc(Spvc,[]) ->
+ debug_disabled,
+ Spvc.
+
+set_attrs_spvpc(Spvc,[{2,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetAddress = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{3,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetSelectType = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{15,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetVpi = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{4,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcTargetVpi = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{5,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcLastReleaseCause = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{6,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcLastReleaseDiagnostic = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{9,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryInterval = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{10,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryTimer = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{11,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryThreshold = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{12,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryFailures = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[{13,Value}|T]) ->
+ Spvc1 = Spvc#spvcObj{spvcRetryLimit = Value},
+ set_attrs_spvpc(Spvc1,T);
+set_attrs_spvpc(Spvc,[_|T]) ->
+ set_attrs_spvpc(Spvc,T);
+set_attrs_spvpc(Spvc,[]) ->
+ Spvc.
+
+call_failure(Spvc) ->
+ debug_disabled,
+ Key = case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ spvcLib:update_counter(callFailures,1,spvcLib:get_membership(node())),
+ atm_spvc;
+ _ ->
+ spvcLib:update_counter(callFrFailures,1,spvcLib:get_membership(node())),
+ fr_spvc
+ end,
+ Obj = spvcDataBase:db_read({spvcFailures,Key}),
+ case Obj#spvcFailures.spvcCallFailuresTrapEnable of
+ 1 ->
+ EventIndObj = spvcDataBase:db_read({spvcEventIndicator,Key}),
+ case EventIndObj#spvcEventIndicator.spvcTimerInd of
+ 1 ->
+ spvcDataBase:db_write(EventIndObj#spvcEventIndicator{spvcSendEventInd = 1}),
+ NI = Obj#spvcFailures.spvcNotificationInterval,
+ sysTimer:apply_after(1000 * NI,spvcOrig,timeout_event,[EventIndObj]);
+ _ ->
+ spvcManager:send_event(Key),
+ NI = Obj#spvcFailures.spvcNotificationInterval,
+ sysTimer:apply_after(1000 * NI,spvcManager,timeout,[Key]),
+ spvcDataBase:db_write(EventIndObj#spvcEventIndicator{spvcTimerInd = 1,
+ spvcSendEventInd = 2})
+ end;
+ _ ->
+ ok
+ end.
+
+timeout_event(EventIndObj) ->
+ spvcDataBase:db_write(EventIndObj#spvcEventIndicator{spvcTimerInd = 2}).
+
+check_limits(Spvc) ->
+ debug_disabled,
+ T = Spvc#spvcObj.spvcRetryThreshold,
+ L = Spvc#spvcObj.spvcRetryLimit,
+ F = Spvc#spvcObj.spvcRetryFailures,
+ I = Spvc#spvcObj.spvcRetryInterval,
+ {check_threshold(F,T),check_limit(F,L),check_interval(I)}.
+
+check_threshold(Failures,Threshold) when Failures == Threshold ->
+ debug_disabled,
+ retry_threshold;
+check_threshold(Failures,Threshold) ->
+ debug_disabled,
+ ok.
+
+check_limit(Failures,0) ->
+ debug_disabled,
+ ok;
+check_limit(Failures,Limit) when Failures < Limit ->
+ debug_disabled,
+ ok;
+check_limit(Failures,Limit) ->
+ debug_disabled,
+ retry_limit.
+
+check_interval(0) ->
+ no_retries;
+check_interval(I) ->
+ I.
+
+start_timer(wait,Spvc) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ Id = spvcReestablishTimer:apply_after(backoff_delay(Key),spvcServer,cast_to_spvc,[node(),spvcOrig,timeout,[wait,Key]]).
+
+timeout(wait,Key) ->
+ debug_disabled,
+ case spvcDataBase:db_read({spvcObj,Key}) of
+ [] ->
+ debug_disabled,
+ ok;
+ Spvc ->
+ case Spvc#spvcObj.currentState of
+ wait ->
+ IfIndex = element(1,Key),
+ case spvcOam:is_reassign_et_in_progress(IfIndex) of
+ true ->
+ ok;
+ _ ->
+ orig_state_machine(wait,timeout,Spvc,[])
+ end;
+ _ ->
+ ok
+ end
+ end;
+timeout(X,Y) ->
+ debug_disabled,
+ ok.
+
+clear(Spvc) ->
+ debug_disabled,
+ Key = Spvc#spvcObj.spvcEntry,
+ PchKey = case Key of
+ {IfIndex_Value,Vpi_Value,Vci_Value,_} ->
+ {IfIndex_Value,Vpi_Value,Vci_Value};
+ {IfIndex_Value,Vpi_Value,_} ->
+ {IfIndex_Value,Vpi_Value};
+ [IfIndex_Value,Vpi_Value,Vci_Value,_] ->
+ [IfIndex_Value,Vpi_Value,Vci_Value];
+ [IfIndex_Value,Vpi_Value,_] ->
+ [IfIndex_Value,Vpi_Value]
+ end,
+ spvcEndPoint:free_tp_spvc(PchKey),
+ spvcDataBase:db_delete({spvcObj,Key}),
+ update_state(Spvc,clear),
+ OrigKey = list_to_tuple([orig] ++ tuple_to_list(Key)),
+ case Spvc#spvcObj.currentState of
+ created ->
+ ok;
+ _ ->
+ case spvcDataBase:db_read({spvcTpToHcId,OrigKey}) of
+ [] ->
+ ok;
+ #spvcTpToHcId{hcId = HcId} ->
+ spvcDataBase:db_delete({spvcHcIdToTp,HcId})
+ end,
+ ets:delete(spvcTpToHcId,OrigKey),
+ spvcReestablishTimer:cancel(Key),
+ ets:delete(spvcBackoff,Spvc#spvcObj.spvcEntry)
+ end,
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ sccmManager:cast_to_sccm(spvcLib:get_cp(om_node),spvcEndPoint,remove_tp,[tuple_to_list(PchKey)]);
+ FrKey ->
+ spvcFr:clean_up(FrKey)
+ end,
+ case {Spvc#spvcObj.spvcRerCap,Spvc#spvcObj.spvcEntry} of
+ {false,_} ->
+ ok;
+ {true,Entry} when size(Entry) == 3 ->
+ spvcDataBase:db_delete({spvcRerVp,Entry});
+ {true,Entry} when size(Entry) == 4 ->
+ spvcDataBase:db_delete({spvcRerVc,Entry})
+ end.
+
+get_link_state(If) when integer(If) ->
+ debug_disabled,
+ cnhChi:get_link_opstate(If);
+get_link_state(Other) ->
+ debug_disabled,
+ disabled.
+
+get_link_state_intf(If,Msg) when integer(If) ->
+ debug_disabled,
+ case cnhChi:get_link_opstate(If) of
+ enabled ->
+ enabled;
+ _ ->
+ Om_Node = spvcLib:get_cp(om_node),
+ case rpc:call(Om_Node,intfI,get_link_op_state,[If]) of
+ {ok,enabled} ->
+ enabled;
+ Result ->
+ disabled
+ end
+ end;
+get_link_state_intf(Other,Msg) ->
+ debug_disabled,
+ disabled.
+
+setup(HcId,Setup,Spvc) ->
+ case spvcDataBase:db_read({spvcObj,Spvc#spvcObj.spvcEntry}) of
+ [] ->
+ ok;
+ Spvc1 ->
+ case Spvc#spvcObj.currentState == Spvc1#spvcObj.currentState of
+ true ->
+ spvcLib:increase_counter(spvcSaEtStat,Spvc),
+ case Spvc#spvcObj.spvcFrKey of
+ undefined ->
+ do_setup(HcId,Setup,Spvc#spvcObj.spvcRerCap);
+ FrKey ->
+ do_setup(HcId,Setup,FrKey)
+ end;
+ _ ->
+ ok
+ end
+ end.
+
+do_setup(HcId,Setup,Type) when Type == undefined; Type == false ->
+ debug_disabled,
+ ReturnData = {0,HcId},
+ L3Data = {0,[HcId,Setup]},
+ mdisp:msg(node(),{plcOperator,1,infinity},{HcId,{spvcI,ReturnData}},{ccI,l3_msg,[HcId,spvcI,L3Data]});
+do_setup(HcId,Setup,true) ->
+ debug_disabled,
+ ReturnData = {0,HcId},
+ L3Data = {0,[HcId,Setup]},
+ mdisp:msg(node(),{plcOperator,1,infinity},{HcId,{spvcRerI,ReturnData}},{ccI,l3_msg,[HcId,spvcRerI,L3Data]});
+do_setup(HcId,Setup,FrKey) ->
+ debug_disabled,
+ ReturnData = {0,HcId},
+ L3Data = {0,[HcId,Setup]},
+ mdisp:msg(node(),{plcOperator,1,infinity},{HcId,{spvcFrI,ReturnData}},{ccI,l3_msg,[HcId,spvcFrI,L3Data]}).
+
+backoff_delay(Key) ->
+ debug_disabled,
+ Obj = spvcDataBase:db_read({spvcObj,Key}),
+ Var = spvcDataBase:db_read({spvcFailures,atm_spvc}),
+ {Delay,Flag} = case Obj#spvcObj.spvcRetryFailures of
+ 0 ->
+ {100,no_alarm};
+ 1 ->
+ {Obj#spvcObj.spvcRetryInterval,no_alarm};
+ _ ->
+ Table = get_backoff_table(Key,Obj),
+ Max_Delay = Var#spvcFailures.max_delay,
+ case Var#spvcFailures.delay_factor * Table#spvcBackoff.delay_time of
+ DelayValue when DelayValue < Max_Delay ->
+ {DelayValue,no_alarm};
+ _ ->
+ Org_Retry_Interval = Obj#spvcObj.spvcRetryInterval,
+ if
+ Org_Retry_Interval < Max_Delay ->
+ spvcLib:send_spvcStillTryingAlarm(Key,Table#spvcBackoff.flag),
+ {Max_Delay,alarm};
+ true ->
+ spvcLib:send_spvcStillTryingAlarm(Key,Table#spvcBackoff.flag),
+ {Org_Retry_Interval,alarm}
+ end
+ end
+ end,
+ ets:insert(spvcBackoff,#spvcBackoff{key = Key,
+ delay_time = Delay,
+ flag = Flag}),
+ round(Delay).
+
+get_backoff_table(Index,Spvc) ->
+ case ets:lookup(spvcBackoff,Index) of
+ [Obj] ->
+ Obj;
+ _ ->
+ #spvcBackoff{key = Spvc#spvcObj.spvcEntry,
+ delay_time = Spvc#spvcObj.spvcRetryInterval,
+ flag = no_alarm}
+ end.
+
+set_fr_atm_iw_admin_state(FrEndPoint,up,Spvc) ->
+ ok;
+set_fr_atm_iw_admin_state(FrEndPoint,NewStatus,Spvc) ->
+ ok.
+
+forced_release(FrEndPoint) ->
+ FrPerm = spvcDataBase:db_read({spvcFr,FrEndPoint}),
+ case FrPerm of
+ [] ->
+ {error,no_fr_spvc};
+ _ ->
+ Key = FrPerm#spvcFr.spvcFrAtmEntry,
+ Spvc = spvcDataBase:db_read({spvcObj,Key}),
+ SpvcFrObj = spvcDataBase:db_read({spvcFrPerm,FrEndPoint}),
+ case SpvcFrObj#spvcFrPerm.spvcFrConnect of
+ 3 ->
+ SpvcTpToHcId = read_spvcTpToHcId(Key),
+ Release = spvcEncode:encode_cc_release(31),
+ spvcManager:release_un(b_side,SpvcTpToHcId#spvcTpToHcId.hcId,Release,Spvc);
+ _ ->
+ {error,target_not_owned_by_this_connection}
+ end
+ end.
+
+
+
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/wdp.hrl b/lib/dialyzer/test/user_tests_SUITE_data/src/wdp.hrl
new file mode 100644
index 0000000000..fa0e8af8c7
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/wdp.hrl
@@ -0,0 +1,97 @@
+
+%%
+%% WAP Port Number Definitions (WDP Appendix B.)
+%%
+
+-define(WAP_PORT_WTA_CL_SEC, 2805).
+-define(WAP_PORT_WTA_CO_SEC, 2923).
+-define(WAP_PORT_PUSH_CL, 2948).
+-define(WAP_PORT_PUSH_CL_SEC, 2949).
+
+-define(WAP_PORT_CL, 9200).
+-define(WAP_PORT_CO, 9201).
+-define(WAP_PORT_CL_SEC, 9202).
+-define(WAP_PORT_CO_SEC, 9203).
+-define(WAP_PORT_VCARD, 9204).
+-define(WAP_PORT_VCAL, 9205).
+-define(WAP_PORT_VCARD_SEC, 9206).
+-define(WAP_PORT_VCAL_SEC, 9207).
+
+-define(WAP_PORT_RINGTONE, 5505).
+-define(WAP_PORT_OPER_LOGO, 5506).
+-define(WAP_PORT_CLI_LOGO, 5507).
+
+%%
+%% WDP Bearer Type Assignments (WDP Appendix C.)
+%%
+
+%%
+%% Names after the tag WAP_BEARER_ is [network]_[bearer_type]_[address_type]
+%%
+-define(WAP_BEARER_ANY_ANY_IPV4, 16#00).
+-define(WAP_BEARER_ANY_ANY_IPV6, 16#01).
+-define(WAP_BEARER_GSM_USSD_ANY, 16#02).
+-define(WAP_BEARER_GSM_SMS_GSMMSISDN, 16#03).
+-define(WAP_BEARER_ANSI136_GUTS_ANSI136MSISDN, 16#04).
+-define(WAP_BEARER_IS95CDMA_SMS_IS637MSISDN, 16#05).
+-define(WAP_BEARER_IS95CDMA_CSD_IPV4, 16#06).
+-define(WAP_BEARER_IS95CDMA_PACKETDATA_IPV4, 16#07).
+-define(WAP_BEARER_ANSI136_CSD_IPV4, 16#08).
+-define(WAP_BEARER_ANSI136_PACKETDATA_IPV4, 16#09).
+-define(WAP_BEARER_GSM_CSD_IPV4, 16#0a).
+-define(WAP_BEARER_GSM_GPRS_IPV4, 16#0b).
+-define(WAP_BEARER_GSM_USSD_IPV4, 16#0c).
+-define(WAP_BEARER_AMPS_CDPD_IPV4, 16#0d).
+-define(WAP_BEARER_PDC_CSD_IPV4, 16#0e).
+-define(WAP_BEARER_PDC_PACKETDATA_IPV4, 16#0f).
+-define(WAP_BEARER_IDEN_SMS_IDENMSISDN, 16#10).
+-define(WAP_BEARER_IDEN_CSD_IPV4, 16#11).
+-define(WAP_BEARER_IDEN_PACKETDATA_IPV4, 16#12).
+-define(WAP_BEARER_PAGINGNETWORK_FLEX_FLEXMSISDN, 16#13).
+-define(WAP_BEARER_PHS_SMS_PHSMSISDN, 16#14).
+-define(WAP_BEARER_PHS_CSD_IPV4, 16#15).
+-define(WAP_BEARER_GSM_USSD_GSMSERVICECODE, 16#16).
+-define(WAP_BEARER_TETRA_SDS_TETRAITSI, 16#17).
+-define(WAP_BEARER_TETRA_SDS_TETRAMSISDN, 16#18).
+-define(WAP_BEARER_TETRA_PACKETDATA_IPV4, 16#19).
+-define(WAP_BEARER_PAGINGNETWORK_REFLEX_REFLEXMSISDN, 16#1a).
+-define(WAP_BEARER_GSM_USSD_GSMMSISDN, 16#1b).
+-define(WAP_BEARER_MOBITEX_MPAK_MAN, 16#1c).
+-define(WAP_BEARER_ANSI136_GHOST_GSMMSISDN, 16#1d).
+
+-record(wdp_address,
+ {
+ bearer,
+ address,
+ portnum
+ }).
+
+-record(wdp_sap_info,
+ {
+ mtu, %% max transmission unit (bytes)
+ mru %% max receive unit (bytes)
+ }).
+
+%%
+%% Source and destination address are wdp_addresses
+%%
+-record(wdp_socket_pair,
+ {
+ source,
+ destination
+ }).
+
+-record(wdp_local_port,
+ {
+ port, %% wdp "socket"
+ sap, %% source address
+ user, %% WDP user process
+ monitor %% monitor on WDP user
+ }).
+
+-record(wdp_local_sap,
+ {
+ sap, %% source address
+ port %% wdp "socket"
+ }).
+
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/wsp.hrl b/lib/dialyzer/test/user_tests_SUITE_data/src/wsp.hrl
new file mode 100644
index 0000000000..8190bd6f6f
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/wsp.hrl
@@ -0,0 +1,242 @@
+
+%% WSP Table 34. PDU Type Assignments
+%%
+
+-define(WSP_Connect, 16#01).
+-define(WSP_ConnectReply, 16#02).
+-define(WSP_Redirect, 16#03).
+-define(WSP_Reply, 16#04).
+-define(WSP_Disconnect, 16#05).
+-define(WSP_Push, 16#06).
+-define(WSP_ConfirmedPush, 16#07).
+-define(WSP_Suspend, 16#08).
+-define(WSP_Resume, 16#09).
+
+-define(WSP_Get, 16#40).
+-define(WSP_Options, 16#41).
+-define(WSP_Head, 16#42).
+-define(WSP_Delete, 16#43).
+-define(WSP_Trace, 16#44).
+
+-define(WSP_Post, 16#60).
+-define(WSP_Put, 16#61).
+
+-define(WSP_DataFragmentPDU, 16#80).
+
+%%
+%% WSP Table 37. Capability Assignments
+%%
+
+-define(WSP_CAP_CLIENT_SDU_SIZE, 16#00).
+-define(WSP_CAP_SERVER_SDU_SIZE, 16#01).
+-define(WSP_CAP_PROTOCOL_OPTIONS, 16#02).
+-define(WSP_CAP_METHOD_MOR, 16#03).
+-define(WSP_CAP_PUSH_MOR, 16#04).
+-define(WSP_CAP_EXTENDED_METHODS, 16#05).
+-define(WSP_CAP_HEADER_CODE_PAGES, 16#06).
+-define(WSP_CAP_ALIASES, 16#07).
+-define(WSP_CAP_CLIENT_MESSAGE_SIZE, 16#08).
+-define(WSP_CAP_SERVER_MESSAGE_SIZE, 16#09).
+
+-define(WSP_CODEPAGE_1, 1).
+-define(WSP_DEFAULT_CODEPAGE, ?WSP_CODEPAGE_1).
+
+-define(ANY_LANGUAGE,128).
+
+-define(WSP_10, {1,0}).
+-define(WSP_11, {1,1}).
+-define(WSP_12, {1,2}).
+-define(WSP_13, {1,3}).
+-define(WSP_14, {1,4}).
+-define(WSP_15, {1,5}).
+
+-define(WSP_COMPLIENT_VERSION, ?WSP_15).
+-define(WSP_DEFAULT_VERSION, ?WSP_12).
+
+-define(WSP_STATUS_CONTINUE, 100).
+-define(WSP_STATUS_SWITCHING_PROTOCOLS, 101).
+-define(WSP_STATUS_OK, 200).
+-define(WSP_STATUS_CREATED, 201).
+-define(WSP_STATUS_ACCEPTED, 202).
+-define(WSP_STATUS_NON_AUTHORITATIVE_INFORMATION, 203).
+-define(WSP_STATUS_NO_CONTENT, 204).
+-define(WSP_STATUS_RESET_CONTENT, 205).
+-define(WSP_STATUS_PARTIAL_CONTENT, 206).
+-define(WSP_STATUS_MULTIPLE_CHOICES, 300).
+-define(WSP_STATUS_MOVED_PERMANENTLY, 301).
+-define(WSP_STATUS_MOVED_TEMPORARILY, 302).
+-define(WSP_STATUS_SEE_OTHER, 303).
+-define(WSP_STATUS_NOT_MODIFIED, 304).
+-define(WSP_STATUS_USE_PROXY, 305).
+-define(WSP_STATUS_RESERVED, 306).
+-define(WSP_STATUS_TEMPORARY_REDIRECT, 307).
+-define(WSP_STATUS_BAD_REQUEST, 400).
+-define(WSP_STATUS_UNAUTHORIZED, 401).
+-define(WSP_STATUS_PAYMENT_REQUIRED, 402).
+-define(WSP_STATUS_FORBIDDEN, 403).
+-define(WSP_STATUS_NOT_FOUND, 404).
+-define(WSP_STATUS_METHOD_NOT_ALLOWED, 405).
+-define(WSP_STATUS_NOT_ACCEPTABLE, 406).
+-define(WSP_STATUS_PROXY_AUTHENTICATION_REQUIRED, 407).
+-define(WSP_STATUS_REQUEST_TIMEOUT, 408).
+-define(WSP_STATUS_CONFLICT, 409).
+-define(WSP_STATUS_GONE, 410).
+-define(WSP_STATUS_LENGTH_REQUIRED, 411).
+-define(WSP_STATUS_PRECONDITION_FAILED, 412).
+-define(WSP_STATUS_REQUEST_ENTITY_TOO_LARGE, 413).
+-define(WSP_STATUS_REQUEST_URI_TOO_LARGE, 414).
+-define(WSP_STATUS_UNSUPPORTED_MEDIA_TYPE, 415).
+-define(WSP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE, 416).
+-define(WSP_STATUS_EXPECTATION_FAILED, 417).
+-define(WSP_STATUS_INTERNAL_SERVER_ERROR, 500).
+-define(WSP_STATUS_NOT_IMPLEMENTED, 501).
+-define(WSP_STATUS_BAD_GATEWAY, 502).
+-define(WSP_STATUS_SERVICE_UNAVAILABLE, 503).
+-define(WSP_STATUS_GATEWAY_TIMEOUT, 504).
+-define(WSP_STATUS_HTTP_VERSION_NOT_SUPPORTED, 505).
+
+-define(ENCODE_SHORT(X), <<1:1, (X):7>>).
+
+-define(ENCODE_LONG(X),
+ if (X) =< 16#ff -> <<1, (X):8>>;
+ (X) =< 16#ffff -> <<2, (X):16>>;
+ (X) =< 16#ffffff -> <<3, (X):24>>;
+ (X) =< 16#ffffffff -> <<4, (X):32>>;
+ true -> encode_long1(X)
+ end).
+
+
+-record(wsp_session,
+ {
+ id, %% uniq session id
+ ref, %% address quadruple (socketpair)
+ state=null, %% connected, suspended
+ version, %% encoding version to use
+ capabilities, %% client capabilities
+ headers %% client hop-by-hop headers!!!
+ }).
+
+-record(wsp_header,
+ {
+ name, %% field name
+ value, %% field value (binary value)
+ params=[] %% field params [{Name,Value} | Value]
+ }).
+
+-record(wsp_multipart_entry,
+ {
+ content_type, %% #wsp_header
+ headers=[],
+ data=(<<>>)
+ }).
+
+-record(wsp_capabilities,
+ {
+ aliases=[], %% [#wdp_address]
+ client_sdu_size=1400,
+ extended_methods=[], %% [{PduType, Name}]
+ header_code_pages=[], %% [{Page,Name}] | [Page]
+ protocol_options=[], %% [push,confirmed_push,resume,
+ %% acknowledgement_headers]
+ method_mor = 10, %% 1?
+ push_mor = 10, %% 1?
+ server_sdu_size=1400,
+ client_message_size,
+ server_message_size,
+ unknown=[]
+ }).
+
+%% WSP PDU records
+
+-record(wsp_connect,
+ {
+ version, %% protocol version, not wsp version?
+ capabilities,
+ headers
+ }).
+
+-record(wsp_connect_reply,
+ {
+ server_session_id,
+ capabilities,
+ headers=[]
+ }).
+
+-define(WSP_PERMANENT_REDIRECT, 16#80).
+-define(WSP_REUSE_SECURITY, 16#40).
+
+-record(wsp_redirect,
+ {
+ flags=[],
+ addresses=[]
+ }).
+
+-record(wsp_disconnect,
+ {
+ server_session_id
+ }).
+
+-record(wsp_get,
+ {
+ type,
+ uri,
+ headers=[]
+ }).
+
+-record(wsp_post,
+ {
+ type,
+ uri,
+ content_type, %% #wsp_header
+ headers=[],
+ data
+ }).
+
+-record(wsp_reply,
+ {
+ status,
+ content_type, %% #wsp_header
+ headers=[],
+ data
+ }).
+
+-record(wsp_data_fragment_pdu,
+ {
+ headers=[],
+ data
+ }).
+
+-record(wsp_push,
+ {
+ type = push,
+ content_type, %% #wsp_header
+ headers=[],
+ data
+ }).
+
+-record(wsp_suspend,
+ {
+ session_id
+ }).
+
+-record(wsp_resume,
+ {
+ session_id,
+ capabilities,
+ headers
+ }).
+
+%% NOTE: not a real pdu
+-record(wsp_acknowledgement_headers,
+ {
+ headers=[]
+ }).
+
+-record(wsp_unknown_pdu,
+ {
+ type, %% integer
+ data %% the payload
+ }).
+
+
+
diff --git a/lib/dialyzer/test/user_tests_SUITE_data/src/wsp_pdu.erl b/lib/dialyzer/test/user_tests_SUITE_data/src/wsp_pdu.erl
new file mode 100644
index 0000000000..596a2f63ac
--- /dev/null
+++ b/lib/dialyzer/test/user_tests_SUITE_data/src/wsp_pdu.erl
@@ -0,0 +1,5423 @@
+%%%=======================================================================
+%%% File : wsp_pdu.erl
+%%% Author : Tony Rogvall <[email protected]>
+%%% Description : WSP PDU
+%%% Created : 18 Aug 2003 by <[email protected]>
+%%%=======================================================================
+%%%
+%%% There are a couple of bugs in this file. Some are detected by
+%%% Dialyzer v1.1 starting both from byte code and from source, some
+%%% other ones are detected only starting from sourse, while some
+%%% others go unnoticed (these are identified by "BUG" below). It is
+%%% expected that at least some of them are detected when the new type
+%%% analysis is integrated into Dialyzer. Some other ones, like the
+%%% one with the unused _Acc argument are harder to detect and might
+%%% require different techniques.
+%%%
+%%%=======================================================================
+
+-module(wsp_pdu).
+-export([encode/1, encode/2, decode/1, decode/2]).
+
+%% The following is just to suppress unused function warnings
+-export([decode_address/1, decode_header/2,
+ decode_headers/1, decode_mms_version/1, decode_multipart/1,
+ encode_headers/1, encode_mms_version/1, encode_multipart/1,
+ encode_language/1, encode_short_integer/1,
+ fmt_current_date/0,
+ format_header/1, format_headers/1,
+ parse_header/1, format/1]).
+
+-include("wsp.hrl").
+-include("wdp.hrl").
+
+-ifdef(debug).
+-define(dbg(Fmt,Args), io:format(Fmt, Args)).
+-else.
+-define(dbg(Fmt,Args), ok).
+-endif.
+
+-define(WARN(Cond, Message),
+ if (Cond) ->
+ io:format("Warning: ~s\n", [(Message)]);
+ true ->
+ ok
+ end).
+
+
+format(Pdu) ->
+ if record(Pdu, wsp_connect) ->
+ fmt(Pdu, record_info(fields, wsp_connect));
+ record(Pdu, wsp_connect_reply) ->
+ fmt(Pdu, record_info(fields, wsp_connect_reply));
+ record(Pdu, wsp_redirect) ->
+ fmt(Pdu, record_info(fields, wsp_redirect));
+ record(Pdu, wsp_disconnect) ->
+ fmt(Pdu, record_info(fields, wsp_disconnect));
+ record(Pdu, wsp_get) ->
+ fmt(Pdu, record_info(fields, wsp_get));
+ record(Pdu, wsp_post) ->
+ fmt(Pdu, record_info(fields, wsp_post));
+ record(Pdu,wsp_reply) ->
+ fmt(Pdu, record_info(fields, wsp_reply));
+ record(Pdu,wsp_data_fragment_pdu) ->
+ fmt(Pdu, record_info(fields, wsp_data_fragment_pdu));
+ record(Pdu,wsp_push) ->
+ fmt(Pdu, record_info(fields, wsp_push));
+ record(Pdu, wsp_suspend) ->
+ fmt(Pdu, record_info(fields, wsp_suspend));
+ record(Pdu, wsp_resume) ->
+ fmt(Pdu, record_info(fields, wsp_resume));
+ record(Pdu, wsp_unknown_pdu) ->
+ fmt(Pdu, record_info(fields, wsp_unknown_pdu))
+ end.
+
+fmt(Pdu, Fs) ->
+ [Name | Vs] = tuple_to_list(Pdu),
+ lists:flatten(["\n",atom_to_list(Name)," {\n" , fmt1(Fs, Vs), "\n}"]).
+
+fmt1([F|Fs],[V|Vs]) ->
+ [io_lib:format(" ~s: ~s;\n", [F,fmt_value(V)]) | fmt1(Fs, Vs)];
+fmt1([], []) ->
+ "".
+
+fmt_value(V) when binary(V) -> "#Bin";
+fmt_value(V) -> lists:flatten(io_lib:format("~p",[V])).
+
+
+%%
+%% Wsp pdu encoder
+%%
+encode(Pdu) ->
+ encode(Pdu, ?WSP_DEFAULT_VERSION).
+
+encode(Pdu, Version) ->
+ ?dbg("encode pdu using encoding version ~p\n", [Version]),
+ Enc = encode1(Pdu, Version),
+ ?dbg("pdu: ~p\nreversed pdu: ~p\n",
+ [Pdu, decode(Enc, Version)]),
+ Enc.
+
+
+encode1(Pdu, Version) ->
+ case Pdu of
+ #wsp_connect_reply {server_session_id=ServerSessionId,
+ capabilities=Capabilities,
+ headers=Headers} ->
+ EncServerSessionId = e_uintvar(ServerSessionId),
+ EncCapabilities = encode_capabilities(Capabilities),
+ EncCapabilitiesLength = e_uintvar(size(EncCapabilities)),
+ EncHeaders = encode_headers(Headers,Version),
+ EncHeadersLength = e_uintvar(size(EncHeaders)),
+ <<?WSP_ConnectReply,
+ EncServerSessionId/binary,
+ EncCapabilitiesLength/binary, EncHeadersLength/binary,
+ EncCapabilities/binary, EncHeaders/binary>>;
+
+ #wsp_reply{ status=Status,
+ content_type=ContentType,
+ headers=Headers,
+ data=Data} ->
+ EncStatus = encode_status_code(Status),
+ EncContentType = encode_content_type(ContentType,Version),
+ EncHeaders = encode_headers(Headers,Version),
+ EncHeadersLength = e_uintvar(size(EncContentType)+
+ size(EncHeaders)),
+ <<?WSP_Reply,
+ EncStatus:8,
+ EncHeadersLength/binary,
+ EncContentType/binary,
+ EncHeaders/binary,
+ Data/binary>>;
+
+ #wsp_post{type=Type, uri=URI, content_type=ContentType,
+ headers=Headers, data=Data} ->
+ %% WSP_Post, WSP_Put
+ PDUType = encode_pdu_type(Type),
+ UriLength = e_uintvar(length(URI)),
+ EncContentType = encode_content_type(ContentType,Version),
+ EncHeaders = encode_headers(Headers,Version),
+ EncHeadersLength = e_uintvar(size(EncContentType)+
+ size(EncHeaders)),
+ %% FIXME
+ <<PDUType:8,
+ UriLength/binary,
+ EncHeadersLength/binary,
+ (list_to_binary(URI))/binary,
+ EncContentType/binary,
+ EncHeaders/binary,
+ Data/binary>>;
+
+ #wsp_push{type=Type, content_type=ContentType,
+ headers=Headers, data=Data} ->
+ %% WSP_Push, WSP_ConfirmedPush
+ PDUType = encode_pdu_type(Type),
+ EncContentType = encode_content_type(ContentType,Version),
+ EncHeaders = encode_headers(Headers,Version),
+ ?dbg("Version ~p Headers ~p", [Version, Headers]),
+ ?dbg("EncHeaders ~p", [EncHeaders]),
+ EncHeadersLength = e_uintvar(size(EncContentType)+
+ size(EncHeaders)),
+ ?dbg("EncCT = ~w ~w", [ContentType, EncContentType]),
+ ?dbg("EncHL = ~w", [EncHeadersLength]),
+ <<PDUType:8,
+ EncHeadersLength/binary,
+ EncContentType/binary,
+ EncHeaders/binary,
+ Data/binary>>;
+
+ #wsp_get{type=Type, uri=URI, headers=Headers} ->
+ %% WSP_Get, WSP_Options, WSP_Head, WSP_Delete, WSP_Trace
+ PDUType = encode_pdu_type(Type),
+ UriLength = length(URI),
+ EncHeaders = encode_headers(Headers,Version),
+ <<PDUType:8,
+ (e_uintvar(UriLength))/binary,
+ (list_to_binary(URI))/binary,
+ EncHeaders/binary>>;
+
+ #wsp_redirect { flags = Flags, addresses = Addrs } ->
+ Flg = lists:foldl(fun(permanent,F) ->
+ ?WSP_PERMANENT_REDIRECT bor F;
+ (resue, F) ->
+ ?WSP_REUSE_SECURITY bor F
+ end, 0, Flags),
+ EncAddr = encode_addresses(Addrs),
+ <<?WSP_Redirect, Flg:8, EncAddr/binary >>;
+
+
+ #wsp_data_fragment_pdu { headers=Headers, data=Data } ->
+ EncHeaders = encode_headers(Headers,Version),
+ << ?WSP_DataFragmentPDU, EncHeaders/binary, Data/binary >>
+ end.
+
+decode(Data) ->
+ decode(Data, ?WSP_COMPLIENT_VERSION).
+
+decode(Data0, Version) ->
+ case Data0 of
+ <<?WSP_Connect:8,PduVersion:8,D0/binary>> ->
+ %% 8.2.2.1
+ {CapabilitiesLen,D1} = d_uintvar(D0),
+ {HeadersLen,D2} = d_uintvar(D1),
+ {Capabilities,D3} = split_binary(D2, CapabilitiesLen),
+ Caps = decode_capabilities(Capabilities,#wsp_capabilities{}),
+ {Headers,D4} = split_binary(D3, HeadersLen),
+ DecHeaders = decode_headers(Headers, Version),
+ ?WARN(D4 =/= <<>>, "Connect pdu contains trailing data"),
+ %% FIXME: warn when D4 is not <<>>
+ #wsp_connect{ version = PduVersion,
+ capabilities=Caps,
+ headers = DecHeaders };
+
+ <<?WSP_ConnectReply:8,D0/binary>> ->
+ %% 8.2.2.2
+ {ServerSessionId,D1} = d_uintvar(D0),
+ {CapabilitiesLen,D2} = d_uintvar(D1),
+ {HeadersLen,D3} = d_uintvar(D2),
+ {Capabilities,D4} = split_binary(D3, CapabilitiesLen),
+ Caps = decode_capabilities(Capabilities,#wsp_capabilities{}),
+ {Headers,D5} = split_binary(D4, HeadersLen),
+ DecHeaders = decode_headers(Headers, Version),
+ ?WARN(D5 =/= <<>>, "ConnectReply pdu contains trailing data"),
+ #wsp_connect_reply{server_session_id=ServerSessionId,
+ capabilities=Caps,
+ headers=DecHeaders};
+
+ <<?WSP_Redirect:8,Flg:8,D0/binary>> ->
+ Flags =
+ if Flg band ?WSP_PERMANENT_REDIRECT =/= 0 -> [permanent];
+ true -> []
+ end ++
+ if Flg band ?WSP_REUSE_SECURITY =/= 0 -> [security];
+ true -> []
+ end,
+ Addrs = decode_addresses(D0),
+ %% 8.2.2.3 Redirect
+ #wsp_redirect{flags=Flags,addresses=Addrs};
+
+
+ <<?WSP_Disconnect:8,D0/binary>> ->
+ %% 8.2.2.4 Disconnect
+ {ServerSessionId,_D1} = d_uintvar(D0),
+ #wsp_disconnect{server_session_id=ServerSessionId};
+
+ <<?WSP_Get:8,D0/binary>> ->
+ {URILength, D1} = d_uintvar(D0),
+ <<UriData:URILength/binary,D2/binary>> = D1,
+ Hs = decode_headers(D2, Version),
+ #wsp_get{type='GET',uri=binary_to_list(UriData),headers=Hs };
+
+ <<?WSP_Options:8,D0/binary>> ->
+ {URILength, D1} = d_uintvar(D0),
+ <<UriData:URILength/binary,D2/binary>> = D1,
+ Hs = decode_headers(D2, Version),
+ #wsp_get{type='OPTIONS',uri=binary_to_list(UriData),headers=Hs };
+
+ <<?WSP_Head:8,D0/binary>> ->
+ {URILength, D1} = d_uintvar(D0),
+ <<UriData:URILength/binary,D2/binary>> = D1,
+ Hs = decode_headers(D2, Version),
+ #wsp_get{type='HEAD',uri=binary_to_list(UriData),headers=Hs };
+
+ <<?WSP_Delete:8,D0/binary>> ->
+ {URILength, D1} = d_uintvar(D0),
+ <<UriData:URILength/binary,D2/binary>> = D1,
+ Hs = decode_headers(D2, Version),
+ #wsp_get{type='DELETE',uri=binary_to_list(UriData),headers=Hs };
+
+ <<?WSP_Trace:8,D0/binary>> ->
+ {URILength, D1} = d_uintvar(D0),
+ <<UriData:URILength/binary,D2/binary>> = D1,
+ Hs = decode_headers(D2, Version),
+ #wsp_get{type='TRACE',uri=binary_to_list(UriData),headers=Hs };
+
+ %% 8.2.3.2 Post
+ <<?WSP_Post:8,D0/binary>> ->
+ {URILen, D1} = d_uintvar(D0),
+ {HL0, D2} = d_uintvar(D1),
+ <<UriData:URILen/binary,D3/binary>> = D2,
+ {FieldData,D4} = scan_header_data(D3),
+ HL1 = (HL0-(size(D3)-size(D4))),
+ <<D5:HL1/binary,Data/binary>> = D4,
+ ContentType = decode_content_type(FieldData, Version),
+ Headers = decode_headers(D5, Version),
+ #wsp_post{ type='POST', uri=binary_to_list(UriData),
+ content_type=ContentType, headers=Headers, data=Data};
+
+ <<?WSP_Put:8,D0/binary>> ->
+ {URILen, D1} = d_uintvar(D0),
+ {HL0, D2} = d_uintvar(D1),
+ <<UriData:URILen/binary,D3/binary>> = D2,
+ {FieldData,D4} = scan_header_data(D3),
+ HL1 = (HL0-(size(D3)-size(D4))),
+ <<D5:HL1/binary,Data/binary>> = D4,
+ ContentType = decode_content_type(FieldData, Version),
+ Headers = decode_headers(D5, Version),
+ #wsp_post{ type='PUT', uri=binary_to_list(UriData),
+ content_type=ContentType, headers=Headers, data=Data};
+
+ <<?WSP_Reply:8,StatusCode:8,D0/binary>> ->
+ %% 8.2.3.3 Reply
+ Status = decode_status_code(StatusCode),
+ {HL0, D1} = d_uintvar(D0),
+ {FieldData, D2} = scan_header_data(D1),
+ ContentType = decode_content_type(FieldData, Version),
+ %% Headers are headersLength - binary size of content type
+ HL1 = (HL0-(size(D1)-size(D2))),
+ <<D3:HL1/binary,Data/binary>> = D2,
+ Hs = decode_headers(D3, Version),
+ #wsp_reply{status=Status, content_type=ContentType,
+ headers=Hs, data=Data};
+
+ <<?WSP_DataFragmentPDU:8,D0/binary>> ->
+ %% 8.2.3.4 Data Fragment PDU
+ {HL0, D1} = d_uintvar(D0),
+ <<D2:HL0/binary,Data/binary>> = D1,
+ Hs = decode_headers(D2, Version),
+ #wsp_data_fragment_pdu{headers=Hs, data=Data};
+
+ %% 8.2.4.1 Push or ConfirmedPush
+ <<?WSP_Push:8,D0/binary>> ->
+ {HeadersLength, T200} = d_uintvar(D0),
+ {FieldData, T300} = scan_header_data(T200),
+ ContentType = decode_content_type(FieldData, Version),
+ RealHeadersLength = (HeadersLength-(size(T200)-size(T300))),
+ <<T400:RealHeadersLength/binary,Data/binary>> = T300,
+ Headers = decode_headers(T400, Version),
+ #wsp_push{type=push,content_type=ContentType,
+ headers=Headers,data=Data};
+
+ <<?WSP_ConfirmedPush:8,D0/binary>> ->
+ {HeadersLength, T200} = d_uintvar(D0),
+ {FieldData, T300} = scan_header_data(T200),
+ ContentType = decode_content_type(FieldData, Version),
+ RealHeadersLength = (HeadersLength-(size(T200)-size(T300))),
+ <<T400:RealHeadersLength/binary,Data/binary>> = T300,
+ Headers = decode_headers(T400, Version),
+ #wsp_push{type=confirmed_push,
+ content_type=ContentType,
+ headers=Headers,data=Data};
+
+ <<PDUType:8,T100/binary>> ->
+ #wsp_unknown_pdu { type = PDUType, data = T100 }
+ end.
+
+
+encode_pdu_type(connect) -> ?WSP_Connect;
+encode_pdu_type(connect_reply) -> ?WSP_ConnectReply;
+encode_pdu_type(redirect) -> ?WSP_Redirect;
+encode_pdu_type(reply) -> ?WSP_Reply;
+encode_pdu_type(disconnect) -> ?WSP_Disconnect;
+encode_pdu_type(push) -> ?WSP_Push;
+encode_pdu_type(confirmed_push) -> ?WSP_ConfirmedPush;
+encode_pdu_type(suspend) -> ?WSP_Suspend;
+encode_pdu_type(resume) -> ?WSP_Resume;
+encode_pdu_type(data_fragment_pdu) -> ?WSP_DataFragmentPDU;
+encode_pdu_type('GET') -> ?WSP_Get;
+encode_pdu_type('OPTIONS') -> ?WSP_Options;
+encode_pdu_type('HEAD') -> ?WSP_Head;
+encode_pdu_type('DELETE') -> ?WSP_Delete;
+encode_pdu_type('TRACE') -> ?WSP_Trace;
+encode_pdu_type('POST') -> ?WSP_Post;
+encode_pdu_type('PUT') -> ?WSP_Put;
+encode_pdu_type(Type) when integer(Type) -> Type.
+
+
+decode_pdu_type(?WSP_Connect) -> connect;
+decode_pdu_type(?WSP_ConnectReply) -> connect_reply;
+decode_pdu_type(?WSP_Redirect) -> redirect;
+decode_pdu_type(?WSP_Reply) -> reply;
+decode_pdu_type(?WSP_Disconnect) -> disconnect;
+decode_pdu_type(?WSP_Push) -> push;
+decode_pdu_type(?WSP_ConfirmedPush) -> confirmed_push;
+decode_pdu_type(?WSP_Suspend) -> suspend;
+decode_pdu_type(?WSP_Resume) -> resume;
+decode_pdu_type(?WSP_DataFragmentPDU) -> data_fragment_pdu;
+decode_pdu_type(?WSP_Get) -> 'GET';
+decode_pdu_type(?WSP_Options) -> 'OPTIONS';
+decode_pdu_type(?WSP_Head) -> 'HEAD';
+decode_pdu_type(?WSP_Delete) -> 'DELETE';
+decode_pdu_type(?WSP_Trace) -> 'TRACE';
+decode_pdu_type(?WSP_Post) -> 'POST';
+decode_pdu_type(?WSP_Put) -> 'PUT';
+decode_pdu_type(Type) -> Type. %% allow unknown pdu types.
+
+
+%% Convert various data types to list
+
+to_list(I) when integer(I) ->
+ integer_to_list(I);
+to_list(A) when atom(A) ->
+ atom_to_list(A);
+to_list(Version={X,Y}) when integer(X), integer(Y) ->
+ format_version(Version);
+to_list(DateTime={{_,_,_},{_,_,_}}) ->
+ fmt_date(DateTime);
+to_list(L) when list(L) ->
+ L.
+
+
+
+encode_capabilities(Capa) ->
+ encode_capabilities(Capa,#wsp_capabilities{}).
+
+encode_capabilities(Cap,Def) ->
+ Known =
+ [encode_capability(?WSP_CAP_ALIASES,
+ Cap#wsp_capabilities.aliases,
+ Def#wsp_capabilities.aliases),
+ encode_capability(?WSP_CAP_CLIENT_SDU_SIZE,
+ Cap#wsp_capabilities.client_sdu_size,
+ Def#wsp_capabilities.client_sdu_size),
+ encode_capability(?WSP_CAP_SERVER_SDU_SIZE,
+ Cap#wsp_capabilities.server_sdu_size,
+ Def#wsp_capabilities.server_sdu_size),
+ encode_capability(?WSP_CAP_PROTOCOL_OPTIONS,
+ Cap#wsp_capabilities.protocol_options,
+ Def#wsp_capabilities.protocol_options),
+ encode_capability(?WSP_CAP_METHOD_MOR,
+ Cap#wsp_capabilities.method_mor,
+ Def#wsp_capabilities.method_mor),
+ encode_capability(?WSP_CAP_PUSH_MOR,
+ Cap#wsp_capabilities.push_mor,
+ Def#wsp_capabilities.push_mor),
+ encode_capability(?WSP_CAP_EXTENDED_METHODS,
+ Cap#wsp_capabilities.extended_methods,
+ Def#wsp_capabilities.extended_methods),
+ encode_capability(?WSP_CAP_HEADER_CODE_PAGES,
+ Cap#wsp_capabilities.header_code_pages,
+ Def#wsp_capabilities.header_code_pages),
+ encode_capability(?WSP_CAP_CLIENT_MESSAGE_SIZE,
+ Cap#wsp_capabilities.client_message_size,
+ Def#wsp_capabilities.client_message_size),
+ encode_capability(?WSP_CAP_SERVER_MESSAGE_SIZE,
+ Cap#wsp_capabilities.server_message_size,
+ Def#wsp_capabilities.server_message_size)],
+ Unknown =
+ lists:map(fun({Id, Data}) when integer(Id) ->
+ <<1:1, Id:7, Data/binary>>;
+ ({Id,Data}) ->
+ <<(encode_text_string(Id))/binary, Data/binary>>
+ end, Cap#wsp_capabilities.unknown),
+ list_to_binary(
+ lists:map(fun(<<>>) -> [];
+ (Bin) ->
+ [e_uintvar(size(Bin)), Bin]
+ end, Known ++ Unknown)).
+
+
+
+
+encode_capability(_Capa, Default, Default) ->
+ <<>>;
+encode_capability(Capa, Value, _) ->
+ case Capa of
+ ?WSP_CAP_ALIASES ->
+ <<1:1, ?WSP_CAP_ALIASES:7, (encode_addresses(Value))/binary>>;
+
+ ?WSP_CAP_CLIENT_SDU_SIZE ->
+ <<1:1, ?WSP_CAP_CLIENT_SDU_SIZE:7, (e_uintvar(Value))/binary>>;
+
+ ?WSP_CAP_SERVER_SDU_SIZE ->
+ <<1:1, ?WSP_CAP_SERVER_SDU_SIZE:7, (e_uintvar(Value))/binary>>;
+
+ ?WSP_CAP_PROTOCOL_OPTIONS ->
+ Opts = case lists:member(confirmed_push, Value) of
+ true -> 16#80;
+ false -> 0
+ end bor
+ case lists:member(push, Value) of
+ true -> 16#40;
+ false -> 0
+ end bor
+ case lists:member(resume, Value) of
+ true -> 16#20;
+ false -> 0
+ end bor
+ case lists:member(acknowledgement_headers, Value) of
+ true -> 16#10;
+ false -> 0
+ end,
+ %% FIXME: symbolic encode/decode of options
+ <<1:1, ?WSP_CAP_PROTOCOL_OPTIONS:7, Opts>>;
+
+ ?WSP_CAP_METHOD_MOR ->
+ <<1:1, ?WSP_CAP_METHOD_MOR:7, (e_uintvar(Value))/binary>>;
+
+ ?WSP_CAP_PUSH_MOR ->
+ <<1:1, ?WSP_CAP_PUSH_MOR:7, (e_uintvar(Value))/binary>>;
+
+ ?WSP_CAP_EXTENDED_METHODS ->
+ <<1:1, ?WSP_CAP_EXTENDED_METHODS:7,
+ (encode_extended_methods(Value))/binary>>;
+
+ ?WSP_CAP_HEADER_CODE_PAGES ->
+ Data = list_to_binary(
+ lists:map(fun(Page) when integer(Page) -> Page;
+ ({Page,Name}) ->
+ [Page, encode_text_string(Name)]
+ end, Value)),
+ <<1:1, ?WSP_CAP_HEADER_CODE_PAGES:7, Data/binary>>;
+
+ ?WSP_CAP_CLIENT_MESSAGE_SIZE ->
+ <<1:1, ?WSP_CAP_CLIENT_MESSAGE_SIZE:7,
+ (e_uintvar(Value))/binary>>;
+
+ ?WSP_CAP_SERVER_MESSAGE_SIZE ->
+ <<1:1, ?WSP_CAP_SERVER_MESSAGE_SIZE:7,
+ (e_uintvar(Value))/binary>>;
+ _ when integer(Capa) ->
+ <<1:1, Capa:7, Value/binary>>;
+ _ when list(Capa) ->
+ <<(encode_text_string(Capa))/binary, Value/binary>>
+ end.
+
+
+decode_capabilities(<<>>, WspCaps) ->
+ WspCaps;
+decode_capabilities(D0,WspCaps) ->
+ {Len, D1} = d_uintvar(D0),
+ <<Capa:Len/binary, D2/binary>> = D1,
+ WspCaps1 =
+ case Capa of
+ <<1:1, Id:7, Data/binary>> ->
+ decode_capa(Id, Data, WspCaps);
+ _ ->
+ {Id,Data} = d_text_string(Capa),
+ decode_capa(Id, Data, WspCaps)
+ end,
+ decode_capabilities(D2, WspCaps1).
+
+
+
+decode_capa(Id,Data, WspCaps) ->
+ case Id of
+ ?WSP_CAP_SERVER_SDU_SIZE ->
+ {Val,_} = d_uintvar(Data),
+ WspCaps#wsp_capabilities{server_sdu_size=Val};
+
+ ?WSP_CAP_CLIENT_SDU_SIZE ->
+ {Val,_} = d_uintvar(Data),
+ WspCaps#wsp_capabilities{client_sdu_size=Val};
+
+ ?WSP_CAP_PROTOCOL_OPTIONS ->
+ <<POP,_/binary>> = Data,
+ Opts =
+ if POP band 16#80 == 16#80 -> [confirmed_push];
+ true -> []
+ end ++
+ if POP band 16#40 == 16#40 -> [push];
+ true -> []
+ end ++
+ if POP band 16#20 == 16#20 -> [resume];
+ true -> []
+ end ++
+ if POP band 16#10 == 16#10 -> [acknowledgement_headers];
+ true -> []
+ end,
+ WspCaps#wsp_capabilities{protocol_options=Opts};
+
+ ?WSP_CAP_METHOD_MOR ->
+ {Val,_} = d_uintvar(Data),
+ WspCaps#wsp_capabilities{method_mor=Val};
+
+ ?WSP_CAP_PUSH_MOR ->
+ {Val,_} = d_uintvar(Data),
+ WspCaps#wsp_capabilities{push_mor=Val};
+
+ ?WSP_CAP_EXTENDED_METHODS ->
+ Extended = decode_extended_methods(Data),
+ WspCaps#wsp_capabilities { extended_methods = Extended };
+
+ ?WSP_CAP_HEADER_CODE_PAGES ->
+ %% Client send [Code(uint8) Name(text-string)]*
+ %% Server send [Code(uint8)]*
+ io:format("FIXME: Header Code Pages = ~p\n",[Data]),
+ WspCaps;
+
+ ?WSP_CAP_ALIASES ->
+ Aliases = decode_addresses(Data),
+ WspCaps#wsp_capabilities { aliases = Aliases };
+
+ ?WSP_CAP_CLIENT_MESSAGE_SIZE ->
+ {Val,_} = d_uintvar(Data),
+ WspCaps#wsp_capabilities{client_message_size=Val};
+
+ ?WSP_CAP_SERVER_MESSAGE_SIZE ->
+ {Val,_} = d_uintvar(Data),
+ WspCaps#wsp_capabilities{server_message_size=Val};
+ _ ->
+ Unknown = [{Id, Data} | WspCaps#wsp_capabilities.unknown],
+ io:format("WARNING: ignoring unknown capability ~p\n",
+ [Unknown]),
+ WspCaps#wsp_capabilities{unknown = Unknown}
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Headers = [ Header ]
+%% Header = {FieldName, FieldValue}
+%% FieldName = atom()
+%% FieldValue = {Value, Params}
+%% | Value
+%%
+%% Params = [{Param,Value} | Param]
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(WH(Name,Value,Params),
+ #wsp_header { name = (Name), value = (Value), params = Params}).
+
+encode_headers(Headers) ->
+ encode_headers(Headers, ?WSP_DEFAULT_VERSION).
+
+encode_headers(Headers, Version) ->
+ encode_headers(Headers, Version, []).
+
+encode_headers([H|T], Version, Acc) ->
+ encode_headers(T, Version, [encode_header(H, Version)|Acc]);
+encode_headers([], _, Acc) ->
+ list_to_binary(lists:reverse(Acc)).
+
+
+decode_headers(Bin) ->
+ decode_headers(Bin, ?WSP_DEFAULT_VERSION).
+
+decode_headers(<<>>, _Version) ->
+ [];
+decode_headers(Data, Version) ->
+ decode_headers(Data, [], Version, ?WSP_DEFAULT_CODEPAGE).
+
+
+decode_headers(<<1:1,Code:7,Data/binary>>,Acc,Version,CP) ->
+ FieldName = lookup_field_name(Code),
+ {FieldData,Data1} = scan_header_data(Data),
+ H = decode_header(FieldName, FieldData,Version,CP),
+ ?dbg("header: ~p, field data=~p, header=~p\n",
+ [FieldName, FieldData, H]),
+ if H#wsp_header.name == 'Encoding-Version' ->
+ Version1 = H#wsp_header.value,
+ ?dbg("Version switch from ~w to ~w\n", [Version, Version1]),
+ decode_headers(Data1,[H|Acc],Version1, CP);
+ true ->
+ decode_headers(Data1,[H|Acc],Version, CP)
+ end;
+decode_headers(Data = <<Code,_/binary>>,Acc,Version,CP)
+ when Code >= 32, Code < 127->
+ {TmpField,Data1} = d_text_string(Data),
+ FieldName = normalise_field_name(TmpField),
+ {FieldData,Data2} = scan_header_data(Data1),
+ H = decode_header(FieldName,FieldData,Version,CP),
+ ?dbg("header: ~p, field data=~p, header=~p\n",
+ [FieldName, FieldData, H]),
+ if H#wsp_header.name == 'Encoding-Version' ->
+ Version1 = H#wsp_header.value,
+ ?dbg("Version switch from ~w to ~w\n", [Version, Version1]),
+ decode_headers(Data2,[H|Acc],Version1, CP);
+ true ->
+ decode_headers(Data2,[H|Acc],Version, CP)
+ end;
+decode_headers(<<CP1,Data/binary>>,Acc,Version,_CP) when CP1 >= 1, CP1 =< 31 ->
+ ?dbg("decode_headers: codpage changed form ~w -> ~w\n",[_CP,CP1]),
+ decode_headers(Data,Acc,Version,CP1);
+decode_headers(<<16#7f,CP1,Data/binary>>,Acc,Version,_CP) ->
+ ?dbg("decode_headers: codpage changed form ~w -> ~w\n",[_CP,CP1]),
+ decode_headers(Data,Acc,Version,CP1);
+
+decode_headers(<<>>, Acc, _Version, _CP) ->
+ lists:reverse(Acc).
+
+%%
+%% Retrive the header data
+%% (this makes it possible to skip unknown encoding)
+%%
+scan_header_data(Data = <<N,Data0/binary>>) ->
+ if N >= 0, N =< 30 ->
+ <<Value:N/binary, Data1/binary>> = Data0,
+ {{short,Value}, Data1};
+ N == 31 ->
+ {N1, Data1} = d_uintvar(Data0),
+ <<Value:N1/binary, Data2/binary>> = Data1,
+ {{long,Value}, Data2};
+ N >= 32, N =< 127 ->
+ d_text_string(Data);
+ true ->
+ { N band 16#7f, Data0}
+ end.
+
+%%
+%% Decode header: return #wsp_header
+%%
+decode_header(Field, Value) ->
+ decode_header(Field, Value,
+ ?WSP_DEFAULT_VERSION,
+ ?WSP_DEFAULT_CODEPAGE).
+
+decode_header(Field, Value, Version, 1) ->
+ case Field of
+ 'Accept' ->
+ decode_accept(Value, Version);
+
+ 'Accept-Charset' when Version >= ?WSP_13 ->
+ decode_accept_charset(Value, Version);
+ 'Accept-Charset' ->
+ decode_accept_charset(Value, Version);
+
+ 'Accept-Encoding' when Version >= ?WSP_13 ->
+ decode_accept_encoding(Value, Version);
+ 'Accept-Encoding' ->
+ decode_accept_encoding(Value, Version);
+
+ 'Accept-Language' ->
+ decode_accept_language(Value, Version);
+ 'Accept-Ranges' ->
+ decode_accept_ranges(Value, Version);
+ 'Age' ->
+ decode_age(Value,Version);
+ 'Allow' ->
+ decode_allow(Value,Version);
+ 'Authorization' ->
+ decode_authorization(Value,Version);
+
+ 'Cache-Control' when Version >= ?WSP_14 ->
+ decode_cache_control(Value,Version);
+ 'Cache-Control' when Version >= ?WSP_13 ->
+ decode_cache_control(Value,Version);
+ 'Cache-Control' ->
+ decode_cache_control(Value,Version);
+
+ 'Connection' ->
+ decode_connection(Value,Version);
+ 'Content-Base' ->
+ decode_content_base(Value,Version);
+ 'Content-Encoding' ->
+ decode_content_encoding(Value,Version);
+ 'Content-Language' ->
+ decode_content_language(Value,Version);
+ 'Content-Length' ->
+ decode_content_length(Value,Version);
+ 'Content-Location' ->
+ decode_content_location(Value,Version);
+ 'Content-Md5' ->
+ decode_content_md5(Value,Version);
+
+ 'Content-Range' when Version >= ?WSP_13 ->
+ decode_content_range(Value,Version);
+ 'Content-Range' ->
+ decode_content_range(Value,Version);
+
+ 'Content-Type' ->
+ decode_content_type(Value,Version);
+ 'Date' ->
+ decode_date(Value, Version);
+ 'Etag' ->
+ decode_etag(Value,Version);
+ 'Expires' ->
+ decode_expires(Value,Version);
+ 'From' ->
+ decode_from(Value,Version);
+ 'Host' ->
+ decode_host(Value,Version);
+ 'If-Modified-Since' ->
+ decode_if_modified_since(Value,Version);
+ 'If-Match' ->
+ decode_if_match(Value,Version);
+ 'If-None-Match' ->
+ decode_if_none_match(Value,Version);
+ 'If-Range' ->
+ decode_if_range(Value,Version);
+ 'If-Unmodified-Since' ->
+ decode_if_unmodified_since(Value,Version);
+ 'Location' ->
+ decode_location(Value,Version);
+ 'Last-Modified' ->
+ decode_last_modified(Value,Version);
+ 'Max-Forwards' ->
+ decode_max_forwards(Value,Version);
+ 'Pragma' ->
+ decode_pragma(Value,Version);
+ 'Proxy-Authenticate' ->
+ decode_proxy_authenticate(Value,Version);
+ 'Proxy-Authorization' ->
+ decode_proxy_authorization(Value,Version);
+ 'Public' ->
+ decode_public(Value,Version);
+ 'Range' ->
+ decode_range(Value,Version);
+ 'Referer' ->
+ decode_referer(Value,Version);
+ 'Retry-After' ->
+ decode_retry_after(Value,Version);
+ 'Server' ->
+ decode_server(Value,Version);
+ 'Transfer-Encoding' ->
+ decode_transfer_encoding(Value,Version);
+ 'Upgrade' ->
+ decode_upgrade(Value,Version);
+ 'User-Agent' ->
+ decode_user_agent(Value,Version);
+ 'Vary' ->
+ decode_vary(Value,Version);
+ 'Via' ->
+ decode_via(Value,Version);
+ 'Warning' ->
+ decode_warning(Value,Version);
+ 'Www-Authenticate' ->
+ decode_www_authenticate(Value,Version);
+
+ 'Content-Disposition' when Version >= ?WSP_14 ->
+ decode_content_disposition(Value,Version);
+ 'Content-Disposition' ->
+ decode_content_disposition(Value,Version);
+
+ 'X-Wap-Application-Id' when Version >= ?WSP_12 ->
+ decode_x_wap_application_id(Value,Version);
+
+ 'X-Wap-Content-Uri' when Version >= ?WSP_12 ->
+ decode_x_wap_content_uri(Value,Version);
+
+ 'X-Wap-Initiator-Uri' when Version >= ?WSP_12 ->
+ decode_x_wap_initiator_uri(Value,Version);
+
+ 'Accept-Application' when Version >= ?WSP_12 ->
+ decode_accept_application(Value,Version);
+
+ 'Bearer-Indication' when Version >= ?WSP_12 ->
+ decode_bearer_indication(Value,Version);
+
+ 'Push-Flag' when Version >= ?WSP_12 ->
+ decode_push_flag(Value,Version);
+
+ 'Profile' when Version >= ?WSP_12 ->
+ decode_profile(Value,Version);
+
+ 'Profile-Diff' when Version >= ?WSP_12 ->
+ decode_profile_diff(Value,Version);
+
+ 'Profile-Warning' when Version >= ?WSP_12 ->
+ decode_profile_warning(Value,Version);
+
+ 'Expect' when Version >= ?WSP_15 ->
+ decode_expect(Value,Version);
+ 'Expect' when Version >= ?WSP_13 ->
+ decode_expect(Value,Version);
+
+ 'Te' when Version >= ?WSP_13 ->
+ decode_te(Value,Version);
+ 'Trailer' when Version >= ?WSP_13 ->
+ decode_trailer(Value,Version);
+
+ 'X-Wap-Tod' when Version >= ?WSP_13 ->
+ decode_x_wap_tod(Value,Version);
+ 'X-Wap.tod' when Version >= ?WSP_13 ->
+ decode_x_wap_tod(Value,Version);
+
+ 'Content-Id' when Version >= ?WSP_13 ->
+ decode_content_id(Value,Version);
+ 'Set-Cookie' when Version >= ?WSP_13 ->
+ decode_set_cookie(Value,Version);
+ 'Cookie' when Version >= ?WSP_13 ->
+ decode_cookie(Value,Version);
+
+ 'Encoding-Version' when Version >= ?WSP_13 ->
+ decode_encoding_version(Value,Version);
+ 'Profile-Warning' when Version >= ?WSP_14 ->
+ decode_profile_warning(Value,Version);
+
+ 'X-Wap-Security' when Version >= ?WSP_14 ->
+ decode_x_wap_security(Value,Version);
+ 'X-Wap-Loc-Invocation' when Version >= ?WSP_15 ->
+ decode_x_wap_loc_invocation(Value,Version); %% ???
+ 'X-Wap-Loc-Delivery' when Version >= ?WSP_15 ->
+ decode_x_wap_loc_delivery(Value,Version); %% ???
+ _ ->
+ ?dbg("Warning: none standard field ~p in version ~p codepage=1\n",
+ [Field, Version]),
+ ?WH(Field, Value, [])
+ end;
+decode_header(Field, Value, _Version, _CP) ->
+ ?dbg("Warning: none standard field ~p in version ~p codepage=~w\n",
+ [Field, _Version, _CP]),
+ ?WH(Field, Value, []).
+
+%%
+%% Encode field and value according to version
+%% FIXME: spilt multiple header values (i.e Via) into multiple
+%% headers
+%%
+encode_header(H, Version) ->
+ case H#wsp_header.name of
+ 'Accept' ->
+ [16#80, encode_accept(H, Version)];
+ 'Accept-Charset' when Version >= ?WSP_13 ->
+ [16#bb, encode_accept_charset(H, Version)];
+ 'Accept-Charset' ->
+ [16#81, encode_accept_charset(H, Version)];
+ 'Accept-Encoding' when Version >= ?WSP_13 ->
+ [16#bc, encode_accept_encoding(H, Version)];
+ 'Accept-Encoding' ->
+ [16#82, encode_accept_encoding(H, Version)];
+ 'Accept-Language' ->
+ [16#83, encode_accept_language(H, Version)];
+ 'Accept-Ranges' ->
+ [16#84, encode_accept_ranges(H, Version)];
+ 'Accept-Application' when Version >= ?WSP_12 ->
+ [16#b2, encode_accept_application(H,Version)];
+ 'Age' ->
+ [16#85, encode_age(H, Version)];
+ 'Allow' ->
+ [16#86, encode_allow(H, Version)];
+ 'Authorization' ->
+ [16#87, encode_authorization(H, Version)];
+ 'Cache-Control' when Version >= ?WSP_14 ->
+ [16#c7, encode_cache_control(H, Version)];
+ 'Cache-Control' when Version >= ?WSP_13 ->
+ [16#bd, encode_cache_control(H, Version)];
+ 'Cache-Control' ->
+ [16#88, encode_cache_control(H, Version)];
+ 'Connection' ->
+ [16#89, encode_connection(H, Version)];
+ 'Content-Base' ->
+ [16#8a, encode_content_base(H, Version)];
+ 'Content-Encoding' ->
+ [16#8b, encode_content_encoding(H, Version)];
+
+ 'Content-Language' ->
+ [16#8c, encode_content_language(H,Version)];
+ 'Content-Length' ->
+ [16#8d, encode_content_length(H,Version)];
+ 'Content-Location' ->
+ [16#8e, encode_content_location(H,Version)];
+ 'Content-Md5' ->
+ [16#8f, encode_content_md5(H,Version)];
+ 'Content-Range' when Version >= ?WSP_13 ->
+ [16#be, encode_content_range(H,Version)];
+ 'Content-Range' ->
+ [16#90, encode_content_range(H,Version)];
+ 'Content-Type' ->
+ [16#91, encode_content_type(H,Version)];
+ 'Date' ->
+ [16#92, encode_date(H,Version)];
+ 'Etag' ->
+ [16#93, encode_etag(H,Version)];
+ 'Expires' ->
+ [16#94, encode_expires(H,Version)];
+ 'From' ->
+ [16#95, encode_from(H,Version)];
+ 'Host' ->
+ [16#96, encode_host(H,Version)];
+ 'If-Modified-Since' ->
+ [16#97, encode_if_modified_since(H,Version)];
+ 'If-Match' ->
+ [16#98, encode_if_match(H,Version)];
+ 'If-None-Match' ->
+ [16#99, encode_if_none_match(H,Version)];
+ 'If-Range' ->
+ [16#9a, encode_if_range(H,Version)];
+ 'If-Unmodified-Since' ->
+ [16#9b, encode_if_unmodified_since(H,Version)];
+ 'Location' ->
+ [16#9c, encode_location(H,Version)];
+ 'Last-Modified' ->
+ [16#9d, encode_last_modified(H,Version)];
+ 'Max-Forwards' ->
+ [16#9e, encode_max_forwards(H,Version)];
+ 'Pragma' ->
+ [16#9f, encode_pragma(H,Version)];
+ 'Proxy-Authenticate' ->
+ [16#a0, encode_proxy_authenticate(H,Version)];
+ 'Proxy-Authorization' ->
+ [16#a1, encode_proxy_authorization(H,Version)];
+ 'Public' ->
+ [16#a2, encode_public(H,Version)];
+ 'Range' ->
+ [16#a3, encode_range(H,Version)];
+ 'Referer' ->
+ [16#a4, encode_referer(H,Version)];
+ 'Retry-After' ->
+ [16#a5, encode_retry_after(H,Version)];
+ 'Server' ->
+ [16#a6, encode_server(H,Version)];
+ 'Transfer-Encoding' ->
+ [16#a7, encode_transfer_encoding(H,Version)];
+ 'Upgrade' ->
+ [16#a8, encode_upgrade(H,Version)];
+ 'User-Agent' ->
+ [16#a9, encode_user_agent(H,Version)];
+ 'Vary' ->
+ [16#aa, encode_vary(H,Version)];
+ 'Via' ->
+ [16#ab, encode_via(H,Version)];
+ 'Warning' ->
+ [16#ac, encode_warning(H,Version)];
+ 'Www-Authenticate' ->
+ [16#ad, encode_www_authenticate(H,Version)];
+
+ 'Content-Disposition' when Version >= ?WSP_14 ->
+ [16#c5, encode_content_disposition(H,Version)];
+ 'Content-Disposition' ->
+ [16#ae, encode_content_disposition(H,Version)];
+
+
+ 'X-Wap-Application-Id' when Version >= ?WSP_12 ->
+ [16#af, encode_x_wap_application_id(H,Version)];
+ 'X-Wap-Content-Uri' when Version >= ?WSP_12 ->
+ [16#b0, encode_x_wap_content_uri(H,Version)];
+ 'X-Wap-Initiator-Uri' when Version >= ?WSP_12 ->
+ [16#b1, encode_x_wap_initiator_uri(H,Version)];
+
+ 'Bearer-Indication' when Version >= ?WSP_12 ->
+ [16#b3, encode_bearer_indication(H,Version)];
+ 'Push-Flag' when Version >= ?WSP_12 ->
+ [16#b4, encode_push_flag(H,Version)];
+
+ 'Profile' when Version >= ?WSP_12 ->
+ [16#b5, encode_profile(H,Version)];
+ 'Profile-Diff' when Version >= ?WSP_12 ->
+ [16#b6, encode_profile_diff(H,Version)];
+ 'Profile-Warning' when Version >= ?WSP_14 ->
+ [16#c4, encode_profile_warning(H,Version)];
+ 'Profile-Warning' when Version >= ?WSP_12 ->
+ [16#b7, encode_profile_warning(H,Version)];
+
+ 'Expect' when Version >= ?WSP_15 ->
+ [16#c8, encode_expect(H,Version)];
+ 'Expect' when Version >= ?WSP_13 ->
+ [16#b8, encode_expect(H,Version)];
+ 'Te' when Version >= ?WSP_13 ->
+ [16#b9, encode_te(H,Version)];
+ 'Trailer' when Version >= ?WSP_13 ->
+ [16#ba, encode_trailer(H,Version)];
+ 'X-Wap-Tod' when Version >= ?WSP_13 ->
+ [16#bf, encode_x_wap_tod(H,Version)];
+ 'Content-Id' when Version >= ?WSP_13 ->
+ [16#c0, encode_content_id(H,Version)];
+ 'Set-Cookie' when Version >= ?WSP_13 ->
+ [16#c1, encode_set_cookie(H,Version)];
+ 'Cookie' when Version >= ?WSP_13 ->
+ [16#c2, encode_cookie(H,Version)];
+ 'Encoding-Version' when Version >= ?WSP_13 ->
+ [16#c3, encode_encoding_version(H,Version)];
+ 'Encoding-Version' when Version < ?WSP_13 ->
+ [encode_text_string("Encoding-Version"),
+ encode_text_string(lists:flatten(format_version(H#wsp_header.value)))];
+
+ 'X-Wap-Security' when Version >= ?WSP_14 ->
+ [16#c6, encode_x_wap_security(H,Version)];
+ 'X-Wap-Loc-Invocation' when Version >= ?WSP_15 ->
+ [16#c9, encode_x_wap_loc_invocation(H,Version)];
+ 'X-Wap-Loc-Delivery' when Version >= ?WSP_15 ->
+ [16#ca, encode_x_wap_loc_delivery(H,Version)];
+ Field when atom(Field) ->
+ [encode_text_string(atom_to_list(Field)),
+ encode_text_string(H#wsp_header.value)];
+ Field when list(Field) ->
+ [encode_text_string(Field),
+ encode_text_string(H#wsp_header.value)]
+ end.
+
+%%
+%% Convert HTTP headers into WSP headers
+%%
+parse_headers([H | Hs]) ->
+ parse_header(H, Hs);
+parse_headers([]) ->
+ [].
+
+parse_header(H) ->
+ parse_header(H, []).
+
+parse_header({FieldName,FieldValue}, Hs) ->
+ case single_comma_field(FieldName) of
+ true ->
+ io:format("parse: ~s: ~s\n", [FieldName, FieldValue]),
+ H = parse_hdr(FieldName,FieldValue),
+ io:format("header: ~p\n", [H]),
+ [H | parse_headers(Hs)];
+ false ->
+ Values = string:tokens(FieldValue, ","),
+ parse_header(FieldName, Values, Hs)
+ end.
+
+parse_header(FieldName, [Value|Vs], Hs) ->
+ io:format("parse: ~s: ~s\n", [FieldName, Value]),
+ H = parse_hdr(FieldName, Value),
+ io:format("header: ~p\n", [H]),
+ [H | parse_header(FieldName, Vs, Hs)];
+parse_header(_FieldName, [], Hs) ->
+ parse_headers(Hs).
+
+
+single_comma_field(Field) ->
+ case Field of
+ 'Set-Cookie' -> true; %% FIXME (Is multiple!)
+ 'Date' -> true;
+ 'Expires' -> true;
+ 'If-Modified-Since' -> true;
+ 'If-Range' -> true;
+ 'If-Unmodified-Since' -> true;
+ 'Last-Modified' -> true;
+ 'Retry-After' -> true;
+ 'X-Wap-Tod' -> true;
+ _ -> false
+ end.
+
+
+parse_hdr(Field, Value0) ->
+ Value = trim(Value0),
+ case Field of
+ 'Accept' -> parse_accept(Value);
+ 'Accept-Charset' -> parse_accept_charset(Value);
+ 'Accept-Encoding' -> parse_accept_encoding(Value);
+ 'Accept-Language' -> parse_accept_language(Value);
+ 'Accept-Ranges' -> parse_accept_ranges(Value);
+ 'Age' -> parse_age(Value);
+ 'Allow' -> parse_allow(Value);
+ 'Authorization' -> parse_authorization(Value);
+ 'Cache-Control' -> parse_cache_control(Value);
+ 'Connection' -> parse_connection(Value);
+ 'Content-Base' -> parse_content_base(Value);
+ 'Content-Encoding' -> parse_content_encoding(Value);
+ 'Content-Language' -> parse_content_language(Value);
+ 'Content-Length' -> parse_content_length(Value);
+ 'Content-Location' -> parse_content_location(Value);
+ 'Content-Md5' -> parse_content_md5(Value);
+ 'Content-Range' -> parse_content_range(Value);
+ 'Content-Type' -> parse_content_type(Value);
+ 'Date' -> parse_date(Value);
+ 'Etag' -> parse_etag(Value);
+ 'Expires' -> parse_expires(Value);
+ 'From' -> parse_from(Value);
+ 'Host' -> parse_host(Value);
+ 'If-Modified-Since' -> parse_if_modified_since(Value);
+ 'If-Match' -> parse_if_match(Value);
+ 'If-None-Match' -> parse_if_none_match(Value);
+ 'If-Range' -> parse_if_range(Value);
+ 'If-Unmodified-Since' -> parse_if_unmodified_since(Value);
+ 'Location' -> parse_location(Value);
+ 'Last-Modified' -> parse_last_modified(Value);
+ 'Max-Forwards' -> parse_max_forwards(Value);
+ 'Pragma' -> parse_pragma(Value);
+ 'Proxy-Authenticate' -> parse_proxy_authenticate(Value);
+ 'Proxy-Authorization' -> parse_proxy_authorization(Value);
+ 'Public' -> parse_public(Value);
+ 'Range' -> parse_range(Value);
+ 'Referer' -> parse_referer(Value);
+ 'Retry-After' -> parse_retry_after(Value);
+ 'Server' -> parse_server(Value);
+ 'Transfer-Encoding' -> parse_transfer_encoding(Value);
+ 'Upgrade' -> parse_upgrade(Value);
+ 'User-Agent' -> parse_user_agent(Value);
+ 'Vary' -> parse_vary(Value);
+ 'Via' -> parse_via(Value);
+ 'Warning' -> parse_warning(Value);
+ 'Www-Authenticate' -> parse_www_authenticate(Value);
+ 'Content-Disposition' -> parse_content_disposition(Value);
+ 'X-Wap-Application-Id' -> parse_x_wap_application_id(Value);
+ 'X-Wap-Content-Uri' -> parse_x_wap_content_uri(Value);
+ 'X-Wap-Initiator-Uri' -> parse_x_wap_initiator_uri(Value);
+ 'Accept-Application' -> parse_accept_application(Value);
+ 'Bearer-Indication' -> parse_bearer_indication(Value);
+ 'Push-Flag' -> parse_push_flag(Value);
+ 'Profile' -> parse_profile(Value);
+ 'Profile-Diff' -> parse_profile_diff(Value);
+ 'Profile-Warning' -> parse_profile_warning(Value);
+ 'Expect' -> parse_expect(Value);
+ 'Te' -> parse_te(Value);
+ 'Trailer' -> parse_trailer(Value);
+ 'X-Wap-Tod' -> parse_x_wap_tod(Value);
+ 'Content-Id' -> parse_content_id(Value);
+ 'Set-Cookie' -> parse_set_cookie(Value);
+ 'Cookie' -> parse_cookie(Value);
+ 'Encoding-Version' -> parse_encoding_version(Value);
+ 'X-Wap-Security' -> parse_x_wap_security(Value);
+ 'X-Wap-Loc-Invocation' -> parse_x_wap_loc_invocation(Value);
+ 'X-Wap-Loc-Delivery' -> parse_x_wap_loc_delivery(Value);
+ _ ->
+ ?dbg("Warning: header field ~p not recognissed\n",[Field]),
+ #wsp_header { name = Field, value = Value}
+ end.
+
+%%
+%% Format headers, will combine multiple headers into one
+%% FIXME: if length is < MAX_HTTP_HEADER_LENGTH
+%%
+format_headers(Hs) ->
+ format_hdrs(lists:keysort(#wsp_header.name,Hs), []).
+
+format_hdrs([H | Hs], Acc) ->
+ V1 = format_value(H),
+ format_hdrs(Hs, H#wsp_header.name, V1, Acc);
+format_hdrs([], Acc) ->
+ lists:reverse(Acc).
+
+format_hdrs([H|Hs], FieldName, FieldValue, Acc)
+ when FieldName == H#wsp_header.name ->
+ V1 = format_value(H),
+ format_hdrs(Hs, FieldName, [FieldValue,",",V1], Acc);
+format_hdrs(Hs, FieldName, FieldValue, Acc) ->
+ format_hdrs(Hs, [{FieldName, lists:flatten(FieldValue)} | Acc]).
+
+
+%%
+%% Format header: #wsp_header => {FieldName, Value}
+%%
+
+format_header(H) ->
+ {H#wsp_header.name, format_value(H)}.
+
+format_value(H) ->
+ case H#wsp_header.name of
+ 'Accept' -> format_accept(H);
+ 'Accept-Charset' -> format_accept_charset(H);
+ 'Accept-Encoding' -> format_accept_encoding(H);
+ 'Accept-Language' -> format_accept_language(H);
+ 'Accept-Ranges' -> format_accept_ranges(H);
+ 'Age' -> format_age(H);
+ 'Allow' -> format_allow(H);
+ 'Authorization' -> format_authorization(H);
+ 'Cache-Control' -> format_cache_control(H);
+ 'Connection' -> format_connection(H);
+ 'Content-Base' -> format_content_base(H);
+ 'Content-Encoding' -> format_content_encoding(H);
+ 'Content-Language' -> format_content_language(H);
+ 'Content-Length' -> format_content_length(H);
+ 'Content-Location' -> format_content_location(H);
+ 'Content-Md5' -> format_content_md5(H);
+ 'Content-Range' -> format_content_range(H);
+ 'Content-Type' -> format_content_type(H);
+ 'Date' -> format_date(H);
+ 'Etag' -> format_etag(H);
+ 'Expires' -> format_expires(H);
+ 'From' -> format_from(H);
+ 'Host' -> format_host(H);
+ 'If-Modified-Since' -> format_if_modified_since(H);
+ 'If-Match' -> format_if_match(H);
+ 'If-None-Match' -> format_if_none_match(H);
+ 'If-Range' -> format_if_range(H);
+ 'If-Unmodified-Since' -> format_if_unmodified_since(H);
+ 'Location' -> format_location(H);
+ 'Last-Modified' -> format_last_modified(H);
+ 'Max-Forwards' -> format_max_forwards(H);
+ 'Pragma' -> format_pragma(H);
+ 'Proxy-Authenticate' -> format_proxy_authenticate(H);
+ 'Proxy-Authorization' -> format_proxy_authorization(H);
+ 'Public' -> format_public(H);
+ 'Range' -> format_range(H);
+ 'Referer' -> format_referer(H);
+ 'Retry-After' -> format_retry_after(H);
+ 'Server' -> format_server(H);
+ 'Transfer-Encoding' -> format_transfer_encoding(H);
+ 'Upgrade' -> format_upgrade(H);
+ 'User-Agent' -> format_user_agent(H);
+ 'Vary' -> format_vary(H);
+ 'Via' -> format_via(H);
+ 'Warning' -> format_warning(H);
+ 'Www-Authenticate' -> format_www_authenticate(H);
+ 'Content-Disposition' -> format_content_disposition(H);
+ 'X-Wap-Application-Id' -> format_x_wap_application_id(H);
+ 'X-Wap-Content-Uri' -> format_x_wap_content_uri(H);
+ 'X-Wap-Initiator-Uri' -> format_x_wap_initiator_uri(H);
+ 'Accept-Application' -> format_accept_application(H);
+ 'Bearer-Indication' -> format_bearer_indication(H);
+ 'Push-Flag' -> format_push_flag(H);
+ 'Profile' -> format_profile(H);
+ 'Profile-Diff' -> format_profile_diff(H);
+ 'Profile-Warning' -> format_profile_warning(H);
+ 'Expect' -> format_expect(H);
+ 'Te' -> format_te(H);
+ 'Trailer' -> format_trailer(H);
+ 'X-Wap-Tod' -> format_x_wap_tod(H);
+ 'Content-Id' -> format_content_id(H);
+ 'Set-Cookie' -> format_set_cookie(H);
+ 'Cookie' -> format_cookie(H);
+ 'Encoding-Version' -> format_encoding_version(H);
+ 'X-Wap-Security' -> format_x_wap_security(H);
+ 'X-Wap-Loc-Invocation' -> format_x_wap_loc_invocation(H);
+ 'X-Wap-Loc-Delivery' -> format_x_wap_loc_delivery(H);
+ _Field ->
+ ?dbg("Warning: header field ~s not recognissed\n",[_Field]),
+ to_list(H#wsp_header.value)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Encode of field values
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Accept: <content-type> [q=<q-value>] [params]
+%% Type: Multiple
+%% Ref: 8.4.2.7
+%%
+%% Accept-value = Constrained-media | Accept-general-form
+%%
+%% Accept-general-form = Value-length Media-range [Accept-parameters]
+%% Media-range = (Well-known-media | Extension-media) *(Parameter)
+%% Accept-parameters = Q-token Q-value *(Accept-extension)
+%% Accept-extension = Parameter
+%% Constrain-media = Constrained-encoding
+%% Well-known-media = Integer-value
+%% Constrained-encoding = Short-Integer | Extension-media
+%% Q-token = <Octet 128>
+%%
+parse_accept(String) ->
+ %% FIXME
+ ?WH('Accept',String,[]).
+
+format_accept(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_accept(H, Version) ->
+ case encode_params(H#wsp_header.params,Version) of
+ <<>> ->
+ encode_well_known_media(H#wsp_header.value, Version);
+ Params ->
+ Media = encode_well_known_media(H#wsp_header.value, Version),
+ e_value(Media, Params)
+ end.
+
+decode_accept(Value, Version) when integer(Value) ->
+ %% Constrained-encoding: Short-Integer
+ ?WH('Accept',decode_well_known_media(Value, Version),[]);
+decode_accept(Value, Version) when list(Value) ->
+ ?WH('Accept',decode_well_known_media(Value,Version),[]);
+decode_accept({_,Data}, Version) ->
+ %% Accept-general-form
+ {Value,QData} = scan_header_data(Data),
+ Media_Range = decode_well_known_media(Value,Version),
+ Params = decode_params(QData, Version),
+ ?WH('Accept',Media_Range,Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Accept-Charset: <charset> | * [q=<q-value>]
+%% Type: Multiple
+%% Ref: 8.4.2.8
+%% Note that the definition of this one is a mess!!!!
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_accept_charset(String) ->
+ %% FIXME
+ ?WH('Accept-Charset',String,[]).
+
+format_accept_charset(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_accept_charset(H, _Version) ->
+ %% FIXME
+ encode_text_string(H#wsp_header.value).
+
+decode_accept_charset(0, _Version) ->
+ ?WH('Accept-Charset',"*",[]);
+decode_accept_charset(Value, _Version) when integer(Value) ->
+ ?WH('Accept-Charset', decode_charset(Value),[]);
+decode_accept_charset(Value, _Version) when list(Value) ->
+ ?WH('Accept-Charset',Value,[]);
+decode_accept_charset({short,Data}, _Version) ->
+ %% Me guessing that the short form SHOULD be mulit octet integer!!!
+ Value = d_long(Data),
+ ?WH('Accept-Charset', decode_charset(Value),[]);
+decode_accept_charset({long,Value}, _Version) ->
+ {Data1, QData} = scan_header_data(Value),
+ CharSet = case Data1 of
+ 0 ->
+ "*";
+ Value1 when integer(Value1) ->
+ decode_charset(Value1);
+ Value1 when list(Value1) ->
+ Value1;
+ {short,Value1} ->
+ Value2 = d_long(Value1),
+ decode_charset(Value2)
+ end,
+ Params = if QData == <<>> ->
+ [];
+ true ->
+ {QValue,_} = d_q_value(QData),
+ {CharSet,[{q, QValue}]}
+ end,
+ ?WH('Accept-Charset',CharSet, Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Accept-Encoding: gzip | compress | deflate | * [q=<q-value>]
+%% Ref:
+%% Type: Multiple
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_accept_encoding(String) ->
+ ?WH('Accept-Encoding',String,[]).
+
+format_accept_encoding(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_accept_encoding(H, _Version) ->
+ %% FIXME general form
+ case H#wsp_header.value of
+ "gzip" -> ?ENCODE_SHORT(0);
+ "compress" -> ?ENCODE_SHORT(1);
+ "deflate" -> ?ENCODE_SHORT(2);
+ Value -> encode_text_string(Value)
+ end.
+
+decode_accept_encoding(0, _Version) ->
+ ?WH('Accept-Encoding',"gzip",[]);
+decode_accept_encoding(1, _Version) ->
+ ?WH('Accept-Encoding',"compress",[]);
+decode_accept_encoding(2, _Version) ->
+ ?WH('Accept-Encoding',"deflate",[]);
+decode_accept_encoding(Value, Version) when list(Version) ->
+ ?WH('Accept-Encoding',Value,[]);
+decode_accept_encoding({_,Data}, _Version) when binary(Data) ->
+ {Enc, Data1} = scan_header_data(Data),
+ Params = if Data1 == <<>> ->
+ [];
+ true ->
+ {QVal,_} = d_q_value(Data1),
+ [{q, QVal}]
+ end,
+ case Enc of
+ 0 -> ?WH('Accept-Encoding',"gzip",Params);
+ 1 -> ?WH('Accept-Encoding',"compress",Params);
+ 2 -> ?WH('Accept-Encoding',"deflate",Params);
+ 3 -> ?WH('Accept-Encoding',"*",Params);
+ _ when list(Enc) ->
+ ?WH('Accept-Encoding',Enc,Params)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%
+%% Accept-Language: * | <lang> [q=<q-value>]
+%% Type: Multiple
+%% Ref: 8.4.2.10
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_accept_language(Value) ->
+ ?WH('Accept-Language',Value,[]).
+
+format_accept_language(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_accept_language(H, _Version) ->
+ case H#wsp_header.value of
+ "*" -> ?ENCODE_SHORT(0);
+ Lang -> case catch encode_lang(Lang) of
+ {'EXIT', _} -> encode_text_string(Lang);
+ Code -> encode_integer(Code)
+ end
+ end.
+
+decode_accept_language(0, _Version) ->
+ ?WH('Accept-Language',"*",[]);
+decode_accept_language(Value, _Version) when integer(Value) ->
+ ?WH('Accept-Language',decode_lang(Value),[]);
+decode_accept_language(Value, _Version) when list(Value) ->
+ ?WH('Accept-Language',Value,[]);
+decode_accept_language({_,Data}, _Version) ->
+ {Data1, QData} = scan_header_data(Data),
+ Charset = case Data1 of
+ 0 ->
+ "*";
+ Value1 when integer(Value1) ->
+ decode_lang(Value1);
+ Value1 when list(Value1) ->
+ Value1;
+ {short,Data2} ->
+ decode_lang(d_long(Data2))
+ end,
+ Params =
+ if QData == <<>> ->
+ [];
+ true ->
+ {QVal,_} = d_q_value(QData),
+ [{q, QVal}]
+ end,
+ ?WH('Accept-Language',Charset,Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Accept-Ranges: none | bytes | <extension>
+%% Type: single
+%% Ref:
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_accept_ranges(Value) ->
+ ?WH('Accept-Ranges', Value, []).
+
+format_accept_ranges(H) ->
+ H#wsp_header.value.
+
+encode_accept_ranges(H, _Version) ->
+ case H#wsp_header.value of
+ "none" -> ?ENCODE_SHORT(0);
+ "bytes" -> ?ENCODE_SHORT(1);
+ Value -> encode_text_string(Value)
+ end.
+
+decode_accept_ranges(0, _Version) ->
+ ?WH('Accept-Ranges', "none", []);
+decode_accept_ranges(1, _Version) ->
+ ?WH('Accept-Ranges', "bytes", []);
+decode_accept_ranges(Value, _Version) when list(Value) ->
+ ?WH('Accept-Ranges', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Age: <delta-seconds>
+%% Type: single
+%% Ref:
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_age(Value) ->
+ %% FIXME
+ ?WH('Age', Value, []).
+
+format_age(H) ->
+ integer_to_list(H#wsp_header.value).
+
+encode_age(H, _Version) ->
+ e_delta_seconds(H#wsp_header.value).
+
+decode_age(Value, _Version) when integer(Value) ->
+ ?WH('Age', Value, []);
+decode_age({short,Data}, _Version) ->
+ ?WH('Age', d_long(Data), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Allow: <well-known-method>
+%% Type: multiple
+%% Ref:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_allow(Value) ->
+ ?WH('Allow', parse_well_known_method(Value), []).
+
+format_allow(H) ->
+ atom_to_list(H#wsp_header.value).
+
+encode_allow(H, Version) ->
+ encode_well_known_method(H#wsp_header.value, Version).
+
+decode_allow(Value, Version) ->
+ ?WH('Allow', decode_well_known_method(Value,Version), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Authorization:
+%% Ref: 8.4.2.14
+%% Type: server-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_authorization(Value) ->
+ parse_credentials('Authorization', Value).
+
+format_authorization(H) ->
+ format_credentials(H#wsp_header.value, H#wsp_header.params).
+
+encode_authorization(H, Version) ->
+ encode_credentials(H#wsp_header.value, H#wsp_header.params, Version).
+
+decode_authorization({_,Data}, Version) ->
+ decode_credentials('Authorization', Data, Version).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%%
+%% Cache-Control:
+%% 8.4.2.15
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_cache_control(Value) ->
+ case Value of
+ "no-cache" -> ?WH('Cache-Control',Value,[]);
+ "no-store" -> ?WH('Cache-Control',Value,[]);
+ "max-stale" -> ?WH('Cache-Control',Value,[]);
+ "only-if-cached" -> ?WH('Cache-Control',Value,[]);
+ "private" -> ?WH('Cache-Control',Value,[]);
+ "public" -> ?WH('Cache-Control',Value,[]);
+ "no-transform" -> ?WH('Cache-Control',Value,[]);
+ "must-revalidate" -> ?WH('Cache-Control',Value,[]);
+ "proxy-revalidate" -> ?WH('Cache-Control',Value,[]);
+ _ ->
+ Params = parse_params([Value]),
+ ?WH('Cache-Control',"",Params)
+ end.
+
+format_cache_control(H) ->
+ if H#wsp_header.value == "" ->
+ format_params0(H#wsp_header.params);
+ true ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)]
+ end.
+
+
+
+encode_cache_control(H, Version) ->
+ case H#wsp_header.value of
+ "no-cache" -> ?ENCODE_SHORT(0);
+ "no-store" -> ?ENCODE_SHORT(1);
+ "max-stale" -> ?ENCODE_SHORT(3);
+ "only-if-cached" -> ?ENCODE_SHORT(5);
+ "private" -> ?ENCODE_SHORT(7);
+ "public" -> ?ENCODE_SHORT(6);
+ "no-transform" -> ?ENCODE_SHORT(8);
+ "must-revalidate" -> ?ENCODE_SHORT(9);
+ "proxy-revalidate" -> ?ENCODE_SHORT(10);
+ "" ->
+ case H#wsp_header.params of
+ [{'no-cache',Field}] ->
+ e_value(?ENCODE_SHORT(0),
+ e_field_name(Field,Version));
+ [{'max-age',Sec}] ->
+ e_value(?ENCODE_SHORT(2),
+ e_delta_seconds(Sec));
+ [{'max-fresh',Sec}] ->
+ e_value(?ENCODE_SHORT(4),
+ e_delta_seconds(Sec));
+ [{'private',Field}] ->
+ e_value(?ENCODE_SHORT(7),
+ e_field_name(Field,Version));
+ [{'s-maxage',Sec}] ->
+ e_value(?ENCODE_SHORT(11),
+ e_delta_seconds(Sec))
+ end;
+ Ext ->
+ [Param] = H#wsp_header.params,
+ e_value(encode_text_string(Ext),
+ encode_parameter(Param, Version))
+ end.
+
+
+decode_cache_control(Value, _Version) when integer(Value) ->
+ case Value of
+ 0 -> ?WH('Cache-Control',"no-cache",[]);
+ 1 -> ?WH('Cache-Control',"no-store",[]);
+ 3 -> ?WH('Cache-Control',"max-stale",[]);
+ 5 -> ?WH('Cache-Control',"only-if-cached",[]);
+ 7 -> ?WH('Cache-Control',"private",[]);
+ 6 -> ?WH('Cache-Control',"public",[]);
+ 8 -> ?WH('Cache-Control',"no-transform",[]);
+ 9 -> ?WH('Cache-Control',"must-revalidate",[]);
+ 10 -> ?WH('Cache-Control',"proxy-revalidate",[])
+ end;
+decode_cache_control(Value, _Version) when list(Value) ->
+ ?WH('Cache-Control',Value,[]);
+decode_cache_control({_,Data},Version) ->
+ {CacheDir, Data1} = scan_header_data(Data),
+ case CacheDir of
+ 0 ->
+ {Field,_} = d_field_name(Data1),
+ ?WH('Cache-Control',"",[{'no-cache',Field}]);
+ 2 ->
+ {Sec,_} = d_integer_value(Data1),
+ ?WH('Cache-Control',"",[{'max-age',Sec}]);
+ 4 ->
+ {Sec,_} = d_integer_value(Data1),
+ ?WH('Cache-Control',"",[{'max-fresh',Sec}]);
+ 7 ->
+ {Field,_} = d_field_name(Data1),
+ ?WH('Cache-Control',"",[{private,Field}]);
+ 11 ->
+ {Sec,_} = d_integer_value(Data1),
+ ?WH('Cache-Control',"",[{'s-maxage',Sec}]);
+ Ext when list(Ext) ->
+ {Param,_} = decode_parameter(Data1, Version),
+ ?WH('Cache-Control',Ext,[Param])
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Connection: close | Ext
+%% Type: single
+%% Ref: 8.4.2.16
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_connection(Value) ->
+ ?WH('Connection', Value, []).
+
+format_connection(H) ->
+ H#wsp_header.value.
+
+encode_connection(H, _Version) ->
+ case H#wsp_header.value of
+ "close" -> ?ENCODE_SHORT(0);
+ Value -> encode_text_string(Value)
+ end.
+
+decode_connection(0, _Version) ->
+ ?WH('Connection', "close", []);
+decode_connection(Value, _Version) when list(Value) ->
+ ?WH('Connection', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Base: <uri>
+%% Type: single
+%% Ref:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_content_base(Value) ->
+ ?WH('Content-Base', Value, []).
+
+format_content_base(H) ->
+ H#wsp_header.value.
+
+encode_content_base(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_content_base(Value, _Version) when list(Value) ->
+ ?WH('Content-Base', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Encoding:
+%% Ref: 8.4.2.18
+%% Type: single
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_content_encoding(Value) ->
+ ?WH('Content-Encoding', tolower(Value), []).
+
+format_content_encoding(H) ->
+ H#wsp_header.value.
+
+encode_content_encoding(H, _Version) ->
+ case H#wsp_header.value of
+ "gzip" -> ?ENCODE_SHORT(0);
+ "compress" -> ?ENCODE_SHORT(1);
+ "deflate" -> ?ENCODE_SHORT(2);
+ Value -> encode_text_string(Value)
+ end.
+
+decode_content_encoding(0, _Version) ->
+ ?WH('Content-Encoding', "gzip", []);
+decode_content_encoding(1, _Version) ->
+ ?WH('Content-Encoding', "compress", []);
+decode_content_encoding(2, _Version) ->
+ ?WH('Content-Encoding',"deflate", []);
+decode_content_encoding(Value, _Version) when list(Value) ->
+ ?WH('Content-Encoding', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Language:
+%% Ref: 8.4.2.19
+%% Type: single
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_content_language(Value) ->
+ ?WH('Content-Language', Value, []).
+
+format_content_language(H) ->
+ H#wsp_header.value.
+
+encode_content_language(H, _Version) ->
+ case H#wsp_header.value of
+ "*" -> ?ENCODE_SHORT(0);
+ Lang -> case catch encode_lang(Lang) of
+ {'EXIT', _} -> encode_text_string(Lang);
+ Code -> encode_integer(Code)
+ end
+ end.
+
+decode_content_language(0, _Version) ->
+ ?WH('Content-Language',"*",[]);
+decode_content_language(Value, _Version) when integer(Value) ->
+ ?WH('Content-Language',decode_lang(Value),[]);
+decode_content_language(Value, _Version) when list(Value) ->
+ ?WH('Content-Language',Value,[]);
+decode_content_language({short,Data}, _Version) ->
+ Value = d_long(Data),
+ ?WH('Content-Language',decode_lang(Value),[]);
+decode_content_language(Value, _Version) when list(Value) ->
+ ?WH('Content-Language',Value,[]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Length: <integer-value>
+%% Ref: 8.4.2.20
+%% Type: single
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_content_length(Value) ->
+ ?WH('Content-Length', list_to_integer(Value), []).
+
+format_content_length(H) ->
+ integer_to_list(H#wsp_header.value).
+
+encode_content_length(H, _Version) ->
+ encode_integer(H#wsp_header.value).
+
+decode_content_length(Value, _Version) when integer(Value) ->
+ ?WH('Content-Length', Value, []);
+decode_content_length({short,Data}, _Version) ->
+ Value = d_long(Data),
+ ?WH('Content-Length', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Location: <uri-value>
+%% Ref: 8.4.2.21
+%% Type: single
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_content_location(Value) ->
+ ?WH('Content-Location', Value, []).
+
+format_content_location(H) ->
+ H#wsp_header.value.
+
+encode_content_location(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_content_location(Value, _Version) when list(Value) ->
+ ?WH('Content-Location', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Md5: <value-length> <digest>
+%% Ref: 8.4.2.22
+%% Type: single, end-to-end
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_content_md5(Value) ->
+ ?WH('Content-Md5', base64:decode(Value), []).
+
+format_content_md5(H) ->
+ base64:encode(H#wsp_header.value).
+
+encode_content_md5(H, _Version) ->
+ e_value(H#wsp_header.value).
+
+decode_content_md5({_,Data}, _Version) ->
+ ?WH('Content-Md5', Data, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Range: <first-byte-pos> <entity-len>
+%% Ref: 8.4.2.23
+%% Type: single
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_content_range(Value) ->
+ %% FIXME:
+ ?WH('Content-Range', Value, []).
+
+format_content_range(H) ->
+ {Pos,Len} = H#wsp_header.value,
+ if Len == "*" ->
+ ["bytes ", integer_to_list(Pos), "-*/*"];
+ true ->
+ ["bytes ", integer_to_list(Pos),"-",integer_to_list(Len-1),
+ "/", integer_to_list(Len)]
+ end.
+
+encode_content_range(H, _Version) ->
+ case H#wsp_header.value of
+ {Pos, "*"} ->
+ e_value(e_uintvar(Pos), <<128>>);
+ {Pos, Len} ->
+ e_value(e_uintvar(Pos), e_uintvar(Len))
+ end.
+
+decode_content_range({_, Data}, _Version) ->
+ {Pos, Data1} = d_uintvar(Data),
+ Len =
+ case Data1 of
+ <<128>> -> "*";
+ _ ->
+ {L, _} = d_uintvar(Data1),
+ L
+ end,
+ ?WH('Content-Range', {Pos,Len}, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Type:
+%% Ref: 8.4.2.24
+%% Type: single, end-to-end
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_content_type(Value) ->
+ case string:tokens(Value, ";") of
+ [Type | Ps] ->
+ Params = parse_params(Ps),
+ ?WH('Content-Type', Type, Params);
+ [] ->
+ ?WH('Content-Type', Value, [])
+ end.
+
+format_content_type(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_content_type(H, Version) ->
+ case encode_params(H#wsp_header.params,Version) of
+ <<>> ->
+ encode_well_known_media(H#wsp_header.value, Version);
+ Params ->
+ Media = encode_well_known_media(H#wsp_header.value, Version),
+ e_value(Media, Params)
+ end.
+
+decode_content_type(Value,Version) when integer(Value) ->
+ ?WH('Content-Type', decode_well_known_media(Value,Version), []);
+decode_content_type(Value,Version) when list(Value) ->
+ ?WH('Content-Type', decode_well_known_media(Value,Version), []);
+decode_content_type({_, Data}, Version) ->
+ {Value,Data1} = scan_header_data(Data),
+ ContentType = if integer(Value) ->
+ decode_well_known_media(Value,Version);
+ list(Value) ->
+ decode_well_known_media(Value,Version);
+ true ->
+ {_,Data2} = Value,
+ decode_well_known_media(d_long(Data2),Version)
+ end,
+ Params = decode_params(Data1, Version),
+ ?WH('Content-Type', ContentType, Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Date: <http-date>
+%% Ref: 8.2.4.25
+%% Type: single, end-to-end
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_date(String) ->
+ {DateTime, _} = parse_http_date(String),
+ ?WH('Date', DateTime, []).
+
+format_date(H) ->
+ fmt_date(H#wsp_header.value).
+
+encode_date(H, _Version) ->
+ e_date(H#wsp_header.value).
+
+decode_date(Value, _Version) ->
+ ?WH('Date', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Etag: <text-string>
+%% Ref: 8.2.4.26
+%% Type: single, end-to-end
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_etag(Value) ->
+ ?WH('Etag', Value, []).
+
+format_etag(H) ->
+ H#wsp_header.value.
+
+encode_etag(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_etag(Value, _Version) ->
+ ?WH('Etag', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Expires: <date-value>
+%% Ref: 8.4.2.27
+%% Type: single, end-to-end, server-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_expires(String) ->
+ {DateTime, _} = parse_http_date(String),
+ ?WH('Expires', DateTime, []).
+
+format_expires(H) ->
+ fmt_date(H#wsp_header.value).
+
+encode_expires(H, _Version) ->
+ e_date(H#wsp_header.value).
+
+decode_expires(Value, _Version) ->
+ ?WH('Expires', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% From: <text-string>
+%% Ref: 8.4.2.28
+%% Type: single,
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_from(Value) ->
+ ?WH('From', Value, []).
+
+format_from(H) ->
+ H#wsp_header.value.
+
+encode_from(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_from(Value, _Version) ->
+ ?WH('From', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Host: <text-string>
+%% Ref: 8.4.2.29
+%% Type: single, end-to-end, client-to-server
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_host(Value) ->
+ ?WH('Host', Value, []).
+
+format_host(H) ->
+ H#wsp_header.value.
+
+encode_host(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_host(Value, _Version) ->
+ ?WH('Host', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% If-Modified-Since: <date-value>
+%% Ref: 8.4.2.30
+%% Type: single, end-to-end, client-to-server
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_if_modified_since(String) ->
+ {DateTime, _} = parse_http_date(String),
+ ?WH('If-Modified-Since', DateTime, []).
+
+format_if_modified_since(H) ->
+ fmt_date(H#wsp_header.value).
+
+encode_if_modified_since(H, _Version) ->
+ e_date(H#wsp_header.value).
+
+decode_if_modified_since(Value, _Version) ->
+ ?WH('If-Modified-Since', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% If-Match: <text-string>
+%% Ref: 8.4.2.31
+%% Type: end-to-end, client-to-server
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_if_match(Value) ->
+ ?WH('If-Match', Value, []).
+
+format_if_match(H) ->
+ H#wsp_header.value.
+
+encode_if_match(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_if_match(Value, _Version) ->
+ ?WH('If-Match', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% If-None-Match: <text-string>
+%% Ref: 8.4.2.32
+%% Type: end-to-end, client-to-server
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_if_none_match(Value) ->
+ ?WH('If-None-Match', Value, []).
+
+format_if_none_match(H) ->
+ H#wsp_header.value.
+
+encode_if_none_match(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_if_none_match(Value, _Version) ->
+ ?WH('If-None-Match', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% If-Range: Text | Date
+%% Ref: 8.4.2.33
+%% Type: end-to-end, client-to-server
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_if_range(Value) ->
+ case catch parse_http_date(Value) of
+ {'EXIT', _} ->
+ ?WH('If-Range', Value, []);
+ {DateTime,_} ->
+ ?WH('If-Range', DateTime, [])
+ end.
+
+
+format_if_range(H) ->
+ case H#wsp_header.value of
+ Value when list(Value) -> Value;
+ DateTime -> fmt_date(DateTime)
+ end.
+
+encode_if_range(H, _Version) ->
+ case H#wsp_header.value of
+ Value when list(Value) ->
+ encode_text_string(Value);
+ DateTime ->
+ e_date(DateTime)
+ end.
+
+decode_if_range(Value, _Version) when list(Value) ->
+ ?WH('If-Range', decode_text_string(Value), []);
+decode_if_range(Value, _Version) ->
+ ?WH('If-Range', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% If-Unmodified-Since: <date-value>
+%% Ref: 8.4.2.34
+%% Type: single, end-to-end, client-to-server
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_if_unmodified_since(String) ->
+ {DateTime, _} = parse_http_date(String),
+ ?WH('If-Unmodified-Since', DateTime, []).
+
+format_if_unmodified_since(H) ->
+ fmt_date(H#wsp_header.value).
+
+encode_if_unmodified_since(H, _Version) ->
+ e_date(H#wsp_header.value).
+
+decode_if_unmodified_since(Value, _Version) ->
+ ?WH('If-Unmodified-Since', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Location: <uri-value>
+%% Ref: 8.4.2.36
+%% Type: single, end-to-end, server-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_location(Value) ->
+ ?WH('Location', Value, []).
+
+format_location(H) ->
+ H#wsp_header.value.
+
+encode_location(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_location(Value, _Version) when list(Value) ->
+ ?WH('Location', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Last-Modified: <date-value>
+%% Ref: 8.4.2.35
+%% Type: single, end-to-end, server-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_last_modified(String) ->
+ {DateTime, _} = parse_http_date(String),
+ ?WH('Last-Modified', DateTime, []).
+
+format_last_modified(H) ->
+ fmt_date(H#wsp_header.value).
+
+encode_last_modified(H, _Version) ->
+ e_date(H#wsp_header.value).
+
+decode_last_modified(Value, _Version) ->
+ ?WH('Last-Modified', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Max-Forwards: <integer-value>
+%% Ref: 8.4.2.37
+%% Type: single, end-to-end, server-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_max_forwards(String) ->
+ ?WH('Max-Forwards', list_to_integer(String), []).
+
+format_max_forwards(H) ->
+ integer_to_list(H#wsp_header.value).
+
+encode_max_forwards(H, _Version) ->
+ encode_integer(H#wsp_header.value).
+
+decode_max_forwards(Value, _Version) ->
+ decode_integer(Value).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Pragma: No-Cache | value-length Parameter
+%% Ref:
+%% Type:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_pragma(Value) ->
+ ?WH('Pragma',Value,[]).
+
+format_pragma(H) ->
+ case H#wsp_header.value of
+ "" -> format_params(H#wsp_header.params);
+ Value -> Value
+ end.
+
+encode_pragma(H, Version) ->
+ case H#wsp_header.value of
+ "no-cache" -> ?ENCODE_SHORT(0);
+ "" ->
+ encode_parameter(hd(H#wsp_header.params), Version)
+ end.
+
+decode_pragma(0, _Version) ->
+ ?WH('Pragma',"no-cache",[]);
+decode_pragma({_,Data}, Version) ->
+ {Param,_} = decode_parameter(Data, Version),
+ ?WH('Pragma',"",[Param]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Proxy-Authenticate:
+%% Ref: 8.4.2.39
+%% Type: single?, client-to-proxy
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_proxy_authenticate(Value) ->
+ parse_challenge('Proxy-Authenticate', Value).
+
+format_proxy_authenticate(H) ->
+ format_challenge(H#wsp_header.value, H#wsp_header.params).
+
+encode_proxy_authenticate(H, Version) ->
+ encode_challenge(H#wsp_header.value,
+ H#wsp_header.params, Version).
+
+decode_proxy_authenticate({_, Data}, Version) ->
+ decode_challenge('Proxy-Authenticate', Data, Version).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Proxy-authorization:
+%% Ref: 8.4.2.40
+%% Type: single?, proxy-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_proxy_authorization(Value) ->
+ parse_credentials('Proxy-Authorization', Value).
+
+format_proxy_authorization(H) ->
+ format_credentials(H#wsp_header.value, H#wsp_header.params).
+
+encode_proxy_authorization(H, Version) ->
+ encode_credentials(H#wsp_header.value, H#wsp_header.params, Version).
+
+decode_proxy_authorization({_,Data}, Version) ->
+ decode_credentials('Proxy-Authorization', Data, Version).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Public: <well-known-method> | Token-Text
+%% Ref: 8.4.2.41
+%% Type:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_public(Value) ->
+ ?WH('Public', parse_well_known_method(Value), []).
+
+format_public(H) ->
+ if atom(H#wsp_header.value) ->
+ atom_to_list(H#wsp_header.value);
+ list(H#wsp_header.value) ->
+ H#wsp_header.value
+ end.
+
+encode_public(H, Version) ->
+ if atom(H#wsp_header.value) ->
+ encode_well_known_method(H#wsp_header.value,Version);
+ list(H#wsp_header.value) ->
+ encode_text_string(H#wsp_header.value)
+ end.
+
+decode_public(Value, _Version) when list(Value) ->
+ ?WH('Public', Value, []);
+decode_public(Value, Version) ->
+ ?WH('Public', decode_well_known_method(Value,Version), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Range:
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_range(Value) ->
+ %% FIXME:
+ ?WH('Range', Value, []).
+
+format_range(H) ->
+ case H#wsp_header.value of
+ {First,undefined} ->
+ ["bytes=", integer_to_list(First), "-"];
+ {First,Last} ->
+ ["bytes=", integer_to_list(First), "-", integer_to_list(Last)];
+ Len when integer(Len) ->
+ ["bytes=-", integer_to_list(Len)]
+ end.
+
+encode_range(H, _Version) ->
+ case H#wsp_header.value of
+ {First,undefined} ->
+ e_value(?ENCODE_SHORT(0),
+ e_uintvar(First));
+ {First,Last} ->
+ e_value(?ENCODE_SHORT(0),
+ e_uintvar(First),
+ e_uintvar(Last));
+ Len when integer(Len) ->
+ e_value(?ENCODE_SHORT(1),
+ e_uintvar(Len))
+ end.
+
+decode_range({_,Data}, _Version) ->
+ case scan_header_data(Data) of
+ {0, Data1} ->
+ case d_uintvar(Data1) of
+ {First, <<>>} ->
+ ?WH('Range', {First, undefined},[]);
+ {First, Data2} ->
+ {Last, _} = d_uintvar(Data2),
+ ?WH('Range', {First, Last}, [])
+ end;
+ {1, Data1} ->
+ {Len, _} =d_uintvar(Data1),
+ ?WH('Range', Len, [])
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Referer: <uri-value>
+%% Ref: 8.4.2.43
+%% Type: single
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_referer(Value) ->
+ ?WH('Referer', Value, []).
+
+format_referer(H) ->
+ H#wsp_header.value.
+
+encode_referer(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_referer(Value, _Version) when list(Value) ->
+ ?WH('Referer', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Retry-After: Value-length (Retry-date-value | Retry-delta-seconds)
+%% Ref: 8.4.2.44
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_retry_after(Value) ->
+ case catch parse_http_date(Value) of
+ {'EXIT', _} ->
+ ?WH('Retry-After', list_to_integer(Value), []);
+ {DateTime,_} ->
+ ?WH('Retry-After', DateTime, [])
+ end.
+
+format_retry_after(H) ->
+ Value = H#wsp_header.value,
+ if integer(Value) ->
+ integer_to_list(Value);
+ true ->
+ fmt_date(Value)
+ end.
+
+encode_retry_after(H, _Version) ->
+ Value = H#wsp_header.value,
+ if integer(Value) ->
+ e_value(?ENCODE_SHORT(1),
+ e_delta_seconds(Value));
+ true ->
+ e_value(?ENCODE_SHORT(0),
+ e_date(Value))
+ end.
+
+decode_retry_after({_,Data}, _Version) ->
+ case scan_header_data(Data) of
+ {0, Data1} ->
+ ?WH('Retry-After', d_date(Data1), []);
+ {1, Data1} ->
+ case scan_header_data(Data1) of
+ Sec when integer(Sec) ->
+ ?WH('Retry-After', Sec, []);
+ {short,Data2} ->
+ ?WH('Retry-After', d_long(Data2), [])
+ end
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Server: <text-string>
+%% Ref: 8.4.2.45
+%% Type: server-to-client
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_server(Value) ->
+ ?WH('Server', Value, []).
+
+format_server(H) ->
+ H#wsp_header.value.
+
+encode_server(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_server(Value, _Version) ->
+ ?WH('Server', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Transfer-Encoding:
+%% Ref: 8.4.2.46
+%% Type: hop-by-hop
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_transfer_encoding(Value) ->
+ ?WH('Transfer-Encoding', Value, []).
+
+format_transfer_encoding(H) ->
+ H#wsp_header.value.
+
+encode_transfer_encoding(H, _Version) ->
+ case H#wsp_header.value of
+ "chunked" -> ?ENCODE_SHORT(0);
+ Value -> encode_text_string(Value)
+ end.
+
+decode_transfer_encoding(0, _Version) ->
+ ?WH('Transfer-Encoding', "chunked", []);
+decode_transfer_encoding(Value, _Version) when list(Value)->
+ ?WH('Transfer-Encoding', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Upgrade: Text-String
+%% Ref: 8.4.2.47
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_upgrade(Value) ->
+ ?WH('Upgrade', Value, []).
+
+format_upgrade(H) ->
+ H#wsp_header.value.
+
+encode_upgrade(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_upgrade(Value, _Version) when list(Value) ->
+ ?WH('Upgrade', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% User-Agent:
+%% Ref: 8.4.2.48
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_user_agent(Value) ->
+ ?WH('User-Agent', Value, []).
+
+format_user_agent(H) ->
+ H#wsp_header.value.
+
+encode_user_agent(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_user_agent(Value, _Version) ->
+ ?WH('User-Agent', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Vary: Well-known-header-field | Token-text
+%% Ref: 8.4.2.49
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_vary(Value) ->
+ ?WH('Vary', normalise_field_name(Value), []).
+
+format_vary(H) ->
+ to_list(H#wsp_header.value).
+
+encode_vary(H, Version) ->
+ e_field_name(H#wsp_header.value, Version).
+
+decode_vary(Value, _Version) when integer(Value) ->
+ ?WH('Vary', lookup_field_name(Value), []);
+decode_vary(Value, _Version) when list(Value) ->
+ ?WH('Vary', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Via: <text-string>
+%% Ref: 8.4.2.50
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_via(Value) ->
+ ?WH('Via', Value, []).
+
+format_via(H) ->
+ H#wsp_header.value.
+
+encode_via(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_via(Value, _Version) when list(Value) ->
+ ?WH('Via', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Warning: Warn-Code | Warning-value
+%% Ref: 8.4.2.51
+%% Type: general, multiple
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_warning(Value) ->
+ case string:tokens(Value, " ") of
+ [Code] ->
+ ?WH('Warning', {list_to_integer(Code),"",""}, []);
+ [Code,Agent,Text] ->
+ ?WH('Warning', {list_to_integer(Code), Agent, Text}, [])
+ end.
+
+format_warning(H) ->
+ case H#wsp_header.value of
+ {Code, "", ""} ->
+ integer_to_list(Code);
+ {Code, Agent, Text} ->
+ [integer_to_list(Code), " ", Agent, " ", Text]
+ end.
+
+encode_warning(H, _Version) ->
+ case H#wsp_header.value of
+ {Code,"",""} ->
+ ?ENCODE_SHORT(Code);
+ {Code, Agent, Text} ->
+ e_value(?ENCODE_SHORT(Code),
+ encode_text_string(Agent),
+ encode_text_string(Text))
+ end.
+
+decode_warning(Value, _Version) when integer(Value) ->
+ ?WH('Warning', {Value, "", ""}, []);
+decode_warning({_, Data}, _Version) ->
+ {Code,Data1}= scan_header_data(Data),
+ {Agent,Data2} = d_text_string(Data1),
+ {Text,_Data3} = d_text_string(Data2),
+ ?WH('Warning', {Code,Agent,Text}, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% WWW-Authenticate: challenge
+%% Ref: 8.4.2.52
+%% Type: single? client-to-server
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_www_authenticate(Value) ->
+ parse_challenge('Www-Authenticate', Value).
+
+format_www_authenticate(H) ->
+ format_challenge(H#wsp_header.value, H#wsp_header.params).
+
+encode_www_authenticate(H, Version) ->
+ encode_challenge(H#wsp_header.value,
+ H#wsp_header.params, Version).
+
+decode_www_authenticate({_, Data}, Version) ->
+ decode_challenge('Www-Authenticate', Data, Version).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Disposition: "form-data" | "attachment" [<param>]*
+%% Ref: 8.4.2.53
+%% Type: single
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_content_disposition(Value) ->
+ ?WH('Content-Disposition', Value, []).
+
+format_content_disposition(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_content_disposition(H, Version) ->
+ case H#wsp_header.value of
+ "form-data" ->
+ e_value(?ENCODE_SHORT(0),
+ encode_params(H#wsp_header.params, Version));
+ "attachment" ->
+ e_value(?ENCODE_SHORT(1),
+ encode_params(H#wsp_header.params, Version))
+ end.
+
+decode_content_disposition({_,Data}, Version) when binary(Data) ->
+ case scan_header_data(Data) of
+ {0, Data1} ->
+ Params = decode_params(Data1, Version),
+ ?WH('Content-Disposition', "form-data", Params);
+ {1, Data1} ->
+ Params = decode_params(Data1, Version),
+ ?WH('Content-Disposition', "attachment", Params)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Application-Id:
+%% Ref: 8.4.2.54
+%% Type:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_application_id(Value) ->
+ ?WH('X-Wap-Application-Id', Value, []).
+
+format_x_wap_application_id(H) ->
+ H#wsp_header.value.
+
+encode_x_wap_application_id(H, _Version) ->
+ encode_push_application(H#wsp_header.value).
+
+decode_x_wap_application_id(Value, _Version) ->
+ ?WH('X-Wap-Application-Id', decode_push_application(Value),[]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Content-Uri: <uri-value>
+%% Ref: 8.4.2.55
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_content_uri(Value) ->
+ ?WH('X-Wap-Content-Uri', Value, []).
+
+format_x_wap_content_uri(H) ->
+ H#wsp_header.value.
+
+encode_x_wap_content_uri(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_x_wap_content_uri(Value, _Version) when list(Value) ->
+ ?WH('X-Wap-Content-Uri', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Initiator-Uri: <uri-value>
+%% Ref: 8.4.2.56
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_initiator_uri(Value) ->
+ ?WH('X-Wap-Initiator-Uri', Value, []).
+
+format_x_wap_initiator_uri(H) ->
+ H#wsp_header.value.
+
+encode_x_wap_initiator_uri(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_x_wap_initiator_uri(Value, _Version) when list(Value) ->
+ ?WH('X-Wap-Initiator-Uri', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Accept-Application: Any-Application | Appication-Id-Value
+%% Ref: 8.4.2.57
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_accept_application(Value) ->
+ ?WH('Accept-Application', Value, []).
+
+format_accept_application(H) ->
+ H#wsp_header.value.
+
+
+encode_accept_application(H, _Version) ->
+ case H#wsp_header.value of
+ "*" -> ?ENCODE_SHORT(0);
+ Value ->
+ case catch encode_push_application(Value) of
+ {'EXIT',_} ->
+ encode_uri_value(Value);
+ App ->
+ encode_integer(App)
+ end
+ end.
+
+decode_accept_application(0, _Version) ->
+ ?WH('Accept-Application', "*", []);
+decode_accept_application(Value, _Version) when integer(Value) ->
+ ?WH('Accept-Application', decode_push_application(Value), []);
+decode_accept_application({short,Data}, _Version) ->
+ Value = d_long(Data),
+ ?WH('Accept-Application', decode_push_application(Value), []);
+decode_accept_application(Value, _Version) when list(Value) ->
+ ?WH('Accept-Application', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Bearer-Indication: <integer-value>
+%% Type: sinlge
+%% Ref: 8.4.2.58
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_bearer_indication(Value) ->
+ ?WH('Bearer-Indication', Value, []).
+
+format_bearer_indication(H) ->
+ integer_to_list(H#wsp_header.value).
+
+encode_bearer_indication(H, _Version) ->
+ encode_integer(H#wsp_header.value).
+
+decode_bearer_indication(Value, _Version) when integer(Value) ->
+ ?WH('Bearer-Indication', Value, []);
+decode_bearer_indication({short,Data}, _Version) ->
+ Value = d_long(Data),
+ ?WH('Bearer-Indication', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Push-Flag: Short-Integer
+%% Type: single
+%% Ref: 8.4.2.59
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_push_flag(Value) ->
+ ?WH('Push-Flag', integer_to_list(Value), []).
+
+format_push_flag(H) ->
+ integer_to_list(H#wsp_header.value).
+
+encode_push_flag(H, _Version) ->
+ ?ENCODE_SHORT(H#wsp_header.value).
+
+decode_push_flag(Value, _Version) when integer(Value) ->
+ ?WH('Push-Flag', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Profile: <uri-value>
+%% Ref: 8.4.2.60
+%% Type: single, hop-by-hop, client-to-proxy
+%%
+%% Note: Normally transfered as 'X-Wap-Profile'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_profile(Value) ->
+ ?WH('Profile', Value, []).
+
+format_profile(H) ->
+ H#wsp_header.value.
+
+encode_profile(H, _Version) ->
+ encode_uri_value(H#wsp_header.value).
+
+decode_profile(Value, _Version) ->
+ ?WH('Profile', decode_uri_value(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Profile-Diff: Value-Length Octets
+%% Ref: 8.4.2.61
+%% Type: single, hop-by-hop, client-to-proxy
+%%
+%% Value is WBXML encoded profile diff information
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_profile_diff(Value) ->
+ %% FIXME parse XML code?
+ ?WH('Profile-Diff', Value, []).
+
+format_profile_diff(_H) ->
+ %% FIXME emit ???
+ "WBXML".
+
+encode_profile_diff(H, _Version) ->
+ e_value(H#wsp_header.value).
+
+decode_profile_diff({_,Value}, _Version) ->
+ ?WH('Profile-Diff', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Profile-Warning: Code
+%% Ref: 8.4.2.62
+%% Type: single
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_profile_warning(Value) ->
+ ?WH('Profile-Warning', {Value,"",undefined}, []).
+
+format_profile_warning(H) ->
+ {Code,Target,Date} = H#wsp_header.value,
+ CodeData = integer_to_list(Code),
+ if Target == "", Date == undefined ->
+ CodeData;
+ Date == undefined ->
+ [CodeData," ",Target];
+ true ->
+ [CodeData," ",Target," ",format_date(Date)]
+ end.
+
+
+encode_profile_warning(H, _Version) ->
+ {Code,Target,Date} = H#wsp_header.value,
+ CodeData = case Code of
+ 100 -> ?ENCODE_SHORT(16#10);
+ 101 -> ?ENCODE_SHORT(16#11);
+ 102 -> ?ENCODE_SHORT(16#12);
+ 200 -> ?ENCODE_SHORT(16#20);
+ 201 -> ?ENCODE_SHORT(16#21);
+ 202 -> ?ENCODE_SHORT(16#22);
+ 203 -> ?ENCODE_SHORT(16#23)
+ end,
+ if Target == "", Date == undefined ->
+ CodeData;
+ Date == undefined ->
+ e_value(CodeData, encode_text_string(Target));
+ true ->
+ e_value(CodeData, encode_text_string(Target), e_date(Date))
+ end.
+
+
+decode_profile_warning(Value, _Version) when integer(Value) ->
+ Code = case Value of
+ 16#10 -> 100;
+ 16#11 -> 101;
+ 16#12 -> 102;
+ 16#20 -> 200;
+ 16#21 -> 201;
+ 16#22 -> 202;
+ 16#23 -> 203
+ end,
+ ?WH('Profile-Warning', {Code,"",undefined}, []);
+decode_profile_warning({_, <<1:1, Value:7, Data>>}, _Version) ->
+ Code = case Value of
+ 16#10 -> 100;
+ 16#11 -> 101;
+ 16#12 -> 102;
+ 16#20 -> 200;
+ 16#21 -> 201;
+ 16#22 -> 202;
+ 16#23 -> 203
+ end,
+ {Target,Data1} = d_text_string(Data),
+ Date =
+ if Data1 == <<>> ->
+ undefined;
+ true ->
+ {DateValue,_} = scan_header_data(Data1),
+ d_date(DateValue)
+ end,
+ ?WH('Profile-Warning', {Code,Target,Date}, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Expect: 100-contine | Expect-expression
+%% Ref: 8.4.2.63
+%% Type: client-to-server
+%% Note: Bug in the spec value-length is missing !!!
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_expect(Value) ->
+ ?WH('Expect', Value, []).
+
+format_expect(H) ->
+ case H#wsp_header.value of
+ {Var,Val} ->
+ [Var,"=",Val, format_params(H#wsp_header.params)];
+ Val when list(Val) ->
+ Val
+ end.
+
+encode_expect(H, Version) ->
+ case H#wsp_header.value of
+ "100-continue" ->
+ ?ENCODE_SHORT(0);
+ {Var,Val} ->
+ e_value(encode_text_string(Var),
+ encode_text_string(Val),
+ encode_params(H#wsp_header.params,Version))
+ end.
+
+decode_expect(0, _Version) ->
+ ?WH('Expect', "100-continue", []);
+decode_expect({_, Data}, Version) ->
+ {Var, Data1} = d_text_string(Data),
+ {Val, Data2} = d_text_string(Data1),
+ Params = decode_params(Data2, Version),
+ ?WH('Expect', {decode_text_string(Var),
+ decode_text_string(Val)}, Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Te: Trailers | TE-General-From
+%% Ref: 8.4.2.64
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_te(Value) ->
+ ?WH('Te', Value, []).
+
+format_te(H) ->
+ [H#wsp_header.value, format_params(H#wsp_header.params)].
+
+encode_te(H, Version) ->
+ case H#wsp_header.value of
+ "trailers" -> ?ENCODE_SHORT(1);
+ "chunked" ->
+ e_value(?ENCODE_SHORT(2),
+ encode_params(H#wsp_header.params,Version));
+ "identity" ->
+ e_value(?ENCODE_SHORT(3),
+ encode_params(H#wsp_header.params,Version));
+ "gzip" ->
+ e_value(?ENCODE_SHORT(4),
+ encode_params(H#wsp_header.params,Version));
+ "compress" ->
+ e_value(?ENCODE_SHORT(5),
+ encode_params(H#wsp_header.params,Version));
+ "deflate" ->
+ e_value(?ENCODE_SHORT(6),
+ encode_params(H#wsp_header.params,Version));
+ Value ->
+ e_value(encode_text_string(Value),
+ encode_params(H#wsp_header.params,Version))
+ end.
+
+decode_te(1, _Version) ->
+ ?WH('Te', "trailers", []);
+decode_te({_, Data}, _Version) ->
+ {Val, Data1} = scan_header_data(Data),
+ Value =
+ case Val of
+ 2 -> "chunked";
+ 3 -> "identity";
+ 4 -> "gzip";
+ 5 -> "compress";
+ 6 -> "deflate";
+ V when list(V) -> V
+ end,
+ Params = case Data1 of
+ <<>> -> [];
+ <<128, QData>> ->
+ {QValue, _} = d_q_value(QData),
+ [{q, QValue}]
+ end,
+ ?WH('Te', Value, Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Trailer: Well-known-header-field | Token-text
+%% Ref: 8.4.2.65
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_trailer(Value) ->
+ ?WH('Trailer', normalise_field_name(Value), []).
+
+format_trailer(H) ->
+ to_list(H#wsp_header.value).
+
+encode_trailer(H, Version) ->
+ e_field_name(H#wsp_header.value, Version).
+
+decode_trailer(Value, _Version) when integer(Value) ->
+ ?WH('Trailer', lookup_field_name(Value), []);
+decode_trailer(Value, _Version) when list(Value) ->
+ ?WH('Trailer', Value, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Tod:
+%% Ref: 8.4.2.66
+%% Type: hop-by-hop
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_tod(String) ->
+ {DateTime, _} = parse_http_date(String),
+ ?WH('X-Wap-Tod', DateTime, []).
+
+format_x_wap_tod(H) ->
+ fmt_date(H#wsp_header.value).
+
+encode_x_wap_tod(H, _Version) ->
+ e_date(H#wsp_header.value).
+
+decode_x_wap_tod(Value, _Version) ->
+ ?WH('X-Wap-Tod', d_date(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Content-Id: <quoted-string>
+%% Type:
+%% Ref: 8.4.2.67
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_content_id(Value) ->
+ ?WH('Content-Id', Value, []).
+
+format_content_id(H) ->
+ [$", H#wsp_header.value, $"].
+
+encode_content_id(H, _Version) ->
+ encode_quoted_string(H#wsp_header.value).
+
+decode_content_id(Value, _Version) when list(Value) ->
+ ?WH('Content-Id', decode_quoted_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Set-Cookie: <len> <cookie-version> <cookie-name> <cokie-value> <parm>*
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_set_cookie(String) ->
+ %% MEGA FIXME; Cookie-value may be a quoted string and
+ %% contain both ,=; etc Fix several cookies on same line!!
+ case string:tokens(String, ";") of
+ [Cookie | Ps] ->
+ case string:tokens(Cookie, "=") of
+ [Name,Value] ->
+ Params = parse_params(Ps),
+ ?WH('Set-Cookie', {{1,0}, Name, Value}, Params);
+ [Name] ->
+ Params = parse_params(Ps),
+ ?WH('Set-Cookie', {{1,0}, Name, ""}, Params)
+ end;
+ [] ->
+ ?WH('Set-Cookie', {{1,0}, String, ""}, [])
+ end.
+
+format_set_cookie(H) ->
+ case H#wsp_header.value of
+ {{1,0},Name,Value} ->
+ [Name, "=", Value,format_params(H#wsp_header.params)];
+ {Version,Name,Value} ->
+ [format_version(Version)," ",
+ Name, "=", Value,
+ format_params(H#wsp_header.params)]
+ end.
+
+encode_set_cookie(H, Version) ->
+ {CookieVersion,Name,Value} = H#wsp_header.value,
+ e_value(encode_version(CookieVersion),
+ encode_text_string(Name),
+ encode_text_string(Value),
+ encode_params(H#wsp_header.params, Version)).
+
+decode_set_cookie({_, Data}, Version) ->
+ {CookieVersion, Data1} = scan_header_data(Data),
+ {CookieName, Data2} = scan_header_data(Data1),
+ {CookieValue, Data3} = scan_header_data(Data2),
+ Params = decode_params(Data3, Version),
+ ?WH('Set-Cookie', {decode_version(CookieVersion),
+ decode_text_string(CookieName),
+ decode_text_string(CookieValue)}, Params).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Cookie:
+%% Ref: 8.4.2.69
+%% Type: single?, client-to-server
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_cookie(Value) ->
+ %% FIXME parse cookie version etc
+ ?WH('Cookie', {{1,0},Value}, []).
+
+format_cookie(H) ->
+ case H#wsp_header.value of
+ {{1,0}, Cookies} ->
+ lists:map(fun({Name,Value,Ps}) ->
+ [Name,"=",Value, format_params(Ps)]
+ end, Cookies);
+ {Version, Cookies} ->
+ [format_version(Version)," ",
+ lists:map(fun({Name,Value,Ps}) ->
+ [Name,"=",Value, format_params(Ps)]
+ end, Cookies)]
+ end.
+
+encode_cookie(H, Version) ->
+ {Version, Cookies} = H#wsp_header.value,
+ e_value(encode_version(Version),
+ encode_cookies(Cookies, [])).
+
+encode_cookies([{Name,Value,Ps} | Cs], Acc) ->
+ List =
+ [encode_text_string(Name),
+ encode_text_string(Value) |
+ case Ps of
+ [{path,P},{domain,D}] ->
+ [encode_text_string(P), encode_text_string(D)];
+ [{domain,D},{path,P}] ->
+ [encode_text_string(P), encode_text_string(D)];
+ [{path,P}] ->
+ [encode_text_string(P)];
+ [{domain,D}] ->
+ [encode_text_string(""), encode_text_string(D)];
+ [] ->
+ []
+ end],
+ Sz = lists:sum(lists:map(fun(B) -> size(B) end, List)),
+ encode_cookies(Cs, [[e_uintvar(Sz) | List] | Acc]);
+encode_cookies([], Acc) ->
+ list_to_binary(lists:reverse(Acc)).
+
+
+decode_cookie({_, Data}, _Version) ->
+ {CookieVersion, Data1} = scan_header_data(Data),
+ Cookies = decode_cookies(Data1, []),
+ ?WH('Cookie', {decode_version(CookieVersion), Cookies}, []).
+
+decode_cookies(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_cookies(Data0, _Acc) -> %% IS IGNORING Acc A BUG OR NOT ?
+ {Len, Data1} = d_uintvar(Data0),
+ <<C0:Len/binary, Data2/binary>> = Data1,
+ {Name, C1} = scan_header_data(C0),
+ {Value, C2} = scan_header_data(C1),
+ {Ps1, C3} =
+ case d_text_string(C2) of
+ {"", C21} -> {[], C21};
+ {Path,C21} -> {[{path,Path}], C21}
+ end,
+ {Ps2, _} =
+ case C3 of
+ <<>> -> {[], <<>>};
+ _ ->
+ {Domain,C4} = d_text_string(C3),
+ {[{domain,Domain}], C4}
+ end,
+ decode_cookies(Data2, [{decode_text_string(Name),
+ decode_text_string(Value),
+ Ps1++Ps2}]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% Encoding-Version: Version-Value | Value-length Code-Page [Version-Value]
+%% Ref: 8.4.2.70
+%% Type: single, hop-by-hop, client-and-proxys
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_encoding_version(Value) ->
+ ?WH('Encoding-Version', parse_version(Value), []).
+
+format_encoding_version(H) ->
+ format_version(H#wsp_header.value).
+
+encode_encoding_version(H, _Version) ->
+ encode_version(H#wsp_header.value).
+
+decode_encoding_version(Value, _Version) when integer(Value) ->
+ ?WH('Encoding-Version', decode_version(Value), []);
+decode_encoding_version(Value, _Version) when list(Value) ->
+ %% Note: in this case we parse the Value since we
+ %% Must know the Encoding version
+ ?WH('Encoding-Version', parse_version(Value), []);
+decode_encoding_version({_,<<_:1,_CodePage:7>>}, _Version) ->
+ %% ??? FIXME
+ ?WH('Encoding-Version', "", []);
+decode_encoding_version({_,<<_:1,_CodePage:7, Data1/binary>>}, _Version) ->
+ {Value,_Data2} = scan_header_data(Data1),
+ %% FIXME CodePage
+ ?WH('Encoding-Version', decode_version(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Security:
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_security(Value) ->
+ ?WH('X-Wap-Security', Value, []).
+
+format_x_wap_security(H) ->
+ H#wsp_header.value.
+
+encode_x_wap_security(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_x_wap_security(Value, _Version) ->
+ ?WH('X-Wap-Security', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Loc-Invocation:
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_loc_invocation(Value) ->
+ ?WH('X-Wap-Loc-Invocation', Value, []).
+
+format_x_wap_loc_invocation(H) ->
+ H#wsp_header.value.
+
+encode_x_wap_loc_invocation(H, _Version) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_x_wap_loc_invocation(Value, _Version) ->
+ ?WH('X-Wap-Loc-Invocation', decode_text_string(Value), []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% X-Wap-Loc-Delivery:
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+parse_x_wap_loc_delivery(Value) ->
+ ?WH('X-Wap-Loc-Delivery', Value, []).
+
+format_x_wap_loc_delivery(H) ->
+ H#wsp_header.value.
+
+encode_x_wap_loc_delivery(H, _Value) ->
+ encode_text_string(H#wsp_header.value).
+
+decode_x_wap_loc_delivery(Value, _Version) ->
+ ?WH('X-Wap-Loc-Delivery', decode_text_string(Value), []).
+
+
+%%
+%% Header Field parameters
+%%
+
+parse_params([Param|Ps]) ->
+ case string:tokens(Param, "=") of
+ [Name,Value0] ->
+ Val = trim(Value0),
+ P = case trim(tolower(Name)) of
+ "q" ->{q,Val};
+ "charset" -> {charset,Val};
+ "level" -> {level,Val};
+ "type" -> {type,Val};
+ "name" -> {name,Val};
+ "filename" -> {filename,Val};
+ "differences" -> {differences,Val};
+ "padding" -> {padding,Val};
+ "start" -> {start,Val};
+ "start-info" -> {'start-info',Val};
+ "comment" -> {comment,Val};
+ "domain" -> {domain,Val};
+ "max-age" -> {'max-age',Val};
+ "path" -> {path,Val};
+ "secure" -> {secure,no_value};
+ "sec" -> {sec, Val};
+ "mac" -> {mac, Val};
+ "creation-date" -> {'creation-date', Val};
+ "modification-date" -> {'modification-date', Val};
+ "read-date" -> {'read-date', Val};
+ "size" -> {size, Val};
+ Nm -> {Nm, Val}
+ end,
+ [P | parse_params(Ps)];
+ _ ->
+ parse_params(Ps)
+ end;
+parse_params([]) ->
+ [].
+
+%% format Params without leading ";"
+format_params0([{Param,no_value}|Ps]) ->
+ [to_list(Param) | format_params(Ps)];
+format_params0([{Param,Value}|Ps]) ->
+ [to_list(Param),"=",to_list(Value) | format_params(Ps)].
+
+format_params(Ps) ->
+ lists:map(fun({Param,no_value}) ->
+ ["; ", to_list(Param)];
+ ({Param,Value})->
+ ["; ", to_list(Param),"=",to_list(Value)]
+ end, Ps).
+
+
+encode_params(Params, Version) ->
+ list_to_binary(encode_params1(Params,Version)).
+
+encode_params1([Param|Ps], Version) ->
+ [ encode_parameter(Param, Version) | encode_params1(Ps, Version)];
+encode_params1([], _Version) ->
+ [].
+
+
+decode_params(Data, Version) ->
+ decode_params(Data, [], Version).
+
+decode_params(<<>>, Ps, _Version) ->
+ lists:reverse(Ps);
+decode_params(Data, Ps, Version) ->
+ {ParamVal, Data1} = decode_parameter(Data, Version),
+ decode_params(Data1, [ParamVal | Ps], Version).
+
+
+
+
+encode_parameter({ParamName, ParamValue}, Ver) ->
+ case ParamName of
+ q when Ver >= 16#01 ->
+ <<1:1, 16#00:7,
+ (encode_typed_field(Ver,'Q-value', ParamValue))/binary>>;
+ charset when Ver >= 16#01 ->
+ <<1:1, 16#01:7,
+ (encode_typed_field(Ver,'Well-known-charset',ParamValue))/binary>>;
+ level when Ver >= 16#01 ->
+ <<1:1, 16#02:7,
+ (encode_typed_field(Ver,'Ver-value',ParamValue))/binary>>;
+
+ type when Ver >= ?WSP_12 ->
+ <<1:1, 16#09:7,
+ (encode_typed_field(Ver,'Constrained-encoding',ParamValue))/binary>>;
+ type when Ver >= 16#01 ->
+ <<1:1, 16#03:7,
+ (encode_typed_field(Ver,'Integer-value',ParamValue))/binary>>;
+
+ name when Ver >= ?WSP_14 ->
+ <<1:1, 16#17:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ name when Ver >= 16#01 ->
+ <<1:1, 16#05:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+ filename when Ver >= ?WSP_14 ->
+ <<1:1, 16#18:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ filename when Ver >= 16#01 ->
+ <<1:1, 16#06:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+ differences when Ver >= 16#01 ->
+ <<1:1, 16#07:7,
+ (encode_typed_field(Ver,'Field-name',ParamValue))/binary>>;
+
+ padding when Ver >= 16#01 ->
+ <<1:1, 16#08:7,
+ (encode_typed_field(Ver,'Short-integer',ParamValue))/binary>>;
+
+
+ start when Ver >= ?WSP_14 ->
+ <<1:1, 16#19:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ start when Ver >= ?WSP_12 ->
+ <<1:1, 16#0A:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+
+ 'start-info' when Ver >= ?WSP_14 ->
+ <<1:1, 16#1A:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ 'start-info' when Ver >= ?WSP_12 ->
+ <<1:1, 16#0B:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+ comment when Ver >= ?WSP_14 ->
+ <<1:1, 16#1B:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ comment when Ver >= ?WSP_13 ->
+ <<1:1, 16#0C:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+ domain when Ver >= ?WSP_14 ->
+ <<1:1, 16#1C:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ domain when Ver >= ?WSP_13 ->
+ <<1:1, 16#0D:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+ 'max-age' when Ver >= ?WSP_13 ->
+ <<1:1, 16#0E:7,
+ (encode_typed_field(Ver,'Delta-seconds-value',ParamValue))/binary>>;
+
+ path when Ver >= ?WSP_14 ->
+ <<1:1, 16#1D:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ path when Ver >= ?WSP_13 ->
+ <<1:1, 16#0F:7,
+ (encode_typed_field(Ver,'Text-string',ParamValue))/binary>>;
+
+ secure when Ver >= ?WSP_13 ->
+ <<1:1, 16#10:7,
+ (encode_typed_field(Ver,'No-value',ParamValue))/binary>>;
+ %% NOTE: "sec" and "mac" are really 1.4 features but used by 1.3 client provisioning
+ %"sec" when Ver >= ?WSP_14 ->
+ sec when Ver >= ?WSP_13 ->
+ <<1:1, 16#11:7,
+ (encode_typed_field(Ver,'Short-integer',ParamValue))/binary>>;
+ %"mac" when Ver >= ?WSP_14 ->
+ mac when Ver >= ?WSP_13 ->
+ <<1:1, 16#12:7,
+ (encode_typed_field(Ver,'Text-value',ParamValue))/binary>>;
+ 'creation-date' when Ver >= ?WSP_14 ->
+ <<1:1, 16#13:7,
+ (encode_typed_field(Ver,'Date-value',ParamValue))/binary>>;
+ 'modification-date' when Ver >= ?WSP_14 ->
+ <<1:1, 16#14:7,
+ (encode_typed_field(Ver,'Date-value',ParamValue))/binary>>;
+ 'read-date' when Ver >= ?WSP_14 ->
+ <<1:1, 16#15:7,
+ (encode_typed_field(Ver,'Date-value',ParamValue))/binary>>;
+ size when Ver >= ?WSP_14 ->
+ <<1:1, 16#16:7,
+ (encode_typed_field(Ver,'Integer-value',ParamValue))/binary>>;
+ _ ->
+ <<(encode_text_string(ParamName))/binary,
+ (encode_text_string(ParamValue))/binary >>
+ end.
+
+%% decode_parameter: return {ParameterName, ParamterValue}
+decode_parameter(<<1:1,Code:7,Data/binary>>, Version) ->
+ case Code of
+ 16#00 ->
+ {Val,Data1} = decode_typed_field('Q-value', Data, Version),
+ {{ q, Val}, Data1};
+
+ 16#01 ->
+ {Val,Data1} = decode_typed_field('Well-known-charset',Data,Version),
+ {{charset, Val}, Data1};
+
+ 16#02 ->
+ {Val,Data1} = decode_typed_field('Version-value',Data,Version),
+ {{level, Val}, Data1};
+
+ 16#03 ->
+ {Val,Data1} = decode_typed_field('Integer-value', Data,Version),
+ {{type, Val}, Data1};
+
+ 16#05 ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{name, Val}, Data1};
+
+ 16#06 ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{filename, Val}, Data1};
+
+ 16#07 ->
+ {Val,Data1} = decode_typed_field('Field-name', Data,Version),
+ {{differences, Val}, Data1};
+
+ 16#08 ->
+ {Val,Data1} = decode_typed_field('Short-integer', Data,Version),
+ {{padding, Val}, Data1};
+
+ 16#09 ->
+ {Val,Data1} = decode_typed_field('Constrained-encoding', Data,Version),
+ {{type, Val}, Data1};
+
+ 16#0A ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{start, Val}, Data1};
+
+ 16#0B ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{'start-info', Val}, Data1};
+
+ 16#0C ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{comment, Val}, Data1};
+
+ 16#0D ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{domain, Val}, Data1};
+
+ 16#0E ->
+ {Val,Data1} = decode_typed_field('Delta-seconds-value', Data,Version),
+ {{'max-age', Val}, Data1};
+
+ 16#0F ->
+ {Val,Data1} = decode_typed_field('Text-string', Data,Version),
+ {{path, Val}, Data1};
+
+ 16#10 ->
+ {Val,Data1} = decode_typed_field('No-value', Data,Version),
+ {{secure, Val}, Data1};
+
+ 16#11 ->
+ {Val,Data1} = decode_typed_field('Short-integer', Data,Version),
+ {{sec, Val}, Data1};
+
+ 16#12 ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{mac, Val}, Data1};
+
+ 16#13 ->
+ {Val,Data1} = decode_typed_field('Date-value', Data,Version),
+ {{'creation-date', Val}, Data1};
+
+ 16#14 ->
+ {Val,Data1} = decode_typed_field('Date-value', Data,Version),
+ {{'modification-date', Val}, Data1};
+
+ 16#15 ->
+ {Val,Data1} = decode_typed_field('Date-value', Data,Version),
+ {{'read-date', Val}, Data1};
+
+ 16#16 ->
+ {Val,Data1} = decode_typed_field('Integer-value', Data,Version),
+ {{size, Val}, Data1};
+
+ 16#17 ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{name, Val}, Data1};
+
+ 16#18 ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{filename, Val}, Data1};
+
+ 16#19 ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{start, Val}, Data1};
+
+ 16#1A ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{'start-info', Val}, Data1};
+
+ 16#1B ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{comment, Val}, Data1};
+
+ 16#1C ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{domain, Val}, Data1};
+
+ 16#1D ->
+ {Val,Data1} = decode_typed_field('Text-value', Data,Version),
+ {{path, Val}, Data1};
+ _ ->
+ exit({error, unknown_parameter})
+ end;
+decode_parameter(Data, _Version) ->
+ %% Untyped-parameter: Token-Text Untype-value
+ {ParamName,Data1} = d_text_string(Data),
+ %% Untype-value: Integer-Value | Text-Value!
+ {ParamValue, Data2} = decode_untyped_value(Data1),
+ {{ParamName,ParamValue}, Data2}.
+
+
+encode_typed_field(Ver,Type,Value) ->
+ case Type of
+ 'Well-known-charset' ->
+ MIBenum = encode_charset(Value),
+ encode_integer(MIBenum);
+
+ 'Constrained-encoding' ->
+ encode_constrained_media(Value, Ver);
+
+ 'Text-string' ->
+ encode_text_string(Value);
+
+ 'Text-value' ->
+ encode_text_value(Value);
+
+ 'Short-integer' ->
+ ?ENCODE_SHORT(Value);
+
+ 'Date-value' ->
+ e_date(Value);
+
+ 'Delta-Seconds-value' ->
+ e_delta_seconds(Value);
+
+ 'No-value' ->
+ e_no_value(Value);
+
+ _ ->
+ io:format("FIXME: encode_typed_field unsupported type = ~p\n",
+ [Type]),
+ exit({error,badtype})
+ end.
+
+
+decode_typed_field(Type, Data, Version) ->
+ case Type of
+ 'Q-value' ->
+ d_q_value(Data);
+
+ 'Well-known-charset' ->
+ {MIBenum, T100} = d_integer_value(Data),
+ {decode_charset(MIBenum), T100};
+
+ 'Constrained-encoding' ->
+ {Value, Data1} = scan_header_data(Data),
+ {decode_constrained_media(Value,Version), Data1};
+
+ 'Text-string' ->
+ d_text_string(Data);
+
+ 'Text-value' ->
+ d_text_value(Data);
+
+ 'Short-integer' ->
+ decode_short_integer(Data);
+
+ 'Delta-seconds-value' ->
+ d_integer_value(Data);
+
+ 'Date-value' ->
+ {Val, Data1} = decode_long_integer(Data),
+ {d_date(Val), Data1};
+
+ 'Field-name' ->
+ d_field_name(Data);
+
+ 'No-value' ->
+ d_no_value(Data);
+
+ _ ->
+ io:format("FIXME: unsupported type = ~p\n",[Type]),
+ exit({error,badtype})
+ end.
+
+
+%% Integer-Value | Text-Value
+%% return as {Value, Tail}
+decode_untyped_value(<<1:1, Short:7, Tail/binary>>) ->
+ {Short, Tail};
+decode_untyped_value(<<0:3, Len:5, Data/binary>>) when Len =/= 31 ->
+ Sz = Len*8,
+ <<Long:Sz, Tail/binary>> = Data,
+ {Long, Tail};
+decode_untyped_value(Data) ->
+ d_text_string(Data).
+
+
+e_field_name(Value, Version) ->
+ case normalise_field_name(Value) of
+ 'Accept' -> <<16#80>>;
+ 'Accept-Charset' when Version >= ?WSP_13 -> <<16#bb>>;
+ 'Accept-Charset' -> <<16#81>>;
+ 'Accept-Encoding' when Version >= ?WSP_13 -> <<16#bc>>;
+ 'Accept-Encoding' -> <<16#82>>;
+ 'Accept-Language' -> <<16#83>>;
+ 'Accept-Ranges' -> <<16#84>>;
+ 'Age' -> <<16#85>>;
+ 'Allow' -> <<16#86>>;
+ 'Authorization' -> <<16#87>>;
+ 'Cache-Control' when Version >= ?WSP_14 -> <<16#c7>>;
+ 'Cache-Control' when Version >= ?WSP_13 -> <<16#bd>>;
+ 'Cache-Control' -> <<16#88>>;
+ 'Connection' -> <<16#89>>;
+ 'Content-Base' -> <<16#8a>>;
+ 'Content-Encoding' -> <<16#8b>>;
+ 'Content-Language' -> <<16#8c>>;
+ 'Content-Length' -> <<16#8d>>;
+ 'Content-Location' -> <<16#8e>>;
+ 'Content-Md5' -> <<16#8f>>;
+ 'Content-Range' when Version >= ?WSP_13 -> <<16#be>>;
+ 'Content-Range' -> <<16#90>>;
+ 'Content-Type' -> <<16#91>>;
+ 'Date' -> <<16#92>>;
+ 'Etag' -> <<16#93>>;
+ 'Expires' -> <<16#94>>;
+ 'From' -> <<16#95>>;
+ 'Host' -> <<16#96>>;
+ 'If-Modified-Since' -> <<16#97>>;
+ 'If-Match' -> <<16#98>>;
+ 'If-None-Match' -> <<16#99>>;
+ 'If-Range' -> <<16#9a>>;
+ 'If-Unmodified-Since' -> <<16#9b>>;
+ 'Location' -> <<16#9c>>;
+ 'Last-Modified' -> <<16#9d>>;
+ 'Max-Forwards' -> <<16#9e>>;
+ 'Pragma' -> <<16#9f>>;
+ 'Proxy-Authenticate' -> <<16#a0>>;
+ 'Proxy-Authorization' -> <<16#a1>>;
+ 'Public' -> <<16#a2>>;
+ 'Range' -> <<16#a3>>;
+ 'Referer' -> <<16#a4>>;
+ 'Retry-After' -> <<16#a5>>;
+ 'Server' -> <<16#a6>>;
+ 'Transfer-Encoding' -> <<16#a7>>;
+ 'Upgrade' -> <<16#a8>>;
+ 'User-Agent' -> <<16#a9>>;
+ 'Vary' -> <<16#aa>>;
+ 'Via' -> <<16#ab>>;
+ 'Warning' -> <<16#ac>>;
+ 'Www-Authenticate' -> <<16#ad>>;
+ 'Content-Disposition' when Version >= ?WSP_14 -> <<16#c5>>;
+ 'Content-Disposition' -> <<16#ae>>;
+ %% VERSION > 1.1
+ 'X-Wap-Application-Id' when Version >= ?WSP_12 -> <<16#af>>;
+ 'X-Wap-Content-Uri' when Version >= ?WSP_12 -> <<16#b0>>;
+ 'X-Wap-Initiator-Uri' when Version >= ?WSP_12 -> <<16#b1>>;
+ 'Accept-Application' when Version >= ?WSP_12 -> <<16#b2>>;
+ 'Bearer-Indication' when Version >= ?WSP_12 -> <<16#b3>>;
+ 'Push-Flag' when Version >= ?WSP_12 -> <<16#b4>>;
+ 'Profile' when Version >= ?WSP_12 -> <<16#b5>>;
+ 'Profile-Diff' when Version >= ?WSP_12 -> <<16#b6>>;
+ 'Profile-Warning' when Version >= ?WSP_12 -> <<16#b7>>;
+ 'Expect' when Version >= ?WSP_15 -> <<16#c8>>;
+ 'Expect' when Version >= ?WSP_13 -> <<16#b8>>;
+ 'Te' when Version >= ?WSP_13 -> <<16#b9>>;
+ 'Trailer' when Version >= ?WSP_13 -> <<16#ba>>;
+ 'X-Wap-Tod' when Version >= ?WSP_13 -> <<16#bf>>;
+ 'Content-Id' when Version >= ?WSP_13 -> <<16#c0>>;
+ 'Set-Cookie' when Version >= ?WSP_13 -> <<16#c1>>;
+ 'Cookie' when Version >= ?WSP_13 -> <<16#c2>>;
+ 'Encoding-Version' when Version >= ?WSP_13 -> <<16#c3>>;
+ 'Profile-Warning' when Version >= ?WSP_14 -> <<16#c4>>;
+ 'X-Wap-Security' when Version >= ?WSP_14 -> <<16#c6>>;
+ 'X-Wap-Loc-Invocation' when Version >= ?WSP_15 -> <<16#c9>>;
+ 'X-Wap-Loc-Delivery' when Version >= ?WSP_15 -> <<16#ca>>;
+ Field -> encode_text_string(atom_to_list(Field))
+ end.
+
+
+%%
+%% decode and normalise on form list_to_atom("Ulll-Ulll-Ull")
+%%
+normalise_field_name(Cs) when atom(Cs) ->
+ Cs;
+normalise_field_name(Cs) ->
+ list_to_atom(normalise_fieldU(Cs)).
+
+normalise_fieldU([C|Cs]) when C >= $a, C =< $z ->
+ [(C-$a)+$A | normalise_fieldL(Cs)];
+normalise_fieldU([C|Cs]) -> [ C | normalise_fieldL(Cs)];
+normalise_fieldU([]) -> [].
+
+normalise_fieldL([C|Cs]) when C >= $A, C =< $Z ->
+ [(C-$A)+$a | normalise_fieldL(Cs)];
+normalise_fieldL([$-|Cs]) -> [$- | normalise_fieldU(Cs)];
+normalise_fieldL([C|Cs]) -> [C | normalise_fieldL(Cs)];
+normalise_fieldL([]) -> [].
+
+
+tolower([C|Cs]) when C >= $A, C =< $Z ->
+ [(C-$A)+$a | tolower(Cs)];
+tolower([C|Cs]) -> [C|tolower(Cs)];
+tolower([]) -> [].
+
+trim(Cs) ->
+ lists:reverse(trim1(lists:reverse(trim1(Cs)))).
+
+trim1([$\s|Cs]) -> trim1(Cs);
+trim1([$\t|Cs]) -> trim1(Cs);
+trim1([$\r|Cs]) -> trim1(Cs);
+trim1([$\n|Cs]) -> trim1(Cs);
+trim1(Cs) -> Cs.
+
+
+d_field_name(Data) ->
+ case scan_header_data(Data) of
+ {Code, Data1} when integer(Code) ->
+ {lookup_field_name(Code), Data1};
+ {TmpField,Data1} when list(TmpField) ->
+ {normalise_field_name(TmpField), Data1}
+ end.
+
+d_no_value(<<0, Data/binary>>) ->
+ {no_value, Data}.
+
+e_no_value(_) ->
+ <<0>>.
+
+
+lookup_field_name(Code) ->
+ case Code of
+%%% Version 1.1
+ 16#00 -> 'Accept';
+ 16#01 -> 'Accept-Charset';
+ 16#02 -> 'Accept-Encoding';
+ 16#03 -> 'Accept-Language';
+ 16#04 -> 'Accept-Ranges';
+ 16#05 -> 'Age';
+ 16#06 -> 'Allow';
+ 16#07 -> 'Authorization';
+ 16#08 -> 'Cache-Control';
+ 16#09 -> 'Connection';
+ 16#0a -> 'Content-Base';
+ 16#0b -> 'Content-Encoding';
+ 16#0c -> 'Content-Language';
+ 16#0d -> 'Content-Length';
+ 16#0e -> 'Content-Location';
+ 16#0f -> 'Content-Md5';
+ 16#10 -> 'Content-Range';
+ 16#11 -> 'Content-Type';
+ 16#12 -> 'Date';
+ 16#13 -> 'Etag';
+ 16#14 -> 'Expires';
+ 16#15 -> 'From';
+ 16#16 -> 'Host';
+ 16#17 -> 'If-Modified-Since';
+ 16#18 -> 'If-Match';
+ 16#19 -> 'If-None-Match';
+ 16#1a -> 'If-Range';
+ 16#1b -> 'If-Unmodified-Since';
+ 16#1c -> 'Location';
+ 16#1d -> 'Last-Modified';
+ 16#1e -> 'Max-Forwards';
+ 16#1f -> 'Pragma';
+ 16#20 -> 'Proxy-Authenticate';
+ 16#21 -> 'Proxy-Authorization';
+ 16#22 -> 'Public';
+ 16#23 -> 'Range';
+ 16#24 -> 'Referer';
+ 16#25 -> 'Retry-After';
+ 16#26 -> 'Server';
+ 16#27 -> 'Transfer-Encoding';
+ 16#28 -> 'Upgrade';
+ 16#29 -> 'User-Agent';
+ 16#2a -> 'Vary';
+ 16#2b -> 'Via';
+ 16#2c -> 'Warning';
+ 16#2d -> 'Www-Authenticate';
+ 16#2e -> 'Content-Disposition';
+%%% Version 1.2
+ 16#2f -> 'X-Wap-Application-Id';
+ 16#30 -> 'X-Wap-Content-Uri';
+ 16#31 -> 'X-Wap-Initiator-Uri';
+ 16#32 -> 'Accept-Application';
+ 16#33 -> 'Bearer-Indication';
+ 16#34 -> 'Push-Flag';
+ 16#35 -> 'Profile';
+ 16#36 -> 'Profile-Diff';
+ 16#37 -> 'Profile-Warning';
+%%% Version 1.3
+ 16#38 -> 'Expect';
+ 16#39 -> 'Te';
+ 16#3a -> 'Trailer';
+ 16#3b -> 'Accept-Charset';
+ 16#3c -> 'Accept-Encoding';
+ 16#3d -> 'Cache-Control';
+ 16#3e -> 'Content-Range';
+ 16#3f -> 'X-Wap-Tod';
+ 16#40 -> 'Content-Id';
+ 16#41 -> 'Set-Cookie';
+ 16#42 -> 'Cookie';
+ 16#43 -> 'Encoding-Version';
+%%% Version 1.4
+ 16#44 -> 'Profile-Warning';
+ 16#45 -> 'Content-Disposition';
+ 16#46 -> 'X-Wap-Security';
+ 16#47 -> 'Cache-Control';
+%%% Version 1.5
+ 16#48 -> 'Expect';
+ 16#49 -> 'X-Wap-Loc-Invocation';
+ 16#4a -> 'X-Wap-Loc-Delivery';
+%% Unknown
+ _ ->
+ list_to_atom("X-Unknown-"++erlang:integer_to_list(Code, 16))
+ end.
+
+
+encode_charset(Charset) ->
+ %% FIXME: we should really resolve aliases as well
+ %% charset:from_aliases(Charset)
+ case charset:from_mime_name(Charset) of
+ 0 -> exit({error, unknown_charset});
+ MIBenum -> MIBenum
+ end.
+
+encode_language(Language) ->
+ Code = encode_lang(tolower(Language)),
+ <<Code>>.
+
+
+
+decode_charset(MIBenum) ->
+ case charset:to_mime_name(MIBenum) of
+ undefined ->
+ exit({error, unknown_charset});
+ Preferred ->
+ Preferred
+ end.
+
+%% ISO 639 Language Assignments, Appendix A, Table 41, Page 102-103
+decode_lang(Code) ->
+ case lookup_language(Code) of
+ [L|_] -> atom_to_list(L);
+ [] -> ""
+ end.
+
+
+lookup_language(Code) ->
+ case Code of
+ 16#01 -> ['aa','afar'];
+ 16#02 -> ['ab','abkhazian'];
+ 16#03 -> ['af','afrikans'];
+ 16#04 -> ['am','amharic'];
+ 16#05 -> ['ar','arabic'];
+ 16#06 -> ['as','assamese'];
+ 16#07 -> ['ay','aymara'];
+ 16#08 -> ['az','azerbaijani'];
+ 16#09 -> ['ba','bashkir'];
+ 16#0a -> ['be','byelorussian'];
+ 16#0b -> ['bg','bulgarian'];
+ 16#0c -> ['bh','bihari'];
+ 16#0d -> ['bi','bislama'];
+ 16#0e -> ['bn','bangla','bengali'];
+ 16#0f -> ['bo','tibetan'];
+ 16#10 -> ['br','breton'];
+ 16#11 -> ['ca','catalan'];
+ 16#12 -> ['co','corsican'];
+ 16#13 -> ['cs','czech'];
+ 16#14 -> ['cy','welsh'];
+ 16#15 -> ['da','danish'];
+ 16#16 -> ['de','german'];
+ 16#17 -> ['dz','bhutani'];
+ 16#18 -> ['el','greek'];
+ 16#19 -> ['en','english'];
+ 16#1a -> ['eo','esperanto'];
+ 16#1b -> ['es','spanish'];
+ 16#1c -> ['et','estonian'];
+ 16#1d -> ['eu','basque'];
+ 16#1e -> ['fa','persian'];
+ 16#1f -> ['fi','finnish'];
+ 16#20 -> ['fj','fiji'];
+ 16#82 -> ['fo','faeroese'];
+ 16#22 -> ['fr','french'];
+ 16#83 -> ['fy','frisian'];
+ 16#24 -> ['ga','irish'];
+ 16#25 -> ['gd','scots-gaelic'];
+ 16#26 -> ['gl','galician'];
+ 16#27 -> ['gn','guarani'];
+ 16#28 -> ['gu','gujarati'];
+ 16#29 -> ['ha','hausa'];
+ 16#2a -> ['he','hebrew'];
+ 16#2b -> ['hi','hindi'];
+ 16#2c -> ['hr','croatian'];
+ 16#2d -> ['hu','hungarian'];
+ 16#2e -> ['hy','armenian'];
+ 16#84 -> ['ia','interlingua'];
+ 16#30 -> ['id','indonesian'];
+ 16#86 -> ['ie','interlingue'];
+ 16#87 -> ['ik','inupiak'];
+ 16#33 -> ['is','icelandic'];
+ 16#34 -> ['it','italian'];
+ 16#89 -> ['iu','inuktitut'];
+ 16#36 -> ['ja','japanese'];
+ 16#37 -> ['jw','javanese'];
+ 16#38 -> ['ka','georgian'];
+ 16#39 -> ['kk','kazakh'];
+ 16#8a -> ['kl','greenlandic'];
+ 16#3b -> ['km','cambodian'];
+ 16#3c -> ['kn','kannada'];
+ 16#3d -> ['ko','korean'];
+ 16#3e -> ['ks','kashmiri'];
+ 16#3f -> ['ku','kurdish'];
+ 16#40 -> ['ky','kirghiz'];
+ 16#8b -> ['la','latin'];
+ 16#42 -> ['ln','lingala'];
+ 16#43 -> ['lo','laothian'];
+ 16#44 -> ['lt','lithuanian'];
+ 16#45 -> ['lv','lettish','latvian'];
+ 16#46 -> ['mg','malagese'];
+ 16#47 -> ['mi','maori'];
+ 16#48 -> ['mk','macedonian'];
+ 16#49 -> ['ml','malayalam'];
+ 16#4a -> ['mn','mongolian'];
+ 16#4b -> ['mo','moldavian'];
+ 16#4c -> ['mr','marathi'];
+ 16#4d -> ['ms','malay'];
+ 16#4e -> ['mt','maltese'];
+ 16#4f -> ['my','burmese'];
+ 16#81 -> ['na','nauru'];
+ 16#51 -> ['ne','nepali'];
+ 16#52 -> ['nl','dutch'];
+ 16#53 -> ['no','norwegian'];
+ 16#54 -> ['oc','occitan'];
+ 16#55 -> ['om','oromo'];
+ 16#56 -> ['or','oriya'];
+ 16#57 -> ['pa','punjabi'];
+ 16#58 -> ['po','polish'];
+ 16#59 -> ['ps','pushto','pashto'];
+ 16#5a -> ['pt','portugese'];
+ 16#5b -> ['qu','quechua'];
+ 16#8c -> ['rm','rhaeto-romance'];
+ 16#5d -> ['rn','kirundi'];
+ 16#5e -> ['ro','romanian'];
+ 16#5f -> ['ru','russian'];
+ 16#60 -> ['rw','kinyarwanda'];
+ 16#61 -> ['sa','sanskrit'];
+ 16#62 -> ['sd','sindhi'];
+ 16#63 -> ['sg','sangho'];
+ 16#64 -> ['sh','serbo-croatian'];
+ 16#65 -> ['si','sinhalese'];
+ 16#66 -> ['sk','slovak'];
+ 16#67 -> ['sl','slovenian'];
+ 16#68 -> ['sm','samoan'];
+ 16#69 -> ['sn','shona'];
+ 16#6a -> ['so','somali'];
+ 16#6b -> ['sq','albanian'];
+ 16#6c -> ['sr','serbian'];
+ 16#6d -> ['ss','siswati'];
+ 16#6e -> ['st','seshoto'];
+ 16#6f -> ['su','sundanese'];
+ 16#70 -> ['sv','swedish'];
+ 16#71 -> ['sw','swahili'];
+ 16#72 -> ['ta','tamil'];
+ 16#73 -> ['te','telugu'];
+ 16#74 -> ['tg','tajik'];
+ 16#75 -> ['th','thai'];
+ 16#76 -> ['ti','tigrinya'];
+ 16#77 -> ['tk','turkmen'];
+ 16#78 -> ['tl','tagalog'];
+ 16#79 -> ['tn','setswana'];
+ 16#7a -> ['to','tonga'];
+ 16#7b -> ['tr','turkish'];
+ 16#7c -> ['ts','tsonga'];
+ 16#7d -> ['tt','tatar'];
+ 16#7e -> ['tw','twi'];
+ 16#7f -> ['ug','uighur'];
+ 16#50 -> ['uk','ukrainian'];
+ 16#21 -> ['ur','urdu'];
+ 16#23 -> ['uz','uzbek'];
+ 16#2f -> ['vi','vietnamese'];
+ 16#85 -> ['vo','volapuk'];
+ 16#31 -> ['wo','wolof'];
+ 16#32 -> ['xh','xhosa'];
+ 16#88 -> ['yi','yiddish'];
+ 16#35 -> ['yo','yoruba'];
+ 16#3a -> ['za','zhuang'];
+ 16#41 -> ['zh','chinese'];
+ 16#5c -> ['zu','zulu'];
+ _ -> []
+ end.
+
+encode_lang(Language) ->
+ case tolower(Language) of
+ "aa" -> 16#01;
+ "afar" -> 16#01;
+ "ab" -> 16#02;
+ "abkhazian" -> 16#02;
+ "af" -> 16#03;
+ "afrikans" -> 16#03;
+ "am" -> 16#04;
+ "amharic" -> 16#04;
+ "ar" -> 16#05;
+ "arabic" -> 16#05;
+ "as" -> 16#06;
+ "assamese" -> 16#06;
+ "ay" -> 16#07;
+ "aymara" -> 16#07;
+ "az" -> 16#08;
+ "azerbaijani" -> 16#08;
+ "ba" -> 16#09;
+ "bashkir" -> 16#09;
+ "be" -> 16#0a;
+ "byelorussian" -> 16#0a;
+ "bg" -> 16#0b;
+ "bulgarian" -> 16#0b;
+ "bh" -> 16#0c;
+ "bihari" -> 16#0c;
+ "bi" -> 16#0d;
+ "bislama" -> 16#0d;
+ "bn" -> 16#0e;
+ "bangla" -> 16#0e;
+ "bengali" -> 16#0e;
+ "bo" -> 16#0f;
+ "tibetan" -> 16#0f;
+ "br" -> 16#10;
+ "breton" -> 16#10;
+ "ca" -> 16#11;
+ "catalan" -> 16#11;
+ "co" -> 16#12;
+ "corsican" -> 16#12;
+ "cs" -> 16#13;
+ "czech" -> 16#13;
+ "cy" -> 16#14;
+ "welsh" -> 16#14;
+ "da" -> 16#15;
+ "danish" -> 16#15;
+ "de" -> 16#16;
+ "german" -> 16#16;
+ "dz" -> 16#17;
+ "bhutani" -> 16#17;
+ "el" -> 16#18;
+ "greek" -> 16#18;
+ "en" -> 16#19;
+ "english" -> 16#19;
+ "eo" -> 16#1a;
+ "esperanto" -> 16#1a;
+ "es" -> 16#1b;
+ "spanish" -> 16#1b;
+ "et" -> 16#1c;
+ "estonian" -> 16#1c;
+ "eu" -> 16#1d;
+ "basque" -> 16#1d;
+ "fa" -> 16#1e;
+ "persian" -> 16#1e;
+ "fi" -> 16#1f;
+ "finnish" -> 16#1f;
+ "fj" -> 16#20;
+ "fiji" -> 16#20;
+ "fo" -> 16#82;
+ "faeroese" -> 16#82;
+ "fr" -> 16#22;
+ "french" -> 16#22;
+ "fy" -> 16#83;
+ "frisian" -> 16#83;
+ "ga" -> 16#24;
+ "irish" -> 16#24;
+ "gd" -> 16#25;
+ "scots-gaelic" -> 16#25;
+ "gl" -> 16#26;
+ "galician" -> 16#26;
+ "gn" -> 16#27;
+ "guarani" -> 16#27;
+ "gu" -> 16#28;
+ "gujarati" -> 16#28;
+ "ha" -> 16#29;
+ "hausa" -> 16#29;
+ "he" -> 16#2a;
+ "hebrew" -> 16#2a;
+ "hi" -> 16#2b;
+ "hindi" -> 16#2b;
+ "hr" -> 16#2c;
+ "croatian" -> 16#2c;
+ "hu" -> 16#2d;
+ "hungarian" -> 16#2d;
+ "hy" -> 16#2e;
+ "armenian" -> 16#2e;
+ "ia" -> 16#84;
+ "interlingua" -> 16#84;
+ "id" -> 16#30;
+ "indonesian" -> 16#30;
+ "ie" -> 16#86;
+ "interlingue" -> 16#86;
+ "ik" -> 16#87;
+ "inupiak" -> 16#87;
+ "is" -> 16#33;
+ "icelandic" -> 16#33;
+ "it" -> 16#34;
+ "italian" -> 16#34;
+ "iu" -> 16#89;
+ "inuktitut" -> 16#89;
+ "ja" -> 16#36;
+ "japanese" -> 16#36;
+ "jw" -> 16#37;
+ "javanese" -> 16#37;
+ "ka" -> 16#38;
+ "georgian" -> 16#38;
+ "kk" -> 16#39;
+ "kazakh" -> 16#39;
+ "kl" -> 16#8a;
+ "greenlandic" -> 16#8a;
+ "km" -> 16#3b;
+ "cambodian" -> 16#3b;
+ "kn" -> 16#3c;
+ "kannada" -> 16#3c;
+ "ko" -> 16#3d;
+ "korean" -> 16#3d;
+ "ks" -> 16#3e;
+ "kashmiri" -> 16#3e;
+ "ku" -> 16#3f;
+ "kurdish" -> 16#3f;
+ "ky" -> 16#40;
+ "kirghiz" -> 16#40;
+ "la" -> 16#8b;
+ "latin" -> 16#8b;
+ "ln" -> 16#42;
+ "lingala" -> 16#42;
+ "lo" -> 16#43;
+ "laothian" -> 16#43;
+ "lt" -> 16#44;
+ "lithuanian" -> 16#44;
+ "lv" -> 16#45;
+ "lettish" -> 16#45;
+ "latvian" -> 16#45;
+ "mg" -> 16#46;
+ "malagese" -> 16#46;
+ "mi" -> 16#47;
+ "maori" -> 16#47;
+ "mk" -> 16#48;
+ "macedonian" -> 16#48;
+ "ml" -> 16#49;
+ "malayalam" -> 16#49;
+ "mn" -> 16#4a;
+ "mongolian" -> 16#4a;
+ "mo" -> 16#4b;
+ "moldavian" -> 16#4b;
+ "mr" -> 16#4c;
+ "marathi" -> 16#4c;
+ "ms" -> 16#4d;
+ "malay" -> 16#4d;
+ "mt" -> 16#4e;
+ "maltese" -> 16#4e;
+ "my" -> 16#4f;
+ "burmese" -> 16#4f;
+ "na" -> 16#81;
+ "nauru" -> 16#81;
+ "ne" -> 16#51;
+ "nepali" -> 16#51;
+ "nl" -> 16#52;
+ "dutch" -> 16#52;
+ "no" -> 16#53;
+ "norwegian" -> 16#53;
+ "oc" -> 16#54;
+ "occitan" -> 16#54;
+ "om" -> 16#55;
+ "oromo" -> 16#55;
+ "or" -> 16#56;
+ "oriya" -> 16#56;
+ "pa" -> 16#57;
+ "punjabi" -> 16#57;
+ "po" -> 16#58;
+ "polish" -> 16#58;
+ "ps" -> 16#59;
+ "pushto" -> 16#59;
+ "pt" -> 16#5a;
+ "portugese" -> 16#5a;
+ "qu" -> 16#5b;
+ "quechua" -> 16#5b;
+ "rm" -> 16#8c;
+ "rhaeto-romance" -> 16#8c;
+ "rn" -> 16#5d;
+ "kirundi" -> 16#5d;
+ "ro" -> 16#5e;
+ "romanian" -> 16#5e;
+ "ru" -> 16#5f;
+ "russian" -> 16#5f;
+ "rw" -> 16#60;
+ "kinyarwanda" -> 16#60;
+ "sa" -> 16#61;
+ "sanskrit" -> 16#61;
+ "sd" -> 16#62;
+ "sindhi" -> 16#62;
+ "sg" -> 16#63;
+ "sangho" -> 16#63;
+ "sh" -> 16#64;
+ "serbo-croatian" -> 16#64;
+ "si" -> 16#65;
+ "sinhalese" -> 16#65;
+ "sk" -> 16#66;
+ "slovak" -> 16#66;
+ "sl" -> 16#67;
+ "slovenian" -> 16#67;
+ "sm" -> 16#68;
+ "samoan" -> 16#68;
+ "sn" -> 16#69;
+ "shona" -> 16#69;
+ "so" -> 16#6a;
+ "somali" -> 16#6a;
+ "sq" -> 16#6b;
+ "albanian" -> 16#6b;
+ "sr" -> 16#6c;
+ "serbian" -> 16#6c;
+ "ss" -> 16#6d;
+ "siswati" -> 16#6d;
+ "st" -> 16#6e;
+ "seshoto" -> 16#6e;
+ "su" -> 16#6f;
+ "sundanese" -> 16#6f;
+ "sv" -> 16#70;
+ "swedish" -> 16#70;
+ "sw" -> 16#71;
+ "swahili" -> 16#71;
+ "ta" -> 16#72;
+ "tamil" -> 16#72;
+ "te" -> 16#73;
+ "telugu" -> 16#73;
+ "tg" -> 16#74;
+ "tajik" -> 16#74;
+ "th" -> 16#75;
+ "thai" -> 16#75;
+ "ti" -> 16#76;
+ "tigrinya" -> 16#76;
+ "tk" -> 16#77;
+ "turkmen" -> 16#77;
+ "tl" -> 16#78;
+ "tagalog" -> 16#78;
+ "tn" -> 16#79;
+ "setswana" -> 16#79;
+ "to" -> 16#7a;
+ "tonga" -> 16#7a;
+ "tr" -> 16#7b;
+ "turkish" -> 16#7b;
+ "ts" -> 16#7c;
+ "tsonga" -> 16#7c;
+ "tt" -> 16#7d;
+ "tatar" -> 16#7d;
+ "tw" -> 16#7e;
+ "twi" -> 16#7e;
+ "ug" -> 16#7f;
+ "uighur" -> 16#7f;
+ "uk" -> 16#50;
+ "ukrainian" -> 16#50;
+ "ur" -> 16#21;
+ "urdu" -> 16#21;
+ "uz" -> 16#23;
+ "uzbek" -> 16#23;
+ "vi" -> 16#2f;
+ "vietnamese" -> 16#2f;
+ "vo" -> 16#85;
+ "volapuk" -> 16#85;
+ "wo" -> 16#31;
+ "wolof" -> 16#31;
+ "xh" -> 16#32;
+ "xhosa" -> 16#32;
+ "yi" -> 16#88;
+ "yiddish" -> 16#88;
+ "yo" -> 16#35;
+ "yoruba" -> 16#35;
+ "za" -> 16#3a;
+ "zhuang" -> 16#3a;
+ "zh" -> 16#41;
+ "chinese" -> 16#41;
+ "zu" -> 16#5c;
+ "zulu" -> 16#5c
+ end.
+
+
+%% Push Application ID Assignments
+%%
+%% Assingment are found at http://www.wapforum.org/wina/push-app-id.htm
+%%
+decode_push_application({short,Data}) ->
+ decode_push_application(d_long(Data));
+
+decode_push_application(Code) when integer(Code) ->
+ case Code of
+ 16#00 -> "x-wap-application:*";
+ 16#01 -> "x-wap-application:push.sia";
+ 16#02 -> "x-wap-application:wml.ua";
+ 16#03 -> "x-wap-application:wta.ua";
+ 16#04 -> "x-wap-application:mms.ua";
+ 16#05 -> "x-wap-application:push.syncml";
+ 16#06 -> "x-wap-application:loc.ua";
+ 16#07 -> "x-wap-application:syncml.dm";
+ 16#08 -> "x-wap-application:drm.ua";
+ 16#09 -> "x-wap-application:emn.ua";
+ 16#0A -> "x-wap-application:wv.ua";
+ 16#8000 -> "x-wap-microsoft:localcontent.ua";
+ 16#8001 -> "x-wap-microsoft:IMclient.ua";
+ 16#8002 -> "x-wap-docomo:imode.mail.ua";
+ 16#8003 -> "x-wap-docomo:imode.mr.ua";
+ 16#8004 -> "x-wap-docomo:imode.mf.ua";
+ 16#8005 -> "x-motorola:location.ua";
+ 16#8006 -> "x-motorola:now.ua";
+ 16#8007 -> "x-motorola:otaprov.ua";
+ 16#8008 -> "x-motorola:browser.ua";
+ 16#8009 -> "x-motorola:splash.ua";
+ 16#800B -> "x-wap-nai:mvsw.command";
+ 16#8010 -> "x-wap-openwave:iota.ua"
+ end;
+decode_push_application(App) when list(App) ->
+ App.
+
+
+
+encode_push_application(App) ->
+ case App of
+ "x-wap-application:*" -> ?ENCODE_SHORT(16#00);
+ "x-wap-application:push.sia" -> ?ENCODE_SHORT(16#01);
+ "x-wap-application:wml.ua" -> ?ENCODE_SHORT(16#02);
+ "x-wap-application:wta.ua" -> ?ENCODE_SHORT(16#03);
+ "x-wap-application:mms.ua" -> ?ENCODE_SHORT(16#04);
+ "x-wap-application:push.syncml" -> ?ENCODE_SHORT(16#05);
+ "x-wap-application:loc.ua" -> ?ENCODE_SHORT(16#06);
+ "x-wap-application:syncml.dm" -> ?ENCODE_SHORT(16#07);
+ "x-wap-application:drm.ua" -> ?ENCODE_SHORT(16#08);
+ "x-wap-application:emn.ua" -> ?ENCODE_SHORT(16#09);
+ "x-wap-application:wv.ua" -> ?ENCODE_SHORT(16#0A);
+ "x-wap-microsoft:localcontent.ua" -> encode_integer(16#8000);
+ "x-wap-microsoft:IMclient.ua" -> encode_integer(16#8001);
+ "x-wap-docomo:imode.mail.ua" -> encode_integer(16#8002);
+ "x-wap-docomo:imode.mr.ua" -> encode_integer(16#8003);
+ "x-wap-docomo:imode.mf.ua" -> encode_integer(16#8004);
+ "x-motorola:location.ua" -> encode_integer(16#8005);
+ "x-motorola:now.ua" -> encode_integer(16#8006);
+ "x-motorola:otaprov.ua" -> encode_integer(16#8007);
+ "x-motorola:browser.ua" -> encode_integer(16#8008);
+ "x-motorola:splash.ua" -> encode_integer(16#8009);
+ "x-wap-nai:mvsw.command" -> encode_integer(16#800B);
+ "x-wap-openwave:iota.ua" -> encode_integer(16#8010);
+ _ -> encode_uri_value(App)
+ end.
+
+
+
+
+%% WSP 8.5 Multipart handling
+
+encode_multipart(Entries) ->
+ encode_multipart(Entries, ?WSP_DEFAULT_VERSION).
+
+encode_multipart([], _Version) ->
+ <<>>;
+encode_multipart(Entries, Version) ->
+ EncEntries = encode_multipart_entries(Entries, Version),
+ <<(e_uintvar(length(Entries)))/binary, EncEntries/binary >>.
+
+encode_multipart_entries(Entries, Version) ->
+ encode_multipart_entries(Entries, Version, []).
+
+encode_multipart_entries([], _Version, Acc) ->
+ list_to_binary(lists:reverse(Acc));
+encode_multipart_entries([Entry|T], Version, Acc) ->
+ EncEntry = encode_multipart_entry(Entry, Version),
+ encode_multipart_entries(T, Version, [EncEntry | Acc]).
+
+encode_multipart_entry(Entry, Version) ->
+ #wsp_multipart_entry { content_type = ContentType,
+ headers = Headers,
+ data = Data } = Entry,
+ EncContentType = encode_content_type(ContentType,Version),
+ EncHeaders = encode_headers(Headers, Version),
+ EncHeadersLength = e_uintvar(size(EncContentType)+size(EncHeaders)),
+ DataLen = e_uintvar(size(Data)),
+ <<EncHeadersLength/binary,
+ DataLen/binary,
+ EncContentType/binary,
+ EncHeaders/binary,
+ Data/binary>>.
+
+
+decode_multipart(Data) ->
+ decode_multipart(Data, ?WSP_DEFAULT_VERSION).
+
+decode_multipart(<<>>, _Version) ->
+ {[], <<>>};
+decode_multipart(Data, Version) ->
+ {Entries, Data1} = d_uintvar(Data),
+ decode_multipart_entries(Entries, Data1, Version).
+
+decode_multipart_entries(Entries, Data, Version) ->
+ decode_multipart_entries(Entries, Data, Version, []).
+
+decode_multipart_entries(0, Data, _Version, Acc) ->
+ {lists:reverse(Acc), Data};
+decode_multipart_entries(Entries, Data, Version, Acc) ->
+ {MultiPartEntry, Data1} = decode_multipart_entry(Data,Version),
+ decode_multipart_entries(Entries-1, Data1, Version, [MultiPartEntry|Acc]).
+
+decode_multipart_entry(Data, Version) ->
+ {HeadersLen, Data1} = d_uintvar(Data),
+ {DataLen, Data2} = d_uintvar(Data1),
+ {FieldData,Data3} = scan_header_data(Data2),
+ ContentType = decode_content_type(FieldData, Version),
+ BinHeadersLen = (HeadersLen-(size(Data2)-size(Data3))),
+ <<BinHeaders:BinHeadersLen/binary,Data4/binary>> = Data3,
+ Headers = decode_headers(BinHeaders, Version),
+ <<ValueData:DataLen/binary, Data5/binary>> = Data4,
+ {#wsp_multipart_entry{content_type=ContentType,
+ headers=Headers,
+ data=ValueData},Data5}.
+
+
+parse_credentials(Field, Value) ->
+ %% FIXME
+ ?WH(Field, Value, []).
+
+format_credentials("basic", [User,Password]) ->
+ ["Basic ", base64:encode(User++":"++Password)];
+format_credentials(Scheme, Params) ->
+ [Scheme, format_params(Params)].
+
+encode_credentials("basic", [User,Password], _Version) ->
+ e_value(?ENCODE_SHORT(0),
+ encode_text_string(User),
+ encode_text_string(Password));
+encode_credentials(Scheme, Params, Version) ->
+ e_value(encode_text_string(Scheme), encode_params(Params, Version)).
+
+decode_credentials(Field, Data, Version) ->
+ case scan_header_data(Data) of
+ {0, Data0} ->
+ {User,Data1} = d_text_string(Data0),
+ {Password,_Data2} = d_text_string(Data1),
+ ?WH(Field, "basic", [User,Password]);
+ {Scheme, Data0} when list(Scheme) ->
+ Params = decode_params(Data0, Version),
+ ?WH(Field, Scheme, Params)
+ end.
+
+%%
+%% Challenge: Basic Realm-value | Auth-Scheme Realm *Auth-Params
+%%
+
+parse_challenge(Field, Value) ->
+ %% FIXME
+ ?WH(Field, Value, []).
+
+format_challenge({"basic",Realm}, []) ->
+ ["Basic ", Realm];
+format_challenge({Scheme,Realm}, Params) ->
+ [Scheme," ",Realm, format_params(Params)].
+
+encode_challenge({"basic",Realm}, [], _Version) ->
+ e_value(?ENCODE_SHORT(0),
+ encode_text_string(Realm));
+encode_challenge({Scheme,Realm}, Params, Version) ->
+ e_value(encode_text_string(Scheme),
+ encode_text_string(Realm),
+ encode_params(Params, Version)).
+
+decode_challenge(Field, Data, Version) ->
+ case scan_header_data(Data) of
+ {0, Data0} ->
+ {Realm,_} = d_text_string(Data0),
+ ?WH(Field, {"basic", Realm}, []);
+ {Scheme, Data0} when list(Scheme) ->
+ {Realm,_} = d_text_string(Data0),
+ Params = decode_params(Data0, Version),
+ ?WH(Field, {Scheme,Realm}, Params)
+ end.
+
+
+parse_well_known_method(Value) ->
+ case Value of
+ "GET" -> 'GET';
+ "OPTIONS" -> 'OPTIONS';
+ "HEAD" -> 'HEAD';
+ "DELETE" -> 'DELETE';
+ "TRACE" -> 'TRACE';
+ "POST" -> 'POST';
+ "PUT" -> 'PUT'
+ end.
+
+encode_well_known_method(Value, _Version) ->
+ case Value of
+ 'GET' -> ?ENCODE_SHORT(16#40);
+ 'OPTIONS' -> ?ENCODE_SHORT(16#41);
+ 'HEAD' -> ?ENCODE_SHORT(16#42);
+ 'DELETE' -> ?ENCODE_SHORT(16#43);
+ 'TRACE' -> ?ENCODE_SHORT(16#44);
+ 'POST' -> ?ENCODE_SHORT(16#60);
+ 'PUT' -> ?ENCODE_SHORT(16#61)
+ end.
+
+decode_well_known_method(Value, _Version) ->
+ case Value of
+ 16#40 -> 'GET';
+ 16#41 -> 'OPTIONS';
+ 16#42 -> 'HEAD';
+ 16#43 -> 'DELETE';
+ 16#44 -> 'TRACE';
+ 16#60 -> 'POST';
+ 16#61 -> 'PUT'
+ end.
+
+
+
+%%
+%% WSP Table 36. Status Code Assignments
+%%
+
+encode_status_code(Status) ->
+ case Status of
+ 100 -> 16#10; %% 'Continue'
+ 101 -> 16#11; %% 'Switching Protocols'
+ 200 -> 16#20; %% 'OK, Success'
+ 201 -> 16#21; %% 'Created'
+ 202 -> 16#22; %% 'Accepted'
+ 203 -> 16#23; %% 'Non-Authoritative Information'
+ 204 -> 16#24; %% 'No Content'
+ 205 -> 16#25; %% 'Reset Content'
+ 206 -> 16#26; %% 'Partial Content'
+ 300 -> 16#30; %% 'Multiple Choices'
+ 301 -> 16#31; %% 'Moved Permanently'
+ 302 -> 16#32; %% 'Moved temporarily'
+ 303 -> 16#33; %% 'See Other'
+ 304 -> 16#34; %% 'Not modified'
+ 305 -> 16#35; %% 'Use Proxy'
+ 306 -> 16#36; %% '(reserved)'
+ 307 -> 16#37; %% 'Temporary Redirect'
+ 400 -> 16#40; %% 'Bad Request - server could not understand request'
+ 401 -> 16#41; %% 'Unauthorized'
+ 402 -> 16#42; %% 'Payment required'
+ 403 -> 16#43; %% 'Forbidden operation is understood but refused'
+ 404 -> 16#44; %% 'Not Found'
+ 405 -> 16#45; %% 'Method not allowed'
+ 406 -> 16#46; %% 'Not Acceptable'
+ 407 -> 16#47; %% 'Proxy Authentication required'
+ 408 -> 16#48; %% 'Request Timeout'
+ 409 -> 16#49; %% 'Conflict'
+ 410 -> 16#4A; %% 'Gone'
+ 411 -> 16#4B; %% 'Length Required'
+ 412 -> 16#4C; %% 'Precondition failed'
+ 413 -> 16#4D; %% 'Request entity too large'
+ 414 -> 16#4E; %% 'Request-URI too large'
+ 415 -> 16#4F; %% 'Unsupported media type'
+ 416 -> 16#50; %% 'Requested Range Not Satisfiable'
+ 417 -> 16#51; %% 'Expectation Failed'
+ 500 -> 16#60; %% 'Internal Server Error'
+ 501 -> 16#61; %% 'Not Implemented'
+ 502 -> 16#62; %% 'Bad Gateway'
+ 503 -> 16#63; %% 'Service Unavailable'
+ 504 -> 16#64; %% 'Gateway Timeout'
+ 505 -> 16#65 %% 'HTTP version not supported'
+ end.
+
+
+decode_status_code(StatusCode) ->
+ case StatusCode of
+ 16#10 -> 100; %% 'Continue'
+ 16#11 -> 101; %% 'Switching Protocols'
+ 16#20 -> 200; %% 'OK, Success'
+ 16#21 -> 201; %% 'Created'
+ 16#22 -> 202; %% 'Accepted'
+ 16#23 -> 203; %% 'Non-Authoritative Information'
+ 16#24 -> 204; %% 'No Content'
+ 16#25 -> 205; %% 'Reset Content'
+ 16#26 -> 206; %% 'Partial Content'
+ 16#30 -> 300; %% 'Multiple Choices'
+ 16#31 -> 301; %% 'Moved Permanently'
+ 16#32 -> 302; %% 'Moved temporarily'
+ 16#33 -> 303; %% 'See Other'
+ 16#34 -> 304; %% 'Not modified'
+ 16#35 -> 305; %% 'Use Proxy'
+ 16#36 -> 306; %% '(reserved)'
+ 16#37 -> 307; %% 'Temporary Redirect'
+ 16#40 -> 400; %% 'Bad Request - server could not understand request'
+ 16#41 -> 401; %% 'Unauthorized'
+ 16#42 -> 402; %% 'Payment required'
+ 16#43 -> 403; %% 'Forbidden operation is understood but refused'
+ 16#44 -> 404; %% 'Not Found'
+ 16#45 -> 405; %% 'Method not allowed'
+ 16#46 -> 406; %% 'Not Acceptable'
+ 16#47 -> 407; %% 'Proxy Authentication required'
+ 16#48 -> 408; %% 'Request Timeout'
+ 16#49 -> 409; %% 'Conflict'
+ 16#4A -> 410; %% 'Gone'
+ 16#4B -> 411; %% 'Length Required'
+ 16#4C -> 412; %% 'Precondition failed'
+ 16#4D -> 413; %% 'Request entity too large'
+ 16#4E -> 414; %% 'Request-URI too large'
+ 16#4F -> 415; %% 'Unsupported media type'
+ 16#50 -> 416; %% 'Requested Range Not Satisfiable'
+ 16#51 -> 417; %% 'Expectation Failed'
+ 16#60 -> 500; %% 'Internal Server Error'
+ 16#61 -> 501; %% 'Not Implemented'
+ 16#62 -> 502; %% 'Bad Gateway'
+ 16#63 -> 503; %% 'Service Unavailable'
+ 16#64 -> 504; %% 'Gateway Timeout'
+ 16#65 -> 505 %% 'HTTP version not supported'
+ end.
+
+
+%%
+%% Content Type Assignments
+%%
+%% Assingment are found at http://www.wapforum.org/wina/wsp-content-type.htm
+%%
+%%
+%% string(Version, ContentType) -> Code
+%%
+encode_well_known_media(ContentType, Version) ->
+ case ContentType of
+ %% WSP_REGISTERED_CONTENT_TYPES
+ "application/vnd.uplanet.cacheop-wbxml" ->
+ encode_integer(16#0201);
+ "application/vnd.uplanet.signal" ->
+ encode_integer(16#0202);
+ "application/vnd.uplanet.alert-wbxml" ->
+ encode_integer(16#0203);
+ "application/vnd.uplanet.list-wbxml" ->
+ encode_integer(16#0204);
+ "application/vnd.uplanet.listcmd-wbxml" ->
+ encode_integer(16#0205);
+ "application/vnd.uplanet.channel-wbxml" ->
+ encode_integer(16#0206);
+ "application/vnd.uplanet.provisioning-status-uri" ->
+ encode_integer(16#0207);
+ "x-wap.multipart/vnd.uplanet.header-set" ->
+ encode_integer(16#0208);
+ "application/vnd.uplanet.bearer-choice-wbxml" ->
+ encode_integer(16#0209);
+ "application/vnd.phonecom.mmc-wbxml" ->
+ encode_integer(16#020A);
+ "application/vnd.nokia.syncset+wbxml" ->
+ encode_integer(16#020B);
+ "image/x-up-wpng" ->
+ encode_integer(16#020C);
+ _ ->
+ encode_constrained_media(ContentType, Version)
+ end.
+
+
+encode_constrained_media(ContentType, Version) ->
+ case ContentType of
+ "*/*" -> ?ENCODE_SHORT(16#00);
+ "text/*" -> ?ENCODE_SHORT(16#01);
+ "text/html" -> ?ENCODE_SHORT(16#02);
+ "text/plain" -> ?ENCODE_SHORT(16#03);
+ "text/x-hdml" -> ?ENCODE_SHORT(16#04);
+ "text/x-ttml" -> ?ENCODE_SHORT(16#05);
+ "text/x-vcalendar" -> ?ENCODE_SHORT(16#06);
+ "text/x-vcard" -> ?ENCODE_SHORT(16#07);
+ "text/vnd.wap.wml" -> ?ENCODE_SHORT(16#08);
+ "text/vnd.wap.wmlscript" -> ?ENCODE_SHORT(16#09);
+ "text/vnd.wap.wta-event" -> ?ENCODE_SHORT(16#0A);
+ "multipart/*" -> ?ENCODE_SHORT(16#0B);
+ "multipart/mixed" -> ?ENCODE_SHORT(16#0C);
+ "multipart/form-data" -> ?ENCODE_SHORT(16#0D);
+ "multipart/byterantes" -> ?ENCODE_SHORT(16#0E);
+ "multipart/alternative" -> ?ENCODE_SHORT(16#0F);
+ "application/*" -> ?ENCODE_SHORT(16#10);
+ "application/java-vm" -> ?ENCODE_SHORT(16#11);
+ "application/x-www-form-urlencoded" -> ?ENCODE_SHORT(16#12);
+ "application/x-hdmlc" -> ?ENCODE_SHORT(16#13);
+ "application/vnd.wap.wmlc" -> ?ENCODE_SHORT(16#14);
+ "application/vnd.wap.wmlscriptc" -> ?ENCODE_SHORT(16#15);
+ "application/vnd.wap.wta-eventc" -> ?ENCODE_SHORT(16#16);
+ "application/vnd.wap.uaprof" -> ?ENCODE_SHORT(16#17);
+ "application/vnd.wap.wtls-ca-certificate" -> ?ENCODE_SHORT(16#18);
+ "application/vnd.wap.wtls-user-certificate" -> ?ENCODE_SHORT(16#19);
+ "application/x-x509-ca-cert" -> ?ENCODE_SHORT(16#1A);
+ "application/x-x509-user-cert" -> ?ENCODE_SHORT(16#1B);
+ "image/*" -> ?ENCODE_SHORT(16#1C);
+ "image/gif" -> ?ENCODE_SHORT(16#1D);
+ "image/jpeg" -> ?ENCODE_SHORT(16#1E);
+ "image/tiff" -> ?ENCODE_SHORT(16#1F);
+ "image/png" -> ?ENCODE_SHORT(16#20);
+ "image/vnd.wap.wbmp" -> ?ENCODE_SHORT(16#21);
+ "application/vnd.wap.multipart.*" -> ?ENCODE_SHORT(16#22);
+ "application/vnd.wap.multipart.mixed" -> ?ENCODE_SHORT(16#23);
+ "application/vnd.wap.multipart.form-data" -> ?ENCODE_SHORT(16#24);
+ "application/vnd.wap.multipart.byteranges" -> ?ENCODE_SHORT(16#25);
+ "application/vnd.wap.multipart.alternative" -> ?ENCODE_SHORT(16#26);
+ "application/xml" -> ?ENCODE_SHORT(16#27);
+ "text/xml" -> ?ENCODE_SHORT(16#28);
+ "application/vnd.wap.wbxml" -> ?ENCODE_SHORT(16#29);
+ "application/x-x968-cross-cert" -> ?ENCODE_SHORT(16#2A);
+ "application/x-x968-ca-cert" -> ?ENCODE_SHORT(16#2B);
+ "application/x-x968-user-cert" -> ?ENCODE_SHORT(16#2C);
+
+ %% WAP Version 1.2
+ "text/vnd.wap.si" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#2D);
+ "application/vnd.wap.sic" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#2E);
+ "text/vnd.wap.sl" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#2F);
+ "application/vnd.wap.slc" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#30);
+ "text/vnd.wap.co" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#31);
+ "application/vnd.wap.coc" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#32);
+ "application/vnd.wap.multipart.related" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#33);
+ "application/vnd.wap.sia" when Version >= ?WSP_12 ->
+ ?ENCODE_SHORT(16#34);
+ %% WAP Version 1.3
+ "text/vnd.wap.connectivity-xml" when Version >= ?WSP_13 ->
+ ?ENCODE_SHORT(16#35);
+ "application/vnd.wap.connectivity-wbxml" when Version >= ?WSP_13 ->
+ ?ENCODE_SHORT(16#36);
+ %% WAP Version 1.4
+ "application/pkcs7-mime" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#37);
+ "application/vnd.wap.hashed-certificate" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#38);
+ "application/vnd.wap.signed-certificate" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#39);
+ "application/vnd.wap.cert-response" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#3A);
+ "application/xhtml+xml" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#3B);
+ "application/wml+xml" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#3C);
+ "text/css" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#3D);
+ "application/vnd.wap.mms-message" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#3E);
+ "application/vnd.wap.rollover-certificate" when Version >= ?WSP_14 ->
+ ?ENCODE_SHORT(16#3F);
+ %% WAP Version 1.5
+ "application/vnd.wap.locc+wbxml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#40);
+ "application/vnd.wap.loc+xml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#41);
+ "application/vnd.syncml.dm+wbxml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#42);
+ "application/vnd.syncml.dm+xml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#43);
+ "application/vnd.syncml.notification" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#44);
+ "application/vnd.wap.xhtml+xml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#45);
+ "application/vnd.wv.csp.cir" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#46);
+ "application/vnd.oma.dd+xml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#47);
+ "application/vnd.oma.drm.message" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#48);
+ "application/vnd.oma.drm.content" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#49);
+ "application/vnd.oma.drm.rights+xml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#4A);
+ "application/vnd.oma.drm.rights+wbxml" when Version >= ?WSP_15 ->
+ ?ENCODE_SHORT(16#4B);
+ _ ->
+ encode_text_string(ContentType)
+ end.
+
+
+decode_well_known_media(Code, Version) when integer(Code) ->
+ case Code of
+ %% WSP_REGISTERED_CONTENT_TYPES
+ 16#0201 -> "application/vnd.uplanet.cacheop-wbxml";
+ 16#0202 -> "application/vnd.uplanet.signal";
+ 16#0203 -> "application/vnd.uplanet.alert-wbxml";
+ 16#0204 -> "application/vnd.uplanet.list-wbxml";
+ 16#0205 -> "application/vnd.uplanet.listcmd-wbxml";
+ 16#0206 -> "application/vnd.uplanet.channel-wbxml";
+ 16#0207 -> "application/vnd.uplanet.provisioning-status-uri";
+ 16#0208 -> "x-wap.multipart/vnd.uplanet.header-set";
+ 16#0209 -> "application/vnd.uplanet.bearer-choice-wbxml";
+ 16#020A -> "application/vnd.phonecom.mmc-wbxml";
+ 16#020B -> "application/vnd.nokia.syncset+wbxml";
+ 16#020C -> "image/x-up-wpng";
+ _ -> decode_constrained_media(Code, Version)
+ end;
+decode_well_known_media(Media, _Version) when list(Media) ->
+ Media;
+decode_well_known_media({short,_Data}, Version) ->
+ decode_well_known_media(d_long(data), Version). %% BUG HERE: Data
+
+
+decode_constrained_media(Code, _Version) when integer(Code) ->
+ case Code of
+ 16#00 -> "*/*";
+ 16#01 -> "text/*";
+ 16#02 -> "text/html";
+ 16#03 -> "text/plain";
+ 16#04 -> "text/x-hdml";
+ 16#05 -> "text/x-ttml";
+ 16#06 -> "text/x-vcalendar";
+ 16#07 -> "text/x-vcard";
+ 16#08 -> "text/vnd.wap.wml";
+ 16#09 -> "text/vnd.wap.wmlscript";
+ 16#0A -> "text/vnd.wap.wta-event";
+ 16#0B -> "multipart/*";
+ 16#0C -> "multipart/mixed";
+ 16#0D -> "multipart/form-data";
+ 16#0E -> "multipart/byterantes";
+ 16#0F -> "multipart/alternative";
+ 16#10 -> "application/*";
+ 16#11 -> "application/java-vm";
+ 16#12 -> "application/x-www-form-urlencoded";
+ 16#13 -> "application/x-hdmlc";
+ 16#14 -> "application/vnd.wap.wmlc";
+ 16#15 -> "application/vnd.wap.wmlscriptc";
+ 16#16 -> "application/vnd.wap.wta-eventc";
+ 16#17 -> "application/vnd.wap.uaprof";
+ 16#18 -> "application/vnd.wap.wtls-ca-certificate";
+ 16#19 -> "application/vnd.wap.wtls-user-certificate";
+ 16#1A -> "application/x-x509-ca-cert";
+ 16#1B -> "application/x-x509-user-cert";
+ 16#1C -> "image/*";
+ 16#1D -> "image/gif";
+ 16#1E -> "image/jpeg";
+ 16#1F -> "image/tiff";
+ 16#20 -> "image/png";
+ 16#21 -> "image/vnd.wap.wbmp";
+ 16#22 -> "application/vnd.wap.multipart.*";
+ 16#23 -> "application/vnd.wap.multipart.mixed";
+ 16#24 -> "application/vnd.wap.multipart.form-data";
+ 16#25 -> "application/vnd.wap.multipart.byteranges";
+ 16#26 -> "application/vnd.wap.multipart.alternative";
+ 16#27 -> "application/xml";
+ 16#28 -> "text/xml";
+ 16#29 -> "application/vnd.wap.wbxml";
+ 16#2A -> "application/x-x968-cross-cert";
+ 16#2B -> "application/x-x968-ca-cert";
+ 16#2C -> "application/x-x968-user-cert";
+ %% WAP Version 1.2
+ 16#2D -> "text/vnd.wap.si";
+ 16#2E -> "application/vnd.wap.sic";
+ 16#2F -> "text/vnd.wap.sl";
+ 16#30 -> "application/vnd.wap.slc";
+ 16#31 -> "text/vnd.wap.co";
+ 16#32 -> "application/vnd.wap.coc";
+ 16#33 -> "application/vnd.wap.multipart.related";
+ 16#34 -> "application/vnd.wap.sia";
+ %% WAP Version 1.3
+ 16#35 -> "text/vnd.wap.connectivity-xml";
+ 16#36 -> "application/vnd.wap.connectivity-wbxml";
+ %% WAP Version 1.4
+ 16#37 -> "application/pkcs7-mime";
+ 16#38 -> "application/vnd.wap.hashed-certificate";
+ 16#39 -> "application/vnd.wap.signed-certificate";
+ 16#3A -> "application/vnd.wap.cert-response";
+ 16#3B -> "application/xhtml+xml";
+ 16#3C -> "application/wml+xml";
+ 16#3D -> "text/css";
+ 16#3E -> "application/vnd.wap.mms-message";
+ 16#3F -> "application/vnd.wap.rollover-certificate";
+ %% WAP Version 1.5
+ 16#40 -> "application/vnd.wap.locc+wbxml";
+ 16#41 -> "application/vnd.wap.loc+xml";
+ 16#42 -> "application/vnd.syncml.dm+wbxml";
+ 16#43 -> "application/vnd.syncml.dm+xml";
+ 16#44 -> "application/vnd.syncml.notification";
+ 16#45 -> "application/vnd.wap.xhtml+xml";
+ 16#46 -> "application/vnd.wv.csp.cir";
+ 16#47 -> "application/vnd.oma.dd+xml";
+ 16#48 -> "application/vnd.oma.drm.message";
+ 16#49 -> "application/vnd.oma.drm.content";
+ 16#4A -> "application/vnd.oma.drm.rights+xml";
+ 16#4B -> "application/vnd.oma.drm.rights+wbxml"
+ end;
+decode_constrained_media(Media, _Version) when list(Media) ->
+ Media.
+
+
+%% Parse <integer> or <integer>.<integer>
+
+parse_version(Value) ->
+ case string:tokens(Value, ".") of
+ [Major,Minor] ->
+ {list_to_integer(Major), list_to_integer(Minor)};
+ [Major] ->
+ case catch list_to_integer(Major) of
+ {'EXIT', _} ->
+ Value;
+ V -> V
+ end
+ end.
+
+format_version({Major,Minor}) ->
+ [integer_to_list(Major),".",integer_to_list(Minor)];
+format_version(Major) when integer(Major) ->
+ integer_to_list(Major);
+format_version(Version) when list(Version) ->
+ Version.
+
+encode_version({Major,Minor}) ->
+ Ver = (((Major-1) band 16#7) bsl 4) bor (Minor band 16#f),
+ ?ENCODE_SHORT(Ver);
+encode_version(Major) when integer(Major) ->
+ Ver = ((Major band 16#7) bsl 4) bor 16#f,
+ ?ENCODE_SHORT(Ver);
+encode_version(Value) when list(Value) ->
+ encode_text_string(Value).
+
+
+decode_version(Value) when integer(Value) ->
+ Major = (Value bsr 4) band 16#7,
+ Minor = Value band 16#f,
+ if Minor == 16#f ->
+ Major;
+ true ->
+ {Major+1,Minor}
+ end;
+decode_version(Value) when list(Value) ->
+ Value.
+
+
+encode_mms_version({Major,Minor}) ->
+ Ver = ((Major band 16#7) bsl 4) bor (Minor band 16#f),
+ ?ENCODE_SHORT(Ver);
+encode_mms_version(Major) when integer(Major) ->
+ Ver = ((Major band 16#7) bsl 4) bor 16#f,
+ ?ENCODE_SHORT(Ver);
+encode_mms_version(Value) when list(Value) ->
+ encode_text_string(Value).
+
+
+decode_mms_version(Value) when integer(Value) ->
+ Major = (Value bsr 4) band 16#7,
+ Minor = Value band 16#f,
+ if Minor == 16#f ->
+ Major;
+ true ->
+ {Major,Minor}
+ end;
+decode_mms_version(Value) when list(Value) ->
+ Value.
+
+
+%%%
+%%% Basic data types
+%%%
+
+e_delta_seconds(Value) ->
+ encode_integer(Value).
+
+
+encode_integer(I) when integer(I), I >= 0 , I < 127 ->
+ ?ENCODE_SHORT(I);
+encode_integer(I) when integer(I) ->
+ encode_long_integer(I);
+encode_integer(List) when list(List) ->
+ encode_integer(list_to_integer(List)).
+
+decode_integer(Value) when integer(Value) ->
+ Value;
+decode_integer({short,Data}) ->
+ Sz = size(Data)*8,
+ <<Value:Sz>> = Data,
+ Value.
+
+encode_short_integer(I) ->
+ ?ENCODE_SHORT(I).
+
+encode_long_integer(I) when I >= 0 ->
+ MOInt = encode_multioctet_integer(I, []),
+ MOIntLen = length(MOInt),
+ list_to_binary([MOIntLen band 16#1f | MOInt]).
+
+encode_multioctet_integer(I,Acc) when I < 256 ->
+ [I | Acc];
+encode_multioctet_integer(I,Acc) ->
+ encode_multioctet_integer(I bsr 8, [(I band 16#ff) | Acc]).
+
+
+%% Integer-Value: Short-Integer | Long-Integer
+%% Short-Integer: <<1:Short:7>>
+%% Long-Integer: <<0-30, X:0-30>>
+%% return {Integer,Tail}
+d_integer_value(<<1:1,Integer:7,Tail/binary>>) ->
+ {Integer, Tail};
+d_integer_value(<<0:3,Len:5,Data/binary>>) when Len =/= 31 ->
+ Sz = Len*8,
+ <<Integer:Sz, Tail/binary>> = Data,
+ {Integer, Tail}.
+
+decode_short_integer(<<1:1,Septet:7,T100/binary>>) ->
+ {Septet, T100}.
+
+decode_long_integer(<<0:3,Len:5,Data/binary>>) when Len =/= 31 ->
+ Sz = Len*8,
+ <<Val:Sz, Tail/binary>> = Data,
+ {Val, Tail}.
+
+d_long(Data) ->
+ Sz = size(Data)*8,
+ <<Value:Sz>> = Data,
+ Value.
+
+
+encode_uri_value(Data) ->
+ encode_text_string(Data).
+
+decode_uri_value(Data) when list(Data) ->
+ Data.
+
+%% parse quoted string
+decode_quoted_string([$" | List]) ->
+ List.
+
+encode_quoted_string([$" | Value]) ->
+ case lists:reverse(Value) of
+ [$" | Value1] ->
+ <<$", (list_to_binary(lists:reverse(Value1)))/binary, 0>>;
+ _ ->
+ <<$", (list_to_binary(Value))/binary, 0>>
+ end;
+encode_quoted_string(Value) ->
+ <<$", (list_to_binary(Value))/binary, 0>>.
+
+
+
+decode_text_string(List) when list(List) ->
+ List;
+decode_text_string(Bin) when binary(Bin) ->
+ binary_to_list(Bin).
+
+
+
+encode_text_string(A) when atom(A) ->
+ encode_text_string(atom_to_list(A));
+encode_text_string([H|T]) when H >= 128 ->
+ <<(list_to_binary([127,H|T]))/binary,0>>;
+encode_text_string(S) ->
+ <<(list_to_binary(S))/binary,0>>.
+
+
+encode_text_value(undefined) ->
+ <<0>>;
+encode_text_value([$"|T]) ->
+ %% remove ending quote ?
+ <<34,(list_to_binary(T))/binary>>;
+encode_text_value(L) ->
+ encode_text_string(L).
+
+
+d_text_value(<<0,T100/binary>>) ->
+ { "", T100};
+d_text_value(<<34,_Tail/binary>>=Data) ->
+ d_text_string(Data);
+d_text_value(Data) ->
+ d_text_string(Data).
+
+
+d_text_string(<<127,Data/binary>>) -> %% Remove quote
+ d_text_string(Data,[]);
+d_text_string(Data) ->
+ d_text_string(Data,[]).
+
+d_text_string(<<0,Tail/binary>>,A) ->
+ {lists:reverse(A), Tail};
+d_text_string(<<C,Tail/binary>>,A) ->
+ d_text_string(Tail,[C|A]);
+d_text_string(<<>>, A) ->
+ {lists:reverse(A), <<>>}.
+
+
+d_q_value(<<0:1,Q:7,Tail/binary>>) ->
+ QVal =
+ if Q >= 1, Q =< 100 ->
+ lists:flatten(io_lib:format("0.~2..0w", [Q-1]));
+ Q >= 101, Q =< 1099 ->
+ lists:flatten(io_lib:format("0.~3..0w", [Q-100]));
+ true ->
+ io:format("Q-value to big ~w\n", [Q]),
+ "***"
+ end,
+ {QVal, Tail};
+d_q_value(<<1:1,Q1:7,0:1,Q0:7,Tail/binary>>) ->
+ Q = (Q1 bsl 7) bor Q0,
+ QVal =
+ if Q >= 1, Q =< 100 ->
+ lists:flatten(io_lib:format("0.~2..0w", [Q-1]));
+ Q >= 101, Q =< 1099 ->
+ lists:flatten(io_lib:format("0.~3..0w", [Q-100]));
+ true ->
+ io:format("Q-value to big ~w\n", [Q]),
+ "***"
+ end,
+ {QVal, Tail}.
+
+
+%%
+%% Decode uintvar
+%%
+d_uintvar(<<0:1,S0:7,T100/binary>>) ->
+ {S0, T100};
+d_uintvar(<<1:1,S1:7,0:1,S0:7,T100/binary>>) ->
+ {(S1 bsl 7) bor S0, T100};
+d_uintvar(<<1:1,S2:7,1:1,S1:7,0:1,S0:7,T100/binary>>) ->
+ {(S2 bsl 14) bor (S1 bsl 7) bor S0, T100};
+d_uintvar(<<1:1,S3:7,1:1,S2:7,1:1,S1:7,0:1,S0:7,T100/binary>>) ->
+ {(S3 bsl 21) bor (S2 bsl 14) bor (S1 bsl 7) bor S0, T100};
+d_uintvar(<<1:1,S4:7,1:1,S3:7,1:1,S2:7,1:1,S1:7,0:1,S0:7,T100/binary>>) ->
+ {(S4 bsl 28) bor (S3 bsl 21) bor (S2 bsl 14) bor (S1 bsl 7) bor S0, T100}.
+
+
+e_uintvar(I) when I < 128 -> <<I>>;
+e_uintvar(I) -> e_uintvar(I,[]).
+
+e_uintvar(0,Acc) ->
+ list_to_binary(Acc);
+e_uintvar(I,[]) ->
+ e_uintvar(I bsr 7, [I band 16#7f]);
+e_uintvar(I,Acc) ->
+ e_uintvar(I bsr 7, [16#80 bor (I band 16#7f) | Acc]).
+
+
+e_value(B) ->
+ Sz = size(B),
+ if Sz =< 30 ->
+ <<Sz:8, B/binary>>;
+ true ->
+ <<31:8, (e_uintvar(Sz))/binary, B/binary >>
+ end.
+
+e_value(B1,B2) ->
+ Sz = size(B1)+size(B2),
+ if Sz =< 30 ->
+ <<Sz:8, B1/binary, B2/binary>>;
+ true ->
+ <<31:8, (e_uintvar(Sz))/binary, B1/binary, B2/binary >>
+ end.
+
+e_value(B1,B2,B3) ->
+ Sz = size(B1)+size(B2)+size(B3),
+ if Sz =< 30 ->
+ <<Sz:8, B1/binary,B2/binary,B3/binary>>;
+ true ->
+ <<31:8,(e_uintvar(Sz))/binary,B1/binary,B2/binary,B3/binary>>
+ end.
+
+e_value(B1,B2,B3,B4) ->
+ Sz = size(B1)+size(B2)+size(B3)+size(B4),
+ if Sz =< 30 ->
+ <<Sz:8, B1/binary,B2/binary,B3/binary,B4/binary>>;
+ true ->
+ <<31:8,(e_uintvar(Sz))/binary,B1/binary,
+ B2/binary,B3/binary,B4/binary>>
+ end.
+
+%%
+%% Extened methods
+%%
+decode_extended_methods(<<PduType:8, Data/binary>>) ->
+ Type = decode_pdu_type(PduType),
+ {Method, Data1} = d_text_string(Data),
+ [{Type,Method} | decode_extended_methods(Data1)];
+decode_extended_methods(<<>>) ->
+ [].
+
+encode_extended_methods(Ms) ->
+ list_to_binary(encode_ext_methods(Ms)).
+
+encode_ext_methods([{Type,Method} | T]) ->
+ [ encode_pdu_type(Type), encode_text_string(Method) |
+ encode_ext_methods(T)];
+encode_ext_methods([]) ->
+ [].
+
+%%
+%% Address lists used by redirect-pdu and aliases-capability
+%%
+decode_address(D0) ->
+ [A] = decode_addresses(D0),
+ A.
+
+decode_addresses(D0) ->
+ case D0 of
+ <<1:1, 1:1,Len:6,B:8,P:16,Addr:Len/binary,D1/binary>> ->
+ [#wdp_address { bearer = B, address = Addr, portnum=P } |
+ decode_addresses(D1)];
+ <<1:1, 0:1,Len:6,B:8,Addr:Len/binary,D1/binary>> ->
+ [#wdp_address { bearer = B, address = Addr } |
+ decode_addresses(D1)];
+ <<0:1, 1:1,Len:6,P:16,Addr:Len/binary,D1/binary>> ->
+ [#wdp_address { portnum=P, address=Addr } |
+ decode_addresses(D1)];
+ <<0:1, 0:1,Len:6,Addr:Len/binary,D1/binary>> ->
+ [#wdp_address { address=Addr } |
+ decode_addresses(D1)];
+ <<>> ->
+ []
+ end.
+
+encode_addresses(As) ->
+ encode_addresses(As, []).
+
+encode_addresses([A|As], Acc) ->
+ encode_addresses(As, [encode_address(A)|Acc]);
+encode_addresses([], Acc) ->
+ list_to_binary(lists:reverse(Acc)).
+
+encode_address(#wdp_address { bearer = B, address = Addr, portnum = P }) ->
+ BAddr = if tuple(Addr) ->
+ list_to_binary(inet:ip_to_bytes(Addr));
+ binary(Addr) ->
+ Addr
+ end,
+ Len = size(BAddr),
+ if B == undefined, P == undefined ->
+ <<0:1, 0:1, Len:6, BAddr/binary>>;
+ B == undefined ->
+ <<0:1, 1:1, Len:6, P:16, BAddr/binary>>;
+ P == undefined ->
+ <<1:1, 0:1, Len:6, B:8, BAddr/binary>>;
+ true ->
+ <<1:1, 1:1, Len:6, B:8, P:16, BAddr/binary>>
+ end.
+
+
+
+
+-define(UNIX_TIME_OFFSET, 62167219200).
+
+d_date(Val) when integer(Val) ->
+ calendar:gregorian_seconds_to_datetime(Val+?UNIX_TIME_OFFSET);
+d_date({short,Data}) ->
+ Sz = size(Data)*8,
+ <<Sec:Sz>> = Data,
+ calendar:gregorian_seconds_to_datetime(Sec+?UNIX_TIME_OFFSET).
+
+e_date(DateTime) ->
+ Sec = calendar:datetime_to_gregorian_seconds(DateTime),
+ encode_long_integer(Sec - ?UNIX_TIME_OFFSET).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode http-date (RFC 2068). (MUST be send in RFC1123 date format)
+%% HTTP-date = rfc1123-date | rfc850-date | asctime-date
+%% rfc1123-date = wkday "," SP date1 SP time SP "GMT"
+%% rfc850-date = weekday "," SP date2 SP time SP "GMT"
+%% asctime-date = wkday SP date3 SP time SP 4DIGIT
+%%
+%% date1 = 2DIGIT SP month SP 4DIGIT
+%% ; day month year (e.g., 02 Jun 1982)
+%% date2 = 2DIGIT "-" month "-" 2DIGIT
+%% ; day-month-year (e.g., 02-Jun-82)
+%% date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
+%% ; month day (e.g., Jun 2)
+%%
+%% time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+%% ; 00:00:00 - 23:59:59
+%%
+%% wkday = "Mon" | "Tue" | "Wed"
+%% | "Thu" | "Fri" | "Sat" | "Sun"
+%%
+%%
+%% weekday = "Monday" | "Tuesday" | "Wednesday"
+%% | "Thursday" | "Friday" | "Saturday" | "Sunday"
+%%
+%% month = "Jan" | "Feb" | "Mar" | "Apr"
+%% | "May" | "Jun" | "Jul" | "Aug"
+%% | "Sep" | "Oct" | "Nov" | "Dec"
+%%
+%% decode date or crash!
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+parse_http_date(Date) ->
+ parse_hdate(tolower(Date)).
+
+parse_hdate([$m,$o,$n,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$t,$u,$e,$s,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$w,$e,$d,$n,$s,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$t,$h,$u,$r,$s,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$f,$r,$i,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$s,$a,$t,$u,$r,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$s,$u,$n,$d,$a,$y,$ | Cs]) -> date2(Cs);
+parse_hdate([$m,$o,$n,X | Cs]) -> date13(X,Cs);
+parse_hdate([$t,$u,$e,X | Cs]) -> date13(X,Cs);
+parse_hdate([$w,$e,$d,X | Cs]) -> date13(X,Cs);
+parse_hdate([$t,$h,$u,X | Cs]) -> date13(X,Cs);
+parse_hdate([$f,$r,$i,X | Cs]) -> date13(X,Cs);
+parse_hdate([$s,$a,$t,X | Cs]) -> date13(X,Cs);
+parse_hdate([$s,$u,$n,X | Cs]) -> date13(X,Cs).
+
+date13($ , Cs) -> date3(Cs);
+date13($,, [$ |Cs]) -> date1(Cs).
+
+%% date1
+date1([D1,D2,$ ,M1,M2,M3,$ ,Y1,Y2,Y3,Y4,$ | Cs]) ->
+ M = parse_month([M1,M2,M3]),
+ D = list_to_integer([D1,D2]),
+ Y = list_to_integer([Y1,Y2,Y3,Y4]),
+ {Time,[$ ,$g,$m,$t|Cs1]} = parse_time(Cs),
+ { {{Y,M,D},Time}, Cs1}.
+
+%% date2
+date2([D1,D2,$-,M1,M2,M3,$-,Y1,Y2 | Cs]) ->
+ M = parse_month([M1,M2,M3]),
+ D = list_to_integer([D1,D2]),
+ Y = 1900 + list_to_integer([Y1,Y2]),
+ {Time, [$ ,$g,$m,$t|Cs1]} = parse_time(Cs),
+ {{{Y,M,D}, Time}, Cs1}.
+
+%% date3
+date3([M1,M2,M3,$ ,D1,D2,$ | Cs]) ->
+ M = parse_month([M1,M2,M3]),
+ D = if D1 == $ -> list_to_integer([D2]);
+ true -> list_to_integer([D1,D2])
+ end,
+ {Time,[$ ,Y1,Y2,Y3,Y4|Cs1]} = parse_time(Cs),
+ Y = list_to_integer([Y1,Y2,Y3,Y4]),
+ { {{Y,M,D}, Time}, Cs1 }.
+
+%% decode lowercase month
+parse_month("jan") -> 1;
+parse_month("feb") -> 2;
+parse_month("mar") -> 3;
+parse_month("apr") -> 4;
+parse_month("may") -> 5;
+parse_month("jun") -> 6;
+parse_month("jul") -> 7;
+parse_month("aug") -> 8;
+parse_month("sep") -> 9;
+parse_month("oct") -> 10;
+parse_month("nov") -> 11;
+parse_month("dec") -> 12.
+
+%% decode time HH:MM:SS
+parse_time([H1,H2,$:,M1,M2,$:,S1,S2|Cs]) ->
+ { {list_to_integer([H1,H2]),
+ list_to_integer([M1,M2]),
+ list_to_integer([S1,S2]) }, Cs}.
+
+%% encode date into rfc1123-date (must be a GMT time!!!)
+fmt_date({{Y,M,D},{TH,TM,TS}}) ->
+ WkDay = case calendar:day_of_the_week({Y,M,D}) of
+ 1 -> "Mon";
+ 2 -> "Tue";
+ 3 -> "Wed";
+ 4 -> "Thu";
+ 5 -> "Fri";
+ 6 -> "Sat";
+ 7 -> "Sun"
+ end,
+ lists:flatten(io_lib:format("~s, ~2..0w ~s ~4..0w "
+ "~2..0w:~2..0w:~2..0w GMT",
+ [WkDay, D, fmt_month(M), Y, TH, TM, TS])).
+
+fmt_current_date() ->
+ fmt_date(calendar:universal_time()).
+
+%% decode lowercase month
+fmt_month(1) -> "Jan";
+fmt_month(2) -> "Feb";
+fmt_month(3) -> "Mar";
+fmt_month(4) -> "Apr";
+fmt_month(5) -> "May";
+fmt_month(6) -> "Jun";
+fmt_month(7) -> "Jul";
+fmt_month(8) -> "Aug";
+fmt_month(9) -> "Sep";
+fmt_month(10) -> "Oct";
+fmt_month(11) -> "Nov";
+fmt_month(12) -> "Dec".
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index e3e3f6d668..53b6f8c553 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.2.0
+DIALYZER_VSN = 2.4.2
diff --git a/lib/docbuilder/doc/src/docb_gen.xml b/lib/docbuilder/doc/src/docb_gen.xml
index 49eb79ae24..d4ebfd0f84 100644
--- a/lib/docbuilder/doc/src/docb_gen.xml
+++ b/lib/docbuilder/doc/src/docb_gen.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/docbuilder/doc/src/docb_transform.xml b/lib/docbuilder/doc/src/docb_transform.xml
index b8975e2698..06a04c8c02 100644
--- a/lib/docbuilder/doc/src/docb_transform.xml
+++ b/lib/docbuilder/doc/src/docb_transform.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2001</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/docbuilder/doc/src/docb_xml_check.xml b/lib/docbuilder/doc/src/docb_xml_check.xml
index 7ec456c014..eff4fc4342 100644
--- a/lib/docbuilder/doc/src/docb_xml_check.xml
+++ b/lib/docbuilder/doc/src/docb_xml_check.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/docbuilder/doc/src/docbuilder_app.xml b/lib/docbuilder/doc/src/docbuilder_app.xml
index a1df496258..58b8daf598 100644
--- a/lib/docbuilder/doc/src/docbuilder_app.xml
+++ b/lib/docbuilder/doc/src/docbuilder_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/docbuilder/doc/src/notes.xml b/lib/docbuilder/doc/src/notes.xml
index 725aae0a68..4b8c04f323 100644
--- a/lib/docbuilder/doc/src/notes.xml
+++ b/lib/docbuilder/doc/src/notes.xml
@@ -31,6 +31,37 @@
<p>This document describes the changes made to the DocBuilder
application.</p>
+<section><title>Docbuilder 0.9.8.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Fix compatibility issues with docbuilder for R11
+ documentation patches. </p>
+ <p>
+ Own Id: OTP-8946</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Docbuilder 0.9.8.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fixed problem with a centered table that was
+ transformed into an xml document which then produced
+ mis-formatted html. </p>
+ <p>
+ Own Id: OTP-8784</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Docbuilder 0.9.8.7</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/docbuilder/src/docb_edoc_xml_cb.erl b/lib/docbuilder/src/docb_edoc_xml_cb.erl
index 4dba843341..90491bc007 100644
--- a/lib/docbuilder/src/docb_edoc_xml_cb.erl
+++ b/lib/docbuilder/src/docb_edoc_xml_cb.erl
@@ -340,9 +340,7 @@ otp_xmlify_e(#xmlElement{name=code} = E) -> % 4)
[E#xmlElement{content=Content}]
end;
otp_xmlify_e(#xmlElement{name=Tag} = E) % 5a
- when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5;
- Tag==center;
- Tag==font ->
+ when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5 ->
Content = text_only(E#xmlElement.content),
[E#xmlElement{name=b, content=Content}];
otp_xmlify_e(#xmlElement{name=Tag} = E) % 5b-c)
@@ -354,21 +352,16 @@ otp_xmlify_e(#xmlElement{name=table} = E) -> % 6)
module ->
otp_xmlify_table(E#xmlElement.content);
overview ->
- case get_attrval(border, E) of
- "" -> % implies border="0"
- [{p, otp_xmlify_table(E#xmlElement.content)}];
- "0" ->
- [{p, otp_xmlify_table(E#xmlElement.content)}];
- _Val ->
- Content0 = otp_xmlify_e(E#xmlElement.content),
- Summary = #xmlText{value=get_attrval(summary, E)},
- TCaption = E#xmlElement{name=tcaption,
- attributes=[],
- content=[Summary]},
- Content = Content0 ++ [TCaption],
- [E#xmlElement{attributes=[], content=Content}]
- end
+ Content0 = otp_xmlify_e(E#xmlElement.content),
+ Summary = #xmlText{value=get_attrval(summary, E)},
+ TCaption = E#xmlElement{name=tcaption,
+ attributes=[],
+ content=[Summary]},
+ Content = Content0 ++ [TCaption],
+ [E#xmlElement{attributes=[], content=Content}]
end;
+otp_xmlify_e(#xmlElement{name=tbody} = E) ->
+ otp_xmlify_e(E#xmlElement.content);
otp_xmlify_e(#xmlElement{name=sup} = E) -> % 7)
Text = get_text(E),
[#xmlText{parents = E#xmlElement.parents,
@@ -478,31 +471,29 @@ otp_xmlify_a_href("#"++_ = Marker, Es0) -> % <seealso marker="#what">
otp_xmlify_a_href("http:"++_ = URL, Es0) -> % external URL
{URL, Es0};
otp_xmlify_a_href("OTPROOT"++AppRef, Es0) -> % <.. marker="App:FileRef
- case split(AppRef, "/") of
- [AppS, "doc", FileRef1] ->
- FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS),
- [#xmlText{value=Str0} = T] = Es0,
- Str = case split(Str0, "/") of
- %% //Application
- [AppS2] ->
- %% AppS2 can differ from AppS
- %% Example: xmerl/XMerL
- AppS2;
- [_AppS,ModRef] ->
- case split(ModRef, ":") of
- %% //Application/Module
- [Module] ->
- Module++"(3)";
- %% //Application/Module:Type()
- [_Module,_Type] ->
- ModRef
- end;
- %% //Application/Module:Function/Arity
- [_AppS,ModFunc,Arity] ->
- ModFunc++"/"++Arity
- end,
- {FileRef, [T#xmlText{value=Str}]}
- end;
+ [AppS, "doc", FileRef1] = split(AppRef, "/"),
+ FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS),
+ [#xmlText{value=Str0} = T] = Es0,
+ Str = case split(Str0, "/") of
+ %% //Application
+ [AppS2] ->
+ %% AppS2 can differ from AppS
+ %% Example: xmerl/XMerL
+ AppS2;
+ [_AppS,ModRef] ->
+ case split(ModRef, ":") of
+ %% //Application/Module
+ [Module] ->
+ Module++"(3)";
+ %% //Application/Module:Type()
+ [_Module,_Type] ->
+ ModRef
+ end;
+ %% //Application/Module:Function/Arity
+ [_AppS,ModFunc,Arity] ->
+ ModFunc++"/"++Arity
+ end,
+ {FileRef, [T#xmlText{value=Str}]};
otp_xmlify_a_href("../"++File, Es0) ->
%% Special case: This kind of relative path is used on some
%% places within i.e. EDoc and refers to a file within the same
@@ -639,13 +630,13 @@ otp_xmlify_table([#xmlText{} = E|Es]) ->
[E | otp_xmlify_table(Es)];
otp_xmlify_table([#xmlElement{name=tbody} = E|Es]) ->
otp_xmlify_table(E#xmlElement.content)++otp_xmlify_table(Es);
-otp_xmlify_table([#xmlElement{name=tr} = E|Es]) ->
+otp_xmlify_table([#xmlElement{name=tr, content=Content}|Es]) ->
%% Insert newlines between table rows
- otp_xmlify_table(E#xmlElement.content)++[{br,[]}]++otp_xmlify_table(Es);
-otp_xmlify_table([#xmlElement{name=th} = E|Es]) ->
- [{em, E#xmlElement.content} | otp_xmlify_table(Es)];
-otp_xmlify_table([#xmlElement{name=td} = E|Es]) ->
- otp_xmlify_e(E#xmlElement.content) ++ otp_xmlify_table(Es);
+ otp_xmlify_table(Content)++[{br,[]}]++otp_xmlify_table(Es);
+otp_xmlify_table([#xmlElement{name=th, content=Content}|Es]) ->
+ [{em, Content} | otp_xmlify_table(Es)];
+otp_xmlify_table([#xmlElement{name=td, content=Content}|Es]) ->
+ otp_xmlify_e(Content) ++ otp_xmlify_table(Es);
otp_xmlify_table([]) ->
[].
@@ -1155,8 +1146,8 @@ get_text(#xmlElement{content=[E]}) ->
%% text_only(Es) -> Ts
%% Takes a list of xmlElement and xmlText and return a lists of xmlText.
-text_only([#xmlElement{} = E |Es]) ->
- text_only(E#xmlElement.content) ++ text_only(Es);
+text_only([#xmlElement{content = Content}|Es]) ->
+ text_only(Content) ++ text_only(Es);
text_only([#xmlText{} = E |Es]) ->
[E | text_only(Es)];
text_only([]) ->
diff --git a/lib/docbuilder/src/docb_html.erl b/lib/docbuilder/src/docb_html.erl
index 9aea4c8a66..bdfc5ea876 100644
--- a/lib/docbuilder/src/docb_html.erl
+++ b/lib/docbuilder/src/docb_html.erl
@@ -283,16 +283,16 @@ rule([term|_], {_, [ID], _}, Opts) ->
ID,
"</strong></em> "]}, Opts};
TermList ->
- case lists:keysearch(ID, 1, TermList) of
+ case lists:keyfind(ID, 1, TermList) of
false ->
{{drop, ["<em><strong>", ID,
"</strong></em> "]},
Opts};
- {value, {ID, Name, _Description, _Resp}} ->
+ {ID, Name, _Description, _Resp} ->
{{drop, ["<em><strong>", Name,
"</strong></em> "]},
Opts};
- {value, {ID, Name, _Description}} ->
+ {ID, Name, _Description} ->
{{drop, [ "<em><strong>", Name,
"</strong></em> "]},
Opts}
@@ -306,16 +306,16 @@ rule([term|_], {_, [ID], _}, Opts) ->
TermList ->
PartApplication =
docb_util:lookup_option(part_application, Opts),
- case lists:keysearch(ID, 1, TermList) of
+ case lists:keyfind(ID, 1, TermList) of
false ->
{{drop, ["<a href=\"", PartApplication,
"_term.html#", ID, "\">", ID,
"</a> "]}, Opts};
- {value, {ID, Name, _Description, _Resp}} ->
+ {ID, Name, _Description, _Resp} ->
{{drop, ["<a href=\"", PartApplication,
"_term.html#", ID, "\">", Name,
"</a> "]}, Opts};
- {value, {ID, Name, _Description}} ->
+ {ID, Name, _Description} ->
{{drop, ["<a href=\"", PartApplication,
"_term.html#", ID, "\">", Name,
"</a> "]}, Opts}
@@ -331,17 +331,16 @@ rule([cite|_], {_, [ID], _}, Opts) ->
{{drop, ["<em><strong>", ID, "</strong></em> "]},
Opts};
CiteList ->
- case lists:keysearch(ID, 1, CiteList) of
+ case lists:keyfind(ID, 1, CiteList) of
false ->
{{drop,
["<em><strong>", ID, "</strong></em> "]},
Opts};
-
- {value, {ID, Name, _Description, _Resp}} ->
+ {ID, Name, _Description, _Resp} ->
{{drop, ["<em><strong>", Name,
"</strong></em> "]},
Opts};
- {value, {ID, Name, _Description}} ->
+ {ID, Name, _Description} ->
{{drop, ["<em><strong>", Name,
"</strong></em> "]},
Opts}
@@ -355,18 +354,18 @@ rule([cite|_], {_, [ID], _}, Opts) ->
CiteList ->
PartApp =
docb_util:lookup_option(part_application, Opts),
- case lists:keysearch(ID, 1, CiteList) of
+ case lists:keyfind(ID, 1, CiteList) of
false ->
{{drop, ["<a href=\"", PartApp,
"_cite.html#", ID, "\">", ID,
"</a> "]},
Opts};
- {value, {ID, Name, _Description, _Resp}} ->
+ {ID, Name, _Description, _Resp} ->
{{drop, ["<a href=\"", PartApp,
"_cite.html#", ID, "\">", Name,
"</a> "]},
Opts};
- {value, {ID, Name, _Description}} ->
+ {ID, Name, _Description} ->
{{drop, ["<a href=\"", PartApp,
"_cite.html#", ID, "\">", Name,
"</a> "]},
@@ -376,7 +375,7 @@ rule([cite|_], {_, [ID], _}, Opts) ->
end;
rule([code|_], {_, [Type], [{pcdata, _, Code}]}, Opts) ->
- case lists:member(Type,["ERL","C","NONE"]) of
+ case lists:member(Type, ["ERL","C","NONE"]) of
true ->
{{drop, ["\n<div class=\"example\"><pre>\n", docb_html_util:element_cdata_to_html(Code),
"\n</pre></div>\n"]}, Opts};
diff --git a/lib/docbuilder/src/docb_html_util.erl b/lib/docbuilder/src/docb_html_util.erl
index b2951706ea..02ce8b52a7 100644
--- a/lib/docbuilder/src/docb_html_util.erl
+++ b/lib/docbuilder/src/docb_html_util.erl
@@ -136,7 +136,6 @@ copy_pics(Src, Dest, Opts) ->
Dir = code:lib_dir(docbuilder),
InFile = filename:join([Dir, "etc", Src]),
OutFile = docb_util:outfile(Dest, "", Opts),
-
case filelib:last_modified(OutFile) of
0 -> % File doesn't exist
file:copy(InFile, OutFile);
@@ -156,10 +155,10 @@ copy_pics(Src, Dest, Opts) ->
%%--Resolve header data-------------------------------------------------
extract_header_data(Key, {header, [], List}) ->
- case lists:keysearch(Key, 1, List) of
- {value, {Key, [], []}} ->
+ case lists:keyfind(Key, 1, List) of
+ {Key, [], []} ->
"";
- {value, {Key, [], [{pcdata, [], Value}]}} ->
+ {Key, [], [{pcdata, [], Value}]} ->
pcdata_to_html(Value);
false ->
""
@@ -253,7 +252,7 @@ make_anchor_href(HRef) ->
{ok, [HRef]} ->
%% No `#' in HRef, i.e. only path
make_anchor_href(HRef, "");
- {ok, [Path, Fragment]}->
+ {ok, [Path, Fragment]} ->
make_anchor_href(Path, Fragment)
end.
@@ -398,10 +397,10 @@ count_sections([]) ->
%%--Make a ToC----------------------------------------------------------
format_toc(Toc) ->
- lists:map(fun({Number, Title}) ->
- [Number, " <a href = \"#", Number,
- "\">", Title, "</a><br/>\n"]
- end, Toc).
+ [format_toc1(T) || T <- Toc].
+
+format_toc1({Number, Title}) ->
+ [Number, " <a href = \"#", Number, "\">", Title, "</a><br/>\n"].
%%--Convert HTML ISO Latin 1 characters to ordinary characters----------
diff --git a/lib/docbuilder/src/docb_main.erl b/lib/docbuilder/src/docb_main.erl
index ef21f65557..4f5f035a65 100644
--- a/lib/docbuilder/src/docb_main.erl
+++ b/lib/docbuilder/src/docb_main.erl
@@ -34,14 +34,23 @@
%% Parses the source file File and transforms the result to html,
%% latex and/or man page format.
process(File, Opts) ->
-
- File1 = File ++ ".tmpconv",
- os:cmd("sed -e 's/xi:include[ \t]*href/include file/g' -e 's/xmlns:xi=\"http:\\/\\/www.w3.org\\/2001\\/XInclude\"//g' < " ++
- File ++ ".xml > " ++ File1 ++ ".xml"), %LATH
+
+ SrcType = docb_util:lookup_option(src_type, Opts),
+
+ File1 =
+ case SrcType of
+ ".xml" ->
+ FileTmp = File ++ ".tmpconv",
+ os:cmd("sed -e 's/xi:include[ \t]*href/include file/g' -e 's/xmlns:xi=\"http:\\/\\/www.w3.org\\/2001\\/XInclude\"//g' < " ++
+ File ++ ".xml > " ++ FileTmp ++ ".xml"),
+ FileTmp;
+ ".sgml" ->
+ File
+ end,
case parse1(File1, Opts) of
errors ->
- file:delete(File1 ++ ".xml"),
+ delete_tmp_file(SrcType, File1),
errors;
{ok, Tree} ->
From = element(1, Tree),
@@ -55,22 +64,28 @@ process(File, Opts) ->
%% If no target format is specified, assume HTML:
Tos = if
- Tos0==[] -> [html];
+ Tos0 =:= [] -> [html];
true -> Tos0
end,
Result = [transform(From, To, Opts, File, Tree)||To <- Tos],
case lists:member(transformation_error,Result) of
true ->
- file:delete(File1 ++ ".xml"),
+ delete_tmp_file(SrcType, File1),
errors;
_ ->
- file:delete(File1 ++ ".xml"),
+ delete_tmp_file(SrcType, File1),
ok
end
end.
+
+delete_tmp_file(".xml", File) ->
+ file:delete(File ++ ".xml");
+delete_tmp_file(_, _) ->
+ ok.
+
%%----------------------------------------------------------------------
%% parse(File, Opts) -> {ok, Tree} | errors
@@ -327,12 +342,8 @@ verify(Tree) -> verify(Tree, [], 1).
verify({pcdata, Optional, _}, Path, Level) ->
verify_optional(Optional, Path, Level);
verify({Tag, Optional, Args}, Path, Level) when is_list(Args) ->
- case verify_optional(Optional, Path, Level) of
- true ->
- verify_list(Args, [Tag|Path], Level);
- false ->
- false
- end;
+ verify_optional(Optional, Path, Level)
+ andalso verify_list(Args, [Tag|Path], Level);
verify(Other, Path, Level) ->
verify_error(Other, Path, Level).
@@ -342,12 +353,7 @@ verify_error(X, Path, Level) ->
false.
verify_list([H|T], Path, Level) ->
- case verify(H, Path, Level) of
- true ->
- verify_list(T, Path, Level +1);
- false ->
- false
- end;
+ verify(H, Path, Level) andalso verify_list(T, Path, Level + 1);
verify_list([], _, _) ->
true.
@@ -419,7 +425,7 @@ edit1_list(_Tag, [], _Op) ->
%% Actual transformation of tree structure to desired format.
transform(From, To, Opts, File, Tree) ->
Filter = if
- To==html; To==kwic ->
+ To =:= html; To =:= kwic ->
list_to_atom("docb_tr_" ++ atom_to_list(From) ++
[$2|atom_to_list(To)]);
true ->
@@ -427,7 +433,7 @@ transform(From, To, Opts, File, Tree) ->
[$2|atom_to_list(To)])
end,
- case catch apply(Filter, transform, [File, Tree, Opts]) of
+ case catch Filter:transform(File, Tree, Opts) of
%% R5C
{'EXIT', {undef, [{Filter, transform, [File, Tree, Opts]}|_]}}->
@@ -459,9 +465,9 @@ transform(From, To, Opts, File, Tree) ->
finish_transform(Tree, File, Opts, Filter) ->
{Str, NewOpts} = pp(Tree, [], 1, Filter, Opts),
Extension =
- case catch apply(Filter, extension, [NewOpts]) of
+ case catch Filter:extension(NewOpts) of
{'EXIT', _} ->
- apply(Filter, extension, []);
+ Filter:extension();
Others ->
Others
end,
@@ -606,7 +612,7 @@ include_all(Fd) ->
eof ->
[];
ListOfChars ->
- lists:append(ListOfChars, include_all(Fd))
+ ListOfChars ++ include_all(Fd)
end.
extract(File, Fd, StartTag, StopTag, State) ->
diff --git a/lib/docbuilder/src/docb_pretty_format.erl b/lib/docbuilder/src/docb_pretty_format.erl
index 0c4fb0507b..25dcd8987b 100644
--- a/lib/docbuilder/src/docb_pretty_format.erl
+++ b/lib/docbuilder/src/docb_pretty_format.erl
@@ -47,7 +47,7 @@ term(Term) ->
%% the next line to need an "extra" indent!).
term([], Indent) ->
{Indent, [$[,$]]};
-term(L, Indent) when list(L) ->
+term(L, Indent) when is_list(L) ->
case is_string(L) of
true ->
{Indent, io_lib:write_string(L)};
@@ -59,7 +59,7 @@ term(L, Indent) when list(L) ->
write_simple_list(L, Indent)
end
end;
-term(T, Indent) when tuple(T) ->
+term(T, Indent) when is_tuple(T) ->
case complex_tuple(T) of
true ->
write_complex_tuple(T, Indent);
diff --git a/lib/docbuilder/src/docb_tr_application2html.erl b/lib/docbuilder/src/docb_tr_application2html.erl
index 4084cfe6ba..d8cb214d0a 100644
--- a/lib/docbuilder/src/docb_tr_application2html.erl
+++ b/lib/docbuilder/src/docb_tr_application2html.erl
@@ -119,8 +119,8 @@ transform(File, {application, _Attrs, [Header|Rest]}, Opts0) ->
case docb_main:parse1("fascicules", Opts0) of
{ok, Parse} ->
FascData = get_fasc_data(Parse),
- case lists:keysearch(File, 1, FascData) of
- {value, {_, _, "YES", _}} ->
+ case lists:keyfind(File, 1, FascData) of
+ {_, _, "YES", _} ->
OrigFile =
docb_util:outfile(File++"_frame",
".html", Opts0),
@@ -167,7 +167,7 @@ concat_files([File|Rest], Body, Opts) ->
%% Remove the reference manual header
[{Ref, [], [_Hdr| NewBody]}] = NewParse,
RefParse = [{Ref, [], NewBody}],
- lists:append(Body, concat_files(Rest, RefParse, Opts));
+ Body ++ concat_files(Rest, RefParse, Opts);
errors ->
errors
end;
@@ -216,7 +216,7 @@ make_toc([{lib, Attrs, More}|Rest]) ->
make_toc([{com, Attrs, More}|Rest]) ->
[{com, Attrs, More}|make_toc(Rest)];
make_toc([{_Tag, _Attrs, More}|Rest]) ->
- lists:append(make_toc(More), make_toc(Rest)).
+ make_toc(More) ++ make_toc(Rest).
rule([module|_], {_, [File], _}) ->
{"<small><a target=\"document\" href=\"" ++
@@ -280,9 +280,7 @@ get_fasc_data({fascicules, _, Fascs}) ->
Fascs).
get_avals(Atts) ->
- lists:map(fun(Tuple) ->
- element(3, Tuple) end,
- Atts).
+ [element(3, Tuple) || Tuple <- Atts].
get_pc_text([{pcdata, _, Text}]) ->
Text.
diff --git a/lib/docbuilder/src/docb_tr_cite2html.erl b/lib/docbuilder/src/docb_tr_cite2html.erl
index 4ecbfa4e91..77f1c4e636 100644
--- a/lib/docbuilder/src/docb_tr_cite2html.erl
+++ b/lib/docbuilder/src/docb_tr_cite2html.erl
@@ -39,24 +39,22 @@ purge_body([], _) ->
purge_body([{pcdata,_Attrs,_More}|Rest], CiteList) ->
purge_body(Rest, CiteList);
purge_body([{cite,[{"ID","CDATA",ID}],More}|Rest], CiteList) ->
- case lists:keysearch(ID, 1, CiteList) of
+ case lists:keyfind(ID, 1, CiteList) of
false ->
[{cite, [{"NAME","CDATA",ID}, {"ID","CDATA",ID}], More}|
purge_body(Rest, CiteList)];
- {value, {ID, Name, _Description, _Responsible}} ->
+ {ID, Name, _Description, _Responsible} ->
[{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}|
purge_body(Rest, CiteList)];
- {value, {ID, Name, _Description}} ->
+ {ID, Name, _Description} ->
[{cite, [{"NAME","CDATA",Name}, {"ID","CDATA",ID}], More}|
purge_body(Rest, CiteList)]
end;
purge_body([{_Tag,_Attrs,More}|Rest], CiteList) ->
- lists:append(purge_body(More, CiteList),
- purge_body(Rest, CiteList)).
+ purge_body(More, CiteList) ++ purge_body(Rest, CiteList).
rule([header|_], _) ->
{drop, ""};
-
rule(_, _) ->
{drop, ""}.
@@ -91,19 +89,19 @@ rule([cite|_], {_,[Name,ID], [{citedef,[],[{pcdata,[],Def}]}]}, Opts) ->
false -> [];
Value -> Value
end,
- case lists:keysearch(ID, 1, CiteList) of
+ case lists:keyfind(ID, 1, CiteList) of
false ->
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts};
- {value, {ID, Name, Description, _Responsible}} ->
+ {ID, Name, Description, _Responsible} ->
docb_util:message(warning,
"Global cite ~s overriding local", [ID]),
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"},
Opts};
- {value, {ID, Name, Description}} ->
+ {ID, Name, Description} ->
docb_util:message(warning,
"Global cite ~s overriding local", [ID]),
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
@@ -117,19 +115,19 @@ rule([cite|_], {_,[Name,ID],_}, Opts) ->
false -> [];
Value -> Value
end,
- case lists:keysearch(ID, 1, CiteList) of
+ case lists:keyfind(ID, 1, CiteList) of
false ->
docb_util:message(error,
"The cite ~s has no definition", [ID]),
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++
"??" ++ "\n</dd>\n"}, Opts};
- {value, {ID, Name, Description, _Responsible}} ->
+ {ID, Name, Description, _Responsible} ->
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"},
Opts};
- {value, {ID, Name, Description}} ->
+ {ID, Name, Description} ->
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}
diff --git a/lib/docbuilder/src/docb_tr_index2html.erl b/lib/docbuilder/src/docb_tr_index2html.erl
index bbf419f3ef..312342add2 100644
--- a/lib/docbuilder/src/docb_tr_index2html.erl
+++ b/lib/docbuilder/src/docb_tr_index2html.erl
@@ -73,9 +73,7 @@ prune_flat([], _) ->
[].
keep_pcdata(Trees) ->
- lists:filter(fun({pcdata, _, _}) -> true;
- (_) -> false
- end, Trees).
+ [T || T = {pcdata, _, _} <- Trees].
new_trees(FileFuncs) ->
Files0 = [{File, RefType} || {File, RefType, _} <- FileFuncs],
diff --git a/lib/docbuilder/src/docb_tr_part2html.erl b/lib/docbuilder/src/docb_tr_part2html.erl
index dd44c4a8df..30befe8432 100644
--- a/lib/docbuilder/src/docb_tr_part2html.erl
+++ b/lib/docbuilder/src/docb_tr_part2html.erl
@@ -93,8 +93,8 @@ transform(File, {part, _Attrs, [Header| Rest]}, Opts0) ->
case docb_main:parse1("fascicules", Opts0) of
{ok, Parse} ->
FascData = get_fasc_data(Parse),
- case lists:keysearch(File, 1, FascData) of
- {value, {_, _, "YES", _}} ->
+ case lists:keyfind(File, 1, FascData) of
+ {_, _, "YES", _} ->
OrigFile =
docb_util:outfile(File++"_frame",
".html", Opts0),
@@ -137,7 +137,7 @@ concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) ->
case docb_main:parse1(File, Opts) of
{ok, Parse} ->
{TopTag, Attrs, [Header = {header, _, HeaderContents} | More]} = Parse,
- {value,{title,_,Title}} = lists:keysearch(title,1,HeaderContents),
+ {title,_,Title} = lists:keyfind(title,1,HeaderContents),
NewMore = [{section, [], [{title, [], Title}| More]}],
NewParse = {TopTag, Attrs, [Header| NewMore]},
if
@@ -156,9 +156,8 @@ concat_files([File | Rest], Body, ChLevel, Opts, TP, TOpts, Ext) ->
docb_html_util:number(NewParse,
integer_to_list(ChLevel), File),
{_, [], [_| NewBody]} = NumberTree,
- lists:append(Body,
- concat_files(Rest, NewBody, ChLevel+1, Opts,
- TP, TOpts, Ext));
+ Body ++ concat_files(Rest, NewBody, ChLevel+1, Opts,
+ TP, TOpts, Ext);
errors ->
throw({error,"Parse error when building chapter "++File})
end;
@@ -232,9 +231,7 @@ get_fasc_data({fascicules, _, Fascs}) ->
Fascs).
get_avals(Atts) ->
- lists:map(fun(Tuple) ->
- element(3, Tuple) end,
- Atts).
+ [element(3, Tuple) || Tuple <- Atts].
get_pc_text([{pcdata, _, Text}]) ->
Text.
diff --git a/lib/docbuilder/src/docb_tr_term2html.erl b/lib/docbuilder/src/docb_tr_term2html.erl
index 0a993cebb1..a3c4a5312a 100644
--- a/lib/docbuilder/src/docb_tr_term2html.erl
+++ b/lib/docbuilder/src/docb_tr_term2html.erl
@@ -39,24 +39,22 @@ purge_body([], _) ->
purge_body([{pcdata,_Attrs,_More}|Rest], TermList) ->
purge_body(Rest, TermList);
purge_body([{term,[{"ID","CDATA",ID}],More}|Rest], TermList) ->
- case lists:keysearch(ID, 1, TermList) of
+ case lists:keyfind(ID, 1, TermList) of
false ->
[{term,[{"NAME","CDATA",ID},{"ID","CDATA",ID}],More}|
purge_body(Rest, TermList)];
- {value, {ID, Name, _Description, _Responsible}} ->
+ {ID, Name, _Description, _Responsible} ->
[{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}|
purge_body(Rest, TermList)];
- {value, {ID, Name, _Description}} ->
+ {ID, Name, _Description} ->
[{term,[{"NAME","CDATA",Name},{"ID","CDATA",ID}],More}|
purge_body(Rest, TermList)]
end;
purge_body([{_Tag,_Attrs,More}|Rest], TermList) ->
- lists:append(purge_body(More, TermList),
- purge_body(Rest, TermList)).
+ purge_body(More, TermList) ++ purge_body(Rest, TermList).
rule([header|_], _) ->
{drop, ""};
-
rule(_, _) ->
{drop, ""}.
@@ -82,19 +80,19 @@ rule([term|_], {_,[Name,ID],[{termdef,[],[{pcdata,[],Def}]}]}, Opts) ->
false -> [];
Value -> Value
end,
- case lists:keysearch(ID, 1, TermList) of
+ case lists:keyfind(ID, 1, TermList) of
false ->
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ ID ++ "</strong></a>\n</dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Def) ++ "\n</dd>\n"}, Opts};
- {value, {ID, Name, Description, _Responsible}} ->
+ {ID, Name, Description, _Responsible} ->
docb_util:message(warning,
"Global term ~s overriding local", [ID]),
{{drop,"\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"},
Opts};
- {value, {ID, Name, Description}} ->
+ {ID, Name, Description} ->
docb_util:message(warning,
"Global term ~s overriding local", [ID]),
{{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++
@@ -107,19 +105,19 @@ rule([term|_], {_,[Name,ID],_}, Opts) ->
false -> [];
Value -> Value
end,
- case lists:keysearch(ID, 1, TermList) of
+ case lists:keyfind(ID, 1, TermList) of
false ->
docb_util:message(error,
"The term ~s has no definition", [ID]),
{{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ ID ++ "</strong></a></dt>\n<dd>" ++
"??" ++ "\n</dd>\n"}, Opts};
- {value, {ID, Name, Description, _Responsible}} ->
+ {ID, Name, Description, _Responsible} ->
{{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"},
Opts};
- {value, {ID, Name, Description}} ->
+ {ID, Name, Description} ->
{{drop, "\n<dt><a name=\"" ++ ID ++ "\">" ++
"<strong>" ++ Name ++ "</strong></a></dt>\n<dd>" ++
docb_html_util:pcdata_to_html(Description) ++ "\n</dd>\n"}, Opts}
diff --git a/lib/docbuilder/src/docb_transform.erl b/lib/docbuilder/src/docb_transform.erl
index a432038adf..9c7561b07b 100644
--- a/lib/docbuilder/src/docb_transform.erl
+++ b/lib/docbuilder/src/docb_transform.erl
@@ -135,8 +135,8 @@ parse([Opt | _RawOpts], _Opts) ->
get_defs(Type, File, Opts) ->
Key = {defs,Type},
{PrevDefs, Opts2} =
- case lists:keysearch(Key, 1, Opts) of
- {value, {_, Defs0}} ->
+ case lists:keyfind(Key, 1, Opts) of
+ {_, Defs0} ->
{Defs0, lists:keydelete(Key, 1, Opts)};
false ->
{[], Opts}
diff --git a/lib/docbuilder/src/docb_util.erl b/lib/docbuilder/src/docb_util.erl
index 59673ef3a4..9b2eec7733 100644
--- a/lib/docbuilder/src/docb_util.erl
+++ b/lib/docbuilder/src/docb_util.erl
@@ -61,7 +61,7 @@ html_snippet(What, Opts) ->
case lookup_option(html_mod, Opts) of
false -> "";
Module ->
- case catch apply(Module, What, []) of
+ case catch Module:What() of
HTML when is_list(HTML) ->
HTML;
{'EXIT', {undef, _}} ->
@@ -82,7 +82,7 @@ html_snippet(What, Arg, Opts) ->
case lookup_option(html_mod, Opts) of
false -> "";
Module ->
- case catch apply(Module, What, [Arg]) of
+ case catch Module:What(Arg) of
HTML when is_list(HTML) ->
HTML;
{'EXIT', {undef, _}} ->
@@ -106,8 +106,8 @@ html_snippet(What, Arg, Opts) ->
%% lookup_option(Opt, Opts) -> Value | false
lookup_option(Opt, Opts) ->
- case lists:keysearch(Opt, 1, Opts) of
- {value, {Opt,Value}} -> Value;
+ case lists:keyfind(Opt, 1, Opts) of
+ {Opt,Value} -> Value;
false -> false
end.
diff --git a/lib/docbuilder/src/docb_xmerl_tree_cb.erl b/lib/docbuilder/src/docb_xmerl_tree_cb.erl
index d57f55bff8..bc62069230 100644
--- a/lib/docbuilder/src/docb_xmerl_tree_cb.erl
+++ b/lib/docbuilder/src/docb_xmerl_tree_cb.erl
@@ -188,8 +188,8 @@ attrs(DTD, Tag, GivenAttrs) ->
merge_attrs(Tag, default_attrs(DTD, Tag), GivenAttrs).
merge_attrs(Tag, [{NameA, Type, DefVal}|Default], GivenAttrs) ->
- Val = case lists:keysearch(NameA, #xmlAttribute.name, GivenAttrs) of
- {value, #xmlAttribute{value=Val0}} -> Val0;
+ Val = case lists:keyfind(NameA, #xmlAttribute.name, GivenAttrs) of
+ #xmlAttribute{value=Val0} -> Val0;
false -> DefVal
end,
Attr = {attr_name(NameA), Type, attr_val(Type, Val)},
diff --git a/lib/docbuilder/test/Makefile b/lib/docbuilder/test/Makefile
index 080479ee71..53dff193dc 100644
--- a/lib/docbuilder/test/Makefile
+++ b/lib/docbuilder/test/Makefile
@@ -71,8 +71,8 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(SPEC_FILES) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) $(SPEC_FILES) docb.cover $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/docbuilder/test/docb.cover b/lib/docbuilder/test/docb.cover
new file mode 100644
index 0000000000..80bab6eba7
--- /dev/null
+++ b/lib/docbuilder/test/docb.cover
@@ -0,0 +1,2 @@
+{incl_app,docbuilder,details}
+
diff --git a/lib/docbuilder/test/docb_SUITE.erl b/lib/docbuilder/test/docb_SUITE.erl
index c871130521..d286824539 100644
--- a/lib/docbuilder/test/docb_SUITE.erl
+++ b/lib/docbuilder/test/docb_SUITE.erl
@@ -17,13 +17,32 @@
%%
-module(docb_SUITE).
--export([all/1,html/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2,html/1]).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
-all(suite) -> [html].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+[html].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
html(suite) -> [];
html(Config) when is_list(Config) ->
diff --git a/lib/docbuilder/vsn.mk b/lib/docbuilder/vsn.mk
index 2852ebcc8b..1209b80d94 100644
--- a/lib/docbuilder/vsn.mk
+++ b/lib/docbuilder/vsn.mk
@@ -1,14 +1 @@
-DOCB_VSN = 0.9.8.7
-
-TICKETS = OTP-8343
-
-TICKETS_0.9.8.6 = OTP-8201
-
-TICKETS_0.9.8.5 = OTP-7851
-
-TICKETS_0.9.8.4 = OTP-7236
-
-TICKETS_0.9.8.1 = OTP-7236
-
-
-
+DOCB_VSN = 0.9.8.9
diff --git a/lib/edoc/doc/overview.edoc b/lib/edoc/doc/overview.edoc
index 9b25c17b1f..bd603b7a13 100644
--- a/lib/edoc/doc/overview.edoc
+++ b/lib/edoc/doc/overview.edoc
@@ -205,8 +205,12 @@ The following tags can be used anywhere within a module:
the text. See {@section Type specifications} for syntax and
examples.
All data type descriptions are placed in a separate section of
- the documentation, regardless of where the tags occur.</dd>
+ the documentation, regardless of where the tags occur.
+ Instead of specifying the complete type alias in an EDoc
+ documentation comment, type definitions from the actual
+ Erlang code can be re-used for documentation.
+ See {@section Type specifications} for examples.</dd>
</dl>
@@ -405,7 +409,12 @@ The following tags can be used before a function definition:
included in the specification, it must match the name in the
actual code. When parameter names are not given in the
specification, suitable names will be taken from the source
- code if possible, and otherwise synthesized.</dd>
+ code if possible, and otherwise synthesized.
+
+ Instead of specifying the complete function type in an EDoc
+ documentation comment, specifications from the actual
+ Erlang code can be re-used for documentation.
+ See {@section Type specifications} for examples.</dd>
<dt><a name="ftag-throws">`@throws'</a></dt>
<dd>Specifies which types of terms may be thrown by the
@@ -763,6 +772,17 @@ following escape sequences may be used: <dl>
=== Function specifications ===
+<note>Although the syntax described in the following can still be used
+for specifying functions we recommend that Erlang specifications as
+described in <seealso marker="doc/reference_manual:typespec"> Types
+and Function Specification</seealso> should be added to the source
+code instead. This way the analyses of <seealso
+marker="dialyzer:dialyzer">Dialyzer</seealso>'s can be utilized in the
+process of keeping the documentation consistent and up-to-date.
+Erlang specifications will be used unless there is also a function
+specification (a `@spec' tag followed by a type) with the same name.
+</note>
+
The following grammar describes the form of the specifications following
a `@spec' tag. A '`?'' suffix implies that the element is optional.
Function types have higher precedence than union types; e.g., "`(atom())
@@ -818,16 +838,51 @@ not as `(atom()) -> (atom() | integer())'.
<br/>| Atom
<br/>| Integer
<br/>| Float
+ <br/>| Integer ".." Integer
<br/>| FunType
+ <br/>| "fun(" FunType ")"
+ <br/>| "fun(...)"
<br/>| "{" UnionTypes? "}"
+ <br/>| "#" Atom "{" Fields? "}"
<br/>| "[" "]"
<br/>| "[" UnionType "]"
+ <br/>| "[" UnionType "," "..." "]"
<br/>| "(" UnionType ")"
+ <br/>| BinType
<br/>| TypeName "(" UnionTypes? ")"
<br/>| ModuleName ":" TypeName "(" UnionTypes? ")"
<br/>| "//" AppName "/" ModuleName ":" TypeName "(" UnionTypes? ")"</code></td>
</tr>
<tr>
+ <td><code>Fields</code></td>
+ <td>::=</td>
+ <td><code>Field
+ <br/>| Fields "," Fields</code></td>
+ </tr>
+ <tr>
+ <td><code>Field</code></td>
+ <td>::=</td>
+ <td><code>Atom "=" UnionList</code></td>
+ </tr>
+ <tr>
+ <td><code>BinType</code></td>
+ <td>::=</td>
+ <td><code>"&lt;&lt;&gt;&gt;"
+ <br/>| "&lt;&lt;" BaseType "&gt;&gt;"
+ <br/>| "&lt;&lt;" UnitType "&gt;&gt;"
+ <br/>| "&lt;&lt;" BaseType "," UnitType "&gt;&gt;"</code></td>
+ </tr>
+ <tr>
+ <td><code>BaseType</code></td>
+ <td>::=</td>
+ <td><code>"_" ":" Integer</code></td>
+ </tr>
+ <tr>
+ <td><code>UnitType</code></td>
+ <td>::=</td>
+ <td><code>"_" ":" "_" "*" Integer</code></td>
+ </tr>
+ <tr>
<td><code>TypeVariable</code></td>
<td>::=</td>
<td><code>Variable</code></td>
@@ -858,7 +913,7 @@ not as `(atom()) -> (atom() | integer())'.
<tr>
<td><code>Def</code></td>
<td>::=</td>
- <td><code>TypeVariable "=" UnionType
+ <td><code>TypeVariable "=" UnionList
<br/>| TypeName "(" TypeVariables? ")" "=" UnionType</code></td>
</tr>
<tr>
@@ -873,6 +928,9 @@ not as `(atom()) -> (atom() | integer())'.
Examples:
```
+ -spec my_function(X :: integer()) -> integer().
+ %% @doc Creates ...'''
+```
%% @spec my_function(X::integer()) -> integer()'''
```
%% @spec (X::integer()) -> integer()'''
@@ -895,6 +953,8 @@ Examples:
```
%% @spec close(graphics:window()) -> ok'''
+The first example shows the recommended way of specifying functions.
+
In the above examples, `X', `A', `B',
and `File' are parameter names, used for referring to the
parameters from the documentation text. The <em>type variables</em>
@@ -930,6 +990,13 @@ contain any annotations at all.
=== Type definitions ===
+<note>Although the syntax described in the following can still be used
+for specifying types we recommend that Erlang types as described in
+<seealso marker="doc/reference_manual:typespec"> Types and Function
+Specification</seealso> should be added to the source code instead.
+Erlang types will be used unless there is a type alias with the same
+name.</note>
+
The following grammar (see above for auxiliary definitions) describes
the form of the definitions that may follow a `@type' tag:
@@ -939,7 +1006,7 @@ the form of the definitions that may follow a `@type' tag:
<td><code>Typedef</code></td>
<td>::=</td>
<td><code>TypeName "(" TypeVariables? ")" DefList?
- <br/>| TypeName "(" TypeVariables? ")" "=" UnionType DefList?</code></td>
+ <br/>| TypeName "(" TypeVariables? ")" "=" UnionList DefList?</code></td>
</tr>
</tbody>
</table>
@@ -947,6 +1014,11 @@ the form of the definitions that may follow a `@type' tag:
(For a truly abstract data type, no equivalence is specified.) The main
definition may be followed by additional local definitions. Examples:
```
+ -type my_list(X) :: [X]. %% A special kind of lists ...'''
+```
+ -opaque another_list(X) :: [X].
+ %% another_list() is a kind of list...'''
+```
%% @type myList(X). A special kind of lists ...'''
```
%% @type filename() = string(). Atoms not allowed!'''
@@ -955,6 +1027,7 @@ definition may be followed by additional local definitions. Examples:
%% A = term().
%% A kind of wrapper type thingy.'''
+The first two examples show the recommended way of specifying types.
=== Pre-defined data types ===
@@ -962,24 +1035,42 @@ The following data types are predefined by EDoc, and may not be
redefined:
```
any()
+ arity()
atom()
binary()
- bool()
+ bitstring()
+ bool() (allowed, but use boolean() instead)
+ boolean()
+ byte()
char()
cons()
deep_string()
float()
function()
integer()
+ iodata()
+ iolist()
list()
+ maybe_improper_list()
+ mfa()
+ module()
nil()
+ neg_integer()
+ node()
+ non_neg_integer()
+ nonempty_improper_list()
+ nonempty_list()
+ nonempty_maybe_improper_list()
+ nonempty_string()
none()
number()
pid()
port()
+ pos_integer()
reference()
string()
term()
+ timeout()
tuple()
'''
Details:
@@ -991,7 +1082,7 @@ Details:
`integer()', `pid()', `port()'
and `reference()' are primitive data types of
the Erlang programming language.</li>
- <li>`bool()' is the subset of `atom()' consisting
+ <li>`boolean()' is the subset of `atom()' consisting
of the atoms `true' and `false'.</li>
<li>`char()' is a subset of
`integer()' representing character codes.</li>
diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile
index 748691d173..5ee0096f0f 100644
--- a/lib/edoc/doc/src/Makefile
+++ b/lib/edoc/doc/src/Makefile
@@ -105,7 +105,7 @@ man: $(MAN3_FILES)
$(XML_REF3_FILES):
escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EDOC_VSN) -i $(ERL_TOP)/lib/edoc/include $(SRC_DIR)/$(@:%.xml=%.erl)
-$(XML_CHAPTER_FILES):
+$(XML_CHAPTER_FILES): ../overview.edoc
escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(EDOC_VSN) -chapter ../overview.edoc
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
diff --git a/lib/edoc/doc/src/notes.xml b/lib/edoc/doc/src/notes.xml
index 74fa2d3ab6..c18a126264 100644
--- a/lib/edoc/doc/src/notes.xml
+++ b/lib/edoc/doc/src/notes.xml
@@ -31,6 +31,91 @@
<p>This document describes the changes made to the EDoc
application.</p>
+<section><title>Edoc 0.7.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Add encoding when parsing Wiki text. EDoc used to
+ fail on strings such as "���". (Thanks to Richard
+ Carlsson.) </p>
+ <p>
+ Own Id: OTP-9109</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> It is now possible to use Erlang specifications and
+ types in EDoc documentation. Erlang specifications and
+ types will be used unless there is also a function
+ specification (<c>@spec</c>) or a type alias
+ (<c>@type</c>) with the same name. In the current
+ implementation the placement of <c>-spec</c> matters: it
+ should be placed where the <c>@spec</c> would otherwise
+ have been placed. </p>
+ <p>Not all Erlang types are included in the
+ documentation, but only those exported by some
+ <c>export_type</c> declaration or used by some documented
+ Erlang specification (<c>-spec</c>). </p>
+ <p> There is currently no support for overloaded Erlang
+ specifications. </p>
+ <p> The syntax definitions of EDoc have been augmented to
+ cope with most of the Erlang types. (But we recommend
+ that Erlang types should be used instead.) </p>
+ <p> <c>edoc:read_source()</c> takes one new option,
+ <c>report_missing_types</c>. <c>edoc_layout:module()</c>
+ takes one new option, <c>pretty_printer</c>. </p>
+ <p>
+ Own Id: OTP-8525</p>
+ </item>
+ <item>
+ <p> The <c>edoc_lib</c> module is meant to be private,
+ but since it is referred to from other man pages it has
+ been included in the OTP documentation. The modifications
+ introduced in this ticket make all functions private
+ except those referred to from other pages. </p>
+ <p>
+ Own Id: OTP-9110</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Compiler warnings were eliminated.</p>
+ <p>
+ Own Id: OTP-8855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Edoc 0.7.6.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Edoc now uses the new API functions to <c>inets</c>
+ instead of the deprecated ones.</p>
+ <p>
+ Own Id: OTP-8749</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Edoc 0.7.6.6</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/edoc/doc/src/ref_man.xml b/lib/edoc/doc/src/ref_man.xml
index 619fbaa7ca..a9af8740b9 100644
--- a/lib/edoc/doc/src/ref_man.xml
+++ b/lib/edoc/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile
index ca95c4cdad..9c5a9d30d1 100644
--- a/lib/edoc/src/Makefile
+++ b/lib/edoc/src/Makefile
@@ -29,7 +29,8 @@ SOURCES= \
edoc.erl edoc_data.erl edoc_doclet.erl edoc_extract.erl \
edoc_layout.erl edoc_lib.erl edoc_macros.erl edoc_parser.erl \
edoc_refs.erl edoc_report.erl edoc_run.erl edoc_scanner.erl \
- edoc_tags.erl edoc_types.erl edoc_wiki.erl otpsgml_layout.erl
+ edoc_specs.erl edoc_tags.erl edoc_types.erl edoc_wiki.erl \
+ otpsgml_layout.erl
OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
index 2177533441..0c8d5b85f8 100644
--- a/lib/edoc/src/edoc.app.src
+++ b/lib/edoc/src/edoc.app.src
@@ -15,6 +15,7 @@
edoc_report,
edoc_run,
edoc_scanner,
+ edoc_specs,
edoc_tags,
edoc_types,
edoc_wiki,
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index ec452a5929..360f2dbc9e 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -58,6 +58,8 @@
read_comments/1, read_comments/2,
read_source/1, read_source/2]).
+-compile({no_auto_import,[error/1]}).
+
-import(edoc_report, [report/2, report/3, error/1, error/3]).
-include("edoc.hrl").
@@ -256,6 +258,7 @@ opt_defaults() ->
opt_negations() ->
[{no_preprocess, preprocess},
{no_subpackages, subpackages},
+ {no_report_missing_types, report_missing_types},
{no_packages, packages}].
%% @spec run(Packages::[package()],
@@ -308,13 +311,13 @@ opt_negations() ->
%% <dd>Specifies the suffix used for output files. The default value is
%% `".html"'. Note that this also affects generated references.
%% </dd>
-%% <dt>{@type {new, bool()@}}
+%% <dt>{@type {new, boolean()@}}
%% </dt>
%% <dd>If the value is `true', any existing `edoc-info' file in the
%% target directory will be ignored and overwritten. The default
%% value is `false'.
%% </dd>
-%% <dt>{@type {packages, bool()@}}
+%% <dt>{@type {packages, boolean()@}}
%% </dt>
%% <dd>If the value is `true', it it assumed that packages (module
%% namespaces) are being used, and that the source code directory
@@ -340,7 +343,7 @@ opt_negations() ->
%% <dd>Specifies the expected suffix of input files. The default
%% value is `".erl"'.
%% </dd>
-%% <dt>{@type {subpackages, bool()@}}
+%% <dt>{@type {subpackages, boolean()@}}
%% </dt>
%% <dd>If the value is `true', all subpackages of specified packages
%% will also be included in the documentation. The default value is
@@ -576,6 +579,12 @@ layout(Doc, Opts) ->
%% @spec (File) -> [comment()]
+%% @type comment() = {Line, Column, Indentation, Text}
+%% where
+%% Line = integer(),
+%% Column = integer(),
+%% Indentation = integer(),
+%% Text = [string()]
%% @equiv read_comments(File, [])
read_comments(File) ->
@@ -583,12 +592,6 @@ read_comments(File) ->
%% @spec read_comments(File::filename(), Options::proplist()) ->
%% [comment()]
-%% where
-%% comment() = {Line, Column, Indentation, Text},
-%% Line = integer(),
-%% Column = integer(),
-%% Indentation = integer(),
-%% Text = [string()]
%%
%% @doc Extracts comments from an Erlang source code file. See the
%% module {@link //syntax_tools/erl_comment_scan} for details on the
@@ -614,7 +617,7 @@ read_source(Name) ->
%%
%% Options:
%% <dl>
-%% <dt>{@type {preprocess, bool()@}}
+%% <dt>{@type {preprocess, boolean()@}}
%% </dt>
%% <dd>If the value is `true', the source file will be read via the
%% Erlang preprocessor (`epp'). The default value is `false'.
@@ -640,6 +643,13 @@ read_source(Name) ->
%% macro definitions, used if the `preprocess' option is turned on.
%% The default value is the empty list.</dd>
%% </dl>
+%% <dt>{@type {report_missing_types, boolean()@}}
+%% </dt>
+%% <dd>If the value is `true', warnings are issued for missing types.
+%% The default value is `false'.
+%% `no_report_missing_types' is an alias for
+%% `{report_missing_types, false}'.
+%% </dd>
%%
%% @see get_doc/2
%% @see //syntax_tools/erl_syntax
@@ -722,17 +732,17 @@ get_doc(File) ->
%% <a href="overview-summary.html#Macro_expansion">Inline macro expansion</a>
%% for details.
%% </dd>
-%% <dt>{@type {hidden, bool()@}}
+%% <dt>{@type {hidden, boolean()@}}
%% </dt>
%% <dd>If the value is `true', documentation of hidden functions will
%% also be included. The default value is `false'.
%% </dd>
-%% <dt>{@type {private, bool()@}}
+%% <dt>{@type {private, boolean()@}}
%% </dt>
%% <dd>If the value is `true', documentation of private functions will
%% also be included. The default value is `false'.
%% </dd>
-%% <dt>{@type {todo, bool()@}}
+%% <dt>{@type {todo, boolean()@}}
%% </dt>
%% <dd>If the value is `true', To-Do notes written using `@todo' or
%% `@TODO' tags will be included in the documentation. The default
diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl
index 71cc1a52b9..43657b3b8f 100644
--- a/lib/edoc/src/edoc.hrl
+++ b/lib/edoc/src/edoc.hrl
@@ -37,6 +37,7 @@
-define(SOURCE_DIR, "src").
-define(EBIN_DIR, "ebin").
-define(EDOC_DIR, "doc").
+-define(REPORT_MISSING_TYPE, false).
-include("edoc_doclet.hrl").
@@ -83,10 +84,11 @@
%% Module Entries (one per function, plus module header and footer)
-%% @type entry() = #entry{name = atom(),
-%% args = [string()],
+%% @type entry() = #entry{{atom(), integer()} % function
+%% | name = atom(), % other
+%% args = [atom()],
%% line = integer(),
-%% export = bool(),
+%% export = boolean(),
%% data = term()}
-record(entry, {name, args = [], line = 0, export, data}).
@@ -95,6 +97,7 @@
%% @type tag() = #tag{name = atom(),
%% line = integer(),
+%% origin = comment | code,
%% data = term()}
--record(tag, {name, line = 0, data}).
+-record(tag, {name, line = 0, origin = comment, data}).
diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl
index 124f8eb9a1..27f43dca5a 100644
--- a/lib/edoc/src/edoc_data.erl
+++ b/lib/edoc/src/edoc_data.erl
@@ -20,7 +20,7 @@
%% @copyright 2003 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @see edoc
-%% @end
+%% @end
%% =====================================================================
%% @doc Building the EDoc external data structure. See the file
@@ -30,9 +30,10 @@
-export([module/4, package/4, overview/4, type/2]).
+-export([hidden_filter/2, get_all_tags/1]).
+
-include("edoc.hrl").
-%% TODO: report multiple definitions of the same type in the same module.
%% TODO: check that variables in @equiv are found in the signature
%% TODO: copy types from target (if missing) when using @equiv
@@ -139,6 +140,15 @@ functions(Es, Env, Opts) ->
|| #entry{name = {_,_}=N, args = As, export = Export, data = Ts}
<- Es].
+hidden_filter(Es, Opts) ->
+ Private = proplists:get_bool(private, Opts),
+ Hidden = proplists:get_bool(hidden, Opts),
+ [E || E <- Es,
+ case E#entry.name of
+ {_, _} -> function_filter(E, Private, Hidden);
+ _ -> true
+ end].
+
function_filter(Es, Opts) ->
Private = proplists:get_bool(private, Opts),
Hidden = proplists:get_bool(hidden, Opts),
@@ -298,7 +308,7 @@ get_deprecated(Ts, F, A, Env) ->
case otp_internal:obsolete(M, F, A) of
{Tag, Text} when Tag =:= deprecated; Tag =:= removed ->
deprecated([Text]);
- {Tag, Repl, _Rel} when Tag =:= deprecated; Tag =:= removed ->
+ {Tag, Repl, _Rel} when Tag =:= deprecated; Tag =:= removed ->
deprecated(Repl, Env);
_ ->
[]
diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl
index f1d876d593..30eef3e63a 100644
--- a/lib/edoc/src/edoc_doclet.erl
+++ b/lib/edoc/src/edoc_doclet.erl
@@ -76,7 +76,7 @@
%% <dd>Specifies the suffix used for output files. The default value is
%% `".html"'.
%% </dd>
-%% <dt>{@type {hidden, bool()@}}
+%% <dt>{@type {hidden, boolean()@}}
%% </dt>
%% <dd>If the value is `true', documentation of hidden modules and
%% functions will also be included. The default value is `false'.
@@ -86,7 +86,7 @@
%% <dd>Specifies the name of the overview-file. By default, this doclet
%% looks for a file `"overview.edoc"' in the target directory.
%% </dd>
-%% <dt>{@type {private, bool()@}}
+%% <dt>{@type {private, boolean()@}}
%% </dt>
%% <dd>If the value is `true', documentation of private modules and
%% functions will also be included. The default value is `false'.
diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl
index ea2755f7aa..5e28762c53 100644
--- a/lib/edoc/src/edoc_extract.erl
+++ b/lib/edoc/src/edoc_extract.erl
@@ -14,7 +14,7 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
+%% $Id: $
%%
%% @copyright 2001-2003 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
@@ -34,10 +34,12 @@
%% %% @headerfile "edoc.hrl" (disabled until it can be made private)
-include("edoc.hrl").
-%% @type filename() = file:filename()
+%% @type filename() = file:filename().
+%% @type proplist() = proplists:property().
+%% @type syntaxTree() = erl_syntax:syntaxTree().
%% @spec source(File::filename(), Env::edoc_env(), Options::proplist())
-%% -> {ModuleName, edoc_module()}
+%% -> {ModuleName, edoc:edoc_module()}
%% ModuleName = atom()
%% proplist() = [term()]
%%
@@ -53,16 +55,11 @@ source(File, Env, Opts) ->
Comments = edoc:read_comments(File, Opts),
source(Forms, Comments, File, Env, Opts).
-%% @spec source(Forms, Comments::[comment()], File::filename(),
+%% @spec source(Forms, Comments::[edoc:comment()], File::filename(),
%% Env::edoc_env(), Options::proplist()) ->
-%% {ModuleName, edoc_module()}
+%% {ModuleName, edoc:edoc_module()}
%%
%% Forms = syntaxTree() | [syntaxTree()]
-%% comment() = {Line, Column, Indentation, Text}
-%% Line = integer()
-%% Column = integer()
-%% Indentation = integer()
-%% Text = [string()]
%% ModuleName = atom()
%%
%% @doc Like {@link source/4}, but first inserts the given comments in
@@ -80,15 +77,15 @@ source(Forms, Comments, File, Env, Opts) when is_list(Forms) ->
source(Forms1, Comments, File, Env, Opts);
source(Forms, Comments, File, Env, Opts) ->
Tree = erl_recomment:quick_recomment_forms(Forms, Comments),
- source(Tree, File, Env, Opts).
+ TypeDocs = find_type_docs(Forms, Comments),
+ source1(Tree, File, Env, Opts, TypeDocs).
%% @spec source(Forms, File::filename(), Env::edoc_env(),
%% Options::proplist()) ->
-%% {ModuleName, edoc_module()}
+%% {ModuleName, edoc:edoc_module()}
%%
%% Forms = syntaxTree() | [syntaxTree()]
%% ModuleName = atom()
-%% edoc_module() = edoc:edoc_module()
%% @type edoc_env() = edoc_lib:edoc_env()
%%
%% @doc Extracts EDoc documentation from commented source code syntax
@@ -116,6 +113,11 @@ source(Forms, Comments, File, Env, Opts) ->
source(Forms, File, Env, Opts) when is_list(Forms) ->
source(erl_syntax:form_list(Forms), File, Env, Opts);
source(Tree, File0, Env, Opts) ->
+ TypeDocs = find_type_docs(Tree, []),
+ source1(Tree, File0, Env, Opts, TypeDocs).
+
+%% Forms0 and Comments is used for extracting Erlang type documentation.
+source1(Tree, File0, Env, Opts, TypeDocs) ->
Forms = preprocess_forms(Tree),
File = edoc_lib:filename(File0),
Module = get_module_info(Tree, File),
@@ -126,11 +128,12 @@ source(Tree, File0, Env, Opts) ->
package = Package,
root = edoc_refs:relative_package_path('', Package)},
Env2 = add_macro_defs(module_macros(Env1), Opts, Env1),
- Entries1 = get_tags([Header, Footer | Entries], Env2, File),
- Data = edoc_data:module(Module, Entries1, Env2, Opts),
+ Entries1 = get_tags([Header, Footer | Entries], Env2, File, TypeDocs),
+ Entries2 = edoc_specs:add_data(Entries1, Opts, File, Module),
+ edoc_tags:check_types(Entries2, Opts, File),
+ Data = edoc_data:module(Module, Entries2, Env2, Opts),
{Name, Data}.
-
%% @spec header(File::filename(), Env::edoc_env(), Options::proplist())
%% -> {ok, Tags} | {error, Reason}
%% Tags = [term()]
@@ -148,7 +151,7 @@ header(File, Env, Opts) ->
Comments = edoc:read_comments(File),
header(Forms, Comments, File, Env, Opts).
-%% @spec header(Forms, Comments::[comment()], File::filename(),
+%% @spec header(Forms, Comments::[edoc:comment()], File::filename(),
%% Env::edoc_env(), Options::proplist()) ->
%% {ok, Tags} | {error, Reason}
%% Forms = syntaxTree() | [syntaxTree()]
@@ -196,7 +199,7 @@ header(Tree, File0, Env, _Opts) ->
%% kill all the information above it up to that point. Then we call
%% this the 'header' to make error reports make better sense.
{Header, Footer, Entries} = collect(Forms, Module),
- if Header#entry.data /= [] ->
+ if Header#entry.data /= {[],[],[]} ->
warning(File, "documentation before module declaration is ignored by @headerfile", []);
true -> ok
end,
@@ -215,7 +218,6 @@ add_macro_defs(Defs0, Opts, Env) ->
edoc_macros:check_defs(Defs),
Env#env{macros = Defs ++ Defs0 ++ Env#env.macros}.
-
%% @spec file(File::filename(), Context, Env::edoc_env(),
%% Options::proplist()) -> {ok, Tags} | {error, Reason}
%% Context = overview | package
@@ -276,7 +278,7 @@ text(Text, Context, Env, Opts, Where) ->
end.
-%% @spec (Forms::[syntaxTree()], File::filename()) -> moduleInfo()
+%% @spec (Forms::[syntaxTree()], File::filename()) -> module()
%% @doc Initialises a module-info record with data about the module
%% represented by the list of forms. Exports are guaranteed to exist in
%% the set of defined names.
@@ -351,6 +353,13 @@ preprocess_forms_2(F, Fs) ->
[F | preprocess_forms_1(Fs)];
text ->
[F | preprocess_forms_1(Fs)];
+ {attribute, {N, _}} ->
+ case edoc_specs:is_tag(N) of
+ true ->
+ [F | preprocess_forms_1(Fs)];
+ false ->
+ preprocess_forms_1(Fs)
+ end;
_ ->
preprocess_forms_1(Fs)
end.
@@ -362,42 +371,55 @@ preprocess_forms_2(F, Fs) ->
%% in the list.
collect(Fs, Mod) ->
- collect(Fs, [], [], undefined, Mod).
+ collect(Fs, [], [], [], [], undefined, Mod).
-collect([F | Fs], Cs, As, Header, Mod) ->
+collect([F | Fs], Cs, Ss, Ts, As, Header, Mod) ->
case erl_syntax_lib:analyze_form(F) of
comment ->
- collect(Fs, [F | Cs], As, Header, Mod);
+ collect(Fs, [F | Cs], Ss, Ts, As, Header, Mod);
{function, Name} ->
L = erl_syntax:get_pos(F),
Export = ordsets:is_element(Name, Mod#module.exports),
Args = parameters(erl_syntax:function_clauses(F)),
- collect(Fs, [], [#entry{name = Name, args = Args, line = L,
- export = Export,
- data = comment_text(Cs)} | As],
+ collect(Fs, [], [], [],
+ [#entry{name = Name, args = Args, line = L,
+ export = Export,
+ data = {comment_text(Cs),Ss,Ts}} | As],
Header, Mod);
{rule, Name} ->
L = erl_syntax:get_pos(F),
Export = ordsets:is_element(Name, Mod#module.exports),
Args = parameters(erl_syntax:rule_clauses(F)),
- collect(Fs, [], [#entry{name = Name, args = Args, line = L,
- export = Export,
- data = comment_text(Cs)} | As],
+ collect(Fs, [], [], [],
+ [#entry{name = Name, args = Args, line = L,
+ export = Export,
+ data = {comment_text(Cs),Ss,Ts}} | As],
Header, Mod);
{attribute, {module, _}} when Header =:= undefined ->
L = erl_syntax:get_pos(F),
- collect(Fs, [], As, #entry{name = module, line = L,
- data = comment_text(Cs)},
+ collect(Fs, [], [], [], As,
+ #entry{name = module, line = L,
+ data = {comment_text(Cs),Ss,Ts}},
Mod);
+ {attribute, {N, _}} ->
+ case edoc_specs:tag(N) of
+ spec ->
+ collect(Fs, Cs, [F | Ss], Ts, As, Header, Mod);
+ type ->
+ collect(Fs, Cs, Ss, [F | Ts], As, Header, Mod);
+ unknown ->
+ %% Drop current seen comments.
+ collect(Fs, [], [], [], As, Header, Mod)
+ end;
_ ->
%% Drop current seen comments.
- collect(Fs, [], As, Header, Mod)
+ collect(Fs, [], [], [], As, Header, Mod)
end;
-collect([], Cs, As, Header, _Mod) ->
- Footer = #entry{name = footer, data = comment_text(Cs)},
+collect([], Cs, Ss, Ts, As, Header, _Mod) ->
+ Footer = #entry{name = footer, data = {comment_text(Cs),Ss,Ts}},
As1 = lists:reverse(As),
if Header =:= undefined ->
- {#entry{name = module, data = []}, Footer, As1};
+ {#entry{name = module, data = {[],[],[]}}, Footer, As1};
true ->
{Header, Footer, As1}
end.
@@ -475,7 +497,7 @@ select_names([Ns | Ls], As, S) ->
select_names([], As, _) ->
lists:reverse(As).
-select_name([A | Ns], S) ->
+select_name([A | Ns], S) ->
case sets:is_element(A, S) of
true ->
select_name(Ns, S);
@@ -522,6 +544,9 @@ capitalize(Cs) -> Cs.
-record(tags, {names,single,module,function,footer}).
get_tags(Es, Env, File) ->
+ get_tags(Es, Env, File, dict:new()).
+
+get_tags(Es, Env, File, TypeDocs) ->
%% Cache this stuff for quick lookups.
Tags = #tags{names = sets:from_list(edoc_tags:tag_names()),
single = sets:from_list(edoc_tags:tags(single)),
@@ -529,17 +554,20 @@ get_tags(Es, Env, File) ->
footer = sets:from_list(edoc_tags:tags(footer)),
function = sets:from_list(edoc_tags:tags(function))},
How = dict:from_list(edoc_tags:tag_parsers()),
- get_tags(Es, Tags, Env, How, File).
+ get_tags(Es, Tags, Env, How, File, TypeDocs).
-get_tags([#entry{name = Name, data = Cs} = E | Es], Tags, Env,
- How, File) ->
+get_tags([#entry{name = Name, data = {Cs,Specs,Types}} = E | Es], Tags, Env,
+ How, File, TypeDocs) ->
Where = {File, Name},
Ts0 = scan_tags(Cs),
- Ts1 = check_tags(Ts0, Tags, Where),
- Ts2 = edoc_macros:expand_tags(Ts1, Env, Where),
- Ts = edoc_tags:parse_tags(Ts2, How, Env, Where),
- [E#entry{data = Ts} | get_tags(Es, Tags, Env, How, File)];
-get_tags([], _, _, _, _) ->
+ {Ts1,Specs1} = select_spec(Ts0, Where, Specs),
+ Ts2 = check_tags(Ts1, Tags, Where),
+ Ts3 = edoc_macros:expand_tags(Ts2, Env, Where),
+ Ts4 = edoc_tags:parse_tags(Ts3, How, Env, Where),
+ Ts = selected_specs(Specs1, Ts4),
+ ETypes = [edoc_specs:type(Type, TypeDocs) || Type <- Types],
+ [E#entry{data = Ts++ETypes} | get_tags(Es, Tags, Env, How, File, TypeDocs)];
+get_tags([], _, _, _, _, _) ->
[].
%% Scanning a list of separate comments for tags.
@@ -572,6 +600,22 @@ check_tags_1(Ts, Tags, Where) ->
Single = Tags#tags.single,
edoc_tags:check_tags(Ts, Allow, Single, Where).
+select_spec(Ts, {_, {_F, _A}}, Specs) ->
+ case edoc_tags:filter_tags(Ts, sets:from_list([spec])) of
+ [] ->
+ %% Just a dummy to get us through check_tags()
+ {[edoc_specs:dummy_spec(S) || S <- Specs] ++ Ts, Specs};
+ _ ->
+ {Ts,[]}
+ end;
+select_spec(Ts, _Where, _Specs) ->
+ {Ts,[]}.
+
+selected_specs([], Ts) ->
+ Ts;
+selected_specs([F], [_ | Ts]) ->
+ [edoc_specs:spec(F, _Clause=1) | Ts].
+
%% Macros for modules
module_macros(Env) ->
@@ -582,3 +626,25 @@ module_macros(Env) ->
file_macros(_Context, Env) ->
edoc_macros:std_macros(Env).
+
+%% @doc Extracts what will be documentation of Erlang types.
+%% Returns a dict of {Name, Doc} where Name is {TypeName, Arity}.
+%%
+%% The idea is to mimic how the @type tag works.
+%% Using @type:
+%% @type t() = t1(). Some docs of t/0;
+%% Further docs of t/0.
+%% The same thing using -type:
+%% -type t() :: t1(). % Some docs of t/0;
+%% Further docs of t/0.
+find_type_docs(Forms0, Comments) ->
+ Tree = erl_recomment:recomment_forms(Forms0, Comments),
+ Forms = preprocess_forms(Tree),
+ edoc_specs:docs(Forms, fun find_fun/2).
+
+find_fun(C0, Line) ->
+ C1 = comment_text(C0),
+ Text = lists:append([C#comment.text || C <- C1]),
+ Comm = #comment{line = Line, text = Text},
+ [Tag | _] = scan_tags([Comm]),
+ Tag.
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index 900f0b3040..3ec87b7060 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -14,7 +14,7 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
+%% $Id: $
%%
%% @author Richard Carlsson <[email protected]>
%% @copyright 2001-2006 Richard Carlsson
@@ -49,7 +49,6 @@
-define(FUNCTIONS_TITLE, "Function Details").
-define(FUNCTIONS_LABEL, "functions").
-
%% @doc The layout function.
%%
%% Options to the standard layout:
@@ -59,13 +58,20 @@
%% <dd>Specifies the number of column pairs used for the function
%% index tables. The default value is 1.
%% </dd>
+%% <dt>{@type {pretty_printer, atom()@}}
+%% </dt>
+%% <dd>Specifies how types and specifications are pretty printed.
+%% If the value `erl_pp' is specified the Erlang pretty printer
+%% (the module `erl_pp') will be used. The default is to do
+%% no pretty printing which implies that lines can be very long.
+%% </dd>
%% <dt>{@type {stylesheet, string()@}}
%% </dt>
%% <dd>Specifies the URI used for referencing the stylesheet. The
%% default value is `"stylesheet.css"'. If an empty string is
%% specified, no stylesheet reference will be generated.
%% </dd>
-%% <dt>{@type {sort_functions, bool()@}}
+%% <dt>{@type {sort_functions, boolean()@}}
%% </dt>
%% <dd>If `true', the detailed function descriptions are listed by
%% name, otherwise they are listed in the order of occurrence in
@@ -96,14 +102,20 @@ module(Element, Options) ->
%% % stylesheet = string(),
%% % index_columns = integer()}
--record(opts, {root, stylesheet, index_columns, sort_functions}).
+-record(opts, {root,
+ stylesheet,
+ index_columns,
+ sort_functions,
+ pretty_printer}).
init_opts(Element, Options) ->
R = #opts{root = get_attrval(root, Element),
index_columns = proplists:get_value(index_columns,
Options, 1),
sort_functions = proplists:get_value(sort_functions,
- Options, true)
+ Options, true),
+ pretty_printer = proplists:get_value(pretty_printer,
+ Options, '')
},
case proplists:get_value(stylesheet, Options) of
undefined ->
@@ -112,7 +124,7 @@ init_opts(Element, Options) ->
"" ->
R; % don't use any stylesheet
S when is_list(S) ->
- R#opts{stylesheet = S};
+ R#opts{stylesheet = S};
_ ->
report("bad value for option `stylesheet'.", []),
exit(error)
@@ -192,10 +204,10 @@ layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
["Description"]}]}
| FullDesc]
end
- ++ types(lists:sort(Types))
+ ++ types(lists:sort(Types), Opts)
++ function_index(SortedFs, Opts#opts.index_columns)
- ++ if Opts#opts.sort_functions -> functions(SortedFs);
- true -> functions(Functions)
+ ++ if Opts#opts.sort_functions -> functions(SortedFs, Opts);
+ true -> functions(Functions, Opts)
end
++ [hr, ?NL]
++ navigation("bottom")
@@ -218,7 +230,7 @@ timestamp() ->
edoc_lib:timestr(time())])
]}]},
?NL].
-
+
stylesheet(Opts) ->
case Opts#opts.stylesheet of
undefined ->
@@ -335,8 +347,8 @@ label_href(Content, F) ->
%% <!ELEMENT equiv (expr, see?)>
%% <!ELEMENT expr (#PCDATA)>
-functions(Fs) ->
- Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
+functions(Fs, Opts) ->
+ Es = lists:flatmap(fun ({Name, E}) -> function(Name, E, Opts) end, Fs),
if Es == [] -> [];
true ->
[?NL,
@@ -344,7 +356,7 @@ functions(Fs) ->
?NL | Es]
end.
-function(Name, E=#xmlElement{content = Es}) ->
+function(Name, E=#xmlElement{content = Es}, Opts) ->
([?NL,
{h3, [{class, "function"}],
label_anchor(function_header(Name, E, " *"), E)},
@@ -352,7 +364,7 @@ function(Name, E=#xmlElement{content = Es}) ->
++ [{'div', [{class, "spec"}],
[?NL,
{p,
- case typespec(get_content(typespec, Es)) of
+ case typespec(get_content(typespec, Es), Opts) of
[] ->
signature(get_content(args, Es),
get_attrval(name, E));
@@ -367,7 +379,7 @@ function(Name, E=#xmlElement{content = Es}) ->
[] -> [];
Rs -> [{p, Rs}, ?NL]
end}]
- ++ throws(Es)
+ ++ throws(Es, Opts)
++ equiv_p(Es)
++ deprecated(Es, "function")
++ fulldesc(Es)
@@ -402,7 +414,7 @@ label_anchor(Content, E) ->
%% This is currently only done for functions without type spec.
-signature(Es, Name) ->
+signature(Es, Name) ->
[{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> any()"]}].
arg(#xmlElement{content = Es}) ->
@@ -432,66 +444,168 @@ returns(Es) ->
%% <!ELEMENT throws (type, localdef*)>
-throws(Es) ->
+throws(Es, Opts) ->
case get_content(throws, Es) of
[] -> [];
Es1 ->
+ %% Doesn't use format_type; keep it short!
[{p, (["throws ", {tt, t_utype(get_elem(type, Es1))}]
- ++ local_defs(get_elem(localdef, Es1)))},
+ ++ local_defs(get_elem(localdef, Es1), Opts))},
?NL]
end.
%% <!ELEMENT typespec (erlangName, type, localdef*)>
-typespec([]) -> [];
-typespec(Es) ->
- [{tt, ([t_name(get_elem(erlangName, Es))]
- ++ t_utype(get_elem(type, Es)))}]
- ++ local_defs(get_elem(localdef, Es)).
+typespec([], _Opts) -> [];
+typespec(Es, Opts) ->
+ Name = t_name(get_elem(erlangName, Es)),
+ Defs = get_elem(localdef, Es),
+ [Type] = get_elem(type, Es),
+ format_spec(Name, Type, Defs, Opts) ++ local_defs(Defs, Opts).
%% <!ELEMENT typedecl (typedef, description?)>
%% <!ELEMENT typedef (erlangName, argtypes, type?, localdef*)>
-types([]) -> [];
-types(Ts) ->
- Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts),
+types([], _Opts) -> [];
+types(Ts, Opts) ->
+ Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E, Opts) end, Ts),
[?NL,
{h2, [{a, [{name, ?DATA_TYPES_LABEL}],
[?DATA_TYPES_TITLE]}]},
?NL | Es].
-typedecl(Name, E=#xmlElement{content = Es}) ->
+typedecl(Name, E=#xmlElement{content = Es}, Opts) ->
([?NL, {h3, [{class, "typedecl"}], label_anchor([Name, "()"], E)}, ?NL]
- ++ [{p, typedef(get_content(typedef, Es))}, ?NL]
+ ++ [{p, typedef(get_content(typedef, Es), Opts)}, ?NL]
++ fulldesc(Es)).
type_name(#xmlElement{content = Es}) ->
t_name(get_elem(erlangName, get_content(typedef, Es))).
-typedef(Es) ->
+typedef(Es, Opts) ->
Name = ([t_name(get_elem(erlangName, Es)), "("]
- ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
+ ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
(case get_elem(type, Es) of
[] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}];
- Type ->
- [{tt, Name ++ [" = "] ++ t_utype(Type)}]
+ Type -> format_type(Name, Name, Type, [], Opts)
end
- ++ local_defs(get_elem(localdef, Es))).
+ ++ local_defs(get_elem(localdef, Es), Opts)).
-local_defs([]) -> [];
-local_defs(Es) ->
+local_defs(Es, Opts) ->
+ local_defs(Es, [], Opts).
+
+local_defs([], _, _Opts) -> [];
+local_defs(Es0, Last, Opts) ->
+ [E | Es] = lists:reverse(Es0),
[?NL,
{ul, [{class, "definitions"}],
- lists:concat([[{li, [{tt, localdef(E)}]}, ?NL] || E <- Es])}].
-
-localdef(E = #xmlElement{content = Es}) ->
- (case get_elem(typevar, Es) of
- [] ->
- label_anchor(t_abstype(get_content(abstype, Es)), E);
- [V] ->
- t_var(V)
- end
- ++ [" = "] ++ t_utype(get_elem(type, Es))).
+ lists:reverse(lists:append([localdef(E1, [], Opts) || E1 <- Es]),
+ localdef(E, Last, Opts))}].
+
+localdef(E = #xmlElement{content = Es}, Last, Opts) ->
+ Name = case get_elem(typevar, Es) of
+ [] ->
+ label_anchor(N0 = t_abstype(get_content(abstype, Es)), E);
+ [V] ->
+ N0 = t_var(V)
+ end,
+ [{li, format_type(Name, N0, get_elem(type, Es), Last, Opts)}].
+
+%% Use the default formatting of EDoc, which creates references, and
+%% then insert newlines and indentation according to erl_pp (the
+%% (fast) Erlang pretty printer).
+format_spec(Name, Type, Defs, #opts{pretty_printer = erl_pp}=Opts) ->
+ try
+ L = t_clause(Name, Type),
+ O = pp_clause(Name, Type),
+ {R, ".\n"} = etypef(L, O),
+ [{pre, R}]
+ catch _:_ ->
+ %% Example: "@spec ... -> record(a)"
+ format_spec(Name, Type, Defs, Opts#opts{pretty_printer=''})
+ end;
+format_spec(Sep, Type, Defs, _Opts) ->
+ %% Very limited formatting.
+ Br = if Defs =:= [] -> br; true -> [] end,
+ [{tt, t_clause(Sep, Type)}, Br].
+
+t_clause(Name, Type) ->
+ #xmlElement{content = [#xmlElement{name = 'fun', content = C}]} = Type,
+ [Name] ++ t_fun(C).
+
+pp_clause(Pre, Type) ->
+ Types = ot_utype([Type]),
+ Atom = lists:duplicate(iolist_size(Pre), $a),
+ L1 = erl_pp:attribute({attribute,0,spec,{{list_to_atom(Atom),0},[Types]}}),
+ "-spec " ++ L2 = lists:flatten(L1),
+ L3 = Pre ++ lists:nthtail(length(Atom), L2),
+ re:replace(L3, "\n ", "\n", [{return,list},global]).
+
+format_type(Prefix, Name, Type, Last, #opts{pretty_printer = erl_pp}=Opts) ->
+ try
+ L = t_utype(Type),
+ O = pp_type(Name, Type),
+ {R, ".\n"} = etypef(L, O),
+ [{pre, Prefix ++ [" = "] ++ R ++ Last}]
+ catch _:_ ->
+ %% Example: "t() = record(a)."
+ format_type(Prefix, Name, Type, Last, Opts#opts{pretty_printer =''})
+ end;
+format_type(Prefix, _Name, Type, Last, _Opts) ->
+ [{tt, Prefix ++ [" = "] ++ t_utype(Type) ++ Last}].
+
+pp_type(Prefix, Type) ->
+ Atom = list_to_atom(lists:duplicate(iolist_size(Prefix), $a)),
+ L1 = erl_pp:attribute({attribute,0,type,{Atom,ot_utype(Type),[]}}),
+ {L2,N} = case lists:dropwhile(fun(C) -> C =/= $: end, lists:flatten(L1)) of
+ ":: " ++ L3 -> {L3,9}; % compensation for extra "()" and ":"
+ "::\n" ++ L3 -> {"\n"++L3,6}
+ end,
+ Ss = lists:duplicate(N, $\s),
+ re:replace(L2, "\n"++Ss, "\n", [{return,list},global]).
+
+etypef(L, O0) ->
+ {R, O} = etypef(L, [], O0, []),
+ {lists:reverse(R), O}.
+
+etypef([C | L], St, [C | O], R) ->
+ etypef(L, St, O, [[C] | R]);
+etypef(" "++L, St, O, R) ->
+ etypef(L, St, O, R);
+etypef("", [Cs | St], O, R) ->
+ etypef(Cs, St, O, R);
+etypef("", [], O, R) ->
+ {R, O};
+etypef(L, St, " "++O, R) ->
+ etypef(L, St, O, [" " | R]);
+etypef(L, St, "\n"++O, R) ->
+ Ss = lists:takewhile(fun(C) -> C =:= $\s end, O),
+ etypef(L, St, lists:nthtail(length(Ss), O), ["\n"++Ss | R]);
+etypef([{a, HRef, S0} | L], St, O0, R) ->
+ {S, O} = etypef(S0, app_fix(O0)),
+ etypef(L, St, O, [{a, HRef, S} | R]);
+etypef("="++L, St, "::"++O, R) ->
+ %% EDoc uses "=" for record field types; Erlang types use "::".
+ %% Maybe there should be an option for this, possibly affecting
+ %% other similar discrepancies.
+ etypef(L, St, O, ["=" | R]);
+etypef([Cs | L], St, O, R) ->
+ etypef(Cs, [L | St], O, R).
+
+app_fix(L) ->
+ try
+ {"//" ++ R1,L2} = app_fix(L, 1),
+ [App, Mod] = string:tokens(R1, "/"),
+ "//" ++ atom(App) ++ "/" ++ atom(Mod) ++ L2
+ catch _:_ -> L
+ end.
+
+app_fix(L, I) -> % a bit slow
+ {L1, L2} = lists:split(I, L),
+ case erl_scan:tokens([], L1 ++ ". ", 1) of
+ {done, {ok,[{atom,_,Atom}|_],_}, _} -> {atom_to_list(Atom), L2};
+ _ -> app_fix(L, I+1)
+ end.
fulldesc(Es) ->
case get_content(fullDescription, get_content(description, Es)) of
@@ -702,21 +816,28 @@ t_type([E=#xmlElement{name = atom}]) ->
t_atom(E);
t_type([E=#xmlElement{name = integer}]) ->
t_integer(E);
+t_type([E=#xmlElement{name = range}]) ->
+ t_range(E);
+t_type([E=#xmlElement{name = binary}]) ->
+ t_binary(E);
t_type([E=#xmlElement{name = float}]) ->
t_float(E);
t_type([#xmlElement{name = nil}]) ->
t_nil();
+t_type([#xmlElement{name = paren, content = Es}]) ->
+ t_paren(Es);
t_type([#xmlElement{name = list, content = Es}]) ->
t_list(Es);
+t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ t_nonempty_list(Es);
t_type([#xmlElement{name = tuple, content = Es}]) ->
t_tuple(Es);
t_type([#xmlElement{name = 'fun', content = Es}]) ->
- t_fun(Es);
-t_type([#xmlElement{name = record, content = Es}]) ->
- t_record(Es);
+ ["fun("] ++ t_fun(Es) ++ [")"];
+t_type([E = #xmlElement{name = record, content = Es}]) ->
+ t_record(E, Es);
t_type([E = #xmlElement{name = abstype, content = Es}]) ->
- T = t_abstype(Es),
- see(E, T);
+ t_abstype(E, Es);
t_type([#xmlElement{name = union, content = Es}]) ->
t_union(Es).
@@ -729,15 +850,27 @@ t_atom(E) ->
t_integer(E) ->
[get_attrval(value, E)].
+t_range(E) ->
+ [get_attrval(value, E)].
+
+t_binary(E) ->
+ [get_attrval(value, E)].
+
t_float(E) ->
[get_attrval(value, E)].
t_nil() ->
["[]"].
+t_paren(Es) ->
+ ["("] ++ t_utype(get_elem(type, Es)) ++ [")"].
+
t_list(Es) ->
["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+t_nonempty_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ [", ...]"].
+
t_tuple(Es) ->
["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
@@ -745,13 +878,27 @@ t_fun(Es) ->
["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
[") -> "] ++ t_utype(get_elem(type, Es))).
-t_record(Es) ->
- ["#"] ++ t_type(get_elem(atom, Es)) ++ ["{"]
- ++ seq(fun t_field/1, get_elem(field, Es), ["}"]).
+t_record(E, Es) ->
+ Name = ["#"] ++ t_type(get_elem(atom, Es)),
+ case get_elem(field, Es) of
+ [] ->
+ see(E, [Name, "{}"]);
+ Fs ->
+ see(E, Name) ++ ["{"] ++ seq(fun t_field/1, Fs, ["}"])
+ end.
t_field(#xmlElement{content = Es}) ->
t_type(get_elem(atom, Es)) ++ [" = "] ++ t_utype(get_elem(type, Es)).
+t_abstype(E, Es) ->
+ Name = t_name(get_elem(erlangName, Es)),
+ case get_elem(type, Es) of
+ [] ->
+ see(E, [Name, "()"]);
+ Ts ->
+ see(E, [Name]) ++ ["("] ++ seq(fun t_utype_elem/1, Ts, [")"])
+ end.
+
t_abstype(Es) ->
([t_name(get_elem(erlangName, Es)), "("]
++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])).
@@ -827,7 +974,8 @@ type(E) ->
type(E, []).
type(E, Ds) ->
- xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
+ Opts = [],
+ xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds, Opts),
?HTML_EXPORT).
package(E=#xmlElement{name = package, content = Es}, Options) ->
@@ -873,3 +1021,142 @@ overview(E=#xmlElement{name = overview, content = Es}, Options) ->
++ timestamp()),
XML = xhtml(Title, stylesheet(Opts), Body),
xmerl:export_simple(XML, ?HTML_EXPORT, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% NYTT
+
+ot_utype([E]) ->
+ ot_utype_elem(E).
+
+ot_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> ot_type(Es);
+ N ->
+ Name = {var,0,list_to_atom(N)},
+ T = ot_type(Es),
+ case T of
+ Name -> T;
+ T -> {ann_type,0,[Name, T]}
+ end
+ end.
+
+ot_type([E=#xmlElement{name = typevar}]) ->
+ ot_var(E);
+ot_type([E=#xmlElement{name = atom}]) ->
+ ot_atom(E);
+ot_type([E=#xmlElement{name = integer}]) ->
+ ot_integer(E);
+ot_type([E=#xmlElement{name = range}]) ->
+ ot_range(E);
+ot_type([E=#xmlElement{name = binary}]) ->
+ ot_binary(E);
+ot_type([E=#xmlElement{name = float}]) ->
+ ot_float(E);
+ot_type([#xmlElement{name = nil}]) ->
+ ot_nil();
+ot_type([#xmlElement{name = paren, content = Es}]) ->
+ ot_paren(Es);
+ot_type([#xmlElement{name = list, content = Es}]) ->
+ ot_list(Es);
+ot_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ ot_nonempty_list(Es);
+ot_type([#xmlElement{name = tuple, content = Es}]) ->
+ ot_tuple(Es);
+ot_type([#xmlElement{name = 'fun', content = Es}]) ->
+ ot_fun(Es);
+ot_type([#xmlElement{name = record, content = Es}]) ->
+ ot_record(Es);
+ot_type([#xmlElement{name = abstype, content = Es}]) ->
+ ot_abstype(Es);
+ot_type([#xmlElement{name = union, content = Es}]) ->
+ ot_union(Es).
+
+ot_var(E) ->
+ {var,0,list_to_atom(get_attrval(name, E))}.
+
+ot_atom(E) ->
+ {ok, [Atom], _} = erl_scan:string(get_attrval(value, E), 0),
+ Atom.
+
+ot_integer(E) ->
+ {integer,0,list_to_integer(get_attrval(value, E))}.
+
+ot_range(E) ->
+ [I1, I2] = string:tokens(get_attrval(value, E), "."),
+ {type,0,range,[{integer,0,list_to_integer(I1)},
+ {integer,0,list_to_integer(I2)}]}.
+
+ot_binary(E) ->
+ {Base, Unit} =
+ case string:tokens(get_attrval(value, E), ",:*><") of
+ [] ->
+ {0, 0};
+ ["_",B] ->
+ {list_to_integer(B), 0};
+ ["_","_",U] ->
+ {0, list_to_integer(U)};
+ ["_",B,_,"_",U] ->
+ {list_to_integer(B), list_to_integer(U)}
+ end,
+ {type,0,binary,[{integer,0,Base},{integer,0,Unit}]}.
+
+ot_float(E) ->
+ {float,0,list_to_float(get_attrval(value, E))}.
+
+ot_nil() ->
+ {nil,0}.
+
+ot_paren(Es) ->
+ {paren_type,0,[ot_utype(get_elem(type, Es))]}.
+
+ot_list(Es) ->
+ {type,0,list,[ot_utype(get_elem(type, Es))]}.
+
+ot_nonempty_list(Es) ->
+ {type,0,nonempty_list,[ot_utype(get_elem(type, Es))]}.
+
+ot_tuple(Es) ->
+ {type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
+
+ot_fun(Es) ->
+ Range = ot_utype(get_elem(type, Es)),
+ Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)],
+ {type,0,'fun',[{type,0,product,Args},Range]}.
+
+ot_record(Es) ->
+ {type,0,record,[ot_type(get_elem(atom, Es)) |
+ [ot_field(F) || F <- get_elem(field, Es)]]}.
+
+ot_field(#xmlElement{content = Es}) ->
+ {type,0,field_type,
+ [ot_type(get_elem(atom, Es)), ot_utype(get_elem(type, Es))]}.
+
+ot_abstype(Es) ->
+ ot_name(get_elem(erlangName, Es),
+ [ot_utype_elem(Elem) || Elem <- get_elem(type, Es)]).
+
+ot_union(Es) ->
+ {type,0,union,[ot_utype_elem(E) || E <- Es]}.
+
+ot_name(Es, T) ->
+ case ot_name(Es) of
+ [Mod, ":", Atom] ->
+ {remote_type,0,[{atom,0,list_to_atom(Mod)},
+ {atom,0,list_to_atom(Atom)},T]};
+ "tuple" when T =:= [] ->
+ {type,0,tuple,any};
+ Atom ->
+ {type,0,list_to_atom(Atom),T}
+ end.
+
+ot_name([E]) ->
+ Atom = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> Atom;
+ M ->
+ case get_attrval(app, E) of
+ "" ->
+ [M, ":", Atom];
+ A ->
+ ["//"++A++"/" ++ M, ":", Atom] % EDoc only!
+ end
+ end.
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index 47e61f7932..585e30a2d2 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -16,7 +16,6 @@
%%
%% $Id$
%%
-%% @private
%% @copyright 2001-2003 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @see edoc
@@ -49,14 +48,17 @@
%% ---------------------------------------------------------------------
%% List and string utilities
+%% @private
timestr({H,M,Sec}) ->
lists:flatten(io_lib:fwrite("~2.2.0w:~2.2.0w:~2.2.0w",[H,M,Sec])).
+%% @private
datestr({Y,M,D}) ->
Ms = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"],
lists:flatten(io_lib:fwrite("~s ~w ~w",[lists:nth(M, Ms),D,Y])).
+%% @private
count(X, Xs) ->
count(X, Xs, 0).
@@ -67,6 +69,7 @@ count(X, [_ | Xs], N) ->
count(_X, [], N) ->
N.
+%% @private
lines(Cs) ->
lines(Cs, [], []).
@@ -77,6 +80,7 @@ lines([C | Cs], As, Ls) ->
lines([], As, Ls) ->
lists:reverse([lists:reverse(As) | Ls]).
+%% @private
split_at(Cs, K) ->
split_at(Cs, K, []).
@@ -87,6 +91,7 @@ split_at([C | Cs], K, As) ->
split_at([], _K, As) ->
{lists:reverse(As), []}.
+%% @private
split_at_stop(Cs) ->
split_at_stop(Cs, []).
@@ -103,6 +108,7 @@ split_at_stop([C | Cs], As) ->
split_at_stop([], As) ->
{lists:reverse(As), []}.
+%% @private
split_at_space(Cs) ->
split_at_space(Cs, []).
@@ -117,17 +123,20 @@ split_at_space([C | Cs], As) ->
split_at_space([], As) ->
{lists:reverse(As), []}.
+%% @private
is_space([$\s | Cs]) -> is_space(Cs);
is_space([$\t | Cs]) -> is_space(Cs);
is_space([$\n | Cs]) -> is_space(Cs);
is_space([_C | _Cs]) -> false;
is_space([]) -> true.
+%% @private
strip_space([$\s | Cs]) -> strip_space(Cs);
strip_space([$\t | Cs]) -> strip_space(Cs);
strip_space([$\n | Cs]) -> strip_space(Cs);
strip_space(Cs) -> Cs.
+%% @private
segment(Es, N) ->
segment(Es, [], [], 0, N).
@@ -140,6 +149,7 @@ segment([], [], Cs, _N, _M) ->
segment([], As, Cs, _N, _M) ->
lists:reverse([lists:reverse(As) | Cs]).
+%% @private
transpose([]) -> [];
transpose([[] | Xss]) -> transpose(Xss);
transpose([[X | Xs] | Xss]) ->
@@ -151,6 +161,7 @@ transpose([[X | Xs] | Xss]) ->
%% end of the summary sentence only if it is also the last segment in
%% the list, or is followed by a 'p' or 'br' ("whitespace") element.
+%% @private
get_first_sentence([#xmlElement{name = p, content = Es} | _]) ->
%% Descend into initial paragraph.
get_first_sentence_1(Es);
@@ -230,6 +241,7 @@ end_of_sentence_1(_, false, _) ->
%% Names must begin with a lowercase letter and contain only
%% alphanumerics and underscores.
+%% @private
is_name([C | Cs]) when C >= $a, C =< $z ->
is_name_1(Cs);
is_name([C | Cs]) when C >= $\337, C =< $\377, C =/= $\367 ->
@@ -252,6 +264,7 @@ is_name_1(_) -> false.
to_atom(A) when is_atom(A) -> A;
to_atom(S) when is_list(S) -> list_to_atom(S).
+%% @private
unique([X | Xs]) -> [X | unique(Xs, X)];
unique([]) -> [].
@@ -267,6 +280,7 @@ unique([], _) -> [].
%% content of <a href="overview-summary.html#ftag-equiv">`@equiv'</a>
%% tags, and strings denoting file names, e.g. in @headerfile. Also used
%% by {@link edoc_run}.
+%% @private
parse_expr(S, L) ->
case erl_scan:string(S ++ ".", L) of
@@ -287,12 +301,15 @@ parse_expr(S, L) ->
%% @doc EDoc "contact information" parsing. This is the type of the
%% content in e.g.
%% <a href="overview-summary.html#mtag-author">`@author'</a> tags.
+%% @private
-%% @type info() = #info{name = string(),
-%% mail = string(),
-%% uri = string()}
+%% % @type info() = #info{name = string(),
+%% % email = string(),
+%% % uri = string()}
--record(info, {name = "", email = "", uri = ""}).
+-record(info, {name = "" :: string(),
+ email = "" :: string(),
+ uri = "" :: string()}).
parse_contact(S, L) ->
I = scan_name(S, L, #info{}, []),
@@ -365,6 +382,7 @@ strip_and_reverse(As) ->
%%
%% TODO: general utf-8 encoding for all of Unicode (0-16#10ffff)
+%% @private
escape_uri([C | Cs]) when C >= $a, C =< $z ->
[C | escape_uri(Cs)];
escape_uri([C | Cs]) when C >= $A, C =< $Z ->
@@ -407,6 +425,7 @@ hex_octet(N) ->
%% Please note that URI are *not* file names. Don't use the stdlib
%% 'filename' module for operations on (any parts of) URI.
+%% @private
join_uri(Base, "") ->
Base;
join_uri("", Path) ->
@@ -416,6 +435,7 @@ join_uri(Base, Path) ->
%% Check for relative URI; "network paths" ("//...") not included!
+%% @private
is_relative_uri([$: | _]) ->
false;
is_relative_uri([$/, $/ | _]) ->
@@ -431,6 +451,7 @@ is_relative_uri([_ | Cs]) ->
is_relative_uri([]) ->
true.
+%% @private
uri_get("file:///" ++ Path) ->
uri_get_file(Path);
uri_get("file://localhost/" ++ Path) ->
@@ -472,7 +493,7 @@ uri_get_file(File0) ->
uri_get_http(URI) ->
%% Try using option full_result=false
- case catch {ok, http:request(get, {URI,[]}, [],
+ case catch {ok, httpc:request(get, {URI,[]}, [],
[{full_result, false}])} of
{'EXIT', _} ->
uri_get_http_r10(URI);
@@ -482,7 +503,7 @@ uri_get_http(URI) ->
uri_get_http_r10(URI) ->
%% Try most general form of request
- Result = (catch {ok, http:request(get, {URI,[]}, [], [])}),
+ Result = (catch {ok, httpc:request(get, {URI,[]}, [], [])}),
uri_get_http_1(Result, URI).
uri_get_http_1(Result, URI) ->
@@ -530,6 +551,7 @@ uri_get_ftp(URI) ->
Msg = io_lib:format("cannot access ftp scheme yet: '~s'.", [URI]),
{error, Msg}.
+%% @private
to_label([$\s | Cs]) ->
to_label(Cs);
to_label([$\t | Cs]) ->
@@ -562,6 +584,7 @@ to_label_2(Cs) ->
%% ---------------------------------------------------------------------
%% Files
+%% @private
filename([C | T]) when is_integer(C), C > 0 ->
[C | filename(T)];
filename([H|T]) ->
@@ -574,6 +597,7 @@ filename(N) ->
report("bad filename: `~P'.", [N, 25]),
exit(error).
+%% @private
copy_file(From, To) ->
case file:copy(From, To) of
{ok, _} -> ok;
@@ -598,6 +622,7 @@ list_dir(Dir, Error) ->
F("could not read directory '~s': ~s.", [filename(Dir), R1])
end.
+%% @private
simplify_path(P) ->
case filename:basename(P) of
"." ->
@@ -634,6 +659,7 @@ simplify_path(P) ->
%% exit(error)
%% end.
+%% @private
try_subdir(Dir, Subdir) ->
D = filename:join(Dir, Subdir),
case filelib:is_dir(D) of
@@ -646,6 +672,7 @@ try_subdir(Dir, Subdir) ->
%%
%% @doc Write the given `Text' to the file named by `Name' in directory
%% `Dir'. If the target directory does not exist, it will be created.
+%% @private
write_file(Text, Dir, Name) ->
write_file(Text, Dir, Name, '').
@@ -655,6 +682,7 @@ write_file(Text, Dir, Name) ->
%% Name::edoc:filename(), Package::atom()|string()) -> ok
%% @doc Like {@link write_file/3}, but adds path components to the target
%% directory corresponding to the specified package.
+%% @private
write_file(Text, Dir, Name, Package) ->
Dir1 = filename:join([Dir | packages:split(Package)]),
@@ -670,6 +698,7 @@ write_file(Text, Dir, Name, Package) ->
exit(error)
end.
+%% @private
write_info_file(App, Packages, Modules, Dir) ->
Ts = [{packages, Packages},
{modules, Modules}],
@@ -701,6 +730,7 @@ info_file_data(Ts) ->
%% Local file access - don't complain if file does not exist.
+%% @private
read_info_file(Dir) ->
File = filename:join(Dir, ?INFO_FILE),
case filelib:is_file(File) of
@@ -767,11 +797,13 @@ parse_terms_1([], _As, _Vs) ->
%% ---------------------------------------------------------------------
%% Source files and packages
+%% @private
find_sources(Path, Opts) ->
find_sources(Path, "", Opts).
%% @doc See {@link edoc:run/3} for a description of the options
%% `subpackages', `source_suffix' and `exclude_packages'.
+%% @private
%% NEW-OPTIONS: subpackages, source_suffix, exclude_packages
%% DEFER-OPTIONS: edoc:run/3
@@ -825,6 +857,7 @@ is_package_dir(Name, Dir) ->
is_name(filename:rootname(filename:basename(Name)))
andalso filelib:is_dir(filename:join(Dir, Name)).
+%% @private
find_file([P | Ps], Pkg, Name) ->
Dir = filename:join(P, filename:join(packages:split(Pkg))),
File = filename:join(Dir, Name),
@@ -837,6 +870,7 @@ find_file([P | Ps], Pkg, Name) ->
find_file([], _Pkg, _Name) ->
"".
+%% @private
find_doc_dirs() ->
find_doc_dirs(code:get_path()).
@@ -902,6 +936,7 @@ add_new(K, V, D) ->
%% @spec (Options::proplist()) -> edoc_env()
%% @equiv get_doc_env([], [], [], Opts)
+%% @private
get_doc_env(Opts) ->
get_doc_env([], [], [], Opts).
@@ -912,6 +947,7 @@ get_doc_env(Opts) ->
%% Modules = [atom()]
%% proplist() = [term()]
%%
+%% @type proplist() = proplists:property().
%% @type edoc_env(). Environment information needed by EDoc for
%% generating references. The data representation is not documented.
%%
@@ -950,6 +986,7 @@ get_doc_env(App, Packages, Modules, Opts) ->
%% NEW-OPTIONS: doclet
%% DEFER-OPTIONS: edoc:run/3
+%% @private
run_doclet(Fun, Opts) ->
run_plugin(doclet, ?DEFAULT_DOCLET, Fun, Opts).
@@ -959,6 +996,7 @@ run_doclet(Fun, Opts) ->
%% NEW-OPTIONS: layout
%% DEFER-OPTIONS: edoc:layout/2
+%% @private
run_layout(Fun, Opts) ->
run_plugin(layout, ?DEFAULT_LAYOUT, Fun, Opts).
@@ -988,6 +1026,14 @@ get_plugin(Key, Default, Opts) ->
%% ---------------------------------------------------------------------
%% Error handling
+-type line() :: erl_scan:line().
+-type err() :: 'eof'
+ | {'missing', char()}
+ | {line(), atom(), string()}
+ | string().
+
+-spec throw_error(err(), line()) -> no_return().
+
throw_error({missing, C}, L) ->
throw_error({"missing '~c'.", [C]}, L);
throw_error(eof, L) ->
diff --git a/lib/edoc/src/edoc_macros.erl b/lib/edoc/src/edoc_macros.erl
index 2874e2940c..5b512cb53a 100644
--- a/lib/edoc/src/edoc_macros.erl
+++ b/lib/edoc/src/edoc_macros.erl
@@ -14,8 +14,6 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @private
%% @copyright 2001-2005 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
@@ -317,6 +315,14 @@ macro_content([C | Cs], As, L, N) ->
macro_content([], _As, _L, _N) ->
throw('end').
+-type line() :: erl_scan:line().
+-type err() :: 'unterminated_macro'
+ | 'macro_name'
+ | {'macro_name', string()}
+ | {string(), [string()]}.
+
+-spec throw_error(line(), err()) -> no_return().
+
throw_error(L, unterminated_macro) ->
throw_error(L, {"unexpected end of macro.", []});
throw_error(L, macro_name) ->
diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl
index 0eea8ae66f..6943f1bdb8 100644
--- a/lib/edoc/src/edoc_parser.yrl
+++ b/lib/edoc/src/edoc_parser.yrl
@@ -24,21 +24,22 @@
%%
%% Author contact: [email protected]
%%
-%% $Id$
+%% $Id $
%%
%% =====================================================================
Nonterminals
start spec func_type utype_list utype_tuple utypes utype ptypes ptype
-nutype function_name where_defs defs def typedef etype throws qname ref
-aref mref lref pref var_list vars fields field.
+nutype function_name where_defs defs defs2 def typedef etype
+throws qname ref aref mref lref pref var_list vars fields field
+futype_list bin_base_type bin_unit_type.
Terminals
-atom float integer var string start_spec start_typedef start_throws
+atom float integer var an_var string start_spec start_typedef start_throws
start_ref
'(' ')' ',' '.' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*'
-'#' 'where'.
+'#' 'where' '<<' '>>' '..' '...'.
Rootsymbol start.
@@ -52,9 +53,9 @@ qname -> atom: [tok_val('$1')].
qname -> qname '.' atom: [tok_val('$3') | '$1'].
spec -> func_type where_defs:
- #t_spec{type = '$1', defs = lists:reverse('$2')}.
+ #t_spec{type = '$1', defs = '$2'}.
spec -> function_name func_type where_defs:
- #t_spec{name = '$1', type = '$2', defs = lists:reverse('$3')}.
+ #t_spec{name = '$1', type = '$2', defs = '$3'}.
where_defs -> 'where' defs: '$2'.
where_defs -> defs: '$1'.
@@ -66,13 +67,15 @@ func_type -> utype_list '->' utype:
%% Paired with line number, for later error reporting
-utype_list -> '(' ')' : {[], tok_line('$1')}.
utype_list -> '(' utypes ')' : {lists:reverse('$2'), tok_line('$1')}.
-utype_tuple -> '{' '}' : [].
+futype_list -> utype_list : '$1'.
+futype_list -> '(' '...' ')' : {[#t_var{name = '...'}], tok_line('$1')}.
+
utype_tuple -> '{' utypes '}' : lists:reverse('$2').
%% Produced in reverse order.
+utypes -> '$empty' : [].
utypes -> utype : ['$1'].
utypes -> utypes ',' utype : ['$3' | '$1'].
@@ -90,20 +93,25 @@ ptypes -> ptypes '|' ptype : ['$3' | '$1'].
ptype -> var : #t_var{name = tok_val('$1')}.
ptype -> atom : #t_atom{val = tok_val('$1')}.
ptype -> integer: #t_integer{val = tok_val('$1')}.
+ptype -> integer '..' integer: #t_integer_range{from = tok_val('$1'),
+ to = tok_val('$3')}.
ptype -> float: #t_float{val = tok_val('$1')}.
ptype -> utype_tuple : #t_tuple{types = '$1'}.
ptype -> '[' ']' : #t_nil{}.
ptype -> '[' utype ']' : #t_list{type = '$2'}.
+ptype -> '[' utype ',' '...' ']' : #t_nonempty_list{type = '$2'}.
ptype -> utype_list:
- if length(element(1, '$1')) == 1 ->
+ if length(element(1, '$1')) == 1 ->
%% there must be exactly one utype in the list
hd(element(1, '$1'));
+ %% Replace last line when releasing next major release:
+ %% #t_paren{type = hd(element(1, '$1'))};
length(element(1, '$1')) == 0 ->
return_error(element(2, '$1'), "syntax error before: ')'");
true ->
return_error(element(2, '$1'), "syntax error before: ','")
end.
-ptype -> utype_list '->' ptype:
+ptype -> futype_list '->' ptype:
#t_fun{args = element(1, '$1'), range = '$3'}.
ptype -> '#' atom '{' '}' :
#t_record{name = #t_atom{val = tok_val('$2')}}.
@@ -111,17 +119,45 @@ ptype -> '#' atom '{' fields '}' :
#t_record{name = #t_atom{val = tok_val('$2')},
fields = lists:reverse('$4')}.
ptype -> atom utype_list:
- #t_type{name = #t_name{name = tok_val('$1')},
- args = element(1, '$2')}.
-ptype -> qname ':' atom utype_list :
+ case {tok_val('$1'), element(1, '$2')} of
+ {nil, []} ->
+ %% Prefer '[]' before 'nil(). Due to
+ %% compatibility with Erlang types, which do not
+ %% separate '[]' from 'nil()'.
+ #t_nil{};
+ {list, [T]} ->
+ %% Prefer '[T]' before 'list(T). Due to
+ %% compatibility with Erlang types, which do not
+ %% separate '[T]' from 'list(T)'.
+ #t_list{type = T};
+ {'fun', [#t_fun{}=Fun]} ->
+ %% An incompatible change as compared to EDOc 0.7.6.6.
+ %% Due to compatibility with Erlang types.
+ Fun;
+ {'fun', []} ->
+ #t_type{name = #t_name{name = function}};
+ {Name, Args} ->
+ #t_type{name = #t_name{name = Name},
+ args = Args}
+ end.
+ptype -> qname ':' atom utype_list :
#t_type{name = #t_name{module = qname('$1'),
name = tok_val('$3')},
args = element(1, '$4')}.
-ptype -> '//' atom '/' qname ':' atom utype_list :
+ptype -> '//' atom '/' qname ':' atom utype_list :
#t_type{name = #t_name{app = tok_val('$2'),
module = qname('$4'),
name = tok_val('$6')},
args = element(1, '$7')}.
+ptype -> '<<' '>>' : #t_binary{}.
+ptype -> '<<' bin_base_type '>>' : #t_binary{base_size = '$2'}.
+ptype -> '<<' bin_unit_type '>>' : #t_binary{unit_size = '$2'}.
+ptype -> '<<' bin_base_type ',' bin_unit_type '>>' :
+ #t_binary{base_size = '$2', unit_size = '$4'}.
+
+bin_base_type -> an_var ':' integer: tok_val('$3').
+
+bin_unit_type -> an_var ':' an_var '*' integer : tok_val('$5').
%% Produced in reverse order.
fields -> field : ['$1'].
@@ -130,18 +166,19 @@ fields -> fields ',' field : ['$3' | '$1'].
field -> atom '=' utype :
#t_field{name = #t_atom{val = tok_val('$1')}, type = '$3'}.
-%% Produced in reverse order.
defs -> '$empty' : [].
-defs -> defs def : ['$2' | '$1'].
-defs -> defs ',' def : ['$3' | '$1'].
+defs -> def defs2 : ['$1' | lists:reverse('$2')].
+
+%% Produced in reverse order.
+defs2 -> '$empty' : [].
+defs2 -> defs2 def : ['$2' | '$1'].
+defs2 -> defs2 ',' def : ['$3' | '$1'].
def -> var '=' utype:
#t_def{name = #t_var{name = tok_val('$1')},
type = '$3'}.
-def -> atom var_list '=' utype:
- #t_def{name = #t_type{name = #t_name{name = tok_val('$1')},
- args = '$2'},
- type = '$4'}.
+def -> atom '(' utypes ')' '=' utype:
+ build_def(tok_val('$1'), '$2', '$3', '$6').
var_list -> '(' ')' : [].
var_list -> '(' vars ')' : lists:reverse('$2').
@@ -153,12 +190,12 @@ vars -> vars ',' var : [#t_var{name = tok_val('$3')} | '$1'].
typedef -> atom var_list where_defs:
#t_typedef{name = #t_name{name = tok_val('$1')},
args = '$2',
- defs = lists:reverse('$3')}.
+ defs = '$3'}.
typedef -> atom var_list '=' utype where_defs:
#t_typedef{name = #t_name{name = tok_val('$1')},
args = '$2',
type = '$4',
- defs = lists:reverse('$5')}.
+ defs = '$5'}.
%% References
@@ -195,7 +232,7 @@ etype -> utype: '$1'.
throws -> etype where_defs:
#t_throws{type = '$1',
- defs = lists:reverse('$2')}.
+ defs = '$2'}.
%% (commented out for now)
%% Header
@@ -297,7 +334,22 @@ union(Ts) ->
end.
annotate(T, A) -> ?add_t_ann(T, A).
-
+
+build_def(S, P, As, T) ->
+ case all_vars(As) of
+ true ->
+ #t_def{name = #t_type{name = #t_name{name = S},
+ args = lists:reverse(As)},
+ type = T};
+ false ->
+ return_error(element(2, P), "variable expected after '('")
+ end.
+
+all_vars([#t_var{} | As]) ->
+ all_vars(As);
+all_vars(As) ->
+ As =:= [].
+
%% ---------------------------------------------------------------------
%% @doc EDoc type specification parsing. Parses the content of
@@ -379,7 +431,7 @@ parse_param(S, L) ->
{S1, S2} = edoc_lib:split_at_space(edoc_lib:strip_space(S)),
case edoc_lib:strip_space(S1) of
"" -> throw_error(parse_param, L);
- Name ->
+ Name ->
Text = edoc_lib:strip_space(S2),
{list_to_atom(Name), edoc_wiki:parse_xml(Text, L)}
end.
@@ -404,6 +456,8 @@ parse_throws(S, L) ->
%% ---------------------------------------------------------------------
+-spec throw_error(term(), erl_scan:line()) -> no_return().
+
throw_error({L, M, D}, _L0) ->
throw({error,L,{format_error,M,D}});
throw_error({parse_spec, E}, L) ->
diff --git a/lib/edoc/src/edoc_refs.erl b/lib/edoc/src/edoc_refs.erl
index c2146bbe02..b974cf77c1 100644
--- a/lib/edoc/src/edoc_refs.erl
+++ b/lib/edoc/src/edoc_refs.erl
@@ -14,14 +14,12 @@
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%% USA
%%
-%% $Id$
-%%
%% @private
%% @copyright 2003 Richard Carlsson
%% @author Richard Carlsson <[email protected]>
%% @see edoc
%% @see edoc_parse_ref
-%% @end
+%% @end
%% =====================================================================
%% @doc Representation and handling of EDoc object references. See
diff --git a/lib/edoc/src/edoc_report.erl b/lib/edoc/src/edoc_report.erl
index b87c58dde3..ee54c60c90 100644
--- a/lib/edoc/src/edoc_report.erl
+++ b/lib/edoc/src/edoc_report.erl
@@ -27,6 +27,8 @@
-module(edoc_report).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([error/1,
error/2,
error/3,
diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl
index 37025d6621..96e5ea4631 100644
--- a/lib/edoc/src/edoc_run.erl
+++ b/lib/edoc/src/edoc_run.erl
@@ -42,6 +42,8 @@
-export([file/1, application/1, packages/1, files/1, toc/1]).
+-compile({no_auto_import,[error/1]}).
+
-import(edoc_report, [report/2, error/1]).
diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl
index d3dff64682..9d2e6f3aed 100644
--- a/lib/edoc/src/edoc_scanner.erl
+++ b/lib/edoc/src/edoc_scanner.erl
@@ -3,24 +3,24 @@
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
-%%
+%%
%% The Initial Developer of the Original Code is Ericsson Utvecklings
%% AB. Portions created by Ericsson are Copyright 1999, Ericsson
%% Utvecklings AB. All Rights Reserved.''
%%
-%% $Id$
+%% $Id: $
%%
%% @private
%% @copyright Richard Carlsson 2001-2003. Portions created by Ericsson
%% are Copyright 1999, Ericsson Utvecklings AB. All Rights Reserved.
%% @author Richard Carlsson <[email protected]>
%% @see edoc
-%% @end
+%% @end
%% @doc Tokeniser for EDoc. Based on the Erlang standard library module
%% {@link //stdlib/erl_scan}.
@@ -139,13 +139,21 @@ scan1([$"|Cs0], Toks, Pos) -> % String
scan_error({illegal, string}, Pos)
end;
%% Punctuation characters and operators, first recognise multiples.
+scan1([$<,$<|Cs], Toks, Pos) ->
+ scan1(Cs, [{'<<',Pos}|Toks], Pos);
+scan1([$>,$>|Cs], Toks, Pos) ->
+ scan1(Cs, [{'>>',Pos}|Toks], Pos);
scan1([$-,$>|Cs], Toks, Pos) ->
scan1(Cs, [{'->',Pos}|Toks], Pos);
scan1([$:,$:|Cs], Toks, Pos) ->
scan1(Cs, [{'::',Pos}|Toks], Pos);
scan1([$/,$/|Cs], Toks, Pos) ->
scan1(Cs, [{'//',Pos}|Toks], Pos);
-scan1([C|Cs], Toks, Pos) -> % Punctuation character
+scan1([$.,$.,$.|Cs], Toks, Pos) ->
+ scan1(Cs, [{'...',Pos}|Toks], Pos);
+scan1([$.,$.|Cs], Toks, Pos) ->
+ scan1(Cs, [{'..',Pos}|Toks], Pos);
+scan1([C|Cs], Toks, Pos) -> % Punctuation character
P = list_to_atom([C]),
scan1(Cs, [{P,Pos}|Toks], Pos);
scan1([], Toks0, _Pos) ->
@@ -158,7 +166,7 @@ scan_variable(C, Cs, Toks, Pos) ->
W = [C|reverse(Wcs)],
case W of
"_" ->
- scan_error({illegal,token}, Pos);
+ scan1(Cs1, [{an_var,Pos,'_'}|Toks], Pos);
_ ->
case catch list_to_atom(W) of
A when is_atom(A) ->
@@ -318,7 +326,7 @@ scan_integer(Cs, Stack, Pos) ->
scan_after_int([$.,C|Cs0], Ncs0, Toks, SPos, CPos) when C >= $0, C =< $9 ->
{Ncs,Cs,CPos1} = scan_integer(Cs0, [C,$.|Ncs0], CPos),
- scan_after_fraction(Cs, Ncs, Toks, SPos, CPos1);
+ scan_after_fraction(Cs, Ncs, Toks, SPos, CPos1);
scan_after_int(Cs, Ncs, Toks, SPos, CPos) ->
N = list_to_integer(reverse(Ncs)),
scan1(Cs, [{integer,SPos,N}|Toks], CPos).
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
new file mode 100644
index 0000000000..45016ef85a
--- /dev/null
+++ b/lib/edoc/src/edoc_specs.erl
@@ -0,0 +1,603 @@
+%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%% @doc EDoc interface to Erlang specifications and types.
+
+-module(edoc_specs).
+
+-export([type/2, spec/2, dummy_spec/1, docs/2]).
+
+-export([add_data/4, tag/1, is_tag/1]).
+
+-include("edoc.hrl").
+-include("edoc_types.hrl").
+
+-type proplist() :: [proplists:property()].
+-type syntaxTree() :: erl_syntax:syntaxTree().
+
+-define(TOP_TYPE, term).
+
+%%
+%% Exported functions
+%%
+
+-spec type(Form::syntaxTree(), TypeDocs::dict()) -> #tag{}.
+
+%% @doc Convert an Erlang type to EDoc representation.
+%% TypeDocs is a dict of {Name, Doc}.
+%% Note: #t_typedef.name is set to {record, R} for record types.
+type(Form, TypeDocs) ->
+ {Name, Data0} = erl_syntax_lib:analyze_wild_attribute(Form),
+ type = tag(Name),
+ {TypeName, Type, Args, Doc} =
+ case Data0 of
+ {{record, R}, Fs, []} ->
+ L = erl_syntax:get_pos(Form),
+ {{record, R}, {type, L, record, [{atom,L,R} | Fs]}, [], ""};
+ {N,T,As} ->
+ Doc0 =
+ case dict:find({N, length(As)}, TypeDocs) of
+ {ok, Doc1} ->
+ Doc1;
+ error ->
+ ""
+ end,
+ {#t_name{name = N}, T, As, Doc0}
+ end,
+ #tag{name = type, line = element(2, Type),
+ origin = code,
+ data = {#t_typedef{name = TypeName,
+ args = d2e(Args),
+ type = d2e(opaque2abstr(Name, Type))},
+ Doc}}.
+
+-spec spec(Form::syntaxTree(), ClauseN::pos_integer()) -> #tag{}.
+
+%% @doc Convert an Erlang spec to EDoc representation.
+spec(Form, Clause) ->
+ {Name, _Arity, TypeSpecs} = get_spec(Form),
+ TypeSpec = lists:nth(Clause, TypeSpecs),
+ #tag{name = spec, line = element(2, TypeSpec),
+ origin = code,
+ data = aspec(d2e(TypeSpec), Name)}.
+
+-spec dummy_spec(Form::syntaxTree()) -> #tag{}.
+
+%% @doc Create a #tag{} record where data is a string with the name of
+%% the given Erlang spec and an empty list of arguments.
+dummy_spec(Form) ->
+ {#t_name{name = Name}, Arity, TypeSpecs} = get_spec(Form),
+ As = string:join(lists:duplicate(Arity, "_X"), ","),
+ S = lists:flatten(io_lib:format("~p(~s) -> true\n", [Name, As])),
+ #tag{name = spec, line = element(2, hd(TypeSpecs)),
+ origin = code, data = S}.
+
+-spec docs(Forms::[syntaxTree()], CommentFun) -> dict() when
+ CommentFun :: fun(([syntaxTree()], Line :: term()) -> #tag{}).
+
+%% @doc Find comments after -type/-opaque declarations.
+%% Postcomments "inside" the type are skipped.
+docs(Forms, CommentFun) ->
+ find_type_docs(Forms, [], CommentFun).
+
+-type entry() :: #entry{}.
+-type module_info() :: #module{}.
+-type entries() :: [entry()].
+-spec add_data(Entries::entries(), Options::proplist(),
+ File::file:filename(), Module::module_info()) -> entries().
+
+%% @doc Create tags a la EDoc for Erlang specifications and types.
+%% Exported types and types used (indirectly) by Erlang specs are
+%% added to the entries.
+add_data(Entries, Opts, File, Module) ->
+ TypeDefs0 = espec_types(Entries),
+ TypeTable = ets:new(etypes, [ordered_set]),
+ Es1 = expand_records(Entries, TypeDefs0, TypeTable, Opts, File, Module),
+ Es = [use_tags(E, TypeTable) || E <- Es1],
+ true = ets:delete(TypeTable),
+ Es.
+
+%%
+%% Local functions
+%%
+
+aspec(#t_spec{}=Spec, Name) ->
+ Spec#t_spec{name = Name};
+aspec(Type, Name) ->
+ #t_spec{name = Name, type = Type}.
+
+get_spec(Form) ->
+ {spec, Data0} = erl_syntax_lib:analyze_wild_attribute(Form),
+ case Data0 of
+ {{F,A}, D} ->
+ {#t_name{name = F}, A, D};
+ {{M,F,A}, D} ->
+ {#t_name{module = M, name = F}, A, D}
+ end.
+
+find_type_docs([], Cs, _Fun) ->
+ dict:from_list(Cs);
+find_type_docs([F | Fs], Cs, Fun) ->
+ try get_name_and_last_line(F) of
+ {Name, LastTypeLine} ->
+ C0 = erl_syntax:comment(["% @type f(). "]),
+ C1 = erl_syntax:set_pos(C0, LastTypeLine),
+ %% Postcomments before the dot after the typespec are ignored.
+ C2 = [C1 | [C ||
+ C <- erl_syntax:get_postcomments(F),
+ get_line(erl_syntax:get_pos(C)) >= LastTypeLine]],
+ C3 = collect_comments(Fs, LastTypeLine),
+ #tag{data = Doc0} = Fun(lists:reverse(C2 ++ C3), LastTypeLine),
+ case strip(Doc0) of % Strip away "f(). \n"
+ "" ->
+ find_type_docs(Fs, Cs, Fun);
+ Doc ->
+ W = edoc_wiki:parse_xml(Doc, LastTypeLine),
+ find_type_docs(Fs, [{Name, W}|Cs], Fun)
+ end
+ catch _:_ ->
+ find_type_docs(Fs, Cs, Fun)
+ end.
+
+collect_comments([], _Line) ->
+ [];
+collect_comments([F | Fs], Line) ->
+ L1 = get_line(erl_syntax:get_pos(F)),
+ if
+ L1 =:= Line + 1;
+ L1 =:= Line -> % a separate postcomment
+ case is_comment(F) of
+ true ->
+ [F | collect_comments(Fs, L1)];
+ false ->
+ []
+ end;
+ true ->
+ []
+ end.
+%% Note: there is a creepy bug concerning an include file terminated
+%% by a -type attribute and the include statement is followed by a
+%% comment (which is not meant to be documentation of the type).
+
+is_comment(F) ->
+ erl_syntax_lib:analyze_form(F) =:= comment.
+
+strip("") ->
+ "";
+strip([$\n | S]) ->
+ S;
+strip([_ | S]) ->
+ strip(S).
+
+%% Find the type name and the greatest line number of a type spec.
+%% Should use syntax_tools but this has to do for now.
+get_name_and_last_line(F) ->
+ {Name, Data} = erl_syntax_lib:analyze_wild_attribute(F),
+ type = edoc_specs:tag(Name),
+ Attr = {attribute, erl_syntax:get_pos(F), Name, Data},
+ Ref = make_ref(),
+ Fun = fun(L) -> {Ref, get_line(L)} end,
+ TypeName = case Data of
+ {N, _T, As} when is_atom(N) -> % skip records
+ {N, length(As)}
+ end,
+ Line = gll(erl_lint:modify_line(Attr, Fun), Ref),
+ {TypeName, Line}.
+
+gll({Ref, Line}, Ref) ->
+ Line;
+gll([], _Ref) ->
+ 0;
+gll(List, Ref) when is_list(List) ->
+ lists:max([gll(E, Ref) || E <- List]);
+gll(Tuple, Ref) when is_tuple(Tuple) ->
+ gll(tuple_to_list(Tuple), Ref);
+gll(_, _) ->
+ 0.
+
+get_line(Pos) ->
+ {line, Line} = erl_scan:attributes_info(Pos, line),
+ Line.
+
+%% Collect all Erlang types. Types in comments (@type) shadow Erlang
+%% types (-spec/-opaque).
+espec_types(Entries) ->
+ Tags = get_all_tags(Entries),
+ CommTs = [type_name(T) ||
+ #tag{name = type, origin = comment}=T <- Tags],
+ CT = sets:from_list(CommTs),
+ [T || #tag{name = Name, origin = code}=T <- Tags,
+ tag(Name) =:= type,
+ not sets:is_element(type_name(T), CT)].
+
+get_all_tags(Es) ->
+ lists:flatmap(fun (#entry{data = Ts}) -> Ts end, Es).
+
+%% Turns an opaque type into an abstract datatype.
+%% Note: top level annotation is ignored.
+opaque2abstr(opaque, _T) -> undefined;
+opaque2abstr(type, T) -> T.
+
+%% Replaces the parameters extracted from the source (by
+%% edoc_extract:parameters/1) by annotations and variable names, using
+%% the source parameters as default values
+%% Selects seen types (exported types, types used by specs),
+%% skips records and unused types.
+use_tags(#entry{data = Ts}=E, TypeTable) ->
+ use_tags(Ts, E, TypeTable, []).
+
+use_tags([], E, _TypeTable, NTs) ->
+ E#entry{data = lists:reverse(NTs)};
+use_tags([#tag{origin = code}=T | Ts], E, TypeTable, NTs) ->
+ case tag(T#tag.name) of
+ spec ->
+ Args = params(T, E#entry.args),
+ use_tags(Ts, E#entry{args = Args}, TypeTable, [T | NTs]);
+ type ->
+ TypeName = type_name(T),
+ case ets:lookup(TypeTable, TypeName) of
+ [{{{record,_},_},_,_}] ->
+ use_tags(Ts, E, TypeTable, NTs);
+ [{_,_,not_seen}] ->
+ use_tags(Ts, E, TypeTable, NTs);
+ [] ->
+ use_tags(Ts, E, TypeTable, NTs);
+ [{TypeName, Tag, seen}] ->
+ use_tags(Ts, E, TypeTable, [Tag | NTs])
+ end
+ end;
+use_tags([T | Ts], E, TypeTable, NTs) ->
+ use_tags(Ts, E, TypeTable, [T | NTs]).
+
+params(#tag{name = spec, data=#t_spec{type = #t_fun{args = As}}}, Default) ->
+ parms(As, Default).
+
+parms([], []) ->
+ [];
+parms([A | As], [D | Ds]) ->
+ [param(A, D) | parms(As, Ds)].
+
+param(#t_list{type = Type}, Default) ->
+ param(Type, Default);
+param(#t_paren{type = Type}, Default) ->
+ param(Type, Default);
+param(#t_nonempty_list{type = Type}, Default) ->
+ param(Type, Default);
+param(#t_record{name = #t_atom{val = Name}}, _Default) ->
+ list_to_atom(capitalize(atom_to_list(Name)));
+param(T, Default) ->
+ arg_name(?t_ann(T), Default).
+
+capitalize([C | Cs]) when C >= $a, C =< $z -> [C - 32 | Cs];
+capitalize(Cs) -> Cs.
+
+%% Like edoc_types:arg_name/1
+arg_name([], Default) ->
+ Default;
+arg_name([A | As], Default) ->
+ case is_name(A) of
+ true -> A;
+ false -> arg_name(As, Default)
+ end.
+
+is_name(A) ->
+ is_atom(A).
+
+d2e({ann_type,_,[V, T0]}) ->
+ %% Note: the -spec/-type syntax allows annotations everywhere, but
+ %% EDoc does not. The fact that the annotation is added to the
+ %% type here does not necessarily mean that it will be used by the
+ %% layout module.
+ T = d2e(T0),
+ ?add_t_ann(T, element(3, V));
+d2e({type,_,no_return,[]}) ->
+ #t_type{name = #t_name{name = none}};
+d2e({remote_type,_,[{atom,_,M},{atom,_,F},Ts0]}) ->
+ Ts = d2e(Ts0),
+ typevar_anno(#t_type{name = #t_name{module = M, name = F}, args = Ts}, Ts);
+d2e({type,_,'fun',[{type,_,product,As0},Ran0]}) ->
+ Ts = [Ran|As] = d2e([Ran0|As0]),
+ %% Assume that the linter has checked type variables.
+ typevar_anno(#t_fun{args = As, range = Ran}, Ts);
+d2e({type,_,'fun',[A0={type,_,any},Ran0]}) ->
+ Ts = [A, Ran] = d2e([A0, Ran0]),
+ typevar_anno(#t_fun{args = [A], range = Ran}, Ts);
+d2e({type,_,'fun',[]}) ->
+ #t_type{name = #t_name{name = function}, args = []};
+d2e({type,_,any}) ->
+ #t_var{name = '...'}; % Kludge... not a type variable!
+d2e({type,_,nil,[]}) ->
+ #t_nil{};
+d2e({paren_type,_,[T]}) ->
+ #t_paren{type = d2e(T)};
+d2e({type,_,list,[T0]}) ->
+ T = d2e(T0),
+ typevar_anno(#t_list{type = T}, [T]);
+d2e({type,_,nonempty_list,[T0]}) ->
+ T = d2e(T0),
+ typevar_anno(#t_nonempty_list{type = T}, [T]);
+d2e({type,_,bounded_fun,[T,Gs]}) ->
+ [F0|Defs] = d2e([T|Gs]),
+ F = ?set_t_ann(F0, lists:keydelete(type_variables, 1, ?t_ann(F0))),
+ %% Assume that the linter has checked type variables.
+ #t_spec{type = typevar_anno(F, [F0]), defs = Defs};
+d2e({type,_,range,[V1,V2]}) ->
+ {integer,_,I1} = erl_eval:partial_eval(V1),
+ {integer,_,I2} = erl_eval:partial_eval(V2),
+ #t_integer_range{from = I1, to = I2};
+d2e({type,_,constraint,[Sub,Ts0]}) ->
+ case {Sub,Ts0} of
+ {{atom,_,is_subtype},[{var,_,N},T0]} ->
+ Ts = [T] = d2e([T0]),
+ #t_def{name = #t_var{name = N}, type = typevar_anno(T, Ts)};
+ {{atom,_,is_subtype},[ST0,T0]} ->
+ %% Should not happen.
+ Ts = [ST,T] = d2e([ST0,T0]),
+ #t_def{name = ST, type = typevar_anno(T, Ts)};
+ _ ->
+ throw_error(element(2, Sub), "cannot handle guard", [])
+ end;
+d2e({type,_,union,Ts0}) ->
+ Ts = d2e(Ts0),
+ typevar_anno(#t_union{types = Ts}, Ts);
+d2e({type,_,tuple,any}) ->
+ #t_type{name = #t_name{name = tuple}, args = []};
+d2e({type,_,binary,[Base,Unit]}) ->
+ #t_binary{base_size = element(3, Base),
+ unit_size = element(3, Unit)};
+d2e({type,_,tuple,Ts0}) ->
+ Ts = d2e(Ts0),
+ typevar_anno(#t_tuple{types = Ts}, Ts);
+d2e({type,_,record,[Name|Fs0]}) ->
+ Atom = #t_atom{val = element(3, Name)},
+ Fs = d2e(Fs0),
+ typevar_anno(#t_record{name = Atom, fields = Fs}, Fs);
+d2e({type,_,field_type,[Name,Type0]}) ->
+ Type = d2e(Type0),
+ typevar_anno(#t_field{name = #t_atom{val = element(3, Name)}, type = Type},
+ [Type]);
+d2e({typed_record_field,{record_field,L,Name},Type}) ->
+ d2e({type,L,field_type,[Name,Type]});
+d2e({typed_record_field,{record_field,L,Name,_E},Type}) ->
+ d2e({type,L,field_type,[Name,Type]});
+d2e({record_field,L,_Name,_E}=F) ->
+ d2e({typed_record_field,F,{type,L,any,[]}}); % Maybe skip...
+d2e({record_field,L,_Name}=F) ->
+ d2e({typed_record_field,F,{type,L,any,[]}}); % Maybe skip...
+d2e({type,_,Name,Types0}) ->
+ Types = d2e(Types0),
+ typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types);
+d2e({var,_,'_'}) ->
+ #t_type{name = #t_name{name = ?TOP_TYPE}};
+d2e({var,_,TypeName}) ->
+ TypeVar = ordsets:from_list([TypeName]),
+ T = #t_var{name = TypeName},
+ %% Annotate type variables with the name of the variable.
+ %% Doing so will stop edoc_layout (and possibly other layout modules)
+ %% from using the argument name from the source or to invent a new name.
+ T1 = ?add_t_ann(T, {type_variables, TypeVar}),
+ ?add_t_ann(T1, TypeName);
+d2e(L) when is_list(L) ->
+ [d2e(T) || T <- L];
+d2e({atom,_,A}) ->
+ #t_atom{val = A};
+d2e(undefined = U) -> % opaque
+ U;
+d2e(Expr) ->
+ {integer,_,I} = erl_eval:partial_eval(Expr),
+ #t_integer{val = I}.
+
+%% A type annotation (a tuple; neither an atom nor a list).
+typevar_anno(Type, Ts) ->
+ Vs = typevars(Ts),
+ case ordsets:to_list(Vs) of
+ [] -> Type;
+ _ -> ?add_t_ann(Type, {type_variables, Vs})
+ end.
+
+typevars(Ts) ->
+ ordsets:union(get_typevars(Ts)).
+
+get_typevars(Ts) ->
+ [Vs || T <- Ts, T =/= undefined, {type_variables, Vs} <- ?t_ann(T)].
+
+-record(parms, {tab, warn, file, line}).
+
+%% Expands record references. Explicitly given record fields are kept,
+%% but otherwise the fields from the record definition are substituted
+%% for the reference. The reason is that there are no record types.
+%% It is recommended to introduce types like "r() :: r{}" and then use
+%% r() everywhere. The right hand side, r{}, is expanded in order to
+%% show all fields.
+%% Returns updated types in the ETS table DT.
+expand_records(Entries, TypeDefs, DT, Opts, File, Module) ->
+ TypeList = [{type_name(T), T, not_seen} || T <- TypeDefs],
+ true = ets:insert(DT, TypeList),
+ Warn = proplists:get_value(report_missing_type, Opts,
+ ?REPORT_MISSING_TYPE) =:= true,
+ P = #parms{tab = DT, warn = Warn, file = File, line = 0},
+ ExportedTypes = [Name ||
+ {export_type,Ts} <- Module#module.attributes,
+ is_list(Ts),
+ {N,I} <- Ts,
+ ets:member(DT, Name = {#t_name{name = N}, I})],
+ _ = lists:foreach(fun({N,A}) -> true = seen_type(N, A, P)
+ end, ExportedTypes),
+ entries(Entries, P, Opts).
+
+entries([E0 | Es], P, Opts) ->
+ E = case edoc_data:hidden_filter([E0], Opts) of
+ [] ->
+ E0;
+ [_] ->
+ E0#entry{data = specs(E0#entry.data, P)}
+ end,
+ [E | entries(Es, P, Opts)];
+entries([], _P, _Opts) ->
+ [].
+
+specs([#tag{line = L, name = spec, origin = code, data = Spec}=Tag0 | Tags],
+ P0) ->
+ #t_spec{type = Type0, defs = Defs0} = Spec,
+ P = P0#parms{line = L},
+ Type = xrecs(Type0, P),
+ Defs = xrecs(Defs0, P),
+ Tag = Tag0#tag{data = Spec#t_spec{type = Type, defs = Defs}},
+ [Tag | specs(Tags, P)];
+specs([Tag | Tags], P) ->
+ [Tag | specs(Tags, P)];
+specs([], _P) ->
+ [].
+
+xrecs(#t_def{type = Type0}=T, P) ->
+ Type = xrecs(Type0, P),
+ T#t_def{type = Type};
+xrecs(#t_type{name = Name, args = Args0}=T, P) ->
+ Args = xrecs(Args0, P),
+ NArgs = length(Args),
+ true = seen_type(Name, NArgs, P),
+ T#t_type{args = Args};
+xrecs(#t_var{}=T, _P) ->
+ T;
+xrecs(#t_fun{args = Args0, range = Range0}=T, P) ->
+ Args = xrecs(Args0, P),
+ Range = xrecs(Range0, P),
+ T#t_fun{args = Args, range = Range};
+xrecs(#t_tuple{types = Types0}=T, P) ->
+ Types = xrecs(Types0, P),
+ T#t_tuple{types = Types};
+xrecs(#t_list{type = Type0}=T, P) ->
+ Type = xrecs(Type0, P),
+ T#t_list{type = Type};
+xrecs(#t_nil{}=T, _P) ->
+ T;
+xrecs(#t_paren{type = Type0}=T, P) ->
+ Type = xrecs(Type0, P),
+ T#t_paren{type = Type};
+xrecs(#t_nonempty_list{type = Type0}=T, P) ->
+ Type = xrecs(Type0, P),
+ T#t_nonempty_list{type = Type};
+xrecs(#t_atom{}=T, _P) ->
+ T;
+xrecs(#t_integer{}=T, _P) ->
+ T;
+xrecs(#t_integer_range{}=T, _P) ->
+ T;
+xrecs(#t_binary{}=T, _P) ->
+ T;
+xrecs(#t_float{}=T, _P) ->
+ T;
+xrecs(#t_union{types = Types0}=T, P) ->
+ Types = xrecs(Types0, P),
+ T#t_union{types = Types};
+xrecs(#t_record{fields = Fields0}=T, P) ->
+ Fields1 = xrecs(Fields0, P),
+ #t_record{name = #t_atom{val = Name}} = T,
+ RName = {record, Name},
+ true = seen_type(RName, 0, P),
+ Fields = select_fields(Fields1, RName, P#parms.tab),
+ T#t_record{fields = Fields};
+xrecs(#t_field{type = Type0}=T, P) ->
+ Type = xrecs(Type0, P),
+ T#t_field{type = Type};
+xrecs(undefined=T, _P) -> % opaque
+ T;
+xrecs([]=T, _P) ->
+ T;
+xrecs([E0 | Es0], P) ->
+ [xrecs(E0, P) | xrecs(Es0, P)].
+
+seen_type(N, NArgs, P) ->
+ TypeName = {N, NArgs},
+ #parms{tab = DT} = P,
+ case {ets:lookup(DT, TypeName), N} of
+ {[{TypeName, _, seen}], _} ->
+ true;
+ {[{TypeName, TagType, not_seen}], _} when N#t_name.module =:= [] ->
+ expand_datatype(TagType, proper_type, DT, P);
+ {[{TypeName, TagType, not_seen}], {record, _}} ->
+ expand_datatype(TagType, record_type, DT, P);
+ {[], {record, R}} ->
+ #parms{warn = W, line = L, file = File} = P,
+ [edoc_report:warning(L, File, "reference to untyped record ~w",
+ [R]) || W],
+ ets:insert(DT, {TypeName, fake, seen});
+ {[], _} -> % External type or missing type.
+ true
+ end.
+
+expand_datatype(Tag0, Kind, DT, P0) ->
+ #tag{line = L, data = {T0, Doc}} = Tag0,
+ #t_typedef{type = Type0, defs = []} = T0,
+ TypeName = type_name(Tag0),
+ true = ets:update_element(DT, TypeName, {3, seen}),
+ P = P0#parms{line = L},
+ Type = case Kind of
+ record_type ->
+ #t_record{fields = Fields0} = Type0,
+ Fields = xrecs(Fields0, P),
+ Type0#t_record{fields = Fields};
+ proper_type ->
+ xrecs(Type0, P)
+ end,
+ Tag = Tag0#tag{data={T0#t_typedef{type=Type}, Doc}},
+ ets:insert(DT, {TypeName, Tag, seen}).
+
+select_fields(Fields, Name, DT) ->
+ RecordName = {Name, 0},
+ case ets:lookup(DT, RecordName) of
+ [{RecordName, fake, seen}] ->
+ Fields;
+ [{RecordName, #tag{data = {T, _Doc}}, seen}] ->
+ #t_typedef{args = [], type = #t_record{fields = Fs}, defs = []}=T,
+ [find_field(F, Fields) || F <- Fs]
+ end.
+
+find_field(F, Fs) ->
+ case lists:keyfind(F#t_field.name, #t_field.name, Fs) of
+ false -> F;
+ NF -> NF
+ end.
+
+type_name(#tag{name = type,
+ data = {#t_typedef{name = Name, args = As},_}}) ->
+ {Name, length(As)}.
+
+%% @doc Return `true' if `Tag' is one of the specification and type
+%% attribute tags recognized by the Erlang compiler.
+
+-spec is_tag(Tag::atom()) -> boolean().
+
+is_tag(opaque) -> true;
+is_tag(spec) -> true;
+is_tag(type) -> true;
+is_tag(_) -> false.
+
+%% @doc Return the kind of the attribute tag.
+
+-type tag_kind() :: 'type' | 'spec' | 'unknown'.
+-spec tag(Tag::atom()) -> tag_kind().
+
+tag(opaque) -> type;
+tag(spec) -> spec;
+tag(type) -> type;
+tag(_) -> unknown.
+
+throw_error(Line, S, A) ->
+ edoc_report:error(Line, "", io_lib:format(S, A)),
+ throw(error).
diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl
index 1f2cb99c75..def39ee34c 100644
--- a/lib/edoc/src/edoc_tags.erl
+++ b/lib/edoc/src/edoc_tags.erl
@@ -31,7 +31,8 @@
-module(edoc_tags).
-export([tags/0, tags/1, tag_names/0, tag_parsers/0, scan_lines/2,
- filter_tags/3, check_tags/4, parse_tags/4]).
+ filter_tags/2, filter_tags/3, check_tags/4, parse_tags/4,
+ check_types/3]).
-import(edoc_report, [report/4, warning/4, error/3]).
@@ -201,6 +202,9 @@ append_lines([]) -> [].
%% Filtering out unknown tags.
+filter_tags(Ts, Tags) ->
+ filter_tags(Ts, Tags, no).
+
filter_tags(Ts, Tags, Where) ->
filter_tags(Ts, Tags, Where, []).
@@ -211,7 +215,8 @@ filter_tags([#tag{name = N, line = L} = T | Ts], Tags, Where, Ts1) ->
true ->
filter_tags(Ts, Tags, Where, [T | Ts1]);
false ->
- warning(L, Where, "tag @~s not recognized.", [N]),
+ [warning(L, Where, "tag @~s not recognized.", [N]) ||
+ Where =/= no],
filter_tags(Ts, Tags, Where, Ts1)
end;
filter_tags([], _, _, Ts) ->
@@ -320,16 +325,32 @@ parse_contact(Data, Line, _Env, _Where) ->
Info
end.
-parse_typedef(Data, Line, _Env, _Where) ->
+parse_typedef(Data, Line, _Env, Where) ->
Def = edoc_parser:parse_typedef(Data, Line),
- {#t_typedef{name = #t_name{name = T}}, _} = Def,
- case edoc_types:is_predefined(T) of
+ {#t_typedef{name = #t_name{name = T}, args = As}, _} = Def,
+ NAs = length(As),
+ case edoc_types:is_predefined(T, NAs) of
true ->
- throw_error(Line, {"redefining built-in type '~w'.", [T]});
+ case
+ edoc_types:is_new_predefined(T, NAs)
+ orelse edoc_types:is_predefined_otp_type(T, NAs)
+ of
+ false ->
+ throw_error(Line, {"redefining built-in type '~w'.",
+ [T]});
+ true ->
+ warning(Line, Where, "redefining built-in type '~w'.",
+ [T]),
+ Def
+ end;
false ->
Def
end.
+-type line() :: erl_scan:line().
+
+-spec parse_file(_, line(), _, _) -> no_return().
+
parse_file(Data, Line, Env, _Where) ->
case edoc_lib:parse_expr(Data, Line) of
{string, _, File0} ->
@@ -344,6 +365,8 @@ parse_file(Data, Line, Env, _Where) ->
throw_error(Line, file_not_string)
end.
+-spec parse_header(_, line(), _, _) -> no_return().
+
parse_header(Data, Line, Env, {Where, _}) ->
parse_header(Data, Line, Env, Where);
parse_header(Data, Line, Env, Where) when is_list(Where) ->
@@ -362,6 +385,13 @@ parse_header(Data, Line, Env, Where) when is_list(Where) ->
throw_error(Line, file_not_string)
end.
+-type err() :: 'file_not_string'
+ | {'file_not_found', file:filename()}
+ | {'read_file', file:filename(), term()}
+ | string().
+
+-spec throw_error(line(), err()) -> no_return().
+
throw_error(L, {read_file, File, R}) ->
throw_error(L, {"error reading file '~s': ~w",
[edoc_lib:filename(File), R]});
@@ -371,3 +401,107 @@ throw_error(L, file_not_string) ->
throw_error(L, "expected file name as a string");
throw_error(L, D) ->
throw({error, L, D}).
+
+%% Checks local types.
+
+-record(parms, {tab, warn, file, line}).
+
+check_types(Entries0, Opts, File) ->
+ Entries = edoc_data:hidden_filter(Entries0, Opts),
+ Tags = edoc_data:get_all_tags(Entries),
+ DT = ets:new(types, [bag]),
+ _ = [add_type(DT, Name, As, File, Line) ||
+ #tag{line = Line,
+ data = {#t_typedef{name = Name, args = As},_}} <- Tags],
+ Warn = proplists:get_value(report_missing_type, Opts,
+ ?REPORT_MISSING_TYPE) =:= true,
+ P = #parms{tab = DT, warn = Warn, file = File, line = 0},
+ try check_types(Tags, P)
+ after true = ets:delete(DT)
+ end.
+
+add_type(DT, Name, Args, File, Line) ->
+ NArgs = length(Args),
+ TypeName = {Name, NArgs},
+ case lists:member(TypeName, ets:lookup(DT, Name)) of
+ true ->
+ #t_name{name = N} = Name,
+ type_warning(Line, File, "duplicated type", N, NArgs);
+ false ->
+ ets:insert(DT, {Name, NArgs})
+ end.
+
+check_types([], _P)->
+ ok;
+check_types([Tag | Tags], P) ->
+ check_type(Tag, P, Tags).
+
+check_type(#tag{line = L, data = Data}, P0, Ts) ->
+ P = P0#parms{line = L},
+ case Data of
+ {#t_typedef{type = Type, defs = Defs},_} ->
+ check_type(Type, P, Defs++Ts);
+ #t_spec{type = Type, defs = Defs} ->
+ check_type(Type, P, Defs++Ts);
+ _->
+ check_types(Ts, P0)
+ end;
+check_type(#t_def{type = Type}, P, Ts) ->
+ check_type(Type, P, Ts);
+check_type(#t_type{name = Name, args = Args}, P, Ts) ->
+ check_used_type(Name, Args, P),
+ check_types(Args++Ts, P);
+check_type(#t_var{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_fun{args = Args, range = Range}, P, Ts) ->
+ check_type(Range, P, Args++Ts);
+check_type(#t_tuple{types = Types}, P, Ts) ->
+ check_types(Types ++Ts, P);
+check_type(#t_list{type = Type}, P, Ts) ->
+ check_type(Type, P, Ts);
+check_type(#t_nil{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_paren{type = Type}, P, Ts) ->
+ check_type(Type, P, Ts);
+check_type(#t_nonempty_list{type = Type}, P, Ts) ->
+ check_type(Type, P, Ts);
+check_type(#t_atom{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_integer{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_integer_range{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_binary{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_float{}, P, Ts) ->
+ check_types(Ts, P);
+check_type(#t_union{types = Types}, P, Ts) ->
+ check_types(Types++Ts, P);
+check_type(#t_record{fields = Fields}, P, Ts) ->
+ check_types(Fields++Ts, P);
+check_type(#t_field{type = Type}, P, Ts) ->
+ check_type(Type, P, Ts);
+check_type(undefined, P, Ts) ->
+ check_types(Ts, P).
+
+check_used_type(#t_name{name = N, module = Mod}=Name, Args, P) ->
+ NArgs = length(Args),
+ TypeName = {Name, NArgs},
+ DT = P#parms.tab,
+ case
+ Mod =/= []
+ orelse lists:member(TypeName, ets:lookup(DT, Name))
+ orelse edoc_types:is_predefined(N, NArgs)
+ orelse edoc_types:is_predefined_otp_type(N, NArgs)
+ of
+ true ->
+ ok;
+ false ->
+ #parms{warn = W, line = L, file = File} = P,
+ %% true = ets:insert(DT, TypeName),
+ [type_warning(L, File, "missing type", N, NArgs) || W]
+ end.
+
+type_warning(Line, File, S, N, NArgs) ->
+ AS = ["/"++integer_to_list(NArgs) || NArgs > 0],
+ warning(Line, File, S++" ~w~s", [N, AS]).
diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl
index 85c9ee6f2a..1ded63dffe 100644
--- a/lib/edoc/src/edoc_types.erl
+++ b/lib/edoc/src/edoc_types.erl
@@ -27,8 +27,9 @@
-module(edoc_types).
--export([is_predefined/1, to_ref/1, to_xml/2, to_label/1, arg_names/1,
- set_arg_names/2, arg_descs/1, range_desc/1]).
+-export([is_predefined/2, is_new_predefined/2, is_predefined_otp_type/2,
+ to_ref/1, to_xml/2, to_label/1, arg_names/1, set_arg_names/2,
+ arg_descs/1, range_desc/1]).
%% @headerfile "edoc_types.hrl"
@@ -36,27 +37,63 @@
-include("xmerl.hrl").
-is_predefined(any) -> true;
-is_predefined(atom) -> true;
-is_predefined(binary) -> true;
-is_predefined(bool) -> true;
-is_predefined(char) -> true;
-is_predefined(cons) -> true;
-is_predefined(deep_string) -> true;
-is_predefined(float) -> true;
-is_predefined(function) -> true;
-is_predefined(integer) -> true;
-is_predefined(list) -> true;
-is_predefined(nil) -> true;
-is_predefined(none) -> true;
-is_predefined(number) -> true;
-is_predefined(pid) -> true;
-is_predefined(port) -> true;
-is_predefined(reference) -> true;
-is_predefined(string) -> true;
-is_predefined(term) -> true;
-is_predefined(tuple) -> true;
-is_predefined(_) -> false.
+is_predefined(any, 0) -> true;
+is_predefined(atom, 0) -> true;
+is_predefined(binary, 0) -> true;
+is_predefined(bool, 0) -> true;
+is_predefined(char, 0) -> true;
+is_predefined(cons, 2) -> true;
+is_predefined(deep_string, 0) -> true;
+is_predefined(float, 0) -> true;
+is_predefined(function, 0) -> true;
+is_predefined(integer, 0) -> true;
+is_predefined(list, 0) -> true;
+is_predefined(list, 1) -> true;
+is_predefined(nil, 0) -> true;
+is_predefined(none, 0) -> true;
+is_predefined(number, 0) -> true;
+is_predefined(pid, 0) -> true;
+is_predefined(port, 0) -> true;
+is_predefined(reference, 0) -> true;
+is_predefined(string, 0) -> true;
+is_predefined(term, 0) -> true;
+is_predefined(tuple, 0) -> true;
+is_predefined(F, A) -> is_new_predefined(F, A).
+
+%% Should eventually be coalesced with is_predefined/2.
+is_new_predefined(arity, 0) -> true;
+is_new_predefined(bitstring, 0) -> true;
+is_new_predefined(boolean, 0) -> true;
+is_new_predefined(byte, 0) -> true;
+is_new_predefined(iodata, 0) -> true;
+is_new_predefined(iolist, 0) -> true;
+is_new_predefined(maybe_improper_list, 0) -> true;
+is_new_predefined(maybe_improper_list, 2) -> true;
+is_new_predefined(mfa, 0) -> true;
+is_new_predefined(module, 0) -> true;
+is_new_predefined(neg_integer, 0) -> true;
+is_new_predefined(node, 0) -> true;
+is_new_predefined(non_neg_integer, 0) -> true;
+is_new_predefined(nonempty_improper_list, 2) -> true;
+is_new_predefined(nonempty_list, 0) -> true;
+is_new_predefined(nonempty_list, 1) -> true;
+is_new_predefined(nonempty_maybe_improper_list, 0) -> true;
+is_new_predefined(nonempty_maybe_improper_list, 2) -> true;
+is_new_predefined(nonempty_string, 0) -> true;
+is_new_predefined(pos_integer, 0) -> true;
+is_new_predefined(timeout, 0) -> true;
+is_new_predefined(_, _) -> false.
+
+%% The following types will be removed later, but they are currently
+%% kind of built-in.
+is_predefined_otp_type(array, 0) -> true;
+is_predefined_otp_type(dict, 0) -> true;
+is_predefined_otp_type(digraph, 0) -> true;
+is_predefined_otp_type(gb_set, 0) -> true;
+is_predefined_otp_type(gb_tree, 0) -> true;
+is_predefined_otp_type(queue, 0) -> true;
+is_predefined_otp_type(set, 0) -> true;
+is_predefined_otp_type(_, _) -> false.
to_ref(#t_typedef{name = N}) ->
to_ref(N);
@@ -91,7 +128,9 @@ to_xml(#t_name{app = A, module = M, name = N}, _Env) ->
to_xml(#t_type{name = N, args = As}, Env) ->
Predef = case N of
#t_name{module = [], name = T} ->
- is_predefined(T);
+ NArgs = length(As),
+ (is_predefined(T, NArgs)
+ orelse is_predefined_otp_type(T, NArgs));
_ ->
false
end,
@@ -109,14 +148,30 @@ to_xml(#t_list{type = T}, Env) ->
{list, [wrap_utype(T, Env)]};
to_xml(#t_nil{}, _Env) ->
nil;
+to_xml(#t_paren{type = T}, Env) ->
+ {paren, [wrap_utype(T, Env)]};
+to_xml(#t_nonempty_list{type = T}, Env) ->
+ {nonempty_list, [wrap_utype(T, Env)]};
to_xml(#t_atom{val = V}, _Env) ->
{atom, [{value, io_lib:write(V)}], []};
to_xml(#t_integer{val = V}, _Env) ->
{integer, [{value, integer_to_list(V)}], []};
+to_xml(#t_integer_range{from = From, to = To}, _Env) ->
+ {range, [{value, integer_to_list(From)++".."++integer_to_list(To)}], []};
+to_xml(#t_binary{base_size = 0, unit_size = 0}, _Ens) ->
+ {binary, [{value, "<<>>"}], []};
+to_xml(#t_binary{base_size = B, unit_size = 0}, _Ens) ->
+ {binary, [{value, io_lib:fwrite("<<_:~w>>", [B])}], []};
+%to_xml(#t_binary{base_size = 0, unit_size = 8}, _Ens) ->
+% {binary, [{value, "binary()"}], []};
+to_xml(#t_binary{base_size = 0, unit_size = U}, _Ens) ->
+ {binary, [{value, io_lib:fwrite("<<_:_*~w>>", [U])}], []};
+to_xml(#t_binary{base_size = B, unit_size = U}, _Ens) ->
+ {binary, [{value, io_lib:fwrite("<<_:~w, _:_*~w>>", [B, U])}], []};
to_xml(#t_float{val = V}, _Env) ->
{float, [{value, io_lib:write(V)}], []};
to_xml(#t_union{types = Ts}, Env) ->
- {union, map(fun wrap_type/2, Ts, Env)};
+ {union, map(fun wrap_utype/2, Ts, Env)};
to_xml(#t_record{name = N = #t_atom{}, fields = Fs}, Env) ->
{record, [to_xml(N, Env) | map(fun to_xml/2, Fs, Env)]};
to_xml(#t_field{name = N = #t_atom{}, type = T}, Env) ->
diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl
index 1dcbdd9493..1353bfb93a 100644
--- a/lib/edoc/src/edoc_types.hrl
+++ b/lib/edoc/src/edoc_types.hrl
@@ -1,6 +1,6 @@
%% =====================================================================
%% Header file for EDoc Type Representations
-%%
+%%
%% Copyright (C) 2001-2005 Richard Carlsson
%%
%% This library is free software; you can redistribute it and/or modify
@@ -29,13 +29,15 @@
-record(t_spec, {name, type, defs=[]}). % function specification
-%% @type type() = t_atom() | t_fun() | t_integer() | t_list() | t_nil()
-%% | t_tuple() | t_type() | t_union() | t_var()
+%% @type type() = t_atom() | t_binary() | t_float() | t_fun() | t_integer()
+%% | t_integer_range() | t_list() | t_nil()| t_nonempty_list()
+%% | t_record() | t_tuple() | t_type() | t_union() | t_var()
+%% | t_paren()
%% @type t_typedef() = #t_typedef{name = t_name(),
%% args = [type()],
-%% type = type(),
-%% defs = [t_def()]}
+%% type = type() | undefined,
+%% defs = [t_def()]}.
-record(t_typedef, {name, args, type,
defs=[]}). % type declaration/definition
@@ -45,7 +47,7 @@
-record(t_throws, {type, defs=[]}). % exception declaration
-%% @type t_def() = #t_def{name = t_name(),
+%% @type t_def() = #t_def{name = t_type() | t_var(),
%% type = type()}
-record(t_def, {name, type}). % local definition 'name = type'
@@ -75,7 +77,9 @@
%% name = t_name(),
%% args = [type()]}
--record(t_type, {a=[], name, args = []}). % abstract type 'name(...)'
+-record(t_type, {a=[], % abstract type 'name(...)'
+ name,
+ args = []}).
%% @type t_union() = #t_union{a = list(),
%% types = [type()]}
@@ -102,6 +106,11 @@
-record(t_nil, {a=[]}). % empty-list constant '[]'
+%% @type t_nonempty_list() = #t_nonempty_list{a = list(),
+%% type = type()}
+
+-record(t_nonempty_list, {a=[], type}). % list type '[type, ...]'
+
%% @type t_atom() = #t_atom{a = list(),
%% val = atom()}
@@ -112,19 +121,37 @@
-record(t_integer, {a=[], val}). % integer constant
+%% @type t_integer_range() = #t_integer_range{a = list(),
+%% from = integer(),
+%% to = integer()}
+
+-record(t_integer_range, {a=[], from, to}).
+
+%% @type t_binary() = #t_binary{a = list(),
+%% base_size = integer(),
+%% unit_size = integer()}
+
+-record(t_binary, {a=[], base_size = 0, unit_size = 0}).
+
%% @type t_float() = #t_float{a = list(),
%% val = float()}
-record(t_float, {a=[], val}). % floating-point constant
%% @type t_record() = #t_list{a = list(),
-%% name = type(),
+%% name = t_atom(),
%% fields = [field()]}
--record(t_record, {a=[], name, fields = []}). % record type '#r{f1,...,fN}'
+-record(t_record, {a=[], % record "type" '#r{f1,...,fN}'
+ name,
+ fields = []}).
%% @type t_field() = #t_field{a = list(),
%% name = type(),
%% type = type()}
-record(t_field, {a=[], name, type}). % named field 'n1=t1'
+
+%% @type t_paren() = #t_paren{a = list(), type = type()}
+
+-record(t_paren, {a=[], type}). % parentheses
diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl
index e4a3d74734..9a31bc9a82 100644
--- a/lib/edoc/src/edoc_wiki.erl
+++ b/lib/edoc/src/edoc_wiki.erl
@@ -82,7 +82,8 @@ parse_xml(Data, Line) ->
parse_xml_1(Text, Line) ->
Text1 = "<doc>" ++ Text ++ "</doc>",
- case catch {ok, xmerl_scan:string(Text1, [{line, Line}])} of
+ Options = [{line, Line}, {encoding, "iso-8859-1"}],
+ case catch {ok, xmerl_scan:string(Text1, Options)} of
{ok, {E, _}} ->
E#xmlElement.content;
{'EXIT', {fatal, {Reason, L, _C}}} ->
@@ -295,6 +296,8 @@ push_uri(Us, Ss, As) ->
strip_empty_lines(Cs) ->
strip_empty_lines(Cs, 0).
+strip_empty_lines([], N) ->
+ {[], N}; % reached the end of input
strip_empty_lines(Cs, N) ->
{Cs1, Cs2} = edoc_lib:split_at(Cs, $\n),
case edoc_lib:is_space(Cs1) of
diff --git a/lib/edoc/test/Makefile b/lib/edoc/test/Makefile
index 4ce9799f6d..2dbdb77eff 100644
--- a/lib/edoc/test/Makefile
+++ b/lib/edoc/test/Makefile
@@ -59,8 +59,8 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
- $(INSTALL_DATA) edoc.spec $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) edoc.spec edoc.cover $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/edoc/test/edoc.cover b/lib/edoc/test/edoc.cover
new file mode 100644
index 0000000000..50140fafde
--- /dev/null
+++ b/lib/edoc/test/edoc.cover
@@ -0,0 +1,2 @@
+{incl_app,edoc,details}.
+
diff --git a/lib/edoc/test/edoc.spec b/lib/edoc/test/edoc.spec
index 8443a28028..8371427270 100644
--- a/lib/edoc/test/edoc.spec
+++ b/lib/edoc/test/edoc.spec
@@ -1 +1 @@
-{topcase, {dir, "../edoc_test"}}.
+{suites,"../edoc_test",all}.
diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl
index ea833f89b2..0d57591e3e 100644
--- a/lib/edoc/test/edoc_SUITE.erl
+++ b/lib/edoc/test/edoc_SUITE.erl
@@ -17,17 +17,36 @@
%%
-module(edoc_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%% Test cases
-export([build_std/1]).
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[build_std].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
build_std(suite) ->
[];
build_std(doc) ->
diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk
index 2de2641b4a..febac9cc42 100644
--- a/lib/edoc/vsn.mk
+++ b/lib/edoc/vsn.mk
@@ -1 +1 @@
-EDOC_VSN = 0.7.6.6
+EDOC_VSN = 0.7.7
diff --git a/lib/erl_docgen/Makefile b/lib/erl_docgen/Makefile
index c5bed632a5..93a6353cac 100644
--- a/lib/erl_docgen/Makefile
+++ b/lib/erl_docgen/Makefile
@@ -1,19 +1,20 @@
-# ``The contents of this file are subject to the Erlang Public License,
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
-# retrieved via the world wide web at http://www.erlang.org/.
+# retrieved online at http://www.erlang.org/.
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
#
-# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-# AB. All Rights Reserved.''
-#
-# $Id$
+# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -22,9 +23,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Macros
#
-SUB_DIRECTORIES = priv
+SUB_DIRECTORIES = src priv
#doc/src
+include vsn.mk
+VSN = $(ERL_DOCGEN_VSN)
SPECIAL_TARGETS =
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index ee74e598d9..7c8a2c8208 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,7 +29,84 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the erl_docgen application.</p>
- <section><title>Erl_Docgen 0.2</title>
+
+ <section><title>erl_docgen 0.2.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Subsections below level 2 where not handled correct when generating html and pdf.</p>
+ <p>
+ Own Id: OTP-90730</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>erl_docgen 0.2.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix format_man_pages so it handles all man sections
+ and remove warnings/errors in various man pages. </p>
+ <p>
+ Own Id: OTP-8600</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Support for using Dialyzer specifications and types
+ has been added. This is an experimental release; changes
+ are expected before the new functionality is used when
+ building the OTP documentation. </p>
+ <p>
+ Own Id: OTP-8720</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>erl_docgen 0.2.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fixed the transformation from xml to html of the
+ funcs block in comref pages. </p>
+ <p>
+ Own Id: OTP-8792</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>erl_docgen 0.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The text within code examples (CODE and PRE tags) will not be
+ space normalized for man pages.
+ </p>
+ <p>
+ Own Id: OTP-8476
+ </p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>erl_docgen 0.2</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/erl_docgen/ebin/.gitignore b/lib/erl_docgen/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/erl_docgen/ebin/.gitignore
diff --git a/lib/erl_docgen/priv/bin/specs_gen.escript b/lib/erl_docgen/priv/bin/specs_gen.escript
new file mode 100644
index 0000000000..840fed6dd5
--- /dev/null
+++ b/lib/erl_docgen/priv/bin/specs_gen.escript
@@ -0,0 +1,129 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%% <script> [-I<dir>]... [-o<dir>] [-module Module] [File]
+%%%
+%%% Use EDoc and the layout module 'otp_specs' to create an XML file
+%%% containing Dialyzer types and specifications (-type, -spec).
+%%%
+%%% Options:
+%%%
+%%% "-o<dir>" The output directory for the created file.
+%%% Default is ".".
+%%% "-I<dir>" Directory to be searched when including a file.
+%%% "-module Module"
+%%% Module name to use when there is no File argument.
+%%% A temporary file will be created.
+%%% Exactly one of -module Module and File must be given.
+%%%
+%%% The name of the generated file is "specs_<module>.xml". Its exact
+%%% format is not further described here.
+
+main(Args) ->
+ case catch parse(Args, [], ".", no_module) of
+ {ok, FileSpec, InclFs, Dir} ->
+ call_edoc(FileSpec, InclFs, Dir);
+ {error, Msg} ->
+ io:format("~s\n", [Msg]),
+ usage()
+ end.
+
+parse(["-o"++Dir | Opts], InclFs, _, Module) ->
+ parse(Opts, InclFs, Dir, Module);
+parse(["-I"++I | Opts], InclFs, Dir, Module) ->
+ parse(Opts, [I | InclFs], Dir, Module);
+parse(["-module", Module | Opts], InclFs, Dir, _) ->
+ parse(Opts, InclFs, Dir, Module);
+parse([File], InclFs, Dir, no_module) ->
+ {ok, {file, File}, lists:reverse(InclFs), Dir};
+parse([_], _, _, _) ->
+ {error, io_lib:format("Cannot have both -module option and file", [])};
+parse([], _, _, no_module) ->
+ {error, io_lib:format("Missing -module option or file", [])};
+parse([], InclFs, Dir, Module) ->
+ {ok, {module, Module}, lists:reverse(InclFs), Dir};
+parse(Args, _, _, _) ->
+ {error, io_lib:format("Bad arguments: ~p", [Args])}.
+
+usage() ->
+ io:format("usage: ~s [-I<include_dir>]... [-o<out_dir>] "
+ "[-module <module>] [file]\n", [escript:script_name()]),
+ halt(1).
+
+call_edoc(FileSpec, InclFs, Dir) ->
+ Incl = [{includes, InclFs}],
+ Pre = [{preprocess, true}],
+ Choice = [{dialyzer_specs, all}],
+ DirOpt = [{dir, Dir}],
+ Pretty = [{pretty_print, erl_pp}],
+ Layout = [{layout, otp_specs},
+ {file_suffix, ".specs"},
+ {stylesheet, ""}],
+ Warn = [{report_missing_type, false},
+ {report_type_mismatch, false}],
+ OptionList = (DirOpt ++ Choice ++ Pre ++ Warn ++ Pretty ++ Layout ++ Incl),
+ {File, TmpFile} = case FileSpec of
+ {file, File0} ->
+ {File0, false};
+ {module, Module} ->
+ {create_tmp_file(Dir, Module), true}
+ end,
+ try edoc:files([File], OptionList) of
+ ok ->
+ clean_up(Dir, File, TmpFile),
+ rename(Dir, File)
+ catch
+ _:_ ->
+ io:format("EDoc could not process file '~s'\n", [File]),
+ clean_up(Dir, File, TmpFile),
+ halt(3)
+ end.
+
+rename(Dir, F) ->
+ Mod = filename:basename(F, ".erl"),
+ Old = filename:join(Dir, Mod ++ ".specs"),
+ New = filename:join(Dir, "specs_" ++ Mod ++ ".xml"),
+ case file:rename(Old, New) of
+ ok ->
+ ok;
+ {error, R} ->
+ R1 = file:format_error(R),
+ io:format("could not rename file '~s': ~s\n", [New, R1]),
+ halt(2)
+ end.
+
+clean_up(Dir, File, TmpFile) ->
+ [file:delete(File) || TmpFile],
+ _ = [file:delete(filename:join(Dir, F)) ||
+ F <- ["packages-frame.html",
+ "overview-summary.html",
+ "modules-frame.html",
+ "index.html", "erlang.png", "edoc-info"]],
+ ok.
+
+create_tmp_file(Dir, Module) ->
+ TmpFile = filename:join(Dir, Module++".erl"),
+ case file:write_file(TmpFile, "-module(" ++ Module ++ ").\n") of
+ ok ->
+ TmpFile;
+ {error, R} ->
+ R1 = file:format_error(R),
+ io:format("could not write file '~s': ~s\n", [TmpFile, R1]),
+ halt(2)
+ end.
diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript
new file mode 100755
index 0000000000..13671ef2f8
--- /dev/null
+++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript
@@ -0,0 +1,105 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+
+%%% Find all applications and all modules given a root directory.
+%%% Output an XML file that can be used for finding which application
+%%% a given module belongs to.
+%%%
+%%% Options:
+%%%
+%%% "-topdir <D>" Applications are found under D/lib/.
+%%% The default value is $ERL_TOP.
+%%%
+%%% "-outfile <F>" Output is written onto F.
+%%% The default value is "mod2app.xml".
+%%%
+%%% The output file has the following format:
+%%%
+%%% <?xml version="1.0"?>
+%%% <mod2app>
+%%% <module name="ModName1">AppName1</module>
+%%% ...
+%%% <mod2app>
+%%%
+%%% meaning that module ModName1 resides in application AppName1.
+
+main(Args) ->
+ case catch parse(Args, os:getenv("ERL_TOP"), "mod2app.xml") of
+ {ok, TopDir, OutFile} ->
+ case modapp(TopDir) of
+ [] ->
+ io:format("no applications found\n"),
+ halt(3);
+ MA ->
+ Layout = layout(MA),
+ XML = xmerl:export_simple(Layout, xmerl_xml),
+ write_file(XML, OutFile)
+ end;
+ {error, Msg} ->
+ io:format("~s\n", [Msg]),
+ usage()
+ end.
+
+parse(["-topdir", TopDir | Opts], _, OutFile) ->
+ parse(Opts, TopDir, OutFile);
+parse(["-outfile", OutFile | Opts], TopDir, _) ->
+ parse(Opts, TopDir, OutFile);
+parse([], TopDir, OutFile) ->
+ {ok, TopDir, OutFile};
+parse([Opt | _], _, _) ->
+ {error, io_lib:format("Bad option: ~p", [Opt])}.
+
+usage() ->
+ io:format("usage: ~s [-topdir <dir>] [-outfile <file>]\n",
+ [escript:script_name()]),
+ halt(1).
+
+modapp(TopDir) ->
+ AppDirs = filelib:wildcard(filename:join([TopDir,"lib","*"])),
+ AM = [appmods(D) || D <- AppDirs],
+ lists:keysort(1, [{M,A} || {A,Ms} <- AM, M <- Ms]).
+
+%% It's OK if too much data is generated as long as all applications
+%% and all modules are mentioned.
+appmods(D) ->
+ ErlFiles = filelib:wildcard(filename:join([D,"src","*.erl"])),
+ AppV = filename:basename(D),
+ App = case string:rstr(AppV, "-") of
+ 0 -> AppV;
+ P -> string:sub_string(AppV, 1, P-1)
+ end,
+ {App, [filename:basename(EF, ".erl") || EF <- ErlFiles]}.
+
+-define(IND(N), lists:duplicate(N, $\s)).
+-define(NL, "\n").
+
+layout(MAL) ->
+ ML = lists:append([[?IND(2),{module,[{name,M}],[A]},?NL] || {M,A} <- MAL]),
+ [?NL,{mod2app,[?NL|ML]},?NL].
+
+write_file(Text, File) ->
+ case file:open(File, [write]) of
+ {ok, FD} ->
+ io:put_chars(FD, Text),
+ ok = file:close(FD);
+ {error, R} ->
+ R1 = file:format_error(R),
+ io:format("could not write file '~s': ~s\n", [File, R1]),
+ halt(2)
+ end.
diff --git a/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd b/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
index 7b9974fbda..c1237766e1 100644
--- a/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
+++ b/lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
@@ -26,15 +26,18 @@
<!ELEMENT description (%block;|quote|br|marker|warning|note)* >
<!ELEMENT funcs (func)+ >
-<!ELEMENT func (name+,fsummary,type?,desc?) >
+<!ELEMENT func (name+,type_desc+,fsummary,type?,desc?) >
<!-- ELEMENT name is defined in each ref dtd -->
<!ELEMENT fsummary (#PCDATA|c|em)* >
<!ELEMENT type (v,d?)+ >
<!ELEMENT v (#PCDATA) >
<!ELEMENT d (#PCDATA|c|em)* >
-<!ELEMENT desc (%block;|quote|br|marker|warning|note)* >
+<!ELEMENT desc (%block;|quote|br|marker|warning|note|anno)* >
<!ELEMENT authors (aname,email)+ >
<!ELEMENT aname (#PCDATA) >
<!ELEMENT email (#PCDATA) >
<!ELEMENT section (marker*,title,(%block;|quote|br|marker|
warning|note)*) >
+<!ELEMENT datatypes (datatype)+ >
+<!ELEMENT datatype (name+,desc?) >
+<!ELEMENT type_desc (#PCDATA) >
diff --git a/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd b/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
index 21656a1446..9905086ff4 100644
--- a/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
+++ b/lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
@@ -22,7 +22,7 @@
%common.refs;
<!ELEMENT erlref (header,module,modulesummary,description,
- (section|funcs)*,authors?) >
+ (section|funcs|datatypes)*,authors?) >
<!ELEMENT module (#PCDATA) >
<!ELEMENT modulesummary (#PCDATA) >
diff --git a/lib/erl_docgen/priv/dtd_man_entities/xhtml-lat1.ent b/lib/erl_docgen/priv/dtd_man_entities/xhtml-lat1.ent
index 3df9970a43..7a07e2c406 100644
--- a/lib/erl_docgen/priv/dtd_man_entities/xhtml-lat1.ent
+++ b/lib/erl_docgen/priv/dtd_man_entities/xhtml-lat1.ent
@@ -21,26 +21,26 @@
<!ENTITY sect "&#167;"> <!-- section sign, U+00A7 ISOnum -->
<!ENTITY uml "&#168;"> <!-- diaeresis = spacing diaeresis,
U+00A8 ISOdia -->
-<!ENTITY copy "&#169;"> <!-- copyright sign, U+00A9 ISOnum -->
+<!ENTITY copy "(C)"> <!-- copyright sign, U+00A9 ISOnum -->
<!ENTITY ordf "&#170;"> <!-- feminine ordinal indicator, U+00AA ISOnum -->
-<!ENTITY laquo "&#171;"> <!-- left-pointing double angle quotation mark
- = left pointing guillemet, U+00AB ISOnum -->
+<!ENTITY laquo "&#34;"> <!-- left-pointing double angle quotation mark
+ = left pointing guillemetn = " in man pages, U+00AB ISOnum -->
<!ENTITY not "&#172;"> <!-- not sign = discretionary hyphen,
U+00AC ISOnum -->
-<!ENTITY shy "&#173;"> <!-- soft hyphen = discretionary hyphen,
+<!ENTITY shy ""> <!-- soft hyphen = discretionary hyphen,
U+00AD ISOnum -->
-<!ENTITY reg "&#174;"> <!-- registered sign = registered trade mark sign,
+<!ENTITY reg "(R)"> <!-- registered sign = registered trade mark sign,
U+00AE ISOnum -->
<!ENTITY macr "&#175;"> <!-- macron = spacing macron = overline
= APL overbar, U+00AF ISOdia -->
<!ENTITY deg "&#176;"> <!-- degree sign, U+00B0 ISOnum -->
-<!ENTITY plusmn "&#177;"> <!-- plus-minus sign = plus-or-minus sign,
+<!ENTITY plusmn "+/-"> <!-- plus-minus sign = plus-or-minus sign,
U+00B1 ISOnum -->
<!ENTITY sup2 "&#178;"> <!-- superscript two = superscript digit two
= squared, U+00B2 ISOnum -->
<!ENTITY sup3 "&#179;"> <!-- superscript three = superscript digit three
= cubed, U+00B3 ISOnum -->
-<!ENTITY acute "&#180;"> <!-- acute accent = spacing acute,
+<!ENTITY acute "'"> <!-- acute accent = spacing acute,
U+00B4 ISOdia -->
<!ENTITY micro "&#181;"> <!-- micro sign, U+00B5 ISOnum -->
<!ENTITY para "&#182;"> <!-- pilcrow sign = paragraph sign,
@@ -62,134 +62,134 @@
= fraction three quarters, U+00BE ISOnum -->
<!ENTITY iquest "&#191;"> <!-- inverted question mark
= turned question mark, U+00BF ISOnum -->
-<!ENTITY Agrave "&#192;"> <!-- latin capital letter A with grave
+<!ENTITY Agrave "A"> <!-- latin capital letter A with grave
= latin capital letter A grave,
U+00C0 ISOlat1 -->
-<!ENTITY Aacute "&#193;"> <!-- latin capital letter A with acute,
+<!ENTITY Aacute "A"> <!-- latin capital letter A with acute,
U+00C1 ISOlat1 -->
-<!ENTITY Acirc "&#194;"> <!-- latin capital letter A with circumflex,
+<!ENTITY Acirc "A"> <!-- latin capital letter A with circumflex,
U+00C2 ISOlat1 -->
-<!ENTITY Atilde "&#195;"> <!-- latin capital letter A with tilde,
+<!ENTITY Atilde "A"> <!-- latin capital letter A with tilde,
U+00C3 ISOlat1 -->
-<!ENTITY Auml "&#196;"> <!-- latin capital letter A with diaeresis,
+<!ENTITY Auml "A"> <!-- latin capital letter A with diaeresis,
U+00C4 ISOlat1 -->
-<!ENTITY Aring "&#197;"> <!-- latin capital letter A with ring above
+<!ENTITY Aring "A"> <!-- latin capital letter A with ring above
= latin capital letter A ring,
U+00C5 ISOlat1 -->
-<!ENTITY AElig "&#198;"> <!-- latin capital letter AE
+<!ENTITY AElig "AE"> <!-- latin capital letter AE
= latin capital ligature AE,
U+00C6 ISOlat1 -->
-<!ENTITY Ccedil "&#199;"> <!-- latin capital letter C with cedilla,
+<!ENTITY Ccedil "C"> <!-- latin capital letter C with cedilla,
U+00C7 ISOlat1 -->
-<!ENTITY Egrave "&#200;"> <!-- latin capital letter E with grave,
+<!ENTITY Egrave "E"> <!-- latin capital letter E with grave,
U+00C8 ISOlat1 -->
-<!ENTITY Eacute "&#201;"> <!-- latin capital letter E with acute,
+<!ENTITY Eacute "E"> <!-- latin capital letter E with acute,
U+00C9 ISOlat1 -->
-<!ENTITY Ecirc "&#202;"> <!-- latin capital letter E with circumflex,
+<!ENTITY Ecirc "E"> <!-- latin capital letter E with circumflex,
U+00CA ISOlat1 -->
-<!ENTITY Euml "&#203;"> <!-- latin capital letter E with diaeresis,
+<!ENTITY Euml "E"> <!-- latin capital letter E with diaeresis,
U+00CB ISOlat1 -->
-<!ENTITY Igrave "&#204;"> <!-- latin capital letter I with grave,
+<!ENTITY Igrave "I"> <!-- latin capital letter I with grave,
U+00CC ISOlat1 -->
-<!ENTITY Iacute "&#205;"> <!-- latin capital letter I with acute,
+<!ENTITY Iacute "I"> <!-- latin capital letter I with acute,
U+00CD ISOlat1 -->
-<!ENTITY Icirc "&#206;"> <!-- latin capital letter I with circumflex,
+<!ENTITY Icirc "I"> <!-- latin capital letter I with circumflex,
U+00CE ISOlat1 -->
-<!ENTITY Iuml "&#207;"> <!-- latin capital letter I with diaeresis,
+<!ENTITY Iuml "I"> <!-- latin capital letter I with diaeresis,
U+00CF ISOlat1 -->
<!ENTITY ETH "&#208;"> <!-- latin capital letter ETH, U+00D0 ISOlat1 -->
-<!ENTITY Ntilde "&#209;"> <!-- latin capital letter N with tilde,
+<!ENTITY Ntilde "N"> <!-- latin capital letter N with tilde,
U+00D1 ISOlat1 -->
-<!ENTITY Ograve "&#210;"> <!-- latin capital letter O with grave,
+<!ENTITY Ograve "O"> <!-- latin capital letter O with grave,
U+00D2 ISOlat1 -->
-<!ENTITY Oacute "&#211;"> <!-- latin capital letter O with acute,
+<!ENTITY Oacute "O"> <!-- latin capital letter O with acute,
U+00D3 ISOlat1 -->
-<!ENTITY Ocirc "&#212;"> <!-- latin capital letter O with circumflex,
+<!ENTITY Ocirc "O"> <!-- latin capital letter O with circumflex,
U+00D4 ISOlat1 -->
-<!ENTITY Otilde "&#213;"> <!-- latin capital letter O with tilde,
+<!ENTITY Otilde "O"> <!-- latin capital letter O with tilde,
U+00D5 ISOlat1 -->
-<!ENTITY Ouml "&#214;"> <!-- latin capital letter O with diaeresis,
+<!ENTITY Ouml "O"> <!-- latin capital letter O with diaeresis,
U+00D6 ISOlat1 -->
-<!ENTITY times "&#215;"> <!-- multiplication sign, U+00D7 ISOnum -->
+<!ENTITY times "x"> <!-- multiplication sign, U+00D7 ISOnum -->
<!ENTITY Oslash "&#216;"> <!-- latin capital letter O with stroke
= latin capital letter O slash,
U+00D8 ISOlat1 -->
-<!ENTITY Ugrave "&#217;"> <!-- latin capital letter U with grave,
+<!ENTITY Ugrave "U"> <!-- latin capital letter U with grave,
U+00D9 ISOlat1 -->
-<!ENTITY Uacute "&#218;"> <!-- latin capital letter U with acute,
+<!ENTITY Uacute "U"> <!-- latin capital letter U with acute,
U+00DA ISOlat1 -->
-<!ENTITY Ucirc "&#219;"> <!-- latin capital letter U with circumflex,
+<!ENTITY Ucirc "U"> <!-- latin capital letter U with circumflex,
U+00DB ISOlat1 -->
-<!ENTITY Uuml "&#220;"> <!-- latin capital letter U with diaeresis,
+<!ENTITY Uuml "U"> <!-- latin capital letter U with diaeresis,
U+00DC ISOlat1 -->
-<!ENTITY Yacute "&#221;"> <!-- latin capital letter Y with acute,
+<!ENTITY Yacute "Y"> <!-- latin capital letter Y with acute,
U+00DD ISOlat1 -->
<!ENTITY THORN "&#222;"> <!-- latin capital letter THORN,
U+00DE ISOlat1 -->
<!ENTITY szlig "&#223;"> <!-- latin small letter sharp s = ess-zed,
U+00DF ISOlat1 -->
-<!ENTITY agrave "&#224;"> <!-- latin small letter a with grave
+<!ENTITY agrave "a"> <!-- latin small letter a with grave
= latin small letter a grave,
U+00E0 ISOlat1 -->
-<!ENTITY aacute "&#225;"> <!-- latin small letter a with acute,
+<!ENTITY aacute "a"> <!-- latin small letter a with acute,
U+00E1 ISOlat1 -->
-<!ENTITY acirc "&#226;"> <!-- latin small letter a with circumflex,
+<!ENTITY acirc "a"> <!-- latin small letter a with circumflex,
U+00E2 ISOlat1 -->
-<!ENTITY atilde "&#227;"> <!-- latin small letter a with tilde,
+<!ENTITY atilde "a"> <!-- latin small letter a with tilde,
U+00E3 ISOlat1 -->
-<!ENTITY auml "&#228;"> <!-- latin small letter a with diaeresis,
+<!ENTITY auml "a"> <!-- latin small letter a with diaeresis,
U+00E4 ISOlat1 -->
-<!ENTITY aring "&#229;"> <!-- latin small letter a with ring above
+<!ENTITY aring "a"> <!-- latin small letter a with ring above
= latin small letter a ring,
U+00E5 ISOlat1 -->
-<!ENTITY aelig "&#230;"> <!-- latin small letter ae
+<!ENTITY aelig "ae"> <!-- latin small letter ae
= latin small ligature ae, U+00E6 ISOlat1 -->
-<!ENTITY ccedil "&#231;"> <!-- latin small letter c with cedilla,
+<!ENTITY ccedil "c"> <!-- latin small letter c with cedilla,
U+00E7 ISOlat1 -->
-<!ENTITY egrave "&#232;"> <!-- latin small letter e with grave,
+<!ENTITY egrave "e"> <!-- latin small letter e with grave,
U+00E8 ISOlat1 -->
-<!ENTITY eacute "&#233;"> <!-- latin small letter e with acute,
+<!ENTITY eacute "e"> <!-- latin small letter e with acute,
U+00E9 ISOlat1 -->
-<!ENTITY ecirc "&#234;"> <!-- latin small letter e with circumflex,
+<!ENTITY ecirc "e"> <!-- latin small letter e with circumflex,
U+00EA ISOlat1 -->
-<!ENTITY euml "&#235;"> <!-- latin small letter e with diaeresis,
+<!ENTITY euml "e"> <!-- latin small letter e with diaeresis,
U+00EB ISOlat1 -->
-<!ENTITY igrave "&#236;"> <!-- latin small letter i with grave,
+<!ENTITY igrave "i"> <!-- latin small letter i with grave,
U+00EC ISOlat1 -->
-<!ENTITY iacute "&#237;"> <!-- latin small letter i with acute,
+<!ENTITY iacute "i"> <!-- latin small letter i with acute,
U+00ED ISOlat1 -->
-<!ENTITY icirc "&#238;"> <!-- latin small letter i with circumflex,
+<!ENTITY icirc "i"> <!-- latin small letter i with circumflex,
U+00EE ISOlat1 -->
-<!ENTITY iuml "&#239;"> <!-- latin small letter i with diaeresis,
+<!ENTITY iuml "i"> <!-- latin small letter i with diaeresis,
U+00EF ISOlat1 -->
<!ENTITY eth "&#240;"> <!-- latin small letter eth, U+00F0 ISOlat1 -->
-<!ENTITY ntilde "&#241;"> <!-- latin small letter n with tilde,
+<!ENTITY ntilde "n"> <!-- latin small letter n with tilde,
U+00F1 ISOlat1 -->
-<!ENTITY ograve "&#242;"> <!-- latin small letter o with grave,
+<!ENTITY ograve "o"> <!-- latin small letter o with grave,
U+00F2 ISOlat1 -->
-<!ENTITY oacute "&#243;"> <!-- latin small letter o with acute,
+<!ENTITY oacute "o"> <!-- latin small letter o with acute,
U+00F3 ISOlat1 -->
-<!ENTITY ocirc "&#244;"> <!-- latin small letter o with circumflex,
+<!ENTITY ocirc "o"> <!-- latin small letter o with circumflex,
U+00F4 ISOlat1 -->
-<!ENTITY otilde "&#245;"> <!-- latin small letter o with tilde,
+<!ENTITY otilde "o"> <!-- latin small letter o with tilde,
U+00F5 ISOlat1 -->
-<!ENTITY ouml "&#246;"> <!-- latin small letter o with diaeresis,
+<!ENTITY ouml "o"> <!-- latin small letter o with diaeresis,
U+00F6 ISOlat1 -->
<!ENTITY divide "&#247;"> <!-- division sign, U+00F7 ISOnum -->
-<!ENTITY oslash "&#248;"> <!-- latin small letter o with stroke,
+<!ENTITY oslash "o"> <!-- latin small letter o with stroke,
= latin small letter o slash,
U+00F8 ISOlat1 -->
-<!ENTITY ugrave "&#249;"> <!-- latin small letter u with grave,
+<!ENTITY ugrave "u"> <!-- latin small letter u with grave,
U+00F9 ISOlat1 -->
-<!ENTITY uacute "&#250;"> <!-- latin small letter u with acute,
+<!ENTITY uacute "u"> <!-- latin small letter u with acute,
U+00FA ISOlat1 -->
-<!ENTITY ucirc "&#251;"> <!-- latin small letter u with circumflex,
+<!ENTITY ucirc "u"> <!-- latin small letter u with circumflex,
U+00FB ISOlat1 -->
-<!ENTITY uuml "&#252;"> <!-- latin small letter u with diaeresis,
+<!ENTITY uuml "u"> <!-- latin small letter u with diaeresis,
U+00FC ISOlat1 -->
-<!ENTITY yacute "&#253;"> <!-- latin small letter y with acute,
+<!ENTITY yacute "y"> <!-- latin small letter y with acute,
U+00FD ISOlat1 -->
<!ENTITY thorn "&#254;"> <!-- latin small letter thorn with,
U+00FE ISOlat1 -->
-<!ENTITY yuml "&#255;"> <!-- latin small letter y with diaeresis,
+<!ENTITY yuml "y"> <!-- latin small letter y with diaeresis,
U+00FF ISOlat1 -->
diff --git a/lib/erl_docgen/priv/xsl/db_eix.xsl b/lib/erl_docgen/priv/xsl/db_eix.xsl
index 970b85ccb9..4545322bc2 100644
--- a/lib/erl_docgen/priv/xsl/db_eix.xsl
+++ b/lib/erl_docgen/priv/xsl/db_eix.xsl
@@ -3,7 +3,7 @@
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl
index 5614b02bb7..c6375ea621 100644
--- a/lib/erl_docgen/priv/xsl/db_html.xsl
+++ b/lib/erl_docgen/priv/xsl/db_html.xsl
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2010. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -17,15 +17,315 @@
# under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
+ xmlns:fn="http://www.w3.org/2005/02/xpath-functions">
<xsl:include href="db_html_params.xsl"/>
+ <!-- Start of Dialyzer type/spec tags.
+ See also the template matching "name" and the template "menu.funcs"
+ -->
+
+ <xsl:param name="specs_file" select="''"/>
+ <xsl:variable name="i" select="document($specs_file)"></xsl:variable>
+
+ <xsl:param name="mod2app_file" select="''"/>
+ <xsl:variable name="m2a" select="document($mod2app_file)"></xsl:variable>
+ <xsl:key name="mod2app" match="module" use="@name"/>
+
+ <xsl:template name="err">
+ <xsl:param name="m"/>
+ <xsl:param name="n"/>
+ <xsl:param name="a"/>
+ <xsl:param name="s"/>
+ <xsl:message terminate="yes">
+ Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if>
+ <xsl:value-of
+ select="$n"/>/<xsl:value-of
+ select="$a"/>: <xsl:value-of select="$s"/>
+ </xsl:message>
+ </xsl:template>
+
+ <xsl:template name="spec_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="arity" select="@arity"/>
+ <xsl:variable name="clause" select="@clause"/>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec" select="$spec0[string-length($clause) = 0
+ or position() = $clause]"/>
+ <xsl:if test="count($spec) = 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$arity"/>
+ <xsl:with-param name="s">unknown spec</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="arity_clause">
+ <xsl:choose>
+ <xsl:when test="string-length(@clause) > 0">
+ <xsl:value-of select="@arity"/>/<xsl:value-of select="@clause"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:message terminate="yes">
+ Error: did not expect a 'name' tag with name/arity attributes here!
+ </xsl:message>
+ </xsl:when>
+ <xsl:when test="ancestor::erlref">
+ <a name="{$name}-{$arity_clause}"></a>
+ <xsl:choose>
+ <xsl:when test="string(@with_guards) = 'no'">
+ <xsl:apply-templates select="$spec/contract/clause/head"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="contract">
+ <xsl:with-param name="contract" select="$spec/contract"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="contract">
+ <xsl:param name="contract"/>
+ <xsl:call-template name="clause">
+ <xsl:with-param name="clause" select="$contract/clause"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="clause">
+ <xsl:param name="clause"/>
+ <xsl:variable name="type_desc" select="../type_desc"/>
+ <xsl:for-each select="$clause">
+ <xsl:apply-templates select="head"/>
+ <xsl:if test="count(guard) > 0">
+ <xsl:call-template name="guard">
+ <xsl:with-param name="guard" select="guard"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="head">
+ <span class="bold_code">
+ <xsl:apply-templates/>
+ </span>
+ <br/>
+ </xsl:template>
+
+ <xsl:template name="guard">
+ <xsl:param name="guard"/>
+ <xsl:param name="type_desc"/>
+ <div class="REFBODY"><p>Types:</p>
+ <xsl:call-template name="subtype">
+ <xsl:with-param name="subtype" select="$guard/subtype"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </div>
+ </xsl:template>
+
+ <xsl:template name="subtype">
+ <xsl:param name="subtype"/>
+ <xsl:param name="type_desc"/>
+ <xsl:for-each select="$subtype">
+ <xsl:variable name="tname" select="typename"/>
+ <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/>
+ <div class="REFTYPES">
+ <span class="bold_code">
+ <xsl:apply-templates select="string"/>
+ </span>
+ </div>
+ <xsl:apply-templates select="$type_desc[@name = $tname]"/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Note: <type_desc> has not been implemented for data types. -->
+
+ <!-- Similar to <d> -->
+ <xsl:template match="type_desc">
+ <div class="REFBODY">
+ <xsl:apply-templates/>
+ </div>
+ </xsl:template>
+
+ <!-- This is for debugging. All modules! -->
+ <xsl:template match="all_etypes">
+ <xsl:for-each select= "$i//type">
+ <pre>
+ <span class="bold_code">
+ <xsl:apply-templates select="typedecl"/>
+ </span><xsl:text>
+</xsl:text>
+ </pre>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Datatypes -->
+ <xsl:template match="datatypes">
+ <h3>
+ <xsl:text>DATA TYPES</xsl:text>
+ </h3>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- Datatype -->
+ <xsl:template match="datatype">
+ <p><xsl:apply-templates select="name"/></p>
+ <xsl:apply-templates select="desc"/>
+ </xsl:template>
+
+ <xsl:template match="typehead">
+ <span class="bold_code">
+ <xsl:apply-templates/>
+ </span><br/>
+ </xsl:template>
+
+ <!-- local_defs -->
+ <xsl:template match="local_defs">
+ <div class="REFBODY">
+ <xsl:apply-templates>
+ </xsl:apply-templates>
+ </div>
+ </xsl:template>
+
+ <xsl:template match="local_def">
+ <div class="REFTYPES">
+ <span class="bold_code">
+ <xsl:apply-templates/>
+ </span>
+ </div>
+ </xsl:template>
+
+ <xsl:template name="type_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length(@n_vars) > 0">
+ <xsl:value-of select="@n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="string-length($name) > 0">
+ <xsl:variable name="type" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]"/>
+
+ <xsl:if test="count($type) != 1">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n_vars"/>
+ <xsl:with-param name="s">unknown type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="$type/typedecl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <span class="bold_code">
+ <xsl:value-of select="."/>
+ </span>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Used both in <datatype> and in <func>! -->
+ <xsl:template match="anno">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="anno" select="normalize-space(text())"/>
+ <xsl:variable name="namespec"
+ select="ancestor::desc/preceding-sibling::name"/>
+ <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>)
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="mod" select="$namespec/@mod"/>
+ <xsl:variable name="name" select="$namespec/@name"/>
+ <xsl:variable name="arity" select="$namespec/@arity"/>
+ <xsl:variable name="clause" select="$namespec/@clause"/>
+ <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length($tmp_n_vars) > 0">
+ <xsl:value-of select="$tmp_n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec_annos" select=
+ "$spec0[string-length($clause) = 0
+ or position() = $clause]/anno[.=$anno]"/>
+ <xsl:variable name="type_annos" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/>
+
+ <xsl:if test="count($spec_annos) = 0
+ and count($type_annos) = 0
+ and string-length($specs_file) > 0">
+ <xsl:variable name="n">
+ <xsl:choose>
+ <xsl:when test="string-length($arity) = 0">
+ <xsl:value-of select="$n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n"/>
+ <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$anno"/>
+ </xsl:template>
+
+ <!-- Used for indentation of formatted types and specs -->
+ <xsl:template match="nbsp">
+ <xsl:text>&#160;</xsl:text>
+ </xsl:template>
+
+ <!-- End of Dialyzer type/spec tags -->
+
<!-- Page layout -->
<xsl:template name="pagelayout">
<xsl:param name="chapnum"/>
@@ -36,19 +336,19 @@
<title>Erlang -- <xsl:value-of select="header/title"/></title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
+
<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 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 ||
+ } else if( document.documentElement && ( document.documentElement.clientWidth ||
document.documentElement.clientHeight ) ) {
//IE 6+ in 'standards compliant mode'
myHeight = document.documentElement.clientHeight;
@@ -56,7 +356,7 @@
//IE 4 compatible
myHeight = document.body.clientHeight;
}
- return myHeight;
+ return myHeight;
}
function setscrollpos() {
@@ -64,16 +364,16 @@
document.getElementById("leftnav").scrollTop = objf.offsetTop - getWinHeight()/2;
}
- function addEvent(obj, evType, fn){
- if (obj.addEventListener){
- obj.addEventListener(evType, fn, true);
- return true;
- } else if (obj.attachEvent){
- var r = obj.attachEvent("on"+evType, fn);
- return r;
- } else {
- return false;
- }
+ function addEvent(obj, evType, fn){
+ if (obj.addEventListener){
+ obj.addEventListener(evType, fn, true);
+ return true;
+ } else if (obj.attachEvent){
+ var r = obj.attachEvent("on"+evType, fn);
+ return r;
+ } else {
+ return false;
+ }
}
addEvent(window, 'load', setscrollpos);
@@ -85,7 +385,7 @@
<xsl:with-param name="chapnum" select="$chapnum"/>
<xsl:with-param name="curModule" select="$curModule"/>
</xsl:call-template>
-
+
<div id="content">
<div class="innertube">
@@ -124,17 +424,17 @@
<xsl:if test="$lname = 'releasenotes'">
<!-- .../part -->
<xsl:call-template name="releasenotes.content" />
- </xsl:if>
+ </xsl:if>
<xsl:if test="$lname = 'part'">
<!-- .../part -->
<xsl:call-template name="part.content" />
- </xsl:if>
+ </xsl:if>
<xsl:if test="$lname = 'chapter'">
<!-- .../part/chapter -->
<xsl:call-template name="chapter.content">
<xsl:with-param name="chapnum" select="$chapnum"/>
</xsl:call-template>
- </xsl:if>
+ </xsl:if>
<xsl:if test="$lname = 'application'">
<!-- .../application -->
<xsl:call-template name="app.content" />
@@ -178,37 +478,37 @@
<small>
<xsl:if test="boolean(/book/parts/part)">
<a href="users_guide.html">User's Guide</a><br/>
- </xsl:if>
+ </xsl:if>
<xsl:if test="boolean(/book/applications)">
<a href="index.html">Reference Manual</a><br/>
- </xsl:if>
+ </xsl:if>
<xsl:if test="boolean(/book/releasenotes)">
<a href="release_notes.html">Release Notes</a><br/>
- </xsl:if>
+ </xsl:if>
<a href="{$pdfdir}/{$appname}-{$appver}.pdf">PDF</a><br/>
<a href="{$topdocdir}/index.html">Top</a>
</small>
</xsl:template>
-
+
<xsl:template name="menu_middle">
<!-- small>
<xsl:choose>
<xsl:when test="ancestor::parts">
<a href="users_guide_bibliography.html">Bibliography</a><br/>
<a href="users_guide_glossary.html">Glossary</a><br/>
- </xsl:when>
- <xsl:when test="ancestor::applications">
+ </xsl:when>
+ <xsl:when test="ancestor::applications">
<a href="ref_man_bibliography.html">Bibliography</a><br/>
<a href="ref_man_glossary.html">Glossary</a><br/>
- </xsl:when>
+ </xsl:when>
</xsl:choose>
</small -->
<br/>
<a href="javascript:openAllFlips()">Expand All</a><br/>
<a href="javascript:closeAllFlips()">Contract All</a>
- </xsl:template>
-
+ </xsl:template>
+
<!-- Book -->
<xsl:template match="/book">
@@ -243,7 +543,7 @@
<!-- Chapter/Section -->
<xsl:template match="chapter/section">
- <xsl:param name="chapnum"/>
+ <xsl:param name="chapnum"/>
<h3>
<a name="{generate-id(title)}">
<xsl:value-of select="$chapnum"/>.<xsl:number/>&#160;
@@ -256,8 +556,8 @@
</xsl:apply-templates>
</xsl:template>
- <!-- Chapter/Subsection -->
- <xsl:template match="chapter/section/section">
+ <!-- Subsections lvl 3 and ... -->
+ <xsl:template match="section/section">
<xsl:param name="chapnum"/>
<xsl:param name="sectnum"/>
<h4>
@@ -269,8 +569,6 @@
</xsl:apply-templates>
</xsl:template>
-
-
<!-- *ref/Section -->
<xsl:template match="erlref/section|cref/section|comref/section|fileref/section|appref/section">
<xsl:param name="chapnum"/>
@@ -302,7 +600,7 @@
<!-- Lists -->
-
+
<xsl:template match="list">
<xsl:param name="chapnum"/>
<ul>
@@ -330,7 +628,7 @@
</xsl:apply-templates>
</dl>
</xsl:template>
-
+
<xsl:template match="taglist/tag">
<xsl:param name="chapnum"/>
<dt>
@@ -377,7 +675,7 @@
</xsl:apply-templates>
</p>
</div>
- </div>
+ </div>
</xsl:template>
<!-- Paragraph -->
@@ -402,7 +700,7 @@
</xsl:template>
<xsl:template match="em">
- <strong><xsl:apply-templates/></strong>
+ <strong><xsl:apply-templates/></strong>
</xsl:template>
<!-- Code -->
@@ -507,7 +805,7 @@
<!-- Part -->
<xsl:template match="part">
<!-- Generate Glossary for Users Guide -->
- <!--xsl:call-template name="glossary">
+ <!--xsl:call-template name="glossary">
<xsl:with-param name="type">users_guide</xsl:with-param>
</xsl:call-template-->
@@ -530,9 +828,9 @@
<center><h4>Version <xsl:value-of select="$appver"/></h4></center>
<center><h4><xsl:value-of select="$gendate"/></h4></center>
-
+
<xsl:apply-templates select="chapter"/>
-
+
</xsl:template>
<!-- Menu.ug -->
@@ -565,10 +863,10 @@
</xsl:call-template>
</ul>
</div>
- </div>
+ </div>
</xsl:template>
-
-
+
+
<xsl:template name="menu.chapter">
<xsl:param name="entries"/>
<xsl:param name="chapnum"/>
@@ -596,7 +894,7 @@
<a href="{$chapter_file}.html">
Top of chapter
</a>
- </li>
+ </li>
<xsl:call-template name="menu.section">
<xsl:with-param name="entries"
select="section[title]"/>
@@ -623,7 +921,7 @@
<!-- Chapter (if top tag)-->
<xsl:template match="/chapter">
- <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
+ <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
<xsl:call-template name="pagelayout">
@@ -635,7 +933,7 @@
<!-- Chapter -->
<xsl:template match="chapter">
- <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
+ <xsl:document href="{substring-before(header/file, '.xml')}.html" method="html" encoding="UTF-8" indent="yes"
doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
<xsl:call-template name="pagelayout">
@@ -670,7 +968,7 @@
<xsl:template match="application">
<!-- Generate Glossary for Ref. Manual -->
- <!--xsl:call-template name="glossary">
+ <!--xsl:call-template name="glossary">
<xsl:with-param name="type">ref_man</xsl:with-param>
</xsl:call-template-->
@@ -678,7 +976,7 @@
<!--xsl:call-template name="bibliography">
<xsl:with-param name="type">ref_man</xsl:with-param>
</xsl:call-template-->
-
+
<xsl:document href="{$outdir}/index.html" method="html" encoding="UTF-8" indent="yes" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN">
@@ -695,9 +993,9 @@
<center><h4>Version <xsl:value-of select="$appver"/></h4></center>
<center><h4><xsl:value-of select="$gendate"/></h4></center>
-
+
<xsl:apply-templates select="erlref|cref|comref|fileref|appref"/>
-
+
</xsl:template>
<!-- Menu.ref -->
@@ -730,16 +1028,16 @@
</xsl:call-template>
</ul>
</div>
- </div>
+ </div>
</xsl:template>
-
-
+
+
<xsl:template name="menu.ref2">
<xsl:param name="entries"/>
<!--xsl:param name="genFuncMenu"/-->
<xsl:param name="curModule"/>
<xsl:for-each select="$entries">
-
+
<xsl:variable name="cval">
<xsl:choose>
<xsl:when test="local-name() = 'erlref'">
@@ -767,9 +1065,9 @@
<xsl:when test="local-name() = 'fileref'">false</xsl:when>
<xsl:when test="descendant::funcs">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:variable>
-
+
<xsl:variable name="expanded">
<xsl:choose>
<xsl:when test="$curModule = $cval">true</xsl:when>
@@ -796,7 +1094,7 @@
<a href="{$link_cval}.html">
Top of manual page
</a>
- </li>
+ </li>
<xsl:call-template name="menu.funcs">
<xsl:with-param name="entries"
select="funcs/func/name"/>
@@ -823,7 +1121,7 @@
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:for-each>
</xsl:template>
@@ -831,7 +1129,7 @@
<xsl:template name="menu.funcs">
<xsl:param name="entries"/>
<xsl:param name="basename"/>
-
+
<xsl:for-each select="$entries">
<xsl:choose>
@@ -840,74 +1138,97 @@
<xsl:choose>
<xsl:when test="string-length($fname) > 0">
<li title="{$fname}">
- <a href="{$basename}.html#{$fname}">
+ <a href="{$basename}.html#{$fname}">
<xsl:value-of select="$fname"/>()
</a>
- </li>
+ </li>
</xsl:when>
<xsl:otherwise>
<li title="{name/nametext}">
- <a href="{$basename}.html#{name/nametext}">
+ <a href="{$basename}.html#{name/nametext}">
<xsl:value-of select="nametext"/>()
- </a>
- </li>
+ </a>
+ </li>
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:when>
-
+
<xsl:when test="ancestor::erlref">
-
+
<xsl:variable name="tmpstring">
<xsl:value-of select="substring-before(substring-after(., '('), '->')"/>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:variable name="ustring">
<xsl:choose>
<xsl:when test="string-length($tmpstring) > 0">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmpstring"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="substring-after(., '(')"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:variable name="arity">
- <xsl:call-template name="calc-arity">
- <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
- <xsl:with-param name="no-of-pars" select="0"/>
- </xsl:call-template>
- </xsl:variable>
-
+ <xsl:choose>
+ <xsl:when test="string-length(@arity) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:choose>
+ <xsl:when test="string-length(@clause) > 0">
+ <xsl:value-of select="@arity"/>/<xsl:value-of select="@clause"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="calc-arity">
+ <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
+ <xsl:with-param name="no-of-pars" select="0"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
<xsl:variable name="fname">
- <xsl:variable name="fname1">
- <xsl:value-of select="substring-before(., '(')"/>
- </xsl:variable>
- <xsl:variable name="fname2">
- <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
- </xsl:variable>
<xsl:choose>
- <xsl:when test="string-length($fname2) > 0">
- <xsl:value-of select="$fname2"/>
+ <xsl:when test="string-length(@name) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:value-of select="@name"/>
</xsl:when>
<xsl:otherwise>
- <xsl:value-of select="$fname1"/>
+ <xsl:variable name="fname1">
+ <xsl:value-of select="substring-before(., '(')"/>
+ </xsl:variable>
+ <xsl:variable name="fname2">
+ <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="string-length($fname2) > 0">
+ <xsl:value-of select="$fname2"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fname1"/>
+ </xsl:otherwise>
+ </xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
-
+
<li title="{$fname}-{$arity}">
- <a href="{$basename}.html#{$fname}-{$arity}">
+ <a href="{$basename}.html#{$fname}-{$arity}">
<xsl:value-of select="$fname"/>/<xsl:value-of select="$arity"/>
</a>
- </li>
+ </li>
</xsl:when>
</xsl:choose>
-
+
</xsl:for-each>
</xsl:template>
@@ -1148,7 +1469,7 @@
<!-- Func -->
<xsl:template match="func">
<xsl:param name="partnum"/>
-
+
<p><xsl:apply-templates select="name"/></p>
<xsl:apply-templates select="fsummary|type|desc">
@@ -1157,34 +1478,50 @@
</xsl:template>
+
<xsl:template match="name">
+ <xsl:choose>
+ <!-- @arity is mandatory when referring to a specification -->
+ <xsl:when test="string-length(@arity) > 0">
+ <xsl:call-template name="spec_name"/>
+ </xsl:when>
+ <xsl:when test="ancestor::datatype">
+ <xsl:call-template name="type_name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="name">
<xsl:variable name="tmpstring">
<xsl:value-of select="substring-before(substring-after(., '('), '->')"/>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="ustring">
<xsl:choose>
<xsl:when test="string-length($tmpstring) > 0">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmpstring"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="substring-after(., '(')"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:variable name="arity">
<xsl:call-template name="calc-arity">
<xsl:with-param name="string" select="substring-before($ustring, ')')"/>
- <xsl:with-param name="no-of-pars" select="0"/>
+ <xsl:with-param name="no-of-pars" select="0"/>
</xsl:call-template>
- </xsl:variable>
-
+ </xsl:variable>
+
<xsl:choose>
<xsl:when test="ancestor::cref">
<a name="{substring-before(nametext, '(')}"><span class="bold_code"><xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/></span></a><br/>
@@ -1198,7 +1535,7 @@
<xsl:value-of select="substring-after($fname1, 'erlang:')"/>
</xsl:variable>
<xsl:choose>
- <xsl:when test="string-length($fname2) > 0">
+ <xsl:when test="string-length($fname2) > 0">
<xsl:value-of select="$fname2"/>
</xsl:when>
<xsl:otherwise>
@@ -1208,22 +1545,24 @@
</xsl:variable>
<a name="{$fname}-{$arity}"><span class="bold_code"><xsl:value-of select="."/></span></a><br/>
</xsl:when>
+ <xsl:otherwise>
+ <span class="bold_code"><xsl:value-of select="."/></span>
+ </xsl:otherwise>
</xsl:choose>
-
- </xsl:template>
+ </xsl:template>
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
- <div class="REFBODY"><p>Types:</p>
+ <div class="REFBODY"><p>Types:</p>
<xsl:apply-templates>
<xsl:with-param name="partnum" select="$partnum"/>
</xsl:apply-templates>
</div>
-
+
</xsl:template>
@@ -1282,16 +1621,37 @@
<xsl:variable name="modulepart"><xsl:value-of select="substring-before($filepart, ':')"/></xsl:variable>
<xsl:choose>
<xsl:when test="string-length($modulepart) > 0">
- <xsl:variable name="filepart1"><xsl:value-of select="substring-after($filepart, ':')"/></xsl:variable>
+ <xsl:variable name="filepart1"><xsl:value-of select="substring-after($filepart, ':')"/></xsl:variable>
<span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$modulepart}','{$filepart1}.html#{$linkpart}');"><xsl:apply-templates/></a></span>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
+ <!-- Dialyzer seealso (the application is unknown) -->
+ <xsl:when test="string-length($specs_file) > 0
+ and count($i/specs/module[@name=$filepart]) = 0">
+ <!-- Deemed to slow; use key() instead
+ <xsl:variable name="app"
+ select="$m2a/mod2app/module[@name=$filepart]"/>
+ -->
+ <xsl:variable name="reftext" select="text()"/>
+ <xsl:for-each select="$m2a">
+ <xsl:variable name="app" select="key('mod2app', $filepart)"/>
+ <xsl:choose>
+ <xsl:when test="string-length($app) > 0">
+ <span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$app}','{$filepart}.html');"><xsl:value-of select="$reftext"/></a></span>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Unknown application; no link -->
+ <xsl:value-of select="$reftext"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+ </xsl:when>
<xsl:when test="string-length($linkpart) > 0">
<span class="bold_code"><a href="{$filepart}.html#{$linkpart}"><xsl:apply-templates/></a></span>
</xsl:when>
- <xsl:otherwise>
- <span class="bold_code"><a href="{$filepart}.html"><xsl:apply-templates/></a></span>
+ <xsl:otherwise>
+ <span class="bold_code"><a href="{$filepart}.html"><xsl:apply-templates/></a></span>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
@@ -1304,16 +1664,16 @@
</xsl:when>
<xsl:otherwise>
<xsl:variable name="modulepart"><xsl:value-of select="substring-before(@marker, ':')"/></xsl:variable>
-
+
<xsl:choose>
<xsl:when test="string-length($modulepart) > 0">
- <xsl:variable name="filepart1"><xsl:value-of select="substring-after(@marker, ':')"/></xsl:variable>
+ <xsl:variable name="filepart1"><xsl:value-of select="substring-after(@marker, ':')"/></xsl:variable>
<span class="bold_code"><a href="javascript:erlhref('{$topdocdir}/../','{$modulepart}','{$filepart1}.html');"><xsl:apply-templates/></a></span>
</xsl:when>
<xsl:otherwise>
- <span class="bold_code"><a href="{@marker}.html"><xsl:apply-templates/></a></span>
+ <span class="bold_code"><a href="{@marker}.html"><xsl:apply-templates/></a></span>
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
@@ -1338,16 +1698,16 @@
<xsl:choose>
<xsl:when test="ancestor::parts">
<a href="users_guide_glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:when>
- <xsl:when test="ancestor::applications">
+ </xsl:when>
+ <xsl:when test="ancestor::applications">
<a href="ref_man_glossary.html#{@id}"><xsl:value-of select="@id"/></a>
- </xsl:when>
+ </xsl:when>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<a href="{$topdocdir}/glossary.html#{@id}"><xsl:value-of select="@id"/></a>
</xsl:otherwise>
- </xsl:choose -->
+ </xsl:choose -->
</xsl:template>
<xsl:template match="cite">
@@ -1371,9 +1731,9 @@
<center><h4>Version <xsl:value-of select="$appver"/></h4></center>
<center><h4><xsl:value-of select="$gendate"/></h4></center>
-
+
<xsl:apply-templates select="chapter"/>
-
+
</xsl:template>
<!-- Menu.rn -->
@@ -1406,7 +1766,7 @@
</xsl:call-template>
</ul>
</div>
- </div>
+ </div>
</xsl:template>
<!-- Glossary -->
@@ -1419,14 +1779,14 @@
<title>Erlang Documentation -- <xsl:value-of select="header/title"/></title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
+
<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>
<!-- Generate menu -->
<xsl:call-template name="menu"/>
-
+
<div id="content">
<div class="innertube">
<h1>Glossary</h1>
@@ -1474,14 +1834,14 @@
<title>Erlang Documentation -- <xsl:value-of select="header/title"/></title>
</head>
<body bgcolor="white" text="#000000" link="#0000ff" vlink="#ff00ff" alink="#ff0000">
-
+
<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>
<!-- Generate menu -->
<xsl:call-template name="menu"/>
-
+
<div id="content">
<div class="innertube">
<h1>Bibliography</h1>
@@ -1494,8 +1854,8 @@
<tr>
<td><xsl:value-of select="@id"/></td>
<td><xsl:value-of select="citedef"/></td>
- </tr>
- </xsl:if>
+ </tr>
+ </xsl:if>
</xsl:for-each>
</table>
@@ -1525,7 +1885,7 @@
<xsl:template name="calc-arity">
<xsl:param name="string"/>
<xsl:param name="no-of-pars"/>
-
+
<xsl:variable name="length">
<xsl:value-of select="string-length($string)"/>
</xsl:variable>
@@ -1534,8 +1894,8 @@
<xsl:when test="$length > 0">
<xsl:call-template name="calc-arity">
<xsl:with-param name="string" select="substring-after($string, ',')"/>
- <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
- </xsl:call-template>
+ <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$no-of-pars"/>
@@ -1550,9 +1910,9 @@
<xsl:variable name="str1">
<xsl:call-template name="remove-paren-1">
<xsl:with-param name="string" select="$string"/>
- <xsl:with-param name="start">(</xsl:with-param>
- <xsl:with-param name="end">)</xsl:with-param>
- </xsl:call-template>
+ <xsl:with-param name="start">(</xsl:with-param>
+ <xsl:with-param name="end">)</xsl:with-param>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str2">
@@ -1560,7 +1920,7 @@
<xsl:with-param name="string" select="$str1"/>
<xsl:with-param name="start">{</xsl:with-param>
<xsl:with-param name="end">}</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str3">
@@ -1568,7 +1928,7 @@
<xsl:with-param name="string" select="$str2"/>
<xsl:with-param name="start">[</xsl:with-param>
<xsl:with-param name="end">]</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="$str3"/>
@@ -1580,7 +1940,7 @@
<xsl:param name="string"/>
<xsl:param name="start"/>
<xsl:param name="end"/>
-
+
<xsl:variable name="tmp1">
<xsl:value-of select="substring-before($string, $start)"/>
</xsl:variable>
@@ -1593,7 +1953,7 @@
<xsl:variable name="retstring">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmp2"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(concat($tmp1, 'x'), $retstring)"/>
</xsl:when>
diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl
index a2b1e755e2..2a8fb9fe3e 100644
--- a/lib/erl_docgen/priv/xsl/db_man.xsl
+++ b/lib/erl_docgen/priv/xsl/db_man.xsl
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
@@ -17,24 +17,294 @@
# under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:preserve-space elements="code pre"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text" encoding="UTF-8" indent="no"/>
+ <!-- Start of Dialyzer type/spec tags. See also the template matching "name"
+ -->
+
+ <!-- Note: specs data for *one* module (as opposed to html and pdf) -->
+ <xsl:param name="specs_file" select="''"/>
+ <xsl:variable name="i" select="document($specs_file)"></xsl:variable>
+
+ <xsl:template name="err">
+ <xsl:param name="m"/>
+ <xsl:param name="n"/>
+ <xsl:param name="a"/>
+ <xsl:param name="s"/>
+ <xsl:message terminate="yes">
+ Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if>
+ <xsl:value-of
+ select="$n"/>/<xsl:value-of
+ select="$a"/>: <xsl:value-of select="$s"/>
+ </xsl:message>
+ </xsl:template>
+
+ <xsl:template name="spec_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="arity" select="@arity"/>
+ <xsl:variable name="clause" select="@clause"/>
+ <xsl:variable name="spec0" select=
+ "$i/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec" select="$spec0[string-length($clause) = 0
+ or position() = $clause]"/>
+ <xsl:if test="count($spec) = 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$arity"/>
+ <xsl:with-param name="s">unknown spec</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:message terminate="yes">
+ Error: did not expect a 'name' tag with name/arity attributes here!
+ </xsl:message>
+ </xsl:when>
+ <xsl:when test="ancestor::erlref">
+ <xsl:choose>
+ <xsl:when test="string(@with_guards) = 'no'">
+ <xsl:apply-templates select="$spec/contract/clause/head"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="contract">
+ <xsl:with-param name="contract" select="$spec/contract"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>&#10;.br</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="contract">
+ <xsl:param name="contract"/>
+ <xsl:call-template name="clause">
+ <xsl:with-param name="clause" select="$contract/clause"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="clause">
+ <xsl:param name="clause"/>
+ <xsl:variable name="type_desc" select="../type_desc"/>
+ <xsl:for-each select="$clause">
+ <xsl:apply-templates select="head"/>
+ <xsl:if test="count(guard) > 0">
+ <xsl:call-template name="guard">
+ <xsl:with-param name="guard" select="guard"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="head">
+ <xsl:text>&#10;.nf&#10;</xsl:text>
+ <xsl:text>&#10;.B&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:text>&#10;.fi</xsl:text>
+ </xsl:template>
+
+ <xsl:template name="guard">
+ <xsl:param name="guard"/>
+ <xsl:param name="type_desc"/>
+ <xsl:text>&#10;.RS</xsl:text>
+ <xsl:text>&#10;.TP</xsl:text>
+ <xsl:text>&#10;Types</xsl:text>
+ <xsl:call-template name="subtype">
+ <xsl:with-param name="subtype" select="$guard/subtype"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ <xsl:text>&#10;.RE</xsl:text>
+ </xsl:template>
+
+ <xsl:template name="subtype">
+ <xsl:param name="subtype"/>
+ <xsl:param name="type_desc"/>
+ <xsl:for-each select="$subtype">
+ <xsl:variable name="tname" select="typename"/>
+ <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/>
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates select="string"/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:apply-templates select="$type_desc[@name = $tname]"/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Note: <type_desc> has not been implemented for data types. -->
+
+ <!-- Similar to <d> -->
+ <xsl:template match="type_desc">
+ <xsl:text>&#10;</xsl:text><xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ </xsl:template>
+
+ <!-- Datatypes -->
+ <xsl:template match="datatypes">
+ <xsl:text>&#10;.SH DATA TYPES</xsl:text>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- Datatype -->
+ <xsl:template match="datatype">
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <xsl:template match="typehead">
+ <xsl:text>&#10;.nf&#10;</xsl:text>
+ <xsl:text>&#10;.B&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:text>&#10;.fi</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="local_defs">
+ <xsl:text>&#10;.RS</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.RE</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="local_def">
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ </xsl:template>
+
+ <xsl:template name="type_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length(@n_vars) > 0">
+ <xsl:value-of select="@n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="string-length($name) > 0">
+ <xsl:variable name="type" select=
+ "$i/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]"/>
+
+ <xsl:if test="count($type) != 1">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n_vars"/>
+ <xsl:with-param name="s">unknown type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="$type/typedecl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#10;.nf&#10;</xsl:text>
+ <xsl:text>&#10;.B&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;.br</xsl:text>
+ <xsl:text>&#10;.fi</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Used both in <datatype> and in <func>! -->
+ <xsl:template match="anno">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="anno" select="normalize-space(text())"/>
+ <xsl:variable name="namespec"
+ select="ancestor::desc/preceding-sibling::name"/>
+ <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>)
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="mod" select="$namespec/@mod"/>
+ <xsl:variable name="name" select="$namespec/@name"/>
+ <xsl:variable name="arity" select="$namespec/@arity"/>
+ <xsl:variable name="clause" select="$namespec/@clause"/>
+ <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length($tmp_n_vars) > 0">
+ <xsl:value-of select="$tmp_n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="spec0" select=
+ "$i/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec_annos" select=
+ "$spec0[string-length($clause) = 0
+ or position() = $clause]/anno[.=$anno]"/>
+ <xsl:variable name="type_annos" select=
+ "$i/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/>
+
+ <xsl:if test="count($spec_annos) = 0
+ and count($type_annos) = 0
+ and string-length($specs_file) > 0">
+ <xsl:variable name="n">
+ <xsl:choose>
+ <xsl:when test="string-length($arity) = 0">
+ <xsl:value-of select="$n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n"/>
+ <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$anno"/>
+ </xsl:template>
+
+ <!-- Used for indentation of formatted types and specs -->
+ <xsl:template match="nbsp">
+ <xsl:text> </xsl:text>
+ </xsl:template>
+
+ <!-- End of Dialyzer type/spec tags -->
+
<!-- Header -->
<xsl:template match="header">
</xsl:template>
-
+
<!-- Section/Title -->
<xsl:template match="section/title">
</xsl:template>
-
+
<!-- *ref/Section -->
<xsl:template match="erlref/section|comref/section|cref/section|fileref/section|appref/section">
<xsl:text>&#10;.SH "</xsl:text><xsl:value-of select="translate(title, 'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/><xsl:text>"&#10;</xsl:text>
@@ -49,11 +319,11 @@
<!-- Lists -->
-
+
<xsl:template match="list">
<xsl:text>&#10;.RS 2</xsl:text>
<xsl:apply-templates/>
- <xsl:text>&#10;.RE</xsl:text>
+ <xsl:text>&#10;.RE&#10;</xsl:text>
</xsl:template>
<xsl:template match="list/item">
@@ -66,9 +336,9 @@
<xsl:template match="taglist">
<xsl:text>&#10;.RS 2</xsl:text>
<xsl:apply-templates select="tag|item"/>
- <xsl:text>&#10;.RE</xsl:text>
+ <xsl:text>&#10;.RE&#10;</xsl:text>
</xsl:template>
-
+
<xsl:template match="taglist/tag">
<xsl:text>&#10;.TP 2&#10;</xsl:text>
<xsl:text>.B&#10;</xsl:text>
@@ -76,7 +346,7 @@
</xsl:template>
<xsl:template match="taglist/item">
- <xsl:apply-templates/>
+ <xsl:apply-templates/>
</xsl:template>
<xsl:template match="item/p">
@@ -88,10 +358,10 @@
<xsl:value-of select="$content"/>
</xsl:when>
<xsl:otherwise>
- <xsl:text>&#10;.RS 2</xsl:text>
- <xsl:text>&#10;.LP&#10;&#10;.LP&#10;</xsl:text>
+ <xsl:text>&#10;.RS 2</xsl:text>
+ <xsl:text>&#10;.LP&#10;&#10;.LP&#10;</xsl:text>
<xsl:value-of select="$content"/>
- <xsl:text>&#10;.RE</xsl:text>
+ <xsl:text>&#10;.RE</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
@@ -171,7 +441,7 @@
<xsl:template match="application">
<xsl:apply-templates/>
</xsl:template>
-
+
<!-- Erlref -->
<xsl:template match="/erlref">
<xsl:variable name="companyname">
@@ -184,7 +454,7 @@
<xsl:text>.TH </xsl:text><xsl:value-of select="module"/><xsl:text> 3 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "Erlang Module Definition"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="module"/><xsl:text> \- </xsl:text><xsl:value-of select="modulesummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="module"/><xsl:text> \- </xsl:text><xsl:value-of select="modulesummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -199,7 +469,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="com"/><xsl:text> 1 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "User Commands"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="com"/><xsl:text> \- </xsl:text><xsl:value-of select="comsummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="com"/><xsl:text> \- </xsl:text><xsl:value-of select="comsummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -214,7 +484,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="lib"/><xsl:text> 3 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "C Library Functions"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="lib"/><xsl:text> \- </xsl:text><xsl:value-of select="libsummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="lib"/><xsl:text> \- </xsl:text><xsl:value-of select="libsummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -229,7 +499,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="file"/><xsl:text> 5 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "Files"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="file"/><xsl:text> \- </xsl:text><xsl:value-of select="filesummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="file"/><xsl:text> \- </xsl:text><xsl:value-of select="filesummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -244,7 +514,7 @@
</xsl:variable>
<xsl:text>.TH </xsl:text><xsl:value-of select="app"/><xsl:text> 7 "</xsl:text><xsl:value-of select="$appname"/><xsl:text> </xsl:text><xsl:value-of select="$appver"/><xsl:text>" "</xsl:text><xsl:value-of select="$companyname"/><xsl:text>" "Erlang Application Definition"&#10;</xsl:text>
<xsl:text>.SH NAME&#10;</xsl:text>
- <xsl:value-of select="app"/><xsl:text> \- </xsl:text><xsl:value-of select="appsummary"/><xsl:text>&#10;</xsl:text>
+ <xsl:value-of select="app"/><xsl:text> \- </xsl:text><xsl:value-of select="appsummary"/><xsl:text>&#10;</xsl:text>
<xsl:apply-templates/>
</xsl:template>
@@ -271,10 +541,26 @@
<!-- Func -->
<xsl:template match="func">
<xsl:text>&#10;.LP</xsl:text>
- <xsl:apply-templates/>
+ <xsl:apply-templates select="name"/>
+ <xsl:apply-templates select="fsummary|type|desc"/>
</xsl:template>
<xsl:template match="name">
+ <xsl:choose>
+ <!-- @arity is mandatory when referring to a specification -->
+ <xsl:when test="string-length(@arity) > 0">
+ <xsl:call-template name="spec_name"/>
+ </xsl:when>
+ <xsl:when test="ancestor::datatype">
+ <xsl:call-template name="type_name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="name">
<xsl:text>&#10;.B&#10;</xsl:text>
<xsl:apply-templates/>
<xsl:text>&#10;.br</xsl:text>
@@ -296,7 +582,7 @@
<xsl:text>&#10;</xsl:text><xsl:value-of select="normalize-space(text())"/>
<xsl:text>&#10;.br</xsl:text>
</xsl:template>
-
+
<!-- D -->
<xsl:template match="d">
<xsl:text>&#10;</xsl:text><xsl:apply-templates/>
@@ -316,7 +602,7 @@
<!-- This tag is skipped for now. -->
</xsl:template>
-
+
<!-- Authors -->
<xsl:template match="authors">
<xsl:text>&#10;.SH AUTHORS</xsl:text>
@@ -336,11 +622,28 @@
<xsl:text>&gt;</xsl:text>
</xsl:template>
+ <!-- Do not noramlize any text within pre and code tags. -->
+ <xsl:template match="pre/text()">
+ <xsl:call-template name="replace-string">
+ <xsl:with-param name="text" select="." />
+ <xsl:with-param name="replace" select="&quot;\&quot;" />
+ <xsl:with-param name="with" select="&quot;\\&quot;" />
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template match="code/text()">
+ <xsl:call-template name="replace-string">
+ <xsl:with-param name="text" select="." />
+ <xsl:with-param name="replace" select="&quot;\&quot;" />
+ <xsl:with-param name="with" select="&quot;\\&quot;" />
+ </xsl:call-template>
+ </xsl:template>
+
<!-- Replace ' by \&' ans . by \&. -->
<xsl:template match="text()">
<xsl:variable name="startstring">
<xsl:value-of select="normalize-space()"/><xsl:text> </xsl:text>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="rep1">
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$startstring" />
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index e12b4d219a..f500cd3fee 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
+<!--
#
# %CopyrightBegin%
#
- # Copyright Ericsson AB 2009-2010. All Rights Reserved.
+ # Copyright Ericsson AB 2009-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,7 @@
# under the License.
#
# %CopyrightEnd%
-
+
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
@@ -27,16 +27,310 @@
<xsl:include href="db_pdf_params.xsl"/>
+ <!-- Start of Dialyzer type/spec tags.
+ See also the template matching "name" and the template "bookmarks6"
+ -->
+
+ <xsl:param name="specs_file" select="''"/>
+ <xsl:variable name="i" select="document($specs_file)"></xsl:variable>
+
+ <xsl:template name="err">
+ <xsl:param name="m"/>
+ <xsl:param name="n"/>
+ <xsl:param name="a"/>
+ <xsl:param name="s"/>
+ <xsl:message terminate="yes">
+ Error <xsl:if test="$m != ''"><xsl:value-of select ="$m"/>:</xsl:if>
+ <xsl:value-of
+ select="$n"/>/<xsl:value-of
+ select="$a"/>: <xsl:value-of select="$s"/>
+ </xsl:message>
+ </xsl:template>
+
+ <xsl:template name="spec_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="arity" select="@arity"/>
+ <xsl:variable name="clause" select="@clause"/>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec" select="$spec0[string-length($clause) = 0
+ or position() = $clause]"/>
+ <xsl:if test="count($spec) = 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$arity"/>
+ <xsl:with-param name="s">unknown spec</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="ancestor::cref">
+ <xsl:message terminate="yes">
+ Error: did not expect a 'name' tag with name/arity attributes here!
+ </xsl:message>
+ </xsl:when>
+ <xsl:when test="ancestor::erlref">
+ <fo:block id="{generate-id()}">
+ <xsl:choose>
+ <xsl:when test="string(@with_guards) = 'no'">
+ <xsl:apply-templates select="$spec/contract/clause/head"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="contract">
+ <xsl:with-param name="contract" select="$spec/contract"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </fo:block>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="contract">
+ <xsl:param name="contract"/>
+ <xsl:call-template name="clause">
+ <xsl:with-param name="clause" select="$contract/clause"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <xsl:template name="clause">
+ <xsl:param name="clause"/>
+ <xsl:variable name="type_desc" select="../type_desc"/>
+ <xsl:for-each select="$clause">
+ <xsl:apply-templates select="head"/>
+ <xsl:if test="count(guard) > 0">
+ <xsl:call-template name="guard">
+ <xsl:with-param name="guard" select="guard"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:template>
+
+ <xsl:template match="head">
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:apply-templates/>
+ </fo:block>
+ </xsl:template>
+
+ <xsl:template name="guard">
+ <fo:block>
+ <xsl:text>Types:</xsl:text>
+ </fo:block>
+ <fo:list-block xsl:use-attribute-sets="type-listblock">
+ <xsl:call-template name="subtype">
+ <xsl:with-param name="subtype" select="$guard/subtype"/>
+ <xsl:with-param name="type_desc" select="$type_desc"/>
+ </xsl:call-template>
+ </fo:list-block>
+ </xsl:template>
+
+ <xsl:template name="subtype">
+ <xsl:param name="subtype"/>
+ <xsl:param name="type_desc"/>
+ <xsl:for-each select="$subtype">
+ <xsl:variable name="tname" select="typename"/>
+ <xsl:variable name="tdesc" select="$type_desc[@name = $tname]"/>
+ <fo:list-item xsl:use-attribute-sets="type-listitem">
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>
+ </fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()" format="justify">
+ <fo:block font-weight="bold">
+ <xsl:apply-templates select="string"/>
+ </fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ <xsl:apply-templates select="$type_desc[@name = $tname]"/>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Note: <type_desc> has not been implemented for data types. -->
+
+ <!-- Similar to <d> -->
+ <xsl:template match="type_desc">
+ <fo:list-item xsl:use-attribute-sets="type-listitem">
+ <fo:list-item-label end-indent="label-end()"><fo:block></fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()" format="justify">
+ <fo:block>
+ <xsl:apply-templates/>
+ </fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ </xsl:template>
+
+ <!-- Datatypes -->
+ <xsl:template match="datatypes">
+ <fo:block xsl:use-attribute-sets="h3">
+ <xsl:text>Data Types</xsl:text>
+ </fo:block>
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- Datatype -->
+ <xsl:template match="datatype">
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:apply-templates select="name"/>
+ </fo:block>
+ <xsl:apply-templates select="desc"/>
+ </xsl:template>
+
+ <!-- Like <head>... -->
+ <xsl:template match="typehead">
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:apply-templates/>
+ </fo:block>
+ </xsl:template>
+
+ <!-- Like <guard>, except "Types:"... -->
+ <xsl:template match="local_defs">
+ <fo:list-block xsl:use-attribute-sets="type-listblock">
+ <xsl:apply-templates/>
+ </fo:list-block>
+ </xsl:template>
+
+ <!-- Like <subtype>... -->
+ <xsl:template match="local_def">
+ <fo:list-item xsl:use-attribute-sets="type-listitem">
+ <fo:list-item-label end-indent="label-end()">
+ <fo:block>
+ </fo:block>
+ </fo:list-item-label>
+ <fo:list-item-body start-indent="body-start()" format="justify">
+ <fo:block font-weight="bold">
+ <xsl:apply-templates/>
+ </fo:block>
+ </fo:list-item-body>
+ </fo:list-item>
+ </xsl:template>
+
+ <xsl:template name="type_name">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="mod" select="@mod"/>
+ <xsl:variable name="name" select="@name"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length(@n_vars) > 0">
+ <xsl:value-of select="@n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="string-length($name) > 0">
+ <xsl:variable name="type" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]"/>
+
+ <xsl:if test="count($type) != 1">
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n_vars"/>
+ <xsl:with-param name="s">unknown type</xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:apply-templates select="$type/typedecl"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <fo:inline font-weight="bold" xsl:use-attribute-sets="type-listitem">
+ <xsl:value-of select="."/>
+ </fo:inline>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Used both in <datatype> and in <func>! -->
+ <xsl:template match="anno">
+ <xsl:variable name="curModule" select="ancestor::erlref/module"/>
+ <xsl:variable name="anno" select="normalize-space(text())"/>
+ <xsl:variable name="namespec"
+ select="ancestor::desc/preceding-sibling::name"/>
+ <xsl:if test="count($namespec) = 0 and string-length($specs_file) > 0">
+ <xsl:call-template name="err">
+ <xsl:with-param name="s">cannot find 'name' (<xsl:value-of select="$anno"/>)
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:variable name="mod" select="$namespec/@mod"/>
+ <xsl:variable name="name" select="$namespec/@name"/>
+ <xsl:variable name="arity" select="$namespec/@arity"/>
+ <xsl:variable name="clause" select="$namespec/@clause"/>
+ <xsl:variable name="tmp_n_vars" select="$namespec/@n_vars"/>
+ <xsl:variable name="n_vars">
+ <xsl:choose>
+ <xsl:when test="string-length($tmp_n_vars) > 0">
+ <xsl:value-of select="$tmp_n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="0"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="spec0" select=
+ "$i/specs/module[@name=$curModule]/spec
+ [name=$name and arity=$arity
+ and (string-length($mod) = 0 or module = $mod)]"/>
+ <xsl:variable name="spec_annos" select=
+ "$spec0[string-length($clause) = 0
+ or position() = $clause]/anno[.=$anno]"/>
+ <xsl:variable name="type_annos" select=
+ "$i/specs/module[@name=$curModule]/type
+ [name=$name and n_vars=$n_vars
+ and (string-length($mod) = 0 or module = $mod)]/anno[.=$anno]"/>
+
+ <xsl:if test="count($spec_annos) = 0
+ and count($type_annos) = 0
+ and string-length($specs_file) > 0">
+ <xsl:variable name="n">
+ <xsl:choose>
+ <xsl:when test="string-length($arity) = 0">
+ <xsl:value-of select="$n_vars"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$arity"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="err">
+ <xsl:with-param name="m" select="$mod"/>
+ <xsl:with-param name="n" select="$name"/>
+ <xsl:with-param name="a" select="$n"/>
+ <xsl:with-param name="s">unknown annotation <xsl:value-of select="$anno"/>
+ </xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:value-of select="$anno"/>
+ </xsl:template>
+
+ <!-- Used for indentation of formatted types and specs -->
+ <xsl:template match="nbsp">
+ <xsl:text>&#160;</xsl:text>
+ </xsl:template>
+
+ <!-- End of Dialyzer type/spec tags -->
<xsl:template match="/">
<xsl:apply-templates select="book"/>
</xsl:template>
-
+
<xsl:template match="book">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
- <!-- Master pages -->
+ <!-- Master pages -->
<fo:layout-master-set>
<fo:simple-page-master
master-name="cover"
@@ -47,7 +341,7 @@
<xsl:attribute name="page-width">
<xsl:value-of select="$page-width"/>
</xsl:attribute>
- <fo:region-body
+ <fo:region-body
margin="0mm"/>
</fo:simple-page-master>
@@ -63,7 +357,7 @@
<xsl:attribute name="page-width">
<xsl:value-of select="$page-width"/>
</xsl:attribute>
- <fo:region-body
+ <fo:region-body
margin-top="15mm"
margin-bottom="20mm"/>
<fo:region-before
@@ -100,10 +394,10 @@
<fo:page-sequence-master master-name="document">
<fo:repeatable-page-master-alternatives>
- <fo:conditional-page-master-reference
+ <fo:conditional-page-master-reference
master-reference="left-page"
odd-or-even="even"/>
- <fo:conditional-page-master-reference
+ <fo:conditional-page-master-reference
master-reference="right-page"
odd-or-even="odd"/>
</fo:repeatable-page-master-alternatives>
@@ -166,7 +460,7 @@
<fo:flow flow-name="xsl-region-body">
<fo:block>
-
+
</fo:block>
<xsl:apply-templates select="parts"/>
@@ -189,7 +483,7 @@
<!-- Cover page -->
<xsl:template match="header/title">
- <fo:page-sequence
+ <fo:page-sequence
font-family="sans-serif"
force-page-count="even"
master-reference="cover">
@@ -242,7 +536,7 @@
the License for the specific language governing rights and limitations
under the License.
- The Initial Developer of the Original Code is
+ The Initial Developer of the Original Code is
-->
<xsl:value-of select="$companyname"/>.
</fo:block>
@@ -281,22 +575,22 @@
<xsl:template name="bookmarks1">
<xsl:param name="entries"/>
<xsl:if test="$entries != ''">
-
+
<fo:bookmark internal-destination="{generate-id(/book/parts/part)}"
starting-state="hide">
<fo:bookmark-title>User's Guide</fo:bookmark-title>
-
+
<xsl:for-each select="$entries">
<xsl:call-template name="bookmarks2">
<xsl:with-param name="entries"
select="chapter[header/title]"/>
</xsl:call-template>
</xsl:for-each>
-
+
</fo:bookmark>
</xsl:if>
</xsl:template>
-
+
<xsl:template name="bookmarks2">
<xsl:param name="entries"/>
<xsl:for-each select="$entries">
@@ -341,7 +635,7 @@
starting-state="hide">
<fo:bookmark-title>Reference Manual</fo:bookmark-title>
<xsl:for-each select="$entries">
-
+
<xsl:call-template name="bookmarks5">
<xsl:with-param name="entries"
select="erlref[module]|comref[com]|cref[lib]|fileref[file]|appref[app]"/>
@@ -387,7 +681,7 @@
<fo:bookmark internal-destination="{generate-id(nametext)}" starting-state="hide">
<xsl:variable name="fname">
<xsl:value-of select="substring-before(nametext, '(')"/>
- </xsl:variable>
+ </xsl:variable>
<fo:bookmark-title>
<xsl:choose>
<xsl:when test="string-length($fname) > 0">
@@ -396,7 +690,7 @@
<xsl:otherwise>
<xsl:value-of select="nametext"/>()
</xsl:otherwise>
- </xsl:choose>
+ </xsl:choose>
</fo:bookmark-title>
</fo:bookmark>
</xsl:when>
@@ -404,60 +698,76 @@
<fo:bookmark internal-destination="{generate-id(.)}" starting-state="hide">
<xsl:variable name="tmpstring">
<xsl:value-of select="substring-before(substring-after(., '('), '->')"/>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="ustring">
<xsl:choose>
<xsl:when test="string-length($tmpstring) > 0">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmpstring"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="substring-after(., '(')"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
- </xsl:variable>
+ </xsl:variable>
<xsl:variable name="arity">
- <xsl:call-template name="calc-arity">
- <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
- <xsl:with-param name="no-of-pars" select="0"/>
- </xsl:call-template>
- </xsl:variable>
-
- <xsl:variable name="fname">
- <xsl:variable name="fname1">
- <xsl:value-of select="substring-before(., '(')"/>
- </xsl:variable>
- <xsl:variable name="fname2">
- <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
- </xsl:variable>
- <xsl:choose>
- <xsl:when test="string-length($fname2) > 0">
- <xsl:value-of select="$fname2"/>
- </xsl:when>
+ <xsl:choose>
+ <xsl:when test="string-length(@arity) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:value-of select="@arity"/>
+ </xsl:when>
<xsl:otherwise>
- <xsl:value-of select="$fname1"/>
+ <xsl:call-template name="calc-arity">
+ <xsl:with-param name="string" select="substring-before($ustring, ')')"/>
+ <xsl:with-param name="no-of-pars" select="0"/>
+ </xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
+ <xsl:variable name="fname">
+ <xsl:choose>
+ <xsl:when test="string-length(@name) > 0">
+ <!-- Dialyzer spec -->
+ <xsl:value-of select="@name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="fname1">
+ <xsl:value-of select="substring-before(., '(')"/>
+ </xsl:variable>
+ <xsl:variable name="fname2">
+ <xsl:value-of select="substring-after($fname1, 'erlang:')"/>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="string-length($fname2) > 0">
+ <xsl:value-of select="$fname2"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fname1"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
<fo:bookmark-title>
<xsl:value-of select="$fname"/>/<xsl:value-of select="$arity"/>
</fo:bookmark-title>
</fo:bookmark>
</xsl:when>
</xsl:choose>
-
+
</xsl:for-each>
</xsl:template>
<!-- UG part -->
-
+
<!-- Parts -->
<xsl:template match="parts">
<xsl:apply-templates select="part"/>
@@ -491,7 +801,7 @@
<xsl:value-of select="$partnum"/>.<xsl:number/>&#160;&#160;<xsl:value-of select="header/title"/>
</fo:marker>
<xsl:value-of select="$partnum"/>.<xsl:number/>&#160;&#160;<xsl:value-of select="header/title"/>
-
+
</fo:block>
<xsl:apply-templates select="section|quote|warning|note|br|image|marker|table|p|pre|code|list|taglist|codeinclude|erleval">
@@ -517,7 +827,7 @@
</xsl:template>
- <!-- Chapter/Subsection -->
+ <!-- Chapter/Subsection -->
<xsl:template match="chapter/section/section">
<xsl:param name="partnum"/>
<xsl:param name="chapnum"/>
@@ -534,6 +844,22 @@
</xsl:template>
+ <!-- Subsection below level 2 -->
+ <xsl:template match="section/section/section">
+ <xsl:param name="partnum"/>
+ <xsl:param name="chapnum"/>
+ <xsl:param name="sectnum"/>
+ <fo:block xsl:use-attribute-sets="h5" id="{generate-id(title)}">
+ <!-- xsl:value-of select="$partnum"/>.<xsl:value-of select="$chapnum"/>.<xsl:value-of select="$sectnum"/>.<xsl:number/ -->
+ <xsl:value-of select="title"/>
+ </fo:block>
+ <xsl:apply-templates>
+ <xsl:with-param name="partnum" select="$partnum"/>
+ <xsl:with-param name="chapnum" select="$chapnum"/>
+ <xsl:with-param name="sectnum" select="$sectnum"/>
+ </xsl:apply-templates>
+ </xsl:template>
+
<!-- *ref/Section -->
<xsl:template match="erlref/section|comref/section|cref/section|fileref/section|appref/section">
@@ -567,7 +893,7 @@
</xsl:template>
<!-- Lists -->
-
+
<xsl:template match="list">
<xsl:param name="partnum"/>
<fo:list-block xsl:use-attribute-sets="listblock">
@@ -692,7 +1018,7 @@
</xsl:variable>
<fo:block xsl:use-attribute-sets="code">
- <xsl:apply-templates select="text()"/>
+ <xsl:apply-templates select="text()"/>
</fo:block>
<xsl:if test="@caption">
@@ -711,7 +1037,7 @@
</xsl:variable>
<fo:block xsl:use-attribute-sets="code">
- <xsl:apply-templates/>
+ <xsl:apply-templates/>
</fo:block>
<xsl:if test="@caption">
@@ -734,23 +1060,23 @@
<xsl:variable name="partnum">
<xsl:number level="any" from="book" count="part|application"/>
</xsl:variable>
-
- <fo:block xsl:use-attribute-sets="h1" id="{generate-id()}">
+
+ <fo:block xsl:use-attribute-sets="h1" id="{generate-id()}">
<xsl:if test="/book/header/title">
<xsl:value-of select="$partnum"/>&#160;&#160;&#160;
<xsl:text>Reference Manual</xsl:text>
- </xsl:if>
+ </xsl:if>
</fo:block>
-
-
+
+
<xsl:apply-templates select="description">
<xsl:with-param name="partnum" select="$partnum"/>
</xsl:apply-templates>
-
+
<xsl:apply-templates select="erlref|comref|cref|fileref|appref">
<xsl:with-param name="partnum" select="$partnum"/>
</xsl:apply-templates>
-
+
</xsl:template>
<!-- Erlref -->
@@ -763,7 +1089,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="module"/>
</fo:marker>
- <xsl:value-of select="module"/>
+ <xsl:value-of select="module"/>
</fo:block>
<xsl:text>Erlang module</xsl:text>
</fo:block>
@@ -784,7 +1110,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="com"/>
</fo:marker>
- <xsl:value-of select="com"/>
+ <xsl:value-of select="com"/>
</fo:block>
<xsl:text>Command</xsl:text>
</fo:block>
@@ -805,7 +1131,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="lib"/>
</fo:marker>
- <xsl:value-of select="lib"/>
+ <xsl:value-of select="lib"/>
</fo:block>
<xsl:text>C Library</xsl:text>
</fo:block>
@@ -826,7 +1152,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="file"/>
</fo:marker>
- <xsl:value-of select="file"/>
+ <xsl:value-of select="file"/>
</fo:block>
<xsl:text>Name</xsl:text>
</fo:block>
@@ -847,7 +1173,7 @@
<fo:marker marker-class-name="chapter-title">
<xsl:value-of select="app"/>
</fo:marker>
- <xsl:value-of select="app"/>
+ <xsl:value-of select="app"/>
</fo:block>
<xsl:text>Application</xsl:text>
</fo:block>
@@ -900,9 +1226,7 @@
<xsl:template match="func">
<xsl:param name="partnum"/>
- <fo:block xsl:use-attribute-sets="function-name">
- <xsl:apply-templates select="name"/>
- </fo:block>
+ <xsl:apply-templates select="name"/>
<xsl:apply-templates select="fsummary|type|desc">
<xsl:with-param name="partnum" select="$partnum"/>
@@ -914,15 +1238,35 @@
<xsl:template match="name">
<xsl:param name="partnum"/>
<xsl:choose>
+ <!-- @arity is mandatory when referring to a specification -->
+ <xsl:when test="string-length(@arity) > 0">
+ <xsl:call-template name="spec_name"/>
+ </xsl:when>
+ <xsl:when test="ancestor::datatype">
+ <xsl:call-template name="type_name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <fo:block xsl:use-attribute-sets="function-name">
+ <xsl:call-template name="name">
+ <xsl:with-param name="partnum" select="$partnum"/>
+ </xsl:call-template>
+ </fo:block>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="name">
+ <xsl:param name="partnum"/>
+ <xsl:choose>
<xsl:when test="ancestor::cref">
<fo:block id="{generate-id(nametext)}">
- <xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/>
- </fo:block>
+ <xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/>
+ </fo:block>
</xsl:when>
<xsl:otherwise>
<fo:block id="{generate-id(.)}">
- <xsl:value-of select="."/>
- </fo:block>
+ <xsl:value-of select="."/>
+ </fo:block>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
@@ -931,9 +1275,9 @@
<!-- Type -->
<xsl:template match="type">
<xsl:param name="partnum"/>
-
+
<fo:block>
- <xsl:text>Types:</xsl:text>
+ <xsl:text>Types:</xsl:text>
</fo:block>
<fo:list-block xsl:use-attribute-sets="type-listblock">
@@ -1001,9 +1345,9 @@
<xsl:param name="chapnum"/>
<xsl:variable name="tabnum">
<xsl:number level="any" from="chapter" count="table"/>
- </xsl:variable>
+ </xsl:variable>
<fo:table xsl:use-attribute-sets="table">
- <fo:table-body>
+ <fo:table-body>
<xsl:apply-templates select="row">
<xsl:with-param name="chapnum" select="$chapnum"/>
<xsl:with-param name="tabnum" select="$tabnum"/>
@@ -1107,7 +1451,7 @@
<xsl:template name="calc-arity">
<xsl:param name="string"/>
<xsl:param name="no-of-pars"/>
-
+
<xsl:variable name="length">
<xsl:value-of select="string-length($string)"/>
</xsl:variable>
@@ -1116,8 +1460,8 @@
<xsl:when test="$length > 0">
<xsl:call-template name="calc-arity">
<xsl:with-param name="string" select="substring-after($string, ',')"/>
- <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
- </xsl:call-template>
+ <xsl:with-param name="no-of-pars" select="$no-of-pars+1"/>
+ </xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$no-of-pars"/>
@@ -1131,9 +1475,9 @@
<xsl:variable name="str1">
<xsl:call-template name="remove-paren-1">
<xsl:with-param name="string" select="$string"/>
- <xsl:with-param name="start">(</xsl:with-param>
- <xsl:with-param name="end">)</xsl:with-param>
- </xsl:call-template>
+ <xsl:with-param name="start">(</xsl:with-param>
+ <xsl:with-param name="end">)</xsl:with-param>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str2">
@@ -1141,7 +1485,7 @@
<xsl:with-param name="string" select="$str1"/>
<xsl:with-param name="start">{</xsl:with-param>
<xsl:with-param name="end">}</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:variable name="str3">
@@ -1149,7 +1493,7 @@
<xsl:with-param name="string" select="$str2"/>
<xsl:with-param name="start">[</xsl:with-param>
<xsl:with-param name="end">]</xsl:with-param>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="$str3"/>
@@ -1161,7 +1505,7 @@
<xsl:param name="string"/>
<xsl:param name="start"/>
<xsl:param name="end"/>
-
+
<xsl:variable name="tmp1">
<xsl:value-of select="substring-before($string, $start)"/>
</xsl:variable>
@@ -1174,7 +1518,7 @@
<xsl:variable name="retstring">
<xsl:call-template name="remove-paren">
<xsl:with-param name="string" select="$tmp2"/>
- </xsl:call-template>
+ </xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat(concat($tmp1, 'x'), $retstring)"/>
</xsl:when>
diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile
new file mode 100644
index 0000000000..8e81bccd59
--- /dev/null
+++ b/lib/erl_docgen/src/Makefile
@@ -0,0 +1,96 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(ERL_DOCGEN_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/erl_docgen-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES = \
+ otp_specs
+
+HRL_FILES =
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+
+APP_FILE = erl_docgen.app
+
+APP_SRC = $(APP_FILE).src
+APP_TARGET = $(EBIN)/$(APP_FILE)
+
+APPUP_FILE = erl_docgen.appup
+
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -I../../xmerl/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
diff --git a/lib/erl_docgen/src/erl_docgen.app.src b/lib/erl_docgen/src/erl_docgen.app.src
new file mode 100644
index 0000000000..1720464b6d
--- /dev/null
+++ b/lib/erl_docgen/src/erl_docgen.app.src
@@ -0,0 +1,12 @@
+{application, erl_docgen,
+ [{description, "Misc tools for building documentation"},
+ {vsn, "%VSN%"},
+ {modules, [otp_specs
+ ]
+ },
+ {registered,[]},
+ {applications, [kernel,stdlib]},
+ {env, []
+ }
+ ]
+}.
diff --git a/lib/erl_docgen/src/erl_docgen.appup.src b/lib/erl_docgen/src/erl_docgen.appup.src
new file mode 100644
index 0000000000..54a63833e6
--- /dev/null
+++ b/lib/erl_docgen/src/erl_docgen.appup.src
@@ -0,0 +1 @@
+{"%VSN%",[],[]}.
diff --git a/lib/erl_docgen/src/otp_specs.erl b/lib/erl_docgen/src/otp_specs.erl
new file mode 100644
index 0000000000..728ddb2e6e
--- /dev/null
+++ b/lib/erl_docgen/src/otp_specs.erl
@@ -0,0 +1,701 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(otp_specs).
+
+-export([module/2, package/2, overview/2, type/1]).
+
+-include("xmerl.hrl").
+
+-define(XML_EXPORT, xmerl_xml).
+-define(DEFAULT_XML_EXPORT, ?XML_EXPORT).
+-define(DEFAULT_PP, erl_pp).
+-define(IND(N), #xmlText{value="\n" ++ lists:duplicate(N, $\s)}).
+-define(NL, "\n").
+
+module(Element, Options) ->
+ XML = layout_module(Element, init_opts(Options)),
+ Export = proplists:get_value(xml_export, Options,
+ ?DEFAULT_XML_EXPORT),
+ xmerl:export_simple(XML, Export, [#xmlAttribute{name=prolog,
+ value=""}]).
+
+-record(opts, {pretty_print, file_suffix}).
+
+init_opts(Options) ->
+ #opts{pretty_print = proplists:get_value(pretty_print,
+ Options, ?DEFAULT_PP),
+ %% It *is* depending on edoc.hrl!
+ file_suffix = proplists:get_value(file_suffix, Options, ".html")}.
+
+layout_module(#xmlElement{name = module, content = Es}=E, Opts) ->
+ Name = get_attrval(name, E),
+ Functions = [{function_name(Elem), Elem} ||
+ Elem <- get_content(functions, Es)],
+ Types = [{type_name(Elem), Elem} || Elem <- get_content(typedecls, Es)],
+ Body = [{module,
+ [{name,[Name]}],
+ ([?NL] ++ types(lists:sort(Types), Opts)
+ ++ functions(lists:sort(Functions), Opts)
+ ++ timestamp())}],
+ Body.
+
+timestamp() ->
+ [{timestamp, [io_lib:fwrite("Generated by EDoc, ~s, ~s.",
+ [edoc_lib:datestr(date()),
+ edoc_lib:timestr(time())])]},?NL].
+
+functions(Fs, Opts) ->
+ lists:flatmap(fun ({Name, E}) -> function(Name, E, Opts) end, Fs).
+
+function(Name, #xmlElement{content = Es}, Opts) ->
+ TS = get_content(typespec, Es),
+ Spec = typespec(TS, Opts),
+ [{spec,(Name
+ ++ [?IND(2),{contract,Spec}]
+ ++ typespec_annos(TS))},
+ ?NL].
+
+function_name(E) ->
+ [] = get_attrval(module, E),
+ [?IND(2),{name,[atom(get_attrval(name, E))]},
+ ?IND(2),{arity,[get_attrval(arity, E)]}].
+
+label_anchor(Content, E) ->
+ case get_attrval(label, E) of
+ "" -> Content;
+ Ref -> [{marker, [{id, Ref}], Content}]
+ end.
+
+typespec([], _Opts) -> [];
+typespec(Es, Opts) ->
+ {Head, LDefs} = collect_clause(Es, Opts),
+ clause(Head, LDefs) ++ [?IND(2)].
+
+collect_clause(Es, Opts) ->
+ Name = t_name(get_elem(erlangName, Es)),
+ Defs = get_elem(localdef, Es),
+ [Type] = get_elem(type, Es),
+ {format_spec(Name, Type, Opts), collect_local_defs(Defs, Opts)}.
+
+clause(Head, LDefs) ->
+ FC = [?IND(6),{head,Head}] ++ local_clause_defs(LDefs),
+ [?IND(4),{clause,FC}].
+
+local_clause_defs([]) -> [];
+local_clause_defs(LDefs) ->
+ LocalDefs = [{subtype,T} || T <- coalesce_local_defs(LDefs, [])],
+ [?IND(6),{guard,margin(8, LocalDefs)}].
+
+types(Ts, Opts) ->
+ lists:flatmap(fun ({Name, E}) -> typedecl(Name, E, Opts) end, Ts).
+
+typedecl(Name, E=#xmlElement{content = Es}, Opts) ->
+ TD = get_content(typedef, Es),
+ TypeDef = typedef(E, TD, Opts),
+ [{type,(Name
+ ++ [?IND(2),{typedecl, TypeDef}]
+ ++ typedef_annos(TD))},
+ ?NL].
+
+type_name(#xmlElement{content = Es}) ->
+ Typedef = get_content(typedef, Es),
+ [E] = get_elem(erlangName, Typedef),
+ Args = get_content(argtypes, Typedef),
+ [] = get_attrval(module, E),
+ [?IND(2),{name,[atom(get_attrval(name, E))]},
+ ?IND(2),{n_vars,[integer_to_list(length(Args))]}].
+
+typedef(E, Es, Opts) ->
+ Ns = get_elem(erlangName, Es),
+ Name =
+ ([t_name(Ns), "("]
+ ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])),
+ LDefs = collect_local_defs(get_elem(localdef, Es), Opts),
+ TypeHead = case get_elem(type, Es) of
+ [] -> label_anchor(Name, E);
+ Type -> (label_anchor(Name, E)
+ ++ format_type(Name, Type, Opts))
+ end,
+ ([?IND(6),{typehead,TypeHead}]
+ ++ local_type_defs(LDefs, [])).
+
+local_type_defs([], _) -> [];
+local_type_defs(LDefs, Last) ->
+ LocalDefs = [{local_def,T} || T <- coalesce_local_defs(LDefs, Last)],
+ [?IND(6),{local_defs,margin(8, LocalDefs)}].
+
+collect_local_defs(Es, Opts) ->
+ [collect_localdef(E, Opts) || E <- Es].
+
+collect_localdef(E = #xmlElement{content = Es}, Opts) ->
+ Name = case get_elem(typevar, Es) of
+ [] ->
+ label_anchor(N0 = t_abstype(get_content(abstype, Es)), E);
+ [V] ->
+ N0 = t_var(V)
+ end,
+ {Name,N0,format_type(N0, get_elem(type, Es), Opts)}.
+
+%% "A = t(), B = t()" is coalesced into "A = B = t()".
+%% Names as B above are kept, but the formated string is empty.
+coalesce_local_defs([], _Last) ->
+ [];
+coalesce_local_defs([{Name,N0,TypeS} | L], Last) when Name =:= N0 ->
+ cld(L, [{Name,N0}], TypeS, Last);
+coalesce_local_defs([{Name,N0,TypeS} | L], Last) ->
+ [local_def(N0, Name, TypeS, Last, L) | coalesce_local_defs(L, Last)].
+
+cld([{Name,N0,TypeS} | L], Names, TypeS, Last) when Name =:= N0 ->
+ cld(L, [{Name,N0} | Names], TypeS, Last);
+cld(L, Names0, TypeS, Last) ->
+ Names = [{_,Name0} | Names1] = lists:reverse(Names0),
+ NS = join([N || {N,_} <- Names], [" = "]),
+ ([local_def(Name0, NS, TypeS, Last, L) |
+ [local_def(N0, "", "", [], L) || {_,N0} <- Names1]]
+ ++ coalesce_local_defs(L, Last)).
+
+local_def(Name, NS, TypeS, Last, L) ->
+ [{typename,Name},{string,NS ++ TypeS ++ [Last || L =:= []]}].
+
+%% join([], Sep) when is_list(Sep) ->
+%% [];
+join([H|T], Sep) ->
+ H ++ lists:append([Sep ++ X || X <- T]).
+
+%% Use the default formatting of EDoc, which creates references, and
+%% then insert newlines and indentation according to erl_pp (the
+%% (fast) Erlang pretty printer).
+format_spec(Name, Type, #opts{pretty_print = erl_pp}=Opts) ->
+ try
+ L = t_clause(Name, Type),
+ O = pp_clause(Name, Type),
+ {R, ".\n"} = diaf(L, O, Opts),
+ R
+ catch _:_ ->
+ %% Example: "@spec ... -> record(a)"
+ format_spec(Name, Type, Opts#opts{pretty_print=default})
+ end;
+format_spec(Sep, Type, _Opts) ->
+ t_clause(Sep, Type).
+
+t_clause(Name, Type) ->
+ #xmlElement{content = [#xmlElement{name = 'fun', content = C}]} = Type,
+ [Name] ++ t_fun(C).
+
+pp_clause(Pre, Type) ->
+ Types = ot_utype([Type]),
+ Atom = lists:duplicate(iolist_size(Pre), $a),
+ L1 = erl_pp:attribute({attribute,0,spec,{{list_to_atom(Atom),0},[Types]}}),
+ "-spec " ++ L2 = lists:flatten(L1),
+ L3 = Pre ++ lists:nthtail(length(Atom), L2),
+ re:replace(L3, "\n ", "\n", [{return,list},global]).
+
+format_type(Name, Type, #opts{pretty_print = erl_pp}=Opts) ->
+ try
+ L = t_utype(Type),
+ O = pp_type(Name, Type),
+ {R, ".\n"} = diaf(L, O, Opts),
+ [" = "] ++ R
+ catch _:_ ->
+ %% Example: "t() = record(a)."
+ format_type(Name, Type, Opts#opts{pretty_print=default})
+ end;
+format_type(_Name, Type, _Opts) ->
+ [" = "] ++ t_utype(Type).
+
+pp_type(Prefix, Type) ->
+ Atom = list_to_atom(lists:duplicate(iolist_size(Prefix), $a)),
+ L1 = erl_pp:attribute({attribute,0,type,{Atom,ot_utype(Type),[]}}),
+ {L2,N} = case lists:dropwhile(fun(C) -> C =/= $: end, lists:flatten(L1)) of
+ ":: " ++ L3 -> {L3,9}; % compensation for extra "()" and ":"
+ "::\n" ++ L3 -> {"\n"++L3,6}
+ end,
+ Ss = lists:duplicate(N, $\s),
+ re:replace(L2, "\n"++Ss, "\n", [{return,list},global]).
+
+diaf(L, O0, Opts) ->
+ {R0, O} = diaf(L, [], O0, [], Opts),
+ R1 = rewrite_some_predefs(lists:reverse(R0)),
+ R = indentation(lists:flatten(R1)),
+ {R, O}.
+
+diaf([C | L], St, [C | O], R, Opts) ->
+ diaf(L, St, O, [[C] | R], Opts);
+diaf(" "++L, St, O, R, Opts) ->
+ diaf(L, St, O, R, Opts);
+diaf("", [Cs | St], O, R, Opts) ->
+ diaf(Cs, St, O, R, Opts);
+diaf("", [], O, R, _Opts) ->
+ {R, O};
+diaf(L, St, " "++O, R, Opts) ->
+ diaf(L, St, O, [" " | R], Opts);
+diaf(L, St, "\n"++O, R, Opts) ->
+ Ss = lists:takewhile(fun(C) -> C =:= $\s end, O),
+ diaf(L, St, lists:nthtail(length(Ss), O), ["\n"++Ss | R], Opts);
+diaf([{seealso, HRef0, S0} | L], St, O0, R, Opts) ->
+ {S, O} = diaf(S0, app_fix(O0), Opts),
+ HRef = fix_mod_ref(HRef0, Opts),
+ diaf(L, St, O, [{seealso, HRef, S} | R], Opts);
+diaf("="++L, St, "::"++O, R, Opts) ->
+ %% EDoc uses "=" for record field types; Dialyzer uses "::". Maybe
+ %% there should be an option for this, possibly affecting other
+ %% similar discrepancies.
+ diaf(L, St, O, ["=" | R], Opts);
+diaf([Cs | L], St, O, R, Opts) ->
+ diaf(Cs, [L | St], O, R, Opts).
+
+rewrite_some_predefs(S) ->
+ xpredef(lists:flatten(S)).
+
+xpredef([]) ->
+ [];
+xpredef("neg_integer()"++L) ->
+ ["integer() =< -1"] ++ xpredef(L);
+xpredef("non_neg_integer()"++L) ->
+ ["integer() >= 0"] ++ xpredef(L);
+xpredef("pos_integer()"++L) ->
+ ["integer() >= 1"] ++ xpredef(L);
+xpredef([T | Es]) when is_tuple(T) ->
+ [T | xpredef(Es)];
+xpredef([E | Es]) ->
+ [[E] | xpredef(Es)].
+
+indentation([]) ->
+ [];
+indentation([$\n|L]) ->
+ [{br,[]}|indent(L)];
+indentation([T | Es]) when is_tuple(T) ->
+ [T | indentation(Es)];
+indentation([E|L]) ->
+ [[E]|indentation(L)].
+
+indent([$\s|L]) ->
+ [{nbsp,[]}|indent(L)];
+indent(L) ->
+ indentation(L).
+
+app_fix(L) ->
+ try
+ {"//" ++ R1,L2} = app_fix(L, 1),
+ [App, Mod] = string:tokens(R1, "/"),
+ "//" ++ atom(App) ++ "/" ++ atom(Mod) ++ L2
+ catch _:_ -> L
+ end.
+
+app_fix(L, I) -> % a bit slow
+ {L1, L2} = lists:split(I, L),
+ case erl_scan:tokens([], L1 ++ ". ", 1) of
+ {done, {ok,[{atom,_,Atom}|_],_}, _} -> {atom_to_list(Atom), L2};
+ _ -> app_fix(L, I+1)
+ end.
+
+%% Remove the file suffix from module references.
+fix_mod_ref(HRef, #opts{file_suffix = ""}) ->
+ HRef;
+fix_mod_ref([{marker, S}]=HRef0, #opts{file_suffix = FS}) ->
+ {A, B} = lists:splitwith(fun(C) -> C =/= $# end, S),
+ case lists:member($:, A) of
+ true ->
+ HRef0; % should "save" most application references "http:"
+ false ->
+ case {lists:suffix(FS, A), B} of
+ {true, "#"++_} ->
+ [{marker, lists:sublist(A, length(A)-length(FS)) ++ B}];
+ _ ->
+ HRef0
+ end
+ end.
+
+see(E, Es) ->
+ case href(E) of
+ [] -> Es;
+ Ref ->
+ [{seealso, Ref, Es}]
+ end.
+
+href(E) ->
+ case get_attrval(href, E) of
+ "" -> [];
+ URI ->
+ [{marker, URI}]
+ end.
+
+atom(String) ->
+ io_lib:write_atom(list_to_atom(String)).
+
+t_name([E]) ->
+ N = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> atom(N);
+ M ->
+ S = atom(M) ++ ":" ++ atom(N),
+ case get_attrval(app, E) of
+ "" -> S;
+ A -> "//" ++ atom(A) ++ "/" ++ S
+ end
+ end.
+
+t_utype([E]) ->
+ t_utype_elem(E).
+
+t_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> t_type(Es);
+ Name ->
+ T = t_type(Es),
+ case T of
+ [Name] -> T; % avoid generating "Foo::Foo"
+ T -> [Name] ++ ["::"] ++ T
+ end
+ end.
+
+t_type([E=#xmlElement{name = typevar}]) ->
+ t_var(E);
+t_type([E=#xmlElement{name = atom}]) ->
+ t_atom(E);
+t_type([E=#xmlElement{name = integer}]) ->
+ t_integer(E);
+t_type([E=#xmlElement{name = range}]) ->
+ t_range(E);
+t_type([E=#xmlElement{name = binary}]) ->
+ t_binary(E);
+t_type([E=#xmlElement{name = float}]) ->
+ t_float(E);
+t_type([#xmlElement{name = nil}]) ->
+ t_nil();
+t_type([#xmlElement{name = list, content = Es}]) ->
+ t_list(Es);
+t_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ t_nonempty_list(Es);
+t_type([#xmlElement{name = tuple, content = Es}]) ->
+ t_tuple(Es);
+t_type([#xmlElement{name = 'fun', content = Es}]) ->
+ ["fun("] ++ t_fun(Es) ++ [")"];
+t_type([E = #xmlElement{name = record, content = Es}]) ->
+ t_record(E, Es);
+t_type([E = #xmlElement{name = abstype, content = Es}]) ->
+ t_abstype(E, Es);
+t_type([#xmlElement{name = union, content = Es}]) ->
+ t_union(Es).
+
+t_var(E) ->
+ [get_attrval(name, E)].
+
+t_atom(E) ->
+ [get_attrval(value, E)].
+
+t_integer(E) ->
+ [get_attrval(value, E)].
+
+t_range(E) ->
+ [get_attrval(value, E)].
+
+t_binary(E) ->
+ [get_attrval(value, E)].
+
+t_float(E) ->
+ [get_attrval(value, E)].
+
+t_nil() ->
+ ["[]"].
+
+t_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"].
+
+t_nonempty_list(Es) ->
+ ["["] ++ t_utype(get_elem(type, Es)) ++ [", ...]"].
+
+t_tuple(Es) ->
+ ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]).
+
+t_fun(Es) ->
+ ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es),
+ [") -> "] ++ t_utype(get_elem(type, Es))).
+
+t_record(E, Es) ->
+ Name = ["#"] ++ t_type(get_elem(atom, Es)),
+ case get_elem(field, Es) of
+ [] ->
+ see(E, [Name, "{}"]);
+ Fs ->
+ see(E, Name) ++ ["{"] ++ seq(fun t_field/1, Fs, ["}"])
+ end.
+
+t_field(#xmlElement{content = Es}) ->
+ t_type(get_elem(atom, Es)) ++ [" = "] ++ t_utype(get_elem(type, Es)).
+
+t_abstype(E, Es) ->
+ Name = t_name(get_elem(erlangName, Es)),
+ case get_elem(type, Es) of
+ [] ->
+ see(E, [Name, "()"]);
+ Ts ->
+ see(E, [Name]) ++ ["("] ++ seq(fun t_utype_elem/1, Ts, [")"])
+ end.
+
+t_abstype(Es) ->
+ ([t_name(get_elem(erlangName, Es)), "("]
+ ++ seq(fun t_utype_elem/1, get_elem(type, Es), [")"])).
+
+t_union(Es) ->
+ seq(fun t_utype_elem/1, Es, " | ", []).
+
+seq(F, Es, Tail) ->
+ seq(F, Es, ", ", Tail).
+
+seq(F, [E], _Sep, Tail) ->
+ F(E) ++ Tail;
+seq(F, [E | Es], Sep, Tail) ->
+ F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail);
+seq(_F, [], _Sep, Tail) ->
+ Tail.
+
+get_elem(Name, [#xmlElement{name = Name} = E | Es]) ->
+ [E | get_elem(Name, Es)];
+get_elem(Name, [_ | Es]) ->
+ get_elem(Name, Es);
+get_elem(_, []) ->
+ [].
+
+get_attr(Name, [#xmlAttribute{name = Name} = A | As]) ->
+ [A | get_attr(Name, As)];
+get_attr(Name, [_ | As]) ->
+ get_attr(Name, As);
+get_attr(_, []) ->
+ [].
+
+get_attrval(Name, #xmlElement{attributes = As}) ->
+ case get_attr(Name, As) of
+ [#xmlAttribute{value = V}] ->
+ V;
+ [] -> ""
+ end.
+
+get_content(Name, Es) ->
+ case get_elem(Name, Es) of
+ [#xmlElement{content = Es1}] ->
+ Es1;
+ [] -> []
+ end.
+
+overview(_, _Options) -> [].
+
+package(_, _Options) -> [].
+
+type(_) -> [].
+
+%% ---------------------------------------------------------------------
+
+ot_utype([E]) ->
+ ot_utype_elem(E).
+
+ot_utype_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> ot_type(Es);
+ N ->
+ Name = {var,0,list_to_atom(N)},
+ T = ot_type(Es),
+ case T of
+ Name -> T;
+ T -> {ann_type,0,[Name, T]}
+ end
+ end.
+
+ot_type([E=#xmlElement{name = typevar}]) ->
+ ot_var(E);
+ot_type([E=#xmlElement{name = atom}]) ->
+ ot_atom(E);
+ot_type([E=#xmlElement{name = integer}]) ->
+ ot_integer(E);
+ot_type([E=#xmlElement{name = range}]) ->
+ ot_range(E);
+ot_type([E=#xmlElement{name = binary}]) ->
+ ot_binary(E);
+ot_type([E=#xmlElement{name = float}]) ->
+ ot_float(E);
+ot_type([#xmlElement{name = nil}]) ->
+ ot_nil();
+ot_type([#xmlElement{name = list, content = Es}]) ->
+ ot_list(Es);
+ot_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ ot_nonempty_list(Es);
+ot_type([#xmlElement{name = tuple, content = Es}]) ->
+ ot_tuple(Es);
+ot_type([#xmlElement{name = 'fun', content = Es}]) ->
+ ot_fun(Es);
+ot_type([#xmlElement{name = record, content = Es}]) ->
+ ot_record(Es);
+ot_type([#xmlElement{name = abstype, content = Es}]) ->
+ ot_abstype(Es);
+ot_type([#xmlElement{name = union, content = Es}]) ->
+ ot_union(Es).
+
+ot_var(E) ->
+ {var,0,list_to_atom(get_attrval(name, E))}.
+
+ot_atom(E) ->
+ {ok, [Atom], _} = erl_scan:string(get_attrval(value, E), 0),
+ Atom.
+
+ot_integer(E) ->
+ {integer,0,list_to_integer(get_attrval(value, E))}.
+
+ot_range(E) ->
+ [I1, I2] = string:tokens(get_attrval(value, E), "."),
+ {type,0,range,[{integer,0,list_to_integer(I1)},
+ {integer,0,list_to_integer(I2)}]}.
+
+ot_binary(E) ->
+ {Base, Unit} =
+ case string:tokens(get_attrval(value, E), ",:*><") of
+ [] ->
+ {0, 0};
+ ["_",B] ->
+ {list_to_integer(B), 0};
+ ["_","_",U] ->
+ {0, list_to_integer(U)};
+ ["_",B,_,"_",U] ->
+ {list_to_integer(B), list_to_integer(U)}
+ end,
+ {type,0,binary,[{integer,0,Base},{integer,0,Unit}]}.
+
+ot_float(E) ->
+ {float,0,list_to_float(get_attrval(value, E))}.
+
+ot_nil() ->
+ {nil,0}.
+
+ot_list(Es) ->
+ {type,0,list,[ot_utype(get_elem(type, Es))]}.
+
+ot_nonempty_list(Es) ->
+ {type,0,nonempty_list,[ot_utype(get_elem(type, Es))]}.
+
+ot_tuple(Es) ->
+ {type,0,tuple,[ot_utype_elem(E) || E <- Es]}.
+
+ot_fun(Es) ->
+ Range = ot_utype(get_elem(type, Es)),
+ Args = [ot_utype_elem(A) || A <- get_content(argtypes, Es)],
+ {type,0,'fun',[{type,0,product,Args},Range]}.
+
+ot_record(Es) ->
+ {type,0,record,[ot_type(get_elem(atom, Es)) |
+ [ot_field(F) || F <- get_elem(field, Es)]]}.
+
+ot_field(#xmlElement{content = Es}) ->
+ {type,0,field_type,
+ [ot_type(get_elem(atom, Es)), ot_utype(get_elem(type, Es))]}.
+
+ot_abstype(Es) ->
+ ot_name(get_elem(erlangName, Es),
+ [ot_utype_elem(Elem) || Elem <- get_elem(type, Es)]).
+
+ot_union(Es) ->
+ {type,0,union,[ot_utype_elem(E) || E <- Es]}.
+
+ot_name(Es, T) ->
+ case ot_name(Es) of
+ [Mod, ":", Atom] ->
+ {remote_type,0,[{atom,0,list_to_atom(Mod)},
+ {atom,0,list_to_atom(Atom)},T]};
+ "tuple" when T =:= [] ->
+ {type,0,tuple,any};
+ Atom ->
+ {type,0,list_to_atom(Atom),T}
+ end.
+
+ot_name([E]) ->
+ Atom = get_attrval(name, E),
+ case get_attrval(module, E) of
+ "" -> Atom;
+ M ->
+ case get_attrval(app, E) of
+ "" ->
+ [M, ":", Atom];
+ A ->
+ ["//"++A++"/" ++ M, ":", Atom] % EDoc only!
+ end
+ end.
+
+%% Returns exactly those annotations that can be referred to. Note
+%% that a Dialyzer type/spec (currently) can have more annotations
+%% than can be represented by EDoc types. Note also that edoc_dia
+%% has annotated all type variables with themselves.
+typespec_annos([]) -> [?NL];
+typespec_annos([_|Es]) ->
+ annotations(clause_annos(Es)).
+
+clause_annos(Es) ->
+ [annos(get_elem(type, Es)), local_defs_annos(get_elem(localdef, Es))].
+
+typedef_annos(Es) ->
+ annotations([(case get_elem(type, Es) of
+ [] -> [];
+ T -> annos(T)
+ end
+ ++ lists:flatmap(fun annos_elem/1,
+ get_content(argtypes, Es))),
+ local_defs_annos(get_elem(localdef, Es))]).
+
+local_defs_annos(Es) ->
+ lists:flatmap(fun localdef_annos/1, Es).
+
+localdef_annos(#xmlElement{content = Es}) ->
+ annos(get_elem(type, Es)).
+
+annotations(AnnoL) ->
+ Annos = lists:usort(lists:flatten(AnnoL)),
+ margin(2, Annos).
+
+margin(N, L) ->
+ lists:append([[?IND(N),E] || E <- L]) ++ [?IND(N-2)].
+
+annos([E]) ->
+ annos_elem(E).
+
+annos_elem(E=#xmlElement{content = Es}) ->
+ case get_attrval(name, E) of
+ "" -> annos_type(Es);
+ "..." -> annos_type(Es); % compensate for a kludge in edoc_dia.erl
+ N ->
+ [{anno,[N]} | annos_type(Es)]
+ end.
+
+annos_type([#xmlElement{name = list, content = Es}]) ->
+ annos(get_elem(type, Es));
+annos_type([#xmlElement{name = nonempty_list, content = Es}]) ->
+ annos(get_elem(type, Es));
+annos_type([#xmlElement{name = tuple, content = Es}]) ->
+ lists:flatmap(fun annos_elem/1, Es);
+annos_type([#xmlElement{name = 'fun', content = Es}]) ->
+ (annos(get_elem(type, Es))
+ ++ lists:flatmap(fun annos_elem/1, get_content(argtypes, Es)));
+annos_type([#xmlElement{name = record, content = Es}]) ->
+ lists:append([annos(get_elem(type, Es1)) ||
+ #xmlElement{content = Es1} <- get_elem(field, Es)]);
+annos_type([#xmlElement{name = abstype, content = Es}]) ->
+ lists:flatmap(fun annos_elem/1, get_elem(type, Es));
+annos_type([#xmlElement{name = union, content = Es}]) ->
+ lists:flatmap(fun annos_elem/1, Es);
+annos_type([E=#xmlElement{name = typevar}]) ->
+ annos_elem(E);
+annos_type(_) ->
+ [].
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index dd74ae9ddd..29585d8520 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1,22 +1,2 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
+ERL_DOCGEN_VSN = 0.2.4
-ERL_DOCGEN_VSN = 0.2
-
-TICKETS =
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index 7728cb97be..72ac8c7bbf 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -288,13 +288,7 @@ case "$threads_disabled" in
;;
win32_threads)
EI_THREADS="true"
- AC_MSG_CHECKING([for __declspec(thread) usability])
- if test "X$GCC" = "Xyes"; then
- AC_MSG_RESULT([no])
- else
- THR_DEFS="$THR_DEFS -DUSE_DECLSPEC_THREAD"
- AC_MSG_RESULT([yes])
- fi
+ THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500"
;;
pthread)
EI_THREADS="true"
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 9083ae02b0..de4e4b4301 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -581,7 +581,7 @@ ei_x_encode_empty_list(&amp;x);
<c><![CDATA[term]]></c> union, it is decoded, and the appropriate field
in <c><![CDATA[term->value]]></c> is set, and <c><![CDATA[*index]]></c> is
incremented by the term size.</p>
- <p>The function returns 0 on successful encoding, -1 on error,
+ <p>The function returns 0 on successful decoding, -1 on error,
and 1 if the term seems alright, but does not fit in the
<c><![CDATA[term]]></c> structure. If it returns 0, the <c><![CDATA[index]]></c>
will be incremented, and the <c><![CDATA[term]]></c> contains the
@@ -641,12 +641,14 @@ ei_x_encode_empty_list(&amp;x);
<p></p>
<pre>
~a - an atom, char*
+~c - a character, char
~s - a string, char*
~i - an integer, int
~l - a long integer, long int
~u - a unsigned long integer, unsigned long int
~f - a float, float
~d - a double float, double float
+~p - an Erlang PID, erlang_pid*
</pre>
<p>For instance, to encode a tuple with some stuff:</p>
<pre>
diff --git a/lib/erl_interface/doc/src/ei_connect.xml b/lib/erl_interface/doc/src/ei_connect.xml
index abf705f9e2..f562615ddd 100644
--- a/lib/erl_interface/doc/src/ei_connect.xml
+++ b/lib/erl_interface/doc/src/ei_connect.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>2001</year><year>2009</year>
+ <year>2001</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -54,7 +54,7 @@
the operation, if the primitive does not complete within the time
specified, the function will return an error and
<c><![CDATA[erl_errno]]></c> will be set to <c><![CDATA[ETIMEDOUT]]></c>. With
- communication primitive is ment an operation on the socket, like
+ communication primitive is meant an operation on the socket, like
<c><![CDATA[connect]]></c>, <c><![CDATA[accept]]></c>, <c><![CDATA[recv]]></c> or <c><![CDATA[send]]></c>.</p>
<p>Obviously the timeouts are for implementing fault tolerance,
not to keep hard realtime promises. The <c><![CDATA[_tmo]]></c> functions
@@ -116,7 +116,7 @@
int n = 0;
struct in_addr addr;
ei_cnode ec;
-addr = inet_addr("150.236.14.75");
+addr.s_addr = inet_addr("150.236.14.75");
if (ei_connect_xinit(&ec,
"chivas",
"madonna",
@@ -132,7 +132,7 @@ if (ei_connect_xinit(&ec,
</p>
<code type="none"><![CDATA[
if (ei_connect_init(&ec, "madonna", "cookie...", n++) < 0) {
- fprintf("ERROR when initializing: %d",erl_errno);
+ fprintf(stderr,"ERROR when initializing: %d",erl_errno);
exit(-1);
}
]]></code>
@@ -177,7 +177,7 @@ int fd = ei_connect(&ec, NODE);
/*** Variant 2 ***/
struct in_addr addr;
-addr = inet_addr(IP_ADDR);
+addr.s_addr = inet_addr(IP_ADDR);
fd = ei_xconnect(&ec, &addr, ALIVE);
]]></code>
</desc>
@@ -508,7 +508,7 @@ if (ei_decode_version(result.buff, &index) < 0
same as the port number that was previously bound to the socket.</p>
<p><c><![CDATA[addr]]></c> is the 32-bit IP address of the local host.</p>
<p>To unregister with epmd, simply close the returned
- descriptor. See also <c><![CDATA[ei_unpublish()]]></c>.</p>
+ descriptor. Do not use <c><![CDATA[ei_unpublish()]]></c>, which is deprecated anyway.</p>
<p>On success, the functions return a descriptor connecting the
calling process to epmd. On failure, they return -1 and set
<c><![CDATA[erl_errno]]></c> to <c><![CDATA[EIO]]></c>.</p>
@@ -558,18 +558,21 @@ typedef struct {
</func>
<func>
<name><ret>int</ret><nametext>ei_unpublish(ei_cnode *ec)</nametext></name>
- <fsummary>Unpublish a node name</fsummary>
+ <fsummary>Forcefully unpublish a node name</fsummary>
<desc>
<p>This function can be called by a process to unregister a
- specified node from epmd on the localhost. This may be
- useful, for example, when epmd has not detected the failure of a
- node, and will not allow the name to be reused. If you use this
- function to unregister your own process, be sure to also close
- the descriptor that was returned by <c><![CDATA[ei_publish()]]></c>.</p>
- <note>
- <p>Careless use of this function may have unpredictable
- results, if the registered node is in fact still running.</p>
- </note>
+ specified node from epmd on the localhost. This is however usually not
+ allowed, unless epmd was started with the -relaxed_command_check
+ flag, which it normally isn't.</p>
+
+ <p>To unregister a node you have published, you should
+ close the descriptor that was returned by
+ <c><![CDATA[ei_publish()]]></c>.</p>
+
+ <warning>
+ <p>This function is deprecated and will be removed in a future
+ release.</p>
+ </warning>
<p><c><![CDATA[ec]]></c> is the node structure of the node to unregister.</p>
<p>If the node was successfully unregistered from epmd, the
function returns 0. Otherwise, it returns -1 and sets
diff --git a/lib/erl_interface/doc/src/erl_connect.xml b/lib/erl_interface/doc/src/erl_connect.xml
index b2235925b2..bd5e637244 100644
--- a/lib/erl_interface/doc/src/erl_connect.xml
+++ b/lib/erl_interface/doc/src/erl_connect.xml
@@ -4,7 +4,7 @@
<cref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -451,7 +451,7 @@ typedef struct {
<p><c><![CDATA[port]]></c> is the local name to register, and should be the
same as the port number that was previously bound to the socket.</p>
<p>To unregister with epmd, simply close the returned
- descriptor. See also <c><![CDATA[erl_unpublish()]]></c>.
+ descriptor.
</p>
<p>On success, the functions return a descriptor connecting the
calling process to epmd. On failure, they return -1 and set
@@ -507,21 +507,24 @@ typedef struct {
</func>
<func>
<name><ret>int</ret><nametext>erl_unpublish(alive)</nametext></name>
- <fsummary>Unpublish a node name</fsummary>
+ <fsummary>Forcefully unpublish a node name</fsummary>
<type>
<v>char *alive;</v>
</type>
<desc>
- <p>This function can be called by a process to unregister a
- specified node name from epmd on the localhost. This may be
- useful, for example, when epmd has not detected the failure of a
- node, and will not allow the name to be reused. If you use this
- function to unregister your own process, be sure to also close
- the descriptor that was returned by <c><![CDATA[erl_publish()]]></c>.</p>
- <note>
- <p>Careless use of this function may have unpredictable
- results, if the registered node is in fact still running.</p>
- </note>
+ <p>This function can be called by a process to unregister a
+ specified node from epmd on the localhost. This is however usually not
+ allowed, unless epmd was started with the -relaxed_command_check
+ flag, which it normally isn't.</p>
+
+ <p>To unregister a node you have published, you should instead
+ close the descriptor that was returned by
+ <c><![CDATA[ei_publish()]]></c>.</p>
+
+ <warning>
+ <p>This function is deprecated and will be removed in a future
+ release.</p>
+ </warning>
<p><c><![CDATA[alive]]></c> is the name of the node to unregister, i.e., the
first component of the nodename, without the <c><![CDATA[@hostname]]></c>.</p>
<p>If the node was successfully unregistered from epmd, the
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index 14aec4a4d9..784ba78d3e 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -30,6 +30,206 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.7.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Some malformed distribution messages could cause VM to
+ crash, this is now corrected.</p>
+ <p>
+ Own Id: OTP-8993</p>
+ </item>
+ <item>
+ <p>
+ Strengthen string copy check (Thanks to Michael Santos).</p>
+ <p>
+ Own Id: OTP-9071</p>
+ </item>
+ <item>
+ <p>
+ Strengthen atom length check when decoding atoms (Thanks
+ to Michael Santos).</p>
+ <p>
+ Own Id: OTP-9072</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Fix global registration. C node needed
+ DFLAG_DIST_MONITOR_FLAT set when connecting. Fix list
+ compare in erl_compare_ext to return correct result.
+ (Thanks to Vitaliy Batichko and Evgeny Khirin)</p>
+ <p>
+ Own Id: OTP-9015</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ erl_call: remove get_hostent</p>
+ <p>
+ get_hostent does not properly handle IPv4 addresses on
+ little endian platforms and fails with hostnames
+ beginning with a number. Remove get_hostent and use
+ ei_gethostbyname directly since gethostbyname supports
+ IPv4 addresses.</p>
+ <p>
+ (Thanks to Michael Santos)</p>
+ <p>
+ Own Id: OTP-8890</p>
+ </item>
+ <item>
+ <p> teach ei_x_format to handle unary - and + (Thanks to
+ Steve Vinoski)</p>
+ <p>
+ Own Id: OTP-8891</p>
+ </item>
+ <item>
+ <p>Fix zero byte allocation in registry. (Thanks to
+ Michael Santos)</p>
+ <p>
+ Own Id: OTP-8893</p>
+ </item>
+ <item>
+ <p> Check the length of the node name to prevent an
+ overflow. Memory error control of ei_alloc_big. (Thanks
+ to Michael Santos) </p>
+ <p>
+ Own Id: OTP-8943</p>
+ </item>
+ <item>
+ <p>
+ erl_term_len() in erl_interface could returned too large
+ values for integers (since R14B) and too small values for
+ refs (since R9B).</p>
+ <p>
+ Own Id: OTP-8945</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7.1.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The <c>erl_interface</c> tracelevel for erlang messages was incorrect. This has now been fixed.
+ </p>
+ <p>
+ Own Id: OTP-8874</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+<section><title>Erl_Interface 3.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Removed unused variable in <c>ei_decode_term.c</c>.</p>
+ <p>
+ Fixed faulty deallocation in <c>erl_call</c>.</p>
+ <p>
+ Own Id: OTP-8748</p>
+ </item>
+ <item>
+ <p>ei_connect: correct man page examples (Thanks to
+ Michael Santos)</p>
+ <p>
+ Own Id: OTP-8813</p>
+ </item>
+ <item>
+ <p>ei: prevent overflow in <c>ei_connect_init</c> and
+ <c>ei_xconnect</c></p> <p>Add length check of the buffer
+ before copying. (Thanks to Michael Santos)</p>
+ <p>
+ Own Id: OTP-8814</p>
+ </item>
+ <item>
+ <p>Remove DECLSPEC feature which fails on Windows Vista
+ and use the fallback implementation instead.</p>
+ <p>
+ Own Id: OTP-8826</p>
+ </item>
+ <item>
+ <p>erl_call: fix multiple buffer overflows (Thanks to
+ Michael Santos)</p>
+ <p>
+ Own Id: OTP-8827</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Fix incorrect writev iovec buffer handling in
+ <c>erl_interface</c> (Thanks to Steve Vinoski)</p>
+ <p>
+ Own Id: OTP-8837</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Interface 3.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>compact IEEE 754 double encoding in external binary
+ format for ei</p> <list><item><p>Implement the compact
+ IEEE 754 double encoding in external binary format for
+ ei. Encoding for ei now always produces the NEW_FLOAT_EXT
+ format. Decoding and term printing handle both the old
+ ERL_FLOAT_EXT encoding and the new NEW_FLOAT_EXT
+ encoding. </p></item> <item><p>Legacy erl_interface code
+ also handles the new encoding, but still produces the
+ ERL_FLOAT_EXT encoding by default.</p></item>
+ <item><p>Also enable the DFLAG_NEW_FLOATS distribution
+ flag.</p></item> <item><p>ei_get_type() will return
+ ERL_FLOAT_EXT regardless if the external format is
+ encoded with ERL_FLOAT_EXT or NEW_FLOAT_EXT for
+ doubles.</p></item> <item><p>Reduce the number of copies
+ of the code for encoding and decoding doubles throughout
+ ei and erl_interface by instead calling the ei encoding
+ and decoding functions wherever possible.</p></item>
+ <item><p>Restore commented-out float tests in
+ ei_decode_SUITE and ei_encode_SUITE in
+ lib/erl_interface/test. Modify them to make them match
+ the style of other tests in the same suites.</p></item>
+ </list> <p>These changes are based on an ei float patch
+ from Serge Aleynikov originally submitted against R12B-2
+ in July 2008 and reworked by Steve Vinoski May 2010.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8684</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.6.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h
index d1a697615a..ae815b414a 100644
--- a/lib/erl_interface/include/ei.h
+++ b/lib/erl_interface/include/ei.h
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
#ifndef EI_H
@@ -80,21 +80,24 @@
#define ERL_NO_TIMEOUT -1
/* these are the control message types */
-#define ERL_LINK 1
-#define ERL_SEND 2
-#define ERL_EXIT 3
-#define ERL_UNLINK 4
-#define ERL_NODE_LINK 5
-#define ERL_REG_SEND 6
-#define ERL_GROUP_LEADER 7
-#define ERL_EXIT2 8
-#define ERL_PASS_THROUGH 'p'
+#define ERL_LINK 1
+#define ERL_SEND 2
+#define ERL_EXIT 3
+#define ERL_UNLINK 4
+#define ERL_NODE_LINK 5
+#define ERL_REG_SEND 6
+#define ERL_GROUP_LEADER 7
+#define ERL_EXIT2 8
+#define ERL_PASS_THROUGH 'p'
/* new ones for tracing, from Kenneth */
-#define ERL_SEND_TT 12
-#define ERL_EXIT_TT 13
-#define ERL_REG_SEND_TT 16
-#define ERL_EXIT2_TT 18
+#define ERL_SEND_TT 12
+#define ERL_EXIT_TT 13
+#define ERL_REG_SEND_TT 16
+#define ERL_EXIT2_TT 18
+#define ERL_MONITOR_P 19
+#define ERL_DEMONITOR_P 20
+#define ERL_MONITOR_P_EXIT 21
/* -------------------------------------------------------------------- */
@@ -110,6 +113,7 @@
#define ERL_SMALL_INTEGER_EXT 'a'
#define ERL_INTEGER_EXT 'b'
#define ERL_FLOAT_EXT 'c'
+#define NEW_FLOAT_EXT 'F'
#define ERL_ATOM_EXT 'd'
#define ERL_REFERENCE_EXT 'e'
#define ERL_NEW_REFERENCE_EXT 'r'
@@ -718,11 +722,9 @@ int ei_x_encode_bignum(ei_x_buff *x, mpz_t obj);
#define EI_LONGLONG __int64
#define EI_ULONGLONG unsigned __int64
#else
-#ifndef VXWORKS
#define EI_LONGLONG long long
#define EI_ULONGLONG unsigned long long
#endif
-#endif
#ifndef VXWORKS
int ei_decode_longlong(const char *buf, int *index, EI_LONGLONG *p);
diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c
index d2d0a7e7c1..34362b4b9f 100644
--- a/lib/erl_interface/src/connect/ei_connect.c
+++ b/lib/erl_interface/src/connect/ei_connect.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2000-2011. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
/*
@@ -366,16 +366,16 @@ static int initWinSock(void)
WORD wVersionRequested;
WSADATA wsaData;
int i;
- /* FIXME problem for threaded ? */
- static int initialized = 0;
+
+ static LONG volatile initialized = 0;
wVersionRequested = MAKEWORD(1, 1);
- if (!initialized) {
- initialized = 1;
+ if (InterlockedCompareExchange((LPLONG) &initialized,1L,0L) == 0L) {
/* FIXME not terminate, just a message?! */
if ((i = WSAStartup(wVersionRequested, &wsaData))) {
EI_TRACE_ERR1("ei_connect_init",
"ERROR: can't initialize windows sockets: %d",i);
+ initialized = 2L;
return 0;
}
@@ -383,10 +383,14 @@ static int initWinSock(void)
EI_TRACE_ERR0("initWinSock","ERROR: this version of windows "
"sockets not supported");
WSACleanup();
+ initialized = 2L;
return 0;
}
+ initialized = 3L;
+ } else while (initialized < 2) {
+ SwitchToThread();
}
- return 1;
+ return (int) (initialized - 2);
}
#endif
@@ -502,10 +506,14 @@ int ei_connect_init(ei_cnode* ec, const char* this_node_name,
return ERL_ERROR;
}
- if (this_node_name == NULL)
+ if (this_node_name == NULL) {
sprintf(thisalivename, "c%d", (int) getpid());
- else
+ } else if (strlen(this_node_name) >= sizeof(thisalivename)) {
+ EI_TRACE_ERR0("ei_connect_init","ERROR: this_node_name too long");
+ return ERL_ERROR;
+ } else {
strcpy(thisalivename, this_node_name);
+ }
if ((hp = ei_gethostbyname(thishostname)) == 0) {
/* Looking up IP given hostname fails. We must be on a standalone
@@ -930,7 +938,7 @@ int ei_do_receive_msg(int fd, int staticbuffer_p,
return ERL_ERROR;
}
x->index = x->buffsz;
- switch (msg->msgtype) { /* FIXME are these all? */
+ switch (msg->msgtype) { /* FIXME does not handle trace tokens and monitors */
case ERL_SEND:
case ERL_REG_SEND:
case ERL_LINK:
@@ -938,7 +946,6 @@ int ei_do_receive_msg(int fd, int staticbuffer_p,
case ERL_GROUP_LEADER:
case ERL_EXIT:
case ERL_EXIT2:
- case ERL_NODE_LINK:
return ERL_MSG;
default:
@@ -1190,7 +1197,7 @@ static char *hex(char digest[16], char buff[33])
char *p = buff;
int i;
- for (i = 0; i < sizeof(digest); ++i) {
+ for (i = 0; i < 16; ++i) {
*p++ = tab[(int)((*d) >> 4)];
*p++ = tab[(int)((*d++) & 0xF)];
}
@@ -1321,9 +1328,11 @@ static int send_name_or_challenge(int fd, char *nodename,
put8(s, 'n');
put16be(s, version);
put32be(s, (DFLAG_EXTENDED_REFERENCES
+ | DFLAG_DIST_MONITOR
| DFLAG_EXTENDED_PIDS_PORTS
| DFLAG_FUN_TAGS
- | DFLAG_NEW_FUN_TAGS));
+ | DFLAG_NEW_FUN_TAGS
+ | DFLAG_NEW_FLOATS));
if (f_chall)
put32be(s, challenge);
memcpy(s, nodename, strlen(nodename));
@@ -1393,6 +1402,11 @@ static int recv_challenge(int fd, unsigned *challenge,
goto error;
}
+ if (!(*flags & DFLAG_NEW_FLOATS)) {
+ EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE peer cannot "
+ "handle binary float encoding");
+ goto error;
+ }
if (getpeername(fd, (struct sockaddr *) &sin, &sin_len) < 0) {
EI_TRACE_ERR0("recv_challenge","<- RECV_CHALLENGE can't get peername");
diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h
index 9926f799df..3c42b49b82 100644
--- a/lib/erl_interface/src/connect/ei_connect_int.h
+++ b/lib/erl_interface/src/connect/ei_connect_int.h
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
/*
@@ -101,6 +101,7 @@ extern int h_errno;
#define DFLAG_FUN_TAGS 16
#define DFLAG_NEW_FUN_TAGS 0x80
#define DFLAG_EXTENDED_PIDS_PORTS 0x100
+#define DFLAG_NEW_FLOATS 0x800
ei_cnode *ei_fd_to_cnode(int fd);
int ei_distversion(int fd);
diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c
index 42aeab22b1..50c5a4161d 100644
--- a/lib/erl_interface/src/connect/ei_resolve.c
+++ b/lib/erl_interface/src/connect/ei_resolve.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -601,7 +601,7 @@ struct hostent *ei_gethostbyaddr_r(const char *addr,
#ifndef HAVE_GETHOSTBYNAME_R
return my_gethostbyaddr_r(addr,length,type,hostp,buffer,buflen,h_errnop);
#else
-#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000))
+#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__))
struct hostent *result;
gethostbyaddr_r(addr, length, type, hostp, buffer, buflen, &result,
@@ -628,7 +628,7 @@ struct hostent *ei_gethostbyname_r(const char *name,
#ifndef HAVE_GETHOSTBYNAME_R
return my_gethostbyname_r(name,hostp,buffer,buflen,h_errnop);
#else
-#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000))
+#if (defined(__GLIBC__) || (__FreeBSD_version >= 602000) || defined(__DragonFly__))
struct hostent *result;
gethostbyname_r(name, hostp, buffer, buflen, &result, h_errnop);
diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c
index 51fc32d65c..86852f947d 100644
--- a/lib/erl_interface/src/connect/eirecv.c
+++ b/lib/erl_interface/src/connect/eirecv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -107,7 +107,7 @@ ei_recv_internal (int fd,
switch (msg->msgtype) {
case ERL_SEND: /* { SEND, Cookie, ToPid } */
- if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_atom(header,&index,msg->cookie)
|| ei_decode_pid(header,&index,&msg->to))
{
@@ -118,7 +118,7 @@ ei_recv_internal (int fd,
break;
case ERL_REG_SEND: /* { REG_SEND, From, Cookie, ToName } */
- if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_pid(header,&index,&msg->from)
|| ei_decode_atom(header,&index,msg->cookie)
|| ei_decode_atom(header,&index,msg->toname))
@@ -133,7 +133,7 @@ ei_recv_internal (int fd,
case ERL_LINK: /* { LINK, From, To } */
case ERL_UNLINK: /* { UNLINK, From, To } */
case ERL_GROUP_LEADER: /* { GROUP_LEADER, From, To } */
- if (ei_tracelevel > 1) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_pid(header,&index,&msg->from)
|| ei_decode_pid(header,&index,&msg->to))
{
@@ -145,7 +145,7 @@ ei_recv_internal (int fd,
case ERL_EXIT: /* { EXIT, From, To, Reason } */
case ERL_EXIT2: /* { EXIT2, From, To, Reason } */
- if (ei_tracelevel > 1) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_pid(header,&index,&msg->from)
|| ei_decode_pid(header,&index,&msg->to))
{
@@ -156,7 +156,7 @@ ei_recv_internal (int fd,
break;
case ERL_SEND_TT: /* { SEND_TT, Cookie, ToPid, TraceToken } */
- if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_atom(header,&index,msg->cookie)
|| ei_decode_pid(header,&index,&msg->to)
|| ei_decode_trace(header,&index,&msg->token))
@@ -169,7 +169,7 @@ ei_recv_internal (int fd,
break;
case ERL_REG_SEND_TT: /* { REG_SEND_TT, From, Cookie, ToName, TraceToken } */
- if (ei_tracelevel > 0) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_pid(header,&index,&msg->from)
|| ei_decode_atom(header,&index,msg->cookie)
|| ei_decode_atom(header,&index,msg->toname)
@@ -184,7 +184,7 @@ ei_recv_internal (int fd,
case ERL_EXIT_TT: /* { EXIT_TT, From, To, TraceToken, Reason } */
case ERL_EXIT2_TT: /* { EXIT2_TT, From, To, TraceToken, Reason } */
- if (ei_tracelevel > 1) show_this_msg = 1;
+ if (ei_tracelevel >= 4) show_this_msg = 1;
if (ei_decode_pid(header,&index,&msg->from)
|| ei_decode_pid(header,&index,&msg->to)
|| ei_decode_trace(header,&index,&msg->token))
@@ -196,10 +196,6 @@ ei_recv_internal (int fd,
ei_trace(1,&msg->token); /* turn on tracing */
break;
- case ERL_NODE_LINK: /* { NODE_LINK } */
- if (ei_tracelevel > 1) show_this_msg = 1;
- break;
-
default:
/* unknown type, just put any remaining bytes into buffer */
break;
diff --git a/lib/erl_interface/src/connect/send.c b/lib/erl_interface/src/connect/send.c
index cd832db4ea..2fb487d7e8 100644
--- a/lib/erl_interface/src/connect/send.c
+++ b/lib/erl_interface/src/connect/send.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -87,8 +87,7 @@ int ei_send_encoded_tmo(int fd, const erlang_pid *to,
put8(s, ERL_PASS_THROUGH); /* 1 */
/*** sum: 1070 */
- /* FIXME incorrect level */
- if (ei_tracelevel > 0)
+ if (ei_tracelevel >= 4)
ei_show_sendmsg(stderr,header,msg);
#ifdef HAVE_WRITEV
diff --git a/lib/erl_interface/src/connect/send_exit.c b/lib/erl_interface/src/connect/send_exit.c
index 098797c96d..c5beb358b0 100644
--- a/lib/erl_interface/src/connect/send_exit.c
+++ b/lib/erl_interface/src/connect/send_exit.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -88,8 +88,7 @@ int ei_send_exit_tmo(int fd, const erlang_pid *from, const erlang_pid *to,
put32be(s, index - 4); /* 4 */
put8(s, ERL_PASS_THROUGH); /* 1 */
/*** sum: len + 1080 */
- /* FIXME incorrect level */
- if (ei_tracelevel > 1)
+ if (ei_tracelevel >= 4)
ei_show_sendmsg(stderr,msgbuf,NULL);
ei_write_fill_t(fd,msgbuf,index,ms);
diff --git a/lib/erl_interface/src/connect/send_reg.c b/lib/erl_interface/src/connect/send_reg.c
index 8f0e40309c..b011142e76 100644
--- a/lib/erl_interface/src/connect/send_reg.c
+++ b/lib/erl_interface/src/connect/send_reg.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -82,8 +82,7 @@ int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from,
put32be(s, index + msglen - 4); /* 4 */
put8(s, ERL_PASS_THROUGH); /* 1 */
/*** sum: 1336 */
- /* FIXME incorrect level.... */
- if (ei_tracelevel > 0)
+ if (ei_tracelevel >= 4)
ei_show_sendmsg(stderr,header,msg);
#ifdef HAVE_WRITEV
diff --git a/lib/erl_interface/src/decode/decode_atom.c b/lib/erl_interface/src/decode/decode_atom.c
index b247bd4e17..c2e6a0426e 100644
--- a/lib/erl_interface/src/decode/decode_atom.c
+++ b/lib/erl_interface/src/decode/decode_atom.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -31,6 +31,8 @@ int ei_decode_atom(const char *buf, int *index, char *p)
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
+
if (p) {
memmove(p,s,len);
p[len] = (char)0;
diff --git a/lib/erl_interface/src/decode/decode_big.c b/lib/erl_interface/src/decode/decode_big.c
index efe9c6e5d9..b54ac85be2 100644
--- a/lib/erl_interface/src/decode/decode_big.c
+++ b/lib/erl_interface/src/decode/decode_big.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -74,7 +74,7 @@ erlang_big *ei_alloc_big(unsigned int digit_bytes) {
memset(b,(char)0,sizeof(erlang_big));
if ( (b->digits = malloc(2*n)) == NULL) {
free(b);
- return 0;
+ return NULL;
}
b->arity = digit_bytes;
diff --git a/lib/erl_interface/src/decode/decode_double.c b/lib/erl_interface/src/decode/decode_double.c
index 66dbe474ec..ed6e39655e 100644
--- a/lib/erl_interface/src/decode/decode_double.c
+++ b/lib/erl_interface/src/decode/decode_double.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
#include <stdio.h>
@@ -26,14 +26,22 @@ int ei_decode_double(const char *buf, int *index, double *p)
{
const char *s = buf + *index;
const char *s0 = s;
- double f;
+ FloatExt f;
- if (get8(s) != ERL_FLOAT_EXT) return -1;
-
- if (sscanf(s, "%lf", &f) != 1) return -1;
+ switch (get8(s)) {
+ case ERL_FLOAT_EXT:
+ if (sscanf(s, "%lf", &f.d) != 1) return -1;
+ s += 31;
+ break;
+ case NEW_FLOAT_EXT:
+ /* IEEE 754 format */
+ f.val = get64be(s);
+ break;
+ default:
+ return -1;
+ }
- s += 31;
- if (p) *p = f;
+ if (p) *p = f.d;
*index += s-s0;
return 0;
}
diff --git a/lib/erl_interface/src/decode/decode_pid.c b/lib/erl_interface/src/decode/decode_pid.c
index 5f2aec3b44..9ed1c36db6 100644
--- a/lib/erl_interface/src/decode/decode_pid.c
+++ b/lib/erl_interface/src/decode/decode_pid.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -33,6 +33,8 @@ int ei_decode_pid(const char *buf, int *index, erlang_pid *p)
if (get8(s) != ERL_ATOM_EXT) return -1;
len = get16be(s);
+
+ if (len > MAXATOMLEN) return -1;
if (p) {
memmove(p->node, s, len);
diff --git a/lib/erl_interface/src/decode/decode_port.c b/lib/erl_interface/src/decode/decode_port.c
index 7fb7d8d414..28abed801a 100644
--- a/lib/erl_interface/src/decode/decode_port.c
+++ b/lib/erl_interface/src/decode/decode_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,8 @@ int ei_decode_port(const char *buf, int *index, erlang_port *p)
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
+
if (p) {
memmove(p->node, s, len);
p->node[len] = (char)0;
diff --git a/lib/erl_interface/src/decode/decode_ref.c b/lib/erl_interface/src/decode/decode_ref.c
index 6fc2cd6533..7b15808bc5 100644
--- a/lib/erl_interface/src/decode/decode_ref.c
+++ b/lib/erl_interface/src/decode/decode_ref.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,8 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
+
if (p) {
memmove(p->node, s, len);
p->node[len] = (char)0;
@@ -62,6 +64,7 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p)
/* then the nodename */
if (get8(s) != ERL_ATOM_EXT) return -1;
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
if (p) {
memmove(p->node, s, len);
diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c
index 316b5bee98..f6c5d861ab 100644
--- a/lib/erl_interface/src/decode/decode_skip.c
+++ b/lib/erl_interface/src/decode/decode_skip.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2002-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
#include "eidef.h"
@@ -77,6 +77,7 @@ int ei_skip_term(const char* buf, int* index)
if (ei_decode_big(buf, index, NULL) < 0) return -1;
break;
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
if (ei_decode_double(buf, index, NULL) < 0) return -1;
break;
case ERL_FUN_EXT:
diff --git a/lib/erl_interface/src/encode/encode_double.c b/lib/erl_interface/src/encode/encode_double.c
index 53f3d52ba6..148a49f73a 100644
--- a/lib/erl_interface/src/encode/encode_double.c
+++ b/lib/erl_interface/src/encode/encode_double.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
#include <stdio.h>
@@ -27,13 +27,13 @@ int ei_encode_double(char *buf, int *index, double p)
char *s = buf + *index;
char *s0 = s;
- if (!buf) s ++;
+ if (!buf)
+ s += 9;
else {
- put8(s,ERL_FLOAT_EXT);
- memset(s, 0, 31);
- sprintf(s, "%.20e", p);
+ /* IEEE 754 format */
+ put8(s, NEW_FLOAT_EXT);
+ put64be(s, ((FloatExt*)&p)->val);
}
- s += 31;
*index += s-s0;
diff --git a/lib/erl_interface/src/epmd/ei_epmd.h b/lib/erl_interface/src/epmd/ei_epmd.h
index 40e5ece572..ccacfed244 100644
--- a/lib/erl_interface/src/epmd/ei_epmd.h
+++ b/lib/erl_interface/src/epmd/ei_epmd.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -40,20 +40,13 @@
#define EI_MYPROTO 0 /* tcp/ip */
#endif
-/* epmd r3 protocol */
-#ifndef EI_EPMD_ALIVE_REQ
-#define EI_EPMD_ALIVE_REQ 'a'
-#define EI_EPMD_ALIVE_OK_RESP 'Y'
-#define EI_EPMD_PORT_REQ 'p'
-#define EI_EPMD_STOP_REQ 's'
-#endif
-
/* epmd r4 */
#ifndef EI_EPMD_ALIVE2_REQ
#define EI_EPMD_ALIVE2_REQ 120
#define EI_EPMD_ALIVE2_RESP 121
#define EI_EPMD_PORT2_REQ 122
#define EI_EPMD_PORT2_RESP 119
+#define EI_EPMD_STOP_REQ 's'
#endif
/* internal functions */
diff --git a/lib/erl_interface/src/epmd/epmd_port.c b/lib/erl_interface/src/epmd/epmd_port.c
index 663b38d2d4..698c75c217 100644
--- a/lib/erl_interface/src/epmd/epmd_port.c
+++ b/lib/erl_interface/src/epmd/epmd_port.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -92,64 +92,6 @@ int ei_epmd_connect_tmo(struct in_addr *inaddr, unsigned ms)
return sd;
}
-/* get the given node's listen port using old epmd protocol */
-static int ei_epmd_r3_port (struct in_addr *addr, const char *alive,
- unsigned ms)
-{
- char buf[EPMDBUF];
- char *s = buf;
- int len = strlen(alive) + 1;
- int fd;
- int port;
- int res;
-#if defined(VXWORKS)
- char ntoabuf[32];
-#endif
-
- put16be(s,len);
- put8(s,EI_EPMD_PORT_REQ);
- strcpy(s,alive);
-
- /* connect to epmd */
- if ((fd = ei_epmd_connect_tmo(addr,ms)) < 0)
- {
- /* ei_epmd_connect_tmo() sets erl_errno */
- return -1;
- }
-
- if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) {
- closesocket(fd);
- erl_errno = (res == -2) ? ETIMEDOUT : EIO;
- return -1;
- }
-
-#ifdef VXWORKS
- /* FIXME use union/macro for level. Correct level? */
- if (ei_tracelevel > 2) {
- inet_ntoa_b(*addr,ntoabuf);
- EI_TRACE_CONN2("ei_epmd_r3_port",
- "-> PORT_REQ alive=%s ip=%s",alive,ntoabuf);
- }
-#else
- EI_TRACE_CONN2("ei_epmd_r3_port",
- "-> PORT_REQ alive=%s ip=%s",alive,inet_ntoa(*addr));
-#endif
-
- if ((res = ei_read_fill_t(fd, buf, 2, ms)) != 2) {
- EI_TRACE_ERR0("ei_epmd_r3_port","<- CLOSE");
- closesocket(fd);
- erl_errno = (res == -2) ? ETIMEDOUT : EIO;
- return -1;
- }
- closesocket(fd);
- s = buf;
- port = get16be(s);
-
- EI_TRACE_CONN1("ei_epmd_r3_port","<- PORT_RESP port=%d",port);
-
- return port;
-}
-
static int ei_epmd_r4_port (struct in_addr *addr, const char *alive,
int *dist, unsigned ms)
{
@@ -164,6 +106,12 @@ static int ei_epmd_r4_port (struct in_addr *addr, const char *alive,
#if defined(VXWORKS)
char ntoabuf[32];
#endif
+
+ if (len > sizeof(buf) - 3)
+ {
+ erl_errno = ERANGE;
+ return -1;
+ }
put16be(s,len);
put8(s,EI_EPMD_PORT2_REQ);
@@ -285,15 +233,6 @@ int ei_epmd_port_tmo (struct in_addr *addr, const char *alive, int *dist,
{
int i;
- /* try the new one first, then the old one */
- i = ei_epmd_r4_port(addr,alive,dist,ms);
-
- /* -2: new protocol not understood */
- if (i == -2) {
- *dist = 0;
- i = ei_epmd_r3_port(addr,alive,ms);
- }
-
- return i;
+ return ei_epmd_r4_port(addr,alive,dist,ms);
}
diff --git a/lib/erl_interface/src/epmd/epmd_publish.c b/lib/erl_interface/src/epmd/epmd_publish.c
index 09b3dce43b..d45fe644c0 100644
--- a/lib/erl_interface/src/epmd/epmd_publish.c
+++ b/lib/erl_interface/src/epmd/epmd_publish.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -55,62 +55,6 @@
/* publish our listen port and alive name */
/* return the (useless) creation number */
-static int ei_epmd_r3_publish (int port, const char *alive, unsigned ms)
-{
- char buf[EPMDBUF];
- char *s = buf;
- int fd;
- int len = strlen(alive) + 3;
- int res,creation;
-
- s = buf;
- put16be(s,len);
- put8(s,EI_EPMD_ALIVE_REQ);
- put16be(s,port);
- strcpy(s, alive);
-
- if ((fd = ei_epmd_connect_tmo(NULL,ms)) < 0) return fd;
-
- if ((res = ei_write_fill_t(fd, buf, len+2, ms)) != len+2) {
- closesocket(fd);
- erl_errno = (res == -2) ? ETIMEDOUT : EIO;
- return -1;
- }
-
- EI_TRACE_CONN2("ei_epmd_r3_publish",
- "-> ALIVE_REQ alive=%s port=%d",alive,port);
-
- if ((res = ei_read_fill_t(fd, buf, 3, ms)) != 3) {
- closesocket(fd);
- erl_errno = (res == -2) ? ETIMEDOUT : EIO;
- return -1;
- }
-
- s = buf;
- if ((res=get8(s)) != EI_EPMD_ALIVE_OK_RESP) {
- EI_TRACE_ERR1("ei_epmd_r3_publish",
- "<- ALIVE_NOK result=%d (failure)",res);
- closesocket(fd);
- erl_errno = EIO;
- return -1;
- }
-
- creation = get16be(s);
-
- EI_TRACE_CONN1("ei_epmd_r3_publish","<- ALIVE_OK creation=%d",creation);
-
- /* Don't close fd here! It keeps us registered with epmd */
-
- /* probably should save fd so we can close it later... */
- /* epmd_saveconn(OPEN,fd,alive); */
-
- /* return the creation number, for no good reason */
- /* return creation; */
-
- /* no! return the descriptor */
- return fd;
-}
-
/* publish our listen port and alive name */
/* return the (useless) creation number */
/* this protocol is a lot more complex than the old one */
@@ -125,6 +69,12 @@ static int ei_epmd_r4_publish (int port, const char *alive, unsigned ms)
int n;
int res, creation;
+ if (len > sizeof(buf)-2)
+ {
+ erl_errno = ERANGE;
+ return -1;
+ }
+
s = buf;
put16be(s,len);
@@ -200,15 +150,7 @@ int ei_epmd_publish(int port, const char *alive)
int ei_epmd_publish_tmo(int port, const char *alive, unsigned ms)
{
- int i;
-
- /* try the new one first, then the old one */
- i = ei_epmd_r4_publish(port,alive, ms);
-
- /* -2: new protocol not understood */
- if (i == -2) i = ei_epmd_r3_publish(port,alive, ms);
-
- return i;
+ return ei_epmd_r4_publish(port,alive, ms);;
}
diff --git a/lib/erl_interface/src/epmd/epmd_unpublish.c b/lib/erl_interface/src/epmd/epmd_unpublish.c
index 08662fe1ec..3afa89ab1d 100644
--- a/lib/erl_interface/src/epmd/epmd_unpublish.c
+++ b/lib/erl_interface/src/epmd/epmd_unpublish.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,11 @@ int ei_unpublish_tmo(const char *alive, unsigned ms)
int len = 1 + strlen(alive);
int fd, res;
+ if (len > sizeof(buf)-3) {
+ erl_errno = ERANGE;
+ return -1;
+ }
+
put16be(s,len);
put8(s,EI_EPMD_STOP_REQ);
strcpy(s, alive);
diff --git a/lib/erl_interface/src/legacy/decode_term.c b/lib/erl_interface/src/legacy/decode_term.c
index ef29d6f57d..796cebdfef 100644
--- a/lib/erl_interface/src/legacy/decode_term.c
+++ b/lib/erl_interface/src/legacy/decode_term.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
#include "eidef.h"
@@ -59,6 +59,7 @@ int ei_decode_term(const char *buf, int *index, void *t)
return ei_decode_long(buf,index,NULL);
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
return ei_decode_double(buf,index,NULL);
case ERL_ATOM_EXT:
diff --git a/lib/erl_interface/src/legacy/erl_connect.c b/lib/erl_interface/src/legacy/erl_connect.c
index 3c8c946506..fdf689e191 100644
--- a/lib/erl_interface/src/legacy/erl_connect.c
+++ b/lib/erl_interface/src/legacy/erl_connect.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -180,9 +180,7 @@ int erl_xconnect(Erl_IpAddr addr, char *alivename)
*
* Close a connection. FIXME call ei_close_connection() later.
*
- * Returns valid file descriptor on success and < 0 on failure.
- * Set erl_errno to EHOSTUNREACH, ENOMEM, EIO or errno from socket(2)
- * or connect(2).
+ * Returns 0 on success and -1 on failure.
*
***************************************************************************/
@@ -250,7 +248,8 @@ int erl_send(int fd, ETERM *to ,ETERM *msg)
return -1;
}
- strcpy(topid.node, (char *)ERL_PID_NODE(to));
+ strncpy(topid.node, (char *)ERL_PID_NODE(to), sizeof(topid.node));
+ topid.node[sizeof(topid.node)-1] = '\0';
topid.num = ERL_PID_NUMBER(to);
topid.serial = ERL_PID_SERIAL(to);
topid.creation = ERL_PID_CREATION(to);
diff --git a/lib/erl_interface/src/legacy/erl_format.c b/lib/erl_interface/src/legacy/erl_format.c
index 9848e9296a..dc85806c36 100644
--- a/lib/erl_interface/src/legacy/erl_format.c
+++ b/lib/erl_interface/src/legacy/erl_format.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -116,7 +116,7 @@ static lvar *lvar_alloc(void)
lvar *tmp;
if ((tmp = ef.idle) == NULL) {
- tmp = (lvar *) malloc(sizeof(lvar)); /* FIXME check result */
+ tmp = (lvar *) erl_malloc(sizeof(lvar));
}
else {
tmp = ef.idle;
diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c
index 4b5f28178f..dad715c762 100644
--- a/lib/erl_interface/src/legacy/erl_marshal.c
+++ b/lib/erl_interface/src/legacy/erl_marshal.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1996-2011. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
/*
@@ -26,6 +26,7 @@
#include <ctype.h>
#include <sys/types.h>
#include <string.h>
+#include <limits.h>
#include "erl_interface.h"
#include "erl_marshal.h"
@@ -102,6 +103,7 @@ void erl_init_marshal(void)
cmp_array[ERL_SMALL_INTEGER_EXT] = 1;
cmp_array[ERL_INTEGER_EXT] = 1;
cmp_array[ERL_FLOAT_EXT] = 1;
+ cmp_array[NEW_FLOAT_EXT] = 1;
cmp_array[ERL_SMALL_BIG_EXT] = 1;
cmp_array[ERL_LARGE_BIG_EXT] = 1;
cmp_array[ERL_ATOM_EXT] = 2;
@@ -124,6 +126,7 @@ void erl_init_marshal(void)
cmp_num_class[ERL_SMALL_INTEGER_EXT] = SMALL;
cmp_num_class[ERL_INTEGER_EXT] = SMALL;
cmp_num_class[ERL_FLOAT_EXT] = FLOAT;
+ cmp_num_class[NEW_FLOAT_EXT] = FLOAT;
cmp_num_class[ERL_SMALL_BIG_EXT] = BIG;
cmp_num_class[ERL_LARGE_BIG_EXT] = BIG;
init_cmp_num_class_p = 0;
@@ -176,26 +179,14 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
return 0;
case ERL_INTEGER:
- i = ep->uval.ival.i;
- /* ERL_SMALL_BIG */
- if ((i > ERL_MAX) || (i < ERL_MIN)) {
- *(*ext)++ = ERL_SMALL_BIG_EXT;
- *(*ext)++ = 4; /* four bytes */
- if ((*(*ext)++ = ((i>>31) & 0x01))) /* sign byte */
- i = -i;
- *(*ext)++ = i & 0xff; /* LSB first */
- *(*ext)++ = (i >> 8) & 0xff;
- *(*ext)++ = (i >> 16) & 0xff;
- *(*ext)++ = (i >> 24) & 0x7f; /* Don't include the sign bit */
- return 0;
- }
+ i = ep->uval.ival.i;
/* SMALL_INTEGER */
if ((i < 256) && (i >= 0)) {
*(*ext)++ = ERL_SMALL_INTEGER_EXT;
*(*ext)++ = i & 0xff;
return 0;
}
- /* INTEGER */
+ /* R14B: Use all 32 bits of INTEGER_EXT */
*(*ext)++ = ERL_INTEGER_EXT;
*(*ext)++ = (i >> 24) & 0xff;
*(*ext)++ = (i >> 16) & 0xff;
@@ -206,23 +197,23 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
case ERL_U_INTEGER:
u = ep->uval.uival.u;
/* ERL_U_SMALL_BIG */
- if (u > ERL_MAX) {
- *(*ext)++ = ERL_SMALL_BIG_EXT;
- *(*ext)++ = 4; /* four bytes */
- *(*ext)++ = 0; /* sign byte */
- *(*ext)++ = u & 0xff; /* LSB first */
- *(*ext)++ = (u >> 8) & 0xff;
- *(*ext)++ = (u >> 16) & 0xff;
- *(*ext)++ = (u >> 24) & 0xff;
- return 0;
+ if ((int)u < 0) {
+ *(*ext)++ = ERL_SMALL_BIG_EXT;
+ *(*ext)++ = 4; /* four bytes */
+ *(*ext)++ = 0; /* sign byte */
+ *(*ext)++ = u & 0xff; /* LSB first */
+ *(*ext)++ = (u >> 8) & 0xff;
+ *(*ext)++ = (u >> 16) & 0xff;
+ *(*ext)++ = (u >> 24) & 0xff;
+ return 0;
}
/* SMALL_INTEGER */
- if ((u < 256) && (u >= 0)) {
+ if (u < 256) {
*(*ext)++ = ERL_SMALL_INTEGER_EXT;
*(*ext)++ = u & 0xff;
return 0;
}
- /* INTEGER */
+ /* R14B: Use all 32 bits of INTEGER_EXT */
*(*ext)++ = ERL_INTEGER_EXT;
*(*ext)++ = (u >> 24) & 0xff;
*(*ext)++ = (u >> 16) & 0xff;
@@ -232,29 +223,28 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
case ERL_LONGLONG:
l = ep->uval.llval.i;
/* ERL_SMALL_BIG */
- if ((l > ((long long) ERL_MAX)) ||
- (l < ((long long) ERL_MIN))) {
- *(*ext)++ = ERL_SMALL_BIG_EXT;
- *(*ext)++ = 8; /* eight bytes */
- if ((*(*ext)++ = ((l>>63) & 0x01))) /* sign byte */
+ if (l > ((long long) INT_MAX) || l < ((long long) INT_MIN)) {
+ *(*ext)++ = ERL_SMALL_BIG_EXT;
+ *(*ext)++ = 8;
+ if ((*(*ext)++ = (l<0))) /* sign byte */
l = -l;
- *(*ext)++ = l & 0xff; /* LSB first */
+ *(*ext)++ = l & 0xff; /* LSB first */
*(*ext)++ = (l >> 8) & 0xff;
*(*ext)++ = (l >> 16) & 0xff;
- *(*ext)++ = (l >> 24) & 0xff;
- *(*ext)++ = (l >> 32) & 0xff;
- *(*ext)++ = (l >> 40) & 0xff;
- *(*ext)++ = (l >> 48) & 0xff;
- *(*ext)++ = (l >> 56) & 0x7f; /* Don't include the sign bit */
+ *(*ext)++ = (l >> 24) & 0xff;
+ *(*ext)++ = (l >> 32) & 0xff;
+ *(*ext)++ = (l >> 40) & 0xff;
+ *(*ext)++ = (l >> 48) & 0xff;
+ *(*ext)++ = (l >> 56) & 0xff;
return 0;
}
/* SMALL_INTEGER */
if ((l < 256) && (l >= 0)) {
- *(*ext)++ = ERL_SMALL_INTEGER_EXT;
- *(*ext)++ = l & 0xff;
- return 0;
+ *(*ext)++ = ERL_SMALL_INTEGER_EXT;
+ *(*ext)++ = l & 0xff;
+ return 0;
}
- /* INTEGER */
+ /* R14B: Use all 32 bits of INTEGER_EXT */
*(*ext)++ = ERL_INTEGER_EXT;
*(*ext)++ = (l >> 24) & 0xff;
*(*ext)++ = (l >> 16) & 0xff;
@@ -265,7 +255,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
case ERL_U_LONGLONG:
ul = ep->uval.ullval.u;
/* ERL_U_SMALL_BIG */
- if (ul > ((unsigned long long) ERL_MAX)) {
+ if (ul > ((unsigned long long) INT_MAX)) {
*(*ext)++ = ERL_SMALL_BIG_EXT;
*(*ext)++ = 8; /* eight bytes */
*(*ext)++ = 0; /* sign byte */
@@ -285,7 +275,7 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist)
*(*ext)++ = ul & 0xff;
return 0;
}
- /* INTEGER */
+ /* R14B: Use all 32 bits of INTEGER_EXT */
*(*ext)++ = ERL_INTEGER_EXT;
*(*ext)++ = (ul >> 24) & 0xff;
*(*ext)++ = (ul >> 16) & 0xff;
@@ -521,29 +511,28 @@ static int erl_term_len_helper(ETERM *ep, int dist)
case ERL_INTEGER:
i = ep->uval.ival.i;
- if ((i > ERL_MAX) || (i < ERL_MIN)) len = 7;
- else if ((i < 256) && (i >= 0)) len = 2;
+ if ((i < 256) && (i >= 0)) len = 2;
else len = 5;
break;
case ERL_U_INTEGER:
u = ep->uval.uival.u;
- if (u > ERL_MAX) len = 7;
+ if ((int)u < 0) len = 7;
else if (u < 256) len = 2;
else len = 5;
break;
case ERL_LONGLONG:
l = ep->uval.llval.i;
- if ((l > ((long long) ERL_MAX)) ||
- (l < ((long long) ERL_MIN))) len = 11;
+ if ((l > ((long long) INT_MAX)) ||
+ (l < ((long long) INT_MIN))) len = 11;
else if ((l < 256) && (l >= 0)) len = 2;
else len = 5;
break;
case ERL_U_LONGLONG:
ul = ep->uval.ullval.u;
- if (ul > ((unsigned long long) ERL_MAX)) len = 11;
+ if (ul > ((unsigned long long) INT_MAX)) len = 11;
else if (ul < 256) len = 2;
else len = 5;
break;
@@ -556,12 +545,7 @@ static int erl_term_len_helper(ETERM *ep, int dist)
case ERL_REF:
i = strlen((char *)ERL_REF_NODE(ep));
- if (dist >= 4 && ERL_REF_LEN(ep) > 1) {
- len = 1 + 2 + (i+3) + 1 + ERL_REF_LEN(ep) * 4;
- } else {
- /* 1 + N + 4 + 1 where N = 3 + strlen */
- len = 9 + i;
- }
+ len = 1 + 2 + (i+3) + 1 + ERL_REF_LEN(ep) * 4;
break;
case ERL_PORT:
@@ -678,7 +662,7 @@ len = i
#define STATIC_NODE_BUF_SZ 30
#define SET_NODE(node,node_buf,cp,len) \
-if (len >= STATIC_NODE_BUF_SZ) node = malloc(len+1); \
+if (len >= STATIC_NODE_BUF_SZ) node = erl_malloc(len+1); \
else node = node_buf; \
memcpy(node, cp, len); \
node[len] = '\0'
@@ -730,11 +714,6 @@ static ETERM *erl_decode_it(unsigned char **ext)
if (arity > 8)
goto big_truncate;
- if (arity == 8 && ((*ext)[7] & 0x80) && sign) {
- /* MSB already occupied ! */
- goto big_truncate;
- }
-
if (arity == 4 && ((*ext)[3] & 0x80) && !sign) {
/* It will fit into an unsigned int !! */
u = (((*ext)[3] << 24)|((*ext)[2])<< 16|((*ext)[1]) << 8 |(**ext));
@@ -745,14 +724,10 @@ static ETERM *erl_decode_it(unsigned char **ext)
return ep;
} else if (arity == 4 && !((*ext)[3] & 0x80)) {
/* It will fit into an int !!
- * Note: It comes in "one's-complement notation"
*/
- if (sign)
- i = (int) (~(((*ext)[3] << 24) | ((*ext)[2])<< 16 |
- ((*ext)[1]) << 8 | (**ext)) | (unsigned int) sign);
- else
- i = (int) (((*ext)[3] << 24) | ((*ext)[2])<< 16 |
- ((*ext)[1]) << 8 | (**ext));
+ i = (int) (((*ext)[3] << 24) | ((*ext)[2])<< 16 |
+ ((*ext)[1]) << 8 | (**ext));
+ if (sign) i = -i;
ERL_TYPE(ep) = ERL_INTEGER;
ep->uval.ival.i = i;
*ext += arity;
@@ -778,8 +753,10 @@ static ETERM *erl_decode_it(unsigned char **ext)
for(x = 0 ; x < arity ; x++) {
l |= ((long long)(*ext)[x]) << ((long long)(8*x));
}
-
- if (sign) l = (long long) (~l | (unsigned long long) sign);
+ if (sign) {
+ l = -l;
+ if (l > 0) goto big_truncate;
+ }
ERL_TYPE(ep) = ERL_LONGLONG;
ep->uval.llval.i = l;
@@ -1008,10 +985,13 @@ static ETERM *erl_decode_it(unsigned char **ext)
return ep;
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
ERL_TYPE(ep) = ERL_FLOAT;
- if (sscanf((char *) *ext, "%lf", &ff) != 1)
+ cp = (char *) *ext;
+ i = -1;
+ if (ei_decode_double(cp, &i, &ff) == -1)
goto failure;
- *ext += 31;
+ *ext += i;
ep->uval.fval.f = ff;
return ep;
@@ -1176,6 +1156,7 @@ unsigned char erl_ext_type(unsigned char *ext)
case ERL_LARGE_TUPLE_EXT:
return ERL_TUPLE;
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
return ERL_FLOAT;
case ERL_BINARY_EXT:
return ERL_BINARY;
@@ -1218,6 +1199,7 @@ int erl_ext_size(unsigned char *t)
case ERL_BINARY_EXT:
case ERL_STRING_EXT:
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
case ERL_SMALL_BIG_EXT:
case ERL_LARGE_BIG_EXT:
return 0;
@@ -1332,6 +1314,9 @@ static int jump(unsigned char **ext)
case ERL_FLOAT_EXT:
*ext += 31;
break;
+ case NEW_FLOAT_EXT:
+ *ext += 8;
+ break;
case ERL_BINARY_EXT:
i = (**ext << 24) | ((*ext)[1] << 16) |((*ext)[2] << 8) | (*ext)[3];
*ext += 4+i;
@@ -1549,7 +1534,7 @@ static int cmp_string_list(unsigned char **e1, unsigned char **e2) {
if ( e1_len < 256 ) {
bp = buf;
} else {
- bp = malloc(5+(2*e1_len)+1);
+ bp = erl_malloc(5+(2*e1_len)+1);
}
bp[0] = ERL_LIST_EXT;
@@ -1661,11 +1646,14 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2)
min = (i < j) ? i : j;
k = 0;
while (1) {
- if (k++ == min)
- return compare_top_ext(e1 , e2);
- if ((ret = compare_top_ext(e1 , e2)) == 0)
- continue;
- return ret;
+ if (k++ == min){
+ if (i == j) return 0;
+ if (i < j) return -1;
+ return 1;
+ }
+ if ((ret = compare_top_ext(e1 , e2)) == 0)
+ continue;
+ return ret;
}
case ERL_STRING_EXT:
i = (**e1 << 8) | ((*e1)[1]);
@@ -1696,12 +1684,15 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2)
}
return 0;
case ERL_FLOAT_EXT:
- if (sscanf((char *) *e1, "%lf", &ff1) != 1)
- return -1;
- *e1 += 31;
- if (sscanf((char *) *e2, "%lf", &ff2) != 1)
- return -1;
- *e2 += 31;
+ case NEW_FLOAT_EXT:
+ i = -1;
+ if (ei_decode_double((char *) *e1, &i, &ff1) != 0)
+ return -1;
+ *e1 += i;
+ j = -1;
+ if (ei_decode_double((char *) *e2, &j, &ff2) != 0)
+ return -1;
+ *e2 += j;
return cmp_floats(ff1,ff2);
case ERL_BINARY_EXT:
@@ -1896,8 +1887,11 @@ static int cmp_big_big(unsigned char**e1, unsigned char **e2)
ei_get_type((char *)*e1,&i1,&t1,&n1);
ei_get_type((char *)*e2,&i2,&t2,&n2);
- b1 = ei_alloc_big(n1);
- b2 = ei_alloc_big(n2);
+ if ( (b1 = ei_alloc_big(n1)) == NULL) return -1;
+ if ( (b2 = ei_alloc_big(n2)) == NULL) {
+ ei_free_big(b1);
+ return 1;
+ }
ei_decode_big((char *)*e1,&i1,b1);
ei_decode_big((char *)*e2,&i2,b2);
diff --git a/lib/erl_interface/src/legacy/erl_timeout.c b/lib/erl_interface/src/legacy/erl_timeout.c
index af1a4a1f3a..d9560eebc8 100644
--- a/lib/erl_interface/src/legacy/erl_timeout.c
+++ b/lib/erl_interface/src/legacy/erl_timeout.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -74,7 +74,7 @@ jmp_buf *timeout_setup(int ms)
t.it_value.tv_usec = (ms % 1000) * 1000;
/* get a jump buffer and save it */
- j = malloc(sizeof(*j)); /* FIXME check result */
+ j = erl_malloc(sizeof(*j));
j->siginfo = s;
push(j);
diff --git a/lib/erl_interface/src/legacy/global_register.c b/lib/erl_interface/src/legacy/global_register.c
index 3a4de8b08e..cce60f25da 100644
--- a/lib/erl_interface/src/legacy/global_register.c
+++ b/lib/erl_interface/src/legacy/global_register.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
int index = 0;
erlang_pid self;
erlang_msg msg;
- int needlink, needatom;
+ int needlink, needatom, needmonitor;
int arity;
int version;
int msglen;
@@ -65,7 +65,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
if (ei_send_reg_encoded(fd,&self,"rex",buf,index)) return -1;
/* get the reply: expect link and an atom, or just an atom */
- needlink = needatom = 1;
+ needlink = needatom = needmonitor = 1;
while (1) {
/* get message */
while (1) {
@@ -78,9 +78,15 @@ int erl_global_register(int fd, const char *name, ETERM *pid)
case ERL_LINK:
/* got link */
if (!needlink) return -1;
- needlink = 0;
+ needlink = 0;
break;
+ case ERL_MONITOR_P-10:
+ /* got monitor */
+ if (!needmonitor) { return -1;}
+ needmonitor = 0;
+ break;
+
case ERL_SEND:
/* got message - does it contain our atom? */
if (!needatom) return -1;
diff --git a/lib/erl_interface/src/legacy/global_unregister.c b/lib/erl_interface/src/legacy/global_unregister.c
index 514dbc3c68..593a8a7860 100644
--- a/lib/erl_interface/src/legacy/global_unregister.c
+++ b/lib/erl_interface/src/legacy/global_unregister.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -37,7 +37,7 @@ int erl_global_unregister(int fd, const char *name)
erlang_msg msg;
int i;
int version,arity,msglen;
- int needunlink, needatom;
+ int needunlink, needatom, needdemonitor;
/* make a self pid */
self->num = fd;
@@ -57,7 +57,7 @@ int erl_global_unregister(int fd, const char *name)
if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1;
/* get the reply: expect unlink and an atom, or just an atom */
- needunlink = needatom = 1;
+ needunlink = needatom = needdemonitor = 1;
while (1) {
/* get message */
while (1) {
@@ -68,11 +68,17 @@ int erl_global_unregister(int fd, const char *name)
switch (i) {
case ERL_UNLINK:
- /* got link */
+ /* got unlink */
if (!needunlink) return -1;
needunlink = 0;
break;
+ case ERL_DEMONITOR_P-10:
+ /* got demonitor */
+ if (!needdemonitor) return -1;
+ needdemonitor = 0;
+ break;
+
case ERL_SEND:
/* got message - does it contain our atom? */
if (!needatom) return -1;
diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c
index 7b95ff232f..bfb4571337 100644
--- a/lib/erl_interface/src/misc/ei_decode_term.c
+++ b/lib/erl_interface/src/misc/ei_decode_term.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2001-2011. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*
@@ -25,16 +25,15 @@
#include "ei_decode_term.h"
#include "putget.h"
-/* Returns 1 if term is decoded, 0 if term is OK, but not decoded here
- and -1 if something is wrong.
- ONLY changes index if term is decoded (return value 1)! */
+/* Returns 0 on successful encoding, -1 on error, and 1 if the term seems
+ alright, but does not fit in the term structure. If it returns 0, the
+ index will be incremented, and the term contains the decoded term. */
int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
{
const char* s = buf + *index, * s0 = s;
int len, i, n, sign;
char c;
- double f;
if (term == NULL) return -1;
c = term->ei_type = get8(s);
@@ -46,13 +45,11 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
term->value.i_val = get32be(s);
break;
case ERL_FLOAT_EXT:
- if (s[30]) return -1;
- if (sscanf(s, "%lf", &f) != 1) return -1;
- s += 31;
- term->value.d_val = f;
- break;
+ case NEW_FLOAT_EXT:
+ return ei_decode_double(buf, index, &term->value.d_val);
case ERL_ATOM_EXT:
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
memcpy(term->value.atom_name, s, len);
term->value.atom_name[len] = '\0';
s += len;
@@ -61,6 +58,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
/* first the nodename */
if (get8(s) != ERL_ATOM_EXT) return -1;
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
memcpy(term->value.ref.node, s, len);
term->value.ref.node[len] = '\0';
s += len;
@@ -75,6 +73,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
/* then the nodename */
if (get8(s) != ERL_ATOM_EXT) return -1;
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
memcpy(term->value.ref.node, s, len);
term->value.ref.node[len] = '\0';
s += len;
@@ -91,6 +90,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
case ERL_PORT_EXT:
if (get8(s) != ERL_ATOM_EXT) return -1;
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
memcpy(term->value.port.node, s, len);
term->value.port.node[len] = '\0';
term->value.port.id = get32be(s) & 0x0fffffff; /* 28 bits */;
@@ -100,6 +100,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term)
if (get8(s) != ERL_ATOM_EXT) return -1;
/* name first */
len = get16be(s);
+ if (len > MAXATOMLEN) return -1;
memcpy(term->value.pid.node, s, len);
term->value.pid.node[len] = '\0';
s += len;
diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c
index 08235d0ebe..cf50f12451 100644
--- a/lib/erl_interface/src/misc/ei_format.c
+++ b/lib/erl_interface/src/misc/ei_format.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -47,10 +47,12 @@
* array of unions.
*/
union arg {
+ char c;
char* s;
long l;
unsigned long u;
double d;
+ erlang_pid* pid;
};
static int eiformat(const char** s, union arg** args, ei_x_buff* x);
@@ -106,6 +108,8 @@ static int eiformat(const char** fmt, union arg** args, ei_x_buff* x)
default:
if (isdigit((int)*p))
res = pdigit(&p, x);
+ else if ((*p == '-' || *p == '+') && isdigit((int)*(p+1)))
+ res = pdigit(&p, x);
else if (islower((int)*p))
res = patom(&p, x);
else
@@ -149,6 +153,8 @@ static int pdigit(const char** fmt, ei_x_buff* x)
double d;
long l;
+ if (**fmt == '-' || **fmt == '+')
+ (*fmt)++;
for (;;) {
c = *(*fmt)++;
if (isdigit((int)c))
@@ -220,12 +226,14 @@ static int pquotedatom(const char** fmt, ei_x_buff* x)
/*
* The format letters are:
* a - An atom
+ * c - A character
* s - A string
* i - An integer
* l - A long integer
* u - An unsigned long integer
* f - A float
* d - A double float
+ * p - An Erlang PID
*/
static int pformat(const char** fmt, union arg** args, ei_x_buff* x)
{
@@ -236,6 +244,10 @@ static int pformat(const char** fmt, union arg** args, ei_x_buff* x)
res = ei_x_encode_atom(x, (*args)->s);
(*args)++;
break;
+ case 'c':
+ res = ei_x_encode_char(x, (*args)->c);
+ (*args)++;
+ break;
case 's':
res = ei_x_encode_string(x, (*args)->s);
(*args)++;
@@ -257,6 +269,10 @@ static int pformat(const char** fmt, union arg** args, ei_x_buff* x)
res = ei_x_encode_double(x, (*args)->d);
(*args)++;
break;
+ case 'p':
+ res = ei_x_encode_pid(x, (*args)->pid);
+ (*args)++;
+ break;
default:
res = -1;
break;
@@ -392,6 +408,9 @@ static int read_args(const char* fmt, va_list ap, union arg **argp)
return -1; /* Error, string not complete */
}
switch (*p++) {
+ case 'c':
+ args[i++].c = (char) va_arg(ap, int);
+ break;
case 'a':
case 's':
args[i++].s = va_arg(ap, char*);
@@ -411,6 +430,9 @@ static int read_args(const char* fmt, va_list ap, union arg **argp)
case 'd':
args[i++].d = va_arg(ap, double);
break;
+ case 'p':
+ args[i++].pid = va_arg(ap, erlang_pid*);
+ break;
default:
ei_free(args); /* Invalid specifier */
return -1;
diff --git a/lib/erl_interface/src/misc/ei_portio.c b/lib/erl_interface/src/misc/ei_portio.c
index b73ebebbe1..f879c4e2f9 100644
--- a/lib/erl_interface/src/misc/ei_portio.c
+++ b/lib/erl_interface/src/misc/ei_portio.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -166,11 +166,16 @@ int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, unsigned
if (done < sum) {
if (iov_base == NULL) {
iov_base = malloc(sizeof(struct iovec) * iovcnt);
+ if (iov_base == NULL) {
+ return -1;
+ }
memcpy(iov_base, iov, sizeof(struct iovec) * iovcnt);
current_iov = iov_base;
}
while (i > 0) {
if (i < current_iov[0].iov_len) {
+ char *p = (char*)current_iov[0].iov_base;
+ current_iov[0].iov_base = p + i;
current_iov[0].iov_len -= i;
i = 0;
} else {
diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c
index 8d0eef5e79..5fc6b3542c 100644
--- a/lib/erl_interface/src/misc/ei_printterm.c
+++ b/lib/erl_interface/src/misc/ei_printterm.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*
@@ -253,7 +253,8 @@ static int print_term(FILE* fp, ei_x_buff* x,
erlang_big *b;
char *ds;
- b = ei_alloc_big(n);
+ if ( (b = ei_alloc_big(n)) == NULL) goto err;
+
if (ei_decode_big(buf, index, b) < 0) {
ei_free_big(b);
goto err;
@@ -272,6 +273,7 @@ static int print_term(FILE* fp, ei_x_buff* x,
break;
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
if (ei_decode_double(buf, index, &d) < 0) goto err;
ch_written += xprintf(fp, x, "%f", d);
break;
diff --git a/lib/erl_interface/src/misc/get_type.c b/lib/erl_interface/src/misc/get_type.c
index d67a6a80d3..2a680d0f94 100644
--- a/lib/erl_interface/src/misc/get_type.c
+++ b/lib/erl_interface/src/misc/get_type.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*
@@ -122,7 +122,12 @@ int ei_get_type_internal(const char *buf, const int *index,
case ERL_STRING_EXT:
*len = get16be(s);
break;
-
+
+ case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
+ *type = ERL_FLOAT_EXT;
+ break;
+
case ERL_LARGE_TUPLE_EXT:
case ERL_LIST_EXT:
case ERL_BINARY_EXT:
diff --git a/lib/erl_interface/src/misc/putget.h b/lib/erl_interface/src/misc/putget.h
index 98d9ebb64c..7a43de324b 100644
--- a/lib/erl_interface/src/misc/putget.h
+++ b/lib/erl_interface/src/misc/putget.h
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*
@@ -54,6 +54,18 @@
(s) += 4; \
} while (0)
+#define put64be(s,n) do { \
+ (s)[0] = ((n) >> 56) & 0xff; \
+ (s)[1] = ((n) >> 48) & 0xff; \
+ (s)[2] = ((n) >> 40) & 0xff; \
+ (s)[3] = ((n) >> 32) & 0xff; \
+ (s)[4] = ((n) >> 24) & 0xff; \
+ (s)[5] = ((n) >> 16) & 0xff; \
+ (s)[6] = ((n) >> 8) & 0xff; \
+ (s)[7] = (n) & 0xff; \
+ (s) += 8; \
+} while (0)
+
#define get8(s) \
((s) += 1, \
((unsigned char *)(s))[-1] & 0xff)
@@ -82,4 +94,20 @@
(((unsigned char *)(s))[-2] << 8) | \
((unsigned char *)(s))[-1]))
+#define get64be(s) \
+ ((s) += 8, \
+ (((EI_ULONGLONG)((unsigned char *)(s))[-8] << 56) | \
+ ((EI_ULONGLONG)((unsigned char *)(s))[-7] << 48) | \
+ ((EI_ULONGLONG)((unsigned char *)(s))[-6] << 40) | \
+ ((EI_ULONGLONG)((unsigned char *)(s))[-5] << 32) | \
+ ((EI_ULONGLONG)((unsigned char *)(s))[-4] << 24) | \
+ ((EI_ULONGLONG)((unsigned char *)(s))[-3] << 16) | \
+ ((EI_ULONGLONG)((unsigned char *)(s))[-2] << 8) | \
+ (EI_ULONGLONG)((unsigned char *)(s))[-1]))
+
+typedef union float_ext {
+ double d;
+ EI_ULONGLONG val;
+} FloatExt;
+
#endif /* _PUTGET_H */
diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c
index 25865d6f8e..194296798b 100644
--- a/lib/erl_interface/src/misc/show_msg.c
+++ b/lib/erl_interface/src/misc/show_msg.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 1998-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*
@@ -181,11 +181,6 @@ int ei_show_sendmsg(FILE *stream, const char *header, const char *msgbuf)
mbuf = header;
break;
- case ERL_NODE_LINK:
- /* nothing to do */
- mbuf = header;
- break;
-
default:
break;
}
@@ -241,10 +236,6 @@ static void show_msg(FILE *stream, int direction, const erlang_msg *msg,
show_pid(stream,&msg->to);
break;
- case ERL_NODE_LINK:
- fprintf(stream,"NODE_LINK");
- break;
-
case ERL_REG_SEND:
fprintf(stream,"REG_SEND From: ");
show_pid(stream,&msg->from);
@@ -400,6 +391,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream)
break;
case ERL_FLOAT_EXT:
+ case NEW_FLOAT_EXT:
ei_decode_double(termbuf,index,&fnum);
fprintf(stream,"%f",fnum);
break;
diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c
index f0d638324d..4182ab2d5e 100644
--- a/lib/erl_interface/src/prog/erl_call.c
+++ b/lib/erl_interface/src/prog/erl_call.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1996-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -118,11 +118,14 @@ static void usage_arg(const char *progname, const char *switchname);
static void usage_error(const char *progname, const char *switchname);
static void usage(const char *progname);
static int get_module(char **mbuf, char **mname);
-static struct hostent* get_hostent(char *host);
static int do_connect(ei_cnode *ec, char *nodename, struct call_flags *flags);
static int read_stdin(char **buf);
static void split_apply_string(char *str, char **mod,
char **fun, char **args);
+static void* ei_chk_malloc(size_t size);
+static void* ei_chk_calloc(size_t nmemb, size_t size);
+static void* ei_chk_realloc(void *old, size_t size);
+static char* ei_chk_strdup(char *s);
/***************************************************************************
@@ -132,7 +135,6 @@ static void split_apply_string(char *str, char **mod,
***************************************************************************/
/* FIXME isn't VxWorks to handle arguments differently? */
-/* FIXME check errors from malloc */
#if !defined(VXWORKS)
int main(int argc, char *argv[])
@@ -165,8 +167,7 @@ int erl_call(int argc, char **argv)
usage_arg(progname, "-sname ");
}
- flags.node = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.node, argv[i+1]);
+ flags.node = ei_chk_strdup(argv[i+1]);
i++;
flags.use_long_name = 0;
} else if (strcmp(argv[i], "-name") == 0) { /* -name NAME */
@@ -174,8 +175,7 @@ int erl_call(int argc, char **argv)
usage_arg(progname, "-name ");
}
- flags.node = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.node, argv[i+1]);
+ flags.node = ei_chk_strdup(argv[i+1]);
i++;
flags.use_long_name = 1;
} else {
@@ -210,16 +210,14 @@ int erl_call(int argc, char **argv)
usage_arg(progname, "-c ");
}
flags.cookiep = 1;
- flags.cookie = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.cookie, argv[i+1]);
+ flags.cookie = ei_chk_strdup(argv[i+1]);
i++;
break;
case 'n':
if (i+1 >= argc) {
usage_arg(progname, "-n ");
}
- flags.node = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.node, argv[i+1]);
+ flags.node = ei_chk_strdup(argv[i+1]);
flags.use_long_name = 1;
i++;
break;
@@ -227,24 +225,21 @@ int erl_call(int argc, char **argv)
if (i+1 >= argc) {
usage_arg(progname, "-h ");
}
- flags.hidden = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.hidden, argv[i+1]);
+ flags.hidden = ei_chk_strdup(argv[i+1]);
i++;
break;
case 'x':
if (i+1 >= argc) {
usage_arg(progname, "-x ");
}
- flags.script = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.script, argv[i+1]);
+ flags.script = ei_chk_strdup(argv[i+1]);
i++;
break;
case 'a':
if (i+1 >= argc) {
usage_arg(progname, "-a ");
}
- flags.apply = (char *) malloc(strlen(argv[i+1]) + 1);
- strcpy(flags.apply, argv[i+1]);
+ flags.apply = ei_chk_strdup(argv[i+1]);
i++;
break;
case '?':
@@ -304,8 +299,7 @@ int erl_call(int argc, char **argv)
if (flags.hidden == NULL) {
/* As default we are c17@gethostname */
i = flags.randomp ? (time(NULL) % 997) : 17;
- /* FIXME allocates to small !!! */
- flags.hidden = (char *) malloc(3 + 2 ); /* c17 or cXYZ */
+ flags.hidden = (char *) ei_chk_malloc(10 + 2 ); /* c17 or cXYZ */
#if defined(VXWORKS)
sprintf(flags.hidden, "c%d",
i < 0 ? (int) taskIdSelf() : i);
@@ -330,17 +324,25 @@ int erl_call(int argc, char **argv)
initWinSock();
#endif
- gethostname(h_hostname, EI_MAXHOSTNAMELEN);
+ if (gethostname(h_hostname, EI_MAXHOSTNAMELEN) < 0) {
+ fprintf(stderr,"erl_call: failed to get host name: %d\n", errno);
+ exit(1);
+ }
if ((hp = ei_gethostbyname(h_hostname)) == 0) {
fprintf(stderr,"erl_call: can't resolve hostname %s\n", h_hostname);
exit(1);
}
- /* If shortnames cut of the name at first '.' */
+ /* If shortnames, cut off the name at first '.' */
if (flags.use_long_name == 0 && (ct = strchr(hp->h_name, '.')) != NULL) {
*ct = '\0';
}
- strcpy(h_hostname, hp->h_name);
+ strncpy(h_hostname, hp->h_name, EI_MAXHOSTNAMELEN);
+ h_hostname[EI_MAXHOSTNAMELEN] = '\0';
memcpy(&h_ipadr.s_addr, *hp->h_addr_list, sizeof(struct in_addr));
+ if (strlen(h_alivename) + strlen(h_hostname) + 2 > sizeof(h_nodename)) {
+ fprintf(stderr,"erl_call: hostname too long: %s\n", h_hostname);
+ exit(1);
+ }
sprintf(h_nodename, "%s@%s", h_alivename, h_hostname);
if (ei_connect_xinit(&ec, h_hostname, h_alivename, h_nodename,
@@ -364,15 +366,20 @@ int erl_call(int argc, char **argv)
* Expand name to a real name (may be ip-address)
*/
/* FIXME better error string */
- if ((hp = get_hostent(host)) == 0) {
- fprintf(stderr,"erl_call: can't get_hostent(%s)\n", host);
+ if ((hp = ei_gethostbyname(host)) == 0) {
+ fprintf(stderr,"erl_call: can't ei_gethostbyname(%s)\n", host);
exit(1);
}
- /* If shortnames cut of the name at first '.' */
+ /* If shortnames, cut off the name at first '.' */
if (flags.use_long_name == 0 && (ct = strchr(hp->h_name, '.')) != NULL) {
*ct = '\0';
}
- strcpy(host_name, hp->h_name);
+ strncpy(host_name, hp->h_name, EI_MAXHOSTNAMELEN);
+ host_name[EI_MAXHOSTNAMELEN] = '\0';
+ if (strlen(flags.node) + strlen(host_name) + 2 > sizeof(nodename)) {
+ fprintf(stderr,"erl_call: nodename too long: %s\n", flags.node);
+ exit(1);
+ }
sprintf(nodename, "%s@%s", flags.node, host_name);
/*
@@ -401,7 +408,7 @@ int erl_call(int argc, char **argv)
ei_encode_empty_list(NULL, &i);
- p = (char *)malloc(i);
+ p = (char *)ei_chk_malloc(i);
i = 0; /* Reset */
ei_encode_empty_list(p, &i);
@@ -426,6 +433,10 @@ int erl_call(int argc, char **argv)
if (flags.modp && (modname != NULL)) {
char fname[256];
+ if (strlen(modname) + 4 + 1 > sizeof(fname)) {
+ fprintf(stderr,"erl_call: module name too long: %s\n", modname);
+ exit(1);
+ }
strcpy(fname, modname);
strcat(fname, ".erl");
@@ -443,7 +454,7 @@ int erl_call(int argc, char **argv)
ei_encode_binary(NULL, &i, module, modsize);
ei_encode_empty_list(NULL, &i);
- p = (char *)malloc(i);
+ p = (char *)ei_chk_malloc(i);
i = 0; /* Reset */
ei_encode_list_header(p, &i, 2);
@@ -476,7 +487,7 @@ int erl_call(int argc, char **argv)
ei_encode_empty_list(NULL, &i);
ei_encode_empty_list(NULL, &i);
- p = (char *)malloc(i);
+ p = (char *)ei_chk_malloc(i);
i = 0; /* Reset */
ei_encode_list_header(p, &i, 2);
@@ -521,7 +532,7 @@ int erl_call(int argc, char **argv)
ei_encode_binary(NULL, &i, evalbuf, len);
ei_encode_empty_list(NULL, &i);
- p = (char *)malloc(i);
+ p = (char *)ei_chk_malloc(i);
i = 0; /* Reset */
ei_encode_list_header(p, &i, 1);
@@ -592,32 +603,6 @@ int erl_call(int argc, char **argv)
*
***************************************************************************/
-/*
- * Get host entry (by address or name)
- */
-/* FIXME: will fail on names like '2fun4you'. */
-static struct hostent* get_hostent(char *host)
-{
- if (isdigit((int)*host)) {
- struct in_addr ip_addr;
- int b1, b2, b3, b4;
- long addr;
-
- /* FIXME: Use inet_aton() (or inet_pton() and get v6 for free). */
- if (sscanf(host, "%d.%d.%d.%d", &b1, &b2, &b3, &b4) != 4) {
- return NULL;
- }
- addr = inet_addr(host);
- ip_addr.s_addr = htonl(addr);
-
- return ei_gethostbyaddr((char *)&ip_addr,sizeof(struct in_addr), AF_INET);
- }
-
- return ei_gethostbyname(host);
-} /* get_hostent */
-
-
-
/*
* This function does only return on success.
@@ -719,32 +704,28 @@ static void split_apply_string(char *str,
EAT(str);
len = str-begin;
- *mod = (char *) calloc(len + 1, sizeof(char));
+ *mod = (char *) ei_chk_calloc(len + 1, sizeof(char));
memcpy(*mod, begin, len);
SKIP_SPACE(str);
if (*str == '\0') {
- *fun = (char *) calloc(strlen(start)+1, sizeof(char));
- strcpy(*fun, start);
- *args = (char *) calloc(strlen(empty_list)+1, sizeof(char));
- strcpy(*args, empty_list);
+ *fun = ei_chk_strdup(start);
+ *args = ei_chk_strdup(empty_list);
return;
}
begin = str;
EAT(str);
len = str-begin;
- *fun = (char *) calloc(len + 1, sizeof(char));
+ *fun = (char *) ei_chk_calloc(len + 1, sizeof(char));
memcpy(*fun, begin, len);
SKIP_SPACE(str);
if (*str == '\0') {
- *args = (char *) calloc(strlen(empty_list)+1, sizeof(char));
- strcpy(*args, empty_list);
+ *args = ei_chk_strdup(empty_list);
return;
}
- *args = (char *) calloc(strlen(str) + 1, sizeof(char));
- strcpy(*args, str);
+ *args = ei_chk_strdup(str);
return;
@@ -760,7 +741,7 @@ static int read_stdin(char **buf)
int bsize = BUFSIZ;
int len = 0;
int i;
- char *tmp = (char *) malloc(bsize);
+ char *tmp = (char *) ei_chk_malloc(bsize);
while (1) {
if ((i = read(0, &tmp[len], bsize-len)) < 0) {
@@ -772,7 +753,7 @@ static int read_stdin(char **buf)
len += i;
if ((len+50) > bsize) {
bsize = len * 2;
- tmp = (char *) realloc(tmp, bsize);
+ tmp = (char *) ei_chk_realloc(tmp, bsize);
} else {
continue;
}
@@ -809,10 +790,11 @@ static int get_module(char **mbuf, char **mname)
}
} /* while */
i = tmp - start;
- *mname = (char *) calloc(i+1, sizeof(char));
+ *mname = (char *) ei_chk_calloc(i+1, sizeof(char));
memcpy(*mname, start, i);
}
- free(mbuf); /* Allocated in read_stdin() */
+ if (*mbuf)
+ free(*mbuf); /* Allocated in read_stdin() */
return len;
@@ -904,3 +886,51 @@ static void initWinSock(void)
}
}
#endif
+
+
+/***************************************************************************
+ *
+ * Utility functions
+ *
+ ***************************************************************************/
+
+static void* ei_chk_malloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL) {
+ fprintf(stderr,"erl_call: insufficient memory\n");
+ exit(1);
+ }
+ return p;
+}
+
+static void* ei_chk_calloc(size_t nmemb, size_t size)
+{
+ void *p = calloc(nmemb, size);
+ if (p == NULL) {
+ fprintf(stderr,"erl_call: insufficient memory\n");
+ exit(1);
+ }
+ return p;
+}
+
+static void* ei_chk_realloc(void *old, size_t size)
+{
+ void *p = realloc(old, size);
+ if (!p) {
+ fprintf(stderr, "erl_call: cannot reallocate %u bytes of memory from %p\n",
+ (unsigned) size, old);
+ exit (1);
+ }
+ return p;
+}
+
+static char* ei_chk_strdup(char *s)
+{
+ char *p = strdup(s);
+ if (p == NULL) {
+ fprintf(stderr,"erl_call: insufficient memory\n");
+ exit(1);
+ }
+ return p;
+}
diff --git a/lib/erl_interface/src/registry/reg_dump.c b/lib/erl_interface/src/registry/reg_dump.c
index 50a6949177..1e640fb506 100644
--- a/lib/erl_interface/src/registry/reg_dump.c
+++ b/lib/erl_interface/src/registry/reg_dump.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -157,7 +157,7 @@ static int mn_send_delete(int fd, erlang_pid *mnesia, const char *key)
int len = strlen(key) + 32; /* 32 is a slight overestimate */
if (len > EISMALLBUF)
- if (!(dbuf = malloc(index)))
+ if (!(dbuf = malloc(len)))
return -1;
msgbuf = (dbuf ? dbuf : sbuf);
@@ -187,7 +187,7 @@ static int mn_send_write(int fd, erlang_pid *mnesia, const char *key, ei_reg_obj
int len = 32 + keylen + obj->size;
if (len > EISMALLBUF)
- if (!(dbuf = malloc(index)))
+ if (!(dbuf = malloc(len)))
return -1;
msgbuf = (dbuf ? dbuf : sbuf);
diff --git a/lib/erl_interface/src/registry/reg_restore.c b/lib/erl_interface/src/registry/reg_restore.c
index 27918d2364..765c3f4314 100644
--- a/lib/erl_interface/src/registry/reg_restore.c
+++ b/lib/erl_interface/src/registry/reg_restore.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1998-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -266,7 +266,7 @@ int ei_reg_restore(int fd, ei_reg *reg, const char *mntab)
/* make sure receive buffer can handle largest expected message */
len = maxkey + maxobj + 512;
if (len > EISMALLBUF)
- if (!(dbuf = malloc(index))) {
+ if (!(dbuf = malloc(len))) {
ei_send_exit(fd,&self,&mnesia,"cannot allocate space for incoming data");
return -1;
}
diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile
index b7a1a4e4d8..4faf89c0d6 100644
--- a/lib/erl_interface/test/Makefile
+++ b/lib/erl_interface/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -33,6 +33,7 @@ MODULES= \
ei_print_SUITE \
ei_tmo_SUITE \
erl_connect_SUITE \
+ erl_global_SUITE \
erl_eterm_SUITE \
erl_ext_SUITE \
erl_format_SUITE \
@@ -41,9 +42,9 @@ MODULES= \
runner
SPEC_FILES = \
- erl_interface.spec \
- erl_interface.dynspec \
- erl_interface.spec.vxworks
+ erl_interface.spec
+
+COVER_FILE = erl_interface.cover
ERL_FILES = $(MODULES:%=%.erl)
@@ -71,8 +72,8 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl
index bc83d6a62e..48469e68dc 100644
--- a/lib/erl_interface/test/ei_accept_SUITE.erl
+++ b/lib/erl_interface/test/ei_accept_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,21 +20,42 @@
%%
-module(ei_accept_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_accept_SUITE_data/ei_accept_test_cases.hrl").
--export([all/1, init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
ei_accept/1, ei_threaded_accept/1]).
-import(runner, [get_term/1,send_term/2]).
-all(suite) -> [ei_accept, ei_threaded_accept].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [ei_accept, ei_threaded_accept].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
- Dog = ?t:timetrap(?t:minutes(0.25)),
+ Dog = ?t:timetrap(?t:seconds(30)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -43,8 +64,6 @@ ei_accept(Config) when is_list(Config) ->
?line P = runner:start(?interpret),
?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
-% ?line AMsg={a,[message, with], " strings in it!", [-12, -23], 1.001},
- %% shouldn't this be a bif or function or something?
?line Myname= hd(tl(string:tokens(atom_to_list(node()), "@"))),
?line io:format("Myname ~p ~n", [Myname]),
?line EINode= list_to_atom("c42@"++Myname),
@@ -52,9 +71,13 @@ ei_accept(Config) when is_list(Config) ->
?line Self= self(),
?line TermToSend= {call, Self, "Test"},
?line F= fun() ->
- timer:sleep(500),
- {any, EINode} ! TermToSend,
- Self ! sent_ok,
+ case waitfornode("c42",20) of
+ true ->
+ {any, EINode} ! TermToSend,
+ Self ! sent_ok;
+ false ->
+ Self ! never_published
+ end,
ok
end,
@@ -72,7 +95,7 @@ ei_accept(Config) when is_list(Config) ->
after 1000 ->
io:format("timeout ~n")
end,
- ?line ok= ei_unpublish(P),
+ ?line runner:finish(P),
ok.
ei_threaded_accept(Config) when is_list(Config) ->
@@ -90,12 +113,29 @@ ei_threaded_accept(Config) when is_list(Config) ->
|| I <- lists:seq(0, N-1) ],
ok.
+waitfornode(String,0) ->
+ io:format("~s never published itself.~n",[String]),
+ false;
+waitfornode(String,N) ->
+ Registered = [X || {X,_} <- element(2,erl_epmd:names())],
+ case lists:member(String,Registered) of
+ true ->
+ true;
+ false ->
+ timer:sleep(1000),
+ waitfornode(String,N-1)
+ end.
+
send_rec_einode(N, TestServerPid) ->
?line Myname= hd(tl(string:tokens(atom_to_list(node()), "@"))),
- ?line EINode= list_to_atom("eiacc" ++ integer_to_list(N) ++ "@" ++ Myname),
+ ?line FirstPart = "eiacc" ++ integer_to_list(N),
+ ?line EINode= list_to_atom(FirstPart ++ "@" ++ Myname),
?line io:format("EINode ~p ~n", [EINode]),
?line Self= self(),
- ?line timer:sleep(10*1000),
+ ?line case waitfornode(FirstPart,20) of
+ true -> ok;
+ false -> test_server:fail({never_published,EINode})
+ end,
?line {any, EINode} ! Self,
?line receive
{N,_}=X ->
@@ -136,13 +176,6 @@ ei_receive(P, Fd) ->
{term, T}= get_term(P),
T.
-ei_unpublish(P) ->
- send_command(P, ei_unpublish, []),
- case get_term(P) of
- {term,{0, _}} -> ok;
- {term,{_X, Errno}} -> {error,Errno}
- end.
-
send_command(P, Name, Args) ->
runner:send_term(P, {Name,list_to_tuple(Args)}).
diff --git a/lib/erl_interface/test/ei_connect_SUITE.erl b/lib/erl_interface/test/ei_connect_SUITE.erl
index 56f478edad..432437d3b8 100644
--- a/lib/erl_interface/test/ei_connect_SUITE.erl
+++ b/lib/erl_interface/test/ei_connect_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,37 +20,53 @@
%%
-module(ei_connect_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_connect_SUITE_data/ei_connect_test_cases.hrl").
-export([
- all/1,
- init_per_testcase/2,
- fin_per_testcase/2,
-
- ei_send/1,
- ei_reg_send/1,
- ei_rpc/1,
- rpc_test/1,
- ei_send_funs/1,
- ei_threaded_send/1,
- ei_set_get_tracelevel/1
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ ei_send/1,
+ ei_reg_send/1,
+ ei_format_pid/1,
+ ei_rpc/1,
+ rpc_test/1,
+ ei_send_funs/1,
+ ei_threaded_send/1,
+ ei_set_get_tracelevel/1
]).
-import(runner, [get_term/1,send_term/2]).
-all(suite) -> [ ei_send,
- ei_reg_send,
- ei_rpc,
- ei_send_funs,
- ei_threaded_send,
- ei_set_get_tracelevel].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [ei_send, ei_reg_send, ei_rpc, ei_format_pid, ei_send_funs,
+ ei_threaded_send, ei_set_get_tracelevel].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?t:minutes(0.25)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -67,6 +83,19 @@ ei_send(Config) when is_list(Config) ->
?line runner:recv_eot(P),
ok.
+ei_format_pid(Config) when is_list(Config) ->
+ ?line S = self(),
+ ?line P = runner:start(?interpret),
+ ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
+ ?line {ok,Fd} = ei_connect(P, node()),
+
+ ?line ok = ei_format_pid(P, Fd, S),
+ ?line receive S -> ok end,
+
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
ei_send_funs(Config) when is_list(Config) ->
?line P = runner:start(?interpret),
?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0),
@@ -103,10 +132,12 @@ ei_threaded_send(Config) when is_list(Config) ->
?line Einode = filename:join(?config(data_dir, Config), "einode"),
?line N = 15,
?line Host = atom_to_list(node()),
- ?line spawn_link(fun() -> start_einode(Einode, N, Host) end),
?line TestServerPid = self(),
?line [ spawn_link(fun() -> rec_einode(I, TestServerPid) end)
|| I <- lists:seq(0, N-1) ],
+ ?line [ receive {I,registered} -> ok end
+ || I <- lists:seq(0, N-1) ],
+ ?line spawn_link(fun() -> start_einode(Einode, N, Host) end),
?line [ receive I -> ok end
|| I <- lists:seq(0, N-1) ],
ok.
@@ -114,6 +145,7 @@ ei_threaded_send(Config) when is_list(Config) ->
rec_einode(N, TestServerPid) ->
?line Regname = list_to_atom("mth"++integer_to_list(N)),
?line register(Regname, self()),
+ ?line TestServerPid ! {N, registered},
?line io:format("~p waiting~n", [Regname]),
?line receive
X ->
@@ -186,6 +218,10 @@ ei_send(P, Fd, To, Msg) ->
send_command(P, ei_send, [Fd,To,Msg]),
get_send_result(P).
+ei_format_pid(P, Fd, To) ->
+ send_command(P, ei_format_pid, [Fd, To]),
+ get_send_result(P).
+
ei_send_funs(P, Fd, To, Msg) ->
send_command(P, ei_send_funs, [Fd,To,Msg]),
get_send_result(P).
diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
index debd3e789b..88a9950994 100644
--- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
+++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,7 @@
static void cmd_ei_connect_init(char* buf, int len);
static void cmd_ei_connect(char* buf, int len);
static void cmd_ei_send(char* buf, int len);
+static void cmd_ei_format_pid(char* buf, int len);
static void cmd_ei_send_funs(char* buf, int len);
static void cmd_ei_reg_send(char* buf, int len);
static void cmd_ei_rpc(char* buf, int len);
@@ -57,6 +58,7 @@ static struct {
"ei_reg_send", 3, cmd_ei_reg_send,
"ei_rpc", 4, cmd_ei_rpc,
"ei_set_get_tracelevel", 1, cmd_ei_set_get_tracelevel,
+ "ei_format_pid", 2, cmd_ei_format_pid,
};
@@ -111,7 +113,7 @@ static void cmd_ei_connect_init(char* buf, int len)
ei_x_buff res;
if (ei_decode_long(buf, &index, &l) < 0)
fail("expected int");
- sprintf(b, "c%d", l);
+ sprintf(b, "c%ld", l);
/* FIXME don't use internal and maybe use skip?! */
ei_get_type_internal(buf, &index, &type, &size);
if (ei_decode_atom(buf, &index, cookie) < 0)
@@ -183,6 +185,25 @@ static void cmd_ei_send(char* buf, int len)
ei_x_free(&x);
}
+static void cmd_ei_format_pid(char* buf, int len)
+{
+ int index = 0;
+ long fd;
+ erlang_pid pid;
+ ei_x_buff x;
+
+ if (ei_decode_long(buf, &index, &fd) < 0)
+ fail("expected long");
+ if (ei_decode_pid(buf, &index, &pid) < 0)
+ fail("expected pid (node)");
+ if (ei_x_new_with_version(&x) < 0)
+ fail("ei_x_new_with_version");
+ if (ei_x_format_wo_ver(&x, "~p", &pid) < 0)
+ fail("ei_x_format_wo_ver");
+ send_errno_result(ei_send(fd, &pid, x.buff, x.index));
+ ei_x_free(&x);
+}
+
static void cmd_ei_send_funs(char* buf, int len)
{
int index = 0, n;
diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl
index ea528728ab..bb44b78854 100644
--- a/lib/erl_interface/test/ei_decode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_SUITE.erl
@@ -1,31 +1,32 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(ei_decode_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_decode_SUITE_data/ei_decode_test_cases.hrl").
-export(
[
- all/1,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
test_ei_decode_long/1,
test_ei_decode_ulong/1,
test_ei_decode_longlong/1,
@@ -35,16 +36,29 @@
test_ei_decode_misc/1
]).
-all(suite) ->
- [
- test_ei_decode_long,
- test_ei_decode_ulong,
- test_ei_decode_longlong,
- test_ei_decode_ulonglong,
- test_ei_decode_char,
- test_ei_decode_nonoptimal,
- test_ei_decode_misc
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [test_ei_decode_long, test_ei_decode_ulong,
+ test_ei_decode_longlong, test_ei_decode_ulonglong,
+ test_ei_decode_char, test_ei_decode_nonoptimal,
+ test_ei_decode_misc].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% ---------------------------------------------------------------------------
@@ -181,22 +195,9 @@ test_ei_decode_misc(suite) -> [];
test_ei_decode_misc(Config) when is_list(Config) ->
?line P = runner:start(?test_ei_decode_misc),
-% ?line <<131>> = get_binaries(P),
-
-% ?line {term,F} = get_term(P),
-% ?line match_float(F, 0.0),
-% ?line {term,F} = get_term(P),
-% ?line match_float(F, 0.0),
-
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, -1.0),
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, -1.0),
-
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, 1.0),
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, 1.0),
+ ?line send_term_as_binary(P,0.0),
+ ?line send_term_as_binary(P,-1.0),
+ ?line send_term_as_binary(P,1.0),
?line send_term_as_binary(P,false),
?line send_term_as_binary(P,true),
@@ -235,15 +236,17 @@ send_integers(P) ->
?line send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*)
?line send_term_as_binary(P,-1), % INTEGER_EXT largest neg
- ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT largest (28 bits)
- ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT smallest
- ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT smallest pos(*)
- ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT largest neg (*)
+ ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits)
+ ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest
+ ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*)
+ ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*)
- ?line send_term_as_binary(P, 16#7fffffff), % SMALL_BIG_EXT largest i32
- ?line send_term_as_binary(P,-16#80000000), % SMALL_BIG_EXT smallest i32
-
- case erlang:system_info(wordsize) of
+ ?line send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits)
+ ?line send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest (32 bis)
+ ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*)
+ ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*)
+
+ case erlang:system_info({wordsize,external}) of
4 ->
?line send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32
?line send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32
@@ -279,15 +282,17 @@ send_integers2(P) ->
?line send_term_as_binary(P,255), % SMALL_INTEGER_EXT largest
?line send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*)
?line send_term_as_binary(P,-1), % INTEGER_EXT largest neg
+
+ ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits)
+ ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest
+ ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*)
+ ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*)
- ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT largest (28 bits)
- ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT smallest
- ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT smallest pos(*)
- ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT largest neg (*)
+ ?line send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits)
+ ?line send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest
+ ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*)
+ ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*)
- ?line send_term_as_binary(P, 16#7fffffff), % SMALL_BIG_EXT largest i32
- ?line send_term_as_binary(P,-16#80000000), % SMALL_BIG_EXT smallest i32
- ?line send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32
?line send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32
?line send_term_as_binary(P, 16#7fffffffffff), % largest i48
diff --git a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
index d81ea88437..b349138ae9 100644
--- a/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
+++ b/lib/erl_interface/test/ei_decode_SUITE_data/ei_decode_test.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -246,13 +246,23 @@ TESTCASE(test_ei_decode_long)
EI_DECODE_2 (decode_long, 5, long, 256);
EI_DECODE_2 (decode_long, 5, long, -1);
+ /* Old 28 bit limits for INTEGER_EXT */
EI_DECODE_2 (decode_long, 5, long, 0x07ffffff);
EI_DECODE_2 (decode_long, 5, long, -0x08000000);
- EI_DECODE_2 (decode_long, 7, long, 0x08000000);
- EI_DECODE_2 (decode_long, 7, long, -0x08000001);
+ EI_DECODE_2 (decode_long, 5, long, 0x08000000);
+ EI_DECODE_2 (decode_long, 5, long, -0x08000001);
- EI_DECODE_2 (decode_long, 7, long, 0x7fffffff);
- EI_DECODE_2 (decode_long, 7, long, -ll(0x80000000)); /* Strange :-( */
+ /* New 32 bit limits for INTEGER_EXT */
+ EI_DECODE_2 (decode_long, 5, long, 0x7fffffff);
+ EI_DECODE_2 (decode_long, 5, long, -ll(0x80000000)); /* Strange :-( */
+ if (sizeof(long) > 4) {
+ EI_DECODE_2(decode_long, 7, long, 0x80000000);
+ EI_DECODE_2(decode_long, 7, long, -ll(0x80000001));
+ }
+ else {
+ EI_DECODE_2_FAIL(decode_long, 7, long, 0x80000000);
+ EI_DECODE_2_FAIL(decode_long, 7, long, -ll(0x80000001));
+ }
EI_DECODE_2_FAIL(decode_long, 7, long, 0x80000000);
EI_DECODE_2_FAIL(decode_long, 7, long, 0xffffffff);
@@ -280,11 +290,13 @@ TESTCASE(test_ei_decode_ulong)
EI_DECODE_2 (decode_ulong, 5, unsigned long, 0x07ffffff);
EI_DECODE_2_FAIL(decode_ulong, 5, unsigned long, -0x08000000);
- EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x08000000);
- EI_DECODE_2_FAIL(decode_ulong, 7, unsigned long, -0x08000001);
+ EI_DECODE_2 (decode_ulong, 5, unsigned long, 0x08000000);
+ EI_DECODE_2_FAIL(decode_ulong, 5, unsigned long, -0x08000001);
- EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x7fffffff);
- EI_DECODE_2_FAIL(decode_ulong, 7, unsigned long, -ll(0x80000000));
+ EI_DECODE_2 (decode_ulong, 5, unsigned long, 0x7fffffff);
+ EI_DECODE_2_FAIL(decode_ulong, 5, unsigned long, -ll(0x80000000));
+ EI_DECODE_2 (decode_ulong, 7, unsigned long, 0x80000000);
+ EI_DECODE_2_FAIL(decode_ulong, 7, unsigned long, -ll(0x80000001));
if (sizeof(long) > 4) {
EI_DECODE_2 (decode_ulong, 11, unsigned long, ll(0x8000000000000000));
@@ -319,13 +331,14 @@ TESTCASE(test_ei_decode_longlong)
EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 0x07ffffff);
EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, -0x08000000);
- EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0x08000000);
- EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, -0x08000001);
-
- EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0x7fffffff);
- EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, -ll(0x80000000));
+ EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 0x08000000);
+ EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, -0x08000001);
+ EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, 0x7fffffff);
+ EI_DECODE_2 (decode_longlong, 5, EI_LONGLONG, -ll(0x80000000));
EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0x80000000);
+ EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, -ll(0x80000001));
+
EI_DECODE_2 (decode_longlong, 7, EI_LONGLONG, 0xffffffff);
EI_DECODE_2 (decode_longlong, 9, EI_LONGLONG, ll(0x7fffffffffff));
@@ -352,13 +365,14 @@ TESTCASE(test_ei_decode_ulonglong)
EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 0x07ffffff);
EI_DECODE_2_FAIL(decode_ulonglong, 5, EI_ULONGLONG, -0x08000000);
- EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0x08000000);
- EI_DECODE_2_FAIL(decode_ulonglong, 7, EI_ULONGLONG, -0x08000001);
-
- EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0x7fffffff);
- EI_DECODE_2_FAIL(decode_ulonglong, 7, EI_ULONGLONG, -ll(0x80000000));
+ EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 0x08000000);
+ EI_DECODE_2_FAIL(decode_ulonglong, 5, EI_ULONGLONG, -0x08000001);
+ EI_DECODE_2 (decode_ulonglong, 5, EI_ULONGLONG, 0x7fffffff);
+ EI_DECODE_2_FAIL(decode_ulonglong, 5, EI_ULONGLONG, -ll(0x80000000));
EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0x80000000);
+ EI_DECODE_2_FAIL(decode_ulonglong, 7, EI_ULONGLONG, -0x80000001);
+
EI_DECODE_2 (decode_ulonglong, 7, EI_ULONGLONG, 0xffffffff);
EI_DECODE_2 (decode_ulonglong, 9, EI_ULONGLONG, ll(0x7fffffffffff));
@@ -515,11 +529,10 @@ TESTCASE(test_ei_decode_misc)
/*
EI_DECODE_0(decode_version);
*/
-/*
- EI_DECODE_2(decode_double, 0.0);
- EI_DECODE_2(decode_double, -1.0);
- EI_DECODE_2(decode_double, 1.0);
-*/
+ EI_DECODE_2(decode_double, 32, double, 0.0);
+ EI_DECODE_2(decode_double, 32, double, -1.0);
+ EI_DECODE_2(decode_double, 32, double, 1.0);
+
EI_DECODE_2(decode_boolean, 8, int, 0);
EI_DECODE_2(decode_boolean, 7, int, 1);
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
index c19c1d0887..85cb62239b 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,19 +20,36 @@
%%
-module(ei_decode_encode_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_decode_encode_SUITE_data/ei_decode_encode_test_cases.hrl").
-export(
[
- all/1,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
test_ei_decode_encode/1
]).
-all(suite) ->
- [
- test_ei_decode_encode
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [test_ei_decode_encode].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% ---------------------------------------------------------------------------
diff --git a/lib/erl_interface/test/ei_encode_SUITE.erl b/lib/erl_interface/test/ei_encode_SUITE.erl
index fb790eb7c3..cefd33e5f6 100644
--- a/lib/erl_interface/test/ei_encode_SUITE.erl
+++ b/lib/erl_interface/test/ei_encode_SUITE.erl
@@ -1,31 +1,32 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(ei_encode_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_encode_SUITE_data/ei_encode_test_cases.hrl").
-export(
[
- all/1,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
test_ei_encode_long/1,
test_ei_encode_ulong/1,
test_ei_encode_longlong/1,
@@ -35,16 +36,29 @@
test_ei_encode_fails/1
]).
-all(suite) ->
- [
- test_ei_encode_long,
- test_ei_encode_ulong,
- test_ei_encode_longlong,
- test_ei_encode_ulonglong,
- test_ei_encode_char,
- test_ei_encode_misc,
- test_ei_encode_fails
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [test_ei_encode_long, test_ei_encode_ulong,
+ test_ei_encode_longlong, test_ei_encode_ulonglong,
+ test_ei_encode_char, test_ei_encode_misc,
+ test_ei_encode_fails].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% ---------------------------------------------------------------------------
@@ -181,20 +195,14 @@ test_ei_encode_misc(Config) when is_list(Config) ->
?line <<131>> = get_binaries(P),
-% ?line {term,F} = get_term(P),
-% ?line match_float(F, 0.0),
-% ?line {term,F} = get_term(P),
-% ?line match_float(F, 0.0),
+ ?line {<<70,_:8/binary>>,F0} = get_buf_and_term(P),
+ ?line true = match_float(F0, 0.0),
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, -1.0),
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, -1.0),
+ ?line {<<70,_:8/binary>>,Fn1} = get_buf_and_term(P),
+ ?line true = match_float(Fn1, -1.0),
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, 1.0),
-% ?line {term,F} = get_term(P),
-% ?line true = match_float(F, 1.0),
+ ?line {<<70,_:8/binary>>,Fp1} = get_buf_and_term(P),
+ ?line true = match_float(Fp1, 1.0),
?line {<<100,0,5,"false">>,false} = get_buf_and_term(P),
?line {<<100,0,4,"true">> ,true} = get_buf_and_term(P),
@@ -310,6 +318,8 @@ get_term(P) ->
%%
+match_float(F, Match) when is_float(F), is_float(Match), F == Match ->
+ true;
match_float(F, Match) when is_float(F), F > Match*0.99, F < Match*1.01 ->
true.
diff --git a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
index f8de0b7878..c373658152 100644
--- a/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
+++ b/lib/erl_interface/test/ei_encode_SUITE_data/ei_encode_test.c
@@ -1,19 +1,19 @@
/*
* %CopyrightBegin%
- *
- * Copyright Ericsson AB 2004-2009. All Rights Reserved.
- *
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
* retrieved online at http://www.erlang.org/.
- *
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
+ *
* %CopyrightEnd%
*/
@@ -350,13 +350,13 @@ TESTCASE(test_ei_encode_char)
TESTCASE(test_ei_encode_misc)
{
EI_ENCODE_0(encode_version);
-/*
+
EI_ENCODE_1(encode_double, 0.0);
EI_ENCODE_1(encode_double, -1.0);
EI_ENCODE_1(encode_double, 1.0);
-*/
+
EI_ENCODE_1(encode_boolean, 0) /* Only case it should be false */;
EI_ENCODE_1(encode_boolean, 1);
diff --git a/lib/erl_interface/test/ei_format_SUITE.erl b/lib/erl_interface/test/ei_format_SUITE.erl
index 7871f07ae9..2a26ed142b 100644
--- a/lib/erl_interface/test/ei_format_SUITE.erl
+++ b/lib/erl_interface/test/ei_format_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,15 +20,17 @@
%%
-module(ei_format_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_format_SUITE_data/ei_format_test_cases.hrl").
-export([
- format_wo_ver/1,
- all/1,
- atoms/1,
- tuples/1,
- lists/1
+ format_wo_ver/1,
+ all/0, suite/0,groups/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ atoms/1,
+ tuples/1,
+ lists/1
]).
-import(runner, [get_term/1]).
@@ -36,12 +38,26 @@
%% This test suite test the erl_format() function.
%% It uses the port program "ei_format_test".
-all(suite) -> [
- format_wo_ver,
- atoms,
- tuples,
- lists
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [format_wo_ver, atoms, tuples, lists].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Tests formatting various atoms.
@@ -155,7 +171,7 @@ format_wo_ver(suite) -> [];
format_wo_ver(Config) when is_list(Config) ->
?line P = runner:start(?format_wo_ver),
- ?line {term, [{a, "b"}, {c, 10}]} = get_term(P),
+ ?line {term, [-1, 2, $c, {a, "b"}, {c, 10}]} = get_term(P),
?line runner:recv_eot(P),
ok.
diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
index a969ded3dc..4f6c15ba9c 100644
--- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
+++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2001-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2001-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -176,7 +176,7 @@ TESTCASE(format_wo_ver) {
ei_x_buff x;
ei_x_new (&x);
- ei_x_format(&x, "[{~a,~s},{~a,~i}]", "a", "b", "c", 10);
+ ei_x_format(&x, "[-1, +2, ~c, {~a,~s},{~a,~i}]", 'c', "a", "b", "c", 10);
send_bin_term(&x);
free(x.buff);
diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl
index a0f15338c6..2a3ed81f53 100644
--- a/lib/erl_interface/test/ei_print_SUITE.erl
+++ b/lib/erl_interface/test/ei_print_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,17 +20,38 @@
%%
-module(ei_print_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ei_print_SUITE_data/ei_print_test_cases.hrl").
--export([all/1, atoms/1, tuples/1, lists/1, strings/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ atoms/1, tuples/1, lists/1, strings/1]).
-import(runner, [get_term/1]).
%% This test suite test the ei_print() function.
%% It uses the port program "ei_format_test".
-all(suite) -> [atoms, tuples, lists, strings].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [atoms, tuples, lists, strings].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Tests formatting various atoms.
diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl
index 0c211aa148..7ff8c08280 100644
--- a/lib/erl_interface/test/ei_tmo_SUITE.erl
+++ b/lib/erl_interface/test/ei_tmo_SUITE.erl
@@ -1,37 +1,58 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(ei_tmo_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/inet.hrl").
-include("ei_tmo_SUITE_data/ei_tmo_test_cases.hrl").
-define(dummy_host,test01).
--export([all/1, init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
framework_check/1, ei_accept_tmo/1, ei_connect_tmo/1, ei_send_tmo/1,
ei_recv_tmo/1]).
-all(suite) -> [framework_check,ei_accept_tmo,ei_connect_tmo,
- ei_send_tmo,ei_recv_tmo].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [framework_check, ei_accept_tmo, ei_connect_tmo,
+ ei_send_tmo, ei_recv_tmo].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?t:minutes(1)),
@@ -43,7 +64,7 @@ init_per_testcase(_Case, Config) ->
end,
[{vxsim,Bool},{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -349,10 +370,12 @@ make_and_check_dummy() ->
-define(DFLAG_ATOM_CACHE,2).
-define(DFLAG_EXTENDED_REFERENCES,4).
-define(DFLAG_EXTENDED_PIDS_PORTS,16#100).
+-define(DFLAG_NEW_FLOATS,16#800).
-define(DFLAG_DIST_MONITOR,8).
%% From R9 and forward extended references is compulsory
--define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS)).
+%% From 14 and forward new float is compulsory
+-define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS bor ?DFLAG_NEW_FLOATS)).
-define(shutdown(X), exit(X)).
-define(int16(X), [((X) bsr 8) band 16#ff, (X) band 16#ff]).
diff --git a/lib/erl_interface/test/erl_connect_SUITE.erl b/lib/erl_interface/test/erl_connect_SUITE.erl
index 0d6539d98f..bd54013402 100644
--- a/lib/erl_interface/test/erl_connect_SUITE.erl
+++ b/lib/erl_interface/test/erl_connect_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,22 +20,42 @@
%%
-module(erl_connect_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("erl_connect_SUITE_data/erl_connect_test_cases.hrl").
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
erl_send/1,erl_reg_send/1, erl_send_cookie_file/1]).
-import(runner, [get_term/1,send_term/2]).
-all(suite) ->
- [erl_send,erl_reg_send,erl_send_cookie_file].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [erl_send, erl_reg_send, erl_send_cookie_file].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
Dog = ?t:timetrap(?t:minutes(0.25)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/erl_interface/test/erl_eterm_SUITE.erl b/lib/erl_interface/test/erl_eterm_SUITE.erl
index 634e2f9aa0..10a27e48e3 100644
--- a/lib/erl_interface/test/erl_eterm_SUITE.erl
+++ b/lib/erl_interface/test/erl_eterm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
%%
-module(erl_eterm_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("erl_eterm_SUITE_data/eterm_test_cases.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -33,7 +33,9 @@
%%% 5. Miscellanous functions.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([all/1, build_terms/1, round_trip_conversion/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ build_terms/1, round_trip_conversion/1,
decode_terms/1, decode_float/1,
t_erl_mk_int/1, t_erl_mk_list/1,
basic_copy/1,
@@ -73,38 +75,38 @@
%% This test suite controls the running of the C language functions
%% in eterm_test.c and print_term.c.
-all(suite) -> [build_terms, round_trip_conversion,
- decode_terms, decode_float,
- t_erl_mk_int, t_erl_mk_list,
- basic_copy,
- t_erl_mk_atom,
- t_erl_mk_binary,
- t_erl_mk_empty_list,
- t_erl_mk_float,
- t_erl_mk_pid,
- t_erl_mk_xpid,
- t_erl_mk_port,
- t_erl_mk_xport,
- t_erl_mk_ref,
- t_erl_mk_long_ref,
- t_erl_mk_string,
- t_erl_mk_estring,
- t_erl_mk_tuple,
- t_erl_mk_uint,
- t_erl_mk_var,
- t_erl_size,
- t_erl_var_content,
- t_erl_element,
- t_erl_cons,
- t_erl_length, t_erl_hd, t_erl_tl,
- type_checks, extractor_macros,
- t_erl_iolist_length, t_erl_iolist_to_binary,
- t_erl_iolist_to_string,
- erl_print_term, print_string,
- t_erl_free_compound,
- high_chaparal,
- broken_data,
- cnode_1].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [build_terms, round_trip_conversion, decode_terms,
+ decode_float, t_erl_mk_int, t_erl_mk_list, basic_copy,
+ t_erl_mk_atom, t_erl_mk_binary, t_erl_mk_empty_list,
+ t_erl_mk_float, t_erl_mk_pid, t_erl_mk_xpid,
+ t_erl_mk_port, t_erl_mk_xport, t_erl_mk_ref,
+ t_erl_mk_long_ref, t_erl_mk_string, t_erl_mk_estring,
+ t_erl_mk_tuple, t_erl_mk_uint, t_erl_mk_var, t_erl_size,
+ t_erl_var_content, t_erl_element, t_erl_cons,
+ t_erl_length, t_erl_hd, t_erl_tl, type_checks,
+ extractor_macros, t_erl_iolist_length,
+ t_erl_iolist_to_binary, t_erl_iolist_to_string,
+ erl_print_term, print_string, t_erl_free_compound,
+ high_chaparal, broken_data, cnode_1].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
index 6b2ec8f766..80d7f69520 100644
--- a/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
+++ b/lib/erl_interface/test/erl_eterm_SUITE_data/eterm_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1997-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -63,32 +63,124 @@ TESTCASE(build_terms)
report(1);
}
+static int abs_and_sign(ETERM* v, unsigned long long* av, int* sign)
+{
+ long long sv;
+ switch (ERL_TYPE(v)) {
+ case ERL_INTEGER: sv = ERL_INT_VALUE(v); break;
+ case ERL_U_INTEGER: *av = ERL_INT_UVALUE(v); *sign = 0; return 1;
+ case ERL_LONGLONG: sv = ERL_LL_VALUE(v); break;
+ case ERL_U_LONGLONG: *av = ERL_LL_UVALUE(v); *sign = 0; return 1;
+ default: return 0;
+ }
+ if (sv < 0) {
+ *av = -sv;
+ *sign = 1;
+ }
+ else {
+ *av = sv;
+ *sign = 0;
+ }
+ return 1;
+}
+
+/* Shouldn't erl_match() cope with this?
+*/
+static int eq_ints(ETERM* a, ETERM* b)
+{
+ unsigned long long a_abs, b_abs;
+ int a_sign, b_sign;
+ return abs_and_sign(a, &a_abs, &a_sign) && abs_and_sign(b, &b_abs, &b_sign)
+ && (a_abs == b_abs) && (a_sign == b_sign);
+}
+
+static void encode_decode(ETERM* original, const char* text)
+{
+ static unsigned char encoded[16*1024];
+ ETERM* new_terms;
+ ETERM* head;
+ int bytes;
+ int len;
+
+ /* If a list, check the elements one by one first */
+ head = erl_hd(original);
+ if (head != NULL) {
+ encode_decode(head, "CAR");
+ encode_decode(erl_tl(original), "CDR");
+ }
+
+ bytes = erl_encode(original, encoded);
+ if (bytes == 0) {
+ fail("failed to encode terms");
+ }
+ else if (bytes > sizeof(encoded)) {
+ fail("encoded terms buffer overflow");
+ }
+ else if (bytes != (len=erl_term_len(original))) {
+ fprintf(stderr, "bytes(%d) != len(%d) for term ", bytes, len);
+ erl_print_term(stderr, original);
+ fprintf(stderr, " [%s]\r\n", text);
+ fail("erl_encode and erl_term_len do not agree");
+ }
+ else if ((new_terms = erl_decode(encoded)) == NULL) {
+ fail("failed to decode terms");
+ }
+ else if (!erl_match(original, new_terms) && !eq_ints(original, new_terms)) {
+ erl_print_term(stderr, original);
+ fprintf(stderr, "(%i) != (%i)", ERL_TYPE(original), ERL_TYPE(new_terms));
+ erl_print_term(stderr, new_terms);
+ fprintf(stderr, " [%s]\r\n", text);
+ fail("decoded terms didn't match original");
+ }
+ erl_free_term(original);
+ erl_free_term(new_terms);
+}
/*
* Converts an Erlang term to the external term format and back again.
*/
TESTCASE(round_trip_conversion)
{
- ETERM* original;
- ETERM* new_terms;
- char encoded[16*1024];
- int n;
+ int n, i;
erl_init(NULL, 0);
- original = all_types();
- if (erl_encode(original, encoded) == 0)
+ encode_decode(all_types(), "ALL");
+
{
- fail("failed to encode terms");
- } else if ((new_terms = erl_decode(encoded)) == NULL)
+ int v;
+ for (v = 8; v; v <<= 1) {
+ for (i=-4; i<4; i++) {
+ encode_decode(erl_mk_int(v+i), "INT");
+ encode_decode(erl_mk_int(-(v+i)), "NEG INT");
+ }
+ }
+ }
{
- fail("failed to decode terms");
- } else if (!erl_match(original, new_terms))
+ unsigned int v;
+ for (v = 8; v; v <<= 1) {
+ for (i=-4; i<4; i++) {
+ encode_decode(erl_mk_uint(v+i), "UINT");
+ }
+ }
+ }
{
- fail("decoded terms didn't match original");
+ long long v;
+ for (v = 8; v; v <<= 1) {
+ for (i=-4; i<4; i++) {
+ encode_decode(erl_mk_longlong(v+i), "LONGLONG");
+ encode_decode(erl_mk_longlong(-(v+i)), "NEG LONGLONG");
+ }
+ }
+ }
+ {
+ unsigned long long v;
+ for (v = 8; v; v <<= 1) {
+ for (i=-4; i<4; i++) {
+ encode_decode(erl_mk_ulonglong(v+i), "ULONGLONG");
+ }
+ }
}
- erl_free_term(original);
- erl_free_term(new_terms);
report(1);
}
diff --git a/lib/erl_interface/test/erl_ext_SUITE.erl b/lib/erl_interface/test/erl_ext_SUITE.erl
index dbafea0e39..fc3e823d42 100644
--- a/lib/erl_interface/test/erl_ext_SUITE.erl
+++ b/lib/erl_interface/test/erl_ext_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,27 +20,42 @@
%%
-module(erl_ext_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("erl_ext_SUITE_data/ext_test_cases.hrl").
-export([
- all/1,
- compare_tuple/1,
- compare_list/1,
- compare_string/1,
- compare_list_string/1,
- compare_nc_ext/1
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ compare_tuple/1,
+ compare_list/1,
+ compare_string/1,
+ compare_list_string/1,
+ compare_nc_ext/1
]).
-import(runner, [get_term/1]).
-all(suite) -> [
- compare_tuple,
- compare_list,
- compare_string,
- compare_list_string,
- compare_nc_ext
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [compare_tuple, compare_list, compare_string,
+ compare_list_string, compare_nc_ext].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
compare_tuple(suite) -> [];
compare_tuple(doc) -> [];
diff --git a/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c b/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c
index ba1a6c66da..a4a8da6347 100644
--- a/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c
+++ b/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2002-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2002-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -82,6 +82,11 @@ TESTCASE(compare_list) {
// erlang:term_to_binary([34,{a,n},a,erlang])
unsigned char term2[] = {131,108,0,0,0,4,97,34,104,2,100,0,1,97,100,0,1,110,100,0,1,97,100,0,6,101,114,108,97,110,103,106};
+ // erlang:term_to_binary([0])
+ unsigned char term3[] = {131,107,0,1,0};
+ // erlang:term_to_binary([0, 1000])
+ unsigned char term4[] = {131,108,0,0,0,2,97,0,98,0,0,3,232,106};
+
erl_init(NULL, 0);
start_a = term1;
start_b = term2;
@@ -90,6 +95,13 @@ TESTCASE(compare_list) {
test_compare_ext("lists", start_a, end_a, start_b, end_b, 1);
+ start_a = term3;
+ start_b = term4;
+ end_a = term3 + sizeof(term3);
+ end_b = term4 + sizeof(term4);
+
+ test_compare_ext("lists1", start_a, end_a, start_b, end_b, -1);
+
report(1);
}
diff --git a/lib/erl_interface/test/erl_format_SUITE.erl b/lib/erl_interface/test/erl_format_SUITE.erl
index 81a0bca80f..c722bd050f 100644
--- a/lib/erl_interface/test/erl_format_SUITE.erl
+++ b/lib/erl_interface/test/erl_format_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,17 +20,37 @@
%%
-module(erl_format_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("erl_format_SUITE_data/format_test_cases.hrl").
--export([all/1, atoms/1, tuples/1, lists/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, atoms/1, tuples/1, lists/1]).
-import(runner, [get_term/1]).
%% This test suite test the erl_format() function.
%% It uses the port program "format_test".
-all(suite) -> [atoms, tuples, lists].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [atoms, tuples, lists].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Tests formatting various atoms.
diff --git a/lib/erl_interface/test/erl_global_SUITE.erl b/lib/erl_interface/test/erl_global_SUITE.erl
new file mode 100644
index 0000000000..a27cb0664c
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE.erl
@@ -0,0 +1,142 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(erl_global_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+-include("erl_global_SUITE_data/erl_global_test_cases.hrl").
+
+-export([all/0,suite/0,init_per_suite/1,end_per_suite/1,
+ init_per_testcase/2,end_per_testcase/2,
+ erl_global_registration/1, erl_global_whereis/1, erl_global_names/1]).
+
+-import(runner, [get_term/1,send_term/2]).
+
+-define(GLOBAL_NAME, global_register_node_test).
+
+all() ->
+ [erl_global_registration, erl_global_whereis, erl_global_names].
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?t:minutes(0.25)),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+erl_global_registration(Config) when is_list(Config) ->
+ ?line P = runner:start(?interpret),
+ ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+
+ ?line ok = erl_global_register(P, Fd, ?GLOBAL_NAME),
+ ?line ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME),
+
+ ?line 0 = erl_close_connection(P,Fd),
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
+erl_global_whereis(Config) when is_list(Config) ->
+ ?line P = runner:start(?interpret),
+ ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+
+ ?line Self = self(),
+ ?line yes = global:register_name(?GLOBAL_NAME, Self),
+ ?line Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME),
+ ?line global:unregister_name(?GLOBAL_NAME),
+ ?line 0 = erl_close_connection(P, Fd),
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
+erl_global_names(Config) when is_list(Config) ->
+ ?line P = runner:start(?interpret),
+ ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0),
+
+ ?line Self = self(),
+ ?line global:register_name(?GLOBAL_NAME, Self),
+ ?line {Names1, _N1} = erl_global_names(P, Fd),
+ ?line true = lists:member(atom_to_list(?GLOBAL_NAME), Names1),
+ ?line global:unregister_name(?GLOBAL_NAME),
+ ?line {Names2, _N2} = erl_global_names(P, Fd),
+ ?line false = lists:member(atom_to_list(?GLOBAL_NAME), Names2),
+ ?line 0 = erl_close_connection(P, Fd),
+ ?line runner:send_eot(P),
+ ?line runner:recv_eot(P),
+ ok.
+
+%%% Interface functions for erl_interface functions.
+
+erl_connect(P, Node, Num, Cookie, Creation) ->
+ send_command(P, erl_connect, [Num, Node, Cookie, Creation]),
+ case get_term(P) of
+ {term,{Fd,_}} when Fd >= 0 -> {ok,Fd};
+ {term,{-1,Errno}} -> {error,Errno}
+ end.
+
+erl_close_connection(P, FD) ->
+ send_command(P, erl_close_connection, [FD]),
+ case get_term(P) of
+ {term,Int} when is_integer(Int) -> Int
+ end.
+
+erl_global_register(P, Fd, Name) ->
+ send_command(P, erl_global_register, [Fd,Name]),
+ get_send_result(P).
+
+erl_global_whereis(P, Fd, Name) ->
+ send_command(P, erl_global_whereis, [Fd,Name]),
+ case get_term(P) of
+ {term, What} ->
+ What
+ end.
+
+erl_global_names(P, Fd) ->
+ send_command(P, erl_global_names, [Fd]),
+ case get_term(P) of
+ {term, What} ->
+ What
+ end.
+
+erl_global_unregister(P, Fd, Name) ->
+ send_command(P, erl_global_unregister, [Fd,Name]),
+ get_send_result(P).
+
+get_send_result(P) ->
+ case get_term(P) of
+ {term,{1,_}} -> ok;
+ {term,{0, 0}} -> ok;
+ {term,{-1, Errno}} -> {error,Errno};
+ {term,{_,_}}->
+ ?t:fail(bad_return_value)
+ end.
+
+send_command(P, Name, Args) ->
+ runner:send_term(P, {Name,list_to_tuple(Args)}).
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first
new file mode 100644
index 0000000000..8e3fcb924e
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first
@@ -0,0 +1,21 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+erl_global_test_decl.c: erl_global_test.c
+ erl -noinput -pa ../all_SUITE_data -s init_tc run erl_global_test -s erlang halt
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..ef846bc440
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src
@@ -0,0 +1,41 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2000-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include @erl_interface_mk_include@@[email protected]
+
+CC0 = @CC@
+CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)"
+LD = @LD@
+LIBPATH = @erl_interface_libpath@
+LIBERL = $(LIBPATH)/@erl_interface_lib@
+LIBEI = $(LIBPATH)/@erl_interface_eilib@
+LIBFLAGS = ../all_SUITE_data/runner@obj@ \
+ $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \
+ @erl_interface_threadlib@
+CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data
+OBJS = erl_global_test@obj@ erl_global_test_decl@obj@
+
+all: erl_global_test@exe@
+
+erl_global_test@exe@: $(OBJS) $(LIBERL) $(LIBEI)
+ $(LD) @CROSSLDFLAGS@ -o $@ $(OBJS) $(LIBFLAGS)
+
+clean:
+ $(RM) $(OBJS)
+ $(RM) erl_global_test@exe@
diff --git a/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c
new file mode 100644
index 0000000000..dc0d8a0091
--- /dev/null
+++ b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c
@@ -0,0 +1,263 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2000-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Purpose: Tests the functions in erl_global.c.
+ *
+ * See the erl_global_SUITE.erl file for a "table of contents".
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "runner.h"
+
+static void cmd_erl_connect(ETERM* args);
+static void cmd_erl_global_register(ETERM *args);
+static void cmd_erl_global_whereis(ETERM *args);
+static void cmd_erl_global_names(ETERM *args);
+static void cmd_erl_global_unregister(ETERM *args);
+static void cmd_erl_close_connection(ETERM *args);
+
+static void send_errno_result(int value);
+
+static struct {
+ char* name;
+ int num_args; /* Number of arguments. */
+ void (*func)(ETERM* args);
+} commands[] = {
+ "erl_connect", 4, cmd_erl_connect,
+ "erl_close_connection", 1, cmd_erl_close_connection,
+ "erl_global_register", 2, cmd_erl_global_register,
+ "erl_global_whereis", 2, cmd_erl_global_whereis,
+ "erl_global_names", 1, cmd_erl_global_names,
+ "erl_global_unregister", 2, cmd_erl_global_unregister,
+};
+
+
+/*
+ * Sends a list contaning all data types to the Erlang side.
+ */
+
+TESTCASE(interpret)
+{
+ ETERM* term;
+
+ erl_init(NULL, 0);
+
+ outer_loop:
+
+ term = get_term();
+
+ if (term == NULL) {
+ report(1);
+ return;
+ } else {
+ ETERM* Func;
+ ETERM* Args;
+ int i;
+
+ if (!ERL_IS_TUPLE(term) || ERL_TUPLE_SIZE(term) != 2) {
+ fail("term should be a tuple of size 2");
+ }
+
+ Func = erl_element(1, term);
+ if (!ERL_IS_ATOM(Func)) {
+ fail("function name should be an atom");
+ }
+ Args = erl_element(2, term);
+ if (!ERL_IS_TUPLE(Args)) {
+ fail("function arguments should be a tuple");
+ }
+ erl_free_term(term);
+ for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) {
+ int n = strlen(commands[i].name);
+ if (ERL_ATOM_SIZE(Func) != n) {
+ continue;
+ }
+ if (memcmp(ERL_ATOM_PTR(Func), commands[i].name, n) == 0) {
+ erl_free_term(Func);
+ if (ERL_TUPLE_SIZE(Args) != commands[i].num_args) {
+ fail("wrong number of arguments");
+ }
+ commands[i].func(Args);
+ erl_free_term(Args);
+ goto outer_loop;
+ }
+ }
+ fail("bad command");
+ }
+}
+
+#define VERIFY_TYPE(Test, Term) \
+if (!Test(Term)) { \
+ fail("wrong type for " #Term); \
+} else { \
+}
+
+static void
+cmd_erl_connect(ETERM* args)
+{
+ ETERM* number;
+ ETERM* node;
+ ETERM* cookie;
+
+ int res;
+ char buffer[256];
+
+ number = ERL_TUPLE_ELEMENT(args, 0);
+ VERIFY_TYPE(ERL_IS_INTEGER, number);
+ node = ERL_TUPLE_ELEMENT(args, 1);
+ VERIFY_TYPE(ERL_IS_ATOM, node);
+ cookie = ERL_TUPLE_ELEMENT(args, 2);
+ VERIFY_TYPE(ERL_IS_ATOM, cookie);
+
+ if (ERL_ATOM_SIZE(cookie) == 0) {
+ res = erl_connect_init(ERL_INT_VALUE(number), 0, 0);
+ } else {
+ memcpy(buffer, ERL_ATOM_PTR(cookie), ERL_ATOM_SIZE(cookie));
+ buffer[ERL_ATOM_SIZE(cookie)] = '\0';
+ res = erl_connect_init(ERL_INT_VALUE(number), buffer, 0);
+ }
+
+ if(!res) {
+ send_errno_result(res);
+ return;
+ }
+
+ memcpy(buffer, ERL_ATOM_PTR(node), ERL_ATOM_SIZE(node));
+ buffer[ERL_ATOM_SIZE(node)] = '\0';
+ send_errno_result(erl_connect(buffer));
+}
+
+static void
+cmd_erl_close_connection(ETERM* args)
+{
+ ETERM* number;
+ ETERM* res;
+
+ number = ERL_TUPLE_ELEMENT(args, 0);
+ VERIFY_TYPE(ERL_IS_INTEGER, number);
+ res = erl_mk_int(erl_close_connection(ERL_INT_VALUE(number)));
+ send_term(res);
+ erl_free_term(res);
+}
+
+static void
+cmd_erl_global_register(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+ ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
+ ETERM* pid = erl_mk_pid(erl_thisnodename(), 14, 0, 0);
+
+ char buffer[256];
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+ VERIFY_TYPE(ERL_IS_ATOM, name);
+
+ memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
+ buffer[ERL_ATOM_SIZE(name)] = '\0';
+
+ send_errno_result(erl_global_register(ERL_INT_VALUE(fd_term), buffer, pid));
+ erl_free_term(pid);
+}
+
+static void
+cmd_erl_global_whereis(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+ ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
+ ETERM* pid = NULL;
+
+ char buffer[256];
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+ VERIFY_TYPE(ERL_IS_ATOM, name);
+
+ memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
+ buffer[ERL_ATOM_SIZE(name)] = '\0';
+
+ pid = erl_global_whereis(ERL_INT_VALUE(fd_term), buffer, NULL);
+ send_term(pid);
+ erl_free_term(pid);
+}
+
+static void
+cmd_erl_global_names(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+
+ ETERM* res_array[2], *res_tuple, *name;
+ char** names = NULL;
+ int count = 0, i;
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+
+ names = erl_global_names(ERL_INT_VALUE(fd_term), &count);
+
+ res_array[0] = erl_mk_empty_list();
+ for(i=0; i<count; i++) {
+ name = erl_mk_string(names[i]);
+ res_array[0] = erl_cons(name, res_array[0]);
+ }
+
+ free(names);
+
+ res_array[1] = erl_mk_int(count);
+ res_tuple = erl_mk_tuple(res_array, 2);
+
+ send_term(res_tuple);
+
+ erl_free_compound(res_array[0]);
+ erl_free_term(res_array[1]);
+ erl_free_term(res_tuple);
+}
+
+static void
+cmd_erl_global_unregister(ETERM* args)
+{
+ ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0);
+ ETERM* name = ERL_TUPLE_ELEMENT(args, 1);
+
+ char buffer[256];
+
+ VERIFY_TYPE(ERL_IS_INTEGER, fd_term);
+ VERIFY_TYPE(ERL_IS_ATOM, name);
+
+ memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name));
+ buffer[ERL_ATOM_SIZE(name)] = '\0';
+
+ send_errno_result(erl_global_unregister(ERL_INT_VALUE(fd_term), buffer));
+}
+
+static void
+send_errno_result(int value)
+{
+ ETERM* res_array[2];
+ ETERM* res_tuple;
+
+ res_array[0] = erl_mk_int(value);
+ res_array[1] = erl_mk_int(erl_errno);
+ res_tuple = erl_mk_tuple(res_array, 2);
+ send_term(res_tuple);
+ erl_free_term(res_array[0]);
+ erl_free_term(res_array[1]);
+ erl_free_term(res_tuple);
+}
diff --git a/lib/erl_interface/test/erl_interface.cover b/lib/erl_interface/test/erl_interface.cover
new file mode 100644
index 0000000000..879201a3cd
--- /dev/null
+++ b/lib/erl_interface/test/erl_interface.cover
@@ -0,0 +1,2 @@
+{incl_app,erl_interface,details}.
+
diff --git a/lib/erl_interface/test/erl_interface.spec b/lib/erl_interface/test/erl_interface.spec
index 2789bd3e2c..a0a7acfa50 100644
--- a/lib/erl_interface/test/erl_interface.spec
+++ b/lib/erl_interface/test/erl_interface.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../erl_interface_test"}}.
-
+{suites,"../erl_interface_test",all}.
diff --git a/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl
index f506638544..e019fecca8 100644
--- a/lib/erl_interface/test/erl_match_SUITE.erl
+++ b/lib/erl_interface/test/erl_match_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,10 +20,12 @@
%%
-module(erl_match_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("erl_match_SUITE_data/match_test_cases.hrl").
--export([all/1, atoms/1, lists/1, tuples/1, references/1, pids/1, ports/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ atoms/1, lists/1, tuples/1, references/1, pids/1, ports/1,
bind/1, integers/1, floats/1, binaries/1, strings/1]).
%% For interactive running of matcher.
@@ -31,8 +33,27 @@
%% This test suite tests the erl_match() function.
-all(suite) -> [atoms, lists, tuples, references, pids, ports, bind,
- integers, floats, binaries, strings].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [atoms, lists, tuples, references, pids, ports, bind,
+ integers, floats, binaries, strings].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
atoms(suite) -> [];
atoms(Config) when is_list(Config) ->
diff --git a/lib/erl_interface/test/port_call_SUITE.erl b/lib/erl_interface/test/port_call_SUITE.erl
index 895e29ad2e..1ce5b0b748 100644
--- a/lib/erl_interface/test/port_call_SUITE.erl
+++ b/lib/erl_interface/test/port_call_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,17 +31,37 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--export([all/1, basic/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, basic/1]).
% Private exports
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-all(suite) ->
- [basic].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+[basic].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
basic(suite) -> [];
basic(Config) when is_list(Config) ->
case os:type() of
+ {unix, linux} ->
+ do_basic(Config);
{unix, sunos} ->
do_basic(Config);
{win32,_} ->
diff --git a/lib/erl_interface/test/runner.erl b/lib/erl_interface/test/runner.erl
index b72723c6a5..e41440708a 100644
--- a/lib/erl_interface/test/runner.erl
+++ b/lib/erl_interface/test/runner.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index 589b9e2f9c..0317462106 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1 +1 @@
-EI_VSN = 3.6.5
+EI_VSN = 3.7.3
diff --git a/lib/et/doc/src/et.xml b/lib/et/doc/src/et.xml
index 9b170dd7d9..5e3453c348 100644
--- a/lib/et/doc/src/et.xml
+++ b/lib/et/doc/src/et.xml
@@ -29,6 +29,7 @@
<checked></checked>
<date></date>
<rev>%VSN%</rev>
+ <file>et</file>
</header>
<module>et</module>
<modulesummary>Main API of the Event Trace (ET) application</modulesummary>
diff --git a/lib/et/doc/src/et_collector.xml b/lib/et/doc/src/et_collector.xml
index 88c478c89a..e9885dcbb3 100644
--- a/lib/et/doc/src/et_collector.xml
+++ b/lib/et/doc/src/et_collector.xml
@@ -29,6 +29,7 @@
<checked></checked>
<date></date>
<rev>%VSN%</rev>
+ <file>et_collector.xml</file>
</header>
<module>et_collector</module>
<modulesummary>Collect trace events and provide a backing storage appropriate for iteration </modulesummary>
diff --git a/lib/et/doc/src/et_selector.xml b/lib/et/doc/src/et_selector.xml
index dd12166d85..34203306bb 100644
--- a/lib/et/doc/src/et_selector.xml
+++ b/lib/et/doc/src/et_selector.xml
@@ -29,6 +29,7 @@
<checked></checked>
<date></date>
<rev>%VSN%</rev>
+ <file>et_selector.xml</file>
</header>
<module>et_selector</module>
<modulesummary>Define event transforms and trace patterns</modulesummary>
diff --git a/lib/et/doc/src/et_tutorial.xmlsrc b/lib/et/doc/src/et_tutorial.xmlsrc
index c72234a587..1337af76d1 100644
--- a/lib/et/doc/src/et_tutorial.xmlsrc
+++ b/lib/et/doc/src/et_tutorial.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2009</year><year>2009</year>
+ <year>2009</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,6 +29,7 @@
<checked></checked>
<date></date>
<rev>%VSN%</rev>
+ <file>et_tutorial.xml</file>
</header>
<section>
diff --git a/lib/et/doc/src/et_viewer.xml b/lib/et/doc/src/et_viewer.xml
index c16e5b8869..d4cfbdfa31 100644
--- a/lib/et/doc/src/et_viewer.xml
+++ b/lib/et/doc/src/et_viewer.xml
@@ -29,6 +29,7 @@
<checked></checked>
<date></date>
<rev>%VSN%</rev>
+ <file>et_viewer.xml</file>
</header>
<module>et_viewer</module>
<modulesummary>Displays a sequence chart for trace events (messages/actions)</modulesummary>
diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml
index 8611955d3d..cd4787c5e7 100644
--- a/lib/et/doc/src/notes.xml
+++ b/lib/et/doc/src/notes.xml
@@ -36,6 +36,47 @@
one section in this document. The title of each section is the
version number of <c>Event Tracer (ET)</c>.</p>
+<section><title>ET 1.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix error when module et was used in et_selector
+ trace patterns. </p>
+ <p>
+ Own Id: OTP-8904</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ET 1.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fixed broken links in the documentation. </p>
+ <p>
+ Own Id: OTP-8796</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed gui crash on windows.</p>
+ <p>
+ Own Id: OTP-8830</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>ET 1.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/et/src/et_selector.erl b/lib/et/src/et_selector.erl
index f39f21aa70..c8e9c907b2 100644
--- a/lib/et/src/et_selector.erl
+++ b/lib/et/src/et_selector.erl
@@ -115,13 +115,13 @@ change_pattern({Mod, Pattern}) when is_atom(Mod) ->
old_ctp({Mod, _Fun, Args}) ->
case Mod of
- et -> ignore;
+ et -> {ok, ignore};
_ -> dbg:ctp({Mod, report_event, Args})
end.
old_tp({Mod, _Fun, Args}, Pattern) ->
case Mod of
- et -> ignore;
+ et -> {ok, ignore};
_ -> dbg:tp({Mod, report_event, Args}, Pattern)
end.
diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl
index 8a8d9ef1ee..aada184a76 100644
--- a/lib/et/src/et_wx_contents_viewer.erl
+++ b/lib/et/src/et_wx_contents_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl
index 5cd3563aed..d42f8c0c86 100644
--- a/lib/et/src/et_wx_viewer.erl
+++ b/lib/et/src/et_wx_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1233,7 +1233,7 @@ create_main_window(S) ->
[{flag, ?wxEXPAND}]),
CanvasSizer = wxBoxSizer:new(?wxHORIZONTAL),
- Canvas = wxPanel:new(Panel, []),
+ Canvas = wxPanel:new(Panel, [{style, ?wxFULL_REPAINT_ON_RESIZE}]),
{CanvasW,CanvasH} = wxPanel:getSize(Canvas),
ScrollBar = wxScrollBar:new(Panel, ?wxID_ANY, [{style, ?wxSB_VERTICAL}]),
@@ -1244,7 +1244,13 @@ create_main_window(S) ->
wxPanel:connect(Canvas, left_up),
wxPanel:connect(Canvas, right_up),
wxPanel:connect(Canvas, size),
- wxPanel:connect(Canvas, paint),
+ Self = self(),
+ wxPanel:connect(Canvas, paint, [{callback, %% Needed on windows
+ fun(Ev, _) ->
+ DC = wxPaintDC:new(Canvas),
+ wxPaintDC:destroy(DC),
+ Self ! Ev
+ end}]),
wxPanel:connect(Canvas, key_down),
wxPanel:connect(Canvas, kill_focus),
wxPanel:connect(Canvas, enter_window, [{skip, true}]),
@@ -1437,6 +1443,7 @@ create_help_menu(Bar) ->
clear_canvas(S) ->
DC = wxClientDC:new(S#state.canvas),
+ wxDC:setBackground(DC, ?wxWHITE_BRUSH), %% Needed on mac
wxDC:clear(DC),
{CanvasW, CanvasH} = wxPanel:getSize(S#state.canvas),
wxSizer:recalcSizes(S#state.canvas_sizer),
diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile
index 9aedf96ce9..e10a2a1587 100644
--- a/lib/et/test/Makefile
+++ b/lib/et/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -71,9 +71,10 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) et.spec $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
- $(INSTALL_PROGRAM) ett $(INSTALL_PROGS) $(RELSYSDIR)
-# chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) et.spec et.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+ $(INSTALL_SCRIPT) ett $(RELSYSDIR)
+ $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR)
+# chmod -R u+w $(RELSYSDIR)
# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/et/test/et.cover b/lib/et/test/et.cover
new file mode 100644
index 0000000000..471e6d985d
--- /dev/null
+++ b/lib/et/test/et.cover
@@ -0,0 +1,2 @@
+{incl_app,et,details}.
+
diff --git a/lib/et/test/et.spec b/lib/et/test/et.spec
index 69cd8d7582..09993a217a 100644
--- a/lib/et/test/et.spec
+++ b/lib/et/test/et.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../et_test"}}.
-
+{suites,"../et_test",all}.
diff --git a/lib/et/test/et_test_lib.erl b/lib/et/test/et_test_lib.erl
index b91b63786c..c1bfeb9fc0 100644
--- a/lib/et/test/et_test_lib.erl
+++ b/lib/et/test/et_test_lib.erl
@@ -95,7 +95,7 @@ wx_init_per_suite(Config) ->
exit({skipped, "Can not test on MacOSX"});
{unix, _} ->
io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
- case proplists:get_value(xserver, Config, none) of
+ case ct:get_config(xserver, none) of
none -> ignore;
Server -> os:putenv("DISPLAY", Server)
end;
@@ -295,7 +295,7 @@ eval_test_case(Mod, Fun, Config) ->
test_case_evaluator(Mod, Fun, [Config]) ->
NewConfig = Mod:init_per_testcase(Fun, Config),
R = apply(Mod, Fun, [NewConfig]),
- Mod:fin_per_testcase(Fun, NewConfig),
+ Mod:end_per_testcase(Fun, NewConfig),
exit({test_case_ok, R}).
wait_for_evaluator(Pid, Mod, Fun, Config) ->
@@ -311,12 +311,12 @@ wait_for_evaluator(Pid, Mod, Fun, Config) ->
{'EXIT', Pid, {skipped, Reason}} ->
log("<WARNING> Test case ~w skipped, because ~p~n",
[{Mod, Fun}, Reason]),
- Mod:fin_per_testcase(Fun, Config),
+ Mod:end_per_testcase(Fun, Config),
{skip, {Mod, Fun}, Reason};
{'EXIT', Pid, Reason} ->
log("<ERROR> Eval process ~w exited, because\n\t~p~n",
[{Mod, Fun}, Reason]),
- Mod:fin_per_testcase(Fun, Config),
+ Mod:end_per_testcase(Fun, Config),
{crash, {Mod, Fun}, Reason}
end.
diff --git a/lib/et/test/et_wx_SUITE.erl b/lib/et/test/et_wx_SUITE.erl
index 1a16ca69a3..b5f98f8616 100644
--- a/lib/et/test/et_wx_SUITE.erl
+++ b/lib/et/test/et_wx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,8 +18,9 @@
-module(et_wx_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -36,16 +37,22 @@ init_per_testcase(Func,Config) ->
et_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
et_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- et_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- start_all_windows
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start_all_windows].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% The test cases
diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk
index b7d65b12a2..d7cfd7bc84 100644
--- a/lib/et/vsn.mk
+++ b/lib/et/vsn.mk
@@ -1,24 +1 @@
-# This is an -*-makefile-*- file.
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2002-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-
-ET_VSN = 1.4
-TICKETS = OTP-8058
-
-TICKETS_1_3_3 = OTP-8201
-TICKETS_1_3_2 = OTP-8078
-TICKETS_1_3_1 = OTP-7830
+ET_VSN = 1.4.2
diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc
index 2583f0be25..be05a13fba 100644
--- a/lib/eunit/doc/overview.edoc
+++ b/lib/eunit/doc/overview.edoc
@@ -913,7 +913,7 @@ To make the descriptions simpler, we first list some definitions:
<td>`CleanupX'</td><td>`(X::any(), R::any()) -> any()'</td>
</tr>
<tr>
-<td>`Instantiator'</td><td>`((R::any()) -> Tests) | {with, [AbstractTestFun::((any()) -> any())]}'</td>
+<td>`Instantiator'</td><td>`((R::any()) -> Tests | {with, [AbstractTestFun::((any()) -> any())]}'</td>
</tr>
<tr>
<td>`Where'</td><td>`local | spawn | {spawn, Node::atom()}'</td>
diff --git a/lib/eunit/doc/src/Makefile b/lib/eunit/doc/src/Makefile
index 19be96d763..2cdc579275 100644
--- a/lib/eunit/doc/src/Makefile
+++ b/lib/eunit/doc/src/Makefile
@@ -146,7 +146,7 @@ debug opt:
clean clean_docs:
rm -rf $(HTMLDIR)/*
rm -f $(MAN3DIR)/*
- rm -f $(XML_CHAPTER_FILES) *.html
+ rm -f $(XML_REF3_FILES) $(XML_CHAPTER_FILES) *.html
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
rm -f errs core *~
diff --git a/lib/eunit/doc/src/book.xml b/lib/eunit/doc/src/book.xml
index 4444b1dd7a..eb044c1a66 100644
--- a/lib/eunit/doc/src/book.xml
+++ b/lib/eunit/doc/src/book.xml
@@ -5,7 +5,7 @@
<header titlestyle="normal">
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 974ba1db4e..a9960153e5 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,21 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.1.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fix format_man_pages so it handles all man sections
+ and remove warnings/errors in various man pages. </p>
+ <p>
+ Own Id: OTP-8600</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.1.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/eunit/doc/src/part.xml b/lib/eunit/doc/src/part.xml
index e31a8d1b78..84e5aec039 100644
--- a/lib/eunit/doc/src/part.xml
+++ b/lib/eunit/doc/src/part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/eunit/doc/src/part_notes.xml b/lib/eunit/doc/src/part_notes.xml
index 28644f961b..191d69b915 100644
--- a/lib/eunit/doc/src/part_notes.xml
+++ b/lib/eunit/doc/src/part_notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/eunit/doc/src/ref_man.xml b/lib/eunit/doc/src/ref_man.xml
index 02feef5e97..eb46ceda1e 100644
--- a/lib/eunit/doc/src/ref_man.xml
+++ b/lib/eunit/doc/src/ref_man.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl
index 59084a52fb..4a86a108cf 100644
--- a/lib/eunit/src/eunit.erl
+++ b/lib/eunit/src/eunit.erl
@@ -16,7 +16,7 @@
%% $Id: eunit.erl 339 2009-04-05 14:10:47Z rcarlsson $
%%
%% @copyright 2004-2009 Micka�l R�mond, Richard Carlsson
-%% @author Micka�l R�mond <[email protected]>
+%% @author Micka&euml;l R&eacute;mond <[email protected]>
%% [http://www.process-one.net/]
%% @author Richard Carlsson <[email protected]>
%% [http://user.it.uu.se/~richardc/]
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index aeda31d251..eb994a990a 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -15,7 +15,7 @@
%%
%% $Id: $
%%
-%% @author Micka�l R�mond <[email protected]>
+%% @author Micka&euml;l R&eacute;mond <[email protected]>
%% @copyright 2009 Micka�l R�mond, Paul Guyot
%% @see eunit
%% @doc Surefire reports for EUnit (Format used by Maven and Atlassian
diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile
index 74d485d1cc..a2d276f619 100644
--- a/lib/eunit/test/Makefile
+++ b/lib/eunit/test/Makefile
@@ -75,7 +75,7 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) eunit.dynspec $(EMAKEFILE) \
+ $(INSTALL_DATA) eunit.spec $(EMAKEFILE) \
$(COVERFILE) $(ERL_FILES) \
$(RELSYSDIR)
diff --git a/lib/eunit/test/eunit.cover b/lib/eunit/test/eunit.cover
index d1eaf770b6..00c09127a8 100644
--- a/lib/eunit/test/eunit.cover
+++ b/lib/eunit/test/eunit.cover
@@ -1,3 +1,5 @@
+{incl_app,eunit,details}.
+
%% -*- erlang -*-
-{exclude,[eunit_test]}.
+{excl_mods,eunit,[eunit_test]}.
diff --git a/lib/eunit/test/eunit.dynspec b/lib/eunit/test/eunit.dynspec
deleted file mode 100644
index c1d345ac14..0000000000
--- a/lib/eunit/test/eunit.dynspec
+++ /dev/null
@@ -1,6 +0,0 @@
-%% -*- erlang -*-
-%% You can test this file using this command.
-%% file:script("eunit.dynspec", [{'Os',"Unix"}]).
-
-[].
-
diff --git a/lib/eunit/test/eunit.spec b/lib/eunit/test/eunit.spec
new file mode 100644
index 0000000000..2db7731a7e
--- /dev/null
+++ b/lib/eunit/test/eunit.spec
@@ -0,0 +1,3 @@
+%% -*- erlang -*-
+{suites,"../eunit_test",all}.
+
diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl
index 4ebcec6f5d..47c2435d63 100644
--- a/lib/eunit/test/eunit_SUITE.erl
+++ b/lib/eunit/test/eunit_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,13 +18,32 @@
%%
-module(eunit_SUITE).
--export([all/1,eunit_test/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,eunit_test/1]).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[eunit_test].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
eunit_test(Config) when is_list(Config) ->
ok = file:set_cwd(code:lib_dir(eunit)),
ok = eunit:test(eunit).
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 3bfa9c8000..e1965630e3 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.1.5
+EUNIT_VSN = 2.1.6
diff --git a/lib/gs/contribs/bonk/bonk.erl b/lib/gs/contribs/bonk/bonk.erl
index 12d94f6c5e..79f01bf659 100644
--- a/lib/gs/contribs/bonk/bonk.erl
+++ b/lib/gs/contribs/bonk/bonk.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -33,10 +33,10 @@ run() ->
run([ColorMode]) -> % This is for the start script...
run(ColorMode);
-run(ColorMode) when atom(ColorMode) ->
+run(ColorMode) when is_atom(ColorMode) ->
GS = gs:start(),
- SoundPid=spawn_link(bonk_sound,start,[]),
- {H,M,S}=time(),
+ SoundPid = spawn_link(bonk_sound,start,[]),
+ {H,M,S} = time(),
random:seed(H*13,M*7,S*3),
{SqrPids, Bmps, Colors} = create_board(GS, ColorMode),
{ScoreL,_File} = get_highscore(),
@@ -96,7 +96,7 @@ init(SoundPid, SqrPids, Bmps, Colors) ->
game(SoundPid, SqrPids, Bmps, Colors, Scores) ->
receive
- {gs, _Square, buttonpress, SqrPid, [1 | _Rest]} when pid(SqrPid) ->
+ {gs, _Square, buttonpress, SqrPid, [1 | _Rest]} when is_pid(SqrPid) ->
SqrPid ! bonk,
game(SoundPid, SqrPids, Bmps, Colors, Scores);
{gs, _Id, buttonpress, _Data, [Butt | _Rest]} when Butt =/= 1 ->
@@ -224,11 +224,9 @@ update_score(SoundPid, SqrPids, Scores) ->
send_to_all([], _Msg) ->
true;
-
-send_to_all([Pid|Rest],Msg) when pid(Pid) ->
+send_to_all([Pid|Rest],Msg) when is_pid(Pid) ->
Pid ! Msg,
send_to_all(Rest,Msg);
-
send_to_all([_Else|Rest],Msg) ->
send_to_all(Rest,Msg).
@@ -460,7 +458,7 @@ update_scorelist(SoundPid, Scores) ->
{ScoreL,FileName} = get_highscore(),
New_scorelist=update_scorelist_2(ScoreL, Score, 0, SoundPid),
display_highscore(New_scorelist),
- case file:open(FileName, write) of
+ case file:open(FileName, [write]) of
{error,_} ->
true;
{ok,FD} ->
@@ -559,7 +557,7 @@ display_about() ->
{activebg, BGColor}]),
gs:create(text, aboutText, aboutCan, [{width, Wid-30}, {coords, [{15, 0}]},
{fg, TextColor}, {justify, center}]),
- case file:open(lists:append(bonk_dir(),"bonk.txt"), read) of
+ case file:open(lists:append(bonk_dir(),"bonk.txt"), [read]) of
{ok, Fd} ->
write_text(Fd, "", io:get_line(Fd, "")),
file:close(Fd);
diff --git a/lib/gs/contribs/othello/othello_adt.erl b/lib/gs/contribs/othello/othello_adt.erl
index d1d3ec950b..fb60c30b89 100644
--- a/lib/gs/contribs/othello/othello_adt.erl
+++ b/lib/gs/contribs/othello/othello_adt.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -375,29 +375,29 @@ is_good(Colour,H,Board) ->
false.
is_good_0(_,_,false,_) -> false;
-is_good_0(_,H,D,_) when integer(H), integer(D), H+D<0 -> false;
-is_good_0(_,H,D,_) when integer(H), integer(D), H+D>63 -> false;
-is_good_0(black,H,D,Board) when integer(H), integer(D) ->
+is_good_0(_,H,D,_) when is_integer(H), is_integer(D), H+D<0 -> false;
+is_good_0(_,H,D,_) when is_integer(H), is_integer(D), H+D>63 -> false;
+is_good_0(black,H,D,Board) when is_integer(H), is_integer(D) ->
case element((H+D)+1,Board) of
white -> is_good_1(black,H+D,dir(H+D,D),Board);
_ -> false
end;
-is_good_0(white,H,D,Board) when integer(H), integer(D) ->
+is_good_0(white,H,D,Board) when is_integer(H), is_integer(D) ->
case element((H+D)+1,Board) of
black -> is_good_1(white,H+D,dir(H+D,D),Board);
_ -> false
end.
is_good_1(_,_,false,_) -> false;
-is_good_1(_,H,D,_) when integer(H), integer(D), H+D<0 -> false;
-is_good_1(_,H,D,_) when integer(H), integer(D), H+D>63 -> false;
-is_good_1(black,H,D,Board) when integer(H), integer(D) ->
+is_good_1(_,H,D,_) when is_integer(H), is_integer(D), H+D<0 -> false;
+is_good_1(_,H,D,_) when is_integer(H), is_integer(D), H+D>63 -> false;
+is_good_1(black,H,D,Board) when is_integer(H), is_integer(D) ->
case element((H+D)+1,Board) of
white -> is_good_1(black,H+D,dir(H+D,D),Board);
black -> throw(true);
_ -> false
end;
-is_good_1(white,H,D,Board) when integer(H), integer(D) ->
+is_good_1(white,H,D,Board) when is_integer(H), is_integer(D) ->
case element((H+D)+1,Board) of
black -> is_good_1(white,H+D,dir(H+D,D),Board);
white -> throw(true);
@@ -429,15 +429,15 @@ turn(Colour,H,D,Board) ->
Board
end.
-turn_0(_,H,D,B) when integer(H), integer(D), H+D<0 -> B;
-turn_0(_,H,D,B) when integer(H), integer(D), H+D>63 -> B;
-turn_0(black,H,D,Board) when integer(H), integer(D) ->
+turn_0(_,H,D,B) when is_integer(H), is_integer(D), H+D<0 -> B;
+turn_0(_,H,D,B) when is_integer(H), is_integer(D), H+D>63 -> B;
+turn_0(black,H,D,Board) when is_integer(H), is_integer(D) ->
E = H+D,
case element(E+1,Board) of
white -> turn_0(black,H+D,D,swap(black,E,Board));
_ -> Board
end;
-turn_0(white,H,D,Board) when integer(H), integer(D) ->
+turn_0(white,H,D,Board) when is_integer(H), is_integer(D) ->
E = H+D,
case element(E+1,Board) of
black -> turn_0(white,H+D,D,swap(white,E,Board));
@@ -450,7 +450,7 @@ turn_0(white,H,D,Board) when integer(H), integer(D) ->
%% Neighbours are not changed !!
%%-------------------------------------------------------
-swap(Colour,Pos,Board) when integer(Pos) ->
+swap(Colour,Pos,Board) when is_integer(Pos) ->
setelement(Pos+1,Board,Colour).
score(Pos) -> score1({col(Pos),row(Pos)}).
diff --git a/lib/gs/doc/src/gs.xml b/lib/gs/doc/src/gs.xml
index b1c7e505dc..f2182fc673 100644
--- a/lib/gs/doc/src/gs.xml
+++ b/lib/gs/doc/src/gs.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2010</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,18 @@
<module>gs</module>
<modulesummary>The Graphics System for Erlang.</modulesummary>
<description>
+ <warning>
+ <p>
+ GS is not recommended for use in new applications.
+ Instead we recommend WX for applications that need a
+ graphical user interface.
+ </p>
+ <p>
+ GS is not maintained and we plan to deprecate and remove it from
+ the distribution as soon as possible, maybe already in the next
+ major release (R15).
+ </p>
+ </warning>
<p>The Graphics System, GS, is easy to learn and
designed to be portable to many different platforms.</p>
<p>In the description below, the type <c><![CDATA[gsobj()]]></c> denotes a
diff --git a/lib/gs/doc/src/gs_chapter1.xml b/lib/gs/doc/src/gs_chapter1.xml
index cfcae94f7c..912d6e1ef9 100644
--- a/lib/gs/doc/src/gs_chapter1.xml
+++ b/lib/gs/doc/src/gs_chapter1.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,6 +31,18 @@
<section>
<title>Introduction</title>
+ <warning>
+ <p>
+ GS is not recommended for use in new applications.
+ Instead we recommend WX for applications that need a
+ graphical user interface.
+ </p>
+ <p>
+ GS is not maintained and we plan to deprecate and remove it from
+ the distribution as soon as possible, maybe already in the next
+ major release (R15).
+ </p>
+ </warning>
<p>This section describes the general graphics interface to Erlang. This system was designed with the following requirements in mind:</p>
<list type="bulleted">
<item>a graphics system which is easy to learn</item>
diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml
index 8e4032ccc1..744efbd4fc 100644
--- a/lib/gs/doc/src/notes.xml
+++ b/lib/gs/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>GS Release Notes</title>
@@ -30,7 +30,27 @@
</header>
<p>This document describes the changes made to the GS application.</p>
- <section>
+ <section><title>GS 1.5.13</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation for GS is updated with the warning that
+ it should not be used in new applications. GS is planned
+ to be deprecated soon and might be removed from the
+ distribution already in the next major release (R15). For
+ graphical applications we recommend the use of WX
+ instead.</p>
+ <p>
+ Own Id: OTP-8824</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section>
<title>GS 1.5.10</title>
<section>
@@ -47,7 +67,22 @@
</section>
</section>
- <section><title>GS 1.5.11</title>
+ <section><title>GS 1.5.12</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warnings due to new autoimported BIFs removed</p>
+ <p>
+ Own Id: OTP-8674 Aux Id: OTP-8579 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>GS 1.5.11</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/gs/src/tool_utils.erl b/lib/gs/src/tool_utils.erl
index 697dd07151..b07e92c4f0 100644
--- a/lib/gs/src/tool_utils.erl
+++ b/lib/gs/src/tool_utils.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -224,11 +224,11 @@ help_win(Type, Parent, Strings) ->
{Wbtn0,Hbtn0} = gs:read(Lbl, {font_wh,{Font,"Cancel"}}),
%% Compute size of the objects and adjust the graphics accordingly
- Wbtn = max(Wbtn0+10, ?Wbtn),
- Hbtn = max(Hbtn0+10, ?Hbtn),
- Hent = max(Hent0+10, ?Hent),
- Wlbl = max(Wlbl0, max(Nbtn*Wbtn+(Nbtn-1)*?PAD, ?Wlbl)),
- Hlbl = max(Hlbl0, ?Hlbl),
+ Wbtn = erlang:max(Wbtn0+10, ?Wbtn),
+ Hbtn = erlang:max(Hbtn0+10, ?Hbtn),
+ Hent = erlang:max(Hent0+10, ?Hent),
+ Wlbl = erlang:max(Wlbl0, erlang:max(Nbtn*Wbtn+(Nbtn-1)*?PAD, ?Wlbl)),
+ Hlbl = erlang:max(Hlbl0, ?Hlbl),
Wwin = ?PAD+Wlbl+?PAD,
@@ -297,9 +297,6 @@ data("Yes") -> {helpwin,yes};
data("No") -> {helpwin,no};
data("Cancel") -> {helpwin,cancel}.
-max(X, Y) when X>Y -> X;
-max(_X, Y) -> Y.
-
get_coords(Parent, W, H) ->
case gs:read(Parent, x) of
X when is_integer(X) ->
diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk
index 701d0e178d..4c91857572 100644
--- a/lib/gs/vsn.mk
+++ b/lib/gs/vsn.mk
@@ -1,2 +1,2 @@
-GS_VSN = 1.5.11
+GS_VSN = 1.5.13
diff --git a/lib/hipe/cerl/cerl_closurean.erl b/lib/hipe/cerl/cerl_closurean.erl
index 12771668ac..021acd5b35 100644
--- a/lib/hipe/cerl/cerl_closurean.erl
+++ b/lib/hipe/cerl/cerl_closurean.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% =====================================================================
@@ -808,7 +808,7 @@ take_work({Queue0, Set0}) ->
is_escape_op(match_fail, 1) -> false;
is_escape_op(F, A) when is_atom(F), is_integer(A) -> true.
--spec is_escape_op(module(), atom(), arity()) -> boolean().
+-spec is_escape_op(atom(), atom(), arity()) -> boolean().
is_escape_op(erlang, error, 1) -> false;
is_escape_op(erlang, error, 2) -> false;
@@ -825,7 +825,7 @@ is_escape_op(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> true.
is_literal_op(match_fail, 1) -> true;
is_literal_op(F, A) when is_atom(F), is_integer(A) -> false.
--spec is_literal_op(module(), atom(), arity()) -> boolean().
+-spec is_literal_op(atom(), atom(), arity()) -> boolean().
is_literal_op(erlang, '+', 2) -> true;
is_literal_op(erlang, '-', 2) -> true;
diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl
index 0753376e7d..6dd93adaa3 100644
--- a/lib/hipe/cerl/cerl_messagean.erl
+++ b/lib/hipe/cerl/cerl_messagean.erl
@@ -1,19 +1,19 @@
%% =====================================================================
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Message analysis of Core Erlang programs.
@@ -1043,7 +1043,7 @@ get_deps(L, Dep) ->
%% is_escape_op(_F, _A) -> [].
--spec is_escape_op(module(), atom(), arity()) -> [arity()].
+-spec is_escape_op(atom(), atom(), arity()) -> [arity()].
is_escape_op(erlang, '!', 2) -> [2];
is_escape_op(erlang, send, 2) -> [2];
@@ -1064,7 +1064,7 @@ is_escape_op(_M, _F, _A) -> [].
is_imm_op(match_fail, 1) -> true;
is_imm_op(_, _) -> false.
--spec is_imm_op(module(), atom(), arity()) -> boolean().
+-spec is_imm_op(atom(), atom(), arity()) -> boolean().
is_imm_op(erlang, self, 0) -> true;
is_imm_op(erlang, '=:=', 2) -> true;
@@ -1102,4 +1102,4 @@ is_imm_op(erlang, throw, 1) -> true;
is_imm_op(erlang, exit, 1) -> true;
is_imm_op(erlang, error, 1) -> true;
is_imm_op(erlang, error, 2) -> true;
-is_imm_op(_, _, _) -> false.
+is_imm_op(_M, _F, _A) -> false.
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index be3073c0e6..30b911c41b 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -48,6 +48,7 @@
t_boolean/0,
t_byte/0,
t_char/0,
+ t_charlist/0,
t_cons/0,
t_cons/2,
t_cons_hd/1,
@@ -124,7 +125,8 @@
t_tuple/1,
t_tuple_args/1,
t_tuple_size/1,
- t_tuple_subtypes/1
+ t_tuple_subtypes/1,
+ t_unicode_string/0
]).
-ifdef(DO_ERL_BIF_TYPES_TEST).
@@ -143,128 +145,65 @@ type(M, F, A) ->
-spec type(atom(), atom(), arity(), [erl_types:erl_type()]) -> erl_types:erl_type().
-%%-- code ---------------------------------------------------------------------
-type(code, add_path, 1, Xs) ->
- strict(arg_types(code, add_path, 1), Xs,
- fun (_) ->
- t_sup(t_boolean(),
- t_tuple([t_atom('error'), t_atom('bad_directory')]))
+%%-- binary -------------------------------------------------------------------
+type(binary, at, 2, Xs) ->
+ strict(arg_types(binary, at, 2), Xs, fun(_) -> t_integer() end);
+type(binary, bin_to_list, Arity, Xs) when 1 =< Arity, Arity =< 3 ->
+ strict(arg_types(binary, bin_to_list, Arity), Xs,
+ fun(_) -> t_list(t_integer()) end);
+type(binary, compile_pattern, 1, Xs) ->
+ strict(arg_types(binary, compile_pattern, 1), Xs,
+ fun(_) -> t_tuple([t_atom(bm),t_binary()]) end);
+type(binary, copy, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
+ strict(arg_types(binary, copy, Arity), Xs,
+ fun(_) -> t_binary() end);
+type(binary, decode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
+ strict(arg_types(binary, decode_unsigned, Arity), Xs,
+ fun(_) -> t_non_neg_integer() end);
+type(binary, encode_unsigned, Arity, Xs) when Arity =:= 1; Arity =:= 2 ->
+ strict(arg_types(binary, encode_unsigned, Arity), Xs,
+ fun(_) -> t_binary() end);
+type(binary, first, 1, Xs) ->
+ strict(arg_types(binary, first, 1), Xs, fun(_) -> t_non_neg_integer() end);
+type(binary, last, 1, Xs) ->
+ strict(arg_types(binary, last, 1), Xs, fun(_) -> t_non_neg_integer() end);
+type(binary, list_to_bin, 1, Xs) ->
+ type(erlang, list_to_binary, 1, Xs);
+type(binary, longest_common_prefix, 1, Xs) ->
+ strict(arg_types(binary, longest_common_prefix, 1), Xs,
+ fun(_) -> t_integer() end);
+type(binary, longest_common_suffix, 1, Xs) ->
+ strict(arg_types(binary, longest_common_suffix, 1), Xs,
+ fun(_) -> t_integer() end);
+type(binary, match, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
+ strict(arg_types(binary, match, Arity), Xs,
+ fun(_) ->
+ t_sup(t_atom('nomatch'), t_binary_canonical_part())
end);
-type(code, add_patha, 1, Xs) ->
- type(code, add_path, 1, Xs);
-type(code, add_paths, 1, Xs) ->
- strict(arg_types(code, add_paths, 1), Xs, fun(_) -> t_atom('ok') end);
-type(code, add_pathsa, 1, Xs) ->
- type(code, add_paths, 1, Xs);
-type(code, add_pathsz, 1, Xs) ->
- type(code, add_paths, 1, Xs);
-type(code, add_pathz, 1, Xs) ->
- type(code, add_path, 1, Xs);
-type(code, all_loaded, 0, _) ->
- t_list(t_tuple([t_atom(), t_code_loaded_fname_or_status()]));
-type(code, compiler_dir, 0, _) ->
- t_string();
-type(code, del_path, 1, Xs) ->
- strict(arg_types(code, del_path, 1), Xs,
- fun (_) ->
- t_sup(t_boolean(),
- t_tuple([t_atom('error'), t_atom('bad_name')]))
- end);
-type(code, delete, 1, Xs) ->
- strict(arg_types(code, delete, 1), Xs, fun (_) -> t_boolean() end);
-type(code, ensure_loaded, 1, Xs) ->
- type(code, load_file, 1, Xs);
+type(binary, matches, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
+ strict(arg_types(binary, matches, Arity), Xs,
+ fun(_) -> t_list(t_binary_canonical_part()) end);
+type(binary, part, 2, Xs) ->
+ type(erlang, binary_part, 2, Xs);
+type(binary, part, 3, Xs) ->
+ type(erlang, binary_part, 3, Xs);
+type(binary, referenced_byte_size, 1, Xs) ->
+ strict(arg_types(binary, referenced_byte_size, 1), Xs,
+ fun(_) -> t_non_neg_integer() end);
+%%-- code ---------------------------------------------------------------------
type(code, get_chunk, 2, Xs) ->
strict(arg_types(code, get_chunk, 2), Xs,
fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
-type(code, get_object_code, 1, Xs) ->
- strict(arg_types(code, get_object_code, 1), Xs,
- fun (_) ->
- t_sup(t_tuple([t_atom(), t_binary(), t_string()]),
- t_atom('error'))
- end);
-type(code, get_path, 0, _) ->
- t_list(t_string());
-type(code, is_loaded, 1, Xs) ->
- strict(arg_types(code, is_loaded, 1), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('file'), t_code_loaded_fname_or_status()]),
- t_atom('false')])
- end);
-type(code, is_sticky, 1, Xs) ->
- strict(arg_types(code, is_sticky, 1), Xs, fun (_) -> t_boolean() end);
type(code, is_module_native, 1, Xs) ->
strict(arg_types(code, is_module_native, 1), Xs,
fun (_) -> t_sup(t_boolean(), t_atom('undefined')) end);
-type(code, lib_dir, 0, _) ->
- t_string();
-type(code, lib_dir, 1, Xs) ->
- strict(arg_types(code, lib_dir, 1), Xs,
- fun (_) ->
- t_sup(t_string(),
- t_tuple([t_atom('error'), t_atom('bad_name')]))
- end);
-type(code, load_abs, 1, Xs) ->
- strict(arg_types(code, load_abs, 1), Xs,
- fun ([_File]) -> t_code_load_return(t_atom()) end); % XXX: cheating
-type(code, load_abs, 2, Xs) ->
- strict(arg_types(code, load_abs, 2), Xs,
- fun ([_File,Mod]) -> t_code_load_return(Mod) end);
-type(code, load_binary, 3, Xs) ->
- strict(arg_types(code, load_binary, 3), Xs,
- fun ([Mod,_File,_Bin]) -> t_code_load_return(Mod) end);
-type(code, load_file, 1, Xs) ->
- strict(arg_types(code, load_file, 1), Xs,
- fun ([Mod]) -> t_code_load_return(Mod) end);
-type(code, load_native_partial, 2, Xs) ->
- strict(arg_types(code, load_native_partial, 2), Xs,
- fun ([Mod,_Bin]) -> t_code_load_return(Mod) end);
-type(code, load_native_sticky, 3, Xs) ->
- strict(arg_types(code, load_native_sticky, 3), Xs,
- fun ([Mod,_Bin,_]) -> t_code_load_return(Mod) end);
type(code, module_md5, 1, Xs) ->
strict(arg_types(code, module_md5, 1), Xs,
fun (_) -> t_sup(t_binary(), t_atom('undefined')) end);
type(code, make_stub_module, 3, Xs) ->
strict(arg_types(code, make_stub_module, 3), Xs, fun ([Mod,_,_]) -> Mod end);
-type(code, priv_dir, 1, Xs) ->
- strict(arg_types(code, priv_dir, 1), Xs,
- fun (_) ->
- t_sup(t_string(), t_tuple([t_atom('error'), t_atom('bad_name')]))
- end);
-type(code, purge, 1, Xs) ->
- type(code, delete, 1, Xs);
-type(code, rehash, 0, _) -> t_atom('ok');
-type(code, replace_path, 2, Xs) ->
- strict(arg_types(code, replace_path, 2), Xs,
- fun (_) ->
- t_sup([t_atom('true'),
- t_tuple([t_atom('error'), t_atom('bad_name')]),
- t_tuple([t_atom('error'), t_atom('bad_directory')]),
- t_tuple([t_atom('error'),
- t_tuple([t_atom('badarg'), t_any()])])])
- end);
-type(code, root_dir, 0, _) ->
- t_string();
-type(code, set_path, 1, Xs) ->
- strict(arg_types(code, set_path, 1), Xs,
- fun (_) ->
- t_sup([t_atom('true'),
- t_tuple([t_atom('error'), t_atom('bad_path')]),
- t_tuple([t_atom('error'), t_atom('bad_directory')])])
- end);
-type(code, soft_purge, 1, Xs) ->
- type(code, delete, 1, Xs);
-type(code, stick_mod, 1, Xs) ->
- strict(arg_types(code, stick_mod, 1), Xs, fun (_) -> t_atom('true') end);
-type(code, unstick_mod, 1, Xs) ->
- type(code, stick_mod, 1, Xs);
-type(code, which, 1, Xs) ->
- strict(arg_types(code, which, 1), Xs,
- fun (_) ->
- t_sup([t_code_loaded_fname_or_status(),
- t_atom('non_existing')])
- end);
+type(code, rehash, 0, _) ->
+ t_atom('ok');
%%-- erl_ddll -----------------------------------------------------------------
type(erl_ddll, demonitor, 1, Xs) ->
type(erlang, demonitor, 1, Xs);
@@ -665,6 +604,14 @@ type(erlang, 'bnot', 1, Xs) ->
%% strict(arg_types(erlang, 'bnot', 1), Xs, fun (_) -> t_integer() end);
type(erlang, abs, 1, Xs) ->
strict(arg_types(erlang, abs, 1), Xs, fun ([X]) -> X end);
+type(erlang, adler32, 1, Xs) ->
+ strict(arg_types(erlang, adler32, 1), Xs, fun (_) -> t_adler32() end);
+type(erlang, adler32, 2, Xs) ->
+ strict(arg_types(erlang, adler32, 2), Xs, fun (_) -> t_adler32() end);
+type(erlang, adler32_combine, 3, Xs) ->
+ strict(arg_types(erlang, adler32_combine, 3), Xs,
+ fun (_) -> t_adler32() end);
+type(erlang, append, 2, Xs) -> type(erlang, '++', 2, Xs); % alias
type(erlang, append_element, 2, Xs) ->
strict(arg_types(erlang, append_element, 2), Xs, fun (_) -> t_tuple() end);
type(erlang, apply, 2, Xs) ->
@@ -683,6 +630,10 @@ type(erlang, atom_to_binary, 2, Xs) ->
strict(arg_types(erlang, atom_to_binary, 2), Xs, fun (_) -> t_binary() end);
type(erlang, atom_to_list, 1, Xs) ->
strict(arg_types(erlang, atom_to_list, 1), Xs, fun (_) -> t_string() end);
+type(erlang, binary_part, 2, Xs) ->
+ strict(arg_types(erlang, binary_part, 2), Xs, fun (_) -> t_binary() end);
+type(erlang, binary_part, 3, Xs) ->
+ strict(arg_types(erlang, binary_part, 3), Xs, fun (_) -> t_binary() end);
type(erlang, binary_to_atom, 2, Xs) ->
strict(arg_types(erlang, binary_to_atom, 2), Xs, fun (_) -> t_atom() end);
type(erlang, binary_to_existing_atom, 2, Xs) ->
@@ -726,11 +677,11 @@ type(erlang, check_process_code, 2, Xs) ->
type(erlang, concat_binary, 1, Xs) ->
strict(arg_types(erlang, concat_binary, 1), Xs, fun (_) -> t_binary() end);
type(erlang, crc32, 1, Xs) ->
- strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_integer() end);
+ strict(arg_types(erlang, crc32, 1), Xs, fun (_) -> t_crc32() end);
type(erlang, crc32, 2, Xs) ->
- strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_integer() end);
+ strict(arg_types(erlang, crc32, 2), Xs, fun (_) -> t_crc32() end);
type(erlang, crc32_combine, 3, Xs) ->
- strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_integer() end);
+ strict(arg_types(erlang, crc32_combine, 3), Xs, fun (_) -> t_crc32() end);
type(erlang, date, 0, _) ->
t_date();
type(erlang, decode_packet, 3, Xs) ->
@@ -752,6 +703,10 @@ type(erlang, demonitor, 2, Xs) ->
type(erlang, disconnect_node, 1, Xs) ->
strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_boolean() end);
type(erlang, display, 1, _) -> t_atom('true');
+type(erlang, display_string, 1, Xs) ->
+ strict(arg_types(erlang, display_string, 1), Xs, fun(_) -> t_atom('true') end);
+type(erlang, display_nl, 0, _) ->
+ t_atom('true');
type(erlang, dist_exit, 3, Xs) ->
strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end);
type(erlang, element, 2, Xs) ->
@@ -802,6 +757,8 @@ type(erlang, fun_to_list, 1, Xs) ->
type(erlang, garbage_collect, 0, _) -> t_atom('true');
type(erlang, garbage_collect, 1, Xs) ->
strict(arg_types(erlang, garbage_collect, 1), Xs, fun (_) -> t_boolean() end);
+type(erlang, garbage_collect_message_area, 0, _) ->
+ t_boolean();
type(erlang, get, 0, _) -> t_list(t_tuple(2));
type(erlang, get, 1, _) -> t_any(); % | t_atom('undefined')
type(erlang, get_cookie, 0, _) -> t_atom(); % | t_atom('nocookie')
@@ -1155,6 +1112,10 @@ type(erlang, monitor_node, 2, Xs) ->
type(erlang, monitor_node, 3, Xs) ->
strict(arg_types(erlang, monitor_node, 3), Xs,
fun (_) -> t_atom('true') end);
+type(erlang, nif_error, 1, _) ->
+ t_any(); % this BIF and the next one are stubs for NIFs and never return
+type(erlang, nif_error, 2, Xs) ->
+ strict(arg_types(erlang, nif_error, 2), Xs, fun (_) -> t_any() end);
type(erlang, node, 0, _) -> t_node();
type(erlang, node, 1, Xs) ->
strict(arg_types(erlang, node, 1), Xs, fun (_) -> t_node() end);
@@ -1173,8 +1134,8 @@ type(erlang, phash2, 2, Xs) ->
strict(arg_types(erlang, phash2, 2), Xs, fun (_) -> t_non_neg_integer() end);
type(erlang, pid_to_list, 1, Xs) ->
strict(arg_types(erlang, pid_to_list, 1), Xs, fun (_) -> t_string() end);
-type(erlang, port_call, 3, Xs) ->
- strict(arg_types(erlang, port_call, 3), Xs, fun (_) -> t_any() end);
+type(erlang, port_call, Arity, Xs) when Arity =:= 2; Arity =:= 3 ->
+ strict(arg_types(erlang, port_call, Arity), Xs, fun (_) -> t_any() end);
type(erlang, port_close, 1, Xs) ->
strict(arg_types(erlang, port_close, 1), Xs,
fun (_) -> t_atom('true') end);
@@ -1503,6 +1464,7 @@ type(erlang, statistics, 1, Xs) ->
T_statistics_1
end
end);
+type(erlang, subtract, 2, Xs) -> type(erlang, '--', 2, Xs); % alias
type(erlang, suspend_process, 1, Xs) ->
strict(arg_types(erlang, suspend_process, 1), Xs,
fun (_) -> t_atom('true') end);
@@ -1570,7 +1532,6 @@ type(erlang, system_info, 1, Xs) ->
t_non_neg_integer()])]));
['allocator'] ->
t_tuple([t_sup([t_atom('undefined'),
- t_atom('elib_malloc'),
t_atom('glibc')]),
t_list(t_integer()),
t_list(t_atom()),
@@ -1591,11 +1552,10 @@ type(erlang, system_info, 1, Xs) ->
t_binary();
['dist_ctrl'] ->
t_list(t_tuple([t_atom(), t_sup([t_pid(), t_port])]));
- ['elib_malloc'] ->
- t_sup([t_atom('false'),
- t_list(t_tuple([t_atom(), t_any()]))]);
+ %% elib_malloc is intentionally not included,
+ %% because it scheduled for removal in R15.
['endian'] ->
- t_sup([t_atom('big'), t_atom('little')]);
+ t_endian();
['fullsweep_after'] ->
t_tuple([t_atom('fullsweep_after'), t_non_neg_integer()]);
['garbage_collection'] ->
@@ -1607,9 +1567,8 @@ type(erlang, system_info, 1, Xs) ->
['heap_type'] ->
t_sup([t_atom('private'), t_atom('hybrid')]);
['hipe_architecture'] ->
- t_sup([t_atom('amd64'), t_atom('arm'),
- t_atom('powerpc'), t_atom('undefined'),
- t_atom('ultrasparc'), t_atom('x86')]);
+ t_atoms(['amd64', 'arm', 'powerpc', 'ppc64',
+ 'undefined', 'ultrasparc', 'x86']);
['info'] ->
t_binary();
['internal_cpu_topology'] -> %% Undocumented internal feature
@@ -1785,11 +1744,23 @@ type(erts_debug, disassemble, 1, Xs) ->
fun (_) -> t_sup([t_atom('false'),
t_atom('undef'),
t_tuple([t_integer(), t_binary(), t_mfa()])]) end);
+type(erts_debug, display, 1, _) ->
+ t_string();
type(erts_debug, dist_ext_to_term, 2, Xs) ->
strict(arg_types(erts_debug, dist_ext_to_term, 2), Xs,
fun (_) -> t_any() end);
+type(erts_debug, dump_monitors, 1, Xs) ->
+ strict(arg_types(erts_debug, dump_monitors, 1), Xs,
+ fun(_) -> t_atom('true') end);
+type(erts_debug, dump_links, 1, Xs) ->
+ strict(arg_types(erts_debug, dump_links, 1), Xs,
+ fun(_) -> t_atom('true') end);
type(erts_debug, flat_size, 1, Xs) ->
strict(arg_types(erts_debug, flat_size, 1), Xs, fun (_) -> t_integer() end);
+type(erts_debug, get_internal_state, 1, _) ->
+ t_any();
+type(erts_debug, instructions, 0, _) ->
+ t_list(t_list(t_byte()));
type(erts_debug, lock_counters, 1, Xs) ->
strict(arg_types(erts_debug, lock_counters, 1), Xs,
fun ([Arg]) ->
@@ -1810,6 +1781,8 @@ type(erts_debug, lock_counters, 1, Xs) ->
end);
type(erts_debug, same, 2, Xs) ->
strict(arg_types(erts_debug, same, 2), Xs, fun (_) -> t_boolean() end);
+type(erts_debug, set_internal_state, 2, _) ->
+ t_any();
%%-- ets ----------------------------------------------------------------------
type(ets, all, 0, _) ->
t_list(t_tab());
@@ -1893,38 +1866,40 @@ type(ets, slot, 2, Xs) ->
strict(arg_types(ets, slot, 2), Xs,
fun (_) -> t_sup(t_list(t_tuple()), t_atom('$end_of_table')) end);
type(ets, update_counter, 3, Xs) ->
- strict(arg_types(ets, update_counter, 3), Xs, fun (_) -> t_integer() end);
+ strict(arg_types(ets, update_counter, 3), Xs,
+ fun ([_, _, Op]) ->
+ case t_is_integer(Op) of
+ true -> t_integer();
+ false ->
+ case t_is_tuple(Op) of
+ true -> t_integer();
+ false ->
+ case t_is_list(Op) of
+ true -> t_list(t_integer());
+ false ->
+ case t_is_nil(Op) of
+ true -> t_nil();
+ false -> t_sup([t_integer(), t_list(t_integer())])
+ end
+ end
+ end
+ end
+ end);
type(ets, update_element, 3, Xs) ->
strict(arg_types(ets, update_element, 3), Xs, fun (_) -> t_boolean() end);
%%-- file ---------------------------------------------------------------------
-type(file, close, 1, Xs) ->
- strict(arg_types(file, close, 1), Xs, fun (_) -> t_file_return() end);
-type(file, delete, 1, Xs) ->
- strict(arg_types(file, delete, 1), Xs, fun (_) -> t_file_return() end);
-type(file, get_cwd, 0, _) ->
- t_sup(t_tuple([t_atom('ok'), t_string()]),
- t_tuple([t_atom('error'), t_file_posix_error()]));
-type(file, make_dir, 1, Xs) ->
- strict(arg_types(file, make_dir, 1), Xs, fun (_) -> t_file_return() end);
-type(file, open, 2, Xs) ->
- strict(arg_types(file, open, 2), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_file_io_device()]),
- t_tuple([t_atom('error'), t_file_posix_error()])])
- end);
-type(file, read_file, 1, Xs) ->
- strict(arg_types(file, read_file, 1), Xs,
- fun (_) ->
- t_sup([t_tuple([t_atom('ok'), t_binary()]),
- t_tuple([t_atom('error'), t_file_posix_error()])])
- end);
-type(file, set_cwd, 1, Xs) ->
- strict(arg_types(file, set_cwd, 1), Xs,
- fun (_) -> t_sup(t_atom('ok'),
- t_tuple([t_atom('error'), t_file_posix_error()]))
- end);
-type(file, write_file, 2, Xs) ->
- strict(arg_types(file, write_file, 2), Xs, fun (_) -> t_file_return() end);
+type(file, native_name_encoding, 0, _) ->
+ t_file_encoding();
+%%-- prim_file ----------------------------------------------------------------
+type(prim_file, internal_name2native, 1, Xs) ->
+ strict(arg_types(prim_file, internal_name2native, 1), Xs,
+ fun (_) -> t_binary() end);
+type(prim_file, internal_native2name, 1, Xs) ->
+ strict(arg_types(prim_file, internal_native2name, 1), Xs,
+ fun (_) -> t_prim_file_name() end);
+type(prim_file, internal_normalize_utf8, 1, Xs) ->
+ strict(arg_types(prim_file, internal_normalize_utf8, 1), Xs,
+ fun (_) -> t_binary() end);
%%-- gen_tcp ------------------------------------------------------------------
%% NOTE: All type information for this module added to avoid loss of precision
type(gen_tcp, accept, 1, Xs) ->
@@ -2099,7 +2074,7 @@ type(hipe_bifs, set_native_address, 3, Xs) ->
strict(arg_types(hipe_bifs, set_native_address, 3), Xs,
fun (_) -> t_nil() end);
type(hipe_bifs, system_crc, 1, Xs) ->
- strict(arg_types(hipe_bifs, system_crc, 1), Xs, fun (_) -> t_integer() end);
+ strict(arg_types(hipe_bifs, system_crc, 1), Xs, fun (_) -> t_crc32() end);
type(hipe_bifs, term_to_word, 1, Xs) ->
strict(arg_types(hipe_bifs, term_to_word, 1), Xs,
fun (_) -> t_integer() end);
@@ -2258,8 +2233,7 @@ type(lists, flatten, 1, Xs) ->
t_list();
false ->
X2 = type(lists, flatten, 1, [t_inf(X1, t_list())]),
- t_sup(t_list(t_subtract(X1, t_list())),
- X2)
+ t_sup(t_list(t_subtract(X1, t_list())), X2)
end
end
end);
@@ -2270,10 +2244,20 @@ type(lists, flatmap, 2, Xs) ->
true -> t_nil();
false ->
case check_fun_application(F, [t_list_elements(List)]) of
- ok ->
- case t_is_cons(List) of
- true -> t_nonempty_list(t_list_elements(t_fun_range(F)));
- false -> t_list(t_list_elements(t_fun_range(F)))
+ ok ->
+ R = t_fun_range(F),
+ case t_is_nil(R) of
+ true -> t_nil();
+ false ->
+ Elems = t_list_elements(R),
+ case t_is_cons(List) of
+ true ->
+ case t_is_subtype(t_nil(), R) of
+ true -> t_list(Elems);
+ false -> t_nonempty_list(Elems)
+ end;
+ false -> t_list(Elems)
+ end
end;
error ->
case t_is_cons(List) of
@@ -3194,81 +3178,64 @@ arith(Op, X1, X2) ->
-spec arg_types(atom(), atom(), arity()) -> [erl_types:erl_type()] | 'unknown'.
+%%------- binary --------------------------------------------------------------
+arg_types(binary, at, 2) ->
+ [t_binary(), t_non_neg_integer()];
+arg_types(binary, bin_to_list, 1) ->
+ [t_binary()];
+arg_types(binary, bin_to_list, 2) ->
+ [t_binary(), t_binary_part()];
+arg_types(binary, bin_to_list, 3) ->
+ [t_binary(), t_integer(), t_non_neg_integer()];
+arg_types(binary, compile_pattern, 1) ->
+ [t_sup(t_binary(), t_list(t_binary()))];
+arg_types(binary, copy, 1) ->
+ [t_binary()];
+arg_types(binary, copy, 2) ->
+ [t_binary(), t_non_neg_integer()];
+arg_types(binary, decode_unsigned, 1) ->
+ [t_binary()];
+arg_types(binary, decode_unsigned, 2) ->
+ [t_binary(), t_endian()];
+arg_types(binary, encode_unsigned, 1) ->
+ [t_non_neg_integer()];
+arg_types(binary, encode_unsigned, 2) ->
+ [t_non_neg_integer(), t_endian()];
+arg_types(binary, first, 1) ->
+ [t_binary()];
+arg_types(binary, last, 1) ->
+ [t_binary()];
+arg_types(binary, list_to_bin, 1) ->
+ arg_types(erlang, list_to_binary, 1);
+arg_types(binary, longest_common_prefix, 1) ->
+ [t_list(t_binary())];
+arg_types(binary, longest_common_suffix, 1) ->
+ [t_list(t_binary())];
+arg_types(binary, match, 2) ->
+ [t_binary(), t_binary_pattern()];
+arg_types(binary, match, 3) ->
+ [t_binary(), t_binary_pattern(), t_binary_options()];
+arg_types(binary, matches, 2) ->
+ [t_binary(), t_binary_pattern()];
+arg_types(binary, matches, 3) ->
+ [t_binary(), t_binary_pattern(), t_binary_options()];
+arg_types(binary, part, 2) ->
+ arg_types(erlang, binary_part, 2);
+arg_types(binary, part, 3) ->
+ arg_types(erlang, binary_part, 3);
+arg_types(binary, referenced_byte_size, 1) ->
+ [t_binary()];
%%------- code ----------------------------------------------------------------
-arg_types(code, add_path, 1) ->
- [t_string()];
-arg_types(code, add_patha, 1) ->
- arg_types(code, add_path, 1);
-arg_types(code, add_paths, 1) ->
- [t_list(t_string())];
-arg_types(code, add_pathsa, 1) ->
- arg_types(code, add_paths, 1);
-arg_types(code, add_pathsz, 1) ->
- arg_types(code, add_paths, 1);
-arg_types(code, add_pathz, 1) ->
- arg_types(code, add_path, 1);
-arg_types(code, all_loaded, 0) ->
- [];
-arg_types(code, compiler_dir, 0) ->
- [];
-arg_types(code, del_path, 1) ->
- [t_sup(t_string(), t_atom())]; % OBS: doc differs from add_path/1 - why?
-arg_types(code, delete, 1) ->
- [t_atom()];
-arg_types(code, ensure_loaded, 1) ->
- arg_types(code, load_file, 1);
arg_types(code, get_chunk, 2) ->
[t_binary(), t_string()];
-arg_types(code, get_object_code, 1) ->
- [t_atom()];
-arg_types(code, get_path, 0) ->
- [];
-arg_types(code, is_loaded, 1) ->
- [t_atom()];
-arg_types(code, is_sticky, 1) ->
- [t_atom()];
arg_types(code, is_module_native, 1) ->
[t_atom()];
-arg_types(code, lib_dir, 0) ->
- [];
-arg_types(code, lib_dir, 1) ->
- [t_atom()];
-arg_types(code, load_abs, 1) ->
- [t_string()];
-arg_types(code, load_abs, 2) ->
- [t_code_loaded_fname_or_status(), t_atom()];
-arg_types(code, load_binary, 3) ->
- [t_atom(), t_code_loaded_fname_or_status(), t_binary()];
-arg_types(code, load_file, 1) ->
- [t_atom()];
-arg_types(code, load_native_partial, 2) ->
- [t_atom(), t_binary()];
-arg_types(code, load_native_sticky, 3) ->
- [t_atom(), t_binary(), t_sup(t_binary(), t_atom('false'))];
arg_types(code, module_md5, 1) ->
[t_binary()];
arg_types(code, make_stub_module, 3) ->
[t_atom(), t_binary(), t_tuple([t_list(), t_list()])];
-arg_types(code, priv_dir, 1) ->
- [t_atom()];
-arg_types(code, purge, 1) ->
- arg_types(code, delete, 1);
arg_types(code, rehash, 0) ->
[];
-arg_types(code, replace_path, 2) ->
- [t_atom(), t_string()];
-arg_types(code, root_dir, 0) ->
- [];
-arg_types(code, set_path, 1) ->
- [t_string()];
-arg_types(code, soft_purge, 1) ->
- arg_types(code, delete, 1);
-arg_types(code, stick_mod, 1) ->
- [t_atom()];
-arg_types(code, unstick_mod, 1) ->
- arg_types(code, stick_mod, 1);
-arg_types(code, which, 1) ->
- [t_atom()];
%%------- erl_ddll ------------------------------------------------------------
arg_types(erl_ddll, demonitor, 1) ->
arg_types(erlang, demonitor, 1);
@@ -3375,6 +3342,14 @@ arg_types(erlang, 'bnot', 1) ->
[t_integer()];
arg_types(erlang, abs, 1) ->
[t_number()];
+arg_types(erlang, adler32, 1) ->
+ [t_iodata()];
+arg_types(erlang, adler32, 2) ->
+ [t_adler32(), t_iodata()];
+arg_types(erlang, adler32_combine, 3) ->
+ [t_adler32(), t_adler32(), t_non_neg_integer()];
+arg_types(erlang, append, 2) ->
+ arg_types(erlang, '++', 2);
arg_types(erlang, append_element, 2) ->
[t_tuple(), t_any()];
arg_types(erlang, apply, 2) ->
@@ -3388,6 +3363,10 @@ arg_types(erlang, atom_to_binary, 2) ->
[t_atom(), t_encoding_a2b()];
arg_types(erlang, atom_to_list, 1) ->
[t_atom()];
+arg_types(erlang, binary_part, 2) ->
+ [t_binary(), t_tuple([t_non_neg_integer(), t_integer()])];
+arg_types(erlang, binary_part, 3) ->
+ [t_binary(), t_non_neg_integer(), t_integer()];
arg_types(erlang, binary_to_atom, 2) ->
[t_binary(), t_encoding_a2b()];
arg_types(erlang, binary_to_existing_atom, 2) ->
@@ -3423,9 +3402,9 @@ arg_types(erlang, concat_binary, 1) ->
arg_types(erlang, crc32, 1) ->
[t_iodata()];
arg_types(erlang, crc32, 2) ->
- [t_integer(), t_iodata()];
+ [t_crc32(), t_iodata()];
arg_types(erlang, crc32_combine, 3) ->
- [t_integer(), t_integer(), t_integer()];
+ [t_crc32(), t_crc32(), t_non_neg_integer()];
arg_types(erlang, date, 0) ->
[];
arg_types(erlang, decode_packet, 3) ->
@@ -3440,6 +3419,10 @@ arg_types(erlang, disconnect_node, 1) ->
[t_node()];
arg_types(erlang, display, 1) ->
[t_any()];
+arg_types(erlang, display_nl, 0) ->
+ [];
+arg_types(erlang, display_string, 1) ->
+ [t_string()];
arg_types(erlang, dist_exit, 3) ->
[t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())];
arg_types(erlang, element, 2) ->
@@ -3476,6 +3459,8 @@ arg_types(erlang, garbage_collect, 0) ->
[];
arg_types(erlang, garbage_collect, 1) ->
[t_pid()];
+arg_types(erlang, garbage_collect_message_area, 0) ->
+ [];
arg_types(erlang, get, 0) ->
[];
arg_types(erlang, get, 1) ->
@@ -3628,6 +3613,10 @@ arg_types(erlang, monitor_node, 2) ->
[t_node(), t_boolean()];
arg_types(erlang, monitor_node, 3) ->
[t_node(), t_boolean(), t_list(t_atom('allow_passive_connect'))];
+arg_types(erlang, nif_error, 1) ->
+ [t_any()];
+arg_types(erlang, nif_error, 2) ->
+ [t_any(), t_list()];
arg_types(erlang, node, 0) ->
[];
arg_types(erlang, node, 1) ->
@@ -3640,9 +3629,10 @@ arg_types(erlang, nodes, 1) ->
arg_types(erlang, now, 0) ->
[];
arg_types(erlang, open_port, 2) ->
+ ArgT = t_sup(t_unicode_string(), t_binary()),
[t_sup(t_atom(), t_sup([t_tuple([t_atom('spawn'), t_string()]),
t_tuple([t_atom('spawn_driver'), t_string()]),
- t_tuple([t_atom('spawn_executable'), t_string()]),
+ t_tuple([t_atom('spawn_executable'), ArgT]),
t_tuple([t_atom('fd'), t_integer(), t_integer()])])),
t_list(t_sup(t_sup([t_atom('stream'),
t_atom('exit_status'),
@@ -3658,8 +3648,8 @@ arg_types(erlang, open_port, 2) ->
t_tuple([t_atom('line'), t_integer()]),
t_tuple([t_atom('cd'), t_string()]),
t_tuple([t_atom('env'), t_list(t_tuple(2))]), % XXX: More
- t_tuple([t_atom('args'), t_list(t_string())]),
- t_tuple([t_atom('arg0'), t_string()])])))];
+ t_tuple([t_atom('args'), t_list(ArgT)]),
+ t_tuple([t_atom('arg0'), ArgT])])))];
arg_types(erlang, phash, 2) ->
[t_any(), t_pos_integer()];
arg_types(erlang, phash2, 1) ->
@@ -3668,6 +3658,8 @@ arg_types(erlang, phash2, 2) ->
[t_any(), t_pos_integer()];
arg_types(erlang, pid_to_list, 1) ->
[t_pid()];
+arg_types(erlang, port_call, 2) ->
+ [t_sup(t_port(), t_atom()), t_any()];
arg_types(erlang, port_call, 3) ->
[t_sup(t_port(), t_atom()), t_integer(), t_any()];
arg_types(erlang, port_close, 1) ->
@@ -3795,6 +3787,8 @@ arg_types(erlang, statistics, 1) ->
t_atom('run_queue'),
t_atom('runtime'),
t_atom('wall_clock')])];
+arg_types(erlang, subtract, 2) ->
+ arg_types(erlang, '--', 2);
arg_types(erlang, suspend_process, 1) ->
[t_pid()];
arg_types(erlang, suspend_process, 2) ->
@@ -3874,7 +3868,8 @@ arg_types(erlang, trace_info, 2) ->
t_atom('flags'), t_atom('tracer'),
%% while the following are items about a func
t_atom('traced'), t_atom('match_spec'), t_atom('meta'),
- t_atom('meta_match_spec'), t_atom('call_count'), t_atom('all')])];
+ t_atom('meta_match_spec'), t_atom('call_count'),
+ t_atom('call_time'), t_atom('all')])];
arg_types(erlang, trace_pattern, 2) ->
[t_sup(t_tuple([t_atom(), t_atom(), t_sup(t_arity(), t_atom('_'))]),
t_atom('on_load')),
@@ -3883,7 +3878,7 @@ arg_types(erlang, trace_pattern, 3) ->
arg_types(erlang, trace_pattern, 2) ++
[t_list(t_sup([t_atom('global'), t_atom('local'),
t_atom('meta'), t_tuple([t_atom('meta'), t_pid()]),
- t_atom('call_count')]))];
+ t_atom('call_count'), t_atom('call_time')]))];
arg_types(erlang, trunc, 1) ->
[t_number()];
arg_types(erlang, tuple_size, 1) ->
@@ -3917,10 +3912,20 @@ arg_types(erts_debug, breakpoint, 2) ->
[t_tuple([t_atom(), t_atom(), t_sup(t_integer(), t_atom('_'))]), t_boolean()];
arg_types(erts_debug, disassemble, 1) ->
[t_sup(t_mfa(), t_integer())];
+arg_types(erts_debug, display, 1) ->
+ [t_any()];
arg_types(erts_debug, dist_ext_to_term, 2) ->
[t_tuple(), t_binary()];
+arg_types(erts_debug, dump_monitors, 1) ->
+ [t_sup([t_pid(),t_atom()])];
+arg_types(erts_debug, dump_links, 1) ->
+ [t_sup([t_pid(),t_atom(),t_port()])];
arg_types(erts_debug, flat_size, 1) ->
[t_any()];
+arg_types(erts_debug, get_internal_state, 1) ->
+ [t_any()];
+arg_types(erts_debug, instructions, 0) ->
+ [];
arg_types(erts_debug, lock_counters, 1) ->
[t_sup([t_atom(enabled),
t_atom(info),
@@ -3929,6 +3934,8 @@ arg_types(erts_debug, lock_counters, 1) ->
t_tuple([t_atom(process_locks), t_boolean()])])];
arg_types(erts_debug, same, 2) ->
[t_any(), t_any()];
+arg_types(erts_debug, set_internal_state, 2) ->
+ [t_any(), t_any()];
%%------- ets -----------------------------------------------------------------
arg_types(ets, all, 0) ->
[];
@@ -4011,32 +4018,22 @@ arg_types(ets, setopts, 2) ->
t_tuple([t_atom('heir'), t_atom('none')])),
[t_tab(), t_sup(Opt, t_list(Opt))];
arg_types(ets, update_counter, 3) ->
- [t_tab(), t_any(), t_sup(t_integer(),
- t_sup(t_tuple([t_integer(), t_integer()]),
- t_tuple([t_integer(), t_integer(),
- t_integer(), t_integer()])))];
+ Int = t_integer(),
+ UpdateOp = t_sup(t_tuple([Int, Int]), t_tuple([Int, Int, Int, Int])),
+ [t_tab(), t_any(), t_sup([UpdateOp, t_list(UpdateOp), Int])];
arg_types(ets, update_element, 3) ->
PosValue = t_tuple([t_integer(), t_any()]),
[t_tab(), t_any(), t_sup(PosValue, t_list(PosValue))];
%%------- file ----------------------------------------------------------------
-arg_types(file, close, 1) ->
- [t_file_io_device()];
-arg_types(file, delete, 1) ->
- [t_file_name()];
-arg_types(file, get_cwd, 0) ->
+arg_types(file, native_name_encoding, 0) ->
[];
-arg_types(file, make_dir, 1) ->
- [t_file_name()];
-arg_types(file, open, 2) ->
- [t_file_name(), t_list(t_file_open_option())];
-arg_types(file, read_file, 1) ->
- [t_file_name()];
-arg_types(file, set_cwd, 1) ->
- [t_file_name()];
-arg_types(file, write, 2) ->
- [t_file_io_device(), t_iodata()];
-arg_types(file, write_file, 2) ->
- [t_file_name(), t_sup(t_binary(), t_list())];
+%%-- prim_file ----------------------------------------------------------------
+arg_types(prim_file, internal_name2native, 1) ->
+ [t_prim_file_name()];
+arg_types(prim_file, internal_native2name, 1) ->
+ [t_binary()];
+arg_types(prim_file, internal_normalize_utf8, 1) ->
+ [t_binary()];
%%------- gen_tcp -------------------------------------------------------------
arg_types(gen_tcp, accept, 1) ->
[t_socket()];
@@ -4109,7 +4106,7 @@ arg_types(hipe_bifs, call_count_off, 1) ->
arg_types(hipe_bifs, call_count_on, 1) ->
[t_mfa()];
arg_types(hipe_bifs, check_crc, 1) ->
- [t_integer()];
+ [t_crc32()];
arg_types(hipe_bifs, enter_code, 2) ->
[t_binary(), t_sup(t_nil(), t_tuple())];
arg_types(hipe_bifs, enter_sdesc, 1) ->
@@ -4153,7 +4150,7 @@ arg_types(hipe_bifs, set_funinfo_native_address, 3) ->
arg_types(hipe_bifs, set_native_address, 3) ->
[t_mfa(), t_integer(), t_boolean()];
arg_types(hipe_bifs, system_crc, 1) ->
- [t_integer()];
+ [t_crc32()];
arg_types(hipe_bifs, term_to_word, 1) ->
[t_any()];
arg_types(hipe_bifs, update_code_size, 3) ->
@@ -4355,11 +4352,11 @@ arg_types(os, timestamp, 0) ->
arg_types(re, compile, 1) ->
[t_iodata()];
arg_types(re, compile, 2) ->
- [t_iodata(), t_list(t_re_compile_option())];
+ [t_sup(t_iodata(), t_charlist()), t_list(t_re_compile_option())];
arg_types(re, run, 2) ->
- [t_iodata(), t_re_RE()];
+ [t_sup(t_iodata(), t_charlist()), t_re_RE()];
arg_types(re, run, 3) ->
- [t_iodata(), t_re_RE(), t_list(t_re_run_option())];
+ [t_sup(t_iodata(), t_charlist()), t_re_RE(), t_list(t_re_run_option())];
%%------- string --------------------------------------------------------------
arg_types(string, chars, 2) ->
[t_char(), t_non_neg_integer()];
@@ -4467,6 +4464,30 @@ t_httppacket() ->
t_sup([t_HttpRequest(), t_HttpResponse(),
t_HttpHeader(), t_atom('http_eoh'), t_HttpError()]).
+t_endian() ->
+ t_sup(t_atom('big'), t_atom('little')).
+
+%% =====================================================================
+%% Types for the binary module
+%% =====================================================================
+
+t_binary_part() ->
+ t_tuple([t_non_neg_integer(), t_integer()]).
+
+t_binary_canonical_part() ->
+ t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
+
+t_binary_pattern() ->
+ t_sup([t_binary(),
+ t_list(t_binary()),
+ t_binary_compiled_pattern()]).
+
+t_binary_compiled_pattern() ->
+ t_tuple([t_atom('cp'), t_binary()]).
+
+t_binary_options() ->
+ t_list(t_tuple([t_atom('scope'), t_binary_part()])).
+
%% =====================================================================
%% HTTP types documented in R12B-4
%% =====================================================================
@@ -4475,16 +4496,16 @@ t_HttpRequest() ->
t_tuple([t_atom('http_request'), t_HttpMethod(), t_HttpUri(), t_HttpVersion()]).
t_HttpResponse() ->
- t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_string()]).
+ t_tuple([t_atom('http_response'), t_HttpVersion(), t_integer(), t_HttpString()]).
t_HttpHeader() ->
- t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_string()]).
+ t_tuple([t_atom('http_header'), t_integer(), t_HttpField(), t_any(), t_HttpString()]).
t_HttpError() ->
- t_tuple([t_atom('http_error'), t_string()]).
+ t_tuple([t_atom('http_error'), t_HttpString()]).
t_HttpMethod() ->
- t_sup(t_HttpMethodAtom(), t_string()).
+ t_sup(t_HttpMethodAtom(), t_HttpString()).
t_HttpMethodAtom() ->
t_atoms(['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE']).
@@ -4493,18 +4514,18 @@ t_HttpUri() ->
t_sup([t_atom('*'),
t_tuple([t_atom('absoluteURI'),
t_sup(t_atom('http'), t_atom('https')),
- t_string(),
+ t_HttpString(),
t_sup(t_non_neg_integer(), t_atom('undefined')),
- t_string()]),
- t_tuple([t_atom('scheme'), t_string(), t_string()]),
- t_tuple([t_atom('abs_path'), t_string()]),
- t_string()]).
+ t_HttpString()]),
+ t_tuple([t_atom('scheme'), t_HttpString(), t_HttpString()]),
+ t_tuple([t_atom('abs_path'), t_HttpString()]),
+ t_HttpString()]).
t_HttpVersion() ->
t_tuple([t_non_neg_integer(), t_non_neg_integer()]).
t_HttpField() ->
- t_sup(t_HttpFieldAtom(), t_string()).
+ t_sup(t_HttpFieldAtom(), t_HttpString()).
t_HttpFieldAtom() ->
t_atoms(['Cache-Control', 'Connection', 'Date', 'Pragma', 'Transfer-Encoding',
@@ -4521,6 +4542,9 @@ t_HttpFieldAtom() ->
'Set-Cookie', 'Set-Cookie2', 'X-Forwarded-For', 'Cookie',
'Keep-Alive', 'Proxy-Connection']).
+t_HttpString() ->
+ t_sup(t_string(),t_binary()).
+
%% =====================================================================
%% These are used for the built-in functions of 'code'
%% =====================================================================
@@ -4549,6 +4573,12 @@ t_code_loaded_fname_or_status() ->
%% These are used for the built-in functions of 'erlang'
%% =====================================================================
+t_adler32() ->
+ t_non_neg_integer().
+
+t_crc32() ->
+ t_non_neg_integer().
+
t_decode_packet_option() ->
t_sup([t_tuple([t_atom('packet_size'), t_non_neg_integer()]),
t_tuple([t_atom('line_length'), t_non_neg_integer()])]).
@@ -4606,7 +4636,8 @@ t_process_priority_level() ->
t_sup([t_atom('max'), t_atom('high'), t_atom('normal'), t_atom('low')]).
t_process_status() ->
- t_sup([t_atom('runnable'), t_atom('running'),
+ t_sup([t_atom('exiting'), t_atom('garbage_collecting'),
+ t_atom('runnable'), t_atom('running'),
t_atom('suspended'), t_atom('waiting')]).
t_raise_errorclass() ->
@@ -4745,10 +4776,11 @@ t_matchres() ->
%% From the 'ets' documentation
%%-----------------------------
%% Option = Type | Access | named_table | {keypos,Pos}
-%% | {heir,pid(),HeirData} | {heir,none}
-%% | {write_concurrency,boolean()}
+%% | {heir,pid(),HeirData} | {heir,none} | Tweaks
%% Type = set | ordered_set | bag | duplicate_bag
%% Access = public | protected | private
+%% Tweaks = {write_concurrency,boolean()}
+%% | {read_concurrency,boolean()} | compressed
%% Pos = integer()
%% HeirData = term()
t_ets_new_options() ->
@@ -4760,10 +4792,12 @@ t_ets_new_options() ->
t_atom('protected'),
t_atom('private'),
t_atom('named_table'),
+ t_tuple([t_atom('keypos'), t_integer()]),
t_tuple([t_atom('heir'), t_pid(), t_any()]),
t_tuple([t_atom('heir'), t_atom('none')]),
- t_tuple([t_atom('keypos'), t_integer()]),
- t_tuple([t_atom('write_concurrency'), t_boolean()])])).
+ t_tuple([t_atom('write_concurrency'), t_boolean()]),
+ t_tuple([t_atom('read_concurrency'), t_boolean()]),
+ t_atom('compressed')])).
t_ets_info_items() ->
t_sup([t_atom('fixed'),
@@ -4779,68 +4813,11 @@ t_ets_info_items() ->
t_atom('type')]).
%% =====================================================================
-%% These are used for the built-in functions of 'file'
+%% These are used for the built-in functions of 'prim_file'
%% =====================================================================
-t_file_io_device() ->
- t_sup(t_pid(), t_tuple([t_atom('file_descriptor'), t_atom(), t_any()])).
-
-t_file_name() ->
- t_sup([t_atom(),
- t_string(),
- %% DeepList = [char() | atom() | DeepList] -- approximation below
- t_list(t_sup([t_atom(), t_string(), t_list()]))]).
-
-t_file_open_option() ->
- t_sup([t_atom('read'),
- t_atom('write'),
- t_atom('append'),
- t_atom('raw'),
- t_atom('binary'),
- t_atom('delayed_write'),
- t_atom('read_ahead'),
- t_atom('compressed'),
- t_tuple([t_atom('delayed_write'),
- t_pos_integer(), t_non_neg_integer()]),
- t_tuple([t_atom('read_ahead'), t_pos_integer()])]).
-
-%% This lists all Posix errors that can occur in file:*/* functions
-t_file_posix_error() ->
- t_sup([t_atom('eacces'),
- t_atom('eagain'),
- t_atom('ebadf'),
- t_atom('ebusy'),
- t_atom('edquot'),
- t_atom('eexist'),
- t_atom('efault'),
- t_atom('efbig'),
- t_atom('eintr'),
- t_atom('einval'),
- t_atom('eio'),
- t_atom('eisdir'),
- t_atom('eloop'),
- t_atom('emfile'),
- t_atom('emlink'),
- t_atom('enametoolong'),
- t_atom('enfile'),
- t_atom('enodev'),
- t_atom('enoent'),
- t_atom('enomem'),
- t_atom('enospc'),
- t_atom('enotblk'),
- t_atom('enotdir'),
- t_atom('enotsup'),
- t_atom('enxio'),
- t_atom('eperm'),
- t_atom('epipe'),
- t_atom('erofs'),
- t_atom('espipe'),
- t_atom('esrch'),
- t_atom('estale'),
- t_atom('exdev')]).
-
-t_file_return() ->
- t_sup(t_atom('ok'), t_tuple([t_atom('error'), t_file_posix_error()])).
+t_prim_file_name() ->
+ t_sup(t_unicode_string(), t_binary()).
%% =====================================================================
%% These are used for the built-in functions of 'gen_tcp'
@@ -4997,13 +4974,14 @@ t_re_MP() -> %% it's supposed to be an opaque data type
t_tuple([t_atom('re_pattern'), t_integer(), t_integer(), t_binary()]).
t_re_RE() ->
- t_sup(t_re_MP(), t_iodata()).
+ t_sup([t_re_MP(), t_iodata(), t_charlist()]).
t_re_compile_option() ->
- t_sup([t_atoms(['anchored', 'caseless', 'dollar_endonly', 'dotall',
- 'extended', 'firstline', 'multiline', 'no_auto_capture',
- 'dupnames', 'ungreedy']),
- t_tuple([t_atom('newline'), t_re_NLSpec()])]).
+ t_sup([t_atoms(['unicode', 'anchored', 'caseless', 'dollar_endonly',
+ 'dotall', 'extended', 'firstline', 'multiline',
+ 'no_auto_capture', 'dupnames', 'ungreedy']),
+ t_tuple([t_atom('newline'), t_re_NLSpec()]),
+ t_atoms(['bsr_anycrlf', 'bsr_unicode'])]).
t_re_run_option() ->
t_sup([t_atoms(['anchored', 'global', 'notbol', 'noteol', 'notempty']),
@@ -5020,7 +4998,7 @@ t_re_Type() ->
t_atoms(['index', 'list', 'binary']).
t_re_NLSpec() ->
- t_atoms(['cr', 'crlf', 'lf', 'anycrlf']).
+ t_atoms(['cr', 'crlf', 'lf', 'anycrlf', 'any']).
t_re_ValueSpec() ->
t_sup(t_atoms(['all', 'all_but_first', 'first', 'none']), t_re_ValueList()).
@@ -5042,7 +5020,12 @@ t_ML() -> % a binary or a possibly deep list of integers or binaries
t_sup(t_list(t_sup([t_integer(), t_binary(), t_list()])), t_binary()).
t_encoding() ->
- t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']).
+ t_sup([t_atoms(['latin1', 'unicode', 'utf8', 'utf16', 'utf32']),
+ t_tuple([t_atom('utf16'), t_endian()]),
+ t_tuple([t_atom('utf32'), t_endian()])]).
+
+t_file_encoding() ->
+ t_atoms(['latin1', 'utf8']).
t_encoding_a2b() -> % for the 2nd arg of atom_to_binary/2 and binary_to_atom/2
t_atoms(['latin1', 'unicode', 'utf8']).
diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl
index f3b91b3953..080d6936b2 100644
--- a/lib/hipe/cerl/erl_types.erl
+++ b/lib/hipe/cerl/erl_types.erl
@@ -29,7 +29,7 @@
%% In late 2008, Manouk Manoukian and Kostis Sagonas added support for
%% opaque types to the structure-based representation of types.
%% During February and March 2009, Kostis Sagonas significantly
-%% cleaned up the type representation added spec declarations.
+%% cleaned up the type representation and added spec declarations.
%%
%% ======================================================================
@@ -62,6 +62,7 @@
t_boolean/0,
t_byte/0,
t_char/0,
+ t_charlist/0,
t_collect_vars/1,
t_cons/0,
t_cons/2,
@@ -178,7 +179,7 @@
t_remote/3,
t_string/0,
t_struct_from_opaque/2,
- t_solve_remote/2,
+ t_solve_remote/3,
t_subst/2,
t_subtract/2,
t_subtract_list/2,
@@ -195,6 +196,7 @@
t_tuple_size/1,
t_tuple_sizes/1,
t_tuple_subtypes/1,
+ t_unicode_string/0,
t_unify/2,
t_unify/3,
t_unit/0,
@@ -205,11 +207,14 @@
t_var_name/1,
%% t_assign_variables_to_subtype/2,
type_is_defined/3,
+ record_field_diffs_to_string/2,
subst_all_vars_to_any/1,
- lift_list_to_pos_empty/1
+ lift_list_to_pos_empty/1,
+ is_erl_type/1
]).
%%-define(DO_ERL_TYPES_TEST, true).
+-compile({no_auto_import,[min/2,max/2]}).
-ifdef(DO_ERL_TYPES_TEST).
-export([test/0]).
@@ -221,6 +226,8 @@
-export([t_is_identifier/1]).
-endif.
+-export_type([erl_type/0]).
+
%%=============================================================================
%%
%% Definition of the type structure
@@ -299,7 +306,7 @@
%% Auxiliary types and convenient macros
%%
--type parse_form() :: {atom(), _, _} | {atom(), _, _, _}. %% XXX: Temporarily
+-type parse_form() :: {atom(), _, _} | {atom(), _, _, _} | {'op', _, _, _, _}. %% XXX: Temporarily
-type rng_elem() :: 'pos_inf' | 'neg_inf' | integer().
-record(int_set, {set :: [integer()]}).
@@ -398,7 +405,8 @@ t_is_none(_) -> false.
-spec t_opaque(module(), atom(), [_], erl_type()) -> erl_type().
t_opaque(Mod, Name, Args, Struct) ->
- ?opaque(set_singleton(#opaque{mod=Mod, name=Name, args=Args, struct=Struct})).
+ O = #opaque{mod = Mod, name = Name, args = Args, struct = Struct},
+ ?opaque(set_singleton(O)).
-spec t_is_opaque(erl_type()) -> boolean().
@@ -427,7 +435,7 @@ t_opaque_structure(?opaque(Elements)) ->
t_opaque_module(?opaque(Elements)) ->
case ordsets:size(Elements) of
1 ->
- [#opaque{mod=Module}] = ordsets:to_list(Elements),
+ [#opaque{mod = Module}] = ordsets:to_list(Elements),
Module;
_ -> throw({error, "Unexpected multiple opaque types"})
end.
@@ -631,7 +639,7 @@ t_unopaque_on_mismatch(GenType, Type, Opaques) ->
case t_inf(GenType, Type) of
?none ->
Unopaqued = t_unopaque(Type, Opaques),
- %% Unions might be a problem, must investigate.
+ %% XXX: Unions might be a problem, must investigate.
case t_inf(GenType, Unopaqued) of
?none -> Type;
_ -> Unopaqued
@@ -643,12 +651,12 @@ t_unopaque_on_mismatch(GenType, Type, Opaques) ->
module_builtin_opaques(Module) ->
[O || O <- all_opaque_builtins(), t_opaque_module(O) =:= Module].
-
+
%%-----------------------------------------------------------------------------
-%% Remote types
-%% These types are used for preprocessing they should never reach the analysis stage
+%% Remote types: these types are used for preprocessing;
+%% they should never reach the analysis stage.
--spec t_remote(module(), atom(), [_]) -> erl_type().
+-spec t_remote(atom(), atom(), [erl_type()]) -> erl_type().
t_remote(Mod, Name, Args) ->
?remote(set_singleton(#remote{mod = Mod, name = Name, args = Args})).
@@ -658,126 +666,135 @@ t_remote(Mod, Name, Args) ->
t_is_remote(?remote(_)) -> true;
t_is_remote(_) -> false.
--spec t_solve_remote(erl_type(), dict()) -> erl_type().
+-spec t_solve_remote(erl_type(), set(), dict()) -> erl_type().
-t_solve_remote(Type , Records) ->
- {RT, _RR} = t_solve_remote(Type, Records, []),
+t_solve_remote(Type, ExpTypes, Records) ->
+ {RT, _RR} = t_solve_remote(Type, ExpTypes, Records, []),
RT.
-t_solve_remote(?function(Domain, Range), R, C) ->
- {RT1, RR1} = t_solve_remote(Domain, R, C),
- {RT2, RR2} = t_solve_remote(Range, R, C),
+t_solve_remote(?function(Domain, Range), ET, R, C) ->
+ {RT1, RR1} = t_solve_remote(Domain, ET, R, C),
+ {RT2, RR2} = t_solve_remote(Range, ET, R, C),
{?function(RT1, RT2), RR1 ++ RR2};
-t_solve_remote(?list(Types, Term, Size), R, C) ->
- {RT, RR} = t_solve_remote(Types, R, C),
+t_solve_remote(?list(Types, Term, Size), ET, R, C) ->
+ {RT, RR} = t_solve_remote(Types, ET, R, C),
{?list(RT, Term, Size), RR};
-t_solve_remote(?product(Types), R, C) ->
- {RL, RR} = list_solve_remote(Types, R, C),
+t_solve_remote(?product(Types), ET, R, C) ->
+ {RL, RR} = list_solve_remote(Types, ET, R, C),
{?product(RL), RR};
-t_solve_remote(?opaque(Set), R, C) ->
+t_solve_remote(?opaque(Set), ET, R, C) ->
List = ordsets:to_list(Set),
- {NewList, RR} = opaques_solve_remote(List, R, C),
+ {NewList, RR} = opaques_solve_remote(List, ET, R, C),
{?opaque(ordsets:from_list(NewList)), RR};
-t_solve_remote(?tuple(?any, _, _) = T, _R, _C) -> {T, []};
-t_solve_remote(?tuple(Types, Arity, Tag), R, C) ->
- {RL, RR} = list_solve_remote(Types, R, C),
+t_solve_remote(?tuple(?any, _, _) = T, _ET, _R, _C) -> {T, []};
+t_solve_remote(?tuple(Types, Arity, Tag), ET, R, C) ->
+ {RL, RR} = list_solve_remote(Types, ET, R, C),
{?tuple(RL, Arity, Tag), RR};
-t_solve_remote(?tuple_set(Set), R, C) ->
- {NewSet, RR} = tuples_solve_remote(Set, R, C),
+t_solve_remote(?tuple_set(Set), ET, R, C) ->
+ {NewSet, RR} = tuples_solve_remote(Set, ET, R, C),
{?tuple_set(NewSet), RR};
-t_solve_remote(?remote(Set), R, C) ->
+t_solve_remote(?remote(Set), ET, R, C) ->
RemoteList = ordsets:to_list(Set),
- {RL, RR} = list_solve_remote_type(RemoteList, R, C),
+ {RL, RR} = list_solve_remote_type(RemoteList, ET, R, C),
{t_sup(RL), RR};
-t_solve_remote(?union(List), R, C) ->
- {RL, RR} = list_solve_remote(List, R, C),
+t_solve_remote(?union(List), ET, R, C) ->
+ {RL, RR} = list_solve_remote(List, ET, R, C),
{t_sup(RL), RR};
-t_solve_remote(T, _R, _C) -> {T, []}.
+t_solve_remote(T, _ET, _R, _C) -> {T, []}.
t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType,
- R, C) ->
+ ET, R, C) ->
+ ArgsLen = length(Args),
case dict:find(RemMod, R) of
error ->
- Msg = io_lib:format("Cannot locate module ~w to "
- "resolve the remote type: ~w:~w()~n",
- [RemMod, RemMod, Name]),
- throw({error, Msg});
+ self() ! {self(), ext_types, {RemMod, Name, ArgsLen}},
+ {t_any(), []};
{ok, RemDict} ->
- case lookup_type(Name, RemDict) of
- {type, {_Mod, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
- {NewType, NewCycle, NewRR} =
- case unfold(RemType, C) of
- true ->
- List = lists:zip(ArgNames, Args),
- TmpVarDict = dict:from_list(List),
- {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []};
- false -> {t_any(), C, [RemType]}
- end,
- {RT, RR} = t_solve_remote(NewType, R, NewCycle),
- RetRR = NewRR ++ RR,
- RT1 =
- case lists:member(RemType, RetRR) of
- true -> t_limit(RT, ?REC_TYPE_LIMIT);
- false -> RT
- end,
- {RT1, RetRR};
- {opaque, {Mod, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
- List = lists:zip(ArgNames, Args),
- TmpVarDict = dict:from_list(List),
- {Rep, NewCycle, NewRR} =
- case unfold(RemType, C) of
- true -> {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []};
- false -> {t_any(), C, [RemType]}
- end,
- {NewRep, RR} = t_solve_remote(Rep, R, NewCycle),
- RetRR = NewRR ++ RR,
- RT1 =
- case lists:member(RemType, RetRR) of
- true -> t_limit(NewRep, ?REC_TYPE_LIMIT);
- false -> NewRep
- end,
- {t_from_form({opaque, -1, Name, {Mod, Args, RT1}},
- RemDict, TmpVarDict),
- RetRR};
- {type, _} ->
- Msg = io_lib:format("Unknown remote type ~w\n", [Name]),
- throw({error, Msg});
- {opaque, _} ->
- Msg = io_lib:format("Unknown remote opaque type ~w\n", [Name]),
- throw({error, Msg});
- error ->
- Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
- [RemMod, Name]),
- throw({error, Msg})
+ MFA = {RemMod, Name, ArgsLen},
+ case sets:is_element(MFA, ET) of
+ true ->
+ case lookup_type(Name, RemDict) of
+ {type, {_Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) ->
+ {NewType, NewCycle, NewRR} =
+ case can_unfold_more(RemType, C) of
+ true ->
+ List = lists:zip(ArgNames, Args),
+ TmpVarDict = dict:from_list(List),
+ {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []};
+ false ->
+ {t_any(), C, [RemType]}
+ end,
+ {RT, RR} = t_solve_remote(NewType, ET, R, NewCycle),
+ RetRR = NewRR ++ RR,
+ RT1 =
+ case lists:member(RemType, RetRR) of
+ true -> t_limit(RT, ?REC_TYPE_LIMIT);
+ false -> RT
+ end,
+ {RT1, RetRR};
+ {opaque, {Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) ->
+ List = lists:zip(ArgNames, Args),
+ TmpVarDict = dict:from_list(List),
+ {Rep, NewCycle, NewRR} =
+ case can_unfold_more(RemType, C) of
+ true ->
+ {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []};
+ false ->
+ {t_any(), C, [RemType]}
+ end,
+ {NewRep, RR} = t_solve_remote(Rep, ET, R, NewCycle),
+ RetRR = NewRR ++ RR,
+ RT1 =
+ case lists:member(RemType, RetRR) of
+ true -> t_limit(NewRep, ?REC_TYPE_LIMIT);
+ false -> NewRep
+ end,
+ {t_from_form({opaque, -1, Name, {Mod, Args, RT1}},
+ RemDict, TmpVarDict),
+ RetRR};
+ {type, _} ->
+ Msg = io_lib:format("Unknown remote type ~w\n", [Name]),
+ throw({error, Msg});
+ {opaque, _} ->
+ Msg = io_lib:format("Unknown remote opaque type ~w\n", [Name]),
+ throw({error, Msg});
+ error ->
+ Msg = io_lib:format("Unable to find remote type ~w:~w()\n",
+ [RemMod, Name]),
+ throw({error, Msg})
+ end;
+ false ->
+ self() ! {self(), ext_types, {RemMod, Name, ArgsLen}},
+ {t_any(), []}
end
end.
-list_solve_remote([], _R, _C) ->
+list_solve_remote([], _ET, _R, _C) ->
{[], []};
-list_solve_remote([Type|Types], R, C) ->
- {RT, RR1} = t_solve_remote(Type, R, C),
- {RL, RR2} = list_solve_remote(Types, R, C),
+list_solve_remote([Type|Types], ET, R, C) ->
+ {RT, RR1} = t_solve_remote(Type, ET, R, C),
+ {RL, RR2} = list_solve_remote(Types, ET, R, C),
{[RT|RL], RR1 ++ RR2}.
-list_solve_remote_type([], _R, _C) ->
+list_solve_remote_type([], _ET, _R, _C) ->
{[], []};
-list_solve_remote_type([Type|Types], R, C) ->
- {RT, RR1} = t_solve_remote_type(Type, R, C),
- {RL, RR2} = list_solve_remote_type(Types, R, C),
+list_solve_remote_type([Type|Types], ET, R, C) ->
+ {RT, RR1} = t_solve_remote_type(Type, ET, R, C),
+ {RL, RR2} = list_solve_remote_type(Types, ET, R, C),
{[RT|RL], RR1 ++ RR2}.
-opaques_solve_remote([], _R, _C) ->
+opaques_solve_remote([], _ET, _R, _C) ->
{[], []};
-opaques_solve_remote([#opaque{struct = Struct} = Remote|Tail], R, C) ->
- {RT, RR1} = t_solve_remote(Struct, R, C),
- {LOp, RR2} = opaques_solve_remote(Tail, R, C),
+opaques_solve_remote([#opaque{struct = Struct} = Remote|Tail], ET, R, C) ->
+ {RT, RR1} = t_solve_remote(Struct, ET, R, C),
+ {LOp, RR2} = opaques_solve_remote(Tail, ET, R, C),
{[Remote#opaque{struct = RT}|LOp], RR1 ++ RR2}.
-tuples_solve_remote([], _R, _C) ->
+tuples_solve_remote([], _ET, _R, _C) ->
{[], []};
-tuples_solve_remote([{Sz, Tuples}|Tail], R, C) ->
- {RL, RR1} = list_solve_remote(Tuples, R, C),
- {LSzTpls, RR2} = tuples_solve_remote(Tail, R, C),
+tuples_solve_remote([{Sz, Tuples}|Tail], ET, R, C) ->
+ {RL, RR1} = list_solve_remote(Tuples, ET, R, C),
+ {LSzTpls, RR2} = tuples_solve_remote(Tail, ET, R, C),
{[{Sz, RL}|LSzTpls], RR1 ++ RR2}.
%%-----------------------------------------------------------------------------
@@ -801,7 +818,7 @@ t_is_none_or_unit(?unit) -> true;
t_is_none_or_unit(_) -> false.
%%-----------------------------------------------------------------------------
-%% Atoms and the derived type bool
+%% Atoms and the derived type boolean
%%
-spec t_atom() -> erl_type().
@@ -1440,6 +1457,26 @@ t_is_tuple(_) -> false.
%% Non-primitive types, including some handy syntactic sugar types
%%
+-spec t_unicode_string() -> erl_type().
+
+t_unicode_string() ->
+ t_list(t_unicode_char()).
+
+-spec t_charlist() -> erl_type().
+
+t_charlist() ->
+ t_charlist(1).
+
+-spec t_charlist(non_neg_integer()) -> erl_type().
+
+t_charlist(N) when N > 0 ->
+ t_maybe_improper_list(t_sup([t_unicode_char(),
+ t_unicode_binary(),
+ t_charlist(N-1)]),
+ t_sup(t_unicode_binary(), t_nil()));
+t_charlist(0) ->
+ t_maybe_improper_list(t_any(), t_sup(t_unicode_binary(), t_nil())).
+
-spec t_constant() -> erl_type().
t_constant() ->
@@ -1534,6 +1571,16 @@ t_parameterized_module() ->
t_timeout() ->
t_sup(t_non_neg_integer(), t_atom('infinity')).
+-spec t_unicode_binary() -> erl_type().
+
+t_unicode_binary() ->
+ t_binary(). % with characters encoded in UTF-8 coding standard
+
+-spec t_unicode_char() -> erl_type().
+
+t_unicode_char() ->
+ t_integer(). % representing a valid unicode codepoint
+
%%-----------------------------------------------------------------------------
%% Some built-in opaque types
%%
@@ -1596,7 +1643,7 @@ t_set() ->
t_tid() ->
t_opaque(ets, tid, [], t_integer()).
--spec all_opaque_builtins() -> [erl_type()].
+-spec all_opaque_builtins() -> [erl_type(),...].
all_opaque_builtins() ->
[t_array(), t_dict(), t_digraph(), t_gb_set(),
@@ -2112,7 +2159,8 @@ t_elements(?identifier(IDs)) ->
t_elements(?list(_, _, _) = T) -> [T];
t_elements(?number(_, _) = T) ->
case T of
- ?number(?any, ?unknown_qual) -> [T];
+ ?number(?any, ?unknown_qual) ->
+ [?float, ?integer(?any)];
?float -> [T];
?integer(?any) -> [T];
?int_range(_, _) -> [T];
@@ -2159,10 +2207,10 @@ t_inf(?var(_), T, _Mode) -> subst_all_vars_to_any(T);
t_inf(T, ?var(_), _Mode) -> subst_all_vars_to_any(T);
t_inf(?any, T, _Mode) -> subst_all_vars_to_any(T);
t_inf(T, ?any, _Mode) -> subst_all_vars_to_any(T);
-t_inf(?unit, _, _Mode) -> ?unit;
-t_inf(_, ?unit, _Mode) -> ?unit;
t_inf(?none, _, _Mode) -> ?none;
t_inf(_, ?none, _Mode) -> ?none;
+t_inf(?unit, _, _Mode) -> ?unit; % ?unit cases should appear below ?none
+t_inf(_, ?unit, _Mode) -> ?unit;
t_inf(T, T, _Mode) -> subst_all_vars_to_any(T);
t_inf(?atom(Set1), ?atom(Set2), _) ->
case set_intersection(Set1, Set2) of
@@ -2371,14 +2419,16 @@ inf_tuple_sets(L1, L2, Mode) ->
List -> ?tuple_set(List)
end.
-inf_tuple_sets([{Arity, Tuples1}|Left1], [{Arity, Tuples2}|Left2], Acc, Mode) ->
+inf_tuple_sets([{Arity, Tuples1}|Ts1], [{Arity, Tuples2}|Ts2], Acc, Mode) ->
case inf_tuples_in_sets(Tuples1, Tuples2, Mode) of
- [] -> inf_tuple_sets(Left1, Left2, Acc, Mode);
- NewTuples -> inf_tuple_sets(Left1, Left2, [{Arity, NewTuples}|Acc], Mode)
+ [] -> inf_tuple_sets(Ts1, Ts2, Acc, Mode);
+ [?tuple_set([{Arity, NewTuples}])] ->
+ inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Mode);
+ NewTuples -> inf_tuple_sets(Ts1, Ts2, [{Arity, NewTuples}|Acc], Mode)
end;
-inf_tuple_sets(L1 = [{Arity1, _}|Left1], L2 = [{Arity2, _}|Left2], Acc, Mode) ->
- if Arity1 < Arity2 -> inf_tuple_sets(Left1, L2, Acc, Mode);
- Arity1 > Arity2 -> inf_tuple_sets(L1, Left2, Acc, Mode)
+inf_tuple_sets([{Arity1, _}|Ts1] = L1, [{Arity2, _}|Ts2] = L2, Acc, Mode) ->
+ if Arity1 < Arity2 -> inf_tuple_sets(Ts1, L2, Acc, Mode);
+ Arity1 > Arity2 -> inf_tuple_sets(L1, Ts2, Acc, Mode)
end;
inf_tuple_sets([], _, Acc, _Mode) -> lists:reverse(Acc);
inf_tuple_sets(_, [], Acc, _Mode) -> lists:reverse(Acc).
@@ -2394,17 +2444,17 @@ inf_tuples_in_sets(L1, [?tuple(Elements2, _, ?any)], Mode) ->
inf_tuples_in_sets(L1, L2, Mode) ->
inf_tuples_in_sets(L1, L2, [], Mode).
-inf_tuples_in_sets([?tuple(Elements1, Arity, Tag)|Left1],
- [?tuple(Elements2, Arity, Tag)|Left2], Acc, Mode) ->
+inf_tuples_in_sets([?tuple(Elements1, Arity, Tag)|Ts1],
+ [?tuple(Elements2, Arity, Tag)|Ts2], Acc, Mode) ->
case t_inf_lists_strict(Elements1, Elements2, Mode) of
- bottom -> inf_tuples_in_sets(Left1, Left2, Acc, Mode);
- NewElements ->
- inf_tuples_in_sets(Left1, Left2, [?tuple(NewElements, Arity, Tag)|Acc], Mode)
+ bottom -> inf_tuples_in_sets(Ts1, Ts2, Acc, Mode);
+ NewElements ->
+ inf_tuples_in_sets(Ts1, Ts2, [?tuple(NewElements, Arity, Tag)|Acc], Mode)
end;
-inf_tuples_in_sets([?tuple(_, _, Tag1)|Left1] = L1,
- [?tuple(_, _, Tag2)|Left2] = L2, Acc, Mode) ->
- if Tag1 < Tag2 -> inf_tuples_in_sets(Left1, L2, Acc, Mode);
- Tag1 > Tag2 -> inf_tuples_in_sets(L1, Left2, Acc, Mode)
+inf_tuples_in_sets([?tuple(_, _, Tag1)|Ts1] = L1,
+ [?tuple(_, _, Tag2)|Ts2] = L2, Acc, Mode) ->
+ if Tag1 < Tag2 -> inf_tuples_in_sets(Ts1, L2, Acc, Mode);
+ Tag1 > Tag2 -> inf_tuples_in_sets(L1, Ts2, Acc, Mode)
end;
inf_tuples_in_sets([], _, Acc, _Mode) -> lists:reverse(Acc);
inf_tuples_in_sets(_, [], Acc, _Mode) -> lists:reverse(Acc).
@@ -2523,12 +2573,14 @@ t_subst(T, _Dict, _Fun) ->
%% Unification
%%
--spec t_unify(erl_type(), erl_type()) -> {erl_type(), [{_, erl_type()}]}.
+-type t_unify_ret() :: {erl_type(), [{_, erl_type()}]}.
+
+-spec t_unify(erl_type(), erl_type()) -> t_unify_ret().
t_unify(T1, T2) ->
t_unify(T1, T2, []).
--spec t_unify(erl_type(), erl_type(), [erl_type()]) -> {erl_type(), [{_, erl_type()}]}.
+-spec t_unify(erl_type(), erl_type(), [erl_type()]) -> t_unify_ret().
t_unify(T1, T2, Opaques) ->
{T, Dict} = t_unify(T1, T2, dict:new(), Opaques),
@@ -2541,7 +2593,7 @@ t_unify(?var(Id1) = T, ?var(Id2), Dict, Opaques) ->
error ->
case dict:find(Id2, Dict) of
error -> {T, dict:store(Id2, T, Dict)};
- {ok, Type} -> {Type, t_unify(T, Type, Dict, Opaques)}
+ {ok, Type} -> t_unify(T, Type, Dict, Opaques)
end;
{ok, Type1} ->
case dict:find(Id2, Dict) of
@@ -2749,7 +2801,9 @@ t_subtract_list(T, []) ->
-spec t_subtract(erl_type(), erl_type()) -> erl_type().
t_subtract(_, ?any) -> ?none;
+t_subtract(_, ?var(_)) -> ?none;
t_subtract(?any, _) -> ?any;
+t_subtract(?var(_) = T, _) -> T;
t_subtract(T, ?unit) -> T;
t_subtract(?unit, _) -> ?unit;
t_subtract(?none, _) -> ?none;
@@ -2777,13 +2831,13 @@ t_subtract(?opaque(Set1), ?opaque(Set2)) ->
Set -> ?opaque(Set)
end;
t_subtract(?matchstate(Pres1, Slots1), ?matchstate(Pres2, _Slots2)) ->
- Pres = t_subtract(Pres1,Pres2),
+ Pres = t_subtract(Pres1, Pres2),
case t_is_none(Pres) of
true -> ?none;
- false -> ?matchstate(Pres,Slots1)
+ false -> ?matchstate(Pres, Slots1)
end;
-t_subtract(?matchstate(Present,Slots),_) ->
- ?matchstate(Present,Slots);
+t_subtract(?matchstate(Present, Slots), _) ->
+ ?matchstate(Present, Slots);
t_subtract(?nil, ?nil) ->
?none;
t_subtract(?nil, ?nonempty_list(_, _)) ->
@@ -2803,7 +2857,7 @@ t_subtract(?list(Contents1, Termination1, Size1) = T,
true ->
case {Size1, Size2} of
{?nonempty_qual, ?unknown_qual} -> ?none;
- {?unknown_qual, ?nonempty_qual} -> Termination1;
+ {?unknown_qual, ?nonempty_qual} -> ?nil;
{S, S} -> ?none
end;
false ->
@@ -2905,7 +2959,7 @@ t_subtract(T, ?product(_)) ->
T;
t_subtract(?union(U1), ?union(U2)) ->
subtract_union(U1, U2);
-t_subtract(T1, T2) ->
+t_subtract(T1, T2) ->
?union(U1) = force_union(T1),
?union(U2) = force_union(T2),
subtract_union(U1, U2).
@@ -3298,28 +3352,44 @@ record_to_string(Tag, [_|Fields], FieldNames, RecDict) ->
FieldStrings = record_fields_to_string(Fields, FieldNames, RecDict, []),
"#" ++ atom_to_list(Tag) ++ "{" ++ sequence(FieldStrings, [], ",") ++ "}".
-record_fields_to_string([Field|Left1], [{FieldName, DeclaredType}|Left2],
- RecDict, Acc) ->
- PrintType =
- case t_is_equal(Field, DeclaredType) of
- true -> false;
+record_fields_to_string([F|Fs], [{FName, _DefType}|FDefs], RecDict, Acc) ->
+ NewAcc =
+ case t_is_any(F) orelse t_is_atom('undefined', F) of
+ true -> Acc;
false ->
- case t_is_any(DeclaredType) andalso t_is_atom(undefined, Field) of
- true -> false;
- false ->
- TmpType = t_subtract(DeclaredType, t_atom(undefined)),
- not t_is_equal(Field, TmpType)
- end
+ StrFV = atom_to_list(FName) ++ "::" ++ t_to_string(F, RecDict),
+ %% ActualDefType = t_subtract(DefType, t_atom('undefined')),
+ %% Str = case t_is_any(ActualDefType) of
+ %% true -> StrFV;
+ %% false -> StrFV ++ "::" ++ t_to_string(ActualDefType, RecDict)
+ %% end,
+ [StrFV|Acc]
end,
- case PrintType of
- false -> record_fields_to_string(Left1, Left2, RecDict, Acc);
- true ->
- String = atom_to_list(FieldName) ++ "::" ++ t_to_string(Field, RecDict),
- record_fields_to_string(Left1, Left2, RecDict, [String|Acc])
- end;
+ record_fields_to_string(Fs, FDefs, RecDict, NewAcc);
record_fields_to_string([], [], _RecDict, Acc) ->
lists:reverse(Acc).
+-spec record_field_diffs_to_string(erl_type(), dict()) -> string().
+
+record_field_diffs_to_string(?tuple([_|Fs], Arity, Tag), RecDict) ->
+ [TagAtom] = t_atom_vals(Tag),
+ {ok, FieldNames} = lookup_record(TagAtom, Arity-1, RecDict),
+ %% io:format("RecCElems = ~p\nRecTypes = ~p\n", [Fs, FieldNames]),
+ FieldDiffs = field_diffs(Fs, FieldNames, RecDict, []),
+ sequence(FieldDiffs, [], " and ").
+
+field_diffs([F|Fs], [{FName, DefType}|FDefs], RecDict, Acc) ->
+ NewAcc =
+ case t_is_subtype(F, DefType) of
+ true -> Acc;
+ false ->
+ Str = atom_to_list(FName) ++ "::" ++ t_to_string(DefType, RecDict),
+ [Str|Acc]
+ end,
+ field_diffs(Fs, FDefs, RecDict, NewAcc);
+field_diffs([], [], _, Acc) ->
+ lists:reverse(Acc).
+
comma_sequence(Types, RecDict) ->
List = [case T =:= ?any of
true -> "_";
@@ -3338,8 +3408,8 @@ sequence([], [], _Delimiter) ->
[];
sequence([T], Acc, _Delimiter) ->
lists:flatten(lists:reverse([T|Acc]));
-sequence([T|Left], Acc, Delimiter) ->
- sequence(Left, [T ++ Delimiter|Acc], Delimiter).
+sequence([T|Ts], Acc, Delimiter) ->
+ sequence(Ts, [T ++ Delimiter|Acc], Delimiter).
%%=============================================================================
%%
@@ -3360,188 +3430,263 @@ t_from_form(Form, RecDict) ->
-spec t_from_form(parse_form(), dict(), dict()) -> erl_type().
t_from_form(Form, RecDict, VarDict) ->
- {T, _R} = t_from_form(Form, [], RecDict, VarDict),
+ {T, _R} = t_from_form(Form, [], false, RecDict, VarDict),
T.
-type type_names() :: [{'type' | 'opaque' | 'record', atom()}].
--spec t_from_form(parse_form(), type_names(), dict(), dict()) ->
+-spec t_from_form(parse_form(), type_names(), boolean(), dict(), dict()) ->
{erl_type(), type_names()}.
-t_from_form({var, _L, '_'}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({var, _L, '_'}, _TypeNames, _InOpaque, _RecDict, _VarDict) ->
{t_any(), []};
-t_from_form({var, _L, Name}, _TypeNames, _RecDict, VarDict) ->
+t_from_form({var, _L, Name}, _TypeNames, _InOpaque, _RecDict, VarDict) ->
case dict:find(Name, VarDict) of
error -> {t_var(Name), []};
{ok, Val} -> {Val, []}
end;
-t_from_form({ann_type, _L, [_Var, Type]}, TypeNames, RecDict, VarDict) ->
- t_from_form(Type, TypeNames, RecDict, VarDict);
-t_from_form({paren_type, _L, [Type]}, TypeNames, RecDict, VarDict) ->
- t_from_form(Type, TypeNames, RecDict, VarDict);
+t_from_form({ann_type, _L, [_Var, Type]}, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict);
+t_from_form({paren_type, _L, [Type]}, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict);
t_from_form({remote_type, _L, [{atom, _, Module}, {atom, _, Type}, Args]},
- TypeNames, RecDict, VarDict) ->
- {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict),
+ TypeNames, InOpaque, RecDict, VarDict) ->
+ {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict),
{t_remote(Module, Type, L), R};
-t_from_form({atom, _L, Atom}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({atom, _L, Atom}, _TypeNames, _InOpaque, _RecDict, _VarDict) ->
{t_atom(Atom), []};
-t_from_form({integer, _L, Int}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({integer, _L, Int}, _TypeNames, _InOpaque, _RecDict, _VarDict) ->
{t_integer(Int), []};
-t_from_form({type, _L, any, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({op, _L, _Op, _Arg} = Op, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, Val} ->
+ {t_integer(Val), []};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])})
+ end;
+t_from_form({op, _L, _Op, _Arg1, _Arg2} = Op, _TypeNames, _InOpaque,
+ _RecDict, _VarDict) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, Val} ->
+ {t_integer(Val), []};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Op])})
+ end;
+t_from_form({type, _L, any, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_any(), []};
-t_from_form({type, _L, arity, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, arity, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_arity(), []};
-t_from_form({type, _L, array, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, array, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_array(), []};
-t_from_form({type, _L, atom, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, atom, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_atom(), []};
-t_from_form({type, _L, binary, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, binary, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_binary(), []};
-t_from_form({type, _L, binary, [{integer, _, Base}, {integer, _, Unit}]},
- _TypeNames, _RecDict, _VarDict) ->
- {t_bitstr(Unit, Base), []};
-t_from_form({type, _L, bitstring, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, binary, [Base, Unit]} = Type,
+ _TypeNames, _InOpaque, _RecDict, _VarDict) ->
+ case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
+ {{integer, _, BaseVal},
+ {integer, _, UnitVal}}
+ when BaseVal >= 0, UnitVal >= 0 ->
+ {t_bitstr(UnitVal, BaseVal), []};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])})
+ end;
+t_from_form({type, _L, bitstring, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_bitstr(), []};
-t_from_form({type, _L, bool, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, bool, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_boolean(), []}; % XXX: Temporarily
-t_from_form({type, _L, boolean, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, boolean, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_boolean(), []};
-t_from_form({type, _L, byte, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, byte, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_byte(), []};
-t_from_form({type, _L, char, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, char, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_char(), []};
-t_from_form({type, _L, dict, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, dict, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_dict(), []};
-t_from_form({type, _L, digraph, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, digraph, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_digraph(), []};
-t_from_form({type, _L, float, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, float, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_float(), []};
-t_from_form({type, _L, function, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, function, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_fun(), []};
-t_from_form({type, _L, 'fun', []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, 'fun', []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_fun(), []};
t_from_form({type, _L, 'fun', [{type, _, any, []}, Range]}, TypeNames,
- RecDict, VarDict) ->
- {T, R} = t_from_form(Range, TypeNames, RecDict, VarDict),
+ InOpaque, RecDict, VarDict) ->
+ {T, R} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict),
{t_fun(T), R};
t_from_form({type, _L, 'fun', [{type, _, product, Domain}, Range]},
- TypeNames, RecDict, VarDict) ->
- {L, R1} = list_from_form(Domain, TypeNames, RecDict, VarDict),
- {T, R2} = t_from_form(Range, TypeNames, RecDict, VarDict),
+ TypeNames, InOpaque, RecDict, VarDict) ->
+ {L, R1} = list_from_form(Domain, TypeNames, InOpaque, RecDict, VarDict),
+ {T, R2} = t_from_form(Range, TypeNames, InOpaque, RecDict, VarDict),
{t_fun(L, T), R1 ++ R2};
-t_from_form({type, _L, gb_set, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, gb_set, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_gb_set(), []};
-t_from_form({type, _L, gb_tree, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, gb_tree, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_gb_tree(), []};
-t_from_form({type, _L, identifier, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, identifier, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_identifier(), []};
-t_from_form({type, _L, integer, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, integer, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_integer(), []};
-t_from_form({type, _L, iodata, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, iodata, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_iodata(), []};
-t_from_form({type, _L, iolist, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, iolist, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_iolist(), []};
-t_from_form({type, _L, list, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, list, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_list(), []};
-t_from_form({type, _L, list, [Type]}, TypeNames, RecDict, VarDict) ->
- {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, list, [Type]}, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ {T, R} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict),
{t_list(T), R};
-t_from_form({type, _L, mfa, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, mfa, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_mfa(), []};
-t_from_form({type, _L, module, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, module, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_module(), []};
-t_from_form({type, _L, nil, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, nil, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_nil(), []};
-t_from_form({type, _L, neg_integer, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, neg_integer, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_neg_integer(), []};
-t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, non_neg_integer, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_non_neg_integer(), []};
-t_from_form({type, _L, no_return, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, no_return, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_unit(), []};
-t_from_form({type, _L, node, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, node, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_node(), []};
-t_from_form({type, _L, none, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, none, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_none(), []};
-t_from_form({type, _L, nonempty_list, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, nonempty_list, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_nonempty_list(), []};
-t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, RecDict, VarDict) ->
- {T, R} = t_from_form(Type, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, nonempty_list, [Type]}, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ {T, R} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict),
{t_nonempty_list(T), R};
t_from_form({type, _L, nonempty_improper_list, [Cont, Term]}, TypeNames,
- RecDict, VarDict) ->
- {T1, R1} = t_from_form(Cont, TypeNames, RecDict, VarDict),
- {T2, R2} = t_from_form(Term, TypeNames, RecDict, VarDict),
+ InOpaque, RecDict, VarDict) ->
+ {T1, R1} = t_from_form(Cont, TypeNames, InOpaque, RecDict, VarDict),
+ {T2, R2} = t_from_form(Term, TypeNames, InOpaque, RecDict, VarDict),
{t_cons(T1, T2), R1 ++ R2};
t_from_form({type, _L, nonempty_maybe_improper_list, []}, _TypeNames,
- _RecDict, _VarDict) ->
+ _InOpaque, _RecDict, _VarDict) ->
{t_cons(?any, ?any), []};
-t_from_form({type, _L, nonempty_maybe_improper_list, [Cont, Term]}, TypeNames,
- RecDict, VarDict) ->
- {T1, R1} = t_from_form(Cont, TypeNames, RecDict, VarDict),
- {T2, R2} = t_from_form(Term, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, nonempty_maybe_improper_list, [Cont, Term]},
+ TypeNames, InOpaque, RecDict, VarDict) ->
+ {T1, R1} = t_from_form(Cont, TypeNames, InOpaque, RecDict, VarDict),
+ {T2, R2} = t_from_form(Term, TypeNames, InOpaque, RecDict, VarDict),
{t_cons(T1, T2), R1 ++ R2};
-t_from_form({type, _L, nonempty_string, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, nonempty_string, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_nonempty_string(), []};
-t_from_form({type, _L, number, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, number, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_number(), []};
-t_from_form({type, _L, pid, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, pid, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_pid(), []};
-t_from_form({type, _L, port, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, port, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_port(), []};
-t_from_form({type, _L, pos_integer, []}, _TypeNames, _RecDict, _VarDict) ->
- {t_pos_integer(), []};
-t_from_form({type, _L, maybe_improper_list, []}, _TypeNames, _RecDict,
+t_from_form({type, _L, pos_integer, []}, _TypeNames, _InOpaque, _RecDict,
_VarDict) ->
+ {t_pos_integer(), []};
+t_from_form({type, _L, maybe_improper_list, []}, _TypeNames, _InOpaque,
+ _RecDict, _VarDict) ->
{t_maybe_improper_list(), []};
-t_from_form({type, _L, maybe_improper_list, [Content, Termination]}, TypeNames,
- RecDict, VarDict) ->
- {T1, R1} = t_from_form(Content, TypeNames, RecDict, VarDict),
- {T2, R2} = t_from_form(Termination, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, maybe_improper_list, [Content, Termination]},
+ TypeNames, InOpaque, RecDict, VarDict) ->
+ {T1, R1} = t_from_form(Content, TypeNames, InOpaque, RecDict, VarDict),
+ {T2, R2} = t_from_form(Termination, TypeNames, InOpaque, RecDict, VarDict),
{t_maybe_improper_list(T1, T2), R1 ++ R2};
-t_from_form({type, _L, product, Elements}, TypeNames, RecDict, VarDict) ->
- {L, R} = list_from_form(Elements, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, product, Elements}, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ {L, R} = list_from_form(Elements, TypeNames, InOpaque, RecDict, VarDict),
{t_product(L), R};
-t_from_form({type, _L, queue, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, queue, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_queue(), []};
-t_from_form({type, _L, range, [{integer, _, From}, {integer, _, To}]},
- _TypeNames, _RecDict, _VarDict) ->
- {t_from_range(From, To), []};
-t_from_form({type, _L, record, [Name|Fields]}, TypeNames, RecDict, VarDict) ->
- record_from_form(Name, Fields, TypeNames, RecDict, VarDict);
-t_from_form({type, _L, reference, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, range, [From, To]} = Type,
+ _TypeNames, _InOpaque, _RecDict, _VarDict) ->
+ case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
+ {{integer, _, FromVal}, {integer, _, ToVal}} ->
+ {t_from_range(FromVal, ToVal), []};
+ _ -> throw({error, io_lib:format("Unable to evaluate type ~w\n", [Type])})
+ end;
+t_from_form({type, _L, record, [Name|Fields]}, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ record_from_form(Name, Fields, TypeNames, InOpaque, RecDict, VarDict);
+t_from_form({type, _L, reference, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_reference(), []};
-t_from_form({type, _L, set, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, set, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_set(), []};
-t_from_form({type, _L, string, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, string, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_string(), []};
-t_from_form({type, _L, term, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, term, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_any(), []};
-t_from_form({type, _L, tid, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, tid, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_tid(), []};
-t_from_form({type, _L, timeout, []}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, timeout, []}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_timeout(), []};
-t_from_form({type, _L, tuple, any}, _TypeNames, _RecDict, _VarDict) ->
+t_from_form({type, _L, tuple, any}, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{t_tuple(), []};
-t_from_form({type, _L, tuple, Args}, TypeNames, RecDict, VarDict) ->
- {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, tuple, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
+ {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict),
{t_tuple(L), R};
-t_from_form({type, _L, union, Args}, TypeNames, RecDict, VarDict) ->
- {L, R} = list_from_form(Args, TypeNames, RecDict, VarDict),
+t_from_form({type, _L, union, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
+ {L, R} = list_from_form(Args, TypeNames, InOpaque, RecDict, VarDict),
{t_sup(L), R};
-t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) ->
+t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) ->
case lookup_type(Name, RecDict) of
{type, {_Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
- case unfold({type, Name}, TypeNames) of
+ case can_unfold_more({type, Name}, TypeNames) of
true ->
List = lists:zipwith(
fun(ArgName, ArgType) ->
{Ttemp, _R} = t_from_form(ArgType, TypeNames,
- RecDict, VarDict),
+ InOpaque, RecDict,
+ VarDict),
{ArgName, Ttemp}
end,
ArgNames, Args),
TmpVarDict = dict:from_list(List),
- {T, R} = t_from_form(Type, [{type, Name}|TypeNames], RecDict,
- TmpVarDict),
+ {T, R} = t_from_form(Type, [{type, Name}|TypeNames], InOpaque,
+ RecDict, TmpVarDict),
case lists:member({type, Name}, R) of
true -> {t_limit(T, ?REC_TYPE_LIMIT), R};
false -> {T, R}
@@ -3550,26 +3695,32 @@ t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) ->
end;
{opaque, {Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) ->
{Rep, Rret} =
- case unfold({opaque, Name}, TypeNames) of
+ case can_unfold_more({opaque, Name}, TypeNames) of
true ->
List = lists:zipwith(
fun(ArgName, ArgType) ->
- {Ttemp, _R} = t_from_form(ArgType, TypeNames,
- RecDict, VarDict),
+ {Ttemp, _R} = t_from_form(ArgType, TypeNames,
+ InOpaque, RecDict,
+ VarDict),
{ArgName, Ttemp}
end,
ArgNames, Args),
TmpVarDict = dict:from_list(List),
- {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames], RecDict,
- TmpVarDict),
+ {T, R} = t_from_form(Type, [{opaque, Name}|TypeNames], true,
+ RecDict, TmpVarDict),
case lists:member({opaque, Name}, R) of
true -> {t_limit(T, ?REC_TYPE_LIMIT), R};
false -> {T, R}
end;
false -> {t_any(), [{opaque, Name}]}
end,
- Tret = t_from_form({opaque, -1, Name, {Module, Args, Rep}},
- RecDict, VarDict),
+ Tret =
+ case InOpaque of
+ true -> Rep;
+ false ->
+ t_from_form({opaque, -1, Name, {Module, Args, Rep}},
+ RecDict, VarDict)
+ end,
{Tret, Rret};
{type, _} ->
throw({error, io_lib:format("Unknown type ~w\n", [Name])});
@@ -3578,15 +3729,16 @@ t_from_form({type, _L, Name, Args}, TypeNames, RecDict, VarDict) ->
error ->
throw({error, io_lib:format("Unable to find type ~w\n", [Name])})
end;
-t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _RecDict,
- _VarDict) ->
+t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _InOpaque,
+ _RecDict, _VarDict) ->
case Args of
[] -> {t_opaque(Mod, Name, Args, Rep), []};
_ -> throw({error, "Polymorphic opaque types not supported yet"})
end.
-record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) ->
- case unfold({record, Name}, TypeNames) of
+record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ case can_unfold_more({record, Name}, TypeNames) of
true ->
case lookup_record(Name, RecDict) of
{ok, DeclFields} ->
@@ -3596,14 +3748,15 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) ->
{DeclFields1, R1} =
case lists:all(fun(Elem) -> Elem end, AreTyped) of
true -> {DeclFields, []};
- false -> fields_from_form(DeclFields, TypeNames1,
+ false -> fields_from_form(DeclFields, TypeNames1, InOpaque,
RecDict, dict:new())
end,
{GetModRec, R2} = get_mod_record(ModFields, DeclFields1,
- TypeNames1, RecDict, VarDict),
+ TypeNames1, InOpaque,
+ RecDict, VarDict),
case GetModRec of
{error, FieldName} ->
- throw({error, io_lib:format("Illegal declaration of ~w#{~w}\n",
+ throw({error, io_lib:format("Illegal declaration of #~w{~w}\n",
[Name, FieldName])});
{ok, NewFields} ->
{t_tuple(
@@ -3611,17 +3764,18 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, RecDict, VarDict) ->
R1 ++ R2}
end;
error ->
- throw({error, erlang:error(io_lib:format("Unknown record #~w{}\n",
- [Name]))})
+ throw({error, io_lib:format("Unknown record #~w{}\n", [Name])})
end;
false -> {t_any(), []}
end.
-get_mod_record([], DeclFields, _TypeNames, _RecDict, _VarDict) ->
+get_mod_record([], DeclFields, _TypeNames, _InOpaque, _RecDict,
+ _VarDict) ->
{{ok, DeclFields}, []};
-get_mod_record(ModFields, DeclFields, TypeNames, RecDict, VarDict) ->
+get_mod_record(ModFields, DeclFields, TypeNames, InOpaque, RecDict,
+ VarDict) ->
DeclFieldsDict = orddict:from_list(DeclFields),
- {ModFieldsDict, R} = build_field_dict(ModFields, TypeNames,
+ {ModFieldsDict, R} = build_field_dict(ModFields, TypeNames, InOpaque,
RecDict, VarDict),
case get_mod_record(DeclFieldsDict, ModFieldsDict, []) of
{error, _FieldName} = Error -> {Error, R};
@@ -3631,21 +3785,23 @@ get_mod_record(ModFields, DeclFields, TypeNames, RecDict, VarDict) ->
R}
end.
-build_field_dict(FieldTypes, TypeNames, RecDict, VarDict) ->
- build_field_dict(FieldTypes, TypeNames, RecDict, VarDict, []).
+build_field_dict(FieldTypes, TypeNames, InOpaque, RecDict, VarDict) ->
+ build_field_dict(FieldTypes, TypeNames, InOpaque, RecDict, VarDict, []).
build_field_dict([{type, _, field_type, [{atom, _, Name}, Type]}|Left],
- TypeNames, RecDict, VarDict, Acc) ->
- {T, R1} = t_from_form(Type, TypeNames, RecDict, VarDict),
+ TypeNames, InOpaque, RecDict, VarDict, Acc) ->
+ {T, R1} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict),
NewAcc = [{Name, T}|Acc],
- {D, R2} = build_field_dict(Left, TypeNames, RecDict, VarDict, NewAcc),
+ {D, R2} = build_field_dict(Left, TypeNames, InOpaque, RecDict, VarDict,
+ NewAcc),
{D, R1 ++ R2};
-build_field_dict([], _TypeNames, _RecDict, _VarDict, Acc) ->
+build_field_dict([], _TypeNames, _InOpaque, _RecDict, _VarDict, Acc) ->
{orddict:from_list(Acc), []}.
get_mod_record([{FieldName, DeclType}|Left1],
[{FieldName, ModType}|Left2], Acc) ->
- case t_is_var(ModType) orelse t_is_subtype(ModType, DeclType) of
+ case t_is_var(ModType) orelse t_is_remote(ModType) orelse
+ t_is_subtype(ModType, DeclType) of
false -> {error, FieldName};
true -> get_mod_record(Left1, Left2, [{FieldName, ModType}|Acc])
end;
@@ -3658,18 +3814,19 @@ get_mod_record(DeclFields, [], Acc) ->
get_mod_record(_, [{FieldName2, _ModType}|_], _Acc) ->
{error, FieldName2}.
-fields_from_form([], _TypeNames, _RecDict, _VarDict) ->
+fields_from_form([], _TypeNames, _InOpaque, _RecDict, _VarDict) ->
{[], []};
-fields_from_form([{Name, Type}|Tail], TypeNames, RecDict, VarDict) ->
- {T, R1} = t_from_form(Type, TypeNames, RecDict, VarDict),
- {F, R2} = fields_from_form(Tail, TypeNames, RecDict, VarDict),
+fields_from_form([{Name, Type}|Tail], TypeNames, InOpaque, RecDict,
+ VarDict) ->
+ {T, R1} = t_from_form(Type, TypeNames, InOpaque, RecDict, VarDict),
+ {F, R2} = fields_from_form(Tail, TypeNames, InOpaque, RecDict, VarDict),
{[{Name, T}|F], R1 ++ R2}.
-list_from_form([], _TypeNames, _RecDict, _VarDict) ->
+list_from_form([], _TypeNames, _InOpaque, _RecDict, _VarDict) ->
{[], []};
-list_from_form([H|Tail], TypeNames, RecDict, VarDict) ->
- {T, R1} = t_from_form(H, TypeNames, RecDict, VarDict),
- {L, R2} = list_from_form(Tail, TypeNames, RecDict, VarDict),
+list_from_form([H|Tail], TypeNames, InOpaque, RecDict, VarDict) ->
+ {T, R1} = t_from_form(H, TypeNames, InOpaque, RecDict, VarDict),
+ {L, R2} = list_from_form(Tail, TypeNames, InOpaque, RecDict, VarDict),
{[T|L], R1 ++ R2}.
-spec t_form_to_string(parse_form()) -> string().
@@ -3679,6 +3836,16 @@ t_form_to_string({var, _L, Name}) -> atom_to_list(Name);
t_form_to_string({atom, _L, Atom}) ->
io_lib:write_string(atom_to_list(Atom), $'); % To quote or not to quote... '
t_form_to_string({integer, _L, Int}) -> integer_to_list(Int);
+t_form_to_string({op, _L, _Op, _Arg} = Op) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, _} = Int -> t_form_to_string(Int);
+ _ -> io_lib:format("Bad formed type ~w",[Op])
+ end;
+t_form_to_string({op, _L, _Op, _Arg1, _Arg2} = Op) ->
+ case erl_eval:partial_eval(Op) of
+ {integer, _, _} = Int -> t_form_to_string(Int);
+ _ -> io_lib:format("Bad formed type ~w",[Op])
+ end;
t_form_to_string({ann_type, _L, [Var, Type]}) ->
t_form_to_string(Var) ++ "::" ++ t_form_to_string(Type);
t_form_to_string({paren_type, _L, [Type]}) ->
@@ -3705,8 +3872,12 @@ t_form_to_string({type, _L, nonempty_list, [Type]}) ->
t_form_to_string({type, _L, nonempty_string, []}) -> "nonempty_string()";
t_form_to_string({type, _L, product, Elements}) ->
"<" ++ sequence(t_form_to_string_list(Elements), ",") ++ ">";
-t_form_to_string({type, _L, range, [{integer, _, From}, {integer, _, To}]}) ->
- io_lib:format("~w..~w", [From, To]);
+t_form_to_string({type, _L, range, [From, To]} = Type) ->
+ case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
+ {{integer, _, FromVal}, {integer, _, ToVal}} ->
+ io_lib:format("~w..~w", [FromVal, ToVal]);
+ _ -> io_lib:format("Bad formed type ~w",[Type])
+ end;
t_form_to_string({type, _L, record, [{atom, _, Name}]}) ->
io_lib:format("#~w{}", [Name]);
t_form_to_string({type, _L, record, [{atom, _, Name}|Fields]}) ->
@@ -3725,13 +3896,17 @@ t_form_to_string({type, _L, Name, []} = T) ->
try t_to_string(t_from_form(T))
catch throw:{error, _} -> atom_to_list(Name) ++ "()"
end;
-t_form_to_string({type, _L, binary, [{integer, _, X}, {integer, _, Y}]}) ->
- case Y of
- 0 ->
- case X of
- 0 -> "<<>>";
- _ -> io_lib:format("<<_:~w>>", [X])
- end
+t_form_to_string({type, _L, binary, [X,Y]} = Type) ->
+ case {erl_eval:partial_eval(X), erl_eval:partial_eval(Y)} of
+ {{integer, _, XVal}, {integer, _, YVal}} ->
+ case YVal of
+ 0 ->
+ case XVal of
+ 0 -> "<<>>";
+ _ -> io_lib:format("<<_:~w>>", [XVal])
+ end
+ end;
+ _ -> io_lib:format("Bad formed type ~w",[Type])
end;
t_form_to_string({type, _L, Name, List}) ->
io_lib:format("~w(~s)", [Name, sequence(t_form_to_string_list(List), ",")]).
@@ -3763,6 +3938,8 @@ any_none_or_unit([?unit|_]) -> true;
any_none_or_unit([_|Left]) -> any_none_or_unit(Left);
any_none_or_unit([]) -> false.
+-spec is_erl_type(any()) -> boolean().
+
is_erl_type(?any) -> true;
is_erl_type(?none) -> true;
is_erl_type(?unit) -> true;
@@ -3808,8 +3985,9 @@ lookup_type(Name, RecDict) ->
type_is_defined(TypeOrOpaque, Name, RecDict) ->
dict:is_key({TypeOrOpaque, Name}, RecDict).
-unfold(TypeName, TypeNames) ->
- not lists:member(TypeName, TypeNames).
+can_unfold_more(TypeName, TypeNames) ->
+ Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end,
+ lists:foldl(Fun, 0, TypeNames) < ?REC_TYPE_LIMIT.
%% -----------------------------------
%% Set
diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml
index 2c78558190..434bfac64c 100644
--- a/lib/hipe/doc/src/notes.xml
+++ b/lib/hipe/doc/src/notes.xml
@@ -30,6 +30,218 @@
</header>
<p>This document describes the changes made to HiPE.</p>
+<section><title>Hipe 3.7.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix erroneous fail info of a hipe_bs_primop</p>
+ <p>
+ Own Id: OTP-9036</p>
+ </item>
+ <item>
+ <p>
+ The change fixes a bug in the translation of 'bs_add'
+ BEAM instruction to HiPE's Icode representation. When
+ these instructions appeared in a guard context the
+ previous translation was obviously buggy.</p>
+ <p>
+ Own Id: OTP-9044</p>
+ </item>
+ <item>
+ <p>
+ Sanitize the specs of the code module</p>
+ <p>
+ After the addition of unicode_binary() to the
+ file:filename() type, dialyzer started complaining about
+ erroneous or incomplete specs in some functions of the
+ 'code' module. The culprit was hard-coded information in
+ erl_bif_types for functions of this module, which were
+ not updated. Since these functions have proper specs
+ these days and code duplication (pun intended) is never a
+ good idea, their type information was removed from
+ erl_bif_types.</p>
+ <p>
+ While doing this, some erroneous comments were fixed in
+ the code module and also made sure that the code now runs
+ without dialyzer warnings even when the
+ -Wunmatched_returns option is used.</p>
+ <p>
+ Some cleanups were applied to erl_bif_types too.</p>
+ <p>
+ Own Id: OTP-9100</p>
+ </item>
+ <item>
+ <p>
+ Fix bug in the simplification of inexact comparisons</p>
+ <p>
+ On 31/1/2011 Paul Guyot reported a bug in the native code
+ compilation of inexact equality/inequality tests between
+ floats and integers. The relevant test was:</p>
+ <p>
+ f(X) -&gt; Y = X / 2, Y == 0.</p>
+ <p>
+ and hipe erroneously evaluated the calls f(0) and f(0.0)
+ to 'false'.</p>
+ <p>
+ The culprit was in the simplification code of the Icode
+ range analysis which used an erroneous test (lists:any/1
+ instead of lists:all/1).</p>
+ <p>
+ Own Id: OTP-9101</p>
+ </item>
+ <item>
+ <p>
+ Document exiting and garbage_collecting process statuses</p>
+ <p>
+ Own Id: OTP-9102</p>
+ </item>
+ <item>
+ <p>
+ Remove hipe constants pool</p>
+ <p>
+ Hipe constants used to be allocated within a single,
+ fixed-size pool for interaction with the garbage
+ collector. However, the garbage collector no longer
+ depends on constants being allocated within a single
+ pool, and the fixed size of the pool both meant
+ unnecessary allocations on most deployments and crashes
+ on deployments requiring more constants.</p>
+ <p>
+ The code was simplified to directly invoke erts_alloc.
+ Debugging and undocumented function
+ hipe_bifs:show_literals/0 was removed (it returned true
+ and output text to the console), and debugging and
+ undocumented function hipe_bifs:constants_size/0 was
+ rewritten with a global to count the size of allocated
+ constants.</p>
+ <p>
+ Own Id: OTP-9128</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.7.8.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Several type specifications for standard libraries were
+ wrong in the R14B01 release. This is now corrected. The
+ corrections concern types in re,io,filename and the
+ module erlang itself.</p>
+ <p>
+ Own Id: OTP-9008</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.7.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Compiler warnings were eliminated.</p>
+ <p>
+ Own Id: OTP-8855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.7.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The HiPE compiler could crash when compiling certain
+ modules (the bug has been latent, and been exposed by new
+ optimizations introduced in the BEAM compiler in R14A).
+ (Thanks to Mikael Pettersson.)</p>
+ <p>
+ Own Id: OTP-8800</p>
+ </item>
+ <item>
+ <p>
+ hipe:load/1 was broken. (Thanks to Paul Guyot.)</p>
+ <p>
+ Own Id: OTP-8802</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Hipe 3.7.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p><c>receive</c> statements that can only read out a
+ newly created reference are now specially optimized so
+ that it will execute in constant time regardless of the
+ number of messages in the receive queue for the process.
+ That optimization will benefit calls to
+ <c>gen_server:call()</c>. (See <c>gen:do_call/4</c> for
+ an example of a receive statement that will be
+ optimized.)</p>
+ <p>
+ Own Id: OTP-8623</p>
+ </item>
+ <item>
+ <p>
+ Various changes to dialyzer-related files for R14.</p>
+ <p>
+ - Dialyzer properly supports the new attribute
+ -export_type and checks that remote types only refer to
+ exported types. A warning is produced if some
+ files/applications refer to types defined in modules
+ which are neither in the PLT nor in the analyzed
+ applications.</p>
+ <p>
+ - Support for detecting data races involving whereis/1
+ and unregister/1.</p>
+ <p>
+ - More precise identification of the reason(s) why a
+ record construction violates the types declared for its
+ fields.</p>
+ <p>
+ - Fixed bug in the handling of the 'or' guard.</p>
+ <p>
+ - Better handling of the erlang:element/2 BIF.</p>
+ <p>
+ - Complete handling of Erlang BIFs.</p>
+ <p>
+ Own Id: OTP-8699</p>
+ </item>
+ <item>
+ <p><c>eprof</c> has been reimplemented with support in
+ the Erlang virtual machine and is now both faster (i.e.
+ slows down the code being measured less) and scales much
+ better. In measurements we saw speed-ups compared to the
+ old eprof ranging from 6 times (for sequential code that
+ only uses one scheduler/core) up to 84 times (for
+ parallel code that uses 8 cores).</p>
+ <p>Note: The API for the <c>eprof</c> has been cleaned up
+ and extended. See the documentation.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8706</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Hipe 3.7.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/hipe/doc/src/ref_man.xml b/lib/hipe/doc/src/ref_man.xml
index 09d10147ee..bdafb61d08 100644
--- a/lib/hipe/doc/src/ref_man.xml
+++ b/lib/hipe/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
diff --git a/lib/hipe/flow/hipe_dominators.erl b/lib/hipe/flow/hipe_dominators.erl
index 3bfa6d43c4..17357461a5 100644
--- a/lib/hipe/flow/hipe_dominators.erl
+++ b/lib/hipe/flow/hipe_dominators.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%------------------------------------------------------------------------
@@ -37,6 +37,8 @@
domFrontier_create/2,
domFrontier_get/2]).
+-export_type([domTree/0]).
+
-include("cfg.hrl").
%%========================================================================
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index b9679fbb12..d7eb035551 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -369,6 +369,10 @@ trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) ->
trans_fun([{bif,BifName,{f,Lbl},[_,_] = Args,Reg}|Instructions], Env) ->
{BifInsts,Env1} = trans_bif(2,BifName,Lbl,Args,Reg,Env),
[hipe_icode:mk_comment({bif2,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
+%%--- bif3 ---
+trans_fun([{bif,BifName,{f,Lbl},[_,_,_] = Args,Reg}|Instructions], Env) ->
+ {BifInsts,Env1} = trans_bif(3,BifName,Lbl,Args,Reg,Env),
+ [hipe_icode:mk_comment({bif3,BifName})|BifInsts] ++ trans_fun(Instructions,Env1);
%%--- allocate
trans_fun([{allocate,StackSlots,_}|Instructions], Env) ->
trans_allocate(StackSlots) ++ trans_fun(Instructions,Env);
@@ -895,11 +899,6 @@ trans_fun([{bs_init_bits,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}|
end,
trans_bin_call({hipe_bs_primop,Name}, Lbl, Args, [Dst, Base, Offset],
Base, Offset, Env, Instructions);
-trans_fun([{bs_bits_to_bytes2, Bits, Bytes}|Instructions], Env) ->
- Src = trans_arg(Bits),
- Dst = mk_var(Bytes),
- [hipe_icode:mk_primop([Dst], 'bsl', [Src, hipe_icode:mk_const(3)])|
- trans_fun(Instructions,Env)];
trans_fun([{bs_add, {f,Lbl}, [Old,New,Unit], Res}|Instructions], Env) ->
Dst = mk_var(Res),
Temp = mk_var(new),
@@ -919,7 +918,7 @@ trans_fun([{bs_add, {f,Lbl}, [Old,New,Unit], Res}|Instructions], Env) ->
Succ = mk_label(new),
[hipe_icode:mk_primop([Temp], '*',
[NewVar, hipe_icode:mk_const(Unit)],
- hipe_icode:label_name(Succ), Lbl),
+ hipe_icode:label_name(Succ), map_label(Lbl)),
Succ]
end
end,
@@ -931,7 +930,7 @@ trans_fun([{bs_add, {f,Lbl}, [Old,New,Unit], Res}|Instructions], Env) ->
[FailLbl,
hipe_icode:mk_fail([hipe_icode:mk_const(badarg)], error)]};
true ->
- {Lbl, []}
+ {map_label(Lbl), []}
end,
IsPos =
[hipe_icode:mk_if('>=', [Temp, hipe_icode:mk_const(0)],
@@ -1129,13 +1128,6 @@ trans_fun([{gc_bif,Name,Fail,_Live,SrcRs,DstR}|Instructions], Env) ->
trans_fun([{bif,Name,Fail,SrcRs,DstR}|Instructions], Env)
end;
%%--------------------------------------------------------------------
-%% Instruction for constant pool added in February 2007 for R11B-4.
-%%--------------------------------------------------------------------
-trans_fun([{put_literal,{literal,Literal},DstR}|Instructions], Env) ->
- DstV = mk_var(DstR),
- Move = hipe_icode:mk_move(DstV, hipe_icode:mk_const(Literal)),
- [Move | trans_fun(Instructions, Env)];
-%%--------------------------------------------------------------------
%% New test instruction added in July 2007 for R12.
%%--------------------------------------------------------------------
%%--- is_bitstr ---
diff --git a/lib/hipe/icode/hipe_icode_callgraph.erl b/lib/hipe/icode/hipe_icode_callgraph.erl
index 95182fc002..ae4b5785c4 100644
--- a/lib/hipe/icode/hipe_icode_callgraph.erl
+++ b/lib/hipe/icode/hipe_icode_callgraph.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,8 +25,6 @@
%% in hipe_icode_type.erl.
%%
%% Created : 7 Jun 2004 by Tobias Lindahl <[email protected]>
-%%
-%% $Id$
%%-----------------------------------------------------------------------
-module(hipe_icode_callgraph).
@@ -48,7 +46,7 @@
-type mfa_icode() :: {mfa(), #icode{}}.
--record(icode_callgraph, {codedict :: dict(), ordered_sccs :: [[atom()]]}).
+-record(icode_callgraph, {codedict :: dict(), ordered_sccs :: [[mfa()]]}).
%%------------------------------------------------------------------------
%% Exported functions
@@ -78,7 +76,7 @@ construct_callgraph(List) ->
to_list(#icode_callgraph{codedict = Dict, ordered_sccs = SCCs}) ->
FlatList = lists:flatten(SCCs),
- [{Mod, dict:fetch(Mod, Dict)} || Mod <- FlatList].
+ [{MFA, dict:fetch(MFA, Dict)} || MFA <- FlatList].
%%------------------------------------------------------------------------
diff --git a/lib/hipe/icode/hipe_icode_exceptions.erl b/lib/hipe/icode/hipe_icode_exceptions.erl
index 787fb05415..00caffb24b 100644
--- a/lib/hipe/icode/hipe_icode_exceptions.erl
+++ b/lib/hipe/icode/hipe_icode_exceptions.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -344,6 +344,16 @@ pop_catch(Cs) ->
pop_catch_1([[_|C] | Cs]) ->
[C | pop_catch_1(Cs)];
+pop_catch_1([[] | Cs]) ->
+ %% The elements in the list represent different possible incoming
+ %% stacks of catch handlers to this BB. Before the fixpoint has
+ %% been found these elements are underapproximations of the true
+ %% stacks, therefore it's possible for these elements to be too
+ %% short for the number of pops implied by the code in the BB.
+ %% We must not fail in that case, so we set pop([]) = [].
+ %% This fixes find_catches_crash.erl and compiler_tests in the
+ %% HiPE test suite.
+ [[] | pop_catch_1(Cs)];
pop_catch_1([]) ->
[].
diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
index b0fe7eb708..a413531c07 100644
--- a/lib/hipe/icode/hipe_icode_primops.erl
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -2,19 +2,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -26,9 +26,6 @@
%% Notes :
%% History : * 2001-06-13 Erik Johansson ([email protected]):
%% Created.
-%%
-%% $Id$
-%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-module(hipe_icode_primops).
@@ -197,7 +194,7 @@ fails(#element{}) -> true;
%% fails(#gc_test{}) -> ???
fails({hipe_bs_primop, {bs_start_match, _}}) -> true;
fails({hipe_bs_primop, {{bs_start_match, bitstr}, _}}) -> true;
-fails({hipe_bs_primop, {{bs_start_match, ok_matchstate}, _}}) -> false;
+fails({hipe_bs_primop, {{bs_start_match, ok_matchstate}, _}}) -> true;
fails({hipe_bs_primop, {bs_get_binary, _, _}}) -> true;
fails({hipe_bs_primop, {bs_get_binary_all, _, _}}) -> true;
fails({hipe_bs_primop, {bs_get_binary_all_2, _, _}}) -> true;
diff --git a/lib/hipe/icode/hipe_icode_range.erl b/lib/hipe/icode/hipe_icode_range.erl
index bcc857acf4..c222e8a5d5 100644
--- a/lib/hipe/icode/hipe_icode_range.erl
+++ b/lib/hipe/icode/hipe_icode_range.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%%-------------------------------------------------------------------
@@ -59,15 +59,17 @@
-record(range, {range :: range_rep(),
other :: boolean()}).
+-type range() :: #range{}.
--record(ann, {range :: #range{},
+-record(ann, {range :: range(),
type :: erl_types:erl_type(),
count :: integer()}).
+-type ann() :: #ann{}.
--type range_anno() :: {range_anno, #ann{}, fun((#ann{}) -> string())}.
--type args_fun() :: fun((mfa(),cfg()) -> [#range{}]).
--type call_fun() :: fun((mfa(),[#range{}]) -> #range{}).
--type final_fun() :: fun((mfa(),[#range{}]) -> ok).
+-type range_anno() :: {'range_anno', ann(), fun((ann()) -> string())}.
+-type args_fun() :: fun((mfa(), cfg()) -> [range()]).
+-type call_fun() :: fun((mfa(), [range()]) -> range()).
+-type final_fun() :: fun((mfa(), [range()]) -> 'ok').
-type data() :: {mfa(), args_fun(), call_fun(), final_fun()}.
-type label() :: non_neg_integer().
-type info() :: gb_tree().
@@ -75,15 +77,15 @@
-type variable() :: #icode_variable{}.
-type annotated_variable() :: #icode_variable{}.
-type argument() :: #icode_const{} | variable().
--type three_range_fun() :: fun((#range{},#range{},#range{}) -> #range{}).
+-type three_range_fun() :: fun((range(),range(),range()) -> range()).
-type instr_split_info() :: {icode_instr(), [{label(),info()}]}.
--type last_instr_return() :: {instr_split_info(), #range{}}.
+-type last_instr_return() :: {instr_split_info(), range()}.
-record(state, {info_map = gb_trees:empty() :: info(),
counter = dict:new() :: dict(),
cfg :: cfg(),
liveness = gb_trees:empty() :: gb_tree(),
- ret_type :: #range{},
+ ret_type :: range(),
lookup_fun :: call_fun(),
result_action :: final_fun()}).
@@ -108,8 +110,8 @@ cfg(Cfg, MFA, Options, Servers) ->
-spec concurrent_cfg(cfg(), mfa(), pid()) -> cfg().
concurrent_cfg(Cfg, MFA, CompServer) ->
- CompServer ! {ready, {MFA,self()}},
- {ArgsFun,CallFun,FinalFun} = do_analysis(Cfg, MFA),
+ CompServer ! {ready, {MFA, self()}},
+ {ArgsFun, CallFun, FinalFun} = do_analysis(Cfg, MFA),
Ans = do_rewrite(Cfg, MFA, ArgsFun, CallFun, FinalFun),
CompServer ! {done_rewrite, MFA},
Ans.
@@ -227,7 +229,7 @@ analyse_block(Label, Info, State, Rewrite) ->
state__update_info(State2, InfoList, Rewrite).
-spec analyse_BB([icode_instr()], info(), [icode_instr()], boolean(), call_fun()) ->
- {[icode_instr()], [{label(),info()}], #range{}}.
+ {[icode_instr()], [{label(),info()}], range()}.
analyse_BB([Last], Info, Code, Rewrite, LookupFun) ->
{{NewI, LabelInfoList}, RetType} =
@@ -266,9 +268,9 @@ handle_args(I, Info, WidenFun) ->
%% io:format("Uses: ~p~nRanges: ~p~n", [Uses, PresentRanges]),
JoinFun = fun(Var, Range) -> update_info(Var, Range, WidenFun) end,
NewUses = lists:zipwith(JoinFun, Uses, PresentRanges),
- hipe_icode:subst_uses(lists:zip(Uses, NewUses),I).
+ hipe_icode:subst_uses(lists:zip(Uses, NewUses), I).
--spec join_info(#ann{}, #range{}, three_range_fun()) -> #ann{}.
+-spec join_info(ann(), range(), three_range_fun()) -> ann().
join_info(Ann = #ann{range = R1, type = Type, count = ?WIDEN}, R2, Fun) ->
Ann#ann{range = Fun(R1, R2, range_from_simple_type(Type))};
@@ -278,17 +280,17 @@ join_info(Ann = #ann{range = R1, type = Type, count = C}, R2, _Fun) when C < ?WI
NewR -> Ann#ann{range = NewR, count = C+1}
end.
--spec join_three(#range{}, #range{}, #range{}) -> #range{}.
+-spec join_three(range(), range(), range()) -> range().
join_three(R1, R2, R3) ->
inf(sup(R1, R2), R3).
--spec update_info(variable(), #range{}) -> annotated_variable().
+-spec update_info(variable(), range()) -> annotated_variable().
update_info(Var, Range) ->
update_info(Var, Range, fun update_three/3).
--spec update_info(variable(), #range{}, three_range_fun()) -> annotated_variable().
+-spec update_info(variable(), range(), three_range_fun()) -> annotated_variable().
update_info(Arg, R, Fun) ->
case hipe_icode:is_annotated_variable(Arg) of
@@ -299,7 +301,7 @@ update_info(Arg, R, Fun) ->
Arg
end.
--spec update_info1(any(), #range{}, three_range_fun()) -> range_anno().
+-spec update_info1(any(), range(), three_range_fun()) -> range_anno().
update_info1({range_anno, Ann, _}, R2, Fun) ->
make_range_anno(update_ann(Ann,R2,Fun));
@@ -314,71 +316,71 @@ update_ann(Ann = #ann{range = R1, type = Type, count = C}, R2, _Fun) ->
NewR -> Ann#ann{range = NewR, count = C+1}
end.
--spec type_to_ann(erl_types:erl_type()) -> #ann{}.
+-spec type_to_ann(erl_types:erl_type()) -> ann().
type_to_ann(Type) ->
- #ann{range = range_from_simple_type(Type), type = t_limit(Type,1), count=1}.
+ #ann{range = range_from_simple_type(Type), type = t_limit(Type,1), count = 1}.
--spec make_range_anno(#ann{}) -> range_anno().
+-spec make_range_anno(ann()) -> range_anno().
make_range_anno(Ann) ->
{range_anno, Ann, fun pp_ann/1}.
--spec update_three(#range{}, #range{}, #range{}) -> #range{}.
+-spec update_three(range(), range(), range()) -> range().
update_three(_R1, R2, R3) ->
inf(R2, R3).
--spec safe_widen(#range{}, #range{}, #range{}) -> #range{}.
+-spec safe_widen(range(), range(), range()) -> range().
safe_widen(#range{range=Old}, #range{range=New}, T = #range{range=Wide}) ->
ResRange =
- case {Old,New,Wide} of
- {{Min,Max1},{Min,Max2},{_,Max}} ->
- case inf_geq(OMax = next_up_limit(inf_max([Max1,Max2])),Max) of
+ case {Old, New, Wide} of
+ {{Min,Max1}, {Min,Max2}, {_,Max}} ->
+ case inf_geq(OMax = next_up_limit(inf_max([Max1, Max2])), Max) of
true -> {Min,Max};
false -> {Min,OMax}
end;
- {{Min1,Max},{Min2,Max},{Min,_}} ->
- case inf_geq(Min, OMin = next_down_limit(inf_min([Min1,Min2]))) of
+ {{Min1,Max}, {Min2,Max}, {Min,_}} ->
+ case inf_geq(Min, OMin = next_down_limit(inf_min([Min1, Min2]))) of
true -> {Min,Max};
false -> {OMin,Max}
end;
- {{Min1,Max1},{Min2,Max2},{Min,Max}} ->
+ {{Min1,Max1}, {Min2,Max2}, {Min,Max}} ->
RealMax =
- case inf_geq(OMax = next_up_limit(inf_max([Max1,Max2])),Max) of
+ case inf_geq(OMax = next_up_limit(inf_max([Max1, Max2])), Max) of
true -> Max;
false -> OMax
end,
RealMin =
- case inf_geq(Min, OMin = next_down_limit(inf_min([Min1,Min2]))) of
+ case inf_geq(Min, OMin = next_down_limit(inf_min([Min1, Min2]))) of
true -> Min;
false -> OMin
end,
- {RealMin,RealMax};
+ {RealMin, RealMax};
_ ->
Wide
end,
- T#range{range=ResRange}.
+ T#range{range = ResRange}.
--spec widen(#range{}, #range{}, #range{}) -> #range{}.
+-spec widen(range(), range(), range()) -> range().
widen(#range{range=Old}, #range{range=New}, T = #range{range=Wide}) ->
ResRange =
- case {Old,New,Wide} of
- {{Min,_},{Min,Max2},{_,Max}} ->
- case inf_geq(OMax = next_up_limit(Max2),Max) of
+ case {Old, New, Wide} of
+ {{Min,_}, {Min,Max2}, {_,Max}} ->
+ case inf_geq(OMax = next_up_limit(Max2), Max) of
true -> {Min,Max};
false -> {Min,OMax}
end;
- {{_,Max},{Min2,Max},{Min,_}} ->
+ {{_,Max}, {Min2,Max}, {Min,_}} ->
case inf_geq(Min, OMin = next_down_limit(Min2)) of
true -> {Min,Max};
false -> {OMin,Max}
end;
- {_,{Min2,Max2},{Min,Max}} ->
+ {_, {Min2,Max2}, {Min,Max}} ->
RealMax =
- case inf_geq(OMax = next_up_limit(Max2),Max) of
+ case inf_geq(OMax = next_up_limit(Max2), Max) of
true -> Max;
false -> OMax
end,
@@ -387,11 +389,11 @@ widen(#range{range=Old}, #range{range=New}, T = #range{range=Wide}) ->
true -> Min;
false -> OMin
end,
- {RealMin,RealMax};
+ {RealMin, RealMax};
_ ->
Wide
end,
- T#range{range=ResRange}.
+ T#range{range = ResRange}.
-spec analyse_call(#icode_call{}, call_fun()) -> #icode_call{}.
@@ -421,7 +423,7 @@ analyse_move(Move) ->
analyse_begin_handler(Handler) ->
SubstList =
- [{Dst,update_info(Dst,any_type())} ||
+ [{Dst, update_info(Dst, any_type())} ||
Dst <- hipe_icode:begin_handler_dstlist(Handler)],
hipe_icode:subst_defines(SubstList, Handler).
@@ -494,14 +496,14 @@ analyse_switch_val(Switch, Info, Rewrite) ->
end
end.
--spec update_infos(argument(), info(), [{#range{},label()}]) -> [{label(),info()}].
+-spec update_infos(argument(), info(), [{range(),label()}]) -> [{label(),info()}].
update_infos(Arg, Info, [{Range, Label}|Rest]) ->
- [{Label,enter_define({Arg,Range},Info)} | update_infos(Arg,Info,Rest)];
+ [{Label,enter_define({Arg,Range},Info)} | update_infos(Arg, Info, Rest)];
update_infos(_, _, []) -> [].
--spec get_range_label_list([{argument(),label()}], #range{}, [{#range{},label()}]) ->
- {#range{},[{#range{},label()}]}.
+-spec get_range_label_list([{argument(),label()}], range(), [{range(),label()}]) ->
+ {range(),[{range(),label()}]}.
get_range_label_list([{Val,Label}|Cases], SRange, Acc) ->
VRange = get_range_from_arg(Val),
@@ -516,7 +518,7 @@ get_range_label_list([], SRange, Acc) ->
{PointTypes, _} = lists:unzip(Acc),
{remove_point_types(SRange, PointTypes), Acc}.
--spec update_switch(#icode_switch_val{}, [{#range{},label()}], boolean()) ->
+-spec update_switch(#icode_switch_val{}, [{range(),label()}], boolean()) ->
#icode_switch_val{}.
update_switch(Switch, LabelRangeList, KeepFail) ->
@@ -524,14 +526,14 @@ update_switch(Switch, LabelRangeList, KeepFail) ->
case label_range_list_to_cases(LabelRangeList, []) of
no_update ->
Switch;
- Cases ->
+ Cases ->
hipe_icode:switch_val_cases_update(Switch, Cases)
end,
if KeepFail -> S2;
true -> S2
end.
--spec label_range_list_to_cases([{#range{},label()}], [{#icode_const{},label()}]) ->
+-spec label_range_list_to_cases([{range(),label()}], [{#icode_const{},label()}]) ->
'no_update' | [{#icode_const{},label()}].
label_range_list_to_cases([{#range{range={C,C},other=false},Label}|Rest],
@@ -586,9 +588,9 @@ analyse_last_call(Call, Info, LookupFun) ->
NewInfo = enter_vals(NewI, Info),
case hipe_icode:call_fail_label(Call) of
[] ->
- {NewI, [{Continuation,NewInfo}]};
+ {NewI, [{Continuation, NewInfo}]};
Fail ->
- {NewI, [{Continuation,NewInfo}, {Fail,Info}]}
+ {NewI, [{Continuation, NewInfo}, {Fail, Info}]}
end.
-spec analyse_if(#icode_if{}, info(), boolean()) ->
@@ -596,16 +598,16 @@ analyse_last_call(Call, Info, LookupFun) ->
analyse_if(If, Info, Rewrite) ->
case hipe_icode:if_args(If) of
- Args = [_,_] ->
+ [_, _] = Args ->
analyse_sane_if(If, Info, Args, get_range_from_args(Args), Rewrite);
_ ->
TrueLabel = hipe_icode:if_true_label(If),
FalseLabel = hipe_icode:if_false_label(If),
- {If, [{TrueLabel,Info},{FalseLabel,Info}]}
+ {If, [{TrueLabel, Info}, {FalseLabel, Info}]}
end.
-spec analyse_sane_if(#icode_if{}, info(), [argument(),...],
- [#range{},...], boolean()) ->
+ [range(),...], boolean()) ->
{#icode_goto{} | #icode_if{}, [{label(), info()}]}.
analyse_sane_if(If, Info, [Arg1, Arg2], [Range1, Range2], Rewrite) ->
@@ -613,59 +615,61 @@ analyse_sane_if(If, Info, [Arg1, Arg2], [Range1, Range2], Rewrite) ->
'>' ->
{TrueRange2, TrueRange1, FalseRange2, FalseRange1} =
range_inequality_propagation(Range2, Range1);
- '==' ->
- {TempTrueRange1, TempTrueRange2, FalseRange1, FalseRange2}=
- range_equality_propagation(Range1, Range2),
- TrueRange1 = set_other(TempTrueRange1,other(Range1)),
- TrueRange2 = set_other(TempTrueRange2,other(Range2));
'<' ->
- {TrueRange1, TrueRange2, FalseRange1, FalseRange2} =
+ {TrueRange1, TrueRange2, FalseRange1, FalseRange2} =
range_inequality_propagation(Range1, Range2);
'>=' ->
{FalseRange1, FalseRange2, TrueRange1, TrueRange2} =
range_inequality_propagation(Range1, Range2);
'=<' ->
- {FalseRange2, FalseRange1, TrueRange2, TrueRange1} =
+ {FalseRange2, FalseRange1, TrueRange2, TrueRange1} =
range_inequality_propagation(Range2, Range1);
'=:=' ->
- {TrueRange1, TrueRange2, FalseRange1, FalseRange2}=
+ {TrueRange1, TrueRange2, FalseRange1, FalseRange2} =
range_equality_propagation(Range1, Range2);
'=/=' ->
{FalseRange1, FalseRange2, TrueRange1, TrueRange2} =
range_equality_propagation(Range1, Range2);
+ '==' ->
+ {TempTrueRange1, TempTrueRange2, FalseRange1, FalseRange2} =
+ range_equality_propagation(Range1, Range2),
+ TrueRange1 = set_other(TempTrueRange1, other(Range1)),
+ TrueRange2 = set_other(TempTrueRange2, other(Range2));
'/=' ->
- {TempFalseRange1, TempFalseRange2, TrueRange1, TrueRange2}=
+ {TempFalseRange1, TempFalseRange2, TrueRange1, TrueRange2} =
range_equality_propagation(Range1, Range2),
- FalseRange1 = set_other(TempFalseRange1,other(Range1)),
- FalseRange2 = set_other(TempFalseRange2,other(Range2))
+ FalseRange1 = set_other(TempFalseRange1, other(Range1)),
+ FalseRange2 = set_other(TempFalseRange2, other(Range2))
end,
- TrueLabel = hipe_icode:if_true_label(If),
- FalseLabel = hipe_icode:if_false_label(If),
- TrueInfo =
- enter_defines([{Arg1,TrueRange1}, {Arg2,TrueRange2}],Info),
- FalseInfo =
- enter_defines([{Arg1,FalseRange1}, {Arg2,FalseRange2}],Info),
- True =
- case lists:any(fun range__is_none/1,[TrueRange1,TrueRange2]) of
+ %% io:format("TR1 = ~w\nTR2 = ~w\n", [TrueRange1, TrueRange2]),
+ True =
+ case lists:all(fun range__is_none/1, [TrueRange1, TrueRange2]) of
true -> [];
- false -> [{TrueLabel,TrueInfo}]
+ false ->
+ TrueLabel = hipe_icode:if_true_label(If),
+ TrueArgRanges = [{Arg1, TrueRange1}, {Arg2, TrueRange2}],
+ TrueInfo = enter_defines(TrueArgRanges, Info),
+ [{TrueLabel, TrueInfo}]
end,
- False =
- case lists:any(fun range__is_none/1, [FalseRange1,FalseRange2]) of
+ %% io:format("FR1 = ~w\nFR2 = ~w\n", [FalseRange1, FalseRange2]),
+ False =
+ case lists:all(fun range__is_none/1, [FalseRange1, FalseRange2]) of
true -> [];
- false -> [{FalseLabel,FalseInfo}]
+ false ->
+ FalseLabel = hipe_icode:if_false_label(If),
+ FalseArgRanges = [{Arg1, FalseRange1}, {Arg2, FalseRange2}],
+ FalseInfo = enter_defines(FalseArgRanges, Info),
+ [{FalseLabel, FalseInfo}]
end,
- UpdateInfo = True++False,
+ UpdateInfo = True ++ False,
NewIF =
if Rewrite ->
- %%io:format("~w~n~w~n", [{Arg1,FalseRange1},{Arg2,FalseRange2}]),
- %%io:format("Any none: ~w~n", [lists:any(fun range__is_none/1,[FalseRange1,FalseRange2])]),
case UpdateInfo of
- [] -> %%This is weird
+ [] -> %% This is weird
If;
- [{Label,_Info}] ->
+ [{Label, _Info}] ->
hipe_icode:mk_goto(Label);
- [_,_] ->
+ [_, _] ->
If
end;
true ->
@@ -686,13 +690,13 @@ normalize_name(Name) ->
Name -> Name
end.
--spec range_equality_propagation(#range{}, #range{}) ->
- {#range{}, #range{}, #range{}, #range{}}.
+-spec range_equality_propagation(range(), range()) ->
+ {range(), range(), range(), range()}.
range_equality_propagation(Range_1, Range_2) ->
True_range = inf(Range_1, Range_2),
case {range(Range_1), range(Range_2)} of
- {{N,N},{ N,N}} ->
+ {{N,N}, {N,N}} ->
False_range_1 = none_range(),
False_range_2 = none_range();
{{N1,N1}, {N2,N2}} ->
@@ -710,8 +714,8 @@ range_equality_propagation(Range_1, Range_2) ->
end,
{True_range, True_range, False_range_1, False_range_2}.
--spec range_inequality_propagation(#range{}, #range{}) ->
- {#range{}, #range{}, #range{}, #range{}}.
+-spec range_inequality_propagation(range(), range()) ->
+ {range(), range(), range(), range()}.
%% Range1 < Range2
range_inequality_propagation(Range1, Range2) ->
@@ -781,26 +785,24 @@ analyse_type(Type, Info, Rewrite) ->
TrueRange = inf(any_range(), OldVarRange),
FalseRange = inf(none_range(), OldVarRange);
_ ->
- TrueRange = inf(none_range(),OldVarRange),
+ TrueRange = inf(none_range(), OldVarRange),
FalseRange = OldVarRange
end,
TrueLabel = hipe_icode:type_true_label(Type),
FalseLabel = hipe_icode:type_false_label(Type),
- TrueInfo =
- enter_define({Arg,TrueRange},Info),
- FalseInfo =
- enter_define({Arg,FalseRange},Info),
- True =
+ TrueInfo = enter_define({Arg, TrueRange}, Info),
+ FalseInfo = enter_define({Arg, FalseRange}, Info),
+ True =
case range__is_none(TrueRange) of
true -> [];
- false -> [{TrueLabel,TrueInfo}]
+ false -> [{TrueLabel, TrueInfo}]
end,
- False =
+ False =
case range__is_none(FalseRange) of
true -> [];
- false -> [{FalseLabel,FalseInfo}]
+ false -> [{FalseLabel, FalseInfo}]
end,
- UpdateInfo = True++False,
+ UpdateInfo = True ++ False,
NewType =
if Rewrite ->
case UpdateInfo of
@@ -808,15 +810,15 @@ analyse_type(Type, Info, Rewrite) ->
Type;
[{Label,_Info}] ->
hipe_icode:mk_goto(Label);
- [_,_] ->
+ [_, _] ->
Type
end;
true ->
Type
end,
- {NewType,True ++ False}.
+ {NewType, True ++ False}.
--spec compare_with_integer(integer(), #range{}) -> {#range{}, #range{}}.
+-spec compare_with_integer(integer(), range()) -> {range(), range()}.
compare_with_integer(N, OldVarRange) ->
TestRange = range_init({N, N}, false),
@@ -843,13 +845,13 @@ compare_with_integer(N, OldVarRange) ->
%%== Ranges ==================================================================
--spec pp_ann(#ann{} | erl_types:erl_type()) -> [string()].
+-spec pp_ann(ann() | erl_types:erl_type()) -> string().
-pp_ann(#ann{range=#range{range=R, other=false}}) ->
+pp_ann(#ann{range = #range{range = R, other = false}}) ->
pp_range(R);
-pp_ann(#ann{range=#range{range=empty, other=true}, type=Type}) ->
+pp_ann(#ann{range = #range{range = empty, other = true}, type = Type}) ->
t_to_string(Type);
-pp_ann(#ann{range=#range{range=R, other=true}, type=Type}) ->
+pp_ann(#ann{range = #range{range = R, other = true}, type = Type}) ->
pp_range(R) ++ " | " ++ t_to_string(Type);
pp_ann(Type) ->
t_to_string(Type).
@@ -867,12 +869,12 @@ val_to_string(pos_inf) -> "inf";
val_to_string(neg_inf) -> "-inf";
val_to_string(X) when is_integer(X) -> integer_to_list(X).
--spec range_from_type(erl_types:erl_type()) -> [#range{}].
+-spec range_from_type(erl_types:erl_type()) -> [range()].
range_from_type(Type) ->
[range_from_simple_type(T) || T <- t_to_tlist(Type)].
--spec range_from_simple_type(erl_types:erl_type()) -> #range{}.
+-spec range_from_simple_type(erl_types:erl_type()) -> range().
range_from_simple_type(Type) ->
None = t_none(),
@@ -887,7 +889,7 @@ range_from_simple_type(Type) ->
#range{range = Range, other = true}
end.
--spec range_init(range_rep(), boolean()) -> #range{}.
+-spec range_init(range_rep(), boolean()) -> range().
range_init({Min, Max} = Range, Other) ->
case inf_geq(Max, Min) of
@@ -899,39 +901,39 @@ range_init({Min, Max} = Range, Other) ->
range_init(empty, Other) ->
#range{range = empty, other = Other}.
--spec range(#range{}) -> range_rep().
+-spec range(range()) -> range_rep().
range(#range{range = R}) -> R.
--spec other(#range{}) -> boolean().
+-spec other(range()) -> boolean().
other(#range{other = O}) -> O.
--spec set_other(#range{}, boolean()) -> #range{}.
+-spec set_other(range(), boolean()) -> range().
set_other(R, O) -> R#range{other = O}.
--spec range__min(#range{}) -> 'empty' | 'neg_inf' | integer().
+-spec range__min(range()) -> 'empty' | 'neg_inf' | integer().
-range__min(#range{range=empty}) -> empty;
-range__min(#range{range={Min,_}}) -> Min.
+range__min(#range{range = empty}) -> empty;
+range__min(#range{range = {Min,_}}) -> Min.
--spec range__max(#range{}) -> 'empty' | 'pos_inf' | integer().
+-spec range__max(range()) -> 'empty' | 'pos_inf' | integer().
-range__max(#range{range=empty}) -> empty;
-range__max(#range{range={_,Max}}) -> Max.
+range__max(#range{range = empty}) -> empty;
+range__max(#range{range = {_,Max}}) -> Max.
--spec range__is_none(#range{}) -> boolean().
+-spec range__is_none(range()) -> boolean().
-range__is_none(#range{range=empty, other=false}) -> true;
+range__is_none(#range{range = empty, other = false}) -> true;
range__is_none(#range{}) -> false.
--spec range__is_empty(#range{}) -> boolean().
+-spec range__is_empty(range()) -> boolean().
-range__is_empty(#range{range=empty}) -> true;
-range__is_empty(#range{range={_,_}}) -> false.
+range__is_empty(#range{range = empty}) -> true;
+range__is_empty(#range{range = {_,_}}) -> false.
--spec remove_point_types(#range{}, [#range{}]) -> #range{}.
+-spec remove_point_types(range(), [range()]) -> range().
remove_point_types(Range, Ranges) ->
Sorted = lists:sort(Ranges),
@@ -939,35 +941,35 @@ remove_point_types(Range, Ranges) ->
Range1 = lists:foldl(FoldFun, Range, Sorted),
lists:foldl(FoldFun, Range1, lists:reverse(Sorted)).
--spec range__remove_constant(#range{}, #range{}) -> #range{}.
+-spec range__remove_constant(range(), range()) -> range().
-range__remove_constant(R = #range{range={C,C}}, #range{range={C,C}}) ->
- R#range{range=empty};
-range__remove_constant(R = #range{range={C,H}}, #range{range={C,C}}) ->
- R#range{range={C+1,H}};
-range__remove_constant(R = #range{range={L,C}}, #range{range={C,C}}) ->
- R#range{range={L,C-1}};
-range__remove_constant(R = #range{}, #range{range={C,C}}) ->
+range__remove_constant(#range{range = {C, C}} = R, #range{range = {C, C}}) ->
+ R#range{range = empty};
+range__remove_constant(#range{range = {C, H}} = R, #range{range = {C, C}}) ->
+ R#range{range = {C+1, H}};
+range__remove_constant(#range{range = {L, C}} = R, #range{range = {C, C}}) ->
+ R#range{range = {L, C-1}};
+range__remove_constant(#range{} = R, #range{range = {C,C}}) ->
R;
-range__remove_constant(R = #range{}, _) ->
+range__remove_constant(#range{} = R, _) ->
R.
--spec any_type() -> #range{}.
+-spec any_type() -> range().
any_type() ->
- #range{range=any_r(), other=true}.
+ #range{range = any_r(), other = true}.
--spec any_range() -> #range{}.
+-spec any_range() -> range().
any_range() ->
- #range{range=any_r(), other=false}.
+ #range{range = any_r(), other = false}.
--spec none_range() -> #range{}.
+-spec none_range() -> range().
none_range() ->
- #range{range=empty, other=true}.
+ #range{range = empty, other = true}.
--spec none_type() -> #range{}.
+-spec none_type() -> range().
none_type() ->
#range{range = empty, other = false}.
@@ -976,12 +978,12 @@ none_type() ->
any_r() -> {neg_inf, pos_inf}.
--spec get_range_from_args([argument()]) -> [#range{}].
+-spec get_range_from_args([argument()]) -> [range()].
get_range_from_args(Args) ->
[get_range_from_arg(Arg) || Arg <- Args].
--spec get_range_from_arg(argument()) -> #range{}.
+-spec get_range_from_arg(argument()) -> range().
get_range_from_arg(Arg) ->
case hipe_icode:is_const(Arg) of
@@ -989,15 +991,15 @@ get_range_from_arg(Arg) ->
Value = hipe_icode:const_value(Arg),
case is_integer(Value) of
true ->
- #range{range={Value,Value}, other=false};
+ #range{range = {Value, Value}, other = false};
false ->
- #range{range=empty, other=true}
+ #range{range = empty, other = true}
end;
false ->
case hipe_icode:is_annotated_variable(Arg) of
true ->
case hipe_icode:variable_annotation(Arg) of
- {range_anno, #ann{range=Range}, _} ->
+ {range_anno, #ann{range = Range}, _} ->
Range;
{type_anno, Type, _} ->
range_from_simple_type(Type)
@@ -1012,7 +1014,7 @@ get_range_from_arg(Arg) ->
%% inf([R1,R2|Rest]) ->
%% inf([inf(R1,R2)|Rest]).
--spec inf(#range{}, #range{}) -> #range{}.
+-spec inf(range(), range()) -> range().
inf(#range{range=R1, other=O1}, #range{range=R2, other=O2}) ->
#range{range=range_inf(R1,R2), other=other_inf(O1,O2)}.
@@ -1022,8 +1024,8 @@ inf(#range{range=R1, other=O1}, #range{range=R2, other=O2}) ->
range_inf(empty, _) -> empty;
range_inf(_, empty) -> empty;
range_inf({Min1,Max1}, {Min2,Max2}) ->
- NewMin = inf_max([Min1,Min2]),
- NewMax = inf_min([Max1,Max2]),
+ NewMin = inf_max([Min1, Min2]),
+ NewMax = inf_min([Max1, Max2]),
case inf_geq(NewMax, NewMin) of
true ->
{NewMin, NewMax};
@@ -1035,14 +1037,14 @@ range_inf({Min1,Max1}, {Min2,Max2}) ->
other_inf(O1, O2) -> O1 and O2.
--spec sup([#range{},...]) -> #range{}.
+-spec sup([range(),...]) -> range().
sup([R]) ->
R;
sup([R1,R2|Rest]) ->
sup([sup(R1, R2)|Rest]).
--spec sup(#range{}, #range{}) -> #range{}.
+-spec sup(range(), range()) -> range().
sup(#range{range=R1,other=O1}, #range{range=R2,other=O2}) ->
#range{range=range_sup(R1,R2), other=other_sup(O1,O2)}.
@@ -1063,7 +1065,7 @@ other_sup(O1, O2) -> O1 or O2.
%%== Call Support =============================================================
-spec analyse_call_or_enter_fun(fun_name(), [argument()],
- icode_call_type(), call_fun()) -> [#range{}].
+ icode_call_type(), call_fun()) -> [range()].
analyse_call_or_enter_fun(Fun, Args, CallType, LookupFun) ->
%%io:format("Fun: ~p~n Args: ~p~n CT: ~p~n LF: ~p~n", [Fun, Args, CallType, LookupFun]),
@@ -1105,19 +1107,19 @@ analyse_call_or_enter_fun(Fun, Args, CallType, LookupFun) ->
[any_type()];
{hipe_bs_primop, {bs_get_integer, Size, Flags}} ->
{Min, Max} = analyse_bs_get_integer(Size, Flags, length(Args) =:= 1),
- [#range{range={Min, Max}, other=false}, any_type()];
+ [#range{range = {Min, Max}, other = false}, any_type()];
{hipe_bs_primop, _} = Primop ->
Type = hipe_icode_primops:type(Primop),
range_from_type(Type)
end.
--type bin_operation() :: fun((#range{},#range{}) -> #range{}).
--type unary_operation() :: fun((#range{}) -> #range{}).
+-type bin_operation() :: fun((range(), range()) -> range()).
+-type unary_operation() :: fun((range()) -> range()).
-spec basic_type(fun_name()) -> 'not_int' | 'not_analysed'
- | {bin, bin_operation()}
- | {unary, unary_operation()}
- | {fcall, mfa()} | {hipe_bs_primop, _}.
+ | {'bin', bin_operation()}
+ | {'unary', unary_operation()}
+ | {'fcall', mfa()} | {'hipe_bs_primop', _}.
%% Arithmetic operations
basic_type('+') -> {bin, fun(R1, R2) -> range_add(R1, R2) end};
@@ -1214,7 +1216,7 @@ analyse_bs_get_integer(Size, Flags, false) when is_integer(Size),
%% Arithmetic
--spec range_add(#range{}, #range{}) -> #range{}.
+-spec range_add(range(), range()) -> range().
range_add(Range1, Range2) ->
NewMin = inf_add(range__min(Range1), range__min(Range2)),
@@ -1222,7 +1224,7 @@ range_add(Range1, Range2) ->
Other = other(Range1) orelse other(Range2),
range_init({NewMin, NewMax}, Other).
--spec range_sub(#range{}, #range{}) -> #range{}.
+-spec range_sub(range(), range()) -> range().
range_sub(Range1, Range2) ->
Min_sub = inf_min([inf_inv(range__max(Range2)),
@@ -1234,7 +1236,7 @@ range_sub(Range1, Range2) ->
Other = other(Range1) orelse other(Range2),
range_init({NewMin, NewMax}, Other).
--spec range_mult(#range{}, #range{}) -> #range{}.
+-spec range_mult(range(), range()) -> range().
range_mult(#range{range=empty, other=true}, _Range2) ->
range_init(empty, true);
@@ -1274,7 +1276,7 @@ range_mult(Range1, Range2) ->
Other = other(Range1) orelse other(Range2),
range_init(Range, Other).
--spec extreme_divisors(#range{}) -> range_tuple().
+-spec extreme_divisors(range()) -> range_tuple().
extreme_divisors(#range{range={0,0}}) -> {0,0};
extreme_divisors(#range{range={0,Max}}) -> {1,Max};
@@ -1289,7 +1291,7 @@ extreme_divisors(#range{range={Min,Max}}) ->
end
end.
--spec range_div(#range{}, #range{}) -> #range{}.
+-spec range_div(range(), range()) -> range().
%% this is div, not /.
range_div(_, #range{range={0,0}}) ->
@@ -1306,7 +1308,7 @@ range_div(Range1, Den) ->
inf_div(Max1, Min2), inf_div(Max1, Max2)],
range_init({inf_min(Min_max_list), inf_max(Min_max_list)}, false).
--spec range_rem(#range{}, #range{}) -> #range{}.
+-spec range_rem(range(), range()) -> range().
range_rem(Range1, Range2) ->
%% Range1 desides the sign of the answer.
@@ -1332,7 +1334,7 @@ range_rem(Range1, Range2) ->
%%--- Bit operations ----------------------------
--spec range_bsr(#range{}, #range{}) -> #range{}.
+-spec range_bsr(range(), range()) -> range().
range_bsr(Range1, Range2=#range{range={Min, Max}}) ->
New_Range2 = range_init({inf_inv(Max), inf_inv(Min)}, other(Range2)),
@@ -1340,7 +1342,7 @@ range_bsr(Range1, Range2=#range{range={Min, Max}}) ->
%% io:format("bsr res:~w~nInput:= ~w~n", [Ans, {Range1,Range2}]),
Ans.
--spec range_bsl(#range{}, #range{}) -> #range{}.
+-spec range_bsl(range(), range()) -> range().
range_bsl(Range1, Range2) ->
Min1 = range__min(Range1),
@@ -1359,13 +1361,13 @@ range_bsl(Range1, Range2) ->
end,
range_init(MinMax, false).
--spec range_bnot(#range{}) -> #range{}.
+-spec range_bnot(range()) -> range().
range_bnot(Range) ->
Minus_one = range_init({-1,-1}, false),
range_add(range_mult(Range, Minus_one), Minus_one).
--spec width(range_rep() | integer()) -> 'pos_inf' | non_neg_integer().
+-spec width(range_rep() | inf_integer()) -> 'pos_inf' | non_neg_integer().
width({Min, Max}) -> inf_max([width(Min), width(Max)]);
width(pos_inf) -> pos_inf;
@@ -1389,7 +1391,7 @@ negwidth(X, N) ->
false -> negwidth(X, N+1)
end.
--spec range_band(#range{}, #range{}) -> #range{}.
+-spec range_band(range(), range()) -> range().
range_band(R1, R2) ->
{_Min1, Max1} = MM1 = range(R1),
@@ -1423,7 +1425,7 @@ range_band(R1, R2) ->
end,
range_init(Range, false).
--spec range_bor(#range{}, #range{}) -> #range{}.
+-spec range_bor(range(), range()) -> range().
range_bor(R1, R2) ->
{Min1, _Max1} = MM1 = range(R1),
@@ -1457,7 +1459,7 @@ range_bor(R1, R2) ->
end,
range_init(Range, false).
--spec classify_range(#range{}) -> 'minus_minus' | 'minus_plus' | 'plus_plus'.
+-spec classify_range(range()) -> 'minus_minus' | 'minus_plus' | 'plus_plus'.
classify_range(Range) ->
case range(Range) of
@@ -1480,7 +1482,7 @@ classify_int_range(_Number1, Number2) when Number2 < 0 ->
classify_int_range(_Number1, _Number2) ->
minus_plus.
--spec range_bxor(#range{}, #range{}) -> #range{}.
+-spec range_bxor(range(), range()) -> range().
range_bxor(R1, R2) ->
{Min1, Max1} = MM1 = range(R1),
@@ -1895,7 +1897,7 @@ convert_ann_to_types(#ann{range=#range{other=true}, type=Type}) ->
%% Icode Coordinator Callbacks
%%=====================================================================
--spec replace_nones([#range{}]) -> [#range{}].
+-spec replace_nones([range()]) -> [range()].
replace_nones(Args) ->
[replace_none(Arg) || Arg <- Args].
@@ -1905,7 +1907,7 @@ replace_none(Arg) ->
false -> Arg
end.
--spec update__info([#range{}], [#range{}]) -> {boolean(), [#ann{}]}.
+-spec update__info([range()], [range()]) -> {boolean(), [ann()]}.
update__info(NewRanges, OldRanges) ->
SupFun = fun (Ann, Range) ->
join_info(Ann, Range, fun safe_widen/3)
@@ -1915,19 +1917,19 @@ update__info(NewRanges, OldRanges) ->
Change = lists:zipwith(EqFun, ResRanges, OldRanges),
{lists:all(fun (X) -> X end, Change), ResRanges}.
--spec new__info/1 :: ([#range{}]) -> [#ann{}].
+-spec new__info([range()]) -> [ann()].
new__info(NewRanges) ->
[#ann{range=Range,count=1,type=t_any()} || Range <- NewRanges].
--spec return__info/1 :: ([#ann{}]) -> [#range{}].
+-spec return__info([ann()]) -> [range()].
return__info(Ranges) ->
[Range || #ann{range=Range} <- Ranges].
--spec return_none/0 :: () -> [#range{},...].
+-spec return_none() -> [range(),...].
return_none() ->
[none_type()].
--spec return_none_args/2 :: (#cfg{}, mfa()) -> [#range{}].
+-spec return_none_args(cfg(), mfa()) -> [range()].
return_none_args(Cfg, {_M,_F,A}) ->
NoArgs =
case hipe_icode_cfg:is_closure(Cfg) of
@@ -1936,7 +1938,7 @@ return_none_args(Cfg, {_M,_F,A}) ->
end,
lists:duplicate(NoArgs, none_type()).
--spec return_any_args/2 :: (#cfg{}, mfa()) -> [#range{}].
+-spec return_any_args(cfg(), mfa()) -> [range()].
return_any_args(Cfg, {_M,_F,A}) ->
NoArgs =
case hipe_icode_cfg:is_closure(Cfg) of
diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl
index 28198467f7..3f9488d7c3 100644
--- a/lib/hipe/icode/hipe_icode_type.erl
+++ b/lib/hipe/icode/hipe_icode_type.erl
@@ -2,19 +2,19 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%%--------------------------------------------------------------------
@@ -23,8 +23,6 @@
%%% Description : Propagate type information.
%%%
%%% Created : 25 Feb 2003 by Tobias Lindahl <[email protected]>
-%%%
-%%% $Id$
%%%--------------------------------------------------------------------
-module(hipe_icode_type).
@@ -78,7 +76,7 @@
%-define(server_debug, fun(X, Y) -> io:format("~p server: ~s ~p~n", [self(), X, Y]) end).
-define(server_debug, fun(_, _) -> ok end).
--import(erl_types, [min/2, max/2, number_min/1, number_max/1,
+-import(erl_types, [number_min/1, number_max/1,
t_any/0, t_atom/1, t_atom/0, t_atom_vals/1,
t_binary/0, t_bitstr/0, t_bitstr_base/1, t_bitstr_unit/1,
t_boolean/0, t_cons/0, t_constant/0,
@@ -494,10 +492,10 @@ integer_range_less_then_propagator(IntArg1, IntArg2) ->
Min2 = number_min(IntArg2),
Max2 = number_max(IntArg2),
%% is this the same as erl_types:t_subtract?? no ... ??
- TrueMax1 = min(Max1, erl_bif_types:infinity_add(Max2, -1)),
- TrueMin2 = max(erl_bif_types:infinity_add(Min1, 1), Min2),
- FalseMin1 = max(Min1, Min2),
- FalseMax2 = min(Max1, Max2),
+ TrueMax1 = erl_types:min(Max1, erl_bif_types:infinity_add(Max2, -1)),
+ TrueMin2 = erl_types:max(erl_bif_types:infinity_add(Min1, 1), Min2),
+ FalseMin1 = erl_types:max(Min1, Min2),
+ FalseMax2 = erl_types:min(Max1, Max2),
{t_from_range(Min1, TrueMax1),
t_from_range(TrueMin2, Max2),
t_from_range(FalseMin1, Max1),
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index ed722fecba..570e4d9d17 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% ====================================================================
@@ -25,7 +25,6 @@
%% Purpose :
%% Notes :
%% History : * 1998-01-28 Erik Johansson ([email protected]): Created.
-%% CVS : $Id$
%% ====================================================================
%% @doc This is the direct interface to the HiPE compiler.
%%
@@ -274,7 +273,7 @@ load(Mod, BeamFileName) when is_list(BeamFileName) ->
Architecture = erlang:system_info(hipe_architecture),
ChunkName = hipe_unified_loader:chunk_name(Architecture),
case beam_lib:chunks(BeamFileName, [ChunkName]) of
- {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> do_load(Mod, Bin, Bin);
+ {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> do_load(Mod, Bin, BeamFileName);
Error -> {error, Error}
end.
@@ -506,7 +505,7 @@ compile(Name, File, Opts0) ->
run_compiler(Name, DisasmFun, IcodeFun, NewOpts)
end.
--spec compile_core(mod(), _, compile_file(), comp_options()) ->
+-spec compile_core(mod(), cerl:c_module(), compile_file(), comp_options()) ->
{'ok', compile_ret()} | {'error', term()}.
compile_core(Name, Core0, File, Opts) ->
@@ -535,7 +534,7 @@ compile_core(Name, Core0, File, Opts) ->
%%
%% @see compile/3
--spec compile(mod(), _, compile_file(), comp_options()) ->
+-spec compile(mod(), cerl:c_module() | [], compile_file(), comp_options()) ->
{'ok', compile_ret()} | {'error', term()}.
compile(Name, [], File, Opts) ->
@@ -790,7 +789,7 @@ finalize_fun(MfaIcodeList, Exports, Opts) ->
FalseVal when (FalseVal =:= undefined) orelse (FalseVal =:= false) ->
[finalize_fun_sequential(MFAIcode, Opts, #comp_servers{})
|| {_MFA, _Icode} = MFAIcode <- MfaIcodeList];
- TrueVal when (TrueVal =:= true) or (TrueVal =:= debug) ->
+ TrueVal when (TrueVal =:= true) orelse (TrueVal =:= debug) ->
finalize_fun_concurrent(MfaIcodeList, Exports, Opts)
end.
@@ -913,7 +912,7 @@ do_load(Mod, Bin, WholeModule) ->
%% In this case, the emulated code for the module must be loaded.
{module, Mod} = code:ensure_loaded(Mod),
code:load_native_partial(Mod, Bin);
- BinCode when is_binary(BinCode) ->
+ BeamBinOrPath when is_binary(BeamBinOrPath) orelse is_list(BeamBinOrPath) ->
case code:is_sticky(Mod) of
true ->
%% We unpack and repack the Beam binary as a workaround to
@@ -939,6 +938,8 @@ assemble(CompiledCode, Closures, Exports, Options) ->
hipe_sparc_assemble:assemble(CompiledCode, Closures, Exports, Options);
powerpc ->
hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
+ ppc64 ->
+ hipe_ppc_assemble:assemble(CompiledCode, Closures, Exports, Options);
arm ->
hipe_arm_assemble:assemble(CompiledCode, Closures, Exports, Options);
x86 ->
@@ -1048,7 +1049,7 @@ post(Res, Icode, Options) ->
%% --------------------------------------------------------------------
%% @doc Returns the current HiPE version as a string().
--spec version() -> string().
+-spec version() -> nonempty_string().
version() ->
?VERSION_STRING().
@@ -1390,6 +1391,8 @@ o1_opts() ->
Common;
powerpc ->
Common;
+ ppc64 ->
+ Common;
arm ->
Common -- [inline_fp]; % Pointless optimising for absent hardware
x86 ->
@@ -1411,6 +1414,8 @@ o2_opts() ->
Common;
powerpc ->
Common;
+ ppc64 ->
+ Common;
arm ->
Common;
x86 ->
@@ -1429,6 +1434,8 @@ o3_opts() ->
Common;
powerpc ->
Common;
+ ppc64 ->
+ Common;
arm ->
Common;
x86 ->
diff --git a/lib/hipe/main/hipe_main.erl b/lib/hipe/main/hipe_main.erl
index fe9bc83fd2..e81642fb33 100644
--- a/lib/hipe/main/hipe_main.erl
+++ b/lib/hipe/main/hipe_main.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% @doc This is the HiPE compiler's main "loop".
@@ -102,7 +102,7 @@ compile_icode(MFA, LinearIcode0, Options, Servers, DebugState) ->
?opt_start_timer("Icode"),
LinearIcode1 = icode_no_comment(LinearIcode0, Options),
IcodeCfg0 = icode_linear_to_cfg(LinearIcode1, Options),
- %%hipe_icode_cfg:pp(IcodeCfg1),
+ %% hipe_icode_cfg:pp(IcodeCfg0),
IcodeCfg1 = icode_handle_exceptions(IcodeCfg0, MFA, Options),
IcodeCfg3 = icode_inline_bifs(IcodeCfg1, Options),
pp(IcodeCfg3, MFA, icode, pp_icode, Options, Servers),
diff --git a/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl b/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl
index ac555b933c..6ba4ac814e 100644
--- a/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl
+++ b/lib/hipe/regalloc/hipe_graph_coloring_regalloc.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -389,23 +389,23 @@ decrement_each([N|Ns], OldLow, IG, Vis, K) ->
%% {Spilled_node, Low_degree_neighbors, New_interference_graph}
spill(IG, Vis, Spill, K, SpillLimit, Target) ->
- Ns = list_ig(IG),
- Costs = spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target),
- ?report3("spill costs are ~p~n",[Costs]),
- ActualCosts = lists:sort(Costs),
- ?report3("actual costs are ~p~n",[ActualCosts]),
+ Ns = list_ig(IG),
+ Costs = spill_costs(Ns, IG, Vis, Spill, SpillLimit, Target),
+ ?report3("spill costs are ~p~n", [Costs]),
+ ActualCosts = lists:sort(Costs),
+ ?report3("actual costs are ~p~n", [ActualCosts]),
case ActualCosts of
- [] ->
- ?error_msg("There is no node to spill",[]),
+ [] ->
+ ?error_msg("There is no node to spill", []),
?EXIT('no node to spill');
[{_Cost,N}|_] ->
{Low, NewIG} = decrement_neighbors(N, [], IG, Vis, K),
- %?report("spilled node ~p at cost ~p (~p now ready)~n",[N,Cost,Low]),
+ %% ?report("spilled node ~p at cost ~p (~p now ready)~n", [N,Cost,Low]),
{N, Low, NewIG}
end.
spill_costs([], _IG, _Vis, _Spill, _SpillLimit, _Target) ->
- [];
+ [];
spill_costs([{N,Info}|Ns], IG, Vis, Spill, SpillLimit, Target) ->
case degree(Info) of
0 -> spill_costs(Ns,IG,Vis,Spill, SpillLimit, Target);
@@ -451,28 +451,28 @@ select_colors([{X,colorable}|Xs], IG, Cols, PhysRegs, K) ->
{Reg,NewCols} = select_color(X, IG, Cols, PhysRegs),
?report("~p~n",[Reg]),
[{X,{reg,Reg}} | select_colors(Xs, IG, NewCols, PhysRegs, K)];
-%select_colors([{X,{spill,M}}|Xs], IG, Cols, PhysRegs, K) ->
-% ?report('spilled: ~p~n',[X]),
-% %% Check if optimistic coloring could have found a color
-% case catch select_color(X,IG,Cols,K) of
-% {'EXIT',_} -> % no color possible
-% ?report('(no optimistic color)~n',[]),
-% [{X,{spill,M}}|select_colors(Xs, IG, Cols, PhysRegs, K)];
-% {Reg,NewCols} ->
-% ?report('(optimistic color: ~p)~n',[Reg]),
-% [{X,{reg,Reg}}|select_colors(Xs, IG, Cols, PhysRegs, K)]
-% end.
+%%select_colors([{X,{spill,M}}|Xs], IG, Cols, PhysRegs, K) ->
+%% ?report('spilled: ~p~n',[X]),
+%% %% Check if optimistic coloring could have found a color
+%% case catch select_color(X,IG,Cols,K) of
+%% {'EXIT',_} -> % no color possible
+%% ?report('(no optimistic color)~n',[]),
+%% [{X,{spill,M}}|select_colors(Xs, IG, Cols, PhysRegs, K)];
+%% {Reg,NewCols} ->
+%% ?report('(optimistic color: ~p)~n',[Reg]),
+%% [{X,{reg,Reg}}|select_colors(Xs, IG, Cols, PhysRegs, K)]
+%% end.
%% Old code / pessimistic coloring:
select_colors([{X,{spill,M}}|Xs], IG, Cols, PhysRegs, K) ->
?report("spilled: ~p~n",[X]),
%% Check if optimistic coloring could have found a color
-% case catch select_color(X,IG,Cols,K) of
-% {'EXIT',_} -> % no color possible
-% ?report('(no optimistic color)~n',[]);
-% {Reg,NewCols} ->
-% ?report('(optimistic color: ~p)~n',[Reg])
-% end,
+%% case catch select_color(X,IG,Cols,K) of
+%% {'EXIT',_} -> % no color possible
+%% ?report('(no optimistic color)~n',[]);
+%% {Reg,NewCols} ->
+%% ?report('(optimistic color: ~p)~n',[Reg])
+%% end,
[{X,{spill,M}} | select_colors(Xs, IG, Cols, PhysRegs, K)].
select_color(X, IG, Cols, PhysRegs) ->
diff --git a/lib/hipe/rtl/hipe_rtl.erl b/lib/hipe/rtl/hipe_rtl.erl
index ef06b2abf8..29e9c8c8fe 100644
--- a/lib/hipe/rtl/hipe_rtl.erl
+++ b/lib/hipe/rtl/hipe_rtl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -354,6 +354,8 @@
phi_arglist_update/2,
phi_redirect_pred/3]).
+-export_type([alub_cond/0]).
+
%%
%% RTL
%%
@@ -590,6 +592,9 @@ branch_pred(#branch{p=P}) -> P.
%% alub
%%
+-type alub_cond() :: 'eq' | 'ne' | 'ge' | 'geu' | 'gt' | 'gtu' | 'le'
+ | 'leu' | 'lt' | 'ltu' | 'overflow' | 'not_overflow'.
+
mk_alub(Dst, Src1, Op, Src2, Cond, True, False) ->
mk_alub(Dst, Src1, Op, Src2, Cond, True, False, 0.5).
mk_alub(Dst, Src1, Op, Src2, Cond, True, False, P) ->
diff --git a/lib/hipe/rtl/hipe_rtl_arith.inc b/lib/hipe/rtl/hipe_rtl_arith.inc
index 31fedd927e..e608506234 100644
--- a/lib/hipe/rtl/hipe_rtl_arith.inc
+++ b/lib/hipe/rtl/hipe_rtl_arith.inc
@@ -3,7 +3,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -119,7 +119,8 @@ eval_alu(Op, Arg1, Arg2) ->
%% there are cases where we can evaluate a subset of the bits, but can
%% not do a full eval-alub call (eg. a + 0 gives no carry)
%%
--spec eval_cond_bits(atom(), boolean(), boolean(), boolean(), boolean()) -> boolean().
+-spec eval_cond_bits(hipe_rtl:alub_cond(), boolean(),
+ boolean(), boolean(), boolean()) -> boolean().
eval_cond_bits(Cond, N, Z, V, C) ->
case Cond of
@@ -146,9 +147,7 @@ eval_cond_bits(Cond, N, Z, V, C) ->
'overflow' ->
V;
'not_overflow' ->
- not V;
- _ ->
- ?EXIT({'condition code not handled',Cond})
+ not V
end.
eval_alub(Op, Cond, Arg1, Arg2) ->
diff --git a/lib/hipe/rtl/hipe_rtl_primops.erl b/lib/hipe/rtl/hipe_rtl_primops.erl
index 560e0259f8..5f273d8251 100644
--- a/lib/hipe/rtl/hipe_rtl_primops.erl
+++ b/lib/hipe/rtl/hipe_rtl_primops.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -701,9 +701,9 @@ gen_cons(Dst, [Arg1, Arg2]) ->
%% Increase refcount
%% fe->refc++;
%%
-%% Link to the process off_heap.funs list
-%% funp->next = p->off_heap.funs;
-%% p->off_heap.funs = funp;
+%% Link to the process off_heap list
+%% funp->next = p->off_heap.first;
+%% p->off_heap.first = funp;
%%
%% Tag the thing
%% return make_fun(funp);
@@ -729,9 +729,9 @@ gen_mkfun([Dst], {_Mod, _FunId, _Arity} = MFidA, MagicNr, Index, FreeVars) ->
%% fe->refc++;
SkeletonCode = gen_fun_thing_skeleton(HP, MFidA, NumFree, MagicNr, Index),
- %% Link to the process off_heap.funs list
- %% funp->next = p->off_heap.funs;
- %% p->off_heap.funs = funp;
+ %% Link to the process off_heap list
+ %% funp->next = p->off_heap.first;
+ %% p->off_heap.first = funp;
LinkCode = gen_link_closure(HP),
%% Tag the thing and increase the heap_pointer.
@@ -797,14 +797,14 @@ gen_link_closure(FUNP) ->
end.
gen_link_closure_private(FUNP) ->
- %% Link to the process off_heap.funs list
- %% funp->next = p->off_heap.funs;
- %% p->off_heap.funs = funp;
+ %% Link fun to the process off_heap list
+ %% funp->next = p->off_heap.first;
+ %% p->off_heap.first = funp;
FunsVar = hipe_rtl:mk_new_reg(),
- [load_p_field(FunsVar,?P_OFF_HEAP_FUNS),
+ [load_p_field(FunsVar,?P_OFF_HEAP_FIRST),
hipe_rtl:mk_store(FUNP, hipe_rtl:mk_imm(?EFT_NEXT), FunsVar),
- store_p_field(FUNP,?P_OFF_HEAP_FUNS)].
+ store_p_field(FUNP,?P_OFF_HEAP_FIRST)].
gen_link_closure_non_private(_FUNP) -> [].
diff --git a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl
index 76c0a88933..194cf29b64 100644
--- a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl
+++ b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -93,8 +93,6 @@
-include("../ssa/hipe_ssa_const_prop.inc").
-type bool_lattice() :: 'true' | 'false' | 'top' | 'bottom'.
--type conditional() :: 'eq' | 'ne' | 'ge' | 'geu' | 'gt' | 'gtu' | 'le'
- | 'leu' | 'lt' | 'ltu' | 'overflow' | 'not_overflow'.
%%-----------------------------------------------------------------------------
%% Procedure : visit_expression/2
@@ -400,7 +398,7 @@ maybe_top_or_bottom([top | Rest], _) -> maybe_top_or_bottom(Rest, top);
maybe_top_or_bottom([bottom | _], _) -> bottom;
maybe_top_or_bottom([_ | Rest], TB) -> maybe_top_or_bottom(Rest, TB).
--spec partial_eval_branch(conditional(), bool_lattice(), bool_lattice(),
+-spec partial_eval_branch(hipe_rtl:alub_cond(), bool_lattice(), bool_lattice(),
bool_lattice() | 0, bool_lattice() | 0) ->
bool_lattice().
partial_eval_branch(Cond, N0, Z0, V0, C0) ->
@@ -441,14 +439,14 @@ visit_alub(Inst, Env) ->
hipe_rtl:alub_false_label(Inst)];
top -> [];
_ ->
- %if the partial branch cannot be evaluated we must execute the
- % instruction at runtime.
+ %% if the partial branch cannot be evaluated we must execute the
+ %% instruction at runtime.
case partial_eval_branch(hipe_rtl:alub_cond(Inst), N, Z, C, V) of
bottom -> [hipe_rtl:alub_true_label(Inst),
hipe_rtl:alub_false_label(Inst)];
top -> [];
- true -> [hipe_rtl:alub_true_label(Inst) ];
- false -> [hipe_rtl:alub_false_label(Inst) ]
+ true -> [hipe_rtl:alub_true_label(Inst)];
+ false -> [hipe_rtl:alub_false_label(Inst)]
end
end,
{[], NewSSA, NewEnv} = set_to(hipe_rtl:alub_dst(Inst), NewVal, Env),
@@ -944,8 +942,8 @@ update_branch(Inst, Env) ->
%% some small helpers.
alub_to_move(Inst, Res, Lab) ->
- [ hipe_rtl:mk_move(hipe_rtl:alub_dst(Inst), Res),
- hipe_rtl:mk_goto(Lab) ].
+ [hipe_rtl:mk_move(hipe_rtl:alub_dst(Inst), Res),
+ hipe_rtl:mk_goto(Lab)].
make_alub_subst_list(bottom, _, Tail) -> Tail;
make_alub_subst_list(top, Src, _) ->
@@ -970,13 +968,13 @@ update_alub(Inst, Env) ->
%% move and the branch. We can however replace variable with constants:
S1 = make_alub_subst_list(Val1, Src1, []),
S2 = make_alub_subst_list(Val2, Src2, S1),
- [ hipe_rtl:subst_uses(S2, Inst) ];
- _ -> % we know where we will be going, let's find out what Dst should be.
- % knowing where we are going means that at most one of the values is
- % bottom, hence we can replace the alu-instr with a move.
- % remember, a = b + 0 can give us enough info to know what jump to
- % do without knowing the value of a. (I wonder if this will ever
- % actualy happen ;)
+ [hipe_rtl:subst_uses(S2, Inst)];
+ _ -> %% we know where we will be going, let's find out what Dst should be.
+ %% knowing where we are going means that at most one of the values is
+ %% bottom, hence we can replace the alu-instr with a move.
+ %% remember, a = b + 0 can give us enough info to know what jump to
+ %% do without knowing the value of a. (I wonder if this will ever
+ %% actualy happen ;)
Res = case ResVal of
bottom -> % something nonconstant.
if (Val1 =:= bottom) -> Src1;
@@ -985,11 +983,12 @@ update_alub(Inst, Env) ->
_ -> hipe_rtl:mk_imm(ResVal)
end,
case CondRes of
- top -> io:format("oops. something VERY bad: ~w ~w V1 & 2 ~w ~w\n",
- [Inst, {ResVal, N, Z, C, V} , Val1, Val2]),
- [Inst ];
- true -> alub_to_move(Inst, Res, hipe_rtl:alub_true_label(Inst));
- false -> alub_to_move(Inst, Res, hipe_rtl:alub_false_label(Inst))
+ top ->
+ io:format("oops. something VERY bad: ~w ~w V1 & 2 ~w ~w\n",
+ [Inst, {ResVal, N, Z, C, V} , Val1, Val2]),
+ [Inst];
+ true -> alub_to_move(Inst, Res, hipe_rtl:alub_true_label(Inst));
+ false -> alub_to_move(Inst, Res, hipe_rtl:alub_false_label(Inst))
end
end.
@@ -1050,7 +1049,7 @@ update_phi(Instruction, Environment) ->
%%-----------------------------------------------------------------------------
-%% make sure that all precoloured rgisters are taken out of the equation.
+%% make sure that all precoloured registers are taken out of the equation.
lookup_lattice_value(X, Environment) ->
case hipe_rtl_arch:is_precoloured(X) or hipe_rtl:is_const_label(X) of
true ->
diff --git a/lib/hipe/rtl/hipe_tagscheme.erl b/lib/hipe/rtl/hipe_tagscheme.erl
index dc44b803a1..5859c345d0 100644
--- a/lib/hipe/rtl/hipe_tagscheme.erl
+++ b/lib/hipe/rtl/hipe_tagscheme.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -849,9 +849,9 @@ create_refc_binary(Base, Size, Flags, Dst) ->
heap_arch_spec(HP) ->
Tmp1 = hipe_rtl:mk_new_reg(), % MSO state
- [hipe_rtl_arch:pcb_load(Tmp1, ?P_OFF_HEAP_MSO),
+ [hipe_rtl_arch:pcb_load(Tmp1, ?P_OFF_HEAP_FIRST),
set_field_from_pointer({proc_bin, next}, HP, Tmp1),
- hipe_rtl_arch:pcb_store(?P_OFF_HEAP_MSO, HP)].
+ hipe_rtl_arch:pcb_store(?P_OFF_HEAP_FIRST, HP)].
test_heap_binary(Binary, TrueLblName, FalseLblName) ->
Tmp1 = hipe_rtl:mk_new_reg_gcsafe(),
diff --git a/lib/hipe/tools/hipe_tool.erl b/lib/hipe/tools/hipe_tool.erl
index a1bd79895d..990805ceca 100644
--- a/lib/hipe/tools/hipe_tool.erl
+++ b/lib/hipe/tools/hipe_tool.erl
@@ -56,9 +56,9 @@
-record(state, {win_created = false :: boolean(),
mindex = 0 :: integer(),
- mod :: module(),
+ mod :: atom(),
funs = [] :: [fa()],
- mods = [] :: [module()],
+ mods = [] :: [atom()],
options = [o2] :: comp_options(),
compiling = false :: 'false' | pid()
}).
@@ -291,8 +291,7 @@ update_code_listbox(State) ->
integer_to_list(length(Mods))++")"),
catch gs:config(code_listbox, [{data, Mods},
{items, Mods},
- {selection, 0}
- ]),
+ {selection, 0}]),
update_module_box(State#state{mods = Mods}, 0, Mods, "")
end
end.
@@ -367,7 +366,7 @@ update_text(Lab, Text) ->
%% @doc Returns a list of all loaded modules.
%%---------------------------------------------------------------------
--spec mods() -> [module()].
+-spec mods() -> [atom()].
mods() ->
[Mod || {Mod,_File} <- code:all_loaded()].
@@ -382,25 +381,26 @@ funs(Mod) ->
native_code(Mod) ->
Mod:module_info(native_addresses).
--spec mfas(module(), [fa()]) -> [mfa()].
+-spec mfas(atom(), [fa()]) -> [mfa()].
mfas(Mod, Funs) ->
[{Mod,F,A} || {F,A} <- Funs].
--spec fun_names(module(), [fa()], [fa_address()], boolean()) -> string().
+-spec fun_names(atom(), [fa()], [fa_address()], boolean()) -> [string()].
fun_names(M, Funs, NativeCode, Prof) ->
- [list_to_atom(atom_to_list(F) ++ "/" ++ integer_to_list(A) ++
- (case in_native(F, A, NativeCode) of
- true -> " [native] ";
- false -> ""
- end)
- ++
- if Prof ->
- (catch integer_to_list(hipe_bifs:call_count_get({M,F,A})));
- true -> ""
- end) ||
- {F,A} <- Funs].
+ [atom_to_list(F) ++ "/" ++ integer_to_list(A)
+ ++
+ (case in_native(F, A, NativeCode) of
+ true -> " [native] ";
+ false -> ""
+ end)
+ ++
+ if Prof ->
+ (catch integer_to_list(hipe_bifs:call_count_get({M,F,A})));
+ true -> ""
+ end
+ || {F,A} <- Funs].
-spec in_native(atom(), arity(), [fa_address()]) -> boolean().
@@ -461,7 +461,7 @@ get_compile(Info) ->
false -> []
end.
--spec is_profiled(module()) -> boolean().
+-spec is_profiled(atom()) -> boolean().
is_profiled(Mod) ->
case hipe_bifs:call_count_get({Mod,module_info,0}) of
@@ -478,7 +478,7 @@ compile(State) ->
P = spawn(fun() -> c(Parent, State#state.mod, State#state.options) end),
State#state{compiling = P}.
--spec c(pid(), module(), comp_options()) -> 'ok'.
+-spec c(pid(), atom(), comp_options()) -> 'ok'.
c(Parent, Mod, Options) ->
Res = hipe:c(Mod, Options),
diff --git a/lib/hipe/util/hipe_digraph.erl b/lib/hipe/util/hipe_digraph.erl
index a62e913fe5..fcfaa64684 100644
--- a/lib/hipe/util/hipe_digraph.erl
+++ b/lib/hipe/util/hipe_digraph.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%-----------------------------------------------------------------------
@@ -30,6 +30,8 @@
from_list/1, to_list/1, get_parents/2, get_children/2]).
-export([reverse_preorder_sccs/1]).
+-export_type([hdg/0]).
+
%%------------------------------------------------------------------------
-type ordset(T) :: [T]. % XXX: temporarily
diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk
index 129718a305..6ba9009a24 100644
--- a/lib/hipe/vsn.mk
+++ b/lib/hipe/vsn.mk
@@ -1 +1 @@
-HIPE_VSN = 3.7.5
+HIPE_VSN = 3.7.9
diff --git a/lib/hipe/x86/hipe_x86_spill_restore.erl b/lib/hipe/x86/hipe_x86_spill_restore.erl
index e60c446e17..cd927669fb 100644
--- a/lib/hipe/x86/hipe_x86_spill_restore.erl
+++ b/lib/hipe/x86/hipe_x86_spill_restore.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% ====================================================================
@@ -71,9 +71,9 @@ firstPass(Defun) ->
case hipe_x86_cfg:reverse_postorder(CFG0) of
[Label1, Label2|_] ->
SaveTreeElement = saveTreeLookup(Label2, SaveTree),
- %% FilteredSaveTreeElement is the to be spilled temps around the function call.
- %% They are spilled just before move formals
- FilteredSaveTreeElement = [Temp || Temp <- SaveTreeElement, temp_is_pseudo(Temp)],
+ %% FilteredSaveTreeElement is the to be spilled temps around the
+ %% function call. They are spilled just before move formals.
+ FilteredSaveTreeElement = [T || T <- SaveTreeElement, temp_is_pseudo(T)],
Block = hipe_x86_cfg:bb(CFG1, Label1),
Code = hipe_bb:code(Block),
%% The following statements are tedious but work ok.
@@ -83,7 +83,7 @@ firstPass(Defun) ->
%% Another solution may be to introduce another block.
MoveCodes = lists:sublist(Code, length(Code)-1),
JumpCode = lists:last(Code),
- hipe_x86_cfg:bb_add(CFG1, Label1, hipe_bb:mk_bb(MoveCodes ++ [hipe_x86:mk_pseudo_spill(FilteredSaveTreeElement)] ++ [JumpCode]));
+ hipe_x86_cfg:bb_add(CFG1, Label1, hipe_bb:mk_bb(MoveCodes ++ [hipe_x86:mk_pseudo_spill(FilteredSaveTreeElement), JumpCode]));
_ ->
CFG1
end.
@@ -110,13 +110,12 @@ firstPassHelper([Label|Labels], Liveness, CFG, SaveTree) ->
NewBlock = hipe_bb:code_update(Block, NewCode),
NewCFG = hipe_x86_cfg:bb_add(CFG, Label, NewBlock),
SizeOfSet = setSize(NewIntersectedList),
-
%% if the Intersected Save List is not empty, insert it in the save tree.
if SizeOfSet =/= 0 ->
- UpdatedSaveTree = gb_trees:insert(Label,NewIntersectedList,SaveTree),
- firstPassHelper(Labels, Liveness, NewCFG,UpdatedSaveTree);
+ UpdatedSaveTree = gb_trees:insert(Label, NewIntersectedList, SaveTree),
+ firstPassHelper(Labels, Liveness, NewCFG, UpdatedSaveTree);
true ->
- firstPassHelper(Labels, Liveness, NewCFG,SaveTree)
+ firstPassHelper(Labels, Liveness, NewCFG, SaveTree)
end;
firstPassHelper([], _, CFG, SaveTree) ->
{CFG, SaveTree}.
@@ -125,17 +124,15 @@ firstPassHelper([], _, CFG, SaveTree) ->
firstPassDoBlock(Insts, LiveOut, IntersectedSaveList) ->
lists:foldr(fun firstPassDoInsn/2, {LiveOut,IntersectedSaveList,[]}, Insts).
-firstPassDoInsn(I, {LiveOut,IntersectedSaveList,PrevInsts} ) ->
+firstPassDoInsn(I, {LiveOut,IntersectedSaveList,PrevInsts}) ->
case I of
#pseudo_call{} ->
do_pseudo_call(I, {LiveOut,IntersectedSaveList,PrevInsts});
_ -> % other instructions
DefinedList = from_list( ?HIPE_X86_LIVENESS:defines(I)),
UsedList = from_list(?HIPE_X86_LIVENESS:uses(I)),
-
NewLiveOut = subtract(union(LiveOut, UsedList), DefinedList),
- NewIntersectedSaveList = subtract(IntersectedSaveList, DefinedList),
-
+ NewIntersectedSaveList = subtract(IntersectedSaveList, DefinedList),
{NewLiveOut, NewIntersectedSaveList, [I|PrevInsts]}
end.
@@ -162,7 +159,7 @@ saveTreeLookup(Label, SaveTree) ->
[]
end.
-%% Performs the second pass of the algoritm.
+%% Performs the second pass of the algorithm.
%% It basically eliminates the unnecessary spills and introduces restores.
%% Works top down
secondPass(CFG0) ->
@@ -306,7 +303,8 @@ addRestoreBlockToEdge(PseudoCall, ContLabel, CFG, TempArgsList) ->
NewCFG = hipe_x86_cfg:bb_add(CFG, NextLabel, NewBlock),
{NewCFG, NewPseudoCall}.
-%% used instead of hipe_x86_cfg:redirect_jmp since it does not handle pseudo_call calls.
+%% used instead of hipe_x86_cfg:redirect_jmp since it does not handle
+%% pseudo_call calls.
redirect_pseudo_call(I = #pseudo_call{contlab=ContLabel}, Old, New) ->
case Old =:= ContLabel of
true -> I#pseudo_call{contlab=New};
@@ -323,8 +321,8 @@ temp_is_pseudo(Temp) ->
%% Set operations where the module name is an easily changeable macro
%%---------------------------------------------------------------------
-union(Set1,Set2) ->
- ?SET_MODULE:union(Set1,Set2).
+union(Set1, Set2) ->
+ ?SET_MODULE:union(Set1, Set2).
setSize(Set) ->
?SET_MODULE:size(Set).
diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile
index 26d0932a95..8eda436a24 100644
--- a/lib/ic/doc/src/Makefile
+++ b/lib/ic/doc/src/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1998-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1998-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -211,7 +211,11 @@ $(HTMLDIR)/%.gif: %.gif
ifdef DOCSUPPORT
+ifneq (,$(JAVA))
docs: pdf html man $(JAVADOC_GENERATED_FILES)
+else
+docs: pdf html man
+endif
$(TOP_PDF_FILE): $(XML_FILES)
@@ -301,6 +305,7 @@ release_docs_spec: docs
$(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
$(RELSYSDIR)/doc/html
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ifneq (,$(JAVA))
$(INSTALL_DIR) $(RELSYSDIR)/doc/html/java
$(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/resources
$(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/com
@@ -313,6 +318,7 @@ release_docs_spec: docs
$(RELSYSDIR)/doc/html/java/resources
$(INSTALL_DATA) $(JAVADOC_PACK_HTML_FILES) \
$(RELSYSDIR)/doc/html/java/com/ericsson/otp/ic
+endif
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml
index dbafde7b4b..5f6c31069c 100644
--- a/lib/ic/doc/src/notes.xml
+++ b/lib/ic/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1998</year><year>2009</year>
+ <year>1998</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>IDL Compiler Release Notes</title>
@@ -31,6 +31,42 @@
</header>
<section>
+ <title>IC 4.2.26</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Partial support for recursive structs and unions. Only available
+ for the erl_corba backend and requires that Light IFR is used.
+ I.e. the IC option {light_ifr, true} and that Orber is configured
+ in such a way that Light IFR is activated. Recursive TypeCode is
+ currently not supported.</p>
+ <p>
+ Own Id: OTP-8868 Aux Id: seq11633</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>IC 4.2.25</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ The documentation can now be built and installed without Java.</p>
+ <p>
+ Own Id: OTP-8639 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
<title>IC 4.2.24</title>
<section>
diff --git a/lib/ic/src/ic_forms.erl b/lib/ic/src/ic_forms.erl
index 7409ddeb7b..fc46a2ed40 100644
--- a/lib/ic/src/ic_forms.erl
+++ b/lib/ic/src/ic_forms.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -65,6 +65,7 @@ get_line(X) when is_record(X, scoped_id) -> X#scoped_id.line;
get_line(X) when is_record(X, module) -> get_line(X#module.id);
get_line(X) when is_record(X, interface) -> get_line(X#interface.id);
get_line(X) when is_record(X, forward) -> get_line(X#forward.id);
+get_line(X) when is_record(X, constr_forward) -> get_line(X#constr_forward.id);
get_line(X) when is_record(X, const) -> get_line(X#const.id);
get_line(X) when is_record(X, typedef) -> get_line(X#typedef.id);
get_line(X) when is_record(X, struct) -> get_line(X#struct.id);
@@ -114,6 +115,7 @@ get_line(_) -> -1.
get_id2(X) when is_record(X, module) -> get_id(X#module.id);
get_id2(X) when is_record(X, interface) -> get_id(X#interface.id);
get_id2(X) when is_record(X, forward) -> get_id(X#forward.id);
+get_id2(X) when is_record(X, constr_forward) -> get_id(X#constr_forward.id);
get_id2(X) when is_record(X, const) -> get_id(X#const.id);
get_id2(X) when is_record(X, typedef) -> get_id(hd(X#typedef.id));
get_id2(X) when is_record(X, struct) -> get_id(X#struct.id);
@@ -156,6 +158,7 @@ get_type(X) when is_record(X, param) -> X#param.type.
%% Temporary place
get_tk(X) when is_record(X, interface) -> X#interface.tk;
get_tk(X) when is_record(X, forward) -> X#forward.tk;
+get_tk(X) when is_record(X, constr_forward) -> X#constr_forward.tk;
get_tk(X) when is_record(X, const) -> X#const.tk;
get_tk(X) when is_record(X, type_dcl) -> X#type_dcl.tk;
get_tk(X) when is_record(X, typedef) -> X#typedef.tk;
@@ -228,6 +231,7 @@ clean_up_scope([N|Ns],Found) ->
get_type_code2(_, _, X) when is_record(X, interface) -> X#interface.tk;
get_type_code2(_, _, X) when is_record(X, forward) -> X#forward.tk;
+get_type_code2(_, _, X) when is_record(X, constr_forward) -> X#constr_forward.tk;
get_type_code2(_, _, X) when is_record(X, const) -> X#const.tk;
get_type_code2(_, _, X) when is_record(X, type_dcl) -> X#type_dcl.tk;
get_type_code2(_, _, X) when is_record(X, typedef) ->
diff --git a/lib/ic/src/ic_pragma.erl b/lib/ic/src/ic_pragma.erl
index 9165e3b03b..45cb64c9c8 100644
--- a/lib/ic/src/ic_pragma.erl
+++ b/lib/ic/src/ic_pragma.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -60,7 +60,7 @@ pragma_reg(G,X) ->
init_pragma_status(S),
registerOptions(G,S),
pragma_reg_all(G, S, [], X),
- denote_specific_code_opts(G), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ denote_specific_code_opts(G),
case get_pragma_compilation_status(S) of
true ->
%% Remove ugly pragmas from form
@@ -132,6 +132,7 @@ applyCodeOpt(G) ->
%% This removes all pragma records from the form.
%% When debugged, it can be enbodied in pragma_reg_all.
+cleanup(undefined,C) -> C;
cleanup([],C) -> C;
cleanup([X|Xs],CSF) ->
cleanup(Xs, CSF++cleanup(X)).
@@ -279,7 +280,12 @@ pragma_reg(G, S, N, X) when is_record(X, union) ->
pragma_reg(G, S, N, X) when is_record(X, struct) ->
mk_ref(G,[get_id2(X) | N],struct_ref),
mk_file_data(G,X,N,struct),
- pragma_reg_all(G, S, N, X#struct.body);
+ case X#struct.body of
+ undefined ->
+ ok;
+ _ ->
+ pragma_reg_all(G, S, N, X#struct.body)
+ end;
pragma_reg(G, _S, N, X) when is_record(X, attr) ->
XX = #id_of{type=X},
diff --git a/lib/ic/src/ic_symtab.erl b/lib/ic/src/ic_symtab.erl
index 889c75e3a2..d710154a5d 100644
--- a/lib/ic/src/ic_symtab.erl
+++ b/lib/ic/src/ic_symtab.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -69,6 +69,8 @@ store(G, N, X) ->
ets:insert(G#genobj.symtab, {Name, X});
{ok, Y} when is_record(Y, forward) ->
ets:insert(G#genobj.symtab, {Name, X});
+ {ok, Y} when is_record(Y, constr_forward) ->
+ ets:insert(G#genobj.symtab, {Name, X});
{ok, _Y} ->
ic_error:error(G, {multiply_defined, X})
end.
diff --git a/lib/ic/src/icforms.hrl b/lib/ic/src/icforms.hrl
index d1869e6330..1b394a11b4 100644
--- a/lib/ic/src/icforms.hrl
+++ b/lib/ic/src/icforms.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@
-record(module, {id, body}).
-record(interface, {id, inherit, body, inherit_body, tk}).
-record(forward, {id, tk}).
+-record(constr_forward, {id, tk}).
-record(const, {type, id, val, tk}).
-record(type_dcl, {type, tk}).
-record(typedef, {type, id, tk}).
diff --git a/lib/ic/src/icparse.yrl b/lib/ic/src/icparse.yrl
index 25b0f452e7..d0dd6cde4c 100644
--- a/lib/ic/src/icparse.yrl
+++ b/lib/ic/src/icparse.yrl
@@ -1,21 +1,20 @@
-%%<copyright>
-%% <year>1997-2007</year>
-%% <holder>Ericsson AB, All Rights Reserved</holder>
-%%</copyright>
-%%<legalnotice>
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson AB.
-%%</legalnotice>
+%%
+%% %CopyrightEnd%
%%
%%------------------------------------------------------------
%% Yecc spec for IDL
@@ -150,6 +149,7 @@ Nonterminals
'ZorM_<integer_literal>'
'<fixed_pt_type>'
'<fixed_pt_const_type>'
+ '<constr_forward_decl>'
.
@@ -473,6 +473,7 @@ OE_preproc -> '#' '<integer_literal>' '<string_literal>'
'<type_dcl>' -> '<struct_type>' : '$1' .
'<type_dcl>' -> '<union_type>' : '$1' .
'<type_dcl>' -> '<enum_type>' : '$1' .
+'<type_dcl>' -> '<constr_forward_decl>' : '$1' .
%% (28) NIY multiple declarators (FIXED)
'<type_declarator>' -> '<type_spec>' '<declarators>'
@@ -832,6 +833,9 @@ OE_preproc -> '#' '<integer_literal>' '<string_literal>'
'<fixed_pt_type>' -> 'fixed' '<' '<positive_int_const>' ',' '<positive_int_const>' '>'
: #fixed{digits='$3',scale='$5'} .
+%% (99)
+'<constr_forward_decl>' -> 'struct' '<identifier>' : #constr_forward{id='$2', tk=tk_struct} .
+'<constr_forward_decl>' -> 'union' '<identifier>' : #constr_forward{id='$2', tk=tk_union} .
%% Added clause
'ZorM_<string_literal>' -> '$empty' : [] .
diff --git a/lib/ic/src/ictype.erl b/lib/ic/src/ictype.erl
index 4704191bee..9e20801464 100644
--- a/lib/ic/src/ictype.erl
+++ b/lib/ic/src/ictype.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -407,6 +407,18 @@ check(G, S, N, X) when is_record(X, forward) ->
tktab_add(G, S, N, X, {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}),
X;
+check(G, S, N, #constr_forward{tk = tk_struct} = X) ->
+ ?STDDBG,
+ ID = ic_forms:get_id2(X),
+ Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")),
+ tktab_add(G, S, N, X, {tk_struct, ictk:get_IR_ID(G, N, X), ID, Module}),
+ X;
+check(G, S, N, #constr_forward{tk = tk_union} = X) ->
+ ?STDDBG,
+ ID = ic_forms:get_id2(X),
+ Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")),
+ tktab_add(G, S, N, X, {tk_union, ictk:get_IR_ID(G, N, X), ID, [], [], Module}),
+ X;
check(G, S, N, X) when is_record(X, const) ->
?STDDBG,
@@ -427,21 +439,6 @@ check(G, S, N, X) when is_record(X, const) ->
end
end;
-check(G, S, N, X) when is_record(X, const) ->
- ?STDDBG,
- case tk_base(G, S, N, ic_forms:get_type(X)) of
- Err when element(1, Err) == error -> X;
- TK ->
- check_const_tk(G, S, N, X, TK),
- case iceval:eval_const(G, S, N, TK, X#const.val) of
- Err when element(1, Err) == error -> X;
- Val ->
- V = iceval:get_val(Val),
- tktab_add(G, S, N, X, TK, V),
- X#const{val=V, tk=TK}
- end
- end;
-
check(G, S, N, X) when is_record(X, except) ->
?STDDBG,
TK = tk(G, S, N, X),
@@ -795,9 +792,15 @@ tktab_add_id(G, S, N, X, Id, TK, Aux) ->
Name = [Id | N],
UName = mk_uppercase(Name),
case ets:lookup(S, Name) of
- [{_, forward, _, _}] when is_record(X, interface) -> ok;
- [XX] when is_record(X, forward) andalso element(2, XX)==interface -> ok;
- [_] -> ic_error:error(G, {multiply_defined, X});
+ [{_, forward, _, _}] when is_record(X, interface) ->
+ ok;
+ [{_, constr_forward, _, _}] when is_record(X, union) orelse
+ is_record(X, struct) ->
+ ok;
+ [XX] when is_record(X, forward) andalso element(2, XX)==interface ->
+ ok;
+ [_] ->
+ ic_error:error(G, {multiply_defined, X});
[] ->
case ets:lookup(S, UName) of
[] -> ok;
diff --git a/lib/ic/test/Makefile b/lib/ic/test/Makefile
new file mode 100644
index 0000000000..1d90a1bc17
--- /dev/null
+++ b/lib/ic/test/Makefile
@@ -0,0 +1,277 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1998-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(IC_VSN)
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/ic_test
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+TEST_SPEC_FILE = ic.spec
+
+
+IDL_FILES =
+
+COMPILER_TEST_FILES = \
+ ic_SUITE_data/Corba.idl \
+ ic_SUITE_data/Coss.idl \
+ ic_SUITE_data/attr.idl \
+ ic_SUITE_data/c_err1.idl \
+ ic_SUITE_data/c_err2.idl \
+ ic_SUITE_data/c_err3.idl \
+ ic_SUITE_data/c_norm.idl \
+ ic_SUITE_data/enum.idl \
+ ic_SUITE_data/forward.idl \
+ ic_SUITE_data/include.idl \
+ ic_SUITE_data/include2.idl \
+ ic_SUITE_data/include3.idl \
+ ic_SUITE_data/inherit.idl \
+ ic_SUITE_data/inherit_err.idl \
+ ic_SUITE_data/inherit_warn.idl \
+ ic_SUITE_data/mult_ids.idl \
+ ic_SUITE_data/nasty.idl \
+ ic_SUITE_data/one.idl \
+ ic_SUITE_data/one_out.idl \
+ ic_SUITE_data/one_raises.idl \
+ ic_SUITE_data/one_followed.idl \
+ ic_SUITE_data/one_void.idl \
+ ic_SUITE_data/raises_reg.idl \
+ ic_SUITE_data/struct.idl \
+ ic_SUITE_data/syntax1.idl \
+ ic_SUITE_data/syntax2.idl \
+ ic_SUITE_data/syntax3.idl \
+ ic_SUITE_data/syntax4.idl \
+ ic_SUITE_data/syntax5.idl \
+ ic_SUITE_data/syntax6.idl \
+ ic_SUITE_data/type.idl \
+ ic_SUITE_data/typeid.idl \
+ ic_SUITE_data/u_case_mult.idl \
+ ic_SUITE_data/u_mult.idl \
+ ic_SUITE_data/u_norm.idl \
+ ic_SUITE_data/u_type.idl \
+ ic_SUITE_data/u_default.idl \
+ ic_SUITE_data/undef_id.idl
+
+
+COMPILER_TEST_FILES2 = \
+ ic_register_SUITE_data/reg_m8.idl \
+ ic_register_SUITE_data/reg_m9.idl \
+ ic_register_SUITE_data/reg_m10.idl \
+ ic_register_SUITE_data/reg_m11.idl \
+ ic_register_SUITE_data/reg_m12.idl
+
+
+COMPILER_TEST_FILES3 = \
+ ic_pragma_SUITE_data/reg_m0.idl \
+ ic_pragma_SUITE_data/reg_m1.idl \
+ ic_pragma_SUITE_data/reg_m2.idl \
+ ic_pragma_SUITE_data/reg_m3.idl \
+ ic_pragma_SUITE_data/reg_m4.idl \
+ ic_pragma_SUITE_data/reg_m5.idl \
+ ic_pragma_SUITE_data/reg_m6.idl \
+ ic_pragma_SUITE_data/reg_m7.idl \
+ ic_pragma_SUITE_data/uggly.idl
+
+
+COMPILER_TEST_FILES4 = \
+ ic_be_SUITE_data/plain.idl
+
+
+PREPROCESSOR_TEST_FILES = \
+ ic_pp_SUITE_data/arg.idl \
+ ic_pp_SUITE_data/cascade.idl \
+ ic_pp_SUITE_data/comment.idl \
+ ic_pp_SUITE_data/concat.idl \
+ ic_pp_SUITE_data/define.idl \
+ ic_pp_SUITE_data/if.idl \
+ ic_pp_SUITE_data/if_zero.idl \
+ ic_pp_SUITE_data/improp_nest_constr.idl \
+ ic_pp_SUITE_data/inc.idl \
+ ic_pp_SUITE_data/line.idl \
+ ic_pp_SUITE_data/misc.idl \
+ ic_pp_SUITE_data/nopara.idl \
+ ic_pp_SUITE_data/predef.idl \
+ ic_pp_SUITE_data/predef_time.idl \
+ ic_pp_SUITE_data/self_ref.idl \
+ ic_pp_SUITE_data/separate.idl \
+ ic_pp_SUITE_data/swallow_sc.idl \
+ ic_pp_SUITE_data/unintended_grp.idl
+
+C_CLIENT_ERL_SERVER_TEST_FILES = \
+ c_client_erl_server_SUITE_data/Makefile.src \
+ c_client_erl_server_SUITE_data/c_erl_test.idl \
+ c_client_erl_server_SUITE_data/c_client.c \
+ c_client_erl_server_SUITE_data/m_i_impl.erl
+
+C_CLIENT_ERL_SERVER_PROTO_TEST_FILES = \
+ c_client_erl_server_proto_SUITE_data/Makefile.src \
+ c_client_erl_server_proto_SUITE_data/c_erl_test.idl \
+ c_client_erl_server_proto_SUITE_data/c_client.c \
+ c_client_erl_server_proto_SUITE_data/my.c \
+ c_client_erl_server_proto_SUITE_data/m_i_impl.erl
+
+C_CLIENT_ERL_SERVER_PROTO_TMO_TEST_FILES = \
+ c_client_erl_server_proto_tmo_SUITE_data/Makefile.src \
+ c_client_erl_server_proto_tmo_SUITE_data/c_erl_test.idl \
+ c_client_erl_server_proto_tmo_SUITE_data/c_client.c \
+ c_client_erl_server_proto_tmo_SUITE_data/my.c \
+ c_client_erl_server_proto_tmo_SUITE_data/m_i_impl.erl
+
+ERL_CLIENT_C_SERVER_TEST_FILES = \
+ erl_client_c_server_SUITE_data/Makefile.src \
+ erl_client_c_server_SUITE_data/erl_c_test.idl \
+ erl_client_c_server_SUITE_data/erl_client.erl \
+ erl_client_c_server_SUITE_data/c_server.c \
+ erl_client_c_server_SUITE_data/callbacks.c
+
+ERL_CLIENT_C_SERVER_PROTO_TEST_FILES = \
+ erl_client_c_server_proto_SUITE_data/Makefile.src \
+ erl_client_c_server_proto_SUITE_data/erl_c_test.idl \
+ erl_client_c_server_proto_SUITE_data/erl_client.erl \
+ erl_client_c_server_proto_SUITE_data/c_server.c \
+ erl_client_c_server_proto_SUITE_data/callbacks.c
+
+JAVA_CLIENT_ERL_SERVER_TEST_FILES = \
+ java_client_erl_server_SUITE_data/Makefile.src \
+ java_client_erl_server_SUITE_data/java_erl_test.idl \
+ java_client_erl_server_SUITE_data/JavaClient.java \
+ java_client_erl_server_SUITE_data/m_i_impl.erl
+
+MODULES = \
+ ic_SUITE \
+ ic_register_SUITE \
+ ic_pragma_SUITE \
+ ic_pp_SUITE \
+ ic_be_SUITE \
+ c_client_erl_server_SUITE \
+ c_client_erl_server_proto_SUITE \
+ c_client_erl_server_proto_tmo_SUITE \
+ erl_client_c_server_SUITE \
+ erl_client_c_server_proto_SUITE \
+ java_client_erl_server_SUITE
+
+GEN_MODULES =
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+HRL_FILES =
+
+GEN_HRL_FILES =
+
+
+GEN_FILES = \
+ $(GEN_HRL_FILES:%=$(IDLOUTDIR)/%) \
+ $(GEN_MODULES=:%=$(IDLOUTDIR)/%.erl)
+
+GEN_TARGET_FILES = $(GEN_MODULES:%=$(IDLOUTDIR)/%.$(EMULATOR))
+
+SUITE_TARGET_FILES = $(MODULES:%=%.$(EMULATOR))
+
+TARGET_FILES = \
+ $(GEN_TARGET_FILES) \
+ $(SUITE_TARGET_FILES)
+
+# ----------------------------------------------------
+# PROGRAMS
+# ----------------------------------------------------
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_LOCAL_FLAGS += -pa $(ERL_TOP)/lib/orber/ebin -pa $(ERL_TOP)/lib/ic/ebin
+
+ERL_COMPILE_FLAGS += \
+ $(ERL_LOCAL_FLAGS) \
+ -pa $(ERL_TOP)/lib/test_server/ebin \
+ -pa $(ERL_TOP)/lib/orber/ebin \
+ -I$(ERL_TOP)/lib/orber \
+ -I$(ERL_TOP)/lib/test_server/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+tests debug opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f errs core *~
+
+docs:
+
+# ----------------------------------------------------
+# Special Targets
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# Release Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+
+release_docs_spec:
+
+release_tests_spec: tests
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELSYSDIR)/ic_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/ic_register_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/ic_pragma_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/ic_pp_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/ic_be_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_proto_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/erl_client_c_server_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/erl_client_c_server_proto_SUITE_data
+ $(INSTALL_DIR) $(RELSYSDIR)/java_client_erl_server_SUITE_data
+ $(INSTALL_DATA) $(IDL_FILES) ic.cover $(TEST_SPEC_FILE) $(ERL_FILES) \
+ $(RELSYSDIR)
+ $(INSTALL_DATA) $(COMPILER_TEST_FILES) $(RELSYSDIR)/ic_SUITE_data
+ $(INSTALL_DATA) $(COMPILER_TEST_FILES2) \
+ $(RELSYSDIR)/ic_register_SUITE_data
+ $(INSTALL_DATA) $(COMPILER_TEST_FILES3) \
+ $(RELSYSDIR)/ic_pragma_SUITE_data
+ $(INSTALL_DATA) $(COMPILER_TEST_FILES4) \
+ $(RELSYSDIR)/ic_be_SUITE_data
+ $(INSTALL_DATA) $(PREPROCESSOR_TEST_FILES) \
+ $(RELSYSDIR)/ic_pp_SUITE_data
+ $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_TEST_FILES) \
+ $(RELSYSDIR)/c_client_erl_server_SUITE_data
+ $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_PROTO_TEST_FILES) \
+ $(RELSYSDIR)/c_client_erl_server_proto_SUITE_data
+ $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_PROTO_TMO_TEST_FILES) \
+ $(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data
+ $(INSTALL_DATA) $(ERL_CLIENT_C_SERVER_TEST_FILES) \
+ $(RELSYSDIR)/erl_client_c_server_SUITE_data
+ $(INSTALL_DATA) $(ERL_CLIENT_C_SERVER_PROTO_TEST_FILES) \
+ $(RELSYSDIR)/erl_client_c_server_proto_SUITE_data
+ $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(JAVA_CLIENT_ERL_SERVER_TEST_FILES) \
+ $(RELSYSDIR)/java_client_erl_server_SUITE_data
diff --git a/lib/ic/test/c_client_erl_server_SUITE.erl b/lib/ic/test/c_client_erl_server_SUITE.erl
new file mode 100644
index 0000000000..9f43d28f4d
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_SUITE.erl
@@ -0,0 +1,332 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose : Test suite for c-client/erl-server
+%%----------------------------------------------------------------------
+
+
+-module(c_client_erl_server_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-export([init_per_testcase/2, end_per_testcase/2,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ void_test/1, long_test/1, long_long_test/1,
+ unsigned_short_test/1, unsigned_long_test/1,
+ unsigned_long_long_test/1, double_test/1, char_test/1,
+ wchar_test/1, octet_test/1, bool_test/1, struct_test/1,
+ struct2_test/1, seq1_test/1, seq2_test/1, seq3_test/1,
+ seq4_test/1, seq5_test/1, array1_test/1, array2_test/1,
+ enum_test/1, string1_test/1, string2_test/1, string3_test/1,
+ string4_test/1, pid_test/1, port_test/1, ref_test/1, term_test/1,
+ typedef_test/1, inline_sequence_test/1, term_sequence_test/1,
+ term_struct_test/1, wstring1_test/1]).
+
+-define(DEFAULT_TIMEOUT, 20000).
+-define(PORT_TIMEOUT, 15000).
+-define(ERLANG_SERVER_NAME, idl_erlang_server).
+-define(C_CLIENT_NODE_NAME, c_client_idl_test).
+
+%% Add/remove code path and watchdog before/after each test case.
+%%
+init_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:add_patha(DataDir),
+
+ %% Since other test suites use the module m_i, we have
+ %% to make sure we are using the right m_i module.
+ code:purge(m_i),
+ code:load_file(m_i),
+
+ WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:del_path(DataDir),
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [void_test, long_test, long_long_test,
+ unsigned_short_test, unsigned_long_test,
+ unsigned_long_long_test, double_test, char_test,
+ wchar_test, octet_test, bool_test, struct_test,
+ struct2_test, seq1_test, seq2_test, seq3_test,
+ seq4_test, seq5_test, array1_test, array2_test,
+ enum_test, string1_test, string2_test, string3_test,
+ string4_test, pid_test, port_test, ref_test, term_test,
+ typedef_test, inline_sequence_test, term_sequence_test,
+ term_struct_test, wstring1_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+array1_test(doc) -> "";
+array1_test(suite) -> [];
+array1_test(Config) ->
+ do_test(array1_test, Config).
+
+array2_test(doc) -> "";
+array2_test(suite) -> [];
+array2_test(Config) ->
+ do_test(array2_test, Config).
+
+bool_test(doc) -> "";
+bool_test(suite) -> [];
+bool_test(Config) ->
+ do_test(bool_test, Config).
+
+char_test(doc) -> "";
+char_test(suite) -> [];
+char_test(Config) ->
+ do_test(char_test, Config).
+
+double_test(doc) -> "";
+double_test(suite) -> [];
+double_test(Config) ->
+ do_test(double_test, Config).
+
+enum_test(doc) -> "";
+enum_test(suite) -> [];
+enum_test(Config) ->
+ do_test(enum_test, Config).
+
+inline_sequence_test(doc) -> "";
+inline_sequence_test(suite) -> [];
+inline_sequence_test(Config) ->
+ do_test(inline_sequence_test, Config).
+
+long_long_test(doc) -> "";
+long_long_test(suite) -> [];
+long_long_test(Config) ->
+ do_test(long_long_test, Config).
+
+long_test(doc) -> "";
+long_test(suite) -> [];
+long_test(Config) ->
+ do_test(long_test, Config).
+
+octet_test(doc) -> "";
+octet_test(suite) -> [];
+octet_test(Config) ->
+ do_test(octet_test, Config).
+
+pid_test(doc) -> "";
+pid_test(suite) -> [];
+pid_test(Config) ->
+ do_test(pid_test, Config).
+
+port_test(doc) -> "";
+port_test(suite) -> [];
+port_test(Config) ->
+ do_test(port_test, Config).
+
+ref_test(doc) -> "";
+ref_test(suite) -> [];
+ref_test(Config) ->
+ do_test(ref_test, Config).
+
+seq1_test(doc) -> "";
+seq1_test(suite) -> [];
+seq1_test(Config) ->
+ do_test(seq1_test, Config).
+
+seq2_test(doc) -> "";
+seq2_test(suite) -> [];
+seq2_test(Config) ->
+ do_test(seq2_test, Config).
+
+seq3_test(doc) -> "";
+seq3_test(suite) -> [];
+seq3_test(Config) ->
+ do_test(seq3_test, Config).
+
+seq4_test(doc) -> "";
+seq4_test(suite) -> [];
+seq4_test(Config) ->
+ do_test(seq4_test, Config).
+
+seq5_test(doc) -> "";
+seq5_test(suite) -> [];
+seq5_test(Config) ->
+ do_test(seq5_test, Config).
+
+string1_test(doc) -> "";
+string1_test(suite) -> [];
+string1_test(Config) ->
+ do_test(string1_test, Config).
+
+string2_test(doc) -> "";
+string2_test(suite) -> [];
+string2_test(Config) ->
+ do_test(string2_test, Config).
+
+string3_test(doc) -> "";
+string3_test(suite) -> [];
+string3_test(Config) ->
+ do_test(string3_test, Config).
+
+string4_test(doc) -> "";
+string4_test(suite) -> [];
+string4_test(Config) ->
+ do_test(string4_test, Config).
+
+struct2_test(doc) -> "";
+struct2_test(suite) -> [];
+struct2_test(Config) ->
+ do_test(struct2_test, Config).
+
+struct_test(doc) -> "";
+struct_test(suite) -> [];
+struct_test(Config) ->
+ do_test(struct_test, Config).
+
+term_sequence_test(doc) -> "";
+term_sequence_test(suite) -> [];
+term_sequence_test(Config) ->
+ do_test(term_sequence_test, Config).
+
+term_struct_test(doc) -> "";
+term_struct_test(suite) -> [];
+term_struct_test(Config) ->
+ do_test(term_struct_test, Config).
+
+term_test(doc) -> "";
+term_test(suite) -> [];
+term_test(Config) ->
+ do_test(term_test, Config).
+
+typedef_test(doc) -> "";
+typedef_test(suite) -> [];
+typedef_test(Config) ->
+ do_test(typedef_test, Config).
+
+unsigned_long_long_test(doc) -> "";
+unsigned_long_long_test(suite) -> [];
+unsigned_long_long_test(Config) ->
+ do_test(unsigned_long_long_test, Config).
+
+unsigned_long_test(doc) -> "";
+unsigned_long_test(suite) -> [];
+unsigned_long_test(Config) ->
+ do_test(unsigned_long_test, Config).
+
+unsigned_short_test(doc) -> "";
+unsigned_short_test(suite) -> [];
+unsigned_short_test(Config) ->
+ do_test(unsigned_short_test, Config).
+
+void_test(doc) -> "";
+void_test(suite) -> [];
+void_test(Config) ->
+ do_test(void_test, Config).
+
+wchar_test(doc) -> "";
+wchar_test(suite) -> [];
+wchar_test(Config) ->
+ do_test(wchar_test, Config).
+
+wstring1_test(doc) -> "";
+wstring1_test(suite) -> [];
+wstring1_test(Config) ->
+ do_test(wstring1_test, Config).
+
+
+%% It is here that all tests really are done.
+%%
+
+do_test(Case, Config) ->
+ %% Trap exits
+ process_flag(trap_exit, true),
+ %% Start the server
+ {ok, _Pid} = m_i:oe_create_link([], {local, ?ERLANG_SERVER_NAME}),
+ Node = atom_to_list(node()),
+ DataDir = ?config(data_dir, Config),
+ %% io:format("~p: data directory: ~p~n", [?MODULE, DataDir]),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ %% Start C-client node as a port program.
+ Cmd = filename:join([DataDir, "c_client"]) ++
+ " -this-node-name " ++ atom_to_list(?C_CLIENT_NODE_NAME) ++
+ " -peer-node " ++ Node ++
+ " -peer-process-name " ++ atom_to_list(?ERLANG_SERVER_NAME) ++
+ " -cookie " ++ Cookie ++
+ " -test-case " ++ atom_to_list(Case),
+ Port = open_port({spawn, Cmd}, [exit_status, eof, stderr_to_stdout]),
+ Res = wait_for_completion(Port),
+ %% Kill off node if there was timeout
+ case Res of
+ {error, timeout} ->
+ catch rpc:cast(?C_CLIENT_NODE_NAME, erlang, halt, [1]);
+ _ ->
+ ok
+ end,
+ process_flag(trap_exit, false),
+ catch m_i:stop(?ERLANG_SERVER_NAME),
+ ok = Res.
+
+
+%% Wait for eof *and* exit status, but return if exit status indicates
+%% an error, or we have been waiting more than PORT_TIMEOUT seconds.
+%%
+wait_for_completion(Port) ->
+ wait_for_completion(Port, 0).
+
+wait_for_completion(Port, N) when N < 2 ->
+ receive
+ {Port, {data, Bytes}} ->
+ %% Relay output
+ io:format("~s", [Bytes]),
+ wait_for_completion(Port, N);
+ {Port, {exit_status, 0}} ->
+ wait_for_completion(Port, N + 1);
+ {Port, {exit_status, Status}} ->
+ {error, Status};
+ {Port, eof} ->
+ wait_for_completion(Port, N + 1);
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason: ~w~n", [Reason]),
+ wait_for_completion(Port, N);
+ {'EXIT', From, Reason} ->
+ io:format("Got unexpected exit: ~p~n", [{'EXIT', From, Reason}]),
+ wait_for_completion(Port, N)
+ after ?PORT_TIMEOUT ->
+ {error, timeout}
+ end;
+wait_for_completion(_, _) ->
+ ok.
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..d5277eb256
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src
@@ -0,0 +1,145 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Makefile.src for c_client_erl_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .h .erl .idl @obj@ .@EMULATOR@
+
+
+# Variables from ts:
+#
+
+ERL_INCLUDE = @erl_include@
+
+IC_INCLUDE_PATH = @ic_include_path@
+IC_LIB = @ic_libpath@@DS@@ic_lib@
+
+ERL_INTERFACE_INCLUDE = @erl_interface_include@
+ERL_INTERFACE_LIB = @erl_interface_libpath@@DS@@erl_interface_lib@
+ERL_INTERFACE_EILIB = @erl_interface_libpath@@DS@@erl_interface_eilib@
+ERL_INTERFACE_THREADLIB = @erl_interface_threadlib@
+ERL_INTERFACE_SOCK_LIBS = @erl_interface_sock_libs@
+
+CC = @CC@
+## XXX Should set warning flag with a DEBUG_FLAG
+CFLAGS = @CFLAGS@ @DEFS@ -I@erl_include@ \
+ -I@ic_include_path@ -I@erl_interface_include@
+
+LD = @LD@
+LDFLAGS = @CROSSLDFLAGS@
+LIBS = $(IC_LIB) $(ERL_INTERFACE_LIB) $(ERL_INTERFACE_EILIB) \
+ $(ERL_INTERFACE_THREADLIB) @LIBS@ $(ERL_INTERFACE_SOCK_LIBS)
+ERLC = erlc
+
+# Generated C header files
+GEN_H_FILES = \
+ m.h \
+ m_i.h \
+ oe_c_erl_test.h
+
+# Generated C files
+GEN_C_FILES = \
+ m.c \
+ m_i.c \
+ oe_c_erl_test.c \
+ oe_code_m_a.c \
+ oe_code_m_arr1.c \
+ oe_code_m_arr2.c \
+ oe_code_m_arr3.c \
+ oe_code_m_aseq.c \
+ oe_code_m_b.c \
+ oe_code_m_bseq.c \
+ oe_code_m_dd.c \
+ oe_code_m_dyn.c \
+ oe_code_m_dyn_sl.c \
+ oe_code_m_es.c \
+ oe_code_m_et.c \
+ oe_code_m_etseq.c \
+ oe_code_m_fruit.c \
+ oe_code_m_lseq.c \
+ oe_code_m_s.c \
+ oe_code_m_s_sl.c \
+ oe_code_m_sarr3.c \
+ oe_code_m_simple.c \
+ oe_code_m_ssarr3.c \
+ oe_code_m_sseq.c \
+ oe_code_m_ssstr3.c \
+ oe_code_m_sstr3.c \
+ oe_code_m_str1.c \
+ oe_code_m_str3.c \
+ oe_code_m_strRec.c \
+ oe_code_m_strRec_str5.c \
+ oe_code_m_strRec_str7.c
+
+GEN_HRL_FILES = \
+ m.hrl \
+ m_i.hrl \
+ oe_c_erl_test.hrl
+
+GEN_ERL_FILES = \
+ m.erl \
+ m_arr2.erl \
+ m_arr3.erl \
+ m_i.erl \
+ m_str3.erl \
+ oe_c_erl_test.erl
+
+C_FILES = $(GEN_C_FILES) c_client.c
+
+OBJS = $(C_FILES:.c=@obj@)
+
+PGMS = c_client@exe@
+
+ERL_FILES = $(GEN_ERL_FILES) m_i_impl.erl
+
+EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
+
+
+all: $(PGMS) $(EBINS)
+
+clean:
+ -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+ -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+
+$(PGMS): $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" c_erl_test.idl
+
+$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl
+
+.c@obj@:
+ $(CC) -c -o $*@obj@ $(CFLAGS) $<
+
+.erl.@EMULATOR@:
+ $(ERLC) -I $(IC_INCLUDE_PATH) $<
+
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
new file mode 100644
index 0000000000..e4f9cfdece
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/c_client.c
@@ -0,0 +1,1760 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2001-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+/* C-client for test of IC.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+
+#include <string.h>
+
+#ifdef __WIN32__
+# include <time.h>
+# include <sys/timeb.h>
+#elif defined VXWORKS
+#include <time.h>
+#include <sys/times.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <ctype.h>
+
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+
+#include "ei.h"
+#include "erl_interface.h"
+#include "m_i.h"
+
+#define HOSTNAMESZ 256
+#define NODENAMESZ 512
+
+#define INBUFSZ 10
+#define OUTBUFSZ 0
+
+#define MAXTRIES 5
+
+#define CHECK_EXCEPTION(x) \
+ if ((x)->_major != CORBA_NO_EXCEPTION) { \
+ fprintf(stderr,"\n\nException: %s\n\n", \
+ (char *)CORBA_exception_value((x))); \
+ CORBA_exception_free((x)); \
+ return -1; \
+ } \
+
+/* XXX Should free things here too! */
+#define RETURN_IF_OK(x) \
+ if ((x)) {\
+ fprintf(stdout, "ok\n");\
+ return 0;\
+ }\
+
+#define cmp_str(x,y) (!strcmp((x),(y)))
+#define cmp_wstr(x,y) (!ic_wstrcmp((x),(y)))
+
+typedef CORBA_Environment IC_Env;
+
+typedef int (*TestFunc)(IC_Env *);
+typedef struct {
+ char *name;
+ TestFunc func;
+} TestCase;
+
+static char longtext[] =
+"Introduction The IC application is an IDL compiler implemented in Erlang."
+" The IDL compiler generates client stubs and server skeletons."
+" Several back-ends are supported, and they fall into three main groups."
+" For more details on IC compiler options consult the ic(3) manual page."
+" Argument passing cases 1 Caller allocates all necessary storage,"
+" except that which may be encapsulated and managed within the parameter itself."
+" 2 The caller allocates a pointer and passes it by reference to the callee."
+" The callee sets the pointer to point to a valid instance of the parameter's type."
+" The caller is responsible for releasing the returned storage."
+" Following completion of a request, the caller is not allowed to modify any values"
+" in the returned storage. To do so the caller must first copy the returned instance"
+" into a new instance, then modify the new instance. 3 The caller allocates a"
+" pointer to an array slice which has all the same dimensions of the original"
+" array except the first, and passes it by reference to the callee. The callee sets"
+" the pointer to point to a valid instance of the array. The caller is responsible for"
+" releasing the returned storage. Following completion of a request, the caller is not"
+" allowed to modify any values in the returned storage. To do so the caller must first"
+" copy the returned instance into a new instance, then modify the new instance."
+" Generated Files Two files will be generated for each scope. One set of files will be"
+" generated for each module and each interface scope. An extra set is generated for"
+" those definitions at top level scope. One of the files is a header file(.h), and the"
+" other file is a C source code file (.c). In addition to these files a number of C"
+" source files will be generated for type encodings, they are named according to the "
+"following template: oe_code_<type>.c.";
+static char this_node[NODENAMESZ + 1];
+static char *progname;
+
+/* Test function prototypes */
+
+static int void_test(IC_Env *env);
+static int long_test(IC_Env *env);
+static int long_long_test(IC_Env *env);
+static int unsigned_short_test(IC_Env *env);
+static int unsigned_long_test(IC_Env *env);
+static int unsigned_long_long_test(IC_Env *env);
+static int double_test(IC_Env *env);
+static int char_test(IC_Env *env);
+static int wchar_test(IC_Env *env);
+static int octet_test(IC_Env *env);
+static int bool_test(IC_Env *env);
+static int struct_test(IC_Env *env);
+static int struct2_test(IC_Env *env);
+static int seq1_test(IC_Env *env);
+static int seq2_test(IC_Env *env);
+static int seq3_test(IC_Env *env);
+static int seq4_test(IC_Env *env);
+static int seq5_test(IC_Env *env);
+static int array1_test(IC_Env *env);
+static int array2_test(IC_Env *env);
+static int enum_test(IC_Env *env);
+static int string1_test(IC_Env *env);
+static int string2_test(IC_Env *env);
+static int string3_test(IC_Env *env);
+static int string4_test(IC_Env *env);
+static int pid_test(IC_Env *env);
+static int port_test(IC_Env *env);
+static int ref_test(IC_Env *env);
+static int term_test(IC_Env *env);
+static int typedef_test(IC_Env *env);
+static int inline_sequence_test(IC_Env *env);
+static int term_sequence_test(IC_Env *env);
+static int term_struct_test(IC_Env *env);
+static int wstring1_test(IC_Env *env);
+
+static TestCase test_cases[] = {
+ {"void_test", void_test},
+ {"long_test", long_test},
+ {"long_long_test", long_long_test},
+ {"unsigned_short_test", unsigned_short_test},
+ {"unsigned_long_test", unsigned_long_test},
+ {"unsigned_long_long_test", unsigned_long_long_test},
+ {"double_test", double_test},
+ {"char_test", char_test},
+ {"wchar_test", wchar_test},
+ {"octet_test", octet_test},
+ {"bool_test", bool_test},
+ {"struct_test", struct_test},
+ {"struct2_test", struct2_test},
+ {"seq1_test", seq1_test},
+ {"seq2_test", seq2_test},
+ {"seq3_test", seq3_test},
+ {"seq4_test", seq4_test},
+ {"seq5_test", seq5_test},
+ {"array1_test", array1_test},
+ {"array2_test", array2_test},
+ {"enum_test", enum_test},
+ {"string1_test", string1_test},
+ {"string2_test", string2_test},
+ {"string3_test", string3_test},
+ {"string4_test", string4_test},
+ {"pid_test", pid_test},
+ {"port_test", port_test},
+ {"ref_test", ref_test},
+ {"term_test", term_test},
+ {"typedef_test", typedef_test},
+ {"inline_sequence_test", inline_sequence_test},
+ {"term_sequence_test", term_sequence_test},
+ {"term_struct_test", term_struct_test},
+ {"wstring1_test", wstring1_test},
+ {"", NULL}
+};
+
+/* Other prototypes */
+static int cmp_aseq(m_aseq *a1, m_aseq *a2);
+static int cmp_a(m_a *a1, m_a *a2);
+static int cmp_bseq(m_bseq *b1, m_bseq *b2);
+static int cmp_b(m_b *b1, m_b *b2);
+static int cmp_lseq(m_lseq *b1, m_lseq *b2);
+static int cmp_etseq(m_etseq *b1, m_etseq *b2);
+static int cmp_et(m_et* b1, m_et *b2);
+static int cmp_es(m_es *b1, m_es *b2);
+static int cmp_arr1(m_arr1 b1, m_arr1 b2);
+static int cmp_dd(m_dd b1, m_dd b2);
+static int cmp_strRec(m_strRec *b1, m_strRec *b2);
+static int cmp_sseq(m_sseq *b1, m_sseq *b2);
+static int cmp_pid(erlang_pid *p1, erlang_pid *p2);
+static int cmp_port(erlang_port *p1, erlang_port *p2);
+static int cmp_ref(erlang_ref *p1, erlang_ref *p2);
+static int cmp_s(m_s *b1, m_s *b2);
+static int cmp_ssstr3(m_ssstr3 *b1, m_ssstr3 *b2);
+static int cmp_ssarr3(m_ssarr3 *b1, m_ssarr3 *b2);
+static int cmp_sarr3(m_sarr3 *b1, m_sarr3 *b2);
+static int cmp_arr3(m_arr3 b1, m_arr3 b2);
+
+static void print_aseq(m_aseq *a);
+static void print_a(m_a *a);
+static void print_bseq(m_bseq *b);
+static void print_lseq(m_lseq *b);
+static void print_b(m_b *b);
+static void print_etseq(m_etseq *b);
+static void print_et(m_et* b);
+static void print_es(m_es *b);
+static void print_arr1(long a[500]);
+static void print_dd(long a[2][3]);
+static void print_strRec(m_strRec* sr);
+static void print_sseq(m_sseq *b);
+static void print_pid(erlang_pid *p);
+static void print_port(erlang_port *p);
+static void print_ref(erlang_ref *p);
+static void print_term(ETERM *t);
+static void print_s(m_s *p);
+static void print_ssstr3(m_ssstr3 *b1);
+static void print_ssarr3(m_ssarr3 *b1);
+static void print_sarr3(m_sarr3 *b1);
+static void print_arr3(m_arr3 b1);
+static void print_wstr(CORBA_wchar *ws);
+
+static void free_etseq_buf(m_etseq *b);
+static void free_et(m_et* b);
+
+#ifdef __WIN32__
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} MyTimeval;
+#else
+typedef struct timeval MyTimeval;
+#endif
+static void my_gettimeofday(MyTimeval *tv);
+static void showtime(MyTimeval *start, MyTimeval *stop);
+static void usage(void);
+static void done(int r);
+
+
+
+/* main */
+
+#ifdef VXWORKS
+int client(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ struct hostent *hp;
+ erlang_pid pid;
+ MyTimeval start, stop;
+ int i, fd, ires, tres;
+ IC_Env *env;
+ int tries = 0;
+ char *this_node_name = NULL;
+ char *peer_node = NULL;
+ char *peer_process_name = NULL;
+ char *cookie = NULL;
+ char host[HOSTNAMESZ + 1];
+ TestFunc test_func = NULL;
+ TestCase *test_case;
+ char *test_case_name = NULL;
+
+#ifdef __WIN32__
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ fprintf(stderr, "Could not load winsock2 v2.0 compatible DLL");
+ exit(1);
+ }
+#endif
+
+ progname = argv[0];
+ host[HOSTNAMESZ] = '\0';
+ if (gethostname(host, HOSTNAMESZ) < 0) {
+ fprintf(stderr, "Can't find own hostname\n");
+ done(1);
+ }
+ if ((hp = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Can't get ip address for host %s\n", host);
+ done(1);
+ }
+ for (i = 1; i < argc; i++) {
+ if (cmp_str(argv[i], "-help")) {
+ usage();
+ done(0);
+ } else if (cmp_str(argv[i], "-this-node-name")) {
+ i++;
+ this_node_name = argv[i];
+ } else if (cmp_str(argv[i], "-peer-node")) {
+ i++;
+ peer_node = argv[i];
+ } else if (cmp_str(argv[i], "-peer-process-name")) {
+ i++;
+ peer_process_name = argv[i];
+ } else if (cmp_str(argv[i], "-cookie")) {
+ i++;
+ cookie = argv[i];
+ } else if (cmp_str(argv[i], "-test-case")) {
+ i++;
+ test_case_name = argv[i];
+ } else {
+ fprintf(stderr, "Error : invalid argument \"%s\"\n", argv[i]);
+ usage();
+ done(1);
+ }
+ }
+
+ if (this_node_name == NULL || peer_node == NULL || test_case_name == NULL
+ || peer_process_name == NULL || cookie == NULL) {
+ fprintf(stderr, "Error: missing option\n");
+ usage();
+ done(1);
+ }
+
+ test_case = test_cases;
+ while (test_case->func) {
+ if (cmp_str(test_case->name, test_case_name)) {
+ test_func = test_case->func;
+ break;
+ }
+ test_case++;
+ }
+ if (test_func == NULL) {
+ fprintf(stderr, "Error: illegal test case: \"%s\"\n", test_case_name);
+ done(1);
+ }
+
+ /* Behead hostname at first dot */
+ for (i=0; host[i] != '\0'; i++) {
+ if (host[i] == '.') { host[i] = '\0'; break; }
+ }
+ sprintf(this_node, "%s@%s", this_node_name, host);
+ fprintf(stderr, "c_client: this node: \"%s\"\n", this_node);
+ fprintf(stderr, "c_client: peer node: \"%s\"\n", peer_node);
+ fprintf(stderr, "c_client: test case: \"%s\"\n", test_case_name);
+
+ fprintf(stderr, "c_client: starting\n");
+
+ /* initialize erl_interface */
+ erl_init(NULL, 0);
+
+ for (tries = 0; tries < MAXTRIES; tries++) {
+
+ /* connect to erlang node */
+
+ ires = erl_connect_xinit(host, this_node_name, this_node,
+ (struct in_addr *)*hp->h_addr_list,
+ cookie, 0);
+
+ fprintf(stderr, "c_client: erl_connect_xinit(): %d\n", ires);
+
+ fd = erl_connect(peer_node);
+ fprintf(stderr, "c_client: erl_connect(): %d\n", fd);
+
+ if (fd >= 0)
+ break;
+ fprintf(stderr, "c_client: cannot connect, retrying\n");
+ }
+ if (fd < 0) {
+ fprintf(stderr, "c_client: cannot connect, exiting\n");
+ done(1);
+ }
+ env = CORBA_Environment_alloc(INBUFSZ, OUTBUFSZ);
+ env->_fd = fd;
+ strcpy(env->_regname, peer_process_name);
+ env->_to_pid = NULL;
+ env->_from_pid = &pid;
+
+ strcpy(pid.node, this_node);
+ pid.num = fd;
+ pid.serial = 0;
+ pid.creation = 0;
+
+ my_gettimeofday(&start);
+ tres = test_func(env); /* Call test case */
+ my_gettimeofday(&stop);
+ showtime(&start, &stop);
+ erl_close_connection(fd);
+
+ printf("c_client: env->_inbuf before : %d\n", INBUFSZ);
+ printf("c_client: env->_outbuf before : %d\n", OUTBUFSZ);
+ printf("c_client: env->_inbuf after : %d\n", env->_inbufsz);
+ printf("c_client: env->_outbuf after : %d\n", env->_outbufsz);
+
+ CORBA_free(env->_inbuf);
+ CORBA_free(env->_outbuf);
+ CORBA_free(env);
+ done(tres);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [-help] -this-node-name <name> "
+ "-peer-node <nodename> -peer-process-name <name> "
+ "-cookie <cookie> -test-case <test case name>\n", progname);
+ fprintf(stderr, "Example:\n %s -this-node-name kalle "
+ "-peer-node olle@home -peer-process-name idltest "
+ "-cookie oa678er -test-case octet_test\n", progname);
+}
+
+static void done(int r)
+{
+#ifdef __WIN32__
+ WSACleanup();
+#endif
+ exit(r);
+}
+
+
+/* TESTS */
+
+static int void_test(IC_Env *env)
+{
+ fprintf(stdout, "\n======== m_i_void test ======\n\n");
+ m_i_void_test(NULL,env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(1);
+}
+
+static int long_test(IC_Env *env)
+{
+ long l = 4711, lo, lr;
+
+ fprintf(stdout, "\n======== m_i_long test ======\n\n");
+ lr = m_i_long_test(NULL, l, &lo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(l == lo && l == lr);
+ if (l != lo)
+ fprintf(stdout, " out parameter error, sent: %ld, got: %ld\n", l, lo);
+ if (l != lr)
+ fprintf(stdout, " result error, sent: %ld, got: %ld\n", l, lr);
+ return -1;
+}
+
+static int long_long_test(IC_Env *env)
+{
+ CORBA_long_long ll = 4711, llo, llr;
+
+ fprintf(stdout, "\n======== m_i_longlong test ======\n\n");
+ llr = m_i_longlong_test(NULL, ll, &llo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ll == llo && ll == llr);
+ if (ll != llo)
+ fprintf(stdout, " out parameter error, sent: %ld, got: %ld\n",
+ ll, llo);
+ if (ll != llr)
+ fprintf(stdout, " result error, sent: %ld, got: %ld\n", ll, llr);
+ return -1;
+}
+
+static int unsigned_short_test(IC_Env *env)
+{
+ unsigned short x, y = 2, z;
+
+ fprintf(stdout, "\n======== m_i_ushort test ======\n\n");
+ x = m_i_ushort_test(NULL, y, &z, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(y == z && y == x);
+ if (y != z)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", y, z);
+ if (y != x)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", y, x);
+ return -1;
+}
+
+
+static int unsigned_long_test(IC_Env *env)
+{
+ unsigned long ul = 5050, ulo, ulr;
+
+ fprintf(stdout, "\n======== m_i_ulong test ======\n\n");
+ ulr = m_i_ulong_test(NULL, ul, &ulo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ul == ulo && ul == ulr);
+ if (ul != ulo)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ ul, ulo);
+ if (ul != ulr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n", ul, ulr);
+ return -1;
+}
+
+/*
+ * Note: CORBA_unsigned_long_long is in fact a plain long.
+ */
+static int unsigned_long_long_test(IC_Env *env)
+{
+ CORBA_unsigned_long_long ull = 5050, ullo, ullr;
+
+ fprintf(stdout, "\n======== m_i_ulonglong test ======\n\n");
+ ullr = m_i_ulonglong_test(NULL, ull, &ullo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ull == ullo && ull == ullr);
+ if (ull != ullo)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ ull, ullo);
+ if (ull != ullr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n",
+ ull, ullr);
+ return -1;
+}
+
+static int double_test(IC_Env *env)
+{
+ double d = 12.1212, db, dr;
+
+ fprintf(stdout, "\n======== m_i_double test ======\n\n");
+ dr = m_i_double_test(NULL, d, &db, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(d == db && d == dr);
+ if (d != db)
+ fprintf(stdout, " out parameter error, sent: %f, got: %f\n", d, db);
+ if (d != dr)
+ fprintf(stdout, " result error, sent: %f, got: %f\n", d, dr);
+ return -1;
+}
+
+static int char_test(IC_Env *env)
+{
+ char c = 'g', co, cr;
+
+ /* char test */
+ fprintf(stdout, "\n======== m_i_char test ======\n\n");
+ cr = m_i_char_test(NULL, c, &co, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(c == co && c == cr);
+ if (c !=co)
+ fprintf(stdout, " out parameter error, sent: %c, got: %c\n", c, co);
+ if (c != cr)
+ fprintf(stdout, " result error, sent: %c, got: %c\n", c, cr);
+ return -1;
+}
+
+static int wchar_test(IC_Env *env)
+{
+ CORBA_wchar wc = 103, wco, wcr;
+
+ fprintf(stdout, "\n======== m_i_wchar test ======\n\n");
+ wcr = m_i_wchar_test(NULL, wc, &wco, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(wc == wco && wc == wcr);
+ if (wc != wco)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ wc, wco);
+ if (wc != wcr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n",
+ wc, wcr);
+ return -1;
+}
+
+static int octet_test(IC_Env *env)
+{
+ char o ='r', oo, or;
+
+ fprintf(stdout, "\n======== m_i_octet test ======\n\n");
+ or = m_i_octet_test(NULL, o, &oo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(o == oo && o == or);
+ if (o != oo)
+ fprintf(stdout, " out parameter error, sent: %c, got: %c\n", o, oo);
+ if (o != or)
+ fprintf(stdout, " result error, sent: %c, got: %c\n", o, or);
+ return -1;
+}
+
+static int bool_test(IC_Env *env)
+{
+ unsigned char i = 0, io, ir;
+
+ fprintf(stdout, "\n======== m_i_bool test ======\n\n");
+ ir = m_i_bool_test(NULL, i, &io, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(i == io && i == ir);
+ if (i != io)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", i, io);
+ if (i != ir)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", i, ir);
+ return -1;
+}
+
+static int struct_test(IC_Env *env)
+{
+ m_b b = {4711, 'a'}, bo, br;
+
+ fprintf(stdout, "\n======== m_i_struct test ======\n\n");
+ br = m_i_struct_test(NULL, &b, &bo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_b(&b, &bo) && cmp_b(&b, &br));
+ if (!cmp_b(&b, &bo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_b(&b);
+ fprintf(stdout, " got:\n");
+ print_b(&bo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_b(&b, &br)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_b(&b);
+ fprintf(stdout, " got:\n");
+ print_b(&br);
+ fprintf(stdout, "\n");
+ }
+ return -1;
+}
+
+static int struct2_test(IC_Env *env)
+{
+ m_es esi = {m_peach, 5050}, eso, esr;
+
+ fprintf(stdout, "\n======== m_i_struct2 test ======\n\n");
+ esr = m_i_struct2_test(NULL, &esi, &eso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_es(&esi, &eso) && cmp_es(&esi, &esr));
+ if (!cmp_es(&esi, &eso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_es(&esi);
+ fprintf(stdout, " got:\n");
+ print_es(&eso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_es(&esi, &esr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_es(&esi);
+ fprintf(stdout, " got:\n");
+ print_es(&esr);
+ fprintf(stdout, "\n");
+ }
+ return -1;
+}
+
+
+static int seq1_test(IC_Env *env)
+{
+ m_bseq bs, *bso, *bsr;
+
+ m_b ba[3] = {{4711, 'a'}, {4712, 'b'}, {4713, 'c'}};
+ bs._length = 3;
+ bs._buffer = ba;
+
+ fprintf(stdout, "\n======== m_i_seq1 test ======\n\n");
+ bsr = m_i_seq1_test(NULL, &bs, &bso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_bseq(&bs, bso) && cmp_bseq(&bs, bsr));
+ if (!cmp_bseq(&bs, bso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_bseq(&bs);
+ fprintf(stdout, " got:\n");
+ print_bseq(bso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_bseq(&bs, bsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_bseq(&bs);
+ fprintf(stdout, " got:\n");
+ print_bseq(bsr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(bso);
+ CORBA_free(bsr);
+ return -1;
+}
+
+static int seq2_test(IC_Env *env)
+{
+ m_b ba[3] = {{4711, 'a'}, {4712, 'b'}, {4713, 'c'}};
+ m_a a;
+ m_a aa[2];
+ m_aseq as, *aso, *asr;
+
+ a.l = 9999;
+ a.y._length = 3;
+ a.y._buffer = ba;
+ a.d = 66.89898989;
+
+ aa[0] = a;
+ aa[1] = a;
+ as._length = 2;
+ as._buffer = aa;
+
+ fprintf(stdout, "\n======== m_i_seq2 test ======\n\n");
+ asr = m_i_seq2_test(NULL, &as, &aso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_aseq(&as, aso) && cmp_aseq(&as, asr));
+ if (!cmp_aseq(&as, aso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_aseq(&as);
+ fprintf(stdout, " got:\n");
+ print_aseq(aso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_aseq(&as, asr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_aseq(&as);
+ fprintf(stdout, " got:\n");
+ print_aseq(asr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(aso);
+ CORBA_free(asr);
+ return -1;
+}
+
+static int seq3_test(IC_Env *env)
+{
+ m_lseq lsi, *lso, *lsr;
+ long al[500];
+ int i=0;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+ lsi._length = 500;
+ lsi._buffer = al;
+
+ fprintf(stdout, "\n======== m_i_seq3 test ======\n\n");
+ lsr = m_i_seq3_test(NULL, &lsi, &lso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_lseq(&lsi, lso) && cmp_lseq(&lsi, lsr));
+ if (!cmp_lseq(&lsi, lso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_lseq(&lsi);
+ fprintf(stdout, " got:\n");
+ print_lseq(lso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_lseq(&lsi, lsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_lseq(&lsi);
+ fprintf(stdout, " got:\n");
+ print_lseq(lsr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(lso);
+ CORBA_free(lsr);
+ return -1;
+}
+
+static int seq4_test(IC_Env *env)
+{
+ char *stra0[3] = {"a", "long", "time"};
+ char *stra1[3] = {"ago", "there", "was"};
+ char *stra2[3] = {"a", "buggy", "compiler"};
+ m_sstr3 str3s[3] = {{3, 3, stra0}, {3, 3, stra1}, {3, 3, stra2}};
+ m_ssstr3 str3ssi = {3, 3, str3s};
+ m_ssstr3 *str3sso, *str3ssr;
+
+ fprintf(stdout, "\n======== m_i_seq4 test ======\n\n");
+ str3ssr = m_i_seq4_test(NULL, &str3ssi, &str3sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ssstr3(&str3ssi, str3sso) &&
+ cmp_ssstr3(&str3ssi, str3ssr));
+ if (!cmp_ssstr3(&str3ssi, str3sso)){
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ssstr3(&str3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssstr3(str3sso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_ssstr3(&str3ssi, str3ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ssstr3(&str3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssstr3(str3ssr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(str3sso);
+ CORBA_free(str3ssr);
+ return -1;
+}
+
+static int seq5_test(IC_Env *env)
+{
+ m_arr3 arr3a[3] = {
+ {4711, 18931947, 3},
+ {4711, 18931947, 3},
+ {4711, 18931947, 3}};
+ m_sarr3 arr3sa[3] = {{3, 3, arr3a}, {3, 3, arr3a}, {3, 3, arr3a}};
+ m_ssarr3 arr3ssi = {3, 3, arr3sa};
+ m_ssarr3 *arr3sso;
+ m_ssarr3 *arr3ssr;
+
+ fprintf(stdout, "\n======== m_i_seq5 test ======\n\n");
+ arr3ssr = m_i_seq5_test(NULL, &arr3ssi, &arr3sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ssarr3(&arr3ssi, arr3sso) &&
+ cmp_ssarr3(&arr3ssi, arr3ssr));
+ if (!cmp_ssarr3(&arr3ssi, arr3sso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ssarr3(&arr3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssarr3(arr3sso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_ssarr3(&arr3ssi, arr3ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ssarr3(&arr3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssarr3(arr3ssr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(arr3sso);
+ CORBA_free(arr3ssr);
+ return -1;
+}
+
+static int array1_test(IC_Env *env)
+{
+ int i;
+ long al[500];
+ m_arr1 alo;
+ m_arr1_slice* alr;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+
+ fprintf(stdout, "\n======== m_i_array1 test ======\n\n");
+ alr = m_i_array1_test(NULL, al, alo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_arr1(al, alo) && cmp_arr1(al, alr));
+ if (!cmp_arr1(al, alo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_arr1(al);
+ fprintf(stdout, " got:\n");
+ print_arr1(alo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_arr1(al,alr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_arr1(al);
+ fprintf(stdout, " got:\n");
+ print_arr1(alr);
+ fprintf(stdout, "\n");
+ }
+ free(alo);
+ free(alr);
+ return -1;
+}
+
+static int array2_test(IC_Env *env)
+{
+ long dl[2][3] = {{11, 2, 7}, {22, 8 ,13}};
+ m_dd dlo;
+ m_dd_slice* dlr;
+
+ fprintf(stdout, "\n======== m_i_array2 test ======\n\n");
+ dlr = m_i_array2_test(NULL, dl, dlo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_dd(dl,dlo) && cmp_dd(dl,dlr));
+ if (!cmp_dd(dl,dlo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_dd(dl);
+ fprintf(stdout, " got:\n");
+ print_dd(dlo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_dd(dl,dlr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_dd(dl);
+ fprintf(stdout, " got:\n");
+ print_dd(dlr);
+ fprintf(stdout, "\n");
+ }
+ free(*dlr);
+ return -1;
+}
+
+static int enum_test(IC_Env *env)
+{
+ m_fruit ei = m_banana, eo, er;
+
+ fprintf(stdout, "\n======== m_i_enum test ======\n\n");
+ er = m_i_enum_test(NULL, ei, &eo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ei == eo && ei == er);
+ if (ei != eo)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", ei, eo);
+ if (ei != er)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", ei, er);
+ return -1;
+}
+
+static int string1_test(IC_Env *env)
+{
+ char* si = longtext;
+ char* so;
+ char* sr;
+
+ fprintf(stdout, "\n======== m_i_string1 test ======\n\n");
+ sr = m_i_string1_test(NULL, si, &so, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_str(si, so) && cmp_str(si, sr));
+ if (!cmp_str(si, so))
+ fprintf(stdout, " out parameter error, sent: %s, got: %s\n", si, so);
+ if (!cmp_str(si, sr))
+ fprintf(stdout, " result error, sent: %s, got: %s\n", si, sr);
+ CORBA_free(so);
+ CORBA_free(sr);
+ return -1;
+}
+
+static int string2_test(IC_Env *env)
+{
+ char* sa[3] = {"hello", "foo", "bar"};
+ m_sseq ssi = {3, 3, sa};
+ m_sseq *sso, *ssr;
+
+ fprintf(stdout, "\n======== m_i_string2 test ======\n\n");
+ ssr = m_i_string2_test(NULL, &ssi, &sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_sseq(&ssi, sso) && cmp_sseq(&ssi, sso));
+ if (!cmp_sseq(&ssi, sso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_sseq(&ssi);
+ fprintf(stdout, "got:\n");
+ print_sseq(sso);
+ }
+ if (!cmp_sseq(&ssi, ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_sseq(&ssi);
+ fprintf(stdout, "got:\n");
+ print_sseq(ssr);
+ }
+ CORBA_free(sso);
+ CORBA_free(ssr);
+ return -1;
+}
+
+static int string3_test(IC_Env *env)
+{
+ char* si = longtext;
+ char* so;
+ char* sr;
+
+ fprintf(stdout, "\n======== m_i_string3 test ======\n\n");
+ sr = m_i_string3_test(NULL, si, &so, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_str(si, so) && cmp_str(si, so));
+ if (!cmp_str(si, so))
+ fprintf(stdout, " out parameter error, sent: %s, got: %s\n", si, so);
+ if (!cmp_str(si, sr))
+ fprintf(stdout, " result error, sent: %s, got: %s\n", si, sr);
+ CORBA_free(so);
+ CORBA_free(sr);
+ return -1;
+}
+
+static int string4_test(IC_Env *env)
+{
+ char as1[100] = "a string", as2[200] = "help", as3[200] = "hello there";
+ m_strRec stri = { 1, /* dd */
+ as1, /* str4 */
+ {{'a', 'k'}, {'z', 'g'}, {'n', 'q'}}, /* str7 */
+ {3, 3, "buf"}, /* str5 */
+ as2, /* str6 */
+ {'m', 'f', 'o'}, /* str8 */
+ as3, /* str9 */
+ {3, 3, "stu"} /* str10 */
+ };
+ m_strRec *stro, *strr;
+
+ fprintf(stdout, "\n======== m_i_string4 test ======\n\n");
+ strr = m_i_string4_test(NULL, &stri, &stro, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_strRec(&stri,stro) && cmp_strRec(&stri,strr));
+ if (!cmp_strRec(&stri,stro)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_strRec(&stri);
+ fprintf(stdout, " got:\n");
+ print_strRec(stro);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_strRec(&stri,strr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_strRec(&stri);
+ fprintf(stdout, " got:\n");
+ print_strRec(strr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(stro);
+ CORBA_free(strr);
+ return -1;
+}
+
+
+static int pid_test(IC_Env *env)
+{
+ erlang_pid pid = {"", 7, 0, 0}, pido, pidr;
+
+ strcpy(pid.node, this_node), /* this currently running node */
+ fprintf(stdout, "\n======== m_i_pid test ======\n\n");
+ pidr = m_i_pid_test(NULL, &pid, &pido, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_pid(&pid, &pido) && cmp_pid(&pid, &pidr));
+ if (!cmp_pid(&pid, &pido)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_pid(&pid);
+ fprintf(stdout, "got:\n");
+ print_pid(&pido);
+ }
+ if (!cmp_pid(&pid, &pidr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_pid(&pid);
+ fprintf(stdout, "got:\n");
+ print_pid(&pidr);
+ }
+ return -1;
+}
+
+static int port_test(IC_Env *env)
+{
+ erlang_port porti = {"node", 5, 1}, porto, portr;
+
+ fprintf(stdout, "\n======== m_i_port test ======\n\n");
+ portr = m_i_port_test(NULL, &porti, &porto, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_port(&porti, &porto) && cmp_port(&porti, &portr));
+ if (!cmp_port(&porti, &porto)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_port(&porti);
+ fprintf(stdout, "got:\n");
+ print_port(&porto);
+ }
+ if (!cmp_port(&porti, &portr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_port(&porti);
+ fprintf(stdout, "got:\n");
+ print_port(&portr);
+ }
+ return -1;
+}
+
+static int ref_test(IC_Env *env)
+{
+ erlang_ref refi = { "node1", 3, {1, 2, 3}, 1},
+ refo, refr;
+
+ fprintf(stdout, "\n======== m_i_ref test ======\n\n");
+ refr = m_i_ref_test(NULL, &refi, &refo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ref(&refi, &refo) && cmp_ref(&refi, &refr));
+ if (!cmp_ref(&refi, &refo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ref(&refi);
+ fprintf(stdout, "got:\n");
+ print_ref(&refo);
+ }
+ if (!cmp_ref(&refi, &refr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ref(&refi);
+ fprintf(stdout, "got:\n");
+ print_ref(&refr);
+ }
+ return -1;
+}
+
+static int term_test(IC_Env *env)
+{
+ ETERM *ti, *to, *tr;
+
+ ti = erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]");
+
+ fprintf(stdout, "\n======== m_i_term test ======\n\n");
+ tr = m_i_term_test(NULL, ti, &to, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(erl_match(ti, to) && erl_match(ti, tr));
+ if (!erl_match(ti, to)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_term(ti);
+ fprintf(stdout, "got:\n");
+ print_term(to);
+ }
+ if (!erl_match(ti, tr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_term(ti);
+ fprintf(stdout, "got:\n");
+ print_term(tr);
+ }
+ erl_free_term(ti);
+ erl_free_term(to);
+ erl_free_term(tr);
+ return -1;
+}
+
+static int typedef_test(IC_Env *env)
+{
+ m_banan mbi, mbo; /* erlang_port */
+ m_apa mai; /* ETERM* */
+ m_apa mao = NULL;
+ long tl;
+
+ strcpy(mbi.node,"node");
+ mbi.id = 15;
+ mbi.creation = 1;
+
+ fprintf(stdout, "\n======== m_i_typedef test ======\n\n");
+ mai = erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]");
+ tl = m_i_typedef_test(NULL, mai, &mbi, &mao, &mbo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(erl_match(mai, mao) && cmp_port(&mbi, &mbo) && tl == 4711);
+ if (!erl_match(mai, mao)) {
+ fprintf(stdout, " out parameter error (term), sent:\n");
+ print_term(mai);
+ fprintf(stdout, "got:\n");
+ print_term(mao);
+ }
+ if (!cmp_port(&mbi, &mbo)) {
+ fprintf(stdout, " out parameter error (port), sent:\n");
+ print_port(&mbi);
+ fprintf(stdout, "got:\n");
+ print_port(&mbo);
+ }
+ if (tl != 4711) {
+ fprintf(stdout, " result error, sent: 4711, got %ld\n", tl);
+ }
+ erl_free_term(mai);
+ erl_free_term(mao);
+ return -1;
+}
+
+static int inline_sequence_test(IC_Env *env)
+{
+ int i;
+ long al[500];
+ m_s isi = {4711, {500, 10, al}},
+ *iso, *isr;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+ fprintf(stdout, "\n======== m_i_inline_sequence test ======\n\n");
+ isr = m_i_inline_sequence_test(NULL, &isi, &iso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_s(&isi, iso) && cmp_s(&isi, isr));
+ if (!cmp_s(&isi, iso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_s(&isi);
+ fprintf(stdout, "got:\n");
+ print_s(iso);
+ }
+ if (!cmp_s(&isi, isr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_s(&isi);
+ fprintf(stdout, "got:\n");
+ print_s(isr);
+ }
+ CORBA_free(iso);
+ CORBA_free(isr);
+ return -1;
+}
+
+static int term_sequence_test(IC_Env *env)
+{
+ ETERM* et_array[4] = {
+ erl_format("[{apa, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{banan, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{apelsin, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{mango, 1, 23}, \"string\", {1.23, 45}]")};
+ m_etseq etsi = {4, 4, et_array}, *etso, *etsr;
+
+ fprintf(stdout, "\n======== m_i_term_sequence test ======\n\n");
+ etsr = m_i_term_sequence_test(NULL, &etsi, &etso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_etseq(&etsi, etso) && cmp_etseq(&etsi, etsr));
+ if (!cmp_etseq(&etsi, etso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_etseq(&etsi);
+ fprintf(stdout, "got:\n");
+ print_etseq(etso);
+ }
+ if (!cmp_etseq(&etsi, etsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_etseq(&etsi);
+ fprintf(stdout, "got:\n");
+ print_etseq(etsr);
+ }
+ free_etseq_buf(&etsi);
+ free_etseq_buf(etso);
+ free_etseq_buf(etsr);
+ CORBA_free(etso);
+ CORBA_free(etsr);
+ return -1;
+}
+
+static int term_struct_test(IC_Env *env)
+{
+ m_et eti = { erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]"),
+ 121212 };
+ m_et eto, etr;
+
+ fprintf(stdout, "\n======== m_i_term_struct test ======\n\n");
+ etr = m_i_term_struct_test(NULL, &eti, &eto, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_et(&eti, &eto) && cmp_et(&eti, &etr));
+ if (!cmp_et(&eti, &eto)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_et(&eti);
+ fprintf(stdout, "got:\n");
+ print_et(&eto);
+ }
+ if (!cmp_et(&eti, &etr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_et(&eti);
+ fprintf(stdout, "got:\n");
+ print_et(&etr);
+ }
+ free_et(&eti);
+ free_et(&eto);
+ free_et(&etr);
+ return -1;
+}
+
+static int wstring1_test(IC_Env *env)
+{
+ CORBA_wchar wsi[] = {100, 101, 102, 103, 104, 0}, *wso, *wsr;
+
+ fprintf(stdout, "\n======== m_i_wstring1 test ======\n\n");
+ wsr = m_i_wstring1_test(NULL, wsi, &wso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_wstr(wsi, wso) && cmp_wstr(wsi, wsr));
+ if (!cmp_wstr(wsi, wso)) {
+ fprintf(stdout, " out parameter error, sent: \n");
+ print_wstr(wsi);
+ fprintf(stdout, "got:\n");
+ print_wstr(wso);
+ }
+ if (!cmp_wstr(wsi, wsr)) {
+ fprintf(stdout, " result error, sent: \n");
+ print_wstr(wsi);
+ fprintf(stdout, "got:\n");
+ print_wstr(wsr);
+ }
+ CORBA_free(wso);
+ CORBA_free(wsr);
+ return -1;
+}
+
+/* Compare functions */
+static int cmp_aseq(m_aseq *a1, m_aseq *a2)
+{
+ int i;
+
+ if (a1->_length != a2->_length)
+ return 0;
+ for (i = 0; i < a1->_length; i++)
+ if (cmp_a(&(a1->_buffer[i]), &(a2->_buffer[i])) == 0)
+ return 0;
+ return 1;
+}
+
+static int cmp_a(m_a *a1, m_a *a2)
+{
+ return a1->l == a2->l &&
+ a1->d == a2->d &&
+ cmp_bseq(&a1->y, &a2->y);
+}
+
+static int cmp_bseq(m_bseq *b1, m_bseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (cmp_b(&(b1->_buffer[i]), &(b2->_buffer[i])) == 0)
+ return 0;
+ return 1;
+}
+
+static int cmp_b(m_b *b1, m_b *b2)
+{
+ return b1->l == b2->l && b1->c == b2->c;
+}
+
+static int cmp_lseq(m_lseq *b1, m_lseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (b1->_buffer[i] != b2->_buffer[i])
+ return 0;
+ return 1;
+}
+
+static int cmp_etseq(m_etseq *b1, m_etseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (!erl_match(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ return 1;
+}
+
+static int cmp_et(m_et* b1, m_et *b2)
+{
+ return erl_match(b1->e, b2->e) && b1->l == b2->l;
+}
+
+static int cmp_es(m_es *b1, m_es *b2)
+{
+ return b1->f == b2->f && b1->l == b2->l;
+}
+
+static int cmp_arr1(m_arr1 b1, m_arr1 b2)
+{
+ int i;
+
+ for (i = 0; i < 500; i++)
+ if (b1[i] != b2[i])
+ return 0;
+ return 1;
+}
+
+static int cmp_dd(m_dd b1, m_dd b2)
+{
+
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ if (b1[i][j] != b2[i][j])
+ return 0;
+ return 1;
+}
+
+
+
+static int cmp_strRec(m_strRec *b1, m_strRec *b2)
+{
+ int i, j;
+
+ if (b1->bb != b2->bb)
+ return 0;
+ if (!cmp_str(b1->str4,b2->str4))
+ return 0;
+ if (b1->str5._length != b2->str5._length)
+ return 0;
+ for (j = 0; j < b1->str5._length; j++)
+ if (b1->str5._buffer[j] != b2->str5._buffer[j])
+ return 0;
+ if (!cmp_str(b1->str6,b2->str6))
+ return 0;
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ if (b1->str7[i][j] != b2->str7[i][j])
+ return 0;
+ for (j = 0; j < 3; j++)
+ if (b1->str8[j] != b2->str8[j])
+ return 0;
+ if (!cmp_str(b1->str9,b2->str9))
+ return 0;
+ if (b1->str10._length != b2->str10._length)
+ return 0;
+ for (j = 0; j < b1->str10._length; j++)
+ if (b1->str10._buffer[j] != b2->str10._buffer[j])
+ return 0;
+ return 1;
+}
+
+
+static int cmp_sseq(m_sseq *b1, m_sseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (!cmp_str(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ return 1;
+}
+
+
+static int cmp_pid(erlang_pid *p1, erlang_pid *p2)
+{
+ return cmp_str(p1->node,p2-> node) &&
+ p1->num == p2->num &&
+ p1->serial == p2->serial &&
+ p1->creation == p2->creation;
+}
+
+static int cmp_port(erlang_port *p1, erlang_port *p2)
+{
+ return cmp_str(p1->node,p2-> node) && p1->id == p2->id;
+}
+
+static int cmp_ref(erlang_ref *p1, erlang_ref *p2)
+{
+ return cmp_str(p1->node, p2->node) &&
+ p1->len == p2->len &&
+ (p1->len < 1 || p1->n[0] == p2->n[0]) &&
+ (p1->len < 2 || p1->n[1] == p2->n[1]) &&
+ (p1->len < 3 || p1->n[2] == p2->n[2]);
+}
+
+static int cmp_s(m_s *b1, m_s *b2)
+{
+ int i;
+
+ if (b1->l != b2->l)
+ return 0;
+ if (b1->sl._length != b2->sl._length)
+ return 0;
+ for (i = 0; i < b1->sl._length; i++)
+ if (b1->sl._buffer[i] != b2->sl._buffer[i])
+ return 0;
+ return 1;
+}
+
+
+static int cmp_ssstr3(m_ssstr3 *b1, m_ssstr3 *b2)
+{
+ int i,j;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (b1->_buffer[i]._length != b2->_buffer[i]._length)
+ return 0;
+ for (j = 0; j < b1->_buffer[i]._length; j++)
+ if (!cmp_str(b1->_buffer[i]._buffer[j],
+ b2->_buffer[i]._buffer[j]))
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static int cmp_ssarr3(m_ssarr3 *b1, m_ssarr3 *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (!cmp_sarr3(&b1->_buffer[i], &b2->_buffer[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_sarr3(m_sarr3 *b1, m_sarr3 *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (!cmp_arr3(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_arr3(m_arr3 b1, m_arr3 b2)
+{
+ int i;
+
+ for (i = 0; i < sizeof(m_arr3)/sizeof(CORBA_long); i++) {
+ if (b1[i] != b2[i])
+ return 0;
+ }
+ return 1;
+}
+
+/* Print functions */
+static void print_aseq(m_aseq *a)
+{
+ int i;
+ fprintf(stdout, "\nm_aseq size: %ld --------\n", a->_length);
+ for (i = 0; i < a->_length; i++)
+ print_a(&(a->_buffer[i]));
+}
+
+static void print_a(m_a *a)
+{
+ fprintf(stdout, "\nm_a --------\n l: %ld\n d:%f\n", a->l, a->d);
+ print_bseq(&a->y);
+}
+
+static void print_bseq(m_bseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_bseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ print_b(&(b->_buffer[i]));
+}
+
+static void print_lseq(m_lseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_lseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ fprintf(stdout, "[%d]: %ld\n", i, b->_buffer[i]);
+}
+
+static void print_b(m_b *b)
+{
+ fprintf(stdout, "\nm_b --------\n l: %ld\n c: %c\n", b->l, b->c);
+}
+
+
+static void print_etseq(m_etseq *b)
+{
+ int i;
+
+ for (i = 0; i < b->_length; i++) {
+ fprintf(stdout, "[%d]:\n", i);
+ erl_print_term(stdout, b->_buffer[i]);
+ }
+}
+
+
+static void print_et(m_et* b)
+{
+ fprintf(stdout, "\net struct --------\n");
+ erl_print_term(stdout, b->e);
+ fprintf(stdout, "long: %ld\n", b->l);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_es(m_es *b)
+{
+ fprintf(stdout, "\nm_es --------\n f: %d\n l: %ld\n", b->f, b->l);
+}
+
+
+static void print_arr1(long a[10])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ fprintf(stdout, "\n[%d]: %ld\n", i, a[i]);
+}
+
+static void print_dd(long a[2][3])
+{
+ int i, j;
+
+ fprintf(stdout, "\nlong dd[2][3] --------\n");
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "\n[%d][%d]: %ld\n", i, j, a[i][j]);
+}
+
+
+static void print_strRec(m_strRec* sr)
+{
+ int i, j;
+
+ fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
+ fprintf(stdout, "string str4 : %s\n",sr->str4);
+ fprintf(stdout, "str7[2][3] :\n");
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
+ fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
+ for (j = 0; j < sr->str5._length; j++)
+ fprintf(stdout, "str5._buffer[%d]: %c\n", j, sr->str5._buffer[j]);
+ fprintf(stdout, "string str6 : %s\n",sr->str6);
+ fprintf(stdout, "str8 :\n");
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "str8[%d]: %c\n", j, sr->str8[j]);
+ fprintf(stdout, "string str9 : %s\n",sr->str9);
+ fprintf(stdout, "str10._length : %ld\n",sr->str10._length);
+ for (j = 0; j < sr->str10._length; j++)
+ fprintf(stdout, "str10._buffer[%d]: %c\n", j, sr->str10._buffer[j]);
+}
+
+static void print_sseq(m_sseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_sseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ fprintf(stdout, "%s\n", b->_buffer[i]);
+
+}
+
+
+static void print_pid(erlang_pid *p)
+{
+ fprintf(stdout, "\nerlang_pid --------\n node: %s\n num: %d\n "
+ "serial: %d\n creation: %d\n",
+ p->node, p->num, p->serial, p->creation);
+}
+
+static void print_port(erlang_port *p)
+{
+ fprintf(stdout, "\nerlang_port --------\n node: %s\n id: %d\n "
+ "creation: %d\n", p->node, p->id, p->creation);
+}
+
+static void print_ref(erlang_ref *p)
+{
+ fprintf(stdout, "\nerlang_ref --------\n node: %s\n len: %d\n "
+ "n[0]: %d\n n[1]: %d\n n[2]: %d\n creation: %d\n",
+ p->node, p->len, p->n[0], p->n[1], p->n[2], p->creation);
+}
+
+static void print_term(ETERM *t)
+{
+ fprintf(stdout, "\nETERM --------\n");
+ erl_print_term(stdout, t);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_s(m_s *p)
+{
+ int i;
+
+ fprintf(stdout, "\n%ld\n", p->l);
+ for (i = 0; i < p->sl._length; i++)
+ fprintf(stdout, "\n[%d]: %ld\n", i, p->sl._buffer[i]);
+}
+
+
+static void print_ssstr3(m_ssstr3 *b1)
+{
+ int i,j;
+
+ fprintf(stdout, "\nSSSTR3 --------\n");
+ fprintf(stdout,"b1->_length = %ld\n",b1->_length);
+ for (i = 0; i < b1->_length; i++) {
+ fprintf(stdout,"\nb1->_buffer[%d]._length %ld\n",
+ i, b1->_buffer[i]._length);
+ for (j = 0; j < b1->_buffer[i]._length; j++)
+ fprintf(stdout,"b1->_buffer[%d]._buffer[%d] = %s\n",
+ i, j, b1->_buffer[i]._buffer[j]);
+ }
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_wstr(CORBA_wchar *ws)
+{
+ int i = 0;
+
+ fprintf(stdout, "\nwstr --------\n");
+ while (ws[i]) {
+ fprintf(stdout, "[%d]: %ld\n", i, ws[i]);
+ i++;
+ }
+ fprintf(stdout, "\n--------\n");
+}
+
+
+static void print_ssarr3(m_ssarr3 *b1)
+{
+ int i;
+
+ fprintf(stdout, "\nssarr3 --------\n");
+ fprintf(stdout,"length: %ld\n",b1->_length);
+ fprintf(stdout, "buffer:\n");
+ for (i = 0; i < b1->_length; i++)
+ print_sarr3(&b1->_buffer[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_sarr3(m_sarr3 *b1)
+{
+ int i;
+
+ fprintf(stdout, "\nsarr3 --------\n");
+ fprintf(stdout,"length: %ld\n",b1->_length);
+ fprintf(stdout, "buffer:\n");
+ for (i = 0; i < b1->_length; i++)
+ print_arr3(b1->_buffer[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_arr3(m_arr3 b1)
+{
+ int i;
+
+ fprintf(stdout, "\narr3 --------\n");
+ for (i = 0; i < sizeof(m_arr3)/sizeof(CORBA_long); i++)
+ fprintf(stdout, "%ld ", b1[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void free_etseq_buf(m_etseq *b)
+{
+ int i;
+
+ for (i = 0; i < b->_length; i++)
+ erl_free_term(b->_buffer[i]);
+}
+
+static void free_et(m_et* b)
+{
+ erl_free_term(b->e);
+}
+
+static void showtime(MyTimeval *start, MyTimeval *stop)
+{
+ MyTimeval elapsed;
+
+ elapsed.tv_sec = stop->tv_sec - start->tv_sec;
+ elapsed.tv_usec = stop->tv_usec - start->tv_usec;
+ while (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ fprintf(stderr,"%ld.%06ld seconds\n",elapsed.tv_sec, elapsed.tv_usec);
+}
+
+static void my_gettimeofday(MyTimeval *tv)
+#ifdef __WIN32__
+#define EPOCH_JULIAN_DIFF 11644473600i64
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ LONGLONG lft;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ memcpy(&lft, &ft, sizeof(lft));
+ tv->tv_usec = (long) ((lft / 10i64) % 1000000i64);
+ tv->tv_sec = (long) ((lft / 10000000i64) - EPOCH_JULIAN_DIFF);
+}
+#elif defined VXWORKS
+{
+ int rate = sysClkRateGet(); /* Ticks per second */
+ unsigned long ctick = tickGet();
+ tv->tv_sec = ctick / rate; /* secs since reboot */
+ tv->tv_usec = ((ctick - (tv->tv_sec * rate))*1000000)/rate;
+}
+#else
+{
+ gettimeofday(tv, NULL);
+}
+#endif
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/c_erl_test.idl b/lib/ic/test/c_client_erl_server_SUITE_data/c_erl_test.idl
new file mode 100644
index 0000000000..ccb8f54508
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/c_erl_test.idl
@@ -0,0 +1,174 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2001-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+#include "erlang.idl"
+
+
+const short TestConst = 1;
+
+module m {
+
+ const short TestConst = 2;
+
+ struct b {
+ long l;
+ char c;
+ };
+
+ struct simple {
+ long l;
+ b b_t;
+ };
+
+ enum fruit {orange, banana, apple, peach, pear};
+
+ typedef sequence<long> lseq;
+
+ typedef sequence<b> bseq;
+
+ struct a {
+ long l;
+ bseq y;
+ double d;
+ };
+
+ typedef sequence<a> aseq;
+
+ typedef sequence<string> sseq;
+ typedef string str;
+ typedef long myLong;
+
+ typedef long arr1[500], dd[2][3];
+
+ typedef erlang::term apa;
+ typedef erlang::port banan;
+
+ typedef sequence<erlang::term> etseq;
+
+ struct s {
+ long l;
+ sequence<long> sl;
+ };
+
+ struct es {
+ fruit f;
+ myLong l;
+ };
+
+ struct et {
+ erlang::term e;
+ long l;
+ };
+
+
+ typedef sequence<char> str1;
+ typedef string<12> str2;
+ typedef char str3[3];
+
+ typedef sequence<string> sstr3; // sequence of string
+ typedef sequence<sstr3> ssstr3; // sequence of sequences of strings
+
+ typedef long arr3[3]; // array of long
+ typedef sequence<arr3> sarr3; // sequence of array
+ typedef sequence<sarr3> ssarr3; // sequence of sequnces of arrays of strings
+
+ struct strRec{
+ boolean bb;
+ string str4;
+ long str7[3][2];
+ sequence<char> str5;
+ string<12> str6;
+ str3 str8;
+ str2 str9;
+ str1 str10;
+ };
+
+
+ struct dyn {
+ long l;
+ sequence<long> sl;
+ };
+ typedef dyn arr2[1][2];
+
+
+ interface i {
+
+ const short TestConst = 3;
+
+ //arr2 suck(in arr2 x, out arr2 y );
+
+ ///////////////////////////////// attribute long l;
+
+ // simple types
+ void void_test();
+ long long_test(in long a, out long a1);
+ long long longlong_test(in long long a, out long long a1);
+ unsigned short ushort_test(in unsigned short a, out unsigned short a1);
+ unsigned long ulong_test(in unsigned long a, out unsigned long a1);
+ unsigned long long ulonglong_test(in unsigned long long a, out unsigned long long a1);
+ double double_test(in double a, out double a1);
+ char char_test(in char a, out char a1);
+ wchar wchar_test(in wchar a, out wchar a1);
+ octet octet_test(in octet a, out octet a1);
+ boolean bool_test(in boolean a, out boolean a1);
+
+ // Seq. and struct tests
+ b struct_test(in b a, out b a1);
+ es struct2_test(in es a, out es a1);
+ //simple struct3_test(in simple x, out simple y);
+ bseq seq1_test(in bseq a, out bseq a1);
+ aseq seq2_test(in aseq a, out aseq a1);
+ lseq seq3_test(in lseq a, out lseq a1);
+ ssstr3 seq4_test(in ssstr3 a, out ssstr3 a1);
+ ssarr3 seq5_test(in ssarr3 a, out ssarr3 a1);
+
+ // Array tests
+ arr1 array1_test(in arr1 a, out arr1 a1);
+ dd array2_test(in dd a, out dd a1);
+
+ // enum test
+ fruit enum_test(in fruit a, out fruit a1);
+
+ // string tests
+ string string1_test(in string a, out string a1);
+ wstring wstring1_test(in wstring a, out wstring a1);
+ sseq string2_test(in sseq a, out sseq a1);
+ str string3_test(in str a, out str a1);
+ strRec string4_test(in strRec a, out strRec a1);
+
+ // Special erlang types
+ erlang::pid pid_test(in erlang::pid a, out erlang::pid a1);
+ erlang::port port_test(in erlang::port a, out erlang::port a1);
+ erlang::ref ref_test(in erlang::ref a, out erlang::ref a1);
+ erlang::term term_test(in erlang::term a, out erlang::term a1);
+
+ // typedef test
+ long typedef_test(in apa a, in banan b, out apa a1, out banan b1);
+
+ // inlined seq. test
+ s inline_sequence_test(in s a, out s a1);
+
+ // term seq. test
+ etseq term_sequence_test(in etseq a, out etseq a1);
+ // term struct test
+ et term_struct_test(in et a, out et a1);
+
+ };
+
+};
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/erl_server.erl b/lib/ic/test/c_client_erl_server_SUITE_data/erl_server.erl
new file mode 100644
index 0000000000..8ccb00aa4d
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/erl_server.erl
@@ -0,0 +1,28 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(erl_server).
+
+-export([run/0, stop/0]).
+
+run() ->
+ m_i:oe_create().
+
+stop() ->
+ gen_server:cast(cidl_test, stop).
diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/m_i_impl.erl b/lib/ic/test/c_client_erl_server_SUITE_data/m_i_impl.erl
new file mode 100644
index 0000000000..9bb29bba16
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_SUITE_data/m_i_impl.erl
@@ -0,0 +1,161 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(m_i_impl).
+-include("m.hrl").
+
+-export([init/1, terminate/2, void_test/1, long_test/2, ushort_test/2,
+ longlong_test/2, ulong_test/2, ulonglong_test/2,
+ double_test/2, char_test/2, wchar_test/2, octet_test/2,
+ bool_test/2, struct_test/2, struct2_test/2, seq1_test/2,
+ seq2_test/2, seq3_test/2, seq4_test/2, seq5_test/2,
+ array1_test/2, array2_test/2, enum_test/2, string1_test/2,
+ string2_test/2, string3_test/2, string4_test/2, pid_test/2,
+ port_test/2, ref_test/2, term_test/2, typedef_test/3,
+ inline_sequence_test/2, '_set_l'/2, '_get_l'/1,
+ term_struct_test/2, term_sequence_test/2, wstring1_test/2]).
+
+-define(PRINTDEBUG(Case),
+ io:format("erl_server: case: ~p~n"
+ "erl_server: location: ~p~n", [Case, [?FILE, ?LINE]])).
+-define(PRINTDEBUG2(Case, Msg),
+ io:format("erl_server: case: ~p~n"
+ "erl_server: Msg: ~p~n"
+ "erl_server: location: ~p~n", [Case, Msg, [?FILE, ?LINE]])).
+
+init(Env) ->
+ {ok, []}.
+
+terminate(F, R) ->
+ ok.
+
+'_get_l'(State) ->
+ ?PRINTDEBUG("_get_l"),
+ {reply, State, State}.
+void_test(State) ->
+ ?PRINTDEBUG("void_test"),
+ {reply, ok, State}.
+
+'_set_l'(State, V) ->
+ ?PRINTDEBUG2("_set_l", V),
+ {reply, ok, V}.
+ushort_test(State, V) ->
+ ?PRINTDEBUG2("ushort_test", V),
+ {reply, {V, V}, State}.
+long_test(State, V) ->
+ ?PRINTDEBUG2("long_test", V),
+ {reply, {V, V}, State}.
+longlong_test(State, V) ->
+ ?PRINTDEBUG2("longlong_test", V),
+ {reply, {V, V}, State}.
+ulong_test(State, V) ->
+ ?PRINTDEBUG2("ulong_test", V),
+ {reply, {V, V}, State}.
+ulonglong_test(State, V) ->
+ ?PRINTDEBUG2("ulonglong_test", V),
+ {reply, {V, V}, State}.
+double_test(State, V) ->
+ ?PRINTDEBUG2("double_test", V),
+ {reply, {V, V}, State}.
+char_test(State, V) ->
+ ?PRINTDEBUG2("char_test", V),
+ {reply, {V, V}, State}.
+wchar_test(State, V) ->
+ ?PRINTDEBUG2("wchar_test", V),
+ {reply, {V, V}, State}.
+octet_test(State, V) ->
+ ?PRINTDEBUG2("octet_test", V),
+ {reply, {V, V}, State}.
+bool_test(State, V) ->
+ ?PRINTDEBUG2("bool_test", V),
+ {reply, {V, V}, State}.
+
+struct_test(State, V) ->
+ ?PRINTDEBUG2("struct_test", V),
+ {reply, {V, V}, State}.
+struct2_test(State, V) ->
+ ?PRINTDEBUG2("struct2_test", V),
+ {reply, {V, V}, State}.
+seq1_test(State, V) ->
+ ?PRINTDEBUG2("seq1_test", V),
+ {reply, {V, V}, State}.
+seq2_test(State, V) ->
+ ?PRINTDEBUG2("seq2_test", V),
+ {reply, {V, V}, State}.
+seq3_test(State, V) ->
+ ?PRINTDEBUG2("seq3_test", V),
+ {reply, {V, V}, State}.
+seq4_test(State, V) ->
+ ?PRINTDEBUG2("seq4_test", V),
+ {reply, {V, V}, State}.
+seq5_test(State, V) ->
+ ?PRINTDEBUG2("seq5_test", V),
+ {reply, {V, V}, State}.
+array1_test(State, V) ->
+ ?PRINTDEBUG2("array1_test", V),
+ {reply, {V, V}, State}.
+array2_test(State, V) ->
+ ?PRINTDEBUG2("array2_test", V),
+ {reply, {V, V}, State}.
+enum_test(State, V) ->
+ ?PRINTDEBUG2("enum_test", V),
+ {reply, {V, V}, State}.
+string1_test(State, V) ->
+ ?PRINTDEBUG2("string1_test", V),
+ {reply, {V, V}, State}.
+string2_test(State, V) ->
+ ?PRINTDEBUG2("string2_test", V),
+ {reply, {V, V}, State}.
+string3_test(State, V) ->
+ ?PRINTDEBUG2("string3_test", V),
+ {reply, {V, V}, State}.
+string4_test(State, V) ->
+ ?PRINTDEBUG2("string4_test", V),
+ {reply, {V, V}, State}.
+pid_test(State, V) ->
+ ?PRINTDEBUG2("pid_test", V),
+ {reply, {V, V}, State}.
+port_test(State, V) ->
+ ?PRINTDEBUG2("port_test", binary_to_list(term_to_binary(V))),
+ {reply, {V, V}, State}.
+ref_test(State, V) ->
+ ?PRINTDEBUG2("ref_test", binary_to_list(term_to_binary(V))),
+ {reply, {V, V}, State}.
+term_test(State, V) ->
+ ?PRINTDEBUG2("term_test", V),
+ {reply, {V, V}, State}.
+typedef_test(State, A, B) ->
+ ?PRINTDEBUG2("typedef_test", [A,B]),
+ {reply, {4711, A, B}, State}.
+inline_sequence_test(State, V) ->
+ ?PRINTDEBUG2("inline_sequence_test", V),
+ {reply, {V, V}, State}.
+term_sequence_test(State, V) ->
+ ?PRINTDEBUG2("term_sequence_test", V),
+ {reply, {V, V}, State}.
+term_struct_test(State, V) ->
+ ?PRINTDEBUG2("term_struct_test", V),
+ {reply, {V, V}, State}.
+wstring1_test(State, V) ->
+ ?PRINTDEBUG2("wstring1_test", V),
+ {reply, {V, V}, State}.
+
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE.erl b/lib/ic/test/c_client_erl_server_proto_SUITE.erl
new file mode 100644
index 0000000000..de643ee8cc
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE.erl
@@ -0,0 +1,332 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose : Test suite for c-client/erl-server
+%%----------------------------------------------------------------------
+
+-module(c_client_erl_server_proto_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-export([init_per_testcase/2, end_per_testcase/2,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ void_test/1, long_test/1, long_long_test/1,
+ unsigned_short_test/1, unsigned_long_test/1,
+ unsigned_long_long_test/1, double_test/1, char_test/1,
+ wchar_test/1, octet_test/1, bool_test/1, struct_test/1,
+ struct2_test/1, seq1_test/1, seq2_test/1, seq3_test/1,
+ seq4_test/1, seq5_test/1, array1_test/1, array2_test/1,
+ enum_test/1, string1_test/1, string2_test/1, string3_test/1,
+ string4_test/1, pid_test/1, port_test/1, ref_test/1, term_test/1,
+ typedef_test/1, inline_sequence_test/1, term_sequence_test/1,
+ term_struct_test/1, wstring1_test/1]).
+
+-define(DEFAULT_TIMEOUT, 20000).
+-define(PORT_TIMEOUT, 15000).
+-define(ERLANG_SERVER_NAME, idl_erlang_server).
+-define(C_CLIENT_NODE_NAME, c_client_idl_test).
+
+%% Add/remove code path and watchdog before/after each test case.
+%%
+init_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:add_patha(DataDir),
+
+ %% Since other test suites use the module m_i, we have
+ %% to make sure we are using the right m_i module.
+ code:purge(m_i),
+ code:load_file(m_i),
+
+ WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:del_path(DataDir),
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [void_test, long_test, long_long_test,
+ unsigned_short_test, unsigned_long_test,
+ unsigned_long_long_test, double_test, char_test,
+ wchar_test, octet_test, bool_test, struct_test,
+ struct2_test, seq1_test, seq2_test, seq3_test,
+ seq4_test, seq5_test, array1_test, array2_test,
+ enum_test, string1_test, string2_test, string3_test,
+ string4_test, pid_test, port_test, ref_test, term_test,
+ typedef_test, inline_sequence_test, term_sequence_test,
+ term_struct_test, wstring1_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+array1_test(doc) -> "";
+array1_test(suite) -> [];
+array1_test(Config) ->
+ do_test(array1_test, Config).
+
+array2_test(doc) -> "";
+array2_test(suite) -> [];
+array2_test(Config) ->
+ do_test(array2_test, Config).
+
+bool_test(doc) -> "";
+bool_test(suite) -> [];
+bool_test(Config) ->
+ do_test(bool_test, Config).
+
+char_test(doc) -> "";
+char_test(suite) -> [];
+char_test(Config) ->
+ do_test(char_test, Config).
+
+double_test(doc) -> "";
+double_test(suite) -> [];
+double_test(Config) ->
+ do_test(double_test, Config).
+
+enum_test(doc) -> "";
+enum_test(suite) -> [];
+enum_test(Config) ->
+ do_test(enum_test, Config).
+
+inline_sequence_test(doc) -> "";
+inline_sequence_test(suite) -> [];
+inline_sequence_test(Config) ->
+ do_test(inline_sequence_test, Config).
+
+long_long_test(doc) -> "";
+long_long_test(suite) -> [];
+long_long_test(Config) ->
+ do_test(long_long_test, Config).
+
+long_test(doc) -> "";
+long_test(suite) -> [];
+long_test(Config) ->
+ do_test(long_test, Config).
+
+octet_test(doc) -> "";
+octet_test(suite) -> [];
+octet_test(Config) ->
+ do_test(octet_test, Config).
+
+pid_test(doc) -> "";
+pid_test(suite) -> [];
+pid_test(Config) ->
+ do_test(pid_test, Config).
+
+port_test(doc) -> "";
+port_test(suite) -> [];
+port_test(Config) ->
+ do_test(port_test, Config).
+
+ref_test(doc) -> "";
+ref_test(suite) -> [];
+ref_test(Config) ->
+ do_test(ref_test, Config).
+
+seq1_test(doc) -> "";
+seq1_test(suite) -> [];
+seq1_test(Config) ->
+ do_test(seq1_test, Config).
+
+seq2_test(doc) -> "";
+seq2_test(suite) -> [];
+seq2_test(Config) ->
+ do_test(seq2_test, Config).
+
+seq3_test(doc) -> "";
+seq3_test(suite) -> [];
+seq3_test(Config) ->
+ do_test(seq3_test, Config).
+
+seq4_test(doc) -> "";
+seq4_test(suite) -> [];
+seq4_test(Config) ->
+ do_test(seq4_test, Config).
+
+seq5_test(doc) -> "";
+seq5_test(suite) -> [];
+seq5_test(Config) ->
+ do_test(seq5_test, Config).
+
+string1_test(doc) -> "";
+string1_test(suite) -> [];
+string1_test(Config) ->
+ do_test(string1_test, Config).
+
+string2_test(doc) -> "";
+string2_test(suite) -> [];
+string2_test(Config) ->
+ do_test(string2_test, Config).
+
+string3_test(doc) -> "";
+string3_test(suite) -> [];
+string3_test(Config) ->
+ do_test(string3_test, Config).
+
+string4_test(doc) -> "";
+string4_test(suite) -> [];
+string4_test(Config) ->
+ do_test(string4_test, Config).
+
+struct2_test(doc) -> "";
+struct2_test(suite) -> [];
+struct2_test(Config) ->
+ do_test(struct2_test, Config).
+
+struct_test(doc) -> "";
+struct_test(suite) -> [];
+struct_test(Config) ->
+ do_test(struct_test, Config).
+
+term_sequence_test(doc) -> "";
+term_sequence_test(suite) -> [];
+term_sequence_test(Config) ->
+ do_test(term_sequence_test, Config).
+
+term_struct_test(doc) -> "";
+term_struct_test(suite) -> [];
+term_struct_test(Config) ->
+ do_test(term_struct_test, Config).
+
+term_test(doc) -> "";
+term_test(suite) -> [];
+term_test(Config) ->
+ do_test(term_test, Config).
+
+typedef_test(doc) -> "";
+typedef_test(suite) -> [];
+typedef_test(Config) ->
+ do_test(typedef_test, Config).
+
+unsigned_long_long_test(doc) -> "";
+unsigned_long_long_test(suite) -> [];
+unsigned_long_long_test(Config) ->
+ do_test(unsigned_long_long_test, Config).
+
+unsigned_long_test(doc) -> "";
+unsigned_long_test(suite) -> [];
+unsigned_long_test(Config) ->
+ do_test(unsigned_long_test, Config).
+
+unsigned_short_test(doc) -> "";
+unsigned_short_test(suite) -> [];
+unsigned_short_test(Config) ->
+ do_test(unsigned_short_test, Config).
+
+void_test(doc) -> "";
+void_test(suite) -> [];
+void_test(Config) ->
+ do_test(void_test, Config).
+
+wchar_test(doc) -> "";
+wchar_test(suite) -> [];
+wchar_test(Config) ->
+ do_test(wchar_test, Config).
+
+wstring1_test(doc) -> "";
+wstring1_test(suite) -> [];
+wstring1_test(Config) ->
+ do_test(wstring1_test, Config).
+
+
+%% It is here that all tests really are done.
+%%
+
+do_test(Case, Config) ->
+ %% Trap exits
+ process_flag(trap_exit, true),
+ %% Start the server
+ {ok, _Pid} = m_i:oe_create_link([], {local, ?ERLANG_SERVER_NAME}),
+ Node = atom_to_list(node()),
+ %% [NodeName, HostName] = string:tokens(Node, "@"),
+ DataDir = ?config(data_dir, Config),
+ %% io:format("~p: data directory: ~p~n", [?MODULE, DataDir]),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ %% Start C-client node as a port program.
+ Cmd = filename:join([DataDir, "c_client"]) ++
+ " -this-node-name " ++ atom_to_list(?C_CLIENT_NODE_NAME) ++
+ " -peer-node " ++ Node ++
+ " -peer-process-name " ++ atom_to_list(?ERLANG_SERVER_NAME) ++
+ " -cookie " ++ Cookie ++
+ " -test-case " ++ atom_to_list(Case),
+ Port = open_port({spawn, Cmd}, [exit_status, eof, stderr_to_stdout]),
+ Res = wait_for_completion(Port),
+ %% Kill off node if there was timeout
+ case Res of
+ {error, timeout} ->
+ catch rpc:cast(?C_CLIENT_NODE_NAME, erlang, halt, [1]);
+ _ ->
+ ok
+ end,
+ process_flag(trap_exit, false),
+ catch m_i:stop(?ERLANG_SERVER_NAME),
+ ok = Res.
+
+
+%% Wait for eof *and* exit status, but return if exit status indicates
+%% an error, or we have been waiting more than PORT_TIMEOUT seconds.
+%%
+wait_for_completion(Port) ->
+ wait_for_completion(Port, 0).
+
+wait_for_completion(Port, N) when N < 2 ->
+ receive
+ {Port, {data, Bytes}} ->
+ %% Relay output
+ io:format("~s", [Bytes]),
+ wait_for_completion(Port, N);
+ {Port, {exit_status, 0}} ->
+ wait_for_completion(Port, N + 1);
+ {Port, {exit_status, Status}} ->
+ {error, Status};
+ {Port, eof} ->
+ wait_for_completion(Port, N + 1);
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason: ~w~n", [Reason]),
+ wait_for_completion(Port, N);
+ {'EXIT', From, Reason} ->
+ io:format("Got unexpected exit: ~p~n", [{'EXIT', From, Reason}]),
+ wait_for_completion(Port, N)
+ after ?PORT_TIMEOUT ->
+ {error, timeout}
+ end;
+wait_for_completion(_, _) ->
+ ok.
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..8bc1a907a7
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src
@@ -0,0 +1,146 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Makefile.src for c_client_erl_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .h .erl .idl @obj@ .@EMULATOR@
+
+
+# Variables from ts:
+#
+
+ERL_INCLUDE = @erl_include@
+
+IC_INCLUDE_PATH = @ic_include_path@
+IC_LIB = @ic_libpath@@DS@@ic_lib@
+
+ERL_INTERFACE_INCLUDE = @erl_interface_include@
+ERL_INTERFACE_LIB = @erl_interface_libpath@@DS@@erl_interface_lib@
+ERL_INTERFACE_EILIB = @erl_interface_libpath@@DS@@erl_interface_eilib@
+ERL_INTERFACE_THREADLIB = @erl_interface_threadlib@
+ERL_INTERFACE_SOCK_LIBS = @erl_interface_sock_libs@
+
+CC = @CC@
+## XXX Should set warning flag with a DEBUG_FLAG
+CFLAGS = @CFLAGS@ @DEFS@ -I@erl_include@ \
+ -I@ic_include_path@ -I@erl_interface_include@
+
+LD = @LD@
+LDFLAGS = @CROSSLDFLAGS@
+LIBS = $(IC_LIB) $(ERL_INTERFACE_LIB) $(ERL_INTERFACE_EILIB) \
+ $(ERL_INTERFACE_THREADLIB) @LIBS@ $(ERL_INTERFACE_SOCK_LIBS)
+ERLC = erlc
+
+# Generated C header files
+GEN_H_FILES = \
+ m.h \
+ m_i.h \
+ oe_c_erl_test.h
+
+# Generated C files
+GEN_C_FILES = \
+ m.c \
+ m_i.c \
+ oe_c_erl_test.c \
+ oe_code_m_a.c \
+ oe_code_m_arr1.c \
+ oe_code_m_arr2.c \
+ oe_code_m_arr3.c \
+ oe_code_m_aseq.c \
+ oe_code_m_b.c \
+ oe_code_m_bseq.c \
+ oe_code_m_dd.c \
+ oe_code_m_dyn.c \
+ oe_code_m_dyn_sl.c \
+ oe_code_m_es.c \
+ oe_code_m_et.c \
+ oe_code_m_etseq.c \
+ oe_code_m_fruit.c \
+ oe_code_m_lseq.c \
+ oe_code_m_s.c \
+ oe_code_m_s_sl.c \
+ oe_code_m_sarr3.c \
+ oe_code_m_simple.c \
+ oe_code_m_ssarr3.c \
+ oe_code_m_sseq.c \
+ oe_code_m_ssstr3.c \
+ oe_code_m_sstr3.c \
+ oe_code_m_str1.c \
+ oe_code_m_str3.c \
+ oe_code_m_strRec.c \
+ oe_code_m_strRec_str5.c \
+ oe_code_m_strRec_str7.c
+
+GEN_HRL_FILES = \
+ m.hrl \
+ m_i.hrl \
+ oe_c_erl_test.hrl
+
+GEN_ERL_FILES = \
+ m.erl \
+ m_arr2.erl \
+ m_arr3.erl \
+ m_i.erl \
+ m_str3.erl \
+ oe_c_erl_test.erl
+
+C_FILES = $(GEN_C_FILES) c_client.c my.c
+
+OBJS = $(C_FILES:.c=@obj@)
+
+PGMS = c_client@exe@
+
+ERL_FILES = $(GEN_ERL_FILES) m_i_impl.erl
+
+EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
+
+
+all: $(PGMS) $(EBINS)
+
+clean:
+ -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+ -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+
+$(PGMS): $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" \
+ "+{user_protocol,my}" c_erl_test.idl
+
+$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl
+
+.c@obj@:
+ $(CC) -c -o $*@obj@ $(CFLAGS) $<
+
+.erl.@EMULATOR@:
+ $(ERLC) -I $(IC_INCLUDE_PATH) $<
+
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
new file mode 100644
index 0000000000..f352b91fd5
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_client.c
@@ -0,0 +1,1763 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+/* C-client for test of IC.
+ *
+ * TODO:
+ *
+ * 1. XXX #includes for VxWorks, Windows
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+
+#include <string.h>
+
+#ifdef __WIN32__
+# include <time.h>
+# include <sys/timeb.h>
+#elif defined VXWORKS
+#include <time.h>
+#include <sys/times.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <ctype.h>
+
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+
+#include "ei.h"
+#include "erl_interface.h"
+#include "m_i.h"
+
+#define HOSTNAMESZ 256
+#define NODENAMESZ 512
+
+#define INBUFSZ 10
+#define OUTBUFSZ 0
+
+#define MAXTRIES 5
+
+#define CHECK_EXCEPTION(x) \
+ if ((x)->_major != CORBA_NO_EXCEPTION) { \
+ fprintf(stderr,"\n\nException: %s\n\n", \
+ (char *)CORBA_exception_value((x))); \
+ CORBA_exception_free((x)); \
+ return -1; \
+ } \
+
+/* XXX Should free things here too! */
+#define RETURN_IF_OK(x) \
+ if ((x)) {\
+ fprintf(stdout, "ok\n");\
+ return 0;\
+ }\
+
+#define cmp_str(x,y) (!strcmp((x),(y)))
+#define cmp_wstr(x,y) (!ic_wstrcmp((x),(y)))
+
+typedef CORBA_Environment IC_Env;
+
+typedef int (*TestFunc)(IC_Env *);
+typedef struct {
+ char *name;
+ TestFunc func;
+} TestCase;
+
+static char longtext[] =
+"Introduction The IC application is an IDL compiler implemented in Erlang."
+" The IDL compiler generates client stubs and server skeletons."
+" Several back-ends are supported, and they fall into three main groups."
+" For more details on IC compiler options consult the ic(3) manual page."
+" Argument passing cases 1 Caller allocates all necessary storage,"
+" except that which may be encapsulated and managed within the parameter itself."
+" 2 The caller allocates a pointer and passes it by reference to the callee."
+" The callee sets the pointer to point to a valid instance of the parameter's type."
+" The caller is responsible for releasing the returned storage."
+" Following completion of a request, the caller is not allowed to modify any values"
+" in the returned storage. To do so the caller must first copy the returned instance"
+" into a new instance, then modify the new instance. 3 The caller allocates a"
+" pointer to an array slice which has all the same dimensions of the original"
+" array except the first, and passes it by reference to the callee. The callee sets"
+" the pointer to point to a valid instance of the array. The caller is responsible for"
+" releasing the returned storage. Following completion of a request, the caller is not"
+" allowed to modify any values in the returned storage. To do so the caller must first"
+" copy the returned instance into a new instance, then modify the new instance."
+" Generated Files Two files will be generated for each scope. One set of files will be"
+" generated for each module and each interface scope. An extra set is generated for"
+" those definitions at top level scope. One of the files is a header file(.h), and the"
+" other file is a C source code file (.c). In addition to these files a number of C"
+" source files will be generated for type encodings, they are named according to the "
+"following template: oe_code_<type>.c.";
+static char this_node[NODENAMESZ + 1];
+static char *progname;
+
+/* Test function prototypes */
+
+static int void_test(IC_Env *env);
+static int long_test(IC_Env *env);
+static int long_long_test(IC_Env *env);
+static int unsigned_short_test(IC_Env *env);
+static int unsigned_long_test(IC_Env *env);
+static int unsigned_long_long_test(IC_Env *env);
+static int double_test(IC_Env *env);
+static int char_test(IC_Env *env);
+static int wchar_test(IC_Env *env);
+static int octet_test(IC_Env *env);
+static int bool_test(IC_Env *env);
+static int struct_test(IC_Env *env);
+static int struct2_test(IC_Env *env);
+static int seq1_test(IC_Env *env);
+static int seq2_test(IC_Env *env);
+static int seq3_test(IC_Env *env);
+static int seq4_test(IC_Env *env);
+static int seq5_test(IC_Env *env);
+static int array1_test(IC_Env *env);
+static int array2_test(IC_Env *env);
+static int enum_test(IC_Env *env);
+static int string1_test(IC_Env *env);
+static int string2_test(IC_Env *env);
+static int string3_test(IC_Env *env);
+static int string4_test(IC_Env *env);
+static int pid_test(IC_Env *env);
+static int port_test(IC_Env *env);
+static int ref_test(IC_Env *env);
+static int term_test(IC_Env *env);
+static int typedef_test(IC_Env *env);
+static int inline_sequence_test(IC_Env *env);
+static int term_sequence_test(IC_Env *env);
+static int term_struct_test(IC_Env *env);
+static int wstring1_test(IC_Env *env);
+
+static TestCase test_cases[] = {
+ {"void_test", void_test},
+ {"long_test", long_test},
+ {"long_long_test", long_long_test},
+ {"unsigned_short_test", unsigned_short_test},
+ {"unsigned_long_test", unsigned_long_test},
+ {"unsigned_long_long_test", unsigned_long_long_test},
+ {"double_test", double_test},
+ {"char_test", char_test},
+ {"wchar_test", wchar_test},
+ {"octet_test", octet_test},
+ {"bool_test", bool_test},
+ {"struct_test", struct_test},
+ {"struct2_test", struct2_test},
+ {"seq1_test", seq1_test},
+ {"seq2_test", seq2_test},
+ {"seq3_test", seq3_test},
+ {"seq4_test", seq4_test},
+ {"seq5_test", seq5_test},
+ {"array1_test", array1_test},
+ {"array2_test", array2_test},
+ {"enum_test", enum_test},
+ {"string1_test", string1_test},
+ {"string2_test", string2_test},
+ {"string3_test", string3_test},
+ {"string4_test", string4_test},
+ {"pid_test", pid_test},
+ {"port_test", port_test},
+ {"ref_test", ref_test},
+ {"term_test", term_test},
+ {"typedef_test", typedef_test},
+ {"inline_sequence_test", inline_sequence_test},
+ {"term_sequence_test", term_sequence_test},
+ {"term_struct_test", term_struct_test},
+ {"wstring1_test", wstring1_test},
+ {"", NULL}
+};
+
+/* Other prototypes */
+static int cmp_aseq(m_aseq *a1, m_aseq *a2);
+static int cmp_a(m_a *a1, m_a *a2);
+static int cmp_bseq(m_bseq *b1, m_bseq *b2);
+static int cmp_b(m_b *b1, m_b *b2);
+static int cmp_lseq(m_lseq *b1, m_lseq *b2);
+static int cmp_etseq(m_etseq *b1, m_etseq *b2);
+static int cmp_et(m_et* b1, m_et *b2);
+static int cmp_es(m_es *b1, m_es *b2);
+static int cmp_arr1(m_arr1 b1, m_arr1 b2);
+static int cmp_dd(m_dd b1, m_dd b2);
+static int cmp_strRec(m_strRec *b1, m_strRec *b2);
+static int cmp_sseq(m_sseq *b1, m_sseq *b2);
+static int cmp_pid(erlang_pid *p1, erlang_pid *p2);
+static int cmp_port(erlang_port *p1, erlang_port *p2);
+static int cmp_ref(erlang_ref *p1, erlang_ref *p2);
+static int cmp_s(m_s *b1, m_s *b2);
+static int cmp_ssstr3(m_ssstr3 *b1, m_ssstr3 *b2);
+static int cmp_ssarr3(m_ssarr3 *b1, m_ssarr3 *b2);
+static int cmp_sarr3(m_sarr3 *b1, m_sarr3 *b2);
+static int cmp_arr3(m_arr3 b1, m_arr3 b2);
+
+static void print_aseq(m_aseq *a);
+static void print_a(m_a *a);
+static void print_bseq(m_bseq *b);
+static void print_lseq(m_lseq *b);
+static void print_b(m_b *b);
+static void print_etseq(m_etseq *b);
+static void print_et(m_et* b);
+static void print_es(m_es *b);
+static void print_arr1(long a[500]);
+static void print_dd(long a[2][3]);
+static void print_strRec(m_strRec* sr);
+static void print_sseq(m_sseq *b);
+static void print_pid(erlang_pid *p);
+static void print_port(erlang_port *p);
+static void print_ref(erlang_ref *p);
+static void print_term(ETERM *t);
+static void print_s(m_s *p);
+static void print_ssstr3(m_ssstr3 *b1);
+static void print_ssarr3(m_ssarr3 *b1);
+static void print_sarr3(m_sarr3 *b1);
+static void print_arr3(m_arr3 b1);
+static void print_wstr(CORBA_wchar *ws);
+
+static void free_etseq_buf(m_etseq *b);
+static void free_et(m_et* b);
+
+#ifdef __WIN32__
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} MyTimeval;
+#else
+typedef struct timeval MyTimeval;
+#endif
+static void my_gettimeofday(MyTimeval *tv);
+static void showtime(MyTimeval *start, MyTimeval *stop);
+static void usage(void);
+static void done(int r);
+
+
+
+/* main */
+
+#ifdef VXWORKS
+int client(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ struct hostent *hp;
+ erlang_pid pid;
+ MyTimeval start, stop;
+ int i, fd, ires, tres;
+ IC_Env *env;
+ int tries = 0;
+ char *this_node_name = NULL;
+ char *peer_node = NULL;
+ char *peer_process_name = NULL;
+ char *cookie = NULL;
+ char host[HOSTNAMESZ + 1];
+ TestFunc test_func = NULL;
+ TestCase *test_case;
+ char *test_case_name = NULL;
+
+#ifdef __WIN32__
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ fprintf(stderr, "Could not load winsock2 v2.0 compatible DLL");
+ exit(1);
+ }
+#endif
+
+ progname = argv[0];
+ host[HOSTNAMESZ] = '\0';
+ if (gethostname(host, HOSTNAMESZ) < 0) {
+ fprintf(stderr, "Can't find own hostname\n");
+ done(1);
+ }
+ if ((hp = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Can't get ip address for host %s\n", host);
+ done(1);
+ }
+ for (i = 1; i < argc; i++) {
+ if (cmp_str(argv[i], "-help")) {
+ usage();
+ done(0);
+ } else if (cmp_str(argv[i], "-this-node-name")) {
+ i++;
+ this_node_name = argv[i];
+ } else if (cmp_str(argv[i], "-peer-node")) {
+ i++;
+ peer_node = argv[i];
+ } else if (cmp_str(argv[i], "-peer-process-name")) {
+ i++;
+ peer_process_name = argv[i];
+ } else if (cmp_str(argv[i], "-cookie")) {
+ i++;
+ cookie = argv[i];
+ } else if (cmp_str(argv[i], "-test-case")) {
+ i++;
+ test_case_name = argv[i];
+ } else {
+ fprintf(stderr, "Error : invalid argument \"%s\"\n", argv[i]);
+ usage();
+ done(1);
+ }
+ }
+
+ if (this_node_name == NULL || peer_node == NULL || test_case_name == NULL
+ || peer_process_name == NULL || cookie == NULL) {
+ fprintf(stderr, "Error: missing option\n");
+ usage();
+ done(1);
+ }
+
+ test_case = test_cases;
+ while (test_case->func) {
+ if (cmp_str(test_case->name, test_case_name)) {
+ test_func = test_case->func;
+ break;
+ }
+ test_case++;
+ }
+ if (test_func == NULL) {
+ fprintf(stderr, "Error: illegal test case: \"%s\"\n", test_case_name);
+ done(1);
+ }
+
+ /* Behead hostname at first dot */
+ for (i=0; host[i] != '\0'; i++) {
+ if (host[i] == '.') { host[i] = '\0'; break; }
+ }
+ sprintf(this_node, "%s@%s", this_node_name, host);
+ fprintf(stderr, "c_client: this node: \"%s\"\n", this_node);
+ fprintf(stderr, "c_client: peer node: \"%s\"\n", peer_node);
+ fprintf(stderr, "c_client: test case: \"%s\"\n", test_case_name);
+
+ fprintf(stderr, "c_client: starting\n");
+
+ /* initialize erl_interface */
+ erl_init(NULL, 0);
+
+ for (tries = 0; tries < MAXTRIES; tries++) {
+
+ /* connect to erlang node */
+
+ ires = erl_connect_xinit(host, this_node_name, this_node,
+ (struct in_addr *)*hp->h_addr_list,
+ cookie, 0);
+
+ fprintf(stderr, "c_client: erl_connect_xinit(): %d\n", ires);
+
+ fd = erl_connect(peer_node);
+ fprintf(stderr, "c_client: erl_connect(): %d\n", fd);
+
+ if (fd >= 0)
+ break;
+ fprintf(stderr, "c_client: cannot connect, retrying\n");
+ }
+ if (fd < 0) {
+ fprintf(stderr, "c_client: cannot connect, exiting\n");
+ done(1);
+ }
+ env = CORBA_Environment_alloc(INBUFSZ, OUTBUFSZ);
+ env->_fd = fd;
+ strcpy(env->_regname, peer_process_name);
+ env->_to_pid = NULL;
+ env->_from_pid = &pid;
+
+ strcpy(pid.node, this_node);
+ pid.num = fd;
+ pid.serial = 0;
+ pid.creation = 0;
+
+ my_gettimeofday(&start);
+ tres = test_func(env); /* Call test case */
+ my_gettimeofday(&stop);
+ showtime(&start, &stop);
+ erl_close_connection(fd);
+
+ printf("c_client: env->_inbuf before : %d\n", INBUFSZ);
+ printf("c_client: env->_outbuf before : %d\n", OUTBUFSZ);
+ printf("c_client: env->_inbuf after : %d\n", env->_inbufsz);
+ printf("c_client: env->_outbuf after : %d\n", env->_outbufsz);
+
+ CORBA_free(env->_inbuf);
+ CORBA_free(env->_outbuf);
+ CORBA_free(env);
+ done(tres);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [-help] -this-node-name <name> "
+ "-peer-node <nodename> -peer-process-name <name> "
+ "-cookie <cookie> -test-case <test case name>\n", progname);
+ fprintf(stderr, "Example:\n %s -this-node-name kalle "
+ "-peer-node olle@home -peer-process-name idltest "
+ "-cookie oa678er -test-case octet_test\n", progname);
+}
+
+static void done(int r)
+{
+#ifdef __WIN32__
+ WSACleanup();
+#endif
+ exit(r);
+}
+
+
+/* TESTS */
+
+static int void_test(IC_Env *env)
+{
+ fprintf(stdout, "\n======== m_i_void test ======\n\n");
+ m_i_void_test(NULL,env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(1);
+}
+
+static int long_test(IC_Env *env)
+{
+ long l = 4711, lo, lr;
+
+ fprintf(stdout, "\n======== m_i_long test ======\n\n");
+ lr = m_i_long_test(NULL, l, &lo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(l == lo && l == lr);
+ if (l != lo)
+ fprintf(stdout, " out parameter error, sent: %ld, got: %ld\n", l, lo);
+ if (l != lr)
+ fprintf(stdout, " result error, sent: %ld, got: %ld\n", l, lr);
+ return -1;
+}
+
+static int long_long_test(IC_Env *env)
+{
+ CORBA_long_long ll = 4711, llo, llr;
+
+ fprintf(stdout, "\n======== m_i_longlong test ======\n\n");
+ llr = m_i_longlong_test(NULL, ll, &llo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ll == llo && ll == llr);
+ if (ll != llo)
+ fprintf(stdout, " out parameter error, sent: %ld, got: %ld\n",
+ ll, llo);
+ if (ll != llr)
+ fprintf(stdout, " result error, sent: %ld, got: %ld\n", ll, llr);
+ return -1;
+}
+
+static int unsigned_short_test(IC_Env *env)
+{
+ unsigned short x, y = 2, z;
+
+ fprintf(stdout, "\n======== m_i_ushort test ======\n\n");
+ x = m_i_ushort_test(NULL, y, &z, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(y == z && y == x);
+ if (y != z)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", y, z);
+ if (y != x)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", y, x);
+ return -1;
+}
+
+
+static int unsigned_long_test(IC_Env *env)
+{
+ unsigned long ul = 5050, ulo, ulr;
+
+ fprintf(stdout, "\n======== m_i_ulong test ======\n\n");
+ ulr = m_i_ulong_test(NULL, ul, &ulo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ul == ulo && ul == ulr);
+ if (ul != ulo)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ ul, ulo);
+ if (ul != ulr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n", ul, ulr);
+ return -1;
+}
+
+/*
+ * Note: CORBA_unsigned_long_long is in fact a plain long.
+ */
+static int unsigned_long_long_test(IC_Env *env)
+{
+ CORBA_unsigned_long_long ull = 5050, ullo, ullr;
+
+ fprintf(stdout, "\n======== m_i_ulonglong test ======\n\n");
+ ullr = m_i_ulonglong_test(NULL, ull, &ullo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ull == ullo && ull == ullr);
+ if (ull != ullo)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ ull, ullo);
+ if (ull != ullr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n",
+ ull, ullr);
+ return -1;
+}
+
+static int double_test(IC_Env *env)
+{
+ double d = 12.1212, db, dr;
+
+ fprintf(stdout, "\n======== m_i_double test ======\n\n");
+ dr = m_i_double_test(NULL, d, &db, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(d == db && d == dr);
+ if (d != db)
+ fprintf(stdout, " out parameter error, sent: %f, got: %f\n", d, db);
+ if (d != dr)
+ fprintf(stdout, " result error, sent: %f, got: %f\n", d, dr);
+ return -1;
+}
+
+static int char_test(IC_Env *env)
+{
+ char c = 'g', co, cr;
+
+ /* char test */
+ fprintf(stdout, "\n======== m_i_char test ======\n\n");
+ cr = m_i_char_test(NULL, c, &co, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(c == co && c == cr);
+ if (c !=co)
+ fprintf(stdout, " out parameter error, sent: %c, got: %c\n", c, co);
+ if (c != cr)
+ fprintf(stdout, " result error, sent: %c, got: %c\n", c, cr);
+ return -1;
+}
+
+static int wchar_test(IC_Env *env)
+{
+ CORBA_wchar wc = 103, wco, wcr;
+
+ fprintf(stdout, "\n======== m_i_wchar test ======\n\n");
+ wcr = m_i_wchar_test(NULL, wc, &wco, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(wc == wco && wc == wcr);
+ if (wc != wco)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ wc, wco);
+ if (wc != wcr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n",
+ wc, wcr);
+ return -1;
+}
+
+static int octet_test(IC_Env *env)
+{
+ char o ='r', oo, or;
+
+ fprintf(stdout, "\n======== m_i_octet test ======\n\n");
+ or = m_i_octet_test(NULL, o, &oo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(o == oo && o == or);
+ if (o != oo)
+ fprintf(stdout, " out parameter error, sent: %c, got: %c\n", o, oo);
+ if (o != or)
+ fprintf(stdout, " result error, sent: %c, got: %c\n", o, or);
+ return -1;
+}
+
+static int bool_test(IC_Env *env)
+{
+ unsigned char i = 0, io, ir;
+
+ fprintf(stdout, "\n======== m_i_bool test ======\n\n");
+ ir = m_i_bool_test(NULL, i, &io, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(i == io && i == ir);
+ if (i != io)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", i, io);
+ if (i != ir)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", i, ir);
+ return -1;
+}
+
+static int struct_test(IC_Env *env)
+{
+ m_b b = {4711, 'a'}, bo, br;
+
+ fprintf(stdout, "\n======== m_i_struct test ======\n\n");
+ br = m_i_struct_test(NULL, &b, &bo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_b(&b, &bo) && cmp_b(&b, &br));
+ if (!cmp_b(&b, &bo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_b(&b);
+ fprintf(stdout, " got:\n");
+ print_b(&bo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_b(&b, &br)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_b(&b);
+ fprintf(stdout, " got:\n");
+ print_b(&br);
+ fprintf(stdout, "\n");
+ }
+ return -1;
+}
+
+static int struct2_test(IC_Env *env)
+{
+ m_es esi = {m_peach, 5050}, eso, esr;
+
+ fprintf(stdout, "\n======== m_i_struct2 test ======\n\n");
+ esr = m_i_struct2_test(NULL, &esi, &eso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_es(&esi, &eso) && cmp_es(&esi, &esr));
+ if (!cmp_es(&esi, &eso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_es(&esi);
+ fprintf(stdout, " got:\n");
+ print_es(&eso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_es(&esi, &esr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_es(&esi);
+ fprintf(stdout, " got:\n");
+ print_es(&esr);
+ fprintf(stdout, "\n");
+ }
+ return -1;
+}
+
+
+static int seq1_test(IC_Env *env)
+{
+ m_bseq bs, *bso, *bsr;
+
+ m_b ba[3] = {{4711, 'a'}, {4712, 'b'}, {4713, 'c'}};
+ bs._length = 3;
+ bs._buffer = ba;
+
+ fprintf(stdout, "\n======== m_i_seq1 test ======\n\n");
+ bsr = m_i_seq1_test(NULL, &bs, &bso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_bseq(&bs, bso) && cmp_bseq(&bs, bsr));
+ if (!cmp_bseq(&bs, bso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_bseq(&bs);
+ fprintf(stdout, " got:\n");
+ print_bseq(bso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_bseq(&bs, bsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_bseq(&bs);
+ fprintf(stdout, " got:\n");
+ print_bseq(bsr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(bso);
+ CORBA_free(bsr);
+ return -1;
+}
+
+static int seq2_test(IC_Env *env)
+{
+ m_b ba[3] = {{4711, 'a'}, {4712, 'b'}, {4713, 'c'}};
+ m_a a;
+ m_a aa[2];
+ m_aseq as, *aso, *asr;
+
+ a.l = 9999;
+ a.y._length = 3;
+ a.y._buffer = ba;
+ a.d = 66.89898989;
+
+ aa[0] = a;
+ aa[1] = a;
+ as._length = 2;
+ as._buffer = aa;
+
+ fprintf(stdout, "\n======== m_i_seq2 test ======\n\n");
+ asr = m_i_seq2_test(NULL, &as, &aso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_aseq(&as, aso) && cmp_aseq(&as, asr));
+ if (!cmp_aseq(&as, aso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_aseq(&as);
+ fprintf(stdout, " got:\n");
+ print_aseq(aso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_aseq(&as, asr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_aseq(&as);
+ fprintf(stdout, " got:\n");
+ print_aseq(asr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(aso);
+ CORBA_free(asr);
+ return -1;
+}
+
+static int seq3_test(IC_Env *env)
+{
+ m_lseq lsi, *lso, *lsr;
+ long al[500];
+ int i=0;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+ lsi._length = 500;
+ lsi._buffer = al;
+
+ fprintf(stdout, "\n======== m_i_seq3 test ======\n\n");
+ lsr = m_i_seq3_test(NULL, &lsi, &lso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_lseq(&lsi, lso) && cmp_lseq(&lsi, lsr));
+ if (!cmp_lseq(&lsi, lso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_lseq(&lsi);
+ fprintf(stdout, " got:\n");
+ print_lseq(lso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_lseq(&lsi, lsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_lseq(&lsi);
+ fprintf(stdout, " got:\n");
+ print_lseq(lsr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(lso);
+ CORBA_free(lsr);
+ return -1;
+}
+
+static int seq4_test(IC_Env *env)
+{
+ char *stra0[3] = {"a", "long", "time"};
+ char *stra1[3] = {"ago", "there", "was"};
+ char *stra2[3] = {"a", "buggy", "compiler"};
+ m_sstr3 str3s[3] = {{3, 3, stra0}, {3, 3, stra1}, {3, 3, stra2}};
+ m_ssstr3 str3ssi = {3, 3, str3s};
+ m_ssstr3 *str3sso, *str3ssr;
+
+ fprintf(stdout, "\n======== m_i_seq4 test ======\n\n");
+ str3ssr = m_i_seq4_test(NULL, &str3ssi, &str3sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ssstr3(&str3ssi, str3sso) &&
+ cmp_ssstr3(&str3ssi, str3ssr));
+ if (!cmp_ssstr3(&str3ssi, str3sso)){
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ssstr3(&str3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssstr3(str3sso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_ssstr3(&str3ssi, str3ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ssstr3(&str3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssstr3(str3ssr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(str3sso);
+ CORBA_free(str3ssr);
+ return -1;
+}
+
+static int seq5_test(IC_Env *env)
+{
+ m_arr3 arr3a[3] = {
+ {4711, 18931947, 3},
+ {4711, 18931947, 3},
+ {4711, 18931947, 3}};
+ m_sarr3 arr3sa[3] = {{3, 3, arr3a}, {3, 3, arr3a}, {3, 3, arr3a}};
+ m_ssarr3 arr3ssi = {3, 3, arr3sa};
+ m_ssarr3 *arr3sso;
+ m_ssarr3 *arr3ssr;
+
+ fprintf(stdout, "\n======== m_i_seq5 test ======\n\n");
+ arr3ssr = m_i_seq5_test(NULL, &arr3ssi, &arr3sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ssarr3(&arr3ssi, arr3sso) &&
+ cmp_ssarr3(&arr3ssi, arr3ssr));
+ if (!cmp_ssarr3(&arr3ssi, arr3sso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ssarr3(&arr3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssarr3(arr3sso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_ssarr3(&arr3ssi, arr3ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ssarr3(&arr3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssarr3(arr3ssr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(arr3sso);
+ CORBA_free(arr3ssr);
+ return -1;
+}
+
+static int array1_test(IC_Env *env)
+{
+ int i;
+ long al[500];
+ m_arr1 alo;
+ m_arr1_slice* alr;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+
+ fprintf(stdout, "\n======== m_i_array1 test ======\n\n");
+ alr = m_i_array1_test(NULL, al, alo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_arr1(al, alo) && cmp_arr1(al, alr));
+ if (!cmp_arr1(al, alo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_arr1(al);
+ fprintf(stdout, " got:\n");
+ print_arr1(alo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_arr1(al,alr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_arr1(al);
+ fprintf(stdout, " got:\n");
+ print_arr1(alr);
+ fprintf(stdout, "\n");
+ }
+ free(alo);
+ free(alr);
+ return -1;
+}
+
+static int array2_test(IC_Env *env)
+{
+ long dl[2][3] = {{11, 2, 7}, {22, 8 ,13}};
+ m_dd dlo;
+ m_dd_slice* dlr;
+
+ fprintf(stdout, "\n======== m_i_array2 test ======\n\n");
+ dlr = m_i_array2_test(NULL, dl, dlo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_dd(dl,dlo) && cmp_dd(dl,dlr));
+ if (!cmp_dd(dl,dlo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_dd(dl);
+ fprintf(stdout, " got:\n");
+ print_dd(dlo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_dd(dl,dlr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_dd(dl);
+ fprintf(stdout, " got:\n");
+ print_dd(dlr);
+ fprintf(stdout, "\n");
+ }
+ free(*dlr);
+ return -1;
+}
+
+static int enum_test(IC_Env *env)
+{
+ m_fruit ei = m_banana, eo, er;
+
+ fprintf(stdout, "\n======== m_i_enum test ======\n\n");
+ er = m_i_enum_test(NULL, ei, &eo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ei == eo && ei == er);
+ if (ei != eo)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", ei, eo);
+ if (ei != er)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", ei, er);
+ return -1;
+}
+
+static int string1_test(IC_Env *env)
+{
+ char* si = longtext;
+ char* so;
+ char* sr;
+
+ fprintf(stdout, "\n======== m_i_string1 test ======\n\n");
+ sr = m_i_string1_test(NULL, si, &so, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_str(si, so) && cmp_str(si, sr));
+ if (!cmp_str(si, so))
+ fprintf(stdout, " out parameter error, sent: %s, got: %s\n", si, so);
+ if (!cmp_str(si, sr))
+ fprintf(stdout, " result error, sent: %s, got: %s\n", si, sr);
+ CORBA_free(so);
+ CORBA_free(sr);
+ return -1;
+}
+
+static int string2_test(IC_Env *env)
+{
+ char* sa[3] = {"hello", "foo", "bar"};
+ m_sseq ssi = {3, 3, sa};
+ m_sseq *sso, *ssr;
+
+ fprintf(stdout, "\n======== m_i_string2 test ======\n\n");
+ ssr = m_i_string2_test(NULL, &ssi, &sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_sseq(&ssi, sso) && cmp_sseq(&ssi, sso));
+ if (!cmp_sseq(&ssi, sso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_sseq(&ssi);
+ fprintf(stdout, "got:\n");
+ print_sseq(sso);
+ }
+ if (!cmp_sseq(&ssi, ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_sseq(&ssi);
+ fprintf(stdout, "got:\n");
+ print_sseq(ssr);
+ }
+ CORBA_free(sso);
+ CORBA_free(ssr);
+ return -1;
+}
+
+static int string3_test(IC_Env *env)
+{
+ char* si = longtext;
+ char* so;
+ char* sr;
+
+ fprintf(stdout, "\n======== m_i_string3 test ======\n\n");
+ sr = m_i_string3_test(NULL, si, &so, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_str(si, so) && cmp_str(si, so));
+ if (!cmp_str(si, so))
+ fprintf(stdout, " out parameter error, sent: %s, got: %s\n", si, so);
+ if (!cmp_str(si, sr))
+ fprintf(stdout, " result error, sent: %s, got: %s\n", si, sr);
+ CORBA_free(so);
+ CORBA_free(sr);
+ return -1;
+}
+
+static int string4_test(IC_Env *env)
+{
+ char as1[100] = "a string", as2[200] = "help", as3[200] = "hello there";
+ m_strRec stri = { 1, /* dd */
+ as1, /* str4 */
+ {{'a', 'k'}, {'z', 'g'}, {'n', 'q'}}, /* str7 */
+ {3, 3, "buf"}, /* str5 */
+ as2, /* str6 */
+ {'m', 'f', 'o'}, /* str8 */
+ as3, /* str9 */
+ {3, 3, "stu"} /* str10 */
+ };
+ m_strRec *stro, *strr;
+
+ fprintf(stdout, "\n======== m_i_string4 test ======\n\n");
+ strr = m_i_string4_test(NULL, &stri, &stro, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_strRec(&stri,stro) && cmp_strRec(&stri,strr));
+ if (!cmp_strRec(&stri,stro)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_strRec(&stri);
+ fprintf(stdout, " got:\n");
+ print_strRec(stro);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_strRec(&stri,strr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_strRec(&stri);
+ fprintf(stdout, " got:\n");
+ print_strRec(strr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(stro);
+ CORBA_free(strr);
+ return -1;
+}
+
+
+static int pid_test(IC_Env *env)
+{
+ erlang_pid pid = {"", 7, 0, 0}, pido, pidr;
+
+ strcpy(pid.node, this_node), /* this currently running node */
+ fprintf(stdout, "\n======== m_i_pid test ======\n\n");
+ pidr = m_i_pid_test(NULL, &pid, &pido, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_pid(&pid, &pido) && cmp_pid(&pid, &pidr));
+ if (!cmp_pid(&pid, &pido)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_pid(&pid);
+ fprintf(stdout, "got:\n");
+ print_pid(&pido);
+ }
+ if (!cmp_pid(&pid, &pidr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_pid(&pid);
+ fprintf(stdout, "got:\n");
+ print_pid(&pidr);
+ }
+ return -1;
+}
+
+static int port_test(IC_Env *env)
+{
+ erlang_port porti = {"node", 5, 1}, porto, portr;
+
+ fprintf(stdout, "\n======== m_i_port test ======\n\n");
+ portr = m_i_port_test(NULL, &porti, &porto, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_port(&porti, &porto) && cmp_port(&porti, &portr));
+ if (!cmp_port(&porti, &porto)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_port(&porti);
+ fprintf(stdout, "got:\n");
+ print_port(&porto);
+ }
+ if (!cmp_port(&porti, &portr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_port(&porti);
+ fprintf(stdout, "got:\n");
+ print_port(&portr);
+ }
+ return -1;
+}
+
+static int ref_test(IC_Env *env)
+{
+ erlang_ref refi = { "node1", 3, {1, 2, 3}, 1},
+ refo, refr;
+
+ fprintf(stdout, "\n======== m_i_ref test ======\n\n");
+ refr = m_i_ref_test(NULL, &refi, &refo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ref(&refi, &refo) && cmp_ref(&refi, &refr));
+ if (!cmp_ref(&refi, &refo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ref(&refi);
+ fprintf(stdout, "got:\n");
+ print_ref(&refo);
+ }
+ if (!cmp_ref(&refi, &refr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ref(&refi);
+ fprintf(stdout, "got:\n");
+ print_ref(&refr);
+ }
+ return -1;
+}
+
+static int term_test(IC_Env *env)
+{
+ ETERM *ti, *to, *tr;
+
+ ti = erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]");
+
+ fprintf(stdout, "\n======== m_i_term test ======\n\n");
+ tr = m_i_term_test(NULL, ti, &to, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(erl_match(ti, to) && erl_match(ti, tr));
+ if (!erl_match(ti, to)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_term(ti);
+ fprintf(stdout, "got:\n");
+ print_term(to);
+ }
+ if (!erl_match(ti, tr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_term(ti);
+ fprintf(stdout, "got:\n");
+ print_term(tr);
+ }
+ erl_free_term(ti);
+ erl_free_term(to);
+ erl_free_term(tr);
+ return -1;
+}
+
+static int typedef_test(IC_Env *env)
+{
+ m_banan mbi, mbo; /* erlang_port */
+ m_apa mai; /* ETERM* */
+ m_apa mao = NULL;
+ long tl;
+
+ strcpy(mbi.node,"node");
+ mbi.id = 15;
+ mbi.creation = 1;
+
+ fprintf(stdout, "\n======== m_i_typedef test ======\n\n");
+ mai = erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]");
+ tl = m_i_typedef_test(NULL, mai, &mbi, &mao, &mbo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(erl_match(mai, mao) && cmp_port(&mbi, &mbo) && tl == 4711);
+ if (!erl_match(mai, mao)) {
+ fprintf(stdout, " out parameter error (term), sent:\n");
+ print_term(mai);
+ fprintf(stdout, "got:\n");
+ print_term(mao);
+ }
+ if (!cmp_port(&mbi, &mbo)) {
+ fprintf(stdout, " out parameter error (port), sent:\n");
+ print_port(&mbi);
+ fprintf(stdout, "got:\n");
+ print_port(&mbo);
+ }
+ if (tl != 4711) {
+ fprintf(stdout, " result error, sent: 4711, got %ld\n", tl);
+ }
+ erl_free_term(mai);
+ erl_free_term(mao);
+ return -1;
+}
+
+static int inline_sequence_test(IC_Env *env)
+{
+ int i;
+ long al[500];
+ m_s isi = {4711, {500, 10, al}},
+ *iso, *isr;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+ fprintf(stdout, "\n======== m_i_inline_sequence test ======\n\n");
+ isr = m_i_inline_sequence_test(NULL, &isi, &iso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_s(&isi, iso) && cmp_s(&isi, isr));
+ if (!cmp_s(&isi, iso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_s(&isi);
+ fprintf(stdout, "got:\n");
+ print_s(iso);
+ }
+ if (!cmp_s(&isi, isr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_s(&isi);
+ fprintf(stdout, "got:\n");
+ print_s(isr);
+ }
+ CORBA_free(iso);
+ CORBA_free(isr);
+ return -1;
+}
+
+static int term_sequence_test(IC_Env *env)
+{
+ ETERM* et_array[4] = {
+ erl_format("[{apa, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{banan, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{apelsin, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{mango, 1, 23}, \"string\", {1.23, 45}]")};
+ m_etseq etsi = {4, 4, et_array}, *etso, *etsr;
+
+ fprintf(stdout, "\n======== m_i_term_sequence test ======\n\n");
+ etsr = m_i_term_sequence_test(NULL, &etsi, &etso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_etseq(&etsi, etso) && cmp_etseq(&etsi, etsr));
+ if (!cmp_etseq(&etsi, etso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_etseq(&etsi);
+ fprintf(stdout, "got:\n");
+ print_etseq(etso);
+ }
+ if (!cmp_etseq(&etsi, etsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_etseq(&etsi);
+ fprintf(stdout, "got:\n");
+ print_etseq(etsr);
+ }
+ free_etseq_buf(&etsi);
+ free_etseq_buf(etso);
+ free_etseq_buf(etsr);
+ CORBA_free(etso);
+ CORBA_free(etsr);
+ return -1;
+}
+
+static int term_struct_test(IC_Env *env)
+{
+ m_et eti = { erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]"),
+ 121212 };
+ m_et eto, etr;
+
+ fprintf(stdout, "\n======== m_i_term_struct test ======\n\n");
+ etr = m_i_term_struct_test(NULL, &eti, &eto, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_et(&eti, &eto) && cmp_et(&eti, &etr));
+ if (!cmp_et(&eti, &eto)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_et(&eti);
+ fprintf(stdout, "got:\n");
+ print_et(&eto);
+ }
+ if (!cmp_et(&eti, &etr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_et(&eti);
+ fprintf(stdout, "got:\n");
+ print_et(&etr);
+ }
+ free_et(&eti);
+ free_et(&eto);
+ free_et(&etr);
+ return -1;
+}
+
+static int wstring1_test(IC_Env *env)
+{
+ CORBA_wchar wsi[] = {100, 101, 102, 103, 104, 0}, *wso, *wsr;
+
+ fprintf(stdout, "\n======== m_i_wstring1 test ======\n\n");
+ wsr = m_i_wstring1_test(NULL, wsi, &wso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_wstr(wsi, wso) && cmp_wstr(wsi, wsr));
+ if (!cmp_wstr(wsi, wso)) {
+ fprintf(stdout, " out parameter error, sent: \n");
+ print_wstr(wsi);
+ fprintf(stdout, "got:\n");
+ print_wstr(wso);
+ }
+ if (!cmp_wstr(wsi, wsr)) {
+ fprintf(stdout, " result error, sent: \n");
+ print_wstr(wsi);
+ fprintf(stdout, "got:\n");
+ print_wstr(wsr);
+ }
+ CORBA_free(wso);
+ CORBA_free(wsr);
+ return -1;
+}
+
+/* Compare functions */
+static int cmp_aseq(m_aseq *a1, m_aseq *a2)
+{
+ int i;
+
+ if (a1->_length != a2->_length)
+ return 0;
+ for (i = 0; i < a1->_length; i++)
+ if (cmp_a(&(a1->_buffer[i]), &(a2->_buffer[i])) == 0)
+ return 0;
+ return 1;
+}
+
+static int cmp_a(m_a *a1, m_a *a2)
+{
+ return a1->l == a2->l &&
+ a1->d == a2->d &&
+ cmp_bseq(&a1->y, &a2->y);
+}
+
+static int cmp_bseq(m_bseq *b1, m_bseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (cmp_b(&(b1->_buffer[i]), &(b2->_buffer[i])) == 0)
+ return 0;
+ return 1;
+}
+
+static int cmp_b(m_b *b1, m_b *b2)
+{
+ return b1->l == b2->l && b1->c == b2->c;
+}
+
+static int cmp_lseq(m_lseq *b1, m_lseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (b1->_buffer[i] != b2->_buffer[i])
+ return 0;
+ return 1;
+}
+
+static int cmp_etseq(m_etseq *b1, m_etseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (!erl_match(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ return 1;
+}
+
+static int cmp_et(m_et* b1, m_et *b2)
+{
+ return erl_match(b1->e, b2->e) && b1->l == b2->l;
+}
+
+static int cmp_es(m_es *b1, m_es *b2)
+{
+ return b1->f == b2->f && b1->l == b2->l;
+}
+
+static int cmp_arr1(m_arr1 b1, m_arr1 b2)
+{
+ int i;
+
+ for (i = 0; i < 500; i++)
+ if (b1[i] != b2[i])
+ return 0;
+ return 1;
+}
+
+static int cmp_dd(m_dd b1, m_dd b2)
+{
+
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ if (b1[i][j] != b2[i][j])
+ return 0;
+ return 1;
+}
+
+
+
+static int cmp_strRec(m_strRec *b1, m_strRec *b2)
+{
+ int i, j;
+
+ if (b1->bb != b2->bb)
+ return 0;
+ if (!cmp_str(b1->str4,b2->str4))
+ return 0;
+ if (b1->str5._length != b2->str5._length)
+ return 0;
+ for (j = 0; j < b1->str5._length; j++)
+ if (b1->str5._buffer[j] != b2->str5._buffer[j])
+ return 0;
+ if (!cmp_str(b1->str6,b2->str6))
+ return 0;
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ if (b1->str7[i][j] != b2->str7[i][j])
+ return 0;
+ for (j = 0; j < 3; j++)
+ if (b1->str8[j] != b2->str8[j])
+ return 0;
+ if (!cmp_str(b1->str9,b2->str9))
+ return 0;
+ if (b1->str10._length != b2->str10._length)
+ return 0;
+ for (j = 0; j < b1->str10._length; j++)
+ if (b1->str10._buffer[j] != b2->str10._buffer[j])
+ return 0;
+ return 1;
+}
+
+
+static int cmp_sseq(m_sseq *b1, m_sseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (!cmp_str(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ return 1;
+}
+
+
+static int cmp_pid(erlang_pid *p1, erlang_pid *p2)
+{
+ return cmp_str(p1->node,p2-> node) &&
+ p1->num == p2->num &&
+ p1->serial == p2->serial &&
+ p1->creation == p2->creation;
+}
+
+static int cmp_port(erlang_port *p1, erlang_port *p2)
+{
+ return cmp_str(p1->node,p2-> node) && p1->id == p2->id;
+}
+
+static int cmp_ref(erlang_ref *p1, erlang_ref *p2)
+{
+ return cmp_str(p1->node, p2->node) &&
+ p1->len == p2->len &&
+ (p1->len < 1 || p1->n[0] == p2->n[0]) &&
+ (p1->len < 2 || p1->n[1] == p2->n[1]) &&
+ (p1->len < 3 || p1->n[2] == p2->n[2]);
+}
+
+static int cmp_s(m_s *b1, m_s *b2)
+{
+ int i;
+
+ if (b1->l != b2->l)
+ return 0;
+ if (b1->sl._length != b2->sl._length)
+ return 0;
+ for (i = 0; i < b1->sl._length; i++)
+ if (b1->sl._buffer[i] != b2->sl._buffer[i])
+ return 0;
+ return 1;
+}
+
+
+static int cmp_ssstr3(m_ssstr3 *b1, m_ssstr3 *b2)
+{
+ int i,j;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (b1->_buffer[i]._length != b2->_buffer[i]._length)
+ return 0;
+ for (j = 0; j < b1->_buffer[i]._length; j++)
+ if (!cmp_str(b1->_buffer[i]._buffer[j],
+ b2->_buffer[i]._buffer[j]))
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static int cmp_ssarr3(m_ssarr3 *b1, m_ssarr3 *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (!cmp_sarr3(&b1->_buffer[i], &b2->_buffer[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_sarr3(m_sarr3 *b1, m_sarr3 *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (!cmp_arr3(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_arr3(m_arr3 b1, m_arr3 b2)
+{
+ int i;
+
+ for (i = 0; i < sizeof(m_arr3)/sizeof(CORBA_long); i++) {
+ if (b1[i] != b2[i])
+ return 0;
+ }
+ return 1;
+}
+
+/* Print functions */
+static void print_aseq(m_aseq *a)
+{
+ int i;
+ fprintf(stdout, "\nm_aseq size: %ld --------\n", a->_length);
+ for (i = 0; i < a->_length; i++)
+ print_a(&(a->_buffer[i]));
+}
+
+static void print_a(m_a *a)
+{
+ fprintf(stdout, "\nm_a --------\n l: %ld\n d:%f\n", a->l, a->d);
+ print_bseq(&a->y);
+}
+
+static void print_bseq(m_bseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_bseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ print_b(&(b->_buffer[i]));
+}
+
+static void print_lseq(m_lseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_lseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ fprintf(stdout, "[%d]: %ld\n", i, b->_buffer[i]);
+}
+
+static void print_b(m_b *b)
+{
+ fprintf(stdout, "\nm_b --------\n l: %ld\n c: %c\n", b->l, b->c);
+}
+
+
+static void print_etseq(m_etseq *b)
+{
+ int i;
+
+ for (i = 0; i < b->_length; i++) {
+ fprintf(stdout, "[%d]:\n", i);
+ erl_print_term(stdout, b->_buffer[i]);
+ }
+}
+
+
+static void print_et(m_et* b)
+{
+ fprintf(stdout, "\net struct --------\n");
+ erl_print_term(stdout, b->e);
+ fprintf(stdout, "long: %ld\n", b->l);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_es(m_es *b)
+{
+ fprintf(stdout, "\nm_es --------\n f: %d\n l: %ld\n", b->f, b->l);
+}
+
+
+static void print_arr1(long a[10])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ fprintf(stdout, "\n[%d]: %ld\n", i, a[i]);
+}
+
+static void print_dd(long a[2][3])
+{
+ int i, j;
+
+ fprintf(stdout, "\nlong dd[2][3] --------\n");
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "\n[%d][%d]: %ld\n", i, j, a[i][j]);
+}
+
+
+static void print_strRec(m_strRec* sr)
+{
+ int i, j;
+
+ fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
+ fprintf(stdout, "string str4 : %s\n",sr->str4);
+ fprintf(stdout, "str7[2][3] :\n");
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
+ fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
+ for (j = 0; j < sr->str5._length; j++)
+ fprintf(stdout, "str5._buffer[%d]: %c\n", j, sr->str5._buffer[j]);
+ fprintf(stdout, "string str6 : %s\n",sr->str6);
+ fprintf(stdout, "str8 :\n");
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "str8[%d]: %c\n", j, sr->str8[j]);
+ fprintf(stdout, "string str9 : %s\n",sr->str9);
+ fprintf(stdout, "str10._length : %ld\n",sr->str10._length);
+ for (j = 0; j < sr->str10._length; j++)
+ fprintf(stdout, "str10._buffer[%d]: %c\n", j, sr->str10._buffer[j]);
+}
+
+static void print_sseq(m_sseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_sseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ fprintf(stdout, "%s\n", b->_buffer[i]);
+
+}
+
+
+static void print_pid(erlang_pid *p)
+{
+ fprintf(stdout, "\nerlang_pid --------\n node: %s\n num: %d\n "
+ "serial: %d\n creation: %d\n",
+ p->node, p->num, p->serial, p->creation);
+}
+
+static void print_port(erlang_port *p)
+{
+ fprintf(stdout, "\nerlang_port --------\n node: %s\n id: %d\n "
+ "creation: %d\n", p->node, p->id, p->creation);
+}
+
+static void print_ref(erlang_ref *p)
+{
+ fprintf(stdout, "\nerlang_ref --------\n node: %s\n len: %d\n "
+ "n[0]: %d\n n[1]: %d\n n[2]: %d\n creation: %d\n",
+ p->node, p->len, p->n[0], p->n[1], p->n[2], p->creation);
+}
+
+static void print_term(ETERM *t)
+{
+ fprintf(stdout, "\nETERM --------\n");
+ erl_print_term(stdout, t);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_s(m_s *p)
+{
+ int i;
+
+ fprintf(stdout, "\n%ld\n", p->l);
+ for (i = 0; i < p->sl._length; i++)
+ fprintf(stdout, "\n[%d]: %ld\n", i, p->sl._buffer[i]);
+}
+
+
+static void print_ssstr3(m_ssstr3 *b1)
+{
+ int i,j;
+
+ fprintf(stdout, "\nSSSTR3 --------\n");
+ fprintf(stdout,"b1->_length = %ld\n",b1->_length);
+ for (i = 0; i < b1->_length; i++) {
+ fprintf(stdout,"\nb1->_buffer[%d]._length %ld\n",
+ i, b1->_buffer[i]._length);
+ for (j = 0; j < b1->_buffer[i]._length; j++)
+ fprintf(stdout,"b1->_buffer[%d]._buffer[%d] = %s\n",
+ i, j, b1->_buffer[i]._buffer[j]);
+ }
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_wstr(CORBA_wchar *ws)
+{
+ int i = 0;
+
+ fprintf(stdout, "\nwstr --------\n");
+ while (ws[i]) {
+ fprintf(stdout, "[%d]: %ld\n", i, ws[i]);
+ i++;
+ }
+ fprintf(stdout, "\n--------\n");
+}
+
+
+static void print_ssarr3(m_ssarr3 *b1)
+{
+ int i;
+
+ fprintf(stdout, "\nssarr3 --------\n");
+ fprintf(stdout,"length: %ld\n",b1->_length);
+ fprintf(stdout, "buffer:\n");
+ for (i = 0; i < b1->_length; i++)
+ print_sarr3(&b1->_buffer[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_sarr3(m_sarr3 *b1)
+{
+ int i;
+
+ fprintf(stdout, "\nsarr3 --------\n");
+ fprintf(stdout,"length: %ld\n",b1->_length);
+ fprintf(stdout, "buffer:\n");
+ for (i = 0; i < b1->_length; i++)
+ print_arr3(b1->_buffer[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_arr3(m_arr3 b1)
+{
+ int i;
+
+ fprintf(stdout, "\narr3 --------\n");
+ for (i = 0; i < sizeof(m_arr3)/sizeof(CORBA_long); i++)
+ fprintf(stdout, "%ld ", b1[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void free_etseq_buf(m_etseq *b)
+{
+ int i;
+
+ for (i = 0; i < b->_length; i++)
+ erl_free_term(b->_buffer[i]);
+}
+
+static void free_et(m_et* b)
+{
+ erl_free_term(b->e);
+}
+
+static void showtime(MyTimeval *start, MyTimeval *stop)
+{
+ MyTimeval elapsed;
+
+ elapsed.tv_sec = stop->tv_sec - start->tv_sec;
+ elapsed.tv_usec = stop->tv_usec - start->tv_usec;
+ while (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ fprintf(stderr,"%ld.%06ld seconds\n",elapsed.tv_sec, elapsed.tv_usec);
+}
+
+static void my_gettimeofday(MyTimeval *tv)
+#ifdef __WIN32__
+#define EPOCH_JULIAN_DIFF 11644473600i64
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ LONGLONG lft;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ memcpy(&lft, &ft, sizeof(lft));
+ tv->tv_usec = (long) ((lft / 10i64) % 1000000i64);
+ tv->tv_sec = (long) ((lft / 10000000i64) - EPOCH_JULIAN_DIFF);
+}
+#elif defined VXWORKS
+{
+ int rate = sysClkRateGet(); /* Ticks per second */
+ unsigned long ctick = tickGet();
+ tv->tv_sec = ctick / rate; /* secs since reboot */
+ tv->tv_usec = ((ctick - (tv->tv_sec * rate))*1000000)/rate;
+}
+#else
+{
+ gettimeofday(tv, NULL);
+}
+#endif
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_erl_test.idl b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_erl_test.idl
new file mode 100644
index 0000000000..6d229c3ac1
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/c_erl_test.idl
@@ -0,0 +1,173 @@
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2003-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+#include "erlang.idl"
+
+
+const short TestConst = 1;
+
+module m {
+
+ const short TestConst = 2;
+
+ struct b {
+ long l;
+ char c;
+ };
+
+ struct simple {
+ long l;
+ b b_t;
+ };
+
+ enum fruit {orange, banana, apple, peach, pear};
+
+ typedef sequence<long> lseq;
+
+ typedef sequence<b> bseq;
+
+ struct a {
+ long l;
+ bseq y;
+ double d;
+ };
+
+ typedef sequence<a> aseq;
+
+ typedef sequence<string> sseq;
+ typedef string str;
+ typedef long myLong;
+
+ typedef long arr1[500], dd[2][3];
+
+ typedef erlang::term apa;
+ typedef erlang::port banan;
+
+ typedef sequence<erlang::term> etseq;
+
+ struct s {
+ long l;
+ sequence<long> sl;
+ };
+
+ struct es {
+ fruit f;
+ myLong l;
+ };
+
+ struct et {
+ erlang::term e;
+ long l;
+ };
+
+
+ typedef sequence<char> str1;
+ typedef string<12> str2;
+ typedef char str3[3];
+
+ typedef sequence<string> sstr3; // sequence of string
+ typedef sequence<sstr3> ssstr3; // sequence of sequences of strings
+
+ typedef long arr3[3]; // array of long
+ typedef sequence<arr3> sarr3; // sequence of array
+ typedef sequence<sarr3> ssarr3; // sequence of sequnces of arrays of strings
+
+ struct strRec{
+ boolean bb;
+ string str4;
+ long str7[3][2];
+ sequence<char> str5;
+ string<12> str6;
+ str3 str8;
+ str2 str9;
+ str1 str10;
+ };
+
+
+ struct dyn {
+ long l;
+ sequence<long> sl;
+ };
+ typedef dyn arr2[1][2];
+
+
+ interface i {
+
+ const short TestConst = 3;
+
+ //arr2 suck(in arr2 x, out arr2 y );
+
+ ///////////////////////////////// attribute long l;
+
+ // simple types
+ void void_test();
+ long long_test(in long a, out long a1);
+ long long longlong_test(in long long a, out long long a1);
+ unsigned short ushort_test(in unsigned short a, out unsigned short a1);
+ unsigned long ulong_test(in unsigned long a, out unsigned long a1);
+ unsigned long long ulonglong_test(in unsigned long long a, out unsigned long long a1);
+ double double_test(in double a, out double a1);
+ char char_test(in char a, out char a1);
+ wchar wchar_test(in wchar a, out wchar a1);
+ octet octet_test(in octet a, out octet a1);
+ boolean bool_test(in boolean a, out boolean a1);
+
+ // Seq. and struct tests
+ b struct_test(in b a, out b a1);
+ es struct2_test(in es a, out es a1);
+ //simple struct3_test(in simple x, out simple y);
+ bseq seq1_test(in bseq a, out bseq a1);
+ aseq seq2_test(in aseq a, out aseq a1);
+ lseq seq3_test(in lseq a, out lseq a1);
+ ssstr3 seq4_test(in ssstr3 a, out ssstr3 a1);
+ ssarr3 seq5_test(in ssarr3 a, out ssarr3 a1);
+
+ // Array tests
+ arr1 array1_test(in arr1 a, out arr1 a1);
+ dd array2_test(in dd a, out dd a1);
+
+ // enum test
+ fruit enum_test(in fruit a, out fruit a1);
+
+ // string tests
+ string string1_test(in string a, out string a1);
+ wstring wstring1_test(in wstring a, out wstring a1);
+ sseq string2_test(in sseq a, out sseq a1);
+ str string3_test(in str a, out str a1);
+ strRec string4_test(in strRec a, out strRec a1);
+
+ // Special erlang types
+ erlang::pid pid_test(in erlang::pid a, out erlang::pid a1);
+ erlang::port port_test(in erlang::port a, out erlang::port a1);
+ erlang::ref ref_test(in erlang::ref a, out erlang::ref a1);
+ erlang::term term_test(in erlang::term a, out erlang::term a1);
+
+ // typedef test
+ long typedef_test(in apa a, in banan b, out apa a1, out banan b1);
+
+ // inlined seq. test
+ s inline_sequence_test(in s a, out s a1);
+
+ // term seq. test
+ etseq term_sequence_test(in etseq a, out etseq a1);
+ // term struct test
+ et term_struct_test(in et a, out et a1);
+
+ };
+
+};
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/erl_server.erl b/lib/ic/test/c_client_erl_server_proto_SUITE_data/erl_server.erl
new file mode 100644
index 0000000000..ec0757bfab
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/erl_server.erl
@@ -0,0 +1,28 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(erl_server).
+
+-export([run/0, stop/0]).
+
+run() ->
+ m_i:oe_create().
+
+stop() ->
+ gen_server:cast(cidl_test, stop).
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/m_i_impl.erl b/lib/ic/test/c_client_erl_server_proto_SUITE_data/m_i_impl.erl
new file mode 100644
index 0000000000..1eb792cb6d
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/m_i_impl.erl
@@ -0,0 +1,161 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(m_i_impl).
+-include("m.hrl").
+
+-export([init/1, terminate/2, void_test/1, long_test/2, ushort_test/2,
+ longlong_test/2, ulong_test/2, ulonglong_test/2,
+ double_test/2, char_test/2, wchar_test/2, octet_test/2,
+ bool_test/2, struct_test/2, struct2_test/2, seq1_test/2,
+ seq2_test/2, seq3_test/2, seq4_test/2, seq5_test/2,
+ array1_test/2, array2_test/2, enum_test/2, string1_test/2,
+ string2_test/2, string3_test/2, string4_test/2, pid_test/2,
+ port_test/2, ref_test/2, term_test/2, typedef_test/3,
+ inline_sequence_test/2, '_set_l'/2, '_get_l'/1,
+ term_struct_test/2, term_sequence_test/2, wstring1_test/2]).
+
+-define(PRINTDEBUG(Case),
+ io:format("erl_server: case: ~p~n"
+ "erl_server: location: ~p~n", [Case, [?FILE, ?LINE]])).
+-define(PRINTDEBUG2(Case, Msg),
+ io:format("erl_server: case: ~p~n"
+ "erl_server: Msg: ~p~n"
+ "erl_server: location: ~p~n", [Case, Msg, [?FILE, ?LINE]])).
+
+init(Env) ->
+ {ok, []}.
+
+terminate(F, R) ->
+ ok.
+
+'_get_l'(State) ->
+ ?PRINTDEBUG("_get_l"),
+ {reply, State, State}.
+void_test(State) ->
+ ?PRINTDEBUG("void_test"),
+ {reply, ok, State}.
+
+'_set_l'(State, V) ->
+ ?PRINTDEBUG2("_set_l", V),
+ {reply, ok, V}.
+ushort_test(State, V) ->
+ ?PRINTDEBUG2("ushort_test", V),
+ {reply, {V, V}, State}.
+long_test(State, V) ->
+ ?PRINTDEBUG2("long_test", V),
+ {reply, {V, V}, State}.
+longlong_test(State, V) ->
+ ?PRINTDEBUG2("longlong_test", V),
+ {reply, {V, V}, State}.
+ulong_test(State, V) ->
+ ?PRINTDEBUG2("ulong_test", V),
+ {reply, {V, V}, State}.
+ulonglong_test(State, V) ->
+ ?PRINTDEBUG2("ulonglong_test", V),
+ {reply, {V, V}, State}.
+double_test(State, V) ->
+ ?PRINTDEBUG2("double_test", V),
+ {reply, {V, V}, State}.
+char_test(State, V) ->
+ ?PRINTDEBUG2("char_test", V),
+ {reply, {V, V}, State}.
+wchar_test(State, V) ->
+ ?PRINTDEBUG2("wchar_test", V),
+ {reply, {V, V}, State}.
+octet_test(State, V) ->
+ ?PRINTDEBUG2("octet_test", V),
+ {reply, {V, V}, State}.
+bool_test(State, V) ->
+ ?PRINTDEBUG2("bool_test", V),
+ {reply, {V, V}, State}.
+
+struct_test(State, V) ->
+ ?PRINTDEBUG2("struct_test", V),
+ {reply, {V, V}, State}.
+struct2_test(State, V) ->
+ ?PRINTDEBUG2("struct2_test", V),
+ {reply, {V, V}, State}.
+seq1_test(State, V) ->
+ ?PRINTDEBUG2("seq1_test", V),
+ {reply, {V, V}, State}.
+seq2_test(State, V) ->
+ ?PRINTDEBUG2("seq2_test", V),
+ {reply, {V, V}, State}.
+seq3_test(State, V) ->
+ ?PRINTDEBUG2("seq3_test", V),
+ {reply, {V, V}, State}.
+seq4_test(State, V) ->
+ ?PRINTDEBUG2("seq4_test", V),
+ {reply, {V, V}, State}.
+seq5_test(State, V) ->
+ ?PRINTDEBUG2("seq5_test", V),
+ {reply, {V, V}, State}.
+array1_test(State, V) ->
+ ?PRINTDEBUG2("array1_test", V),
+ {reply, {V, V}, State}.
+array2_test(State, V) ->
+ ?PRINTDEBUG2("array2_test", V),
+ {reply, {V, V}, State}.
+enum_test(State, V) ->
+ ?PRINTDEBUG2("enum_test", V),
+ {reply, {V, V}, State}.
+string1_test(State, V) ->
+ ?PRINTDEBUG2("string1_test", V),
+ {reply, {V, V}, State}.
+string2_test(State, V) ->
+ ?PRINTDEBUG2("string2_test", V),
+ {reply, {V, V}, State}.
+string3_test(State, V) ->
+ ?PRINTDEBUG2("string3_test", V),
+ {reply, {V, V}, State}.
+string4_test(State, V) ->
+ ?PRINTDEBUG2("string4_test", V),
+ {reply, {V, V}, State}.
+pid_test(State, V) ->
+ ?PRINTDEBUG2("pid_test", V),
+ {reply, {V, V}, State}.
+port_test(State, V) ->
+ ?PRINTDEBUG2("port_test", binary_to_list(term_to_binary(V))),
+ {reply, {V, V}, State}.
+ref_test(State, V) ->
+ ?PRINTDEBUG2("ref_test", binary_to_list(term_to_binary(V))),
+ {reply, {V, V}, State}.
+term_test(State, V) ->
+ ?PRINTDEBUG2("term_test", V),
+ {reply, {V, V}, State}.
+typedef_test(State, A, B) ->
+ ?PRINTDEBUG2("typedef_test", [A,B]),
+ {reply, {4711, A, B}, State}.
+inline_sequence_test(State, V) ->
+ ?PRINTDEBUG2("inline_sequence_test", V),
+ {reply, {V, V}, State}.
+term_sequence_test(State, V) ->
+ ?PRINTDEBUG2("term_sequence_test", V),
+ {reply, {V, V}, State}.
+term_struct_test(State, V) ->
+ ?PRINTDEBUG2("term_struct_test", V),
+ {reply, {V, V}, State}.
+wstring1_test(State, V) ->
+ ?PRINTDEBUG2("wstring1_test", V),
+ {reply, {V, V}, State}.
+
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/my.c b/lib/ic/test/c_client_erl_server_proto_SUITE_data/my.c
new file mode 100644
index 0000000000..103066a795
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/my.c
@@ -0,0 +1,50 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+
+#include "ic.h"
+#include "m_i.h"
+
+int my_prepare_notification_encoding(CORBA_Environment *env)
+{
+ return oe_prepare_notification_encoding(env);
+}
+
+int my_send_notification(CORBA_Environment *env)
+{
+ return oe_send_notification(env);
+}
+
+int my_prepare_request_encoding(CORBA_Environment *env)
+{
+ return oe_prepare_request_encoding(env);
+}
+
+int my_send_request_and_receive_reply(CORBA_Environment *env)
+{
+ return oe_send_request_and_receive_reply(env);
+}
+
+int my_prepare_reply_decoding(CORBA_Environment *env)
+{
+ return oe_prepare_reply_decoding(env);
+}
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE.erl b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE.erl
new file mode 100644
index 0000000000..1a2d885867
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE.erl
@@ -0,0 +1,334 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose : Test suite for c-client/erl-server
+%%----------------------------------------------------------------------
+
+-module(c_client_erl_server_proto_tmo_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-export([init_per_testcase/2, end_per_testcase/2,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ void_test/1, long_test/1, long_long_test/1,
+ unsigned_short_test/1, unsigned_long_test/1,
+ unsigned_long_long_test/1, double_test/1, char_test/1,
+ wchar_test/1, octet_test/1, bool_test/1, struct_test/1,
+ struct2_test/1, seq1_test/1, seq2_test/1, seq3_test/1,
+ seq4_test/1, seq5_test/1, array1_test/1, array2_test/1,
+ enum_test/1, string1_test/1, string2_test/1, string3_test/1,
+ string4_test/1, pid_test/1, port_test/1, ref_test/1, term_test/1,
+ typedef_test/1, inline_sequence_test/1, term_sequence_test/1,
+ term_struct_test/1, wstring1_test/1]).
+
+-define(DEFAULT_TIMEOUT, 20000).
+-define(PORT_TIMEOUT, 15000).
+-define(ERLANG_SERVER_NAME, idl_erlang_server).
+-define(C_CLIENT_NODE_NAME, c_client_idl_test).
+
+%% Add/remove code path and watchdog before/after each test case.
+%%
+init_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:add_patha(DataDir),
+
+ %% Since other test suites use the module m_i, we have
+ %% to make sure we are using the right m_i module.
+ code:purge(m_i),
+ code:load_file(m_i),
+
+ WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:del_path(DataDir),
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [void_test, long_test, long_long_test,
+ unsigned_short_test, unsigned_long_test,
+ unsigned_long_long_test, double_test, char_test,
+ wchar_test, octet_test, bool_test, struct_test,
+ struct2_test, seq1_test, seq2_test, seq3_test,
+ seq4_test, seq5_test, array1_test, array2_test,
+ enum_test, string1_test, string2_test, string3_test,
+ string4_test, pid_test, port_test, ref_test, term_test,
+ typedef_test, inline_sequence_test, term_sequence_test,
+ term_struct_test, wstring1_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+array1_test(doc) -> "";
+array1_test(suite) -> [];
+array1_test(Config) ->
+ do_test(array1_test, Config).
+
+array2_test(doc) -> "";
+array2_test(suite) -> [];
+array2_test(Config) ->
+ do_test(array2_test, Config).
+
+bool_test(doc) -> "";
+bool_test(suite) -> [];
+bool_test(Config) ->
+ do_test(bool_test, Config).
+
+char_test(doc) -> "";
+char_test(suite) -> [];
+char_test(Config) ->
+ do_test(char_test, Config).
+
+double_test(doc) -> "";
+double_test(suite) -> [];
+double_test(Config) ->
+ do_test(double_test, Config).
+
+enum_test(doc) -> "";
+enum_test(suite) -> [];
+enum_test(Config) ->
+ do_test(enum_test, Config).
+
+inline_sequence_test(doc) -> "";
+inline_sequence_test(suite) -> [];
+inline_sequence_test(Config) ->
+ do_test(inline_sequence_test, Config).
+
+long_long_test(doc) -> "";
+long_long_test(suite) -> [];
+long_long_test(Config) ->
+ do_test(long_long_test, Config).
+
+long_test(doc) -> "";
+long_test(suite) -> [];
+long_test(Config) ->
+ do_test(long_test, Config).
+
+octet_test(doc) -> "";
+octet_test(suite) -> [];
+octet_test(Config) ->
+ do_test(octet_test, Config).
+
+pid_test(doc) -> "";
+pid_test(suite) -> [];
+pid_test(Config) ->
+ do_test(pid_test, Config).
+
+port_test(doc) -> "";
+port_test(suite) -> [];
+port_test(Config) ->
+ do_test(port_test, Config).
+
+ref_test(doc) -> "";
+ref_test(suite) -> [];
+ref_test(Config) ->
+ do_test(ref_test, Config).
+
+seq1_test(doc) -> "";
+seq1_test(suite) -> [];
+seq1_test(Config) ->
+ do_test(seq1_test, Config).
+
+seq2_test(doc) -> "";
+seq2_test(suite) -> [];
+seq2_test(Config) ->
+ do_test(seq2_test, Config).
+
+seq3_test(doc) -> "";
+seq3_test(suite) -> [];
+seq3_test(Config) ->
+ do_test(seq3_test, Config).
+
+seq4_test(doc) -> "";
+seq4_test(suite) -> [];
+seq4_test(Config) ->
+ do_test(seq4_test, Config).
+
+seq5_test(doc) -> "";
+seq5_test(suite) -> [];
+seq5_test(Config) ->
+ do_test(seq5_test, Config).
+
+string1_test(doc) -> "";
+string1_test(suite) -> [];
+string1_test(Config) ->
+ do_test(string1_test, Config).
+
+string2_test(doc) -> "";
+string2_test(suite) -> [];
+string2_test(Config) ->
+ do_test(string2_test, Config).
+
+string3_test(doc) -> "";
+string3_test(suite) -> [];
+string3_test(Config) ->
+ do_test(string3_test, Config).
+
+string4_test(doc) -> "";
+string4_test(suite) -> [];
+string4_test(Config) ->
+ do_test(string4_test, Config).
+
+struct2_test(doc) -> "";
+struct2_test(suite) -> [];
+struct2_test(Config) ->
+ do_test(struct2_test, Config).
+
+struct_test(doc) -> "";
+struct_test(suite) -> [];
+struct_test(Config) ->
+ do_test(struct_test, Config).
+
+term_sequence_test(doc) -> "";
+term_sequence_test(suite) -> [];
+term_sequence_test(Config) ->
+ do_test(term_sequence_test, Config).
+
+term_struct_test(doc) -> "";
+term_struct_test(suite) -> [];
+term_struct_test(Config) ->
+ do_test(term_struct_test, Config).
+
+term_test(doc) -> "";
+term_test(suite) -> [];
+term_test(Config) ->
+ do_test(term_test, Config).
+
+typedef_test(doc) -> "";
+typedef_test(suite) -> [];
+typedef_test(Config) ->
+ do_test(typedef_test, Config).
+
+unsigned_long_long_test(doc) -> "";
+unsigned_long_long_test(suite) -> [];
+unsigned_long_long_test(Config) ->
+ do_test(unsigned_long_long_test, Config).
+
+unsigned_long_test(doc) -> "";
+unsigned_long_test(suite) -> [];
+unsigned_long_test(Config) ->
+ do_test(unsigned_long_test, Config).
+
+unsigned_short_test(doc) -> "";
+unsigned_short_test(suite) -> [];
+unsigned_short_test(Config) ->
+ do_test(unsigned_short_test, Config).
+
+void_test(doc) -> "";
+void_test(suite) -> [];
+void_test(Config) ->
+ do_test(void_test, Config).
+
+wchar_test(doc) -> "";
+wchar_test(suite) -> [];
+wchar_test(Config) ->
+ do_test(wchar_test, Config).
+
+wstring1_test(doc) -> "";
+wstring1_test(suite) -> [];
+wstring1_test(Config) ->
+ do_test(wstring1_test, Config).
+
+
+%% It is here that all tests really are done.
+%%
+
+do_test(Case, Config) ->
+ %% Trap exits
+ process_flag(trap_exit, true),
+ %% Start the server
+ {ok, _Pid} = m_i:oe_create_link([], {local, ?ERLANG_SERVER_NAME}),
+ Node = atom_to_list(node()),
+ %% [NodeName, HostName] = string:tokens(Node, "@"),
+ DataDir = ?config(data_dir, Config),
+ %% io:format("~p: data directory: ~p~n", [?MODULE, DataDir]),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ %% Start C-client node as a port program.
+ Cmd = filename:join([DataDir, "c_client"]) ++
+ " -this-node-name " ++ atom_to_list(?C_CLIENT_NODE_NAME) ++
+ " -peer-node " ++ Node ++
+ " -peer-process-name " ++ atom_to_list(?ERLANG_SERVER_NAME) ++
+ " -cookie " ++ Cookie ++
+ " -test-case " ++ atom_to_list(Case),
+ Port = open_port({spawn, Cmd}, [exit_status, eof, stderr_to_stdout]),
+ Res = wait_for_completion(Port),
+ %% Kill off node if there was timeout
+ case Res of
+ {error, timeout} ->
+ catch rpc:cast(?C_CLIENT_NODE_NAME, erlang, halt, [1]);
+ _ ->
+ ok
+ end,
+ process_flag(trap_exit, false),
+ catch m_i:stop(?ERLANG_SERVER_NAME),
+ ok = Res.
+
+
+%% Wait for eof *and* exit status, but return if exit status indicates
+%% an error, or we have been waiting more than PORT_TIMEOUT seconds.
+%%
+wait_for_completion(Port) ->
+ wait_for_completion(Port, 0).
+
+wait_for_completion(Port, N) when N < 2 ->
+ receive
+ {Port, {data, Bytes}} ->
+ %% Relay output
+ io:format("~s", [Bytes]),
+ wait_for_completion(Port, N);
+ {Port, {exit_status, 0}} ->
+ wait_for_completion(Port, N + 1);
+ {Port, {exit_status, Status}} ->
+ {error, Status};
+ {Port, eof} ->
+ wait_for_completion(Port, N + 1);
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason: ~w~n", [Reason]),
+ wait_for_completion(Port, N);
+ {'EXIT', From, Reason} ->
+ io:format("Got unexpected exit: ~p~n", [{'EXIT', From, Reason}]),
+ wait_for_completion(Port, N)
+ after ?PORT_TIMEOUT ->
+ {error, timeout}
+ end;
+wait_for_completion(_, _) ->
+ ok.
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..2585341791
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src
@@ -0,0 +1,146 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Makefile.src for c_client_erl_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .h .erl .idl @obj@ .@EMULATOR@
+
+
+# Variables from ts:
+#
+
+ERL_INCLUDE = @erl_include@
+
+IC_INCLUDE_PATH = @ic_include_path@
+IC_LIB = @ic_libpath@@DS@@ic_lib@
+
+ERL_INTERFACE_INCLUDE = @erl_interface_include@
+ERL_INTERFACE_LIB = @erl_interface_libpath@@DS@@erl_interface_lib@
+ERL_INTERFACE_EILIB = @erl_interface_libpath@@DS@@erl_interface_eilib@
+ERL_INTERFACE_THREADLIB = @erl_interface_threadlib@
+ERL_INTERFACE_SOCK_LIBS = @erl_interface_sock_libs@
+
+CC = @CC@
+## XXX Should set warning flag with a DEBUG_FLAG
+CFLAGS = @CFLAGS@ @DEFS@ -I@erl_include@ \
+ -I@ic_include_path@ -I@erl_interface_include@
+
+LD = @LD@
+LDFLAGS = @CROSSLDFLAGS@
+LIBS = $(IC_LIB) $(ERL_INTERFACE_LIB) $(ERL_INTERFACE_EILIB) \
+ $(ERL_INTERFACE_THREADLIB) @LIBS@ $(ERL_INTERFACE_SOCK_LIBS)
+ERLC = erlc
+
+# Generated C header files
+GEN_H_FILES = \
+ m.h \
+ m_i.h \
+ oe_c_erl_test.h
+
+# Generated C files
+GEN_C_FILES = \
+ m.c \
+ m_i.c \
+ oe_c_erl_test.c \
+ oe_code_m_a.c \
+ oe_code_m_arr1.c \
+ oe_code_m_arr2.c \
+ oe_code_m_arr3.c \
+ oe_code_m_aseq.c \
+ oe_code_m_b.c \
+ oe_code_m_bseq.c \
+ oe_code_m_dd.c \
+ oe_code_m_dyn.c \
+ oe_code_m_dyn_sl.c \
+ oe_code_m_es.c \
+ oe_code_m_et.c \
+ oe_code_m_etseq.c \
+ oe_code_m_fruit.c \
+ oe_code_m_lseq.c \
+ oe_code_m_s.c \
+ oe_code_m_s_sl.c \
+ oe_code_m_sarr3.c \
+ oe_code_m_simple.c \
+ oe_code_m_ssarr3.c \
+ oe_code_m_sseq.c \
+ oe_code_m_ssstr3.c \
+ oe_code_m_sstr3.c \
+ oe_code_m_str1.c \
+ oe_code_m_str3.c \
+ oe_code_m_strRec.c \
+ oe_code_m_strRec_str5.c \
+ oe_code_m_strRec_str7.c
+
+GEN_HRL_FILES = \
+ m.hrl \
+ m_i.hrl \
+ oe_c_erl_test.hrl
+
+GEN_ERL_FILES = \
+ m.erl \
+ m_arr2.erl \
+ m_arr3.erl \
+ m_i.erl \
+ m_str3.erl \
+ oe_c_erl_test.erl
+
+C_FILES = $(GEN_C_FILES) c_client.c my.c
+
+OBJS = $(C_FILES:.c=@obj@)
+
+PGMS = c_client@exe@
+
+ERL_FILES = $(GEN_ERL_FILES) m_i_impl.erl
+
+EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
+
+
+all: $(PGMS) $(EBINS)
+
+clean:
+ -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+ -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+
+$(PGMS): $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" \
+ "+{user_protocol,my}" "+{c_timeout,{5000,5000}}" c_erl_test.idl
+
+$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl
+
+.c@obj@:
+ $(CC) -c -o $*@obj@ $(CFLAGS) $<
+
+.erl.@EMULATOR@:
+ $(ERLC) -I $(IC_INCLUDE_PATH) $<
+
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
new file mode 100644
index 0000000000..b2c5b0c836
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_client.c
@@ -0,0 +1,1763 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+/* C-client for test of IC.
+ *
+ * TODO:
+ *
+ * 1. XXX #includes for VxWorks, Windows
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+
+#include <string.h>
+
+#ifdef __WIN32__
+# include <time.h>
+# include <sys/timeb.h>
+#elif defined VXWORKS
+#include <time.h>
+#include <sys/times.h>
+#else
+#include <sys/time.h>
+#endif
+
+#include <ctype.h>
+
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+
+#include "ei.h"
+#include "erl_interface.h"
+#include "m_i.h"
+
+#define HOSTNAMESZ 256
+#define NODENAMESZ 512
+
+#define INBUFSZ 10
+#define OUTBUFSZ 0
+
+#define MAXTRIES 5
+
+#define CHECK_EXCEPTION(x) \
+ if ((x)->_major != CORBA_NO_EXCEPTION) { \
+ fprintf(stderr,"\n\nException: %s\n\n", \
+ (char *)CORBA_exception_value((x))); \
+ CORBA_exception_free((x)); \
+ return -1; \
+ } \
+
+/* XXX Should free things here too! */
+#define RETURN_IF_OK(x) \
+ if ((x)) {\
+ fprintf(stdout, "ok\n");\
+ return 0;\
+ }\
+
+#define cmp_str(x,y) (!strcmp((x),(y)))
+#define cmp_wstr(x,y) (!ic_wstrcmp((x),(y)))
+
+typedef CORBA_Environment IC_Env;
+
+typedef int (*TestFunc)(IC_Env *);
+typedef struct {
+ char *name;
+ TestFunc func;
+} TestCase;
+
+static char longtext[] =
+"Introduction The IC application is an IDL compiler implemented in Erlang."
+" The IDL compiler generates client stubs and server skeletons."
+" Several back-ends are supported, and they fall into three main groups."
+" For more details on IC compiler options consult the ic(3) manual page."
+" Argument passing cases 1 Caller allocates all necessary storage,"
+" except that which may be encapsulated and managed within the parameter itself."
+" 2 The caller allocates a pointer and passes it by reference to the callee."
+" The callee sets the pointer to point to a valid instance of the parameter's type."
+" The caller is responsible for releasing the returned storage."
+" Following completion of a request, the caller is not allowed to modify any values"
+" in the returned storage. To do so the caller must first copy the returned instance"
+" into a new instance, then modify the new instance. 3 The caller allocates a"
+" pointer to an array slice which has all the same dimensions of the original"
+" array except the first, and passes it by reference to the callee. The callee sets"
+" the pointer to point to a valid instance of the array. The caller is responsible for"
+" releasing the returned storage. Following completion of a request, the caller is not"
+" allowed to modify any values in the returned storage. To do so the caller must first"
+" copy the returned instance into a new instance, then modify the new instance."
+" Generated Files Two files will be generated for each scope. One set of files will be"
+" generated for each module and each interface scope. An extra set is generated for"
+" those definitions at top level scope. One of the files is a header file(.h), and the"
+" other file is a C source code file (.c). In addition to these files a number of C"
+" source files will be generated for type encodings, they are named according to the "
+"following template: oe_code_<type>.c.";
+static char this_node[NODENAMESZ + 1];
+static char *progname;
+
+/* Test function prototypes */
+
+static int void_test(IC_Env *env);
+static int long_test(IC_Env *env);
+static int long_long_test(IC_Env *env);
+static int unsigned_short_test(IC_Env *env);
+static int unsigned_long_test(IC_Env *env);
+static int unsigned_long_long_test(IC_Env *env);
+static int double_test(IC_Env *env);
+static int char_test(IC_Env *env);
+static int wchar_test(IC_Env *env);
+static int octet_test(IC_Env *env);
+static int bool_test(IC_Env *env);
+static int struct_test(IC_Env *env);
+static int struct2_test(IC_Env *env);
+static int seq1_test(IC_Env *env);
+static int seq2_test(IC_Env *env);
+static int seq3_test(IC_Env *env);
+static int seq4_test(IC_Env *env);
+static int seq5_test(IC_Env *env);
+static int array1_test(IC_Env *env);
+static int array2_test(IC_Env *env);
+static int enum_test(IC_Env *env);
+static int string1_test(IC_Env *env);
+static int string2_test(IC_Env *env);
+static int string3_test(IC_Env *env);
+static int string4_test(IC_Env *env);
+static int pid_test(IC_Env *env);
+static int port_test(IC_Env *env);
+static int ref_test(IC_Env *env);
+static int term_test(IC_Env *env);
+static int typedef_test(IC_Env *env);
+static int inline_sequence_test(IC_Env *env);
+static int term_sequence_test(IC_Env *env);
+static int term_struct_test(IC_Env *env);
+static int wstring1_test(IC_Env *env);
+
+static TestCase test_cases[] = {
+ {"void_test", void_test},
+ {"long_test", long_test},
+ {"long_long_test", long_long_test},
+ {"unsigned_short_test", unsigned_short_test},
+ {"unsigned_long_test", unsigned_long_test},
+ {"unsigned_long_long_test", unsigned_long_long_test},
+ {"double_test", double_test},
+ {"char_test", char_test},
+ {"wchar_test", wchar_test},
+ {"octet_test", octet_test},
+ {"bool_test", bool_test},
+ {"struct_test", struct_test},
+ {"struct2_test", struct2_test},
+ {"seq1_test", seq1_test},
+ {"seq2_test", seq2_test},
+ {"seq3_test", seq3_test},
+ {"seq4_test", seq4_test},
+ {"seq5_test", seq5_test},
+ {"array1_test", array1_test},
+ {"array2_test", array2_test},
+ {"enum_test", enum_test},
+ {"string1_test", string1_test},
+ {"string2_test", string2_test},
+ {"string3_test", string3_test},
+ {"string4_test", string4_test},
+ {"pid_test", pid_test},
+ {"port_test", port_test},
+ {"ref_test", ref_test},
+ {"term_test", term_test},
+ {"typedef_test", typedef_test},
+ {"inline_sequence_test", inline_sequence_test},
+ {"term_sequence_test", term_sequence_test},
+ {"term_struct_test", term_struct_test},
+ {"wstring1_test", wstring1_test},
+ {"", NULL}
+};
+
+/* Other prototypes */
+static int cmp_aseq(m_aseq *a1, m_aseq *a2);
+static int cmp_a(m_a *a1, m_a *a2);
+static int cmp_bseq(m_bseq *b1, m_bseq *b2);
+static int cmp_b(m_b *b1, m_b *b2);
+static int cmp_lseq(m_lseq *b1, m_lseq *b2);
+static int cmp_etseq(m_etseq *b1, m_etseq *b2);
+static int cmp_et(m_et* b1, m_et *b2);
+static int cmp_es(m_es *b1, m_es *b2);
+static int cmp_arr1(m_arr1 b1, m_arr1 b2);
+static int cmp_dd(m_dd b1, m_dd b2);
+static int cmp_strRec(m_strRec *b1, m_strRec *b2);
+static int cmp_sseq(m_sseq *b1, m_sseq *b2);
+static int cmp_pid(erlang_pid *p1, erlang_pid *p2);
+static int cmp_port(erlang_port *p1, erlang_port *p2);
+static int cmp_ref(erlang_ref *p1, erlang_ref *p2);
+static int cmp_s(m_s *b1, m_s *b2);
+static int cmp_ssstr3(m_ssstr3 *b1, m_ssstr3 *b2);
+static int cmp_ssarr3(m_ssarr3 *b1, m_ssarr3 *b2);
+static int cmp_sarr3(m_sarr3 *b1, m_sarr3 *b2);
+static int cmp_arr3(m_arr3 b1, m_arr3 b2);
+
+static void print_aseq(m_aseq *a);
+static void print_a(m_a *a);
+static void print_bseq(m_bseq *b);
+static void print_lseq(m_lseq *b);
+static void print_b(m_b *b);
+static void print_etseq(m_etseq *b);
+static void print_et(m_et* b);
+static void print_es(m_es *b);
+static void print_arr1(long a[500]);
+static void print_dd(long a[2][3]);
+static void print_strRec(m_strRec* sr);
+static void print_sseq(m_sseq *b);
+static void print_pid(erlang_pid *p);
+static void print_port(erlang_port *p);
+static void print_ref(erlang_ref *p);
+static void print_term(ETERM *t);
+static void print_s(m_s *p);
+static void print_ssstr3(m_ssstr3 *b1);
+static void print_ssarr3(m_ssarr3 *b1);
+static void print_sarr3(m_sarr3 *b1);
+static void print_arr3(m_arr3 b1);
+static void print_wstr(CORBA_wchar *ws);
+
+static void free_etseq_buf(m_etseq *b);
+static void free_et(m_et* b);
+
+#ifdef __WIN32__
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} MyTimeval;
+#else
+typedef struct timeval MyTimeval;
+#endif
+static void my_gettimeofday(MyTimeval *tv);
+static void showtime(MyTimeval *start, MyTimeval *stop);
+static void usage(void);
+static void done(int r);
+
+
+
+/* main */
+
+#ifdef VXWORKS
+int client(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ struct hostent *hp;
+ erlang_pid pid;
+ MyTimeval start, stop;
+ int i, fd, ires, tres;
+ IC_Env *env;
+ int tries = 0;
+ char *this_node_name = NULL;
+ char *peer_node = NULL;
+ char *peer_process_name = NULL;
+ char *cookie = NULL;
+ char host[HOSTNAMESZ + 1];
+ TestFunc test_func = NULL;
+ TestCase *test_case;
+ char *test_case_name = NULL;
+
+#ifdef __WIN32__
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ fprintf(stderr, "Could not load winsock2 v2.0 compatible DLL");
+ exit(1);
+ }
+#endif
+
+ progname = argv[0];
+ host[HOSTNAMESZ] = '\0';
+ if (gethostname(host, HOSTNAMESZ) < 0) {
+ fprintf(stderr, "Can't find own hostname\n");
+ done(1);
+ }
+ if ((hp = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Can't get ip address for host %s\n", host);
+ done(1);
+ }
+ for (i = 1; i < argc; i++) {
+ if (cmp_str(argv[i], "-help")) {
+ usage();
+ done(0);
+ } else if (cmp_str(argv[i], "-this-node-name")) {
+ i++;
+ this_node_name = argv[i];
+ } else if (cmp_str(argv[i], "-peer-node")) {
+ i++;
+ peer_node = argv[i];
+ } else if (cmp_str(argv[i], "-peer-process-name")) {
+ i++;
+ peer_process_name = argv[i];
+ } else if (cmp_str(argv[i], "-cookie")) {
+ i++;
+ cookie = argv[i];
+ } else if (cmp_str(argv[i], "-test-case")) {
+ i++;
+ test_case_name = argv[i];
+ } else {
+ fprintf(stderr, "Error : invalid argument \"%s\"\n", argv[i]);
+ usage();
+ done(1);
+ }
+ }
+
+ if (this_node_name == NULL || peer_node == NULL || test_case_name == NULL
+ || peer_process_name == NULL || cookie == NULL) {
+ fprintf(stderr, "Error: missing option\n");
+ usage();
+ done(1);
+ }
+
+ test_case = test_cases;
+ while (test_case->func) {
+ if (cmp_str(test_case->name, test_case_name)) {
+ test_func = test_case->func;
+ break;
+ }
+ test_case++;
+ }
+ if (test_func == NULL) {
+ fprintf(stderr, "Error: illegal test case: \"%s\"\n", test_case_name);
+ done(1);
+ }
+
+ /* Behead hostname at first dot */
+ for (i=0; host[i] != '\0'; i++) {
+ if (host[i] == '.') { host[i] = '\0'; break; }
+ }
+ sprintf(this_node, "%s@%s", this_node_name, host);
+ fprintf(stderr, "c_client: this node: \"%s\"\n", this_node);
+ fprintf(stderr, "c_client: peer node: \"%s\"\n", peer_node);
+ fprintf(stderr, "c_client: test case: \"%s\"\n", test_case_name);
+
+ fprintf(stderr, "c_client: starting\n");
+
+ /* initialize erl_interface */
+ erl_init(NULL, 0);
+
+ for (tries = 0; tries < MAXTRIES; tries++) {
+
+ /* connect to erlang node */
+
+ ires = erl_connect_xinit(host, this_node_name, this_node,
+ (struct in_addr *)*hp->h_addr_list,
+ cookie, 0);
+
+ fprintf(stderr, "c_client: erl_connect_xinit(): %d\n", ires);
+
+ fd = erl_connect(peer_node);
+ fprintf(stderr, "c_client: erl_connect(): %d\n", fd);
+
+ if (fd >= 0)
+ break;
+ fprintf(stderr, "c_client: cannot connect, retrying\n");
+ }
+ if (fd < 0) {
+ fprintf(stderr, "c_client: cannot connect, exiting\n");
+ done(1);
+ }
+ env = CORBA_Environment_alloc(INBUFSZ, OUTBUFSZ);
+ env->_fd = fd;
+ strcpy(env->_regname, peer_process_name);
+ env->_to_pid = NULL;
+ env->_from_pid = &pid;
+
+ strcpy(pid.node, this_node);
+ pid.num = fd;
+ pid.serial = 0;
+ pid.creation = 0;
+
+ my_gettimeofday(&start);
+ tres = test_func(env); /* Call test case */
+ my_gettimeofday(&stop);
+ showtime(&start, &stop);
+ erl_close_connection(fd);
+
+ printf("c_client: env->_inbuf before : %d\n", INBUFSZ);
+ printf("c_client: env->_outbuf before : %d\n", OUTBUFSZ);
+ printf("c_client: env->_inbuf after : %d\n", env->_inbufsz);
+ printf("c_client: env->_outbuf after : %d\n", env->_outbufsz);
+
+ CORBA_free(env->_inbuf);
+ CORBA_free(env->_outbuf);
+ CORBA_free(env);
+ done(tres);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [-help] -this-node-name <name> "
+ "-peer-node <nodename> -peer-process-name <name> "
+ "-cookie <cookie> -test-case <test case name>\n", progname);
+ fprintf(stderr, "Example:\n %s -this-node-name kalle "
+ "-peer-node olle@home -peer-process-name idltest "
+ "-cookie oa678er -test-case octet_test\n", progname);
+}
+
+static void done(int r)
+{
+#ifdef __WIN32__
+ WSACleanup();
+#endif
+ exit(r);
+}
+
+
+/* TESTS */
+
+static int void_test(IC_Env *env)
+{
+ fprintf(stdout, "\n======== m_i_void test ======\n\n");
+ m_i_void_test(NULL,env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(1);
+}
+
+static int long_test(IC_Env *env)
+{
+ long l = 4711, lo, lr;
+
+ fprintf(stdout, "\n======== m_i_long test ======\n\n");
+ lr = m_i_long_test(NULL, l, &lo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(l == lo && l == lr);
+ if (l != lo)
+ fprintf(stdout, " out parameter error, sent: %ld, got: %ld\n", l, lo);
+ if (l != lr)
+ fprintf(stdout, " result error, sent: %ld, got: %ld\n", l, lr);
+ return -1;
+}
+
+static int long_long_test(IC_Env *env)
+{
+ CORBA_long_long ll = 4711, llo, llr;
+
+ fprintf(stdout, "\n======== m_i_longlong test ======\n\n");
+ llr = m_i_longlong_test(NULL, ll, &llo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ll == llo && ll == llr);
+ if (ll != llo)
+ fprintf(stdout, " out parameter error, sent: %ld, got: %ld\n",
+ ll, llo);
+ if (ll != llr)
+ fprintf(stdout, " result error, sent: %ld, got: %ld\n", ll, llr);
+ return -1;
+}
+
+static int unsigned_short_test(IC_Env *env)
+{
+ unsigned short x, y = 2, z;
+
+ fprintf(stdout, "\n======== m_i_ushort test ======\n\n");
+ x = m_i_ushort_test(NULL, y, &z, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(y == z && y == x);
+ if (y != z)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", y, z);
+ if (y != x)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", y, x);
+ return -1;
+}
+
+
+static int unsigned_long_test(IC_Env *env)
+{
+ unsigned long ul = 5050, ulo, ulr;
+
+ fprintf(stdout, "\n======== m_i_ulong test ======\n\n");
+ ulr = m_i_ulong_test(NULL, ul, &ulo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ul == ulo && ul == ulr);
+ if (ul != ulo)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ ul, ulo);
+ if (ul != ulr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n", ul, ulr);
+ return -1;
+}
+
+/*
+ * Note: CORBA_unsigned_long_long is in fact a plain long.
+ */
+static int unsigned_long_long_test(IC_Env *env)
+{
+ CORBA_unsigned_long_long ull = 5050, ullo, ullr;
+
+ fprintf(stdout, "\n======== m_i_ulonglong test ======\n\n");
+ ullr = m_i_ulonglong_test(NULL, ull, &ullo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ull == ullo && ull == ullr);
+ if (ull != ullo)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ ull, ullo);
+ if (ull != ullr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n",
+ ull, ullr);
+ return -1;
+}
+
+static int double_test(IC_Env *env)
+{
+ double d = 12.1212, db, dr;
+
+ fprintf(stdout, "\n======== m_i_double test ======\n\n");
+ dr = m_i_double_test(NULL, d, &db, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(d == db && d == dr);
+ if (d != db)
+ fprintf(stdout, " out parameter error, sent: %f, got: %f\n", d, db);
+ if (d != dr)
+ fprintf(stdout, " result error, sent: %f, got: %f\n", d, dr);
+ return -1;
+}
+
+static int char_test(IC_Env *env)
+{
+ char c = 'g', co, cr;
+
+ /* char test */
+ fprintf(stdout, "\n======== m_i_char test ======\n\n");
+ cr = m_i_char_test(NULL, c, &co, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(c == co && c == cr);
+ if (c !=co)
+ fprintf(stdout, " out parameter error, sent: %c, got: %c\n", c, co);
+ if (c != cr)
+ fprintf(stdout, " result error, sent: %c, got: %c\n", c, cr);
+ return -1;
+}
+
+static int wchar_test(IC_Env *env)
+{
+ CORBA_wchar wc = 103, wco, wcr;
+
+ fprintf(stdout, "\n======== m_i_wchar test ======\n\n");
+ wcr = m_i_wchar_test(NULL, wc, &wco, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(wc == wco && wc == wcr);
+ if (wc != wco)
+ fprintf(stdout, " out parameter error, sent: %lu, got: %lu\n",
+ wc, wco);
+ if (wc != wcr)
+ fprintf(stdout, " result error, sent: %lu, got: %lu\n",
+ wc, wcr);
+ return -1;
+}
+
+static int octet_test(IC_Env *env)
+{
+ char o ='r', oo, or;
+
+ fprintf(stdout, "\n======== m_i_octet test ======\n\n");
+ or = m_i_octet_test(NULL, o, &oo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(o == oo && o == or);
+ if (o != oo)
+ fprintf(stdout, " out parameter error, sent: %c, got: %c\n", o, oo);
+ if (o != or)
+ fprintf(stdout, " result error, sent: %c, got: %c\n", o, or);
+ return -1;
+}
+
+static int bool_test(IC_Env *env)
+{
+ unsigned char i = 0, io, ir;
+
+ fprintf(stdout, "\n======== m_i_bool test ======\n\n");
+ ir = m_i_bool_test(NULL, i, &io, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(i == io && i == ir);
+ if (i != io)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", i, io);
+ if (i != ir)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", i, ir);
+ return -1;
+}
+
+static int struct_test(IC_Env *env)
+{
+ m_b b = {4711, 'a'}, bo, br;
+
+ fprintf(stdout, "\n======== m_i_struct test ======\n\n");
+ br = m_i_struct_test(NULL, &b, &bo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_b(&b, &bo) && cmp_b(&b, &br));
+ if (!cmp_b(&b, &bo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_b(&b);
+ fprintf(stdout, " got:\n");
+ print_b(&bo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_b(&b, &br)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_b(&b);
+ fprintf(stdout, " got:\n");
+ print_b(&br);
+ fprintf(stdout, "\n");
+ }
+ return -1;
+}
+
+static int struct2_test(IC_Env *env)
+{
+ m_es esi = {m_peach, 5050}, eso, esr;
+
+ fprintf(stdout, "\n======== m_i_struct2 test ======\n\n");
+ esr = m_i_struct2_test(NULL, &esi, &eso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_es(&esi, &eso) && cmp_es(&esi, &esr));
+ if (!cmp_es(&esi, &eso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_es(&esi);
+ fprintf(stdout, " got:\n");
+ print_es(&eso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_es(&esi, &esr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_es(&esi);
+ fprintf(stdout, " got:\n");
+ print_es(&esr);
+ fprintf(stdout, "\n");
+ }
+ return -1;
+}
+
+
+static int seq1_test(IC_Env *env)
+{
+ m_bseq bs, *bso, *bsr;
+
+ m_b ba[3] = {{4711, 'a'}, {4712, 'b'}, {4713, 'c'}};
+ bs._length = 3;
+ bs._buffer = ba;
+
+ fprintf(stdout, "\n======== m_i_seq1 test ======\n\n");
+ bsr = m_i_seq1_test(NULL, &bs, &bso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_bseq(&bs, bso) && cmp_bseq(&bs, bsr));
+ if (!cmp_bseq(&bs, bso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_bseq(&bs);
+ fprintf(stdout, " got:\n");
+ print_bseq(bso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_bseq(&bs, bsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_bseq(&bs);
+ fprintf(stdout, " got:\n");
+ print_bseq(bsr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(bso);
+ CORBA_free(bsr);
+ return -1;
+}
+
+static int seq2_test(IC_Env *env)
+{
+ m_b ba[3] = {{4711, 'a'}, {4712, 'b'}, {4713, 'c'}};
+ m_a a;
+ m_a aa[2];
+ m_aseq as, *aso, *asr;
+
+ a.l = 9999;
+ a.y._length = 3;
+ a.y._buffer = ba;
+ a.d = 66.89898989;
+
+ aa[0] = a;
+ aa[1] = a;
+ as._length = 2;
+ as._buffer = aa;
+
+ fprintf(stdout, "\n======== m_i_seq2 test ======\n\n");
+ asr = m_i_seq2_test(NULL, &as, &aso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_aseq(&as, aso) && cmp_aseq(&as, asr));
+ if (!cmp_aseq(&as, aso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_aseq(&as);
+ fprintf(stdout, " got:\n");
+ print_aseq(aso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_aseq(&as, asr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_aseq(&as);
+ fprintf(stdout, " got:\n");
+ print_aseq(asr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(aso);
+ CORBA_free(asr);
+ return -1;
+}
+
+static int seq3_test(IC_Env *env)
+{
+ m_lseq lsi, *lso, *lsr;
+ long al[500];
+ int i=0;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+ lsi._length = 500;
+ lsi._buffer = al;
+
+ fprintf(stdout, "\n======== m_i_seq3 test ======\n\n");
+ lsr = m_i_seq3_test(NULL, &lsi, &lso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_lseq(&lsi, lso) && cmp_lseq(&lsi, lsr));
+ if (!cmp_lseq(&lsi, lso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_lseq(&lsi);
+ fprintf(stdout, " got:\n");
+ print_lseq(lso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_lseq(&lsi, lsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_lseq(&lsi);
+ fprintf(stdout, " got:\n");
+ print_lseq(lsr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(lso);
+ CORBA_free(lsr);
+ return -1;
+}
+
+static int seq4_test(IC_Env *env)
+{
+ char *stra0[3] = {"a", "long", "time"};
+ char *stra1[3] = {"ago", "there", "was"};
+ char *stra2[3] = {"a", "buggy", "compiler"};
+ m_sstr3 str3s[3] = {{3, 3, stra0}, {3, 3, stra1}, {3, 3, stra2}};
+ m_ssstr3 str3ssi = {3, 3, str3s};
+ m_ssstr3 *str3sso, *str3ssr;
+
+ fprintf(stdout, "\n======== m_i_seq4 test ======\n\n");
+ str3ssr = m_i_seq4_test(NULL, &str3ssi, &str3sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ssstr3(&str3ssi, str3sso) &&
+ cmp_ssstr3(&str3ssi, str3ssr));
+ if (!cmp_ssstr3(&str3ssi, str3sso)){
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ssstr3(&str3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssstr3(str3sso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_ssstr3(&str3ssi, str3ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ssstr3(&str3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssstr3(str3ssr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(str3sso);
+ CORBA_free(str3ssr);
+ return -1;
+}
+
+static int seq5_test(IC_Env *env)
+{
+ m_arr3 arr3a[3] = {
+ {4711, 18931947, 3},
+ {4711, 18931947, 3},
+ {4711, 18931947, 3}};
+ m_sarr3 arr3sa[3] = {{3, 3, arr3a}, {3, 3, arr3a}, {3, 3, arr3a}};
+ m_ssarr3 arr3ssi = {3, 3, arr3sa};
+ m_ssarr3 *arr3sso;
+ m_ssarr3 *arr3ssr;
+
+ fprintf(stdout, "\n======== m_i_seq5 test ======\n\n");
+ arr3ssr = m_i_seq5_test(NULL, &arr3ssi, &arr3sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ssarr3(&arr3ssi, arr3sso) &&
+ cmp_ssarr3(&arr3ssi, arr3ssr));
+ if (!cmp_ssarr3(&arr3ssi, arr3sso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ssarr3(&arr3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssarr3(arr3sso);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_ssarr3(&arr3ssi, arr3ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ssarr3(&arr3ssi);
+ fprintf(stdout, " got:\n");
+ print_ssarr3(arr3ssr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(arr3sso);
+ CORBA_free(arr3ssr);
+ return -1;
+}
+
+static int array1_test(IC_Env *env)
+{
+ int i;
+ long al[500];
+ m_arr1 alo;
+ m_arr1_slice* alr;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+
+ fprintf(stdout, "\n======== m_i_array1 test ======\n\n");
+ alr = m_i_array1_test(NULL, al, alo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_arr1(al, alo) && cmp_arr1(al, alr));
+ if (!cmp_arr1(al, alo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_arr1(al);
+ fprintf(stdout, " got:\n");
+ print_arr1(alo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_arr1(al,alr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_arr1(al);
+ fprintf(stdout, " got:\n");
+ print_arr1(alr);
+ fprintf(stdout, "\n");
+ }
+ free(alo);
+ free(alr);
+ return -1;
+}
+
+static int array2_test(IC_Env *env)
+{
+ long dl[2][3] = {{11, 2, 7}, {22, 8 ,13}};
+ m_dd dlo;
+ m_dd_slice* dlr;
+
+ fprintf(stdout, "\n======== m_i_array2 test ======\n\n");
+ dlr = m_i_array2_test(NULL, dl, dlo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_dd(dl,dlo) && cmp_dd(dl,dlr));
+ if (!cmp_dd(dl,dlo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_dd(dl);
+ fprintf(stdout, " got:\n");
+ print_dd(dlo);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_dd(dl,dlr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_dd(dl);
+ fprintf(stdout, " got:\n");
+ print_dd(dlr);
+ fprintf(stdout, "\n");
+ }
+ free(*dlr);
+ return -1;
+}
+
+static int enum_test(IC_Env *env)
+{
+ m_fruit ei = m_banana, eo, er;
+
+ fprintf(stdout, "\n======== m_i_enum test ======\n\n");
+ er = m_i_enum_test(NULL, ei, &eo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(ei == eo && ei == er);
+ if (ei != eo)
+ fprintf(stdout, " out parameter error, sent: %d, got: %d\n", ei, eo);
+ if (ei != er)
+ fprintf(stdout, " result error, sent: %d, got: %d\n", ei, er);
+ return -1;
+}
+
+static int string1_test(IC_Env *env)
+{
+ char* si = longtext;
+ char* so;
+ char* sr;
+
+ fprintf(stdout, "\n======== m_i_string1 test ======\n\n");
+ sr = m_i_string1_test(NULL, si, &so, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_str(si, so) && cmp_str(si, sr));
+ if (!cmp_str(si, so))
+ fprintf(stdout, " out parameter error, sent: %s, got: %s\n", si, so);
+ if (!cmp_str(si, sr))
+ fprintf(stdout, " result error, sent: %s, got: %s\n", si, sr);
+ CORBA_free(so);
+ CORBA_free(sr);
+ return -1;
+}
+
+static int string2_test(IC_Env *env)
+{
+ char* sa[3] = {"hello", "foo", "bar"};
+ m_sseq ssi = {3, 3, sa};
+ m_sseq *sso, *ssr;
+
+ fprintf(stdout, "\n======== m_i_string2 test ======\n\n");
+ ssr = m_i_string2_test(NULL, &ssi, &sso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_sseq(&ssi, sso) && cmp_sseq(&ssi, sso));
+ if (!cmp_sseq(&ssi, sso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_sseq(&ssi);
+ fprintf(stdout, "got:\n");
+ print_sseq(sso);
+ }
+ if (!cmp_sseq(&ssi, ssr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_sseq(&ssi);
+ fprintf(stdout, "got:\n");
+ print_sseq(ssr);
+ }
+ CORBA_free(sso);
+ CORBA_free(ssr);
+ return -1;
+}
+
+static int string3_test(IC_Env *env)
+{
+ char* si = longtext;
+ char* so;
+ char* sr;
+
+ fprintf(stdout, "\n======== m_i_string3 test ======\n\n");
+ sr = m_i_string3_test(NULL, si, &so, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_str(si, so) && cmp_str(si, so));
+ if (!cmp_str(si, so))
+ fprintf(stdout, " out parameter error, sent: %s, got: %s\n", si, so);
+ if (!cmp_str(si, sr))
+ fprintf(stdout, " result error, sent: %s, got: %s\n", si, sr);
+ CORBA_free(so);
+ CORBA_free(sr);
+ return -1;
+}
+
+static int string4_test(IC_Env *env)
+{
+ char as1[100] = "a string", as2[200] = "help", as3[200] = "hello there";
+ m_strRec stri = { 1, /* dd */
+ as1, /* str4 */
+ {{'a', 'k'}, {'z', 'g'}, {'n', 'q'}}, /* str7 */
+ {3, 3, "buf"}, /* str5 */
+ as2, /* str6 */
+ {'m', 'f', 'o'}, /* str8 */
+ as3, /* str9 */
+ {3, 3, "stu"} /* str10 */
+ };
+ m_strRec *stro, *strr;
+
+ fprintf(stdout, "\n======== m_i_string4 test ======\n\n");
+ strr = m_i_string4_test(NULL, &stri, &stro, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_strRec(&stri,stro) && cmp_strRec(&stri,strr));
+ if (!cmp_strRec(&stri,stro)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_strRec(&stri);
+ fprintf(stdout, " got:\n");
+ print_strRec(stro);
+ fprintf(stdout, "\n");
+ }
+ if (!cmp_strRec(&stri,strr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_strRec(&stri);
+ fprintf(stdout, " got:\n");
+ print_strRec(strr);
+ fprintf(stdout, "\n");
+ }
+ CORBA_free(stro);
+ CORBA_free(strr);
+ return -1;
+}
+
+
+static int pid_test(IC_Env *env)
+{
+ erlang_pid pid = {"", 7, 0, 0}, pido, pidr;
+
+ strcpy(pid.node, this_node), /* this currently running node */
+ fprintf(stdout, "\n======== m_i_pid test ======\n\n");
+ pidr = m_i_pid_test(NULL, &pid, &pido, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_pid(&pid, &pido) && cmp_pid(&pid, &pidr));
+ if (!cmp_pid(&pid, &pido)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_pid(&pid);
+ fprintf(stdout, "got:\n");
+ print_pid(&pido);
+ }
+ if (!cmp_pid(&pid, &pidr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_pid(&pid);
+ fprintf(stdout, "got:\n");
+ print_pid(&pidr);
+ }
+ return -1;
+}
+
+static int port_test(IC_Env *env)
+{
+ erlang_port porti = {"node", 5, 1}, porto, portr;
+
+ fprintf(stdout, "\n======== m_i_port test ======\n\n");
+ portr = m_i_port_test(NULL, &porti, &porto, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_port(&porti, &porto) && cmp_port(&porti, &portr));
+ if (!cmp_port(&porti, &porto)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_port(&porti);
+ fprintf(stdout, "got:\n");
+ print_port(&porto);
+ }
+ if (!cmp_port(&porti, &portr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_port(&porti);
+ fprintf(stdout, "got:\n");
+ print_port(&portr);
+ }
+ return -1;
+}
+
+static int ref_test(IC_Env *env)
+{
+ erlang_ref refi = { "node1", 3, {1, 2, 3}, 1},
+ refo, refr;
+
+ fprintf(stdout, "\n======== m_i_ref test ======\n\n");
+ refr = m_i_ref_test(NULL, &refi, &refo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_ref(&refi, &refo) && cmp_ref(&refi, &refr));
+ if (!cmp_ref(&refi, &refo)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_ref(&refi);
+ fprintf(stdout, "got:\n");
+ print_ref(&refo);
+ }
+ if (!cmp_ref(&refi, &refr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_ref(&refi);
+ fprintf(stdout, "got:\n");
+ print_ref(&refr);
+ }
+ return -1;
+}
+
+static int term_test(IC_Env *env)
+{
+ ETERM *ti, *to, *tr;
+
+ ti = erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]");
+
+ fprintf(stdout, "\n======== m_i_term test ======\n\n");
+ tr = m_i_term_test(NULL, ti, &to, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(erl_match(ti, to) && erl_match(ti, tr));
+ if (!erl_match(ti, to)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_term(ti);
+ fprintf(stdout, "got:\n");
+ print_term(to);
+ }
+ if (!erl_match(ti, tr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_term(ti);
+ fprintf(stdout, "got:\n");
+ print_term(tr);
+ }
+ erl_free_term(ti);
+ erl_free_term(to);
+ erl_free_term(tr);
+ return -1;
+}
+
+static int typedef_test(IC_Env *env)
+{
+ m_banan mbi, mbo; /* erlang_port */
+ m_apa mai; /* ETERM* */
+ m_apa mao = NULL;
+ long tl;
+
+ strcpy(mbi.node,"node");
+ mbi.id = 15;
+ mbi.creation = 1;
+
+ fprintf(stdout, "\n======== m_i_typedef test ======\n\n");
+ mai = erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]");
+ tl = m_i_typedef_test(NULL, mai, &mbi, &mao, &mbo, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(erl_match(mai, mao) && cmp_port(&mbi, &mbo) && tl == 4711);
+ if (!erl_match(mai, mao)) {
+ fprintf(stdout, " out parameter error (term), sent:\n");
+ print_term(mai);
+ fprintf(stdout, "got:\n");
+ print_term(mao);
+ }
+ if (!cmp_port(&mbi, &mbo)) {
+ fprintf(stdout, " out parameter error (port), sent:\n");
+ print_port(&mbi);
+ fprintf(stdout, "got:\n");
+ print_port(&mbo);
+ }
+ if (tl != 4711) {
+ fprintf(stdout, " result error, sent: 4711, got %ld\n", tl);
+ }
+ erl_free_term(mai);
+ erl_free_term(mao);
+ return -1;
+}
+
+static int inline_sequence_test(IC_Env *env)
+{
+ int i;
+ long al[500];
+ m_s isi = {4711, {500, 10, al}},
+ *iso, *isr;
+
+ for (i = 0; i < 500; i++)
+ al[i]=i;
+ fprintf(stdout, "\n======== m_i_inline_sequence test ======\n\n");
+ isr = m_i_inline_sequence_test(NULL, &isi, &iso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_s(&isi, iso) && cmp_s(&isi, isr));
+ if (!cmp_s(&isi, iso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_s(&isi);
+ fprintf(stdout, "got:\n");
+ print_s(iso);
+ }
+ if (!cmp_s(&isi, isr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_s(&isi);
+ fprintf(stdout, "got:\n");
+ print_s(isr);
+ }
+ CORBA_free(iso);
+ CORBA_free(isr);
+ return -1;
+}
+
+static int term_sequence_test(IC_Env *env)
+{
+ ETERM* et_array[4] = {
+ erl_format("[{apa, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{banan, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{apelsin, 1, 23}, \"string\", {1.23, 45}]"),
+ erl_format("[{mango, 1, 23}, \"string\", {1.23, 45}]")};
+ m_etseq etsi = {4, 4, et_array}, *etso, *etsr;
+
+ fprintf(stdout, "\n======== m_i_term_sequence test ======\n\n");
+ etsr = m_i_term_sequence_test(NULL, &etsi, &etso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_etseq(&etsi, etso) && cmp_etseq(&etsi, etsr));
+ if (!cmp_etseq(&etsi, etso)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_etseq(&etsi);
+ fprintf(stdout, "got:\n");
+ print_etseq(etso);
+ }
+ if (!cmp_etseq(&etsi, etsr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_etseq(&etsi);
+ fprintf(stdout, "got:\n");
+ print_etseq(etsr);
+ }
+ free_etseq_buf(&etsi);
+ free_etseq_buf(etso);
+ free_etseq_buf(etsr);
+ CORBA_free(etso);
+ CORBA_free(etsr);
+ return -1;
+}
+
+static int term_struct_test(IC_Env *env)
+{
+ m_et eti = { erl_format("[{hej, 1, 23}, \"string\", {1.23, 45}]"),
+ 121212 };
+ m_et eto, etr;
+
+ fprintf(stdout, "\n======== m_i_term_struct test ======\n\n");
+ etr = m_i_term_struct_test(NULL, &eti, &eto, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_et(&eti, &eto) && cmp_et(&eti, &etr));
+ if (!cmp_et(&eti, &eto)) {
+ fprintf(stdout, " out parameter error, sent:\n");
+ print_et(&eti);
+ fprintf(stdout, "got:\n");
+ print_et(&eto);
+ }
+ if (!cmp_et(&eti, &etr)) {
+ fprintf(stdout, " result error, sent:\n");
+ print_et(&eti);
+ fprintf(stdout, "got:\n");
+ print_et(&etr);
+ }
+ free_et(&eti);
+ free_et(&eto);
+ free_et(&etr);
+ return -1;
+}
+
+static int wstring1_test(IC_Env *env)
+{
+ CORBA_wchar wsi[] = {100, 101, 102, 103, 104, 0}, *wso, *wsr;
+
+ fprintf(stdout, "\n======== m_i_wstring1 test ======\n\n");
+ wsr = m_i_wstring1_test(NULL, wsi, &wso, env);
+ CHECK_EXCEPTION(env);
+ RETURN_IF_OK(cmp_wstr(wsi, wso) && cmp_wstr(wsi, wsr));
+ if (!cmp_wstr(wsi, wso)) {
+ fprintf(stdout, " out parameter error, sent: \n");
+ print_wstr(wsi);
+ fprintf(stdout, "got:\n");
+ print_wstr(wso);
+ }
+ if (!cmp_wstr(wsi, wsr)) {
+ fprintf(stdout, " result error, sent: \n");
+ print_wstr(wsi);
+ fprintf(stdout, "got:\n");
+ print_wstr(wsr);
+ }
+ CORBA_free(wso);
+ CORBA_free(wsr);
+ return -1;
+}
+
+/* Compare functions */
+static int cmp_aseq(m_aseq *a1, m_aseq *a2)
+{
+ int i;
+
+ if (a1->_length != a2->_length)
+ return 0;
+ for (i = 0; i < a1->_length; i++)
+ if (cmp_a(&(a1->_buffer[i]), &(a2->_buffer[i])) == 0)
+ return 0;
+ return 1;
+}
+
+static int cmp_a(m_a *a1, m_a *a2)
+{
+ return a1->l == a2->l &&
+ a1->d == a2->d &&
+ cmp_bseq(&a1->y, &a2->y);
+}
+
+static int cmp_bseq(m_bseq *b1, m_bseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (cmp_b(&(b1->_buffer[i]), &(b2->_buffer[i])) == 0)
+ return 0;
+ return 1;
+}
+
+static int cmp_b(m_b *b1, m_b *b2)
+{
+ return b1->l == b2->l && b1->c == b2->c;
+}
+
+static int cmp_lseq(m_lseq *b1, m_lseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (b1->_buffer[i] != b2->_buffer[i])
+ return 0;
+ return 1;
+}
+
+static int cmp_etseq(m_etseq *b1, m_etseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (!erl_match(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ return 1;
+}
+
+static int cmp_et(m_et* b1, m_et *b2)
+{
+ return erl_match(b1->e, b2->e) && b1->l == b2->l;
+}
+
+static int cmp_es(m_es *b1, m_es *b2)
+{
+ return b1->f == b2->f && b1->l == b2->l;
+}
+
+static int cmp_arr1(m_arr1 b1, m_arr1 b2)
+{
+ int i;
+
+ for (i = 0; i < 500; i++)
+ if (b1[i] != b2[i])
+ return 0;
+ return 1;
+}
+
+static int cmp_dd(m_dd b1, m_dd b2)
+{
+
+ int i, j;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ if (b1[i][j] != b2[i][j])
+ return 0;
+ return 1;
+}
+
+
+
+static int cmp_strRec(m_strRec *b1, m_strRec *b2)
+{
+ int i, j;
+
+ if (b1->bb != b2->bb)
+ return 0;
+ if (!cmp_str(b1->str4,b2->str4))
+ return 0;
+ if (b1->str5._length != b2->str5._length)
+ return 0;
+ for (j = 0; j < b1->str5._length; j++)
+ if (b1->str5._buffer[j] != b2->str5._buffer[j])
+ return 0;
+ if (!cmp_str(b1->str6,b2->str6))
+ return 0;
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ if (b1->str7[i][j] != b2->str7[i][j])
+ return 0;
+ for (j = 0; j < 3; j++)
+ if (b1->str8[j] != b2->str8[j])
+ return 0;
+ if (!cmp_str(b1->str9,b2->str9))
+ return 0;
+ if (b1->str10._length != b2->str10._length)
+ return 0;
+ for (j = 0; j < b1->str10._length; j++)
+ if (b1->str10._buffer[j] != b2->str10._buffer[j])
+ return 0;
+ return 1;
+}
+
+
+static int cmp_sseq(m_sseq *b1, m_sseq *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++)
+ if (!cmp_str(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ return 1;
+}
+
+
+static int cmp_pid(erlang_pid *p1, erlang_pid *p2)
+{
+ return cmp_str(p1->node,p2-> node) &&
+ p1->num == p2->num &&
+ p1->serial == p2->serial &&
+ p1->creation == p2->creation;
+}
+
+static int cmp_port(erlang_port *p1, erlang_port *p2)
+{
+ return cmp_str(p1->node,p2-> node) && p1->id == p2->id;
+}
+
+static int cmp_ref(erlang_ref *p1, erlang_ref *p2)
+{
+ return cmp_str(p1->node, p2->node) &&
+ p1->len == p2->len &&
+ (p1->len < 1 || p1->n[0] == p2->n[0]) &&
+ (p1->len < 2 || p1->n[1] == p2->n[1]) &&
+ (p1->len < 3 || p1->n[2] == p2->n[2]);
+}
+
+static int cmp_s(m_s *b1, m_s *b2)
+{
+ int i;
+
+ if (b1->l != b2->l)
+ return 0;
+ if (b1->sl._length != b2->sl._length)
+ return 0;
+ for (i = 0; i < b1->sl._length; i++)
+ if (b1->sl._buffer[i] != b2->sl._buffer[i])
+ return 0;
+ return 1;
+}
+
+
+static int cmp_ssstr3(m_ssstr3 *b1, m_ssstr3 *b2)
+{
+ int i,j;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (b1->_buffer[i]._length != b2->_buffer[i]._length)
+ return 0;
+ for (j = 0; j < b1->_buffer[i]._length; j++)
+ if (!cmp_str(b1->_buffer[i]._buffer[j],
+ b2->_buffer[i]._buffer[j]))
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static int cmp_ssarr3(m_ssarr3 *b1, m_ssarr3 *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (!cmp_sarr3(&b1->_buffer[i], &b2->_buffer[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_sarr3(m_sarr3 *b1, m_sarr3 *b2)
+{
+ int i;
+
+ if (b1->_length != b2->_length)
+ return 0;
+ for (i = 0; i < b1->_length; i++) {
+ if (!cmp_arr3(b1->_buffer[i], b2->_buffer[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static int cmp_arr3(m_arr3 b1, m_arr3 b2)
+{
+ int i;
+
+ for (i = 0; i < sizeof(m_arr3)/sizeof(CORBA_long); i++) {
+ if (b1[i] != b2[i])
+ return 0;
+ }
+ return 1;
+}
+
+/* Print functions */
+static void print_aseq(m_aseq *a)
+{
+ int i;
+ fprintf(stdout, "\nm_aseq size: %ld --------\n", a->_length);
+ for (i = 0; i < a->_length; i++)
+ print_a(&(a->_buffer[i]));
+}
+
+static void print_a(m_a *a)
+{
+ fprintf(stdout, "\nm_a --------\n l: %ld\n d:%f\n", a->l, a->d);
+ print_bseq(&a->y);
+}
+
+static void print_bseq(m_bseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_bseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ print_b(&(b->_buffer[i]));
+}
+
+static void print_lseq(m_lseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_lseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ fprintf(stdout, "[%d]: %ld\n", i, b->_buffer[i]);
+}
+
+static void print_b(m_b *b)
+{
+ fprintf(stdout, "\nm_b --------\n l: %ld\n c: %c\n", b->l, b->c);
+}
+
+
+static void print_etseq(m_etseq *b)
+{
+ int i;
+
+ for (i = 0; i < b->_length; i++) {
+ fprintf(stdout, "[%d]:\n", i);
+ erl_print_term(stdout, b->_buffer[i]);
+ }
+}
+
+
+static void print_et(m_et* b)
+{
+ fprintf(stdout, "\net struct --------\n");
+ erl_print_term(stdout, b->e);
+ fprintf(stdout, "long: %ld\n", b->l);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_es(m_es *b)
+{
+ fprintf(stdout, "\nm_es --------\n f: %d\n l: %ld\n", b->f, b->l);
+}
+
+
+static void print_arr1(long a[10])
+{
+ int i;
+
+ for (i = 0; i < 10; i++)
+ fprintf(stdout, "\n[%d]: %ld\n", i, a[i]);
+}
+
+static void print_dd(long a[2][3])
+{
+ int i, j;
+
+ fprintf(stdout, "\nlong dd[2][3] --------\n");
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "\n[%d][%d]: %ld\n", i, j, a[i][j]);
+}
+
+
+static void print_strRec(m_strRec* sr)
+{
+ int i, j;
+
+ fprintf(stdout, "\nboolean bb : %d\n",sr->bb);
+ fprintf(stdout, "string str4 : %s\n",sr->str4);
+ fprintf(stdout, "str7[2][3] :\n");
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "str7[%d][%d]: %ld\n", i, j, sr->str7[i][j]);
+ fprintf(stdout, "str5._length : %ld\n",sr->str5._length);
+ for (j = 0; j < sr->str5._length; j++)
+ fprintf(stdout, "str5._buffer[%d]: %c\n", j, sr->str5._buffer[j]);
+ fprintf(stdout, "string str6 : %s\n",sr->str6);
+ fprintf(stdout, "str8 :\n");
+ for (j = 0; j < 3; j++)
+ fprintf(stdout, "str8[%d]: %c\n", j, sr->str8[j]);
+ fprintf(stdout, "string str9 : %s\n",sr->str9);
+ fprintf(stdout, "str10._length : %ld\n",sr->str10._length);
+ for (j = 0; j < sr->str10._length; j++)
+ fprintf(stdout, "str10._buffer[%d]: %c\n", j, sr->str10._buffer[j]);
+}
+
+static void print_sseq(m_sseq *b)
+{
+ int i;
+
+ fprintf(stdout, "\nm_sseq size: %ld --------\n",b->_length);
+ for (i = 0; i < b->_length; i++)
+ fprintf(stdout, "%s\n", b->_buffer[i]);
+
+}
+
+
+static void print_pid(erlang_pid *p)
+{
+ fprintf(stdout, "\nerlang_pid --------\n node: %s\n num: %d\n "
+ "serial: %d\n creation: %d\n",
+ p->node, p->num, p->serial, p->creation);
+}
+
+static void print_port(erlang_port *p)
+{
+ fprintf(stdout, "\nerlang_port --------\n node: %s\n id: %d\n "
+ "creation: %d\n", p->node, p->id, p->creation);
+}
+
+static void print_ref(erlang_ref *p)
+{
+ fprintf(stdout, "\nerlang_ref --------\n node: %s\n len: %d\n "
+ "n[0]: %d\n n[1]: %d\n n[2]: %d\n creation: %d\n",
+ p->node, p->len, p->n[0], p->n[1], p->n[2], p->creation);
+}
+
+static void print_term(ETERM *t)
+{
+ fprintf(stdout, "\nETERM --------\n");
+ erl_print_term(stdout, t);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_s(m_s *p)
+{
+ int i;
+
+ fprintf(stdout, "\n%ld\n", p->l);
+ for (i = 0; i < p->sl._length; i++)
+ fprintf(stdout, "\n[%d]: %ld\n", i, p->sl._buffer[i]);
+}
+
+
+static void print_ssstr3(m_ssstr3 *b1)
+{
+ int i,j;
+
+ fprintf(stdout, "\nSSSTR3 --------\n");
+ fprintf(stdout,"b1->_length = %ld\n",b1->_length);
+ for (i = 0; i < b1->_length; i++) {
+ fprintf(stdout,"\nb1->_buffer[%d]._length %ld\n",
+ i, b1->_buffer[i]._length);
+ for (j = 0; j < b1->_buffer[i]._length; j++)
+ fprintf(stdout,"b1->_buffer[%d]._buffer[%d] = %s\n",
+ i, j, b1->_buffer[i]._buffer[j]);
+ }
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_wstr(CORBA_wchar *ws)
+{
+ int i = 0;
+
+ fprintf(stdout, "\nwstr --------\n");
+ while (ws[i]) {
+ fprintf(stdout, "[%d]: %ld\n", i, ws[i]);
+ i++;
+ }
+ fprintf(stdout, "\n--------\n");
+}
+
+
+static void print_ssarr3(m_ssarr3 *b1)
+{
+ int i;
+
+ fprintf(stdout, "\nssarr3 --------\n");
+ fprintf(stdout,"length: %ld\n",b1->_length);
+ fprintf(stdout, "buffer:\n");
+ for (i = 0; i < b1->_length; i++)
+ print_sarr3(&b1->_buffer[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_sarr3(m_sarr3 *b1)
+{
+ int i;
+
+ fprintf(stdout, "\nsarr3 --------\n");
+ fprintf(stdout,"length: %ld\n",b1->_length);
+ fprintf(stdout, "buffer:\n");
+ for (i = 0; i < b1->_length; i++)
+ print_arr3(b1->_buffer[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void print_arr3(m_arr3 b1)
+{
+ int i;
+
+ fprintf(stdout, "\narr3 --------\n");
+ for (i = 0; i < sizeof(m_arr3)/sizeof(CORBA_long); i++)
+ fprintf(stdout, "%ld ", b1[i]);
+ fprintf(stdout, "\n--------\n");
+}
+
+static void free_etseq_buf(m_etseq *b)
+{
+ int i;
+
+ for (i = 0; i < b->_length; i++)
+ erl_free_term(b->_buffer[i]);
+}
+
+static void free_et(m_et* b)
+{
+ erl_free_term(b->e);
+}
+
+static void showtime(MyTimeval *start, MyTimeval *stop)
+{
+ MyTimeval elapsed;
+
+ elapsed.tv_sec = stop->tv_sec - start->tv_sec;
+ elapsed.tv_usec = stop->tv_usec - start->tv_usec;
+ while (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ fprintf(stderr,"%ld.%06ld seconds\n",elapsed.tv_sec, elapsed.tv_usec);
+}
+
+static void my_gettimeofday(MyTimeval *tv)
+#ifdef __WIN32__
+#define EPOCH_JULIAN_DIFF 11644473600i64
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ LONGLONG lft;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ memcpy(&lft, &ft, sizeof(lft));
+ tv->tv_usec = (long) ((lft / 10i64) % 1000000i64);
+ tv->tv_sec = (long) ((lft / 10000000i64) - EPOCH_JULIAN_DIFF);
+}
+#elif defined VXWORKS
+{
+ int rate = sysClkRateGet(); /* Ticks per second */
+ unsigned long ctick = tickGet();
+ tv->tv_sec = ctick / rate; /* secs since reboot */
+ tv->tv_usec = ((ctick - (tv->tv_sec * rate))*1000000)/rate;
+}
+#else
+{
+ gettimeofday(tv, NULL);
+}
+#endif
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_erl_test.idl b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_erl_test.idl
new file mode 100644
index 0000000000..e687cec114
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/c_erl_test.idl
@@ -0,0 +1,173 @@
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2004-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+#include "erlang.idl"
+
+
+const short TestConst = 1;
+
+module m {
+
+ const short TestConst = 2;
+
+ struct b {
+ long l;
+ char c;
+ };
+
+ struct simple {
+ long l;
+ b b_t;
+ };
+
+ enum fruit {orange, banana, apple, peach, pear};
+
+ typedef sequence<long> lseq;
+
+ typedef sequence<b> bseq;
+
+ struct a {
+ long l;
+ bseq y;
+ double d;
+ };
+
+ typedef sequence<a> aseq;
+
+ typedef sequence<string> sseq;
+ typedef string str;
+ typedef long myLong;
+
+ typedef long arr1[500], dd[2][3];
+
+ typedef erlang::term apa;
+ typedef erlang::port banan;
+
+ typedef sequence<erlang::term> etseq;
+
+ struct s {
+ long l;
+ sequence<long> sl;
+ };
+
+ struct es {
+ fruit f;
+ myLong l;
+ };
+
+ struct et {
+ erlang::term e;
+ long l;
+ };
+
+
+ typedef sequence<char> str1;
+ typedef string<12> str2;
+ typedef char str3[3];
+
+ typedef sequence<string> sstr3; // sequence of string
+ typedef sequence<sstr3> ssstr3; // sequence of sequences of strings
+
+ typedef long arr3[3]; // array of long
+ typedef sequence<arr3> sarr3; // sequence of array
+ typedef sequence<sarr3> ssarr3; // sequence of sequnces of arrays of strings
+
+ struct strRec{
+ boolean bb;
+ string str4;
+ long str7[3][2];
+ sequence<char> str5;
+ string<12> str6;
+ str3 str8;
+ str2 str9;
+ str1 str10;
+ };
+
+
+ struct dyn {
+ long l;
+ sequence<long> sl;
+ };
+ typedef dyn arr2[1][2];
+
+
+ interface i {
+
+ const short TestConst = 3;
+
+ //arr2 suck(in arr2 x, out arr2 y );
+
+ ///////////////////////////////// attribute long l;
+
+ // simple types
+ void void_test();
+ long long_test(in long a, out long a1);
+ long long longlong_test(in long long a, out long long a1);
+ unsigned short ushort_test(in unsigned short a, out unsigned short a1);
+ unsigned long ulong_test(in unsigned long a, out unsigned long a1);
+ unsigned long long ulonglong_test(in unsigned long long a, out unsigned long long a1);
+ double double_test(in double a, out double a1);
+ char char_test(in char a, out char a1);
+ wchar wchar_test(in wchar a, out wchar a1);
+ octet octet_test(in octet a, out octet a1);
+ boolean bool_test(in boolean a, out boolean a1);
+
+ // Seq. and struct tests
+ b struct_test(in b a, out b a1);
+ es struct2_test(in es a, out es a1);
+ //simple struct3_test(in simple x, out simple y);
+ bseq seq1_test(in bseq a, out bseq a1);
+ aseq seq2_test(in aseq a, out aseq a1);
+ lseq seq3_test(in lseq a, out lseq a1);
+ ssstr3 seq4_test(in ssstr3 a, out ssstr3 a1);
+ ssarr3 seq5_test(in ssarr3 a, out ssarr3 a1);
+
+ // Array tests
+ arr1 array1_test(in arr1 a, out arr1 a1);
+ dd array2_test(in dd a, out dd a1);
+
+ // enum test
+ fruit enum_test(in fruit a, out fruit a1);
+
+ // string tests
+ string string1_test(in string a, out string a1);
+ wstring wstring1_test(in wstring a, out wstring a1);
+ sseq string2_test(in sseq a, out sseq a1);
+ str string3_test(in str a, out str a1);
+ strRec string4_test(in strRec a, out strRec a1);
+
+ // Special erlang types
+ erlang::pid pid_test(in erlang::pid a, out erlang::pid a1);
+ erlang::port port_test(in erlang::port a, out erlang::port a1);
+ erlang::ref ref_test(in erlang::ref a, out erlang::ref a1);
+ erlang::term term_test(in erlang::term a, out erlang::term a1);
+
+ // typedef test
+ long typedef_test(in apa a, in banan b, out apa a1, out banan b1);
+
+ // inlined seq. test
+ s inline_sequence_test(in s a, out s a1);
+
+ // term seq. test
+ etseq term_sequence_test(in etseq a, out etseq a1);
+ // term struct test
+ et term_struct_test(in et a, out et a1);
+
+ };
+
+};
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/erl_server.erl b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/erl_server.erl
new file mode 100644
index 0000000000..06b39b8c35
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/erl_server.erl
@@ -0,0 +1,28 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(erl_server).
+
+-export([run/0, stop/0]).
+
+run() ->
+ m_i:oe_create().
+
+stop() ->
+ gen_server:cast(cidl_test, stop).
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/m_i_impl.erl b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/m_i_impl.erl
new file mode 100644
index 0000000000..094855c27f
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/m_i_impl.erl
@@ -0,0 +1,161 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(m_i_impl).
+-include("m.hrl").
+
+-export([init/1, terminate/2, void_test/1, long_test/2, ushort_test/2,
+ longlong_test/2, ulong_test/2, ulonglong_test/2,
+ double_test/2, char_test/2, wchar_test/2, octet_test/2,
+ bool_test/2, struct_test/2, struct2_test/2, seq1_test/2,
+ seq2_test/2, seq3_test/2, seq4_test/2, seq5_test/2,
+ array1_test/2, array2_test/2, enum_test/2, string1_test/2,
+ string2_test/2, string3_test/2, string4_test/2, pid_test/2,
+ port_test/2, ref_test/2, term_test/2, typedef_test/3,
+ inline_sequence_test/2, '_set_l'/2, '_get_l'/1,
+ term_struct_test/2, term_sequence_test/2, wstring1_test/2]).
+
+-define(PRINTDEBUG(Case),
+ io:format("erl_server: case: ~p~n"
+ "erl_server: location: ~p~n", [Case, [?FILE, ?LINE]])).
+-define(PRINTDEBUG2(Case, Msg),
+ io:format("erl_server: case: ~p~n"
+ "erl_server: Msg: ~p~n"
+ "erl_server: location: ~p~n", [Case, Msg, [?FILE, ?LINE]])).
+
+init(Env) ->
+ {ok, []}.
+
+terminate(F, R) ->
+ ok.
+
+'_get_l'(State) ->
+ ?PRINTDEBUG("_get_l"),
+ {reply, State, State}.
+void_test(State) ->
+ ?PRINTDEBUG("void_test"),
+ {reply, ok, State}.
+
+'_set_l'(State, V) ->
+ ?PRINTDEBUG2("_set_l", V),
+ {reply, ok, V}.
+ushort_test(State, V) ->
+ ?PRINTDEBUG2("ushort_test", V),
+ {reply, {V, V}, State}.
+long_test(State, V) ->
+ ?PRINTDEBUG2("long_test", V),
+ {reply, {V, V}, State}.
+longlong_test(State, V) ->
+ ?PRINTDEBUG2("longlong_test", V),
+ {reply, {V, V}, State}.
+ulong_test(State, V) ->
+ ?PRINTDEBUG2("ulong_test", V),
+ {reply, {V, V}, State}.
+ulonglong_test(State, V) ->
+ ?PRINTDEBUG2("ulonglong_test", V),
+ {reply, {V, V}, State}.
+double_test(State, V) ->
+ ?PRINTDEBUG2("double_test", V),
+ {reply, {V, V}, State}.
+char_test(State, V) ->
+ ?PRINTDEBUG2("char_test", V),
+ {reply, {V, V}, State}.
+wchar_test(State, V) ->
+ ?PRINTDEBUG2("wchar_test", V),
+ {reply, {V, V}, State}.
+octet_test(State, V) ->
+ ?PRINTDEBUG2("octet_test", V),
+ {reply, {V, V}, State}.
+bool_test(State, V) ->
+ ?PRINTDEBUG2("bool_test", V),
+ {reply, {V, V}, State}.
+
+struct_test(State, V) ->
+ ?PRINTDEBUG2("struct_test", V),
+ {reply, {V, V}, State}.
+struct2_test(State, V) ->
+ ?PRINTDEBUG2("struct2_test", V),
+ {reply, {V, V}, State}.
+seq1_test(State, V) ->
+ ?PRINTDEBUG2("seq1_test", V),
+ {reply, {V, V}, State}.
+seq2_test(State, V) ->
+ ?PRINTDEBUG2("seq2_test", V),
+ {reply, {V, V}, State}.
+seq3_test(State, V) ->
+ ?PRINTDEBUG2("seq3_test", V),
+ {reply, {V, V}, State}.
+seq4_test(State, V) ->
+ ?PRINTDEBUG2("seq4_test", V),
+ {reply, {V, V}, State}.
+seq5_test(State, V) ->
+ ?PRINTDEBUG2("seq5_test", V),
+ {reply, {V, V}, State}.
+array1_test(State, V) ->
+ ?PRINTDEBUG2("array1_test", V),
+ {reply, {V, V}, State}.
+array2_test(State, V) ->
+ ?PRINTDEBUG2("array2_test", V),
+ {reply, {V, V}, State}.
+enum_test(State, V) ->
+ ?PRINTDEBUG2("enum_test", V),
+ {reply, {V, V}, State}.
+string1_test(State, V) ->
+ ?PRINTDEBUG2("string1_test", V),
+ {reply, {V, V}, State}.
+string2_test(State, V) ->
+ ?PRINTDEBUG2("string2_test", V),
+ {reply, {V, V}, State}.
+string3_test(State, V) ->
+ ?PRINTDEBUG2("string3_test", V),
+ {reply, {V, V}, State}.
+string4_test(State, V) ->
+ ?PRINTDEBUG2("string4_test", V),
+ {reply, {V, V}, State}.
+pid_test(State, V) ->
+ ?PRINTDEBUG2("pid_test", V),
+ {reply, {V, V}, State}.
+port_test(State, V) ->
+ ?PRINTDEBUG2("port_test", binary_to_list(term_to_binary(V))),
+ {reply, {V, V}, State}.
+ref_test(State, V) ->
+ ?PRINTDEBUG2("ref_test", binary_to_list(term_to_binary(V))),
+ {reply, {V, V}, State}.
+term_test(State, V) ->
+ ?PRINTDEBUG2("term_test", V),
+ {reply, {V, V}, State}.
+typedef_test(State, A, B) ->
+ ?PRINTDEBUG2("typedef_test", [A,B]),
+ {reply, {4711, A, B}, State}.
+inline_sequence_test(State, V) ->
+ ?PRINTDEBUG2("inline_sequence_test", V),
+ {reply, {V, V}, State}.
+term_sequence_test(State, V) ->
+ ?PRINTDEBUG2("term_sequence_test", V),
+ {reply, {V, V}, State}.
+term_struct_test(State, V) ->
+ ?PRINTDEBUG2("term_struct_test", V),
+ {reply, {V, V}, State}.
+wstring1_test(State, V) ->
+ ?PRINTDEBUG2("wstring1_test", V),
+ {reply, {V, V}, State}.
+
+
+
+
diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/my.c b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/my.c
new file mode 100644
index 0000000000..9567635742
--- /dev/null
+++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/my.c
@@ -0,0 +1,51 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+#include "ic.h"
+#include "m_i.h"
+
+int my_prepare_notification_encoding(CORBA_Environment *env)
+{
+ return oe_prepare_notification_encoding(env);
+}
+
+int my_send_notification_tmo(CORBA_Environment *env, unsigned int send_ms)
+{
+ return oe_send_notification_tmo(env, send_ms);
+}
+
+int my_prepare_request_encoding(CORBA_Environment *env)
+{
+ return oe_prepare_request_encoding(env);
+}
+
+int my_send_request_and_receive_reply_tmo(CORBA_Environment *env,
+ unsigned int send_ms,
+ unsigned int recv_ms)
+{
+ return oe_send_request_and_receive_reply_tmo(env, send_ms, recv_ms);
+}
+
+int my_prepare_reply_decoding(CORBA_Environment *env)
+{
+ return oe_prepare_reply_decoding(env);
+}
+
+
+
diff --git a/lib/ic/test/erl_client_c_server_SUITE.erl b/lib/ic/test/erl_client_c_server_SUITE.erl
new file mode 100644
index 0000000000..9bd9d4a46d
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_SUITE.erl
@@ -0,0 +1,366 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose : Test suite for erl-client/c-server
+%%----------------------------------------------------------------------
+
+
+-module(erl_client_c_server_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-export([init_per_testcase/2, end_per_testcase/2,all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, void_test/1,
+ long_test/1, longlong_test/1, ushort_test/1, ulong_test/1,
+ ulonglong_test/1, double_test/1, char_test/1, wchar_test/1,
+ octet_test/1, bool_test/1, struct_test/1, struct2_test/1,
+ seq1_test/1, seq2_test/1, seq3_test/1, seq4_test/1,
+ seq5_test/1, array1_test/1, array2_test/1, enum_test/1,
+ string1_test/1, string2_test/1, string3_test/1,
+ string4_test/1, pid_test/1, port_test/1, ref_test/1,
+ term_test/1, typedef_test/1, inline_sequence_test/1,
+ term_sequence_test/1, term_struct_test/1, wstring1_test/1]).
+
+-define(DEFAULT_TIMEOUT, 20000).
+-define(PORT_TIMEOUT, 15000).
+-define(CALL_TIMEOUT, 5000).
+
+-define(C_SERVER_NODE_NAME, idl_c_server_test).
+
+%% Add/remove code path and watchdog before/after each test case.
+%%
+init_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:add_patha(DataDir),
+
+ %% Since other test suites use the module m_i, we have
+ %% to make sure we are using the right m_i module.
+ code:purge(m_i),
+ code:load_file(m_i),
+
+ WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:del_path(DataDir),
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+[void_test, long_test, longlong_test, ushort_test,
+ ulong_test, ulonglong_test, double_test, char_test,
+ wchar_test, octet_test, bool_test, struct_test,
+ struct2_test, seq1_test, seq2_test, seq3_test,
+ seq4_test, seq5_test, array1_test, array2_test,
+ enum_test, string1_test, string2_test, string3_test,
+ string4_test, pid_test, port_test, ref_test, term_test,
+ typedef_test, inline_sequence_test, term_sequence_test,
+ term_struct_test, wstring1_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+array1_test(doc) -> "";
+array1_test(suite) -> [];
+array1_test(Config) ->
+ do_test(array1_test, Config).
+
+array2_test(doc) -> "";
+array2_test(suite) -> [];
+array2_test(Config) ->
+ do_test(array2_test, Config).
+
+bool_test(doc) -> "";
+bool_test(suite) -> [];
+bool_test(Config) ->
+ do_test(bool_test, Config).
+
+char_test(doc) -> "";
+char_test(suite) -> [];
+char_test(Config) ->
+ do_test(char_test, Config).
+
+double_test(doc) -> "";
+double_test(suite) -> [];
+double_test(Config) ->
+ do_test(double_test, Config).
+
+enum_test(doc) -> "";
+enum_test(suite) -> [];
+enum_test(Config) ->
+ do_test(enum_test, Config).
+
+inline_sequence_test(doc) -> "";
+inline_sequence_test(suite) -> [];
+inline_sequence_test(Config) ->
+ do_test(inline_sequence_test, Config).
+
+longlong_test(doc) -> "";
+longlong_test(suite) -> [];
+longlong_test(Config) ->
+ do_test(longlong_test, Config).
+
+long_test(doc) -> "";
+long_test(suite) -> [];
+long_test(Config) ->
+ do_test(long_test, Config).
+
+octet_test(doc) -> "";
+octet_test(suite) -> [];
+octet_test(Config) ->
+ do_test(octet_test, Config).
+
+pid_test(doc) -> "";
+pid_test(suite) -> [];
+pid_test(Config) ->
+ do_test(pid_test, Config).
+
+port_test(doc) -> "";
+port_test(suite) -> [];
+port_test(Config) ->
+ do_test(port_test, Config).
+
+ref_test(doc) -> "";
+ref_test(suite) -> [];
+ref_test(Config) ->
+ do_test(ref_test, Config).
+
+seq1_test(doc) -> "";
+seq1_test(suite) -> [];
+seq1_test(Config) ->
+ do_test(seq1_test, Config).
+
+seq2_test(doc) -> "";
+seq2_test(suite) -> [];
+seq2_test(Config) ->
+ do_test(seq2_test, Config).
+
+seq3_test(doc) -> "";
+seq3_test(suite) -> [];
+seq3_test(Config) ->
+ do_test(seq3_test, Config).
+
+seq4_test(doc) -> "";
+seq4_test(suite) -> [];
+seq4_test(Config) ->
+ do_test(seq4_test, Config).
+
+seq5_test(doc) -> "";
+seq5_test(suite) -> [];
+seq5_test(Config) ->
+ do_test(seq5_test, Config).
+
+string1_test(doc) -> "";
+string1_test(suite) -> [];
+string1_test(Config) ->
+ do_test(string1_test, Config).
+
+string2_test(doc) -> "";
+string2_test(suite) -> [];
+string2_test(Config) ->
+ do_test(string2_test, Config).
+
+string3_test(doc) -> "";
+string3_test(suite) -> [];
+string3_test(Config) ->
+ do_test(string3_test, Config).
+
+string4_test(doc) -> "";
+string4_test(suite) -> [];
+string4_test(Config) ->
+ do_test(string4_test, Config).
+
+struct2_test(doc) -> "";
+struct2_test(suite) -> [];
+struct2_test(Config) ->
+ do_test(struct2_test, Config).
+
+struct_test(doc) -> "";
+struct_test(suite) -> [];
+struct_test(Config) ->
+ do_test(struct_test, Config).
+
+term_sequence_test(doc) -> "";
+term_sequence_test(suite) -> [];
+term_sequence_test(Config) ->
+ do_test(term_sequence_test, Config).
+
+term_struct_test(doc) -> "";
+term_struct_test(suite) -> [];
+term_struct_test(Config) ->
+ do_test(term_struct_test, Config).
+
+term_test(doc) -> "";
+term_test(suite) -> [];
+term_test(Config) ->
+ do_test(term_test, Config).
+
+typedef_test(doc) -> "";
+typedef_test(suite) -> [];
+typedef_test(Config) ->
+ do_test(typedef_test, Config).
+
+ulonglong_test(doc) -> "";
+ulonglong_test(suite) -> [];
+ulonglong_test(Config) ->
+ do_test(ulonglong_test, Config).
+
+ulong_test(doc) -> "";
+ulong_test(suite) -> [];
+ulong_test(Config) ->
+ do_test(ulong_test, Config).
+
+ushort_test(doc) -> "";
+ushort_test(suite) -> [];
+ushort_test(Config) ->
+ do_test(ushort_test, Config).
+
+void_test(doc) -> "";
+void_test(suite) -> [];
+void_test(Config) ->
+ do_test(void_test, Config).
+
+wchar_test(doc) -> "";
+wchar_test(suite) -> [];
+wchar_test(Config) ->
+ do_test(wchar_test, Config).
+
+wstring1_test(doc) -> "";
+wstring1_test(suite) -> [];
+wstring1_test(Config) ->
+ do_test(wstring1_test, Config).
+
+
+do_test(Case, Config) ->
+ %% Trap exits
+ process_flag(trap_exit, true),
+ Node = atom_to_list(node()),
+ [_NodeName, HostName] = string:tokens(Node, "@"),
+ DataDir = ?config(data_dir, Config),
+ %% io:format("~p: data directory: ~p~n", [?MODULE, DataDir]),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ ServerNodeName = atom_to_list(?C_SERVER_NODE_NAME),
+ %% Start C-server node as a port program. We wait for the node
+ %% to connect to us.
+ Cmd = filename:join([DataDir, "c_server"]) ++
+ " -this-node-name " ++ ServerNodeName ++
+ " -peer-node " ++ Node ++
+ " -cookie " ++ Cookie,
+ Port = open_port({spawn, Cmd}, [exit_status, eof, stderr_to_stdout]),
+ ServerNode = list_to_atom(ServerNodeName ++ "@" ++ HostName),
+ Res = case wait_for_hidden_node(ServerNode) of
+ ok ->
+ %% Need a port for port_test and typedef_test
+ put(port_test_port, Port),
+ R = (catch erl_client:Case(ServerNode, ?CALL_TIMEOUT)),
+ case wait_for_completion(Port) of
+ {error, timeout} ->
+ kill_off_node(ServerNode);
+ _ ->
+ ok
+ end,
+ R;
+ {error, timeout} ->
+ case wait_for_completion(Port) of
+ {error, timeout} ->
+ kill_off_node(ServerNode);
+ _ ->
+ ok
+ end,
+ {error, timeout}
+ end,
+ process_flag(trap_exit, false),
+ true = Res.
+
+
+%% Wait for eof *and* exit status, but return if exit status indicates
+%% an error, or we have been waiting more than PORT_TIMEOUT seconds.
+%%
+wait_for_completion(Port) ->
+ wait_for_completion(Port, 0).
+
+wait_for_completion(Port, N) when N < 2 ->
+ receive
+ {Port, {data, Bytes}} ->
+ %% Relay output
+ io:format("~s", [Bytes]),
+ wait_for_completion(Port, N);
+ {Port, {exit_status, 0}} ->
+ wait_for_completion(Port, N + 1);
+ {Port, {exit_status, Status}} ->
+ {error, Status};
+ {Port, eof} ->
+ wait_for_completion(Port, N + 1);
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason: ~w~n", [Reason]),
+ wait_for_completion(Port, N);
+ {'EXIT', From, Reason} ->
+ io:format("Got unexpected exit: ~p~n", [{'EXIT', From, Reason}]),
+ wait_for_completion(Port, N)
+ after ?PORT_TIMEOUT ->
+ {error, timeout}
+ end;
+wait_for_completion(_, _) ->
+ ok.
+
+wait_for_hidden_node(Node) ->
+ Times = ?DEFAULT_TIMEOUT div 100,
+ wait_for_hidden_node(Node, Times, 100).
+
+wait_for_hidden_node(Node, Times, WaitTime) when Times > 0 ->
+ io:format("Waiting for hidden node: ~p~n", [Node]),
+ case lists:member(Node, erlang:nodes(hidden)) of
+ true ->
+ ok;
+ false ->
+ delay(WaitTime),
+ wait_for_hidden_node(Node, Times - 1, WaitTime)
+ end;
+wait_for_hidden_node(_Node, _, _WaitTime) ->
+ {error, timeout}.
+
+kill_off_node(Node) ->
+ catch rpc:cast(Node, erlang, halt, [1]).
+
+delay(Time) ->
+ receive
+ after Time ->
+ ok
+ end.
+
+
+
+
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src b/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..50cf9d4445
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src
@@ -0,0 +1,150 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Makefile.src for erl_client_c_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .h .erl .idl @obj@ .@EMULATOR@
+
+
+# Variables from ts:
+#
+
+ERL_INCLUDE = @erl_include@
+
+IC_INCLUDE_PATH = @ic_include_path@
+IC_LIB = @ic_libpath@@DS@@ic_lib@
+
+ERL_INTERFACE_INCLUDE = @erl_interface_include@
+ERL_INTERFACE_LIB = @erl_interface_libpath@@DS@@erl_interface_lib@
+ERL_INTERFACE_EILIB = @erl_interface_libpath@@DS@@erl_interface_eilib@
+ERL_INTERFACE_THREADLIB = @erl_interface_threadlib@
+ERL_INTERFACE_SOCK_LIBS = @erl_interface_sock_libs@
+
+CC = @CC@
+## XXX Should set warning flag with a DEBUG_FLAG
+CFLAGS = @CFLAGS@ @DEFS@ -I$(ERL_INCLUDE) \
+ -I$(IC_INCLUDE_PATH) -I$(ERL_INTERFACE_INCLUDE)
+
+LD = @LD@
+LDFLAGS = @CROSSLDFLAGS@
+LIBS = $(IC_LIB) $(ERL_INTERFACE_LIB) $(ERL_INTERFACE_EILIB) \
+ $(ERL_INTERFACE_THREADLIB) @LIBS@ $(ERL_INTERFACE_SOCK_LIBS)
+ERLC = erlc
+
+# Generated C header files
+GEN_H_FILES = \
+ m__s.h \
+ m_i__s.h \
+ oe_erl_c_test__s.h
+
+# Generated C files
+GEN_C_FILES = \
+ m__s.c \
+ m_i__s.c \
+ oe_code_m_a.c \
+ oe_code_m_arr1.c \
+ oe_code_m_arr2.c \
+ oe_code_m_arr3.c \
+ oe_code_m_aseq.c \
+ oe_code_m_b.c \
+ oe_code_m_bseq.c \
+ oe_code_m_dd.c \
+ oe_code_m_dyn.c \
+ oe_code_m_dyn_sl.c \
+ oe_code_m_es.c \
+ oe_code_m_et.c \
+ oe_code_m_etseq.c \
+ oe_code_m_fruit.c \
+ oe_code_m_lseq.c \
+ oe_code_m_s.c \
+ oe_code_m_s_sl.c \
+ oe_code_m_sarr3.c \
+ oe_code_m_simple.c \
+ oe_code_m_ssarr3.c \
+ oe_code_m_sseq.c \
+ oe_code_m_ssstr3.c \
+ oe_code_m_sstr3.c \
+ oe_code_m_str1.c \
+ oe_code_m_str3.c \
+ oe_code_m_strRec.c \
+ oe_code_m_strRec_str5.c \
+ oe_code_m_strRec_str7.c \
+ oe_erl_c_test__s.c
+
+GEN_HRL_FILES = \
+ m.hrl \
+ m_i.hrl \
+ oe_erl_c_test.hrl
+
+GEN_ERL_FILES = \
+ m.erl \
+ m_arr2.erl \
+ m_arr3.erl \
+ m_i.erl \
+ m_str3.erl \
+ oe_erl_c_test.erl
+
+C_FILES = $(GEN_C_FILES) c_server.c callbacks.c
+
+OBJS = $(C_FILES:.c=@obj@)
+
+PGMS = c_server@exe@
+
+ERL_FILES = $(GEN_ERL_FILES) erl_client.erl
+
+EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
+
+
+all: $(PGMS) $(EBINS)
+
+clean:
+ -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+ -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+
+$(PGMS): $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(GEN_C_FILES) $(GEN_H_FILES): erl_c_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_server}" \
+ "+{scoped_op_calls,true}" erl_c_test.idl
+
+# If we have scoped operation calls for C, we must have that for
+# Erlang as well, if we use the m_i.erl file for calling the server.
+
+$(GEN_ERL_FILES) $(GEN_HRL_FILES): erl_c_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" \
+ "+{scoped_op_calls,true}" "+{timeout,true}" erl_c_test.idl
+
+.c@obj@:
+ $(CC) -c -o $*@obj@ $(CFLAGS) $<
+
+.erl.@EMULATOR@:
+ $(ERLC) -W -I $(IC_INCLUDE_PATH) $<
+
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c b/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c
new file mode 100644
index 0000000000..74f29f59f9
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/c_server.c
@@ -0,0 +1,299 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+/* C-server for test of IC.
+ *
+ * The C-node implemented here connects to its peer node, waits for
+ * one message, evaluates the message, returns an result message, and
+ * terminates.
+ *
+ * TODO:
+ *
+ * 1. XXX #includes for VxWorks, Windows
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+
+#include <string.h>
+
+#ifdef __WIN32__
+# include <time.h>
+# include <sys/timeb.h>
+#elif defined VXWORKS
+# include <time.h>
+# include <sys/times.h>
+#else
+# include <sys/time.h>
+#endif
+
+#include <ctype.h>
+
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+
+#include "ic.h"
+#include "ei.h"
+#include "erl_interface.h"
+#include "eicode.h"
+#include "m_i__s.h"
+#include "m__s.h"
+
+#ifdef __WIN32__
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} MyTimeval;
+#else
+typedef struct timeval MyTimeval;
+#endif
+static void my_gettimeofday(MyTimeval *tv);
+static void showtime(MyTimeval *start, MyTimeval *stop);
+static void usage(void);
+static void done(int r);
+
+#define HOSTNAMESZ 256
+#define NODENAMESZ 512
+#define INBUFSZ 10
+#define OUTBUFSZ 0
+#define MAXTRIES 5
+
+static char *progname;
+
+/* main */
+#ifdef VXWORKS
+int c_server(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ struct hostent *hp;
+ MyTimeval start, stop;
+ int i, fd, ires, tries;
+ CORBA_Environment *env;
+ char *this_node_name = NULL;
+ char *peer_node = NULL;
+ char *cookie = NULL;
+ char host[HOSTNAMESZ + 1];
+ char this_node[NODENAMESZ + 1];
+ erlang_msg msg;
+ int status, loop;
+
+#ifdef __WIN32__
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ fprintf(stderr, "Could not load winsock2 v2.0 compatible DLL");
+ exit(1);
+ }
+#endif
+
+ progname = argv[0];
+ host[HOSTNAMESZ] = '\0';
+ if (gethostname(host, HOSTNAMESZ) < 0) {
+ fprintf(stderr, "Can't find own hostname\n");
+ done(1);
+ }
+ if ((hp = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Can't get ip address for host %s\n", host);
+ done(1);
+ }
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-help") == 0) {
+ usage();
+ done(0);
+ } else if (strcmp(argv[i], "-this-node-name") == 0) {
+ i++;
+ this_node_name = argv[i];
+ } else if (strcmp(argv[i], "-peer-node") == 0) {
+ i++;
+ peer_node = argv[i];
+ } else if (strcmp(argv[i], "-cookie") == 0) {
+ i++;
+ cookie = argv[i];
+ } else {
+ fprintf(stderr, "Error : invalid argument \"%s\"\n", argv[i]);
+ usage();
+ done(1);
+ }
+ }
+
+ if (this_node_name == NULL || peer_node == NULL || cookie == NULL) {
+ fprintf(stderr, "Error: missing option\n");
+ usage();
+ done(1);
+ }
+
+ /* Behead hostname at first dot */
+ for (i=0; host[i] != '\0'; i++) {
+ if (host[i] == '.') { host[i] = '\0'; break; }
+ }
+ sprintf(this_node, "%s@%s", this_node_name, host);
+
+ fprintf(stderr, "c_server: this node: \"%s\"\n", this_node);
+ fprintf(stderr, "c_server: peer node: \"%s\"\n", peer_node);
+
+ /* initialize erl_interface */
+ erl_init(NULL, 0);
+
+ for (tries = 0; tries < MAXTRIES; tries++) {
+ /* connect to peer node */
+ ires = erl_connect_xinit(host, this_node_name, this_node,
+ (struct in_addr *)*hp->h_addr_list,
+ cookie, 0);
+ fprintf(stderr, "c_server: erl_connect_xinit(): %d\n", ires);
+
+ fd = erl_connect(peer_node);
+ fprintf(stderr, "c_server: erl_connect(): %d\n", fd);
+ if (fd >= 0)
+ break;
+ fprintf(stderr, "c_server: cannot connect, retrying\n");
+ }
+ if (fd < 0) {
+ fprintf(stderr, "c_server: cannot connect, exiting\n");
+ done(1);
+ }
+ env = CORBA_Environment_alloc(INBUFSZ, OUTBUFSZ);
+ env->_fd = fd;
+
+ status = 1;
+ loop = 1;
+ my_gettimeofday(&start);
+ while (status >= 0 && loop > 0) {
+ status = ei_receive_encoded(env->_fd, &env->_inbuf, &env->_inbufsz,
+ &msg, &env->_iin);
+ switch(status) {
+ case ERL_SEND:
+ case ERL_REG_SEND:
+ /* get result */
+ m_i__switch(NULL, env);
+ switch(env->_major) {
+ case CORBA_NO_EXCEPTION:
+ break;
+ case CORBA_SYSTEM_EXCEPTION:
+ fprintf(stderr, "Request failure, reason : %s\n",
+ (char *) CORBA_exception_value(env));
+ CORBA_exception_free(env);
+ break;
+ default: /* Should not happen */
+ CORBA_exception_free(env);
+ break;
+ }
+ /* send back result data */
+ if (env->_iout > 0)
+ ei_send_encoded(env->_fd, &env->_caller, env->_outbuf,
+ env->_iout);
+ loop = 0;
+ break;
+ case ERL_TICK:
+ break;
+ default:
+ if (status < 0) {
+ fprintf(stderr, "Status negative: %d\n", status);
+ loop = 0;
+ }
+ break;
+ }
+ }
+ my_gettimeofday(&stop);
+ showtime(&start, &stop);
+
+ erl_close_connection(fd);
+
+ CORBA_free(env->_inbuf);
+ CORBA_free(env->_outbuf);
+ CORBA_free(env);
+ if (status < 0)
+ done(-status);
+ else
+ done(0);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [-help] -this-node-name <name> "
+ "-peer-node <nodename> -cookie <cookie>\n", progname);
+ fprintf(stderr, "Example:\n %s -this-node-name kalle "
+ "-peer-node olle@home -cookie oa678er\n", progname);
+}
+
+static void done(int r)
+{
+#ifdef __WIN32__
+ WSACleanup();
+#endif
+ exit(r);
+}
+
+static void showtime(MyTimeval *start, MyTimeval *stop)
+{
+ MyTimeval elapsed;
+
+ elapsed.tv_sec = stop->tv_sec - start->tv_sec;
+ elapsed.tv_usec = stop->tv_usec - start->tv_usec;
+ while (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ fprintf(stderr,"%ld.%06ld seconds\n",elapsed.tv_sec, elapsed.tv_usec);
+}
+
+
+
+static void my_gettimeofday(MyTimeval *tv)
+#ifdef __WIN32__
+#define EPOCH_JULIAN_DIFF 11644473600i64
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ LONGLONG lft;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ memcpy(&lft, &ft, sizeof(lft));
+ tv->tv_usec = (long) ((lft / 10i64) % 1000000i64);
+ tv->tv_sec = (long) ((lft / 10000000i64) - EPOCH_JULIAN_DIFF);
+}
+#elif defined VXWORKS
+{
+ int rate = sysClkRateGet(); /* Ticks per second */
+ unsigned long ctick = tickGet();
+ tv->tv_sec = ctick / rate; /* secs since reboot */
+ tv->tv_usec = ((ctick - (tv->tv_sec * rate))*1000000)/rate;
+}
+#else
+{
+ gettimeofday(tv, NULL);
+}
+#endif
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c b/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c
new file mode 100644
index 0000000000..305017ae85
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/callbacks.c
@@ -0,0 +1,610 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2002-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include <ic.h>
+#include <erl_interface.h>
+#include <ei.h>
+#include "m_i__s.h"
+
+
+
+/* OK */
+
+void my_void_test(CORBA_Object oe_obj,
+ CORBA_Environment *oe_env)
+{
+ /* printf("void test !\n"); */
+}
+
+m_i_void_test__rs* m_i_void_test__cb(CORBA_Object oe_obj,
+ CORBA_Environment *oe_env)
+{
+ return (m_i_void_test__rs*) (my_void_test);
+}
+
+
+
+/* OK */
+
+void my_long_test(CORBA_Object oe_obj,
+ long* a,
+ long* b,
+ long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("long test !\n"); */
+}
+
+
+m_i_long_test__rs* m_i_long_test__cb(CORBA_Object oe_obj,
+ long* a,
+ long* b,
+ long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_long_test__rs*) (my_long_test);
+}
+
+/* OK */
+
+void my_longlong_test(CORBA_Object oe_obj,
+ CORBA_long_long* a,
+ CORBA_long_long* b,
+ CORBA_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("long test !\n"); */
+}
+
+m_i_longlong_test__rs* m_i_longlong_test__cb(CORBA_Object oe_obj,
+ CORBA_long_long* a,
+ CORBA_long_long* b,
+ CORBA_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_longlong_test__rs*) (my_longlong_test);
+}
+
+/* OK */
+void my_ulong_test(CORBA_Object oe_obj,
+ unsigned long* a,
+ unsigned long* b,
+ unsigned long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("ulong test !\n"); */
+}
+
+m_i_ulong_test__rs* m_i_ulong_test__cb(CORBA_Object oe_obj,
+ unsigned long* a,
+ unsigned long* b,
+ unsigned long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_ulong_test__rs*) (my_ulong_test);
+}
+
+/* OK */
+void my_ulonglong_test(CORBA_Object oe_obj,
+ CORBA_unsigned_long_long* a,
+ CORBA_unsigned_long_long* b,
+ CORBA_unsigned_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("ulong test !\n"); */
+}
+
+m_i_ulonglong_test__rs* m_i_ulonglong_test__cb(CORBA_Object oe_obj,
+ CORBA_unsigned_long_long* a,
+ CORBA_unsigned_long_long* b,
+ CORBA_unsigned_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_ulonglong_test__rs*) (my_ulonglong_test);
+}
+
+m_i_ushort_test__rs* m_i_ushort_test__cb(CORBA_Object oe_obj,
+ unsigned short* a,
+ unsigned short* b,
+ unsigned short* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_ushort_test__rs*) NULL;
+}
+
+
+/* OK */
+void my_double_test(CORBA_Object oe_obj,
+ double* a,
+ double* b,
+ double* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("double test !\n"); */
+}
+
+m_i_double_test__rs* m_i_double_test__cb(CORBA_Object oe_obj,
+ double* a,
+ double* b,
+ double* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_double_test__rs*) (my_double_test);
+}
+
+/* OK */
+m_i_char_test__rs* m_i_char_test__cb(CORBA_Object oe_obj,
+ char* a,
+ char* b,
+ char* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_char_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+
+/* OK */
+m_i_wchar_test__rs* m_i_wchar_test__cb(CORBA_Object oe_obj,
+ CORBA_wchar* a,
+ CORBA_wchar* b,
+ CORBA_wchar* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_wchar_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_octet_test__rs* m_i_octet_test__cb(CORBA_Object oe_obj,
+ char* a,
+ char* b,
+ char* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_octet_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_bool_test__rs* m_i_bool_test__cb(CORBA_Object oe_obj,
+ CORBA_boolean* a,
+ CORBA_boolean* b,
+ CORBA_boolean* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_bool_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+void my_struct_test(CORBA_Object oe_obj,
+ m_b* a,
+ m_b* b,
+ m_b* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("struct test !\n"); */
+}
+
+m_i_struct_test__rs* m_i_struct_test__cb(CORBA_Object oe_obj,
+ m_b* a,
+ m_b* b,
+ m_b* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_struct_test__rs*) (my_struct_test);
+}
+
+/* OK */
+m_i_struct2_test__rs* m_i_struct2_test__cb(CORBA_Object oe_obj,
+ m_es* a,
+ m_es* b,
+ m_es* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_struct2_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+/* XXX Commented out
+m_i_struct3_test__rs* m_i_struct3_test__cb(CORBA_Object oe_obj,
+ m_simple* a,
+ m_simple* b,
+ m_simple* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_struct3_test__rs* rs = NULL;
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+*/
+
+/* OK */
+m_i_seq1_test__rs* m_i_seq1_test__cb(CORBA_Object oe_obj,
+ m_bseq** a,
+ m_bseq* b,
+ m_bseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq1_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+
+/* OK */
+m_i_seq2_test__rs* m_i_seq2_test__cb(CORBA_Object oe_obj,
+ m_aseq** a,
+ m_aseq* b,
+ m_aseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq2_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_seq3_test__rs* m_i_seq3_test__cb(CORBA_Object oe_obj,
+ m_lseq** a,
+ m_lseq* b,
+ m_lseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq3_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_seq4_test__rs* m_i_seq4_test__cb(CORBA_Object oe_obj,
+ m_ssstr3** a,
+ m_ssstr3* b,
+ m_ssstr3** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq4_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_seq5_test__rs* m_i_seq5_test__cb(CORBA_Object oe_obj,
+ m_ssarr3** a,
+ m_ssarr3* b,
+ m_ssarr3** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq5_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_array1_test__rs* m_i_array1_test__cb(CORBA_Object oe_obj,
+ m_arr1 a,
+ m_arr1 b,
+ m_arr1 c,
+ CORBA_Environment *oe_env)
+{
+ int i;
+ m_i_array1_test__rs* rs = NULL;
+
+ for (i = 0; i < 500; i++) {
+ a[i] = b[i];
+ c[i] = b[i];
+ }
+ return rs;
+}
+
+/* OK */
+m_i_array2_test__rs* m_i_array2_test__cb(CORBA_Object oe_obj,
+ m_dd a,
+ m_dd b,
+ m_dd c,
+ CORBA_Environment *oe_env)
+{
+ int i,j;
+ m_i_array2_test__rs* rs = NULL;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++) {
+ a[i][j] = b[i][j];
+ c[i][j] = b[i][j];
+ }
+ return rs;
+}
+
+
+/* OK */
+m_i_enum_test__rs* m_i_enum_test__cb(CORBA_Object oe_obj,
+ m_fruit* a,
+ m_fruit* b,
+ m_fruit* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_enum_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_string1_test__rs* m_i_string1_test__cb(CORBA_Object oe_obj,
+ char ** a,
+ char * b,
+ char ** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_string1_test__rs* rs = NULL;
+
+ /*printf("\nString in ------> %s\n\n",b);*/
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_string2_test__rs* m_i_string2_test__cb(CORBA_Object oe_obj,
+ m_sseq** a,
+ m_sseq* b,
+ m_sseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_string2_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_string3_test__rs* m_i_string3_test__cb(CORBA_Object oe_obj,
+ char ** a,
+ char * b,
+ char ** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_string3_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+m_i_string4_test__rs* m_i_string4_test__cb(CORBA_Object oe_obj,
+ m_strRec** a,
+ m_strRec* b,
+ m_strRec** c,
+ CORBA_Environment *oe_env)
+{
+ *a = b;
+ *c = b;
+
+ return (m_i_string4_test__rs*) NULL;
+}
+
+/* OK */
+m_i_wstring1_test__rs* m_i_wstring1_test__cb(CORBA_Object oe_obj,
+ CORBA_wchar ** a,
+ CORBA_wchar * b,
+ CORBA_wchar ** c,
+ CORBA_Environment *oe_env)
+{
+ int tmp;
+ m_i_wstring1_test__rs* rs = NULL;
+
+ /*printf("\nString in ------> %s\n\n",b);*/
+
+ for(tmp = 0; tmp < 5; tmp++)
+ fprintf(stderr,"\np[%d] = %ld\n", tmp, b[tmp]);
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+
+/* OK */
+m_i_pid_test__rs* m_i_pid_test__cb(CORBA_Object oe_obj,
+ erlang_pid* a,
+ erlang_pid* b,
+ erlang_pid* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_pid_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_port_test__rs* m_i_port_test__cb(CORBA_Object oe_obj,
+ erlang_port* a,
+ erlang_port* b,
+ erlang_port* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_port_test__rs* rs = NULL;
+
+ strcpy((*a).node,(*b).node);
+ (*a).id = (*b).id;
+ (*a).creation = 0;
+
+ strcpy((*c).node,(*b).node);
+ (*c).id = (*b).id;
+ (*c).creation = 0;
+ return rs;
+}
+
+/* OK */
+m_i_ref_test__rs* m_i_ref_test__cb(CORBA_Object oe_obj,
+ erlang_ref* a,
+ erlang_ref* b,
+ erlang_ref* c,
+ CORBA_Environment *oe_env)
+{
+
+ m_i_ref_test__rs* rs = NULL;
+
+ strcpy((*a).node,(*b).node);
+ /*(*a).id = (*b).id;*/
+ (*a).len = (*b).len;
+ (*a).n[0] = (*b).n[0];
+ (*a).n[1] = (*b).n[1];
+ (*a).n[2] = (*b).n[2];
+ (*a).creation = 0;
+
+ strcpy((*c).node,(*b).node);
+ /*(*c).id = (*b).id;*/
+ (*c).len = (*b).len;
+ (*c).n[0] = (*b).n[0];
+ (*c).n[1] = (*b).n[1];
+ (*c).n[2] = (*b).n[2];
+ (*c).creation = 0;
+ return rs;
+}
+
+/* OK */
+m_i_term_test__rs* m_i_term_test__cb(CORBA_Object oe_obj,
+ ETERM** a,
+ ETERM** b,
+ ETERM** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_term_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+m_i_typedef_test__rs* m_i_typedef_test__cb(CORBA_Object oe_obj,
+ long* a,
+ ETERM** b,
+ erlang_port* c,
+ ETERM** d ,
+ erlang_port* e,
+ CORBA_Environment *oe_env)
+{
+ m_i_typedef_test__rs* rs = NULL;
+
+ *d = *b;
+ strcpy((*e).node,(*c).node);
+ (*e).id = (*c).id;
+ (*e).creation = 0;
+ *a = 4711;
+ return rs;
+}
+
+/* OK */
+m_i_inline_sequence_test__rs* m_i_inline_sequence_test__cb(
+ CORBA_Object oe_obj,
+ m_s** a,
+ m_s* b,
+ m_s** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_inline_sequence_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_term_sequence_test__rs* m_i_term_sequence_test__cb(
+ CORBA_Object oe_obj,
+ m_etseq** a,
+ m_etseq* b,
+ m_etseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_term_sequence_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+
+/* OK */
+m_i_term_struct_test__rs* m_i_term_struct_test__cb(CORBA_Object oe_obj,
+ m_et* a,
+ m_et* b,
+ m_et* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_term_struct_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/erl_c_test.idl b/lib/ic/test/erl_client_c_server_SUITE_data/erl_c_test.idl
new file mode 100644
index 0000000000..963bc69017
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/erl_c_test.idl
@@ -0,0 +1,174 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2002-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+#include "erlang.idl"
+
+
+const short TestConst = 1;
+
+module m {
+
+ const short TestConst = 2;
+
+ struct b {
+ long l;
+ char c;
+ };
+
+ struct simple {
+ long l;
+ b b_t;
+ };
+
+ enum fruit {orange, banana, apple, peach, pear};
+
+ typedef sequence<long> lseq;
+
+ typedef sequence<b> bseq;
+
+ struct a {
+ long l;
+ bseq y;
+ double d;
+ };
+
+ typedef sequence<a> aseq;
+
+ typedef sequence<string> sseq;
+ typedef string str;
+ typedef long myLong;
+
+ typedef long arr1[500], dd[2][3];
+
+ typedef erlang::term apa;
+ typedef erlang::port banan;
+
+ typedef sequence<erlang::term> etseq;
+
+ struct s {
+ long l;
+ sequence<long> sl;
+ };
+
+ struct es {
+ fruit f;
+ myLong l;
+ };
+
+ struct et {
+ erlang::term e;
+ long l;
+ };
+
+
+ typedef sequence<char> str1;
+ typedef string<12> str2;
+ typedef char str3[3];
+
+ typedef sequence<string> sstr3; // sequence of string
+ typedef sequence<sstr3> ssstr3; // sequence of sequences of strings
+
+ typedef long arr3[3]; // array of long
+ typedef sequence<arr3> sarr3; // sequence of array
+ typedef sequence<sarr3> ssarr3; // sequence of sequnces of arrays of strings
+
+ struct strRec{
+ boolean bb;
+ string str4;
+ long str7[3][2];
+ sequence<char> str5;
+ string<12> str6;
+ str3 str8;
+ str2 str9;
+ str1 str10;
+ };
+
+
+ struct dyn {
+ long l;
+ sequence<long> sl;
+ };
+ typedef dyn arr2[1][2];
+
+
+ interface i {
+
+ const short TestConst = 3;
+
+ //arr2 suck(in arr2 x, out arr2 y );
+
+ ///////////////////////////////// attribute long l;
+
+ // simple types
+ void void_test();
+ long long_test(in long a, out long a1);
+ long long longlong_test(in long long a, out long long a1);
+ unsigned short ushort_test(in unsigned short a, out unsigned short a1);
+ unsigned long ulong_test(in unsigned long a, out unsigned long a1);
+ unsigned long long ulonglong_test(in unsigned long long a, out unsigned long long a1);
+ double double_test(in double a, out double a1);
+ char char_test(in char a, out char a1);
+ wchar wchar_test(in wchar a, out wchar a1);
+ octet octet_test(in octet a, out octet a1);
+ boolean bool_test(in boolean a, out boolean a1);
+
+ // Seq. and struct tests
+ b struct_test(in b a, out b a1);
+ es struct2_test(in es a, out es a1);
+ //simple struct3_test(in simple x, out simple y);
+ bseq seq1_test(in bseq a, out bseq a1);
+ aseq seq2_test(in aseq a, out aseq a1);
+ lseq seq3_test(in lseq a, out lseq a1);
+ ssstr3 seq4_test(in ssstr3 a, out ssstr3 a1);
+ ssarr3 seq5_test(in ssarr3 a, out ssarr3 a1);
+
+ // Array tests
+ arr1 array1_test(in arr1 a, out arr1 a1);
+ dd array2_test(in dd a, out dd a1);
+
+ // enum test
+ fruit enum_test(in fruit a, out fruit a1);
+
+ // string tests
+ string string1_test(in string a, out string a1);
+ wstring wstring1_test(in wstring a, out wstring a1);
+ sseq string2_test(in sseq a, out sseq a1);
+ str string3_test(in str a, out str a1);
+ strRec string4_test(in strRec a, out strRec a1);
+
+ // Special erlang types
+ erlang::pid pid_test(in erlang::pid a, out erlang::pid a1);
+ erlang::port port_test(in erlang::port a, out erlang::port a1);
+ erlang::ref ref_test(in erlang::ref a, out erlang::ref a1);
+ erlang::term term_test(in erlang::term a, out erlang::term a1);
+
+ // typedef test
+ long typedef_test(in apa a, in banan b, out apa a1, out banan b1);
+
+ // inlined seq. test
+ s inline_sequence_test(in s a, out s a1);
+
+ // term seq. test
+ etseq term_sequence_test(in etseq a, out etseq a1);
+ // term struct test
+ et term_struct_test(in et a, out et a1);
+
+ };
+
+};
diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/erl_client.erl b/lib/ic/test/erl_client_c_server_SUITE_data/erl_client.erl
new file mode 100644
index 0000000000..79ec28a921
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_SUITE_data/erl_client.erl
@@ -0,0 +1,331 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(erl_client).
+
+-export([void_test/2, long_test/2, longlong_test/2, ushort_test/2,
+ ulong_test/2, ulonglong_test/2, double_test/2, char_test/2,
+ wchar_test/2, octet_test/2, bool_test/2, struct_test/2,
+ struct2_test/2, seq1_test/2, seq2_test/2, seq3_test/2,
+ seq4_test/2, seq5_test/2, array1_test/2, array2_test/2,
+ enum_test/2, string1_test/2, wstring1_test/2, string2_test/2,
+ string3_test/2, string4_test/2, pid_test/2, port_test/2,
+ ref_test/2, term_test/2, typedef_test/2,
+ inline_sequence_test/2, term_sequence_test/2,
+ term_struct_test/2
+
+]).
+
+-include("m.hrl").
+-include("m_i.hrl").
+-include("oe_erl_c_test.hrl").
+
+%%b
+void_test(Node, Timeout) ->
+ Ret = m_i:void_test({olsson, Node}, Timeout),
+ Ret == void. % XXX Not documented
+%%e
+
+%%b
+long_test(Node, Timeout) ->
+ In = max_long(),
+ {Ret, Out} = m_i:long_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+longlong_test(Node, Timeout) ->
+ In = 65537,
+ {Ret, Out} = m_i:longlong_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ushort_test(Node, Timeout) ->
+ In = max_ushort(),
+ {Ret, Out} = m_i:ushort_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ulong_test(Node, Timeout) ->
+ In = max_ulong(),
+ {Ret, Out} = m_i:ulong_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ulonglong_test(Node, Timeout) ->
+ In = 65537,
+ {Ret, Out} = m_i:ulonglong_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+double_test(Node, Timeout) ->
+ In = 37768.93,
+ {Ret, Out} = m_i:double_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+char_test(Node, Timeout) ->
+ In = 80,
+ {Ret, Out} = m_i:char_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+wchar_test(Node, Timeout) ->
+ In = 4097,
+ {Ret, Out} = m_i:wchar_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+octet_test(Node, Timeout) ->
+ In = 255,
+ {Ret, Out} = m_i:octet_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+bool_test(Node, Timeout) ->
+ In = false,
+ {Ret, Out} = m_i:bool_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+struct_test(Node, Timeout) ->
+ In = #m_b{l = max_long(), c = $a},
+ {Ret, Out} = m_i:struct_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+struct2_test(Node, Timeout) ->
+ In = #m_es{ f = banana, l = max_long()},
+ {Ret, Out} = m_i:struct2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq1_test(Node, Timeout) ->
+ B1 = #m_b{l = max_long(), c = $a},
+ B2 = #m_b{l = min_long(), c = $b},
+ In = [B1, B2],
+ {Ret, Out} = m_i:seq1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq2_test(Node, Timeout) ->
+ B = #m_b{l = max_long(), c = $a},
+ A = #m_a{l = min_long(), y = [B, B], d = 4711.31},
+ In = [A, A, A],
+ {Ret, Out} = m_i:seq2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq3_test(Node, Timeout) ->
+ In = [max_long(), min_long(), max_long()],
+ {Ret, Out} = m_i:seq3_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq4_test(Node, Timeout) ->
+ In = [["hello", "all"], ["Erlang", "users", "!"]],
+ {Ret, Out} = m_i:seq4_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq5_test(Node, Timeout) ->
+ Arr3 = mk_array(3, max_long()),
+ In = [[Arr3, Arr3], [Arr3, Arr3, Arr3]],
+ {Ret, Out} = m_i:seq5_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+array1_test(Node, Timeout) ->
+ In = mk_array(500, min_long()),
+ {Ret, Out} = m_i:array1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+array2_test(Node, Timeout) ->
+ In = mk_array(2, mk_array(3, min_long())),
+ {Ret, Out} = m_i:array2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+enum_test(Node, Timeout) ->
+ In = banana,
+ {Ret, Out} = m_i:enum_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string1_test(Node, Timeout) ->
+ In = "Developing Erlang applications is fun!",
+ {Ret, Out} = m_i:string1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+wstring1_test(Node, Timeout) ->
+ In = [1047| "eveloping Erlang applications is fun!"],
+ {Ret, Out} = m_i:wstring1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string2_test(Node, Timeout) ->
+ In = ["Developing Erlang applications ", "is fun!"],
+ {Ret, Out} = m_i:string2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string3_test(Node, Timeout) ->
+ In = "Developing Erlang applications is fun!",
+ {Ret, Out} = m_i:string3_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string4_test(Node, Timeout) ->
+
+ In = #m_strRec{
+ bb = true,
+ str4 = "Developing Erlang applications "
+ "is fun!",
+ str7 = mk_array(3, mk_array(2, max_long())),
+ str5 = [$a, $b, $c, $d, $e, $f],
+ str6 = "123456789012",
+ str8 = {$x, $y, $x},
+ str9 = "123456789012",
+ str10 = [$a, $b, $c, $d, $e, $f]
+ },
+ {Ret, Out} = m_i:string4_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+pid_test(Node, Timeout) ->
+ In = self(),
+ {Ret, Out} = m_i:pid_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+port_test(Node, Timeout) ->
+ In = get(port_test_port),
+ {Ret, Out} = m_i:port_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ref_test(Node, Timeout) ->
+ In = make_ref(),
+ {Ret, Out} = m_i:ref_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+term_test(Node, Timeout) ->
+ In = {[a, b], 17, kalle},
+ {Ret, Out} = m_i:term_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+typedef_test(Node, Timeout) ->
+ In1 = {nisse, [1, 2], olsson},
+ In2 = get(port_test_port),
+ {Ret, Out1, Out2} = m_i:typedef_test({olsson, Node}, Timeout, In1, In2),
+ %% XXX Should check that Ret is an integer.
+ (Out1 == In1) and (Out2 == In2).
+%%e
+
+%%b
+inline_sequence_test(Node, Timeout) ->
+ In = #m_s{l = min_long(), sl = [max_long(), min_long()]},
+ {Ret, Out} = m_i:inline_sequence_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+term_sequence_test(Node, Timeout) ->
+ In = lists:duplicate(17, {nisse, [1, 2], {kalle, olsson}}),
+ {Ret, Out} = m_i:term_sequence_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+term_struct_test(Node, Timeout) ->
+ In = #m_et{e = {nisse, ["abcde"], {kalle, olsson}}, l = 4711},
+ {Ret, Out} = m_i:term_struct_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+
+%% Locals
+
+mk_array(Es) ->
+ list_to_tuple(Es).
+
+mk_array(N, E) ->
+ mk_array(lists:duplicate(N, E)).
+
+%% max_short() ->
+%% power_of_two(15) - 1.
+max_long() ->
+ power_of_two(31) - 1.
+max_longlong() ->
+ power_of_two(63) - 1.
+max_ushort() ->
+ power_of_two(16) - 1.
+max_ulong() ->
+ power_of_two(32) - 1.
+max_ulonglong() ->
+ power_of_two(64) - 1.
+
+%% min_short() ->
+%% -power_of_two(15).
+min_long() ->
+ -power_of_two(31).
+%% min_longlong() ->
+%% -power_of_two(63).
+%% min_ushort() ->
+%% 0.
+%% min_ulong() ->
+%% 0.
+%% min_ulonglong() ->
+%% 0.
+
+power_of_two(N) ->
+ round(math:pow(2, N)).
+
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE.erl b/lib/ic/test/erl_client_c_server_proto_SUITE.erl
new file mode 100644
index 0000000000..f4a06b0f16
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE.erl
@@ -0,0 +1,366 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose : Test suite for erl-client/c-server
+%%----------------------------------------------------------------------
+
+
+-module(erl_client_c_server_proto_SUITE).
+-include_lib("common_test/include/ct.hrl").
+
+-export([init_per_testcase/2, end_per_testcase/2,all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, void_test/1,
+ long_test/1, longlong_test/1, ushort_test/1, ulong_test/1,
+ ulonglong_test/1, double_test/1, char_test/1, wchar_test/1,
+ octet_test/1, bool_test/1, struct_test/1, struct2_test/1,
+ seq1_test/1, seq2_test/1, seq3_test/1, seq4_test/1,
+ seq5_test/1, array1_test/1, array2_test/1, enum_test/1,
+ string1_test/1, string2_test/1, string3_test/1,
+ string4_test/1, pid_test/1, port_test/1, ref_test/1,
+ term_test/1, typedef_test/1, inline_sequence_test/1,
+ term_sequence_test/1, term_struct_test/1, wstring1_test/1]).
+
+-define(DEFAULT_TIMEOUT, 20000).
+-define(PORT_TIMEOUT, 15000).
+-define(CALL_TIMEOUT, 5000).
+
+-define(C_SERVER_NODE_NAME, idl_c_server_test).
+
+%% Add/remove code path and watchdog before/after each test case.
+%%
+init_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:add_patha(DataDir),
+
+ %% Since other test suites use the module m_i, we have
+ %% to make sure we are using the right m_i module.
+ code:purge(m_i),
+ code:load_file(m_i),
+
+ WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:del_path(DataDir),
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+[void_test, long_test, longlong_test, ushort_test,
+ ulong_test, ulonglong_test, double_test, char_test,
+ wchar_test, octet_test, bool_test, struct_test,
+ struct2_test, seq1_test, seq2_test, seq3_test,
+ seq4_test, seq5_test, array1_test, array2_test,
+ enum_test, string1_test, string2_test, string3_test,
+ string4_test, pid_test, port_test, ref_test, term_test,
+ typedef_test, inline_sequence_test, term_sequence_test,
+ term_struct_test, wstring1_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+array1_test(doc) -> "";
+array1_test(suite) -> [];
+array1_test(Config) ->
+ do_test(array1_test, Config).
+
+array2_test(doc) -> "";
+array2_test(suite) -> [];
+array2_test(Config) ->
+ do_test(array2_test, Config).
+
+bool_test(doc) -> "";
+bool_test(suite) -> [];
+bool_test(Config) ->
+ do_test(bool_test, Config).
+
+char_test(doc) -> "";
+char_test(suite) -> [];
+char_test(Config) ->
+ do_test(char_test, Config).
+
+double_test(doc) -> "";
+double_test(suite) -> [];
+double_test(Config) ->
+ do_test(double_test, Config).
+
+enum_test(doc) -> "";
+enum_test(suite) -> [];
+enum_test(Config) ->
+ do_test(enum_test, Config).
+
+inline_sequence_test(doc) -> "";
+inline_sequence_test(suite) -> [];
+inline_sequence_test(Config) ->
+ do_test(inline_sequence_test, Config).
+
+longlong_test(doc) -> "";
+longlong_test(suite) -> [];
+longlong_test(Config) ->
+ do_test(longlong_test, Config).
+
+long_test(doc) -> "";
+long_test(suite) -> [];
+long_test(Config) ->
+ do_test(long_test, Config).
+
+octet_test(doc) -> "";
+octet_test(suite) -> [];
+octet_test(Config) ->
+ do_test(octet_test, Config).
+
+pid_test(doc) -> "";
+pid_test(suite) -> [];
+pid_test(Config) ->
+ do_test(pid_test, Config).
+
+port_test(doc) -> "";
+port_test(suite) -> [];
+port_test(Config) ->
+ do_test(port_test, Config).
+
+ref_test(doc) -> "";
+ref_test(suite) -> [];
+ref_test(Config) ->
+ do_test(ref_test, Config).
+
+seq1_test(doc) -> "";
+seq1_test(suite) -> [];
+seq1_test(Config) ->
+ do_test(seq1_test, Config).
+
+seq2_test(doc) -> "";
+seq2_test(suite) -> [];
+seq2_test(Config) ->
+ do_test(seq2_test, Config).
+
+seq3_test(doc) -> "";
+seq3_test(suite) -> [];
+seq3_test(Config) ->
+ do_test(seq3_test, Config).
+
+seq4_test(doc) -> "";
+seq4_test(suite) -> [];
+seq4_test(Config) ->
+ do_test(seq4_test, Config).
+
+seq5_test(doc) -> "";
+seq5_test(suite) -> [];
+seq5_test(Config) ->
+ do_test(seq5_test, Config).
+
+string1_test(doc) -> "";
+string1_test(suite) -> [];
+string1_test(Config) ->
+ do_test(string1_test, Config).
+
+string2_test(doc) -> "";
+string2_test(suite) -> [];
+string2_test(Config) ->
+ do_test(string2_test, Config).
+
+string3_test(doc) -> "";
+string3_test(suite) -> [];
+string3_test(Config) ->
+ do_test(string3_test, Config).
+
+string4_test(doc) -> "";
+string4_test(suite) -> [];
+string4_test(Config) ->
+ do_test(string4_test, Config).
+
+struct2_test(doc) -> "";
+struct2_test(suite) -> [];
+struct2_test(Config) ->
+ do_test(struct2_test, Config).
+
+struct_test(doc) -> "";
+struct_test(suite) -> [];
+struct_test(Config) ->
+ do_test(struct_test, Config).
+
+term_sequence_test(doc) -> "";
+term_sequence_test(suite) -> [];
+term_sequence_test(Config) ->
+ do_test(term_sequence_test, Config).
+
+term_struct_test(doc) -> "";
+term_struct_test(suite) -> [];
+term_struct_test(Config) ->
+ do_test(term_struct_test, Config).
+
+term_test(doc) -> "";
+term_test(suite) -> [];
+term_test(Config) ->
+ do_test(term_test, Config).
+
+typedef_test(doc) -> "";
+typedef_test(suite) -> [];
+typedef_test(Config) ->
+ do_test(typedef_test, Config).
+
+ulonglong_test(doc) -> "";
+ulonglong_test(suite) -> [];
+ulonglong_test(Config) ->
+ do_test(ulonglong_test, Config).
+
+ulong_test(doc) -> "";
+ulong_test(suite) -> [];
+ulong_test(Config) ->
+ do_test(ulong_test, Config).
+
+ushort_test(doc) -> "";
+ushort_test(suite) -> [];
+ushort_test(Config) ->
+ do_test(ushort_test, Config).
+
+void_test(doc) -> "";
+void_test(suite) -> [];
+void_test(Config) ->
+ do_test(void_test, Config).
+
+wchar_test(doc) -> "";
+wchar_test(suite) -> [];
+wchar_test(Config) ->
+ do_test(wchar_test, Config).
+
+wstring1_test(doc) -> "";
+wstring1_test(suite) -> [];
+wstring1_test(Config) ->
+ do_test(wstring1_test, Config).
+
+
+do_test(Case, Config) ->
+ %% Trap exits
+ process_flag(trap_exit, true),
+ Node = atom_to_list(node()),
+ [_NodeName, HostName] = string:tokens(Node, "@"),
+ DataDir = ?config(data_dir, Config),
+ %% io:format("~p: data directory: ~p~n", [?MODULE, DataDir]),
+ Cookie = atom_to_list(erlang:get_cookie()),
+ ServerNodeName = atom_to_list(?C_SERVER_NODE_NAME),
+ %% Start C-server node as a port program. We wait for the node
+ %% to connect to us.
+ Cmd = filename:join([DataDir, "c_server"]) ++
+ " -this-node-name " ++ ServerNodeName ++
+ " -peer-node " ++ Node ++
+ " -cookie " ++ Cookie,
+ Port = open_port({spawn, Cmd}, [exit_status, eof, stderr_to_stdout]),
+ ServerNode = list_to_atom(ServerNodeName ++ "@" ++ HostName),
+ Res = case wait_for_hidden_node(ServerNode) of
+ ok ->
+ %% Need a port for port_test and typedef_test
+ put(port_test_port, Port),
+ R = (catch erl_client:Case(ServerNode, ?CALL_TIMEOUT)),
+ case wait_for_completion(Port) of
+ {error, timeout} ->
+ kill_off_node(ServerNode);
+ _ ->
+ ok
+ end,
+ R;
+ {error, timeout} ->
+ case wait_for_completion(Port) of
+ {error, timeout} ->
+ kill_off_node(ServerNode);
+ _ ->
+ ok
+ end,
+ {error, timeout}
+ end,
+ process_flag(trap_exit, false),
+ true = Res.
+
+
+%% Wait for eof *and* exit status, but return if exit status indicates
+%% an error, or we have been waiting more than PORT_TIMEOUT seconds.
+%%
+wait_for_completion(Port) ->
+ wait_for_completion(Port, 0).
+
+wait_for_completion(Port, N) when N < 2 ->
+ receive
+ {Port, {data, Bytes}} ->
+ %% Relay output
+ io:format("~s", [Bytes]),
+ wait_for_completion(Port, N);
+ {Port, {exit_status, 0}} ->
+ wait_for_completion(Port, N + 1);
+ {Port, {exit_status, Status}} ->
+ {error, Status};
+ {Port, eof} ->
+ wait_for_completion(Port, N + 1);
+ {'EXIT', Port, Reason} ->
+ io:format("Port exited with reason: ~w~n", [Reason]),
+ wait_for_completion(Port, N);
+ {'EXIT', From, Reason} ->
+ io:format("Got unexpected exit: ~p~n", [{'EXIT', From, Reason}]),
+ wait_for_completion(Port, N)
+ after ?PORT_TIMEOUT ->
+ {error, timeout}
+ end;
+wait_for_completion(_, _) ->
+ ok.
+
+wait_for_hidden_node(Node) ->
+ Times = ?DEFAULT_TIMEOUT div 100,
+ wait_for_hidden_node(Node, Times, 100).
+
+wait_for_hidden_node(Node, Times, WaitTime) when Times > 0 ->
+ io:format("Waiting for hidden node: ~p~n", [Node]),
+ case lists:member(Node, erlang:nodes(hidden)) of
+ true ->
+ ok;
+ false ->
+ delay(WaitTime),
+ wait_for_hidden_node(Node, Times - 1, WaitTime)
+ end;
+wait_for_hidden_node(_Node, _, _WaitTime) ->
+ {error, timeout}.
+
+kill_off_node(Node) ->
+ catch rpc:cast(Node, erlang, halt, [1]).
+
+delay(Time) ->
+ receive
+ after Time ->
+ ok
+ end.
+
+
+
+
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src b/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..6c7701ca50
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src
@@ -0,0 +1,150 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Makefile.src for erl_client_c_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .c .h .erl .idl @obj@ .@EMULATOR@
+
+
+# Variables from ts:
+#
+
+ERL_INCLUDE = @erl_include@
+
+IC_INCLUDE_PATH = @ic_include_path@
+IC_LIB = @ic_libpath@@DS@@ic_lib@
+
+ERL_INTERFACE_INCLUDE = @erl_interface_include@
+ERL_INTERFACE_LIB = @erl_interface_libpath@@DS@@erl_interface_lib@
+ERL_INTERFACE_EILIB = @erl_interface_libpath@@DS@@erl_interface_eilib@
+ERL_INTERFACE_THREADLIB = @erl_interface_threadlib@
+ERL_INTERFACE_SOCK_LIBS = @erl_interface_sock_libs@
+
+CC = @CC@
+## XXX Should set warning flag with a DEBUG_FLAG
+CFLAGS = @CFLAGS@ @DEFS@ -I$(ERL_INCLUDE) \
+ -I$(IC_INCLUDE_PATH) -I$(ERL_INTERFACE_INCLUDE)
+
+LD = @LD@
+LDFLAGS = @CROSSLDFLAGS@
+LIBS = $(IC_LIB) $(ERL_INTERFACE_LIB) $(ERL_INTERFACE_EILIB) \
+ $(ERL_INTERFACE_THREADLIB) @LIBS@ $(ERL_INTERFACE_SOCK_LIBS)
+ERLC = erlc
+
+# Generated C header files
+GEN_H_FILES = \
+ m__s.h \
+ m_i__s.h \
+ oe_erl_c_test__s.h
+
+# Generated C files
+GEN_C_FILES = \
+ m__s.c \
+ m_i__s.c \
+ oe_code_m_a.c \
+ oe_code_m_arr1.c \
+ oe_code_m_arr2.c \
+ oe_code_m_arr3.c \
+ oe_code_m_aseq.c \
+ oe_code_m_b.c \
+ oe_code_m_bseq.c \
+ oe_code_m_dd.c \
+ oe_code_m_dyn.c \
+ oe_code_m_dyn_sl.c \
+ oe_code_m_es.c \
+ oe_code_m_et.c \
+ oe_code_m_etseq.c \
+ oe_code_m_fruit.c \
+ oe_code_m_lseq.c \
+ oe_code_m_s.c \
+ oe_code_m_s_sl.c \
+ oe_code_m_sarr3.c \
+ oe_code_m_simple.c \
+ oe_code_m_ssarr3.c \
+ oe_code_m_sseq.c \
+ oe_code_m_ssstr3.c \
+ oe_code_m_sstr3.c \
+ oe_code_m_str1.c \
+ oe_code_m_str3.c \
+ oe_code_m_strRec.c \
+ oe_code_m_strRec_str5.c \
+ oe_code_m_strRec_str7.c \
+ oe_erl_c_test__s.c
+
+GEN_HRL_FILES = \
+ m.hrl \
+ m_i.hrl \
+ oe_erl_c_test.hrl
+
+GEN_ERL_FILES = \
+ m.erl \
+ m_arr2.erl \
+ m_arr3.erl \
+ m_i.erl \
+ m_str3.erl \
+ oe_erl_c_test.erl
+
+C_FILES = $(GEN_C_FILES) c_server.c callbacks.c
+
+OBJS = $(C_FILES:.c=@obj@)
+
+PGMS = c_server@exe@
+
+ERL_FILES = $(GEN_ERL_FILES) erl_client.erl
+
+EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
+
+
+all: $(PGMS) $(EBINS)
+
+clean:
+ -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+ -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \
+ $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES)
+
+$(PGMS): $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+
+$(GEN_C_FILES) $(GEN_H_FILES): erl_c_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_server}" \
+ "+{scoped_op_calls,true}" erl_c_test.idl
+
+# If we have scoped operation calls for C, we must have that for
+# Erlang as well, if we use the m_i.erl file for calling the server.
+
+$(GEN_ERL_FILES) $(GEN_HRL_FILES): erl_c_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" \
+ "+{scoped_op_calls,true}" "+{timeout,true}" erl_c_test.idl
+
+.c@obj@:
+ $(CC) -c -o $*@obj@ $(CFLAGS) $<
+
+.erl.@EMULATOR@:
+ $(ERLC) -W -I $(IC_INCLUDE_PATH) $<
+
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c b/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c
new file mode 100644
index 0000000000..8192341548
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/c_server.c
@@ -0,0 +1,299 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+/* C-server for test of IC.
+ *
+ * The C-node implemented here connects to its peer node, waits for
+ * one message, evaluates the message, returns an result message, and
+ * terminates.
+ *
+ * TODO:
+ *
+ * 1. XXX #includes for VxWorks, Windows
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+
+#include <string.h>
+
+#ifdef __WIN32__
+# include <time.h>
+# include <sys/timeb.h>
+#elif defined VXWORKS
+# include <time.h>
+# include <sys/times.h>
+#else
+# include <sys/time.h>
+#endif
+
+#include <ctype.h>
+
+#ifdef __WIN32__
+# include <winsock2.h>
+# include <windows.h>
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+#endif
+
+#include "ic.h"
+#include "ei.h"
+#include "erl_interface.h"
+#include "eicode.h"
+#include "m_i__s.h"
+#include "m__s.h"
+
+#ifdef __WIN32__
+typedef struct {
+ long tv_sec;
+ long tv_usec;
+} MyTimeval;
+#else
+typedef struct timeval MyTimeval;
+#endif
+static void my_gettimeofday(MyTimeval *tv);
+static void showtime(MyTimeval *start, MyTimeval *stop);
+static void usage(void);
+static void done(int r);
+
+#define HOSTNAMESZ 256
+#define NODENAMESZ 512
+#define INBUFSZ 10
+#define OUTBUFSZ 0
+#define MAXTRIES 5
+
+static char *progname;
+
+/* main */
+#ifdef VXWORKS
+int c_server(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ struct hostent *hp;
+ MyTimeval start, stop;
+ int i, fd, ires, tries;
+ CORBA_Environment *env;
+ char *this_node_name = NULL;
+ char *peer_node = NULL;
+ char *cookie = NULL;
+ char host[HOSTNAMESZ + 1];
+ char this_node[NODENAMESZ + 1];
+ erlang_msg msg;
+ int status, loop;
+
+#ifdef __WIN32__
+ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD(2, 0);
+
+ if (WSAStartup(wVersionRequested, &wsaData) != 0) {
+ fprintf(stderr, "Could not load winsock2 v2.0 compatible DLL");
+ exit(1);
+ }
+#endif
+
+ progname = argv[0];
+ host[HOSTNAMESZ] = '\0';
+ if (gethostname(host, HOSTNAMESZ) < 0) {
+ fprintf(stderr, "Can't find own hostname\n");
+ done(1);
+ }
+ if ((hp = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Can't get ip address for host %s\n", host);
+ done(1);
+ }
+ for (i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-help") == 0) {
+ usage();
+ done(0);
+ } else if (strcmp(argv[i], "-this-node-name") == 0) {
+ i++;
+ this_node_name = argv[i];
+ } else if (strcmp(argv[i], "-peer-node") == 0) {
+ i++;
+ peer_node = argv[i];
+ } else if (strcmp(argv[i], "-cookie") == 0) {
+ i++;
+ cookie = argv[i];
+ } else {
+ fprintf(stderr, "Error : invalid argument \"%s\"\n", argv[i]);
+ usage();
+ done(1);
+ }
+ }
+
+ if (this_node_name == NULL || peer_node == NULL || cookie == NULL) {
+ fprintf(stderr, "Error: missing option\n");
+ usage();
+ done(1);
+ }
+
+ /* Behead hostname at first dot */
+ for (i=0; host[i] != '\0'; i++) {
+ if (host[i] == '.') { host[i] = '\0'; break; }
+ }
+ sprintf(this_node, "%s@%s", this_node_name, host);
+
+ fprintf(stderr, "c_server: this node: \"%s\"\n", this_node);
+ fprintf(stderr, "c_server: peer node: \"%s\"\n", peer_node);
+
+ /* initialize erl_interface */
+ erl_init(NULL, 0);
+
+ for (tries = 0; tries < MAXTRIES; tries++) {
+ /* connect to peer node */
+ ires = erl_connect_xinit(host, this_node_name, this_node,
+ (struct in_addr *)*hp->h_addr_list,
+ cookie, 0);
+ fprintf(stderr, "c_server: erl_connect_xinit(): %d\n", ires);
+
+ fd = erl_connect(peer_node);
+ fprintf(stderr, "c_server: erl_connect(): %d\n", fd);
+ if (fd >= 0)
+ break;
+ fprintf(stderr, "c_server: cannot connect, retrying\n");
+ }
+ if (fd < 0) {
+ fprintf(stderr, "c_server: cannot connect, exiting\n");
+ done(1);
+ }
+ env = CORBA_Environment_alloc(INBUFSZ, OUTBUFSZ);
+ env->_fd = fd;
+
+ status = 1;
+ loop = 1;
+ my_gettimeofday(&start);
+ while (status >= 0 && loop > 0) {
+ status = ei_receive_encoded(env->_fd, &env->_inbuf, &env->_inbufsz,
+ &msg, &env->_iin);
+ switch(status) {
+ case ERL_SEND:
+ case ERL_REG_SEND:
+ /* get result */
+ m_i__switch(NULL, env);
+ switch(env->_major) {
+ case CORBA_NO_EXCEPTION:
+ break;
+ case CORBA_SYSTEM_EXCEPTION:
+ fprintf(stderr, "Request failure, reason : %s\n",
+ (char *) CORBA_exception_value(env));
+ CORBA_exception_free(env);
+ break;
+ default: /* Should not happen */
+ CORBA_exception_free(env);
+ break;
+ }
+ /* send back result data */
+ if (env->_iout > 0)
+ ei_send_encoded(env->_fd, &env->_caller, env->_outbuf,
+ env->_iout);
+ loop = 0;
+ break;
+ case ERL_TICK:
+ break;
+ default:
+ if (status < 0) {
+ fprintf(stderr, "Status negative: %d\n", status);
+ loop = 0;
+ }
+ break;
+ }
+ }
+ my_gettimeofday(&stop);
+ showtime(&start, &stop);
+
+ erl_close_connection(fd);
+
+ CORBA_free(env->_inbuf);
+ CORBA_free(env->_outbuf);
+ CORBA_free(env);
+ if (status < 0)
+ done(-status);
+ else
+ done(0);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [-help] -this-node-name <name> "
+ "-peer-node <nodename> -cookie <cookie>\n", progname);
+ fprintf(stderr, "Example:\n %s -this-node-name kalle "
+ "-peer-node olle@home -cookie oa678er\n", progname);
+}
+
+static void done(int r)
+{
+#ifdef __WIN32__
+ WSACleanup();
+#endif
+ exit(r);
+}
+
+static void showtime(MyTimeval *start, MyTimeval *stop)
+{
+ MyTimeval elapsed;
+
+ elapsed.tv_sec = stop->tv_sec - start->tv_sec;
+ elapsed.tv_usec = stop->tv_usec - start->tv_usec;
+ while (elapsed.tv_usec < 0) {
+ elapsed.tv_sec -= 1;
+ elapsed.tv_usec += 1000000;
+ }
+ fprintf(stderr,"%ld.%06ld seconds\n",elapsed.tv_sec, elapsed.tv_usec);
+}
+
+
+
+static void my_gettimeofday(MyTimeval *tv)
+#ifdef __WIN32__
+#define EPOCH_JULIAN_DIFF 11644473600i64
+{
+ SYSTEMTIME t;
+ FILETIME ft;
+ LONGLONG lft;
+
+ GetSystemTime(&t);
+ SystemTimeToFileTime(&t, &ft);
+ memcpy(&lft, &ft, sizeof(lft));
+ tv->tv_usec = (long) ((lft / 10i64) % 1000000i64);
+ tv->tv_sec = (long) ((lft / 10000000i64) - EPOCH_JULIAN_DIFF);
+}
+#elif defined VXWORKS
+{
+ int rate = sysClkRateGet(); /* Ticks per second */
+ unsigned long ctick = tickGet();
+ tv->tv_sec = ctick / rate; /* secs since reboot */
+ tv->tv_usec = ((ctick - (tv->tv_sec * rate))*1000000)/rate;
+}
+#else
+{
+ gettimeofday(tv, NULL);
+}
+#endif
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c b/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c
new file mode 100644
index 0000000000..c423a9e51c
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/callbacks.c
@@ -0,0 +1,610 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef __WIN32__
+# include <unistd.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include <ic.h>
+#include <erl_interface.h>
+#include <ei.h>
+#include "m_i__s.h"
+
+
+
+/* OK */
+
+void my_void_test(CORBA_Object oe_obj,
+ CORBA_Environment *oe_env)
+{
+ /* printf("void test !\n"); */
+}
+
+m_i_void_test__rs* m_i_void_test__cb(CORBA_Object oe_obj,
+ CORBA_Environment *oe_env)
+{
+ return (m_i_void_test__rs*) (my_void_test);
+}
+
+
+
+/* OK */
+
+void my_long_test(CORBA_Object oe_obj,
+ long* a,
+ long* b,
+ long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("long test !\n"); */
+}
+
+
+m_i_long_test__rs* m_i_long_test__cb(CORBA_Object oe_obj,
+ long* a,
+ long* b,
+ long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_long_test__rs*) (my_long_test);
+}
+
+/* OK */
+
+void my_longlong_test(CORBA_Object oe_obj,
+ CORBA_long_long* a,
+ CORBA_long_long* b,
+ CORBA_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("long test !\n"); */
+}
+
+m_i_longlong_test__rs* m_i_longlong_test__cb(CORBA_Object oe_obj,
+ CORBA_long_long* a,
+ CORBA_long_long* b,
+ CORBA_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_longlong_test__rs*) (my_longlong_test);
+}
+
+/* OK */
+void my_ulong_test(CORBA_Object oe_obj,
+ unsigned long* a,
+ unsigned long* b,
+ unsigned long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("ulong test !\n"); */
+}
+
+m_i_ulong_test__rs* m_i_ulong_test__cb(CORBA_Object oe_obj,
+ unsigned long* a,
+ unsigned long* b,
+ unsigned long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_ulong_test__rs*) (my_ulong_test);
+}
+
+/* OK */
+void my_ulonglong_test(CORBA_Object oe_obj,
+ CORBA_unsigned_long_long* a,
+ CORBA_unsigned_long_long* b,
+ CORBA_unsigned_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("ulong test !\n"); */
+}
+
+m_i_ulonglong_test__rs* m_i_ulonglong_test__cb(CORBA_Object oe_obj,
+ CORBA_unsigned_long_long* a,
+ CORBA_unsigned_long_long* b,
+ CORBA_unsigned_long_long* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_ulonglong_test__rs*) (my_ulonglong_test);
+}
+
+m_i_ushort_test__rs* m_i_ushort_test__cb(CORBA_Object oe_obj,
+ unsigned short* a,
+ unsigned short* b,
+ unsigned short* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_ushort_test__rs*) NULL;
+}
+
+
+/* OK */
+void my_double_test(CORBA_Object oe_obj,
+ double* a,
+ double* b,
+ double* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("double test !\n"); */
+}
+
+m_i_double_test__rs* m_i_double_test__cb(CORBA_Object oe_obj,
+ double* a,
+ double* b,
+ double* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_double_test__rs*) (my_double_test);
+}
+
+/* OK */
+m_i_char_test__rs* m_i_char_test__cb(CORBA_Object oe_obj,
+ char* a,
+ char* b,
+ char* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_char_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+
+/* OK */
+m_i_wchar_test__rs* m_i_wchar_test__cb(CORBA_Object oe_obj,
+ CORBA_wchar* a,
+ CORBA_wchar* b,
+ CORBA_wchar* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_wchar_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_octet_test__rs* m_i_octet_test__cb(CORBA_Object oe_obj,
+ char* a,
+ char* b,
+ char* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_octet_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_bool_test__rs* m_i_bool_test__cb(CORBA_Object oe_obj,
+ CORBA_boolean* a,
+ CORBA_boolean* b,
+ CORBA_boolean* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_bool_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+void my_struct_test(CORBA_Object oe_obj,
+ m_b* a,
+ m_b* b,
+ m_b* c,
+ CORBA_Environment *oe_env)
+{
+ /* printf("struct test !\n"); */
+}
+
+m_i_struct_test__rs* m_i_struct_test__cb(CORBA_Object oe_obj,
+ m_b* a,
+ m_b* b,
+ m_b* c,
+ CORBA_Environment *oe_env)
+{
+ *a = *b;
+ *c = *b;
+ return (m_i_struct_test__rs*) (my_struct_test);
+}
+
+/* OK */
+m_i_struct2_test__rs* m_i_struct2_test__cb(CORBA_Object oe_obj,
+ m_es* a,
+ m_es* b,
+ m_es* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_struct2_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+/* XXX Commented out
+m_i_struct3_test__rs* m_i_struct3_test__cb(CORBA_Object oe_obj,
+ m_simple* a,
+ m_simple* b,
+ m_simple* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_struct3_test__rs* rs = NULL;
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+*/
+
+/* OK */
+m_i_seq1_test__rs* m_i_seq1_test__cb(CORBA_Object oe_obj,
+ m_bseq** a,
+ m_bseq* b,
+ m_bseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq1_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+
+/* OK */
+m_i_seq2_test__rs* m_i_seq2_test__cb(CORBA_Object oe_obj,
+ m_aseq** a,
+ m_aseq* b,
+ m_aseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq2_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_seq3_test__rs* m_i_seq3_test__cb(CORBA_Object oe_obj,
+ m_lseq** a,
+ m_lseq* b,
+ m_lseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq3_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_seq4_test__rs* m_i_seq4_test__cb(CORBA_Object oe_obj,
+ m_ssstr3** a,
+ m_ssstr3* b,
+ m_ssstr3** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq4_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_seq5_test__rs* m_i_seq5_test__cb(CORBA_Object oe_obj,
+ m_ssarr3** a,
+ m_ssarr3* b,
+ m_ssarr3** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_seq5_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_array1_test__rs* m_i_array1_test__cb(CORBA_Object oe_obj,
+ m_arr1 a,
+ m_arr1 b,
+ m_arr1 c,
+ CORBA_Environment *oe_env)
+{
+ int i;
+ m_i_array1_test__rs* rs = NULL;
+
+ for (i = 0; i < 500; i++) {
+ a[i] = b[i];
+ c[i] = b[i];
+ }
+ return rs;
+}
+
+/* OK */
+m_i_array2_test__rs* m_i_array2_test__cb(CORBA_Object oe_obj,
+ m_dd a,
+ m_dd b,
+ m_dd c,
+ CORBA_Environment *oe_env)
+{
+ int i,j;
+ m_i_array2_test__rs* rs = NULL;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < 3; j++) {
+ a[i][j] = b[i][j];
+ c[i][j] = b[i][j];
+ }
+ return rs;
+}
+
+
+/* OK */
+m_i_enum_test__rs* m_i_enum_test__cb(CORBA_Object oe_obj,
+ m_fruit* a,
+ m_fruit* b,
+ m_fruit* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_enum_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_string1_test__rs* m_i_string1_test__cb(CORBA_Object oe_obj,
+ char ** a,
+ char * b,
+ char ** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_string1_test__rs* rs = NULL;
+
+ /*printf("\nString in ------> %s\n\n",b);*/
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_string2_test__rs* m_i_string2_test__cb(CORBA_Object oe_obj,
+ m_sseq** a,
+ m_sseq* b,
+ m_sseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_string2_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_string3_test__rs* m_i_string3_test__cb(CORBA_Object oe_obj,
+ char ** a,
+ char * b,
+ char ** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_string3_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+m_i_string4_test__rs* m_i_string4_test__cb(CORBA_Object oe_obj,
+ m_strRec** a,
+ m_strRec* b,
+ m_strRec** c,
+ CORBA_Environment *oe_env)
+{
+ *a = b;
+ *c = b;
+
+ return (m_i_string4_test__rs*) NULL;
+}
+
+/* OK */
+m_i_wstring1_test__rs* m_i_wstring1_test__cb(CORBA_Object oe_obj,
+ CORBA_wchar ** a,
+ CORBA_wchar * b,
+ CORBA_wchar ** c,
+ CORBA_Environment *oe_env)
+{
+ int tmp;
+ m_i_wstring1_test__rs* rs = NULL;
+
+ /*printf("\nString in ------> %s\n\n",b);*/
+
+ for(tmp = 0; tmp < 5; tmp++)
+ fprintf(stderr,"\np[%d] = %ld\n", tmp, b[tmp]);
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+
+/* OK */
+m_i_pid_test__rs* m_i_pid_test__cb(CORBA_Object oe_obj,
+ erlang_pid* a,
+ erlang_pid* b,
+ erlang_pid* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_pid_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+/* OK */
+m_i_port_test__rs* m_i_port_test__cb(CORBA_Object oe_obj,
+ erlang_port* a,
+ erlang_port* b,
+ erlang_port* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_port_test__rs* rs = NULL;
+
+ strcpy((*a).node,(*b).node);
+ (*a).id = (*b).id;
+ (*a).creation = 0;
+
+ strcpy((*c).node,(*b).node);
+ (*c).id = (*b).id;
+ (*c).creation = 0;
+ return rs;
+}
+
+/* OK */
+m_i_ref_test__rs* m_i_ref_test__cb(CORBA_Object oe_obj,
+ erlang_ref* a,
+ erlang_ref* b,
+ erlang_ref* c,
+ CORBA_Environment *oe_env)
+{
+
+ m_i_ref_test__rs* rs = NULL;
+
+ strcpy((*a).node,(*b).node);
+ /*(*a).id = (*b).id;*/
+ (*a).len = (*b).len;
+ (*a).n[0] = (*b).n[0];
+ (*a).n[1] = (*b).n[1];
+ (*a).n[2] = (*b).n[2];
+ (*a).creation = 0;
+
+ strcpy((*c).node,(*b).node);
+ /*(*c).id = (*b).id;*/
+ (*c).len = (*b).len;
+ (*c).n[0] = (*b).n[0];
+ (*c).n[1] = (*b).n[1];
+ (*c).n[2] = (*b).n[2];
+ (*c).creation = 0;
+ return rs;
+}
+
+/* OK */
+m_i_term_test__rs* m_i_term_test__cb(CORBA_Object oe_obj,
+ ETERM** a,
+ ETERM** b,
+ ETERM** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_term_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
+m_i_typedef_test__rs* m_i_typedef_test__cb(CORBA_Object oe_obj,
+ long* a,
+ ETERM** b,
+ erlang_port* c,
+ ETERM** d ,
+ erlang_port* e,
+ CORBA_Environment *oe_env)
+{
+ m_i_typedef_test__rs* rs = NULL;
+
+ *d = *b;
+ strcpy((*e).node,(*c).node);
+ (*e).id = (*c).id;
+ (*e).creation = 0;
+ *a = 4711;
+ return rs;
+}
+
+/* OK */
+m_i_inline_sequence_test__rs* m_i_inline_sequence_test__cb(
+ CORBA_Object oe_obj,
+ m_s** a,
+ m_s* b,
+ m_s** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_inline_sequence_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+/* OK */
+m_i_term_sequence_test__rs* m_i_term_sequence_test__cb(
+ CORBA_Object oe_obj,
+ m_etseq** a,
+ m_etseq* b,
+ m_etseq** c,
+ CORBA_Environment *oe_env)
+{
+ m_i_term_sequence_test__rs* rs = NULL;
+
+ *a = b;
+ *c = b;
+ return rs;
+}
+
+
+/* OK */
+m_i_term_struct_test__rs* m_i_term_struct_test__cb(CORBA_Object oe_obj,
+ m_et* a,
+ m_et* b,
+ m_et* c,
+ CORBA_Environment *oe_env)
+{
+ m_i_term_struct_test__rs* rs = NULL;
+
+ *a = *b;
+ *c = *b;
+ return rs;
+}
+
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_c_test.idl b/lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_c_test.idl
new file mode 100644
index 0000000000..e90d0dd5f0
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_c_test.idl
@@ -0,0 +1,174 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2004-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+#include "erlang.idl"
+
+
+const short TestConst = 1;
+
+module m {
+
+ const short TestConst = 2;
+
+ struct b {
+ long l;
+ char c;
+ };
+
+ struct simple {
+ long l;
+ b b_t;
+ };
+
+ enum fruit {orange, banana, apple, peach, pear};
+
+ typedef sequence<long> lseq;
+
+ typedef sequence<b> bseq;
+
+ struct a {
+ long l;
+ bseq y;
+ double d;
+ };
+
+ typedef sequence<a> aseq;
+
+ typedef sequence<string> sseq;
+ typedef string str;
+ typedef long myLong;
+
+ typedef long arr1[500], dd[2][3];
+
+ typedef erlang::term apa;
+ typedef erlang::port banan;
+
+ typedef sequence<erlang::term> etseq;
+
+ struct s {
+ long l;
+ sequence<long> sl;
+ };
+
+ struct es {
+ fruit f;
+ myLong l;
+ };
+
+ struct et {
+ erlang::term e;
+ long l;
+ };
+
+
+ typedef sequence<char> str1;
+ typedef string<12> str2;
+ typedef char str3[3];
+
+ typedef sequence<string> sstr3; // sequence of string
+ typedef sequence<sstr3> ssstr3; // sequence of sequences of strings
+
+ typedef long arr3[3]; // array of long
+ typedef sequence<arr3> sarr3; // sequence of array
+ typedef sequence<sarr3> ssarr3; // sequence of sequnces of arrays of strings
+
+ struct strRec{
+ boolean bb;
+ string str4;
+ long str7[3][2];
+ sequence<char> str5;
+ string<12> str6;
+ str3 str8;
+ str2 str9;
+ str1 str10;
+ };
+
+
+ struct dyn {
+ long l;
+ sequence<long> sl;
+ };
+ typedef dyn arr2[1][2];
+
+
+ interface i {
+
+ const short TestConst = 3;
+
+ //arr2 suck(in arr2 x, out arr2 y );
+
+ ///////////////////////////////// attribute long l;
+
+ // simple types
+ void void_test();
+ long long_test(in long a, out long a1);
+ long long longlong_test(in long long a, out long long a1);
+ unsigned short ushort_test(in unsigned short a, out unsigned short a1);
+ unsigned long ulong_test(in unsigned long a, out unsigned long a1);
+ unsigned long long ulonglong_test(in unsigned long long a, out unsigned long long a1);
+ double double_test(in double a, out double a1);
+ char char_test(in char a, out char a1);
+ wchar wchar_test(in wchar a, out wchar a1);
+ octet octet_test(in octet a, out octet a1);
+ boolean bool_test(in boolean a, out boolean a1);
+
+ // Seq. and struct tests
+ b struct_test(in b a, out b a1);
+ es struct2_test(in es a, out es a1);
+ //simple struct3_test(in simple x, out simple y);
+ bseq seq1_test(in bseq a, out bseq a1);
+ aseq seq2_test(in aseq a, out aseq a1);
+ lseq seq3_test(in lseq a, out lseq a1);
+ ssstr3 seq4_test(in ssstr3 a, out ssstr3 a1);
+ ssarr3 seq5_test(in ssarr3 a, out ssarr3 a1);
+
+ // Array tests
+ arr1 array1_test(in arr1 a, out arr1 a1);
+ dd array2_test(in dd a, out dd a1);
+
+ // enum test
+ fruit enum_test(in fruit a, out fruit a1);
+
+ // string tests
+ string string1_test(in string a, out string a1);
+ wstring wstring1_test(in wstring a, out wstring a1);
+ sseq string2_test(in sseq a, out sseq a1);
+ str string3_test(in str a, out str a1);
+ strRec string4_test(in strRec a, out strRec a1);
+
+ // Special erlang types
+ erlang::pid pid_test(in erlang::pid a, out erlang::pid a1);
+ erlang::port port_test(in erlang::port a, out erlang::port a1);
+ erlang::ref ref_test(in erlang::ref a, out erlang::ref a1);
+ erlang::term term_test(in erlang::term a, out erlang::term a1);
+
+ // typedef test
+ long typedef_test(in apa a, in banan b, out apa a1, out banan b1);
+
+ // inlined seq. test
+ s inline_sequence_test(in s a, out s a1);
+
+ // term seq. test
+ etseq term_sequence_test(in etseq a, out etseq a1);
+ // term struct test
+ et term_struct_test(in et a, out et a1);
+
+ };
+
+};
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_client.erl b/lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_client.erl
new file mode 100644
index 0000000000..f204896aee
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/erl_client.erl
@@ -0,0 +1,331 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+-module(erl_client).
+
+-export([void_test/2, long_test/2, longlong_test/2, ushort_test/2,
+ ulong_test/2, ulonglong_test/2, double_test/2, char_test/2,
+ wchar_test/2, octet_test/2, bool_test/2, struct_test/2,
+ struct2_test/2, seq1_test/2, seq2_test/2, seq3_test/2,
+ seq4_test/2, seq5_test/2, array1_test/2, array2_test/2,
+ enum_test/2, string1_test/2, wstring1_test/2, string2_test/2,
+ string3_test/2, string4_test/2, pid_test/2, port_test/2,
+ ref_test/2, term_test/2, typedef_test/2,
+ inline_sequence_test/2, term_sequence_test/2,
+ term_struct_test/2
+
+]).
+
+-include("m.hrl").
+-include("m_i.hrl").
+-include("oe_erl_c_test.hrl").
+
+%%b
+void_test(Node, Timeout) ->
+ Ret = m_i:void_test({olsson, Node}, Timeout),
+ Ret == void. % XXX Not documented
+%%e
+
+%%b
+long_test(Node, Timeout) ->
+ In = max_long(),
+ {Ret, Out} = m_i:long_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+longlong_test(Node, Timeout) ->
+ In = 65537,
+ {Ret, Out} = m_i:longlong_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ushort_test(Node, Timeout) ->
+ In = max_ushort(),
+ {Ret, Out} = m_i:ushort_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ulong_test(Node, Timeout) ->
+ In = max_ulong(),
+ {Ret, Out} = m_i:ulong_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ulonglong_test(Node, Timeout) ->
+ In = 65537,
+ {Ret, Out} = m_i:ulonglong_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+double_test(Node, Timeout) ->
+ In = 37768.93,
+ {Ret, Out} = m_i:double_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+char_test(Node, Timeout) ->
+ In = 80,
+ {Ret, Out} = m_i:char_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+wchar_test(Node, Timeout) ->
+ In = 4097,
+ {Ret, Out} = m_i:wchar_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+octet_test(Node, Timeout) ->
+ In = 255,
+ {Ret, Out} = m_i:octet_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+bool_test(Node, Timeout) ->
+ In = false,
+ {Ret, Out} = m_i:bool_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+struct_test(Node, Timeout) ->
+ In = #m_b{l = max_long(), c = $a},
+ {Ret, Out} = m_i:struct_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+struct2_test(Node, Timeout) ->
+ In = #m_es{ f = banana, l = max_long()},
+ {Ret, Out} = m_i:struct2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq1_test(Node, Timeout) ->
+ B1 = #m_b{l = max_long(), c = $a},
+ B2 = #m_b{l = min_long(), c = $b},
+ In = [B1, B2],
+ {Ret, Out} = m_i:seq1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq2_test(Node, Timeout) ->
+ B = #m_b{l = max_long(), c = $a},
+ A = #m_a{l = min_long(), y = [B, B], d = 4711.31},
+ In = [A, A, A],
+ {Ret, Out} = m_i:seq2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq3_test(Node, Timeout) ->
+ In = [max_long(), min_long(), max_long()],
+ {Ret, Out} = m_i:seq3_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq4_test(Node, Timeout) ->
+ In = [["hej", "hopp"], ["ditt", "feta", "nylle"]],
+ {Ret, Out} = m_i:seq4_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+seq5_test(Node, Timeout) ->
+ Arr3 = mk_array(3, max_long()),
+ In = [[Arr3, Arr3], [Arr3, Arr3, Arr3]],
+ {Ret, Out} = m_i:seq5_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+array1_test(Node, Timeout) ->
+ In = mk_array(500, min_long()),
+ {Ret, Out} = m_i:array1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+array2_test(Node, Timeout) ->
+ In = mk_array(2, mk_array(3, min_long())),
+ {Ret, Out} = m_i:array2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+enum_test(Node, Timeout) ->
+ In = banana,
+ {Ret, Out} = m_i:enum_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string1_test(Node, Timeout) ->
+ In = "Die Paula muss beim Tango immer weinen",
+ {Ret, Out} = m_i:string1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+wstring1_test(Node, Timeout) ->
+ In = [1047| "ie Paula muss beim Tango immer weinen"],
+ {Ret, Out} = m_i:wstring1_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string2_test(Node, Timeout) ->
+ In = ["Lass doch die Blumen,", "Konrad!"],
+ {Ret, Out} = m_i:string2_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string3_test(Node, Timeout) ->
+ In = "Seeman, lass uns freuden!",
+ {Ret, Out} = m_i:string3_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+string4_test(Node, Timeout) ->
+
+ In = #m_strRec{
+ bb = true,
+ str4 = "Paula war zu Hause in ihrem Stadtchen als die beste Tanzerin"
+ "bekannt",
+ str7 = mk_array(3, mk_array(2, max_long())),
+ str5 = [$a, $b, $c, $d, $e, $f],
+ str6 = "123456789012",
+ str8 = {$x, $y, $x},
+ str9 = "123456789012",
+ str10 = [$a, $b, $c, $d, $e, $f]
+ },
+ {Ret, Out} = m_i:string4_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+pid_test(Node, Timeout) ->
+ In = self(),
+ {Ret, Out} = m_i:pid_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+port_test(Node, Timeout) ->
+ In = get(port_test_port),
+ {Ret, Out} = m_i:port_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+ref_test(Node, Timeout) ->
+ In = make_ref(),
+ {Ret, Out} = m_i:ref_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+term_test(Node, Timeout) ->
+ In = {[a, b], 17, kalle},
+ {Ret, Out} = m_i:term_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+typedef_test(Node, Timeout) ->
+ In1 = {nisse, [1, 2], olsson},
+ In2 = get(port_test_port),
+ {Ret, Out1, Out2} = m_i:typedef_test({olsson, Node}, Timeout, In1, In2),
+ %% XXX Should check that Ret is an integer.
+ (Out1 == In1) and (Out2 == In2).
+%%e
+
+%%b
+inline_sequence_test(Node, Timeout) ->
+ In = #m_s{l = min_long(), sl = [max_long(), min_long()]},
+ {Ret, Out} = m_i:inline_sequence_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+term_sequence_test(Node, Timeout) ->
+ In = lists:duplicate(17, {nisse, [1, 2], {kalle, olsson}}),
+ {Ret, Out} = m_i:term_sequence_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+%%b
+term_struct_test(Node, Timeout) ->
+ In = #m_et{e = {nisse, ["abcde"], {kalle, olsson}}, l = 4711},
+ {Ret, Out} = m_i:term_struct_test({olsson, Node}, Timeout, In),
+ (Ret == In) and (Out == In).
+%%e
+
+
+%% Locals
+
+mk_array(Es) ->
+ list_to_tuple(Es).
+
+mk_array(N, E) ->
+ mk_array(lists:duplicate(N, E)).
+
+%% max_short() ->
+%% power_of_two(15) - 1.
+max_long() ->
+ power_of_two(31) - 1.
+max_longlong() ->
+ power_of_two(63) - 1.
+max_ushort() ->
+ power_of_two(16) - 1.
+max_ulong() ->
+ power_of_two(32) - 1.
+max_ulonglong() ->
+ power_of_two(64) - 1.
+
+%% min_short() ->
+%% -power_of_two(15).
+min_long() ->
+ -power_of_two(31).
+%% min_longlong() ->
+%% -power_of_two(63).
+%% min_ushort() ->
+%% 0.
+%% min_ulong() ->
+%% 0.
+%% min_ulonglong() ->
+%% 0.
+
+power_of_two(N) ->
+ round(math:pow(2, N)).
+
diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/my.c b/lib/ic/test/erl_client_c_server_proto_SUITE_data/my.c
new file mode 100644
index 0000000000..88417ef498
--- /dev/null
+++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/my.c
@@ -0,0 +1,34 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+#include "ic.h"
+#include "m_i.h"
+
+int my_prepare_request_decoding(CORBA_Environment *env)
+{
+ return oe_prepare_request_decoding(env);
+}
+
+int my_prepare_reply_encoding(CORBA_Environment *env)
+{
+ return oe_prepare_reply_encoding(env);
+}
+
+
+
diff --git a/lib/ic/test/ic.cover b/lib/ic/test/ic.cover
new file mode 100644
index 0000000000..5a679c8b6f
--- /dev/null
+++ b/lib/ic/test/ic.cover
@@ -0,0 +1,2 @@
+{incl_app,ic,details}.
+
diff --git a/lib/ic/test/ic.spec b/lib/ic/test/ic.spec
new file mode 100644
index 0000000000..22905dcee4
--- /dev/null
+++ b/lib/ic/test/ic.spec
@@ -0,0 +1 @@
+{suites,"../ic_test",all}.
diff --git a/lib/ic/test/ic.spec.vxworks b/lib/ic/test/ic.spec.vxworks
new file mode 100644
index 0000000000..b15260ab70
--- /dev/null
+++ b/lib/ic/test/ic.spec.vxworks
@@ -0,0 +1,2 @@
+{topcase, {dir, "../ic_test"}}.
+{skip,{ic_pp_SUITE,"Uses gcc"}}.
diff --git a/lib/ic/test/ic_SUITE.erl b/lib/ic/test/ic_SUITE.erl
new file mode 100644
index 0000000000..c30d6485ad
--- /dev/null
+++ b/lib/ic/test/ic_SUITE.erl
@@ -0,0 +1,980 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : Test suite for the IDL compiler
+%%%----------------------------------------------------------------------
+
+-module(ic_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+
+
+-include_lib("orber/src/orber_ifr.hrl").
+-include_lib("orber/src/ifr_objects.hrl").
+-include_lib("orber/include/ifr_types.hrl").
+
+
+%% The type cases
+-export([ type_norm/1]).
+
+%% The syntax case
+-export([]).
+-export([syntax1/1, syntax2/1, syntax3/1, syntax4/1, syntax5/1, syntax6/1]).
+
+%% The constant cases
+-export([]).
+-export([const_norm/1, const_bad_tk/1, const_bad_type/1]).
+-export([const_bad_comb/1]).
+
+%% The union cases
+-export([]).
+-export([union_norm/1, union_type/1, union_mult_err/1, union_case_mult/1]).
+-export([union_default/1]).
+
+%% The enum cases
+-export([]).
+-export([enum_norm/1]).
+
+%% The struct cases
+-export([]).
+-export([struct_norm/1]).
+
+%% The oneway cases
+-export([]).
+-export([oneway_norm/1, oneway_raises/1, oneway_out/1, oneway_void/1, oneway_followed/1]).
+
+%% The attributes cases
+-export([]).
+-export([attr_norm/1]).
+
+%% The raises registration case
+-export([raises_reg/1]).
+
+
+%% The typeID case
+
+%% general stuff
+-export([]).
+-export([typeid/1, undef_id/1, dir/1, nasty_names/1, coss/1, mult_ids/1]).
+-export([forward/1, include/1, app_test/1]).
+
+%% inheritance stuff
+-export([ inherit_norm/1, inherit_warn/1, inherit_err/1]).
+
+%% Standard options to the ic compiler, NOTE unholy use of OutDir
+
+-define(OUT(X), filename:join([?config(priv_dir, Config), gen, to_list(X)])).
+
+
+%% Top of cases
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test, {group, const}, {group, union},
+ {group, enum}, {group, attr}, {group, type},
+ {group, struct}, {group, general}, {group, inherit},
+ {group, oneway}, {group, syntax}, raises_reg].
+
+groups() ->
+ [{const, [],
+ [const_norm, const_bad_tk, const_bad_type,
+ const_bad_comb]},
+ {union, [],
+ [union_norm, union_type, union_mult_err,
+ union_case_mult, union_default]},
+ {enum, [], [enum_norm]}, {struct, [], [struct_norm]},
+ {general, [],
+ [typeid, undef_id, mult_ids, forward, include,
+ nasty_names]},
+ {inherit, [],
+ [inherit_norm, inherit_warn, inherit_err]},
+ {oneway, [],
+ [oneway_norm, oneway_out, oneway_raises, oneway_void,
+ oneway_followed]},
+ {attr, [], [attr_norm]}, {type, [], [type_norm]},
+ {syntax, [],
+ [syntax1, syntax2, syntax3, syntax4, syntax5, syntax6]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+app_test(doc) -> [];
+app_test(suite) -> [];
+app_test(_Config) ->
+ ok=test_server:app_test(ic),
+ ok.
+
+%%---------------------------------------------------------------------
+%%
+%% Test of constant expressions.
+%%
+
+
+
+const_norm(doc) ->
+ ["Checks normal constant types and values"];
+const_norm(suite) -> [];
+const_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(const_norm),
+ File = filename:join(DataDir, c_norm),
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, const_norm_files()),
+ ok.
+
+const_bad_tk(doc) ->
+ ["Checks when the constant value doesn't match the declared type"];
+const_bad_tk(suite) -> [];
+const_bad_tk(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, c_err1),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(18, bad_tk_match, R),
+ ok.
+
+const_bad_type(doc) ->
+ ["Checks operands of ops are of correct type"];
+const_bad_type(suite) -> [];
+const_bad_type(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, c_err2),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(4, bad_type, R),
+ ok.
+
+const_bad_comb(doc) ->
+ ["Checks operands of ops are of conflicting types"];
+const_bad_comb(suite) -> [];
+const_bad_comb(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, c_err3),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(3, bad_type_combination, R),
+ ok.
+
+
+
+
+
+union_norm(doc) ->
+ ["Checks that normal union declarations works."];
+union_norm(suite) -> [];
+union_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(union_norm),
+ File = filename:join(DataDir, u_norm),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, union_norm_files()),
+ ok.
+
+
+%% Checks OTP-2007
+union_default(doc) ->
+ ["Checks that default cases are correct in type code."];
+union_default(suite) -> [];
+union_default(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(union_default),
+ File = filename:join(DataDir, u_default),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, union_default_files(), [load]),
+ TkList = i1:oe_get_interface(),
+ check_label("op0", 0, TkList),
+ check_label("op1", 1, TkList),
+ check_label("op2", 2, TkList),
+ check_label("op3", -1, TkList),
+ ok.
+
+check_label(Id, N, List) ->
+ case lists:keysearch(Id, 1, List) of
+ {value, {_, {{_, _, _, _, D, L}, _, _}}} ->
+ if D /= N ->
+ test_server:fail({bad_default_num, D, N});
+ D /= -1 ->
+ case lists:nth(D+1, L) of
+ T when element(1, T) == default ->
+ ok;
+ _Que ->
+ test_server:fail({bad_default_list, D, L})
+ end;
+ true ->
+ %% D = N = -1, just check that there is no default label
+ case lists:keysearch(default, 1, L) of
+ false ->
+ ok;
+ _ ->
+ test_server:fail({bad_default_label, D, L})
+ end
+ end;
+ _ ->
+ test_server:fail({'no_such_op!', Id, List})
+ end.
+
+union_type(doc) ->
+ ["Checks that errors are detected. Check that mismatch between case ",
+ "value and declared discriminator type is detected."];
+union_type(suite) -> [];
+union_type(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, u_type),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(28, bad_case_type, R),
+ ok.
+
+
+union_mult_err(doc) ->
+ ["Check that multiple declared declarators are caught.",
+ "Also check that if the discriminator is an enum, then the enum name",
+ "must not be used as a declarator in the union switch (declarator",
+ "as opposed to label)."];
+union_mult_err(suite) -> [];
+union_mult_err(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, u_mult),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(8, multiply_defined, R),
+ ok.
+
+%% Checking mult cases. Now check that other errors are found in the
+%% correct order XXXX
+
+
+union_case_mult(doc) ->
+ ["Check that multiply defined case labels are found and reported."];
+union_case_mult(suite) -> [];
+union_case_mult(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, u_case_mult),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(7, multiple_cases, R),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%%
+%% Enum cases
+%%
+
+
+enum_norm(doc) ->
+ ["Checks that normal enum declarations works."];
+enum_norm(suite) -> [];
+enum_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(enum_norm),
+ File = filename:join(DataDir, enum),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, enum_norm_files()),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%%
+%% Struct cases
+%%
+
+
+struct_norm(doc) ->
+ ["Checks that normal struct declarations works."];
+struct_norm(suite) -> [];
+struct_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(struct_norm),
+ File = filename:join(DataDir, struct),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, struct_norm_files()),
+ Mod = ridiculous_name_to_avoid_clash_svenne,
+ TestFile = filename:join(OutDir, Mod),
+ ?line ok = gen_struct_file(TestFile, Mod),
+ ?line ok = compile(OutDir, [Mod], [load]),
+%% ?line {ok, Mod, []} = compile:file(TestFile,
+%% [{i, OutDir}, {outdir, OutDir},
+%% return, load]),
+ ?line ok = Mod:test(),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%%
+%% General cases
+%%
+
+%% coss (add sometimes, takes 440 seconds!)
+
+typeid(doc) ->
+ ["Check that type id's are generated correctly"];
+typeid(suite) -> [];
+typeid(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(typeid),
+ File = filename:join(DataDir, typeid),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, typeid_files(), [load]),
+ ?line "IDL:I1:1.0" = 'I1':'typeID'(),
+ ?line "IDL:M1/I1:1.0" = 'M1_I1':'typeID'(),
+ ?line "IDL:M2/M1/I1:1.0" = 'M2_M1_I1':'typeID'(),
+ ?line "IDL:M3/M2/M1/I1:1.0" = 'M3_M2_M1_I1':'typeID'(),
+ ok.
+
+
+%%% This test case is removed because there's no way to test this from
+%%% an automated test suite.
+dir(doc) ->
+ ["Check that relative directories work, absolute is used in",
+ "all other cases in the suite."];
+%%% xxxxxx
+dir(suite) -> [];
+dir(Config) when is_list(Config) ->
+ok;
+dir(Config) ->
+ DataDir = ?config(data_dir, Config),
+
+ %% Needs a unique directory (any better way?)
+ OutDir = mk_unique("oe_the_dir"),
+
+ %% More unique names
+ File = filename:join(DataDir, mk_unique("oe_the_file")),
+ Const = mk_unique("oe_the_constant"),
+ Mod = list_to_atom(File),
+ Func = list_to_atom(Const),
+
+ %% Generate a unique IDL file with a single constant
+ gen_file(File, Const),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line ok = compile(OutDir, [load]),
+ ?line 19955 = Mod:Func(),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, [load]),
+ ?line 19955 = Mod:Func(),
+
+ ?line ok = ic:gen(File),
+%%% ?line ok = compile(".", [load]),
+ ok.
+
+undef_id(doc) ->
+ ["Check that various undefied id's are detected correctly"];
+undef_id(suite) -> [];
+undef_id(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, undef_id),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(16, tk_not_found, R),
+ ok.
+
+mult_ids(doc) ->
+ ["Check that multiply defined ids are caught."];
+mult_ids(suite) -> [];
+mult_ids(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, mult_ids),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(22, multiply_defined, R),
+ ok.
+
+
+nasty_names(doc) ->
+ ["Check that various nasty names can be generated.",
+ "Try to provoke name clashes and name conflicts with",
+ "Erlang and IDL"];
+nasty_names(suite) -> [];
+nasty_names(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(nasty_names),
+ File = filename:join(DataDir, nasty),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, nasty_names_files(), [load]),
+ ok.
+
+coss(doc) ->
+ ["Check that the Coss standard specification works."];
+coss(suite) -> [];
+coss(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(coss),
+ File = filename:join(DataDir, 'Coss'),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, [_W1]} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, []),
+ ok.
+
+forward(doc) ->
+ ["Check that forward declaratios work."];
+forward(suite) -> [];
+forward(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(forward),
+ File = filename:join(DataDir, forward),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, forward_files(), [load]),
+ ok.
+
+include(doc) ->
+ ["Check that various undefied id's are detected correctly"];
+include(suite) -> [];
+include(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, include),
+ ?line error = ic:gen(File, stdopts(OutDir)++[{preproc_flags,"-I" ++ DataDir}]),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[{preproc_flags,"-I" ++ DataDir},silent2]),
+ case lists:map(fun(D) ->
+ filename:rootname(filename:basename(element(3, D)))
+ end,
+ lists:sort(R)) of
+ ["include",
+ "include2",
+ "include2",
+ "include3"] ->
+ ok;
+ RRR ->
+ test_server:fail({bad_include_file, RRR})
+ end,
+ ok.
+
+
+
+
+%%--------------------------------------------------------------------
+%%
+%% Inhertit cases
+%%
+
+
+inherit_norm(doc) ->
+ ["Checks that normal inheritance works."];
+inherit_norm(suite) -> [];
+inherit_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(inherit_norm),
+ File = filename:join(DataDir, inherit),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, _Ws} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, inherit_norm_files(), [load]),
+
+ %% Now check constant values:
+ ?line 9 = m1_I1:c1(),
+
+ ?line 9 = m1_I2:c1(),
+ ?line 14 = m1_I2:c2(),
+ ?line 27 = m1_I2:c3(),
+
+ ?line 50 = m1_I3:c1(),
+ ?line 14 = m1_I3:c2(),
+ ?line 27 = m1_I3:c3(),
+ ?line 91 = m1_I3:c4(),
+ ?line 100 = m1_I3:c5(),
+ ok.
+
+inherit_warn(doc) ->
+ ["Check that various inheritance shadowing is detected"];
+inherit_warn(suite) -> [];
+inherit_warn(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, inherit_warn),
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(7, inherit_name_shadow, R),
+ ok.
+
+inherit_err(doc) ->
+ ["Check that various inheritance errors is detected"];
+inherit_err(suite) -> [];
+inherit_err(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, inherit_err),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, _Ws, R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(21, inherit_name_collision, R),
+ ok.
+
+
+
+oneway_norm(doc) ->
+ ["Checks that normal oneway operations works."];
+oneway_norm(suite) -> [];
+oneway_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(oneway_norm),
+ File = filename:join(DataDir, one),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line ok = compile(OutDir, oneway_norm_files(), [load]),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, oneway_norm_files(), [load]),
+ ok.
+
+oneway_void(doc) ->
+ ["Check that non-void oneways are detected."];
+oneway_void(suite) -> [];
+oneway_void(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, one_void),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(2, bad_oneway_type, R),
+ ok.
+
+oneway_raises(doc) ->
+ ["Check that oneways cannot raise exceptions."];
+oneway_raises(suite) -> [];
+oneway_raises(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, one_raises),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(3, oneway_raises, R),
+ ok.
+
+oneway_out(doc) ->
+ ["Check that illegal out parameters are detected"];
+oneway_out(suite) -> [];
+oneway_out(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, one_out),
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(2, oneway_outparams, R),
+ ok.
+
+oneway_followed(doc) ->
+ ["Checks that normal oneways, followed by other operations."];
+oneway_followed(suite) -> [];
+oneway_followed(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(oneway_followed),
+ File = filename:join(DataDir, one_followed),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line ok = compile(OutDir, oneway_followed_files(), [load]),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, oneway_followed_files(), [load]),
+ ok.
+
+
+attr_norm(doc) ->
+ ["Checks that normal attr operations works."];
+attr_norm(suite) -> [];
+attr_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(attr_norm),
+ File = filename:join(DataDir, attr),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line ok = compile(OutDir, attr_norm_files(), [load]),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, attr_norm_files(), [load]),
+ ok.
+
+
+type_norm(doc) ->
+ ["Checks all types are handled."];
+type_norm(suite) -> [];
+type_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(type_norm),
+ File = filename:join(DataDir, type),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line ok = compile(OutDir, type_norm_files(), [load]),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, type_norm_files(), [load]),
+ ok.
+
+
+
+syntax1(suite) -> [];
+syntax1(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, syntax1),
+
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(1, parse_error, R),
+ ok.
+
+syntax2(suite) -> [];
+syntax2(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, syntax2),
+
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(1, parse_error, R),
+ ok.
+
+syntax3(suite) -> [];
+syntax3(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, syntax3),
+
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(1, parse_error, R),
+ ok.
+
+syntax4(suite) -> [];
+syntax4(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, syntax4),
+
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(1, parse_error, R),
+ ok.
+
+syntax5(suite) -> [];
+syntax5(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, syntax5),
+
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(1, parse_error, R),
+ ok.
+
+syntax6(suite) -> [];
+syntax6(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, syntax6),
+
+ ?line error = ic:gen(File, stdopts(OutDir)),
+ ?line {error, [], R} =
+ ic:gen(File, stdopts(OutDir)++[silent2]),
+ check_errors(1, parse_error, R),
+ ok.
+
+
+
+%%--------------------------------------------------------------------
+%%
+%% Checks RAISES to be registered under IFR operation registration
+%% ( OTP-2102 )
+%%
+
+raises_reg(doc) ->
+ ["Check that exceptions are really registered to operations."];
+raises_reg(suite) -> [];
+raises_reg(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(raises_reg_check),
+ File = filename:join(DataDir, raises_reg),
+
+ ?line ok = ic:gen(File, stdopts(OutDir)),
+ ?line {ok, []} = ic:gen(File, stdopts(OutDir)++[silent2]),
+ ?line ok = compile(OutDir, raises_reg_files(), [load]),
+
+ set_up('oe_raises_reg'),
+
+ io:format("~n##### Starting the test case #####~n"),
+ io:format("Checking for existance of exception : ~s~n",["IDL:Raises_RegModule/Exception_1:1.0"]),
+ raises_register_check("IDL:Raises_RegModule/R_R/op:1.0","IDL:Raises_RegModule/Exception_1:1.0"),
+
+ io:format("Checking for existance of exception : ~s~n",["IDL:Raises_RegModule/Exception_2:1.0"]),
+ raises_register_check("IDL:Raises_RegModule/R_R/op:1.0","IDL:Raises_RegModule/Exception_2:1.0"),
+
+ io:format("Checking for existance of exception : ~s~n",["IDL:Raises_RegModule/XXXXXXXX:1.0"]),
+ raises_register_check("IDL:Raises_RegModule/R_R/op:1.0","IDL:RaisesModule/XXXXXXXX:1.0"),
+
+ set_down('oe_raises_reg'),
+
+ ok.
+
+set_up(Register) ->
+ io:format("Setting up.....~n"),
+ mnesia:stop(),
+ mnesia:delete_schema([node()]),
+ mnesia:create_schema([node()]),
+ mnesia:start(),
+ orber:install([node()]),
+ orber:start(),
+ io:format("Running OE_register()~n"),
+ Register:'oe_register'().
+
+set_down(Register) ->
+ io:format("Running OE_unregister()~n"),
+ Register:'oe_unregister'(),
+ io:format("Setting down.....~n"),
+ orber:stop(),
+ orber:uninstall(),
+ mnesia:stop(),
+ mnesia:delete_schema([node()]).
+
+
+raises_register_check(OpId,ExcId) ->
+ case is_valid_exc(OpId,ExcId) of
+ true ->
+ ok; % Because right exception where found,
+ % the test succeeds for normal cases.
+ false ->
+ ok; % Because the exception tested, is not
+ % registered for that operation.
+ FailReason ->
+ test_server:fail({FailReason, OpId, ExcId})
+ % Because the test descovered errors in a previous
+ % stage, or no exceptions where registered att all.
+ % ( This testcase assumes that operations to be
+ % checked allways raise excption(s) )
+ end.
+
+is_valid_exc(OpId,ExcId) ->
+ OE_IFR = orber_ifr:find_repository(),
+ OpDef = orber_ifr:'Repository_lookup_id'(OE_IFR,OpId),
+ ExcDefList = orber_ifr:get_exceptions(OpDef),
+ case ExcDefList of
+ [] ->
+ no_exceptions_registered;
+ _ ->
+ ExcDef=orber_ifr:lookup_id(OE_IFR,ExcId),
+ lists:member(ExcDef,ExcDefList)
+ end.
+
+%%--------------------------------------------------------------------
+%%
+%% Utilities
+
+
+stdopts(OutDir) ->
+ [{outdir, OutDir},{maxerrs, infinity}].
+
+mk_unique(Prefix) ->
+ {A,B,C} = now(),
+ Prefix++"_"++integer_to_list(A)++"_"++integer_to_list(B)++"_"++
+ integer_to_list(C).
+
+gen_file(File, Const) ->
+ {ok, Fd} = file:open(File++".idl", [write]),
+ io:format(Fd, "interface ~s {~n", [File]),
+ io:format(Fd, " const long ~s = 19955;~n", [Const]),
+ io:format(Fd, "};~n", []),
+ file:close(Fd).
+
+
+%% Compile all files in Dir. Used for checking that valid Erlang has
+%% been generated.
+%%compile(Dir) ->
+%% compile(Dir, []).
+%%compile(Dir, Opts) ->
+%% {ok, Cwd} = file:get_cwd(),
+%% catch do_compile(Dir, Opts),
+%% file:set_cwd(Cwd).
+
+%%do_compile(Dir, Opts) ->
+%% ok = file:set_cwd(Dir),
+%% up_to_date = ts_make_erl:all(Opts),
+%% ok.
+
+compile(Dir, Files) ->
+ compile(Dir, Files, []).
+
+compile(Dir, Files, Opts) ->
+ {ok, Cwd} = file:get_cwd(),
+ file:set_cwd(Dir),
+ io:format("Changing to ~p~n", [Dir]),
+ case catch do_compile(Files, Opts) of
+ ok ->
+ file:set_cwd(Cwd);
+ Err ->
+ file:set_cwd(Cwd),
+ test_server:fail(Err)
+ end.
+
+do_compile([], _Opts) -> ok;
+do_compile([F | Fs], Opts) ->
+ io:format("Compiling ~p", [F]),
+ case compile:file(F, Opts) of
+ ok ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ {ok, _} ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ {ok, _, _} ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ Err ->
+ io:format(" error: ~p~n", [Err]),
+ Err
+ end.
+
+do_load(File, Opts) ->
+ case lists:member(load, Opts) of
+ true ->
+ io:format("Loading file ~p", [File]),
+ code:purge(File),
+ R = code:load_abs(File),
+ io:format("Loaded: ~p", [R]);
+ false ->
+ ok
+ end.
+
+
+%% Check that ErrList consists of exactly Num errors of type ErrType
+check_errors(Num, ErrType, ErrList) ->
+ Num = length(ErrList),
+ lists:foreach(fun(T) ->
+ case catch element(1, element(4, T)) of
+ ErrType -> ok;
+ Else ->
+ test_server:fail({bad, ErrType, Else})
+ end end, ErrList).
+
+to_list(X) when is_atom(X) -> atom_to_list(X);
+to_list(X) -> X.
+
+
+%% File must be an atom
+gen_struct_file(File, Mod) ->
+
+ ?line {ok, Fd} = file:open(to_list(File)++".erl", [write]),
+ io:format(Fd, "~n", []),
+ io:format(Fd, "-module(~p).~n", [Mod]),
+ io:format(Fd, "-export([test/0]).~n", []),
+ io:format(Fd, "-include(\"oe_struct.hrl\").~n", []),
+ io:format(Fd, "test() ->~n", []),
+ io:format(Fd, " A = #'S1'{a=99, b=$a, s=\"123456789\"},~n", []),
+ io:format(Fd, " B = #'S2'{a=9, b=#'S2_S3'{a=1, b=9, b1=5, c=$2},~n", []),
+ io:format(Fd, " c=[#'S1'{a=1}, #'S1'{a=2}],~n", []),
+ io:format(Fd,
+" c2=[#'S1'{a=2}, #'S1'{a=3}, #'S1'{a=2}, #'S1'{a=3}]},~n", []),
+ io:format(Fd, " C = #'S2_S3'{a=11, b=999, b1=19},~n", []),
+ io:format(Fd, " D = #s4{a=7},~n", []),
+ io:format(Fd, " E = {1, #'U1_S5'{a=3}},~n", []),
+ io:format(Fd, " F = {2, {$b, #'U1_U2_s6'{a=6, b=false}}},~n", []),
+ io:format(Fd, " ok.~n", []),
+ file:close(Fd).
+
+
+union_norm_files() -> ['oe_u_norm'].
+union_default_files() -> ['oe_u_default', i1].
+
+typeid_files() -> ['oe_typeid', 'M3_M2_M1_I1', 'M2_M1_I1', 'M1_I1', 'I1'].
+
+struct_norm_files() -> ['oe_struct'].
+oneway_norm_files() -> ['oe_one', 'I1'].
+oneway_followed_files() -> ['oe_one_followed', 'I1'].
+nasty_names_files() -> ['oe_nasty', 'I2', 'I1'].
+
+inherit_norm_files() -> [m1_I3, m1_I2, m1_I1, 'oe_inherit', 'I4', 'I3',
+ 'I2', 'I1'].
+
+forward_files() -> [i1, 'oe_forward'].
+enum_norm_files() -> ['oe_enum'].
+const_norm_files() -> ['oe_c_norm'].
+attr_norm_files() -> ['oe_attr', 'I1', 'I2'].
+type_norm_files() -> ['oe_type'].
+
+raises_reg_files() -> ['oe_raises_reg'].
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/Corba.idl b/lib/ic/test/ic_SUITE_data/Corba.idl
new file mode 100644
index 0000000000..6b81132500
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/Corba.idl
@@ -0,0 +1,1013 @@
+// This file contains OMG IDL from CORBA V2.0, July 1995.
+// Includes IDL for CORBA Core
+// (Interface Repository, ORB Interface, Basic Object Adapter Interface)
+// and CORBA Interoperability (IOP, GIOP, IIOP, and DCE CIOP modules)
+
+// Complete OMG IDL for Interface Repository starts on pg 6-42, CORBA V2.0 July 1995
+// IRObject interface described on pg 6-9 CORBA V2.0, July 1995
+// Contained interface: pg 6-11 CORBA V2, 7-95
+// Container interface: pg 6-12 thru 6-15 CORBA V2, 7-95
+// IDLType interface: pg 6-15 CORBA V2, 7-95
+// Repository interface: pg 6-16 CORBA V2, 7-95
+// ModuleDef interface: pg 6-17 CORBA V2, 7-95
+// ConstantDef interface: pg 6-18 CORBA V2, 7-95
+// TypeDef interface: pg 6-19 CORBA V2, 7-95
+// StructDef interface: pg 6-19 CORBA V2, 7-95
+// UnionDef interface: pg 6-19 CORBA V2, 7-95
+// EnumDef interface: pg 6-20 CORBA V2, 7-95
+// AliasDef interface: pg 6-21 CORBA V2, 7-95
+// PrimitiveDef interface: pg 6-21 CORBA V2, 7-95
+// StringDef interface: pg 6-22 CORBA V2, 7-95
+// SequenceDef interface: pg 6-22 CORBA V2, 7-95
+// ArrayDef interface: pg 6-23 CORBA V2, 7-95
+// ExceptionDef interface: pg 6-24 CORBA V2, 7-95
+// AttributeDef interface: pg 6-25 CORBA V2, 7-95
+// OperationDef interface: pg 6-26 CORBA V2, 7-95
+// InterfaceDef interface: pg 6-28 CORBA V2, 7-95
+// TypeCode interface (PIDL): pg 6-34 CORBA V2, 7-95
+// ORB interface: pg 6-40 CORBA V2, 7-95
+
+#ifndef __CORBA_IDL
+#define __CORBA_IDL
+
+// #pragma prefix "omg.org"
+module CORBA {
+
+ interface TypeCode;
+ typedef string Identifier;
+ typedef string ScopedName;
+ typedef string RepositoryId;
+
+ /*
+ * start of section added by Christian Blum
+ */
+
+ typedef enum new_type {NO,USER,SYSTEM_EXCEPTION} exception_type;
+
+ /**
+ * no definition for this type
+ */
+ interface ImplementationDef
+ {
+ };
+
+ /**
+ * no definition for this type
+ */
+ //interface Principal
+ struct Principal
+ {
+ string str;
+ };
+
+ /**
+ * no definition for this type
+ */
+ interface Environment
+ {
+
+ };
+
+ typedef unsigned long Flags;
+ typedef unsigned long Status;
+
+ struct NamedValue // PIDL
+ {
+ Identifier name; // argument name
+ any argument; // argument
+ long len; // length/count of argument value
+ Flags arg_modes; // argument mode flags
+
+ };
+
+ typedef sequence<NamedValue> NVList; /* C */
+
+ interface Request // PIDL
+ {
+
+ Status add_arg (
+ in Identifier name, // argument name
+ in TypeCode arg_type, // argument datatype
+ // in void * value, // argument value to be added
+ in any value_LOOK_AT_SOURCE, // changed by blum
+ in long len, // length/count of argument value
+ in Flags arg_flags // argument flags
+ );
+
+ Status invoke (
+ in Flags invoke_flags // invocation flags
+ );
+
+ Status delete ();
+ Status send (
+ in Flags invoke_flags // invocation flags
+ );
+
+ Status get_response (
+ in Flags response_flags // response flags
+ );
+
+ };
+
+
+ interface Context // PIDL
+ {
+
+ Status set_one_value (
+ in Identifier prop_name, // property name to add
+ in string value // property value to add
+ );
+
+ Status set_values (
+ in NVList values // property values to be changed
+ );
+
+ Status get_values (
+ in Identifier start_scope, // search scope
+ in Flags op_flags, // operation flags
+ in Identifier prop_name, // name of property(s) to retrieve
+ out NVList values // requested property(s)
+ );
+
+ Status delete_values (
+ in Identifier prop_name // name of property(s) to delete
+ );
+
+ Status create_child (
+ in Identifier ctx_name, // name of context object
+ out Context child_ctx // newly created context object
+ );
+
+ Status delete (
+ in Flags del_flags // flags controlling deletion
+ );
+
+ };
+
+ /*
+ * end of section added by Christian Blum
+ */
+
+
+ enum DefinitionKind {
+ dk_none, dk_all,
+ dk_Attribute, dk_Constant, dk_Exception, dk_Interface,
+ dk_Module, dk_Operation, dk_Typedef,
+ dk_Alias, dk_Struct, dk_Union, dk_Enum,
+ dk_Primitive, dk_String, dk_Sequence, dk_Array,
+ dk_Repository
+ };
+
+
+ interface IRObject {
+ // read interface
+ readonly attribute DefinitionKind def_kind;
+
+ // write interface
+ void destroy ();
+ };
+
+
+
+ typedef string VersionSpec;
+
+ interface Contained;
+ interface Repository;
+ interface Container;
+
+ interface Contained : IRObject {
+ // read/write interface
+
+ attribute RepositoryId id;
+ attribute Identifier name;
+ attribute VersionSpec version;
+
+ // read interface
+
+ readonly attribute Container defined_in;
+ readonly attribute ScopedName absolute_name;
+ readonly attribute Repository containing_repository;
+
+ struct Description {
+ DefinitionKind kind;
+ any value;
+ };
+
+ Description describe ();
+
+ // write interface
+
+ void move (
+ in Container new_container,
+ in Identifier new_name,
+ in VersionSpec new_version
+ );
+ };
+
+
+ interface ModuleDef;
+ interface ConstantDef;
+ interface IDLType;
+ interface StructDef;
+ interface UnionDef;
+ interface EnumDef;
+ interface AliasDef;
+ interface InterfaceDef;
+ typedef sequence <InterfaceDef> InterfaceDefSeq;
+
+ typedef sequence <Contained> ContainedSeq;
+
+ struct StructMember {
+ Identifier name;
+ TypeCode type;
+ IDLType type_def;
+ };
+ typedef sequence <StructMember> StructMemberSeq;
+
+ struct UnionMember {
+ Identifier name;
+ any label;
+ TypeCode type;
+ IDLType type_def;
+ };
+ typedef sequence <UnionMember> UnionMemberSeq;
+
+ typedef sequence <Identifier> EnumMemberSeq;
+
+ interface Container : IRObject {
+ // read interface
+
+ Contained lookup ( in ScopedName search_name);
+
+ ContainedSeq contents (
+ in DefinitionKind limit_type,
+ in boolean exclude_inherited
+ );
+
+ ContainedSeq lookup_name (
+ in Identifier search_name,
+ in long levels_to_search,
+ in DefinitionKind limit_type,
+ in boolean exclude_inherited
+ );
+
+ struct Description {
+ Contained contained_object;
+ DefinitionKind kind;
+ any value;
+ };
+
+ typedef sequence<Description> DescriptionSeq;
+
+ DescriptionSeq describe_contents (
+ in DefinitionKind limit_type,
+ in boolean exclude_inherited,
+ in long max_returned_objs
+ );
+
+ // write interface
+
+ ModuleDef create_module (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version
+ );
+
+ ConstantDef create_constant (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in IDLType type,
+ in any value
+ );
+
+ StructDef create_struct (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in StructMemberSeq members
+ );
+
+ UnionDef create_union (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in IDLType discriminator_type,
+ in UnionMemberSeq members
+ );
+
+ EnumDef create_enum (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in EnumMemberSeq members
+ );
+
+ AliasDef create_alias (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in IDLType original_type
+ );
+
+ InterfaceDef create_interface (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in InterfaceDefSeq base_interfaces
+ );
+ };
+
+
+
+ interface IDLType : IRObject {
+ readonly attribute TypeCode type;
+ };
+
+
+
+ interface PrimitiveDef;
+ interface StringDef;
+ interface SequenceDef;
+ interface ArrayDef;
+
+ enum PrimitiveKind {
+ pk_null, pk_void, pk_short, pk_long, pk_ushort, pk_ulong,
+ pk_float, pk_double, pk_boolean, pk_char, pk_octet,
+ pk_any, pk_TypeCode, pk_Principal, pk_string, pk_objref
+ };
+
+ interface Repository : Container {
+ // read interface
+
+ Contained lookup_id (in RepositoryId search_id);
+
+ PrimitiveDef get_primitive (in PrimitiveKind kind);
+
+ // write interface
+
+ StringDef create_string (in unsigned long bound);
+
+ SequenceDef create_sequence (
+ in unsigned long bound,
+ in IDLType element_type
+ );
+
+ ArrayDef create_array (
+ in unsigned long length,
+ in IDLType element_type
+ );
+ };
+
+
+ interface ModuleDef : Container, Contained {
+ };
+
+ struct ModuleDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ };
+
+
+ interface ConstantDef : Contained {
+ readonly attribute TypeCode type;
+ attribute IDLType type_def;
+ attribute any value;
+ };
+
+ struct ConstantDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ TypeCode type;
+ any value;
+ };
+
+
+ interface TypedefDef : Contained, IDLType {
+ };
+
+ struct TypeDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ TypeCode type;
+ };
+
+
+ interface StructDef : TypedefDef {
+ attribute StructMemberSeq members;
+ };
+
+
+ interface UnionDef : TypedefDef {
+ readonly attribute TypeCode discriminator_type;
+ attribute IDLType discriminator_type_def;
+ attribute UnionMemberSeq members;
+ };
+
+
+ interface EnumDef : TypedefDef {
+ attribute EnumMemberSeq members;
+ };
+
+
+ interface AliasDef : TypedefDef {
+ attribute IDLType original_type_def;
+ };
+
+
+ interface PrimitiveDef: IDLType {
+ readonly attribute PrimitiveKind kind;
+ };
+
+
+ interface StringDef : IDLType {
+ attribute unsigned long bound;
+ };
+
+
+ interface SequenceDef : IDLType {
+ attribute unsigned long bound;
+ readonly attribute TypeCode element_type;
+ attribute IDLType element_type_def;
+ };
+
+ interface ArrayDef : IDLType {
+ attribute unsigned long length;
+ readonly attribute TypeCode element_type;
+ attribute IDLType element_type_def;
+ };
+
+
+ interface ExceptionDef : Contained {
+ readonly attribute TypeCode type;
+ attribute StructMemberSeq members;
+ };
+ struct ExceptionDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ TypeCode type;
+ };
+
+
+
+ enum AttributeMode {ATTR_NORMAL, ATTR_READONLY};
+
+ interface AttributeDef : Contained {
+ readonly attribute TypeCode type;
+ attribute IDLType type_def;
+ attribute AttributeMode mode;
+ };
+
+ struct AttributeDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ TypeCode type;
+ AttributeMode mode;
+ };
+
+
+
+ enum OperationMode {OP_NORMAL, OP_ONEWAY};
+
+ enum ParameterMode {PARAM_IN, PARAM_OUT, PARAM_INOUT};
+ struct ParameterDescription {
+ Identifier name;
+ TypeCode type;
+ IDLType type_def;
+ ParameterMode mode;
+ };
+ typedef sequence <ParameterDescription> ParDescriptionSeq;
+
+ typedef Identifier ContextIdentifier;
+ typedef sequence <ContextIdentifier> ContextIdSeq;
+
+ typedef sequence <ExceptionDef> ExceptionDefSeq;
+ typedef sequence <ExceptionDescription> ExcDescriptionSeq;
+
+ interface OperationDef : Contained {
+ readonly attribute TypeCode result;
+ attribute IDLType result_def;
+ attribute ParDescriptionSeq params;
+ attribute OperationMode mode;
+ attribute ContextIdSeq contexts;
+ attribute ExceptionDefSeq exceptions;
+ };
+
+ struct OperationDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ TypeCode result;
+ OperationMode mode;
+ ContextIdSeq contexts;
+ ParDescriptionSeq parameters;
+ ExcDescriptionSeq exceptions;
+ };
+
+
+
+ typedef sequence <RepositoryId> RepositoryIdSeq;
+ typedef sequence <OperationDescription> OpDescriptionSeq;
+ typedef sequence <AttributeDescription> AttrDescriptionSeq;
+
+ interface InterfaceDef : Container, Contained, IDLType {
+ // read/write interface
+
+ attribute InterfaceDefSeq base_interfaces;
+
+ // read interface
+
+ boolean is_a (in RepositoryId interface_id);
+
+ struct FullInterfaceDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ OpDescriptionSeq operations;
+ AttrDescriptionSeq attributes;
+ RepositoryIdSeq base_interfaces;
+ TypeCode type;
+ };
+
+ FullInterfaceDescription describe_interface();
+
+ // write interface
+
+ AttributeDef create_attribute (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in IDLType type,
+ in AttributeMode mode
+ );
+
+ OperationDef create_operation (
+ in RepositoryId id,
+ in Identifier name,
+ in VersionSpec version,
+ in IDLType result,
+ in OperationMode mode,
+ in ParDescriptionSeq params,
+ in ExceptionDefSeq exceptions,
+ in ContextIdSeq contexts
+ );
+ };
+
+ struct InterfaceDescription {
+ Identifier name;
+ RepositoryId id;
+ RepositoryId defined_in;
+ VersionSpec version;
+ RepositoryIdSeq base_interfaces;
+ };
+
+
+
+ enum TCKind {
+ tk_null, tk_void,
+ tk_short, tk_long, tk_ushort, tk_ulong,
+ tk_float, tk_double, tk_boolean, tk_char,
+ tk_octet, tk_any, tk_TypeCode, tk_Principal, tk_objref,
+ tk_struct, tk_union, tk_enum, tk_string,
+ tk_sequence, tk_array, tk_alias, tk_except
+ };
+
+ interface TypeCode { // PIDL
+ exception Bounds {};
+ exception BadKind {};
+
+ // for all TypeCode kinds
+ boolean equal (in TypeCode tc);
+ TCKind kind ();
+
+ // for tk_objref, tk_struct, tk_union, tk_enum, tk_alias, and tk_except
+ RepositoryId id () raises (BadKind);
+
+ // for tk_objref, tk_struct, tk_union, tk_enum, tk_alias, and tk_except
+ Identifier name () raises (BadKind);
+
+ // for tk_struct, tk_union, tk_enum, and tk_except
+ unsigned long member_count () raises (BadKind);
+ Identifier member_name (in unsigned long index) raises (BadKind, Bounds);
+
+ // for tk_struct, tk_union, and tk_except
+ TypeCode member_type (in unsigned long index) raises (BadKind, Bounds);
+
+ // for tk_union
+ any member_label (in unsigned long index) raises (BadKind, Bounds);
+ TypeCode discriminator_type () raises (BadKind);
+ long default_index () raises (BadKind);
+
+ // for tk_string, tk_sequence, and tk_array
+ unsigned long length () raises (BadKind);
+
+ // for tk_sequence, tk_array, and tk_alias
+ TypeCode content_type () raises (BadKind);
+
+ // deprecated interface
+ long param_count ();
+ any parameter (in long index) raises (Bounds);
+ };
+
+
+ /*
+ * following line added by Christian Blum
+ */
+ interface BOA;
+
+ interface ORB {
+ // other operations ...
+
+ TypeCode create_struct_tc (
+ in RepositoryId id,
+ in Identifier name,
+ in StructMemberSeq members
+ );
+
+ TypeCode create_union_tc (
+ in RepositoryId id,
+ in Identifier name,
+ in TypeCode discriminator_type,
+ in UnionMemberSeq members
+ );
+
+ TypeCode create_enum_tc (
+ in RepositoryId id,
+ in Identifier name,
+ in EnumMemberSeq members
+ );
+
+ TypeCode create_alias_tc (
+ in RepositoryId id,
+ in Identifier name,
+ in TypeCode original_type
+ );
+
+ TypeCode create_exception_tc (
+ in RepositoryId id,
+ in Identifier name,
+ in StructMemberSeq members
+ );
+
+ TypeCode create_interface_tc (
+ in RepositoryId id,
+ in Identifier name
+ );
+
+ TypeCode create_string_tc (
+ in unsigned long bound
+ );
+
+ TypeCode create_sequence_tc (
+ in unsigned long bound,
+ in TypeCode element_type
+ );
+
+ TypeCode create_recursive_sequence_tc (
+ in unsigned long bound,
+ in unsigned long offset
+ );
+
+ TypeCode create_array_tc (
+ in unsigned long length,
+ in TypeCode element_type
+ );
+
+ /*
+ * following line commented out by Christian Blum
+ */
+ // };
+
+ // The ORB interface (PIDL) is described in Chapter 7, CORBA V2.0 July 1995
+ // Object interface (object reference operations): pg 7-3 CORBA V2, 7-95
+ // ORB initialization: pg 7-7 CORBA V2, 7-95
+ // Object Adapter and Basic Object Adapter initialization: pg 7-8 CORBA V2 7-95
+ // Getting initial references: pg 7-10 CORBA V2 7-95
+ //PIDL
+
+ /*
+ * following line commented out by Christian Blum
+ */
+ //interface ORB {
+
+
+ string object_to_string (in Object obj);
+ Object string_to_object (in string str);
+
+ Status create_list (
+ in long count,
+ out NVList new_list
+ );
+ Status create_operation_list (
+ in OperationDef oper,
+ out NVList new_list
+ );
+ Status get_default_context (out Context ctx);
+
+ // Initializing the ORB
+ typedef string ORBid;
+ typedef sequence <string> arg_list;
+ ORB ORB_init (inout arg_list argv, in ORBid orb_identifier);
+
+ // Initializing an object adapter and the Basic Object Adapter
+ typedef string OAid;
+
+ // Template for OA initialization operations
+ // <OA> <OA>_init (inout arg_list argv,
+ // in OAid oa_identifier);
+
+
+
+ BOA BOA_init (inout arg_list argv,
+ in OAid boa_identifier);
+
+
+
+ // Getting initial object references
+ typedef string ObjectId;
+ typedef sequence <ObjectId> ObjectIdList;
+
+ exception InvalidName {};
+
+ ObjectIdList list_initial_services ();
+
+ Object resolve_initial_references (in ObjectId identifier)
+ raises (InvalidName);
+ };
+
+ // had to be changed..., Gerald Brose 1996
+ interface ORBject {
+
+ ImplementationDef get_implementation ();
+ InterfaceDef get_interface ();
+ boolean is_nil();
+ Object duplicate ();
+ void release ();
+ boolean is_a (in string logical_type_id);
+ boolean non_existent();
+ boolean is_equivalent (in Object other_object);
+ unsigned long hash(in unsigned long maximum);
+
+
+ Status create_request (
+ in Context ctx,
+ in Identifier operation,
+ in NVList arg_list,
+ inout NamedValue result,
+ out Request request,
+ in Flags req_flags
+ );
+ };
+
+
+ // Basic Object Adapter interface described in Chapter 8, CORBA V2.0, July 1995
+ // interface InterfaceDef; // from Interface Repository // PIDL
+ // interface ImplementationDef; // from Implementation Repository
+ // interface Object; // an object reference
+ // interface Principal; // for the authentication service
+ typedef sequence <octet, 1024> ReferenceData;
+
+ interface BOA {
+ Object create (
+ in ReferenceData id,
+ in InterfaceDef intf,
+ in ImplementationDef impl
+ );
+ void dispose (in Object obj);
+ ReferenceData get_id (in Object obj);
+
+ void change_implementation (in Object obj,
+ in ImplementationDef impl
+ );
+
+ Principal get_principal (in Object obj,
+ in Environment ev
+ );
+
+ void set_exception (in exception_type major, // NO, USER,
+ //or SYSTEM_EXCEPTION
+ in string userid, // exception type id
+ in any param_LOOK_AT_SOURCE
+ // in void *param // pointer to associated data
+ );
+
+ void impl_is_ready (in ImplementationDef impl);
+ void deactivate_impl (in ImplementationDef impl);
+ void obj_is_ready (in Object obj, in ImplementationDef impl);
+ void deactivate_obj (in Object obj);
+ };
+};
+
+// IOP module described in chap 10 CORBA V2, 7-95
+module IOP{ // IDL
+ //
+ // Standard Protocol Profile tag values
+ //
+ typedef unsigned long ProfileId;
+ const ProfileId TAG_INTERNET_IOP = 0;
+ const ProfileId TAG_MULTIPLE_COMPONENTS = 1;
+
+ struct TaggedProfile {
+ ProfileId tag;
+ sequence <octet> profile_data;
+ };
+
+ //
+ // an Interoperable Object Reference is a sequence of
+ // object-specific protocol profiles, plus a type ID.
+ //
+ struct IOR {
+ string type_id;
+ sequence <TaggedProfile> profiles;
+ };
+
+ //
+ // Standard way of representing multicomponent profiles.
+ // This would be encapsulated in a TaggedProfile.
+ //
+ typedef unsigned long ComponentId;
+ struct TaggedComponent {
+ ComponentId tag;
+ sequence <octet> component_data;
+ };
+ typedef sequence <TaggedComponent> MultipleComponentProfile;
+
+
+ typedef unsigned long ServiceID;
+
+ struct ServiceContext {
+ ServiceID context_id;
+ sequence <octet> context_data;
+ };
+ typedef sequence <ServiceContext> ServiceContextList;
+
+ const ServiceID TransactionService = 0;
+
+
+
+};
+// GIOP module described in CORBA V2, 7-95 chap 12
+// Complete IDL for GIOP module in CORBA
+// V2.0, 7-95 p 10-29
+// GIOP message header: CORBA V2, 7-95 p 12-16
+// GIOP request header: CORBA V2, 7-95 p 12-17
+// GIOP reply header: CORBA V2, 7-95 p 12-19
+// GIOP cancel request and locate request: CORBA V2, 7-95 pp 12-20 -- 12-21
+// GIOP locate reply: CORBA V2, 7-95 p 12-22
+module GIOP { // IDL
+ enum MsgType {
+ Request, Reply, CancelRequest,
+ LocateRequest, LocateReply,
+ CloseConnection, MessageError
+ };
+
+ struct Version {
+ char major;
+ char minor;
+ };
+
+ struct MessageHeader {
+ char magic [4];
+ Version GIOP_version;
+ boolean byte_order;
+ octet message_type;
+ unsigned long message_size;
+ };
+
+ struct RequestHeader {
+ ::IOP::ServiceContextList service_context;
+ unsigned long request_id;
+ boolean response_expected;
+ sequence <octet> object_key;
+ string operation;
+
+ /*
+ * ::CORBA:: added for correct scope
+ */
+ ::CORBA::Principal requesting_principal;
+ };
+
+ enum ReplyStatusType {
+ NO_EXCEPTION,
+ USER_EXCEPTION,
+ SYSTEM_EXCEPTION,
+ LOCATION_FORWARD
+ };
+
+ struct ReplyHeader {
+ ::IOP::ServiceContextList service_context;
+ unsigned long request_id;
+ ReplyStatusType reply_status;
+ };
+
+ struct CancelRequestHeader {
+ unsigned long request_id;
+ };
+
+ struct LocateRequestHeader {
+ unsigned long request_id;
+ sequence <octet> object_key;
+ };
+
+ enum LocateStatusType {
+ UNKNOWN_OBJECT,
+ OBJECT_HERE,
+ OBJECT_FORWARD
+ };
+
+ struct LocateReplyHeader {
+ unsigned long request_id;
+ LocateStatusType locate_status;
+ };
+};
+// IIOP module described in CORBA V2, 7-95 chap 12
+// Complete IDL for IIOP module: CORBA V2, 7-95 p 12-31
+module IIOP { // IDL
+ struct Version {
+ char major;
+ char minor;
+ };
+
+ struct ProfileBody {
+ Version iiop_version;
+ string host;
+ unsigned short port;
+ sequence <octet> object_key;
+ };
+};
+// DCE CIOP module described in CORBA V2, 7-95 chap 13
+// IDL for DCE CIOP module: CORBA V2, 7-95 p 13-2
+module DCE_CIOP {
+ struct InvokeRequestHeader {
+ boolean byte_order;
+ ::IOP::ServiceContextList service_context;
+ sequence <octet> object_key;
+ string endpoint_id;
+ string operation;
+ ::CORBA::Principal principal;
+ sequence <string> client_context;
+
+ // in and inout parameters follow
+ };
+ enum InvokeResponseStatus {
+ INVOKE_NO_EXCEPTION,
+ INVOKE_USER_EXCEPTION,
+ INVOKE_SYSTEM_EXCEPTION,
+ INVOKE_LOCATION_FORWARD,
+ INVOKE_TRY_AGAIN
+ };
+
+ struct InvokeResponseHeader {
+ boolean byte_order;
+ ::IOP::ServiceContextList service_context;
+ InvokeResponseStatus status;
+
+ // if status = INVOKE_NO_EXCEPTION,
+ // result then inouts and outs follow
+
+ // if status = INVOKE_USER_EXCEPTION or
+ // INVOKE_SYSTEM_EXCEPTION, an exception follows
+
+ // if status = INVOKE_LOCATION_FORWARD, an
+ // ::IOP::MultipleComponentsProfile follows
+ };
+
+ struct LocateRequestHeader {
+ boolean byte_order;
+ sequence <octet> object_key;
+ string endpoint_id;
+ string operation;
+
+ // no body follows
+ };
+
+ module IOP {
+
+ /*
+ * ::IOP:: added to get the right scope
+ */
+ const ::IOP::ComponentId TAG_OBJECT_KEY = 10;
+ const ::IOP::ComponentId TAG_ENDPOINT_ID = 11;
+ const ::IOP::ComponentId TAG_LOCATION_POLICY = 12;
+ // illegal IDL
+ /* const octet LOCATE_NEVER = 0;
+ const octet LOCATE_OBJECT = 1;
+ const octet LOCATE_OPERATION = 2;
+ const octet LOCATE_ALWAYS = 3;
+ */
+ };
+};
+
+#endif
diff --git a/lib/ic/test/ic_SUITE_data/Coss.idl b/lib/ic/test/ic_SUITE_data/Coss.idl
new file mode 100644
index 0000000000..c84d4a8247
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/Coss.idl
@@ -0,0 +1,1537 @@
+// This file contains OMG IDL and PIDL for the Common Object Services.
+// CosNaming Module, p 3-6 CORBAservices, Naming Service V1.0, 3/94
+
+// A few minor changes for the JacORB distribution:
+//
+// added am enclosing COSS module and changed scoped names accordingly
+//
+// corrected a few syntax errors
+//
+// commented out:
+// #includes
+// forward declaration of Object
+
+#include "Corba.idl"
+
+module COSS {
+
+module CosNaming {
+
+ typedef string Istring;
+ struct NameComponent {
+ Istring id;
+ Istring kind;
+ };
+
+ typedef sequence <NameComponent> Name;
+
+ enum BindingType {nobject, ncontext};
+
+ struct Binding {
+ Name binding_name;
+ BindingType binding_type;
+ };
+
+ typedef sequence <Binding> BindingList;
+ interface BindingIterator;
+
+ interface NamingContext {
+
+ enum NotFoundReason { missing_node, not_context, not_object};
+
+ exception NotFound {
+ NotFoundReason why;
+ Name rest_of_name;
+ };
+
+ exception CannotProceed {
+ NamingContext cxt;
+ Name rest_of_name;
+ };
+
+ exception InvalidName{};
+ exception AlreadyBound {};
+ exception NotEmpty{};
+
+ void bind(in Name n, in Object obj)
+ raises(NotFound, CannotProceed, InvalidName, AlreadyBound);
+ void rebind(in Name n, in Object obj)
+ raises(NotFound, CannotProceed, InvalidName);
+ void bind_context(in Name n, in NamingContext nc)
+ raises(NotFound, CannotProceed, InvalidName, AlreadyBound);
+ void rebind_context(in Name n, in NamingContext nc)
+ raises(NotFound, CannotProceed, InvalidName);
+ Object resolve (in Name n)
+ raises(NotFound, CannotProceed, InvalidName);
+ void unbind(in Name n)
+ raises(NotFound, CannotProceed, InvalidName);
+ NamingContext new_context();
+ NamingContext bind_new_context(in Name n)
+ raises(NotFound, AlreadyBound, CannotProceed, InvalidName);
+ void destroy( )
+ raises(NotEmpty);
+ void list (in unsigned long how_many,
+ out BindingList bl, out BindingIterator bi);
+ };
+
+ interface BindingIterator {
+ boolean next_one(out Binding b);
+ boolean next_n(in unsigned long how_many,
+ out BindingList bl);
+ void destroy();
+ };
+};
+
+// Names Library interface in PIDL, CORBAservices p 3- 14, Naming Service V1.0 3/94
+/*
+interface LNameComponent { // PIDL
+ exception NotSet{};
+ string get_id()
+ raises(NotSet);
+ void set_id(in string i);
+ string get_kind()
+ raises(NotSet);
+ void set_kind(in string k);
+ void destroy();
+};
+
+interface LName { // PIDL
+ exception NoComponent{};
+ exception OverFlow{};
+ exception InvalidName{};
+ LName insert_component(in unsigned long i,
+ in LNameComponent n)
+ raises(NoComponent, OverFlow);
+ LNameComponent get_component(in unsigned long i)
+ raises(NoComponent);
+ LNameComponent delete_component(in unsigned long i)
+ raises(NoComponent);
+ unsigned long num_components();
+ boolean equal(in LName ln);
+ boolean less_than(in LName ln);
+ Name to_idl_form()
+ raises(InvalidName);
+ void from_idl_form(in Name n);
+ void destroy();
+};
+
+LName create_lname(); // C/C++
+LNameComponent create_lname_component(); // C/C++
+*/
+
+// CosEventComm Module, CORBAservices p 4-8, Event Service V1.0 3/94
+
+module CosEventComm {
+
+ exception Disconnected{};
+
+ interface PushConsumer {
+ void push (in any data) raises(Disconnected);
+ void disconnect_push_consumer();
+ };
+
+ interface PushSupplier {
+ void disconnect_push_supplier();
+ };
+
+ interface PullSupplier {
+ any pull () raises(Disconnected);
+ any try_pull (out boolean has_event)
+ raises(Disconnected);
+ void disconnect_pull_supplier();
+ };
+
+ interface PullConsumer {
+ void disconnect_pull_consumer();
+ };
+
+};
+
+// CosEventChannelAdmin Module, p 4-15 CORBAservices, Event
+// Service V1.0, 3/94
+
+// #include "CosEventComm.idl"
+
+module CosEventChannelAdmin {
+
+ exception AlreadyConnected {};
+ exception TypeError {};
+
+ interface ProxyPushConsumer: ::COSS::CosEventComm::PushConsumer {
+ void connect_push_supplier(
+ in ::COSS::CosEventComm::PushSupplier push_supplier)
+ raises(AlreadyConnected);
+ };
+
+ interface ProxyPullSupplier: ::COSS::CosEventComm::PullSupplier {
+ void connect_pull_consumer(
+ in ::COSS::CosEventComm::PullConsumer pull_consumer)
+ raises(AlreadyConnected);
+ };
+
+ interface ProxyPullConsumer: ::COSS::CosEventComm::PullConsumer {
+ void connect_pull_supplier(
+ in ::COSS::CosEventComm::PullSupplier pull_supplier)
+ raises(AlreadyConnected,TypeError);
+ };
+
+ interface ProxyPushSupplier: ::COSS::CosEventComm::PushSupplier {
+ void connect_push_consumer(
+ in ::COSS::CosEventComm::PushConsumer
+ push_consumer)
+ raises(AlreadyConnected, TypeError);
+ };
+
+
+ interface ConsumerAdmin {
+ ProxyPushSupplier obtain_push_supplier();
+ ProxyPullSupplier obtain_pull_supplier();
+ };
+
+ interface SupplierAdmin {
+ ProxyPushConsumer obtain_push_consumer();
+ ProxyPullConsumer obtain_pull_consumer();
+ };
+
+ interface EventChannel {
+ ConsumerAdmin for_consumers();
+ SupplierAdmin for_suppliers();
+ void destroy();
+ };
+
+};
+
+
+// CosTyped Event Module, p 4-22 CORBAservices, Event Service
+// V1.0, 3/94
+
+// // #include "CosEventComm.idl"
+
+module CosTypedEventComm {
+
+ interface TypedPushConsumer : ::COSS::CosEventComm::PushConsumer {
+ Object get_typed_consumer();
+ };
+
+ interface TypedPullSupplier : ::COSS::CosEventComm::PullSupplier {
+ Object get_typed_supplier();
+ };
+
+};
+
+// CosTypedEventChannelAdmin Module, p 4- 25 CORBAservices,
+// Event Service V1.0, 3/94
+
+// // #include "CosEventChannel.idl"
+// // #include "CosTypedEventComm.idl"
+module CosTypedEventChannelAdmin {
+ exception InterfaceNotSupported {};
+ exception NoSuchImplementation {};
+ typedef string Key;
+
+ interface TypedProxyPushConsumer :
+ ::COSS::CosEventChannelAdmin::ProxyPushConsumer,
+ ::COSS::CosTypedEventComm::TypedPushConsumer { };
+
+ interface TypedProxyPullSupplier :
+ ::COSS::CosEventChannelAdmin::ProxyPullSupplier,
+ ::COSS::CosTypedEventComm::TypedPullSupplier { };
+
+ interface TypedSupplierAdmin :
+ ::COSS::CosEventChannelAdmin::SupplierAdmin {
+ TypedProxyPushConsumer obtain_typed_push_consumer(
+ in Key supported_interface)
+ raises(InterfaceNotSupported);
+ ::COSS::CosEventChannelAdmin::ProxyPullConsumer obtain_typed_pull_consumer (
+ in Key uses_interface)
+ raises(NoSuchImplementation);
+ };
+
+ interface TypedConsumerAdmin :
+ ::COSS::CosEventChannelAdmin::ConsumerAdmin {
+ TypedProxyPullSupplier obtain_typed_pull_supplier(
+ in Key supported_interface)
+ raises (InterfaceNotSupported);
+ ::COSS::CosEventChannelAdmin::ProxyPushSupplier obtain_typed_push_supplier(
+ in Key uses_interface)
+ raises(NoSuchImplementation);
+ };
+
+ interface TypedEventChannel {
+ TypedConsumerAdmin for_consumers();
+ TypedSupplierAdmin for_suppliers();
+ void destroy ();
+ };
+};
+
+
+// CosPersistencePID Module, p 5-20 CORBAservices,
+// Persistent Object Service V1.0, 3/94
+
+//#ifndef __COSPERSISTENCE
+//#define __COSPERSISTENCE
+
+module CosPersistencePID {
+
+ interface PID {
+ attribute string datastore_type;
+ string get_PIDString();
+ };
+};
+
+
+// CosPersistencePDS Module, p 5-20 CORBAservices,
+// Persistent Object Service V1.0, 3/94
+
+// #include "CosPersistencePID.idl"
+
+module CosPersistencePDS {
+
+// interface Object;
+ interface PDS {
+ PDS connect (in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void disconnect (in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void store (in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void restore (in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void delete (in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ };
+};
+
+
+// CosPersistencePO Module, p 5-12 CORBAservices,
+// Persistent Object Service V1.0, 3/94
+
+// // #include "CosPersistencePDS.idl"
+// CosPersistencePDS.idl
+// // #includes CosPersistencePID.idl
+
+module CosPersistencePO {
+
+ interface PO {
+ attribute ::COSS::CosPersistencePID::PID p;
+ ::COSS::CosPersistencePDS::PDS connect (
+ in ::COSS::CosPersistencePID::PID p);
+ void disconnect (in ::COSS::CosPersistencePID::PID p);
+ void store (in ::COSS::CosPersistencePID::PID p);
+ void restore (in ::COSS::CosPersistencePID::PID p);
+ void delete (in ::COSS::CosPersistencePID::PID p);
+ };
+
+ interface SD {
+ void pre_store();
+ void post_restore();
+ };
+};
+
+
+// CosPersistencePOM Module, p 5-15 CORBAservices,
+// Persistent Object Service V1.0, 3/94
+
+// #include "CosPersistencePDS.idl"
+
+// CosPersistencePDS.idl // #includes CosPersistencePID.idl
+
+module CosPersistencePOM {
+
+// interface Object;
+
+ interface POM {
+ ::COSS::CosPersistencePDS::PDS connect (
+ in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void disconnect (
+ in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void store (
+ in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void restore (
+ in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ void delete (
+ in Object obj,
+ in ::COSS::CosPersistencePID::PID p);
+ };
+ };
+
+// CosPersistencePDS_DA Module, p 5-22 CORBAservices,
+// Persistent Object Service, V1.0, 3/94
+
+// #include "CosPersistencePDS.idl"
+// CosPersistencePDS.idl // #includes CosPersistencePID.idl
+
+module CosPersistencePDS_DA {
+
+ typedef string DAObjectID;
+
+ interface PID_DA : ::COSS::CosPersistencePID::PID {
+ attribute DAObjectID oid;
+ };
+
+ interface DAObject {
+ boolean dado_same(in DAObject d);
+ DAObjectID dado_oid();
+ PID_DA dado_pid();
+ void dado_remove();
+ void dado_free();
+ };
+
+ interface DAObjectFactory {
+ DAObject create();
+ };
+
+ interface DAObjectFactoryFinder {
+ DAObjectFactory find_factory(in string key);
+ };
+
+ interface PDS_DA : ::COSS::CosPersistencePDS::PDS {
+ DAObject get_data();
+ void set_data(in DAObject new_data);
+ DAObject lookup(in DAObjectID id);
+ PID_DA get_pid();
+ PID_DA get_object_pid(in DAObject dao);
+ DAObjectFactoryFinder data_factories();
+ };
+
+ typedef sequence<string> AttributeNames;
+ interface DynamicAttributeAccess {
+ AttributeNames attribute_names();
+ any attribute_get(in string name);
+ void attribute_set(in string name, in any value);
+ };
+
+ typedef string ClusterID;
+ typedef sequence<ClusterID> ClusterIDs;
+ interface PDS_ClusteredDA : PDS_DA{
+ ClusterID cluster_id();
+ string cluster_kind();
+ ClusterIDs clusters_of();
+ PDS_ClusteredDA create_cluster(in string kind);
+ PDS_ClusteredDA open_cluster(in ClusterID cluster);
+ PDS_ClusteredDA copy_cluster(
+ in PDS_DA source);
+ };
+};
+
+// CosPersistenceDDO Module, p 5-32 CORBAservices, Persistent Object Service V1.0, 3/94
+
+// #include "CosPersistencePID.idl"
+module CosPersistenceDDO {
+
+ interface DDO {
+ attribute string object_type;
+ attribute ::COSS::CosPersistencePID::PID p;
+ short add_data();
+ short add_data_property (in short data_id);
+ short get_data_count();
+ short get_data_property_count (in short data_id);
+ void get_data_property (in short data_id,
+ in short property_id,
+ out string property_name,
+ out any property_value);
+ void set_data_property (in short data_id,
+ in short property_id,
+ in string property_name,
+ in any property_value);
+ void get_data (in short data_id,
+ out string data_name,
+ out any data_value);
+ void set_data (in short data_id,
+ in string data_name,
+ in any data_value);
+ };
+};
+
+// CosPersistenceDS_CLI module, p 5-34 CORBAservices,
+// Persistent Object Service V1.0, 3/94
+
+// #include "CosPersistenceDDO.idl"
+// CosPersistenceDDO.idl // #includes CosPersistencePID.idl
+
+module CosPersistenceDS_CLI {
+ interface UserEnvironment {
+ void set_option (in long option,in any value);
+ void get_option (in long option,out any value);
+ void release();
+ };
+
+ interface Connection {
+ void set_option (in long option,in any value);
+ void get_option (in long option,out any value);
+ };
+
+ interface ConnectionFactory {
+ Connection create_object (
+ in UserEnvironment user_envir);
+ };
+
+ interface Cursor {
+ void set_position (in long position,in any value);
+ ::COSS::CosPersistenceDDO::DDO fetch_object();
+ };
+
+ interface CursorFactory {
+ Cursor create_object (
+ in Connection connection);
+ };
+
+ interface PID_CLI : ::COSS::CosPersistencePID::PID {
+ attribute string datastore_id;
+ attribute string id;
+ };
+
+
+
+ interface Datastore_CLI {
+ void connect (in Connection connection,
+ in string datastore_id,
+ in string user_name,
+ in string authentication);
+ void disconnect (in Connection connection);
+ Connection get_connection (
+ in string datastore_id,
+ in string user_name);
+ void add_object (in Connection connection,
+ in ::COSS::CosPersistenceDDO::DDO data_obj);
+ void delete_object (
+ in Connection connection,
+ in ::COSS::CosPersistenceDDO::DDO data_obj);
+ void update_object (
+ in Connection connection,
+ in ::COSS::CosPersistenceDDO::DDO data_obj);
+ void retrieve_object(
+ in Connection connection,
+ in ::COSS::CosPersistenceDDO::DDO data_obj);
+ Cursor select_object(
+ in Connection connection,
+ in string key);
+ void transact (in UserEnvironment user_envir,
+ in short completion_type);
+ void assign_PID (in PID_CLI p);
+ void assign_PID_relative (
+ in PID_CLI source_pid,
+ in PID_CLI target_pid);
+ boolean is_identical_PID (
+ in PID_CLI pid_1,
+ in PID_CLI pid_2);
+ string get_object_type (in PID_CLI p);
+ void register_mapping_schema (in string schema_file);
+ Cursor execute (in Connection connection,
+ in string command);
+ };
+
+};
+
+
+// CosLifeCycle Module, p 6-10 CORBAservices, LifeCycle Service V1.0, 3/94
+
+// #include "Naming.idl"
+
+module CosLifeCycle
+{
+ typedef ::COSS::CosNaming::Name Key;
+ typedef Object Factory;
+ typedef sequence <Factory> Factories;
+ typedef struct NVP {
+ ::COSS::CosNaming::Istring name;
+ any value;
+ } NameValuePair;
+ typedef sequence <NameValuePair> Criteria;
+
+ exception NoFactory {
+ Key search_key;
+ };
+ exception NotCopyable { string reason; };
+ exception NotMovable { string reason; };
+ exception NotRemovable { string reason; };
+ exception InvalidCriteria{
+ Criteria invalid_criteria;
+ };
+ exception CannotMeetCriteria {
+ Criteria unmet_criteria;
+ };
+
+
+ interface FactoryFinder {
+ Factories find_factories(in Key factory_key)
+ raises(NoFactory);
+ };
+
+ interface LifeCycleObject {
+ LifeCycleObject copy(in FactoryFinder there,
+ in Criteria the_criteria)
+ raises(NoFactory, NotCopyable, InvalidCriteria,
+ CannotMeetCriteria);
+ void move(in FactoryFinder there,
+ in Criteria the_criteria)
+ raises(NoFactory, NotMovable, InvalidCriteria,
+ CannotMeetCriteria);
+ void remove()
+ raises(NotRemovable);
+ };
+
+ interface GenericFactory {
+ boolean supports(in Key k);
+ Object create_object(
+ in Key k,
+ in Criteria the_criteria)
+ raises (NoFactory, InvalidCriteria,
+ CannotMeetCriteria);
+ };
+};
+
+
+
+// LifeCycleService Module, p 6- 55 CORBAservices, Life Cycle
+// Service V1.0, 3/94
+
+// #include "LifeCycle.idl"
+
+module LifeCycleService {
+
+ typedef sequence <::COSS::CosLifeCycle::NameValuePair> PolicyList;
+ typedef sequence <::COSS::CosLifeCycle::Key> Keys;
+ typedef sequence <::COSS::CosLifeCycle::NameValuePair> PropertyList;
+ typedef sequence <::COSS::CosNaming::NameComponent> NameComponents;
+
+ interface LifeCycleServiceAdmin {
+
+ attribute PolicyList policies;
+
+ void bind_generic_factory(
+ in ::COSS::CosLifeCycle::GenericFactory gf,
+ in ::COSS::CosNaming::NameComponent name,
+ in Keys key_set,
+ in PropertyList other_properties)
+ raises (::COSS::CosNaming::NamingContext::AlreadyBound, ::COSS::CosNaming::NamingContext::InvalidName);
+
+ void unbind_generic_factory(
+ in ::COSS::CosNaming::NameComponent name)
+ raises (::COSS::CosNaming::NamingContext::NotFound, ::COSS::CosNaming::NamingContext::InvalidName);
+
+ ::COSS::CosLifeCycle::GenericFactory resolve_generic_factory(
+ in ::COSS::CosNaming::NameComponent name)
+ raises (::COSS::CosNaming::NamingContext::NotFound, ::COSS::CosNaming::NamingContext::InvalidName);
+
+ NameComponents list_generic_factories();
+
+ boolean match_service (in ::COSS::CosLifeCycle::GenericFactory f);
+
+ string get_hint();
+
+ void get_link_properties(
+ in ::COSS::CosNaming::NameComponent name,
+ out Keys key_set,
+ out PropertyList other_properties)
+ raises (::COSS::CosNaming::NamingContext::NotFound, ::COSS::CosNaming::NamingContext::InvalidName);
+ };
+};
+
+// CosTransactions Module, p 10-66
+// CORBAservices, Transaction Service V1.0, 3/94
+
+module CosTransactions {
+// DATATYPES
+enum Status {
+ StatusActive,
+ StatusMarkedRollback,
+ StatusPrepared,
+ StatusCommitted,
+ StatusRolledBack,
+ StatusUnknown,
+ StatusNoTransaction
+};
+
+enum Vote {
+ VoteCommit,
+ VoteRollback,
+ VoteReadOnly
+};
+
+// Standard exceptions
+exception TransactionRequired {};
+exception TransactionRolledBack {};
+exception InvalidTransaction {};
+
+// Heuristic exceptions
+exception HeuristicRollback {};
+exception HeuristicCommit {};
+exception HeuristicMixed {};
+exception HeuristicHazard {};
+
+// Exception from Orb operations
+exception WrongTransaction {};
+
+// Other transaction-specific exceptions
+exception SubtransactionsUnavailable {};
+exception NotSubtransaction {};
+exception Inactive {};
+exception NotPrepared {};
+exception NoTransaction {};
+exception InvalidControl {};
+exception Unavailable {};
+
+// Forward references for interfaces defined later in module
+interface Control;
+interface Terminator;
+interface Coordinator;
+interface Resource;
+interface RecoveryCoordinator;
+interface SubtransactionAwareResource;
+interface TransactionFactory;
+interface TransactionalObject;
+interface Current;
+
+// Current transaction pseudo object (PIDL)
+ interface Current {
+ void begin()
+ raises(SubtransactionsUnavailable);
+ void commit(in boolean report_heuristics)
+ raises(
+ NoTransaction,
+ HeuristicMixed,
+ HeuristicHazard
+ );
+ void rollback()
+ raises(NoTransaction);
+ void rollback_only()
+ raises(NoTransaction);
+
+ Status get_status();
+ string get_transaction_name();
+ void set_timeout(in unsigned long seconds);
+
+ Control get_control();
+ Control suspend();
+ void resume(in Control which)
+ raises(InvalidControl);
+ };
+
+ interface TransactionFactory {
+ Control create(in unsigned long time_out);
+ };
+
+ interface Control {
+ Terminator get_terminator()
+ raises(Unavailable);
+ Coordinator get_coordinator()
+ raises(Unavailable);
+ };
+
+ interface Terminator {
+ void commit(in boolean report_heuristics)
+ raises(
+ HeuristicMixed,
+ HeuristicHazard
+ );
+ void rollback();
+ };
+
+
+ interface Coordinator {
+
+ Status get_status();
+ Status get_parent_status();
+ Status get_top_level_status();
+
+ boolean is_same_transaction(in Coordinator tc);
+ boolean is_related_transaction(in Coordinator tc);
+ boolean is_ancestor_transaction(in Coordinator tc);
+ boolean is_descendant_transaction(in Coordinator tc);
+ boolean is_top_level_transaction();
+
+ unsigned long hash_transaction();
+ unsigned long hash_top_level_tran();
+
+ RecoveryCoordinator register_resource(in Resource r)
+ raises(Inactive);
+
+ void register_subtran_aware(in SubtransactionAwareResource r)
+ raises(Inactive, NotSubtransaction);
+
+ void rollback_only()
+ raises(Inactive);
+
+ string get_transaction_name();
+
+ Control create_subtransaction()
+ raises(SubtransactionsUnavailable, Inactive);
+ };
+
+ interface RecoveryCoordinator {
+ Status replay_completion(in Resource r)
+ raises(NotPrepared);
+ };
+
+}; // end module CosTransactions
+
+
+// CosConcurrency Control Module, p 7-8 CORBAservices,
+// Concurrency Control Service V1.0, 3/94
+
+// #include <CosTransactions.idl>
+module CosConcurrencyControl {
+
+ enum lock_mode {
+ read,
+ write,
+ upgrade,
+ intention_read,
+ intention_write
+ };
+
+ exception LockNotHeld{};
+
+ interface LockCoordinator
+ {
+ void drop_locks();
+ };
+
+ interface LockSet
+ {
+ void lock(in lock_mode mode);
+ boolean try_lock(in lock_mode mode);
+
+ void unlock(in lock_mode mode)
+ raises(LockNotHeld);
+ void change_mode(in lock_mode held_mode,
+ in lock_mode new_mode)
+ raises(LockNotHeld);
+ LockCoordinator get_coordinator(
+ in ::COSS::CosTransactions::Coordinator which);
+ };
+
+ interface TransactionalLockSet
+ {
+ void lock(in ::COSS::CosTransactions::Coordinator current,
+ in lock_mode mode);
+ boolean try_lock(in ::COSS::CosTransactions::Coordinator current,
+ in lock_mode mode);
+ void unlock(in ::COSS::CosTransactions::Coordinator current,
+ in lock_mode mode)
+ raises(LockNotHeld);
+ void change_mode(in ::COSS::CosTransactions::Coordinator current,
+ in lock_mode held_mode,
+ in lock_mode new_mode)
+ raises(LockNotHeld);
+ LockCoordinator get_coordinator(
+ in ::COSS::CosTransactions::Coordinator which);
+ };
+
+ interface LockSetFactory
+ {
+ LockSet create();
+ LockSet create_related(in LockSet which);
+ TransactionalLockSet create_transactional();
+ TransactionalLockSet create_transactional_related(in
+ TransactionalLockSet which);
+ };
+};
+
+// CosObjectIdentity Module, p 9-19 CORBAservices, Relationship
+// Service V1.0, 3/94
+
+
+module CosObjectIdentity {
+
+ typedef unsigned long ObjectIdentifier;
+
+ interface IdentifiableObject {
+ readonly attribute ObjectIdentifier constant_random_id;
+ boolean is_identical (
+ in IdentifiableObject other_object);
+ };
+
+};
+
+
+// CosRelationships Module, p 9-21 CORBAservices, Relationship
+// Service V1.0, 3/94
+
+// #include <ObjectIdentity.idl>
+
+module CosRelationships {
+
+ interface RoleFactory;
+ interface RelationshipFactory;
+ interface Relationship;
+ interface Role;
+ interface RelationshipIterator;
+
+ typedef Object RelatedObject;
+ typedef sequence<Role> Roles;
+ typedef string RoleName;
+ typedef sequence<RoleName> RoleNames;
+
+ struct NamedRole {RoleName name; Role aRole;};
+ typedef sequence<NamedRole> NamedRoles;
+
+ struct RelationshipHandle {
+ Relationship the_relationship;
+ ::COSS::CosObjectIdentity::ObjectIdentifier constant_random_id;
+ };
+ typedef sequence<RelationshipHandle> RelationshipHandles;
+
+ interface RelationshipFactory {
+ struct NamedRoleType {
+ RoleName name;
+ ::CORBA::InterfaceDef named_role_type;
+ };
+ typedef sequence<NamedRoleType> NamedRoleTypes;
+ readonly attribute ::CORBA::InterfaceDef relationship_type;
+ readonly attribute unsigned short degree;
+ readonly attribute NamedRoleTypes named_role_types;
+ exception RoleTypeError {NamedRoles culprits;};
+ exception MaxCardinalityExceeded {
+ NamedRoles culprits;};
+ exception DegreeError {unsigned short required_degree;};
+ exception DuplicateRoleName {NamedRoles culprits;};
+ exception UnknownRoleName {NamedRoles culprits;};
+
+ Relationship create (in NamedRoles named_roles)
+ raises (RoleTypeError,
+ MaxCardinalityExceeded,
+ DegreeError,
+ DuplicateRoleName,
+ UnknownRoleName);
+ };
+
+ interface Relationship :
+ ::COSS::CosObjectIdentity::IdentifiableObject {
+ exception CannotUnlink {
+ Roles offending_roles;
+ };
+ readonly attribute NamedRoles named_roles;
+ void destroy () raises(CannotUnlink);
+ };
+
+ interface Role {
+ exception UnknownRoleName {};
+ exception UnknownRelationship {};
+ exception RelationshipTypeError {};
+ exception CannotDestroyRelationship {
+ RelationshipHandles offenders;
+ };
+ exception ParticipatingInRelationship {
+ RelationshipHandles the_relationships;
+ };
+ readonly attribute RelatedObject related_object;
+ RelatedObject get_other_related_object (
+ in RelationshipHandle rel,
+ in RoleName target_name)
+ raises (UnknownRoleName,
+ UnknownRelationship);
+ Role get_other_role (in RelationshipHandle rel,
+ in RoleName target_name)
+ raises (UnknownRoleName, UnknownRelationship);
+ void get_relationships (
+ in unsigned long how_many,
+ out RelationshipHandles rels,
+ out RelationshipIterator iterator);
+ void destroy_relationships()
+ raises(CannotDestroyRelationship);
+ void destroy() raises(ParticipatingInRelationship);
+ boolean check_minimum_cardinality ();
+ void link (in RelationshipHandle rel,
+ in NamedRoles named_roles)
+ raises(RelationshipFactory::MaxCardinalityExceeded,
+ RelationshipTypeError);
+ void unlink (in RelationshipHandle rel)
+ raises (UnknownRelationship);
+ };
+
+ interface RoleFactory {
+ exception NilRelatedObject {};
+ exception RelatedObjectTypeError {};
+ readonly attribute ::CORBA::InterfaceDef role_type;
+ readonly attribute unsigned long max_cardinality;
+ readonly attribute unsigned long min_cardinality;
+// the following isn't allowed in IDL,
+// readonly attribute sequence <::CORBA::InterfaceDef> related_object_types;
+ typedef sequence <::CORBA::InterfaceDef> InterfaceDefSeq;
+ readonly attribute InterfaceDefSeq related_object_types;
+ Role create_role (in RelatedObject related_object)
+ raises (NilRelatedObject, RelatedObjectTypeError);
+ };
+
+ interface RelationshipIterator {
+ boolean next_one (out RelationshipHandle rel);
+ boolean next_n (in unsigned long how_many,
+ out RelationshipHandles rels);
+ void destroy ();
+ };
+
+};
+
+// CosCompoundExternalization Module, p 8-20 CORBAservices,
+// Externalization Service V1.0, 3/94
+
+// #include <Graphs.idl>
+// #include <Stream.idl>
+
+// CosGraphs Module, p 9-39 CORBAservices, Relationship Service
+// V1.0, 3/94
+
+// #include <Relationships.idl>
+// #include <ObjectIdentity.idl>
+
+module CosGraphs {
+
+ interface TraversalFactory;
+ interface Traversal;
+ interface TraversalCriteria;
+ interface Node;
+ interface NodeFactory;
+ interface Role;
+ interface EdgeIterator;
+
+ struct NodeHandle {
+ Node the_node;
+ ::COSS::CosObjectIdentity::ObjectIdentifier constant_random_id;
+ };
+ typedef sequence<NodeHandle> NodeHandles;
+
+ struct NamedRole {
+ Role the_role;
+ ::COSS::CosRelationships::RoleName the_name;
+ };
+ typedef sequence<NamedRole> NamedRoles;
+
+ struct EndPoint {
+ NodeHandle the_node;
+ NamedRole the_role;
+ };
+ typedef sequence<EndPoint> EndPoints;
+
+ struct Edge {
+ EndPoint from;
+ ::COSS::CosRelationships::RelationshipHandle the_relationship;
+ EndPoints relatives;
+ };
+ typedef sequence<Edge> Edges;
+
+ enum PropagationValue {deep, shallow, none, inhibit};
+ enum Mode {depthFirst, breadthFirst, bestFirst};
+
+ interface TraversalFactory {
+ Traversal create_traversal_on (
+ in NodeHandle root_node,
+ in TraversalCriteria the_criteria,
+ in Mode how);
+ };
+
+ interface Traversal {
+ typedef unsigned long TraversalScopedId;
+ struct ScopedEndPoint {
+ EndPoint point;
+ TraversalScopedId id;
+ };
+ typedef sequence<ScopedEndPoint> ScopedEndPoints;
+ struct ScopedRelationship {
+ ::COSS::CosRelationships::RelationshipHandle
+ scoped_relationship;
+ TraversalScopedId id;
+ };
+ struct ScopedEdge {
+ ScopedEndPoint from;
+ ScopedRelationship the_relationship;
+ ScopedEndPoints relatives;
+ };
+ typedef sequence<ScopedEdge> ScopedEdges;
+ boolean next_one (out ScopedEdge the_edge);
+ boolean next_n (in short how_many,
+ out ScopedEdges the_edges);
+ void destroy ();
+ };
+
+ interface TraversalCriteria {
+ struct WeightedEdge {
+ Edge the_edge;
+ unsigned long weight;
+ sequence<NodeHandle> next_nodes;
+ };
+ typedef sequence<WeightedEdge> WeightedEdges;
+ void visit_node(in NodeHandle a_node,
+ in Mode search_mode);
+ boolean next_one (out WeightedEdge the_edge);
+ boolean next_n (in short how_many,
+ out WeightedEdges the_edges);
+ void destroy();
+ };
+
+ interface Node: ::COSS::CosObjectIdentity::IdentifiableObject {
+ typedef sequence<Role> Roles;
+ exception NoSuchRole {};
+ exception DuplicateRoleType {};
+
+ readonly attribute ::COSS::CosRelationships::RelatedObject
+ related_object;
+ readonly attribute Roles roles_of_node;
+ Roles roles_of_type (
+ in ::CORBA::InterfaceDef role_type);
+ void add_role (in Role a_role)
+ raises (DuplicateRoleType);
+ void remove_role (in ::CORBA::InterfaceDef of_type)
+ raises (NoSuchRole);
+ };
+
+ interface NodeFactory {
+ Node create_node (in Object related_object);
+ };
+
+ interface Role : ::COSS::CosRelationships::Role {
+ void get_edges ( in long how_many,
+ out Edges the_edges,
+ out EdgeIterator the_rest);
+ };
+
+ interface EdgeIterator {
+ boolean next_one (out Edge the_edge);
+ boolean next_n ( in unsigned long how_many,
+ out Edges the_edges);
+ void destroy ();
+ };
+
+};
+
+
+
+// CosStream Module, 8-15 CORBAservices,
+// Externalization Service V1.0, 3/94
+
+// #include <LifeCycle.idl>
+// #include <ObjectIdentity.idl>
+// #include <CompoundExternalization.idl>
+module CosStream {
+ exception ObjectCreationError{};
+ exception StreamDataFormatError{};
+ interface StreamIO;
+
+ interface Streamable: ::COSS::CosObjectIdentity::IdentifiableObject
+ {
+ readonly attribute ::COSS::CosLifeCycle::Key external_form_id;
+ void externalize_to_stream(
+ in StreamIO targetStreamIO);
+ void internalize_from_stream(
+ in StreamIO sourceStreamIO,
+ in ::COSS::CosLifeCycle::FactoryFinder there)
+ raises( ::COSS::CosLifeCycle::NoFactory,
+ ObjectCreationError,
+ StreamDataFormatError );
+ };
+
+ interface StreamableFactory {
+ Streamable create_uninitialized();
+ };
+
+
+ interface StreamIO {
+ void write_string(in string aString);
+ void write_char(in char aChar);
+ void write_octet(in octet anOctet);
+ void write_unsigned_long(
+ in unsigned long anUnsignedLong);
+ void write_unsigned_short(
+ in unsigned short anUnsignedShort);
+ void write_long(in long aLong);
+ void write_short(in short aShort);
+ void write_float(in float aFloat);
+ void write_double(in double aDouble);
+ void write_boolean(in boolean aBoolean);
+ void write_object(in Streamable aStreamable);
+ // void write_graph(in ::COSS::CosCompoundExternalization::Node aNode);
+ string read_string()
+ raises(StreamDataFormatError);
+ char read_char()
+ raises(StreamDataFormatError );
+ octet read_octet()
+ raises(StreamDataFormatError );
+ unsigned long read_unsigned_long()
+ raises(StreamDataFormatError );
+ unsigned short read_unsigned_short()
+ raises( StreamDataFormatError );
+ long read_long()
+ raises(StreamDataFormatError );
+ short read_short()
+ raises(StreamDataFormatError );
+ float read_float()
+ raises(StreamDataFormatError );
+ double read_double()
+ raises(StreamDataFormatError );
+ boolean read_boolean()
+ raises(StreamDataFormatError );
+ Streamable read_object(
+ in ::COSS::CosLifeCycle::FactoryFinder there,
+ in Streamable aStreamable)
+ raises(StreamDataFormatError );
+// void read_graph(
+// in ::COSS::CosCompoundExternalization::Node starting_node,
+// in ::COSS::CosLifeCycle::FactoryFinder there)
+// raises(StreamDataFormatError );
+ };
+};
+
+module CosCompoundExternalization {
+ interface Node;
+ interface Role;
+ interface Relationship;
+ interface PropagationCriteriaFactory;
+
+ struct RelationshipHandle {
+ Relationship theRelationship;
+ ::COSS::CosObjectIdentity::ObjectIdentifier constantRandomId;
+ };
+
+ interface Node : ::COSS::CosGraphs::Node, ::COSS::CosStream::Streamable{
+ void externalize_node (in ::COSS::CosStream::StreamIO sio);
+ void internalize_node (in ::COSS::CosStream::StreamIO sio,
+ in ::COSS::CosLifeCycle::FactoryFinder there,
+ out ::COSS::CosGraphs::Node::Roles rolesOfNode)
+ raises (::COSS::CosLifeCycle::NoFactory);
+ };
+
+ interface Role : ::COSS::CosGraphs::Role {
+ void externalize_role (in ::COSS::CosStream::StreamIO sio);
+ void internalize_role (in ::COSS::CosStream::StreamIO sio);
+ ::COSS::CosGraphs::PropagationValue externalize_propagation (
+ in RelationshipHandle rel,
+ in ::COSS::CosRelationships::RoleName toRoleName,
+ out boolean sameForAll);
+ };
+
+ interface Relationship :
+ ::COSS::CosRelationships::Relationship {
+ void externalize_relationship (
+ in ::COSS::CosStream::StreamIO sio);
+ void internalize_relationship(
+ in ::COSS::CosStream::StreamIO sio,
+ in ::COSS::CosGraphs::NamedRoles newRoles);
+ ::COSS::CosGraphs::PropagationValue externalize_propagation (
+ in ::COSS::CosRelationships::RoleName fromRoleName,
+ in ::COSS::CosRelationships::RoleName toRoleName,
+ out boolean sameForAll);
+ };
+
+ interface PropagationCriteriaFactory {
+ ::COSS::CosGraphs::TraversalCriteria create_for_externalize( );
+ };
+
+};
+
+// CosExternalization Module, 8-12 CORBAservices,
+// Externalization Service V1.0, 3/94
+
+
+// #include <LifeCycle.idl>
+// #include <Stream.idl>
+module CosExternalization {
+ exception InvalidFileNameError{};
+ exception ContextAlreadyRegistered{};
+ interface Stream: ::COSS::CosLifeCycle::LifeCycleObject{
+ void externalize(
+ in ::COSS::CosStream::Streamable theObject);
+ ::COSS::CosStream::Streamable internalize(
+ in ::COSS::CosLifeCycle::FactoryFinder there)
+ raises( ::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosStream::StreamDataFormatError );
+ void begin_context()
+ raises( ContextAlreadyRegistered);
+ void end_context();
+ void flush();
+ };
+ interface StreamFactory {
+ Stream create();
+ };
+ interface FileStreamFactory {
+ Stream create(
+ in string theFileName)
+ raises( InvalidFileNameError );
+ };
+};
+
+// CosContainment Module, p 9- 48 CORBAservices, Relationship
+// Service V1.0, 3/94
+
+// #include <Graphs.idl>
+
+module CosContainment {
+
+ interface Relationship :
+ ::COSS::CosRelationships::Relationship {};
+
+ interface ContainsRole : ::COSS::CosGraphs::Role {};
+
+ interface ContainedInRole : ::COSS::CosGraphs::Role {};
+
+};
+
+// CosExternalizationContainment Module, p 8-26 CORBAservices,
+// Externalization Service V1.0, 3/94
+
+// #include <Containment.idl>
+// #include <CompoundExternalization.idl>
+
+module CosExternalizationContainment {
+
+ interface Relationship :
+ ::COSS::CosCompoundExternalization::Relationship,
+ ::COSS::CosContainment::Relationship {};
+
+ interface ContainsRole :
+ ::COSS::CosCompoundExternalization::Role,
+ ::COSS::CosContainment::ContainsRole {};
+
+ interface ContainedInRole :
+ ::COSS::CosCompoundExternalization::Role,
+ ::COSS::CosContainment::ContainedInRole {};
+};
+
+// CosReference Module, p 9-50 CORBAservices,
+// Relationship Service V1.0, 3/94
+
+// #include <Graphs.idl>
+
+module CosReference {
+
+ interface Relationship :
+ ::COSS::CosRelationships::Relationship {};
+
+ interface ReferencesRole : ::COSS::CosGraphs::Role {};
+
+ interface ReferencedByRole : ::COSS::CosGraphs::Role {};
+
+};
+
+// CosExternalizationReference Module, p 8-28 CORBAservices,
+// Externalization Service V1.0, 3/94
+
+// #include <Reference.idl>
+// #include <CompoundExternalization.idl>
+
+module CosExternalizationReference {
+
+ interface Relationship :
+ ::COSS::CosCompoundExternalization::Relationship,
+ ::COSS::CosReference::Relationship {};
+
+ interface ReferencesRole :
+ ::COSS::CosCompoundExternalization::Role,
+ ::COSS::CosReference::ReferencesRole {};
+
+ interface ReferencedByRole :
+ ::COSS::CosCompoundExternalization::Role,
+ ::COSS::CosReference::ReferencedByRole {};
+};
+
+// PIDL for CosTSInteroperation Module, p 10-59
+// CORBAservices, Transaction Service V1.0, 3/94
+module CosTSInteroperation { // PIDL
+ struct otid_t {
+ long formatID; /*format identifier. 0 is OSI TP */
+ long bequal_length;
+ sequence <octet> tid;
+ };
+ struct TransIdentity {
+ ::COSS::CosTransactions::Coordinator coordinator;
+ ::COSS::CosTransactions::Terminator terminator;
+ otid_t otid;
+ };
+ struct PropagationContext {
+ unsigned long timeout;
+ TransIdentity current;
+ sequence <TransIdentity> parents;
+ any implementation_specific_data;
+ };
+};
+
+// PIDL for CosTSPortability Module, p 10-63
+// CORBAservices, Transaction Service V1.0, 3/94
+
+module CosTSPortability { // PIDL
+ typedef long ReqId;
+
+ interface Sender {
+ void sending_request(in ReqId id,
+ out ::COSS::CosTSInteroperation::PropagationContext ctx);
+ void received_reply(in ReqId id,
+ in ::COSS::CosTSInteroperation::PropagationContext ctx,
+ in ::CORBA::Environment env);
+ };
+
+ interface Receiver {
+ void received_request(in ReqId id,
+ in ::COSS::CosTSInteroperation::PropagationContext ctx);
+ void sending_reply(in ReqId id,
+ out::COSS::CosTSInteroperation::PropagationContext ctx);
+ };
+};
+
+// CosCompoundLifeCycle Module, p 6-30 CORBAservices,
+// Life Cycle Service V1.0, 3/94
+
+// #include <LifeCycle.idl>
+// #include <Relationships.idl>
+// #include <Graphs.idl>
+
+module CosCompoundLifeCycle {
+ interface OperationsFactory;
+ interface Operations;
+ interface Node;
+ interface Role;
+ interface Relationship;
+ interface PropagationCriteriaFactory;
+
+ enum Operation {copy, move, remove};
+
+ struct RelationshipHandle {
+ Relationship the_relationship;
+ ::COSS::CosObjectIdentity::ObjectIdentifier constant_random_id;
+ };
+
+ interface OperationsFactory {
+ Operations create_compound_operations();
+ };
+
+ interface Operations {
+ Node copy (
+ in Node starting_node,
+ in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotCopyable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ void move (
+ in Node starting_node,
+ in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotMovable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ void remove (in Node starting_node)
+ raises (::COSS::CosLifeCycle::NotRemovable);
+ void destroy();
+ };
+
+ interface Node : ::COSS::CosGraphs::Node {
+ exception NotLifeCycleObject {};
+ void copy_node ( in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria,
+ out Node new_node,
+ out ::COSS::CosGraphs::Node::Roles roles_of_new_node)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotCopyable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ void move_node (in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotMovable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ void remove_node ()
+ raises (::COSS::CosLifeCycle::NotRemovable);
+ ::COSS::CosLifeCycle::LifeCycleObject get_life_cycle_object()
+ raises (NotLifeCycleObject);
+ };
+
+ interface Role : ::COSS::CosGraphs::Role {
+ Role copy_role (in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotCopyable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ void move_role (in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotMovable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ ::COSS::CosGraphs::PropagationValue life_cycle_propagation (
+ in Operation op,
+ in RelationshipHandle rel,
+ in ::COSS::CosRelationships::RoleName to_role_name,
+ out boolean same_for_all);
+ };
+
+ interface Relationship :
+ ::COSS::CosRelationships::Relationship {
+ Relationship copy_relationship (
+ in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria,
+ in ::COSS::CosGraphs::NamedRoles new_roles)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotCopyable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ void move_relationship (
+ in ::COSS::CosLifeCycle::FactoryFinder there,
+ in ::COSS::CosLifeCycle::Criteria the_criteria)
+ raises (::COSS::CosLifeCycle::NoFactory,
+ ::COSS::CosLifeCycle::NotMovable,
+ ::COSS::CosLifeCycle::InvalidCriteria,
+ ::COSS::CosLifeCycle::CannotMeetCriteria);
+ ::COSS::CosGraphs::PropagationValue life_cycle_propagation (
+ in Operation op,
+ in ::COSS::CosRelationships::RoleName from_role_name,
+ in ::COSS::CosRelationships::RoleName to_role_name,
+ out boolean same_for_all);
+ };
+
+ interface PropagationCriteriaFactory {
+ ::COSS::CosGraphs::TraversalCriteria create(in Operation op);
+ };
+
+};
+
+// CosLifeCycleContainment Module, p 6-42 CORBAservices,
+// Life Cycle Service V1.0, 3/94
+
+// #include <Containment.idl>
+// #include <CompoundLifeCycle.idl>
+
+module CosLifeCycleContainment {
+
+ interface Relationship :
+ ::COSS::CosCompoundLifeCycle::Relationship,
+ ::COSS::CosContainment::Relationship {};
+
+ interface ContainsRole :
+ ::COSS::CosCompoundLifeCycle::Role,
+ ::COSS::CosContainment::ContainsRole {};
+
+ interface ContainedInRole :
+ ::COSS::CosCompoundLifeCycle::Role,
+ ::COSS::CosContainment::ContainedInRole {};
+};
+
+// CosLifeCycleReference Module, p 6-44 CORBAservices,
+// Life Cycle Service V1.0, 3/94
+
+// #include <Reference.idl>
+// #include <CompoundLifeCycle.idl>
+
+module CosLifeCycleReference {
+
+ interface Relationship :
+ ::COSS::CosCompoundLifeCycle::Relationship,
+ ::COSS::CosReference::Relationship {};
+
+ interface ReferencesRole :
+ ::COSS::CosCompoundLifeCycle::Role,
+ ::COSS::CosReference::ReferencesRole {};
+
+ interface ReferencedByRole :
+ ::COSS::CosCompoundLifeCycle::Role,
+ ::COSS::CosReference::ReferencedByRole {};
+};
+
+
+}; // end module COSS
diff --git a/lib/ic/test/ic_SUITE_data/attr.idl b/lib/ic/test/ic_SUITE_data/attr.idl
new file mode 100644
index 0000000000..c74223eca6
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/attr.idl
@@ -0,0 +1,29 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+interface I1 {
+ attribute long a1, a2;
+ attribute char a3;
+};
+
+interface I2 : I1 {
+ attribute short a4;
+ readonly attribute char a5;
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/c_err1.idl b/lib/ic/test/ic_SUITE_data/c_err1.idl
new file mode 100644
index 0000000000..e1bc93dae8
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/c_err1.idl
@@ -0,0 +1,63 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// This file forces the bad_tk_match. This triggers when the type of
+// the expression does not match the declared type of the constant
+//
+
+const long c1 = TRUE;
+const unsigned short c1b= TRUE;
+const boolean c2 = +5;
+const long c3 = 'c';
+const float c5 = 3;
+const unsigned long c6 = -2; // Maybe not checked in compiler or suite
+
+const boolean c4 = 1 | 2;
+
+
+// Now define some correct constants for use in reference checking
+
+const long longC = -9;
+const short shortC = -9;
+const unsigned long ulongC = 1;
+const unsigned short ushortC = 0;
+
+const float floatC = 5.1;
+const double doubleC = -2.111;
+
+const boolean boolC = TRUE;
+
+const char charC = 'f';
+const string stringC = "hej";
+const string<9> stringCb = "hejdu";
+
+// Check the reference errors
+
+const long c19 = floatC;
+const short c20 = doubleC;
+const unsigned long c21 = charC;
+const unsigned short c22 = stringC;
+const float c23 = stringCb;
+const double c24 = boolC;
+const boolean c25 = longC;
+const char c26 = shortC;
+const string c27 = ushortC;
+const string<9> c28 = ulongC;
+const long c29 = 3+floatC;
diff --git a/lib/ic/test/ic_SUITE_data/c_err2.idl b/lib/ic/test/ic_SUITE_data/c_err2.idl
new file mode 100644
index 0000000000..8dac241c7f
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/c_err2.idl
@@ -0,0 +1,30 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Checks bad type of operands
+//
+
+
+const long c1 = 1 + TRUE;
+const boolean c3 = TRUE | FALSE | 19.8;
+const long c4 = 1 << TRUE;
+const long c5 = TRUE >> TRUE;
+
+
diff --git a/lib/ic/test/ic_SUITE_data/c_err3.idl b/lib/ic/test/ic_SUITE_data/c_err3.idl
new file mode 100644
index 0000000000..dde9539f6f
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/c_err3.idl
@@ -0,0 +1,28 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Checks ill-formed expressions (type conflict in operands)
+//
+
+
+const long c1 = 5|TRUE;
+const long c2 = 5&TRUE;
+const long c3 = 5^TRUE;
+
diff --git a/lib/ic/test/ic_SUITE_data/c_norm.idl b/lib/ic/test/ic_SUITE_data/c_norm.idl
new file mode 100644
index 0000000000..6f6ef8ff79
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/c_norm.idl
@@ -0,0 +1,163 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Check normal values and expressions for constants
+//
+
+// Integer types
+const long co1 = 077;
+const long ch1 = 0xf1;
+const long ch2 = 0XAB;
+const long c1 = 1;
+const short c2 = 3;
+const unsigned long c3 = 1;
+const unsigned short c4 = 3;
+
+// Unary ops
+const long c1hb = -0x1;
+const long c1b = -1;
+const short c2b = -3;
+const long c1c = +1;
+const short c2c = +3;
+// ~ not supported
+
+// Check binary ops
+const long c1d = 9+1-3;
+const long c1hd = 9+1-0xf3;
+const short c2d = 7+3;
+const short c2e = 7*3;
+const long c1e = 1 | 7;
+const long c1f = 7 & 9;
+const long c1g = (1 | 7) & 9;
+const long c1h = 1^7;
+
+//floats
+const float c5 = 1.9;
+const double c6 = 1.9;
+const float c5b = -1.9;
+const double c6b = -1.9;
+
+// Check type operand casting
+const float c5c = 1/(9+2) * 2;
+const double c6c = 1.9-1;
+//const double c6d = 1; // Does not work yet
+
+// Booleans and expressions
+const boolean c7 = TRUE;
+const boolean c7b = FALSE;
+const boolean c7c = TRUE | FALSE;
+const boolean c7d = TRUE & FALSE;
+const boolean c7e = TRUE&TRUE | FALSE&TRUE;
+const boolean c7f = TRUE&TRUE ^ FALSE&TRUE;
+
+// Character and string
+const char c8 = 'c';
+const char c8b = '\n';
+const string c9 = "hej";
+const string<9> c9b = "hejdu";
+
+
+//
+// Check that value references work
+//
+
+const long rc1 = c1g;
+const long rc1h = c1h + 9;
+const short rc2 = c2;
+const unsigned long rc3 = c3;
+const unsigned short rc4 = c4;
+
+
+const float rc5c = c5c;
+const double rc6c = c6c;
+const double rc6d = c6c+1.3;
+
+const boolean rc7 = c7;
+const boolean rc7c = c7c | TRUE;
+
+const char rc8 = c8;
+const char rc8b = c8b;
+const string rc9 = c9;
+const string<9> rc9b = c9b;
+
+
+
+
+//
+// Now check that all typerefs work
+//
+
+typedef long longT;
+typedef short shortT;
+typedef unsigned long ulongT;
+typedef unsigned short ushortT;
+
+typedef float floatT;
+typedef double doubleT;
+
+typedef char charT;
+typedef string stringT;
+
+typedef boolean booleanT;
+
+const longT cc1 = 1;
+const shortT cc2 = 3;
+const ::longT cc1b = -1;
+const ::shortT cc2b = -3;
+
+const floatT cc5 = 1.9;
+const doubleT cc6 = 1.9;
+const floatT cc5b = -1.9;
+const doubleT cc6b = -1.9;
+const floatT cc5c = 1/(9+2) * 2;
+const doubleT cc6c = 1.9-1;
+
+const booleanT cc7 = TRUE;
+const booleanT cc7b = TRUE;
+const booleanT cc7c = TRUE | FALSE;
+const booleanT cc7d = TRUE & FALSE;
+const booleanT cc7e = TRUE&TRUE | FALSE&TRUE;
+
+
+const charT cc8 = 'c';
+const charT cc8b = '\n';
+const stringT cc9 = "hej";
+const stringT cc9b = "hejdu";
+
+
+//
+// Check value casting
+//
+const long longC = -9;
+const short shortC = -9;
+const unsigned long ulongC = 1;
+const unsigned short ushortC = 0;
+
+const float floatC = 5.1;
+const double doubleC = -2.111;
+
+const long c20 = shortC;
+const long c21 = ulongC;
+const long c22 = ushortC;
+const short c23 = ushortC;
+const double c34 = floatC;
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/enum.idl b/lib/ic/test/ic_SUITE_data/enum.idl
new file mode 100644
index 0000000000..c164e4bf74
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/enum.idl
@@ -0,0 +1,32 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+
+enum E1 {kalle, sune};
+
+enum E2 { el0, el1, el2, el3, el4, el5, el6, el7, el8, el9, el10, el11, el12, el13,
+el14, el15, el16, el17, el18, el19, el20, el21, el22, el23, el24, el25, el26, el27,
+el28, el29, el30, el31, el32, el33, el34, el35, el36, el37, el38, el39, el40, el41,
+el42, el43, el44, el45, el46, el47, el48, el49, el50, el51, el52, el53, el54, el55,
+el56, el57, el58, el59};
+
+
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/forward.idl b/lib/ic/test/ic_SUITE_data/forward.idl
new file mode 100644
index 0000000000..1e16265af5
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/forward.idl
@@ -0,0 +1,34 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Check that forward declarations are handled correctly
+//
+
+
+interface i1;
+
+
+interface i1 {
+ typedef long T;
+};
+
+
+interface i1;
+
diff --git a/lib/ic/test/ic_SUITE_data/include.idl b/lib/ic/test/ic_SUITE_data/include.idl
new file mode 100644
index 0000000000..24022bfa1e
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/include.idl
@@ -0,0 +1,30 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Check that errors are given with the correct file name reference
+
+#include "include2.idl"
+
+
+typedef T1 T7;
+typedef long T7;
+typedef long T111;
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/include2.idl b/lib/ic/test/ic_SUITE_data/include2.idl
new file mode 100644
index 0000000000..2f8f7fd62c
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/include2.idl
@@ -0,0 +1,26 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Check that errors are given with the correct file name reference
+
+#include "include3.idl"
+
+
+typedef T7 T1;
+
diff --git a/lib/ic/test/ic_SUITE_data/include3.idl b/lib/ic/test/ic_SUITE_data/include3.idl
new file mode 100644
index 0000000000..c5f89c6c63
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/include3.idl
@@ -0,0 +1,25 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Check that errors are given with the correct file name reference
+
+typedef T7 T1;
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/inherit.idl b/lib/ic/test/ic_SUITE_data/inherit.idl
new file mode 100644
index 0000000000..71b79c8748
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/inherit.idl
@@ -0,0 +1,68 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+
+interface I1 {
+ typedef long T1;
+ typedef struct S1 {long a; boolean b;} T2;
+ typedef string StringT, StringT_arr[10];
+
+ T1 op1( in StringT a, inout char b, out StringT_arr c );
+ T2 op2( in char a, inout char b, out StringT_arr c );
+
+ const T1 LongC = 10;
+ const StringT StringC = "Hola bambino";
+
+};
+
+
+interface I2 : I1 {
+ T1 op3( in long a);
+
+ const long c1 = LongC;
+ const string c2 = StringC;
+};
+
+interface I3 : I1 {};
+
+interface I4 : I3, I2 {}; // Check that branced inherit works
+
+
+
+// Now use cnstants to check that inheritance works as expected
+
+module m1 {
+ interface I1 {
+ typedef long T1;
+
+ const T1 c1 = 9;
+ };
+
+ interface I2 : I1 {
+ const T1 c2 = c1+5; // c2 = 14
+ const long c3 = c2+c1+4; // c3 = 27
+ };
+
+ interface I3 : I2, I1 {
+ const long c1 = 50; // Overrides I1::c1
+ const T1 c4 = c1+c2+c3; // c4=91
+ const T1 c5 = I1::c1+c1+c2+c3; // 100
+ };
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/inherit_err.idl b/lib/ic/test/ic_SUITE_data/inherit_err.idl
new file mode 100644
index 0000000000..4cfc3ffbff
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/inherit_err.idl
@@ -0,0 +1,71 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Ops and attributes must not be redefined (shadowed)
+
+interface I1 {
+ long op1( in long a, inout char b, out boolean c );
+ long op2( in char a, inout char b, out boolean c );
+ attribute long a1, a2;
+ readonly attribute char a3;
+};
+
+interface I2 : I1 {
+ long op1( in float a, inout char b, out boolean c );
+ long op2( in char a, inout char b, out boolean c );
+ attribute long a1, a2;
+ readonly attribute char a3;
+};
+
+interface I3 : I1 {
+ long op3 (in string<19> b);
+};
+
+
+interface I4 : I3 {
+ long op1( in float a, inout char b, out boolean c );
+ long op2( in char a, inout char b, out boolean c );
+ attribute long a1, a2;
+ readonly attribute char a3;
+
+ long op3 (in string<19> b);
+};
+
+
+interface I11 {
+ long op1( in float a, inout char b, out boolean c );
+ long op2( in char a, inout char b, out boolean c );
+ attribute long a1, a2;
+ readonly attribute char a3;
+};
+
+
+
+interface I5 : I1, I11 {};
+
+interface I6 : I1 {
+ const long op1=0;
+ const long op2=0;
+ const long a1=0;
+ const long a2=0;
+ const long a3=0;
+};
+
+
diff --git a/lib/ic/test/ic_SUITE_data/inherit_warn.idl b/lib/ic/test/ic_SUITE_data/inherit_warn.idl
new file mode 100644
index 0000000000..502bfac8d4
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/inherit_warn.idl
@@ -0,0 +1,64 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Checks that shadow warnings comes out as expected
+//
+
+
+interface I1 {
+ typedef long T1;
+ typedef struct S1 {long a; boolean b;} T2;
+ typedef string StringT, StringT_arr[10];
+
+ T1 op1( in StringT a, inout char b, out StringT_arr c );
+ T2 op2( in char a, inout char b, out StringT_arr c );
+
+ const T1 LongC = 10;
+ const StringT StringC = "Hola bambino";
+
+};
+
+
+interface I2 : I1 {
+ typedef char T1; // Shadows I1::T1
+ const boolean StringC = FALSE; // shadows I1::StringC
+
+ T1 op3( in long a);
+
+ const long c1 = LongC;
+ const boolean c2 = StringC;
+};
+
+interface I3 : I2 {}; // More shadows
+
+interface I4 : I1 {
+ T1 op4();
+ const T1 c2 = 66;
+};
+
+interface I5 : I4 {
+ typedef string T1; // Shadows I1::T1
+ const char LongC = 'a'; // Shadows I1::LongC
+};
+
+
+interface I6 : I4, I3 {
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/mult_ids.idl b/lib/ic/test/ic_SUITE_data/mult_ids.idl
new file mode 100644
index 0000000000..46deaa9f55
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/mult_ids.idl
@@ -0,0 +1,92 @@
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+// Check that multiply defined identifiers are detected
+//
+
+typedef long T1;
+typedef long T1;
+typedef long T2;
+exception T2 {};
+
+
+//Exceptions
+exception Exc1 {};
+exception Exc1 {};
+
+
+// Enums
+enum E1 {kalle};
+enum E1 {kalle};
+enum E2 {kalle, sune, kalle};
+
+
+// Structs
+struct S1 {long a;};
+struct S1 {long a;};
+struct S2 {long a; short a;};
+struct S3 {long a,b; short a;};
+struct S4 {long a,a; short a;};
+
+
+// Constants
+const long c1 = 0;
+const long c1 = 0;
+
+
+// Interfaces
+
+interface i1 {};
+interface i1 {};
+
+interface i2 {
+ attribute long a1;
+ attribute long a1;
+};
+
+interface i3 {
+ attribute long a1, a2;
+ attribute long a2;
+};
+
+interface i4 {
+ attribute long a1, a1;
+};
+
+interface i5 {
+ long op1();
+ long op1();
+
+ long op2(in long a, inout char a);
+};
+
+
+// Unions
+
+union U1 switch (long) {case 1: long a;};
+union U1 switch (long) {case 1: long a;};
+
+union U2 switch (long) {
+case 1: long a;
+default: char a;
+};
+
+
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/nasty.idl b/lib/ic/test/ic_SUITE_data/nasty.idl
new file mode 100644
index 0000000000..15fd523c0f
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/nasty.idl
@@ -0,0 +1,60 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Checks nasty name collisions
+//
+
+typedef string T;
+
+
+#define nasty01 version
+#define nasty02 preproc
+#define nasty03 pragma
+#define nasty04 compile
+#define nasty05 if
+#define nasty06 receive
+#define nasty07 foldr
+#define nasty08 length
+#define nasty09 ID
+
+interface I1 {
+ attribute T nasty01;
+ attribute T nasty02;
+ attribute T nasty03;
+ attribute T nasty04;
+ attribute T nasty05;
+ attribute T nasty06;
+ attribute T nasty07;
+ attribute T nasty08;
+ attribute T nasty09;
+};
+
+interface I2 {
+ T nasty01();
+ T nasty02();
+ T nasty03();
+ T nasty04();
+ T nasty05();
+ T nasty06();
+ T nasty07();
+ T nasty08();
+ T nasty09();
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/one.idl b/lib/ic/test/ic_SUITE_data/one.idl
new file mode 100644
index 0000000000..99281d6079
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/one.idl
@@ -0,0 +1,29 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Test oneway operations
+
+interface I1 {
+ long op1(in char a, inout boolean b, out string c);
+ oneway void op2(in char a, in boolean b, in string c);
+ oneway void op3();
+};
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/one_followed.idl b/lib/ic/test/ic_SUITE_data/one_followed.idl
new file mode 100644
index 0000000000..da8ee74e25
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/one_followed.idl
@@ -0,0 +1,54 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Test oneway operations followed by other operations
+
+interface I1 {
+ oneway void op1();
+ oneway void op2(in char a, in boolean b, in string c);
+ long op3(in char a, inout boolean b, out string c);
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/one_out.idl b/lib/ic/test/ic_SUITE_data/one_out.idl
new file mode 100644
index 0000000000..65f177ff22
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/one_out.idl
@@ -0,0 +1,28 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Test oneway operations not using in out params
+
+interface I1 {
+ oneway void op1(in char a, inout boolean b, in string c);
+ oneway void op2(in char a, out boolean b, in string c);
+};
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/one_raises.idl b/lib/ic/test/ic_SUITE_data/one_raises.idl
new file mode 100644
index 0000000000..8290877363
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/one_raises.idl
@@ -0,0 +1,32 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Test oneway operations not using in out params
+
+exception hell {boolean burn; unsigned long for_how_long;};
+exception high_water {long mark;};
+
+interface I1 {
+ oneway void op1(in char a) raises (hell);
+ oneway void op2(in char a) raises (hell);
+ oneway void op3() raises (hell, high_water);
+};
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/one_void.idl b/lib/ic/test/ic_SUITE_data/one_void.idl
new file mode 100644
index 0000000000..e1d51c7abb
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/one_void.idl
@@ -0,0 +1,30 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Test oneway operations not using in out params
+
+typedef long T;
+
+interface I1 {
+ oneway char op1(in char a);
+ oneway T op2(in char a);
+};
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/raises_reg.idl b/lib/ic/test/ic_SUITE_data/raises_reg.idl
new file mode 100644
index 0000000000..d4458811dc
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/raises_reg.idl
@@ -0,0 +1,52 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#ifndef _RAISES_REG_IDL
+#define _RAISES_REG_IDL
+
+module Raises_RegModule {
+
+ exception Exception_1 {};
+
+ exception Exception_2 {};
+
+ interface R_R {
+
+ void op()
+ raises(Raises_RegModule::Exception_1,Raises_RegModule::Exception_2);
+
+ };
+
+};
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/struct.idl b/lib/ic/test/ic_SUITE_data/struct.idl
new file mode 100644
index 0000000000..337ee170e3
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/struct.idl
@@ -0,0 +1,53 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+
+struct S1 {
+ long a;
+ char b;
+ string<9> s;
+};
+
+struct S2 {
+ long a;
+ struct S3 {
+ long a;
+ short b, b1;
+ char c;
+ } b;
+ sequence <S1> c, c2, c3, c4, c5, c6, c7;
+};
+
+
+// Check that structs are detected down in other types
+
+
+typedef struct s4 {long a;} T1;
+union U1 switch (long) {
+case 1:
+ struct S5 {unsigned short a;} a;
+case 2:
+ union U2 switch (char) {
+ case 'a':
+ boolean a;
+ case 'b':
+ struct s6 {long a; boolean b;} c;
+ } b;
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/syntax1.idl b/lib/ic/test/ic_SUITE_data/syntax1.idl
new file mode 100644
index 0000000000..83c7de7943
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/syntax1.idl
@@ -0,0 +1,28 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Check syntax errors
+//
+
+
+typedef long T1 _;
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/syntax2.idl b/lib/ic/test/ic_SUITE_data/syntax2.idl
new file mode 100644
index 0000000000..10498206c1
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/syntax2.idl
@@ -0,0 +1,27 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+struct S2 {
+ long a_arr[99];
+ struct S3 {
+ long a;_arr[99]
+ boolean b_arr[99];
+ } b;
+};
+
+
diff --git a/lib/ic/test/ic_SUITE_data/syntax3.idl b/lib/ic/test/ic_SUITE_data/syntax3.idl
new file mode 100644
index 0000000000..69ab6b9783
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/syntax3.idl
@@ -0,0 +1,20 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+typdef long T1;
+
diff --git a/lib/ic/test/ic_SUITE_data/syntax4.idl b/lib/ic/test/ic_SUITE_data/syntax4.idl
new file mode 100644
index 0000000000..077a251729
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/syntax4.idl
@@ -0,0 +1,23 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+union U1 switch (long) {
+case 1: long a;
+2: short b;
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/syntax5.idl b/lib/ic/test/ic_SUITE_data/syntax5.idl
new file mode 100644
index 0000000000..10af9fc18c
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/syntax5.idl
@@ -0,0 +1,22 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+union U1 switch (enum E1 {kalle, sune}) {
+case kalle: long a;
+sune: short b;
+};
diff --git a/lib/ic/test/ic_SUITE_data/syntax6.idl b/lib/ic/test/ic_SUITE_data/syntax6.idl
new file mode 100644
index 0000000000..dc15704d94
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/syntax6.idl
@@ -0,0 +1,20 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+constant long c1 = 0;
diff --git a/lib/ic/test/ic_SUITE_data/type.idl b/lib/ic/test/ic_SUITE_data/type.idl
new file mode 100644
index 0000000000..67e1d502bd
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/type.idl
@@ -0,0 +1,190 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Check all types in IDL
+//
+
+typedef long T01;
+typedef unsigned long T02;
+typedef short T03;
+typedef unsigned short T04;
+typedef float T05;
+typedef double T06;
+typedef char T07;
+typedef boolean T08;
+typedef octet T09;
+typedef any T10;
+typedef Object T11;
+typedef T01 T12;
+
+// Template types
+typedef sequence <long> T21;
+typedef sequence <unsigned long> T22;
+typedef sequence <short, 2> T23;
+typedef sequence <unsigned short, 6> T24;
+typedef sequence <float, 12> T25;
+typedef sequence <double> T26;
+typedef sequence <char, 1> T27;
+typedef sequence <boolean> T28;
+typedef sequence <octet, 9> T29;
+typedef sequence <any> T30;
+typedef sequence <Object,2 > T31;
+typedef sequence <T01> T32;
+typedef sequence <sequence <sequence <T32> > > T33;
+
+struct S1 {
+ long a;
+ boolean b;
+};
+
+struct S2 {
+ long a;
+ struct S3 {
+ long a;
+ boolean b;
+ } b;
+};
+
+union U1 switch (enum E1 {kalle1, sune1}) {
+case kalle1: long a;
+default: boolean b;
+case sune1: octet c;
+};
+
+union U2 switch (enum E2 {kalle2, sune2}) {
+case kalle2: long a;
+default: struct S4 { long a; short b;} b;
+case sune2: octet c;
+};
+
+// Typedefs of above types
+
+typedef struct S11 {
+ long a;
+ boolean b;
+} T41;
+
+typedef struct S21 {
+ long a;
+ struct S3 {
+ long a;
+ boolean b;
+ } b;
+} T42;
+
+typedef union U11 switch (enum E3 {kalle3, sune3}) {
+case kalle3: long a;
+default: boolean b;
+case sune3: octet c;
+} T43;
+
+typedef union U21 switch (enum E4 {kalle4, sune4}) {
+case kalle4: long a;
+default: struct S4 { long a; short b;} b;
+case sune4: octet c;
+} T44;
+
+
+
+
+// Array versions
+
+typedef long T01_arr[99];
+typedef unsigned long T02_arr[99];
+typedef short T03_arr[99];
+typedef unsigned short T04_arr[99];
+typedef float T05_arr[99];
+typedef double T06_arr[99];
+typedef char T07_arr[99];
+typedef boolean T08_arr[99];
+typedef octet T09_arr[99];
+typedef any T10_arr[99];
+typedef Object T11_arr[99];
+typedef T01 T12_arr[99];
+
+typedef sequence <long> T21_arr[99];
+typedef sequence <unsigned long> T22_arr[99];
+typedef sequence <short, 2> T23_arr[99];
+typedef sequence <unsigned short, 6> T24_arr[99];
+typedef sequence <float, 12> T25_arr[99];
+typedef sequence <double> T26_arr[99];
+typedef sequence <char, 1> T27_arr[99];
+typedef sequence <boolean> T28_arr[99];
+typedef sequence <octet, 9> T29_arr[99];
+typedef sequence <any> T30_arr[99];
+typedef sequence <Object,2 > T31_arr[99];
+typedef sequence <T01> T32_arr[99];
+typedef sequence <sequence <sequence <T32> > > T33_arr[99];
+
+struct S12 {
+ long a;
+ boolean b_arr[99];
+};
+
+struct S22 {
+ long a_arr[99];
+ struct S3 {
+ long a_arr[99];
+ boolean b_arr[99];
+ } b;
+};
+
+union U12 switch (enum E12 {kalle12, sune12}) {
+case kalle12: long a_arr[99];
+default: boolean b;
+case sune12: octet c;
+};
+
+union U22 switch (enum E22 {kalle22, sune22}) {
+case kalle22: long a;
+default: struct S4 { long a; short b;} b_arr[99];
+case sune22: octet c;
+};
+
+// Typedefs of above types
+
+typedef struct S13 {
+ long a_arr[99];
+ boolean b;
+} T41_arr[99];
+
+typedef struct S23 {
+ long a;
+ struct S3 {
+ long a;
+ boolean b_arr[99];
+ char c;
+ } b;
+} T42_arr[99];
+
+typedef union U13 switch (enum E13 {kalle13, sune13}) {
+case kalle13: long a;
+default: boolean b_arr[99];
+case sune13: octet c;
+} T43_arr[99];
+
+typedef union U23 switch (enum E23 {kalle23, sune23}) {
+case kalle23: long a_arr[99];
+default: struct S4 { long a; short b;} b_arr[99];
+case sune23: octet c_arr[99];
+} T44_arr[99];
+
+
+
diff --git a/lib/ic/test/ic_SUITE_data/typeid.idl b/lib/ic/test/ic_SUITE_data/typeid.idl
new file mode 100644
index 0000000000..6e99f4a50d
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/typeid.idl
@@ -0,0 +1,28 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+interface I1 {};
+
+module M1 { interface I1 {};};
+
+module M2 { module M1 { interface I1 {};};};
+
+module M3 { module M2 { module M1 { interface I1 {};};};};
+
+
diff --git a/lib/ic/test/ic_SUITE_data/u_case_mult.idl b/lib/ic/test/ic_SUITE_data/u_case_mult.idl
new file mode 100644
index 0000000000..3c30e144d8
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/u_case_mult.idl
@@ -0,0 +1,54 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Check that case labels are not duplicated
+
+union U1 switch (long) {
+case 1 : long a;
+case 1 : short b;
+};
+
+union U2 switch (char) {
+case 'c' : long a;
+case 'c' : short b;
+};
+
+union U2b switch (char) {
+case 'c' :
+case 'c' : long a;
+case 'e': long b;
+case 'c': long c;
+};
+
+union U3 switch (enum E1 {kalle, kula}) {
+case kula : long a;
+case kula : short b;
+};
+
+union U4 switch (boolean) {
+case TRUE : long a;
+case TRUE : short b;
+};
+
+union U5 switch (boolean) {
+case TRUE : long a;
+default: short p;
+default: short pp;
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/u_default.idl b/lib/ic/test/ic_SUITE_data/u_default.idl
new file mode 100644
index 0000000000..e5d94a5e54
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/u_default.idl
@@ -0,0 +1,51 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Checking that default labels are correct in TK
+//
+
+interface i1 {
+ union U1 switch (long) {
+ default: long a;
+ case 1: case 2: long b;
+ };
+
+ union U2 switch (long) {
+ case 0: default: long a;
+ case 1: case 2: long b;
+ };
+
+ union U3 switch (long) {
+ case -1: long aa;
+ case 0: default: long a;
+ case 1: case 2: long b;
+ };
+
+ union U4 switch (long) {
+ case -1: long aa;
+ case 0: long a;
+ case 1: case 2: long b;
+ };
+
+ U1 op0();
+ U2 op1();
+ U3 op2();
+ U4 op3();
+};
diff --git a/lib/ic/test/ic_SUITE_data/u_mult.idl b/lib/ic/test/ic_SUITE_data/u_mult.idl
new file mode 100644
index 0000000000..b916861eec
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/u_mult.idl
@@ -0,0 +1,61 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+
+// Check multiply defined declarators
+
+enum E2 {kal, kula, E1}; // legal, but used below
+
+// Now check that declarator a is multiply defined in all unions below
+union U0 switch (long) {
+case 0: long a;
+case 1: short a;
+};
+union U00 switch (char) {
+case 'c' : long a;
+case 'f' : char c;
+case 'b' : short a;
+};
+union U000 switch (boolean) {
+case TRUE: long a;
+case FALSE: short a;
+};
+union U0000 switch (E2) {
+case kal: long a;
+case kula: short a;
+};
+
+
+
+
+// Check that enum name duplication is found.
+
+union U1 switch (enum E1 {kalle, kula, E1}) {
+case E1 : long a; // legal
+case kalle : short E1; // illegal
+};
+
+
+// This is legal, but ended up here anyway
+
+union U2 switch(::E2) {
+case kal : long a;
+case kula : short b;
+default : boolean E1;
+};
diff --git a/lib/ic/test/ic_SUITE_data/u_norm.idl b/lib/ic/test/ic_SUITE_data/u_norm.idl
new file mode 100644
index 0000000000..e23796b8ca
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/u_norm.idl
@@ -0,0 +1,63 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+
+union U1 switch (long) {
+case 1: long a;
+case 2: case 3: short b;
+};
+
+
+union U2 switch (unsigned short) {
+case 10: boolean a;
+case 188: char b;
+default: string c;
+};
+
+
+union U3 switch (enum E1 {kalle, kula, boll}) {
+case kalle: long a;
+case kula: U2 b;
+};
+
+enum E2 {Cissi, Anders};
+
+union U4 switch (::E2) {
+case Cissi: U1 a;
+default: case Anders: unsigned long b;
+};
+
+union U5 switch(char) {
+case 'e': long a;
+case 'b': case 'f': char b;
+default: struct S {long a; boolean b;} c;
+};
+
+
+// Now check that references can be used as case values
+
+const long c1 = 9;
+const long c2 = 10;
+
+union U6 switch (long) {
+case c1: boolean a;
+case ::c2: boolean b;
+};
+
+
diff --git a/lib/ic/test/ic_SUITE_data/u_type.idl b/lib/ic/test/ic_SUITE_data/u_type.idl
new file mode 100644
index 0000000000..44e3326305
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/u_type.idl
@@ -0,0 +1,82 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+
+//
+// Check that case values match declared discriminator type
+//
+
+
+const long longC = 0;
+const short shortC = 0;
+const char charC = 'c';
+const string stringC = "Yacht";
+
+enum E1 {kalle, kula};
+
+union U1 switch (long) {
+case 'c' : long a;
+case TRUE : long b;
+case stringC : long d;
+case kalle : long f;
+};
+
+union U2 switch (unsigned long) {
+case 'c' : long a;
+case TRUE : long b;
+case stringC : long d;
+case kalle : long f;
+};
+
+union U3 switch (short) {
+case 'c' : long a;
+case TRUE : long b;
+case stringC : long d;
+case kalle : long f;
+};
+
+union U4 switch (unsigned short) {
+case 'c' : long a;
+case TRUE : long b;
+case stringC : long d;
+case kalle : long f;
+};
+
+union U5 switch (char) {
+case TRUE : long b;
+case stringC : long d;
+case shortC : long e;
+case kalle : long f;
+};
+
+
+union U6 switch (E1) {
+case 'c' : long a;
+case TRUE : long b;
+case stringC : long d;
+case shortC : long e;
+};
+
+union U7 switch (enum E2 {ja, nej, kanske}) {
+case 'c' : long a;
+case TRUE : long b;
+case stringC : long d;
+case shortC : long e;
+};
+
diff --git a/lib/ic/test/ic_SUITE_data/undef_id.idl b/lib/ic/test/ic_SUITE_data/undef_id.idl
new file mode 100644
index 0000000000..01a35c4ef8
--- /dev/null
+++ b/lib/ic/test/ic_SUITE_data/undef_id.idl
@@ -0,0 +1,63 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+//
+// Check that undefined ids are detected
+//
+
+typedef T7 T1;
+
+const char c1 = ::c0;
+const T01 c2 = 'h';
+const T7 c3 = 9;
+
+interface i1 {
+ T17 op();
+ long op2( in T7 a);
+ attribute T7 a1, a2;
+ readonly attribute T17 a3;
+};
+
+union U1 switch (long) {
+case 1: long a;
+case ::g : short b;
+};
+
+union U2 switch (enum E1 {kalle, kula}) {
+case kula1: long a;
+case kalle : short b;
+};
+
+union U3 switch (long) {
+case kula2: long a;
+case ::E3::kalle : short b;
+case ::E4::kalle : short c;
+};
+
+enum E2 {kalle2, kula2};
+
+union U4 switch (E2) {
+case kula1: long a;
+case kula1: long b;
+case c3: short c;
+};
+
+
+
+
diff --git a/lib/ic/test/ic_be_SUITE.erl b/lib/ic/test/ic_be_SUITE.erl
new file mode 100644
index 0000000000..5a213ebd5f
--- /dev/null
+++ b/lib/ic/test/ic_be_SUITE.erl
@@ -0,0 +1,89 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : Test suite for the backends of the IDL compiler
+%%%----------------------------------------------------------------------
+
+-module(ic_be_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,plain/1]).
+
+
+-define(OUT(X), filename:join([?config(priv_dir, Config), gen, to_list(X)])).
+
+
+%% Top of cases
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [plain].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
+plain(doc) ->
+ ["Checking code for the plain backend."];
+plain(suite) -> [];
+plain(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(slask),
+ File = filename:join(DataDir, plain),
+
+ ?line ok = ic:gen(File,stdopts(OutDir)++[{be,erl_plain}]),
+
+ ok.
+
+
+
+
+%%--------------------------------------------------------------------
+%%
+%% Utilities
+
+
+stdopts(OutDir) ->
+ [{outdir, OutDir}, {maxerrs, infinity}].
+
+
+
+
+
+to_list(X) when is_atom(X) -> atom_to_list(X);
+to_list(X) -> X.
+
diff --git a/lib/ic/test/ic_be_SUITE_data/plain.idl b/lib/ic/test/ic_be_SUITE_data/plain.idl
new file mode 100644
index 0000000000..ee0a995807
--- /dev/null
+++ b/lib/ic/test/ic_be_SUITE_data/plain.idl
@@ -0,0 +1,33 @@
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+module m {
+
+ struct s {
+ long x;
+ long y;
+ };
+
+ interface i {
+
+ void foo( in s a, out short b );
+
+ };
+
+};
+
diff --git a/lib/ic/test/ic_pp_SUITE.erl b/lib/ic/test/ic_pp_SUITE.erl
new file mode 100644
index 0000000000..571c37c3da
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE.erl
@@ -0,0 +1,622 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose : Test suite for the IDL preprocessor
+%%----------------------------------------------------------------------
+
+-module(ic_pp_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+
+
+%% Standard options to the ic compiler, NOTE unholy use of OutDir
+
+-define(OUT(X), filename:join([?config(priv_dir, Config), gen, to_list(X)])).
+-define(GCC, "g++").
+-define(GCC_VER, "2.95.3").
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
+-export([arg_norm/1]).
+-export([cascade_norm/1]).
+-export([comment_norm/1]).
+-export([concat_norm/1]).
+-export([define_norm/1]).
+-export([if_norm/1]).
+-export([if_zero/1]).
+-export([misc_norm/1]).
+-export([improp_nest_constr_norm/1]).
+-export([inc_norm/1]).
+-export([line_norm/1]).
+-export([nopara_norm/1]).
+-export([predef_norm/1]).
+-export([predef_time_norm/1]).
+-export([self_ref_norm/1]).
+-export([separate_norm/1]).
+-export([swallow_sc_norm/1]).
+-export([unintended_grp_norm/1]).
+-export([cases/0, init_per_suite/1, end_per_suite/1]).
+
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [{arg, [], [arg_norm]}, {cascade, [], [cascade_norm]},
+ {comment, [], [comment_norm]},
+ {concat, [], [concat_norm]},
+ {define, [], [define_norm]}, {inc, [], [inc_norm]},
+ {improp_nest_constr, [], [improp_nest_constr_norm]},
+ {misc, [], [misc_norm]}, {line, [], [line_norm]},
+ {nopara, [], [nopara_norm]},
+ {predef, [], [predef_norm]},
+ {predef_time, [], [predef_time_norm]},
+ {self_ref, [], [self_ref_norm]},
+ {separate, [], [separate_norm]},
+ {swallow_sc, [], [swallow_sc_norm]},
+ {unintended_grp, [], [unintended_grp_norm]},
+ {'if', [],[if_norm, if_zero]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) ->
+ if
+ is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ {skipped, "Very unplesent to run on windows"};
+ _ ->
+ check_gcc(Config)
+ end;
+ true ->
+ exit("Config not a list")
+ end.
+
+check_gcc(Config) ->
+ case os:find_executable(?GCC) of
+ false ->
+ {skipped,
+ lists:flatten(io_lib:format("Can not run without ~s in path",
+ [?GCC]))};
+ _ ->
+ case trim(os:cmd(?GCC++" --version")) of
+ ?GCC_VER++[] ->
+ Config;
+ ?GCC_VER++[D|_] when is_integer(D), D>=$0, D=<$9 ->
+ fail_gcc(?GCC_VER++[D]);
+ ?GCC_VER++_ ->
+ Config;
+ Ver ->
+ fail_gcc(Ver)
+ end
+ end.
+
+fail_gcc(Ver) ->
+ {skipped, lists:flatten(io_lib:format("Need ~s v~s, not ~s",
+ [?GCC, ?GCC_VER, Ver]))}.
+
+trim(S) -> lists:reverse(skip_white(lists:reverse(skip_white(S)))).
+
+skip_white([$\s|T]) -> skip_white(T);
+skip_white([$\n|T]) -> skip_white(T);
+skip_white([$\r|T]) -> skip_white(T);
+skip_white([$\t|T]) -> skip_white(T);
+skip_white(L) -> L.
+
+
+end_per_suite(Config) ->
+ Config.
+
+
+cases() ->
+ [{group, arg}, {group, cascade}, {group, comment},
+ {group, concat}, {group, define}, {group, misc}, {group, 'if'},
+ {group, improp_nest_constr}, {group, inc},
+ {group, line}, {group, nopara}, {group, predef},
+ {group, predef_time}, {group, self_ref},
+ {group, separate}, {group, swallow_sc},
+ {group, unintended_grp}].
+
+
+
+%%--------------------------------------------------------------------
+%% arg
+%%--------------------------------------------------------------------
+
+
+arg_norm(doc) -> ["Checks arguments for #define."];
+arg_norm(suite) -> [];
+arg_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(arg_norm),
+ File = filename:join(DataDir, arg),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% cascade
+%%--------------------------------------------------------------------
+
+
+cascade_norm(doc) -> ["Check cascade #define."];
+cascade_norm(suite) -> [];
+cascade_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(cascade_norm),
+ File = filename:join(DataDir, cascade),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% comment
+%%--------------------------------------------------------------------
+
+
+comment_norm(doc) -> ["Check comments."];
+comment_norm(suite) -> [];
+comment_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(comment_norm),
+ File = filename:join(DataDir, comment),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% concat
+%%--------------------------------------------------------------------
+
+
+concat_norm(doc) -> ["Check concatinations, i.e ## ."];
+concat_norm(suite) -> [];
+concat_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(concat_norm),
+ File = filename:join(DataDir, concat),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% define
+%%--------------------------------------------------------------------
+
+
+define_norm(doc) -> ["Check misceleaneous #define."];
+define_norm(suite) -> [];
+define_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(define_norm),
+ File = filename:join(DataDir, define),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% if
+%%--------------------------------------------------------------------
+
+if_norm(doc) -> ["Check #if, #elif, and #endif. ."];
+if_norm(suite) -> [];
+if_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(if_norm),
+ File = filename:join(DataDir, 'if'),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+if_zero(doc) -> ["Check #if 0"];
+if_zero(suite) -> [];
+if_zero(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(if_zero),
+ File = filename:join(DataDir, if_zero),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% inc
+%%--------------------------------------------------------------------
+
+
+inc_norm(doc) -> ["Check #include."];
+inc_norm(suite) -> [];
+inc_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(inc_norm),
+ File = filename:join(DataDir, inc),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+
+%%--------------------------------------------------------------------
+%% improp_nest_constr
+%%--------------------------------------------------------------------
+
+
+improp_nest_constr_norm(doc) -> ["Check improperly nested constructs."];
+improp_nest_constr_norm(suite) -> [];
+improp_nest_constr_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(improp_nest_constr_norm),
+ File = filename:join(DataDir, improp_nest_constr),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% misc
+%%--------------------------------------------------------------------
+
+
+misc_norm(doc) -> ["Misceleaneous checks."];
+misc_norm(suite) -> [];
+misc_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(misc_norm),
+ File = filename:join(DataDir, misc),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% line
+%%--------------------------------------------------------------------
+
+
+line_norm(doc) -> ["Checks #line."];
+line_norm(suite) -> [];
+line_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(line_norm),
+ File = filename:join(DataDir, line),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% nopara
+%%--------------------------------------------------------------------
+
+
+nopara_norm(doc) -> ["Checks #define with no parameters."];
+nopara_norm(suite) -> [];
+nopara_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(nopara_norm),
+ File = filename:join(DataDir, nopara),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% predef
+%%--------------------------------------------------------------------
+
+
+predef_norm(doc) -> ["Checks predefined macros. Note: not __TIME__ and __DATE__."];
+predef_norm(suite) -> [];
+predef_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(predef_norm),
+ File = filename:join(DataDir, predef),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% predef_time
+%%--------------------------------------------------------------------
+
+
+predef_time_norm(doc) -> ["Checks the predefined macros __TIME__ and __DATE__."];
+predef_time_norm(suite) -> [];
+predef_time_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(predef_time_norm),
+ File = filename:join(DataDir, predef_time),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% self_ref
+%%--------------------------------------------------------------------
+
+
+self_ref_norm(doc) -> ["Checks self referring macros."];
+self_ref_norm(suite) -> [];
+self_ref_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(self_ref_norm),
+ File = filename:join(DataDir, self_ref),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% separate
+%%--------------------------------------------------------------------
+
+
+separate_norm(doc) -> ["Checks separete expansion of macro arguments."];
+separate_norm(suite) -> [];
+separate_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(separate_norm),
+ File = filename:join(DataDir, separate),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% swallow_sc
+%%--------------------------------------------------------------------
+
+
+swallow_sc_norm(doc) -> ["Checks swallowing an undesirable semicolon."];
+swallow_sc_norm(suite) -> [];
+swallow_sc_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(swallow_sc_norm),
+ File = filename:join(DataDir, swallow_sc),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+%%--------------------------------------------------------------------
+%% unintended_grp
+%%--------------------------------------------------------------------
+
+
+unintended_grp_norm(doc) -> ["Checks unintended grouping of arithmetic."];
+unintended_grp_norm(suite) -> [];
+unintended_grp_norm(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ _OutDir = ?OUT(unintended_grp_norm),
+ File = filename:join(DataDir, unintended_grp),
+
+ ?line ok = test_file(File, DataDir),
+ ok.
+
+
+
+
+
+test_file(FileT, DataDir) ->
+ case test_file_1(FileT, DataDir) of
+ ok -> ok;
+ Chars ->
+ io:put_chars(Chars),
+ {error,{FileT,DataDir}}
+ end.
+
+test_file_1(FileT, DataDir) ->
+ Tok = string:tokens(FileT, "/"),
+ FileName = lists:last(Tok),
+ File = FileT++".idl",
+
+ ?line test_server:format("File ~p~n",[File]),
+ ?line test_server:format("FileName ~p~n",[FileName]),
+
+ Flags = "-I"++DataDir,
+
+ ?line test_server:format("Flags ~p~n",[Flags]),
+
+ ?line Erl = pp_erl(File, Flags),
+ ?line Gcc = pp_gcc(File, Flags),
+
+ ?line case Erl of
+ {error,_ErlError} ->
+ ?line test_server:format("Internal_pp Result ~n==================~n~p~n~n",[Erl]);
+ {warning, _ErlWar} ->
+ ?line test_server:format("Internal_pp Result ~n==================~n~p~n~n",[Erl]);
+ _ ->
+ ?line test_server:format("Internal_pp Result ~n==================~n~s~n~n",[Erl])
+ end,
+
+ ?line case Gcc of
+ {error,GccError} ->
+ Error = string:tokens(GccError, "\n"),
+ ?line test_server:format(?GCC" Result ~n==========~n~p~n~n",
+ [Error]);
+ _ ->
+ ?line test_server:format(?GCC" Result ~n==========~n~s~n~n",[Gcc])
+ end,
+
+
+
+ ?line case {Erl,Gcc} of
+ {{warning,W}, {error,X}} ->
+ ?line case is_ok(W,X) of
+ yes ->
+ ok;
+ no ->
+ io_lib:format("Internal_pp found Warning = ~p ~n"
+ ?GCC" found Error = ~p~n",[W,X])
+ end;
+
+
+ {{warning,W}, _} ->
+ io_lib:format(?GCC" did not find warnings while ~n"
+ "Internal_pp found the following Warning = ~p~n",[W]);
+
+ {{error,E}, {error,X}} ->
+ ?line case is_ok(E,X) of
+ yes ->
+ ok;
+ no ->
+ io_lib:format("Internal_pp found Error = ~p ~n"
+ ?GCC" found Error = ~p~n",[E,X])
+ end;
+
+ {{error,E}, _} ->
+ ?line case FileName of
+ "if" ->
+ ?line case if_res(E) of
+ ok ->
+ ok;
+ _ ->
+ io_lib:format(?GCC" did not find errors while ~n"
+ "Internal_pp found the following Error = ~p~n",[E])
+ end;
+ _ ->
+ io_lib:format(?GCC" did not find errors while ~n"
+ "Internal_pp found the following Error = ~p~n",[lists:flatten(E)])
+ end;
+
+ {_, {error,X}} ->
+ io_lib:format("Internal_pp did not find errors while ~n"
+ ?GCC" found the following Error = ~p~n",[X]);
+
+ _ ->
+
+ ?line file:write_file("/tmp/Erl.pp",list_to_binary(Erl)),
+ ?line file:write_file("/tmp/Gcc.pp",list_to_binary(Gcc)),
+
+ ?line Res = os:cmd("diff -b -w /tmp/Erl.pp /tmp/Gcc.pp"),
+ ?line test_server:format("///////////{error,E} E ~p FileName~p~n",[Res,FileName]),
+ ?line case {Res, FileName} of
+ {[], _} ->
+ ?line test_server:format("Diff = [] OK!!!!!!~n"),
+ ok;
+ {_, "predef_time"} ->
+ Tokens = string:tokens(Res,"\n"),
+ ?line test_server:format("///////////{error,E} Tokens~p~n",[Tokens]),
+ case Tokens of
+ ["3c3",_,"---",_,"5c5",_,"---",_,"9c9",_,"---",_] ->
+ ok;
+ _ ->
+ io_lib:format("Diff Result = ~p~n",[Res])
+ end;
+ _ ->
+ io_lib:format("Diff Result = ~p~n",[Res])
+ end
+ end.
+
+
+
+
+
+pp_erl(File, Flags) ->
+ case ic_pp:run(File,Flags) of
+ {ok, [$#, $ , $1 | Rest], []} ->
+ [$#, $ , $1 | Rest];
+ {ok, [$#, $ , $1 | _Rest], Warning} ->
+ {warning,Warning};
+ {error,Error} ->
+ {error,Error}
+ end.
+
+pp_gcc(File, Flags) ->
+ Cmd = ?GCC" -x c++ -E",
+ Line = Cmd++" "++Flags++" "++File,
+
+ case os:cmd(Line) of
+ [$#, $ , $1 | Rest] ->
+ [$#, $ , $1 | Rest];
+ Res ->
+
+ case string:str(Res,"# 1 \"") of
+ 0 ->
+ {error,Res};
+ X ->
+ {error, string:sub_string(Res, 1, X-1)}
+ end
+ end.
+
+
+is_ok([],_Gcc) ->
+ yes;
+is_ok([{FileName,Line,Text}|T],Gcc) ->
+ Str = FileName++":"++integer_to_list(Line)++": "++Text,
+ case string:str(Gcc,Str) of
+ 0 ->
+ io:format("~n is_ok Internal_pp missed Error = ~s~n",[Str]),
+ no;
+ _X ->
+ is_ok(T,Gcc)
+ end;
+is_ok([Str|T],Gcc) ->
+ case string:str(Gcc,Str) of
+ 0 ->
+ io:format("~n is_ok Internal_pp missed Error = ~s~n",[Str]),
+ no;
+ _X ->
+ is_ok(T,Gcc)
+ end.
+
+
+to_list(X) when is_atom(X) -> atom_to_list(X);
+to_list(X) -> X.
+
+
+
+if_res(E) ->
+ if_res(E,1).
+
+if_res([H|T],Nr) ->
+ %% Dir = "/clearcase/otp/libraries/ic/test/ic_pp_SUITE_data/if.idl",
+ case {Nr, H} of
+ {1, {_Dir, 2, "only '#if 0' is implemented at present"}} ->
+ if_res(T,Nr+1);
+ {2, {_Dir, 3, "only '#if 0' is implemented at present"}} ->
+ if_res(T,Nr+1);
+ {3, {_Dir, 5, "`else' command is not implemented at present"}} ->
+ if_res(T,Nr+1);
+ {4, {_Dir, 9, "`elif' command is not implemented at present"}} ->
+ if_res(T,Nr+1);
+ {5, {_Dir, 11, "`else' command is not implemented at present"}} ->
+ ok;
+ _ ->
+ error
+ end;
+if_res(_, _) ->
+ error.
+
+
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/arg.idl b/lib/ic/test/ic_pp_SUITE_data/arg.idl
new file mode 100644
index 0000000000..b4d266121d
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/arg.idl
@@ -0,0 +1,38 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define xstr (s) str(s)
+#define str(s) #s
+#define foo 4
+
+xstr(foo);
+
+#define x(kalle)stina
+x(kurt)
+x
+
+#define y(kalle) stina
+y(kurt)
+y
+
+#define a(kalle) stina
+a(kurt)
+a
+
+#define b (kalle) stina
+b(kurt)
diff --git a/lib/ic/test/ic_pp_SUITE_data/cascade.idl b/lib/ic/test/ic_pp_SUITE_data/cascade.idl
new file mode 100644
index 0000000000..8dff1ee99f
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/cascade.idl
@@ -0,0 +1,29 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define BUFS 1020
+#define TABS BUFS
+#undef BUFS
+#define BUFS 37
+
+
+main()
+{
+ TABS;
+
+}
diff --git a/lib/ic/test/ic_pp_SUITE_data/comment.idl b/lib/ic/test/ic_pp_SUITE_data/comment.idl
new file mode 100644
index 0000000000..d2ca3e7872
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/comment.idl
@@ -0,0 +1,72 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define T 12
+#define F T
+
+//comment
+/*exception except {};*/
+
+// comment
+ // comment
+/* another */
+ /* another */
+/* still
+another */
+ /* still
+ another */
+__LINE__
+/* yet \
+ another */
+// yet \
+ another
+__LINE__
+
+#include "all.c"
+#include <all.c>
+#include /* comment */ "all.c"
+#include /* comment */ <all.c>
+#include "all.c" /* comment */
+#include <all.c> /* comment */
+#include // "all.c"
+#include // <all.c>
+#include "all.c" // comment
+#include <all.c> // comment
+#include "all/*cc*/.c"
+#include <all/*cc*/.c>
+
+main()
+{
+ printf(" %d \n",F);
+ a();
+
+}
+//comment
+/*exception hell {};*/
+#undef T
+#define T "3/*com\
+ment*/4"
+a()
+{
+ printf(" %d \n",F);
+ printf(" %d \n",T);
+}
+
+b()
+{}
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/concat.idl b/lib/ic/test/ic_pp_SUITE_data/concat.idl
new file mode 100644
index 0000000000..b8527fadfc
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/concat.idl
@@ -0,0 +1,60 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define sune kurt
+#define a(name) a #name name##_command
+#define b(name) b #name name## _command
+#define c(name) c #name name ##_command
+#define d(name) d #name name ## _command
+#define e(name) e #name command ## _command
+#define f(name) f #name command ## %_command
+#define g(name) g #name name ## %_command
+#define h(name) h #name %_command ## name
+#define i(name) i #name name ## _ ## name
+#define j(name) j #name name ## name
+#define k(name) k #name name ## name
+#define l(name) l #name !name ## name
+#define m(name) m #name name ## !name
+#define n(name) n #name !name ## !name
+#define o(name) stina
+#define p(name) name
+#define q1(name) q1 #name j(name) ## j(name)
+#define q2(name) q2 #name j(name)
+#define q3(name) q3 #name !! ## j(name)
+#define q4(name) q4 #name ## j(name)
+
+a(quit)
+b(quit)
+c(quit)
+d(quit)
+e(quit)
+f(quit)
+g(sune)
+h(sune)
+i(sune)
+j(sune)
+l(sune)
+m(sune)
+n(sune)
+k(j(sune))
+k(o(sune))
+k(p(sune))
+q1(sune)
+q2(sune)
+q3(sune)
+q4(sune)
diff --git a/lib/ic/test/ic_pp_SUITE_data/define.idl b/lib/ic/test/ic_pp_SUITE_data/define.idl
new file mode 100644
index 0000000000..6aac63dd1e
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/define.idl
@@ -0,0 +1,41 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define 8
+#define
+#define a
+#define _a
+#define b dfs
+#define 9 fdas
+#define a8
+#define A
+#define (c) fadfas
+#define )c) fadfas
+#define % c) fadfas
+#define d(p) kfdsa
+#define e(p) sinus(p)
+#warning warning line
+#define w%er percent
+#define q() no_para
+#warning warning line
+#undef
+#undef 8
+#undef a
+#undef b
+#undef _a d(kk)
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/if.idl b/lib/ic/test/ic_pp_SUITE_data/if.idl
new file mode 100644
index 0000000000..c381fa73ee
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/if.idl
@@ -0,0 +1,32 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define kurt 12
+#if !true
+#if X == 1
+ett
+#else
+else
+#endif
+true
+#elif kurt
+trueelif
+#else
+trueelse
+#endif
+end
diff --git a/lib/ic/test/ic_pp_SUITE_data/if_zero.idl b/lib/ic/test/ic_pp_SUITE_data/if_zero.idl
new file mode 100644
index 0000000000..d715f9d61e
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/if_zero.idl
@@ -0,0 +1,31 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#if 0
+pelle = mallan
+#endif
+pelle = stina
+#if 0
+kalle = stina
+#endif
+kalle = mallan
+#if 0
+kurt = fia
+#endif
+fia = kurt
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/improp_nest_constr.idl b/lib/ic/test/ic_pp_SUITE_data/improp_nest_constr.idl
new file mode 100644
index 0000000000..463ee3c695
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/improp_nest_constr.idl
@@ -0,0 +1,30 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define double(x) (2*(x))
+#define call_with_1(x) x(1)
+
+#define strange(file) fprintf (file, "%s %d",
+
+main()
+{
+ call_with_1(double);
+ strange(stderr) p, 35)
+
+}
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/inc.idl b/lib/ic/test/ic_pp_SUITE_data/inc.idl
new file mode 100644
index 0000000000..0dcd637082
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/inc.idl
@@ -0,0 +1,68 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+int x;
+
+#include "head.h"
+#warning line nr
+main()
+{
+ printf(test());
+}
+
+
+
+
+
+
+
+
+#define C false
+#define Z on
+#include "inc2.h"
+#undef Z
+"Ca" C
+"Za" Z
+#include "inc2.h"
+"Cb" C
+"Zb" Z
+
+main()
+{
+#define Q(a,b) sinus(a,b kurt ## b)
+ if (Q(34,56)=='NULL') printf(" T AAA%sEEEE \n",Q);
+ printf(" %d \n",F);
+ a();
+}
+//comment
+/*exception
+hell {};*/
+#undef T
+#define T "3/*com\ment*/4"
+#define T 33
+#define F again
+a ()
+{
+ printf(" %d \n",F);
+ printf(" %d \n",T);
+}
+
+b()
+{}
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/included1.idl b/lib/ic/test/ic_pp_SUITE_data/included1.idl
new file mode 100644
index 0000000000..4cd26c4543
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/included1.idl
@@ -0,0 +1,35 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2000-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#ifndef INCLUDED1_IDL
+#define INCLUDED1_IDL
+
+
+#ifndef SOMETHING
+#endif
+
+
+struct s {
+
+ long l;
+
+};
+
+
+
+#endif
diff --git a/lib/ic/test/ic_pp_SUITE_data/included2.idl b/lib/ic/test/ic_pp_SUITE_data/included2.idl
new file mode 100644
index 0000000000..7cc44eef3e
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/included2.idl
@@ -0,0 +1,41 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2000-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#ifndef INCLUDED2_IDL
+#define INCLUDED2_IDL
+
+#include "included1.idl"
+
+
+#ifdef SOMETHING
+#endif
+
+
+module m {
+
+ struct t {
+
+ s st;
+
+ };
+
+
+};
+
+
+#endif
diff --git a/lib/ic/test/ic_pp_SUITE_data/includer.idl b/lib/ic/test/ic_pp_SUITE_data/includer.idl
new file mode 100644
index 0000000000..c6ebc234e8
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/includer.idl
@@ -0,0 +1,45 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2000-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#ifndef INCLUDER_IDL
+#define INCLUDER_IDL
+
+#include "included1.idl"
+#include "included2.idl"
+
+#ifdef SOMETHING
+#endif
+
+
+
+module n {
+
+ interface j {
+
+ s op(in m::t inpar);
+
+ };
+
+};
+
+
+
+
+#endif
+
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/line.idl b/lib/ic/test/ic_pp_SUITE_data/line.idl
new file mode 100644
index 0000000000..5bd9c9446d
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/line.idl
@@ -0,0 +1,45 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#line
+#line 8
+#line 8a
+#line 12 abc.c
+#line 12 "kurt.c"
+#warning fdafdsaf
+
+
+#define T 12
+#define F T
+#define Q(a) sinus(a)
+#undef Q
+#
+#line 12
+#warning test of warning
+#warning second of warning
+#warning third of warning
+#pragma kurt
+#ident kurt
+#kurt fdsafd
+#line 20
+main()
+{
+ if (Q(34,56)=='NULL') printf(" T AAA%sEEEE \n",Q);
+ printf(" %d \n",F);
+}
+sune
diff --git a/lib/ic/test/ic_pp_SUITE_data/misc.idl b/lib/ic/test/ic_pp_SUITE_data/misc.idl
new file mode 100644
index 0000000000..9c18610fcf
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/misc.idl
@@ -0,0 +1,44 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define str(s) #s
+str(fool);
+str(foo);
+str(kurt);
+#define xstr(s) str(s)
+#define foo 4
+#define kurt sune
+#define sune 17
+
+xstr(fool);
+xstr(foo);
+xstr(kurt);
+
+#define a(b) b #8b
+#define r(b) b #
+#define t(b) b ## a
+a(sinus)
+
+#define ww #www
+ww
+
+#define x 14 + y
+#define y 12 + #x
+x
+
+#define e(a) cosinus(a)
diff --git a/lib/ic/test/ic_pp_SUITE_data/nopara.idl b/lib/ic/test/ic_pp_SUITE_data/nopara.idl
new file mode 100644
index 0000000000..1bb137da11
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/nopara.idl
@@ -0,0 +1,35 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#11a
+#define xstr str(s) + kurt*2;
+#define asdf pragma
+#asdf
+#define asd #pragma asd
+
+#10
+#12 8kurt
+
+#define sss "stringing in the rain"
+#define ddd "string
+ing in the rain" asd
+#line 20
+#include "head.h" qqqq
+#include %!#
+#include <sys.h>
+
diff --git a/lib/ic/test/ic_pp_SUITE_data/predef.idl b/lib/ic/test/ic_pp_SUITE_data/predef.idl
new file mode 100644
index 0000000000..d8abcb25d5
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/predef.idl
@@ -0,0 +1,33 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define b(q,w) kurt q w
+
+
+b(__LINE__, __FILE__)
+__LINE__
+__FILE__
+
+
+
+b(__INCLUDE_LEVEL__, __BASE_FILE__)
+__INCLUDE_LEVEL__
+__BASE_FILE__
+
+Line __LINE__
+#include "predef.h"
diff --git a/lib/ic/test/ic_pp_SUITE_data/predef_time.idl b/lib/ic/test/ic_pp_SUITE_data/predef_time.idl
new file mode 100644
index 0000000000..05e3ba9175
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/predef_time.idl
@@ -0,0 +1,24 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define b(q,w) kurt q w
+b(__DATE__, __TIME__)
+__DATE__
+__TIME__
+
+#include "predef_time.h"
diff --git a/lib/ic/test/ic_pp_SUITE_data/self_ref.idl b/lib/ic/test/ic_pp_SUITE_data/self_ref.idl
new file mode 100644
index 0000000000..a44666272e
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/self_ref.idl
@@ -0,0 +1,26 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define foo (4 + foo)
+
+
+main()
+{
+ foo;
+
+}
diff --git a/lib/ic/test/ic_pp_SUITE_data/separate.idl b/lib/ic/test/ic_pp_SUITE_data/separate.idl
new file mode 100644
index 0000000000..a3faf9b986
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/separate.idl
@@ -0,0 +1,37 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define xstr(s) str(s)
+#define str(s) #s
+#define foo 4
+#define str1(s) #s lose(s)
+#define foo1 4
+
+main()
+{
+ str(foo);
+ str1(foo1);
+ xstr(foo);
+
+#define qxstr(s) qstr(s)
+ qxstr(qfoo);
+#define qstr(s) #s
+ qstr( 4 ) ;
+#define qfoo 4
+ qstr(qfoo);
+}
diff --git a/lib/ic/test/ic_pp_SUITE_data/swallow_sc.idl b/lib/ic/test/ic_pp_SUITE_data/swallow_sc.idl
new file mode 100644
index 0000000000..71ed329ca6
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/swallow_sc.idl
@@ -0,0 +1,37 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+/* comment \
+ ends */
+// comment\
+ends
+Line __LINE__
+#define SKIP_SPACES(p, limit) \
+{register char *lim = (limit); \
+ while (p != lim) { \
+ if (*p++ != ' ') { \
+ p--; break; }}}
+
+
+main()
+{
+ if (*p != 0)
+ SKIP_SPACES (ppp, lim);
+ else
+ a = 17;
+}
diff --git a/lib/ic/test/ic_pp_SUITE_data/unintended_grp.idl b/lib/ic/test/ic_pp_SUITE_data/unintended_grp.idl
new file mode 100644
index 0000000000..3618bab1bc
--- /dev/null
+++ b/lib/ic/test/ic_pp_SUITE_data/unintended_grp.idl
@@ -0,0 +1,29 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+#define ceil_div( xz, yz) (xz + yz - 1) / yz
+#define ceil_div2(xz, yz) ((xz) + (yz) - 1) / (yz)
+
+#define b kurt
+
+main()
+{
+ ceil_div(b & c, sizeof(int));
+ ceil_div2(b & c, sizeof(int));
+
+}
diff --git a/lib/ic/test/ic_pragma_SUITE.erl b/lib/ic/test/ic_pragma_SUITE.erl
new file mode 100644
index 0000000000..6919af78b5
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE.erl
@@ -0,0 +1,306 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%-----------------------------------------------------------------
+%% File: ic_pragma_SUITE.erl
+%%
+%% Description:
+%% Test suite for the IFR object registration when
+%% pragmas are engaged
+%%
+%%-----------------------------------------------------------------
+-module(ic_pragma_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("orber/include/corba.hrl").
+%%-----------------------------------------------------------------
+%% External exports
+%%-----------------------------------------------------------------
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1]).
+-export([ifr_pragma_reg/1, pragma_error/1, uggly_pragmas/1]).
+
+
+%%-----------------------------------------------------------------
+%% Macros
+%%-----------------------------------------------------------------
+-define(REMAP_EXCEPT(F), case catch F of
+ {'EXCEPTION', E} -> exit(E);
+ R -> R
+ end).
+%% Standard options to the ic compiler, NOTE unholy use of OutDir
+
+-define(OUT(X), filename:join([?config(priv_dir, Config), gen, to_list(X)])).
+
+
+%%-----------------------------------------------------------------
+%% Func: all/1
+%% Args:
+%% Returns:
+%%-----------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [ifr_pragma_reg, pragma_error, uggly_pragmas].
+
+%%-----------------------------------------------------------------
+%% Init and cleanup functions.
+%%-----------------------------------------------------------------
+init_per_suite(Config) ->
+ io:format("Setting up.....~n"),
+ mnesia:stop(),
+ mnesia:delete_schema([node()]),
+ mnesia:create_schema([node()]),
+ mnesia:start(),
+ orber:install([node()]),
+ orber:start(),
+ if
+ is_list(Config) ->
+ Config;
+ true ->
+ exit("Config not a list")
+ end.
+
+end_per_suite(Config) ->
+ io:format("Setting down.....~n"),
+ orber:stop(),
+ orber:uninstall(),
+ mnesia:stop(),
+ mnesia:delete_schema([node()]),
+ Config.
+
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: IFR registration with pragmas
+%%-----------------------------------------------------------------
+ifr_pragma_reg(doc) ->
+ ["Checks that IFR object is correctly registered under pragma engagement."];
+ifr_pragma_reg(suite) -> [];
+ifr_pragma_reg(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(ifr_pragma_reg_run(Config)).
+
+ifr_pragma_reg_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(ifr_pragma_reg),
+ File0 = filename:join(DataDir, reg_m0),
+ ?line ok = ic:gen(File0, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = compile(OutDir, ifr_pragma_files()),
+ code:add_pathz(OutDir),
+
+ %% OE_register for all files
+ ?line ok = 'oe_reg_m0':'oe_register'(),
+
+ %% Pragma registration test
+ OE_IFR = orber_ifr:find_repository(),
+ io:format("~n##### Starting the test case #####~n"),
+ check_pragma_effect(OE_IFR,"IDL:M1/T1:1.0"),
+ check_pragma_effect(OE_IFR,"DCE:d62207a2-011e-11ce-88b4-0800090b5d3e:3"),
+ check_pragma_effect(OE_IFR,"IDL:P2/T3:1.0"),
+ check_pragma_effect(OE_IFR,"IDL:P1/M2/T4:2.4"),
+
+ %% OE_unregister for all files
+ ?line ok = 'oe_reg_m0':'oe_unregister'(),
+ code:del_path(OutDir),
+ ok.
+
+
+ifr_pragma_files() -> ['oe_reg_m0'].
+
+
+check_pragma_effect(OE_IFR,ID) ->
+ io:format("Checking for existance of : ~s~n",[ID]),
+ case orber_ifr:lookup_id(OE_IFR,ID) of
+ [] ->
+ test_server:fail(ID ++ " does not exist"),
+ false;
+ {Def,_} ->
+ io:format("Id refers to = {~p,#Bin}~n",[Def]),
+ true
+ end.
+
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: Syntactical / Semantical error pragma definitions
+%%-----------------------------------------------------------------
+pragma_error(doc) ->
+ ["Finds errornous pragma definitions under compilation."];
+pragma_error(suite) -> [];
+pragma_error(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(pragma_error_run(Config)).
+
+pragma_error_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(pragma_error),
+ File1 = filename:join(DataDir, reg_m1),
+ File2 = filename:join(DataDir, reg_m2),
+ File3 = filename:join(DataDir, reg_m3),
+ File4 = filename:join(DataDir, reg_m4),
+ File5 = filename:join(DataDir, reg_m5),
+ File6 = filename:join(DataDir, reg_m6),
+
+ ?line error = ic:gen(File1, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+
+ ?line error = ic:gen(File2, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+
+ ?line error = ic:gen(File3, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+
+ ?line ok = ic:gen(File4, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+
+ ?line error = ic:gen(File5, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+
+ ?line error = ic:gen(File6, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ok.
+
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: IFR registration with realy uggly placed pragmas
+%%-----------------------------------------------------------------
+uggly_pragmas(doc) ->
+ ["Checks that IFR object is correctly registered under really uggly pragma engagement."];
+uggly_pragmas(suite) -> [];
+uggly_pragmas(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(uggly_pragmas_run(Config)).
+
+uggly_pragmas_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(ifr_pragma_reg),
+ File0 = filename:join(DataDir, uggly),
+
+ ?line ok = ic:gen(File0, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}]),
+
+ ?line ok = compile(OutDir, uggly_pragma_files()),
+ code:add_pathz(OutDir),
+
+ %% OE_register for all files
+ ?line ok = 'oe_uggly':'oe_register'(),
+
+ %% Pragma registration test
+ OE_IFR = orber_ifr:find_repository(),
+ io:format("~n##### Starting the test case #####~n"),
+
+ check_pragma_effect(OE_IFR, "IDL:M:1.0"),
+ check_pragma_effect(OE_IFR, "LOCAL:SomeLocalId:10"),
+ check_pragma_effect(OE_IFR, "LOCAL:SomeLocalId:11"),
+ check_pragma_effect(OE_IFR, "LOCAL:SomeLocalId:17"),
+ check_pragma_effect(OE_IFR, "LOCAL:SomeLocalId:34"),
+ check_pragma_effect(OE_IFR, "IDL:Exc1:2.2"),
+ check_pragma_effect(OE_IFR, "IDL:Exc2:2.2"),
+ check_pragma_effect(OE_IFR, "IDL:S:1.0"),
+ check_pragma_effect(OE_IFR, "IDL:U:1.0"),
+ check_pragma_effect(OE_IFR, "LOCAL:SomeLocalId:23"),
+
+ %% OE_unregister for all files
+ ?line ok = 'oe_uggly':'oe_unregister'(),
+
+ code:del_path(OutDir),
+ ok.
+
+
+uggly_pragma_files() -> ['oe_uggly'].
+
+
+
+
+%%----------------------------
+
+
+stdopts(OutDir) ->
+ [{outdir, OutDir}, {maxerrs, infinity}].
+
+
+compile(Dir, Files) ->
+ compile(Dir, Files, []).
+
+compile(Dir, Files, Opts) ->
+ {ok, Cwd} = file:get_cwd(),
+ file:set_cwd(Dir),
+ io:format("Changing to ~p~n", [Dir]),
+ case catch do_compile(Files, Opts) of
+ ok ->
+ file:set_cwd(Cwd);
+ Err ->
+ file:set_cwd(Cwd),
+ test_server:fail(Err)
+ end.
+
+do_compile([], _Opts) -> ok;
+do_compile([F | Fs], Opts) ->
+ io:format("Compiling ~p", [F]),
+ case compile:file(F, Opts) of
+ ok ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ {ok, _} ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ {ok, _, _} ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ Err ->
+ io:format(" error: ~p~n", [Err]),
+ Err
+ end.
+
+do_load(File, Opts) ->
+ case lists:member(load, Opts) of
+ true ->
+ io:format("Loading file ~p", [File]),
+ code:purge(File),
+ R = code:load_abs(File),
+ io:format("Loaded: ~p", [R]);
+ false ->
+ ok
+ end.
+
+
+to_list(X) when is_atom(X) -> atom_to_list(X);
+to_list(X) -> X.
+
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m0.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m0.idl
new file mode 100644
index 0000000000..80f0f2cdd1
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m0.idl
@@ -0,0 +1,77 @@
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Normal pragmas
+
+module M1 {
+
+ typedef long T1;
+
+ typedef long T2;
+
+#pragma ID T2 "DCE:d62207a2-011e-11ce-88b4-0800090b5d3e:3"
+
+};
+
+
+#pragma prefix "P1"
+
+module M2 {
+
+ module M3 {
+
+#pragma prefix "P2"
+
+ interface I1 {
+ void Op( in short b,
+ out short c);
+ };
+ typedef long T3;
+ };
+
+
+ typedef long T4;
+
+#pragma version T4 2.4
+
+};
+
+
+
+/*
+
+ Specified types with the following scoped names
+ and RepositoryIds
+
+ ::M1::T1 IDL:M1/T1:1.0
+
+ ::M1::T2 DCE:d62207a2-011e-11ce-88b4-0800090b5d3e:3
+
+ ::M2::M3::T3 IDL:P2/T3:1.0
+
+ ::M2::T4 IDL:P1/M2/T4:2.4
+
+*/
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m1.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m1.idl
new file mode 100644
index 0000000000..6c22788290
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m1.idl
@@ -0,0 +1,75 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+// Bad pragma IDs
+
+// Completelly bad id
+module M1 {
+
+ typedef long T1;
+
+ typedef long T2;
+
+#pragma ID T2 "CompletelyBadId"
+
+};
+
+
+// Bad id, should start with DCE
+module M2 {
+
+ typedef long T1;
+
+ typedef long T2;
+
+#pragma ID T2 "BAD:d62207a2-011e-11ce-88b4-0800090b5d3e:3"
+
+};
+
+
+// Bad version in ID : not a short number
+module M3 {
+
+ typedef long T1;
+
+ typedef long T2;
+
+#pragma ID T2 "DCE:d62207a2-011e-11ce-88b4-0800090b5d3e:ABCD"
+
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m2.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m2.idl
new file mode 100644
index 0000000000..1751751295
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m2.idl
@@ -0,0 +1,40 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+// Bad pragma versions
+
+
+// Bad major version : not a short number
+module M1 {
+
+ typedef long T4;
+
+#pragma version T4 2000000000.4
+
+};
+
+// Bad minor version : not a short number
+module M2 {
+
+ typedef long T4;
+
+#pragma version T4 2.4000000000000
+
+};
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m3.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m3.idl
new file mode 100644
index 0000000000..b7c9da249f
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m3.idl
@@ -0,0 +1,38 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+// Bad pragma prefixs
+
+module M2 {
+
+ module M3 {
+
+#pragma prefix P2 // Should be "P2"
+
+ interface I1 {
+ void foo( in short b,
+ out short c);
+ };
+ typedef long T3;
+ };
+
+
+ typedef long T4;
+};
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m4.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m4.idl
new file mode 100644
index 0000000000..0c7079e3dd
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m4.idl
@@ -0,0 +1,64 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+// Unrecognmizable pragmas
+
+module M1 {
+
+ typedef long T1;
+
+ typedef long T2;
+
+
+ // Should be ID directive
+
+#pragma ShouldBeId T2 "DCE:d62207a2-011e-11ce-88b4-0800090b5d3e:3"
+
+};
+
+ // Should be prefix directive
+
+#pragma ShouldBePrefix "P1"
+
+module M2 {
+
+ module M3 {
+
+ // Should be prefix directive
+
+#pragma ShouldBePrefix "P2"
+
+ interface I1 {
+ void foo( in short b,
+ out short c);
+ };
+ typedef long T3;
+ };
+
+
+ typedef long T4;
+
+
+ // Should be version
+
+#pragma ShouldBeVersion T4 2.4
+
+};
+
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m5.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m5.idl
new file mode 100644
index 0000000000..cbf053fac4
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m5.idl
@@ -0,0 +1,28 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Version not in valid form : major.ninor
+
+module M1 {
+
+ typedef long T4;
+
+ #pragma version T4 2
+
+};
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m6.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m6.idl
new file mode 100644
index 0000000000..b7c9da249f
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m6.idl
@@ -0,0 +1,38 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+// Bad pragma prefixs
+
+module M2 {
+
+ module M3 {
+
+#pragma prefix P2 // Should be "P2"
+
+ interface I1 {
+ void foo( in short b,
+ out short c);
+ };
+ typedef long T3;
+ };
+
+
+ typedef long T4;
+};
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/reg_m7.idl b/lib/ic/test/ic_pragma_SUITE_data/reg_m7.idl
new file mode 100644
index 0000000000..349c13b244
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/reg_m7.idl
@@ -0,0 +1,62 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+
+// Very uggly pragmas
+
+
+#pragma prefix "P1" // Normal pragma
+
+module M4 {
+
+ module M5 {
+
+#pragma prefix "P2" // Inside a parameter list
+
+ interface I1 {
+ void Op(
+ #pragma prefix "P2" // Inside a parameter list
+ in short b,
+ #pragma prefix "P2" // Inside a parameter list
+ out short c
+ #pragma prefix "P2" // Inside a parameter list
+ );
+ };
+ typedef long T3;
+ };
+
+};
+
+
+
+/*
+
+ Specified types with the following scoped names
+ and RepositoryIds
+
+ ::M4::M5::T3 IDL:P2/T3:1.0
+
+*/
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_pragma_SUITE_data/uggly.idl b/lib/ic/test/ic_pragma_SUITE_data/uggly.idl
new file mode 100644
index 0000000000..8ed3ac57ec
--- /dev/null
+++ b/lib/ic/test/ic_pragma_SUITE_data/uggly.idl
@@ -0,0 +1,204 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+// Really uggly pragmas
+
+
+struct S {
+
+#pragma ID TDL1 "LOCAL:SomeLocalId:1"
+#pragma ID TDL1 "LOCAL:SomeLocalId:2"
+
+long a
+
+#pragma ID TDL1 "LOCAL:SomeLocalId:3"
+#pragma ID TDL1 "LOCAL:SomeLocalId:4"
+
+;
+
+#pragma ID TDL1 "LOCAL:SomeLocalId:5"
+#pragma ID TDL1 "LOCAL:SomeLocalId:6"
+
+long b
+
+#pragma ID TDL1 "LOCAL:SomeLocalId:7"
+#pragma ID TDL1 "LOCAL:SomeLocalId:8"
+
+;
+
+
+#pragma ID TDL1 "LOCAL:SomeLocalId:9"
+#pragma ID TDL1 "LOCAL:SomeLocalId:10"
+
+};
+
+
+typedef long TDL1;
+
+
+exception Exc1{
+
+#pragma version Exc1 2.2
+#pragma ID TDL2 "LOCAL:SomeLocalId:11"
+
+};
+
+
+typedef long TDL2;
+
+
+exception Exc2 {
+
+#pragma version Exc2 2.2
+#pragma ID TDL3 "LOCAL:SomeLocalId:11"
+
+ long a
+
+#pragma ID TDL3 "LOCAL:SomeLocalId:12"
+#pragma ID TDL3 "LOCAL:SomeLocalId:13"
+
+ ;
+
+#pragma ID TDL3 "LOCAL:SomeLocalId:14"
+#pragma ID TDL3 "LOCAL:SomeLocalId:15"
+
+ long b
+
+#pragma ID TDL3 "LOCAL:SomeLocalId:16"
+
+ ;
+
+#pragma ID TDL3 "LOCAL:SomeLocalId:17"
+
+
+};
+
+typedef long TDL3;
+
+enum E {
+#pragma ID E "LOCAL:SomeLocalId:18"
+ a
+#pragma ID E "LOCAL:SomeLocalId:19"
+ ,
+#pragma ID E "LOCAL:SomeLocalId:20"
+ b
+#pragma ID E "LOCAL:SomeLocalId:21"
+,
+#pragma ID E "LOCAL:SomeLocalId:22"
+ c
+#pragma ID E "LOCAL:SomeLocalId:23"
+};
+
+
+
+union U switch (long) {
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:24"
+
+ case 1:
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:25"
+
+ long a
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:26"
+
+;
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:27"
+
+ case 2:
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:28"
+
+ case 3:
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:29"
+
+long b
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:30"
+
+;
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:31"
+
+ default :
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:32"
+
+long c
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:33"
+
+;
+
+#pragma ID TDL4 "LOCAL:SomeLocalId:34"
+
+};
+
+
+typedef long TDL4;
+
+
+
+module M {
+
+ interface I {
+
+ void fun1(
+
+#pragma version fun1 3.0
+#pragma ID TDL5 "LOCAL:SomeLocalId:35"
+
+ in short b
+
+#pragma ID TDL5 "LOCAL:SomeLocalId:36"
+#pragma ID TDL5 "LOCAL:SomeLocalId:37"
+
+ ,
+
+#pragma ID TDL5 "LOCAL:SomeLocalId:38"
+#pragma ID TDL5 "LOCAL:SomeLocalId:39"
+
+ out short c
+
+#pragma ID TDL5 "LOCAL:SomeLocalId:40"
+#pragma ID TDL5 "LOCAL:SomeLocalId:41"
+
+ );
+
+
+ typedef long TDL5;
+
+
+ void fun2(
+
+#pragma ID TDL6 "LOCAL:SomeLocalId:42"
+#pragma ID TDL6 "LOCAL:SomeLocalId:43"
+
+ );
+
+ typedef long TDL6;
+
+ };
+
+
+
+};
+
diff --git a/lib/ic/test/ic_register_SUITE.erl b/lib/ic/test/ic_register_SUITE.erl
new file mode 100644
index 0000000000..c3a9464a10
--- /dev/null
+++ b/lib/ic/test/ic_register_SUITE.erl
@@ -0,0 +1,437 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%-----------------------------------------------------------------
+%% File: ic_register_SUITE.erl
+%%
+%% Description:
+%% Test suite for the IFR object registration
+%%
+%%-----------------------------------------------------------------
+-module(ic_register_SUITE).
+
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("orber/include/corba.hrl").
+%%-----------------------------------------------------------------
+%% External exports
+%%-----------------------------------------------------------------
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1, ifr_reg_unreg/1]).
+-export([ifr_inheritence_reg/1,ifr_reg_unreg_with_inheritence/1]).
+-export([ifr_reg_unreg_with_inheritence_bad_order/1]).
+
+%%-----------------------------------------------------------------
+%% Internal exports
+%%-----------------------------------------------------------------
+-export([]).
+
+%%-----------------------------------------------------------------
+%% Macros
+%%-----------------------------------------------------------------
+-define(REMAP_EXCEPT(F), case catch F of
+ {'EXCEPTION', E} -> exit(E);
+ R -> R
+ end).
+%% Standard options to the ic compiler, NOTE unholy use of OutDir
+
+-define(OUT(X), filename:join([?config(priv_dir, Config), gen, to_list(X)])).
+
+
+%%-----------------------------------------------------------------
+%% Func: all/1
+%% Args:
+%% Returns:
+%%-----------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [ifr_reg_unreg, ifr_reg_unreg_with_inheritence,
+ ifr_reg_unreg_with_inheritence_bad_order,
+ ifr_inheritence_reg].
+
+%%-----------------------------------------------------------------
+%% Init and cleanup functions.
+%%-----------------------------------------------------------------
+
+init_per_suite(Config) ->
+ io:format("Setting up.....~n"),
+ mnesia:stop(),
+ mnesia:delete_schema([node()]),
+ mnesia:create_schema([node()]),
+ mnesia:start(),
+ orber:install([node()]),
+ orber:start(),
+ if
+ is_list(Config) ->
+ Config;
+ true ->
+ exit("Config not a list")
+ end.
+
+end_per_suite(Config) ->
+ io:format("Setting down.....~n"),
+ orber:stop(),
+ orber:uninstall(),
+ mnesia:stop(),
+ mnesia:delete_schema([node()]),
+ Config.
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: IFR type registration
+%%-----------------------------------------------------------------
+ifr_reg_unreg(doc) ->
+ ["Checks that the generated register/unregister "
+ "code for the IFR is correct."];
+ifr_reg_unreg(suite) -> [];
+ifr_reg_unreg(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(ifr_reg_unregt_run(Config)).
+
+ifr_reg_unregt_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(ifr_reg_unreg),
+ File0 = filename:join(DataDir, reg_m8),
+ File1 = filename:join(DataDir, reg_m9),
+ File2 = filename:join(DataDir, reg_m10),
+ ?line ok = ic:gen(File0, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File0, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File1, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File1, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File2, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File2, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = compile(OutDir, ifr_reg_unreg_files()),
+ code:add_pathz(OutDir),
+ ?line ok = 'oe_reg_m8':'oe_register'(),
+ ?line ok = 'oe_reg_m9':'oe_register'(),
+ ?line ok = 'oe_reg_m10':'oe_register'(),
+ ?line ok = 'oe_reg_m10':'oe_unregister'(),
+ ?line ok = 'oe_reg_m9':'oe_unregister'(),
+ ?line ok = 'oe_reg_m8':'oe_unregister'(),
+ code:del_path(OutDir),
+ ok.
+
+ifr_reg_unreg_files() -> ['oe_reg_m8', 'oe_reg_m9', 'oe_reg_m10'].
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: IFR registration when object inheritence
+%% is applied and registered.
+%%-----------------------------------------------------------------
+ifr_reg_unreg_with_inheritence(doc) ->
+ ["Checks that the generated register/unregister "
+ "code for the IFR is correct, and works even when"
+ "the object inheritence is registered. This fixes"
+ "two bugs in ifr that caused crash when trying to"
+ "use OE_register/OE_unregister in a sequence of"
+ "compiled files that contained interfaces who"
+ "inherited others in sequence."];
+ifr_reg_unreg_with_inheritence(suite) -> [];
+ifr_reg_unreg_with_inheritence(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(ifr_reg_unreg_with_inheritence_run(Config)).
+
+ifr_reg_unreg_with_inheritence_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(ifr_reg_unreg),
+ File0 = filename:join(DataDir, reg_m8),
+ File1 = filename:join(DataDir, reg_m9),
+ File2 = filename:join(DataDir, reg_m10),
+ File3 = filename:join(DataDir, reg_m11),
+ File4 = filename:join(DataDir, reg_m12),
+ ?line ok = ic:gen(File0, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File0, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File1, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File1, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File2, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File2, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File3, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File3, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File4, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File4, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = compile(OutDir, ifr_reg_unreg_with_inheritence_files()),
+ code:add_pathz(OutDir),
+ ?line ok = 'oe_reg_m8':'oe_register'(),
+ ?line ok = 'oe_reg_m9':'oe_register'(),
+ ?line ok = 'oe_reg_m10':'oe_register'(),
+ ?line ok = 'oe_reg_m11':'oe_register'(),
+ ?line ok = 'oe_reg_m12':'oe_register'(),
+ ?line ok = 'oe_reg_m8':'oe_unregister'(),
+ ?line ok = 'oe_reg_m9':'oe_unregister'(),
+ ?line ok = 'oe_reg_m10':'oe_unregister'(),
+ ?line ok = 'oe_reg_m11':'oe_unregister'(),
+ ?line ok = 'oe_reg_m12':'oe_unregister'(),
+ code:del_path(OutDir),
+ ok.
+
+ifr_reg_unreg_with_inheritence_files() ->
+ ['oe_reg_m8', 'oe_reg_m9', 'oe_reg_m10', 'oe_reg_m11', 'oe_reg_m12'].
+
+
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: IFR registration when object inheritence
+%% is applied and registered in a bad order.
+%% Modules included and used from an ifr object
+%% are not allready registered when the current
+%% object is getting registered.
+%%-----------------------------------------------------------------
+ifr_reg_unreg_with_inheritence_bad_order(doc) ->
+ ["This tests that ifr registration is done with
+ the right write order."
+ "Modules included and used from an ifr object"
+ "are tested if allready registered when the "
+ "current object is getting registered."];
+ifr_reg_unreg_with_inheritence_bad_order(suite) -> [];
+ifr_reg_unreg_with_inheritence_bad_order(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(ifr_reg_unreg_with_inheritence_bad_order_run(Config)).
+
+ifr_reg_unreg_with_inheritence_bad_order_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(ifr_reg_unreg),
+ File1 = filename:join(DataDir, reg_m9),
+ File2 = filename:join(DataDir, reg_m10),
+ File4 = filename:join(DataDir, reg_m12),
+ ?line ok = ic:gen(File1, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File1, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File2, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File2, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File4, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File4, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = compile(OutDir, ifr_reg_unreg_with_inheritence_files()),
+ code:add_pathz(OutDir),
+ case catch 'oe_reg_m12':'oe_register'() of
+ {'EXIT',Reason1} ->
+ io:format("IFR object missing detected : ~p~n",[Reason1]),
+ true;
+ _ ->
+ test_server:fail("Failed to detect object missing : IDL:M1:1.0~n")
+ end,
+ ?line ok = 'oe_reg_m9':'oe_register'(),
+ case catch 'oe_reg_m10':'oe_register'() of
+ {'EXIT',Reason2} ->
+ io:format("IFR object missing detected : ~p~n",[Reason2]),
+ true;
+ _ ->
+ test_server:fail("Failed to detect object missing : IDL:M0:1.0~n")
+ end,
+ ?line ok = 'oe_reg_m9':'oe_unregister'(),
+ code:del_path(OutDir),
+ ok.
+
+
+
+%%-----------------------------------------------------------------
+%% Test Case: IFR registration with inheritence
+%%-----------------------------------------------------------------
+ifr_inheritence_reg(doc) ->
+ ["Checks that IFR object inheritence is correctly registered."];
+ifr_inheritence_reg(suite) -> [];
+ifr_inheritence_reg(Config) when is_list(Config) ->
+ ?REMAP_EXCEPT(ifr_inh_reg_run(Config)).
+
+ifr_inh_reg_run(Config) ->
+ DataDir = ?config(data_dir, Config),
+ OutDir = ?OUT(ifr_reg_unreg),
+ File0 = filename:join(DataDir, reg_m8),
+ File1 = filename:join(DataDir, reg_m9),
+ File2 = filename:join(DataDir, reg_m10),
+ File3 = filename:join(DataDir, reg_m11),
+ File4 = filename:join(DataDir, reg_m12),
+ ?line ok = ic:gen(File0, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File0, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File1, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File1, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File2, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File2, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File3, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File3, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = ic:gen(File4, stdopts(OutDir)++[{preproc_flags,
+ "-I" ++ DataDir}] ),
+ ?line {ok, []} = ic:gen(File4, stdopts(OutDir)++[silent2, {preproc_flags,
+ "-I" ++ DataDir}]),
+ ?line ok = compile(OutDir, ifr_reg_unreg_with_inheritence_files()),
+ code:add_pathz(OutDir),
+ %% OE_register for all files
+ ?line ok = 'oe_reg_m8':'oe_register'(),
+ ?line ok = 'oe_reg_m9':'oe_register'(),
+ ?line ok = 'oe_reg_m10':'oe_register'(),
+ ?line ok = 'oe_reg_m11':'oe_register'(),
+ ?line ok = 'oe_reg_m12':'oe_register'(),
+
+ %% Inheritence registration test
+ OE_IFR = orber_ifr:find_repository(),
+ %% Interfaces that not inherit from other interfaces
+ ?line [] = get_inh(OE_IFR, "IDL:m0/i0:1.0"),
+ ?line [] = get_inh(OE_IFR, "IDL:m1/i1:1.0"),
+ ?line [] = get_inh(OE_IFR, "IDL:m3/i3:1.0"),
+ %% Interfaces that inherit from other interfaces
+ ?line ["IDL:m1/i1:1.0"] = get_inh(OE_IFR, "IDL:m2/i2:1.0"),
+ ?line ["IDL:m1/i1:1.0","IDL:m2/i2:1.0"] = get_inh(OE_IFR, "IDL:m4/i4:1.0"),
+ ?line ["IDL:m3/i3:1.0"] = get_inh(OE_IFR, "IDL:m4/i5:1.0"),
+
+ %% OE_unregister for all files
+ ?line ok = 'oe_reg_m8':'oe_unregister'(),
+ ?line ok = 'oe_reg_m9':'oe_unregister'(),
+ ?line ok = 'oe_reg_m10':'oe_unregister'(),
+ ?line ok = 'oe_reg_m11':'oe_unregister'(),
+ ?line ok = 'oe_reg_m12':'oe_unregister'(),
+ code:del_path(OutDir),
+ ok.
+
+
+get_inh(OE_IFR,ID) ->
+ OE_CURRENT = orber_ifr:lookup_id(OE_IFR,ID),
+ INH_LIST = orber_ifr:get_base_interfaces(OE_CURRENT),
+ case INH_LIST of
+ [] ->
+ io:format("~nInterface ~p inherits from nobody.~n",[ID]),
+ [];
+ _ ->
+ print_inh_list_ids(ID, INH_LIST, [])
+ end.
+
+print_inh_list_ids(_ID, [], Acc) ->
+ lists:reverse(Acc);
+print_inh_list_ids(ID, [H|T], Acc) ->
+ io:format("~n"),
+ Parent = orber_ifr:get_id(H),
+ io:format("Interface ~p inherits from ~p.~n", [ID, Parent]),
+ print_inh_list_ids(ID, T, [Parent|Acc]).
+
+
+
+
+stdopts(OutDir) ->
+ [{outdir, OutDir}, {maxerrs, infinity}].
+
+
+compile(Dir, Files) ->
+ compile(Dir, Files, []).
+
+compile(Dir, Files, Opts) ->
+ {ok, Cwd} = file:get_cwd(),
+ file:set_cwd(Dir),
+ io:format("Changing to ~p~n", [Dir]),
+ case catch do_compile(Files, Opts) of
+ ok ->
+ file:set_cwd(Cwd);
+ Err ->
+ file:set_cwd(Cwd),
+ test_server:fail(Err)
+ end.
+
+do_compile([], _Opts) -> ok;
+do_compile([F | Fs], Opts) ->
+ io:format("Compiling ~p", [F]),
+ case compile:file(F, Opts) of
+ ok ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ {ok, _} ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ {ok, _, _} ->
+ io:format(" ok~n", []),
+ do_load(F, Opts),
+ do_compile(Fs, Opts);
+ Err ->
+ io:format(" error: ~p~n", [Err]),
+ Err
+ end.
+
+do_load(File, Opts) ->
+ case lists:member(load, Opts) of
+ true ->
+ io:format("Loading file ~p", [File]),
+ code:purge(File),
+ R = code:load_abs(File),
+ io:format("Loaded: ~p", [R]);
+ false ->
+ ok
+ end.
+
+
+to_list(X) when is_atom(X) -> atom_to_list(X);
+to_list(X) -> X.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/ic/test/ic_register_SUITE_data/reg_m10.idl b/lib/ic/test/ic_register_SUITE_data/reg_m10.idl
new file mode 100644
index 0000000000..9c1f126b64
--- /dev/null
+++ b/lib/ic/test/ic_register_SUITE_data/reg_m10.idl
@@ -0,0 +1,37 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+// IDL for testing register/unregister in the IFR when using included specs
+//
+#include "reg_m9.idl"
+
+typedef sequence<long> Sequence1;
+
+#include "reg_m8.idl"
+
+module m2 {
+
+ interface i2 : m1::i1
+ {
+ short op3( in long a, inout char b, out long c );
+ };
+
+
+};
+
diff --git a/lib/ic/test/ic_register_SUITE_data/reg_m11.idl b/lib/ic/test/ic_register_SUITE_data/reg_m11.idl
new file mode 100644
index 0000000000..607d695357
--- /dev/null
+++ b/lib/ic/test/ic_register_SUITE_data/reg_m11.idl
@@ -0,0 +1,32 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+// IDL for testing register/unregister in the IFR when using included specs
+//
+
+module m3 {
+
+ interface i3
+ {
+ short op4( in long a, inout char b, out long c );
+ };
+
+
+};
+
diff --git a/lib/ic/test/ic_register_SUITE_data/reg_m12.idl b/lib/ic/test/ic_register_SUITE_data/reg_m12.idl
new file mode 100644
index 0000000000..3dd9267655
--- /dev/null
+++ b/lib/ic/test/ic_register_SUITE_data/reg_m12.idl
@@ -0,0 +1,40 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+// IDL for testing register/unregister in the IFR when using included specs
+// Special case with multiple inheritence.
+//
+#include "reg_m10.idl"
+#include "reg_m11.idl"
+
+module m4 {
+
+ interface i4 : m2::i2
+ {
+ short op5( in long a, inout char b, out long c );
+ };
+
+ interface i5 : m3::i3
+ {
+ short op6( in long a, inout char b, out long c );
+ };
+
+
+};
+
diff --git a/lib/ic/test/ic_register_SUITE_data/reg_m8.idl b/lib/ic/test/ic_register_SUITE_data/reg_m8.idl
new file mode 100644
index 0000000000..dc7432c05d
--- /dev/null
+++ b/lib/ic/test/ic_register_SUITE_data/reg_m8.idl
@@ -0,0 +1,32 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+// IDL for testing register/unregister in the IFR when using included specs
+//
+module m0 {
+
+ interface i0 {
+ void op1( in short c );
+ float op2( in char a);
+
+ };
+
+
+};
+
diff --git a/lib/ic/test/ic_register_SUITE_data/reg_m9.idl b/lib/ic/test/ic_register_SUITE_data/reg_m9.idl
new file mode 100644
index 0000000000..e937c41608
--- /dev/null
+++ b/lib/ic/test/ic_register_SUITE_data/reg_m9.idl
@@ -0,0 +1,32 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+// IDL for testing register/unregister in the IFR when using included specs
+//
+module m1 {
+
+ interface i1 {
+ void op1( in short c );
+ float op2( in char a);
+
+ };
+
+
+};
+
diff --git a/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl
new file mode 100644
index 0000000000..407c3d2d44
--- /dev/null
+++ b/lib/ic/test/java_client_erl_server_SUITE.erl
@@ -0,0 +1,340 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : Test suite for the backends of the IDL compiler
+%%%----------------------------------------------------------------------
+
+-module(java_client_erl_server_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1,end_per_suite/1,
+ init_per_testcase/2,end_per_testcase/2]).
+-export([marshal_ll/1,marshal_ull/1,
+ marshal_l/1,marshal_ul/1,
+ marshal_s/1,marshal_us/1,
+ marshal_c/1,marshal_wc/1,
+ marshal_str/1,
+ marshal_any_3/1,marshal_any_2/1]).
+
+
+%% Top of cases
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [marshal_ll, marshal_ull, marshal_l, marshal_ul,
+ marshal_s, marshal_us, marshal_c, marshal_wc,
+ marshal_str, marshal_any_3, marshal_any_2].
+
+init_per_suite(Config) when is_list(Config) ->
+ case case code:priv_dir(jinterface) of
+ {error,bad_name} ->
+ false;
+ P ->
+ filelib:is_dir(P)
+ end
+ of
+ true ->
+ case find_executable(["java"]) of
+ false ->
+ {skip,"Found no Java VM"};
+ Path ->
+ [{java,Path}|Config]
+ end;
+ false ->
+ {skip,"No jinterface application"}
+ end.
+
+
+find_executable([]) ->
+ false;
+find_executable([E|T]) ->
+ case os:find_executable(E) of
+ false -> find_executable(T);
+ Path -> Path
+ end.
+
+end_per_suite(Config) -> Config.
+
+
+
+%% Add/remove code path and watchdog before/after each test case.
+%%
+init_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:add_patha(DataDir),
+
+ %% Since other test suites use the module m_i et,al, we have
+ %% to make sure we are using the right modules.
+ code:purge(m_i),
+ code:purge(m_i_impl),
+ code:purge(oe_java_erl_test),
+ code:load_file(m_i),
+ code:load_file(m_i_impl),
+ code:load_file(oe_java_erl_test),
+
+ WatchDog = test_server:timetrap(test_server:seconds(20)),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ DataDir = ?config(data_dir, Config),
+ code:del_path(DataDir),
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+
+
+%%--------------------------------------------------------------------
+%%
+%% Test cases
+
+marshal_ll(doc) ->
+ ["Testing marshalling of IDL long long"];
+marshal_ll(suite) -> [];
+marshal_ll(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_ll}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_ll]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_ull(doc) ->
+ ["Testing marshalling of IDL unsigned long long"];
+marshal_ull(suite) -> [];
+marshal_ull(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_ull}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_ull]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_l(doc) ->
+ ["Testing marshalling of IDL long"];
+marshal_l(suite) -> [];
+marshal_l(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_l}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_l]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_ul(doc) ->
+ ["Testing marshalling of IDL unsigned long"];
+marshal_ul(suite) -> [];
+marshal_ul(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_ul}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_ul]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_s(doc) ->
+ ["Testing marshalling of IDL short"];
+marshal_s(suite) -> [];
+marshal_s(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_s}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_s]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_us(doc) ->
+ ["Testing marshalling of IDL unsigned short"];
+marshal_us(suite) -> [];
+marshal_us(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_us}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_us]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_c(doc) ->
+ ["Testing marshalling of IDL char"];
+marshal_c(suite) -> [];
+marshal_c(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_c}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_c]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_wc(doc) ->
+ ["Testing marshalling of IDL char"];
+marshal_wc(suite) -> [];
+marshal_wc(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_wc}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_wc]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_str(doc) ->
+ ["Testing marshalling of IDL string"];
+marshal_str(suite) -> [];
+marshal_str(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_str}),
+ ?line ok = java(?config(java, Config), DataDir,
+%%% "-DOtpConnection.trace=4 "
+ "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_str]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_any_3(doc) ->
+ ["Testing marshalling of IDL any"];
+marshal_any_3(suite) -> [];
+marshal_any_3(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_any_3}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_any_3]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+marshal_any_2(doc) ->
+ ["Testing marshalling of IDL any"];
+marshal_any_2(suite) -> [];
+marshal_any_2(Config) when is_list(Config) ->
+ ?line DataDir = ?config(data_dir, Config),
+ ?line {ok,Server} = m_i:oe_create_link([], {local,marshal_any_2}),
+ ?line ok = java(?config(java, Config), DataDir, "JavaClient",
+ ["JavaClient",node(),erlang:get_cookie(),marshal_any_2]),
+ ?line ok = m_i:stop(Server),
+ ok.
+
+%%--------------------------------------------------------------------
+%%
+%% Utilities
+
+
+java(Java, Dir, ClassAndArgs) ->
+ cmd(Java++" -classpath "++classpath(Dir)++" "++ClassAndArgs).
+
+java(Java, Dir, Class, Args) ->
+ java(Java, Dir, Class++" "++to_string(Args)).
+
+to_string([H|T]) when is_integer(H) ->
+ integer_to_list(H)++" "++to_string(T);
+to_string([H|T]) when is_atom(H) ->
+ atom_to_list(H)++" "++to_string(T);
+to_string([H|T]) when is_list(H) ->
+ lists:flatten(H)++" "++to_string(T);
+to_string([]) -> [].
+
+% javac(Dir, File) ->
+% cmd("javac -d "++Dir++" -classpath "++classpath(Dir)++" "++
+% filename:join(Dir, File)).
+
+classpath(Dir) ->
+ PS =
+ case os:type() of
+ {win32, _} -> ";";
+ _ -> ":"
+ end,
+ Dir++PS++
+ filename:join([code:lib_dir(ic),"priv","ic.jar"])++PS++
+ filename:join([code:lib_dir(jinterface),"priv","OtpErlang.jar"])++PS++
+ case os:getenv("CLASSPATH") of
+ false -> "";
+ Classpath -> Classpath
+ end.
+
+
+cmd(Cmd) ->
+ PortOpts = [{line,80},eof,exit_status,stderr_to_stdout],
+ io:format("<cmd> ~s~n", [Cmd]),
+ case catch open_port({spawn,Cmd}, PortOpts) of
+ Port when is_port(Port) ->
+ Result = cmd_loop(Port, []),
+ io:format("<cmd=~w>~n", [Result]),
+ case Result of
+ 0 -> ok;
+ ExitCode when is_integer(ExitCode) -> {error,ExitCode};
+ Error -> Error
+ end;
+ {'EXIT',Reason} ->
+ {error,Reason}
+ end.
+
+cmd_loop(Port, Line) ->
+ receive
+ {Port,eof} ->
+ receive
+ {Port,{exit_status,ExitStatus}} ->
+ ExitStatus
+ after 1 ->
+ undefined
+ end;
+ {Port,{exit_status,ExitStatus}} ->
+ receive
+ {Port,eof} ->
+ ok after 1 -> ok end,
+ ExitStatus;
+ {Port,{data,{Tag,Data}}} ->
+ case Tag of
+ eol ->
+ io:put_chars([Line|cr_to_nl(Data)]),
+ io:nl(),
+ cmd_loop(Port, []);
+ noeol ->
+ cmd_loop(Port, [Line|cr_to_nl(Data)])
+ end;
+ {'EXIT',Port,Reason} ->
+ {error,Reason};
+ Other ->
+ io:format("WARNING: Unexpected at ~s:~p: ~p~n",
+ [?MODULE_STRING,?LINE,Other]),
+ cmd_loop(Port, Line)
+ end.
+
+%% Convert lonely CR to NL, and CRLF to NL
+%%
+cr_to_nl([$\r,$\n|T]) ->
+ [$\n|cr_to_nl(T)];
+cr_to_nl([$\r|T]) ->
+ [$\n|cr_to_nl(T)];
+cr_to_nl([C|T]) ->
+ [C|cr_to_nl(T)];
+cr_to_nl([]) ->
+ [].
diff --git a/lib/ic/test/java_client_erl_server_SUITE_data/JavaClient.java b/lib/ic/test/java_client_erl_server_SUITE_data/JavaClient.java
new file mode 100644
index 0000000000..7da5a99c03
--- /dev/null
+++ b/lib/ic/test/java_client_erl_server_SUITE_data/JavaClient.java
@@ -0,0 +1,759 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2011. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ *
+ */
+public class JavaClient {
+
+ public static void main(String[] argv)
+ {
+ System.out.println("Hello World!");
+ if (argv.length < 4) {
+ System.out.println("Too few arguments!");
+ System.exit(1);
+ }
+ // for (int j = 0; j < argv.length; j++)
+ // System.out.println(argv[j]);
+ try {
+ if (argv[3].equals("marshal_ll")) {
+ System.out.println("marshal_ll");
+ marshal_ll(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_ull")) {
+ marshal_ull(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_l")) {
+ marshal_l(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_ul")) {
+ marshal_ul(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_s")) {
+ marshal_s(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_us")) {
+ marshal_us(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_c")) {
+ marshal_c(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_wc")) {
+ marshal_wc(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_str")) {
+ marshal_str(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_any_3")) {
+ marshal_any_3(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else if (argv[3].equals("marshal_any_2")) {
+ marshal_any_2(argv[0], argv[1], argv[2], argv[3]);
+ }
+ else {
+ System.out.println("Unknown test: "+argv[3]);
+ System.exit(2);
+ }
+ } catch (java.lang.Exception e) {
+ System.out.println("Exception!: "+e);
+ System.exit(3);
+ }
+ System.exit(0);
+ }
+
+
+
+ static void marshal_ll(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ System.out.println("Just warming up.."+i);
+ verify_ll(i, 3, 2, 1);
+ verify_ll(i, 5, 4, 3);
+ verify_ll(i, -128, 0, 1);
+ // The small integer border
+ verify_ll(i, 255, 0, 1);
+ verify_ll(i, 256, 0, 1);
+ // The integer border
+ verify_ll(i, (1L<<26)-1L, 0L, 1);
+ verify_ll(i, 1L<<26, 0L, 1);
+ verify_ll(i, -(1L<<26), 0L, 1);
+ verify_ll(i, (1L<<27)-1L, 0L, 1);
+ verify_ll(i, 1L<<27, 0L, 1);
+ verify_ll(i, -(1L<<27), 0L, 1);
+ // Bignum byte borders
+ verify_ll(i, (1L<<32)-1L, 0L, 1);
+ verify_ll(i, 1L<<32, 0L, 1);
+ verify_ll(i, -(1L<<32)+1L, 0L, 1);
+ verify_ll(i, -(1L<<32), 0L, 1);
+ verify_ll(i, (1L<<40)-1L, 0L, 1);
+ verify_ll(i, 1L<<40, 0L, 1);
+ verify_ll(i, -(1L<<40)+1L, 0L, 1);
+ verify_ll(i, -(1L<<40), 0L, 1);
+ verify_ll(i, (1L<<48)-1L, 0L, 1);
+ verify_ll(i, 1L<<48, 0L, 1);
+ verify_ll(i, -(1L<<48)+1L, 0L, 1);
+ verify_ll(i, -(1L<<48), 0L, 1);
+ // Java long border
+ verify_ll(i, java.lang.Long.MAX_VALUE, 0L, 1);
+ verify_ll(i, java.lang.Long.MIN_VALUE, 0L, 1);
+ verify_ll(i, -1L, 0L, 1);
+ // Impossible decodes
+ verify_ll_bad(i, java.lang.Long.MAX_VALUE, -1L, 1);
+ verify_ll_bad(i, java.lang.Long.MIN_VALUE, 1L, 1);
+ verify_ll_bad(i, java.lang.Long.MIN_VALUE, 0L, -1);
+ verify_ll_bad(i, java.lang.Long.MAX_VALUE, -1L, 2);
+ verify_ll_bad(i, java.lang.Long.MIN_VALUE, 0L, 2);
+ }
+
+ static void marshal_ull(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_ull(i, 3, 2, 1);
+ verify_ull(i, 5, 4, 3);
+ // The small integer border
+ verify_ull(i, 255, 0, 1);
+ verify_ull(i, 256, 0, 1);
+ // The integer border
+ verify_ull(i, (1L<<26)-1L, 0L, 1);
+ verify_ull(i, 1L<<26, 0L, 1);
+ verify_ull(i, (1L<<27)-1L, 0L, 1);
+ verify_ull(i, 1L<<27, 0L, 1);
+ // Bignum byte borders
+ verify_ull(i, (1L<<32)-1L, 0L, 1);
+ verify_ull(i, 1L<<32, 0L, 1);
+ verify_ull(i, (1L<<40)-1L, 0L, 1);
+ verify_ull(i, 1L<<40, 0L, 1);
+ verify_ull(i, (1L<<48)-1L, 0L, 1);
+ verify_ull(i, 1L<<48, 0L, 1);
+ // Java long border
+ verify_ull(i, java.lang.Long.MAX_VALUE, 0L, 1);
+ verify_ull(i, java.lang.Long.MIN_VALUE, 0L, 1);
+ verify_ull(i, -1L, 0L, 1);
+ verify_ull(i, java.lang.Long.MAX_VALUE,
+ java.lang.Long.MIN_VALUE, 1);
+ // Impossible decodes
+ verify_ull_bad(i, -1L, -1L, 1);
+ verify_ull_bad(i, java.lang.Long.MAX_VALUE, -1L, 2);
+ }
+
+ static void marshal_l(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_l(i, 3, 2, 1);
+ verify_l(i, 5, 4, 3);
+ verify_l(i, -128, 0, 1);
+ // The small integer border
+ verify_l(i, 255, 0, 1);
+ verify_l(i, 256, 0, 1);
+ // The integer border
+ verify_l(i, (1<<26)-1, 0, 1);
+ verify_l(i, 1<<26, 0, 1);
+ verify_l(i, -(1<<26), 0, 1);
+ verify_l(i, (1<<27)-1, 0, 1);
+ verify_l(i, 1<<27, 0, 1);
+ verify_l(i, -(1<<27), 0, 1);
+ // Java int border
+ verify_l(i, java.lang.Integer.MAX_VALUE, 0, 1);
+ verify_l(i, java.lang.Integer.MIN_VALUE, 0, 1);
+ // Impossible decodes
+ verify_l_bad(i, java.lang.Integer.MAX_VALUE, -1, 1);
+ verify_l_bad(i, java.lang.Integer.MIN_VALUE, 1, 1);
+ verify_l_bad(i, java.lang.Integer.MIN_VALUE, 0, -1);
+ }
+
+ static void marshal_ul(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_ul(i, 3, 2, 1);
+ verify_ul(i, 5, 4, 3);
+ // The small integer border
+ verify_ul(i, 255, 0, 1);
+ verify_ul(i, 256, 0, 1);
+ // The integer border
+ verify_ul(i, (1<<26)-1, 0, 1);
+ verify_ul(i, 1<<26, 0, 1);
+ verify_ul(i, (1<<27)-1, 0, 1);
+ verify_ul(i, 1<<27, 0, 1);
+ // Java int border
+ verify_ul(i, java.lang.Integer.MAX_VALUE, 0, 1);
+ verify_ul(i, java.lang.Integer.MIN_VALUE, 0, 1);
+ verify_ul(i, -1, 0, 1);
+ verify_ul(i, java.lang.Integer.MAX_VALUE,
+ java.lang.Integer.MIN_VALUE, 1);
+ // Impossible decodes
+ verify_ul_bad(i, -1, -1, 1);
+ }
+
+ static void marshal_s(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_s(i, 3, 2, 1);
+ verify_s(i, 5, 4, 3);
+ verify_s(i, -128, 0, 1);
+ // The small integer border
+ verify_s(i, 255, 0, 1);
+ verify_s(i, 256, 0, 1);
+ // Java short border
+ verify_s(i, java.lang.Short.MAX_VALUE, 0, 1);
+ verify_s(i, java.lang.Short.MIN_VALUE, 0, 1);
+ // Impossible decodes
+ verify_s_bad(i, java.lang.Short.MAX_VALUE, -1, 1);
+ verify_s_bad(i, java.lang.Short.MIN_VALUE, 1, 1);
+ verify_s_bad(i, java.lang.Short.MIN_VALUE, 0, -1);
+ }
+
+ static void marshal_us(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_us(i, 3, 2, 1);
+ verify_us(i, 5, 4, 3);
+ // The small integer border
+ verify_us(i, 255, 0, 1);
+ verify_us(i, 256, 0, 1);
+ // Java short border
+ verify_us(i, java.lang.Short.MAX_VALUE, 0, 1);
+ verify_us(i, java.lang.Short.MIN_VALUE, 0, 1);
+ verify_us(i, -1, 0, 1);
+ verify_us(i, java.lang.Short.MAX_VALUE,
+ java.lang.Short.MIN_VALUE, 1);
+ // Impossible decodes
+ verify_us_bad(i, -1, -1, 1);
+ }
+
+ static void marshal_c(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_c(i, '\3', '\2', 1);
+ verify_c(i, '\5', '\4', 3);
+ // The small integer border
+ verify_c(i, '\u00FF', '\0', 1);
+ verify_c(i, '\u0100', '\0', 1);
+ // Java char border
+ verify_c(i, java.lang.Character.MAX_VALUE, '\0', 1);
+ verify_c(i, java.lang.Character.MIN_VALUE, '\0', 1);
+ verify_c(i, java.lang.Character.MAX_VALUE,
+ java.lang.Character.MIN_VALUE, 1);
+ // Impossible decodes
+ verify_c_bad(i, '\u8000', '\0', 2);
+ }
+
+ static void marshal_wc(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_wc(i, '\3', '\2', 1);
+ verify_wc(i, '\5', '\4', 3);
+ // The small integer border
+ verify_wc(i, '\u00FF', '\0', 1);
+ verify_wc(i, '\u0100', '\0', 1);
+ // Java char border
+ verify_wc(i, java.lang.Character.MAX_VALUE, '\0', 1);
+ verify_wc(i, java.lang.Character.MIN_VALUE, '\0', 1);
+ verify_wc(i, java.lang.Character.MAX_VALUE,
+ java.lang.Character.MIN_VALUE, 1);
+ // Impossible decodes
+ verify_c_bad(i, '\u8000', '\0', 2);
+ }
+
+ static void marshal_str(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ // Just warming up..
+ verify_str(i, 100, 100);
+ verify_str(i, 100, 1);
+ // Erlang string border
+ verify_str(i, 65535, 1);
+ verify_str(i, 2, 65535);
+ // Erlang string border out
+ verify_str(i, 65536, 1);
+ verify_str(i, 65536, 65536);
+ }
+
+ static void marshal_any_3(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ com.ericsson.otp.ic.Any x = new com.ericsson.otp.ic.Any();
+ com.ericsson.otp.ic.Any y = new com.ericsson.otp.ic.Any();
+ com.ericsson.otp.ic.Any z = new com.ericsson.otp.ic.Any();
+
+ x.insert_longlong(java.lang.Long.MAX_VALUE);
+ y.insert_longlong(1L);
+ z.insert_longlong(java.lang.Long.MAX_VALUE-1L);
+ System.out.println("verify_any_3 longlong max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_longlong(java.lang.Long.MIN_VALUE);
+ y.insert_longlong(-1L);
+ z.insert_longlong(java.lang.Long.MIN_VALUE+1L);
+ System.out.println("verify_any_3 longlong min");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_ulonglong(-1L);
+ y.insert_longlong(1L);
+ z.insert_ulonglong(-2L);
+ System.out.println("verify_any_3 ulonglong max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_ulonglong(0L);
+ y.insert_longlong(-1L);
+ z.insert_ulonglong(1L);
+ System.out.println("verify_any_3 ulonglong min");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_long(java.lang.Integer.MAX_VALUE);
+ y.insert_long(1);
+ z.insert_long(java.lang.Integer.MAX_VALUE-1);
+ System.out.println("verify_any_3 long max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_long(java.lang.Integer.MIN_VALUE);
+ y.insert_long(-1);
+ z.insert_long(java.lang.Integer.MIN_VALUE+1);
+ System.out.println("verify_any_3 long min");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_ulong(-1);
+ y.insert_long(1);
+ z.insert_ulong(-2);
+ System.out.println("verify_any_3 ulong max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_ulong(0);
+ y.insert_long(-1);
+ z.insert_ulong(1);
+ System.out.println("verify_any_3 ulong min");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_short(java.lang.Short.MAX_VALUE);
+ y.insert_short((short)1);
+ z.insert_short((short)(java.lang.Short.MAX_VALUE-1));
+ System.out.println("verify_any_3 short max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_short(java.lang.Short.MIN_VALUE);
+ y.insert_short((short)-1);
+ z.insert_short((short)(java.lang.Short.MIN_VALUE+1));
+ System.out.println("verify_any_3 short min");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_ushort((short)-1);
+ y.insert_short((short)1);
+ z.insert_ushort((short)-2);
+ System.out.println("verify_any_3 ushort max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_ushort((short)0);
+ y.insert_short((short)-1);
+ z.insert_ushort((short)1);
+ System.out.println("verify_any_3 ushort min");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_char('\377');
+ y.insert_char('\1');
+ z.insert_char('\376');
+ System.out.println("verify_any_3 char max");
+ verify_any_3(i, x, y, 1, z);
+
+ x.insert_wchar('\uFFFF');
+ y.insert_wchar('\u0001');
+ z.insert_wchar('\uFFFE');
+ System.out.println("verify_any_3 char max");
+ verify_any_3(i, x, y, 1, z);
+ }
+
+ static void marshal_any_2(String selfNode, String peerNode,
+ String cookie, String serverName)
+ throws java.lang.Exception
+ {
+ m._iStub i = new m._iStub(selfNode, peerNode, cookie, serverName);
+ m.s s = new m.s();
+ com.ericsson.otp.ic.Any a = new com.ericsson.otp.ic.Any();
+ //
+ s.ull_x = -1L;
+ s.ll_x = java.lang.Long.MAX_VALUE;
+ s.ll_y = 1L;
+ s.ull_z = -2L;
+ s.ll_z = java.lang.Long.MAX_VALUE-1L;
+ //
+ s.ul_x = -1;
+ s.l_x = java.lang.Integer.MAX_VALUE;
+ s.l_y = 1;
+ s.ul_z = -2;
+ s.l_z = java.lang.Integer.MAX_VALUE-1;
+ //
+ s.us_x = (short)-1;
+ s.s_x = java.lang.Short.MAX_VALUE;
+ s.s_y = (short)1;
+ s.us_z = (short)-2;
+ s.s_z = (short)(java.lang.Short.MAX_VALUE-1);
+ //
+ s.c_x = '\377';
+ s.c_y = '\1';
+ s.c_z = '\376';
+ s.wc_x = '\uFFFF';
+ s.wc_y = '\u0001';
+ s.wc_z = '\uFFFE';
+ m.sHelper.insert(a, s);
+ verify_any_2(i, a, 1);
+
+ s.ull_x = 0L;
+ s.ll_x = java.lang.Long.MIN_VALUE;
+ s.ll_y = -1L;
+ s.ull_z = 1L;
+ s.ll_z = java.lang.Long.MIN_VALUE+1L;
+ //
+ s.ul_x = 0;
+ s.l_x = java.lang.Integer.MIN_VALUE;
+ s.l_y = -1;
+ s.ul_z = 1;
+ s.l_z = java.lang.Integer.MIN_VALUE+1;
+ //
+ s.us_x = (short)0;
+ s.s_x = java.lang.Short.MIN_VALUE;
+ s.s_y = (short)-1;
+ s.us_z = (short)1;
+ s.s_z = (short)(java.lang.Short.MIN_VALUE+1);
+ //
+ s.c_x = '\0';
+ s.c_y = '\0';
+ s.c_z = '\0';
+ s.wc_x = '\u0000';
+ s.wc_y = '\u0000';
+ s.wc_z = '\u0000';
+ m.sHelper.insert(a, s);
+ verify_any_2(i, a, 1);
+ }
+
+
+ static void verify_ll(m._iStub i, long x, long y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ System.out.println("verify_ll "+a);
+ a.ll_x = x;
+ a.ll_y = y;
+ long expected = (x - y)*(short)b;
+ long result = i.marshal_ll(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_ll("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_ll("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_ull(m._iStub i, long x, long y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.ull_x = x;
+ a.ll_y = y;
+ long expected = (x - y)*(short)b;
+ long result = i.marshal_ull(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_ull("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_ull("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_l(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.l_x = x;
+ a.l_y = y;
+ int expected = (x - y)*(short)b;
+ int result = i.marshal_l(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_l("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_l("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_ul(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.ul_x = x;
+ a.l_y = y;
+ int expected = (x - y)*(short)b;
+ int result = i.marshal_ul(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_ul("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_ul("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_s(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.s_x = (short)x;
+ a.s_y = (short)y;
+ short expected = (short)((x - y)*(short)b);
+ short result = i.marshal_s(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_s("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_s("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_us(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.us_x = (short)x;
+ a.s_y = (short)y;
+ short expected = (short)((x - y)*(short)b);
+ short result = i.marshal_us(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_us("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_us("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_c(m._iStub i, char x, char y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.c_x = x;
+ a.c_y = y;
+ char expected = (char)(((int)x - (int)y)*(short)b);
+ char result = i.marshal_c(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_c("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_c("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_wc(m._iStub i, char x, char y, int b)
+ throws java.lang.Exception
+ {
+ m.s a = new m.s();
+ a.wc_x = x;
+ a.wc_y = y;
+ char expected = (char)(((int)x - (int)y)*(short)b);
+ char result = i.marshal_wc(a, (short)b);
+ if (result == expected) {
+ System.out.println("verify_wc("+x+", "+y+", "+b+") => "
+ +result);
+ } else {
+ System.out.println("verify_wc("+x+", "+y+", "+b+") => "
+ +result+" != "+expected);
+ System.exit(4);
+ }
+ }
+
+ static void verify_str(m._iStub i, int a_len, int b_len)
+ throws java.lang.Exception
+ {
+ String a = mk_str(a_len);
+ String b = mk_str(b_len);
+ String expected = a + b;
+ String result = i.strcat(a, b);
+ if (result.equals(expected)) {
+ System.out.println("verify_str(\""+a+"\", \""+b+"\") => \""
+ +result+"\"");
+ } else {
+ System.out.println("verify_str(\""+a+"\", \""+b+"\") => \""
+ +result+"\" != \""+expected.length()+"\"");
+ System.exit(4);
+ }
+ }
+
+ static String mk_str(int len)
+ throws StringIndexOutOfBoundsException
+ {
+ StringBuffer s = new StringBuffer();
+ // 17 characters is prime relative all bases of two - on purpose
+ do s.append("qwertyuiopasdfghj"); while (s.length() < len);
+ return s.substring(0, len);
+ }
+
+ static void verify_any_3(m._iStub i,
+ com.ericsson.otp.ic.Any x,
+ com.ericsson.otp.ic.Any y,
+ int b,
+ com.ericsson.otp.ic.Any expected)
+ throws java.lang.Exception
+ {
+ com.ericsson.otp.ic.Any result = i.marshal_any_3(x, y, (short)b);
+ if (! expected.equal(result)) {
+ System.exit(4);
+ }
+ }
+
+ static void verify_any_2(m._iStub i, com.ericsson.otp.ic.Any a, int b)
+ throws java.lang.Exception
+ {
+ com.ericsson.otp.ic.Any result = i.marshal_any_2(a, (short)b);
+ if (! a.equal(result)) {
+ System.exit(4);
+ }
+ }
+
+
+
+ static void verify_ll_bad(m._iStub i, long x, long y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_ll(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_ull_bad(m._iStub i, long x, long y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_ull(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_l_bad(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_l(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_ul_bad(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_ul(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_s_bad(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_s(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_us_bad(m._iStub i, int x, int y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_us(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_c_bad(m._iStub i, char x, char y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_c(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+ static void verify_wc_bad(m._iStub i, char x, char y, int b)
+ throws java.lang.Exception
+ {
+ try {
+ verify_wc(i, x, y, b);
+ System.out.println("Expected exception missing!");
+ System.exit(5);
+ } catch (com.ericsson.otp.erlang.OtpErlangDecodeException e) {
+ System.out.println("Expected exception: "+e);
+ }
+ }
+
+}
diff --git a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..5e190fe1a5
--- /dev/null
+++ b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src
@@ -0,0 +1,88 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Makefile.src for java_client_erl_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .erl .idl .@EMULATOR@ .java
+
+
+JAVAC = @JAVAC@
+ERLC = erlc
+
+# ic variables available from ts:
+#
+# ic_libpath: @ic_libpath@
+# ic_include_path: @ic_include_path@
+
+IC_INCLUDE_PATH = @ic_include_path@
+IC_CLASSPATH = @ic_classpath@
+
+JINTERFACE_CLASSPATH = @jinterface_classpath@
+
+CLASSPATH = .@PS@$(IC_CLASSPATH)@PS@$(JINTERFACE_CLASSPATH)@PS@
+
+GEN_JAVA_FILES = \
+ m@DS@_iImplBase.java \
+ m@DS@_iStub.java \
+
+GEN_HRL_FILES = \
+ m.hrl \
+ m_i.hrl \
+ oe_java_erl_test.hrl
+
+GEN_ERL_FILES = \
+ m_i.erl \
+ oe_java_erl_test.erl
+
+JAVA_FILES = $(GEN_JAVA_FILES) JavaClient.java
+CLASS_FILES = $(JAVA_FILES:.java=.class)
+ERL_FILES = $(GEN_ERL_FILES) m_i_impl.erl
+EBINS = $(ERL_FILES:.erl=.@EMULATOR@)
+
+
+all: $(CLASS_FILES) $(EBINS)
+
+clean:
+ -rm -f $(GEN_JAVA_FILES) $(CLASS_FILES) \
+ $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS)
+ -del /F /Q $(GEN_JAVA_FILES) $(CLASS_FILES) \
+ $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS)
+
+$(GEN_JAVA_FILES) : java_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,java}" java_erl_test.idl
+
+$(CLASS_FILES) : $(JAVA_FILES)
+ $(JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES)
+
+$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.idl
+ $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" java_erl_test.idl
+
+.erl.@EMULATOR@:
+ $(ERLC) -I $(IC_INCLUDE_PATH) $<
diff --git a/lib/ic/test/java_client_erl_server_SUITE_data/java_erl_test.idl b/lib/ic/test/java_client_erl_server_SUITE_data/java_erl_test.idl
new file mode 100644
index 0000000000..b72c972d3b
--- /dev/null
+++ b/lib/ic/test/java_client_erl_server_SUITE_data/java_erl_test.idl
@@ -0,0 +1,68 @@
+
+
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 2003-2010. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+module m {
+
+ struct s {
+ long long ll_x;
+ unsigned long long ull_x;
+ long long ll_y;
+ long long ll_z;
+ unsigned long long ull_z;
+
+ long l_x;
+ unsigned long ul_x;
+ long l_y;
+ long l_z;
+ unsigned long ul_z;
+
+ short s_x;
+ unsigned short us_x;
+ short s_y;
+ short s_z;
+ unsigned short us_z;
+
+ char c_x;
+ char c_y;
+ char c_z;
+
+ wchar wc_x;
+ wchar wc_y;
+ wchar wc_z;
+ };
+
+ interface i {
+ long long marshal_ll( in s a, in short b );
+ unsigned long long marshal_ull( in s a, in short b );
+
+ long marshal_l( in s a, in short b );
+ unsigned long marshal_ul( in s a, in short b );
+
+ short marshal_s( in s a, in short b );
+ unsigned short marshal_us( in s a, in short b );
+
+ char marshal_c( in s a, in short b );
+ wchar marshal_wc( in s a, in short b );
+
+ string strcat( in string a, in string b );
+
+ any marshal_any_3( in any x, in any y, in short b );
+ any marshal_any_2( in any a, in short b );
+ };
+
+};
diff --git a/lib/ic/test/java_client_erl_server_SUITE_data/m_i_impl.erl b/lib/ic/test/java_client_erl_server_SUITE_data/m_i_impl.erl
new file mode 100644
index 0000000000..20959b549d
--- /dev/null
+++ b/lib/ic/test/java_client_erl_server_SUITE_data/m_i_impl.erl
@@ -0,0 +1,169 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%--------------------------------------------------------------------
+-module(m_i_impl).
+
+-export([marshal_ll/3,marshal_ull/3,
+ marshal_l/3,marshal_ul/3,
+ marshal_s/3,marshal_us/3,
+ marshal_c/3,marshal_wc/3,
+ strcat/3,
+ marshal_any_3/4,marshal_any_2/3]).
+-export([init/1,terminate/2,code_change/3]).
+
+-include("m.hrl").
+
+-define(TK_M_S, {tk_struct,
+ "IDL:m/s:1.0",
+ "s",
+ [{"ll_x",tk_longlong},
+ {"ull_x",tk_ulonglong},
+ {"ll_y",tk_longlong},
+ {"ll_z",tk_longlong},
+ {"ull_z",tk_ulonglong},
+ {"l_x",tk_long},
+ {"ul_x",tk_ulong},
+ {"l_y",tk_long},
+ {"l_z",tk_long},
+ {"ul_z",tk_ulong},
+ {"s_x",tk_short},
+ {"us_x",tk_ushort},
+ {"s_y",tk_short},
+ {"s_z",tk_short},
+ {"us_z",tk_ushort},
+ {"c_x",tk_char},
+ {"c_y",tk_char},
+ {"c_z",tk_char},
+ {"wc_x",tk_wchar},
+ {"wc_y",tk_wchar},
+ {"wc_z",tk_wchar}|_]}).
+
+
+
+marshal_ll(State, #m_s{ll_x = X, ll_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+marshal_ull(State, #m_s{ull_x = X, ll_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+
+marshal_l(State, #m_s{l_x = X, l_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+marshal_ul(State, #m_s{ul_x = X, l_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+
+marshal_s(State, #m_s{s_x = X, s_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+marshal_us(State, #m_s{us_x = X, s_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+
+marshal_c(State, #m_s{c_x = X, c_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+marshal_wc(State, #m_s{wc_x = X, wc_y = Y}=_A, B) when integer(B) ->
+ R = (X - Y)*B,
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+strcat(State, A, B) when list(A), list(B) ->
+ R = A++B,
+ io:format("~p", [{?MODULE,?LINE,[length(A),length(B),A,B,R]}]),
+ {reply, R, State};
+strcat(State, A, B) ->
+ io:format("~p", [{?MODULE,?LINE,[A,B]}]),
+ {reply, [], State}.
+
+marshal_any_3(State, {any,TkX,_}=X, {any,_,_}=Y, B) when integer(B) ->
+ R = any(mul(sub(any(X), any(Y)), B), TkX),
+ io:format("~p", [{?MODULE,?LINE,[X,Y,B,R]}]),
+ {reply, R, State}.
+
+marshal_any_2(State,
+ {any,TkA,#m_s{ll_x=LL_X, ull_x=ULL_X, ll_y=LL_Y,
+ l_x=L_X, ul_x=UL_X, l_y=L_Y,
+ s_x=S_X, us_x=US_X, s_y=S_Y,
+ c_x=C_X, c_y=C_Y,
+ wc_x=WC_X, wc_y=WC_Y} = A},
+ B) when integer(B) ->
+ {check_type_code,?TK_M_S} = {check_type_code,TkA},
+ ULL_Z = (ULL_X - LL_Y) * B,
+ LL_Z = (LL_X - LL_Y) * B,
+ UL_Z = (UL_X - L_Y) * B,
+ L_Z = (L_X - L_Y) * B,
+ US_Z = (US_X - S_Y) * B,
+ S_Z = (S_X - S_Y) * B,
+ C_Z = (C_X - C_Y) * B,
+ WC_Z = (WC_X - WC_Y) * B,
+ R = A#m_s{ll_z=LL_Z, ull_z=ULL_Z,
+ l_z=L_Z, ul_z=UL_Z,
+ s_z=S_Z, us_z=US_Z,
+ c_z=C_Z, wc_z=WC_Z},
+ io:format("~p", [{?MODULE,?LINE,[A,B,R]}]),
+ {reply, {any,TkA,R}, State}.
+
+
+
+init(_Env) ->
+ {ok, []}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+any({any,tk_longlong,X}) -> X;
+any({any,tk_long,X}) -> X;
+any({any,tk_short,X}) -> X;
+any({any,tk_ulonglong,X}) -> X;
+any({any,tk_ulong,X}) -> X;
+any({any,tk_ushort,X}) -> X;
+any({any,tk_char,X}) -> X;
+any({any,tk_wchar,X}) -> X.
+
+any(X, Tk) when integer(X) -> {any,Tk,X}.
+
+sub(X, Y) when integer(X), integer(Y) ->
+ X - Y.
+
+mul(X, Y) when integer(X), integer(Y) ->
+ X * Y.
+
+napp(0, L) -> L;
+napp(N, L) when integer(N), N >= 1 -> napp(N-1, L)++L.
diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk
index e0fccf4889..6d6c7fa625 100644
--- a/lib/ic/vsn.mk
+++ b/lib/ic/vsn.mk
@@ -1,18 +1 @@
-IC_VSN = 4.2.24
-
-TICKETS = OTP-8307 \
- OTP-8353 \
- OTP-8354 \
- OTP-8355
-
-TICKETS_4.2.23 = OTP-8201
-
-TICKETS_4.2.22 = OTP-8088
-
-TICKETS_4.2.21 = OTP-7982
-
-TICKETS_4.2.20 = OTP-7837
-
-TICKETS_4.2.19 = OTP-7595
-
-TICKETS_4.2.18 = OTP-7313
+IC_VSN = 4.2.26
diff --git a/lib/inets/doc/src/ftp.xml b/lib/inets/doc/src/ftp.xml
index 25dfe716fc..ca902d8d9d 100644
--- a/lib/inets/doc/src/ftp.xml
+++ b/lib/inets/doc/src/ftp.xml
@@ -107,7 +107,7 @@
<tag>{mode, Mode}</tag>
<item>
<marker id="mode"></marker>
- <p>Mode = <c>active | passive</c> </p>>
+ <p>Mode = <c>active | passive</c> </p>
<p>Default is <c>passive</c>. </p>
</item>
diff --git a/lib/inets/doc/src/http_client.xml b/lib/inets/doc/src/http_client.xml
index 510c30eb35..4542211d71 100644
--- a/lib/inets/doc/src/http_client.xml
+++ b/lib/inets/doc/src/http_client.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -42,10 +42,10 @@
dynamically in runtime. Each client profile will spawn a new
process to handle each request unless there is a possibility to use
a persistent connection with or without pipelining.
- The client will add a host header and an empty
- te header if there are no such headers present in the request.</p>
+ The client will add a <c>host</c> header and an empty
+ <c>te</c> header if there are no such headers present in the request.</p>
- <p>The clients supports ipv6 as long as the underlying mechanisms also do
+ <p>The client supports ipv6 as long as the underlying mechanisms also do
so.</p>
</section>
@@ -56,8 +56,8 @@
<pre>
[{inets, [{services, [{httpc, PropertyList}]}]}]
</pre>
- <p>For valid properties see <seealso
- marker="http">http(3)</seealso></p>
+ <p>For valid properties see
+ <seealso marker="httpc">httpc(3)</seealso>. </p>
</section>
<section>
@@ -71,67 +71,66 @@
but not for requests to localhost. This will apply to all subsequent
requests</p>
<code type="erl">
- 2 > http:set_options([{proxy, {{"www-proxy.mycompany.com", 8000},
+ 2 > httpc:set_options([{proxy, {{"www-proxy.mycompany.com", 8000},
["localhost"]}}]).
ok
</code>
<p>An ordinary synchronous request. </p>
<code type="erl">
3 > {ok, {{Version, 200, ReasonPhrase}, Headers, Body}} =
- http:request(get, {"http://www.erlang.org", []}, [], []).
+ httpc:request(get, {"http://www.erlang.org", []}, [], []).
</code>
<p>With all default values, as above, a get request can also be written
like this.</p>
<code type="erl">
4 > {ok, {{Version, 200, ReasonPhrase}, Headers, Body}} =
- http:request("http://www.erlang.org").
+ httpc:request("http://www.erlang.org").
</code>
<p>An ordinary asynchronous request. The result will be sent
- to the calling process on the form {http, {ReqestId, Result}}</p>
+ to the calling process in the form <c>{http, {ReqestId, Result}}</c></p>
<code type="erl">
5 > {ok, RequestId} =
- http:request(get, {"http://www.erlang.org", []}, [], [{sync, false}]).
+ httpc:request(get, {"http://www.erlang.org", []}, [], [{sync, false}]).
</code>
<p>In this case the calling process is the shell, so we receive the
result.</p>
<code type="erl">
- 6 > receive {http, {RequestId, Result}} -> ok after 500 -> error end.
+ 6 > receive {http, {RequestId, Result}} -> ok after 500 -> error end.
ok
</code>
<p>Send a request with a specified connection header. </p>
<code type="erl">
7 > {ok, {{NewVersion, 200, NewReasonPhrase}, NewHeaders, NewBody}} =
- http:request(get, {"http://www.erlang.org", [{"connection", "close"}]},
+ httpc:request(get, {"http://www.erlang.org", [{"connection", "close"}]},
[], []).
</code>
<p>Start a HTTP client profile. </p>
<code><![CDATA[
- 8 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
+ 8 > {ok, Pid} = inets:start(httpc, [{profile, foo}]).
{ok, <0.45.0>}
]]></code>
<p>The new profile has no proxy settings so the connection will
be refused</p>
<code type="erl">
- 9 > http:request("http://www.erlang.org", foo).
- {error,econnrefused}
+ 9 > httpc:request("http://www.erlang.org", foo).
+ {error, econnrefused}
</code>
<p>Stop a HTTP client profile. </p>
<code type="erl">
- 10 > inets:stop(httpc, foo).
+ 10 > inets:stop(httpc, foo).
ok
</code>
<p>Alternatively:</p>
<code type="erl">
- 10 > inets:stop(httpc, Pid).
+ 10 > inets:stop(httpc, Pid).
ok
</code>
-
</section>
</chapter>
diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml
index 01e0b47d37..47ed9cd229 100644
--- a/lib/inets/doc/src/http_server.xml
+++ b/lib/inets/doc/src/http_server.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
@@ -30,6 +30,8 @@
<date></date>
<rev></rev>
<file>http_server.xml</file>
+
+ <marker id="intro"></marker>
</header>
<section>
@@ -65,6 +67,8 @@
Server API. This API can be used to advantage by all who wants
to enhance the server core functionality, for example custom
logging and authentication.</p>
+
+ <marker id="config"></marker>
</section>
<section>
@@ -109,6 +113,8 @@
functions or only exported functions on chosen modules.</p>
<p>{accept_timeout, integer()} sets the wanted timeout value for
the server to set up a request connection.</p>
+
+ <marker id="using_http_server_api"></marker>
</section>
<section>
@@ -173,6 +179,7 @@
the ip address reported by the info function and can
not be the hostname that is allowed when inputting bind_address.</p>
+ <marker id="htaccess"></marker>
</section>
<section>
@@ -337,6 +344,8 @@ UserName:Password
</item>
</list>
</section>
+
+ <marker id="dynamic_we_pages"></marker>
</section>
<section>
@@ -434,6 +443,8 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
</note>
</section>
</section>
+
+ <marker id="logging"></marker>
</section>
<section>
@@ -467,6 +478,8 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
</p>
<p><em>[date]</em> access to <em>path</em> failed for
<em>remotehost</em>, reason: <em>reason</em></p>
+
+ <marker id="ssi"></marker>
</section>
<section>
@@ -753,7 +766,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[
<code>
-module(mnesia_test).
-export([start/0,load_data/0]).
--include("mod_auth.hrl").
+-include_lib("mod_auth.hrl").
first_start() ->
mnesia:create_schema([node()]),
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 7430a62b1b..8f68087871 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2004</year><year>2010</year>
+ <year>2004</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,7 +36,7 @@
<note>
<p>When starting the Inets application a manager process for the
default profile will be started. The functions in this API
- that does not explicitly use a profile will accesses the
+ that do not explicitly use a profile will access the
default profile. A profile keeps track of proxy options,
cookies and other options that can be applied to more than one
request. </p>
@@ -117,7 +117,7 @@ ssl_options() = {verify, code()} |
application or started dynamically in runtime by calling the
inets application API <c>inets:start(httpc, ServiceConfig)</c>, or
<c>inets:start(httpc, ServiceConfig, How)</c>
- see <seealso marker="inets">inets(3)</seealso> Below follows a
+ see <seealso marker="inets">inets(3)</seealso>. Below follows a
description of the available configuration options.</p>
<taglist>
<tag>{profile, profile()}</tag>
@@ -129,8 +129,8 @@ ssl_options() = {verify, code()} |
as session cookies.</item>
</taglist>
- <p>The client can be stopped using inets:stop(httpc, Pid) or
- inets:stop(httpc, Profile).</p>
+ <p>The client can be stopped using <c>inets:stop(httpc, Pid)</c> or
+ <c>inets:stop(httpc, Profile)</c>.</p>
<marker id="request1"></marker>
</section>
@@ -148,7 +148,7 @@ ssl_options() = {verify, code()} |
<v>Reason = term() </v>
</type>
<desc>
- <p>Equivalent to httpc:request(get, {Url, []}, [], []).</p>
+ <p>Equivalent to <c>httpc:request(get, {Url, []}, [], [])</c>.</p>
<marker id="request2"></marker>
</desc>
@@ -167,10 +167,13 @@ ssl_options() = {verify, code()} |
<v>http_option() = {timeout, timeout()} |
{connect_timeout, timeout()} |
{ssl, ssl_options()} |
+ {ossl, ssl_options()} |
+ {essl, ssl_options()} |
{autoredirect, boolean()} |
{proxy_auth, {userstring(), passwordstring()}} |
{version, http_version()} |
- {relaxed, boolean()}</v>
+ {relaxed, boolean()} |
+ {url_encode, boolean()}</v>
<v>timeout() = integer() >= 0 | infinity</v>
<v>Options = options()</v>
<v>options() = [option()]</v>
@@ -198,7 +201,7 @@ ssl_options() = {verify, code()} |
<desc>
<p>Sends a HTTP-request. The function can be both synchronous
and asynchronous. In the later case the function will return
- {ok, RequestId} and later on the information will be delivered
+ <c>{ok, RequestId}</c> and later on the information will be delivered
to the <c>receiver</c> depending on that value. </p>
<p>Http option (<c>http_option()</c>) details: </p>
@@ -206,7 +209,7 @@ ssl_options() = {verify, code()} |
<tag><c><![CDATA[timeout]]></c></tag>
<item>
<p>Timeout time for the request. </p>
- <p>The clock start ticking as soon as the request has been
+ <p>The clock starts ticking as soon as the request has been
sent. </p>
<p>Time is in milliseconds. </p>
<p>Defaults to <c>infinity</c>. </p>
@@ -222,17 +225,32 @@ ssl_options() = {verify, code()} |
<tag><c><![CDATA[ssl]]></c></tag>
<item>
- <p>If using SSL, these SSL-specific options are used. </p>
+ <p>This is the default ssl config option, currently defaults to
+ <c>ossl</c>, see below. </p>
+ <p>Defaults to <c>[]</c>. </p>
+ </item>
+
+ <tag><c><![CDATA[ossl]]></c></tag>
+ <item>
+ <p>If using the OpenSSL based (old) implementation of SSL,
+ these SSL-specific options are used. </p>
+ <p>Defaults to <c>[]</c>. </p>
+ </item>
+
+ <tag><c><![CDATA[essl]]></c></tag>
+ <item>
+ <p>If using the Erlang based (new) implementation of SSL,
+ these SSL-specific options are used. </p>
<p>Defaults to <c>[]</c>. </p>
</item>
<tag><c><![CDATA[autoredirect]]></c></tag>
<item>
- <p>Should the client automatically retreive the information
+ <p>Should the client automatically retrieve the information
from the new URI and return that as the result instead
of a 30X-result code. </p>
<p>Note that for some 30X-result codes automatic redirect
- is not allowed in these cases the 30X-result will always
+ is not allowed. In these cases the 30X-result will always
be returned. </p>
<p>Defaults to <c>true</c>. </p>
</item>
@@ -249,16 +267,21 @@ ssl_options() = {verify, code()} |
<c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c>
client. When using <c>HTTP/1.0</c> persistent connections will
not be used. </p>
- <p>Defaults to the trsing <c>"HTTP/1.1"</c>. </p>
+ <p>Defaults to the string <c>"HTTP/1.1"</c>. </p>
</item>
<tag><c><![CDATA[relaxed]]></c></tag>
<item>
- <p>If set to true workarounds for known server deviations from
+ <p>If set to <c>true</c> workarounds for known server deviations from
the HTTP-standard are enabled. </p>
<p>Defaults to <c>false</c>. </p>
</item>
+ <tag><c><![CDATA[url_encode]]></c></tag>
+ <item>
+ <p>Will apply Percent-encoding, also known as URL encoding on the URL.</p>
+ <p>Defaults to <c>false</c>. </p>
+ </item>
</taglist>
<p>Option (<c>option()</c>) details: </p>
@@ -273,21 +296,21 @@ ssl_options() = {verify, code()} |
<item>
<p>Streams the body of a 200 or 206 response to the calling
process or to a file. When streaming to the calling process
- using the option <c>self</c> the the following stream messages
- will be sent to that process: {http, {RequestId,
+ using the option <c>self</c> the following stream messages
+ will be sent to that process: <c>{http, {RequestId,
stream_start, Headers}, {http, {RequestId, stream,
- BinBodyPart}, {http, {RequestId, stream_end, Headers}. When
+ BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When
streaming to to the calling processes using the option
<c>{self, once}</c> the first message will have an additional
- element e.i. {http, {RequestId, stream_start, Headers, Pid},
+ element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>,
this is the process id that should be used as an argument to
- http:stream_next/1 to trigger the next message to be sent to
+ <c>http:stream_next/1</c> to trigger the next message to be sent to
the calling process. </p>
<p>Note that it is possible that chunked encoding will add
- headers so that there are more headers in the stream_end
- message than in the stream_start.
+ headers so that there are more headers in the <c>stream_end</c>
+ message than in the <c>stream_start</c>.
When streaming to a file and the request is asynchronous the
- message {http, {RequestId, saved_to_file}} will be sent. </p>
+ message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p>
<p>Defaults to <c>none</c>. </p>
</item>
@@ -315,7 +338,7 @@ ssl_options() = {verify, code()} |
case insenstive. This feature should only be used if there is
no other way to communicate with the server or for testing
purpose. Also note that when this option is used no headers
- will be automatically added, all necessary headers has to be
+ will be automatically added, all necessary headers have to be
provided by the user. </p>
<p>Defaults to <c>false</c>. </p>
</item>
@@ -325,22 +348,22 @@ ssl_options() = {verify, code()} |
<p>Socket options to be used for this and subsequent
request(s). </p>
<p>Overrides any value set by the
- <seealso marker="set_options">set_options</seealso>
+ <seealso marker="#set_options">set_options</seealso>
function. </p>
<p>Note that the validity of the options are <em>not</em>
checked in any way. </p>
<p>Note that this may change the socket behaviour
- (see <seealso marker="kernel:inet#setopts">inet:setopts/2</seealso>)
- for an already existing, and therefor already connected
+ (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>)
+ for an already existing one, and therefore an already connected
request handler. </p>
- <p>By defaults the socket options set by the
+ <p>By default the socket options set by the
<seealso marker="#set_options">set_options/1,2</seealso>
- function is used when establishing connection. </p>
+ function are used when establishing a connection. </p>
</item>
<tag><c><![CDATA[receiver]]></c></tag>
<item>
- <p>Defines how the client will deliver the result for a
+ <p>Defines how the client will deliver the result of an
asynchroneous request (<c>sync</c> has the value
<c>false</c>). </p>
@@ -372,7 +395,7 @@ apply(Module, Function, [ReplyInfo | Args])
</item>
</taglist>
- <p>In all cases above, <c>ReplyInfo</c> has the following
+ <p>In all of the above cases, <c>ReplyInfo</c> has the following
structure: </p>
<pre>
@@ -447,46 +470,46 @@ apply(Module, Function, [ReplyInfo | Args])
<v>IpDesc = string()</v>
<d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d>
<v>MaxSessions = integer() </v>
- <d>Default is <em>2</em>.
+ <d>Default is <c>2</c>.
Maximum number of persistent connections to a host.</d>
<v>MaxKeepAlive = integer() </v>
- <d>Default is <em>5</em>.
+ <d>Default is <c>5</c>.
Maximum number of outstanding requests on the same connection to
a host.</d>
<v>KeepAliveTimeout = integer() </v>
- <d>Default is <em>120000</em> (= 2 min).
+ <d>Default is <c>120000</c> (= 2 min).
If a persistent connection is idle longer than the
- keep_alive_timeout the client will close the connection.
- The server may also have a such a time out but you should
+ <c>keep_alive_timeout</c> the client will close the connection.
+ The server may also have such a time out but you should
not count on it!</d>
<v>MaxPipeline = integer() </v>
- <d>Default is <em>2</em>.
+ <d>Default is <c>2</c>.
Maximum number of outstanding requests on a pipelined connection to a host.</d>
<v>PipelineTimeout = integer() </v>
- <d>Default is <em>0</em>,
+ <d>Default is <c>0</c>,
which will result in pipelining not being used.
If a persistent connection is idle longer than the
- pipeline_timeout the client will close the connection. </d>
+ <c>pipeline_timeout</c> the client will close the connection. </d>
<v>CookieMode = enabled | disabled | verify </v>
- <d>Default is <em>disabled</em>.
+ <d>Default is <c>disabled</c>.
If Cookies are enabled all valid cookies will automatically be
saved in the client manager's cookie database.
- If the option verify is used the function http:verify_cookie/2
- has to be called for the cookie to be saved.</d>
+ If the option <c>verify</c> is used the function <c>store_cookies/2</c>
+ has to be called for the cookies to be saved.</d>
<v>IpFamily = inet | inet6 | inet6fb4 </v>
- <d>By default <em>inet</em>.
+ <d>By default <c>inet</c>.
When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6.
It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>.
The option is here to provide a workaround for buggy ipv6 stacks to ensure that
ipv4 will always work.</d>
<v>IpAddress = ip_address() </v>
<d>If the host has several network interfaces, this option specifies which one to use.
- See gen_tcp:connect/3,4 for more info. </d>
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
<v>Port = integer() </v>
<d>Specify which local port number to use.
- See gen_tcp:connect/3,4 for more info. </d>
+ See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d>
<v>VerboseMode = false | verbose | debug | trace </v>
- <d>Default is <em>false</em>.
+ <d>Default is <c>false</c>.
This option is used to switch on (or off)
different levels of erlang trace on the client.
It is a debug feature.</d>
@@ -500,14 +523,14 @@ apply(Module, Function, [ReplyInfo | Args])
alive and use persistent connections
with or without pipeline depending on configuration
and current circumstances. The HTTP/1.1 specification does not
- provide a guideline for how many requests that would be
+ provide a guideline for how many requests would be
ideal to be sent on a persistent connection,
this very much depends on the
application. Note that a very long queue of requests may cause a
- user perceived delays as earlier request may take a long time
+ user perceived delay as earlier requests may take a long time
to complete. The HTTP/1.1 specification does suggest a
limit of 2 persistent connections per server, which is the
- default value of the max_sessions option. </p>
+ default value of the <c>max_sessions</c> option. </p>
</note>
<marker id="stream_next"></marker>
@@ -526,14 +549,14 @@ apply(Module, Function, [ReplyInfo | Args])
<p>Triggers the next message to be streamed, e.i.
same behavior as active once for sockets.</p>
- <marker id="verify_cookie"></marker>
- <marker id="store_cookie"></marker>
+ <marker id="verify_cookies"></marker>
+ <marker id="store_cookies"></marker>
</desc>
</func>
<func>
- <name>store_cookie(SetCookieHeaders, Url) -> </name>
- <name>store_cookie(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name>
+ <name>store_cookies(SetCookieHeaders, Url) -> </name>
+ <name>store_cookies(SetCookieHeaders, Url, Profile) -> ok | {error, Reason}</name>
<fsummary>Saves the cookies defined in SetCookieHeaders in the client profile's cookie database.</fsummary>
<type>
<v>SetCookieHeaders = headers() - where field = "set-cookie"</v>
@@ -543,7 +566,7 @@ apply(Module, Function, [ReplyInfo | Args])
<desc>
<p>Saves the cookies defined in SetCookieHeaders
in the client profile's cookie database. You need to
- call this function if you set the option cookies to <c>verify</c>.
+ call this function if you have set the option <c>cookies</c> to <c>verify</c>.
If no profile is specified the default profile will be used.
</p>
@@ -553,16 +576,16 @@ apply(Module, Function, [ReplyInfo | Args])
<func>
<name>cookie_header(Url) -> </name>
- <name>cookie_header(Url, Profile) -> header() | {error, Rason}</name>
+ <name>cookie_header(Url, Profile) -> header() | {error, Reason}</name>
<fsummary>Returns the cookie header that would be sent when
- making a request to Url using the profile Profile.</fsummary>
+ making a request to Url using the profile <c>Profile</c>.</fsummary>
<type>
<v>Url = url()</v>
<v>Profile = profile()</v>
</type>
<desc>
<p>Returns the cookie header that would be sent
- when making a request to Url using the profile Profile.
+ when making a request to <c>Url</c> using the profile <c>Profile</c>.
If no profile is specified the default profile will be used.
</p>
@@ -579,7 +602,7 @@ apply(Module, Function, [ReplyInfo | Args])
<v>Profile = profile()</v>
</type>
<desc>
- <p>Resets (clears) the cookie database for the specified Profile.
+ <p>Resets (clears) the cookie database for the specified <c>Profile</c>.
If no profile is specified the default profile will be used.
</p>
</desc>
@@ -615,4 +638,3 @@ apply(Module, Function, [ReplyInfo | Args])
</section>
</erlref>
-
diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml
index 7dabeb33e9..62f4e18f82 100644
--- a/lib/inets/doc/src/httpd.xml
+++ b/lib/inets/doc/src/httpd.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
@@ -148,8 +148,13 @@
in the apache like configuration file.
</item>
- <tag>{socket_type, ip_comm | ssl}</tag>
+ <tag>{socket_type, ip_comm | ssl | ossl | essl}</tag>
<item>
+ <p>When using ssl, there are several alternatives.
+ <c>ossl</c> specifically uses the OpenSSL based (old) SSL.
+ <c>essl</c> specifically uses the Erlang based (new) SSL.
+ When using <c>ssl</c> it <em>currently</em> defaults to
+ <c>ossl</c>. </p>
<p>Defaults to <c>ip_comm</c>. </p>
</item>
@@ -267,18 +272,22 @@ text/plain asc txt
The <c>common</c> format is one line that looks like this:
<c>remotehost rfc931 authuser [date] "request" status bytes</c></p>
- <pre>remotehost
+ <pre>
+remotehost
Remote
rfc931
The client's remote username (RFC 931).
authuser
- The username with which the user authenticated himself.
+ The username with which the user authenticated
+ himself.
[date]
Date and time of the request (RFC 1123).
"request"
- The request line exactly as it came from the client(RFC 1945).
+ The request line exactly as it came from the client
+ (RFC 1945).
status
- The HTTP status code returned to the client (RFC 1945).
+ The HTTP status code returned to the client
+ (RFC 1945).
bytes
The content-length of the document transferred.
</pre>
@@ -286,10 +295,11 @@ bytes
<p>The <c>combined</c> format is on line that look like this:
<c>remotehost rfc931 authuser [date] "request" status bytes "referer" "user_agent" </c></p>
- <pre>"referer"
+ <pre>
+"referer"
The url the client was on before
- requesting your url. (If it could not be determined a minus
- sign will be placed in this field)
+ requesting your url. (If it could not be determined
+ a minus sign will be placed in this field)
"user_agent"
The software the client claims to be using. (If it
could not be determined a minus sign will be placed in
@@ -389,6 +399,31 @@ bytes
and an access to http://your.server.org/image/foo.gif would refer to
the file /ftp/pub/image/foo.gif.</item>
+ <tag>{re_write, {Re, Replacement}}</tag>
+
+ <item> Where Re = string() and Replacement = string().
+ The ReWrite property allows documents to be stored in the local file
+ system instead of the document_root location. URLs are rewritten
+ by re:replace/3 to produce a path in the local filesystem.
+ For example:
+
+ <code>{re_write, {"^/[~]([^/]+)(.*)$", "/home/\\1/public\\2"}</code>
+
+ and an access to http://your.server.org/~bob/foo.gif would refer to
+ the file /home/bob/public/foo.gif.
+
+ In an Apache like configuration file the Re is separated
+ from Replacement with one single space, and as expected
+ backslashes do not need to be backslash escaped so the
+ same example would become:
+
+ <code>ReWrite ^/[~]([^/]+)(.*)$ /home/\1/public\2</code>
+
+ Beware of trailing space in Replacement that will be used.
+ If you must have a space in Re use e.g the character encoding
+ <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>.
+ </item>
+
<tag>{directory_index, [string()]}</tag>
<item>
@@ -408,7 +443,7 @@ bytes
</taglist>
<marker id="cgi_prop"></marker>
- <p><em>CGI properties - requires mod_cgi</em></p>
+ <p><em>CGI properties - requires mod_cgi</em></p>
<taglist>
<tag>{script_alias, {Alias, RealName}}</tag>
<item> Where Alias = string() and RealName = string().
@@ -423,6 +458,19 @@ bytes
the server to run the script /web/cgi-bin/foo.
</item>
+ <tag>{script_re_write, {Re, Replacement}}</tag>
+ <item> Where Re = string() and Replacement = string().
+ Has the same behavior as the ReWrite property, except that
+ it also marks the target directory as containing CGI
+ scripts. URLs with a path beginning with url-path are mapped to
+ scripts beginning with directory-filename, for example:
+
+ <code> {script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code>
+
+ and an access to http://your.server.org/cgi-bin/17/foo would cause
+ the server to run the script /web/17/cgi-bin/foo.
+ </item>
+
<tag>{script_nocache, boolean()}</tag>
<item>
@@ -883,6 +931,10 @@ bytes
connection
}).
</code>
+
+ <p>To acess the record in your callback-module use </p>
+ <code> -include_lib("inets/include/httpd.hrl"). </code>
+
<p>The fields of the <c>mod</c> record has the following meaning:
</p>
<taglist>
@@ -930,10 +982,10 @@ bytes
<c>parsed_header</c> contains all HTTP header fields from the
HTTP-request stored in a list as key-value tuples. See RFC 2616
for a listing of all header fields. For example the date field
- would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"}.
+ would be stored as: <c>{"date","Wed, 15 Oct 1997 14:35:17 GMT"} </c>.
RFC 2616 defines that HTTP is a case insensitive protocol and
the header fields may be in lower case or upper case. Httpd will
- ensure that all header field names are in lower case. </c>.
+ ensure that all header field names are in lower case.
</item>
<tag><c>entity_body</c></tag>
<item>The <c>Entity-Body</c> as defined
diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml
index 81dfe7e944..c367d7fa77 100644
--- a/lib/inets/doc/src/inets.xml
+++ b/lib/inets/doc/src/inets.xml
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>inets</title>
@@ -40,13 +40,13 @@
<title>COMMON DATA TYPES </title>
<p>Type definitions that are used more than once in
this module: </p>
- <p><c> service() = ftpc | tfptd | httpc | httpd</c></p>
+ <p><c> service() = ftpc | tftp | httpc | httpd</c></p>
<p><c> property() = atom() </c></p>
</section>
<funcs>
<func>
<name>services() -> [{Service, Pid}]</name>
- <fsummary>Returns a list of currently running services. </fsummary>
+ <fsummary>Returns a list of currently running services. </fsummary>
<type>
<v>Service = service()</v>
<v>Pid = pid()</v>
@@ -97,7 +97,7 @@
<name>start(Type) -> ok | {error, Reason}</name>
<fsummary>Starts the Inets application. </fsummary>
<type>
- <v>Type = permanent | transient | temporary</v>
+ <v>Type = permanent | transient | temporary</v>
</type>
<desc>
<p>Starts the Inets application. Default type
@@ -115,11 +115,9 @@
</func>
<func>
<name>start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason}</name>
- <name>start(Service, ServiceConfig, How) -> {ok, Pid} |
- {error, Reason}</name>
+ <name>start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason}</name>
<fsummary>Dynamically starts an inets
- service after the inets application has been
- started. </fsummary>
+ service after the inets application has been started. </fsummary>
<type>
<v>Service = service()</v>
<v>ServiceConfig = [{Option, Value}]</v>
@@ -153,9 +151,9 @@
<fsummary>Stops a started service of the inets application or takes
down a "stand_alone-service" gracefully.</fsummary>
<type>
- <v>Service = service() | stand_alone</v>
+ <v>Service = service() | stand_alone</v>
<v>Reference = pid() | term() - service specified reference</v>
- <v>Reason = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
<p>Stops a started service of the inets application or takes
@@ -169,7 +167,7 @@
<section>
<title>SEE ALSO</title>
<p><seealso marker="ftp">ftp(3)</seealso>,
- <seealso marker="http">http(3)</seealso>,
+ <seealso marker="httpc">httpc(3)</seealso>,
<seealso marker="httpd">httpd(3)</seealso>,
<seealso marker="tftp">tftp(3)</seealso></p>
</section>
diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml
index f3628c8297..42c49e9c35 100644
--- a/lib/inets/doc/src/mod_auth.xml
+++ b/lib/inets/doc/src/mod_auth.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -111,7 +111,8 @@
</desc>
</func>
<func>
- <name>list_users(Options) -> {ok, Users} | {error, Reason} &lt;name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name>
+ <name>list_users(Options) -> {ok, Users} | {error, Reason}</name>
+ <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name>
<name>list_users(Address, Port, Dir) -> {ok, Users} | {error, Reason}</name>
<fsummary>List users in the user database.</fsummary>
<type>
diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml
index 6bad77dc0a..3c473d3f94 100644
--- a/lib/inets/doc/src/mod_esi.xml
+++ b/lib/inets/doc/src/mod_esi.xml
@@ -73,7 +73,8 @@
<v>SessionID = term()</v>
<v>Env = [EnvironmentDirectives] ++ ParsedHeader</v>
<v>EnvironmentDirectives = {Key,Value}</v>
- <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name. &lt;v>Input = string()</v>
+ <v>Key = query_string | content_length | server_software | gateway_interface | server_protocol | server_port | request_method | remote_addr | script_name</v>
+ <v>Input = string()</v>
</type>
<desc>
<p>The <c>Module</c> must be found in the code path and export
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 3216b0c2cd..5da9d98002 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2002</year><year>2010</year>
+ <year>2002</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,241 @@
<file>notes.xml</file>
</header>
+ <section><title>Inets 5.5.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <p>-</p>
+
+<!--
+ <list>
+ <item>
+ <p>
+ Miscellaneous inet6 related problems.</p>
+ <p>Own Id: OTP-8927</p>
+ </item>
+ </list>
+-->
+
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>[httpd] httpd_response:send_chunk handles empty list and
+ empty binary - i.e. no chunk is sent, but it does
+ not handle a list with an empty binary [&lt;&lt;&gt;&gt;].
+ This will be sent as an empty chunk - which in turn
+ will be encoded by http_chunk to the same as a final
+ chunk, which will make the http client believe that
+ the end of the page is reached.</p>
+ <p>Own Id: OTP-8906</p>
+ </item>
+ </list>
+ </section>
+
+ </section> <!-- 5.5.2 -->
+
+
+ <section><title>Inets 5.5.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Miscellaneous inet6 related problems.</p>
+ <p>Own Id: OTP-8927</p>
+ </item>
+ <item>
+ <p>Updated http-server to make sure URLs in error-messages
+ are URL-encoded. Added support in http-client to use
+ URL-encoding. Also added the missing include directory
+ for the inets application.</p>
+ <p>Own Id: OTP-8940 Aux Id: seq11735 </p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fix format_man_pages so it handles all man sections
+ and remove warnings/errors in various man pages. </p>
+ <p>Own Id: OTP-8600</p>
+ </item>
+ <item>
+ <p>[httpc] Pipelined and queued requests not processed when
+ connection closed remotelly.</p>
+ <p>Own Id: OTP-8906</p>
+ </item>
+ </list>
+ </section>
+
+ </section> <!-- 5.5.1 -->
+
+
+ <section><title>Inets 5.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ [httpc] If a request times out (not connect timeout), the
+ handler process exited (normal) but neglected to inform
+ the manager process. For this reason, the manager did not
+ clean up the request table., resulting in a memory leak.
+ Also the manager did not create a monitor for the
+ handler, so in an unforseen handler crash, this could
+ also create a memory leak.</p>
+ <p>
+ Own Id: OTP-8739</p>
+ </item>
+ <item>
+ <p>
+ The service tftp was spelled wrong in documentation and
+ in some parts of the code. It should be tftp.</p>
+ <p>
+ Own Id: OTP-8741 Aux Id: seq11635 </p>
+ </item>
+ <item>
+ <p>
+ [httpc] Replaced the old http client api module (http)
+ with the new, httpc in the users guide.</p>
+ <p>
+ Own Id: OTP-8742</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Eliminated warnings for auto-imported BIF clashes.</p>
+ <p>
+ Own Id: OTP-8840</p>
+ </item>
+ </list>
+ </section>
+
+ </section> <!-- 5.5 -->
+
+
+ <section><title>Inets 5.4</title>
+
+ <section><title>Improvements and New Features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list>
+ <item>
+ <p>[httpc|httpd] - Now allow the use of the "new" ssl, by using
+ the <c>essl</c> tag instead. </p>
+ <p>See the <c>http_option</c> option in the
+ <seealso marker="httpc#request2">request/4,5</seealso> or
+ the <seealso marker="httpd#comm_prop">socket-type</seealso>
+ section of the Communication properties chapter for more info, </p>
+ <p>Own Id: OTP-7907</p>
+ </item>
+
+ <item>
+ <p>Deprecated functions designated to be removed in R14 has been
+ removed. Also, some new functions has been marked as deprecated
+ (the old http client api module). </p>
+ <p>Own Id: OTP-8564</p>
+ <p>*** POTENTIAL INCOMPATIBILITY ***</p>
+ </item>
+
+ <item>
+ <p>[httpd] - Improved mod_alias.
+ Now able to do better URL rewrites. </p>
+ <p>See
+ <seealso marker="httpd#alias_prop">URL aliasing properties</seealso>
+ and the
+ <seealso marker="httpd#cgi_prop">CGI properties</seealso>
+ section(s) for more info, </p>
+ <p>Own Id: OTP-8573</p>
+ </item>
+
+ </list>
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+
+ <p>-</p>
+
+<!--
+ <list>
+ <item>
+ <p>[httpd] The server did not fully support the documented module
+ callback api. Specifically, the load function should be able to
+ return the atom <c>ok</c>, but this was not accepted. </p>
+ <p>Own Id: OTP-8359</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ </section> <!-- 5.4 -->
+
+
+ <section><title>Inets 5.3.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <p>-</p>
+
+<!--
+ <list>
+ <item>
+ <p>[httpc] - Allow users to pass socket options to the transport
+ module when making requests. </p>
+ <p>See the <c>socket_opts</c> option in the
+ <seealso marker="httpc#request2">request/4</seealso> or
+ <seealso marker="httpc#set_options">set_options/1,2</seealso>
+ for more info, </p>
+ <p>Own Id: OTP-8352</p>
+ </item>
+
+ </list>
+-->
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list>
+ <item>
+ <p>[httpc] - Made cookie handling more case insensitive.</p>
+ <p>Own Id: OTP-8609</p>
+ <p>Nicolas Thauvin</p>
+ </item>
+
+ <item>
+ <p>[httpc|httpd] - Netscape cookie dates can also be given with a
+ 2-digit year (e.g. 06 = 2006). </p>
+ <p>Own Id: OTP-8610</p>
+ <p>Nicolas Thauvin</p>
+ </item>
+
+ <item>
+ <p>[httpd] - Added support (again) for the documented debugging
+ features. See the User's Guide
+ <seealso marker="http_server#config">Configuration</seealso>
+ chapter for more info. </p>
+ <p>Own Id: OTP-8624</p>
+ </item>
+
+ </list>
+ </section>
+
+ </section> <!-- 5.3.3 -->
+
+
<section><title>Inets 5.3.2</title>
<section><title>Improvements and New Features</title>
@@ -249,6 +484,7 @@
<p>Own Id: OTP-8016</p>
<p>*** POTENTIAL INCOMPATIBILITY ***</p>
</item>
+
</list>
</section>
diff --git a/lib/inets/examples/Makefile b/lib/inets/examples/Makefile
index a42b0e38b6..775c449062 100644
--- a/lib/inets/examples/Makefile
+++ b/lib/inets/examples/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
#
@@ -21,189 +21,15 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../vsn.mk
-VSN=$(INETS_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
-
-# ----------------------------------------------------
-# Target Specs
+# Common Macros
# ----------------------------------------------------
-MODULE=
-AUTH_FILES = server_root/auth/group \
- server_root/auth/passwd
-CGI_FILES = server_root/cgi-bin/printenv.sh
-CONF_FILES = server_root/conf/8080.conf \
- server_root/conf/8888.conf \
- server_root/conf/httpd.conf \
- server_root/conf/ssl.conf \
- server_root/conf/mime.types
-OPEN_FILES = server_root/htdocs/open/dummy.html
-MNESIA_OPEN_FILES = server_root/htdocs/mnesia_open/dummy.html
-MISC_FILES = server_root/htdocs/misc/friedrich.html \
- server_root/htdocs/misc/oech.html
-SECRET_FILES = server_root/htdocs/secret/dummy.html
-MNESIA_SECRET_FILES = server_root/htdocs/mnesia_secret/dummy.html
-HTDOCS_FILES = server_root/htdocs/index.html \
- server_root/htdocs/config.shtml \
- server_root/htdocs/echo.shtml \
- server_root/htdocs/exec.shtml \
- server_root/htdocs/flastmod.shtml \
- server_root/htdocs/fsize.shtml \
- server_root/htdocs/include.shtml
-ICON_FILES = server_root/icons/README \
- server_root/icons/a.gif \
- server_root/icons/alert.black.gif \
- server_root/icons/alert.red.gif \
- server_root/icons/apache_pb.gif \
- server_root/icons/back.gif \
- server_root/icons/ball.gray.gif \
- server_root/icons/ball.red.gif \
- server_root/icons/binary.gif \
- server_root/icons/binhex.gif \
- server_root/icons/blank.gif \
- server_root/icons/bomb.gif \
- server_root/icons/box1.gif \
- server_root/icons/box2.gif \
- server_root/icons/broken.gif \
- server_root/icons/burst.gif \
- server_root/icons/button1.gif \
- server_root/icons/button10.gif \
- server_root/icons/button2.gif \
- server_root/icons/button3.gif \
- server_root/icons/button4.gif \
- server_root/icons/button5.gif \
- server_root/icons/button6.gif \
- server_root/icons/button7.gif \
- server_root/icons/button8.gif \
- server_root/icons/button9.gif \
- server_root/icons/buttonl.gif \
- server_root/icons/buttonr.gif \
- server_root/icons/c.gif \
- server_root/icons/comp.blue.gif \
- server_root/icons/comp.gray.gif \
- server_root/icons/compressed.gif \
- server_root/icons/continued.gif \
- server_root/icons/dir.gif \
- server_root/icons/down.gif \
- server_root/icons/dvi.gif \
- server_root/icons/f.gif \
- server_root/icons/folder.gif \
- server_root/icons/folder.open.gif \
- server_root/icons/folder.sec.gif \
- server_root/icons/forward.gif \
- server_root/icons/generic.gif \
- server_root/icons/generic.red.gif \
- server_root/icons/generic.sec.gif \
- server_root/icons/hand.right.gif \
- server_root/icons/hand.up.gif \
- server_root/icons/htdig.gif \
- server_root/icons/icon.sheet.gif \
- server_root/icons/image1.gif \
- server_root/icons/image2.gif \
- server_root/icons/image3.gif \
- server_root/icons/index.gif \
- server_root/icons/layout.gif \
- server_root/icons/left.gif \
- server_root/icons/link.gif \
- server_root/icons/movie.gif \
- server_root/icons/p.gif \
- server_root/icons/patch.gif \
- server_root/icons/pdf.gif \
- server_root/icons/pie0.gif \
- server_root/icons/pie1.gif \
- server_root/icons/pie2.gif \
- server_root/icons/pie3.gif \
- server_root/icons/pie4.gif \
- server_root/icons/pie5.gif \
- server_root/icons/pie6.gif \
- server_root/icons/pie7.gif \
- server_root/icons/pie8.gif \
- server_root/icons/portal.gif \
- server_root/icons/poweredby.gif \
- server_root/icons/ps.gif \
- server_root/icons/quill.gif \
- server_root/icons/right.gif \
- server_root/icons/screw1.gif \
- server_root/icons/screw2.gif \
- server_root/icons/script.gif \
- server_root/icons/sound1.gif \
- server_root/icons/sound2.gif \
- server_root/icons/sphere1.gif \
- server_root/icons/sphere2.gif \
- server_root/icons/star.gif \
- server_root/icons/star_blank.gif \
- server_root/icons/tar.gif \
- server_root/icons/tex.gif \
- server_root/icons/text.gif \
- server_root/icons/transfer.gif \
- server_root/icons/unknown.gif \
- server_root/icons/up.gif \
- server_root/icons/uu.gif \
- server_root/icons/uuencoded.gif \
- server_root/icons/world1.gif \
- server_root/icons/world2.gif
+include subdirs.mk
-SSL_FILES = server_root/ssl/ssl_client.pem \
- server_root/ssl/ssl_server.pem
+SPECIAL_TARGETS =
# ----------------------------------------------------
-# FLAGS
+# Default Subdir Targets
# ----------------------------------------------------
-ERL_COMPILE_FLAGS +=
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug opt:
-
-clean:
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth
- $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin
- $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf
- $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open
- $(INSTALL_DATA) $(OPEN_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/open
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
- $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc
- $(INSTALL_DATA) $(MISC_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/misc
- $(INSTALL_DIR) \
- $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret
- $(INSTALL_DIR) \
- $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret
- $(INSTALL_DATA) $(SECRET_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/secret
- $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \
- $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs
- $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons
- $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl
- $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl
- $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs
-
-release_docs_spec:
+include $(ERL_TOP)/make/otp_subdir.mk
diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile
new file mode 100644
index 0000000000..1cc61ad8ae
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/Makefile
@@ -0,0 +1,123 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+include $(ERL_TOP)/make/target.mk
+
+EBIN = .
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+
+VSN=$(INETS_VSN)
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
+EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples
+HDLT_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/httpd_load_test
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+include modules.mk
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+SOURCE = $(ERL_FILES) $(INTERNAL_HRL_FILES)
+
+TARGET_FILES = \
+ $(ERL_FILES:%.erl=$(EBIN)/%.$(EMULATOR))
+
+ifeq ($(TYPE),debug)
+ERL_COMPILE_FLAGS += -Ddebug -W
+endif
+
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+include ../../src/inets_app/inets.mk
+
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS) \
+ -I../../include
+
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+debug:
+ @${MAKE} TYPE=debug opt
+
+opt: $(TARGET_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f errs core *~
+
+docs:
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+
+release_spec: opt
+ $(INSTALL_DIR) $(EXAMPLE_RELSYSDIR)
+ $(INSTALL_DIR) $(HDLT_RELSYSDIR)
+ $(INSTALL_DATA) $(SCRIPT_SKELETONS) $(HDLT_RELSYSDIR)
+ $(INSTALL_DATA) $(CONF_SKELETONS) $(HDLT_RELSYSDIR)
+ $(INSTALL_DATA) $(CERT_FILES) $(HDLT_RELSYSDIR)
+ $(INSTALL_DATA) $(TARGET_FILES) $(HDLT_RELSYSDIR)
+ $(INSTALL_DATA) $(ERL_FILES) $(HDLT_RELSYSDIR)
+
+
+release_docs_spec:
+
+
+# ----------------------------------------------------
+# Include dependencies
+# ----------------------------------------------------
+
+megaco_codec_transform.$(EMULATOR): megaco_codec_transform.erl
+
+megaco_codec_meas.$(EMULATOR): megaco_codec_meas.erl
+
+megaco_codec_mstone1.$(EMULATOR): megaco_codec_mstone1.erl
+
+megaco_codec_mstone2.$(EMULATOR): megaco_codec_mstone2.erl
+
+megaco_codec_mstone_lib.$(EMULATOR): megaco_codec_mstone_lib.erl
+
diff --git a/lib/inets/examples/httpd_load_test/hdlt.config.skel b/lib/inets/examples/httpd_load_test/hdlt.config.skel
new file mode 100644
index 0000000000..640867ebac
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt.config.skel
@@ -0,0 +1,20 @@
+%% Debug: silence | info | log | debug
+{debug, [{ctrl, info}, {proxy, silence}, {slave, silence}, {client, silence}]}.
+{server, {"/usr/local/bin", "fooserver"}}.
+%% {port, 8888}. % integer() > 0
+{server_dir, "/tmp/hdlt"}. % Absolute path
+{work_dir, "/tmp/hdlt"}. % Absolute path
+{clients,
+ [
+ {"/opt/local/bin", "foo"},
+ {"/usr/local/bin", "bar"}
+ ]
+}.
+%% {send_rate, 80}. % Max number of outstanding requests, integer() > 0
+%% {test_time, 120}. % Number of seconds,
+%% {max_nof_schedulers, 8}. % integer() >= 0
+%% {work_simulator, 10000}. % integer() > 0
+%% {data_size, {100, 500, 2}}. % {integer() > 0, integer() > 0, integer() > 0}
+%% {socket_type, ip_comm}. % ip_comm | ssl | essl | ossl
+%% {server_cert_file, "hdlt_ssl_server_cert.pem"}.
+%% {client_cert_file, "hdlt_ssl_client_cert.pem"}. \ No newline at end of file
diff --git a/lib/inets/examples/httpd_load_test/hdlt.erl b/lib/inets/examples/httpd_load_test/hdlt.erl
new file mode 100644
index 0000000000..18d8c34ccf
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt.erl
@@ -0,0 +1,74 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Main API module for the httpd load test utility
+%%----------------------------------------------------------------------
+
+-module(hdlt).
+
+
+%%-----------------------------------------------------------------
+%% Public interface
+%%-----------------------------------------------------------------
+
+-export([start/0, start/1, stop/0, help/0]).
+
+
+%%-----------------------------------------------------------------
+%% Start the HDLT utility
+%%-----------------------------------------------------------------
+
+start() ->
+ ConfigFile = "hdlt.config",
+ case file:consult(ConfigFile) of
+ {ok, Config} when is_list(Config) ->
+ start(Config);
+ Error ->
+ Error
+ end.
+
+start(Config) ->
+ Flag = process_flag(trap_exit, true),
+ Result =
+ case hdlt_ctrl:start(Config) of
+ {ok, Pid} ->
+ receive
+ {'EXIT', Pid, normal} ->
+ ok;
+ {'EXIT', Pid, Reason} ->
+ io:format("HDLT failed: "
+ "~n ~p"
+ "~n", [Reason]),
+ {error, Reason}
+ end;
+ Error ->
+ Error
+ end,
+ process_flag(trap_exit, Flag),
+ Result.
+
+
+
+stop() ->
+ hdlt_ctrl:stop().
+
+
+help() ->
+ hdlt_ctrl:help().
diff --git a/lib/inets/examples/httpd_load_test/hdlt.sh.skel b/lib/inets/examples/httpd_load_test/hdlt.sh.skel
new file mode 100644
index 0000000000..a250bad9c5
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt.sh.skel
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+# Skeleton for a script intended to run the hdlt(N)
+# performance test.
+#
+# This test can be used for several things depending on the
+# configuration: SMP or SocketType performance tests
+#
+
+ERL_HOME=<path to otp top dir>
+INETS_HOME=$ERL_HOME/lib/erlang/lib/<inets dir>
+HDLT_HOME=$INETS_HOME/examples/httpd_load_test
+PATH=$ERL_HOME/bin:$PATH
+
+HDLT="-s hdlt start"
+STOP="-s init stop"
+
+ERL="erl \
+ -noshell \
+ -pa $HDLT_HOME \
+ $HDLT \
+ $STOP"
+
+echo $ERL
+$ERL | tee hdlt.log
+
diff --git a/lib/inets/examples/httpd_load_test/hdlt_client.erl b/lib/inets/examples/httpd_load_test/hdlt_client.erl
new file mode 100644
index 0000000000..d65ac5a885
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_client.erl
@@ -0,0 +1,370 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The HDLT client module.
+%% This is the traffic generator
+%%----------------------------------------------------------------------
+
+-module(hdlt_client).
+
+-export([
+ start/1,
+ stop/0,
+ start_inets/0,
+ start_service/1,
+ release/0,
+ node_info/0
+ ]).
+
+-export([
+ proxy/1
+ ]).
+
+-include("hdlt_logger.hrl").
+
+-define(CTRL, hdlt_ctrl).
+-define(PROXY, hdlt_proxy).
+
+-record(state,
+ {
+ mode = initial,
+ send_rate,
+ time,
+ stop_time,
+ url,
+ nof_reqs = 0,
+ nof_reps = 0,
+ last_req,
+ sizes,
+ socket_type,
+ cert_file
+ }).
+
+
+
+start(Debug) ->
+ proc_lib:start_link(?MODULE, proxy, [Debug]).
+
+stop() ->
+ (catch erlang:send(?PROXY, stop)),
+ ok.
+
+start_inets() ->
+ ?PROXY ! start_inets.
+
+start_service(Args) ->
+ ?PROXY ! {start_client, Args, self()},
+ receive
+ client_started ->
+ %% ?LOG("client service started"),
+ ok
+ end.
+
+release() ->
+ ?PROXY ! release.
+
+node_info() ->
+ ?PROXY ! {node_info, self()},
+ receive
+ {node_info, NodeInfo} ->
+ NodeInfo
+ end.
+
+
+%% ---------------------------------------------------------------------
+%%
+%% The proxy process
+%%
+
+proxy(Debug) ->
+ process_flag(trap_exit, true),
+ erlang:register(?PROXY, self()),
+ SName = lists:flatten(
+ io_lib:format("HDLT PROXY[~p,~p]", [self(), node()])),
+ ?SET_NAME(SName),
+ ?SET_LEVEL(Debug),
+ ?LOG("starting", []),
+ Ref = await_for_controller(10),
+ CtrlNode = node(Ref),
+ erlang:monitor_node(CtrlNode, true),
+ proc_lib:init_ack({ok, self()}),
+ ?DEBUG("started", []),
+ proxy_loop(Ref, CtrlNode, undefined).
+
+await_for_controller(N) when N > 0 ->
+ case global:whereis_name(hdlt_ctrl) of
+ Pid when is_pid(Pid) ->
+ erlang:monitor(process, Pid);
+ _ ->
+ timer:sleep(1000),
+ await_for_controller(N-1)
+ end;
+await_for_controller(_) ->
+ proc_lib:init_ack({error, controller_not_found, nodes()}),
+ timer:sleep(500),
+ init:stop().
+
+
+proxy_loop(Ref, CtrlNode, Client) ->
+ ?DEBUG("await command", []),
+ receive
+ stop ->
+ ?LOG("stop", []),
+ timer:sleep(1000),
+ halt();
+
+ start_inets ->
+ ?LOG("start the inets service framework", []),
+ %% inets:enable_trace(max, "/tmp/inets-httpc-trace.log", all),
+ case (catch inets:start()) of
+ ok ->
+ ?LOG("framework started", []),
+ proxy_loop(Ref, CtrlNode, Client);
+ Error ->
+ ?LOG("failed starting inets service framework: "
+ "~n Error: ~p", [Error]),
+ timer:sleep(1000),
+ halt()
+ end;
+
+ {start_client, Args, From} ->
+ ?LOG("start client with"
+ "~n Args: ~p", [Args]),
+ Client2 = spawn_link(fun() -> client(Args) end),
+ From ! client_started,
+ proxy_loop(Ref, CtrlNode, Client2);
+
+ release ->
+ ?LOG("release", []),
+ Client ! go,
+ proxy_loop(Ref, CtrlNode, Client);
+
+ {node_info, Pid} ->
+ ?LOG("received requets for node info", []),
+ NodeInfo = get_node_info(),
+ Pid ! {node_info, NodeInfo},
+ proxy_loop(Ref, CtrlNode, Client);
+
+ {'EXIT', Client, normal} ->
+ ?LOG("received normal exit message from client (~p)",
+ [Client]),
+ exit(normal);
+
+ {'EXIT', Client, Reason} ->
+ ?INFO("received exit message from client (~p)"
+ "~n Reason: ~p", [Client, Reason]),
+ %% Unexpected client termination, inform the controller and die
+ global:send(hdlt_ctrl, {client_exit, Client, node(), Reason}),
+ exit({client_exit, Reason});
+
+ {nodedown, CtrlNode} ->
+ ?LOG("received nodedown for controller node - terminate", []),
+ halt();
+
+ {'DOWN', Ref, process, _, _} ->
+ ?INFO("received DOWN message for controller - terminate", []),
+ %% The controller has terminated, dont care why, time to die
+ halt()
+
+ end.
+
+
+
+%% ---------------------------------------------------------------------
+%%
+%% The client process
+%%
+
+client([SocketType, CertFile, URLBase, Sizes, Time, SendRate, Debug]) ->
+ SName = lists:flatten(
+ io_lib:format("HDLT CLIENT[~p,~p]", [self(), node()])),
+ ?SET_NAME(SName),
+ ?SET_LEVEL(Debug),
+ ?LOG("starting with"
+ "~n SocketType: ~p"
+ "~n Time: ~p"
+ "~n SendRate: ~p", [SocketType, Time, SendRate]),
+ httpc:set_options([{max_pipeline_length, 0}]),
+ if
+ (SocketType =:= ssl) orelse
+ (SocketType =:= ossl) orelse
+ (SocketType =:= essl) ->
+ %% Ensure crypto and ssl started:
+ crypto:start(),
+ ssl:start();
+ true ->
+ ok
+ end,
+ State = #state{mode = idle,
+ url = URLBase,
+ time = Time,
+ send_rate = SendRate,
+ sizes = Sizes,
+ socket_type = SocketType,
+ cert_file = CertFile},
+ ?DEBUG("started", []),
+ client_loop(State).
+
+%% The point is to first start all client nodes and then this
+%% process. Then, when they are all started, the go-ahead, go,
+%% message is sent to let them lose at the same time.
+client_loop(#state{mode = idle,
+ time = Time,
+ send_rate = SendRate} = State) ->
+ ?DEBUG("[idle] awaiting the go command", []),
+ receive
+ go ->
+ ?LOG("[idle] received go", []),
+ erlang:send_after(Time, self(), stop),
+ NewState = send_requests(State, SendRate),
+ client_loop(NewState#state{mode = generating,
+ nof_reqs = SendRate})
+ end;
+
+%% In this mode the client is generating traffic.
+%% It will continue to do so until the stop message
+%% is received.
+client_loop(#state{mode = generating} = State) ->
+ receive
+ stop ->
+ ?LOG("[generating] received stop", []),
+ StopTime = timestamp(),
+ req_reply(State),
+ client_loop(State#state{mode = stopping, stop_time = StopTime});
+
+ {http, {_, {{_, 200, _}, _, _}}} ->
+ %% ?DEBUG("[generating] received reply - send another request", []),
+ NewState = send_requests(State, 1),
+ client_loop(NewState#state{nof_reps = NewState#state.nof_reps + 1,
+ nof_reqs = NewState#state.nof_reqs + 1});
+
+ {http, {ReqId, {error, Reason}}} ->
+ ?INFO("[generating] request ~p failed: "
+ "~n Reason: ~p"
+ "~n NofReqs: ~p"
+ "~n NofReps: ~p",
+ [ReqId, Reason, State#state.nof_reqs, State#state.nof_reps]),
+ exit({Reason, generating, State#state.nof_reqs, State#state.nof_reps});
+
+ Else ->
+ ?LOG("[generating] received unexpected message: "
+ "~n~p", [Else]),
+ unexpected_data(Else),
+ client_loop(State)
+ end;
+
+%% The client no longer issues any new requests, instead it
+%% waits for replies for all the oustanding requests to
+%% arrive.
+client_loop(#state{mode = stopping,
+ time = Time,
+ last_req = LastReqId} = State) ->
+ receive
+ {http, {LastReqId, {{_, 200, _}, _, _}}} ->
+ ?DEBUG("[stopping] received reply for last request (~p)", [LastReqId]),
+ time_to_complete(State),
+ ok;
+
+ {http, {ReqId, {{_, 200, _}, _, _}}} ->
+ ?DEBUG("[stopping] received reply ~p", [ReqId]),
+ client_loop(State);
+
+ {http, {ReqId, {error, Reason}}} ->
+ ?INFO("[stopping] request ~p failed: "
+ "~n Reason: ~p"
+ "~n NofReqs: ~p"
+ "~n NofReps: ~p",
+ [ReqId, Reason, State#state.nof_reqs, State#state.nof_reps]),
+ exit({Reason, stopping, State#state.nof_reqs, State#state.nof_reps});
+
+ Else ->
+ ?LOG("[stopping] received unexpected message: "
+ "~n~p", [Else]),
+ unexpected_data(Else),
+ client_loop(State)
+
+ after Time ->
+ ?INFO("timeout when"
+ "~n Number of requests: ~p"
+ "~n Number of replies: ~p",
+ [State#state.nof_reqs, State#state.nof_reps]),
+ exit({timeout, State#state.nof_reqs, State#state.nof_reps})
+ end.
+
+req_reply(#state{nof_reqs = NofReqs, nof_reps = NofReps}) ->
+ load_data({req_reply, node(), NofReqs, NofReps}).
+
+time_to_complete(#state{stop_time = StopTime}) ->
+ StoppedTime = os:timestamp(),
+ load_data({time_to_complete, node(), StopTime, StoppedTime}).
+
+load_data(Data) ->
+ global:send(?CTRL, {load_data, Data}).
+
+unexpected_data(Else) ->
+ global:send(?CTRL, {unexpected_data, Else}).
+
+
+send_requests(#state{sizes = Sizes} = State, N) ->
+ send_requests(State, N, Sizes).
+
+send_requests(State, 0, Sizes) ->
+ State#state{sizes = Sizes};
+send_requests(#state{socket_type = SocketType,
+ cert_file = CertFile} = State, N, [Sz | Sizes]) ->
+ URL = lists:flatten(io_lib:format("~s~w", [State#state.url, Sz])),
+ Method = get,
+ Request = {URL, []},
+ HTTPOptions =
+ case SocketType of
+ ip_comm ->
+ [];
+ _ ->
+ SslOpts = [{verify, 0},
+ {certfile, CertFile},
+ {keyfile, CertFile}],
+ case SocketType of
+ ssl ->
+ [{ssl, SslOpts}];
+ ossl ->
+ [{ssl, {ossl, SslOpts}}];
+ essl ->
+ [{ssl, {essl, SslOpts}}]
+ end
+ end,
+ Options = [{sync, false}],
+ {ok, Ref} = httpc:request(Method, Request, HTTPOptions, Options),
+ send_requests(State#state{last_req = Ref}, N-1, lists:append(Sizes, [Sz])).
+
+
+timestamp() ->
+ os:timestamp().
+
+
+get_node_info() ->
+ [{cpu_topology, erlang:system_info(cpu_topology)},
+ {heap_type, erlang:system_info(heap_type)},
+ {nof_schedulers, erlang:system_info(schedulers)},
+ {otp_release, erlang:system_info(otp_release)},
+ {version, erlang:system_info(version)},
+ {system_version, erlang:system_info(system_version)},
+ {system_architecture, erlang:system_info(system_architecture)}].
+
+
diff --git a/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl b/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl
new file mode 100644
index 0000000000..950d2632f7
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_ctrl.erl
@@ -0,0 +1,1530 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The httpd load test (hdlt) controller/collector module,
+%% This module contains all the code of the httpd load test
+%% controller/collector. It sets up the test, starts all
+%% server and client nodes and applications and finally
+%% collects test data.
+%%----------------------------------------------------------------------
+
+-module(hdlt_ctrl).
+
+-export([start/1, stop/0, help/0]).
+
+-export([init/1, proxy/7]).
+
+-include_lib("kernel/include/file.hrl").
+-include("hdlt_logger.hrl").
+
+-define(DEFAULT_SENDRATE, 89).
+-define(DEFAULT_TEST_TIME, 120). % 2 minutes
+-define(DEFAULT_PORT, 8889).
+-define(TIMEOUT, 60000).
+-define(DEFAULT_MAX_NOF_SCHEDULERS, 8).
+-define(DEFAULT_SERVER_DIR, "/tmp/hdlt").
+-define(DEFAULT_WORK_DIR, "/tmp/hdlt").
+-define(SSH_PORT, 22).
+-define(DEFAULT_SOCKET_TYPE, ip_comm).
+-define(DEFAULT_SERVER_CERT, "hdlt_ssl_server_cert.pem").
+-define(DEFAULT_CLIENT_CERT, "hdlt_ssl_client_cert.pem").
+-define(SSH_CONNECT_TIMEOUT, 5000).
+-define(NODE_START_TIMEOUT, 5000).
+-define(LOCAL_PROXY_START_TIMEOUT, ?NODE_START_TIMEOUT * 4).
+-define(DEFAULT_DEBUGS,
+ [{ctrl, info}, {slave, silence}, {proxy, silence}, {client, silence}]).
+-define(DEFAULT_WORK_SIM, 10000).
+-define(DEFAULT_DATA_SIZE_START, 500).
+-define(DEFAULT_DATA_SIZE_END, 1500).
+-define(DEFAULT_DATA_SIZE_INCR, 1).
+-define(DEFAULT_DATA_SIZE, {?DEFAULT_DATA_SIZE_START,
+ ?DEFAULT_DATA_SIZE_END,
+ ?DEFAULT_DATA_SIZE_INCR}).
+
+
+%% hdlt = httpd load test
+
+-define(COLLECTOR, hdlt_ctrl).
+-define(RESULTS_TAB, hdlt_results).
+
+-define(CLIENT_MOD, hdlt_client).
+-define(CLIENT_NODE_NAME, ?CLIENT_MOD).
+
+-define(SERVER_MOD, hdlt_server).
+-define(SERVER_NODE_NAME, ?SERVER_MOD).
+
+-define(LOGGER, hdlt_logger).
+
+
+-record(state,
+ {
+ url,
+ test_time,
+ send_rate,
+ http_server,
+ http_port,
+ results = ?RESULTS_TAB,
+ nodes,
+ server_root,
+ doc_root,
+ server_dir,
+ work_dir,
+ server_conn,
+ client_conns = [],
+ client_mod = ?CLIENT_MOD,
+ clients,
+ nof_schedulers = 0,
+ max_nof_schedulers,
+ socket_type,
+ server_cert_file,
+ client_cert_file,
+ debugs,
+ client_sz_from,
+ client_sz_to,
+ client_sz_incr
+ }
+ ).
+
+-record(proxy,
+ {
+ mode,
+ mod,
+ connection,
+ channel,
+ host,
+ cmd,
+ node_name,
+ node,
+ ref,
+ erl_path,
+ paths,
+ args
+ }).
+
+-record(connection,
+ {
+ proxy,
+ node,
+ node_name,
+ host
+ }).
+
+
+-record(client, {host, path, version}).
+-record(server, {host, path, version}).
+
+
+start(Config) when is_list(Config) ->
+ proc_lib:start_link(?MODULE, init, [Config]).
+
+stop() ->
+ global:send(?COLLECTOR, stop).
+
+init(Config) ->
+ %% io:format("Config: ~n~p~n", [Config]),
+ case (catch do_init(Config)) of
+ {ok, State} ->
+ proc_lib:init_ack({ok, self()}),
+ loop(State);
+ {error, _Reason} = Error ->
+ proc_lib:init_ack(Error),
+ ok;
+ {'EXIT', Reason} ->
+ proc_lib:init_ack({error, Reason}),
+ ok
+ end.
+
+do_init(Config) ->
+ %% Do not trap exit, but register ourself
+ global:register_name(?COLLECTOR, self()),
+
+ State = #state{},
+ ets:new(State#state.results, [bag, named_table]),
+
+ hdlt_logger:start(),
+ global:sync(),
+
+ %% Maybe enable debug
+ Debugs = get_debugs(Config),
+ ?SET_NAME("HDLT CTRL"),
+ set_debug_level(Debugs),
+
+ ?DEBUG("network info: "
+ "~n Global names: ~p"
+ "~n Nodes: ~p", [global:registered_names(), nodes()]),
+
+ %% Read config
+ ?LOG("read config", []),
+ SendRate = get_send_rate(Config),
+ Clients = get_clients(Config),
+ TestTime = get_test_time(Config),
+ Server = get_server(Config),
+ Port = get_port(Config),
+ ServerDir = get_server_dir(Config),
+ WorkingDir = get_work_dir(Config),
+ MaxNofSchedulers = get_max_nof_schedulers(Config),
+ SocketType = get_socket_type(Config),
+ ServerCertFile = get_server_cert_file(Config),
+ ClientCertFile = get_client_cert_file(Config),
+ WorkSim = get_work_sim(Config),
+ {From, To, Incr} = get_data_size(Config),
+
+ URL = url(Server, Port, SocketType, WorkSim),
+ ServerRoot = filename:join(ServerDir, "server_root"),
+ DocRoot = ServerRoot, %% Not really used in this test
+
+ ?DEBUG("randomize setup", []),
+ randomized_sizes_init(),
+
+ %% Start used applications
+ ?DEBUG("ensure crypto started", []),
+ crypto:start(),
+ ?DEBUG("ensure ssh started", []),
+ ssh:start(),
+
+ State2 = State#state{server_root = ServerRoot,
+ doc_root = DocRoot,
+ server_dir = ServerDir,
+ work_dir = WorkingDir,
+ max_nof_schedulers = MaxNofSchedulers,
+ socket_type = SocketType,
+ server_cert_file = ServerCertFile,
+ client_cert_file = ClientCertFile,
+ http_server = Server,
+ http_port = Port,
+ url = URL,
+ test_time = TestTime,
+ send_rate = SendRate,
+ clients = Clients,
+ debugs = Debugs,
+ client_sz_from = From,
+ client_sz_to = To,
+ client_sz_incr = Incr},
+
+ ?LOG("prepare server host", []),
+ prepare_server_host(State2),
+
+ ?LOG("prepare client hosts", []),
+ State3 = prepare_client_hosts(State2),
+
+ ?LOG("basic init done", []),
+ {ok, State3}.
+
+
+loop(#state{nof_schedulers = N, max_nof_schedulers = M} = State) when N > M ->
+
+ ?INFO("Starting to analyse data", []),
+
+ AnalysedTab = analyse_data(State),
+
+ Files = save_results_to_file(AnalysedTab, State),
+ io:format("~n******************************************************"
+ "~n~nResult(s) saved to: ~n~p~n", [Files]),
+ clean_up(State);
+
+loop(#state{url = URL,
+ test_time = TestTime,
+ send_rate = SendRate,
+ nof_schedulers = NofSchedulers} = State) ->
+
+ {StartH, StartM, StartS} = erlang:time(),
+
+ ?INFO("Performing test with ~p smp-scheduler(s): ~n"
+ " It will take a minimum of: ~p seconds. ~n"
+ " Start time: ~.2.0w:~.2.0w:~.2.0w",
+ [NofSchedulers, round(TestTime/1000), StartH, StartM, StartS]),
+
+ %% Start the server node
+ %% (The local proxy, the node, the remote proxy, and the inets framework)
+ State1 = start_server_node(State),
+ ?DEBUG("nodes after server start: ~p", [nodes() -- [node()]]),
+
+ %% Start the client node(s)
+ %% (The local proxy, the node, the remote proxy, and the inets framework)
+ ?LOG("start client node(s)", []),
+ State2 = start_client_nodes(State1),
+ ?DEBUG("nodes after client(s) start: ~p", [nodes() -- [node()]]),
+
+ ?LOG("start server", []),
+ start_server(State2),
+
+ ?LOG("start clients", []),
+ start_clients(State2, URL, TestTime, SendRate),
+
+ ?LOG("release clients", []),
+ release_clients(State2),
+
+ ?LOG("collect data", []),
+ collect_data(State2),
+
+ ?LOG("stop all nodes", []),
+ State3 = stop_nodes(State2),
+
+ ?INFO("Test with ~p smp-scheduler(s) complete"
+ "~n~n"
+ "****************************************************************"
+ "~n",
+ [NofSchedulers]),
+ loop(State3#state{nof_schedulers = NofSchedulers + 1}).
+
+
+prepare_server_host(#state{server_root = ServerRoot,
+ http_server = #server{host = Host},
+ socket_type = SocketType,
+ server_cert_file = CertFile}) ->
+ ?INFO("prepare server host ~s", [Host]),
+ Opts = [{user_interaction, false},
+ {silently_accept_hosts, true},
+ {timeout, 2*?SSH_CONNECT_TIMEOUT},
+ {connect_timeout, ?SSH_CONNECT_TIMEOUT}],
+ case ssh_sftp:start_channel(Host, Opts) of
+ {ok, Sftp, ConnectionRef} ->
+ ?DEBUG("sftp connection established - now transer server content",
+ []),
+ create_server_content(Sftp, ServerRoot, SocketType, CertFile),
+ ?DEBUG("server content transfered - now close ssh connection ",
+ []),
+ ssh:close(ConnectionRef),
+ ?DEBUG("server preparation complete ", []),
+ ok;
+ Error ->
+ ?INFO("FAILED creating sftp channel to server host ~s: "
+ "~n ~p", [Host, Error]),
+ exit({failed_establishing_sftp_connection, Error})
+ end.
+
+create_server_content(Sftp, ServerRoot, SocketType, CertFile) ->
+ %% Create server root
+ ?DEBUG("ensure existence of ~p", [ServerRoot]),
+ ensure_remote_dir_exist(Sftp, ServerRoot),
+
+ %% Create the server ebin dir (for the starter module)
+ EBIN = filename:join(ServerRoot, "ebin"),
+ ?DEBUG("make ebin dir: ~p", [EBIN]),
+ maybe_create_remote_dir(Sftp, EBIN),
+
+ %% Create the server ebin dir (for the starter module)
+ LOG = filename:join(ServerRoot, "log"),
+ ?DEBUG("make log dir: ~p", [LOG]),
+ maybe_create_remote_dir(Sftp, LOG),
+
+ LocalServerMod = local_server_module(),
+ ?DEBUG("copy server stub/proxy module ~s", [LocalServerMod]),
+ RemoteServerMod = remote_server_module(EBIN),
+ {ok, ServerModBin} = file:read_file(LocalServerMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteServerMod, ServerModBin),
+
+ LocalSlaveMod = local_slave_module(),
+ ?DEBUG("copy slave module ~s", [LocalSlaveMod]),
+ RemoteSlaveMod = remote_slave_module(EBIN),
+ {ok, SlaveModBin} = file:read_file(LocalSlaveMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteSlaveMod, SlaveModBin),
+
+ LocalLoggerMod = local_logger_module(),
+ ?DEBUG("copy logger module ~s", [LocalLoggerMod]),
+ RemoteLoggerMod = remote_logger_module(EBIN),
+ {ok, LoggerModBin} = file:read_file(LocalLoggerMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteLoggerMod, LoggerModBin),
+
+ %% Create the inets server data dir
+ CGI = filename:join(ServerRoot, "cgi-bin"),
+ ?DEBUG("make cgi dir: ~p", [CGI]),
+ maybe_create_remote_dir(Sftp, CGI),
+
+ LocalRandomMod = local_random_html_module(),
+ ?DEBUG("copy random-html module ~s", [LocalRandomMod]),
+ RemoteRandomMod = remote_random_html_module(EBIN),
+ {ok, RandomModBin} = file:read_file(LocalRandomMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteRandomMod, RandomModBin),
+
+ case SocketType of
+ ip_comm ->
+ ok;
+ _ ->
+ SSLDir = filename:join(ServerRoot, "ssl"),
+ ?DEBUG("make conf dir: ~p", [SSLDir]),
+ maybe_create_remote_dir(Sftp, SSLDir),
+ ?DEBUG("copy ssl cert file ~s", [CertFile]),
+ {ok, CertBin} = file:read_file(CertFile),
+ RemoteCertFile = filename:join(SSLDir,
+ filename:basename(CertFile)),
+ ok = ssh_sftp:write_file(Sftp, RemoteCertFile, CertBin),
+ ok
+ end,
+
+ ?DEBUG("done", []),
+ ok.
+
+remote_server_module(Path) ->
+ Mod = server_module(),
+ filename:join(Path, Mod).
+
+local_server_module() ->
+ Mod = server_module(),
+ case code:where_is_file(Mod) of
+ Path when is_list(Path) ->
+ Path;
+ _ ->
+ exit({server_module_not_found, Mod})
+ end.
+
+server_module() ->
+ module(?SERVER_MOD).
+
+
+prepare_client_hosts(#state{work_dir = WorkDir,
+ clients = Clients,
+ socket_type = SocketType,
+ client_cert_file = CertFile} = State) ->
+ Clients2 =
+ prepare_client_hosts(WorkDir, SocketType, CertFile, Clients, []),
+ State#state{clients = Clients2}.
+
+prepare_client_hosts(_WorkDir, _SocketType, _CertFile, [], Acc) ->
+ lists:reverse(Acc);
+prepare_client_hosts(WorkDir, SocketType, CertFile, [Client|Clients], Acc) ->
+ case prepare_client_host(WorkDir, SocketType, CertFile, Client) of
+ ok ->
+ prepare_client_hosts(WorkDir, SocketType, CertFile, Clients,
+ [Client|Acc]);
+ _ ->
+ prepare_client_hosts(WorkDir, SocketType, CertFile, Clients, Acc)
+ end.
+
+prepare_client_host(WorkDir, SocketType, CertFile, #client{host = Host}) ->
+ ?INFO("prepare client host ~s", [Host]),
+ Opts = [{user_interaction, false},
+ {silently_accept_hosts, true},
+ {timeout, 2*?SSH_CONNECT_TIMEOUT},
+ {connect_timeout, ?SSH_CONNECT_TIMEOUT}],
+ case ssh_sftp:start_channel(Host, Opts) of
+ {ok, Sftp, ConnectionRef} ->
+ ?DEBUG("sftp connection established - now transer client content",
+ []),
+ create_client_content(Sftp, WorkDir, SocketType, CertFile),
+ ?DEBUG("client content transered - now close ssh connection ", []),
+ ssh:close(ConnectionRef),
+ ?DEBUG("client preparation complete ", []),
+ ok;
+ Error ->
+ ?INFO("FAILED creating sftp channel to client host ~s: skipping"
+ "~n ~p", [Host, Error]),
+ Error
+ end.
+
+create_client_content(Sftp, WorkDir, SocketType, CertFile) ->
+ %% Create work dir
+ ?DEBUG("ensure existence of ~p", [WorkDir]),
+ ensure_remote_dir_exist(Sftp, WorkDir),
+
+ %% Create the client ebin dir
+ EBIN = filename:join(WorkDir, "ebin"),
+ RemoteClientMod = remote_client_module(EBIN),
+ ?DEBUG("make ebin dir: ~p", [EBIN]),
+ maybe_create_remote_dir(Sftp, EBIN),
+
+ LocalClientMod = local_client_module(),
+ ?DEBUG("copy client stub/proxy module ~s", [LocalClientMod]),
+ {ok, ClientModBin} = file:read_file(LocalClientMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteClientMod, ClientModBin),
+
+ LocalSlaveMod = local_slave_module(),
+ ?DEBUG("copy slave module ~s", [LocalSlaveMod]),
+ RemoteSlaveMod = remote_slave_module(EBIN),
+ {ok, SlaveModBin} = file:read_file(LocalSlaveMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteSlaveMod, SlaveModBin),
+
+ LocalLoggerMod = local_logger_module(),
+ ?DEBUG("copy logger module ~s", [LocalLoggerMod]),
+ RemoteLoggerMod = remote_logger_module(EBIN),
+ {ok, LoggerModBin} = file:read_file(LocalLoggerMod),
+ ok = ssh_sftp:write_file(Sftp, RemoteLoggerMod, LoggerModBin),
+
+ case SocketType of
+ ip_comm ->
+ ok;
+ _ ->
+ %% We should really store the remote path somewhere as
+ %% we use it when starting the client service...
+ SSLDir = filename:join(WorkDir, "ssl"),
+ ?DEBUG("make ssl dir: ~p", [SSLDir]),
+ maybe_create_remote_dir(Sftp, SSLDir),
+ ?DEBUG("copy ssl cert file ~s", [CertFile]),
+ {ok, CertBin} = file:read_file(CertFile),
+ RemoteCertFile = filename:join(SSLDir,
+ filename:basename(CertFile)),
+ ok = ssh_sftp:write_file(Sftp, RemoteCertFile, CertBin),
+ ok
+ end,
+
+ ?DEBUG("done", []),
+ ok.
+
+remote_client_module(Path) ->
+ Mod = client_module(),
+ filename:join(Path, Mod).
+
+local_client_module() ->
+ Mod = client_module(),
+ case code:where_is_file(Mod) of
+ Path when is_list(Path) ->
+ Path;
+ _ ->
+ exit({client_module_not_found, Mod})
+ end.
+
+client_module() ->
+ module(?CLIENT_MOD).
+
+
+remote_slave_module(Path) ->
+ Mod = slave_module(),
+ filename:join(Path, Mod).
+
+local_slave_module() ->
+ Mod = slave_module(),
+ case code:where_is_file(Mod) of
+ Path when is_list(Path) ->
+ Path;
+ _ ->
+ exit({slave_module_not_found, Mod})
+ end.
+
+slave_module() ->
+ module(hdlt_slave).
+
+
+remote_logger_module(Path) ->
+ Mod = logger_module(),
+ filename:join(Path, Mod).
+
+local_logger_module() ->
+ Mod = logger_module(),
+ case code:where_is_file(Mod) of
+ Path when is_list(Path) ->
+ Path;
+ _ ->
+ exit({logger_module_not_found, Mod})
+ end.
+
+logger_module() ->
+ module(hdlt_logger).
+
+
+remote_random_html_module(Path) ->
+ Mod = random_html_module(),
+ filename:join(Path, Mod).
+
+local_random_html_module() ->
+ Mod = random_html_module(),
+ case code:where_is_file(Mod) of
+ Path when is_list(Path) ->
+ Path;
+ _ ->
+ exit({random_module_not_found, Mod})
+ end.
+
+random_html_module() ->
+ module(hdlt_random_html).
+
+
+module(Mod) ->
+ Ext = string:to_lower(erlang:system_info(machine)),
+ lists:flatten(io_lib:format("~w.~s", [Mod, Ext])).
+
+
+%% -----------------------------------------------------------------------
+%% - For every node created (server and client both) there is both
+%% a local and remote proxy.
+%% - The local proxy is running on the local (controller/collector) node.
+%% - The remote proxy is running on the client or server node(s).
+%% - The local (ctrl) proxy monitor the remote (server/client) proxy.
+%% - The remote (server/client) proxy monitor the local (ctrl) proxy.
+%%
+
+start_client_nodes(#state{clients = Clients,
+ work_dir = WorkDir,
+ debugs = Debugs} = State) ->
+ Connections =
+ [start_client_node(Client, WorkDir, Debugs) || Client <- Clients],
+ State#state{client_conns = Connections}.
+
+start_client_node(#client{path = ErlPath, host = Host}, WorkDir, Debugs) ->
+ ?INFO("start client on host ~p", [Host]),
+ EbinDir = filename:join(WorkDir, "ebin"),
+ start_client_node(Host, ErlPath, [EbinDir], Debugs).
+
+start_client_node(Host, ErlPath, Paths, Debugs) ->
+ start_node(Host, ?CLIENT_NODE_NAME,
+ ErlPath, Paths, [], ?CLIENT_MOD, Debugs).
+
+
+start_server_node(#state{http_server = #server{path = ErlPath, host = Host},
+ server_root = ServerRoot,
+ nof_schedulers = NofScheds,
+ debugs = Debugs} = State) ->
+ ?INFO("start server on host ~p", [Host]),
+ CgiBinDir = filename:join(ServerRoot, "cgi-bin"),
+ EbinDir = filename:join(ServerRoot, "ebin"),
+ Connection =
+ start_server_node(Host, ErlPath, [CgiBinDir, EbinDir],
+ Debugs, NofScheds),
+ State#state{server_conn = Connection}.
+
+start_server_node(Host, ErlPath, Paths, Debugs, NofScheds) ->
+ Args =
+ if
+ NofScheds =:= 0 ->
+ "-smp disable";
+ true ->
+ lists:flatten(io_lib:format("-smp +S ~w", [NofScheds]))
+ end,
+ start_node(Host, ?SERVER_NODE_NAME,
+ ErlPath, Paths, Args, ?SERVER_MOD, Debugs).
+
+
+%% -----------------------------------------------------------------------
+%% - For every node created (server and client both) there is both
+%% a local and remote proxy.
+%% - The local proxy is running on the local (controller/collector) node.
+%% - The remote proxy is running on the client or server node(s).
+%% - The local (ctrl) proxy monitor the remote (server/client) proxy.
+%% - The remote (server/client) proxy monitor the local (ctrl) proxy.
+%%
+
+start_node(Host, NodeName, ErlPath, Paths, Args, Module, Debugs) ->
+ %% Start the (local) proxy
+ ?DEBUG("start_node -> start local proxy and remote node", []),
+ ProxyDebug = proplists:get_value(proxy, Debugs, silence),
+ Proxy = proxy_start(Host, NodeName, ErlPath, Paths, Args, Module,
+ ProxyDebug),
+
+ ?DEBUG("start_node -> local proxy started - now start node", []),
+ SlaveDebug = proplists:get_value(slave, Debugs, silence),
+ Node = proxy_start_node(Proxy, SlaveDebug),
+
+ ?DEBUG("start_node -> sync global", []),
+ global:sync(),
+
+ ?DEBUG("start_node -> start remote proxy", []),
+ proxy_start_remote(Proxy),
+
+ ?DEBUG("start_node -> start (remote) inets framework", []),
+ proxy_start_inets(Proxy),
+
+ ?DEBUG("start_node -> done", []),
+ #connection{proxy = Proxy, node = Node, node_name = NodeName, host = Host}.
+
+
+proxy_start(Host, NodeName, ErlPath, Paths, Args, Module, Debug) ->
+ ?LOG("try starting local proxy for ~p@~s", [NodeName, Host]),
+ ProxyArgs = [Host, NodeName, ErlPath, Paths, Args, Module, Debug],
+ case proc_lib:start_link(?MODULE, proxy,
+ ProxyArgs, ?LOCAL_PROXY_START_TIMEOUT) of
+ {ok, Proxy} ->
+ Proxy;
+ Error ->
+ exit({failed_starting_proxy, Error})
+ end.
+
+proxy_start_node(Proxy, Debug) ->
+ {ok, Node} = proxy_request(Proxy, {start_node, Debug}),
+ Node.
+
+proxy_start_remote(Proxy) ->
+ proxy_request(Proxy, start_remote_proxy).
+
+proxy_start_inets(Proxy) ->
+ proxy_request(Proxy, start_inets).
+
+proxy_start_service(Proxy, Args) ->
+ proxy_request(Proxy, {start_service, Args}).
+
+proxy_release(Proxy) ->
+ proxy_request(Proxy, release).
+
+proxy_stop(Proxy) ->
+ StopResult = proxy_request(Proxy, stop),
+ ?DEBUG("proxy stop result: ~p", [StopResult]),
+ StopResult.
+
+proxy_request(Proxy, Req) ->
+ Ref = make_ref(),
+ Proxy ! {proxy_request, Ref, self(), Req},
+ receive
+ {proxy_reply, Ref, Proxy, Rep} ->
+ Rep
+ end.
+
+proxy_reply(From, Ref, Rep) ->
+ From ! {proxy_reply, Ref, self(), Rep}.
+
+proxy(Host, NodeName, ErlPath, Paths, Args, Module, Debug) ->
+ process_flag(trap_exit, true),
+ SName = lists:flatten(
+ io_lib:format("HDLT CTRL PROXY[~p,~s,~w]",
+ [self(), Host, NodeName])),
+ ?SET_NAME(SName),
+ ?SET_LEVEL(Debug),
+ ?LOG("starting with"
+ "~n Host: ~p"
+ "~n NodeName: ~p"
+ "~n ErlPath: ~p"
+ "~n Paths: ~p"
+ "~n Args: ~p"
+ "~n Module: ~p", [Host, NodeName, ErlPath, Paths, Args, Module]),
+ State = #proxy{mode = started,
+ mod = Module,
+ host = Host,
+ node_name = NodeName,
+ erl_path = ErlPath,
+ paths = Paths,
+ args = Args},
+ proc_lib:init_ack({ok, self()}),
+ ?DEBUG("started", []),
+ proxy_loop(State).
+
+
+proxy_loop(#proxy{mode = stopping}) ->
+ receive
+ {proxy_request, Ref, From, stop} ->
+ ?LOG("[stopping] received stop order", []),
+ proxy_reply(From, Ref, ok),
+ exit(normal);
+
+ {'EXIT', Pid, Reason} ->
+ ?INFO("[stopping] received exit message from ~p: "
+ "~n Reason: ~p", [Pid, Reason]),
+ exit(Reason)
+
+ end;
+
+proxy_loop(#proxy{mode = started,
+ host = Host,
+ node_name = NodeName,
+ erl_path = ErlPath,
+ paths = Paths,
+ args = Args} = State) ->
+ receive
+ {proxy_request, Ref, From, {start_node, Debug}} ->
+ ?LOG("[starting] received start_node order", []),
+ case hdlt_slave:start_link(Host, NodeName,
+ ErlPath, Paths, Args,
+ Debug) of
+ {ok, Node} ->
+ ?DEBUG("[starting] node ~p started - now monitor", [Node]),
+ erlang:monitor_node(Node, true),
+ State2 = State#proxy{mode = operational,
+ node = Node},
+ proxy_reply(From, Ref, {ok, Node}),
+ proxy_loop(State2);
+ {error, Reason} ->
+ ?INFO("[starting] failed starting node: "
+ "~n Reason: ~p", [Reason]),
+ exit({failed_starting_node, {Host, NodeName, Reason}})
+ end;
+
+ {'EXIT', Pid, Reason} ->
+ ?INFO("[stopping] received exit message from ~p: "
+ "~n Reason: ~p", [Pid, Reason]),
+ exit(Reason)
+
+ end;
+
+proxy_loop(#proxy{mode = operational,
+ mod = Mod,
+ node = Node} = State) ->
+ ?DEBUG("[operational] await command", []),
+ receive
+ {proxy_request, Ref, From, start_remote_proxy} ->
+ ?LOG("[operational] start remote proxy", []),
+ case rpc:call(Node, Mod, start, [?GET_LEVEL()]) of
+ {ok, Pid} ->
+ ?DEBUG("[operational] remote proxy started (~p) - "
+ "create monitor", [Pid]),
+ ProxyRef = erlang:monitor(process, Pid),
+ ?DEBUG("[operational] monitor: ~p", [Ref]),
+ proxy_reply(From, Ref, ok),
+ proxy_loop(State#proxy{ref = ProxyRef});
+ Error ->
+ ?INFO("[operational] failed starting remote proxy"
+ "~n Error: ~p", [Error]),
+ ReplyReason = {failed_starting_remote_proxy,
+ {Node, Error}},
+ Reply = {error, ReplyReason},
+ proxy_reply(From, Ref, Reply),
+ exit({failed_starting_remote_proxy, {Node, Error}})
+ end;
+
+ {proxy_request, Ref, From, start_inets} ->
+ ?INFO("[operational] start inets framework", []),
+ rpc:cast(Node, Mod, start_inets, []),
+ proxy_reply(From, Ref, ok),
+ proxy_loop(State);
+
+ {proxy_request, Ref, From, {start_service, Args}} ->
+ ?INFO("[operational] start service with"
+ "~n ~p", [Args]),
+ case rpc:call(Node, Mod, start_service, Args) of
+ ok ->
+ ?DEBUG("[operational] service started", []),
+ proxy_reply(From, Ref, ok),
+ proxy_loop(State);
+ Error ->
+ ?INFO("[operational] failed starting service: "
+ "~n Args. ~p"
+ "~n Error: ~p", [Args, Error]),
+ erlang:demonitor(State#proxy.ref, [flush]),
+ Reply = {error, {failed_starting_service, Node, Error}},
+ proxy_reply(From, Ref, Reply),
+ exit({failed_starting_service, Node, Error})
+ end;
+
+ {proxy_request, Ref, From, release} ->
+ ?INFO("[operational] release", []),
+ rpc:call(Node, Mod, release, []),
+ proxy_reply(From, Ref, ok),
+ proxy_loop(State);
+
+ {proxy_request, Ref, From, stop} ->
+ ?INFO("[operational] received stop order", []),
+ erlang:demonitor(State#proxy.ref, [flush]),
+ ?DEBUG("[operational] rpc cast stop order", []),
+ rpc:cast(Node, Mod, stop, []),
+ %% And wait for the node death to be reported
+ Reason =
+ receive
+ {nodedown, Node} when State#proxy.node =:= Node ->
+ ok
+ after 10000 ->
+ ?INFO("Node did not die within expected time frame",
+ []),
+ {node_death_timeout, Node}
+ end,
+ ?DEBUG("[operational] ack stop", []),
+ proxy_reply(From, Ref, Reason),
+ exit(normal);
+
+ {nodedown, Node} when State#proxy.node =:= Node ->
+ ?INFO("[operational] received unexpected nodedoen message", []),
+ exit({node_died, Node});
+
+ {'DOWN', Ref, process, _, normal} when State#proxy.ref =:= Ref ->
+ ?INFO("[operational] remote proxy terminated normally", []),
+ proxy_loop(State#proxy{ref = undefined,
+ connection = undefined,
+ mode = stopping});
+
+ {'DOWN', Ref, process, _, noconnection} when State#proxy.ref =:= Ref ->
+ ?INFO("[operational] remote proxy terminated - no node", []),
+ proxy_loop(State#proxy{ref = undefined,
+ connection = undefined,
+ mode = stopping});
+
+ {'DOWN', Ref, process, _, Reason} when State#proxy.ref =:= Ref ->
+ ?INFO("[operational] remote proxy terminated: "
+ "~n Reason: ~p", [Reason]),
+ exit({remote_proxy_crash, Reason});
+
+ {'EXIT', Pid, Reason} ->
+ ?INFO("[operational] received unexpected exit message from ~p: "
+ "~n Reason: ~p", [Pid, Reason]),
+ proxy_loop(State)
+
+ end.
+
+
+stop_nodes(#state{server_conn = ServerConn,
+ client_conns = ClientConns} = State) ->
+ lists:foreach(
+ fun(#connection{proxy = Proxy, node_name = NodeName, host = Host}) ->
+ ?DEBUG("stop_erlang_nodes -> send stop order to local proxy ~p"
+ "~n for node ~p on ~s", [Proxy, NodeName, Host]),
+ proxy_stop(Proxy)
+ end,
+ ClientConns ++ [ServerConn]),
+ ?DEBUG("stop_erlang_nodes -> sleep some to give the nodes time to die",
+ []),
+ timer:sleep(1000),
+ ?DEBUG("stop_erlang_nodes -> and a final cleanup round", []),
+ lists:foreach(fun(Node) ->
+ ?INFO("try brutal stop node ~p", [Node]),
+ rpc:cast(Node, erlang, halt, [])
+ end,
+ nodes() -- [node()]),
+ ?DEBUG("stop_erlang_nodes -> done", []),
+ State#state{server_conn = undefined, client_conns = []}.
+
+
+%% The nodes on which the HDLT clients run have been started previously
+start_clients(#state{client_conns = Connections,
+ debugs = Debugs,
+ work_dir = WorkDir,
+ socket_type = SocketType,
+ client_cert_file = CertFile,
+ client_sz_from = From,
+ client_sz_to = To,
+ client_sz_incr = Incr},
+ URL, TestTime, SendRate) ->
+ Debug = proplists:get_value(client, Debugs, silence),
+ StartClient =
+ fun(#connection{host = Host} = Connection) ->
+ ?DEBUG("start client on ~p", [Host]),
+ start_client(Connection,
+ WorkDir, SocketType, CertFile,
+ URL, From, To, Incr,
+ TestTime, SendRate, Debug);
+ (_) ->
+ ok
+ end,
+ lists:foreach(StartClient, Connections).
+
+start_client(#connection{proxy = Proxy},
+ WorkDir, SocketType, LocalCertFile,
+ URL, From, To, Incr,
+ TestTime, SendRate, Debug) ->
+ SSLDir = filename:join(WorkDir, "ssl"),
+ CertFile = filename:join(SSLDir, filename:basename(LocalCertFile)),
+ Sizes = randomized_sizes(From, To, Incr),
+ Args = [SocketType, CertFile, URL, Sizes, TestTime, SendRate, Debug],
+ proxy_start_service(Proxy, [Args]).
+
+release_clients(#state{client_conns = Connections}) ->
+ ReleaseClient =
+ fun(#connection{proxy = Proxy,
+ host = Host}) ->
+ ?DEBUG("release client on ~p", [Host]),
+ proxy_release(Proxy);
+ (_) ->
+ ok
+ end,
+ lists:foreach(ReleaseClient, Connections).
+
+
+start_server(#state{server_conn = #connection{proxy = Proxy},
+ http_port = Port,
+ server_root = ServerRoot,
+ doc_root = DocRoot,
+ socket_type = SocketType,
+ server_cert_file = CertFile}) ->
+
+ HttpdConfig =
+ httpd_config(Port, "hdlt", ServerRoot, DocRoot, SocketType, CertFile),
+ ?LOG("start the httpd inets service with config: "
+ "~n ~p", [HttpdConfig]),
+ proxy_start_service(Proxy, [HttpdConfig]),
+ ?DEBUG("start_server -> done", []),
+ ok.
+
+
+httpd_config(Port, ServerName, ServerRoot, DocRoot,
+ SocketType, LocalCertFile) ->
+ LogDir = filename:join(ServerRoot, "log"),
+ ErrorLog = filename:join(LogDir, "error_log"),
+ TransferLog = filename:join(LogDir, "access_log"),
+
+ SSL =
+ case SocketType of
+ ip_comm ->
+ [];
+ _ -> % ssl
+ SSLDir = filename:join(ServerRoot, "ssl"),
+ CertFile =
+ filename:join(SSLDir, filename:basename(LocalCertFile)),
+ [
+ {ssl_certificate_file, CertFile},
+ {ssl_certificate_key_file, CertFile},
+ {ssl_verify_client, 0}
+ ]
+ end,
+ [{port, Port},
+ {server_name, ServerName},
+ {server_root, ServerRoot},
+ {document_root, DocRoot},
+ {error_log, ErrorLog},
+ {error_log_format, pretty},
+ {transfer_log, TransferLog},
+ {socket_type, SocketType},
+ {max_clients, 10000},
+ {modules, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi,
+ mod_dir, mod_get, mod_head, mod_log, mod_disk_log]},
+ {script_alias, {"/cgi-bin", filename:join(ServerRoot, "cgi-bin")}},
+ {erl_script_alias, {"/cgi-bin", [hdlt_random_html]}},
+ {erl_script_timeout, 120000} | SSL].
+
+
+clean_up(#state{server_root = ServerRoot,
+ work_dir = WorkDir,
+ http_server = #server{host = Host},
+ clients = Clients}) ->
+ ?DEBUG("begin server cleanup", []),
+ server_clean_up(ServerRoot, WorkDir, Host),
+ ?DEBUG("begin lient cleanup", []),
+ clients_clean_up(WorkDir, Clients),
+ ?DEBUG("cleanup done", []),
+ ok.
+
+server_clean_up(ServerRoot, WorkDir, Host) ->
+ ?DEBUG("server cleanup - create sftp channel", []),
+ {ok, Sftp, ConnectionRef} =
+ ssh_sftp:start_channel(Host, [{user_interaction, false},
+ {silently_accept_hosts, true}]),
+ ?DEBUG("server cleanup - delete ~p dirs", [ServerRoot]),
+ del_dirs(Sftp, ServerRoot),
+ ?DEBUG("server cleanup - delete ~p dirs", [WorkDir]),
+ del_dirs(Sftp, WorkDir),
+ ?DEBUG("server cleanup - close sftp channel", []),
+ ssh:close(ConnectionRef).
+
+clients_clean_up(_WorkDir, []) ->
+ ok;
+clients_clean_up(WorkDir, [Client|Clients]) ->
+ client_clean_up(WorkDir, Client),
+ clients_clean_up(WorkDir, Clients).
+
+client_clean_up(WorkDir, #client{host = Host}) ->
+ ?DEBUG("client cleanup - create sftp channel to ~p", [Host]),
+ {ok, Sftp, ConnectionRef} =
+ ssh_sftp:start_channel(Host, [{user_interaction, false},
+ {silently_accept_hosts, true}]),
+ ?DEBUG("client cleanup - delete ~p dirs", [WorkDir]),
+ del_dirs(Sftp, WorkDir),
+ ?DEBUG("client cleanup - close sftp channel", []),
+ ssh:close(ConnectionRef).
+
+
+del_dirs(Sftp, Dir) ->
+ case ssh_sftp:list_dir(Sftp, Dir) of
+ {ok, []} ->
+ ssh_sftp:del_dir(Sftp, Dir);
+ {ok, Files} ->
+ Files2 = [F || F <- Files, (F =/= "..") andalso (F =/= ".")],
+ lists:foreach(fun(File) when ((File =/= "..") andalso
+ (File =/= ".")) ->
+ FullPath = filename:join(Dir, File),
+ case ssh_sftp:read_file_info(Sftp,
+ FullPath) of
+ {ok, #file_info{type = directory}} ->
+ del_dirs(Sftp, FullPath),
+ ssh_sftp:del_dir(Sftp, FullPath);
+ {ok, _} ->
+ ssh_sftp:delete(Sftp, FullPath)
+ end
+ end, Files2);
+ _ ->
+ ok
+ end.
+
+collect_data(#state{clients = Clients} = State) ->
+ N = length(Clients),
+ collect_req_reply(N, State),
+ collect_time(N, State).
+
+collect_req_reply(0, _State) ->
+ ?DEBUG("all reply data collected", []),
+ ok;
+collect_req_reply(N, #state{nof_schedulers = NofScheduler,
+ results = Db,
+ client_conns = Conns} = State) ->
+ ?DEBUG("await reply data from ~p client(s)", [N]),
+ receive
+ {load_data,
+ {req_reply, Client, NoRequests, NoReplys}} ->
+ ?DEBUG("received req_reply load-data from client ~p: "
+ "~n Number of requests: ~p"
+ "~n Number of replies: ~p",
+ [Client, NoRequests, NoReplys]),
+ ets:insert(Db, {{NofScheduler, Client},
+ {req_reply, NoRequests, NoReplys}});
+ stop ->
+ ?INFO("received stop", []),
+ exit(self(), stop);
+
+ {client_exit, Client, Node, Reason} ->
+ ?INFO("Received unexpected client exit from ~p on node ~p "
+ "while collecting replies: "
+ "~n ~p", [Client, Node, Reason]),
+ case lists:keysearch(Node, #connection.node, Conns) of
+ {value, Conn} ->
+ ?LOG("Found problem connection: "
+ "~n ~p", [Conn]),
+ exit({unexpected_client_exit, Reason});
+ false ->
+ collect_req_reply(N, State)
+ end
+ end,
+ collect_req_reply(N-1, State).
+
+collect_time(0, _State) ->
+ ?DEBUG("all time data collected", []),
+ ok;
+collect_time(N, #state{nof_schedulers = NofScheduler,
+ results = Db,
+ client_conns = Conns} = State) ->
+ ?DEBUG("await time data from ~p clients", [N]),
+ receive
+ {load_data,
+ {time_to_complete, Client, StopTime, LastResponseTime}} ->
+ ?LOG("received time load-data from client ~p: "
+ "~n Time of stop: ~p"
+ "~n Time of last response: ~p",
+ [Client, StopTime, LastResponseTime]),
+ ets:insert(Db, {{NofScheduler, Client},
+ {time, StopTime, LastResponseTime}});
+ stop ->
+ ?INFO("received stop while collecting data, when N = ~p", [N]),
+ exit(self(), stop);
+
+ {client_exit, Client, Node, Reason} ->
+ ?INFO("Received unexpected exit from client ~p on node ~p "
+ "while collecting time data: "
+ "~n ~p", [Client, Node, Reason]),
+ case lists:keysearch(Node, #connection.node, Conns) of
+ {value, Conn} ->
+ ?LOG("Found problem connection: "
+ "~n ~p", [Conn]),
+ exit({unexpected_client_exit, Reason});
+ false ->
+ collect_req_reply(N, State)
+ end;
+
+ Else -> %%% Something is wrong!
+ ?INFO("RECEIVED UNEXPECTED MESSAGE WHILE COLLECTING TIME DATA: "
+ "~n ~p", [Else]),
+ collect_time(N, State)
+ end,
+ collect_time(N-1, State).
+
+analyse_data(#state{results = Db,
+ max_nof_schedulers = MaxNofSchedulers,
+ test_time = MicroSec}) ->
+ Tab = ets:new(analysed_results, [set]),
+ lists:foreach(fun(NofSchedulers) ->
+ Result = analyse(NofSchedulers, Db, MicroSec),
+ ets:insert(Tab, Result)
+ end, [N || N <- lists:seq(0, MaxNofSchedulers)]),
+ Tab.
+
+
+no_requests_replys(NoSchedulers, Tab) ->
+ NoRequests =
+ ets:select(Tab, [{{{NoSchedulers,'_'},{req_reply, '$1', '_'}},
+ [],['$$']}]),
+ NoReplys =
+ ets:select(Tab, [{{{NoSchedulers, '_'}, {req_reply, '_', '$1'}},
+ [], ['$$']}]),
+
+ {lists:sum(lists:append(NoRequests)),
+ lists:sum(lists:append(NoReplys))}.
+
+max_time_to_final_response(NofSchedulers, Tab) ->
+ Candidates =
+ ets:select(Tab, [{{{NofSchedulers, '_'}, {time, '$1', '$2'}},
+ [], ['$$']}]),
+
+ NewCandidates = lists:map(
+ fun([StopTime, LastTime]) ->
+ round(
+ timer:now_diff(LastTime, StopTime) / 100000)/10
+ end, Candidates),
+
+ lists:max(NewCandidates).
+
+
+analyse(NofSchedulers, Db, TestTime) ->
+ Sec = TestTime / 1000,
+ {NoRequests, NoReplys} = no_requests_replys(NofSchedulers, Db),
+ {NofSchedulers, round(NoReplys / Sec), NoRequests,
+ max_time_to_final_response(NofSchedulers, Db)}.
+
+
+save_results_to_file(AnalysedTab,
+ #state{socket_type = SocketType,
+ http_server = #server{host = Server},
+ max_nof_schedulers = MaxNofSchedulers}) ->
+ FileName = fun(Post) ->
+ File =
+ lists:flatten(
+ io_lib:format("~s_~w_~s",
+ [Server, SocketType, Post])),
+ filename:join("./", File)
+ end,
+ Reps = FileName("replys_per_sec.txt"),
+ Reqs = FileName("total_requests.txt"),
+ Decay = FileName("decay_time.txt"),
+
+ [FdReps, FdReqs, FdDecay] =
+ lists:map(fun(File) ->
+ {ok, Fd} = file:open(File, [write]),
+ Fd
+ end, [Reps, Reqs, Decay]),
+ lists:foreach(fun(NofSchedulers) ->
+ save_result_to_file(NofSchedulers,
+ FdReps, FdReqs,
+ FdDecay, AnalysedTab)
+ end, [N || N <- lists:seq(0, MaxNofSchedulers)]),
+ [Reps, Reqs, Decay].
+
+save_result_to_file(NofSchedulers,
+ FdReps, FdReqs, FdDecay, AnalysedTab) ->
+
+ [{NofSchedulers, NofRepsPerSec, NofReqs, MaxFinalResponseTime}] =
+ ets:lookup(AnalysedTab, NofSchedulers),
+
+ file:write(FdReps, io_lib:format("~p,~p~n",
+ [NofRepsPerSec, NofSchedulers])),
+ file:write(FdReqs, io_lib:format("~p,~p~n",
+ [NofReqs, NofSchedulers])),
+ file:write(FdDecay, io_lib:format("~p,~p~n", [MaxFinalResponseTime,
+ NofSchedulers])).
+
+
+help() ->
+ io:format("hdlt:start(Options). Where options:~n "
+ " ~n~p~n~n hdlt:start([]). -> hdlt:start(~p)~n~n",
+ [[{send_rate, "integer()",
+ "Numer of outstanding requests that a client "
+ "should have during the test to create a load situation."},
+ {clients, "[{path(), host()}]", "Paths to erlang and names of hosts to run clients on."},
+ {test_time, "{hours(), mins(), sec()}",
+ "How long the test should be run."},
+ {server, "{path(), host()}", "Path to erl and name of host to run the HTTP-server on."},
+ {port, "port()", "The port that the HTTP-server should use."},
+ {server_dir, "dir()", "The directory where the HTTP server "
+ " stores its contents and configuration."},
+ {work_dir, "dir()", "Path on the computer, where the test "
+ "is run, to a directory where the results can be saved."},
+ {max_no_schedulers, "integer()",
+ "Max number of schedulers to run."},
+ {socket_type, "Httpd configuration option socket_type"}],
+ defaults()]).
+
+
+defaults() ->
+ [{send_rate, ?DEFAULT_SENDRATE},
+ %% {clients, []},
+ {test_time, ?DEFAULT_TEST_TIME},
+ %% {server, ?DEFAULT_SERVER},
+ {port, ?DEFAULT_PORT},
+ {server_dir, ?DEFAULT_SERVER_DIR},
+ {work_dir, ?DEFAULT_WORK_DIR},
+ {max_nof_schedulers, ?DEFAULT_MAX_NOF_SCHEDULERS},
+ {socket_type, ?DEFAULT_SOCKET_TYPE}].
+
+
+get_debugs(Config) ->
+ ?DEBUG("get debugs", []),
+ Debugs = proplists:get_value(debug, Config, ?DEFAULT_DEBUGS),
+ verify_debugs(Debugs),
+ Debugs.
+
+verify_debugs([]) ->
+ ok;
+verify_debugs([{Tag, Debug}|Debugs]) ->
+ verify_debug(Tag, Debug),
+ verify_debugs(Debugs).
+
+verify_debug(Tag, Debug) ->
+ case lists:member(Tag, [ctrl, proxy, slave, client]) of
+ true ->
+ ok;
+ false ->
+ exit({bad_debug_tag, Tag})
+ end,
+ case lists:member(Debug, [silence, info, log, debug]) of
+ true ->
+ ok;
+ false ->
+ exit({bad_debug_level, Debug})
+ end.
+
+get_send_rate(Config) ->
+ ?DEBUG("get send_rate", []),
+ case proplists:get_value(send_rate, Config, ?DEFAULT_SENDRATE) of
+ SendRate when is_integer(SendRate) andalso (SendRate > 0) ->
+ SendRate;
+ BadSendRate ->
+ exit({bad_sendrate, BadSendRate})
+ end.
+
+
+get_clients(Config) ->
+ ?DEBUG("get clients", []),
+ case proplists:get_value(clients, Config, undefined) of
+ undefined ->
+ missing_mandatory_config(clients);
+ Clients when is_list(Clients) andalso (length(Clients) > 0) ->
+ case [#client{path = Path, host = Host} ||
+ {Path, Host} <- Clients] of
+ Clients2 when (length(Clients2) > 0) ->
+ Clients2;
+ _ ->
+ exit({bad_clients, Clients})
+ end;
+
+ BadClients ->
+ exit({bad_clients, BadClients})
+
+ end.
+
+get_server(Config) ->
+ ?DEBUG("get server", []),
+ case proplists:get_value(server, Config) of
+ {Path, Host} when is_list(Path) andalso is_list(Host) ->
+ #server{path = Path, host = Host};
+ undefined ->
+ missing_mandatory_config(server)
+ end.
+
+get_server_dir(Config) ->
+ ?DEBUG("get server_dir", []),
+ get_dir(server_dir, Config, ?DEFAULT_SERVER_DIR).
+
+get_work_dir(Config) ->
+ ?DEBUG("get work_dir", []),
+ get_dir(work_dir, Config, ?DEFAULT_WORK_DIR).
+
+get_dir(Key, Config, Default) ->
+ Dir = proplists:get_value(Key, Config, Default),
+ ensure_absolute(Dir),
+ Dir.
+
+ensure_absolute(Path) ->
+ case filename:pathtype(Path) of
+ absolute ->
+ ok;
+ PathType ->
+ exit({bad_pathtype, Path, PathType})
+ end.
+
+get_port(Config) ->
+ ?DEBUG("get port", []),
+ case proplists:get_value(port, Config, ?DEFAULT_PORT) of
+ Port when is_integer(Port) andalso (Port > 0) ->
+ Port;
+ BadPort ->
+ exit({bad_port, BadPort})
+ end.
+
+get_socket_type(Config) ->
+ ?DEBUG("get socket_type", []),
+ case proplists:get_value(socket_type, Config, ?DEFAULT_SOCKET_TYPE) of
+ SocketType when ((SocketType =:= ip_comm) orelse
+ (SocketType =:= ssl) orelse
+ (SocketType =:= essl) orelse
+ (SocketType =:= ossl)) ->
+ SocketType;
+ BadSocketType ->
+ exit({bad_socket_type, BadSocketType})
+ end.
+
+get_test_time(Config) ->
+ ?DEBUG("get test_time", []),
+ case proplists:get_value(test_time, Config, ?DEFAULT_TEST_TIME) of
+ Seconds when is_integer(Seconds) andalso (Seconds > 0) ->
+ timer:seconds(Seconds);
+ BadTestTime ->
+ exit({bad_test_time, BadTestTime})
+ end.
+
+get_max_nof_schedulers(Config) ->
+ ?DEBUG("get max_nof_schedulers", []),
+ case proplists:get_value(max_nof_schedulers,
+ Config,
+ ?DEFAULT_MAX_NOF_SCHEDULERS) of
+ MaxNofScheds when (is_integer(MaxNofScheds) andalso
+ (MaxNofScheds >= 0)) ->
+ MaxNofScheds;
+ BadMaxNofScheds ->
+ exit({bad_max_nof_schedulers, BadMaxNofScheds})
+ end.
+
+
+get_server_cert_file(Config) ->
+ ?DEBUG("get server cert file", []),
+ get_cert_file(server_cert_file, ?DEFAULT_SERVER_CERT, Config).
+
+get_client_cert_file(Config) ->
+ ?DEBUG("get client cert file", []),
+ get_cert_file(client_cert_file, ?DEFAULT_CLIENT_CERT, Config).
+
+get_cert_file(Tag, DefaultCertFileName, Config) ->
+ LibDir = code:lib_dir(inets),
+ HdltDir = filename:join(LibDir, "examples/httpd_load_test"),
+ DefaultCertFile = filename:join(HdltDir, DefaultCertFileName),
+ case proplists:get_value(Tag, Config, DefaultCertFile) of
+ F when is_list(F) ->
+ case file:read_file_info(F) of
+ {ok, #file_info{type = regular}} ->
+ F;
+ {ok, #file_info{type = Type}} ->
+ exit({wrong_file_type, Tag, F, Type});
+ {error, Reason} ->
+ exit({failed_readin_file_info, Tag, F, Reason})
+ end;
+ BadFile ->
+ exit({bad_cert_file, Tag, BadFile})
+ end.
+
+
+get_work_sim(Config) ->
+ ?DEBUG("get work_sim", []),
+ case proplists:get_value(work_simulator, Config, ?DEFAULT_WORK_SIM) of
+ WS when is_integer(WS) andalso (WS > 0) ->
+ WS;
+ BadWS ->
+ exit({bad_work_simulator, BadWS})
+ end.
+
+
+get_data_size(Config) ->
+ ?DEBUG("get data_size", []),
+ case proplists:get_value(data_size, Config, ?DEFAULT_DATA_SIZE) of
+ {From, To, Incr} = DS when (is_integer(From) andalso
+ is_integer(To) andalso
+ is_integer(Incr) andalso
+ (To > From) andalso
+ (From > 0) andalso
+ (Incr > 0)) ->
+ DS;
+ {From, To} when (is_integer(From) andalso
+ is_integer(To) andalso
+ (To > From) andalso
+ (From > 0)) ->
+ {From, To, ?DEFAULT_DATA_SIZE_INCR};
+ BadDS ->
+ exit({bad_data_size, BadDS})
+ end.
+
+
+url(#server{host = Host}, Port, SocketType, WorkSim) ->
+ Scheme =
+ case SocketType of
+ ip_comm ->
+ "http";
+ _ -> %% SSL
+ "https"
+ end,
+ lists:flatten(
+ io_lib:format("~s://~s:~w/cgi-bin/hdlt_random_html:page?~w:",
+ [Scheme, Host, Port, WorkSim])).
+
+
+missing_mandatory_config(Missing) ->
+ exit({missing_mandatory_config, Missing}).
+
+
+ensure_remote_dir_exist(Sftp, Path0) ->
+ case filename:split(Path0) of
+ [Root, Dir | Rest] ->
+ %% We never accept creating the root directory,
+ %% or the next level, so these *must* exist:
+ Path = filename:join(Root, Dir),
+ case ssh_sftp:read_file_info(Sftp, Path) of
+ {ok, #file_info{type = directory}} ->
+ ensure_remote_dir_exist(Sftp, Path, Rest);
+ {ok, #file_info{type = Type}} ->
+ ?INFO("Not a dir: ~p (~p)", [Path, Type]),
+ exit({not_a_dir, Path, Type});
+ {error, Reason} ->
+ ?INFO("Failed reading file info for ~p: ~p",
+ [Path, Reason]),
+ exit({failed_reading_file_info, Path, Reason})
+ end;
+ BadSplit ->
+ ?INFO("Bad remote dir path: ~p -> ~p", [Path0, BadSplit]),
+ exit({bad_dir, Path0})
+ end.
+
+ensure_remote_dir_exist(_Sftp, _Dir, []) ->
+ ok;
+ensure_remote_dir_exist(Sftp, Path, [Dir|Rest]) ->
+ NewPath = filename:join(Path, Dir),
+ case ssh_sftp:read_file_info(Sftp, NewPath) of
+ {ok, #file_info{type = directory}} ->
+ ensure_remote_dir_exist(Sftp, NewPath, Rest);
+ {ok, #file_info{type = Type}} ->
+ %% Exist, but is not a dir
+ ?INFO("Not a dir: ~p (~p)", [NewPath, Type]),
+ exit({not_a_dir, NewPath, Type});
+ {error, Reason} ->
+ %% This *could* be because the dir does not exist,
+ %% but it could also be some other error.
+ %% As usual, the error reason of the sftp is
+ %% a pease of crap, so we cannot use the
+ %% error reason.
+ %% The simplest way to test this is to simply
+ %% try to create the directory, since we should
+ %% ensure its existence anyway..
+ case ssh_sftp:make_dir(Sftp, NewPath) of
+ ok ->
+ ensure_remote_dir_exist(Sftp, NewPath, Rest);
+ _ ->
+ ?INFO("Failed reading file info for ~p: ~p",
+ [Dir, Reason]),
+ exit({failed_reading_file_info, NewPath, Reason})
+ end
+ end.
+
+maybe_create_remote_dir(Sftp, Dir) ->
+ case ssh_sftp:read_file_info(Sftp, Dir) of
+ {ok, #file_info{type = directory}} ->
+ ok;
+ {ok, #file_info{type = Type}} ->
+ %% Exist, but is not a dir
+ ?INFO("Not a dir: ~p (~p)", [Dir, Type]),
+ exit({not_a_dir, Dir, Type});
+ {error, Reason} ->
+ %% Assume dir noes not exist...
+ case ssh_sftp:make_dir(Sftp, Dir) of
+ ok ->
+ ok;
+ _ ->
+ ?INFO("Failed reading file info for ~p: ~p",
+ [Dir, Reason]),
+ exit({failed_reading_file_info, Dir, Reason})
+ end
+ end.
+
+
+set_debug_level(Debugs) ->
+ Debug = proplists:get_value(ctrl, Debugs, silence),
+ ?SET_LEVEL(Debug).
+
+
+%% Generates a list of numbers between A and B, such that
+%% there is exact one number between A and B and then
+%% randomizes that list.
+
+randomized_sizes_init() ->
+ {A, B, C} = os:timestamp(),
+ random:seed(A, B, C).
+
+randomized_sizes(From, To, Incr) ->
+ L = lists:seq(From, To, Incr),
+ Len = length(L),
+ randomized_sizes2(L, 0, Len-1).
+
+randomized_sizes2(L, N, Len) when N >= Len ->
+ L;
+randomized_sizes2(L, N, Len) ->
+ SplitWhere = random:uniform(Len),
+ {A, B} = lists:split(SplitWhere, L),
+ randomized_sizes2(B ++ A, N+1, Len).
diff --git a/lib/inets/examples/httpd_load_test/hdlt_logger.erl b/lib/inets/examples/httpd_load_test/hdlt_logger.erl
new file mode 100644
index 0000000000..b0c7eab2d1
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_logger.erl
@@ -0,0 +1,138 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: This is a simple logger utility for the HDLT toolkit.
+%% It assumesd that the debug level and the "name" of the
+%% logging entity has been put in process environment
+%% (using the set_level and set_name functions respectively).
+%%----------------------------------------------------------------------
+
+%%
+
+-module(hdlt_logger).
+
+-export([
+ start/0,
+ set_level/1, get_level/0, set_name/1,
+ info/2, log/2, debug/2
+ ]).
+
+-export([logger/1]).
+
+-define(LOGGER, ?MODULE).
+-define(MSG, hdlt_logger_msg).
+-define(LEVEL, hdlt_logger_level).
+-define(NAME, hdlt_logger_name).
+-define(INFO_STR, "INFO").
+-define(LOG_STR, "LOG ").
+-define(DEBUG_STR, "DBG ").
+
+
+start() ->
+ Self = self(),
+ proc_lib:start(?MODULE, logger, [Self]).
+
+set_name(Name) when is_list(Name) ->
+ put(?NAME, Name),
+ ok.
+
+get_level() ->
+ get(?LEVEL).
+
+set_level(Level) ->
+ case lists:member(Level, [silence, info, log, debug]) of
+ true ->
+ put(?LEVEL, Level),
+ ok;
+ false ->
+ erlang:error({bad_debug_level, Level})
+ end.
+
+
+info(F, A) ->
+%% io:format("info -> " ++ F ++ "~n", A),
+ do_log(info, get(?LEVEL), F, A).
+
+log(F, A) ->
+%% io:format("log -> " ++ F ++ "~n", A),
+ do_log(log, get(?LEVEL), F, A).
+
+debug(F, A) ->
+%% io:format("debug -> " ++ F ++ "~n", A),
+ do_log(debug, get(?LEVEL), F, A).
+
+
+logger(Parent) ->
+ global:register_name(?LOGGER, self()),
+ Ref = erlang:monitor(process, Parent),
+ proc_lib:init_ack(self()),
+ logger_loop(Ref).
+
+logger_loop(Ref) ->
+ receive
+ {?MSG, F, A} ->
+ io:format(F, A),
+ logger_loop(Ref);
+ {'DOWN', Ref, process, _Object, _Info} ->
+ %% start the stop timer
+ erlang:send_after(timer:seconds(5), self(), stop),
+ logger_loop(undefined);
+ stop ->
+ global:unregister_name(?LOGGER),
+ ok
+ end.
+
+
+formated_timestamp() ->
+ {Date, Time} = erlang:localtime(),
+ {YYYY,MM,DD} = Date,
+ {Hour,Min,Sec} = Time,
+ FormatDate =
+ io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
+ [YYYY,MM,DD,Hour,Min,Sec]),
+ lists:flatten(FormatDate).
+
+do_log(_, silence, _, _) ->
+ ok;
+do_log(info, info, F, A) ->
+ do_log(?INFO_STR, F, A);
+do_log(info, log, F, A) ->
+ do_log(?INFO_STR, F, A);
+do_log(log, log, F, A) ->
+ do_log(?LOG_STR, F, A);
+do_log(info, debug, F, A) ->
+ do_log(?INFO_STR, F, A);
+do_log(log, debug, F, A) ->
+ do_log(?LOG_STR, F, A);
+do_log(debug, debug, F, A) ->
+ do_log(?DEBUG_STR, F, A);
+do_log(_, _, _F, _A) ->
+ ok.
+
+do_log(SEV, F, A) ->
+ Name =
+ case get(?NAME) of
+ L when is_list(L) ->
+ L;
+ _ ->
+ "UNDEFINED"
+ end,
+ Msg = {?MSG, "~s ~s [~s] " ++ F ++ "~n",
+ [SEV, Name, formated_timestamp() | A]},
+ (catch global:send(?LOGGER, Msg)).
diff --git a/lib/inets/examples/httpd_load_test/hdlt_logger.hrl b/lib/inets/examples/httpd_load_test/hdlt_logger.hrl
new file mode 100644
index 0000000000..aa94babc48
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_logger.hrl
@@ -0,0 +1,33 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-ifndef(hdlt_logger_hrl).
+-define(hdlt_logger_hrl, true).
+
+%% Various log macros
+-define(SET_LEVEL(N), hdlt_logger:set_level(N)).
+-define(GET_LEVEL(), hdlt_logger:get_level()).
+-define(SET_NAME(N), hdlt_logger:set_name(N)).
+
+-define(INFO(F, A), hdlt_logger:info(F, A)).
+-define(LOG(F, A), hdlt_logger:log(F, A)).
+-define(DEBUG(F, A), hdlt_logger:debug(F, A)).
+
+-endif. % -ifdef(hdlt_logger_hrl).
diff --git a/lib/inets/examples/httpd_load_test/hdlt_random_html.erl b/lib/inets/examples/httpd_load_test/hdlt_random_html.erl
new file mode 100644
index 0000000000..e3a572c61f
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_random_html.erl
@@ -0,0 +1,59 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(hdlt_random_html).
+-export([page/3]).
+
+page(SessionID, _Env, Input) ->
+%% log("page(~p) -> deliver content-type when"
+%% "~n SessionID: ~p"
+%% "~n Env: ~p"
+%% "~n Input: ~p", [self(), SessionID, Env, Input]),
+ [WorkSimStr, SzSimStr] = string:tokens(Input, [$:]),
+ WorkSim = list_to_integer(WorkSimStr),
+ SzSim = list_to_integer(SzSimStr),
+ mod_esi:deliver(SessionID, "Content-Type:text/html\r\n\r\n"),
+ mod_esi:deliver(SessionID, start("Random test page")),
+ mod_esi:deliver(SessionID, content(WorkSim, SzSim)),
+ mod_esi:deliver(SessionID, stop()),
+ ok.
+
+start(Title) ->
+ "<HTML>
+<HEAD>
+<TITLE>" ++ Title ++ "</TITLE>
+ </HEAD>
+<BODY>\n".
+
+stop() ->
+ "</BODY>
+</HTML>
+".
+
+content(WorkSim, SzSim) ->
+ {A, B, C} = now(),
+ random:seed(A, B, C),
+ lists:sort([random:uniform(X) || X <- lists:seq(1, WorkSim)]),
+ lists:flatten(lists:duplicate(SzSim, "Dummy data ")).
+
+%% log(F, A) ->
+%% hdlt_logger:set_name("HDLT RANDOM-HTML"),
+%% hdlt_logger:set_level(debug),
+%% hdlt_logger:log(F, A).
diff --git a/lib/inets/examples/httpd_load_test/hdlt_server.erl b/lib/inets/examples/httpd_load_test/hdlt_server.erl
new file mode 100644
index 0000000000..3e5a849d5b
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_server.erl
@@ -0,0 +1,163 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: The HDLT server module.
+%% This is just a stub, making future expansion easy.
+%% All code in this module is executed in the local node!
+%%----------------------------------------------------------------------
+
+-module(hdlt_server).
+
+-export([start/1, stop/0, start_inets/0, start_service/1]).
+
+-export([proxy/1]).
+
+-include_lib("kernel/include/file.hrl").
+-include("hdlt_logger.hrl").
+
+
+-define(PROXY, hdlt_proxy).
+
+
+%% This function is used to start the proxy process
+%% This function is called *after* the nodes has been
+%% "connected" with the controller/collector node.
+
+start(Debug) ->
+ proc_lib:start(?MODULE, proxy, [Debug]).
+
+stop() ->
+ ?PROXY ! stop.
+
+start_inets() ->
+ ?PROXY ! start_inets.
+
+start_service(Config) ->
+ ?PROXY ! {server_start, Config, self()},
+ receive
+ {server_start_result, Result} ->
+ Result
+ after 15000 ->
+ {error, timeout}
+ end.
+
+
+proxy(Debug) ->
+ process_flag(trap_exit, true),
+ erlang:register(?PROXY, self()),
+ ?SET_NAME("HDLT PROXY"),
+ ?SET_LEVEL(Debug),
+ ?LOG("starting", []),
+ Ref = await_for_controller(10),
+ CtrlNode = node(Ref),
+ erlang:monitor_node(CtrlNode, true),
+ proc_lib:init_ack({ok, self()}),
+ ?DEBUG("started", []),
+ proxy_loop(Ref, CtrlNode).
+
+await_for_controller(N) when N > 0 ->
+ case global:whereis_name(hdlt_ctrl) of
+ Pid when is_pid(Pid) ->
+ erlang:monitor(process, Pid);
+ _ ->
+ timer:sleep(1000),
+ await_for_controller(N-1)
+ end;
+await_for_controller(_) ->
+ proc_lib:init_ack({error, controller_not_found, nodes()}),
+ timer:sleep(500),
+ halt().
+
+
+proxy_loop(Ref, CtrlNode) ->
+ ?DEBUG("await command", []),
+ receive
+ stop ->
+ ?LOG("received stop", []),
+ halt();
+
+ start_inets ->
+ ?LOG("start the inets service framework", []),
+ case (catch inets:start()) of
+ ok ->
+ ?LOG("framework started", []),
+ proxy_loop(Ref, CtrlNode);
+ Error ->
+ ?LOG("failed starting inets service framework: "
+ "~n Error: ~p", [Error]),
+ halt()
+ end;
+
+ {server_start, Config, From} ->
+ ?LOG("start-server", []),
+ maybe_start_crypto_and_ssl(Config),
+ %% inets:enable_trace(max, "/tmp/inets-httpd-trace.log", httpd),
+ %% inets:enable_trace(max, "/tmp/inets-httpd-trace.log", all),
+ case (catch inets:start(httpd, Config)) of
+ {ok, _} ->
+ ?LOG("server started when"
+ "~n which(inets): ~p"
+ "~n RootDir: ~p"
+ "~n System info: ~p", [code:which(inets),
+ code:root_dir(),
+ get_node_info()]),
+ From ! {server_start_result, ok},
+ proxy_loop(Ref, CtrlNode);
+ Error ->
+ ?INFO("server start failed"
+ "~n Error: ~p", [Error]),
+ From ! {server_start_result, Error},
+ halt()
+ end;
+
+ {nodedown, CtrlNode} ->
+ ?LOG("received nodedown for controller node - terminate", []),
+ halt();
+
+ {'DOWN', Ref, process, _, _} ->
+ ?LOG("received DOWN message for controller - terminate", []),
+ %% The controller has terminated, time to die
+ halt()
+
+ end.
+
+
+maybe_start_crypto_and_ssl(Config) ->
+ case lists:keysearch(socket_type, 1, Config) of
+ {value, {socket_type, SocketType}} when ((SocketType =:= ssl) orelse
+ (SocketType =:= ossl) orelse
+ (SocketType =:= essl)) ->
+ ?LOG("maybe start crypto and ssl", []),
+ (catch crypto:start()),
+ ssl:start();
+ _ ->
+ ok
+ end.
+
+
+get_node_info() ->
+ [{cpu_topology, erlang:system_info(cpu_topology)},
+ {heap_type, erlang:system_info(heap_type)},
+ {nof_schedulers, erlang:system_info(schedulers)},
+ {otp_release, erlang:system_info(otp_release)},
+ {version, erlang:system_info(version)},
+ {system_version, erlang:system_info(system_version)},
+ {system_architecture, erlang:system_info(system_architecture)}].
+
diff --git a/lib/inets/examples/httpd_load_test/hdlt_slave.erl b/lib/inets/examples/httpd_load_test/hdlt_slave.erl
new file mode 100644
index 0000000000..52af9b5b90
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_slave.erl
@@ -0,0 +1,291 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(hdlt_slave).
+
+
+-export([start_link/4, start_link/5, start_link/6, stop/1]).
+
+%% Internal exports
+-export([wait_for_slave/9, slave_start/1, wait_for_master_to_die/3]).
+
+-include("hdlt_logger.hrl").
+
+-define(SSH_PORT, 22).
+-define(TIMEOUT, 60000).
+-define(LOGGER, hdlt_logger).
+
+
+%% ***********************************************************************
+%% start_link/4,5 --
+%%
+%% The start/4,5 functions are used to start a slave Erlang node.
+%% The node on which the start/N functions are used is called the
+%% master in the description below.
+%%
+%% If hostname is the same for the master and the slave,
+%% the Erlang node will simply be spawned. The only requirment for
+%% this to work is that the 'erl' program can be found in PATH.
+%%
+%% If the master and slave are on different hosts, start/N uses
+%% the 'rsh' program to spawn an Erlang node on the other host.
+%% Alternative, if the master was started as
+%% 'erl -sname xxx -rsh my_rsh...', then 'my_rsh' will be used instead
+%% of 'rsh' (this is useful for systems where the rsh program is named
+%% 'remsh').
+%%
+%% For this to work, the following conditions must be fulfilled:
+%%
+%% 1. There must be an Rsh program on computer; if not an error
+%% is returned.
+%%
+%% 2. The hosts must be configured to allowed 'rsh' access without
+%% prompts for password.
+%%
+%% The slave node will have its filer and user server redirected
+%% to the master. When the master node dies, the slave node will
+%% terminate. For the start_link functions, the slave node will
+%% terminate also if the process which called start_link terminates.
+%%
+%% Returns: {ok, Name@Host} |
+%% {error, timeout} |
+%% {error, no_rsh} |
+%% {error, {already_running, Name@Host}}
+
+start_link(Host, Name, ErlPath, Paths) ->
+ start_link(Host, Name, ErlPath, Paths, [], silence).
+
+start_link(Host, Name, ErlPath, Paths, DebugLevel) when is_atom(DebugLevel) ->
+ start_link(Host, Name, ErlPath, Paths, [], DebugLevel);
+start_link(Host, Name, ErlPath, Paths, Args) when is_list(Args) ->
+ start_link(Host, Name, ErlPath, Paths, Args, silence).
+
+start_link(Host, Name, ErlPath, Paths, Args, DebugLevel) ->
+ Node = list_to_atom(lists:concat([Name, "@", Host])),
+ case net_adm:ping(Node) of
+ pang ->
+ start_it(Host, Name, Node, ErlPath, Paths, Args, DebugLevel);
+ pong ->
+ {error, {already_running, Node}}
+ end.
+
+%% Stops a running node.
+
+stop(Node) ->
+ rpc:call(Node, erlang, halt, []),
+ ok.
+
+
+%% Starts a new slave node.
+
+start_it(Host, Name, Node, ErlPath, Paths, Args, DebugLevel) ->
+ Prog = filename:join([ErlPath, "erl"]),
+ spawn(?MODULE, wait_for_slave, [self(), Host, Name, Node, Paths, Args, self(), Prog, DebugLevel]),
+ receive
+ {result, Result} -> Result
+ end.
+
+%% Waits for the slave to start.
+
+wait_for_slave(Parent, Host, Name, Node, Paths, Args,
+ LinkTo, Prog, DebugLevel) ->
+ ?SET_NAME("HDLT SLAVE STARTER"),
+ ?SET_LEVEL(DebugLevel),
+ ?DEBUG("begin", []),
+ Waiter = register_unique_name(0),
+ case mk_cmd(Host, Name, Paths, Args, Waiter, Prog) of
+ {ok, Cmd} ->
+ ?DEBUG("command generated: ~n~s", [Cmd]),
+ case (catch ssh_slave_start(Host, Cmd)) of
+ {ok, Conn, _Chan} ->
+ ?DEBUG("ssh channel created", []),
+ receive
+ {SlavePid, slave_started} ->
+ ?DEBUG("slave started: ~p", [SlavePid]),
+ unregister(Waiter),
+ slave_started(Parent, LinkTo, SlavePid, Conn,
+ DebugLevel)
+ after 32000 ->
+ ?INFO("slave node failed to report in on time",
+ []),
+ %% If it seems that the node was partially started,
+ %% try to kill it.
+ case net_adm:ping(Node) of
+ pong ->
+ spawn(Node, erlang, halt, []),
+ ok;
+ _ ->
+ ok
+ end,
+ Parent ! {result, {error, timeout}}
+ end;
+ {error, Reason} = Error ->
+ ?INFO("FAILED starting node: "
+ "~n ~p"
+ "~n ~p", [Reason, Cmd]),
+ Parent ! {result, Error}
+ end;
+ Other ->
+ ?INFO("FAILED creating node command string: "
+ "~n ~p", [Other]),
+ Parent ! {result, Other}
+ end.
+
+
+ssh_slave_start(Host, ErlCmd) ->
+ ?DEBUG("ssh_slave_start -> try connect to ~p", [Host]),
+ Connection =
+ case (catch ssh:connect(Host, ?SSH_PORT,
+ [{silently_accept_hosts, true}])) of
+ {ok, Conn} ->
+ ?DEBUG("ssh_exec_erl -> connected: ~p", [Conn]),
+ Conn;
+ Error1 ->
+ ?LOG("failed connecting to ~p: ~p", [Host, Error1]),
+ throw({error, {ssh_connect_failed, Error1}})
+ end,
+
+ ?DEBUG("ssh_exec_erl -> connected - now create channel", []),
+ Channel =
+ case (catch ssh_connection:session_channel(Connection, ?TIMEOUT)) of
+ {ok, Chan} ->
+ ?DEBUG("ssh_exec_erl -> channel ~p created", [Chan]),
+ Chan;
+ Error2 ->
+ ?LOG("failed creating channel: ~p", [Error2]),
+ throw({error, {ssh_channel_create_failed, Error2}})
+ end,
+
+ ?DEBUG("ssh_exec_erl -> channel created - now exec command: "
+ "~n ~p", [ErlCmd]),
+ case (catch ssh_connection:exec(Connection, Channel, ErlCmd, infinity)) of
+ success ->
+ ?DEBUG("ssh_exec_erl -> command exec'ed - clean ssh msg", []),
+ clean_ssh_msg(),
+ ?DEBUG("ssh_exec_erl -> done", []),
+ {ok, Connection, Channel};
+ Error3 ->
+ ?LOG("failed exec comand: ~p", [Error3]),
+ throw({error, {ssh_exec_failed, Error3}})
+ end.
+
+clean_ssh_msg() ->
+ receive
+ {ssh_cm, _X, _Y} ->
+ clean_ssh_msg()
+ after 1000 ->
+ ok
+ end.
+
+
+slave_started(ReplyTo, Master, Slave, Conn, Level)
+ when is_pid(Master) andalso is_pid(Slave) ->
+ process_flag(trap_exit, true),
+ SName = lists:flatten(
+ io_lib:format("HDLT SLAVE CTRL[~p,~p]",
+ [self(), node(Slave)])),
+ ?SET_NAME(SName),
+ ?SET_LEVEL(Level),
+ ?LOG("initiating", []),
+ MasterRef = erlang:monitor(process, Master),
+ SlaveRef = erlang:monitor(process, Slave),
+ ReplyTo ! {result, {ok, node(Slave)}},
+ slave_running(Master, MasterRef, Slave, SlaveRef, Conn).
+
+
+%% The slave node will be killed if the master process terminates,
+%% The master process will not be killed if the slave node terminates.
+
+slave_running(Master, MasterRef, Slave, SlaveRef, Conn) ->
+ ?DEBUG("await message", []),
+ receive
+ {'DOWN', MasterRef, process, _Object, _Info} ->
+ ?LOG("received DOWN from master", []),
+ erlang:demonitor(SlaveRef, [flush]),
+ Slave ! {nodedown, node()},
+ ssh:close(Conn);
+
+ {'DOWN', SlaveRef, process, Object, _Info} ->
+ ?LOG("received DOWN from slave (~p)", [Object]),
+ erlang:demonitor(MasterRef, [flush]),
+ ssh:close(Conn);
+
+ Other ->
+ ?DEBUG("received unknown: ~n~p", [Other]),
+ slave_running(Master, MasterRef, Slave, SlaveRef, Conn)
+
+ end.
+
+register_unique_name(Number) ->
+ Name = list_to_atom(lists:concat([?MODULE, "_waiter_", Number])),
+ case catch register(Name, self()) of
+ true ->
+ Name;
+ {'EXIT', {badarg, _}} ->
+ register_unique_name(Number+1)
+ end.
+
+
+%% Makes up the command to start the nodes.
+%% If the node should run on the local host, there is
+%% no need to use rsh.
+
+mk_cmd(Host, Name, Paths, Args, Waiter, Prog) ->
+ PaPaths = [[" -pa ", Path] || Path <- Paths],
+ {ok, lists:flatten(
+ lists:concat([Prog,
+ " -detached -nopinput ",
+ Args, " ",
+ " -sname ", Name, "@", Host,
+ " -s ", ?MODULE, " slave_start ", node(),
+ " ", Waiter,
+ " ", PaPaths]))}.
+
+
+%% This function will be invoked on the slave, using the -s option of erl.
+%% It will wait for the master node to terminate.
+
+slave_start([Master, Waiter]) ->
+ spawn(?MODULE, wait_for_master_to_die, [Master, Waiter, silence]);
+slave_start([Master, Waiter, Level]) ->
+ spawn(?MODULE, wait_for_master_to_die, [Master, Waiter, Level]).
+
+
+wait_for_master_to_die(Master, Waiter, Level) ->
+ process_flag(trap_exit, true),
+ SName = lists:flatten(
+ io_lib:format("HDLT-SLAVE MASTER MONITOR[~p,~p,~p]",
+ [self(), node(), Master])),
+ ?SET_NAME(SName),
+ ?SET_LEVEL(Level),
+ erlang:monitor_node(Master, true),
+ {Waiter, Master} ! {self(), slave_started},
+ wloop(Master).
+
+wloop(Master) ->
+ ?DEBUG("await message", []),
+ receive
+ {nodedown, Master} ->
+ ?INFO("received master nodedown", []),
+ halt();
+ _Other ->
+ wloop(Master)
+ end.
+
+
+
diff --git a/lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem b/lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem
new file mode 120000
index 0000000000..41644a1098
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_ssl_client_cert.pem
@@ -0,0 +1 @@
+../../test/httpc_SUITE_data/ssl_client_cert.pem \ No newline at end of file
diff --git a/lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem b/lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem
new file mode 120000
index 0000000000..41644a1098
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/hdlt_ssl_server_cert.pem
@@ -0,0 +1 @@
+../../test/httpc_SUITE_data/ssl_client_cert.pem \ No newline at end of file
diff --git a/lib/inets/examples/httpd_load_test/modules.mk b/lib/inets/examples/httpd_load_test/modules.mk
new file mode 100644
index 0000000000..9d0d7103d5
--- /dev/null
+++ b/lib/inets/examples/httpd_load_test/modules.mk
@@ -0,0 +1,44 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+SCRIPT_SKELETONS = \
+ hdlt.sh.skel
+
+CONF_SKELETONS = \
+ hdlt.config.skel
+
+CERT_FILES = \
+ hdlt_ssl_client_cert.pem \
+ hdlt_ssl_server_cert.pem
+
+README = HDLT_README
+
+MODULES = \
+ hdlt \
+ hdlt_ctrl \
+ hdlt_client \
+ hdlt_logger \
+ hdlt_random_html \
+ hdlt_server \
+ hdlt_slave
+
+INTERNAL_HRL_FILES = \
+ hdlt_logger.hrl
+
+
diff --git a/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile
new file mode 100644
index 0000000000..d7a3231068
--- /dev/null
+++ b/lib/inets/examples/server_root/Makefile
@@ -0,0 +1,209 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(INETS_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULE=
+
+AUTH_FILES = auth/group \
+ auth/passwd
+CGI_FILES = cgi-bin/printenv.sh
+CONF_FILES = conf/8080.conf \
+ conf/8888.conf \
+ conf/httpd.conf \
+ conf/ssl.conf \
+ conf/mime.types
+OPEN_FILES = htdocs/open/dummy.html
+MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html
+MISC_FILES = htdocs/misc/friedrich.html \
+ htdocs/misc/oech.html
+SECRET_FILES = htdocs/secret/dummy.html
+MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html
+HTDOCS_FILES = htdocs/index.html \
+ htdocs/config.shtml \
+ htdocs/echo.shtml \
+ htdocs/exec.shtml \
+ htdocs/flastmod.shtml \
+ htdocs/fsize.shtml \
+ htdocs/include.shtml
+ICON_FILES = icons/README \
+ icons/a.gif \
+ icons/alert.black.gif \
+ icons/alert.red.gif \
+ icons/apache_pb.gif \
+ icons/back.gif \
+ icons/ball.gray.gif \
+ icons/ball.red.gif \
+ icons/binary.gif \
+ icons/binhex.gif \
+ icons/blank.gif \
+ icons/bomb.gif \
+ icons/box1.gif \
+ icons/box2.gif \
+ icons/broken.gif \
+ icons/burst.gif \
+ icons/button1.gif \
+ icons/button10.gif \
+ icons/button2.gif \
+ icons/button3.gif \
+ icons/button4.gif \
+ icons/button5.gif \
+ icons/button6.gif \
+ icons/button7.gif \
+ icons/button8.gif \
+ icons/button9.gif \
+ icons/buttonl.gif \
+ icons/buttonr.gif \
+ icons/c.gif \
+ icons/comp.blue.gif \
+ icons/comp.gray.gif \
+ icons/compressed.gif \
+ icons/continued.gif \
+ icons/dir.gif \
+ icons/down.gif \
+ icons/dvi.gif \
+ icons/f.gif \
+ icons/folder.gif \
+ icons/folder.open.gif \
+ icons/folder.sec.gif \
+ icons/forward.gif \
+ icons/generic.gif \
+ icons/generic.red.gif \
+ icons/generic.sec.gif \
+ icons/hand.right.gif \
+ icons/hand.up.gif \
+ icons/htdig.gif \
+ icons/icon.sheet.gif \
+ icons/image1.gif \
+ icons/image2.gif \
+ icons/image3.gif \
+ icons/index.gif \
+ icons/layout.gif \
+ icons/left.gif \
+ icons/link.gif \
+ icons/movie.gif \
+ icons/p.gif \
+ icons/patch.gif \
+ icons/pdf.gif \
+ icons/pie0.gif \
+ icons/pie1.gif \
+ icons/pie2.gif \
+ icons/pie3.gif \
+ icons/pie4.gif \
+ icons/pie5.gif \
+ icons/pie6.gif \
+ icons/pie7.gif \
+ icons/pie8.gif \
+ icons/portal.gif \
+ icons/poweredby.gif \
+ icons/ps.gif \
+ icons/quill.gif \
+ icons/right.gif \
+ icons/screw1.gif \
+ icons/screw2.gif \
+ icons/script.gif \
+ icons/sound1.gif \
+ icons/sound2.gif \
+ icons/sphere1.gif \
+ icons/sphere2.gif \
+ icons/star.gif \
+ icons/star_blank.gif \
+ icons/tar.gif \
+ icons/tex.gif \
+ icons/text.gif \
+ icons/transfer.gif \
+ icons/unknown.gif \
+ icons/up.gif \
+ icons/uu.gif \
+ icons/uuencoded.gif \
+ icons/world1.gif \
+ icons/world2.gif
+
+SSL_FILES = ssl/ssl_client.pem \
+ ssl/ssl_server.pem
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt:
+
+clean:
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth
+ $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin
+ $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf
+ $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open
+ $(INSTALL_DATA) $(OPEN_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/open
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
+ $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc
+ $(INSTALL_DATA) $(MISC_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/misc
+ $(INSTALL_DIR) \
+ $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret
+ $(INSTALL_DIR) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret
+ $(INSTALL_DATA) $(SECRET_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/secret
+ $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs
+ $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons
+ $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl
+ $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs
+
+release_docs_spec:
+
diff --git a/lib/inets/examples/subdirs.mk b/lib/inets/examples/subdirs.mk
new file mode 100644
index 0000000000..10a331fc26
--- /dev/null
+++ b/lib/inets/examples/subdirs.mk
@@ -0,0 +1,3 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+SUB_DIRECTORIES = server_root httpd_load_test \ No newline at end of file
diff --git a/lib/inets/include/httpd.hrl b/lib/inets/include/httpd.hrl
new file mode 100644
index 0000000000..a7e63ca670
--- /dev/null
+++ b/lib/inets/include/httpd.hrl
@@ -0,0 +1,41 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-ifndef(httpd_hrl).
+-define(httpd_hrl, true).
+
+-include_lib("kernel/include/file.hrl").
+
+-record(init_data,{peername,resolve}).
+
+-record(mod,{init_data,
+ data=[],
+ socket_type=ip_comm,
+ socket,
+ config_db,
+ method,
+ absolute_uri=[],
+ request_uri,
+ http_version,
+ request_line,
+ parsed_header=[],
+ entity_body,
+ connection}).
+-endif. % -ifdef(httpd_hrl).
diff --git a/lib/inets/include/mod_auth.hrl b/lib/inets/include/mod_auth.hrl
new file mode 100644
index 0000000000..cf931e681a
--- /dev/null
+++ b/lib/inets/include/mod_auth.hrl
@@ -0,0 +1,33 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-ifndef(mod_auth_hrl).
+-define(mod_auth_hrl, true).
+
+-record(httpd_user,
+ {username,
+ password,
+ user_data}).
+
+-record(httpd_group,
+ {name,
+ userlist}).
+
+-endif. % -ifdef(mod_auth_hrl).
diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile
index 0c15277a18..19b93870df 100644
--- a/lib/inets/src/ftp/Makefile
+++ b/lib/inets/src/ftp/Makefile
@@ -22,6 +22,7 @@ include $(ERL_TOP)/make/target.mk
EBIN = ../../ebin
include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
# ----------------------------------------------------
# Application version
# ----------------------------------------------------
@@ -29,6 +30,7 @@ include ../../vsn.mk
VSN = $(INETS_VSN)
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -52,24 +54,21 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
# ----------------------------------------------------
-# INETS FLAGS
+# FLAGS
# ----------------------------------------------------
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
+
+include ../inets_app/inets.mk
ifeq ($(FTP_DEBUG),true)
INETS_FLAGS += -Dftp_debug
endif
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS) \
+ -I../../include \
+ -I../inets_app
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-INETS_ERL_FLAGS += -I ../inets_app -pa ../../ebin
-
-ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \
- $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
# ----------------------------------------------------
# Targets
@@ -89,9 +88,10 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/ftp
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/ftp
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl
index 534fcae675..5ad74851c8 100644
--- a/lib/inets/src/ftp/ftp.erl
+++ b/lib/inets/src/ftp/ftp.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -25,14 +25,12 @@
-behaviour(gen_server).
-behaviour(inets_service).
--deprecated({open, 3, next_major_release}).
--deprecated({force_active, 1, next_major_release}).
%% API - Client interface
-export([cd/2, close/1, delete/2, formaterror/1,
lcd/2, lpwd/1, ls/1, ls/2,
mkdir/2, nlist/1, nlist/2,
- open/1, open/2, open/3, force_active/1,
+ open/1, open/2,
pwd/1, quote/2,
recv/2, recv/3, recv_bin/2,
recv_chunk_start/2, recv_chunk/1,
@@ -133,11 +131,6 @@ open(Host, Port) when is_integer(Port) ->
open(Host, [{port, Port}]);
%% </BACKWARD-COMPATIBILLITY>
-%% <BACKWARD-COMPATIBILLITY>
-open(Host, [H|_] = Flags) when is_atom(H) ->
- open(Host, ?FTP_PORT, Flags);
-%% </BACKWARD-COMPATIBILLITY>
-
open(Host, Opts) when is_list(Opts) ->
?fcrt("open", [{host, Host}, {opts, Opts}]),
try
@@ -160,32 +153,6 @@ open(Host, Opts) when is_list(Opts) ->
end.
-%% <BACKWARD-COMPATIBILLITY>
-open(Host, Port, Flags) when is_integer(Port) andalso is_list(Flags) ->
- ?fcrt("open", [{host, Host}, {port, Port}, {flags, Flags}]),
- try
- {ok, StartOptions} = start_options([{flags, Flags}]),
- ?fcrt("open", [{start_options, StartOptions}]),
- {ok, OpenOptions} = open_options([{host, Host}, {port, Port}|Flags]),
- ?fcrt("open", [{open_options, OpenOptions}]),
- case ftp_sup:start_child([[{client, self()} | StartOptions], []]) of
- {ok, Pid} ->
- ?fcrt("open - ok", [{pid, Pid}]),
- call(Pid, {open, ip_comm, OpenOptions}, plain);
- Error1 ->
- ?fcrt("open - error", [{error1, Error1}]),
- Error1
- end
- catch
- throw:Error2 ->
- Error2
- end.
-%% </BACKWARD-COMPATIBILLITY>
-
-
-
-
-
%%--------------------------------------------------------------------------
%% user(Pid, User, Pass, <Acc>) -> ok | {error, euser} | {error, econn}
%% | {error, eacct}
@@ -528,16 +495,6 @@ close(Pid) ->
cast(Pid, close),
ok.
-%%--------------------------------------------------------------------------
-%% force_active(Pid) -> ok
-%% Pid = pid()
-%%
-%% Description: Force connection to use active mode.
-%%--------------------------------------------------------------------------
-force_active(Pid) ->
- error_logger:info_report("This function is deprecated use the mode flag "
- "instead"),
- call(Pid, force_active, atom).
%%--------------------------------------------------------------------------
%% formaterror(Tag) -> string()
@@ -886,9 +843,6 @@ handle_call({_, {open, ip_comm, Host, Opts}}, From, State) ->
{stop, normal, State2#state{client = undefined}}
end;
-handle_call({_, force_active}, _, State) ->
- {reply, ok, State#state{mode = active}};
-
handle_call({_, {user, User, Password}}, From,
#state{csock = CSock} = State) when (CSock =/= undefined) ->
handle_user(User, Password, "", State#state{client = From});
diff --git a/lib/inets/src/ftp/ftp_internal.hrl b/lib/inets/src/ftp/ftp_internal.hrl
index c3fa1e611d..148f8217ba 100644
--- a/lib/inets/src/ftp/ftp_internal.hrl
+++ b/lib/inets/src/ftp/ftp_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,7 +21,8 @@
-ifndef(ftp_internal_hrl).
-define(ftp_internal_hrl, true).
--include("inets_internal.hrl").
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
-define(SERVICE, ftpc).
-define(fcri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
-define(fcrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile
index 628c91421f..0397b48ab2 100644
--- a/lib/inets/src/http_client/Makefile
+++ b/lib/inets/src/http_client/Makefile
@@ -51,7 +51,6 @@ MODULES = \
httpc_profile_sup \
httpc_response \
httpc_request \
- http_uri \
HRL_FILES = httpc_internal.hrl
@@ -61,20 +60,17 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
# ----------------------------------------------------
-# INETS FLAGS
-# ----------------------------------------------------
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
-
-
-# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-INETS_ERL_FLAGS += -I ../http_lib -I ../inets_app -pa ../../ebin
-ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \
- $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+include ../inets_app/inets.mk
+
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS) \
+ -I../../include \
+ -I../inets_app \
+ -I../http_lib
# ----------------------------------------------------
@@ -94,9 +90,10 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/http_client
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_client
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
diff --git a/lib/inets/src/http_client/http.erl b/lib/inets/src/http_client/http.erl
index 7e1e90b50e..bbe2fec267 100644
--- a/lib/inets/src/http_client/http.erl
+++ b/lib/inets/src/http_client/http.erl
@@ -18,21 +18,38 @@
%%
%%
-%% Description:
-%%% This version of the HTTP/1.1 client supports:
-%%% - RFC 2616 HTTP 1.1 client part
-%%% - RFC 2818 HTTP Over TLS
+%%% Description: OLD API MODULE - USE httpc INSTEAD
-module(http).
-%% API
--export([request/1, request/2, request/4, request/5,
+-deprecated({request, 1, next_major_release}).
+-deprecated({request, 2, next_major_release}).
+-deprecated({request, 4, next_major_release}).
+-deprecated({request, 5, next_major_release}).
+-deprecated({cancel_request, 1, next_major_release}).
+-deprecated({cancel_request, 2, next_major_release}).
+-deprecated({set_option, 2, next_major_release}).
+-deprecated({set_option, 3, next_major_release}).
+-deprecated({set_options, 1, next_major_release}).
+-deprecated({set_options, 2, next_major_release}).
+-deprecated({verify_cookies, 2, next_major_release}).
+-deprecated({verify_cookies, 3, next_major_release}).
+-deprecated({cookie_header, 1, next_major_release}).
+-deprecated({cookie_header, 2, next_major_release}).
+-deprecated({stream_next, 1, next_major_release}).
+-deprecated({default_profile, 0, next_major_release}).
+
+%% Deprecated
+-export([
+ request/1, request/2, request/4, request/5,
cancel_request/1, cancel_request/2,
set_option/2, set_option/3,
set_options/1, set_options/2,
- verify_cookies/2, verify_cookies/3, cookie_header/1,
- cookie_header/2, stream_next/1,
- default_profile/0]).
+ verify_cookies/2, verify_cookies/3,
+ cookie_header/1, cookie_header/2,
+ stream_next/1,
+ default_profile/0
+ ]).
%%%=========================================================================
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 6deeab6948..04fae13b20 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -48,7 +48,7 @@
stop_service/1,
services/0, service_info/1]).
--include("http_internal.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
-define(DEFAULT_PROFILE, default).
@@ -104,8 +104,14 @@ request(Url, Profile) ->
%% HTTPOptions - [HttpOption]
%% HTTPOption - {timeout, Time} | {connect_timeout, Time} |
%% {ssl, SSLOptions} | {proxy_auth, {User, Password}}
-%% Ssloptions = [SSLOption]
-%% SSLOption = {verify, code()} | {depth, depth()} | {certfile, path()} |
+%% Ssloptions = ssl_options() |
+%% {ssl, ssl_options()} |
+%% {ossl, ssl_options()} |
+%% {essl, ssl_options()}
+%% ssl_options() = [ssl_option()]
+%% ssl_option() = {verify, code()} |
+%% {depth, depth()} |
+%% {certfile, path()} |
%% {keyfile, path()} | {password, string()} | {cacertfile, path()} |
%% {ciphers, string()}
%% Options - [Option]
@@ -246,7 +252,7 @@ set_option(Key, Value, Profile) ->
%% Description: Store the cookies from <SetCookieHeaders>
%% in the cookie database
%% for the profile <Profile>. This function shall be used when the option
-%% cookie is set to verify.
+%% cookies is set to verify.
%%-------------------------------------------------------------------------
store_cookies(SetCookieHeaders, Url) ->
store_cookies(SetCookieHeaders, Url, default_profile()).
@@ -436,18 +442,23 @@ handle_request(Method, Url,
HeadersRecord = header_record(NewHeaders, Host2, HTTPOptions),
Receiver = proplists:get_value(receiver, Options),
SocketOpts = proplists:get_value(socket_opts, Options),
+ UrlEncodeBool = HTTPOptions#http_options.url_encode,
+ MaybeEscPath = url_encode(Path, UrlEncodeBool),
+ MaybeEscQuery = url_encode(Query, UrlEncodeBool),
+ AbsUri = url_encode(Url, UrlEncodeBool),
+
Request = #request{from = Receiver,
scheme = Scheme,
address = {Host, Port},
- path = Path,
- pquery = Query,
+ path = MaybeEscPath,
+ pquery = MaybeEscQuery,
method = Method,
headers = HeadersRecord,
content = {ContentType, Body},
settings = HTTPOptions,
- abs_uri = Url,
+ abs_uri = AbsUri,
userinfo = UserInfo,
- stream = Stream,
+ stream = Stream,
headers_as_is = headers_as_is(Headers, Options),
socket_opts = SocketOpts,
started = Started},
@@ -465,6 +476,10 @@ handle_request(Method, Url,
Error
end.
+url_encode(URI, true) ->
+ http_uri:encode(URI);
+url_encode(URI, false) ->
+ URI.
handle_answer(RequestId, false, _) ->
{ok, RequestId};
@@ -572,14 +587,16 @@ http_options_default() ->
(_) ->
error
end,
- AutoRedirectPost = fun(Value) when (Value =:= true) orelse
- (Value =:= false) ->
- {ok, Value};
- (_) ->
- error
- end,
+ AutoRedirectPost = boolfun(),
+
SslPost = fun(Value) when is_list(Value) ->
- {ok, Value};
+ {ok, {?HTTP_DEFAULT_SSL_KIND, Value}};
+ ({ssl, SslOptions}) when is_list(SslOptions) ->
+ {ok, {?HTTP_DEFAULT_SSL_KIND, SslOptions}};
+ ({ossl, SslOptions}) when is_list(SslOptions) ->
+ {ok, {ossl, SslOptions}};
+ ({essl, SslOptions}) when is_list(SslOptions) ->
+ {ok, {essl, SslOptions}};
(_) ->
error
end,
@@ -589,12 +606,8 @@ http_options_default() ->
(_) ->
error
end,
- RelaxedPost = fun(Value) when (Value =:= true) orelse
- (Value =:= false) ->
- {ok, Value};
- (_) ->
- error
- end,
+ RelaxedPost = boolfun(),
+
ConnTimeoutPost =
fun(Value) when is_integer(Value) andalso (Value >= 0) ->
{ok, Value};
@@ -603,25 +616,30 @@ http_options_default() ->
(_) ->
error
end,
+
+ UrlDecodePost = boolfun(),
[
- {version, {value, "HTTP/1.1"}, #http_options.version, VersionPost},
- {timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost},
- {autoredirect, {value, true}, #http_options.autoredirect, AutoRedirectPost},
- {ssl, {value, []}, #http_options.ssl, SslPost},
- {proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost},
- {relaxed, {value, false}, #http_options.relaxed, RelaxedPost},
- %% this field has to be *after* the timeout field (as that field is used for the default value)
- {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}
+ {version, {value, "HTTP/1.1"}, #http_options.version, VersionPost},
+ {timeout, {value, ?HTTP_REQUEST_TIMEOUT}, #http_options.timeout, TimeoutPost},
+ {autoredirect, {value, true}, #http_options.autoredirect, AutoRedirectPost},
+ {ssl, {value, {?HTTP_DEFAULT_SSL_KIND, []}}, #http_options.ssl, SslPost},
+ {proxy_auth, {value, undefined}, #http_options.proxy_auth, ProxyAuthPost},
+ {relaxed, {value, false}, #http_options.relaxed, RelaxedPost},
+ {url_encode, {value, false}, #http_options.url_encode, UrlDecodePost},
+ %% this field has to be *after* the timeout option (as that field is used for the default value)
+ {connect_timeout, {field, #http_options.timeout}, #http_options.connect_timeout, ConnTimeoutPost}
].
+boolfun() ->
+ fun(Value) when (Value =:= true) orelse
+ (Value =:= false) ->
+ {ok, Value};
+ (_) ->
+ error
+ end.
request_options_defaults() ->
- VerifyBoolean =
- fun(Value) when ((Value =:= true) orelse (Value =:= false)) ->
- ok;
- (_) ->
- error
- end,
+ VerifyBoolean = boolfun(),
VerifySync = VerifyBoolean,
diff --git a/lib/inets/src/http_client/httpc_cookie.erl b/lib/inets/src/http_client/httpc_cookie.erl
index 586701b4a1..4d61f82b5a 100644
--- a/lib/inets/src/http_client/httpc_cookie.erl
+++ b/lib/inets/src/http_client/httpc_cookie.erl
@@ -476,13 +476,13 @@ path_sort(Cookies)->
lists:reverse(lists:keysort(#http_cookie.path, Cookies)).
-%% Informally, the Set-Cookie response header comprises the token
-%% Set-Cookie:, followed by a comma-separated list of one or more
-%% cookies. Netscape cookies expires attribute may also have a
-%% , in this case the header list will have been incorrectly split
-%% in parse_set_cookies/2 this functions fixs that problem.
+%% Informally, the Set-Cookie response header comprises the token
+%% Set-Cookie:, followed by a comma-separated list of one or more
+%% cookies. Netscape cookies expires attribute may also have a,
+%% in this case the header list will have been incorrectly split
+%% in parse_set_cookies/2 this functions fix that problem.
fix_netscape_cookie([Cookie1, Cookie2 | Rest], Acc) ->
- case inets_regexp:match(Cookie1, "expires=") of
+ case inets_regexp:match(string:to_lower(Cookie1), "expires=") of
{_, _, _} ->
fix_netscape_cookie(Rest, [Cookie1 ++ Cookie2 | Acc]);
nomatch ->
diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl
index 5e79d874fb..cb6f3e2841 100644
--- a/lib/inets/src/http_client/httpc_handler.erl
+++ b/lib/inets/src/http_client/httpc_handler.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
+%%
%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
-%%
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -22,8 +22,8 @@
-behaviour(gen_server).
+-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
--include("http_internal.hrl").
%%--------------------------------------------------------------------
@@ -177,8 +177,8 @@ stream(BodyPart, Request = #request{stream = Self}, Code)
stream(BodyPart, Request = #request{stream = Self}, 404)
when (Self =:= self) orelse (Self =:= {self, once}) ->
?hcrt("stream - self with 404", [{stream, Self}]),
- httpc_response:send(Request#request.from,
- {Request#request.id, stream, BodyPart}),
+ httpc_response:send(Request#request.from,
+ {Request#request.id, stream, BodyPart}),
{<<>>, Request};
%% Stream to file
@@ -286,8 +286,7 @@ handle_call({connect_and_send, #request{address = Address0,
handle_call(#request{address = Addr} = Request, _,
#state{status = Status,
- session = #tcp_session{socket = Socket,
- type = pipeline} = Session,
+ session = #session{type = pipeline} = Session,
timers = Timers,
options = #options{proxy = Proxy} = _Options,
profile_name = ProfileName} = State)
@@ -301,7 +300,7 @@ handle_call(#request{address = Addr} = Request, _,
Address = handle_proxy(Addr, Proxy),
- case httpc_request:send(Address, Request, Socket) of
+ case httpc_request:send(Address, Session, Request) of
ok ->
?hcrd("request sent", []),
@@ -320,10 +319,10 @@ handle_call(#request{address = Addr} = Request, _,
NewTimers = NewState#state.timers,
NewPipeline = queue:in(Request, State#state.pipeline),
NewSession =
- Session#tcp_session{queue_length =
- %% Queue + current
- queue:len(NewPipeline) + 1,
- client_close = ClientClose},
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(NewPipeline) + 1,
+ client_close = ClientClose},
httpc_manager:insert_session(NewSession, ProfileName),
?hcrd("session updated", []),
{reply, ok, State#state{pipeline = NewPipeline,
@@ -336,8 +335,8 @@ handle_call(#request{address = Addr} = Request, _,
cancel_timer(Timers#timers.queue_timer,
timeout_queue),
NewSession =
- Session#tcp_session{queue_length = 1,
- client_close = ClientClose},
+ Session#session{queue_length = 1,
+ client_close = ClientClose},
httpc_manager:insert_session(NewSession, ProfileName),
Relaxed =
(Request#request.settings)#http_options.relaxed,
@@ -357,8 +356,7 @@ handle_call(#request{address = Addr} = Request, _,
handle_call(#request{address = Addr} = Request, _,
#state{status = Status,
- session = #tcp_session{socket = Socket,
- type = keep_alive} = Session,
+ session = #session{type = keep_alive} = Session,
timers = Timers,
options = #options{proxy = Proxy} = _Options,
profile_name = ProfileName} = State)
@@ -370,7 +368,7 @@ handle_call(#request{address = Addr} = Request, _,
{status, Status}]),
Address = handle_proxy(Addr, Proxy),
- case httpc_request:send(Address, Request, Socket) of
+ case httpc_request:send(Address, Session, Request) of
ok ->
?hcrd("request sent", []),
@@ -389,10 +387,10 @@ handle_call(#request{address = Addr} = Request, _,
NewTimers = NewState#state.timers,
NewKeepAlive = queue:in(Request, State#state.keep_alive),
NewSession =
- Session#tcp_session{queue_length =
- %% Queue + current
- queue:len(NewKeepAlive) + 1,
- client_close = ClientClose},
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(NewKeepAlive) + 1,
+ client_close = ClientClose},
httpc_manager:insert_session(NewSession, ProfileName),
?hcrd("session updated", []),
{reply, ok, State#state{keep_alive = NewKeepAlive,
@@ -405,8 +403,8 @@ handle_call(#request{address = Addr} = Request, _,
cancel_timer(Timers#timers.queue_timer,
timeout_queue),
NewSession =
- Session#tcp_session{queue_length = 1,
- client_close = ClientClose},
+ Session#session{queue_length = 1,
+ client_close = ClientClose},
httpc_manager:insert_session(NewSession, ProfileName),
Relaxed =
(Request#request.settings)#http_options.relaxed,
@@ -589,13 +587,13 @@ handle_info({ssl_closed, _}, State = #state{request = undefined}) ->
%%% Error cases
handle_info({tcp_closed, _}, #state{session = Session0} = State) ->
- Socket = Session0#tcp_session.socket,
- Session = Session0#tcp_session{socket = {remote_close, Socket}},
+ Socket = Session0#session.socket,
+ Session = Session0#session{socket = {remote_close, Socket}},
%% {stop, session_remotly_closed, State};
{stop, normal, State#state{session = Session}};
handle_info({ssl_closed, _}, #state{session = Session0} = State) ->
- Socket = Session0#tcp_session.socket,
- Session = Session0#tcp_session{socket = {remote_close, Socket}},
+ Socket = Session0#session.socket,
+ Session = Session0#session{socket = {remote_close, Socket}},
%% {stop, session_remotly_closed, State};
{stop, normal, State#state{session = Session}};
handle_info({tcp_error, _, _} = Reason, State) ->
@@ -607,24 +605,29 @@ handle_info({ssl_error, _, _} = Reason, State) ->
%% Internally, to a request handling process, a request timeout is
%% seen as a canceled request.
handle_info({timeout, RequestId},
- #state{request = #request{id = RequestId} = Request,
- canceled = Canceled} = State) ->
+ #state{request = #request{id = RequestId} = Request,
+ canceled = Canceled,
+ profile_name = ProfileName} = State) ->
?hcri("timeout of current request", [{id, RequestId}]),
httpc_response:send(Request#request.from,
httpc_response:error(Request, timeout)),
+ httpc_manager:request_done(RequestId, ProfileName),
?hcrv("response (timeout) sent - now terminate", []),
{stop, normal,
State#state{request = Request#request{from = answer_sent},
canceled = [RequestId | Canceled]}};
-handle_info({timeout, RequestId}, #state{canceled = Canceled} = State) ->
+handle_info({timeout, RequestId},
+ #state{canceled = Canceled,
+ profile_name = ProfileName} = State) ->
?hcri("timeout", [{id, RequestId}]),
Filter =
fun(#request{id = Id, from = From} = Request) when Id =:= RequestId ->
?hcrv("found request", [{id, Id}, {from, From}]),
%% Notify the owner
- Response = httpc_response:error(Request, timeout),
- httpc_response:send(From, Response),
+ httpc_response:send(From,
+ httpc_response:error(Request, timeout)),
+ httpc_manager:request_done(RequestId, ProfileName),
?hcrv("response (timeout) sent", []),
[Request#request{from = answer_sent}];
(_) ->
@@ -699,52 +702,56 @@ terminate(normal, #state{session = undefined}) ->
%% Init error sending, no session information has been setup but
%% there is a socket that needs closing.
terminate(normal,
- #state{request = Request,
- session = #tcp_session{id = undefined,
- socket = Socket}}) ->
- http_transport:close(socket_type(Request), Socket);
+ #state{session = #session{id = undefined} = Session}) ->
+ close_socket(Session);
%% Socket closed remotely
terminate(normal,
- #state{session = #tcp_session{socket = {remote_close, Socket},
- id = Id},
+ #state{session = #session{socket = {remote_close, Socket},
+ socket_type = SocketType,
+ id = Id},
profile_name = ProfileName,
- request = Request,
- timers = Timers,
- pipeline = Pipeline}) ->
+ request = Request,
+ timers = Timers,
+ pipeline = Pipeline,
+ keep_alive = KeepAlive} = State) ->
?hcrt("terminate(normal) - remote close",
[{id, Id}, {profile, ProfileName}]),
%% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
+ maybe_retry_queue(Pipeline, State),
+ maybe_retry_queue(KeepAlive, State),
+
%% Cancel timers
- #timers{request_timers = ReqTmrs, queue_timer = QTmr} = Timers,
- cancel_timer(QTmr, timeout_queue),
- lists:foreach(fun({_, Timer}) -> cancel_timer(Timer, timeout) end,
- ReqTmrs),
+ cancel_timers(Timers),
%% Maybe deliver answers to requests
- deliver_answers([Request | queue:to_list(Pipeline)]),
+ deliver_answer(Request),
%% And, just in case, close our side (**really** overkill)
- http_transport:close(socket_type(Request), Socket);
+ http_transport:close(SocketType, Socket);
-terminate(_, #state{session = #tcp_session{id = Id,
- socket = Socket,
- scheme = Scheme},
+terminate(Reason, #state{session = #session{id = Id,
+ socket = Socket,
+ socket_type = SocketType},
request = undefined,
profile_name = ProfileName,
timers = Timers,
pipeline = Pipeline,
keep_alive = KeepAlive} = State) ->
+ ?hcrt("terminate",
+ [{id, Id}, {profile, ProfileName}, {reason, Reason}]),
+
+ %% Clobber session
(catch httpc_manager:delete_session(Id, ProfileName)),
maybe_retry_queue(Pipeline, State),
maybe_retry_queue(KeepAlive, State),
cancel_timer(Timers#timers.queue_timer, timeout_queue),
- http_transport:close(socket_type(Scheme), Socket);
+ http_transport:close(SocketType, Socket);
terminate(Reason, #state{request = undefined}) ->
?hcrt("terminate", [{reason, Reason}]),
@@ -770,59 +777,55 @@ maybe_send_answer(#request{from = answer_sent}, _Reason, State) ->
maybe_send_answer(Request, Answer, State) ->
answer_request(Request, Answer, State).
-deliver_answers([]) ->
- ?hcrd("deliver answer done", []),
- ok;
-deliver_answers([#request{id = Id, from = From} = Request | Requests])
+deliver_answer(#request{id = Id, from = From} = Request)
when is_pid(From) ->
Response = httpc_response:error(Request, socket_closed_remotely),
?hcrd("deliver answer", [{id, Id}, {from, From}, {response, Response}]),
- httpc_response:send(From, Response),
- deliver_answers(Requests);
-deliver_answers([Request|Requests]) ->
+ httpc_response:send(From, Response);
+deliver_answer(Request) ->
?hcrd("skip deliver answer", [{request, Request}]),
- deliver_answers(Requests).
+ ok.
%%--------------------------------------------------------------------
%% Func: code_change(_OldVsn, State, Extra) -> {ok, NewState}
%% Purpose: Convert process state when code is changed
%%--------------------------------------------------------------------
-code_change(_, #state{request = Request, pipeline = Queue} = State,
- [{from, '5.0.1'}, {to, '5.0.2'}]) ->
- Settings = new_http_options(Request#request.settings),
- NewRequest = Request#request{settings = Settings},
- NewQueue = new_queue(Queue, fun new_http_options/1),
- {ok, State#state{request = NewRequest, pipeline = NewQueue}};
-
-code_change(_, #state{request = Request, pipeline = Queue} = State,
- [{from, '5.0.2'}, {to, '5.0.1'}]) ->
- Settings = old_http_options(Request#request.settings),
- NewRequest = Request#request{settings = Settings},
- NewQueue = new_queue(Queue, fun old_http_options/1),
- {ok, State#state{request = NewRequest, pipeline = NewQueue}};
+%% code_change(_, #state{request = Request, pipeline = Queue} = State,
+%% [{from, '5.0.1'}, {to, '5.0.2'}]) ->
+%% Settings = new_http_options(Request#request.settings),
+%% NewRequest = Request#request{settings = Settings},
+%% NewQueue = new_queue(Queue, fun new_http_options/1),
+%% {ok, State#state{request = NewRequest, pipeline = NewQueue}};
+
+%% code_change(_, #state{request = Request, pipeline = Queue} = State,
+%% [{from, '5.0.2'}, {to, '5.0.1'}]) ->
+%% Settings = old_http_options(Request#request.settings),
+%% NewRequest = Request#request{settings = Settings},
+%% NewQueue = new_queue(Queue, fun old_http_options/1),
+%% {ok, State#state{request = NewRequest, pipeline = NewQueue}};
code_change(_, State, _) ->
{ok, State}.
-new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
- Auth, Relaxed}) ->
- {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
- Auth, Relaxed}.
-
-old_http_options({http_options, _, TimeOut, AutoRedirect,
- SslOpts, Auth, Relaxed}) ->
- {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}.
-
-new_queue(Queue, Fun) ->
- List = queue:to_list(Queue),
- NewList =
- lists:map(fun(Request) ->
- Settings =
- Fun(Request#request.settings),
- Request#request{settings = Settings}
- end, List),
- queue:from_list(NewList).
+%% new_http_options({http_options, TimeOut, AutoRedirect, SslOpts,
+%% Auth, Relaxed}) ->
+%% {http_options, "HTTP/1.1", TimeOut, AutoRedirect, SslOpts,
+%% Auth, Relaxed}.
+
+%% old_http_options({http_options, _, TimeOut, AutoRedirect,
+%% SslOpts, Auth, Relaxed}) ->
+%% {http_options, TimeOut, AutoRedirect, SslOpts, Auth, Relaxed}.
+
+%% new_queue(Queue, Fun) ->
+%% List = queue:to_list(Queue),
+%% NewList =
+%% lists:map(fun(Request) ->
+%% Settings =
+%% Fun(Request#request.settings),
+%% Request#request{settings = Settings}
+%% end, List),
+%% queue:from_list(NewList).
%%%--------------------------------------------------------------------
@@ -852,12 +855,18 @@ connect(SocketType, ToAddress,
inet6fb4 ->
Opts3 = [inet6 | Opts2],
case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of
- {error, Reason} when ((Reason =:= nxdomain) orelse
- (Reason =:= eafnosupport)) ->
+ {error, _Reason} = Error ->
Opts4 = [inet | Opts2],
- http_transport:connect(SocketType, ToAddress, Opts4, Timeout);
- Other ->
- Other
+ case http_transport:connect(SocketType,
+ ToAddress, Opts4, Timeout) of
+ {error, _} ->
+ %% Reply with the "original" error
+ Error;
+ OK ->
+ OK
+ end;
+ OK ->
+ OK
end;
_ ->
Opts3 = [IpFamily | Opts2],
@@ -878,22 +887,23 @@ connect_and_send_first_request(Address,
ConnTimeout = Settings#http_options.connect_timeout,
case connect(SocketType, Address, Options, ConnTimeout) of
{ok, Socket} ->
+ Session = #session{id = {OrigAddress, self()},
+ scheme = Scheme,
+ socket = Socket,
+ socket_type = SocketType},
?hcrd("connected - now send first request", [{socket, Socket}]),
- case httpc_request:send(Address, Request, Socket) of
+ case httpc_request:send(Address, Session, Request) of
ok ->
?hcrd("first request sent", []),
ClientClose =
httpc_request:is_client_closing(Headers),
SessionType = httpc_manager:session_type(Options),
- Session =
- #tcp_session{id = {OrigAddress, self()},
- scheme = Scheme,
- socket = Socket,
- client_close = ClientClose,
- type = SessionType},
+ Session2 =
+ Session#session{client_close = ClientClose,
+ type = SessionType},
TmpState =
State#state{request = Request,
- session = Session,
+ session = Session2,
mfa = init_mfa(Request, State),
status_line = init_status_line(Request),
headers = undefined,
@@ -947,21 +957,20 @@ handler_info(#state{request = Request,
?hcrt("handler info", [{request_info, RequestInfo}]),
%% Info about the current session/socket
- SessionType = Session#tcp_session.type,
- QueueLen = case Session#tcp_session.type of
+ SessionType = Session#session.type,
+ QueueLen = case SessionType of
pipeline ->
queue:len(Pipeline);
keep_alive ->
queue:len(KeepAlive)
end,
- Socket = Session#tcp_session.socket,
- Scheme = Session#tcp_session.scheme,
- SocketType = socket_type(Scheme),
+ Scheme = Session#session.scheme,
+ Socket = Session#session.socket,
+ SocketType = Session#session.socket_type,
?hcrt("handler info", [{session_type, SessionType},
{queue_length, QueueLen},
{scheme, Scheme},
- {socket_type, SocketType},
{socket, Socket}]),
SocketOpts = http_transport:getopts(SocketType, Socket),
@@ -1118,9 +1127,7 @@ handle_response(#state{request = Request,
?hcrd("handle response - continue", []),
%% Send request body
{_, RequestBody} = Request#request.content,
- http_transport:send(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- RequestBody),
+ send_raw(Session, RequestBody),
%% Wait for next response
activate_once(Session),
Relaxed = (Request#request.settings)#http_options.relaxed,
@@ -1217,7 +1224,7 @@ handle_pipeline(#state{status = pipeline,
%% If a pipeline that has been idle for some time is not
%% closed by the server, the client may want to close it.
NewState = activate_queue_timeout(TimeOut, State),
- NewSession = Session#tcp_session{queue_length = 0},
+ NewSession = Session#session{queue_length = 0},
httpc_manager:insert_session(NewSession, ProfileName),
%% Note mfa will be initilized when a new request
%% arrives.
@@ -1239,9 +1246,9 @@ handle_pipeline(#state{status = pipeline,
false ->
?hcrv("next request", [{request, NextRequest}]),
NewSession =
- Session#tcp_session{queue_length =
- %% Queue + current
- queue:len(Pipeline) + 1},
+ Session#session{queue_length =
+ %% Queue + current
+ queue:len(Pipeline) + 1},
httpc_manager:insert_session(NewSession, ProfileName),
Relaxed =
(NextRequest#request.settings)#http_options.relaxed,
@@ -1290,16 +1297,16 @@ handle_keep_alive_queue(
%% If a keep_alive session has been idle for some time is not
%% closed by the server, the client may want to close it.
NewState = activate_queue_timeout(TimeOut, State),
- NewSession = Session#tcp_session{queue_length = 0},
+ NewSession = Session#session{queue_length = 0},
httpc_manager:insert_session(NewSession, ProfileName),
%% Note mfa will be initilized when a new request
%% arrives.
{noreply,
- NewState#state{request = undefined,
- mfa = undefined,
+ NewState#state{request = undefined,
+ mfa = undefined,
status_line = undefined,
- headers = undefined,
- body = undefined
+ headers = undefined,
+ body = undefined
}
};
{{value, NextRequest}, KeepAlive} ->
@@ -1342,10 +1349,12 @@ case_insensitive_header(Str) when is_list(Str) ->
case_insensitive_header(Str) ->
Str.
-activate_once(#tcp_session{scheme = Scheme, socket = Socket}) ->
- SocketType = socket_type(Scheme),
+activate_once(#session{socket = Socket, socket_type = SocketType}) ->
http_transport:setopts(SocketType, Socket, [{active, once}]).
+close_socket(#session{socket = Socket, socket_type = SocketType}) ->
+ http_transport:close(SocketType, Socket).
+
activate_request_timeout(
#state{request = #request{timer = undefined} = Request} = State) ->
Timeout = (Request#request.settings)#http_options.timeout,
@@ -1378,7 +1387,7 @@ activate_queue_timeout(Time, State) ->
State#state{timers = #timers{queue_timer = Ref}}.
-is_pipeline_enabled_client(#tcp_session{type = pipeline}) ->
+is_pipeline_enabled_client(#session{type = pipeline}) ->
true;
is_pipeline_enabled_client(_) ->
false.
@@ -1391,7 +1400,7 @@ is_keep_alive_enabled_server("HTTP/1.0",
is_keep_alive_enabled_server(_,_) ->
false.
-is_keep_alive_connection(Headers, #tcp_session{client_close = ClientClose}) ->
+is_keep_alive_connection(Headers, #session{client_close = ClientClose}) ->
(not ((ClientClose) orelse httpc_response:is_server_closing(Headers))).
try_to_enable_pipeline_or_keep_alive(
@@ -1416,7 +1425,7 @@ try_to_enable_pipeline_or_keep_alive(
httpc_manager:insert_session(Session, ProfileName),
%% Make sure type is keep_alive in session
%% as it in this case might be pipeline
- NewSession = Session#tcp_session{type = keep_alive},
+ NewSession = Session#session{type = keep_alive},
State#state{status = keep_alive,
session = NewSession}
end;
@@ -1438,6 +1447,12 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg,
timers =
Timers#timers{request_timers =
lists:delete(Timer, RequestTimers)}}.
+
+cancel_timers(#timers{request_timers = ReqTmrs, queue_timer = QTmr}) ->
+ cancel_timer(QTmr, timeout_queue),
+ CancelTimer = fun({_, Timer}) -> cancel_timer(Timer, timeout) end,
+ lists:foreach(CancelTimer, ReqTmrs).
+
cancel_timer(undefined, _) ->
ok;
cancel_timer(Timer, TimeoutMsg) ->
@@ -1551,11 +1566,11 @@ init_status_line(#request{settings = Settings}) ->
socket_type(#request{scheme = http}) ->
ip_comm;
socket_type(#request{scheme = https, settings = Settings}) ->
- {ssl, Settings#http_options.ssl};
-socket_type(http) ->
- ip_comm;
-socket_type(https) ->
- {ssl, []}. %% Dummy value ok for ex setopts that does not use this value
+ Settings#http_options.ssl.
+%% socket_type(http) ->
+%% ip_comm;
+%% socket_type(https) ->
+%% {ssl1, []}. %% Dummy value ok for ex setopts that does not use this value
start_stream({_Version, _Code, _ReasonPhrase}, _Headers,
#request{stream = none} = Request) ->
@@ -1624,18 +1639,15 @@ end_stream(SL, R) ->
next_body_chunk(#state{request = #request{stream = {self, once}},
- once = once, session = Session} = State) ->
- http_transport:setopts(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- [{active, once}]),
+ once = once,
+ session = Session} = State) ->
+ activate_once(Session),
State#state{once = inactive};
next_body_chunk(#state{request = #request{stream = {self, once}},
once = inactive} = State) ->
State; %% Wait for user to call stream_next
next_body_chunk(#state{session = Session} = State) ->
- http_transport:setopts(socket_type(Session#tcp_session.scheme),
- Session#tcp_session.socket,
- [{active, once}]),
+ activate_once(Session),
State.
handle_verbose(verbose) ->
@@ -1712,6 +1724,11 @@ handle_verbose(_) ->
%% ok.
+send_raw(#session{socket = Socket, socket_type = SocketType}, Body) ->
+ http_transport:send(SocketType, Socket, Body).
+
+
+
call(Msg, Pid) ->
Timeout = infinity,
call(Msg, Pid, Timeout).
diff --git a/lib/inets/src/http_client/httpc_internal.hrl b/lib/inets/src/http_client/httpc_internal.hrl
index 4d76c4beb3..1d8a5b6a92 100644
--- a/lib/inets/src/http_client/httpc_internal.hrl
+++ b/lib/inets/src/http_client/httpc_internal.hrl
@@ -18,7 +18,11 @@
%%
%%
--include("inets_internal.hrl").
+-ifndef(httpc_internal_hrl).
+-define(httpc_internal_hrl, true).
+
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
-define(SERVICE, httpc).
-define(hcri(Label, Data), ?report_important(Label, ?SERVICE, Data)).
-define(hcrv(Label, Data), ?report_verbose(Label, ?SERVICE, Data)).
@@ -56,7 +60,11 @@
relaxed = false,
%% integer() - ms before a connect times out
- connect_timeout = ?HTTP_REQUEST_CTIMEOUT
+ connect_timeout = ?HTTP_REQUEST_CTIMEOUT,
+
+ %% bool() - Use %-encoding rfc 2396
+ url_encode
+
}
).
@@ -104,13 +112,14 @@
}
).
--record(tcp_session,
+-record(session,
{
id, % {{Host, Port}, HandlerPid}
client_close, % true | false
scheme, % http (HTTP/TCP) | https (HTTP/SSL/TCP)
socket, % Open socket, used by connection
- queue_length = 1, % Current length of pipeline or keep alive queue
+ socket_type, % socket-type, used by connection
+ queue_length = 1, % Current length of pipeline or keep-alive queue
type % pipeline | keep_alive (wait for response before sending new request)
}).
@@ -138,3 +147,6 @@
%% path, % string()
%% q % query: string()
%% }).
+
+
+-endif. % -ifdef(httpc_internal_hrl).
diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl
index b278077a66..591cb78c29 100644
--- a/lib/inets/src/http_client/httpc_manager.erl
+++ b/lib/inets/src/http_client/httpc_manager.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
+%%
%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
-%%
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,8 +21,8 @@
-behaviour(gen_server).
+-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
--include("http_internal.hrl").
%% Internal Application API
-export([
@@ -66,15 +66,6 @@
state % State of the handler: initiating | started | operational | canceled
}).
-%% Entries in the handler / request cross-ref table
-%% -record(request_info,
-%% {
-%% id, % Id of the request
-%% handler, % Pid of the handler process
-%% from, % The From value for the caller
-%% mref % Monitor ref for the caller
-%% }).
-
%%====================================================================
%% Internal Application API
@@ -333,7 +324,7 @@ do_init(ProfileName, CookiesDir) ->
?hcrt("create session db", []),
SessionDbName = session_db_name(ProfileName),
ets:new(SessionDbName,
- [public, set, named_table, {keypos, #tcp_session.id}]),
+ [public, set, named_table, {keypos, #session.id}]),
%% Create handler db
?hcrt("create handler/request db", []),
@@ -577,7 +568,9 @@ handle_info({'EXIT', Pid, Reason}, #state{handler_db = HandlerDb} = State) ->
handle_info({'DOWN', _, _, Pid, _}, State) ->
%%
- %% Check what happens to waiting requests! Chall we not send a reply?
+ %% Normally this should have been cleaned up already
+ %% (when receiving {request_done, PequestId}), but
+ %% just in case there is a glitch, cleanup anyway.
%%
Pattern = #handler_info{handler = Pid, _ = '_'},
@@ -649,7 +642,16 @@ get_handler_info(Tab) ->
Acc
end,
Handlers2 = lists:foldl(F, [], Handlers1),
- Handlers3 = [{Pid, State, httpc_handler:info(Pid)} ||
+ Handlers3 = [{Pid, State,
+ case (catch httpc_handler:info(Pid)) of
+ {'EXIT', _} ->
+ %% Why would this crash?
+ %% Only if the process has died, but we don't
+ %% know about it?
+ [];
+ Else ->
+ Else
+ end} ||
{Pid, State} <- Handlers2],
Handlers3.
@@ -666,6 +668,10 @@ handle_started(StarterPid, ReqId, HandlerPid,
case ets:lookup(HandlerDb, ReqId) of
[#handler_info{state = initiating} = HandlerInfo] ->
?hcri("received started ack for initiating handler", []),
+ %% As a last resort, make sure we know when it exits,
+ %% in case it forgets to notify us.
+ %% We dont need to know the ref id?
+ erlang:monitor(process, HandlerPid),
HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid,
state = started},
ets:insert(HandlerDb, HandlerInfo2),
@@ -728,10 +734,11 @@ handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result,
ok;
[] ->
- error_report(Profile,
- "handler (~p) successfully started "
- "for unknown request ~p => canceling",
- [HandlerPid, ReqId]),
+ ?hcri("handler successfully started "
+ "for unknown request => canceling",
+ [{profile, Profile},
+ {handler, HandlerPid},
+ {request, ReqId}]),
httpc_handler:cancel(ReqId, HandlerPid)
end.
@@ -876,12 +883,12 @@ select_session(Method, HostPort, Scheme, SessionType,
%% client_close, scheme and type specified.
%% The fields id (part of: HandlerPid) and queue_length
%% specified.
- Pattern = #tcp_session{id = {HostPort, '$1'},
- client_close = false,
- scheme = Scheme,
- socket = '_',
- queue_length = '$2',
- type = SessionType},
+ Pattern = #session{id = {HostPort, '$1'},
+ client_close = false,
+ scheme = Scheme,
+ queue_length = '$2',
+ type = SessionType,
+ _ = '_'},
%% {'_', {HostPort, '$1'}, false, Scheme, '_', '$2', SessionTyp},
Candidates = ets:match(SessionDb, Pattern),
?hcrd("select session", [{host_port, HostPort},
diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl
index 55e0af4b42..d4df97ad40 100644
--- a/lib/inets/src/http_client/httpc_request.erl
+++ b/lib/inets/src/http_client/httpc_request.erl
@@ -19,12 +19,13 @@
-module(httpc_request).
--include("http_internal.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
%%% Internal API
-export([send/3, is_idempotent/1, is_client_closing/1]).
+
%%%=========================================================================
%%% Internal application API
%%%=========================================================================
@@ -39,10 +40,9 @@
%%
%% Description: Composes and sends a HTTP-request.
%%-------------------------------------------------------------------------
-send(SendAddr, #request{scheme = Scheme, socket_opts = SocketOpts} = Request,
- Socket)
+send(SendAddr, #session{socket = Socket, socket_type = SocketType},
+ #request{socket_opts = SocketOpts} = Request)
when is_list(SocketOpts) ->
- SocketType = socket_type(Scheme),
case http_transport:setopts(SocketType, Socket, SocketOpts) of
ok ->
send(SendAddr, Socket, SocketType,
@@ -50,8 +50,7 @@ send(SendAddr, #request{scheme = Scheme, socket_opts = SocketOpts} = Request,
{error, Reason} ->
{error, {setopts_failed, Reason}}
end;
-send(SendAddr, #request{scheme = Scheme} = Request, Socket) ->
- SocketType = socket_type(Scheme),
+send(SendAddr, #session{socket = Socket, socket_type = SocketType}, Request) ->
send(SendAddr, Socket, SocketType, Request).
send(SendAddr, Socket, SocketType,
@@ -209,10 +208,6 @@ headers(_, "HTTP/0.9") ->
headers(Headers, _) ->
Headers.
-socket_type(http) ->
- ip_comm;
-socket_type(https) ->
- {ssl, []}.
http_headers([], Headers) ->
lists:flatten(Headers);
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl
index df7d40a33e..207b96271c 100644
--- a/lib/inets/src/http_client/httpc_response.erl
+++ b/lib/inets/src/http_client/httpc_response.erl
@@ -19,10 +19,12 @@
-module(httpc_response).
--include("http_internal.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpc_internal.hrl").
%% API
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([parse/1, result/2, send/2, error/2, is_server_closing/1,
stream_start/3]).
diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile
index 7f4c92861c..aaf3cfb995 100644
--- a/lib/inets/src/http_lib/Makefile
+++ b/lib/inets/src/http_lib/Makefile
@@ -45,7 +45,8 @@ MODULES = \
http_transport\
http_util \
http_request \
- http_response
+ http_response \
+ http_uri
HRL_FILES = http_internal.hrl
@@ -55,24 +56,16 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
# ----------------------------------------------------
-# INETS FLAGS
-# ----------------------------------------------------
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
-
-
-# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-INETS_ERL_FLAGS += -I ../inets_app
-ifeq ($(WARN_UNUSED_WARS),true)
-ERL_COMPILE_FLAGS += +warn_unused_vars
-endif
+include ../inets_app/inets.mk
-ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \
- $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS) \
+ -I../../include \
+ -I../inets_app
# ----------------------------------------------------
@@ -94,9 +87,10 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/http_lib
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_lib
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
diff --git a/lib/inets/src/http_lib/http_chunk.erl b/lib/inets/src/http_lib/http_chunk.erl
index 621bc68eae..57647438e9 100644
--- a/lib/inets/src/http_lib/http_chunk.erl
+++ b/lib/inets/src/http_lib/http_chunk.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,8 @@
%% %CopyrightEnd%
%%
%% Description: Implements chunked transfer encoding see RFC2616 section
-%% 3.6.1
+%% 3.6.1
+
-module(http_chunk).
-include("http_internal.hrl").
@@ -28,6 +29,7 @@
%% little at a time on a socket.
-export([decode_size/1, ignore_extensions/1, decode_data/1, decode_trailer/1]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -81,6 +83,9 @@ encode(Chunk) when is_binary(Chunk)->
HEXSize = list_to_binary(http_util:integer_to_hexlist(size(Chunk))),
<<HEXSize/binary, ?CR, ?LF, Chunk/binary, ?CR, ?LF>>;
+encode([<<>>]) ->
+ [];
+
encode(Chunk) when is_list(Chunk)->
HEXSize = http_util:integer_to_hexlist(erlang:iolist_size(Chunk)),
[HEXSize, ?CR, ?LF, Chunk, ?CR, ?LF].
@@ -88,6 +93,7 @@ encode(Chunk) when is_list(Chunk)->
encode_last() ->
<<$0, ?CR, ?LF, ?CR, ?LF >>.
+
%%-------------------------------------------------------------------------
%% handle_headers(HeaderRecord, ChunkedHeaders) -> NewHeaderRecord
%%
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl
index bb2e831727..5440f214b5 100644
--- a/lib/inets/src/http_lib/http_internal.hrl
+++ b/lib/inets/src/http_lib/http_internal.hrl
@@ -1,28 +1,37 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
--include("inets_internal.hrl").
+-ifndef(http_internal_hrl).
+-define(http_internal_hrl, true).
--define(HTTP_MAX_BODY_SIZE, nolimit).
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
+-define(HTTP_MAX_BODY_SIZE, nolimit).
-define(HTTP_MAX_HEADER_SIZE, 10240).
--define(HTTP_MAX_URI_SIZE, nolimit).
+-define(HTTP_MAX_URI_SIZE, nolimit).
+
+-ifndef(HTTP_DEFAULT_SSL_KIND).
+-define(HTTP_DEFAULT_SSL_KIND, ossl).
+%% -define(HTTP_DEFAULT_SSL_KIND, essl).
+-endif. % -ifdef(HTTP_DEFAULT_SSL_KIND).
+
%%% Response headers
-record(http_response_h,{
@@ -106,3 +115,5 @@
'last-modified',
other=[] % list() - Key/Value list with other headers
}).
+
+-endif. % -ifdef(http_internal_hrl).
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index 7c2ac626e6..0024d19fc1 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -36,7 +36,9 @@
-export([negotiate/3]).
--include("inets_internal.hrl").
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+-include("http_internal.hrl").
+
-define(SERVICE, httpl).
-define(hlri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
-define(hlrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
@@ -55,6 +57,18 @@
%% Description: Makes sure inet_db or ssl is started.
%%-------------------------------------------------------------------------
start(ip_comm) ->
+ do_start_ip_comm();
+
+%% This is just for backward compatibillity
+start({ssl, _}) ->
+ do_start_ssl();
+start({ossl, _}) ->
+ do_start_ssl();
+start({essl, _}) ->
+ do_start_ssl().
+
+
+do_start_ip_comm() ->
case inet_db:start() of
{ok, _} ->
ok;
@@ -62,8 +76,9 @@ start(ip_comm) ->
ok;
Error ->
Error
- end;
-start({ssl, _}) ->
+ end.
+
+do_start_ssl() ->
case ssl:start() of
ok ->
ok;
@@ -97,18 +112,26 @@ connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout)
[{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]),
gen_tcp:connect(Host, Port, Opts, Timeout);
-connect({ssl, SslConfig}, {Host, Port}, _, Timeout) ->
- Opts = [binary, {active, false}] ++ SslConfig,
- ?hlrt("connect using ssl",
- [{host, Host}, {port, Port}, {ssl_config, SslConfig},
- {timeout, Timeout}]),
+%% Wrapper for backaward compatibillity
+connect({ssl, SslConfig}, Address, Opts, Timeout) ->
+ connect({?HTTP_DEFAULT_SSL_KIND, SslConfig}, Address, Opts, Timeout);
+
+connect({ossl, SslConfig}, {Host, Port}, _, Timeout) ->
+ Opts = [binary, {active, false}, {ssl_imp, old}] ++ SslConfig,
+ ?hlrt("connect using ossl",
+ [{host, Host},
+ {port, Port},
+ {ssl_config, SslConfig},
+ {timeout, Timeout}]),
ssl:connect(Host, Port, Opts, Timeout);
-connect({erl_ssl, SslConfig}, {Host, Port}, _, Timeout) ->
+connect({essl, SslConfig}, {Host, Port}, _, Timeout) ->
Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig,
- ?hlrt("connect using erl_ssl",
- [{host, Host}, {port, Port}, {ssl_config, SslConfig},
- {timeout, Timeout}]),
+ ?hlrt("connect using essl",
+ [{host, Host},
+ {port, Port},
+ {ssl_config, SslConfig},
+ {timeout, Timeout}]),
ssl:connect(Host, Port, Opts, Timeout).
@@ -136,13 +159,32 @@ listen(ip_comm, Addr, Port) ->
Else
end;
-listen({ssl, SSLConfig} = Ssl, Addr, Port) ->
+%% Wrapper for backaward compatibillity
+listen({ssl, SSLConfig}, Addr, Port) ->
+ ?hlrt("listen (wrapper)",
+ [{addr, Addr},
+ {port, Port},
+ {ssl_config, SSLConfig}]),
+ listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port);
+
+listen({ossl, SSLConfig} = Ssl, Addr, Port) ->
+ ?hlrt("listen (ossl)",
+ [{addr, Addr},
+ {port, Port},
+ {ssl_config, SSLConfig}]),
Opt = sock_opt(Ssl, Addr, SSLConfig),
- ssl:listen(Port, Opt);
-
-listen({erl_ssl, SSLConfig} = Ssl, Addr, Port) ->
+ ?hlrt("listen options", [{opt, Opt}]),
+ ssl:listen(Port, [{ssl_imp, old} | Opt]);
+
+listen({essl, SSLConfig} = Ssl, Addr, Port) ->
+ ?hlrt("listen (essl)",
+ [{addr, Addr},
+ {port, Port},
+ {ssl_config, SSLConfig}]),
Opt = sock_opt(Ssl, Addr, SSLConfig),
- ssl:listen(Port, [{ssl_imp, new} | Opt]).
+ ?hlrt("listen options", [{opt, Opt}]),
+ Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt],
+ ssl:listen(Port, Opt2).
listen_ip_comm(Addr, Port) ->
@@ -150,24 +192,31 @@ listen_ip_comm(Addr, Port) ->
case IpFamily of
inet6fb4 ->
Opts2 = [inet6 | Opts],
+ ?hlrt("try ipv6 listen", [{port, NewPort}, {opts, Opts2}]),
case (catch gen_tcp:listen(NewPort, Opts2)) of
{error, Reason} when ((Reason =:= nxdomain) orelse
(Reason =:= eafnosupport)) ->
Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen failed - try ipv4 instead",
+ [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
gen_tcp:listen(NewPort, Opts3);
%% This is when a given hostname has resolved to a
%% IPv4-address. The inet6-option together with a
%% {ip, IPv4} option results in badarg
- {'EXIT', _} ->
+ {'EXIT', Reason} ->
Opts3 = [inet | Opts],
+ ?hlrt("ipv6 listen exit - try ipv4 instead",
+ [{reason, Reason}, {port, NewPort}, {opts, Opts3}]),
gen_tcp:listen(NewPort, Opts3);
Other ->
+ ?hlrt("ipv6 listen done", [{other, Other}]),
Other
end;
_ ->
Opts2 = [IpFamily | Opts],
+ ?hlrt("listen", [{port, NewPort}, {opts, Opts2}]),
gen_tcp:listen(NewPort, Opts2)
end.
@@ -228,9 +277,17 @@ ip_family_of(IpFamilyStr) ->
%%-------------------------------------------------------------------------
accept(SocketType, ListenSocket) ->
accept(SocketType, ListenSocket, infinity).
+
accept(ip_comm, ListenSocket, Timeout) ->
gen_tcp:accept(ListenSocket, Timeout);
-accept({ssl,_SSLConfig}, ListenSocket, Timeout) ->
+
+%% Wrapper for backaward compatibillity
+accept({ssl, SSLConfig}, ListenSocket, Timeout) ->
+ accept({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, ListenSocket, Timeout);
+
+accept({ossl, _SSLConfig}, ListenSocket, Timeout) ->
+ ssl:transport_accept(ListenSocket, Timeout);
+accept({essl, _SSLConfig}, ListenSocket, Timeout) ->
ssl:transport_accept(ListenSocket, Timeout).
@@ -244,7 +301,15 @@ accept({ssl,_SSLConfig}, ListenSocket, Timeout) ->
%%-------------------------------------------------------------------------
controlling_process(ip_comm, Socket, NewOwner) ->
gen_tcp:controlling_process(Socket, NewOwner);
-controlling_process({ssl, _}, Socket, NewOwner) ->
+
+%% Wrapper for backaward compatibillity
+controlling_process({ssl, SSLConfig}, Socket, NewOwner) ->
+ controlling_process({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, NewOwner);
+
+controlling_process({ossl, _}, Socket, NewOwner) ->
+ ssl:controlling_process(Socket, NewOwner);
+
+controlling_process({essl, _}, Socket, NewOwner) ->
ssl:controlling_process(Socket, NewOwner).
@@ -259,9 +324,23 @@ controlling_process({ssl, _}, Socket, NewOwner) ->
setopts(ip_comm, Socket, Options) ->
?hlrt("ip_comm setopts", [{socket, Socket}, {options, Options}]),
inet:setopts(Socket, Options);
-setopts({ssl, _}, Socket, Options) ->
- ?hlrt("ssl setopts", [{socket, Socket}, {options, Options}]),
- ssl:setopts(Socket, Options).
+
+%% Wrapper for backaward compatibillity
+setopts({ssl, SSLConfig}, Socket, Options) ->
+ setopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
+
+setopts({ossl, _}, Socket, Options) ->
+ ?hlrt("[o]ssl setopts", [{socket, Socket}, {options, Options}]),
+ Reason = (catch ssl:setopts(Socket, Options)),
+ ?hlrt("[o]ssl setopts result", [{reason, Reason}]),
+ Reason;
+
+
+setopts({essl, _}, Socket, Options) ->
+ ?hlrt("[e]ssl setopts", [{socket, Socket}, {options, Options}]),
+ Reason = (catch ssl:setopts(Socket, Options)),
+ ?hlrt("[e]ssl setopts result", [{reason, Reason}]),
+ Reason.
%%-------------------------------------------------------------------------
@@ -283,15 +362,27 @@ getopts(ip_comm, Socket, Options) ->
{error, _} ->
[]
end;
-getopts({ssl, _}, Socket, Options) ->
+
+%% Wrapper for backaward compatibillity
+getopts({ssl, SSLConfig}, Socket, Options) ->
+ getopts({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Options);
+
+getopts({ossl, _}, Socket, Options) ->
?hlrt("ssl getopts", [{socket, Socket}, {options, Options}]),
+ getopts_ssl(Socket, Options);
+
+getopts({essl, _}, Socket, Options) ->
+ ?hlrt("essl getopts", [{socket, Socket}, {options, Options}]),
+ getopts_ssl(Socket, Options).
+
+getopts_ssl(Socket, Options) ->
case ssl:getopts(Socket, Options) of
{ok, SocketOpts} ->
SocketOpts;
{error, _} ->
[]
end.
-
+
%%-------------------------------------------------------------------------
%% getstat(SocketType, Socket) -> socket_stats()
@@ -308,8 +399,15 @@ getstat(ip_comm = _SocketType, Socket) ->
{error, _} ->
[]
end;
-getstat({ssl, _} = _SocketType, _Socket) ->
- %% ?hlrt("ssl getstat", [{socket, Socket}]),
+
+%% Wrapper for backaward compatibillity
+getstat({ssl, SSLConfig}, Socket) ->
+ getstat({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
+
+getstat({ossl, _} = _SocketType, _Socket) ->
+ [];
+
+getstat({essl, _} = _SocketType, _Socket) ->
[].
@@ -322,7 +420,15 @@ getstat({ssl, _} = _SocketType, _Socket) ->
%%-------------------------------------------------------------------------
send(ip_comm, Socket, Message) ->
gen_tcp:send(Socket, Message);
-send({ssl, _}, Socket, Message) ->
+
+%% Wrapper for backaward compatibillity
+send({ssl, SSLConfig}, Socket, Message) ->
+ send({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Message);
+
+send({ossl, _}, Socket, Message) ->
+ ssl:send(Socket, Message);
+
+send({essl, _}, Socket, Message) ->
ssl:send(Socket, Message).
@@ -335,9 +441,18 @@ send({ssl, _}, Socket, Message) ->
%%-------------------------------------------------------------------------
close(ip_comm, Socket) ->
gen_tcp:close(Socket);
-close({ssl, _}, Socket) ->
+
+%% Wrapper for backaward compatibillity
+close({ssl, SSLConfig}, Socket) ->
+ close({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
+
+close({ossl, _}, Socket) ->
+ ssl:close(Socket);
+
+close({essl, _}, Socket) ->
ssl:close(Socket).
+
%%-------------------------------------------------------------------------
%% peername(SocketType, Socket) -> {Port, SockName}
%% SocketType = ip_comm | {ssl, _}
@@ -368,7 +483,17 @@ peername(ip_comm, Socket) ->
{-1, "unknown"}
end;
-peername({ssl, _}, Socket) ->
+%% Wrapper for backaward compatibillity
+peername({ssl, SSLConfig}, Socket) ->
+ peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
+
+peername({ossl, _}, Socket) ->
+ peername_ssl(Socket);
+
+peername({essl, _}, Socket) ->
+ peername_ssl(Socket).
+
+peername_ssl(Socket) ->
case ssl:peername(Socket) of
{ok,{{A, B, C, D}, Port}} ->
PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++
@@ -409,7 +534,17 @@ sockname(ip_comm, Socket) ->
{-1, "unknown"}
end;
-sockname({ssl, _}, Socket) ->
+%% Wrapper for backaward compatibillity
+sockname({ssl, SSLConfig}, Socket) ->
+ sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket);
+
+sockname({ossl, _}, Socket) ->
+ sockname_ssl(Socket);
+
+sockname({essl, _}, Socket) ->
+ sockname_ssl(Socket).
+
+sockname_ssl(Socket) ->
case ssl:sockname(Socket) of
{ok,{{A, B, C, D}, Port}} ->
SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++
@@ -455,22 +590,31 @@ sock_opt2(Opts) ->
[{packet, 0}, {active, false} | Opts].
negotiate(ip_comm,_,_) ->
+ ?hlrt("negotiate(ip_comm)", []),
ok;
-negotiate({ssl,_},Socket,Timeout) ->
- negotiate(Socket, Timeout);
-negotiate({erl_ssl, _}, Socket, Timeout) ->
- negotiate(Socket, Timeout).
-
-negotiate(Socket, Timeout) ->
+negotiate({ssl, SSLConfig}, Socket, Timeout) ->
+ ?hlrt("negotiate(ssl)", []),
+ negotiate({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket, Timeout);
+negotiate({ossl, _}, Socket, Timeout) ->
+ ?hlrt("negotiate(ossl)", []),
+ negotiate_ssl(Socket, Timeout);
+negotiate({essl, _}, Socket, Timeout) ->
+ ?hlrt("negotiate(essl)", []),
+ negotiate_ssl(Socket, Timeout).
+
+negotiate_ssl(Socket, Timeout) ->
+ ?hlrt("negotiate_ssl", [{socket, Socket}, {timeout, Timeout}]),
case ssl:ssl_accept(Socket, Timeout) of
ok ->
ok;
- {error, Error} ->
- case lists:member(Error,
- [timeout,econnreset,esslaccept,esslerrssl]) of
+ {error, Reason} ->
+ ?hlrd("negotiate_ssl - accept failed", [{reason, Reason}]),
+ %% Look for "valid" error reasons
+ ValidReasons = [timeout, econnreset, esslaccept, esslerrssl],
+ case lists:member(Reason, ValidReasons) of
true ->
- {error,normal};
+ {error, normal};
false ->
- {error, Error}
+ {error, Reason}
end
end.
diff --git a/lib/inets/src/http_client/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl
index 615a0d8ec4..44b9face0b 100644
--- a/lib/inets/src/http_client/http_uri.erl
+++ b/lib/inets/src/http_lib/http_uri.erl
@@ -1,26 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(http_uri).
--export([parse/1]).
+-export([parse/1, encode/1, decode/1]).
%%%=========================================================================
%%% API
@@ -34,10 +34,25 @@ parse(AbsURI) ->
{UserInfo, Host, Port, Path, Query} ->
{Scheme, UserInfo, Host, Port, Path, Query};
_ ->
- {error, {malformed_url, AbsURI}}
+ {error, {malformed_url, AbsURI}}
end
end.
+encode(URI) ->
+ Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?,
+ $#, $[, $], $<, $>, $\", ${, $}, $|,
+ $\\, $', $^, $%, $ ]),
+ lists:append(lists:map(fun(Char) ->
+ uri_encode(Char, Reserved)
+ end, URI)).
+
+decode([$%,Hex1,Hex2|Rest]) ->
+ [hex2dec(Hex1)*16+hex2dec(Hex2)|decode(Rest)];
+decode([First|Rest]) ->
+ [First|decode(Rest)];
+decode([]) ->
+ [].
+
%%%========================================================================
%%% Internal functions
%%%========================================================================
@@ -56,7 +71,7 @@ parse_scheme(AbsURI) ->
parse_uri_rest(Scheme, "//" ++ URIPart) ->
- {Authority, PathQuery} =
+ {Authority, PathQuery} =
case split_uri(URIPart, "/", URIPart, 1, 0) of
Split = {_, _} ->
Split;
@@ -68,7 +83,7 @@ parse_uri_rest(Scheme, "//" ++ URIPart) ->
{URIPart,""}
end
end,
-
+
{UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1),
{Host, Port} = parse_host_port(Scheme, HostPort),
{Path, Query} = parse_path_query(PathQuery),
@@ -78,7 +93,6 @@ parse_uri_rest(Scheme, "//" ++ URIPart) ->
parse_path_query(PathQuery) ->
{Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0),
{path(Path), Query}.
-
parse_host_port(Scheme,"[" ++ HostPort) -> %ipv6
DefaultPort = default_port(Scheme),
@@ -90,12 +104,12 @@ parse_host_port(Scheme, HostPort) ->
DefaultPort = default_port(Scheme),
{Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1),
{Host, int_port(Port)}.
-
+
split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
case inets_regexp:first_match(UriPart, SplitChar) of
{match, Match, _} ->
{string:substr(UriPart, 1, Match - SkipLeft),
- string:substr(UriPart, Match + SkipRight, length(UriPart))};
+ string:substr(UriPart, Match + SkipRight, length(UriPart))};
nomatch ->
NoMatchResult
end.
@@ -114,3 +128,15 @@ path("") ->
"/";
path(Path) ->
Path.
+
+uri_encode(Char, Reserved) ->
+ case sets:is_element(Char, Reserved) of
+ true ->
+ [ $% | http_util:integer_to_hexlist(Char)];
+ false ->
+ [Char]
+ end.
+
+hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
+hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
+hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl
index ddb58c7116..4f1147176c 100644
--- a/lib/inets/src/http_lib/http_util.erl
+++ b/lib/inets/src/http_lib/http_util.erl
@@ -38,13 +38,79 @@ to_upper(Str) ->
to_lower(Str) ->
string:to_lower(Str).
-convert_netscapecookie_date([_D,_A,_Y, $,, _SP,
- D1,D2,_DA,
- M,O,N,_DA,
- Y1,Y2,Y3,Y4,_SP,
- H1,H2,_Col,
- M1,M2,_Col,
+%% Example: Mon, 09-Dec-2002 13:46:00 GMT
+convert_netscapecookie_date([_D,_A,_Y, $,, $ ,
+ D1,D2, $-,
+ M,O,N, $-,
+ Y1,Y2,Y3,Y4, $ ,
+ H1,H2, $:,
+ M1,M2, $:,
+ S1,S2|_Rest]) ->
+ Year = list_to_integer([Y1,Y2,Y3,Y4]),
+ Day = list_to_integer([D1,D2]),
+ Month = convert_month([M,O,N]),
+ Hour = list_to_integer([H1,H2]),
+ Min = list_to_integer([M1,M2]),
+ Sec = list_to_integer([S1,S2]),
+ {{Year,Month,Day},{Hour,Min,Sec}};
+
+convert_netscapecookie_date([_D,_A,_Y, $,, $ ,
+ D1,D2, $-,
+ M,O,N, $-,
+ Y3,Y4, $ ,
+ H1,H2, $:,
+ M1,M2, $:,
+ S1,S2|_Rest]) ->
+ {CurrentYear, _, _} = date(),
+ [Y1,Y2|_] = integer_to_list(CurrentYear),
+ Year = list_to_integer([Y1,Y2,Y3,Y4]),
+ Day = list_to_integer([D1,D2]),
+ Month = convert_month([M,O,N]),
+ Hour = list_to_integer([H1,H2]),
+ Min = list_to_integer([M1,M2]),
+ Sec = list_to_integer([S1,S2]),
+ {{Year,Month,Day},{Hour,Min,Sec}};
+
+convert_netscapecookie_date([_D,_A,_Y, $ ,
+ D1,D2, $-,
+ M,O,N, $-,
+ Y1,Y2,Y3,Y4, $ ,
+ H1,H2, $:,
+ M1,M2, $:,
S1,S2|_Rest]) ->
+ Year = list_to_integer([Y1,Y2,Y3,Y4]),
+ Day = list_to_integer([D1,D2]),
+ Month = convert_month([M,O,N]),
+ Hour = list_to_integer([H1,H2]),
+ Min = list_to_integer([M1,M2]),
+ Sec = list_to_integer([S1,S2]),
+ {{Year,Month,Day},{Hour,Min,Sec}};
+
+convert_netscapecookie_date([_D,_A,_Y, $ ,
+ D1,D2, $-,
+ M,O,N, $-,
+ Y3,Y4, $ ,
+ H1,H2, $:,
+ M1,M2, $:,
+ S1,S2|_Rest]) ->
+ {CurrentYear, _, _} = date(),
+ [Y1,Y2|_] = integer_to_list(CurrentYear),
+ Year = list_to_integer([Y1,Y2,Y3,Y4]),
+ Day = list_to_integer([D1,D2]),
+ Month = convert_month([M,O,N]),
+ Hour = list_to_integer([H1,H2]),
+ Min = list_to_integer([M1,M2]),
+ Sec = list_to_integer([S1,S2]),
+ {{Year,Month,Day},{Hour,Min,Sec}};
+
+%% Sloppy...
+convert_netscapecookie_date([_D,_A,_Y, $,, _SP,
+ D1,D2,_DA,
+ M,O,N,_DA,
+ Y1,Y2,Y3,Y4,_SP,
+ H1,H2,_Col,
+ M1,M2,_Col,
+ S1,S2|_Rest]) ->
Year=list_to_integer([Y1,Y2,Y3,Y4]),
Day=list_to_integer([D1,D2]),
Month=convert_month([M,O,N]),
@@ -54,12 +120,12 @@ convert_netscapecookie_date([_D,_A,_Y, $,, _SP,
{{Year,Month,Day},{Hour,Min,Sec}};
convert_netscapecookie_date([_D,_A,_Y, _SP,
- D1,D2,_DA,
- M,O,N,_DA,
- Y1,Y2,Y3,Y4,_SP,
- H1,H2,_Col,
- M1,M2,_Col,
- S1,S2|_Rest]) ->
+ D1,D2,_DA,
+ M,O,N,_DA,
+ Y1,Y2,Y3,Y4,_SP,
+ H1,H2,_Col,
+ M1,M2,_Col,
+ S1,S2|_Rest]) ->
Year=list_to_integer([Y1,Y2,Y3,Y4]),
Day=list_to_integer([D1,D2]),
Month=convert_month([M,O,N]),
@@ -68,17 +134,17 @@ convert_netscapecookie_date([_D,_A,_Y, _SP,
Sec=list_to_integer([S1,S2]),
{{Year,Month,Day},{Hour,Min,Sec}}.
-hexlist_to_integer([])->
+hexlist_to_integer([]) ->
empty;
%%When the string only contains one value its eaasy done.
%% 0-9
-hexlist_to_integer([Size]) when Size >= 48 , Size =< 57 ->
+hexlist_to_integer([Size]) when (Size >= 48) andalso (Size =< 57) ->
Size - 48;
%% A-F
-hexlist_to_integer([Size]) when Size >= 65 , Size =< 70 ->
+hexlist_to_integer([Size]) when (Size >= 65) andalso (Size =< 70) ->
Size - 55;
%% a-f
-hexlist_to_integer([Size]) when Size >= 97 , Size =< 102 ->
+hexlist_to_integer([Size]) when (Size >= 97) andalso (Size =< 102) ->
Size - 87;
hexlist_to_integer([_Size]) ->
not_a_num;
@@ -141,7 +207,7 @@ hexlist_to_integer2([HexVal | HexString], Pos, Sum)
hexlist_to_integer2(_AfterHexString, _Pos, Sum)->
Sum.
-integer_to_hexlist(Num, Pot, Res) when Pot<0 ->
+integer_to_hexlist(Num, Pot, Res) when Pot < 0 ->
convert_to_ascii([Num | Res]);
integer_to_hexlist(Num,Pot,Res) ->
@@ -163,7 +229,9 @@ convert_to_ascii(RevesedNum) ->
convert_to_ascii([], Num)->
Num;
-convert_to_ascii([Num | Reversed], Number) when Num > -1, Num < 10 ->
+convert_to_ascii([Num | Reversed], Number)
+ when (Num > -1) andalso (Num < 10) ->
convert_to_ascii(Reversed, [Num + 48 | Number]);
-convert_to_ascii([Num | Reversed], Number) when Num > 9, Num < 16 ->
+convert_to_ascii([Num | Reversed], Number)
+ when (Num > 9) andalso (Num < 16) ->
convert_to_ascii(Reversed, [Num + 55 | Number]).
diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile
index ce1405011e..55cc68dede 100644
--- a/lib/inets/src/http_server/Makefile
+++ b/lib/inets/src/http_server/Makefile
@@ -82,7 +82,7 @@ MODULES = \
mod_security \
mod_security_server
-HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl
+HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl
ERL_FILES = $(MODULES:%=%.erl)
@@ -90,20 +90,16 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
# ----------------------------------------------------
-# INETS FLAGS
-# ----------------------------------------------------
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
-
-
-# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-INETS_ERL_FLAGS += -I ../http_lib -I ../inets_app -pa ../../ebin
-ERL_COMPILE_FLAGS += $(INETS_ERL_FLAGS) \
- $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+include ../inets_app/inets.mk
+
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS) \
+ -I../inets_app \
+ -I../http_lib \
# ----------------------------------------------------
@@ -125,9 +121,10 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/http_server
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_server
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
diff --git a/lib/inets/src/http_server/httpd.erl b/lib/inets/src/http_server/httpd.erl
index 8fe54ccef6..93608dbf96 100644
--- a/lib/inets/src/http_server/httpd.erl
+++ b/lib/inets/src/http_server/httpd.erl
@@ -24,54 +24,24 @@
-include("httpd.hrl").
--deprecated({start, 0, next_major_release}).
--deprecated({start, 1, next_major_release}).
--deprecated({start_link, 1, next_major_release}).
--deprecated({start_child, 0, next_major_release}).
--deprecated({start_child, 1, next_major_release}).
--deprecated({stop, 0, next_major_release}).
--deprecated({stop, 1, next_major_release}).
--deprecated({stop, 2, next_major_release}).
--deprecated({stop_child, 0, next_major_release}).
--deprecated({stop_child, 1, next_major_release}).
--deprecated({stop_child, 2, next_major_release}).
--deprecated({restart, 0, next_major_release}).
--deprecated({restart, 1, next_major_release}).
--deprecated({restart, 2, next_major_release}).
--deprecated({block, 0, next_major_release}).
--deprecated({block, 1, next_major_release}).
--deprecated({block, 2, next_major_release}).
--deprecated({block, 3, next_major_release}).
--deprecated({block, 4, next_major_release}).
--deprecated({unblock, 0, next_major_release}).
--deprecated({unblock, 1, next_major_release}).
--deprecated({unblock, 2, next_major_release}).
-
%% Behavior callbacks
--export([start_standalone/1, start_service/1, stop_service/1, services/0,
- service_info/1]).
+-export([
+ start_standalone/1,
+ start_service/1,
+ stop_service/1,
+ services/0,
+ service_info/1
+ ]).
%% API
-export([parse_query/1, reload_config/2, info/1, info/2, info/3]).
-%% Deprecated
--export([start/0, start/1,
- start_link/0, start_link/1,
- start_child/0,start_child/1,
- stop/0,stop/1,stop/2,
- stop_child/0,stop_child/1,stop_child/2,
- restart/0,restart/1,restart/2]).
-
-%% Management stuff should be internal functions
-%% Will be from r13
--export([block/0,block/1,block/2,block/3,block/4,
- unblock/0,unblock/1,unblock/2]).
-
-%% Internal Debugging and status info stuff...
-%% Keep for now should probably be moved to test catalog
--export([get_status/1,get_status/2,get_status/3,
- get_admin_state/0,get_admin_state/1,get_admin_state/2,
- get_usage_state/0,get_usage_state/1,get_usage_state/2]).
+%% Internal debugging and status info stuff...
+-export([
+ get_status/1, get_status/2, get_status/3,
+ get_admin_state/0, get_admin_state/1, get_admin_state/2,
+ get_usage_state/0, get_usage_state/1, get_usage_state/2
+ ]).
%%%========================================================================
%%% API
@@ -111,6 +81,7 @@ info(Address, Port, Properties) when is_integer(Port) andalso
is_list(Properties) ->
httpd_conf:get_config(Address, Port, Properties).
+
%%%========================================================================
%%% Behavior callbacks
%%%========================================================================
@@ -149,6 +120,8 @@ service_info(Pid) ->
exit:{noproc, _} ->
{error, service_not_available}
end.
+
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -176,6 +149,7 @@ child_name2info({httpd_instance_sup, Address, Port}) ->
{ok, [{bind_address, Address}, {port, Port} | Info]}
end.
+
reload(Config, Address, Port) ->
Name = make_name(Address,Port),
case whereis(Name) of
@@ -185,26 +159,12 @@ reload(Config, Address, Port) ->
{error,not_started}
end.
-reload(Addr, Port) when is_integer(Port) ->
- Name = make_name(Addr,Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- httpd_manager:reload(Pid, undefined);
- _ ->
- {error,not_started}
- end.
%%% =========================================================
-%%% Function: block/0, block/1, block/2, block/3, block/4
-%%% block()
-%%% block(Port)
-%%% block(ConfigFile)
-%%% block(Addr,Port)
-%%% block(Port,Mode)
-%%% block(ConfigFile,Mode)
-%%% block(Addr,Port,Mode)
-%%% block(ConfigFile,Mode,Timeout)
-%%% block(Addr,Port,Mode,Timeout)
+%%% Function: block/3, block/4
+%%% block(Addr, Port, Mode)
+%%% block(ConfigFile, Mode, Timeout)
+%%% block(Addr, Port, Mode, Timeout)
%%%
%%% Returns: ok | {error,Reason}
%%%
@@ -237,58 +197,32 @@ reload(Addr, Port) when is_integer(Port) ->
%%% Mode -> disturbing | non_disturbing
%%% Timeout -> integer()
%%%
-block() -> block(undefined,8888,disturbing).
-block(Port) when is_integer(Port) ->
- block(undefined,Port,disturbing);
+block(Addr, Port, disturbing) when is_integer(Port) ->
+ do_block(Addr, Port, disturbing);
+block(Addr, Port, non_disturbing) when is_integer(Port) ->
+ do_block(Addr, Port, non_disturbing);
-block(ConfigFile) when is_list(ConfigFile) ->
+block(ConfigFile, Mode, Timeout)
+ when is_list(ConfigFile) andalso
+ is_atom(Mode) andalso
+ is_integer(Timeout) ->
case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- block(Addr,Port,disturbing);
- Error ->
- Error
- end.
-
-block(Addr,Port) when is_integer(Port) ->
- block(Addr,Port,disturbing);
-
-block(Port,Mode) when is_integer(Port) andalso is_atom(Mode) ->
- block(undefined,Port,Mode);
-
-block(ConfigFile,Mode) when is_list(ConfigFile) andalso is_atom(Mode) ->
- case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- block(Addr,Port,Mode);
- Error ->
- Error
- end.
-
-
-block(Addr,Port,disturbing) when is_integer(Port) ->
- do_block(Addr,Port,disturbing);
-block(Addr,Port,non_disturbing) when is_integer(Port) ->
- do_block(Addr,Port,non_disturbing);
-
-block(ConfigFile,Mode,Timeout) when is_list(ConfigFile) andalso
- is_atom(Mode) andalso
- is_integer(Timeout) ->
- case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- block(Addr,Port,Mode,Timeout);
+ {ok, Addr, Port} ->
+ block(Addr, Port, Mode, Timeout);
Error ->
Error
end.
-block(Addr,Port,non_disturbing,Timeout)
+block(Addr, Port, non_disturbing, Timeout)
+ when is_integer(Port) andalso is_integer(Timeout) ->
+ do_block(Addr, Port, non_disturbing, Timeout);
+block(Addr,Port,disturbing,Timeout)
when is_integer(Port) andalso is_integer(Timeout) ->
- do_block(Addr,Port,non_disturbing,Timeout);
-block(Addr,Port,disturbing,Timeout) when is_integer(Port) andalso
- is_integer(Timeout) ->
- do_block(Addr,Port,disturbing,Timeout).
+ do_block(Addr, Port, disturbing, Timeout).
-do_block(Addr,Port,Mode) when is_integer(Port) andalso is_atom(Mode) ->
+do_block(Addr, Port, Mode) when is_integer(Port) andalso is_atom(Mode) ->
Name = make_name(Addr,Port),
case whereis(Name) of
Pid when is_pid(Pid) ->
@@ -298,7 +232,7 @@ do_block(Addr,Port,Mode) when is_integer(Port) andalso is_atom(Mode) ->
end.
-do_block(Addr,Port,Mode,Timeout)
+do_block(Addr, Port, Mode, Timeout)
when is_integer(Port) andalso is_atom(Mode) ->
Name = make_name(Addr,Port),
case whereis(Name) of
@@ -310,11 +244,8 @@ do_block(Addr,Port,Mode,Timeout)
%%% =========================================================
-%%% Function: unblock/0, unblock/1, unblock/2
-%%% unblock()
-%%% unblock(Port)
-%%% unblock(ConfigFile)
-%%% unblock(Addr,Port)
+%%% Function: unblock/2
+%%% unblock(Addr, Port)
%%%
%%% Description: This function is used to reverse a previous block
%%% operation on the HTTP server.
@@ -323,16 +254,6 @@ do_block(Addr,Port,Mode,Timeout)
%%% Addr -> {A,B,C,D} | string() | undefined
%%% ConfigFile -> string()
%%%
-unblock() -> unblock(undefined,8888).
-unblock(Port) when is_integer(Port) -> unblock(undefined,Port);
-
-unblock(ConfigFile) when is_list(ConfigFile) ->
- case get_addr_and_port(ConfigFile) of
- {ok,Addr,Port} ->
- unblock(Addr,Port);
- Error ->
- Error
- end.
unblock(Addr, Port) when is_integer(Port) ->
Name = make_name(Addr,Port),
@@ -349,8 +270,8 @@ foreach([KeyValue|Rest]) ->
{ok, Plus2Space, _} = inets_regexp:gsub(KeyValue,"[\+]"," "),
case inets_regexp:split(Plus2Space,"=") of
{ok,[Key|Value]} ->
- [{httpd_util:decode_hex(Key),
- httpd_util:decode_hex(lists:flatten(Value))}|foreach(Rest)];
+ [{http_uri:decode(Key),
+ http_uri:decode(lists:flatten(Value))}|foreach(Rest)];
{ok,_} ->
foreach(Rest)
end.
@@ -521,80 +442,81 @@ do_reload_config(ConfigList, Mode) ->
%%%--------------------------------------------------------------
%%% Deprecated
%%%--------------------------------------------------------------
-start() ->
- start("/var/tmp/server_root/conf/8888.conf").
-start(ConfigFile) ->
- {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone),
- unlink(Pid),
- {ok, Pid}.
+%% start() ->
+%% start("/var/tmp/server_root/conf/8888.conf").
-start_link() ->
- start("/var/tmp/server_root/conf/8888.conf").
+%% start(ConfigFile) ->
+%% {ok, Pid} = inets:start(httpd, ConfigFile, stand_alone),
+%% unlink(Pid),
+%% {ok, Pid}.
-start_link(ConfigFile) when is_list(ConfigFile) ->
- inets:start(httpd, ConfigFile, stand_alone).
+%% start_link() ->
+%% start("/var/tmp/server_root/conf/8888.conf").
-stop() ->
- stop(8888).
+%% start_link(ConfigFile) when is_list(ConfigFile) ->
+%% inets:start(httpd, ConfigFile, stand_alone).
-stop(Port) when is_integer(Port) ->
- stop(undefined, Port);
-stop(Pid) when is_pid(Pid) ->
- old_stop(Pid);
-stop(ConfigFile) when is_list(ConfigFile) ->
- old_stop(ConfigFile).
+%% stop() ->
+%% stop(8888).
-stop(Addr, Port) when is_integer(Port) ->
- old_stop(Addr, Port).
+%% stop(Port) when is_integer(Port) ->
+%% stop(undefined, Port);
+%% stop(Pid) when is_pid(Pid) ->
+%% old_stop(Pid);
+%% stop(ConfigFile) when is_list(ConfigFile) ->
+%% old_stop(ConfigFile).
-start_child() ->
- start_child("/var/tmp/server_root/conf/8888.conf").
+%% stop(Addr, Port) when is_integer(Port) ->
+%% old_stop(Addr, Port).
-start_child(ConfigFile) ->
- httpd_sup:start_child(ConfigFile).
+%% start_child() ->
+%% start_child("/var/tmp/server_root/conf/8888.conf").
-stop_child() ->
- stop_child(8888).
+%% start_child(ConfigFile) ->
+%% httpd_sup:start_child(ConfigFile).
-stop_child(Port) ->
- stop_child(undefined, Port).
+%% stop_child() ->
+%% stop_child(8888).
-stop_child(Addr, Port) when is_integer(Port) ->
- httpd_sup:stop_child(Addr, Port).
+%% stop_child(Port) ->
+%% stop_child(undefined, Port).
-restart() -> reload(undefined, 8888).
+%% stop_child(Addr, Port) when is_integer(Port) ->
+%% httpd_sup:stop_child(Addr, Port).
-restart(Port) when is_integer(Port) ->
- reload(undefined, Port).
-restart(Addr, Port) ->
- reload(Addr, Port).
+%% restart() -> reload(undefined, 8888).
-old_stop(Pid) when is_pid(Pid) ->
- do_stop(Pid);
-old_stop(ConfigFile) when is_list(ConfigFile) ->
- case get_addr_and_port(ConfigFile) of
- {ok, Addr, Port} ->
- old_stop(Addr, Port);
-
- Error ->
- Error
- end;
-old_stop(_StartArgs) ->
- ok.
+%% restart(Port) when is_integer(Port) ->
+%% reload(undefined, Port).
+%% restart(Addr, Port) ->
+%% reload(Addr, Port).
-old_stop(Addr, Port) when is_integer(Port) ->
- Name = old_make_name(Addr, Port),
- case whereis(Name) of
- Pid when is_pid(Pid) ->
- do_stop(Pid),
- ok;
- _ ->
- not_started
- end.
+%% old_stop(Pid) when is_pid(Pid) ->
+%% do_stop(Pid);
+%% old_stop(ConfigFile) when is_list(ConfigFile) ->
+%% case get_addr_and_port(ConfigFile) of
+%% {ok, Addr, Port} ->
+%% old_stop(Addr, Port);
+
+%% Error ->
+%% Error
+%% end;
+%% old_stop(_StartArgs) ->
+%% ok.
+
+%% old_stop(Addr, Port) when is_integer(Port) ->
+%% Name = old_make_name(Addr, Port),
+%% case whereis(Name) of
+%% Pid when is_pid(Pid) ->
+%% do_stop(Pid),
+%% ok;
+%% _ ->
+%% not_started
+%% end.
-do_stop(Pid) ->
- exit(Pid, shutdown).
+%% do_stop(Pid) ->
+%% exit(Pid, shutdown).
-old_make_name(Addr,Port) ->
- httpd_util:make_name("httpd_instance_sup",Addr,Port).
+%% old_make_name(Addr,Port) ->
+%% httpd_util:make_name("httpd_instance_sup",Addr,Port).
diff --git a/lib/inets/src/http_server/httpd.hrl b/lib/inets/src/http_server/httpd.hrl
index 0db8a029bb..4eba833e2c 100644
--- a/lib/inets/src/http_server/httpd.hrl
+++ b/lib/inets/src/http_server/httpd.hrl
@@ -1,82 +1,27 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
-%% %CopyrightEnd%
%%
+%% %CopyrightEnd%
%%
+%% This is a simple wrapper for code that has not been updated to
+%% handle the move of this file to the include dir.
--include_lib("kernel/include/file.hrl").
-
--ifndef(SERVER_SOFTWARE).
--define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile!
--endif.
--define(SERVER_PROTOCOL,"HTTP/1.1").
--define(DEFAULT_MODS, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi,
- mod_dir, mod_get, mod_head, mod_log, mod_disk_log]).
--define(SOCKET_CHUNK_SIZE,8192).
--define(SOCKET_MAX_POLL,25).
--define(FILE_CHUNK_SIZE,64*1024).
--define(GATEWAY_INTERFACE,"CGI/1.1").
--define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
--define(DEFAULT_CONTEXT,
- [{errmsg,"[an error occurred while processing this directive]"},
- {timefmt,"%A, %d-%b-%y %T %Z"},
- {sizefmt,"abbrev"}]).
-
-
--ifdef(inets_error).
--define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(ERROR(F,A),[]).
--endif.
-
--ifdef(inets_log).
--define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(LOG(F,A),[]).
--endif.
-
--ifdef(inets_debug).
--define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(DEBUG(F,A),[]).
--endif.
-
--ifdef(inets_cdebug).
--define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n",
- [self(),?MODULE,?LINE]++Args)).
--else.
--define(CDEBUG(F,A),[]).
--endif.
+-ifndef(src_httpd_hrl).
+-define(src_httpd_hrl, true).
+-include_lib("inets/include/httpd.hrl").
--record(init_data,{peername,resolve}).
--record(mod,{init_data,
- data=[],
- socket_type=ip_comm,
- socket,
- config_db,
- method,
- absolute_uri=[],
- request_uri,
- http_version,
- request_line,
- parsed_header=[],
- entity_body,
- connection}).
+-endif. % -ifdef(src_httpd_hrl).
diff --git a/lib/inets/src/http_server/httpd_acceptor.erl b/lib/inets/src/http_server/httpd_acceptor.erl
index 568fd3c610..bcebb6a9e3 100644
--- a/lib/inets/src/http_server/httpd_acceptor.erl
+++ b/lib/inets/src/http_server/httpd_acceptor.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,6 +21,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
%% Internal application API
-export([start_link/5, start_link/6]).
@@ -138,9 +139,9 @@ acceptor_loop(Manager, SocketType, ListenSocket, ConfigDb, AcceptTimeout) ->
handle_error(Reason, ConfigDb),
?MODULE:acceptor_loop(Manager, SocketType, ListenSocket,
ConfigDb, AcceptTimeout);
- {'EXIT', Reason} ->
- ?hdri("accept exited", [{reason, Reason}]),
- handle_error({'EXIT', Reason}, ConfigDb),
+ {'EXIT', _Reason} = EXIT ->
+ ?hdri("accept exited", [{reason, _Reason}]),
+ handle_error(EXIT, ConfigDb),
?MODULE:acceptor_loop(Manager, SocketType, ListenSocket,
ConfigDb, AcceptTimeout)
end.
diff --git a/lib/inets/src/http_server/httpd_cgi.erl b/lib/inets/src/http_server/httpd_cgi.erl
index 0532d7d100..c06a06aad3 100644
--- a/lib/inets/src/http_server/httpd_cgi.erl
+++ b/lib/inets/src/http_server/httpd_cgi.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,7 +21,8 @@
-export([parse_headers/1, handle_headers/1]).
--include("inets_internal.hrl").
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
%%%=========================================================================
%%% Internal application API
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index 5ca2e47eb5..f4d8a6c09f 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -25,13 +25,15 @@
%% Application internal API
-export([load/1, load/2, load_mime_types/1, store/1, store/2,
- remove/1, remove_all/1, config/1, get_config/2, get_config/3,
- lookup/2, lookup/3, lookup/4,
- validate_properties/1]).
+ remove/1, remove_all/1, get_config/2, get_config/3,
+ lookup_socket_type/1,
+ lookup/2, lookup/3, lookup/4,
+ validate_properties/1]).
-define(VMODULE,"CONF").
--include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("httpd.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
%%%=========================================================================
@@ -216,9 +218,12 @@ load("ServerName " ++ ServerName, []) ->
{ok,[],{server_name,clean(ServerName)}};
load("SocketType " ++ SocketType, []) ->
- case check_enum(clean(SocketType),["ssl","ip_comm"]) of
+ %% ssl is the same as HTTP_DEFAULT_SSL_KIND
+ %% ossl is ssl based on OpenSSL (the "old" ssl)
+ %% essl is the pure Erlang-based ssl (the "new" ssl)
+ case check_enum(clean(SocketType), ["ssl", "ossl", "essl", "ip_comm"]) of
{ok, ValidSocketType} ->
- {ok, [], {socket_type,ValidSocketType}};
+ {ok, [], {socket_type, ValidSocketType}};
{error,_} ->
{error, ?NICE(clean(SocketType) ++ " is an invalid SocketType")}
end;
@@ -226,7 +231,7 @@ load("SocketType " ++ SocketType, []) ->
load("Port " ++ Port, []) ->
case make_integer(Port) of
{ok, Integer} ->
- {ok, [], {port,Integer}};
+ {ok, [], {port, Integer}};
{error, _} ->
{error, ?NICE(clean(Port)++" is an invalid Port")}
end;
@@ -534,7 +539,10 @@ validate_config_params([{server_name, Value} | _]) ->
throw({server_name, Value});
validate_config_params([{socket_type, Value} | Rest])
- when (Value =:= ip_comm) orelse (Value =:= ssl) ->
+ when (Value =:= ip_comm) orelse
+ (Value =:= ssl) orelse
+ (Value =:= ossl) orelse
+ (Value =:= essl) ->
validate_config_params(Rest);
validate_config_params([{socket_type, Value} | _]) ->
throw({socket_type, Value});
@@ -695,6 +703,8 @@ store(ConfigList0) ->
ConfigList)
catch
throw:Error ->
+ ?hdri("store - config parameter validation failed",
+ [{error, Error}]),
{error, {invalid_option, Error}}
end.
@@ -741,27 +751,27 @@ remove(ConfigDB) ->
ets:delete(ConfigDB),
ok.
-config(ConfigDB) ->
- case httpd_util:lookup(ConfigDB, socket_type,ip_comm) of
- ssl ->
- case ssl_certificate_file(ConfigDB) of
- undefined ->
- {error,
- "Directive SSLCertificateFile "
- "not found in the config file"};
- SSLCertificateFile ->
- {ssl,
- SSLCertificateFile++
- ssl_certificate_key_file(ConfigDB)++
- ssl_verify_client(ConfigDB)++
- ssl_ciphers(ConfigDB)++
- ssl_password(ConfigDB)++
- ssl_verify_depth(ConfigDB)++
- ssl_ca_certificate_file(ConfigDB)}
- end;
- ip_comm ->
- ip_comm
- end.
+%% config(ConfigDB) ->
+%% case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
+%% ssl ->
+%% case ssl_certificate_file(ConfigDB) of
+%% undefined ->
+%% {error,
+%% "Directive SSLCertificateFile "
+%% "not found in the config file"};
+%% SSLCertificateFile ->
+%% {ssl,
+%% SSLCertificateFile++
+%% ssl_certificate_key_file(ConfigDB)++
+%% ssl_verify_client(ConfigDB)++
+%% ssl_ciphers(ConfigDB)++
+%% ssl_password(ConfigDB)++
+%% ssl_verify_depth(ConfigDB)++
+%% ssl_ca_certificate_file(ConfigDB)}
+%% end;
+%% ip_comm ->
+%% ip_comm
+%% end.
get_config(Address, Port) ->
@@ -797,6 +807,38 @@ table(Address, Port) ->
httpd_util:make_name("httpd_conf", Address, Port).
+lookup_socket_type(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB, socket_type, ip_comm) of
+ ip_comm ->
+ ip_comm;
+ SSL when (SSL =:= ssl) orelse (SSL =:= ossl) orelse (SSL =:= essl) ->
+ SSLTag =
+ if
+ (SSL =:= ssl) ->
+ ?HTTP_DEFAULT_SSL_KIND;
+ true ->
+ SSL
+ end,
+ case ssl_certificate_file(ConfigDB) of
+ undefined ->
+ Reason = "Directive SSLCertificateFile "
+ "not found in the config file",
+ throw({error, Reason});
+ SSLCertificateFile ->
+ {SSLTag, SSLCertificateFile ++ ssl_config(ConfigDB)}
+ end
+ end.
+
+ssl_config(ConfigDB) ->
+ ssl_certificate_key_file(ConfigDB) ++
+ ssl_verify_client(ConfigDB) ++
+ ssl_ciphers(ConfigDB) ++
+ ssl_password(ConfigDB) ++
+ ssl_verify_depth(ConfigDB) ++
+ ssl_ca_certificate_file(ConfigDB).
+
+
+
%%%========================================================================
%%% Internal functions
%%%========================================================================
diff --git a/lib/inets/src/http_server/httpd_esi.erl b/lib/inets/src/http_server/httpd_esi.erl
index b1a75fda52..026ec9a5fe 100644
--- a/lib/inets/src/http_server/httpd_esi.erl
+++ b/lib/inets/src/http_server/httpd_esi.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,7 +21,8 @@
-export([parse_headers/1, handle_headers/1]).
--include("inets_internal.hrl").
+-include_lib("inets/src/inets_app/inets_internal.hrl").
+
%%%=========================================================================
%%% Internal application API
diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl
index 5fd529100e..7e21d9e158 100644
--- a/lib/inets/src/http_server/httpd_file.erl
+++ b/lib/inets/src/http_server/httpd_file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,11 +22,13 @@
-export([handle_error/4]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
+
handle_error(eacces, Op, ModData, Path) ->
- handle_error(403, Op, ModData, Path,"");
+ handle_error(403, Op, ModData, Path,"Forbidden");
handle_error(enoent, Op, ModData, Path) ->
- handle_error(404, Op, ModData, Path,"");
+ handle_error(404, Op, ModData, Path,"File not found");
handle_error(enotdir, Op, ModData, Path) ->
handle_error(404, Op, ModData, Path,
": A component of the file name is not a directory");
@@ -34,8 +36,8 @@ handle_error(emfile, Op, _ModData, Path) ->
handle_error(500, Op, none, Path, ": To many open files");
handle_error({enfile,_}, Op, _ModData, Path) ->
handle_error(500, Op, none, Path, ": File table overflow");
-handle_error(_Reason, Op, _ModData, Path) ->
- handle_error(500, Op, none, Path, "").
+handle_error(_Reason, Op, ModData, Path) ->
+ handle_error(404, Op, ModData, Path, "File not found").
handle_error(StatusCode, Op, none, Path, Reason) ->
{StatusCode, none, ?NICE("Can't " ++ Op ++ Path ++ Reason)};
diff --git a/lib/inets/src/http_server/httpd_instance_sup.erl b/lib/inets/src/http_server/httpd_instance_sup.erl
index 0aaeb838c2..baa60d318c 100644
--- a/lib/inets/src/http_server/httpd_instance_sup.erl
+++ b/lib/inets/src/http_server/httpd_instance_sup.erl
@@ -97,14 +97,16 @@ start_link(ConfigFile, AcceptTimeout, ListenInfo, Debug) ->
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-init([ConfigFile, ConfigList, AcceptTimeout, _Debug, Address, Port]) ->
+init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port]) ->
+ httpd_util:enable_debug(Debug),
Flags = {one_for_one, 0, 1},
Children = [sup_spec(httpd_acceptor_sup, Address, Port),
sup_spec(httpd_misc_sup, Address, Port),
worker_spec(httpd_manager, Address, Port,
ConfigFile, ConfigList,AcceptTimeout)],
{ok, {Flags, Children}};
-init([ConfigFile, ConfigList, AcceptTimeout, _Debug, Address, Port, ListenInfo]) ->
+init([ConfigFile, ConfigList, AcceptTimeout, Debug, Address, Port, ListenInfo]) ->
+ httpd_util:enable_debug(Debug),
Flags = {one_for_one, 0, 1},
Children = [sup_spec(httpd_acceptor_sup, Address, Port),
sup_spec(httpd_misc_sup, Address, Port),
diff --git a/lib/inets/src/http_server/httpd_internal.hrl b/lib/inets/src/http_server/httpd_internal.hrl
index 7795ab6c18..108469ea0a 100644
--- a/lib/inets/src/http_server/httpd_internal.hrl
+++ b/lib/inets/src/http_server/httpd_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,7 +21,51 @@
-ifndef(httpd_internal_hrl).
-define(httpd_internal_hrl, true).
--include("inets_internal.hrl").
+-ifndef(SERVER_SOFTWARE).
+-define(SERVER_SOFTWARE,"inets/develop"). % Define in Makefile!
+-endif.
+-define(SERVER_PROTOCOL,"HTTP/1.1").
+-define(DEFAULT_MODS, [mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi,
+ mod_dir, mod_get, mod_head, mod_log, mod_disk_log]).
+-define(SOCKET_CHUNK_SIZE,8192).
+-define(SOCKET_MAX_POLL,25).
+-define(FILE_CHUNK_SIZE,64*1024).
+-define(GATEWAY_INTERFACE,"CGI/1.1").
+-define(NICE(Reason),lists:flatten(atom_to_list(?MODULE)++": "++Reason)).
+-define(DEFAULT_CONTEXT,
+ [{errmsg,"[an error occurred while processing this directive]"},
+ {timefmt,"%A, %d-%b-%y %T %Z"},
+ {sizefmt,"abbrev"}]).
+
+
+-ifdef(inets_error).
+-define(ERROR(Format, Args), io:format("E(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(ERROR(F,A),[]).
+-endif.
+
+-ifdef(inets_log).
+-define(LOG(Format, Args), io:format("L(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(LOG(F,A),[]).
+-endif.
+
+-ifdef(inets_debug).
+-define(DEBUG(Format, Args), io:format("D(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(DEBUG(F,A),[]).
+-endif.
+
+-ifdef(inets_cdebug).
+-define(CDEBUG(Format, Args), io:format("C(~p:~p:~p) : "++Format++"~n",
+ [self(),?MODULE,?LINE]++Args)).
+-else.
+-define(CDEBUG(F,A),[]).
+-endif.
+
-define(SERVICE, httpd).
-define(hdri(Label, Content), ?report_important(Label, ?SERVICE, Content)).
-define(hdrv(Label, Content), ?report_verbose(Label, ?SERVICE, Content)).
diff --git a/lib/inets/src/http_server/httpd_manager.erl b/lib/inets/src/http_server/httpd_manager.erl
index f2e8763907..b44bc77c41 100644
--- a/lib/inets/src/http_server/httpd_manager.erl
+++ b/lib/inets/src/http_server/httpd_manager.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -238,24 +238,25 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port]) ->
case (catch do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port)) of
{error, Reason} ->
String = lists:flatten(
- io_lib:format("Failed initiating "
- "web server: ~n~p~n~p~n",
- [ConfigFile,Reason])),
+ io_lib:format("Failed initiating web server: "
+ "~n~p"
+ "~n~p"
+ "~n", [ConfigFile, Reason])),
error_logger:error_report(String),
{stop, {error, Reason}};
{ok, State} ->
{ok, State}
end;
-init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port,
- ListenInfo]) ->
+init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo]) ->
process_flag(trap_exit, true),
case (catch do_init(ConfigFile, ConfigList, AcceptTimeout,
Addr, Port, ListenInfo)) of
{error, Reason} ->
String = lists:flatten(
- io_lib:format("Failed initiating "
- "web server: ~n~p~n~p~n",
- [ConfigFile,Reason])),
+ io_lib:format("Failed initiating web server: "
+ "~n~p"
+ "~n~p"
+ "~n", [ConfigFile, Reason])),
error_logger:error_report(String),
{stop, {error, Reason}};
{ok, State} ->
@@ -264,13 +265,14 @@ init([ConfigFile, ConfigList, AcceptTimeout, Addr, Port,
do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) ->
NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
- ConfigDB = do_initial_store(ConfigList),
- SocketType = httpd_conf:config(ConfigDB),
+ ConfigDB = do_initial_store(ConfigList),
+ SocketType = httpd_conf:lookup_socket_type(ConfigDB),
case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
Port, ConfigDB, AcceptTimeout) of
{ok, _Pid} ->
- Status = [{max_conn,0}, {last_heavy_load,never},
- {last_connection,never}],
+ Status = [{max_conn, 0},
+ {last_heavy_load, never},
+ {last_connection, never}],
State = #state{socket_type = SocketType,
config_file = NewConfigFile,
config_db = ConfigDB,
@@ -284,7 +286,7 @@ do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port) ->
do_init(ConfigFile, ConfigList, AcceptTimeout, Addr, Port, ListenInfo) ->
NewConfigFile = proplists:get_value(file, ConfigList, ConfigFile),
ConfigDB = do_initial_store(ConfigList),
- SocketType = httpd_conf:config(ConfigDB),
+ SocketType = httpd_conf:lookup_socket_type(ConfigDB),
case httpd_acceptor_sup:start_acceptor(SocketType, Addr,
Port, ConfigDB,
AcceptTimeout, ListenInfo) of
diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl
index 8eee08e766..7084d9824a 100644
--- a/lib/inets/src/http_server/httpd_request.erl
+++ b/lib/inets/src/http_server/httpd_request.erl
@@ -19,22 +19,35 @@
-module(httpd_request).
--include("http_internal.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
-include("httpd.hrl").
+-include("httpd_internal.hrl").
--export([parse/1, whole_body/2, validate/3, update_mod_data/5,
- body_data/2]).
+-export([
+ parse/1,
+ whole_body/2,
+ validate/3,
+ update_mod_data/5,
+ body_data/2
+ ]).
%% Callback API - used for example if the header/body is received a
%% little at a time on a socket.
--export([parse_method/1, parse_uri/1, parse_version/1, parse_headers/1,
- whole_body/1]).
+-export([
+ parse_method/1, parse_uri/1, parse_version/1, parse_headers/1,
+ whole_body/1
+ ]).
+
%%%=========================================================================
%%% Internal application API
%%%=========================================================================
parse([Bin, MaxSizes]) ->
- parse_method(Bin, [], MaxSizes, []).
+ ?hdrt("parse", [{bin, Bin}, {max_sizes, MaxSizes}]),
+ parse_method(Bin, [], MaxSizes, []);
+parse(Unknown) ->
+ ?hdrt("parse", [{unknown, Unknown}]),
+ exit({bad_args, Unknown}).
%% Functions that may be returned during the decoding process
%% if the input data is incompleate.
@@ -119,30 +132,65 @@ update_mod_data(ModData, Method, RequestURI, HTTPVersion, Headers)->
%%% Internal functions
%%%========================================================================
parse_method(<<>>, Method, MaxSizes, Result) ->
+ ?hdrt("parse_method - empty bin",
+ [{method, Method}, {max_sizes, MaxSizes}, {result, Result}]),
{?MODULE, parse_method, [Method, MaxSizes, Result]};
parse_method(<<?SP, Rest/binary>>, Method, MaxSizes, Result) ->
+ ?hdrt("parse_method - SP begin",
+ [{rest, Rest},
+ {method, Method},
+ {max_sizes, MaxSizes},
+ {result, Result}]),
parse_uri(Rest, [], 0, MaxSizes,
[string:strip(lists:reverse(Method)) | Result]);
parse_method(<<Octet, Rest/binary>>, Method, MaxSizes, Result) ->
+ ?hdrt("parse_method",
+ [{octet, Octet},
+ {rest, Rest},
+ {method, Method},
+ {max_sizes, MaxSizes},
+ {result, Result}]),
parse_method(Rest, [Octet | Method], MaxSizes, Result).
-parse_uri(_, _, CurrSize, {MaxURI, _}, _) when CurrSize > MaxURI,
- MaxURI =/= nolimit ->
+parse_uri(_, _, CurrSize, {MaxURI, _}, _)
+ when (CurrSize > MaxURI) andalso (MaxURI =/= nolimit) ->
+ ?hdrt("parse_uri",
+ [{current_size, CurrSize},
+ {max_uri, MaxURI}]),
%% We do not know the version of the client as it comes after the
%% uri send the lowest version in the response so that the client
%% will be able to handle it.
HttpVersion = "HTTP/0.9",
{error, {uri_too_long, MaxURI}, HttpVersion};
parse_uri(<<>>, URI, CurrSize, MaxSizes, Result) ->
+ ?hdrt("parse_uri - empty bin",
+ [{uri, URI},
+ {current_size, CurrSize},
+ {max_sz, MaxSizes},
+ {result, Result}]),
{?MODULE, parse_uri, [URI, CurrSize, MaxSizes, Result]};
parse_uri(<<?SP, Rest/binary>>, URI, _, MaxSizes, Result) ->
+ ?hdrt("parse_uri - SP begin",
+ [{uri, URI},
+ {max_sz, MaxSizes},
+ {result, Result}]),
parse_version(Rest, [], MaxSizes,
[string:strip(lists:reverse(URI)) | Result]);
%% Can happen if it is a simple HTTP/0.9 request e.i "GET /\r\n\r\n"
-parse_uri(<<?CR, _Rest/binary>> = Data, URI, _,MaxSizes, Result) ->
+parse_uri(<<?CR, _Rest/binary>> = Data, URI, _, MaxSizes, Result) ->
+ ?hdrt("parse_uri - CR begin",
+ [{uri, URI},
+ {max_sz, MaxSizes},
+ {result, Result}]),
parse_version(Data, [], MaxSizes,
[string:strip(lists:reverse(URI)) | Result]);
parse_uri(<<Octet, Rest/binary>>, URI, CurrSize, MaxSizes, Result) ->
+ ?hdrt("parse_uri",
+ [{octet, Octet},
+ {uri, URI},
+ {curr_sz, CurrSize},
+ {max_sz, MaxSizes},
+ {result, Result}]),
parse_uri(Rest, [Octet | URI], CurrSize + 1, MaxSizes, Result).
parse_version(<<>>, Version, MaxSizes, Result) ->
@@ -256,9 +304,9 @@ validate_uri(RequestURI) ->
UriNoQueryNoHex =
case string:str(RequestURI, "?") of
0 ->
- (catch httpd_util:decode_hex(RequestURI));
+ (catch http_uri:decode(RequestURI));
Ndx ->
- (catch httpd_util:decode_hex(string:left(RequestURI, Ndx)))
+ (catch http_uri:decode(string:left(RequestURI, Ndx)))
end,
case UriNoQueryNoHex of
{'EXIT',_Reason} ->
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index fa832cba3f..a9db6e2058 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -101,11 +101,13 @@ init([Manager, ConfigDB, AcceptTimeout]) ->
Then = erlang:now(),
+ ?hdrd("negotiate", []),
case http_transport:negotiate(SocketType, Socket, TimeOut) of
{error, Error} ->
+ ?hdrd("negotiation failed", [{error, Error}]),
exit(Error); %% Can be 'normal'.
ok ->
- ?hdrt("negotiated", []),
+ ?hdrt("negotiation successfull", []),
NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000,
continue_init(Manager, ConfigDB, SocketType, Socket, NewTimeout)
end.
@@ -121,12 +123,9 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
socket = Socket,
init_data = InitData},
- MaxHeaderSize = httpd_util:lookup(ConfigDB, max_header_size,
- ?HTTP_MAX_HEADER_SIZE),
- MaxURISize = httpd_util:lookup(ConfigDB, max_uri_size,
- ?HTTP_MAX_URI_SIZE),
- NrOfRequest = httpd_util:lookup(ConfigDB,
- max_keep_alive_request, infinity),
+ MaxHeaderSize = max_header_size(ConfigDB),
+ MaxURISize = max_uri_size(ConfigDB),
+ NrOfRequest = max_keep_alive_request(ConfigDB),
{_, Status} = httpd_manager:new_connection(Manager),
@@ -142,9 +141,10 @@ continue_init(Manager, ConfigDB, SocketType, Socket, TimeOut) ->
?hdrt("activate request timeout", []),
NewState = activate_request_timeout(State),
- ?hdrt("update socket options", []),
- http_transport:setopts(SocketType, Socket, [binary,{packet, 0},
- {active, once}]),
+ ?hdrt("set socket options (binary, packet & active)", []),
+ http_transport:setopts(SocketType, Socket,
+ [binary, {packet, 0}, {active, once}]),
+
?hdrt("init done", []),
gen_server:enter_loop(?MODULE, [], NewState).
@@ -180,21 +180,29 @@ handle_cast(Msg, State) ->
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
-handle_info({Proto, Socket, Data}, State =
+handle_info({Proto, Socket, Data},
#state{mfa = {Module, Function, Args} = MFA,
mod = #mod{socket_type = SockType,
socket = Socket} = ModData} = State)
when (((Proto =:= tcp) orelse
(Proto =:= ssl) orelse
(Proto =:= dummy)) andalso is_binary(Data)) ->
+
?hdrd("received data",
[{data, Data}, {proto, Proto},
{socket, Socket}, {socket_type, SockType}, {mfa, MFA}]),
- case Module:Function([Data | Args]) of
+
+%% case (catch Module:Function([Data | Args])) of
+ PROCESSED = (catch Module:Function([Data | Args])),
+
+ ?hdrt("data processed", [{processing_result, PROCESSED}]),
+
+ case PROCESSED of
{ok, Result} ->
?hdrd("data processed", [{result, Result}]),
NewState = cancel_request_timeout(State),
handle_http_msg(Result, NewState);
+
{error, {uri_too_long, MaxSize}, Version} ->
?hdrv("uri too long", [{max_size, MaxSize}, {version, Version}]),
NewModData = ModData#mod{http_version = Version},
@@ -205,7 +213,8 @@ handle_info({Proto, Socket, Data}, State =
{stop, normal, State#state{response_sent = true,
mod = NewModData}};
{error, {header_too_long, MaxSize}, Version} ->
- ?hdrv("header too long", [{max_size, MaxSize}, {version, Version}]),
+ ?hdrv("header too long",
+ [{max_size, MaxSize}, {version, Version}]),
NewModData = ModData#mod{http_version = Version},
httpd_response:send_status(NewModData, 413, "Header too long"),
Reason = io_lib:format("Header too long, max size is ~p~n",
@@ -263,14 +272,16 @@ terminate(Reason, #state{response_sent = false, mod = ModData} = State) ->
httpd_response:send_status(ModData, 500, none),
error_log(httpd_util:reason_phrase(500), ModData),
terminate(Reason, State#state{response_sent = true, mod = ModData});
-terminate(_, State) ->
+terminate(_Reason, State) ->
do_terminate(State).
do_terminate(#state{mod = ModData, manager = Manager} = State) ->
catch httpd_manager:done_connection(Manager),
cancel_request_timeout(State),
+ %% receive after 5000 -> ok end,
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket).
+
%%--------------------------------------------------------------------
%% code_change(OldVsn, State, Extra) -> {ok, NewState}
%%
@@ -279,6 +290,7 @@ do_terminate(#state{mod = ModData, manager = Manager} = State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -383,9 +395,8 @@ is_host_specified_if_required(_, _, _) ->
handle_body(#state{mod = #mod{config_db = ConfigDB}} = State) ->
?hdrt("handle body", []),
- MaxHeaderSize =
- httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE),
- MaxBodySize = httpd_util:lookup(ConfigDB, max_body_size, nolimit),
+ MaxHeaderSize = max_header_size(ConfigDB),
+ MaxBodySize = max_body_size(ConfigDB),
case handle_expect(State, MaxBodySize) of
ok ->
@@ -538,24 +549,23 @@ handle_response(#state{body = Body,
{stop, normal, State#state{response_sent = true}}.
handle_next_request(#state{mod = #mod{connection = true} = ModData,
- max_keep_alive_request = Max} = State, Data) ->
+ max_keep_alive_request = Max} = State, Data) ->
?hdrt("handle next request", [{max, Max}]),
+
NewModData = #mod{socket_type = ModData#mod.socket_type,
- socket = ModData#mod.socket,
- config_db = ModData#mod.config_db,
- init_data = ModData#mod.init_data},
- MaxHeaderSize =
- httpd_util:lookup(ModData#mod.config_db,
- max_header_size, ?HTTP_MAX_HEADER_SIZE),
- MaxURISize = httpd_util:lookup(ModData#mod.config_db, max_uri_size,
- ?HTTP_MAX_URI_SIZE),
- TmpState = State#state{mod = NewModData,
- mfa = {httpd_request, parse, [{MaxURISize,
- MaxHeaderSize}]},
+ socket = ModData#mod.socket,
+ config_db = ModData#mod.config_db,
+ init_data = ModData#mod.init_data},
+ MaxHeaderSize = max_header_size(ModData#mod.config_db),
+ MaxURISize = max_uri_size(ModData#mod.config_db),
+
+ MFA = {httpd_request, parse, [{MaxURISize, MaxHeaderSize}]},
+ TmpState = State#state{mod = NewModData,
+ mfa = MFA,
max_keep_alive_request = decrease(Max),
- headers = undefined,
- body = undefined,
- response_sent = false},
+ headers = undefined,
+ body = undefined,
+ response_sent = false},
NewState = activate_request_timeout(TmpState),
@@ -596,7 +606,7 @@ decrease(N) ->
error_log(ReasonString, Info) ->
Error = lists:flatten(
- io_lib:format("Error reading request:~s",[ReasonString])),
+ io_lib:format("Error reading request: ~s", [ReasonString])),
error_log(mod_log, Info, Error),
error_log(mod_disk_log, Info, Error).
@@ -609,3 +619,21 @@ error_log(Mod, #mod{config_db = ConfigDB} = Info, String) ->
_ ->
ok
end.
+
+
+%%--------------------------------------------------------------------
+%% Config access wrapper functions
+%%--------------------------------------------------------------------
+
+max_header_size(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, max_header_size, ?HTTP_MAX_HEADER_SIZE).
+
+max_uri_size(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, max_uri_size, ?HTTP_MAX_URI_SIZE).
+
+max_body_size(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, max_body_size, nolimit).
+
+max_keep_alive_request(ConfigDB) ->
+ httpd_util:lookup(ConfigDB, max_keep_alive_request, infinity).
+
diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl
index a742cbef76..d3115150b0 100644
--- a/lib/inets/src/http_server/httpd_script_env.erl
+++ b/lib/inets/src/http_server/httpd_script_env.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,7 @@
-export([create_env/3]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%%%=========================================================================
%%% Internal application API
diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl
index 3399f78b53..f94e5459c1 100644
--- a/lib/inets/src/http_server/httpd_sup.erl
+++ b/lib/inets/src/http_server/httpd_sup.erl
@@ -37,7 +37,7 @@
-define(TIMEOUT, 15000).
-include("httpd_internal.hrl").
-
+-include("inets_internal.hrl").
%%%=========================================================================
%%% API
@@ -185,14 +185,14 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) ->
httpd_child_spec(Config, AcceptTimeout, Debug, Addr, 0) ->
case start_listen(Addr, 0, Config) of
{Pid, {NewPort, NewConfig, ListenSocket}} ->
- Name = {httpd_instance_sup, Addr, NewPort},
+ Name = {httpd_instance_sup, Addr, NewPort},
StartFunc = {httpd_instance_sup, start_link,
[NewConfig, AcceptTimeout,
{Pid, ListenSocket}, Debug]},
- Restart = permanent,
- Shutdown = infinity,
- Modules = [httpd_instance_sup],
- Type = supervisor,
+ Restart = permanent,
+ Shutdown = infinity,
+ Modules = [httpd_instance_sup],
+ Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules};
{Pid, {error, Reason}} ->
exit(Pid, normal),
diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl
index b59fd861dc..789f12652b 100644
--- a/lib/inets/src/http_server/httpd_util.erl
+++ b/lib/inets/src/http_server/httpd_util.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,7 +21,7 @@
-export([ip_address/2, lookup/2, lookup/3, multi_lookup/2,
lookup_mime/2, lookup_mime/3, lookup_mime_default/2,
lookup_mime_default/3, reason_phrase/1, message/3, rfc1123_date/0,
- rfc1123_date/1, day/1, month/1, decode_hex/1,
+ rfc1123_date/1, day/1, month/1,
flatlength/1, split_path/1, split_script_path/1,
suffix/1, split/3, uniq/1,
make_name/2,make_name/3,make_name/4,strip/1,
@@ -32,7 +32,7 @@
dir_validate/2, file_validate/2, mime_type_validate/1,
mime_types_validate/1, custom_date/0]).
--export([encode_hex/1]).
+-export([encode_hex/1, decode_hex/1]).
-include_lib("kernel/include/file.hrl").
ip_address({_,_,_,_} = Address, _IpFamily) ->
@@ -175,13 +175,13 @@ reason_phrase(_) -> "Internal Server Error".
%% message
message(301,URL,_) ->
- "The document has moved <A HREF=\""++URL++"\">here</A>.";
+ "The document has moved <A HREF=\""++ maybe_encode(URL) ++"\">here</A>.";
message(304, _URL,_) ->
"The document has not been changed.";
message(400,none,_) ->
"Your browser sent a query that this server could not understand.";
message(400,Msg,_) ->
- "Your browser sent a query that this server could not understand. "++Msg;
+ "Your browser sent a query that this server could not understand. "++ maybe_encode(Msg);
message(401,none,_) ->
"This server could not verify that you
are authorized to access the document you
@@ -190,9 +190,9 @@ credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.";
message(403,RequestURI,_) ->
- "You don't have permission to access "++RequestURI++" on this server.";
+ "You don't have permission to access "++ maybe_encode(RequestURI) ++" on this server.";
message(404,RequestURI,_) ->
- "The requested URL "++RequestURI++" was not found on this server.";
+ "The requested URL " ++ maybe_encode(RequestURI) ++ " was not found on this server.";
message(408, Timeout, _) ->
Timeout;
message(412,none,_) ->
@@ -200,7 +200,7 @@ message(412,none,_) ->
message(413, Reason,_) ->
"Entity: " ++ Reason;
message(414,ReasonPhrase,_) ->
- "Message "++ReasonPhrase++".";
+ "Message "++ ReasonPhrase ++".";
message(416,ReasonPhrase,_) ->
ReasonPhrase;
@@ -216,15 +216,23 @@ message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) ->
if
is_atom(Method) ->
atom_to_list(Method)++
- " to "++RequestURI++" ("++HTTPVersion++") not supported.";
+ " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported.";
is_list(Method) ->
Method++
- " to "++RequestURI++" ("++HTTPVersion++") not supported."
+ " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported."
end;
message(503, String, _ConfigDB) ->
"This service in unavailable due to: "++String.
+maybe_encode(URI) ->
+ case lists:member($%, URI) of
+ true ->
+ URI;
+ false ->
+ http_uri:encode(URI)
+ end.
+
%%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}}
convert_request_date([D,A,Y,DateType| Rest])->
@@ -381,16 +389,11 @@ month(12) -> "Dec".
%% decode_hex
-decode_hex([$%,Hex1,Hex2|Rest]) ->
- [hex2dec(Hex1)*16+hex2dec(Hex2)|decode_hex(Rest)];
-decode_hex([First|Rest]) ->
- [First|decode_hex(Rest)];
-decode_hex([]) ->
- [].
+decode_hex(URI) ->
+ http_uri:decode(URI).
-hex2dec(X) when (X>=$0) andalso (X=<$9) -> X-$0;
-hex2dec(X) when (X>=$A) andalso (X=<$F) -> X-$A+10;
-hex2dec(X) when (X>=$a) andalso (X=<$f) -> X-$a+10.
+encode_hex(URI) ->
+ http_uri:encode(URI).
%% flatlength
flatlength(List) ->
@@ -411,7 +414,7 @@ split_path(Path) ->
case inets_regexp:match(Path,"[\?].*\$") of
%% A QUERY_STRING exists!
{match,Start,Length} ->
- {httpd_util:decode_hex(string:substr(Path,1,Start-1)),
+ {http_uri:decode(string:substr(Path,1,Start-1)),
string:substr(Path,Start,Length)};
%% A possible PATH_INFO exists!
nomatch ->
@@ -419,9 +422,9 @@ split_path(Path) ->
end.
split_path([],SoFar) ->
- {httpd_util:decode_hex(lists:reverse(SoFar)),[]};
+ {http_uri:decode(lists:reverse(SoFar)),[]};
split_path([$/|Rest],SoFar) ->
- Path=httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path=http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path,[$/|Rest]};
@@ -454,7 +457,7 @@ pathinfo_querystring([C|Rest], SoFar) ->
pathinfo_querystring(Rest, [C|SoFar]).
split_script_path([$?|QueryString], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, [$?|QueryString]};
@@ -464,7 +467,7 @@ split_script_path([$?|QueryString], SoFar) ->
not_a_script
end;
split_script_path([], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok,FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, []};
@@ -474,7 +477,7 @@ split_script_path([], SoFar) ->
not_a_script
end;
split_script_path([$/|Rest], SoFar) ->
- Path = httpd_util:decode_hex(lists:reverse(SoFar)),
+ Path = http_uri:decode(lists:reverse(SoFar)),
case file:read_file_info(Path) of
{ok, FileInfo} when FileInfo#file_info.type =:= regular ->
{Path, [$/|Rest]};
@@ -608,9 +611,6 @@ hexlist_to_integer(List)->
%%----------------------------------------------------------------------
%%Converts an integer to an hexlist
%%----------------------------------------------------------------------
-encode_hex(Num)->
- integer_to_hexlist(Num).
-
integer_to_hexlist(Num) when is_integer(Num) ->
http_util:integer_to_hexlist(Num).
@@ -735,7 +735,6 @@ valid_accept_timeout(A) ->
valid_config(_) ->
ok.
-
%%----------------------------------------------------------------------
%% Enable debugging,
%%----------------------------------------------------------------------
@@ -755,23 +754,18 @@ do_enable_debug([{Level,Modules}|Rest])
when is_atom(Level) andalso is_list(Modules) ->
case Level of
all_functions ->
- io:format("Tracing on all functions set on modules: ~p~n",
- [Modules]),
lists:foreach(
- fun(X)->
+ fun(X) ->
dbg:tpl(X, [{'_', [], [{return_trace}]}])
end, Modules);
exported_functions ->
- io:format("Tracing on exported functions set on "
- "modules: ~p~n",[Modules]),
lists:foreach(
- fun(X)->
+ fun(X) ->
dbg:tp(X, [{'_', [], [{return_trace}]}])
end, Modules);
disable ->
- io:format("Tracing disabled on modules: ~p~n", [Modules]),
lists:foreach(
- fun(X)->
+ fun(X) ->
dbg:ctp(X)
end, Modules);
_ ->
diff --git a/lib/inets/src/http_server/mod_actions.erl b/lib/inets/src/http_server/mod_actions.erl
index d50ed4b16c..c3946ff9b4 100644
--- a/lib/inets/src/http_server/mod_actions.erl
+++ b/lib/inets/src/http_server/mod_actions.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
-export([do/1,load/2, store/2]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%% do
diff --git a/lib/inets/src/http_server/mod_alias.erl b/lib/inets/src/http_server/mod_alias.erl
index ec0a12242f..0b9fe4cfe0 100644
--- a/lib/inets/src/http_server/mod_alias.erl
+++ b/lib/inets/src/http_server/mod_alias.erl
@@ -29,6 +29,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"ALIAS").
@@ -103,6 +104,19 @@ real_name(ConfigDB, RequestURI, []) ->
httpd_util:split_path(default_index(ConfigDB, RealName)),
{ShortPath, Path, AfterPath};
+real_name(ConfigDB, RequestURI, [{MP,Replacement}|Rest])
+ when element(1, MP) =:= re_pattern ->
+ case re:run(RequestURI, MP, [{capture,[]}]) of
+ match ->
+ NewURI = re:replace(RequestURI, MP, Replacement, [{return,list}]),
+ {ShortPath,_} = httpd_util:split_path(NewURI),
+ {Path,AfterPath} =
+ httpd_util:split_path(default_index(ConfigDB, NewURI)),
+ {ShortPath, Path, AfterPath};
+ nomatch ->
+ real_name(ConfigDB, RequestURI, Rest)
+ end;
+
real_name(ConfigDB, RequestURI, [{FakeName,RealName}|Rest]) ->
case inets_regexp:match(RequestURI, "^" ++ FakeName) of
{match, _, _} ->
@@ -120,6 +134,18 @@ real_name(ConfigDB, RequestURI, [{FakeName,RealName}|Rest]) ->
real_script_name(_ConfigDB, _RequestURI, []) ->
not_a_script;
+
+real_script_name(ConfigDB, RequestURI, [{MP,Replacement} | Rest])
+ when element(1, MP) =:= re_pattern ->
+ case re:run(RequestURI, MP, [{capture,[]}]) of
+ match ->
+ ActualName =
+ re:replace(RequestURI, MP, Replacement, [{return,list}]),
+ httpd_util:split_script_path(default_index(ConfigDB, ActualName));
+ nomatch ->
+ real_script_name(ConfigDB, RequestURI, Rest)
+ end;
+
real_script_name(ConfigDB, RequestURI, [{FakeName,RealName} | Rest]) ->
case inets_regexp:match(RequestURI, "^" ++ FakeName) of
{match,_,_} ->
@@ -180,6 +206,8 @@ load("Alias " ++ Alias, []) ->
{ok, _} ->
{error,?NICE(httpd_conf:clean(Alias)++" is an invalid Alias")}
end;
+load("ReWrite " ++ Rule, Acc) ->
+ load_re_write(Rule, Acc, "ReWrite", re_write);
load("ScriptAlias " ++ ScriptAlias, []) ->
case inets_regexp:split(ScriptAlias, " ") of
{ok, [FakeName, RealName]} ->
@@ -189,6 +217,24 @@ load("ScriptAlias " ++ ScriptAlias, []) ->
{ok, _} ->
{error, ?NICE(httpd_conf:clean(ScriptAlias)++
" is an invalid ScriptAlias")}
+ end;
+load("ScriptReWrite " ++ Rule, Acc) ->
+ load_re_write(Rule, Acc, "ScriptReWrite", script_re_write).
+
+load_re_write(Rule0, Acc, Type, Tag) ->
+ case lists:dropwhile(
+ fun ($\s) -> true; ($\t) -> true; (_) -> false end,
+ Rule0) of
+ "" ->
+ {error, ?NICE(httpd_conf:clean(Rule0)++" is an invalid "++Type)};
+ Rule ->
+ case string:chr(Rule, $\s) of
+ 0 ->
+ {ok, Acc, {Tag, {Rule, ""}}};
+ N ->
+ {Re, [_|Replacement]} = lists:split(N-1, Rule),
+ {ok, Acc, {Tag, {Re, Replacement}}}
+ end
end.
store({directory_index, Value} = Conf, _) when is_list(Value) ->
@@ -200,16 +246,36 @@ store({directory_index, Value} = Conf, _) when is_list(Value) ->
end;
store({directory_index, Value}, _) ->
{error, {wrong_type, {directory_index, Value}}};
-store({alias, {Fake, Real}} = Conf, _)
- when is_list(Fake) andalso is_list(Real) ->
+store({alias, {Fake, Real}} = Conf, _)
+ when is_list(Fake), is_list(Real) ->
{ok, Conf};
store({alias, Value}, _) ->
{error, {wrong_type, {alias, Value}}};
+store({re_write, {Re, Replacement}} = Conf, _)
+ when is_list(Re), is_list(Replacement) ->
+ case re:compile(Re) of
+ {ok, MP} ->
+ {ok, {alias, {MP, Replacement}}};
+ {error,_} ->
+ {error, {re_compile, Conf}}
+ end;
+store({re_write, _} = Conf, _) ->
+ {error, {wrong_type, Conf}};
store({script_alias, {Fake, Real}} = Conf, _)
- when is_list(Fake) andalso is_list(Real) ->
+ when is_list(Fake), is_list(Real) ->
{ok, Conf};
store({script_alias, Value}, _) ->
- {error, {wrong_type, {script_alias, Value}}}.
+ {error, {wrong_type, {script_alias, Value}}};
+store({script_re_write, {Re, Replacement}} = Conf, _)
+ when is_list(Re), is_list(Replacement) ->
+ case re:compile(Re) of
+ {ok, MP} ->
+ {ok, {script_alias, {MP, Replacement}}};
+ {error,_} ->
+ {error, {re_compile, Conf}}
+ end;
+store({script_re_write, _} = Conf, _) ->
+ {error, {wrong_type, Conf}}.
is_directory_index_list([]) ->
true;
diff --git a/lib/inets/src/http_server/mod_auth.erl b/lib/inets/src/http_server/mod_auth.erl
index 07cafb4726..85a87ab884 100644
--- a/lib/inets/src/http_server/mod_auth.erl
+++ b/lib/inets/src/http_server/mod_auth.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,6 +38,7 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"AUTH").
diff --git a/lib/inets/src/http_server/mod_auth.hrl b/lib/inets/src/http_server/mod_auth.hrl
index 9b316cecc4..674e6d1652 100644
--- a/lib/inets/src/http_server/mod_auth.hrl
+++ b/lib/inets/src/http_server/mod_auth.hrl
@@ -1,29 +1,27 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
-%% %CopyrightEnd%
%%
+%% %CopyrightEnd%
%%
+%% This is a simple wrapper for code that has not been updated to
+%% handle the move of this file to the include dir.
+
+-ifndef(src_mod_auth_hrl).
+-define(src_mod_auth_hrl, true).
--record(httpd_user,
- {username,
- password,
- user_data}).
+-include_lib("inets/include/mod_auth.hrl").
--record(httpd_group,
- {name,
- userlist}).
-
+-endif. % -ifdef(src_mod_auth_hrl).
diff --git a/lib/inets/src/http_server/mod_auth_dets.erl b/lib/inets/src/http_server/mod_auth_dets.erl
index bc6c2b70a0..a48725d5d9 100644
--- a/lib/inets/src/http_server/mod_auth_dets.erl
+++ b/lib/inets/src/http_server/mod_auth_dets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,7 @@
-export([store_directory_data/3]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
-include("mod_auth.hrl").
store_directory_data(_Directory, DirData, Server_root) ->
diff --git a/lib/inets/src/http_server/mod_auth_plain.erl b/lib/inets/src/http_server/mod_auth_plain.erl
index d88859d28a..c0a83711ba 100644
--- a/lib/inets/src/http_server/mod_auth_plain.erl
+++ b/lib/inets/src/http_server/mod_auth_plain.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,8 @@
-include("httpd.hrl").
-include("mod_auth.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
+
-define(VMODULE,"AUTH_PLAIN").
diff --git a/lib/inets/src/http_server/mod_auth_server.erl b/lib/inets/src/http_server/mod_auth_server.erl
index 5f9e59be9d..947273bd9e 100644
--- a/lib/inets/src/http_server/mod_auth_server.erl
+++ b/lib/inets/src/http_server/mod_auth_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-behaviour(gen_server).
diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl
index 33605b9698..c854166c29 100644
--- a/lib/inets/src/http_server/mod_cgi.erl
+++ b/lib/inets/src/http_server/mod_cgi.erl
@@ -27,6 +27,7 @@
-export([do/1, load/2, store/2]).
-include("http_internal.hrl").
+-include("httpd_internal.hrl").
-include("httpd.hrl").
-define(VMODULE,"CGI").
diff --git a/lib/inets/src/http_server/mod_dir.erl b/lib/inets/src/http_server/mod_dir.erl
index cdc7cc01e4..d791ee28e9 100644
--- a/lib/inets/src/http_server/mod_dir.erl
+++ b/lib/inets/src/http_server/mod_dir.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,11 @@
%%
%%
-module(mod_dir).
--export([do/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
+
+-export([do/1]).
%% do
@@ -57,7 +59,7 @@ do_dir(Info) ->
case file:read_file_info(DefaultPath) of
{ok,FileInfo} when FileInfo#file_info.type == directory ->
DecodedRequestURI =
- httpd_util:decode_hex(Info#mod.request_uri),
+ http_uri:decode(Info#mod.request_uri),
?DEBUG("do_dir -> ~n"
" Path: ~p~n"
" DefaultPath: ~p~n"
diff --git a/lib/inets/src/http_server/mod_disk_log.erl b/lib/inets/src/http_server/mod_disk_log.erl
index 95e0d00c70..5a3766de66 100644
--- a/lib/inets/src/http_server/mod_disk_log.erl
+++ b/lib/inets/src/http_server/mod_disk_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-define(VMODULE,"DISK_LOG").
-include("httpd.hrl").
-
+-include("httpd_internal.hrl").
%%%=========================================================================
%%% API
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index cb33544540..929185a67a 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -29,6 +29,8 @@
-export([do/1, load/2, store/2]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"ESI").
-define(DEFAULT_ERL_TIMEOUT,15000).
@@ -37,6 +39,7 @@
%%%=========================================================================
%%% API
%%%=========================================================================
+
%%--------------------------------------------------------------------------
%% deliver(SessionID, Data) -> ok | {error, bad_sessionID}
%% SessionID = pid()
@@ -48,7 +51,7 @@
%% request handling process so it can forward it to the client.
%%-------------------------------------------------------------------------
deliver(SessionID, Data) when is_pid(SessionID) ->
- SessionID ! {ok, Data},
+ SessionID ! {esi_data, Data},
ok;
deliver(_SessionID, _Data) ->
{error, bad_sessionID}.
@@ -65,6 +68,7 @@ deliver(_SessionID, _Data) ->
%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
%%-------------------------------------------------------------------------
do(ModData) ->
+ ?hdrt("do", []),
case proplists:get_value(status, ModData#mod.data) of
{_StatusCode, _PhraseArgs, _Reason} ->
{proceed, ModData#mod.data};
@@ -184,6 +188,7 @@ store({erl_script_nocache, Value}, _) ->
%%% Internal functions
%%%========================================================================
generate_response(ModData) ->
+ ?hdrt("generate response", []),
case scheme(ModData#mod.request_uri, ModData#mod.config_db) of
{eval, ESIBody, Modules} ->
eval(ModData, ESIBody, Modules);
@@ -235,6 +240,7 @@ alias_match_str(Alias, eval_script_alias) ->
erl(#mod{method = Method} = ModData, ESIBody, Modules)
when (Method =:= "GET") orelse (Method =:= "HEAD") ->
+ ?hdrt("erl", [{method, Method}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok, [ModuleName, FuncAndInput]} ->
case httpd_util:split(FuncAndInput,"[\?/]",2) of
@@ -260,6 +266,7 @@ erl(#mod{request_uri = ReqUri,
method = "PUT",
http_version = Version,
data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, put}]),
{proceed, [{status,{501,{"PUT", ReqUri, Version},
?NICE("Erl mechanism doesn't support method PUT")}}|
Data]};
@@ -268,12 +275,14 @@ erl(#mod{request_uri = ReqUri,
method = "DELETE",
http_version = Version,
data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("erl", [{method, delete}]),
{proceed,[{status,{501,{"DELETE", ReqUri, Version},
?NICE("Erl mechanism doesn't support method DELETE")}}|
Data]};
erl(#mod{method = "POST",
entity_body = Body} = ModData, ESIBody, Modules) ->
+ ?hdrt("erl", [{method, post}]),
case httpd_util:split(ESIBody,":|%3A|/",2) of
{ok,[ModuleName, Function]} ->
generate_webpage(ModData, ESIBody, Modules,
@@ -289,6 +298,7 @@ generate_webpage(ModData, ESIBody, [all], Module, FunctionName,
FunctionName, Input, ScriptElements);
generate_webpage(ModData, ESIBody, Modules, Module, FunctionName,
Input, ScriptElements) ->
+ ?hdrt("generate webpage", []),
Function = list_to_atom(FunctionName),
case lists:member(Module, Modules) of
true ->
@@ -309,8 +319,9 @@ generate_webpage(ModData, ESIBody, Modules, Module, FunctionName,
%% Old API that waits for the dymnamic webpage to be totally generated
%% before anythig is sent back to the client.
-erl_scheme_webpage_whole(Module, Function, Env, Input, ModData) ->
- case (catch Module:Function(Env, Input)) of
+erl_scheme_webpage_whole(Mod, Func, Env, Input, ModData) ->
+ ?hdrt("erl_scheme_webpage_whole", [{module, Mod}, {function, Func}]),
+ case (catch Mod:Func(Env, Input)) of
{'EXIT',{undef, _}} ->
{proceed, [{status, {404, ModData#mod.request_uri, "Not found"}}
| ModData#mod.data]};
@@ -347,6 +358,7 @@ erl_scheme_webpage_whole(Module, Function, Env, Input, ModData) ->
%% in small chunks at the time during generation.
erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) ->
process_flag(trap_exit, true),
+ ?hdrt("erl_scheme_webpage_chunk", [{module, Mod}, {function, Func}]),
Self = self(),
%% Spawn worker that generates the webpage.
%% It would be nicer to use erlang:function_exported/3 but if the
@@ -372,9 +384,12 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid) ->
deliver_webpage_chunk(ModData, Pid, Timeout).
deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
+ ?hdrt("deliver_webpage_chunk", [{timeout, Timeout}]),
case receive_headers(Timeout) of
{error, Reason} ->
%% Happens when webpage generator callback/3 is undefined
+ ?hdrv("deliver_webpage_chunk - failed receiving headers",
+ [{reason, Reason}]),
{error, Reason};
{Headers, Body} ->
case httpd_esi:handle_headers(Headers) of
@@ -399,6 +414,7 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
IsDisableChunkedSend)
end;
timeout ->
+ ?hdrv("deliver_webpage_chunk - timeout", []),
send_headers(ModData, {504, "Timeout"},[{"connection", "close"}]),
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket),
process_flag(trap_exit,false),
@@ -407,11 +423,17 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
receive_headers(Timeout) ->
receive
+ {esi_data, Chunk} ->
+ ?hdrt("receive_headers - received esi data (esi)", []),
+ httpd_esi:parse_headers(lists:flatten(Chunk));
{ok, Chunk} ->
+ ?hdrt("receive_headers - received esi data (ok)", []),
httpd_esi:parse_headers(lists:flatten(Chunk));
{'EXIT', Pid, erl_scheme_webpage_chunk_undefined} when is_pid(Pid) ->
+ ?hdrd("receive_headers - exit:chunk-undef", []),
{error, erl_scheme_webpage_chunk_undefined};
{'EXIT', Pid, Reason} when is_pid(Pid) ->
+ ?hdrv("receive_headers - exit", [{reason, Reason}]),
exit({mod_esi_linked_process_died, Pid, Reason})
after Timeout ->
timeout
@@ -427,19 +449,29 @@ handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
{proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
+ ?hdrt("handle_body - send chunk", [{timeout, Timeout}, {size, Size}]),
httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend),
receive
+ {esi_data, Data} ->
+ ?hdrt("handle_body - received data (esi)", []),
+ handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
+ IsDisableChunkedSend);
{ok, Data} ->
+ ?hdrt("handle_body - received data (ok)", []),
handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
IsDisableChunkedSend);
{'EXIT', Pid, normal} when is_pid(Pid) ->
+ ?hdrt("handle_body - exit:normal", []),
httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
{proceed, [{response, {already_sent, 200, Size}} |
ModData#mod.data]};
{'EXIT', Pid, Reason} when is_pid(Pid) ->
+ ?hdrv("handle_body - exit", [{reason, Reason}]),
httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
exit({mod_esi_linked_process_died, Pid, Reason})
+
after Timeout ->
+ ?hdrv("handle_body - timeout", []),
process_flag(trap_exit,false),
httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
exit({mod_esi_linked_process_timeout, Pid})
@@ -473,6 +505,7 @@ eval(#mod{request_uri = ReqUri,
method = "PUT",
http_version = Version,
data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("eval", [{method, put}]),
{proceed,[{status,{501,{"PUT", ReqUri, Version},
?NICE("Eval mechanism doesn't support method PUT")}}|
Data]};
@@ -481,6 +514,7 @@ eval(#mod{request_uri = ReqUri,
method = "DELETE",
http_version = Version,
data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("eval", [{method, delete}]),
{proceed,[{status,{501,{"DELETE", ReqUri, Version},
?NICE("Eval mechanism doesn't support method DELETE")}}|
Data]};
@@ -489,12 +523,14 @@ eval(#mod{request_uri = ReqUri,
method = "POST",
http_version = Version,
data = Data}, _ESIBody, _Modules) ->
+ ?hdrt("eval", [{method, post}]),
{proceed,[{status,{501,{"POST", ReqUri, Version},
?NICE("Eval mechanism doesn't support method POST")}}|
Data]};
eval(#mod{method = Method} = ModData, ESIBody, Modules)
- when Method == "GET"; Method == "HEAD" ->
+ when (Method =:= "GET") orelse (Method =:= "HEAD") ->
+ ?hdrt("eval", [{method, Method}]),
case is_authorized(ESIBody, Modules) of
true ->
case generate_webpage(ESIBody) of
diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl
index 9fd1fcec47..5cb30e3d97 100644
--- a/lib/inets/src/http_server/mod_get.erl
+++ b/lib/inets/src/http_server/mod_get.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(mod_get).
-export([do/1]).
-include("httpd.hrl").
-
+-include("httpd_internal.hrl").
%% do
do(Info) ->
diff --git a/lib/inets/src/http_server/mod_head.erl b/lib/inets/src/http_server/mod_head.erl
index 8b08d61651..c346fd4d23 100644
--- a/lib/inets/src/http_server/mod_head.erl
+++ b/lib/inets/src/http_server/mod_head.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/http_server/mod_htaccess.erl b/lib/inets/src/http_server/mod_htaccess.erl
index d8835198f5..e1f66d01c8 100644
--- a/lib/inets/src/http_server/mod_htaccess.erl
+++ b/lib/inets/src/http_server/mod_htaccess.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,7 @@
-export([do/1, load/2, store/2]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Public methods that interface the eswapi %%
diff --git a/lib/inets/src/http_server/mod_include.erl b/lib/inets/src/http_server/mod_include.erl
index 534eba8a36..35f45bdd33 100644
--- a/lib/inets/src/http_server/mod_include.erl
+++ b/lib/inets/src/http_server/mod_include.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
-export([do/1,parse/2,config/6,include/6,echo/6,fsize/6,flastmod/6,exec/6]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
-define(VMODULE,"INCLUDE").
@@ -186,9 +187,9 @@ document_uri(ConfigDB, RequestURI) ->
FileName = string:substr(Path,Start,Length),
case inets_regexp:match(VirtualPath, FileName++"\$") of
{match, _, _} ->
- httpd_util:decode_hex(VirtualPath)++AfterPath;
+ http_uri:decode(VirtualPath)++AfterPath;
nomatch ->
- string:strip(httpd_util:decode_hex(VirtualPath),right,$/)++
+ string:strip(http_uri:decode(VirtualPath),right,$/)++
"/"++FileName++AfterPath
end.
diff --git a/lib/inets/src/http_server/mod_log.erl b/lib/inets/src/http_server/mod_log.erl
index de24d5a569..c8a2ec0dc4 100644
--- a/lib/inets/src/http_server/mod_log.erl
+++ b/lib/inets/src/http_server/mod_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,7 @@
-export([do/1, load/2, store/2, remove/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
-define(VMODULE,"LOG").
%%%=========================================================================
diff --git a/lib/inets/src/http_server/mod_range.erl b/lib/inets/src/http_server/mod_range.erl
index 0698fb9099..a0408cba79 100644
--- a/lib/inets/src/http_server/mod_range.erl
+++ b/lib/inets/src/http_server/mod_range.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(mod_range).
-export([do/1]).
-include("httpd.hrl").
-
+-include("httpd_internal.hrl").
%% do
do(Info) ->
diff --git a/lib/inets/src/http_server/mod_responsecontrol.erl b/lib/inets/src/http_server/mod_responsecontrol.erl
index 79e2e1bdba..5d5b60cdbd 100644
--- a/lib/inets/src/http_server/mod_responsecontrol.erl
+++ b/lib/inets/src/http_server/mod_responsecontrol.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,7 @@
-export([do/1]).
-include("httpd.hrl").
+-include("httpd_internal.hrl").
do(Info) ->
?DEBUG("do -> response_control",[]),
diff --git a/lib/inets/src/http_server/mod_security.erl b/lib/inets/src/http_server/mod_security.erl
index 95793e1cfb..41988732ad 100644
--- a/lib/inets/src/http_server/mod_security.erl
+++ b/lib/inets/src/http_server/mod_security.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,6 +32,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-define(VMODULE,"SEC").
diff --git a/lib/inets/src/http_server/mod_security_server.erl b/lib/inets/src/http_server/mod_security_server.erl
index 58060686b3..784b3eba70 100644
--- a/lib/inets/src/http_server/mod_security_server.erl
+++ b/lib/inets/src/http_server/mod_security_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,6 +45,7 @@
-include("httpd.hrl").
-include("httpd_internal.hrl").
+-include("inets_internal.hrl").
-behaviour(gen_server).
diff --git a/lib/inets/src/http_server/mod_trace.erl b/lib/inets/src/http_server/mod_trace.erl
index df482228d8..7233925783 100644
--- a/lib/inets/src/http_server/mod_trace.erl
+++ b/lib/inets/src/http_server/mod_trace.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile
index 33c9e34a3a..20e22917e2 100644
--- a/lib/inets/src/inets_app/Makefile
+++ b/lib/inets/src/inets_app/Makefile
@@ -47,7 +47,9 @@ MODULES = \
inets_sup \
inets_regexp
-HRL_FILES = inets_internal.hrl
+INTERNAL_HRL_FILES = inets_internal.hrl
+EXTERNAL_HRL_FILES = ../../include/httpd.hrl \
+ ../../include/mod_auth.hrl
ERL_FILES = $(MODULES:%=%.erl)
@@ -67,18 +69,14 @@ APPUP_TARGET = $(EBIN)/$(APPUP_FILE)
# ----------------------------------------------------
-# INETS FLAGS
-# ----------------------------------------------------
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
-
-
-# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+include inets.mk
+
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS)
# ----------------------------------------------------
@@ -112,7 +110,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app
+ $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app
+ $(INSTALL_DIR) $(RELSYSDIR)/include
+ $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include
$(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src
index 04f6365b98..cb036157a5 100644
--- a/lib/inets/src/inets_app/inets.app.src
+++ b/lib/inets/src/inets_app/inets.app.src
@@ -107,5 +107,6 @@
tftp_sup
]},
{registered,[inets_sup, httpc_manager]},
+ %% If the "new" ssl is used then 'crypto' must be started before inets.
{applications,[kernel,stdlib]},
{mod,{inets_app,[]}}]}.
diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src
index dfdfb41373..07da8ca961 100644
--- a/lib/inets/src/inets_app/inets.appup.src
+++ b/lib/inets/src/inets_app/inets.appup.src
@@ -1,7 +1,7 @@
%% This is an -*- erlang -*- file.
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,64 +18,34 @@
{"%VSN%",
[
- {"5.3.1",
+ {"5.5.1",
[
- {load_module, httpc, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {load_module, http_chunk, soft_purge, soft_purge, []}
]
},
- {"5.3",
- [
- {load_module, httpc, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []},
- {load_module, mod_esi, soft_purge, soft_purge, []}
- ]
- },
- {"5.2",
- [
- {restart_application, inets}
- ]
- },
- {"5.1.3",
+ {"5.5",
[
{restart_application, inets}
]
},
- {"5.1.2",
+ {"5.4",
[
{restart_application, inets}
]
- }
+ }
],
[
- {"5.3.1",
+ {"5.5.1",
[
- {load_module, httpc, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []}
+ {load_module, http_chunk, soft_purge, soft_purge, []}
]
},
- {"5.3",
- [
- {load_module, httpc, soft_purge, soft_purge, []},
- {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]},
- {update, httpc_manager, soft, soft_purge, soft_purge, []},
- {load_module, mod_esi, soft_purge, soft_purge, []}
- ]
- },
- {"5.2",
- [
- {restart_application, inets}
- ]
- },
- {"5.1.3",
+ {"5.5",
[
{restart_application, inets}
]
- },
- {"5.1.2",
+ },
+ {"5.4",
[
{restart_application, inets}
]
diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl
index 7e3f862ee7..054468e445 100644
--- a/lib/inets/src/inets_app/inets.erl
+++ b/lib/inets/src/inets_app/inets.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
+%%
%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
-%%
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -57,7 +57,7 @@ start(Type) ->
%% Function: start(Service, ServiceConfig [, How]) -> {ok, Pid} |
%% {error, Reason}
%%
-%% Service = - ftpc | tftpd | httpc | httpd
+%% Service = - ftpc | tftpd | tftpc | tftp | httpc | httpd
%% ServiceConfig = ConfPropList | ConfFile
%% ConfPropList = [{Property, Value}] according to service
%% ConfFile = Path - when service is httpd
@@ -100,7 +100,7 @@ stop() ->
%%--------------------------------------------------------------------
%% Function: stop(Service, Pid) -> ok
%%
-%% Service - ftp | tftpd | http | httpd | stand_alone
+%% Service - ftpc | ftp | tftpd | tftpc | tftp | httpc | httpd | stand_alone
%%
%% Description: Stops a started service of the inets application or takes
%% down a stand alone "service" gracefully.
@@ -382,7 +382,7 @@ key1search(Key, Vals, Def) ->
%% Description: Returns a list of supported services
%%-------------------------------------------------------------------
service_names() ->
- [ftpc, tftpd, httpc, httpd].
+ [ftpc, tftp, httpc, httpd].
%%-----------------------------------------------------------------
@@ -533,7 +533,7 @@ error_to_exit(Where, {error, Reason}) ->
%%-----------------------------------------------------------------
-%% report_event(Serverity, Label, Service, Content)
+%% report_event(Severity, Label, Service, Content)
%%
%% Parameters:
%% Severity -> 0 =< integer() =< 100
@@ -725,8 +725,8 @@ call_service(Service, Call, Args) ->
service_module(tftpd) ->
tftp;
-service_module(httpc) ->
- httpc;
+service_module(tftpc) ->
+ tftp;
service_module(ftpc) ->
ftp;
service_module(Service) ->
diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk
new file mode 100644
index 0000000000..b6e9fe1d96
--- /dev/null
+++ b/lib/inets/src/inets_app/inets.mk
@@ -0,0 +1,45 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+ifeq ($(INETS_TRACE), io)
+ERL_COMPILE_FLAGS += -Dinets_trace_io
+endif
+
+ifeq ($(INETS_DEBUG), true)
+ERL_COMPILE_FLAGS += -Dinets_debug
+endif
+
+ifeq ($(USE_INETS_HIPE), true)
+ERL_COMPILE_FLAGS += +native
+endif
+
+ifeq ($(WARN_UNUSED_WARS), true)
+ERL_COMPILE_FLAGS += +warn_unused_vars
+endif
+
+INETS_APP_VSN_COMPILE_FLAGS = \
+ +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}'
+
+INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
+
+INETS_ERL_COMPILE_FLAGS += \
+ -pa $(ERL_TOP)/lib/inets/ebin \
+ $(INETS_APP_VSN_COMPILE_FLAGS)
+
diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl
index 3499314d54..e9eb9892f2 100644
--- a/lib/inets/src/inets_app/inets_service.erl
+++ b/lib/inets/src/inets_app/inets_service.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -61,5 +61,5 @@ behaviour_info(_) ->
%% service_info() -> [{Property, Value}] | {error, Reason}
-%% ex: http:service_info() -> [{profile, ProfileName}]
+%% ex: httpc:service_info() -> [{profile, ProfileName}]
%% httpd:service_info() -> [{host, Host}, {port, Port}]
diff --git a/lib/inets/src/tftp/Makefile b/lib/inets/src/tftp/Makefile
index b4339da1e2..759b70c8e4 100644
--- a/lib/inets/src/tftp/Makefile
+++ b/lib/inets/src/tftp/Makefile
@@ -56,17 +56,16 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
# ----------------------------------------------------
-# INETS FLAGS
+# FLAGS
# ----------------------------------------------------
-INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"'
+include ../inets_app/inets.mk
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-ERL_COMPILE_FLAGS += $(INETS_FLAGS) \
- +'{parse_transform,sys_pre_attributes}' \
- +'{attribute,insert,app_vsn,$(APP_VSN)}'
+ERL_COMPILE_FLAGS += \
+ $(INETS_FLAGS) \
+ $(INETS_ERL_COMPILE_FLAGS) \
+ -I../../include \
+ -I../inets_app
# ----------------------------------------------------
@@ -87,9 +86,10 @@ docs:
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/src/tftp
+ $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/tftp
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
release_docs_spec:
diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile
index 668752da9e..87ca60e4b3 100644
--- a/lib/inets/test/Makefile
+++ b/lib/inets/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -143,6 +143,8 @@ else
INETS_FLAGS += -Dhttpd_security_verbosity=log
endif
+INETS_FLAGS += -pa ../../inets/ebin
+
INETS_ROOT = ../../inets
MODULES = \
@@ -198,7 +200,8 @@ SOURCE = $(ERL_FILES) $(HRL_FILES)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-INETS_SPECS = inets.spec inets.spec.vxworks
+INETS_SPECS = inets.spec
+COVER_FILE = inets.cover
INETS_FILES = inets.config $(INETS_SPECS)
# SUB_SUITES = \
@@ -220,10 +223,10 @@ MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
ifeq ($(MAKE_EMAKE),)
BUILDTARGET = $(TARGET_FILES)
-RELTEST_FILES = $(INETS_SPECS) $(SOURCE)
+RELTEST_FILES = $(COVER_FILE) $(INETS_SPECS) $(SOURCE)
else
BUILDTARGET = emakebuild
-RELTEST_FILES = $(EMAKEFILE) $(INETS_SPECS) $(SOURCE)
+RELTEST_FILES = $(EMAKEFILE) $(COVER_FILE) $(INETS_SPECS) $(SOURCE)
endif
@@ -241,8 +244,11 @@ RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin
# The path to the test_server ebin dir is needed when
# running the target "targets".
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -pa ../../../internal_tools/test_server/ebin \
- $(INCLUDES) $(FTP_FLAGS) $(INETS_FLAGS)
+ERL_COMPILE_FLAGS += \
+ -pa ../../../internal_tools/test_server/ebin \
+ $(INCLUDES) \
+ $(FTP_FLAGS) \
+ $(INETS_FLAGS)
# ----------------------------------------------------
# Targets
@@ -283,26 +289,30 @@ release_spec: opt
$(INSTALL_DATA) $(INETS_FILES) $(RELSYSDIR)/test
@for d in $(DATADIRS); do \
echo "installing data dir $$d"; \
- echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
- cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
- find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
- find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
- find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
- find $$d -name '*~' >> $$d/TAR.exclude2; \
- find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
- find $$d -name 'core' >> $$d/TAR.exclude2; \
- find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
- tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ if test -f $$d/TAR.exclude; then \
+ echo $$d/TAR.exclude2 > $$d/TAR.exclude2; \
+ cat $$d/TAR.exclude >> $$d/TAR.exclude2; \
+ find $$d -name '*.contrib*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.keep*' >> $$d/TAR.exclude2; \
+ find $$d -name '*.mkelem*' >> $$d/TAR.exclude2; \
+ find $$d -name '*~' >> $$d/TAR.exclude2; \
+ find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \
+ find $$d -name 'core' >> $$d/TAR.exclude2; \
+ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \
+ tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ else \
+ tar cf - $$d | (cd $(RELSYSDIR)/test; tar xf -); \
+ fi; \
done
release_tests_spec: opt
$(INSTALL_DIR) $(RELTESTSYSDIR)
$(INSTALL_DATA) $(RELTEST_FILES) $(RELTESTSYSDIR)
- chmod -f -R u+w $(RELTESTSYSDIR)
+ chmod -R u+w $(RELTESTSYSDIR)
tar chf - $(DATADIRS) | (cd $(RELTESTSYSDIR); tar xf -)
$(INSTALL_DIR) $(RELTESTSYSALLDATADIR)
$(INSTALL_DIR) $(RELTESTSYSBINDIR)
- chmod -f -R +x $(RELTESTSYSBINDIR)
+ chmod -R +x $(RELTESTSYSBINDIR)
$(INSTALL_DIR) $(RELTESTSYSALLDATADIR)/win32/lib
release_docs_spec:
diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl
index e7404f945b..4bafdbfef8 100644
--- a/lib/inets/test/ftp_SUITE.erl
+++ b/lib/inets/test/ftp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,29 +20,14 @@
-module(ftp_SUITE).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
% -export([init_per_testcase/2, end_per_testcase/2]).
-export([init_per_suite/1, end_per_suite/1]).
-%% Test cases must be exported.
--export([solaris8_test/1,
- solaris9_test/1,
- solaris10_test/1,
- linux_x86_test/1,
- linux_ppc_test/1,
- macosx_x86_test/1,
- macosx_ppc_test/1,
- openbsd_test/1,
- freebsd_test/1,
- netbsd_test/1,
- windows_xp_test/1,
- windows_2003_server_test/1,
- ticket_tests/1]).
-
-define(FTP_USER, "anonymous").
-define(FTP_PASS, passwd()).
-define(FTP_PORT, 21).
@@ -72,52 +57,44 @@
%% Description: Returns documentation/test cases in this test suite
%% or a skip tuple if the platform is not supported.
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test the ftp client in the inets application."];
-all(suite) ->
- [
- solaris8_test,
- solaris9_test,
- solaris10_test,
- linux_x86_test,
- linux_ppc_test,
- macosx_x86_test,
- macosx_ppc_test,
- openbsd_test,
- freebsd_test,
- netbsd_test,
- windows_xp_test,
- windows_2003_server_test,
- ticket_tests
- ].
-
-solaris8_test(suite) ->
- [{ftp_solaris8_sparc_test,all}].
-solaris9_test(suite) ->
- [{ftp_solaris9_sparc_test,all}].
-solaris10_test(suite) ->
- [{ftp_solaris10_sparc_test,all}, {ftp_solaris10_x86_test,all}].
-linux_x86_test(suite) ->
- [{ftp_linux_x86_test,all}].
-linux_ppc_test(suite) ->
- [{ftp_linux_ppc_test,all}].
-macosx_x86_test(suite) ->
- [{ftp_macosx_x86_test,all}].
-macosx_ppc_test(suite) ->
- [{ftp_macosx_ppc_test,all}].
-openbsd_test(suite) ->
- [{ftp_openbsd_x86_test,all}].
-freebsd_test(suite) ->
- [{ftp_freebsd_x86_test,all}].
-netbsd_test(suite) ->
- [{ftp_netbsd_x86_test,all}].
-windows_xp_test(suite) ->
- [{ftp_windows_xp_test,all}].
-windows_2003_server_test(suite) ->
- [{ftp_windows_2003_server_test,all}].
-
-ticket_tests(suite) ->
- [{ftp_ticket_test, all}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, solaris8_test}, {group, solaris9_test},
+ {group, solaris10_test}, {group, linux_x86_test},
+ {group, linux_ppc_test}, {group, macosx_x86_test},
+ {group, macosx_ppc_test}, {group, openbsd_test},
+ {group, freebsd_test}, {group, netbsd_test},
+ {group, windows_xp_test},
+ {group, windows_2003_server_test},
+ {group, ticket_tests}].
+
+groups() ->
+ [{solaris8_test, [], [{ftp_solaris8_sparc_test, all}]},
+ {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]},
+ {solaris10_test, [],
+ [{ftp_solaris10_sparc_test, all},
+ {ftp_solaris10_x86_test, all}]},
+ {linux_x86_test, [], [{ftp_linux_x86_test, all}]},
+ {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]},
+ {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]},
+ {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]},
+ {openbsd_test, [], [{ftp_openbsd_x86_test, all}]},
+ {freebsd_test, [], [{ftp_freebsd_x86_test, all}]},
+ {netbsd_test, [], [{ftp_netbsd_x86_test, all}]},
+ {windows_xp_test, [], [{ftp_windows_xp_test, all}]},
+ {windows_2003_server_test, [],
+ [{ftp_windows_2003_server_test, all}]},
+ {ticket_tests, [], [{ftp_ticket_test, all}]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl
index 9ca6575b2d..cbc1b04bbb 100644
--- a/lib/inets/test/ftp_format_SUITE.erl
+++ b/lib/inets/test/ftp_format_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,23 +20,44 @@
-module(ftp_format_SUITE).
-author('[email protected]').
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include("ftp_internal.hrl").
%% Test server specific exports
--export([all/1, init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
--export([ftp_response/1, ftp_150/1,
- ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1,
- ftp_other_status_codes/1, ftp_multiple_lines/1,
- ftp_multipel_ctrl_messages/1, format_error/1]).
+-export([ ftp_150/1,
+ ftp_200/1, ftp_220/1, ftp_226/1, ftp_257/1, ftp_331/1, ftp_425/1,
+ ftp_other_status_codes/1, ftp_multiple_lines/1,
+ ftp_multipel_ctrl_messages/1, format_error/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, ftp_response}, format_error].
+
+groups() ->
+ [{ftp_response, [],
+ [ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331,
+ ftp_425, ftp_other_status_codes, ftp_multiple_lines,
+ ftp_multipel_ctrl_messages]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(doc) ->
- ["Test library functions for the ftp client."];
-all(suite) ->
- [ftp_response, format_error].
init_per_testcase(_, Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
@@ -51,14 +72,6 @@ end_per_testcase(_, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-ftp_response(doc) ->
- ["Test ftp_response:parse_lines/3 and ftp_response:interpret/1."
- " This test case will simulate that the "
- "message will be recived a little at the time on a socket and the "
- "package may be broken up into smaller parts at arbitrary point."];
-ftp_response(suite) ->
- [ftp_150, ftp_200, ftp_220, ftp_226, ftp_257, ftp_331, ftp_425,
- ftp_other_status_codes, ftp_multiple_lines, ftp_multipel_ctrl_messages].
ftp_150(doc) ->
["Especially check that respons can be devided in a random place."];
diff --git a/lib/inets/test/ftp_freebsd_x86_test.erl b/lib/inets/test/ftp_freebsd_x86_test.erl
index 457e18ffbe..1d66779882 100644
--- a/lib/inets/test/ftp_freebsd_x86_test.erl
+++ b/lib/inets/test/ftp_freebsd_x86_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,23 +86,30 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_linux_ppc_test.erl b/lib/inets/test/ftp_linux_ppc_test.erl
index ad38137678..bba97237f1 100644
--- a/lib/inets/test/ftp_linux_ppc_test.erl
+++ b/lib/inets/test/ftp_linux_ppc_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -87,23 +87,30 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_linux_x86_test.erl b/lib/inets/test/ftp_linux_x86_test.erl
index b9c88d121a..bbefd8231e 100644
--- a/lib/inets/test/ftp_linux_x86_test.erl
+++ b/lib/inets/test/ftp_linux_x86_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,30 +86,30 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
-
-all(suite) ->
- [
- open,
- open_port,
- passive,
- active,
- api_missuse,
- not_owner,
- progress_report
- ].
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_macosx_ppc_test.erl b/lib/inets/test/ftp_macosx_ppc_test.erl
index cf548a73c0..c9f33b8beb 100644
--- a/lib/inets/test/ftp_macosx_ppc_test.erl
+++ b/lib/inets/test/ftp_macosx_ppc_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -87,21 +87,28 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+[open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_macosx_x86_test.erl b/lib/inets/test/ftp_macosx_x86_test.erl
index c59a992421..17b7160b95 100644
--- a/lib/inets/test/ftp_macosx_x86_test.erl
+++ b/lib/inets/test/ftp_macosx_x86_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,29 +86,36 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+[open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
passive_cd(X) -> ?LIB_MOD:passive_cd(X).
passive_lcd(X) -> ?LIB_MOD:passive_lcd(X).
passive_ls(X) -> ?LIB_MOD:passive_ls(X).
-passive_nlist(X) -> ?LIB_MOD:passive_nlist(X).
+passive_nlist(X) -> ?LIB_MOD:passive_nlist([{wildcard_support, false} | X]).
passive_rename(X) -> ?LIB_MOD:passive_rename(X).
passive_delete(X) -> ?LIB_MOD:passive_delete(X).
passive_mkdir(X) -> ?LIB_MOD:passive_mkdir(X).
@@ -129,7 +136,7 @@ active_pwd(X) -> ?LIB_MOD:active_pwd(X).
active_cd(X) -> ?LIB_MOD:active_cd(X).
active_lcd(X) -> ?LIB_MOD:active_lcd(X).
active_ls(X) -> ?LIB_MOD:active_ls(X).
-active_nlist(X) -> ?LIB_MOD:active_nlist(X).
+active_nlist(X) -> ?LIB_MOD:active_nlist([{wildcard_support, false} | X]).
active_rename(X) -> ?LIB_MOD:active_rename(X).
active_delete(X) -> ?LIB_MOD:active_delete(X).
active_mkdir(X) -> ?LIB_MOD:active_mkdir(X).
diff --git a/lib/inets/test/ftp_netbsd_x86_test.erl b/lib/inets/test/ftp_netbsd_x86_test.erl
index a5711b7bde..bb474852c5 100644
--- a/lib/inets/test/ftp_netbsd_x86_test.erl
+++ b/lib/inets/test/ftp_netbsd_x86_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,22 +86,29 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_openbsd_x86_test.erl b/lib/inets/test/ftp_openbsd_x86_test.erl
index 4833b6332b..54fce702a0 100644
--- a/lib/inets/test/ftp_openbsd_x86_test.erl
+++ b/lib/inets/test/ftp_openbsd_x86_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -87,23 +87,30 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_solaris10_sparc_test.erl b/lib/inets/test/ftp_solaris10_sparc_test.erl
index 6066195f9b..0da50dc91b 100644
--- a/lib/inets/test/ftp_solaris10_sparc_test.erl
+++ b/lib/inets/test/ftp_solaris10_sparc_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -87,23 +87,30 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_solaris10_x86_test.erl b/lib/inets/test/ftp_solaris10_x86_test.erl
index 3bd99fc3f2..3e7045bb4d 100644
--- a/lib/inets/test/ftp_solaris10_x86_test.erl
+++ b/lib/inets/test/ftp_solaris10_x86_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD, ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_), ?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -88,23 +88,30 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
%%--------------------------------------------------------------------
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_solaris8_sparc_test.erl b/lib/inets/test/ftp_solaris8_sparc_test.erl
index 9764071cd9..23dbfc8fe3 100644
--- a/lib/inets/test/ftp_solaris8_sparc_test.erl
+++ b/lib/inets/test/ftp_solaris8_sparc_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,22 +86,29 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_solaris9_sparc_test.erl b/lib/inets/test/ftp_solaris9_sparc_test.erl
index a9f77bbdac..896e2f497f 100644
--- a/lib/inets/test/ftp_solaris9_sparc_test.erl
+++ b/lib/inets/test/ftp_solaris9_sparc_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -85,22 +85,29 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl
index 75e1a5a7f9..d0d07a8358 100644
--- a/lib/inets/test/ftp_suite_lib.erl
+++ b/lib/inets/test/ftp_suite_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,8 +21,8 @@
-module(ftp_suite_lib).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("test_server/include/test_server_line.hrl").
-include("inets_test_lib.hrl").
%% Test server specific exports
@@ -48,14 +48,17 @@
-ifdef(ftp_debug_client).
-define(ftp_open(Host, Flags),
- do_ftp_open(Host, [debug, {timeout, timer:seconds(15)}] ++ Flags)).
+ do_ftp_open(Host, [{debug, debug},
+ {timeout, timer:seconds(15)} | Flags])).
-else.
-ifdef(ftp_trace_client).
-define(ftp_open(Host, Flags),
- do_ftp_open(Host, [trace, {timeout, timer:seconds(15)}] ++ Flags)).
+ do_ftp_open(Host, [{debug, trace},
+ {timeout, timer:seconds(15)} | Flags])).
-else.
-define(ftp_open(Host, Flags),
- do_ftp_open(Host, [verbose, {timeout, timer:seconds(15)}] ++ Flags)).
+ do_ftp_open(Host, [{verbose, true},
+ {timeout, timer:seconds(15)} | Flags])).
-endif.
-endif.
@@ -71,7 +74,7 @@ tickets(suite) ->
ftpd_init(FtpdTag, Config) ->
%% Get the host name(s) of FTP server
Hosts =
- case ?config(ftpd_hosts, Config) of
+ case ct:get_config(ftpd_hosts) of
undefined ->
ftpd_hosts(data_dir(Config));
H ->
@@ -113,9 +116,7 @@ get_ftpd_host([Host|Hosts]) ->
p("get_ftpd_host -> entry with"
"~n Host: ~p"
"~n", [Host]),
- case (catch ftp:open({option_list,
- [{host, Host}, {port, ?FTP_PORT},
- {timeout, 20000}]})) of
+ case (catch ftp:open(Host, [{port, ?FTP_PORT}, {timeout, 20000}])) of
{ok, Pid} ->
(catch ftp:close(Pid)),
{ok, Host};
@@ -128,7 +129,7 @@ get_ftpd_host([Host|Hosts]) ->
dirty_select_ftpd_host(Config) ->
Hosts =
- case ?config(ftpd_hosts, Config) of
+ case ct:get_config(ftpd_hosts) of
undefined ->
ftpd_hosts(data_dir(Config));
H ->
@@ -212,7 +213,7 @@ do_init_per_testcase(Case, Config)
inets:start(),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [])) of
+ case (catch ?ftp_open(Host, [{mode, passive}])) of
{ok, Pid} ->
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
@@ -225,9 +226,8 @@ do_init_per_testcase(Case, Config)
inets:start(),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
- case (catch ?ftp_open(Host, [])) of
+ case (catch ?ftp_open(Host, [{mode, active}])) of
{ok, Pid} ->
- ok = ftp:force_active(Pid),
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
SKIP
@@ -240,11 +240,10 @@ do_init_per_testcase(Case, Config)
io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
- Opts = [{host, Host},
- {port, ?FTP_PORT},
- {flags, [verbose]},
+ Opts = [{port, ?FTP_PORT},
+ {verbose, true},
{progress, {?MODULE, progress, #progress{}}}],
- case ftp:open({option_list, Opts}) of
+ case ftp:open(Host, Opts) of
{ok, Pid} ->
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
[{ftp, Pid} | data_dir(NewConfig)];
@@ -257,22 +256,23 @@ do_init_per_testcase(Case, Config) ->
inets:start(),
NewConfig = close_connection(watch_dog(Config)),
Host = ftp_host(Config),
- Flags =
+ Opts1 =
if
((Case =:= passive_ip_v6_disabled) orelse
(Case =:= active_ip_v6_disabled)) ->
- [ip_v6_disabled];
+ [{ipfamily, inet}];
true ->
[]
end,
- case (catch ?ftp_open(Host, Flags)) of
+ Opts2 =
+ case string:tokens(atom_to_list(Case), [$_]) of
+ [_, "active" | _] ->
+ [{mode, active} | Opts1];
+ _ ->
+ [{mode, passive} | Opts1]
+ end,
+ case (catch ?ftp_open(Host, Opts2)) of
{ok, Pid} ->
- case string:tokens(atom_to_list(Case), [$_]) of
- [_, "active"|_] ->
- ok = ftp:force_active(Pid);
- _ ->
- ok
- end,
ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS),
[{ftp, Pid} | data_dir(NewConfig)];
{skip, _} = SKIP ->
@@ -365,6 +365,7 @@ open(Config) when is_list(Config) ->
Host = ftp_host(Config),
(catch tc_open(Host)).
+
tc_open(Host) ->
{ok, Pid} = ?ftp_open(Host, []),
ok = ftp:close(Pid),
@@ -374,8 +375,9 @@ tc_open(Host) ->
{flags, [verbose]},
{timeout, 30000}]}),
ok = ftp:close(Pid1),
- {error, ehost} = ftp:open({option_list, [{port, ?FTP_PORT},
- {flags, [verbose]}]}),
+
+ {error, ehost} =
+ ftp:open({option_list, [{port, ?FTP_PORT}, {flags, [verbose]}]}),
{ok, Pid2} = ftp:open(Host),
ok = ftp:close(Pid2),
@@ -408,6 +410,15 @@ tc_open(Host) ->
{mode, cool}]}),
test_server:sleep(100),
ok = ftp:close(Pid6),
+
+ {ok, Pid7} =
+ ftp:open(Host, [{port, ?FTP_PORT}, {verbose, true}, {timeout, 30000}]),
+ ok = ftp:close(Pid7),
+
+ {ok, Pid8} =
+ ftp:open(Host, ?FTP_PORT),
+ ok = ftp:close(Pid8),
+
ok.
@@ -420,7 +431,7 @@ open_port(suite) ->
[];
open_port(Config) when is_list(Config) ->
Host = ftp_host(Config),
- {ok, Pid} = ftp:open(Host, ?FTP_PORT),
+ {ok, Pid} = ftp:open(Host, [{port, ?FTP_PORT}]),
ok = ftp:close(Pid),
{error, ehost} = ftp:open(?BAD_HOST, []),
ok.
@@ -497,7 +508,8 @@ passive_nlist(suite) ->
[];
passive_nlist(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
- do_nlist(Pid).
+ WildcardSupport = ?config(wildcard_support, Config),
+ do_nlist(Pid, WildcardSupport).
%%-------------------------------------------------------------------------
@@ -757,7 +769,8 @@ active_nlist(suite) ->
[];
active_nlist(Config) when is_list(Config) ->
Pid = ?config(ftp, Config),
- do_nlist(Pid).
+ WildcardSupport = ?config(wildcard_support, Config),
+ do_nlist(Pid, WildcardSupport).
%%-------------------------------------------------------------------------
@@ -954,26 +967,39 @@ api_missuse(doc)->
["Test that behaviour of the ftp process if the api is abused"];
api_missuse(suite) -> [];
api_missuse(Config) when is_list(Config) ->
+ io:format("api_missuse -> entry~n", []),
+ Flag = process_flag(trap_exit, true),
Pid = ?config(ftp, Config),
Host = ftp_host(Config),
-
+
%% Serious programming fault, connetion will be shut down
- {error, {connection_terminated, 'API_violation'}} =
- gen_server:call(Pid, {self(), foobar, 10}, infinity),
+ io:format("api_missuse -> verify bad call termination (~p)~n", [Pid]),
+ case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of
+ {error, {connection_terminated, 'API_violation'}} ->
+ ok;
+ Unexpected1 ->
+ exit({unexpected_result, Unexpected1})
+ end,
test_server:sleep(500),
undefined = process_info(Pid, status),
+ io:format("api_missuse -> start new client~n", []),
{ok, Pid2} = ?ftp_open(Host, []),
%% Serious programming fault, connetion will be shut down
+ io:format("api_missuse -> verify bad cast termination~n", []),
gen_server:cast(Pid2, {self(), foobar, 10}),
test_server:sleep(500),
undefined = process_info(Pid2, status),
+ io:format("api_missuse -> start new client~n", []),
{ok, Pid3} = ?ftp_open(Host, []),
%% Could be an innocent misstake the connection lives.
+ io:format("api_missuse -> verify bad bang~n", []),
Pid3 ! foobar,
test_server:sleep(500),
{status, _} = process_info(Pid3, status),
+ process_flag(trap_exit, Flag),
+ io:format("api_missuse -> done~n", []),
ok.
@@ -1218,16 +1244,20 @@ do_ls(Pid) ->
{ok, _} = ftp:ls(Pid, "incom*"),
ok.
-do_nlist(Pid) ->
+do_nlist(Pid, WildcardSupport) ->
{ok, _} = ftp:nlist(Pid),
{ok, _} = ftp:nlist(Pid, "incoming"),
%% neither nlist nor ls operates on a directory
%% they operate on a pathname, which *can* be a
%% directory, but can also be a filename or a group
%% of files (including wildcards).
- {ok, _} = ftp:nlist(Pid, "incom*"),
-%% {error, epath} = ftp:nlist(Pid, ?BAD_DIR),
- ok.
+ case WildcardSupport of
+ true ->
+ {ok, _} = ftp:nlist(Pid, "incom*"),
+ ok;
+ _ ->
+ ok
+ end.
do_rename(Pid, Config) ->
PrivDir = ?config(priv_dir, Config),
@@ -1525,11 +1555,11 @@ split([C| Cs], I, Is) ->
split([], I, Is) ->
lists:reverse([lists:reverse(I)| Is]).
-do_ftp_open(Host, Flags) ->
+do_ftp_open(Host, Opts) ->
io:format("do_ftp_open -> entry with"
- "~n Host: ~p"
- "~n Flags: ~p", [Host, Flags]),
- case ftp:open(Host, Flags) of
+ "~n Host: ~p"
+ "~n Opts: ~p", [Host, Opts]),
+ case ftp:open(Host, Opts) of
{ok, _} = OK ->
OK;
{error, Reason} ->
diff --git a/lib/inets/test/ftp_ticket_test.erl b/lib/inets/test/ftp_ticket_test.erl
index 6748df03bb..fe4ab35728 100644
--- a/lib/inets/test/ftp_ticket_test.erl
+++ b/lib/inets/test/ftp_ticket_test.erl
@@ -35,17 +35,27 @@ end_per_testcase(Case, Config) ->
ftp_suite_lib:end_per_testcase(Case, Config).
-all(suite) ->
- {conf,init,tickets(),fin}.
+all() ->
+ tickets().
-init(Config) ->
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) ->
?LIB_MOD:ftpd_init(ticket_test, Config).
-tickets() ->
+tickets() ->
[ticket_6035].
-fin(Config) ->
+end_per_suite(Config) ->
?LIB_MOD:ftpd_fin(Config).
ticket_6035(X) -> ?LIB_MOD:ticket_6035(X).
diff --git a/lib/inets/test/ftp_windows_2003_server_test.erl b/lib/inets/test/ftp_windows_2003_server_test.erl
index d24318d04f..57f1ae8358 100644
--- a/lib/inets/test/ftp_windows_2003_server_test.erl
+++ b/lib/inets/test/ftp_windows_2003_server_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,22 +86,29 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
%% Test cases starts here.
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/ftp_windows_xp_test.erl b/lib/inets/test/ftp_windows_xp_test.erl
index bc161e4f6a..06d919ba00 100644
--- a/lib/inets/test/ftp_windows_xp_test.erl
+++ b/lib/inets/test/ftp_windows_xp_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,7 +22,7 @@
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(LIB_MOD,ftp_suite_lib).
-define(CASE_WRAPPER(_A_,_B_,_C_),?LIB_MOD:wrapper(_A_,_B_,_C_)).
@@ -86,20 +86,27 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test ftp client"];
+all() ->
+ [open, open_port, {group, passive}, {group, active},
+ api_missuse, not_owner, {group, progress_report}].
+
+groups() ->
+ [{passive, [], ftp_suite_lib:passive(suite)},
+ {active, [], ftp_suite_lib:active(suite)},
+ {progress_report, [],
+ ftp_suite_lib:progress_report(suite)}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [open, open_port, passive, active, api_missuse,
- not_owner, progress_report].
open(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open/1).
open_port(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:open_port/1).
-passive(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:passive/1).
-active(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:active/1).
api_missuse(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:api_missuse/1).
not_owner(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:not_owner/1).
-progress_report(X) -> ?CASE_WRAPPER(?PLATFORM,X,fun ?LIB_MOD:progress_report/1).
passive_user(X) -> ?LIB_MOD:passive_user(X).
passive_pwd(X) -> ?LIB_MOD:passive_pwd(X).
diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl
index 9559317640..931ac6e024 100644
--- a/lib/inets/test/http_format_SUITE.erl
+++ b/lib/inets/test/http_format_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -21,28 +21,49 @@
-module(http_format_SUITE).
-author('[email protected]').
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include("http_internal.hrl").
%% Test server specific exports
--export([all/1, init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
--export([chunk/1, chunk_decode/1, chunk_encode/1,
- chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1,
- chunk_decode_empty_chunk_otp_6511/1,
- chunk_decode_trailer/1,
- http_response/1, http_request/1, validate_request_line/1, script/1,
- esi_parse_headers/1, cgi_parse_headers/1,
- is_absolut_uri/1, convert_netscapecookie_date/1]).
-
-all(doc) ->
- ["Test library functions to the http client and server."];
-all(suite) ->
- [chunk,
- http_response, http_request, validate_request_line,
- script, is_absolut_uri, convert_netscapecookie_date].
+-export([ chunk_decode/1, chunk_encode/1,
+ chunk_extensions_otp_6005/1, chunk_decode_otp_6264/1,
+ chunk_decode_empty_chunk_otp_6511/1,
+ chunk_decode_trailer/1,
+ http_response/1, http_request/1, validate_request_line/1,
+ esi_parse_headers/1, cgi_parse_headers/1,
+ is_absolut_uri/1, convert_netscapecookie_date/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, chunk}, http_response, http_request,
+ validate_request_line, {group, script}, is_absolut_uri,
+ convert_netscapecookie_date].
+
+groups() ->
+ [{script, [], [esi_parse_headers, cgi_parse_headers]},
+ {chunk, [],
+ [chunk_decode, chunk_encode, chunk_extensions_otp_6005,
+ chunk_decode_otp_6264,
+ chunk_decode_empty_chunk_otp_6511,
+ chunk_decode_trailer]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_, Config) ->
Dog = test_server:timetrap(?t:minutes(1)),
@@ -57,17 +78,7 @@ end_per_testcase(_, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-script(doc) ->
- ["Test header parsing in esi/cgi functionality."];
-script(suite) ->
- [esi_parse_headers, cgi_parse_headers].
-
-chunk(doc) ->
- ["Test chunk encoding"];
-chunk(suite) ->
- [chunk_decode, chunk_encode, chunk_extensions_otp_6005,
- chunk_decode_otp_6264, chunk_decode_empty_chunk_otp_6511,
- chunk_decode_trailer].
+
%%-------------------------------------------------------------------------
chunk_decode(doc) ->
@@ -567,6 +578,12 @@ convert_netscapecookie_date(Config) when is_list(Config) ->
http_util:convert_netscapecookie_date("Sun, 12-Nov-2006 08:59:38 GMT"),
{{2006,12,12},{8,59,38}} =
http_util:convert_netscapecookie_date("Sun, 12-Dec-2006 08:59:38 GMT"),
+ {{2006,12,12},{8,59,38}} =
+ http_util:convert_netscapecookie_date("Sun 12-Dec-2006 08:59:38 GMT"),
+ {{2006,12,12},{8,59,38}} =
+ http_util:convert_netscapecookie_date("Sun, 12-Dec-06 08:59:38 GMT"),
+ {{2006,12,12},{8,59,38}} =
+ http_util:convert_netscapecookie_date("Sun 12-Dec-06 08:59:38 GMT"),
ok.
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index aa65fb1197..2c8febf5ed 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -24,7 +24,7 @@
-module(httpc_SUITE).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include_lib("kernel/include/file.hrl").
@@ -59,58 +59,47 @@
%% or a skip tuple if the platform is not supported.
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test the http client in the intes application."];
-all(suite) ->
- [
- proxy_options,
- proxy_head,
- proxy_get,
- proxy_trace,
- proxy_post,
- proxy_put,
- proxy_delete,
- proxy_auth,
- proxy_headers,
- proxy_emulate_lower_versions,
- http_options,
- http_head,
- http_get,
- http_post,
- http_dummy_pipe,
- http_inets_pipe,
- http_trace,
- http_async,
- http_save_to_file,
- http_save_to_file_async,
- http_headers,
- http_headers_dummy,
- http_bad_response,
- ssl_head,
- ssl_get,
- ssl_trace,
- http_redirect,
- http_redirect_loop,
- http_internal_server_error,
- http_userinfo,
- http_cookie,
- http_server_does_not_exist,
- http_invalid_http,
- http_emulate_lower_versions,
- http_relaxed,
- page_does_not_exist,
- proxy_page_does_not_exist,
- proxy_https_not_supported,
- http_stream,
- http_stream_once,
- proxy_stream,
- parse_url,
- options,
- ipv6,
- headers_as_is,
- tickets
- ].
-
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [proxy_options, proxy_head, proxy_get, proxy_trace,
+ proxy_post, proxy_put, proxy_delete, proxy_auth,
+ proxy_headers, proxy_emulate_lower_versions,
+ http_options, http_head, http_get, http_post,
+ http_dummy_pipe, http_inets_pipe, http_trace,
+ http_async, http_save_to_file, http_save_to_file_async,
+ http_headers, http_headers_dummy, http_bad_response,
+ ssl_head, ossl_head, essl_head, ssl_get, ossl_get,
+ essl_get, ssl_trace, ossl_trace, essl_trace,
+ http_redirect, http_redirect_loop,
+ http_internal_server_error, http_userinfo, http_cookie,
+ http_server_does_not_exist, http_invalid_http,
+ http_emulate_lower_versions, http_relaxed,
+ page_does_not_exist, proxy_page_does_not_exist,
+ proxy_https_not_supported, http_stream,
+ http_stream_once, proxy_stream, parse_url, options,
+ ipv6, headers_as_is, {group, tickets}].
+
+groups() ->
+ [{tickets, [],
+ [hexed_query_otp_6191, empty_body_otp_6243,
+ empty_response_header_otp_6830,
+ transfer_encoding_otp_6807, proxy_not_modified_otp_6821,
+ no_content_204_otp_6982, missing_CR_otp_7304,
+ {group, otp_7883}, {group, otp_8154}, {group, otp_8106},
+ otp_8056, otp_8352, otp_8371, otp_8739]},
+ {otp_7883, [], [otp_7883_1, otp_7883_2]},
+ {otp_8154, [], [otp_8154_1]},
+ {otp_8106, [],
+ [otp_8106_pid, otp_8106_fun, otp_8106_mfa]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
%% Config - [tuple()]
@@ -179,49 +168,68 @@ init_per_testcase(otp_8154_1 = Case, Config) ->
init_per_testcase(Case, Config) ->
init_per_testcase(Case, 2, Config).
+init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) ->
+ tsp("init_per_testcase_ssl -> stop ssl"),
+ application:stop(ssl),
+ Config2 = lists:keydelete(local_ssl_server, 1, Config),
+ %% Will start inets
+ tsp("init_per_testcase_ssl -> try start http server (including inets)"),
+ Server = inets_test_lib:start_http_server(
+ filename:join(PrivDir, SslConfFile), Tag),
+ tsp("init_per_testcase -> Server: ~p", [Server]),
+ [{local_ssl_server, Server} | Config2].
+
init_per_testcase(Case, Timeout, Config) ->
- io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n",
+ io:format(user, "~n~n*** INIT ~w:[~w][~w] ***~n~n",
[?MODULE, Timeout, Case]),
- PrivDir = ?config(priv_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ tsp("init_per_testcase -> stop inets"),
application:stop(inets),
- Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)),
- TmpConfig = lists:keydelete(watchdog, 1, Config),
- IpConfFile = integer_to_list(?IP_PORT) ++ ".conf",
+ Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)),
+ TmpConfig = lists:keydelete(watchdog, 1, Config),
+ IpConfFile = integer_to_list(?IP_PORT) ++ ".conf",
SslConfFile = integer_to_list(?SSL_PORT) ++ ".conf",
+ %% inets:enable_trace(max, io, httpd),
+ %% inets:enable_trace(max, io, httpc),
+ inets:enable_trace(max, io, all),
+
NewConfig =
case atom_to_list(Case) of
- "ssl" ++ _ ->
- application:stop(ssl),
- TmpConfig2 =
- lists:keydelete(local_ssl_server, 1, TmpConfig),
- %% Will start inets
- Server =
- inets_test_lib:start_http_server(
- filename:join(PrivDir, SslConfFile)),
- [{watchdog, Dog}, {local_ssl_server, Server} | TmpConfig2];
+ [$s, $s, $l | _] ->
+ init_per_testcase_ssl(ssl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+
+ [$o, $s, $s, $l | _] ->
+ init_per_testcase_ssl(ossl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+
+ [$e, $s, $s, $l | _] ->
+ init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]);
+
"proxy" ++ Rest ->
- case Rest of
- "_https_not_supported" ->
- inets:start(),
- case (catch application:start(ssl)) of
- ok ->
- [{watchdog, Dog} | TmpConfig];
- _ ->
- [{skip,
- "SSL does not seem to be supported"}
- | TmpConfig]
- end;
- _ ->
- case is_proxy_available(?PROXY, ?PROXY_PORT) of
- true ->
- inets:start(),
- [{watchdog, Dog} | TmpConfig];
- false ->
- [{skip, "Failed to contact proxy"} |
- TmpConfig]
- end
- end;
+ case Rest of
+ "_https_not_supported" ->
+ tsp("init_per_testcase -> [proxy case] start inets"),
+ inets:start(),
+ tsp("init_per_testcase -> [proxy case] start ssl"),
+ application:start(crypto),
+ application:start(public_key),
+ case (catch application:start(ssl)) of
+ ok ->
+ [{watchdog, Dog} | TmpConfig];
+ _ ->
+ [{skip, "SSL does not seem to be supported"}
+ | TmpConfig]
+ end;
+ _ ->
+ case is_proxy_available(?PROXY, ?PROXY_PORT) of
+ true ->
+ inets:start(),
+ [{watchdog, Dog} | TmpConfig];
+ false ->
+ [{skip, "Failed to contact proxy"} |
+ TmpConfig]
+ end
+ end;
_ ->
TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig),
Server =
@@ -231,13 +239,17 @@ init_per_testcase(Case, Timeout, Config) ->
[{watchdog, Dog}, {local_server, Server} | TmpConfig2]
end,
- http:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
- ["localhost", ?IPV6_LOCAL_HOST]}}]),
- inets:enable_trace(max, io, httpc),
- %% inets:enable_trace(max, io, all),
- %% snmp:set_trace([gen_tcp, inet_tcp, prim_inet]),
+ %% httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
+ %% ["localhost", ?IPV6_LOCAL_HOST]}}]),
+
+ httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT},
+ ["localhost", ?IPV6_LOCAL_HOST]}},
+ {ipfamily, inet6fb4}]),
+
+ %% snmp:set_trace([gen_tcp]),
NewConfig.
+
%%--------------------------------------------------------------------
%% Function: end_per_testcase(Case, Config) -> _
%% Case - atom()
@@ -268,24 +280,6 @@ finish(Config) ->
%% Test cases starts here.
%%-------------------------------------------------------------------------
-tickets(doc) ->
- ["."];
-tickets(suite) ->
- [
- hexed_query_otp_6191,
- empty_body_otp_6243,
- empty_response_header_otp_6830,
- transfer_encoding_otp_6807,
- proxy_not_modified_otp_6821,
- no_content_204_otp_6982,
- missing_CR_otp_7304,
- otp_7883,
- otp_8154,
- otp_8106,
- otp_8056,
- otp_8352,
- otp_8371
- ].
%%-------------------------------------------------------------------------
@@ -306,7 +300,7 @@ http_head(Config) when is_list(Config) ->
ok ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- case http:request(head, {URL, []}, [], []) of
+ case httpc:request(head, {URL, []}, [], []) of
{ok, {{_,200,_}, [_ | _], []}} ->
ok;
{ok, WrongReply} ->
@@ -337,7 +331,7 @@ http_get(Config) when is_list(Config) ->
HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}],
Options1 = [],
Body =
- case http:request(Method, Request, HttpOptions1, Options1) of
+ case httpc:request(Method, Request, HttpOptions1, Options1) of
{ok, {{_,200,_}, [_ | _], ReplyBody = [_ | _]}} ->
ReplyBody;
{ok, UnexpectedReply1} ->
@@ -346,12 +340,12 @@ http_get(Config) when is_list(Config) ->
tsf({bad_reply, Error1})
end,
- %% eqvivivalent to http:request(get, {URL, []}, [], []),
+ %% eqvivivalent to httpc:request(get, {URL, []}, [], []),
inets_test_lib:check_body(Body),
HttpOptions2 = [],
Options2 = [{body_format, binary}],
- case http:request(Method, Request, HttpOptions2, Options2) of
+ case httpc:request(Method, Request, HttpOptions2, Options2) of
{ok, {{_,200,_}, [_ | _], Bin}} when is_binary(Bin) ->
ok;
{ok, {{_,200,_}, [_ | _], BadBin}} ->
@@ -390,11 +384,11 @@ http_post(Config) when is_list(Config) ->
Body = lists:duplicate(100, "1"),
{ok, {{_,200,_}, [_ | _], [_ | _]}} =
- http:request(post, {URL, [{"expect","100-continue"}],
+ httpc:request(post, {URL, [{"expect","100-continue"}],
"text/plain", Body}, [], []),
{ok, {{_,504,_}, [_ | _], []}} =
- http:request(post, {URL, [{"expect","100-continue"}],
+ httpc:request(post, {URL, [{"expect","100-continue"}],
"text/plain", "foobar"}, [], []);
_ ->
{skip, "Failed to start local http-server"}
@@ -411,13 +405,13 @@ http_emulate_lower_versions(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
{ok, Body0} =
- http:request(get, {URL, []}, [{version, "HTTP/0.9"}], []),
+ httpc:request(get, {URL, []}, [{version, "HTTP/0.9"}], []),
inets_test_lib:check_body(Body0),
{ok, {{"HTTP/1.0", 200, _}, [_ | _], Body1 = [_ | _]}} =
- http:request(get, {URL, []}, [{version, "HTTP/1.0"}], []),
+ httpc:request(get, {URL, []}, [{version, "HTTP/1.0"}], []),
inets_test_lib:check_body(Body1),
{ok, {{"HTTP/1.1", 200, _}, [_ | _], Body2 = [_ | _]}} =
- http:request(get, {URL, []}, [{version, "HTTP/1.1"}], []),
+ httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []),
inets_test_lib:check_body(Body2);
_->
{skip, "Failed to start local http-server"}
@@ -431,24 +425,24 @@ http_relaxed(doc) ->
http_relaxed(suite) ->
[];
http_relaxed(Config) when is_list(Config) ->
- ok = http:set_options([{ipv6, disabled}]), % also test the old option
- %% ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
+ %% ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/missing_reason_phrase.html",
{error, Reason} =
- http:request(get, {URL, []}, [{relaxed, false}], []),
+ httpc:request(get, {URL, []}, [{relaxed, false}], []),
test_server:format("Not relaxed: ~p~n", [Reason]),
{ok, {{_, 200, _}, [_ | _], [_ | _]}} =
- http:request(get, {URL, []}, [{relaxed, true}], []),
+ httpc:request(get, {URL, []}, [{relaxed, true}], []),
DummyServerPid ! stop,
- ok = http:set_options([{ipv6, enabled}]),
- %% ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipv6, enabled}]),
+ %% ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -458,7 +452,7 @@ http_dummy_pipe(doc) ->
http_dummy_pipe(suite) ->
[];
http_dummy_pipe(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html",
@@ -466,7 +460,7 @@ http_dummy_pipe(Config) when is_list(Config) ->
test_pipeline(URL),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
http_inets_pipe(doc) ->
@@ -488,11 +482,11 @@ test_pipeline(URL) ->
p("test_pipeline -> entry with"
"~n URL: ~p", [URL]),
- http:set_options([{pipeline_timeout, 50000}]),
+ httpc:set_options([{pipeline_timeout, 50000}]),
p("test_pipeline -> issue (async) request 1"),
{ok, RequestId1} =
- http:request(get, {URL, []}, [], [{sync, false}]),
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
test_server:format("RequestId1: ~p~n", [RequestId1]),
p("test_pipeline -> RequestId1: ~p", [RequestId1]),
@@ -502,13 +496,13 @@ test_pipeline(URL) ->
p("test_pipeline -> issue (async) request 2"),
{ok, RequestId2} =
- http:request(get, {URL, []}, [], [{sync, false}]),
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
tsp("RequestId2: ~p", [RequestId2]),
p("test_pipeline -> RequestId2: ~p", [RequestId2]),
p("test_pipeline -> issue (sync) request 3"),
{ok, {{_,200,_}, [_ | _], [_ | _]}} =
- http:request(get, {URL, []}, [], []),
+ httpc:request(get, {URL, []}, [], []),
p("test_pipeline -> expect reply for (async) request 1 or 2"),
receive
@@ -544,18 +538,18 @@ test_pipeline(URL) ->
p("test_pipeline -> issue (async) request 4"),
{ok, RequestId3} =
- http:request(get, {URL, []}, [], [{sync, false}]),
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
tsp("RequestId3: ~p", [RequestId3]),
p("test_pipeline -> RequestId3: ~p", [RequestId3]),
p("test_pipeline -> issue (async) request 5"),
{ok, RequestId4} =
- http:request(get, {URL, []}, [], [{sync, false}]),
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
tsp("RequestId4: ~p~n", [RequestId4]),
p("test_pipeline -> RequestId4: ~p", [RequestId4]),
p("test_pipeline -> cancel (async) request 4"),
- ok = http:cancel_request(RequestId3),
+ ok = httpc:cancel_request(RequestId3),
p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"),
receive
@@ -607,7 +601,7 @@ http_trace(Config) when is_list(Config) ->
ok ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- case http:request(trace, {URL, []}, [], []) of
+ case httpc:request(trace, {URL, []}, [], []) of
{ok, {{_,200,_}, [_ | _], "TRACE /dummy.html" ++ _}} ->
ok;
{ok, {{_,200,_}, [_ | _], WrongBody}} ->
@@ -631,7 +625,7 @@ http_async(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
{ok, RequestId} =
- http:request(get, {URL, []}, [], [{sync, false}]),
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
Body =
receive
@@ -644,8 +638,8 @@ http_async(Config) when is_list(Config) ->
inets_test_lib:check_body(binary_to_list(Body)),
{ok, NewRequestId} =
- http:request(get, {URL, []}, [], [{sync, false}]),
- ok = http:cancel_request(NewRequestId),
+ httpc:request(get, {URL, []}, [], [{sync, false}]),
+ ok = httpc:cancel_request(NewRequestId),
receive
{http, {NewRequestId, _NewResult}} ->
test_server:fail(http_cancel_request_failed)
@@ -669,9 +663,9 @@ http_save_to_file(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
{ok, saved_to_file}
- = http:request(get, {URL, []}, [], [{stream, FilePath}]),
+ = httpc:request(get, {URL, []}, [], [{stream, FilePath}]),
{ok, Bin} = file:read_file(FilePath),
- {ok, {{_,200,_}, [_ | _], Body}} = http:request(URL),
+ {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL),
Bin == Body;
_ ->
{skip, "Failed to start local http-server"}
@@ -690,7 +684,7 @@ http_save_to_file_async(Config) when is_list(Config) ->
FilePath = filename:join(PrivDir, "dummy.html"),
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- {ok, RequestId} = http:request(get, {URL, []}, [],
+ {ok, RequestId} = httpc:request(get, {URL, []}, [],
[{stream, FilePath},
{sync, false}]),
receive
@@ -701,7 +695,7 @@ http_save_to_file_async(Config) when is_list(Config) ->
end,
{ok, Bin} = file:read_file(FilePath),
- {ok, {{_,200,_}, [_ | _], Body}} = http:request(URL),
+ {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL),
Bin == Body;
_ ->
{skip, "Failed to start local http-server"}
@@ -731,7 +725,7 @@ http_headers(Config) when is_list(Config) ->
Date = httpd_util:rfc1123_date({date(), time()}),
{ok, {{_,200,_}, [_ | _], [_ | _]}} =
- http:request(get, {URL, [{"If-Modified-Since",
+ httpc:request(get, {URL, [{"If-Modified-Since",
Mod},
{"From","[email protected]"},
{"Date", Date}
@@ -742,7 +736,7 @@ http_headers(Config) when is_list(Config) ->
CreatedSec+1)),
{ok, {{_,200,_}, [_ | _], [_ | _]}} =
- http:request(get, {URL, [{"If-UnModified-Since",
+ httpc:request(get, {URL, [{"If-UnModified-Since",
Mod1}
]}, [], []),
@@ -750,12 +744,12 @@ http_headers(Config) when is_list(Config) ->
{ok, {{_,200,_}, [_ | _], [_ | _]}} =
- http:request(get, {URL, [{"If-Match",
+ httpc:request(get, {URL, [{"If-Match",
Tag}
]}, [], []),
{ok, {{_,200,_}, [_ | _], _}} =
- http:request(get, {URL, [{"If-None-Match",
+ httpc:request(get, {URL, [{"If-None-Match",
"NotEtag,NeihterEtag"},
{"Connection", "Close"}
]}, [], []),
@@ -773,7 +767,7 @@ http_headers_dummy(doc) ->
http_headers_dummy(suite) ->
[];
http_headers_dummy(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html",
@@ -789,7 +783,7 @@ http_headers_dummy(Config) when is_list(Config) ->
%% that the client header-handling code. This would not
%% be a vaild http-request!
{ok, {{_,200,_}, [_ | _], [_|_]}} =
- http:request(post,
+ httpc:request(post,
{URL,
[{"Via",
"1.0 fred, 1.1 nowhere.com (Apache/1.1)"},
@@ -828,7 +822,7 @@ http_headers_dummy(Config) when is_list(Config) ->
], "text/plain", FooBar},
[], []),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -838,21 +832,21 @@ http_bad_response(doc) ->
http_bad_response(suite) ->
[];
http_bad_response(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html",
URL1 = ?URL_START ++ integer_to_list(Port) ++ "/wrong_statusline.html",
- {error, timeout} = http:request(get, {URL, []}, [{timeout, 400}], []),
+ {error, timeout} = httpc:request(get, {URL, []}, [{timeout, 400}], []),
- {error, Reason} = http:request(URL1),
+ {error, Reason} = httpc:request(URL1),
test_server:format("Wrong Statusline: ~p~n", [Reason]),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -862,69 +856,168 @@ ssl_head(doc) ->
ssl_head(suite) ->
[];
ssl_head(Config) when is_list(Config) ->
+ ssl_head(ssl, Config).
+
+ossl_head(doc) ->
+ ["Same as http_head/1 but over ssl sockets."];
+ossl_head(suite) ->
+ [];
+ossl_head(Config) when is_list(Config) ->
+ ssl_head(ossl, Config).
+
+essl_head(doc) ->
+ ["Same as http_head/1 but over ssl sockets."];
+essl_head(suite) ->
+ [];
+essl_head(Config) when is_list(Config) ->
+ ssl_head(essl, Config).
+
+ssl_head(SslTag, Config) ->
+ tsp("ssl_head -> entry with"
+ "~n SslTag: ~p"
+ "~n Config: ~p", [SslTag, Config]),
case ?config(local_ssl_server, Config) of
ok ->
- DataDir = ?config(data_dir, Config),
- Port = ?config(local_ssl_port, Config),
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
+ DataDir = ?config(data_dir, Config),
+ Port = ?config(local_ssl_port, Config),
+ URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
+ SSLConfig =
+ case SslTag of
+ ssl ->
+ SSLOptions;
+ ossl ->
+ {ossl, SSLOptions};
+ essl ->
+ {essl, SSLOptions}
+ end,
+ tsp("ssl_head -> make request using: "
+ "~n URL: ~p"
+ "~n SslTag: ~p"
+ "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]),
{ok, {{_,200, _}, [_ | _], []}} =
- http:request(head, {URL, []}, [{ssl, SSLOptions}], []);
+ httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []);
{ok, _} ->
- {skip, "Failed to start local http-server"};
+ {skip, "local http-server not started"};
_ ->
- {skip, "Failed to start SSL"}
+ {skip, "SSL not started"}
end.
+
+
%%-------------------------------------------------------------------------
ssl_get(doc) ->
["Same as http_get/1 but over ssl sockets."];
ssl_get(suite) ->
[];
ssl_get(Config) when is_list(Config) ->
+ ssl_get(ssl, Config).
+
+ossl_get(doc) ->
+ ["Same as http_get/1 but over ssl sockets."];
+ossl_get(suite) ->
+ [];
+ossl_get(Config) when is_list(Config) ->
+ ssl_get(ossl, Config).
+
+essl_get(doc) ->
+ ["Same as http_get/1 but over ssl sockets."];
+essl_get(suite) ->
+ [];
+essl_get(Config) when is_list(Config) ->
+ ssl_get(essl, Config).
+
+ssl_get(SslTag, Config) when is_list(Config) ->
case ?config(local_ssl_server, Config) of
ok ->
- DataDir = ?config(data_dir, Config),
- Port = ?config(local_ssl_port, Config),
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
+ DataDir = ?config(data_dir, Config),
+ Port = ?config(local_ssl_port, Config),
+ URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
- {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} =
- http:request(get, {URL, []}, [{ssl, SSLOptions}], []),
- inets_test_lib:check_body(Body);
+ SSLConfig =
+ case SslTag of
+ ssl ->
+ SSLOptions;
+ ossl ->
+ {ossl, SSLOptions};
+ essl ->
+ {essl, SSLOptions}
+ end,
+ tsp("ssl_get -> make request using: "
+ "~n URL: ~p"
+ "~n SslTag: ~p"
+ "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]),
+ {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} =
+ httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []),
+ inets_test_lib:check_body(Body);
{ok, _} ->
{skip, "Failed to start local http-server"};
_ ->
{skip, "Failed to start SSL"}
end.
+
+
%%-------------------------------------------------------------------------
ssl_trace(doc) ->
["Same as http_trace/1 but over ssl sockets."];
ssl_trace(suite) ->
[];
ssl_trace(Config) when is_list(Config) ->
+ ssl_trace(ssl, Config).
+
+ossl_trace(doc) ->
+ ["Same as http_trace/1 but over ssl sockets."];
+ossl_trace(suite) ->
+ [];
+ossl_trace(Config) when is_list(Config) ->
+ ssl_trace(ossl, Config).
+
+essl_trace(doc) ->
+ ["Same as http_trace/1 but over ssl sockets."];
+essl_trace(suite) ->
+ [];
+essl_trace(Config) when is_list(Config) ->
+ ssl_trace(essl, Config).
+
+ssl_trace(SslTag, Config) when is_list(Config) ->
case ?config(local_ssl_server, Config) of
ok ->
- DataDir = ?config(data_dir, Config),
- Port = ?config(local_ssl_port, Config),
- URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
- CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
+ DataDir = ?config(data_dir, Config),
+ Port = ?config(local_ssl_port, Config),
+ URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ CertFile = filename:join(DataDir, "ssl_client_cert.pem"),
SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}],
- case http:request(trace, {URL, []}, [{ssl, SSLOptions}], []) of
+ SSLConfig =
+ case SslTag of
+ ssl ->
+ SSLOptions;
+ ossl ->
+ {ossl, SSLOptions};
+ essl ->
+ {essl, SSLOptions}
+ end,
+ tsp("ssl_trace -> make request using: "
+ "~n URL: ~p"
+ "~n SslTag: ~p"
+ "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]),
+ case httpc:request(trace, {URL, []}, [{ssl, SSLConfig}], []) of
{ok, {{_,200, _}, [_ | _], "TRACE /dummy.html" ++ _}} ->
ok;
{ok, {{_,200,_}, [_ | _], WrongBody}} ->
- test_server:fail({wrong_body, WrongBody});
+ tsf({wrong_body, WrongBody});
{ok, WrongReply} ->
- test_server:fail({wrong_reply, WrongReply});
+ tsf({wrong_reply, WrongReply});
Error ->
- test_server:fail({failed, Error})
+ tsf({failed, Error})
end;
{ok, _} ->
{skip, "Failed to start local http-server"};
_ ->
{skip, "Failed to start SSL"}
end.
+
+
%%-------------------------------------------------------------------------
http_redirect(doc) ->
["Test redirect with dummy server as httpd does not implement"
@@ -937,7 +1030,7 @@ http_redirect(Config) when is_list(Config) ->
case ?config(local_server, Config) of
ok ->
tsp("http_redirect -> set ipfamily option to inet"),
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
tsp("http_redirect -> start dummy server inet"),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
@@ -948,29 +1041,29 @@ http_redirect(Config) when is_list(Config) ->
tsp("http_redirect -> issue request 1: "
"~n ~p", [URL300]),
{ok, {{_,200,_}, [_ | _], [_|_]}}
- = http:request(get, {URL300, []}, [], []),
+ = httpc:request(get, {URL300, []}, [], []),
tsp("http_redirect -> issue request 2: "
"~n ~p", [URL300]),
{ok, {{_,300,_}, [_ | _], _}} =
- http:request(get, {URL300, []}, [{autoredirect, false}], []),
+ httpc:request(get, {URL300, []}, [{autoredirect, false}], []),
URL301 = ?URL_START ++ integer_to_list(Port) ++ "/301.html",
tsp("http_redirect -> issue request 3: "
"~n ~p", [URL301]),
{ok, {{_,200,_}, [_ | _], [_|_]}}
- = http:request(get, {URL301, []}, [], []),
+ = httpc:request(get, {URL301, []}, [], []),
tsp("http_redirect -> issue request 4: "
"~n ~p", [URL301]),
{ok, {{_,200,_}, [_ | _], []}}
- = http:request(head, {URL301, []}, [], []),
+ = httpc:request(head, {URL301, []}, [], []),
tsp("http_redirect -> issue request 5: "
"~n ~p", [URL301]),
{ok, {{_,301,_}, [_ | _], [_|_]}}
- = http:request(post, {URL301, [],"text/plain", "foobar"},
+ = httpc:request(post, {URL301, [],"text/plain", "foobar"},
[], []),
URL302 = ?URL_START ++ integer_to_list(Port) ++ "/302.html",
@@ -978,8 +1071,8 @@ http_redirect(Config) when is_list(Config) ->
tsp("http_redirect -> issue request 6: "
"~n ~p", [URL302]),
{ok, {{_,200,_}, [_ | _], [_|_]}}
- = http:request(get, {URL302, []}, [], []),
- case http:request(get, {URL302, []}, [], []) of
+ = httpc:request(get, {URL302, []}, [], []),
+ case httpc:request(get, {URL302, []}, [], []) of
{ok, Reply7} ->
case Reply7 of
{{_,200,_}, [_ | _], [_|_]} ->
@@ -1006,12 +1099,12 @@ http_redirect(Config) when is_list(Config) ->
tsp("http_redirect -> issue request 7: "
"~n ~p", [URL302]),
{ok, {{_,200,_}, [_ | _], []}}
- = http:request(head, {URL302, []}, [], []),
+ = httpc:request(head, {URL302, []}, [], []),
tsp("http_redirect -> issue request 8: "
"~n ~p", [URL302]),
{ok, {{_,302,_}, [_ | _], [_|_]}}
- = http:request(post, {URL302, [],"text/plain", "foobar"},
+ = httpc:request(post, {URL302, [],"text/plain", "foobar"},
[], []),
URL307 = ?URL_START ++ integer_to_list(Port) ++ "/307.html",
@@ -1019,23 +1112,23 @@ http_redirect(Config) when is_list(Config) ->
tsp("http_redirect -> issue request 9: "
"~n ~p", [URL307]),
{ok, {{_,200,_}, [_ | _], [_|_]}}
- = http:request(get, {URL307, []}, [], []),
+ = httpc:request(get, {URL307, []}, [], []),
tsp("http_redirect -> issue request 10: "
"~n ~p", [URL307]),
{ok, {{_,200,_}, [_ | _], []}}
- = http:request(head, {URL307, []}, [], []),
+ = httpc:request(head, {URL307, []}, [], []),
tsp("http_redirect -> issue request 11: "
"~n ~p", [URL307]),
{ok, {{_,307,_}, [_ | _], [_|_]}}
- = http:request(post, {URL307, [],"text/plain", "foobar"},
+ = httpc:request(post, {URL307, [],"text/plain", "foobar"},
[], []),
tsp("http_redirect -> stop dummy server"),
DummyServerPid ! stop,
tsp("http_redirect -> reset ipfamily option (to inet6fb4)"),
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
tsp("http_redirect -> done"),
ok;
@@ -1051,15 +1144,15 @@ http_redirect_loop(doc) ->
http_redirect_loop(suite) ->
[];
http_redirect_loop(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html",
{ok, {{_,300,_}, [_ | _], _}}
- = http:request(get, {URL, []}, [], []),
+ = httpc:request(get, {URL, []}, [], []),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
%%-------------------------------------------------------------------------
@@ -1068,13 +1161,13 @@ http_internal_server_error(doc) ->
http_internal_server_error(suite) ->
[];
http_internal_server_error(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html",
{ok, {{_,500,_}, [_ | _], _}}
- = http:request(get, {URL500, []}, [], []),
+ = httpc:request(get, {URL500, []}, [], []),
URL503 = ?URL_START ++ integer_to_list(Port) ++ "/503.html",
@@ -1084,16 +1177,16 @@ http_internal_server_error(Config) when is_list(Config) ->
ets:insert(unavailable, {503, unavailable}),
{ok, {{_,200, _}, [_ | _], [_|_]}} =
- http:request(get, {URL503, []}, [], []),
+ httpc:request(get, {URL503, []}, [], []),
ets:insert(unavailable, {503, long_unavailable}),
{ok, {{_,503, _}, [_ | _], [_|_]}} =
- http:request(get, {URL503, []}, [], []),
+ httpc:request(get, {URL503, []}, [], []),
ets:delete(unavailable),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1103,7 +1196,7 @@ http_userinfo(doc) ->
http_userinfo(suite) ->
[];
http_userinfo(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
@@ -1111,16 +1204,16 @@ http_userinfo(Config) when is_list(Config) ->
++ integer_to_list(Port) ++ "/userinfo.html",
{ok, {{_,200,_}, [_ | _], _}}
- = http:request(get, {URLAuth, []}, [], []),
+ = httpc:request(get, {URLAuth, []}, [], []),
URLUnAuth = "http://alladin:foobar@localhost:"
++ integer_to_list(Port) ++ "/userinfo.html",
{ok, {{_,401, _}, [_ | _], _}} =
- http:request(get, {URLUnAuth, []}, [], []),
+ httpc:request(get, {URLUnAuth, []}, [], []),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1130,7 +1223,7 @@ http_cookie(doc) ->
http_cookie(suite) ->
[];
http_cookie(Config) when is_list(Config) ->
- ok = http:set_options([{cookies, enabled}, {ipfamily, inet}]),
+ ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URLStart = ?URL_START
@@ -1139,19 +1232,19 @@ http_cookie(Config) when is_list(Config) ->
URLCookie = URLStart ++ "/cookie.html",
{ok, {{_,200,_}, [_ | _], [_|_]}}
- = http:request(get, {URLCookie, []}, [], []),
+ = httpc:request(get, {URLCookie, []}, [], []),
ets:new(cookie, [named_table, public, set]),
ets:insert(cookie, {cookies, true}),
{ok, {{_,200,_}, [_ | _], [_|_]}}
- = http:request(get, {URLStart ++ "/", []}, [], []),
+ = httpc:request(get, {URLStart ++ "/", []}, [], []),
ets:delete(cookie),
- ok = http:set_options([{cookies, disabled}, {ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{cookies, disabled}]),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
%%-------------------------------------------------------------------------
@@ -1162,7 +1255,7 @@ proxy_options(suite) ->
proxy_options(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(options, {?PROXY_URL, []}, [], []) of
+ case httpc:request(options, {?PROXY_URL, []}, [], []) of
{ok, {{_,200,_}, Headers, _}} ->
case lists:keysearch("allow", 1, Headers) of
{value, {"allow", _}} ->
@@ -1186,7 +1279,7 @@ proxy_head(suite) ->
proxy_head(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(head, {?PROXY_URL, []}, [], []) of
+ case httpc:request(head, {?PROXY_URL, []}, [], []) of
{ok, {{_,200, _}, [_ | _], []}} ->
ok;
Unexpected ->
@@ -1205,7 +1298,7 @@ proxy_get(suite) ->
proxy_get(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(get, {?PROXY_URL, []}, [], []) of
+ case httpc:request(get, {?PROXY_URL, []}, [], []) of
{ok, {{_,200,_}, [_ | _], Body = [_ | _]}} ->
inets_test_lib:check_body(Body);
Unexpected ->
@@ -1257,7 +1350,7 @@ proxy_emulate_lower_versions(Config) when is_list(Config) ->
end.
pelv_get(Version) ->
- http:request(get, {?PROXY_URL, []}, [{version, Version}], []).
+ httpc:request(get, {?PROXY_URL, []}, [{version, Version}], []).
%%-------------------------------------------------------------------------
proxy_trace(doc) ->
@@ -1266,7 +1359,7 @@ proxy_trace(suite) ->
[];
proxy_trace(Config) when is_list(Config) ->
%%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} =
- %% http:request(trace, {?PROXY_URL, []}, [], []),
+ %% httpc:request(trace, {?PROXY_URL, []}, [], []),
{skip, "HTTP TRACE is no longer allowed on the ?PROXY_URL server due "
"to security reasons"}.
@@ -1281,7 +1374,7 @@ proxy_post(suite) ->
proxy_post(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(post, {?PROXY_URL, [],
+ case httpc:request(post, {?PROXY_URL, [],
"text/plain", "foobar"}, [],[]) of
{ok, {{_,405,_}, [_ | _], [_ | _]}} ->
ok;
@@ -1303,7 +1396,7 @@ proxy_put(suite) ->
proxy_put(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
- case http:request(put, {"http://www.erlang.org/foobar.html", [],
+ case httpc:request(put, {"http://www.erlang.org/foobar.html", [],
"html", "<html> <body><h1> foo </h1>"
"<p>bar</p> </body></html>"}, [], []) of
{ok, {{_,405,_}, [_ | _], [_ | _]}} ->
@@ -1328,7 +1421,7 @@ proxy_delete(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
URL = ?PROXY_URL ++ "/foobar.html",
- case http:request(delete, {URL, []}, [], []) of
+ case httpc:request(delete, {URL, []}, [], []) of
{ok, {{_,404,_}, [_ | _], [_ | _]}} ->
ok;
Unexpected ->
@@ -1348,7 +1441,7 @@ proxy_headers(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
{ok, {{_,200,_}, [_ | _], [_ | _]}}
- = http:request(get, {?PROXY_URL,
+ = httpc:request(get, {?PROXY_URL,
[
{"Accept",
"text/*, text/html,"
@@ -1383,7 +1476,7 @@ proxy_auth(Config) when is_list(Config) ->
%% atleast the code for sending the header does not crash!
case ?config(skip, Config) of
undefined ->
- case http:request(get, {?PROXY_URL, []},
+ case httpc:request(get, {?PROXY_URL, []},
[{proxy_auth, {"foo", "bar"}}], []) of
{ok, {{_,200, _}, [_ | _], [_|_]}} ->
ok;
@@ -1403,7 +1496,7 @@ http_server_does_not_exist(suite) ->
[];
http_server_does_not_exist(Config) when is_list(Config) ->
{error, _} =
- http:request(get, {"http://localhost:" ++
+ httpc:request(get, {"http://localhost:" ++
integer_to_list(?NOT_IN_USE_PORT)
++ "/", []},[], []),
ok.
@@ -1418,7 +1511,7 @@ page_does_not_exist(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/doesnotexist.html",
{ok, {{_,404,_}, [_ | _], [_ | _]}}
- = http:request(get, {URL, []}, [], []),
+ = httpc:request(get, {URL, []}, [], []),
ok.
@@ -1432,7 +1525,7 @@ proxy_page_does_not_exist(Config) when is_list(Config) ->
undefined ->
URL = ?PROXY_URL ++ "/doesnotexist.html",
{ok, {{_,404,_}, [_ | _], [_ | _]}} =
- http:request(get, {URL, []}, [], []),
+ httpc:request(get, {URL, []}, [], []),
ok;
Reason ->
{skip, Reason}
@@ -1446,7 +1539,7 @@ proxy_https_not_supported(doc) ->
proxy_https_not_supported(suite) ->
[];
proxy_https_not_supported(Config) when is_list(Config) ->
- Result = http:request(get, {"https://login.yahoo.com", []}, [], []),
+ Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []),
case Result of
{error, Reason} ->
%% ok so far
@@ -1478,10 +1571,10 @@ http_stream(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
{ok, {{_,200,_}, [_ | _], Body}} =
- http:request(get, {URL, []}, [], []),
+ httpc:request(get, {URL, []}, [], []),
{ok, RequestId} =
- http:request(get, {URL, []}, [], [{sync, false},
+ httpc:request(get, {URL, []}, [], [{sync, false},
{stream, self}]),
receive
@@ -1506,7 +1599,7 @@ http_stream_once(Config) when is_list(Config) ->
"~n Config: ~p", [Config]),
p("http_stream_once -> set ipfamily to inet", []),
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
p("http_stream_once -> start dummy server", []),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
@@ -1521,18 +1614,18 @@ http_stream_once(Config) when is_list(Config) ->
p("http_stream_once -> stop dummy server", []),
DummyServerPid ! stop,
p("http_stream_once -> set ipfamily to inet6fb4", []),
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
p("http_stream_once -> done", []),
ok.
once(URL) ->
p("once -> issue sync request for ~p", [URL]),
{ok, {{_,200,_}, [_ | _], Body}} =
- http:request(get, {URL, []}, [], []),
+ httpc:request(get, {URL, []}, [], []),
p("once -> issue async (self stream) request for ~p", [URL]),
{ok, RequestId} =
- http:request(get, {URL, []}, [], [{sync, false},
+ httpc:request(get, {URL, []}, [], [{sync, false},
{stream, {self, once}}]),
p("once -> await stream_start reply for (async) request ~p", [RequestId]),
@@ -1576,10 +1669,10 @@ proxy_stream(Config) when is_list(Config) ->
case ?config(skip, Config) of
undefined ->
{ok, {{_,200,_}, [_ | _], Body}} =
- http:request(get, {?PROXY_URL, []}, [], []),
+ httpc:request(get, {?PROXY_URL, []}, [], []),
{ok, RequestId} =
- http:request(get, {?PROXY_URL, []}, [],
+ httpc:request(get, {?PROXY_URL, []}, [],
[{sync, false}, {stream, self}]),
receive
@@ -1644,6 +1737,8 @@ parse_url(Config) when is_list(Config) ->
%%-------------------------------------------------------------------------
+ipv6() ->
+ [{require,ipv6_hosts}].
ipv6(doc) ->
["Test ipv6."];
ipv6(suite) ->
@@ -1652,14 +1747,14 @@ ipv6(Config) when is_list(Config) ->
{ok, Hostname} = inet:gethostname(),
case lists:member(list_to_atom(Hostname),
- ?config(ipv6_hosts, Config)) of
+ ct:get_config(ipv6_hosts)) of
true ->
{DummyServerPid, Port} = dummy_server(self(), ipv6),
URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++
integer_to_list(Port) ++ "/foobar.html",
{ok, {{_,200,_}, [_ | _], [_|_]}} =
- http:request(get, {URL, []}, [], []),
+ httpc:request(get, {URL, []}, [], []),
DummyServerPid ! stop,
ok;
@@ -1677,11 +1772,11 @@ headers_as_is(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
{ok, {{_,200,_}, [_|_], [_|_]}} =
- http:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]},
+ httpc:request(get, {URL, [{"Host", "localhost"},{"Te", ""}]},
[], [{headers_as_is, true}]),
{ok, {{_,400,_}, [_|_], [_|_]}} =
- http:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]),
+ httpc:request(get, {URL, [{"Te", ""}]},[], [{headers_as_is, true}]),
ok.
@@ -1696,13 +1791,13 @@ options(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
{ok, {{_,200,_}, [_ | _], Bin}}
- = http:request(get, {URL, []}, [{foo, bar}],
+ = httpc:request(get, {URL, []}, [{foo, bar}],
%% Ignore unknown options
[{body_format, binary}, {foo, bar}]),
true = is_binary(Bin),
{ok, {200, [_|_]}}
- = http:request(get, {URL, []}, [{timeout, infinity}],
+ = httpc:request(get, {URL, []}, [{timeout, infinity}],
[{full_result, false}]);
_ ->
{skip, "Failed to start local http-server"}
@@ -1715,17 +1810,17 @@ http_invalid_http(doc) ->
http_invalid_http(suite) ->
[];
http_invalid_http(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html",
{error, {could_not_parse_as_http, _} = Reason} =
- http:request(get, {URL, []}, [], []),
+ httpc:request(get, {URL, []}, [], []),
test_server:format("Parse error: ~p ~n", [Reason]),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1762,7 +1857,7 @@ empty_body_otp_6243(Config) when is_list(Config) ->
Port = ?config(local_port, Config),
URL = ?URL_START ++ integer_to_list(Port) ++ "/empty.html",
{ok, {{_,200,_}, [_ | _], []}} =
- http:request(get, {URL, []}, [{timeout, 500}], []).
+ httpc:request(get, {URL, []}, [{timeout, 500}], []).
%%-------------------------------------------------------------------------
@@ -1772,14 +1867,14 @@ transfer_encoding_otp_6807(doc) ->
transfer_encoding_otp_6807(suite) ->
[];
transfer_encoding_otp_6807(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/capital_transfer_encoding.html",
- {ok, {{_,200,_}, [_|_], [_ | _]}} = http:request(URL),
+ {ok, {{_,200,_}, [_|_], [_ | _]}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1805,13 +1900,13 @@ empty_response_header_otp_6830(doc) ->
empty_response_header_otp_6830(suite) ->
[];
empty_response_header_otp_6830(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html",
- {ok, {{_,200,_}, [], [_ | _]}} = http:request(URL),
+ {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1822,13 +1917,13 @@ no_content_204_otp_6982(doc) ->
no_content_204_otp_6982(suite) ->
[];
no_content_204_otp_6982(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html",
- {ok, {{_,204,_}, [], []}} = http:request(URL),
+ {ok, {{_,204,_}, [], []}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
@@ -1840,35 +1935,33 @@ missing_CR_otp_7304(doc) ->
missing_CR_otp_7304(suite) ->
[];
missing_CR_otp_7304(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html",
- {ok, {{_,200,_}, _, [_ | _]}} = http:request(URL),
+ {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
%%-------------------------------------------------------------------------
-otp_7883(suite) ->
- [otp_7883_1, otp_7883_2].
otp_7883_1(doc) ->
["OTP-7883-sync"];
otp_7883_1(suite) ->
[];
otp_7883_1(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html",
- {error, socket_closed_remotely} = http:request(URL),
+ {error, socket_closed_remotely} = httpc:request(URL),
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
otp_7883_2(doc) ->
@@ -1876,7 +1969,7 @@ otp_7883_2(doc) ->
otp_7883_2(suite) ->
[];
otp_7883_2(Config) when is_list(Config) ->
- ok = http:set_options([{ipfamily, inet}]),
+ ok = httpc:set_options([{ipfamily, inet}]),
{DummyServerPid, Port} = dummy_server(self(), ipv4),
@@ -1885,9 +1978,9 @@ otp_7883_2(Config) when is_list(Config) ->
Request = {URL, []},
HttpOptions = [],
Options = [{sync, false}],
- Profile = http:default_profile(),
+ Profile = httpc:default_profile(),
{ok, RequestId} =
- http:request(Method, Request, HttpOptions, Options, Profile),
+ httpc:request(Method, Request, HttpOptions, Options, Profile),
ok =
receive
{http, {RequestId, {error, socket_closed_remotely}}} ->
@@ -1895,14 +1988,12 @@ otp_7883_2(Config) when is_list(Config) ->
end,
DummyServerPid ! stop,
- ok = http:set_options([{ipfamily, inet6fb4}]), % ********** ipfamily = inet6 *************
+ ok = httpc:set_options([{ipfamily, inet6fb4}]),
ok.
%%-------------------------------------------------------------------------
-otp_8154(suite) ->
- [otp_8154_1].
otp_8154_1(doc) ->
["OTP-8154"];
@@ -1966,7 +2057,7 @@ run_clients(NumClients, ServerPort, SeqNumServer) ->
fun() ->
io:format("[~w] client started - "
"issue request~n", [Id]),
- case http:request(Url) of
+ case httpc:request(Url) of
{ok, {{_,200,_}, _, Resp}} ->
io:format("[~w] 200 response: "
"~p~n", [Id, Resp]),
@@ -2182,12 +2273,6 @@ f(F, A) -> lists:flatten(io_lib:format(F,A)).
%%-------------------------------------------------------------------------
-otp_8106(suite) ->
- [
- otp_8106_pid,
- otp_8106_fun,
- otp_8106_mfa
- ].
otp_8106_pid(doc) ->
@@ -2354,7 +2439,7 @@ otp_8352(Config) when is_list(Config) ->
ConnOptions = [{max_sessions, MaxSessions},
{max_keep_alive_length, MaxKeepAlive},
{keep_alive_timeout, KeepAliveTimeout}],
- http:set_options(ConnOptions),
+ httpc:set_options(ConnOptions),
Method = get,
Port = ?config(local_port, Config),
@@ -2366,9 +2451,9 @@ otp_8352(Config) when is_list(Config) ->
Options1 = [{socket_opts, [{tos, 87},
{recbuf, 16#FFFF},
{sndbuf, 16#FFFF}]}],
- case http:request(Method, Request, HttpOptions1, Options1) of
+ case httpc:request(Method, Request, HttpOptions1, Options1) of
{ok, {{_,200,_}, [_ | _], ReplyBody1 = [_ | _]}} ->
- %% equivaliant to http:request(get, {URL, []}, [], []),
+ %% equivaliant to httpc:request(get, {URL, []}, [], []),
inets_test_lib:check_body(ReplyBody1);
{ok, UnexpectedReply1} ->
tsf({unexpected_reply, UnexpectedReply1});
@@ -2382,9 +2467,9 @@ otp_8352(Config) when is_list(Config) ->
Options2 = [{socket_opts, [{tos, 84},
{recbuf, 32#1FFFF},
{sndbuf, 32#1FFFF}]}],
- case http:request(Method, Request, HttpOptions2, Options2) of
+ case httpc:request(Method, Request, HttpOptions2, Options2) of
{ok, {{_,200,_}, [_ | _], ReplyBody2 = [_ | _]}} ->
- %% equivaliant to http:request(get, {URL, []}, [], []),
+ %% equivaliant to httpc:request(get, {URL, []}, [], []),
inets_test_lib:check_body(ReplyBody2);
{ok, UnexpectedReply2} ->
tsf({unexpected_reply, UnexpectedReply2});
@@ -2406,13 +2491,13 @@ otp_8371(doc) ->
otp_8371(suite) ->
[];
otp_8371(Config) when is_list(Config) ->
- ok = http:set_options([{ipv6, disabled}]), % also test the old option
+ ok = httpc:set_options([{ipv6, disabled}]), % also test the old option
{DummyServerPid, Port} = dummy_server(self(), ipv4),
URL = ?URL_START ++ integer_to_list(Port) ++
"/ensure_host_header_with_port.html",
- case http:request(get, {URL, []}, [], []) of
+ case httpc:request(get, {URL, []}, [], []) of
{ok, Result} ->
case Result of
{{_, 200, _}, _Headers, Body} ->
@@ -2436,11 +2521,82 @@ otp_8371(Config) when is_list(Config) ->
end,
DummyServerPid ! stop,
- ok = http:set_options([{ipv6, enabled}]),
+ ok = httpc:set_options([{ipv6, enabled}]),
ok.
+%%-------------------------------------------------------------------------
+otp_8739(doc) ->
+ ["OTP-8739"];
+otp_8739(suite) ->
+ [];
+otp_8739(Config) when is_list(Config) ->
+ {_DummyServerPid, Port} = otp_8739_dummy_server(),
+ URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html",
+ Method = get,
+ Request = {URL, []},
+ HttpOptions = [{connect_timeout, 500}, {timeout, 1}],
+ Options = [{sync, true}],
+ case http:request(Method, Request, HttpOptions, Options) of
+ {error, timeout} ->
+ %% And now we check the size of the handler db
+ Info = httpc:info(),
+ tsp("Info: ~p", [Info]),
+ {value, {handlers, Handlers}} =
+ lists:keysearch(handlers, 1, Info),
+ case Handlers of
+ [] ->
+ ok;
+ _ ->
+ tsf({unexpected_handlers, Handlers})
+ end;
+ Unexpected ->
+ tsf({unexpected, Unexpected})
+ end.
+
+
+otp_8739_dummy_server() ->
+ Parent = self(),
+ Pid = spawn_link(fun() -> otp_8739_dummy_server_init(Parent) end),
+ receive
+ {port, Port} ->
+ {Pid, Port}
+ end.
+
+otp_8739_dummy_server_init(Parent) ->
+ {ok, ListenSocket} =
+ gen_tcp:listen(0, [binary, inet, {packet, 0},
+ {reuseaddr,true},
+ {active, false}]),
+ {ok, Port} = inet:port(ListenSocket),
+ Parent ! {port, Port},
+ otp_8739_dummy_server_main(Parent, ListenSocket).
+
+otp_8739_dummy_server_main(Parent, ListenSocket) ->
+ case gen_tcp:accept(ListenSocket) of
+ {ok, Sock} ->
+ %% Ignore the request, and simply wait for the socket to close
+ receive
+ {tcp_closed, Sock} ->
+ (catch gen_tcp:close(ListenSocket)),
+ exit(normal);
+ {tcp_error, Sock, Reason} ->
+ tsp("socket error: ~p", [Reason]),
+ (catch gen_tcp:close(ListenSocket)),
+ exit(normal)
+ after 10000 ->
+ %% Just in case
+ (catch gen_tcp:close(Sock)),
+ (catch gen_tcp:close(ListenSocket)),
+ exit(timeout)
+ end;
+ Error ->
+ exit(Error)
+ end.
+
+
+
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
@@ -2537,7 +2693,7 @@ receive_streamed_body(RequestId, Body) ->
end.
receive_streamed_body(RequestId, Body, Pid) ->
- http:stream_next(Pid),
+ httpc:stream_next(Pid),
test_server:format("~p:receive_streamed_body -> requested next stream ~n", [?MODULE]),
receive
{http, {RequestId, stream, BinBodyPart}} ->
@@ -2921,11 +3077,11 @@ provocate_not_modified_bug(Url) ->
Timeout = 15000, %% 15s should be plenty
{ok, {{_, 200, _}, ReplyHeaders, _Body}} =
- http:request(get, {Url, []}, [{timeout, Timeout}], []),
+ httpc:request(get, {Url, []}, [{timeout, Timeout}], []),
Etag = pick_header(ReplyHeaders, "ETag"),
Last = pick_header(ReplyHeaders, "last-modified"),
- case http:request(get, {Url, [{"If-None-Match", Etag},
+ case httpc:request(get, {Url, [{"If-None-Match", Etag},
{"If-Modified-Since", Last}]},
[{timeout, 15000}],
[]) of
diff --git a/lib/inets/test/httpc_cookie_SUITE.erl b/lib/inets/test/httpc_cookie_SUITE.erl
index ad5df656c6..feef5f1eea 100644
--- a/lib/inets/test/httpc_cookie_SUITE.erl
+++ b/lib/inets/test/httpc_cookie_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,11 +19,11 @@
%%
-module(httpc_cookie_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
%% Test server specific exports
--export([all/1, init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
-export([session_cookies_only/1, netscape_cookies/1,
@@ -116,22 +116,29 @@ end_per_testcase(Case, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Describe the main purpose of this suite"];
-
-all(suite) ->
- [
- session_cookies_only,
- netscape_cookies,
- cookie_cancel,
- cookie_expires,
- persistent_cookie,
- domain_cookie,
- secure_cookie,
- update_cookie,
- update_cookie_session,
- cookie_attributes
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [session_cookies_only, netscape_cookies, cookie_cancel,
+ cookie_expires, persistent_cookie, domain_cookie,
+ secure_cookie, update_cookie, update_cookie_session,
+ cookie_attributes].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Test cases starts here.
%%--------------------------------------------------------------------
diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl
index 055d034bec..2a6110e3ea 100644
--- a/lib/inets/test/httpd_1_1.erl
+++ b/lib/inets/test/httpd_1_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index 7403d4a643..fde5178879 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,56 +20,177 @@
-module(httpd_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("test_server_line.hrl").
-include("inets_test_lib.hrl").
-include_lib("kernel/include/file.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([init_per_testcase/2, end_per_testcase/2,
init_per_suite/1, end_per_suite/1]).
-%% Test cases must be exported.
--export([ip/1, ssl/1, http_1_1_ip/1, http_1_0_ip/1, http_0_9_ip/1,
- ipv6/1, tickets/1]).
-
%% Core Server tests
--export([ip_mod_alias/1, ip_mod_actions/1, ip_mod_security/1, ip_mod_auth/1,
- ip_mod_auth_api/1, ip_mod_auth_mnesia_api/1,
- ip_mod_htaccess/1, ip_mod_cgi/1, ip_mod_esi/1,
- ip_mod_get/1, ip_mod_head/1, ip_mod_all/1, ip_load_light/1,
- ip_load_medium/1, ip_load_heavy/1, ip_dos_hostname/1,
- ip_time_test/1, ip_block_disturbing_idle/1,
- ip_block_non_disturbing_idle/1, ip_block_503/1,
- ip_block_disturbing_active/1, ip_block_non_disturbing_active/1,
+-export([
+ ip_mod_alias/1,
+ ip_mod_actions/1,
+ ip_mod_security/1,
+ ip_mod_auth/1,
+ ip_mod_auth_api/1,
+ ip_mod_auth_mnesia_api/1,
+ ip_mod_htaccess/1,
+ ip_mod_cgi/1,
+ ip_mod_esi/1,
+ ip_mod_get/1,
+ ip_mod_head/1,
+ ip_mod_all/1,
+ ip_load_light/1,
+ ip_load_medium/1,
+ ip_load_heavy/1,
+ ip_dos_hostname/1,
+ ip_time_test/1,
+ ip_block_disturbing_idle/1,
+ ip_block_non_disturbing_idle/1,
+ ip_block_503/1,
+ ip_block_disturbing_active/1,
+ ip_block_non_disturbing_active/1,
ip_block_disturbing_active_timeout_not_released/1,
ip_block_disturbing_active_timeout_released/1,
ip_block_non_disturbing_active_timeout_not_released/1,
ip_block_non_disturbing_active_timeout_released/1,
ip_block_disturbing_blocker_dies/1,
ip_block_non_disturbing_blocker_dies/1,
- ip_restart_no_block/1, ip_restart_disturbing_block/1,
+ ip_restart_no_block/1,
+ ip_restart_disturbing_block/1,
ip_restart_non_disturbing_block/1
]).
--export([ssl_mod_alias/1, ssl_mod_actions/1, ssl_mod_security/1,
- ssl_mod_auth/1, ssl_mod_auth_api/1,
- ssl_mod_auth_mnesia_api/1, ssl_mod_htaccess/1,
- ssl_mod_cgi/1, ssl_mod_esi/1, ssl_mod_get/1, ssl_mod_head/1,
- ssl_mod_all/1, ssl_load_light/1, ssl_load_medium/1,
- ssl_load_heavy/1, ssl_dos_hostname/1, ssl_time_test/1,
- ssl_restart_no_block/1, ssl_restart_disturbing_block/1,
- ssl_restart_non_disturbing_block/1, ssl_block_disturbing_idle/1,
- ssl_block_non_disturbing_idle/1, ssl_block_503/1,
- ssl_block_disturbing_active/1, ssl_block_non_disturbing_active/1,
- ssl_block_disturbing_active_timeout_not_released/1,
- ssl_block_disturbing_active_timeout_released/1,
- ssl_block_non_disturbing_active_timeout_not_released/1,
- ssl_block_non_disturbing_active_timeout_released/1,
- ssl_block_disturbing_blocker_dies/1,
- ssl_block_non_disturbing_blocker_dies/1]).
+-export([
+ pssl_mod_alias/1,
+ ossl_mod_alias/1,
+ essl_mod_alias/1,
+
+ pssl_mod_actions/1,
+ ossl_mod_actions/1,
+ essl_mod_actions/1,
+
+ pssl_mod_security/1,
+ ossl_mod_security/1,
+ essl_mod_security/1,
+
+ pssl_mod_auth/1,
+ ossl_mod_auth/1,
+ essl_mod_auth/1,
+
+ pssl_mod_auth_api/1,
+ ossl_mod_auth_api/1,
+ essl_mod_auth_api/1,
+
+ pssl_mod_auth_mnesia_api/1,
+ ossl_mod_auth_mnesia_api/1,
+ essl_mod_auth_mnesia_api/1,
+
+ pssl_mod_htaccess/1,
+ ossl_mod_htaccess/1,
+ essl_mod_htaccess/1,
+
+ pssl_mod_cgi/1,
+ ossl_mod_cgi/1,
+ essl_mod_cgi/1,
+
+ pssl_mod_esi/1,
+ ossl_mod_esi/1,
+ essl_mod_esi/1,
+
+ pssl_mod_get/1,
+ ossl_mod_get/1,
+ essl_mod_get/1,
+
+ pssl_mod_head/1,
+ ossl_mod_head/1,
+ essl_mod_head/1,
+
+ pssl_mod_all/1,
+ ossl_mod_all/1,
+ essl_mod_all/1,
+
+ pssl_load_light/1,
+ ossl_load_light/1,
+ essl_load_light/1,
+
+ pssl_load_medium/1,
+ ossl_load_medium/1,
+ essl_load_medium/1,
+
+ pssl_load_heavy/1,
+ ossl_load_heavy/1,
+ essl_load_heavy/1,
+
+ pssl_dos_hostname/1,
+ ossl_dos_hostname/1,
+ essl_dos_hostname/1,
+
+ pssl_time_test/1,
+ ossl_time_test/1,
+ essl_time_test/1,
+
+ pssl_restart_no_block/1,
+ ossl_restart_no_block/1,
+ essl_restart_no_block/1,
+
+ pssl_restart_disturbing_block/1,
+ ossl_restart_disturbing_block/1,
+ essl_restart_disturbing_block/1,
+
+ pssl_restart_non_disturbing_block/1,
+ ossl_restart_non_disturbing_block/1,
+ essl_restart_non_disturbing_block/1,
+
+ pssl_block_disturbing_idle/1,
+ ossl_block_disturbing_idle/1,
+ essl_block_disturbing_idle/1,
+
+ pssl_block_non_disturbing_idle/1,
+ ossl_block_non_disturbing_idle/1,
+ essl_block_non_disturbing_idle/1,
+
+ pssl_block_503/1,
+ ossl_block_503/1,
+ essl_block_503/1,
+
+ pssl_block_disturbing_active/1,
+ ossl_block_disturbing_active/1,
+ essl_block_disturbing_active/1,
+
+ pssl_block_non_disturbing_active/1,
+ ossl_block_non_disturbing_active/1,
+ essl_block_non_disturbing_active/1,
+
+ pssl_block_disturbing_active_timeout_not_released/1,
+ ossl_block_disturbing_active_timeout_not_released/1,
+ essl_block_disturbing_active_timeout_not_released/1,
+
+ pssl_block_disturbing_active_timeout_released/1,
+ ossl_block_disturbing_active_timeout_released/1,
+ essl_block_disturbing_active_timeout_released/1,
+
+ pssl_block_non_disturbing_active_timeout_not_released/1,
+ ossl_block_non_disturbing_active_timeout_not_released/1,
+ essl_block_non_disturbing_active_timeout_not_released/1,
+
+ pssl_block_non_disturbing_active_timeout_released/1,
+ ossl_block_non_disturbing_active_timeout_released/1,
+ essl_block_non_disturbing_active_timeout_released/1,
+
+ pssl_block_disturbing_blocker_dies/1,
+ ossl_block_disturbing_blocker_dies/1,
+ essl_block_disturbing_blocker_dies/1,
+
+ pssl_block_non_disturbing_blocker_dies/1,
+ ossl_block_non_disturbing_blocker_dies/1,
+ essl_block_non_disturbing_blocker_dies/1
+ ]).
%%% HTTP 1.1 tests
-export([ip_host/1, ip_chunked/1, ip_expect/1, ip_range/1,
@@ -103,8 +224,8 @@
%% Seconds before successful auths timeout.
-define(AUTH_TIMEOUT,5).
--record(httpd_user, {user_name, password, user_data}).
--record(httpd_group,{group_name, userlist}).
+-record(httpd_user, {user_name, password, user_data}).
+-record(httpd_group, {group_name, userlist}).
%%--------------------------------------------------------------------
@@ -117,19 +238,109 @@
%% Description: Returns documentation/test cases in this test suite
%% or a skip tuple if the platform is not supported.
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test the http server in the intes application."];
-all(suite) ->
- [
- ip,
- ssl,
- http_1_1_ip,
- http_1_0_ip,
- http_0_9_ip,
- %% ipv6,
- tickets
- ].
-
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, ip}, {group, ssl}, {group, http_1_1_ip},
+ {group, http_1_0_ip}, {group, http_0_9_ip},
+ {group, tickets}].
+
+groups() ->
+ [{ip, [],
+ [ip_mod_alias, ip_mod_actions, ip_mod_security,
+ ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api,
+ ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get,
+ ip_mod_head, ip_mod_all, ip_load_light, ip_load_medium,
+ ip_load_heavy, ip_dos_hostname, ip_time_test,
+ ip_restart_no_block, ip_restart_disturbing_block,
+ ip_restart_non_disturbing_block,
+ ip_block_disturbing_idle, ip_block_non_disturbing_idle,
+ ip_block_503, ip_block_disturbing_active,
+ ip_block_non_disturbing_active,
+ ip_block_disturbing_active_timeout_not_released,
+ ip_block_disturbing_active_timeout_released,
+ ip_block_non_disturbing_active_timeout_not_released,
+ ip_block_non_disturbing_active_timeout_released,
+ ip_block_disturbing_blocker_dies,
+ ip_block_non_disturbing_blocker_dies]},
+ {ssl, [],
+ [{group, pssl}, {group, ossl}, {group, essl}]},
+ {pssl, [],
+ [pssl_mod_alias, pssl_mod_actions, pssl_mod_security,
+ pssl_mod_auth, pssl_mod_auth_api,
+ pssl_mod_auth_mnesia_api, pssl_mod_htaccess,
+ pssl_mod_cgi, pssl_mod_esi, pssl_mod_get, pssl_mod_head,
+ pssl_mod_all, pssl_load_light, pssl_load_medium,
+ pssl_load_heavy, pssl_dos_hostname, pssl_time_test,
+ pssl_restart_no_block, pssl_restart_disturbing_block,
+ pssl_restart_non_disturbing_block,
+ pssl_block_disturbing_idle,
+ pssl_block_non_disturbing_idle, pssl_block_503,
+ pssl_block_disturbing_active,
+ pssl_block_non_disturbing_active,
+ pssl_block_disturbing_active_timeout_not_released,
+ pssl_block_disturbing_active_timeout_released,
+ pssl_block_non_disturbing_active_timeout_not_released,
+ pssl_block_non_disturbing_active_timeout_released,
+ pssl_block_disturbing_blocker_dies,
+ pssl_block_non_disturbing_blocker_dies]},
+ {ossl, [],
+ [ossl_mod_alias, ossl_mod_actions, ossl_mod_security,
+ ossl_mod_auth, ossl_mod_auth_api,
+ ossl_mod_auth_mnesia_api, ossl_mod_htaccess,
+ ossl_mod_cgi, ossl_mod_esi, ossl_mod_get, ossl_mod_head,
+ ossl_mod_all, ossl_load_light, ossl_load_medium,
+ ossl_load_heavy, ossl_dos_hostname, ossl_time_test,
+ ossl_restart_no_block, ossl_restart_disturbing_block,
+ ossl_restart_non_disturbing_block,
+ ossl_block_disturbing_idle,
+ ossl_block_non_disturbing_idle, ossl_block_503,
+ ossl_block_disturbing_active,
+ ossl_block_non_disturbing_active,
+ ossl_block_disturbing_active_timeout_not_released,
+ ossl_block_disturbing_active_timeout_released,
+ ossl_block_non_disturbing_active_timeout_not_released,
+ ossl_block_non_disturbing_active_timeout_released,
+ ossl_block_disturbing_blocker_dies,
+ ossl_block_non_disturbing_blocker_dies]},
+ {essl, [],
+ [essl_mod_alias, essl_mod_actions, essl_mod_security,
+ essl_mod_auth, essl_mod_auth_api,
+ essl_mod_auth_mnesia_api, essl_mod_htaccess,
+ essl_mod_cgi, essl_mod_esi, essl_mod_get, essl_mod_head,
+ essl_mod_all, essl_load_light, essl_load_medium,
+ essl_load_heavy, essl_dos_hostname, essl_time_test,
+ essl_restart_no_block, essl_restart_disturbing_block,
+ essl_restart_non_disturbing_block,
+ essl_block_disturbing_idle,
+ essl_block_non_disturbing_idle, essl_block_503,
+ essl_block_disturbing_active,
+ essl_block_non_disturbing_active,
+ essl_block_disturbing_active_timeout_not_released,
+ essl_block_disturbing_active_timeout_released,
+ essl_block_non_disturbing_active_timeout_not_released,
+ essl_block_non_disturbing_active_timeout_released,
+ essl_block_disturbing_blocker_dies,
+ essl_block_non_disturbing_blocker_dies]},
+ {http_1_1_ip, [],
+ [ip_host, ip_chunked, ip_expect, ip_range, ip_if_test,
+ ip_http_trace, ip_http1_1_head,
+ ip_mod_cgi_chunked_encoding_test]},
+ {http_1_0_ip, [],
+ [ip_head_1_0, ip_get_1_0, ip_post_1_0]},
+ {http_0_9_ip, [], [ip_get_0_9]},
+ {ipv6, [], [ipv6_hostname, ipv6_address]},
+ {tickets, [],
+ [ticket_5775, ticket_5865, ticket_5913, ticket_6003,
+ ticket_7304]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
%% Config - [tuple()]
@@ -197,9 +408,9 @@ init_per_testcase2(Case, Config) ->
"~n Config: ~p"
"~n", [?MODULE, Case, Config]),
- IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
- IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf",
- SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
+ IpNormal = integer_to_list(?IP_PORT) ++ ".conf",
+ IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf",
+ SslNormal = integer_to_list(?SSL_PORT) ++ ".conf",
SslHtacess = integer_to_list(?SSL_PORT) ++ "htacess.conf",
DataDir = ?config(data_dir, Config),
@@ -210,8 +421,8 @@ init_per_testcase2(Case, Config) ->
"~n DataDir: ~p"
"~n", [?MODULE, Case, SuiteTopDir, DataDir]),
- TcTopDir = filename:join(SuiteTopDir, Case),
- ?line ok = file:make_dir(TcTopDir),
+ TcTopDir = filename:join(SuiteTopDir, Case),
+ ?line ok = file:make_dir(TcTopDir),
io:format(user, "~w:init_per_testcase2(~w) -> "
"~n TcTopDir: ~p"
@@ -267,9 +478,21 @@ init_per_testcase2(Case, Config) ->
%% To be used by SSL test cases
io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n",
[?MODULE, Case]),
- create_config([{port, ?SSL_PORT}, {sock_type, ssl} | NewConfig],
+ SocketType =
+ case atom_to_list(Case) of
+ [X, $s, $s, $l | _] ->
+ case X of
+ $p -> ssl;
+ $o -> ossl;
+ $e -> essl
+ end;
+ _ ->
+ ssl
+ end,
+
+ create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
normal_acess, SslNormal),
- create_config([{port, ?SSL_PORT}, {sock_type, ssl} | NewConfig],
+ create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig],
mod_htaccess, SslHtacess),
%% To be used by IPv6 test cases. Case-clause is so that
@@ -300,8 +523,14 @@ init_per_testcase3(Case, Config) ->
io:format(user, "~w:init_per_testcase3(~w) -> entry with"
"~n Config: ~p", [?MODULE, Case, Config]),
+
+%% %% Create a new fresh node to be used by the server in this test-case
+
+%% NodeName = list_to_atom(atom_to_list(Case) ++ "_httpd"),
+%% Node = inets_test_lib:start_node(NodeName),
+
%% Clean up (we do not want this clean up in end_per_testcase
- %% if init_per_testcase crases for some testcase it will
+ %% if init_per_testcase crashes for some testcase it will
%% have contaminated the environment and there will be no clean up.)
%% This init can take a few different paths so that one crashes
%% does not mean that all invocations will.
@@ -310,15 +539,26 @@ init_per_testcase3(Case, Config) ->
application:stop(inets),
application:stop(ssl),
cleanup_mnesia(),
-
- %% TraceLevel = max,
- TraceLevel = 70,
- TraceDest = io,
- inets:enable_trace(TraceLevel, TraceDest),
+ %% Set trace
+ case lists:reverse(atom_to_list(Case)) of
+ "tset_emit" ++ _Rest -> % test-cases ending with time_test
+ io:format(user, "~w:init_per_testcase3(~w) -> disabling trace",
+ [?MODULE, Case]),
+ inets:disable_trace();
+ _ ->
+ %% TraceLevel = max,
+ io:format(user, "~w:init_per_testcase3(~w) -> enabling trace",
+ [?MODULE, Case]),
+ TraceLevel = 70,
+ TraceDest = io,
+ inets:enable_trace(TraceLevel, TraceDest, httpd)
+ end,
+
%% Start initialization
io:format(user, "~w:init_per_testcase3(~w) -> start init",
[?MODULE, Case]),
+
Dog = test_server:timetrap(inets_test_lib:minutes(10)),
NewConfig = lists:keydelete(watchdog, 1, Config),
@@ -351,22 +591,35 @@ init_per_testcase3(Case, Config) ->
filename:join(TcTopDir,
integer_to_list(?IP_PORT) ++ ".conf")}]),
Rest;
- "ssl_mod_htaccess" ->
+
+ [X, $s, $s, $l, $_, $m, $o, $d, $_, $h, $t, $a, $c, $c, $e, $s, $s] ->
+ SslTag =
+ case X of
+ $p -> ssl; % plain
+ $o -> ossl; % OpenSSL based ssl
+ $e -> essl % Erlang based ssl
+ end,
case inets_test_lib:start_http_server_ssl(
filename:join(TcTopDir,
integer_to_list(?SSL_PORT) ++
- "htacess.conf")) of
+ "htacess.conf"), SslTag) of
ok ->
"mod_htaccess";
Other ->
error_logger:info_report("Other: ~p~n", [Other]),
{skip, "SSL does not seem to be supported"}
end;
- "ssl_" ++ Rest ->
+ [X, $s, $s, $l, $_ | Rest] ->
+ SslTag =
+ case X of
+ $p -> ssl;
+ $o -> ossl;
+ $e -> essl
+ end,
case inets_test_lib:start_http_server_ssl(
filename:join(TcTopDir,
integer_to_list(?SSL_PORT) ++
- ".conf")) of
+ ".conf"), SslTag) of
ok ->
Rest;
Other ->
@@ -431,6 +684,7 @@ end_per_testcase2(Case, Config) ->
application:unset_env(inets, services),
application:stop(inets),
application:stop(ssl),
+ application:stop(crypto), % used by the new ssl (essl test cases)
cleanup_mnesia(),
io:format(user, "~w:end_per_testcase2(~w) -> done~n",
[?MODULE, Case]),
@@ -440,134 +694,23 @@ end_per_testcase2(Case, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-ip(doc) ->
- ["HTTP tests using TCP/IP"];
-ip(suite) ->
- [
- ip_mod_alias,
- ip_mod_actions,
- ip_mod_security,
- ip_mod_auth,
- ip_mod_auth_api,
- ip_mod_auth_mnesia_api,
- ip_mod_htaccess,
- ip_mod_cgi,
- ip_mod_esi,
- ip_mod_get,
- ip_mod_head,
- ip_mod_all,
- ip_load_light,
- ip_load_medium,
- ip_load_heavy,
- ip_dos_hostname,
- ip_time_test,
- ip_block_disturbing_idle,
- ip_block_non_disturbing_idle,
- ip_block_503,
- ip_block_disturbing_active,
- ip_block_non_disturbing_active,
- ip_block_disturbing_active_timeout_not_released,
- ip_block_disturbing_active_timeout_released,
- ip_block_non_disturbing_active_timeout_not_released,
- ip_block_non_disturbing_active_timeout_released,
- ip_block_disturbing_blocker_dies,
- ip_block_non_disturbing_blocker_dies,
- ip_restart_no_block,
- ip_restart_disturbing_block,
- ip_restart_non_disturbing_block
- ].
%%-------------------------------------------------------------------------
-ssl(doc) ->
- ["HTTP test using SSL"];
-ssl(suite) ->
- [
- ssl_mod_alias,
- ssl_mod_actions,
- ssl_mod_security,
- ssl_mod_auth,
- ssl_mod_auth_api,
- ssl_mod_auth_mnesia_api,
- ssl_mod_htaccess,
- ssl_mod_cgi,
- ssl_mod_esi,
- ssl_mod_get,
- ssl_mod_head,
- ssl_mod_all,
- ssl_load_light,
- ssl_load_medium,
- ssl_load_heavy,
- ssl_dos_hostname,
- ssl_time_test,
- ssl_restart_no_block,
- ssl_restart_disturbing_block,
- ssl_restart_non_disturbing_block,
- ssl_block_disturbing_idle,
- ssl_block_non_disturbing_idle,
- ssl_block_503,
- ssl_block_disturbing_active,
- ssl_block_non_disturbing_active,
- ssl_block_disturbing_active_timeout_not_released,
- ssl_block_disturbing_active_timeout_released,
- ssl_block_non_disturbing_active_timeout_not_released,
- ssl_block_non_disturbing_active_timeout_released,
- ssl_block_disturbing_blocker_dies,
- ssl_block_non_disturbing_blocker_dies
- ].
+
+
+
+
+
%%-------------------------------------------------------------------------
-http_1_1_ip(doc) ->
- ["HTTP/1.1"];
-http_1_1_ip(suite) ->
- [
- ip_host,
- ip_chunked,
- ip_expect,
- ip_range,
- ip_if_test,
- ip_http_trace,
- ip_http1_1_head,
- ip_mod_cgi_chunked_encoding_test
- ].
%%-------------------------------------------------------------------------
-http_1_0_ip(doc) ->
- ["HTTP/1.0"];
-http_1_0_ip(suite) ->
- [
- ip_head_1_0,
- ip_get_1_0,
- ip_post_1_0
- ].
%%-------------------------------------------------------------------------
-http_0_9_ip(doc) ->
- ["HTTP/0.9"];
-http_0_9_ip(suite) ->
- [
- ip_get_0_9
- ].
%%-------------------------------------------------------------------------
-ipv6(doc) ->
- ["Tests ipv6 functionality."];
-ipv6(suite) ->
- [
- ipv6_hostname,
- ipv6_address
- ].
%%-------------------------------------------------------------------------
-tickets(doc) ->
- ["Test cases for reported bugs."];
-tickets(suite) ->
- [
- ticket_5775,
- ticket_5865,
- ticket_5913,
- ticket_6003,
- ticket_7304
- ].
%%-------------------------------------------------------------------------
ip_mod_alias(doc) ->
@@ -721,6 +864,8 @@ ip_load_heavy(Config) when is_list(Config) ->
?config(node, Config),
get_nof_clients(ip_comm, heavy)),
ok.
+
+
%%-------------------------------------------------------------------------
ip_dos_hostname(doc) ->
["Denial Of Service (DOS) attack test case"];
@@ -730,6 +875,8 @@ ip_dos_hostname(Config) when is_list(Config) ->
dos_hostname(ip_comm, ?IP_PORT, ?config(host, Config),
?config(node, Config), ?MAX_HEADER_SIZE),
ok.
+
+
%%-------------------------------------------------------------------------
ip_time_test(doc) ->
[""];
@@ -966,363 +1113,1072 @@ ip_restart_non_disturbing_block(Config) when is_list(Config) ->
ok.
%%-------------------------------------------------------------------------
-ssl_mod_alias(doc) ->
- ["Module test: mod_alias"];
-ssl_mod_alias(suite) ->
+
+pssl_mod_alias(doc) ->
+ ["Module test: mod_alias - old SSL config"];
+pssl_mod_alias(suite) ->
+ [];
+pssl_mod_alias(Config) when is_list(Config) ->
+ ssl_mod_alias(ssl, Config).
+
+ossl_mod_alias(doc) ->
+ ["Module test: mod_alias - using new of configure old SSL"];
+ossl_mod_alias(suite) ->
[];
-ssl_mod_alias(Config) when is_list(Config) ->
- httpd_mod:alias(ssl, ?SSL_PORT,
+ossl_mod_alias(Config) when is_list(Config) ->
+ ssl_mod_alias(ossl, Config).
+
+essl_mod_alias(doc) ->
+ ["Module test: mod_alias - using new of configure new SSL"];
+essl_mod_alias(suite) ->
+ [];
+essl_mod_alias(Config) when is_list(Config) ->
+ ssl_mod_alias(essl, Config).
+
+
+ssl_mod_alias(Tag, Config) ->
+ httpd_mod:alias(Tag, ?SSL_PORT,
?config(host, Config), ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_actions(doc) ->
- ["Module test: mod_actions"];
-ssl_mod_actions(suite) ->
+
+pssl_mod_actions(doc) ->
+ ["Module test: mod_actions - old SSL config"];
+pssl_mod_actions(suite) ->
[];
-ssl_mod_actions(Config) when is_list(Config) ->
- httpd_mod:actions(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_actions(Config) when is_list(Config) ->
+ ssl_mod_actions(ssl, Config).
+
+ossl_mod_actions(doc) ->
+ ["Module test: mod_actions - using new of configure old SSL"];
+ossl_mod_actions(suite) ->
+ [];
+ossl_mod_actions(Config) when is_list(Config) ->
+ ssl_mod_actions(ossl, Config).
+
+essl_mod_actions(doc) ->
+ ["Module test: mod_actions - using new of configure new SSL"];
+essl_mod_actions(suite) ->
+ [];
+essl_mod_actions(Config) when is_list(Config) ->
+ ssl_mod_actions(essl, Config).
+
+
+ssl_mod_actions(Tag, Config) ->
+ httpd_mod:actions(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_security(doc) ->
- ["Module test: mod_security"];
-ssl_mod_security(suite) ->
+
+pssl_mod_security(doc) ->
+ ["Module test: mod_security - old SSL config"];
+pssl_mod_security(suite) ->
+ [];
+pssl_mod_security(Config) when is_list(Config) ->
+ ssl_mod_security(ssl, Config).
+
+ossl_mod_security(doc) ->
+ ["Module test: mod_security - using new of configure old SSL"];
+ossl_mod_security(suite) ->
[];
-ssl_mod_security(Config) when is_list(Config) ->
+ossl_mod_security(Config) when is_list(Config) ->
+ ssl_mod_security(ossl, Config).
+
+essl_mod_security(doc) ->
+ ["Module test: mod_security - using new of configure new SSL"];
+essl_mod_security(suite) ->
+ [];
+essl_mod_security(Config) when is_list(Config) ->
+ ssl_mod_security(essl, Config).
+
+ssl_mod_security(Tag, Config) ->
ServerRoot = ?config(server_root, Config),
- httpd_mod:security(ServerRoot, ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+ httpd_mod:security(ServerRoot,
+ Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_auth(doc) ->
- ["Module test: mod_auth"];
-ssl_mod_auth(suite) ->
+
+pssl_mod_auth(doc) ->
+ ["Module test: mod_auth - old SSL config"];
+pssl_mod_auth(suite) ->
[];
-ssl_mod_auth(Config) when is_list(Config) ->
- httpd_mod:auth(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_auth(Config) when is_list(Config) ->
+ ssl_mod_auth(ssl, Config).
+
+ossl_mod_auth(doc) ->
+ ["Module test: mod_auth - using new of configure old SSL"];
+ossl_mod_auth(suite) ->
+ [];
+ossl_mod_auth(Config) when is_list(Config) ->
+ ssl_mod_auth(ossl, Config).
+
+essl_mod_auth(doc) ->
+ ["Module test: mod_auth - using new of configure new SSL"];
+essl_mod_auth(suite) ->
+ [];
+essl_mod_auth(Config) when is_list(Config) ->
+ ssl_mod_auth(essl, Config).
+
+ssl_mod_auth(Tag, Config) ->
+ httpd_mod:auth(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_auth_api(doc) ->
- ["Module test: mod_auth"];
-ssl_mod_auth_api(suite) ->
+
+pssl_mod_auth_api(doc) ->
+ ["Module test: mod_auth - old SSL config"];
+pssl_mod_auth_api(suite) ->
+ [];
+pssl_mod_auth_api(Config) when is_list(Config) ->
+ ssl_mod_auth_api(ssl, Config).
+
+ossl_mod_auth_api(doc) ->
+ ["Module test: mod_auth - using new of configure old SSL"];
+ossl_mod_auth_api(suite) ->
+ [];
+ossl_mod_auth_api(Config) when is_list(Config) ->
+ ssl_mod_auth_api(ossl, Config).
+
+essl_mod_auth_api(doc) ->
+ ["Module test: mod_auth - using new of configure new SSL"];
+essl_mod_auth_api(suite) ->
[];
-ssl_mod_auth_api(Config) when is_list(Config) ->
+essl_mod_auth_api(Config) when is_list(Config) ->
+ ssl_mod_auth_api(essl, Config).
+
+ssl_mod_auth_api(Tag, Config) ->
ServerRoot = ?config(server_root, Config),
- Host = ?config(host, Config),
- Node = ?config(node, Config),
- httpd_mod:auth_api(ServerRoot, "", ssl, ?SSL_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "dets_", ssl, ?SSL_PORT, Host, Node),
- httpd_mod:auth_api(ServerRoot, "mnesia_", ssl, ?SSL_PORT, Host, Node),
+ Host = ?config(host, Config),
+ Node = ?config(node, Config),
+ httpd_mod:auth_api(ServerRoot, "", Tag, ?SSL_PORT, Host, Node),
+ httpd_mod:auth_api(ServerRoot, "dets_", Tag, ?SSL_PORT, Host, Node),
+ httpd_mod:auth_api(ServerRoot, "mnesia_", Tag, ?SSL_PORT, Host, Node),
ok.
+
%%-------------------------------------------------------------------------
-ssl_mod_auth_mnesia_api(doc) ->
- ["Module test: mod_auth_mnesia_api"];
-ssl_mod_auth_mnesia_api(suite) ->
+
+pssl_mod_auth_mnesia_api(doc) ->
+ ["Module test: mod_auth_mnesia_api - old SSL config"];
+pssl_mod_auth_mnesia_api(suite) ->
[];
-ssl_mod_auth_mnesia_api(Config) when is_list(Config) ->
- httpd_mod:auth_mnesia_api(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_auth_mnesia_api(Config) when is_list(Config) ->
+ ssl_mod_auth_mnesia_api(ssl, Config).
+
+ossl_mod_auth_mnesia_api(doc) ->
+ ["Module test: mod_auth_mnesia_api - using new of configure old SSL"];
+ossl_mod_auth_mnesia_api(suite) ->
+ [];
+ossl_mod_auth_mnesia_api(Config) when is_list(Config) ->
+ ssl_mod_auth_mnesia_api(ossl, Config).
+
+essl_mod_auth_mnesia_api(doc) ->
+ ["Module test: mod_auth_mnesia_api - using new of configure new SSL"];
+essl_mod_auth_mnesia_api(suite) ->
+ [];
+essl_mod_auth_mnesia_api(Config) when is_list(Config) ->
+ ssl_mod_auth_mnesia_api(essl, Config).
+
+ssl_mod_auth_mnesia_api(Tag, Config) ->
+ httpd_mod:auth_mnesia_api(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_htaccess(doc) ->
- ["Module test: mod_htaccess"];
-ssl_mod_htaccess(suite) ->
+
+pssl_mod_htaccess(doc) ->
+ ["Module test: mod_htaccess - old SSL config"];
+pssl_mod_htaccess(suite) ->
[];
-ssl_mod_htaccess(Config) when is_list(Config) ->
- httpd_mod:htaccess(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_htaccess(Config) when is_list(Config) ->
+ ssl_mod_htaccess(ssl, Config).
+
+ossl_mod_htaccess(doc) ->
+ ["Module test: mod_htaccess - using new of configure old SSL"];
+ossl_mod_htaccess(suite) ->
+ [];
+ossl_mod_htaccess(Config) when is_list(Config) ->
+ ssl_mod_htaccess(ossl, Config).
+
+essl_mod_htaccess(doc) ->
+ ["Module test: mod_htaccess - using new of configure new SSL"];
+essl_mod_htaccess(suite) ->
+ [];
+essl_mod_htaccess(Config) when is_list(Config) ->
+ ssl_mod_htaccess(essl, Config).
+
+ssl_mod_htaccess(Tag, Config) ->
+ httpd_mod:htaccess(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_cgi(doc) ->
- ["Module test: mod_cgi"];
-ssl_mod_cgi(suite) ->
+
+pssl_mod_cgi(doc) ->
+ ["Module test: mod_cgi - old SSL config"];
+pssl_mod_cgi(suite) ->
+ [];
+pssl_mod_cgi(Config) when is_list(Config) ->
+ ssl_mod_cgi(ssl, Config).
+
+ossl_mod_cgi(doc) ->
+ ["Module test: mod_cgi - using new of configure old SSL"];
+ossl_mod_cgi(suite) ->
+ [];
+ossl_mod_cgi(Config) when is_list(Config) ->
+ ssl_mod_cgi(ossl, Config).
+
+essl_mod_cgi(doc) ->
+ ["Module test: mod_cgi - using new of configure new SSL"];
+essl_mod_cgi(suite) ->
[];
-ssl_mod_cgi(Config) when is_list(Config) ->
+essl_mod_cgi(Config) when is_list(Config) ->
+ ssl_mod_cgi(essl, Config).
+
+ssl_mod_cgi(Tag, Config) ->
case test_server:os_type() of
vxworks ->
{skip, cgi_not_supported_on_vxwoks};
_ ->
- httpd_mod:cgi(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+ httpd_mod:cgi(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok
end.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_esi(doc) ->
- ["Module test: mod_esi"];
-ssl_mod_esi(suite) ->
+
+pssl_mod_esi(doc) ->
+ ["Module test: mod_esi - old SSL config"];
+pssl_mod_esi(suite) ->
[];
-ssl_mod_esi(Config) when is_list(Config) ->
- httpd_mod:esi(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_esi(Config) when is_list(Config) ->
+ ssl_mod_esi(ssl, Config).
+
+ossl_mod_esi(doc) ->
+ ["Module test: mod_esi - using new of configure old SSL"];
+ossl_mod_esi(suite) ->
+ [];
+ossl_mod_esi(Config) when is_list(Config) ->
+ ssl_mod_esi(ossl, Config).
+
+essl_mod_esi(doc) ->
+ ["Module test: mod_esi - using new of configure new SSL"];
+essl_mod_esi(suite) ->
+ [];
+essl_mod_esi(Config) when is_list(Config) ->
+ ssl_mod_esi(essl, Config).
+
+ssl_mod_esi(Tag, Config) ->
+ httpd_mod:esi(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_mod_get(doc) ->
- ["Module test: mod_get"];
-ssl_mod_get(suite) ->
+
+pssl_mod_get(doc) ->
+ ["Module test: mod_get - old SSL config"];
+pssl_mod_get(suite) ->
[];
-ssl_mod_get(Config) when is_list(Config) ->
- httpd_mod:get(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_get(Config) when is_list(Config) ->
+ ssl_mod_get(ssl, Config).
+
+ossl_mod_get(doc) ->
+ ["Module test: mod_get - using new of configure old SSL"];
+ossl_mod_get(suite) ->
+ [];
+ossl_mod_get(Config) when is_list(Config) ->
+ ssl_mod_get(ossl, Config).
+
+essl_mod_get(doc) ->
+ ["Module test: mod_get - using new of configure new SSL"];
+essl_mod_get(suite) ->
+ [];
+essl_mod_get(Config) when is_list(Config) ->
+ ssl_mod_get(essl, Config).
+
+ssl_mod_get(Tag, Config) ->
+ httpd_mod:get(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_head(doc) ->
- ["Module test: mod_head"];
-ssl_mod_head(suite) ->
+
+pssl_mod_head(doc) ->
+ ["Module test: mod_head - old SSL config"];
+pssl_mod_head(suite) ->
[];
-ssl_mod_head(Config) when is_list(Config) ->
- httpd_mod:head(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_head(Config) when is_list(Config) ->
+ ssl_mod_head(ssl, Config).
+
+ossl_mod_head(doc) ->
+ ["Module test: mod_head - using new of configure old SSL"];
+ossl_mod_head(suite) ->
+ [];
+ossl_mod_head(Config) when is_list(Config) ->
+ ssl_mod_head(ossl, Config).
+
+essl_mod_head(doc) ->
+ ["Module test: mod_head - using new of configure new SSL"];
+essl_mod_head(suite) ->
+ [];
+essl_mod_head(Config) when is_list(Config) ->
+ ssl_mod_head(essl, Config).
+
+ssl_mod_head(Tag, Config) ->
+ httpd_mod:head(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_mod_all(doc) ->
- ["All modules test"];
-ssl_mod_all(suite) ->
+
+pssl_mod_all(doc) ->
+ ["All modules test - old SSL config"];
+pssl_mod_all(suite) ->
[];
-ssl_mod_all(Config) when is_list(Config) ->
- httpd_mod:all(ssl, ?SSL_PORT,
- ?config(host, Config), ?config(node, Config)),
+pssl_mod_all(Config) when is_list(Config) ->
+ ssl_mod_all(ssl, Config).
+
+ossl_mod_all(doc) ->
+ ["All modules test - using new of configure old SSL"];
+ossl_mod_all(suite) ->
+ [];
+ossl_mod_all(Config) when is_list(Config) ->
+ ssl_mod_all(ossl, Config).
+
+essl_mod_all(doc) ->
+ ["All modules test - using new of configure new SSL"];
+essl_mod_all(suite) ->
+ [];
+essl_mod_all(Config) when is_list(Config) ->
+ ssl_mod_all(essl, Config).
+
+ssl_mod_all(Tag, Config) ->
+ httpd_mod:all(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_load_light(doc) ->
- ["Test light load"];
-ssl_load_light(suite) ->
+
+pssl_load_light(doc) ->
+ ["Test light load - old SSL config"];
+pssl_load_light(suite) ->
+ [];
+pssl_load_light(Config) when is_list(Config) ->
+ ssl_load_light(ssl, Config).
+
+ossl_load_light(doc) ->
+ ["Test light load - using new of configure old SSL"];
+ossl_load_light(suite) ->
+ [];
+ossl_load_light(Config) when is_list(Config) ->
+ ssl_load_light(ossl, Config).
+
+essl_load_light(doc) ->
+ ["Test light load - using new of configure new SSL"];
+essl_load_light(suite) ->
[];
-ssl_load_light(Config) when is_list(Config) ->
- httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config),
+essl_load_light(Config) when is_list(Config) ->
+ ssl_load_light(essl, Config).
+
+ssl_load_light(Tag, Config) ->
+ httpd_load:load_test(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
?config(node, Config),
get_nof_clients(ssl, light)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_load_medium(doc) ->
- ["Test medium load"];
-ssl_load_medium(suite) ->
+
+pssl_load_medium(doc) ->
+ ["Test medium load - old SSL config"];
+pssl_load_medium(suite) ->
+ [];
+pssl_load_medium(Config) when is_list(Config) ->
+ ssl_load_medium(ssl, Config).
+
+ossl_load_medium(doc) ->
+ ["Test medium load - using new of configure old SSL"];
+ossl_load_medium(suite) ->
+ [];
+ossl_load_medium(Config) when is_list(Config) ->
+ ssl_load_medium(ossl, Config).
+
+essl_load_medium(doc) ->
+ ["Test medium load - using new of configure new SSL"];
+essl_load_medium(suite) ->
[];
-ssl_load_medium(Config) when is_list(Config) ->
+essl_load_medium(Config) when is_list(Config) ->
+ ssl_load_medium(essl, Config).
+
+ssl_load_medium(Tag, Config) ->
%% <CONDITIONAL-SKIP>
Skippable = [win32],
Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
?NON_PC_TC_MAYBE_SKIP(Config, Condition),
%% </CONDITIONAL-SKIP>
- httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config),
+ httpd_load:load_test(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
?config(node, Config),
get_nof_clients(ssl, medium)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_load_heavy(doc) ->
- ["Test heavy load"];
-ssl_load_heavy(suite) ->
+
+pssl_load_heavy(doc) ->
+ ["Test heavy load - old SSL config"];
+pssl_load_heavy(suite) ->
+ [];
+pssl_load_heavy(Config) when is_list(Config) ->
+ ssl_load_heavy(ssl, Config).
+
+ossl_load_heavy(doc) ->
+ ["Test heavy load - using new of configure old SSL"];
+ossl_load_heavy(suite) ->
[];
-ssl_load_heavy(Config) when is_list(Config) ->
+ossl_load_heavy(Config) when is_list(Config) ->
+ ssl_load_heavy(ossl, Config).
+
+essl_load_heavy(doc) ->
+ ["Test heavy load - using new of configure new SSL"];
+essl_load_heavy(suite) ->
+ [];
+essl_load_heavy(Config) when is_list(Config) ->
+ ssl_load_heavy(essl, Config).
+
+ssl_load_heavy(Tag, Config) ->
%% <CONDITIONAL-SKIP>
Skippable = [win32],
Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
?NON_PC_TC_MAYBE_SKIP(Config, Condition),
%% </CONDITIONAL-SKIP>
- httpd_load:load_test(ssl, ?SSL_PORT, ?config(host, Config),
+ httpd_load:load_test(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
?config(node, Config),
get_nof_clients(ssl, heavy)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_dos_hostname(doc) ->
- ["Denial Of Service (DOS) attack test case"];
-ssl_dos_hostname(suite) ->
+
+pssl_dos_hostname(doc) ->
+ ["Denial Of Service (DOS) attack test case - old SSL config"];
+pssl_dos_hostname(suite) ->
[];
-ssl_dos_hostname(Config) when is_list(Config) ->
- dos_hostname(ssl, ?SSL_PORT, ?config(host, Config),
- ?config(node, Config), ?MAX_HEADER_SIZE),
+pssl_dos_hostname(Config) when is_list(Config) ->
+ ssl_dos_hostname(ssl, Config).
+
+ossl_dos_hostname(doc) ->
+ ["Denial Of Service (DOS) attack test case - using new of configure old SSL"];
+ossl_dos_hostname(suite) ->
+ [];
+ossl_dos_hostname(Config) when is_list(Config) ->
+ ssl_dos_hostname(ossl, Config).
+
+essl_dos_hostname(doc) ->
+ ["Denial Of Service (DOS) attack test case - using new of configure new SSL"];
+essl_dos_hostname(suite) ->
+ [];
+essl_dos_hostname(Config) when is_list(Config) ->
+ ssl_dos_hostname(essl, Config).
+
+ssl_dos_hostname(Tag, Config) ->
+ dos_hostname(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config),
+ ?MAX_HEADER_SIZE),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_time_test(doc) ->
- [""];
-ssl_time_test(suite) ->
+
+pssl_time_test(doc) ->
+ ["old SSL config"];
+pssl_time_test(suite) ->
[];
-ssl_time_test(Config) when is_list(Config) ->
+pssl_time_test(Config) when is_list(Config) ->
+ ssl_time_test(ssl, Config).
+
+ossl_time_test(doc) ->
+ ["using new of configure old SSL"];
+ossl_time_test(suite) ->
+ [];
+ossl_time_test(Config) when is_list(Config) ->
+ ssl_time_test(ossl, Config).
+
+essl_time_test(doc) ->
+ ["using new of configure new SSL"];
+essl_time_test(suite) ->
+ [];
+essl_time_test(Config) when is_list(Config) ->
+ ssl_time_test(essl, Config).
+
+ssl_time_test(Tag, Config) when is_list(Config) ->
%% <CONDITIONAL-SKIP>
- Condition = fun() -> true end,
+ FreeBSDVersionVerify =
+ fun() ->
+ case os:version() of
+ {7, 1, _} -> % We only have one such machine, so...
+ true;
+ _ ->
+ false
+ end
+ end,
+ Skippable = [win32, {unix, [{freebsd, FreeBSDVersionVerify}]}],
+ Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
?NON_PC_TC_MAYBE_SKIP(Config, Condition),
%% </CONDITIONAL-SKIP>
- httpd_time_test:t(ssl, ?config(host, Config), ?SSL_PORT),
+ httpd_time_test:t(Tag,
+ ?config(host, Config),
+ ?SSL_PORT),
ok.
+
%%-------------------------------------------------------------------------
-ssl_block_503(doc) ->
+
+pssl_block_503(doc) ->
["Check that you will receive status code 503 when the server"
- " is blocked and 200 when its not blocked."];
-ssl_block_503(suite) ->
+ " is blocked and 200 when its not blocked - old SSL config."];
+pssl_block_503(suite) ->
+ [];
+pssl_block_503(Config) when is_list(Config) ->
+ ssl_block_503(ssl, Config).
+
+ossl_block_503(doc) ->
+ ["Check that you will receive status code 503 when the server"
+ " is blocked and 200 when its not blocked - using new of configure old SSL."];
+ossl_block_503(suite) ->
+ [];
+ossl_block_503(Config) when is_list(Config) ->
+ ssl_block_503(ossl, Config).
+
+essl_block_503(doc) ->
+ ["Check that you will receive status code 503 when the server"
+ " is blocked and 200 when its not blocked - using new of configure new SSL."];
+essl_block_503(suite) ->
[];
-ssl_block_503(Config) when is_list(Config) ->
- httpd_block:block_503(ssl, ?SSL_PORT, ?config(host, Config),
+essl_block_503(Config) when is_list(Config) ->
+ ssl_block_503(essl, Config).
+
+ssl_block_503(Tag, Config) ->
+ httpd_block:block_503(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_disturbing_idle(doc) ->
+
+pssl_block_disturbing_idle(doc) ->
["Check that you can block/unblock an idle server. The strategy "
- "distribing does not really make a difference in this case."];
-ssl_block_disturbing_idle(suite) ->
+ "distribing does not really make a difference in this case."
+ "Old SSL config"];
+pssl_block_disturbing_idle(suite) ->
[];
-ssl_block_disturbing_idle(Config) when is_list(Config) ->
- httpd_block:block_disturbing_idle(ssl, ?SSL_PORT,
+pssl_block_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_disturbing_idle(ssl, Config).
+
+ossl_block_disturbing_idle(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "distribing does not really make a difference in this case."
+ "Using new of configure old SSL"];
+ossl_block_disturbing_idle(suite) ->
+ [];
+ossl_block_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_disturbing_idle(ossl, Config).
+
+essl_block_disturbing_idle(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "distribing does not really make a difference in this case."
+ "Using new of configure new SSL"];
+essl_block_disturbing_idle(suite) ->
+ [];
+essl_block_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_disturbing_idle(essl, Config).
+
+ssl_block_disturbing_idle(Tag, Config) ->
+ httpd_block:block_disturbing_idle(Tag,
+ ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_non_disturbing_idle(doc) ->
+
+pssl_block_non_disturbing_idle(doc) ->
["Check that you can block/unblock an idle server. The strategy "
- "non distribing does not really make a difference in this case."];
-ssl_block_non_disturbing_idle(suite) ->
+ "non distribing does not really make a difference in this case."
+ "Old SSL config"];
+pssl_block_non_disturbing_idle(suite) ->
+ [];
+pssl_block_non_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_idle(ssl, Config).
+
+ossl_block_non_disturbing_idle(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "non distribing does not really make a difference in this case."
+ "Using new of configure old SSL"];
+ossl_block_non_disturbing_idle(suite) ->
+ [];
+ossl_block_non_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_idle(ossl, Config).
+
+essl_block_non_disturbing_idle(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "non distribing does not really make a difference in this case."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_idle(suite) ->
[];
-ssl_block_non_disturbing_idle(Config) when is_list(Config) ->
- httpd_block:block_non_disturbing_idle(ssl, ?SSL_PORT,
+essl_block_non_disturbing_idle(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_idle(essl, Config).
+
+ssl_block_non_disturbing_idle(Tag, Config) ->
+ httpd_block:block_non_disturbing_idle(Tag,
+ ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_disturbing_active(doc) ->
+
+pssl_block_disturbing_active(doc) ->
["Check that you can block/unblock an active server. The strategy "
- "distribing means ongoing requests should be terminated."];
-ssl_block_disturbing_active(suite) ->
+ "distribing means ongoing requests should be terminated."
+ "Old SSL config"];
+pssl_block_disturbing_active(suite) ->
[];
-ssl_block_disturbing_active(Config) when is_list(Config) ->
- httpd_block:block_disturbing_active(ssl, ?SSL_PORT,
+pssl_block_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_disturbing_active(ssl, Config).
+
+ossl_block_disturbing_active(doc) ->
+ ["Check that you can block/unblock an active server. The strategy "
+ "distribing means ongoing requests should be terminated."
+ "Using new of configure old SSL"];
+ossl_block_disturbing_active(suite) ->
+ [];
+ossl_block_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_disturbing_active(ossl, Config).
+
+essl_block_disturbing_active(doc) ->
+ ["Check that you can block/unblock an active server. The strategy "
+ "distribing means ongoing requests should be terminated."
+ "Using new of configure new SSL"];
+essl_block_disturbing_active(suite) ->
+ [];
+essl_block_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_disturbing_active(essl, Config).
+
+ssl_block_disturbing_active(Tag, Config) ->
+ httpd_block:block_disturbing_active(Tag,
+ ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_non_disturbing_active(doc) ->
+
+pssl_block_non_disturbing_active(doc) ->
["Check that you can block/unblock an idle server. The strategy "
- "non distribing means the ongoing requests should be compleated."];
-ssl_block_non_disturbing_active(suite) ->
+ "non distribing means the ongoing requests should be compleated."
+ "Old SSL config"];
+pssl_block_non_disturbing_active(suite) ->
[];
-ssl_block_non_disturbing_active(Config) when is_list(Config) ->
- httpd_block:block_non_disturbing_idle(ssl, ?SSL_PORT,
+pssl_block_non_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_active(ssl, Config).
+
+ossl_block_non_disturbing_active(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "non distribing means the ongoing requests should be compleated."
+ "Using new of configure old SSL"];
+ossl_block_non_disturbing_active(suite) ->
+ [];
+ossl_block_non_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_active(ossl, Config).
+
+essl_block_non_disturbing_active(doc) ->
+ ["Check that you can block/unblock an idle server. The strategy "
+ "non distribing means the ongoing requests should be compleated."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_active(suite) ->
+ [];
+essl_block_non_disturbing_active(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_active(essl, Config).
+
+ssl_block_non_disturbing_active(Tag, Config) ->
+ httpd_block:block_non_disturbing_idle(Tag,
+ ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_block_disturbing_active_timeout_not_released(doc) ->
+
+pssl_block_disturbing_active_timeout_not_released(doc) ->
["Check that you can block an active server. The strategy "
"distribing means ongoing requests should be compleated"
- "if the timeout does not occur."];
-ssl_block_disturbing_active_timeout_not_released(suite) ->
+ "if the timeout does not occur."
+ "Old SSL config"];
+pssl_block_disturbing_active_timeout_not_released(suite) ->
[];
-ssl_block_disturbing_active_timeout_not_released(Config)
+pssl_block_disturbing_active_timeout_not_released(Config)
when is_list(Config) ->
- httpd_block:
- block_disturbing_active_timeout_not_released(ssl,
- ?SSL_PORT,
- ?config(host,
- Config),
- ?config(node,
- Config)),
+ ssl_block_disturbing_active_timeout_not_released(ssl, Config).
+
+ossl_block_disturbing_active_timeout_not_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "distribing means ongoing requests should be compleated"
+ "if the timeout does not occur."
+ "Using new of configure old SSL"];
+ossl_block_disturbing_active_timeout_not_released(suite) ->
+ [];
+ossl_block_disturbing_active_timeout_not_released(Config)
+ when is_list(Config) ->
+ ssl_block_disturbing_active_timeout_not_released(ossl, Config).
+
+essl_block_disturbing_active_timeout_not_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "distribing means ongoing requests should be compleated"
+ "if the timeout does not occur."
+ "Using new of configure new SSL"];
+essl_block_disturbing_active_timeout_not_released(suite) ->
+ [];
+essl_block_disturbing_active_timeout_not_released(Config)
+ when is_list(Config) ->
+ ssl_block_disturbing_active_timeout_not_released(essl, Config).
+
+ssl_block_disturbing_active_timeout_not_released(Tag, Config) ->
+ Port = ?SSL_PORT,
+ Host = ?config(host, Config),
+ Node = ?config(node, Config),
+ httpd_block:block_disturbing_active_timeout_not_released(Tag,
+ Port, Host, Node),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_disturbing_active_timeout_released(doc) ->
+
+pssl_block_disturbing_active_timeout_released(doc) ->
["Check that you can block an active server. The strategy "
"distribing means ongoing requests should be terminated when"
- "the timeout occurs."];
-ssl_block_disturbing_active_timeout_released(suite) ->
+ "the timeout occurs."
+ "Old SSL config"];
+pssl_block_disturbing_active_timeout_released(suite) ->
[];
-ssl_block_disturbing_active_timeout_released(Config)
+pssl_block_disturbing_active_timeout_released(Config)
when is_list(Config) ->
- httpd_block:block_disturbing_active_timeout_released(ssl,
- ?SSL_PORT,
- ?config(host,
- Config),
- ?config(node,
- Config)),
+ ssl_block_disturbing_active_timeout_released(ssl, Config).
+
+ossl_block_disturbing_active_timeout_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "distribing means ongoing requests should be terminated when"
+ "the timeout occurs."
+ "Using new of configure old SSL"];
+ossl_block_disturbing_active_timeout_released(suite) ->
+ [];
+ossl_block_disturbing_active_timeout_released(Config)
+ when is_list(Config) ->
+ ssl_block_disturbing_active_timeout_released(ossl, Config).
+
+essl_block_disturbing_active_timeout_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "distribing means ongoing requests should be terminated when"
+ "the timeout occurs."
+ "Using new of configure new SSL"];
+essl_block_disturbing_active_timeout_released(suite) ->
+ [];
+essl_block_disturbing_active_timeout_released(Config)
+ when is_list(Config) ->
+ ssl_block_disturbing_active_timeout_released(essl, Config).
+
+ssl_block_disturbing_active_timeout_released(Tag, Config) ->
+ Port = ?SSL_PORT,
+ Host = ?config(host, Config),
+ Node = ?config(node, Config),
+ httpd_block:block_disturbing_active_timeout_released(Tag,
+ Port,
+ Host,
+ Node),
ok.
+
%%-------------------------------------------------------------------------
-ssl_block_non_disturbing_active_timeout_not_released(doc) ->
+
+pssl_block_non_disturbing_active_timeout_not_released(doc) ->
["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed."];
-ssl_block_non_disturbing_active_timeout_not_released(suite) ->
+ "non non distribing means ongoing requests should be completed."
+ "Old SSL config"];
+pssl_block_non_disturbing_active_timeout_not_released(suite) ->
[];
-ssl_block_non_disturbing_active_timeout_not_released(Config)
+pssl_block_non_disturbing_active_timeout_not_released(Config)
when is_list(Config) ->
- httpd_block:
- block_non_disturbing_active_timeout_not_released(ssl,
- ?SSL_PORT,
- ?config(host,
- Config),
- ?config(node,
- Config)),
+ ssl_block_non_disturbing_active_timeout_not_released(ssl, Config).
+
+ossl_block_non_disturbing_active_timeout_not_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "non non distribing means ongoing requests should be completed."
+ "Using new of configure old SSL"];
+ossl_block_non_disturbing_active_timeout_not_released(suite) ->
+ [];
+ossl_block_non_disturbing_active_timeout_not_released(Config)
+ when is_list(Config) ->
+ ssl_block_non_disturbing_active_timeout_not_released(ossl, Config).
+
+essl_block_non_disturbing_active_timeout_not_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "non non distribing means ongoing requests should be completed."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_active_timeout_not_released(suite) ->
+ [];
+essl_block_non_disturbing_active_timeout_not_released(Config)
+ when is_list(Config) ->
+ ssl_block_non_disturbing_active_timeout_not_released(essl, Config).
+
+ssl_block_non_disturbing_active_timeout_not_released(Tag, Config) ->
+ Port = ?SSL_PORT,
+ Host = ?config(host, Config),
+ Node = ?config(node, Config),
+ httpd_block:block_non_disturbing_active_timeout_not_released(Tag,
+ Port,
+ Host,
+ Node),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_non_disturbing_active_timeout_released(doc) ->
+
+pssl_block_non_disturbing_active_timeout_released(doc) ->
["Check that you can block an active server. The strategy "
- "non non distribing means ongoing requests should be completed. "
- "When the timeout occurs the block operation sohould be canceled." ];
-ssl_block_non_disturbing_active_timeout_released(suite) ->
+ "non distribing means ongoing requests should be completed. "
+ "When the timeout occurs the block operation sohould be canceled."
+ "Old SSL config"];
+pssl_block_non_disturbing_active_timeout_released(suite) ->
[];
-ssl_block_non_disturbing_active_timeout_released(Config)
+pssl_block_non_disturbing_active_timeout_released(Config)
when is_list(Config) ->
- httpd_block:
- block_non_disturbing_active_timeout_released(ssl,
- ?SSL_PORT,
- ?config(host,
- Config),
- ?config(node,
- Config)),
+ ssl_block_non_disturbing_active_timeout_released(ssl, Config).
+
+ossl_block_non_disturbing_active_timeout_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "non distribing means ongoing requests should be completed. "
+ "When the timeout occurs the block operation sohould be canceled."
+ "Using new of configure old SSL"];
+ossl_block_non_disturbing_active_timeout_released(suite) ->
+ [];
+ossl_block_non_disturbing_active_timeout_released(Config)
+ when is_list(Config) ->
+ ssl_block_non_disturbing_active_timeout_released(ossl, Config).
+
+essl_block_non_disturbing_active_timeout_released(doc) ->
+ ["Check that you can block an active server. The strategy "
+ "non distribing means ongoing requests should be completed. "
+ "When the timeout occurs the block operation sohould be canceled."
+ "Using new of configure new SSL"];
+essl_block_non_disturbing_active_timeout_released(suite) ->
+ [];
+essl_block_non_disturbing_active_timeout_released(Config)
+ when is_list(Config) ->
+ ssl_block_non_disturbing_active_timeout_released(essl, Config).
+
+ssl_block_non_disturbing_active_timeout_released(Tag, Config)
+ when is_list(Config) ->
+ Port = ?SSL_PORT,
+ Host = ?config(host, Config),
+ Node = ?config(node, Config),
+ httpd_block:block_non_disturbing_active_timeout_released(Tag,
+ Port,
+ Host,
+ Node),
+
ok.
+
%%-------------------------------------------------------------------------
-ssl_block_disturbing_blocker_dies(doc) ->
+
+pssl_block_disturbing_blocker_dies(doc) ->
+ ["old SSL config"];
+pssl_block_disturbing_blocker_dies(suite) ->
+ [];
+pssl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_disturbing_blocker_dies(ssl, Config).
+
+ossl_block_disturbing_blocker_dies(doc) ->
+ ["using new of configure old SSL"];
+ossl_block_disturbing_blocker_dies(suite) ->
[];
-ssl_block_disturbing_blocker_dies(suite) ->
+ossl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_disturbing_blocker_dies(ossl, Config).
+
+essl_block_disturbing_blocker_dies(doc) ->
+ ["using new of configure new SSL"];
+essl_block_disturbing_blocker_dies(suite) ->
[];
-ssl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
- httpd_block:disturbing_blocker_dies(ssl, ?SSL_PORT,
+essl_block_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_disturbing_blocker_dies(essl, Config).
+
+ssl_block_disturbing_blocker_dies(Tag, Config) ->
+ httpd_block:disturbing_blocker_dies(Tag,
+ ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_block_non_disturbing_blocker_dies(doc) ->
+
+pssl_block_non_disturbing_blocker_dies(doc) ->
+ ["old SSL config"];
+pssl_block_non_disturbing_blocker_dies(suite) ->
[];
-ssl_block_non_disturbing_blocker_dies(suite) ->
+pssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_blocker_dies(ssl, Config).
+
+ossl_block_non_disturbing_blocker_dies(doc) ->
+ ["using new of configure old SSL"];
+ossl_block_non_disturbing_blocker_dies(suite) ->
[];
-ssl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
- httpd_block:non_disturbing_blocker_dies(ssl, ?SSL_PORT,
+ossl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_blocker_dies(ossl, Config).
+
+essl_block_non_disturbing_blocker_dies(doc) ->
+ ["using new of configure new SSL"];
+essl_block_non_disturbing_blocker_dies(suite) ->
+ [];
+essl_block_non_disturbing_blocker_dies(Config) when is_list(Config) ->
+ ssl_block_non_disturbing_blocker_dies(essl, Config).
+
+ssl_block_non_disturbing_blocker_dies(Tag, Config) ->
+ httpd_block:non_disturbing_blocker_dies(Tag,
+ ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_restart_no_block(doc) ->
- [""];
-ssl_restart_no_block(suite) ->
+
+pssl_restart_no_block(doc) ->
+ ["old SSL config"];
+pssl_restart_no_block(suite) ->
[];
-ssl_restart_no_block(Config) when is_list(Config) ->
- httpd_block:restart_no_block(ssl, ?SSL_PORT, ?config(host, Config),
+pssl_restart_no_block(Config) when is_list(Config) ->
+ ssl_restart_no_block(ssl, Config).
+
+ossl_restart_no_block(doc) ->
+ ["using new of configure old SSL"];
+ossl_restart_no_block(suite) ->
+ [];
+ossl_restart_no_block(Config) when is_list(Config) ->
+ ssl_restart_no_block(ossl, Config).
+
+essl_restart_no_block(doc) ->
+ ["using new of configure new SSL"];
+essl_restart_no_block(suite) ->
+ [];
+essl_restart_no_block(Config) when is_list(Config) ->
+ ssl_restart_no_block(essl, Config).
+
+ssl_restart_no_block(Tag, Config) ->
+ httpd_block:restart_no_block(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
?config(node, Config)),
ok.
+
+
%%-------------------------------------------------------------------------
-ssl_restart_disturbing_block(doc) ->
- [""];
-ssl_restart_disturbing_block(suite) ->
+
+pssl_restart_disturbing_block(doc) ->
+ ["old SSL config"];
+pssl_restart_disturbing_block(suite) ->
[];
-ssl_restart_disturbing_block(Config) when is_list(Config) ->
+pssl_restart_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_disturbing_block(ssl, Config).
+
+ossl_restart_disturbing_block(doc) ->
+ ["using new of configure old SSL"];
+ossl_restart_disturbing_block(suite) ->
+ [];
+ossl_restart_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_disturbing_block(ossl, Config).
+
+essl_restart_disturbing_block(doc) ->
+ ["using new of configure new SSL"];
+essl_restart_disturbing_block(suite) ->
+ [];
+essl_restart_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_disturbing_block(essl, Config).
+
+ssl_restart_disturbing_block(Tag, Config) ->
%% <CONDITIONAL-SKIP>
Condition =
fun() ->
case os:type() of
{unix, linux} ->
- HW = string:strip(os:cmd("uname -m"), right, $\n),
- case HW of
+ case ?OSCMD("uname -m") of
"ppc" ->
- case inet:gethostname() of
- {ok, "peach"} ->
- true;
+ case file:read_file_info("/etc/fedora-release") of
+ {ok, _} ->
+ case ?OSCMD("awk '{print $2}' /etc/fedora-release") of
+ "release" ->
+ %% Fedora 7 and later
+ case ?OSCMD("awk '{print $3}' /etc/fedora-release") of
+ "7" ->
+ true;
+ _ ->
+ false
+ end;
+ _ ->
+ false
+ end;
_ ->
false
end;
@@ -1336,17 +2192,36 @@ ssl_restart_disturbing_block(Config) when is_list(Config) ->
?NON_PC_TC_MAYBE_SKIP(Config, Condition),
%% </CONDITIONAL-SKIP>
- httpd_block:restart_disturbing_block(ssl, ?SSL_PORT,
+ httpd_block:restart_disturbing_block(Tag, ?SSL_PORT,
?config(host, Config),
?config(node, Config)),
ok.
+
%%-------------------------------------------------------------------------
-ssl_restart_non_disturbing_block(doc) ->
- [""];
-ssl_restart_non_disturbing_block(suite) ->
+
+pssl_restart_non_disturbing_block(doc) ->
+ ["old SSL config"];
+pssl_restart_non_disturbing_block(suite) ->
[];
-ssl_restart_non_disturbing_block(Config) when is_list(Config) ->
+pssl_restart_non_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_non_disturbing_block(ssl, Config).
+
+ossl_restart_non_disturbing_block(doc) ->
+ ["using new of configure old SSL"];
+ossl_restart_non_disturbing_block(suite) ->
+ [];
+ossl_restart_non_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_non_disturbing_block(ossl, Config).
+
+essl_restart_non_disturbing_block(doc) ->
+ ["using new of configure new SSL"];
+essl_restart_non_disturbing_block(suite) ->
+ [];
+essl_restart_non_disturbing_block(Config) when is_list(Config) ->
+ ssl_restart_non_disturbing_block(essl, Config).
+
+ssl_restart_non_disturbing_block(Tag, Config) ->
%% <CONDITIONAL-SKIP>
Condition =
fun() ->
@@ -1371,11 +2246,13 @@ ssl_restart_non_disturbing_block(Config) when is_list(Config) ->
?NON_PC_TC_MAYBE_SKIP(Config, Condition),
%% </CONDITIONAL-SKIP>
- httpd_block:restart_non_disturbing_block(ssl, ?SSL_PORT,
- ?config(host, Config),
- ?config(node, Config)),
+ httpd_block:restart_non_disturbing_block(Tag,
+ ?SSL_PORT,
+ ?config(host, Config),
+ ?config(node, Config)),
ok.
+
%%-------------------------------------------------------------------------
ip_host(doc) ->
["Control that the server accepts/rejects requests with/ without host"];
@@ -1665,17 +2542,29 @@ dos_hostname(Type, Port, Host, Node, Max) ->
%% Other help functions
create_config(Config, Access, FileName) ->
ServerRoot = ?config(server_root, Config),
- TcTopDir = ?config(tc_top_dir, Config),
- Port = ?config(port, Config),
- Type = ?config(sock_type, Config),
- Host = ?config(host, Config),
- Mods = io_lib:format("~p", [httpd_mod]),
- Funcs = io_lib:format("~p", [ssl_password_cb]),
- MaxHdrSz = io_lib:format("~p", [256]),
- MaxHdrAct = io_lib:format("~p", [close]),
+ TcTopDir = ?config(tc_top_dir, Config),
+ Port = ?config(port, Config),
+ Type = ?config(sock_type, Config),
+ Host = ?config(host, Config),
+ Mods = io_lib:format("~p", [httpd_mod]),
+ Funcs = io_lib:format("~p", [ssl_password_cb]),
+ MaxHdrSz = io_lib:format("~p", [256]),
+ MaxHdrAct = io_lib:format("~p", [close]),
+
+ io:format(user,
+ "create_config -> "
+ "~n ServerRoot: ~p"
+ "~n TcTopDir: ~p"
+ "~n Type: ~p"
+ "~n Port: ~p"
+ "~n Host: ~p"
+ "~n", [ServerRoot, TcTopDir, Port, Type, Host]),
+
SSL =
- case Type of
- ssl ->
+ if
+ (Type =:= ssl) orelse
+ (Type =:= ossl) orelse
+ (Type =:= essl) ->
[cline(["SSLCertificateFile ",
filename:join(ServerRoot, "ssl/ssl_server.pem")]),
cline(["SSLCertificateKeyFile ",
@@ -1686,25 +2575,25 @@ create_config(Config, Access, FileName) ->
cline(["SSLPasswordCallbackFunction ", Funcs]),
cline(["SSLVerifyClient 0"]),
cline(["SSLVerifyDepth 1"])];
- _ ->
+ true ->
[]
end,
- Mod_order = case Access of
- mod_htaccess ->
- "Modules mod_alias mod_htaccess mod_auth "
- "mod_security "
- "mod_responsecontrol mod_trace mod_esi "
- "mod_actions mod_cgi mod_include mod_dir "
- "mod_range mod_get "
- "mod_head mod_log mod_disk_log";
- _ ->
- "Modules mod_alias mod_auth mod_security "
- "mod_responsecontrol mod_trace mod_esi "
- "mod_actions mod_cgi mod_include mod_dir "
- "mod_range mod_get "
- "mod_head mod_log mod_disk_log"
- end,
-
+ ModOrder = case Access of
+ mod_htaccess ->
+ "Modules mod_alias mod_htaccess mod_auth "
+ "mod_security "
+ "mod_responsecontrol mod_trace mod_esi "
+ "mod_actions mod_cgi mod_include mod_dir "
+ "mod_range mod_get "
+ "mod_head mod_log mod_disk_log";
+ _ ->
+ "Modules mod_alias mod_auth mod_security "
+ "mod_responsecontrol mod_trace mod_esi "
+ "mod_actions mod_cgi mod_include mod_dir "
+ "mod_range mod_get "
+ "mod_head mod_log mod_disk_log"
+ end,
+
%% The test suite currently does not handle an explicit BindAddress.
%% They assume any has been used, that is Addr is always set to undefined!
@@ -1720,7 +2609,7 @@ create_config(Config, Access, FileName) ->
cline(["Port ", integer_to_list(Port)]),
cline(["ServerName ", Host]),
cline(["SocketType ", atom_to_list(Type)]),
- cline([Mod_order]),
+ cline([ModOrder]),
%% cline(["LogFormat ", "erlang"]),
cline(["ServerAdmin [email protected]"]),
cline(["BindAddress ", BindAddress]),
@@ -1882,18 +2771,18 @@ start_mnesia(Node) ->
ok ->
ok;
Other ->
- test_server:fail({failed_to_cleanup_mnesia, Other})
+ tsf({failed_to_cleanup_mnesia, Other})
end,
- case rpc:call(Node, ?MODULE, setup_mnesia, []) of
+ case rpc:call(Node, ?MODULE, setup_mnesia, []) of
{atomic, ok} ->
ok;
Other2 ->
- test_server:fail({failed_to_setup_mnesia, Other2})
+ tsf({failed_to_setup_mnesia, Other2})
end,
ok.
setup_mnesia() ->
- setup_mnesia([node()]).
+ setup_mnesia([node()]).
setup_mnesia(Nodes) ->
ok = mnesia:create_schema(Nodes),
@@ -2029,20 +2918,20 @@ dos_hostname_request(Host) ->
get_nof_clients(Mode, Load) ->
get_nof_clients(test_server:os_type(), Mode, Load).
-get_nof_clients(vxworks, _, light) -> 1;
+get_nof_clients(vxworks, _, light) -> 1;
get_nof_clients(vxworks, ip_comm, medium) -> 3;
-get_nof_clients(vxworks, ssl, medium) -> 3;
+get_nof_clients(vxworks, ssl, medium) -> 3;
get_nof_clients(vxworks, ip_comm, heavy) -> 5;
-get_nof_clients(vxworks, ssl, heavy) -> 5;
-get_nof_clients(_, ip_comm, light) -> 5;
-get_nof_clients(_, ssl, light) -> 2;
-get_nof_clients(_, ip_comm, medium) -> 10;
-get_nof_clients(_, ssl, medium) -> 4;
-get_nof_clients(_, ip_comm, heavy) -> 20;
-get_nof_clients(_, ssl, heavy) -> 6.
+get_nof_clients(vxworks, ssl, heavy) -> 5;
+get_nof_clients(_, ip_comm, light) -> 5;
+get_nof_clients(_, ssl, light) -> 2;
+get_nof_clients(_, ip_comm, medium) -> 10;
+get_nof_clients(_, ssl, medium) -> 4;
+get_nof_clients(_, ip_comm, heavy) -> 20;
+get_nof_clients(_, ssl, heavy) -> 6.
%% Make a file 100 bytes long containing 012...9*10
-create_range_data(Path)->
+create_range_data(Path) ->
PathAndFileName=filename:join([Path,"range.txt"]),
file:write_file(PathAndFileName,list_to_binary(["12345678901234567890",
"12345678901234567890",
@@ -2079,3 +2968,6 @@ create_range_data(Path)->
%% {ok, Fd} = file:open(ConfigFile, [write]),
%% ok = file:write(Fd, lists:flatten(HttpConfig)),
%% ok = file:close(Fd).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/Makefile b/lib/inets/test/httpd_SUITE_data/server_root/Makefile
new file mode 100644
index 0000000000..d7a3231068
--- /dev/null
+++ b/lib/inets/test/httpd_SUITE_data/server_root/Makefile
@@ -0,0 +1,209 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(INETS_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULE=
+
+AUTH_FILES = auth/group \
+ auth/passwd
+CGI_FILES = cgi-bin/printenv.sh
+CONF_FILES = conf/8080.conf \
+ conf/8888.conf \
+ conf/httpd.conf \
+ conf/ssl.conf \
+ conf/mime.types
+OPEN_FILES = htdocs/open/dummy.html
+MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html
+MISC_FILES = htdocs/misc/friedrich.html \
+ htdocs/misc/oech.html
+SECRET_FILES = htdocs/secret/dummy.html
+MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html
+HTDOCS_FILES = htdocs/index.html \
+ htdocs/config.shtml \
+ htdocs/echo.shtml \
+ htdocs/exec.shtml \
+ htdocs/flastmod.shtml \
+ htdocs/fsize.shtml \
+ htdocs/include.shtml
+ICON_FILES = icons/README \
+ icons/a.gif \
+ icons/alert.black.gif \
+ icons/alert.red.gif \
+ icons/apache_pb.gif \
+ icons/back.gif \
+ icons/ball.gray.gif \
+ icons/ball.red.gif \
+ icons/binary.gif \
+ icons/binhex.gif \
+ icons/blank.gif \
+ icons/bomb.gif \
+ icons/box1.gif \
+ icons/box2.gif \
+ icons/broken.gif \
+ icons/burst.gif \
+ icons/button1.gif \
+ icons/button10.gif \
+ icons/button2.gif \
+ icons/button3.gif \
+ icons/button4.gif \
+ icons/button5.gif \
+ icons/button6.gif \
+ icons/button7.gif \
+ icons/button8.gif \
+ icons/button9.gif \
+ icons/buttonl.gif \
+ icons/buttonr.gif \
+ icons/c.gif \
+ icons/comp.blue.gif \
+ icons/comp.gray.gif \
+ icons/compressed.gif \
+ icons/continued.gif \
+ icons/dir.gif \
+ icons/down.gif \
+ icons/dvi.gif \
+ icons/f.gif \
+ icons/folder.gif \
+ icons/folder.open.gif \
+ icons/folder.sec.gif \
+ icons/forward.gif \
+ icons/generic.gif \
+ icons/generic.red.gif \
+ icons/generic.sec.gif \
+ icons/hand.right.gif \
+ icons/hand.up.gif \
+ icons/htdig.gif \
+ icons/icon.sheet.gif \
+ icons/image1.gif \
+ icons/image2.gif \
+ icons/image3.gif \
+ icons/index.gif \
+ icons/layout.gif \
+ icons/left.gif \
+ icons/link.gif \
+ icons/movie.gif \
+ icons/p.gif \
+ icons/patch.gif \
+ icons/pdf.gif \
+ icons/pie0.gif \
+ icons/pie1.gif \
+ icons/pie2.gif \
+ icons/pie3.gif \
+ icons/pie4.gif \
+ icons/pie5.gif \
+ icons/pie6.gif \
+ icons/pie7.gif \
+ icons/pie8.gif \
+ icons/portal.gif \
+ icons/poweredby.gif \
+ icons/ps.gif \
+ icons/quill.gif \
+ icons/right.gif \
+ icons/screw1.gif \
+ icons/screw2.gif \
+ icons/script.gif \
+ icons/sound1.gif \
+ icons/sound2.gif \
+ icons/sphere1.gif \
+ icons/sphere2.gif \
+ icons/star.gif \
+ icons/star_blank.gif \
+ icons/tar.gif \
+ icons/tex.gif \
+ icons/text.gif \
+ icons/transfer.gif \
+ icons/unknown.gif \
+ icons/up.gif \
+ icons/uu.gif \
+ icons/uuencoded.gif \
+ icons/world1.gif \
+ icons/world2.gif
+
+SSL_FILES = ssl/ssl_client.pem \
+ ssl/ssl_server.pem
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt:
+
+clean:
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth
+ $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin
+ $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf
+ $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open
+ $(INSTALL_DATA) $(OPEN_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/open
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
+ $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc
+ $(INSTALL_DATA) $(MISC_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/misc
+ $(INSTALL_DIR) \
+ $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret
+ $(INSTALL_DIR) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret
+ $(INSTALL_DATA) $(SECRET_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/secret
+ $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs
+ $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons
+ $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl
+ $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs
+
+release_docs_spec:
+
diff --git a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf
index 8a74ed1afd..ceb94237d2 100644
--- a/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf
+++ b/lib/inets/test/httpd_SUITE_data/server_root/conf/httpd.conf
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index f86c1fcb49..3e29b68283 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,20 +19,26 @@
%%
-module(httpd_basic_SUITE).
--include("test_server.hrl").
--include("test_server_line.hrl").
+-include_lib("common_test/include/ct.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
-all(doc) ->
- ["Basic test of httpd."];
+-define(URL_START, "http://localhost:").
-all(suite) ->
- [
- uri_too_long_414,
- header_too_long_413
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [uri_too_long_414, header_too_long_413, escaped_url_in_error_body].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
@@ -131,6 +137,31 @@ header_too_long_413(Config) when is_list(Config) ->
{version, "HTTP/1.1"}]),
inets:stop(httpd, Pid).
+escaped_url_in_error_body(doc) ->
+ ["Test Url-encoding see OTP-8940"];
+escaped_url_in_error_body(suite) ->
+ [];
+escaped_url_in_error_body(Config) when is_list(Config) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ Address = proplists:get_value(bind_address, Info),
+ Path = "/<b>this_is_bold<b>",
+ URL = ?URL_START ++ integer_to_list(Port) ++ Path,
+ EscapedPath = http_uri:encode(Path),
+ {ok, {404, Body}} = httpc:request(get, {URL, []},
+ [{url_encode, true}],
+ [{version, "HTTP/1.0"}, {full_result, false}]),
+ EscapedPath = find_URL_path(string:tokens(Body, " ")),
+ {ok, {404, Body1}} = httpc:request(get, {URL, []}, [],
+ [{version, "HTTP/1.0"}, {full_result, false}]),
+ EscapedPath = find_URL_path(string:tokens(Body1, " ")),
+ inets:stop(httpd, Pid).
-
-
+find_URL_path([]) ->
+ "";
+find_URL_path(["URL", URL | _]) ->
+ URL;
+find_URL_path([_ | Rest]) ->
+ find_URL_path(Rest).
diff --git a/lib/inets/test/httpd_block.erl b/lib/inets/test/httpd_block.erl
index f967d8172a..ac1bf43ff5 100644
--- a/lib/inets/test/httpd_block.erl
+++ b/lib/inets/test/httpd_block.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -36,6 +36,7 @@
]).
%% Help functions
+-export([httpd_block/3, httpd_block/4, httpd_unblock/2, httpd_restart/2]).
-export([do_block_server/4, do_block_nd_server/5, do_long_poll/6]).
-define(report(Label, Content),
@@ -47,18 +48,24 @@
%% Test cases starts here.
%%-------------------------------------------------------------------------
block_disturbing_idle(_Type, Port, Host, Node) ->
- unblocked = get_admin_state(Node, Host, Port),
+ io:format("block_disturbing_idle -> entry~n", []),
+ validate_admin_state(Node, Host, Port, unblocked),
block_server(Node, Host, Port),
- blocked = get_admin_state(Node, Host, Port),
+ validate_admin_state(Node, Host, Port, blocked),
unblock_server(Node, Host, Port),
- unblocked = get_admin_state(Node, Host, Port).
+ validate_admin_state(Node, Host, Port, unblocked),
+ io:format("block_disturbing_idle -> done~n", []),
+ ok.
+
%%--------------------------------------------------------------------
block_non_disturbing_idle(_Type, Port, Host, Node) ->
unblocked = get_admin_state(Node, Host, Port),
block_nd_server(Node, Host, Port),
blocked = get_admin_state(Node, Host, Port),
unblock_server(Node, Host, Port),
- unblocked = get_admin_state(Node, Host, Port).
+ unblocked = get_admin_state(Node, Host, Port),
+ ok.
+
%%--------------------------------------------------------------------
block_503(Type, Port, Host, Node) ->
Req = "GET / HTTP/1.0\r\ndummy-host.ericsson.se:\r\n\r\n",
@@ -76,6 +83,7 @@ block_503(Type, Port, Host, Node) ->
ok = httpd_test_lib:verify_request(Type, Host, Port, Node, Req,
[{statuscode, 200},
{version, "HTTP/1.0"}]).
+
%%--------------------------------------------------------------------
block_disturbing_active(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
@@ -87,6 +95,7 @@ block_disturbing_active(Type, Port, Host, Node) ->
blocked = get_admin_state(Node, Host, Port),
process_flag(trap_exit, false),
ok.
+
%%--------------------------------------------------------------------
block_non_disturbing_active(Type, Port, Host, Node) ->
process_flag(trap_exit, true),
@@ -219,32 +228,91 @@ do_block_nd_server(Node, Host, Port, Timeout, Reply) ->
restart_server(Node, _Host, Port) ->
Addr = undefined,
- rpc:call(Node, httpd, restart, [Addr, Port]).
+ rpc:call(Node, ?MODULE, httpd_restart, [Addr, Port]).
+
block_server(Node, _Host, Port) ->
+ io:format("block_server -> entry~n", []),
Addr = undefined,
- rpc:call(Node, httpd, block, [Addr, Port]).
+ rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, disturbing]).
+
block_server(Node, _Host, Port, Timeout) ->
Addr = undefined,
- rpc:call(Node, httpd, block, [Addr, Port, disturbing, Timeout]).
+ rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, disturbing, Timeout]).
+
block_nd_server(Node, _Host, Port) ->
Addr = undefined,
- rpc:call(Node, httpd, block, [Addr, Port, non_disturbing]).
+ rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, non_disturbing]).
block_nd_server(Node, _Host, Port, Timeout) ->
Addr = undefined,
- rpc:call(Node, httpd, block, [Addr, Port, non_disturbing, Timeout]).
+ rpc:call(Node, ?MODULE, httpd_block, [Addr, Port, non_disturbing, Timeout]).
unblock_server(Node, _Host, Port) ->
+ io:format("~p:~p:block_server -> entry~n", [node(),self()]),
Addr = undefined,
- rpc:call(Node, httpd, unblock, [Addr, Port]).
+ rpc:call(Node, ?MODULE, httpd_unblock, [Addr, Port]).
+
+
+httpd_block(Addr, Port, Mode) ->
+ io:format("~p:~p:httpd_block -> entry~n", [node(),self()]),
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ Pid when is_pid(Pid) ->
+ httpd_manager:block(Pid, Mode);
+ _ ->
+ {error, not_started}
+ end.
+
+httpd_block(Addr, Port, Mode, Timeout) ->
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ Pid when is_pid(Pid) ->
+ httpd_manager:block(Pid, Mode, Timeout);
+ _ ->
+ {error, not_started}
+ end.
+
+httpd_unblock(Addr, Port) ->
+ io:format("~p:~p:httpd_unblock -> entry~n", [node(),self()]),
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ Pid when is_pid(Pid) ->
+ httpd_manager:unblock(Pid);
+ _ ->
+ {error, not_started}
+ end.
+
+httpd_restart(Addr, Port) ->
+ Name = make_name(Addr, Port),
+ case whereis(Name) of
+ Pid when is_pid(Pid) ->
+ httpd_manager:reload(Pid, undefined);
+ _ ->
+ {error, not_started}
+ end.
+
+make_name(Addr, Port) ->
+ httpd_util:make_name("httpd", Addr, Port).
-get_admin_state(Node,_Host,Port) ->
+get_admin_state(Node, _Host, Port) ->
Addr = undefined,
rpc:call(Node, httpd, get_admin_state, [Addr, Port]).
+validate_admin_state(Node, Host, Port, Expect) ->
+ io:format("try validating server admin state: ~p~n", [Expect]),
+ case get_admin_state(Node, Host, Port) of
+ Expect ->
+ ok;
+ Unexpected ->
+ io:format("failed validating server admin state: ~p~n",
+ [Unexpected]),
+ exit({unexpected_admin_state, Unexpected, Expect})
+ end.
+
+
await_normal_process_exit(Pid, Name, Timeout) ->
receive
{'EXIT', Pid, normal} ->
@@ -260,6 +328,7 @@ await_normal_process_exit(Pid, Name, Timeout) ->
test_server:fail("timeout while waiting for " ++ Name)
end.
+
await_suite_failed_process_exit(Pid, Name, Timeout, Why) ->
receive
{'EXIT', Pid, {suite_failed, Why}} ->
diff --git a/lib/inets/test/httpd_load.erl b/lib/inets/test/httpd_load.erl
index 9bb9f9f94e..83520033dc 100644
--- a/lib/inets/test/httpd_load.erl
+++ b/lib/inets/test/httpd_load.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl
index b03f842e7c..f2c1fd6a65 100644
--- a/lib/inets/test/httpd_mod.erl
+++ b/lib/inets/test/httpd_mod.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -40,6 +40,13 @@
%% Test cases starts here.
%%-------------------------------------------------------------------------
alias(Type, Port, Host, Node) ->
+%% io:format(user, "~w:alias -> entry with"
+%% "~n Type: ~p"
+%% "~n Port: ~p"
+%% "~n Host: ~p"
+%% "~n Node: ~p"
+%% "~n", [?MODULE, Type, Port, Host, Node]),
+
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /pics/icon.sheet.gif "
"HTTP/1.0\r\n\r\n",
@@ -82,14 +89,15 @@ actions(Type, Port, Host, Node) ->
%%-------------------------------------------------------------------------
security(ServerRoot, Type, Port, Host, Node) ->
- io:format(user, "~w:security -> entry with"
- "~n ServerRoot: ~p"
- "~n Type: ~p"
- "~n Port: ~p"
- "~n Host: ~p"
- "~n Node: ~p"
- "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]),
+%% io:format(user, "~w:security -> entry with"
+%% "~n ServerRoot: ~p"
+%% "~n Type: ~p"
+%% "~n Port: ~p"
+%% "~n Host: ~p"
+%% "~n Node: ~p"
+%% "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]),
+%% io:format(user, "~w:security -> register~n", [?MODULE]),
global:register_name(mod_security_test, self()), % Receive events
test_server:sleep(5000),
@@ -99,54 +107,71 @@ security(ServerRoot, Type, Port, Host, Node) ->
%% Test blocking / unblocking of users.
%% /open, require user one Aladdin
+%% io:format(user, "~w:security -> remove user~n", [?MODULE]),
remove_users(Node, ServerRoot, Host, Port, "open"),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "onePassword"}]},
Node, Port),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "two"}, {password, "twoPassword"}]},
Node, Port),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "Aladdin",
"AladdinPassword", [{statuscode, 401}]),
+%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "Aladdin"},
{password, "AladdinPassword"}]},
Node, Port),
+%% io:format(user, "~w:security -> add users~n", [?MODULE]),
add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []),
add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]),
receive_security_event({event, auth_fail, Port, OpenDir,
[{user, "one"}, {password, "WrongPassword"}]},
Node, Port),
+%% io:format(user, "~w:security -> await block security event~n", [?MODULE]),
receive_security_event({event, user_block, Port, OpenDir,
[{user, "one"}]}, Node, Port),
+%% io:format(user, "~w:security -> unregister~n", [?MODULE]),
global:unregister_name(mod_security_test), % No more events.
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 403}]),
%% User "one" should be blocked now..
%% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
case list_blocked_users(Node, Port) of
[{"one",_, Port, OpenDir,_}] ->
ok;
@@ -156,35 +181,54 @@ security(ServerRoot, Type, Port, Host, Node) ->
exit({unexpected_blocked, Blocked})
end,
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
[{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir),
+%% io:format(user, "~w:security -> unblock user~n", [?MODULE]),
true = unblock_user(Node, "one", Port, OpenDir),
%% User "one" should not be blocked any more..
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
[] = list_blocked_users(Node, Port),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
[] = list_blocked_users(Node, Port, OpenDir),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword",
[{statuscode, 200}]),
%% Test list_auth_users & auth_timeout
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
["one"] = list_auth_users(Node, Port),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
["one"] = list_auth_users(Node, Port, OpenDir),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
["one"] = list_auth_users(Node, Port),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
["one"] = list_auth_users(Node, Port, OpenDir),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
["one"] = list_auth_users(Node, Port),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
["one"] = list_auth_users(Node, Port, OpenDir),
%% Wait for successful auth to timeout.
test_server:sleep(?AUTH_TIMEOUT*1001),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
[] = list_auth_users(Node, Port),
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
[] = list_auth_users(Node, Port, OpenDir),
%% "two" is blocked.
+%% io:format(user, "~w:security -> unblock user~n", [?MODULE]),
true = unblock_user(Node, "two", Port, OpenDir),
%% Test explicit blocking. Block user 'two'.
+%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]),
[] = list_blocked_users(Node,Port,OpenDir),
+%% io:format(user, "~w:security -> block user~n", [?MODULE]),
true = block_user(Node, "two", Port, OpenDir, 10),
+%% io:format(user, "~w:security -> auth request~n", [?MODULE]),
auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword",
[{statuscode, 401}]).
@@ -600,6 +644,11 @@ htaccess(Type, Port, Host, Node) ->
{header, "WWW-Authenticate"}]).
%%--------------------------------------------------------------------
cgi(Type, Port, Host, Node) ->
+%% tsp("cgi -> entry with"
+%% "~n Type: ~p"
+%% "~n Port: ~p"
+%% "~n Host: ~p"
+%% "~n Node: ~p", []),
{Script, Script2, Script3} =
case test_server:os_type() of
{win32, _} ->
@@ -609,6 +658,7 @@ cgi(Type, Port, Host, Node) ->
end,
%% The length (> 100) is intentional
+%% tsp("cgi -> request 01 with length > 100"),
ok = httpd_test_lib:
verify_request(Type, Host, Port, Node,
"POST /cgi-bin/" ++ Script3 ++
@@ -636,46 +686,55 @@ cgi(Type, Port, Host, Node) ->
{version, "HTTP/1.0"},
{header, "content-type", "text/plain"}]),
+%% tsp("cgi -> request 02"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 03"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/not_there "
"HTTP/1.0\r\n\r\n",
[{statuscode, 404},{statuscode, 500},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 04"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /cgi-bin/"++ Script ++
"?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 04"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /cgi-bin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 05"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 06"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /htbin/not_there "
"HTTP/1.0\r\n\r\n",
[{statuscode, 404},{statuscode, 500},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 07"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"GET /htbin/"++ Script ++
"?Nisse:kkk?sss/lll HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 08"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 200},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 09"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script ++
" HTTP/1.0\r\n\r\n",
@@ -683,19 +742,24 @@ cgi(Type, Port, Host, Node) ->
{version, "HTTP/1.0"}]),
%% Execute an existing, but bad CGI script..
+%% tsp("cgi -> request 10 - bad script"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /htbin/"++ Script2 ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 404},
{version, "HTTP/1.0"}]),
+%% tsp("cgi -> request 11 - bad script"),
ok = httpd_test_lib:verify_request(Type, Host, Port, Node,
"POST /cgi-bin/"++ Script2 ++
" HTTP/1.0\r\n\r\n",
[{statuscode, 404},
{version, "HTTP/1.0"}]),
+
+%% tsp("cgi -> done"),
ok.
+
%%--------------------------------------------------------------------
esi(Type, Port, Host, Node) ->
%% Check "ErlScriptAlias" and "EvalScriptAlias" directives
@@ -850,25 +914,44 @@ list_users(Node, Root, _Host, Port, Dir) ->
Directory = filename:join([Root, "htdocs", Dir]),
rpc:call(Node, mod_auth, list_users, [Addr, Port, Directory]).
+
receive_security_event(Event, Node, Port) ->
- io:format(user, "~w:receive_security_event -> entry with"
- "~n Event: ~p"
- "~n Node: ~p"
- "~n Port: ~p"
- "~n", [?MODULE, Event, Node, Port]),
+%% io:format(user, "~w:receive_security_event -> entry with"
+%% "~n Event: ~p"
+%% "~n Node: ~p"
+%% "~n Port: ~p"
+%% "~n", [?MODULE, Event, Node, Port]),
receive
Event ->
ok;
{'EXIT', _, _} ->
- receive_security_event(Event, Node, Port);
- Other ->
- test_server:fail({unexpected_event,
- {expected, Event}, {received, Other}})
+ receive_security_event(Event, Node, Port)
after 5000 ->
- test_server:fail(no_event_recived)
+ %% Flush the message queue, to see if we got something...
+ Msgs = inets_test_lib:flush(),
+ tsf({expected_event_not_received, Msgs})
end.
+%% receive_security_event(Event, Node, Port) ->
+%% io:format(user, "~w:receive_security_event -> entry with"
+%% "~n Event: ~p"
+%% "~n Node: ~p"
+%% "~n Port: ~p"
+%% "~n", [?MODULE, Event, Node, Port]),
+%% receive
+%% Event ->
+%% ok;
+%% {'EXIT', _, _} ->
+%% receive_security_event(Event, Node, Port);
+%% Other ->
+%% test_server:fail({unexpected_event,
+%% {expected, Event}, {received, Other}})
+%% after 5000 ->
+%% test_server:fail(no_event_recived)
+
+%% end.
+
list_blocked_users(Node,Port) ->
Addr = undefined, % Assumed to be on the same host
rpc:call(Node, mod_security, list_blocked_users, [Addr,Port]).
@@ -945,3 +1028,12 @@ check_lists_members1(L,L) ->
ok;
check_lists_members1(L1,L2) ->
{error,{lists_not_equal,L1,L2}}.
+
+
+%% tsp(F) ->
+%% tsp(F, []).
+%% tsp(F, A) ->
+%% test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
diff --git a/lib/inets/test/httpd_poll.erl b/lib/inets/test/httpd_poll.erl
index 1cc10365a7..32335cabcf 100644
--- a/lib/inets/test/httpd_poll.erl
+++ b/lib/inets/test/httpd_poll.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -27,7 +27,8 @@
%% gen_server exports
-export([init/1,
- handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
+ handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
-define(default_verbosity,error).
@@ -86,8 +87,8 @@ options(Options) ->
options([], Defaults, Options) ->
Options ++ Defaults;
-options([{Key,Val} = Opt|Opts], Defaults, Options) ->
- options(Opts, lists:keydelete(Key, 1, Defaults), [Opt|Options]).
+options([{Key, _Val} = Opt|Opts], Defaults, Options) ->
+ options(Opts, lists:keydelete(Key, 1, Defaults), [Opt | Options]).
verbosity(silence) ->
@@ -134,10 +135,9 @@ uris(otp) ->
uri_top_index(),
uri_internal_product1(),
uri_internal_product2(),
- uri_p7a_test_results(),
+ uri_r13b03_test_results(),
uri_bjorn1(),
- uri_bjorn2(),
- uri_top_ronja()
+ uri_bjorn2()
].
uri_top_index() ->
@@ -149,9 +149,9 @@ uri_internal_product1() ->
uri_internal_product2() ->
{"product internal page (2)","/product/internal"}.
-uri_p7a_test_results() ->
- {"test summery index page",
- "/product/internal/test/test_results/progress_P7A/index.html"}.
+uri_r13b03_test_results() ->
+ {"daily build index page",
+ "/product/internal/test/daily/logs.html"}.
uri_bjorn1() ->
{"bjorns home page (1)","/~bjorn/"}.
@@ -159,9 +159,6 @@ uri_bjorn1() ->
uri_bjorn2() ->
{"bjorns home page (2)","/~bjorn"}.
-uri_top_ronja() ->
- {"ronja top page","/ronja/"}.
-
handle_call(stop, _From, State) ->
vlog("stop request"),
@@ -199,7 +196,11 @@ handle_info(Info, State) ->
{noreply, State}.
-terminate(Reason,State) ->
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+terminate(_Reason, State) ->
tcancel(State#state.tref),
log_close(get(log_file)),
ok.
@@ -287,16 +288,16 @@ trash_the_rest(Socket,N) ->
end.
-add(N1,N2) when integer(N1),integer(N2) ->
+add(N1, N2) when is_integer(N1) andalso is_integer(N2) ->
N1 + N2;
-add(N1,N2) when integer(N1) ->
+add(N1, _N2) when is_integer(N1) ->
N1;
-add(N1,N2) when integer(N2) ->
+add(_N1, N2) when is_integer(N2) ->
N2.
-sz(L) when list(L) ->
+sz(L) when is_list(L) ->
length(lists:flatten(L));
-sz(B) when binary(B) ->
+sz(B) when is_binary(B) ->
size(B);
sz(O) ->
{unknown_size,O}.
@@ -307,9 +308,9 @@ sz(O) ->
%% Status code to printable string
%%
-status_to_message(L) when list(L) ->
+status_to_message(L) when is_list(L) ->
case (catch list_to_integer(L)) of
- I when integer(I) ->
+ I when is_integer(I) ->
status_to_message(I);
_ ->
io_lib:format("UNKNOWN STATUS CODE: '~p'",[L])
@@ -470,12 +471,12 @@ vlog(F,A) -> vprint(get(verbosity),log,F,A).
verror(F) -> vprint(get(verbosity),error,F,[]).
verror(F,A) -> vprint(get(verbosity),error,F,A).
-vprint(trace,Severity,F,A) -> vprint(Severity,F,A);
-vprint(debug,trace,F,A) -> ok;
-vprint(debug,Severity,F,A) -> vprint(Severity,F,A);
-vprint(log,log,F,A) -> vprint(log,F,A);
-vprint(log,error,F,A) -> vprint(log,F,A);
-vprint(error,error,F,A) -> vprint(error,F,A);
+vprint(trace, Severity, F, A) -> vprint(Severity,F,A);
+vprint(debug, trace, _F, _A) -> ok;
+vprint(debug, Severity, F, A) -> vprint(Severity,F,A);
+vprint(log, log, F, A) -> vprint(log,F,A);
+vprint(log, error, F, A) -> vprint(log,F,A);
+vprint(error, error, F, A) -> vprint(error,F,A);
vprint(_Verbosity,_Severity,_F,_A) -> ok.
vprint(Severity,F,A) ->
@@ -491,6 +492,3 @@ image_of(trace) -> "TRC: ".
local_time() -> calendar:local_time().
-
-
-
diff --git a/lib/inets/test/httpd_test_data/server_root/Makefile b/lib/inets/test/httpd_test_data/server_root/Makefile
new file mode 100644
index 0000000000..d7a3231068
--- /dev/null
+++ b/lib/inets/test/httpd_test_data/server_root/Makefile
@@ -0,0 +1,209 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(INETS_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULE=
+
+AUTH_FILES = auth/group \
+ auth/passwd
+CGI_FILES = cgi-bin/printenv.sh
+CONF_FILES = conf/8080.conf \
+ conf/8888.conf \
+ conf/httpd.conf \
+ conf/ssl.conf \
+ conf/mime.types
+OPEN_FILES = htdocs/open/dummy.html
+MNESIA_OPEN_FILES = htdocs/mnesia_open/dummy.html
+MISC_FILES = htdocs/misc/friedrich.html \
+ htdocs/misc/oech.html
+SECRET_FILES = htdocs/secret/dummy.html
+MNESIA_SECRET_FILES = htdocs/mnesia_secret/dummy.html
+HTDOCS_FILES = htdocs/index.html \
+ htdocs/config.shtml \
+ htdocs/echo.shtml \
+ htdocs/exec.shtml \
+ htdocs/flastmod.shtml \
+ htdocs/fsize.shtml \
+ htdocs/include.shtml
+ICON_FILES = icons/README \
+ icons/a.gif \
+ icons/alert.black.gif \
+ icons/alert.red.gif \
+ icons/apache_pb.gif \
+ icons/back.gif \
+ icons/ball.gray.gif \
+ icons/ball.red.gif \
+ icons/binary.gif \
+ icons/binhex.gif \
+ icons/blank.gif \
+ icons/bomb.gif \
+ icons/box1.gif \
+ icons/box2.gif \
+ icons/broken.gif \
+ icons/burst.gif \
+ icons/button1.gif \
+ icons/button10.gif \
+ icons/button2.gif \
+ icons/button3.gif \
+ icons/button4.gif \
+ icons/button5.gif \
+ icons/button6.gif \
+ icons/button7.gif \
+ icons/button8.gif \
+ icons/button9.gif \
+ icons/buttonl.gif \
+ icons/buttonr.gif \
+ icons/c.gif \
+ icons/comp.blue.gif \
+ icons/comp.gray.gif \
+ icons/compressed.gif \
+ icons/continued.gif \
+ icons/dir.gif \
+ icons/down.gif \
+ icons/dvi.gif \
+ icons/f.gif \
+ icons/folder.gif \
+ icons/folder.open.gif \
+ icons/folder.sec.gif \
+ icons/forward.gif \
+ icons/generic.gif \
+ icons/generic.red.gif \
+ icons/generic.sec.gif \
+ icons/hand.right.gif \
+ icons/hand.up.gif \
+ icons/htdig.gif \
+ icons/icon.sheet.gif \
+ icons/image1.gif \
+ icons/image2.gif \
+ icons/image3.gif \
+ icons/index.gif \
+ icons/layout.gif \
+ icons/left.gif \
+ icons/link.gif \
+ icons/movie.gif \
+ icons/p.gif \
+ icons/patch.gif \
+ icons/pdf.gif \
+ icons/pie0.gif \
+ icons/pie1.gif \
+ icons/pie2.gif \
+ icons/pie3.gif \
+ icons/pie4.gif \
+ icons/pie5.gif \
+ icons/pie6.gif \
+ icons/pie7.gif \
+ icons/pie8.gif \
+ icons/portal.gif \
+ icons/poweredby.gif \
+ icons/ps.gif \
+ icons/quill.gif \
+ icons/right.gif \
+ icons/screw1.gif \
+ icons/screw2.gif \
+ icons/script.gif \
+ icons/sound1.gif \
+ icons/sound2.gif \
+ icons/sphere1.gif \
+ icons/sphere2.gif \
+ icons/star.gif \
+ icons/star_blank.gif \
+ icons/tar.gif \
+ icons/tex.gif \
+ icons/text.gif \
+ icons/transfer.gif \
+ icons/unknown.gif \
+ icons/up.gif \
+ icons/uu.gif \
+ icons/uuencoded.gif \
+ icons/world1.gif \
+ icons/world2.gif
+
+SSL_FILES = ssl/ssl_client.pem \
+ ssl/ssl_server.pem
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt:
+
+clean:
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth
+ $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin
+ $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf
+ $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open
+ $(INSTALL_DATA) $(OPEN_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/open
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
+ $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc
+ $(INSTALL_DATA) $(MISC_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/misc
+ $(INSTALL_DIR) \
+ $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret
+ $(INSTALL_DIR) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret
+ $(INSTALL_DATA) $(SECRET_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/secret
+ $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \
+ $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs
+ $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons
+ $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl
+ $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs
+
+release_docs_spec:
+
diff --git a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf
index 8a74ed1afd..ceb94237d2 100644
--- a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf
+++ b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl
index 6abee5be2c..3189a758a5 100644
--- a/lib/inets/test/httpd_test_lib.erl
+++ b/lib/inets/test/httpd_test_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -72,6 +72,8 @@
'last-modified',
other=[] % list() - Key/Value list with other headers
}).
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%--------------------------------------------------------------------
@@ -81,7 +83,8 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options) ->
verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000).
verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) ->
{ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port),
- inets_test_lib:send(SocketType, Socket, RequestStr),
+
+ _SendRes = inets_test_lib:send(SocketType, Socket, RequestStr),
State = case inets_regexp:match(RequestStr, "printenv") of
nomatch ->
@@ -90,18 +93,26 @@ verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) ->
#state{print = true}
end,
- case request(State#state{request = RequestStr, socket = Socket}, TimeOut) of
- {error, Reson} ->
- {error, Reson};
+ case request(State#state{request = RequestStr,
+ socket = Socket}, TimeOut) of
+ {error, Reason} ->
+ tsp("request failed: "
+ "~n Reason: ~p", [Reason]),
+ {error, Reason};
NewState ->
+ tsp("validate reply: "
+ "~n NewState: ~p", [NewState]),
ValidateResult = validate(RequestStr, NewState, Options,
Node, Port),
+ tsp("validation result: "
+ "~n ~p", [ValidateResult]),
inets_test_lib:close(SocketType, Socket),
ValidateResult
end.
request(#state{mfa = {Module, Function, Args},
request = RequestStr, socket = Socket} = State, TimeOut) ->
+
HeadRequest = lists:sublist(RequestStr, 1, 4),
receive
{tcp, Socket, Data} ->
@@ -109,12 +120,12 @@ request(#state{mfa = {Module, Function, Args},
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
- {_, whole_body, _} when HeadRequest == "HEAD" ->
+ {_, whole_body, _} when HeadRequest =:= "HEAD" ->
State#state{body = <<>>};
NewMFA ->
request(State#state{mfa = NewMFA}, TimeOut)
end;
- {tcp_closed, Socket} when Function == whole_body ->
+ {tcp_closed, Socket} when Function =:= whole_body ->
print(tcp, "closed", State),
State#state{body = hd(Args)};
{tcp_closed, Socket} ->
@@ -126,12 +137,12 @@ request(#state{mfa = {Module, Function, Args},
case Module:Function([Data | Args]) of
{ok, Parsed} ->
handle_http_msg(Parsed, State);
- {_, whole_body, _} when HeadRequest == "HEAD" ->
+ {_, whole_body, _} when HeadRequest =:= "HEAD" ->
State#state{body = <<>>};
NewMFA ->
request(State#state{mfa = NewMFA}, TimeOut)
end;
- {ssl_closed, Socket} when Function == whole_body ->
+ {ssl_closed, Socket} when Function =:= whole_body ->
print(ssl, "closed", State),
State#state{body = hd(Args)};
{ssl_closed, Socket} ->
@@ -330,3 +341,9 @@ print(Proto, Data, #state{print = true}) ->
print(_, _, #state{print = false}) ->
ok.
+
+%% tsp(F) ->
+%% tsp(F, []).
+tsp(F, A) ->
+ test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+
diff --git a/lib/inets/test/httpd_time_test.erl b/lib/inets/test/httpd_time_test.erl
index 7d6aa08542..f39f9faff0 100644
--- a/lib/inets/test/httpd_time_test.erl
+++ b/lib/inets/test/httpd_time_test.erl
@@ -1,25 +1,25 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(httpd_time_test).
--export([t/3, t1/2, t2/2]).
+-export([t/3, t1/2, t2/2, t3/2, t4/2]).
-export([do/1, do/2, do/3, do/4, do/5]).
@@ -29,6 +29,9 @@
-record(stat, {pid, time = undefined, count = undefined, res}).
+%% -define(NUM_POLLERS, 10).
+-define(NUM_POLLERS, 1).
+
%%% -----------------------------------------------------------------
%%% Test suite interface
@@ -42,9 +45,17 @@ t2(Host, Port) ->
t(ssl, Host, Port).
+t3(Host, Port) ->
+ t(ossl, Host, Port).
+
+
+t4(Host, Port) ->
+ t(essl, Host, Port).
+
+
t(SocketType, Host, Port) ->
%% put(dbg,true),
- main(1, SocketType, Host, Port, 60000).
+ main(?NUM_POLLERS, SocketType, Host, Port, 60000).
@@ -111,28 +122,40 @@ loop(Pollers, Timeout) ->
"~n Timeout: ~p", [Timeout]),
Start = t(),
receive
- {'EXIT', Pid, {poller_stat_failure, Time, Reason}} ->
+ {'EXIT', Pid, {poller_stat_failure, SocketType, Host, Port, Time, Reason}} ->
case is_poller(Pid, Pollers) of
true ->
error_msg("received unexpected exit from poller ~p~n"
"befor completion of test "
- "(after ~p micro sec):~n"
- "~p~n", [Pid,Time,Reason]),
- exit({fail, {poller_exit, Pid, Reason}});
+ "after ~p micro sec"
+ "~n SocketType: ~p"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n~p~n",
+ [Pid, SocketType, Host, Port, Time, Reason]),
+ exit({fail, {poller_exit, Pid, Time, Reason}});
false ->
error_msg("received unexpected ~p from ~p"
"befor completion of test", [Reason, Pid]),
loop(Pollers, to(Timeout, Start))
end;
- {poller_stat_failure, Pid, {Time, Reason}} ->
+ {poller_stat_failure, Pid, {SocketType, Host, Port, Time, Reason}} ->
error_msg("received stat failure ~p from poller ~p after ~p "
- "befor completion of test", [Reason, Pid, Time]),
- exit({fail, {poller_failure, Pid, Reason}});
-
- {poller_stat_failure, Pid, Reason} ->
+ "befor completion of test"
+ "~n SocketType: ~p"
+ "~n Host: ~p"
+ "~n Port: ~p",
+ [Reason, Pid, Time, SocketType, Host, Port]),
+ exit({fail, {poller_failure, Pid, Time, Reason}});
+
+ {poller_stat_failure, Pid, SocketType, Host, Port, Reason} ->
error_msg("received stat failure ~p from poller ~p "
- "befor completion of test", [Reason, Pid]),
+ "befor completion of test"
+ "~n SocketType: ~p"
+ "~n Host: ~p"
+ "~n Port: ~p",
+ [Reason, Pid, SocketType, Host, Port]),
exit({fail, {poller_failure, Pid, Reason}});
Any ->
@@ -250,16 +273,16 @@ is_poller(Pid, [_|Rest]) ->
poller_main(Parent, SocketType, Host, Port) ->
process_flag(trap_exit,true),
- put(sname,poller),
+ put(sname, poller),
case timer:tc(?MODULE, poller_loop, [SocketType, Host, Port, uris()]) of
{Time, Count} when is_integer(Time) andalso is_integer(Count) ->
Parent ! {poller_statistics, self(), {Time, Count}};
{Time, {'EXIT', Reason}} when is_integer(Time) ->
- exit({poller_stat_failure, Time, Reason});
+ exit({poller_stat_failure, SocketType, Host, Port, Time, Reason});
{Time, Other} when is_integer(Time) ->
- Parent ! {poller_stat_failure, self(), {Time, Other}};
+ Parent ! {poller_stat_failure, self(), {SocketType, Host, Port, Time, Other}};
Else ->
- Parent ! {poller_stat_failure, self(), Else}
+ Parent ! {poller_stat_failure, self(), SocketType, Host, Port, Else}
end.
diff --git a/lib/inets/test/inets.cover b/lib/inets/test/inets.cover
new file mode 100644
index 0000000000..fd0ca41db3
--- /dev/null
+++ b/lib/inets/test/inets.cover
@@ -0,0 +1,2 @@
+{incl_app,inets,details}.
+
diff --git a/lib/inets/test/inets.spec b/lib/inets/test/inets.spec
index a9b4524295..ed102f8219 100644
--- a/lib/inets/test/inets.spec
+++ b/lib/inets/test/inets.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../inets_test"}}.
-{hosts, ["tuor"]}.
+{suites,"../inets_test",all}.
diff --git a/lib/inets/test/inets_SUITE.erl b/lib/inets/test/inets_SUITE.erl
index 56983caace..6fa0f44d77 100644
--- a/lib/inets/test/inets_SUITE.erl
+++ b/lib/inets/test/inets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
%%
-module(inets_SUITE).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include("inets_test_lib.hrl").
@@ -28,25 +28,26 @@
-define(NUM_DEFAULT_SERVICES, 1).
-all(doc) ->
- ["Test suites for the inets application."];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, app_test}, {group, appup_test},
+ {group, services_test}, httpd_reload].
+
+groups() ->
+ [{services_test, [],
+ [start_inets, start_httpc, start_httpd, start_ftpc,
+ start_tftpd]},
+ {app_test, [], [{inets_app_test, all}]},
+ {appup_test, [], [{inets_appup_test, all}]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [
- app_test,
- appup_test,
- services_test,
- httpd_reload
- ].
-services_test(suite) ->
- [
- start_inets,
- start_httpc,
- start_httpd,
- start_ftpc,
- start_tftpd
- ].
%%--------------------------------------------------------------------
@@ -100,11 +101,7 @@ end_per_testcase(_, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-app_test(suite) ->
- [{inets_app_test, all}].
-appup_test(suite) ->
- [{inets_appup_test, all}].
%%-------------------------------------------------------------------------
diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl
index 6bdb9bb308..11b507fa26 100644
--- a/lib/inets/test/inets_app_test.erl
+++ b/lib/inets/test/inets_app_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -39,28 +39,31 @@ init_per_testcase(undef_funcs, Config) ->
init_per_testcase(_, Config) ->
Config.
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- fields,
- modules,
- exportall,
- app_depend,
- undef_funcs
- ],
- {req, [], {conf, app_init, Cases, app_fin}}.
+all() ->
+ [fields, modules, exportall, app_depend,
+ undef_funcs].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-app_init(suite) -> [];
-app_init(doc) -> [];
-app_init(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
case is_app(inets) of
{ok, AppFile} ->
io:format("AppFile: ~n~p~n", [AppFile]),
@@ -81,9 +84,9 @@ is_app(App) ->
end.
-app_fin(suite) -> [];
-app_fin(doc) -> [];
-app_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
diff --git a/lib/inets/test/inets_appup_test.erl b/lib/inets/test/inets_appup_test.erl
index d580c6c4c5..7ed237243e 100644
--- a/lib/inets/test/inets_appup_test.erl
+++ b/lib/inets/test/inets_appup_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,40 +18,47 @@
%%
%%
%%----------------------------------------------------------------------
-%% Purpose: Verify the application specifics of the Megaco application
+%% Purpose: Verify the application specifics of the Inets application
%%----------------------------------------------------------------------
-module(inets_appup_test).
-compile(export_all).
+-compile({no_auto_import,[error/1]}).
-include("inets_test_lib.hrl").
-% t() -> megaco_test_lib:t(?MODULE).
-% t(Case) -> megaco_test_lib:t({?MODULE, Case}).
+ % t() -> megaco_test_lib:t(?MODULE).
+ % t(Case) -> megaco_test_lib:t({?MODULE, Case}).
%% Test server callbacks
init_per_testcase(_Case, Config) ->
Config.
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- appup
- ],
- {req, [], {conf, appup_init, Cases, appup_fin}}.
+all() ->
+ [appup].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup_init(suite) -> [];
-appup_init(doc) -> [];
-appup_init(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
AppFile = file_name(inets, ".app"),
AppupFile = file_name(inets, ".appup"),
[{app_file, AppFile}, {appup_file, AppupFile}|Config].
@@ -62,9 +69,9 @@ file_name(App, Ext) ->
filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-appup_fin(suite) -> [];
-appup_fin(doc) -> [];
-appup_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
diff --git a/lib/inets/test/inets_sup_SUITE.erl b/lib/inets/test/inets_sup_SUITE.erl
index ba41e0960c..1d262a2739 100644
--- a/lib/inets/test/inets_sup_SUITE.erl
+++ b/lib/inets/test/inets_sup_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,22 +20,27 @@
-module(inets_sup_SUITE).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
%% Note: This directive should only be used in test suites.
-compile(export_all).
-all(doc) ->
- ["Test that the inets supervisorstructur is the expected one."];
-all(suite) ->
- [
- default_tree,
- ftpc_worker,
- tftpd_worker,
- httpd_subtree,
- httpc_subtree
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [default_tree, ftpc_worker, tftpd_worker, httpd_subtree,
+ httpc_subtree].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
@@ -372,11 +377,11 @@ httpc_subtree(Config) when is_list(Config) ->
"~n Config: ~p", [Config]),
tsp("httpc_subtree -> start inets service httpc with profile foo"),
- {ok, Foo} = inets:start(httpc, [{profile, foo}]),
+ {ok, _Foo} = inets:start(httpc, [{profile, foo}]),
tsp("httpc_subtree -> "
"start stand-alone inets service httpc with profile bar"),
- {ok, Bar} = inets:start(httpc, [{profile, bar}], stand_alone),
+ {ok, _Bar} = inets:start(httpc, [{profile, bar}], stand_alone),
tsp("httpc_subtree -> retreive list of httpc instances"),
HttpcChildren = supervisor:which_children(httpc_profile_sup),
diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl
index 6af2ad32f7..c56a714f5a 100644
--- a/lib/inets/test/inets_test_lib.erl
+++ b/lib/inets/test/inets_test_lib.erl
@@ -1,44 +1,136 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(inets_test_lib).
-include("inets_test_lib.hrl").
+-include_lib("inets/src/http_lib/http_internal.hrl").
%% Various small utility functions
--export([start_http_server/1, start_http_server_ssl/1]).
+-export([start_http_server/1, start_http_server/2]).
+-export([start_http_server_ssl/1, start_http_server_ssl/2]).
-export([hostname/0]).
-export([connect_bin/3, connect_byte/3, send/3, close/2]).
-export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]).
-export([info/4, log/4, debug/4, print/4]).
-export([check_body/1]).
-export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]).
+-export([oscmd/1]).
-export([non_pc_tc_maybe_skip/4, os_based_skip/1]).
+-export([flush/0]).
+-export([start_node/1, stop_node/1]).
+
+%% -- Misc os command and stuff
+
+oscmd(Cmd) ->
+ string:strip(os:cmd(Cmd), right, $\n).
+
+%% -- Misc node operation wrapper functions --
+
+start_node(Name) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ Args = case init:get_argument('CC_TEST') of
+ {ok, [[]]} ->
+ " -pa /clearcase/otp/libraries/snmp/ebin ";
+ {ok, [[Path]]} ->
+ " -pa " ++ Path;
+ error ->
+ ""
+ end,
+ A = Args ++ " -pa " ++ Pa,
+ Opts = [{cleanup,false}, {args, A}],
+ case (catch test_server:start_node(Name, slave, Opts)) of
+ {ok, Node} ->
+ Node;
+ Else ->
+ exit({failed_starting_node, Name, Else})
+ end.
+
+stop_node(Node) ->
+ rpc:cast(Node, erlang, halt, []),
+ await_stopped(Node, 5).
+
+await_stopped(_, 0) ->
+ ok;
+await_stopped(Node, N) ->
+ Nodes = erlang:nodes(),
+ case lists:member(Node, Nodes) of
+ true ->
+ sleep(1000),
+ await_stopped(Node, N-1);
+ false ->
+ ok
+ end.
+
+
+%% ----------------------------------------------------------------
+%% HTTPD starter functions
+%%
start_http_server(Conf) ->
+ start_http_server(Conf, ?HTTP_DEFAULT_SSL_KIND).
+
+start_http_server(Conf, essl = _SslTag) ->
+ application:start(crypto),
+ do_start_http_server(Conf);
+start_http_server(Conf, _SslTag) ->
+ do_start_http_server(Conf).
+
+do_start_http_server(Conf) ->
+ tsp("start http server with "
+ "~n Conf: ~p"
+ "~n", [Conf]),
application:load(inets),
- ok = application:set_env(inets, services, [{httpd, Conf}]),
- ok = application:start(inets).
-
+ case application:set_env(inets, services, [{httpd, Conf}]) of
+ ok ->
+ case application:start(inets) of
+ ok ->
+ ok;
+ Error1 ->
+ test_server:format("<ERROR> Failed starting application: "
+ "~n Error: ~p"
+ "~n", [Error1]),
+ Error1
+ end;
+ Error2 ->
+ test_server:format("<ERROR> Failed set application env: "
+ "~n Error: ~p"
+ "~n", [Error2]),
+ Error2
+ end.
+
start_http_server_ssl(FileName) ->
+ start_http_server_ssl(FileName, ?HTTP_DEFAULT_SSL_KIND).
+
+start_http_server_ssl(FileName, essl = _SslTag) ->
+ application:start(crypto),
+ do_start_http_server_ssl(FileName);
+start_http_server_ssl(FileName, _SslTag) ->
+ do_start_http_server_ssl(FileName).
+
+do_start_http_server_ssl(FileName) ->
+ tsp("start (ssl) http server with "
+ "~n FileName: ~p"
+ "~n", [FileName]),
application:start(ssl),
- catch start_http_server(FileName).
+ catch do_start_http_server(FileName).
+
%% ----------------------------------------------------------------------
%% print functions
@@ -84,27 +176,17 @@ copy_files(FromDir, ToDir) ->
copy_dirs(FromDirRoot, ToDirRoot) ->
-%% io:format("~w:copy_dirs -> entry with"
-%% "~n FromDirRoot: ~p"
-%% "~n ToDirRoot: ~p"
-%% "~n", [?MODULE, FromDirRoot, ToDirRoot]),
{ok, Files} = file:list_dir(FromDirRoot),
lists:foreach(
fun(FileOrDir) ->
%% Check if it's a directory or a file
-%% io:format("~w:copy_dirs -> check ~p"
-%% "~n", [?MODULE, FileOrDir]),
case filelib:is_dir(filename:join(FromDirRoot, FileOrDir)) of
true ->
-%% io:format("~w:copy_dirs -> ~p is a directory"
-%% "~n", [?MODULE, FileOrDir]),
FromDir = filename:join([FromDirRoot, FileOrDir]),
ToDir = filename:join([ToDirRoot, FileOrDir]),
ok = file:make_dir(ToDir),
copy_dirs(FromDir, ToDir);
false ->
-%% io:format("~w:copy_dirs -> ~p is a file"
-%% "~n", [?MODULE, FileOrDir]),
copy_file(FileOrDir, FromDirRoot, ToDirRoot)
end
end, Files).
@@ -133,8 +215,8 @@ check_body(Body) ->
0 ->
case string:rstr(Body, "</HTML>") of
0 ->
- test_server:format("Body ~p~n", [Body]),
- test_server:fail(did_not_receive_whole_body);
+ tsp("Body ~p", [Body]),
+ tsf(did_not_receive_whole_body);
_ ->
ok
end;
@@ -204,9 +286,31 @@ os_based_skip(_) ->
%% Port -> integer()
connect_bin(ssl, Host, Port) ->
+ connect(ssl, Host, Port, [binary, {packet,0}]);
+connect_bin(ossl, Host, Port) ->
+ connect(ssl, Host, Port, [{ssl_imp, old}, binary, {packet,0}]);
+connect_bin(essl, Host, Port) ->
+ connect(ssl, Host, Port, [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true}]);
+connect_bin(ip_comm, Host, Port) ->
+ Opts = [inet6, binary, {packet,0}],
+ connect(ip_comm, Host, Port, Opts).
+
+
+connect_byte(ssl, Host, Port) ->
+ connect(ssl, Host, Port, [{packet,0}]);
+connect_byte(ossl, Host, Port) ->
+ connect(ssl, Host, Port, [{ssl_imp, old}, {packet,0}]);
+connect_byte(essl, Host, Port) ->
+ connect(ssl, Host, Port, [{ssl_imp, new}, {packet,0}]);
+connect_byte(ip_comm, Host, Port) ->
+ Opts = [inet6, {packet,0}],
+ connect(ip_comm, Host, Port, Opts).
+
+
+connect(ssl, Host, Port, Opts) ->
ssl:start(),
%% Does not support ipv6 in old ssl
- case ssl:connect(Host, Port, [binary, {packet,0}]) of
+ case ssl:connect(Host, Port, Opts) of
{ok, Socket} ->
{ok, Socket};
{error, Reason} ->
@@ -214,61 +318,51 @@ connect_bin(ssl, Host, Port) ->
Error ->
Error
end;
-connect_bin(ip_comm, Host, Port) ->
- Opts = [inet6, binary, {packet,0}],
- connect(ip_comm, Host, Port, Opts).
-
-
connect(ip_comm, Host, Port, Opts) ->
- test_server:format("gen_tcp:connect(~p, ~p, ~p) ~n", [Host, Port, Opts]),
case gen_tcp:connect(Host,Port, Opts) of
{ok, Socket} ->
- test_server:format("connect success~n", []),
+ %% tsp("connect success"),
{ok, Socket};
{error, nxdomain} ->
- test_server:format("nxdomain opts: ~p~n", [Opts]),
+ tsp("nxdomain opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, eafnosupport} ->
- test_server:format("eafnosupport opts: ~p~n", [Opts]),
+ tsp("eafnosupport opts: ~p", [Opts]),
+ connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
+ {error, enetunreach} ->
+ tsp("eafnosupport opts: ~p", [Opts]),
connect(ip_comm, Host, Port, lists:delete(inet6, Opts));
{error, {enfile,_}} ->
- test_server:format("Error enfile~n", []),
+ tsp("Error enfile"),
{error, enfile};
Error ->
- test_server:format("Unexpected error: "
- "~n Error: ~p"
- "~nwhen"
- "~n Host: ~p"
- "~n Port: ~p"
- "~n Opts: ~p"
- "~n", [Error, Host, Port, Opts]),
+ tsp("Unexpected error: "
+ "~n Error: ~p"
+ "~nwhen"
+ "~n Host: ~p"
+ "~n Port: ~p"
+ "~n Opts: ~p"
+ "~n", [Error, Host, Port, Opts]),
Error
end.
-connect_byte(ip_comm, Host, Port) ->
- Opts = [inet6, {packet,0}],
- connect(ip_comm, Host, Port, Opts);
-
-connect_byte(ssl, Host, Port) ->
- ssl:start(),
- %% Does not support ipv6 in old ssl
- case ssl:connect(Host,Port,[{packet,0}]) of
- {ok,Socket} ->
- {ok,Socket};
- {error,{enfile,_}} ->
- {error, enfile};
- Error ->
- Error
- end.
send(ssl, Socket, Data) ->
ssl:send(Socket, Data);
+send(ossl, Socket, Data) ->
+ ssl:send(Socket, Data);
+send(essl, Socket, Data) ->
+ ssl:send(Socket, Data);
send(ip_comm,Socket,Data) ->
gen_tcp:send(Socket,Data).
close(ssl,Socket) ->
catch ssl:close(Socket);
+close(ossl,Socket) ->
+ catch ssl:close(Socket);
+close(essl,Socket) ->
+ catch ssl:close(Socket);
close(ip_comm,Socket) ->
catch gen_tcp:close(Socket).
@@ -300,3 +394,20 @@ sleep(MSecs) ->
skip(Reason, File, Line) ->
exit({skipped, {Reason, File, Line}}).
+
+flush() ->
+ receive
+ Msg ->
+ [Msg | flush()]
+ after 1000 ->
+ []
+ end.
+
+
+tsp(F) ->
+ tsp(F, []).
+tsp(F, A) ->
+ test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]).
+
+tsf(Reason) ->
+ test_server:fail(Reason).
diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl
index 12a43fa136..0cdb04139c 100644
--- a/lib/inets/test/inets_test_lib.hrl
+++ b/lib/inets/test/inets_test_lib.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -46,6 +46,11 @@
-endif.
+%% - OS Command and stuff
+
+-define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)).
+
+
%% - Test case macros -
-define(EXPANDABLE(I, C, F), inets_test_lib:expandable(I, C, F)).
diff --git a/lib/inets/test/tftp_SUITE.erl b/lib/inets/test/tftp_SUITE.erl
index 5768fff88b..59fb644667 100644
--- a/lib/inets/test/tftp_SUITE.erl
+++ b/lib/inets/test/tftp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -64,24 +64,34 @@ default_config() ->
init_per_testcase(Case, Config) ->
tftp_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) when is_list(Config) ->
- tftp_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) when is_list(Config) ->
+ tftp_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(doc) ->
- ["Test suites for TFTP."];
-
-all(suite) ->
- [
- simple,
- extra,
- reuse_connection,
- resend_client,
- resend_server
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [simple, extra, reuse_connection, resend_client,
+ resend_server].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Simple
diff --git a/lib/inets/test/tftp_test_lib.erl b/lib/inets/test/tftp_test_lib.erl
index 3729309b0e..e9b691828f 100644
--- a/lib/inets/test/tftp_test_lib.erl
+++ b/lib/inets/test/tftp_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,7 @@ init_per_testcase(_Case, Config) when is_list(Config) ->
?IGNORE(application:stop(inets)),
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
?IGNORE(application:stop(inets)),
Config.
@@ -143,7 +143,7 @@ eval(Mod, Fun, Config) ->
Config2 = Mod:init_per_testcase(Fun, Config),
Pid = spawn_link(?MODULE, do_eval, [self(), Mod, Fun, Config2]),
R = wait_for_evaluator(Pid, Mod, Fun, Config2, []),
- Mod:fin_per_testcase(Fun, Config2),
+ Mod:end_per_testcase(Fun, Config2),
global:unregister_name(tftp_test_case_sup),
process_flag(trap_exit, Flag),
R.
diff --git a/lib/inets/test/tftp_test_lib.hrl b/lib/inets/test/tftp_test_lib.hrl
index da4b065976..bef024720a 100644
--- a/lib/inets/test/tftp_test_lib.hrl
+++ b/lib/inets/test/tftp_test_lib.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 7776bef0a5..b1de3fef43 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -1,102 +1,24 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2001-2011. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 5.3.2
+INETS_VSN = 5.5.2
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
-TICKETS = OTP-8542 OTP-8607
-
-TICKETS_5_3_1 = \
- OTP-8508 \
- OTP-8509
-
-TICKETS_5_3 = \
- OTP-8016 \
- OTP-8056 \
- OTP-8103 \
- OTP-8106 \
- OTP-8312 \
- OTP-8315 \
- OTP-8327 \
- OTP-8349 \
- OTP-8351 \
- OTP-8352 \
- OTP-8359 \
- OTP-8371
-
-TICKETS_5_2 = OTP-8204 OTP-8206 OTP-8247 OTP-8248 OTP-8249 OTP-8258 OTP-8280
-
-TICKETS_5_1_3 = OTP-8154
-
-TICKETS_5_1_2 = OTP-7298 OTP-8101 OTP-8118
-
-TICKETS_5_1_1 = OTP-8052 OTP-8069
-
-TICKETS_5_1 = OTP-7994 OTP-7998 OTP-8001 OTP-8004 OTP-8005
-
-TICKETS_5_0_14 = OTP-7882 OTP-7883 OTP-7888 OTP-7950 OTP-7976
-
-TICKETS_5.0.13 = \
- OTP-7723 \
- OTP-7724 \
- OTP-7726 \
- OTP-7463 \
- OTP-7815 \
- OTP-7857
-
-# TICKETS_5.0.12 = \
-# OTP-7636
-#
-# TICKETS_5.0.11 = \
-# OTP-7574 \
-# OTP-7597 \
-# OTP-7598 \
-# OTP-7605
-#
-# TICKETS_5.0.10 = \
-# OTP-7450 \
-# OTP-7454 \
-# OTP-7490 \
-# OTP-7512
-#
-# TICKETS_5.0.9 = \
-# OTP-7257 \
-# OTP-7323 \
-# OTP-7341
-#
-# TICKETS_5.0.8 = \
-# OTP-7315 \
-# OTP-7321
-#
-# TICKETS_5.0.7 = \
-# OTP-7304
-#
-# TICKETS_5.0.6 = \
-# OTP-7266
-#
-# TICKETS_5.0.5 = \
-# OTP-7220 \
-# OTP-7221
-#
-# TICKETS_5.0.4 = \
-# OTP-7173
-#
-
diff --git a/lib/inviso/doc/src/inviso_as_lib.xml b/lib/inviso/doc/src/inviso_as_lib.xml
index 80694efd67..1f4961166c 100644
--- a/lib/inviso/doc/src/inviso_as_lib.xml
+++ b/lib/inviso/doc/src/inviso_as_lib.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/inviso/doc/src/inviso_lfm.xml b/lib/inviso/doc/src/inviso_lfm.xml
index 02e012f2ea..70207d0b58 100644
--- a/lib/inviso/doc/src/inviso_lfm.xml
+++ b/lib/inviso/doc/src/inviso_lfm.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/inviso/doc/src/inviso_lfm_tpfreader.xml b/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
index eba3e63e2e..bae40522a3 100644
--- a/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
+++ b/lib/inviso/doc/src/inviso_lfm_tpfreader.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/inviso/doc/src/inviso_rt.xml b/lib/inviso/doc/src/inviso_rt.xml
index 1579c873a3..3a8e77f65c 100644
--- a/lib/inviso/doc/src/inviso_rt.xml
+++ b/lib/inviso/doc/src/inviso_rt.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/inviso/doc/src/notes.xml b/lib/inviso/doc/src/notes.xml
index 443aaf523b..7c2c3c3bde 100644
--- a/lib/inviso/doc/src/notes.xml
+++ b/lib/inviso/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2006</year><year>2009</year>
+ <year>2006</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -31,7 +31,23 @@
<p>This document describes the changes made to the Inviso application.</p>
- <section><title>Inviso 0.6.1</title>
+ <section><title>Inviso 0.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The obsolete guards has now been changed to the new guard
+ interface.</p>
+ <p>
+ Own Id: OTP-8747</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inviso 0.6.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/inviso/src/inviso.erl b/lib/inviso/src/inviso.erl
index de42926ffe..0eda06a5c2 100644
--- a/lib/inviso/src/inviso.erl
+++ b/lib/inviso/src/inviso.erl
@@ -129,7 +129,7 @@ start(Options) ->
add_node(Reference) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,any_ref},?CALL_TIMEOUT).
-add_node(Reference,Options) when list(Options) ->
+add_node(Reference,Options) when is_list(Options) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,any_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -142,7 +142,7 @@ add_node(Reference,Options) when list(Options) ->
add_node_if_ref(Reference) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],[],Reference,if_ref},?CALL_TIMEOUT).
-add_node_if_ref(Reference,Options) when list(Options) ->
+add_node_if_ref(Reference,Options) when is_list(Options) ->
gen_server:call(?CONTROLLER,{add_nodes,[node()],Options,Reference,if_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -156,10 +156,10 @@ add_node_if_ref(Reference,Options) when list(Options) ->
%% system. By speicifying node() as the node where the runtime component shall
%% be started. The return value will then follow the rules of non distributed
%% returnvalues and not have a node indicator.
-add_nodes(Nodes,Reference) when list(Nodes) ->
+add_nodes(Nodes,Reference) when is_list(Nodes) ->
gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,any_ref},?CALL_TIMEOUT).
-add_nodes(Nodes,Reference,Options) when list(Nodes),list(Options) ->
+add_nodes(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) ->
gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,any_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -168,10 +168,10 @@ add_nodes(Nodes,Reference,Options) when list(Nodes),list(Options) ->
%%
%% As add_nodes/2,/3 but will only connect to an already existing runtime component
%% if its reference is the same as the one given as argument.
-add_nodes_if_ref(Nodes,Reference) when list(Nodes) ->
+add_nodes_if_ref(Nodes,Reference) when is_list(Nodes) ->
gen_server:call(?CONTROLLER,{add_nodes,Nodes,[],Reference,if_ref},?CALL_TIMEOUT).
-add_nodes_if_ref(Nodes,Reference,Options) when list(Nodes),list(Options) ->
+add_nodes_if_ref(Nodes,Reference,Options) when is_list(Nodes),is_list(Options) ->
gen_server:call(?CONTROLLER,{add_nodes,Nodes,Options,Reference,if_ref},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -182,9 +182,9 @@ add_nodes_if_ref(Nodes,Reference,Options) when list(Nodes),list(Options) ->
%%
%% Change options on all or specified Nodes. This may result in for instance
%% reinitialization of overloadcheck.
-change_options(Options) when list(Options) ->
+change_options(Options) when is_list(Options) ->
gen_server:call(?CONTROLLER,{change_options,all,Options},?CALL_TIMEOUT).
-change_options(Nodes,Options) when list(Nodes),list(Options) ->
+change_options(Nodes,Options) when is_list(Nodes),is_list(Options) ->
gen_server:call(?CONTROLLER,{change_options,Nodes,Options},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -234,7 +234,7 @@ change_options(Nodes,Options) when list(Nodes),list(Options) ->
init_tracing(TracerDataList) ->
gen_server:call(?CONTROLLER,{init_tracing,TracerDataList},?CALL_TIMEOUT).
-init_tracing(Nodes,TracerData) when list(Nodes) ->
+init_tracing(Nodes,TracerData) when is_list(Nodes) ->
gen_server:call(?CONTROLLER,{init_tracing,Nodes,TracerData},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -270,10 +270,10 @@ stop_tracing(Nodes) when is_list(Nodes) ->
clear() ->
gen_server:call(?CONTROLLER,{clear,all,[]},?CALL_TIMEOUT).
-clear(Nodes) when list(Nodes) ->
+clear(Nodes) when is_list(Nodes) ->
gen_server:call(?CONTROLLER,{clear,Nodes,[]},?CALL_TIMEOUT).
-clear(Nodes,Options) when list(Nodes),list(Options) ->
+clear(Nodes,Options) when is_list(Nodes),is_list(Options) ->
gen_server:call(?CONTROLLER,{clear,Nodes,Options},?CALL_TIMEOUT).
%% -----------------------------------------------------------------------------
@@ -355,9 +355,9 @@ stop_all() ->
tp(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
-tp(Nodes,Module,Function,Arity,MatchSpec) when list(Nodes) ->
+tp(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[global]);
-tp(Module,Function,Arity,MatchSpec,Opts) when atom(Module) ->
+tp(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) ->
trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[global]).
tp(Module,Function,Arity,MatchSpec) ->
@@ -384,9 +384,9 @@ tp(PatternList) ->
tpl(Nodes,Module,Function,Arity,MatchSpec,Opts) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
-tpl(Nodes,Module,Function,Arity,MatchSpec) when list(Nodes) ->
+tpl(Nodes,Module,Function,Arity,MatchSpec) when is_list(Nodes) ->
trace_pattern(Nodes,[{Module,Function,Arity,MatchSpec,[]}],[local]);
-tpl(Module,Function,Arity,MatchSpec,Opts) when atom(Module) ->
+tpl(Module,Function,Arity,MatchSpec,Opts) when is_atom(Module) ->
trace_pattern(all,[{Module,Function,Arity,MatchSpec,Opts}],[local]).
tpl(Module,Function,Arity,MatchSpec) ->
@@ -417,12 +417,12 @@ ctp(Nodes,Module,Function,Arity) ->
ctp(Module,Function,Arity) ->
trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[global]).
-ctp(Nodes,PatternList) when list(PatternList) ->
+ctp(Nodes,PatternList) when is_list(PatternList) ->
trace_pattern(Nodes,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[global]).
-ctp(PatternList) when list(PatternList) ->
+ctp(PatternList) when is_list(PatternList) ->
trace_pattern(all,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[global]).
@@ -445,12 +445,12 @@ ctpl(Nodes,Module,Function,Arity) ->
ctpl(Module,Function,Arity) ->
trace_pattern(all,[{Module,Function,Arity,false,[only_loaded]}],[local]).
-ctpl(Nodes,PatternList) when list(PatternList) ->
+ctpl(Nodes,PatternList) when is_list(PatternList) ->
trace_pattern(Nodes,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[local]).
-ctpl(PatternList) when list(PatternList) ->
+ctpl(PatternList) when is_list(PatternList) ->
trace_pattern(all,
lists:map(fun({M,F,A})->{M,F,A,false,[only_loaded]} end,PatternList),
[local]).
@@ -485,19 +485,19 @@ trace_pattern(Nodes,Patterns,FlagList) ->
%% specifying a certain pid at all nodes. Or an empty TraceConfList for all
%% nodes.
%% When calling several nodes, the nodes are called in parallel.
-tf(Nodes,PidSpec,FlagList) when list(Nodes),list(FlagList) ->
+tf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) ->
trace_flags(Nodes,[{PidSpec, FlagList}],true).
-tf(Nodes,TraceConfList) when list(Nodes),list(TraceConfList) ->
+tf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) ->
trace_flags(Nodes,TraceConfList,true);
-tf(PidSpec,FlagList) when list(FlagList) ->
+tf(PidSpec,FlagList) when is_list(FlagList) ->
trace_flags(all,[{PidSpec,FlagList}],true).
-tf(ArgList) when list(ArgList) -> % This one has triple functionality!
+tf(ArgList) when is_list(ArgList) -> % This one has triple functionality!
case ArgList of
- [{_Process,Flags}|_] when list(Flags),atom(hd(Flags))-> % A call to all nodes.
+ [{_Process,Flags}|_] when is_list(Flags),is_atom(hd(Flags))-> % A call to all nodes.
trace_flags(all,ArgList,true);
- [{_Node,TraceConfList}|_] when list(TraceConfList),tuple(hd(TraceConfList)) ->
+ [{_Node,TraceConfList}|_] when is_list(TraceConfList),is_tuple(hd(TraceConfList)) ->
trace_flags(ArgList,true);
[{_Node,_TraceConfList,_How}|_] ->
trace_flags(ArgList);
@@ -517,15 +517,15 @@ tf(ArgList) when list(ArgList) -> % This one has triple functionality
%% The functions without a Nodes argument means all nodes, in a non-distributed
%% environment it means the local node.
%% When calling several nodes, the nodes are called in parallel.
-ctf(Nodes,PidSpec,FlagList) when list(Nodes),list(FlagList) ->
+ctf(Nodes,PidSpec,FlagList) when is_list(Nodes),is_list(FlagList) ->
trace_flags(Nodes,[{PidSpec,FlagList}],false).
-ctf(Nodes,TraceConfList) when list(Nodes),list(TraceConfList) ->
+ctf(Nodes,TraceConfList) when is_list(Nodes),is_list(TraceConfList) ->
trace_flags(Nodes,TraceConfList,false);
-ctf(PidSpec,FlagList) when list(FlagList) ->
+ctf(PidSpec,FlagList) when is_list(FlagList) ->
trace_flags(all,[{PidSpec,FlagList}],false).
-ctf(TraceConfList) when list(TraceConfList) ->
+ctf(TraceConfList) when is_list(TraceConfList) ->
trace_flags(all,TraceConfList,false).
%% -----------------------------------------------------------------------------
@@ -668,10 +668,10 @@ init_tpm(Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
tpm(Mod,Func,Arity,MS) ->
tpm(all,Mod,Func,Arity,MS).
-tpm(Nodes,Mod,Func,Arity,MS) when integer(Arity) ->
+tpm(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) ->
gen_server:call(?CONTROLLER,
{meta_pattern,Nodes,{tpm,[Mod,Func,Arity,MS]}});
-tpm(Mod,Func,Arity,MS,CallFunc) when integer(Arity) ->
+tpm(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) ->
tpm(all,Mod,Func,Arity,MS,CallFunc).
tpm(Nodes,Mod,Func,Arity,MS,CallFunc) ->
@@ -695,11 +695,11 @@ tpm(Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
tpm_tracer(Mod,Func,Arity,MS) ->
tpm_tracer(all,Mod,Func,Arity,MS).
-tpm_tracer(Nodes,Mod,Func,Arity,MS) when integer(Arity) ->
+tpm_tracer(Nodes,Mod,Func,Arity,MS) when is_integer(Arity) ->
gen_server:call(?CONTROLLER,
{meta_pattern,Nodes,{tpm_tracer,[Mod,Func,Arity,MS]}},
?CALL_TIMEOUT);
-tpm_tracer(Mod,Func,Arity,MS,CallFunc) when integer(Arity) ->
+tpm_tracer(Mod,Func,Arity,MS,CallFunc) when is_integer(Arity) ->
tpm_tracer(all,Mod,Func,Arity,MS,CallFunc).
tpm_tracer(Nodes,Mod,Func,Arity,MS,CallFunc) ->
@@ -878,7 +878,7 @@ cancel_suspension() ->
%% Status=running|{suspended,SReason}
%%
%% Get Status form all or specified runtime components.
-get_status(Nodes) when list(Nodes) ->
+get_status(Nodes) when is_list(Nodes) ->
gen_server:call(?CONTROLLER,{get_status,Nodes},?CALL_TIMEOUT).
get_status() ->
@@ -918,7 +918,7 @@ get_tracerdata(Nodes) when is_list(Nodes) ->
list_logs() ->
gen_server:call(?CONTROLLER,list_logs,?CALL_TIMEOUT).
-list_logs(TracerDataOrNodesList) when list(TracerDataOrNodesList) ->
+list_logs(TracerDataOrNodesList) when is_list(TracerDataOrNodesList) ->
gen_server:call(?CONTROLLER,{list_logs,TracerDataOrNodesList},?CALL_TIMEOUT).
%% ------------------------------------------------------------------------------
@@ -960,17 +960,17 @@ list_logs(TracerDataOrNodesList) when list(TracerDataOrNodesList) ->
%% Note that the client process using this function will wait until all files
%% are moved. The job can be cancelled, causing any already copied files to be
%% removed, by simply terminating the waiting client process.
-fetch_log(DestDir,Prefix) when list(DestDir),list(Prefix) ->
+fetch_log(DestDir,Prefix) when is_list(DestDir),is_list(Prefix) ->
gen_server:call(?CONTROLLER,{fetch_log,node(),all,DestDir,Prefix},infinity).
-fetch_log(ToNode,DestDir,Prefix) when atom(ToNode),list(DestDir),list(Prefix) ->
+fetch_log(ToNode,DestDir,Prefix) when is_atom(ToNode),is_list(DestDir),is_list(Prefix) ->
gen_server:call(?CONTROLLER,{fetch_log,ToNode,all,DestDir,Prefix},infinity);
-fetch_log(LogSpecList,DestDir,Prefix) when list(LogSpecList),list(DestDir),list(Prefix) ->
+fetch_log(LogSpecList,DestDir,Prefix) when is_list(LogSpecList),is_list(DestDir),is_list(Prefix) ->
gen_server:call(?CONTROLLER,{fetch_log,node(),LogSpecList,DestDir,Prefix},infinity).
fetch_log(ToNode,LogSpecList,DestDir,Prefix)
- when atom(ToNode),list(LogSpecList),list(DestDir),list(Prefix) ->
+ when is_atom(ToNode),is_list(LogSpecList),is_list(DestDir),is_list(Prefix) ->
gen_server:call(?CONTROLLER,{fetch_log,ToNode,LogSpecList,DestDir,Prefix},infinity).
%% ------------------------------------------------------------------------------
diff --git a/lib/inviso/src/inviso_c.erl b/lib/inviso/src/inviso_c.erl
index ecbd5b0778..b1597a7f35 100644
--- a/lib/inviso/src/inviso_c.erl
+++ b/lib/inviso/src/inviso_c.erl
@@ -100,7 +100,7 @@ init({_Parent,Options}) ->
end.
%% ------------------------------------------------------------------------------
-handle_call({subscribe,Pid},_From,LD) when pid(Pid) ->
+handle_call({subscribe,Pid},_From,LD) when is_pid(Pid) ->
MRef=erlang:monitor(process,Pid),
{reply,ok,LD#state{subscribers=[{Pid,MRef}|LD#state.subscribers]}};
handle_call({subscribe,Faulty},_From,LD) ->
@@ -131,7 +131,7 @@ handle_call({change_options,Nodes,Opts},_From,LD) ->
end;
handle_call({init_tracing,TracerDataList},_From,LD) ->
{reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};
-handle_call({init_tracing,Nodes,TracerData},_From,LD) when list(Nodes) ->
+handle_call({init_tracing,Nodes,TracerData},_From,LD) when is_list(Nodes) ->
TracerDataList=
lists:map(fun(N)->{N,TracerData} end,started_trace_nodes(Nodes,LD)),
{reply,adapt_reply(LD,do_init_tracing(TracerDataList,LD)),LD};
@@ -173,7 +173,7 @@ handle_call({fetch_log,ToNode,Spec,Dest,Prefix},From,LD) ->
end;
handle_call({delete_log,NodesOrNodeSpecs},_From,LD) ->
{reply,adapt_reply(LD,do_delete_log(NodesOrNodeSpecs,LD)),LD};
-handle_call({delete_log,Nodes,Specs},_From,LD) when list(Nodes) ->
+handle_call({delete_log,Nodes,Specs},_From,LD) when is_list(Nodes) ->
Reply=do_delete_log(lists:map(fun(N)->{N,Specs} end,Nodes),LD),
{reply,adapt_reply(LD,Reply),LD};
handle_call({delete_log,FaultyNodes,_Specs},_From,LD) ->
@@ -283,7 +283,7 @@ do_change_option(Nodes,Options,LD) ->
do_change_option_2([Node|Tail],Options,LD,Replies) ->
case get_node_rec(Node,LD) of
- Rec when record(Rec,node) ->
+ Rec when is_record(Rec,node) ->
Answer=?RUNTIME:change_options(Rec#node.pid,Options),
do_change_option_2(Tail,Options,LD,[{Node,Answer}|Replies]);
Error ->
@@ -333,7 +333,7 @@ do_init_tracing_2(What,_LD,_) ->
%% Returns {ok,Reply} or {error,Reason}.
distribute_tp(all,Patterns,FlagList,LD) ->
distribute_tp(started_trace_nodes(all,LD),Patterns,FlagList,LD);
-distribute_tp(Nodes,Patterns,FlagList,LD) when list(Nodes) ->
+distribute_tp(Nodes,Patterns,FlagList,LD) when is_list(Nodes) ->
RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
#node{pid=Pid} ->
{Pid,N};
@@ -354,7 +354,7 @@ distribute_tp(Faulty,_,_,_) ->
%% Returns {ok,Reply} or {error,Reason}.
distribute_tf(all,Args,How,LD) ->
distribute_tf(started_trace_nodes(all,LD),Args,How,LD);
-distribute_tf(Nodes,Args,How,LD) when list(Nodes) ->
+distribute_tf(Nodes,Args,How,LD) when is_list(Nodes) ->
RTpids=lists:map(fun(Node)->
case get_node_rec(Node,LD) of
#node{pid=Pid} ->
@@ -369,7 +369,7 @@ distribute_tf(Faulty,_,_,_) ->
{error,{badarg,Faulty}}.
%% As above but specific args for each node.
-distribute_tf(NodeArgs,How,LD) when list(NodeArgs) ->
+distribute_tf(NodeArgs,How,LD) when is_list(NodeArgs) ->
RTpidArgs=lists:map(fun({Node,Args})->
case get_node_rec(Node,LD) of
#node{pid=Pid} ->
@@ -384,7 +384,7 @@ distribute_tf(Faulty,_,_) ->
{error,{badarg,Faulty}}.
%% As above but both specific args for each node and How (set or remove flag).
-distribute_tf(NodeArgHows,LD) when list(NodeArgHows) ->
+distribute_tf(NodeArgHows,LD) when is_list(NodeArgHows) ->
RTpidArgHows=
lists:map(fun({Node,Args,How}) ->
case get_node_rec(Node,LD) of
@@ -405,7 +405,7 @@ distribute_tf(Faulty,_) ->
%% Returns {ok,Reply} or {error,Reason}.
distribute_metapattern(all,Args,LD) ->
distribute_metapattern(started_trace_nodes(all,LD),Args,LD);
-distribute_metapattern(Nodes,Args,LD) when list(Nodes) ->
+distribute_metapattern(Nodes,Args,LD) when is_list(Nodes) ->
RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
#node{pid=Pid} ->
{Pid,N};
@@ -480,7 +480,7 @@ do_cancel_suspension_2(Faulty,_,_) ->
%% Return {ok,Reply} or {error,Reason}.
do_stop_tracing(all,LD) ->
do_stop_tracing(started_trace_nodes(all,LD),LD);
-do_stop_tracing(Nodes,LD) when list(Nodes) ->
+do_stop_tracing(Nodes,LD) when is_list(Nodes) ->
RTpids=lists:map(fun(N)->case get_node_rec(N,LD) of
#node{pid=Pid} ->
{Pid,N};
@@ -580,7 +580,7 @@ do_list_logs_2(Other,_LD,_Replies) ->
%% proper strings.
do_fetch_log(ToNode,all,Dest,Prefix,From,LD) ->
do_fetch_log(ToNode,started_trace_nodes(all,LD),Dest,Prefix,From,LD);
-do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when list(Dest),list(Prefix) ->
+do_fetch_log(ToNode,Specs,Dest,Prefix,From,LD) when is_list(Dest),is_list(Prefix) ->
CollectPid=spawn_link(ToNode,?MODULE,log_rec_init,[self(),Dest,Prefix,From]),
do_fetch_log_2(Specs,LD,CollectPid,[],[]);
do_fetch_log(_ToNode,_Specs,Dest,Prefix,From,_LD) ->
@@ -639,7 +639,7 @@ do_delete_log(NodeSpecs,LD) ->
LD,[]);
false ->
if
- list(NodeSpecs),list(hd(NodeSpecs)) -> % A list of files.
+ is_list(NodeSpecs),is_list(hd(NodeSpecs)) -> % A list of files.
do_delete_log_2(lists:map(fun(N)->{N,NodeSpecs} end,
started_trace_nodes(all,LD)),
LD,[]);
@@ -785,9 +785,9 @@ check_options(Options, Context) ->
check_options_2([],_Context,Result) ->
{ok,Result};
-check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when pid(Pid) ->
+check_options_2([{subscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) ->
check_options_2(OptionsTail,start,[{subscribe,Pid}|Result]);
-check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when pid(Pid) ->
+check_options_2([{unsubscribe,Pid}|OptionsTail],start,Result) when is_pid(Pid) ->
check_options_2(OptionsTail,start,[{unsubscribe,Pid}|Result]);
check_options_2([{dependency,How}|OptionsTail],Context,Result) ->
check_options_2(OptionsTail,Context,[{dependency,How}|Result]);
@@ -819,12 +819,12 @@ initiate_state(Options) ->
LD1#state{distributed=true}
end.
-initiate_state_2([{subscribe,Proc}|Tail],LD) when pid(Proc);atom(Proc)->
+initiate_state_2([{subscribe,Proc}|Tail],LD) when is_pid(Proc);is_atom(Proc)->
MRef=erlang:monitor(process,Proc),
initiate_state_2(Tail,LD#state{subscribers=[{Proc,MRef}|LD#state.subscribers]});
-initiate_state_2([Opt|Tail],LD) when tuple(Opt),size(Opt)>=1 ->
+initiate_state_2([Opt|Tail],LD) when is_tuple(Opt),size(Opt)>=1 ->
initiate_state_2(Tail,initiate_state_3(element(1,Opt),Opt,LD));
-initiate_state_2([Opt|Tail],LD) when atom(Opt) ->
+initiate_state_2([Opt|Tail],LD) when is_atom(Opt) ->
initiate_state_2(Tail,initiate_state_3(Opt,Opt,LD));
initiate_state_2([_|Tail],LD) ->
initiate_state_2(Tail,LD);
@@ -853,9 +853,9 @@ initiate_state_is_rt_option(_) -> false.
%% or more values associated with the Parameter, or just an atom.
merge_options([], Options) ->
Options;
-merge_options([T|DefaultTail],Options) when tuple(T),size(T)>=1 ->
+merge_options([T|DefaultTail],Options) when is_tuple(T),size(T)>=1 ->
merge_options(DefaultTail,merge_options_2(element(1,T),T,Options));
-merge_options([Param|DefaultTail],Options) when atom(Param) ->
+merge_options([Param|DefaultTail],Options) when is_atom(Param) ->
merge_options(DefaultTail,merge_options_2(Param,Param,Options));
merge_options([_|DefaultTail],Options) -> % Strange, bad default option!
merge_options(DefaultTail,Options).
@@ -868,7 +868,7 @@ merge_options_2(Param,Opt,Options) ->
[Opt|Options]
end.
-merge_options_find(Param,[T|_]) when tuple(T),element(1,T)==Param ->
+merge_options_find(Param,[T|_]) when is_tuple(T),element(1,T)==Param ->
true;
merge_options_find(Param,[Param|_]) ->
true;
@@ -883,7 +883,7 @@ merge_options_find(_,[]) ->
%% It also checks the formatting of the tracerdata since runtime components
%% does not accept too badly formatted tracerdata.
%% Returns {ok,NewTraceData} or {error,Reason}.
-check_modify_tracerdata(TracerData,LoopData) when list(TracerData) ->
+check_modify_tracerdata(TracerData,LoopData) when is_list(TracerData) ->
case lists:keysearch(trace,1,TracerData) of
{value,{_,TraceTD}} -> % Examine the trace part.
case check_modify_tracerdata(TraceTD,LoopData) of
@@ -908,7 +908,7 @@ check_modify_tracerdata({relayer,Collector},LoopData) when is_atom(Collector) ->
end;
check_modify_tracerdata({Type,Data},_LoopData) when Type==ip;Type==file ->
{ok,{Type,Data}};
-check_modify_tracerdata({Handler,Data},_LoopData) when function(Handler) ->
+check_modify_tracerdata({Handler,Data},_LoopData) when is_function(Handler) ->
{ok,{Handler,Data}};
check_modify_tracerdata(Data,_LoopData) ->
{error,{bad_tracerdata,Data}}.
@@ -999,7 +999,7 @@ set_node_rec_2(Rec,[NodeRec|Tail]) ->
%% Help function finding a node record for Node in a list of #node or in loopdata.
%% Returns the #node in question or {error,not_an_added_node}.
-get_node_rec(Node,NodeList) when list(NodeList) ->
+get_node_rec(Node,NodeList) when is_list(NodeList) ->
get_node_rec_2(Node,NodeList);
get_node_rec(Node,#state{nodes=NodeList}) ->
get_node_rec_2(Node,NodeList).
@@ -1016,7 +1016,7 @@ get_node_rec_2(Node,[_NodeRec|Tail]) ->
%% structure. Returns a new list of #node or a new loopdata structure.
delete_node_rec(Node,LD=#state{nodes=NodeList}) ->
LD#state{nodes=delete_node_rec_2(Node,NodeList)};
-delete_node_rec(Node,NodeList) when list(NodeList) ->
+delete_node_rec(Node,NodeList) when is_list(NodeList) ->
delete_node_rec_2(Node,NodeList).
delete_node_rec_2(_,[]) ->
@@ -1059,7 +1059,7 @@ log_rec_init(Parent,Dest,Prefix,From={ClientPid,_}) ->
Fetchers),
CMRef=erlang:monitor(process,ClientPid), % Monitor the client.
case log_rec_loop(Dest,Prefix,RTs,InitialReplies,CMRef) of
- Reply when list(Reply) -> % It is an ok value.
+ Reply when is_list(Reply) -> % It is an ok value.
gen_server:reply(From,{ok,Reply});
{error,Reason} ->
gen_server:reply(From,{error,Reason});
diff --git a/lib/inviso/src/inviso_lfm.erl b/lib/inviso/src/inviso_lfm.erl
index 362176c776..085048518c 100644
--- a/lib/inviso/src/inviso_lfm.erl
+++ b/lib/inviso/src/inviso_lfm.erl
@@ -87,13 +87,13 @@
%% close the outfile.
%%
%% Using merge/2 assumes you want to use default handlers writing to a file.
-merge(Files,OutputFile) when list(OutputFile) ->
+merge(Files,OutputFile) when is_list(OutputFile) ->
merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,off).
-merge(Files,WorkHandlerFun,HandlerData) when function(WorkHandlerFun) ->
+merge(Files,WorkHandlerFun,HandlerData) when is_function(WorkHandlerFun) ->
merge(Files,void,WorkHandlerFun,void,HandlerData,off);
-merge(Files,OutputFile,Dbg) when list(OutputFile) ->
+merge(Files,OutputFile,Dbg) when is_list(OutputFile) ->
merge(Files,fun outfile_opener/1,fun outfile_writer/4,fun outfile_closer/1,OutputFile,Dbg).
-merge(Files,WorkHandlerFun,HandlerData,Dbg) when function(WorkHandlerFun) ->
+merge(Files,WorkHandlerFun,HandlerData,Dbg) when is_function(WorkHandlerFun) ->
merge(Files,void,WorkHandlerFun,void,HandlerData,Dbg).
merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData) ->
merge(Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerData,off).
@@ -123,7 +123,7 @@ init_receiver(From,Files,BeginHandlerFun,WorkHandlerFun,EndHandlerFun,HandlerDat
{ok,Readers} ->
process_flag(trap_exit,true),
if
- function(BeginHandlerFun) ->
+ is_function(BeginHandlerFun) ->
case catch BeginHandlerFun(HandlerData) of
{ok,NewHandlerData} ->
init_receiver_2(From,WorkHandlerFun,EndHandlerFun,
@@ -145,7 +145,7 @@ init_receiver_2(From,WorkHandlerFun,EndHandlerFun,HandlerData,Dbg,Readers) ->
{Reply,NewHandlerData}=
loop(From,WorkHandlerFun,HandlerData,NewReaders,EntryStruct,Dbg,0),
if
- function(EndHandlerFun) ->
+ is_function(EndHandlerFun) ->
case EndHandlerFun(NewHandlerData) of
ok ->
From ! {reply,self(),Reply};
@@ -207,7 +207,7 @@ find_oldest_entry(EntryStruct) ->
case list_all_entries(EntryStruct) of
[] -> % The we are done!
done;
- EntryList when list(EntryList) -> % Find smallest timestamp in here then.
+ EntryList when is_list(EntryList) -> % Find smallest timestamp in here then.
{Pid,Node,PidMappings,_TS,Term}=
lists:foldl(fun({P,N,PMap,TS1,T},{_P,_N,_PMap,TS0,_T}) when TS1<TS0 ->
{P,N,PMap,TS1,T};
diff --git a/lib/inviso/src/inviso_lfm_tpfreader.erl b/lib/inviso/src/inviso_lfm_tpfreader.erl
index d0db4b6d02..6de4d11fe0 100644
--- a/lib/inviso/src/inviso_lfm_tpfreader.erl
+++ b/lib/inviso/src/inviso_lfm_tpfreader.erl
@@ -60,9 +60,9 @@
%% File=string()
%% Spawn on this function to start a reader process for trace-port generated
%% logfiles, possibly with inviso-generated ti-files.
-init(RecPid,LogFiles=[Tuple|_]) when tuple(Tuple) -> % Only one LogFiles.
+init(RecPid,LogFiles=[Tuple|_]) when is_tuple(Tuple) -> % Only one LogFiles.
init(RecPid,[LogFiles]);
-init(RecPid,FileStruct) when list(FileStruct) ->
+init(RecPid,FileStruct) when is_list(FileStruct) ->
logfiles_loop(RecPid,FileStruct).
%% -----------------------------------------------------------------------------
@@ -135,7 +135,7 @@ read_traceport_file(FileName,FD) ->
case file:read(FD,5) of % Trace-port file entries start with 5 bytes.
{ok,<<0,Size:32>>} -> % Each entry in a traceport file begins.
case file:read(FD,Size) of
- {ok,Bin} when binary(Bin),size(Bin)=:=Size ->
+ {ok,Bin} when is_binary(Bin),size(Bin)=:=Size ->
try binary_to_term(Bin) of
Term -> % Bin was a properly formatted term!
{ok,Term}
@@ -244,7 +244,7 @@ find_timestamp_in_term(_) -> % Don't know if there is a timestamp
%% (1) plain straight raw binary files.
handle_ti_file(FileStruct) ->
case lists:keysearch(ti_log,1,FileStruct) of
- {value,{_,[FileName]}} when list(FileName) -> % There is one ti-file in this set.
+ {value,{_,[FileName]}} when is_list(FileName) -> % There is one ti-file in this set.
case file:open(FileName,[read,raw,binary]) of
{ok,FD} ->
TIdAlias=ets:new(list_to_atom("inviso_ti_atab_"++pid_to_list(self())),
@@ -308,9 +308,9 @@ handle_ti_file_2(FD,TIdAlias,TIdUnalias) ->
handle_logfiles(FileStruct) ->
handle_logfiles_2(lists:keysearch(trace_log,1,FileStruct)).
-handle_logfiles_2({value,{_,[FileName]}}) when list(FileName)-> % One single plain file.
+handle_logfiles_2({value,{_,[FileName]}}) when is_list(FileName)-> % One single plain file.
[FileName];
-handle_logfiles_2({value,{_,Files}}) when list(Files) -> % A wrap-set.
+handle_logfiles_2({value,{_,Files}}) when is_list(Files) -> % A wrap-set.
handle_logfile_sort_wrapset(Files);
handle_logfiles_2(_) ->
[]. % Pretend there were no files otherwise.
diff --git a/lib/inviso/src/inviso_tool.erl b/lib/inviso/src/inviso_tool.erl
index 7126ba4387..7d3cfb9da0 100644
--- a/lib/inviso/src/inviso_tool.erl
+++ b/lib/inviso/src/inviso_tool.erl
@@ -1,3324 +1,3255 @@
-% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
-%%
-%% Description:
-%% The inviso_tool implementation. A tool that uses inviso.
-%%
-%% Authors:
-%% Lennart �hman, [email protected]
-%% -----------------------------------------------------------------------------
-
--module(inviso_tool).
-
-
-%% This is the inviso tool, which is a tool using the inviso trace application.
-%% It is developed to make tracing using trace cases possible in an environment
-%% of distributed Erlang nodes.
-%% A current restriction is that the Erlang nodes are supposed to have the same
-%% code. This since inviso tool can at this point not handle subsets of nodes.
-%% Instead all participating Erlang nodes are treated the same.
-%%
-%% The main functionality of the inviso tool are:
-%%
-%% (1) Handles start and stop of tracing at participating nodes.
-%% (2) Interprets trace-case files at a distributed network level.
-%% (The inviso runtime component is responsible for interpreting
-%% trace cases at a local level, if run in an autostart).
-%% (3) Keeps a command history log from which:
-%% (a) Sequences easily can be repeated.
-%% (b) Autostart configuration files can be created (understood by the
-%% default inviso autostart mechanism).
-%% (4) Performs reactivation in case tracing is suspended (manually or by
-%% an overload mechanism).
-%% (5) Can reconnect crashed nodes and by using the history bringing them
-%% up to speed.
-
-%% Distributed Erlang
-%% ------------------
-%% Inviso is built to run in a distributed environment.
-%% The inviso tool can also be used in a non distributed environment.
-
-%% Short description
-%% -----------------
-%% Start-up of the inviso tool
-%% During the start-up of the tool, the tool starts runtime components at
-%% all participating nodes. A runtime component can already be running at
-%% a particular node and will then simply be adopted.
-%%
-%% Session
-%% A session is said to start when tracing is initiated, and ends when
-%% made to stop by the user. When a session is stopped, tracing is stopped
-%% at all participating nodes. Note that participating nodes may come and
-%% go though the time-frame of a session. That means that if a node is
-%% reconnected it may resume its tracing in the current session through
-%% a 'restart_session'. A runtime component that is already tracing at the
-%% time start-session will simply be part of the session without its
-%% ingoing tracing being changed.
-%%
-%% Reactivation
-%% A node that is suspended can be reactivated to resume tracing. Note that
-%% tracing has in this situation never been stopped at the node in question.
-%% The inviso tool resumes the node and applies the history to it.
-%%
-%% Reconnect
-%% A node that is "down" from the inviso tool's perspective can be
-%% reconnected. During reconnection the tool restarts the runtime component
-%% at that node but does not (re)initiate tracing. The latter is called
-%% restart_session and must be done explicitly, unless the node in question
-%% is in fact already tracing. If the node is already tracing (due to an autostart
-%% for instance), it automatically becomes part of the ongoing session (if
-%% there is an ongoing session).
-%%
-%% Restart Session
-%% A node that has been down and has been reconnected can be made to
-%% initialize and resume its tracing. This is done by starting the session
-%% at the node in question and redoing the current history.
-
-%% Trace files within a session
-%% Since it is possible to init-tracing (from an inviso perspective) several
-%% times within the same session, a session may leave several trace log files
-%% behind. This must be resolved by the tracer data generator function
-%% (user supplied) by marking filenames in a chronological order but still
-%% making them possible to identify as part of the same session
-
-
-
-%% -----------------------------------------------------------------------------
-%% API exports.
-%% -----------------------------------------------------------------------------
-
--export([start/0,start/1,stop/0,stop/1]).
--export([reconnect_nodes/0,reconnect_nodes/1,
- start_session/0,start_session/1,
- reinitiate_session/0,reinitiate_session/1,
- restore_session/0,restore_session/1,restore_session/2,
- stop_session/0,
- reset_nodes/0,reset_nodes/1,
- atc/3,sync_atc/3,sync_atc/4,
- sync_rtc/2,sync_rtc/3,
- dtc/2,sync_dtc/2,sync_dtc/3,
- inviso/2]).
--export([reactivate/0,reactivate/1,
- save_history/1,
- get_autostart_data/1,get_autostart_data/2,
- get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
--export([flush/0,flush/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Debug exports.
-%% -----------------------------------------------------------------------------
-
--export([get_loopdata/0]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% OTP exports and call backs.
-%% -----------------------------------------------------------------------------
-
--export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal exports.
-%% -----------------------------------------------------------------------------
-
--export([tc_executer/4,reactivator_executer/6]).
--export([std_options_generator/1]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Constants.
-%% -----------------------------------------------------------------------------
-
-%% Defines the inviso function calls that shall be possible to do through the
-%% inviso API in this tool.
--define(INVISO_CMDS,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0},
- {suspend,1},{cancel_suspension,0}]).
-%% -----------------------------------------------------------------------------
-
-%% These inviso functions shall be included in the command history log. Others
-%% are not relevant to be redone during a recactivation, a restart session or
-%% exported to an autostart file.
--define(INVISO_CMD_HISTORY,
- [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
- {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
- {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
- {init_tpm,4},{init_tpm,7},
- {tpm,4},{tpm,5},{tpm,8},
- {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
- {tpm_ms,5},{tpm_ms_tracer,5},
- {ctpm_ms,4},{ctpm,3},
- {tpm_localnames,0},{ctpm_localnames,0},
- {tpm_globalnames,0},{ctpm_globalnames,0},
- {ctp_all,0}]).
-%% -----------------------------------------------------------------------------
-
-%% Since many function calls to inviso may take long time, especially if they
-%% involve difficult and many trace patterns to set, the default gen_server:call
-%% time out can not be used. We just do not want to get stuck for ever if some
-%% error occurs.
--define(CALL_TIMEOUT,60000).
-
-%% Default max time to wait for a trace case called synchronously to return.
--define(SYNC_TC_TIMEOUT,10000).
-
-%% Runtime components shall terminate when the tool terminates.
--define(DEFAULT_DEPENDENCY,{dependency,0}).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Record definitions.
-%% -----------------------------------------------------------------------------
-
-%% The loopdata record.
--record(ld,{
- dir=".", % Working dir of the tool.
- nodes=down, % The nodesD database, defaults to non-distr.
- c_node, % Location of inviso_c.
- c_pid, % The inviso control component.
- regexp_node, % Node for regexp expansions.
- tc_dict, % Trace case definition db.
- chl, % Command history log.
- session_state=passive, % passive | tracing
- tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
- tracer_data, % Current session nr and TDGargs.
- reactivators=[], % Pids of now running reactivators.
- tc_def_file, % Trace case definition file.
- optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
- initial_tcs=[], % Initial trace cases.
- started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
- history_dir, % File path for history file.
- keep_nodes=[], % Nodes that shall not be cleared when stopping.
- debug=false % Internal debug mode
- }).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% API
-%% =============================================================================
-
-%% start()={ok,Pid} | {error,{already_started,pid()}}
-%% start(Config)
-%% Config=[{Opt,Value},...], list of tuple options.
-%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
-%% history_dir|keep_nodes
-%% Starts the inviso_tool process. Options in Config are the same as those
-%% which are kept in the #ld structure.
-start() ->
- start([]).
-start(Config) ->
- gen_server:start({local,?MODULE},?MODULE,Config,[]).
-%% -----------------------------------------------------------------------------
-
-%% stop(UntouchedNodes)=
-%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
-%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason} | patterns_untouched
-%% Stops the inviso tool and the inviso control component. Runtime components are
-%% stopped by them selves depending on their dependcy of the control component.
-%% All runtime components that are not marked as to be kept will have their
-%% trace patterns cleared before the inviso control component is shutdown.
-%% The NodeResults indicates which nodes were successfullt handled.
-stop() ->
- stop([]).
-stop(UntouchedNodes) ->
- gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
-%% reconnect_nodes(Nodes)={ok,NodesResults}
-%% NodesResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{State,Status}} | {error,NReason}
-%% State=tracing | inactive
-%% Status=running | suspended
-%% NReason=unknown_node | already_connected | down
-%% (Re)starts the inviso runtime components at Nodes. Depending on its state
-%% (new,idle or tracing) and if the tool is running a session or not, it becomes
-%% part of the tool's ongoing session. If the newly reconnected node is not
-%% tracing but the tool runs a session, the node must be reinitiated to become
-%% tracing.
-reconnect_nodes() ->
- gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
-reconnect_nodes(Node) when atom(Node) ->
- reconnect_nodes([Node]);
-reconnect_nodes(Nodes) when list(Nodes) ->
- gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% start_session(MoreTDGargs)=
-%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
-%% tracer data generator function.
-%% SessionNr=integer(), trace sessions are numbered by the tool.
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
-%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
-%% Initiates tracing at all participating nodes.
-start_session() ->
- start_session([]).
-start_session(MoreTDGargs) ->
- gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
-%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
-%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
-%% of the tool or not in state inactive will be marked as failing by the
-%% tool in the InvisoReturn.
-%% To reinitate a node means to (inviso) init tracing at it according to saved
-%% tracer data generator arguments for the current session and then redo the current
-%% history to bring it up to speed. Note that the tool must be running a session
-%% for reinitiate to work.
-reinitiate_session() ->
- gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
-reinitiate_session(Nodes) ->
- gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% restore_session()=
-%% restore_session(MoreTDGargs)=
-%% restore_session(FileName)=
-%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
-%% The two first clauses will start a new session using the last history. This
-%% implies that there must have been a session running prior.
-%% The two last clauses starts a session and reads a history file and executes the
-%% tracecases in it at all inactive nodes.
-%% In both cases the reused or read history becomes the current histoy, just if the
-%% session had been initiated manually. The tool may not
-%% have a session ongoing, and nodes already tracing (nodes which were adopted)
-%% are not effected. Just like when starting a session manually.
-restore_session() ->
- restore_session([]).
-restore_session([]) -> % This cant be a filename.
- gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
-restore_session(FileNameOrMoreTDGargs) ->
- case is_string(FileNameOrMoreTDGargs) of
- true -> % Interpret it as a filename.
- restore_session(FileNameOrMoreTDGargs,[]);
- false -> % The we want to use last session history!
- gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
- end.
-restore_session(FileName,MoreTDGargs) ->
- gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
-%% SessionNr=integer()
-%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
-%% NodeResult=ok | {error,Reason}
-%% NonDistributedNodeResult=[ok] | []
-%% Stops inviso tracing at all participating nodes. The inviso runtime components
-%% will go to state idle. It is now time to fetch the logfiles. Will most often
-%% succeed. Will only return an error if the entire inviso call returned an
-%% error. Not if an individual node failed stop tracing successfully.
-%% Any running trace case, including reactivator processes will be terminated.
-stop_session() ->
- gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reset_nodes()=NodeResult | {error,Reason}
-%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
-%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
-%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
-%% must not be having a running session.
-reset_nodes() ->
- gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
-reset_nodes(Nodes) ->
- gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% atc(TC,Id,Vars)=ok | {error,Reason}
-%% TC=atom(), name of the trace case.
-%% Id=term(), given name of this usage of TC.
-%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
-%% Function activating a trace case. The trace case must be defined in the
-%% trace case dictionary. The 'ok' return value is only a signal that the
-%% trace case has started successfully. It may then run for as long as it is
-%% programmed to run. An erroneous return value does not necessarily mean that
-%% the trace case has not been executed. It rather means that is undetermined
-%% what happend.
-atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
-%% sync_atc(TC,Id,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As atc/3 but waits for the trace case to finish.
-sync_atc(TC,Id,Vars) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_atc(TC,Id,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_rtc(TC,Vars)=Result | {error,Reason}
-%% sync_rtc(TC,Vars,TimeOut)=
-%% Result=term(), what ever is returned be the last expression in the trace case.
-%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
-%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
-%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
-sync_rtc(TC,Vars) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_rtc(TC,Vars,TimeOut) ->
- gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% dtc(TC,Id)=ok | {error,Reason}
-%% Deactivates a previosly activated trace case. This function can only be used
-%% on trace cases that has a deactivation defined in the trace case dictionary.
-%% There is of course really no difference between a file containing an activation
-%% compared to a deactivation. But to be able cancelling activations out from the
-%% history log, a defined deactivation is essential.
-%% As with activation, the returned 'ok' simply indicates the start of the trace
-%% case.
-dtc(TC,Id) ->
- gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% sync_dtc(TC,Id)=Result | {error,Reason}
-%% sync_dtc(TC,Id,TimeOut)=
-%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
-%% parameters.
-sync_dtc(TC,Id) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
-sync_dtc(TC,Id,TimeOut) ->
- gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% inviso(Cmd,Args)=Result
-%% Cmd=atom(), the (inviso) function name that shall be called.
-%% Args=list(), the arguments to Cmd.
-%% Result=term(), the result from the inviso function call.
-%% This function executes a Cmd in the inviso tool context. The inviso call will
-%% be logged in history log and thereby repeated in case of a reactivation.
-%% Note that this function is intended for use with inviso function API without
-%% specifying any nodes, since the function call is supposed to be carried out on
-%% all nodes.
-%% When these functions are written to an autostart config file by the tool there
-%% is supposed to be a translation to inviso_rt functions.
-inviso(Cmd,Args) ->
- gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% reactivate()=ok | {error,Reason}
-%% reactivate(Node)=ok | {error,Reason}
-%% Moves a runtime component from suspended to the state running. This can be
-%% done for both tracing and inactive nodes. The later is necessary since you
-%% may have stopped tracing with a node suspended.
-%% In case the node is tracing, commands in the command history log are redone at
-%% the node in questions.
-%% Note that this function returns 'ok' before the node is running. This because the
-%% the reactivated history is done by a separate process and there is no guarantee
-%% when it will be ready. The reactivated node will not be marked as running in
-%% the tool until done reactivating.
-%% Further it is important to understand that if there are "ongoing" tracecases
-%% (i.e tracecase scripts that are currently executing) and this node was running
-%% at the time that tracecase script started to execute, the list of nodes bound
-%% to the Nodes variable in that script executer includes this node. Making it
-%% no longer suspended makes it start executing inviso commands from where ever
-%% such are called. Hence the reactivation may be interferred by that tracecase.
-reactivate() -> % Non-distributed API.
- reactivate(node()).
-reactivate(Node) ->
- gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
-%% Saves the currently collected command history log to a file. The file will
-%% be a binary-file. If FileName is an absolute path, it will be saved to that
-%% file. Otherwise the history dir will be used. If no history dir was specified
-%% the tool dir will be used, prepended to FileName.
-save_history(FileName) ->
- gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
-%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
-%% Dependency=inviso dependency parameter which will be used for every
-%% autostarted runtime component (included in Options).
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
-%% Options=add_nodes options to the inviso runtime component.
-%% M,F=atom(), the module and function for tracerdata generation.
-%% CompleteTDGargs=list(), all arguments as they are given to the tracer
-%% data generator function.
-%% AutostartData=[CaseSpec,...]
-%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
-%% FileName=string(), pointing out the trace case file. Note that this
-%% is the same as the path used by the tool.
-%% Bindings=Var bindings used according to the history for the
-%% invocation.
-%% M,F=atom(), the function that shall be called (normally some inviso).
-%% Args=list(), the actual arguments. Note that this may contain things
-%% which can not be written to file (ports, pids,...).
-%% Function returning information on how to autostart a node to make it trace
-%% according to the current history. The inviso_tool does not know how to write
-%% the necessary files at the nodes in question. That must be done by the user
-%% of the tool, guided by the return value from this function.
-%% Note that there will be two types of trace case files. Regular trace case
-%% files and binaries returned from this function. The latter contains the
-%% inviso commands which have been executed. Note that the order amongst the
-%% trace cases and binaries is of importance (otherwise they will be redone in
-%% an incorrect order).
-get_autostart_data(Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
-get_autostart_data(Nodes,Dependency) ->
- gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_activities()={ok,Ongoing} | {error,Reason}
-%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
-%% TraceCases={tracecases,TraceCaseList}
-%% TraceCaseList=[{{TCname,Id},Phase},...]
-%% Phase=activating | deactivating
-%% Reactivators={reactivating_nodes,ReactivatingNodes}
-%% ReactivatingNodes=[Node,...]
-%% Returns a list of assynchronous tracecases and nodes doing reactivation at
-%% this momement. This can be useful to implement "home brewn" synchronization,
-%% waiting for the runtime components to reach a certain state.
-get_activities() ->
- gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_status(Node)={ok,StateStatus} | {error,Reason}
-%% StateStatus={State,Status} | reactivating | down
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-get_node_status() ->
- get_node_status(local_runtime).
-get_node_status(Node) ->
- gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
-%% Status=tracing | not_tracing, info about current/last session.
-%% SessionNr=integer()
-%% TDGargs=list(), list of the arguments that will be given to the tracer data
-%% generator function (not including the leading Nodes list).
-%% Returns data about the current or last session.
-get_session_data() ->
- gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
-%% flush(Nodes)={ok,NodesResults} | {error,Reason}
-%% NodeResults=[{Node,NodeResult},...]
-%% NodeResult=ok | {error,Reason}
-%% Makes runtime components flush their trace ports.
-flush() ->
- gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
-flush(Nodes) ->
- gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% get_loopdata()=#ld
-%% Debug API returning the internal loopdata structure. See #ld above for details.
-get_loopdata() ->
- gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Internal APIs.
-%% -----------------------------------------------------------------------------
-
-%% tc_executer_reply(To,Reply)=nothing significant
-%% To=pid()
-%% Reply=term()
-%% Internal API used by a trace case executer process to signal its completion.
-tc_executer_reply(To,Reply) ->
- gen_server:cast(To,{tc_executer_reply,Reply}).
-%% -----------------------------------------------------------------------------
-
-%% Internal API used by a reactivator process indicating it is done with the
-%% history log it has got so far.
-%% Timeout set to infinity since the tool may be busy, then the reactivator just
-%% have to wait. If the tool crashes the reactivator will be go down too automatically.
-reactivator_reply(TPid,Counter) ->
- gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% gen_server implementation.
-%% =============================================================================
-
-init(Config) ->
- case fetch_configuration(Config) of % From conf-file and Config.
- {ok,LD} when record(LD,ld) ->
- case start_inviso_at_c_node(LD) of
- {ok,CPid} ->
- LD2=start_runtime_components(LD),
- LD3=read_trace_case_definitions(LD2),
- process_flag(trap_exit,true),
- start_subscribe_inviso_events(LD3#ld.c_node),
- {ok,LD3#ld{c_pid=CPid}};
- {error,Reason} -> % Most likely already running.
- {stop,{error,Reason}}
- end;
- {error,Reason} ->
- {stop,{error,{start_up,Reason}}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the inviso control component at node c_node, or "here"
-%% if it is not a distributed network.
-start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
- case inviso:start() of
- {ok,Pid} ->
- {ok,Pid};
- {error,Reason} ->
- {error,Reason}
- end;
-start_inviso_at_c_node(#ld{c_node=CNode}) ->
- case rpc:call(CNode,inviso,start,[]) of
- {ok,Pid} ->
- {ok,Pid};
- {error,{already_started,_}} -> % A control component already started.
- {error,{inviso_control_already_running,CNode}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the runtime components at all particapting nodes.
-%% It also updates the nodes structure in the #ld to indicate which nodes where
-%% successfully started. Returns a new #ld.
-%% Note that a runtime component may actually be running at one or several nodes.
-%% This is supposed to be the result of an (wanted) autostart. Meaning that the
-%% inviso tool can not handle the situation if a runtime component is not doing
-%% what it is supposed to do. In case a runtime component is already running it
-%% will be adopted and therefore marked as running.
-start_runtime_components(LD=#ld{c_node=undefined}) ->
- start_runtime_components_2(local_runtime,undefined,LD);
-start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
- start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
-start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
- start_runtime_components_2(Nodes,CNode,LD).
-
-start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(local_runtime,OptG),
- case inviso:add_node(mk_rt_tag(),Opts) of
- {ok,NAnsw} -> % Should be more clever really!
- NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
- LD#ld{nodes=NewNodesD};
- {error,_Reason} ->
- LD
- end;
-start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
- Opts=start_runtime_components_mk_opts(Node,OptG),
- case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
- {ok,NodeResults} ->
- NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
- start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
- {error,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD);
- {badrpc,_Reason} ->
- start_runtime_components_2(Rest,CNode,LD)
- end;
-start_runtime_components_2([],_,LD) ->
- LD.
-
-start_runtime_components_mk_opts(Node,{M,F,Args}) ->
- case catch apply(M,F,[Node|Args]) of
- {ok,Opts} when list(Opts) ->
- start_runtime_component_mk_opts_add_dependency(Opts);
- _ ->
- [?DEFAULT_DEPENDENCY]
- end.
-
-%% The options generator is not supposed to generate the dependency. Hence this
-%% function adds and if necessary removes an incorrectly added dependency tag.
-start_runtime_component_mk_opts_add_dependency(Opts) ->
- case lists:keysearch(dependency,1,Opts) of
- {value,_} -> % Not allowed!!!
- [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
- false ->
- [?DEFAULT_DEPENDENCY|Opts]
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function subscribing to inviso events from the inviso controller. This
-%% will make it possible to follow runtime components going down.
-start_subscribe_inviso_events(undefined) ->
- inviso:subscribe();
-start_subscribe_inviso_events(CNode) ->
- rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% gen_server handle call back functions.
-%% -----------------------------------------------------------------------------
-
-handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
- when is_list(UntouchedNodes) ->
- {stop,
- normal,
- remove_all_trace_patterns(CNode,
- UntouchedNodes++KeepNodes,
- get_available_nodes(NodesD)),
- LD};
-handle_call({stop,BadArg},_From,LD) ->
- {reply,{error,{badarg,BadArg}},LD};
-
-handle_call({reconnect_nodes,Nodes},_From,LD) ->
- case h_reconnect_nodes(Nodes,LD) of
- {ok,{Nodes2,NodesErr,NewLD}} ->
- if
- Nodes==local_runtime ->
- {reply,
- build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
- NewLD};
- list(Nodes) ->
- {reply,
- {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
- NewLD}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
-
-handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % No session running.
- if
- list(MoreTDGargs) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
- NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
- Nodes2,
- NewLD#ld{chl=mk_chl(LD#ld.chl)}),
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD2#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true -> % Faulty TDGargs.
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true -> % The tool must be tracing.
- {M,F,_Args}=LD#ld.tdg,
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
- {ok,{NodesErr,ReturnVal,NewLD}} ->
- {reply,
- {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
- NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Must have a running session!
- {reply,{error,no_session},LD}
- end;
-
-handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
- when list(MoreTDGargs) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case catch make_absolute_path(FileName,LD#ld.dir) of
- AbsFileName when list(AbsFileName) ->
- case file:read_file(AbsFileName) of
- {ok,Bin} ->
- if
- list(MoreTDGargs) ->
- case catch replace_history_chl(OldCHL,
- binary_to_term(Bin)) of
- {ok,CHL} -> % The file was well formatted.
- case h_restore_session(MoreTDGargs,
- LD#ld{chl=CHL}) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=
- tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- Error -> % Badly formatted file.
- {reply,
- {error,{bad_file,{AbsFileName,Error}}},
- LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- {error,Reason} ->
- {reply,{error,{read_file,Reason}},LD}
- end;
- Error ->
- {reply,{error,{bad_filename,{FileName,Error}}},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-%% This is doing restore session on the current history.
-handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
- case is_tracing(LD#ld.session_state) of
- false ->
- case history_exists_chl(CHL) of
- true -> % There is a history to redo.
- if
- list(MoreTDGargs) ->
- case h_restore_session(MoreTDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} ->
- {reply,
- {ok,{SessionNr,ReturnVal}},
- NewLD#ld{session_state=tracing_sessionstate()}};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,MoreTDGargs}},LD}
- end;
- false ->
- {reply,{error,no_history},LD}
- end;
- true ->
- {reply,{error,session_already_started},LD}
- end;
-
-%% To stop tracing means stop_tracing through the inviso API. But we must also
-%% remove any help processes executing inviso commands (trace case executers
-%% and reactivators).
-%% Note that to be really sure we should actually wait for EXIT-signals from those
-%% processes before returning a successful returnvalue to the caller. In theory
-%% those processes could issue an inviso call effecting a new trace session started
-%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
-%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
-%% if the node is tracing or not
-handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
- case is_tracing(SState) of
- true ->
- NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
- NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
- case h_stop_session(LD) of
- {ok,{SessionNr,Result}} ->
- NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
- {reply,
- {ok,{SessionNr,Result}},
- LD#ld{session_state=passive_sessionstate(),
- nodes=NewNodesD,
- chl=NewCHL,
- reactivators=NewReAct,
- started_initial_tcs=[]}};
- {error,Reason} -> % Now we're really in deep shit :-)
- {reply,{error,{unrecoverable,Reason}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- false -> % We can not be in a session.
- {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
- true ->
- {reply,{error,session_active},LD}
- end;
-
-%% Calling a trace-case, or "turning it on".
-handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_atc(TC,Id,Vars,LD) of
- {ok,NewLD} -> % Trace case executed.
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- integer(TimeOut);TimeOut==infinity ->
- case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- integer(TimeOut);TimeOut==infinity ->
- case h_sync_rtc(TC,Vars,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false ->
- {reply,{error,no_session},LD}
- end;
-
-
-handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- case h_dtc(TC,Id,LD) of
- {ok,NewLD} ->
- {reply,ok,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of % Check that we are tracing now.
- true ->
- if
- integer(TimeOut);TimeOut==infinity ->
- case h_sync_dtc(TC,Id,TimeOut,LD) of
- {ok,NewLD,Result} ->
- {reply,Result,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,TimeOut}},LD}
- end;
- false -> % Can't activate if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
- case is_tracing(SState) of
- true ->
- if
- list(Args) ->
- case h_inviso(Cmd,Args,LD) of
- {ok,{Reply,NewLD}} ->
- {reply,Reply,NewLD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- true ->
- {reply,{error,{badarg,Args}},LD}
- end;
- false -> % Can't do if not tracing.
- {reply,{error,no_session},LD}
- end;
-
-handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
- case get_state_nodes(Node,NodesD) of
- {trace_failure,_} ->
- {reply,{error,trace_failure},LD};
- {State,suspended} -> % The node is infact suspended.
- case h_reactivate(Node,CNode) of
- ok ->
- case {State,is_tracing(LD#ld.session_state)} of
- {tracing,true} -> % Only then shall we redo cmds.
- {reply,ok,redo_cmd_history(Node,LD)};
- _ -> % All other just no longer suspended.
- {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
- end;
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end;
- reactivating ->
- {reply,{error,reactivating},LD};
- {_,running} ->
- {reply,{error,already_running},LD};
- down ->
- {reply,{error,not_available},LD};
- false ->
- {reply,{error,unknown_node},LD}
- end;
-
-handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
- case lists:keysort(2,get_loglist_chl(CHL)) of
- [] -> % Empty history or no history.
- {reply,{error,no_history},LD};
- Log ->
- case h_save_history(HDir,Dir,FileName,Log) of
- {ok,AbsFileName} ->
- {reply,{ok,AbsFileName},LD};
- {error,Reason} ->
- {reply,{error,Reason},LD}
- end
- end;
-
-
-handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
- case build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict) of
- {ok,ASD} ->
- TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
- {M,F,_}=LD#ld.tdg,
- OptsG=LD#ld.optg, % Addnodes options generator.
- {reply,
- h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
- LD};
- {error,Reason} -> % Bad datatypes in command args.
- {reply,{error,Reason},LD}
- end;
-
-handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
- handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
-handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
- Nodes=get_all_nodenames_nodes(NodesD),
- handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
-
-handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
- TraceCases=get_ongoing_chl(CHL),
- RNodes=get_all_nodes_reactivators(Reactivators),
- ReturnList1=
- if
- TraceCases==[] ->
- [];
- true ->
- [{tracecases,TraceCases}]
- end,
- ReturnList2=
- if
- RNodes==[] ->
- ReturnList1;
- true ->
- [{reactivating_nodes,RNodes}|ReturnList1]
- end,
- {reply,{ok,ReturnList2},LD};
-
-handle_call({get_node_status,Node},_Node,LD) ->
- case get_state_nodes(Node,LD#ld.nodes) of
- false ->
- {reply,{error,unknown_node},LD};
- StateStatus ->
- {reply,{ok,StateStatus},LD}
- end;
-
-handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
- case get_latest_session_nr_tracer_data(TD) of
- undefined ->
- {reply,{error,no_session},LD};
- SessionNr ->
- TDGargs=get_latest_tdgargs_tracer_data(TD),
- case is_tracing(SState) of
- true ->
- {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
- false ->
- {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
- end
- end;
-
-handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- Nodes=get_tracing_nodes(NodesD),
- {reply,h_flush(CNode,Nodes),LD};
-handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
- {reply,h_flush(CNode,Nodes),LD};
-
-handle_call(get_loopdata,_From,LD) ->
- {reply,LD,LD};
-
-%% Internal handle_call callbacks.
-
-handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
- HighestUsedCounter=get_highest_used_counter_chl(CHL),
- if
- HighestUsedCounter>Counter -> % There are now more log entries.
- NewUnsortedLog=get_loglist_chl(CHL),
- {reply,{more,NewUnsortedLog},LD};
- true -> % No Counter is youngest log entry.
- NodesD=LD#ld.nodes,
- Node=get_node_reactivators(RPid,LD#ld.reactivators),
- {reply,
- done,
- LD#ld{nodes=set_running_nodes(Node,NodesD),
- reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Handling a notification from a trace case execution process. Receiving this
-%% indicated that this phase of the trace case is finnished.
-handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
- case Phase of
- activating -> % The trace case is running now.
- {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
- {noreply,NewLD};
- stopping ->
- {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
- {noreply,NewLD};
- _ ->
- {noreply,LD}
- end;
-handle_cast(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% This is the case when a runtime component goes down. We stop all running
-%% reactivators for this node. Note that there can also be tracecases ongoing
-%% where this node is part of the Nodes variable. But there is not much we can
-%% do about that. Other then informing the user that it is unwise to reconnect
-%% this node before those tracecases have stopped being ongoing.
-handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
- {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-%% This is the case when a runtime component gets suspended. Much of the same
-%% problem as described above applies.
-handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
- {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
- reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
-
-handle_info(_,LD) ->
- {noreply,LD}.
-%% -----------------------------------------------------------------------------
-
-%% Called when the tool server stops. First clause, termination is initiated by
-%% our self and therefore controlled another way. In the second case we are
-%% stopping for some external reason, and we must then do more here in terminate/2.
-terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
- stop_inviso_at_c_node(CNode);
-terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
- remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
- stop_inviso_at_c_node(CNode).
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% =============================================================================
-%% Handler first level help functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% reconnect_nodes
-%% -----------------------------------------------------------------------------
-
-%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
-%% the set of nodes handled by the tool. It is not possible to reconnect a node
-%% that is not marked as down. This partly because we otherwise risk losing the
-%% trace_failure state (which can not be rediscovered).
-h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
- _ -> % Allready connected!
- {ok,{[],{error,already_connected},LD}}
- end;
-h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when list(Nodes) ->
- {Nodes2,NodesErr}=
- lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- down -> % Yes this node can be reconnected.
- {[N|Nodes2],NodesErr};
- false -> % Not part of the node-set!
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ -> % Allready connected!
- {Nodes2,[{N,{error,already_connected}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes),
- LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
- {ok,{Nodes2,NodesErr,LD2}};
-h_reconnect_nodes(Nodes,_LD) ->
- {error,{badarg,Nodes}}.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% start_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting the tracing at all nodes. Note that the tracer data
-%% is calculated using a user defined function. This is how for instance the
-%% file names (of the log files) are determined.
-%% Before the nodes are initiated their (possibly remaining) trace patterns are
-%% cleared, both local and global.
-h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case get_inactive_running_nodes(NodesD) of
- [] -> % There are no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,[]);
- Nodes -> % List of nodes or 'local_runtime'.
- case h_start_session_ctp_all(CNode,Nodes) of
- {ok,Errors,[]} -> % Now no nodes to initiate!
- h_start_session_nonodes(TDGargs,LD,Errors);
- {ok,Errors,Nodes2} -> % Now these nodes are fresh.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} -> % Generated our tracerdata.
- case h_start_session_2(CNode,TracerList,Errors) of
- {ok,ReturnValue} -> % Some nodes are initialized now.
- {NewNodesD,Nodes3}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- {ok,{SessionNr,
- ReturnValue,
- Nodes3, % The nodes that shall get initial tracases.
- LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} -> % Faulty tracer data generator func.
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} -> % Error clearing patterns.
- {error,Reason}
- end
- end.
-
-h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
- {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
- if
- CNode==undefined ->
- {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
- true ->
- {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
- end.
-
-%% Help function clearing all trace patterns on all nodes.
-h_start_session_ctp_all(CNode,Nodes) ->
- case remove_all_trace_patterns(CNode,[],Nodes) of
- ok -> % Non-distributed case1.
- {ok,[],local_runtime};
- {error,Reason} -> % Non-distributed case2 and general failure.
- {error,Reason};
- {ok,NodeResults} ->
- h_start_session_ctp_all_2(NodeResults,[],[])
- end.
-
-h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
-h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
- h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
-h_start_session_ctp_all_2([],Errors,Nodes) ->
- {ok,Errors,Nodes}.
-
-%% Help function doing the actual init_tracing.
-h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
- case inviso:init_tracing(TracerData) of
- {ok,LogResult} when list(LogResult) ->
- {ok,{ok,LogResult}};
- {error,already_initated} -> % Perhaps adopted!?
- {ok,{error,already_initiated}}; % Not necessarily wrong.
- {error,Reason} ->
- {error,Reason}
- end;
-h_start_session_2(CNode,TracerList,Errors) ->
- case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
- {ok,NodeResults} ->
- {ok,{ok,Errors++NodeResults}};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function starting all initial trace cases. They are actually handled
-%% the same way as user started trace cases. We actually only start initial
-%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
-%% adopted some nodes some already tracing nodes, and such are supposed to have
-%% the correct patterns and flags set.
-do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
- Id=make_ref(), % Trace case ID.
- case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
- {ok,NewLD} -> % Trace case was successfully started.
- NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
- do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
- {error,_Reason} ->
- do_initial_tcs(Rest,Nodes,LD)
- end;
-do_initial_tcs([_|Rest],Nodes,LD) ->
- do_initial_tcs(Rest,Nodes,LD);
-do_initial_tcs([],_Nodes,LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% This help functio is used instead of do_initial_tcs/3 if there actually are no
-%% nodes to do the trace cases on. The reason we must have this function is that
-%% the tracecases must still be entered into the history with bindings and all.
-%% But we let them be marked as 'running' immediately (no need for the activator
-%% process).
-add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} ->
- Id=make_ref(), % Trace case ID.
- FakeProcH=make_ref(), % Need something to enter as activator.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
- NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
- add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
- started_initial_tcs=NewInitialTcs});
- {error,_Reason} -> % Not much we can do about that.
- add_initial_tcs_to_history(Rest,LD)
- end;
- false ->
- add_initial_tcs_to_history(Rest,LD)
- end;
-add_initial_tcs_to_history([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reinitiate_session
-%% -----------------------------------------------------------------------------
-
-%% Function doing the reinitiation. That means first do init_tracing at the nodes
-%% in question. Then redo the command history to bring them up to speed.
-%% But first the runtime component is cleared of all trace patterns.
-h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
- case h_reinitiate_session_2(Nodes,NodesD,CNode) of
- {ok,{[],NodesErr}} -> % No nodes to reinitiate.
- {ok,{NodesErr,{ok,[]},LD}};
- {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
- case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
- {ok,TracerList} ->
- case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
- {ok,ReturnValue} -> % Ok, now we must redo cmd history.
- {NewNodesD,_Nodes}=
- set_tracing_running_nodes(CNode,ReturnValue,NodesD),
- NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
- {ok,{NodesErr,ReturnValue,NewLD}};
- {error,Reason} ->
- {error,Reason}
- end;
- {error,Reason} ->
- {error,{bad_tdg,Reason}}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Help function finding out which nodes in Nodes actually can be reinitiated.
-%% A node must be up, inactive and not suspended in order for this to work. All the
-%% rest is just a matter of how detailed error return values we want to generate.
-h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
- case get_state_nodes(local_runtime,NodesD) of
- {inactive,running} -> % Only ok case.
- case inviso:ctp_all() of
- ok ->
- {ok,{local_runtime,[]}};
- {error,Reason} -> % This is strange.
- {error,Reason}
- end;
- {_,suspended} ->
- {ok,{[],{error,suspended}}};
- down ->
- {ok,{[],{error,down}}};
- _ ->
- {ok,{[],{error,already_in_session}}}
- end;
-h_reinitiate_session_2(Nodes,NodesD,CNode) when list(Nodes) ->
- {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
- case get_state_nodes(N,NodesD) of
- {inactive,running} -> % Only ok case.
- case rpc:call(CNode,inviso,ctp_all,[[N]]) of
- {ok,[{N,ok}]} ->
- {[N|Nodes2],NodesErr};
- {ok,[{N,{error,Reason}}]} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {error,Reason} ->
- {Nodes2,[{N,{error,Reason}}|NodesErr]};
- {badrpc,Reason} ->
- {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
- end;
- {_,suspended} ->
- {Nodes2,[{N,{error,suspended}}|NodesErr]};
- down ->
- {Nodes2,[{N,{error,down}}|NodesErr]};
- false ->
- {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
- _ ->
- {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
- end
- end,
- {[],[]},
- Nodes)};
-h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
- {error,{badarg7,Nodes}}.
-
-%% Help function redoing the command history log at all nodes that actually
-%% started to trace. Note that we do not modify the return value which will be
-%% given to the caller just because we decide not to redo commands. The user
-%% must conclude him self from the inviso return value that commands were not
-%% redone at a particular node.
-h_reinitiate_session_chl(local_runtime,LD) ->
- h_reinitiate_session_chl([local_runtime],LD);
-h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
- case get_state_nodes(Node,NodesD) of
- {tracing,running} -> % Only case when we shall redo!
- h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
- _ -> % No redo of chl in other cases.
- h_reinitiate_session_chl(Rest,LD)
- end;
-h_reinitiate_session_chl([],LD) ->
- LD.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% restore_session
-%% -----------------------------------------------------------------------------
-
-%% Help function starting a session (init tracing) and redoes the history
-%% found in CHL.
-h_restore_session(MoreTDGargs,LD) ->
- DateTime=calendar:universal_time(),
- {M,F,Args}=LD#ld.tdg,
- TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
- case h_start_session(M,F,TDGargs,LD) of
- {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
- {ok,{SessionNr,ReturnVal,NewLD}};
- {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
- NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
- {ok,{SessionNr,ReturnVal,NewLD2}};
- {error,Reason} -> % Risk of out of control.
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% stop_session
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping tracing at tracing nodes.
-h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
- case h_stop_session_2(CNode,NodesD) of
- {ok,Result} ->
- {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
- {error,Reason} ->
- {error,Reason}
- end.
-
-h_stop_session_2(undefined,NodesD) -> % The non distributed case.
- case get_tracing_nodes(NodesD) of
- {up,{inactive,_}} -> % Already not tracing!
- {ok,[]};
- {up,_} ->
- case inviso:stop_tracing() of
- {ok,_State} ->
- {ok,[ok]};
- {error,no_response} ->
- {ok,[]};
- {error,Reason} ->
- {error,Reason}
- end;
- down ->
- {ok,[]}
- end;
-h_stop_session_2(CNode,NodesD) ->
- Nodes=get_tracing_nodes(NodesD),
- case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
- {ok,NodeResults} ->
- {ok,lists:map(fun({N,{ok,_}})->{N,ok};
- (NodeError)->NodeError
- end,
- NodeResults)};
- {error,Reason} ->
- {error,Reason};
- {badrpc,Reason} ->
- {error,{inviso_control_node_error,Reason}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function removing any trace flags, trace patterns and meta trace patterns
-%% at Nodes. This will cause the nodes to become "fresh".
-h_reset_nodes(local_runtime,_CNode) ->
- inviso:clear([keep_log_files]);
-h_reset_nodes(Nodes,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
- {ok,NodeResults} ->
- {ok,NodeResults};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% atc
-%% -----------------------------------------------------------------------------
-
-%% Function handling ativating a trace case. Trace cases that do not have a
-%% particular on/off handling (but just on in some scense) are handled here too.
-%% The trace case is entered into the Command History Log.
-%% Note that the trace case can not be executed at this node but must be
-%% executed where the inviso control component is.
-%% Further it is possible to either activated the tracecase for all running and
-%% tracing nodes, or just for a specified list of nodes.
-%% TC=tracecase_name(),
-%% Id=term(), identifiying this usage so we can turn it off later.
-%% Vars=list(), list of variable-value bindnings.
-h_atc(TC,Id,Vars,LD) ->
- h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
-
-h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- if
- list(Nodes) -> % Nodes predefined.
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
- true -> % Use all tracing and running nodes.
- Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
- h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-
-h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
- case exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} -> % Trace cases have no return values.
- NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_atc
-%% -----------------------------------------------------------------------------
-
-h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- activating -> % Already started.
- {error,activating};
- stopping -> % Not yet stopped.
- {error,deactivating};
- false ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
- NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end;
- {ok,_Bindings} -> % Already activated and running.
- {error,already_started}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% rtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling running a trace case without marking it as activated. It
-%% is in the history mearly indicated as activated
-h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} -> % Such a trace case exists.
- case check_bindings(Vars,TraceCase) of
- {ok,Bindings} -> % Necessary vars exists in Vars.
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
- {error,Reason} ->
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- {error,Reason} -> % Variable def missing.
- {error,Reason}
- end;
- false ->
- {error,unknown_tracecase}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% dtc
-%% -----------------------------------------------------------------------------
-
-%% Function handling turning a trace case off. The trace case must be registered
-%% as having an off mechanism. If it has an off mechanism and was previously entered
-%% into the Command History Log and is done with its activation phase, it will be
-%% executed and removed from the CHL.
-h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
- {ok,ProcH} ->
- NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
- {ok,LD#ld{chl=NewCHL}};
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% sync_dtc
-%% -----------------------------------------------------------------------------
-
-h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
- case find_id_chl(TC,Id,CHL) of
- {ok,Bindings} -> % Yes, we have turned it on before.
- case get_tracecase_tc_dict(TC,TCdict) of
- {ok,TraceCase} ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} ->
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
- RpcNode=get_rpc_nodename(CNode),
- case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
- {ok,Value} ->
- FakeProcH=make_ref(),
- NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
- NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
- {ok,LD#ld{chl=NewCHL2},Value};
- {error,Reason} -> % Script fault.
- {error,{faulty_tracecase,{TcFName,Reason}}};
- {badrpc,Reason} ->
- {error,{badrpc,Reason}}
- end;
- false ->
- {error,no_deactivation}
- end;
- false -> % Strange, Id ok but no such trace case.
- {error,unknown_tracecase}
- end;
- false -> % Not previously turned on.
- {error,unknown_id};
- activating ->
- {error,activating};
- stopping ->
- {error,already_deactivating}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% inviso
-%% -----------------------------------------------------------------------------
-
-%% Function executing one inviso command. The returnvalue from the inviso
-%% function call will be the return value to the client. The command is
-%% entered into the history command log.
-%% Note that the inviso call may have to be done at another node, dictated
-%% by the c_node field. Further, if the module name is not an atom it is
-%% most likely a regexp, which must be expanded at the regexp_node. Note
-%% this is only relevant for tp and tpl.
-h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
- Arity=length(Args),
- case check_proper_inviso_call(Cmd,Arity) of
- {true,RegExpFlag} -> % Yes it is an inviso call.
- Nodes=get_nodenames_running_nodes(LD#ld.nodes),
- case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
- {ok,Result} ->
- case check_inviso_call_to_history(Cmd,Arity) of
- true -> % This function shall be added to chl.
- {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
- false -> % Do not add it.
- {ok,{Result,LD}}
- end;
- {error,Reason} ->
- {error,Reason}
- end;
- false -> % Not an inviso function.
- {error,invalid_function_name}
- end.
-
-h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
- case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
- {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
- {error,{'EXIT',Reason}};
- Result ->
- {ok,Result}
- end;
-h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
- case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
- {ok,NewArgs} ->
- case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- {error,{badrpc,Reason}} -> % Includes runtime failure.
- {error,{badrpc,Reason}};
- Result ->
- {ok,Result}
- end;
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% reactivate
-%% -----------------------------------------------------------------------------
-
-h_reactivate(_Node,undefined) -> % The non-distributed case.
- case inviso:cancel_suspension() of
- ok ->
- ok;
- {error,Reason} ->
- {error,Reason}
- end;
-h_reactivate(Node,CNode) ->
- case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
- {ok,[{Node,ok}]} ->
- ok;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% save_history
-%% -----------------------------------------------------------------------------
-
-h_save_history(HDir,Dir,FileName,SortedLog) ->
- Dir0=
- if
- list(HDir) -> % There is a history dir specified.
- HDir; % Use it then.
- true ->
- Dir % Else use the tool dir.
- end,
- case catch make_absolute_path(FileName,Dir0) of
- AbsFileName when list(AbsFileName) ->
- Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
- case file:write_file(AbsFileName,term_to_binary(Log2)) of
- ok ->
- {ok,AbsFileName};
- {error,Reason} ->
- {error,{write_file,Reason}}
- end;
- {'EXIT',_Reason} ->
- {error,{bad_filename,FileName}}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% get_autostart_data
-%% -----------------------------------------------------------------------------
-
-%% Help function building the structures used when exporting autostart information
-%% from the tool. Note that we remove the tool-dependency and insert the one
-%% specify in the get_autostart_data call.
-h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
- Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
-
-h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when list(Nodes) ->
- {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
-h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
- {error,{badarg,Nodes}}.
-
-h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
- CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
- Opts0=start_runtime_components_mk_opts(Node,OptsG),
- Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
- [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
- h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
-h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% flush
-%% -----------------------------------------------------------------------------
-
-h_flush(undefined,_Nodes) ->
- inviso:flush();
-h_flush(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% tc_executer_reply
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its activation phase and
-%% shall now be marked in the Command History Log as running.
-h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {activating,{TC,Id}} ->
- case Result of
- {ok,Value} -> % The trace case is successful activated.
- {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
- {error,_} -> % Then pretend it never happend :-)
- {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
- end;
- _ -> % Where did this come from?
- {ok,LD} % Well just ignore it then.
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function handling that a trace case has completed its stopping phase and
-%% shall now be nulled in the Command History Log (meaning that it will not
-%% be repeated in the event of a reactivation).
-h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
- case find_tc_executer_chl(ProcH,CHL) of
- {stopping,{TC,Id}} ->
- case Result of
- {ok,_Result} -> % _Result is returned from the tracecase.
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
- {error,_} -> % This is difficult, is it still active?
- {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
- end;
- _ -> % Strange.
- {ok,LD}
- end.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% Terminate.
-%% -----------------------------------------------------------------------------
-
-%% Help function stopping the inviso control component. Does not return
-%% anything significant.
-stop_inviso_at_c_node(undefined) -> % Non distributed case.
- inviso:stop();
-stop_inviso_at_c_node(CNode) ->
- rpc:call(CNode,inviso,stop,[]).
-%% -----------------------------------------------------------------------------
-
-%% Help function that removes all trace patterns from the nodes that are not
-%% marked as such were patterns shall be left after stopping of inviso.
-%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
-%% 'ok' is returned incase of success, ot 'patterns_untouched'.
-remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
- case KeepNodes of
- undefined -> % No, remove patterns from localruntime.
- inviso:ctp_all();
- _ ->
- patterns_untouched
- end;
-remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
- Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
- case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
- {ok,NodeResults} ->
- F=fun(N) ->
- case lists:member(N,KeepNodes) of
- true ->
- {N,patterns_untouched};
- false ->
- case lists:keysearch(N,1,NodeResults) of
- {value,Result} ->
- Result; % {Node,ok}
- false -> % Extremely strange.
- {N,{error,general_error}}
- end
- end
- end,
- {ok,lists:map(F,Nodes)};
- {error,{badrpc,Reason}} ->
- {error,{inviso_control_node_error,Reason}};
- {error,Reason} ->
- {error,Reason}
- end.
-%% -----------------------------------------------------------------------------
-
-%% =============================================================================
-%% Second level help functions.
-%% =============================================================================
-
-%% Help function building a reply to a reconnection call based on which nodes
-%% where asked to be reconnected and which of those are actually now working.
-%% We actually make an effort to serve the return value in the same order as the
-%% nodes were mentioned in the original call (Nodes).
-build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
- case get_state_nodes(local_runtime,NodesD) of
- down ->
- {error,down};
- {State,Status} ->
- {ok,{State,Status}}
- end;
-build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
- NodesErr;
-build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
- case lists:member(Node,Nodes2) of
- true -> % Ok, look in the #ld.nodes.
- case get_state_nodes(Node,NodesD) of
- down -> % Somekind of failure, still down.
- [{Node,{error,down}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
- {State,Status} -> % {State,Status}
- [{Node,{ok,{State,Status}}}|
- build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
- false -> % Error already from the beginning.
- {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
- [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
- end;
-build_reconnect_nodes_reply([],_,_,_) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function building a return value to reinitiate_session. Nodes contains
-%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
-%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
-build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
- {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
-build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
- NodeResult;
-build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
- NodesErr.
-build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
- case lists:keysearch(Node,1,NodesErr) of
- {value,{_,Error}} ->
- [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
- false ->
- case lists:keysearch(Node,1,NodeResults) of
- {value,Value} ->
- [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
- end
- end;
-build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Help function returning a history log where stop and stopping entries have
-%% been removed. Further all tracecase log entries must be set to running since
-%% there can not be such a thing as an activating tracecase stored away in a
-%% saved historyfile!
-%% We must also take away any #Ref.
-build_saved_history_data(SortedLog) ->
- CleanedLog=
- lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
- (_) -> true
- end,
- SortedLog),
- lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
- ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
- ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
- ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
- end,
- CleanedLog).
-%% -----------------------------------------------------------------------------
-
-%% This help function builds the AutoStartData structure which is returned from
-%% get_austostart_data. An AutoStartData structure is a list of trace-files and
-%% inviso commands. The order is significant since it is the idea that doing
-%% the trace case files and inviso commands in that order will bring a node to
-%% a certain state in a trace perspective.
-%% Returns {ok,AutoStartData} or {error,Reason}
-build_autostart_data(SortedLog,TCdict) ->
- build_autostart_data_2(SortedLog,TCdict,[]).
-
-build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
- build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
-build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
- build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
-build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
- build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
-build_autostart_data_2([],_TCdict,Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function placing the filename in the AutoStartData structure.
-build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
- {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
- {ok,FName}=get_tc_activate_fname(TC),
- build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
-%% -----------------------------------------------------------------------------
-
-%% Help function generating tracerdata to init inviso tracing. The generation
-%% is done by the TracerDataGenerator, TDG, function.
-%% Individual tracerdata is generated for each node in Nodes.
-%% Returns {ok,TracerData} or {error,Reason}.
-call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
- case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerData ->
- {ok,TracerData}
- end;
-call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
- case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
- {'EXIT',Reason} ->
- {error,{'EXIT',Reason}};
- TracerList ->
- {ok,TracerList}
- end.
-
-call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
- [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
- call_tracer_data_generator_2(M,F,TDGargs,Rest)];
-call_tracer_data_generator_2(_,_,_,[]) ->
- [].
-
-call_tracer_data_generator_3(M,F,TDGargs,Node) ->
- apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
-
-%% This function creates the arguments that the tracer data generator function
-%% accepts (in an apply call). The reason for making it a sepparate function is
-%% that the arguments are constructed in more situations than just when actually
-%% doing the apply. By having a function it will become obvious where to change
-%% should the arguments change.
-call_tracer_data_generator_mkargs(Node,TDGargs) ->
- inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
-%% -----------------------------------------------------------------------------
-
-%% This function acts as standard options generator function. That is returning
-%% the options argument to inviso:add_node/3. Note that this function must not
-%% return the dependency part of that option.
-std_options_generator(_Node) ->
- []. % No particular options(!)
-%% -----------------------------------------------------------------------------
-
-
-%% Help function checking that Vars contains a binding for every variable
-%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
-%% is disregarded, since it is always added by the inviso_tool.
-%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
-%% according to file:eval functionality.
-check_bindings(Vars,TraceCase) ->
- case catch check_bindings_2(Vars,
- get_tc_varnames(TraceCase),
- erl_eval:new_bindings()) of
- {'EXIT',_Reason} ->
- {error,variable_error};
- {error,Reason} -> % Missing a bindning.
- {error,Reason};
- {ok,Bindings} ->
- {ok,Bindings}
- end.
-
-check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
- check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
-check_bindings_2(Vars,[VarName|Rest],Bindings) ->
- case lists:keysearch(VarName,1,Vars) of
- {value,{_,Val}} ->
- check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
- false -> % Mandatory variable missing.
- {error,{missing_variable,VarName}} % Quite here then.
- end;
-check_bindings_2(_,[],Bindings) ->
- {ok,Bindings}.
-%% -----------------------------------------------------------------------------
-
-%% This help function checks that the command the user tries to do is amongst
-%% the inviso API. It at the same time returns what kind of command it is.
-%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
-%% needs to have its argument modified by module regexp expansion or not.
-check_proper_inviso_call(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMDS) of
- true -> % It is part of inviso API.
- {true,check_proper_inviso_call_regexp(Cmd,Arity)};
- false ->
- false
- end.
-
-%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
-check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
-check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
-check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
-check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
-check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
-check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
- false.
-%% -----------------------------------------------------------------------------
-
-%% Help function checking if this inviso command shall be added to the command
-%% history log. Returns true or false.
-check_inviso_call_to_history(Cmd,Arity) ->
- case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
- true ->
- true;
- false ->
- false
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function traversing the arguments and expanding module names stated
-%% as regular expressions. This means that the resulting arguments may be longer
-%% than the orginal ones.
-%% When we run this function it has been determined that we are a distributed
-%% system.
-%% Also note that if there are no regexps in Args, no regexpansion will be
-%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
-%% If RegExpNode is unavailable the nodes found in Nodes will be used until
-%% one that works is found.
-expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
- {ok,Args};
-expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
- case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
- NewPatternList when list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
- case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
- NewPatternList when list(NewPatternList) ->
- {ok,[NewPatternList]};
- {error,Reason} ->
- {error,Reason}
- end;
-expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
- expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
-expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
- expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
-
-
-expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when atom(M) ->
- [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when list(M);tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,Modules} ->
- expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
- {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {error,_Reason} ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity,MS,Opts}|
- expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
-expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes).
-
-expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when atom(M) ->
- [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when list(M);tuple(M) ->
- case inviso_tool_lib:expand_module_names([RegExpNode],
- M,
- [{expand_only_at,RegExpNode}]) of
- {singlenode_expansion,badrpc} -> % RegExpNode probably down.
- case Nodes of
- [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
- expand_module_regexps_ctp([{M,F,Arity}|Rest],NewRegExpNode,RestNodes);
- [] -> % No more nodes to choose from.
- throw({error,no_available_regexpnode})
- end;
- {singlenode_expansion,Modules} ->
- expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
- {error,_Reason} ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
- end;
-expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
- expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
-expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
- [].
-
-expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
- [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
-expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
- expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
-%% -----------------------------------------------------------------------------
-
-
-
-%% Help function running the activation of a trace case. Note that this must
-%% be done at the inviso control component's Erlang node *and* that it must be
-%% done in its own process since there is no telling for how long a trace case
-%% may run.
-%% Returns {ok,ActivationHandler}.
-exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
- {ok,TcFName}=get_tc_activate_fname(TraceCase),
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- activating)}.
-
-%% Help function running the deactivation of a trace case.
-exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
- case get_tc_deactivate_fname(TraceCase) of
- {ok,TcFName} -> % There is a deactivation.
- {ok,exec_trace_case_2(CNode,
- TcFName,
- erl_eval:add_binding('Nodes',Nodes,Bindings),
- stopping)};
- false ->
- {error,no_deactivation}
- end.
-
-exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
- if
- CNode==undefined -> % The non distributed case.
- spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
- true ->
- spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
- end.
-
-%% This function is run in its own process and is responsible for executing
-%% the trace case.
-tc_executer(TcFName,Bindings,Phase,Parent) ->
- case catch file:script(TcFName,Bindings) of
- {ok,Value} ->
- tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
- {'EXIT',Reason} ->
- tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
- Error ->
- tc_executer_reply(Parent,{Phase,self(),Error})
- end.
-%% -----------------------------------------------------------------------------
-
-%% Help function which starts a reactivator process redoing command history at
-%% Node. It also updates the loopdata to indicate that Node is now in state
-%% reactivating. It is a good idea to only handle one node per reactivator process.
-%% This because if the node terminates and comes back up, the reactivator must be
-%% stopped.
-redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
- P=start_reactivator(Node,CNode,TCdict,CHL),
- LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
- reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
-
-%% Help function starting a reactivator process replaying the command history log.
-%% Returns a pid of the reactivator process.
-start_reactivator(Node,CNode,TCdict,CHL) ->
- UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
- if
- CNode==undefined -> % The non-distributed case.
- spawn_link(?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]]);
- true ->
- spawn_link(CNode,
- ?MODULE,
- reactivator_executer,
- [Node,TCdict,UnsortedLog,self(),0,[]])
- end.
-
-%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
-%% commands one by one. We wait until one command is finished until we do the
-%% next. Commands marked as nullified are not performed. In fact when a command
-%% is nullified only the stop will be found in the CHL. Its activation will be
-%% removed.
-reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
- SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
- Log=reactivator_skip_log_entries(SortedLog,StartCounter),
- case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
- done ->
- true; % Simply terminate the reactivator then.
- {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
- reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
- end.
-
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
- reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
- reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{M,F,Args,_Ref},NextC}|Rest]) ->
- reactivator_executer_cmd(Node,M,F,Args),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
-reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
- [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
- case lists:member({TCname,Id},DoneCases) of
- true -> % We have activated it, must stop then.
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,_,FNameOff}} ->
- reactivator_executer_tc(Node,Bindings,FNameOff),
- NewDoneCases=lists:delete({TCname,Id},DoneCases),
- reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
- {ok,_} -> % No stop-filename, strange!
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Even stranger, does not exist!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
- false -> % Never activated in the first place.
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end;
-%% Done all log entries found this lap. See if there are more entries by now.
-reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
- case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
- done -> % No more entries in the CHL.
- done;
- {more,NewUnsortedLog} -> % Repeat the procedure
- {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
- end.
-
-%% This help function activates a tracecase.
-reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-%% Help function executing a trace case in the reactivators context. Does not
-%% return anything significant.
-reactivator_executer_tc(Node,Bindings,FileName) ->
- catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
-
-%% Help function handling trace case that are simply executed - rtc.
-reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
- case get_tracecase_tc_dict(TCname,TCdict) of
- {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- {ok,{_,_,_,FNameOn,_}} ->
- reactivator_executer_tc(Node,Bindings,FNameOn),
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
- false -> % Strange, does not exist anylonger!?
- reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
- end.
-
-reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
- catch apply(M,F,Args); % Non-distributed.
-reactivator_executer_cmd(Node,M,F,Args) ->
- catch apply(M,F,[[Node]|Args]).
-
-%% Help function returning a list of log entries missing the first entries
-%% having a counter less or equal to C1.
-reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
- reactivator_skip_log_entries(Rest,C1);
-reactivator_skip_log_entries(Log,_) ->
- Log.
-%% -----------------------------------------------------------------------------
-
-%% Help function returning the node name to use in an rpc call.
-get_rpc_nodename(undefined) ->
- node();
-get_rpc_nodename(CNode) ->
- CNode.
-%% -----------------------------------------------------------------------------
-
-mk_rt_tag() ->
- inviso_tool.
-%% -----------------------------------------------------------------------------
-
-is_string([C|Rest]) when C>=32, C=<255 ->
- is_string(Rest);
-is_string([]) ->
- true;
-is_string(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Functions for handling the configuration file.
-%% -----------------------------------------------------------------------------
-
-%% The inviso tool is configured via start arguments and/or a configuration file.
-%% Start arguments will override any definitions in a configuration file.
-%% The configuration file is pointed out by either a start argument or the
-%% inviso application parameter 'inviso_tool_config_file'.
-
-%% Help function building the internal configuration structure. Configurations
-%% in the start argument will override parameters found in a configuration file.
-fetch_configuration(Config) ->
- case fetch_config_filename(Config) of
- {ok,FName} -> % We are supposed to use a conf-file.
- case read_config_file(FName) of
- {ok,LD} -> % Managed to open a file.
- NewLD=read_config_list(LD,Config),
- {ok,NewLD};
- {error,_Reason} -> % Problem finding/opening file.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end;
- false -> % No filename specified.
- LD=read_config_list(#ld{},Config),
- {ok,LD}
- end.
-
-%% Help function determining the name of the file which shall be consulted as
-%% the main configuration file.
-%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
-fetch_config_filename(Config) ->
- case catch lists:keysearch(config_file,1,Config) of
- {value,{_,FName}} when list(FName) ->
- {ok,FName};
- _ -> % No filename in the start argument.
- fetch_config_filename_2()
- end.
-
-fetch_config_filename_2() ->
- case application:get_env(inviso_tool_config_file) of
- {ok,FName} when list(FName) ->
- {ok,FName};
- _ -> % Application parameter not specified.
- false % Means no config file will be used.
- end.
-
-%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
-read_config_file(FName) ->
- case catch file:consult(FName) of
- {ok,Terms} ->
- {ok,read_config_list(#ld{},Terms)};
- {error,Reason} ->
- {error,{file_consult,Reason}};
- {'EXIT',Reason} ->
- {error,{failure,Reason}}
- end.
-
-%% Help function traversing the Terms list entering known tag-values into #ld.
-read_config_list(LD,Terms) ->
- LD1=read_config_list_2(LD,Terms,nodes),
- LD2=read_config_list_2(LD1,Terms,c_node),
- LD3=read_config_list_2(LD2,Terms,regexp_node),
- LD4=read_config_list_2(LD3,Terms,tc_def_file),
- LD6=read_config_list_2(LD4,Terms,tdg),
- LD8=read_config_list_2(LD6,Terms,debug),
- LD10=read_config_list_2(LD8,Terms,initial_tcs),
- LD11=read_config_list_2(LD10,Terms,dir),
- _LD12=read_config_list_2(LD11,Terms,optg).
-
-read_config_list_2(LD,Terms,Tag) ->
- case catch lists:keysearch(Tag,1,Terms) of
- {value,{_,Value}} ->
- update_ld_record(LD,Tag,Value);
- _ ->
- LD % Tag not found in Terms (or error!)
- end.
-%% -----------------------------------------------------------------------------
-
-%% Function updating a named field in a record. Returns a new record. Note that
-%% this function must be maintained due the fact that field names are removed
-%% at compile time.
-update_ld_record(LD,nodes,Value) when record(LD,ld) ->
- case mk_nodes(Value) of
- {ok,NodesD} ->
- LD#ld{nodes=NodesD};
- error ->
- LD
- end;
-update_ld_record(LD,Tag,Value) when record(LD,ld) ->
- Index=
- case Tag of
- c_node -> % atom()
- #ld.c_node;
- regexp_node -> % atom()
- #ld.regexp_node;
- tc_def_file -> % string()
- #ld.tc_def_file;
- initial_tcs -> % [{TCname,VarList},...]
- #ld.initial_tcs;
- history_dir -> % string()
- #ld.history_dir;
- debug -> % true | false
- #ld.debug;
- dir -> % string()
- #ld.dir;
- optg -> % {Mod,Func,Args}
- #ld.optg;
- tdg -> % {Mod,Func,Args}
- #ld.tdg;
- keep_nodes -> % [Nodes,...]
- #ld.keep_nodes
- end,
- setelement(Index,LD,Value). % Cheeting!
-%% -----------------------------------------------------------------------------
-
-
-%% Help function which, if it exists, consults the trace definition file. The
-%% idea behind the trace definition file is to point out which trace cases there
-%% are, where to find them and how to turn them on and off.
-%% Trace case definitions are:
-%% {TCname,Type,VariableNameList,ActivatioFileName} |
-%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
-%% TCname=atom()
-%% Type=on | on_off
-%% VariableNameList=[atom(),...]
-%% ActivationFileName=DeactivationFileName=string()
-read_trace_case_definitions(LD) ->
- case LD#ld.tc_def_file of
- TCfileName when list(TCfileName) ->
- case catch file:consult(TCfileName) of
- {ok,Terms} ->
- Dir=LD#ld.dir, % The working directory of the tool.
- TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
- LD#ld{tc_dict=TCdict};
- _ ->
- LD
- end;
- _ ->
- LD
- end.
-
-read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
- FileName=make_absolute_path(FName,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on,
- VarNames,
- FileName,
- TCdict));
-read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
- FileNameOn=make_absolute_path(FNameOn,Dir),
- FileNameOff=make_absolute_path(FNameOff,Dir),
- read_trace_case_definitions_2(Rest,
- Dir,
- insert_tracecase_tc_dict(TCname,
- on_off,
- VarNames,
- FileNameOn,
- FileNameOff,
- TCdict));
-read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
- read_trace_case_definitions_2(Rest,Dir,TCdict);
-read_trace_case_definitions_2([],_Dir,TCdict) ->
- TCdict.
-
-%% Help function returning an absolute path to FName if FName is not already
-%% absolute. Dir is the working dir of the tool and supposed to be absolute.
-make_absolute_path(FName,Dir) ->
- case filename:pathtype(FName) of
- absolute -> % Then do nothing, allready absolute.
- FName;
- _ ->
- filename:join(Dir,FName)
- end.
-%% -----------------------------------------------------------------------------
-
-get_status(undefined,_Node) ->
- inviso:get_status();
-get_status(CNode,Nodes) ->
- inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
-%% -----------------------------------------------------------------------------
-
-
-%% =============================================================================
-%% Internal data structure functions.
-%% =============================================================================
-
-%% -----------------------------------------------------------------------------
-%% The nodes database structure.
-%% -----------------------------------------------------------------------------
-
-%% The purpose of the nodes database structure is to keep track of what runtime
-%% nodes we have, and their current status.
-%% Implementation:
-%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
-%% non-distributed case.
-%% AvailableStatus={up,Status1} | down
-%% Status1={State,Status} | reactivating
-%% State=tracing | inactive | trace_failure
-%% Status=running | suspended
-%% reactivating=the node is now being brought up to date.
-%% inactive=not tracing, can be initiated and then reactivated.
-%% The following states can occure.
-%% {inactive,running}
-%% Mainly when we start the tool, before a session has been started.
-%% {tracing,running}
-%% When a trace session is on-going.
-%% {trace_failure,running}
-%% If init_tracing failed for some reason.
-%% {tracing,suspended}
-%% reactivating
-%% The node is tracing (has always been) but was suspended. It is now
-%% no longer suspended and the tool is redong commands.
-%% {inactive,suspended}
-%% We can end up here if a session is stopped with this node suspended.
-
-%% Returns a nodes database structure filled with the nodes Nodes.
-mk_nodes(Nodes) when list(Nodes) ->
- {ok,lists:map(fun(N) when atom(N)->{N,down} end,Nodes)};
-mk_nodes(local_runtime) -> % The non-distributed case.
- down;
-mk_nodes(_Nodes) ->
- error.
-%% -----------------------------------------------------------------------------
-
-%% Updates the nodes database structure for each node that has been added.
-%% This is the case when we start the tool or reactivate a node. Note that a node
-%% may have become adopted instead of started.
-%% Returns a new nodes database structure.
-update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added to the control component.
- case get_status(CNode,[Node]) of % Examine if it is tracing or not.
- {ok,[{Node,NodeResult2}]} ->
- Result=mk_nodes_state_from_status(NodeResult2),
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
- {error,_Reason} -> % Strange, mark it as down now.
- update_added_nodes_2(CNode,Node,down,NodesD,Rest)
- end;
- Result ->
- update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
- end;
-update_added_nodes(_CNode,[],NodesD) ->
- NodesD;
-update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
- case update_added_nodes_3(NodeResult) of
- already_added -> % Already added, most likely autostart.
- mk_nodes_state_from_status(inviso:get_status());
- Result ->
- Result % Simply replace NodesD.
- end.
-
-update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,_} -> % Node already exists, replace!
- update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
- false -> % Strange, unknown node!
- update_added_nodes(CNode,Rest,NodesD)
- end.
-
-update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
- {up,{tracing,running}};
-update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
- {up,{tracing,suspended}};
-update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
- {up,{inactive,suspended}};
-update_added_nodes_3({ok,new}) ->
- {up,{inactive,running}};
-update_added_nodes_3({ok,already_added}) ->
- already_added; % This is an error value!
-update_added_nodes_3({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Function marking all nodes that, according to the returnvalue from init_tracing,
-%% now are successfully initiated as tracing and running. Note that nodes that
-%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
-%% Also note that we assume that the nodes must be running to have made it this far.
-%% A node can of course have become suspended in the process, but that node will
-%% be marked as suspended later when that inviso event message arrives to the tool.
-%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
-%% as a result of the init_tracing call (judged from the LogResults).
-set_tracing_running_nodes(undefined,{ok,LogResults},_AvailableStatus) -> % Non-distr. case.
- case set_tracing_running_nodes_checkresult(LogResults) of
- ok ->
- {{up,{tracing,running}},local_runtime};
- error ->
- {down,[]}
- end;
-set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
- {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
-set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
- {down,[]}; % This is questionable!
-set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
- set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
-
-set_tracing_running_nodes_2(CNode,[{Node,{ok,LogResults}}|Rest],NodesD,Nodes) ->
- case set_tracing_running_nodes_checkresult(LogResults) of
- ok -> % The result is good.
- case lists:keysearch(Node,1,NodesD) of
- {value,_} ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
- false -> % Strange.
- set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
- end;
- error -> % This node is not tracing correctly.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
- case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
- {ok,[{Node,NodeResult}]} ->
- Result=mk_nodes_state_from_status(NodeResult),
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
- {error,_Reason} -> % Strange, mark it as down.
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
- end;
-set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
- NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
- set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
-set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
- {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
-
-%% Help function checking if a returnvalue from inviso:init_tracing really
-%% means that tracing has started as requested.
-set_tracing_running_nodes_checkresult(_LogResults) -> ok. % Should really be better!
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in the NodesD structure and sets it to 'down'.
-%% Returns a new nodes structure.
-set_down_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,down}|Rest];
-set_down_nodes(Node,[NodeStruct|Rest]) ->
- [NodeStruct|set_down_nodes(Node,Rest)];
-set_down_nodes(_,[]) ->
- [];
-set_down_nodes(_,_) -> % Non-distributed case.
- down. % One can argue if this can happend.
-%% -----------------------------------------------------------------------------
-
-%% Function updating Node in NodesD to now be suspended. Note that if the node is
-%% reactivating it must be moved to state tracing because that is what is doing.
-set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
- [{Node,{up,{tracing,suspended}}}|Rest];
-set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
- [{Node,{up,{State,suspended}}}|Rest];
-set_suspended_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_suspended_nodes(Node,Rest)];
-set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
- [];
-set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{tracing,suspended}};
-set_suspended_nodes(_,{up,{State,_}}) ->
- {up,{State,suspended}}.
-%% -----------------------------------------------------------------------------
-
-%% This function is called when reactivation is completed. Hence it moves the
-%% node to no longer suspended. Note this can mean that the node is either
-%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
-set_running_nodes(Node,NodesD) when list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
- false -> % Very strange!
- NodesD
- end;
-set_running_nodes(_,NodesD) -> % The non-distributed case.
- set_running_nodes_2(NodesD).
-
-set_running_nodes_2({up,reactivating}) ->
- {up,{tracing,running}};
-set_running_nodes_2({up,{State,suspended}}) ->
- {up,{State,running}}.
-%% -----------------------------------------------------------------------------
-
-%% Function marking node as now reactivating. That means it is not suspended
-%% any longer (and tracing), but still not part of the set of nodes which shall
-%% get all commands. Returns a new NodesD.
-set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
- [{Node,{up,reactivating}}|Rest];
-set_reactivating_nodes(Node,[NodesData|Rest]) ->
- [NodesData|set_reactivating_nodes(Node,Rest)];
-set_reactivating_nodes(_,[]) ->
- [];
-set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
- {up,reactivating}.
-%% -----------------------------------------------------------------------------
-
-%% Function called when stop-tracing is done. That is all nodes in Nodes shall
-%% be inactive now. Note that an inactive node can still be suspended.
-%% Returns a new NodesD.
-set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
- {up,{inactive,running}};
-set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
- {up,{inactive,Status}};
-set_inactive_nodes(_,down) ->
- down;
-set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,{up,reactivating}}} ->
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
- {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
- set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
- _ -> % This should not happend.
- set_inactive_nodes(Rest,NodesD)
- end;
-set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
- set_inactive_nodes(Rest,NodesD);
-set_inactive_nodes([],NodesD) ->
- NodesD.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all node names. Note that it can only be used in the
-%% distributed case.
-get_all_nodenames_nodes(NodesD) ->
- lists:map(fun({Node,_})->Node end,NodesD).
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are up, tracing and running (not suspended),
-%% or 'void' in the non-distributed case. This is the list of nodes that shall get
-%% inviso commands.
-get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
- [Node|get_nodenames_running_nodes(Rest)];
-get_nodenames_running_nodes([{_Node,_}|Rest]) ->
- get_nodenames_running_nodes(Rest);
-get_nodenames_running_nodes([]) ->
- [];
-get_nodenames_running_nodes(_) ->
- void. % When non distributed, N/A.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that can be made to initiate tracing.
-get_inactive_running_nodes({up,{inactive,running}}) ->
- local_runtime;
-get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
- [];
-get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
- [Node|get_inactive_running_nodes(Rest)];
-get_inactive_running_nodes([{_Node,_}|Rest]) ->
- get_inactive_running_nodes(Rest);
-get_inactive_running_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of nodes that are currently tracing (not necessarily running).
-%% In the non-distributed case the status of the runtime component will be
-%% returned.
-%% Note that nodes showing trace_failure will be included since we like to stop
-%% tracing at those nodes too.
-get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
- [Node|get_tracing_nodes(Rest)];
-get_tracing_nodes([_|Rest]) ->
- get_tracing_nodes(Rest);
-get_tracing_nodes([]) ->
- [];
-get_tracing_nodes(AvailableStatus) ->
- AvailableStatus.
-%% -----------------------------------------------------------------------------
-
-%% Returns a list of all nodes that are currently up.
-get_available_nodes(down) ->
- undefined;
-get_available_nodes([{_Node,down}|Rest]) ->
- get_available_nodes(Rest);
-get_available_nodes([{Node,_}|Rest]) ->
- [Node|get_available_nodes(Rest)];
-get_available_nodes([]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function returning the "state" of Node. Mainly used to check if the node is
-%% suspended or not.
-%% Returns {State,Status} | reactivating | down
-%% where
-get_state_nodes(Node,NodesD) when list(NodesD) ->
- case lists:keysearch(Node,1,NodesD) of
- {value,{_,AvailableStatus}} ->
- get_state_nodes_2(AvailableStatus);
- false ->
- false
- end;
-get_state_nodes(_,NodesD) -> % Non distributed case.
- get_state_nodes_2(NodesD).
-
-get_state_nodes_2({up,{trace_failure,Status}}) ->
- {trace_failure,Status};
-get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
- {State,suspended};
-get_state_nodes_2({up,reactivating}) ->
- reactivating;
-get_state_nodes_2({up,{State,running}}) ->
- {State,running};
-get_state_nodes_2(down) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% Help function in the case we need to consult the state/status of a runtime
-%% component. Returns a nodesD value that can be added to the nodes database.
-mk_nodes_state_from_status({ok,{tracing,running}}) ->
- {up,{tracing,running}};
-mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
- {up,{tracing,suspended}};
-mk_nodes_state_from_status({ok,{_,running}}) ->
- {up,{inactive,running}};
-mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
- {up,{inactive,suspended}};
-mk_nodes_state_from_status({error,_Reason}) ->
- down.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The session_state.
-%% -----------------------------------------------------------------------------
-
-%% The session state reflects if the inviso_tool is tracing or not.
-%% This means that if the tool is tracing a reconnected node can be made to
-%% restart_session.
-
-%% Returns the correct value indicating that we are tracing now.
-tracing_sessionstate() ->
- tracing.
-%% -----------------------------------------------------------------------------
-
-%% Returns true or false depending on if we are tracing now or not.
-is_tracing(tracing) ->
- true;
-is_tracing(_) ->
- false.
-%% -----------------------------------------------------------------------------
-
-%% Returns the correct value indicating that the tool is not tracing.
-passive_sessionstate() ->
- idle.
-%% -----------------------------------------------------------------------------
-
-%% -----------------------------------------------------------------------------
-%% The tracer_data datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tracer_data structure collects the tracer data arguments used to init tracing
-%% by this inviso tool. The args are saved per session. Each session has
-%% a number.
-%% Implementation:
-%% Sessions=[{SessionNr,TDGargs},...]
-%% SessionNr=integer()
-%% TDGargs=list(), args given to the tracer data generator
-%% minus the first argument which is the Node name.
-
-%% Function taking tracerdata args structure inserting yet another session.
-%% Returns {SessionNr,NewTDs}.
-insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
- {SNr+1,[{SNr+1,TDGargs}|TDs]};
-insert_td_tracer_data(TDGargs,undefined) ->
- {1,[{1,TDGargs}]}.
-%% -----------------------------------------------------------------------------
-
-%% Returns the latest session nr.
-get_latest_session_nr_tracer_data(undefined) ->
- undefined;
-get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
- SessionNr.
-%% -----------------------------------------------------------------------------
-
-%% Returns the tracer data arguments used when creating the trace data for the
-%% latest session.
-get_latest_tdgargs_tracer_data(undefined) ->
- undefined;
-get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
- TDGargs.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% The tc_dict or trace case dictionary datastructure.
-%% -----------------------------------------------------------------------------
-
-%% The tc_dict stores information about all available trace cases.
-%% Implementation:
-%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
-%% TCname=atom()
-%% Type=on | on_off
-%% VarNames=[atom(),...]
-%% FNameOn=FNameOff=string()
-
-%% Returns the empty trace case dictionary.
-mk_tc_dict() ->
- [].
-%% -----------------------------------------------------------------------------
-
-%% Function inserting a new trace case into the trace case dictionary.
-insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
- [{TCname,on,VarNames,FNameOn}|TCdict].
-insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
- [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
-%% -----------------------------------------------------------------------------
-
-%% Function finding a trace case definition in the tc_dict structure.
-%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
-get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
- {ok,Tuple};
-get_tracecase_tc_dict(TCname,[_|Rest]) ->
- get_tracecase_tc_dict(TCname,Rest);
-get_tracecase_tc_dict(_,[]) ->
- false;
-get_tracecase_tc_dict(_,_) -> % There are no trace cases!
- false.
-%% -----------------------------------------------------------------------------
-
-%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
-%% function.
-%% Returning {ok,ActivationFileName}.
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
- {ok,FNameOn};
-get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
- {ok,FNameOn}.
-
-get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
- {ok,FNameOff};
-get_tc_deactivate_fname(_) -> % Not a case with off function.
- false.
-
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
- VarNames;
-get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
- VarNames.
-
-%% -----------------------------------------------------------------------------
-
-
-%% The Command History Log (CHL) stores commands to make it possible to
-%% reactivate suspended nodes, reconnect restarted nodes, and to make
-%% autostart files.
-%% Each time tracing is initiated (that is started) the CHL is cleared since
-%% it would not make scense to repeat commands from an earlier tracing at
-%% reactivation for instance.
-
-%% Implementation: {NextCounter,OnGoingList,ETStable}
-%% NextCounter=integer(), next command number - to be able to sort them in order.
-%% OnGoingList=[{ProcH,{TCname,ID}},...]
-%% ID=term(), instance id for this execution of this trace case.
-%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
-%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
-%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
-%% {{TCname,#Ref},Counter,Bindings} % An rtc
-%% {{M,F,Args,#Ref},Counter}
-%% Counter=integer(), the order-counter for this logged entry.
-%% State1=activating | stopping
-%% Where:
-%% activating: the activation file for the tracecase is running.
-%% running : activation is completed.
-%% stopping : set on the previously running ETS entry when deactivation
-%% file is currently executing.
-%% stop : entered with own Counter into the ETS table when
-%% deactivation file is executing. Remains after too.
-%% Result=term(), the result returned from the tr-case or inviso call.
-
-
-%% Returning an initial empty CHL.
-mk_chl(undefined) ->
- {1,[],ets:new(inviso_tool_chl,[set,protected])};
-mk_chl({_,_,TId}) ->
- ets:delete(TId),
- mk_chl(undefined).
-
-%% Help function returning 'true' if there is a current history.
-history_exists_chl(undefined) ->
- false;
-history_exists_chl({_,_,_}) ->
- true.
-
-%% Function looking up the state of this trace case.
-find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
- {ok,Bindings};
- [{_,_,State,_}] -> % activating or stopping.
- State;
- [] ->
- false
- end.
-
-%% Function finding the Trace case associated with a process handle
-%% doing this trace case's activation or stopping.
-find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
- case lists:keysearch(ProcH,1,OnGoingList) of
- {value,{_,{TCname,Id}}} ->
- [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
- {State,{TCname,Id}}; % Should be activating or stopping.
- false ->
- false
- end.
-
-%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
-%% is called for trace cases that do not have on/off functionality.
-set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
- ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
- {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function marking a trace case as now running. That is the activation
-%% phase is completed. It is normaly completed when the process executing
-%% the trace case signals that it is done.
-set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
- [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function marking trace case TCname with identifier Id as now in its stopping
-%% state. Where ProcH is the handler to the process running the stopping
-%% trace case.
-set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
- [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
- ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
- ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
- {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
-
-%% Function removing a TCname-Id from the CHL. This is mostly used
-%% if activating the trace case failed for some reason. We do not then
-%% expect the user to stop the trace case. Hence it must be removed now.
-%% A reactivation process may have noticed the activating-entry and started
-%% to activate it. But since the general state reached after an unsuccessful
-%% activation can not easily be determined, we don't try to do much about it.
-del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter,NewOnGoingList,TId}.
-
-%% Function removing the entry TCname+Id from the CHL. This makes it
-%% possible to activate a tracecase with this id again. The entry was
-%% previously marked as stopping.
-nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
- ets:delete(TId,{TCname,Id}),
- NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
- {NextCounter+1,NewOnGoingList,TId}.
-
-%% Function stopping all processes saved as being now running tc executers.
-%% This is useful as cleanup during stop tracing for instance.
-%% Returns a new CHL which is not in all parts correct. Entries in the
-%% ETS table are for instance not properly state-changed. But the CHL will
-%% from now on only be used to create command files and similar.
-stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
- exit(ProcH,kill),
- stop_all_tc_executer_chl({NextCounter,Rest,TId});
-stop_all_tc_executer_chl({NextCounter,[],TId}) ->
- {NextCounter,[],TId}.
-
-%% Function adding a "plain" inviso call to the CHL.
-add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
- {NextCounter+1,OnGoingList,TId}.
-
-%% Function adding a run trace case entry to the chl.
-add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
- ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
- {NextCounter+1,OnGoingList,TId}.
-%% Returns the highest used counter number in the command history log.
-get_highest_used_counter_chl({NextCounter,_,_}) ->
- NextCounter-1.
-
-%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
-%% assynchronous tracecases.
-get_ongoing_chl(undefined) ->
- [];
-get_ongoing_chl({_,OngoingList,TId}) ->
- get_ongoing_chl_2(OngoingList,TId).
-
-get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
- case ets:lookup(TId,{TCname,Id}) of
- [{_,_C,activating,_B}] ->
- [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
- [{_,_C,stopping,_B}] ->
- [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
- end;
-get_ongoing_chl_2([],_) ->
- [].
-
-%% Function returning a list of log entries. Note that the list is unsorted
-%% in respect to Counter.
-get_loglist_chl({_,_,TId}) ->
- L=ets:tab2list(TId),
- lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
- (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
- (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
- (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
- (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
- end,
- L);
-get_loglist_chl(_) -> % The history is not initiated, ever!
- [].
-
-%% Function returning a list of log entries, but only those which are not
-%% cancelled out by deactivations.
-% get_loglist_active_chl({_,_,TId}) ->
-% L=ets:tab2list(TId),
-% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
-% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
-% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
-% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
-% end,
-% L);
-% get_loglist_chl(_) -> % The history is not initiated, ever!
-% [].
-
-
-%% This helpfunction recreates a history from a saved history list. This function
-%% is supposed to crash if the log is not well formatted. Note that we must restore
-%% the counter in order for the counter to work if new commands are added to the
-%% history.
-replace_history_chl(OldCHL,SortedLog) ->
- {_,Ongoing,TId}=mk_chl(OldCHL),
- {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
- {ok,{Counter+1,Ongoing,NewTId}}.
-
-replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,Id},C,running,B,undefined}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
- ets:insert(TId,{{M,F,Args,make_ref()},C}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
- ets:insert(TId,{{TC,make_ref()},C,B}),
- replace_history_chl_2(TId,Rest,C);
-replace_history_chl_2(TId,[],Counter) ->
- {TId,Counter}.
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Reactivators data structure.
-%% -----------------------------------------------------------------------------
-
-%% Function adding a new node-reactivatorpid pair to the reactivators structure.
-%% In this way we know which reactivators to remove if Node terminates, or when
-%% a node is fully updated when a reactivator is done.
-add_reactivators(Node,Pid,Reactivators) ->
- [{Node,Pid}|Reactivators].
-
-%% Function removing a reactivator entry from the reactivators structure.
-del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
- Rest;
-del_reactivators(RPid,[Element|Rest]) ->
- [Element|del_reactivators(RPid,Rest)];
-del_reactivators(_,[]) -> % This should not happend.
- [].
-
-get_node_reactivators(RPid,Reactivators) ->
- case lists:keysearch(RPid,2,Reactivators) of
- {value,{Node,_}} ->
- Node;
- false -> % This should not happend.
- false
- end.
-
-%% Returns a list of list all nodes that are currently reactivating.
-get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
- [Nodes|get_all_nodes_reactivators(Rest)];
-get_all_nodes_reactivators([]) ->
- [].
-
-%% Function stopping all running reactivator processes. Returns a new empty
-%% reactivators structure. Note that this function does not set the state of
-%% Nodes. It must most often be set to running.
-stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
- exit(Pid,kill),
- stop_all_reactivators(Rest);
-stop_all_reactivators([]) ->
- []. % Returns an empty reactivators.
-
-%% Help function stopping the reactivator (if any) that reactivates Node.
-%% Returns a new list of reactivators structure.
-stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
- exit(Pid,kill),
- Rest;
-stop_node_reactivators(Node,[NodePid|Rest]) ->
- [NodePid|stop_node_reactivators(Node,Rest)];
-stop_node_reactivators(_,[]) ->
- [].
-%% -----------------------------------------------------------------------------
-
-
-%% -----------------------------------------------------------------------------
-%% Started initial trace cases data structure.
-%% -----------------------------------------------------------------------------
-
-%% This datastructure keeps information about ongoing trace cases started
-%% automatically at init_tracing. These must be automatically stopped when calling
-%% stop_tracing.
-
-add_initial_tcs(TCname,Id,StartedInitialTcs) ->
- [{TCname,Id}|StartedInitialTcs].
-%% -----------------------------------------------------------------------------
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Description:
+%% The inviso_tool implementation. A tool that uses inviso.
+%%
+%% Authors:
+%% Lennart Öhman, [email protected]
+%% -----------------------------------------------------------------------------
+
+-module(inviso_tool).
+
+
+%% This is the inviso tool, which is a tool using the inviso trace application.
+%% It is developed to make tracing using trace cases possible in an environment
+%% of distributed Erlang nodes.
+%% A current restriction is that the Erlang nodes are supposed to have the same
+%% code. This since inviso tool can at this point not handle subsets of nodes.
+%% Instead all participating Erlang nodes are treated the same.
+%%
+%% The main functionality of the inviso tool are:
+%%
+%% (1) Handles start and stop of tracing at participating nodes.
+%% (2) Interprets trace-case files at a distributed network level.
+%% (The inviso runtime component is responsible for interpreting
+%% trace cases at a local level, if run in an autostart).
+%% (3) Keeps a command history log from which:
+%% (a) Sequences easily can be repeated.
+%% (b) Autostart configuration files can be created (understood by the
+%% default inviso autostart mechanism).
+%% (4) Performs reactivation in case tracing is suspended (manually or by
+%% an overload mechanism).
+%% (5) Can reconnect crashed nodes and by using the history bringing them
+%% up to speed.
+
+%% Distributed Erlang
+%% ------------------
+%% Inviso is built to run in a distributed environment.
+%% The inviso tool can also be used in a non distributed environment.
+
+%% Short description
+%% -----------------
+%% Start-up of the inviso tool
+%% During the start-up of the tool, the tool starts runtime components at
+%% all participating nodes. A runtime component can already be running at
+%% a particular node and will then simply be adopted.
+%%
+%% Session
+%% A session is said to start when tracing is initiated, and ends when
+%% made to stop by the user. When a session is stopped, tracing is stopped
+%% at all participating nodes. Note that participating nodes may come and
+%% go though the time-frame of a session. That means that if a node is
+%% reconnected it may resume its tracing in the current session through
+%% a 'restart_session'. A runtime component that is already tracing at the
+%% time start-session will simply be part of the session without its
+%% ingoing tracing being changed.
+%%
+%% Reactivation
+%% A node that is suspended can be reactivated to resume tracing. Note that
+%% tracing has in this situation never been stopped at the node in question.
+%% The inviso tool resumes the node and applies the history to it.
+%%
+%% Reconnect
+%% A node that is "down" from the inviso tool's perspective can be
+%% reconnected. During reconnection the tool restarts the runtime component
+%% at that node but does not (re)initiate tracing. The latter is called
+%% restart_session and must be done explicitly, unless the node in question
+%% is in fact already tracing. If the node is already tracing (due to an autostart
+%% for instance), it automatically becomes part of the ongoing session (if
+%% there is an ongoing session).
+%%
+%% Restart Session
+%% A node that has been down and has been reconnected can be made to
+%% initialize and resume its tracing. This is done by starting the session
+%% at the node in question and redoing the current history.
+
+%% Trace files within a session
+%% Since it is possible to init-tracing (from an inviso perspective) several
+%% times within the same session, a session may leave several trace log files
+%% behind. This must be resolved by the tracer data generator function
+%% (user supplied) by marking filenames in a chronological order but still
+%% making them possible to identify as part of the same session
+
+
+
+%% -----------------------------------------------------------------------------
+%% API exports.
+%% -----------------------------------------------------------------------------
+
+-export([start/0,start/1,stop/0,stop/1]).
+-export([reconnect_nodes/0,reconnect_nodes/1,
+ start_session/0,start_session/1,
+ reinitiate_session/0,reinitiate_session/1,
+ restore_session/0,restore_session/1,restore_session/2,
+ stop_session/0,
+ reset_nodes/0,reset_nodes/1,
+ atc/3,sync_atc/3,sync_atc/4,
+ sync_rtc/2,sync_rtc/3,
+ dtc/2,sync_dtc/2,sync_dtc/3,
+ inviso/2]).
+-export([reactivate/0,reactivate/1,
+ save_history/1,
+ get_autostart_data/1,get_autostart_data/2,
+ get_activities/0,get_node_status/0,get_node_status/1,get_session_data/0]).
+-export([flush/0,flush/1]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Debug exports.
+%% -----------------------------------------------------------------------------
+
+-export([get_loopdata/0]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% OTP exports and call backs.
+%% -----------------------------------------------------------------------------
+
+-export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Internal exports.
+%% -----------------------------------------------------------------------------
+
+-export([tc_executer/4,reactivator_executer/6]).
+-export([std_options_generator/1]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Constants.
+%% -----------------------------------------------------------------------------
+
+%% Defines the inviso function calls that shall be possible to do through the
+%% inviso API in this tool.
+-define(INVISO_CMDS,
+ [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
+ {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
+ {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
+ {init_tpm,4},{init_tpm,7},
+ {tpm,4},{tpm,5},{tpm,8},
+ {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
+ {tpm_ms,5},{tpm_ms_tracer,5},
+ {ctpm_ms,4},{ctpm,3},
+ {tpm_localnames,0},{ctpm_localnames,0},
+ {tpm_globalnames,0},{ctpm_globalnames,0},
+ {ctp_all,0},
+ {suspend,1},{cancel_suspension,0}]).
+%% -----------------------------------------------------------------------------
+
+%% These inviso functions shall be included in the command history log. Others
+%% are not relevant to be redone during a recactivation, a restart session or
+%% exported to an autostart file.
+-define(INVISO_CMD_HISTORY,
+ [{tp,5},{tp,4},{tp,1},{tpl,5},{tpl,4},{tpl,1},
+ {ctp,1},{ctp,2},{ctp,3},{ctpl,1},{ctpl,2},{ctpl,3},
+ {tf,2},{tf,1},{ctf,2},{ctf,1},{ctf_all,0},
+ {init_tpm,4},{init_tpm,7},
+ {tpm,4},{tpm,5},{tpm,8},
+ {tpm_tracer,4},{tpm_tracer,5},{init_tpm,8},
+ {tpm_ms,5},{tpm_ms_tracer,5},
+ {ctpm_ms,4},{ctpm,3},
+ {tpm_localnames,0},{ctpm_localnames,0},
+ {tpm_globalnames,0},{ctpm_globalnames,0},
+ {ctp_all,0}]).
+%% -----------------------------------------------------------------------------
+
+%% Since many function calls to inviso may take long time, especially if they
+%% involve difficult and many trace patterns to set, the default gen_server:call
+%% time out can not be used. We just do not want to get stuck for ever if some
+%% error occurs.
+-define(CALL_TIMEOUT,60000).
+
+%% Default max time to wait for a trace case called synchronously to return.
+-define(SYNC_TC_TIMEOUT,10000).
+
+%% Runtime components shall terminate when the tool terminates.
+-define(DEFAULT_DEPENDENCY,{dependency,0}).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Record definitions.
+%% -----------------------------------------------------------------------------
+
+%% The loopdata record.
+-record(ld,{
+ dir=".", % Working dir of the tool.
+ nodes=down, % The nodesD database, defaults to non-distr.
+ c_node, % Location of inviso_c.
+ c_pid, % The inviso control component.
+ regexp_node, % Node for regexp expansions.
+ tc_dict, % Trace case definition db.
+ chl, % Command history log.
+ session_state=passive, % passive | tracing
+ tdg={inviso_tool_lib,std_tdg,[]}, % Tracer data generator func.
+ tracer_data, % Current session nr and TDGargs.
+ reactivators=[], % Pids of now running reactivators.
+ tc_def_file, % Trace case definition file.
+ optg={?MODULE,std_options_generator,[]}, % Generates options to add_nodes/3.
+ initial_tcs=[], % Initial trace cases.
+ started_initial_tcs=[], % Cases that must be stopped when stop_tracing.
+ history_dir, % File path for history file.
+ keep_nodes=[], % Nodes that shall not be cleared when stopping.
+ debug=false % Internal debug mode
+ }).
+%% -----------------------------------------------------------------------------
+
+
+%% =============================================================================
+%% API
+%% =============================================================================
+
+%% start()={ok,Pid} | {error,{already_started,pid()}}
+%% start(Config)
+%% Config=[{Opt,Value},...], list of tuple options.
+%% Opt=dir|nodes|c_node|regexp_node|tdg|tc_def_file|optg|initial_tcs|
+%% history_dir|keep_nodes
+%% Starts the inviso_tool process. Options in Config are the same as those
+%% which are kept in the #ld structure.
+start() ->
+ start([]).
+start(Config) ->
+ gen_server:start({local,?MODULE},?MODULE,Config,[]).
+%% -----------------------------------------------------------------------------
+
+%% stop(UntouchedNodes)=
+%% stop()={ok,NodeResults} | NodeResult | {error,Reason}
+%% UntouchedNodes=list(), nodes where any trace patterns shall not be removed.
+%% NodeResults=[{Node,NodeResult},...]
+%% NodeResult=ok | {error,Reason} | patterns_untouched
+%% Stops the inviso tool and the inviso control component. Runtime components are
+%% stopped by them selves depending on their dependcy of the control component.
+%% All runtime components that are not marked as to be kept will have their
+%% trace patterns cleared before the inviso control component is shutdown.
+%% The NodeResults indicates which nodes were successfullt handled.
+stop() ->
+ stop([]).
+stop(UntouchedNodes) ->
+ gen_server:call(?MODULE,{stop,UntouchedNodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reconnect_nodes()=NodeResult; function for the nod-distributed case.
+%% reconnect_nodes(Nodes)={ok,NodesResults}
+%% NodesResults=[{Node,NodeResult},...]
+%% NodeResult={ok,{State,Status}} | {error,NReason}
+%% State=tracing | inactive
+%% Status=running | suspended
+%% NReason=unknown_node | already_connected | down
+%% (Re)starts the inviso runtime components at Nodes. Depending on its state
+%% (new,idle or tracing) and if the tool is running a session or not, it becomes
+%% part of the tool's ongoing session. If the newly reconnected node is not
+%% tracing but the tool runs a session, the node must be reinitiated to become
+%% tracing.
+reconnect_nodes() ->
+ gen_server:call(?MODULE,{reconnect_nodes,local_runtime},?CALL_TIMEOUT).
+reconnect_nodes(Node) when is_atom(Node) ->
+ reconnect_nodes([Node]);
+reconnect_nodes(Nodes) when is_list(Nodes) ->
+ gen_server:call(?MODULE,{reconnect_nodes,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% start_session()={ok,{SessionNr,InvisoReturn}} | {error,Reason}
+%% start_session(MoreTDGargs)=
+%% MoreTDGargs=list(), prepended to the fixed list of args used when calling the
+%% tracer data generator function.
+%% SessionNr=integer(), trace sessions are numbered by the tool.
+%% InvisoReturn=If successful inviso call, the returnvalue from inviso.
+%% Note that individual nodes may be unsuccessful. See inviso:init_tracing/1
+%% Initiates tracing at all participating nodes.
+start_session() ->
+ start_session([]).
+start_session(MoreTDGargs) ->
+ gen_server:call(?MODULE,{start_session,MoreTDGargs},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reinitiate_session(Nodes)={ok,InvisoReturn} | {error,Reason}
+%% InvisoReturn=If successful inviso call, the returnvalue from inviso:init_tracing/1.
+%% Note that individual nodes may be unsuccessful. Mentioned nodes not part
+%% of the tool or not in state inactive will be marked as failing by the
+%% tool in the InvisoReturn.
+%% To reinitate a node means to (inviso) init tracing at it according to saved
+%% tracer data generator arguments for the current session and then redo the current
+%% history to bring it up to speed. Note that the tool must be running a session
+%% for reinitiate to work.
+reinitiate_session() ->
+ gen_server:call(?MODULE,{reinitiate_session,local_runtime},?CALL_TIMEOUT).
+reinitiate_session(Nodes) ->
+ gen_server:call(?MODULE,{reinitiate_session,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% restore_session()=
+%% restore_session(MoreTDGargs)=
+%% restore_session(FileName)=
+%% restore_session(FileName,MoreTDGargs)={ok,{SessionNr,InvisoReturn}} | {error,Reason}
+%% The two first clauses will start a new session using the last history. This
+%% implies that there must have been a session running prior.
+%% The two last clauses starts a session and reads a history file and executes the
+%% tracecases in it at all inactive nodes.
+%% In both cases the reused or read history becomes the current histoy, just if the
+%% session had been initiated manually. The tool may not
+%% have a session ongoing, and nodes already tracing (nodes which were adopted)
+%% are not effected. Just like when starting a session manually.
+restore_session() ->
+ restore_session([]).
+restore_session([]) -> % This cant be a filename.
+ gen_server:call(?MODULE,{restore_session,[]},?CALL_TIMEOUT);
+restore_session(FileNameOrMoreTDGargs) ->
+ case is_string(FileNameOrMoreTDGargs) of
+ true -> % Interpret it as a filename.
+ restore_session(FileNameOrMoreTDGargs,[]);
+ false -> % The we want to use last session history!
+ gen_server:call(?MODULE,{restore_session,FileNameOrMoreTDGargs},?CALL_TIMEOUT)
+ end.
+restore_session(FileName,MoreTDGargs) ->
+ gen_server:call(?MODULE,{restore_session,{FileName,MoreTDGargs}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% stop_session()={ok,{SessionNr,Result}} | {error,Reason}
+%% SessionNr=integer()
+%% Result=[{Node,NodeResult},...] | NonDistributedNodeResult
+%% NodeResult=ok | {error,Reason}
+%% NonDistributedNodeResult=[ok] | []
+%% Stops inviso tracing at all participating nodes. The inviso runtime components
+%% will go to state idle. It is now time to fetch the logfiles. Will most often
+%% succeed. Will only return an error if the entire inviso call returned an
+%% error. Not if an individual node failed stop tracing successfully.
+%% Any running trace case, including reactivator processes will be terminated.
+stop_session() ->
+ gen_server:call(?MODULE,stop_session,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reset_nodes()=NodeResult | {error,Reason}
+%% reset_nodes(Nodes)={ok,NodeResults} | {error,Reason}
+%% NodeResults and NodeResult as returned by inviso:clear/1 and /0.
+%% Clear nodes from trace flags, trace patterns and meta trace patterns. The tool
+%% must not be having a running session.
+reset_nodes() ->
+ gen_server:call(?MODULE,{reset_nodes,local_runtime},?CALL_TIMEOUT).
+reset_nodes(Nodes) ->
+ gen_server:call(?MODULE,{reset_nodes,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% atc(TC,Id,Vars)=ok | {error,Reason}
+%% TC=atom(), name of the trace case.
+%% Id=term(), given name of this usage of TC.
+%% Vars=list(), list of variable bindings [{Var,Value},...], Var=atom(),Value=term().
+%% Function activating a trace case. The trace case must be defined in the
+%% trace case dictionary. The 'ok' return value is only a signal that the
+%% trace case has started successfully. It may then run for as long as it is
+%% programmed to run. An erroneous return value does not necessarily mean that
+%% the trace case has not been executed. It rather means that is undetermined
+%% what happend.
+atc(TC,Id,Vars) ->
+ gen_server:call(?MODULE,{atc,{TC,Id,Vars}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% sync_atc(TC,Id,Vars)=Result | {error,Reason}
+%% sync_atc(TC,Id,Vars,TimeOut)=
+%% Result=term(), what ever is returned be the last expression in the trace case.
+%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
+%% As atc/3 but waits for the trace case to finish.
+sync_atc(TC,Id,Vars) ->
+ gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
+sync_atc(TC,Id,Vars,TimeOut) ->
+ gen_server:call(?MODULE,{sync_atc,{TC,Id,Vars,TimeOut}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% sync_rtc(TC,Vars)=Result | {error,Reason}
+%% sync_rtc(TC,Vars,TimeOut)=
+%% Result=term(), what ever is returned be the last expression in the trace case.
+%% TimeOut=interger() | infinity, the max wait time for the trace case to finnish.
+%% As sync_atc/3 but the trace case is not marked as activated. It is mearly placed
+%% in the history. Hence with sync_rtc a trace case can be "activated" multiple time.
+sync_rtc(TC,Vars) ->
+ gen_server:call(?MODULE,{sync_rtc,{TC,Vars,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
+sync_rtc(TC,Vars,TimeOut) ->
+ gen_server:call(?MODULE,{sync_rtc,{TC,Vars,TimeOut}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% dtc(TC,Id)=ok | {error,Reason}
+%% Deactivates a previosly activated trace case. This function can only be used
+%% on trace cases that has a deactivation defined in the trace case dictionary.
+%% There is of course really no difference between a file containing an activation
+%% compared to a deactivation. But to be able cancelling activations out from the
+%% history log, a defined deactivation is essential.
+%% As with activation, the returned 'ok' simply indicates the start of the trace
+%% case.
+dtc(TC,Id) ->
+ gen_server:call(?MODULE,{dtc,{TC,Id}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% sync_dtc(TC,Id)=Result | {error,Reason}
+%% sync_dtc(TC,Id,TimeOut)=
+%% Synchronous deactivation of trace case. See dtc/2 and sync_atc/3 for
+%% parameters.
+sync_dtc(TC,Id) ->
+ gen_server:call(?MODULE,{sync_dtc,{TC,Id,?SYNC_TC_TIMEOUT}},?CALL_TIMEOUT).
+sync_dtc(TC,Id,TimeOut) ->
+ gen_server:call(?MODULE,{sync_dtc,{TC,Id,TimeOut}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% inviso(Cmd,Args)=Result
+%% Cmd=atom(), the (inviso) function name that shall be called.
+%% Args=list(), the arguments to Cmd.
+%% Result=term(), the result from the inviso function call.
+%% This function executes a Cmd in the inviso tool context. The inviso call will
+%% be logged in history log and thereby repeated in case of a reactivation.
+%% Note that this function is intended for use with inviso function API without
+%% specifying any nodes, since the function call is supposed to be carried out on
+%% all nodes.
+%% When these functions are written to an autostart config file by the tool there
+%% is supposed to be a translation to inviso_rt functions.
+inviso(Cmd,Args) ->
+ gen_server:call(?MODULE,{inviso,{Cmd,Args}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% reactivate()=ok | {error,Reason}
+%% reactivate(Node)=ok | {error,Reason}
+%% Moves a runtime component from suspended to the state running. This can be
+%% done for both tracing and inactive nodes. The later is necessary since you
+%% may have stopped tracing with a node suspended.
+%% In case the node is tracing, commands in the command history log are redone at
+%% the node in questions.
+%% Note that this function returns 'ok' before the node is running. This because the
+%% the reactivated history is done by a separate process and there is no guarantee
+%% when it will be ready. The reactivated node will not be marked as running in
+%% the tool until done reactivating.
+%% Further it is important to understand that if there are "ongoing" tracecases
+%% (i.e tracecase scripts that are currently executing) and this node was running
+%% at the time that tracecase script started to execute, the list of nodes bound
+%% to the Nodes variable in that script executer includes this node. Making it
+%% no longer suspended makes it start executing inviso commands from where ever
+%% such are called. Hence the reactivation may be interferred by that tracecase.
+reactivate() -> % Non-distributed API.
+ reactivate(node()).
+reactivate(Node) ->
+ gen_server:call(?MODULE,{reactivate,Node},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% save_history(FileName)={ok,AbsFileName} | {error,Reason}
+%% Saves the currently collected command history log to a file. The file will
+%% be a binary-file. If FileName is an absolute path, it will be saved to that
+%% file. Otherwise the history dir will be used. If no history dir was specified
+%% the tool dir will be used, prepended to FileName.
+save_history(FileName) ->
+ gen_server:call(?MODULE,{save_history,FileName},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_autostart_data(Nodes,Dependency)={ok,{AutoStartData,NodeResults} |
+%% {ok,{AutoStartData,NodeResult}} | {error,Reason}
+%% Dependency=inviso dependency parameter which will be used for every
+%% autostarted runtime component (included in Options).
+%% NodeResults=[{Node,NodeResult},...]
+%% NodeResult={ok,{Options,{tdg,{M,F,CompleteTDGargs}}}} | {error,Reason}
+%% Options=add_nodes options to the inviso runtime component.
+%% M,F=atom(), the module and function for tracerdata generation.
+%% CompleteTDGargs=list(), all arguments as they are given to the tracer
+%% data generator function.
+%% AutostartData=[CaseSpec,...]
+%% CaseSpec={file,{FileName,Bindings}} | {mfa,{M,F,Args}}
+%% FileName=string(), pointing out the trace case file. Note that this
+%% is the same as the path used by the tool.
+%% Bindings=Var bindings used according to the history for the
+%% invocation.
+%% M,F=atom(), the function that shall be called (normally some inviso).
+%% Args=list(), the actual arguments. Note that this may contain things
+%% which can not be written to file (ports, pids,...).
+%% Function returning information on how to autostart a node to make it trace
+%% according to the current history. The inviso_tool does not know how to write
+%% the necessary files at the nodes in question. That must be done by the user
+%% of the tool, guided by the return value from this function.
+%% Note that there will be two types of trace case files. Regular trace case
+%% files and binaries returned from this function. The latter contains the
+%% inviso commands which have been executed. Note that the order amongst the
+%% trace cases and binaries is of importance (otherwise they will be redone in
+%% an incorrect order).
+get_autostart_data(Dependency) ->
+ gen_server:call(?MODULE,{get_autostart_data,Dependency},?CALL_TIMEOUT).
+get_autostart_data(Nodes,Dependency) ->
+ gen_server:call(?MODULE,{get_autostart_data,{Nodes,Dependency}},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_activities()={ok,Ongoing} | {error,Reason}
+%% Ongoing=list(); [ [TraceCases] [,Reactivators] ]
+%% TraceCases={tracecases,TraceCaseList}
+%% TraceCaseList=[{{TCname,Id},Phase},...]
+%% Phase=activating | deactivating
+%% Reactivators={reactivating_nodes,ReactivatingNodes}
+%% ReactivatingNodes=[Node,...]
+%% Returns a list of assynchronous tracecases and nodes doing reactivation at
+%% this momement. This can be useful to implement "home brewn" synchronization,
+%% waiting for the runtime components to reach a certain state.
+get_activities() ->
+ gen_server:call(?MODULE,get_activities,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_status(Node)={ok,StateStatus} | {error,Reason}
+%% StateStatus={State,Status} | reactivating | down
+%% State=tracing | inactive | trace_failure
+%% Status=running | suspended
+get_node_status() ->
+ get_node_status(local_runtime).
+get_node_status(Node) ->
+ gen_server:call(?MODULE,{get_node_status,Node},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_session_data()={ok,{Status,SessionNr,TDGargs}} | {error,Reason}
+%% Status=tracing | not_tracing, info about current/last session.
+%% SessionNr=integer()
+%% TDGargs=list(), list of the arguments that will be given to the tracer data
+%% generator function (not including the leading Nodes list).
+%% Returns data about the current or last session.
+get_session_data() ->
+ gen_server:call(?MODULE,get_session_data,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% flush()={ok,NodeResults} | NodeResult | {error,Reason}
+%% flush(Nodes)={ok,NodesResults} | {error,Reason}
+%% NodeResults=[{Node,NodeResult},...]
+%% NodeResult=ok | {error,Reason}
+%% Makes runtime components flush their trace ports.
+flush() ->
+ gen_server:call(?MODULE,flush,?CALL_TIMEOUT).
+flush(Nodes) ->
+ gen_server:call(?MODULE,{flush,Nodes},?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% get_loopdata()=#ld
+%% Debug API returning the internal loopdata structure. See #ld above for details.
+get_loopdata() ->
+ gen_server:call(?MODULE,get_loopdata,?CALL_TIMEOUT).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Internal APIs.
+%% -----------------------------------------------------------------------------
+
+%% tc_executer_reply(To,Reply)=nothing significant
+%% To=pid()
+%% Reply=term()
+%% Internal API used by a trace case executer process to signal its completion.
+tc_executer_reply(To,Reply) ->
+ gen_server:cast(To,{tc_executer_reply,Reply}).
+%% -----------------------------------------------------------------------------
+
+%% Internal API used by a reactivator process indicating it is done with the
+%% history log it has got so far.
+%% Timeout set to infinity since the tool may be busy, then the reactivator just
+%% have to wait. If the tool crashes the reactivator will be go down too automatically.
+reactivator_reply(TPid,Counter) ->
+ gen_server:call(TPid,{reactivator_reply,{Counter,self()}},infinity).
+%% -----------------------------------------------------------------------------
+
+
+%% =============================================================================
+%% gen_server implementation.
+%% =============================================================================
+
+init(Config) ->
+ case fetch_configuration(Config) of % From conf-file and Config.
+ {ok,LD} when is_record(LD,ld) ->
+ case start_inviso_at_c_node(LD) of
+ {ok,CPid} ->
+ LD2=start_runtime_components(LD),
+ LD3=read_trace_case_definitions(LD2),
+ process_flag(trap_exit,true),
+ start_subscribe_inviso_events(LD3#ld.c_node),
+ {ok,LD3#ld{c_pid=CPid}};
+ {error,Reason} -> % Most likely already running.
+ {stop,{error,Reason}}
+ end;
+ {error,Reason} ->
+ {stop,{error,{start_up,Reason}}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function starting the inviso control component at node c_node, or "here"
+%% if it is not a distributed network.
+start_inviso_at_c_node(#ld{c_node=undefined}) -> % Non distributed case.
+ case inviso:start() of
+ {ok,Pid} ->
+ {ok,Pid};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+start_inviso_at_c_node(#ld{c_node=CNode}) ->
+ case rpc:call(CNode,inviso,start,[]) of
+ {ok,Pid} ->
+ {ok,Pid};
+ {error,{already_started,_}} -> % A control component already started.
+ {error,{inviso_control_already_running,CNode}};
+ {error,Reason} ->
+ {error,Reason};
+ {badrpc,Reason} ->
+ {error,{inviso_control_node_error,Reason}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function starting the runtime components at all particapting nodes.
+%% It also updates the nodes structure in the #ld to indicate which nodes where
+%% successfully started. Returns a new #ld.
+%% Note that a runtime component may actually be running at one or several nodes.
+%% This is supposed to be the result of an (wanted) autostart. Meaning that the
+%% inviso tool can not handle the situation if a runtime component is not doing
+%% what it is supposed to do. In case a runtime component is already running it
+%% will be adopted and therefore marked as running.
+start_runtime_components(LD=#ld{c_node=undefined}) ->
+ start_runtime_components_2(local_runtime,undefined,LD);
+start_runtime_components(LD=#ld{c_node=CNode,nodes=NodesD}) ->
+ start_runtime_components_2(get_all_nodenames_nodes(NodesD),CNode,LD).
+start_runtime_components(Nodes,LD=#ld{c_node=CNode}) ->
+ start_runtime_components_2(Nodes,CNode,LD).
+
+start_runtime_components_2(local_runtime,CNode,LD=#ld{optg=OptG}) ->
+ Opts=start_runtime_components_mk_opts(local_runtime,OptG),
+ case inviso:add_node(mk_rt_tag(),Opts) of
+ {ok,NAnsw} -> % Should be more clever really!
+ NewNodesD=update_added_nodes(CNode,{ok,NAnsw},LD#ld.nodes),
+ LD#ld{nodes=NewNodesD};
+ {error,_Reason} ->
+ LD
+ end;
+start_runtime_components_2([Node|Rest],CNode,LD=#ld{optg=OptG}) ->
+ Opts=start_runtime_components_mk_opts(Node,OptG),
+ case rpc:call(CNode,inviso,add_nodes,[[Node],mk_rt_tag(),Opts]) of
+ {ok,NodeResults} ->
+ NewNodesD=update_added_nodes(CNode,NodeResults,LD#ld.nodes),
+ start_runtime_components_2(Rest,CNode,LD#ld{nodes=NewNodesD});
+ {error,_Reason} ->
+ start_runtime_components_2(Rest,CNode,LD);
+ {badrpc,_Reason} ->
+ start_runtime_components_2(Rest,CNode,LD)
+ end;
+start_runtime_components_2([],_,LD) ->
+ LD.
+
+start_runtime_components_mk_opts(Node,{M,F,Args}) ->
+ case catch apply(M,F,[Node|Args]) of
+ {ok,Opts} when is_list(Opts) ->
+ start_runtime_component_mk_opts_add_dependency(Opts);
+ _ ->
+ [?DEFAULT_DEPENDENCY]
+ end.
+
+%% The options generator is not supposed to generate the dependency. Hence this
+%% function adds and if necessary removes an incorrectly added dependency tag.
+start_runtime_component_mk_opts_add_dependency(Opts) ->
+ case lists:keysearch(dependency,1,Opts) of
+ {value,_} -> % Not allowed!!!
+ [?DEFAULT_DEPENDENCY|lists:keydelete(dependecy,1,Opts)];
+ false ->
+ [?DEFAULT_DEPENDENCY|Opts]
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function subscribing to inviso events from the inviso controller. This
+%% will make it possible to follow runtime components going down.
+start_subscribe_inviso_events(undefined) ->
+ inviso:subscribe();
+start_subscribe_inviso_events(CNode) ->
+ rpc:call(CNode,inviso,subscribe,[self()]). % Don't want the rpc-proc to subscribe!
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% gen_server handle call back functions.
+%% -----------------------------------------------------------------------------
+
+handle_call({stop,UntouchedNodes},_From,LD=#ld{nodes=NodesD,c_node=CNode,keep_nodes=KeepNodes})
+ when is_list(UntouchedNodes) ->
+ {stop,
+ normal,
+ remove_all_trace_patterns(CNode,
+ UntouchedNodes++KeepNodes,
+ get_available_nodes(NodesD)),
+ LD};
+handle_call({stop,BadArg},_From,LD) ->
+ {reply,{error,{badarg,BadArg}},LD};
+
+handle_call({reconnect_nodes,Nodes},_From,LD) ->
+ case h_reconnect_nodes(Nodes,LD) of
+ {ok,{Nodes2,NodesErr,NewLD}} ->
+ if
+ Nodes==local_runtime ->
+ {reply,
+ build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes),
+ NewLD};
+ is_list(Nodes) ->
+ {reply,
+ {ok,build_reconnect_nodes_reply(Nodes,Nodes2,NodesErr,NewLD#ld.nodes)},
+ NewLD}
+ end;
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+
+handle_call({start_session,MoreTDGargs},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ false -> % No session running.
+ if
+ is_list(MoreTDGargs) ->
+ DateTime=calendar:universal_time(),
+ {M,F,Args}=LD#ld.tdg,
+ TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
+ case h_start_session(M,F,TDGargs,LD) of
+ {ok,{SessionNr,ReturnVal,NewLD}} -> % No nodes to initiate.
+ NewLD2=add_initial_tcs_to_history(NewLD#ld.initial_tcs,
+ NewLD#ld{chl=mk_chl(LD#ld.chl)}),
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD2#ld{session_state=tracing_sessionstate()}};
+ {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
+ NewLD2=do_initial_tcs(NewLD#ld.initial_tcs,
+ Nodes2,
+ NewLD#ld{chl=mk_chl(LD#ld.chl)}),
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD2#ld{session_state=tracing_sessionstate()}};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true -> % Faulty TDGargs.
+ {reply,{error,{badarg,MoreTDGargs}},LD}
+ end;
+ true ->
+ {reply,{error,session_already_started},LD}
+ end;
+
+handle_call({reinitiate_session,Nodes},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true -> % The tool must be tracing.
+ {M,F,_Args}=LD#ld.tdg,
+ TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
+ case h_reinitiate_session(Nodes,M,F,TDGargs,LD) of
+ {ok,{NodesErr,ReturnVal,NewLD}} ->
+ {reply,
+ {ok,build_reinitiate_session_reply(Nodes,NodesErr,ReturnVal)},
+ NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ false -> % Must have a running session!
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({restore_session,{FileName,MoreTDGargs}},_From,LD=#ld{chl=OldCHL})
+ when is_list(MoreTDGargs) ->
+ case is_tracing(LD#ld.session_state) of
+ false ->
+ case catch make_absolute_path(FileName,LD#ld.dir) of
+ AbsFileName when is_list(AbsFileName) ->
+ case file:read_file(AbsFileName) of
+ {ok,Bin} ->
+ if
+ is_list(MoreTDGargs) ->
+ case catch replace_history_chl(OldCHL,
+ binary_to_term(Bin)) of
+ {ok,CHL} -> % The file was well formatted.
+ case h_restore_session(MoreTDGargs,
+ LD#ld{chl=CHL}) of
+ {ok,{SessionNr,ReturnVal,NewLD}} ->
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD#ld{session_state=
+ tracing_sessionstate()}};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ Error -> % Badly formatted file.
+ {reply,
+ {error,{bad_file,{AbsFileName,Error}}},
+ LD}
+ end;
+ true ->
+ {reply,{error,{badarg,MoreTDGargs}},LD}
+ end;
+ {error,Reason} ->
+ {reply,{error,{read_file,Reason}},LD}
+ end;
+ Error ->
+ {reply,{error,{bad_filename,{FileName,Error}}},LD}
+ end;
+ true ->
+ {reply,{error,session_already_started},LD}
+ end;
+%% This is doing restore session on the current history.
+handle_call({restore_session,MoreTDGargs},_From,LD=#ld{chl=CHL}) ->
+ case is_tracing(LD#ld.session_state) of
+ false ->
+ case history_exists_chl(CHL) of
+ true -> % There is a history to redo.
+ if
+ is_list(MoreTDGargs) ->
+ case h_restore_session(MoreTDGargs,LD) of
+ {ok,{SessionNr,ReturnVal,NewLD}} ->
+ {reply,
+ {ok,{SessionNr,ReturnVal}},
+ NewLD#ld{session_state=tracing_sessionstate()}};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,MoreTDGargs}},LD}
+ end;
+ false ->
+ {reply,{error,no_history},LD}
+ end;
+ true ->
+ {reply,{error,session_already_started},LD}
+ end;
+
+%% To stop tracing means stop_tracing through the inviso API. But we must also
+%% remove any help processes executing inviso commands (trace case executers
+%% and reactivators).
+%% Note that to be really sure we should actually wait for EXIT-signals from those
+%% processes before returning a successful returnvalue to the caller. In theory
+%% those processes could issue an inviso call effecting a new trace session started
+%% with init_tracing shortly after the call to stop_tracing. But too complicated! :-)
+%% Further, stop-tracing is done on all nodes in our nodes structure. Regardless
+%% if the node is tracing or not
+handle_call(stop_session,_From,LD=#ld{session_state=SState,chl=CHL,reactivators=ReAct}) ->
+ case is_tracing(SState) of
+ true ->
+ NewCHL=stop_all_tc_executer_chl(CHL), % Stop any running trace case proc.
+ NewReAct=stop_all_reactivators(ReAct), % Stop any running reactivators.
+ case h_stop_session(LD) of
+ {ok,{SessionNr,Result}} ->
+ NewNodesD=set_inactive_nodes(Result,LD#ld.nodes),
+ {reply,
+ {ok,{SessionNr,Result}},
+ LD#ld{session_state=passive_sessionstate(),
+ nodes=NewNodesD,
+ chl=NewCHL,
+ reactivators=NewReAct,
+ started_initial_tcs=[]}};
+ {error,Reason} -> % Now we're really in deep shit :-)
+ {reply,{error,{unrecoverable,Reason}},LD}
+ end;
+ false ->
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({reset_nodes,Nodes},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ false -> % We can not be in a session.
+ {reply,h_reset_nodes(Nodes,LD#ld.c_node),LD};
+ true ->
+ {reply,{error,session_active},LD}
+ end;
+
+%% Calling a trace-case, or "turning it on".
+handle_call({atc,{TC,Id,Vars}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of % Check that we are tracing now.
+ true ->
+ case h_atc(TC,Id,Vars,LD) of
+ {ok,NewLD} -> % Trace case executed.
+ {reply,ok,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ false -> % Can't activate if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({sync_atc,{TC,Id,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true ->
+ if
+ is_integer(TimeOut);TimeOut==infinity ->
+ case h_sync_atc(TC,Id,Vars,TimeOut,LD) of
+ {ok,NewLD,Result} ->
+ {reply,Result,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,TimeOut}},LD}
+ end;
+ false ->
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({sync_rtc,{TC,Vars,TimeOut}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true ->
+ if
+ is_integer(TimeOut);TimeOut==infinity ->
+ case h_sync_rtc(TC,Vars,TimeOut,LD) of
+ {ok,NewLD,Result} ->
+ {reply,Result,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,TimeOut}},LD}
+ end;
+ false ->
+ {reply,{error,no_session},LD}
+ end;
+
+
+handle_call({dtc,{TC,Id}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of % Check that we are tracing now.
+ true ->
+ case h_dtc(TC,Id,LD) of
+ {ok,NewLD} ->
+ {reply,ok,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ false -> % Can't activate if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({sync_dtc,{TC,Id,TimeOut}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of % Check that we are tracing now.
+ true ->
+ if
+ is_integer(TimeOut);TimeOut==infinity ->
+ case h_sync_dtc(TC,Id,TimeOut,LD) of
+ {ok,NewLD,Result} ->
+ {reply,Result,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,TimeOut}},LD}
+ end;
+ false -> % Can't activate if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({inviso,{Cmd,Args}},_From,LD=#ld{session_state=SState}) ->
+ case is_tracing(SState) of
+ true ->
+ if
+ is_list(Args) ->
+ case h_inviso(Cmd,Args,LD) of
+ {ok,{Reply,NewLD}} ->
+ {reply,Reply,NewLD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ true ->
+ {reply,{error,{badarg,Args}},LD}
+ end;
+ false -> % Can't do if not tracing.
+ {reply,{error,no_session},LD}
+ end;
+
+handle_call({reactivate,Node},_From,LD=#ld{nodes=NodesD,c_node=CNode}) ->
+ case get_state_nodes(Node,NodesD) of
+ {trace_failure,_} ->
+ {reply,{error,trace_failure},LD};
+ {State,suspended} -> % The node is infact suspended.
+ case h_reactivate(Node,CNode) of
+ ok ->
+ case {State,is_tracing(LD#ld.session_state)} of
+ {tracing,true} -> % Only then shall we redo cmds.
+ {reply,ok,redo_cmd_history(Node,LD)};
+ _ -> % All other just no longer suspended.
+ {reply,ok,LD#ld{nodes=set_running_nodes(Node,NodesD)}}
+ end;
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end;
+ reactivating ->
+ {reply,{error,reactivating},LD};
+ {_,running} ->
+ {reply,{error,already_running},LD};
+ down ->
+ {reply,{error,not_available},LD};
+ false ->
+ {reply,{error,unknown_node},LD}
+ end;
+
+handle_call({save_history,FileName},_From,LD=#ld{chl=CHL,dir=Dir,history_dir=HDir}) ->
+ case lists:keysort(2,get_loglist_chl(CHL)) of
+ [] -> % Empty history or no history.
+ {reply,{error,no_history},LD};
+ Log ->
+ case h_save_history(HDir,Dir,FileName,Log) of
+ {ok,AbsFileName} ->
+ {reply,{ok,AbsFileName},LD};
+ {error,Reason} ->
+ {reply,{error,Reason},LD}
+ end
+ end;
+
+handle_call({get_autostart_data,{Nodes,Dependency}},_From,LD=#ld{chl=CHL}) ->
+ {ok,ASD} = build_autostart_data(lists:keysort(2,get_loglist_chl(CHL)),LD#ld.tc_dict),
+ TDGargs=get_latest_tdgargs_tracer_data(LD#ld.tracer_data),
+ {M,F,_}=LD#ld.tdg,
+ OptsG=LD#ld.optg, % Addnodes options generator.
+ {reply,
+ h_get_autostart_data(Nodes,LD#ld.c_node,Dependency,ASD,M,F,TDGargs,OptsG),
+ LD};
+
+handle_call({get_autostart_data,Dependency},From,LD=#ld{c_node=undefined}) ->
+ handle_call({get_autostart_data,{local_runtime,Dependency}},From,LD);
+handle_call({get_autostart_data,Dependency},From,LD=#ld{nodes=NodesD}) ->
+ Nodes=get_all_nodenames_nodes(NodesD),
+ handle_call({get_autostart_data,{local_runtime,{Nodes,Dependency}}},From,LD);
+
+handle_call(get_activities,_From,LD=#ld{chl=CHL,reactivators=Reactivators}) ->
+ TraceCases=get_ongoing_chl(CHL),
+ RNodes=get_all_nodes_reactivators(Reactivators),
+ ReturnList1=
+ if
+ TraceCases==[] ->
+ [];
+ true ->
+ [{tracecases,TraceCases}]
+ end,
+ ReturnList2=
+ if
+ RNodes==[] ->
+ ReturnList1;
+ true ->
+ [{reactivating_nodes,RNodes}|ReturnList1]
+ end,
+ {reply,{ok,ReturnList2},LD};
+
+handle_call({get_node_status,Node},_Node,LD) ->
+ case get_state_nodes(Node,LD#ld.nodes) of
+ false ->
+ {reply,{error,unknown_node},LD};
+ StateStatus ->
+ {reply,{ok,StateStatus},LD}
+ end;
+
+handle_call(get_session_data,_From,LD=#ld{session_state=SState,tracer_data=TD}) ->
+ case get_latest_session_nr_tracer_data(TD) of
+ undefined ->
+ {reply,{error,no_session},LD};
+ SessionNr ->
+ TDGargs=get_latest_tdgargs_tracer_data(TD),
+ case is_tracing(SState) of
+ true ->
+ {reply,{ok,{tracing,SessionNr,TDGargs}},LD};
+ false ->
+ {reply,{ok,{not_tracing,SessionNr,TDGargs}},LD}
+ end
+ end;
+
+handle_call(flush,_From,LD=#ld{c_node=CNode,nodes=NodesD}) ->
+ Nodes=get_tracing_nodes(NodesD),
+ {reply,h_flush(CNode,Nodes),LD};
+handle_call({flush,Nodes},_From,LD=#ld{c_node=CNode}) ->
+ {reply,h_flush(CNode,Nodes),LD};
+
+handle_call(get_loopdata,_From,LD) ->
+ {reply,LD,LD};
+
+%% Internal handle_call callbacks.
+
+handle_call({reactivator_reply,{Counter,RPid}},_From,LD=#ld{chl=CHL}) ->
+ HighestUsedCounter=get_highest_used_counter_chl(CHL),
+ if
+ HighestUsedCounter>Counter -> % There are now more log entries.
+ NewUnsortedLog=get_loglist_chl(CHL),
+ {reply,{more,NewUnsortedLog},LD};
+ true -> % No Counter is youngest log entry.
+ NodesD=LD#ld.nodes,
+ Node=get_node_reactivators(RPid,LD#ld.reactivators),
+ {reply,
+ done,
+ LD#ld{nodes=set_running_nodes(Node,NodesD),
+ reactivators=del_reactivators(RPid,LD#ld.reactivators)}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Handling a notification from a trace case execution process. Receiving this
+%% indicated that this phase of the trace case is finnished.
+handle_cast({tc_executer_reply,{Phase,ProcH,Result}},LD) ->
+ case Phase of
+ activating -> % The trace case is running now.
+ {ok,NewLD}=h_tc_activation_done(ProcH,Result,LD),
+ {noreply,NewLD};
+ stopping ->
+ {ok,NewLD}=h_tc_stopping_done(ProcH,Result,LD),
+ {noreply,NewLD};
+ _ ->
+ {noreply,LD}
+ end;
+handle_cast(_,LD) ->
+ {noreply,LD}.
+%% -----------------------------------------------------------------------------
+
+%% This is the case when a runtime component goes down. We stop all running
+%% reactivators for this node. Note that there can also be tracecases ongoing
+%% where this node is part of the Nodes variable. But there is not much we can
+%% do about that. Other then informing the user that it is unwise to reconnect
+%% this node before those tracecases have stopped being ongoing.
+handle_info({inviso_event,_CNode,_Time,{disconnected,Node,_}},LD) ->
+ {noreply,LD#ld{nodes=set_down_nodes(Node,LD#ld.nodes),
+ reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
+
+%% This is the case when a runtime component gets suspended. Much of the same
+%% problem as described above applies.
+handle_info({inviso_event,_CNode,_Time,{state_change,Node,{_,{suspended,_}}}},LD) ->
+ {noreply,LD#ld{nodes=set_suspended_nodes(Node,LD#ld.nodes),
+ reactivators=stop_node_reactivators(Node,LD#ld.reactivators)}};
+
+handle_info(_,LD) ->
+ {noreply,LD}.
+%% -----------------------------------------------------------------------------
+
+%% Called when the tool server stops. First clause, termination is initiated by
+%% our self and therefore controlled another way. In the second case we are
+%% stopping for some external reason, and we must then do more here in terminate/2.
+terminate(normal,#ld{c_node=CNode}) -> % This is when we are stopping our self.
+ stop_inviso_at_c_node(CNode);
+terminate(_,#ld{c_node=CNode,nodes=NodesD,keep_nodes=KeepNodes}) ->
+ remove_all_trace_patterns(CNode,KeepNodes,get_all_nodenames_nodes(NodesD)),
+ stop_inviso_at_c_node(CNode).
+%% -----------------------------------------------------------------------------
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% =============================================================================
+%% Handler first level help functions.
+%% =============================================================================
+
+%% -----------------------------------------------------------------------------
+%% reconnect_nodes
+%% -----------------------------------------------------------------------------
+
+%% Help function reconnecting the nodes in Nodes. Listed nodes must be part of
+%% the set of nodes handled by the tool. It is not possible to reconnect a node
+%% that is not marked as down. This partly because we otherwise risk losing the
+%% trace_failure state (which can not be rediscovered).
+h_reconnect_nodes(local_runtime,LD=#ld{nodes=NodesD}) -> % Non-distributed.
+ case get_state_nodes(local_runtime,NodesD) of
+ down ->
+ {ok,{local_runtime,[],start_runtime_components(local_runtime,LD)}};
+ _ -> % Allready connected!
+ {ok,{[],{error,already_connected},LD}}
+ end;
+h_reconnect_nodes(Nodes,LD=#ld{nodes=NodesD}) when is_list(Nodes) ->
+ {Nodes2,NodesErr}=
+ lists:foldl(fun(N,{Nodes2,NodesErr})->
+ case get_state_nodes(N,NodesD) of
+ down -> % Yes this node can be reconnected.
+ {[N|Nodes2],NodesErr};
+ false -> % Not part of the node-set!
+ {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
+ _ -> % Allready connected!
+ {Nodes2,[{N,{error,already_connected}}|NodesErr]}
+ end
+ end,
+ {[],[]},
+ Nodes),
+ LD2=start_runtime_components(Nodes2,LD), % Inpect the #ld.nodes for result.
+ {ok,{Nodes2,NodesErr,LD2}};
+h_reconnect_nodes(Nodes,_LD) ->
+ {error,{badarg,Nodes}}.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% start_session
+%% -----------------------------------------------------------------------------
+
+%% Help function starting the tracing at all nodes. Note that the tracer data
+%% is calculated using a user defined function. This is how for instance the
+%% file names (of the log files) are determined.
+%% Before the nodes are initiated their (possibly remaining) trace patterns are
+%% cleared, both local and global.
+h_start_session(M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
+ case get_inactive_running_nodes(NodesD) of
+ [] -> % There are no nodes to initiate!
+ h_start_session_nonodes(TDGargs,LD,[]);
+ Nodes -> % List of nodes or 'local_runtime'.
+ case h_start_session_ctp_all(CNode,Nodes) of
+ {ok,Errors,[]} -> % Now no nodes to initiate!
+ h_start_session_nonodes(TDGargs,LD,Errors);
+ {ok,Errors,Nodes2} -> % Now these nodes are fresh.
+ case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
+ {ok,TracerList} -> % Generated our tracerdata.
+ case h_start_session_2(CNode,TracerList,Errors) of
+ {ok,ReturnValue} -> % Some nodes are initialized now.
+ {NewNodesD,Nodes3}=
+ set_tracing_running_nodes(CNode,ReturnValue,NodesD),
+ {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
+ {ok,{SessionNr,
+ ReturnValue,
+ Nodes3, % The nodes that shall get initial tracases.
+ LD#ld{nodes=NewNodesD,tracer_data=NewTDs}}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ {error,Reason} -> % Faulty tracer data generator func.
+ {error,{bad_tdg,Reason}}
+ end;
+ {error,Reason} -> % Error clearing patterns.
+ {error,Reason}
+ end
+ end.
+
+h_start_session_nonodes(TDGargs,LD=#ld{c_node=CNode,tracer_data=TDs},Errors) ->
+ {SessionNr,NewTDs}=insert_td_tracer_data(TDGargs,TDs),
+ if
+ CNode==undefined ->
+ {ok,{SessionNr,[],LD#ld{tracer_data=NewTDs}}};
+ true ->
+ {ok,{SessionNr,{ok,Errors},LD#ld{tracer_data=NewTDs}}}
+ end.
+
+%% Help function clearing all trace patterns on all nodes.
+h_start_session_ctp_all(CNode,Nodes) ->
+ case remove_all_trace_patterns(CNode,[],Nodes) of
+ ok -> % Non-distributed case1.
+ {ok,[],local_runtime};
+ {error,Reason} -> % Non-distributed case2 and general failure.
+ {error,Reason};
+ {ok,NodeResults} ->
+ h_start_session_ctp_all_2(NodeResults,[],[])
+ end.
+
+h_start_session_ctp_all_2([{Node,{error,Reason}}|Rest],Errors,Nodes) ->
+ h_start_session_ctp_all_2(Rest,[{Node,{error,Reason}}|Errors],Nodes);
+h_start_session_ctp_all_2([{Node,_OkOrPatternsUntouched}|Rest],Errors,Nodes) ->
+ h_start_session_ctp_all_2(Rest,Errors,[Node|Nodes]);
+h_start_session_ctp_all_2([],Errors,Nodes) ->
+ {ok,Errors,Nodes}.
+
+%% Help function doing the actual init_tracing.
+h_start_session_2(undefined,TracerData,_Errors) -> % Non distributed case.
+ case inviso:init_tracing(TracerData) of
+ {ok,LogResult} when is_list(LogResult) ->
+ {ok,{ok,LogResult}};
+ {error,already_initated} -> % Perhaps adopted!?
+ {ok,{error,already_initiated}}; % Not necessarily wrong.
+ {error,Reason} ->
+ {error,Reason}
+ end;
+h_start_session_2(CNode,TracerList,Errors) ->
+ case rpc:call(CNode,inviso,init_tracing,[TracerList]) of
+ {ok,NodeResults} ->
+ {ok,{ok,Errors++NodeResults}};
+ {error,Reason} ->
+ {error,Reason};
+ {badrpc,Reason} ->
+ {error,{inviso_control_node_error,Reason}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function starting all initial trace cases. They are actually handled
+%% the same way as user started trace cases. We actually only start initial
+%% tracecases at Nodes (if Nodes is a list of nodes). This because we may have
+%% adopted some nodes some already tracing nodes, and such are supposed to have
+%% the correct patterns and flags set.
+do_initial_tcs([{TC,Vars}|Rest],Nodes,LD) ->
+ Id=make_ref(), % Trace case ID.
+ case h_atc(TC,Id,Vars,LD,Nodes) of % Start using regular start methods.
+ {ok,NewLD} -> % Trace case was successfully started.
+ NewInitialTcs=add_initial_tcs(TC,Id,NewLD#ld.started_initial_tcs),
+ do_initial_tcs(Rest,Nodes,NewLD#ld{started_initial_tcs=NewInitialTcs});
+ {error,_Reason} ->
+ do_initial_tcs(Rest,Nodes,LD)
+ end;
+do_initial_tcs([_|Rest],Nodes,LD) ->
+ do_initial_tcs(Rest,Nodes,LD);
+do_initial_tcs([],_Nodes,LD) ->
+ LD.
+%% -----------------------------------------------------------------------------
+
+%% This help functio is used instead of do_initial_tcs/3 if there actually are no
+%% nodes to do the trace cases on. The reason we must have this function is that
+%% the tracecases must still be entered into the history with bindings and all.
+%% But we let them be marked as 'running' immediately (no need for the activator
+%% process).
+add_initial_tcs_to_history([{TC,Vars}|Rest],LD=#ld{tc_dict=TCdict,chl=CHL}) ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} ->
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} ->
+ Id=make_ref(), % Trace case ID.
+ FakeProcH=make_ref(), % Need something to enter as activator.
+ NewCHL=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
+ NewCHL2=set_running_chl(FakeProcH,TC,Id,void,NewCHL), % Result=void.
+ NewInitialTcs=add_initial_tcs(TC,Id,LD#ld.started_initial_tcs),
+ add_initial_tcs_to_history(Rest,LD#ld{chl=NewCHL2,
+ started_initial_tcs=NewInitialTcs});
+ {error,_Reason} -> % Not much we can do about that.
+ add_initial_tcs_to_history(Rest,LD)
+ end;
+ false ->
+ add_initial_tcs_to_history(Rest,LD)
+ end;
+add_initial_tcs_to_history([],LD) ->
+ LD.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% reinitiate_session
+%% -----------------------------------------------------------------------------
+
+%% Function doing the reinitiation. That means first do init_tracing at the nodes
+%% in question. Then redo the command history to bring them up to speed.
+%% But first the runtime component is cleared of all trace patterns.
+h_reinitiate_session(Nodes,M,F,TDGargs,LD=#ld{c_node=CNode,nodes=NodesD}) ->
+ case h_reinitiate_session_2(Nodes,NodesD,CNode) of
+ {ok,{[],NodesErr}} -> % No nodes to reinitiate.
+ {ok,{NodesErr,{ok,[]},LD}};
+ {ok,{Nodes2,NodesErr}} -> % List of nodes or local_runtime.
+ case call_tracer_data_generator(CNode,M,F,TDGargs,Nodes2) of
+ {ok,TracerList} ->
+ case h_start_session_2(CNode,TracerList,[]) of % Borrow from start_session.
+ {ok,ReturnValue} -> % Ok, now we must redo cmd history.
+ {NewNodesD,_Nodes}=
+ set_tracing_running_nodes(CNode,ReturnValue,NodesD),
+ NewLD=h_reinitiate_session_chl(Nodes2,LD#ld{nodes=NewNodesD}),
+ {ok,{NodesErr,ReturnValue,NewLD}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ {error,Reason} ->
+ {error,{bad_tdg,Reason}}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+%% Help function finding out which nodes in Nodes actually can be reinitiated.
+%% A node must be up, inactive and not suspended in order for this to work. All the
+%% rest is just a matter of how detailed error return values we want to generate.
+h_reinitiate_session_2(local_runtime,NodesD,undefined) -> % Non distributed case.
+ case get_state_nodes(local_runtime,NodesD) of
+ {inactive,running} -> % Only ok case.
+ case inviso:ctp_all() of
+ ok ->
+ {ok,{local_runtime,[]}};
+ {error,Reason} -> % This is strange.
+ {error,Reason}
+ end;
+ {_,suspended} ->
+ {ok,{[],{error,suspended}}};
+ down ->
+ {ok,{[],{error,down}}};
+ _ ->
+ {ok,{[],{error,already_in_session}}}
+ end;
+h_reinitiate_session_2(Nodes,NodesD,CNode) when is_list(Nodes) ->
+ {ok,lists:foldl(fun(N,{Nodes2,NodesErr})->
+ case get_state_nodes(N,NodesD) of
+ {inactive,running} -> % Only ok case.
+ case rpc:call(CNode,inviso,ctp_all,[[N]]) of
+ {ok,[{N,ok}]} ->
+ {[N|Nodes2],NodesErr};
+ {ok,[{N,{error,Reason}}]} ->
+ {Nodes2,[{N,{error,Reason}}|NodesErr]};
+ {error,Reason} ->
+ {Nodes2,[{N,{error,Reason}}|NodesErr]};
+ {badrpc,Reason} ->
+ {Nodes2,[{N,{error,{badrpc,Reason}}}|NodesErr]}
+ end;
+ {_,suspended} ->
+ {Nodes2,[{N,{error,suspended}}|NodesErr]};
+ down ->
+ {Nodes2,[{N,{error,down}}|NodesErr]};
+ false ->
+ {Nodes2,[{N,{error,unknown_node}}|NodesErr]};
+ _ ->
+ {Nodes2,[{N,{error,already_in_session}}|NodesErr]}
+ end
+ end,
+ {[],[]},
+ Nodes)};
+h_reinitiate_session_2(Nodes,_NodesD,_CNode) ->
+ {error,{badarg7,Nodes}}.
+
+%% Help function redoing the command history log at all nodes that actually
+%% started to trace. Note that we do not modify the return value which will be
+%% given to the caller just because we decide not to redo commands. The user
+%% must conclude him self from the inviso return value that commands were not
+%% redone at a particular node.
+h_reinitiate_session_chl(local_runtime,LD) ->
+ h_reinitiate_session_chl([local_runtime],LD);
+h_reinitiate_session_chl([Node|Rest],LD=#ld{nodes=NodesD}) ->
+ case get_state_nodes(Node,NodesD) of
+ {tracing,running} -> % Only case when we shall redo!
+ h_reinitiate_session_chl(Rest,redo_cmd_history(Node,LD));
+ _ -> % No redo of chl in other cases.
+ h_reinitiate_session_chl(Rest,LD)
+ end;
+h_reinitiate_session_chl([],LD) ->
+ LD.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% restore_session
+%% -----------------------------------------------------------------------------
+
+%% Help function starting a session (init tracing) and redoes the history
+%% found in CHL.
+h_restore_session(MoreTDGargs,LD) ->
+ DateTime=calendar:universal_time(),
+ {M,F,Args}=LD#ld.tdg,
+ TDGargs=inviso_tool_lib:mk_tdg_args(DateTime,MoreTDGargs++Args),
+ case h_start_session(M,F,TDGargs,LD) of
+ {ok,{SessionNr,ReturnVal,NewLD}} -> % There were no available nodes.
+ {ok,{SessionNr,ReturnVal,NewLD}};
+ {ok,{SessionNr,ReturnVal,Nodes2,NewLD}} ->
+ NewLD2=h_reinitiate_session_chl(Nodes2,NewLD),
+ {ok,{SessionNr,ReturnVal,NewLD2}};
+ {error,Reason} -> % Risk of out of control.
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% stop_session
+%% -----------------------------------------------------------------------------
+
+%% Help function stopping tracing at tracing nodes.
+h_stop_session(#ld{c_node=CNode,nodes=NodesD,tracer_data=TDs}) ->
+ case h_stop_session_2(CNode,NodesD) of
+ {ok,Result} ->
+ {ok,{get_latest_session_nr_tracer_data(TDs),Result}};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+h_stop_session_2(undefined,NodesD) -> % The non distributed case.
+ case get_tracing_nodes(NodesD) of
+ {up,{inactive,_}} -> % Already not tracing!
+ {ok,[]};
+ {up,_} ->
+ case inviso:stop_tracing() of
+ {ok,_State} ->
+ {ok,[ok]};
+ {error,no_response} ->
+ {ok,[]};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ down ->
+ {ok,[]}
+ end;
+h_stop_session_2(CNode,NodesD) ->
+ Nodes=get_tracing_nodes(NodesD),
+ case rpc:call(CNode,inviso,stop_tracing,[Nodes]) of
+ {ok,NodeResults} ->
+ {ok,lists:map(fun({N,{ok,_}})->{N,ok};
+ (NodeError)->NodeError
+ end,
+ NodeResults)};
+ {error,Reason} ->
+ {error,Reason};
+ {badrpc,Reason} ->
+ {error,{inviso_control_node_error,Reason}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function removing any trace flags, trace patterns and meta trace patterns
+%% at Nodes. This will cause the nodes to become "fresh".
+h_reset_nodes(local_runtime,_CNode) ->
+ inviso:clear([keep_log_files]);
+h_reset_nodes(Nodes,CNode) ->
+ case inviso_tool_lib:inviso_cmd(CNode,clear,[Nodes,[keep_log_files]]) of
+ {ok,NodeResults} ->
+ {ok,NodeResults};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% atc
+%% -----------------------------------------------------------------------------
+
+%% Function handling ativating a trace case. Trace cases that do not have a
+%% particular on/off handling (but just on in some scense) are handled here too.
+%% The trace case is entered into the Command History Log.
+%% Note that the trace case can not be executed at this node but must be
+%% executed where the inviso control component is.
+%% Further it is possible to either activated the tracecase for all running and
+%% tracing nodes, or just for a specified list of nodes.
+%% TC=tracecase_name(),
+%% Id=term(), identifiying this usage so we can turn it off later.
+%% Vars=list(), list of variable-value bindnings.
+h_atc(TC,Id,Vars,LD) ->
+ h_atc(TC,Id,Vars,LD,void). % For all running-tracing nodes.
+
+h_atc(TC,Id,Vars,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL},Nodes) ->
+ case find_id_chl(TC,Id,CHL) of
+ activating -> % Already started.
+ {error,activating};
+ stopping -> % Not yet stopped.
+ {error,deactivating};
+ false ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} -> % Such a trace case exists.
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} -> % Necessary vars exists in Vars.
+ if
+ is_list(Nodes) -> % Nodes predefined.
+ h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes);
+ true -> % Use all tracing and running nodes.
+ Nodes1=get_nodenames_running_nodes(LD#ld.nodes),
+ h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes1)
+ end;
+ {error,Reason} -> % Variable def missing.
+ {error,Reason}
+ end;
+ false ->
+ {error,unknown_tracecase}
+ end;
+ {ok,_Bindings} -> % Already activated and running.
+ {error,already_started}
+ end.
+
+h_atc_2(TC,Id,CNode,CHL,LD,TraceCase,Bindings,Nodes) ->
+ {ok,ProcH} = exec_trace_case_on(CNode,TraceCase,Bindings,Nodes),
+ %% Trace cases have no return values.
+ NewCHL=set_activating_chl(TC,Id,CHL,Bindings,ProcH),
+ {ok,LD#ld{chl=NewCHL}}.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% sync_atc
+%% -----------------------------------------------------------------------------
+
+h_sync_atc(TC,Id,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case find_id_chl(TC,Id,CHL) of
+ activating -> % Already started.
+ {error,activating};
+ stopping -> % Not yet stopped.
+ {error,deactivating};
+ false ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} -> % Such a trace case exists.
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} -> % Necessary vars exists in Vars.
+ {ok,TcFName}=get_tc_activate_fname(TraceCase),
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
+ RpcNode=get_rpc_nodename(CNode),
+ case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
+ {ok,Value} ->
+ FakeProcH=make_ref(),
+ NewCHL1=set_activating_chl(TC,Id,CHL,Bindings,FakeProcH),
+ NewCHL2=set_running_chl(FakeProcH,TC,Id,Value,NewCHL1),
+ {ok,LD#ld{chl=NewCHL2},Value};
+ {error,Reason} ->
+ {error,{faulty_tracecase,{TcFName,Reason}}};
+ {badrpc,Reason} ->
+ {error,{badrpc,Reason}}
+ end;
+ {error,Reason} -> % Variable def missing.
+ {error,Reason}
+ end;
+ false ->
+ {error,unknown_tracecase}
+ end;
+ {ok,_Bindings} -> % Already activated and running.
+ {error,already_started}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% rtc
+%% -----------------------------------------------------------------------------
+
+%% Function handling running a trace case without marking it as activated. It
+%% is in the history mearly indicated as activated
+h_sync_rtc(TC,Vars,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} -> % Such a trace case exists.
+ case check_bindings(Vars,TraceCase) of
+ {ok,Bindings} -> % Necessary vars exists in Vars.
+ {ok,TcFName}=get_tc_activate_fname(TraceCase),
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
+ RpcNode=get_rpc_nodename(CNode),
+ case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
+ {ok,Value} ->
+ {ok,LD#ld{chl=add_rtc_chl(TC,Bindings2,CHL)},Value};
+ {error,Reason} ->
+ {error,{faulty_tracecase,{TcFName,Reason}}};
+ {badrpc,Reason} ->
+ {error,{badrpc,Reason}}
+ end;
+ {error,Reason} -> % Variable def missing.
+ {error,Reason}
+ end;
+ false ->
+ {error,unknown_tracecase}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% dtc
+%% -----------------------------------------------------------------------------
+
+%% Function handling turning a trace case off. The trace case must be registered
+%% as having an off mechanism. If it has an off mechanism and was previously entered
+%% into the Command History Log and is done with its activation phase, it will be
+%% executed and removed from the CHL.
+h_dtc(TC,Id,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case find_id_chl(TC,Id,CHL) of
+ {ok,Bindings} -> % Yes, we have turned it on before.
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} ->
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ case exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) of
+ {ok,ProcH} ->
+ NewCHL=set_stopping_chl(TC,Id,CHL,ProcH),
+ {ok,LD#ld{chl=NewCHL}};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ false -> % Strange, Id ok but no such trace case.
+ {error,unknown_tracecase}
+ end;
+ false -> % Not previously turned on.
+ {error,unknown_id};
+ activating ->
+ {error,activating};
+ stopping ->
+ {error,already_deactivating}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% sync_dtc
+%% -----------------------------------------------------------------------------
+
+h_sync_dtc(TC,Id,TimeOut,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL}) ->
+ case find_id_chl(TC,Id,CHL) of
+ {ok,Bindings} -> % Yes, we have turned it on before.
+ case get_tracecase_tc_dict(TC,TCdict) of
+ {ok,TraceCase} ->
+ case get_tc_deactivate_fname(TraceCase) of
+ {ok,TcFName} ->
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ Bindings2=erl_eval:add_binding('Nodes',Nodes,Bindings),
+ RpcNode=get_rpc_nodename(CNode),
+ case rpc:call(RpcNode,file,script,[TcFName,Bindings2],TimeOut) of
+ {ok,Value} ->
+ FakeProcH=make_ref(),
+ NewCHL1=set_stopping_chl(TC,Id,CHL,FakeProcH),
+ NewCHL2=nullify_chl(FakeProcH,TC,Id,NewCHL1),
+ {ok,LD#ld{chl=NewCHL2},Value};
+ {error,Reason} -> % Script fault.
+ {error,{faulty_tracecase,{TcFName,Reason}}};
+ {badrpc,Reason} ->
+ {error,{badrpc,Reason}}
+ end;
+ false ->
+ {error,no_deactivation}
+ end;
+ false -> % Strange, Id ok but no such trace case.
+ {error,unknown_tracecase}
+ end;
+ false -> % Not previously turned on.
+ {error,unknown_id};
+ activating ->
+ {error,activating};
+ stopping ->
+ {error,already_deactivating}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% inviso
+%% -----------------------------------------------------------------------------
+
+%% Function executing one inviso command. The returnvalue from the inviso
+%% function call will be the return value to the client. The command is
+%% entered into the history command log.
+%% Note that the inviso call may have to be done at another node, dictated
+%% by the c_node field. Further, if the module name is not an atom it is
+%% most likely a regexp, which must be expanded at the regexp_node. Note
+%% this is only relevant for tp and tpl.
+h_inviso(Cmd,Args,LD=#ld{c_node=CNode,regexp_node=RegExpNode,chl=CHL}) ->
+ Arity=length(Args),
+ case check_proper_inviso_call(Cmd,Arity) of
+ {true,RegExpFlag} -> % Yes it is an inviso call.
+ Nodes=get_nodenames_running_nodes(LD#ld.nodes),
+ case h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) of
+ {ok,Result} ->
+ case check_inviso_call_to_history(Cmd,Arity) of
+ true -> % This function shall be added to chl.
+ {ok,{Result,LD#ld{chl=add_inviso_call_chl(Cmd,Args,CHL)}}};
+ false -> % Do not add it.
+ {ok,{Result,LD}}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end;
+ false -> % Not an inviso function.
+ {error,invalid_function_name}
+ end.
+
+h_inviso_2(Cmd,Args,undefined,_,_,_) -> % A non distributed system.
+ case catch apply(inviso,Cmd,Args) of % Regexp expansion only relevant when
+ {'EXIT',Reason} -> % distributed, here let inviso_rt expand.
+ {error,{'EXIT',Reason}};
+ Result ->
+ {ok,Result}
+ end;
+h_inviso_2(Cmd,Args,CNode,RegExpNode,RegExpFlag,Nodes) ->
+ case expand_module_regexps(Args,RegExpNode,Nodes,RegExpFlag) of
+ {ok,NewArgs} ->
+ case catch inviso_tool_lib:inviso_cmd(CNode,Cmd,[Nodes|NewArgs]) of
+ {'EXIT',Reason} ->
+ {error,{'EXIT',Reason}};
+ {error,{badrpc,Reason}} -> % Includes runtime failure.
+ {error,{badrpc,Reason}};
+ Result ->
+ {ok,Result}
+ end;
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% reactivate
+%% -----------------------------------------------------------------------------
+
+h_reactivate(_Node,undefined) -> % The non-distributed case.
+ case inviso:cancel_suspension() of
+ ok ->
+ ok;
+ {error,Reason} ->
+ {error,Reason}
+ end;
+h_reactivate(Node,CNode) ->
+ case inviso_tool_lib:inviso_cmd(CNode,cancel_suspension,[[Node]]) of
+ {ok,[{Node,ok}]} ->
+ ok;
+ {ok,[{Node,{error,Reason}}]} ->
+ {error,Reason};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% save_history
+%% -----------------------------------------------------------------------------
+
+h_save_history(HDir,Dir,FileName,SortedLog) ->
+ Dir0=
+ if
+ is_list(HDir) -> % There is a history dir specified.
+ HDir; % Use it then.
+ true ->
+ Dir % Else use the tool dir.
+ end,
+ case catch make_absolute_path(FileName,Dir0) of
+ AbsFileName when is_list(AbsFileName) ->
+ Log2=build_saved_history_data(SortedLog), % Remove stopped tracecases.
+ case file:write_file(AbsFileName,term_to_binary(Log2)) of
+ ok ->
+ {ok,AbsFileName};
+ {error,Reason} ->
+ {error,{write_file,Reason}}
+ end;
+ {'EXIT',_Reason} ->
+ {error,{bad_filename,FileName}}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% get_autostart_data
+%% -----------------------------------------------------------------------------
+
+%% Help function building the structures used when exporting autostart information
+%% from the tool. Note that we remove the tool-dependency and insert the one
+%% specify in the get_autostart_data call.
+h_get_autostart_data(local_runtime,_,Dependency,ASD,M,F,TDGargs,OptsG) ->
+ CompleteTDGargs=call_tracer_data_generator_mkargs(local_runtime,TDGargs),
+ Opts0=start_runtime_components_mk_opts(local_runtime,OptsG),
+ Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
+ {ok,{ASD,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}};
+
+h_get_autostart_data(Nodes,CNode,Dependency,ASD,M,F,TDGargs,OptsG) when is_list(Nodes) ->
+ {ok,{ASD,h_get_autostart_data_2(Nodes,CNode,Dependency,M,F,TDGargs,OptsG)}};
+h_get_autostart_data(Nodes,_CNode,_Dependency,_ASD,_M,_F,_TDGargs,_OptsG) ->
+ {error,{badarg,Nodes}}.
+
+h_get_autostart_data_2([Node|Rest],CNode,Dependency,M,F,TDGargs,OptsG) ->
+ CompleteTDGargs=call_tracer_data_generator_mkargs(Node,TDGargs),
+ Opts0=start_runtime_components_mk_opts(Node,OptsG),
+ Opts=[Dependency|lists:keydelete(dependency,1,Opts0)],
+ [{Node,{ok,{Opts,{tdg,{M,F,CompleteTDGargs}}}}}|
+ h_get_autostart_data_2(Rest,CNode,Dependency,M,F,TDGargs,OptsG)];
+h_get_autostart_data_2([],_CNode,_Dependency,_M,_F,_TDGargs,_OptsG) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% flush
+%% -----------------------------------------------------------------------------
+
+h_flush(undefined,_Nodes) ->
+ inviso:flush();
+h_flush(CNode,Nodes) ->
+ inviso_tool_lib:inviso_cmd(CNode,flush,[Nodes]).
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% tc_executer_reply
+%% -----------------------------------------------------------------------------
+
+%% Function handling that a trace case has completed its activation phase and
+%% shall now be marked in the Command History Log as running.
+h_tc_activation_done(ProcH,Result,LD=#ld{chl=CHL}) ->
+ case find_tc_executer_chl(ProcH,CHL) of
+ {activating,{TC,Id}} ->
+ case Result of
+ {ok,Value} -> % The trace case is successful activated.
+ {ok,LD#ld{chl=set_running_chl(ProcH,TC,Id,Value,CHL)}};
+ {error,_} -> % Then pretend it never happend :-)
+ {ok,LD#ld{chl=del_tc_chl(ProcH,TC,Id,CHL)}} % Remove it.
+ end;
+ _ -> % Where did this come from?
+ {ok,LD} % Well just ignore it then.
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Function handling that a trace case has completed its stopping phase and
+%% shall now be nulled in the Command History Log (meaning that it will not
+%% be repeated in the event of a reactivation).
+h_tc_stopping_done(ProcH,Result,LD=#ld{chl=CHL}) ->
+ case find_tc_executer_chl(ProcH,CHL) of
+ {stopping,{TC,Id}} ->
+ case Result of
+ {ok,_Result} -> % _Result is returned from the tracecase.
+ {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}};
+ {error,_} -> % This is difficult, is it still active?
+ {ok,LD#ld{chl=nullify_chl(ProcH,TC,Id,CHL)}}
+ end;
+ _ -> % Strange.
+ {ok,LD}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Terminate.
+%% -----------------------------------------------------------------------------
+
+%% Help function stopping the inviso control component. Does not return
+%% anything significant.
+stop_inviso_at_c_node(undefined) -> % Non distributed case.
+ inviso:stop();
+stop_inviso_at_c_node(CNode) ->
+ rpc:call(CNode,inviso,stop,[]).
+%% -----------------------------------------------------------------------------
+
+%% Help function that removes all trace patterns from the nodes that are not
+%% marked as such were patterns shall be left after stopping of inviso.
+%% Returns {ok,NodeResult} or {error,Reason}. In the non-distributed case
+%% 'ok' is returned incase of success, ot 'patterns_untouched'.
+remove_all_trace_patterns(undefined,KeepNodes,_Nodes) ->
+ case KeepNodes of
+ undefined -> % No, remove patterns from localruntime.
+ inviso:ctp_all();
+ _ ->
+ patterns_untouched
+ end;
+remove_all_trace_patterns(CNode,KeepNodes,Nodes) ->
+ Nodes2=lists:filter(fun(N)->not(lists:member(N,KeepNodes)) end,Nodes),
+ case inviso_tool_lib:inviso_cmd(CNode,ctp_all,[Nodes2]) of
+ {ok,NodeResults} ->
+ F=fun(N) ->
+ case lists:member(N,KeepNodes) of
+ true ->
+ {N,patterns_untouched};
+ false ->
+ case lists:keysearch(N,1,NodeResults) of
+ {value,Result} ->
+ Result; % {Node,ok}
+ false -> % Extremely strange.
+ {N,{error,general_error}}
+ end
+ end
+ end,
+ {ok,lists:map(F,Nodes)};
+ {error,{badrpc,Reason}} ->
+ {error,{inviso_control_node_error,Reason}};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% -----------------------------------------------------------------------------
+
+%% =============================================================================
+%% Second level help functions.
+%% =============================================================================
+
+%% Help function building a reply to a reconnection call based on which nodes
+%% where asked to be reconnected and which of those are actually now working.
+%% We actually make an effort to serve the return value in the same order as the
+%% nodes were mentioned in the original call (Nodes).
+build_reconnect_nodes_reply(local_runtime,local_runtime,_NodesErr,NodesD) ->
+ case get_state_nodes(local_runtime,NodesD) of
+ down ->
+ {error,down};
+ {State,Status} ->
+ {ok,{State,Status}}
+ end;
+build_reconnect_nodes_reply(local_runtime,_,NodesErr,_NodesD) ->
+ NodesErr;
+build_reconnect_nodes_reply([Node|Rest],Nodes2,NodesErr,NodesD) ->
+ case lists:member(Node,Nodes2) of
+ true -> % Ok, look in the #ld.nodes.
+ case get_state_nodes(Node,NodesD) of
+ down -> % Somekind of failure, still down.
+ [{Node,{error,down}}|
+ build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)];
+ {State,Status} -> % {State,Status}
+ [{Node,{ok,{State,Status}}}|
+ build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
+ end;
+ false -> % Error already from the beginning.
+ {value,{_,Error}}=lists:keysearch(Node,1,NodesErr),
+ [{Node,Error}|build_reconnect_nodes_reply(Rest,Nodes2,NodesErr,NodesD)]
+ end;
+build_reconnect_nodes_reply([],_,_,_) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Help function building a return value to reinitiate_session. Nodes contains
+%% all involved nodes. If the node occurrs in NodesErr, we choose the error in
+%% NodesErr. Otherwise the returnvalue in ReturnVal is used.
+build_reinitiate_session_reply(Nodes,NodesErr,{ok,NodesResults}) ->
+ {ok,build_reinitiate_session_reply_2(Nodes,NodesErr,NodesResults)};
+build_reinitiate_session_reply(local_runtime,[],NodeResult) ->
+ NodeResult;
+build_reinitiate_session_reply(local_runtime,NodesErr,_NodeResult) ->
+ NodesErr.
+build_reinitiate_session_reply_2([Node|Rest],NodesErr,NodeResults) ->
+ case lists:keysearch(Node,1,NodesErr) of
+ {value,{_,Error}} ->
+ [{Node,Error}|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)];
+ false ->
+ case lists:keysearch(Node,1,NodeResults) of
+ {value,Value} ->
+ [Value|build_reinitiate_session_reply_2(Rest,NodesErr,NodeResults)]
+ end
+ end;
+build_reinitiate_session_reply_2([],_NodesErr,_NodeResults) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Help function returning a history log where stop and stopping entries have
+%% been removed. Further all tracecase log entries must be set to running since
+%% there can not be such a thing as an activating tracecase stored away in a
+%% saved historyfile!
+%% We must also take away any #Ref.
+build_saved_history_data(SortedLog) ->
+ CleanedLog=
+ lists:filter(fun({_,_,Stop,_}) when Stop==stop;Stop==stopping -> false;
+ (_) -> true
+ end,
+ SortedLog),
+ lists:map(fun({{TC,Id},C,activating,B}) -> {{TC,Id},C,running,B};
+ ({{TC,Id},C,S,B}) -> {{TC,Id},C,S,B};
+ ({{M,F,Args,_Ref},C}) -> {{M,F,Args},C};
+ ({{TC,_Ref},C,B}) -> {TC,C,B} % An rtc.
+ end,
+ CleanedLog).
+%% -----------------------------------------------------------------------------
+
+%% This help function builds the AutoStartData structure which is returned from
+%% get_austostart_data. An AutoStartData structure is a list of trace-files and
+%% inviso commands. The order is significant since it is the idea that doing
+%% the trace case files and inviso commands in that order will bring a node to
+%% a certain state in a trace perspective.
+%% Returns {ok,AutoStartData} or {error,Reason}
+build_autostart_data(SortedLog,TCdict) ->
+ build_autostart_data_2(SortedLog,TCdict,[]).
+
+build_autostart_data_2([{_,_C,Stop,_B}|Rest],TCdict,Accum) when Stop==stop;Stop==stopping->
+ build_autostart_data_2(Rest,TCdict,Accum); % Simply skip deactivated/deativating.
+build_autostart_data_2([{{TCname,_},_C,activating,Bindings}|Rest],TCdict,Accum) ->
+ build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
+build_autostart_data_2([{{TCname,_},_C,running,Bindings}|Rest],TCdict,Accum) ->
+ build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
+build_autostart_data_2([{{TCname,_Ref},_C,Bindings}|Rest],TCdict,Accum) ->
+ build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum);
+build_autostart_data_2([{{M,F,Args,_Ref},_C}|Rest],TCdict,Accum) ->
+ build_autostart_data_2(Rest,TCdict,[{mfa,{M,F,Args}}|Accum]);
+build_autostart_data_2([],_TCdict,Accum) ->
+ {ok,lists:reverse(Accum)}.
+
+%% Help function placing the filename in the AutoStartData structure.
+build_autostart_data_tc(TCname,Bindings,TCdict,Rest,Accum) ->
+ {ok,TC}=get_tracecase_tc_dict(TCname,TCdict),
+ {ok,FName}=get_tc_activate_fname(TC),
+ build_autostart_data_2(Rest,TCdict,[{file,{FName,Bindings}}|Accum]).
+%% -----------------------------------------------------------------------------
+
+%% Help function generating tracerdata to init inviso tracing. The generation
+%% is done by the TracerDataGenerator, TDG, function.
+%% Individual tracerdata is generated for each node in Nodes.
+%% Returns {ok,TracerData} or {error,Reason}.
+call_tracer_data_generator(undefined,M,F,TDGargs,_Nodes) -> % Non distributed.
+ case catch call_tracer_data_generator_3(M,F,TDGargs,local_runtime) of
+ {'EXIT',Reason} ->
+ {error,{'EXIT',Reason}};
+ TracerData ->
+ {ok,TracerData}
+ end;
+call_tracer_data_generator(_CNode,M,F,TDGargs,Nodes) ->
+ case catch call_tracer_data_generator_2(M,F,TDGargs,Nodes) of
+ {'EXIT',Reason} ->
+ {error,{'EXIT',Reason}};
+ TracerList ->
+ {ok,TracerList}
+ end.
+
+call_tracer_data_generator_2(M,F,TDGargs,[Node|Rest]) ->
+ [{Node,call_tracer_data_generator_3(M,F,TDGargs,Node)}|
+ call_tracer_data_generator_2(M,F,TDGargs,Rest)];
+call_tracer_data_generator_2(_,_,_,[]) ->
+ [].
+
+call_tracer_data_generator_3(M,F,TDGargs,Node) ->
+ apply(M,F,call_tracer_data_generator_mkargs(Node,TDGargs)).
+
+%% This function creates the arguments that the tracer data generator function
+%% accepts (in an apply call). The reason for making it a sepparate function is
+%% that the arguments are constructed in more situations than just when actually
+%% doing the apply. By having a function it will become obvious where to change
+%% should the arguments change.
+call_tracer_data_generator_mkargs(Node,TDGargs) ->
+ inviso_tool_lib:mk_complete_tdg_args(Node,TDGargs).
+%% -----------------------------------------------------------------------------
+
+%% This function acts as standard options generator function. That is returning
+%% the options argument to inviso:add_node/3. Note that this function must not
+%% return the dependency part of that option.
+std_options_generator(_Node) ->
+ []. % No particular options(!)
+%% -----------------------------------------------------------------------------
+
+
+%% Help function checking that Vars contains a binding for every variable
+%% listed in the VarNames field in TraceCase. Note that the special variable 'Nodes'
+%% is disregarded, since it is always added by the inviso_tool.
+%% Returns {ok,Bindings} or {error,Reason}. Where Bindings is a bindngs structure
+%% according to file:eval functionality.
+check_bindings(Vars,TraceCase) ->
+ case catch check_bindings_2(Vars,
+ get_tc_varnames(TraceCase),
+ erl_eval:new_bindings()) of
+ {'EXIT',_Reason} ->
+ {error,variable_error};
+ {error,Reason} -> % Missing a bindning.
+ {error,Reason};
+ {ok,Bindings} ->
+ {ok,Bindings}
+ end.
+
+check_bindings_2(Vars,['Nodes'|Rest],Bindings) ->
+ check_bindings_2(Vars,Rest,Bindings); % Disregard Nodes since it is automatic.
+check_bindings_2(Vars,[VarName|Rest],Bindings) ->
+ case lists:keysearch(VarName,1,Vars) of
+ {value,{_,Val}} ->
+ check_bindings_2(Vars,Rest,erl_eval:add_binding(VarName,Val,Bindings));
+ false -> % Mandatory variable missing.
+ {error,{missing_variable,VarName}} % Quite here then.
+ end;
+check_bindings_2(_,[],Bindings) ->
+ {ok,Bindings}.
+%% -----------------------------------------------------------------------------
+
+%% This help function checks that the command the user tries to do is amongst
+%% the inviso API. It at the same time returns what kind of command it is.
+%% {true,RegExpFlag} or 'false' where RegExpFlag indicates if this command
+%% needs to have its argument modified by module regexp expansion or not.
+check_proper_inviso_call(Cmd,Arity) ->
+ case lists:member({Cmd,Arity},?INVISO_CMDS) of
+ true -> % It is part of inviso API.
+ {true,check_proper_inviso_call_regexp(Cmd,Arity)};
+ false ->
+ false
+ end.
+
+%% Returns {Type,Arity,PlaceOfModuleSpec} or 'false'.
+check_proper_inviso_call_regexp(tp,5) -> {tp,5,1};
+check_proper_inviso_call_regexp(tp,4) -> {tp,4,1};
+check_proper_inviso_call_regexp(tp,1) -> {tp,1,1};
+check_proper_inviso_call_regexp(tpl,5) -> {tp,5,1};
+check_proper_inviso_call_regexp(tpl,4) -> {tp,4,1};
+check_proper_inviso_call_regexp(tpl,1) -> {tp,1,1};
+check_proper_inviso_call_regexp(ctp,3) -> {ctp,3,1};
+check_proper_inviso_call_regexp(ctp,1) -> {ctp,1,1};
+check_proper_inviso_call_regexp(ctpl,3) -> {ctp,3,1};
+check_proper_inviso_call_regexp(ctpl,1) -> {ctp,1,1};
+check_proper_inviso_call_regexp(_,_) -> % No regexp expansion.
+ false.
+%% -----------------------------------------------------------------------------
+
+%% Help function checking if this inviso command shall be added to the command
+%% history log. Returns true or false.
+check_inviso_call_to_history(Cmd,Arity) ->
+ case lists:member({Cmd,Arity},?INVISO_CMD_HISTORY) of
+ true ->
+ true;
+ false ->
+ false
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function traversing the arguments and expanding module names stated
+%% as regular expressions. This means that the resulting arguments may be longer
+%% than the orginal ones.
+%% When we run this function it has been determined that we are a distributed
+%% system.
+%% Also note that if there are no regexps in Args, no regexpansion will be
+%% made and RegExpNode may be 'undefined' (as it is if not set at start-up).
+%% If RegExpNode is unavailable the nodes found in Nodes will be used until
+%% one that works is found.
+expand_module_regexps(Args,_RegExpNode,_Nodes,false) ->
+ {ok,Args};
+expand_module_regexps([PatternList],RegExpNode,Nodes,{tp,1,1}) ->
+ case catch expand_module_regexps_tp(PatternList,RegExpNode,Nodes) of
+ NewPatternList when is_list(NewPatternList) ->
+ {ok,[NewPatternList]};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+expand_module_regexps([PatternList],RegExpNode,Nodes,{ctp,1,1}) ->
+ case catch expand_module_regexps_ctp(PatternList,RegExpNode,Nodes) of
+ NewPatternList when is_list(NewPatternList) ->
+ {ok,[NewPatternList]};
+ {error,Reason} ->
+ {error,Reason}
+ end;
+expand_module_regexps([M,F,Arity,MS,Opts],RegExpNode,Nodes,{tp,5,1}) ->
+ expand_module_regexps([[{M,F,Arity,MS,Opts}]],RegExpNode,Nodes,{tp,1,1});
+expand_module_regexps([M,F,Arity,MS],RegExpNode,Nodes,{tp,4,1}) ->
+ expand_module_regexps([[{M,F,Arity,MS,[]}]],RegExpNode,Nodes,{tp,1,1});
+expand_module_regexps([M,F,Arity],RegExpNode,Nodes,{ctp,3,1}) ->
+ expand_module_regexps([[{M,F,Arity}]],RegExpNode,Nodes,{ctp,1,1}).
+
+
+expand_module_regexps_tp([E={M,_,_,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
+ [E|expand_module_regexps_tp(Rest,RegExpNode,Nodes)];
+expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
+ case inviso_tool_lib:expand_module_names([RegExpNode],
+ M,
+ [{expand_only_at,RegExpNode}]) of
+ {singlenode_expansion,Modules} ->
+ expand_module_regexps_tp_2(Modules,F,Arity,MS,Opts,Rest,RegExpNode,Nodes);
+ {error,{faulty_node,RegExpNode}} -> % RegExpNode probably down.
+ case Nodes of
+ [NewRegExpNode|RestNodes] -> % Ok, just choose a node.
+ expand_module_regexps_tp([{M,F,Arity,MS,Opts}|Rest],NewRegExpNode,RestNodes);
+ [] -> % No more nodes to choose from.
+ throw({error,no_available_regexpnode})
+ end;
+ {error,_Reason} ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes)
+ end;
+expand_module_regexps_tp([_|Rest],RegExpNode,Nodes) ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
+expand_module_regexps_tp([],_RegExpNodes,_Nodes) ->
+ [].
+
+expand_module_regexps_tp_2([M|MRest],F,Arity,MS,Opts,Rest,RegExpNode,Nodes) ->
+ [{M,F,Arity,MS,Opts}|
+ expand_module_regexps_tp_2(MRest,F,Arity,MS,Opts,Rest,RegExpNode,Nodes)];
+expand_module_regexps_tp_2([],_,_,_,_,Rest,RegExpNode,Nodes) ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes).
+
+expand_module_regexps_ctp([E={M,_,_}|Rest],RegExpNode,Nodes) when is_atom(M) ->
+ [E|expand_module_regexps_ctp(Rest,RegExpNode,Nodes)];
+expand_module_regexps_ctp([{M,F,Arity}|Rest],RegExpNode,Nodes) when is_list(M);is_tuple(M) ->
+ case inviso_tool_lib:expand_module_names([RegExpNode],
+ M,
+ [{expand_only_at,RegExpNode}]) of
+ {singlenode_expansion,Modules} ->
+ expand_module_regexps_ctp_2(Modules,F,Arity,Rest,RegExpNode,Nodes);
+ {error,_Reason} ->
+ expand_module_regexps_ctp(Rest,RegExpNode,Nodes)
+ end;
+expand_module_regexps_ctp([_|Rest],RegExpNode,Nodes) ->
+ expand_module_regexps_tp(Rest,RegExpNode,Nodes); % Skip faulty module specification.
+expand_module_regexps_ctp([],_RegExpNodes,_Nodes) ->
+ [].
+
+expand_module_regexps_ctp_2([M|MRest],F,Arity,Rest,RegExpNode,Nodes) ->
+ [{M,F,Arity}|expand_module_regexps_ctp_2(MRest,F,Arity,Rest,RegExpNode,Nodes)];
+expand_module_regexps_ctp_2([],_,_,Rest,RegExpNode,Nodes) ->
+ expand_module_regexps_ctp(Rest,RegExpNode,Nodes).
+%% -----------------------------------------------------------------------------
+
+
+
+%% Help function running the activation of a trace case. Note that this must
+%% be done at the inviso control component's Erlang node *and* that it must be
+%% done in its own process since there is no telling for how long a trace case
+%% may run.
+%% Returns {ok,ActivationHandler}.
+exec_trace_case_on(CNode,TraceCase,Bindings,Nodes) ->
+ {ok,TcFName}=get_tc_activate_fname(TraceCase),
+ {ok,exec_trace_case_2(CNode,
+ TcFName,
+ erl_eval:add_binding('Nodes',Nodes,Bindings),
+ activating)}.
+
+%% Help function running the deactivation of a trace case.
+exec_trace_case_off(CNode,TraceCase,Bindings,Nodes) ->
+ case get_tc_deactivate_fname(TraceCase) of
+ {ok,TcFName} -> % There is a deactivation.
+ {ok,exec_trace_case_2(CNode,
+ TcFName,
+ erl_eval:add_binding('Nodes',Nodes,Bindings),
+ stopping)};
+ false ->
+ {error,no_deactivation}
+ end.
+
+exec_trace_case_2(CNode,TcFName,Bindings,Phase) ->
+ if
+ CNode==undefined -> % The non distributed case.
+ spawn_link(?MODULE,tc_executer,[TcFName,Bindings,Phase,self()]);
+ true ->
+ spawn_link(CNode,?MODULE,tc_executer,[TcFName,Bindings,Phase,self()])
+ end.
+
+%% This function is run in its own process and is responsible for executing
+%% the trace case.
+tc_executer(TcFName,Bindings,Phase,Parent) ->
+ case catch file:script(TcFName,Bindings) of
+ {ok,Value} ->
+ tc_executer_reply(Parent,{Phase,self(),{ok,Value}});
+ {'EXIT',Reason} ->
+ tc_executer_reply(Parent,{Phase,self(),{error,{'EXIT',Reason}}});
+ Error ->
+ tc_executer_reply(Parent,{Phase,self(),Error})
+ end.
+%% -----------------------------------------------------------------------------
+
+%% Help function which starts a reactivator process redoing command history at
+%% Node. It also updates the loopdata to indicate that Node is now in state
+%% reactivating. It is a good idea to only handle one node per reactivator process.
+%% This because if the node terminates and comes back up, the reactivator must be
+%% stopped.
+redo_cmd_history(Node,LD=#ld{c_node=CNode,tc_dict=TCdict,chl=CHL,nodes=NodesD}) ->
+ P=start_reactivator(Node,CNode,TCdict,CHL),
+ LD#ld{nodes=set_reactivating_nodes(Node,NodesD),
+ reactivators=add_reactivators(Node,P,LD#ld.reactivators)}.
+
+%% Help function starting a reactivator process replaying the command history log.
+%% Returns a pid of the reactivator process.
+start_reactivator(Node,CNode,TCdict,CHL) ->
+ UnsortedLog=get_loglist_chl(CHL), % Must fetch here, later on wrong node.
+ if
+ CNode==undefined -> % The non-distributed case.
+ spawn_link(?MODULE,
+ reactivator_executer,
+ [Node,TCdict,UnsortedLog,self(),0,[]]);
+ true ->
+ spawn_link(CNode,
+ ?MODULE,
+ reactivator_executer,
+ [Node,TCdict,UnsortedLog,self(),0,[]])
+ end.
+
+%% The strategy is to traverse the CHL ETS table in Counter order, redoing the
+%% commands one by one. We wait until one command is finished until we do the
+%% next. Commands marked as nullified are not performed. In fact when a command
+%% is nullified only the stop will be found in the CHL. Its activation will be
+%% removed.
+reactivator_executer(Node,TCdict,UnsortedLog,TPid,StartCounter,DoneCases) ->
+ SortedLog=lists:keysort(2,UnsortedLog), % Sort on Counter, oldest first.
+ Log=reactivator_skip_log_entries(SortedLog,StartCounter),
+ case reactivator_executer_2(Node,TCdict,TPid,StartCounter,DoneCases,Log) of
+ done ->
+ true; % Simply terminate the reactivator then.
+ {more,{NewStartCounter,NewDoneCases,NewUnsortedLog}} ->
+ reactivator_executer(Node,TCdict,NewUnsortedLog,TPid,NewStartCounter,NewDoneCases)
+ end.
+
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,Id},NextC,running,Bindings}|Rest]) ->
+ reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,_Ref},NextC,Bindings}|Rest]) ->
+ reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,Id},NextC,activating,Bindings}|Rest]) ->
+ reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{M,F,Args,_Ref},NextC}|Rest]) ->
+ reactivator_executer_cmd(Node,M,F,Args),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{_TCname,_Id},NextC,stopping,_Bindings}|Rest]) ->
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+reactivator_executer_2(Node,TCdict,TPid,_Counter,DoneCases,
+ [{{TCname,Id,_Ref},NextC,stop,Bindings}|Rest]) ->
+ case lists:member({TCname,Id},DoneCases) of
+ true -> % We have activated it, must stop then.
+ case get_tracecase_tc_dict(TCname,TCdict) of
+ {ok,{_,_,_,_,FNameOff}} ->
+ reactivator_executer_tc(Node,Bindings,FNameOff),
+ NewDoneCases=lists:delete({TCname,Id},DoneCases),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,NewDoneCases,Rest);
+ {ok,_} -> % No stop-filename, strange!
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+ false -> % Even stranger, does not exist!?
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end;
+ false -> % Never activated in the first place.
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end;
+%% Done all log entries found this lap. See if there are more entries by now.
+reactivator_executer_2(_Node,_TCdict,TPid,Counter,DoneCases,[]) ->
+ case reactivator_reply(TPid,Counter) of % Ask the tool process for more entries.
+ done -> % No more entries in the CHL.
+ done;
+ {more,NewUnsortedLog} -> % Repeat the procedure
+ {more,{Counter+1,DoneCases,NewUnsortedLog}} % with log entries from Counter+1.
+ end.
+
+%% This help function activates a tracecase.
+reactivator_executer_3(Node,TCdict,TPid,DoneCases,Rest,TCname,Id,NextC,Bindings,Rest) ->
+ case get_tracecase_tc_dict(TCname,TCdict) of
+ {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
+ {ok,{_,_,_,FNameOn,_}} ->
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,[{TCname,Id}|DoneCases],Rest);
+ false -> % Strange, does not exist anylonger!?
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end.
+
+%% Help function executing a trace case in the reactivators context. Does not
+%% return anything significant.
+reactivator_executer_tc(Node,Bindings,FileName) ->
+ catch file:eval(FileName,erl_eval:add_binding('Nodes',[Node],Bindings)).
+
+%% Help function handling trace case that are simply executed - rtc.
+reactivator_executer_rtc(Node,TCdict,TPid,DoneCases,Rest,TCname,NextC,Bindings,Rest) ->
+ case get_tracecase_tc_dict(TCname,TCdict) of
+ {ok,{_,_,_,FNameOn}} -> % A case with just on functionality.
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+ {ok,{_,_,_,FNameOn,_}} ->
+ reactivator_executer_tc(Node,Bindings,FNameOn),
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest);
+ false -> % Strange, does not exist anylonger!?
+ reactivator_executer_2(Node,TCdict,TPid,NextC,DoneCases,Rest)
+ end.
+
+reactivator_executer_cmd(nonode@nohost,M,F,Args) ->
+ catch apply(M,F,Args); % Non-distributed.
+reactivator_executer_cmd(Node,M,F,Args) ->
+ catch apply(M,F,[[Node]|Args]).
+
+%% Help function returning a list of log entries missing the first entries
+%% having a counter less or equal to C1.
+reactivator_skip_log_entries([{_,C,_,_}|Rest],C1) when C<C1 ->
+ reactivator_skip_log_entries(Rest,C1);
+reactivator_skip_log_entries([{_,C}|Rest],C1) when C<C1 ->
+ reactivator_skip_log_entries(Rest,C1);
+reactivator_skip_log_entries(Log,_) ->
+ Log.
+%% -----------------------------------------------------------------------------
+
+%% Help function returning the node name to use in an rpc call.
+get_rpc_nodename(undefined) ->
+ node();
+get_rpc_nodename(CNode) ->
+ CNode.
+%% -----------------------------------------------------------------------------
+
+mk_rt_tag() ->
+ inviso_tool.
+%% -----------------------------------------------------------------------------
+
+is_string([C|Rest]) when C>=32, C=<255 ->
+ is_string(Rest);
+is_string([]) ->
+ true;
+is_string(_) ->
+ false.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% Functions for handling the configuration file.
+%% -----------------------------------------------------------------------------
+
+%% The inviso tool is configured via start arguments and/or a configuration file.
+%% Start arguments will override any definitions in a configuration file.
+%% The configuration file is pointed out by either a start argument or the
+%% inviso application parameter 'inviso_tool_config_file'.
+
+%% Help function building the internal configuration structure. Configurations
+%% in the start argument will override parameters found in a configuration file.
+fetch_configuration(Config) ->
+ case fetch_config_filename(Config) of
+ {ok,FName} -> % We are supposed to use a conf-file.
+ case read_config_file(FName) of
+ {ok,LD} -> % Managed to open a file.
+ NewLD=read_config_list(LD,Config),
+ {ok,NewLD};
+ Error = {error,_Reason} -> % Problem finding/opening file.
+ Error
+ end;
+ false -> % No filename specified.
+ LD=read_config_list(#ld{},Config),
+ {ok,LD}
+ end.
+
+%% Help function determining the name of the file which shall be consulted as
+%% the main configuration file.
+%% Returns {ok,FileName} or 'false'. The latter if no name could be determined.
+fetch_config_filename(Config) ->
+ case catch lists:keysearch(config_file,1,Config) of
+ {value,{_,FName}} when is_list(FName) ->
+ {ok,FName};
+ _ -> % No filename in the start argument.
+ fetch_config_filename_2()
+ end.
+
+fetch_config_filename_2() ->
+ case application:get_env(inviso_tool_config_file) of
+ {ok,FName} when is_list(FName) ->
+ {ok,FName};
+ _ -> % Application parameter not specified.
+ false % Means no config file will be used.
+ end.
+
+%% Help function reading the configuration file. Returns a #conf or {error,Reason}.
+read_config_file(FName) ->
+ case catch file:consult(FName) of
+ {ok,Terms} ->
+ {ok,read_config_list(#ld{},Terms)};
+ {error,Reason} ->
+ {error,{file_consult,Reason}};
+ {'EXIT',Reason} ->
+ {error,{failure,Reason}}
+ end.
+
+%% Help function traversing the Terms list entering known tag-values into #ld.
+read_config_list(LD,Terms) ->
+ LD#ld{
+ nodes = case mk_nodes(proplists:get_value(nodes,Terms,LD#ld.nodes)) of
+ {ok,Nodes} -> Nodes;
+ _ -> LD#ld.nodes
+ end,
+ c_node = proplists:get_value(c_node,Terms,LD#ld.c_node), % atom8)
+ regexp_node = proplists:get_value(regexp_node,Terms,LD#ld.regexp_node), % atom()
+ tc_def_file = proplists:get_value(tc_def_file,Terms,LD#ld.tc_def_file),
+ tdg = proplists:get_value(tdg,Terms,LD#ld.tdg),
+ debug = proplists:get_value(debug,Terms,LD#ld.debug),
+ initial_tcs = proplists:get_value(initial_tcs,Terms,LD#ld.initial_tcs),
+ dir = proplists:get_value(dir,Terms,LD#ld.dir),
+ optg = proplists:get_value(optg,Terms,LD#ld.optg)
+ }.
+
+%% -----------------------------------------------------------------------------
+
+
+%% Help function which, if it exists, consults the trace definition file. The
+%% idea behind the trace definition file is to point out which trace cases there
+%% are, where to find them and how to turn them on and off.
+%% Trace case definitions are:
+%% {TCname,Type,VariableNameList,ActivatioFileName} |
+%% {TCname,Type,VariableNameList,ActivationFileName,DeactivationFileName}
+%% TCname=atom()
+%% Type=on | on_off
+%% VariableNameList=[atom(),...]
+%% ActivationFileName=DeactivationFileName=string()
+read_trace_case_definitions(LD) ->
+ case LD#ld.tc_def_file of
+ TCfileName when is_list(TCfileName) ->
+ case catch file:consult(TCfileName) of
+ {ok,Terms} ->
+ Dir=LD#ld.dir, % The working directory of the tool.
+ TCdict=read_trace_case_definitions_2(Terms,Dir,mk_tc_dict()),
+ LD#ld{tc_dict=TCdict};
+ _ ->
+ LD
+ end;
+ _ ->
+ LD
+ end.
+
+read_trace_case_definitions_2([{TCname,on,VarNames,FName}|Rest],Dir,TCdict) ->
+ FileName=make_absolute_path(FName,Dir),
+ read_trace_case_definitions_2(Rest,
+ Dir,
+ insert_tracecase_tc_dict(TCname,
+ on,
+ VarNames,
+ FileName,
+ TCdict));
+read_trace_case_definitions_2([{TCname,on_off,VarNames,FNameOn,FNameOff}|Rest],Dir,TCdict) ->
+ FileNameOn=make_absolute_path(FNameOn,Dir),
+ FileNameOff=make_absolute_path(FNameOff,Dir),
+ read_trace_case_definitions_2(Rest,
+ Dir,
+ insert_tracecase_tc_dict(TCname,
+ on_off,
+ VarNames,
+ FileNameOn,
+ FileNameOff,
+ TCdict));
+read_trace_case_definitions_2([_|Rest],Dir,TCdict) ->
+ read_trace_case_definitions_2(Rest,Dir,TCdict);
+read_trace_case_definitions_2([],_Dir,TCdict) ->
+ TCdict.
+
+%% Help function returning an absolute path to FName if FName is not already
+%% absolute. Dir is the working dir of the tool and supposed to be absolute.
+make_absolute_path(FName,Dir) ->
+ case filename:pathtype(FName) of
+ absolute -> % Then do nothing, allready absolute.
+ FName;
+ _ ->
+ filename:join(Dir,FName)
+ end.
+%% -----------------------------------------------------------------------------
+
+get_status(undefined,_Node) ->
+ inviso:get_status();
+get_status(CNode,Nodes) ->
+ inviso_tool_lib:inviso_cmd(CNode,get_status,[Nodes]).
+%% -----------------------------------------------------------------------------
+
+
+%% =============================================================================
+%% Internal data structure functions.
+%% =============================================================================
+
+%% -----------------------------------------------------------------------------
+%% The nodes database structure.
+%% -----------------------------------------------------------------------------
+
+%% The purpose of the nodes database structure is to keep track of what runtime
+%% nodes we have, and their current status.
+%% Implementation:
+%% [{NodeName,AvailableStatus},...] or AvailableStatus in the
+%% non-distributed case.
+%% AvailableStatus={up,Status1} | down
+%% Status1={State,Status} | reactivating
+%% State=tracing | inactive | trace_failure
+%% Status=running | suspended
+%% reactivating=the node is now being brought up to date.
+%% inactive=not tracing, can be initiated and then reactivated.
+%% The following states can occure.
+%% {inactive,running}
+%% Mainly when we start the tool, before a session has been started.
+%% {tracing,running}
+%% When a trace session is on-going.
+%% {trace_failure,running}
+%% If init_tracing failed for some reason.
+%% {tracing,suspended}
+%% reactivating
+%% The node is tracing (has always been) but was suspended. It is now
+%% no longer suspended and the tool is redong commands.
+%% {inactive,suspended}
+%% We can end up here if a session is stopped with this node suspended.
+
+%% Returns a nodes database structure filled with the nodes Nodes.
+mk_nodes(Nodes) when is_list(Nodes) ->
+ {ok,lists:map(fun(N) when is_atom(N)->{N,down} end,Nodes)};
+mk_nodes(local_runtime) -> % The non-distributed case.
+ down;
+mk_nodes(_Nodes) ->
+ error.
+%% -----------------------------------------------------------------------------
+
+%% Updates the nodes database structure for each node that has been added.
+%% This is the case when we start the tool or reactivate a node. Note that a node
+%% may have become adopted instead of started.
+%% Returns a new nodes database structure.
+update_added_nodes(CNode,[{Node,NodeResult}|Rest],NodesD) ->
+ case update_added_nodes_3(NodeResult) of
+ already_added -> % Already added to the control component.
+ case get_status(CNode,[Node]) of % Examine if it is tracing or not.
+ {ok,[{Node,NodeResult2}]} ->
+ Result=mk_nodes_state_from_status(NodeResult2),
+ update_added_nodes_2(CNode,Node,Result,NodesD,Rest);
+ {error,_Reason} -> % Strange, mark it as down now.
+ update_added_nodes_2(CNode,Node,down,NodesD,Rest)
+ end;
+ Result ->
+ update_added_nodes_2(CNode,Node,Result,NodesD,Rest)
+ end;
+update_added_nodes(_CNode,[],NodesD) ->
+ NodesD;
+update_added_nodes(_CNode,NodeResult,_NodesD) -> % Non distributed case.
+ case update_added_nodes_3(NodeResult) of
+ already_added -> % Already added, most likely autostart.
+ mk_nodes_state_from_status(inviso:get_status());
+ Result ->
+ Result % Simply replace NodesD.
+ end.
+
+update_added_nodes_2(CNode,Node,Result,NodesD,Rest) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,_} -> % Node already exists, replace!
+ update_added_nodes(CNode,Rest,lists:keyreplace(Node,1,NodesD,{Node,Result}));
+ false -> % Strange, unknown node!
+ update_added_nodes(CNode,Rest,NodesD)
+ end.
+
+update_added_nodes_3({ok,{adopted,tracing,running,_Tag}}) ->
+ {up,{tracing,running}};
+update_added_nodes_3({ok,{adopted,tracing,{suspended,_SReason},_Tag}}) ->
+ {up,{tracing,suspended}};
+update_added_nodes_3({ok,{adopted,_,running,_Tag}}) ->
+ {up,{inactive,running}};
+update_added_nodes_3({ok,{adopted,_,{suspended,_SReason},_Tag}}) ->
+ {up,{inactive,suspended}};
+update_added_nodes_3({ok,new}) ->
+ {up,{inactive,running}};
+update_added_nodes_3({ok,already_added}) ->
+ already_added; % This is an error value!
+update_added_nodes_3({error,_Reason}) ->
+ down.
+%% -----------------------------------------------------------------------------
+
+%% Function marking all nodes that, according to the returnvalue from init_tracing,
+%% now are successfully initiated as tracing and running. Note that nodes that
+%% does not fully respond 'ok' when init_tracing are marked as 'trace_failure'.
+%% Also note that we assume that the nodes must be running to have made it this far.
+%% A node can of course have become suspended in the process, but that node will
+%% be marked as suspended later when that inviso event message arrives to the tool.
+%% Returns {NewNodesD,Nodes} where Nodes are the nodes that actually got initiated
+%% as a result of the init_tracing call (judged from the LogResults).
+set_tracing_running_nodes(undefined,{ok,_LogResults},_AvailableStatus) -> % Non-distr. case.
+ {{up,{tracing,running}},local_runtime};
+set_tracing_running_nodes(undefined,{error,already_initiated},_) -> % Non-distributed case.
+ {mk_nodes_state_from_status(inviso:get_status()),[]}; % Ask it for its status.
+set_tracing_running_nodes(undefined,{error,_Reason},_) -> % Non-distributed case.
+ {down,[]}; % This is questionable!
+set_tracing_running_nodes(CNode,{ok,NodeResults},NodesD) ->
+ set_tracing_running_nodes_2(CNode,NodeResults,NodesD,[]).
+
+set_tracing_running_nodes_2(CNode,[{Node,{ok,_LogResults}}|Rest],NodesD,Nodes) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,_} ->
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{tracing,running}}}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,[Node|Nodes]);
+ false -> % Strange.
+ set_tracing_running_nodes_2(CNode,Rest,NodesD,Nodes)
+ end;
+set_tracing_running_nodes_2(CNode,[{Node,{error,already_initiated}}|Rest],NodesD,Nodes) ->
+ case get_status(CNode,[Node]) of % Then we must ask what it is doing now.
+ {ok,[{Node,NodeResult}]} ->
+ Result=mk_nodes_state_from_status(NodeResult),
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,Result}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
+ {error,_Reason} -> % Strange, mark it as down.
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,down}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes)
+ end;
+set_tracing_running_nodes_2(CNode,[{Node,{error,_Reason}}|Rest],NodesD,Nodes) ->
+ NewNodesD=lists:keyreplace(Node,1,NodesD,{Node,{up,{trace_failure,running}}}),
+ set_tracing_running_nodes_2(CNode,Rest,NewNodesD,Nodes);
+set_tracing_running_nodes_2(_CNode,[],NodesD,Nodes) ->
+ {NodesD,Nodes}. % New NodesD and nodes successfully initiated.
+
+%% -----------------------------------------------------------------------------
+
+%% Function updating Node in the NodesD structure and sets it to 'down'.
+%% Returns a new nodes structure.
+set_down_nodes(Node,[{Node,_}|Rest]) ->
+ [{Node,down}|Rest];
+set_down_nodes(Node,[NodeStruct|Rest]) ->
+ [NodeStruct|set_down_nodes(Node,Rest)];
+set_down_nodes(_,[]) ->
+ [];
+set_down_nodes(_,_) -> % Non-distributed case.
+ down. % One can argue if this can happend.
+%% -----------------------------------------------------------------------------
+
+%% Function updating Node in NodesD to now be suspended. Note that if the node is
+%% reactivating it must be moved to state tracing because that is what is doing.
+set_suspended_nodes(Node,[{Node,{up,reactivating}}|Rest]) ->
+ [{Node,{up,{tracing,suspended}}}|Rest];
+set_suspended_nodes(Node,[{Node,{up,{State,_}}}|Rest]) ->
+ [{Node,{up,{State,suspended}}}|Rest];
+set_suspended_nodes(Node,[NodesData|Rest]) ->
+ [NodesData|set_suspended_nodes(Node,Rest)];
+set_suspended_nodes(_Node,[]) -> % Hmm, strange why did we end up here?
+ [];
+set_suspended_nodes(_,{up,reactivating}) -> % Non-distributed case.
+ {up,{tracing,suspended}};
+set_suspended_nodes(_,{up,{State,_}}) ->
+ {up,{State,suspended}}.
+%% -----------------------------------------------------------------------------
+
+%% This function is called when reactivation is completed. Hence it moves the
+%% node to no longer suspended. Note this can mean that the node is either
+%% tracing or inactive. Reactivation is not allowed for a node have trace_failure.
+set_running_nodes(Node,NodesD) when is_list(NodesD) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,{_,AvailableStatus}} ->
+ lists:keyreplace(Node,1,NodesD,{Node,set_running_nodes_2(AvailableStatus)});
+ false -> % Very strange!
+ NodesD
+ end;
+set_running_nodes(_,NodesD) -> % The non-distributed case.
+ set_running_nodes_2(NodesD).
+
+set_running_nodes_2({up,reactivating}) ->
+ {up,{tracing,running}};
+set_running_nodes_2({up,{State,suspended}}) ->
+ {up,{State,running}}.
+%% -----------------------------------------------------------------------------
+
+%% Function marking node as now reactivating. That means it is not suspended
+%% any longer (and tracing), but still not part of the set of nodes which shall
+%% get all commands. Returns a new NodesD.
+set_reactivating_nodes(Node,[{Node,_}|Rest]) ->
+ [{Node,{up,reactivating}}|Rest];
+set_reactivating_nodes(Node,[NodesData|Rest]) ->
+ [NodesData|set_reactivating_nodes(Node,Rest)];
+set_reactivating_nodes(_,[]) ->
+ [];
+set_reactivating_nodes(_,{up,_}) -> % The non-distributed case.
+ {up,reactivating}.
+%% -----------------------------------------------------------------------------
+
+%% Function called when stop-tracing is done. That is all nodes in Nodes shall
+%% be inactive now. Note that an inactive node can still be suspended.
+%% Returns a new NodesD.
+set_inactive_nodes(_,{up,reactivating}) -> % Non-distributed case.
+ {up,{inactive,running}};
+set_inactive_nodes(_,{up,{_,Status}}) -> % Tracing or trace_failure.
+ {up,{inactive,Status}};
+set_inactive_nodes(_,down) ->
+ down;
+set_inactive_nodes([{Node,ok}|Rest],NodesD) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,{_,{up,reactivating}}} ->
+ set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,running}}}));
+ {value,{_,{up,{_,Status}}}} -> % Tracing or trace_failure.
+ set_inactive_nodes(Rest,lists:keyreplace(Node,1,NodesD,{Node,{up,{inactive,Status}}}));
+ _ -> % This should not happend.
+ set_inactive_nodes(Rest,NodesD)
+ end;
+set_inactive_nodes([{_Node,_Error}|Rest],NodesD) ->
+ set_inactive_nodes(Rest,NodesD);
+set_inactive_nodes([],NodesD) ->
+ NodesD.
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of all node names. Note that it can only be used in the
+%% distributed case.
+get_all_nodenames_nodes(NodesD) ->
+ lists:map(fun({Node,_})->Node end,NodesD).
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of all nodes that are up, tracing and running (not suspended),
+%% or 'void' in the non-distributed case. This is the list of nodes that shall get
+%% inviso commands.
+get_nodenames_running_nodes([{Node,{up,{tracing,running}}}|Rest]) ->
+ [Node|get_nodenames_running_nodes(Rest)];
+get_nodenames_running_nodes([{_Node,_}|Rest]) ->
+ get_nodenames_running_nodes(Rest);
+get_nodenames_running_nodes([]) ->
+ [];
+get_nodenames_running_nodes(_) ->
+ void. % When non distributed, N/A.
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of nodes that can be made to initiate tracing.
+get_inactive_running_nodes({up,{inactive,running}}) ->
+ local_runtime;
+get_inactive_running_nodes(NonDistributed) when not(is_list(NonDistributed)) ->
+ [];
+get_inactive_running_nodes([{Node,{up,{inactive,running}}}|Rest]) ->
+ [Node|get_inactive_running_nodes(Rest)];
+get_inactive_running_nodes([{_Node,_}|Rest]) ->
+ get_inactive_running_nodes(Rest);
+get_inactive_running_nodes([]) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of nodes that are currently tracing (not necessarily running).
+%% In the non-distributed case the status of the runtime component will be
+%% returned.
+%% Note that nodes showing trace_failure will be included since we like to stop
+%% tracing at those nodes too.
+get_tracing_nodes([{Node,{up,{tracing,_}}}|Rest]) ->
+ [Node|get_tracing_nodes(Rest)];
+get_tracing_nodes([{Node,{up,{trace_failure,_}}}|Rest]) ->
+ [Node|get_tracing_nodes(Rest)];
+get_tracing_nodes([{Node,{up,reactivating}}|Rest]) ->
+ [Node|get_tracing_nodes(Rest)];
+get_tracing_nodes([_|Rest]) ->
+ get_tracing_nodes(Rest);
+get_tracing_nodes([]) ->
+ [];
+get_tracing_nodes(AvailableStatus) ->
+ AvailableStatus.
+%% -----------------------------------------------------------------------------
+
+%% Returns a list of all nodes that are currently up.
+get_available_nodes(down) ->
+ undefined;
+get_available_nodes([{_Node,down}|Rest]) ->
+ get_available_nodes(Rest);
+get_available_nodes([{Node,_}|Rest]) ->
+ [Node|get_available_nodes(Rest)];
+get_available_nodes([]) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Function returning the "state" of Node. Mainly used to check if the node is
+%% suspended or not.
+%% Returns {State,Status} | reactivating | down
+%% where
+get_state_nodes(Node,NodesD) when is_list(NodesD) ->
+ case lists:keysearch(Node,1,NodesD) of
+ {value,{_,AvailableStatus}} ->
+ get_state_nodes_2(AvailableStatus);
+ false ->
+ false
+ end;
+get_state_nodes(_,NodesD) -> % Non distributed case.
+ get_state_nodes_2(NodesD).
+
+get_state_nodes_2({up,{trace_failure,Status}}) ->
+ {trace_failure,Status};
+get_state_nodes_2({up,{State,suspended}}) -> % {tracing|inactive,suspended}
+ {State,suspended};
+get_state_nodes_2({up,reactivating}) ->
+ reactivating;
+get_state_nodes_2({up,{State,running}}) ->
+ {State,running};
+get_state_nodes_2(down) ->
+ down.
+%% -----------------------------------------------------------------------------
+
+%% Help function in the case we need to consult the state/status of a runtime
+%% component. Returns a nodesD value that can be added to the nodes database.
+mk_nodes_state_from_status({ok,{tracing,running}}) ->
+ {up,{tracing,running}};
+mk_nodes_state_from_status({ok,{tracing,{suspended,_SReason}}}) ->
+ {up,{tracing,suspended}};
+mk_nodes_state_from_status({ok,{_,running}}) ->
+ {up,{inactive,running}};
+mk_nodes_state_from_status({ok,{_,{suspended,_SReason}}}) ->
+ {up,{inactive,suspended}};
+mk_nodes_state_from_status({error,_Reason}) ->
+ down.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% The session_state.
+%% -----------------------------------------------------------------------------
+
+%% The session state reflects if the inviso_tool is tracing or not.
+%% This means that if the tool is tracing a reconnected node can be made to
+%% restart_session.
+
+%% Returns the correct value indicating that we are tracing now.
+tracing_sessionstate() ->
+ tracing.
+%% -----------------------------------------------------------------------------
+
+%% Returns true or false depending on if we are tracing now or not.
+is_tracing(tracing) ->
+ true;
+is_tracing(_) ->
+ false.
+%% -----------------------------------------------------------------------------
+
+%% Returns the correct value indicating that the tool is not tracing.
+passive_sessionstate() ->
+ idle.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% The tracer_data datastructure.
+%% -----------------------------------------------------------------------------
+
+%% The tracer_data structure collects the tracer data arguments used to init tracing
+%% by this inviso tool. The args are saved per session. Each session has
+%% a number.
+%% Implementation:
+%% Sessions=[{SessionNr,TDGargs},...]
+%% SessionNr=integer()
+%% TDGargs=list(), args given to the tracer data generator
+%% minus the first argument which is the Node name.
+
+%% Function taking tracerdata args structure inserting yet another session.
+%% Returns {SessionNr,NewTDs}.
+insert_td_tracer_data(TDGargs,TDs=[{SNr,_}|_]) ->
+ {SNr+1,[{SNr+1,TDGargs}|TDs]};
+insert_td_tracer_data(TDGargs,undefined) ->
+ {1,[{1,TDGargs}]}.
+%% -----------------------------------------------------------------------------
+
+%% Returns the latest session nr.
+get_latest_session_nr_tracer_data(undefined) ->
+ undefined;
+get_latest_session_nr_tracer_data([{SessionNr,_}|_]) ->
+ SessionNr.
+%% -----------------------------------------------------------------------------
+
+%% Returns the tracer data arguments used when creating the trace data for the
+%% latest session.
+get_latest_tdgargs_tracer_data(undefined) ->
+ undefined;
+get_latest_tdgargs_tracer_data([{_,TDGargs}|_]) ->
+ TDGargs.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% The tc_dict or trace case dictionary datastructure.
+%% -----------------------------------------------------------------------------
+
+%% The tc_dict stores information about all available trace cases.
+%% Implementation:
+%% [{TCname,Type,VarNames,FNameOn [,FNameOff]},...]
+%% TCname=atom()
+%% Type=on | on_off
+%% VarNames=[atom(),...]
+%% FNameOn=FNameOff=string()
+
+%% Returns the empty trace case dictionary.
+mk_tc_dict() ->
+ [].
+%% -----------------------------------------------------------------------------
+
+%% Function inserting a new trace case into the trace case dictionary.
+insert_tracecase_tc_dict(TCname,on,VarNames,FNameOn,TCdict) ->
+ [{TCname,on,VarNames,FNameOn}|TCdict].
+insert_tracecase_tc_dict(TCname,on_off,VarNames,FNameOn,FNameOff,TCdict) ->
+ [{TCname,on_off,VarNames,FNameOn,FNameOff}|TCdict].
+%% -----------------------------------------------------------------------------
+
+%% Function finding a trace case definition in the tc_dict structure.
+%% Returns {ok,{TCname,Type,VarNAmes,FNameOn [,FNameOff]}} or 'false'.
+get_tracecase_tc_dict(TCname,[Tuple|_]) when element(1,Tuple)==TCname ->
+ {ok,Tuple};
+get_tracecase_tc_dict(TCname,[_|Rest]) ->
+ get_tracecase_tc_dict(TCname,Rest);
+get_tracecase_tc_dict(_,[]) ->
+ false;
+get_tracecase_tc_dict(_,_) -> % There are no trace cases!
+ false.
+%% -----------------------------------------------------------------------------
+
+%% Function working on the trace case definition returned by get_tracecase_tc_dict/2
+%% function.
+%% Returning {ok,ActivationFileName}.
+get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn}) ->
+ {ok,FNameOn};
+get_tc_activate_fname({_TCname,_Type,_VarNames,FNameOn,_FNameOff}) ->
+ {ok,FNameOn}.
+
+get_tc_deactivate_fname({_TCname,_Type,_VarNames,_FNameOn,FNameOff}) ->
+ {ok,FNameOff};
+get_tc_deactivate_fname(_) -> % Not a case with off function.
+ false.
+
+get_tc_varnames({_TCname,_Type,VarNames,_FNameOn}) ->
+ VarNames;
+get_tc_varnames({_TCname,_Type,VarNames,_FNameOn,_FNameOff}) ->
+ VarNames.
+
+%% -----------------------------------------------------------------------------
+
+
+%% The Command History Log (CHL) stores commands to make it possible to
+%% reactivate suspended nodes, reconnect restarted nodes, and to make
+%% autostart files.
+%% Each time tracing is initiated (that is started) the CHL is cleared since
+%% it would not make scense to repeat commands from an earlier tracing at
+%% reactivation for instance.
+
+%% Implementation: {NextCounter,OnGoingList,ETStable}
+%% NextCounter=integer(), next command number - to be able to sort them in order.
+%% OnGoingList=[{ProcH,{TCname,ID}},...]
+%% ID=term(), instance id for this execution of this trace case.
+%% ETStable=tid() -> {{TCname,Id},Counter,State1,Bindings}
+%% ETStable=tid() -> {{TCname,Id},Counter,running,Bindings,Result} |
+%% {{TCname,Id,#Ref},Counter,stop,Bindings} |
+%% {{TCname,#Ref},Counter,Bindings} % An rtc
+%% {{M,F,Args,#Ref},Counter}
+%% Counter=integer(), the order-counter for this logged entry.
+%% State1=activating | stopping
+%% Where:
+%% activating: the activation file for the tracecase is running.
+%% running : activation is completed.
+%% stopping : set on the previously running ETS entry when deactivation
+%% file is currently executing.
+%% stop : entered with own Counter into the ETS table when
+%% deactivation file is executing. Remains after too.
+%% Result=term(), the result returned from the tr-case or inviso call.
+
+
+%% Returning an initial empty CHL.
+mk_chl(undefined) ->
+ {1,[],ets:new(inviso_tool_chl,[set,protected])};
+mk_chl({_,_,TId}) ->
+ ets:delete(TId),
+ mk_chl(undefined).
+
+%% Help function returning 'true' if there is a current history.
+history_exists_chl(undefined) ->
+ false;
+history_exists_chl({_,_,_}) ->
+ true.
+
+%% Function looking up the state of this trace case.
+find_id_chl(TCname,Id,{_NextCounter,_OnGoingList,TId}) ->
+ case ets:lookup(TId,{TCname,Id}) of
+ [{_,_,running,Bindings,_Result}] -> % The trace case is tracing.
+ {ok,Bindings};
+ [{_,_,State,_}] -> % activating or stopping.
+ State;
+ [] ->
+ false
+ end.
+
+%% Function finding the Trace case associated with a process handle
+%% doing this trace case's activation or stopping.
+find_tc_executer_chl(ProcH,{_,OnGoingList,TId}) ->
+ case lists:keysearch(ProcH,1,OnGoingList) of
+ {value,{_,{TCname,Id}}} ->
+ [{_,_,State,_}]=ets:lookup(TId,{TCname,Id}),
+ {State,{TCname,Id}}; % Should be activating or stopping.
+ false ->
+ false
+ end.
+
+%% Adds a Trace case to the CHL. This is done when it is turned on. Or when it
+%% is called for trace cases that do not have on/off functionality.
+set_activating_chl(TCname,Id,{Counter,OnGoingList,TId},Bindings,ProcH) ->
+ ets:insert(TId,{{TCname,Id},Counter,activating,Bindings}),
+ {Counter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
+
+%% Function marking a trace case as now running. That is the activation
+%% phase is completed. It is normaly completed when the process executing
+%% the trace case signals that it is done.
+set_running_chl(ProcH,TCname,Id,Result,{NextCounter,OnGoingList,TId}) ->
+ [{_,Counter,_,Bindings}]=ets:lookup(TId,{TCname,Id}),
+ ets:insert(TId,{{TCname,Id},Counter,running,Bindings,Result}),
+ NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
+ {NextCounter,NewOnGoingList,TId}.
+
+%% Function marking trace case TCname with identifier Id as now in its stopping
+%% state. Where ProcH is the handler to the process running the stopping
+%% trace case.
+set_stopping_chl(TCname,Id,{NextCounter,OnGoingList,TId},ProcH)->
+ [{_,Counter,_,Bindings,_}]=ets:lookup(TId,{TCname,Id}),
+ ets:insert(TId,{{TCname,Id},Counter,stopping,Bindings}),
+ ets:insert(TId,{{TCname,Id,make_ref()},NextCounter,stop,Bindings}),
+ {NextCounter+1,[{ProcH,{TCname,Id}}|OnGoingList],TId}.
+
+%% Function removing a TCname-Id from the CHL. This is mostly used
+%% if activating the trace case failed for some reason. We do not then
+%% expect the user to stop the trace case. Hence it must be removed now.
+%% A reactivation process may have noticed the activating-entry and started
+%% to activate it. But since the general state reached after an unsuccessful
+%% activation can not easily be determined, we don't try to do much about it.
+del_tc_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
+ ets:delete(TId,{TCname,Id}),
+ NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
+ {NextCounter,NewOnGoingList,TId}.
+
+%% Function removing the entry TCname+Id from the CHL. This makes it
+%% possible to activate a tracecase with this id again. The entry was
+%% previously marked as stopping.
+nullify_chl(ProcH,TCname,Id,{NextCounter,OnGoingList,TId}) ->
+ ets:delete(TId,{TCname,Id}),
+ NewOnGoingList=lists:keydelete(ProcH,1,OnGoingList),
+ {NextCounter+1,NewOnGoingList,TId}.
+
+%% Function stopping all processes saved as being now running tc executers.
+%% This is useful as cleanup during stop tracing for instance.
+%% Returns a new CHL which is not in all parts correct. Entries in the
+%% ETS table are for instance not properly state-changed. But the CHL will
+%% from now on only be used to create command files and similar.
+stop_all_tc_executer_chl({NextCounter,[{ProcH,_}|Rest],TId}) ->
+ exit(ProcH,kill),
+ stop_all_tc_executer_chl({NextCounter,Rest,TId});
+stop_all_tc_executer_chl({NextCounter,[],TId}) ->
+ {NextCounter,[],TId}.
+
+%% Function adding a "plain" inviso call to the CHL.
+add_inviso_call_chl(Cmd,Args,{NextCounter,OnGoingList,TId}) ->
+ ets:insert(TId,{{inviso,Cmd,Args,make_ref()},NextCounter}),
+ {NextCounter+1,OnGoingList,TId}.
+
+%% Function adding a run trace case entry to the chl.
+add_rtc_chl(TCname,Bindings,{NextCounter,OnGoingList,TId}) ->
+ ets:insert(TId,{{TCname,make_ref()},NextCounter,Bindings}),
+ {NextCounter+1,OnGoingList,TId}.
+%% Returns the highest used counter number in the command history log.
+get_highest_used_counter_chl({NextCounter,_,_}) ->
+ NextCounter-1.
+
+%% Help function returning a list of {{TCname,Id},Phase} for all ongoing
+%% assynchronous tracecases.
+get_ongoing_chl(undefined) ->
+ [];
+get_ongoing_chl({_,OngoingList,TId}) ->
+ get_ongoing_chl_2(OngoingList,TId).
+
+get_ongoing_chl_2([{_ProcH,{TCname,Id}}|Rest],TId) ->
+ case ets:lookup(TId,{TCname,Id}) of
+ [{_,_C,activating,_B}] ->
+ [{{TCname,Id},activating}|get_ongoing_chl_2(Rest,TId)];
+ [{_,_C,stopping,_B}] ->
+ [{{TCname,Id},deactivating}|get_ongoing_chl_2(Rest,TId)]
+ end;
+get_ongoing_chl_2([],_) ->
+ [].
+
+%% Function returning a list of log entries. Note that the list is unsorted
+%% in respect to Counter.
+get_loglist_chl({_,_,TId}) ->
+ L=ets:tab2list(TId),
+ lists:map(fun({{TC,Id},C,S,B,_Result}) -> {{TC,Id},C,S,B}; % running
+ (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
+ (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
+ (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple;
+ (Tuple={{_TC,_Ref},_C,_B}) -> Tuple
+ end,
+ L);
+get_loglist_chl(_) -> % The history is not initiated, ever!
+ [].
+
+%% Function returning a list of log entries, but only those which are not
+%% cancelled out by deactivations.
+% get_loglist_active_chl({_,_,TId}) ->
+% L=ets:tab2list(TId),
+% lists:zf(fun({{TC,Id},C,S,B,_Result}) -> {true,{{TC,Id},C,S,B}}; % running
+% (Tuple={{_TC,_Id},_C,_S,_B}) -> Tuple; % activating | stopping
+% (Tuple={{_TC,_Id,_Ref},_C,_S,_B}) -> Tuple; % stop
+% (Tuple={{_M,_F,_Args,_Ref},_C}) -> Tuple
+% end,
+% L);
+% get_loglist_chl(_) -> % The history is not initiated, ever!
+% [].
+
+
+%% This helpfunction recreates a history from a saved history list. This function
+%% is supposed to crash if the log is not well formatted. Note that we must restore
+%% the counter in order for the counter to work if new commands are added to the
+%% history.
+replace_history_chl(OldCHL,SortedLog) ->
+ {_,Ongoing,TId}=mk_chl(OldCHL),
+ {NewTId,Counter}=replace_history_chl_2(TId,SortedLog,0),
+ {ok,{Counter+1,Ongoing,NewTId}}.
+
+replace_history_chl_2(TId,[{{TC,Id},C,running,B}|Rest],_Counter) ->
+ ets:insert(TId,{{TC,Id},C,running,B,undefined}),
+ replace_history_chl_2(TId,Rest,C);
+replace_history_chl_2(TId,[{{M,F,Args},C}|Rest],_Counter) ->
+ ets:insert(TId,{{M,F,Args,make_ref()},C}),
+ replace_history_chl_2(TId,Rest,C);
+replace_history_chl_2(TId,[{TC,C,B}|Rest],_Counter) ->
+ ets:insert(TId,{{TC,make_ref()},C,B}),
+ replace_history_chl_2(TId,Rest,C);
+replace_history_chl_2(TId,[],Counter) ->
+ {TId,Counter}.
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% Reactivators data structure.
+%% -----------------------------------------------------------------------------
+
+%% Function adding a new node-reactivatorpid pair to the reactivators structure.
+%% In this way we know which reactivators to remove if Node terminates, or when
+%% a node is fully updated when a reactivator is done.
+add_reactivators(Node,Pid,Reactivators) ->
+ [{Node,Pid}|Reactivators].
+
+%% Function removing a reactivator entry from the reactivators structure.
+del_reactivators(RPid,[{_Node,RPid}|Rest]) ->
+ Rest;
+del_reactivators(RPid,[Element|Rest]) ->
+ [Element|del_reactivators(RPid,Rest)];
+del_reactivators(_,[]) -> % This should not happend.
+ [].
+
+get_node_reactivators(RPid,Reactivators) ->
+ case lists:keysearch(RPid,2,Reactivators) of
+ {value,{Node,_}} ->
+ Node;
+ false -> % This should not happend.
+ false
+ end.
+
+%% Returns a list of list all nodes that are currently reactivating.
+get_all_nodes_reactivators([{Nodes,_Pid}|Rest]) ->
+ [Nodes|get_all_nodes_reactivators(Rest)];
+get_all_nodes_reactivators([]) ->
+ [].
+
+%% Function stopping all running reactivator processes. Returns a new empty
+%% reactivators structure. Note that this function does not set the state of
+%% Nodes. It must most often be set to running.
+stop_all_reactivators([{_Nodes,Pid}|Rest]) ->
+ exit(Pid,kill),
+ stop_all_reactivators(Rest);
+stop_all_reactivators([]) ->
+ []. % Returns an empty reactivators.
+
+%% Help function stopping the reactivator (if any) that reactivates Node.
+%% Returns a new list of reactivators structure.
+stop_node_reactivators(Node,[{Node,Pid}|Rest]) ->
+ exit(Pid,kill),
+ Rest;
+stop_node_reactivators(Node,[NodePid|Rest]) ->
+ [NodePid|stop_node_reactivators(Node,Rest)];
+stop_node_reactivators(_,[]) ->
+ [].
+%% -----------------------------------------------------------------------------
+
+
+%% -----------------------------------------------------------------------------
+%% Started initial trace cases data structure.
+%% -----------------------------------------------------------------------------
+
+%% This datastructure keeps information about ongoing trace cases started
+%% automatically at init_tracing. These must be automatically stopped when calling
+%% stop_tracing.
+
+add_initial_tcs(TCname,Id,StartedInitialTcs) ->
+ [{TCname,Id}|StartedInitialTcs].
+%% -----------------------------------------------------------------------------
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/inviso/src/inviso_tool_lib.erl b/lib/inviso/src/inviso_tool_lib.erl
index 20a8b509ae..7953acedd6 100644
--- a/lib/inviso/src/inviso_tool_lib.erl
+++ b/lib/inviso/src/inviso_tool_lib.erl
@@ -83,7 +83,7 @@ inviso_cmd(NodeName,Func,Args) ->
%% In the non-distributed case the singlenode_expansion will be returned.
expand_module_names(_Nodes,Mod={_,'_'},_) ->
{error,{faulty_regexp_combination,Mod}};
-expand_module_names(Nodes,{DirStr,ModStr},Opts) when list(DirStr), list(ModStr) ->
+expand_module_names(Nodes,{DirStr,ModStr},Opts) when is_list(DirStr), is_list(ModStr) ->
case expand_module_names_special_regexp(DirStr) of
{ok,NewDirStr} ->
case expand_module_names_special_regexp(ModStr) of
@@ -97,11 +97,11 @@ expand_module_names(Nodes,{DirStr,ModStr},Opts) when list(DirStr), list(ModStr)
end;
expand_module_names(_,'_',_Opts) -> % If we want to trace all modules
wildcard; % we shall not expand it.
-expand_module_names(_Nodes,Mod,_Opts) when atom(Mod) ->
+expand_module_names(_Nodes,Mod,_Opts) when is_atom(Mod) ->
module; % If it is an atom, no expansion.
expand_module_names(Nodes,"*",Opts) -> % Treat this as a reg.exp.
expand_module_names(Nodes,".*",Opts);
-expand_module_names(Nodes,ModStr,Opts) when list(ModStr) ->
+expand_module_names(Nodes,ModStr,Opts) when is_list(ModStr) ->
case expand_module_names_special_regexp(ModStr) of
{ok,NewModStr} ->
expand_module_names_2(Nodes,NewModStr,Opts);
@@ -115,7 +115,7 @@ expand_module_names_2(Nodes,ModStr,Opts) ->
case get_expand_regexp_at_opts(Opts) of
{ok,Node} -> % Expansion only at this node.
case inviso_rt_lib:expand_regexp([Node],ModStr,Opts) of
- [{Node,Modules}] when list(Modules) ->
+ [{Node,Modules}] when is_list(Modules) ->
{singlenode_expansion,Modules};
[{Node,_}] -> % Most likely badrpc.
{error,{faulty_node,Node}}
@@ -130,7 +130,7 @@ expand_module_names_2(Nodes,DirStr,ModStr,Opts) ->
case get_expand_regexp_at_opts(Opts) of
{ok,Node} -> % Expansion only at this node.
case inviso_rt_lib:expand_regexp([Node],DirStr,ModStr,Opts) of
- [{Node,Modules}] when list(Modules) ->
+ [{Node,Modules}] when is_list(Modules) ->
{singlenode_expansion,Modules};
[{Node,_}] -> % Most likely badrpc.
{error,{faulty_node,Node}}
@@ -186,12 +186,12 @@ make_patterns(Catches,Opts,Dbg,NodeModsOrMods,F,A,MS) ->
make_patterns_2(Catches,OwnArg,Dbg,NodeModsOrMods,F,A,MS)
end.
-make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when list(Mods) ->
+make_patterns_2(Catches,OwnArg,Dbg,[{Node,Mods}|Rest],F,A,MS) when is_list(Mods) ->
TPs=make_patterns_3(Catches,OwnArg,Dbg,Mods,F,A,MS,[]),
[{Node,join_patterns(TPs)}|make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS)];
make_patterns_2(Catches,OwnArg,Dbg,[{_Node,_}|Rest],F,A,MS) -> % badrpc!?
make_patterns_2(Catches,OwnArg,Dbg,Rest,F,A,MS);
-make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when list(Modules) ->
+make_patterns_2(Catches,OwnArg,Dbg,Modules,F,A,MS) when is_list(Modules) ->
TPs=make_patterns_3(Catches,OwnArg,Dbg,Modules,F,A,MS,[]),
join_patterns(TPs);
make_patterns_2(_,_,_,[],_,_,_) ->
@@ -330,7 +330,7 @@ get_datetime_from_tdg_args([DateTime|_]) ->
%% Returns a list.
get_ownarg_opts(Opts) ->
case lists:keysearch(arg,1,Opts) of
- {value,{_,OwnArg}} when list(OwnArg) ->
+ {value,{_,OwnArg}} when is_list(OwnArg) ->
OwnArg;
{value,{_,OwnArg}} ->
[OwnArg];
@@ -350,7 +350,7 @@ get_disable_safety_opts(Opts) ->
get_expand_regexp_at_opts(Opts) ->
case lists:keysearch(expand_only_at,1,Opts) of
- {value,{_,Node}} when atom(Node) ->
+ {value,{_,Node}} when is_atom(Node) ->
{ok,Node};
_ ->
false
diff --git a/lib/inviso/src/inviso_tool_sh.erl b/lib/inviso/src/inviso_tool_sh.erl
index fe876b955a..b02f498c5b 100644
--- a/lib/inviso/src/inviso_tool_sh.erl
+++ b/lib/inviso/src/inviso_tool_sh.erl
@@ -1,1731 +1,1749 @@
-%%%------------------------------------------------------------------------------
-%%% File : inviso_tool_sh.erl
-%%% Author : Lennart �hman <[email protected]>
-%%% Description :
-%%%
-%%% Created : 24 Oct 2005 by Lennart �hman
-%%%------------------------------------------------------------------------------
--module(inviso_tool_sh).
-
-%% Inviso Session Handler.
-%% This is the code for the session handler process. Its purpose is that we have
-%% one session handler process for each trace session started through the
-%% start_session inviso tool API. The session handler process is responsible for:
-%%
-%% -Knowing the state/status of all participating runtime components.
-%% -Keeping storage of all tracerdata all our participants have used. This means
-%% also to find out the tracerdata of runtime components connecting by them
-%% selves.
-%%
-%% STORAGE STRATEGY
-%% ----------------
-%% The local information storage can be changed by two things. Either by executing
-%% commands issued through our APIs. Or by receiving trace_event from the control
-%% component. When we execute commands, a corresponding event will also follow.
-%% Meaning that in those situations we are informed twice.
-%% A simple strategy could be to wait for the event even when doing the changes
-%% to the runtime components our self (through commands). But that may result in
-%% a small time frame where someone might do yet another command and failing
-%% because the local information storage is not uptodate as it would have been
-%% expected to be. Therefore we always update the local storage when making changes
-%% to a runtime component our selves. There will eventually be a double update
-%% through an incoming event. But the storage must coop with that, preventing
-%% inconsitancies to happend. An example of a strategy is that the tracerdata table
-%% is a bag, not allowing for double entries of the same kind. Therefore a double
-%% update is harmless there.
-
-%% ------------------------------------------------------------------------------
-%% Module wide constants.
-%% ------------------------------------------------------------------------------
--define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
--define(TRACING,tracing). % A state defined by the control component.
--define(RUNNING,running). % A status according to control componet.
-
--define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% API exports.
-%% ------------------------------------------------------------------------------
--export([start_link/5,start_link/8]).
--export([cancel_session/1,stop_session/3]).
--export([reactivate/1,reactivate/2]).
--export([tpl/5,tpl/6,tpl/7,
- tf/2,tf/3,
- tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
- tpm_ms/7,ctpm_ms/6,ctpm/5
- ]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Internal exports.
-%% ------------------------------------------------------------------------------
--export([init/1,handle_call/3,handle_info/2,terminate/2]).
-
--export([get_loopdata/1]).
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Includes.
-%% ------------------------------------------------------------------------------
--include_lib("kernel/include/file.hrl"). % Necessary for file module.
-%% ------------------------------------------------------------------------------
-
-
-%% ==============================================================================
-%% Exported API functions.
-%% ==============================================================================
-
-%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
-%% {ok,Pid} | {error,Reason}
-%% From= pid(), the initial client expecting the reply.
-%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
-%% CtrlNode=atom() | 'void', the node where the trace control component is.
-%% CtrlPid=pid(), the pid of the trace control component.
-%% SafetyCatches=
-%% Dir=string(), where to place fetched logs and the merged log.
-%% Dbg=debug structure.
-%% NodesIn=[Node,...], list of nodes already in another session.
-%% NodesNotIn=[Node,...], list of nodes not in another session.
-%%
-%% Starts a session-handler. It keeps track of the the state and status of all
-%% participating runtime components. Note that there is a non-distributed case too.
-%% In the non-distributed case there is no things such as CtrlNode.
-start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
- gen_server:start_link(?MODULE,
- {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
- []).
-
-start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
- gen_server:start_link(?MODULE,
- {self(),From,NodeParams,CtrlNode,CtrlPid,
- SafetyCatches,Dbg,NodesIn,NodesNotIn},
- []).
-%% ------------------------------------------------------------------------------
-
-%% Stops tracing where it is ongoing. Fetches all logfiles.
-stop_session(SID,Dir,Prefix) ->
- gen_server:call(SID,{stop_session,Dir,Prefix}).
-%% ------------------------------------------------------------------------------
-
-%% stop_session(SID) = ok
-%%
-%% Cancels the session brutaly. All runtime components are made to stop tracing,
-%% all local log files are removed using the tracerdata we know for them.
-cancel_session(SID) ->
- gen_server:call(SID,cancel_session).
-%% ------------------------------------------------------------------------------
-
-%% reactivate(SID) = {ok,
-%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
-%% SID=session id, pid().
-%% Nodes=[Node,...]
-%% NodeResult=[{Node,Result},...]
-%% Result={Good,Bad}
-%% Good,Bad=integer(), the number of redone activities.
-%%
-%% Function which reactivates runtime components being suspended. This is done
-%% replaying all trace flags (in the correct order) to the corresponding nodes.
-%% Note that this may also mean turning flags off. Like first turning them on
-%% then off a split second later.
-reactivate(SID) ->
- gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
-reactivate(SID,Nodes) ->
- gen_server:call(SID,{reactivate,Nodes}).
-%% ------------------------------------------------------------------------------
-
-
-%% tpl(SessionID,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
-%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
-%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
-%% ModRegExp=DirRegExp= string()
-%% Func='_' | FunctionName
-%% Arity='_' | integer()
-%% MS=[] | false | a match specification
-%% Opts=[Opts,...]
-%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
-%% Nodes=[NodeName,...]
-tpl(SID,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
-tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
- gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
-tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
-tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
- gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
-%% ------------------------------------------------------------------------------
-
-%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
-%% See tpl/X for arguments.
-%%
-%% Removes local trace-patterns from functions.
-ctpl(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-tpm_localnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_localnames,Nodes}).
-tpm_globalnames(SID,Nodes) ->
- gen_server:call(SID,{tpm_globalnames,Nodes}).
-
-init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
- gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
-init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,
- {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
-tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
- gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
-
-tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
-
-ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
- gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
-
-ctpm(SID,Nodes,Mod,Func,Arity) ->
- gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
-%% ------------------------------------------------------------------------------
-
-
-%% tf(SessionID,Nodes,TraceConfList)=
-%% TraceConfList=[{PidSpec,Flags},...]
-%% PidSpec=pid()|atom()|all|new|existing
-%% Flags=[Flag,...]
-tf(SID,TraceConfList) ->
- gen_server:call(SID,{tf,TraceConfList}).
-tf(SID,Nodes,TraceConfList) ->
- gen_server:call(SID,{tf,Nodes,TraceConfList}).
-%% ------------------------------------------------------------------------------
-
-
-get_loopdata(SID) ->
- gen_server:call(SID,get_loopdata).
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Genserver call-backs.
-%% ==============================================================================
-
-%% Initial function for the session handler process. The nodes participating in
-%% the session must previously have been added to our control component by the tool.
-%% The session handler first finds out the state/status of the specified runtime
-%% components, then it tries to initiate tracing on those where it is applicable.
-%% Note that a reply to the initial (tool)client is done from here instead from
-%% the tool-server.
-init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
- {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
- case is_tool_internal_tracerdata(TracerData) of
- false -> % We shall initiate local runtime.
- case inviso:init_tracing(TracerData) of
- ok ->
- gen_server:reply(From,{ok,{self(),ok}}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)};
- {error,Reason} -> % It might have become suspended?!
- gen_server:reply(From,{error,Reason}),
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [{?LOCAL_RUNTIME,TracerData}],
- [],
- SafetyCatches,
- Dbg)}
- end;
- true -> % We shall not pass this one on.
- gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
- {ok,mk_ld(Parent,
- void,
- CtrlPid,
- to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
- [],
- [?LOCAL_RUNTIME],
- SafetyCatches,
- Dbg)}
- end;
-init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
- case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
- {ok,States} -> % A list of {Node,{State,Status},Opts}.
- {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
- case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
- {ok,Result} -> % Resulted in state changes!
- RTStates=set_tracing_rtstates(to_rtstates(States),Result),
- ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
- gen_server:reply(From,{ok,{self(),ReplyValue}}),
- {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
- NodeParams2,Nodes2,SafetyCatches,Dbg)};
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,{init_tracing,Reason}}),
- {stop,{init_tracing,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end;
- {error,Reason} -> % Unable to get the state/status.
- inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
- gen_server:reply(From,{error,Reason}),
- {stop,{error,Reason}};
- What ->
- io:format("GOT:~n~w~n",[What]),
- exit(foo)
- end.
-%% ------------------------------------------------------------------------------
-
-%% To stop a session means stop the tracing and remove all local files on the
-%% runtime nodes. We do have a table with all tracer data and that is how we are
-%% going to recreate what files to remove.
-%% Since runtime components may actually change state when this procedure is
-%% on-going, we do not care! It is the state in the session handling process at
-%% the time of start of this procedure which is used.
-handle_call(cancel_session,_From,LD) ->
- CtrlNode=get_ctrlnode_ld(LD),
- RTStates=get_rtstates_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok-> % Hopefully all nodes are stopped now.
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- TRDstorage=get_trdstorage_ld(LD),
- remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
- {stop,normal,ok,LD}; % LD actually not correct now!
- {error,Reason} -> % Some serious error when stop_tracing.
- {stop,normal,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% *Stop all tracing on runtime components still tracing.
-%% *Copy all local log files to the collection directory.
-handle_call({stop_session,Dir,Prefix},_From,LD) ->
- case check_directory_exists(Dir) of % Check that this directory exists here.
- true ->
- RTStates=get_rtstates_ld(LD),
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
- case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
- ok -> % Hopefully no node is still tracing now.
- TRDstorage=get_trdstorage_ld(LD),
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {FailedNodes,FetchedFiles}=
- transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
- TRDstorage,Dbg,AvailableNodes),
- RemoveNodes= % We only delete local logs where fetch ok.
- lists:filter(fun(N)->
- case lists:keysearch(N,1,FailedNodes) of
- {value,_} ->
- false;
- false ->
- true
- end
- end,
- AvailableNodes),
- remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
- {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
- {error,Reason} -> % Some general failure, quit.
- {stop,normal,{error,Reason},LD}
- end;
- false -> % You specified a non-existing directory!
- {reply,{error,{faulty_dir,Dir}},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-handle_call({reactivate,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- {OurNodes,OtherNodes}=
- remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
- CtrlNode=get_ctrlnode_ld(LD),
- ACTstorage=get_actstorage_ld(LD),
- case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
- {ok,Results} -> % A list of {Node,Result}.
- if
- OtherNodes==[] -> % Normal case, no non-session nodes.
- {reply,{ok,Results},LD};
- true -> % Add error values for non-session nodes.
- {reply,
- {ok,
- lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
- Results},
- LD}
- end;
- {error,Reason} -> % Then this error takes presidence.
- {reply,{error,Reason},LD}
- end;
-%% ------------------------------------------------------------------------------
-
-%% Call-back for set trace-pattern for both global and local functions.
-handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
- Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
- {reply,Reply,LD};
-handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
- {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
- Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
- {reply,ErrorReply++Reply,LD};
-%% ------------------------------------------------------------------------------
-
-%% Call-back handling the removal of both local and global trace-patterns.
-%% NOT IMPLEMENTED YET.
-handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
- Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
- {reply,Reply,LD};
-%% ------------------------------------------------------------------------------
-
-handle_call({tpm_localnames,Nodes},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- init_tpm,
- [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,CallFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm,
- [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- tpm_ms,
- [Mod,Func,Arity,MSname,MS],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),
- Nodes2,
- ctpm_ms,
- [Mod,Func,Arity,MSname],
- RTStates,
- ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-
-handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
- RTStates=get_rtstates_ld(LD),
- OurNodes=get_all_session_nodes_rtstates(RTStates),
- {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
- ACTstorage=get_actstorage_ld(LD),
- {Reply,NewACTstorage}=
- h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
- ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
- {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-%% Call-back for setting process trace-flags. Handles both distributed and non-
-%% distributed case.
-handle_call({tf,TraceConfList},From,LD) ->
- handle_call({tf,all,TraceConfList},From,LD);
-handle_call({tf,Nodes,TraceConfList},_From,LD) ->
- {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
- Nodes,
- TraceConfList,
- get_actstorage_ld(LD),
- get_rtstates_ld(LD)),
- {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
-%% ------------------------------------------------------------------------------
-
-
-
-handle_call(get_loopdata,_From,LD) ->
- io:format("The loopdata:~n~p~n",[LD]),
- {reply,ok,LD}.
-%% ------------------------------------------------------------------------------
-
-
-%% Clause handling an incomming state-change event from the control component.
-%% Note that it does not have to be one of our nodes since it is not possible
-%% to subscribe to certain node-events.
-%% We may very well get state-change events for state-changes we are the source
-%% to our selves. Those state-changes are already incorporated into the RTStates.
-%% There is however no harm in doing them again since we know that this event
-%% message will reach us before a reply to a potentially following state-change
-%% request will reach us. Hence we will do all state-changes in the correct order,
-%% even if sometimes done twice.
-handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case {State,Status} of
- {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
- NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
- Node,
- get_rtstates_ld(LD),
- get_trdstorage_ld(LD)),
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_trdstorage_ld(NewTracerData,
- put_rtstates_ld(NewRTStates,LD))};
- _ -> % In all other cases, just fix rtstates.
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)}
- end;
- _ ->
- {noreply,LD}
- end;
-%% If a new runtime component connects to our trace control component, and it is
-%% in our list of runtime components belonging to this session, we may update its
-%% state to now being present. Otherwise it does not belong to this session.
-%% Note that we avoid updating an already connected runtime component. This
-%% can happend if it connected by itself after we started the session handler,
-%% but before we managed to initiate tracing. Doing so or not will not result in
-%% any error in the long run, but during a short period of time we might be
-%% prevented from doing things with the runtime though it actually is tracing.
-handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
- {ok,unavailable} -> % This is the situation when we update!
- NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
- {noreply,put_rtstates_ld(NewRTStates,LD)};
- _ -> % In all other cases, let it be.
- {noreply,LD}
- end;
- _ -> % Not from our control component.
- {noreply,LD}
- end;
-%% If a runtime component disconnects we mark it as unavailable. We must also
-%% remove all saved trace-flags in order for them to not be accidently reactivated
-%% should the runtime component reconnect and then suspend.
-handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
- case get_ctrlpid_ld(LD) of
- CtrlPid -> % It is from our control component.
- NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
- NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
- {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
- _ ->
- {noreply,LD}
- end;
-handle_info(_,LD) ->
- {noreply,LD}.
-%% ------------------------------------------------------------------------------
-
-%% In terminate we cancel our subscription to event from the trace control component.
-%% That should actually not be necessary, but lets do it the correct way!
-terminate(_,LD) ->
- case get_ctrlnode_ld(LD) of
- void -> % Non-distributed.
- inviso:unsubscribe();
- Node ->
- inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
- end.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ==============================================================================
-%% First level help functions to call-backs.
-%% ==============================================================================
-
-%% ------------------------------------------------------------------------------
-%% Help functions to init.
-%% ------------------------------------------------------------------------------
-
-%% Help function which find out the state/status of the runtime components.
-%% Note that since we have just started subscribe to state changes we must
-%% check our inqueue to see that we have no waiting messages for the nodes
-%% we learned the state/status of. If there is a waiting message we don't
-%% know whether that was a state change received before or after the state
-%% check was done. We will then redo the state-check.
-%% Returns {ok,States} or {error,Reason}.
-%% Where States is [{Node,{State,Status},Opts},...].
-%% Note that {error,Reason} can not occur in the non-distributed case.
-init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
- ok=inviso:subscribe(),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
-init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
- ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
-
-init_rtcomponent_states_2(_,_,_,[],States) ->
- {ok,States};
-init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
- case inviso:get_status() of
- {ok,StateStatus} -> % Got its state/status, now...
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
- [],States),
- init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
- {error,_Reason} -> % The runtime is not available!?
- {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
- end;
-init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
- {ok,NodeResult} ->
- {ProblemNodes,NewStates}=
- init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
- init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
- {error,Reason} -> % Severe problem, abort the session.
- {error,{get_status,Reason}}
- end.
-
-%% Traverses the list of returnvalues and checks that we do not have an event
-%% waiting in the message queue. If we do have, it is a problem. That node will
-%% be asked about its state again.
-%% Note that it is here we construct the RTStatesList.
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
- receive
- {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
- after
- 0 -> % Not in msg queue, then we're safe!
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,{State,Status},Opts};
- _ -> % No option available, use [].
- {Node,{State,Status},[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
- end;
-init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
- RTState=case lists:keysearch(Node,1,NodeParams) of
- {value,{_Node,_TracerData,Opts}} ->
- {Node,unavailable,Opts};
- _ -> % No option available, use [].
- {Node,unavailable,[]}
- end,
- init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
-init_rtcomponent_states_3(_,_,[],Problems,States) ->
- {Problems,States}.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing nodes from NodeParams. The reason for this can either
-%% be that we are using a tool internal tracerdata that shall not be forwarded to
-%% the trace control component, or that the node is actually already part of
-%% another session.
-%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
-remove_nodeparams(Nodes,NodesParams) ->
- remove_nodeparams_2(Nodes,NodesParams,[],[]).
-
-remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
- (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
- Node=element(1,NodeParam),
- Params=element(2,NodeParam), % This is tracerdata!
- case lists:member(Node,Nodes) of
- true -> % Remove this one, in another session.
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
- false -> % Ok so far...
- case is_tool_internal_tracerdata(Params) of
- false -> % Then keep it and use it later!
- remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
- true -> % Since it is, remove it from the list.
- remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
- end
- end;
-remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
- remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
-remove_nodeparams_2(_,[],NPAcc,NAcc) ->
- {lists:reverse(NPAcc),NAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds both the nodes which were already part of another
-%% session and the nodes that we actually did not issue any init_tracing for.
-%% Returns a new Result list of [{Node,NodeResult},...].
-init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
- NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
- init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
-
-init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
- [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
-init_fix_resultnodes_2([],_,Result) ->
- Result. % Append Result to the end of the list.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to reactivate.
-%% ------------------------------------------------------------------------------
-
-h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
- {ok,CSuspResults} ->
- {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
- lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
- {[Node|GoodNs],BadNs};
- ({Node,{error,Reason}},{GoodNs,BadNs})->
- {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
- end,
- {[],[]},
- CSuspResults),
- Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
- {ok,BadResults++Results};
- {error,Reason} -> % General failure cancelling suspend.
- {error,{cancel_suspension,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses the list of nodes known to be ours and have
-%% cancelled their suspend. If we fail redoing one of the activities associated
-%% with a node, the node will be reported in the return value as failed. From
-%% that point on its state must be considered unknown since we do not know how
-%% many of the activities were successfully redone.
-h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
- case get_activities_actstorage(Node,ACTstorage) of
- {ok,Activities} -> % The node existed in activity storage.
- {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
- false -> % Node not present in activity storage.
- h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
- end;
-h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
- lists:reverse(Acc).
-
-%% Help function actually redoing the activity. Note that there must be one
-%% clause here for every type of activity.
-%% Returns {NrGoodCmds,NrBadCmds}.
-%% The number of good or bad commands refers to inviso commands done. If any
-%% of the subparts of such a command returned an error, the command is concidered
-%% no good.
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
- {ok,[{_Node,{ok,Answers}}]} ->
- case h_reactivate_redo_activity_check_tf(Answers) of
- ok ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- error -> % At least oneReports the first encountered error.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
- {ok,[{_Node,ok}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
- {ok,[{_Node,{error,_Reason}}]} ->
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
- {error,_Reason} -> % General error when doing cmd.
- h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
- end;
-h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
- {Good,Bad}.
-
-%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
-%% to see if there were any errors.
-h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
- h_reactivate_redo_activity_check_tf(Rest);
-h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
- error;
-h_reactivate_redo_activity_check_tf([]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions to tp (setting trace patterns, both local and global).
-%% ------------------------------------------------------------------------------
-
-%% Help function which handles both tpl and tp. Note that the non-distributed case
-%% handled with Nodes='all'.
-%% Returns what shall be the reply to the client.
-h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
- Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
- h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
-h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
- CtrlNode=get_ctrlnode_ld(LD),
- Dbg=get_dbg_ld(LD),
- SafetyCatches=get_safetycatches_ld(LD),
- case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
- {multinode_expansion,NodeMods} ->
- NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
- {singlenode_expansion,Modules} ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- module ->
- TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
- h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
- wildcard -> % Means do for all modules, no safety.
- h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
- {error,Reason} ->
- {error,Reason}
- end.
-
-%% Note that this function can never be called in the non-distributed case.
-h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
- case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
- {ok,[{Node,Result}]} ->
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
- {error,Reason} -> % Failure, but don't stop.
- h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
- end;
-h_tp_node_by_node(_,_,_,[],Accum) ->
- {ok,lists:reverse(Accum)}.
-
-%% Help function which does the actual call to the trace control component.
-%% Note that Nodes can be a list of nodes (including a single one) or
-%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
-%% detected by the 'void' CtrlNode.
-%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
-%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
-%% {error,Reason}.
-h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
- inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
- case inviso:PatternFunc(TPs) of
- {ok,Result} -> % A list of [Nr1,Nr2,error,...].
- {ok,
- lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
- {AccNr+N,AccErr};
- (error,{AccNr,AccErr}) ->
- {AccNr,AccErr+1}
- end,
- {0,0},
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end;
-h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
- inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
- case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
- {ok,Result} -> % Result is [{Node,Result},...].
- {ok,
- lists:map(fun({Node,{ok,Res}})->
- {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
- {ok,{AccNr+N,AccErr}};
- (error,{AccNr,AccErr}) ->
- {ok,{AccNr,AccErr+1}}
- end,
- {ok,{0,0}},
- Res)};
- ({_Node,{error,Reason}})->
- {error,Reason}
- end,
- Result)};
- {error,Reason} ->
- {error,{PatternFunc,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions for removing trace-patterns.
-%% ------------------------------------------------------------------------------
-
-%% NOT IMPLEMENTED YET.
-h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
- tbd.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for calling the trace information facility.
-%% ------------------------------------------------------------------------------
-
-
-%% Function handling the meta trace pattern for capturing registration of local
-%% process names.
-h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Functions calling meta trace functions for specified nodes. This function is
-%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
-%% ctpm.
-%% Note that we must store called meta trace functions and their parameters in the
-%% activity storage in order to be able to redo them in case of a reactivate.
-h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
- AvailableNodes=get_all_available_nodes_rtstates(RTStates),
- {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
- case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
- {ok,Result} -> % That good we want to modify tpmstorage!
- NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
- ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
- {{ok,ErrorResult++Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify storage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Help functions for set trace flags.
-%% ------------------------------------------------------------------------------
-
-%% Help function which sets the tracepatterns in TraceConfList for all nodes
-%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
-%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
-%% and NewACTstorage is traceflag storage modified with the flags added to the
-%% corresponding nodes.
-h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
- Reply=inviso:tf(TraceConfList),
- NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
- {Reply,NewACTstorage};
-h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
- AllNodes=get_all_session_nodes_rtstates(RTStates),
- h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
-h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
- {ok,Result} -> % That good we want to modify actstorage!
- NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
- {{ok,Result},NewACTstorage};
- {error,Reason} -> % If general failure, do not modify actstorage.
- {{error,Reason},ACTstorage}
- end.
-%% ------------------------------------------------------------------------------
-
-%% ------------------------------------------------------------------------------
-%% Help functions to stop_session.
-%% ------------------------------------------------------------------------------
-
-%% This function fetches all local log-files using our stored tracerdata. Note
-%% that there are two major ways of tranfering logfiles. Either via distributed
-%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
-%% But there may be info in the RTStates structure about a common file-system.
-%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
-%% nodenames where problems occurred. Note that problems does not necessarily
-%% mean that no files were copied.
-%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
-%% {ti_log,Files}, listing all files successfully fetched. Note that the
-%% list of fetched files contains sublists of filenames. One for each node and
-%% tracerdata.
-%% In the non-distributed system we always use copy (since the files always
-%% resides locally).
-transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
- if
- CtrlNode==void -> % When non-distributed, always copy!
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
- true -> % The distributed case.
- {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
- {FailedFetchNodes,FetchedFiles}=
- case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
- {ok,Failed,Files} -> % So far no disasters.
- {Failed,Files};
- {error,Reason} -> % Means all fetch-nodes failed!
- inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
- {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
- end,
- {FailedCopyNodes,CopiedFiles}=
- fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
- {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
- end.
-
-%% Help function which finds out which node we have a common file system with
-%% and from which we must make distributed erlang tranfere.
-%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
-find_logfile_transfer_methods(Nodes,RTStates) ->
- find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
-
-find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
- {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
- case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
- {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
- find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
- {value,_} -> % Can't understand dir option.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
- false -> % Then we want to use fetch instead.
- find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
- end;
-find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
- {FetchAcc,CopyAcc}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which transferes all local logfiles according to the tracerdata
-%% stored for the nodes in Nodes.
-%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
-%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
-%% or not at all.
-%% FileNames is a list of list of actually fetched files (the name as it is here, including
-%% Dir). The sublists are files which belong together.
-fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
- LogSpecList=build_logspeclist(Nodes,TRDstorage),
- case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
- fetch_log,
- [LogSpecList,Dir,Prefix])) of
- {ok,Result} ->
- Files=get_all_filenames_fetchlog_result(Result,Dbg),
- FailedNodes=get_all_failednodes_fetchlog_result(Result),
- {ok,FailedNodes,Files};
- {error,Reason} -> % Some general failure!
- {error,{fetch_log,Reason}}
- end.
-
-%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
-%% Note that there may be more than one tracerdata for a node, resulting in multiple
-%% tuples for that node.
-build_logspeclist(Nodes,TRDstorage) ->
- build_logspeclist_2(Nodes,TRDstorage,[]).
-
-build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
- build_logspeclist_2(Rest,
- TRDstorage,
- [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
-build_logspeclist_2([],_,Acc) ->
- lists:flatten(Acc).
-
-%% Help function which translates inviso:fetch_log return values to what I
-%% want!
-inviso_fetch_log({error,Reason}) ->
- {error,Reason};
-inviso_fetch_log({_Success,ResultList}) ->
- {ok,ResultList}.
-
-%% Help function which collects all filenames mentioned in a noderesult structure.
-%% The files may or may not be complete.
-%% Returns a list of list of filenames. Each sublist contains files which belong
-%% together, i.e because they are a wrap-set.
-get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
- get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
-
-get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
- when Success=/=error, list(FileInfo) ->
- SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
-get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
- inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
- get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
-get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
- Accum.
-
-get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
- FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
- get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
-get_all_filenames_fetchlog_result_3([],SubAccum) ->
- SubAccum.
-
-%% Help function which traverses a noderesult and builds a list as return
-%% value containing the nodenames of all nodes not being complete.
-%% Note that a node may occur multiple times since may have fetched logfiles
-%% for several tracerdata from the same node. Makes sure the list contains
-%% unique node names.
-%% Returns a list nodes.
-get_all_failednodes_fetchlog_result(NodeResult) ->
- get_all_failednodes_fetchlog_result_2(NodeResult,[]).
-
-get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
-get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
- case lists:member(Node,Acc) of
- true -> % Already in the list.
- get_all_failednodes_fetchlog_result_2(Rest,Acc);
- false -> % Not in Acc, add it!
- get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
- end;
-get_all_failednodes_fetchlog_result_2([],Acc) ->
- Acc.
-%% ------------------------------------------------------------------------------
-
-%% Help function which copies files from one location to Dir and at the same time
-%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
-%% reason the node information is still part of NodeSpecs is that otherwise we can
-%% not report faulty nodes. Note that one node may occur multiple times since there
-%% may be more than one tracerdata for a node.
-%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
-%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
-fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
- CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
-
-fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
- case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
- {0,LocalFiles} -> % Copy went ok and zero errors.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
- {_N,LocalFiles} -> % Copied files, but some went wrong.
- case lists:member(Node,FailedNodes) of
- true -> % Node already in FailedNodes.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
- [{Node,LocalFiles}|Files]);
- false -> % Node not marked as failed, yet.
- fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
- [{Node,LocalFiles}|Files])
- end
- end;
-fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
- {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
-
-fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
- {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
- fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
-fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
- {Errors,Results}.
-
-%% For each file of one file-type (e.g. trace_log).
-fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
- DestName=Prefix++filename:basename(File),
- Destination=filename:join(Dir,DestName),
- case do_copy_file(File,Destination) of
- ok ->
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
- {error,Reason} ->
- inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
- fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
- end;
-fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
- {Errors,LocalFiles}.
-
-%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
-%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
-%% is a complete path to a file to be copied.
-build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
- build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
-
-%% For each node specified in the NodeSpecList.
-build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
- TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
- CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
- build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
-build_copylist_2(_,_,[],_,Acc) ->
- lists:flatten(Acc).
-
-%% For each tracerdata found for the node.
-build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
- case inviso:list_logs(TRD) of
- {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
- {ok,no_log} -> % This tracedata not associated with any log.
- build_copylist_3(void,Dbg,SourceDir,Node,Rest);
- {error,Reason} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(void,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
- case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
- {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
- NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
- [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
- {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {ok,[{Node,{error,Reason}}]} ->
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
- {error,Reason} -> % Some general failure.
- inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
- build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
- end;
-build_copylist_3(_,_,_,_,[]) ->
- [].
-
-%% Help function which makes a [{Type,Files},...] list where each file in Files
-%% is with full path as found from our file-system.
-build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
- NewFiles=
- lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
- [],
- Files),
- build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
-build_copylist_4(_,[],Accum) ->
- Accum.
-
-
-%% Help function which copies a file using os:cmd.
-%% Returns 'ok' or {error,Reason}.
-do_copy_file(Source,Destination) ->
- case os:type() of
- {win32,_} ->
- os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
- ok;
- {unix,_} ->
- os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
- ok
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-
-%% ==============================================================================
-%% Various help functions.
-%% ==============================================================================
-
-%% Help function going through the Nodes list and checking that only nodes
-%% mentioned in OurNodes gets returned. It also makes the nodes in the return
-%% value unique.
-remove_nodes_not_ours(Nodes,OurNodes) ->
- remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
-
-remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
- case lists:member(Node,OurNodes) of
- true -> % Ok it is one of our nodes.
- case lists:member(Node,OurAcc) of
- true -> % Already in the list, skip.
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
- end;
- false ->
- case lists:member(Node,OtherAcc) of
- true ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
- false ->
- remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
- end
- end;
-remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
- {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns 'true' or 'false' depending on if TracerData is
-%% meant to be used by the session handler (true) or if it supposed to be passed
-%% on to the trace system.
-is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
- false.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that all nodes in the first list of nodes exists
-%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
-%% as one incorrect node was found.
-check_our_nodes([Node|Rest],AllNodes) ->
- case lists:member(Node,AllNodes) of
- true ->
- check_our_nodes(Rest,AllNodes);
- false -> % Then we can stop right here.
- false
- end;
-check_our_nodes([],_) ->
- true.
-%% ------------------------------------------------------------------------------
-
-%% Help function which checks that a directory actually exists. Returns 'true' or
-%% 'false'.
-check_directory_exists(Dir) ->
- case file:read_file_info(Dir) of
- {ok,#file_info{type=directory}} ->
- true;
- _ -> % In all other cases it is not valid.
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
-%% of only tracing runtime components. Not that there will actually be any difference
-%% since the return value does not reflect how stopping the nodes went.
-%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
-stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
- case inviso:stop_tracing() of
- {ok,_State} ->
- ok;
- {error,Reason} -> % We actually don't care.
- inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
- ok
- end;
-stop_all_tracing(void,_,_) -> % There is no local runtime started.
- ok;
-stop_all_tracing(CtrlNode,Dbg,Nodes) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
- {ok,Result} -> % The result is only used for debug.
- Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
- (_,Acc)->Acc
- end,
- [],
- Result),
- if
- Failed==[] ->
- ok;
- true ->
- inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
- ok
- end;
- {error,Reason} ->
- {error,{stop_tracing,Reason}}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function removing all local logs using the tracerdata to determine what
-%% logs to remove from where.
-%% There is no significant return value since it is not really clear what to do
-%% if removal went wrong. The function can make debug-reports thought.
-remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
- LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
- case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
- {ok,Results} ->
- case look_for_errors_resultlist(Results) of
- [] -> % No errors found in the result!
- true;
- Errors ->
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
- true
- end;
- {error,Reason} -> % Some general error.
- inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
- true
- end.
-
-%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
-%% we must build one tuple for each tracerdata for one node.
-build_logspeclist_remove_logs(Nodes,TRDstorage) ->
- [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
-%% ------------------------------------------------------------------------------
-
-%% Help function which traverses a resultlist from an inviso function. Such are
-%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
-%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
-%% or {ok,FileName}.
-%% Returns a list of {Node,[{error,Reason},...]}.
-look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
- [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
-look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
- case look_for_errors_resultlist_2(NResults,[]) of
- [] ->
- look_for_errors_resultlist(Rest);
- Errors -> % A list of lists.
- [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
- end;
-look_for_errors_resultlist([_|Rest]) ->
- look_for_errors_resultlist(Rest);
-look_for_errors_resultlist([]) ->
- [].
-
-look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
- case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
- [] -> % No errors for this node.
- look_for_errors_resultlist_2(Rest,Accum);
- Errors -> % A list of at least one error.
- look_for_errors_resultlist_2(Rest,[Errors|Accum])
- end;
-look_for_errors_resultlist_2([],Accum) ->
- Accum.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the loopdata structure.
-%% Its main purpose is to store information about runtime components participating
-%% in the session and their current status.
-%% ------------------------------------------------------------------------------
-
--record(ld,{parent,
- ctrlnode,
- ctrlpid, % To where to send inviso cmd.
- rtstates,
- tracerdata,
- safetycatches,
- dbg,
- actstorage % Activity storage, for reactivate.
- }).
-
-%% Function creating the initial datastructure.
-%% The datastructure is [{Node,State},...].
-%%
-%% The tracerdata table is a bag simply for the reason that if we try to insert
-%% the same tracerdata for a node twice, we will end up with one tracerdata after
-%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
-%% come as a state-change too.
-mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
- TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
- TRDtid=ets:new(TRDtableName,[bag]),
- ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
- ACTtid=ets:new(ACTtableName,[bag]),
- mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
- #ld{parent=Parent, % The tool main process.
- ctrlnode=CtrlNode, % Node name where the control component is.
- ctrlpid=CtrlPid, % The process id of the control component.
- rtstates=RTStates, % All nodes and their state/status.
- tracerdata=TRDtid,
- safetycatches=SafetyCatches,
- dbg=Dbg,
- actstorage=ACTtid
- }.
-
-%% Help function which inserts tracer data for the nodes. Note that we can get
-%% tracer data either from the return value from init_tracing or by asking the
-%% node for it. The latter is necessary for the nodes which were marked not to
-%% be initiated by the session handler. This maybe because those nodes have
-%% autostarted.
-mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
- mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
- mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
-
-mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_nodeparams(TId,Rest);
-mk_ld_fill_tracerdata_nodeparams(_,[]) ->
- ok.
-
-mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
- ok;
-mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
- case inviso:get_tracerdata() of
- {error,_Reason} -> % Perhaps in state new or disconnected.
- ok; % Do nothing.
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData})
- end;
-mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
- case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
- {ok,Results} ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Results);
- {error,_Reason} -> % Strange, we will probably crash later.
- ok
- end.
-
-mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
-mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
- ets:insert(TId,{Node,TracerData}),
- mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
-mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
- ok.
-%% ------------------------------------------------------------------------------
-
-get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
- CtrlNode.
-%% ------------------------------------------------------------------------------
-
-
-get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
- CtrlPid.
-%% ------------------------------------------------------------------------------
-
-get_rtstates_ld(#ld{rtstates=RTStates}) ->
- RTStates.
-
-put_rtstates_ld(NewRTStates,LD) ->
- LD#ld{rtstates=NewRTStates}.
-%% ------------------------------------------------------------------------------
-
-get_trdstorage_ld(#ld{tracerdata=TId}) ->
- TId.
-
-put_trdstorage_ld(_NewTId,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-%% Help function which adds the current tracerdata of node Node to the tracerdata
-%% storage. We only want to add tracerdata we have not seen before. We therefore
-%% avoid adding it if the node already is in state ?TRACING.
-%% Returns a new tracerdata (what ever it is)!
-add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
- case get_statestatus_rtstates(Node,RTStates) of
- {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
- TId; % Then do nothing.
- {ok,_} -> % Since we were not tracing before.
- case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
- {ok,TracerData} ->
- ets:insert(TId,{Node,TracerData});
- no_tracerdata -> % Strange, how could we become tracing
- ok;
- {error,_Reason} -> % The node perhaps disconnected!?
- ok
- end;
- false -> % Very strange, not our node!
- ok % Do nothing.
- end.
-
-add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
- case inviso:get_tracerdata() of
- {ok,TracerData} ->
- {ok,TracerData};
- {error,no_tracerdata} ->
- no_tracerdata;
- {error,Reason} ->
- {error,Reason}
- end;
-add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
- case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
- {ok,[{Node,{ok,TracerData}}]} ->
- {ok,TracerData};
- {ok,[{Node,{error,no_tracerdata}}]} ->
- no_tracerdata;
- {ok,[{Node,{error,Reason}}]} ->
- {error,Reason};
- {error,Reason} ->
- {error,Reason}
- end.
-%% ------------------------------------------------------------------------------
-
-
-get_safetycatches_ld(#ld{safetycatches=SCs}) ->
- SCs.
-%% ------------------------------------------------------------------------------
-
-get_dbg_ld(#ld{dbg=Dbg}) ->
- Dbg.
-%% ------------------------------------------------------------------------------
-
-get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
- ACTstorage.
-
-put_actstorage_ld(_NewACTstorage,LD) ->
- LD.
-%% ------------------------------------------------------------------------------
-
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the rtstates structure (which is a substructure of loopdata).
-%% It is either:
-%% [{Node,StateStatus,Opts},...]
-%% Node is either the node name of the runtime component erlang node or
-%% ?LOCAL_RUNTIME as returned from the trace control component.
-%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
-%% Status is the returnvalue from trace control component.
-%% i.e: running | {suspended,Reason}
-%% ------------------------------------------------------------------------------
-
-%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
-to_rtstates(ListOfStates) when list(ListOfStates) ->
- ListOfStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
-from_rtstates(RTStates) ->
- RTStates.
-%% ------------------------------------------------------------------------------
-
-%% Function which takes an rtstates structure and a result as returned from
-%% init_tracing. The RTStates is modified for the nodes that changed state as a
-%% result of successful init_tracing.
-%% Returns a new RTStates.
-set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
- case lists:keysearch(Node,1,Result) of
- {value,{_,ok}} -> % Means state-change to tracing!
- [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
- _ -> % Otherwise, leave it as is.
- [E|set_tracing_rtstates(Rest,Result)]
- end;
-set_tracing_rtstates([],_Result) ->
- [].
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component.
-%% Returns a new RTStates structure. Note that Node must not necessarily be one
-%% of the nodes in the session. Meaning that Node shall not be added to RTStates
-%% should it not already be in there.
-statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function updating the state/status for a certain runtime component. The
-%% state/status is set to 'unavailable'.
-%% Returns a new RTStates structure.
-set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
- _ -> % Then Node does not exist.
- RTStates % Just keep it as is, as keyreplace would have done.
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function finding the statestatus associated with Node in the RTStates structure.
-%% Returns {ok,StateStatus} or 'false'.
-get_statestatus_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,StateStatus,_}} ->
- {ok,StateStatus};
- false ->
- false
- end.
-%% ------------------------------------------------------------------------------
-
-%% Help function which returns a list of all nodes that are currently marked
-%% as available to us in the runtime state structure.
-get_all_available_nodes_rtstates(RTStates) ->
- get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
- (_)->true
- end,
- RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Help function returning a list of all nodes belonging to this session.
-get_all_session_nodes_rtstates(RTStates) ->
- lists:map(fun({Node,_,_})->Node end,RTStates).
-%% ------------------------------------------------------------------------------
-
-%% Function which returns a list of nodes that are indicated as tracing in the
-%% RTStates structure.
-get_all_tracing_nodes_rtstates(RTStates) ->
- lists:map(fun({N,_,_})->N end,
- lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
-%% ------------------------------------------------------------------------------
-
-%% Returns the options associated with Node in the RTStates structure.
-get_opts_rtstates(Node,RTStates) ->
- case lists:keysearch(Node,1,RTStates) of
- {value,{_,_,Opts}} ->
- {ok,Opts};
- false ->
- false
- end.
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the tracerdata structure, which is a part of the loopdata.
-%% The tracerdata structure is an ETS-table of type bag storing:
-%% {Node,TracerData}.
-%% Note that there can of course be multiple entries for a node.
-%% ------------------------------------------------------------------------------
-
-%% Help function which takes a tracerdata loopdata structure and returns a list
-%% of all stored tracerdata for a certain Node.
-find_tracerdata_for_node_trd(Node,TRD) ->
- case ets:lookup(TRD,Node) of
- Result when list(Result) ->
- lists:map(fun({_Node,TracerData})->TracerData end,Result);
- _ -> % Should probably never happend.
- []
- end.
-%% ------------------------------------------------------------------------------
-
-
-%% ------------------------------------------------------------------------------
-%% Functions working on the activity storage structure, which is part of the
-%% loopdata. It stores entries about things that needs to be "redone" in case
-%% of a reactivation of the node. The time order is also important.
-%% Note that for every ActivityType there must be a "handler" in the reactivation
-%% functionality.
-%%
-%% The structure is a bag of {Node,ActivityType,What}.
-%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
-%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
-%% /{Op,[]}
-%% TraceConfList=[{Proc,Flags},...]
-%% How=true|false
-%% ------------------------------------------------------------------------------
-
-%% Function that adds meta-pattern activities to the activity storage. Note
-%% that one of the parameters to the function is a return value from an
-%% inviso call. In that way we do not enter activities that were unsuccessful.
-%% Op can be either the setting or clearing of a meta pattern.
-%% Returns a new ACTstorage.
-add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
- add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
-add_tpm_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-
-%% Function that adds process trace-flags to the activity storage. Note that one
-%% of the parameters is the return value from an inviso function. Meaning that
-%% if the flags failed in their entirety, no activity will be saved. If only
-%% some of the flags failed, we will not go through the effort of trying to find
-%% out exactly which.
-%% Returns a new activity storage structure.
-add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
- true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
- add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
-add_tf_actstorage([],_,_,ACTstorage) ->
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
-%% Finds all activities associated with Node. Returns a list of them in the
-%% same order as they were inserted.
-get_activities_actstorage(Node,ACTstorage) ->
- case ets:lookup(ACTstorage,Node) of
- [] ->
- false;
- Result when list(Result) ->
- {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
- end.
-%% ------------------------------------------------------------------------------
-
-%% Function removing all activity entries associated with Node. This is useful
-%% if the Node disconnects for instance.
-del_node_actstorage(Node,ACTstorage) ->
- ets:delete(ACTstorage,Node),
- ACTstorage.
-%% ------------------------------------------------------------------------------
-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Description:
+%% The runtime component of the trace tool Inviso.
+%%
+%% Authors:
+%% Lennart �hman, [email protected]
+%% -----------------------------------------------------------------------------
+
+-module(inviso_tool_sh).
+
+%% Inviso Session Handler.
+%% This is the code for the session handler process. Its purpose is that we have
+%% one session handler process for each trace session started through the
+%% start_session inviso tool API. The session handler process is responsible for:
+%%
+%% -Knowing the state/status of all participating runtime components.
+%% -Keeping storage of all tracerdata all our participants have used. This means
+%% also to find out the tracerdata of runtime components connecting by them
+%% selves.
+%%
+%% STORAGE STRATEGY
+%% ----------------
+%% The local information storage can be changed by two things. Either by executing
+%% commands issued through our APIs. Or by receiving trace_event from the control
+%% component. When we execute commands, a corresponding event will also follow.
+%% Meaning that in those situations we are informed twice.
+%% A simple strategy could be to wait for the event even when doing the changes
+%% to the runtime components our self (through commands). But that may result in
+%% a small time frame where someone might do yet another command and failing
+%% because the local information storage is not uptodate as it would have been
+%% expected to be. Therefore we always update the local storage when making changes
+%% to a runtime component our selves. There will eventually be a double update
+%% through an incoming event. But the storage must coop with that, preventing
+%% inconsitancies to happend. An example of a strategy is that the tracerdata table
+%% is a bag, not allowing for double entries of the same kind. Therefore a double
+%% update is harmless there.
+
+%% ------------------------------------------------------------------------------
+%% Module wide constants.
+%% ------------------------------------------------------------------------------
+-define(LOCAL_RUNTIME,local_runtime). % Used as node name when non-disitrbuted.
+-define(TRACING,tracing). % A state defined by the control component.
+-define(RUNNING,running). % A status according to control componet.
+
+-define(COPY_LOG_FROM,copy_log_from). % Common fileystem option.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% API exports.
+%% ------------------------------------------------------------------------------
+-export([start_link/5,start_link/8]).
+-export([cancel_session/1,stop_session/3]).
+-export([reactivate/1,reactivate/2]).
+-export([ctpl/5,tpl/5,tpl/6,tpl/7,
+ tf/2,tf/3,
+ tpm_localnames/2,init_tpm/6,init_tpm/9,tpm/6,tpm/7,tpm/10,
+ tpm_ms/7,ctpm_ms/6,ctpm/5
+ ]).
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Internal exports.
+%% ------------------------------------------------------------------------------
+-export([init/1,handle_call/3,handle_info/2,terminate/2]).
+
+-export([get_loopdata/1]).
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Includes.
+%% ------------------------------------------------------------------------------
+-include_lib("kernel/include/file.hrl"). % Necessary for file module.
+%% ------------------------------------------------------------------------------
+
+
+%% ==============================================================================
+%% Exported API functions.
+%% ==============================================================================
+
+%% start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,NodesIn,NodesNotIn) =
+%% {ok,Pid} | {error,Reason}
+%% From= pid(), the initial client expecting the reply.
+%% NodeParams=[{Node,TracerData},{Node,TracerData,Opts}...]
+%% CtrlNode=atom() | 'void', the node where the trace control component is.
+%% CtrlPid=pid(), the pid of the trace control component.
+%% SafetyCatches=
+%% Dir=string(), where to place fetched logs and the merged log.
+%% Dbg=debug structure.
+%% NodesIn=[Node,...], list of nodes already in another session.
+%% NodesNotIn=[Node,...], list of nodes not in another session.
+%%
+%% Starts a session-handler. It keeps track of the the state and status of all
+%% participating runtime components. Note that there is a non-distributed case too.
+%% In the non-distributed case there is no things such as CtrlNode.
+start_link(From,TracerData,CtrlPid,SafetyCatches,Dbg) ->
+ gen_server:start_link(?MODULE,
+ {self(),From,TracerData,CtrlPid,SafetyCatches,Dbg},
+ []).
+
+start_link(From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn) ->
+ gen_server:start_link(?MODULE,
+ {self(),From,NodeParams,CtrlNode,CtrlPid,
+ SafetyCatches,Dbg,NodesIn,NodesNotIn},
+ []).
+%% ------------------------------------------------------------------------------
+
+%% Stops tracing where it is ongoing. Fetches all logfiles.
+stop_session(SID,Dir,Prefix) ->
+ gen_server:call(SID,{stop_session,Dir,Prefix}).
+%% ------------------------------------------------------------------------------
+
+%% stop_session(SID) = ok
+%%
+%% Cancels the session brutaly. All runtime components are made to stop tracing,
+%% all local log files are removed using the tracerdata we know for them.
+cancel_session(SID) ->
+ gen_server:call(SID,cancel_session).
+%% ------------------------------------------------------------------------------
+
+%% reactivate(SID) = {ok,
+%% reactivate(SID,Nodes) = {ok,NodeResults} | {error,Reason}.
+%% SID=session id, pid().
+%% Nodes=[Node,...]
+%% NodeResult=[{Node,Result},...]
+%% Result={Good,Bad}
+%% Good,Bad=integer(), the number of redone activities.
+%%
+%% Function which reactivates runtime components being suspended. This is done
+%% replaying all trace flags (in the correct order) to the corresponding nodes.
+%% Note that this may also mean turning flags off. Like first turning them on
+%% then off a split second later.
+reactivate(SID) ->
+ gen_server:call(SID,reactivate). %% NOT IMPLEMENTED YET.
+reactivate(SID,Nodes) ->
+ gen_server:call(SID,{reactivate,Nodes}).
+%% ------------------------------------------------------------------------------
+
+
+%% tpl(SessionID,Mod,Func,Arity,MS)=
+%% tpl(SessionID,Mod,Func,Arity,MS,Opts)={ok,N}|{error,Reason}.
+%% tpl(SessionID,Nodes,Mod,Func,Arity,MS)=
+%% tpl(SessionID,Nodes,Mod,Func,Arity,MS,Opts)={ok,Result}|{error,Reason}
+%% Mod='_' | ModuleName | ModRegExp | {DirRegExp,ModRegExp}
+%% ModRegExp=DirRegExp= string()
+%% Func='_' | FunctionName
+%% Arity='_' | integer()
+%% MS=[] | false | a match specification
+%% Opts=[Opts,...]
+%% Opt={arg,Arg}, disable_safety, {expand_regexp_at,NodeName}, only_loaded
+%% Nodes=[NodeName,...]
+tpl(SID,Mod,Func,Arity,MS) ->
+ gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,[]}).
+tpl(SID,Mod,Func,Arity,MS,Opts) when list(MS);MS==true;MS==false ->
+ gen_server:call(SID,{tp,tpl,Mod,Func,Arity,MS,Opts});
+tpl(SID,Nodes,Mod,Func,Arity,MS) when integer(Arity);Arity=='_' ->
+ gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,[]}).
+tpl(SID,Nodes,Mod,Func,Arity,MS,Opts) ->
+ gen_server:call(SID,{tp,tpl,Nodes,Mod,Func,Arity,MS,Opts}).
+%% ------------------------------------------------------------------------------
+
+%% ctpl(SessionID,Nodes,Mod,Func,Arity)=
+%% See tpl/X for arguments.
+%%
+%% Removes local trace-patterns from functions.
+ctpl(SID,Nodes,Mod,Func,Arity) ->
+ gen_server:call(SID,{ctp,ctpl,Nodes,Mod,Func,Arity}).
+%% ------------------------------------------------------------------------------
+
+
+tpm_localnames(SID,Nodes) ->
+ gen_server:call(SID,{tpm_localnames,Nodes}).
+
+%% tpm_globalnames(SID,Nodes) ->
+%% gen_server:call(SID,{tpm_globalnames,Nodes}).
+
+init_tpm(SID,Nodes,Mod,Func,Arity,CallFunc) ->
+ gen_server:call(SID,{init_tpm,Nodes,Mod,Func,Arity,CallFunc}).
+init_tpm(SID,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
+ gen_server:call(SID,
+ {init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
+tpm(SID,Nodes,Mod,Func,Arity,MS) ->
+ gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS}).
+tpm(SID,Nodes,Mod,Func,Arity,MS,CallFunc) ->
+ gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,CallFunc}).
+tpm(SID,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc) ->
+ gen_server:call(SID,{tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc}).
+
+tpm_ms(SID,Nodes,Mod,Func,Arity,MSname,MS) ->
+ gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname,MS}).
+
+ctpm_ms(SID,Nodes,Mod,Func,Arity,MSname) ->
+ gen_server:call(SID,{tpm_ms,Nodes,Mod,Func,Arity,MSname}).
+
+ctpm(SID,Nodes,Mod,Func,Arity) ->
+ gen_server:call(SID,{ctpm,Nodes,Mod,Func,Arity}).
+%% ------------------------------------------------------------------------------
+
+
+%% tf(SessionID,Nodes,TraceConfList)=
+%% TraceConfList=[{PidSpec,Flags},...]
+%% PidSpec=pid()|atom()|all|new|existing
+%% Flags=[Flag,...]
+tf(SID,TraceConfList) ->
+ gen_server:call(SID,{tf,TraceConfList}).
+tf(SID,Nodes,TraceConfList) ->
+ gen_server:call(SID,{tf,Nodes,TraceConfList}).
+%% ------------------------------------------------------------------------------
+
+
+get_loopdata(SID) ->
+ gen_server:call(SID,get_loopdata).
+%% ------------------------------------------------------------------------------
+
+%% ==============================================================================
+%% Genserver call-backs.
+%% ==============================================================================
+
+%% Initial function for the session handler process. The nodes participating in
+%% the session must previously have been added to our control component by the tool.
+%% The session handler first finds out the state/status of the specified runtime
+%% components, then it tries to initiate tracing on those where it is applicable.
+%% Note that a reply to the initial (tool)client is done from here instead from
+%% the tool-server.
+init({Parent,From,TracerData,CtrlPid,SafetyCatches,Dbg}) -> % The non-distributed case.
+ {ok,StateStatus}=init_rtcomponent_states([],void,CtrlPid,[?LOCAL_RUNTIME]),
+ case is_tool_internal_tracerdata(TracerData) of
+ false -> % We shall initiate local runtime.
+ case inviso:init_tracing(TracerData) of
+ ok ->
+ gen_server:reply(From,{ok,{self(),ok}}),
+ {ok,mk_ld(Parent,
+ void,
+ CtrlPid,
+ to_rtstates([{?LOCAL_RUNTIME,{tracing,?RUNNING},[]}]),
+ [{?LOCAL_RUNTIME,TracerData}],
+ [],
+ SafetyCatches,
+ Dbg)};
+ {error,Reason} -> % It might have become suspended?!
+ gen_server:reply(From,{error,Reason}),
+ {ok,mk_ld(Parent,
+ void,
+ CtrlPid,
+ to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
+ [{?LOCAL_RUNTIME,TracerData}],
+ [],
+ SafetyCatches,
+ Dbg)}
+ end;
+ true -> % We shall not pass this one on.
+ gen_server:reply(From,{ok,{self(),ok}}), % Then it is ok.
+ {ok,mk_ld(Parent,
+ void,
+ CtrlPid,
+ to_rtstates([{?LOCAL_RUNTIME,StateStatus,[]}]),
+ [],
+ [?LOCAL_RUNTIME],
+ SafetyCatches,
+ Dbg)}
+ end;
+init({Parent,From,NodeParams,CtrlNode,CtrlPid,SafetyCatches,Dbg,NodesIn,NodesNotIn}) ->
+ case init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,NodesNotIn) of
+ {ok,States} -> % A list of {Node,{State,Status},Opts}.
+ {NodeParams2,Nodes2}=remove_nodeparams(NodesIn,NodeParams),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,init_tracing,[NodeParams2]) of
+ {ok,Result} -> % Resulted in state changes!
+ RTStates=set_tracing_rtstates(to_rtstates(States),Result),
+ ReplyValue=init_fix_resultnodes(NodesIn,Nodes2,Result),
+ gen_server:reply(From,{ok,{self(),ReplyValue}}),
+ {ok,mk_ld(Parent,CtrlNode,CtrlPid,RTStates,
+ NodeParams2,Nodes2,SafetyCatches,Dbg)};
+ {error,Reason} -> % Some general failure.
+ inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
+ gen_server:reply(From,{error,{init_tracing,Reason}}),
+ {stop,{init_tracing,Reason}};
+ What ->
+ io:format("GOT:~n~w~n",[What]),
+ exit(foo)
+ end;
+ {error,Reason} -> % Unable to get the state/status.
+ inviso_tool_lib:inviso_cmd(CtrlNode,unsubscribe,[]),
+ gen_server:reply(From,{error,Reason}),
+ {stop,{error,Reason}};
+ What ->
+ io:format("GOT:~n~w~n",[What]),
+ exit(foo)
+ end.
+%% ------------------------------------------------------------------------------
+
+%% To stop a session means stop the tracing and remove all local files on the
+%% runtime nodes. We do have a table with all tracer data and that is how we are
+%% going to recreate what files to remove.
+%% Since runtime components may actually change state when this procedure is
+%% on-going, we do not care! It is the state in the session handling process at
+%% the time of start of this procedure which is used.
+handle_call(cancel_session,_From,LD) ->
+ CtrlNode=get_ctrlnode_ld(LD),
+ RTStates=get_rtstates_ld(LD),
+ Dbg=get_dbg_ld(LD),
+ TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
+ case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
+ ok-> % Hopefully all nodes are stopped now.
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ TRDstorage=get_trdstorage_ld(LD),
+ remove_all_local_logs(CtrlNode,TRDstorage,AvailableNodes,Dbg),
+ {stop,normal,ok,LD}; % LD actually not correct now!
+ {error,Reason} -> % Some serious error when stop_tracing.
+ {stop,normal,{error,Reason},LD}
+ end;
+%% ------------------------------------------------------------------------------
+
+%% *Stop all tracing on runtime components still tracing.
+%% *Copy all local log files to the collection directory.
+handle_call({stop_session,Dir,Prefix},_From,LD) ->
+ case check_directory_exists(Dir) of % Check that this directory exists here.
+ true ->
+ RTStates=get_rtstates_ld(LD),
+ CtrlNode=get_ctrlnode_ld(LD),
+ Dbg=get_dbg_ld(LD),
+ TracingNodes=get_all_tracing_nodes_rtstates(RTStates),
+ case stop_all_tracing(CtrlNode,Dbg,TracingNodes) of
+ ok -> % Hopefully no node is still tracing now.
+ TRDstorage=get_trdstorage_ld(LD),
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ {FailedNodes,FetchedFiles}=
+ transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,
+ TRDstorage,Dbg,AvailableNodes),
+ RemoveNodes= % We only delete local logs where fetch ok.
+ lists:filter(fun(N)->
+ case lists:keysearch(N,1,FailedNodes) of
+ {value,_} ->
+ false;
+ false ->
+ true
+ end
+ end,
+ AvailableNodes),
+ remove_all_local_logs(CtrlNode,TRDstorage,RemoveNodes,Dbg),
+ {stop,normal,{ok,{FailedNodes,FetchedFiles}},LD};
+ {error,Reason} -> % Some general failure, quit.
+ {stop,normal,{error,Reason},LD}
+ end;
+ false -> % You specified a non-existing directory!
+ {reply,{error,{faulty_dir,Dir}},LD}
+ end;
+%% ------------------------------------------------------------------------------
+
+handle_call({reactivate,Nodes},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ {OurNodes,OtherNodes}=
+ remove_nodes_not_ours(Nodes,get_all_session_nodes_rtstates(RTStates)),
+ CtrlNode=get_ctrlnode_ld(LD),
+ ACTstorage=get_actstorage_ld(LD),
+ case h_reactivate(CtrlNode,OurNodes,ACTstorage) of
+ {ok,Results} -> % A list of {Node,Result}.
+ if
+ OtherNodes==[] -> % Normal case, no non-session nodes.
+ {reply,{ok,Results},LD};
+ true -> % Add error values for non-session nodes.
+ {reply,
+ {ok,
+ lists:map(fun(N)->{N,{error,not_in_session}} end,OtherNodes)++
+ Results},
+ LD}
+ end;
+ {error,Reason} -> % Then this error takes presidence.
+ {reply,{error,Reason},LD}
+ end;
+%% ------------------------------------------------------------------------------
+
+%% Call-back for set trace-pattern for both global and local functions.
+handle_call({tp,PatternFunc,Mod,F,A,MS,Opts},_From,LD) ->
+ Reply=h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD), % For all active nodes in the session.
+ {reply,Reply,LD};
+handle_call({tp,PatternFunc,Nodes,Mod,F,A,MS,Opts},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ SNodes=get_all_session_nodes_rtstates(RTStates), % Notes belongoing to the session.
+ {Nodes2,FaultyNodes}=remove_nodes_not_ours(Nodes,SNodes),
+ Reply=h_tp(Nodes2,PatternFunc,Mod,F,A,MS,Opts,LD),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,FaultyNodes),
+ {reply,ErrorReply++Reply,LD};
+%% ------------------------------------------------------------------------------
+
+%% Call-back handling the removal of both local and global trace-patterns.
+%% NOT IMPLEMENTED YET.
+handle_call({ctp,PatternFunc,Nodes,Mod,F,A},_From,LD) ->
+ Reply=h_ctp(Nodes,PatternFunc,Mod,F,A,LD),
+ {reply,Reply,LD};
+%% ------------------------------------------------------------------------------
+
+handle_call({tpm_localnames,Nodes},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_tpm_localnames(get_ctrlnode_ld(LD),Nodes2,RTStates,ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({init_tpm,Nodes,Mod,Func,Arity,CallFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ init_tpm,
+ [Mod,Func,Arity,CallFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({init_tpm,Nodes,Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ init_tpm,
+ [Mod,Func,Arity,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm,Nodes,Mod,Func,Arity,MS},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),Nodes2,tpm,[Mod,Func,Arity,MS],RTStates,ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm,Nodes,Mod,Func,Arity,MS,CallFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ tpm,
+ [Mod,Func,Arity,MS,CallFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm,Nodes,Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ tpm,
+ [Mod,Func,Arity,MS,InitFunc,CallFunc,ReturnFunc,RemoveFunc],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({tpm_ms,Nodes,Mod,Func,Arity,MSname,MS},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ tpm_ms,
+ [Mod,Func,Arity,MSname,MS],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({ctpm_ms,Nodes,Mod,Func,Arity,MSname},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),
+ Nodes2,
+ ctpm_ms,
+ [Mod,Func,Arity,MSname],
+ RTStates,
+ ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+
+handle_call({ctpm,Nodes,Mod,Func,Arity},_From,LD) ->
+ RTStates=get_rtstates_ld(LD),
+ OurNodes=get_all_session_nodes_rtstates(RTStates),
+ {Nodes2,NotOurNodes}=remove_nodes_not_ours(Nodes,OurNodes),
+ ACTstorage=get_actstorage_ld(LD),
+ {Reply,NewACTstorage}=
+ h_all_tpm(get_ctrlnode_ld(LD),Nodes2,ctpm,[Mod,Func,Arity],RTStates,ACTstorage),
+ ErrorReply=lists:map(fun(N)->{N,{error,not_in_session}} end,NotOurNodes),
+ {reply,ErrorReply++Reply,put_actstorage_ld(NewACTstorage,LD)};
+%% ------------------------------------------------------------------------------
+
+%% Call-back for setting process trace-flags. Handles both distributed and non-
+%% distributed case.
+handle_call({tf,TraceConfList},From,LD) ->
+ handle_call({tf,all,TraceConfList},From,LD);
+handle_call({tf,Nodes,TraceConfList},_From,LD) ->
+ {Reply,NewACTstorage}=h_tf(get_ctrlnode_ld(LD),
+ Nodes,
+ TraceConfList,
+ get_actstorage_ld(LD),
+ get_rtstates_ld(LD)),
+ {reply,Reply,put_actstorage_ld(NewACTstorage,LD)};
+%% ------------------------------------------------------------------------------
+
+
+
+handle_call(get_loopdata,_From,LD) ->
+ io:format("The loopdata:~n~p~n",[LD]),
+ {reply,ok,LD}.
+%% ------------------------------------------------------------------------------
+
+
+%% Clause handling an incomming state-change event from the control component.
+%% Note that it does not have to be one of our nodes since it is not possible
+%% to subscribe to certain node-events.
+%% We may very well get state-change events for state-changes we are the source
+%% to our selves. Those state-changes are already incorporated into the RTStates.
+%% There is however no harm in doing them again since we know that this event
+%% message will reach us before a reply to a potentially following state-change
+%% request will reach us. Hence we will do all state-changes in the correct order,
+%% even if sometimes done twice.
+handle_info({trace_event,CtrlPid,_Time,{state_change,Node,{State,Status}}},LD) ->
+ case get_ctrlpid_ld(LD) of
+ CtrlPid -> % It is from our control component.
+ case {State,Status} of
+ {?TRACING,?RUNNING} -> % This is the only case when new tracerdata!
+ NewTracerData=add_current_tracerdata_ld(get_ctrlnode_ld(LD),
+ Node,
+ get_rtstates_ld(LD),
+ get_trdstorage_ld(LD)),
+ NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
+ {noreply,put_trdstorage_ld(NewTracerData,
+ put_rtstates_ld(NewRTStates,LD))};
+ _ -> % In all other cases, just fix rtstates.
+ NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
+ {noreply,put_rtstates_ld(NewRTStates,LD)}
+ end;
+ _ ->
+ {noreply,LD}
+ end;
+%% If a new runtime component connects to our trace control component, and it is
+%% in our list of runtime components belonging to this session, we may update its
+%% state to now being present. Otherwise it does not belong to this session.
+%% Note that we avoid updating an already connected runtime component. This
+%% can happend if it connected by itself after we started the session handler,
+%% but before we managed to initiate tracing. Doing so or not will not result in
+%% any error in the long run, but during a short period of time we might be
+%% prevented from doing things with the runtime though it actually is tracing.
+handle_info({trace_event,CtrlPid,_Time,{connected,Node,{_Tag,{State,Status}}}},LD) ->
+ case get_ctrlpid_ld(LD) of
+ CtrlPid -> % It is from our control component.
+ case get_statestatus_rtstates(Node,get_rtstates_ld(LD)) of
+ {ok,unavailable} -> % This is the situation when we update!
+ NewRTStates=statechange_rtstates(Node,State,Status,get_rtstates_ld(LD)),
+ {noreply,put_rtstates_ld(NewRTStates,LD)};
+ _ -> % In all other cases, let it be.
+ {noreply,LD}
+ end;
+ _ -> % Not from our control component.
+ {noreply,LD}
+ end;
+%% If a runtime component disconnects we mark it as unavailable. We must also
+%% remove all saved trace-flags in order for them to not be accidently reactivated
+%% should the runtime component reconnect and then suspend.
+handle_info({trace_event,CtrlPid,_Time,{disconnected,Node,_}},LD) ->
+ case get_ctrlpid_ld(LD) of
+ CtrlPid -> % It is from our control component.
+ NewRTStates=set_unavailable_rtstates(Node,get_rtstates_ld(LD)),
+ NewACTstorage=del_node_actstorage(Node,get_actstorage_ld(LD)),
+ {noreply,put_actstorage_ld(NewACTstorage,put_rtstates_ld(NewRTStates,LD))};
+ _ ->
+ {noreply,LD}
+ end;
+handle_info(_,LD) ->
+ {noreply,LD}.
+%% ------------------------------------------------------------------------------
+
+%% In terminate we cancel our subscription to event from the trace control component.
+%% That should actually not be necessary, but lets do it the correct way!
+terminate(_,LD) ->
+ case get_ctrlnode_ld(LD) of
+ void -> % Non-distributed.
+ inviso:unsubscribe();
+ Node ->
+ inviso_tool_lib:inviso_cmd(Node,unsubscribe,[])
+ end.
+%% ------------------------------------------------------------------------------
+
+
+
+%% ==============================================================================
+%% First level help functions to call-backs.
+%% ==============================================================================
+
+%% ------------------------------------------------------------------------------
+%% Help functions to init.
+%% ------------------------------------------------------------------------------
+
+%% Help function which find out the state/status of the runtime components.
+%% Note that since we have just started subscribe to state changes we must
+%% check our inqueue to see that we have no waiting messages for the nodes
+%% we learned the state/status of. If there is a waiting message we don't
+%% know whether that was a state change received before or after the state
+%% check was done. We will then redo the state-check.
+%% Returns {ok,States} or {error,Reason}.
+%% Where States is [{Node,{State,Status},Opts},...].
+%% Note that {error,Reason} can not occur in the non-distributed case.
+init_rtcomponent_states(NodeParams,void,CtrlPid,Nodes) -> % The non-distributed case.
+ ok=inviso:subscribe(),
+ init_rtcomponent_states_2(NodeParams,void,CtrlPid,Nodes,[]);
+init_rtcomponent_states(NodeParams,CtrlNode,CtrlPid,Nodes) ->
+ ok=inviso_tool_lib:inviso_cmd(CtrlNode,subscribe,[]),
+ init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,[]).
+
+init_rtcomponent_states_2(_,_,_,[],States) ->
+ {ok,States};
+init_rtcomponent_states_2(NodeParams,void,CtrlPid,_Nodes,States) ->
+ case inviso:get_status() of
+ {ok,StateStatus} -> % Got its state/status, now...
+ {ProblemNodes,NewStates}=
+ init_rtcomponent_states_3(NodeParams,CtrlPid,[{?LOCAL_RUNTIME,{ok,StateStatus}}],
+ [],States),
+ init_rtcomponent_states_2(NodeParams,void,CtrlPid,ProblemNodes,NewStates);
+ {error,_Reason} -> % The runtime is not available!?
+ {ok,[{?LOCAL_RUNTIME,unavailable,[]}]} % Create the return value immediately.
+ end;
+init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,Nodes,States) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,get_status,[Nodes]) of
+ {ok,NodeResult} ->
+ {ProblemNodes,NewStates}=
+ init_rtcomponent_states_3(NodeParams,CtrlPid,NodeResult,[],States),
+ init_rtcomponent_states_2(NodeParams,CtrlNode,CtrlPid,ProblemNodes,NewStates);
+ {error,Reason} -> % Severe problem, abort the session.
+ {error,{get_status,Reason}}
+ end.
+
+%% Traverses the list of returnvalues and checks that we do not have an event
+%% waiting in the message queue. If we do have, it is a problem. That node will
+%% be asked about its state again.
+%% Note that it is here we construct the RTStatesList.
+init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{ok,{State,Status}}}|Rest],Problems,States) ->
+ receive
+ {trace_event,CtrlPid,_Time,{state_change,Node,_}} ->
+ init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,[Node|Problems],States)
+ after
+ 0 -> % Not in msg queue, then we're safe!
+ RTState=case lists:keysearch(Node,1,NodeParams) of
+ {value,{_Node,_TracerData,Opts}} ->
+ {Node,{State,Status},Opts};
+ _ -> % No option available, use [].
+ {Node,{State,Status},[]}
+ end,
+ init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States])
+ end;
+init_rtcomponent_states_3(NodeParams,CtrlPid,[{Node,{error,_Reason}}|Rest],Problems,States) ->
+ RTState=case lists:keysearch(Node,1,NodeParams) of
+ {value,{_Node,_TracerData,Opts}} ->
+ {Node,unavailable,Opts};
+ _ -> % No option available, use [].
+ {Node,unavailable,[]}
+ end,
+ init_rtcomponent_states_3(NodeParams,CtrlPid,Rest,Problems,[RTState|States]);
+init_rtcomponent_states_3(_,_,[],Problems,States) ->
+ {Problems,States}.
+%% ------------------------------------------------------------------------------
+
+%% Help function removing nodes from NodeParams. The reason for this can either
+%% be that we are using a tool internal tracerdata that shall not be forwarded to
+%% the trace control component, or that the node is actually already part of
+%% another session.
+%% Returns {NewNodeParams,NodesWhichShallNotBeInitiated}.
+remove_nodeparams(Nodes,NodesParams) ->
+ remove_nodeparams_2(Nodes,NodesParams,[],[]).
+
+remove_nodeparams_2(Nodes,[NodeParam|Rest],NPAcc,NAcc) when % NPAcc=NodeParamsAcc.
+ (is_tuple(NodeParam) and ((size(NodeParam)==2) or (size(NodeParam)==3))) ->
+ Node=element(1,NodeParam),
+ Params=element(2,NodeParam), % This is tracerdata!
+ case lists:member(Node,Nodes) of
+ true -> % Remove this one, in another session.
+ remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
+ false -> % Ok so far...
+ case is_tool_internal_tracerdata(Params) of
+ false -> % Then keep it and use it later!
+ remove_nodeparams_2(Nodes,Rest,[{Node,Params}|NPAcc],NAcc);
+ true -> % Since it is, remove it from the list.
+ remove_nodeparams_2(Nodes,Rest,NPAcc,[Node|NAcc])
+ end
+ end;
+remove_nodeparams_2(Nodes,[_|Rest],NPAcc,NAcc) -> % Faulty NodeParam, skip it!
+ remove_nodeparams_2(Nodes,Rest,NPAcc,NAcc);
+remove_nodeparams_2(_,[],NPAcc,NAcc) ->
+ {lists:reverse(NPAcc),NAcc}.
+%% ------------------------------------------------------------------------------
+
+%% Help function which adds both the nodes which were already part of another
+%% session and the nodes that we actually did not issue any init_tracing for.
+%% Returns a new Result list of [{Node,NodeResult},...].
+init_fix_resultnodes(NodesOtherSes,NodesNotInit,Result) ->
+ NewResult=init_fix_resultnodes_2(NodesOtherSes,{error,in_other_session},Result),
+ init_fix_resultnodes_2(NodesNotInit,ok,NewResult).
+
+init_fix_resultnodes_2([Node|Rest],NodeResult,Result) ->
+ [{Node,NodeResult}|init_fix_resultnodes_2(Rest,NodeResult,Result)];
+init_fix_resultnodes_2([],_,Result) ->
+ Result. % Append Result to the end of the list.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions to reactivate.
+%% ------------------------------------------------------------------------------
+
+h_reactivate(CtrlNode,Nodes,ACTstorage) -> % Distributed case.
+ case inviso_tool_lib:inviso_cmd(CtrlNode,cancel_suspension,[Nodes]) of
+ {ok,CSuspResults} ->
+ {GoodNodes,BadResults}= % Sort out nodes no longer suspended.
+ lists:foldl(fun({Node,ok},{GoodNs,BadNs})->
+ {[Node|GoodNs],BadNs};
+ ({Node,{error,Reason}},{GoodNs,BadNs})->
+ {GoodNs,[{Node,{error,{cancel_suspension,Reason}}}|BadNs]}
+ end,
+ {[],[]},
+ CSuspResults),
+ Results=h_reactivate_redo_activity(CtrlNode,GoodNodes,ACTstorage,[]),
+ {ok,BadResults++Results};
+ {error,Reason} -> % General failure cancelling suspend.
+ {error,{cancel_suspension,Reason}}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function which traverses the list of nodes known to be ours and have
+%% cancelled their suspend. If we fail redoing one of the activities associated
+%% with a node, the node will be reported in the return value as failed. From
+%% that point on its state must be considered unknown since we do not know how
+%% many of the activities were successfully redone.
+h_reactivate_redo_activity(CtrlNode,[Node|Rest],ACTstorage,Acc) ->
+ case get_activities_actstorage(Node,ACTstorage) of
+ {ok,Activities} -> % The node existed in activity storage.
+ {Good,Bad}=h_reactivate_redo_activity_2(CtrlNode,Node,Activities,0,0),
+ h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{Good,Bad}}|Acc]);
+ false -> % Node not present in activity storage.
+ h_reactivate_redo_activity(CtrlNode,Rest,ACTstorage,[{Node,{0,0}}|Acc])
+ end;
+h_reactivate_redo_activity(_CtrlNode,[],_,Acc) ->
+ lists:reverse(Acc).
+
+%% Help function actually redoing the activity. Note that there must be one
+%% clause here for every type of activity.
+%% Returns {NrGoodCmds,NrBadCmds}.
+%% The number of good or bad commands refers to inviso commands done. If any
+%% of the subparts of such a command returned an error, the command is concidered
+%% no good.
+h_reactivate_redo_activity_2(CtrlNode,Node,[{tf,{Op,TraceConfList}}|Rest],Good,Bad) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node],TraceConfList]) of
+ {ok,[{_Node,{ok,Answers}}]} ->
+ case h_reactivate_redo_activity_check_tf(Answers) of
+ ok ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
+ error -> % At least oneReports the first encountered error.
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
+ end;
+ {ok,[{_Node,{error,_Reason}}]} ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
+ {error,_Reason} -> % General error when doing cmd.
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
+ end;
+h_reactivate_redo_activity_2(CtrlNode,Node,[{tpm,{Op,InvisoCmdParams}}|Rest],Good,Bad) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,Op,[[Node]|InvisoCmdParams]) of
+ {ok,[{_Node,ok}]} ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good+1,Bad);
+ {ok,[{_Node,{error,_Reason}}]} ->
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1);
+ {error,_Reason} -> % General error when doing cmd.
+ h_reactivate_redo_activity_2(CtrlNode,Node,Rest,Good,Bad+1)
+ end;
+h_reactivate_redo_activity_2(_CtrlNode,_Node,[],Good,Bad) ->
+ {Good,Bad}.
+
+%% Help function traversing a list of results from inviso:tf/2 or inviso:ctf/2
+%% to see if there were any errors.
+h_reactivate_redo_activity_check_tf([N|Rest]) when integer(N) ->
+ h_reactivate_redo_activity_check_tf(Rest);
+h_reactivate_redo_activity_check_tf([{error,_Reason}|_]) ->
+ error;
+h_reactivate_redo_activity_check_tf([]) ->
+ ok.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions to tp (setting trace patterns, both local and global).
+%% ------------------------------------------------------------------------------
+
+%% Help function which handles both tpl and tp. Note that the non-distributed case
+%% handled with Nodes='all'.
+%% Returns what shall be the reply to the client.
+h_tp(all,PatternFunc,Mod,F,A,MS,Opts,LD) -> % All available runtime nodes.
+ Nodes=get_all_available_nodes_rtstates(get_rtstates_ld(LD)),
+ h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD);
+h_tp(Nodes,PatternFunc,Mod,F,A,MS,Opts,LD) -> % Only certain nodes in the session.
+ CtrlNode=get_ctrlnode_ld(LD),
+ Dbg=get_dbg_ld(LD),
+ SafetyCatches=get_safetycatches_ld(LD),
+ case inviso_tool_lib:expand_module_names(Nodes,Mod,Opts) of % Take care of any reg-exps.
+ {multinode_expansion,NodeMods} ->
+ NodeTPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,NodeMods,F,A,MS),
+ h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,NodeTPs,[]);
+ {singlenode_expansion,Modules} ->
+ TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,Modules,F,A,MS),
+ h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
+ module ->
+ TPs=inviso_tool_lib:make_patterns(SafetyCatches,Opts,Dbg,[Mod],F,A,MS),
+ h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg);
+ wildcard -> % Means do for all modules, no safety.
+ h_tp_do_tps(CtrlNode,Nodes,[{Mod,F,A,MS}],PatternFunc,Dbg);
+ {error,Reason} ->
+ {error,Reason}
+ end.
+
+%% Note that this function can never be called in the non-distributed case.
+h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,[{Node,TPs}|Rest],Accum) ->
+ case h_tp_do_tps(CtrlNode,[Node],TPs,PatternFunc,Dbg) of
+ {ok,[{Node,Result}]} ->
+ h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,Result}|Accum]);
+ {error,Reason} -> % Failure, but don't stop.
+ h_tp_node_by_node(CtrlNode,PatternFunc,Dbg,Rest,[{Node,{error,Reason}}|Accum])
+ end;
+h_tp_node_by_node(_,_,_,[],Accum) ->
+ {ok,lists:reverse(Accum)}.
+
+%% Help function which does the actual call to the trace control component.
+%% Note that Nodes can be a list of nodes (including a single one) or
+%% ?LOCAL_RUNTIME if we are not distributed. The non-distributed case is otherwise
+%% detected by the 'void' CtrlNode.
+%% Returns {ok,[{Node,{ok,{NrOfFunctions,NrOfErrors}}},{Node,{error,Reason}},...]} or
+%% {error,Reason}. In the non-distributed case {ok,{NrOfFunctions,NrOfErros}} or
+%% {error,Reason}.
+h_tp_do_tps(void,_Nodes,TPs,PatternFunc,Dbg) -> % Non distributed case!
+ inviso_tool_lib:debug(tp,Dbg,[TPs,PatternFunc]),
+ case inviso:PatternFunc(TPs) of
+ {ok,Result} -> % A list of [Nr1,Nr2,error,...].
+ {ok,
+ lists:foldl(fun(N,{AccNr,AccErr}) when integer(N) ->
+ {AccNr+N,AccErr};
+ (error,{AccNr,AccErr}) ->
+ {AccNr,AccErr+1}
+ end,
+ {0,0},
+ Result)};
+ {error,Reason} ->
+ {error,{PatternFunc,Reason}}
+ end;
+h_tp_do_tps(CtrlNode,Nodes,TPs,PatternFunc,Dbg) ->
+ inviso_tool_lib:debug(tp,Dbg,[Nodes,TPs,PatternFunc]),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,PatternFunc,[Nodes,TPs]) of
+ {ok,Result} -> % Result is [{Node,Result},...].
+ {ok,
+ lists:map(fun({Node,{ok,Res}})->
+ {Node,lists:foldl(fun(N,{ok,{AccNr,AccErr}}) when integer(N) ->
+ {ok,{AccNr+N,AccErr}};
+ (error,{AccNr,AccErr}) ->
+ {ok,{AccNr,AccErr+1}}
+ end,
+ {ok,{0,0}},
+ Res)};
+ ({_Node,{error,Reason}})->
+ {error,Reason}
+ end,
+ Result)};
+ {error,Reason} ->
+ {error,{PatternFunc,Reason}}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% Help functions for removing trace-patterns.
+%% ------------------------------------------------------------------------------
+
+%% NOT IMPLEMENTED YET.
+h_ctp(Node,PatternFunc,Mod,F,A,LD) ->
+ tbd.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions for calling the trace information facility.
+%% ------------------------------------------------------------------------------
+
+
+%% Function handling the meta trace pattern for capturing registration of local
+%% process names.
+h_tpm_localnames(CtrlNode,Nodes,RTStates,ACTstorage) ->
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,tpm_localnames,[Nodes3]) of
+ {ok,Result} -> % That good we want to modify tpmstorage!
+ NewACTstorage=add_tpm_actstorage(Result,tpm_localnames,[],ACTstorage),
+ ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
+ {{ok,ErrorResult++Result},NewACTstorage};
+ {error,Reason} -> % If general failure, do not modify storage.
+ {{error,Reason},ACTstorage}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Functions calling meta trace functions for specified nodes. This function is
+%% intended for use with all tmp function calls, init_tpm,tpm,tpm_ms,ctpm_ms and
+%% ctpm.
+%% Note that we must store called meta trace functions and their parameters in the
+%% activity storage in order to be able to redo them in case of a reactivate.
+h_all_tpm(CtrlNode,Nodes,TpmCmd,InvisoCmdParams,RTStates,ACTstorage) ->
+ AvailableNodes=get_all_available_nodes_rtstates(RTStates),
+ {Nodes3,FaultyNodes}=remove_nodes_not_ours(Nodes,AvailableNodes),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,TpmCmd,[Nodes3|InvisoCmdParams]) of
+ {ok,Result} -> % That good we want to modify tpmstorage!
+ NewACTstorage=add_tpm_actstorage(Result,TpmCmd,InvisoCmdParams,ACTstorage),
+ ErrorResult=lists:map(fun(N)->{N,{error,not_available}} end,FaultyNodes),
+ {{ok,ErrorResult++Result},NewACTstorage};
+ {error,Reason} -> % If general failure, do not modify storage.
+ {{error,Reason},ACTstorage}
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Help functions for set trace flags.
+%% ------------------------------------------------------------------------------
+
+%% Help function which sets the tracepatterns in TraceConfList for all nodes
+%% mentioned in Nodes. Note that non-distributed case is handled with Nodes='all'.
+%% Returns {Reply,NewACTstorage} where Reply is whatever shall be returned to caller
+%% and NewACTstorage is traceflag storage modified with the flags added to the
+%% corresponding nodes.
+h_tf(void,_Nodes,TraceConfList,ACTstorage,_RTStates) -> % The non-distributed case.
+ Reply=inviso:tf(TraceConfList),
+ NewACTstorage=add_tf_actstorage([{?LOCAL_RUNTIME,Reply}],tf,TraceConfList,ACTstorage),
+ {Reply,NewACTstorage};
+h_tf(CtrlNode,all,TraceConfList,ACTstorage,RTStates) ->
+ AllNodes=get_all_session_nodes_rtstates(RTStates),
+ h_tf(CtrlNode,AllNodes,TraceConfList,ACTstorage,RTStates);
+h_tf(CtrlNode,Nodes,TraceConfList,ACTstorage,_RTStates) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,tf,[Nodes,TraceConfList]) of
+ {ok,Result} -> % That good we want to modify actstorage!
+ NewACTstorage=add_tf_actstorage(Result,tf,TraceConfList,ACTstorage),
+ {{ok,Result},NewACTstorage};
+ {error,Reason} -> % If general failure, do not modify actstorage.
+ {{error,Reason},ACTstorage}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% Help functions to stop_session.
+%% ------------------------------------------------------------------------------
+
+%% This function fetches all local log-files using our stored tracerdata. Note
+%% that there are two major ways of tranfering logfiles. Either via distributed
+%% Erlang or by common filesystem (like NFS). The default is distributed Erlang.
+%% But there may be info in the RTStates structure about a common file-system.
+%% Returns {FailedNodes,FetchedFileNames} where FailedNodes is a list of
+%% nodenames where problems occurred. Note that problems does not necessarily
+%% mean that no files were copied.
+%% FetchedFileNames contains one or two of the tuples {trace_log,Files} and/or
+%% {ti_log,Files}, listing all files successfully fetched. Note that the
+%% list of fetched files contains sublists of filenames. One for each node and
+%% tracerdata.
+%% In the non-distributed system we always use copy (since the files always
+%% resides locally).
+transfer_logfiles(RTStates,CtrlNode,Dir,Prefix,TRDstorage,Dbg,AvailableNodes) ->
+ if
+ CtrlNode==void -> % When non-distributed, always copy!
+ fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,[?LOCAL_RUNTIME]);
+ true -> % The distributed case.
+ {FetchNodes,CopyNodes}=find_logfile_transfer_methods(AvailableNodes,RTStates),
+ {FailedFetchNodes,FetchedFiles}=
+ case fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,FetchNodes) of
+ {ok,Failed,Files} -> % So far no disasters.
+ {Failed,Files};
+ {error,Reason} -> % Means all fetch-nodes failed!
+ inviso_tool_lib:debug(transfer_logfiles,Dbg,[FetchNodes,Reason]),
+ {lists:map(fun(N)->{N,error} end,FetchNodes),[]}
+ end,
+ {FailedCopyNodes,CopiedFiles}=
+ fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,CopyNodes),
+ {FailedFetchNodes++FailedCopyNodes,FetchedFiles++CopiedFiles}
+ end.
+
+%% Help function which finds out which node we have a common file system with
+%% and from which we must make distributed erlang tranfere.
+%% Returns {DistributedNodes,CopyNodes} where CopyNode is [{Node,CopyFromDir},...].
+find_logfile_transfer_methods(Nodes,RTStates) ->
+ find_logfile_transfer_methods_2(Nodes,RTStates,[],[]).
+
+find_logfile_transfer_methods_2([Node|Rest],RTStates,FetchAcc,CopyAcc) ->
+ {ok,Opts}=get_opts_rtstates(Node,RTStates), % Node must be in RTStates!
+ case lists:keysearch(?COPY_LOG_FROM,1,Opts) of
+ {value,{_,FromDir}} when list(FromDir) -> % Node has common filesystem.
+ find_logfile_transfer_methods_2(Rest,RTStates,FetchAcc,[{Node,FromDir}|CopyAcc]);
+ {value,_} -> % Can't understand dir option.
+ find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc);
+ false -> % Then we want to use fetch instead.
+ find_logfile_transfer_methods_2(Rest,RTStates,[Node|FetchAcc],CopyAcc)
+ end;
+find_logfile_transfer_methods_2([],_,FetchAcc,CopyAcc) ->
+ {FetchAcc,CopyAcc}.
+%% ------------------------------------------------------------------------------
+
+%% Help function which transferes all local logfiles according to the tracerdata
+%% stored for the nodes in Nodes.
+%% Returns {ok,FailedNodes,FileNodeSpecs} or {error,Reason}.
+%% FailedNodes is a list of nodes where fetching logs did not succeed, partially
+%% or not at all.
+%% FileNames is a list of list of actually fetched files (the name as it is here, including
+%% Dir). The sublists are files which belong together.
+fetch_logfiles_distributed(CtrlNode,Dir,Prefix,TRDstorage,Dbg,Nodes) ->
+ LogSpecList=build_logspeclist(Nodes,TRDstorage),
+ case inviso_fetch_log(inviso_tool_lib:inviso_cmd(CtrlNode,
+ fetch_log,
+ [LogSpecList,Dir,Prefix])) of
+ {ok,Result} ->
+ Files=get_all_filenames_fetchlog_result(Result,Dbg),
+ FailedNodes=get_all_failednodes_fetchlog_result(Result),
+ {ok,FailedNodes,Files};
+ {error,Reason} -> % Some general failure!
+ {error,{fetch_log,Reason}}
+ end.
+
+%% Help function which constructs a list {Node,TracerData} for all nodes in Nodes.
+%% Note that there may be more than one tracerdata for a node, resulting in multiple
+%% tuples for that node.
+build_logspeclist(Nodes,TRDstorage) ->
+ build_logspeclist_2(Nodes,TRDstorage,[]).
+
+build_logspeclist_2([Node|Rest],TRDstorage,Acc) ->
+ TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage), % A list of all tracerdata.
+ build_logspeclist_2(Rest,
+ TRDstorage,
+ [lists:map(fun(TRD)->{Node,TRD} end,TRDlist)|Acc]);
+build_logspeclist_2([],_,Acc) ->
+ lists:flatten(Acc).
+
+%% Help function which translates inviso:fetch_log return values to what I
+%% want!
+inviso_fetch_log({error,Reason}) ->
+ {error,Reason};
+inviso_fetch_log({_Success,ResultList}) ->
+ {ok,ResultList}.
+
+%% Help function which collects all filenames mentioned in a noderesult structure.
+%% The files may or may not be complete.
+%% Returns a list of list of filenames. Each sublist contains files which belong
+%% together, i.e because they are a wrap-set.
+get_all_filenames_fetchlog_result(NodeResult,Dbg) ->
+ get_all_filenames_fetchlog_result_2(NodeResult,Dbg,[]).
+
+get_all_filenames_fetchlog_result_2([{Node,{Success,FileInfo}}|Rest],Dbg,Accum)
+ when Success=/=error, list(FileInfo) ->
+ SubAccum=get_all_filenames_fetchlog_result_3(FileInfo,[]),
+ get_all_filenames_fetchlog_result_2(Rest,Dbg,[{Node,SubAccum}|Accum]);
+get_all_filenames_fetchlog_result_2([{Node,{error,FReason}}|Rest],Dbg,Accum) ->
+ inviso_tool_lib:debug(fetch_files,Dbg,[Node,FReason]),
+ get_all_filenames_fetchlog_result_2(Rest,Dbg,Accum);
+get_all_filenames_fetchlog_result_2([],_Dbg,Accum) ->
+ Accum.
+
+get_all_filenames_fetchlog_result_3([{FType,Files}|Rest],SubAccum) ->
+ FilesOnly=lists:foldl(fun({ok,FName},Acc)->[FName|Acc];(_,Acc)->Acc end,[],Files),
+ get_all_filenames_fetchlog_result_3(Rest,[{FType,FilesOnly}|SubAccum]);
+get_all_filenames_fetchlog_result_3([],SubAccum) ->
+ SubAccum.
+
+%% Help function which traverses a noderesult and builds a list as return
+%% value containing the nodenames of all nodes not being complete.
+%% Note that a node may occur multiple times since may have fetched logfiles
+%% for several tracerdata from the same node. Makes sure the list contains
+%% unique node names.
+%% Returns a list nodes.
+get_all_failednodes_fetchlog_result(NodeResult) ->
+ get_all_failednodes_fetchlog_result_2(NodeResult,[]).
+
+get_all_failednodes_fetchlog_result_2([{_Node,{complete,_}}|Rest],Acc) ->
+ get_all_failednodes_fetchlog_result_2(Rest,Acc);
+get_all_failednodes_fetchlog_result_2([{Node,{_Severity,_}}|Rest],Acc) ->
+ case lists:member(Node,Acc) of
+ true -> % Already in the list.
+ get_all_failednodes_fetchlog_result_2(Rest,Acc);
+ false -> % Not in Acc, add it!
+ get_all_failednodes_fetchlog_result_2(Rest,[Node|Acc])
+ end;
+get_all_failednodes_fetchlog_result_2([],Acc) ->
+ Acc.
+%% ------------------------------------------------------------------------------
+
+%% Help function which copies files from one location to Dir and at the same time
+%% adds the Prefix to the filename. NodeSpecs contains full path to the files. The
+%% reason the node information is still part of NodeSpecs is that otherwise we can
+%% not report faulty nodes. Note that one node may occur multiple times since there
+%% may be more than one tracerdata for a node.
+%% Returns {FailedNodes,Files} where FailedNodes is a list of nodes where problems
+%% occurred. Files is a tuple list of [{Node,[{FType,FileNames},...]},...].
+fetch_logfiles_copy(CtrlNode,Dir,Prefix,TRDstorage,Dbg,NodeSpecs) ->
+ CopySpecList=build_copylist(CtrlNode,Dbg,NodeSpecs,TRDstorage),
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,CopySpecList,[],[]).
+
+fetch_logfiles_copy_2(Dir,Prefix,Dbg,[{Node,CopySpecs}|Rest],FailedNodes,Files) ->
+ case fetch_logfiles_copy_3(Dir,Prefix,Dbg,CopySpecs,[],0) of
+ {0,LocalFiles} -> % Copy went ok and zero errors.
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,[{Node,LocalFiles}|Files]);
+ {_N,LocalFiles} -> % Copied files, but some went wrong.
+ case lists:member(Node,FailedNodes) of
+ true -> % Node already in FailedNodes.
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,FailedNodes,
+ [{Node,LocalFiles}|Files]);
+ false -> % Node not marked as failed, yet.
+ fetch_logfiles_copy_2(Dir,Prefix,Dbg,Rest,[Node|FailedNodes],
+ [{Node,LocalFiles}|Files])
+ end
+ end;
+fetch_logfiles_copy_2(_,_,_,[],FailedNodes,Files) ->
+ {FailedNodes,Files}. % The return value from fetch_logfiles_copy.
+
+fetch_logfiles_copy_3(Dir,Prefix,Dbg,[{FType,RemoteFiles}|Rest],Results,Errors) ->
+ {Err,LocalFiles}=fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,RemoteFiles,[],0),
+ fetch_logfiles_copy_3(Dir,Prefix,Dbg,Rest,[{FType,LocalFiles}|Results],Errors+Err);
+fetch_logfiles_copy_3(_,_,_,[],Results,Errors) ->
+ {Errors,Results}.
+
+%% For each file of one file-type (e.g. trace_log).
+fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,[File|Rest],LocalFiles,Errors) ->
+ DestName=Prefix++filename:basename(File),
+ Destination=filename:join(Dir,DestName),
+ case do_copy_file(File,Destination) of
+ ok ->
+ fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,[DestName|LocalFiles],Errors);
+ {error,Reason} ->
+ inviso_tool_lib:debug(copy_files,Dbg,[File,Destination,Reason]),
+ fetch_logfiles_copy_3_1(Dir,Prefix,Dbg,Rest,LocalFiles,Errors+1)
+ end;
+fetch_logfiles_copy_3_1(_,_,_,[],LocalFiles,Errors) ->
+ {Errors,LocalFiles}.
+
+%% Help function which builds a [{Node,[{Type,[ListOfRemoteFiles]}},...}]
+%% where Type describes trace_log or ti_log and each entry in ListOfRemoteFiles
+%% is a complete path to a file to be copied.
+build_copylist(CtrlNode,Dbg,NodeSpecList,TRDstorage) ->
+ build_copylist_2(CtrlNode,Dbg,NodeSpecList,TRDstorage,[]).
+
+%% For each node specified in the NodeSpecList.
+build_copylist_2(CtrlNode,Dbg,[{Node,SourceDir}|Rest],TRDstorage,Acc) ->
+ TRDlist=find_tracerdata_for_node_trd(Node,TRDstorage),
+ CopySpecList=build_copylist_3(CtrlNode,Dbg,SourceDir,Node,TRDlist),
+ build_copylist_2(CtrlNode,Dbg,Rest,TRDstorage,[CopySpecList|Acc]);
+build_copylist_2(_,_,[],_,Acc) ->
+ lists:flatten(Acc).
+
+%% For each tracerdata found for the node.
+build_copylist_3(void,Dbg,SourceDir,Node,[TRD|Rest]) -> % The non-distributed case.
+ case inviso:list_logs(TRD) of
+ {ok,FileSpec} when list(FileSpec) -> % [{trace_log,Dir,Files},...]
+ NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
+ [{Node,NewFileSpec}|build_copylist_3(void,Dbg,SourceDir,Node,Rest)];
+ {ok,no_log} -> % This tracedata not associated with any log.
+ build_copylist_3(void,Dbg,SourceDir,Node,Rest);
+ {error,Reason} ->
+ inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
+ build_copylist_3(void,Dbg,SourceDir,Node,Rest)
+ end;
+build_copylist_3(CtrlNode,Dbg,SourceDir,Node,[TRD|Rest]) -> % The distributed case.
+ case inviso_tool_lib:inviso_cmd(CtrlNode,list_logs,[[{Node,TRD}]]) of
+ {ok,[{Node,{ok,FileSpec}}]} when list(FileSpec) ->
+ NewFileSpec=build_copylist_4(SourceDir,FileSpec,[]),
+ [{Node,NewFileSpec}|build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)];
+ {ok,[{Node,{ok,no_log}}]} -> % It relays to another node, no files!
+ build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
+ {ok,[{Node,{error,Reason}}]} ->
+ inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
+ build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest);
+ {error,Reason} -> % Some general failure.
+ inviso_tool_lib:debug(list_logs,Dbg,[Node,TRD,Reason]),
+ build_copylist_3(CtrlNode,Dbg,SourceDir,Node,Rest)
+ end;
+build_copylist_3(_,_,_,_,[]) ->
+ [].
+
+%% Help function which makes a [{Type,Files},...] list where each file in Files
+%% is with full path as found from our file-system.
+build_copylist_4(SourceDir,[{Type,_Dir,Files}|Rest],Accum) ->
+ NewFiles=
+ lists:foldl(fun(FName,LocalAcc)->[filename:join(SourceDir,FName)|LocalAcc] end,
+ [],
+ Files),
+ build_copylist_4(SourceDir,Rest,[{Type,NewFiles}|Accum]);
+build_copylist_4(_,[],Accum) ->
+ Accum.
+
+
+%% Help function which copies a file using os:cmd.
+%% Returns 'ok' or {error,Reason}.
+do_copy_file(Source,Destination) ->
+ case os:type() of
+ {win32,_} ->
+ os:cmd("copy "++Source++" "++Destination), % Perhaps a test on success?
+ ok;
+ {unix,_} ->
+ os:cmd("cp "++Source++" "++Destination), % Perhaps a test on success?
+ ok
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+
+%% ==============================================================================
+%% Various help functions.
+%% ==============================================================================
+
+%% Help function going through the Nodes list and checking that only nodes
+%% mentioned in OurNodes gets returned. It also makes the nodes in the return
+%% value unique.
+remove_nodes_not_ours(Nodes,OurNodes) ->
+ remove_nodes_not_ours_2(Nodes,OurNodes,[],[]).
+
+remove_nodes_not_ours_2([Node|Rest],OurNodes,OurAcc,OtherAcc) ->
+ case lists:member(Node,OurNodes) of
+ true -> % Ok it is one of our nodes.
+ case lists:member(Node,OurAcc) of
+ true -> % Already in the list, skip.
+ remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
+ false ->
+ remove_nodes_not_ours_2(Rest,OurNodes,[Node|OurAcc],OtherAcc)
+ end;
+ false ->
+ case lists:member(Node,OtherAcc) of
+ true ->
+ remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,OtherAcc);
+ false ->
+ remove_nodes_not_ours_2(Rest,OurNodes,OurAcc,[Node|OtherAcc])
+ end
+ end;
+remove_nodes_not_ours_2([],_,OurAcc,OtherAcc) ->
+ {lists:reverse(OurAcc),lists:reverse(OtherAcc)}.
+%% ------------------------------------------------------------------------------
+
+%% Help function which returns 'true' or 'false' depending on if TracerData is
+%% meant to be used by the session handler (true) or if it supposed to be passed
+%% on to the trace system.
+is_tool_internal_tracerdata(_) -> % CURRENTLY NO INTERNAL TRACER DATA!
+ false.
+%% ------------------------------------------------------------------------------
+
+%% Help function which checks that all nodes in the first list of nodes exists
+%% in the second list of nodes. Returns 'true' or 'false'. The latter if as much
+%% as one incorrect node was found.
+check_our_nodes([Node|Rest],AllNodes) ->
+ case lists:member(Node,AllNodes) of
+ true ->
+ check_our_nodes(Rest,AllNodes);
+ false -> % Then we can stop right here.
+ false
+ end;
+check_our_nodes([],_) ->
+ true.
+%% ------------------------------------------------------------------------------
+
+%% Help function which checks that a directory actually exists. Returns 'true' or
+%% 'false'.
+check_directory_exists(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok,#file_info{type=directory}} ->
+ true;
+ _ -> % In all other cases it is not valid.
+ false
+ end.
+%% ------------------------------------------------------------------------------
+
+%% This function stops the tracing on all nodes in Nodes. Preferably Nodes is a list
+%% of only tracing runtime components. Not that there will actually be any difference
+%% since the return value does not reflect how stopping the nodes went.
+%% Returns 'ok' or {error,Reason}, the latter only in case of general failure.
+stop_all_tracing(void,Dbg,[?LOCAL_RUNTIME]) -> % The non-distributed case, and is tracing.
+ case inviso:stop_tracing() of
+ {ok,_State} ->
+ ok;
+ {error,Reason} -> % We actually don't care.
+ inviso_tool_lib:debug(stop_tracing,Dbg,[?LOCAL_RUNTIME,Reason]),
+ ok
+ end;
+stop_all_tracing(void,_,_) -> % There is no local runtime started.
+ ok;
+stop_all_tracing(CtrlNode,Dbg,Nodes) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,stop_tracing,[Nodes]) of
+ {ok,Result} -> % The result is only used for debug.
+ Failed=lists:foldl(fun({N,{error,Reason}},Acc)->[{N,{error,Reason}}|Acc];
+ (_,Acc)->Acc
+ end,
+ [],
+ Result),
+ if
+ Failed==[] ->
+ ok;
+ true ->
+ inviso_tool_lib:debug(stop_tracing,Dbg,[Nodes,Failed]),
+ ok
+ end;
+ {error,Reason} ->
+ {error,{stop_tracing,Reason}}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function removing all local logs using the tracerdata to determine what
+%% logs to remove from where.
+%% There is no significant return value since it is not really clear what to do
+%% if removal went wrong. The function can make debug-reports thought.
+remove_all_local_logs(CtrlNode,TRDstorage,Nodes,Dbg) ->
+ LogSpecList=build_logspeclist_remove_logs(Nodes,TRDstorage),
+ case inviso_tool_lib:inviso_cmd(CtrlNode,delete_log,[LogSpecList]) of
+ {ok,Results} ->
+ case look_for_errors_resultlist(Results) of
+ [] -> % No errors found in the result!
+ true;
+ Errors ->
+ inviso_tool_lib:debug(remove_all_local_logs,Dbg,[Errors]),
+ true
+ end;
+ {error,Reason} -> % Some general error.
+ inviso_tool_lib:debug(remove_all_local_logs,Dbg,[{error,Reason}]),
+ true
+ end.
+
+%% Help function which puts together a list of {Node,Tracerdata} tuples. Note that
+%% we must build one tuple for each tracerdata for one node.
+build_logspeclist_remove_logs(Nodes,TRDstorage) ->
+ [{Node,TracerData}||Node<-Nodes,TracerData<-find_tracerdata_for_node_trd(Node,TRDstorage)].
+%% ------------------------------------------------------------------------------
+
+%% Help function which traverses a resultlist from an inviso function. Such are
+%% built up as [{Node,SubResults},...] where SubResult is a list of tuples for each
+%% file-type (e.g trace_log) {FType,FileList} where a FileList is either {error,Reason}
+%% or {ok,FileName}.
+%% Returns a list of {Node,[{error,Reason},...]}.
+look_for_errors_resultlist([{Node,{error,Reason}}|Rest]) ->
+ [{Node,{error,Reason}}|look_for_errors_resultlist(Rest)];
+look_for_errors_resultlist([{Node,{ok,NResults}}|Rest]) when list(NResults) ->
+ case look_for_errors_resultlist_2(NResults,[]) of
+ [] ->
+ look_for_errors_resultlist(Rest);
+ Errors -> % A list of lists.
+ [{Node,lists:flatten(Errors)}|look_for_errors_resultlist(Rest)]
+ end;
+look_for_errors_resultlist([_|Rest]) ->
+ look_for_errors_resultlist(Rest);
+look_for_errors_resultlist([]) ->
+ [].
+
+look_for_errors_resultlist_2([{_FType,NSubResult}|Rest],Accum) ->
+ case lists:filter(fun({error,_Reason})->true;(_)->false end,NSubResult) of
+ [] -> % No errors for this node.
+ look_for_errors_resultlist_2(Rest,Accum);
+ Errors -> % A list of at least one error.
+ look_for_errors_resultlist_2(Rest,[Errors|Accum])
+ end;
+look_for_errors_resultlist_2([],Accum) ->
+ Accum.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the loopdata structure.
+%% Its main purpose is to store information about runtime components participating
+%% in the session and their current status.
+%% ------------------------------------------------------------------------------
+
+-record(ld,{parent,
+ ctrlnode,
+ ctrlpid, % To where to send inviso cmd.
+ rtstates,
+ tracerdata,
+ safetycatches,
+ dbg,
+ actstorage % Activity storage, for reactivate.
+ }).
+
+%% Function creating the initial datastructure.
+%% The datastructure is [{Node,State},...].
+%%
+%% The tracerdata table is a bag simply for the reason that if we try to insert
+%% the same tracerdata for a node twice, we will end up with one tracerdata after
+%% all. This is useful when we insert tracerdata ourselves, the tracerdata will
+%% come as a state-change too.
+mk_ld(Parent,CtrlNode,CtrlPid,RTStates,NodeParams,OtherNodes,SafetyCatches,Dbg) ->
+ TRDtableName=list_to_atom("inviso_tool_sh_trdstorage_"++pid_to_list(self())),
+ TRDtid=ets:new(TRDtableName,[bag]),
+ ACTtableName=list_to_atom("inviso_tool_sh_actstorage_"++pid_to_list(self())),
+ ACTtid=ets:new(ACTtableName,[bag]),
+ mk_ld_fill_tracerdata(CtrlNode,TRDtid,NodeParams,OtherNodes), % Fill the ETS table.
+ #ld{parent=Parent, % The tool main process.
+ ctrlnode=CtrlNode, % Node name where the control component is.
+ ctrlpid=CtrlPid, % The process id of the control component.
+ rtstates=RTStates, % All nodes and their state/status.
+ tracerdata=TRDtid,
+ safetycatches=SafetyCatches,
+ dbg=Dbg,
+ actstorage=ACTtid
+ }.
+
+%% Help function which inserts tracer data for the nodes. Note that we can get
+%% tracer data either from the return value from init_tracing or by asking the
+%% node for it. The latter is necessary for the nodes which were marked not to
+%% be initiated by the session handler. This maybe because those nodes have
+%% autostarted.
+mk_ld_fill_tracerdata(CtrlNode,TId,NodeParams,OtherNodes) ->
+ mk_ld_fill_tracerdata_nodeparams(TId,NodeParams),
+ mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,OtherNodes).
+
+mk_ld_fill_tracerdata_nodeparams(TId,[{Node,TracerData}|Rest]) ->
+ ets:insert(TId,{Node,TracerData}),
+ mk_ld_fill_tracerdata_nodeparams(TId,Rest);
+mk_ld_fill_tracerdata_nodeparams(_,[]) ->
+ ok.
+
+mk_ld_fill_tracerdata_othernodes(_,_,[]) -> % Then not necessary to do anything.
+ ok;
+mk_ld_fill_tracerdata_othernodes(void,TId,[Node]) -> % The non-distributed case.
+ case inviso:get_tracerdata() of
+ {error,_Reason} -> % Perhaps in state new or disconnected.
+ ok; % Do nothing.
+ {ok,TracerData} ->
+ ets:insert(TId,{Node,TracerData})
+ end;
+mk_ld_fill_tracerdata_othernodes(CtrlNode,TId,Nodes) ->
+ case inviso_tool_lib:invisomd(CtrlNode,get_tracerdata,[Nodes]) of
+ {ok,Results} ->
+ mk_ld_fill_tracerdata_othernodes_2(TId,Results);
+ {error,_Reason} -> % Strange, we will probably crash later.
+ ok
+ end.
+
+mk_ld_fill_tracerdata_othernodes_2(TId,[{_Node,{ok,no_tracerdata}}|Rest]) ->
+ mk_ld_fill_tracerdata_othernodes_2(TId,Rest); % It was not initiated then!
+mk_ld_fill_tracerdata_othernodes_2(TId,[{Node,{ok,TracerData}}|Rest]) ->
+ ets:insert(TId,{Node,TracerData}),
+ mk_ld_fill_tracerdata_othernodes_2(TId,Rest);
+mk_ld_fill_tracerdata_othernodes_2(_,[]) ->
+ ok.
+%% ------------------------------------------------------------------------------
+
+get_ctrlnode_ld(#ld{ctrlnode=CtrlNode}) ->
+ CtrlNode.
+%% ------------------------------------------------------------------------------
+
+
+get_ctrlpid_ld(#ld{ctrlpid=CtrlPid}) ->
+ CtrlPid.
+%% ------------------------------------------------------------------------------
+
+get_rtstates_ld(#ld{rtstates=RTStates}) ->
+ RTStates.
+
+put_rtstates_ld(NewRTStates,LD) ->
+ LD#ld{rtstates=NewRTStates}.
+%% ------------------------------------------------------------------------------
+
+get_trdstorage_ld(#ld{tracerdata=TId}) ->
+ TId.
+
+put_trdstorage_ld(_NewTId,LD) ->
+ LD.
+%% ------------------------------------------------------------------------------
+
+%% Help function which adds the current tracerdata of node Node to the tracerdata
+%% storage. We only want to add tracerdata we have not seen before. We therefore
+%% avoid adding it if the node already is in state ?TRACING.
+%% Returns a new tracerdata (what ever it is)!
+add_current_tracerdata_ld(CtrlNode,Node,RTStates,TId) ->
+ case get_statestatus_rtstates(Node,RTStates) of
+ {ok,{?TRACING,_}} -> % Then we have already added the tracerdata.
+ TId; % Then do nothing.
+ {ok,_} -> % Since we were not tracing before.
+ case add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) of
+ {ok,TracerData} ->
+ ets:insert(TId,{Node,TracerData});
+ no_tracerdata -> % Strange, how could we become tracing
+ ok;
+ {error,_Reason} -> % The node perhaps disconnected!?
+ ok
+ end;
+ false -> % Very strange, not our node!
+ ok % Do nothing.
+ end.
+
+add_current_tracerdata_ld_fetchtracerdata(void,_Node) ->
+ case inviso:get_tracerdata() of
+ {ok,TracerData} ->
+ {ok,TracerData};
+ {error,no_tracerdata} ->
+ no_tracerdata;
+ {error,Reason} ->
+ {error,Reason}
+ end;
+add_current_tracerdata_ld_fetchtracerdata(CtrlNode,Node) ->
+ case inviso_tool_lib:inviso_cmd(CtrlNode,get_tracerdata,[[Node]]) of
+ {ok,[{Node,{ok,TracerData}}]} ->
+ {ok,TracerData};
+ {ok,[{Node,{error,no_tracerdata}}]} ->
+ no_tracerdata;
+ {ok,[{Node,{error,Reason}}]} ->
+ {error,Reason};
+ {error,Reason} ->
+ {error,Reason}
+ end.
+%% ------------------------------------------------------------------------------
+
+
+get_safetycatches_ld(#ld{safetycatches=SCs}) ->
+ SCs.
+%% ------------------------------------------------------------------------------
+
+get_dbg_ld(#ld{dbg=Dbg}) ->
+ Dbg.
+%% ------------------------------------------------------------------------------
+
+get_actstorage_ld(#ld{actstorage=ACTstorage}) ->
+ ACTstorage.
+
+put_actstorage_ld(_NewACTstorage,LD) ->
+ LD.
+%% ------------------------------------------------------------------------------
+
+
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the rtstates structure (which is a substructure of loopdata).
+%% It is either:
+%% [{Node,StateStatus,Opts},...]
+%% Node is either the node name of the runtime component erlang node or
+%% ?LOCAL_RUNTIME as returned from the trace control component.
+%% StateStatus is {State,Status}, 'unavailable' or 'unknown'.
+%% Status is the returnvalue from trace control component.
+%% i.e: running | {suspended,Reason}
+%% ------------------------------------------------------------------------------
+
+%% Function contructing an rtstates structure from a list of [{Node,StateStatus,Opts},...].
+to_rtstates(ListOfStates) when list(ListOfStates) ->
+ ListOfStates.
+%% ------------------------------------------------------------------------------
+
+%% Function which takes a rtstates structure and returns a list of [{Node,StateStatus},...].
+from_rtstates(RTStates) ->
+ RTStates.
+%% ------------------------------------------------------------------------------
+
+%% Function which takes an rtstates structure and a result as returned from
+%% init_tracing. The RTStates is modified for the nodes that changed state as a
+%% result of successful init_tracing.
+%% Returns a new RTStates.
+set_tracing_rtstates([E={Node,_StateStatus,Opts}|Rest],Result) ->
+ case lists:keysearch(Node,1,Result) of
+ {value,{_,ok}} -> % Means state-change to tracing!
+ [{Node,{tracing,running},Opts}|set_tracing_rtstates(Rest,Result)];
+ _ -> % Otherwise, leave it as is.
+ [E|set_tracing_rtstates(Rest,Result)]
+ end;
+set_tracing_rtstates([],_Result) ->
+ [].
+%% ------------------------------------------------------------------------------
+
+%% Function updating the state/status for a certain runtime component.
+%% Returns a new RTStates structure. Note that Node must not necessarily be one
+%% of the nodes in the session. Meaning that Node shall not be added to RTStates
+%% should it not already be in there.
+statechange_rtstates(Node,State,Status,RTStates) when list(RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,_,Opts}} ->
+ lists:keyreplace(Node,1,RTStates,{Node,{State,Status},Opts});
+ _ -> % Then Node does not exist.
+ RTStates % Just keep it as is, as keyreplace would have done.
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Function updating the state/status for a certain runtime component. The
+%% state/status is set to 'unavailable'.
+%% Returns a new RTStates structure.
+set_unavailable_rtstates(Node,RTStates) when list(RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,_,Opts}} ->
+ lists:keyreplace(Node,1,RTStates,{Node,unavailable,Opts});
+ _ -> % Then Node does not exist.
+ RTStates % Just keep it as is, as keyreplace would have done.
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Function finding the statestatus associated with Node in the RTStates structure.
+%% Returns {ok,StateStatus} or 'false'.
+get_statestatus_rtstates(Node,RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,StateStatus,_}} ->
+ {ok,StateStatus};
+ false ->
+ false
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function which returns a list of all nodes that are currently marked
+%% as available to us in the runtime state structure.
+get_all_available_nodes_rtstates(RTStates) ->
+ get_all_session_nodes_rtstates(lists:filter(fun({_N,unavailable,_})->false;
+ (_)->true
+ end,
+ RTStates)).
+%% ------------------------------------------------------------------------------
+
+%% Help function returning a list of all nodes belonging to this session.
+get_all_session_nodes_rtstates(RTStates) ->
+ lists:map(fun({Node,_,_})->Node end,RTStates).
+%% ------------------------------------------------------------------------------
+
+%% Function which returns a list of nodes that are indicated as tracing in the
+%% RTStates structure.
+get_all_tracing_nodes_rtstates(RTStates) ->
+ lists:map(fun({N,_,_})->N end,
+ lists:filter(fun({_,{tracing,_},_})->true;(_)->false end,RTStates)).
+%% ------------------------------------------------------------------------------
+
+%% Returns the options associated with Node in the RTStates structure.
+get_opts_rtstates(Node,RTStates) ->
+ case lists:keysearch(Node,1,RTStates) of
+ {value,{_,_,Opts}} ->
+ {ok,Opts};
+ false ->
+ false
+ end.
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the tracerdata structure, which is a part of the loopdata.
+%% The tracerdata structure is an ETS-table of type bag storing:
+%% {Node,TracerData}.
+%% Note that there can of course be multiple entries for a node.
+%% ------------------------------------------------------------------------------
+
+%% Help function which takes a tracerdata loopdata structure and returns a list
+%% of all stored tracerdata for a certain Node.
+find_tracerdata_for_node_trd(Node,TRD) ->
+ case ets:lookup(TRD,Node) of
+ Result when list(Result) ->
+ lists:map(fun({_Node,TracerData})->TracerData end,Result);
+ _ -> % Should probably never happend.
+ []
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% ------------------------------------------------------------------------------
+%% Functions working on the activity storage structure, which is part of the
+%% loopdata. It stores entries about things that needs to be "redone" in case
+%% of a reactivation of the node. The time order is also important.
+%% Note that for every ActivityType there must be a "handler" in the reactivation
+%% functionality.
+%%
+%% The structure is a bag of {Node,ActivityType,What}.
+%% ActivityType/What=tf/{Op,TraceConfList}|tpm/{Op,[Mod,Func,Arity,MS,CallFunc]}
+%% /{Op,[Mod,Func,Arity,MS,CallFunc,ReturnFunc]}
+%% /{Op,[]}
+%% TraceConfList=[{Proc,Flags},...]
+%% How=true|false
+%% ------------------------------------------------------------------------------
+
+%% Function that adds meta-pattern activities to the activity storage. Note
+%% that one of the parameters to the function is a return value from an
+%% inviso call. In that way we do not enter activities that were unsuccessful.
+%% Op can be either the setting or clearing of a meta pattern.
+%% Returns a new ACTstorage.
+add_tpm_actstorage([{Node,ok}|Rest],Op,InvisoCmdParams,ACTstorage) ->
+ true=ets:insert(ACTstorage,{Node,tpm,{Op,InvisoCmdParams}}),
+ add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
+add_tpm_actstorage([_|Rest],Op,InvisoCmdParams,ACTstorage) ->
+ add_tpm_actstorage(Rest,Op,InvisoCmdParams,ACTstorage);
+add_tpm_actstorage([],_,_,ACTstorage) ->
+ ACTstorage.
+
+%% Function that adds process trace-flags to the activity storage. Note that one
+%% of the parameters is the return value from an inviso function. Meaning that
+%% if the flags failed in their entirety, no activity will be saved. If only
+%% some of the flags failed, we will not go through the effort of trying to find
+%% out exactly which.
+%% Returns a new activity storage structure.
+add_tf_actstorage([{_Node,{error,_Reason}}|Rest],Op,TraceConfList,ACTstorage) ->
+ add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
+add_tf_actstorage([{Node,_Result}|Rest],Op,TraceConfList,ACTstorage) ->
+ true=ets:insert(ACTstorage,{Node,tf,{Op,TraceConfList}}),
+ add_tf_actstorage(Rest,Op,TraceConfList,ACTstorage);
+add_tf_actstorage([],_,_,ACTstorage) ->
+ ACTstorage.
+%% ------------------------------------------------------------------------------
+
+%% Finds all activities associated with Node. Returns a list of them in the
+%% same order as they were inserted.
+get_activities_actstorage(Node,ACTstorage) ->
+ case ets:lookup(ACTstorage,Node) of
+ [] ->
+ false;
+ Result when list(Result) ->
+ {ok,lists:map(fun({_N,Type,What})->{Type,What} end,Result)}
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Function removing all activity entries associated with Node. This is useful
+%% if the Node disconnects for instance.
+del_node_actstorage(Node,ACTstorage) ->
+ ets:delete(ACTstorage,Node),
+ ACTstorage.
+%% ------------------------------------------------------------------------------
+
diff --git a/lib/inviso/test/Makefile b/lib/inviso/test/Makefile
index 27fe99703a..c1df29d631 100644
--- a/lib/inviso/test/Makefile
+++ b/lib/inviso/test/Makefile
@@ -52,8 +52,8 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) inviso.spec $(ERL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/inviso/test/inviso.cover b/lib/inviso/test/inviso.cover
new file mode 100644
index 0000000000..e23b9fa59b
--- /dev/null
+++ b/lib/inviso/test/inviso.cover
@@ -0,0 +1,2 @@
+{incl_app,inviso,details}.
+
diff --git a/lib/inviso/test/inviso.spec b/lib/inviso/test/inviso.spec
index d655771d64..49f9b0b460 100644
--- a/lib/inviso/test/inviso.spec
+++ b/lib/inviso/test/inviso.spec
@@ -1 +1 @@
-{topcase, {dir, "../inviso_test"}}.
+{suites,"../inviso_test",all}.
diff --git a/lib/inviso/test/inviso_tool_SUITE.erl b/lib/inviso/test/inviso_tool_SUITE.erl
index 206e117c86..6b16e506eb 100644
--- a/lib/inviso/test/inviso_tool_SUITE.erl
+++ b/lib/inviso/test/inviso_tool_SUITE.erl
@@ -1,45 +1,54 @@
-% ``The contents of this file are subject to the Erlang Public License,
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
+%% retrieved online at http://www.erlang.org/.
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
+%%
+%% %CopyrightEnd%
+%%
%%
%% Description:
%% Test suite for the inviso_tool. It is here assumed that inviso works
%% properly.
%%
%% Authors:
-%% Lennart �hman, [email protected]
+%% Lennart Öhman, [email protected]
%% -----------------------------------------------------------------------------
-module(inviso_tool_SUITE).
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
-define(l,?line).
-all(suite) ->
- [
- dist_basic_1,
- dist_rtc,
- dist_reconnect,
- dist_adopt,
- dist_history,
- dist_start_session_special
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [dist_basic_1, dist_rtc, dist_reconnect, dist_adopt,
+ dist_history, dist_start_session_special].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%% -----------------------------------------------------------------------------
init_per_suite(Config) ->
@@ -89,26 +98,26 @@ init_per_testcase(_Case,Config) ->
insert_timetraphandle_config(TH,NewConfig2).
%% -----------------------------------------------------------------------------
-fin_per_testcase(_Case,Config) ->
+end_per_testcase(_Case,Config) ->
?l test_server:stop_node(get_remotenode_config(inviso1,Config)),
?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
?l case whereis(inviso_tool) of % In case inviso_tool did not stop.
- Pid when pid(Pid) ->
+ Pid when is_pid(Pid) ->
?l io:format("Had to kill inviso_tool!~n",[]),
?l exit(Pid,kill);
_ ->
true
end,
?l case whereis(inviso_rt) of % In case we ran a runtime here.
- Pid2 when pid(Pid2) ->
+ Pid2 when is_pid(Pid2) ->
?l io:format("Had to kill inviso_rt!~n",[]),
?l exit(Pid2,kill);
_ ->
true
end,
?l case whereis(inviso_c) of % In case we ran the controll component here.
- Pid3 when pid(Pid3) ->
+ Pid3 when is_pid(Pid3) ->
?l io:format("Had to kill inviso_c!~n",[]),
?l exit(Pid3,kill);
_ ->
@@ -187,7 +196,7 @@ fin_per_testcase(_Case,Config) ->
%% TEST CASE: Basic, distributed, start of inviso_tool with simple tracing.
dist_basic_1(doc) -> ["Simple test"];
dist_basic_1(suite) -> [];
-dist_basic_1(Config) when list(Config) ->
+dist_basic_1(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=node(),
@@ -220,6 +229,10 @@ dist_basic_1(Config) when list(Config) ->
Nodes),
%% Start a test process at every node with a runtime component.
?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
+
+ %% Let the processes start
+ timer:sleep(100),
+
%% Find the pids of the test processes.
?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
Nodes),
@@ -312,7 +325,7 @@ dist_basic_1(Config) when list(Config) ->
inviso_tool:get_autostart_data(Nodes,{dependency,infinity}),
?l true=check_noderesults(Nodes,
fun({_N,{ok,{[{dependency,infinity}],{tdg,{_M,_F,TDlist}}}}})
- when list(TDlist)->
+ when is_list(TDlist)->
true;
(_) ->
false
@@ -500,6 +513,10 @@ dist_rtc(Config) when is_list(Config) ->
Nodes),
%% Start a test process at every node with a runtime component.
?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
+
+ %% Let the processes start
+ timer:sleep(100),
+
%% Find the pids of the test processes.
?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
Nodes),
@@ -537,7 +554,7 @@ dist_rtc(Config) when is_list(Config) ->
%% This test case tests mainly that reconnect and reinitiations of a node works.
dist_reconnect(doc) -> [""];
dist_reconnect(suite) -> [];
-dist_reconnect(Config) when list(Config) ->
+dist_reconnect(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|OtherNodes]=RemoteNodes,
CNode=node(),
@@ -553,6 +570,10 @@ dist_reconnect(Config) when list(Config) ->
?l start_inviso_tool_session(CNode,[],1,Nodes),
%% Start a test process at every node with a runtime component.
?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
+
+ %% Let the processes start
+ timer:sleep(100),
+
%% Find the pids of the test processes.
?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_tool_test_proc]) end,
Nodes),
@@ -574,7 +595,7 @@ dist_reconnect(Config) when list(Config) ->
%% than RexExpNode.
?l {ok,NodeResults1}=inviso_tool:inviso(tp,["application.*",module_info,0,[]]),
?l true=check_noderesults(OtherNodes,
- fun({_N,{ok,Ints}}) when list(Ints) ->
+ fun({_N,{ok,Ints}}) when is_list(Ints) ->
NrOfModules=lists:sum(Ints),
true;
(_) ->
@@ -589,9 +610,9 @@ dist_reconnect(Config) when list(Config) ->
%% Now it is time to restart the crashed node and reconnect it and then
%% finally reinitiate it.
- ?l RegExpNodeString=atom_to_list(RegExpNode),
- ?l {match,Pos,1}=regexp:first_match(RegExpNodeString,"@"),
- ?l RegExpNodeName=list_to_atom(lists:sublist(RegExpNodeString,Pos-1)),
+ ?l RegExpNodeString=atom_to_list(RegExpNode),
+ ?l [NodeNameString,_HostNameString] = string:tokens(RegExpNodeString,[$@]),
+ ?l RegExpNodeName=list_to_atom(NodeNameString),
?l test_server:start_node(RegExpNodeName,peer,[]),
?l ok=poll(net_adm,ping,[RegExpNode],pong,20),
?l SuiteDir=filename:dirname(code:which(?MODULE)),
@@ -605,7 +626,7 @@ dist_reconnect(Config) when list(Config) ->
?l ok=poll(rpc,
call,
[RegExpNode,erlang,whereis,[inviso_tool_test_proc]],
- fun(P) when pid(P) -> true;
+ fun(P) when is_pid(P) -> true;
(undefined) -> false
end,
10),
@@ -664,7 +685,7 @@ dist_reconnect(Config) when list(Config) ->
%% mark it as tracing-running.
dist_adopt(doc) -> [""];
dist_adopt(suite) -> [];
-dist_adopt(Config) when list(Config) ->
+dist_adopt(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=node(),
@@ -734,7 +755,7 @@ dist_adopt(Config) when list(Config) ->
%% This test tests that saving and restoring a history works.
dist_history(doc) -> [""];
dist_history(suite) -> [];
-dist_history(Config) when list(Config) ->
+dist_history(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=RegExpNode, % We use a remote control component.
@@ -883,7 +904,7 @@ dist_history(Config) when list(Config) ->
%% are no nodes that can be initiated or reinitiated.
dist_start_session_special(doc) -> [""];
dist_start_session_special(suite) -> [];
-dist_start_session_special(Config) when list(Config) ->
+dist_start_session_special(Config) when is_list(Config) ->
RemoteNodes=get_remotenodes_config(Config),
[RegExpNode|_]=RemoteNodes,
CNode=RegExpNode, % We use a remote control component.
@@ -998,7 +1019,7 @@ stop_inviso_tool_session(CNode,SessionNr,Nodes) ->
%% Help function checking that there is a Result for each node in Nodes.
%% Returns 'true' if successful.
-check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when function(Fun) ->
+check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
case Fun({Node,Result}) of
true ->
case lists:member(Node,Nodes) of
@@ -1031,7 +1052,7 @@ poll(_,_,_,_,0) ->
error;
poll(M,F,Args,Result,Times) ->
try apply(M,F,Args) of
- What when function(Result) ->
+ What when is_function(Result) ->
case Result(What) of
true ->
ok;
diff --git a/lib/inviso/vsn.mk b/lib/inviso/vsn.mk
index cab3bc0ff3..79093597fe 100644
--- a/lib/inviso/vsn.mk
+++ b/lib/inviso/vsn.mk
@@ -1 +1 @@
-INVISO_VSN = 0.6.1
+INVISO_VSN = 0.6.2
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index 977a7a7f98..962be63968 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -30,6 +30,54 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.5.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Some malformed distribution messages could cause VM to
+ crash, this is now corrected.</p>
+ <p>
+ Own Id: OTP-8993</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Jinterface 1.5.3.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The OtpMbox class did not have a hash() method, which it
+ should have because it overrides equals().</p>
+ <p>
+ Own Id: OTP-8854</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Jinterface 1.5.3.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ An pom.xml file is now generated. (Thanks to Gabor
+ Liptak.)</p>
+ <p>
+ Own Id: OTP-8841</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.5.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/jinterface/java_src/Makefile b/lib/jinterface/java_src/Makefile
index 37a57352ad..755ef46a8b 100644
--- a/lib/jinterface/java_src/Makefile
+++ b/lib/jinterface/java_src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2000-2009. All Rights Reserved.
+# Copyright Ericsson AB 2000-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -35,7 +35,21 @@ VSN=$(JINTERFACE_VSN)
SPECIAL_TARGETS =
+TARGET_FILES= $(POM_TARGET)
+SPECIAL_TARGETS =
+
+POM_FILE= pom.xml
+
+POM_TARGET= ../$(POM_FILE)
+POM_SRC= $(POM_FILE).src
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+$(POM_TARGET): $(POM_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
# ----------------------------------------------------
# Default Subdir Targets
@@ -43,7 +57,7 @@ SPECIAL_TARGETS =
.PHONY: debug opt instr release docs release_docs tests release_tests clean depend
-debug opt instr release docs release_docs tests release_tests clean depend:
+debug opt instr release docs release_docs tests release_tests clean depend: $(TARGET_FILES)
set -e; set -x; \
case "$(MAKE)" in *clearmake*) tflag="-T";; *) tflag="";; esac; \
if test -f com/ericsson/otp/erlang/ignore_config_record.inf; then xflag=$$tflag; fi; \
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
index ab0b299bf9..9ba6a4a0ab 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -68,7 +68,6 @@ public abstract class AbstractConnection extends Thread {
protected static final int sendTag = 2;
protected static final int exitTag = 3;
protected static final int unlinkTag = 4;
- protected static final int nodeLinkTag = 5;
protected static final int regSendTag = 6;
protected static final int groupLeaderTag = 7;
protected static final int exit2Tag = 8;
@@ -697,7 +696,6 @@ public abstract class AbstractConnection extends Thread {
// absolutely no idea what to do with these, so we ignore
// them...
case groupLeaderTag: // { GROUPLEADER, FromPid, ToPid}
- case nodeLinkTag: // { NODELINK }
// (just show trace)
if (traceLevel >= ctrlThreshold) {
System.out.println("<- " + headerType(head) + " "
@@ -880,9 +878,6 @@ public abstract class AbstractConnection extends Thread {
case unlinkTag:
return "UNLINK";
- case nodeLinkTag:
- return "NODELINK";
-
case regSendTag:
return "REG_SEND";
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
index 3bb678c2cc..deac528133 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpEpmd.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -71,11 +71,6 @@ public class OtpEpmd {
// common values
private static final byte stopReq = (byte) 115;
- // version specific value
- private static final byte port3req = (byte) 112;
- private static final byte publish3req = (byte) 97;
- private static final byte publish3ok = (byte) 89;
-
private static final byte port4req = (byte) 122;
private static final byte port4resp = (byte) 119;
private static final byte publish4req = (byte) 120;
@@ -123,11 +118,7 @@ public class OtpEpmd {
* if there was no response from the name server.
*/
public static int lookupPort(final AbstractNode node) throws IOException {
- try {
return r4_lookupPort(node);
- } catch (final IOException e) {
- return r3_lookupPort(node);
- }
}
/**
@@ -147,11 +138,7 @@ public class OtpEpmd {
throws IOException {
Socket s = null;
- try {
- s = r4_publish(node);
- } catch (final IOException e) {
- s = r3_publish(node);
- }
+ s = r4_publish(node);
node.setEpmd(s);
@@ -196,67 +183,6 @@ public class OtpEpmd {
}
}
- private static int r3_lookupPort(final AbstractNode node)
- throws IOException {
- int port = 0;
- Socket s = null;
-
- try {
- final OtpOutputStream obuf = new OtpOutputStream();
- s = new Socket(node.host(), EpmdPort.get());
-
- // build and send epmd request
- // length[2], tag[1], alivename[n] (length = n+1)
- obuf.write2BE(node.alive().length() + 1);
- obuf.write1(port3req);
- obuf.writeN(node.alive().getBytes());
-
- // send request
- obuf.writeTo(s.getOutputStream());
-
- if (traceLevel >= traceThreshold) {
- System.out.println("-> LOOKUP (r3) " + node);
- }
-
- // receive and decode reply
- final byte[] tmpbuf = new byte[100];
-
- s.getInputStream().read(tmpbuf);
- final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
-
- port = ibuf.read2BE();
- } catch (final IOException e) {
- if (traceLevel >= traceThreshold) {
- System.out.println("<- (no response)");
- }
- throw new IOException("Nameserver not responding on " + node.host()
- + " when looking up " + node.alive());
- } catch (final OtpErlangDecodeException e) {
- if (traceLevel >= traceThreshold) {
- System.out.println("<- (invalid response)");
- }
- throw new IOException("Nameserver not responding on " + node.host()
- + " when looking up " + node.alive());
- } finally {
- try {
- if (s != null) {
- s.close();
- }
- } catch (final IOException e) { /* ignore close errors */
- }
- s = null;
- }
-
- if (traceLevel >= traceThreshold) {
- if (port == 0) {
- System.out.println("<- NOT FOUND");
- } else {
- System.out.println("<- PORT " + port);
- }
- }
- return port;
- }
-
private static int r4_lookupPort(final AbstractNode node)
throws IOException {
int port = 0;
@@ -288,8 +214,6 @@ public class OtpEpmd {
final int n = s.getInputStream().read(tmpbuf);
if (n < 0) {
- // this was an r3 node => not a failure (yet)
-
s.close();
throw new IOException("Nameserver not responding on "
+ node.host() + " when looking up " + node.alive());
@@ -342,81 +266,13 @@ public class OtpEpmd {
return port;
}
- private static Socket r3_publish(final OtpLocalNode node)
- throws IOException {
- Socket s = null;
-
- try {
- final OtpOutputStream obuf = new OtpOutputStream();
- s = new Socket((String) null, EpmdPort.get());
-
- obuf.write2BE(node.alive().length() + 3);
-
- obuf.write1(publish3req);
- obuf.write2BE(node.port());
- obuf.writeN(node.alive().getBytes());
-
- // send request
- obuf.writeTo(s.getOutputStream());
- if (traceLevel >= traceThreshold) {
- System.out.println("-> PUBLISH (r3) " + node + " port="
- + node.port());
- }
-
- final byte[] tmpbuf = new byte[100];
-
- final int n = s.getInputStream().read(tmpbuf);
-
- if (n < 0) {
- s.close();
- if (traceLevel >= traceThreshold) {
- System.out.println("<- (no response)");
- }
- return null;
- }
-
- final OtpInputStream ibuf = new OtpInputStream(tmpbuf, 0);
-
- if (ibuf.read1() == publish3ok) {
- node.creation = ibuf.read2BE();
- if (traceLevel >= traceThreshold) {
- System.out.println("<- OK");
- }
- return s; // success - don't close socket
- }
- } catch (final IOException e) {
- // epmd closed the connection = fail
- if (s != null) {
- s.close();
- }
- if (traceLevel >= traceThreshold) {
- System.out.println("<- (no response)");
- }
- throw new IOException("Nameserver not responding on " + node.host()
- + " when publishing " + node.alive());
- } catch (final OtpErlangDecodeException e) {
- if (s != null) {
- s.close();
- }
- if (traceLevel >= traceThreshold) {
- System.out.println("<- (invalid response)");
- }
- throw new IOException("Nameserver not responding on " + node.host()
- + " when publishing " + node.alive());
- }
-
- if (s != null) {
- s.close();
- }
- return null; // failure
- }
-
/*
- * this function will get an exception if it tries to talk to an r3 epmd, or
- * if something else happens that it cannot forsee. In both cases we return
- * an exception (and the caller should try again, using the r3 protocol). If
- * we manage to successfully communicate with an r4 epmd, we return either
- * the socket, or null, depending on the result.
+ * this function will get an exception if it tries to talk to a
+ * very old epmd, or if something else happens that it cannot
+ * forsee. In both cases we return an exception. We no longer
+ * support r3, so the exception is fatal. If we manage to
+ * successfully communicate with an r4 epmd, we return either the
+ * socket, or null, depending on the result.
*/
private static Socket r4_publish(final OtpLocalNode node)
throws IOException {
@@ -454,7 +310,6 @@ public class OtpEpmd {
final int n = s.getInputStream().read(tmpbuf);
if (n < 0) {
- // this was an r3 node => not a failure (yet)
if (s != null) {
s.close();
}
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
index 4146bd3ced..71a419497a 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -678,6 +678,11 @@ public class OtpMbox {
return m.self.equals(self);
}
+ @Override
+ public int hashCode() {
+ return self.hashCode();
+ }
+
/*
* called by OtpNode to deliver message to this mailbox.
*
diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
index 80d8a5ccae..6f507bf4bb 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2000-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2000-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -54,7 +54,6 @@ public class OtpMsg {
public static final int sendTag = 2;
public static final int exitTag = 3;
public static final int unlinkTag = 4;
- /* public static final int nodeLinkTag = 5; */
public static final int regSendTag = 6;
/* public static final int groupLeaderTag = 7; */
public static final int exit2Tag = 8;
diff --git a/lib/jinterface/java_src/pom.xml.src b/lib/jinterface/java_src/pom.xml.src
new file mode 100644
index 0000000000..cef49b735a
--- /dev/null
+++ b/lib/jinterface/java_src/pom.xml.src
@@ -0,0 +1,106 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.erlang.otp</groupId>
+ <artifactId>jinterface</artifactId>
+ <packaging>jar</packaging>
+ <version>%VSN%</version>
+ <name>jinterface</name>
+ <description>
+ Jinterface Java package contains java classes, which help you integrate programs written in Java with Erlang.
+ Erlang is a programming language designed at the Ericsson Computer Science Laboratory.
+ </description>
+ <url>http://erlang.org/</url>
+ <licenses>
+ <license>
+ <name>ERLANG PUBLIC LICENSE 1.1</name>
+ <url>http://www.erlang.org/EPLICENSE</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+ <scm>
+ <connection>git://github.com/erlang/otp.git</connection>
+ <developerConnection>git://github.com/erlang/otp.git</developerConnection>
+ <url>http://github.com/erlang/otp</url>
+ </scm>
+ <developers>
+ <developer>
+ <email>[email protected]</email>
+ </developer>
+ </developers>
+ <organization>
+ <name>Open Source Erlang</name>
+ <url>http://www.erlang.org/</url>
+ </organization>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>java_src</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <distributionManagement>
+ <repository>
+ <id>ossrh</id>
+ <url>http://oss.sonatype.org/service/local/staging/deploy/maven2</url>
+ </repository>
+ <snapshotRepository>
+ <id>ossrh</id>
+ <url>http://oss.sonatype.org/content/repositories/snapshots</url>
+ </snapshotRepository>
+ </distributionManagement>
+ <profiles>
+ <profile>
+ <id>release-sign-artifacts</id>
+ <activation>
+ <property>
+ <name>performRelease</name>
+ <value>true</value>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>1.0-alpha-4</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+</project>
diff --git a/lib/jinterface/test/Makefile b/lib/jinterface/test/Makefile
new file mode 100644
index 0000000000..a85d0e7411
--- /dev/null
+++ b/lib/jinterface/test/Makefile
@@ -0,0 +1,85 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/jinterface_test
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+TEST_SPEC_FILE = jinterface.spec
+COVER_FILE = jinterface.cover
+
+MODULES = nc_SUITE \
+ jinterface_SUITE
+
+GEN_MODULES = jitu
+
+ERL_FILES = $(MODULES:%=%.erl) $(GEN_MODULES:%=%.erl)
+
+GEN_TARGET_FILES = $(GEN_MODULES:%=%.$(EMULATOR))
+
+SUITE_TARGET_FILES = $(MODULES:%=%.$(EMULATOR))
+
+TARGET_FILES = \
+ $(GEN_TARGET_FILES) \
+ $(SUITE_TARGET_FILES)
+
+# ----------------------------------------------------
+# PROGRAMS
+# ----------------------------------------------------
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+tests debug opt:
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f errs core *~
+
+docs:
+
+# ----------------------------------------------------
+# Special Targets
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# Release Targets
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+
+release_docs_spec:
+
+release_tests_spec: tests
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR)
+ @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
diff --git a/lib/jinterface/test/jinterface.cover b/lib/jinterface/test/jinterface.cover
new file mode 100644
index 0000000000..d4edcd99d2
--- /dev/null
+++ b/lib/jinterface/test/jinterface.cover
@@ -0,0 +1,2 @@
+{incl_app,jinterface,details}.
+
diff --git a/lib/jinterface/test/jinterface.spec b/lib/jinterface/test/jinterface.spec
new file mode 100644
index 0000000000..99bc0f4005
--- /dev/null
+++ b/lib/jinterface/test/jinterface.spec
@@ -0,0 +1,20 @@
+%% -*- erlang -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+{suites,"../jinterface_test",all}.
diff --git a/lib/jinterface/test/jinterface_SUITE.erl b/lib/jinterface/test/jinterface_SUITE.erl
new file mode 100644
index 0000000000..82bc878112
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE.erl
@@ -0,0 +1,781 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(jinterface_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
+
+-export([nodename/1, register_and_whereis/1, get_names/1, boolean_atom/1,
+ node_ping/1, mbox_ping/1,
+ java_erlang_send_receive/1,
+ java_internal_send_receive_same_node/1,
+ java_internal_send_receive_different_nodes/1,
+ java_internal_send_receive_self/1,
+ java_link_and_exit/1, erl_link_and_exit/1,
+ erl_link_java_exit/1, java_link_erl_exit/1,
+ internal_link_linking_exits/1, internal_link_linked_exits/1,
+ internal_unlink_linking_exits/1, internal_unlink_linked_exits/1,
+ normal_exit/1, kill_mbox/1,kill_erl_proc_from_java/1,
+ kill_mbox_from_erlang/1,
+ erl_exit_with_reason_any_term/1,
+ java_exit_with_reason_any_term/1,
+ status_handler_localStatus/1, status_handler_remoteStatus/1,
+ status_handler_connAttempt/1]).
+
+-include_lib("common_test/include/ct.hrl").
+-include("test_server_line.hrl").
+
+-define(debug,true).
+-ifdef(debug).
+-define(dbg(Str,Args), io:format(Str,Args)).
+-else.
+-define(dbg(Str,Args), ok).
+-endif.
+
+-define(link_test_reason,link_test_reason).
+
+%% Test cases in MboxSendReceive.java
+-define(java_erlang_send_receive,1).
+-define(java_internal_send_receive_same_node,2).
+-define(java_internal_send_receive_different_nodes,3).
+-define(java_internal_send_receive_self,4).
+
+%% Test cases in MboxLinkUnlink.java
+-define(java_link_and_exit, 1).
+-define(erl_link_and_exit, 2).
+-define(erl_link_java_exit, 3).
+-define(java_link_erl_exit, 4).
+-define(internal_link_linking_exits, 5).
+-define(internal_link_linked_exits, 6).
+-define(internal_unlink_linking_exits,7).
+-define(internal_unlink_linked_exits,8).
+-define(normal_exit,9).
+-define(kill_mbox,10).
+-define(kill_erl_proc_from_java,11).
+-define(kill_mbox_from_erlang,12).
+-define(erl_exit_with_reason_any_term,13).
+-define(java_exit_with_reason_any_term,14).
+
+
+%% Test cases in NodeStatusHandler.java
+-define(status_handler_localStatus,1).
+-define(status_handler_remoteStatus,2).
+-define(status_handler_connAttempt,3).
+
+%%%-----------------------------------------------------------------
+%%% INIT/END
+%%%-----------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ lists:append([fundamental(), ping(), send_receive(),
+ link_unlink(), status_handler()]).
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+fundamental() ->
+ [
+ nodename, % Nodename.java
+ register_and_whereis, % RegisterAndWhereis.java
+ get_names, % GetNames.java
+ boolean_atom % BooleanAtom.java
+ ].
+
+ping() ->
+ [
+ %% Implemented in NodePing.java
+ node_ping,
+
+ %% Implemented in MboxPing.java
+ mbox_ping
+ ].
+
+
+send_receive() ->
+ [
+ %% Implemented in MboxSendReceive.java
+ java_erlang_send_receive,
+ java_internal_send_receive_same_node,
+ java_internal_send_receive_different_nodes,
+ java_internal_send_receive_self
+ ].
+
+%% Note:
+%%
+%% The test cases in MboxLinkUnlink.java and in
+%% NodePing.java, all uses default cookie, and if there
+%% is a problem with having the same default cookie in
+%% erlang vs jinterface, e.g because the home directory
+%% does not get the same in some cases on Windows
+%% - they will all fail.
+
+link_unlink() ->
+ [
+ %% Implemented in MboxLinkUnlink.java
+ java_link_and_exit,
+ erl_link_and_exit,
+ erl_link_java_exit,
+ java_link_erl_exit,
+ internal_link_linking_exits,
+ internal_link_linked_exits,
+ internal_unlink_linking_exits,
+ internal_unlink_linked_exits,
+ normal_exit,
+ kill_mbox,
+ kill_erl_proc_from_java,
+ kill_mbox_from_erlang,
+ erl_exit_with_reason_any_term,
+ java_exit_with_reason_any_term
+ ].
+
+status_handler() ->
+ [
+ %% Implemented in NodeStatusHandler.java
+ status_handler_localStatus,
+ status_handler_remoteStatus,
+ status_handler_connAttempt
+ ].
+
+
+init_per_suite(Config) when is_list(Config) ->
+ case case code:priv_dir(jinterface) of
+ {error,bad_name} -> false;
+ P -> filelib:is_dir(P) end of
+ true ->
+ jitu:init_all(Config);
+ false ->
+ {skip,"No jinterface application"}
+ end.
+
+end_per_suite(Config) when is_list(Config) ->
+ jitu:finish_all(Config).
+
+init_per_testcase(Case, _Config)
+ when Case =:= kill_mbox;
+ Case =:= kill_mbox_from_erlang ->
+ {skip, "Not yet implemented"};
+init_per_testcase(_Case,Config) ->
+ Dog = ?t:timetrap({seconds,10}),
+ [{watch_dog,Dog}|Config].
+
+end_per_testcase(_Case,Config) ->
+ ?t:timetrap_cancel(?config(watch_dog,Config)),
+ ok.
+
+
+%%%-----------------------------------------------------------------
+%%% TEST CASES
+%%%-----------------------------------------------------------------
+nodename(doc) ->
+ ["Nodename.java: "
+ "Test OtpNode.node(), OtpNode.alive() and OtpNode.host()"];
+nodename(suite) ->
+ [];
+nodename(Config) when is_list(Config) ->
+ [_,Host] = string:tokens(atom_to_list(node()),"@"),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "Nodename",
+ [list_to_atom(Host)]).
+
+%%%-----------------------------------------------------------------
+register_and_whereis(doc) ->
+ ["RegisterAndWhereis.java: "
+ "Test OtpNode.registerName(...), OtpMbox.registerName(...) and "
+ "OtpNode.whereis(...)"];
+register_and_whereis(suite) ->
+ [];
+register_and_whereis(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "RegisterAndWhereis",
+ []).
+
+%%%-----------------------------------------------------------------
+get_names(doc) ->
+ ["GetNames.java: "
+ "Test OtpNode.getNames()"];
+get_names(suite) ->
+ [];
+get_names(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "GetNames",
+ []).
+
+%%%-----------------------------------------------------------------
+boolean_atom(doc) ->
+ ["BooleanAtom.java: "
+ "Test OtpErlangAtom.booleanValue()"];
+boolean_atom(suite) ->
+ [];
+boolean_atom(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "BooleanAtom",
+ []).
+
+%%%-----------------------------------------------------------------
+node_ping(doc) ->
+ ["NodePing.java: "
+ "Test OtpNode.ping(java.lang.String node, long timeout)"];
+node_ping(suite) ->
+ [];
+node_ping(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "NodePing",
+ [erlang:get_cookie(),node()]).
+
+%%%-----------------------------------------------------------------
+mbox_ping(doc) ->
+ ["MboxPing.java: "
+ "Test OtpNode.createMbox(...) and OtpMbox.ping(...)"];
+mbox_ping(suite) ->
+ [];
+mbox_ping(Config) when is_list(Config) ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "MboxPing",
+ [erlang:get_cookie(),node()]).
+
+%%%-----------------------------------------------------------------
+java_erlang_send_receive(doc) ->
+ ["Test sending/receiving of erlang messages between erlang and java"];
+java_erlang_send_receive(suite) ->
+ [];
+java_erlang_send_receive(Config) when is_list(Config) ->
+ send_receive(?java_erlang_send_receive, fun echo_loop/0, Config).
+
+echo_loop() ->
+ receive
+ {From,Msg} ->
+ ?dbg("erl_send_receive_server received ~p",[{From,Msg}]),
+ ?dbg("erl_send_receive_server sending ~p",[Msg]),
+ From ! Msg,
+ echo_loop();
+ done ->
+ ok
+ end.
+
+
+%%%-----------------------------------------------------------------
+java_internal_send_receive_same_node(doc) ->
+ ["MboxSendReceive.java: "
+ "Test sending/receiving of erlang messages between mboxes "
+ "on the same java node."];
+java_internal_send_receive_same_node(suite) ->
+ [];
+java_internal_send_receive_same_node(Config) when is_list(Config) ->
+ send_receive(?java_internal_send_receive_same_node,
+ fun() -> receive done -> ok end end,
+ Config).
+
+%%%-----------------------------------------------------------------
+java_internal_send_receive_different_nodes(doc) ->
+ ["MboxSendReceive.java: "
+ "Test sending/receiving of erlang messages between mboxes "
+ "on different java nodes."];
+java_internal_send_receive_different_nodes(suite) ->
+ [];
+java_internal_send_receive_different_nodes(Config) when is_list(Config) ->
+ send_receive(?java_internal_send_receive_different_nodes,
+ fun() -> receive done -> ok end end,
+ Config).
+
+%%%-----------------------------------------------------------------
+java_internal_send_receive_self(doc) ->
+ ["MboxSendReceive.java: "
+ "Test sending/receiving of erlang messages from an mbox to itself"];
+java_internal_send_receive_self(suite) ->
+ [];
+java_internal_send_receive_self(Config) when is_list(Config) ->
+ send_receive(?java_internal_send_receive_self,
+ fun() -> receive done -> ok end end,
+ Config).
+
+%%%-----------------------------------------------------------------
+java_link_and_exit(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link between erlang process and java mailbox."
+ "Java mailbox links and exits"];
+java_link_and_exit(suite) ->
+ [];
+java_link_and_exit(Config) when is_list(Config) ->
+ LinkFun =
+ fun(Mbox) ->
+ Mbox ! {?java_link_and_exit,self(),?link_test_reason},
+ receive after infinity -> ok end
+ end,
+ erl_java_link(LinkFun,java_link_and_exit,Config).
+
+
+%%%-----------------------------------------------------------------
+erl_link_and_exit(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link between erlang process and java mailbox."
+ "Erlang process links and exits"];
+erl_link_and_exit(suite) ->
+ [];
+erl_link_and_exit(Config) when is_list(Config) ->
+ LinkFun = fun(Mbox) ->
+ link(Mbox),
+ Mbox ! {?erl_link_and_exit,self(),?link_test_reason},
+ receive ok -> ok end,
+ exit(?link_test_reason)
+ end,
+ erl_java_link(LinkFun,erl_link_and_exit,Config).
+
+%%%-----------------------------------------------------------------
+erl_link_java_exit(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link between erlang process and java mailbox."
+ "Erlang process links and java mailbox exits"];
+erl_link_java_exit(suite) ->
+ [];
+erl_link_java_exit(Config) when is_list(Config) ->
+ LinkFun = fun(Mbox) ->
+ link(Mbox),
+ Mbox ! {?erl_link_java_exit,self(),?link_test_reason},
+ receive after infinity -> ok end
+ end,
+ erl_java_link(LinkFun,erl_link_java_exit,Config).
+
+
+%%%-----------------------------------------------------------------
+java_link_erl_exit(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link between erlang process and java mailbox."
+ "Java mailbox links and erlang process exits"];
+java_link_erl_exit(suite) ->
+ [];
+java_link_erl_exit(Config) when is_list(Config) ->
+ LinkFun =
+ fun(Mbox) ->
+ Mbox ! {?java_link_erl_exit,self(),?link_test_reason},
+ receive ok -> ok end,
+ exit(?link_test_reason)
+ end,
+ erl_java_link(LinkFun,java_link_erl_exit,Config).
+
+%%%-----------------------------------------------------------------
+internal_link_linking_exits(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link between two java mailboxes."
+ "The mailbox which creates the link is the one exiting"];
+internal_link_linking_exits(suite) ->
+ [];
+internal_link_linking_exits(Config) when is_list(Config) ->
+ internal_link_unlink(?internal_link_linking_exits,
+ internal_link_linking_exits,
+ Config).
+
+%%%-----------------------------------------------------------------
+internal_link_linked_exits(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link between two java mailboxes."
+ "The mailbox which dies not create the link is the one exiting"];
+internal_link_linked_exits(suite) ->
+ [];
+internal_link_linked_exits(Config) when is_list(Config) ->
+ internal_link_unlink(?internal_link_linked_exits,
+ internal_link_linked_exits,
+ Config).
+
+%%%-----------------------------------------------------------------
+internal_unlink_linking_exits(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link and unlink between two java mailboxes. "
+ "Mailbox1 creates a link to mailbox2 and removes it. "
+ "Then it creates another link and mailbox2 removes is. "
+ "Finally mailbox1 exits - mailbox2 must survive"];
+internal_unlink_linking_exits(suite) ->
+ [];
+internal_unlink_linking_exits(Config) when is_list(Config) ->
+ internal_link_unlink(?internal_unlink_linking_exits,
+ internal_unlink_linking_exits,
+ Config).
+
+%%%-----------------------------------------------------------------
+internal_unlink_linked_exits(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test link and unlink between two java mailboxes. "
+ "Mailbox1 creates a link to mailbox2 and removes it. "
+ "Then it creates another link and mailbox2 removes is. "
+ "Finally mailbox2 exits - mailbox1 must survive"];
+internal_unlink_linked_exits(suite) ->
+ [];
+internal_unlink_linked_exits(Config) when is_list(Config) ->
+ internal_link_unlink(?internal_unlink_linked_exits,
+ internal_unlink_linked_exits,
+ Config).
+
+%%%-----------------------------------------------------------------
+normal_exit(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test that mbox.close() uses exit reason 'normal', i.e. "
+ "that linked processes are not terminated."];
+normal_exit(suite) ->
+ [];
+normal_exit(Config) when is_list(Config) ->
+ Fun =
+ fun() ->
+ register(erl_link_server,self()),
+ process_flag(trap_exit,true),
+ receive
+ {Main,Mbox} when is_pid(Main), is_pid(Mbox) ->
+ ?dbg("Erlang sending \"~p\"",[normal_exit]),
+ link(Mbox),
+ Pid = spawn_link(fun() ->
+ link(Mbox),
+ Mbox ! {?normal_exit},
+ receive after infinity -> ok end
+ end),
+ receive
+ {'EXIT',Mbox,normal} ->
+ %% Make sure that we don't get the
+ %% exit signal from Pid, and Pid
+ %% should still be alive.
+ receive
+ {'EXIT',Pid,Reason} ->
+ ?dbg("Got unexpected exit signal: ~p",
+ [{'EXIT',Pid,Reason}]),
+ exit({unexpected,{'EXIT',Pid,Reason}})
+ after 500 ->
+ true = erlang:is_process_alive(Pid),
+ exit(Pid,kill)
+ end,
+ receive done -> Main ! done end
+ after 1000 ->
+ receive
+ Other ->
+ ?dbg("Got garbage when waiting for exit:"
+ " ~p", [Other]),
+ Main ! done,
+ exit({got_unexpected,Other})
+ after 0 ->
+ ok
+ end
+ end;
+ Other ->
+ ?dbg("Got garbage: ~p",[Other]),
+ exit(Other)
+ end
+ end,
+
+ spawn_link(Fun),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "MboxLinkUnlink",
+ [erlang:get_cookie(),node()]).
+
+
+%%%-----------------------------------------------------------------
+kill_mbox(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test that mbox.exit(new OtpErlangAtom(\"kill\") causes linked "
+ "processes to exit with reason 'killed', which can be trapped."];
+kill_mbox(suite) ->
+ {skip, "Not yet implemented"};
+kill_mbox(Config) when is_list(Config) ->
+ Fun =
+ fun() ->
+ register(erl_link_server,self()),
+ process_flag(trap_exit,true),
+ receive
+ {Main,Mbox} when is_pid(Main), is_pid(Mbox) ->
+ ?dbg("Erlang sending \"~p\"",[kill_mbox]),
+ Pid = spawn_link(fun() ->
+ process_flag(trap_exit,true),
+ link(Mbox),
+ Mbox ! {?kill_mbox},
+ receive
+ {'EXIT',Mbox,killed} ->
+ exit(correct_reason);
+ {'EXIT',Mbox,R} ->
+ exit({faulty_reason,R})
+ end
+ end),
+ receive
+ {'EXIT',Pid,{faulty_reason,Reason}} ->
+ receive done -> Main ! done end,
+ exit({faulty_reason,Reason});
+ {'EXIT',Pid,im_killed} ->
+ receive done -> Main ! done end
+ after 1000 ->
+ receive
+ Other ->
+ ?dbg("Got garbage when waiting for exit:"
+ " ~p", [Other]),
+ Main ! done,
+ exit({got_unexpected,Other})
+ after 0 ->
+ ok
+ end
+ end;
+ Other ->
+ ?dbg("Got garbage: ~p",[Other]),
+ exit(Other)
+ end
+ end,
+
+ spawn_link(Fun),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "MboxLinkUnlink",
+ [erlang:get_cookie(),node()]).
+
+%%%-----------------------------------------------------------------
+kill_erl_proc_from_java(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test that mbox.exit(pid, new OtpErlangAtom(\"kill\") causes erlang "
+ "processes <pid> to be killed, even if trapping exits"];
+kill_erl_proc_from_java(suite) ->
+ [];
+kill_erl_proc_from_java(Config) when is_list(Config) ->
+ LinkFun = fun(Mbox) ->
+ process_flag(trap_exit,true),
+ link(Mbox),
+ Mbox ! {?kill_erl_proc_from_java, self()},
+ receive after infinity -> ok end
+ end,
+ erl_java_link(LinkFun,kill_erl_proc_from_java,killed,Config).
+
+%%%-----------------------------------------------------------------
+kill_mbox_from_erlang(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test that exit(Mbox,kill) causes linked the Mbox to be killed, and"
+ "linked processes to exit with reason 'killed', even if trapping exits"];
+kill_mbox_from_erlang(suite) ->
+ {skip, "Not yet implemented"};
+kill_mbox_from_erlang(Config) when is_list(Config) ->
+ LinkFun = fun(Mbox) ->
+ link(Mbox),
+ Mbox ! {?kill_mbox_from_erlang},
+ exit(Mbox,kill),
+ receive after infinity -> ok end
+ end,
+ erl_java_link(LinkFun,kill_mbox_from_erlang,killed,Config).
+
+%%%-----------------------------------------------------------------
+erl_exit_with_reason_any_term(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test that any erlang term can be used as exit reason when erlang "
+ "process exits and is linked to an mbox."];
+erl_exit_with_reason_any_term(suite) ->
+ [];
+erl_exit_with_reason_any_term(Config) when is_list(Config) ->
+ Reason = [hei,self(),{this,is,"a",[different,"reason"]}],
+ LinkFun = fun(Mbox) ->
+ link(Mbox),
+ Mbox ! {?erl_exit_with_reason_any_term,self(),Reason},
+ receive ok -> ok end,
+ exit(Reason)
+ end,
+ erl_java_link(LinkFun,erl_exit_with_reason_any_term,Reason,Config).
+
+%%%-----------------------------------------------------------------
+java_exit_with_reason_any_term(doc) ->
+ ["MboxLinkUnlink.java: "
+ "Test that any erlang term can be used as exit reason when mbox "
+ "exits and is linked to an erlang process."];
+java_exit_with_reason_any_term(suite) ->
+ [];
+java_exit_with_reason_any_term(Config) when is_list(Config) ->
+ Reason = [hei,self(),{this,is,"a",[different,"reason"]}],
+ LinkFun =
+ fun(Mbox) ->
+ Mbox ! {?java_exit_with_reason_any_term,self(),Reason},
+ receive after infinity -> ok end
+ end,
+ erl_java_link(LinkFun,java_exit_with_reason_any_term,Reason,Config).
+
+
+%%%-----------------------------------------------------------------
+status_handler_localStatus(doc) ->
+ ["NodeStatusHandler.java: "
+ "Test OtpNode.registerStatusHandler(...) and the callback "
+ "OtpNodeStatus.localStatus(...)"];
+status_handler_localStatus(suite) ->
+ [];
+status_handler_localStatus(Config) when is_list(Config) ->
+ spawn_link(fun() ->
+ erl_status_server([{opt,{localStatus,"javanode1",true}},
+ {localStatus,"javanode1",false}])
+ end),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "NodeStatusHandler",
+ [erlang:get_cookie(),node(),?status_handler_localStatus]).
+
+%%%-----------------------------------------------------------------
+status_handler_remoteStatus(doc) ->
+ ["NodeStatusHandler.java: "
+ "Test OtpNode.registerStatusHandler(...) and the callback "
+ "OtpNodeStatus.remoteStatus(...)"];
+status_handler_remoteStatus(suite) ->
+ [];
+status_handler_remoteStatus(Config) when is_list(Config) ->
+ spawn_link(fun() ->
+ erl_status_server([{opt,{localStatus,"javanode1",true}},
+ {remoteStatus,"javanode2",true},
+ {remoteStatus,"javanode2",false}])
+ end),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "NodeStatusHandler",
+ [erlang:get_cookie(),node(),?status_handler_remoteStatus]).
+
+
+%%%-----------------------------------------------------------------
+status_handler_connAttempt(doc) ->
+ ["NodeStatusHandler.java: "
+ "Test OtpNode.registerStatusHandler(...) and the callback "
+ "OtpNodeStatus.connAttempt(...)"];
+status_handler_connAttempt(suite) ->
+ [];
+status_handler_connAttempt(Config) when is_list(Config) ->
+ spawn_link(fun() ->
+ erl_status_server([{opt,{localStatus,"javanode1",true}},
+ {connAttempt,"unknown",true},
+ {connAttempt,"javanode3",false}])
+ end),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "NodeStatusHandler",
+ [erlang:get_cookie(),node(),?status_handler_connAttempt]).
+
+
+%%%-----------------------------------------------------------------
+%%% INTERNAL FUNCTIONS
+%%%-----------------------------------------------------------------
+send_receive(TestCaseTag,Fun,Config) ->
+ spawn(fun() ->
+ register(erl_send_receive_server,self()),
+ receive
+ From when is_pid(From) ->
+ JavaNode = node(From),
+ [JavaNode] = nodes(hidden),
+ From ! {TestCaseTag,self()},
+ Fun(),
+ unregister(erl_send_receive_server)
+ end
+ end),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "MboxSendReceive",
+ [erlang:get_cookie(),node()]).
+
+
+internal_link_unlink(Tag,Msg,Config) ->
+ Fun =
+ fun() ->
+ register(erl_link_server,self()),
+ process_flag(trap_exit,true),
+ receive
+ {Main,Mbox} when is_pid(Main), is_pid(Mbox) ->
+ ?dbg("Erlang sending \"~p\"",[Msg]),
+ Mbox ! {Tag,self(),?link_test_reason},
+ receive done -> Main ! done end;
+ Other ->
+ ?dbg("Got garbage: ~p",[Other]),
+ exit(Other)
+ end
+ end,
+
+ spawn_link(Fun),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "MboxLinkUnlink",
+ [erlang:get_cookie(),node()]).
+
+
+erl_java_link(LinkFun,Msg,Config) ->
+ erl_java_link(LinkFun,Msg,?link_test_reason,Config).
+
+erl_java_link(LinkFun,Msg,Reason,Config) ->
+ Fun =
+ fun() ->
+ register(erl_link_server,self()),
+ process_flag(trap_exit,true),
+ receive
+ {Main,Mbox} when is_pid(Mbox), is_pid(Mbox) ->
+ ?dbg("Erlang sending \"~p\"",[Msg]),
+ Pid = spawn_link(fun() -> LinkFun(Mbox) end),
+ receive
+ {'EXIT',Pid,Reason} ->
+ receive done -> Main ! done end
+ after 1000 ->
+ receive
+ Other ->
+ ?dbg("Got garbage when waiting for exit:"
+ " ~p", [Other]),
+ Main ! done,
+ exit({got_unexpected,Other})
+ after 0 ->
+ ok
+ end
+ end;
+ Other ->
+ ?dbg("Got garbage: ~p",[Other]),
+ exit(Other)
+ end
+ end,
+
+ spawn_link(Fun),
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ "MboxLinkUnlink",
+ [erlang:get_cookie(),node()]).
+
+erl_status_server(List) ->
+ register(erl_status_server,self()),
+ erl_status_server(List,undefined).
+erl_status_server([{opt,{Tag,NodeName,Up}},{Tag2,NodeName2,Up2}|Rest],_) ->
+ receive
+ {Tag,Node,Up,From} = M ->
+ ?dbg("erl_status_server got: ~p",[M]),
+ true = lists:prefix(NodeName,Node),
+ erl_status_server([{Tag2,NodeName2,Up2}|Rest],From);
+ {Tag2,Node2,Up2,From2} = M2 ->
+ ?dbg("erl_status_server got: ~p",[M2]),
+ true = lists:prefix(NodeName2,Node2),
+ erl_status_server(Rest,From2)
+ end;
+erl_status_server([{Tag,NodeName,Up}|Rest],_) ->
+ receive
+ {Tag,Node,Up,From} = M ->
+ ?dbg("erl_status_server got: ~p",[M]),
+ true = lists:prefix(NodeName,Node),
+ erl_status_server(Rest,From);
+ Other ->
+ ?dbg("erl_status_server got garbage: ~p",[Other]),
+ exit(Other)
+ end;
+erl_status_server([],From) ->
+ From ! done.
diff --git a/lib/jinterface/test/jinterface_SUITE_data/BooleanAtom.java b/lib/jinterface/test/jinterface_SUITE_data/BooleanAtom.java
new file mode 100644
index 0000000000..9554d50c9f
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/BooleanAtom.java
@@ -0,0 +1,46 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class BooleanAtom {
+
+ /*
+ Implements test case jinterface_SUITE:boolean_atom/1
+
+ Test the function OtpErlangAtom.booleanValue()
+ */
+
+ public static void main(String argv[]) {
+
+ OtpErlangAtom atom = new OtpErlangAtom("true");
+ if (!atom.booleanValue()) fail(1);
+
+ atom = new OtpErlangAtom("false");
+ if (atom.booleanValue()) fail(2);
+
+ atom = new OtpErlangAtom("somethingelse");
+ if (atom.booleanValue()) fail(3);
+
+ }
+
+ private static void fail(int reason) {
+ System.exit(reason);
+ }
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/GetNames.java b/lib/jinterface/test/jinterface_SUITE_data/GetNames.java
new file mode 100644
index 0000000000..3d2bc4ac84
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/GetNames.java
@@ -0,0 +1,68 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import java.util.ArrayList;
+import com.ericsson.otp.erlang.*;
+
+class GetNames {
+
+ /*
+ Implements test case jinterface_SUITE:get_names/1
+
+ */
+
+ public static void main(String argv[]) {
+
+ try {
+ OtpNode node = new OtpNode("javanode");
+ OtpMbox mbox1 = node.createMbox();
+ mbox1.registerName("mbox1");
+ node.createMbox("mbox2");
+ OtpMbox mbox3 = node.createMbox();
+ node.registerName("mbox3",mbox3);
+
+ ArrayList existing_names = new ArrayList();
+ existing_names.add("mbox3");
+ existing_names.add("mbox2");
+ existing_names.add("mbox1");
+
+ String[] names = node.getNames();
+ if (names.length != existing_names.size()) fail(1);
+
+ for(int i=0; i<names.length; i++) {
+ System.out.println("" + names[i]);
+ existing_names.remove(names[i]);
+ }
+
+ if (!existing_names.isEmpty()) fail(2);
+ }
+ catch (Exception e) {
+ fail("" + e, 3);
+ }
+ }
+
+ private static void fail(int reason) {
+ System.exit(reason);
+ }
+
+ private static void fail(String str, int reason) {
+ System.out.println(str);
+ System.exit(reason);
+ }
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Makefile.src b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..2a3dca463b
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/Makefile.src
@@ -0,0 +1,62 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .java
+
+
+JAVAC = @JAVAC@
+ERLC = erlc
+
+JINTERFACE_CLASSPATH = @jinterface_classpath@
+
+CLASSPATH = .@PS@$(JINTERFACE_CLASSPATH)@PS@
+
+JAVA_FILES = \
+ Nodename.java \
+ RegisterAndWhereis.java \
+ GetNames.java \
+ BooleanAtom.java \
+ NodePing.java \
+ MboxPing.java \
+ MboxSendReceive.java \
+ MboxLinkUnlink.java \
+ NodeStatusHandler.java
+
+CLASS_FILES = $(JAVA_FILES:.java=.class)
+
+all: $(CLASS_FILES)
+
+clean:
+ -rm -f $(CLASS_FILES)
+ -del /F /Q $(CLASS_FILES)
+
+$(CLASS_FILES) : $(JAVA_FILES)
+ $(JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES)
+
+#
diff --git a/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java b/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java
new file mode 100644
index 0000000000..5d1d097cc8
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/MboxLinkUnlink.java
@@ -0,0 +1,201 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class MboxLinkUnlink {
+
+ /*
+ Implements test case jinterface_SUITE:mbox_link_unlink/1
+
+
+
+ */
+
+ private static final int java_link_and_exit = 1;
+ private static final int erl_link_and_exit = 2;
+ private static final int erl_link_java_exit = 3;
+ private static final int java_link_erl_exit = 4;
+ private static final int internal_link_linking_exits = 5;
+ private static final int internal_link_linked_exits = 6;
+ private static final int internal_unlink_linking_exits = 7;
+ private static final int internal_unlink_linked_exits = 8;
+ private static final int normal_exit = 9;
+ private static final int kill_mbox = 10;
+ private static final int kill_erl_proc_from_java = 11;
+ private static final int kill_mbox_from_erlang = 12;
+ private static final int erl_exit_with_reason_any_term = 13;
+ private static final int java_exit_with_reason_any_term = 14;
+
+ private static boolean dbg = true;
+
+
+ public static void main(String argv[]) {
+
+
+ //String cookie = argv[0];
+ String erlNode = argv[1];
+ OtpErlangObject expected = null;
+ boolean waiting = false;
+
+ try { //
+ OtpNode node = new OtpNode("javanode");
+ OtpMbox mainMbox = node.createMbox();
+
+ try {
+ // Initiate and set up connection to erlang process
+ OtpMbox mbox = node.createMbox();
+ OtpMbox mbox2;
+
+ OtpErlangObject[] msg = {mainMbox.self(),mbox.self()};
+ mbox.send("erl_link_server", erlNode, new OtpErlangTuple(msg));
+ OtpErlangObject o = mbox.receive(1000);
+ if (o == null) System.exit(1);
+ OtpErlangTuple tuple = (OtpErlangTuple)o;
+ int tag = (int)((OtpErlangLong)tuple.elementAt(0)).longValue();
+
+ switch (tag) {
+
+ case java_exit_with_reason_any_term:
+ case java_link_and_exit:
+ dbg("Java got \"java_link_and_exit\" or " +
+ "\"java_exit_with_reason_any_term\"");
+ mbox.link((OtpErlangPid)tuple.elementAt(1));
+ mbox.send((OtpErlangPid)tuple.elementAt(1),
+ new OtpErlangAtom("ok"));
+ mbox.exit(tuple.elementAt(2));
+ break;
+ case erl_exit_with_reason_any_term:
+ case erl_link_and_exit:
+ dbg("Java got \"erl_link_and_exit\" or " +
+ "\"erl_exit_with_reason_any_term\"");
+ mbox.send((OtpErlangPid)tuple.elementAt(1),
+ new OtpErlangAtom("ok"));
+ waiting = true;
+ expected = tuple.elementAt(2);
+ mbox.receive(1000);
+ System.exit(2);
+ case erl_link_java_exit:
+ dbg("Java got \"erl_link_java_exit\"");
+ mbox.exit(tuple.elementAt(2));
+ break;
+ case java_link_erl_exit:
+ dbg("Java got \"java_link_erl_exit\"");
+ mbox.link((OtpErlangPid)tuple.elementAt(1));
+ mbox.send((OtpErlangPid)tuple.elementAt(1),
+ new OtpErlangAtom("ok"));
+ waiting = true;
+ expected = tuple.elementAt(2);
+ mbox.receive(1000);
+ System.exit(3);
+ case internal_link_linking_exits:
+ dbg("Java got \"internal_link_linking_exits\"");
+ mbox2 = node.createMbox();
+ mbox.link(mbox2.self());
+ mbox.exit(tuple.elementAt(2));
+ waiting = true;
+ expected = tuple.elementAt(2);
+ mbox2.receive(1000); // hanging waiting for exit
+ System.exit(4); // got someting other than exit
+ case internal_link_linked_exits:
+ dbg("Java got \"internal_link_linked_exits\"");
+ mbox2 = node.createMbox();
+ mbox.link(mbox2.self());
+ mbox2.exit(tuple.elementAt(2));
+ waiting = true;
+ expected = tuple.elementAt(2);
+ mbox.receive(1000); // hanging waiting for exit
+ System.exit(5); // got someting other than exit
+ case internal_unlink_linking_exits:
+ dbg("Java got \"internal_unlink_linking_exits\"");
+ mbox2 = node.createMbox();
+ mbox.link(mbox2.self());
+ mbox.unlink(mbox2.self());
+ mbox.link(mbox2.self());
+ mbox2.unlink(mbox.self());
+ mbox2.exit(tuple.elementAt(2));
+ if (mbox.receive(500)!=null) System.exit(6);
+ break;
+ case internal_unlink_linked_exits:
+ dbg("Java got \"internal_unlink_linked_exits\"");
+ mbox2 = node.createMbox();
+ mbox.link(mbox2.self());
+ mbox.unlink(mbox2.self());
+ mbox.link(mbox2.self());
+ mbox2.unlink(mbox.self());
+ mbox.exit(tuple.elementAt(2));
+ if (mbox2.receive(500)!=null) System.exit(7);
+ break;
+ case normal_exit:
+ dbg("Java got \"normal_exit\"");
+ mbox.close();
+ break;
+ case kill_mbox:
+ dbg("Java got \"kill_mbox\"");
+ mbox.exit("kill");
+ break;
+ case kill_erl_proc_from_java:
+ dbg("Java got \"kill_erl_proc_from_java\"");
+ mbox.exit((OtpErlangPid)tuple.elementAt(1),"kill");
+ break;
+ case kill_mbox_from_erlang:
+ dbg("Java got \"kill_mbox_from_erlang\"");
+ /* This will make the testcase successful, but it is
+ not the correct way to do it...
+ Mbox should probably just die when the kill signal is
+ received from erlang (or other mbox).
+
+ try {
+ mbox.receive(1000);
+ System.exit(8);
+ }
+ catch (OtpErlangExit exit) {
+ if(!(exit.reason().equals(new OtpErlangAtom("kill"))))
+ System.exit(9);
+ mbox.exit("killed");
+ }
+ */
+ break;
+ }
+ }
+ catch (OtpErlangExit exit) {
+ dbg("Java got exit: " + exit.reason());
+ if(!(waiting && exit.reason().equals(expected)))
+ System.exit(10);
+ }
+
+ OtpErlangAtom done = new OtpErlangAtom("done");
+ mainMbox.send("erl_link_server", erlNode, done);
+ OtpErlangObject o = mainMbox.receive(1000);
+ if (o == null) System.exit(11);
+ else if (!((OtpErlangAtom)o).equals(done))
+ System.exit(12);
+
+ }
+ catch (Exception e) {
+ System.out.println("EXCEPTION: " + e);
+ System.exit(13);
+ }
+ }
+
+ private static void dbg(String str) {
+ if (dbg) System.out.println(str);
+ }
+
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/MboxPing.java b/lib/jinterface/test/jinterface_SUITE_data/MboxPing.java
new file mode 100644
index 0000000000..3a8497028e
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/MboxPing.java
@@ -0,0 +1,50 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class MboxPing {
+
+ /*
+ Implements test case jinterface_SUITE:mbox_ping/1
+
+ Creates an OtpNode object with an OtpMbox object. The test_server
+ node is pinged from the OtpMbox.
+ */
+
+ public static void main(String argv[]) {
+
+ try {
+ OtpNode node = new OtpNode("javanode",argv[0]);
+ OtpMbox mbox = node.createMbox();
+ if (mbox.ping(argv[1],2000)) {
+ System.out.println("OtpMbox.ping(" + argv[1] + ") -> true");
+ }
+ else {
+ System.out.println("ERROR: OtpMbox.ping(" + argv[1] +
+ ") -> false");
+ System.exit(1);
+ }
+ }
+ catch (Exception e) {
+ System.out.println("" + e);
+ System.exit(2);
+ }
+ }
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java b/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java
new file mode 100644
index 0000000000..2db71bb5cd
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/MboxSendReceive.java
@@ -0,0 +1,229 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class MboxSendReceive {
+
+ /*
+ Implements test case jinterface_SUITE:mbox_send_receive/1
+
+ Test OtpMbox.send(...) and OtpMbox.receive(...)
+ */
+
+ private static final boolean dbg = true;
+ private static final int recTime = 2000;
+
+ private static final int java_erlang_send_receive = 1;
+ private static final int java_internal_send_receive_same_node = 2;
+ private static final int java_internal_send_receive_different_nodes = 3;
+ private static final int java_internal_send_receive_self = 4;
+
+ public static void main(String argv[]) {
+
+ String cookie = argv[0];
+ String erlNode = argv[1];
+
+ OtpErlangObject[] msgArray = new OtpErlangObject[2];
+ msgArray[1] = new OtpErlangAtom("hello world");
+ OtpErlangTuple msg = null;
+
+ try {
+ // Initiate: create javanode and mboxes
+ OtpNode node = new OtpNode("javanode",cookie);
+ OtpMbox mbox = node.createMbox();
+ OtpMbox mbox2 = node.createMbox("java_echo_server2");
+
+ // Send the pid of mbox to erlang and wait for test case
+ // instruction: {TestCaseTag, Pid}
+ mbox.send("erl_send_receive_server", erlNode, mbox.self());
+ OtpErlangObject o = mbox.receive(recTime);
+ if (o == null) System.exit(1);
+ OtpErlangTuple testCase = (OtpErlangTuple)o;
+ dbg("mbox received " + testCase);
+ int tag = (int)((OtpErlangLong)testCase.elementAt(0)).longValue();
+ OtpErlangPid erlangPid = (OtpErlangPid)testCase.elementAt(1);
+
+ switch (tag) {
+
+ case java_erlang_send_receive:
+
+ // Test1 (happened during initiation):
+ // Send mbox pid to erlang process with registered name.
+ // Erlang process sent back its pid to the mbox pid.
+
+ // Test2: Register name and sent it to the erlang pid. Erlang
+ // process shall send message back to my registered name.
+
+ mbox.registerName("java_echo_server");
+ msgArray[0] = getNameNode("java_echo_server",node);
+ msg = new OtpErlangTuple(msgArray);
+
+ dbg("java_echo_server sending " + msg);
+ mbox.send(erlangPid,msg);
+
+ o = mbox.receive(recTime);
+ dbg("java_echo_server received " + o);
+ if (o == null) System.exit(2);
+ if (!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(3);
+
+ // Test3: Same as Test2, but using a new mbox2 which
+ // got its name already when it is created - i.e. not
+ // using mbox.registerName
+ msgArray[0] = getNameNode("java_echo_server2",node);
+ msg = new OtpErlangTuple(msgArray);
+
+ dbg("java_echo_server2 sending " + msg);
+ mbox2.send(erlangPid,msg);
+
+ o = mbox2.receive(recTime);
+ dbg("java_echo_server received " + o);
+ if (o == null) System.exit(4);
+ if (!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(5);
+
+ break;
+
+ case java_internal_send_receive_same_node:
+
+ // Test1: Sending message between mboxes on same node
+ // given registered name and node without host.
+ mbox.send("java_echo_server2","javanode",msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(6);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(7);
+
+ // Test2: Sending message between mboxes on same node
+ // given registered name and node with host.
+ mbox.send("java_echo_server2",mbox2.self().node(),msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(8);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(9);
+
+ // Test3: Sending message between mboxes on same node
+ // given registered name but not node.
+ mbox.send("java_echo_server2",msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(10);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(11);
+
+ // Test4: Sending message between mboxes on same node
+ // given pid.
+ mbox.send(mbox2.self(),msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(12);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(13);
+
+ break;
+
+ case java_internal_send_receive_different_nodes:
+
+ OtpNode node2 = new OtpNode("javanode2", cookie);
+ OtpMbox mboxOtherNode = node2.createMbox("mboxOtherNode");
+
+ // Test1: Sending message between mboxes on different
+ // nodes given registered name and node without host.
+ mbox.send("mboxOtherNode","javanode2",msgArray[1]);
+ o = mboxOtherNode.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(14);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(15);
+
+ // Test2: Sending message between mboxes on different
+ // nodes given registered name and node with host.
+ mbox.send("mboxOtherNode",mboxOtherNode.self().node(),
+ msgArray[1]);
+ o = mboxOtherNode.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(16);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(17);
+
+ // Test3: Sending message between mboxes on different
+ // nodes given pid.
+ mbox.send(mboxOtherNode.self(),msgArray[1]);
+ o = mboxOtherNode.receive(recTime);
+ dbg("Mbox at same node: " + o);
+ if (o == null) System.exit(18);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(19);
+
+ break;
+
+
+ case java_internal_send_receive_self:
+
+ // Test1: Sending message to myself given registered
+ // name and node without host.
+ mbox2.send("java_echo_server2","javanode",msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Self: " + o);
+ if (o == null) System.exit(18);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(19);
+
+ // Test2: Sending message to myself given registered
+ // name and node with host.
+ mbox2.send("java_echo_server2",mbox2.self().node(),msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Self: " + o);
+ if (o == null) System.exit(20);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(21);
+
+ // Test3: Sending message to myself given registered
+ // name but not host.
+ mbox2.send("java_echo_server2",msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Self: " + o);
+ if (o == null) System.exit(22);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(23);
+
+ // Test4: Sending message to myself given pid.
+ mbox2.send(mbox2.self(),msgArray[1]);
+ o = mbox2.receive(recTime);
+ dbg("Self: " + o);
+ if (o == null) System.exit(24);
+ if(!((OtpErlangAtom)o).equals(msgArray[1])) System.exit(25);
+
+ break;
+
+ }
+
+ // Closing erl_send_receive_server by sending the atom 'done' to it.
+ mbox.send(erlangPid,new OtpErlangAtom("done"));
+ }
+ catch (Exception e) {
+ System.out.println("" + e);
+ System.exit(26);
+ }
+ }
+
+ private static OtpErlangTuple getNameNode(String mboxName,OtpNode node) {
+ OtpErlangObject[] array = {new OtpErlangAtom(mboxName),
+ new OtpErlangAtom(node.node())};
+ return new OtpErlangTuple(array);
+
+ }
+
+ private static void dbg(String str) {
+ if (dbg) System.out.println(str);
+ }
+
+
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/NodePing.java b/lib/jinterface/test/jinterface_SUITE_data/NodePing.java
new file mode 100644
index 0000000000..d0df5c46b5
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/NodePing.java
@@ -0,0 +1,83 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class NodePing {
+
+ /*
+ Implements test case jinterface_SUITE:node_ping/1
+
+ Creates three OtpNode objects. One with default cookie, one with
+ specified same cookie as the node running the test case and one
+ with a faulty cookie. From each OtpNode object the test_server
+ node is pinged.
+
+ Also the default cookie node pings itself, and the node with the
+ specified cookie pings the node with default cookie.
+ */
+
+ public static void main(String argv[]) {
+
+ String cookie = argv[0];
+ String erlNode = argv[1];
+
+ try {
+ OtpNode node1 = new OtpNode("javanode1");
+ ping(node1,erlNode,"Default cookie:",true,1);
+ ping(node1,node1.node(),"Self:",true,2);
+ ping(node1,"javanode1","Self:",true,3);
+
+ OtpNode node2 = new OtpNode("javanode2",cookie);
+ ping(node2,erlNode,"Specified cookie:",true,4);
+ ping(node2,"javanode1","Javanode (no host):",true,5);
+ ping(node2,node1.node(),"Javanode:",true,6);
+
+ OtpNode node3 = new OtpNode("javanode3","faultycookie");
+ ping(node3,erlNode,"Faulty cookie:",false,7);
+
+ // Test OtpNode.cookie() and OtpNode.setCookie(cookie) as well
+ if (!node3.cookie().equals("faultycookie"))
+ fail("Testing OtpNode.cookie()",8);
+ String old = node3.setCookie(cookie);
+ if (!old.equals("faultycookie"))
+ fail("Checking return of OtpNode.setCookie(cookie)",9);
+ ping(node3,erlNode,"setCookie:",true,10);
+ }
+ catch (Exception e) {
+ fail("" + e, 11);
+ }
+ }
+
+ private static void ping(OtpNode node, String remote, String descr,
+ boolean expected, int reason) {
+ if ( node.ping(remote,2000) == expected ) {
+ System.out.println(descr + " ping(" + remote + ") -> " + expected);
+ }
+ else {
+ fail("ERROR: " + descr + " ping(" + remote +") -> " + !expected,
+ reason);
+ }
+ }
+
+ private static void fail(String str, int reason) {
+ System.out.println(str);
+ System.exit(reason);
+ }
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java b/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java
new file mode 100644
index 0000000000..51ea15b5ef
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/NodeStatusHandler.java
@@ -0,0 +1,168 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+public class NodeStatusHandler extends OtpNodeStatus {
+ /*
+ Implements java side of test cases in jinterface_SUITE.erl
+
+ Test OtpNode.registerStatusHandler(...) and class OtpNodeStatus.
+ */
+
+ private static final boolean dbg = true;
+ private static final int recTime = 2000;
+
+ private static String erlNode = null;
+ private static String cookie = null;
+ private static OtpMbox mbox = null;
+
+ private static final int status_handler_localStatus = 1;
+ private static final int status_handler_remoteStatus = 2;
+ private static final int status_handler_connAttempt = 3;
+
+ public static void main(String argv[]) {
+
+ cookie = argv[0];
+ erlNode = argv[1];
+
+ try {
+ OtpNode javaNode = new OtpNode("javanode", cookie);
+ mbox = javaNode.createMbox();
+ }
+ catch (Exception e) {
+ dbg("EXCEPTION when creating javanode: " + e);
+ System.exit(1);
+ }
+
+ try {
+ OtpNode node1 = new OtpNode("javanode1", cookie);
+ node1.registerStatusHandler(new NodeStatusHandler());
+
+ switch (Integer.parseInt(argv[2])) {
+
+ case status_handler_localStatus:
+ dbg("java running test case \"status_handler_localStatus\"");
+
+ Thread.sleep(200); // Give 'nodeup' message a chance
+ // before closing
+ node1.close();
+ Thread.sleep(500);
+ break;
+
+ case status_handler_remoteStatus:
+ dbg("java running test case \"status_handler_remoteStatus\"");
+
+ OtpNode node2 = new OtpNode("javanode2", cookie);
+ node2.ping(node1.node(),2000);
+ node2.close();
+ Thread.sleep(500);
+ break;
+
+ case status_handler_connAttempt:
+ dbg("java running test case \"status_handler_connAttempt\"");
+
+ OtpNode node3 = new OtpNode("javanode3","othercookie");
+ node3.ping(node1.node(),2000);
+ node1.ping(node3.node(),2000);
+ break;
+
+ }
+
+ OtpErlangObject o = mbox.receive(recTime);
+ if (o == null) System.exit(2);
+ if (! ((OtpErlangAtom)o).atomValue().equals("done"))
+ System.exit(3);
+
+ }
+ catch (Exception e) {
+ dbg("EXCEPTION: " + e);
+ System.exit(4);
+ }
+
+ }
+
+
+
+ public void remoteStatus(String node, boolean up, Object info) {
+ try {
+ dbg("Got remoteStatus: " + node + " " + up + " " + info);
+ OtpErlangObject[] msgArray = new OtpErlangObject[4];
+ msgArray[0] = new OtpErlangAtom("remoteStatus");
+ msgArray[1] = new OtpErlangString(node);
+ msgArray[2] = new OtpErlangBoolean(up);
+ msgArray[3] = mbox.self();
+ OtpErlangTuple msg = new OtpErlangTuple(msgArray);
+ mbox.send("erl_status_server", erlNode, msg);
+
+ }
+ catch (Exception e) {
+ dbg("EXCEPTION in remoteStatus: " + e + "\nArgs: " +
+ node + " " + up + " " + info);
+ System.exit(5);
+ }
+ }
+
+
+ public void localStatus(String node, boolean up, Object info) {
+ try {
+ dbg("Got localStatus: " + node + " " + up + " " + info);
+ OtpErlangObject[] msgArray = new OtpErlangObject[4];
+ msgArray[0] = new OtpErlangAtom("localStatus");
+ msgArray[1] = new OtpErlangString(node);
+ msgArray[2] = new OtpErlangBoolean(up);
+ msgArray[3] = mbox.self();
+ OtpErlangTuple msg = new OtpErlangTuple(msgArray);
+ mbox.send("erl_status_server", erlNode, msg);
+
+ }
+ catch (Exception e) {
+ dbg("EXCEPTION in localStatus: " + e + "\nArgs: " +
+ node + " " + up + " " + info);
+ System.exit(6);
+ }
+ }
+
+
+
+ public void connAttempt(String node, boolean incoming, Object info) {
+ try {
+ dbg("Got connAttempt: " + node + " " + incoming + " " + info);
+ OtpErlangObject[] msgArray = new OtpErlangObject[4];
+ msgArray[0] = new OtpErlangAtom("connAttempt");
+ msgArray[1] = new OtpErlangString(node);
+ msgArray[2] = new OtpErlangBoolean(incoming);
+ msgArray[3] = mbox.self();
+ OtpErlangTuple msg = new OtpErlangTuple(msgArray);
+ mbox.send("erl_status_server", erlNode, msg);
+
+ }
+ catch (Exception e) {
+ dbg("EXCEPTION in connAttempt: " + e + "\nArgs: " +
+ node + " " + incoming + " " + info);
+ System.exit(7);
+ }
+ }
+
+
+ private static void dbg(String str) {
+ if (dbg) System.out.println(str);
+ }
+
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/Nodename.java b/lib/jinterface/test/jinterface_SUITE_data/Nodename.java
new file mode 100644
index 0000000000..dc8cb9c49f
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/Nodename.java
@@ -0,0 +1,54 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class Nodename {
+
+ /*
+ Implements test case jinterface_SUITE:nodename/1
+
+ */
+
+ public static void main(String argv[]) {
+
+ String host = argv[0];
+
+ try {
+ OtpNode node = new OtpNode("javanode");
+ System.out.println("Given host: " + host +
+ " Host: " + node.host() +
+ " Alive: " + node.alive() +
+ " Node: " + node.node());
+
+ if (!node.host().equals(host)) fail(1);
+ if (!node.alive().equals("javanode")) fail(2);
+ if (!node.node().equals("javanode@" + host)) fail(3);
+ }
+ catch (Exception e) {
+ System.out.println("" + e);
+ fail(4);
+ }
+ }
+
+ private static void fail(int reason) {
+ System.exit(reason);
+ }
+
+}
diff --git a/lib/jinterface/test/jinterface_SUITE_data/RegisterAndWhereis.java b/lib/jinterface/test/jinterface_SUITE_data/RegisterAndWhereis.java
new file mode 100644
index 0000000000..9df01981b2
--- /dev/null
+++ b/lib/jinterface/test/jinterface_SUITE_data/RegisterAndWhereis.java
@@ -0,0 +1,79 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+class RegisterAndWhereis {
+
+ /*
+ Implements test case jinterface_SUITE:register_and_whereis/1
+
+ */
+
+ public static void main(String argv[]) {
+
+ try {
+ OtpNode node = new OtpNode("javanode");
+ OtpMbox mbox1 = node.createMbox();
+ mbox1.registerName("mbox1");
+ OtpMbox mbox2 = node.createMbox("mbox2");
+ OtpMbox mbox3 = node.createMbox();
+ node.registerName("mbox3",mbox3);
+
+ OtpErlangPid pid1 = mbox1.self();
+ OtpErlangPid pid2 = mbox2.self();
+ OtpErlangPid pid3 = mbox3.self();
+
+ if (!pid1.equals(node.whereis("mbox1"))) fail(1);
+ if (!pid1.equals(mbox1.whereis("mbox1"))) fail(2);
+ if (!pid1.equals(mbox2.whereis("mbox1"))) fail(3);
+ if (!pid1.equals(mbox3.whereis("mbox1"))) fail(4);
+ if (!pid2.equals(node.whereis("mbox2"))) fail(5);
+ if (!pid2.equals(mbox2.whereis("mbox2"))) fail(6);
+ if (!pid3.equals(node.whereis("mbox3"))) fail(7);
+ if (!pid3.equals(mbox3.whereis("mbox3"))) fail(8);
+
+ node.closeMbox(mbox1);
+ mbox2.close();
+
+ if (node.whereis("mbox1") != null) fail(9);
+ if (node.whereis("mbox2") != null) fail(10);
+ if (mbox3.whereis("mbox1") != null) fail(11);
+ if (mbox3.whereis("mbox2") != null) fail(12);
+
+ mbox3.close();
+ if (mbox2.whereis("mbox3") != null) fail(13);
+
+
+
+ }
+ catch (Exception e) {
+ fail("" + e, 14);
+ }
+ }
+
+ private static void fail(int reason) {
+ System.exit(reason);
+ }
+
+ private static void fail(String str, int reason) {
+ System.out.println(str);
+ System.exit(reason);
+ }
+}
diff --git a/lib/jinterface/test/jitu.erl b/lib/jinterface/test/jitu.erl
new file mode 100644
index 0000000000..c57fb9bfad
--- /dev/null
+++ b/lib/jinterface/test/jitu.erl
@@ -0,0 +1,156 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%----------------------------------------------------------------------
+%% JInterface Test Utils
+%%----------------------------------------------------------------------
+-module(jitu).
+
+
+-export([java/3,
+ java/4,
+ java/5,
+ init_all/1,
+ finish_all/1]).
+
+%%
+%% Lots of stuff here are originating from java_client_erl_server_SUITE.erl
+%% (ic) ...
+%%
+
+
+
+java(Java, Dir, ClassAndArgs) ->
+ cmd(Java++" -classpath "++classpath(Dir)++" "++ClassAndArgs).
+
+java(Java, Dir, Class, Args) ->
+ java(Java, Dir, Class++" "++to_string(Args)).
+
+java(Java, Dir, Class, Args, Props) ->
+ java(Java, Dir, Props++" "++Class, Args).
+
+
+
+
+init_all(Config) when list(Config) ->
+ case find_executable(["java"]) of
+ false -> {skip,"Found no Java VM"};
+ Path -> [{java,Path}|Config]
+ end.
+
+finish_all(Config) -> Config.
+
+%%
+%% Internal stuff...
+%%
+
+
+find_executable([]) ->
+ false;
+find_executable([E|T]) ->
+ case os:find_executable(E) of
+ false -> find_executable(T);
+ Path -> Path
+ end.
+
+to_string([H|T]) when integer(H) ->
+ integer_to_list(H)++" "++to_string(T);
+to_string([H|T]) when atom(H) ->
+ atom_to_list(H)++" "++to_string(T);
+to_string([H|T]) when pid(H) ->
+ pid_to_list(H)++" "++to_string(T);
+to_string([H|T]) when list(H) ->
+ lists:flatten(H)++" "++to_string(T);
+to_string([]) -> [].
+
+% javac(Dir, File) ->
+% cmd("javac -d "++Dir++" -classpath "++classpath(Dir)++" "++
+% filename:join(Dir, File)).
+
+classpath(Dir) ->
+ PS =
+ case os:type() of
+ {win32, _} -> ";";
+ _ -> ":"
+ end,
+ Dir++PS++
+ filename:join([code:lib_dir(jinterface),"priv","OtpErlang.jar"])++PS++
+ case os:getenv("CLASSPATH") of
+ false -> "";
+ Classpath -> Classpath
+ end.
+
+
+cmd(Cmd) ->
+ PortOpts = [{line,80},eof,exit_status,stderr_to_stdout],
+ io:format("cmd: ~s~n", [Cmd]),
+ case catch open_port({spawn,Cmd}, PortOpts) of
+ Port when port(Port) ->
+ Result = cmd_loop(Port, []),
+ io:format("cmd res: ~w~n", [Result]),
+ case Result of
+ 0 -> ok;
+ ExitCode when integer(ExitCode) -> {error,ExitCode};
+ Error -> Error
+ end;
+ {'EXIT',Reason} ->
+ {error,Reason}
+ end.
+
+cmd_loop(Port, Line) ->
+ receive
+ {Port,eof} ->
+ receive
+ {Port,{exit_status,ExitStatus}} ->
+ ExitStatus
+ after 1 ->
+ undefined
+ end;
+ {Port,{exit_status,ExitStatus}} ->
+ receive
+ {Port,eof} ->
+ ok after 1 -> ok end,
+ ExitStatus;
+ {Port,{data,{Tag,Data}}} ->
+ case Tag of
+ eol ->
+ io:put_chars([Line|cr_to_nl(Data)]),
+ io:nl(),
+ cmd_loop(Port, []);
+ noeol ->
+ cmd_loop(Port, [Line|cr_to_nl(Data)])
+ end;
+ {'EXIT',Port,Reason} ->
+ {error,Reason};
+ Other ->
+ io:format("WARNING: Unexpected at ~s:~p: ~p~n",
+ [?MODULE_STRING,?LINE,Other]),
+ cmd_loop(Port, Line)
+ end.
+
+%% Convert lonely CR to NL, and CRLF to NL
+%%
+cr_to_nl([$\r,$\n|T]) ->
+ [$\n|cr_to_nl(T)];
+cr_to_nl([$\r|T]) ->
+ [$\n|cr_to_nl(T)];
+cr_to_nl([C|T]) ->
+ [C|cr_to_nl(T)];
+cr_to_nl([]) ->
+ [].
diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl
new file mode 100644
index 0000000000..da54f5bf51
--- /dev/null
+++ b/lib/jinterface/test/nc_SUITE.erl
@@ -0,0 +1,740 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(nc_SUITE).
+
+
+-include_lib("common_test/include/ct.hrl").
+-include("test_server_line.hrl").
+
+
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ end_per_testcase/2]).
+
+-export([pid_roundtrip/1,
+ port_roundtrip/1,
+ ref_roundtrip/1,
+ new_float/1,
+ old_stuff/1,
+ binary_roundtrip/1,
+ decompress_roundtrip/1,
+ compress_roundtrip/1,
+ integer_roundtrip/1,
+ fun_roundtrip/1,
+ lists_roundtrip/1,
+ lists_roundtrip_2/1,
+ lists_iterator/1,
+ unicode/1,
+ unicode_list_to_string/1,
+ unicode_string_to_list/1,
+ connect/1]).
+
+
+%% Top of cases
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [pid_roundtrip, port_roundtrip, ref_roundtrip,
+ new_float, old_stuff, binary_roundtrip,
+ decompress_roundtrip, compress_roundtrip,
+ integer_roundtrip, fun_roundtrip, lists_roundtrip,
+ lists_roundtrip_2, lists_iterator, unicode,
+ unicode_list_to_string, unicode_string_to_list, connect].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_suite(Config) when is_list(Config) ->
+ case case code:priv_dir(jinterface) of
+ {error,bad_name} -> false;
+ P -> filelib:is_dir(P) end of
+ true ->
+ jitu:init_all(Config);
+ false ->
+ {skip,"No jinterface application"}
+ end.
+
+end_per_suite(Config) ->
+ jitu:finish_all(Config).
+
+
+
+%% Add/remove watchdog before/after each test case.
+%%
+init_per_testcase(Case, Config) ->
+ T = case atom_to_list(Case) of
+ "unicode"++_ -> 240;
+ _ -> 20
+ end,
+ WatchDog = test_server:timetrap(test_server:seconds(T)),
+ [{watchdog, WatchDog}| Config].
+
+end_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+
+%%
+%% Test cases
+%%
+
+pid_roundtrip(doc) -> [];
+pid_roundtrip(suite) -> [];
+pid_roundtrip(Config) when is_list(Config)->
+ ThisNode = {node(), erlang:system_info(creation)},
+ RemNode = {gurka@sallad, 2},
+ do_echo([self(),
+ mk_pid(ThisNode, 4711, 4711),
+ mk_pid(ThisNode, 32767, 8191),
+ mk_pid(RemNode, 4711, 4711),
+ mk_pid(RemNode, 32767, 8191)],
+ Config).
+
+fun_roundtrip(doc) -> [];
+fun_roundtrip(suite) -> [];
+fun_roundtrip(Config) when is_list(Config)->
+ do_echo([fun(A, B) -> A + B end,
+ fun(A) -> lists:reverse(A) end,
+ fun() -> ok end,
+ fun fun_roundtrip/1],
+ Config).
+
+port_roundtrip(doc) -> [];
+port_roundtrip(suite) -> [];
+port_roundtrip(Config) when is_list(Config)->
+ ThisNode = {node(), erlang:system_info(creation)},
+ RemNode = {gurka@sallad, 2},
+ do_echo([hd(erlang:ports()),
+ mk_port(ThisNode, 4711),
+ mk_port(ThisNode, 268435455),
+ mk_port(RemNode, 4711),
+ mk_port(RemNode, 268435455)],
+ Config).
+
+ref_roundtrip(doc) -> [];
+ref_roundtrip(suite) -> [];
+ref_roundtrip(Config) when is_list(Config)->
+ ThisNode = {node(), erlang:system_info(creation)},
+ RemNode = {gurka@sallad, 2},
+ do_echo([make_ref(),
+ mk_ref(ThisNode, [4711]),
+ mk_ref(ThisNode, [4711, 4711, 4711]),
+ mk_ref(ThisNode, [262143, 4294967295, 4294967295]),
+ mk_ref(RemNode, [4711]),
+ mk_ref(RemNode, [4711, 4711, 4711]),
+ mk_ref(RemNode, [262143, 4294967295, 4294967295])],
+ Config).
+
+new_float(doc) -> [];
+new_float(suite) -> [];
+new_float(Config) when is_list(Config)->
+ Two16 = float(1 bsl 16),
+ X = math:sqrt(2),
+ Floats = lists:reverse(seq(1/X, 63, fun(Y) -> Y / Two16 end),
+ [0.0|seq(X, 63, fun(Y) -> Y * Two16 end)]),
+ io:format("~w", [Floats]),
+ do_echo(Floats, Config).
+
+old_stuff(doc) -> [];
+old_stuff(suite) -> [];
+old_stuff(Config) when is_list(Config)->
+ Terms = [0.0,math:sqrt(2)],
+ OutTrans =
+ fun (D) ->
+ {self(),term_to_binary(D, [{minor_version,0}]),binary}
+ end,
+ InTrans =
+ fun (Echoer, D, {Echoer,D,binary}) ->
+ ok
+ end,
+ do_echo(Terms, Config, OutTrans, InTrans).
+
+binary_roundtrip(doc) -> [];
+binary_roundtrip(suite) -> [];
+binary_roundtrip(Config) when is_list(Config) ->
+ do_echo([<<17>>,
+ <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17>>,
+ <<3:2>>,
+ <<3,7:3>>,
+ <<>>],
+ Config).
+
+decompress_roundtrip(doc) -> [];
+decompress_roundtrip(suite) -> [];
+decompress_roundtrip(Config) when is_list(Config) ->
+ Terms =
+ [0.0,
+ math:sqrt(2),
+ <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,31:5>>,
+ make_ref()],
+ OutTrans =
+ fun (D) ->
+ {self(),term_to_binary(D, [compressed]),binary}
+ end,
+ InTrans =
+ fun (Echoer, D, {Echoer,D,binary}) ->
+ ok
+ end,
+ do_echo(Terms, Config, OutTrans, InTrans).
+
+compress_roundtrip(doc) -> [];
+compress_roundtrip(suite) -> [];
+compress_roundtrip(Config) when is_list(Config) ->
+ Terms =
+ [0.0,
+ math:sqrt(2),
+ <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,31:5>>,
+ make_ref()],
+ OutTrans =
+ fun (D) ->
+ {self(),D,compress}
+ end,
+ InTrans =
+ fun (Echoer, D, {Echoer,B,compress}) ->
+ D = binary_to_term(B)
+ end,
+ do_echo(Terms, Config, OutTrans, InTrans).
+
+
+
+integer_roundtrip(doc) -> [];
+integer_roundtrip(suite) -> [];
+integer_roundtrip(Config) when is_list(Config) ->
+ Xs = [1 bsl X || X <- [26,27,28,29,30,31,32,33,
+ 62,63,64,65,
+ 126,127,128,129]],
+ Terms = [0,1,-1,-2]
+ ++lists:flatmap(fun (X) -> [X-2,X-1,X,-X+1,-X,-X-1] end,
+ Xs),
+ io:format("~w", [Terms]),
+ OutTrans =
+ fun (V) ->
+ {self(),V,bigint}
+ end,
+ InTrans =
+ fun (Echoer, V, {Echoer,{V,W,X,L,U},bigint}) ->
+ Bitlength = bitlength(V),
+ {w,W} = {w,signum(V) * Bitlength},
+ Y = V band 16#FFFFffffFFFFffff,
+ {y,Y} = {y,X band 16#FFFFffffFFFFffff},
+ {l,L} = {l,if V =:= X, Bitlength < 64 -> 1;
+ true -> 0 end},
+ {u,U} = {u,if V =:= Y, V >= 0, Bitlength =< 64 -> 1;
+ true -> 0 end}
+ end,
+ do_echo(Terms, Config, OutTrans, InTrans).
+
+signum(V) when is_integer(V), V > 0 -> 1;
+signum(V) when is_integer(V), V < 0 -> -1;
+signum(0) -> 0.
+
+bitlength(0) ->
+ 0;
+bitlength(-1) ->
+ 0;
+bitlength(V) when is_integer(V) ->
+ 1 + bitlength(V bsr 1).
+
+
+
+lists_roundtrip(doc) -> [];
+lists_roundtrip(suite) -> [];
+lists_roundtrip(Config) when is_list(Config) ->
+ Ls = [lists:seq(1,10),
+ lists:seq(11,17)++last_tail,
+ [{default}],
+ [car|cdr],
+ [[]],
+ []],
+ do_echo(Ls, Config).
+
+
+
+lists_roundtrip_2(doc) -> [];
+lists_roundtrip_2(suite) -> [];
+lists_roundtrip_2(Config) when is_list(Config) ->
+ Ls = [{[a,b],tail},
+ {[c,d,e],tail},
+ {[],tail},
+ {[f],tail},
+ {[g,h|i],tail},
+ {[j,k,l|m],tail},
+ {[n|o],tail},
+ {[z,1,2,3,4],tail3},
+ {[z,5,6,7],tail3},
+ {[z,8,9],tail3},
+ {[z,10],tail3},
+ {[],tail3},
+ {[z,11,12,13,14|15],tail3},
+ {[z,16,17,18|19],tail3},
+ {[z,20,21|22],tail3},
+ {[z,23|24],tail3},
+ {[z|25],tail3},
+ {"abc123",sub3atom},
+ {"abc",sub3atom}
+ ],
+ Trans =
+ fun ([_|T], tail) ->
+ T;
+ (L, tail) when is_list(L) ->
+ null;
+ ([_,_,_|T], tail3) ->
+ T;
+ (L, tail3) when is_list(L) ->
+ null;
+ ([_,_,_|L], sub3atom) ->
+ list_to_atom(L)
+ end,
+ OutTrans =
+ fun ({L,Twist}) ->
+ {self(),L,Twist}
+ end,
+ InTrans =
+ fun (Echoer, {L,Twist}, {Echoer,X,Twist}) ->
+ Y = Trans(L, Twist),
+ io:format("## ~w ~w ~w ~w~n", [L,Twist,X,Y]),
+ X = Y
+ end,
+ do_echo(Ls, Config, OutTrans, InTrans).
+
+
+
+
+lists_iterator(doc) -> [];
+lists_iterator(suite) -> [];
+lists_iterator(Config) when is_list(Config) ->
+ Ls = [["able ","was ","I ","ere ","I ","saw ","elba"]],
+ do_echo(Ls, Config,
+ fun (L) -> {self(),L,strcat} end,
+ fun (Echoer, L, {Echoer,X,strcat}) ->
+ io:format("## ~p ~p~n", [L,X]),
+ X = lists:flatten(L)
+ end).
+
+
+
+unicode(doc) -> [];
+unicode(suite) -> [];
+unicode(Config) when is_list(Config) ->
+ S1 = "plain ascii",
+ S2 = "iso-latin ��� �",
+ S3 = "Codepoints... ��� \x{1000}",
+ S4 = [0,1,31,32,63,64,127,128,255],
+ S5 = [0,1,127,128,255,256,16#d7ff,
+ 16#e000,16#fffd,16#10000,16#10ffff],
+ Ss = [S1,S2,S3,S4,S5],
+ do_echo(unicode_cp_gen([{S,valid} || S <- Ss] ++ cp_gen(71)), Config,
+ fun ({L,invalid}) -> {self(),L,utf8};
+ ({L,Tag}) -> {self(),L,Tag};
+ ({L}) -> {self(),L}
+ end,
+ fun (Echoer, {L,invalid}=Out, {Echoer,X,utf8}=In) ->
+ case L of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end;
+ (Echoer, {L,Tag}=Out, {Echoer,X,Tag}=In) ->
+ case unicode:characters_to_binary(L, utf8) of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end;
+ (Echoer, {L}=Out, {Echoer,X}=In) ->
+ case L of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end
+ end, ["unicode"]).
+
+%% Lazy wrapper to lazy list
+unicode_cp_gen([{S,valid}|Ss]) ->
+ [{S,utf8},{S}|unicode_cp_gen(Ss)];
+unicode_cp_gen([{S,invalid}=St|Ss]) ->
+ [St,{S}|unicode_cp_gen(Ss)];
+unicode_cp_gen([]) ->
+ [];
+unicode_cp_gen(Cont) when is_function(Cont, 0) ->
+ fun () ->
+ unicode_cp_gen(Cont())
+ end.
+
+
+
+unicode_list_to_string(doc) -> [];
+unicode_list_to_string(suite) -> [];
+unicode_list_to_string(Config) when is_list(Config) ->
+ do_echo(cp_gen(73), Config,
+ fun ({L,_}) -> {self(),L,to_string_neg_int_list} end,
+ fun (Echoer, {L,invalid}=Out, {Echoer,X,_}=In) ->
+ case L of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end;
+ (Echoer, {L,valid}=Out, {Echoer,X,_}=In) ->
+ B = unicode:characters_to_binary(L, unicode, {utf16,big}),
+ case [-D || <<D:16/big>> <= B] of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end
+ end).
+
+
+
+unicode_string_to_list(doc) -> [];
+unicode_string_to_list(suite) -> [];
+unicode_string_to_list(Config) when is_list(Config) ->
+ do_echo(cp_gen(79), Config,
+ fun ({L,_}) -> {self(),L,to_neg_int_list} end,
+ fun (Echoer, {L,invalid}=Out, {Echoer,X,_}=In) ->
+ case L of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end;
+ (Echoer, {L,valid}=Out, {Echoer,X,_}=In) ->
+ case [-C || C <- L] of
+ X -> ok;
+ _ ->
+ ?t:fail({mismatch,Out,In})
+ end
+ end, ["unicode"]).
+
+
+%% Lazy list
+cp_gen(N) ->
+ cp_gen(N, -1, 16#110000).
+
+cp_gen(N, Start, End) ->
+ cp_gen(N, Start, End, cp_validity(Start), [], 0, [], 0).
+
+cp_gen(N, U, End, PrevValidity, Acc, Len, Ss, Ls) when Len >= N ->
+ cp_gen(N, U, End, PrevValidity, [], 0, [{Acc,PrevValidity}|Ss], Ls+1);
+cp_gen(N, U, End, PrevValidity, Acc, Len, Ss, Ls) when Ls >= N ->
+ Ss ++ fun () ->
+ cp_gen(N, U, End, PrevValidity, Acc, Len, [], 0)
+ end;
+cp_gen(_, U, End, _, Acc, _, Ss, _) when U > End ->
+ [{Acc,valid}|Ss];
+cp_gen(N, U, End, PrevValidity, Acc, Len, Ss, Ls) ->
+ Validity = cp_validity(U),
+ NextU = U+1,
+ {NextAcc,NextLen} = case Validity of
+ valid -> {[U|Acc],Len+1};
+ invalid -> {Acc,Len}
+ end,
+ {NextSs,NextLs} = case Validity of
+ PrevValidity -> {Ss,Ls};
+ valid -> {[{[U-1],PrevValidity}|Ss],Ls+1};
+ invalid -> {[{[U],Validity}|Ss],Ls+1}
+ end,
+ cp_gen(N, NextU, End, Validity, NextAcc, NextLen, NextSs, NextLs).
+
+cp_validity(UnicodeCP) ->
+ try <<UnicodeCP/big-utf32>> of
+ _ -> valid
+ catch
+ error:_ -> invalid
+ end.
+
+
+
+connect(doc) -> [];
+connect(suite) -> [];
+connect(Config) when is_list(Config) ->
+ WD = filename:dirname(code:which(?MODULE)),
+ {ok,Other} = ?t:start_node(make_name(), slave, [{args,"-pa "++WD}]),
+ Action =
+ fun (Pid) ->
+ JName = node(Pid),
+ Hidden = [JName],
+ Pid ! {self(),Other},
+ receive
+ {Pid,Other,true} ->
+ ok;
+ Unexpected1 ->
+ ?t:fail({result,Unexpected1})
+ end,
+ Hidden = erlang:nodes(hidden),
+ Hidden = rpc:call(Other, erlang, nodes, [hidden]),
+ true =
+ rpc:call(Other, erlang, disconnect_node, [JName]),
+ [] =
+ rpc:call(Other, erlang, nodes, [hidden]),
+ Hidden = erlang:nodes(hidden),
+ %% Again
+ receive after 2000 -> ok end,
+ %% We have no way of knowing when the Java node
+ %% detects the nodedown.
+ Pid ! {self(),Other},
+ receive
+ {Pid,Other,true} ->
+ ok;
+ Unexpected2->
+ ?t:fail({result,Unexpected2})
+ end,
+ Hidden = rpc:call(Other, erlang, nodes, [hidden])
+ end,
+ run_server(connection_server, Config, Action, []).
+
+
+
+seq(_, 0, _) ->
+ [];
+seq(X, N, Fun) ->
+ [X|seq(Fun(X), N-1, Fun)].
+
+do_echo(DataList, Config) ->
+ do_echo(DataList, Config,
+ fun (D) -> % OutTrans
+ {self(),D}
+ end,
+ fun (Echoer, D, {Echoer,D}) -> % InTrans
+ ok
+ end, []).
+
+do_echo(DataList, Config, OutTrans, InTrans) ->
+ do_echo(DataList, Config, OutTrans, InTrans, []).
+
+do_echo(DataList, Config, OutTrans, InTrans, ExtraArgs)
+ when is_list(DataList), is_list(Config) ->
+ run_server(echo_server, Config,
+ fun (Echoer) ->
+ echo_loop(DataList, Echoer, OutTrans, InTrans, [])
+ end,
+ ExtraArgs).
+
+echo_loop([D|Ds], Echoer, OutTrans, InTrans, TermAcc) ->
+ OutMsg = OutTrans(D),
+ Echoer ! OutMsg,
+ io:format("echo_server ~p: ~p ! ~P~n", [self(),Echoer,OutMsg,10]),
+ receive
+ Reply ->
+ io:format("echo_server ~p: receive ~P~n",
+ [self(),Reply,10]),
+ InTrans(Echoer, D, Reply)
+ end,
+ Term = case OutMsg of
+ {_, T, _} -> T;
+ {_, T} -> T
+ end,
+ echo_loop(Ds, Echoer, OutTrans, InTrans, [Term | TermAcc]);
+echo_loop([], Echoer, _, _, TermAcc) ->
+ check_terms(Echoer, TermAcc);
+%% Lazy list
+echo_loop(Cont, Echoer, OutTrans, InTrans, TermAcc)
+ when is_function(Cont, 0) ->
+ check_terms(Echoer, TermAcc),
+ OutMsg = Echoer ! {self(),undefined,hash_clear},
+ io:format("echo_server ~p: ~p ! ~P~n", [self(),Echoer,OutMsg,10]),
+ receive
+ {Echoer,hash_cleared,hash_clear}=Reply ->
+ io:format("echo_server ~p: receive ~P~n",
+ [self(),Reply,10]),
+ ok;
+ Other ->
+ io:format("echo_server_terms unexpected ~p: receive ~P~n",
+ [self(),Other,10]),
+ ?t:fail({unexpected, Other})
+ end,
+ echo_loop(Cont(), Echoer, OutTrans, InTrans, []).
+
+check_terms(Echoer, [Term | Rest]) ->
+ OutMsg = {self(),Term,hash_lookup},
+ Echoer ! OutMsg,
+ io:format("check_terms ~p: ~p ! ~P~n", [self(),Echoer,OutMsg,10]),
+ receive
+ {Echoer,true,hash_lookup} = ReplyMsg ->
+ io:format("check_terms ~p: receive ~P~n",
+ [self(),ReplyMsg,10]),
+ check_terms(Echoer, Rest);
+ Other ->
+ io:format("check_terms unexpected ~p: receive ~P~n",
+ [self(),Other,10]),
+ ?t:fail({unexpected, Other})
+ end;
+check_terms(_, []) ->
+ ok.
+
+run_server(Server, Config, Action, ExtraArgs) ->
+ Name = make_name(),
+ true = register(Name, self()),
+ JName = make_name(),
+ spawn_link(fun () ->
+ ok = jitu:java(?config(java, Config),
+ ?config(data_dir, Config),
+ atom_to_list(Server),
+ [JName,
+ erlang:get_cookie(),
+ node(),
+ Name]++ExtraArgs
+ ),
+ %,"-DOtpConnection.trace=3"),
+ Name ! {done, JName}
+ end),
+ receive
+ {Server, JName, Pid} ->
+ ?t:format("~w: ~p (~p)~n",
+ [Server, Pid, node(Pid)]),
+ ?t:format("nodes(hidden): ~p~n",
+ [nodes(hidden)]),
+ Action(Pid),
+ Pid ! bye,
+ receive
+ {done, JName} ->
+ ok
+ end;
+ Other ->
+ ?t:fail({unexpected,Other})
+ end.
+
+%%
+%% Utils...
+%%
+
+make_name() ->
+ {A, B, C} = now(),
+ list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ integer_to_list(A)
+ ++ "-" ++ integer_to_list(B)
+ ++ "-" ++ integer_to_list(C)).
+
+
+
+-define(VERSION_MAGIC, 131).
+
+-define(ATOM_EXT, 100).
+-define(REFERENCE_EXT, 101).
+-define(PORT_EXT, 102).
+-define(PID_EXT, 103).
+-define(NEW_REFERENCE_EXT, 114).
+
+uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
+ [(Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint32_be(Uint) ->
+ exit({badarg, uint32_be, [Uint]}).
+
+
+uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
+ [(Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint16_be(Uint) ->
+ exit({badarg, uint16_be, [Uint]}).
+
+uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
+ Uint band 16#ff;
+uint8(Uint) ->
+ exit({badarg, uint8, [Uint]}).
+
+
+
+mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
+ mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
+mk_pid({NodeName, Creation}, Number, Serial) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?PID_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint32_be(Serial),
+ uint8(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
+ mk_port({atom_to_list(NodeName), Creation}, Number);
+mk_port({NodeName, Creation}, Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?PORT_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeName, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
+ is_integer(Creation),
+ is_list(Numbers) ->
+ mk_ref({atom_to_list(NodeName), Creation}, Numbers);
+mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
+ is_integer(Creation),
+ is_integer(Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?REFERENCE_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end;
+mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
+ is_integer(Creation),
+ is_list(Numbers) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?NEW_REFERENCE_EXT,
+ uint16_be(length(Numbers)),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint8(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
diff --git a/lib/jinterface/test/nc_SUITE_data/Makefile.src b/lib/jinterface/test/nc_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..3d131250be
--- /dev/null
+++ b/lib/jinterface/test/nc_SUITE_data/Makefile.src
@@ -0,0 +1,53 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+# Makefile.src for java_client_erl_server test
+# Note: This file *must* work for both Unix and Windows
+#
+# We use both `rm' (Unix) and `del' (Windows) for removing files, but
+# with a `-' in front so that the error in not finding `rm' (`del') on
+# Windows (Unix) is ignored.
+#
+# VxWorks? XXX
+#
+
+.SUFFIXES:
+.SUFFIXES: .java
+
+
+JAVAC = @JAVAC@
+ERLC = erlc
+
+JINTERFACE_CLASSPATH = @jinterface_classpath@
+
+CLASSPATH = .@PS@$(JINTERFACE_CLASSPATH)@PS@
+
+JAVA_FILES = echo_server.java connection_server.java
+CLASS_FILES = $(JAVA_FILES:.java=.class)
+
+all: $(CLASS_FILES)
+
+clean:
+ -rm -f $(CLASS_FILES)
+ -del /F /Q $(CLASS_FILES)
+
+$(CLASS_FILES) : $(JAVA_FILES)
+ $(JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES)
+
+#
diff --git a/lib/jinterface/test/nc_SUITE_data/connection_server.java b/lib/jinterface/test/nc_SUITE_data/connection_server.java
new file mode 100644
index 0000000000..19ed1c7d5c
--- /dev/null
+++ b/lib/jinterface/test/nc_SUITE_data/connection_server.java
@@ -0,0 +1,96 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import com.ericsson.otp.erlang.*;
+
+public class connection_server {
+ static java.lang.String Name = "connection_server";
+
+ public static void main(String[] argv) {
+ try {
+ System.out.println("connection_server booting...");
+
+ for (int j = 0; j < argv.length; j++)
+ System.out.println("argv[" + j + "] = \"" + argv[j] + "\"");
+
+ if (argv.length != 4) {
+ System.out.println("Wrong number of arguments!");
+ System.exit(1);
+ }
+
+ // Start node and mbox
+ OtpNode node = new OtpNode(argv[0], argv[1]);
+ OtpMbox mbox = node.createMbox();
+ if (! mbox.registerName(Name)) {
+ System.out.println("Could not register name " + Name);
+ System.exit(3);
+ }
+
+ // Announce our presence
+ OtpErlangObject[] amsg = new OtpErlangObject[3];
+ amsg[0] = new OtpErlangAtom(Name);
+ amsg[1] = new OtpErlangAtom(argv[0]);
+ amsg[2] = mbox.self();
+ OtpErlangTuple atuple = new OtpErlangTuple(amsg);
+ mbox.send(argv[3], argv[2], atuple);
+
+ // Do connects ...
+ while (true) {
+ OtpErlangObject o = mbox.receive();
+ if (o == null)
+ continue;
+ if (o instanceof OtpErlangTuple) {
+ OtpErlangTuple msg = (OtpErlangTuple) o;
+ OtpErlangPid from = (OtpErlangPid)(msg.elementAt(0));
+ OtpErlangAtom conn_node = (OtpErlangAtom) msg.elementAt(1);
+
+ System.out.println("Got request to connect to: "
+ + conn_node);
+ OtpErlangObject[] rmsg = new OtpErlangObject[3];
+ rmsg[0] = mbox.self();
+ rmsg[1] = conn_node;
+ if (node.ping(conn_node.atomValue(), 1000)) {
+ System.out.println("Successfully connected to "
+ + conn_node.toString());
+ rmsg[2] = new OtpErlangAtom("true");
+ }
+ else {
+ System.out.println("Failed to connect to "
+ + conn_node.toString());
+ rmsg[2] = new OtpErlangAtom("false");
+ }
+
+ OtpErlangTuple rtuple = new OtpErlangTuple(rmsg);
+
+ mbox.send(from, rtuple);
+ }
+ else { // probably 'bye'
+ System.out.println("connection_server halting...");
+ System.exit(0);
+ }
+ }
+ }
+ catch (Exception e) {
+ System.out.println("" + e);
+ System.exit(2);
+ }
+
+ }
+
+}
diff --git a/lib/jinterface/test/nc_SUITE_data/echo_server.java b/lib/jinterface/test/nc_SUITE_data/echo_server.java
new file mode 100644
index 0000000000..0550e4beb1
--- /dev/null
+++ b/lib/jinterface/test/nc_SUITE_data/echo_server.java
@@ -0,0 +1,261 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2004-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+import java.io.UnsupportedEncodingException;
+import java.util.HashSet;
+
+import com.ericsson.otp.erlang.OtpErlangAtom;
+import com.ericsson.otp.erlang.OtpErlangBinary;
+import com.ericsson.otp.erlang.OtpErlangBoolean;
+import com.ericsson.otp.erlang.OtpErlangException;
+import com.ericsson.otp.erlang.OtpErlangInt;
+import com.ericsson.otp.erlang.OtpErlangList;
+import com.ericsson.otp.erlang.OtpErlangLong;
+import com.ericsson.otp.erlang.OtpErlangObject;
+import com.ericsson.otp.erlang.OtpErlangPid;
+import com.ericsson.otp.erlang.OtpErlangString;
+import com.ericsson.otp.erlang.OtpErlangTuple;
+import com.ericsson.otp.erlang.OtpExternal;
+import com.ericsson.otp.erlang.OtpInputStream;
+import com.ericsson.otp.erlang.OtpMbox;
+import com.ericsson.otp.erlang.OtpNode;
+import com.ericsson.otp.erlang.OtpOutputStream;
+
+public class echo_server {
+ private static boolean debug = false;
+
+ public static void main(final String[] argv) {
+ try {
+ System.out.println("echo_server booting...");
+
+ for (int j = 0; j < argv.length; j++) {
+ System.out.println("argv[" + j + "] = \"" + argv[j] + "\"");
+ }
+
+ if (argv.length != 4 && argv.length != 5) {
+ System.out.println("Wrong number of arguments!");
+ System.exit(1);
+ }
+
+ // Start node and mbox
+ final OtpNode node = new OtpNode(argv[0], argv[1]);
+ if (argv.length == 5 && argv[4].equals("unicode")) {
+ System.out.println("Setting unicode string mode.");
+ node.setFlags(OtpInputStream.DECODE_INT_LISTS_AS_STRINGS);
+ }
+ final OtpMbox mbox = node.createMbox();
+
+ // Announce our presence
+ final OtpErlangObject[] amsg = new OtpErlangObject[3];
+ amsg[0] = new OtpErlangAtom("echo_server");
+ amsg[1] = new OtpErlangAtom(argv[0]);
+ amsg[2] = mbox.self();
+ final OtpErlangTuple atuple = new OtpErlangTuple(amsg);
+ mbox.send(argv[3], argv[2], atuple);
+
+ // Do echoing...
+ while (true) {
+ final OtpErlangObject o = mbox.receive();
+ if (o == null) {
+ continue;
+ }
+ if (o instanceof OtpErlangTuple) {
+ final OtpErlangTuple msg = (OtpErlangTuple) o;
+ final int arity = msg.arity();
+ if (arity < 2) {
+ System.out
+ .println("Arity < 2; echo_server aborting...");
+ System.exit(2);
+ } else if (arity == 2) {
+ final OtpErlangPid from = (OtpErlangPid) msg
+ .elementAt(0);
+ if (debug) System.out.println("Echoing: "
+ + msg.elementAt(1));
+
+ final OtpErlangObject[] rmsg = new OtpErlangObject[2];
+ rmsg[0] = mbox.self();
+ rmsg[1] = msg.elementAt(1);
+ final OtpErlangTuple rtuple = new OtpErlangTuple(rmsg);
+
+ mbox.send(from, rtuple);
+ continue;
+ } else if (arity == 3) {
+ echoTwisted(mbox, msg);
+ continue;
+ } else {
+ System.out
+ .println("Arity > 3; echo_server aborting...");
+ System.exit(2);
+ }
+ } else if (o instanceof OtpErlangAtom) {
+ OtpErlangAtom a = (OtpErlangAtom) o;
+ if (a.atomValue().equals("debug")) {
+ debug = true;
+ } else if (a.atomValue().equals("bye")) {
+ System.out.println("echo_server halting...");
+ System.exit(0);
+ }
+ } else { // probably 'bye'
+ }
+ System.out.println("Unexpected: " + o +
+ " echo_server aborting...");
+ System.exit(2);
+ }
+ } catch (final Exception e) {
+ System.out.println("" + e);
+ System.exit(2);
+ }
+ }
+
+ private static void echoTwisted(final OtpMbox mbox,
+ final OtpErlangTuple msg)
+ throws OtpErlangException {
+ final OtpErlangPid from = (OtpErlangPid) msg.elementAt(0);
+
+ final OtpErlangObject[] rmsg = new OtpErlangObject[3];
+ if (debug) System.out.println("Echo in: " + msg);
+ rmsg[0] = mbox.self();
+ rmsg[1] = twist(msg.elementAt(1), rmsg[2] = msg.elementAt(2));
+ final OtpErlangTuple rtuple = new OtpErlangTuple(rmsg);
+ if (debug) System.out.println("Echo out: " + rtuple);
+
+ mbox.send(from, rtuple);
+ }
+
+ private static HashSet<OtpErlangObject> hash_set =
+ new HashSet<OtpErlangObject>();
+
+ private static OtpErlangObject twist(final OtpErlangObject i,
+ final OtpErlangObject t) throws OtpErlangException {
+ hash_set.add(i);
+ if (t instanceof OtpErlangAtom) {
+ final String atomValue = ((OtpErlangAtom) t).atomValue();
+ if (atomValue.equals("binary") && i instanceof OtpErlangBinary) {
+ final OtpErlangBinary b = (OtpErlangBinary) i;
+ final OtpInputStream bis = new OtpInputStream(b.binaryValue(),
+ 0);
+ final OtpErlangObject o = bis.read_any();
+ return o;
+ } else if (atomValue.equals("compress")) {
+ final OtpOutputStream oos = new OtpOutputStream();
+ oos.write1(OtpExternal.versionTag);
+ oos.write_compressed(i);
+ final OtpErlangBinary o =
+ new OtpErlangBinary(oos.toByteArray());
+ return o;
+ } else if (atomValue.equals("bigint")
+ && i instanceof OtpErlangLong) {
+ final OtpErlangLong l = (OtpErlangLong) i;
+ final int w = l.signum() * l.bitLength();
+ final OtpErlangLong x = new OtpErlangLong(l.longValue());
+ final java.math.BigInteger b = l.bigIntegerValue();
+ System.out.println("long: " + l + ": " + w + ": " + b.signum()
+ * b.bitLength() + ": " + x + ": " + l.isLong() + ": "
+ + l.isULong());
+ return new OtpErlangTuple(new OtpErlangObject[] { l,
+ new OtpErlangInt(w), x,
+ new OtpErlangInt(l.isLong() ? 1 : 0),
+ new OtpErlangInt(l.isULong() ? 1 : 0) });
+ } else if (atomValue.equals("tail")
+ && i instanceof OtpErlangList) {
+ final OtpErlangObject o = ((OtpErlangList) i).getTail();
+ if (o == null) {
+ return new OtpErlangAtom("null");
+ }
+ return o;
+ } else if (atomValue.equals("tail3")
+ && i instanceof OtpErlangList) {
+ final OtpErlangObject o = ((OtpErlangList) i).getNthTail(3);
+ if (o == null) {
+ return new OtpErlangAtom("null");
+ }
+ return o;
+ } else if (atomValue.equals("strcat")
+ && i instanceof OtpErlangList) {
+ final java.lang.StringBuffer b = new java.lang.StringBuffer();
+ final OtpErlangList l = (OtpErlangList) i;
+ for (final OtpErlangObject j : l) {
+ final OtpErlangString k = (OtpErlangString) j;
+ b.append(k.stringValue());
+ }
+ final OtpErlangObject o = new OtpErlangString(b.toString());
+ return o;
+ } else if (atomValue.equals("sub3atom")
+ && i instanceof OtpErlangString) {
+ final OtpErlangString s = (OtpErlangString) i;
+ final OtpErlangAtom o = new OtpErlangAtom(s.stringValue()
+ .substring(3));
+ return o;
+ } else if (atomValue.equals("utf8")) {
+ if (i instanceof OtpErlangString) {
+ final OtpErlangString s = (OtpErlangString) i;
+ byte[] bytes;
+ try {
+ bytes = s.stringValue().getBytes("UTF-8");
+ } catch (final UnsupportedEncodingException e) {
+ bytes = new byte[] { 'e', 'r', 'r', 'o', 'r' };
+ }
+ final OtpErlangBinary b = new OtpErlangBinary(bytes);
+ return b;
+ }
+ } else if(atomValue.equals("to_string_neg_int_list")) {
+ OtpErlangString oes = null;
+ if (i instanceof OtpErlangString) {
+ oes = (OtpErlangString) i;
+ } else if (i instanceof OtpErlangList) {
+ OtpErlangList oel = (OtpErlangList) i;
+ try {
+ oes = new OtpErlangString(oel);
+ } catch (final Exception e) {
+ }
+ }
+ if (oes != null) {
+ String s = oes.stringValue();
+ int n = s.length();
+ OtpErlangObject l[] = new OtpErlangObject[n];
+ for (int j = 0; j < n; j++) {
+ int c = s.charAt(j);
+ l[j] = new OtpErlangInt(-c);
+ }
+ return new OtpErlangList(l);
+ }
+ } else if(atomValue.equals("to_neg_int_list")) {
+ if (i instanceof OtpErlangString) {
+ OtpErlangString oes = (OtpErlangString) i;
+ OtpErlangList oel = new OtpErlangList(oes.stringValue());
+ int n = oel.arity();
+ OtpErlangObject l[] = new OtpErlangObject[n];
+ for (int j = 0; j < n; j++) {
+ OtpErlangLong c = (OtpErlangLong) oel.elementAt(j);
+ l[j] = new OtpErlangInt(-c.intValue());
+ }
+ return new OtpErlangList(l);
+ }
+ } else if (atomValue.equals("hash_lookup")) {
+ final boolean exists = hash_set.contains(i);
+ final OtpErlangBoolean b = new OtpErlangBoolean(exists);
+ return b;
+ } else if (atomValue.equals("hash_clear")) {
+ hash_set.clear();
+ return new OtpErlangAtom("hash_cleared");
+ }
+ }
+ return i;
+ }
+}
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 6b5719d7c5..9d75a653e3 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1,19 +1 @@
-##
-## %CopyrightBegin%
-##
-## Copyright Ericsson AB 2000-2010. All Rights Reserved.
-##
-## The contents of this file are subject to the Erlang Public License,
-## Version 1.1, (the "License"); you may not use this file except in
-## compliance with the License. You should have received a copy of the
-## Erlang Public License along with this software. If not, it can be
-## retrieved online at http://www.erlang.org/.
-##
-## Software distributed under the License is distributed on an "AS IS"
-## basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-## the License for the specific language governing rights and limitations
-## under the License.
-##
-## %CopyrightEnd%
-
-JINTERFACE_VSN = 1.5.3
+JINTERFACE_VSN = 1.5.4
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml
index 19e1d3221c..4b8f934df1 100644
--- a/lib/kernel/doc/src/code.xml
+++ b/lib/kernel/doc/src/code.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -54,7 +54,7 @@
for and tries to load the module.</p>
</item>
</list>
- <p>To prevent accidentaly reloading modules affecting the Erlang
+ <p>To prevent accidentally reloading modules affecting the Erlang
runtime system itself, the <c>kernel</c>, <c>stdlib</c> and
<c>compiler</c> directories are considered <em>sticky</em>. This
means that the system issues a warning and rejects the request if
diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml
index 07c1844485..324d4264cf 100644
--- a/lib/kernel/doc/src/disk_log.xml
+++ b/lib/kernel/doc/src/disk_log.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml
index 4e65bf46f8..9a62b45d63 100644
--- a/lib/kernel/doc/src/erl_ddll.xml
+++ b/lib/kernel/doc/src/erl_ddll.xml
@@ -177,7 +177,7 @@
<name>demonitor(MonitorRef) -> ok</name>
<fsummary>Remove a monitor for a driver</fsummary>
<type>
- <v>MonitorRef = ref()</v>
+ <v>MonitorRef = reference()</v>
</type>
<desc>
<p>Removes a driver monitor in much the same way as
@@ -185,7 +185,7 @@
monitors. See <seealso marker="#monitor/2">monitor/2</seealso>, <seealso marker="#try_load/3">try_load/3</seealso> and <seealso marker="#try_unload/2">try_unload/2</seealso> for details
about how to create driver monitors.</p>
<p>The function throws a <c>badarg</c> exception if the
- parameter is not a ref(). </p>
+ parameter is not a reference(). </p>
</desc>
</func>
<func>
@@ -400,7 +400,7 @@
<v>Item = {Name, When}</v>
<v>Name = atom() | string()</v>
<v>When = loaded | unloaded | unloaded_only</v>
- <v>MonitorRef = ref()</v>
+ <v>MonitorRef = reference()</v>
</type>
<desc>
<p>This function creates a driver monitor and works in many
@@ -449,7 +449,7 @@
eventually lead to one of the following messages
being sent:</p>
<taglist>
- <tag><em>{'UP', ref(), driver, Name, loaded}</em></tag>
+ <tag><em>{'UP', reference(), driver, Name, loaded}</em></tag>
<item>
<p>This message is sent, either immediately if the
driver is already loaded and no reloading is
@@ -459,7 +459,7 @@
expected to know if reloading is demanded prior
to creating a monitor for loading.</p>
</item>
- <tag><em>{'UP', ref(), driver, Name, permanent}</em></tag>
+ <tag><em>{'UP', reference(), driver, Name, permanent}</em></tag>
<item>
<p>This message will be sent if reloading was
expected, but the (old) driver made itself
@@ -467,7 +467,7 @@
sent if the driver was permanent or statically
linked in when trying to create the monitor.</p>
</item>
- <tag><em>{'DOWN', ref(), driver, Name, load_cancelled}</em></tag>
+ <tag><em>{'DOWN', reference(), driver, Name, load_cancelled}</em></tag>
<item>
<p>This message will arrive if reloading was
underway, but the <seealso marker="#users">user</seealso> having requested
@@ -476,7 +476,7 @@
(or <c>unload/1</c>/<c>unload_driver/1</c>)
again before it was reloaded.</p>
</item>
- <tag><em>{'DOWN', ref(), driver, Name, {load_failure, Failure}}</em></tag>
+ <tag><em>{'DOWN', reference(), driver, Name, {load_failure, Failure}}</em></tag>
<item>
<p>This message will arrive if reloading was
underway but the loading for some reason
@@ -500,7 +500,7 @@
<p>A driver monitor for unload will eventually result
in one of the following messages being sent:</p>
<taglist>
- <tag><em>{'DOWN', ref(), driver, Name, unloaded}</em></tag>
+ <tag><em>{'DOWN', reference(), driver, Name, unloaded}</em></tag>
<item>
<p>The driver instance monitored is now
unloaded. As the unload might have been due to a
@@ -508,7 +508,7 @@
again have been loaded when this message
arrives.</p>
</item>
- <tag><em>{'UP', ref(), driver, Name, unload_cancelled}</em></tag>
+ <tag><em>{'UP', reference(), driver, Name, unload_cancelled}</em></tag>
<item>
<p>This message will be sent if unloading was
expected, but while the driver was waiting for
@@ -525,7 +525,7 @@
similar to an <c>unloaded</c> monitor, but does
never result in this message.</p>
</item>
- <tag><em>{'UP', ref(), driver, Name, permanent}</em></tag>
+ <tag><em>{'UP', reference(), driver, Name, permanent}</em></tag>
<item>
<p>This message will be sent if unloading was
expected, but the driver made itself
@@ -539,7 +539,7 @@
<item>
<p>A monitor created as <c>unloaded_only</c> behaves
exactly as one created as <c>unloaded</c> with the
- exception that the <c>{'UP', ref(), driver, Name, unload_cancelled}</c> message will never be
+ exception that the <c>{'UP', reference(), driver, Name, unload_cancelled}</c> message will never be
sent, but the monitor instead persists until the
driver <em>really</em> gets unloaded.</p>
</item>
@@ -626,7 +626,7 @@
<v>ReloadOption = pending_driver | pending</v>
<v>Status = loaded | already_loaded | PendingStatus </v>
<v>PendingStatus = pending_driver | pending_process</v>
- <v>Ref = ref()</v>
+ <v>Ref = reference()</v>
<v>ErrorDesc = ErrorAtom | OpaqueError</v>
<v>ErrorAtom = linked_in_driver | inconsistent | permanent | not_loaded_by_this_process | not_loaded | pending_reload | pending_process</v>
</type>
@@ -650,7 +650,7 @@
registered and a corresponding <c>try_unload</c> is
expected sometime in the future.</p>
</item>
- <tag><em>{ok, pending_driver}</em>or <em>{ok, pending_driver, ref()}</em></tag>
+ <tag><em>{ok, pending_driver}</em>or <em>{ok, pending_driver, reference()}</em></tag>
<item>
<p>The load request is registered, but the loading is
delayed due to the fact that an earlier instance of the
@@ -665,7 +665,7 @@
set. In other words, this return value will always need
to be handled!</p>
</item>
- <tag><em>{ok, pending_process}</em>or <em>{ok, pending_process, ref()}</em></tag>
+ <tag><em>{ok, pending_process}</em>or <em>{ok, pending_process, reference()}</em></tag>
<item>
<p>The load request is registered, but the loading is
delayed due to the fact that an earlier instance of the
@@ -683,7 +683,7 @@
about when the driver is <em>actually</em> loaded. This can
be achieved by using the <c>{monitor, PendingOption}</c> option.</p>
<p>When monitoring is requested, and a corresponding <c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c> would be
- returned, the function will instead return a tuple <c>{ok, PendingStatus, ref()}</c> and the process will, at a later
+ returned, the function will instead return a tuple <c>{ok, PendingStatus, reference()}</c> and the process will, at a later
time when the driver actually gets loaded, get a monitor
message. The monitor message one can expect is described in
the <seealso marker="#monitor/2">monitor/2</seealso>
@@ -760,7 +760,7 @@
<p>A <c>MonitorOption</c> tells <c>try_load/3</c> to
trigger a driver monitor under certain
conditions. When the monitor is triggered, the
- function will return a three-tuple <c>{ok, PendingStatus, ref()}</c>, where the <c>ref()</c> is
+ function will return a three-tuple <c>{ok, PendingStatus, reference()}</c>, where the <c>reference()</c> is
the monitor ref for the driver monitor.</p>
<p>Only one <c>MonitorOption</c> can be specified and
it is either the atom <c>pending</c>, which means
@@ -891,7 +891,7 @@
<v>MonitorOption = pending_driver | pending</v>
<v>Status = unloaded | PendingStatus </v>
<v>PendingStatus = pending_driver | pending_process</v>
- <v>Ref = ref()</v>
+ <v>Ref = reference()</v>
<v>ErrorAtom = linked_in_driver | not_loaded | not_loaded_by_this_process | permanent</v>
</type>
<desc>
@@ -943,7 +943,7 @@
ports using it and there are no more <seealso marker="#users">users</seealso> requiring it to be
loaded.</p>
</item>
- <tag><em>{ok, pending_driver}</em>or <em>{ok, pending_driver, ref()}</em></tag>
+ <tag><em>{ok, pending_driver}</em>or <em>{ok, pending_driver, reference()}</em></tag>
<item>
<p>This return value indicates that this call removed the
last <seealso marker="#users">user</seealso> from the
@@ -957,7 +957,7 @@
in that case, however transient. Monitors are as always
useful to detect when the driver is really unloaded.</p>
</item>
- <tag><em>{ok, pending_process}</em>or <em>{ok, pending_process, ref()}</em></tag>
+ <tag><em>{ok, pending_process}</em>or <em>{ok, pending_process, reference()}</em></tag>
<item>
<p>The unload request is registered, but there are still
other <seealso marker="#users">users</seealso> holding
diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml
index 94824688d1..7f78322472 100644
--- a/lib/kernel/doc/src/error_handler.xml
+++ b/lib/kernel/doc/src/error_handler.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index 50f9722a1c..36fce464c5 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -36,6 +36,61 @@
other Erlang processes to continue executing in parallel with
the file operations. See the command line flag
<c>+A</c> in <seealso marker="erts:erl">erl(1)</seealso>.</p>
+
+ <p>The Erlang VM supports file names in Unicode to a limited
+ extent. Depending on how the VM is started (with the parameter
+ <c>+fnu</c> or <c>+fnl</c>), file names given can contain
+ characters > 255 and the VM system will convert file names
+ back and forth to the native file name encoding.</p>
+
+ <p>The default behavior for Unicode character translation depends
+ on to what extent the underlying OS/filesystem enforces consistent
+ naming. On OSes where all file names are ensured to be in one or
+ another encoding, Unicode is the default (currently this holds for
+ Windows and MacOSX). On OSes with completely transparent file
+ naming (i.e. all Unixes except MacOSX), ISO-latin-1 file naming is
+ the default. The reason for the ISO-latin-1 default is that
+ file names are not guaranteed to be possible to interpret according to
+ the Unicode encoding expected (i.e. UTF-8), and file names that
+ cannot be decoded will only be accessible by using &quot;raw
+ file names&quot;, in other word file names given as binaries.</p>
+
+ <p>As file names are traditionally not binaries in Erlang,
+ applications that need to handle raw file names need to be
+ converted, why the Unicode mode for file names is not default on
+ systems having completely transparent file naming.</p>
+
+ <note>As of R14B01, the most basic file handling modules
+ (<c>file</c>, <c>prim_file</c>, <c>filelib</c> and
+ <c>filename</c>) accept raw file names, but the rest of OTP is not
+ guaranteed to handle them, why Unicode file naming on systems
+ where it is not default is still considered experimental.</note>
+
+ <p>Raw file names is a new feature in OTP R14B01, which allows the
+ user to supply completely uninterpreted file names to the
+ underlying OS/filesystem. They are supplied as binaries, where it
+ is up to the user to supply a correct encoding for the
+ environment. The function <c>file:native_name_encoding()</c> can
+ be used to check what encoding the VM is working in. If the
+ function returns <c>latin1</c> file names are not in any way
+ converted to Unicode, if it is <c>utf8</c>, raw file names should
+ be encoded as UTF-8 if they are to follow the convention of the VM
+ (and usually the convention of the OS as well). Using raw
+ file names is useful if you have a filesystem with inconsistent
+ file naming, where some files are named in UTF-8 encoding while
+ others are not. A file:list_dir on such mixed file name systems
+ when the VM is in Unicode file name mode might return file names as
+ raw binaries as they cannot be interpreted as Unicode
+ file names. Raw file names can also be used to give UTF-8 encoded
+ file names even though the VM is not started in Unicode file name
+ translation mode.</p>
+
+ <p>Note that on Windows, <c>file:native_name_encoding()</c>
+ returns <c>utf8</c> per default, which is the format for raw
+ file names even on Windows, although the underlying OS specific
+ code works in a limited version of little endian UTF16. As far as
+ the Erlang programmer is concerned, Windows native Unicode format
+ is UTF-8...</p>
</description>
<section>
@@ -47,8 +102,14 @@ iodata() = iolist() | binary()
io_device()
as returned by file:open/2, a process handling IO protocols
-name() = string() | atom() | DeepList
+name() = string() | atom() | DeepList | RawFilename
DeepList = [char() | atom() | DeepList]
+ RawFilename = binary()
+ If VM is in unicode filename mode, string() and char() are allowed to be > 255.
+ RawFilename is a filename not subject to Unicode translation, meaning that it
+ can contain characters not conforming to the Unicode encoding expected from the
+ filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode
+ filename mode).
posix()
an atom which is named from the POSIX error codes used in
@@ -62,6 +123,25 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}}
</section>
<funcs>
<func>
+ <name>advise(IoDevice, Offset, Length, Advise) -> ok | {error, Reason}</name>
+ <fsummary>Predeclare an access pattern for file data</fsummary>
+ <type>
+ <v>IoDevice = io_device()</v>
+ <v>Offset = int()</v>
+ <v>Length = int()</v>
+ <v>Advise = posix_file_advise()</v>
+ <v>posix_file_advise() = normal | sequential | random | no_reuse
+ | will_need | dont_need</v>
+ <v>Reason = ext_posix()</v>
+ </type>
+ <desc>
+ <p><c>advise/4</c> can be used to announce an intention to access file
+ data in a specific pattern in the future, thus allowing the
+ operating system to perform appropriate optimizations.</p>
+ <p>On some platforms, this function might have no effect.</p>
+ </desc>
+ </func>
+ <func>
<name>change_group(Filename, Gid) -> ok | {error, Reason}</name>
<fsummary>Change group of a file</fsummary>
<type>
@@ -579,13 +659,24 @@ f.txt: {person, "kalle", 25}.
</desc>
</func>
<func>
+ <name>native_name_encoding() -> latin1 | utf8</name>
+ <fsummary>Return the VM's configured filename encoding.</fsummary>
+ <desc>
+ <p>This function returns the configured default file name encoding to use for raw file names. Generally an application supplying file names raw (as binaries), should obey the character encoding returned by this function.</p>
+ <p>By default, the VM uses ISO-latin-1 file name encoding on filesystems and/or OSes that use completely transparent file naming. This includes all Unix versions except MacOSX, where the vfs layer enforces UTF-8 file naming. By giving the experimental option <c>+fnu</c> when starting Erlang, UTF-8 translation of file names can be turned on even for those systems. If Unicode file name translation is in effect, the system behaves as usual as long as file names conform to the encoding, but will return file names that are not properly encoded in UTF-8 as raw file names (i.e. binaries).</p>
+ <p>On Windows, this function also returns <c>utf8</c> by default. The OS uses a pure Unicode naming scheme and file names are always possible to interpret as valid Unicode. The fact that the underlying Windows OS actually encodes file names using little endian UTF-16 can be ignored by the Erlang programmer. Windows and MacOSX are the only operating systems where the VM operates in Unicode file name mode by default.</p>
+ </desc>
+ </func>
+ <func>
<name>open(Filename, Modes) -> {ok, IoDevice} | {error, Reason}</name>
<fsummary>Open a file</fsummary>
<type>
<v>Filename = name()</v>
<v>Modes = [Mode]</v>
- <v>&nbsp;Mode = read | write | append | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed</v>
+ <v>&nbsp;Mode = read | write | append | exclusive | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed | {encoding, Encoding}</v>
<v>&nbsp;&nbsp;Size = Delay = int()</v>
+ <v>&nbsp;&nbsp;Encoding = latin1 | unicode | utf8 | utf16 | {utf16, Endian} | utf32 | {utf32, Endian}</v>
+ <v>&nbsp;&nbsp;&nbsp;&nbsp;Endian = big | little</v>
<v>IoDevice = io_device()</v>
<v>Reason = ext_posix() | system_limit</v>
</type>
@@ -611,6 +702,17 @@ f.txt: {person, "kalle", 25}.
file opened with <c>append</c> will take place at
the end of the file.</p>
</item>
+ <tag><c>exclusive</c></tag>
+ <item>
+ <p>The file, when opened for writing, is created if it
+ does not exist. If the file exists, open will return
+ <c>{error, eexist}</c>.</p>
+ <warning><p>This option does not guarantee exclusiveness on
+ file systems that do not support O_EXCL properly,
+ such as NFS. Do not depend on this option unless you
+ know that the file system supports it (in general, local
+ file systems should be safe).</p></warning>
+ </item>
<tag><c>raw</c></tag>
<item>
<p>The <c>raw</c> option allows faster access to a file,
@@ -1199,7 +1301,7 @@ f.txt: {person, "kalle", 25}.
</item>
<tag><c>{no_translation, unicode, latin1}</c></tag>
<item>
- <p>The file is was opened with another <c>encoding</c> than <c>latin1</c> and the data on the file can not be translated to the byte-oriented data that this function returns.</p>
+ <p>The file was opened with another <c>encoding</c> than <c>latin1</c> and the data in the file can not be translated to the byte-oriented data that this function returns.</p>
</item>
</taglist>
</desc>
@@ -1641,6 +1743,33 @@ f.txt: {person, "kalle", 25}.
</desc>
</func>
<func>
+ <name>datasync(IoDevice) -> ok | {error, Reason}</name>
+ <fsummary>Synchronizes the in-memory data of a file, ignoring most of its metadata, with that on the physical medium</fsummary>
+ <type>
+ <v>IoDevice = io_device()</v>
+ <v>Reason = ext_posix() | terminated</v>
+ </type>
+ <desc>
+ <p>Makes sure that any buffers kept by the operating system
+ (not by the Erlang runtime system) are written to disk. In
+ many ways it's resembles fsync but it not requires to update
+ some of file's metadata such as the access time. On
+ some platforms, this function might have no effect.</p>
+ <p>Applications that access databases or log files often write
+ a tiny data fragment (e.g., one line in a log file) and then
+ call fsync() immediately in order to ensure that the written
+ data is physically stored on the harddisk. Unfortunately, fsync()
+ will always initiate two write operations: one for the newly
+ written data and another one in order to update the modification
+ time stored in the inode. If the modification time is not a part
+ of the transaction concept fdatasync() can be used to avoid
+ unnecessary inode disk write operations.</p>
+ <p>Available only in some POSIX systems. This call results in a
+ call to fsync(), or has no effect, in systems not implementing
+ the fdatasync syscall.</p>
+ </desc>
+ </func>
+ <func>
<name>truncate(IoDevice) -> ok | {error, Reason}</name>
<fsummary>Truncate a file</fsummary>
<type>
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index 3a8011e28b..fb09092f1c 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -1173,7 +1173,7 @@ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -&gt;
<title>SEE ALSO</title>
<p><seealso marker="inet">inet(3)</seealso>,
<seealso marker="gen_tcp">gen_tcp(3)</seealso>,
- <seealso marker="gen_udp">gen_upd(3)</seealso>,
+ <seealso marker="gen_udp">gen_udp(3)</seealso>,
<url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> (Stream Control Transmission Protocol),
<url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP.</url></p>
<marker id="authors"></marker>
diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml
index 032dcc5251..aa171c77c2 100644
--- a/lib/kernel/doc/src/gen_tcp.xml
+++ b/lib/kernel/doc/src/gen_tcp.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -74,6 +74,7 @@ posix()
socket()
as returned by accept/1,2 and connect/3,4</code>
+ <marker id="connect"></marker>
</section>
<funcs>
<func>
@@ -284,9 +285,10 @@ socket()
<type>
<v>Socket = socket()</v>
<v>Length = int()</v>
- <v>Packet = [char()] | binary()</v>
+ <v>Packet = [char()] | binary() | HttpPacket</v>
<v>Timeout = int() | infinity</v>
<v>Reason = closed | posix()</v>
+ <v>HttpPacket = see the description of <c>HttpPacket</c> in <seealso marker="erts:erlang#decode_packet/3">erlang:decode_packet/3</seealso></v>
</type>
<desc>
<p>This function receives a packet from a socket in passive
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 2ae230152c..f05a224f33 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -220,6 +220,69 @@ fe80::204:acff:fe17:bf38
<p>Returns the local hostname. Will never fail.</p>
</desc>
</func>
+
+ <func>
+ <name>getifaddrs() -> {ok,Iflist} | {error,posix}</name>
+ <fsummary>Return a list of interfaces and their addresses</fsummary>
+ <type>
+ <v>Iflist = {Ifname,[Ifopt]}</v>
+ <v>Ifname = string()</v>
+ <v>Ifopt = {flag,[Flag]} | {addr,Addr} | {netmask,Netmask}
+ | {broadaddr,Broadaddr} | {dstaddr,Dstaddr}
+ | {hwaddr,Hwaddr}</v>
+ <v>Flag = up | broadcast | loopback | pointtopoint
+ | running | multicast</v>
+ <v>Addr = Netmask = Broadadddr = Dstaddr = ip_address()</v>
+ <v>Hwaddr = [byte()]</v>
+ </type>
+ </func>
+ <desc>
+ <p>
+ Returns a list of 2-tuples containing interface names and the
+ interface's addresses. <c>Ifname</c> is a Unicode string.
+ <c>Hwaddr</c> is hardware dependent, e.g on Ethernet interfaces
+ it is the 6-byte Ethernet address (MAC address (EUI-48 address)).
+ </p>
+ <p>
+ The <c>{addr,Addr}</c>, <c>{netmask,_}</c> and <c>{broadaddr,_}</c>
+ tuples are repeated in the result list iff the interface has multiple
+ addresses. If you come across an interface that has
+ multiple <c>{flag,_}</c> or <c>{hwaddr,_}</c> tuples you have
+ a really strange interface or possibly a bug in this function.
+ The <c>{flag,_}</c> tuple is mandatory, all other optional.
+ </p>
+ <p>
+ Do not rely too much on the order of <c>Flag</c> atoms or
+ <c>Ifopt</c> tuples. There are some rules, though:
+ <list>
+ <item>
+ Immediately after <c>{addr,_}</c> follows <c>{netmask,_}</c>
+ </item>
+ <item>
+ Immediately thereafter follows <c>{broadaddr,_}</c> if
+ the <c>broadcast</c> flag is <em>not</em> set and the
+ <c>pointtopoint</c>flag <em>is</em> set.
+ </item>
+ <item>
+ Any <c>{netmask,_}</c>, <c>{broadaddr,_}</c> or
+ <c>{dstaddr,_}</c> tuples that follow an <c>{addr,_}</c>
+ tuple concerns that address.
+ </item>
+ </list>
+ </p>
+ <p>
+ The <c>{hwaddr,_}</c> tuple is not returned on Solaris since the
+ hardware address historically belongs to the link layer and only
+ the superuser can read such addresses.
+ </p>
+ <p>
+ On Windows, the data is fetched from quite different OS API
+ functions, so the <c>Netmask</c> and <c>Broadaddr</c>
+ values may be calculated, just as some <c>Flag</c> values.
+ You have been warned. Report flagrant bugs.
+ </p>
+ </desc>
+
<func>
<name>getopts(Socket, Options) -> {ok, OptionValues} | {error, posix()}</name>
<fsummary>Get one or more options for a socket</fsummary>
@@ -369,7 +432,7 @@ fe80::204:acff:fe17:bf38
</desc>
</func>
<func>
- <name>port(Socket) -> {ok, Port}</name>
+ <name>port(Socket) -> {ok, Port} | {error, any()}</name>
<fsummary>Return the local port number for a socket</fsummary>
<type>
<v>Socket = socket()</v>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 9c3f6524ae..065b24c53d 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,340 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.14.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>os:find_executable/{1,2}</c> will no longer return the
+ path of a directory that happens to be in the PATH.</p>
+ <p>
+ Own Id: OTP-8983 Aux Id: seq11749 </p>
+ </item>
+ <item>
+ <p>
+ Fix -spec for file:write_file/3</p>
+ <p>
+ Change type for second parameter from binary() to
+ iodata(), since the function explicitly takes steps to
+ accept lists as well as binaries. (thanks to Magnus
+ Henoch).</p>
+ <p>
+ Own Id: OTP-9067</p>
+ </item>
+ <item>
+ <p>
+ Sanitize the specs of the code module</p>
+ <p>
+ After the addition of unicode_binary() to the
+ file:filename() type, dialyzer started complaining about
+ erroneous or incomplete specs in some functions of the
+ 'code' module. The culprit was hard-coded information in
+ erl_bif_types for functions of this module, which were
+ not updated. Since these functions have proper specs
+ these days and code duplication (pun intended) is never a
+ good idea, their type information was removed from
+ erl_bif_types.</p>
+ <p>
+ While doing this, some erroneous comments were fixed in
+ the code module and also made sure that the code now runs
+ without dialyzer warnings even when the
+ -Wunmatched_returns option is used.</p>
+ <p>
+ Some cleanups were applied to erl_bif_types too.</p>
+ <p>
+ Own Id: OTP-9100</p>
+ </item>
+ <item>
+ <p>
+ - Add spec for function that does not return - Strenghen
+ spec - Introduce types to avoid duplication in specs -
+ Add specs for functions that do not return - Add specs
+ for behaviour callbacks - Simplify two specs</p>
+ <p>
+ Own Id: OTP-9127</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.14.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The Erlang VM now supports Unicode filenames. The feature
+ is turned on by default on systems where Unicode
+ filenames are mandatory (Windows and MacOSX), but can be
+ enabled on other systems with the '+fnu' emulator option.
+ Enabling the Unicode filename feature on systems where it
+ is not default is however considered experimental and not
+ to be used for production. Together with the Unicode file
+ name support, the concept of "raw filenames" is
+ introduced, which means filenames provided without
+ implicit unicode encoding translation. Raw filenames are
+ provided as binaries, not lists. For further information,
+ see stdlib users guide and the chapter about using
+ Unicode in Erlang. Also see the file module manual page.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8887</p>
+ </item>
+ <item>
+ <p>
+ There is now a new function inet:getifaddrs/0 modeled
+ after C library function getifaddrs() on BSD and LInux
+ that reports existing interfaces and their addresses on
+ the host. This replaces the undocumented and unsupported
+ inet:getiflist/0 and inet:ifget/2.</p>
+ <p>
+ Own Id: OTP-8926</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.14.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>In embedded mode, on_load handlers that called
+ <c>code:priv_dir/1</c> or other functions in <c>code</c>
+ would hang the system. Since the <c>crypto</c>
+ application now contains an on_loader handler that calls
+ <c>code:priv_dir/1</c>, including the <c>crypto</c>
+ application in the boot file would prevent the system
+ from starting.</p>
+ <p>Also extended the <c>-init_debug</c> option to print
+ information about on_load handlers being run to
+ facilitate debugging.</p>
+ <p>
+ Own Id: OTP-8902 Aux Id: seq11703 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.14.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed: inet:setopts(S, [{linger,{true,2}}]) returned
+ {error,einval} for SCTP sockets. The inet_drv had a bug
+ when checking the option size.</p>
+ <p>
+ Own Id: OTP-8726 Aux Id: seq11617 </p>
+ </item>
+ <item>
+ <p>
+ gen_udp:connect/3 was broken for SCTP enabled builds. It
+ did not detect remote end errors as it should.</p>
+ <p>
+ Own Id: OTP-8729</p>
+ </item>
+ <item>
+ <p>reference() has been substituted for ref() in the
+ documentation.</p>
+ <p>
+ Own Id: OTP-8733</p>
+ </item>
+ <item>
+ <p>A bug introduced in kernel-2.13.5.3 has been fixed. If
+ running <c>net_kernel:set_net_ticktime/1</c> twice within
+ the <c>TransitionPerod</c> the second call caused the
+ net_kernel process to crash with a <c>badmatch</c>.</p>
+ <p>
+ Own Id: OTP-8787 Aux Id: seq11657, OTP-8643 </p>
+ </item>
+ <item>
+ <p>
+ inet:getsockopt for SCTP sctp_default_send_param had a
+ bug to not initialize required feilds causing random
+ answers. It is now corrected.</p>
+ <p>
+ Own Id: OTP-8795 Aux Id: seq11655 </p>
+ </item>
+ <item>
+ <p>For a socket in the HTTP packet mode, the return value
+ from <c>gen_tcp:recv/2,3</c> if there is an error in the
+ header will be <c>{ok,{http_error,String}}</c> instead of
+ <c>{error,{http_error,String}}</c> to be consistent with
+ <c>ssl:recv/2,3</c>.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8831</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Even when configuring erlang with --enable-native-libs,
+ the native code for modules loaded very early (such as
+ lists) would not get loaded. This has been corrected.
+ (Thanks to Paul Guyot.)</p>
+ <p>
+ Own Id: OTP-8750</p>
+ </item>
+ <item>
+ <p>
+ The undocumented function inet:ifget/2 has been improved
+ to return interface hardware address (MAC) on platforms
+ supporting getaddrinfo() (such as BSD unixes). Note it
+ still does not work on all platforms for example not
+ Windows nor Solaris, so the function is still
+ undocumented.</p>
+ <p>
+ Buffer overflow and field init bugs for inet:ifget/2 and
+ inet:getservbyname/2 has also been fixed.</p>
+ <p>
+ Thanks to Michael Santos.</p>
+ <p>
+ Own Id: OTP-8816</p>
+ </item>
+ <item>
+ <p>
+ As a usability improvement the 'inet6' option to
+ functions gen_tcp:listen/2, gen_tcp:connect/3-4,
+ gen_udp:open/2 and gen_sctp:open/1-2 is now implicit if
+ the address argument or the 'ip' option contain an IPv6
+ address (8-tuple).</p>
+ <p>
+ Own Id: OTP-8822</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.14</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ os:find_executable can now be fed with the complete name
+ of the executable on Windows and still find it. I.e
+ os:find_executable("werl.exe") will work as
+ os:find_executable("werl").</p>
+ <p>
+ Own Id: OTP-3626</p>
+ </item>
+ <item>
+ <p>
+ The shell's line editing has been improved to more
+ resemble the behaviour of readline and other shells.
+ (Thanks to Dave Peticolas)</p>
+ <p>
+ Own Id: OTP-8635</p>
+ </item>
+ <item>
+ <p>Under certain circumstances the net kernel could hang.
+ (Thanks to Scott Lystig Fritchie.)</p>
+ <p>
+ Own Id: OTP-8643 Aux Id: seq11584 </p>
+ </item>
+ <item>
+ <p>
+ The kernel DNS resolver was leaking one or two ports if
+ the DNS reply could not be parsed or if the resolver(s)
+ caused noconnection type errors. Bug now fixed. A DNS
+ specification borderline truncated reply triggering the
+ port leakage bug has also been fixed.</p>
+ <p>
+ Own Id: OTP-8652</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>As of this version, the global name server no longer
+ supports nodes running Erlang/OTP R11B.</p>
+ <p>
+ Own Id: OTP-8527</p>
+ </item>
+ <item>
+ <p>
+ The file module's functions write,read and read_line now
+ handles named io_servers like 'standard_io' and
+ 'standard_error' correctly.</p>
+ <p>
+ Own Id: OTP-8611</p>
+ </item>
+ <item>
+ <p>
+ The functions file:advise/4 and file:datasync/1 have been
+ added. (Thanks to Filipe David Manana.)</p>
+ <p>
+ Own Id: OTP-8637</p>
+ </item>
+ <item>
+ <p>When exchanging groups between nodes <c>pg2</c> did
+ not remove duplicated members. This bug was introduced in
+ R13B03 (kernel-2.13.4).</p>
+ <p>
+ Own Id: OTP-8653</p>
+ </item>
+ <item>
+ <p>
+ There is a new option 'exclusive' to file:open/2 that
+ uses the OS O_EXCL flag where supported to open the file
+ in exclusive mode.</p>
+ <p>
+ Own Id: OTP-8670</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.13.5.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug introduced in Kernel 2.13.5.2 has been fixed.</p>
+ <p>
+ Own Id: OTP-8686 Aux Id: OTP-8643</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 2.13.5.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Under certain circumstances the net kernel could hang.
+ (Thanks to Scott Lystig Fritchie.)</p>
+ <p>
+ Own Id: OTP-8643 Aux Id: seq11584</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.13.5.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/doc/src/part_notes_history.xml b/lib/kernel/doc/src/part_notes_history.xml
index 07c7e4abea..a73cc911b8 100644
--- a/lib/kernel/doc/src/part_notes_history.xml
+++ b/lib/kernel/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml
index 86c6ea9178..2b81de170d 100644
--- a/lib/kernel/doc/src/rpc.xml
+++ b/lib/kernel/doc/src/rpc.xml
@@ -454,7 +454,7 @@
</desc>
</func>
<func>
- <name>pmap({Module, Function}, ExtraArgs, List2) -> List1</name>
+ <name>pmap({Module, Function}, ExtraArgs, List1) -> List2</name>
<fsummary>Parallell evaluation of mapping a function over a list </fsummary>
<type>
<v>Module = Function = atom()</v>
diff --git a/lib/kernel/doc/src/user.xml b/lib/kernel/doc/src/user.xml
index d9de2f4b04..4d0f044321 100644
--- a/lib/kernel/doc/src/user.xml
+++ b/lib/kernel/doc/src/user.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/kernel/internal_doc/distribution_handshake.txt b/lib/kernel/internal_doc/distribution_handshake.txt
index f64ebe0302..6a3ee22ed3 100644
--- a/lib/kernel/internal_doc/distribution_handshake.txt
+++ b/lib/kernel/internal_doc/distribution_handshake.txt
@@ -11,7 +11,7 @@ The TCP/IP distribution uses a handshake which expects a
connection based protocol, i.e. the protocol does not include
any authentication after the handshake procedure.
-This is not entirelly safe, as it is vulnerable against takeover
+This is not entirely safe, as it is vulnerable against takeover
attacks, but it is a tradeoff between fair safety and performance.
The cookies are never sent in cleartext and the handshake procedure
@@ -23,7 +23,7 @@ random numbers.
DEFINITIONS
-----------
-A challenge is a 32 bit integer number in big endian. Below the function
+A challenge is a 32 bit integer number in big endian order. Below the function
gen_challenge() returns a random 32 bit integer used as a challenge.
A digest is a (16 bytes) MD5 hash of [the Challenge (as text) concatenated
@@ -46,19 +46,19 @@ The cookies are text strings that can be viewed as passwords.
Every message in the handshake starts with a 16 bit big endian integer
which contains the length of the message (not counting the two initial bytes).
In erlang this corresponds to the gen_tcp option {packet, 2}. Note that after
-the handshake, the distribution switches to 4 byte backet headers.
+the handshake, the distribution switches to 4 byte packet headers.
THE HANDSHAKE IN DETAIL
-----------------------
-Imagine two nodes, node A, which initiates the handshake and node B, whitch
+Imagine two nodes, node A, which initiates the handshake and node B, which
accepts the connection.
1) connect/accept: A connects to B via TCP/IP and B accepts the connection.
2) send_name/receive_name: A sends an initial identification to B.
B receives the message. The message looks
-like this (every "square" beeing one byte and the packet header removed):
+like this (every "square" being one byte and the packet header removed):
+---+--------+--------+-----+-----+-----+-----+-----+-----+-...-+-----+
|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Name0|Name1| ... |NameN|
@@ -67,7 +67,7 @@ like this (every "square" beeing one byte and the packet header removed):
The 'n' is just a message tag,
Version0 & Version1 is the distribution version selected by node A,
based on information from EPMD. (16 bit big endian)
-Flag0 ... Flag3 is capability flags, the capabilities defined in dist.hrl.
+Flag0 ... Flag3 are capability flags, the capabilities defined in dist.hrl.
(32 bit big endian)
Name0 ... NameN is the full nodename of A, as a string of bytes (the
packet length denotes how long it is).
@@ -91,9 +91,9 @@ alive: A connection to the node is already active, which either means
This is the format of the status message:
-+---+-------+-------+ ... +-------+
++---+-------+-------+-...-+-------+
|'s'|Status0|Status1| ... |StatusN|
-+---+-------+-------+ ... +-------+
++---+-------+-------+-...-+-------+
's' is the message tag
Status0 ... StatusN is the status as a string (not terminated)
@@ -111,35 +111,35 @@ initially sent from A to B, with the addition of a 32 bit challenge:
+---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+---
|'n'|Version0|Version1|Flag0|Flag1|Flag2|Flag3|Chal0|Chal1|Chal2|Chal3|
-+---+--------+--------+-----+-----+-----+-----+-----+-----+---- +-----+---
++---+--------+--------+-----+-----+-----+-----+-----+-----+-----+-----+---
------+-----+-...-+-----+
Name0|Name1| ... |NameN|
------+-----+-... +-----+
-Where Chal0 ... Chal3 is the challenge as a 32 bit biog endian integer
+Where Chal0 ... Chal3 is the challenge as a 32 bit big endian integer
and the other fields are B's version, flags and full nodename.
5) send_challenge_reply/recv_challenge_reply: Now A has generated
-a digest and it's own challenge. Those are sent together in a package
+a digest and its own challenge. Those are sent together in a package
to B:
-+---+-----+-----+-----+-----+-----+-----+-----+-----+
-|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3|
-+---+-----+-----+-----+-----+-----+-----+---- +-----+
++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
+|'r'|Chal0|Chal1|Chal2|Chal3|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
++---+-----+-----+-----+-----+-----+-----+-----+-----+-...-+------+
Where 'r' is the tag, Chal0 ... Chal3 is A's challenge for B to handle and
-Dige0 ... Dige3 is the digest that A constructed from the challenge B sent
+Dige0 ... Dige15 is the digest that A constructed from the challenge B sent
in the previous step.
6) recv_challenge_ack/send_challenge_ack: B checks that the digest received
from A is correct and generates a digest from the challenge received from
A. The digest is then sent to A. The message looks like this:
-+---+-----+-----+-----+-----+
-|'a'|Dige0|Dige1|Dige2|Dige3|
-+---+-----+-----+---- +-----+
++---+-----+-----+-----+-----+-...-+------+
+|'a'|Dige0|Dige1|Dige2|Dige3| ... |Dige15|
++---+-----+-----+-----+-----+-...-+------+
-Where 'a' is the tag and Dige0 ... Dige3 is the digest calculated by B
+Where 'a' is the tag and Dige0 ... Dige15 is the digest calculated by B
for A's challenge.
7) A checks the digest from B and the connection is up.
@@ -206,7 +206,7 @@ Currently the following capability flags are defined:
%% The node implements distributed process monitoring.
-define(DFLAG_DIST_MONITOR,8).
-%% The node uses separate tag for fun's (labmdas) in the distribution protocol.
+%% The node uses separate tag for fun's (lambdas) in the distribution protocol.
-define(DFLAG_FUN_TAGS,16).
An R6 erlang node implements all of the above, while a C or Java node only
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index d9db23d652..2a193affd4 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -204,12 +204,12 @@ get_env(Key) ->
get_env(Application, Key) ->
application_controller:get_env(Application, Key).
--spec get_all_env() -> [] | [{atom(), any()}].
+-spec get_all_env() -> [{atom(), any()}].
get_all_env() ->
application_controller:get_pid_all_env(group_leader()).
--spec get_all_env(atom()) -> [] | [{atom(), any()}].
+-spec get_all_env(atom()) -> [{atom(), any()}].
get_all_env(Application) ->
application_controller:get_all_env(Application).
diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl
index 7fe30ae828..5c7fe2421d 100644
--- a/lib/kernel/src/auth.erl
+++ b/lib/kernel/src/auth.erl
@@ -50,6 +50,8 @@
%% Exported functions
%%----------------------------------------------------------------------
+-spec start_link() -> {'ok',pid()} | {'error', term()} | 'ignore'.
+
start_link() ->
gen_server:start_link({local, auth}, auth, [], []).
@@ -134,7 +136,9 @@ init([]) ->
%% The net kernel will let all message to the auth server
%% through as is
--type calls() :: 'echo' | 'sync_cookie' | {'set_cookie', node(), term()}.
+-type calls() :: 'echo' | 'sync_cookie'
+ | {'get_cookie', node()}
+ | {'set_cookie', node(), term()}.
-spec handle_call(calls(), {pid(), term()}, state()) ->
{'reply', 'hello' | 'true' | 'nocookie' | cookie(), state()}.
diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl
index affa5fc0fd..b0f99305f2 100644
--- a/lib/kernel/src/code.erl
+++ b/lib/kernel/src/code.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -66,43 +66,48 @@
set_primary_archive/3,
clash/0]).
+-export_type([load_error_rsn/0, load_ret/0]).
+
-include_lib("kernel/include/file.hrl").
%% User interface.
%%
-%% objfile_extension() -> ".beam"
-%% set_path(Dir*) -> true
-%% get_path() -> Dir*
-%% add_path(Dir) -> true | {error, What}
-%% add_patha(Dir) -> true | {error, What}
-%% add_pathz(Dir) -> true | {error, What}
-%% add_paths(DirList) -> true | {error, What}
-%% add_pathsa(DirList) -> true | {error, What}
-%% add_pathsz(DirList) -> true | {error, What}
-%% del_path(Dir) -> true | {error, What}
-%% replace_path(Name,Dir) -> true | {error, What}
-%% load_file(File) -> {error,What} | {module, Mod}
-%% load_abs(File) -> {error,What} | {module, Mod}
-%% load_abs(File,Mod) -> {error,What} | {module, Mod}
-%% load_binary(Mod,File,Bin) -> {error,What} | {module,Mod}
-%% ensure_loaded(Module) -> {error,What} | {module, Mod}
-%% delete(Module)
-%% purge(Module) kills all procs running old code
-%% soft_purge(Module) -> true | false
-%% is_loaded(Module) -> {file, File} | false
-%% all_loaded() -> {Module, File}*
-%% get_object_code(Mod) -> error | {Mod, Bin, Filename}
-%% stop() -> true
-%% root_dir()
-%% compiler_dir()
-%% lib_dir()
-%% priv_dir(Name)
-%% stick_dir(Dir) -> ok | error
-%% unstick_dir(Dir) -> ok | error
-%% is_sticky(Module) -> true | false
-%% which(Module) -> Filename
-%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason}
-%% clash() -> -> print out
+%% objfile_extension() -> ".beam"
+%% get_path() -> [Dir]
+%% set_path([Dir]) -> true | {error, bad_directory | bad_path}
+%% add_path(Dir) -> true | {error, bad_directory}
+%% add_patha(Dir) -> true | {error, bad_directory}
+%% add_pathz(Dir) -> true | {error, bad_directory}
+%% add_paths([Dir]) -> ok
+%% add_pathsa([Dir]) -> ok
+%% add_pathsz([Dir]) -> ok
+%% del_path(Dir) -> boolean() | {error, bad_name}
+%% replace_path(Name, Dir) -> true | replace_path_error()
+%% load_file(Module) -> {module, Module} | {error, What :: atom()}
+%% load_abs(File) -> {module, Module} | {error, What :: atom()}
+%% load_abs(File, Module) -> {module, Module} | {error, What :: atom()}
+%% load_binary(Module, File, Bin)-> {module, Module} | {error, What :: atom()}
+%% ensure_loaded(Module) -> {module, Module} | {error, What :: atom()}
+%% delete(Module) -> boolean()
+%% purge(Module) -> boolean() kills all procs running old code
+%% soft_purge(Module) -> boolean()
+%% is_loaded(Module) -> {file, loaded_filename()} | false
+%% all_loaded() -> [{Module, loaded_filename()}]
+%% get_object_code(Module) -> {Module, Bin, Filename} | error
+%% stop() -> no_return()
+%% root_dir() -> Dir
+%% compiler_dir() -> Dir
+%% lib_dir() -> Dir
+%% lib_dir(Application) -> Dir | {error, bad_name}
+%% priv_dir(Application) -> Dir | {error, bad_name}
+%% stick_dir(Dir) -> ok | error
+%% unstick_dir(Dir) -> ok | error
+%% stick_mod(Module) -> true
+%% unstick_mod(Module) -> true
+%% is_sticky(Module) -> boolean()
+%% which(Module) -> Filename | loaded_ret_atoms() | non_existing
+%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason}
+%% clash() -> ok prints out number of clashes
%%----------------------------------------------------------------------------
%% Some types for basic exported functions of this module
@@ -118,7 +123,7 @@
%% User interface
%%----------------------------------------------------------------------------
--spec objfile_extension() -> file:filename().
+-spec objfile_extension() -> nonempty_string().
objfile_extension() ->
init:objfile_extension().
@@ -136,21 +141,21 @@ load_abs(File) when is_list(File); is_atom(File) -> call({load_abs,File,[]}).
%% XXX Filename is also an atom(), e.g. 'cover_compiled'
-spec load_abs(Filename :: loaded_filename(), Module :: atom()) -> load_ret().
-load_abs(File,M) when (is_list(File) orelse is_atom(File)), is_atom(M) ->
+load_abs(File, M) when (is_list(File) orelse is_atom(File)), is_atom(M) ->
call({load_abs,File,M}).
%% XXX Filename is also an atom(), e.g. 'cover_compiled'
-spec load_binary(Module :: atom(), Filename :: loaded_filename(), Binary :: binary()) -> load_ret().
-load_binary(Mod,File,Bin)
+load_binary(Mod, File, Bin)
when is_atom(Mod), (is_list(File) orelse is_atom(File)), is_binary(Bin) ->
call({load_binary,Mod,File,Bin}).
-spec load_native_partial(Module :: atom(), Binary :: binary()) -> load_ret().
-load_native_partial(Mod,Bin) when is_atom(Mod), is_binary(Bin) ->
+load_native_partial(Mod, Bin) when is_atom(Mod), is_binary(Bin) ->
call({load_native_partial,Mod,Bin}).
-spec load_native_sticky(Module :: atom(), Binary :: binary(), WholeModule :: 'false' | binary()) -> load_ret().
-load_native_sticky(Mod,Bin,WholeModule)
+load_native_sticky(Mod, Bin, WholeModule)
when is_atom(Mod), is_binary(Bin),
(is_binary(WholeModule) orelse WholeModule =:= false) ->
call({load_native_sticky,Mod,Bin,WholeModule}).
@@ -158,7 +163,7 @@ load_native_sticky(Mod,Bin,WholeModule)
-spec delete(Module :: atom()) -> boolean().
delete(Mod) when is_atom(Mod) -> call({delete,Mod}).
--spec purge/1 :: (Module :: atom()) -> boolean().
+-spec purge(Module :: atom()) -> boolean().
purge(Mod) when is_atom(Mod) -> call({purge,Mod}).
-spec soft_purge(Module :: atom()) -> boolean().
@@ -193,7 +198,7 @@ lib_dir(App, SubDir) when is_atom(App), is_atom(SubDir) -> call({dir,{lib_dir,Ap
compiler_dir() -> call({dir,compiler_dir}).
%% XXX is_list() is for backwards compatibility -- take out in future version
--spec priv_dir(Appl :: atom()) -> file:filename() | {'error', 'bad_name'}.
+-spec priv_dir(App :: atom()) -> file:filename() | {'error', 'bad_name'}.
priv_dir(App) when is_atom(App) ; is_list(App) -> call({dir,{priv_dir,App}}).
-spec stick_dir(Directory :: file:filename()) -> 'ok' | 'error'.
@@ -211,19 +216,21 @@ unstick_mod(Mod) when is_atom(Mod) -> call({unstick_mod,Mod}).
-spec is_sticky(Module :: atom()) -> boolean().
is_sticky(Mod) when is_atom(Mod) -> call({is_sticky,Mod}).
--spec set_path(Directories :: [file:filename()]) -> 'true' | {'error', term()}.
+-spec set_path(Directories :: [file:filename()]) ->
+ 'true' | {'error', 'bad_directory' | 'bad_path'}.
set_path(PathList) when is_list(PathList) -> call({set_path,PathList}).
-spec get_path() -> [file:filename()].
get_path() -> call(get_path).
--spec add_path(Directory :: file:filename()) -> 'true' | {'error', term()}.
+-type add_path_ret() :: 'true' | {'error', 'bad_directory'}.
+-spec add_path(Directory :: file:filename()) -> add_path_ret().
add_path(Dir) when is_list(Dir) -> call({add_path,last,Dir}).
--spec add_pathz(Directory :: file:filename()) -> 'true' | {'error', term()}.
+-spec add_pathz(Directory :: file:filename()) -> add_path_ret().
add_pathz(Dir) when is_list(Dir) -> call({add_path,last,Dir}).
--spec add_patha(Directory :: file:filename()) -> 'true' | {'error', term()}.
+-spec add_patha(Directory :: file:filename()) -> add_path_ret().
add_patha(Dir) when is_list(Dir) -> call({add_path,first,Dir}).
-spec add_paths(Directories :: [file:filename()]) -> 'ok'.
@@ -235,14 +242,13 @@ add_pathsz(Dirs) when is_list(Dirs) -> call({add_paths,last,Dirs}).
-spec add_pathsa(Directories :: [file:filename()]) -> 'ok'.
add_pathsa(Dirs) when is_list(Dirs) -> call({add_paths,first,Dirs}).
-%% XXX Contract's input argument differs from add_path/1 -- why?
-spec del_path(Name :: file:filename() | atom()) -> boolean() | {'error', 'bad_name'}.
del_path(Name) when is_list(Name) ; is_atom(Name) -> call({del_path,Name}).
-type replace_path_error() :: {'error', 'bad_directory' | 'bad_name' | {'badarg',_}}.
-spec replace_path(Name:: atom(), Dir :: file:filename()) -> 'true' | replace_path_error().
-replace_path(Name, Dir) when (is_atom(Name) or is_list(Name)) and
- (is_atom(Dir) or is_list(Dir)) ->
+replace_path(Name, Dir) when (is_atom(Name) orelse is_list(Name)),
+ (is_atom(Dir) orelse is_list(Dir)) ->
call({replace_path,Name,Dir}).
-spec rehash() -> 'ok'.
@@ -273,19 +279,14 @@ start_link(Flags) ->
do_start(Flags) ->
%% The following module_info/1 calls are here to ensure
- %% that the modules are loaded prior to their use elsewhere in
+ %% that these modules are loaded prior to their use elsewhere in
%% the code_server.
%% Otherwise a deadlock may occur when the code_server is starting.
- code_server:module_info(module),
- packages:module_info(module),
+ code_server = code_server:module_info(module),
+ packages = packages:module_info(module),
catch hipe_unified_loader:load_hipe_modules(),
- gb_sets:module_info(module),
- gb_trees:module_info(module),
-
- ets:module_info(module),
- os:module_info(module),
- filename:module_info(module),
- lists:module_info(module),
+ Modules2 = [gb_sets, gb_trees, ets, os, binary, unicode, filename, lists],
+ lists:foreach(fun (M) -> M = M:module_info(module) end, Modules2),
Mode = get_mode(Flags),
case init:get_argument(root) of
@@ -293,7 +294,7 @@ do_start(Flags) ->
Root = filename:join([Root0]), % Normalize. Use filename
case code_server:start_link([Root,Mode]) of
{ok,_Pid} = Ok2 ->
- if
+ if
Mode =:= interactive ->
case lists:member(stick, Flags) of
true -> do_stick_dirs();
@@ -302,12 +303,14 @@ do_start(Flags) ->
true ->
ok
end,
+ %% Quietly load native code for all modules loaded so far
+ catch load_native_code_for_all_loaded(),
Ok2;
Other ->
Other
end;
Other ->
- error_logger:error_msg("Can not start code server ~w ~n",[Other]),
+ error_logger:error_msg("Can not start code server ~w ~n", [Other]),
{error, crash}
end.
@@ -324,7 +327,7 @@ do_s(Lib) ->
%% The return value is intentionally ignored. Missing
%% directories is not a fatal error. (In embedded systems,
%% there is usually no compiler directory.)
- stick_dir(filename:append(Dir, "ebin")),
+ _ = stick_dir(filename:append(Dir, "ebin")),
ok
end.
@@ -422,7 +425,7 @@ where_is_file(Path, File) when is_list(Path), is_list(File) ->
-spec set_primary_archive(ArchiveFile :: file:filename(),
ArchiveBin :: binary(),
- FileInfo :: #file_info{})
+ FileInfo :: file:file_info())
-> 'ok' | {'error', atom()}.
set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo)
@@ -479,13 +482,13 @@ filter(Ext, _, {ok,Files}) ->
filter2(Ext, length(Ext), Files).
filter2(_Ext, _Extlen, []) -> [];
-filter2(Ext, Extlen,[File|Tail]) ->
- case has_ext(Ext,Extlen, File) of
+filter2(Ext, Extlen, [File|Tail]) ->
+ case has_ext(Ext, Extlen, File) of
true -> [File | filter2(Ext, Extlen, Tail)];
false -> filter2(Ext, Extlen, Tail)
end.
-has_ext(Ext, Extlen,File) ->
+has_ext(Ext, Extlen, File) ->
L = length(File),
case catch lists:nthtail(L - Extlen, File) of
Ext -> true;
@@ -494,3 +497,19 @@ has_ext(Ext, Extlen,File) ->
to_path(X) ->
filename:join(packages:split(X)).
+
+-spec load_native_code_for_all_loaded() -> ok.
+load_native_code_for_all_loaded() ->
+ Architecture = erlang:system_info(hipe_architecture),
+ ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ lists:foreach(fun({Module, BeamFilename}) ->
+ case code:is_module_native(Module) of
+ false ->
+ case beam_lib:chunks(BeamFilename, [ChunkName]) of
+ {ok,{_,[{_,Bin}]}} when is_binary(Bin) ->
+ load_native_partial(Module, Bin);
+ {error, beam_lib, _} -> ok
+ end;
+ true -> ok
+ end
+ end, all_loaded()).
diff --git a/lib/kernel/src/disk_log_1.erl b/lib/kernel/src/disk_log_1.erl
index 8ccdb88d12..266df84a03 100644
--- a/lib/kernel/src/disk_log_1.erl
+++ b/lib/kernel/src/disk_log_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -581,11 +581,13 @@ done_scan(In, Out, OutName, FName, RecoveredTerms, BadChars) ->
file:delete(OutName),
throw(Error)
end.
-
+
+-spec repair_err(file:io_device(), #cache{}, file:filename(),
+ file:filename(), {'error', file:posix()}) -> no_return().
repair_err(In, Out, OutName, ErrFileName, Error) ->
file:close(In),
catch fclose(Out, OutName),
- % OutName is often the culprit, try to remove it anyway...
+ %% OutName is often the culprit, try to remove it anyway...
file:delete(OutName),
file_error(ErrFileName, Error).
diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl
index a2937d60b8..f0d54a2f3e 100644
--- a/lib/kernel/src/dist_util.erl
+++ b/lib/kernel/src/dist_util.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%%----------------------------------------------------------------------
@@ -564,7 +564,7 @@ recv_challenge(#hs_data{socket=Socket,other_node=Node,
case Recv(Socket, 0, infinity) of
{ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} ->
Flags = ?u32(Fl1,Fl2,Fl3,Fl4),
- case {list_to_existing_atom(Ns),?u16(V1,V0)} of
+ try {list_to_existing_atom(Ns),?u16(V1,V0)} of
{Node,Version} ->
Challenge = ?u32(CA3,CA2,CA1,CA0),
?trace("recv: node=~w, challenge=~w version=~w\n",
@@ -572,6 +572,9 @@ recv_challenge(#hs_data{socket=Socket,other_node=Node,
{Flags,Challenge};
_ ->
?shutdown(no_node)
+ catch
+ error:badarg ->
+ ?shutdown(no_node)
end;
_ ->
?shutdown(no_node)
diff --git a/lib/kernel/src/erl_ddll.erl b/lib/kernel/src/erl_ddll.erl
index 88f91de24f..ce64589a29 100644
--- a/lib/kernel/src/erl_ddll.erl
+++ b/lib/kernel/src/erl_ddll.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Dynamic Driver Loader and Linker
@@ -29,6 +29,11 @@
%%----------------------------------------------------------------------------
+-type path() :: string() | atom().
+-type driver() :: string() | atom().
+
+%%----------------------------------------------------------------------------
+
-spec start() -> {'error', {'already_started', 'undefined'}}.
start() ->
@@ -39,13 +44,13 @@ start() ->
stop() ->
ok.
--spec load_driver(Path :: string() | atom(), Driver :: string() | atom()) ->
+-spec load_driver(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
load_driver(Path, Driver) ->
do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]).
--spec load(Path :: string() | atom(), Driver :: string() | atom()) ->
+-spec load(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
load(Path, Driver) ->
@@ -95,23 +100,23 @@ do_unload_driver(Driver,Flags) ->
end
end.
--spec unload_driver(Driver :: string() | atom()) -> 'ok' | {'error', any()}.
+-spec unload_driver(Driver :: driver()) -> 'ok' | {'error', any()}.
unload_driver(Driver) ->
do_unload_driver(Driver,[{monitor,pending_driver},kill_ports]).
--spec unload(Driver :: string() | atom()) -> 'ok' | {'error', any()}.
+-spec unload(Driver :: driver()) -> 'ok' | {'error', any()}.
unload(Driver) ->
do_unload_driver(Driver,[]).
--spec reload(Path :: string() | atom(), Driver :: string() | atom()) ->
+-spec reload(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
reload(Path,Driver) ->
do_load_driver(Path, Driver, [{reload,pending_driver}]).
--spec reload_driver(Path :: string() | atom(), Driver :: string() | atom()) ->
+-spec reload_driver(Path :: path(), Driver :: driver()) ->
'ok' | {'error', any()}.
reload_driver(Path,Driver) ->
@@ -122,15 +127,15 @@ reload_driver(Path,Driver) ->
format_error(Code) ->
case Code of
- % This is the only error code returned only from erlang code...
- % 'permanent' has a translation in the emulator, even though the erlang code uses it to...
+ %% This is the only error code returned only from erlang code...
+ %% 'permanent' has a translation in the emulator, even though the erlang code uses it to...
load_cancelled ->
"Loading was cancelled from other process";
_ ->
erl_ddll:format_error_int(Code)
end.
--spec info(Driver :: string() | atom()) -> [{atom(), any()}].
+-spec info(Driver :: driver()) -> [{atom(), any()}, ...].
info(Driver) ->
[{processes, erl_ddll:info(Driver,processes)},
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index 4a22637304..91af49f303 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -210,19 +210,6 @@ open({A,B,C,D,E,F,G,H}=EpmdAddr, Timeout) when ?ip6(A,B,C,D,E,F,G,H) ->
close(Socket) ->
gen_tcp:close(Socket).
-
-do_register_node_v0(NodeName, TcpPort) ->
- case open() of
- {ok, Socket} ->
- Name = cstring(NodeName),
- Len = 1+2+length(Name),
- gen_tcp:send(Socket, [?int16(Len), ?EPMD_ALIVE,
- ?int16(TcpPort), Name]),
- wait_for_reg_reply_v0(Socket, []);
- Error ->
- Error
- end.
-
do_register_node(NodeName, TcpPort) ->
case open() of
{ok, Socket} ->
@@ -240,14 +227,7 @@ do_register_node(NodeName, TcpPort) ->
Name,
?int16(Elen),
Extra]),
- case wait_for_reg_reply(Socket, []) of
- {error, epmd_close} ->
- %% could be old epmd; try old protocol
-% erlang:display('trying old'),
- do_register_node_v0(NodeName, TcpPort);
- Other ->
- Other
- end;
+ wait_for_reg_reply(Socket, []);
Error ->
Error
end.
@@ -305,41 +285,9 @@ wait_for_reg_reply(Socket, SoFar) ->
{error, no_reg_reply_from_epmd}
end.
-wait_for_reg_reply_v0(Socket, SoFar) ->
- receive
- {tcp, Socket, Data0} ->
- case SoFar ++ Data0 of
- [$Y, A, B] ->
- {alive, Socket, ?u16(A, B)};
- Data when length(Data) < 3 ->
- wait_for_reg_reply(Socket, Data);
- Garbage ->
- {error, {garbage_from_epmd, Garbage}}
- end;
- {tcp_closed, Socket} ->
- {error, duplicate_name} % A guess -- the most likely reason.
- after 10000 ->
- gen_tcp:close(Socket),
- {error, no_reg_reply_from_epmd}
- end.
%%
%% Lookup a node "Name" at Host
%%
-get_port_v0(Node, EpmdAddress) ->
- case open(EpmdAddress) of
- {ok, Socket} ->
- Name = cstring(Node),
- Len = 1+length(Name),
- gen_tcp:send(Socket, [?int16(Len),?EPMD_PORT_PLEASE, Name]),
- wait_for_port_reply_v0(Socket, []);
- _Error ->
- ?port_please_failure(),
- noport
- end.
-
-%%% Not used anymore
-%%% get_port(Node, EpmdAddress) ->
-%%% get_port(Node, EpmdAddress, infinity).
get_port(Node, EpmdAddress, Timeout) ->
case open(EpmdAddress, Timeout) of
@@ -347,40 +295,12 @@ get_port(Node, EpmdAddress, Timeout) ->
Name = to_string(Node),
Len = 1+length(Name),
gen_tcp:send(Socket, [?int16(Len),?EPMD_PORT_PLEASE2_REQ, Name]),
- Reply = wait_for_port_reply(Socket, []),
- case Reply of
- closed ->
- get_port_v0(Node, EpmdAddress);
- Other ->
- Other
- end;
+ wait_for_port_reply(Socket, []);
_Error ->
?port_please_failure2(_Error),
noport
end.
-wait_for_port_reply_v0(Socket, SoFar) ->
- receive
- {tcp, Socket, Data0} ->
-% io:format("got ~p~n", [Data0]),
- case SoFar ++ Data0 of
- [A, B] ->
- wait_for_close(Socket, {port, ?u16(A, B), 0});
-% wait_for_close(Socket, {port, ?u16(A, B)});
- Data when length(Data) < 2 ->
- wait_for_port_reply_v0(Socket, Data);
- Garbage ->
- ?port_please_failure(),
- {error, {garbage_from_epmd, Garbage}}
- end;
- {tcp_closed, Socket} ->
- ?port_please_failure(),
- noport
- after 10000 ->
- ?port_please_failure(),
- gen_tcp:close(Socket),
- noport
- end.
wait_for_port_reply(Socket, SoFar) ->
receive
@@ -487,8 +407,6 @@ wait_for_close(Socket, Reply) ->
%%
%% Creates a (flat) null terminated string from atom or list.
%%
-cstring(S) when is_atom(S) -> cstring(atom_to_list(S));
-cstring(S) when is_list(S) -> S ++ [0].
to_string(S) when is_atom(S) -> atom_to_list(S);
to_string(S) when is_list(S) -> S.
diff --git a/lib/kernel/src/erl_epmd.hrl b/lib/kernel/src/erl_epmd.hrl
index 47ab6195d8..5a50fda508 100644
--- a/lib/kernel/src/erl_epmd.hrl
+++ b/lib/kernel/src/erl_epmd.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,16 +17,13 @@
%% %CopyrightEnd%
%%
--define(EPMD_ALIVE, $a).
--define(EPMD_PORT_PLEASE, $p).
--define(EPMD_NAMES, $n).
--define(EPMD_DUMP, $d).
--define(EPMD_KILL, $k).
--define(EPMD_STOP, $s).
-
--define(EPMD_ALIVE_OK, $Y).
-
-define(EPMD_ALIVE2_REQ, $x).
-define(EPMD_PORT_PLEASE2_REQ, $z).
-define(EPMD_ALIVE2_RESP, $y).
-define(EPMD_PORT2_RESP, $w).
+-define(EPMD_NAMES, $n).
+
+%% Commands used only by interactive client
+-define(EPMD_DUMP, $d).
+-define(EPMD_KILL, $k).
+-define(EPMD_STOP, $s).
diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl
index 17dd02acd4..6f69f4ccb9 100644
--- a/lib/kernel/src/error_handler.erl
+++ b/lib/kernel/src/error_handler.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,6 +17,11 @@
%% %CopyrightEnd%
%%
-module(error_handler).
+%% FIXME: remove no_native directive after HiPE has been changed to make
+%% remote calls link to the target's Export* like BEAM does.
+%% For a detailed explanation see the commit titled
+%% "error_handler: add no_native compiler directive"
+-compile(no_native).
%% A simple error handler.
@@ -75,9 +80,13 @@ int() -> int.
%%
%% Crash providing a beautiful stack backtrace.
%%
+-spec crash(atom(), [term()]) -> no_return().
+
crash(Fun, Args) ->
crash({Fun,Args}).
+-spec crash(atom(), atom(), arity()) -> no_return().
+
crash(M, F, A) ->
crash({M,F,A}).
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index 46ffa9d708..88bcf9a9cc 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,11 +36,11 @@
%% Specialized
-export([ipread_s32bu_p32bu/3]).
%% Generic file contents.
--export([open/2, close/1,
+-export([open/2, close/1, advise/4,
read/2, write/2,
pread/2, pread/3, pwrite/2, pwrite/3,
read_line/1,
- position/2, truncate/1, sync/1,
+ position/2, truncate/1, datasync/1, sync/1,
copy/2, copy/3]).
%% High level operations
-export([consult/1, path_consult/2]).
@@ -61,6 +61,9 @@
-export([ipread_s32bu_p32bu_int/3]).
+%% Types that can be used from other modules -- alphabetically ordered.
+-export_type([date_time/0, fd/0, file_info/0, filename/0, io_device/0,
+ name/0, posix/0]).
%%% Includes and defines
-include("file.hrl").
@@ -72,23 +75,34 @@
-define(RAM_FILE, ram_file). % Module
%% data types
--type filename() :: string().
+-type filename() :: string() | binary().
-type file_info() :: #file_info{}.
-type fd() :: #file_descriptor{}.
-type io_device() :: pid() | fd().
-type location() :: integer() | {'bof', integer()} | {'cur', integer()}
| {'eof', integer()} | 'bof' | 'cur' | 'eof'.
--type mode() :: 'read' | 'write' | 'append' | 'raw' | 'binary' |
- {'delayed_write', non_neg_integer(), non_neg_integer()} |
- 'delayed_write' | {'read_ahead', pos_integer()} |
- 'read_ahead' | 'compressed'.
--type name() :: string() | atom() | [name()].
--type posix() :: atom().
+-type mode() :: 'read' | 'write' | 'append'
+ | 'exclusive' | 'raw' | 'binary'
+ | {'delayed_write', non_neg_integer(), non_neg_integer()}
+ | 'delayed_write' | {'read_ahead', pos_integer()}
+ | 'read_ahead' | 'compressed'
+ | {'encoding', unicode:encoding()}.
+-type name() :: string() | atom() | [name()] | binary().
+-type posix() :: 'eacces' | 'eagain' | 'ebadf' | 'ebusy' | 'edquot'
+ | 'eexist' | 'efault' | 'efbig' | 'eintr' | 'einval'
+ | 'eio' | 'eisdir' | 'eloop' | 'emfile' | 'emlink'
+ | 'enametoolong'
+ | 'enfile' | 'enodev' | 'enoent' | 'enomem' | 'enospc'
+ | 'enotblk' | 'enotdir' | 'enotsup' | 'enxio' | 'eperm'
+ | 'epipe' | 'erofs' | 'espipe' | 'esrch' | 'estale'
+ | 'exdev'.
-type bindings() :: any().
-type date() :: {pos_integer(), pos_integer(), pos_integer()}.
-type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
-type date_time() :: {date(), time()}.
+-type posix_file_advise() :: 'normal' | 'sequential' | 'random'
+ | 'no_reuse' | 'will_need' | 'dont_need'.
%%%-----------------------------------------------------------------
%%% General functions
@@ -169,7 +183,7 @@ make_dir(Name) ->
del_dir(Name) ->
check_and_call(del_dir, [file_name(Name)]).
--spec read_file_info(Name :: name()) -> {'ok', #file_info{}} | {'error', posix()}.
+-spec read_file_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}.
read_file_info(Name) ->
check_and_call(read_file_info, [file_name(Name)]).
@@ -179,7 +193,7 @@ read_file_info(Name) ->
altname(Name) ->
check_and_call(altname, [file_name(Name)]).
--spec read_link_info(Name :: name()) -> {'ok', #file_info{}} | {'error', posix()}.
+-spec read_link_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}.
read_link_info(Name) ->
check_and_call(read_link_info, [file_name(Name)]).
@@ -189,7 +203,7 @@ read_link_info(Name) ->
read_link(Name) ->
check_and_call(read_link, [file_name(Name)]).
--spec write_file_info(Name :: name(), Info :: #file_info{}) ->
+-spec write_file_info(Name :: name(), Info :: file_info()) ->
'ok' | {'error', posix()}.
write_file_info(Name, Info = #file_info{}) ->
@@ -200,7 +214,8 @@ write_file_info(Name, Info = #file_info{}) ->
list_dir(Name) ->
check_and_call(list_dir, [file_name(Name)]).
--spec read_file(Name :: name()) -> {'ok', binary()} | {'error', posix()}.
+-spec read_file(Name :: name()) ->
+ {'ok', binary()} | {'error', posix() | 'terminated' | 'system_limit'}.
read_file(Name) ->
check_and_call(read_file, [file_name(Name)]).
@@ -215,17 +230,17 @@ make_link(Old, New) ->
make_symlink(Old, New) ->
check_and_call(make_symlink, [file_name(Old), file_name(New)]).
--spec write_file(Name :: name(), Bin :: binary()) -> 'ok' | {'error', posix()}.
+-spec write_file(Name :: name(), Bin :: iodata()) ->
+ 'ok' | {'error', posix() | 'terminated' | 'system_limit'}.
write_file(Name, Bin) ->
check_and_call(write_file, [file_name(Name), make_binary(Bin)]).
%% This whole operation should be moved to the file_server and prim_file
%% when it is time to change file server protocol again.
-%% Meanwhile, it is implemented here, slihtly less efficient.
-%%
+%% Meanwhile, it is implemented here, slightly less efficient.
--spec write_file(Name :: name(), Bin :: binary(), Modes :: [mode()]) ->
+-spec write_file(Name :: name(), Bin :: iodata(), Modes :: [mode()]) ->
'ok' | {'error', posix()}.
write_file(Name, Bin, ModeList) when is_list(ModeList) ->
@@ -281,7 +296,7 @@ raw_write_file_info(Name, #file_info{} = Info) ->
%% Contemporary mode specification - list of options
-spec open(Name :: name(), Modes :: [mode()]) ->
- {'ok', io_device()} | {'error', posix()}.
+ {'ok', io_device()} | {'error', posix() | 'system_limit'}.
open(Item, ModeList) when is_list(ModeList) ->
case lists:member(raw, ModeList) of
@@ -334,7 +349,7 @@ open(Item, Mode) ->
%%% The File argument must be either a Pid or a handle
%%% returned from ?PRIM_FILE:open.
--spec close(File :: io_device()) -> 'ok' | {'error', posix()}.
+-spec close(File :: io_device()) -> 'ok' | {'error', posix() | 'terminated'}.
close(File) when is_pid(File) ->
R = file_request(File, close),
@@ -352,10 +367,22 @@ close(#file_descriptor{module = Module} = Handle) ->
close(_) ->
{error, badarg}.
--spec read(File :: io_device(), Size :: non_neg_integer()) ->
+-spec advise(File :: io_device(), Offset :: integer(),
+ Length :: integer(), Advise :: posix_file_advise()) ->
+ 'ok' | {'error', posix()}.
+
+advise(File, Offset, Length, Advise) when is_pid(File) ->
+ R = file_request(File, {advise, Offset, Length, Advise}),
+ wait_file_reply(File, R);
+advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) ->
+ Module:advise(Handle, Offset, Length, Advise);
+advise(_, _, _, _) ->
+ {error, badarg}.
+
+-spec read(File :: io_device() | atom(), Size :: non_neg_integer()) ->
'eof' | {'ok', [char()] | binary()} | {'error', posix()}.
-read(File, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 ->
+read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 ->
case io:request(File, {get_chars, '', Sz}) of
Data when is_list(Data); is_binary(Data) ->
{ok, Data};
@@ -368,10 +395,10 @@ read(#file_descriptor{module = Module} = Handle, Sz)
read(_, _) ->
{error, badarg}.
--spec read_line(File :: io_device()) ->
+-spec read_line(File :: io_device() | atom()) ->
'eof' | {'ok', [char()] | binary()} | {'error', posix()}.
-read_line(File) when is_pid(File) ->
+read_line(File) when (is_pid(File) orelse is_atom(File)) ->
case io:request(File, {get_line, ''}) of
Data when is_list(Data); is_binary(Data) ->
{ok, Data};
@@ -422,10 +449,10 @@ pread(#file_descriptor{module = Module} = Handle, Offs, Sz)
pread(_, _, _) ->
{error, badarg}.
--spec write(File :: io_device(), Byte :: iodata()) ->
- 'ok' | {'error', posix()}.
+-spec write(File :: io_device() | atom(), Byte :: iodata()) ->
+ 'ok' | {'error', posix() | 'terminated'}.
-write(File, Bytes) when is_pid(File) ->
+write(File, Bytes) when (is_pid(File) orelse is_atom(File)) ->
case make_binary(Bytes) of
Bin when is_binary(Bin) ->
io:request(File, {put_chars,Bin});
@@ -472,6 +499,16 @@ pwrite(#file_descriptor{module = Module} = Handle, Offs, Bytes) ->
pwrite(_, _, _) ->
{error, badarg}.
+-spec datasync(File :: io_device()) -> 'ok' | {'error', posix()}.
+
+datasync(File) when is_pid(File) ->
+ R = file_request(File, datasync),
+ wait_file_reply(File, R);
+datasync(#file_descriptor{module = Module} = Handle) ->
+ Module:datasync(Handle);
+datasync(_) ->
+ {error, badarg}.
+
-spec sync(File :: io_device()) -> 'ok' | {'error', posix()}.
sync(File) when is_pid(File) ->
@@ -997,22 +1034,26 @@ path_open_first([], _Name, _Mode, LastError) ->
%% Generates a flat file name from a deep list of atoms and
%% characters (integers).
+file_name(N) when is_binary(N) ->
+ N;
file_name(N) ->
try
- file_name_1(N)
+ file_name_1(N,file:native_name_encoding())
catch Reason ->
{error, Reason}
end.
-file_name_1([C|T]) when is_integer(C), C > 0, C =< 255 ->
- [C|file_name_1(T)];
-file_name_1([H|T]) ->
- file_name_1(H) ++ file_name_1(T);
-file_name_1([]) ->
+file_name_1([C|T],latin1) when is_integer(C), C < 256->
+ [C|file_name_1(T,latin1)];
+file_name_1([C|T],utf8) when is_integer(C) ->
+ [C|file_name_1(T,utf8)];
+file_name_1([H|T],E) ->
+ file_name_1(H,E) ++ file_name_1(T,E);
+file_name_1([],_) ->
[];
-file_name_1(N) when is_atom(N) ->
+file_name_1(N,_) when is_atom(N) ->
atom_to_list(N);
-file_name_1(_) ->
+file_name_1(_,_) ->
throw(badarg).
make_binary(Bin) when is_binary(Bin) ->
diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl
index 3ac35a209d..14da9c1a55 100644
--- a/lib/kernel/src/file_io_server.erl
+++ b/lib/kernel/src/file_io_server.erl
@@ -44,11 +44,11 @@ format_error(ErrorId) ->
erl_posix_msg:message(ErrorId).
start(Owner, FileName, ModeList)
- when is_pid(Owner), is_list(FileName), is_list(ModeList) ->
+ when is_pid(Owner), (is_list(FileName) orelse is_binary(FileName)), is_list(ModeList) ->
do_start(spawn, Owner, FileName, ModeList).
start_link(Owner, FileName, ModeList)
- when is_pid(Owner), is_list(FileName), is_list(ModeList) ->
+ when is_pid(Owner), (is_list(FileName) orelse is_binary(FileName)), is_list(ModeList) ->
do_start(spawn_link, Owner, FileName, ModeList).
%%%-----------------------------------------------------------------
@@ -198,6 +198,14 @@ io_reply(From, ReplyAs, Reply) ->
%%%-----------------------------------------------------------------
%%% file requests
+file_request({advise,Offset,Length,Advise},
+ #state{handle=Handle}=State) ->
+ case ?PRIM_FILE:advise(Handle, Offset, Length, Advise) of
+ {error,_}=Reply ->
+ {stop,normal,Reply,State};
+ Reply ->
+ {reply,Reply,State}
+ end;
file_request({pread,At,Sz},
#state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) ->
case position(Handle, At, Buf) of
@@ -219,6 +227,14 @@ file_request({pwrite,At,Data},
Reply ->
std_reply(Reply, State)
end;
+file_request(datasync,
+ #state{handle=Handle}=State) ->
+ case ?PRIM_FILE:datasync(Handle) of
+ {error,_}=Reply ->
+ {stop,normal,Reply,State};
+ Reply ->
+ {reply,Reply,State}
+ end;
file_request(sync,
#state{handle=Handle}=State) ->
case ?PRIM_FILE:sync(Handle) of
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index 5a31e3976f..cccfa75005 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -39,7 +39,7 @@ open() ->
open([]).
open(Opts) when is_list(Opts) ->
- Mod = mod(Opts),
+ Mod = mod(Opts, undefined),
case Mod:open(Opts) of
{error,badarg} ->
erlang:error(badarg, [Opts]);
@@ -166,18 +166,14 @@ send(S, #sctp_assoc_change{assoc_id=AssocId}, Stream, Data)
when is_port(S), is_integer(Stream) ->
case inet_db:lookup_socket(S) of
{ok,Mod} ->
- Mod:sendmsg(S, #sctp_sndrcvinfo{
- stream = Stream,
- assoc_id = AssocId}, Data);
+ Mod:send(S, AssocId, Stream, Data);
Error -> Error
end;
send(S, AssocId, Stream, Data)
when is_port(S), is_integer(AssocId), is_integer(Stream) ->
case inet_db:lookup_socket(S) of
{ok,Mod} ->
- Mod:sendmsg(S, #sctp_sndrcvinfo{
- stream = Stream,
- assoc_id = AssocId}, Data);
+ Mod:send(S, AssocId, Stream, Data);
Error -> Error
end;
send(S, AssocChange, Stream, Data) ->
@@ -238,17 +234,27 @@ controlling_process(S, Pid) ->
%% Utilites
%%
-%% Get the SCTP moudule
-mod() -> inet_db:sctp_module().
+%% Get the SCTP module, but IPv6 address overrides default IPv4
+mod(Address) ->
+ case inet_db:sctp_module() of
+ inet_sctp when tuple_size(Address) =:= 8 ->
+ inet6_sctp;
+ Mod ->
+ Mod
+ end.
%% Get the SCTP module, but option sctp_module|inet|inet6 overrides
-mod([{sctp_module,Mod}|_]) ->
+mod([{sctp_module,Mod}|_], _Address) ->
Mod;
-mod([inet|_]) ->
+mod([inet|_], _Address) ->
inet_sctp;
-mod([inet6|_]) ->
+mod([inet6|_], _Address) ->
inet6_sctp;
-mod([_|Opts]) ->
- mod(Opts);
-mod([]) ->
- mod().
+mod([{ip, Address}|Opts], _) ->
+ mod(Opts, Address);
+mod([{ifaddr, Address}|Opts], _) ->
+ mod(Opts, Address);
+mod([_|Opts], Address) ->
+ mod(Opts, Address);
+mod([], Address) ->
+ mod(Address).
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index 7401b06a64..16a87d71b6 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,7 +46,7 @@ connect(Address, Port, Opts, Time) ->
end.
connect1(Address,Port,Opts,Timer) ->
- Mod = mod(Opts),
+ Mod = mod(Opts, Address),
case Mod:getaddrs(Address,Timer) of
{ok,IPs} ->
case Mod:getserv(Port) of
@@ -73,7 +73,7 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) ->
%% Listen on a tcp port
%%
listen(Port, Opts) ->
- Mod = mod(Opts),
+ Mod = mod(Opts, undefined),
case Mod:getserv(Port) of
{ok,TP} ->
Mod:listen(TP, Opts);
@@ -173,20 +173,30 @@ controlling_process(S, NewOwner) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- Mod = mod(Opts),
+ Mod = mod(Opts, undefined),
Mod:fdopen(Fd, Opts).
-%% Get the tcp_module
-mod() -> inet_db:tcp_module().
+%% Get the tcp_module, but IPv6 address overrides default IPv4
+mod(Address) ->
+ case inet_db:tcp_module() of
+ inet_tcp when tuple_size(Address) =:= 8 ->
+ inet6_tcp;
+ Mod ->
+ Mod
+ end.
%% Get the tcp_module, but option tcp_module|inet|inet6 overrides
-mod([{tcp_module,Mod}|_]) ->
+mod([{tcp_module,Mod}|_], _Address) ->
Mod;
-mod([inet|_]) ->
+mod([inet|_], _Address) ->
inet_tcp;
-mod([inet6|_]) ->
+mod([inet6|_], _Address) ->
inet6_tcp;
-mod([_|Opts]) ->
- mod(Opts);
-mod([]) ->
- mod().
+mod([{ip, Address}|Opts], _) ->
+ mod(Opts, Address);
+mod([{ifaddr, Address}|Opts], _) ->
+ mod(Opts, Address);
+mod([_|Opts], Address) ->
+ mod(Opts, Address);
+mod([], Address) ->
+ mod(Address).
diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl
index 6bded4bda6..99020c7b6c 100644
--- a/lib/kernel/src/gen_udp.erl
+++ b/lib/kernel/src/gen_udp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@ open(Port) ->
open(Port, []).
open(Port, Opts) ->
- Mod = mod(Opts),
+ Mod = mod(Opts, undefined),
{ok,UP} = Mod:getserv(Port),
Mod:open(UP, Opts).
@@ -97,21 +97,31 @@ controlling_process(S, NewOwner) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- Mod = mod(),
+ Mod = mod(Opts, undefined),
Mod:fdopen(Fd, Opts).
-%% Get the udp_module
-mod() -> inet_db:udp_module().
+%% Get the udp_module, but IPv6 address overrides default IPv4
+mod(Address) ->
+ case inet_db:udp_module() of
+ inet_udp when tuple_size(Address) =:= 8 ->
+ inet6_udp;
+ Mod ->
+ Mod
+ end.
%% Get the udp_module, but option udp_module|inet|inet6 overrides
-mod([{udp_module,Mod}|_]) ->
+mod([{udp_module,Mod}|_], _Address) ->
Mod;
-mod([inet|_]) ->
+mod([inet|_], _Address) ->
inet_udp;
-mod([inet6|_]) ->
+mod([inet6|_], _Address) ->
inet6_udp;
-mod([_|Opts]) ->
- mod(Opts);
-mod([]) ->
- mod().
+mod([{ip, Address}|Opts], _) ->
+ mod(Opts, Address);
+mod([{ifaddr, Address}|Opts], _) ->
+ mod(Opts, Address);
+mod([_|Opts], Address) ->
+ mod(Opts, Address);
+mod([], Address) ->
+ mod(Address).
diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl
index 081e7e2f93..6343acd000 100644
--- a/lib/kernel/src/global.erl
+++ b/lib/kernel/src/global.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -113,8 +113,9 @@
syncers = [] :: [pid()],
node_name = node() :: node(),
the_locker, the_registrar, trace,
- global_lock_down = false
+ global_lock_down = false :: boolean()
}).
+-type state() :: #state{}.
%%% There are also ETS tables used for bookkeeping of locks and names
%%% (the first position is the key):
@@ -399,6 +400,9 @@ info() ->
%%%-----------------------------------------------------------------
%%% Call-back functions from gen_server
%%%-----------------------------------------------------------------
+
+-spec init([]) -> {'ok', state()}.
+
init([]) ->
process_flag(trap_exit, true),
_ = ets:new(global_locks, [set, named_table, protected]),
@@ -542,6 +546,11 @@ init([]) ->
%% sent by each node to all new nodes (Node becomes known to them)
%%-----------------------------------------------------------------
+-spec handle_call(term(), {pid(), term()}, state()) ->
+ {'noreply', state()} |
+ {'reply', term(), state()} |
+ {'stop', 'normal', 'stopped', state()}.
+
handle_call({whereis, Name}, From, S) ->
do_whereis(Name, From),
{noreply, S};
@@ -621,6 +630,9 @@ handle_call(Request, From, S) ->
%% init_connect
%%
%%========================================================================
+
+-spec handle_cast(term(), state()) -> {'noreply', state()}.
+
handle_cast({init_connect, Vsn, Node, InitMsg}, S) ->
%% Sent from global_name_server at Node.
?trace({'####', init_connect, {vsn, Vsn}, {node,Node},{initmsg,InitMsg}}),
@@ -782,6 +794,11 @@ handle_cast(Request, S) ->
"handle_cast(~p, _)\n", [Request]),
{noreply, S}.
+%%========================================================================
+
+-spec handle_info(term(), state()) ->
+ {'noreply', state()} | {'stop', term(), state()}.
+
handle_info({'EXIT', Locker, _Reason}=Exit, #state{the_locker=Locker}=S) ->
{stop, {locker_died,Exit}, S#state{the_locker=undefined}};
handle_info({'EXIT', Registrar, _}=Exit, #state{the_registrar=Registrar}=S) ->
@@ -1122,12 +1139,17 @@ do_whereis(Name, From) ->
send_again({whereis, Name, From})
end.
+-spec terminate(term(), state()) -> 'ok'.
+
terminate(_Reason, _S) ->
true = ets:delete(global_names),
true = ets:delete(global_names_ext),
true = ets:delete(global_locks),
true = ets:delete(global_pid_names),
- true = ets:delete(global_pid_ids).
+ true = ets:delete(global_pid_ids),
+ ok.
+
+-spec code_change(term(), state(), term()) -> {'ok', state()}.
code_change(_OldVsn, S, _Extra) ->
{ok, S}.
@@ -1955,7 +1977,7 @@ delete_lock(Ref, S0) ->
Locks = pid_locks(Ref),
F = fun({ResourceId, LockRequesterId, PidRefs}, S) ->
{Pid, _RPid, Ref} = lists:keyfind(Ref, 3, PidRefs),
- remove_lock(ResourceId, LockRequesterId, Pid, PidRefs, true,S)
+ remove_lock(ResourceId, LockRequesterId, Pid, PidRefs, true, S)
end,
lists:foldl(F, S0, Locks).
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index a45ba34eae..f92c6f7208 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(group).
@@ -477,15 +477,15 @@ get_line(Chars, Pbs, Drv, Encoding) ->
get_line1(edlin:edit_line(Chars, Cont), Drv, new_stack(get(line_buffer)),
Encoding).
-get_line1({done,Line,Rest,Rs}, Drv, _Ls, _Encoding) ->
+get_line1({done,Line,Rest,Rs}, Drv, Ls, _Encoding) ->
send_drv_reqs(Drv, Rs),
- put(line_buffer, [Line|lists:delete(Line, get(line_buffer))]),
+ save_line_buffer(Line, get_lines(Ls)),
{done,Line,Rest};
get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
when ((Mode =:= none) and (Char =:= $\^P))
or ((Mode =:= meta_left_sq_bracket) and (Char =:= $A)) ->
send_drv_reqs(Drv, Rs),
- case up_stack(Ls0) of
+ case up_stack(save_line(Ls0, edlin:current_line(Cont))) of
{none,_Ls} ->
send_drv(Drv, beep),
get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding);
@@ -498,14 +498,14 @@ get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
Drv,
Ls, Encoding)
end;
-get_line1({undefined,{_A,Mode,Char},_Cs,Cont,Rs}, Drv, Ls0, Encoding)
+get_line1({undefined,{_A,Mode,Char},Cs,Cont,Rs}, Drv, Ls0, Encoding)
when ((Mode =:= none) and (Char =:= $\^N))
or ((Mode =:= meta_left_sq_bracket) and (Char =:= $B)) ->
send_drv_reqs(Drv, Rs),
- case down_stack(Ls0) of
- {none,Ls} ->
- send_drv_reqs(Drv, edlin:erase_line(Cont)),
- get_line1(edlin:start(edlin:prompt(Cont)), Drv, Ls, Encoding);
+ case down_stack(save_line(Ls0, edlin:current_line(Cont))) of
+ {none,_Ls} ->
+ send_drv(Drv, beep),
+ get_line1(edlin:edit_line(Cs, Cont), Drv, Ls0, Encoding);
{Lcs,Ls} ->
send_drv_reqs(Drv, edlin:erase_line(Cont)),
{more_chars,Ncont,Nrs} = edlin:start(edlin:prompt(Cont)),
@@ -627,6 +627,28 @@ down_stack({stack,U,{},[]}) ->
down_stack({stack,U,C,D}) ->
down_stack({stack,[C|U],{},D}).
+save_line({stack, U, {}, []}, Line) ->
+ {stack, U, {}, [Line]};
+save_line({stack, U, _L, D}, Line) ->
+ {stack, U, Line, D}.
+
+get_lines({stack, U, {}, []}) ->
+ U;
+get_lines({stack, U, {}, D}) ->
+ tl(lists:reverse(D, U));
+get_lines({stack, U, L, D}) ->
+ get_lines({stack, U, {}, [L|D]}).
+
+save_line_buffer("\n", Lines) ->
+ save_line_buffer(Lines);
+save_line_buffer(Line, [Line|_Lines]=Lines) ->
+ save_line_buffer(Lines);
+save_line_buffer(Line, Lines) ->
+ save_line_buffer([Line|Lines]).
+
+save_line_buffer(Lines) ->
+ put(line_buffer, Lines).
+
%% This is get_line without line editing (except for backspace) and
%% without echo.
get_password_line(Chars, Drv) ->
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index eb503235d8..327e0f93f1 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -25,6 +25,7 @@
%% socket
-export([peername/1, sockname/1, port/1, send/2,
setopts/2, getopts/2,
+ getifaddrs/0, getifaddrs/1,
getif/1, getif/0, getiflist/0, getiflist/1,
ifget/3, ifget/2, ifset/3, ifset/2,
getstat/1, getstat/2,
@@ -62,6 +63,8 @@
%% timer interface
-export([start_timer/1, timeout/1, timeout/2, stop_timer/1]).
+-export_type([ip_address/0, socket/0]).
+
%% imports
-import(lists, [append/1, duplicate/2, filter/2, foldl/3]).
@@ -263,6 +266,17 @@ setopts(Socket, Opts) ->
getopts(Socket, Opts) ->
prim_inet:getopts(Socket, Opts).
+-spec getifaddrs(Socket :: socket()) ->
+ {'ok', [string()]} | {'error', posix()}.
+
+getifaddrs(Socket) ->
+ prim_inet:getifaddrs(Socket).
+
+-spec getifaddrs() -> {'ok', [string()]} | {'error', posix()}.
+
+getifaddrs() ->
+ withsocket(fun(S) -> prim_inet:getifaddrs(S) end).
+
-spec getiflist(Socket :: socket()) ->
{'ok', [string()]} | {'error', posix()}.
diff --git a/lib/kernel/src/inet6_sctp.erl b/lib/kernel/src/inet6_sctp.erl
index 5c49c4fec3..5bf3fca647 100644
--- a/lib/kernel/src/inet6_sctp.erl
+++ b/lib/kernel/src/inet6_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,7 @@
-define(FAMILY, inet6).
-export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,recv/2]).
+-export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
@@ -71,5 +71,24 @@ connect(S, Addr, Port, Opts, Timer) ->
sendmsg(S, SRI, Data) ->
prim_inet:sendmsg(S, SRI, Data).
+send(S, AssocId, Stream, Data) ->
+ case prim_inet:getopts(
+ S,
+ [{sctp_default_send_param,#sctp_sndrcvinfo{assoc_id=AssocId}}]) of
+ {ok,
+ [{sctp_default_send_param,
+ #sctp_sndrcvinfo{
+ flags=Flags, context=Context, ppid=PPID, timetolive=TTL}}]} ->
+ prim_inet:sendmsg(
+ S,
+ #sctp_sndrcvinfo{
+ flags=Flags, context=Context, ppid=PPID, timetolive=TTL,
+ assoc_id=AssocId, stream=Stream},
+ Data);
+ _ ->
+ prim_inet:sendmsg(
+ S, #sctp_sndrcvinfo{assoc_id=AssocId, stream=Stream}, Data)
+ end.
+
recv(S, Timeout) ->
prim_inet:recvfrom(S, 0, Timeout).
diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl
index fab00bbb9f..b9c4fa607c 100644
--- a/lib/kernel/src/inet6_tcp_dist.erl
+++ b/lib/kernel/src/inet6_tcp_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -162,8 +162,8 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
inet:getll(S)
end,
f_address = fun get_remote_id/2,
- mf_tick = {?MODULE, tick},
- mf_getstat = {?MODULE,getstat}
+ mf_tick = fun ?MODULE:tick/1,
+ mf_getstat = fun ?MODULE:getstat/1
},
dist_util:handshake_other_started(HSData);
{false,IP} ->
diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl
index 311e6bc9f9..2458876326 100644
--- a/lib/kernel/src/inet_config.erl
+++ b/lib/kernel/src/inet_config.erl
@@ -23,6 +23,8 @@
-import(lists, [foreach/2, member/2, reverse/1]).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([init/0]).
-export([do_load_resolv/2]).
diff --git a/lib/kernel/src/inet_dns.erl b/lib/kernel/src/inet_dns.erl
index 669a361c9d..1289e176c7 100644
--- a/lib/kernel/src/inet_dns.erl
+++ b/lib/kernel/src/inet_dns.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(inet_dns).
@@ -129,27 +129,33 @@ do_decode(<<Id:16,
RA:1,PR:1,_:2,Rcode:4,
QdCount:16,AnCount:16,NsCount:16,ArCount:16,
QdBuf/binary>>=Buffer) ->
- {AnBuf,QdList} = decode_query_section(QdBuf,QdCount,Buffer),
- {NsBuf,AnList} = decode_rr_section(AnBuf,AnCount,Buffer),
- {ArBuf,NsList} = decode_rr_section(NsBuf,NsCount,Buffer),
- {Rest,ArList} = decode_rr_section(ArBuf,ArCount,Buffer),
+ {AnBuf,QdList,QdTC} = decode_query_section(QdBuf,QdCount,Buffer),
+ {NsBuf,AnList,AnTC} = decode_rr_section(AnBuf,AnCount,Buffer),
+ {ArBuf,NsList,NsTC} = decode_rr_section(NsBuf,NsCount,Buffer),
+ {Rest,ArList,ArTC} = decode_rr_section(ArBuf,ArCount,Buffer),
case Rest of
<<>> ->
+ HdrTC = decode_boolean(TC),
DnsHdr =
#dns_header{id=Id,
qr=decode_boolean(QR),
opcode=decode_opcode(Opcode),
aa=decode_boolean(AA),
- tc=decode_boolean(TC),
+ tc=HdrTC,
rd=decode_boolean(RD),
ra=decode_boolean(RA),
pr=decode_boolean(PR),
rcode=Rcode},
- #dns_rec{header=DnsHdr,
- qdlist=QdList,
- anlist=AnList,
- nslist=NsList,
- arlist=ArList};
+ case QdTC or AnTC or NsTC or ArTC of
+ true when not HdrTC ->
+ throw(?DECODE_ERROR);
+ _ ->
+ #dns_rec{header=DnsHdr,
+ qdlist=QdList,
+ anlist=AnList,
+ nslist=NsList,
+ arlist=ArList}
+ end;
_ ->
%% Garbage data after DNS message
throw(?DECODE_ERROR)
@@ -161,8 +167,10 @@ do_decode(_) ->
decode_query_section(Bin, N, Buffer) ->
decode_query_section(Bin, N, Buffer, []).
+decode_query_section(<<>>=Rest, N, _Buffer, Qs) ->
+ {Rest,reverse(Qs),N =/= 0};
decode_query_section(Rest, 0, _Buffer, Qs) ->
- {Rest,reverse(Qs)};
+ {Rest,reverse(Qs),false};
decode_query_section(Bin, N, Buffer, Qs) ->
case decode_name(Bin, Buffer) of
{<<Type:16,Class:16,Rest/binary>>,Name} ->
@@ -179,8 +187,10 @@ decode_query_section(Bin, N, Buffer, Qs) ->
decode_rr_section(Bin, N, Buffer) ->
decode_rr_section(Bin, N, Buffer, []).
+decode_rr_section(<<>>=Rest, N, _Buffer, RRs) ->
+ {Rest,reverse(RRs),N =/= 0};
decode_rr_section(Rest, 0, _Buffer, RRs) ->
- {Rest,reverse(RRs)};
+ {Rest,reverse(RRs),false};
decode_rr_section(Bin, N, Buffer, RRs) ->
case decode_name(Bin, Buffer) of
{<<T:16/unsigned,C:16/unsigned,TTL:4/binary,
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index cf357b7fba..6f1688c6a2 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -82,6 +82,7 @@
-define(INET_REQ_IFGET, 22).
-define(INET_REQ_IFSET, 23).
-define(INET_REQ_SUBSCRIBE, 24).
+-define(INET_REQ_GETIFADDRS, 25).
%% TCP requests
-define(TCP_REQ_ACCEPT, 40).
-define(TCP_REQ_LISTEN, 41).
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index 3bd5fa0958..65edddcb46 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -20,6 +20,8 @@
%% Parser for all kinds of ineternet configuration files
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([hosts/1, hosts/2]).
-export([hosts_vxworks/1]).
-export([protocols/1, protocols/2]).
diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl
index 9b9e078898..de0f23bf24 100644
--- a/lib/kernel/src/inet_res.erl
+++ b/lib/kernel/src/inet_res.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% RFC 1035, 2671, 2782, 2915.
@@ -592,6 +592,7 @@ query_retries(_Q, _NSs, _Timer, Retry, Retry, S) ->
query_retries(Q, NSs, Timer, Retry, I, S0) ->
Num = length(NSs),
if Num =:= 0 ->
+ udp_close(S0),
{error,timeout};
true ->
case query_nss(Q, NSs, Timer, Retry, I, S0, []) of
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index 795bf83807..de74b573bd 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -31,7 +31,7 @@
-define(FAMILY, inet).
-export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,recv/2]).
+-export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
@@ -141,5 +141,24 @@ connect_get_assoc(S, Addr, Port, Active, Timer) ->
sendmsg(S, SRI, Data) ->
prim_inet:sendmsg(S, SRI, Data).
+send(S, AssocId, Stream, Data) ->
+ case prim_inet:getopts(
+ S,
+ [{sctp_default_send_param,#sctp_sndrcvinfo{assoc_id=AssocId}}]) of
+ {ok,
+ [{sctp_default_send_param,
+ #sctp_sndrcvinfo{
+ flags=Flags, context=Context, ppid=PPID, timetolive=TTL}}]} ->
+ prim_inet:sendmsg(
+ S,
+ #sctp_sndrcvinfo{
+ flags=Flags, context=Context, ppid=PPID, timetolive=TTL,
+ assoc_id=AssocId, stream=Stream},
+ Data);
+ _ ->
+ prim_inet:sendmsg(
+ S, #sctp_sndrcvinfo{assoc_id=AssocId, stream=Stream}, Data)
+ end.
+
recv(S, Timeout) ->
prim_inet:recvfrom(S, 0, Timeout).
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index 92ee7b441a..1e07620a3e 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -143,6 +143,13 @@ init(safe) ->
Boot = start_boot_server(),
DiskLog = start_disk_log(),
Pg2 = start_pg2(),
+
+ %% Run the on_load handlers for all modules that have been
+ %% loaded so far. Running them at this point means that
+ %% on_load handlers can safely call kernel processes
+ %% (and in particular call code:priv_dir/1 or code:lib_dir/1).
+ init:run_on_load_handlers(),
+
{ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}.
get_code_args() ->
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 3afaedf274..49a02359b0 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(net_kernel).
@@ -72,7 +72,7 @@
-export([publish_on_node/1, update_publish_nodes/1]).
-%% Internal Exports
+%% Internal Exports
-export([do_spawn/3,
spawn_func/6,
ticker/2,
@@ -94,7 +94,7 @@
connecttime, %% the connection setuptime.
connections, %% table of connections
conn_owners = [], %% List of connection owner pids,
- pend_owners = [], %% List of potential owners
+ pend_owners = [], %% List of potential owners
listen, %% list of #listen
allowed, %% list of allowed nodes in a restricted system
verbose = 0, %% level of verboseness
@@ -232,7 +232,7 @@ do_connect(Node, Type, WaitForBarred) -> %% Type = normal | hidden
%% "connected from other end.~n",[Node]),
true;
{Pid, false} ->
- ?connect_failure(Node,{barred_connection,
+ ?connect_failure(Node,{barred_connection,
ets:lookup(sys_dist, Node)}),
%%io:format("Net Kernel: barred connection (~p) "
%% "- failure.~n",[Node]),
@@ -244,12 +244,12 @@ do_connect(Node, Type, WaitForBarred) -> %% Type = normal | hidden
{ok, never} ->
?connect_failure(Node,{dist_auto_connect,never}),
false;
- % This might happen due to connection close
+ % This might happen due to connection close
% not beeing propagated to user space yet.
- % Save the day by just not connecting...
+ % Save the day by just not connecting...
{ok, once} when Else =/= [],
(hd(Else))#connection.state =:= up ->
- ?connect_failure(Node,{barred_connection,
+ ?connect_failure(Node,{barred_connection,
ets:lookup(sys_dist, Node)}),
false;
_ ->
@@ -276,8 +276,8 @@ passive_connect_monitor(Parent, Node) ->
Parent ! {self(),true}
end
end.
-
-%% If the net_kernel isn't running we ignore all requests to the
+
+%% If the net_kernel isn't running we ignore all requests to the
%% kernel, thus basically accepting them :-)
request(Req) ->
case whereis(net_kernel) of
@@ -302,7 +302,7 @@ start_link([Name, LongOrShortNames]) ->
start_link([Name, LongOrShortNames, 15000]);
start_link([Name, LongOrShortNames, Ticktime]) ->
- case gen_server:start_link({local, net_kernel}, net_kernel,
+ case gen_server:start_link({local, net_kernel}, net_kernel,
{Name, LongOrShortNames, Ticktime}, []) of
{ok, Pid} ->
{ok, Pid};
@@ -313,7 +313,7 @@ start_link([Name, LongOrShortNames, Ticktime]) ->
end.
%% auth:get_cookie should only be able to return an atom
-%% tuple cookies are unknowns
+%% tuple cookies are unknowns
init({Name, LongOrShortNames, TickT}) ->
process_flag(trap_exit,true),
@@ -322,24 +322,19 @@ init({Name, LongOrShortNames, TickT}) ->
process_flag(priority, max),
Ticktime = to_integer(TickT),
Ticker = spawn_link(net_kernel, ticker, [self(), Ticktime]),
- case auth:get_cookie(Node) of
- Cookie when is_atom(Cookie) ->
- {ok, #state{name = Name,
- node = Node,
- type = LongOrShortNames,
- tick = #tick{ticker = Ticker, time = Ticktime},
- connecttime = connecttime(),
- connections =
- ets:new(sys_dist,[named_table,
- protected,
- {keypos, 2}]),
- listen = Listeners,
- allowed = [],
- verbose = 0
- }};
- _ELSE ->
- {stop, {error,{bad_cookie, Node}}}
- end;
+ {ok, #state{name = Name,
+ node = Node,
+ type = LongOrShortNames,
+ tick = #tick{ticker = Ticker, time = Ticktime},
+ connecttime = connecttime(),
+ connections =
+ ets:new(sys_dist,[named_table,
+ protected,
+ {keypos, 2}]),
+ listen = Listeners,
+ allowed = [],
+ verbose = 0
+ }};
Error ->
{stop, Error}
end.
@@ -354,13 +349,13 @@ init({Name, LongOrShortNames, TickT}) ->
%% The response is delayed until the connection is up and
%% running.
%%
-handle_call({connect, _, Node}, _From, State) when Node =:= node() ->
- {reply, true, State};
+handle_call({connect, _, Node}, From, State) when Node =:= node() ->
+ async_reply({reply, true, State}, From);
handle_call({connect, Type, Node}, From, State) ->
verbose({connect, Type, Node}, 1, State),
case ets:lookup(sys_dist, Node) of
[Conn] when Conn#connection.state =:= up ->
- {reply, true, State};
+ async_reply({reply, true, State}, From);
[Conn] when Conn#connection.state =:= pending ->
Waiting = Conn#connection.waiting,
ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}),
@@ -376,74 +371,75 @@ handle_call({connect, Type, Node}, From, State) ->
{noreply,State#state{conn_owners=Owners}};
_ ->
?connect_failure(Node, {setup_call, failed}),
- {reply, false, State}
+ async_reply({reply, false, State}, From)
end
end;
%%
%% Close the connection to Node.
%%
-handle_call({disconnect, Node}, _From, State) when Node =:= node() ->
- {reply, false, State};
-handle_call({disconnect, Node}, _From, State) ->
+handle_call({disconnect, Node}, From, State) when Node =:= node() ->
+ async_reply({reply, false, State}, From);
+handle_call({disconnect, Node}, From, State) ->
verbose({disconnect, Node}, 1, State),
{Reply, State1} = do_disconnect(Node, State),
- {reply, Reply, State1};
+ async_reply({reply, Reply, State1}, From);
-%%
+%%
%% The spawn/4 BIF ends up here.
-%%
+%%
handle_call({spawn,M,F,A,Gleader},{From,Tag},State) when is_pid(From) ->
do_spawn([no_link,{From,Tag},M,F,A,Gleader],[],State);
-%%
+%%
%% The spawn_link/4 BIF ends up here.
-%%
+%%
handle_call({spawn_link,M,F,A,Gleader},{From,Tag},State) when is_pid(From) ->
do_spawn([link,{From,Tag},M,F,A,Gleader],[],State);
-%%
+%%
%% The spawn_opt/5 BIF ends up here.
-%%
+%%
handle_call({spawn_opt,M,F,A,O,L,Gleader},{From,Tag},State) when is_pid(From) ->
do_spawn([L,{From,Tag},M,F,A,Gleader],O,State);
-%%
+%%
%% Only allow certain nodes.
-%%
-handle_call({allow, Nodes}, _From, State) ->
+%%
+handle_call({allow, Nodes}, From, State) ->
case all_atoms(Nodes) of
true ->
Allowed = State#state.allowed,
- {reply,ok,State#state{allowed = Allowed ++ Nodes}};
+ async_reply({reply,ok,State#state{allowed = Allowed ++ Nodes}},
+ From);
false ->
- {reply,error,State}
+ async_reply({reply,error,State}, From)
end;
-%%
+%%
%% authentication, used by auth. Simply works as this:
%% if the message comes through, the other node IS authorized.
-%%
-handle_call({is_auth, _Node}, _From, State) ->
- {reply,yes,State};
+%%
+handle_call({is_auth, _Node}, From, State) ->
+ async_reply({reply,yes,State}, From);
-%%
+%%
%% Not applicable any longer !?
-%%
-handle_call({apply,_Mod,_Fun,_Args}, {From,Tag}, State)
+%%
+handle_call({apply,_Mod,_Fun,_Args}, {From,Tag}, State)
when is_pid(From), node(From) =:= node() ->
- gen_server:reply({From,Tag}, not_implemented),
+ async_gen_server_reply({From,Tag}, not_implemented),
% Port = State#state.port,
% catch apply(Mod,Fun,[Port|Args]),
{noreply,State};
-handle_call(longnames, _From, State) ->
- {reply, get(longnames), State};
+handle_call(longnames, From, State) ->
+ async_reply({reply, get(longnames), State}, From);
-handle_call({update_publish_nodes, Ns}, _From, State) ->
- {reply, ok, State#state{publish_on_nodes = Ns}};
+handle_call({update_publish_nodes, Ns}, From, State) ->
+ async_reply({reply, ok, State#state{publish_on_nodes = Ns}}, From);
-handle_call({publish_on_node, Node}, _From, State) ->
+handle_call({publish_on_node, Node}, From, State) ->
NewState = case State#state.publish_on_nodes of
undefined ->
State#state{publish_on_nodes =
@@ -457,11 +453,12 @@ handle_call({publish_on_node, Node}, _From, State) ->
Nodes ->
lists:member(Node, Nodes)
end,
- {reply, Publish, NewState};
+ async_reply({reply, Publish, NewState}, From);
-handle_call({verbose, Level}, _From, State) ->
- {reply, State#state.verbose, State#state{verbose = Level}};
+handle_call({verbose, Level}, From, State) ->
+ async_reply({reply, State#state.verbose, State#state{verbose = Level}},
+ From);
%%
%% Set new ticktime
@@ -471,16 +468,16 @@ handle_call({verbose, Level}, _From, State) ->
%% #tick_change{} record if the ticker process has been upgraded;
%% otherwise, an integer or an atom.
-handle_call(ticktime, _, #state{tick = #tick{time = T}} = State) ->
- {reply, T, State};
-handle_call(ticktime, _, #state{tick = #tick_change{time = T}} = State) ->
- {reply, {ongoing_change_to, T}, State};
+handle_call(ticktime, From, #state{tick = #tick{time = T}} = State) ->
+ async_reply({reply, T, State}, From);
+handle_call(ticktime, From, #state{tick = #tick_change{time = T}} = State) ->
+ async_reply({reply, {ongoing_change_to, T}, State}, From);
-handle_call({new_ticktime,T,_TP}, _, #state{tick = #tick{time = T}} = State) ->
+handle_call({new_ticktime,T,_TP}, From, #state{tick = #tick{time = T}} = State) ->
?tckr_dbg(no_tick_change),
- {reply, unchanged, State};
+ async_reply({reply, unchanged, State}, From);
-handle_call({new_ticktime,T,TP}, _, #state{tick = #tick{ticker = Tckr,
+handle_call({new_ticktime,T,TP}, From, #state{tick = #tick{ticker = Tckr,
time = OT}} = State) ->
?tckr_dbg(initiating_tick_change),
start_aux_ticker(T, OT, TP),
@@ -493,14 +490,18 @@ handle_call({new_ticktime,T,TP}, _, #state{tick = #tick{ticker = Tckr,
?tckr_dbg(shorter_ticktime),
shorter
end,
- {reply, change_initiated, State#state{tick = #tick_change{ticker = Tckr,
- time = T,
- how = How}}};
+ async_reply({reply, change_initiated,
+ State#state{tick = #tick_change{ticker = Tckr,
+ time = T,
+ how = How}}}, From);
-handle_call({new_ticktime,_,_},
- _,
+handle_call({new_ticktime,_T,_TP},
+ From,
#state{tick = #tick_change{time = T}} = State) ->
- {reply, {ongoing_change_to, T}, State}.
+ async_reply({reply, {ongoing_change_to, T}, State}, From);
+
+handle_call(_Msg, _From, State) ->
+ {noreply, State}.
%% ------------------------------------------------------------
%% handle_cast.
@@ -568,7 +569,7 @@ handle_info({accept,AcceptPid,Socket,Family,Proto}, State) ->
%%
%% A node has successfully been connected.
%%
-handle_info({SetupPid, {nodeup,Node,Address,Type,Immediate}},
+handle_info({SetupPid, {nodeup,Node,Address,Type,Immediate}},
State) ->
case {Immediate, ets:lookup(sys_dist, Node)} of
{true, [Conn]} when Conn#connection.state =:= pending,
@@ -656,7 +657,7 @@ handle_info({From,registered_send,To,Mess},State) ->
send(From,To,Mess),
{noreply,State};
-%% badcookies SHOULD not be sent
+%% badcookies SHOULD not be sent
%% (if someone does erlang:set_cookie(node(),foo) this may be)
handle_info({From,badcookie,_To,_Mess}, State) ->
error_logger:error_msg("~n** Got OLD cookie from ~w~n",
@@ -704,7 +705,7 @@ handle_info(X, State) ->
%% 4. The ticker process.
%% (5. Garbage pid.)
%%
-%% The process type function that handled the process throws
+%% The process type function that handled the process throws
%% the handle_info return value !
%% -----------------------------------------------------------
@@ -994,9 +995,9 @@ ticker(Kernel, Tick) when is_integer(Tick) ->
ticker_loop(Kernel, Tick).
to_integer(T) when is_integer(T) -> T;
-to_integer(T) when is_atom(T) ->
+to_integer(T) when is_atom(T) ->
list_to_integer(atom_to_list(T));
-to_integer(T) when is_list(T) ->
+to_integer(T) when is_list(T) ->
list_to_integer(T).
ticker_loop(Kernel, Tick) ->
@@ -1004,7 +1005,7 @@ ticker_loop(Kernel, Tick) ->
{new_ticktime, NewTick} ->
?tckr_dbg({ticker_changed_time, Tick, NewTick}),
?MODULE:ticker_loop(Kernel, NewTick)
- after Tick ->
+ after Tick ->
Kernel ! tick,
?MODULE:ticker_loop(Kernel, Tick)
end.
@@ -1052,7 +1053,7 @@ send(_From,To,Mess) ->
-ifdef(UNUSED).
safesend(Name,Mess) when is_atom(Name) ->
- case whereis(Name) of
+ case whereis(Name) of
undefined ->
Mess;
P when is_pid(P) ->
@@ -1063,11 +1064,12 @@ safesend(Pid, Mess) -> Pid ! Mess.
-endif.
do_spawn(SpawnFuncArgs, SpawnOpts, State) ->
+ [_,From|_] = SpawnFuncArgs,
case catch spawn_opt(?MODULE, spawn_func, SpawnFuncArgs, SpawnOpts) of
- {'EXIT', {Reason,_}} ->
- {reply, {'EXIT', {Reason,[]}}, State};
- {'EXIT', Reason} ->
- {reply, {'EXIT', {Reason,[]}}, State};
+ {'EXIT', {Reason,_}} ->
+ async_reply({reply, {'EXIT', {Reason,[]}}, State}, From);
+ {'EXIT', Reason} ->
+ async_reply({reply, {'EXIT', {Reason,[]}}, State}, From);
_ ->
{noreply,State}
end.
@@ -1145,7 +1147,7 @@ get_proto_mod(Family,Protocol,[L|Ls]) ->
true ->
get_proto_mod(Family,Protocol,Ls)
end;
-get_proto_mod(_Family, _Protocol, []) ->
+get_proto_mod(_Family, _Protocol, []) ->
error.
%% -------- Initialisation functions ------------------------
@@ -1156,9 +1158,9 @@ init_node(Name, LongOrShortNames) ->
case create_name(Name, LongOrShortNames, 1) of
{ok,Node} ->
case start_protos(list_to_atom(NameWithoutHost),Node) of
- {ok, Ls} ->
+ {ok, Ls} ->
{ok, Node, Ls};
- Error ->
+ Error ->
Error
end;
Error ->
@@ -1167,9 +1169,9 @@ init_node(Name, LongOrShortNames) ->
%% Create the node name
create_name(Name, LongOrShortNames, Try) ->
- put(longnames, case LongOrShortNames of
- shortnames -> false;
- longnames -> true
+ put(longnames, case LongOrShortNames of
+ shortnames -> false;
+ longnames -> true
end),
{Head,Host1} = create_hostpart(Name, LongOrShortNames),
case Host1 of
@@ -1218,7 +1220,7 @@ create_hostpart(Name, LongOrShortNames) ->
{Head,Host1}.
%%
-%%
+%%
%%
protocol_childspecs() ->
case init:get_argument(proto_dist) of
@@ -1228,7 +1230,7 @@ protocol_childspecs() ->
protocol_childspecs(["inet_tcp"])
end.
-protocol_childspecs([]) ->
+protocol_childspecs([]) ->
[];
protocol_childspecs([H|T]) ->
Mod = list_to_atom(H ++ "_dist"),
@@ -1238,15 +1240,15 @@ protocol_childspecs([H|T]) ->
_ ->
protocol_childspecs(T)
end.
-
-
+
+
%%
%% epmd_module() -> module_name of erl_epmd or similar gen_server_module.
%%
epmd_module() ->
case init:get_argument(epmd_module) of
- {ok,[[Module]]} ->
+ {ok,[[Module]]} ->
Module;
_ ->
erl_epmd
@@ -1293,7 +1295,7 @@ start_protos(Name, [Proto | Ps], Node, Ls) ->
error_logger:info_msg("Protocol: ~p: not supported~n", [Proto]),
start_protos(Name,Ps, Node, Ls);
{'EXIT', Reason} ->
- error_logger:info_msg("Protocol: ~p: register error: ~p~n",
+ error_logger:info_msg("Protocol: ~p: register error: ~p~n",
[Proto, Reason]),
start_protos(Name,Ps, Node, Ls);
{error, duplicate_name} ->
@@ -1303,7 +1305,7 @@ start_protos(Name, [Proto | Ps], Node, Ls) ->
[Proto]),
start_protos(Name,Ps, Node, Ls);
{error, Reason} ->
- error_logger:info_msg("Protocol: ~p: register/listen error: ~p~n",
+ error_logger:info_msg("Protocol: ~p: register/listen error: ~p~n",
[Proto, Reason]),
start_protos(Name,Ps, Node, Ls)
end;
@@ -1409,7 +1411,7 @@ reply_waiting(_Node, Waiting, Rep) ->
reply_waiting1(lists:reverse(Waiting), Rep).
reply_waiting1([From|W], Rep) ->
- gen_server:reply(From, Rep),
+ async_gen_server_reply(From, Rep),
reply_waiting1(W, Rep);
reply_waiting1([], _) ->
ok.
@@ -1455,7 +1457,7 @@ display_info({Node, Info}, {I,O}) ->
integer_to_list(In), integer_to_list(Out), Address),
{I+In,O+Out}.
-fmt_address(undefined) ->
+fmt_address(undefined) ->
"-";
fmt_address(A) ->
case A#net_address.family of
@@ -1511,3 +1513,21 @@ verbose(_, _, _) ->
getnode(P) when is_pid(P) -> node(P);
getnode(P) -> P.
+
+async_reply({reply, Msg, State}, From) ->
+ async_gen_server_reply(From, Msg),
+ {noreply, State}.
+
+async_gen_server_reply(From, Msg) ->
+ {Pid, Tag} = From,
+ M = {Tag, Msg},
+ case catch erlang:send(Pid, M, [nosuspend, noconnect]) of
+ ok ->
+ ok;
+ nosuspend ->
+ spawn(fun() -> catch erlang:send(Pid, M, [noconnect]) end);
+ noconnect ->
+ ok; % The gen module takes care of this case.
+ {'EXIT', _}=EXIT ->
+ EXIT
+ end.
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index d0b498edc9..d1feae771d 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,7 +50,7 @@ find_executable(Name, Path) ->
relative ->
find_executable1(Name, split_path(Path), Extensions);
_ ->
- case verify_executable(Name, Extensions) of
+ case verify_executable(Name, Extensions, Extensions) of
{ok, Complete} ->
Complete;
error ->
@@ -60,7 +60,7 @@ find_executable(Name, Path) ->
find_executable1(Name, [Base|Rest], Extensions) ->
Complete0 = filename:join(Base, Name),
- case verify_executable(Complete0, Extensions) of
+ case verify_executable(Complete0, Extensions, Extensions) of
{ok, Complete} ->
Complete;
error ->
@@ -69,7 +69,7 @@ find_executable1(Name, [Base|Rest], Extensions) ->
find_executable1(_Name, [], _Extensions) ->
false.
-verify_executable(Name0, [Ext|Rest]) ->
+verify_executable(Name0, [Ext|Rest], OrigExtensions) ->
Name1 = Name0 ++ Ext,
case os:type() of
vxworks ->
@@ -78,21 +78,40 @@ verify_executable(Name0, [Ext|Rest]) ->
{ok, _} ->
{ok, Name1};
_ ->
- verify_executable(Name0, Rest)
+ verify_executable(Name0, Rest, OrigExtensions)
end;
_ ->
case file:read_file_info(Name1) of
- {ok, #file_info{mode=Mode}} when Mode band 8#111 =/= 0 ->
- %% XXX This test for execution permission is not full-proof
+ {ok, #file_info{type=regular,mode=Mode}}
+ when Mode band 8#111 =/= 0 ->
+ %% XXX This test for execution permission is not fool-proof
%% on Unix, since we test if any execution bit is set.
{ok, Name1};
_ ->
- verify_executable(Name0, Rest)
+ verify_executable(Name0, Rest, OrigExtensions)
end
end;
-verify_executable(_, []) ->
+verify_executable(Name, [], OrigExtensions) when OrigExtensions =/= [""] -> %% Windows
+ %% Will only happen on windows, hence case insensitivity
+ case can_be_full_name(string:to_lower(Name),OrigExtensions) of
+ true ->
+ verify_executable(Name,[""],[""]);
+ _ ->
+ error
+ end;
+verify_executable(_, [], _) ->
error.
+can_be_full_name(_Name,[]) ->
+ false;
+can_be_full_name(Name,[H|T]) ->
+ case lists:suffix(H,Name) of %% Name is in lowercase, cause this is a windows thing
+ true ->
+ true;
+ _ ->
+ can_be_full_name(Name,T)
+ end.
+
split_path(Path) ->
case type() of
{win32, _} ->
@@ -118,7 +137,8 @@ reverse_element([$"|T]) -> %"
reverse_element(List) ->
lists:reverse(List).
--spec extensions() -> [string()].
+-spec extensions() -> [string(),...].
+%% Extensions in lower case
extensions() ->
case type() of
{win32, _} -> [".exe",".com",".cmd",".bat"];
@@ -211,9 +231,13 @@ start_port_srv(Request) ->
catch
error:_ -> false
end,
- start_port_srv_loop(Request, StayAlive).
+ start_port_srv_handle(Request),
+ case StayAlive of
+ true -> start_port_srv_loop();
+ false -> exiting
+ end.
-start_port_srv_loop({Ref,Client}, StayAlive) ->
+start_port_srv_handle({Ref,Client}) ->
Reply = try open_port({spawn, ?SHELL},[stream]) of
Port when is_port(Port) ->
(catch port_connect(Port, Client)),
@@ -223,20 +247,18 @@ start_port_srv_loop({Ref,Client}, StayAlive) ->
error:Reason ->
{Reason,erlang:get_stacktrace()}
end,
- Client ! {Ref,Reply},
- case StayAlive of
- true -> start_port_srv_loop(get_open_port_request(), true);
- false -> exiting
- end.
+ Client ! {Ref,Reply}.
+
-get_open_port_request() ->
+start_port_srv_loop() ->
receive
{Ref, Client} = Request when is_reference(Ref),
is_pid(Client) ->
- Request;
+ start_port_srv_handle(Request);
_Junk ->
- get_open_port_request()
- end.
+ ignore
+ end,
+ start_port_srv_loop().
%%
%% unix_get_data(Port) -> Result
diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl
index cb9fec2ffe..956a900adc 100644
--- a/lib/kernel/src/pg2.erl
+++ b/lib/kernel/src/pg2.erl
@@ -251,7 +251,9 @@ terminate(_Reason, _S) ->
%%% Pid is a member of group Name.
store(List) ->
- _ = [assure_group(Name) andalso [join_group(Name, P) || P <- Members] ||
+ _ = [(assure_group(Name)
+ andalso
+ [join_group(Name, P) || P <- Members -- group_members(Name)]) ||
[Name, Members] <- List],
ok.
diff --git a/lib/kernel/src/ram_file.erl b/lib/kernel/src/ram_file.erl
index d996650948..48ea871433 100644
--- a/lib/kernel/src/ram_file.erl
+++ b/lib/kernel/src/ram_file.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(ram_file).
@@ -24,11 +24,11 @@
-export([open/2, close/1]).
-export([write/2, read/2, copy/3,
pread/2, pread/3, pwrite/2, pwrite/3,
- position/2, truncate/1, sync/1]).
+ position/2, truncate/1, datasync/1, sync/1]).
%% Specialized file operations
-export([get_size/1, get_file/1, set_file/2, get_file_close/1]).
--export([compress/1, uncompress/1, uuencode/1, uudecode/1]).
+-export([compress/1, uncompress/1, uuencode/1, uudecode/1, advise/4]).
-export([open_mode/1]). %% used by ftp-file
@@ -60,6 +60,7 @@
-define(RAM_FILE_TRUNCATE, 14).
-define(RAM_FILE_PREAD, 17).
-define(RAM_FILE_PWRITE, 18).
+-define(RAM_FILE_FDATASYNC, 19).
%% Other operations
-define(RAM_FILE_GET, 30).
@@ -70,6 +71,7 @@
-define(RAM_FILE_UUENCODE, 35).
-define(RAM_FILE_UUDECODE, 36).
-define(RAM_FILE_SIZE, 37).
+-define(RAM_FILE_ADVISE, 38).
%% Open modes for RAM_FILE_OPEN
-define(RAM_FILE_MODE_READ, 1).
@@ -90,6 +92,14 @@
-define(RAM_FILE_RESP_NUMBER, 3).
-define(RAM_FILE_RESP_INFO, 4).
+%% POSIX file advises
+-define(POSIX_FADV_NORMAL, 0).
+-define(POSIX_FADV_RANDOM, 1).
+-define(POSIX_FADV_SEQUENTIAL, 2).
+-define(POSIX_FADV_WILLNEED, 3).
+-define(POSIX_FADV_DONTNEED, 4).
+-define(POSIX_FADV_NOREUSE, 5).
+
%% --------------------------------------------------------------------------
%% Generic file contents operations.
%%
@@ -167,6 +177,8 @@ copy(#file_descriptor{module = ?MODULE} = Source,
%% XXX Should be moved down to the driver for optimization.
file:copy_opened(Source, Dest, Length).
+datasync(#file_descriptor{module = ?MODULE, data = Port}) ->
+ call_port(Port, <<?RAM_FILE_FDATASYNC>>).
sync(#file_descriptor{module = ?MODULE, data = Port}) ->
call_port(Port, <<?RAM_FILE_FSYNC>>).
@@ -349,6 +361,28 @@ uudecode(#file_descriptor{module = ?MODULE, data = Port}) ->
uudecode(#file_descriptor{}) ->
{error, enotsup}.
+advise(#file_descriptor{module = ?MODULE, data = Port}, Offset,
+ Length, Advise) ->
+ Cmd0 = <<?RAM_FILE_ADVISE, Offset:64/signed, Length:64/signed>>,
+ case Advise of
+ normal ->
+ call_port(Port, <<Cmd0/binary, ?POSIX_FADV_NORMAL:32/signed>>);
+ random ->
+ call_port(Port, <<Cmd0/binary, ?POSIX_FADV_RANDOM:32/signed>>);
+ sequential ->
+ call_port(Port, <<Cmd0/binary, ?POSIX_FADV_SEQUENTIAL:32/signed>>);
+ will_need ->
+ call_port(Port, <<Cmd0/binary, ?POSIX_FADV_WILLNEED:32/signed>>);
+ dont_need ->
+ call_port(Port, <<Cmd0/binary, ?POSIX_FADV_DONTNEED:32/signed>>);
+ no_reuse ->
+ call_port(Port, <<Cmd0/binary, ?POSIX_FADV_NOREUSE:32/signed>>);
+ _ ->
+ {error, einval}
+ end;
+advise(#file_descriptor{}, _Offset, _Length, _Advise) ->
+ {error, enotsup}.
+
%%%-----------------------------------------------------------------
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index 293c368e2a..95517ffd6a 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -51,6 +51,7 @@ MODULES= \
error_logger_SUITE \
error_logger_warn_SUITE \
file_SUITE \
+ file_name_SUITE \
prim_file_SUITE \
ram_file_SUITE \
gen_tcp_api_SUITE \
@@ -141,9 +142,9 @@ release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(APP_FILES) $(RELSYSDIR)
- $(INSTALL_DATA) kernel.dynspec $(EMAKEFILE)\
+ $(INSTALL_DATA) kernel.spec $(EMAKEFILE)\
$(COVERFILE) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/kernel/test/appinc1.erl b/lib/kernel/test/appinc1.erl
index 8456b0eac2..343fefb25c 100644
--- a/lib/kernel/test/appinc1.erl
+++ b/lib/kernel/test/appinc1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/appinc1x.erl b/lib/kernel/test/appinc1x.erl
index 2e177727f2..8c144676ac 100644
--- a/lib/kernel/test/appinc1x.erl
+++ b/lib/kernel/test/appinc1x.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/appinc2.erl b/lib/kernel/test/appinc2.erl
index e41d58bb71..d2e0305109 100644
--- a/lib/kernel/test/appinc2.erl
+++ b/lib/kernel/test/appinc2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/appinc2A.erl b/lib/kernel/test/appinc2A.erl
index b51a1f5035..604e31e3d3 100644
--- a/lib/kernel/test/appinc2A.erl
+++ b/lib/kernel/test/appinc2A.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/appinc2B.erl b/lib/kernel/test/appinc2B.erl
index cafb061ae3..abb60010aa 100644
--- a/lib/kernel/test/appinc2B.erl
+++ b/lib/kernel/test/appinc2B.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/appinc2top.erl b/lib/kernel/test/appinc2top.erl
index 5bd19a59e7..5a8d0d6687 100644
--- a/lib/kernel/test/appinc2top.erl
+++ b/lib/kernel/test/appinc2top.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 313b50f976..4ae4151004 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,11 @@
%%
-module(application_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1, failover/1, failover_comp/1, permissions/1, load/1, reported_bugs/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ failover/1, failover_comp/1, permissions/1, load/1,
load_use_cache/1,
otp_1586/1, otp_2078/1, otp_2012/1, otp_2718/1, otp_2973/1,
otp_3002/1, otp_3184/1, otp_4066/1, otp_4227/1, otp_5363/1,
@@ -30,23 +32,46 @@
nodedown_start/1, init2973/0, loop2973/0, loop5606/1]).
-export([config_change/1,
- distr_changed/1, distr_changed_tc1/1, distr_changed_tc2/1,
+ distr_changed_tc1/1, distr_changed_tc2/1,
shutdown_func/1, do_shutdown/1]).
-define(TESTCASE, testcase_name).
-define(testcase, ?config(?TESTCASE, Config)).
--export([init_per_testcase/2, fin_per_testcase/2, start_type/0,
+-export([init_per_testcase/2, end_per_testcase/2, start_type/0,
start_phase/0, conf_change/0]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(2)).
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[failover, failover_comp, permissions, load,
- load_use_cache, reported_bugs,
- start_phases, script_start, nodedown_start,
- permit_false_start_local, permit_false_start_dist,
- get_key, distr_changed, config_change, shutdown_func].
+ load_use_cache, {group, reported_bugs}, start_phases,
+ script_start, nodedown_start, permit_false_start_local,
+ permit_false_start_dist, get_key,
+ {group, distr_changed}, config_change, shutdown_func].
+
+groups() ->
+ [{reported_bugs, [],
+ [otp_1586, otp_2078, otp_2012, otp_2718, otp_2973,
+ otp_3002, otp_3184, otp_4066, otp_4227, otp_5363,
+ otp_5606]},
+ {distr_changed, [],
+ [distr_changed_tc1, distr_changed_tc2]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(otp_2973=Case, Config) ->
@@ -57,12 +82,12 @@ init_per_testcase(Case, Config) ->
?line Dog = test_server:timetrap(?default_timeout),
[{?TESTCASE, Case}, {watchdog, Dog}|Config].
-fin_per_testcase(otp_2973, Config) ->
+end_per_testcase(otp_2973, Config) ->
code:del_path(?config(data_dir,Config)),
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok;
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -932,9 +957,6 @@ nodedown_start(Conf) when is_list(Conf) ->
%%%-----------------------------------------------------------------
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
-reported_bugs(suite) -> [otp_1586, otp_2078, otp_2012, otp_2718,
- otp_2973, otp_3002, otp_3184, otp_4066,
- otp_4227, otp_5363, otp_5606].
%%-----------------------------------------------------------------
%% Ticket: OTP-1586
@@ -1589,7 +1611,6 @@ get_key(Conf) when is_list(Conf) ->
%%%-----------------------------------------------------------------
%%% Testing of change of distributed parameter.
%%%-----------------------------------------------------------------
-distr_changed(suite) -> [distr_changed_tc1, distr_changed_tc2].
distr_changed_tc1(suite) -> [];
distr_changed_tc1(doc) -> ["Test change of distributed parameter."];
diff --git a/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl
index ae2a3a08ff..6276270d20 100644
--- a/lib/kernel/test/bif_SUITE.erl
+++ b/lib/kernel/test/bif_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,15 +17,16 @@
%% %CopyrightEnd%
%%
-module(bif_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([spawn_tests/1,
+-export([
spawn1/1, spawn2/1, spawn3/1, spawn4/1,
- spawn_link_tests/1,
+
spawn_link1/1, spawn_link2/1, spawn_link3/1, spawn_link4/1,
- spawn_opt_tests/1,
+
spawn_opt2/1, spawn_opt3/1, spawn_opt4/1, spawn_opt5/1,
spawn_failures/1,
@@ -33,9 +34,9 @@
run_fun/1,
wilderness/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -43,25 +44,36 @@
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [spawn_tests, spawn_link_tests, spawn_opt_tests, spawn_failures, wilderness].
+suite() -> [{ct_hooks,[ts_install_cth]}].
-spawn_tests(doc) -> ["Test spawn"];
-spawn_tests(suite) ->
- [spawn1, spawn2, spawn3, spawn4].
+all() ->
+ [{group, spawn_tests}, {group, spawn_link_tests},
+ {group, spawn_opt_tests}, spawn_failures, wilderness].
-spawn_link_tests(doc) -> ["Test spawn_link"];
-spawn_link_tests(suite) ->
- [spawn_link1, spawn_link2, spawn_link3, spawn_link4].
+groups() ->
+ [{spawn_tests, [], [spawn1, spawn2, spawn3, spawn4]},
+ {spawn_link_tests, [],
+ [spawn_link1, spawn_link2, spawn_link3, spawn_link4]},
+ {spawn_opt_tests, [],
+ [spawn_opt2, spawn_opt3, spawn_opt4, spawn_opt5]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-spawn_opt_tests(doc) -> ["Test spawn_opt"];
-spawn_opt_tests(suite) ->
- [spawn_opt2, spawn_opt3, spawn_opt4, spawn_opt5].
spawn1(doc) -> ["Test spawn/1"];
spawn1(suite) ->
diff --git a/lib/kernel/test/ch.erl b/lib/kernel/test/ch.erl
index 25d1b4354c..25d6f6d200 100644
--- a/lib/kernel/test/ch.erl
+++ b/lib/kernel/test/ch.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/ch_sup.erl b/lib/kernel/test/ch_sup.erl
index 9d03628839..4c923b2909 100644
--- a/lib/kernel/test/ch_sup.erl
+++ b/lib/kernel/test/ch_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/cleanup.erl b/lib/kernel/test/cleanup.erl
index 831ceba8f5..01db1e9124 100644
--- a/lib/kernel/test/cleanup.erl
+++ b/lib/kernel/test/cleanup.erl
@@ -18,11 +18,22 @@
%%
-module(cleanup).
--export([all/1, cleanup/1]).
+-export([all/0,groups/0,init_per_group/2,end_per_group/2, cleanup/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+all() ->
+ [cleanup].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> {req, [kernel], [cleanup]}.
cleanup(suite) -> [];
cleanup(_) ->
diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl
index 08b6477c9d..3ad49254f1 100644
--- a/lib/kernel/test/code_SUITE.erl
+++ b/lib/kernel/test/code_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,9 @@
%%
-module(code_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([set_path/1, get_path/1, add_path/1, add_paths/1, del_path/1,
replace_path/1, load_file/1, load_abs/1, ensure_loaded/1,
delete/1, purge/1, soft_purge/1, is_loaded/1, all_loaded/1,
@@ -31,9 +31,10 @@
where_is_file_cached/1, where_is_file_no_cache/1,
purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1,
code_archive/1, code_archive2/1, on_load/1,
- on_load_embedded/1, on_load_errors/1]).
+ big_boot_embedded/1,
+ on_load_embedded/1, on_load_errors/1, native_early_modules/1]).
--export([init_per_testcase/2, fin_per_testcase/2,
+-export([init_per_testcase/2, end_per_testcase/2,
init_per_suite/1, end_per_suite/1,
sticky_compiler/1]).
@@ -42,18 +43,29 @@
handle_event/2, handle_call/2, handle_info/2,
terminate/2]).
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[set_path, get_path, add_path, add_paths, del_path,
replace_path, load_file, load_abs, ensure_loaded,
delete, purge, soft_purge, is_loaded, all_loaded,
load_binary, dir_req, object_code, set_path_file,
- pa_pz_option, add_del_path,
- dir_disappeared, ext_mod_dep, clash,
- load_cached, start_node_with_cache, add_and_rehash,
- where_is_file_no_cache, where_is_file_cached,
- purge_stacktrace, mult_lib_roots, bad_erl_libs,
- code_archive, code_archive2, on_load, on_load_embedded,
- on_load_errors].
+ pa_pz_option, add_del_path, dir_disappeared,
+ ext_mod_dep, clash, load_cached, start_node_with_cache,
+ add_and_rehash, where_is_file_no_cache,
+ where_is_file_cached, purge_stacktrace, mult_lib_roots,
+ bad_erl_libs, code_archive, code_archive2, on_load,
+ on_load_embedded, big_boot_embedded, on_load_errors,
+ native_early_modules].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
init_per_suite(Config) ->
%% The compiler will no longer create a Beam file if
@@ -74,7 +86,18 @@ init_per_testcase(_Func, Config) ->
P=code:get_path(),
P=code:get_path(),
[{watchdog, Dog}, {code_path, P}|Config].
-fin_per_testcase(_Func, Config) ->
+
+end_per_testcase(TC, Config) when TC == mult_lib_roots;
+ TC == big_boot_embedded ->
+ {ok, HostName} = inet:gethostname(),
+ NodeName = list_to_atom(atom_to_list(TC)++"@"++HostName),
+ ?t:stop_node(NodeName),
+ end_per_testcase(Config);
+end_per_testcase(_Func, Config) ->
+ end_per_testcase(Config).
+
+end_per_testcase(Config) ->
+ code:purge(code_b_test),
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
P=?config(code_path, Config),
@@ -584,13 +607,21 @@ clash(Config) when is_list(Config) ->
TmpEzFile = Priv++"foobar-0.tmp.ez",
?line {ok, _} = file:copy(DDir++"foobar-0.1.ez", TmpEzFile),
?line true = code:add_path(TmpEzFile++"/foobar-0.1/ebin"),
- ?line ok = file:delete(TmpEzFile),
+ case os:type() of
+ {win32,_} ->
+ %% The file wont be deleted on windows until it's closed, why we
+ %% need to rename instead.
+ ?line ok = file:rename(TmpEzFile,TmpEzFile++".moved");
+ _ ->
+ ?line ok = file:delete(TmpEzFile)
+ end,
test_server:capture_start(),
?line ok = code:clash(),
test_server:capture_stop(),
?line [BadPathMsg|_] = test_server:capture_get(),
?line true = lists:prefix("** Bad path can't read", BadPathMsg),
?line true = code:set_path(P),
+ file:delete(TmpEzFile++".moved"), %% Only effect on windows
ok.
ext_mod_dep(suite) ->
@@ -635,7 +666,7 @@ analyse([], [This={M,F,A}|Path], Visited, ErrCnt0) ->
%% These modules should be loaded by code.erl before
%% the code_server is started.
OK = [erlang, os, prim_file, erl_prim_loader, init, ets,
- code_server, lists, lists_sort, filename, packages,
+ code_server, lists, lists_sort, unicode, binary, filename, packages,
gb_sets, gb_trees, hipe_unified_loader, hipe_bifs,
prim_zip, zlib],
ErrCnt1 =
@@ -664,6 +695,22 @@ analyse2(MFA={_,_,_}, Path, Visited0) ->
%%%% We need to check these manually...
% fun's are ok as long as they are defined locally.
check_funs({'$M_EXPR','$F_EXPR',_},
+ [{unicode,characters_to_binary_int,3},
+ {unicode,characters_to_binary,3},
+ {filename,filename_string_to_binary,1}|_]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',_},
+ [{unicode,ml_map,3},
+ {unicode,characters_to_binary_int,3},
+ {unicode,characters_to_binary,3},
+ {filename,filename_string_to_binary,1}|_]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',_},
+ [{unicode,do_o_binary2,2},
+ {unicode,do_o_binary,2},
+ {unicode,o_trans,1},
+ {unicode,characters_to_binary_int,3},
+ {unicode,characters_to_binary,3},
+ {filename,filename_string_to_binary,1}|_]) -> 0;
+check_funs({'$M_EXPR','$F_EXPR',_},
[{code_server,load_native_code,4},
{code_server,load_native_code_1,2},
{code_server,load_native_code,2},
@@ -880,6 +927,8 @@ add_and_rehash(Config) when is_list(Config) ->
?line true = rpc:call(Node, code, add_path, [OkDir]),
?line {error,_} = rpc:call(Node, code, add_path, [BadDir]),
?line ok = rpc:call(Node, code, rehash, []),
+
+ ?t:stop_node(Node),
ok.
where_is_file_no_cache(suite) ->
@@ -981,9 +1030,9 @@ mult_lib_roots(Config) when is_list(Config) ->
?t:start_node(mult_lib_roots, slave,
[{args,"-env ERL_LIBS "++ErlLibs}]),
- ?line {ok,Cwd} = file:get_cwd(),
+ ?line TSPath = filename:dirname(code:which(test_server)),
?line Path0 = rpc:call(Node, code, get_path, []),
- ?line [Cwd,"."|Path1] = Path0,
+ ?line [TSPath,"."|Path1] = Path0,
?line [Kernel|Path2] = Path1,
?line [Stdlib|Path3] = Path2,
?line mult_lib_verify_lib(Kernel, "kernel"),
@@ -1002,7 +1051,6 @@ mult_lib_roots(Config) when is_list(Config) ->
?line true = rpc:call(Node, code_SUITE_mult_root_module, works_fine, []),
- ?line ?t:stop_node(Node),
ok.
mult_lib_compile(Root, Last) ->
@@ -1145,6 +1193,22 @@ compile_files([File | Files], SrcDir, OutDir) ->
compile_files([], _, _) ->
ok.
+big_boot_embedded(suite) ->
+ [];
+big_boot_embedded(doc) ->
+ ["Test that a boot file with (almost) all of OTP can be used to start an"
+ " embeddedd system."];
+big_boot_embedded(Config) when is_list(Config) ->
+ ?line {BootArg,AppsInBoot} = create_big_boot(Config),
+ ?line {ok, Node} =
+ ?t:start_node(big_boot_embedded, slave,
+ [{args,"-boot "++BootArg++" -mode embedded"}]),
+ ?line RemoteNodeApps =
+ [ {X,Y} || {X,_,Y} <-
+ rpc:call(Node,application,loaded_applications,[]) ],
+ ?line true = lists:sort(AppsInBoot) =:= lists:sort(RemoteNodeApps),
+ ok.
+
on_load(Config) when is_list(Config) ->
Master = on_load_test_case_process,
@@ -1226,7 +1290,8 @@ on_load_embedded_1(Config) ->
?line LibRoot = code:lib_dir(),
?line LinkName = filename:join(LibRoot, "on_load_app-1.0"),
?line OnLoadApp = filename:join(DataDir, "on_load_app-1.0"),
- ?line file:delete(LinkName),
+ ?line del_link(LinkName),
+ io:format("LinkName :~p, OnLoadApp: ~p~n",[LinkName,OnLoadApp]),
case file:make_symlink(OnLoadApp, LinkName) of
{error,enotsup} ->
throw({skip,"Support for symlinks required"});
@@ -1255,7 +1320,15 @@ on_load_embedded_1(Config) ->
%% Clean up.
?line stop_node(Node),
- ?line ok = file:delete(LinkName).
+ ?line ok = del_link(LinkName).
+
+del_link(LinkName) ->
+ case file:delete(LinkName) of
+ {error,eperm} ->
+ file:del_dir(LinkName);
+ Other ->
+ Other
+ end.
create_boot(Config, Options) ->
?line {ok, OldDir} = file:get_cwd(),
@@ -1281,6 +1354,73 @@ create_script(Config) ->
?line file:close(Fd),
{filename:dirname(Name),filename:basename(Name)}.
+create_big_boot(Config) ->
+ ?line {ok, OldDir} = file:get_cwd(),
+ ?line {Options,Local} = case is_source_dir() of
+ true -> {[no_module_tests,local],true};
+ _ -> {[no_module_tests],false}
+ end,
+ ?line {LatestDir,LatestName,Apps} = create_big_script(Config,Local),
+ ?line ok = file:set_cwd(LatestDir),
+ ?line ok = systools:make_script(LatestName, Options),
+ ?line ok = file:set_cwd(OldDir),
+ {filename:join(LatestDir, LatestName),Apps}.
+
+% The following apps cannot be loaded
+% hipe .app references (or can reference) files that have no
+% corresponding beam file (if hipe is not enabled)
+filter_app("hipe",_) ->
+ false;
+% Dialyzer and typer depends on hipe
+filter_app("dialyzer",_) ->
+ false;
+filter_app("typer",_) ->
+ false;
+% Orber requires explicit configuration
+filter_app("orber",_) ->
+ false;
+% cos* depends on orber
+filter_app("cos"++_,_) ->
+ false;
+% ic has a mod instruction in the app file but no corresponding start function
+filter_app("ic",_) ->
+ false;
+% Netconf has some dependency that I really do not understand (maybe like orber)
+filter_app("netconf",_) ->
+ false;
+% Safe has the same kind of error in the .app file as ic
+filter_app("safe",_) ->
+ false;
+% OS_mon does not find it's port program when running cerl
+filter_app("os_mon",true) ->
+ false;
+% Other apps should be OK.
+filter_app(_,_) ->
+ true.
+create_big_script(Config,Local) ->
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Name = filename:join(PrivDir,"full_script_test"),
+ ?line InitialApplications=application:loaded_applications(),
+ %% Applications left loaded by the application suite, unload them!
+ ?line UnloadFix=[app0,app1,app2,group_leader,app_start_error],
+ ?line [application:unload(Leftover) ||
+ Leftover <- UnloadFix,
+ lists:keymember(Leftover,1,InitialApplications) ],
+ %% Now we should have only "real" applications...
+ ?line [application:load(list_to_atom(Y)) || {match,[Y]} <- [ re:run(X,code:lib_dir()++"/"++"([^/-]*).*/ebin",[{capture,[1],list}]) || X <- code:get_path()],filter_app(Y,Local)],
+ ?line Apps = [ {N,V} || {N,_,V} <- application:loaded_applications()],
+ ?line {ok,Fd} = file:open(Name ++ ".rel", write),
+ ?line io:format(Fd,
+ "{release, {\"Test release 3\", \"P2A\"}, \n"
+ " {erts, \"9.42\"}, \n"
+ " ~p}.\n",
+ [Apps]),
+ ?line file:close(Fd),
+ ?line NewlyLoaded =
+ application:loaded_applications() -- InitialApplications,
+ ?line [ application:unload(N) || {N,_,_} <- NewlyLoaded],
+ {filename:dirname(Name),filename:basename(Name),Apps}.
+
is_source_dir() ->
filename:basename(code:lib_dir(kernel)) =:= "kernel" andalso
filename:basename(code:lib_dir(stdlib)) =:= "stdlib".
@@ -1333,6 +1473,34 @@ do_on_load_error(ReturnValue) ->
?line {undef,[{on_load_error,main,[]}|_]} = Exit
end.
+native_early_modules(suite) -> [];
+native_early_modules(doc) -> ["Test that the native code of early loaded modules is loaded"];
+native_early_modules(Config) when is_list(Config) ->
+ case erlang:system_info(hipe_architecture) of
+ undefined ->
+ {skip,"Native code support is not enabled"};
+ Architecture ->
+ native_early_modules_1(Architecture)
+ end.
+
+native_early_modules_1(Architecture) ->
+ ?line {lists, ListsBinary, _ListsFilename} = code:get_object_code(lists),
+ ?line ChunkName = hipe_unified_loader:chunk_name(Architecture),
+ ?line NativeChunk = beam_lib:chunks(ListsBinary, [ChunkName]),
+ ?line IsHipeCompiled = case NativeChunk of
+ {ok,{_,[{_,Bin}]}} when is_binary(Bin) -> true;
+ {error, beam_lib, _} -> false
+ end,
+ case IsHipeCompiled of
+ false ->
+ {skip,"OTP apparently not configured with --enable-native-libs"};
+ true ->
+ ?line true = lists:all(fun code:is_module_native/1,
+ [ets,file,filename,gb_sets,gb_trees,
+ hipe_unified_loader,lists,os,packages]),
+ ok
+ end.
+
%%-----------------------------------------------------------------
%% error_logger handler.
%% (Copied from stdlib/test/proc_lib_SUITE.erl.)
diff --git a/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl b/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl
index a39332f81d..646921026d 100644
--- a/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl
+++ b/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl
@@ -3,6 +3,15 @@
-on_load(run_me/0).
run_me() ->
+ %% An onload handler typically calls code:priv_dir/1
+ %% or code:lib_dir/1, so make sure that it works.
+ LibDir = code:lib_dir(on_load_app),
+ PrivDir = code:priv_dir(on_load_app),
+ LibDir = filename:dirname(PrivDir),
+ ModPath = filename:join(filename:split(code:which(?MODULE))),
+ LibDir = filename:dirname(filename:dirname(ModPath)),
+
+ %% Start a process to remember that the on_load was called.
spawn(fun() ->
register(everything_is_fine, self()),
receive Any ->
diff --git a/lib/kernel/test/code_a_test.erl b/lib/kernel/test/code_a_test.erl
index 745bbf032c..22830fff53 100644
--- a/lib/kernel/test/code_a_test.erl
+++ b/lib/kernel/test/code_a_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/code_b_test.erl b/lib/kernel/test/code_b_test.erl
index 0f0107a2b4..a8ff570e2e 100644
--- a/lib/kernel/test/code_b_test.erl
+++ b/lib/kernel/test/code_b_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl
index 1bfe76f5ea..4ae47b4762 100644
--- a/lib/kernel/test/disk_log_SUITE.erl
+++ b/lib/kernel/test/disk_log_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,46 +28,47 @@
-define(config(X,Y), foo).
-define(t,test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(privdir(Conf), ?config(priv_dir, Conf)).
-define(datadir(Conf), ?config(data_dir, Conf)).
-endif.
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
- halt_int/1, halt_int_inf/1, halt_int_sz/1,
+ halt_int_inf/1,
halt_int_sz_1/1, halt_int_sz_2/1,
- read_mode/1, halt_int_ro/1, halt_ext_ro/1, wrap_int_ro/1,
+ halt_int_ro/1, halt_ext_ro/1, wrap_int_ro/1,
wrap_ext_ro/1, halt_trunc/1, halt_misc/1, halt_ro_alog/1,
halt_ro_balog/1, halt_ro_crash/1,
- wrap_int/1, wrap_int_1/1, wrap_int_2/1, inc_wrap_file/1,
+ wrap_int_1/1, wrap_int_2/1, inc_wrap_file/1,
- halt_ext/1, halt_ext_inf/1,
+ halt_ext_inf/1,
- halt_ext_sz/1, halt_ext_sz_1/1, halt_ext_sz_2/1,
+ halt_ext_sz_1/1, halt_ext_sz_2/1,
- wrap_ext/1, wrap_ext_1/1, wrap_ext_2/1,
+ wrap_ext_1/1, wrap_ext_2/1,
- head/1, head_func/1, plain_head/1, one_header/1,
+ head_func/1, plain_head/1, one_header/1,
- notif/1, wrap_notif/1, full_notif/1, trunc_notif/1, blocked_notif/1,
+ wrap_notif/1, full_notif/1, trunc_notif/1, blocked_notif/1,
new_idx_vsn/1,
reopen/1,
- block/1, block_blocked/1, block_queue/1, block_queue2/1,
+ block_blocked/1, block_queue/1, block_queue2/1,
unblock/1,
- open/1, open_overwrite/1, open_size/1, open_truncate/1, open_error/1,
+ open_overwrite/1, open_size/1, open_truncate/1, open_error/1,
- close/1, close_race/1, close_block/1, close_deadlock/1,
+ close_race/1, close_block/1, close_deadlock/1,
- error/1, error_repair/1, error_log/1, error_index/1,
+ error_repair/1, error_log/1, error_index/1,
chunk/1,
@@ -75,15 +76,15 @@
many_users/1,
- info/1, info_current/1,
+ info_current/1,
- change_size/1, change_size_before/1, change_size_during/1,
+ change_size_before/1, change_size_during/1,
change_size_after/1, default_size/1, change_size2/1,
change_size_truncate/1,
change_attribute/1,
- distribution/1, dist_open/1, dist_error_open/1, dist_notify/1,
+ dist_open/1, dist_error_open/1, dist_notify/1,
dist_terminate/1, dist_accessible/1, dist_deadlock/1,
dist_open2/1, other_groups/1,
@@ -94,7 +95,7 @@
-export([head_fun/1, hf/0, lserv/1,
measure/0, init_m/1, xx/0, head_exit/0, slow_header/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([try_unblock/1]).
@@ -142,8 +143,59 @@
change_size_after, default_size]).
-all(suite) ->
- ?ALL_TESTS.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, halt_int}, {group, wrap_int},
+ {group, halt_ext}, {group, wrap_ext},
+ {group, read_mode}, {group, head}, {group, notif},
+ new_idx_vsn, reopen, {group, block}, unblock,
+ {group, open}, {group, close}, {group, error}, chunk,
+ truncate, many_users, {group, info},
+ {group, change_size}, change_attribute,
+ {group, distribution}, evil, otp_6278].
+
+groups() ->
+ [{halt_int, [], [halt_int_inf, {group, halt_int_sz}]},
+ {halt_int_sz, [], [halt_int_sz_1, halt_int_sz_2]},
+ {read_mode, [],
+ [halt_int_ro, halt_ext_ro, wrap_int_ro, wrap_ext_ro,
+ halt_trunc, halt_misc, halt_ro_alog, halt_ro_balog,
+ halt_ro_crash]},
+ {wrap_int, [], [wrap_int_1, wrap_int_2, inc_wrap_file]},
+ {halt_ext, [], [halt_ext_inf, {group, halt_ext_sz}]},
+ {halt_ext_sz, [], [halt_ext_sz_1, halt_ext_sz_2]},
+ {wrap_ext, [], [wrap_ext_1, wrap_ext_2]},
+ {head, [], [head_func, plain_head, one_header]},
+ {notif, [],
+ [wrap_notif, full_notif, trunc_notif, blocked_notif]},
+ {block, [], [block_blocked, block_queue, block_queue2]},
+ {open, [],
+ [open_overwrite, open_size, open_truncate, open_error]},
+ {close, [], [close_race, close_block, close_deadlock]},
+ {error, [], [error_repair, error_log, error_index]},
+ {info, [], [info_current]},
+ {change_size, [],
+ [change_size_before, change_size_during,
+ change_size_after, default_size, change_size2,
+ change_size_truncate]},
+ {distribution, [],
+ [dist_open, dist_error_open, dist_notify,
+ dist_terminate, dist_accessible, dist_deadlock,
+ dist_open2, other_groups]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(Case, Config) ->
@@ -167,12 +219,11 @@ init_per_testcase(Case, Config) ->
[{watchdog, Dog}|Config]
end.
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-halt_int(suite) -> [halt_int_inf, halt_int_sz].
halt_int_inf(suite) -> [];
halt_int_inf(doc) -> ["Test simple halt disk log, size infinity"];
@@ -187,7 +238,6 @@ halt_int_inf(Conf) when is_list(Conf) ->
?line ok = disk_log:close(a),
?line ok = file:delete(File).
-halt_int_sz(suite) -> [halt_int_sz_1, halt_int_sz_2].
halt_int_sz_1(suite) -> [];
halt_int_sz_1(doc) -> ["Test simple halt disk log, size defined"];
@@ -275,10 +325,6 @@ halt_int_sz_2(Conf) when is_list(Conf) ->
?line ok = file:delete(File3),
ok.
-read_mode(suite) -> [halt_int_ro, halt_ext_ro,
- wrap_int_ro, wrap_ext_ro,
- halt_trunc, halt_misc, halt_ro_alog, halt_ro_balog,
- halt_ro_crash].
halt_int_ro(suite) -> [];
halt_int_ro(doc) -> ["Test simple halt disk log, read only, internal"];
@@ -480,7 +526,6 @@ halt_ro_crash(Conf) when is_list(Conf) ->
-wrap_int(suite) -> [wrap_int_1, wrap_int_2, inc_wrap_file].
wrap_int_1(suite) -> [];
wrap_int_1(doc) -> ["Test wrap disk log, internal"];
@@ -628,7 +673,6 @@ inc_wrap_file(Conf) when is_list(Conf) ->
-halt_ext(suite) -> [halt_ext_inf, halt_ext_sz].
halt_ext_inf(suite) -> [];
halt_ext_inf(doc) -> ["Test halt disk log, external, infinity"];
@@ -642,7 +686,6 @@ halt_ext_inf(Conf) when is_list(Conf) ->
?line ok = disk_log:close(a),
?line ok = file:delete(File).
-halt_ext_sz(suite) -> [halt_ext_sz_1, halt_ext_sz_2].
halt_ext_sz_1(suite) -> [];
halt_ext_sz_1(doc) -> ["Test halt disk log, external, size defined"];
@@ -734,7 +777,6 @@ halt_ext_sz_2(Conf) when is_list(Conf) ->
?line ok = file:delete(File3),
ok.
-wrap_ext(suite) -> [wrap_ext_1, wrap_ext_2].
wrap_ext_1(suite) -> [];
wrap_ext_1(doc) -> ["Test wrap disk log, external, size defined"];
@@ -1147,7 +1189,6 @@ end_times({T1,W1}) ->
{W2, _} = statistics(wall_clock),
{T2-T1, W2-W1}.
-head(suite) -> [head_func, plain_head, one_header].
head_func(suite) -> [];
head_func(doc) -> ["Test head parameter"];
@@ -1327,8 +1368,6 @@ one_header(Conf) when is_list(Conf) ->
ok.
-notif(suite) -> [wrap_notif, full_notif, trunc_notif,
- blocked_notif].
wrap_notif(suite) -> [];
wrap_notif(doc) -> ["Test notify parameter, wrap"];
@@ -1553,7 +1592,6 @@ reopen(Conf) when is_list(Conf) ->
?line Q = qlen(),
ok.
-block(suite) -> [block_blocked, block_queue, block_queue2].
block_blocked(suite) -> [];
block_blocked(doc) ->
@@ -1826,8 +1864,6 @@ try_unblock(Log) ->
?line Error = {error, {not_blocked_by_pid, n}} = disk_log:unblock(Log),
?line "The disk log" ++ _ = format_error(Error).
-open(suite) -> [open_overwrite, open_size,
- open_truncate, open_error].
open_overwrite(suite) -> [];
open_overwrite(doc) ->
@@ -2075,7 +2111,6 @@ open_error(Conf) when is_list(Conf) ->
?line del(File, No).
-close(suite) -> [close_race, close_block, close_deadlock].
close_race(suite) -> [];
close_race(doc) ->
@@ -2497,7 +2532,6 @@ lserv(Log) ->
end,
lserv(Log).
-error(suite) -> [error_repair, error_log, error_index].
error_repair(suite) -> [];
error_repair(doc) ->
@@ -3215,7 +3249,6 @@ del_files(_Size, File) ->
-info(suite) -> [info_current].
info_current(suite) -> [];
info_current(doc) ->
@@ -3420,11 +3453,6 @@ info_current(Conf) when is_list(Conf) ->
ok.
-change_size(suite) -> [change_size_before,
- change_size_during,
- change_size_after,
- default_size, change_size2,
- change_size_truncate].
change_size_before(suite) -> [];
change_size_before(doc) ->
@@ -4094,13 +4122,6 @@ change_attribute(Conf) when is_list(Conf) ->
?line Q = qlen(),
?line del(File, No).
-distribution(suite) -> [dist_open, dist_error_open,
- dist_notify,
- dist_terminate,
- dist_accessible,
- dist_deadlock,
- dist_open2,
- other_groups].
dist_open(suite) -> [];
dist_open(doc) ->
diff --git a/lib/kernel/test/erl_boot_server_SUITE.erl b/lib/kernel/test/erl_boot_server_SUITE.erl
index 241d68fef4..cea3715ce4 100644
--- a/lib/kernel/test/erl_boot_server_SUITE.erl
+++ b/lib/kernel/test/erl_boot_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,9 @@
%%
-module(erl_boot_server_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]).
-export([start/1, start_link/1, stop/1, add/1, delete/1, responses/1]).
@@ -33,9 +33,27 @@
%% Changed for the new erl_boot_server for R3A by Bjorn Gustavsson.
%%-----------------------------------------------------------------
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[start, start_link, stop, add, delete, responses].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
-define(all_ones, {255, 255, 255, 255}).
start(doc) -> "Tests the erl_boot_server:start/1 function.";
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index d15f6aa0d5..9cccdab76b 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,13 +19,14 @@
-module(erl_distribution_SUITE).
%-define(line_trace, 1).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1,
table_waste/1, net_setuptime/1,
- monitor_nodes/1,
+
monitor_nodes_nodedown_reason/1,
monitor_nodes_complex_nodedown_reason/1,
monitor_nodes_node_type/1,
@@ -41,7 +42,7 @@
tick_serv_test/2, tick_serv_test1/1,
keep_conn/1, time_ping/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([start_node/2]).
@@ -57,16 +58,39 @@
%% erl -sname master -rsh ctrsh
%%-----------------------------------------------------------------
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[tick, tick_change, illegal_nodenames, hidden_node,
- table_waste, net_setuptime,
- monitor_nodes].
+ table_waste, net_setuptime, {group, monitor_nodes}].
+
+groups() ->
+ [{monitor_nodes, [],
+ [monitor_nodes_nodedown_reason,
+ monitor_nodes_complex_nodedown_reason,
+ monitor_nodes_node_type, monitor_nodes_misc,
+ monitor_nodes_otp_6481, monitor_nodes_errors,
+ monitor_nodes_combinations, monitor_nodes_cleanup,
+ monitor_nodes_many]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?t:timetrap(?t:minutes(4)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
@@ -273,6 +297,7 @@ tick_change(Config) when is_list(Config) ->
?line PaDir = filename:dirname(code:which(?MODULE)),
?line [BN, CN] = get_nodenames(2, tick_change),
?line DefaultTT = net_kernel:get_net_ticktime(),
+ ?line unchanged = net_kernel:set_net_ticktime(DefaultTT, 60),
?line case DefaultTT of
I when is_integer(I) -> ?line ok;
_ -> ?line ?t:fail(DefaultTT)
@@ -377,6 +402,7 @@ run_tick_change_test(B, C, PrevTT, TT, PaDir) ->
end,
?line change_initiated = net_kernel:set_net_ticktime(TT,20),
+ ?line {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20),
?line sleep(3),
?line change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]),
?line sleep(7),
@@ -528,18 +554,6 @@ check_monitor_nodes_res(Pid, Node) ->
end.
-monitor_nodes(doc) ->
- [];
-monitor_nodes(suite) ->
- [monitor_nodes_nodedown_reason,
- monitor_nodes_complex_nodedown_reason,
- monitor_nodes_node_type,
- monitor_nodes_misc,
- monitor_nodes_otp_6481,
- monitor_nodes_errors,
- monitor_nodes_combinations,
- monitor_nodes_cleanup,
- monitor_nodes_many].
%%
%% Testcase:
@@ -843,13 +857,16 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
?line {ok, Node} = start_node(Name, "", this),
?line receive {nodeup, Node} -> ok end,
- ?line spawn(Node,
+ ?line RemotePid = spawn(Node,
fun () ->
- receive after 1000 -> ok end,
- lists:foreach(fun (No) ->
- Me ! {NodeMsg, No}
- end,
- Seq),
+ receive after 1500 -> ok end,
+ % infinit loop of msgs
+ % we want an endless stream of messages and the kill
+ % the node mercilessly.
+ % We then want to ensure that the nodedown message arrives
+ % last ... without garbage after it.
+ _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end),
+ receive {Me, kill_it} -> ok end,
halt()
end),
@@ -858,9 +875,11 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
%% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message
%% bringing up the connection).
- %%?line no_msgs(500), % Why wait? It fails test sometimes /sverker
+ ?line no_msgs(500),
?line {nodeup, Node} = receive Msg1 -> Msg1 end,
- ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end,
+ ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end,
+ % msg stream has begun, kill the node
+ ?line RemotePid ! {self(), kill_it},
%% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}'
%% message.
@@ -881,6 +900,10 @@ flush_node_msgs(NodeMsg, No) ->
OtherMsg -> OtherMsg
end.
+node_loop_send(Pid, Msg, No) ->
+ Pid ! {Msg, No},
+ node_loop_send(Pid, Msg, No + 1).
+
monitor_nodes_errors(doc) ->
[];
monitor_nodes_errors(suite) ->
diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl
index 627fed1fdd..3b8b2d9150 100644
--- a/lib/kernel/test/erl_distribution_wb_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,13 @@
%%
-module(erl_distribution_wb_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/inet.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, fin_per_testcase/2, whitebox/1,
+-export([init_per_testcase/2, end_per_testcase/2, whitebox/1,
switch_options/1, missing_compulsory_dflags/1]).
%% 1)
@@ -77,14 +78,32 @@
-define(u32(X3,X2,X1,X0),
(((X3) bsl 24) bor ((X2) bsl 16) bor ((X1) bsl 8) bor (X0))).
-all(suite) ->
- [whitebox,switch_options,missing_compulsory_dflags].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [whitebox, switch_options, missing_compulsory_dflags].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?t:timetrap(?t:minutes(1)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl
index 19c84ab34c..f47c4603cf 100644
--- a/lib/kernel/test/erl_prim_loader_SUITE.erl
+++ b/lib/kernel/test/erl_prim_loader_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,9 +19,10 @@
-module(erl_prim_loader_SUITE).
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([get_path/1, set_path/1, get_file/1,
inet_existing/1, inet_coming_up/1, inet_disconnects/1,
@@ -29,27 +30,41 @@
local_archive/1, remote_archive/1,
primary_archive/1, virtual_dir_in_archive/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%%-----------------------------------------------------------------
%% Test suite for erl_prim_loader. (Most code is run during system start/stop.)
%%-----------------------------------------------------------------
-all(suite) ->
- [
- get_path, set_path, get_file,
- inet_existing, inet_coming_up,
- inet_disconnects, multiple_slaves,
- file_requests, local_archive,
- remote_archive, primary_archive,
- virtual_dir_in_archive
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [get_path, set_path, get_file, inet_existing,
+ inet_coming_up, inet_disconnects, multiple_slaves,
+ file_requests, local_archive, remote_archive,
+ primary_archive, virtual_dir_in_archive].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?t:timetrap(?t:minutes(3)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
@@ -291,7 +306,6 @@ wait_and_shutdown([], _) ->
ok.
-file_requests(suite) -> {req, [{local_slave_nodes, 1}, {time, 10}]};
file_requests(doc) -> ["Start a node using the 'inet' loading method, ",
"verify that the boot server responds to file requests."];
file_requests(Config) when is_list(Config) ->
@@ -300,9 +314,11 @@ file_requests(Config) when is_list(Config) ->
%% compare with results from file server calls (the
%% boot server uses the same file sys and cwd)
{ok,Files} = file:list_dir("."),
+ io:format("Files: ~p~n",[Files]),
?line {ok,Files} = rpc:call(Node, erl_prim_loader, list_dir, ["."]),
- {ok,Info} = file:read_file_info("test_server.beam"),
- ?line {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info, ["test_server.beam"]),
+ {ok,Info} = file:read_file_info(code:which(test_server)),
+ ?line {ok,Info} = rpc:call(Node, erl_prim_loader, read_file_info,
+ [code:which(test_server)]),
{ok,Cwd} = file:get_cwd(),
?line {ok,Cwd} = rpc:call(Node, erl_prim_loader, get_cwd, []),
case file:get_cwd("C:") of
diff --git a/lib/kernel/test/error_logger_SUITE.erl b/lib/kernel/test/error_logger_SUITE.erl
index eda86861d5..05bf5aae18 100644
--- a/lib/kernel/test/error_logger_SUITE.erl
+++ b/lib/kernel/test/error_logger_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
%%
-module(error_logger_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%-----------------------------------------------------------------
%% We don't have to test the normal behaviour here, i.e. the tty
@@ -27,7 +27,9 @@
%% error_logger deliver the expected events.
%%-----------------------------------------------------------------
--export([all/1, error_report/1, info_report/1, error/1, info/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ error_report/1, info_report/1, error/1, info/1,
emulator/1, tty/1, logfile/1, add/1, delete/1]).
-export([generate_error/0]).
@@ -37,9 +39,27 @@
terminate/2]).
-all(suite) ->
- [error_report, info_report, error, info,
- emulator, tty, logfile, add, delete].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [error_report, info_report, error, info, emulator, tty,
+ logfile, add, delete].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%-----------------------------------------------------------------
diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl
index 6629eca1ad..265e1ae4c8 100644
--- a/lib/kernel/test/error_logger_warn_SUITE.erl
+++ b/lib/kernel/test/error_logger_warn_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,9 @@
%%
-module(error_logger_warn_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
basic/1,warnings_info/1,warnings_warnings/1,
rb_basic/1,rb_warnings_info/1,rb_warnings_warnings/1,
rb_trunc/1,rb_utc/1,file_utc/1]).
@@ -26,7 +28,7 @@
%% Internal exports.
-export([init/1,handle_event/2,handle_info/2,handle_call/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(EXPECT(Pattern),
(fun() ->
@@ -43,15 +45,33 @@
-define(default_timeout, ?t:minutes(1)).
-all(suite) ->
- [basic, warnings_info, warnings_warnings,
- rb_basic, rb_warnings_info, rb_warnings_warnings,
- rb_trunc,rb_utc, file_utc].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic, warnings_info, warnings_warnings, rb_basic,
+ rb_warnings_info, rb_warnings_warnings, rb_trunc,
+ rb_utc, file_utc].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 1d170790a3..8078c7d021 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,29 +40,29 @@
-module(?FILE_SUITE).
--export([all/1,
- init/1, fini/1,
- init_per_testcase/2, fin_per_testcase/2,
- read_write_file/1, dirs/1, files/1, names/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+ read_write_file/1, names/1]).
-export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1,
- pos/1, pos1/1, pos2/1]).
--export([close/1, consult/1, consult1/1, path_consult/1, delete/1]).
--export([eval/1, eval1/1, path_eval/1, script/1, script1/1, path_script/1,
- open/1, open1/1,
+ pos1/1, pos2/1]).
+-export([close/1, consult1/1, path_consult/1, delete/1]).
+-export([ eval1/1, path_eval/1, script1/1, path_script/1,
+ open1/1,
old_modes/1, new_modes/1, path_open/1, open_errors/1]).
--export([file_info/1, file_info_basic_file/1, file_info_basic_directory/1,
+-export([ file_info_basic_file/1, file_info_basic_directory/1,
file_info_bad/1, file_info_times/1, file_write_file_info/1]).
--export([rename/1, access/1, truncate/1, sync/1,
- read_write/1, pread_write/1, append/1]).
--export([errors/1, e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
+-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
+ read_write/1, pread_write/1, append/1, exclusive/1]).
+-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
-export([otp_5814/1]).
--export([compression/1, read_not_really_compressed/1,
+-export([ read_not_really_compressed/1,
read_compressed_cooked/1, read_compressed_cooked_binary/1,
read_cooked_tar_problem/1,
write_compressed/1, compress_errors/1, catenated_gzips/1]).
--export([links/1, make_link/1, read_link_info_for_non_link/1, symlinks/1]).
+-export([ make_link/1, read_link_info_for_non_link/1, symlinks/1]).
-export([copy/1]).
@@ -82,6 +82,10 @@
-export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]).
+-export([advise/1]).
+
+-export([standard_io/1,mini_server/1]).
+
%% Debug exports
-export([create_file_slow/2, create_file/2, create_bin/2]).
-export([verify_file/2, verify_bin/3]).
@@ -89,22 +93,56 @@
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
-all(suite) ->
- {conf, init,
- [altname, read_write_file, dirs, files,
- delete, rename, names, errors,
- compression, links, copy,
- delayed_write, read_ahead, segment_read, segment_write,
- ipread, pid2name, interleaved_read_write,
- otp_5814, large_file, read_line_1, read_line_2, read_line_3, read_line_4],
- fini}.
-
-init(Config) when is_list(Config) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [altname, read_write_file, {group, dirs},
+ {group, files}, delete, rename, names, {group, errors},
+ {group, compression}, {group, links}, copy,
+ delayed_write, read_ahead, segment_read, segment_write,
+ ipread, pid2name, interleaved_read_write, otp_5814,
+ large_file, read_line_1, read_line_2, read_line_3,
+ read_line_4, standard_io].
+
+groups() ->
+ [{dirs, [], [make_del_dir, cur_dir_0, cur_dir_1]},
+ {files, [],
+ [{group, open}, {group, pos}, {group, file_info},
+ {group, consult}, {group, eval}, {group, script},
+ truncate, sync, datasync, advise]},
+ {open, [],
+ [open1, old_modes, new_modes, path_open, close, access,
+ read_write, pread_write, append, open_errors,
+ exclusive]},
+ {pos, [], [pos1, pos2]},
+ {file_info, [],
+ [file_info_basic_file, file_info_basic_directory,
+ file_info_bad, file_info_times, file_write_file_info]},
+ {consult, [], [consult1, path_consult]},
+ {eval, [], [eval1, path_eval]},
+ {script, [], [script1, path_script]},
+ {errors, [],
+ [e_delete, e_rename, e_make_dir, e_del_dir]},
+ {compression, [],
+ [read_compressed_cooked, read_compressed_cooked_binary,
+ read_cooked_tar_problem, read_not_really_compressed,
+ write_compressed, compress_errors, catenated_gzips]},
+ {links, [],
+ [make_link, read_link_info_for_non_link, symlinks]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
Priv = ?config(priv_dir, Config),
@@ -121,7 +159,7 @@ init(Config) when is_list(Config) ->
?FILE_INIT(Config)
end.
-fini(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
os:cmd("subst z: /d");
@@ -134,7 +172,7 @@ init_per_testcase(_Func, Config) ->
%%error_logger:info_msg("~p:~p *****~n", [?MODULE, _Func]),
?FILE_INIT_PER_TESTCASE(Config).
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
%% error_logger:info_msg("~p:~p END *****~n", [?MODULE, _Func]),
?FILE_FIN_PER_TESTCASE(Config).
@@ -170,6 +208,85 @@ time_dist({_D1, _T1} = DT1, {_D2, _T2} = DT2) ->
- calendar:datetime_to_gregorian_seconds(DT1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+mini_server(Parent) ->
+ receive
+ die ->
+ ok;
+ {io_request,From,To,{put_chars,Data}} ->
+ Parent ! {io_request,From,To,{put_chars,Data}},
+ From ! {io_reply, To, ok},
+ mini_server(Parent);
+ {io_request,From,To,{get_chars,'',N}} ->
+ Parent ! {io_request,From,To,{get_chars,'',N}},
+ From ! {io_reply, To, {ok, lists:duplicate(N,$a)}},
+ mini_server(Parent);
+ {io_request,From,To,{get_line,''}} ->
+ Parent ! {io_request,From,To,{get_line,''}},
+ From ! {io_reply, To, {ok, "hej\n"}},
+ mini_server(Parent)
+ end.
+
+standard_io(suite) ->
+ [];
+standard_io(doc) ->
+ ["Test that standard i/o-servers work with file module"];
+standard_io(Config) when is_list(Config) ->
+ %% Really just a smoke test
+ ?line Pid = spawn(?MODULE,mini_server,[self()]),
+ ?line register(mini_server,Pid),
+ ?line ok = file:write(mini_server,<<"hej\n">>),
+ ?line receive
+ {io_request,_,_,{put_chars,<<"hej\n">>}} ->
+ ok
+ after 1000 ->
+ exit(noreply)
+ end,
+ ?line {ok,"aaaaa"} = file:read(mini_server,5),
+ ?line receive
+ {io_request,_,_,{get_chars,'',5}} ->
+ ok
+ after 1000 ->
+ exit(noreply)
+ end,
+ ?line {ok,"hej\n"} = file:read_line(mini_server),
+ ?line receive
+ {io_request,_,_,{get_line,''}} ->
+ ok
+ after 1000 ->
+ exit(noreply)
+ end,
+ ?line OldGL = group_leader(),
+ ?line group_leader(Pid,self()),
+ ?line ok = file:write(standard_io,<<"hej\n">>),
+ ?line group_leader(OldGL,self()),
+ ?line receive
+ {io_request,_,_,{put_chars,<<"hej\n">>}} ->
+ ok
+ after 1000 ->
+ exit(noreply)
+ end,
+ ?line group_leader(Pid,self()),
+ ?line {ok,"aaaaa"} = file:read(standard_io,5),
+ ?line group_leader(OldGL,self()),
+ ?line receive
+ {io_request,_,_,{get_chars,'',5}} ->
+ ok
+ after 1000 ->
+ exit(noreply)
+ end,
+ ?line group_leader(Pid,self()),
+ ?line {ok,"hej\n"} = file:read_line(standard_io),
+ ?line group_leader(OldGL,self()),
+ ?line receive
+ {io_request,_,_,{get_line,''}} ->
+ ok
+ after 1000 ->
+ exit(noreply)
+ end,
+ Pid ! die,
+ receive after 1000 -> ok end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
read_write_file(suite) -> [];
read_write_file(doc) -> [];
@@ -230,7 +347,6 @@ read_write_file(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-dirs(suite) -> [make_del_dir, cur_dir_0, cur_dir_1].
make_del_dir(suite) -> [];
make_del_dir(doc) -> [];
@@ -377,10 +493,7 @@ win_cur_dir_1(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-files(suite) -> [open,pos,file_info,consult,eval,script,truncate,sync].
-open(suite) -> [open1,old_modes,new_modes,path_open,close,access,read_write,
- pread_write,append,open_errors].
open1(suite) -> [];
open1(doc) -> [];
@@ -754,9 +867,24 @@ open_errors(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+exclusive(suite) -> [];
+exclusive(doc) -> "Test exclusive access to a file.";
+exclusive(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_exclusive"),
+ ?line ok = ?FILE_MODULE:make_dir(NewDir),
+ ?line Name = filename:join(NewDir, "ex_file.txt"),
+ ?line {ok, Fd} = ?FILE_MODULE:open(Name, [write, exclusive]),
+ ?line {error, eexist} = ?FILE_MODULE:open(Name, [write, exclusive]),
+ ?line ok = ?FILE_MODULE:close(Fd),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-pos(suite) -> [pos1,pos2].
pos1(suite) -> [];
pos1(doc) -> [];
@@ -848,8 +976,6 @@ pos2(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-file_info(suite) -> [file_info_basic_file, file_info_basic_directory,
- file_info_bad, file_info_times, file_write_file_info].
file_info_basic_file(suite) -> [];
file_info_basic_file(doc) -> [];
@@ -1115,7 +1241,6 @@ file_write_file_info(Config) when is_list(Config) ->
get_good_directory(Config) ->
?line ?config(priv_dir, Config).
-consult(suite) -> [consult1, path_consult].
consult1(suite) -> [];
consult1(doc) -> [];
@@ -1176,7 +1301,6 @@ path_consult(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-eval(suite) -> [eval1,path_eval].
eval1(suite) -> [];
eval1(doc) -> [];
@@ -1249,7 +1373,6 @@ path_eval(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-script(suite) -> [script1,path_script].
script1(suite) -> [];
script1(doc) -> "";
@@ -1355,6 +1478,30 @@ truncate(Config) when is_list(Config) ->
ok.
+datasync(suite) -> [];
+datasync(doc) -> "Tests that ?FILE_MODULE:datasync/1 at least doesn't crash.";
+datasync(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Sync = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_sync.fil"),
+
+ %% Raw open.
+ ?line {ok, Fd} = ?FILE_MODULE:open(Sync, [write, raw]),
+ ?line ok = ?FILE_MODULE:datasync(Fd),
+ ?line ok = ?FILE_MODULE:close(Fd),
+
+ %% Ordinary open.
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Sync, [write]),
+ ?line ok = ?FILE_MODULE:datasync(Fd2),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
sync(suite) -> [];
sync(doc) -> "Tests that ?FILE_MODULE:sync/1 at least doesn't crash.";
sync(Config) when is_list(Config) ->
@@ -1378,6 +1525,77 @@ sync(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+advise(suite) -> [];
+advise(doc) -> "Tests that ?FILE_MODULE:advise/4 at least doesn't crash.";
+advise(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Advise = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_advise.fil"),
+
+ Line1 = "Hello\n",
+ Line2 = "World!\n",
+
+ ?line {ok, Fd} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = ?FILE_MODULE:advise(Fd, 0, 0, normal),
+ ?line ok = io:format(Fd, "~s", [Line1]),
+ ?line ok = io:format(Fd, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd),
+
+ ?line {ok, Fd2} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = ?FILE_MODULE:advise(Fd2, 0, 0, random),
+ ?line ok = io:format(Fd2, "~s", [Line1]),
+ ?line ok = io:format(Fd2, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd2),
+
+ ?line {ok, Fd3} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = ?FILE_MODULE:advise(Fd3, 0, 0, sequential),
+ ?line ok = io:format(Fd3, "~s", [Line1]),
+ ?line ok = io:format(Fd3, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd3),
+
+ ?line {ok, Fd4} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = ?FILE_MODULE:advise(Fd4, 0, 0, will_need),
+ ?line ok = io:format(Fd4, "~s", [Line1]),
+ ?line ok = io:format(Fd4, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd4),
+
+ ?line {ok, Fd5} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = ?FILE_MODULE:advise(Fd5, 0, 0, dont_need),
+ ?line ok = io:format(Fd5, "~s", [Line1]),
+ ?line ok = io:format(Fd5, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd5),
+
+ ?line {ok, Fd6} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = ?FILE_MODULE:advise(Fd6, 0, 0, no_reuse),
+ ?line ok = io:format(Fd6, "~s", [Line1]),
+ ?line ok = io:format(Fd6, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd6),
+
+ ?line {ok, Fd7} = ?FILE_MODULE:open(Advise, [write]),
+ ?line {error, einval} = ?FILE_MODULE:advise(Fd7, 0, 0, bad_advise),
+ ?line ok = ?FILE_MODULE:close(Fd7),
+
+ %% test write without advise, then a read after an advise
+ ?line {ok, Fd8} = ?FILE_MODULE:open(Advise, [write]),
+ ?line ok = io:format(Fd8, "~s", [Line1]),
+ ?line ok = io:format(Fd8, "~s", [Line2]),
+ ?line ok = ?FILE_MODULE:close(Fd8),
+ ?line {ok, Fd9} = ?FILE_MODULE:open(Advise, [read]),
+ Offset = 0,
+ %% same as a 0 length in some implementations
+ Length = length(Line1) + length(Line2),
+ ?line ok = ?FILE_MODULE:advise(Fd9, Offset, Length, sequential),
+ ?line {ok, Line1} = ?FILE_MODULE:read_line(Fd9),
+ ?line {ok, Line2} = ?FILE_MODULE:read_line(Fd9),
+ ?line eof = ?FILE_MODULE:read_line(Fd9),
+ ?line ok = ?FILE_MODULE:close(Fd9),
+
+ ?line [] = flush(),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1505,7 +1723,6 @@ names(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-errors(suite) -> [e_delete, e_rename, e_make_dir, e_del_dir].
e_delete(suite) -> [];
e_delete(doc) -> [];
@@ -1762,12 +1979,6 @@ e_del_dir(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-compression(suite) ->
- [read_compressed_cooked, read_compressed_cooked_binary,
- read_cooked_tar_problem,
- read_not_really_compressed,
- write_compressed, compress_errors,
- catenated_gzips].
%% Trying reading and positioning from a compressed file.
@@ -2061,8 +2272,6 @@ altname(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
Result.
-links(doc) -> "Test the link functions.";
-links(suite) -> [make_link, read_link_info_for_non_link, symlinks].
make_link(doc) -> "Test creating a hard link.";
make_link(suite) -> [];
@@ -3071,7 +3280,7 @@ large_file(Config) when is_list(Config) ->
{{unix,sunos},{A,B,C}}
when A == 5, B == 5, C >= 1; A == 5, B >= 6; A >= 6 ->
do_large_file(Config);
- {{unix,Unix},_} when Unix =:= linux; Unix =:= darwin ->
+ {{unix,Unix},_} when Unix =/= sunos ->
N = unix_free(Config),
io:format("Free: ~w KByte~n", [N]),
if N < 5 * (1 bsl 20) ->
@@ -3081,7 +3290,7 @@ large_file(Config) when is_list(Config) ->
do_large_file(Config)
end;
_ ->
- {skipped,"Only supported on Win32, Linux, or SunOS >= 5.5.1"}
+ {skipped,"Only supported on Win32, Unix or SunOS >= 5.5.1"}
end.
unix_free(Config) ->
@@ -3093,7 +3302,7 @@ unix_free(Config) ->
N.
do_large_file(Config) ->
- ?line Watchdog = ?t:timetrap(?t:minutes(4)),
+ ?line Watchdog = ?t:timetrap(?t:minutes(5)),
%%
?line Name = filename:join(?config(priv_dir, Config),
?MODULE_STRING ++ "_large_file"),
@@ -3132,6 +3341,17 @@ do_large_file(Config) ->
?line {ok,P} = ?FILE_MODULE:position(F, {eof,-L}),
?line {ok,Rs} = ?FILE_MODULE:read(F, L+1),
?line ok = ?FILE_MODULE:close(F),
+ %% Reopen the file with 'append'; used to fail on Windows causing
+ %% writes to go to the beginning of the file for files > 4GB.
+ ?line PL = P + L,
+ ?line PLL = PL + L,
+ ?line {ok,F1} = ?FILE_MODULE:open(Name, [raw,read,write,append]),
+ ?line ok = ?FILE_MODULE:write(F1, R),
+ ?line {ok,PLL} = ?FILE_MODULE:position(F1, {cur,0}),
+ ?line {ok,Rs} = ?FILE_MODULE:pread(F1, P, L),
+ ?line {ok,PL} = ?FILE_MODULE:position(F1, {eof,-L}),
+ ?line {ok,R} = ?FILE_MODULE:read(F1, L+1),
+ ?line ok = ?FILE_MODULE:close(F1),
%%
?line Mref = erlang:monitor(process, Deleter),
?line Deleter ! {Tester,done},
diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl
new file mode 100644
index 0000000000..53bcb1162d
--- /dev/null
+++ b/lib/kernel/test/file_name_SUITE.erl
@@ -0,0 +1,1756 @@
+-module(file_name_SUITE).
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("kernel/include/file.hrl").
+
+%%
+%% File operations that take filenames as parameters (* not prim_file operation) (** a drive):
+%% altname
+%% copy (*)
+%% del_dir
+%% delete
+%% get_cwd (**)
+%% list_dir
+%% make_dir
+%% make_link
+%% make_symlink
+%% open
+%% read_file
+%% read_file_info
+%% read_link
+%% read_link_info
+%% rename
+%% set_cwd
+%% write_file
+%% write_file_info
+%%
+%% File operations that opens/uses separate driver port (not connected to file)
+%% altname
+%% del_dir
+%% delete
+%% get_cwd
+%% list_dir
+%% make_dir
+%% make_link
+%% make_symlink
+%% read_file_info
+%% read_link
+%% read_link_info
+%% rename
+%% set_cwd
+%% write_file_info
+%%
+%% Operations that use ?FD_DRV in prim_file
+%% open
+%% read_file
+%% write_file
+%%
+%%
+%% Operations that return a filename/path
+%% altname
+%% get_cwd
+%% list_dir
+%% read_link
+
+-export([all/0,groups/0,suite/0,
+ init_per_suite/1,end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
+-export([normal/1,icky/1,very_icky/1,normalize/1]).
+
+
+init_per_testcase(_Func, Config) ->
+ Dog = test_server:timetrap(test_server:seconds(60)),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Func, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [normal, icky, very_icky, normalize].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+normalize(suite) ->
+ [];
+normalize(doc) ->
+ ["Check that filename normalization works"];
+normalize(Config) when is_list(Config) ->
+ random:seed({1290,431421,830412}),
+ try
+ ?line UniMode = file:native_name_encoding() =/= latin1,
+ if
+ not UniMode ->
+ throw(need_unicode_mode);
+ true ->
+ ok
+ end,
+ ?line Pairs = [rand_comp_decomp(200) || _ <- lists:seq(1,1000)],
+ case os:type() of
+ {unix,darwin} ->
+ ?line [ true = (A =:= prim_file:internal_native2name(B)) ||
+ {A,B} <- Pairs ];
+ _ ->
+ ok
+ end,
+ ?line [ true = (A =:= prim_file:internal_normalize_utf8(B)) ||
+ {A,B} <- Pairs ]
+
+ catch
+ throw:need_unicode_mode ->
+ io:format("Sorry, can only run in unicode mode.~n"),
+ {skipped,"VM needs to be started in Unicode filename mode"}
+ end.
+
+normal(suite) ->
+ [];
+normal(doc) ->
+ "Check file operations on normal file names regardless of unicode mode";
+normal(Config) when is_list(Config) ->
+ {ok,Dir} = file:get_cwd(),
+ try
+ Priv = ?config(priv_dir, Config),
+ file:set_cwd(Priv),
+ put(file_module,prim_file),
+ ok = check_normal(prim_file),
+ put(file_module,file),
+ ok = check_normal(file)
+ after
+ file:set_cwd(Dir)
+ end.
+
+
+icky(suite) ->
+ [];
+icky(doc) ->
+ "Check file operations on normal file names regardless of unicode mode";
+icky(Config) when is_list(Config) ->
+ case hopeless_darwin() of
+ true ->
+ {skipped,"This version of darwin does not support icky names at all."};
+ false ->
+ {ok,Dir} = file:get_cwd(),
+ try
+ Priv = ?config(priv_dir, Config),
+ file:set_cwd(Priv),
+ put(file_module,prim_file),
+ ok = check_icky(prim_file),
+ put(file_module,file),
+ ok = check_icky(file)
+ after
+ file:set_cwd(Dir)
+ end
+ end.
+very_icky(suite) ->
+ [];
+very_icky(doc) ->
+ "Check file operations on normal file names regardless of unicode mode";
+very_icky(Config) when is_list(Config) ->
+ case hopeless_darwin() of
+ true ->
+ {skipped,"This version of darwin does not support icky names at all."};
+ false ->
+ {ok,Dir} = file:get_cwd(),
+ try
+ Priv = ?config(priv_dir, Config),
+ file:set_cwd(Priv),
+ put(file_module,prim_file),
+ case check_very_icky(prim_file) of
+ need_unicode_mode ->
+ {skipped,"VM needs to be started in Unicode filename mode"};
+ ok ->
+ put(file_module,file),
+ ok = check_very_icky(file)
+ end
+ after
+ file:set_cwd(Dir)
+ end
+ end.
+
+
+check_normal(Mod) ->
+ {ok,Dir} = Mod:get_cwd(),
+ try
+ ?line make_normal_dir(Mod),
+ ?line {ok, L0} = Mod:list_dir("."),
+ ?line L1 = lists:sort(L0),
+ %erlang:display(L1),
+ ?line L1 = lists:sort(list(normal_dir())),
+ ?line {ok,D2} = Mod:get_cwd(),
+ ?line true = is_list(D2),
+ ?line case Mod:altname("fil1") of
+ {error,enotsup} ->
+ ok;
+ {ok,LLL} when is_list(LLL) ->
+ ok
+ end,
+ ?line [ true = is_list(El) || El <- L1],
+ ?line Syms = [ {S,Targ,list_to_binary(get_data(Targ,normal_dir()))}
+ || {T,S,Targ} <- normal_dir(), T =:= symlink ],
+ ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
+ ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
+ ?line chk_cre_dir(Mod,[{directory,"temp_dir",normal_dir()}]),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line true = is_list(BeginAt),
+ ?line {error,enoent} = Mod:set_cwd("tmp_dir"),
+ ?line ok = Mod:set_cwd("temp_dir"),
+ ?line {ok, NowAt} = Mod:get_cwd(),
+ ?line true = BeginAt =/= NowAt,
+ ?line ok = Mod:set_cwd(".."),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line rm_r(Mod,"temp_dir"),
+ ?line true = is_list(Dir),
+ ?line [ true = is_list(FN) || FN <- L0 ],
+ case has_links() of
+ true ->
+ ?line ok = Mod:make_link("fil1","nisse"),
+ ?line {ok, <<"fil1">>} = Mod:read_file("nisse"),
+ ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"),
+ ?line ok = Mod:delete("nisse"),
+ ?line {ok, <<"fil1">>} = Mod:read_file("fil1"),
+ ?line {error,enoent} = Mod:read_file("nisse"),
+ ?line {error,enoent} = Mod:read_link_info("nisse");
+ false ->
+ ok
+ end,
+ ?line [ begin
+ ?line {ok, FD} = Mod:open(Name,[read]),
+ ?line {ok, Content} = Mod:read(FD,1024),
+ ?line ok = file:close(FD)
+ end || {regular,Name,Content} <- normal_dir() ],
+ ?line [ begin
+ ?line {ok, FD} = Mod:open(Name,[read,binary]),
+ ?line BC = list_to_binary(Content),
+ ?line {ok, BC} = Mod:read(FD,1024),
+ ?line ok = file:close(FD)
+ end || {regular,Name,Content} <- normal_dir() ],
+ ?line Mod:rename("fil1","tmp_fil1"),
+ ?line {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"),
+ ?line {error,enoent} = Mod:read_file("fil1"),
+ ?line Mod:rename("tmp_fil1","fil1"),
+ ?line {ok, <<"fil1">>} = Mod:read_file("fil1"),
+ ?line {error,enoent} = Mod:read_file("tmp_fil1"),
+ ?line {ok,FI} = Mod:read_file_info("fil1"),
+ ?line NewMode = FI#file_info.mode band (bnot 8#333),
+ ?line NewMode2 = NewMode bor 8#222,
+ ?line true = NewMode2 =/= NewMode,
+ ?line ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode}),
+ ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("fil1"),
+ ?line ok = Mod:write_file_info("fil1",FI#file_info{mode = NewMode2}),
+ ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("fil1"),
+ ok
+ after
+ case Mod:read_file_info("fil1") of
+ {ok,FII} ->
+ NewModeI = FII#file_info.mode bor 8#777,
+ Mod:write_file_info("fil1",FII#file_info{mode = NewModeI});
+ _ ->
+ ok
+ end,
+ Mod:set_cwd(Dir),
+ io:format("Wd now: ~s~n",[Dir])
+ end.
+
+check_icky(Mod) ->
+ {ok,Dir} = Mod:get_cwd(),
+ try
+ ?line true=(length("���") =:= 3),
+ ?line UniMode = file:native_name_encoding() =/= latin1,
+ ?line make_icky_dir(Mod),
+ ?line {ok, L0} = Mod:list_dir("."),
+ ?line L1 = lists:sort(L0),
+ io:format("~p ~p~n",[L1,list(icky_dir())]),
+ ?line L1 = lists:sort(convlist(list(icky_dir()))),
+ ?line {ok,D2} = Mod:get_cwd(),
+ ?line true = is_list(D2),
+%% Altname only on windows, and there are no non native filenames there
+%% ?line case Mod:altname("fil1") of
+%% {error,enotsup} ->
+%% ok;
+%% {ok,LLL} when is_list(LLL) ->
+%% ok
+%% end,
+ ?line [ true = ((is_list(El) or (UniMode and is_binary(El)))) || El <- L1],
+ ?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,icky_dir()))}
+ || {T,S,Targ} <- icky_dir(), T =:= symlink ],
+ ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
+ ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
+ ?line chk_cre_dir(Mod,[{directory,"���_dir",icky_dir()}]),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line true = is_list(BeginAt),
+ ?line {error,enoent} = Mod:set_cwd("��_dir"),
+ ?line ok = Mod:set_cwd("���_dir"),
+ ?line {ok, NowAt} = Mod:get_cwd(),
+ ?line true = is_list(NowAt),
+ ?line true = BeginAt =/= NowAt,
+ ?line ok = Mod:set_cwd(".."),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line rm_r2(Mod,"���_dir"),
+ {OS,TYPE} = os:type(),
+ % Check that treat_icky really converts to the same as the OS
+ case UniMode of
+ true ->
+ ?line chk_cre_dir(Mod,[{directory,"���_dir",[]}]),
+ ?line ok = Mod:set_cwd("���_dir"),
+ ?line ok = Mod:write_file(<<"���">>,<<"hello">>),
+ ?line Treated = treat_icky(<<"���">>),
+ ?line {ok,[Treated]} = Mod:list_dir("."),
+ ?line ok = Mod:delete(<<"���">>),
+ ?line {ok,[]} = Mod:list_dir("."),
+ ?line ok = Mod:set_cwd(".."),
+ ?line rm_r2(Mod,"���_dir");
+ false ->
+ ok
+ end,
+
+ ?line chk_cre_dir(Mod,[{directory,treat_icky(<<"���_dir">>),icky_dir()}]),
+ if
+ UniMode and (OS =/= win32) ->
+ ?line {error,enoent} = Mod:set_cwd("���_dir");
+ true ->
+ ok
+ end,
+ ?line ok = Mod:set_cwd(treat_icky(<<"���_dir">>)),
+ ?line {ok, NowAt2} = Mod:get_cwd(),
+ io:format("~p~n",[NowAt2]),
+ % Cannot create raw unicode-breaking filenames on windows or macos
+ ?line true = ((((not UniMode) or (OS =:= win32) or (TYPE=:=darwin)) and is_list(NowAt2)) orelse ((UniMode) and is_binary(NowAt2))),
+ ?line true = BeginAt =/= NowAt2,
+ ?line ok = Mod:set_cwd(".."),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line rm_r2(Mod,conv(treat_icky(<<"���_dir">>))),
+ case has_links() of
+ true ->
+ ?line ok = Mod:make_link("fil1","nisse�"),
+ ?line {ok, <<"fil1">>} = Mod:read_file("nisse�"),
+ ?line {ok, #file_info{type = regular}} = Mod:read_link_info("nisse�"),
+ ?line ok = Mod:delete("nisse�"),
+ ?line ok = Mod:make_link("fil1",treat_icky(<<"nisse�">>)),
+ ?line {ok, <<"fil1">>} = Mod:read_file(treat_icky(<<"nisse�">>)),
+ ?line {ok, #file_info{type = regular}} = Mod:read_link_info(treat_icky(<<"nisse�">>)),
+ ?line ok = Mod:delete(treat_icky(<<"nisse�">>)),
+ ?line {ok, <<"fil1">>} = Mod:read_file("fil1"),
+ ?line {error,enoent} = Mod:read_file("nisse�"),
+ ?line {error,enoent} = Mod:read_link_info("nisse�"),
+ ?line {error,enoent} = Mod:read_file(treat_icky(<<"nisse�">>)),
+ ?line {error,enoent} = Mod:read_link_info(treat_icky(<<"nisse�">>));
+ false ->
+ ok
+ end,
+ ?line [ begin
+ ?line {ok, FD} = Mod:open(Name,[read]),
+ ?line {ok, Content} = Mod:read(FD,1024),
+ ?line ok = file:close(FD)
+ end || {regular,Name,Content} <- icky_dir() ],
+ ?line [ begin
+ ?line {ok, FD} = Mod:open(Name,[read,binary]),
+ ?line BC = list_to_binary([Content]),
+ ?line {ok, BC} = Mod:read(FD,1024),
+ ?line ok = file:close(FD)
+ end || {regular,Name,Content} <- icky_dir() ],
+ ?line Mod:rename("���2","���_fil1"),
+ ?line {ok, <<"���2">>} = Mod:read_file("���_fil1"),
+ ?line {error,enoent} = Mod:read_file("���2"),
+ ?line Mod:rename("���_fil1","���2"),
+ ?line {ok, <<"���2">>} = Mod:read_file("���2"),
+ ?line {error,enoent} = Mod:read_file("���_fil1"),
+
+ ?line Mod:rename("���2",treat_icky(<<"���_fil1">>)),
+ ?line {ok, <<"���2">>} = Mod:read_file(treat_icky(<<"���_fil1">>)),
+ if
+ UniMode and (OS =/= win32) ->
+ {error,enoent} = Mod:read_file("���_fil1");
+ true ->
+ ok
+ end,
+ ?line {error,enoent} = Mod:read_file("���2"),
+ ?line Mod:rename(treat_icky(<<"���_fil1">>),"���2"),
+ ?line {ok, <<"���2">>} = Mod:read_file("���2"),
+ ?line {error,enoent} = Mod:read_file("���_fil1"),
+ ?line {error,enoent} = Mod:read_file(treat_icky(<<"���_fil1">>)),
+
+ ?line {ok,FI} = Mod:read_file_info("���2"),
+ ?line NewMode = FI#file_info.mode band (bnot 8#333),
+ ?line NewMode2 = NewMode bor 8#222,
+ ?line true = NewMode2 =/= NewMode,
+ ?line ok = Mod:write_file_info("���2",FI#file_info{mode = NewMode}),
+ ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info("���2"),
+ ?line ok = Mod:write_file_info("���2",FI#file_info{mode = NewMode2}),
+ ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info("���2"),
+
+ ?line {ok,FII} = Mod:read_file_info(treat_icky(<<"���5">>)),
+ ?line true = NewMode2 =/= NewMode,
+ ?line ok = Mod:write_file_info(treat_icky(<<"���5">>),FII#file_info{mode = NewMode}),
+ ?line {ok,#file_info{mode = NewMode}} = Mod:read_file_info(treat_icky(<<"���5">>)),
+ ?line ok = Mod:write_file_info(<<"���5">>,FII#file_info{mode = NewMode2}),
+ ?line {ok,#file_info{mode = NewMode2}} = Mod:read_file_info(treat_icky(<<"���5">>)),
+ ok
+ after
+ Mod:set_cwd(Dir),
+ io:format("Wd now: ~s~n",[Dir])
+ end.
+
+check_very_icky(Mod) ->
+ {ok,Dir} = Mod:get_cwd(),
+ try
+ ?line true=(length("���") =:= 3),
+ ?line UniMode = file:native_name_encoding() =/= latin1,
+ if
+ not UniMode ->
+ throw(need_unicode_mode);
+ true ->
+ ok
+ end,
+ ?line make_very_icky_dir(Mod),
+ ?line {ok, L0} = Mod:list_dir("."),
+ ?line L1 = lists:sort(L0),
+ ?line L1 = lists:sort(convlist(list(very_icky_dir()))),
+ ?line {ok,D2} = Mod:get_cwd(),
+ ?line true = is_list(D2),
+ ?line [ true = ((is_list(El) or is_binary(El))) || El <- L1],
+ ?line Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,very_icky_dir()))}
+ || {T,S,Targ} <- very_icky_dir(), T =:= symlink ],
+ ?line [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ],
+ ?line [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ],
+ ?line chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line true = is_list(BeginAt),
+ ?line {error,enoent} = Mod:set_cwd("��_dir"),
+ ?line ok = Mod:set_cwd([1088,1079,1091]++"_dir"),
+ ?line {ok, NowAt} = Mod:get_cwd(),
+ ?line true = is_list(NowAt),
+ ?line true = BeginAt =/= NowAt,
+ ?line ok = Mod:set_cwd(".."),
+ ?line {ok,BeginAt} = Mod:get_cwd(),
+ ?line rm_r2(Mod,[1088,1079,1091]++"_dir"),
+
+ case has_links() of
+ true ->
+ ?line ok = Mod:make_link("fil1","nisse"++[1088,1079,1091]),
+ ?line {ok, <<"fil1">>} =
+ Mod:read_file("nisse"++[1088,1079,1091]),
+ ?line {ok, #file_info{type = regular}} =
+ Mod:read_link_info("nisse"++[1088,1079,1091]),
+ ?line ok = Mod:delete("nisse"++[1088,1079,1091]),
+ ?line ok = Mod:make_link("fil1",<<"nisse�">>),
+ ?line {ok, <<"fil1">>} = Mod:read_file(<<"nisse�">>),
+ ?line {ok, #file_info{type = regular}} =
+ Mod:read_link_info(<<"nisse�">>),
+ ?line ok = Mod:delete(<<"nisse�">>),
+ ?line {ok, <<"fil1">>} = Mod:read_file("fil1"),
+ ?line {error,enoent} = Mod:read_file("nisse"++[1088,1079,1091]),
+ ?line {error,enoent} = Mod:read_link_info("nisse"++[1088,1079,1091]),
+ ?line {error,enoent} = Mod:read_file(<<"nisse�">>),
+ ?line {error,enoent} = Mod:read_link_info(<<"nisse�">>);
+ false ->
+ ok
+ end,
+ ?line [ begin
+ ?line {ok, FD} = Mod:open(Name,[read]),
+ ?line {ok, Content} = Mod:read(FD,1024),
+ ?line ok = file:close(FD)
+ end || {regular,Name,Content} <- very_icky_dir() ],
+ ?line [ begin
+ ?line {ok, FD} = Mod:open(Name,[read,binary]),
+ ?line BC = list_to_binary([Content]),
+ ?line {ok, BC} = Mod:read(FD,1024),
+ ?line ok = file:close(FD)
+ end || {regular,Name,Content} <- very_icky_dir() ],
+ ?line Mod:rename([956,965,963,954,959,49],
+ [956,965,963,954,959]++"_fil1"),
+ ?line {ok, <<"���2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"),
+ ?line {error,enoent} = Mod:read_file([956,965,963,954,959,49]),
+ ?line Mod:rename([956,965,963,954,959]++"_fil1",[956,965,963,954,959,49]),
+ ?line {ok, <<"���2">>} = Mod:read_file([956,965,963,954,959,49]),
+ ?line {error,enoent} = Mod:read_file([956,965,963,954,959]++"_fil1"),
+
+ ?line {ok,FI} = Mod:read_file_info([956,965,963,954,959,49]),
+ ?line NewMode = FI#file_info.mode band (bnot 8#333),
+ ?line NewMode2 = NewMode bor 8#222,
+ ?line true = NewMode2 =/= NewMode,
+ ?line ok = Mod:write_file_info([956,965,963,954,959,49],
+ FI#file_info{mode = NewMode}),
+ ?line {ok,#file_info{mode = NewMode}} =
+ Mod:read_file_info([956,965,963,954,959,49]),
+ ?line ok = Mod:write_file_info([956,965,963,954,959,49],
+ FI#file_info{mode = NewMode2}),
+ ?line {ok,#file_info{mode = NewMode2}} =
+ Mod:read_file_info([956,965,963,954,959,49]),
+ ?line NumOK0 = case has_links() of
+ true -> 5;
+ false -> 3
+ end,
+ ?line NumNOK0 = case has_links() of
+ true -> 4;
+ false -> 3
+ end,
+ ?line {NumOK,NumNOK} = case is_binary(treat_icky(<<"foo">>)) of
+ false ->
+ {NumOK0+NumNOK0,0};
+ true ->
+ {NumOK0,NumNOK0}
+ end,
+ ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}),
+ ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false),
+ ?line SF3 = unicode:characters_to_binary("���subfil3",
+ file:native_name_encoding()),
+ ?line SF2 = case treat_icky(<<"���subfil2">>) of
+ LF2 when is_list(LF2) ->
+ unicode:characters_to_binary(LF2,
+ file:native_name_encoding());
+ BF2 ->
+ BF2
+ end,
+ ?line Sorted = lists:sort([SF3,SF2]),
+ ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)),
+ ok
+ catch
+ throw:need_unicode_mode ->
+ io:format("Sorry, can only run in unicode mode.~n"),
+ need_unicode_mode
+ after
+ Mod:set_cwd(Dir),
+ io:format("Wd now: ~s~n",[Dir])
+ end.
+
+%%
+%% Utilities
+%%
+
+
+rm_rf(Mod,Dir) ->
+ case Mod:read_link_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ {ok, Content} = Mod:list_dir(Dir),
+ [ rm_rf(Mod,filename:join(Dir,C)) || C <- Content ],
+ Mod:del_dir(Dir),
+ ok;
+ {ok, #file_info{}} ->
+ Mod:delete(Dir);
+ _ ->
+ ok
+ end.
+
+rm_r(Mod,Dir) ->
+ %erlang:display({rm_r,Dir}),
+ case Mod:read_link_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ {ok,#file_info{type = directory}} = Mod:read_file_info(Dir),
+ {ok, Content} = Mod:list_dir(Dir),
+ [ true = is_list(Part) || Part <- Content ],
+ [ true = is_list(filename:join(Dir,Part)) || Part <- Content ],
+ [ rm_r(Mod,filename:join(Dir,C)) || C <- Content ],
+ ok = Mod:del_dir(Dir),
+ ok;
+ {ok, #file_info{type = regular}} ->
+ {ok,#file_info{type = regular}} = Mod:read_file_info(Dir),
+ ok = Mod:delete(Dir);
+ {ok, #file_info{type = symlink}} ->
+ ok = Mod:delete(Dir)
+ end.
+%% For icky test, allow binaries sometimes
+rm_r2(Mod,Dir) ->
+ %erlang:display({rm_r2,Dir}),
+ case Mod:read_link_info(Dir) of
+ {ok, #file_info{type = directory}} ->
+ {ok,#file_info{type = directory}} = Mod:read_file_info(Dir),
+ {ok, Content} = Mod:list_dir(Dir),
+ UniMode = file:native_name_encoding() =/= latin1,
+ [ true = (is_list(Part) orelse UniMode) || Part <- Content ],
+ [ true = (is_list(filename:join(Dir,Part)) orelse UniMode) || Part <- Content ],
+ [ rm_r2(Mod,filename:join(Dir,C)) || C <- Content ],
+ ok = Mod:del_dir(Dir),
+ ok;
+ {ok, #file_info{type = regular}} ->
+ {ok,#file_info{type = regular}} = Mod:read_file_info(Dir),
+ ok = Mod:delete(Dir);
+ {ok, #file_info{type = symlink}} ->
+ ok = Mod:delete(Dir)
+ end.
+chk_cre_dir(_,[]) ->
+ ok;
+chk_cre_dir(Mod,[{regular,Name,Content}|T]) ->
+ %io:format("~p~n",[Name]),
+ ok = Mod:write_file(Name,Content),
+ chk_cre_dir(Mod,T);
+chk_cre_dir(Mod,[{link,Name,Target}|T]) ->
+ ok = Mod:make_link(Target,Name),
+ chk_cre_dir(Mod,T);
+chk_cre_dir(Mod,[{symlink,Name,Target}|T]) ->
+ ok = Mod:make_symlink(Target,Name),
+ chk_cre_dir(Mod,T);
+chk_cre_dir(Mod,[{directory,Name,Content}|T]) ->
+ ok = Mod:make_dir(Name),
+ %io:format("Content = ~p~n",[Content]),
+ Content2 = [{Ty,filename:join(Name,N),case Ty of link -> filename:join(Name,C); _ -> C end} || {Ty,N,C} <- Content ],
+ %io:format("Content2 = ~p~n",[Content2]),
+ chk_cre_dir(Mod,Content2),
+ chk_cre_dir(Mod,T).
+
+has_links() ->
+ case os:type() of
+ {win32,_} ->
+ case os:version() of
+ {N,NN,_} when (N > 5) andalso (NN >= 1) ->
+ true;
+ _ ->
+ false
+ end;
+ _ ->
+ true
+ end.
+
+make_normal_dir(Mod) ->
+ rm_rf(Mod,"normal_dir"),
+ Mod:make_dir("normal_dir"),
+ Mod:set_cwd("normal_dir"),
+ Mod:write_file("fil1","fil1"),
+ Mod:write_file("fil2","fil2"),
+ case has_links() of
+ true ->
+ Mod:make_link("fil2","fil3"),
+ Mod:make_symlink("fil2","fil4");
+ _ ->
+ ok
+ end,
+ Mod:make_dir("subdir"),
+ Mod:write_file(filename:join("subdir","subfil1"),"subfil1"),
+ ok.
+
+normal_dir() ->
+ [{regular,"fil1","fil1"},
+ {regular,"fil2","fil2"}] ++
+ case has_links() of
+ true ->
+ [{regular,"fil3","fil2"},
+ {symlink,"fil4","fil2"}];
+ false ->
+ []
+ end ++
+ [{directory,"subdir",
+ [{regular,"subfil1","subfil1"}]}].
+
+make_icky_dir(Mod) ->
+ rm_rf(Mod,"icky_dir"),
+ Icky=icky_dir(),
+ chk_cre_dir(Mod,[{directory,"icky_dir",linkify([],Icky)}]),
+ Mod:set_cwd("icky_dir"),
+ ok.
+
+linkify(_Passed,[]) ->
+ [];
+linkify(Passed,[{regular,Name,Content}|T]) ->
+ Regulars = [ {N,C} || {regular,N,C} <- Passed, N =/= Name ],
+ case lists:keysearch(Content,2,Regulars) of
+ {value, {Linkto, Content}} ->
+ [{link,Name,Linkto} | linkify(Passed,T)];
+ _ ->
+ [{regular,Name,Content} | linkify([{regular,Name,Content}|Passed],T)]
+ end;
+linkify(Passed,[{directory, Name, Content}|T]) ->
+ [{directory,Name, linkify(Content,Content)}|linkify(Passed,T)];
+linkify(Passed,[H|T]) ->
+ [H|linkify([H|Passed],T)].
+
+hopeless_darwin() ->
+ case {os:type(),os:version()} of
+ {{unix,darwin},{Major,_,_}} when Major < 9 ->
+ true;
+ _ ->
+ false
+ end.
+
+icky_dir() ->
+ [{regular,"fil1","fil1"},
+ {regular,"���2","���2"}] ++
+ case has_links() of
+ true ->
+ [{regular,"���3","���2"},
+ {symlink,"���4","���2"}];
+ false ->
+ []
+ end ++
+ [{regular,treat_icky(<<"���5">>),"���5"}] ++
+ case has_links() of
+ true ->
+ [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}];
+ false ->
+ []
+ end ++
+ [{directory,treat_icky(<<"���subdir2">>),
+ [{regular,treat_icky(<<"���subfil2">>),"���subfil12"},
+ {regular,"���subfil3","���subfil13"}]},
+ {directory,"���subdir",
+ [{regular,"���subfil1","���subfil1"}]}].
+
+make_very_icky_dir(Mod) ->
+ rm_rf(Mod,"very_icky_dir"),
+ Icky=very_icky_dir(),
+ chk_cre_dir(Mod,[{directory,"very_icky_dir",linkify([],Icky)}]),
+ Mod:set_cwd("very_icky_dir"),
+ ok.
+
+very_icky_dir() ->
+ [{regular,"fil1","fil1"},
+ {regular,[956,965,963,954,959,49],"���2"}] ++
+ case has_links() of
+ true ->
+ [{regular,[956,965,963,954,959,50],"���2"},
+ {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}];
+ false ->
+ []
+ end ++
+ [{regular,treat_icky(<<"���5">>),"���5"}] ++
+ case has_links() of
+ true ->
+ [{symlink,treat_icky(<<"���6">>),treat_icky(<<"���5">>)}];
+ false ->
+ []
+ end ++
+ [{directory,treat_icky(<<"���subdir2">>),
+ [{regular,treat_icky(<<"���subfil2">>),"���subfil12"},
+ {regular,"���subfil3","���subfil13"}]},
+ {directory,[956,965,963,954,959]++"subdir1",
+ [{regular,[956,965,963,954,959]++"subfil1","���subfil1"}]}].
+
+%% Some OS'es simply do not allow non UTF8 filenames
+treat_icky(Bin) ->
+ case os:type() of
+ {unix,darwin} ->
+ binary_to_list(procentify(Bin));
+ {win32,_} ->
+ binary_to_list(Bin);
+ _ ->
+ Bin
+ end.
+
+% Handle windows having absolute soft link targets.
+fixlink({ok,Link}) ->
+ case os:type() of
+ {win32,_} ->
+ {ok,filename:basename(Link)};
+ _ ->
+ {ok,Link}
+ end;
+fixlink(X) ->
+ X.
+
+procentify(<<>>) ->
+ <<>>;
+procentify(<<X:8,Rst/binary>>) when X > 127 ->
+ T=procentify(Rst),
+ Y = list_to_binary([$%
+ | io_lib:format("~2.16B",[X])]),
+ <<Y/binary,T/binary>>;
+procentify(<<X:8,Rst/binary>>) ->
+ T=procentify(Rst),
+ <<X:8,T/binary>>.
+
+
+list([]) ->
+ [];
+list([{_,Name,_} | T]) ->
+ [Name | list(T)].
+
+
+get_data(FN,List) ->
+ case lists:keysearch(FN,2,List) of
+ {value,{regular,FN,C}} ->
+ C;
+ {value,{symlink,FN,NewFN}} ->
+ get_data(NewFN,List);
+ _->
+ []
+ end.
+
+
+convlist(L) ->
+ convlist(file:native_name_encoding(),L).
+convlist(latin1,[Bin|T]) when is_binary(Bin) ->
+ %erlang:display('Convert...'),
+ [binary_to_list(Bin)| convlist(latin1,T)];
+convlist(Any,[H|T]) ->
+ [H|convlist(Any,T)];
+convlist(_,[]) ->
+ [].
+
+conv(L) ->
+ NoUniMode = file:native_name_encoding() =:= latin1,
+ if
+ NoUniMode, is_binary(L) ->
+ binary_to_list(L);
+ true ->
+ L
+ end.
+
+
+rand_comp_decomp(Max) ->
+ N = random:uniform(Max),
+ L = [ rand_decomp() || _ <- lists:seq(1,N) ],
+ LC = [ A || {A,_} <- L],
+ LD = lists:flatten([B || {_,B} <- L]),
+ LB = unicode:characters_to_binary(LD,unicode,utf8),
+ {LC,LB}.
+
+rand_decomp() ->
+ BT = bigtup(),
+ SZ = tuple_size(BT),
+ element(random:uniform(SZ),BT).
+bigtup() ->
+ {{192,[65,768]},
+ {200,[69,768]},
+ {204,[73,768]},
+ {210,[79,768]},
+ {217,[85,768]},
+ {7808,[87,768]},
+ {7922,[89,768]},
+ {224,[97,768]},
+ {232,[101,768]},
+ {236,[105,768]},
+ {242,[111,768]},
+ {249,[117,768]},
+ {7809,[119,768]},
+ {7923,[121,768]},
+ {8173,[168,768]},
+ {7846,[65,770,768]},
+ {7872,[69,770,768]},
+ {7890,[79,770,768]},
+ {7847,[97,770,768]},
+ {7873,[101,770,768]},
+ {7891,[111,770,768]},
+ {7700,[69,772,768]},
+ {7760,[79,772,768]},
+ {7701,[101,772,768]},
+ {7761,[111,772,768]},
+ {7856,[65,774,768]},
+ {7857,[97,774,768]},
+ {475,[85,776,768]},
+ {476,[117,776,768]},
+ {8146,[953,776,768]},
+ {8162,[965,776,768]},
+ {8074,[913,837,787,768]},
+ {8090,[919,837,787,768]},
+ {8106,[937,837,787,768]},
+ {8066,[945,837,787,768]},
+ {8082,[951,837,787,768]},
+ {8098,[969,837,787,768]},
+ {7946,[913,787,768]},
+ {7962,[917,787,768]},
+ {7978,[919,787,768]},
+ {7994,[921,787,768]},
+ {8010,[927,787,768]},
+ {8042,[937,787,768]},
+ {7938,[945,787,768]},
+ {7954,[949,787,768]},
+ {7970,[951,787,768]},
+ {7986,[953,787,768]},
+ {8002,[959,787,768]},
+ {8018,[965,787,768]},
+ {8034,[969,787,768]},
+ {8075,[913,837,788,768]},
+ {8091,[919,837,788,768]},
+ {8107,[937,837,788,768]},
+ {8067,[945,837,788,768]},
+ {8083,[951,837,788,768]},
+ {8099,[969,837,788,768]},
+ {7947,[913,788,768]},
+ {7963,[917,788,768]},
+ {7979,[919,788,768]},
+ {7995,[921,788,768]},
+ {8011,[927,788,768]},
+ {8027,[933,788,768]},
+ {8043,[937,788,768]},
+ {7939,[945,788,768]},
+ {7955,[949,788,768]},
+ {7971,[951,788,768]},
+ {7987,[953,788,768]},
+ {8003,[959,788,768]},
+ {8019,[965,788,768]},
+ {8035,[969,788,768]},
+ {7900,[79,795,768]},
+ {7914,[85,795,768]},
+ {7901,[111,795,768]},
+ {7915,[117,795,768]},
+ {8114,[945,837,768]},
+ {8130,[951,837,768]},
+ {8178,[969,837,768]},
+ {8122,[913,768]},
+ {8136,[917,768]},
+ {8138,[919,768]},
+ {8154,[921,768]},
+ {8184,[927,768]},
+ {8170,[933,768]},
+ {8186,[937,768]},
+ {8048,[945,768]},
+ {8050,[949,768]},
+ {8052,[951,768]},
+ {8054,[953,768]},
+ {8056,[959,768]},
+ {8058,[965,768]},
+ {8060,[969,768]},
+ {8141,[8127,768]},
+ {8157,[8190,768]},
+ {193,[65,769]},
+ {262,[67,769]},
+ {201,[69,769]},
+ {500,[71,769]},
+ {205,[73,769]},
+ {7728,[75,769]},
+ {313,[76,769]},
+ {7742,[77,769]},
+ {323,[78,769]},
+ {211,[79,769]},
+ {7764,[80,769]},
+ {340,[82,769]},
+ {346,[83,769]},
+ {218,[85,769]},
+ {7810,[87,769]},
+ {221,[89,769]},
+ {377,[90,769]},
+ {225,[97,769]},
+ {263,[99,769]},
+ {233,[101,769]},
+ {501,[103,769]},
+ {237,[105,769]},
+ {7729,[107,769]},
+ {314,[108,769]},
+ {7743,[109,769]},
+ {324,[110,769]},
+ {243,[111,769]},
+ {7765,[112,769]},
+ {341,[114,769]},
+ {347,[115,769]},
+ {250,[117,769]},
+ {7811,[119,769]},
+ {253,[121,769]},
+ {378,[122,769]},
+ {8174,[168,769]},
+ {508,[198,769]},
+ {510,[216,769]},
+ {509,[230,769]},
+ {511,[248,769]},
+ {7844,[65,770,769]},
+ {7870,[69,770,769]},
+ {7888,[79,770,769]},
+ {7845,[97,770,769]},
+ {7871,[101,770,769]},
+ {7889,[111,770,769]},
+ {7756,[79,771,769]},
+ {7800,[85,771,769]},
+ {7757,[111,771,769]},
+ {7801,[117,771,769]},
+ {7702,[69,772,769]},
+ {7762,[79,772,769]},
+ {7703,[101,772,769]},
+ {7763,[111,772,769]},
+ {7854,[65,774,769]},
+ {7855,[97,774,769]},
+ {7726,[73,776,769]},
+ {471,[85,776,769]},
+ {7727,[105,776,769]},
+ {472,[117,776,769]},
+ {8147,[953,776,769]},
+ {8163,[965,776,769]},
+ {506,[65,778,769]},
+ {507,[97,778,769]},
+ {8076,[913,837,787,769]},
+ {8092,[919,837,787,769]},
+ {8108,[937,837,787,769]},
+ {8068,[945,837,787,769]},
+ {8084,[951,837,787,769]},
+ {8100,[969,837,787,769]},
+ {7948,[913,787,769]},
+ {7964,[917,787,769]},
+ {7980,[919,787,769]},
+ {7996,[921,787,769]},
+ {8012,[927,787,769]},
+ {8044,[937,787,769]},
+ {7940,[945,787,769]},
+ {7956,[949,787,769]},
+ {7972,[951,787,769]},
+ {7988,[953,787,769]},
+ {8004,[959,787,769]},
+ {8020,[965,787,769]},
+ {8036,[969,787,769]},
+ {8077,[913,837,788,769]},
+ {8093,[919,837,788,769]},
+ {8109,[937,837,788,769]},
+ {8069,[945,837,788,769]},
+ {8085,[951,837,788,769]},
+ {8101,[969,837,788,769]},
+ {7949,[913,788,769]},
+ {7965,[917,788,769]},
+ {7981,[919,788,769]},
+ {7997,[921,788,769]},
+ {8013,[927,788,769]},
+ {8029,[933,788,769]},
+ {8045,[937,788,769]},
+ {7941,[945,788,769]},
+ {7957,[949,788,769]},
+ {7973,[951,788,769]},
+ {7989,[953,788,769]},
+ {8005,[959,788,769]},
+ {8021,[965,788,769]},
+ {8037,[969,788,769]},
+ {7898,[79,795,769]},
+ {7912,[85,795,769]},
+ {7899,[111,795,769]},
+ {7913,[117,795,769]},
+ {7688,[67,807,769]},
+ {7689,[99,807,769]},
+ {8116,[945,837,769]},
+ {8132,[951,837,769]},
+ {8180,[959,837,769]},
+ {8123,[913,769]},
+ {8137,[917,769]},
+ {8139,[919,769]},
+ {8155,[921,769]},
+ {8185,[927,769]},
+ {8171,[933,769]},
+ {8187,[937,769]},
+ {8049,[945,769]},
+ {8051,[949,769]},
+ {8053,[951,769]},
+ {8055,[953,769]},
+ {8057,[959,769]},
+ {8059,[965,769]},
+ {8061,[969,769]},
+ {1027,[1043,769]},
+ {1036,[1050,769]},
+ {1107,[1075,769]},
+ {1116,[1082,769]},
+ {8142,[8127,769]},
+ {8158,[8190,769]},
+ {194,[65,770]},
+ {264,[67,770]},
+ {202,[69,770]},
+ {284,[71,770]},
+ {292,[72,770]},
+ {206,[73,770]},
+ {308,[74,770]},
+ {212,[79,770]},
+ {348,[83,770]},
+ {219,[85,770]},
+ {372,[87,770]},
+ {374,[89,770]},
+ {7824,[90,770]},
+ {226,[97,770]},
+ {265,[99,770]},
+ {234,[101,770]},
+ {285,[103,770]},
+ {293,[104,770]},
+ {238,[105,770]},
+ {309,[106,770]},
+ {244,[111,770]},
+ {349,[115,770]},
+ {251,[117,770]},
+ {373,[119,770]},
+ {375,[121,770]},
+ {7825,[122,770]},
+ {7852,[65,803,770]},
+ {7878,[69,803,770]},
+ {7896,[79,803,770]},
+ {7853,[97,803,770]},
+ {7879,[101,803,770]},
+ {7897,[111,803,770]},
+ {195,[65,771]},
+ {7868,[69,771]},
+ {296,[73,771]},
+ {209,[78,771]},
+ {213,[79,771]},
+ {360,[85,771]},
+ {7804,[86,771]},
+ {7928,[89,771]},
+ {227,[97,771]},
+ {7869,[101,771]},
+ {297,[105,771]},
+ {241,[110,771]},
+ {245,[111,771]},
+ {361,[117,771]},
+ {7805,[118,771]},
+ {7929,[121,771]},
+ {7850,[65,770,771]},
+ {7876,[69,770,771]},
+ {7894,[79,770,771]},
+ {7851,[97,770,771]},
+ {7877,[101,770,771]},
+ {7895,[111,770,771]},
+ {7860,[65,774,771]},
+ {7861,[97,774,771]},
+ {7904,[79,795,771]},
+ {7918,[85,795,771]},
+ {7905,[111,795,771]},
+ {7919,[117,795,771]},
+ {256,[65,772]},
+ {274,[69,772]},
+ {7712,[71,772]},
+ {298,[73,772]},
+ {332,[79,772]},
+ {362,[85,772]},
+ {257,[97,772]},
+ {275,[101,772]},
+ {7713,[103,772]},
+ {299,[105,772]},
+ {333,[111,772]},
+ {363,[117,772]},
+ {482,[198,772]},
+ {483,[230,772]},
+ {480,[65,775,772]},
+ {481,[97,775,772]},
+ {478,[65,776,772]},
+ {469,[85,776,772]},
+ {479,[97,776,772]},
+ {470,[117,776,772]},
+ {7736,[76,803,772]},
+ {7772,[82,803,772]},
+ {7737,[108,803,772]},
+ {7773,[114,803,772]},
+ {492,[79,808,772]},
+ {493,[111,808,772]},
+ {8121,[913,772]},
+ {8153,[921,772]},
+ {8169,[933,772]},
+ {8113,[945,772]},
+ {8145,[953,772]},
+ {8161,[965,772]},
+ {1250,[1048,772]},
+ {1262,[1059,772]},
+ {1251,[1080,772]},
+ {1263,[1091,772]},
+ {258,[65,774]},
+ {276,[69,774]},
+ {286,[71,774]},
+ {300,[73,774]},
+ {334,[79,774]},
+ {364,[85,774]},
+ {259,[97,774]},
+ {277,[101,774]},
+ {287,[103,774]},
+ {301,[105,774]},
+ {335,[111,774]},
+ {365,[117,774]},
+ {7862,[65,803,774]},
+ {7863,[97,803,774]},
+ {7708,[69,807,774]},
+ {7709,[101,807,774]},
+ {8120,[913,774]},
+ {8152,[921,774]},
+ {8168,[933,774]},
+ {8112,[945,774]},
+ {8144,[953,774]},
+ {8160,[965,774]},
+ {1232,[1040,774]},
+ {1238,[1045,774]},
+ {1217,[1046,774]},
+ {1049,[1048,774]},
+ {1038,[1059,774]},
+ {1233,[1072,774]},
+ {1239,[1077,774]},
+ {1218,[1078,774]},
+ {1081,[1080,774]},
+ {1118,[1091,774]},
+ {7682,[66,775]},
+ {266,[67,775]},
+ {7690,[68,775]},
+ {278,[69,775]},
+ {7710,[70,775]},
+ {288,[71,775]},
+ {7714,[72,775]},
+ {304,[73,775]},
+ {7744,[77,775]},
+ {7748,[78,775]},
+ {7766,[80,775]},
+ {7768,[82,775]},
+ {7776,[83,775]},
+ {7786,[84,775]},
+ {7814,[87,775]},
+ {7818,[88,775]},
+ {7822,[89,775]},
+ {379,[90,775]},
+ {7683,[98,775]},
+ {267,[99,775]},
+ {7691,[100,775]},
+ {279,[101,775]},
+ {7711,[102,775]},
+ {289,[103,775]},
+ {7715,[104,775]},
+ {7745,[109,775]},
+ {7749,[110,775]},
+ {7767,[112,775]},
+ {7769,[114,775]},
+ {7777,[115,775]},
+ {7787,[116,775]},
+ {7815,[119,775]},
+ {7819,[120,775]},
+ {7823,[121,775]},
+ {380,[122,775]},
+ {7835,[383,775]},
+ {7780,[83,769,775]},
+ {7781,[115,769,775]},
+ {784,[774,775]},
+ {7782,[83,780,775]},
+ {7783,[115,780,775]},
+ {7784,[83,803,775]},
+ {7785,[115,803,775]},
+ {196,[65,776]},
+ {203,[69,776]},
+ {7718,[72,776]},
+ {207,[73,776]},
+ {214,[79,776]},
+ {220,[85,776]},
+ {7812,[87,776]},
+ {7820,[88,776]},
+ {376,[89,776]},
+ {228,[97,776]},
+ {235,[101,776]},
+ {7719,[104,776]},
+ {239,[105,776]},
+ {246,[111,776]},
+ {7831,[116,776]},
+ {252,[117,776]},
+ {7813,[119,776]},
+ {7821,[120,776]},
+ {255,[121,776]},
+ {1242,[399,776]},
+ {1258,[415,776]},
+ {1243,[601,776]},
+ {1259,[629,776]},
+ {7758,[79,771,776]},
+ {7759,[111,771,776]},
+ {7802,[85,772,776]},
+ {7803,[117,772,776]},
+ {938,[921,776]},
+ {939,[933,776]},
+ {970,[953,776]},
+ {971,[965,776]},
+ {980,[978,776]},
+ {1031,[1030,776]},
+ {1234,[1040,776]},
+ {1025,[1045,776]},
+ {1244,[1046,776]},
+ {1246,[1047,776]},
+ {1252,[1048,776]},
+ {1254,[1054,776]},
+ {1264,[1059,776]},
+ {1268,[1063,776]},
+ {1272,[1067,776]},
+ {1235,[1072,776]},
+ {1105,[1077,776]},
+ {1245,[1078,776]},
+ {1247,[1079,776]},
+ {1253,[1080,776]},
+ {1255,[1086,776]},
+ {1265,[1091,776]},
+ {1269,[1095,776]},
+ {1273,[1099,776]},
+ {1111,[1110,776]},
+ {7842,[65,777]},
+ {7866,[69,777]},
+ {7880,[73,777]},
+ {7886,[79,777]},
+ {7910,[85,777]},
+ {7926,[89,777]},
+ {7843,[97,777]},
+ {7867,[101,777]},
+ {7881,[105,777]},
+ {7887,[111,777]},
+ {7911,[117,777]},
+ {7927,[121,777]},
+ {7848,[65,770,777]},
+ {7874,[69,770,777]},
+ {7892,[79,770,777]},
+ {7849,[97,770,777]},
+ {7875,[101,770,777]},
+ {7893,[111,770,777]},
+ {7858,[65,774,777]},
+ {7859,[97,774,777]},
+ {7902,[79,795,777]},
+ {7916,[85,795,777]},
+ {7903,[111,795,777]},
+ {7917,[117,795,777]},
+ {197,[65,778]},
+ {366,[85,778]},
+ {229,[97,778]},
+ {367,[117,778]},
+ {7832,[119,778]},
+ {7833,[121,778]},
+ {336,[79,779]},
+ {368,[85,779]},
+ {337,[111,779]},
+ {369,[117,779]},
+ {1266,[1059,779]},
+ {1267,[1091,779]},
+ {461,[65,780]},
+ {268,[67,780]},
+ {270,[68,780]},
+ {282,[69,780]},
+ {486,[71,780]},
+ {463,[73,780]},
+ {488,[75,780]},
+ {317,[76,780]},
+ {327,[78,780]},
+ {465,[79,780]},
+ {344,[82,780]},
+ {352,[83,780]},
+ {356,[84,780]},
+ {467,[85,780]},
+ {381,[90,780]},
+ {462,[97,780]},
+ {269,[99,780]},
+ {271,[100,780]},
+ {283,[101,780]},
+ {487,[103,780]},
+ {464,[105,780]},
+ {496,[106,780]},
+ {489,[107,780]},
+ {318,[108,780]},
+ {328,[110,780]},
+ {466,[111,780]},
+ {345,[114,780]},
+ {353,[115,780]},
+ {357,[116,780]},
+ {468,[117,780]},
+ {382,[122,780]},
+ {494,[439,780]},
+ {495,[658,780]},
+ {473,[85,776,780]},
+ {474,[117,776,780]},
+ {901,[168,781]},
+ {912,[953,776,781]},
+ {944,[965,776,781]},
+ {902,[913,781]},
+ {904,[917,781]},
+ {905,[919,781]},
+ {906,[921,781]},
+ {908,[927,781]},
+ {910,[933,781]},
+ {911,[937,781]},
+ {940,[945,781]},
+ {941,[949,781]},
+ {942,[951,781]},
+ {943,[953,781]},
+ {972,[959,781]},
+ {973,[965,781]},
+ {974,[969,781]},
+ {979,[978,781]},
+ {512,[65,783]},
+ {516,[69,783]},
+ {520,[73,783]},
+ {524,[79,783]},
+ {528,[82,783]},
+ {532,[85,783]},
+ {513,[97,783]},
+ {517,[101,783]},
+ {521,[105,783]},
+ {525,[111,783]},
+ {529,[114,783]},
+ {533,[117,783]},
+ {1142,[1140,783]},
+ {1143,[1141,783]},
+ {514,[65,785]},
+ {518,[69,785]},
+ {522,[73,785]},
+ {526,[79,785]},
+ {530,[82,785]},
+ {534,[85,785]},
+ {515,[97,785]},
+ {519,[101,785]},
+ {523,[105,785]},
+ {527,[111,785]},
+ {531,[114,785]},
+ {535,[117,785]},
+ {8072,[913,837,787]},
+ {8088,[919,837,787]},
+ {8104,[937,837,787]},
+ {8064,[945,837,787]},
+ {8080,[951,837,787]},
+ {8096,[969,837,787]},
+ {7944,[913,787]},
+ {7960,[917,787]},
+ {7976,[919,787]},
+ {7992,[921,787]},
+ {8008,[927,787]},
+ {8040,[937,787]},
+ {7936,[945,787]},
+ {7952,[949,787]},
+ {7968,[951,787]},
+ {7984,[953,787]},
+ {8000,[959,787]},
+ {8164,[961,787]},
+ {8016,[965,787]},
+ {8032,[969,787]},
+ {8073,[913,837,788]},
+ {8089,[919,837,788]},
+ {8105,[937,837,788]},
+ {8065,[945,837,788]},
+ {8081,[951,837,788]},
+ {8097,[969,837,788]},
+ {7945,[913,788]},
+ {7961,[917,788]},
+ {7977,[919,788]},
+ {7993,[921,788]},
+ {8009,[927,788]},
+ {8172,[929,788]},
+ {8025,[933,788]},
+ {8041,[937,788]},
+ {7937,[945,788]},
+ {7953,[949,788]},
+ {7969,[951,788]},
+ {7985,[953,788]},
+ {8001,[959,788]},
+ {8165,[961,788]},
+ {8017,[965,788]},
+ {8033,[969,788]},
+ {416,[79,795]},
+ {431,[85,795]},
+ {417,[111,795]},
+ {432,[117,795]},
+ {7840,[65,803]},
+ {7684,[66,803]},
+ {7692,[68,803]},
+ {7864,[69,803]},
+ {7716,[72,803]},
+ {7882,[73,803]},
+ {7730,[75,803]},
+ {7734,[76,803]},
+ {7746,[77,803]},
+ {7750,[78,803]},
+ {7884,[79,803]},
+ {7770,[82,803]},
+ {7778,[83,803]},
+ {7788,[84,803]},
+ {7908,[85,803]},
+ {7806,[86,803]},
+ {7816,[87,803]},
+ {7924,[89,803]},
+ {7826,[90,803]},
+ {7841,[97,803]},
+ {7685,[98,803]},
+ {7693,[100,803]},
+ {7865,[101,803]},
+ {7717,[104,803]},
+ {7883,[105,803]},
+ {7731,[107,803]},
+ {7735,[108,803]},
+ {7747,[109,803]},
+ {7751,[110,803]},
+ {7885,[111,803]},
+ {7771,[114,803]},
+ {7779,[115,803]},
+ {7789,[116,803]},
+ {7909,[117,803]},
+ {7807,[118,803]},
+ {7817,[119,803]},
+ {7925,[121,803]},
+ {7827,[122,803]},
+ {7906,[79,795,803]},
+ {7920,[85,795,803]},
+ {7907,[111,795,803]},
+ {7921,[117,795,803]},
+ {7794,[85,804]},
+ {7795,[117,804]},
+ {7680,[65,805]},
+ {7681,[97,805]},
+ {199,[67,807]},
+ {7696,[68,807]},
+ {290,[71,807]},
+ {7720,[72,807]},
+ {310,[75,807]},
+ {315,[76,807]},
+ {325,[78,807]},
+ {342,[82,807]},
+ {350,[83,807]},
+ {354,[84,807]},
+ {231,[99,807]},
+ {7697,[100,807]},
+ {291,[103,807]},
+ {7721,[104,807]},
+ {311,[107,807]},
+ {316,[108,807]},
+ {326,[110,807]},
+ {343,[114,807]},
+ {351,[115,807]},
+ {355,[116,807]},
+ {260,[65,808]},
+ {280,[69,808]},
+ {302,[73,808]},
+ {490,[79,808]},
+ {370,[85,808]},
+ {261,[97,808]},
+ {281,[101,808]},
+ {303,[105,808]},
+ {491,[111,808]},
+ {371,[117,808]},
+ {7698,[68,813]},
+ {7704,[69,813]},
+ {7740,[76,813]},
+ {7754,[78,813]},
+ {7792,[84,813]},
+ {7798,[85,813]},
+ {7699,[100,813]},
+ {7705,[101,813]},
+ {7741,[108,813]},
+ {7755,[110,813]},
+ {7793,[116,813]},
+ {7799,[117,813]},
+ {7722,[72,814]},
+ {7723,[104,814]},
+ {7706,[69,816]},
+ {7724,[73,816]},
+ {7796,[85,816]},
+ {7707,[101,816]},
+ {7725,[105,816]},
+ {7797,[117,816]},
+ {7686,[66,817]},
+ {7694,[68,817]},
+ {7732,[75,817]},
+ {7738,[76,817]},
+ {7752,[78,817]},
+ {7774,[82,817]},
+ {7790,[84,817]},
+ {7828,[90,817]},
+ {7687,[98,817]},
+ {7695,[100,817]},
+ {7830,[104,817]},
+ {7733,[107,817]},
+ {7739,[108,817]},
+ {7753,[110,817]},
+ {7775,[114,817]},
+ {7791,[116,817]},
+ {7829,[122,817]},
+ {8129,[168,834]},
+ {8151,[953,776,834]},
+ {8167,[965,776,834]},
+ {8078,[913,837,787,834]},
+ {8094,[919,837,787,834]},
+ {8110,[937,837,787,834]},
+ {8070,[945,837,787,834]},
+ {8086,[951,837,787,834]},
+ {8102,[969,837,787,834]},
+ {7950,[913,787,834]},
+ {7982,[919,787,834]},
+ {7998,[921,787,834]},
+ {8046,[937,787,834]},
+ {7942,[945,787,834]},
+ {7974,[951,787,834]},
+ {7990,[953,787,834]},
+ {8022,[965,787,834]},
+ {8038,[969,787,834]},
+ {8079,[913,837,788,834]},
+ {8095,[919,837,788,834]},
+ {8111,[937,837,788,834]},
+ {8071,[945,837,788,834]},
+ {8087,[951,837,788,834]},
+ {8103,[969,837,788,834]},
+ {7951,[913,788,834]},
+ {7983,[919,788,834]},
+ {7999,[921,788,834]},
+ {8031,[933,788,834]},
+ {8047,[937,788,834]},
+ {7943,[945,788,834]},
+ {7975,[951,788,834]},
+ {7991,[953,788,834]},
+ {8023,[965,788,834]},
+ {8039,[969,788,834]},
+ {8119,[945,837,834]},
+ {8135,[951,837,834]},
+ {8183,[969,837,834]},
+ {8118,[945,834]},
+ {8134,[951,834]},
+ {8150,[953,834]},
+ {8166,[965,834]},
+ {8182,[969,834]},
+ {8143,[8127,834]},
+ {8159,[8190,834]},
+ {8124,[913,837]},
+ {8140,[919,837]},
+ {8188,[937,837]},
+ {8115,[945,837]},
+ {8131,[951,837]},
+ {8179,[969,837]},
+ {64302,[1488,1463]},
+ {64287,[1522,1463]},
+ {64303,[1488,1464]},
+ {64331,[1493,1465]},
+ {64304,[1488,1468]},
+ {64305,[1489,1468]},
+ {64306,[1490,1468]},
+ {64307,[1491,1468]},
+ {64308,[1492,1468]},
+ {64309,[1493,1468]},
+ {64310,[1494,1468]},
+ {64312,[1496,1468]},
+ {64313,[1497,1468]},
+ {64314,[1498,1468]},
+ {64315,[1499,1468]},
+ {64316,[1500,1468]},
+ {64318,[1502,1468]},
+ {64320,[1504,1468]},
+ {64321,[1505,1468]},
+ {64323,[1507,1468]},
+ {64324,[1508,1468]},
+ {64326,[1510,1468]},
+ {64327,[1511,1468]},
+ {64328,[1512,1468]},
+ {64329,[1513,1468]},
+ {64330,[1514,1468]},
+ {64332,[1489,1471]},
+ {64333,[1499,1471]},
+ {64334,[1508,1471]},
+ {64300,[1513,1468,1473]},
+ {64298,[1513,1473]},
+ {64301,[1513,1468,1474]},
+ {64299,[1513,1474]},
+ {2392,[2325,2364]},
+ {2393,[2326,2364]},
+ {2394,[2327,2364]},
+ {2395,[2332,2364]},
+ {2396,[2337,2364]},
+ {2397,[2338,2364]},
+ {2345,[2344,2364]},
+ {2398,[2347,2364]},
+ {2399,[2351,2364]},
+ {2353,[2352,2364]},
+ {2356,[2355,2364]},
+ {2524,[2465,2492]},
+ {2525,[2466,2492]},
+ {2480,[2476,2492]},
+ {2527,[2479,2492]},
+ {2507,[2503,2494]},
+ {2508,[2503,2519]},
+ {2649,[2582,2620]},
+ {2650,[2583,2620]},
+ {2651,[2588,2620]},
+ {2652,[2593,2620]},
+ {2654,[2603,2620]},
+ {2908,[2849,2876]},
+ {2909,[2850,2876]},
+ {2911,[2863,2876]},
+ {2891,[2887,2878]},
+ {2888,[2887,2902]},
+ {2892,[2887,2903]},
+ {3018,[3014,3006]},
+ {3019,[3015,3006]},
+ {2964,[2962,3031]},
+ {3020,[3014,3031]},
+ {3144,[3142,3158]},
+ {3274,[3270,3266]},
+ {3264,[3263,3285]},
+ {3275,[3270,3266,3285]},
+ {3271,[3270,3285]},
+ {3272,[3270,3286]},
+ {3402,[3398,3390]},
+ {3403,[3399,3390]},
+ {3404,[3398,3415]},
+ {3635,[3661,3634]},
+ {3763,[3789,3762]},
+ {3955,[3954,3953]},
+ {3957,[3956,3953]},
+ {3959,[4018,3968,3953]},
+ {3961,[4019,3968,3953]},
+ {3958,[4018,3968]},
+ {3960,[4019,3968]},
+ {3945,[3904,4021]},
+ {4025,[3984,4021]},
+ {3907,[3906,4023]},
+ {3917,[3916,4023]},
+ {3922,[3921,4023]},
+ {3927,[3926,4023]},
+ {3932,[3931,4023]},
+ {3987,[3986,4023]},
+ {3997,[3996,4023]},
+ {4002,[4001,4023]},
+ {4007,[4006,4023]},
+ {4012,[4011,4023]},
+ {12436,[12358,12441]},
+ {12364,[12363,12441]},
+ {12366,[12365,12441]},
+ {12368,[12367,12441]},
+ {12370,[12369,12441]},
+ {12372,[12371,12441]},
+ {12374,[12373,12441]},
+ {12376,[12375,12441]},
+ {12378,[12377,12441]},
+ {12380,[12379,12441]},
+ {12382,[12381,12441]},
+ {12384,[12383,12441]},
+ {12386,[12385,12441]},
+ {12389,[12388,12441]},
+ {12391,[12390,12441]},
+ {12393,[12392,12441]},
+ {12400,[12399,12441]},
+ {12403,[12402,12441]},
+ {12406,[12405,12441]},
+ {12409,[12408,12441]},
+ {12412,[12411,12441]},
+ {12446,[12445,12441]},
+ {12532,[12454,12441]},
+ {12460,[12459,12441]},
+ {12462,[12461,12441]},
+ {12464,[12463,12441]},
+ {12466,[12465,12441]},
+ {12468,[12467,12441]},
+ {12470,[12469,12441]},
+ {12472,[12471,12441]},
+ {12474,[12473,12441]},
+ {12476,[12475,12441]},
+ {12478,[12477,12441]},
+ {12480,[12479,12441]},
+ {12482,[12481,12441]},
+ {12485,[12484,12441]},
+ {12487,[12486,12441]},
+ {12489,[12488,12441]},
+ {12496,[12495,12441]},
+ {12499,[12498,12441]},
+ {12502,[12501,12441]},
+ {12505,[12504,12441]},
+ {12508,[12507,12441]},
+ {12535,[12527,12441]},
+ {12536,[12528,12441]},
+ {12537,[12529,12441]},
+ {12538,[12530,12441]},
+ {12542,[12541,12441]},
+ {12401,[12399,12442]},
+ {12404,[12402,12442]},
+ {12407,[12405,12442]},
+ {12410,[12408,12442]},
+ {12413,[12411,12442]},
+ {12497,[12495,12442]},
+ {12500,[12498,12442]},
+ {12503,[12501,12442]},
+ {12506,[12504,12442]},
+ {12509,[12507,12442]}}.
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index fad8c7398b..03e734445c 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,27 +18,67 @@
%%
-module(gen_sctp_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/inet_sctp.hrl").
%%-compile(export_all).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
- basic/1,api_open_close/1,api_listen/1,api_connect_init/1,
- xfer_min/1,xfer_active/1]).
+-export([all/0, suite/0,groups/0,
+ init_per_suite/1,end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
+-export(
+ [basic/1,
+ api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
+ xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic, api_open_close, api_listen, api_connect_init,
+ api_opts, xfer_min, xfer_active, def_sndrcvinfo,
+ implicit_inet6].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ try gen_sctp:open() of
+ {ok,Socket} ->
+ gen_sctp:close(Socket),
+ [];
+ _ ->
+ []
+ catch
+ error:badarg ->
+ {skip,"SCTP not supported on this machine"};
+ _:_ ->
+ Config
+ end.
+
+end_per_suite(_Conifig) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [basic,api_open_close,api_listen,api_connect_init,xfer_min,xfer_active].
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(15)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
+-define(LOGVAR(Var), begin io:format(??Var" = ~p~n", [Var]) end).
+
+
+
basic(doc) ->
"Hello world";
basic(suite) ->
@@ -214,12 +254,17 @@ xfer_active(Config) when is_list(Config) ->
end,
?line ok = gen_sctp:close(Sb),
?line receive
- {sctp,Sa,Loopback,Pb,
- {[],
- #sctp_assoc_change{state=comm_lost,
- assoc_id=SaAssocId}}} -> ok
- after 17 -> ok %% On Solaris this does not arrive
- end,
+ {sctp,Sa,Loopback,Pb,
+ {[],
+ #sctp_assoc_change{state=comm_lost,
+ assoc_id=SaAssocId}}} -> ok
+ after Timeout ->
+ ?line test_server:fail({unexpected,flush()})
+ end,
+ ?line receive
+ {sctp_error,Sa,enotconn} -> ok % Solaris
+ after 17 -> ok %% Only happens on Solaris
+ end,
?line ok = gen_sctp:close(Sa),
%%
?line receive
@@ -228,6 +273,148 @@ xfer_active(Config) when is_list(Config) ->
end,
ok.
+def_sndrcvinfo(doc) ->
+ "Test that #sctp_sndrcvinfo{} parameters set on a socket "
+ "are used by gen_sctp:send/4";
+def_sndrcvinfo(suite) ->
+ [];
+def_sndrcvinfo(Config) when is_list(Config) ->
+ ?line Loopback = {127,0,0,1},
+ ?line Data = <<"What goes up, must come down.">>,
+ %%
+ ?line S1 =
+ ok(gen_sctp:open(
+ 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
+ ?LOGVAR(S1),
+ ?line P1 =
+ ok(inet:port(S1)),
+ ?LOGVAR(P1),
+ ?line #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} =
+ getopt(S1, sctp_default_send_param),
+ ?line ok =
+ gen_sctp:listen(S1, true),
+ %%
+ ?line S2 =
+ ok(gen_sctp:open()),
+ ?LOGVAR(S2),
+ ?line P2 =
+ ok(inet:port(S2)),
+ ?LOGVAR(P2),
+ ?line #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} =
+ getopt(S2, sctp_default_send_param),
+ %%
+ ?line #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=S2AssocId} = S2AssocChange =
+ ok(gen_sctp:connect(S2, Loopback, P1, [])),
+ ?LOGVAR(S2AssocChange),
+ ?line case ok(gen_sctp:recv(S1)) of
+ {Loopback, P2,[],
+ #sctp_assoc_change{
+ state=comm_up,
+ error=0,
+ assoc_id=S1AssocId}} ->
+ ?LOGVAR(S1AssocId)
+ end,
+ ?line #sctp_sndrcvinfo{
+ ppid=17, context=0, timetolive=0, assoc_id=S1AssocId} =
+ getopt(
+ S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
+ ?line #sctp_sndrcvinfo{
+ ppid=0, context=0, timetolive=0, assoc_id=S2AssocId} =
+ getopt(
+ S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}),
+ %%
+ ?line ok =
+ gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>),
+ ?line case ok(gen_sctp:recv(S2)) of
+ {Loopback,P1,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
+ <<"1: ",Data/binary>>} -> ok
+ end,
+ %%
+ ?line ok =
+ setopt(
+ S1, sctp_default_send_param, #sctp_sndrcvinfo{ppid=18}),
+ ?line ok =
+ setopt(
+ S1, sctp_default_send_param,
+ #sctp_sndrcvinfo{ppid=19, assoc_id=S1AssocId}),
+ ?line #sctp_sndrcvinfo{
+ ppid=18, context=0, timetolive=0, assoc_id=0} =
+ getopt(S1, sctp_default_send_param),
+ ?line #sctp_sndrcvinfo{
+ ppid=19, context=0, timetolive=0, assoc_id=S1AssocId} =
+ getopt(
+ S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
+ %%
+ ?line ok =
+ gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>),
+ ?line case ok(gen_sctp:recv(S2)) of
+ {Loopback,P1,
+ [#sctp_sndrcvinfo{
+ stream=0, ppid=19, context=0, assoc_id=S2AssocId}],
+ <<"2: ",Data/binary>>} -> ok
+ end,
+ ?line ok =
+ gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>),
+ ?line case ok(gen_sctp:recv(S1)) of
+ {Loopback,P2,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
+ <<"3: ",Data/binary>>} -> ok;
+ {Loopback,P2,[],
+ #sctp_paddr_change{
+ addr={Loopback,_}, state=addr_available,
+ error=0, assoc_id=S1AssocId}} ->
+ ?line case ok(gen_sctp:recv(S1)) of
+ {Loopback,P2,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=0, context=0,
+ assoc_id=S1AssocId}],
+ <<"3: ",Data/binary>>} -> ok
+ end
+ end,
+ ?line ok =
+ gen_sctp:send(
+ S2,
+ #sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId},
+ <<"4: ",Data/binary>>),
+ ?line case ok(gen_sctp:recv(S1)) of
+ {Loopback,P2,
+ [#sctp_sndrcvinfo{
+ stream=0, ppid=20, context=0, assoc_id=S1AssocId}],
+ <<"4: ",Data/binary>>} -> ok
+ end,
+ %%
+ ?line ok =
+ gen_sctp:close(S1),
+ ?line ok =
+ gen_sctp:close(S2),
+ ?line receive
+ Msg ->
+ test_server:fail({received,Msg})
+ after 17 -> ok
+ end,
+ ok.
+
+getopt(S, Opt) ->
+ {ok,[{Opt,Val}]} = inet:getopts(S, [Opt]),
+ Val.
+
+getopt(S, Opt, Param) ->
+ {ok,[{Opt,Val}]} = inet:getopts(S, [{Opt,Param}]),
+ Val.
+
+setopt(S, Opt, Val) ->
+ inet:setopts(S, [{Opt,Val}]).
+
+ok({ok,X}) ->
+ io:format("OK: ~p~n", [X]),
+ X.
+
flush() ->
receive
Msg ->
@@ -382,3 +569,72 @@ api_connect_init(Config) when is_list(Config) ->
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
ok.
+
+api_opts(doc) ->
+ "Test socket options";
+api_opts(suite) ->
+ [];
+api_opts(Config) when is_list(Config) ->
+ ?line {ok,S} = gen_sctp:open(0),
+ ?line OSType = os:type(),
+ ?line case {inet:setopts(S, [{linger,{true,2}}]),OSType} of
+ {ok,_} ->
+ ok;
+ {{error,einval},{unix,sunos}} ->
+ ok
+ end.
+
+implicit_inet6(Config) when is_list(Config) ->
+ ?line Hostname = ok(inet:gethostname()),
+ ?line
+ case gen_sctp:open(0, [inet6]) of
+ {ok,S1} ->
+ ?line
+ case inet:getaddr(Hostname, inet6) of
+ {ok,Host} ->
+ ?line Loopback = {0,0,0,0,0,0,0,1},
+ ?line io:format("~s ~p~n", ["Loopback",Loopback]),
+ ?line implicit_inet6(S1, Loopback),
+ ?line ok = gen_sctp:close(S1),
+ %%
+ ?line Localhost =
+ ok(inet:getaddr("localhost", inet6)),
+ ?line io:format("~s ~p~n", ["localhost",Localhost]),
+ ?line S2 =
+ ok(gen_sctp:open(0, [{ip,Localhost}])),
+ ?line implicit_inet6(S2, Localhost),
+ ?line ok = gen_sctp:close(S2),
+ %%
+ ?line io:format("~s ~p~n", [Hostname,Host]),
+ ?line S3 =
+ ok(gen_sctp:open(0, [{ifaddr,Host}])),
+ ?line implicit_inet6(S3, Host),
+ ?line ok = gen_sctp:close(S1);
+ {error,eafnosupport} ->
+ ?line ok = gen_sctp:close(S1),
+ {skip,"Can not look up IPv6 address"}
+ end;
+ _ ->
+ {skip,"IPv6 not supported"}
+ end.
+
+implicit_inet6(S1, Addr) ->
+ ?line ok = gen_sctp:listen(S1, true),
+ ?line P1 = ok(inet:port(S1)),
+ ?line S2 = ok(gen_sctp:open(0, [inet6])),
+ ?line P2 = ok(inet:port(S2)),
+ ?line #sctp_assoc_change{state=comm_up} =
+ ok(gen_sctp:connect(S2, Addr, P1, [])),
+ ?line case ok(gen_sctp:recv(S1)) of
+ {Addr,P2,[],#sctp_assoc_change{state=comm_up}} ->
+ ok
+ end,
+ ?line case ok(inet:sockname(S1)) of
+ {Addr,P1} -> ok;
+ {{0,0,0,0,0,0,0,0},P1} -> ok
+ end,
+ ?line case ok(inet:sockname(S2)) of
+ {Addr,P2} -> ok;
+ {{0,0,0,0,0,0,0,0},P2} -> ok
+ end,
+ ?line ok = gen_sctp:close(S2).
diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 11d19aaa82..fd4685cdad 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,29 +22,52 @@
%% are not tested here, because they are tested indirectly in this and
%% and other test suites.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/inet.hrl").
--export([all/1, init_per_testcase/2, fin_per_testcase/2,
- t_accept/1, t_connect_timeout/1, t_accept_timeout/1,
- t_connect/1, t_connect_bad/1,
- t_recv/1, t_recv_timeout/1, t_recv_eof/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+ t_connect_timeout/1, t_accept_timeout/1,
+ t_connect_bad/1,
+ t_recv_timeout/1, t_recv_eof/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
- t_fdopen/1]).
+ t_fdopen/1, t_implicit_inet6/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, t_accept}, {group, t_connect}, {group, t_recv},
+ t_shutdown_write, t_shutdown_both, t_shutdown_error,
+ t_fdopen, t_implicit_inet6].
+
+groups() ->
+ [{t_accept, [], [t_accept_timeout]},
+ {t_connect, [], [t_connect_timeout, t_connect_bad]},
+ {t_recv, [], [t_recv_timeout, t_recv_eof]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> [t_accept, t_connect, t_recv, t_shutdown_write,
- t_shutdown_both, t_shutdown_error, t_fdopen].
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(60)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
%%% gen_tcp:accept/1,2
-t_accept(suite) -> [t_accept_timeout].
t_accept_timeout(doc) -> "Test that gen_tcp:accept/2 (with timeout) works.";
t_accept_timeout(suite) -> [];
@@ -54,7 +77,6 @@ t_accept_timeout(Config) when is_list(Config) ->
%%% gen_tcp:connect/X
-t_connect(suite) -> [t_connect_timeout, t_connect_bad].
t_connect_timeout(doc) -> "Test that gen_tcp:connect/4 (with timeout) works.";
t_connect_timeout(Config) when is_list(Config) ->
@@ -83,7 +105,6 @@ t_connect_bad(Config) when is_list(Config) ->
%%% gen_tcp:recv/X
-t_recv(suite) -> [t_recv_timeout, t_recv_eof].
t_recv_timeout(doc) -> "Test that gen_tcp:recv/3 (with timeout works).";
t_recv_timeout(suite) -> [];
@@ -156,6 +177,58 @@ t_fdopen(Config) when is_list(Config) ->
ok.
+%%% implicit inet6 option to api functions
+
+t_implicit_inet6(Config) when is_list(Config) ->
+ ?line Host = ok(inet:gethostname()),
+ ?line
+ case inet:getaddr(Host, inet6) of
+ {ok,Addr} ->
+ ?line t_implicit_inet6(Host, Addr);
+ {error,Reason} ->
+ {skip,
+ "Can not look up IPv6 address: "
+ ++atom_to_list(Reason)}
+ end.
+
+t_implicit_inet6(Host, Addr) ->
+ ?line
+ case gen_tcp:listen(0, [inet6]) of
+ {ok,S1} ->
+ ?line Loopback = {0,0,0,0,0,0,0,1},
+ ?line io:format("~s ~p~n", ["::1",Loopback]),
+ ?line implicit_inet6(S1, Loopback),
+ ?line ok = gen_tcp:close(S1),
+ %%
+ ?line Localhost = "localhost",
+ ?line Localaddr = ok(inet:getaddr(Localhost, inet6)),
+ ?line io:format("~s ~p~n", [Localhost,Localaddr]),
+ ?line S2 = ok(gen_tcp:listen(0, [{ip,Localaddr}])),
+ ?line implicit_inet6(S2, Localaddr),
+ ?line ok = gen_tcp:close(S2),
+ %%
+ ?line io:format("~s ~p~n", [Host,Addr]),
+ ?line S3 = ok(gen_tcp:listen(0, [{ifaddr,Addr}])),
+ ?line implicit_inet6(S3, Addr),
+ ?line ok = gen_tcp:close(S3);
+ {error,_} ->
+ {skip,"IPv6 not supported"}
+ end.
+
+implicit_inet6(S, Addr) ->
+ ?line P = ok(inet:port(S)),
+ ?line S2 = ok(gen_tcp:connect(Addr, P, [])),
+ ?line P2 = ok(inet:port(S2)),
+ ?line S1 = ok(gen_tcp:accept(S)),
+ ?line P1 = P = ok(inet:port(S1)),
+ ?line {Addr,P2} = ok(inet:peername(S1)),
+ ?line {Addr,P1} = ok(inet:peername(S2)),
+ ?line {Addr,P1} = ok(inet:sockname(S1)),
+ ?line {Addr,P2} = ok(inet:sockname(S2)),
+ ?line ok = gen_tcp:close(S2),
+ ?line ok = gen_tcp:close(S1).
+
+
%%% Utilities
@@ -217,3 +290,5 @@ unused_ip(A, B, C, D) ->
{ok, _} -> unused_ip(A, B, C, D+1);
{error, _} -> {ok, {A, B, C, D}}
end.
+
+ok({ok,V}) -> V.
diff --git a/lib/kernel/test/gen_tcp_echo_SUITE.erl b/lib/kernel/test/gen_tcp_echo_SUITE.erl
index a2e09877af..fffaaf4c45 100644
--- a/lib/kernel/test/gen_tcp_echo_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_echo_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,11 +18,13 @@
%%
-module(gen_tcp_echo_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%-compile(export_all).
--export([all/1, init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
active_echo/1, passive_echo/1, active_once_echo/1,
slow_active_echo/1, slow_passive_echo/1,
limit_active_echo/1, limit_passive_echo/1,
@@ -31,16 +33,34 @@
-define(TPKT_VRSN, 3).
-define(LINE_LENGTH, 1023). % (default value of gen_tcp option 'recbuf') - 1
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[active_echo, passive_echo, active_once_echo,
- slow_active_echo, slow_passive_echo,
- limit_active_echo, limit_passive_echo,
- large_limit_active_echo, large_limit_passive_echo].
+ slow_active_echo, slow_passive_echo, limit_active_echo,
+ limit_passive_echo, large_limit_active_echo,
+ large_limit_passive_echo].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:minutes(5)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index 5d726a3b1b..b1ef8826d5 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -1,31 +1,33 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_tcp_misc_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%-compile(export_all).
--export([all/1, controlling_process/1, no_accept/1, close_with_pending_output/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ controlling_process/1, no_accept/1, close_with_pending_output/1,
data_before_close/1, iter_max_socks/1, get_status/1,
passive_sockets/1, accept_closed_by_other_process/1,
- init_per_testcase/2, fin_per_testcase/2,
+ init_per_testcase/2, end_per_testcase/2,
otp_3924/1, otp_3924_sender/4, closed_socket/1,
shutdown_active/1, shutdown_passive/1, shutdown_pending/1,
default_options/1, http_bad_packet/1,
@@ -34,39 +36,60 @@
partial_recv_and_close_2/1,partial_recv_and_close_3/1,so_priority/1,
% Accept tests
primitive_accept/1,multi_accept_close_listen/1,accept_timeout/1,
- accept_timeouts_in_order/1,accept_timeouts_in_order2/1,accept_timeouts_in_order3/1,
- accept_timeouts_mixed/1,
+ accept_timeouts_in_order/1,accept_timeouts_in_order2/1,
+ accept_timeouts_in_order3/1,accept_timeouts_mixed/1,
killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1,
- several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, otp_7731/1,
- zombie_sockets/1, otp_7816/1, otp_8102/1]).
+ several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, send_timeout_active/1,
+ otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1]).
%% Internal exports.
--export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1, otp_7731_server/1, zombie_server/2]).
+-export([sender/3, not_owner/1, passive_sockets_server/2, priority_server/1,
+ otp_7731_server/1, zombie_server/2]).
init_per_testcase(_Func, Config) when is_list(Config) ->
Dog = test_server:timetrap(test_server:seconds(240)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[controlling_process, no_accept,
- close_with_pending_output,
- data_before_close, iter_max_socks, passive_sockets,
+ close_with_pending_output, data_before_close,
+ iter_max_socks, passive_sockets,
accept_closed_by_other_process, otp_3924, closed_socket,
shutdown_active, shutdown_passive, shutdown_pending,
- default_options, http_bad_packet,
- busy_send, busy_disconnect_passive, busy_disconnect_active,
- fill_sendq, partial_recv_and_close,
- partial_recv_and_close_2, partial_recv_and_close_3, so_priority,
- primitive_accept,multi_accept_close_listen,accept_timeout,
- accept_timeouts_in_order,accept_timeouts_in_order2,accept_timeouts_in_order3,
- accept_timeouts_mixed,
- killing_acceptor,killing_multi_acceptors,killing_multi_acceptors2,
- several_accepts_in_one_go, active_once_closed, send_timeout, otp_7731,
+ default_options, http_bad_packet, busy_send,
+ busy_disconnect_passive, busy_disconnect_active,
+ fill_sendq, partial_recv_and_close,
+ partial_recv_and_close_2, partial_recv_and_close_3,
+ so_priority, primitive_accept,
+ multi_accept_close_listen, accept_timeout,
+ accept_timeouts_in_order, accept_timeouts_in_order2,
+ accept_timeouts_in_order3, accept_timeouts_mixed,
+ killing_acceptor, killing_multi_acceptors,
+ killing_multi_acceptors2, several_accepts_in_one_go,
+ active_once_closed, send_timeout, send_timeout_active, otp_7731,
zombie_sockets, otp_7816, otp_8102].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
default_options(doc) ->
["Tests kernel application variables inet_default_listen_options and "
@@ -957,12 +980,11 @@ http_bad_packet(Config) when is_list(Config) ->
http_worker(S) ->
case gen_tcp:recv(S, 0, 30000) of
+ {ok,{http_error,Error}} ->
+ io:format("Http error: ~s\n", [Error]);
{ok,Data} ->
io:format("Data: ~p\n", [Data]),
- http_worker(S);
- {error,Rsn} ->
- io:format("Error: ~p\n", [Rsn]),
- ok
+ http_worker(S)
end.
http_bad_client(Port) ->
@@ -1935,6 +1957,60 @@ send_timeout(Config) when is_list(Config) ->
ParaFun(false),
ParaFun(true),
ok.
+mad_sender(S) ->
+ {_, _, USec} = now(),
+ case gen_tcp:send(S, integer_to_list(USec)) of
+ ok ->
+ mad_sender(S);
+ Err ->
+ Err
+ end.
+
+
+flush() ->
+ receive
+ _X ->
+ %erlang:display(_X),
+ flush()
+ after 0 ->
+ ok
+ end.
+
+send_timeout_active(suite) ->
+ [];
+send_timeout_active(doc) ->
+ ["Test the send_timeout socket option for active sockets"];
+send_timeout_active(Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:seconds(20)),
+ %% Basic
+ BasicFun =
+ fun(AutoClose) ->
+ ?line {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose),
+ inet:setopts(A, [{active, once}]),
+ ?line Mad = spawn_link(RNode,fun() -> mad_sender(C) end),
+ ?line {error,timeout} =
+ Loop(fun() ->
+ receive
+ {tcp, Sock, _Data} ->
+ inet:setopts(A, [{active, once}]),
+ Res = gen_tcp:send(A,lists:duplicate(1000, $a)),
+ %erlang:display(Res),
+ Res;
+ Err ->
+ io:format("sock closed: ~p~n", [Err]),
+ Err
+ end
+ end),
+ unlink(Mad),
+ exit(Mad,kill),
+ ?line test_server:stop_node(RNode)
+ end,
+ BasicFun(false),
+ flush(),
+ BasicFun(true),
+ flush(),
+ test_server:timetrap_cancel(Dog),
+ ok.
after_send_timeout(AutoClose) ->
case AutoClose of
@@ -2017,35 +2093,35 @@ setup_closed_ao() ->
{Loop,A}.
setup_timeout_sink(Timeout, AutoClose) ->
- Dir = filename:dirname(code:which(?MODULE)),
- {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ ?line Dir = filename:dirname(code:which(?MODULE)),
+ ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
[{args,"-pa " ++ Dir}]),
- Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
- {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
+ ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ ?line {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2},
{send_timeout,Timeout},
{send_timeout_close,AutoClose}]),
- Fun = fun(F) ->
+ ?line Fun = fun(F) ->
receive
{From,X} when is_function(X) ->
From ! {self(),X()}, F(F);
die -> ok
end
end,
- Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
- {ok, Port} = inet:port(L),
- Remote = fun(Fu) ->
+ ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ ?line {ok, Port} = inet:port(L),
+ ?line Remote = fun(Fu) ->
Pid ! {self(), Fu},
receive {Pid,X} -> X
end
end,
- {ok, C} = Remote(fun() ->
+ ?line {ok, C} = Remote(fun() ->
gen_tcp:connect(Host,Port,
[{active,false},{packet,2}])
end),
- {ok,A} = gen_tcp:accept(L),
- gen_tcp:send(A,"Hello"),
- {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
- Loop2 = fun(_,_,0) ->
+ ?line {ok,A} = gen_tcp:accept(L),
+ ?line gen_tcp:send(A,"Hello"),
+ ?line {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ ?line Loop2 = fun(_,_,0) ->
{failure, timeout};
(L2,F2,N) ->
Ret = F2(),
@@ -2056,9 +2132,53 @@ setup_timeout_sink(Timeout, AutoClose) ->
Other -> Other
end
end,
- Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
{Loop,A,R}.
-
+
+setup_active_timeout_sink(Timeout, AutoClose) ->
+ ?line Dir = filename:dirname(code:which(?MODULE)),
+ ?line {ok,R} = test_server:start_node(test_default_options_slave,slave,
+ [{args,"-pa " ++ Dir}]),
+ ?line Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))),
+ ?line {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true},
+ {send_timeout,Timeout},
+ {send_timeout_close,AutoClose}]),
+ ?line Fun = fun(F) ->
+ receive
+ {From,X} when is_function(X) ->
+ From ! {self(),X()}, F(F);
+ die -> ok
+ end
+ end,
+ ?line Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]),
+ ?line {ok, Port} = inet:port(L),
+ ?line Remote = fun(Fu) ->
+ Pid ! {self(), Fu},
+ receive {Pid,X} -> X
+ end
+ end,
+ ?line {ok, C} = Remote(fun() ->
+ gen_tcp:connect(Host,Port,
+ [{active,false}])
+ end),
+ ?line {ok,A} = gen_tcp:accept(L),
+ ?line gen_tcp:send(A,"Hello"),
+ ?line {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end),
+ ?line Loop2 = fun(_,_,0) ->
+ {failure, timeout};
+ (L2,F2,N) ->
+ Ret = F2(),
+ io:format("~p~n",[Ret]),
+ case Ret of
+ ok -> receive after 1 -> ok end,
+ L2(L2,F2,N-1);
+ Other -> Other
+ end
+ end,
+ ?line Loop = fun(F3) -> Loop2(Loop2,F3,1000) end,
+ {Loop,A,R,C}.
+
+
millistamp() ->
{Mega, Secs, Micros} = erlang:now(),
(Micros div 1000) + Secs * 1000 + Mega * 1000000000.
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index bd5685952e..d8a5519195 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
% because udp is not deterministic.
%
-module(gen_udp_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(1)).
@@ -29,23 +29,42 @@
% XXX - we should pick a port that we _know_ is closed. That's pretty hard.
-define(CLOSED_PORT, 6666).
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([send_to_closed/1,
buffer_size/1, binary_passive_recv/1, bad_address/1,
- read_packets/1, open_fd/1]).
+ read_packets/1, open_fd/1, connect/1, implicit_inet6/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [send_to_closed, buffer_size, binary_passive_recv,
+ bad_address, read_packets, open_fd, connect,
+ implicit_inet6].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [send_to_closed,
- buffer_size, binary_passive_recv, bad_address, read_packets,
- open_fd].
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -408,3 +427,79 @@ start_node(Name) ->
stop_node(Node) ->
?t:stop_node(Node).
+
+
+connect(suite) ->
+ [];
+connect(doc) ->
+ ["Test that connect/3 has effect"];
+connect(Config) when is_list(Config) ->
+ ?line Addr = {127,0,0,1},
+ ?line {ok,S1} = gen_udp:open(0),
+ ?line {ok,P1} = inet:port(S1),
+ ?line {ok,S2} = gen_udp:open(0),
+ ?line ok = inet:setopts(S2, [{active,false}]),
+ ?line ok = gen_udp:close(S1),
+ ?line ok = gen_udp:connect(S2, Addr, P1),
+ ?line ok = gen_udp:send(S2, <<16#deadbeef:32>>),
+ ?line ok = case gen_udp:recv(S2, 0, 5) of
+ {error,econnrefused} -> ok;
+ {error,econnreset} -> ok;
+ Other -> Other
+ end,
+ ok.
+
+implicit_inet6(Config) when is_list(Config) ->
+ ?line Host = ok(inet:gethostname()),
+ ?line
+ case inet:getaddr(Host, inet6) of
+ {ok,Addr} ->
+ ?line implicit_inet6(Host, Addr);
+ {error,Reason} ->
+ {skip,
+ "Can not look up IPv6 address: "
+ ++atom_to_list(Reason)}
+ end.
+
+implicit_inet6(Host, Addr) ->
+ ?line Active = {active,false},
+ ?line
+ case gen_udp:open(0, [inet6,Active]) of
+ {ok,S1} ->
+ ?line Loopback = {0,0,0,0,0,0,0,1},
+ ?line io:format("~s ~p~n", ["::1",Loopback]),
+ ?line implicit_inet6(S1, Active, Loopback),
+ ?line ok = gen_udp:close(S1),
+ %%
+ ?line Localhost = "localhost",
+ ?line Localaddr = ok(inet:getaddr(Localhost, inet6)),
+ ?line io:format("~s ~p~n", [Localhost,Localaddr]),
+ ?line S2 = ok(gen_udp:open(0, [{ip,Localaddr},Active])),
+ ?line implicit_inet6(S2, Active, Localaddr),
+ ?line ok = gen_udp:close(S2),
+ %%
+ ?line io:format("~s ~p~n", [Host,Addr]),
+ ?line S3 = ok(gen_udp:open(0, [{ifaddr,Addr},Active])),
+ ?line implicit_inet6(S3, Active, Addr),
+ ?line ok = gen_udp:close(S3);
+ _ ->
+ {skip,"IPv6 not supported"}
+ end.
+
+implicit_inet6(S1, Active, Addr) ->
+ ?line P1 = ok(inet:port(S1)),
+ ?line S2 = ok(gen_udp:open(0, [inet6,Active])),
+ ?line P2 = ok(inet:port(S2)),
+ ?line ok = gen_udp:connect(S2, Addr, P1),
+ ?line ok = gen_udp:connect(S1, Addr, P2),
+ ?line {Addr,P2} = ok(inet:peername(S1)),
+ ?line {Addr,P1} = ok(inet:peername(S2)),
+ ?line {Addr,P1} = ok(inet:sockname(S1)),
+ ?line {Addr,P2} = ok(inet:sockname(S2)),
+ ?line ok = gen_udp:send(S1, Addr, P2, "ping"),
+ ?line {Addr,P1,"ping"} = ok(gen_udp:recv(S2, 1024, 1000)),
+ ?line ok = gen_udp:send(S2, Addr, P1, "pong"),
+ ?line {Addr,P2,"pong"} = ok(gen_udp:recv(S1, 1024)),
+ ?line ok = gen_udp:close(S2).
+
+ok({ok,V}) -> V.
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 7a84ad5e75..1e7bcf1766 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,8 @@
%-define(line_trace, 1).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
names/1, names_hidden/1, locks/1, locks_hidden/1,
bad_input/1, names_and_locks/1, lock_die/1, name_die/1,
basic_partition/1, basic_name_partition/1,
@@ -42,14 +43,14 @@
-export([global_load/3, lock_global/2, lock_global2/2]).
--export([ttt/1]).
+-export([]).
-export([mass_spawn/1]).
-export([start_tracer/0, stop_tracer/0, get_trace/0]).
-compile(export_all).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(NODES, [node()|nodes()]).
@@ -58,41 +59,62 @@
%% The resource used by the global module.
-define(GLOBAL_LOCK, global).
-ttt(suite) ->
- [
-%% 5&6: succeeds
-%% 4&5&6: succeeds
-%% 3&4&5&6: succeeds
-%% 1&2&3&6: fails
-%% 1&2&6: succeeds
-%% 3&6: succeeds
- names, names_hidden, locks, locks_hidden,
- bad_input,
- names_and_locks, lock_die, name_die, basic_partition,
-% advanced_partition, basic_name_partition,
-% stress_partition, simple_ring, simple_line,
- ring].
-
-all(suite) ->
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
case init:get_argument(ring_line) of
- {ok, _} ->
- [ring_line];
+ {ok, _} -> [ring_line];
_ ->
- [names, names_hidden, locks, locks_hidden,
- bad_input,
+ [names, names_hidden, locks, locks_hidden, bad_input,
names_and_locks, lock_die, name_die, basic_partition,
advanced_partition, basic_name_partition,
- stress_partition, simple_ring, simple_line,
- ring, line, global_lost_nodes, otp_1849,
- otp_3162, otp_5640, otp_5737, otp_6931,
- simple_disconnect, simple_resolve, simple_resolve2,
- simple_resolve3,
- leftover_name, re_register_name, name_exit,
- external_nodes, many_nodes, sync_0, global_groups_change,
- register_1, both_known_1, lost_unregister,
- mass_death, garbage_messages]
+ stress_partition, simple_ring, simple_line, ring, line,
+ global_lost_nodes, otp_1849, otp_3162, otp_5640,
+ otp_5737, otp_6931, simple_disconnect, simple_resolve,
+ simple_resolve2, simple_resolve3, leftover_name,
+ re_register_name, name_exit, external_nodes, many_nodes,
+ sync_0, global_groups_change, register_1, both_known_1,
+ lost_unregister, mass_death, garbage_messages]
end.
+groups() ->
+ [{ttt, [],
+ [names, names_hidden, locks, locks_hidden, bad_input,
+ names_and_locks, lock_die, name_die, basic_partition,
+ ring]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_suite(Config) ->
+
+ %% Copied from test_server_ctrl ln 647, we have to do this here as
+ %% the test_server only does this when run without common_test
+ global:sync(),
+ case global:whereis_name(test_server) of
+ undefined ->
+ io:format(user, "Registering test_server globally!~n",[]),
+ global:register_name(test_server, whereis(test_server_ctrl));
+ Pid ->
+ case node() of
+ N when N == node(Pid) ->
+ io:format(user, "Warning: test_server already running!\n", []),
+ global:re_register_name(test_server,self());
+ _ ->
+ ok
+ end
+ end,
+ Config.
+
+end_per_suite(_Config) ->
+ global:unregister_name(test_server),
+ ok.
+
+
-define(TESTCASE, testcase_name).
-define(testcase, ?config(?TESTCASE, Config)).
-define(nodes_tag, '$global_nodes').
@@ -100,9 +122,16 @@ all(suite) ->
init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
ok = gen_server:call(global_name_server, high_level_trace_start,infinity),
+
+ %% Make sure that everything is dead and done. Otherwise there are problems
+ %% on platforms on which it takes a long time to shut down a node.
+ stop_nodes(nodes()),
+ timer:sleep(1000),
+
[{?TESTCASE, Case}, {registered, registered()} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
+ ct:log("Calling end_per_testcase!",[]),
?line write_high_level_trace(Config),
?line _ =
gen_server:call(global_name_server, high_level_trace_stop, infinity),
@@ -114,6 +143,7 @@ fin_per_testcase(_Case, Config) ->
{What, N} <- [{"Added", Registered -- InitRegistered},
{"Removed", InitRegistered -- Registered}],
N =/= []],
+
ok.
%%% General comments:
diff --git a/lib/kernel/test/global_group_SUITE.erl b/lib/kernel/test/global_group_SUITE.erl
index 430cc61267..13b2fd07b5 100644
--- a/lib/kernel/test/global_group_SUITE.erl
+++ b/lib/kernel/test/global_group_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,25 +19,61 @@
-module(global_group_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1]).
-export([start_gg_proc/1, no_gg_proc/1, no_gg_proc_sync/1, compatible/1,
one_grp/1, one_grp_x/1, two_grp/1, hidden_groups/1, test_exit/1]).
-export([init/1, init/2, init2/2, start_proc/1, start_proc_rereg/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%-compile(export_all).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(NODES, [node()|nodes()]).
-define(UNTIL(Seq), loop_until_true(fun() -> Seq end)).
-all(suite) ->
- [start_gg_proc, no_gg_proc, no_gg_proc_sync,
- compatible, one_grp, one_grp_x, two_grp, test_exit,
- hidden_groups].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start_gg_proc, no_gg_proc, no_gg_proc_sync, compatible,
+ one_grp, one_grp_x, two_grp, test_exit, hidden_groups].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) ->
+
+ %% Copied from test_server_ctrl ln 647, we have to do this here as
+ %% the test_server only does this when run without common_test
+ global:sync(),
+ case global:whereis_name(test_server) of
+ undefined ->
+ io:format(user, "Registering test_server globally!~n",[]),
+ global:register_name(test_server, whereis(test_server_ctrl));
+ Pid ->
+ case node() of
+ N when N == node(Pid) ->
+ io:format(user, "Warning: test_server already running!\n", []),
+ global:re_register_name(test_server,self());
+ _ ->
+ ok
+ end
+ end,
+ Config.
+
+end_per_suite(_Config) ->
+ global:unregister_name(test_server),
+ ok.
-define(TESTCASE, testcase_name).
-define(testcase, ?config(?TESTCASE, Config)).
@@ -46,7 +82,7 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog=?t:timetrap(?t:minutes(5)),
[{?TESTCASE, Case}, {watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
@@ -164,8 +200,8 @@ no_gg_proc(Config) when is_list(Config) ->
?line Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn,
Cpxnn, Cpynn, Cpznn],
?line Own_nodes = rpc:call(Cp3, global_group, own_nodes, []),
- ?line true = (Own_nodes -- Own_nodes_should) =:= [],
- ?line true = (Own_nodes_should -- Own_nodes) =:= [],
+ ?line [] = (Own_nodes -- Own_nodes_should),
+ ?line [] = (Own_nodes_should -- Own_nodes),
?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]),
?line receive
@@ -339,8 +375,8 @@ no_gg_proc_sync(Config) when is_list(Config) ->
?line Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn,
Cpxnn, Cpynn, Cpznn],
?line Own_nodes = rpc:call(Cp3, global_group, own_nodes, []),
- ?line true = (Own_nodes -- Own_nodes_should) =:= [],
- ?line true = (Own_nodes_should -- Own_nodes) =:= [],
+ ?line [] = (Own_nodes -- Own_nodes_should),
+ ?line [] = (Own_nodes_should -- Own_nodes),
?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]),
?line receive
@@ -513,8 +549,8 @@ compatible(Config) when is_list(Config) ->
?line Own_nodes_should = [node(), Cp1nn, Cp2nn, Cp3nn,
Cpxnn, Cpynn, Cpznn],
?line Own_nodes = rpc:call(Cp3, global_group, own_nodes, []),
- ?line true = (Own_nodes -- Own_nodes_should) =:= [],
- ?line true = (Own_nodes_should -- Own_nodes) =:= [],
+ ?line [] = (Own_nodes -- Own_nodes_should),
+ ?line [] = (Own_nodes_should -- Own_nodes),
?line Pid2 = rpc:call(Cp1, global_group, send, [test2, {ping, self()}]),
?line receive
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 0d0296238b..043c753cf8 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,14 @@
%%
-module(heart_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1, ostype/1, start/1, restart/1, reboot/1, set_cmd/1, clear_cmd/1,
- dont_drop/1, kill_pid/1, fini/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, start/1, restart/1,
+ reboot/1, set_cmd/1, clear_cmd/1,
+ dont_drop/1, kill_pid/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([start_heart_stress/1, mangle/1, suicide_by_heart/0]).
@@ -33,7 +35,7 @@ init_per_testcase(_Func, Config) ->
Dog=test_server:timetrap(test_server:seconds(?DEFAULT_TIMEOUT_SECS)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Nodes = nodes(),
lists:foreach(fun(X) ->
NNam = list_to_atom(hd(string:tokens(atom_to_list(X),"@"))),
@@ -53,18 +55,29 @@ fin_per_testcase(_Func, Config) ->
%% Should be started in a CC view with:
%% erl -sname master -rsh ctrsh
%%-----------------------------------------------------------------
-all(suite) ->
- [{conf, ostype, [start, restart, reboot,
- set_cmd, clear_cmd, kill_pid], fini}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
-ostype(Config) when is_list(Config) ->
+all() ->
+ [start, restart, reboot, set_cmd, clear_cmd, kill_pid].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) when is_list(Config) ->
case os:type() of
{win32, windows} ->
{skipped, "No use to run on Windows 95/98"};
_ ->
Config
end.
-fini(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
Config.
start_check(Type, Name) ->
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index eb8f918491..1bb173a3ac 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,28 +18,72 @@
%%
-module(inet_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/inet.hrl").
-include_lib("kernel/src/inet_dns.hrl").
--export([all/1, t_gethostbyaddr/1, t_getaddr/1, t_gethostbyname/1,
- t_gethostbyaddr_v6/1, t_getaddr_v6/1, t_gethostbyname_v6/1,
- ipv4_to_ipv6/1, host_and_addr/1, parse/1, t_gethostnative/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ t_gethostbyaddr/0, t_gethostbyaddr/1,
+ t_getaddr/0, t_getaddr/1,
+ t_gethostbyname/0, t_gethostbyname/1,
+ t_gethostbyaddr_v6/0, t_gethostbyaddr_v6/1,
+ t_getaddr_v6/0, t_getaddr_v6/1,
+ t_gethostbyname_v6/0, t_gethostbyname_v6/1,
+ ipv4_to_ipv6/0, ipv4_to_ipv6/1,
+ host_and_addr/0, host_and_addr/1,
+ t_gethostnative/1,
gethostnative_parallell/1, cname_loop/1,
- gethostnative_soft_restart/1,gethostnative_debug_level/1,getif/1]).
+ gethostnative_soft_restart/0, gethostnative_soft_restart/1,
+ gethostnative_debug_level/0, gethostnative_debug_level/1,
+ getif/1,
+ getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1]).
-export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1,
kill_gethost/0, parallell_gethost/0]).
-export([init_per_testcase/2, end_per_testcase/2]).
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [t_gethostbyaddr, t_gethostbyname, t_getaddr,
+ t_gethostbyaddr_v6, t_gethostbyname_v6, t_getaddr_v6,
+ ipv4_to_ipv6, host_and_addr, {group, parse},
+ t_gethostnative, gethostnative_parallell, cname_loop,
+ gethostnative_debug_level, gethostnative_soft_restart,
+ getif, getif_ifr_name_overflow, getservbyname_overflow,
+ getifaddrs].
+
+groups() ->
+ [{parse, [], [parse_hosts, parse_address]}].
+
+%% Required configuaration
+required(v4) ->
+ [{require, test_host_ipv4_only},
+ {require, test_dummy_host}];
+required(v6) ->
+ [{require, test_host_ipv6_only},
+ {require, test_dummy_ipv6_host}];
+required(hosts) ->
+ case os:type() of
+ {OS, _} when OS =:= win32; OS =:= vxworks ->
+ [{require, hardcoded_hosts},
+ {require, hardcoded_ipv6_hosts}];
+ _Else ->
+ [{require, test_hosts}]
+ end.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
-all(suite) ->
- [t_gethostbyaddr, t_gethostbyname, t_getaddr,
- t_gethostbyaddr_v6, t_gethostbyname_v6, t_getaddr_v6,
- ipv4_to_ipv6, host_and_addr, parse,t_gethostnative,
- gethostnative_parallell, cname_loop,
- gethostnative_debug_level,gethostnative_soft_restart,
- getif].
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(60)),
@@ -49,10 +93,12 @@ end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
-
+t_gethostbyaddr() ->
+ required(v4).
t_gethostbyaddr(doc) -> "Test the inet:gethostbyaddr/1 function.";
t_gethostbyaddr(Config) when is_list(Config) ->
- ?line {Name,FullName,IPStr,IP,Aliases,_,_} = ?config(test_host_ipv4_only, Config),
+ ?line {Name,FullName,IPStr,IP,Aliases,_,_} =
+ ct:get_config(test_host_ipv4_only),
?line {ok,HEnt} = inet:gethostbyaddr(IPStr),
?line {ok,HEnt} = inet:gethostbyaddr(IP),
?line {error,Error} = inet:gethostbyaddr(Name),
@@ -74,15 +120,16 @@ t_gethostbyaddr(Config) when is_list(Config) ->
end,
?line {_DName, _DFullName, DIPStr, DIP, _, _, _} =
- ?config(test_dummy_host, Config),
+ ct:get_config(test_dummy_host),
?line {error,nxdomain} = inet:gethostbyaddr(DIPStr),
?line {error,nxdomain} = inet:gethostbyaddr(DIP),
ok.
+t_gethostbyaddr_v6() -> required(v6).
t_gethostbyaddr_v6(doc) -> "Test the inet:gethostbyaddr/1 inet6 function.";
t_gethostbyaddr_v6(Config) when is_list(Config) ->
?line {Name6, FullName6, IPStr6, IP6, Aliases6} =
- ?config(test_host_ipv6_only, Config),
+ ct:get_config(test_host_ipv6_only),
?line case inet:gethostbyaddr(IPStr6) of
%% Even if IPv6 is not supported, the native resolver may succeed
@@ -102,27 +149,28 @@ t_gethostbyaddr_v6(Config) when is_list(Config) ->
{HEnt6#hostent.h_aliases,[[],Aliases6]}]),
?line {_DName6, _DFullName6, DIPStr6, DIP6, _} =
- ?config(test_dummy_ipv6_host, Config),
+ ct:get_config(test_dummy_ipv6_host),
?line {error,nxdomain} = inet:gethostbyaddr(DIPStr6),
?line {error,nxdomain} = inet:gethostbyaddr(DIP6),
ok
end.
+t_gethostbyname() -> required(v4).
t_gethostbyname(doc) -> "Test the inet:gethostbyname/1 function.";
t_gethostbyname(suite) -> [];
t_gethostbyname(Config) when is_list(Config) ->
?line {Name,FullName,IPStr,IP,Aliases,IP_46_Str,_} =
- ?config(test_host_ipv4_only, Config),
+ ct:get_config(test_host_ipv4_only),
?line {ok,_} = inet:gethostbyname(IPStr),
?line {ok,HEnt} = inet:gethostbyname(Name),
?line {ok,HEnt} = inet:gethostbyname(list_to_atom(Name)),
?line HEnt_ = HEnt#hostent{h_addrtype = inet,
h_length = 4,
h_addr_list = [IP]},
+
?line HEnt_ = HEnt,
?line check_elems([{HEnt#hostent.h_name,[Name,FullName]},
{HEnt#hostent.h_aliases,[[],Aliases]}]),
-
?line {ok,HEntF} = inet:gethostbyname(FullName),
?line HEntF_ = HEntF#hostent{h_name = FullName,
h_addrtype = inet,
@@ -132,15 +180,16 @@ t_gethostbyname(Config) when is_list(Config) ->
?line check_elems([{HEnt#hostent.h_aliases,[[],Aliases]}]),
?line {DName, _DFullName, _DIPStr, _DIP, _, _, _} =
- ?config(test_dummy_host, Config),
+ ct:get_config(test_dummy_host),
?line {error,nxdomain} = inet:gethostbyname(DName),
?line {error,nxdomain} = inet:gethostbyname(IP_46_Str).
+t_gethostbyname_v6() -> required(v6).
t_gethostbyname_v6(doc) -> "Test the inet:gethostbyname/1 inet6 function.";
t_gethostbyname_v6(suite) -> [];
t_gethostbyname_v6(Config) when is_list(Config) ->
?line {Name, _, _, _,Aliases,IP_46_Str,IP_46} =
- ?config(test_host_ipv4_only, Config),
+ ct:get_config(test_host_ipv4_only),
case {inet:gethostbyname(IP_46_Str, inet6),
inet:gethostbyname(Name, inet6)} of
@@ -153,7 +202,7 @@ t_gethostbyname_v6(Config) when is_list(Config) ->
?line check_elems([{HEnt46#hostent.h_aliases,[[],Aliases]}]),
?line {Name6, FullName6, IPStr6, IP6, Aliases6} =
- ?config(test_host_ipv6_only, Config),
+ ct:get_config(test_host_ipv6_only),
?line {ok,_} = inet:gethostbyname(IPStr6, inet6),
?line {ok,HEnt6} = inet:gethostbyname(Name6, inet6),
?line {ok,HEnt6} = inet:gethostbyname(list_to_atom(Name6), inet6),
@@ -199,7 +248,7 @@ t_gethostbyname_v6(Config) when is_list(Config) ->
end,
?line {DName6, _DFullName6, _DIPStr6, _DIP6, _} =
- ?config(test_dummy_ipv6_host, Config),
+ ct:get_config(test_dummy_ipv6_host),
?line {error,nxdomain} = inet:gethostbyname(DName6, inet6),
ok;
{_,_} ->
@@ -218,11 +267,12 @@ check_elem(Val, [], Tests0) ->
?t:fail({no_match,Val,Tests0}).
+t_getaddr() -> required(v4).
t_getaddr(doc) -> "Test the inet:getaddr/2 function.";
t_getaddr(suite) -> [];
t_getaddr(Config) when is_list(Config) ->
?line {Name,FullName,IPStr,IP,_,IP_46_Str,IP46} =
- ?config(test_host_ipv4_only, Config),
+ ct:get_config(test_host_ipv4_only),
?line {ok,IP} = inet:getaddr(list_to_atom(Name), inet),
?line {ok,IP} = inet:getaddr(Name, inet),
?line {ok,IP} = inet:getaddr(FullName, inet),
@@ -231,17 +281,18 @@ t_getaddr(Config) when is_list(Config) ->
?line {error,nxdomain} = inet:getaddr(IP_46_Str, inet),
?line {error,eafnosupport} = inet:getaddr(IP46, inet),
- ?line {DName, DFullName, DIPStr, DIP, _, _, _} = ?config(test_dummy_host, Config),
+ ?line {DName, DFullName, DIPStr, DIP, _, _, _} = ct:get_config(test_dummy_host),
?line {error,nxdomain} = inet:getaddr(DName, inet),
?line {error,nxdomain} = inet:getaddr(DFullName, inet),
?line {ok,DIP} = inet:getaddr(DIPStr, inet),
?line {ok,DIP} = inet:getaddr(DIP, inet).
+t_getaddr_v6() -> required(v4) ++ required(v6).
t_getaddr_v6(doc) -> "Test the inet:getaddr/2 function.";
t_getaddr_v6(suite) -> [];
t_getaddr_v6(Config) when is_list(Config) ->
?line {Name,FullName,IPStr,_IP,_,IP_46_Str,IP46} =
- ?config(test_host_ipv4_only, Config),
+ ct:get_config(test_host_ipv4_only),
case {inet:getaddr(IP_46_Str, inet6),inet:getaddr(Name, inet6)} of
{{ok,IP46},{ok,_}} ->
%% Since we suceeded in parsing an IPv6 address string and
@@ -260,7 +311,7 @@ t_getaddr_v6(Config) when is_list(Config) ->
%% inet_db:res_option(lookup))
%% end,
?line {Name6, FullName6, IPStr6, IP6, _} =
- ?config(test_host_ipv6_only, Config),
+ ct:get_config(test_host_ipv6_only),
?line {ok,_} = inet:getaddr(list_to_atom(Name6), inet6),
?line {ok,_} = inet:getaddr(Name6, inet6),
?line {ok,_} = inet:getaddr(FullName6, inet6),
@@ -268,7 +319,7 @@ t_getaddr_v6(Config) when is_list(Config) ->
?line {ok,IP6} = inet:getaddr(IPStr6, inet6),
?line {DName6, DFullName6, DIPStr6, DIP6, _} =
- ?config(test_dummy_ipv6_host, Config),
+ ct:get_config(test_dummy_ipv6_host),
?line {error,nxdomain} = inet:getaddr(DName6, inet6),
?line {error,nxdomain} = inet:getaddr(DFullName6, inet6),
?line {ok,DIP6} = inet:getaddr(DIPStr6, inet6),
@@ -278,6 +329,7 @@ t_getaddr_v6(Config) when is_list(Config) ->
{skip, "IPv6 is not supported on this host"}
end.
+ipv4_to_ipv6() -> required(v4).
ipv4_to_ipv6(doc) -> "Test if IPv4 address is converted to IPv6 address.";
ipv4_to_ipv6(suite) -> [];
ipv4_to_ipv6(Config) when is_list(Config) ->
@@ -286,7 +338,7 @@ ipv4_to_ipv6(Config) when is_list(Config) ->
%% address should be returned. If no IPv6 support on this host, an
%% error should beturned.
?line {_Name,_FullName,IPStr,_IP,Aliases,IP_46_Str,IP_46} =
- ?config(test_host_ipv4_only, Config),
+ ct:get_config(test_host_ipv4_only),
?line IP4to6Res =
case inet:getaddr(IPStr, inet6) of
{ok,IP_46} ->
@@ -313,6 +365,7 @@ ipv4_to_ipv6(Config) when is_list(Config) ->
end,
ok.
+host_and_addr() -> required(hosts).
host_and_addr(doc) -> ["Test looking up hosts and addresses. Use 'ypcat hosts' ",
"or the local eqivalent to find all hosts."];
host_and_addr(suite) -> [];
@@ -333,30 +386,30 @@ try_host({Ip0, Host}) ->
%% Get all hosts from the system using 'ypcat hosts' or the local
%% equvivalent.
-get_hosts(Config) ->
+get_hosts(_Config) ->
case os:type() of
{unix, _} ->
List = lists:map(fun(X) ->
atom_to_list(X)++" "
- end, ?config(test_hosts, Config)),
+ end, ct:get_config(test_hosts)),
Cmd = "ypmatch "++List++" hosts.byname",
HostFile = os:cmd(Cmd),
get_hosts(HostFile, [], [], []);
_ ->
- ?config(hardcoded_hosts, Config)
+ ct:get_config(hardcoded_hosts)
end.
-get_ipv6_hosts(Config) ->
+get_ipv6_hosts(_Config) ->
case os:type() of
{unix, _} ->
List = lists:map(fun(X) ->
atom_to_list(X)++" "
- end, ?config(test_hosts, Config)),
+ end, ct:get_config(ipv6_hosts)),
Cmd = "ypmatch "++List++" ipnodes.byname",
HostFile = os:cmd(Cmd),
get_hosts(HostFile, [], [], []);
_ ->
- ?config(hardcoded_ipv6_hosts, Config)
+ ct:get_config(hardcoded_ipv6_hosts)
end.
get_hosts([$\t|Rest], Cur, Ip, Result) when Ip /= [] ->
@@ -375,9 +428,6 @@ get_hosts([C|Rest], Cur, Ip, Result) ->
get_hosts([], _, _, Result) ->
Result.
-parse(suite) -> [parse_hosts, parse_address];
-parse(doc) -> ["Test that parsing of the hosts file or equivalent works,",
- "and that erroneous lines are skipped"].
parse_hosts(Config) when is_list(Config) ->
?line DataDir = ?config(data_dir,Config),
@@ -729,6 +779,7 @@ cname_loop(Config) when is_list(Config) ->
lookup_count=300,
lookup_processes=20}).
+gethostnative_soft_restart() -> required(hosts).
gethostnative_soft_restart(suite) ->
[];
gethostnative_soft_restart(doc) ->
@@ -739,6 +790,8 @@ gethostnative_soft_restart(Config) when is_list(Config) ->
#gethostnative_control{
control_seq=[soft_restart]}).
+
+gethostnative_debug_level() -> required(hosts).
gethostnative_debug_level(suite) ->
[];
gethostnative_debug_level(doc) ->
@@ -872,10 +925,30 @@ getif(suite) ->
getif(doc) ->
["Tests basic functionality of getiflist, getif, and ifget"];
getif(Config) when is_list(Config) ->
+ ?line case os:type() of
+ {unix,Osname} ->
+ ?line do_getif(Osname);
+ {_,_} ->
+ {skip,"inet:getif/0 probably not supported"}
+ end.
+
+do_getif(Osname) ->
?line {ok,Hostname} = inet:gethostname(),
?line {ok,Address} = inet:getaddr(Hostname, inet),
?line {ok,Loopback} = inet:getaddr("localhost", inet),
?line {ok,Interfaces} = inet:getiflist(),
+ ?line HWAs =
+ lists:sort(
+ lists:foldl(
+ fun (I, Acc) ->
+ case inet:ifget(I, [hwaddr]) of
+ {ok,[{hwaddr,A}]} -> [A|Acc];
+ {ok,[]} -> Acc
+ end
+ end, [], Interfaces)),
+ ?line io:format("HWAs = ~p~n", [HWAs]),
+ ?line (Osname =/= sunos)
+ andalso ((length(HWAs) > 0) orelse (?t:fail(no_HWAs))),
?line Addresses =
lists:sort(
lists:foldl(
@@ -891,6 +964,134 @@ getif(Config) when is_list(Config) ->
?line true = ip_member(Loopback, Addresses),
?line ok.
+getif_ifr_name_overflow(doc) ->
+ "Test long interface names do not overrun buffer";
+getif_ifr_name_overflow(Config) when is_list(Config) ->
+ ?line case os:type() of
+ {unix,Osname} ->
+ ?line do_getif_ifr_name_overflow(Osname);
+ {_,_} ->
+ {skip,"inet:ifget/2 probably not supported"}
+ end.
+
+do_getif_ifr_name_overflow(_) ->
+ %% emulator should not crash
+ ?line {ok,[]} = inet:ifget(lists:duplicate(128, "x"), [addr]),
+ ok.
+
+getservbyname_overflow(doc) ->
+ "Test long service names do not overrun buffer";
+getservbyname_overflow(Config) when is_list(Config) ->
+ %% emulator should not crash
+ ?line {error,einval} = inet:getservbyname(list_to_atom(lists:flatten(lists:duplicate(128, "x"))), tcp),
+ ok.
+
+getifaddrs(doc) ->
+ "Test inet:gifaddrs/0";
+getifaddrs(Config) when is_list (Config) ->
+ ?line {ok,IfAddrs} = inet:getifaddrs(),
+ ?line ?t:format("IfAddrs = ~p.~n", [IfAddrs]),
+ ?line
+ case
+ {os:type(),
+ [If ||
+ {If,Opts} <- IfAddrs,
+ lists:keymember(hwaddr, 1, Opts)]} of
+ {{unix,sunos},[]} -> ok;
+ {OT,[]} ->
+ ?t:fail({should_have_hwaddr,OT});
+ _ -> ok
+ end,
+ ?line Addrs =
+ [element(1, A) || A <- ifaddrs(IfAddrs)],
+ ?line ?t:format("Addrs = ~p.~n", [Addrs]),
+ ?line [check_addr(Addr) || Addr <- Addrs],
+ ok.
+
+check_addr(Addr)
+ when tuple_size(Addr) =:= 8,
+ element(1, Addr) band 16#FFC0 =:= 16#FE80 ->
+ ?line ?t:format("Addr: ~p link local; SKIPPED!~n", [Addr]),
+ ok;
+check_addr(Addr) ->
+ ?line ?t:format("Addr: ~p.~n", [Addr]),
+ ?line Ping = "ping",
+ ?line Pong = "pong",
+ ?line {ok,L} = gen_tcp:listen(0, [{ip,Addr},{active,false}]),
+ ?line {ok,P} = inet:port(L),
+ ?line {ok,S1} = gen_tcp:connect(Addr, P, [{active,false}]),
+ ?line {ok,S2} = gen_tcp:accept(L),
+ ?line ok = gen_tcp:send(S2, Ping),
+ ?line {ok,Ping} = gen_tcp:recv(S1, length(Ping)),
+ ?line ok = gen_tcp:send(S1, Pong),
+ ?line ok = gen_tcp:close(S1),
+ ?line {ok,Pong} = gen_tcp:recv(S2, length(Pong)),
+ ?line ok = gen_tcp:close(S2),
+ ?line ok = gen_tcp:close(L),
+ ok.
+
+-record(ifopts, {name,flags,addrs=[],hwaddr}).
+
+ifaddrs([]) -> [];
+ifaddrs([{If,Opts}|IOs]) ->
+ ?line #ifopts{flags=Flags} = Ifopts =
+ check_ifopts(Opts, #ifopts{name=If}),
+ ?line case Flags =/= undefined andalso lists:member(up, Flags) of
+ true ->
+ Ifopts#ifopts.addrs;
+ false ->
+ []
+ end++ifaddrs(IOs).
+
+check_ifopts([], #ifopts{name=If,flags=Flags,addrs=Raddrs}=Ifopts) ->
+ Addrs = lists:reverse(Raddrs),
+ R = Ifopts#ifopts{addrs=Addrs},
+ ?t:format("~p.~n", [R]),
+ %% See how we did...
+ if is_list(Flags) -> ok;
+ true ->
+ ?t:fail({flags_undefined,If})
+ end,
+ case lists:member(broadcast, Flags) of
+ true ->
+ [case A of
+ {_,_,_} -> A;
+ {T,_} when tuple_size(T) =:= 8 -> A;
+ _ ->
+ ?t:fail({broaddr_missing,If,A})
+ end || A <- Addrs];
+ false ->
+ [case A of {_,_} -> A;
+ _ ->
+ ?t:fail({should_have_netmask,If,A})
+ end || A <- Addrs]
+ end,
+ R;
+check_ifopts([{flags,Flags}|Opts], #ifopts{flags=undefined}=Ifopts) ->
+ check_ifopts(Opts, Ifopts#ifopts{flags=Flags});
+check_ifopts([{flags,Fs}|Opts], #ifopts{flags=Flags}=Ifopts) ->
+ case Fs of
+ Flags ->
+ check_ifopts(Opts, Ifopts#ifopts{});
+ _ ->
+ ?t:fail({multiple_flags,Fs,Ifopts})
+ end;
+check_ifopts(
+ [{addr,Addr},{netmask,Netmask},{broadaddr,Broadaddr}|Opts],
+ #ifopts{addrs=Addrs}=Ifopts) ->
+ check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask,Broadaddr}|Addrs]});
+check_ifopts(
+ [{addr,Addr},{netmask,Netmask}|Opts],
+ #ifopts{addrs=Addrs}=Ifopts) ->
+ check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask}|Addrs]});
+check_ifopts([{addr,Addr}|Opts], #ifopts{addrs=Addrs}=Ifopts) ->
+ check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr}|Addrs]});
+check_ifopts([{hwaddr,Hwaddr}|Opts], #ifopts{hwaddr=undefined}=Ifopts)
+ when is_list(Hwaddr) ->
+ check_ifopts(Opts, Ifopts#ifopts{hwaddr=Hwaddr});
+check_ifopts([{hwaddr,HwAddr}|_], #ifopts{}=Ifopts) ->
+ ?t:fail({multiple_hwaddrs,HwAddr,Ifopts}).
+
%% Works just like lists:member/2, except that any {127,_,_,_} tuple
%% matches any other {127,_,_,_}. We do this to handle Linux systems
%% that use (for instance) 127.0.1.1 as the IP address for the hostname.
diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl
index cc32d1f8f9..5fc8df475d 100644
--- a/lib/kernel/test/inet_res_SUITE.erl
+++ b/lib/kernel/test/inet_res_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,24 +18,51 @@
%%
-module(inet_res_SUITE).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include_lib("kernel/include/inet.hrl").
-include_lib("kernel/src/inet_dns.hrl").
--export([all/1, init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
-export([basic/1, resolve/1, edns0/1, txt_record/1, files_monitor/1]).
--export([gethostbyaddr/1, gethostbyaddr_v6/1,
- gethostbyname/1, gethostbyname_v6/1,
- getaddr/1, getaddr_v6/1, ipv4_to_ipv6/1, host_and_addr/1]).
+-export([
+ gethostbyaddr/0, gethostbyaddr/1,
+ gethostbyaddr_v6/0, gethostbyaddr_v6/1,
+ gethostbyname/0, gethostbyname/1,
+ gethostbyname_v6/0, gethostbyname_v6/1,
+ getaddr/0, getaddr/1,
+ getaddr_v6/0, getaddr_v6/1,
+ ipv4_to_ipv6/0, ipv4_to_ipv6/1,
+ host_and_addr/0, host_and_addr/1
+ ]).
-define(RUN_NAMED, "run-named").
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[basic, resolve, edns0, txt_record, files_monitor,
- gethostbyaddr, gethostbyaddr_v6, gethostbyname, gethostbyname_v6,
- getaddr, getaddr_v6, ipv4_to_ipv6, host_and_addr].
+ gethostbyaddr, gethostbyaddr_v6, gethostbyname,
+ gethostbyname_v6, getaddr, getaddr_v6, ipv4_to_ipv6,
+ host_and_addr].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
zone_dir(basic) ->
otptest;
@@ -450,11 +477,19 @@ do_files_monitor(Config) ->
%% Compatibility tests. Call the inet_SUITE tests, but with
%% lookup = [file,dns] instead of [native]
+gethostbyaddr() -> inet_SUITE:t_gethostbyaddr().
gethostbyaddr(Config) -> inet_SUITE:t_gethostbyaddr(Config).
+gethostbyaddr_v6() -> inet_SUITE:t_gethostbyaddr_v6().
gethostbyaddr_v6(Config) -> inet_SUITE:t_gethostbyaddr_v6(Config).
+gethostbyname() -> inet_SUITE:t_gethostbyname().
gethostbyname(Config) -> inet_SUITE:t_gethostbyname(Config).
+gethostbyname_v6() -> inet_SUITE:t_gethostbyname_v6().
gethostbyname_v6(Config) -> inet_SUITE:t_gethostbyname_v6(Config).
+getaddr() -> inet_SUITE:t_getaddr().
getaddr(Config) -> inet_SUITE:t_getaddr(Config).
+getaddr_v6() -> inet_SUITE:t_getaddr_v6().
getaddr_v6(Config) -> inet_SUITE:t_getaddr_v6(Config).
+ipv4_to_ipv6() -> inet_SUITE:ipv4_to_ipv6().
ipv4_to_ipv6(Config) -> inet_SUITE:ipv4_to_ipv6(Config).
+host_and_addr() -> inet_SUITE:host_and_addr().
host_and_addr(Config) -> inet_SUITE:host_and_addr(Config).
diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl
index 0fa0226ccf..0c63a6d653 100644
--- a/lib/kernel/test/inet_sockopt_SUITE.erl
+++ b/lib/kernel/test/inet_sockopt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
%%
-module(inet_sockopt_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(C_GET_IPPROTO_TCP,1).
@@ -48,7 +48,9 @@
-define(C_QUIT,99).
--export([all/1, simple/1, loop_all/1, simple_raw/1, simple_raw_getbin/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ simple/1, loop_all/1, simple_raw/1, simple_raw_getbin/1,
doc_examples_raw/1,doc_examples_raw_getbin/1,
large_raw/1,large_raw_getbin/1,combined/1,combined_getbin/1,
type_errors/1]).
@@ -56,10 +58,29 @@
-export([init_per_testcase/2, end_per_testcase/2]).
-all(suite) ->
- [simple,loop_all,simple_raw,simple_raw_getbin,
- doc_examples_raw, doc_examples_raw_getbin,
- large_raw,large_raw_getbin,combined,combined_getbin,type_errors].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [simple, loop_all, simple_raw, simple_raw_getbin,
+ doc_examples_raw, doc_examples_raw_getbin, large_raw,
+ large_raw_getbin, combined, combined_getbin,
+ type_errors].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(60)),
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index bbd8261197..2db0f7dcb8 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,16 +18,18 @@
%%
-module(init_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([get_arguments/1, get_argument/1, boot_var/1, restart/1,
+ many_restarts/1,
get_plain_arguments/1,
- reboot/1, stop/1, get_status/1, script_id/1, boot/1]).
+ reboot/1, stop/1, get_status/1, script_id/1]).
-export([boot1/1, boot2/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([init/1, fini/1]).
@@ -38,17 +40,35 @@
%% Should be started in a CC view with:
%% erl -sname master -rsh ctrsh
%%-----------------------------------------------------------------
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[get_arguments, get_argument, boot_var,
- get_plain_arguments,
- restart,
- get_status, script_id, boot].
+ many_restarts,
+ get_plain_arguments, restart, get_status, script_id,
+ {group, boot}].
+
+groups() ->
+ [{boot, [], [boot1, boot2]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?t:timetrap(?t:seconds(?DEFAULT_TIMEOUT_SEC)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog).
@@ -299,6 +319,73 @@ is_real_system(KernelVsn, StdlibVsn) ->
%% Therefore the slave process must be killed
%% before restart.
%% ------------------------------------------------
+many_restarts(doc) -> [];
+many_restarts(suite) ->
+ case ?t:os_type() of
+ {Fam, _} when Fam == unix; Fam == win32 ->
+ {req, [distribution, {local_slave_nodes, 1}, {time, 5}]};
+ _ ->
+ {skip, "Only run on unix and win32"}
+ end;
+
+many_restarts(Config) when is_list(Config) ->
+ ?line Dog = ?t:timetrap(?t:seconds(480)),
+ ?line {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC),
+ ?line loop_restart(30,Node,rpc:call(Node,erlang,whereis,[error_logger])),
+ ?line loose_node:stop(Node),
+ ?line ?t:timetrap_cancel(Dog),
+ ok.
+
+loop_restart(0,_,_) ->
+ ok;
+loop_restart(N,Node,EHPid) ->
+ ?line erlang:monitor_node(Node, true),
+ ?line ok = rpc:call(Node, init, restart, []),
+ ?line receive
+ {nodedown, Node} ->
+ ok
+ after 10000 ->
+ loose_node:stop(Node),
+ ?t:fail(not_stopping)
+ end,
+ ?line ok = wait_for(30, Node, EHPid),
+ ?line loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[error_logger])).
+
+wait_for(0,Node,_) ->
+ loose_node:stop(Node),
+ error;
+wait_for(N,Node,EHPid) ->
+ ?line case rpc:call(Node, erlang, whereis, [error_logger]) of
+ Pid when is_pid(Pid), Pid =/= EHPid ->
+ %% ?line erlang:display(ok),
+ ?line ok;
+ _X ->
+ %% ?line erlang:display(_X),
+ %% ?line Procs = rpc:call(Node, erlang, processes, []),
+ %% ?line erlang:display(Procs),
+ %% case is_list(Procs) of
+ %% true ->
+ %% ?line [(catch erlang:display(
+ %% rpc:call(Node,
+ %% erlang,
+ %% process_info,
+ %% [Y,registered_name])))
+ %% || Y <- Procs];
+ %% _ ->
+ %% ok
+ %% end,
+ receive
+ after 100 ->
+ ok
+ end,
+ ?line wait_for(N-1,Node,EHPid)
+ end.
+
+%% ------------------------------------------------
+%% Slave executes erlang:halt() on master nodedown.
+%% Therefore the slave process must be killed
+%% before restart.
+%% ------------------------------------------------
restart(doc) -> [];
restart(suite) ->
case ?t:os_type() of
@@ -488,7 +575,6 @@ script_id(Config) when is_list(Config) ->
%% ------------------------------------------------
%% Start the slave system with -boot flag.
%% ------------------------------------------------
-boot(suite) -> [boot1, boot2].
boot1(doc) -> [];
boot1(suite) -> {req, [distribution, {local_slave_nodes, 1}, {time, 35}]};
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index c0db292ba5..b2308dd321 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,8 +17,10 @@
%% %CopyrightEnd%
%%
-module(interactive_shell_SUITE).
--include("test_server.hrl").
--export([all/1, get_columns_and_rows/1, exit_initial/1, job_control_local/1,
+-include_lib("test_server/include/test_server.hrl").
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ get_columns_and_rows/1, exit_initial/1, job_control_local/1,
job_control_remote/1,
job_control_remote_noshell/1]).
@@ -44,10 +46,28 @@ end_per_testcase(_Func, Config) ->
test_server:timetrap_cancel(Dog).
-all(suite) ->
- [get_columns_and_rows, exit_initial, job_control_local,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [get_columns_and_rows, exit_initial, job_control_local,
job_control_remote, job_control_remote_noshell].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%-define(DEBUG,1).
-ifdef(DEBUG).
-define(dbg(Data),erlang:display(Data)).
diff --git a/lib/kernel/test/kernel.cover b/lib/kernel/test/kernel.cover
index 228dafc565..f6967ca651 100644
--- a/lib/kernel/test/kernel.cover
+++ b/lib/kernel/test/kernel.cover
@@ -1,4 +1,3 @@
%% -*- erlang -*-
-{exclude,all}.
-{include,[gen_udp,inet6_udp,inet_res,inet_dns]}.
+{incl_mods,[gen_udp,inet6_udp,inet_res,inet_dns]}.
diff --git a/lib/kernel/test/kernel.dynspec b/lib/kernel/test/kernel.dynspec
deleted file mode 100644
index 297a7c71ea..0000000000
--- a/lib/kernel/test/kernel.dynspec
+++ /dev/null
@@ -1,57 +0,0 @@
-%% -*- erlang -*-
-%% You can test this file using this command.
-%% file:script("kernel.dynspec", [{'Os',"Unix"}]).
-
-case Os of
- "VxWorks" ->
- FsCantHandle = "VxWorks filesystem can't handle this",
- FsOverload = "VxWorks filesystem would overload",
- CantHandle = "VxWorks can't handle this",
- SlaveMisadaption = "Test not adopted to slaves on different machine",
- [{skip,{application_SUITE,
- "VxWorks: requires manual testing "++
- "(requires multiple nodes (OTP-1774))"}},
- {skip,{bif_SUITE, spawn_link_race1, "Known bug."}},
- {skip,{erl_distribution_SUITE, "VxWorks: More vx nodes needed"}},
- {skip,{file_SUITE,read_write_file,FsCantHandle}},
- {skip,{file_SUITE,cur_dir_0,FsCantHandle}},
- {skip,{file_SUITE,open1,FsCantHandle}},
- {skip,{file_SUITE,file_info_times,FsCantHandle}},
- {skip,{file_SUITE,file_write_file_info,FsCantHandle}},
- {skip,{file_SUITE,truncate,FsCantHandle}},
- {skip,{file_SUITE,rename,FsCantHandle}},
- {skip,{file_SUITE,e_delete,FsCantHandle}},
- {skip,{file_SUITE,e_rename,FsCantHandle}},
- {skip,{file_SUITE,delayed_write,FsCantHandle}},
- {skip,{file_SUITE,read_ahead,FsCantHandle}},
- {skip,{file_SUITE,segment_write,FsOverload}},
- {skip,{file_SUITE,segment_read,FsOverload}},
- {skip,{file_SUITE,compress_errors,FsCantHandle}},
- {skip,{global_SUITE,
- "To heavy on slavenodes for VxWorks (and more)."}},
- {skip,{global_group_SUITE, "To heavy on slavenodes for VxWorks."}},
- {skip,{heart_SUITE, "Not for VxWorks heart, it's special"}},
- {skip,{init_SUITE,restart,"Uses peer nodes"}},
- {skip,{kernel_config_SUITE, "VxWorks does not support slave nodes"}},
- {skip,{os_SUITE,space_in_cwd,CantHandle}},
- {skip,{os_SUITE,space_in_name,CantHandle}},
- {skip,{os_SUITE,quoting,CantHandle}},
- {skip,{prim_file_SUITE,open1,FsCantHandle}},
- {skip,{prim_file_SUITE,compress_errors,FsCantHandle}},
- {skip,{seq_trace_SUITE,distributed_recv,SlaveMisadaption}},
- {skip,{seq_trace_SUITE,distributed_exit,SlaveMisadaption}}];
- _ ->
- []
-end ++
-try gen_sctp:open() of
- {ok,Socket} ->
- gen_sctp:close(Socket),
- [];
- _ ->
- []
-catch
- error:badarg ->
- [{skip,{gen_sctp_SUITE,"SCTP not supported on this machine"}}];
- _:_ ->
- []
-end.
diff --git a/lib/kernel/test/kernel.spec b/lib/kernel/test/kernel.spec
new file mode 100644
index 0000000000..62afc9f97b
--- /dev/null
+++ b/lib/kernel/test/kernel.spec
@@ -0,0 +1,4 @@
+{config, "../test_server/ts.config"}.
+{config, "../test_server/ts.unix.config"}.
+
+{suites,"../kernel_test", all}.
diff --git a/lib/kernel/test/kernel.spec.wxworks b/lib/kernel/test/kernel.spec.wxworks
new file mode 100644
index 0000000000..370e474e64
--- /dev/null
+++ b/lib/kernel/test/kernel.spec.wxworks
@@ -0,0 +1,63 @@
+%% -*- erlang -*-
+{suites,"kernel_test",all}.
+{skip_cases,"kernel_test",bif_SUITE,[spawn_link_race1],"Known bug."}.
+{skip_cases,"kernel_test",file_SUITE,
+ [read_write_file],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [cur_dir_0],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [open1],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [file_info_times],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [file_write_file_info],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [truncate],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [rename],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [e_delete],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [e_rename],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [delayed_write],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [read_ahead],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [segment_write],
+ "VxWorks filesystem would overload"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [segment_read],
+ "VxWorks filesystem would overload"}.
+{skip_cases,"kernel_test",file_SUITE,
+ [compress_errors],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",init_SUITE,[restart],"Uses peer nodes"}.
+{skip_cases,"kernel_test",os_SUITE,[space_in_cwd],"VxWorks can't handle this"}.
+{skip_cases,"kernel_test",os_SUITE,
+ [space_in_name],
+ "VxWorks can't handle this"}.
+{skip_cases,"kernel_test",os_SUITE,[quoting],"VxWorks can't handle this"}.
+{skip_cases,"kernel_test",prim_file_SUITE,
+ [open1],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",prim_file_SUITE,
+ [compress_errors],
+ "VxWorks filesystem can't handle this"}.
+{skip_cases,"kernel_test",seq_trace_SUITE,
+ [distributed_recv],
+ "Test not adopted to slaves on different machine"}.
+{skip_cases,"kernel_test",seq_trace_SUITE,
+ [distributed_exit],
+ "Test not adopted to slaves on different machine"}.
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index bb1d905de3..16b6c54939 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,15 +20,16 @@
%%% Kernel application test suite.
%%%-----------------------------------------------------------------
-module(kernel_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
-export([app_test/1]).
@@ -36,15 +37,31 @@
%%
%% all/1
%%
-all(doc) ->
- [];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[app_test].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/kernel/test/kernel_config_SUITE.erl b/lib/kernel/test/kernel_config_SUITE.erl
index c72fc3f02d..93bdb8657c 100644
--- a/lib/kernel/test/kernel_config_SUITE.erl
+++ b/lib/kernel/test/kernel_config_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,23 +18,35 @@
%%
-module(kernel_config_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1, sync/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, sync/1]).
--export([init/1, fini/1]).
+-export([init_per_suite/1, end_per_suite/1]).
-all(suite) ->
- [{conf, init, [sync], fini}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
-init(doc) -> [];
-init(suite) -> [];
-init(Config) when is_list(Config) ->
+all() ->
+ [sync].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) -> [];
+init_per_suite(suite) -> [];
+init_per_suite(Config) when is_list(Config) ->
Config.
-fini(doc) -> [];
-fini(suite) -> [];
-fini(Config) when is_list(Config) ->
+end_per_suite(doc) -> [];
+end_per_suite(suite) -> [];
+end_per_suite(Config) when is_list(Config) ->
stop_node(init_test),
Config.
diff --git a/lib/kernel/test/myApp.erl b/lib/kernel/test/myApp.erl
index 2b92046141..26dc74f91b 100644
--- a/lib/kernel/test/myApp.erl
+++ b/lib/kernel/test/myApp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 6a3534b094..b08b12c978 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,15 +18,34 @@
%%
-module(os_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([space_in_cwd/1, quoting/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, evil/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [space_in_cwd, quoting, space_in_name, bad_command,
+ find_executable, unix_comment_in_command, evil].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [space_in_cwd, quoting, space_in_name, bad_command, find_executable,
- unix_comment_in_command, evil].
space_in_cwd(doc) ->
"Test that executing a command in a current working directory "
@@ -137,6 +156,13 @@ find_executable(Config) when is_list(Config) ->
?line find_exe(Abin, "my_ar", ".exe", Path),
?line find_exe(Abin, "my_ascii", ".com", Path),
?line find_exe(Abin, "my_adb", ".bat", Path),
+ %% OTP-3626 find names of executables given with extension
+ ?line find_exe(Abin, "my_ar.exe", "", Path),
+ ?line find_exe(Abin, "my_ascii.com", "", Path),
+ ?line find_exe(Abin, "my_adb.bat", "", Path),
+ ?line find_exe(Abin, "my_ar.EXE", "", Path),
+ ?line find_exe(Abin, "my_ascii.COM", "", Path),
+ ?line find_exe(Abin, "MY_ADB.BAT", "", Path),
%% Search for programs in Abin (second element in PATH).
?line find_exe(Abin, "my_ar", ".exe", Path),
@@ -149,6 +175,21 @@ find_executable(Config) when is_list(Config) ->
?line find_exe(Current, "my_batch", ".bat", Path),
ok;
{unix, _} ->
+ DataDir = ?config(data_dir, Config),
+
+ %% Smoke test.
+ case lib:progname() of
+ erl ->
+ ?line ErlPath = os:find_executable("erl"),
+ ?line true = is_list(ErlPath),
+ ?line true = filelib:is_regular(ErlPath);
+ _ ->
+ %% Don't bother -- the progname could include options.
+ ok
+ end,
+
+ %% Never return a directory name.
+ ?line false = os:find_executable("unix", [DataDir]),
ok;
vxworks ->
ok
@@ -197,8 +238,9 @@ evil(Config) when is_list(Config) ->
evil_loop(Parent, ?EVIL_LOOPS,N)
end)
end, lists:seq(1, ?EVIL_PROCS)),
- Devil = spawn(fun () -> devil(hd(Ps), hd(lists:reverse(Ps))) end),
+ Devil = spawn_link(fun () -> devil(hd(Ps), hd(lists:reverse(Ps))) end),
lists:foreach(fun (P) -> receive {P, done} -> ok end end, Ps),
+ unlink(Devil),
exit(Devil, kill),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/kernel/test/pdict_SUITE.erl b/lib/kernel/test/pdict_SUITE.erl
index 87ee951a0c..8afdfc8a47 100644
--- a/lib/kernel/test/pdict_SUITE.erl
+++ b/lib/kernel/test/pdict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
%% NB: The ?line macro cannot be used when testing the dictionary.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(M(A,B),m(A,B,?MODULE,?LINE)).
-ifdef(DEBUG).
@@ -29,22 +29,41 @@
-define(DEBUGF(A,B), noop).
-endif.
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
simple/1, complicated/1, heavy/1, info/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([other_process/2]).
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(test_server:minutes(10)),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[simple, complicated, heavy, info].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
simple(doc) ->
["Tests simple functionality in process dictionary."];
simple(suite) ->
diff --git a/lib/kernel/test/pg2_SUITE.erl b/lib/kernel/test/pg2_SUITE.erl
index 8eb1a7ca19..0ac34e735c 100644
--- a/lib/kernel/test/pg2_SUITE.erl
+++ b/lib/kernel/test/pg2_SUITE.erl
@@ -1,33 +1,35 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%----------------------------------------------------------------
%% Purpose:Test Suite for the 'pg2' module.
%%-----------------------------------------------------------------
-module(pg2_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Config)).
-define(privdir, ?config(priv_dir, Config)).
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
--export([tickets/1,
- otp_7277/1, otp_8259/1,
+-export([
+ otp_7277/1, otp_8259/1, otp_8653/1,
compat/1, basic/1]).
% Default timetrap timeout (set in init_per_testcase).
@@ -37,22 +39,40 @@
-define(testcase, ?config(?TESTCASE, Config)).
%% Internal export.
--export([mk_part_node/3, part1/5, p_init/3, start_proc/1, sane/0]).
+-export([mk_part_node_and_group/3, part2/4,
+ mk_part_node/3, part1/5, p_init/3, start_proc/1, sane/0]).
init_per_testcase(Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{?TESTCASE, Case}, {watchdog, Dog} | Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [tickets].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, tickets}].
+
+groups() ->
+ [{tickets, [],
+ [otp_7277, otp_8259, otp_8653, compat, basic]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-tickets(suite) ->
- [otp_7277, otp_8259, compat, basic].
otp_7277(doc) ->
"OTP-7277. Bugfix leave().";
@@ -65,9 +85,9 @@ otp_7277(Config) when is_list(Config) ->
?line ok = pg2:leave(b, P),
?line true = exit(P, kill),
case {pg2:get_members(a), pg2:get_local_members(a)} of
- {[], []} ->
+ {[], []} ->
ok;
- _ ->
+ _ ->
timer:sleep(100),
?line [] = pg2:get_members(a),
?line [] = pg2:get_local_members(a)
@@ -79,6 +99,63 @@ otp_7277(Config) when is_list(Config) ->
-define(UNTIL(Seq), loop_until_true(fun() -> Seq end, Config)).
-define(UNTIL_LOOP, 300).
+otp_8653(suite) -> [];
+otp_8653(doc) ->
+ ["OTP-8259. Member was not removed after being killed."];
+otp_8653(Config) when is_list(Config) ->
+ Timeout = 15,
+ ?line Dog = test_server:timetrap({seconds,Timeout}),
+
+ ?line [A, B, C] = start_nodes([a, b, c], peer, Config),
+
+ ?line wait_for_ready_net(Config),
+
+ % make b and c connected, partitioned from node() and a
+ ?line rpc_cast(B, ?MODULE, part2, [Config, node(), A, C]),
+ ?line ?UNTIL(is_ready_partition(Config)),
+
+ % Connect to the other partition.
+ ?line pong = net_adm:ping(B),
+ timer:sleep(100),
+ ?line pong = net_adm:ping(C),
+ ?line _ = global:sync(),
+ ?line [A, B, C] = lists:sort(nodes()),
+
+ G = pg2_otp_8653,
+ ?line ?UNTIL(begin
+ GA = lists:sort(rpc:call(A, pg2, get_members, [G])),
+ GB = lists:sort(rpc:call(B, pg2, get_members, [G])),
+ GC = lists:sort(rpc:call(C, pg2, get_members, [G])),
+ GT = lists:sort(pg2:get_members(G)),
+ GA =:= GB andalso
+ GB =:= GC andalso
+ GC =:= GT andalso
+ 8 =:= length(GA)
+ end),
+ ?line ok = pg2:delete(G),
+ ?line stop_nodes([A,B,C]),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+part2(Config, Main, A, C) ->
+ Function = mk_part_node_and_group,
+ case catch begin
+ make_partition(Config, [Main, A], [node(), C], Function)
+ end
+ of
+ ok -> ok
+ end.
+
+mk_part_node_and_group(File, MyPart0, Config) ->
+ touch(File, "start"), % debug
+ MyPart = lists:sort(MyPart0),
+ ?UNTIL(is_node_in_part(File, MyPart)),
+ G = pg2_otp_8653,
+ Pid = spawn(forever()),
+ ok = pg2:create(G),
+ _ = [ok = pg2:join(G, Pid) || _ <- [1,1]],
+ touch(File, "done").
+
otp_8259(suite) -> [];
otp_8259(doc) ->
["OTP-8259. Member was not removed after being killed."];
@@ -102,7 +179,7 @@ otp_8259(Config) when is_list(Config) ->
% make b and c connected, partitioned from node() and a
?line rpc_cast(B, ?MODULE, part1, [Config, node(), A, C, Name]),
?line ?UNTIL(is_ready_partition(Config)),
-
+
% Connect to the other partition.
% The resolver on node b will be called.
?line pong = net_adm:ping(B),
@@ -140,9 +217,9 @@ start_proc(Name) ->
p_init(Parent, Name, TestServer) ->
Resolve = fun(_Name, Pid1, Pid2) ->
%% The pid on node a will be chosen.
- [{_,Min}, {_,Max}] =
+ [{_,Min}, {_,Max}] =
lists:sort([{node(Pid1),Pid1}, {node(Pid2),Pid2}]),
- %% b is connected to test_server.
+ %% b is connected to test_server.
%% exit(Min, kill), % would ping a
rpc:cast(TestServer, erlang, exit, [Min, kill]),
Max
@@ -165,7 +242,7 @@ compat(Config) when is_list(Config) ->
true ->
Timeout = 15,
?line Dog = test_server:timetrap({seconds,Timeout}),
- Pid = spawn(forever()),
+ Pid = spawn(forever()),
G = a,
?line ok = pg2:create(G),
?line ok = pg2:join(G, Pid),
@@ -365,7 +442,7 @@ killit(N, P, Ps, Ns) ->
timer:sleep(100),
sane(Ns),
lists:keydelete(P, 1, Ps).
-
+
pr(Node, C) ->
_ = [?t:format("~p: ", [Node]) || Node =/= node()],
?t:format("do ~p~n", [C]).
@@ -412,27 +489,27 @@ sane(Ns) ->
wsane(Ns) ->
%% Same members on all nodes:
- {[_],gs} =
+ {[_],gs} =
{lists:usort([rpc:call(N, pg2, which_groups, []) || N <- Ns]),gs},
- _ = [{[_],ms,G} = {lists:usort([rpc:call(N, pg2, get_members, [G]) ||
+ _ = [{[_],ms,G} = {lists:usort([rpc:call(N, pg2, get_members, [G]) ||
N <- Ns]),ms,G} ||
G <- pg2:which_groups()],
%% The local members are a partitioning of the members:
- [begin
- LocalMembers =
+ [begin
+ LocalMembers =
lists:sort(lists:append(
- [rpc:call(N, pg2, get_local_members, [G]) ||
+ [rpc:call(N, pg2, get_local_members, [G]) ||
N <- Ns])),
{part, LocalMembers} = {part, lists:sort(pg2:get_members(G))}
end || G <- pg2:which_groups()],
%% The closest pid should run on the local node, if possible.
[[case rpc:call(N, pg2, get_closest_pid, [G]) of
Pid when is_pid(Pid), node(Pid) =:= N ->
- true =
+ true =
lists:member(Pid, rpc:call(N, pg2, get_local_members, [G]));
%% FIXME. Om annan nod: member, local = [].
_ -> [] = rpc:call(N, pg2, get_local_members, [G])
- end || N <- Ns]
+ end || N <- Ns]
|| G <- pg2:which_groups()].
%% Look inside the pg2_table.
@@ -482,9 +559,9 @@ start_node_rel(Name, Rel, How) ->
{RelList, ""}
end,
?line Pa = filename:dirname(code:which(?MODULE)),
- ?line Res = test_server:start_node(Name, How,
+ ?line Res = test_server:start_node(Name, How,
[{args,
- Compat ++
+ Compat ++
" -kernel net_setuptime 100 "
" -pa " ++ Pa},
{erl, Release}]),
@@ -575,29 +652,30 @@ get_known(Node) ->
case catch gen_server:call({global_name_server,Node},get_known,infinity) of
{'EXIT', _} ->
[list, without, nodenames];
- Known when is_list(Known) ->
+ Known when is_list(Known) ->
lists:sort([Node | Known])
end.
node_name(Name, Config) ->
U = "_",
{{Y,M,D}, {H,Min,S}} = calendar:now_to_local_time(now()),
- Date = io_lib:format("~4w_~2..0w_~2..0w__~2..0w_~2..0w_~2..0w",
+ Date = io_lib:format("~4w_~2..0w_~2..0w__~2..0w_~2..0w_~2..0w",
[Y,M,D, H,Min,S]),
L = lists:flatten(Date),
lists:concat([Name,U,?testcase,U,U,L]).
-%% this one runs on one node in Part2
-%% The partition is ready when is_ready_partition(Config) returns (true).
-%% this one runs on one node in Part2
+%% This one runs on one node in Part2.
%% The partition is ready when is_ready_partition(Config) returns (true).
make_partition(Config, Part1, Part2) ->
+ make_partition(Config, Part1, Part2, mk_part_node).
+
+make_partition(Config, Part1, Part2, Function) ->
Dir = ?config(priv_dir, Config),
- Ns = [begin
+ Ns = [begin
Name = lists:concat([atom_to_list(N),"_",msec(),".part"]),
File = filename:join([Dir, Name]),
file:delete(File),
- rpc_cast(N, ?MODULE, mk_part_node, [File, Part, Config], File),
+ rpc_cast(N, ?MODULE, Function, [File, Part, Config], File),
{N, File}
end || Part <- [Part1, Part2], N <- Part],
all_nodes_files(Ns, "done", Config),
@@ -614,10 +692,10 @@ mk_part_node(File, MyPart0, Config) ->
%% The calls to append_to_file are for debugging.
is_node_in_part(File, MyPart) ->
- lists:foreach(fun(N) ->
+ lists:foreach(fun(N) ->
_ = erlang:disconnect_node(N)
end, nodes() -- MyPart),
- case {(Known = get_known(node())) =:= MyPart,
+ case {(Known = get_known(node())) =:= MyPart,
(Nodes = lists:sort([node() | nodes()])) =:= MyPart} of
{true, true} ->
%% Make sure the resolvers have been terminated,
@@ -649,7 +727,7 @@ wait_for_ready_net(Nodes0, Config) ->
?t:format("wait_for_ready_net ~p~n", [Nodes]),
?UNTIL(begin
lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and
- lists:all(fun(N) ->
+ lists:all(fun(N) ->
LNs = rpc:call(N, erlang, nodes, []),
Nodes =:= lists:sort([N | LNs])
end, Nodes)
@@ -688,11 +766,11 @@ file_contents(File, ContentsList, Config) ->
file_contents(File, ContentsList, Config, no_log_file).
file_contents(File, ContentsList, Config, LogFile) ->
- Contents = list_to_binary(ContentsList),
+ Contents = list_to_binary(ContentsList),
Sz = size(Contents),
?UNTIL(begin
case file:read_file(File) of
- {ok, FileContents}=Reply ->
+ {ok, FileContents}=Reply ->
case catch split_binary(FileContents, Sz) of
{Contents,_} ->
true;
diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl
index 6badbb5090..a04ea3cdcd 100644
--- a/lib/kernel/test/prim_file_SUITE.erl
+++ b/lib/kernel/test/prim_file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,38 +17,40 @@
%% %CopyrightEnd%
%%
-module(prim_file_SUITE).
--export([all/1,
- init/1, fini/1,
- read_write_file/1, dirs/1, files/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ read_write_file/1]).
-export([cur_dir_0a/1, cur_dir_0b/1,
cur_dir_1a/1, cur_dir_1b/1,
make_del_dir_a/1, make_del_dir_b/1,
- pos/1, pos1/1, pos2/1]).
+ pos1/1, pos2/1]).
-export([close/1,
delete_a/1, delete_b/1]).
--export([open/1, open1/1, modes/1]).
--export([file_info/1,
- file_info_basic_file_a/1, file_info_basic_file_b/1,
- file_info_basic_directory_a/1, file_info_basic_directory_b/1,
- file_info_bad_a/1, file_info_bad_b/1,
- file_info_times_a/1, file_info_times_b/1,
- file_write_file_info_a/1, file_write_file_info_b/1]).
+-export([ open1/1, modes/1]).
+-export([
+ file_info_basic_file_a/1, file_info_basic_file_b/1,
+ file_info_basic_directory_a/1, file_info_basic_directory_b/1,
+ file_info_bad_a/1, file_info_bad_b/1,
+ file_info_times_a/1, file_info_times_b/1,
+ file_write_file_info_a/1, file_write_file_info_b/1]).
-export([rename_a/1, rename_b/1,
- access/1, truncate/1, sync/1,
- read_write/1, pread_write/1, append/1]).
--export([errors/1, e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
+ access/1, truncate/1, datasync/1, sync/1,
+ read_write/1, pread_write/1, append/1, exclusive/1]).
+-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
--export([compression/1, read_not_really_compressed/1,
- read_compressed/1, write_compressed/1,
- compress_errors/1]).
+-export([ read_not_really_compressed/1,
+ read_compressed/1, write_compressed/1,
+ compress_errors/1]).
--export([links/1,
- make_link_a/1, make_link_b/1,
- read_link_info_for_non_link/1,
- symlinks_a/1, symlinks_b/1,
- list_dir_limit/1]).
+-export([
+ make_link_a/1, make_link_b/1,
+ read_link_info_for_non_link/1,
+ symlinks_a/1, symlinks_b/1,
+ list_dir_limit/1]).
--include("test_server.hrl").
+-export([advise/1]).
+
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
-define(PRIM_FILE, prim_file).
@@ -65,14 +67,47 @@
_ -> apply(?PRIM_FILE, F, [H | A])
end).
-all(suite) -> {req, [kernel],
- {conf, init,
- [read_write_file, dirs, files,
- delete_a, delete_b, rename_a, rename_b, errors,
- compression, links, list_dir_limit],
- fini}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [read_write_file, {group, dirs}, {group, files},
+ delete_a, delete_b, rename_a, rename_b, {group, errors},
+ {group, compression}, {group, links}, list_dir_limit].
+
+groups() ->
+ [{dirs, [],
+ [make_del_dir_a, make_del_dir_b, cur_dir_0a, cur_dir_0b,
+ cur_dir_1a, cur_dir_1b]},
+ {files, [],
+ [{group, open}, {group, pos}, {group, file_info},
+ truncate, sync, datasync, advise]},
+ {open, [],
+ [open1, modes, close, access, read_write, pread_write,
+ append, exclusive]},
+ {pos, [], [pos1, pos2]},
+ {file_info, [],
+ [file_info_basic_file_a, file_info_basic_file_b,
+ file_info_basic_directory_a,
+ file_info_basic_directory_b, file_info_bad_a,
+ file_info_bad_b, file_info_times_a, file_info_times_b,
+ file_write_file_info_a, file_write_file_info_b]},
+ {errors, [],
+ [e_delete, e_rename, e_make_dir, e_del_dir]},
+ {compression, [],
+ [read_compressed, read_not_really_compressed,
+ write_compressed, compress_errors]},
+ {links, [],
+ [make_link_a, make_link_b, read_link_info_for_non_link,
+ symlinks_a, symlinks_b]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-init(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
Priv = ?config(priv_dir, Config),
@@ -89,7 +124,7 @@ init(Config) when is_list(Config) ->
Config
end.
-fini(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
os:cmd("subst z: /d");
@@ -188,9 +223,6 @@ read_write_file(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-dirs(suite) -> [make_del_dir_a, make_del_dir_b,
- cur_dir_0a, cur_dir_0b,
- cur_dir_1a, cur_dir_1b].
make_del_dir_a(suite) -> [];
make_del_dir_a(doc) -> [];
@@ -380,10 +412,7 @@ win_cur_dir_1(_Config, Handle) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-files(suite) -> [open,pos,file_info,truncate,sync].
-open(suite) -> [open1,modes,close,access,read_write,
- pread_write,append].
open1(suite) -> [];
open1(doc) -> [];
@@ -608,9 +637,24 @@ append(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
+exclusive(suite) -> [];
+exclusive(doc) -> "Test exclusive access to a file.";
+exclusive(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line RootDir = ?config(priv_dir,Config),
+ ?line NewDir = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_exclusive"),
+ ?line ok = ?PRIM_FILE:make_dir(NewDir),
+ ?line Name = filename:join(NewDir, "ex_file.txt"),
+ ?line {ok,Fd} = ?PRIM_FILE:open(Name, [write, exclusive]),
+ ?line {error, eexist} = ?PRIM_FILE:open(Name, [write, exclusive]),
+ ?line ok = ?PRIM_FILE:close(Fd),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-pos(suite) -> [pos1,pos2].
pos1(suite) -> [];
pos1(doc) -> [];
@@ -698,12 +742,6 @@ pos2(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-file_info(suite) -> [file_info_basic_file_a, file_info_basic_file_b,
- file_info_basic_directory_a,
- file_info_basic_directory_b,
- file_info_bad_a, file_info_bad_b,
- file_info_times_a, file_info_times_b,
- file_write_file_info_a, file_write_file_info_b].
file_info_basic_file_a(suite) -> [];
file_info_basic_file_a(doc) -> [];
@@ -1064,6 +1102,24 @@ truncate(Config) when is_list(Config) ->
ok.
+datasync(suite) -> [];
+datasync(doc) -> "Tests that ?PRIM_FILE:datasync/1 at least doesn't crash.";
+datasync(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Sync = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_sync.fil"),
+
+ %% Raw open.
+ ?line {ok, Fd} = ?PRIM_FILE:open(Sync, [write]),
+ ?line ok = ?PRIM_FILE:datasync(Fd),
+ ?line ok = ?PRIM_FILE:close(Fd),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
sync(suite) -> [];
sync(doc) -> "Tests that ?PRIM_FILE:sync/1 at least doesn't crash.";
sync(Config) when is_list(Config) ->
@@ -1082,6 +1138,77 @@ sync(Config) when is_list(Config) ->
ok.
+advise(suite) -> [];
+advise(doc) -> "Tests that ?PRIM_FILE:advise/4 at least doesn't crash.";
+advise(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(5)),
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line Advise = filename:join(PrivDir,
+ atom_to_list(?MODULE)
+ ++"_advise.fil"),
+
+ Line1 = "Hello\n",
+ Line2 = "World!\n",
+
+ ?line {ok, Fd} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:advise(Fd, 0, 0, normal),
+ ?line ok = ?PRIM_FILE:write(Fd, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd),
+
+ ?line {ok, Fd2} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:advise(Fd2, 0, 0, random),
+ ?line ok = ?PRIM_FILE:write(Fd2, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd2, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd2),
+
+ ?line {ok, Fd3} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:advise(Fd3, 0, 0, sequential),
+ ?line ok = ?PRIM_FILE:write(Fd3, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd3, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd3),
+
+ ?line {ok, Fd4} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:advise(Fd4, 0, 0, will_need),
+ ?line ok = ?PRIM_FILE:write(Fd4, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd4, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd4),
+
+ ?line {ok, Fd5} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:advise(Fd5, 0, 0, dont_need),
+ ?line ok = ?PRIM_FILE:write(Fd5, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd5, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd5),
+
+ ?line {ok, Fd6} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:advise(Fd6, 0, 0, no_reuse),
+ ?line ok = ?PRIM_FILE:write(Fd6, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd6, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd6),
+
+ ?line {ok, Fd7} = ?PRIM_FILE:open(Advise, [write]),
+ ?line {error, einval} = ?PRIM_FILE:advise(Fd7, 0, 0, bad_advise),
+ ?line ok = ?PRIM_FILE:close(Fd7),
+
+ %% test write without advise, then a read after an advise
+ ?line {ok, Fd8} = ?PRIM_FILE:open(Advise, [write]),
+ ?line ok = ?PRIM_FILE:write(Fd8, Line1),
+ ?line ok = ?PRIM_FILE:write(Fd8, Line2),
+ ?line ok = ?PRIM_FILE:close(Fd8),
+ ?line {ok, Fd9} = ?PRIM_FILE:open(Advise, [read]),
+ Offset = 0,
+ %% same as a 0 length in some implementations
+ Length = length(Line1) + length(Line2),
+ ?line ok = ?PRIM_FILE:advise(Fd9, Offset, Length, sequential),
+ ?line {ok, Line1} = ?PRIM_FILE:read_line(Fd9),
+ ?line {ok, Line2} = ?PRIM_FILE:read_line(Fd9),
+ ?line eof = ?PRIM_FILE:read_line(Fd9),
+ ?line ok = ?PRIM_FILE:close(Fd9),
+
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
delete_a(suite) -> [];
@@ -1191,7 +1318,6 @@ rename(Config, Handle, Suffix) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-errors(suite) -> [e_delete, e_rename, e_make_dir, e_del_dir].
e_delete(suite) -> [];
e_delete(doc) -> [];
@@ -1443,8 +1569,6 @@ e_del_dir(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-compression(suite) -> [read_compressed, read_not_really_compressed,
- write_compressed, compress_errors].
%% Trying reading and positioning from a compressed file.
@@ -1597,11 +1721,6 @@ compress_errors(Config) when is_list(Config) ->
?line test_server:timetrap_cancel(Dog),
ok.
-links(doc) -> "Test the link functions.";
-links(suite) ->
- [make_link_a, make_link_b,
- read_link_info_for_non_link,
- symlinks_a, symlinks_b].
make_link_a(doc) -> "Test creating a hard link.";
make_link_a(suite) -> [];
diff --git a/lib/kernel/test/ram_file_SUITE.erl b/lib/kernel/test/ram_file_SUITE.erl
index 798a37d3dc..9b3fbb91fc 100644
--- a/lib/kernel/test/ram_file_SUITE.erl
+++ b/lib/kernel/test/ram_file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,14 +19,15 @@
-module(ram_file_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
%% init/1, fini/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([open_modes/1, open_old_modes/1, pread_pwrite/1, position/1,
truncate/1, sync/1, get_set_file/1, compress/1, uuencode/1,
large_file_errors/1, large_file_light/1, large_file_heavy/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
-define(FILE_MODULE, file). % Name of module to test
@@ -34,11 +35,29 @@
%%--------------------------------------------------------------------------
-all(suite) ->
- [open_modes, open_old_modes, pread_pwrite, position,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [open_modes, open_old_modes, pread_pwrite, position,
truncate, sync, get_set_file, compress, uuencode,
large_file_errors, large_file_light, large_file_heavy].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Time =
case Func of
@@ -51,7 +70,7 @@ init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
%% error_logger:info_msg("~p:~p *****~n", [?MODULE, Func]),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
%% error_logger:info_msg("~p:~p END *****~n", [?MODULE, Func]),
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog).
diff --git a/lib/kernel/test/rpc_SUITE.erl b/lib/kernel/test/rpc_SUITE.erl
index 2b7de40797..7adef49014 100644
--- a/lib/kernel/test/rpc_SUITE.erl
+++ b/lib/kernel/test/rpc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,8 @@
%%
-module(rpc_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([call/1, block_call/1, multicall/1, multicall_timeout/1,
multicall_dies/1, multicall_node_dies/1,
called_dies/1, called_node_dies/1,
@@ -26,13 +27,31 @@
-export([suicide/2, suicide/3, f/0, f2/0]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [call, block_call, multicall, multicall_timeout,
+ multicall_dies, multicall_node_dies, called_dies,
+ called_node_dies, called_throws, call_benchmark,
+ async_call].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [call, block_call, multicall, multicall_timeout,
- multicall_dies, multicall_node_dies,
- called_dies, called_node_dies,
- called_throws, call_benchmark, async_call].
call(doc) -> "Test different rpc calls";
diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl
index b557c7fb1e..47eeb4df4c 100644
--- a/lib/kernel/test/seq_trace_SUITE.erl
+++ b/lib/kernel/test/seq_trace_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,9 @@
%%
-module(seq_trace_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2]).
-export([token_set_get/1, tracer_set_get/1, print/1,
send/1, distributed_send/1, recv/1, distributed_recv/1,
trace_exit/1, distributed_exit/1, call/1, port/1,
@@ -29,21 +31,40 @@
start_tracer/0, stop_tracer/1,
do_match_set_seq_token/1, do_gc_seq_token/1, countdown_start/2]).
-%-define(line_trace, 1).
--include("test_server.hrl").
+ %-define(line_trace, 1).
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(1)).
-all(suite) -> [token_set_get, tracer_set_get, print,
- send, distributed_send, recv, distributed_recv,
- trace_exit, distributed_exit, call, port,
- match_set_seq_token, gc_seq_token].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [token_set_get, tracer_set_get, print, send,
+ distributed_send, recv, distributed_recv, trace_exit,
+ distributed_exit, call, port, match_set_seq_token,
+ gc_seq_token].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog = test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/kernel/test/topApp.erl b/lib/kernel/test/topApp.erl
index acf98e6da0..f44e99f738 100644
--- a/lib/kernel/test/topApp.erl
+++ b/lib/kernel/test/topApp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/topApp2.erl b/lib/kernel/test/topApp2.erl
index 4587910ff3..b791d4a914 100644
--- a/lib/kernel/test/topApp2.erl
+++ b/lib/kernel/test/topApp2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/topApp3.erl b/lib/kernel/test/topApp3.erl
index 1bb6f2f31a..456ef5b2fb 100644
--- a/lib/kernel/test/topApp3.erl
+++ b/lib/kernel/test/topApp3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/kernel/test/wrap_log_reader_SUITE.erl b/lib/kernel/test/wrap_log_reader_SUITE.erl
index ceac593e44..ffc8def626 100644
--- a/lib/kernel/test/wrap_log_reader_SUITE.erl
+++ b/lib/kernel/test/wrap_log_reader_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,31 +28,53 @@
-define(config(X,Y), foo).
-define(t,test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(privdir(Conf), ?config(priv_dir, Conf)).
-endif.
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
no_file/1,
- one/1, one_empty/1, one_filled/1,
- two/1, two_filled/1,
- four/1, four_filled/1,
- wrap/1, wrap_filled/1,
+ one_empty/1, one_filled/1,
+ two_filled/1,
+ four_filled/1,
+ wrap_filled/1,
wrapping/1,
external/1,
error/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [no_file, {group, one}, {group, two}, {group, four},
+ {group, wrap}, wrapping, external, error].
+
+groups() ->
+ [{one, [], [one_empty, one_filled]},
+ {two, [], [two_filled]}, {four, [], [four_filled]},
+ {wrap, [], [wrap_filled]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [no_file, one, two, four, wrap, wrapping, external, error].
init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
Dog=?t:timetrap(?t:seconds(60)),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Func, _Config) ->
+end_per_testcase(_Func, _Config) ->
Dog=?config(watchdog, _Config),
?t:timetrap_cancel(Dog).
@@ -76,8 +98,6 @@ no_file(Conf) when is_list(Conf) ->
delete_files(File),
ok.
-one(suite) -> [one_empty, one_filled];
-one(doc) -> ["One index file"].
one_empty(suite) -> [];
one_empty(doc) -> ["One empty index file"];
@@ -139,8 +159,6 @@ test_one(File) ->
{chunk, 1, ["first round, two"]}, eof], wlt, ?LINE),
ok.
-two(suite) -> [two_filled];
-two(doc) -> ["Two index files"].
two_filled(suite) -> [];
two_filled(doc) -> ["Two filled index files"];
@@ -181,8 +199,6 @@ test_two(File) ->
{chunk, 2, ["first round, 12"]}, eof], wlt, ?LINE),
ok.
-four(suite) -> [four_filled];
-four(doc) -> ["Four index files"].
four_filled(suite) -> [];
four_filled(doc) -> ["Four filled index files"];
@@ -226,8 +242,6 @@ test_four(File) ->
{chunk, 2, ["first round, 42"]}, eof], wlt, ?LINE),
ok.
-wrap(suite) -> [wrap_filled];
-wrap(doc) -> ["Wrap index file, first wrapping"].
wrap_filled(suite) -> [];
wrap_filled(doc) -> ["First wrap, open, filled index file"];
diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl
index f20c9a176b..9eb84c9167 100644
--- a/lib/kernel/test/zlib_SUITE.erl
+++ b/lib/kernel/test/zlib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
-module(zlib_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-compile(export_all).
@@ -48,7 +48,7 @@
init_per_testcase(_Func, Config) ->
Dog = test_server:timetrap(test_server:seconds(60)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
@@ -69,33 +69,40 @@ error(Format, Args, File, Line) ->
%% end,
%% log("<>ERROR<>~n" ++ Format, Args, File, Line).
-all(suite) ->
- [api, examples, func, smp, otp_7359].
-
-api(doc) -> "Basic the api tests";
-api(suite) ->
- [api_open_close,
- api_deflateInit,
- api_deflateSetDictionary,
- api_deflateReset,
- api_deflateParams,
- api_deflate,
- api_deflateEnd,
- api_inflateInit,
- api_inflateSetDictionary,
- api_inflateSync,
- api_inflateReset,
- api_inflate,
- api_inflateEnd,
- api_setBufsz,
- api_getBufsz,
- api_crc32,
- api_adler32,
- api_getQSize,
- api_un_compress,
- api_un_zip,
-% api_g_un_zip_file,
- api_g_un_zip].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, api}, {group, examples}, {group, func}, smp,
+ otp_7359].
+
+groups() ->
+ [{api, [],
+ [api_open_close, api_deflateInit,
+ api_deflateSetDictionary, api_deflateReset,
+ api_deflateParams, api_deflate, api_deflateEnd,
+ api_inflateInit, api_inflateSetDictionary,
+ api_inflateSync, api_inflateReset, api_inflate,
+ api_inflateEnd, api_setBufsz, api_getBufsz, api_crc32,
+ api_adler32, api_getQSize, api_un_compress, api_un_zip,
+ api_g_un_zip]},
+ {examples, [], [intro]},
+ {func, [],
+ [zip_usage, gz_usage, gz_usage2, compress_usage,
+ dictionary_usage, large_deflate, crc, adler]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
api_open_close(doc) -> "Test open/0 and close/1";
api_open_close(suite) -> [];
@@ -517,11 +524,6 @@ bad_len_data() ->
%% zlib:zip(<<42>>), one byte changed.
<<31,139,8,0,0,0,0,0,0,3,211,2,0,91,38,185,9,2,0,0,0>>.
-examples(doc) -> "Test the doc examples";
-examples(suite) ->
- [
- intro
- ].
intro(suite) -> [];
intro(doc) -> "";
@@ -551,15 +553,6 @@ intro(Config) when is_list(Config) ->
Orig = list_to_binary(lists:duplicate(5, D)),
?m(Orig, zlib:uncompress(Res)).
-func(doc) -> "Test the functionality";
-func(suite) ->
- [zip_usage, gz_usage, gz_usage2, compress_usage,
- dictionary_usage,
- large_deflate,
- %% inflateSync,
- crc,
- adler
- ].
large_deflate(doc) -> "Test deflate large file, which had a bug reported on erlang-bugs";
large_deflate(suite) -> [];
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 64a358f114..e7b71cc168 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1,20 +1 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-KERNEL_VSN = 2.14
+KERNEL_VSN = 2.14.4
diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile
index d4698eb558..10efaf667f 100644
--- a/lib/megaco/Makefile
+++ b/lib/megaco/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -97,12 +97,19 @@ endif
CONFIGURE_OPTS = $(FLEX_SCANNER_LINENO_ENABLER) $(FLEX_SCANNER_REENTRANT_ENABLER)
+MEGACO_DIA_PLT = ./priv/megaco.plt
+MEGACO_DIA_PLT_LOG = $(basename $(MEGACO_DIA_PLT)).dialyzer_plt_log
+MEGACO_DIA_LOG = $(basename $(MEGACO_DIA_PLT)).dialyzer_log
+
# ----------------------------------------------------
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+.PHONY: reconf conf dconf econf configure setup info version \
+ app_install dialyzer
+
reconf:
(cd $(ERL_TOP) && \
./otp_build autoconf && \
@@ -132,6 +139,10 @@ info:
@echo "APP_TAR_FILE: $(APP_TAR_FILE)"
@echo "OTP_INSTALL_DIR: $(OTP_INSTALL_DIR)"
@echo "APP_INSTALL_DIR: $(APP_INSTALL_DIR)"
+ @echo ""
+ @echo "MEGACO_PLT = $(MEGACO_PLT)"
+ @echo "MEGACO_DIA_LOG = $(MEGACO_DIA_LOG)"
+ @echo ""
version:
@echo "$(VSN)"
@@ -190,9 +201,18 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
-dialyzer:
- (cd ./ebin; \
- dialyzer --build_plt \
- --output_plt ../priv/megaco.plt \
- -r ../../megaco/ebin \
- --verbose)
+dialyzer_plt: $(MEGACO_DIA_PLT)
+
+$(MEGACO_DIA_PLT):
+ @echo "Building megaco plt file"
+ @dialyzer --build_plt \
+ --output_plt $@ \
+ -r ../megaco/ebin \
+ -o $(MEGACO_DIA_PLT_LOG) \
+ --verbose
+
+dialyzer: $(MEGACO_DIA_PLT)
+ (dialyzer --plt $< \
+ -o $(MEGACO_DIA_LOG) \
+ ../megaco/ebin \
+ && (shell cat $(MEGACO_DIA_LOG)))
diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile
index 2355a1b8b9..4b3c117b20 100644
--- a/lib/megaco/doc/src/Makefile
+++ b/lib/megaco/doc/src/Makefile
@@ -125,6 +125,12 @@ DVIPS_FLAGS +=
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
+$(HTMLDIR)/%.jpg: %.jpg
+ $(INSTALL_DATA) $< $@
+
+$(HTMLDIR)/%.png: %.png
+ $(INSTALL_DATA) $< $@
+
ifdef DOCSUPPORT
docs: pdf html man
@@ -135,7 +141,7 @@ $(TOP_PDF_FILE): $(XML_FILES)
pdf: $(TOP_PDF_FILE)
-html: gifs $(HTML_REF_MAN_FILE)
+html: imgs $(HTML_REF_MAN_FILE)
clean clean_docs: clean_html clean_man
rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
@@ -149,7 +155,7 @@ else
ifeq ($(DOCTYPE),ps)
docs: ps
else
-docs: html gifs man
+docs: html imgs man
endif
endif
@@ -157,7 +163,7 @@ pdf: $(TOP_PDF_FILE)
ps: $(TOP_PS_FILE)
-html: gifs $(HTML_FILES) $(TOP_HTML_FILES)
+html: imgs $(HTML_FILES) $(TOP_HTML_FILES)
mhtml: html $(HTML_REF3_FILES) $(HTML_CHAPTER_FILES)
@@ -182,7 +188,7 @@ clean_man:
clean_html:
rm -rf $(HTMLDIR)/*
-gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+imgs: $(IMG_FILES:%=$(HTMLDIR)/%)
man: $(MAN3_FILES)
@@ -208,7 +214,7 @@ info:
@echo "XML_REF3_FILES = $(XML_REF3_FILES)"
@echo "XML_CHAPTER_FILES = $(XML_CHAPTER_FILES)"
@echo ""
- @echo "GIF_FILES = $(GIF_FILES)"
+ @echo "IMG_FILES = $(IMG_FILES)"
@echo ""
@echo "TEX_FILES_USERS_GUIDE = $(TEX_FILES_USERS_GUIDE)"
@echo "TEX_FILES_REF_MAN = $(TEX_FILES_REF_MAN)"
@@ -258,7 +264,7 @@ release_docs_spec: ps
else
release_docs_spec: docs
$(INSTALL_DIR) $(RELSYSDIR)/doc/html
- $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
+ $(INSTALL_DATA) $(IMG_FILES) $(EXTRA_FILES) $(HTML_FILES) \
$(RELSYSDIR)/doc/html
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
diff --git a/lib/megaco/doc/src/files.mk b/lib/megaco/doc/src/files.mk
index debc5d278d..efacb7e422 100644
--- a/lib/megaco/doc/src/files.mk
+++ b/lib/megaco/doc/src/files.mk
@@ -1,20 +1,20 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
XML_APPLICATION_FILES = \
@@ -56,7 +56,7 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
-GIF_FILES = \
+IMG_FILES = \
single_node_config.gif \
distr_node_config.gif \
megaco_sys_arch.gif \
@@ -70,4 +70,4 @@ GIF_FILES = \
MG_startup_call_flow.gif \
call_flow.gif \
call_flow_cont.gif \
- mstone1.gif
+ mstone1.jpg
diff --git a/lib/megaco/doc/src/megaco.xml b/lib/megaco/doc/src/megaco.xml
index 0fb9d5aac6..b9bf414299 100644
--- a/lib/megaco/doc/src/megaco.xml
+++ b/lib/megaco/doc/src/megaco.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>megaco</title>
@@ -40,6 +40,16 @@
<section>
<title>DATA TYPES</title>
<code type="none"><![CDATA[
+megaco_mid() = ip4Address() | ip6Address() |
+ domainName() | deviceName() |
+ mtpAddress()
+ip4Address() = #'IP4Address'{}
+ip6Address() = #'IP6Address'{}
+domainName() = #'DomainName'{}
+deviceName() = pathName()
+pathName() = ia5String(1..64)
+mtpAddress() = octetString(2..4)
+
action_request() = #'ActionRequest'{}
action_reply() = #'ActionReply'{}
error_desc() = #'ErrorDescriptor'{}
@@ -652,7 +662,7 @@ megaco_incr_timer() = #megaco_incr_timer{}
<taglist>
<tag><c><![CDATA[none]]></c></tag>
<item>
- <p>Do not segment outgoing reply messages. This is usefull when
+ <p>Do not segment outgoing reply messages. This is useful when
either it is known that messages are never to large or
that the transport protocol can handle such things
on its own (e.g. TCP or SCTP).</p>
@@ -1172,7 +1182,7 @@ megaco_incr_timer() = #megaco_incr_timer{}
<taglist>
<tag><c><![CDATA[none]]></c></tag>
<item>
- <p>Do not segment outgoing reply messages. This is usefull when
+ <p>Do not segment outgoing reply messages. This is useful when
either it is known that messages are never to large or
that the transport protocol can handle such things
on its own (e.g. TCP or SCTP).</p>
diff --git a/lib/megaco/doc/src/megaco_flex_scanner.xml b/lib/megaco/doc/src/megaco_flex_scanner.xml
index eb206e5d13..b79b6384df 100644
--- a/lib/megaco/doc/src/megaco_flex_scanner.xml
+++ b/lib/megaco/doc/src/megaco_flex_scanner.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2001</year><year>2009</year>
+ <year>2001</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -128,7 +128,7 @@ megaco_version() = integer() >= 1
<v>Boolean = boolean()</v>
</type>
<desc>
- <p>Checks if a port is a flex scanner port or not (usefull when
+ <p>Checks if a port is a flex scanner port or not (useful when
if a port exits). </p>
<marker id="scan"></marker>
diff --git a/lib/megaco/doc/src/megaco_performance.xml b/lib/megaco/doc/src/megaco_performance.xml
index 72b5c156ba..eb3d852a19 100644
--- a/lib/megaco/doc/src/megaco_performance.xml
+++ b/lib/megaco/doc/src/megaco_performance.xml
@@ -50,9 +50,15 @@
of these configurations for each codec. The figures presented are
the average of all used messages.</p>
- <p>For comparison, also included are performance figures
- where the flex driver was built as <c>non-reentrant</c> flex
- (figures within parenthesis). </p>
+ <p>For comparison, also included are first, performance figures with
+ megaco (including the measurement software) and asn1 applications
+ hipe-compiled (second figure in the time columns, note that per bin
+ decode had some issues so those figures are not included), and second,
+ performance figures where the flex driver was built as
+ <c>non-reentrant</c> flex
+ (third figure in the time columns,
+ only valid for text codecs using the flex-scanner,
+ figures within parenthesis). </p>
<table>
<row>
@@ -67,122 +73,122 @@
<row>
<cell align="left" valign="middle">pretty</cell>
<cell align="right" valign="middle">336</cell>
- <cell align="right" valign="middle">22</cell>
- <cell align="right" valign="middle">76</cell>
- <cell align="right" valign="middle">98</cell>
+ <cell align="right" valign="middle">20 / 13</cell>
+ <cell align="right" valign="middle">75 / 40</cell>
+ <cell align="right" valign="middle">95 / 53</cell>
</row>
<row>
<cell align="left" valign="middle">pretty [flex]</cell>
<cell align="right" valign="middle">336</cell>
- <cell align="right" valign="middle">22 (22)</cell>
- <cell align="right" valign="middle">41 (40)</cell>
- <cell align="right" valign="middle">63 (62)</cell>
+ <cell align="right" valign="middle">20 / 13 / 20</cell>
+ <cell align="right" valign="middle">39 / 33 / 38</cell>
+ <cell align="right" valign="middle">59 / 46 / 58</cell>
</row>
<!-- COMPACT -->
<row>
<cell align="left" valign="middle">compact</cell>
<cell align="right" valign="middle">181</cell>
- <cell align="right" valign="middle">19</cell>
- <cell align="right" valign="middle">63</cell>
- <cell align="right" valign="middle">82</cell>
+ <cell align="right" valign="middle">17 / 10</cell>
+ <cell align="right" valign="middle">62 / 35</cell>
+ <cell align="right" valign="middle">79 / 45</cell>
</row>
<row>
<cell align="left" valign="middle">compact [flex]</cell>
<cell align="right" valign="middle">181</cell>
- <cell align="right" valign="middle">19 (19)</cell>
- <cell align="right" valign="middle">38 (36)</cell>
- <cell align="right" valign="middle">57 (55)</cell>
+ <cell align="right" valign="middle">17 / 10 / 17</cell>
+ <cell align="right" valign="middle">37 / 31 / 36</cell>
+ <cell align="right" valign="middle">54 / 41 / 53</cell>
</row>
<!-- PER -->
<row>
<cell align="left" valign="middle">per bin</cell>
<cell align="right" valign="middle">91</cell>
- <cell align="right" valign="middle">63</cell>
- <cell align="right" valign="middle">69</cell>
- <cell align="right" valign="middle">132</cell>
+ <cell align="right" valign="middle">60 / 29</cell>
+ <cell align="right" valign="middle">64 / -</cell>
+ <cell align="right" valign="middle">124 / -</cell>
</row>
<row>
<cell align="left" valign="middle">per bin [driver]</cell>
<cell align="right" valign="middle">91</cell>
- <cell align="right" valign="middle">43</cell>
- <cell align="right" valign="middle">45</cell>
- <cell align="right" valign="middle">88</cell>
+ <cell align="right" valign="middle">39 / 24</cell>
+ <cell align="right" valign="middle">42 / 26</cell>
+ <cell align="right" valign="middle">81 / 50</cell>
</row>
<row>
<cell align="left" valign="middle">per bin [native]</cell>
<cell align="right" valign="middle">91</cell>
- <cell align="right" valign="middle">47</cell>
- <cell align="right" valign="middle">51</cell>
- <cell align="right" valign="middle">99</cell>
+ <cell align="right" valign="middle">45 / 21</cell>
+ <cell align="right" valign="middle">48 / -</cell>
+ <cell align="right" valign="middle">93 / -</cell>
</row>
<row>
<cell align="left" valign="middle">per bin [driver,native]</cell>
<cell align="right" valign="middle">91</cell>
- <cell align="right" valign="middle">26</cell>
- <cell align="right" valign="middle">29</cell>
- <cell align="right" valign="middle">55</cell>
+ <cell align="right" valign="middle">25 / 15</cell>
+ <cell align="right" valign="middle">27 / 18</cell>
+ <cell align="right" valign="middle">52 / 33</cell>
</row>
<!-- BER -->
<row>
<cell align="left" valign="middle">ber bin</cell>
<cell align="right" valign="middle">165</cell>
- <cell align="right" valign="middle">35</cell>
- <cell align="right" valign="middle">42</cell>
- <cell align="right" valign="middle">77</cell>
+ <cell align="right" valign="middle">32 / 19</cell>
+ <cell align="right" valign="middle">38 / 21</cell>
+ <cell align="right" valign="middle">70 / 40</cell>
</row>
<row>
<cell align="left" valign="middle">ber bin [driver]</cell>
<cell align="right" valign="middle">165</cell>
- <cell align="right" valign="middle">35</cell>
- <cell align="right" valign="middle">37</cell>
- <cell align="right" valign="middle">72</cell>
+ <cell align="right" valign="middle">32 / 19</cell>
+ <cell align="right" valign="middle">33 / 20</cell>
+ <cell align="right" valign="middle">65 / 39</cell>
</row>
<row>
<cell align="left" valign="middle">ber bin [native]</cell>
<cell align="right" valign="middle">165</cell>
- <cell align="right" valign="middle">19</cell>
- <cell align="right" valign="middle">26</cell>
- <cell align="right" valign="middle">45</cell>
+ <cell align="right" valign="middle">17 / 11</cell>
+ <cell align="right" valign="middle">25 / 13</cell>
+ <cell align="right" valign="middle">42 / 24</cell>
</row>
<row>
<cell align="left" valign="middle">ber bin [driver,native]</cell>
<cell align="right" valign="middle">165</cell>
- <cell align="right" valign="middle">19</cell>
- <cell align="right" valign="middle">20</cell>
- <cell align="right" valign="middle">39</cell>
+ <cell align="right" valign="middle">17 / 11</cell>
+ <cell align="right" valign="middle">17 / 12</cell>
+ <cell align="right" valign="middle">34 / 23</cell>
</row>
<!-- ERLANG -->
<row>
<cell align="left" valign="middle">erl_dist</cell>
<cell align="right" valign="middle">875</cell>
- <cell align="right" valign="middle">5</cell>
- <cell align="right" valign="middle">10</cell>
- <cell align="right" valign="middle">15</cell>
+ <cell align="right" valign="middle">5 / 5</cell>
+ <cell align="right" valign="middle">10 / 10</cell>
+ <cell align="right" valign="middle">15 / 15</cell>
</row>
<row>
<cell align="left" valign="middle">erl_dist [megaco_compressed]</cell>
<cell align="right" valign="middle">405</cell>
- <cell align="right" valign="middle">6</cell>
- <cell align="right" valign="middle">7</cell>
- <cell align="right" valign="middle">13</cell>
+ <cell align="right" valign="middle">6 / 4</cell>
+ <cell align="right" valign="middle">7 / 4</cell>
+ <cell align="right" valign="middle">13 / 8</cell>
</row>
<row>
<cell align="left" valign="middle">erl_dist [compressed]</cell>
<cell align="right" valign="middle">345</cell>
- <cell align="right" valign="middle">86</cell>
- <cell align="right" valign="middle">21</cell>
- <cell align="right" valign="middle">107</cell>
+ <cell align="right" valign="middle">47 / 47</cell>
+ <cell align="right" valign="middle">20 / 20</cell>
+ <cell align="right" valign="middle">67 / 67</cell>
</row>
<row>
<cell align="left" valign="middle">erl_dist [megaco_compressed,compressed]</cell>
<cell align="right" valign="middle">200</cell>
- <cell align="right" valign="middle">71</cell>
- <cell align="right" valign="middle">12</cell>
- <cell align="right" valign="middle">83</cell>
+ <cell align="right" valign="middle">34 / 33</cell>
+ <cell align="right" valign="middle">11 / 9</cell>
+ <cell align="right" valign="middle">45 / 42</cell>
</row>
<tcaption>Codec performance</tcaption>
@@ -201,8 +207,8 @@
<p>When running SMP erlang on a multi-core machine the "throughput"
is significantly higher. The mstone1 test is an extreme test,
but it shows what is gained by using the reentrant flex-scanner. </p>
- <image file="mstone1.gif">
- <icaption>MStone1 with mstone1.sh -d flex -s 8</icaption>
+ <image file="mstone1.jpg">
+ <icaption>MStone1 with mstone1.sh -d flex -s 4</icaption>
</image>
</section>
@@ -276,10 +282,10 @@ MEGACO/1 [124.124.124.222]
<section>
<title>Setup</title>
<p>The measurements has been performed on a
- Dell PowerEdge 1950iii with
- 2* Intel Xeon L5430 @ 2.66 GHz, with 8 GB memory and
- running SLES 10 SP2 x86_64, kernel 2.6.16.60-0.34-smp.
- Software versions was open source OTP R13B and megaco-3.11.</p>
+ HP xw4600 Workstation with
+ a Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz, with 4 GB memory and
+ running Ubuntu 10.04 x86_64, kernel 2.6.32-22-generic.
+ Software versions was open source OTP R13B04 (megaco-3.14).</p>
</section>
<section>
@@ -302,7 +308,7 @@ MEGACO/1 [124.124.124.222]
to our fastest text encoder (compact). </p>
</item>
<item>
- <p>our fastest binary decoder (ber) is about 47% (44%) faster than our
+ <p>our fastest binary decoder (ber) is about 54% (61%) faster than our
fastest text decoder (compact). </p>
</item>
</list>
diff --git a/lib/megaco/doc/src/megaco_user.xml b/lib/megaco/doc/src/megaco_user.xml
index 7332fa684d..7987ed3392 100644
--- a/lib/megaco/doc/src/megaco_user.xml
+++ b/lib/megaco/doc/src/megaco_user.xml
@@ -504,7 +504,7 @@ protocol_version() = integer() ]]></code>
not, is also included in the <c><![CDATA[UserReply]]></c>. </p>
<p>The <c><![CDATA[ReplyData]]></c> defaults to
<c><![CDATA[megaco:lookup(ConnHandle, reply_data)]]></c>,
- but may be explicitely overridden by a
+ but may be explicitly overridden by a
<c><![CDATA[megaco:cast/3]]></c> option in order to forward info about the
calling context of the originating process.</p>
<p>At <c><![CDATA[success()]]></c>, the <c><![CDATA[UserReply]]></c> either contains:</p>
diff --git a/lib/megaco/doc/src/mstone1-s8flex.log b/lib/megaco/doc/src/mstone1-s8flex.log
deleted file mode 100644
index d9d28399f3..0000000000
--- a/lib/megaco/doc/src/mstone1-s8flex.log
+++ /dev/null
@@ -1,234 +0,0 @@
-
----------------------------------------------
-Factor 01
-
-erl -noshell -smp +S 8 -pa /ldisk/bmk/pgm/otp-r13b-m311p08-re/lib/erlang/lib/megaco-3.11/examples/meas -s megaco_codec_mstone1 start_flex time_test 01 -s init stop
-
-OS: unix-linux: 2.6.16
-System architecture: x86_64-unknown-linux-gnu
-OTP release: R13B
-System version: Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false]
-Heap type: private
-Global heap size: 0
-Thread support: true
-Thread pool size: 0
-Process limit: 32768
-SMP support: true
-Num schedulers: 8
-Scheduler bindings: {0,1,4,5,2,3,6,7}
-Scheduler bind type: thread_no_node_processor_spread
-Cpu topology: [{processor,[{core,{logical,0}},{core,{logical,4}},{core,{logical,2}},{core,{logical,6}}]},{processor,[{core,{logical,1}},{core,{logical,5}},{core,{logical,3}},{core,{logical,7}}]}]
-Megaco version: megaco-3.11-p08
-ASN.1 version: 1.6.10
-
- * starting runners [16] ................ done
- * await runners ready ................ done
- * now snooze
- * release them
-
-16 runners
-Runner heap size data:
- Min: 75025
- Max: 1682835
- Avg: 582577
-Runner reductions data:
- Min: 927126711
- Max: 5292487523
- Avg: 1929935038
-
-MStone: 63283727
-
----------------------------------------------
-Factor 02
-
-erl -noshell -smp +S 8 -pa /ldisk/bmk/pgm/otp-r13b-m311p08-re/lib/erlang/lib/megaco-3.11/examples/meas -s megaco_codec_mstone1 start_flex time_test 02 -s init stop
-
-OS: unix-linux: 2.6.16
-System architecture: x86_64-unknown-linux-gnu
-OTP release: R13B
-System version: Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false]
-Heap type: private
-Global heap size: 0
-Thread support: true
-Thread pool size: 0
-Process limit: 32768
-SMP support: true
-Num schedulers: 8
-Scheduler bindings: {0,1,4,5,2,3,6,7}
-Scheduler bind type: thread_no_node_processor_spread
-Cpu topology: [{processor,[{core,{logical,0}},{core,{logical,4}},{core,{logical,2}},{core,{logical,6}}]},{processor,[{core,{logical,1}},{core,{logical,5}},{core,{logical,3}},{core,{logical,7}}]}]
-Megaco version: megaco-3.11-p08
-ASN.1 version: 1.6.10
-
- * starting runners [32] ................................ done
- * await runners ready ................................ done
- * now snooze
- * release them
-
-32 runners
-Runner heap size data:
- Min: 75025
- Max: 1346269
- Avg: 388569
-Runner reductions data:
- Min: 645498054
- Max: 2774469009
- Avg: 943407719
-
-MStone: 61441342
-
----------------------------------------------
-Factor 04
-
-erl -noshell -smp +S 8 -pa /ldisk/bmk/pgm/otp-r13b-m311p08-re/lib/erlang/lib/megaco-3.11/examples/meas -s megaco_codec_mstone1 start_flex time_test 04 -s init stop
-
-OS: unix-linux: 2.6.16
-System architecture: x86_64-unknown-linux-gnu
-OTP release: R13B
-System version: Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false]
-Heap type: private
-Global heap size: 0
-Thread support: true
-Thread pool size: 0
-Process limit: 32768
-SMP support: true
-Num schedulers: 8
-Scheduler bindings: {0,1,4,5,2,3,6,7}
-Scheduler bind type: thread_no_node_processor_spread
-Cpu topology: [{processor,[{core,{logical,0}},{core,{logical,4}},{core,{logical,2}},{core,{logical,6}}]},{processor,[{core,{logical,1}},{core,{logical,5}},{core,{logical,3}},{core,{logical,7}}]}]
-Megaco version: megaco-3.11-p08
-ASN.1 version: 1.6.10
-
- * starting runners [64] ................................................................ done
- * await runners ready ................................................................ done
- * now snooze
- * release them
-
-64 runners
-Runner heap size data:
- Min: 75025
- Max: 1682835
- Avg: 462690
-Runner reductions data:
- Min: 395464832
- Max: 916378232
- Avg: 507760636
-
-MStone: 66958216
-
----------------------------------------------
-Factor 08
-
-erl -noshell -smp +S 8 -pa /ldisk/bmk/pgm/otp-r13b-m311p08-re/lib/erlang/lib/megaco-3.11/examples/meas -s megaco_codec_mstone1 start_flex time_test 08 -s init stop
-
-OS: unix-linux: 2.6.16
-System architecture: x86_64-unknown-linux-gnu
-OTP release: R13B
-System version: Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false]
-Heap type: private
-Global heap size: 0
-Thread support: true
-Thread pool size: 0
-Process limit: 32768
-SMP support: true
-Num schedulers: 8
-Scheduler bindings: {0,1,4,5,2,3,6,7}
-Scheduler bind type: thread_no_node_processor_spread
-Cpu topology: [{processor,[{core,{logical,0}},{core,{logical,4}},{core,{logical,2}},{core,{logical,6}}]},{processor,[{core,{logical,1}},{core,{logical,5}},{core,{logical,3}},{core,{logical,7}}]}]
-Megaco version: megaco-3.11-p08
-ASN.1 version: 1.6.10
-
- * starting runners [128] ................................................................................................................................ done
- * await runners ready ................................................................................................................................ done
- * now snooze
- * release them
-
-128 runners
-Runner heap size data:
- Min: 75025
- Max: 832040
- Avg: 173900
-Runner reductions data:
- Min: 236710819
- Max: 457961244
- Avg: 269562568
-
-MStone: 72098418
-
----------------------------------------------
-Factor 16
-
-erl -noshell -smp +S 8 -pa /ldisk/bmk/pgm/otp-r13b-m311p08-re/lib/erlang/lib/megaco-3.11/examples/meas -s megaco_codec_mstone1 start_flex time_test 16 -s init stop
-
-OS: unix-linux: 2.6.16
-System architecture: x86_64-unknown-linux-gnu
-OTP release: R13B
-System version: Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false]
-Heap type: private
-Global heap size: 0
-Thread support: true
-Thread pool size: 0
-Process limit: 32768
-SMP support: true
-Num schedulers: 8
-Scheduler bindings: {0,1,4,5,2,3,6,7}
-Scheduler bind type: thread_no_node_processor_spread
-Cpu topology: [{processor,[{core,{logical,0}},{core,{logical,4}},{core,{logical,2}},{core,{logical,6}}]},{processor,[{core,{logical,1}},{core,{logical,5}},{core,{logical,3}},{core,{logical,7}}]}]
-Megaco version: megaco-3.11-p08
-ASN.1 version: 1.6.10
-
- * starting runners [256] ................................................................................................................................................................................................................................................................ done
- * await runners ready ................................................................................................................................................................................................................................................................ done
- * now snooze
- * release them
-
-256 runners
-Runner heap size data:
- Min: 75025
- Max: 317811
- Avg: 131652
-Runner reductions data:
- Min: 134104991
- Max: 163429204
- Avg: 142654707
-
-MStone: 77139535
-
----------------------------------------------
-Factor 32
-
-erl -noshell -smp +S 8 -pa /ldisk/bmk/pgm/otp-r13b-m311p08-re/lib/erlang/lib/megaco-3.11/examples/meas -s megaco_codec_mstone1 start_flex time_test 32 -s init stop
-
-OS: unix-linux: 2.6.16
-System architecture: x86_64-unknown-linux-gnu
-OTP release: R13B
-System version: Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false]
-Heap type: private
-Global heap size: 0
-Thread support: true
-Thread pool size: 0
-Process limit: 32768
-SMP support: true
-Num schedulers: 8
-Scheduler bindings: {0,1,4,5,2,3,6,7}
-Scheduler bind type: thread_no_node_processor_spread
-Cpu topology: [{processor,[{core,{logical,0}},{core,{logical,4}},{core,{logical,2}},{core,{logical,6}}]},{processor,[{core,{logical,1}},{core,{logical,5}},{core,{logical,3}},{core,{logical,7}}]}]
-Megaco version: megaco-3.11-p08
-ASN.1 version: 1.6.10
-
- * starting runners [512] ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ done
- * await runners ready ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................ done
- * now snooze
- * release them
-
-512 runners
-Runner heap size data:
- Min: 75025
- Max: 196418
- Avg: 107328
-Runner reductions data:
- Min: 71186547
- Max: 74170110
- Avg: 72380653
-
-MStone: 78820851
diff --git a/lib/megaco/doc/src/mstone1.gif b/lib/megaco/doc/src/mstone1.gif
deleted file mode 100644
index 54c9c5514c..0000000000
--- a/lib/megaco/doc/src/mstone1.gif
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/mstone1.jpg b/lib/megaco/doc/src/mstone1.jpg
index b417429a08..3edc65faf1 100644
--- a/lib/megaco/doc/src/mstone1.jpg
+++ b/lib/megaco/doc/src/mstone1.jpg
Binary files differ
diff --git a/lib/megaco/doc/src/mstone1.png b/lib/megaco/doc/src/mstone1.png
deleted file mode 100644
index 19af210abc..0000000000
--- a/lib/megaco/doc/src/mstone1.png
+++ /dev/null
Binary files differ
diff --git a/lib/megaco/doc/src/mstone1.ps b/lib/megaco/doc/src/mstone1.ps
deleted file mode 100644
index 6436a4eb43..0000000000
--- a/lib/megaco/doc/src/mstone1.ps
+++ /dev/null
@@ -1,1959 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: GIMP PostScript file plugin V 1.17 by Peter Kirchgessner
-%%Title: mstone1_html_m5496b992.ps
-%%CreationDate: Fri May 29 19:07:25 2009
-%%DocumentData: Clean7Bit
-%%LanguageLevel: 2
-%%Pages: 1
-%%BoundingBox: 14 14 476 247
-%%EndComments
-%%BeginProlog
-% Use own dictionary to avoid conflicts
-10 dict begin
-%%EndProlog
-%%Page: 1 1
-% Translate for offset
-14.173228346456694 14.173228346456694 translate
-% Translate to begin of first scanline
-0 231.99920747433686 translate
-460.99842519685041 -231.99920747433686 scale
-% Image geometry
-461 232 8
-% Transformation matrix
-[ 461 0 0 232 0 0 ]
-% Strings to hold RGB-samples per scanline
-/rstr 461 string def
-/gstr 461 string def
-/bstr 461 string def
-{currentfile /ASCII85Decode filter /RunLengthDecode filter rstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter gstr readstring pop}
-{currentfile /ASCII85Decode filter /RunLengthDecode filter bstr readstring pop}
-true 3
-%%BeginData: 120502 ASCII Bytes
-colorimage
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-i;[-^"<[I>jo>>[s!S-Pn#nhl!"q57bl%G=s7lKks8;orq>UEis7ZKkpAapfqu?Nmr:^0\rj;e)
-s8Dutrr<#nq!DD]s0Gl?!Ao]1s8;oe!<;Hd"Sr)ls7>sarVlfr/b/l@q#CBds82ck([qD(r;Z+-
-8G3#_q>X&-q#CBlrVZ]fs7lWkrV6<jq"ssfrrVrprVcaFr<)rsrr)lpo`#!n$31)/s8VZin5BJi
-oDZ*k&-j;?o`+scYV-/(\,61)<Vugjs8VP@2$sI+9X"<ms7ZKmo_\Zn>9<i*IeWm8s8W#ss6a>7
-!'%(Ss5mc1$9F^WqXX[`s7QE7?l9"V-$I''!:^$grVccn!WW2js8VWhoDeR_rsA5qr;V'f!%3Qh
-rVnG1s8W&:BG^ja+`,[)rrEc4s7u]mq>Wh_s8Vrqr;ZWnp](3as8)ckq>^KXrr35rs8Vlns8W)t
-ruq.6s7?$[s8VurqZ$9hs7--bmf3=ap\k-lp1<7`r;Zfpir=N~>
-i;[-^"<[I>jo>>[s!S-Pn#nhl!"q57bl%G=s7lKks8;orq>UEis7ZKkpAapfqu?Nmr:^0\rj;e)
-s8Dutrr<#nq!DD]s0Gl?!Ao]1s8;oe!<;Hd"Sr)ls7>sarVlfr/b/l@q#CBds82ck([qD(r;Z+-
-8G3#_q>X&-q#CBlrVZ]fs7lWkrV6<jq"ssfrrVrprVcaFr<)rsrr)lpo`#!n$31)/s8VZin5BJi
-oDZ*k&-j;?o`+scYV-/(\,61)<Vugjs8VP@2$sI+9X"<ms7ZKmo_\Zn>9<i*IeWm8s8W#ss6a>7
-!'%(Ss5mc1$9F^WqXX[`s7QE7?l9"V-$I''!:^$grVccn!WW2js8VWhoDeR_rsA5qr;V'f!%3Qh
-rVnG1s8W&:BG^ja+`,[)rrEc4s7u]mq>Wh_s8Vrqr;ZWnp](3as8)ckq>^KXrr35rs8Vlns8W)t
-ruq.6s7?$[s8VurqZ$9hs7--bmf3=ap\k-lp1<7`r;Zfpir=N~>
-i;[-^"<[I>jo>>[s!S-Pn#nhl!"q57bl%G=s7lKks8;orq>UEis7ZKkpAapfqu?Nmr:^0\rj;e)
-s8Dutrr<#nq!DD]s0Gl?!Ao]1s8;oe!<;Hd"Sr)ls7>sarVlfr/b/l@q#CBds82ck([qD(r;Z+-
-8G3#_q>X&-q#CBlrVZ]fs7lWkrV6<jq"ssfrrVrprVcaFr<)rsrr)lpo`#!n$31)/s8VZin5BJi
-oDZ*k&-j;?o`+scYV-/(\,61)<Vugjs8VP@2$sI+9X"<ms7ZKmo_\Zn>9<i*IeWm8s8W#ss6a>7
-!'%(Ss5mc1$9F^WqXX[`s7QE7?l9"V-$I''!:^$grVccn!WW2js8VWhoDeR_rsA5qr;V'f!%3Qh
-rVnG1s8W&:BG^ja+`,[)rrEc4s7u]mq>Wh_s8Vrqr;ZWnp](3as8)ckq>^KXrr35rs8Vlns8W)t
-ruq.6s7?$[s8VurqZ$9hs7--bmf3=ap\k-lp1<7`r;Zfpir=N~>
-iVtA&rr<bBqu6Wds4%SZs+<S^q"W^NJ3s8@meQb[r9s[crVuors69R`q#::FqZ$Qjrr<#ks/l_1
-r;Z`qs8N&ns8Pa:s7KNEj7gnuGk1l7O91hXnc8^Ws6ose')2G)!<<)is8VZas8VrqrrE)krVmPV
-&W6Sfs*kU(s7G^Ys8;ors8V]hrsAN$rqZTir;ZforVm9("8i,tnGE7bp'gKjs%rUhq>]s9#70bt
-s'(TAs2e'#me,NGYlD]_]Dqa-s8)]oRic.Ls6/p>1H44gs7ZKls7'<Ae,7KeMuWhJs7>OU'qaXY
-Q!44a2h^2\T3:=UruCM-s-G.0iqhZ[?qLA7s8VoprVuI#kl(P[k5Y)Prr`3!o'ZMW0P!u3s56>%
-pAY*cs7lVu-^WlnhTC"&s7lX)XnMbos8C%?!<<)ts8N&js8VQes8)]o"nMKhs7,aYrs/&nq"+Oc
-qY^?m%efr!rr;lqo()MSr;ZHQrr3?"s8C2KpAP$krq5=OJ,~>
-iVtA&rr<bBqu6Wds4%SZs+<S^q"W^NJ3s8@meQb[r9s[crVuors69R`q#::FqZ$Qjrr<#ks/l_1
-r;Z`qs8N&ns8Pa:s7KNEj7gnuGk1l7O91hXnc8^Ws6ose')2G)!<<)is8VZas8VrqrrE)krVmPV
-&W6Sfs*kU(s7G^Ys8;ors8V]hrsAN$rqZTir;ZforVm9("8i,tnGE7bp'gKjs%rUhq>]s9#70bt
-s'(TAs2e'#me,NGYlD]_]Dqa-s8)]oRic.Ls6/p>1H44gs7ZKls7'<Ae,7KeMuWhJs7>OU'qaXY
-Q!44a2h^2\T3:=UruCM-s-G.0iqhZ[?qLA7s8VoprVuI#kl(P[k5Y)Prr`3!o'ZMW0P!u3s56>%
-pAY*cs7lVu-^WlnhTC"&s7lX)XnMbos8C%?!<<)ts8N&js8VQes8)]o"nMKhs7,aYrs/&nq"+Oc
-qY^?m%efr!rr;lqo()MSr;ZHQrr3?"s8C2KpAP$krq5=OJ,~>
-iVtA&rr<bBqu6Wds4%SZs+<S^q"W^NJ3s8@meQb[r9s[crVuors69R`q#::FqZ$Qjrr<#ks/l_1
-r;Z`qs8N&ns8Pa:s7KNEj7gnuGk1l7O91hXnc8^Ws6ose')2G)!<<)is8VZas8VrqrrE)krVmPV
-&W6Sfs*kU(s7G^Ys8;ors8V]hrsAN$rqZTir;ZforVm9("8i,tnGE7bp'gKjs%rUhq>]s9#70bt
-s'(TAs2e'#me,NGYlD]_]Dqa-s8)]oRic.Ls6/p>1H44gs7ZKls7'<Ae,7KeMuWhJs7>OU'qaXY
-Q!44a2h^2\T3:=UruCM-s-G.0iqhZ[?qLA7s8VoprVuI#kl(P[k5Y)Prr`3!o'ZMW0P!u3s56>%
-pAY*cs7lVu-^WlnhTC"&s7lX)XnMbos8C%?!<<)ts8N&js8VQes8)]o"nMKhs7,aYrs/&nq"+Oc
-qY^?m%efr!rr;lqo()MSr;ZHQrr3?"s8C2KpAP$krq5=OJ,~>
-iVsMdrr>bfrr;ors%6/hq@)lhn,E=ko(;q^s8)9bru_14rVuWlqZ$Tcs7cE_s7lWorV?Kn!&)Lr
-r;Z`qs8N#t.'ZYLmeleYs82cG/-#GF7K>jVr"Sl)p@SC]pAb'j)>O7*s8;osp%\Od(]aO7s8DZk
-s)AFjq8OP?nGiIerU]p`s5<nV%JTntp\jaaoDejOs7Q9grs&E(qu?Zqqu6UQ!<;cmo`+C[rV/$r
-$Ma5lo)J[grSmnXnR7@Ts8Rmls8W)unGfm(c27P*s8;QiK1>k?q"Odf!:]se$J-*hkPtP]s-Irp
-rr4S:s+4_[rVuTks7u]ap]&\`dIcf*s7?9jli@"`l2UY\rrE)lrVuoos8Dull2:Q%q#:`hrr;HX
-!WVlnnGi%.&^(.JoDeF^rVua+<^-N_)#aJ;:`9!,nG`I\s8V`hs8VZimf2kXs7QBk(ARe,qu?Hk
-s7lWks8V9^lhUDWs8)ZfqYq*#s8Duis8V]j>.4G(r;ZHMs*t~>
-iVsMdrr>bfrr;ors%6/hq@)lhn,E=ko(;q^s8)9bru_14rVuWlqZ$Tcs7cE_s7lWorV?Kn!&)Lr
-r;Z`qs8N#t.'ZYLmeleYs82cG/-#GF7K>jVr"Sl)p@SC]pAb'j)>O7*s8;osp%\Od(]aO7s8DZk
-s)AFjq8OP?nGiIerU]p`s5<nV%JTntp\jaaoDejOs7Q9grs&E(qu?Zqqu6UQ!<;cmo`+C[rV/$r
-$Ma5lo)J[grSmnXnR7@Ts8Rmls8W)unGfm(c27P*s8;QiK1>k?q"Odf!:]se$J-*hkPtP]s-Irp
-rr4S:s+4_[rVuTks7u]ap]&\`dIcf*s7?9jli@"`l2UY\rrE)lrVuoos8Dull2:Q%q#:`hrr;HX
-!WVlnnGi%.&^(.JoDeF^rVua+<^-N_)#aJ;:`9!,nG`I\s8V`hs8VZimf2kXs7QBk(ARe,qu?Hk
-s7lWks8V9^lhUDWs8)ZfqYq*#s8Duis8V]j>.4G(r;ZHMs*t~>
-iVsMdrr>bfrr;ors%6/hq@)lhn,E=ko(;q^s8)9bru_14rVuWlqZ$Tcs7cE_s7lWorV?Kn!&)Lr
-r;Z`qs8N#t.'ZYLmeleYs82cG/-#GF7K>jVr"Sl)p@SC]pAb'j)>O7*s8;osp%\Od(]aO7s8DZk
-s)AFjq8OP?nGiIerU]p`s5<nV%JTntp\jaaoDejOs7Q9grs&E(qu?Zqqu6UQ!<;cmo`+C[rV/$r
-$Ma5lo)J[grSmnXnR7@Ts8Rmls8W)unGfm(c27P*s8;QiK1>k?q"Odf!:]se$J-*hkPtP]s-Irp
-rr4S:s+4_[rVuTks7u]ap]&\`dIcf*s7?9jli@"`l2UY\rrE)lrVuoos8Dull2:Q%q#:`hrr;HX
-!WVlnnGi%.&^(.JoDeF^rVua+<^-N_)#aJ;:`9!,nG`I\s8V`hs8VZimf2kXs7QBk(ARe,qu?Hk
-s7lWks8V9^lhUDWs8)ZfqYq*#s8Duis8V]j>.4G(r;ZHMs*t~>
-hu>A1^]NWns8UpUgAq7!D=RZ+n,!(^!XA]1qu=Z&&kLmDs7cu%,760Ys8Mqh.K^9HrVmB&!W;ur
-s8W)uqu8sss8)`p./i`:k5bP^F=I8:JGoQKrrBGN!B&+$!!!$$s7QDa0-1[tp&>$ls6ose+nu"/
-/bh7Ss8VWhSfo'pRf<?bb<$_4_Z0Z5!j*[M9Z?]'s#p;_qu?Zqs6T4S!;$6im/Qq^potP)!<<&j
-s7H?hs61U)ru9Php&!_is76-gs6(<As6+D9(1oI>r(2A7rr4,.p&FgUs7bF]s8W#ss6)5Xs7uTm
-rr5gAlMpMVrr;]_rr3@+L]@DRs8Mrjmf*:brsIui$2sc%!!WE/kl(Miq=4LTs8NAjs8VcZ)?'S!
-k5SDNs8)cqpAb*knc&U.$0_Efp?`1<!;ZQmndM'D"XDHk*"4[RqY'shXY^#%PlLafMEq4nc^?0]
-1u/'/ruAa=*'MXCrs[:"2Xh6@s6fle']f;88cSDXs8DoWs*t~>
-hu>A1^]NWns8UpUgAq7!D=RZ+n,!(^!XA]1qu=Z&&kLmDs7cu%,760Ys8Mqh.K^9HrVmB&!W;ur
-s8W)uqu8sss8)`p./i`:k5bP^F=I8:JGoQKrrBGN!B&+$!!!$$s7QDa0-1[tp&>$ls6ose+nu"/
-/bh7Ss8VWhSfo'pRf<?bb<$_4_Z0Z5!j*[M9Z?]'s#p;_qu?Zqs6T4S!;$6im/Qq^potP)!<<&j
-s7H?hs61U)ru9Php&!_is76-gs6(<As6+D9(1oI>r(2A7rr4,.p&FgUs7bF]s8W#ss6)5Xs7uTm
-rr5gAlMpMVrr;]_rr3@+L]@DRs8Mrjmf*:brsIui$2sc%!!WE/kl(Miq=4LTs8NAjs8VcZ)?'S!
-k5SDNs8)cqpAb*knc&U.$0_Efp?`1<!;ZQmndM'D"XDHk*"4[RqY'shXY^#%PlLafMEq4nc^?0]
-1u/'/ruAa=*'MXCrs[:"2Xh6@s6fle']f;88cSDXs8DoWs*t~>
-hu>A1^]NWns8UpUgAq7!D=RZ+n,!(^!XA]1qu=Z&&kLmDs7cu%,760Ys8Mqh.K^9HrVmB&!W;ur
-s8W)uqu8sss8)`p./i`:k5bP^F=I8:JGoQKrrBGN!B&+$!!!$$s7QDa0-1[tp&>$ls6ose+nu"/
-/bh7Ss8VWhSfo'pRf<?bb<$_4_Z0Z5!j*[M9Z?]'s#p;_qu?Zqs6T4S!;$6im/Qq^potP)!<<&j
-s7H?hs61U)ru9Php&!_is76-gs6(<As6+D9(1oI>r(2A7rr4,.p&FgUs7bF]s8W#ss6)5Xs7uTm
-rr5gAlMpMVrr;]_rr3@+L]@DRs8Mrjmf*:brsIui$2sc%!!WE/kl(Miq=4LTs8NAjs8VcZ)?'S!
-k5SDNs8)cqpAb*knc&U.$0_Efp?`1<!;ZQmndM'D"XDHk*"4[RqY'shXY^#%PlLafMEq4nc^?0]
-1u/'/ruAa=*'MXCrs[:"2Xh6@s6fle']f;88cSDXs8DoWs*t~>
-hu=Gls!)ggr:-(+q$-Q.*rnZciW&rUr<<3#HEe%,rPo\lq=jqll18LAq"DES`;\%HNq`SHrrN&t
-rVuoss8MF/q#C?nh=psLqi"MGs(0mabZ+TBs764eh"6%KrVmE&q>^,f0WtH)25fU?s82Wls7ZKm
-g(jo5kPtDYXuF;bc8V'gY9D$HaYCQorr?WSr*M;YrVm'""8i,to`"k0r"U.P+0tq?s5tW#rrM9X
-s8V3\r6eJYq>V#urr3)e$M45qs%7iJs6<$gp=)Y:U&N7op@eO`s8V9^s7?8R6N@)]s8)a6Shg?n
-N;qlVV)86&S*L%Rr;Zff)WLPbs8V3\s8)Quq#C<is8W#tmJd+jqGDJ:s8Duarr3c/s8R::cMu?[
-J,''*n,ND(g&M*Ms7#sd!s/<QFT2;3KeMlps8V`f"BX+<M?$H#s)'jms8&H8^])G=M>mQ_f_'Cq
-8)j2O62:KF^+I@_as>1!!,:ots'SRus8Q(cqZ$@$pAb!hrr(pXJ,~>
-hu=Gls!)ggr:-(+q$-Q.*rnZciW&rUr<<3#HEe%,rPo\lq=jqll18LAq"DES`;\%HNq`SHrrN&t
-rVuoss8MF/q#C?nh=psLqi"MGs(0mabZ+TBs764eh"6%KrVmE&q>^,f0WtH)25fU?s82Wls7ZKm
-g(jo5kPtDYXuF;bc8V'gY9D$HaYCQorr?WSr*M;YrVm'""8i,to`"k0r"U.P+0tq?s5tW#rrM9X
-s8V3\r6eJYq>V#urr3)e$M45qs%7iJs6<$gp=)Y:U&N7op@eO`s8V9^s7?8R6N@)]s8)a6Shg?n
-N;qlVV)86&S*L%Rr;Zff)WLPbs8V3\s8)Quq#C<is8W#tmJd+jqGDJ:s8Duarr3c/s8R::cMu?[
-J,''*n,ND(g&M*Ms7#sd!s/<QFT2;3KeMlps8V`f"BX+<M?$H#s)'jms8&H8^])G=M>mQ_f_'Cq
-8)j2O62:KF^+I@_as>1!!,:ots'SRus8Q(cqZ$@$pAb!hrr(pXJ,~>
-hu=Gls!)ggr:-(+q$-Q.*rnZciW&rUr<<3#HEe%,rPo\lq=jqll18LAq"DES`;\%HNq`SHrrN&t
-rVuoss8MF/q#C?nh=psLqi"MGs(0mabZ+TBs764eh"6%KrVmE&q>^,f0WtH)25fU?s82Wls7ZKm
-g(jo5kPtDYXuF;bc8V'gY9D$HaYCQorr?WSr*M;YrVm'""8i,to`"k0r"U.P+0tq?s5tW#rrM9X
-s8V3\r6eJYq>V#urr3)e$M45qs%7iJs6<$gp=)Y:U&N7op@eO`s8V9^s7?8R6N@)]s8)a6Shg?n
-N;qlVV)86&S*L%Rr;Zff)WLPbs8V3\s8)Quq#C<is8W#tmJd+jqGDJ:s8Duarr3c/s8R::cMu?[
-J,''*n,ND(g&M*Ms7#sd!s/<QFT2;3KeMlps8V`f"BX+<M?$H#s)'jms8&H8^])G=M>mQ_f_'Cq
-8)j2O62:KF^+I@_as>1!!,:ots'SRus8Q(cqZ$@$pAb!hrr(pXJ,~>
-i;ZpV!<8Drq>9\2SG36as8TND%g5M.s8N)mrr5.-s8)bi70!8hq"t*c#l4KZ`;fi:gFN=$s8N,t
-s8Dutrr<#=.KBDCq#CBdq8EQds8Vid#64So&b,f+rr3>[%fcM.s7l3c-Ii%p"P4Ii$2+8s*:Npm
-s*k`&s7u]p6.>E)s3!1t6+Hspp8p1ErrE&u"8*3)o)AZ'r<)rsrr;lkqu?]pr7;p=s8R?rs6g^&
-s7Q0err<$rs8VNoqZ$![rt=f#s7H=ZWrK@ps8Du7!8?u:$NL#%oD\CPr9aO!$.f%Js7H0f!%,\^
-dO::Y"X1b_`$(]@q>]j^rrE)hs7cNm'CGer!<<*%!WWu9!<;9WrM:1ui;N[.m.^P[rr<!\!!!Er
-qZ$?js7Gmgs6]jUp](9^nc/@cs-aGes7RD(rrE)os8N)us#ol^gA(^=!<;Wi38aE/p?a[3!WVfl
-mg&CQs8E3"s7&%Vs8W&%2ZEi^lMpGI!8mbC!hKAas"VUpr;?TnjSs`~>
-i;ZpV!<8Drq>9\2SG36as8TND%g5M.s8N)mrr5.-s8)bi70!8hq"t*c#l4KZ`;fi:gFN=$s8N,t
-s8Dutrr<#=.KBDCq#CBdq8EQds8Vid#64So&b,f+rr3>[%fcM.s7l3c-Ii%p"P4Ii$2+8s*:Npm
-s*k`&s7u]p6.>E)s3!1t6+Hspp8p1ErrE&u"8*3)o)AZ'r<)rsrr;lkqu?]pr7;p=s8R?rs6g^&
-s7Q0err<$rs8VNoqZ$![rt=f#s7H=ZWrK@ps8Du7!8?u:$NL#%oD\CPr9aO!$.f%Js7H0f!%,\^
-dO::Y"X1b_`$(]@q>]j^rrE)hs7cNm'CGer!<<*%!WWu9!<;9WrM:1ui;N[.m.^P[rr<!\!!!Er
-qZ$?js7Gmgs6]jUp](9^nc/@cs-aGes7RD(rrE)os8N)us#ol^gA(^=!<;Wi38aE/p?a[3!WVfl
-mg&CQs8E3"s7&%Vs8W&%2ZEi^lMpGI!8mbC!hKAas"VUpr;?TnjSs`~>
-i;ZpV!<8Drq>9\2SG36as8TND%g5M.s8N)mrr5.-s8)bi70!8hq"t*c#l4KZ`;fi:gFN=$s8N,t
-s8Dutrr<#=.KBDCq#CBdq8EQds8Vid#64So&b,f+rr3>[%fcM.s7l3c-Ii%p"P4Ii$2+8s*:Npm
-s*k`&s7u]p6.>E)s3!1t6+Hspp8p1ErrE&u"8*3)o)AZ'r<)rsrr;lkqu?]pr7;p=s8R?rs6g^&
-s7Q0err<$rs8VNoqZ$![rt=f#s7H=ZWrK@ps8Du7!8?u:$NL#%oD\CPr9aO!$.f%Js7H0f!%,\^
-dO::Y"X1b_`$(]@q>]j^rrE)hs7cNm'CGer!<<*%!WWu9!<;9WrM:1ui;N[.m.^P[rr<!\!!!Er
-qZ$?js7Gmgs6]jUp](9^nc/@cs-aGes7RD(rrE)os8N)us#ol^gA(^=!<;Wi38aE/p?a[3!WVfl
-mg&CQs8E3"s7&%Vs8W&%2ZEi^lMpGI!8mbC!hKAas"VUpr;?TnjSs`~>
-i;ZRO!rha,VZ*Y'q#:?oq>1$WrNT!/K`2#Prq?EUpAb0Z'`[_(s8DQf"98B(!!!*$!#,/'s7??i
-s8Dutrr;l;1]$nHnFuq^^&YY=s7?3h')2D+s6]meqtC$iqYgNkr;Z@0r;['9"oeT&s8MlnruTZ)
-'bKO-s76?n&-r7F!:^KhqXjgf&H;A3jo>5T!;uk'r<)rsrp]U\s6Tdbs6]:rrn7A3s8N*!s6K^b
-q>]DNE:j/>mJ$YXmKijjpAaabs6q>Op%n]W!<<#k$iBl"s8VZinGgT1](Q*qs8VWc&`EKbqU6\Z
-!:]RUs4J1drtY2*(ubMpo`"mk$3:)/s8V?`rVultrr38t,anQ0r:p<jrVo=_l&%7PpVo^Equ?<^
-s8N`"qXFOb!!!N=oDK$rm38#!TH`n#$2"8lknEpgq>UHps8;j)nFHSZ!"&]3!!*$!s8VQa#lai)
-nc8[h)%HEAnbr=frW)uur;YkTDZ>J/s8;fp9=k!!rrDuXs*t~>
-i;ZRO!rha,VZ*Y'q#:?oq>1$WrNT!/K`2#Prq?EUpAb0Z'`[_(s8DQf"98B(!!!*$!#,/'s7??i
-s8Dutrr;l;1]$nHnFuq^^&YY=s7?3h')2D+s6]meqtC$iqYgNkr;Z@0r;['9"oeT&s8MlnruTZ)
-'bKO-s76?n&-r7F!:^KhqXjgf&H;A3jo>5T!;uk'r<)rsrp]U\s6Tdbs6]:rrn7A3s8N*!s6K^b
-q>]DNE:j/>mJ$YXmKijjpAaabs6q>Op%n]W!<<#k$iBl"s8VZinGgT1](Q*qs8VWc&`EKbqU6\Z
-!:]RUs4J1drtY2*(ubMpo`"mk$3:)/s8V?`rVultrr38t,anQ0r:p<jrVo=_l&%7PpVo^Equ?<^
-s8N`"qXFOb!!!N=oDK$rm38#!TH`n#$2"8lknEpgq>UHps8;j)nFHSZ!"&]3!!*$!s8VQa#lai)
-nc8[h)%HEAnbr=frW)uur;YkTDZ>J/s8;fp9=k!!rrDuXs*t~>
-i;ZRO!rha,VZ*Y'q#:?oq>1$WrNT!/K`2#Prq?EUpAb0Z'`[_(s8DQf"98B(!!!*$!#,/'s7??i
-s8Dutrr;l;1]$nHnFuq^^&YY=s7?3h')2D+s6]meqtC$iqYgNkr;Z@0r;['9"oeT&s8MlnruTZ)
-'bKO-s76?n&-r7F!:^KhqXjgf&H;A3jo>5T!;uk'r<)rsrp]U\s6Tdbs6]:rrn7A3s8N*!s6K^b
-q>]DNE:j/>mJ$YXmKijjpAaabs6q>Op%n]W!<<#k$iBl"s8VZinGgT1](Q*qs8VWc&`EKbqU6\Z
-!:]RUs4J1drtY2*(ubMpo`"mk$3:)/s8V?`rVultrr38t,anQ0r:p<jrVo=_l&%7PpVo^Equ?<^
-s8N`"qXFOb!!!N=oDK$rm38#!TH`n#$2"8lknEpgq>UHps8;j)nFHSZ!"&]3!!*$!s8VQa#lai)
-nc8[h)%HEAnbr=frW)uur;YkTDZ>J/s8;fp9=k!!rrDuXs*t~>
-hu@6fqYsn^r],f2pC$Qlr;ZZoo`)`;r;ccirt=o&s8)a"r;Qcsrr;fps8)frnG`Idp%JF^rW2rs
-rVuoss8C)%s8;QirV=&7VZ-Vis82WjqZ$<or;Qcls76$qs82lspAb-mpAY'qq>L*lq"k!i,P;$$
-<AiSg48AjU#Q"/es8Vrq"nqurr9=[irrE)os8N*!rr2pHr<)rsrr<#oqZ$Tfs8V<cs$L;rp\4si
-rVuors7Q9[#QO?Iec5@Bk8aL$)uKX8o`#-hs8W"65kb,l2ZN[Sp](!_s0)d1q#::5qt:!h+m]1*
-s8N*[email protected]"BoEY0ls7u]gs8;j*q"4NE-3*K7rVccqs7cQbs8NYkq>^KR
-(]471s8VmuR/[+$p&F[a!;lcrs8?dlr]Y<$rrDrqs8N)urtG)5oD&@c!VcWo#4MQgs8;Wi!;ZWo
-$3B\ss8!'"s8NPrrr3c)&afl"s7,ma`ZXe%I/X*FqC:.ps8:mVJ,~>
-hu@6fqYsn^r],f2pC$Qlr;ZZoo`)`;r;ccirt=o&s8)a"r;Qcsrr;fps8)frnG`Idp%JF^rW2rs
-rVuoss8C)%s8;QirV=&7VZ-Vis82WjqZ$<or;Qcls76$qs82lspAb-mpAY'qq>L*lq"k!i,P;$$
-<AiSg48AjU#Q"/es8Vrq"nqurr9=[irrE)os8N*!rr2pHr<)rsrr<#oqZ$Tfs8V<cs$L;rp\4si
-rVuors7Q9[#QO?Iec5@Bk8aL$)uKX8o`#-hs8W"65kb,l2ZN[Sp](!_s0)d1q#::5qt:!h+m]1*
-s8N*[email protected]"BoEY0ls7u]gs8;j*q"4NE-3*K7rVccqs7cQbs8NYkq>^KR
-(]471s8VmuR/[+$p&F[a!;lcrs8?dlr]Y<$rrDrqs8N)urtG)5oD&@c!VcWo#4MQgs8;Wi!;ZWo
-$3B\ss8!'"s8NPrrr3c)&afl"s7,ma`ZXe%I/X*FqC:.ps8:mVJ,~>
-hu@6fqYsn^r],f2pC$Qlr;ZZoo`)`;r;ccirt=o&s8)a"r;Qcsrr;fps8)frnG`Idp%JF^rW2rs
-rVuoss8C)%s8;QirV=&7VZ-Vis82WjqZ$<or;Qcls76$qs82lspAb-mpAY'qq>L*lq"k!i,P;$$
-<AiSg48AjU#Q"/es8Vrq"nqurr9=[irrE)os8N*!rr2pHr<)rsrr<#oqZ$Tfs8V<cs$L;rp\4si
-rVuors7Q9[#QO?Iec5@Bk8aL$)uKX8o`#-hs8W"65kb,l2ZN[Sp](!_s0)d1q#::5qt:!h+m]1*
-s8N*[email protected]"BoEY0ls7u]gs8;j*q"4NE-3*K7rVccqs7cQbs8NYkq>^KR
-(]471s8VmuR/[+$p&F[a!;lcrs8?dlr]Y<$rrDrqs8N)urtG)5oD&@c!VcWo#4MQgs8;Wi!;ZWo
-$3B\ss8!'"s8NPrrr3c)&afl"s7,ma`ZXe%I/X*FqC:.ps8:mVJ,~>
-iVuRLrrE*!c4-6IhZ*3Js8Dutp](9kiY;Csmf3;>]Dqa,Z7GtT!;lfrrW)uu/@,<Uq>^*ep&Fgf
-s7cQno_SUfjVRgop\Fif$G?05s"aTPs8VZhquH`r!rr2rrrW5s!<<)op/@des7u]frrMros8W#f
-s+gd-o%O@us8P$^rr2pPo`$SRn,NBj5PP0Xs763i!WW,mrr;uupAb$es8;fpqu?]nat*Jo!"o84
-!"')5s7cNm?J6(ms'UW]rVrNjs82irrX.T`#6"2okSn.3s"pbPs8W)unc,-\YlF_&s8N&urr6/k
-q>('@1\+qDq=Xd80DYPGpAap8%I<]as8;co!<3>us7H?kr;Qrjs7cT(rr30!s8Vfirr)it']T,l
-$gIusp]'a_oY_[/q#::<qYpQqo`tNsaoJaJhZ!NVs8;fp!rr;trW)ZlnG`Lgqu8@Pr;S>F$MON!
-rrW5ur;Qius8+jcq#BZi49#EVs8;iqs"]65cg^u/r=Ar$s8DunjSs`~>
-iVuRLrrE*!c4-6IhZ*3Js8Dutp](9kiY;Csmf3;>]Dqa,Z7GtT!;lfrrW)uu/@,<Uq>^*ep&Fgf
-s7cQno_SUfjVRgop\Fif$G?05s"aTPs8VZhquH`r!rr2rrrW5s!<<)op/@des7u]frrMros8W#f
-s+gd-o%O@us8P$^rr2pPo`$SRn,NBj5PP0Xs763i!WW,mrr;uupAb$es8;fpqu?]nat*Jo!"o84
-!"')5s7cNm?J6(ms'UW]rVrNjs82irrX.T`#6"2okSn.3s"pbPs8W)unc,-\YlF_&s8N&urr6/k
-q>('@1\+qDq=Xd80DYPGpAap8%I<]as8;co!<3>us7H?kr;Qrjs7cT(rr30!s8Vfirr)it']T,l
-$gIusp]'a_oY_[/q#::<qYpQqo`tNsaoJaJhZ!NVs8;fp!rr;trW)ZlnG`Lgqu8@Pr;S>F$MON!
-rrW5ur;Qius8+jcq#BZi49#EVs8;iqs"]65cg^u/r=Ar$s8DunjSs`~>
-iVuRLrrE*!c4-6IhZ*3Js8Dutp](9kiY;Csmf3;>]Dqa,Z7GtT!;lfrrW)uu/@,<Uq>^*ep&Fgf
-s7cQno_SUfjVRgop\Fif$G?05s"aTPs8VZhquH`r!rr2rrrW5s!<<)op/@des7u]frrMros8W#f
-s+gd-o%O@us8P$^rr2pPo`$SRn,NBj5PP0Xs763i!WW,mrr;uupAb$es8;fpqu?]nat*Jo!"o84
-!"')5s7cNm?J6(ms'UW]rVrNjs82irrX.T`#6"2okSn.3s"pbPs8W)unc,-\YlF_&s8N&urr6/k
-q>('@1\+qDq=Xd80DYPGpAap8%I<]as8;co!<3>us7H?kr;Qrjs7cT(rr30!s8Vfirr)it']T,l
-$gIusp]'a_oY_[/q#::<qYpQqo`tNsaoJaJhZ!NVs8;fp!rr;trW)ZlnG`Lgqu8@Pr;S>F$MON!
-rrW5ur;Qius8+jcq#BZi49#EVs8;iqs"]65cg^u/r=Ar$s8DunjSs`~>
-i;Yn5#Q4Q#*tUm@jSo\OD2u@1qr,"6UAl%`o)Hi4ci(g)b5_A?s7lKd!rr;B*jb_$L2$\fmJd[r
-s7>s^rrGR1rr3@j)795>s7ZKks7QBjs$-SancAUes7uceruJoTrVrlC\Gt3F;Y'ngs6BUZs3h7G
-s7cAY!87.V(qo_+N`l+s'AEK&-+j3W!:0[\quufnrrhE_)#4(/s*XYB?@_><^a#H>s8VBarrV]a
-s$`OBm`S"Lr:SMI^A#;IcN!_?oD`*\s'Jk-`^Kl^dUl2Hs7bp[qYuBfaT)59s8P"Us7--F(V'.f
-1kYhf'tO@m4,rq2q>1-kn[KQqf_G0uR0!*`s6Td`o`+pks6sYsrr;imqZ$Bis(h*'qK><Wq8,F=
-s8VZip\k-##*Rp8qUgcKlMgq[rVoLj:]L@`!W)irq>p0Zs8NH+s8MWjrVuo4(rGb2L/IsNs763\
-!W)irq>p0es1BSnrn8$orrr2ps8Dip[fA)]s7--5:[e8^rqtgVJ,~>
-i;Yn5#Q4Q#*tUm@jSo\OD2u@1qr,"6UAl%`o)Hi4ci(g)b5_A?s7lKd!rr;B*jb_$L2$\fmJd[r
-s7>s^rrGR1rr3@j)795>s7ZKks7QBjs$-SancAUes7uceruJoTrVrlC\Gt3F;Y'ngs6BUZs3h7G
-s7cAY!87.V(qo_+N`l+s'AEK&-+j3W!:0[\quufnrrhE_)#4(/s*XYB?@_><^a#H>s8VBarrV]a
-s$`OBm`S"Lr:SMI^A#;IcN!_?oD`*\s'Jk-`^Kl^dUl2Hs7bp[qYuBfaT)59s8P"Us7--F(V'.f
-1kYhf'tO@m4,rq2q>1-kn[KQqf_G0uR0!*`s6Td`o`+pks6sYsrr;imqZ$Bis(h*'qK><Wq8,F=
-s8VZip\k-##*Rp8qUgcKlMgq[rVoLj:]L@`!W)irq>p0Zs8NH+s8MWjrVuo4(rGb2L/IsNs763\
-!W)irq>p0es1BSnrn8$orrr2ps8Dip[fA)]s7--5:[e8^rqtgVJ,~>
-i;Yn5#Q4Q#*tUm@jSo\OD2u@1qr,"6UAl%`o)Hi4ci(g)b5_A?s7lKd!rr;B*jb_$L2$\fmJd[r
-s7>s^rrGR1rr3@j)795>s7ZKks7QBjs$-SancAUes7uceruJoTrVrlC\Gt3F;Y'ngs6BUZs3h7G
-s7cAY!87.V(qo_+N`l+s'AEK&-+j3W!:0[\quufnrrhE_)#4(/s*XYB?@_><^a#H>s8VBarrV]a
-s$`OBm`S"Lr:SMI^A#;IcN!_?oD`*\s'Jk-`^Kl^dUl2Hs7bp[qYuBfaT)59s8P"Us7--F(V'.f
-1kYhf'tO@m4,rq2q>1-kn[KQqf_G0uR0!*`s6Td`o`+pks6sYsrr;imqZ$Bis(h*'qK><Wq8,F=
-s8VZip\k-##*Rp8qUgcKlMgq[rVoLj:]L@`!W)irq>p0Zs8NH+s8MWjrVuo4(rGb2L/IsNs763\
-!W)irq>p0es1BSnrn8$orrr2ps8Dip[fA)]s7--5:[e8^rqtgVJ,~>
-iW!firrVHbrNuX@s8VEcs8SNf$kESHV#UI_"[rC]b=3"4d-L0##PA&rr;urpm_([r$!!kqs8VZj
-gAh3Os8;Wgs0P_rs7-Nt!!j#6!<<&up&F:OrrDTh!<<#rrrE)mQkqs[s6d<?)&#W\rt#&-CAS]8
-s6fp`s&1*3s/n^'!(aa!p!bT#3kt^jp^@,js6p$gqY:*jo`,F$!<E06n[2F:"Y$2Gs8;oslMh%f
-p]%j$%gXtTs7?9j_C>O3`;]f5q>^2]./_t&$nf,N!#pR`s7H$br;Zd,qu@E4p+?=BrVuo=/-7/T
-s7c!"0*E>KrVlosr;Q^iY&59h!&@X2rW)Wkq#(*iq#L9k!!a#7!<<)n,l.B<r;HZqqM61+15c,(
-p&G'drUo^;9cO3J/7-Etnc8^gs.0D#rVults8Dor!<3!&ncSOFs8N3#s"O2E;ZmM4Yl=t$qssae
-s8Dor!<;ogs0t>r1Xc'u!<<)pm/QeZ"JYYbs6sBqs8VuTs*t~>
-iW!firrVHbrNuX@s8VEcs8SNf$kESHV#UI_"[rC]b=3"4d-L0##PA&rr;urpm_([r$!!kqs8VZj
-gAh3Os8;Wgs0P_rs7-Nt!!j#6!<<&up&F:OrrDTh!<<#rrrE)mQkqs[s6d<?)&#W\rt#&-CAS]8
-s6fp`s&1*3s/n^'!(aa!p!bT#3kt^jp^@,js6p$gqY:*jo`,F$!<E06n[2F:"Y$2Gs8;oslMh%f
-p]%j$%gXtTs7?9j_C>O3`;]f5q>^2]./_t&$nf,N!#pR`s7H$br;Zd,qu@E4p+?=BrVuo=/-7/T
-s7c!"0*E>KrVlosr;Q^iY&59h!&@X2rW)Wkq#(*iq#L9k!!a#7!<<)n,l.B<r;HZqqM61+15c,(
-p&G'drUo^;9cO3J/7-Etnc8^gs.0D#rVults8Dor!<3!&ncSOFs8N3#s"O2E;ZmM4Yl=t$qssae
-s8Dor!<;ogs0t>r1Xc'u!<<)pm/QeZ"JYYbs6sBqs8VuTs*t~>
-iW!firrVHbrNuX@s8VEcs8SNf$kESHV#UI_"[rC]b=3"4d-L0##PA&rr;urpm_([r$!!kqs8VZj
-gAh3Os8;Wgs0P_rs7-Nt!!j#6!<<&up&F:OrrDTh!<<#rrrE)mQkqs[s6d<?)&#W\rt#&-CAS]8
-s6fp`s&1*3s/n^'!(aa!p!bT#3kt^jp^@,js6p$gqY:*jo`,F$!<E06n[2F:"Y$2Gs8;oslMh%f
-p]%j$%gXtTs7?9j_C>O3`;]f5q>^2]./_t&$nf,N!#pR`s7H$br;Zd,qu@E4p+?=BrVuo=/-7/T
-s7c!"0*E>KrVlosr;Q^iY&59h!&@X2rW)Wkq#(*iq#L9k!!a#7!<<)n,l.B<r;HZqqM61+15c,(
-p&G'drUo^;9cO3J/7-Etnc8^gs.0D#rVults8Dor!<3!&ncSOFs8N3#s"O2E;ZmM4Yl=t$qssae
-s8Dor!<;ogs0t>r1Xc'u!<<)pm/QeZ"JYYbs6sBqs8VuTs*t~>
-i;X>_s8V`gp]('^s8Vrqp&Fdcrs\o,s82-^s7cQjs7Z9frs.uks6ojbrq69j!qH9]rr3T*s8Vur
-s7lNlqu?<gqGZ/Kp&=tMrVuogs8DriqZ$Tos8VQes8N#trpTmdn,<1Rs8N&ls6fpeo`"^erqufk
-s8;oqs7H'bq>^<err3f1s8Voms7u]ks8)3\s8Duos8)chpAb$hru1D,oDARbqt^9lo_/=Qq#C9j
-s7H?kp\+UUr;Q^#q=OUbmJlhXrsnkos("jhs7,^\m.LDYrr)j"q>9d`qtg=(med%ali6\Yl1Y/W
-rVuWlqYpL$o)Jafrr;Wjqu$Hn!r;Wjqu7Ass7H?es75sOs8VN&)?9U6rVu`jrr3;rm/QPJs7cQn
-r;Q^&mf3:dnFu_Js8;iq+Su-8q!\4^rr2rsn,N:bs8)cqnc/X^pAb0Ps8VKds6Tab-h%'7rr2rs
-n,*.as8N&uq#C*ds7?9jqu;`uiW&oWiEbsQs7QEjr8dm.~>
-i;X>_s8V`gp]('^s8Vrqp&Fdcrs\o,s82-^s7cQjs7Z9frs.uks6ojbrq69j!qH9]rr3T*s8Vur
-s7lNlqu?<gqGZ/Kp&=tMrVuogs8DriqZ$Tos8VQes8N#trpTmdn,<1Rs8N&ls6fpeo`"^erqufk
-s8;oqs7H'bq>^<err3f1s8Voms7u]ks8)3\s8Duos8)chpAb$hru1D,oDARbqt^9lo_/=Qq#C9j
-s7H?kp\+UUr;Q^#q=OUbmJlhXrsnkos("jhs7,^\m.LDYrr)j"q>9d`qtg=(med%ali6\Yl1Y/W
-rVuWlqYpL$o)Jafrr;Wjqu$Hn!r;Wjqu7Ass7H?es75sOs8VN&)?9U6rVu`jrr3;rm/QPJs7cQn
-r;Q^&mf3:dnFu_Js8;iq+Su-8q!\4^rr2rsn,N:bs8)cqnc/X^pAb0Ps8VKds6Tab-h%'7rr2rs
-n,*.as8N&uq#C*ds7?9jqu;`uiW&oWiEbsQs7QEjr8dm.~>
-i;X>_s8V`gp]('^s8Vrqp&Fdcrs\o,s82-^s7cQjs7Z9frs.uks6ojbrq69j!qH9]rr3T*s8Vur
-s7lNlqu?<gqGZ/Kp&=tMrVuogs8DriqZ$Tos8VQes8N#trpTmdn,<1Rs8N&ls6fpeo`"^erqufk
-s8;oqs7H'bq>^<err3f1s8Voms7u]ks8)3\s8Duos8)chpAb$hru1D,oDARbqt^9lo_/=Qq#C9j
-s7H?kp\+UUr;Q^#q=OUbmJlhXrsnkos("jhs7,^\m.LDYrr)j"q>9d`qtg=(med%ali6\Yl1Y/W
-rVuWlqYpL$o)Jafrr;Wjqu$Hn!r;Wjqu7Ass7H?es75sOs8VN&)?9U6rVu`jrr3;rm/QPJs7cQn
-r;Q^&mf3:dnFu_Js8;iq+Su-8q!\4^rr2rsn,N:bs8)cqnc/X^pAb0Ps8VKds6Tab-h%'7rr2rs
-n,*.as8N&uq#C*ds7?9jqu;`uiW&oWiEbsQs7QEjr8dm.~>
-i;WrSs7H?^rr3]+s8Vlns8DutqssdTr:p<dmJlnWrr4#2s8V`hpAb'jr9F=^mJm.bs6BXaoDeLZ
-qu?WkoDS[shbO4Hs8VlomJm4WrVm5is7cNfs8VQfs6ose!;QNm$iL&&s69R`p](-jmJ[&$kPX`I
-o)JRds7lWon+QeMs8)9co)JF\r;R-'q#:6is8W#ms6Tab"RuHjs82fq%I=&js8Voprr<#ks8W&s
-rsSi+s6p!bs7lWfs7cKl)u9)A+Fhr3s68@=90rUUq>]XXrU0^cqXjgfnGW@eo)AY)q#CBdr;ZQl
-nGiOUs6fdas8)Hho)8Lcq$[6%rr<#mr:Bs`qtL$g&H2Y(s$X6kq#C9jo`+Uas8;lr$i9o'nc/Ld
-s6BXYoD\b(r;ZTms7lNYs7$'`s8VQfs6p!frVc`umI^GSrr3?)nc/@Qs8VQfs6]gc"7Q9in,<8+
-p\Og]s763hs7Z0dnFkWSI/j$Bnkn9Do`"mjp>c1'~>
-i;WrSs7H?^rr3]+s8Vlns8DutqssdTr:p<dmJlnWrr4#2s8V`hpAb'jr9F=^mJm.bs6BXaoDeLZ
-qu?WkoDS[shbO4Hs8VlomJm4WrVm5is7cNfs8VQfs6ose!;QNm$iL&&s69R`p](-jmJ[&$kPX`I
-o)JRds7lWon+QeMs8)9co)JF\r;R-'q#:6is8W#ms6Tab"RuHjs82fq%I=&js8Voprr<#ks8W&s
-rsSi+s6p!bs7lWfs7cKl)u9)A+Fhr3s68@=90rUUq>]XXrU0^cqXjgfnGW@eo)AY)q#CBdr;ZQl
-nGiOUs6fdas8)Hho)8Lcq$[6%rr<#mr:Bs`qtL$g&H2Y(s$X6kq#C9jo`+Uas8;lr$i9o'nc/Ld
-s6BXYoD\b(r;ZTms7lNYs7$'`s8VQfs6p!frVc`umI^GSrr3?)nc/@Qs8VQfs6]gc"7Q9in,<8+
-p\Og]s763hs7Z0dnFkWSI/j$Bnkn9Do`"mjp>c1'~>
-i;WrSs7H?^rr3]+s8Vlns8DutqssdTr:p<dmJlnWrr4#2s8V`hpAb'jr9F=^mJm.bs6BXaoDeLZ
-qu?WkoDS[shbO4Hs8VlomJm4WrVm5is7cNfs8VQfs6ose!;QNm$iL&&s69R`p](-jmJ[&$kPX`I
-o)JRds7lWon+QeMs8)9co)JF\r;R-'q#:6is8W#ms6Tab"RuHjs82fq%I=&js8Voprr<#ks8W&s
-rsSi+s6p!bs7lWfs7cKl)u9)A+Fhr3s68@=90rUUq>]XXrU0^cqXjgfnGW@eo)AY)q#CBdr;ZQl
-nGiOUs6fdas8)Hho)8Lcq$[6%rr<#mr:Bs`qtL$g&H2Y(s$X6kq#C9jo`+Uas8;lr$i9o'nc/Ld
-s6BXYoD\b(r;ZTms7lNYs7$'`s8VQfs6p!frVc`umI^GSrr3?)nc/@Qs8VQfs6]gc"7Q9in,<8+
-p\Og]s763hs7Z0dnFkWSI/j$Bnkn9Do`"mjp>c1'~>
-hu=5brVuosp%J+TrVu`ds8V?\s8Vt's8Muss7QB^s7cQiqu?]ds8VQfqtg3^s8VWas7cQjoD/Fd
-rqZT]s82ior:^0\s8N&^mJlt]mJm4[s7u]ds8N#toDeXdq>'^`s7u]nqu>mQs8)ZnqXOUUs7u]f
-rr3,hq>^EjrVmT+rr;ilqu?]ls8Vfmq>^3hrqcZkrr5afo`+:Xs6Ta_nc/XYs7$'gn,*.Ts8V]j
-irArVq"+O\s7uTipAb0[rVHKmmf1RE0,FTt+@_mPq#B[Vs7Q-dn+m"`o`+dfoDS%Us82fn#lj]"
-o(rCbm/?n_&,Gu"q>L?ir;ZEhs82fgrVlg,li6taq"OgHs8Vlgs7--grW)Kert4r#s8Vcls7lEi
-meZtRs7QElrquuas7cQer;RN-s8V]jrr2rhs8)ccs7QEfs8;o`rr2p8rVuoos7lWds8V]jrr2rh
-s8W&ps7H?bs7lQm&b5]]%TrQ*pAb0ks8Vrks8W)Ys*t~>
-hu=5brVuosp%J+TrVu`ds8V?\s8Vt's8Muss7QB^s7cQiqu?]ds8VQfqtg3^s8VWas7cQjoD/Fd
-rqZT]s82ior:^0\s8N&^mJlt]mJm4[s7u]ds8N#toDeXdq>'^`s7u]nqu>mQs8)ZnqXOUUs7u]f
-rr3,hq>^EjrVmT+rr;ilqu?]ls8Vfmq>^3hrqcZkrr5afo`+:Xs6Ta_nc/XYs7$'gn,*.Ts8V]j
-irArVq"+O\s7uTipAb0[rVHKmmf1RE0,FTt+@_mPq#B[Vs7Q-dn+m"`o`+dfoDS%Us82fn#lj]"
-o(rCbm/?n_&,Gu"q>L?ir;ZEhs82fgrVlg,li6taq"OgHs8Vlgs7--grW)Kert4r#s8Vcls7lEi
-meZtRs7QElrquuas7cQer;RN-s8V]jrr2rhs8)ccs7QEfs8;o`rr2p8rVuoos7lWds8V]jrr2rh
-s8W&ps7H?bs7lQm&b5]]%TrQ*pAb0ks8Vrks8W)Ys*t~>
-hu=5brVuosp%J+TrVu`ds8V?\s8Vt's8Muss7QB^s7cQiqu?]ds8VQfqtg3^s8VWas7cQjoD/Fd
-rqZT]s82ior:^0\s8N&^mJlt]mJm4[s7u]ds8N#toDeXdq>'^`s7u]nqu>mQs8)ZnqXOUUs7u]f
-rr3,hq>^EjrVmT+rr;ilqu?]ls8Vfmq>^3hrqcZkrr5afo`+:Xs6Ta_nc/XYs7$'gn,*.Ts8V]j
-irArVq"+O\s7uTipAb0[rVHKmmf1RE0,FTt+@_mPq#B[Vs7Q-dn+m"`o`+dfoDS%Us82fn#lj]"
-o(rCbm/?n_&,Gu"q>L?ir;ZEhs82fgrVlg,li6taq"OgHs8Vlgs7--grW)Kert4r#s8Vcls7lEi
-meZtRs7QElrquuas7cQer;RN-s8V]jrr2rhs8)ccs7QEfs8;o`rr2p8rVuoos7lWds8V]jrr2rh
-s8W&ps7H?bs7lQm&b5]]%TrQ*pAb0ks8Vrks8W)Ys*t~>
-iVs,Srr<#ms8Mus..mQ;qu?Wpp]'m]s8Dutqt^9lkl:JYq"FUbs82Efs7uWfs82inp&4mi!Vl?e
-rt+u$s8VZir;ZNks8)<dp&FFYs$cb`s7?9jhZ*KJs82]nq"t'js7u]ns8;Qfs7QElkl:VRq#Bsc
-p&F[\s6TIZq>^-fnc/OSs8Dusr;ZEhq>^Kbs7cQmrVlrnqtU*h!qPsWrr4&<o`+OSs8W)up](9_
-s7$'Zs7Q0es7cBiq>^6ip%\Od!9jF^#Pe?!q#CBnq#::&p&G'cs5j:\kPkMAs5s=\(]+1&s7u]b
-s8)cms8)cps6BXap%SLdq#::@o)J"Lo`+shs7u3bo`"Xcs60LLs8VEbs8;`nq==Rcl2UJWqu$K^
-rr3)us8W)trr`#qr:g'f+o1]qs6'FZq#C6gs7lKcs8UsUs7cQfr;Z0as7Pp^s82cort"Ppq#C6g
-s7lKkq>^0grVc`q(%M>!s7QElq>^9jrqufcs8W)rs8VuWs*t~>
-iVs,Srr<#ms8Mus..mQ;qu?Wpp]'m]s8Dutqt^9lkl:JYq"FUbs82Efs7uWfs82inp&4mi!Vl?e
-rt+u$s8VZir;ZNks8)<dp&FFYs$cb`s7?9jhZ*KJs82]nq"t'js7u]ns8;Qfs7QElkl:VRq#Bsc
-p&F[\s6TIZq>^-fnc/OSs8Dusr;ZEhq>^Kbs7cQmrVlrnqtU*h!qPsWrr4&<o`+OSs8W)up](9_
-s7$'Zs7Q0es7cBiq>^6ip%\Od!9jF^#Pe?!q#CBnq#::&p&G'cs5j:\kPkMAs5s=\(]+1&s7u]b
-s8)cms8)cps6BXap%SLdq#::@o)J"Lo`+shs7u3bo`"Xcs60LLs8VEbs8;`nq==Rcl2UJWqu$K^
-rr3)us8W)trr`#qr:g'f+o1]qs6'FZq#C6gs7lKcs8UsUs7cQfr;Z0as7Pp^s82cort"Ppq#C6g
-s7lKkq>^0grVc`q(%M>!s7QElq>^9jrqufcs8W)rs8VuWs*t~>
-iVs,Srr<#ms8Mus..mQ;qu?Wpp]'m]s8Dutqt^9lkl:JYq"FUbs82Efs7uWfs82inp&4mi!Vl?e
-rt+u$s8VZir;ZNks8)<dp&FFYs$cb`s7?9jhZ*KJs82]nq"t'js7u]ns8;Qfs7QElkl:VRq#Bsc
-p&F[\s6TIZq>^-fnc/OSs8Dusr;ZEhq>^Kbs7cQmrVlrnqtU*h!qPsWrr4&<o`+OSs8W)up](9_
-s7$'Zs7Q0es7cBiq>^6ip%\Od!9jF^#Pe?!q#CBnq#::&p&G'cs5j:\kPkMAs5s=\(]+1&s7u]b
-s8)cms8)cps6BXap%SLdq#::@o)J"Lo`+shs7u3bo`"Xcs60LLs8VEbs8;`nq==Rcl2UJWqu$K^
-rr3)us8W)trr`#qr:g'f+o1]qs6'FZq#C6gs7lKcs8UsUs7cQfr;Z0as7Pp^s82cort"Ppq#C6g
-s7lKkq>^0grVc`q(%M>!s7QElq>^9jrqufcs8W)rs8VuWs*t~>
-i;X\as8Duqs8DrsoDe1Ws7uTms7H?kq#C-hq#14/pAagcs7cQkrr;orpAb0is8Vrps8Vijrr3#q
-rVlg%q>^Hhr;ZHgrVmN%s8W)ss7?9is7H?bs8Vrqs8)`p$hsYss8)ZnrVuonp&4mmq#C!arVllp
-rr3>ss8)c`s8M`lqYU3j!q?6frVpj2s8;osoDeOar;Z`hs8N&uoDJLcq>U<lqu-Ejrr;cks82Tk
-p&Fjdrr<#os8Duas8V]gp]'d\p&Fdas7?9eq#C-es8)cqq#CBhs8Vfmp](3js8)<dq"asirr;fe
-s8N&umJm4_s8Vinqu?Whqu9"Ns7ZKmrr<#ss8Vigs82]nr;Zfrqu?]mqu?9fqu?]hs82imrVuHg
-s8)Egnc/7]p[\@`s763ir;$Bcs8Vrqs8)`p&,Q>+p](9ks763irqHHmqYgEon,<7oqZ$Tls8W)u
-p&>!fqYpm$s8)Whs7QEjr;Q^"p](9kr;PdWJ,~>
-i;X\as8Duqs8DrsoDe1Ws7uTms7H?kq#C-hq#14/pAagcs7cQkrr;orpAb0is8Vrps8Vijrr3#q
-rVlg%q>^Hhr;ZHgrVmN%s8W)ss7?9is7H?bs8Vrqs8)`p$hsYss8)ZnrVuonp&4mmq#C!arVllp
-rr3>ss8)c`s8M`lqYU3j!q?6frVpj2s8;osoDeOar;Z`hs8N&uoDJLcq>U<lqu-Ejrr;cks82Tk
-p&Fjdrr<#os8Duas8V]gp]'d\p&Fdas7?9eq#C-es8)cqq#CBhs8Vfmp](3js8)<dq"asirr;fe
-s8N&umJm4_s8Vinqu?Whqu9"Ns7ZKmrr<#ss8Vigs82]nr;Zfrqu?]mqu?9fqu?]hs82imrVuHg
-s8)Egnc/7]p[\@`s763ir;$Bcs8Vrqs8)`p&,Q>+p](9ks763irqHHmqYgEon,<7oqZ$Tls8W)u
-p&>!fqYpm$s8)Whs7QEjr;Q^"p](9kr;PdWJ,~>
-i;X\as8Duqs8DrsoDe1Ws7uTms7H?kq#C-hq#14/pAagcs7cQkrr;orpAb0is8Vrps8Vijrr3#q
-rVlg%q>^Hhr;ZHgrVmN%s8W)ss7?9is7H?bs8Vrqs8)`p$hsYss8)ZnrVuonp&4mmq#C!arVllp
-rr3>ss8)c`s8M`lqYU3j!q?6frVpj2s8;osoDeOar;Z`hs8N&uoDJLcq>U<lqu-Ejrr;cks82Tk
-p&Fjdrr<#os8Duas8V]gp]'d\p&Fdas7?9eq#C-es8)cqq#CBhs8Vfmp](3js8)<dq"asirr;fe
-s8N&umJm4_s8Vinqu?Whqu9"Ns7ZKmrr<#ss8Vigs82]nr;Zfrqu?]mqu?9fqu?]hs82imrVuHg
-s8)Egnc/7]p[\@`s763ir;$Bcs8Vrqs8)`p&,Q>+p](9ks763irqHHmqYgEon,<7oqZ$Tls8W)u
-p&>!fqYpm$s8)Whs7QEjr;Q^"p](9kr;PdWJ,~>
-JcF^/#QFZ$rVuoqp\b$qp\Y!grVuWkrs&?"s82Wlq#:9pqY:$grs\c%q#:<cs8W#ss8)]nrrMrl
-qu6lrrr;Qhs8DZk'DVUss8VZis7cQnirB&Ls8;corVca!pAb!hp@nReqYU:(q>C9mkl:\Os8VWf
-s7$'grdk+1s*t~>
-JcF^/#QFZ$rVuoqp\b$qp\Y!grVuWkrs&?"s82Wlq#:9pqY:$grs\c%q#:<cs8W#ss8)]nrrMrl
-qu6lrrr;Qhs8DZk'DVUss8VZis7cQnirB&Ls8;corVca!pAb!hp@nReqYU:(q>C9mkl:\Os8VWf
-s7$'grdk+1s*t~>
-JcF^/#QFZ$rVuoqp\b$qp\Y!grVuWkrs&?"s82Wlq#:9pqY:$grs\c%q#:<cs8W#ss8)]nrrMrl
-qu6lrrr;Qhs8DZk'DVUss8VZis7cQnirB&Ls8;corVca!pAb!hp@nReqYU:(q>C9mkl:\Os8VWf
-s7$'grdk+1s*t~>
-JcF[.$1Iomo(MnZqYU6jruM"/s8Vrqs7uEhrVH6fr;HWoqtg?mr9X%Tqu?9drr3#up&=slq"ajf
-!r)9Zrr32as8VEbs7Z3e%eB&fo`+FUrq$0in+-MQrr3B"s8N#ns8Vccs8MW`rrVrcpAY'po`+s`
-m/6kcq>:3bJcFd1J,~>
-JcF[.$1Iomo(MnZqYU6jruM"/s8Vrqs7uEhrVH6fr;HWoqtg?mr9X%Tqu?9drr3#up&=slq"ajf
-!r)9Zrr32as8VEbs7Z3e%eB&fo`+FUrq$0in+-MQrr3B"s8N#ns8Vccs8MW`rrVrcpAY'po`+s`
-m/6kcq>:3bJcFd1J,~>
-JcF[.$1Iomo(MnZqYU6jruM"/s8Vrqs7uEhrVH6fr;HWoqtg?mr9X%Tqu?9drr3#up&=slq"ajf
-!r)9Zrr32as8VEbs7Z3e%eB&fo`+FUrq$0in+-MQrr3B"s8N#ns8Vccs8MW`rrVrcpAY'po`+s`
-m/6kcq>:3bJcFd1J,~>
-JcF^/%/p4rnc/Oequ?]pq=ssh'Dhb*s8;oqq>^?ls8)Wms7u]pq>UC,rVlisqu?]fs8Vufq>^Ko
-q>:'es8Vs"s7?6irUfmb!WDckrrMchr;RE/s7QElo)JUep](9bs8Vris8;]ms82`o&,Z2&pA=mi
-p&+L_s7QEjoR[&&s*t~>
-JcF^/%/p4rnc/Oequ?]pq=ssh'Dhb*s8;oqq>^?ls8)Wms7u]pq>UC,rVlisqu?]fs8Vufq>^Ko
-q>:'es8Vs"s7?6irUfmb!WDckrrMchr;RE/s7QElo)JUep](9bs8Vris8;]ms82`o&,Z2&pA=mi
-p&+L_s7QEjoR[&&s*t~>
-JcF^/%/p4rnc/Oequ?]pq=ssh'Dhb*s8;oqq>^?ls8)Wms7u]pq>UC,rVlisqu?]fs8Vufq>^Ko
-q>:'es8Vs"s7?6irUfmb!WDckrrMchr;RE/s7QElo)JUep](9bs8Vris8;]ms82`o&,Z2&pA=mi
-p&+L_s7QEjoR[&&s*t~>
-JcF^/(%VD)qZ$Qpp\Xges8Dfmp[eFbli6e[rrV]hn,E>7o_8Ccqu-Hgs8VQfs7uKes7Q3bs8Dfc
-s8W)oqu$<gs7H9cs7cQmq#:Eps7-*g%.sT!oDe4Wrr<#ps7-*g"7H0fr;Q]trr;rlrseu'qu$<g
-s8W)us6K^^rr3/us8W#srdk+1s*t~>
-JcF^/(%VD)qZ$Qpp\Xges8Dfmp[eFbli6e[rrV]hn,E>7o_8Ccqu-Hgs8VQfs7uKes7Q3bs8Dfc
-s8W)oqu$<gs7H9cs7cQmq#:Eps7-*g%.sT!oDe4Wrr<#ps7-*g"7H0fr;Q]trr;rlrseu'qu$<g
-s8W)us6K^^rr3/us8W#srdk+1s*t~>
-JcF^/(%VD)qZ$Qpp\Xges8Dfmp[eFbli6e[rrV]hn,E>7o_8Ccqu-Hgs8VQfs7uKes7Q3bs8Dfc
-s8W)oqu$<gs7H9cs7cQmq#:Eps7-*g%.sT!oDe4Wrr<#ps7-*g"7H0fr;Q]trr;rlrseu'qu$<g
-s8W)us6K^^rr3/us8W#srdk+1s*t~>
-JcF[.#k@rpk5Y>Qs8N#t(&7\+s7--hp](9boDej^s8;`ns7c-art>>2s7$'_q>^Enqu?]kr;ZZo
-rr)itp\Fgg"nqTfmJleQrrq`gpAaa]rr2uirVlokrVlfsp&+gnmeHh^l2LMY!<2rs%eof!s82Hg
-s7lKgl2USXrr2ukJcFg2J,~>
-JcF[.#k@rpk5Y>Qs8N#t(&7\+s7--hp](9boDej^s8;`ns7c-art>>2s7$'_q>^Enqu?]kr;ZZo
-rr)itp\Fgg"nqTfmJleQrrq`gpAaa]rr2uirVlokrVlfsp&+gnmeHh^l2LMY!<2rs%eof!s82Hg
-s7lKgl2USXrr2ukJcFg2J,~>
-JcF[.#k@rpk5Y>Qs8N#t(&7\+s7--hp](9boDej^s8;`ns7c-art>>2s7$'_q>^Enqu?]kr;ZZo
-rr)itp\Fgg"nqTfmJleQrrq`gpAaa]rr2uirVlokrVlfsp&+gnmeHh^l2LMY!<2rs%eof!s82Hg
-s7lKgl2USXrr2ukJcFg2J,~>
-JcF^/48/^Js8Vuls82]er;Zfrs8V?RrrDBbs8;ojs8;ons8W)urU]j`o)&ISs8McXFo^7rs8Duq
-p\k'fqu-Ntp]1?or:U(%r;HZ\&c_Fts8Vhn*]"3&mIpMY!r)`crr2ujp](9ls82cp%eTens4'Rf
-"`;Naq#C?dJcFd1J,~>
-JcF^/48/^Js8Vuls82]er;Zfrs8V?RrrDBbs8;ojs8;ons8W)urU]j`o)&ISs8McXFo^7rs8Duq
-p\k'fqu-Ntp]1?or:U(%r;HZ\&c_Fts8Vhn*]"3&mIpMY!r)`crr2ujp](9ls82cp%eTens4'Rf
-"`;Naq#C?dJcFd1J,~>
-JcF^/48/^Js8Vuls82]er;Zfrs8V?RrrDBbs8;ojs8;ons8W)urU]j`o)&ISs8McXFo^7rs8Duq
-p\k'fqu-Ntp]1?or:U(%r;HZ\&c_Fts8Vhn*]"3&mIpMY!r)`crr2ujp](9ls82cp%eTens4'Rf
-"`;Naq#C?dJcFd1J,~>
-JcF^/-2dN;li-GSr:g6kn,E@Zs82`opCRAopAFsds8VTgmelPRs7uZnrsSi+irB#\7h5S!oCi1`
-!;ZWo"oSE!o`"pjrrW&mq=t!irVum9!;HEbqu?[9l2C\_p](9clMpn^s8)?Zs8VihrrDlorseo+
-rUg-hs8P0/lg\Larr3#moR[&&s*t~>
-JcF^/-2dN;li-GSr:g6kn,E@Zs82`opCRAopAFsds8VTgmelPRs7uZnrsSi+irB#\7h5S!oCi1`
-!;ZWo"oSE!o`"pjrrW&mq=t!irVum9!;HEbqu?[9l2C\_p](9clMpn^s8)?Zs8VihrrDlorseo+
-rUg-hs8P0/lg\Larr3#moR[&&s*t~>
-JcF^/-2dN;li-GSr:g6kn,E@Zs82`opCRAopAFsds8VTgmelPRs7uZnrsSi+irB#\7h5S!oCi1`
-!;ZWo"oSE!o`"pjrrW&mq=t!irVum9!;HEbqu?[9l2C\_p](9clMpn^s8)?Zs8VihrrDlorseo+
-rUg-hs8P0/lg\Larr3#moR[&&s*t~>
-JcF[.70Shd+M3OG(8Um.riJ-[/>)nA!WWc1nO!I@pA+RkQkUF6p](9Z5mogQpAb*e!<;fns7H0f
-s7`BG!%k)JrrAE$%^k^!rrrAh1CKlXrr<#s'EJ16!<N)tm7IOWlhUPL0]i8m.eNN9"o"lL!%k)G
-rsJr/p](.$nGiOds7_*EjSs`~>
-JcF[.70Shd+M3OG(8Um.riJ-[/>)nA!WWc1nO!I@pA+RkQkUF6p](9Z5mogQpAb*e!<;fns7H0f
-s7`BG!%k)JrrAE$%^k^!rrrAh1CKlXrr<#s'EJ16!<N)tm7IOWlhUPL0]i8m.eNN9"o"lL!%k)G
-rsJr/p](.$nGiOds7_*EjSs`~>
-JcF[.70Shd+M3OG(8Um.riJ-[/>)nA!WWc1nO!I@pA+RkQkUF6p](9Z5mogQpAb*e!<;fns7H0f
-s7`BG!%k)JrrAE$%^k^!rrrAh1CKlXrr<#s'EJ16!<N)tm7IOWlhUPL0]i8m.eNN9"o"lL!%k)G
-rsJr/p](.$nGiOds7_*EjSs`~>
-JcF[..fjAaQ4IQjSJV>+r#*$@ZY&_,!VucoBlsB/Du]M9EU];5rVu\0POZ/#rVlg4!<;rro_ngb
-oD]r@p:4Q/r;_W1Te#U2rrD]js$?PYqdcSnI/s6Gr;ZNk!<;s#qYsojrO?nJs80N!rD:6%r;Z]p
-s8N&poD]r@p:4Q/s8Vur;N:_9CgR/<s8W)kJcFg2J,~>
-JcF[..fjAaQ4IQjSJV>+r#*$@ZY&_,!VucoBlsB/Du]M9EU];5rVu\0POZ/#rVlg4!<;rro_ngb
-oD]r@p:4Q/r;_W1Te#U2rrD]js$?PYqdcSnI/s6Gr;ZNk!<;s#qYsojrO?nJs80N!rD:6%r;Z]p
-s8N&poD]r@p:4Q/s8Vur;N:_9CgR/<s8W)kJcFg2J,~>
-JcF[..fjAaQ4IQjSJV>+r#*$@ZY&_,!VucoBlsB/Du]M9EU];5rVu\0POZ/#rVlg4!<;rro_ngb
-oD]r@p:4Q/r;_W1Te#U2rrD]js$?PYqdcSnI/s6Gr;ZNk!<;s#qYsojrO?nJs80N!rD:6%r;Z]p
-s8N&poD]r@p:4Q/s8Vur;N:_9CgR/<s8W)kJcFg2J,~>
-JcF[.0`_7Pm/["_rrDuqq\P"Ks7lWf!<;cm)#4'e"98B$s7?6js7cNrp]'8"r:Kpe"T.lks&]-q
-s8*=Xl14lQrrMurncSplqYpNop%/.^q\o#)lkB<ks8)cj!qH9js6ps$o(!^or;-Fi=C^=is7cQa
-pAb-ls8*=Xl14lQoDe^fi#Mdt+m](%rqV-Fir=N~>
-JcF[.0`_7Pm/["_rrDuqq\P"Ks7lWf!<;cm)#4'e"98B$s7?6js7cNrp]'8"r:Kpe"T.lks&]-q
-s8*=Xl14lQrrMurncSplqYpNop%/.^q\o#)lkB<ks8)cj!qH9js6ps$o(!^or;-Fi=C^=is7cQa
-pAb-ls8*=Xl14lQoDe^fi#Mdt+m](%rqV-Fir=N~>
-JcF[.0`_7Pm/["_rrDuqq\P"Ks7lWf!<;cm)#4'e"98B$s7?6js7cNrp]'8"r:Kpe"T.lks&]-q
-s8*=Xl14lQrrMurncSplqYpNop%/.^q\o#)lkB<ks8)cj!qH9js6ps$o(!^or;-Fi=C^=is7cQa
-pAb-ls8*=Xl14lQoDe^fi#Mdt+m](%rqV-Fir=N~>
-JcF^/$2+o.s8Nhls82lrrt=44'K<T$rrDfnoF9p^rsJf&%0$89!<<)b"onW-!<<)nqZQou1AUY?
-s8VfTC*kdZr:L'ipAY-mr:Bsg!"9S/p\t9hmJd1ds8)WdrrE*!#QOf-!"&f.rr4;CciM>is8;ob
-rsf#/rVuTRC*kdZr;ZfdmV%I@pP)lEs8Vopqgne.s*t~>
-JcF^/$2+o.s8Nhls82lrrt=44'K<T$rrDfnoF9p^rsJf&%0$89!<<)b"onW-!<<)nqZQou1AUY?
-s8VfTC*kdZr:L'ipAY-mr:Bsg!"9S/p\t9hmJd1ds8)WdrrE*!#QOf-!"&f.rr4;CciM>is8;ob
-rsf#/rVuTRC*kdZr;ZfdmV%I@pP)lEs8Vopqgne.s*t~>
-JcF^/$2+o.s8Nhls82lrrt=44'K<T$rrDfnoF9p^rsJf&%0$89!<<)b"onW-!<<)nqZQou1AUY?
-s8VfTC*kdZr:L'ipAY-mr:Bsg!"9S/p\t9hmJd1ds8)WdrrE*!#QOf-!"&f.rr4;CciM>is8;ob
-rsf#/rVuTRC*kdZr;ZfdmV%I@pP)lEs8Vopqgne.s*t~>
-JcF[.9*#"cp':Wir!EB$me$MYYS6d2!<;lp)<h+^+Sl$;s7l?jq<e2+q#C$ds6KRX!rW)or;Zfr
-qZ$<ipUCP0rrDofrrDrqs8;Hbqu6U#pFPJ,li@(arr4eM!;uisq=G*qq"k$_m/-fe<Fc'rrq?B`
-rVuirqZ$<ipUCP0qtpEk$1e#orrE)js8VP=s5X-0~>
-JcF[.9*#"cp':Wir!EB$me$MYYS6d2!<;lp)<h+^+Sl$;s7l?jq<e2+q#C$ds6KRX!rW)or;Zfr
-qZ$<ipUCP0rrDofrrDrqs8;Hbqu6U#pFPJ,li@(arr4eM!;uisq=G*qq"k$_m/-fe<Fc'rrq?B`
-rVuirqZ$<ipUCP0qtpEk$1e#orrE)js8VP=s5X-0~>
-JcF[.9*#"cp':Wir!EB$me$MYYS6d2!<;lp)<h+^+Sl$;s7l?jq<e2+q#C$ds6KRX!rW)or;Zfr
-qZ$<ipUCP0rrDofrrDrqs8;Hbqu6U#pFPJ,li@(arr4eM!;uisq=G*qq"k$_m/-fe<Fc'rrq?B`
-rVuirqZ$<ipUCP0qtpEk$1e#orrE)js8VP=s5X-0~>
-JcF[.!!*#u0`_4QrX/5rs'j%=]aXr@1=c$n?B+`3AGu65m/QSYqZ$S"N:4]/o)8Ug"RQ0goDe7X
-rsiM>m'7u<nIts&q?m#trr)j?rr;ZT@C>fB&+9Jtqtp:#rV['&s)En[c=ZqQs0b8o8YQ.`"Si#l
-r:p9k"CeJ!Z6oSN%J'N_C&.Oa70!;Nqu?PEs5a31~>
-JcF[.!!*#u0`_4QrX/5rs'j%=]aXr@1=c$n?B+`3AGu65m/QSYqZ$S"N:4]/o)8Ug"RQ0goDe7X
-rsiM>m'7u<nIts&q?m#trr)j?rr;ZT@C>fB&+9Jtqtp:#rV['&s)En[c=ZqQs0b8o8YQ.`"Si#l
-r:p9k"CeJ!Z6oSN%J'N_C&.Oa70!;Nqu?PEs5a31~>
-JcF[.!!*#u0`_4QrX/5rs'j%=]aXr@1=c$n?B+`3AGu65m/QSYqZ$S"N:4]/o)8Ug"RQ0goDe7X
-rsiM>m'7u<nIts&q?m#trr)j?rr;ZT@C>fB&+9Jtqtp:#rV['&s)En[c=ZqQs0b8o8YQ.`"Si#l
-r:p9k"CeJ!Z6oSN%J'N_C&.Oa70!;Nqu?PEs5a31~>
-JcF^/9`,:rq>(Ttn*UP_j8Z/$%48ggrQHBWs62]U-2[]A!;ulq!<;ZgqHa.WK)blHoF:j#p\uiF
-p\jib0-DUPs8N)ls8N*!qt^-cs8Dor#Nn&HW<rV!r;Q^3!<2uus81be"W!X.naRmpjPMT[q#C$c
-rtkJ/pS]_f-Fs0LnGhn@D#a]1jn\QKpA':>j8XW~>
-JcF^/9`,:rq>(Ttn*UP_j8Z/$%48ggrQHBWs62]U-2[]A!;ulq!<;ZgqHa.WK)blHoF:j#p\uiF
-p\jib0-DUPs8N)ls8N*!qt^-cs8Dor#Nn&HW<rV!r;Q^3!<2uus81be"W!X.naRmpjPMT[q#C$c
-rtkJ/pS]_f-Fs0LnGhn@D#a]1jn\QKpA':>j8XW~>
-JcF^/9`,:rq>(Ttn*UP_j8Z/$%48ggrQHBWs62]U-2[]A!;ulq!<;ZgqHa.WK)blHoF:j#p\uiF
-p\jib0-DUPs8N)ls8N*!qt^-cs8Dor#Nn&HW<rV!r;Q^3!<2uus81be"W!X.naRmpjPMT[q#C$c
-rtkJ/pS]_f-Fs0LnGhn@D#a]1jn\QKpA':>j8XW~>
-JcF^/!:p-h!W)corrD9^rt"f&s8DuknGiO]q>^<gs8)`p%/BJpnc/XZs8VQfs7QBk)"djoq>^Kg
-s82iroCN"\p\4^fq#:0^s8Drs)>sL2s8VKds82Bes8Ducqu?]bs7c3dp](*hrtbA/nc/Rds7lTn
-s8;`ms82iroCN"\p\t1#o(N"]s8;osrVuBbJcFd1J,~>
-JcF^/!:p-h!W)corrD9^rt"f&s8DuknGiO]q>^<gs8)`p%/BJpnc/XZs8VQfs7QBk)"djoq>^Kg
-s82iroCN"\p\4^fq#:0^s8Drs)>sL2s8VKds82Bes8Ducqu?]bs7c3dp](*hrtbA/nc/Rds7lTn
-s8;`ms82iroCN"\p\t1#o(N"]s8;osrVuBbJcFd1J,~>
-JcF^/!:p-h!W)corrD9^rt"f&s8DuknGiO]q>^<gs8)`p%/BJpnc/XZs8VQfs7QBk)"djoq>^Kg
-s82iroCN"\p\4^fq#:0^s8Drs)>sL2s8VKds82Bes8Ducqu?]bs7c3dp](*hrtbA/nc/Rds7lTn
-s8;`ms82iroCN"\p\t1#o(N"]s8;osrVuBbJcFd1J,~>
-JcFX-!V?<hrtbJ2s82`os8N#ms8VHbs8V`ks8Dipo`"k/oDejbs82irq>^Kas7ZKhs7ZEkp&G'[
-s8VZgrrMWbrVlllrr<#rs8NE(s8W)ps7uZorVlg>p&>!jrVuTkq#CBjp&G'hrr)los6]jYs8Vck
-s6fpeo)AY!p]'a_s7ZKgs7uWnnGi66s5X-0~>
-JcFX-!V?<hrtbJ2s82`os8N#ms8VHbs8V`ks8Dipo`"k/oDejbs82irq>^Kas7ZKhs7ZEkp&G'[
-s8VZgrrMWbrVlllrr<#rs8NE(s8W)ps7uZorVlg>p&>!jrVuTkq#CBjp&G'hrr)los6]jYs8Vck
-s6fpeo)AY!p]'a_s7ZKgs7uWnnGi66s5X-0~>
-JcFX-!V?<hrtbJ2s82`os8N#ms8VHbs8V`ks8Dipo`"k/oDejbs82irq>^Kas7ZKhs7ZEkp&G'[
-s8VZgrrMWbrVlllrr<#rs8NE(s8W)ps7uZorVlg>p&>!jrVuTkq#CBjp&G'hrr)los6]jYs8Vck
-s6fpeo)AY!p]'a_s7ZKgs7uWnnGi66s5X-0~>
-l2LbZrr33!r;Zfks8Dor!rDrkrr33%s7lQmq>1'i%fZM$s8W)uoDeF^q"FadJcC<$JcGWIJ,~>
-l2LbZrr33!r;Zfks8Dor!rDrkrr33%s7lQmq>1'i%fZM$s8W)uoDeF^q"FadJcC<$JcGWIJ,~>
-l2LbZrr33!r;Zfks8Dor!rDrkrr33%s7lQmq>1'i%fZM$s8W)uoDeF^q"FadJcC<$JcGWIJ,~>
-kl1_]oD8@a+8l07nb`@^s8Vccr;Zfqs8Dutp\=dcs8Micp&G'jq#13noCdb8JcC<$r;V9~>
-kl1_]oD8@a+8l07nb`@^s8Vccr;Zfqs8Dutp\=dcs8Micp&G'jq#13noCdb8JcC<$r;V9~>
-kl1_]oD8@a+8l07nb`@^s8Vccr;Zfqs8Dutp\=dcs8Micp&G'jq#13noCdb8JcC<$r;V9~>
-l2Le]rVc`uq"+ObrVlosr;-EnpAY'srr<#trVuWXrr3#tqu6Ttr;-BfJcC<$JcGWIJ,~>
-l2Le]rVc`uq"+ObrVlosr;-EnpAY'srr<#trVuWXrr3#tqu6Ttr;-BfJcC<$JcGWIJ,~>
-l2Le]rVc`uq"+ObrVlosr;-EnpAY'srr<#trVuWXrr3#tqu6Ttr;-BfJcC<$JcGWIJ,~>
-l2Lnbs8Vokrr3]'rr;ohs8DlqmJ[(Sn,NF_rr;c^rr3N*s8Vuks8W#ss7Z'as7u>=s+13$s8;nI~>
-l2Lnbs8Vokrr3]'rr;ohs8DlqmJ[(Sn,NF_rr;c^rr3N*s8Vuks8W#ss7Z'as7u>=s+13$s8;nI~>
-l2Lnbs8Vokrr3]'rr;ohs8DlqmJ[(Sn,NF_rr;c^rr3N*s8Vuks8W#ss7Z'as7u>=s+13$s8;nI~>
-kPl+kq#(0kp&G'goDS^hqu??^rVm#ss7uWlrr3&lrVlcq!rW)trr<#sJcC<$JcGTHJ,~>
-kPl+kq#(0kp&G'goDS^hqu??^rVm#ss7uWlrr3&lrVlcq!rW)trr<#sJcC<$JcGTHJ,~>
-kPl+kq#(0kp&G'goDS^hqu??^rVm#ss7uWlrr3&lrVlcq!rW)trr<#sJcC<$JcGTHJ,~>
-l2Le^qu6Trp\b$j&,lP.o)8U\s8Vfms7?6hq#:9rnc/OepAY(!r:g6`qYL6er;ZVEs+13$s8;nI~>
-l2Le^qu6Trp\b$j&,lP.o)8U\s8Vfms7?6hq#:9rnc/OepAY(!r:g6`qYL6er;ZVEs+13$s8;nI~>
-l2Le^qu6Trp\b$j&,lP.o)8U\s8Vfms7?6hq#:9rnc/OepAY(!r:g6`qYL6er;ZVEs+13$s8;nI~>
-k5Q_)rr<#lp&FX`s7cQhs7Z<hs8DutqZ$0ep%&.[s8Dunr;ZNbrVlumqZ#u7s+13$s8;nI~>
-k5Q_)rr<#lp&FX`s7cQhs7Z<hs8DutqZ$0ep%&.[s8Dunr;ZNbrVlumqZ#u7s+13$s8;nI~>
-k5Q_)rr<#lp&FX`s7cQhs7Z<hs8DutqZ$0ep%&.[s8Dunr;ZNbrVlumqZ#u7s+13$s8;nI~>
-l2NC0s8VZis-XfMeGlm-(Uj@a(F-Njs1fWp`W*e''u0d[+<@ojZmug4oDc6F+LZk$JcD,;qYog\
-J,~>
-l2NC0s8VZis-XfMeGlm-(Uj@a(F-Njs1fWp`W*e''u0d[+<@ojZmug4oDc6F+LZk$JcC<$qu;0~>
-l2NC0s8VZis-XfMeGlm-(Uj@a(F-Njs1fWp`W*e''u0d[+<@ojZmug4oDc6F+LZk$JcC<$qu;0~>
-kl1\ZoD\bA%ahRUp-P1O7/o??WB1(V1pE]9s!snQ1B0J7[5.VFUS]gHpH,d[2uipUq>L6k"o82u
-rr2kIs+13LrrE&rquQEerr2?cJ,~>
-kl1\ZoD\bA%ahRUp-P1O7/o??WB1(V1pE]9s!snQ1B0J7[5.VFUS]gHpH,d[2uipUq>L6k"o82u
-rr2kIs+13FrrDfYs*t~>
-kl1\ZoD\bA%ahRUp-P1O7/o??WB1(V1pE]9s!snQ1B0J7[5.VFUS]gHpH,d[2uipUq>L6k"o82u
-rr2kIs+13FrrDo\s*t~>
-l2NL9s8;]mlP[^`$iD+2k8!J#q">Ens!$P(%e(55mhPj5n(nl\"97^,li.:Trs/Jtrr)j'q=O[Z
-qu6Nms8RZJJc)PG"T.iaq"t'g!r2comf.e~>
-l2NL9s8;]mlP[^`$iD+2k8!J#q">Ens!$P(%e(55mhPj5n(nl\"97^,li.:Trs/Jtrr)j'q=O[Z
-qu6Nms8RZJJc)MF!r2WhrVllqm/MS~>
-l2NL9s8;]mlP[^`$iD+2k8!J#q">Ens!$P(%e(55mhPj5n(nl\"97^,li.:Trs/Jtrr)j'q=O[Z
-qu6Nms8RZJJc)PG!ri)qk5Tr~>
-l2NU)s8Drps.0Bb$LA6%rrE'!p[8CfrW)ue"9/B$m0<7es8NGs%J0T$s8N-"q?luns8Vrkp\t6l
-JcC<$WW*;(r;QTerr2KfrpKf:~>
-l2NU)s8Drps.0Bb$LA6%rrE'!p[8CfrW)ue"9/B$m0<7es8NGs%J0T$s8N-"q?luns8Vrkp\t6l
-JcC<$V>gYls8V]Ws*t~>
-l2NU)s8Drps.0Bb$LA6%rrE'!p[8CfrW)ue"9/B$m0<7es8NGs%J0T$s8N-"q?luns8Vrkp\t6l
-JcC<$V>gYps8VcYs*t~>
-kl37.q>^?ln,Mndrr_WI%f[:DknNde&aoK(r"Abl)u:6+lk9<uqtL?frt"Df$i^/8lc--3ZEBq2
-\$`TIZ`\kdJ[DD`$)t/6\[:u.r;Q]`s*t~>
-kl37.q>^?ln,Mndrr_WI%f[:DknNde&aoK(r"Abl)u:6+lk9<uqtL?frt"Df$i^/8lc--3ZEBq2
-\$`TIZ`\kdJ[DD`#H=r4\[;&0rTsQ7~>
-kl37.q>^?ln,Mndrr_WI%f[:DknNde&aoK(r"Abl)u:6+lk9<uqtL?frt"Df$i^/8lc--3ZEBq2
-\$`TIZ`\kdJ[DD`"f\`2\[;"os*t~>
-l2LhPs7ZEk/aB<BqF%]f9E.#MO];AW6Ck#<s%/0S8,[email protected]]nkcH]1AgtKs8D`lrrpF:
-rr<#rJcC<$V>gVpnA4o"s8MZjJ,~>
-l2LhPs7ZEk/aB<BqF%]f9E.#MO];AW6Ck#<s%/0S8,[email protected]]nkcH]1AgtKs8D`lrrpF:
-rr<#rJcC<$V>g\qn%eu&li2J~>
-l2LhPs7ZEk/aB<BqF%]f9E.#MO];AW6Ck#<s%/0S8,[email protected]]nkcH]1AgtKs8D`lrrpF:
-rr<#rJcC<$VuQeq"8V>urTaE5~>
-kl3XAp[S:Q0Esl%s3(fr[f=G^/[k3L_^-&Grjt9(\c8o_*P_Wt"<tV[rPK-YbQ%,1s8W&ts891u
-rr2uoJcC<$V#LSq[f?(#qu?]qo`'F~>
-kl3XAp[S:Q0Esl%s3(fr[f=G^/[k3L_^-&Grjt9(\c8o_*P_Wt"<tV[rPK-YbQ%,1s8W&ts891u
-rr2uoJcC<$VuI&"rr2\urq66hmJh\~>
-kl3XAp[S:Q0Esl%s3(fr[f=G^/[k3L_^-&Grjt9(\c8o_*P_Wt"<tV[rPK-YbQ%,1s8W&ts891u
-rr2uoJcC<$W;d/%q"ss]Z2F4jm/MS~>
-kl2"Xs8V?\s7Z?is8;]m#l"B!nGi:Ws7?0g"76'dnc&Omp](0ks7#XZrs\i&r;Q`%s8W)up&Fi=
-s+13LrrDurrrTY/qYL6lrq-5@~>
-kl2"Xs8V?\s7Z?is8;]m#l"B!nGi:Ws7?0g"76'dnc&Omp](0ks7#XZrs\i&r;Q`%s8W)up&Fi=
-s+13Lrs&8rrr9;'p?Va/~>
-kl2"Xs8V?\s7Z?is8;]m#l"B!nGi:Ws7?0g"76'dnc&Omp](0ks7#XZrs\i&r;Q`%s8W)up&Fi=
-s+13MrsJ_tq"jijqt'^`rU0]9~>
-kl344s7ZKmoDedgs7lWor;ZTmrr;ims8W)urVuoprqlWnrVucps7QEhs8;omrr<#srr3<(s0;V(
-qu?TorIP!"s/#_sYQ+:ls8W)js*t~>
-kl344s7ZKmoDedgs7lWor;ZTmrr;ims8W)urVuoprqlWnrVucps7QEhs8;omrr<#srr3<(s0;V(
-qu?TorIP!"s/H#&rr)]mX8_YTs*t~>
-kl344s7ZKmoDedgs7lWor;ZTmrr;ims8W)urVuoprqlWnrVucps7QEhs8;omrr<#srr3<(s0;V(
-qu?TorIP!"s/Q)+rVQBaql'D[qu-K]s*t~>
-kl1bPs8VcjrttV4rr;TipAb0jp]('hrVuWhs6]jbp&G$irsAZ$s7lWoq=t!eq>UN&s8.BIJcDPG
-$N9u(pU1%rs8N&lrVllro`'F~>
-kl1bPs8VcjrttV4rr;TipAb0jp]('hrVuWhs6]jbp&G$irsAZ$s7lWoq=t!eq>UN&s8.BIJcDPG
-$N0l%p9akos8;ojrVllro`'F~>
-kl1bPs8VcjrttV4rr;TipAb0jp]('hrVuWhs6]jbp&G$irsAZ$s7lWoq=t!eq>UN&s8.BIJcDPG
-$MsSsoWnGgrVccirVllro`'F~>
-kPkYPs8VZhs8Va"rVuohs760hnbiFYrr3,mq#C3frVm;ss8V<_q#9jas7cQeq>UN&s8.BIJcDPG
-$N9u(s0Mb$s6fpbrr2uroDa=~>
-kPkYPs8VZhs8Va"rVuohs760hnbiFYrr3,mq#C3frVm;ss8V<_q#9jas7cQeq>UN&s8.BIJcDPG
-$N9u(s0Mb$s6fpbrr2uroDa=~>
-kPkYPs8VZhs8Va"rVuohs760hnbiFYrr3,mq#C3frVm;ss8V<_q#9jas7cQeq>UN&s8.BIJcDPG
-$N9u(s0Mb$s6fpbrr2uroDa=~>
-l2LkRs8;?brsSPns8Vlorr;ZkoDJUf%/Khks82iro`+Uas5j7[$2FDtrr<#os8VulrrTP,qgncu
-s/#bqrWW2sr361urr*&tmf3=_oDa=~>
-l2LkRs8;?brsSPns8Vlorr;ZkoDJUf%/Khks82iro`+Uas5j7[$2FDtrr<#os8VulrrTP,qgncu
-s.KAnZ2ae%rri5es8Vlcs*t~>
-l2LkRs8;?brsSPns8Vlorr;ZkoDJUf%/Khks82iro`+Uas5j7[$2FDtrr<#os8VulrrTP,qgncu
-s.KAlZi'h,qsOLapAOX`J,~>
-kl1YUrr2ugrr3Dqs8Vfbs8VQfpAap[rr<#d'*%D"s7u]cs8Vc`s7Pj\q"jm_p\t<$s8.BIJcDSH
-$NBqsnG0$[qu#sPrr3#pp%/36~>
-kl1YUrr2ugrr3Dqs8Vfbs8VQfpAap[rr<#d'*%D"s7u]cs8Vc`s7Pj\q"jm_p\t<$s8.BIJcDMF
-"9/&pXoA>$o^Mk[!VuBZs*t~>
-kl1YUrr2ugrr3Dqs8Vfbs8VQfpAap[rr<#d'*%D"s7u]cs8Vc`s7Pj\q"jm_p\t<$s8.BIJcDJE
-!r`/(rr3#no)AXjp[\:Ts*t~>
-kl:\]/H5JFRL9Y(s1KZs_#D:j*5V[TV]62ks/d[j_Yq7g'Y"+^*$`N(s1g$'`q]B0!jhq(JcC<$
-V>h#&q"<qHWq,o\r;6HmrUKo<~>
-kl:\]/H5JFRL9Y(s1KZs_#D:j*5V[TV]62ks/d[j_Yq7g'Y"+^*$`N(s1g$'`q]B0!jhq(JcC<$
-UAk\ss0MV%s8W#qs8;orrq-5@~>
-kl:\]/H5JFRL9Y(s1KZs_#D:j*5V[TV]62ks/d[j_Yq7g'Y"+^*$`N(s1g$'`q]B0!jhq(JcC<$
-T`5#'rVm*$rVu`mrVZ<fJ,~>
-kl1Y\rr4G=+j7n>n5#UR6N9H@VFKqP:SXONpcYFE4o\<LX@E4JT;"sIqbWcO9_eVhZiBoRs+13H
-rt,,$o'Z.anFcP9r:U*bs8Vugs*t~>
-kl1Y\rr4G=+j7n>n5#UR6N9H@VFKqP:SXONpcYFE4o\<LX@E4JT;"sIqbWcO9_eVhZiBoRs+13E
-rso&.bPM5;kl:AVp](6hrq6;A~>
-kl1Y\rr4G=+j7n>n5#UR6N9H@VFKqP:SXONpcYFE4o\<LX@E4JT;"sIqbWcO9_eVhZiBoRs+13C
-rrCRJrs.ojq#C$cqtB[^J,~>
-l2NC/s8V?`ruf+h0)l"HpBpU.l1,/TrsJ;b%0$P)q#L3lrT=@a!<;@!nG`d[r;cWm!jhq(JcC<$
-V>gPmq#CU2k5PDWq=F4XJ,~>
-l2NC/s8V?`ruf+h0)l"HpBpU.l1,/TrsJ;b%0$P)q#L3lrT=@a!<;@!nG`d[r;cWm!jhq(JcC<$
-V#LN!$3^_7!so#ElMpn[q!\7^p&BO~>
-l2NC/s8V?`ruf+h0)l"HpBpU.l1,/TrsJ;b%0$P)q#L3lrT=@a!<;@!nG`d[r;cWm!jhq(JcC<$
-V#Lr8)&!bs%L`X^mJm4\p?_\Ks*t~>
-kPm..s8VSc$4`*r&+KT#rrE)s!<;iqs8E&u$Le!#rsJ,m!:pisrW)un!<;[+q>($lZiBoRs7?9d
-rpg$cro*k[rqu]nrr)lsrr)cprlP0KrquZhq"OIUq>C0irl>$Lq#D6N+;uIN-33i9p&G'^oDa=~>
-kPm..s8VSc$4`*r&+KT#rrE)s!<;iqs8E&u$Le!#rsJ,m!:pisrW)un!<;[+q>($lZiBoRs8;om
-rnm_arr;utrr;utrr;utrm(NJrr;utrr;u`s8DrbrrE&srrE&CrsfPt3^5_o5<AuIr:U*io(2m3~>
-kPm..s8VSc$4`*r&+KT#rrE)s!<;iqs8E&u$Le!#rsJ,m!:pisrW)un!<;[+q>($lZiBoRs7u]m
-rS[\arVuirrVuirrVuirrR1`FrV6EYrTX@\rQP9P,#D<E=Bnm#%fcM!qtodZo`'F~>
-l2O!Hp&G'Zs!,1n/,fqFo*t^4k4T;br"A8_$iV(,pC$m0o&gD\$M3d#s8NE#r<WB"s7+16Za[38
-b-JC`\?<;tZMh'.ZN%6:[CEcY]"G\dZh^g(Z2_-0Zhq'-Z2:^:Z*UdEZaI-IZaI-IZaI-IZa02-
-&[/4DXKAt8Zb<`DVmNG5WMd#lZN%6S[Bm9I[Bm?O]WecLYe@BY]!f/WYIq<TYJIQU^9tAU[C-"B
-!O\s,!!!&t!"G@3Yd1jK]!o/W]<eEFiNick\ZiNGTs_B+Z4X&9'a"sH!"feCo(r:apA=adp&BO~>
-l2O!Hp&G'Zs!,1n/,fqFo*t^4k4T;br"A8_$iV(,pC$m0o&gD\$M3d#s8NE#r<WB"s7+16Za[38
-b-JC`\?<;pZ2q59riuL,rN61)Z2(`rZMV!-Z4+"DZ*LX?Z*LX?Z*LY)Z4XFN_R-SWY-PaNY->UF
-_Qg2JrNZ1(rj2I+(p^KV\?E0CXh([I[B[*CWjf7@Wk>LA\Zu.?rj;^5&?Z-C"WRaQ$3UF*a0W([
-Z*jS:"1bb:\`'n#Y.CmIYbJS9qQg[?#!k4A5Wq\'#64`#s7c*aJ,~>
-l2O!Hp&G'Zs!,1n/,fqFo*t^4k4T;br"A8_$iV(,pC$m0o&gD\$M3d#s8NE#r<WB"s7+16Za[38
-b-JC`\?<;tZN%95[^<LB[/[E3Yl:d,Xfeu*[f3Z6ZN%0+ZMq6.[LomNY->(5Y->(5Y->(5Y->.9
-o!Aq:`3unXXK]CM[(!u_`3ZZF[f3Z6ZMq*-XoYi8rNcI-s0=MiX/rG%[Ap^@Xg"n(Z`UL0\>ld@
-ZG+/j]XbGVZ`OBC&1nkG+VbKhca^?iY-P77Z+@?E^#?C)Ye7<QZD>"AqR$jK-#RIKH?4=@*<6'4
-q!\+Os*t~>
-kPm[Dli6JUfY7df;3naMs$EN[3;WGJWNSJT9V&.Ps$E$a5kI[>X#U.J8?.bGs7QElq"4Obs1eC%
-r;ZfmJcGKEs82rqr;6Hjs8Mcms8N#q"T/,or;?'a*rc3=s8N&ts8N&ts8N&rpYkT=s8VuorqZ9_
-q!7bRl2Lh_rVQQn"nquqqZ$KlrsA,nrr;cnmJZq\rVd?)p])<Y#7Vab/-,8$lMgMUrrr#nrq?-[
-iq!6@p$)JK!VGjWoaC0f'ESXB!!EZ0i:6gH!r)<cp&BO~>
-kPm[Dli6JUfY7df;3naMs$EN[3;WGJWNSJT9V&.Ps$E$a5kI[>X#U.J8?.bGs7QElq"4Obs1eC%
-r;ZfmL&_)Ms8Duq"T/&mr;OP4!Uog_rr`#ms8N#t!qlTnr;Hfurr;ugrr`/rqYpKo&G?&!pAXjd
-rquc\rqcWdrp0L\s8N&u'a-H^0KiB!7g&e[nc/7\q>1!YrVucQrrW0!pAP!kr;$@"#=^mR91DK=
-#P.WfrrDuhs*t~>
-kPm[Dli6JUfY7df;3naMs$EN[3;WGJWNSJT9V&.Ps$E$a5kI[>X#U.J8?.bGs7QElq"4Obs1eC%
-r;ZfmJcG]KrqulprU^'hrUBjXrUBgkn+ZeXqXjU]rrDcds8W&sqZ6Qjq>CEkqY:!fr=JMrp[e:T
-qtp3dkPP#Nnb_\Ns8W)urt5`0=\r[[AmuMTqt^9drV?3ao$.1F/9uQ(O,/C),Q@B2pA"O\o`'F~>
-l2NI:qu?]fs0MnKWW0RV(!?*f&Mq#us0`ONf)NBG!6"oF3!IM*`sX*Co`)`G+3"$Pqu6s"Y5/+t
-rr;ioM>R;MqYU9is8E6#qtg0aqY9sap\Xmb!r2Werqc-]')_Y)qu$?hqu$?hqu$<fq#:6_rr3f.
-s8MrppAa^]r;QWnr;QWnr;QWnr;QWiqu6U/qZ$'brq$-gmJcVMrq$-[rqZQmqYUs$pZVZ0)?L9H
-![%L4o(VtXnc&OlrqZBbo'52r#2J>b%LFF&qB$:h!#PeC!<<-2!:p'eo)JU]rq6;A~>
-l2NI:qu?]fs0MnKWW0RV(!?*f&Mq#us0`ONf)NBG!6"oF3!IM*`sX*Co`)`G+3"$Pqu6s"Y5/+t
-rr;ioM>R;KrqlQgs7lZkrqccpqu#gX!rVrnmJ@[qqtg3dqtg3dqtg3dr;Zcrr9aL_qt9pf!r2fc
-qYCBmqYU-dqYpBhr;ZZort>>,rpTjdo`+sZs7,pbo`+O_qY'pqqZT_a2F]qm63R5d"n;Qlrr;uR
-rs&E$67s]U55@DR#=LXE845^.'`\41n,NFdo`'F~>
-l2NI:qu?]fs0MnKWW0RV(!?*f&Mq#us0`ONf)NBG!6"oF3!IM*`sX*Co`)`G+3"$Pqu6s"Y5/+t
-rr;ioLAUuKp](3j!rr6!r;HBequ$ZtrVuipnbX-uqt^-bqt^-bqt^-bqtpEiqtK:Iq"XI[rrrDp
-qsF:Zr;6NirV-<brq-6a!r)Ndr;R<%s8Vlos7$'[qu?Bip&Fs_rs0KZD.nHOF(H6errD`CrrcOr
-6=*ai=:e[fEGgA_J9>QXs8V`Rq#10`s*t~>
-l2Lq`s8W&trVlg*k5,,Ks7u]prTjI_rVlg=p\k*[s8W)up&G$krVuK`s7Q9ds8N&uqtg9krVcc*
-rr3&qs8ITLs8N#qrVQWo#64]&rr;utrr2ZlrVl`pnc&%X!VQBdrrVoooBuVYrqcO0o^)MQr:^'^
-q=O@ToD&.Op\=IFq=O+Mq>U7Vq=sXQlMq8)$31&2'`d[kh=pR=q"s(Hq!mu#,Tn9R+s8'P+s8'P
-+s8'P+s8'P+s8'P+s8'P+s8'P*%i/fl2KiYkiV*kkiV*k!$2IS#71YT!:p-grrW,krq6;A~>
-l2Lq`s8W&trVlg*k5,,Ks7u]prTjI_rVlg=p\k*[s8W)up&G$krVuK`s7Q9ds8N&uqtg9krVcc*
-rr3&qs8IlTqu-WrrlG-1rXJo$rVuosrr)`nrq6<\rr;Qgq#;-)nGiLfq>^9jrVZ]ls8Vrps8DHe
-rUopb3<qB17mo^73s>T`lMpb]qu>jZs8N$S4[2+p5!M4q5!M4q5!M4q5!M4q5!M4q5!M4q5!M4q
-3))='rs/it4$lD-5Xu%Xs8W'!s82HgJ,~>
-l2Lq`s8W&trVlg*k5,,Ks7u]prTjI_rVlg=p\k*[s8W)up&G$krVuK`s7Q9ds8N&uqtg9krVcc*
-rr3&qs8IiSrr'e8!;l?`&FfGhq"FFVq"t$]qs4%NpA=^bq>:0ir;QfpoD\airq-3mp&G'jp\tUF
->'5@LMM5FXs8VrnrrDHbs!'m0<)ut!<)ut!<)ut!<)ut!<)ut!<)ut!<)ut!<)usp@SZRq%jl"&
-F*rCSC)m9Rq"OUaqt0o=~>
-kl21ms8)cqoD7tVs7-'fli6\Xrt4\hs8;oso)Jacs7c9ap&F[[qu6lms82irrqlWn#Hn%(p%&.\
-r/1LNs8W'1rVH<ap\+7NpA=jhrr;utrr;usqu-?ir;QfsrU]mdrpTjln,NF_qu?]irVllmnc&Xh
-qtg/6qt9j\qtp*So^hbEp$;;Cp##H7n+?;Eq"aa\p\"+E,60.m"Tn`.+93]A-n7J,1FPd6/1i@D
-p\Ogar;-?fr;-?fr;-?fr;-?fr;-?fr;-?fr;-?frquchq#($crqucnrqucj!#lO^$P<dc!;Gp]
-s7H6erq6;A~>
-kl21ms8)cqoD7tVs7-'fli6\Xrt4\hs8;oso)Jacs7c9ap&F[[qu6lms82irrqlWn#Hn%(p%&.\
-r/1ISrVZWm!ri/tq#:TurVlcprVlcbrW)oorq$-jrr)Ecs8NMorr2N\rr2Hdrqucbnc&Ibrr2rt
-#5eH!s8W&krr3&us7uZo"7?-gqu-QprqudT64I-P92\AW9*RRK3B0\a4>BYZ3'B>&rr2lqrVlcp
-rVlcprVlcprVlcprVlcprVlcprVlcprr3,tq>^Blq>Up-4#oPk7QE[>s6fmcoD\@]J,~>
-kl21ms8)cqoD7tVs7-'fli6\Xrt4\hs8;oso)Jacs7c9ap&F[[qu6lms82irrqlWn#Hn%(p%&.\
-r/(CUr;-9frq69qr;QWnr;QWmrql`lrW2rrr;?-c!WMiao+:WjjnSW>nGE+Nq=sUSjn/BHqY:!_
-rs&B#q>:'^oD&=cp@J;"="8Z'IZ'#7DB;bZ8OZ`?764X,6qU)-rrN#qpAYU;<*EmMEaW,is6fa[
-n,;kXJ,~>
-kPkYOs8VQdrs8B!s7cBis7l-`rrr2tp&G'brr2umrr3<!s8W#jp&Fddq>C6srr<#qp\)"Gs82g&
-rquTfp\"1Lp\smdqu63e%fZD*r;QWnr;QWnr;QWnnG`mls8W&tqu6Whnc/UVruqC=q"a^\q"a^Q
-p%e1Nq!RnKm.'Z3n+,]5l13p+nes;<r\4X3/Li++!!O#6!"TG;lf[g/hY$X7nFQ;Clh0'5!;lNj%
-g4(&,SM7;*WZ$9r;ZNkrq-5@~>
-kPkYOs8VQdrs8B!s7cBis7l-`rrr2tp&G'brr2umrr3<!s8W#jp&Fddq>C6srr<#qp\)"Is8W)u
-rVlfdrrE&trrE&fs8N#es8W';rVlcprVlcprVlcprVlTls8Duqs8Vf]rqZTjnc&Ugq#;3(s8W#q
-s7cQnr;Zfns8Vloq>^*es!g;pr\tZR4$,V*&iWKN1/U+k%KHA+s6BXarVHNn!rW)uir'&VpAOaa
-o(`.nrt.7Y8Noa13X#K^p\sdTs*t~>
-kPkYOs8VQdrs8B!s7cBis7l-`rrr2tp&G'brr2umrr3<!s8W#jp&Fddq>C6srr<#qp\)"Hs8;ig
-s8Dlrqu-Kcs8DopqZ-Tbruh46qY9p^qY9p^qY9p^qY9[YqtpEms8Vl`qt9[PoCVnWrU9ajqu?]n
-r;ZE[rrY2T8kT%Q$VO:q0jK$KCm&jA,lR`CpA"XfrSmMSo'c;Ap?VMI%fI8:>@_,X>>Pn2r:BdU
-o)F4~>
-kl2:\s8)?ep](3lmJm4Js8D9`s7QBk!r;lgrr3,pnGhqIrr3#uqu6U#o_\Udrr;]hqu6o,rVuon
-rUfl9rs/Q$r;$-^p%\4[s8N&rs8MTh0E2"Ns8N&ts8N&ts8N&rq=j[Yq"ORXq"ORXq"ORYqr7GG
-m.Bo;q"a=WrV6Ebrq$%2qtTp\p\=LXp[Iq@p\=CPp[mq>ng$+K.4[1k1+"I@n+ZhV)u'$rnE9iY%
-hf-P&etE6mdfi;p\=LWp%7G<pYc&On,<7dn+cqY!!`Sfs8Vurs7c0cJ,~>
-kl2:\s8)?ep](3lmJm4Js8D9`s7QBk!r;lgrr3,pnGhqIrr3#uqu6U#o_\Udrr;]hqu6o,rVuon
-rUflAs8;iss8McmrVcfsrr2Kgq>T=P%d*fkq#CBjs8VNer:p<al2Lt_s6p!fr;$?l%Jp)]7RB*s
-6RY8Z3WK*Rrs0/k5"\.59K<IZrrN)tqu6Zjrr:sV!9jC\!9j7X&H;e7%1<CH#m:e(rqlEgrq?!a
-J,~>
-kl2:\s8)?ep](3lmJm4Js8D9`s7QBk!r;lgrr3,pnGhqIrr3#uqu6U#o_\Udrr;]hqu6o,rVuon
-rUfl>s8Mujs8DfpqYg9krV$9jrVcWnq>gENrrD6Xrs&&oq!e"DrUKmfrU9am5uUQK85M0>6Uq(T
-s8)fpr;R$W??D'^Q],2lo)I\D!93tP!93hL&GlV>)BKk?*YfOZrV?'bs82HgJ,~>
-l2NC7s8W#sqZ$<hs8Vrds8Vfms8Dorq#C<mrqcZns8W#ss6BX]qZ$?jl2Ue[s7ZEk$iB_ss8B>'
-s8Vrqrdk+JrrN,srql?f"TJ>srr2!Y!WMlco`FmR"Sr*6!s%ifqtg3cq"a^\q"a^\q"a^\q"a^[
-pDEEU0/G+;.O[5/./!6!o^_YIo^VSEp%S4[p`&u$oBQB%&el>n&M4"Jo'lDKoCr(Uq!n%Mo).MI
-#Pe>ir:g6kp%n\!rqlK_jLa@>o]lGUs82ieoDa=~>
-l2NC7s8W#sqZ$<hs8Vrds8Vfms8Dorq#C<mrqcZns8W#ss6BX]qZ$?jl2Ue[s7ZEk$iB_ss8B>'
-s8VrqreUULrqHHhrq$0frW<-!ro*kbp(7B6"Vq:Q";:k2jSobf4#f;X5;G&a4#[-=q#C?mrrW0!
-rqQL)r!#GE4A%Xq8-Jkjr;Zfprr2p"qu?]piVs/Xs76*^s8Vccrs\VKpAb0_s82cfrp]pZs*t~>
-l2NC7s8W#sqZ$<hs8Vrds8Vfms8Dorq#C<mrqcZns8W#ss6BX]qZ$?jl2Ue[s7ZEk$iB_ss8B>'
-s8Vrqre1=OrVQKlr;clrrVucoqZ$HmrV-?krVcTpqYU0\rUBgsr#$%d*&&K`)']Rgs8VuYrs;:h
-:.A2O6;U0<p&>0oq>'mdrVm-H@pa/,G]%"(e,K[Js76*^s8VcbrrCpTrs&5tqYTscp%/36~>
-kPkVVs82cp#Oh]mq>^*eq>UBpr;HKl"8)Wgr;Q^&rVuors8)clrqQKm$iU#%rr2lqZ2FOqq>UDO
-rr;ouqu-NkrrDrqrrW3"r;Q]rrql`qrWE,rqu-Bj$NL,*r;Zfjs8W)tr;QcqrVllorVlues8VK]
-ruh42!%8]k*$#V',QRB+o^)5Co(DSHo_%hKo_%kLo_%nQ0a96gp%S+QnG`.iq"ORXq"ORXq"ORX
-rV-BhrqcfrqsOgd!!Vuhq=XXYrql`ls7cWhp=&X?p\XXTm]cBYpAF=XJ,~>
-kPkVVs82cp#Oh]mq>^*eq>UBpr;HKl"8)Wgr;Q^&rVuors8)clrqQKm$iU#%rr2lqZ2FOqq>UDJ
-rr)fqrW`#prr;utr;Q]rqYpKorVc`qs7uX*s8Dups8VZhrqcKjrqlZnq>L9upAP!js6]jdmIpPd
-"@[email protected]'$57.>h!;Z-arAjm=s#U6@62gf`r:'acrY5>1rr)if&H`@D('Y<Q#lal(qZ$E5rr`,q
-q7Z_+!ri/srr2QiJ,~>
-kPkVVs82cp#Oh]mq>^*eq>UBpr;HKl"8)Wgr;Q^&rVuors8)clrqQKm$iU#%rr2lqZ2FOqq>UDO
-rqud4rqcHbp@RYBo(;SLqZ$Top\Xg`q>1$er;ZZn&cDV)rV?Ehnb`4Xp\Xj_qYU!bqZufiqu?]`
-s8VK[rs9EF>$5lf@oQVOm/R)\s%Ebm8Ol388,rVgs7kgX$2b\M()%u3*tAk]rrW3"rQ,!@pA=a)
-r;ZfqnGe"~>
-l2Me&r;Q`rli6bEs76!`rr;Tirr<#ns8VQfp&"X]rVc`ujT#8Trr3<#s8Dutnb)bUrqucr[f$.+
-q=ojI"9/2pr;?Qpqu$KnrWE)qs7lEirqlcqq>UZsrV$'es8;iq#6+Z&q#CB[r;Quus8Vros7Z6f
-(@hGG+pJ&S%2ffZmG7-jnEoB/kOS*`q(*+1p@e7Sp\+@Tpt#63oCr%Nqss[bqZ6Wor;R2es8DNa
[email protected]'^^qu?]q1&UqDp\=LXp\=LXp\=LXp\=LXp\=LXp\=LXp\=LXp\=LXp\=LXp\=LXp\=LX
-p\=R`rWN/$nF?GCs*t~>
-l2Me&r;Q`rli6bEs76!`rr;Tirr<#ns8VQfp&"X]rVc`ujT#8Trr3<#s8Dutnb)bUrqucr[f$.+
-q=o^ErVdK/nb2t]s8Vrps8W#sp\t-irr;uhruM%9pA+agqu6Wos7u]mp&=sQs8Dusrq??joCi1P
-q>UEo('['#4$?#$7QLbRoDeXdrqHHfs8O`8qE+a>rr)rurr)j$iW&rVs8W&ds8N#trVuj*jT#8R
-s8VrqhuEHKr58O=Zh=%ls8N#rr;cihs*t~>
-l2Me&r;Q`rli6bEs76!`rr;Tirr<#ns8VQfp&"X]rVc`ujT#8Trr3<#s8Dutnb)bUrqucr[f$.+
-q=oXC"T.ufkj89>%Ia#js8N&kqtg-bqYgBbrs/K#pA+agq>:0f#57ohnGE%ArqZotqXXFVmI9o8
-q=t!i#p*N"D/joGE*FC\rrE&srrb>P8P)SR9E7i_rs%TbrVZ]oq<7hiirB#Ns8VurirAfQrr<#s
-rQG3FrVHKmZhX@^s*t~>
-l2NC8s7c-brs8Z1!<:7_'Z'gh%kG'Xs2$#sci:I>%D;_D.NcD)aors)s8BhG'$U=S$2aPoqYRSs
-rqlNjs,-dYrVH<drV$6prqZ?br;?Hls8N#p!WN,trr;rqrXAMqr9s[WqYC0\s8VimrrW&pr;Q^!
-r:p<lrV6C/h>eDn":>,2"!7Nt+[Ik+.lSD0-n"TSqXP6jp\=RZq=sd\q=sUWq=XROrU^!krVQKj
-rr)isrqQKnrVlisrQ5'Br:p6d\EX$D~>
-l2NC8s7c-brs8Z1!<:7_'Z'gh%kG'Xs2$#sci:I>%D;_D.NcD)aors)s8BhG'$U=S$2aPoqYRSs
-rqlNjs,$aTs8W)tr<W?"r:g!cs8W)srsSf*qu6QnrVlfrrr;fnrVmi5r;Z9do(MkWn,E@Ys8N&l
-qtU*crU]LVrUfs_r>,G$*()8G:d.9-9,.(Z=%YA681[Iq3:6_LqZ$Tns75a[r;R9+rVcWbqu$3c
-pAFmhs8Drss8;]ls8=_NrVZWlrVZWlrVZWlrVZWlrVZWlrVZWlrVZWlrVZWlrVZWlrVZWlrVZWl
-rVQEiqRHP)rr2lp!<2WjJ,~>
-l2NC8s7c-brs8Z1!<:7_'Z'gh%kG'Xs2$#sci:I>%D;_D.NcD)aors)s8BhG'$U=S$2aPoqYRSs
-rqlNjs,-gRrsSi&p\"1MmdKlCq#($h"o.ujq>C0hrrN,srVl]o)u]g:o)8"Lo_e%RqssX_qt9^X
-q=OCIkk=`;oD8.rs8Ful>^qc]IV<[Ss$gHs5t=a0=$o=<rrN-!oDSdgq>9mjq=4@\qu?Zprs&H!
-rVcWgq"snHr;-6`q=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq=j^Zq>'aU
-q=V2plMlA~>
-l2NR5s8Vons6fp#8Gt65WD*%EV2nRTqa6pO4T@p2YtG$cVR8kS:TU0Xs6rdaSh^-<r;QZnrs-"4
-s8;Qdp&'^I"9/2pr;?Qpqu6U&o(;ePo'GW2qu6Tls8MrnrsJc'pA"7Vq=4:Xr;HU#rVl3`pAXaa
-p\t.FnG`@VpAY!gqu-Ejqu-<W!"]qQ!!!60-3,pkg#__eo]P`:n`oc=oCVYHoCVbNqY:B^qsjRU
-oDAIUs8W)orsST$s8DrskPtAXs8Kt:"T5t5rqc!]J,~>
-l2NR5s8Vons6fp#8Gt65WD*%EV2nRTqa6pO4T@p2YtG$cVR8kS:TU0Xs6rdaSh^-<r;QZnrs-"4
-s8;Qdp&'ODs8ET.rq-0gs75j]rVu`cq#CBmrr2`n!<2or"oA2ms8Vljrs8W(mf3%]pAapes!@=;
-s7?*es8N&ts8N&ts8E3`:JF5J90cD\:]Kh[s7u]err<#mnc&monGi1]p\+Ucli.Opr;6BUqYKaQ
-r8[hMrr;lpqZ$TpdJa(E"T,e0qYC'g!<)orp&BO~>
-l2NR5s8Vons6fp#8Gt65WD*%EV2nRTqa6pO4T@p2YtG$cVR8kS:TU0Xs6rdaSh^-<r;QZnrs-"4
-s8;Qdp&'LC#lFJnmeZeWo(W+_"8r/us8DiprVZZqrVQTurVufqs8Mfn#4VZgs7lWjrr3,os8Vij
-pAYOF@Y0JmMhlG#,%1HErVllro`#3qq"aa_rVuokq>^'b!rM]`rV$9d%f6(erVc?]rS[SDqtg$^
-qYC9jqY&D1#kn,lqY%>op\4%SJ,~>
-kl3=(rVuo[s8P-ts!R+($N1\=kna!j'CY](q%NJk*;gN0kR.=krpBs^rse2a$NKu#rr`&dYPeA!
-!W2kRrVl]qrVlcq%fH@lq"=4JmdKZ9kkX];rr3'!rVc`lrVliq%/fu!q=!SBp%%kPpZ;>J2tHb6
-q!e(Qo]t\pkMk[h'dY(N,:+Q\-:Rt\!!WE'!tu:ImI^)>lhBcAq!7_Nq"a^\q"a^\q=jdkp&+CY
-nc&OZrrE&tr;ciqrrE&mrs&>qqu?]qrVZZArs&5os8TM(rp0T7~>
-kl3=(rVuo[s8P-ts!R+($N1\=kna!j'CY](q%NJk*;gN0kR.=krpBs^rse2a$NKu#rr`&dYPeA!
-!W2kNrVl]tqu$<imJ6bdp&FXRs8;for;uusrqcQrrr<#no)&Femem(fqu?Nmq>C70p](6ms"m#.
-5sdk(5s\TU8hrq,3CQM##6+W,rVHQes8VZVrs&<!pAajdrU]perVcZoqu?Kqo_\Ucrql^"p\=Ub
-s8Drpr;lipdej@Er;Z`"pA+^errN-!rVlKiJ,~>
-kl3=(rVuo[s8P-ts!R+($N1\=kna!j'CY](q%NJk*;gN0kR.=krpBs^rse2a$NKu#rr`&dYPeA!
-!W2kRrVc`rrVZNns7H-e#laktnbr:Zqtp?l"oJ,mqu-KkrrDllrrD`Zrt@t,='8U-='8R?*,f;?
-MLC)$?6&kB"8r3!q!A"aq#C$co)8.T!;lZn&cMUuq=s[UoC;GIqYKdTpA=a_q9o$Eq=XR\poO&Z
-qu-Hms8DTiJ,~>
-l2NsBr;Zcrs7*<[s7QHjquZluq!e^krrE)e"TJK%nHARhs8N>o&G5i&s8N-"q@!,rs8Vicr;Zf*
-q"=@SrV;$E"98;R"o/-##i>CVqu6`sr;?Km&H;V)lM1,@oCD#0nE9B/o&0N<%ajk9m5$+8-6FQB
-,ShQcqW&LWiW'Z($j7%B)\E)EkPP)Nr9XFUrql]^q>:!f!VcKirrDTPrs8Q&qWn1\q>]dYrrDuq
-rltKCr;Zcs]D_d.rpg#=~>
-l2NsBr;Zcrs7*<[s7QHjquZluq!e^krrE)e"TJK%nHARhs8N>o&G5i&s8N-"q@!,rs8Vicr;Zf*
-q"=@SrV;3JrVd<*rr:pg!sAf9"pkG9)!Lqu!<2ip!<2lq%IsJuqZ$Tgr;ZNkr;Z9crsdres8>;D
-2aT_u.mu9]q#:ck#VnV:92e,L1^j?PrVm)hs7ZKjs6fmarVZ`oqu6Wq!VcKirrDWhrseu-rr;ut
-rr;utrr;usq#(KnrqYs]s82i]r;Qcpp\ufDs8N&ts8N&ts8N&ts8N&ts8N&ts8N&ts8N&ts8N&t
-s8N&ts8N&ts8N&trVm#t[f61'r;Zcqo`'F~>
-l2NsBr;Zcrs7*<[s7QHjquZluq!e^krrE)e"TJK%nHARhs8N>o&G5i&s8N-"q@!,rs8Vicr;Zf*
-q"=@SrV;0Irr)ir$gf,U+=8]e+r:n=nc&OjrVQQlrVluuqu-KlrrDfdrrE#srrDKdrs;Li7T`c"
-6sWSlp\tU?<,I>KQ@*sW*<5R-rrVckqYpKpnG`FcquH`lrtbA)qtg0alM18RrVQQjrVQQjrVQQj
-rV-=%q>9gDo^hMGjnelOrVcTer@@pHs8Durs8Durs8Durs8Durs8Durs8Durs8Durs8Durs8Dur
-s8Durs8Dor#6">&q"O[ar;Qisqu-3fJ,~>
-kl3jEs7lW`s"_XprtYCr(\&79p_EE#+lWG6lPolq&cW7.jW4@$s7@?!rsIui#lal(m)ulG\@8'X
-^U:8PYe#[ss0_j76FF/'U:(%C*Yf>&(F^R/Z_FJ(ZaI-FZE^U<ZaI-GZE^U9_iLt&TWt8tWMu=:
-,oJcc'gEZd.&*]9Wgp9(Xfeh#qQ1U=V?SRa$ig86$R>_U_6:/LXi[QQYeR<]q7$F5qQq!H[^`]C
-Z`h'KZEpjCZEpjCZEpjCZMLp-Z4O:MU8tc7[\p72WkPa@Xg5G7YPYR[ZE^^?ZE^^?ZE^^?ZE^^?
-ZE^^?ZE^^?ZE^^?ZE^^?ZE^^?ZE^^?ZE^^?ZE^[5Zc8m@rr`8ur;Q6dJ,~>
-kl3jEs7lW`s"_XprtYCr(\&79p_EE#+lWG6lPolq&cW7.jW4@$s7@?!rsIui#lal(m)ulG\@8'X
-^U:8PYe#[srNR*AZ*CU7_ZMtd5X@Uo6okRhX0/\4Z2Ls1Za-mArNcI0+3^.c]s>Pa`P0'i5WUMo
-1Fc?Y4[$9g[^`<La1AmpY55^I\Y]d'4@M:r68^t4Z,!HJZDkg>[B.!>`3fBK!4)I-'t1NZZ_t+3
-]X"iJZE^^?ZE^^?ZE^_4YlM$-Yn+FBYJ.oj[C<EA]s4fF[_):?1:"6lZa-pCZa-pCZa-pCZa-pC
-Za-pCZa-pCZa-pCZa-pCZa-pCZa-pCZa-mFXgQ-Arr2fps8MZjJ,~>
-kl3jEs7lW`s"_XprtYCr(\&79p_EE#+lWG6lPolq&cW7.jW4@$s7@?!rsIui#lal(m)ulG\@8'X
-^U:8PYe#[s!3lI*6a3l)]=6?4?"n.sC0+P3(<j(hYct:7YdCdG['6^;Yd1XE\'q(lb.uEDeBcIV
-;+j,^6p"sH:/8mV`P]"#d)3f?\,*u[_QFebApefOH!OSeYe-[0XJj.9[]R->_6O!Ds/lF)Y-7],
-'st<TZ)+\+]!/EEYd1L=Yd1L=Yd1M4ZN%9G[^WfX]>Le]`5oj)\?;^>YH=t8](`Kf['I'E['I'E
-['I'E['I'E['I'E['I'E['I'E['I'E['I'E['I'E['I'E['[EK[)8I0qYU0is8)fpp&BO~>
-l2O!HqYU<mrO#K%s7nsOX>p5BT;jsPpb8bD5lWU1\h3qMUouWT6_UD6s7JCXUc/8Gs8W#lrVZN'
-p\"I`s7lNErtGD3rr;utrr;utrr;utrqlWns8Moq"oeQ%r;6KPs7?6crtYG#n,OIE#R1_N*u=qH
-nG)nYrquZhrqQKirqZTh+7_9=2^1"".4?K%i:6<enF?#3nE]N-o_&4Up\t!frqcNnrVuTlrVllr
-rr3)ss8W#cs8W&rrrDo]rt58.p\XXSp[.MFqu?QmrquNfqu"e>"T8,qqn2n-"9/5rrpg#=~>
-l2O!HqYU<mrO#K%s7nsOX>p5BT;jsPpb8bD5lWU1\h3qMUouWT6_UD4rq/:XVE"VLs8W&ns8W)4
-qtL*is7lNUrXJo,s8N&ts8N&ts8N&tlMph^nc/Lco)A^hnc/Ufs8Dus%fcA)"[GC;5t4(.3=#T`
-n,Emq69%Lk68CV`3(``As5s=\%Jg&%s82ims7Q0eqZ$Tjr=o;6"Uk\K#R:\;s82irqYpBjo_na_
-qu6Hl#6+Z%s8Mrlrr)rsq!\7\rWiK#s8;coqYpKo"T/2us80k:"oePu\GlI's8W)js*t~>
-l2O!HqYU<mrO#K%s7nsOX>p5BT;jsPpb8bD5lWU1\h3qMUouWT6_UD5s7\U_V)SACqtp0\r;Z`/
-qY'mfs7lHSrser,rVuirrVuirrVuirnbrFd$2so(qtU$bs8W&rrs8T'rVuirrVuins76-Ws8Doo
-rs9o^EH->RBkLEfm/IAb6W--E5Y4L9=oSF$pA"Xkrr;`hs8;Tj(^Cd"2)dWM,9-scp%7tLqtp?a
-rVH0`qYL3drq6?erqcWks8DuorVllpnc/XfoD]$orV?Bks89k9"SfD$qYU0i!r`#pp&BO~>
-l2Le`p&=tNqDQUus7=eL+3OT)%OAj`s25osa8`h!'Z0je+;D`n^Dn<>oDciH&&J8Cs8VHbqXF6W
-q"+CSrr2otr9F:arVuoqpAYj+r;QWnr;QWnr;QWnr;QN`o^i+Tqu6lms7>mSqu6-c!rW)oqu6Zo
-rVm)sr:p*br8dYNs8)fqrr3l8qtKR[&J,6N!!*B@!9`J7o(D84nE00+.K(Y7.P!&$0^SJrp[7hN
-q>'p`qt^-bqt^-bqt^-drpg!prqlK_o()GIqu4S7rVl`m!<2rsq#8V>#6+T$qmc\*rr<#tnGe"~>
-l2Le`p&=tMqDQUus7=eL+3OT)%OAj`s25osa8`h!'Z0je+;D`n^Dn66mJb0F'?:(Ms8VKds82i%
-s8;olrr3*"pA=deqZ-E^r!*-!r;Q`ks8N!$s8N&ts8MHd$haPps7uTiq>^6ip%JFbrr)cuqu6?h
-o_najr;QHirWiDts82`ns6'1Ws8Mrp%fZM.#!#(@845a37gT.lrr3/qq>^<ks8>(f5!;%k4?iT@
-p&FgErrE&gs8Drbrs&K&rr;utrh'2orr;l)s8Dfo!<2TiJ,~>
-l2Le`p&=tTqDQUus7=eL+3OT)%OAj`s25osa8`h!'Z0je+;D`n^Dn9;nc-fR'Z0_;o^gu6q=Xcj
-s7u]erVcWlpAFmgrrDuerso#&rVQ?drVHKirVQQjrUKdarsSJrq>KdKmI1#Lqt0mf#lFStrVQQj
-rVQKj"ShibqXFI[!r2W`rqZZnrVZZqme$PZrr)j',$f8;K7\Q"GVJjk#$"Jp:/">Sr^m.i!<(@G
-s8Dups8Dorq>L<iqYc]Z"T,V*qu-Bk!<)QiJ,~>
-kPkh^q"=[[s8)ckrr2uirr3?&s8W#gs82irrpTjd#6"Q$s7QE[rr3r1q=j[^pAapfs6B@Ll`K^G
-p$hkPs8N&urr;l`rs&H!p&G'kqWe(br:Tgas7Q'`s8VclrrVrfr;-HhrVZZspAa^[s8W'2k5P8W
-nG`%Yr;QWnr;QTgp\F^cr$_C.m/S(2#64`+#m11Z0IJ7t/MI5M+<g(9lKdd'md]l.p\=L[o_e<3
-s3UcLrr;l)s8D9`J,~>
-kPkh^q"=[[s8)ckrr2uirr3?&s8W#gs82irrpTjd)#aI6s7QE[rr)feqYL-iq>^0gs7--hrk\U6%
-/U#!s8)]loDA%Kqtfm\#QO_uo`+siq<%\`r;-6gp\4[_qY^'es8;ckr;I,qrpKdbrVl]nr;HZ\r
-r3&ls7c'`(Ae%<84#p984lN9#k^VF2)RK^(HOK7q#:Tgs8W)uq>^2?s3UcLrr;l)s8D9`J,~>
-kPkh^q"=[[s8)ckrr2uirr3?&s8W#gs82irrpTjd#6"Q$s7QE[rr3?#rr;rqo(M>>p?MYW!5ea8
-$iBtus82irq>^6fs76'nqYg9co)8Oap\FdYqu6U'qXsg`oB>E0qYg*`rr;oms82ims8;or!rr8u
-rVI#lqs4.VqYU'bq>:3Zrr3&os8)3a'I[m?JV&f=Li>6O@STHe;d2"X<`f(r!;ZTn!r`/oJcF*s
-"oeQ!\,ZEms*t~>
-l2Lk_rVucnrr`5ks8Dor$3'\ts7lWop]'j`rt+_us8M`fr;Zfrr;Zfqr;??grrDT`!"/Mcrr)Qj
-o`+sdqt'Rfq>C9mpAb$gkl21hp](9hn,E@erpT[_s7c?arr`8uqtpBg(%hM)nc&OSrq69ip&=ab
-rqHHes8MNertGA2rVuirq=<tDo(;VJo'PQ>!"Su.*<cQh!%[=.p%.qMo'u50n*]W4n*oo>p%\.F
-r;?5=s3CWJrr;l)s8D9`J,~>
-l2Lk_rVucnrr`5ks8Dor$3'\ts7lWop]'j`rt+_us8M`fr;ZfppAP!is8Vrorrhur#6bP8#Rh4P
-"97fhqXaXQr;?9Yrs&K&s7ZKirV?Horr)Hd#6"AkrVccmrVm0"s8DoiqZ$Qoo`$$-s8VZis6]j[
-s8Vfmq>UEfs7H?gnGiOdrVZWlrVZWgrsf;j9fbj?7m0]Y6hp]YqY^?noCr7fqXOUaon!.grrrE%
-qmZV(li2J~>
-l2Lk_rVucnrr`5ks8Dor$3'\ts7lWop]'j`rsJ;os8M`fr;Zfqq#(.-p\Xg`qt:C5)Bfn1',_Gk
-s763iqZ$Ejs7-'frr_upqu,aY"8VKWo`"mjqYpL&o^VJEp@S"NpA"O`q>V9/s8V`ks6p!_s8Vlo
-qu?]dp?VGCk4\NFo)S^_rsC8_Jp!!4KQM]!?gRdpp&G'bJcF$q"oeQ!\,ZEms*t~>
-kl1h]s7H?dpAP"+nGiO_s7lWorr;rms8;okqu?Wps82cp3;NURs7u]oqYpHnp[e@Z!$!+*(D%K$
-(]a-qnGN:Xr:KRUo^_YFo^_YFo^_YFo^_YIpA4^_r;-3d!<2He$i^2"o(2bUoCDVTrqZTorr2`m
-q#C$sq=s[Uq=j[Oq=s^Vq=ss]!:Kd_!;63d0_d">,pt)i.46AG!$MCH!!*HA(BEmrr;Q*\jnJK)
-p\":Rp\4IXq>9aOs8Vurs8ITLd/O:Ks80;*rTjK6~>
-kl1h]s7H?dpAP"+nGiO_s7lWorr;rms8;okqu?Wps82cp+o209rq6<kqu?WpoC;h[!\ll-5rq1i
-1)']^o`+sas7c6Vrrr?"s8Dusp\t6loDB-ts8VZhs8W&ts8Dutrpp'`rsSf'r;HQkrVccrrVc`q
-$MON"rr)lsqu$<]r;Qcmq>UOX4$#G%3>OY=-5'9A8PDZF6pCnUrVulds6K^bl1b2_p@/+^qYU9j
-JcF*s"oeQ!\,ZEms*t~>
-kl1h]s7H?dpAP"+nGiO_s7lWorr;rms8;okqu?Wps82cp,PhB<rUg-irVucnnFHVZ(JnXZBjb=K
-9.']=rVuoos8N&urpfpGrr`5nk4\WN!qt^MrVHQk!<)Ee!;lTl!W)?arrDiirtA%&:J48M8P;uK
-21KRsMM?P"@j:mO"o&&ks8Vifrs/Ajs8VlhqtksEd/O:Ks80;*rTjK6~>
-kPlUqs8DWjlKnQGs8W&mq=t!irr;rms8;okqu?Wps82cp$2OW"s7ZKlqYpKns!.11!$`6`#nA'm
-'EHeXq"aLVo_%kKq"ag_qt^-bqt^-bqt^3f"o.ueqtoaEq\eSrs7bpNp%J+Pna6)NroWkDo(;VL
-q"agdqu7K(b4YDkkOnK:me-5;nFcAAmd93!lQdtU-3*?5nF?MM)Y3=Z!"L%M"9K>_*WZ!7rqcZp
-p@nL_n,3"WrVQWmJcEgk"oeQ!\,ZEms*t~>
-kPlUqs8DWjlKnQGs8W&mq=t!irr;rms8;okqu?Wps82cp$2OW"s7ZKmr;Z`prXAN#"[>((4$QD%
-1_]6TrrDr]rrrB$q>^Kbl2M:_s8MZfs8Mlkrr;lfrr;H\s8N!.rVufJs8Vcbs8W#ps8Vflr;Qos
-q>WE<q)S3L(D/Z)3CQ2'83@1Xs8)Bes7cBis7$'erVQNmrrrE"rr2clJcF*s"oeQ!\,ZEms*t~>
-kPlUqs8DWjlKnQGs8W&mq=t!irr;rms8;okqu?Wps82cp$Mj`#rUg-iqu?QlrVm0QDK9fOMj&I"
-48o0_q#0s`qu#j]"9&9"p$)JYo)JFPrr<#srr3)hpAadXs8MusrrD$WrrE&grrH&$qG[Gmp\tdX
->^U74F)l)!)uos8o`+shrr2ukrVulrs8;oq#5e5mqY'a`JcF-t"oeQ!\,ZEms*t~>
-l2Le_rr2rtrVZ[)oD/FcrqHBkq#CBgs7?3h.e3H:rq?0cs8W&mrr2lqs8;ons82?e)&<tl!"',N
-!:B@Eq"=+KnEKK7oCWUena6,HoD&.Vq=sd\q=ssbo(rOes6g*d!%A'>s82K[o(;SIp\=LYqY^9i
-rVH3XiUZI.kOS3/l1,`K1+!e_)CR'Yq!0'imdK`=pA"FVoBcMu'-.Z'(CDntr;Z*_s7?9jlMpn^
-q#:Nrq"OggrIP!prrrE%qmZV(li2J~>
-l2Le_rr2rtrVZ[)oD/FcrqHBkq#CBgs7?3h$M"&orq?0cs8W&mr;RN,rq?<ir<GAM8jG6u3BIBD
-s7cQnqtpC+rr;utrr;utrr;utrr2cfs8VrVrsS)r"U>YK$k`dK#3ts_s8Moos8N)hrr3N's8Vrk
-s!gB&2_@-I5XIL(s8W)qr=oGs2b65)4t9,+s8M'WqX=F`lMgh\rVuiq#Q+>go)AX]rdk*rrrrE%
-qmZV(li2J~>
-l2Le_rr2rtrVZ[)oD/FcrqHBkq#CBgs7?3h'(Po"rq?0cs8W&krVulsrV-9brr39ZDh4+:MiE.-
-0`:qQp](3gn,ECbir9Jdj968>-6+'R*Yek>rU0[crqQL"2c*:?6rm&d?;p7k'cC"QCM@Zn6X('#
-rS[JFm/R+Ps8Dcmr;lforqcurp@%GGq"":[JcF-t"oeQ!\,ZEms*t~>
-kl2"gs763ilMpeOs7Q9h$N'l'r:Bscs8W)err4>Drr<#ks6fpdr:0UYqu?Qns7l!^)A)rP$j@7b
-!$NjE3'/S]l0@g+&FfAepuD/=o(2YNq"ORXq"aI[&+oo%!Ytq@!!!'(('=O9o`"mjrY#/)q"=:M
-naZ,:nF?&d.P2i",Q9:r+nGX##5%?Vp$256q=Og`qu6EkpAbEqo`+mis7ZHl!<)Nh!W2hHs3L]K
-rr;l)s8D9`J,~>
-kl2"gs763ilMpeOs7Q9h$N'l'r:Bscs8W)err33$rr<#ks6fmd!;6?g(]X4-rqlTu6:3b':dIN@
-$9("(5!q.+qX"4bmf31]rosFolhgVe5<_:l3Bo\l$2sbdrp]pk-osLH68SU)$Sh\\rr<#rs8Vlo
-p@S@brr2j4rr3!(&-N:D"rIRLr9sUUrr2Kgs8;Zirqc]nrVHTmrVlil!<.QLd/O:Ks80;*rTjK6~>
-kl2"gs763ilMpeOs7Q9h$N'l'r:Bscs8W)err33$rr<#ks6fmd#57oiq>0[\rr3K]Fb"muOaVOr
-.o/l.8kqV9rrD]arrDc`mh4FEoFG5ACh@?sBjD8An)4'@rUTsl2+L8':I+nP!'^>\"oSE#q>^3^
-rrN)qrq[W5rs',X'd"D8//&Kjlh9W;q!\1Yq=OIVq"XUYrq?EgrVH]gp&"]=s3^iMrr;l)s8D9`
-J,~>
-kPnc_s8Vo\4:*)*bo7P9s2b`r_"RfH*?3'!]d4B0rkfim_uIIk*Q[UF`>B6.s8W&nnGjCA!<<-)
-&/P?6p%e+Ppa@UI+=JWf-n6Vp-n6Vp-n6Vp.jlf&2"C/3pC?urrr;utrr;utrr;utrr)j)q#Cj6
-!"'&5%LiF6rVuos/,];>pFe-M.k3"s.Om"Bme-5@o^MJFo^hYHp[n@Rq#($[qYU-dqYU-drUp0s
-p@J%GmG7F)s8Moq!;6'c#QFc&s8)]oo7?qgrrrE%qmZV(li2J~>
-kPmX?s8Vo\4:*)*bo7P9s2b`r_"RfH*?3'!]d4B0rkfim_uIIk*QdgNaVki2rr)`orrtb\4[;J'
-6Tt\QruC\1s"cZ'1d!l^4?GYe4?GYe4?GYe4>SfW55Y9@s8W'-p]Nl_4'#BD<C$c_rr2`nr]L*A
-s#LAar;Zfos8;`n!r`/nrr2ump](9ms8Doo)#+".qYL6hs7$'as7cKhr;Q`ro_e^drr;lp#Q=]$
-s7uTmnq$hfrrrE%qmZV(li2J~>
-kPmX?s8Vo\4:*)*bo7P9s2b`r_"RfH*?3'!]d4B0rkfim_uIIk+3jEWar(]&q"ajfs!;N4H$t6g
-Eb-Kmrte+!>YS3t<E)st<E)st<E)st<DYnC7S!.O%fQ2!q"jd^q"jd^q"jd^rqHus&8@&EOH=RB
-BG1%6q#1'h#t7?U92&)U9LhPCrs&>snbE"YnbDq`qtg*]o`"Fbo^M\VrVQU)r;6$Vo(;bTqsa@T
-pA4X^qZd#rrV-0en:CVdrrrE%qmZV(li2J~>
-l2NC4s8)9cs&Qi5s8G<VXu?;ASuFdNq_YOO49$h,[5.bEVmS;W6)1;3rq&1WVE4_V/bnf8&f(uh
-!"T_e!:om^rquZgp%7qSrr;utrr;utrr;utrr;okp[R`01+FaL00q0=-7C2h-7C2e1Fja?mca00
-j7**P!WrK4!!!K/mk5b0-7C8k.P*1Gq"t!grr;upq"a^\q"a^\q"a^\q"agdjSoD_q==(KqtpEn
-rq$0irr2fqrdk*srs&H$s8B;#qWn03~>
-l2NC4s8)9cs&Qi5s8G<VXu?;ASuFdNq_YOO49$h,[5.bEVmS;W6)1;4rqAO_V`4PR#n&ge8PCm*
-4@954rrr;r5!1YXq)SI9*B?/@3]T5Z7m&d2s7u]ppAYml8Ou9G6TRdDs$AL=48q;*s8W',rVlcp
-rVlcprVlcprVc`qrVZQprVlfls8N#ps8Muus8ITL`;^&?rr;r'qYKOXJ,~>
-l2NpCs8)9cs&Qi5s8G<VXu?;ASuFdNq_YOO49$h,[5.bEVmS;W6)1A;s8G6lU+l62q#::6=)iSF
-GCFRM,PV3KrV66aqY9p^qY9p^qY9pcrVlsi76Nd064?:X;H$Il;H$Ii?WL#"rtI&$It)ctGB.gK
-s%u-Y9MA&M7n#eurrr;pq"F@Orp^-_pA"O`qu6ZprqHfrrVuorqtTs`qu-JEs2Y-DrVliqZhjOa
-s*t~>
-kl40Ms7c3b'(,_lrs%rW*W?cJp([,u,38b6n.kul(&ng5hA?1os6L]irW_Qb#ljr$o(2o'*<Hf_
-+rha+n+QYVr;69_o_/4Srt52&o()P;p\=LXp\=LXp\=FNq<]Qmj6[j>.N]]f,6.iH"onW9$3gJK
-lg*s+nF?&>o_%nNq>:'erVZQaq#L<^r;Z`hrsSf'q!mhFq#(0lrVZZj"T/,ss8DoorVQWjrV-Eh
-qgn[nqum#qs80@ks*t~>
-kl3jDs7c3b'(,_lrs%rW*W?cJp([,u,38b6n.kul(&ng5hA?1os6LZhrX%fg#lXc#q>Ugi9fGI(
-90G<=q"jses8;rtn,<Ffqu?-PrtPD*s"Ql)2E"5h#=:sU6oA+I6jGF?rri?"rVZW^quHWcqZ$Eo
-r;6Ekqu?Nkqu?QorVlfsrVZTlrUp*brIOmoqum#qs80@ks*t~>
-kl3dBs7c3b'(,_lrs%rW*W?cJp([,u,38b6n.kul(&ng5hA?1os6_*'rsn>m!:fOGnc'2"A6Etl
-H"L2!rr<#r!<2rs!;#gK!;$0h!;PjZ&Q3(F<D[.",BJHjHu>(-FX'?Irri?!qYL-Krrr>qq"OIS
-rUU0bp\=U_r;R3%p\4IZr;ZcoqY0a\rVZWnqYpQpJc*so"TJ8tqmktkJ,~>
-kl3F6nc/Ld###Msrs\r)!<3&nmg/sm!<;KirrE)d#5/3"rsJ<+oD\pmrW)ll$MaZ$r878L!X.N^
-rVlotr:'U`q%*8pn+lVAme6>Ip%J(Pp%A1R%/9>ep[RqNn+ZAE!"o21(]aU:#QXMSo_JF_qtp3a
-pA"CTo^q_Fp&+F]!WN#gqZQ`iq"aabqu$BlqZZfjo_&"WrVllqrqQTmrr2rrrr)cnqu?Hmqu20H
-dJjFLqt@Q"qY0@VJ,~>
-kl3sEnc/Ld###Msrs\r)!<3&nmg/sm!<;KirrE)d#5/3"rsJ<+oD\ghrW3&r$i'`$s6L+"%hB$V
-$3peKlMpk^rVcWorU9b<rqQNlo(W+^rVlcprVlcprVlcprVlips8Vrqs7u]nrt/'u7R^3I6UqI_
-li$_Ys8@NKJcGNF#6"Gm\GuF"m/MS~>
-kl3gAnc/Ld###Msrs\r)!<3&nmg/sm!<;KirrE)d#5/3"rsJ<+oD]'qrs&N""mk^Jo\p,b(`O2)
-'+PHdm,e6MrVuoor9""i.t`V5LQmaLH6`Ias8W)t^]4?2!<)fps8.BI_#FW;qt@Q"qY0@VJ,~>
-l2O!Ds82`opBT3n!<3&\r"B#Es616iq[r8m%/^b4mMl!9i9V*V!;lQtq#:iend>Eis80jCR@9nB
-Xe)/qV8\u&[0*eBZ*El(#G86-Vmre2_"b5hZ*U^AZ*U^<Y.UdEVR3_0Yakb'!!E?'!<E0#Y,oON
-]=PS`\$iWGXfSP%Vl-W&o!S%p!jJc*rMojuqlTk!!O/p0[0a1DY-"h1ZaI3Ir367.r3?7*!O8t^
-[*c5`Z+\>RrTaE5~>
-l2O<Ms82`opBT3n!<3&\r"B#Es616iq[r8m%/^b4mMl!9i9V*V!;lKmoDT9cp'guos8C9ZXgZ'U
-`3Z\fY0*9@Za-j?Yd(L?o!J^u[CNBPYdDCE\0/>l\[]2[\[/`\ZG!EO]WelB"@GOK83oa:2$UOr
-aL8MU[f<f>\@&cS\,Nl;\+-g*[IUd'[fX"I\,3T9\$i`QrNuR3s0hs8rj;U2!4;O/J[Ee2"L5Y`
-T`+0UJ,~>
-l2P,ds82`opBT3n!<3&\r"B#Es616iq[r8m%/^b4mMl!9i9V*V!;lU!q>V/tpBgWZoC'Q,X1?-U
-^9"?LWQ(C8\@o\ra2uB:\\#Da\\#Da\\#Da\\#DN\\55`[(+6R]_T/V[^a8_^p(Jd[_K!`EI3Cl
-I"HlW*Pf;4\,NcD\%0,`]=knm^VIXu[e$g)Yk,%"[(F-Q^B;0a]tOEWrOr6G!P,Z<Z3dnJ]=khe
-['6dCrO;j9qmcX9!P#Rh[*c5`Z+\>RrTaE5~>
-l2NF7s8Mfnn5ZX7*</=(Y!DeCViasYqEgaN56"35Yt+ahU9$iO<3)Wcs79$cS1aU9rs[TYp%7pW
-o_\O`o)AXQrs/Q$q>C$cq=a[\,5Cj%o^_YFo^_bDn+H)@pZhDDo*HKT*#^+/*$t[]mc4!6rqQKg
-s8LdQ!VuEeo`"O_p\ssfq#UBlr;Qosq=sd`rVufp!<;inJcF*s#5S2k[f?:)m/MS~>
-l2NF7s8Mfnn5ZX7*</=(Y!DeCViasYqEgaN56"35Yt+ahU9$iO<3)QXr:*UdTeu`Irs%3Wrr<#*
-r;Q]rpAFpnrr<#trposmrVuors8;oqqYL-jrqQL7r;6Nis8Vlos8!Kt6UO%/01\Y@s8)9arVZTl
-r;?Nhs7-'grU]perpKgcrqZQordk*ars&;spU:,"rp9Z8~>
-l2NdAs8Mfnn5ZX7*</=(Y!DeCViasYqEgaN56"35Yt+ahU9$iO<3)TZrUa'pUbqi@p@Zl2rVuo+
-rr2otp\Y!jrTO7]rVHNorqZHss7c9fp&G'err3BR@VB:YBi&Y\)?9a9p\t<nqtpBhnb_nP!;l0`
-s8)fpqYpTnqLS[^rs&;spU:,"rp9Z8~>
-kPm1,s8)cD*u:1Bb8))/s2bm*[djC8,o+l-]0$Y8s03sm^]2I[--#ub_@$dkqu-Nos82iq$G$36
-r;Zcjlhp\[li.1cq#C!ds8)Tl!WMokp^6Teq!@eEp\=LJs7cR%!U':Mq"aa_qu$Hmg&D-Qq>'p_
-s7llrr;?Qnr;QTn"T8)kqu20H])MiAs8;3_J,~>
-kPm^;s8)cD*u:1Bb8))/s2bm*[djC8,o+l-]0$Y8s03sm^]2I[+hdgO_[mO,s8W)us7c?hs0qq#
-qZ$Tjn+m"Sr!<9#qY:*_s8VrcrrrB$s7cQirr3JurW!'*!s\o7$NpUos8DTirr2rt*WH*<s8N&t
-s8N&ts8N#qr;6Ehr;6Ehr;6Ehr;6H]s8W'"rVlfms8W&rrrE%Ls2+d;\GuKms*t~>
-kPn$Ds8)cD*u:1Bb8))/s2bm*[djC8,o+l-]0$Y8s03sm^]2I[+Lq1C`=j'7s8;]hqY'jes181(
-rVuoonGiIaqY9^TpAOO]#5\,po`+sfq#C3h(&.\*nbr+Xs8Vur#8nNq'G)-+)%5[&rtkY2qY'XT
-o_/.YrVQQjrVQQjrVQQjrVQ0^!;l3a"8hrkrVQTsrVHBhrVuTiJcF!p!kA:.li2J~>
-l2Ltarr<#ns8)`p"nqQfqZ$*ars8B!s7$'`pAaa^rr_WfrUKgcs8)upl0\KBq#13no=Ou$#6"Js
-q>'pcl2Lb[mJdFfpAasgs8DcmrsJPnq>0j]nG*%`q;;2\rr;utrr;utrr;utroa:`rqlTjJcEF`
-#5e5qppC"sli2J~>
-l2Ltarr<#ns8)`p"nqQfqZ$*ars8B!s7$'`pAaa^ru:>)rUKperUp3hs8VZ^s75aYo_SFKYl4P"
-rr)$[!<2ut!;Z?gq#1Woo)A=]rqZ6bnGN:c$2s`#rr;fkn,E:`rq6:"rr;utrr;utrr;utroO1Z
-rW)oqrWN2trVlforr`8ur;Q]qp&9OBdJjFJq>U/rrVPp\J,~>
-l2Ltarr<#ns8)`p"nqQfqZ$*ars8B!s7$'`pAaa^ru:>)rUKpbo]u;Ks8Voks7Q$aqZ$TcXRu5]
-qu-Hm"oJ/ko_/.PqZ-Qnr;cTcrV6C"p[eFYs8VrfqWR_MrrVlbmJ?k_rVl]lqZ$L%s8Durs8Dur
-s8DurnGa='qY9p`[email protected]"Xa`rrr;pq"t$gr;6Njr:g<hrIP!srs&ArrqNl!
-qs494~>
-l2M1fs6fpequ?Ths8N&srr3f1s8N&pr;Zfrs8Dutqu6Kjs8Dups8Vflrrr>toD/:\qYpQ,rVm,r
-r;6<equ$HmrtbV3rqucnrqucnrqucnrr2lrs7lWorTaC_rVZ[#q!n1[s7c-an,NFeo)Aair:Bdc
-rosFbrqucpJcE=]"8o_0rp0T7~>
-l2M1fs6fpequ?Ths8N&srr4SGs8N&pr;Zfrs8Dutqu6Kjs8Dups8Vcjq>^Bmqu?]orr)Whp%nQh
-rV?Hlp?2Ger;Z]hrr;forr;utrr;usrVHElq>L4)qt0g^r9jLYq"k!hq"k$gn,2qXrr)iqf`1mK
-rVulrrVc`prquirr;Zfrs8;uts7?5@s3CWHr42k,li2J~>
-l2M1fs6fpequ?Ths8N&srr4#7s8N&pr;Zfrs8Dutqu6Kjs8Dups8VW]nG)k[rqucrrVca"X7PiU
-qt0gd"o\>pq"X^Vq[`K!qt'd`q"t$erVQQjrVcQl!;ufq')qY(p\*\?rVlfrs8)cqo]>f>r;@!"
-rVQQjrVQQjrVb^T!r2K_rqQNirqHoqo^qbIqu-Egq"jmdr;Qrtq"Xa`rVZQmqY^*hqYc!Fci4+F
-\c;Zps*t~>
-q>Uj"r;ZEhq#1-jqu?$^rsAT$s8Drqs7lBbrr3&os8Drs&,c2%q>^Enp\Y!Xs8DWjrr)j'rVQWl
-s8VlgrqcHj!k/..rr;lpr;R3)s8N&ts8N&ts8N&tgA_0PrVllsh#HsEkPkP]JcE=]"oeQ!\,ZEm
-s*t~>
-q>Uj"r;ZEhq#1-jqu?$^rsAT$s8Drqs7lBbrr3&os8Drs&,c2%q>^Enp\Y!Xs8DWjrr)j+rVQTg
-s8Vois82irrr2oq"9,V*qVqM_rr;utrr;utrr;utli-_[q#1<orlkE@rWN9#s8W)ns8W)urrE%L
-s24j?rr;l)s8D9`J,~>
-q>Uj"r;ZEhq#1-jqu?$^rsAT$s8Drqs7lBbrr3&os8Drs&,c2%q>^Enp\Y!Xs8DWjrr)j'rV??_
-rVucks8Dcn"L7jurVcTmlhq4krVuirrVuirrVuibrr`5sqYU*g"8hrlrVZ[$rVuirrVuirrSRVV
-rVQKfrVZNnrVufoqu?Tm!<;]iJcF*s"oeQ!\,ZEms*t~>
-p\tj!s7H-es6p!Xs7#a^r:^-iqY^?or:g-h#4hcnnbiFYrr3,mq#C3frVm;ss8V<_q#9jas7cQe
-q>UN&s8.BIJcDMF"oeQ!\,ZEms*t~>
-p\tj!s7H-es6p!Xs7#a^r:^-iqY^?or:g-h#4hcnnbiFYrr3,mq#C3frVm;ss8V<_q#9jas7cQe
-q>UN&s8.BIJcDMF"oeQ!\,ZEms*t~>
-p\tj!s7H-es6p!Xs7#a^r:^-iqY^?or:g-h#4hcnnbiFYrr3,mq#C3frVm;ss8V<_q#9jas7cQe
-q>UN&s8.BIJcDMF"oeQ!\,ZEms*t~>
-q>Ud!oDeRbs8MEcp&+h'o)JO]s8DQhp%eC\s82<cq>^'arr3E&r:0gas8V`kp&G'Rrr3<"qu?Zq
-s7u]pqt^6nZiBoRs+13FrrrE%qmZV(li2J~>
-q>Ud!oDeRbs8MEcp&+h'o)JO]s8DQhp%eC\s82<cq>^'arr3E&r:0gas8V`kp&G'Rrr3<"qu?Zq
-s7u]pqt^6nZiBoRs+13FrrrE%qmZV(li2J~>
-q>Ud!oDeRbs8MEcp&+h'o)JO]s8DQhp%eC\s82<cq>^'arr3E&r:0gas8V`kp&G'Rrr3<"qu?Zq
-s7u]pqt^6nZiBoRs+13FrrrE%qmZV(li2J~>
-pAZ!*s80]#%KAEas8MBbqZ$Hmo`+seq#CBgrr32pn,N+]q"4Rcs6^O"mf3=_s763ip%/4Vn,N1Z
-qtTpc!jhq(JcC<$U]1Mss80;*rTjK6~>
-pAZ!*s80]#%KAEas8MBbqZ$Hmo`+seq#CBgrr32pn,N+]q"4Rcs6^O"mf3=_s763ip%/4Vn,N1Z
-qtTpc!jhq(JcC<$U]1Mss80;*rTjK6~>
-pAZ!*s80]#%KAEas8MBbqZ$Hmo`+seq#CBgrr32pn,N+]q"4Rcs6^O"mf3=_s763ip%/4Vn,N1Z
-qtTpc!jhq(JcC<$U]1Mss80;*rTjK6~>
-q#;'+s8Vrq-)ptB!<;lks8Dutnb2eTrW"8Jm^`ZEbl>X"*5V[TV]62ks/d[j_Yq7g'Y"+^*$`N(
-s1g$'`q]B0!jhq(JcC<$U]1Mss80;*rTjK6~>
-q#;'+s8Vrq-)ptB!<;lks8Dutnb2eTrW"8Jm^`ZEbl>X"*5V[TV]62ks/d[j_Yq7g'Y"+^*$`N(
-s1g$'`q]B0!jhq(JcC<$U]1Mss80;*rTjK6~>
-q#;'+s8Vrq-)ptB!<;lks8Dutnb2eTrW"8Jm^`ZEbl>X"*5V[TV]62ks/d[j_Yq7g'Y"+^*$`N(
-s1g$'`q]B0!jhq(JcC<$U]1Mss80;*rTjK6~>
-q>UftoDe4XrtEc[h\Q4k#lO8cr;ZZorrE&u,[email protected](0R&3@>s%fD^7JK$ASj!*LSubE]2P6^9
-nG?%ORnWVW!jhq(JcC<$U]1Mss80;*rTjK6~>
-q>UftoDe4XrtEc[h\Q4k#lO8cr;ZZorrE&u,[email protected](0R&3@>s%fD^7JK$ASj!*LSubE]2P6^9
-nG?%ORnWVW!jhq(JcC<$U]1Mss80;*rTjK6~>
-q>UftoDe4XrtEc[h\Q4k#lO8cr;ZZorrE&u,[email protected](0R&3@>s%fD^7JK$ASj!*LSubE]2P6^9
-nG?%ORnWVW!jhq(JcC<$U]1Mss80;*rTjK6~>
-p\tX"s8)E0*WRLunGN+]s"4!Fs8Morqu?0_*o?E;n.b-X$2=H,na?nd#5.clq#^NX"oeT&lP/jg
-#j_Ehq#:E%s8.BIJcDMF"oeQ!\,ZEms*t~>
-p\tX"s8)E0*WRLunGN+]s"4!Fs8Morqu?0_*o?E;n.b-X$2=H,na?nd#5.clq#^NX"oeT&lP/jg
-#j_Ehq#:E%s8.BIJcDMF"oeQ!\,ZEms*t~>
-p\tX"s8)E0*WRLunGN+]s"4!Fs8Morqu?0_*o?E;n.b-X$2=H,na?nd#5.clq#^NX"oeT&lP/jg
-#j_Ehq#:E%s8.BIJcDMF"oeQ!\,ZEms*t~>
-q>W\Yr:U*es8VinoDejgrr;cns7--_rtYnZWrE(nrrE'!s82lsq#UNp!WEGprs8W3m/I(W'));)
-s7cTooG.2trrTP,qgncus.fStrr;l)s8D9`J,~>
-q>W\Yr:U*es8VinoDejgrr;cns7--_rtYnZWrE(nrrE'!s82lsq#UNp!WEGprs8W3m/I(W'));)
-s7cTooG.2trrTP,qgncus.fStrr;l)s8D9`J,~>
-q>W\Yr:U*es8VinoDejgrr;cns7--_rtYnZWrE(nrrE'!s82lsq#UNp!WEGprs8W3m/I(W'));)
-s7cTooG.2trrTP,qgncus.fStrr;l)s8D9`J,~>
-q#:Kqs7--Zrr5I^s82irq#C<hs7lWor;X5>rZ1Cl&-!:)p(%-#''\imrYjqn$iCP+k6q:soC3In
-rsAK!#lX]$n]7o?\$;aOZF%*NY.&tfJ[4gO#Ip\<X1#I?[DB-QUrTd=]Dqiqs*t~>
-q#:Kqs7--Zrr5I^s82irq#C<hs7lWor;X5>rZ1Cl&-!:)p(%-#''\imrYjqn$iCP+k6q:soC3In
-rsAK!#lX]$n]7o?\$;aOZF%*NY.&tfJ[4gO#Ip\<X1#I?[DB-QUrTd=]Dqiqs*t~>
-q#:Kqs7--Zrr5I^s82irq#C<hs7lWor;X5>rZ1Cl&-!:)p(%-#''\imrYjqn$iCP+k6q:soC3In
-rsAK!#lX]$n]7o?\$;aOZF%*NY.&tfJ[4gO#Ip\<X1#I?[DB-QUrTd=]Dqiqs*t~>
-q#:Baq#::aloYFO!<<&ns7u]hs7lEili47%q+Qs]3r_OBW&XYL;5p`^nP#LQ7fPf@]Kl*]SZ=aM
-mmsI?:&jnds7l6bs8Tk0o_eahq18Qss7$'grVum!p&FQrrrqE^]CYpnm/MS~>
-q#:Baq#::aloYFO!<<&ns7u]hs7lEili47%q+Qs]3r_OBW&XYL;5p`^nP#LQ7fPf@]Kl*]SZ=aM
-mmsI?:&jnds7l6bs8Tk0o_eahq18Qss7$'grVum!p&FQrrrqE^]CYpnm/MS~>
-q#:Baq#::aloYFO!<<&ns7u]hs7lEili47%q+Qs]3r_OBW&XYL;5p`^nP#LQ7fPf@]Kl*]SZ=aM
-mmsI?:&jnds7l6bs8Tk0o_eahq18Qss7$'grVum!p&FQrrrqE^]CYpnm/MS~>
-q#:d#rqufr!6bE:s8W#rrr4Y<qZ$T[3WKf0s0+!fbOige1V3Vd[Ls,%s1CJp`;d5)"j?qd$R43p
-s1oTq`VB?-rs8P*q>^Kos8)_GqgnY7qZlQhs6TdWs6%5q#6+Z&qR?J$li2J~>
-q#:d#rqufr!6bE:s8W#rrr4Y<qZ$T[3WKf0s0+!fbOige1V3Vd[Ls,%s1CJp`;d5)"j?qd$R43p
-s1oTq`VB?-rs8P*q>^Kos8)_GqgnY7qZlQhs6TdWs6%5q#6+Z&qR?J$li2J~>
-q#:d#rqufr!6bE:s8W#rrr4Y<qZ$T[3WKf0s0+!fbOige1V3Vd[Ls,%s1CJp`;d5)"j?qd$R43p
-s1oTq`VB?-rs8P*q>^Kos8)_GqgnY7qZlQhs6TdWs6%5q#6+Z&qR?J$li2J~>
-q>Vc1s8VZirJ.0Hs6]jdrV$3_q#CBms6K^bo'QJWq=jphrTjI_rVlg=p\k*[s8W)up&G$krVuK`
-s7Q9ds8N&uqtg9krVcc*rr3&qs8ITLJcG3=!;lcq!Ufp$rs&5tqOd`dq<\-3~>
-q>Vc1s8VZirJ.0Hs6]jdrV$3_q#CBms6K^bo'QJWq=jphrTjI_rVlg=p\k*[s8W)up&G$krVuK`
-s7Q9ds8N&uqtg9krVcc*rr3&qs8ITLJcG3=!;lcq!Ufp$rs&5tqOd`dq<\-3~>
-q>Vc1s8VZirJ.0Hs6]jdrV$3_q#CBms6K^bo'QJWq=jphrTjI_rVlg=p\k*[s8W)up&G$krVuK`
-s7Q9ds8N&uqtg9krVcc*rr3&qs8ITLJcG3=!;lcq!Ufp$rs&5tqOd`dq<\-3~>
-q#;H6o)JLT#QOi-#Pn5ks82irq!\7Ys8Vins8N&uo`+Xart4\hs8;oso)Jacs7c9ap&F[[qu6lm
-s82irrqlWn#Hn%(p%&.\r.4iurpfsms7$'Ys8Vlf_#=Q<mf10"p%dtSJ,~>
-q#;H6o)JLT#QOi-#Pn5ks82irq!\7Ys8Vins8N&uo`+Xart4\hs8;oso)Jacs7c9ap&F[[qu6lm
-s82irrqlWn#Hn%(p%&.\r.4iurpfsms7$'Ys8Vlf_#=Q<mf10"p%dtSJ,~>
-q#;H6o)JLT#QOi-#Pn5ks82irq!\7Ys8Vins8N&uo`+Xart4\hs8;oso)Jacs7c9ap&F[[qu6lm
-s82irrqlWn#Hn%(p%&.\r.4iurpfsms7$'Ys8Vlf_#=Q<mf10"p%dtSJ,~>
-q#:Ees7cHk!:'L^s7?9j&,lOus8)coq>^Hks8N&nnc&Olq#C$es7QBk!;HKm$2=K"r:]g`p]('e
-rs&K&s82Qa[=S@/s6'C`r;ZW.rs&>is6m#gq<S'2~>
-q#:Ees7cHk!:'L^s7?9j&,lOus8)coq>^Hks8N&nnc&Olq#C$es7QBk!;HKm$2=K"r:]g`p]('e
-rs&K&s82Qa[=S@/s6'C`r;ZW.rs&>is6m#gq<S'2~>
-q#:Ees7cHk!:'L^s7?9j&,lOus8)coq>^Hks8N&nnc&Olq#C$es7QBk!;HKm$2=K"r:]g`p]('e
-rs&K&s82Qa[=S@/s6'C`r;ZW.rs&>is6m#gq<S'2~>
-q>V3(s7--bo`+FYs8VTgs8)cos8;coqYpL%r:p<bo)JaUs8VckrrW#ro`"jnp@/+Mo)AXirql]p
-#P@olrr2rkqtpBuZMjh'q>Ks\JcC<$nc&g^s7ZKm!;+#*"SD]os7bjZJ,~>
-q>V3(s7--bo`+FYs8VTgs8)cos8;coqYpL%r:p<bo)JaUs8VckrrW#ro`"jnp@/+Mo)AXirql]p
-#P@olrr2rkqtpBuZMjh'q>Ks\JcC<$nc&g^s7ZKm!;+#*"SD]os7bjZJ,~>
-q>V3(s7--bo`+FYs8VTgs8)cos8;coqYpL%r:p<bo)JaUs8VckrrW#ro`"jnp@/+Mo)AXirql]p
-#P@olrr2rkqtpBuZMjh'q>Ks\JcC<$nc&g^s7ZKm!;+#*"SD]os7bjZJ,~>
-q>Up&s8Vlis2-8h-b]Q[qYgF&qYg9jo`+sbs7ZEkj8T)Yr>P_2s8DusqZ$Nos8;oslMpbXs7lW\
-s8Vrqp&=q#s82Wjs8TS-s8MciqY^?2rr`8urr14C!<2lq"8r&nr9XFcrqu]ndJj:Iqu$Bls8;lr
-!WN#crW3&uj8T2[qu6Tp#6+8pru%7F_#FT9s8O10+TDBCqt^-gnc++~>
-q>Up&s8Vlis2-8h-b]Q[qYgF&qYg9jo`+sbs7ZEkj8T)Yr>P_2s8DusqZ$Nos8;oslMpbXs7lTW
-rr2cop\k*uqtU*hr3Q>$s82cprVl0`!<2rsrVlZn%K?D,s8N&ts8N&ts8N#rr;ciorrE&srSdbG
-rrW,qrVQTprr)ffrmh&Crr2p"rquZkrr3'!rVb+C!r`#orVm6'rr;Zimf7>-oDHN+#laVspAeq.
-p@S7^s8MZjJ,~>
-q>Up&s8Vlis2-8h-b]Q[qYgF&qYg9jo`+sbs7ZEkj8T)Yr>P_2s8DusqZ$Nos8;oslMpbXs7lWY
-s8VrqpAOprq=a[`rNuP's8)Qk!<)ln$iTu$qu-Ejqu-EjqtU-Lrr`/pqYT%I!<)Nd!<)0^r;HTo
-ir/9ErVaS4#lX8fl2YZ$m/+["%JBA[!*fNoo)8UcqYL0]s*t~>
-q>UNos5EtW0,_.lW#bp<nGi:`rV-?lr;ZQhs8Vopq>C6lqt^6krp]sfpAa^\s8VfmrVuKho_ndq
-rr<#smelkXrqud%_>jQ4o^qPFr;?Tprlb<DqtU!WdJs4F!W;rqrrrDro(;\UrVlrss8Dor!;?9h
-"oe>jp&"aarrrAss8Vurp\k?drr)imr;HWert4\knc/V4s8V?Yrr<#onb2eTrVQZkp\P!iqu-Hr
-s8)cqqs==sq<.JOr;ZT_s"hs<eNEm$rqZQbpA"X5rsV6Hf%!:js75XAq>TsVs*t~>
-q>UNos5EtW3#T*uW#bp<nGi:`rV-?lr;ZQhs8Vopq>C6lqt^6krp]sfpAa^\s8VfmrVuKho`+mc
-qt9pfs7?0g"oSAuqmHA#rs&H%s8DimrU0^cr;ccpr<3,urr2lqrYPV6rVcZmr;HQlr;HQlr;HQj
-qY9g[qu6Tp#PnArp@n@Qr8[bUrq$-lrq?![qu7<+oBlACr:p3dqtoj[q>:'erVlEg"TJ8ts8;Tj
-!;-9j!WE&srr2irr;HR0r;6EirUU!cq"=^Yrq-6ho(i=brr)iprrE#js8Dp"s7uZnqX=D"qt0IZ
-qYpH`r;ZB`!+JB,!;H$`qZ$3^q#Ab@&H2>'>t7Wim.gV\rVuoerVlKiJ,~>
-q>UNos5EtW3Z5="W#bp<nGi:`rV-?lr;ZQhs8Vopq>C6lqt^6krp]sfpAa^\s8VfmrVuKho`+pi
-s7lTlr9sOXrqcioqtp<#rquors8;co')VCpp@e:Tq"FLVq"FLVq"FLXrVHNj!<(sX"8)'NlMUY^
-r8R_WrV6!X!;l]os8Dor"T/5qs8;iq!;HKm#lFJnq"jshs8Droqt^Kko_SFXqY0ahlhL5Lo_AFU
-rs8W(qY'[ap]($brrE&rrrE&os8DrprrE&`rtYG2q#CBis8(jB!*D6S!;,sarr;cjao;nMo\fd,
-+#!]Yp&4LEnbW.Ss*t~>
-q>V$$oDeCUr>)[4aTVY>rr2`lrr5+Sp[/"-!rp(X*Q.ok+Y:A)s3(lm])Tl"$GHJN)AU^#_B0rJ
-r;X>?)U@sOq>'scqu?]&s8Vf\o_&"Wqu6ZqbPqeDq"=Oar;5"Ds8N#qrVm'!o^MDCkl1SgpAajd
-rVulss82]n"TJ2eo_eRc(]474qu?]qs8MurrVlcprVHQor;Z6`s8Drqrri2ts8W)trt+o'o`"kC
-R5"[8q"t*kqt0dbrW2omq#19krqQTlr8[eWqtpBt.C\('hVKWsrt"]Og>N#2aYj+gnauhWs7Z*b
-J,~>
-q>V$$oDeCUr>)[4aTVY>rr2`lrr4tOp[/"-!rp(X*Q.ok+Y:A)s3(lm])Tl"$GHJN)AU^#_B0f;
-n,0O()UnQ_s8MunpA"N\pAOmcrVm#uqYL-hmf3(]rVccqnbiXgqY9m`rr)j#rq,XJqYT:NrVcit
-rqHEprqH*^r;RGtr;$-Oqt0m^q=jgcp&"O]r;HWfrtbJ2s8)`prVuiqrVlcprVlWms8Dueqtg?m
-rY>5(qtg*`rVZ0as8Vcm9*"nis7u?^qu6]pqsj[nrr)clqXO@TlhC2Gq$$HPmJm4crq6<k"oqOc
-5s&TWrqZWcrqcHcde=(ChZ.4M8Q8=_qYgErp\XCWp&BO~>
-q>V$$oDeCUr>)[4aTVY>rr2`lrr5"Pp[/"-!rp(X*Q.ok+Y:A)s3(lm])Tl"$GHJN)AU^#_B0iB
-pA_Q2(sV[Lq"XUWoCi$Yq"j[SrVHNrqXjFTmeZq[!<(sX"7tmCo@s9Tqtg3dqtg3dqtg3dqtg3f
-rri;tqu?0brrr)qp](-iqu6ftqY9j_rVulqrVHinqu-6drVQQhq$6TiqtU'Sp\jpf&c;P,rqu`p
-s8N&soB60E!;-3`rrN,tpAb'hs82fl"S2?_mIp,C&bG5RoCVbIo^D&%!&c5Q&jHBqoDn7WoZH_7
-n^IP"#o5*T!;cQ`m-X<5s*t~>
-p\tNsq>^9"0)up+rr4kSs8Vfjs8;osoKrWQ9T-)NoKT4>7K<3NWiA>Z4.<9KqFmTh4mPS;RlCBI
-6F!.Dqu$6frr)is^&J$6p&+^^q>Us&q>0p`q>0p`q>0p`q>1*ds8)`ms8;rsqu7<.rVlcprVlcp
-rVlcprVlcnr;HWo$N0bjrq6<kr;?Qmir8rWs8Muqr;7c8r;[email protected]:Rq#:9jpuD2;
-rr2rtrqc?[p&=Xa!WMohrrDWerrDroq#C-kr;QQkrtG,+s6K\MfuV5Rs7H9is7Q0es8;Qi"oe/a
-o_8=FrtU]@S!:=ORO>\\s8VlX_Cq.>WKX3Nq4uH9%%f5Ian,2gd]F5^rnHrBJ,~>
-p\tNsq>^9"0)up+rr4eQs8Vfjs8;osoKrWQ9T-)NoKT4>7K<3NWiA>Z4.<9KqFmTh4mPS;R4eF7
-4KtG?s8W&rrt58/Z2=M!q#C<fqtg*_qYL-go)ARerr2rr!;ufm,5V38rr)iprr)iprr)iprr)ir
-s8Dilr;Zfjs7?3fr;Zfrir&iQ!r;]hrVH]pqu$?jquZ`mrql^!p&4@Zs82`o"7-!Wrr2frrVlfr
-!;lWg#Pe5or;??]p\=UflLk&Irqc]nr;Q`ps8N)rrVmT%o_n@Y!+nW+!<;ujoBH&Ms7c-Zrqlcj
-qYL9lr;R0(qtTmKmdB,tl/h=(mhkERh.g/4>]F%h!+\8o8jZQl!*<<><bZ"<B@:B(BF8?L<'EEF
-@0$9+h>R3Eq#0mcJ,~>
-p\tNsq>^9"0)up+rr4kSs8Vfjs8;osoKrWQ9T-)NoKT4>7K<3NWiA>Z4.<9KqFmTh4mPS;RPFjA
-5-CG:r;HBfqu-HuZi0dsnb`1ZrVm#tq"4:Yo)A^erqZQjs8Dip!WDoeq#^9\o`"jurUT@8o^r1`
-rq,jYj8JEG%K6+rpA"Obr;ZWns7Q?hrs/8tr;Zfqqtoj^!<)Zl!;63gqYpNp!<)lr'_V7pdEqqa
-/P6$.mI9c+gZ\G/pA4dg!;cQks83E(q=jXTnEfMsiT&SFh::*Je/6Z^`()^`6X2oG!&4p5)$:gF
-!'<>?3_`&c9+Fl#=>23=*_^&DrU\+tp@A66~>
-p\tBks8V]brr3;sl2U2<s8V`ks8MpNJT;(p#5Rfortaep$1.[!lg>#X!<;3^rrr&S&,uq#q]GY2
-$hO9'rqcKhrquK]pTX5g!<2Tf"T/#iq>'scs8;orn,ERjq=sjbrqufrq?6]fp%eU@s8Dp"s8N#q
-qu-?gq[W/lm.gAHrUf%Cp%A(Pr;HWsrqlWfrrDucrs&>so`+pjrr)j#o)&Ieq"O^d"ZX]]s4]Tm
-rs#*`Y`QMjV=UJaWk,k=rru-=p%mV!6G`[.-EH#!iP.#Fs7Yp]J,~>
-p\tBks8V]brr3;sl2U2<s8V`ks8MpOJT;(p#5Rfortaep$1.[!lg>#X!<;3^rrr&S&,uq#q&Aqu
-#OhNss8;fos8MfdpT45jrr<!%rVlcorr)iqc2[hCrVluqqZ$QQrs/Q%rVZWlrVZTl!rW#qpAYEl
-s7QElr;ZHOrVlrss8W&s"98B!r;6?nqY^'arVQZjqY1!c"nqTWnEg/Nrtk8's8Vferr2cjnGuKK
-<_*5gr:0=Ms8W&l!V#RQpC[6!rr)clq=jUSnn)'B8TRsBo4.f.*,P<Dn*f*"p%8;Z9L1X7!:]IH
-lgXB1n*of8n*n]m&b52f6W@8a;ulXio`+mXq=jj\s*t~>
-p\tBks8V]brr3;sl2U2<s8V`ks8MpGJT;(p#5Rfortaep$1.[!lg>#X!<;3^rrr&S&,uq#qAf2&
-#k%KorVQEir<i5ior\)fq>'mar;HWtrVQHgiVsPfq"jd^q"jd^q"jd^q"jXLn,*+a$MsDeqYpNp
-p?V,BdJj7BpAX[mq#16mr;ZEfq!n4Tl2(D[rSRW"rVuWlrVuirrVuieq>^Efp&FpalGrrH%5K:2
-h!!e\hp^$3rp9aKjnJ0Amg\[NlKIBjiDiHS2e#0Go1'B]85[gbdFcOhc,BZ)+VFbj!<1FLcdU@i
-b0pjUjo?e_2DIl*s8V<MmHX9BJ,~>
-q#:?\r;QfmqYpLgrr<#bq#CB^q#@ZVp&t'rs6'mX!<<'+s8N*!rW)s'qu6]s!rr9#o*,*m!WW06
-s69X_qu?ZhpA=a_\ao1`rVuWjq"OFQp%\C\r:Bmhr;-3fpC$ZdoC;>>mdBK0nbVkV%K#nlo_%tT
-qu$HhrqksYpu)#CqZm&rqtTdSp\Xph&c;M#q>C9ks8N&uiq2s3q>1*grrN,qqY:*i"nM<_o`"jc
-rs&E$s5a4[r9jRi38<]ts7OMnq>UW=VQ-r"QMREbVlQkuVkg#WR>Qk"rrDo_rrDlkrrta&i8$d#
-oYLP5;7<p\2#mLNrr2p!q>1*`s*t~>
-q#:?\r;QfmqYpLSrr<#bq#CB^q#@ZVp&t'rs6'mX!<<'+s8N*!rW)s'qu6]s!rr9#o*,*m!<<'1
-r8n"Uq>^KkqYpL+_u96(r;ZKirVlisrr)clq=F@^q>:-gs8Mfn!;uHb!;uir$2sbtqtp?frr;Nf
-rrE&Vr:g'jqYU0grr3#uqu6BmqZ$Qos8W!0pAY*Urr;cnr;-6ap\4CVqu$?hr;Zd$oC`%Ss8N#o
-r=&H!rSRSPp\jp`q"a^aq>C./rVZQckQ#6V7lNS.!:]aMmHF65b_9S@E+N#F@hE0W?=%#J@Us(X
-AG5fdqrmqQoCsI$h=:"'q"!eAm.p,N6!.[uo\TE?p$);?p\=LXp\=LWde4"Aqt17n6>-AjoD\di
-rW2imo`'F~>
-q#:?\r;QfmqYpLSrr<#bq#CB^q#@ZVp&t'rs6'mX!<<'+s8N*!rW)s'qu6]s!rr9#o*,*m!<)j,
-rT=1Xr;Zfmqu6U%_YN]op\OFTp@nL\!;l-_!<)lrqu%0)q>:!bq>:!bq>:!bq>9gJkk>#U$N9et
-qu?]gqX!P?i;4#_qu-Ejqu-Ejqu-Ejp[J1J"7>UMq>L=)qt01?lhLA>s8Vrqq=jUWqt0pgpAt9f
-p\uoGs8V3\s8N&np%S.Rp%S.Uq"ja]q"j[OfDmf5'+5U3!9*.qcagp8Wae@b:.$f55n?=R9MS>Z
-;,^Ij<9NQ)m+Usr(Y\6:j7Dg0n_W?]!&$T(!9)>mmHNEniQCHrnb_eU/fdOaoD&@Zn*]l>s*t~>
-q#<MSs6fpY!<<)_6hgQZq>^Kbp](9`7*kK;rrD3O+9)fCgB7?Q$1I-rjUgtB)Y4F-jWX=@g[bdP
-o+LHg-3!o[m)6-6Yb7/nZ+%?VYe%-CX/W2(ZF.15\c9/=[f<`CZa-g>YHG%0XK/M3o=$B][Bd$@
-ZF.6S^8J3?Wj&S'[C*HN[^EQO[^EQO[^EQO[^EQO[^EQO[^EQO[^ERB[K!]5\,<cj[B-I9]Yh_+
-^7r0>SAW1]*R)$qb,r.HZ*h-T^;.S$]sY)MZF.-M\uWibbKRQ=`VI@V^Vmq?Xi8,tbl#ccbT=mM
-dF5dtI#0Pac27P@qp;o)D47&oX-U*?j5]+[lh][ekje97mc`]de]cL[ZbX#E[^ETS\%&oW\%&oW
-\%&oW][OR+\?*<b\\5_n0#+Hh.DEC"Z+mZR^p^YZ[C3KO[^`iX\@K,[\@K,[\@K,[\@K,[\@K,[
-\@K,[\@K,[\@K,[\@K,[\@K,[\@K,[[^r`S5-R0Lrs8Pcqu?<fp\"IWs*t~>
-q#=Fms6fpY!<<)_6hgQZq>^Kbp](9`7*kK;rrD3O+9)fCgB7?Q$1I-rjUgtB)Y4F-jWX=@g[bjR
-o+C6^+T;6;lc?NI]!%sX]=YP[Xh20W]tM%h\$i^7[/[Q6[f<i:\,s4P])K<+]!o,U[^EQO[^EQO
-[^EQO\%92^\$i]O[()j7[B[9LXgkjK\$icS\$icS\$icS\$icS\$icS\$icS\$icSqmZL3r3[]X
-]tM"bZ*1@9TY7\)^9aa<[\oqCZb`cT\$`TKZ*:F:[JmZ7[L'@9]WeuYVm<M+qPaatX/E^sXJi8$
-r2K[q*iZ6IZ_)VQFUMhW68qJ%:i$,+F`M\L>&ob"U7e<^qP/J3S!f_8StVpVVRNh0]XXrOYHG"1
-Xfee/Xfee/Xfee,[ALUPV5C;b['ZP-B1uV3WOAq2\Zi9MYd(F;Yd(F:YHP+4Y-5"3Y-5"3Y-5"3
-Y-5"3Y-5"3Y-5"3Y-5"3Y-5"3Y-5"3Y-5"3Y-5"4\u(MgA,u8ms7?9jp$r'4~>
-q#<MSs6fpY!<<)_6hgQZq>^Kbp](9`7*kK;rrD3O+9)fCgB7?Q$1I-rjUgtB)Y4F-jWX=@g[baM
-nIOp\,l[fWnB&,N\ZDRN]"5>UW3`\2Z*LaFrONBK]">Pc]">Pc]">QM]D]>>\0JGl[^NQO['m?M
-['m?M['m?MYGA&&]u%Us[^rEM_7-eDQa,PY!4;@'*jMcGWiN8-\@\lb];)s9RK0=ZS].kLX1>UC
-Z*LgLrkJKH%D0-Y['?1.Vm*7iUnOIXU'RBeTX]uXTqnCXTH9\uW2Pr#<Ghe.,ngG&)`(Lp6=X.o
-8iBXiKS4r3N;A6WLPh+ROcu&tS=?UXT<kYlZE^[@Z*U^AZ*U^AZ*U^AZ+[<S_QC5ZXgbU.!+n\p
-!2d61Y._*H]sP)PZF$pEZF$pEZF$pEZF$pEZF$pEZF$pEZF$pEZF$pEZF$pEZF$pEZF$pEZF$pE
-ZF$pE[)/Va!($\LnGi+Tn`BK8s*t~>
-q>Uirr;Zff%1iL>$E3aus"F3Js7lWj!W`H1!(a0)0DJ#0XBG,o8"m"Gs$``h2ZH:AY<;hDX0"\[
-r_Su\9)/Dc#kRHXp%5NWn+ck]#lFAenauVSrquBb!<2Ng!<2Tc!;uirs8;lr"o\K#r;?*2s8Vch
-rrW2cq>UC$2PM;ns8Duhm.gMSrrE&]rrf7%WMc]oWW/psV?NluV3I:[rVm3I]]e\]gpnO,s8:jU
-!<)Bd&GH._nb`+]s82Bes!FE^s8Drss89q;&GGo!s![pIrq5UPrqH'Tkkk&QJ,~>
-q>Uirr;Zff%1iL>$E3aus"sQOs7lWj!W`H1!(a0)0DJ#0XBG,o8"m"Gs$``h2ZH:AY<;hDX05"g
-s%o#V6h1*QrqH*brs$IBp\4CXp\Fghrpg!jrVQKiqtgBirqc`mr:'abrWrH!qsXFYrr2KfrZ2%<
-s8N&ts8N&ts8N&ts8N&ts8N&ts8N&ts8N&trVufps8Duqrqc]prr3Z-p%.kOqsj[apZ;Hc!;H'S
-s7l<ertYM0q=s^Zqu-KkqYBmZoCLu/nEB<-p[/7QmUU'E@:B.Cs'bq:(hIDl<`O[ooCMJPAlL]Y
-6"0imroNqJp\Fg`"7u'Xq"j^cp$r%N!qGmSrq6Kir:]g_rq^L-qYC!`qYC!`qYC!`q;qPCs8MN^
-q>L!_l2Y>pp[7qOqsj=Tq>1!bqtg3dqYC!`qYC!`qYC!`qYC!`qYC!`qYC!`qYC!`qYC!`qYC!`
-qYC!`qYC!`qYC!`r;Q]nl2gGLs8Vurs82fqr:L#>~>
-q>Uirr;Zff%1iL>$E3aus"F3Js7lWj!W`H1!(a0)0DJ#0XBG,o8"m"Gs$``h2ZH:AY<;hDX0+e\
-rD&]W9)/Dc!W)QirsQdFqY^6ipA4RZqYU3j%f6)!qtg3dqtg3dqtg3go`#X(s8DfhqY9p^qY9p^
-qY9p^qY9^Xr;Qfpn,<7gp\OU^`r?DEk5\`enaH#JqtL*i!<)Zl$iBYdjPS,+c,7Q@chPrndaLc`
-91MYO9c-Z):-UsW[GgK9!(/FQ-RD4^n+PZ+h;Ii&p]L-Xq"X^[!;6<^rUgEhp%\CUo(r4QrrVln
-q"t'rr;Z9eB`Rbt_>b#Em*5U]l2CYZp[mhDs8DTiJ,~>
-pAY?ks7u`qs6ose4n8RGs8;ojs8VZirrE)B((eIb_BBZ4s8'eS+M%Nl&KJsn_]Kc5p<=Nk]DMN;
-,TOl)qYpNoqtTmVU@nN_rtbG'p%\Fas8Monqu$?hqu$?hqu$?hrp]sZqZ-WprrE&srs'kMs82fb
-s8C(>!<)os"Y)ae^p5QVrs$$1XerV*WVNIoUopcbrt>>2.88OIc7Aq]q>^Kos8W&nr;Q]rrn.5d
-nabu6s8;omq=t!i)?9U6p\Opiq>^H9rt+htq>\50s7tpRoDS^\o()\Ns*t~>
-pAY?ks7u`qs6ose2=^_?s8;ojs8VZirrE)B((eIb_BBZ4s8'eS+M%Nl&KJsn_]Kc5p<=Nm_#OGI
-+rA&loD/C`rs,k0rqlHbo(W%]s8;Ee!;uck!;ZWj!;ZTi!;l<d!WDrqr@7^>pB9dYq#0XYoDS[e
-rr)iprr)iprr)iprr)iprr)iprr)iprr)iprr)lsrWW9"r;HQkrqdi9s8D]_mdBQ8n,)hN!!$G%
-?NBW]q"spdrVZNep@\(Mrq?Bb$M<o[n^r1uBO>[`pgOJ7BP$GkmHj0;m/?;OmelMsnbrLd!!#tg
-?3'oqs82Wbrr)]dq>C3hqu$EjrVZWlqtg<es7uZj#Q+5lr;6Egr:0Y"o`+p`r:'R_s7Q3[!;Z-^
-o_8=_pAOmbr;ccDq[WT(s76)irr<#ms7H0fq==Q9~>
-pAY?ks7u`qs6ose1\(M=s8;ojs8VZirrE)B((eIb_BBZ4s8'eS+M%Nl&KJsn_]Kc5p<=Nj])2H:
-,oao)qtg<mZMa_'q"F^^!;l`p&,l4spA"@VpA"@VpA"@VqtC'irU0O_qY^?qoBcP>rr3&qs75+J%
-f?5%rVQQjrVQQjrVQQjrqHNjrVZ[/qsj^e;EIYSjP]Lunal;>n+$&DrVI*$p@Ib>k2F6h8P;9C4
-\#6<#=M<[ccsqih"0>5jno#C!!#2=8c\#8rVuicqrdt\r;Q`qrp]pfrV?Hsr;ZWoo^'?ms6'cSk
-k>&Qs6oLMn+-L/~>
-q>UH_rr3#rr;?R,p\Y!_s8VurrVuKhs82Tfs7-'f)Z'C3p%n^_s7c6do_SU`s7cQnqZ$EkoDA@\
-rr2ulrr33#o(`%N]D;:&s8;*\!;l]o!<2uq!<2ut!rN#op&>'hr;Q]t/"IsbrrV`jqY:'orVuTj
-s7baW!;ufq!<2Wj#<Blgs7b3p9)S\kSY)UNrh^%$X0&G&Vkg#WOfmI=$hsGks8P'h0)th@p&=sk
-r:'acrr<#nrr;ofqZ[!!s7l?frVllsqu6Zoo(gZ0"TJH$`;fi9r;Qitqu69gJ,~>
-q>UH_rr3#rr;?R,p\Y!_s8VurrVuKhs82Tfs7-'f0DbVHp%n^_s7c6do_SU`s7cQnqZ$ElqZ$Qn
-rr)c`qY^?ipAY*g^\7Nto^VSLrVlg:rqlKcqu?WprVlfrrr;utrr;usq>'maqssX^rqcX"r;$6]
-qssXYqYMuErVlcmr:oj`@K?6%rq-3_rVcZmr;HQlr;-?_qYg$ar;HQlr;HQlr;HQlr;HQmq>Vf:
-s8)copAOm]na,K#jlu4)mdC-W:eX/M@K>ETjn&%YCM@HoAnCpOs(24B#\[Y!i:Z^8m.^&D"SMEZ
-p%A:Ws7cQgs7luur;Z`mjT&fkr;Q]tr;-?jrX8](rVlcprVlcprVlfprr2rrr;Q'_!<2rs$2jYs
-s8W)qq"am's8W!(ppC"us8MunqYU3]s*t~>
-q>UH_rr3#rr;?R,p\Y!_s8VurrVuKhs82Tfs7-'f)Z'C3p%n^_s7c6do_SU`s7cQnqZ$EjoDAC\
-rr2umrVm&qr;ZN.rVQTsqtTs`rVmE-q"a^\p\O[]q"jd^q"jmcrsJN#rVuipqtp6drU'UmqWlo:
-6icTMs7u]jpAY-lmJeU2oCVYHoCVYAoC2ADlh'T&g"G-8i8WeWf$FCK,97CC493.@`m`=+r^ISo
-92SDQ6UXI<:K1FrHJA&`qXOFUoCN(WqsaUkp@6u>>la*WptYlLrUTjcrV?HtrVH6ZnalOnqu6Tu
-qsUHSq>UBrrV6*_o)F4~>
-pAYumlZDFF(]_bZs8;ols8)]gs82ijp](!Urr3&ls7Q?j"oJ>orVucort>>*s7uZns7c']qu$?i
-r:p<lpAY*lrWDu,s7H0fs8M`l!;?0e''0)js8N&rqYBs]p\+I`s7H?gp&>$frr3-U=No1#0`M(P
-psT0[VLb87Xg5@>Vk93F/&Td%r;Ys!b=8,/M5K\?"8r2lroO1[rr;ltq<S%ZrrjDBs8;]imf*Ii
-qtTs^q>L3jr;HTqr;6*]"oSE"q"XjerrDuprrN&no(^Z/!W2forr^"8rVl0`J,~>
-pAYumlZDFF(]_bZs8;ols8)]gs82ijp](!Urr3&ls7Q?j"oJ>orVucors8Vus7uZns7c-as8W$%
-qXa[anbrIdrsAP0qrdbKn+lk[rr3B*qt^'br:g-eqYKRTr!E8uqVM2Gs8N&tqYq]9qW@YAqY'dZ
-p\+@Tp\+@Xl1t/>!!$A:<rgkJqrmnPqY1*Zo)ACco(_nJp]:6hrVQWk2Yle9q=s94>\[k[?X7#L
-BkqbiE)]:Z9MeGj!)@?*Dt<JhnF,i6md9H1n*ol<o_.VHqt0jZnGW:^s8;fns8N#t#4VZfp[eI[
-rVmN.q>'mcs8N&ts8N&ts8N&ts8M]krVl-_!ri)prr3'!r;FD1%fZA'ppL,"s8Dikq>'pdoDa=~>
-pAYumlZDFF(]_bZs8;ols8)]gs82ijp](!Urr3&ls7Q?j"oJ>orVucort>>*s7uZns7c$[qYg9j
-rVHQop\t0rrVQ8ps7-$e"9&#gqY^?trV6?iqtodWrVum)rSmkQrVZQgq=saap'(9ls7cQmpAY[%
-lML;4!!"o=2#tnprTX1Sp]9gRrqHQcq"=4Q47qq(lKINslKINhlKINfHV.429i"P^;c-7c!'VG7
-!#R"[email protected]\air2dhW=4kqqM,WoDeX_p&ORMnbCo>%K6(uq"jd^q"jd^q"jdb
-qZ$HmrVlisrC$PZq"41Mq"X^\qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^
-qY9p^qY9p^qY9p^qY9p^qY9pcrr_u!qYgEn"9&)kqXXZ:~>
-q>V3+s5j8W_#IB#]cR4Mrr<#nrVuosp&4mpmf3:\s8Voort"`!s5X.Equ?9[pAb0Ns8;iq)tj-s
-rr2corqH-ds8N&ds/c8#pA"1Qn+c\Rq>U6prr;uts8DWjs8E0!rpg$`q#:?nn,EVZf]i/&o?p>(
-rro6_Xe)tpn>-,\rMBOk"d-*aX/NQ&rsJZ's!k\Hs5_D)3pZeMq#C!YqtU!Xp]13bs8Dp$s8Dut
-rr;rjrtbV6rr;utrr;utrr;utrqlNeqtp<jrosFor:osXq>C6kq=sa\r;69ar5/I>r;6Nk[f#su
-r9aN7~>
-q>V3+s5j8W_#IB#]cR4Mrr<#nrVuosp&4mpmf3:\s8Voort"`!s5X.Equ?9[pAb0Ns8;iq(\dt!
-s8VrprUo^[rqZHVrhKJlp%nF_q>1*srV?$\qXOFRqYL!gqYpHn!qcNgq>VT8p%S4XqYL!ar;6Eh
-r;6<]naGiF>!b>39N2#Ylga!'qX+UWj^_8*?$0QGA--7MA,Tm:@3J<]=^tiYcL1;to'u8Aq#'jc
-quBu1;E@`S!:94Ho(MkSrV-Hgq"jsd#Q4Q!rqHHfp[/"Zrq??sq=sdNq==L_r;-TkoCVqErtYP2
-qtg-aqu-Qos8N&ts8N&ts8N&ti;Y_7s8N&ts8N&ts8N&ts8N&ts8N&ts8N&ts8N&ts8N&ts8N&t
-s8N&ts8N&ts8N&urr2f)s8N#t"o\AsrVlfgs*t~>
-q>V3+s5j8W_#IB#]cR4Mrr<#nrVuosp&4mpmf3:\s8Voort"`!s5X.Equ?9[pAb0Ns8;lr'`IV!
-mJ[(_s8Vris8DZaiUiT6s8Drs"oJ8loCVnXrr`9#s8DZk#lOPrq"j=OnbE"T#l+AnrVHQms82Zm
-&c;4jn)sa=*YT)54TO[2n_)Ojcj.t=7Pmk)=$lII&P5bq;Gg=h;GfS_92net^u=SUm3V,Wj4XJg
-2('/'6icB:naH&=n*TT6oChhDm-O-0oCVhSp&Fjcmf*:co(rggp]'jbq"j^Sn+6>Brs8Mnn+$#A
-pA4a_rs\l+rVuirrVuirrVuifs8W#tqu6OUs8Durs8Durs8Durs8Durs8Durs8Durs8Durs8Dur
-s8Durs8Durs8Durs8Durs8Durs8Durs8Durr;Qfp[J^%,rVQHgo)F4~>
-q#;Q2s8N)hl3R1K$N'l$s7u]os7lWop\4^Us8VBas8)cqnbN%]!VuBarrqWdq>0sfrr*,pr:9mf
-rq66i&(LXZq=O1<mI^2OrquZgq>UBorUp0lqt]gCrstKh/\C!,s8V`G[k3`/T;DC`!N33PrsJ\s
-p\+VB_T(HF/+`f:rWr8dr;?Hiq"X[Vp]^Kkrr;utqu.$%rqucqqZ$0erqQ*_q#Bm`ir98]q=sa\
-qu$Biq>^3iq8*(=q=j^XYkRhbp\=X`nGe"~>
-q#;Q2s8N)hl3R1K$N'l$s7u]os7lWop\4^Us8VBas8)cqnbN%]!VuBarrhQcq>1!ert5),oD\aa
-mJZt\qn`4+rVuorq#;!*qY9d[r;-<fr;6Ehr;6HjrV$6js83/rp%.bEnF,l;rqHTcq"OR[pC[)[
-BlF&W><#5J6WI4g@VBOjqI^%EDsZrVn)*O'mfDkCqWn.G$g-a>kjS9Cq=j^^rr2p-o_&8f4'5ql
-n+,T.qYU3fr;cimrrN#frVloqrSdbYnb_DErVm0%qtg0dqY'[^lMpn`s8Don!<)Kfn,Ejrs8N&t
-s8N&ts8N&ts3goFrrN)0q#:?noDa=~>
-q#;Q2s8N)hl3R1K$N'l$s7u]os7lWop\4^Us8VBas8)cqnbN%]!VuBartjo!q=s[[rVccms7cQn
-q!.YHna3RJnbi@c"o@iXmIU,NrrE&krr_u`kk+iFqX4IPlMg/Qk3_O&q<S[L@ql!94q\nY)EL=f
->[h#5qF(WV:!1`#c+V<kblQ;KpYcD6iSi\XpA=a`rV-iqnCuXs3>t+b!;$'WqX=Fbrq5XX!WDfa
-p&aOSkPkM]qZQWaoCi.OrsJYpn+#r>o_/(WrVHO&rVuirrVuirrVuirrU0\JrVuirrVuirrVuir
-rVuirrVuirrVuirrVuirrVuirrVuirrVuirrVuirrVuirrVuirrVuirrVuirrVQTp]DV[2rVQQl
-o)F4~>
-q>V6-s8DF;\oV`gdnTlBqu?]prr;lqs82fq-Mld&p\44Qp&Fmfs6BFRs7Q!`mJleFs7ZKmq=ssh
-jo>>>qt^-drVm9)s8O2@s82BKlL+EErsAW#m/HGPrp0RKqYom^"KA;i^6JJm%%s6\s8OCMaQVg!
-^*Lc!$iB\ghYdER1runm+TDE@rWr2rrr;uqqt^-^rrE#crr`2pqt^'b%K-,$s8VienaYo:p\4L^
-rrW)mr.G"[r:fgTn>u9Qo'u5=nb)_UoDa=~>
-q>V6-s8DF;\oV`gdnTlBqu?]prr;lqs82fq,5U@"p\44Qp&Fmfs6BFRs7Q!`mJleFs7ZKmq=ssg
-jT#8Br;R*%r;,^Pm/ZSPrUp0qnc/+Ys6]jQp\u!-rU9OSna5W(j5AhNiT'@cBP(Lt;u1,=C4kCB
-qZ'nm;GfMj!;QQanb;nR"7#XNmeunMo)[email protected]&Dp?q_Ss8Dlorr2p.n,N@Y!,,AB!:]d[
-qYg0err2lr!<)cl#Q+Apqt9g]r:9gUrrN)hq#:Epr;Q3c!WMuqq?-WmrVi)^!5%LqJ,~>
-q>V6-s8DF;\oV`gdnTlBqu?]prr;lqs82fq,5U@"p\44Qp&Fmfs6BFRs7Q!`mJleFs7ZKmq=smb
-hu3N:r;R*"q"!>+gAogko)8S/p?h#'kjA$;lhg)HrTO4CrVQQjrVQQioC),9jR)j5rp^9Xe'c60
-8jHfDqF_ArDU.Y3!'(u@*tCa?j6bjcq<S4AmH<R/mIKKBlgXcC#P%9Xq"FLPqYpL*rVQKjrVkpN
-m,7q@6<aH_eGfOGnb<OZq#16frr)`jq"X^_lMh_"qtg$YoC;;:mI0cDqY9p^qY9p^qY9p^qtg<e
-rq??drVAnVqY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^qY9p^
-qY9p^qY9p^qYL*gs8Th3s8Muds*t~>
-q#:]ls7s;3-g1M6g@kOG$1RupqXOL`li-e\rrW&rrr2p)o`+p\s7lW[s8V!TrrqWdqsXR]rr35j
-rql0ZjSAZMrs1-T9E5%iht6@#rrDlorsbNXVkKrdXg5:AZEL+3o)B*fs8O<I]tDH9s7#s[rs&H%
-s7lQmpuhYunc/Xas8DfjrVuosqY0XTiqi]T4dI&es763fpA=jgrr`8uqtp0gs8;ormf*=cq>L3h
-r;?Qnrsnedg[ah-p\Fggs8MT[qr[qYrW2rri;X5bs8N&ts8N&ts8N&td/OUTq=aRTZ2++fo'l):
-o_J(XJ,~>
-q#:]ls7s;3-g1M6g@kOG$1RupqXOL`li-e\rrW&rrr2p)o`+p\s7lW[s8V!Ts%_eXqsXR]s8N#a
-s8Vopmf34]oD87V!+Q0#qu6'ap\t'apA"L^rVQ?cnau_Tqtg3dqtg3dqtf4Fp[*!*D.-dW>$+j,
-=_D;flKdoin+uG_o(`7Zs7--k;GhE6rU^'gp\Y6fr;?$Ur9s.QrVQTl%JfthrVcHhr;ZfrrVlfp
-rs\5dp[J;i!;cQVrVlWjrr)j*rVZTlqu$?hqss=Mo_n[YquH]crs&2rs8VWhrr)lsrr)lgrWrK!
-q"aa^qu$EWs8W)urV-?lrkAC5rj_b'!ri/tp&BO~>
-q#:]ls7s;3-g1M6g@kOG$1RupqXOL`li-e\rrW&rrr2p)o`+p\s7lW[s8V!Ts"!=5qsXR]rVQKW
-rVuZmmJZkQlgO3(!(R%EmJ62Mo'tl"gu%/Um-X--jluO.#jq!*lK[AC<,)>?raH1E?!'^%cd:1R
-eG@W+gZS+kn_!mB-mqXfjPoh%oDA@`#5A/to(W1TkPkYTs8VuorrW2trr)j'kje0(!)N[cqX!SK
-s8)iqr9aOTr<E#nqsrkIq>C6qqtTgWqs47grVH6ZnaGl3n+cAKs8)cjrq?Bes8'P."oA#is8Th4
-s8Mucs*t~>
-q>UNhs7H9i48o3Zr;ZT_o)JX^qu?]R49,Ynp;8<i]CWlL(XN-Taql/>rP9I!`9tAU'ZC$g(E1<n
-s1&sW^%_@%q>UBt,F$?a74n!.%_8(0WiN/#WiN.g^S$gejSoVeq=t!i3OL=9rV6?js8)Ttm/HAN
-rq69inbi@arqm-"r:Bg[nbMJFq>:'h!qcEirVm-=s8VWhrp9X`lMgk[nG`Lfnc&dgs8MK_rr)j!
-q=XCSl2USYm/I@hq>($is8Mrq]DhlFrr3-#q"47Wnc++~>
-q>UNhs7H9i2uWdVr;ZT_o)JX^qu?]R49,Ynp;8<i]CWlL(XN-Taql/>rP9I!`9tAU'ZC$g(E'sb
-rjs3hbPqM^p&+[K!)Zic!!)]goCqb?kN;!on*fYjlfm]oE)ZRA'39Kg<GU^emIp>Oqu$ElrUK^Y
-nc&=anG;qmp[RnTs7uBV!,V`3rr;rkm.pMU#OVQYs8Vfms6BOfrr;fos8)cfrVllsrqmT2o`"jg
-qY'R^r;#UWs7$'crVZQjr;Q]crrDiarr<#rrr;osr;6@!pA=@Ys7u]pq=jX\r;Z<cqYp<jnbiRh
-rVZWgs8Ms*rr)iprr)iprr)iprm1NJs8MukZMF=qr;Qlsq"Xg\s*t~>
-q>UNhs7H9i=T/:"r;ZT_o)JX^qu?]R49,Ynp;8<i]CWlL(XN-Taql/>rP9I!`9tAU'ZC$g(Dja\
-rO<mcbQ%V>m.'Go!'WY!!!)`kq>0I9f@/4#gtpl'gtLN3@79rj#ZON?=%m)Wki)O+rp0gUn*9Q:
-m/HVWlh^5cp@S+Zs7>I.!*90dq#CBkn,3%]"n2K[s8VlVrrr5us7u]drVuorq>U`ro)S"7h"Ld>
-rr3-"qtg0dr;?`prVQQkqu6Nop@A1Mrr_liqXF@]"8;-LqTAj<rO`(4q"=Uc_#FB6qZ-T`s*t~>
-q#:]ps8W)hs7cNks7Q?j4T5<[p\b'Os1o-C7%0oCs$3QY55P:,Y*i&]33B#?s";*_2?,h:UJ1Rb
-6'S`3nb2t^rVn4@_;F#"XtBYQ%A0T(['-F$TUNQdX/`_srs&Apo^VSNq#:9onFuVU!U0F[rrjGC
-s82Whrr;fl"nMTco)/+IquH`qr;ZX#p%e1Rq!n+Oq>Bja"oJ?"r;Q]arrDrqrrW&krnIGRrVHHl
-"8MQaqsj[drqcNnrqu]nm/ICkqt^'crr2imr4Dt/_YsK9rqZH\s*t~>
-q#:]ps8W)hs7cNks7Q?j:]:=np\b'Os1o-C7%0oCs$3QY55P:,Y*i&]33B#?s";*_2?,h:U.G"V
-6'\uCqu?]ql1P*iA5P^";?61Nnmu6:?!h&QAl`tQ@:4$'lM9ZMlK@O!nFH;Hr;$?mp\t-mn+QSV
-&cD:qrU9dcmIUDQqu?]kp\+R]rrr,rqtC'akl1hcs82irqu$I*rqu]kr;?HjrVH0_r:L$hr;Q]r
-rqu]orV6Bmr;Q]sqtTX[!<2los8;lnrq['#q>9[Ys8W)sp\":Xr;ZBe!<2`mqu6Wo&cDV*rVZWl
-rVZWlrVZWmrr)firrE&drAOTPr;HQlr;HQlr;HQlr;HQlr;HQlr;HQlr;HQlr;HQlr;HQlr;HQl
-r;HQlrVZQcYk\"krr)j!qtKmap&BO~>
-q#:]ps8W)hs7cNks7Q?j:]:=np\b'Os1o-C7%0oCs$3QY55P:,Y*i&]33B#?s";*_2?,h:TLJGL
-6'o5Jr;QTehWOrA;E67#70)cAo4)!(;cHe%>=iEt<)[8Hh>5n8i8s(ck3VF#lL"!-n,DhYo`+jg
-rrMchq#:^"s8Vclqrd;GnbD_U"o&&pq>^<Trs/N&qZ$Tls8N#ts8Dcn#Q"Dmip?4+qu6U!rVH<_
-q"XU[!;?Eg!VZ-SqZ-T`rri)nqYL0frr`&bn,%_:$MsMqs1\O5rVQKjn,In~>
-q>V--rVud&!!*-$!s&8srr;rso`+jgs#BGS(&nd3oaUj8jn0>^rtOtp#lPb*miDB;l1Y5Y&Fnro
-rq[N%lPTNrlMpl8Y,g1Gj4)>O315TZrrN,srqc]pn,NFequ?]qrVtgT!<;lor;?ToXoAD#r;Zco
-!<2KfqYgNqroX7Zrr<#trk&11]);U.rp]r<~>
-q>V--rVud&!!*-$!s&8srr;rso`+jgs#BGS(&nd3oaUj8jn0>^rtOtp#lPb*miDB;l1Y5Y&Fo$"
-s7mDojV\!slMU2Q?qFO%6V1T^!,cEJp&t'^p\FXSq#:'lqYU3hrp0Rcrqu]mrr`2rqu$Ems82lr
-ci<Y:qZ$EkgAh$Krr)corVluur;HTls7?6\rtPJ4rr;utrr;utrr;utrr;utrr;ugs#^5[rr;ut
-rr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;utrr;us#5nJs
-[/Kt&r;Qlur;HWfs*t~>
-q>V--rVud&!!*-$!s&8srr;rso`+jgs$?(\(&nd3oaUj8jn0>^rtOtp#lPb*miDB;l1Y5Y&Fnoj
-q"5Emmiqf3kk=0>;`mT3,:l2F!+&jrn+$#Aq"jdZo(2qUqtU!`!;l-_"o\;mq"js&rrr;rq"jmd
-rVuor!;kXOmJm1aqYpZrqYL/BrsAW#s8KP.rr;rqmf.e~>
-q#="Xs7Xl<A]=TIrr2rtq>^?lrTsRVrs8\-nc8^i!<3'!rs&2s!;QQoqZ-Zr!;d!#q@*B(s8E)e
-rri<#!WMckoDehRf[A[;gr)U3s+13Hrs&E#q7-J'rp9Z8~>
-q#=.\s7Xl<A]=TIrr2rtq>^?lrTsRVrs8\-nc8^i!<3'!rs&2s!;QQoqZ-Zr!;d!#q@*B(s8NH!
-rrDTc!<2ipm.o`CE)TD)=]YUmnG2t\r;uuurdk*#s0)G,r;QN%s8Dr`s*t~>
-q#=4^s7Xl<A]=TIrr2rtq>^?lrTsRVrs8\-nc8^i!<3'!rs&2s!;QQoqZ-Zr!;d!#q@*B(s8;r_
-qZ?Zp$ig5)jQtIu@mVn(4$!Amh!4G'qgncus/Z/(r;QN%s8Dr`s*t~>
-q>XOqs82iqs0jm1LuA='oDeC]s7lWjp&FEX+9*5Riu72%lf&rbrtb.e*rcN:o+:p0n)"oM&,PT%
-r:qN2q%rbnoDYo?k;g)qa@SVE`RD-*\[f2Y[^EMo[Xkll[KX%B]?$WBlMlA~>
-q>XOqs82iqs0jm1LuA='oDeC]s7lWjp&FEX+9*5Riu72%lf&rbrtb.e*rcN:o+:p0n)"oM&,PZ.
-s7dGtmhGTnrr8^pXoMI%;ZHfbS@PH'[^EQO[^W_s[Xkll[KX%B]?$WBlMlA~>
-q>XOqs82iqs0jm1LuA='oDeC]s7lWjp&FEX+9*5Riu72%lf&rbrtb.e*rcN:o+:p0n)"oM&,PQ#
-qXts'qAoV4q"BlASH),H56(\:MQ3)HZ*LaF[^<Dm[Xkll[KX%B]?$WBlMlA~>
-q>Uius8VZip](%s"r->$s"jZSr;6Nor;Zf5*q2@^\OZZP\Z%aarD9K&0CrG3XZHJSOhXKc8"ueF
-qtig\Ud+bLnFHPX!]-rAqu6ZorVH]nq"X]:qgnXMqZm/rp[u)srVGm\J,~>
-q>Uius8VZip](%s"r->$s"FBOr;6Nor;Zf5*q2@^\OZZP\Z%aarD9K&0CrG3XZHJSOhXKc8"ukM
-rV&FDRQUNJ%K,qi!+#Z\nb2hRs82`nqZHcprVV6DJbubM#QOSnost,$qX"64~>
-q>Uius8VZip](%s"r->$s$H_br;6Nor;Zf5*q2@^\OZZP\Z%aarD9K&0CrG3XZHJSOhXKc8"uhH
-qY3@PUI5(]q=4"Ak2QG=!7'Win*]uFp\ssjp\+@WJbt#qZMOn,q"OHls8Df\s*t~>
-p&@;Is0XU6MYHl9s7lWks8Vi^rriQ?ZM;im#K$Mc&Ke[cs.gD=ci:F%)8kjO/c`H^cl3nFr;X\`
-(s_O;naZAErr39Fs8W)ts7l-]r;HWpquH_Is+13Trr`6"r4;.mJ,~>
-p&@,Ds0XU6MYHl9s7lWks8Vi^rriQ?ZM;im#K$Mc&Ke[cs.gD=ci:F%)8kjO/c`H^cl3tLrVa>I%
-F"GBrsSMuq!8"QpZqSRr;)!EJcD\K"9&8t]'96F~>
-p&@\Ts0XU6MYHl9s7lWks8Vi^rriQ?ZM;im#K$Mc&Ke[cs.gD=ci:F%)8kjO/c`H^cl3qLs8Tk[
-'@64DqtTgEmHrp6n+#N0qtp3grrrAuq"FL]JcC<$Z2Xq)s89Ims*t~>
-q#;Vss8Tl*ABFlOoDe^fp%A@Grr<#ls7QEis6fperVlinp]'pTrVm!!p](-fs8Vd:s7ZBPs8W#s
-s82KZl.bt3qZ"Y:rU9ORp%S4Vr;HQkq>1#?rIOpQrVlg"rNuFrrp9Z8~>
-q#;Vss8Tl*ABFlOoDe^fp%A@Grr<#ls7QEis6fperVlinp]'pTrVm!!p](-fs8Vcus7cQUrVQ'\
-rr)j&o)Ja]r4;RsoDJUirr)forrE%LrIOpQrVlg"rNuFrrp9Z8~>
-q#;Vss8Tl*ABFlOoDe^fp%A@Grr<#ls7QEis6fperVlinp]'pTrVm!!p](-fs8Vd-s7u]\s8Vfm
-s8DikoA]K;lh7m]q"":]"oJ)go_/05rIOpQrVlg"rNuFrrp9Z8~>
-pAY0b"TAB,!WW<#qtg?mpAY(:mJZtZo`+g_s8VfmpAb-ls7u]ds7ZK_s8VNerp]a`s7ZKkrql`q
-s8!B+p@RnDnG*"KYke%co%E[$qu6Tp"9/2prdk*#s0D\)qZ?fr\*<pC~>
-pAY0b"TAB,!WW<#qtg?mpAY(BmJZtZo`+g_s8VfmpAb-ls7u]ds7ZK_s8VNerp]a`s7ZKls8Vrl
-qsjCZqYpT`[/L".mJln[s8ITLJcDhOs8)ltrO;%kJ,~>
-pAY0b"TAB,!WW<#qtg?mpAY(8mJZtZo`+g_s8VfmpAb-ls7u]ds7ZK_s8VNerp]a`s7Z<h$hEob
-q"jj_q>L9Y[Jg+,mf3._rrrAuq"FL]JcC<$ZN't%!rr5.l2Q8~>
-q#:s*s82ios8V`kp&G!es7cQnnG`G)qZ$Tns8Vrqs8D`ms8;osrVuons8W&sq#16mpBLZlrpKUV
-rr)j.r:0FOp$hkUVtJs7nac5Grr)j!rqlTlJcC<$YQ"b&[J]gtm/MS~>
-q#:s*s82ios8V`kp&G!es7cQnnG`G)qZ$Tns8Vrqs8D`ms8;osrVuons8W&sq#16mpC.)rs7QEd
-r;HTks8W&srrr9!s0;Upqu?ZoJcC<$WrE5![J]gtm/MS~>
-q#:s*s82ios8V`kp&G!es7cQnnG`G)qZ$Tns8Vrqs8D`ms8;osrVuons8W&sq#16mpB1His82cp%
-K#qsqXaR^s82irZN'FhrrDrqq>gJFs+13Rrri5,r:p3Vs*t~>
-kl3.%s7--hp]'gaq#Bpbp&Fdds82fqr;Zfls8W)up](6ms7Q*cpAFIUpAY!i%K?8!o_JLaYkRqe
-o(`+Ys8Vusrdk*#s0D\)rWN9!ZhjOas*t~>
-kl3:)s7--hp]'gaq#Bpbp&Fdds82fqr;Zfls8W)up](6ms7Q*cpAb!hqu?ZprVQTo"o\8prqs5(
-rrE&trri;tqYU5Bs+13Qs8W'$s895"qWn03~>
-kl2sus7--hp]'gaq#Bpbp&Fdds82fqr;Zfls8W)up](6ms7Q*cpA=miqZZ`jqu-Eirr3,.q>L9g
-q>^HmJcC<$Z2ak'"TSD+qYKOXJ,~>
-l2LbQrVllnrr2uYrr482s8DWjrVlfks763`s763imf3:Uq>^Kgs82TcpAFsarr;ioqu6o-rVl`g
-r:U!brrW2trdk*#s/uA'\c;Wos*t~>
-l2LbQrVllnrr2uYrr3i&s8DWjrVlfks763`s763imf3:Uq>^KgrVmB,s8MTbqt9gbrr)fnrNcG&
-$2a_opA=[]qY^>Ds+13NrrTb2rTjK6~>
-l2LbQrVllnrr2uYrr3i&s8DWjrVlfks763`s763imf3:Uq>^KgqYp]gpA"4Qqu6o(o_A4Sq==<3
-s+13HrrTb2rTjK6~>
-kl1hcs6B.SlMgekq>9jbs7$'apAadQrVluupAb$ers8Aos7c?Jq>9m\s8W#t^%D=+rqubHs+13U
-rri2lqtIP`s*t~>
-kl1hcs6B.SlMgekq>9jbs7$'apAadQrVluupAb$ertbA(s7lW[s8Vlorr)cmqu$?co('*crr2p&
-rquWgq"jmdJcC<$YQ"b%pA=Tml2Q8~>
-kl1hcs6B.SlMgekq>9jbs7$'apAadQrVluupAb$ers8Aos8)c_s8Vilr;lrtrVm3#Y4M;Xq"X^_
-rIP!"s/Q)%qY'g\[d!gB~>
-kl1YErr4JG1]S,[qpuYr\Gsba*kVIPUD46bs0O*i`9t/h!4i-W*#lopqm@X\aS5N1"M+R4qt9aa
-!WN%Krdk'Rrr2p#rjVn(rp9Z8~>
-kl1YErr4YL1]S,[qpuYr\Gsba*kVIPUD46bs0O*i`9t/h!4i-W*$*3%s19Wl`q00+p\Xjeq@9P$
-qt0IZqtg-aq"adarIOs!rilD$rri>1rql]]s*t~>
-kl1YErr4SJ1]S,[qpuYr\Gsba*kVIPUD46bs0O*i`9t/h!4i-W*$!6's19Tja7TE3rVZ[&Vt0EF
-l1k#Jr;?Qks+10#rj)P&rri>1rql]]s*t~>
-kPm.-s8;`Zs0F$O2P@W<s"1aZ4S/JHW4=VQ>EGpIs%/<e8c(uNV*Y.T2kc]urqZQo_>aH9q>^GF
-s+13Ks8Vs!s805'rp0T7~>
-kPmU:s8;`Zs0F$O2P@W<s"1aZ4S/JHW4=VQ>EGpIs%/<e8c(uNV*Y.V4/\c4s8MfeqY^3er3Q:u
-s8)`ps8;oo"8r,srdk*#s0;V(qZQrr[Jp0ks*t~>
-kPm+,s8;`Zs0F$O2P@W<s"1aZ4S/JHW4=VQ>EGpIs%/<e8c(uNV*Y.U3iAZ6rr2utrVlosZi9e&
-o_\LarIP!"s/Z2"qZQrr[Jp0ks*t~>
-kl1\\qY^@AlllBAm-keX)X?9$s8Ni'k9'^0o(!@l$LZabmg]'Y'`[tGn*UYZqYL3l[Jp10qYBj^
-qu6Tp!WN"JqgnXKqZd*!s80;*rTjK6~>
-kl1\\qY^@KlllBAm-keX)X?9$s8Ni'k9'^0o(!@l$LZabmg]'W%fQ/@p%B-uqYpHlrr<#qWqHAj
-rs8Q&qtg*_q>'l<qgnXKqZd*!s80;*rTjK6~>
-kl1\\qY^@?lllBAm-keX)X?9$s8Ni'k9'^0o(!@l$LZabmg]'X%fQ)=p%TC$rsJ`%poaGms8N&p
-rquZmrIOisqm$#&s8Dup\,ZEms*t~>
-kl3p<s8VZis7^YtrrDTh!<3'!rrr)q!;HKnqZ-Zr!;cs"q@!<'s8NT&rrMWa!;Q$[rqH$\qu">/
-oChkJme?PVJcC<$WrE;&r:d]#q"="RJ,~>
-kl3I/s8VZis7^YtrrDTh!<3'!rrr)q!;HKnqZ-Zr!;cs"q@!<'s8E/er;lTk"98)ps7l9drs$11
-oDS^hrr2rprdk*#s02M-rquN"s7l9Rs*t~>
-kl3[5s8VZis7^YtrrDTh!<3'!rrr)q!;HKnqZ-Zr!;cs"q@!<'s8<&ar;l]n#QOf's8)<]q"r#0
-qu6TqrIP!"s/5l$rquN"s7l9Rs*t~>
-kPmO8s8VclQWsIb&,G`'r>"Dc'DDG>n+-e`*pE&4r"&)f!ril'm2Z0+)rolpo_/1M`N68LY,'4D
-riH<uYH4q5r3Ls[J[2Pd"LP8;`hVeuJ,~>
-kPmjAs8VclQWsIb&,G`'r>"Dc'DDG>n+-e`*pE&4r"&)f!ril'kn<^c)s?H1rVcceag&:dZ_PUI
-[_'5Z]="uNZ%933ZE:D8[&gXSUZqf/~>
-kPm+,s8VclQWsIb&,G`'r>"Dc'DDG>n+-e`*pE&4r"&)f!ril'kRmL`)sZc9rr3T'b,qhNXf9j_
-]=bh_\[8`LZMh"YZ@T<dZ37P9[)Sm*s*t~>
-l2NF7s7ZKfs+Y.cqYrsWSk&`HX/%oTs!b4D:\clIUHec9[APt`5b"cIqYrjG[l3pTrX#q2qY^9g
-qu$HmJcC<$WrE8%s80;*rTjK6~>
-l2NF7s7ZKfs+Y.cqYrsWSk&`HX/%oTs!b4D:\clIUHec9[APt`5b"]CpAI:B\2sH]rrTP+qgncu
-s.fStrr;l)s8D9`J,~>
-l2NF7s7ZKfs+Y.cqYrsWSk&`HX/%oTs!b4D:\clIUHec9[APt`5b"`Cp&77D\i]cds8Mrs[=S@/
-s.TGrrr;l)s8D9`J,~>
-kl37(s8W)n+92cLpWF-s_XtbW(s`-TbS_;9s2Q<1_<8KG*khTt'c4[bs100ca8#Z9ZiBoRs+13F
-rrrE%qmZV(li2J~>
-kl37(s8W)n+92cLpWF-s_XtbW(s`-TbS_;9s2Q<1_<8KG*khTt'c4[bs100ca8#Z9ZiBoRs+13F
-rrrE%qmZV(li2J~>
-kl37(s8W)n+92cLpWF-s_XtbW(s`-TbS_;9s2Q<1_<8KG*khTt'c4[bs100ca8#Z9ZiBoRs+13F
-rrrE%qmZV(li2J~>
-l2M4js8Dffs7cQfp](6fmJ[%lo`+p\s8)c^s8UpRrrqZep$)MQrr3/gs8VfmlhUP^ZiBoRs+13F
-rrrE%qmZV(li2J~>
-l2M4js8Dffs7cQfp](6fmJ[%lo`+p\s8)c^s8UpRrrqZep$)MQrr3/gs8VfmlhUP^ZiBoRs+13F
-rrrE%qmZV(li2J~>
-l2M4js8Dffs7cQfp](6fmJ[%lo`+p\s8)c^s8UpRrrqZep$)MQrr3/gs8VfmlhUP^ZiBoRs+13F
-rrrE%qmZV(li2J~>
-l2N=)s7lWomf3%Xs7?'dqZ$TkpAasfs6BIYs7>XXli6bNs7?9jp@J=ajT#8ApAY3#s8.BIJcDMF
-"oeQ!\,ZEms*t~>
-l2N=)s7lWomf3%Xs7?'dqZ$TkpAasfs6BIYs7>XXli6bNs7?9jp@J=ajT#8ApAY3#s8.BIJcDMF
-"oeQ!\,ZEms*t~>
-l2N=)s7lWomf3%Xs7?'dqZ$TkpAasfs6BIYs7>XXli6bNs7?9jp@J=ajT#8ApAY3#s8.BIJcDMF
-"oeQ!\,ZEms*t~>
-kl2:hrqHHmoD/.\p&G'jq=jphnbN+_&,lP-q>^Kls8DipmJlnZqYgEqq#Bs]rrTP,qgncus.fSt
-rr;l)s8D9`J,~>
-kl2:hrqHHmoD/.\p&G'jq=jphnbN+_&,lP-q>^Kls8DipmJlnZqYgEqq#Bs]rrTP,qgncus.fSt
-rr;l)s8D9`J,~>
-kl2:hrqHHmoD/.\p&G'jq=jphnbN+_&,lP-q>^Kls8DipmJlnZqYgEqq#Bs]rrTP,qgncus.fSt
-rr;l)s8D9`J,~>
-l2Lq_rr<#sq#:9qp&G']rr2umrr3N#qu>XTlMU\Sp%JFckl:JWrs/8tn,NFas8Mio!jhq(JcC<$
-U]1Mss80;*rTjK6~>
-l2Lq_rr<#sq#:9qp&G']rr2umrr3N#qu>XTlMU\Sp%JFckl:JWrs/8tn,NFas8Mio!jhq(JcC<$
-U]1Mss80;*rTjK6~>
-l2Lq_rr<#sq#:9qp&G']rr2umrr3N#qu>XTlMU\Sp%JFckl:JWrs/8tn,NFas8Mio!jhq(JcC<$
-U]1Mss80;*rTjK6~>
-kPl(^s8Vfmme?SXrVuiaq#C!brrr5urr<#jrVm,ms7lNis8VEarr`2tqt9aa!jhq(JcC<$U]1Ms
-s80;*rTjK6~>
-kPl(^s8Vfmme?SXrVuiaq#C!brrr5urr<#jrVm,ms7lNis8VEarr`2tqt9aa!jhq(JcC<$U]1Ms
-s80;*rTjK6~>
-kPl(^s8Vfmme?SXrVuiaq#C!brrr5urr<#jrVm,ms7lNis8VEarr`2tqt9aa!jhq(JcC<$U]1Ms
-s80;*rTjK6~>
-kl1hYs8MQgq>L=9nc/Xfs8Vlls7Q3fo`+UYs7lNlp&Fm^s8Vros7cQkr;Q]ro(i:eZiBoRs+13F
-rrrE%qmZV(li2J~>
-kl1hYs8MQgq>L=9nc/Xfs8Vlls7Q3fo`+UYs7lNlp&Fm^s8Vros7cQkr;Q]ro(i:eZiBoRs+13F
-rrrE%qmZV(li2J~>
-kl1hYs8MQgq>L=9nc/Xfs8Vlls7Q3fo`+UYs7lNlp&Fm^s8Vros7cQkr;Q]ro(i:eZiBoRs+13F
-rrrE%qmZV(li2J~>
-kPm78qu?WfI/s<Daqbl3rPB]m`;f\T)'H<d^)%O/s25fr])9Ph'>OSI]HeE6s8W&rrr3<(s0D\)
-qtg?mrIP!"s/,eur42k,li2J~>
-kPm78qu?WfI/s<Daqbl3rPB]m`;f\T)'H<d^)%O/s25fr])9Ph'>OSI]HeE6s8W&rrr3<(s0D\)
-qtg?mrIP!"s/,eur42k,li2J~>
-kPm78qu?WfI/s<Daqbl3rPB]m`;f\T)'H<d^)%O/s25fr])9Ph'>OSI]HeE6s8W&rrr3<(s0D\)
-qtg?mrIP!"s/,eur42k,li2J~>
-l2NF.s7lWkrr@TSs6rU[Z9&"TTWD;es$rca2uc+BXZ6;NT!L?T0s7TGpARmTV+(%UrsSc%r;Q`-
-s8W&ns8@NKJcD\K#5e5qppC"sli2J~>
-l2NF.s7lWkrr@TSs6rU[Z9&"TTWD;es$rca2uc+BXZ6;NT!L?T0s7TGpARmTV+(%UrsSc%r;Q`-
-s8W&ns8@NKJcD\K#5e5qppC"sli2J~>
-l2NF.s7lWkrr@TSs6rU[Z9&"TTWD;es$rca2uc+BXZ6;NT!L?T0s7TGpARmTV+(%UrsSc%r;Q`-
-s8W&ns8@NKJcD\K#5e5qppC"sli2J~>
-kl3j6oDedds8N)mrsmcK(B4j:gB7?Q$1I0qkmd=D*V0d/koT^Ch=D!Rndk![+oh*1s8W&ts88qi
-qZ$Kmp&0IAJcDSH!kA:.li2J~>
-kl3j6oDedds8N)mrsmcK(B4j:gB7?Q$1I0qkmd=D*V0d/koT^Ch=D!Rndk![+oh*1s8W&ts88qi
-qZ$Kmp&0IAJcDSH!kA:.li2J~>
-kl3j6oDedds8N)mrsmcK(B4j:gB7?Q$1I0qkmd=D*V0d/koT^Ch=D!Rndk![+oh*1s8W&ts88qi
-qZ$Kmp&0IAJcDSH!kA:.li2J~>
-l2LbQrVnPCs6TgdrrE*!"8i0!rsJf+!<<'!rs/N&"9/N(rrD]qs8N*!rt,.l!;cTms8D`lrrBt7
-rrDtJs+13Jrs&;spU:,"rp9Z8~>
-l2LbQrVnPCs6TgdrrE*!"8i0!rsJf+!<<'!rs/N&"9/N(rrD]qs8N*!rt,.l!;cTms8D`lrrBt7
-rrDtJs+13Jrs&;spU:,"rp9Z8~>
-l2LbQrVnPCs6TgdrrE*!"8i0!rsJf+!<<'!rs/N&"9/N(rrD]qs8N*!rt,.l!;cTms8D`lrrBt7
-rrDtJs+13Jrs&;spU:,"rp9Z8~>
-kl37/rVu*]p&>B_s!$Xp$2bS%q[)Wh$L@-drrW5b!<38siY)8!lMDUppBgBf$i^/8lc--3ZEBJ4
-ZEC=9Ye>UpJ[DA_"L5Y`T`+0UJ,~>
-kl37/rVu*]p&>B_s!$Xp$2bS%q[)Wh$L@-drrW5b!<38siY)8!lMDUppBgBf$i^/8lc--3ZEBJ4
-ZEC=9Ye>UpJ[DA_"L5Y`T`+0UJ,~>
-kl37/rVu*]p&>B_s!$Xp$2bS%q[)Wh$L@-drrW5b!<38siY)8!lMDUppBgBf$i^/8lc--3ZEBJ4
-ZEC=9Ye>UpJ[DA_"L5Y`T`+0UJ,~>
-l2NU9s82cprUp0js78RJSk8rHVO'aOqEh3^6hN^1Yu(<kUTd)J8uS79s8P<hTfiAOs8Vrkrr35C
-s8)Wks7lMCs+13Krs&H!p:1/!p[%p1~>
-l2NU9s82cprUp0js78RJSk8rHVO'aOqEh3^6hN^1Yu(<kUTd)J8uS79s8P<hTfiAOs8Vrkrr35C
-s8)Wks7lMCs+13Krs&H!p:1/!p[%p1~>
-l2NU9s82cprUp0js78RJSk8rHVO'aOqEh3^6hN^1Yu(<kUTd)J8uS79s8P<hTfiAOs8Vrkrr35C
-s8)Wks7lMCs+13Krs&H!p:1/!p[%p1~>
-l2NL;s8Vuds7lQno`)<K-aWie+Y:A)s3(lk]`$+u%_D\N*=pg"`u?,Co`)?2)U\<Trr)j!q=LZ^
-rVlosqLSQqr2KSsrqcZl\`s-E~>
-l2NL;s8Vuds7lQno`)<K-aWie+Y:A)s3(lk]`$+u%_D\N*=pg"`u?,Co`)?2)U\<Trr)j!q=LZ^
-rVlosqLSQqr2KSsrqcZl\`s-E~>
-l2NL;s8Vuds7lQno`)<K-aWie+Y:A)s3(lk]`$+u%_D\N*=pg"`u?,Co`)?2)U\<Trr)j!q=LZ^
-rVlosqLSQqr2KSsrqcZl\`s-E~>
-kl2_#qu?]as7H?hqu?$_s7H?gq>UEnnGiI_s7c-bs7ZHl&GcA%rr2fpp\k-jnGiOdq>L9l#.+@0
-qss^_JcC<$W;d)#rr;r'qYKOXJ,~>
-kl2_#qu?]as7H?hqu?$_s7H?gq>UEnnGiI_s7c-bs7ZHl&GcA%rr2fpp\k-jnGiOdq>L9l#.+@0
-qss^_JcC<$W;d)#rr;r'qYKOXJ,~>
-kl2_#qu?]as7H?hqu?$_s7H?gq>UEnnGiI_s7c-bs7ZHl&GcA%rr2fpp\k-jnGiOdq>L9l#.+@0
-qss^_JcC<$W;d)#rr;r'qYKOXJ,~>
-^Ae<5s8VukrrTP,qgncus.fStrr;l)s8D9`J,~>
-^Ae<5s8VukrrTP,qgncus.fStrr;l)s8D9`J,~>
-^Ae<5s8VukrrTP,qgncus.fStrr;l)s8D9`J,~>
-_>ac5s8MrrnGiL`rrTP,qgncus.fStrr;l)s8D9`J,~>
-_>ac5s8MrrnGiL`rrTP,qgncus.fStrr;l)s8D9`J,~>
-_>ac5s8MrrnGiL`rrTP,qgncus.fStrr;l)s8D9`J,~>
-_#FW2s8N&os7Z9g!jhq(JcC<$U]1Mss80;*rTjK6~>
-_#FW2s8N&os7Z9g!jhq(JcC<$U]1Mss80;*rTjK6~>
-_#FW2s8N&os7Z9g!jhq(JcC<$U]1Mss80;*rTjK6~>
-_#FW7s8Vurs7Z9g!jhq(JcC<$U]1Mss80;*rTjK6~>
-_#FW7s8Vurs7Z9g!jhq(JcC<$U]1Mss80;*rTjK6~>
-_#FW7s8Vurs7Z9g!jhq(JcC<$U]1Mss80;*rTjK6~>
-_>a`(s7?9jr;HEj!jhq(JcC<$U]1Mss80;*rTjK6~>
-_>a`(s7?9jr;HEj!jhq(JcC<$U]1Mss80;*rTjK6~>
-_>a`(s7?9jr;HEj!jhq(JcC<$U]1Mss80;*rTjK6~>
-^Ae>R*$)ilq>UN&s8.BIJcDMF"oeQ!\,ZEms*t~>
-^Ae>R*$)ilq>UN&s8.BIJcDMF"oeQ!\,ZEms*t~>
-^Ae>R*$)ilq>UN&s8.BIJcDMF"oeQ!\,ZEms*t~>
-_>a`1s5uVCRRd/Q!jhq(JcC<$U]1Mss80;*rTjK6~>
-_>a`1s5uVCRRd/Q!jhq(JcC<$U]1Mss80;*rTjK6~>
-_>a`1s5uVCRRd/Q!jhq(JcC<$U]1Mss80;*rTjK6~>
-_#FT-rsA2p!;QQqZiBoRs+13FrrrE%qmZV(li2J~>
-_#FT-rsA2p!;QQqZiBoRs+13FrrrE%qmZV(li2J~>
-_#FT-rsA2p!;QQqZiBoRs+13FrrrE%qmZV(li2J~>
-_>b>Hs7uitp]^]js8W#lrr;r+rVuiis8Vr1rr`&rs7jS5"8MorpqHb4rr;rns8W&6rs/N%s8W)q
-s80Y4#58)rrPAC*li2J~>
-_>b>Hs7uitp]^]js8W#lrr;r+rVuiis8Vr1rr`&rs7jS5"8MorpqHb4rr;rns8W&6rs/N%s8W)q
-s80Y4#58)rrPAC*li2J~>
-_>b>Hs7uitp]^]js8W#lrr;r+rVuiis8Vr1rr`&rs7jS5"8MorpqHb4rr;rns8W&6rs/N%s8W)q
-s80Y4#58)rrPAC*li2J~>
-_#FT8rsJ#j!<3!-m)ulG\@8KOW4BOJXgl3Q#H5;AZF-jC\&ko\\$`BHZEq3B_6O<QYd_'G\>QgP
-Z3S"FYcb[<^oY>Ws02X=Vu#]YJ,~>
-_#FT8rsJ#j!<3!-m)ulG\@8KOW4BOJXgl3Q#H5;AZF-jC\&ko\\$`BHZEq3B_6O<QYd_'G\>QgP
-Z3S"FYcb[<^oY>Ws02X=Vu#]YJ,~>
-_#FT8rsJ#j!<3!-m)ulG\@8KOW4BOJXgl3Q#H5;AZF-jC\&ko\\$`BHZEq3B_6O<QYd_'G\>QgP
-Z3S"FYcb[<^oY>Ws02X=Vu#]YJ,~>
-_>b>LnbYhCY<i3cs8Vicr;Zf;r;$BfrV?H/rs-11rqZTlqYA85#Pn5os7uZg]>4FNq>C9cp;H^A
-rs/Q"rVHH,q=hW'"SK>'s7bm[J,~>
-_>b>LnbYhCY<i3cs8Vicr;Zf;r;$BfrV?H/rs-11rqZTlqYA85#Pn5os7uZg]>4FNq>C9cp;H^A
-rs/Q"rVHH,q=hW'"SK>'s7bm[J,~>
-_>b>LnbYhCY<i3cs8Vicr;Zf;r;$BfrV?H/rs-11rqZTlqYA85#Pn5os7uZg]>4FNq>C9cp;H^A
-rs/Q"rVHH,q=hW'"SK>'s7bm[J,~>
-^]+N1`[qeMoD8Cb#5I_aqu?]jrr9h5"0DP&rr)l=rVc`urr<#"_>X<3!rfS,_>X<3!j)D$_>OW2
-r;Zeur;ZTZs*t~>
-^]+N1`[qeMoD8Cb#5I_aqu?]jrr9h5"0DP&rr)l=rVc`urr<#"_>X<3!rfS,_>X<3!j)D$_>OW2
-r;Zeur;ZTZs*t~>
-^]+N1`[qeMoD8Cb#5I_aqu?]jrr9h5"0DP&rr)l=rVc`urr<#"_>X<3!rfS,_>X<3!j)D$_>OW2
-r;Zeur;ZTZs*t~>
-_>ao5rVu]fs763cr;QZnrs-74q>U-dp@c?&#.+@0n+Z\Wa8ZABqY0IYs0KQA#5\,js8Vu"^]4?+
-!rDr'^&J91s/Q,!o'HC,~>
-_>ao5rVu]fs763cr;QZnrs-74q>U-dp@c?&#.+@0n+Z\Wa8ZABqY0IYs0KQA#5\,js8Vu"^]4?+
-!rDr'^&J91s/Q,!o'HC,~>
-_>ao5rVu]fs763cr;QZnrs-74q>U-dp@c?&#.+@0n+Z\Wa8ZABqY0IYs0KQA#5\,js8Vu"^]4?+
-!rDr'^&J91s/Q,!o'HC,~>
-^]+K0s8Vrqp\t0rqt^'aqmHG'"8r3!rPAI8\,?:*rVt"=rVm!!s89@Brquctqn<$GrW3&urr3&7
-r;XV4"S_l_s/G8_J,~>
-^]+K0s8Vrqp\t0rqt^'aqmHG'"8r3!rPAI8\,?:*rVt"=rVm!!s89@Brquctqn<$GrW3&urr3&7
-r;XV4"S_l_s/G8_J,~>
-^]+K0s8Vrqp\t0rqt^'aqmHG'"8r3!rPAI8\,?:*rVt"=rVm!!s89@Brquctqn<$GrW3&urr3&7
-r;XV4"S_l_s/G8_J,~>
-_>al@s76-gs8;cjqu6Nn#H@S"rU^'hqSE15_!V!srrD`6rrD`jrrW&a^qp$Ur;ZZfs7+>(_>ac:
-qu?]b`pio>rrU%/q!7s1~>
-_>al@s76-gs8;cjqu6Nn#H@S"rU^'hqSE15_!V!srrD`6rrD`jrrW&a^qp$Ur;ZZfs7+>(_>ac:
-qu?]b`pio>rrU%/q!7s1~>
-_>al@s76-gs8;cjqu6Nn#H@S"rU^'hqSE15_!V!srrD`6rrD`jrrW&a^qp$Ur;ZZfs7+>(_>ac:
-qu?]b`pio>rrU%/q!7s1~>
-_#FQ8s8Vchrr3<'r;HWorVlfrn,E=fp]&)/!;ZWo"8qups2k9?rrMrnrr2uo_>X]8s8Mios763+
-rW3&prr3&ms8Tq7#QOPurp]sfqX"64~>
-_#FQ8s8Vchrr3<'r;HWorVlfrn,E=fp]&)/!;ZWo"8qups2k9?rrMrnrr2uo_>X]8s8Mios763+
-rW3&prr3&ms8Tq7#QOPurp]sfqX"64~>
-_#FQ8s8Vchrr3<'r;HWorVlfrn,E=fp]&)/!;ZWo"8qups2k9?rrMrnrr2uo_>X]8s8Mios763+
-rW3&prr3&ms8Tq7#QOPurp]sfqX"64~>
-\,QO+q>:-j#5/#joDejfaSuM;s82`fs8Voorr`/us8L%<"8ViooY1>/r;Zfgs7X;/"TJ2rq"t$i
-"oA9!q#Bj.rsSi#rqucgs8W#sq>0FWJ,~>
-\,QO+q>:-j#5/#joDejfaSuM;s82`fs8Voorr`/us8L%<"8ViooY1>/r;Zfgs7X;/"TJ2rq"t$i
-"oA9!q#Bj.rsSi#rqucgs8W#sq>0FWJ,~>
-\,QO+q>:-j#5/#joDejfaSuM;s82`fs8Voorr`/us8L%<"8ViooY1>/r;Zfgs7X;/"TJ2rq"t$i
-"oA9!q#Bj.rsSi#rqucgs8W#sq>0FWJ,~>
-\c2^!r;HWtp&FXRrVlolrQ5'@nGiCbrs%rls8;fpqo8X@q>^Hos8)cl_>a]7qu?*am_8]-qZ$Tk
-m/Q_Rr:0dd!V?-7rs&E$qYgHjq#:9sirB&Us7bm[J,~>
-\c2^!r;HWtp&FXRrVlolrQ5'@nGiCbrs%rls8;fpqo8X@q>^Hos8)cl_>a]7qu?*am_8]-qZ$Tk
-m/Q_Rr:0dd!V?-7rs&E$qYgHjq#:9sirB&Us7bm[J,~>
-\c2^!r;HWtp&FXRrVlolrQ5'@nGiCbrs%rls8;fpqo8X@q>^Hos8)cl_>a]7qu?*am_8]-qZ$Tk
-m/Q_Rr:0dd!V?-7rs&E$qYgHjq#:9sirB&Us7bm[J,~>
-\,QU(qt'jWrr3)so`+j0rrr<"p[/"Qr;Qous8W&qaSuJ9s8VlmrqjJ1!;QQn!W23!rt"i!s6p!f
-nG`Ifo`+RYs8:4C%.X5prr;rfs8Vrgs7kp[J,~>
-\,QU(qt'jWrr3)so`+j0rrr<"p[/"Qr;Qous8W&qaSuJ9s8VlmrqjJ1!;QQn!W23!rt"i!s6p!f
-nG`Ifo`+RYs8:4C%.X5prr;rfs8Vrgs7kp[J,~>
-\,QU(qt'jWrr3)so`+j0rrr<"p[/"Qr;Qous8W&qaSuJ9s8VlmrqjJ1!;QQn!W23!rt"i!s6p!f
-nG`Ifo`+RYs8:4C%.X5prr;rfs8Vrgs7kp[J,~>
-\c2[%rVm#fs)SCsrr3&ls8L.?!<2ut"Ss&;&XrXt!W2f9rs&2so_ngJ#JpEErUg,o!#)NNrsngQ
-$NKkS4UE5-s69R`o[*U<q>UC%mJles"p2(#.Kgcpm/MS~>
-\c2[%rVm#fs)SCsrr3&ls8L.?!<2ut"Ss&;&XrXt!W2f9rs&2so_ngJ#JpEErUg,o!#)NNrsngQ
-$NKkS4UE5-s69R`o[*U<q>UC%mJles"p2(#.Kgcpm/MS~>
-\c2[%rVm#fs)SCsrr3&ls8L.?!<2ut"Ss&;&XrXt!W2f9rs&2so_ngJ#JpEErUg,o!#)NNrsngQ
-$NKkS4UE5-s69R`o[*U<q>UC%mJles"p2(#.Kgcpm/MS~>
-\,Qd&s7u[#K+%_ZpAY'lm`>D;p$r(Zs8TDDs7H?gs8L+>#O29Ws8Upu#J^9BrX@)l%J@R;$3Yt]
-s8-'Ho)8FRrr2uid/OXPs6fmdm.UJX_\<(Gq614ms*t~>
-\,Qd&s7u[#K+%_ZpAY'lm`>D;p$r(Zs8TDDs7H?gs8L+>#O29Ws8Upu#J^9BrX@)l%J@R;$3Yt]
-s8-'Ho)8FRrr2uid/OXPs6fmdm.UJX_\<(Gq614ms*t~>
-\,Qd&s7u[#K+%_ZpAY'lm`>D;p$r(Zs8TDDs7H?gs8L+>#O29Ws8Upu#J^9BrX@)l%J@R;$3Yt]
-s8-'Ho)8FRrr2uid/OXPs6fmdm.UJX_\<(Gq614ms*t~>
-\c2s5s8Mlos5a(Xrr3#uo>gk2rr)j&p&F(fs7ZKkrPnjAr;$'XRt1^Yrs&?"q@VQ0*l%^aq#gQp%
-K#qqs8W#es8:4C%f5htoD\dcr3@F?s7uR9m/MS~>
-\c2s5s8Mlos5a(Xrr3#uo>gk2rr)j&p&F(fs7ZKkrPnjAr;$'XRt1^Yrs&?"q@VQ0*l%^aq#gQp%
-K#qqs8W#es8:4C%f5htoD\dcr3@F?s7uR9m/MS~>
-\c2s5s8Mlos5a(Xrr3#uo>gk2rr)j&p&F(fs7ZKkrPnjAr;$'XRt1^Yrs&?"q@VQ0*l%^aq#gQp%
-K#qqs8W#es8:4C%f5htoD\dcr3@F?s7uR9m/MS~>
-\,Qp:s8MlprsSYpq>^KnrlG*En,N+]q=0?)rVlrqs80q<#Pn5rp#m+d!5\[?r:Bra!W]+i_>b&/
-rrE)k&j6i#l2:SUs8VT9rt#)#s8W)dp%8:uU\XrhFFifYJ,~>
-\,Qp:s8MlprsSYpq>^KnrlG*En,N+]q=0?)rVlrqs80q<#Pn5rp#m+d!5\[?r:Bra!W]+i_>b&/
-rrE)k&j6i#l2:SUs8VT9rt#)#s8W)dp%8:uU\XrhFFifYJ,~>
-\,Qp:s8MlprsSYpq>^KnrlG*En,N+]q=0?)rVlrqs80q<#Pn5rp#m+d!5\[?r:Bra!W]+i_>b&/
-rrE)k&j6i#l2:SUs8VT9rt#)#s8W)dp%8:uU\XrhFFifYJ,~>
-\GuU+s8NE!s7-0ds8W#rqT/[Ir;ZHis8JBds7?9cs8)cmao;VCs8MmObl7d[rri0<^r[M/rsT#(
-rrU?i'*&"*s7l?8rso#-rq-6jp](8b*<5i(>jME?~>
-\GuU+s8NE!s7-0ds8W#rqT/[Ir;ZHis8JBds7?9cs8)cmao;VCs8MmObl7d[rri0<^r[M/rsT#(
-rrU?i'*&"*s7l?8rso#-rq-6jp](8b*<5i(>jME?~>
-\GuU+s8NE!s7-0ds8W#rqT/[Ir;ZHis8JBds7?9cs8)cmao;VCs8MmObl7d[rri0<^r[M/rsT#(
-rrU?i'*&"*s7l?8rso#-rq-6jp](8b*<5i(>jME?~>
-Z2Y".s82Khs7+21&,cD's88Eds82ils8Vrqp<!=Fp](3l!!!]5%IsJps7$'^a8Z>A&^\K3r5&C@
-q>UHps#dX8*;9F/c2S=Os7?9jp](!b_%cg6@/g)js*t~>
-Z2Y".s82Khs7+21&,cD's88Eds82ils8Vrqp<!=Fp](3l!!!]5%IsJps7$'^a8Z>A&^\K3r5&C@
-q>UHps#dX8*;9F/c2S=Os7?9jp](!b_%cg6@/g)js*t~>
-Z2Y".s82Khs7+21&,cD's88Eds82ils8Vrqp<!=Fp](3l!!!]5%IsJps7$'^a8Z>A&^\K3r5&C@
-q>UHps#dX8*;9F/c2S=Os7?9jp](!b_%cg6@/g)js*t~>
-Z2Y"Cl2UeZq!cB)%fQG-k5PGb!!MTel2UVRa8ZSErTjL`lNQhYqu?]ba8ZA=r;Vm#$(\j2%fHJ/
-jP)9eUAt)is82ipdJjaSs8W&hs5"Xp$H2`?!!E;gs*t~>
-Z2Y"Cl2UeZq!cB)%fQG-k5PGb!!MTel2UVRa8ZSErTjL`lNQhYqu?]ba8ZA=r;Vm#$(\j2%fHJ/
-jP)9eUAt)is82ipdJjaSs8W&hs5"Xp$H2`?!!E;gs*t~>
-Z2Y"Cl2UeZq!cB)%fQG-k5PGb!!MTel2UVRa8ZSErTjL`lNQhYqu?]ba8ZA=r;Vm#$(\j2%fHJ/
-jP)9eUAt)is82ipdJjaSs8W&hs5"Xp$H2`?!!E;gs*t~>
-Z2Y'ls7ZKes8Vr;s8W!!p&G']rVm&ts8Dipn]Ce;rVuops6Td`s7?3h!WMl9rs/N&q#:<noCB`t
-&,cJ-qu?]cs8N&js8;`nrmC`Qqu?Tos7cQnrr;inrrD]Xs*t~>
-Z2Y'ls7ZKes8Vr;s8W!!p&G']rVm&ts8Dipn]Ce;rVuops6Td`s7?3h!WMl9rs/N&q#:<noCB`t
-&,cJ-qu?]cs8N&js8;`nrmC`Qqu?Tos7cQnrr;inrrD]Xs*t~>
-Z2Y'ls7ZKes8Vr;s8W!!p&G']rVm&ts8Dipn]Ce;rVuops6Td`s7?3h!WMl9rs/N&q#:<noCB`t
-&,cJ-qu?]cs8N&js8;`nrmC`Qqu?Tos7cQnrr;inrrD]Xs*t~>
-Yl=q*rVufqs8L+>%.F5pnc.qRp%n@]s7FA3&,?1is8VuerqZ6eqs4:^qoJd>jo>AF^Ae`?rVQNk
-s7uQlqu?]gs8W)Ers\/is7c*Us8Vrjs7?!Ns*t~>
-Yl=q*rVufqs8L+>%.F5pnc.qRp%n@]s7FA3&,?1is8VuerqZ6eqs4:^qoJd>jo>AF^Ae`?rVQNk
-s7uQlqu?]gs8W)Ers\/is7c*Us8Vrjs7?!Ns*t~>
-Yl=q*rVufqs8L+>%.F5pnc.qRp%n@]s7FA3&,?1is8VuerqZ6eqs4:^qoJd>jo>AF^Ae`?rVQNk
-s7uQlqu?]gs8W)Ers\/is7c*Us8Vrjs7?!Ns*t~>
-Z2XjrnG`Fgq=MZ+$gRcds8Vlor9"%Zou6q=nc&Ofr;ZforVufnaSuMCs8DornGN*rrsSW%li7"\
-s8Vrqs7Xh>s8;os#jM!]s8D'Zs6o4PJ,~>
-Z2XjrnG`Fgq=MZ+$gRcds8Vlor9"%Zou6q=nc&Ofr;ZforVufnaSuMCs8DornGN*rrsSW%li7"\
-s8Vrqs7Xh>s8;os#jM!]s8D'Zs6o4PJ,~>
-Z2XjrnG`Fgq=MZ+$gRcds8Vlor9"%Zou6q=nc&Ofr;ZforVufnaSuMCs8DornGN*rrsSW%li7"\
-s8Vrqs7Xh>s8;os#jM!]s8D'Zs6o4PJ,~>
-YQ"b&mf3=^aSue?s7cQnnbi4^m/R+^pAas0rrDHcrsA>tp[J4Rs8VrmaSuMAs8;iqqu?Z3rs/)n
-s8W&pq=O[d"8;cpnB_+Ep\k-lqt^*\s8Mcms5s:Hs*t~>
-YQ"b&mf3=^aSue?s7cQnnbi4^m/R+^pAas0rrDHcrsA>tp[J4Rs8VrmaSuMAs8;iqqu?Z3rs/)n
-s8W&pq=O[d"8;cpnB_+Ep\k-lqt^*\s8Mcms5s:Hs*t~>
-YQ"b&mf3=^aSue?s7cQnnbi4^m/R+^pAas0rrDHcrsA>tp[J4Rs8VrmaSuMAs8;iqqu?Z3rs/)n
-s8W&pq=O[d"8;cpnB_+Ep\k-lqt^*\s8Mcms5s:Hs*t~>
-Z2Xmns8;lr!;sn;"T&/ks82cp"o[ras8Vo7rsJ\is8Vlms8Vrhq8WF<pAa^^s7O,+!W)HdrrrAr
-p&Fjcd/OUMs8V?`s763im/6\Zs7kp[J,~>
-Z2Xmns8;lr!;sn;"T&/ks82cp"o[ras8Vo7rsJ\is8Vlms8Vrhq8WF<pAa^^s7O,+!W)HdrrrAr
-p&Fjcd/OUMs8V?`s763im/6\Zs7kp[J,~>
-Z2Xmns8;lr!;sn;"T&/ks82cp"o[ras8Vo7rsJ\is8Vlms8Vrhq8WF<pAa^^s7O,+!W)HdrrrAr
-p&Fjcd/OUMs8V?`s763im/6\Zs7kp[J,~>
-Z2Xn(s7ZEk!<("=%JTc"o`+s`s8;osrV$!+rsnZ#rr;lps8V]js82iroZ7%9r;Zfls8Kh6&,?2!
-s8VZ_s8N&urVuHgq9]-Bo)8Rf!<)lr"T&/hs7>UWJ,~>
-Z2Xn(s7ZEk!<("=%JTc"o`+s`s8;osrV$!+rsnZ#rr;lps8V]js82iroZ7%9r;Zfls8Kh6&,?2!
-s8VZ_s8N&urVuHgq9]-Bo)8Rf!<)lr"T&/hs7>UWJ,~>
-Z2Xn(s7ZEk!<("=%JTc"o`+s`s8;osrV$!+rsnZ#rr;lps8V]js82iroZ7%9r;Zfls8Kh6&,?2!
-s8VZ_s8N&urVuHgq9]-Bo)8Rf!<)lr"T&/hs7>UWJ,~>
-JcD\K$i^,(rqQ?is7cQfqYpL&rqufbs8Vclq>^9frVm*#s8;Qis6kO=Zi>O~>
-JcD\K$i^,(rqQ?is7cQfqYpL&rqufbs8Vclq>^9frVm*#s8;Qis6kO=Zi>O~>
-JcD\K$i^,(rqQ?is7cQfqYpL&rqufbs8Vclq>^9frVm*#s8;Qis6kO=Zi>O~>
-JcD_L"8)Nkqu6U'nG*"ZqZ$<io)JadrVmB'qX=IMs7bs]s763irUtgBZN#F~>
-JcD_L"8)Nkqu6U'nG*"ZqZ$<io)JadrVmB'qX=IMs7bs]s763irUtgBZN#F~>
-JcD_L"8)Nkqu6U'nG*"ZqZ$<io)JadrVmB'qX=IMs7bs]s763irUtgBZN#F~>
-JcD\K#PJ,skP5&Vq#::#o_.tXp[nL^q>]j]rr`,to'u_Z#6"T$s7$'eJcE+WJ,~>
-JcD\K#PJ,skP5&Vq#::#o_.tXp[nL^q>]j]rr`,to'u_Z#6"T$s7$'eJcE+WJ,~>
-JcD\K#PJ,skP5&Vq#::#o_.tXp[nL^q>]j]rr`,to'u_Z#6"T$s7$'eJcE+WJ,~>
-JcD_L%IF,nrq731!!W#pnc/:\rt4`"s7lWbq=FXPs8VfhpAb$UJcDtSJ,~>
-JcD_L%IF,nrq731!!W#pnc/:\rt4`"s7lWbq=FXPs8VfhpAb$UJcDtSJ,~>
-JcD_L%IF,nrq731!!W#pnc/:\rt4`"s7lWbq=FXPs8VfhpAb$UJcDtSJ,~>
-JcDYJ(B"(0!<)ros7`Y[qZ$!UqYL6Xs8VZnrr3B#s6BX_o`+pbs8DYBs0VfV~>
-JcDYJ(B"(0!<)ros7`Y[qZ$!UqYL6Xs8VZnrr3B#s6BX_o`+pbs8DYBs0VfV~>
-JcDYJ(B"(0!<)ros7`Y[qZ$!UqYL6Xs8VZnrr3B#s6BX_o`+pbs8DYBs0VfV~>
-JcD\K%IX/op'^Tks8W#X+WmHXrtN=!%3G$N!W_f!)%smerr?X.s8VM<s0M`U~>
-JcD\K%IX/op'^Tks8W#X+WmHXrtN=!%3G$N!W_f!)%smerr?X.s8VM<s0M`U~>
-JcD\K%IX/op'^Tks8W#X+WmHXrtN=!%3G$N!W_f!)%smerr?X.s8VM<s0M`U~>
-JcDYJ,Q.-4$NL2-q>^3hj8f&KruR9cs7cNns8OOQW&XD@!TX4FpOW@Ms*t~>
-JcDYJ,Q.-4$NL2-q>^3hj8f&KruR9cs7cNns8OOQW&XD@!TX4FpOW@Ms*t~>
-JcDYJ,Q.-4$NL2-q>^3hj8f&KruR9cs7cNns8OOQW&XD@!TX4FpOW@Ms*t~>
-JcD_Ls8)ouqYL9kruD$,-OBeQs7H]rs7lWg"8_usqu-TqrrE)qs8VkFs0M`U~>
-JcD_Ls8)ouqYL9kruD$,-OBeQs7H]rs7lWg"8_usqu-TqrrE)qs8VkFs0M`U~>
-JcD_Ls8)ouqYL9kruD$,-OBeQs7H]rs7lWg"8_usqu-TqrrE)qs8VkFs0M`U~>
-JcDYJ"8)We!<3!=p&>01rrDikrud@%s7-*ro`$&DYVGtR!<;cmqtksEZi>O~>
-JcDYJ"8)We!<3!=p&>01rrDikrud@%s7-*ro`$&DYVGtR!<;cmqtksEZi>O~>
-JcDYJ"8)We!<3!=p&>01rrDikrud@%s7-*ro`$&DYVGtR!<;cmqtksEZi>O~>
-JcD_L!:^!f,P)ZLnGi7\Sdtl&q!J+-,6\YYs.2mR[1!_Rs8EQ/s4mYSq18RQs*t~>
-JcD_L!:^!f,P)ZLnGi7\Sdtl&q!J+-,6\YYs.2mR[1!_Rs8EQ/s4mYSq18RQs*t~>
-JcD_L!:^!f,P)ZLnGi7\Sdtl&q!J+-,6\YYs.2mR[1!_Rs8EQ/s4mYSq18RQs*t~>
-JcD\K-1g[,p\"LbrVulsp%n^]s8Dorq#16Wq>^Kis8VinqXaR_s7Z&8s0M`U~>
-JcD\K-1g[,p\"LbrVulsp%n^]s8Dorq#16Wq>^Kis8VinqXaR_s7Z&8s0M`U~>
-JcD\K-1g[,p\"LbrVulsp%n^]s8Dorq#16Wq>^Kis8VinqXaR_s7Z&8s0M`U~>
-JcDYJ!W)WkrsSc"s8V-Zp&FjVs7H<j&F9Arq>('cq#C-gs8Vops6tU>Zi>O~>
-JcDYJ!W)WkrsSc"s8V-Zp&FjVs7H<j&F9Arq>('cq#C-gs8Vops6tU>Zi>O~>
-JcDYJ!W)WkrsSc"s8V-Zp&FjVs7H<j&F9Arq>('cq#C-gs8Vops6tU>Zi>O~>
-JcD_L$M+5sqYfmXqZ$6crr2ulrVlrmqYC-j!V?9hrsAT#s6]gbmf3=^JcE+WJ,~>
-JcD_L$M+5sqYfmXqZ$6crr2ulrVlrmqYC-j!V?9hrsAT#s6]gbmf3=^JcE+WJ,~>
-JcD_L$M+5sqYfmXqZ$6crr2ulrVlrmqYC-j!V?9hrsAT#s6]gbmf3=^JcE+WJ,~>
-JcD\K"o/#qq>^!aruLn7m/R(bq=s1Rs8Dorp&G'Ym/R%arr;`fs763eJcE+WJ,~>
-JcD\K"o/#qq>^!aruLn7m/R(bq=s1Rs8Dorp&G'Ym/R%arr;`fs763eJcE+WJ,~>
-JcD\K"o/#qq>^!aruLn7m/R(bq=s1Rs8Dorp&G'Ym/R%arr;`fs763eJcE+WJ,~>
-JcD_L%fZM.qu>XTqY1$gs8V`irVm,js8;oos6fmbrs/,plMpVYs87HJZi>O~>
-JcD_L%fZM.qu>XTqY1$gs8V`irVm,js8;oos6fmbrs/,plMpVYs87HJZi>O~>
-JcD_L%fZM.qu>XTqY1$gs8V`irVm,js8;oos6fmbrs/,plMpVYs87HJZi>O~>
-JcF^/s8Miorr1RMrr*T%rr;Kfp&G'is8MThs8)ces8;iprt"o)s7H?`s8VrqqZ$T_s8%<H[/YX~>
-JcE"Ts8Mlp'D2>)nGi1\rV-<hmJ["Yrq$-cqu6U+q#CBds7H?kqZ$Els6]j_JcE+WJ,~>
-JcFR+r;HTorr(LL'D)8(o)JIas82irnc/Xds7?9fr;Q^,q#CBds7H?kqZ$Els6]j_JcE+WJ,~>
-JcFa0"9.ujrql^8qY]s_oD\des8Vros82ioq>^?bs8Dipp%\Odp\t0mpAP!kpAY'pqXjgemJZt^
-rqQKtqt:!es8V]irt4l&qtL-crVuops8Vloqu?Wnrr3N*rVQWiq>^Kfs7ZK_s8VflrsJc*q#:<n
-rVufqq>UBonq$hrs*t~>
-JcF^/-2RZArquTks8W#sq>^-frql`qqYgHks8;]mqt:!fqu??arr;`lrrDckrrDclrWN#gs8VWc
-s!7UBpA+I[p%eISnG`.[rpg$fs7?9fp]($es8Vurs7lWks8Doqrt"u)qu?Hes8VclpAa[_s7ZHl
-$NC)#rr<#ss8;omrr2uhJcFO*J,~>
-JcFR++o([(qYU*gp\jUUq>('jqYgHks8;]mqt:!fqu??arr;`lrrDckrs8>urVcQas8VZ[ru:e-
-s8)cqo)8LdoDe^^s7lQms82irq#C6krVc`q&,Q8%s7lEis7QEcs7--hpAY(!rr;cms8W&tr;ZTl
-rrDV@s4mX)~>
-JcFa0"TJ;qq"t'j#5J&mo(2YUrr4)-s8Vops8;osp&G'equ?]ns7ZHlpAap[q>L!ds7,j_rs8Q%
-l21AUr;Q]prs\Z%s5X.Zqu?]fqu?TlrrN&mr;S>2s7lHis8Vcks75d]rVuZ`s8VNdqYL6dqt]g_
-mf3%Us8W#sr;Z]pr;ZQcJcFI(J,~>
-JcFX-$MjGqrVlKes82`nrVn22s8Vops8;osp&G'equ?]ns7ZHlpAap[q>L!ds6oRYrr;ormJ6br
-rp]jai;EECrVl?\rV6?krr<#rq#(.CkPt>Rrr<#krr;Q\s8Dumo)JaXrqcKkp\XdWs6fp]p](9k
-s8;ops8;olp4<7ts*t~>
-JcFX-2#I"ApA".Ns8)QfqY'^_mJm4^s8W#ss7QElq"t*kqu?Bhs7ZKfo_JIYs8VTZrVuoqs6f[^
-'DVV-kPtSZs8V`fs7uZmr;Zfpq#(.CkPt>Rrr<#krr;Q\s8Dumo)JaXrqcKkp\XdWs6fp]p](9k
-s8;ops8;olp4<7ts*t~>
-JcF^/!VlWms!%:=s8;]^q"t'br;ZZos8;ihs8D-\s7?9gmf2eVs7?-frr2p.n,MqVs8W)hq=ikG
-o_eXdrVm<*o]Yc=kj\96r:U!brr3u7s60L_p](-Ys8Vopp&Fpfs8VWgs8Drns8W&krVluqs8Vfl
-rsSDtnc/4Us6p!fmJd+b!W)M@s4mX)~>
-JcF^/#5S8urqQ'\rr2uprr3l1q>^?ls8;ihs8D-\s7?9gmf2eVs7?-frr2p+n,MqVrr2c`q=iqL
-q#(0lrr<!(rVQWjs8Vrqp\t3mrZ(_5kl:\Ws826as7u]fs82cps7-*grVlZns8DZirr`)ss7ZHl
-$hF>fs7?$cn,NFTrr2ouqY#L?h#Dm~>
-JcF^/-i<rBqXX"Hqu?]os8Vubo)8Ics8;ihs8D-\s7?9gmf2eVs7?-frr2p+n,MqVs8Vudq=inK
-p\=aqp@nUYrVuTkpAb-kruCk7kl:\Ws826as7u]fs82cps7-*grVlZns8DZirr`)ss7ZHl$hF>f
-s7?$cn,NFTrr2ouqY#L?h#Dm~>
-JcF[.!VZNlrs/Dkq""+Oq=FRb$iL&)qu?]ks7Gj]rr2p(qt'ddq>^KaqtpBm"7cEgr;Q`rrW<#s
-rqud,r9`56qsNb7p$i"Np\+Xdrr4#,p]($fs8DEdmJm4Js7#^]p%n^Qs7c9\s7H3foDS\#nFZ,J
-li6eQs7u]bs7QEjrIP"%s*t~>
-JcF[.#kIfhrqcHas8;lr!;6<j$iL&)qu?]ks7Gj]rr2p(qt'ddq>^KaqtpBm"S)NfqYp9is8E-!
-s8Vols!RC;qu?Nmr;Q`rme?bVrr;rcs6]jdjo=iCs7Q6gl2UMPp&F[]rq$*g&Ff>Zs6K^\o`+ae
-nc/:^rV_<Ig&HR~>
-JcF^/#Q=,apA"@Up\b%&o_SF_s8;osqu?]ks7Gj]rr2p(qt'ddq>^KaqtpBm"7cEfqtg?mrVZNn
-qW\"U$2aJon,<"\q>^EmruLP%s7lTnrU9dRs8V3\nFchSqZ#g[p\4@\o_\XZrVmGuo^2\Es7u<e
-q>^!bp&G!hJcFF'J,~>
-JcFR+/,]GFo^;/;mHsiOnc/OTrqcZpnbi1[s8V`[s7--hrVuoppAa^`q#16moD\amlMpkMq"aq%
-jRW?Jn*095s8V]kMYmAQpAP!j!Vu?drrW3"nGE4do_SIb"7-!equ6U1r:p<lqu6Wgs8W)dq#BOW
-nc/O^s71a@h#Dm~>
-JcF^/0E1hIqtL-jrVZ]qqtC'hm/R"OrqcZpnbi1[s8V`[s7--hrVuoppAa^`q#16moD\^llMpnP
-qYpKsrr)fnrr3#smf!.lr;HX+Q2gjapAP!j!Vu?drrW3"nGE4do_SIb"7-!equ6U1r:p<lqu6Wg
-s8W)dq#BOWnc/O^s71a@h#Dm~>
-JcFa0#6"Dhq"=7Wq>VW:l2CPJrqcZpnbi1[s8V`[s7--hrVuoppAa^`q#16moD\aolMpnMp\k!f
-q@iYtl1+B,o(MkOqYU*qOoPF_pAP!j!Vu?drrW3"nGE4do_SIb"7-!equ6U1r:p<lqu6Wgs8W)d
-q#BOWnc/O^s71a@h#Dm~>
-JcF^/!<)lr!WLRF!!)3]rrDurrrM]`rr4#<s8Dutmf34ZlNHGOs6]j\q#BUYs7lWis7u`pp\Fh:
-q#C@Fro2i8s8;Ef,i\_%s8W&rqu?]`q>^Ejr;Zfhp&":Zs8)chrVmf)s7QElr<Duqo(2nZs7Yj[
-s8;osq!nFbq>,[Bg])d~>
-JcF^/%JKYtr;ZfP!t,\D!se/krW)lqrrM]`rr48Cs8Dutmf34ZlNHGOs6]j\q#BUYs7ZHfs7u`q
-q#CBnrr)cmrr4;0!<;uds7b[S$o%#I!WW2urVQWpmJ6e\qu$Koo_&+Os8VrqpAP"0n,N(\s8</q
-s75d]r;ZKXs8W#ss7l-bs7uMBs4dR(~>
-JcFa0%f>bcp@eIbjUr[b*#TRdrr2urrr3#ip&=t6rr;rss6fpbpZhtGs8VKdp\=dQs8Vopq>^9k
-rU]p_q#0n7oBbi)!:St(pA!kE!\EX:"98E"rVQWpmJ6e\qu$Koo_&+Os8VrqpAP"0n,N(\s8</q
-s75d]r;ZKXs8W#ss7l-bs7uMBs4dR(~>
-JcFU,0_b/5nc0@A)Bf+Tr9a@cDZ@!m+M@Hb&2:6coEBRJi"l@nquD0F4octcq#GsYf`1pNpAY1]
-W>PR54o>:eK_YWI\-+Rmp[8T=!9F1Zr:g8On\HRa[/[?N$cW/?!/D$K!!!K.#^?;2!@ln'!.G1*
-rr<Q0s8VqHs4mX)~>
-JcF^/rr+VFoDeh(1,q3S$ig.jqZ^s<Z7@'1pV@CpXo@qrHO8UH!!)osIK)J2-f"LtIh:94rW)rt
-:&b+hr;6Bho)MJbs8Mi`li.3-NW0%Z\-+Rmp[8T=!9F1Zr:g8On\HRa[/[?N$cW/?!/D$K!!!K.
-#^?;2!@ln'!.G1*rr<Q0s8VqHs4mX)~>
-JcFa02#I(Aq""+Xs!^]E=&pLEs7#skDZ@!m+M@Hb&2:6coEBRJi"l@nquD0F4octcq#H![gAh-P
-;#C+ap@S"KoC(o*!)`gamdp/BqZ(>hrrW51">[:Wmga[EjT#5Wp]-<D_']f$s0*LO`W,Z4LCNMK
-!"Jr6GQ0c+.bst&IL"O*!"T)0s8%<Hh#Dm~>
-JcF[.2ZECKq<[JK.L[aH!:0I[r<;BU0s\bNs!GUg3rf-ZT>a@o"98$!iq39N_(,HbT#O%lrrrH"
-q#:FNb/tt+rt#2#*9R>",e^!,s8N(qcoh4)s!S!#ruo+n3:8Z;djG+p!2mk'rs\kr!oEtUs4Jao
-$(K:1p%o!ns8;bFs4mX)~>
-JcF^/>Q4Hjs8Vrq##$dG3s>E[rqlr_o.dPi0)m98Z9&$a!M@>%o`P6e#NGCUs1p2b!1^tmqYpa!
-r;ZfrrVZTjq=Xe^;?6Xln`TB@#Q,n7!WF@XUbDcJ!2\%)qu6UD"4mJq^;;kt28.Hcs8N(sa$K_6
-rUBsGs8DuN-MRn:cpdX)#QFc$qgne&s*t~>
-JcFa0"9&)fnGW@j,&^P'>p0+G8d4DL0s\bNs!GUg3rf-ZT>a@o"98$!iq39N_(,HbT>s:prr`/l
-qY'XToCMM>joALi!:9+@nG)h[pa#;0r?T(P0E;%PV9h@%rr4AKf)Ho-_Dps@^!e>.rrAt;62qAl
-nH.SIrVtOtp^*G:7J6N_rr;onJcFO*J,~>
-JcF[.s8FeI2Bi\4*tSl'!$3pLqZHll"98E-q[NT+#l+6"s7-Bf!rW''s8S?*'F=C6s7cTorrE&u
-:@eGbqi?2_*N[!.S.UX>YQ+V'mu;r"oaLK^)#rk,s7??l!!*$!s8N'!$j-G6"oo5(rsA_u!<3W!
-rr_upRj&+Ao+(g#!<;rsrVuZiJcFO*J,~>
-JcF[.2#dOP6T$>*7QNIq$SO\"qucum"98E-q[NT+#l+6"s7-Bf!rW''s8S?*'F=C6rq6?lrrE&u
-:@A,[mX]1l!+@fjAH6XYC#eq!ooF_*oaLK^)#rk,s7??l!!*$!s8N'!$j-G6"oo5(rsA_u!<3W!
-rr_upRj&+Ao+(g#!<;rsrVuZiJcFO*J,~>
-JcF^/J,T<Fs%GgJ0l:B,B.6DM5Q:icp&k?q#l>)3!!rAr"on,tp&b0l#QOgh*Y\nR!rr&ts8N)r
-qYK=Io]:=,@fT_&BjLdM@WUo.$1j12!qcuon/22j#ljMtrr<'!!<<'!!"8r/#6k/>nG`gpo`5"'
-n,EL`s-k2<"nN6(rs&Q(quH]qq"oXBh#Dm~>
-JcFa0s8G"LqtTIEjo>c2%3PZ6o_ACcs8Oghs6ot:](5n$quHQl!<3E%rrE)h(<PnCrr`<$&G#K)
-p@S:Zrr2urrr3'UdD@%'s#L/ZlQ-60*P8Bip\t6nrrE#os6g9on1)iYp\u8Ns8W#s!:g'jo`4@Y
-!<<!&eF3bD!<;cooCidipjrJ!s*t~>
-JcF[.%/9f%q#C@"1.sPq"o\H#F9)@@0u3hXs!bPMs8Vusq>LBo$Mj]%s7$lHli@%frr*K"oagch
-qtL*br9jFWp]+T!!;cB\jR2aKs61C$rZ/VP)#+%1s8N)tqZ$!js6qMcp%SJ,_Z0Z6rrDTh!qcQ[
-rrE)t#Lr5KrrE)n!V?$rp\9=>gAc[~>
-JcF[.!;QKl"XmMo?Y^nbs)\8@s"V=hn,FiJo)Jaf!;ZTorsJT%!<;R)am9$-"98B6o(<IanFlAF
-n+>`4mcXXa>la0Tna6&B!WD:("TKLSX;L^3!<<'!rVHQ_$30KEdIm86*Q%jVr;QcerrVinm/I(c
-r<LjA#lao)pAsm[&,5jMs4[L'~>
-JcFa0%K?5!q=jC<lMr4O$n;8VmO%o5s5q6=);P#=*Z!,hrrE)s%KE(ZnIYa#9)o8-e-,gO!;-;\
-7fE>erql]s15d%Is#^;]s.Bkp_uq1.s7u`frrDoqmf*:en%fhOjnlt9'A`We!<<'!s/\TW%dO'i
-!"/ej&HDb1s8ScZs7,o9s4mX)~>
-JcFR+2uW@J#"_6=9a1RopAY-mk/82Vh"]JB(=;FJ!<;s+s.D:?&HDc'!!s+a"TJB#o`(COs8N&t
-p\k"[q"Xn[#QOhpmHXZRs8A8fo>CbRci<hAo`"pfs6fmes6mc@&*<],*#%0,rrE*!!<9,fn.+a`
-:B1b&kRddo!<<(m6N?TOJcFO*J,~>
-JcFI("X7_q<c'&Zs)nDBk/82Vh"]JB(=;FJ!<;s+s.D:?&HDc'!!s+a"TSK%o`(=Jqtg0an+HDJ
-oB#6;7KDoIlgX<<!<)kb#kZ%<+4'u`!;-9kqZ$!`!<;N((_>a*`#KHHrVlltrrE)#6gtTNs%`V&
-!9b!orrE*!TgJeLq18S$s*t~>
-JcFa0#QFAjo^q\GmeZtdnb)_Drr4#6s5CEdk5Y1pr;Zfrs8W&ns8Vfiqu?]qp]'a_pAY'qrqu<d
-s8Drs#lXSrq>WSFp[.t[!qZ<arVlurs5WhOrt5),s763ir;Q`rqt-f`s8Vtprr35ls8ViZs8W)s
-rsJ>sqY'ses7H'cnq$hos*t~>
-JcF^/%/g/&rVHQo%0d"L$jHY1!:9^b*r,co[f>LipVm(1s8N&urV?KnpA=aes8McmnG`(Zrr4&=
-oDejerr)fdp&Fg[)=RUsrqH3Ys7c?cpAXpgj7`HO&c)J,o)Jafrr<#qoV_Tds8/bors/#ms7bjZ
-s8Mus$M+5npAb'jo_8CVJcFF'J,~>
-JcFI("X+p2/g_P:rrDKdruLn7iO8dKs7aM1s8W)us8Dcns7Z?es8W)ms7$'_rVm#tnGW7Wrq[Du
-m.'6,'ArECo_/(IqXaO[q>^<kj7`HO&c)J,o)Jafrr<#qoV_Tds8/bors/#ms7bjZs8Mus$M+5n
-pAb'jo_8CVJcFF'J,~>
-JcFa0*<,j2kPY2Iq![b4mdBW<k4elEo`+m`s8Vins6K[a"8DiqpAY(!fDkgArVu]cr;ZKhrs/E"
-r;QZcs8N#t$hO2lp%e7Tqu?]qo)/Lpp%\Ods7Q<ep\*bKrr`8ss8)`p)#F%)o`+XRs8VQ^s8W&r
-s8V?Vs8V?`qtKse!;;!Dg])d~>
-JcFX-#OMKip](0kq#14%med%Ro`+m`s8Vins6K[a"8DiqpAY(&fDkgArVu]cr;ZEgrr<#rrVm#k
-s8)`mrr3#jm/I"hrr)Wlq>^KorX/>nrr<#kr;66^k5PA_rqcZkrr3i3q"s^`p@&%]n+Zk^rVccr
-l1P)Vl2UYTqYpQhJcFL)J,~>
-JcFX-"7Z?jqYC.#nbiFVo`+m`s8Vins6K[a"8DiqpAY(!fDkgArVu]cr;ZHgrrDrqrt>8!qtL!a
-qXX:DjnAKEp%\7Wnc&CorVuQcrr<#kr;66^k5PA_rqcZkrr3i3q"s^`p@&%]n+Zk^rVccrl1P)V
-l2UYTqYpQhJcFL)J,~>
-JcFa0"TIKZrqHEl+S5*sp##99rr;lqnc/X]s7ZHls8Mues8W&os7--ds82cp!rN#mrr4>9lMpkT
-q>C0is7--dr;69hs82H]r;-Ejnbhn?s8W&rs7cQls8;lr#lF>qp@81_pZ_YV!;$3i!VH9grs88s
-r:p<lrr;Tgrs8MqrV?<is7uJAs4mX)~>
-JcF^/%-dflp\t3mp\=dgm/$_](]47&s8V`kpAY*lrr)Bes8Dfonc/Ldqu-O&qu?Hirqu9Ns8Vil
-rVluirqH?frt>80qu?]ks75ITs8Dorp](3lr;Q^%qtC'`nc/X`l2CV^oD\ajo_\Xf#k\/pq#CBn
-s7?3h#lF>oq>1-kq>#UAh#Dm~>
-JcF^/-ggs6p&"XbpA=mio)/Obqt^9^s8V`kpAY*lrr)Bes8Dfonc/Ldqu-O&qu?Hjs8DKQs8Vfj
-rVmQ$s7lHfp\Oa`p\"FVq>0UXmH+6Er<<5qs8Duqrr39$pAajVs8ViXrVllhrr3#kqu6U$o`+ja
-s8W)uoDS[pqtC!aqZ$Tkq18S$s*t~>
-JcFa0!ri,srr3r9s8W#qp\b$cq#CBXs8VEbs7uB_s8VZis7H6grsA5ls7c6emJm4Zrr2ugrr4PK
-n,)_Sr;Q]qs8)Qjs8V?`rr;umo_\IZq"FRas8)0`q#C0iq"jgdqu?]jrr3Don,NCenF6>Tnc/UW
-rVmK!s7cQgmJm(^s8W&ts6p!frIP"(s*t~>
-JcFU,*W5m-pAXmer;Zflo`"mSs8VEbs7uB_s8VZis7H6grsnSqs7c6emJm4Zs8N#brr2p!oDeX`
-rsSMfoDJRFr;-HnrV?Bk&cD\/qX4CYs7u]iqYC0gs8Vimrs\;`s8N&fnGE7Us8MKcrt4c#p]($U
-s82cps8Dutn,NFdJcFO*J,~>
-JcFX-*rYm+l0\38q=spbmca9>kl:\Ks8Vogp](9as8V`hrr3GtqZ$<`s6]jdp&G'jm/?qco`+aa
-rsSYpp](9Rs7uWjp[\:Z')25%s8)0`q#C0iq"jgdqu?]jrr3Don,NCenF6>Tnc/UWrVmK!s7cQg
-mJm(^s8W&ts6p!frIP"(s*t~>
-JcF^/"Sr&ns8Dor"8`&mr;Q^;qZ$Tjs8Vurs8;lgs8V`ks82cks8VNes7u]pqu?<frsAW%q#:3_
-r;-6frr2os!;HKm%/U#'oDJLMq!n+XrVZWo'Dqgus8W#ps7ZKis7cNmrqlQlqu6U4qu?WprqZEj
-s8;ogs8VTgpAb0ks7?9]pOWA!s*t~>
-JcF^/;YpFhrq-0fp](!fq>C9mrV?Knq#CBks8W#ro`+s`s8VupqZ$T`s8Vops82igrql]krV6Em
-p](6krr<#srV?<XrVHB\rr;ips7?9is8W&qrVmQ.s6p!fr;?Tgs82ijrr;upqZ$HlrttY5rVulm
-qZ$Tns7?9jnGi4^s8Duhs75o8s4dR(~>
-JcF^/3r]0RqWmbEme6/HoCVbJo(E%_q#CBks8W#ro`+s`s8VupqZ$T`s8Vops82igs8)]krV6Em
-q#CBnqYpQerr3E!s8DQdqWn%NqYgBjrVmQ.s6p!fr;?Tgs82ijrr;upqZ$HlrttY5rVulmqZ$Tn
-s7?9jnGi4^s8Duhs75o8s4dR(~>
-JcDeNr;Q<frVlfoJcDPGJ,~>
-JcFR+rVkLMs8MZj!WN&prdk*=s*t~>
-JcFU,!<)imrVc`m!<(%>qYc!FV#Pr~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-JcC<$JcE+WJ,~>
-%%EndData
-showpage
-%%Trailer
-end
-%%EOF
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index ab17dd50ca..4f678a2a1b 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2000</year><year>2010</year>
+ <year>2000</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -35,6 +35,153 @@
thus constitutes one section in this document. The title of each
section is the version number of Megaco.</p>
+
+ <section><title>Megaco 3.15.1</title>
+
+ <p>Version 3.15.1 supports code replacement in runtime from/to
+ version 3.15, 3.14.1.1, 3.14.1 and 3.14.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Updated the
+ <seealso marker="megaco_performance">performance</seealso>
+ chapter. </p>
+ <p>Own Id: OTP-8696</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Fixing miscellaneous things detected by dialyzer. </p>
+ <p>Own Id: OTP-9075</p>
+ <!-- <p>Aux Id: Seq 11579</p> -->
+ </item>
+
+ </list>
+
+ </section>
+
+ </section> <!-- 3.15.1 -->
+
+
+ <section><title>Megaco 3.15</title>
+
+ <section><title>Improvements and New Features</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Fixing auto-import issues.</p>
+ <p>Own Id: OTP-8842</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Eliminated a possible race condition while creating
+ pending counters. </p>
+ <p>Own Id: OTP-8634</p>
+ <p>Aux Id: Seq 11579</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ </section> <!-- 3.15 -->
+
+
+ <section>
+ <title>Megaco 3.14.1.1</title>
+
+ <p>Version 3.14.1.1 supports code replacement in runtime from/to
+ version 3.14.1, 3.14, 3.13, 3.12 and 3.11.3.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Updated the
+ <seealso marker="megaco_performance">performance</seealso>
+ chapter. </p>
+ <p>Own Id: OTP-8696</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>A race condition when, during high load, processing
+ both the original and a resent message and delivering
+ this as two separate messages to the user. </p>
+ <p>Note that this solution only protects against multiple
+ reply deliveries! </p>
+ <p>Own Id: OTP-8529</p>
+ <p>Aux Id: Seq 10915</p>
+ </item>
+
+ <item>
+ <p>Fix shared libraries installation. </p>
+ <p>The flex shared lib(s) were incorrectly installed as data
+ files. </p>
+ <p>Peter Lemenkov</p>
+ <p>Own Id: OTP-8627</p>
+ </item>
+
+ <item>
+ <p>Eliminated a possible race condition while creating
+ pending counters. </p>
+ <p>Own Id: OTP-8634</p>
+ <p>Aux Id: Seq 11579</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ </section> <!-- 3.14.1.1 -->
+
+
<section>
<title>Megaco 3.14.1</title>
@@ -66,6 +213,16 @@
<list type="bulleted">
<item>
+ <p>A race condition when, during high load, processing
+ both the original and a resent message and delivering
+ this as two separate messages to the user. </p>
+ <p>Note that this solution only protects against multiple
+ reply deliveries! </p>
+ <p>Own Id: OTP-8529</p>
+ <p>Aux Id: Seq 10915</p>
+ </item>
+
+ <item>
<p>Fix shared libraries installation. </p>
<p>The flex shared lib(s) were incorrectly installed as data
files. </p>
@@ -73,6 +230,13 @@
<p>Own Id: OTP-8627</p>
</item>
+ <item>
+ <p>Eliminated a possible race condition while creating
+ pending counters. </p>
+ <p>Own Id: OTP-8634</p>
+ <p>Aux Id: Seq 11579</p>
+ </item>
+
</list>
</section>
@@ -125,7 +289,7 @@
<item>
<p>Callbacks, when the callback module is unknown (undefined),
results in warning messages. </p>
- <p>A raise condition scenario. As part of a cancelation operation,
+ <p>A race condition scenario. As part of a cancelation operation,
replies with waiting acknowledgements is cancelled. This includes
informing the user (via a call to the handle_trans_ack callback
function). It is possible that at this point the connection data
@@ -657,13 +821,16 @@
<list type="bulleted">
<item>
- <p>Unexpected <seealso marker="megaco_user#unexpected_trans">handle_unexpected_reply</seealso> callbacks. </p>
- <p>The <seealso marker="megaco_user">megaco_user</seealso> callback function
+ <p>Unexpected
+ <seealso marker="megaco_user#unexpected_trans">handle_unexpected_reply</seealso>
+ callbacks. </p>
+ <p>The <seealso marker="megaco_user">megaco_user</seealso> callback
+ function
<seealso marker="megaco_user#unexpected_trans">handle_unexpected_reply</seealso>
could during high load be called with unexpected values for the Trans
- argument, such as an <c>TransactionReply</c> where <c>transactionResult</c>
- had the value <c>{error, timeout}</c>. This was a result of a raise condition
- and has now been fixed. </p>
+ argument, such as an <c>TransactionReply</c> where
+ <c>transactionResult</c> had the value <c>{error, timeout}</c>.
+ This was a result of a race condition and has now been fixed. </p>
<p>Own Id: OTP-7926</p>
<p>Aux Id: Seq 11255</p>
</item>
@@ -858,416 +1025,6 @@
</section>
</section> <!-- 3.10 -->
-
- <section>
- <title>Megaco 3.9.4</title>
-
- <p>Version 3.9.4 supports code replacement in runtime from/to
- version 3.9.3, 3.9.2, 3.9.1.1, 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
- when using any of the drivers (flex for text or asn1 for binary).</p>
-
- <section>
- <title>Improvements and new features</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>Miscellaneous dialyzer related and test case cleanup. </p>
- <p>Own Id: OTP-7614</p>
- </item>
-
- </list>
--->
- </section>
-
- <section>
- <title>Fixed bugs and malfunctions</title>
-<!--
- <p>-</p>
--->
-
- <list type="bulleted">
- <item>
- <p>Segmenting a reply failed (with a badmatch) if the message
- did not actually need to be segmented (e.g. was within the
- size limit,
- <seealso marker="megaco#ui_max_pdu_size">max_pdu_size</seealso>). </p>
- <p>Own Id: OTP-7733</p>
- <p>Aux Id: Seq 11168</p>
- </item>
-
- <item>
- <p>Improve the error handling of megaco_tcp for received
- messages. </p>
- <p>Own Id: OTP-7728</p>
- </item>
-
- </list>
-
- </section>
-
- <section>
- <title>Incompatibilities</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>For those implementing their own codec's, the new megaco_encoder
- behaviour will require three more functions. See above for more
- info. </p>
- <p>Own Id: OTP-7168</p>
- <p>Aux Id: Seq 10867</p>
- </item>
-
- </list>
--->
-
- </section>
- </section> <!-- 3.9.3.1 -->
-
-
- <section>
- <title>Megaco 3.9.3</title>
-
- <p>Version 3.9.3 supports code replacement in runtime from/to
- version 3.9.2, 3.9.1.1, 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
- when using any of the drivers (flex for text or asn1 for binary).</p>
-
- <section>
- <title>Improvements and new features</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>Miscellaneous dialyzer related and test case cleanup. </p>
- <p>Own Id: OTP-7614</p>
- </item>
-
- </list>
--->
- </section>
-
- <section>
- <title>Fixed bugs and malfunctions</title>
-<!--
- <p>-</p>
--->
-
- <list type="bulleted">
- <item>
- <p>Memory leak in the flex scanner. There was a memory
- leak in the flex scanner function handling
- Property Parameters. </p>
- <p>Own Id: OTP-7700</p>
- <p>Aux Id: Seq 11126</p>
- </item>
-
- </list>
-
- </section>
-
- <section>
- <title>Incompatibilities</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>For those implementing their own codec's, the new megaco_encoder
- behaviour will require three more functions. See above for more
- info. </p>
- <p>Own Id: OTP-7168</p>
- <p>Aux Id: Seq 10867</p>
- </item>
-
- </list>
--->
-
- </section>
- </section> <!-- 3.9.3 -->
-
-
- <section>
- <title>Megaco 3.9.2</title>
-
- <p>Version 3.9.2 supports code replacement in runtime from/to
- version 3.9.1.1, 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
- when using any of the drivers (flex for text or asn1 for binary).</p>
-
- <section>
- <title>Improvements and new features</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>Miscellaneous dialyzer related and test case cleanup. </p>
- <p>Own Id: OTP-7614</p>
- </item>
-
- </list>
--->
- </section>
-
- <section>
- <title>Fixed bugs and malfunctions</title>
-<!--
- <p>-</p>
--->
-
- <list type="bulleted">
- <item>
- <p>The text encoders (v1, v2, v3, ...) all failed to
- properly encode the DigitMapDescriptor. </p>
- <p>Own Id: OTP-7671</p>
- <p>Aux Id: Seq 11113</p>
- </item>
-
- <item>
- <p>The mini decoder some time incorrectly identifies
- plain text as tokens. </p>
- <p>Own Id: OTP-7672</p>
- <p>Aux Id: Seq 11103</p>
- </item>
-
- </list>
-
- </section>
-
- <section>
- <title>Incompatibilities</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>For those implementing their own codec's, the new megaco_encoder
- behaviour will require three more functions. See above for more
- info. </p>
- <p>Own Id: OTP-7168</p>
- <p>Aux Id: Seq 10867</p>
- </item>
-
- </list>
--->
-
- </section>
- </section> <!-- 3.9.2 -->
-
-
- <section>
- <title>Megaco 3.9.1.1</title>
-
- <p>Version 3.9.1.1 supports code replacement in runtime from/to
- version 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
- when using any of the drivers (flex for text or asn1 for binary).</p>
-
- <section>
- <title>Improvements and new features</title>
-<!--
- <p>-</p>
--->
-
- <list type="bulleted">
- <item>
- <p>Miscellaneous dialyzer related and test case cleanup. </p>
- <p>Own Id: OTP-7614</p>
- </item>
-
- </list>
- </section>
-
- <section>
- <title>Fixed bugs and malfunctions</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>[text] The flex scanner did not allow an empty quotedString
- in propertyParm. </p>
- <p>Own Id: OTP-7573</p>
- <p>Aux Id: Seq 11062</p>
- </item>
-
- <item>
- <p>[text] Unable to decode a version 2 message with a
- topologyTriple containing an (optional) eventStream. </p>
- <p>Own Id: OTP-7576</p>
- <p>Aux Id: Seq 11066</p>
- </item>
-
- </list>
--->
-
- </section>
-
- <section>
- <title>Incompatibilities</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>For those implementing their own codec's, the new megaco_encoder
- behaviour will require three more functions. See above for more
- info. </p>
- <p>Own Id: OTP-7168</p>
- <p>Aux Id: Seq 10867</p>
- </item>
-
- </list>
--->
-
- </section>
- </section> <!-- 3.9.1.1 -->
-
-
- <section>
- <title>Megaco 3.9.1</title>
-
- <p>Version 3.9.1 supports code replacement in runtime from/to
- version 3.9, 3.8.2, 3.8.1 and 3.8 except
- when using any of the drivers (flex for text or asn1 for binary).</p>
-
- <section>
- <title>Improvements and new features</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>[text] The text codec(s) has been optimized. The parsing of
- "property parameters" has been moved to the scanner(s). Which means
- that when decoding messages containing property parameters, using
- the flex scanner, decode time(s) will be reduced. The reduction
- depends on the message, but can be as large as 25%. </p>
- <p>Own Id: OTP-7431</p>
- </item>
-
- </list>
--->
- </section>
-
- <section>
- <title>Fixed bugs and malfunctions</title>
-<!--
- <p>-</p>
--->
-
- <list type="bulleted">
- <item>
- <p>[text] The flex scanner did not allow an empty quotedString
- in propertyParm. </p>
- <p>Own Id: OTP-7573</p>
- <p>Aux Id: Seq 11062</p>
- </item>
-
- <item>
- <p>[text] Unable to decode a version 2 message with a
- topologyTriple containing an (optional) eventStream. </p>
- <p>Own Id: OTP-7576</p>
- <p>Aux Id: Seq 11066</p>
- </item>
-
- </list>
-
- </section>
-
- <section>
- <title>Incompatibilities</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>For those implementing their own codec's, the new megaco_encoder
- behaviour will require three more functions. See above for more
- info. </p>
- <p>Own Id: OTP-7168</p>
- <p>Aux Id: Seq 10867</p>
- </item>
-
- </list>
--->
-
- </section>
- </section> <!-- 3.9.1 -->
-
-
- <section>
- <title>Megaco 3.9</title>
-
- <p>Version 3.9 supports code replacement in runtime from/to
- version 3.8.2, 3.8.1 and 3.8 except
- when using any of the drivers (flex for text or asn1 for binary).</p>
-
- <section>
- <title>Improvements and new features</title>
-<!--
- <p>-</p>
--->
-
- <list type="bulleted">
- <item>
- <p>[text] The text codec(s) has been optimized. The parsing of
- "property parameters" has been moved to the scanner(s). Which means
- that when decoding messages containing property parameters, using
- the flex scanner, decode time(s) will be reduced. The reduction
- depends on the message, but can be as large as 25%. </p>
- <p>Own Id: OTP-7431</p>
- </item>
-
- </list>
- </section>
-
- <section>
- <title>Fixed bugs and malfunctions</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>If a TransactionRequest arrives while a user is
- connecting (is in the callback function
- handle_connect as a result of a megaco:connect call),
- megaco responds with a pending message and then drops
- the request.</p>
- <p>These messages will now be silently dropped, forcing the
- other side to resend. </p>
- <p>Own Id: OTP-7192</p>
- <p>Aux Id: Seq 10884</p>
- </item>
-
- </list>
--->
-
- </section>
-
- <section>
- <title>Incompatibilities</title>
- <p>-</p>
-
-<!--
- <list type="bulleted">
- <item>
- <p>For those implementing their own codec's, the new megaco_encoder
- behaviour will require three more functions. See above for more
- info. </p>
- <p>Own Id: OTP-7168</p>
- <p>Aux Id: Seq 10867</p>
- </item>
-
- </list>
--->
-
- </section>
- </section> <!-- 3.9 -->
-
-
<!-- section>
<title>Release notes history</title>
<p>For information about older versions see
diff --git a/lib/megaco/doc/src/notes_history.xml b/lib/megaco/doc/src/notes_history.xml
index 640b62230f..220ed4bbb1 100644
--- a/lib/megaco/doc/src/notes_history.xml
+++ b/lib/megaco/doc/src/notes_history.xml
@@ -33,6 +33,415 @@
</header>
<section>
+ <title>Megaco 3.9.4</title>
+
+ <p>Version 3.9.4 supports code replacement in runtime from/to
+ version 3.9.3, 3.9.2, 3.9.1.1, 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
+ when using any of the drivers (flex for text or asn1 for binary).</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Miscellaneous dialyzer related and test case cleanup. </p>
+ <p>Own Id: OTP-7614</p>
+ </item>
+
+ </list>
+-->
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Segmenting a reply failed (with a badmatch) if the message
+ did not actually need to be segmented (e.g. was within the
+ size limit,
+ <seealso marker="megaco#ui_max_pdu_size">max_pdu_size</seealso>). </p>
+ <p>Own Id: OTP-7733</p>
+ <p>Aux Id: Seq 11168</p>
+ </item>
+
+ <item>
+ <p>Improve the error handling of megaco_tcp for received
+ messages. </p>
+ <p>Own Id: OTP-7728</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>For those implementing their own codec's, the new megaco_encoder
+ behaviour will require three more functions. See above for more
+ info. </p>
+ <p>Own Id: OTP-7168</p>
+ <p>Aux Id: Seq 10867</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+ </section> <!-- 3.9.3.1 -->
+
+
+ <section>
+ <title>Megaco 3.9.3</title>
+
+ <p>Version 3.9.3 supports code replacement in runtime from/to
+ version 3.9.2, 3.9.1.1, 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
+ when using any of the drivers (flex for text or asn1 for binary).</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Miscellaneous dialyzer related and test case cleanup. </p>
+ <p>Own Id: OTP-7614</p>
+ </item>
+
+ </list>
+-->
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Memory leak in the flex scanner. There was a memory
+ leak in the flex scanner function handling
+ Property Parameters. </p>
+ <p>Own Id: OTP-7700</p>
+ <p>Aux Id: Seq 11126</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>For those implementing their own codec's, the new megaco_encoder
+ behaviour will require three more functions. See above for more
+ info. </p>
+ <p>Own Id: OTP-7168</p>
+ <p>Aux Id: Seq 10867</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+ </section> <!-- 3.9.3 -->
+
+
+ <section>
+ <title>Megaco 3.9.2</title>
+
+ <p>Version 3.9.2 supports code replacement in runtime from/to
+ version 3.9.1.1, 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
+ when using any of the drivers (flex for text or asn1 for binary).</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>Miscellaneous dialyzer related and test case cleanup. </p>
+ <p>Own Id: OTP-7614</p>
+ </item>
+
+ </list>
+-->
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>The text encoders (v1, v2, v3, ...) all failed to
+ properly encode the DigitMapDescriptor. </p>
+ <p>Own Id: OTP-7671</p>
+ <p>Aux Id: Seq 11113</p>
+ </item>
+
+ <item>
+ <p>The mini decoder some time incorrectly identifies
+ plain text as tokens. </p>
+ <p>Own Id: OTP-7672</p>
+ <p>Aux Id: Seq 11103</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>For those implementing their own codec's, the new megaco_encoder
+ behaviour will require three more functions. See above for more
+ info. </p>
+ <p>Own Id: OTP-7168</p>
+ <p>Aux Id: Seq 10867</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+ </section> <!-- 3.9.2 -->
+
+
+ <section>
+ <title>Megaco 3.9.1.1</title>
+
+ <p>Version 3.9.1.1 supports code replacement in runtime from/to
+ version 3.9.1, 3.9, 3.8.2, 3.8.1 and 3.8 except
+ when using any of the drivers (flex for text or asn1 for binary).</p>
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>Miscellaneous dialyzer related and test case cleanup. </p>
+ <p>Own Id: OTP-7614</p>
+ </item>
+
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>[text] The flex scanner did not allow an empty quotedString
+ in propertyParm. </p>
+ <p>Own Id: OTP-7573</p>
+ <p>Aux Id: Seq 11062</p>
+ </item>
+
+ <item>
+ <p>[text] Unable to decode a version 2 message with a
+ topologyTriple containing an (optional) eventStream. </p>
+ <p>Own Id: OTP-7576</p>
+ <p>Aux Id: Seq 11066</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>For those implementing their own codec's, the new megaco_encoder
+ behaviour will require three more functions. See above for more
+ info. </p>
+ <p>Own Id: OTP-7168</p>
+ <p>Aux Id: Seq 10867</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+ </section> <!-- 3.9.1.1 -->
+
+
+ <section>
+ <title>Megaco 3.9.1</title>
+
+ <p>Version 3.9.1 supports code replacement in runtime from/to
+ version 3.9, 3.8.2, 3.8.1 and 3.8 except
+ when using any of the drivers (flex for text or asn1 for binary).</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>[text] The text codec(s) has been optimized. The parsing of
+ "property parameters" has been moved to the scanner(s). Which means
+ that when decoding messages containing property parameters, using
+ the flex scanner, decode time(s) will be reduced. The reduction
+ depends on the message, but can be as large as 25%. </p>
+ <p>Own Id: OTP-7431</p>
+ </item>
+
+ </list>
+-->
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[text] The flex scanner did not allow an empty quotedString
+ in propertyParm. </p>
+ <p>Own Id: OTP-7573</p>
+ <p>Aux Id: Seq 11062</p>
+ </item>
+
+ <item>
+ <p>[text] Unable to decode a version 2 message with a
+ topologyTriple containing an (optional) eventStream. </p>
+ <p>Own Id: OTP-7576</p>
+ <p>Aux Id: Seq 11066</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>For those implementing their own codec's, the new megaco_encoder
+ behaviour will require three more functions. See above for more
+ info. </p>
+ <p>Own Id: OTP-7168</p>
+ <p>Aux Id: Seq 10867</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+ </section> <!-- 3.9.1 -->
+
+
+ <section>
+ <title>Megaco 3.9</title>
+
+ <p>Version 3.9 supports code replacement in runtime from/to
+ version 3.8.2, 3.8.1 and 3.8 except
+ when using any of the drivers (flex for text or asn1 for binary).</p>
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+
+ <list type="bulleted">
+ <item>
+ <p>[text] The text codec(s) has been optimized. The parsing of
+ "property parameters" has been moved to the scanner(s). Which means
+ that when decoding messages containing property parameters, using
+ the flex scanner, decode time(s) will be reduced. The reduction
+ depends on the message, but can be as large as 25%. </p>
+ <p>Own Id: OTP-7431</p>
+ </item>
+
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed bugs and malfunctions</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>If a TransactionRequest arrives while a user is
+ connecting (is in the callback function
+ handle_connect as a result of a megaco:connect call),
+ megaco responds with a pending message and then drops
+ the request.</p>
+ <p>These messages will now be silently dropped, forcing the
+ other side to resend. </p>
+ <p>Own Id: OTP-7192</p>
+ <p>Aux Id: Seq 10884</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+
+<!--
+ <list type="bulleted">
+ <item>
+ <p>For those implementing their own codec's, the new megaco_encoder
+ behaviour will require three more functions. See above for more
+ info. </p>
+ <p>Own Id: OTP-7168</p>
+ <p>Aux Id: Seq 10867</p>
+ </item>
+
+ </list>
+-->
+
+ </section>
+ </section> <!-- 3.9 -->
+
+
+ <section>
<title>Megaco 3.8.2</title>
<p>Version 3.8.2 supports code replacement in runtime from/to
@@ -901,7 +1310,7 @@
<list>
<item>
<p>When timers expire while a connection cancel
- (megaco:cancel) is in progress, there is a raise
+ (megaco:cancel) is in progress, there is a race
condition possibility. This has been eliminated. </p>
<p>Own Id: OTP-6921</p>
<p>Aux Id: Seq 10450</p>
@@ -1166,7 +1575,7 @@
<list type="bulleted">
<item>
<p>When replies arrive during a call to megaco:cancel
- there is a raise condition possibility. This has been
+ there is a race condition possibility. This has been
eliminated. </p>
<p>Own Id: OTP-6276</p>
<p>Aux Id: Seq 10450</p>
diff --git a/lib/megaco/examples/meas/megaco_codec_meas.erl b/lib/megaco/examples/meas/megaco_codec_meas.erl
index 88b34105ac..51ee396338 100644
--- a/lib/megaco/examples/meas/megaco_codec_meas.erl
+++ b/lib/megaco/examples/meas/megaco_codec_meas.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,6 +43,8 @@
%% API
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([start/0, start/1]).
-export([start1/0]).
diff --git a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl
index 31df945777..040af9826b 100644
--- a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl
+++ b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,8 @@
%% API
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([start_flex_scanner/0, stop_flex_scanner/1,
expanded_messages/2, expanded_messages/3,
set_default_sched_bind/0,
diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src
index 503fcd7176..c0d8218ac8 100644
--- a/lib/megaco/src/app/megaco.app.src
+++ b/lib/megaco/src/app/megaco.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -67,6 +67,7 @@
megaco_compact_text_encoder_prev3c,
megaco_compact_text_encoder_v3,
megaco_config,
+ megaco_config_misc,
megaco_digit_map,
megaco_encoder,
megaco_edist_compress,
diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src
index 5df31f2923..01b070d79f 100644
--- a/lib/megaco/src/app/megaco.appup.src
+++ b/lib/megaco/src/app/megaco.appup.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -127,86 +127,40 @@
%% |
%% v
%% 3.14.1
+%% |
+%% v
+%% 3.14.1.1
+%% |
+%% v
+%% 3.15
+%% |
+%% v
+%% 3.15.1
%%
%%
{"%VSN%",
[
- {"3.14",
- [
- {update, megaco_config, soft, soft_purge, soft_purge, []}
- ]
- },
- {"3.13",
- [
- {load_module, megaco_messenger, soft_purge, soft_purge, []},
- {load_module, megaco_filter, soft_purge, soft_purge, []},
- {update, megaco_config, soft, soft_purge, soft_purge, []},
- {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_13_1},
- soft_purge, soft_purge, []}
- ]
- },
- {"3.12",
- [
- {load_module, megaco_filter, soft_purge, soft_purge, []},
- {load_module, megaco_udp, soft_purge, soft_purge, []},
- {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]},
- {update, megaco_config, soft, soft_purge, soft_purge, []},
- {update, megaco_monitor, soft, soft_purge, soft_purge, []},
- {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_13_1},
- soft_purge, soft_purge, []}
- ]
- },
- {"3.11.3",
+ {"3.15",
[
- {load_module, megaco_filter, soft_purge, soft_purge, []},
- {load_module, megaco_udp, soft_purge, soft_purge, []},
- {load_module, megaco_messenger, soft_purge, soft_purge,
- [megaco_config, megaco_monitor]},
- {update, megaco_monitor, soft, soft_purge, soft_purge, []},
- {update, megaco_config, {advanced, upgrade_from_pre_3_12},
- soft_purge, soft_purge, []},
- {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_13_1},
- soft_purge, soft_purge, []}
+ {load_module, megaco_flex_scanner, soft_purge, soft_purge, []},
+ {load_module, megaco_sdp, soft_purge, soft_purge, []},
+ {load_module, megaco_filter, soft_purge, soft_purge, []},
+ {load_module, megaco_timer, soft_purge, soft_purge, [megaco_config_misc]},
+ {update, megaco_config, soft, soft_purge, soft_purge,
+ [megaco_timer, megaco_config_misc]},
+ {add_module, megaco_config_misc}
]
}
],
[
- {"3.14",
- [
- {update, megaco_config, soft, soft_purge, soft_purge, []}
- ]
- },
- {"3.13",
- [
- {load_module, megaco_messenger, soft_purge, soft_purge, []},
- {load_module, megaco_filter, soft_purge, soft_purge, []},
- {update, megaco_config, soft, soft_purge, soft_purge, []},
- {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_13_1},
- soft_purge, soft_purge, []}
- ]
- },
- {"3.12",
- [
- {load_module, megaco_filter, soft_purge, soft_purge, []},
- {load_module, megaco_udp, soft_purge, soft_purge, []},
- {load_module, megaco_messenger, soft_purge, soft_purge, [megaco_monitor]},
- {update, megaco_config, soft, soft_purge, soft_purge, []},
- {update, megaco_monitor, soft, soft_purge, soft_purge, []},
- {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_13_1},
- soft_purge, soft_purge, []}
- ]
- },
- {"3.11.3",
+ {"3.15",
[
- {load_module, megaco_filter, soft_purge, soft_purge, []},
- {load_module, megaco_udp, soft_purge, soft_purge, []},
- {load_module, megaco_messenger, soft_purge, soft_purge,
- [megaco_config, megaco_monitor]},
- {update, megaco_monitor, soft, soft_purge, soft_purge, []},
- {update, megaco_config, {advanced, downgrade_to_pre_3_12},
- soft_purge, soft_purge, []},
- {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_13_1},
- soft_purge, soft_purge, []}
+ {load_module, megaco_flex_scanner, soft_purge, soft_purge, []},
+ {load_module, megaco_sdp, soft_purge, soft_purge, []},
+ {load_module, megaco_filter, soft_purge, soft_purge, []},
+ {load_module, megaco_timer, soft_purge, soft_purge, [megaco_config]},
+ {update, megaco_config, soft, soft_purge, soft_purge, []},
+ {remove, {megaco_config_misc, soft_purge, brutal_purge}}
]
}
]
diff --git a/lib/megaco/src/app/megaco_internal.hrl b/lib/megaco/src/app/megaco_internal.hrl
index adbaacacef..2c124e9060 100644
--- a/lib/megaco/src/app/megaco_internal.hrl
+++ b/lib/megaco/src/app/megaco_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -139,6 +139,22 @@
[?APPLICATION, ?MODULE, self()|A]))).
+-define(megaco_ereport(Label, Report),
+ ?megaco_report(error_report, Label, Report)).
+
+-define(megaco_wreport(Label, Report),
+ ?megaco_report(warning_report, Label, Report)).
+
+-define(megaco_ireport(Label, Report),
+ ?megaco_report(info_report, Label, Report)).
+
+-define(megaco_report(Func, Label, Report),
+ (catch error_logger:Func([{label, Label},
+ {application, ?APPLICATION},
+ {module, ?MODULE},
+ {process, self()} | Report]))).
+
+
%%%----------------------------------------------------------------------
%%% Default (ignore) value of the Extra argument to the
%%% megaco:receive_message/5 and process_received_message functions/5.
diff --git a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
index 842d6b70d1..967ee93935 100644
--- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
+++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -134,6 +134,12 @@ encode_transaction(EC, {Tag, _} = Trans, AsnMod, TransMod, Type)
encode_transaction(_EC, T, _AsnMod, _TransMod, _Type) ->
{error, {no_megaco_transaction, T}}.
+-spec do_encode_transaction(EC :: list(),
+ Trans :: tuple(),
+ AnsMod :: atom(),
+ TransMod :: atom(),
+ Type :: atom()) ->
+ {'ok', binary()} | {'error', any()}.
do_encode_transaction([native], _Trans, _AsnMod, _TransMod, binary) ->
%% asn1rt:encode(AsnMod, element(1, T), T);
{error, not_implemented};
@@ -160,6 +166,12 @@ do_encode_transaction(EC, _Trans, _AsnMod, _TransMod, _Type) ->
%% Convert a list of ActionRequest record's into a binary
%% Return {ok, DeepIoList} | {error, Reason}
%%----------------------------------------------------------------------
+-spec encode_action_requests(EC :: list(),
+ ARs :: list(),
+ AnsMod :: atom(),
+ TransMod :: atom(),
+ Type :: atom()) ->
+ {'ok', binary()} | {'error', any()}.
encode_action_requests([native], _ARs, _AsnMod, _TransMod, binary) ->
%% asn1rt:encode(AsnMod, element(1, T), T);
{error, not_implemented};
@@ -183,13 +195,20 @@ encode_action_requests(EC, _ARs, _AsnMod, _TransMod, _Type) ->
%% Convert a ActionRequest record into a binary
%% Return {ok, DeepIoList} | {error, Reason}
%%----------------------------------------------------------------------
-encode_action_request([native], _ARs, _AsnMod, _TransMod, binary) ->
+
+-spec encode_action_request(EC :: list(),
+ AR :: tuple(),
+ AnsMod :: atom(),
+ TransMod :: atom(),
+ Type :: atom()) ->
+ {'ok', binary()} | {'error', any()}.
+encode_action_request([native], _AR, _AsnMod, _TransMod, binary) ->
%% asn1rt:encode(AsnMod, element(1, T), T);
{error, not_implemented};
-encode_action_request(_EC, _ARs0, _AsnMod, _TransMod, binary) ->
+encode_action_request(_EC, _AR, _AsnMod, _TransMod, binary) ->
{error, not_implemented};
-encode_action_request(EC, ARs, AsnMod, TransMod, io_list) ->
- case encode_action_request(EC, ARs, AsnMod, TransMod, binary) of
+encode_action_request(EC, AR, AsnMod, TransMod, io_list) ->
+ case encode_action_request(EC, AR, AsnMod, TransMod, binary) of
{ok, Bin} when is_binary(Bin) ->
{ok, Bin};
{ok, DeepIoList} ->
@@ -198,7 +217,7 @@ encode_action_request(EC, ARs, AsnMod, TransMod, io_list) ->
{error, Reason} ->
{error, Reason}
end;
-encode_action_request(EC, _ARs, _AsnMod, _TransMod, _Type) ->
+encode_action_request(EC, _AR, _AsnMod, _TransMod, _Type) ->
{error, {bad_encoding_config, EC}}.
diff --git a/lib/megaco/src/binary/megaco_binary_transformer_prev3a.erl b/lib/megaco/src/binary/megaco_binary_transformer_prev3a.erl
index 609947c933..ba4324f4f2 100644
--- a/lib/megaco/src/binary/megaco_binary_transformer_prev3a.erl
+++ b/lib/megaco/src/binary/megaco_binary_transformer_prev3a.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1619,11 +1619,3 @@ verify_count(Count, Min, Max) ->
true ->
error({count_not_an_integer, Count})
end.
-
-
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
-
-
diff --git a/lib/megaco/src/binary/megaco_binary_transformer_prev3b.erl b/lib/megaco/src/binary/megaco_binary_transformer_prev3b.erl
index baca84bbfe..8e42353b9b 100644
--- a/lib/megaco/src/binary/megaco_binary_transformer_prev3b.erl
+++ b/lib/megaco/src/binary/megaco_binary_transformer_prev3b.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1619,11 +1619,3 @@ verify_count(Count, Min, Max) ->
true ->
error({count_not_an_integer, Count})
end.
-
-
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
-
-
diff --git a/lib/megaco/src/binary/megaco_binary_transformer_prev3c.erl b/lib/megaco/src/binary/megaco_binary_transformer_prev3c.erl
index 8d2f9eea38..c26d1fa99d 100644
--- a/lib/megaco/src/binary/megaco_binary_transformer_prev3c.erl
+++ b/lib/megaco/src/binary/megaco_binary_transformer_prev3c.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1746,11 +1746,3 @@ verify_count(Count, Min, Max) ->
true ->
error({count_not_an_integer, Count})
end.
-
-
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
-
-
diff --git a/lib/megaco/src/binary/megaco_binary_transformer_v3.erl b/lib/megaco/src/binary/megaco_binary_transformer_v3.erl
index cef49b03fd..1ff7c86e82 100644
--- a/lib/megaco/src/binary/megaco_binary_transformer_v3.erl
+++ b/lib/megaco/src/binary/megaco_binary_transformer_v3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1753,11 +1753,3 @@ verify_count(Count, Min, Max) ->
true ->
error({count_not_an_integer, Count})
end.
-
-
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
-
-
diff --git a/lib/megaco/src/engine/depend.mk b/lib/megaco/src/engine/depend.mk
index 8d8c83e923..935eb813e5 100644
--- a/lib/megaco/src/engine/depend.mk
+++ b/lib/megaco/src/engine/depend.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -17,6 +17,8 @@
#
# %CopyrightEnd%
+$(EBIN)/megaco_config_misc.$(EMULATOR): megaco_config_misc.erl
+
$(EBIN)/megaco_config.$(EMULATOR): megaco_config.erl \
../../include/megaco.hrl \
../app/megaco_internal.hrl
diff --git a/lib/megaco/src/engine/megaco_config.erl b/lib/megaco/src/engine/megaco_config.erl
index 0445f10838..b65ddbe232 100644
--- a/lib/megaco/src/engine/megaco_config.erl
+++ b/lib/megaco/src/engine/megaco_config.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,10 +46,10 @@
%% Verification functions
verify_val/2,
- verify_strict_uint/1,
- verify_strict_int/1, verify_strict_int/2,
- verify_uint/1,
- verify_int/1, verify_int/2,
+%% verify_strict_uint/1,
+%% verify_strict_int/1, verify_strict_int/2,
+%% verify_uint/1,
+%% verify_int/1, verify_int/2,
%% Reply limit counter
@@ -628,31 +628,19 @@ incr_counter(Item, Incr) ->
end
catch
error:_ ->
+ %% Counter does not exist, so try creat it
try
begin
cre_counter(Item, Incr)
end
catch
exit:_ ->
- %% Ok, some other process got there before us,
- %% so try again
+ %% This is a raise condition.
+ %% When we tried to update the counter above, it
+ %% did not exist, but now it does...
ets:update_counter(megaco_config, Item, Incr)
end
end.
-%% incr_counter(Item, Incr) ->
-%% case (catch ets:update_counter(megaco_config, Item, Incr)) of
-%% {'EXIT', _} ->
-%% case (catch cre_counter(Item, Incr)) of
-%% {'EXIT', _} ->
-%% %% Ok, some other process got there before us,
-%% %% so try again
-%% ets:update_counter(megaco_config, Item, Incr);
-%% NewVal ->
-%% NewVal
-%% end;
-%% NewVal ->
-%% NewVal
-%% end.
cre_counter(Item, Initial) ->
case whereis(?SERVER) =:= self() of
@@ -660,8 +648,8 @@ cre_counter(Item, Initial) ->
case call({cre_counter, Item, Initial}) of
{ok, Value} ->
Value;
- Error ->
- exit(Error)
+ {error, Reason} ->
+ exit({failed_creating_counter, Item, Initial, Reason})
end;
true ->
%% Check that the counter does not already exists
@@ -671,7 +659,7 @@ cre_counter(Item, Initial) ->
ets:insert(megaco_config, {Item, Initial}),
{ok, Initial};
[_] ->
- %% Ouch, now what?
+ %% Possibly a raise condition
{error, already_exists}
end
@@ -1513,28 +1501,37 @@ verify_val(Item, Val) ->
mid -> true;
local_mid -> true;
remote_mid -> true;
- min_trans_id -> verify_strict_uint(Val, 4294967295); % uint32
- max_trans_id -> verify_uint(Val, 4294967295); % uint32
+ min_trans_id ->
+ megaco_config_misc:verify_strict_uint(Val, 4294967295); % uint32
+ max_trans_id ->
+ megaco_config_misc:verify_uint(Val, 4294967295); % uint32
request_timer -> verify_timer(Val);
long_request_timer -> verify_timer(Val);
- auto_ack -> verify_bool(Val);
+ auto_ack ->
+ megaco_config_misc:verify_bool(Val);
- trans_ack -> verify_bool(Val);
- trans_ack_maxcount -> verify_uint(Val);
+ trans_ack ->
+ megaco_config_misc:verify_bool(Val);
+ trans_ack_maxcount ->
+ megaco_config_misc:verify_uint(Val);
- trans_req -> verify_bool(Val);
- trans_req_maxcount -> verify_uint(Val);
- trans_req_maxsize -> verify_uint(Val);
+ trans_req ->
+ megaco_config_misc:verify_bool(Val);
+ trans_req_maxcount ->
+ megaco_config_misc:verify_uint(Val);
+ trans_req_maxsize ->
+ megaco_config_misc:verify_uint(Val);
- trans_timer -> verify_timer(Val) and (Val >= 0);
- trans_sender when Val == undefined -> true;
+ trans_timer ->
+ verify_timer(Val) and (Val >= 0);
+ trans_sender when Val =:= undefined -> true;
pending_timer -> verify_timer(Val);
- sent_pending_limit -> verify_uint(Val) andalso
- (Val > 0);
- recv_pending_limit -> verify_uint(Val) andalso
- (Val > 0);
+ sent_pending_limit ->
+ megaco_config_misc:verify_uint(Val) andalso (Val > 0);
+ recv_pending_limit ->
+ megaco_config_misc:verify_uint(Val) andalso (Val > 0);
reply_timer -> verify_timer(Val);
control_pid when is_pid(Val) -> true;
monitor_ref -> true; % Internal usage only
@@ -1542,110 +1539,43 @@ verify_val(Item, Val) ->
send_handle -> true;
encoding_mod when is_atom(Val) -> true;
encoding_config when is_list(Val) -> true;
- protocol_version -> verify_strict_uint(Val);
+ protocol_version ->
+ megaco_config_misc:verify_strict_uint(Val);
auth_data -> true;
user_mod when is_atom(Val) -> true;
user_args when is_list(Val) -> true;
reply_data -> true;
- threaded -> verify_bool(Val);
- strict_version -> verify_bool(Val);
- long_request_resend -> verify_bool(Val);
- call_proxy_gc_timeout -> verify_strict_uint(Val);
- cancel -> verify_bool(Val);
+ threaded ->
+ megaco_config_misc:verify_bool(Val);
+ strict_version ->
+ megaco_config_misc:verify_bool(Val);
+ long_request_resend ->
+ megaco_config_misc:verify_bool(Val);
+ call_proxy_gc_timeout ->
+ megaco_config_misc:verify_strict_uint(Val);
+ cancel ->
+ megaco_config_misc:verify_bool(Val);
resend_indication -> verify_resend_indication(Val);
- segment_reply_ind -> verify_bool(Val);
- segment_recv_acc -> verify_bool(Val);
+ segment_reply_ind ->
+ megaco_config_misc:verify_bool(Val);
+ segment_recv_acc ->
+ megaco_config_misc:verify_bool(Val);
segment_recv_timer -> verify_timer(Val);
segment_send -> verify_segmentation_window(Val);
segment_send_timer -> verify_timer(Val);
- max_pdu_size -> verify_int(Val) andalso (Val > 0);
+ max_pdu_size ->
+ megaco_config_misc:verify_int(Val) andalso (Val > 0);
request_keep_alive_timeout ->
- (verify_uint(Val) orelse (Val =:= plain));
+ (megaco_config_misc:verify_uint(Val) orelse (Val =:= plain));
_ -> false
end.
-verify_bool(true) -> true;
-verify_bool(false) -> true;
-verify_bool(_) -> false.
-
verify_resend_indication(flag) -> true;
-verify_resend_indication(Val) -> verify_bool(Val).
-
--spec verify_strict_int(Int :: integer()) -> boolean().
-verify_strict_int(Int) when is_integer(Int) -> true;
-verify_strict_int(_) -> false.
-
--spec verify_strict_int(Int :: integer(),
- Max :: integer() | 'infinity') -> boolean().
-verify_strict_int(Int, infinity) ->
- verify_strict_int(Int);
-verify_strict_int(Int, Max) ->
- verify_strict_int(Int) andalso verify_strict_int(Max) andalso (Int =< Max).
-
--spec verify_strict_uint(Int :: non_neg_integer()) -> boolean().
-verify_strict_uint(Int) when is_integer(Int) andalso (Int >= 0) -> true;
-verify_strict_uint(_) -> false.
-
--spec verify_strict_uint(Int :: non_neg_integer(),
- Max :: non_neg_integer() | 'infinity') -> boolean().
-verify_strict_uint(Int, infinity) ->
- verify_strict_uint(Int);
-verify_strict_uint(Int, Max) ->
- verify_strict_int(Int, 0, Max).
-
--spec verify_uint(Val :: non_neg_integer() | 'infinity') -> boolean().
-verify_uint(infinity) -> true;
-verify_uint(Val) -> verify_strict_uint(Val).
-
--spec verify_int(Val :: integer() | 'infinity') -> boolean().
-verify_int(infinity) -> true;
-verify_int(Val) -> verify_strict_int(Val).
-
--spec verify_int(Int :: integer() | 'infinity',
- Max :: integer() | 'infinity') -> boolean().
-verify_int(Int, infinity) ->
- verify_int(Int);
-verify_int(infinity, _Max) ->
- true;
-verify_int(Int, Max) ->
- verify_strict_int(Int) andalso verify_strict_int(Max) andalso (Int =< Max).
-
--spec verify_uint(Int :: non_neg_integer() | 'infinity',
- Max :: non_neg_integer() | 'infinity') -> boolean().
-verify_uint(Int, infinity) ->
- verify_uint(Int);
-verify_uint(infinity, _Max) ->
- true;
-verify_uint(Int, Max) ->
- verify_strict_int(Int, 0, Max).
-
--spec verify_strict_int(Int :: integer(),
- Min :: integer(),
- Max :: integer()) -> boolean().
-verify_strict_int(Val, Min, Max)
- when (is_integer(Val) andalso
- is_integer(Min) andalso
- is_integer(Max) andalso
- (Val >= Min) andalso
- (Val =< Max)) ->
- true;
-verify_strict_int(_Val, _Min, _Max) ->
- false.
-
--spec verify_int(Val :: integer() | 'infinity',
- Min :: integer(),
- Max :: integer() | 'infinity') -> boolean().
-verify_int(infinity, Min, infinity) ->
- verify_strict_int(Min);
-verify_int(Val, Min, infinity) ->
- verify_strict_int(Val) andalso
- verify_strict_int(Min) andalso (Val >= Min);
-verify_int(Int, Min, Max) ->
- verify_strict_int(Int, Min, Max).
+verify_resend_indication(Val) -> megaco_config_misc:verify_bool(Val).
verify_timer(Timer) ->
megaco_timer:verify(Timer).
@@ -1653,7 +1583,7 @@ verify_timer(Timer) ->
verify_segmentation_window(none) ->
true;
verify_segmentation_window(K) ->
- verify_int(K, 1, infinity).
+ megaco_config_misc:verify_int(K, 1, infinity).
handle_stop_user(UserMid) ->
case catch user_info(UserMid, mid) of
diff --git a/lib/megaco/src/engine/megaco_config_misc.erl b/lib/megaco/src/engine/megaco_config_misc.erl
new file mode 100644
index 0000000000..0a1601c766
--- /dev/null
+++ b/lib/megaco/src/engine/megaco_config_misc.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Utility module for megaco_config
+%%----------------------------------------------------------------------
+%%
+
+-module(megaco_config_misc).
+
+%% Application internal exports
+-export([
+ verify_bool/1,
+
+ verify_int/1, verify_int/2, verify_int/3,
+ verify_strict_int/1, verify_strict_int/2, verify_strict_int/3,
+
+ verify_uint/1, verify_uint/2,
+ verify_strict_uint/1, verify_strict_uint/2
+ ]).
+
+
+%%%----------------------------------------------------------------------
+%%% API
+%%%----------------------------------------------------------------------
+
+verify_bool(true) -> true;
+verify_bool(false) -> true;
+verify_bool(_) -> false.
+
+
+%% verify_int(Val) -> boolean()
+verify_int(infinity) -> true;
+verify_int(Val) -> verify_strict_int(Val).
+
+%% verify_int(Val, Max) -> boolean()
+verify_int(Int, infinity) ->
+ verify_int(Int);
+verify_int(infinity, _Max) ->
+ true;
+verify_int(Int, Max) ->
+ verify_strict_int(Int) andalso verify_strict_int(Max) andalso (Int =< Max).
+
+%% verify_int(Val, Min, Max) -> boolean()
+verify_int(infinity, Min, infinity) ->
+ verify_strict_int(Min);
+verify_int(Val, Min, infinity) ->
+ verify_strict_int(Val) andalso
+ verify_strict_int(Min) andalso (Val >= Min);
+verify_int(Int, Min, Max) ->
+ verify_strict_int(Int, Min, Max).
+
+%% verify_strict_int(Val) -> boolean()
+verify_strict_int(Int) when is_integer(Int) -> true;
+verify_strict_int(_) -> false.
+
+%% verify_strict_int(Val, Max) -> boolean()
+verify_strict_int(Int, infinity) ->
+ verify_strict_int(Int);
+verify_strict_int(Int, Max) ->
+ verify_strict_int(Int) andalso verify_strict_int(Max) andalso (Int =< Max).
+
+%% verify_strict_int(Val, Min, Max) -> boolean()
+verify_strict_int(Val, Min, Max)
+ when (is_integer(Val) andalso
+ is_integer(Min) andalso
+ is_integer(Max) andalso
+ (Val >= Min) andalso
+ (Val =< Max)) ->
+ true;
+verify_strict_int(_Val, _Min, _Max) ->
+ false.
+
+
+%% verify_uint(Val) -> boolean()
+verify_uint(infinity) -> true;
+verify_uint(Val) -> verify_strict_uint(Val).
+
+%% verify_uint(Val, Max) -> boolean()
+verify_uint(Int, infinity) ->
+ verify_uint(Int);
+verify_uint(infinity, _Max) ->
+ true;
+verify_uint(Int, Max) ->
+ verify_strict_int(Int, 0, Max).
+
+%% verify_strict_uint(Val) -> boolean()
+verify_strict_uint(Int) when is_integer(Int) andalso (Int >= 0) -> true;
+verify_strict_uint(_) -> false.
+
+%% verify_strict_uint(Val, Max) -> boolean()
+verify_strict_uint(Int, infinity) ->
+ verify_strict_uint(Int);
+verify_strict_uint(Int, Max) ->
+ verify_strict_int(Int, 0, Max).
+
diff --git a/lib/megaco/src/engine/megaco_filter.erl b/lib/megaco/src/engine/megaco_filter.erl
index 9df752789c..fb0c700a82 100644
--- a/lib/megaco/src/engine/megaco_filter.erl
+++ b/lib/megaco/src/engine/megaco_filter.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,7 @@
%%----------------------------------------------------------------------
%% Purpose : Megaco/H.248 customization of the Event Tracer tool
%%----------------------------------------------------------------------
+%%
-module(megaco_filter).
@@ -33,6 +34,7 @@
-include_lib("megaco/src/app/megaco_internal.hrl").
-include_lib("et/include/et.hrl").
+
%%----------------------------------------------------------------------
%% BUGBUG: There are some opportunities for improvements:
%%
@@ -43,7 +45,8 @@
%% records that already are defined in megaco_message_{v1,v2,v3}.hrl.
%% * The records megaco_udp and megaco_tcp are copied from the files
%% megaco_udp.hrl and megaco_tcp.hrl respectively, as we cannot include
-%% both header files. They both defines the macros HEAP_SIZE and GC_MSG_LIMIT.
+%% both header files.
+%% They both defines the macros HEAP_SIZE and GC_MSG_LIMIT.
%%-include("megaco_message_internal.hrl").
-record('megaco_transaction_reply',
@@ -76,6 +79,8 @@
module = megaco,
serialize = false % false: Spawn a new process for each message
}).
+
+
%%----------------------------------------------------------------------
start() ->
@@ -360,28 +365,24 @@ pretty(_ConnData, MegaMsg) when is_record(MegaMsg, 'MegacoMessage') ->
{ok, Bin} = megaco_pretty_text_encoder:encode_message([], MegaMsg),
term_to_string(Bin);
pretty(_ConnData, CmdReq) when is_record(CmdReq, 'CommandRequest') ->
- {ok, IoList} = megaco_pretty_text_encoder:encode_command_request(CmdReq),
- term_to_string(lists:flatten(IoList));
+ {ok, Bin} = megaco_pretty_text_encoder:encode_command_request(CmdReq),
+ term_to_string(Bin);
pretty(_ConnData, {complete_success, ContextId, RepList}) ->
ActRep = #'ActionReply'{contextId = ContextId,
commandReply = RepList},
- {ok, IoList} = megaco_pretty_text_encoder:encode_action_reply(ActRep),
- term_to_string(lists:flatten(IoList));
+ {ok, Bin} = megaco_pretty_text_encoder:encode_action_reply(ActRep),
+ term_to_string(Bin);
pretty(_ConnData, AR) when is_record(AR, 'ActionReply') ->
- {ok, IoList} = megaco_pretty_text_encoder:encode_action_reply(AR),
- term_to_string(lists:flatten(IoList));
+ {ok, Bin} = megaco_pretty_text_encoder:encode_action_reply(AR),
+ term_to_string(Bin);
pretty(_ConnData, {partial_failure, ContextId, RepList}) ->
ActRep = #'ActionReply'{contextId = ContextId,
commandReply = RepList},
- {ok, IoList} = megaco_pretty_text_encoder:encode_action_reply(ActRep),
- term_to_string(lists:flatten(IoList));
+ {ok, Bin} = megaco_pretty_text_encoder:encode_action_reply(ActRep),
+ term_to_string(Bin);
pretty(_ConnData, {trans, Trans}) ->
- case megaco_pretty_text_encoder:encode_transaction(Trans) of
- {ok, Bin} when is_binary(Bin) ->
- term_to_string(binary_to_list(Bin));
- {ok, IoList} ->
- term_to_string(lists:flatten(IoList))
- end;
+ {ok, Bin} = megaco_pretty_text_encoder:encode_transaction(Trans),
+ term_to_string(Bin);
pretty(__ConnData, Other) ->
term_to_string(Other).
diff --git a/lib/megaco/src/engine/megaco_messenger.erl b/lib/megaco/src/engine/megaco_messenger.erl
index 5756e8e896..5fad29931b 100644
--- a/lib/megaco/src/engine/megaco_messenger.erl
+++ b/lib/megaco/src/engine/megaco_messenger.erl
@@ -1541,30 +1541,6 @@ check_pending_limit(Limit, Direction, TransId) ->
aborted
end.
-%% check_pending_limit(infinity, _, _) ->
-%% {ok, 0};
-%% check_pending_limit(Limit, Direction, TransId) ->
-%% ?rt2("check pending limit", [Direction, Limit, TransId]),
-%% case (catch megaco_config:get_pending_counter(Direction, TransId)) of
-%% {'EXIT', _} ->
-%% %% This function is only called when we "know" the
-%% %% counter to exist. So, the only reason that this
-%% %% would happen is of the counter has been removed.
-%% %% This only happen if the pending limit has been
-%% %% reached. In any case, this is basically the same
-%% %% as aborted!
-%% ?rt2("check pending limit - exit", []),
-%% aborted;
-%% Val when Val =< Limit ->
-%% %% Since we have no intention to increment here, it
-%% %% is ok to be _at_ the limit
-%% ?rt2("check pending limit - ok", [Val]),
-%% {ok, Val};
-%% _Val ->
-%% ?rt2("check pending limit - aborted", [_Val]),
-%% aborted
-%% end.
-
check_and_maybe_incr_pending_limit(infinity, _, _) ->
ok;
@@ -1572,59 +1548,42 @@ check_and_maybe_incr_pending_limit(Limit, Direction, TransId) ->
%%
%% We need this kind of test to detect when we _pass_ the limit
%%
- ?rt2("check and maybe incr pending limit", [Direction, Limit, TransId]),
+ ?rt2("check and maybe incr pending limit", [{direction, Direction},
+ {transaction_id, TransId},
+ {counter_limit, Limit}]),
try megaco_config:get_pending_counter(Direction, TransId) of
Val when Val > Limit ->
- ?rt2("check and maybe incr - aborted", [Direction, Val, Limit]),
+ ?rt2("check and maybe incr - aborted", [{counter_value, Val}]),
aborted; % Already passed the limit
Val ->
- ?rt2("check and maybe incr - incr", [Direction, Val, Limit]),
+ ?rt2("check and maybe incr - incr", [{counter_value, Val}]),
megaco_config:incr_pending_counter(Direction, TransId),
if
Val < Limit ->
ok; % Still within the limit
true ->
?rt2("check and maybe incr - error",
- [Direction, Val, Limit]),
+ [{counter_value, Val}]),
error % Passed the limit
end
catch
_:_ ->
%% Has not been created yet (connect).
- megaco_config:cre_pending_counter(Direction, TransId, 1),
- ok
+ %% Try create it, but bevare of possible raise condition
+ try
+ begin
+ megaco_config:cre_pending_counter(Direction, TransId, 1),
+ ok
+ end
+ catch
+ _:_ ->
+ %% Ouch, raise condition, increment instead...
+ megaco_config:incr_pending_counter(Direction, TransId),
+ ok
+ end
end.
-%% check_and_maybe_incr_pending_limit(infinity, _, _) ->
-%% ok;
-%% check_and_maybe_incr_pending_limit(Limit, Direction, TransId) ->
-%% %%
-%% %% We need this kind of test to detect when we _pass_ the limit
-%% %%
-%% ?rt2("check and maybe incr pending limit", [Direction, Limit, TransId]),
-%% case (catch megaco_config:get_pending_counter(Direction, TransId)) of
-%% {'EXIT', _} ->
-%% %% Has not been created yet (connect).
-%% megaco_config:cre_pending_counter(Direction, TransId, 1),
-%% ok;
-%% Val when Val > Limit ->
-%% ?rt2("check and maybe incr - aborted", [Direction, Val, Limit]),
-%% aborted; % Already passed the limit
-%% Val ->
-%% ?rt2("check and maybe incr - incr", [Direction, Val, Limit]),
-%% megaco_config:incr_pending_counter(Direction, TransId),
-%% if
-%% Val < Limit ->
-%% ok; % Still within the limit
-%% true ->
-%% ?rt2("check and maybe incr - error",
-%% [Direction, Val, Limit]),
-%% error % Passed the limit
-%% end
-%% end.
-
-
%% BUGBUG BUGBUG BUGBUG
%%
%% Do we know that the Rep is still valid? A previous transaction
@@ -2648,33 +2607,84 @@ handle_reply(
handle_reply(#conn_data{conn_handle = CH} = CD, T, Extra) ->
TransId = to_local_trans_id(CD),
?rt2("handle reply", [T, TransId]),
- case megaco_monitor:lookup_request(TransId) of
- [Req] when (is_record(Req, request) andalso
- (CD#conn_data.cancel =:= true)) ->
+ case {megaco_monitor:request_lockcnt_inc(TransId),
+ megaco_monitor:lookup_request(TransId)} of
+ {_Cnt, [Req]} when (is_record(Req, request) andalso
+ (CD#conn_data.cancel =:= true)) ->
?TC_AWAIT_REPLY_EVENT(true),
+ ?report_trace(CD, "trans reply - cancel(1)", [T]),
do_handle_reply_cancel(CD, Req, T);
- [#request{remote_mid = RMid} = Req] when ((RMid =:= preliminary_mid) orelse
- (RMid =:= CH#megaco_conn_handle.remote_mid)) ->
+ {Cnt, [#request{remote_mid = RMid} = Req]} when
+ ((Cnt =:= 1) andalso
+ ((RMid =:= preliminary_mid) orelse
+ (RMid =:= CH#megaco_conn_handle.remote_mid))) ->
+ ?TC_AWAIT_REPLY_EVENT(false),
+ %% Just in case conn_data got update after our lookup
+ %% but before we looked up the request record, we
+ %% check the cancel field again.
+ case megaco_config:conn_info(CD, cancel) of
+ true ->
+ ?report_trace(CD, "trans reply - cancel(2)", [T]),
+ megaco_monitor:request_lockcnt_del(TransId),
+ do_handle_reply_cancel(CD, Req, T);
+ false ->
+ ?report_trace(CD, "trans reply", [T]),
+ do_handle_reply(CD, Req, TransId, T, Extra)
+ end;
+
+ {Cnt, [#request{remote_mid = RMid} = _Req]} when
+ (is_integer(Cnt) andalso
+ ((RMid =:= preliminary_mid) orelse
+ (RMid =:= CH#megaco_conn_handle.remote_mid))) ->
+ ?TC_AWAIT_REPLY_EVENT(false),
+ %% Ok, someone got there before me, now what?
+ %% This is a plain old raise condition
+ ?report_important(CD, "trans reply - raise condition",
+ [T, {request_lockcnt, Cnt}]),
+ megaco_monitor:request_lockcnt_dec(TransId);
+
+ %% no counter
+ {_Cnt, [#request{remote_mid = RMid} = Req]} when
+ ((RMid =:= preliminary_mid) orelse
+ (RMid =:= CH#megaco_conn_handle.remote_mid)) ->
?TC_AWAIT_REPLY_EVENT(false),
+ %% The counter does not exist.
+ %% This can only mean a code upgrade raise condition.
+ %% That is, this request record was created before
+ %% this feature (the counters) was instroduced.
+ %% The simples solution is this is to behave exactly as
+ %% before, that is pass it along, and leave it to the
+ %% user to figure out.
+
%% Just in case conn_data got update after our lookup
%% but before we looked up the request record, we
%% check the cancel field again.
+ ?report_verbose(CD, "trans reply - old style", [T]),
case megaco_config:conn_info(CD, cancel) of
true ->
+ megaco_monitor:request_lockcnt_del(TransId),
do_handle_reply_cancel(CD, Req, T);
false ->
do_handle_reply(CD, Req, TransId, T, Extra)
end;
- [#request{user_mod = UserMod,
- user_args = UserArgs,
- reply_action = Action,
- reply_data = UserData,
- remote_mid = RMid}] ->
+ {Cnt, [#request{user_mod = UserMod,
+ user_args = UserArgs,
+ reply_action = Action,
+ reply_data = UserData,
+ remote_mid = RMid}]} ->
?report_trace(CD,
"received trans reply with invalid remote mid",
- [T, RMid]),
+ [{transaction, T},
+ {remote_mid, RMid},
+ {request_lockcnt, Cnt}]),
+ if
+ is_integer(Cnt) ->
+ megaco_monitor:request_lockcnt_dec(TransId);
+ true ->
+ ok
+ end,
WrongMid = CH#megaco_conn_handle.remote_mid,
T2 = transform_transaction_reply_enc(CD#conn_data.protocol_version,
T),
@@ -2685,7 +2695,15 @@ handle_reply(#conn_data{conn_handle = CH} = CD, T, Extra) ->
reply_data = UserData},
return_reply(CD2, TransId, UserReply, Extra);
- [] ->
+ {Cnt, []} when is_integer(Cnt) ->
+ ?TC_AWAIT_REPLY_EVENT(undefined),
+ ?report_trace(CD, "trans reply (no receiver)",
+ [T, {request_lockcnt, Cnt}]),
+ megaco_monitor:request_lockcnt_dec(TransId),
+ return_unexpected_trans(CD, T, Extra);
+
+ %% No counter
+ {_Cnt, []} ->
?TC_AWAIT_REPLY_EVENT(undefined),
?report_trace(CD, "trans reply (no receiver)", [T]),
return_unexpected_trans(CD, T, Extra)
@@ -2716,6 +2734,7 @@ do_handle_reply(CD,
%% This is the first reply (maybe of many)
megaco_monitor:delete_request(TransId),
+ megaco_monitor:request_lockcnt_del(TransId),
megaco_monitor:cancel_apply_after(Ref), % OTP-4843
megaco_config:del_pending_counter(recv, TransId), % OTP-7189
@@ -3739,6 +3758,11 @@ insert_requests(ConnData, ConnHandle,
insert_request(ConnData, ConnHandle, TransId,
Action, Data, InitTimer, LongTimer) ->
+ %% We dont check the result of the lock-counter creation because
+ %% the only way it could already exist is if the transaction-id
+ %% range has wrapped and an old counter was not deleted.
+ megaco_monitor:request_lockcnt_cre(TransId),
+
#megaco_conn_handle{remote_mid = RemoteMid} = ConnHandle,
#conn_data{protocol_version = Version,
user_mod = UserMod,
@@ -4323,6 +4347,7 @@ cancel_request(ConnData, Req, Reason) ->
cancel_request2(ConnData, TransId, UserReply) ->
megaco_monitor:delete_request(TransId),
+ megaco_monitor:request_lockcnt_del(TransId),
megaco_config:del_pending_counter(recv, TransId), % OTP-7189
Serial = TransId#trans_id.serial,
ConnData2 = ConnData#conn_data{serial = Serial},
@@ -4380,29 +4405,67 @@ receive_reply_remote(ConnData, UserReply) ->
receive_reply_remote(ConnData, UserReply, Extra) ->
TransId = to_local_trans_id(ConnData),
- case (catch megaco_monitor:lookup_request(TransId)) of
- [#request{timer_ref = {_Type, Ref}} = Req] -> %% OTP-4843
+ case {megaco_monitor:request_lockcnt_inc(TransId),
+ (catch megaco_monitor:lookup_request(TransId))} of
+ {Cnt, [Req]} when (Cnt =:= 1) andalso is_record(Req, request) ->
%% Don't care about Req and Rep version diff
- megaco_monitor:delete_request(TransId),
- megaco_monitor:cancel_apply_after(Ref), % OTP-4843
- megaco_config:del_pending_counter(recv, TransId), % OTP-7189
-
- UserMod = Req#request.user_mod,
- UserArgs = Req#request.user_args,
- Action = Req#request.reply_action,
- UserData = Req#request.reply_data,
- ConnData2 = ConnData#conn_data{user_mod = UserMod,
- user_args = UserArgs,
- reply_action = Action,
- reply_data = UserData},
- return_reply(ConnData2, TransId, UserReply, Extra);
-
+ do_receive_reply_remote(ConnData, TransId, Req, UserReply, Extra);
+
+ {Cnt, [Req]} when is_integer(Cnt) andalso is_record(Req, request) ->
+ %% Another process is accessing, handle as unexpected
+ %% (so it has a possibillity to get logged).
+ ?report_important(ConnData, "trans reply (no receiver)",
+ [{user_reply, UserReply},
+ {request_lockcnt, Cnt}]),
+ megaco_monitor:request_lockcnt_dec(TransId),
+ return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra);
+
+ %% no counter
+ {_Cnt, [Req]} when is_record(Req, request) ->
+ %% The counter does not exist.
+ %% This can only mean a code upgrade raise condition.
+ %% That is, this request record was created before
+ %% this feature (the counters) was instroduced.
+ %% The simples solution to this is to behave exactly as
+ %% before, that is, pass it along, and leave it to the
+ %% user to figure out.
+ ?report_trace(ConnData,
+ "remote reply - "
+ "code upgrade raise condition",
+ [{user_reply, UserReply}]),
+ do_receive_reply_remote(ConnData, TransId, Req, UserReply, Extra);
+
+ {Cnt, _} when is_integer(Cnt) ->
+ ?report_trace(ConnData, "trans reply (no receiver)",
+ [{user_reply, UserReply}, {request_lockcnt, Cnt}]),
+ megaco_monitor:request_lockcnt_dec(TransId),
+ return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra);
+
_ ->
?report_trace(ConnData, "remote reply (no receiver)",
- [UserReply]),
+ [{user_reply, UserReply}]),
return_unexpected_trans_reply(ConnData, TransId, UserReply, Extra)
end.
+do_receive_reply_remote(ConnData, TransId,
+ #request{timer_ref = {_Type, Ref},
+ user_mod = UserMod,
+ user_args = UserArgs,
+ reply_action = Action,
+ reply_data = UserData} = _Req,
+ UserReply, Extra) ->
+ megaco_monitor:delete_request(TransId),
+ megaco_monitor:request_lockcnt_del(TransId),
+ megaco_monitor:cancel_apply_after(Ref), % OTP-4843
+ megaco_config:del_pending_counter(recv, TransId), % OTP-7189
+
+ ConnData2 = ConnData#conn_data{user_mod = UserMod,
+ user_args = UserArgs,
+ reply_action = Action,
+ reply_data = UserData},
+ return_reply(ConnData2, TransId, UserReply, Extra).
+
+
cancel_reply(ConnData, #reply{state = waiting_for_ack,
user_mod = UserMod,
user_args = UserArgs} = Rep, Reason) ->
diff --git a/lib/megaco/src/engine/megaco_monitor.erl b/lib/megaco/src/engine/megaco_monitor.erl
index f95a20cf58..29275371be 100644
--- a/lib/megaco/src/engine/megaco_monitor.erl
+++ b/lib/megaco/src/engine/megaco_monitor.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -51,6 +51,11 @@
update_request_field/3, update_request_fields/2,
delete_request/1,
+ request_lockcnt_cre/1,
+ request_lockcnt_del/1,
+ request_lockcnt_inc/1,
+ request_lockcnt_dec/1,
+
lookup_reply/1,
lookup_reply_field/2,
match_replies/1,
@@ -115,6 +120,24 @@ update_request_fields(Key, NewFields) when is_list(NewFields) ->
delete_request(Key) ->
ets:delete(megaco_requests, Key).
+
+request_lockcnt_cre(TransId) ->
+ Key = {TransId, lockcnt},
+ ets:insert_new(megaco_requests, {Key, 1}).
+
+request_lockcnt_del(TransId) ->
+ Key = {TransId, lockcnt},
+ ets:delete(megaco_requests, Key).
+
+request_lockcnt_inc(TransId) ->
+ Key = {TransId, lockcnt},
+ (catch ets:update_counter(megaco_requests, Key, 1)).
+
+request_lockcnt_dec(TransId) ->
+ Key = {TransId, lockcnt},
+ (catch ets:update_counter(megaco_requests, Key, -1)).
+
+
lookup_reply(Key) ->
ets:lookup(megaco_replies, Key).
diff --git a/lib/megaco/src/engine/megaco_sdp.erl b/lib/megaco/src/engine/megaco_sdp.erl
index 90911fe24a..96732584fb 100644
--- a/lib/megaco/src/engine/megaco_sdp.erl
+++ b/lib/megaco/src/engine/megaco_sdp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,6 +38,8 @@
%% External exports
%%----------------------------------------------------------------------
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([
decode/1, encode/1,
get_sdp_record_from_PropertyGroup/2
@@ -875,9 +877,7 @@ decode_bandwidth_bwt("CT") ->
decode_bandwidth_bwt("AS") ->
as;
decode_bandwidth_bwt(BwType) when is_list(BwType) ->
- BwType;
-decode_bandwidth_bwt(BadBwType) ->
- error({invalid_bandwidth_bwtype, BadBwType}).
+ BwType.
encode_bandwidth_bwt(ct) ->
"CT";
diff --git a/lib/megaco/src/engine/megaco_timer.erl b/lib/megaco/src/engine/megaco_timer.erl
index 9f524523a8..1336be0b5b 100644
--- a/lib/megaco/src/engine/megaco_timer.erl
+++ b/lib/megaco/src/engine/megaco_timer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,7 +42,7 @@
%% NewTimer = megaco_timer()
%% TimeoutTime = infinity | integer()
%%
-init(SingleWaitFor) when SingleWaitFor == infinity ->
+init(SingleWaitFor) when SingleWaitFor =:= infinity ->
{SingleWaitFor, timeout};
init(SingleWaitFor) when is_integer(SingleWaitFor) and (SingleWaitFor >= 0) ->
{SingleWaitFor, timeout};
@@ -76,17 +76,17 @@ verify(#megaco_incr_timer{wait_for = WaitFor,
factor = Factor,
incr = Incr,
max_retries = MaxRetries}) ->
- (megaco_config:verify_strict_uint(WaitFor) and
- megaco_config:verify_strict_uint(Factor) and
- megaco_config:verify_strict_int(Incr) and
+ (megaco_config_misc:verify_strict_uint(WaitFor) and
+ megaco_config_misc:verify_strict_uint(Factor) and
+ megaco_config_misc:verify_strict_int(Incr) and
verify_max_retries(MaxRetries));
verify(Timer) ->
- megaco_config:verify_uint(Timer).
+ megaco_config_misc:verify_uint(Timer).
verify_max_retries(infinity_restartable) ->
true;
verify_max_retries(Val) ->
- megaco_config:verify_uint(Val).
+ megaco_config_misc:verify_uint(Val).
%%-----------------------------------------------------------------
diff --git a/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk
index 44bcadc37b..4bc57cd63e 100644
--- a/lib/megaco/src/engine/modules.mk
+++ b/lib/megaco/src/engine/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,7 @@ BEHAVIOUR_MODULES = \
MODULES = \
$(BEHAVIOUR_MODULES) \
+ megaco_config_misc \
megaco_config \
megaco_digit_map \
megaco_erl_dist_encoder \
diff --git a/lib/megaco/src/flex/megaco_flex_scanner.erl b/lib/megaco/src/flex/megaco_flex_scanner.erl
index e471412c13..508f8905e7 100644
--- a/lib/megaco/src/flex/megaco_flex_scanner.erl
+++ b/lib/megaco/src/flex/megaco_flex_scanner.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,21 +30,11 @@
-define(SCHED_ID(), erlang:system_info(scheduler_id)).
-define(SMP_SUPPORT_DEFAULT(), erlang:system_info(smp_support)).
-is_enabled() ->
- case ?ENABLE_MEGACO_FLEX_SCANNER of
- true ->
- true;
- _ ->
- false
- end.
+is_enabled() ->
+ (true =:= ?ENABLE_MEGACO_FLEX_SCANNER).
is_reentrant_enabled() ->
- case ?MEGACO_REENTRANT_FLEX_SCANNER of
- true ->
- true;
- _ ->
- false
- end.
+ (true =:= ?MEGACO_REENTRANT_FLEX_SCANNER).
is_scanner_port(Port, Port) when is_port(Port) ->
true;
diff --git a/lib/megaco/src/text/megaco_text_gen_prev3a.hrl b/lib/megaco/src/text/megaco_text_gen_prev3a.hrl
index b1ddb10a4e..81916cb9ec 100644
--- a/lib/megaco/src/text/megaco_text_gen_prev3a.hrl
+++ b/lib/megaco/src/text/megaco_text_gen_prev3a.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2924,10 +2924,6 @@ verify_count(Count, Min, Max) ->
end.
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
%% -------------------------------------------------------------------
diff --git a/lib/megaco/src/text/megaco_text_gen_prev3b.hrl b/lib/megaco/src/text/megaco_text_gen_prev3b.hrl
index 8a4af877dc..83d25c83c9 100644
--- a/lib/megaco/src/text/megaco_text_gen_prev3b.hrl
+++ b/lib/megaco/src/text/megaco_text_gen_prev3b.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2945,10 +2945,6 @@ verify_count(Count, Min, Max) ->
end.
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
%% -------------------------------------------------------------------
diff --git a/lib/megaco/src/text/megaco_text_gen_prev3c.hrl b/lib/megaco/src/text/megaco_text_gen_prev3c.hrl
index 11db906846..458809ca75 100644
--- a/lib/megaco/src/text/megaco_text_gen_prev3c.hrl
+++ b/lib/megaco/src/text/megaco_text_gen_prev3c.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -3421,10 +3421,6 @@ verify_count(Count, Min, Max) ->
end.
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
%% -------------------------------------------------------------------
diff --git a/lib/megaco/src/text/megaco_text_gen_v1.hrl b/lib/megaco/src/text/megaco_text_gen_v1.hrl
index b150c9ba58..0726d68941 100644
--- a/lib/megaco/src/text/megaco_text_gen_v1.hrl
+++ b/lib/megaco/src/text/megaco_text_gen_v1.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2386,10 +2386,6 @@ verify_count(Count, Min, Max) ->
end.
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
% d(F) ->
% d(F, []).
diff --git a/lib/megaco/src/text/megaco_text_gen_v2.hrl b/lib/megaco/src/text/megaco_text_gen_v2.hrl
index 6cfcac8664..8e12efe65a 100644
--- a/lib/megaco/src/text/megaco_text_gen_v2.hrl
+++ b/lib/megaco/src/text/megaco_text_gen_v2.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -2772,10 +2772,6 @@ verify_count(Count, Min, Max) ->
end.
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
%% -------------------------------------------------------------------
diff --git a/lib/megaco/src/text/megaco_text_gen_v3.hrl b/lib/megaco/src/text/megaco_text_gen_v3.hrl
index 1c19a4aa8b..ce2e5e508d 100644
--- a/lib/megaco/src/text/megaco_text_gen_v3.hrl
+++ b/lib/megaco/src/text/megaco_text_gen_v3.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -3436,10 +3436,6 @@ verify_count(Count, Min, Max) ->
end.
-%% -------------------------------------------------------------------
-
-error(Reason) ->
- erlang:error(Reason).
%% -------------------------------------------------------------------
diff --git a/lib/megaco/test/Makefile b/lib/megaco/test/Makefile
index 682b83d368..88f6f06e73 100644
--- a/lib/megaco/test/Makefile
+++ b/lib/megaco/test/Makefile
@@ -754,5 +754,5 @@ release_tests_spec: tests
# $(HRL_FILES) $(ERL_FILES) \
# $(RELSYSDIR)
#
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
diff --git a/lib/megaco/test/megaco.cover b/lib/megaco/test/megaco.cover
index e7764017d4..be21216c24 100644
--- a/lib/megaco/test/megaco.cover
+++ b/lib/megaco/test/megaco.cover
@@ -1,5 +1,7 @@
+{incl_app,megaco,details}.
+
%% -*- erlang -*-
-{exclude,
+{excl_mods, megaco,
[megaco_encoder,
megaco_edist_compress,
megaco_filter,
diff --git a/lib/megaco/test/megaco.spec b/lib/megaco/test/megaco.spec
index 7493bd5df8..cab8499835 100644
--- a/lib/megaco/test/megaco.spec
+++ b/lib/megaco/test/megaco.spec
@@ -1,5 +1,2 @@
-{topcase, {dir, "../megaco_test"}}.
-{require_nodenames, 1}.
-%{skip, {megaco_digit_map_test, all, "Not yet implemented"}}.
-{skip, {megaco_measure_test, all, "Not yet implemented"}}.
-%{skip, {M, F, "Not yet implemented"}}.
+{suites,"../megaco_test",all}.
+{skip_cases,"../megaco_test",megaco_measure_test,[all],"Not yet implemented"}.
diff --git a/lib/megaco/test/megaco_SUITE.erl b/lib/megaco/test/megaco_SUITE.erl
index 1bb3a570a4..007677ba4d 100644
--- a/lib/megaco/test/megaco_SUITE.erl
+++ b/lib/megaco/test/megaco_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,8 +36,8 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
init() ->
process_flag(trap_exit, true),
@@ -46,97 +46,86 @@ init() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- app_test,
- appup_test,
- config,
- flex,
- udp,
- tcp,
- examples,
- %% call_flow,
- digit_map,
- mess,
- measure,
- binary_term_id,
- codec,
- sdp,
- mib,
- trans,
- actions,
- load,
- pending_limit,
- segmented,
- timer
- ].
+suite() -> [{ct_hooks, [{ts_install_cth, [{nodenames,1}]}]}].
+
+all() ->
+ [{group, app_test},
+ {group, appup_test},
+ {group, config},
+ {group, flex},
+ {group, udp},
+ {group, tcp},
+ {group, examples},
+ {group, digit_map},
+ {group, mess},
+ {group, measure},
+ {group, binary_term_id},
+ {group, codec},
+ {group, sdp},
+ {group, mib},
+ {group, trans},
+ {group, actions},
+ {group, load},
+ {group, pending_limit},
+ {group, segmented},
+ {group, timer}].
+
+groups() ->
+ [{tickets, [], [{group, mess}, {group, codec}]},
+ {app_test, [], [{megaco_app_test, all}]},
+ {appup_test, [], [{megaco_appup_test, all}]},
+ {config, [], [{megaco_config_test, all}]},
+ {call_flow, [], [{megaco_call_flow_test, all}]},
+ {digit_map, [], [{megaco_digit_map_test, all}]},
+ {mess, [], [{megaco_mess_test, all}]},
+ {udp, [], [{megaco_udp_test, all}]},
+ {tcp, [], [{megaco_tcp_test, all}]},
+ {examples, [], [{megaco_examples_test, all}]},
+ {measure, [], [{megaco_measure_test, all}]},
+ {binary_term_id, [], [{megaco_binary_term_id_test, all}]},
+ {codec, [], [{megaco_codec_test, all}]},
+ {sdp, [], [{megaco_sdp_test, all}]},
+ {mib, [], [{megaco_mib_test, all}]},
+ {trans, [], [{megaco_trans_test, all}]},
+ {actions, [], [{megaco_actions_test, all}]},
+ {load, [], [{megaco_load_test, all}]},
+ {pending_limit, [], [{megaco_pending_limit_test, all}]},
+ {segmented, [], [{megaco_segment_test, all}]},
+ {timer, [], [{megaco_timer_test, all}]},
+ {flex, [], [{megaco_flex_test, all}]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-tickets(suite) ->
- [
- mess,
- codec
- ].
-app_test(suite) ->
- [{megaco_app_test, all}].
-appup_test(suite) ->
- [{megaco_appup_test, all}].
-config(suite) ->
- [{megaco_config_test, all}].
-call_flow(suite) ->
- [{megaco_call_flow_test, all}].
-digit_map(suite) ->
- [{megaco_digit_map_test, all}].
-mess(suite) ->
- [{megaco_mess_test, all}].
-udp(suite) ->
- [{megaco_udp_test, all}].
-tcp(suite) ->
- [{megaco_tcp_test, all}].
-examples(suite) ->
- [{megaco_examples_test, all}].
-measure(suite) ->
- [{megaco_measure_test, all}].
-binary_term_id(suite) ->
- [{megaco_binary_term_id_test, all}].
-codec(suite) ->
- [{megaco_codec_test, all}].
-sdp(suite) ->
- [{megaco_sdp_test, all}].
-mib(suite) ->
- [{megaco_mib_test, all}].
-trans(suite) ->
- [{megaco_trans_test, all}].
-actions(suite) ->
- [{megaco_actions_test, all}].
-load(suite) ->
- [{megaco_load_test, all}].
-pending_limit(suite) ->
- [{megaco_pending_limit_test, all}].
-segmented(suite) ->
- [{megaco_segment_test, all}].
-timer(suite) ->
- [{megaco_timer_test, all}].
-flex(suite) ->
- [{megaco_flex_test, all}].
diff --git a/lib/megaco/test/megaco_actions_test.erl b/lib/megaco/test/megaco_actions_test.erl
index d493022ca1..2efb6e834a 100644
--- a/lib/megaco/test/megaco_actions_test.erl
+++ b/lib/megaco/test/megaco_actions_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -72,28 +72,25 @@ init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- pretty_text,
- flex_pretty_text,
- compact_text,
- flex_compact_text,
- erl_dist,
- erl_dist_mc,
- ber_bin,
- ber_bin_drv,
- ber_bin_native,
- ber_bin_drv_native
- ],
- Cases.
+all() ->
+ [pretty_text, flex_pretty_text, compact_text,
+ flex_compact_text, erl_dist, erl_dist_mc, ber_bin,
+ ber_bin_drv, ber_bin_native, ber_bin_drv_native].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_app_test.erl b/lib/megaco/test/megaco_app_test.erl
index 597ec26338..00f7b7fb68 100644
--- a/lib/megaco/test/megaco_app_test.erl
+++ b/lib/megaco/test/megaco_app_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -39,28 +39,36 @@ init_per_testcase(undef_funcs = Case, Config) ->
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- fields,
- modules,
- exportall,
- app_depend,
- undef_funcs
- ],
- {req, [], {conf, app_init, Cases, app_fin}}.
+all() ->
+ [
+ fields,
+ modules,
+ exportall,
+ app_depend,
+ undef_funcs
+ ].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-app_init(suite) -> [];
-app_init(doc) -> [];
-app_init(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
case is_app(megaco) of
{ok, AppFile} ->
io:format("AppFile: ~n~p~n", [AppFile]),
@@ -96,9 +104,9 @@ is_app(App) ->
end.
-app_fin(suite) -> [];
-app_fin(doc) -> [];
-app_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
@@ -110,7 +118,7 @@ fields(doc) ->
[];
fields(Config) when is_list(Config) ->
AppFile = key1search(app_file, Config),
- Fields = [vsn, description, modules, registered, applications],
+ Fields = [vsn, description, modules, registered, applications],
case check_fields(Fields, AppFile, []) of
[] ->
ok;
diff --git a/lib/megaco/test/megaco_appup_mg.erl b/lib/megaco/test/megaco_appup_mg.erl
index f6060e406b..bb8b098f5d 100644
--- a/lib/megaco/test/megaco_appup_mg.erl
+++ b/lib/megaco/test/megaco_appup_mg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_appup_mgc.erl b/lib/megaco/test/megaco_appup_mgc.erl
index b6e53655f8..49c5f24852 100644
--- a/lib/megaco/test/megaco_appup_mgc.erl
+++ b/lib/megaco/test/megaco_appup_mgc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_appup_test.erl b/lib/megaco/test/megaco_appup_test.erl
index 09732c6a4d..40eebcae86 100644
--- a/lib/megaco/test/megaco_appup_test.erl
+++ b/lib/megaco/test/megaco_appup_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,7 @@
-module(megaco_appup_test).
-compile(export_all).
+-compile({no_auto_import,[error/1]}).
-include("megaco_test_lib.hrl").
@@ -37,25 +38,31 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- appup
- ],
- {req, [], {conf, appup_init, Cases, appup_fin}}.
+all() ->
+ [appup].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-appup_init(suite) -> [];
-appup_init(doc) -> [];
-appup_init(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
AppFile = file_name(?APPLICATION, ".app"),
AppupFile = file_name(?APPLICATION, ".appup"),
[{app_file, AppFile}, {appup_file, AppupFile}|Config].
@@ -66,9 +73,9 @@ file_name(App, Ext) ->
filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-appup_fin(suite) -> [];
-appup_fin(doc) -> [];
-appup_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
diff --git a/lib/megaco/test/megaco_binary_term_id_test.erl b/lib/megaco/test/megaco_binary_term_id_test.erl
index da4e69c617..47a7a76c1c 100644
--- a/lib/megaco/test/megaco_binary_term_id_test.erl
+++ b/lib/megaco/test/megaco_binary_term_id_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,8 +37,8 @@
-export([t/0]).
%% Test suite exports
--export([all/1, encode_first/1, decode_first/1,
- init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
%%----------------------------------------------------------------------
@@ -57,25 +57,25 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- encode_first,
- decode_first
- ].
+all() ->
+ [{group, encode_first}, {group, decode_first}].
-encode_first(suite) ->
- encode_first_cases().
+groups() ->
+ [{encode_first, [], encode_first_cases()},
+ {decode_first, [], decode_first_cases()}].
-decode_first(suite) ->
- decode_first_cases().
+init_per_group(_GroupName, Config) ->
+ Config.
+end_per_group(_GroupName, Config) ->
+ Config.
%% Test server callbacks
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%======================================================================
@@ -89,11 +89,12 @@ t() ->
cases() -> encode_first_cases() ++ decode_first_cases().
-encode_first_cases() -> [te01,te02,te03,te04,te05,
- te06,te07,te08,te09,te10,
- te11,te12,te13,te14,te15,
- te16,te17,te18,te19].
-decode_first_cases() -> [td01,td02,td03,td04,td05,td06].
+encode_first_cases() ->
+[te01, te02, te03, te04, te05, te06, te07, te08, te09,
+ te10, te11, te12, te13, te14, te15, te16, te17, te18,
+ te19].
+decode_first_cases() ->
+[td01, td02, td03, td04, td05, td06].
do(Case) ->
case doc(Case) of
diff --git a/lib/megaco/test/megaco_call_flow_test.erl b/lib/megaco/test/megaco_call_flow_test.erl
index a25a7924e8..b9d64ca8b2 100644
--- a/lib/megaco/test/megaco_call_flow_test.erl
+++ b/lib/megaco/test/megaco_call_flow_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,37 +50,25 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary
- ].
+all() ->
+ [{group, text}, {group, binary}].
-text(suite) ->
- [
- pretty,
- compact
- ].
+groups() ->
+ [{text, [], [pretty, compact]},
+ {flex, [], [pretty_flex, compact_flex]},
+ {binary, [], [bin, ber, ber_bin, per]}].
-flex(suite) ->
- [
- pretty_flex,
- compact_flex
- ].
+init_per_group(_GroupName, Config) ->
+ Config.
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per
- ].
+end_per_group(_GroupName, Config) ->
+ Config.
pretty(suite) ->
[];
diff --git a/lib/megaco/test/megaco_codec_flex_lib.erl b/lib/megaco/test/megaco_codec_flex_lib.erl
index de76956711..93bc5d4bbc 100644
--- a/lib/megaco/test/megaco_codec_flex_lib.erl
+++ b/lib/megaco/test/megaco_codec_flex_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_codec_mini_test.erl b/lib/megaco/test/megaco_codec_mini_test.erl
index e509739bb1..ff0c154c7c 100644
--- a/lib/megaco/test/megaco_codec_mini_test.erl
+++ b/lib/megaco/test/megaco_codec_mini_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,14 +34,14 @@
-export([t/0, t/1]).
--export([all/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
tickets/0,
- tickets/1,
+
otp7672_msg01/1,
otp7672_msg02/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
%% ----
@@ -49,31 +49,6 @@
-define(SET_DBG(S,D), begin put(severity, S), put(dbg, D) end).
-define(RESET_DBG(), begin erase(severity), erase(dbg) end).
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
-
expand(RootCase) ->
expand([RootCase], []).
@@ -106,24 +81,51 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- tickets
- ].
+all() ->
+ [{group, tickets}].
+
+groups() ->
+ [{tickets, [], [otp7672_msg01, otp7672_msg02]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
-tickets(suite) ->
- [
- otp7672_msg01,
- otp7672_msg02
- ].
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%% ----
+
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_codec_prev3a_test.erl b/lib/megaco/test/megaco_codec_prev3a_test.erl
index 696a72343c..d50e72aef1 100644
--- a/lib/megaco/test/megaco_codec_prev3a_test.erl
+++ b/lib/megaco/test/megaco_codec_prev3a_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,22 +36,16 @@
-export([t/0, t/1]).
--export([all/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
- text/1,
-
- pretty/1,
pretty_test_msgs/1,
-
- compact/1,
+
compact_test_msgs/1,
-
- flex_pretty/1,
+
flex_pretty_init/1,
flex_pretty_finish/1,
flex_pretty_test_msgs/1,
-
- flex_compact/1,
+
flex_compact_init/1,
flex_compact_finish/1,
flex_compact_test_msgs/1,
@@ -64,32 +58,21 @@
flex_compact_dm_timers6/1,
flex_compact_dm_timers7/1,
flex_compact_dm_timers8/1,
-
- binary/1,
- bin/1,
bin_test_msgs/1,
-
- ber/1,
+
ber_test_msgs/1,
-
- ber_bin/1,
+
ber_bin_test_msgs/1,
-
- per/1,
+
per_test_msgs/1,
-
- per_bin/1,
+
per_bin_test_msgs/1,
-
- erl_dist/1,
- erl_dist_m/1,
+
erl_dist_m_test_msgs/1,
tickets/0,
- tickets/1,
-
- compact_tickets/1,
+
compact_otp4011_msg1/1,
compact_otp4011_msg2/1,
compact_otp4011_msg3/1,
@@ -132,8 +115,7 @@
compact_otp6017_msg01/1,
compact_otp6017_msg02/1,
compact_otp6017_msg03/1,
-
- flex_compact_tickets/1,
+
flex_compact_otp7431_msg01/1,
flex_compact_otp7431_msg02/1,
flex_compact_otp7431_msg03/1,
@@ -141,8 +123,7 @@
flex_compact_otp7431_msg05/1,
flex_compact_otp7431_msg06/1,
flex_compact_otp7431_msg07/1,
-
- pretty_tickets/1,
+
pretty_otp4632_msg1/1,
pretty_otp4632_msg2/1,
pretty_otp4632_msg3/1,
@@ -185,8 +166,7 @@
pretty_otp7671_msg04/1,
pretty_otp7671_msg05/1,
pretty_otp8114_msg01/1,
-
- flex_pretty_tickets/1,
+
flex_pretty_otp5042_msg1/1,
flex_pretty_otp5085_msg1/1,
flex_pretty_otp5085_msg2/1,
@@ -208,7 +188,7 @@
flex_pretty_otp7431_msg06/1,
flex_pretty_otp7431_msg07/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([display_text_messages/0]).
@@ -263,30 +243,7 @@ expand([Case|Cases], Acc) ->
expand(Cases, [Case|Acc])
end.
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
+
%% ----
@@ -306,268 +263,166 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary,
- erl_dist,
- tickets
- ].
-
-text(suite) ->
- [
- pretty,
- flex_pretty,
- compact,
- flex_compact
- ].
-
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per,
- per_bin
- ].
-
-erl_dist(suite) ->
- [
- erl_dist_m
- ].
-
-pretty(suite) ->
- [
- pretty_test_msgs
- ].
-
-
-compact(suite) ->
- [
- compact_test_msgs
- ].
-
-
-flex_pretty(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_cases(), flex_pretty_finish}}.
-
-flex_pretty_cases() ->
- [
- flex_pretty_test_msgs
- ].
-
-flex_compact(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_cases(), flex_compact_finish}}.
-
-flex_compact_cases() ->
- [
- flex_compact_test_msgs,
- flex_compact_dm_timers1,
- flex_compact_dm_timers2,
- flex_compact_dm_timers3,
- flex_compact_dm_timers4,
- flex_compact_dm_timers5,
- flex_compact_dm_timers6,
- flex_compact_dm_timers7,
- flex_compact_dm_timers8
- ].
-
-
-bin(suite) ->
- [
- bin_test_msgs
- ].
-
-
-ber(suite) ->
- [
- ber_test_msgs
- ].
-
-
-ber_bin(suite) ->
- [
- ber_bin_test_msgs
- ].
-
-
-per(suite) ->
- [
- per_test_msgs
- ].
-
+all() ->
+ [{group, text}, {group, binary}, {group, erl_dist},
+ {group, tickets}].
+
+groups() ->
+ [{text, [],
+ [{group, pretty}, {group, flex_pretty},
+ {group, compact}, {group, flex_compact}]},
+ {binary, [],
+ [{group, bin}, {group, ber}, {group, ber_bin},
+ {group, per}, {group, per_bin}]},
+ {erl_dist, [], [{group, erl_dist_m}]},
+ {pretty, [], [pretty_test_msgs]},
+ {compact, [], [compact_test_msgs]},
+ {flex_pretty, [], flex_pretty_cases()},
+ {flex_compact, [], flex_compact_cases()},
+ {bin, [], [bin_test_msgs]}, {ber, [], [ber_test_msgs]},
+ {ber_bin, [], [ber_bin_test_msgs]},
+ {per, [], [per_test_msgs]},
+ {per_bin, [], [per_bin_test_msgs]},
+ {erl_dist_m, [], [erl_dist_m_test_msgs]},
+ {tickets, [],
+ [{group, compact_tickets},
+ {group, flex_compact_tickets}, {group, pretty_tickets},
+ {group, flex_pretty_tickets}]},
+ {compact_tickets, [],
+ [compact_otp4011_msg1, compact_otp4011_msg2,
+ compact_otp4011_msg3, compact_otp4013_msg1,
+ compact_otp4085_msg1, compact_otp4085_msg2,
+ compact_otp4280_msg1, compact_otp4299_msg1,
+ compact_otp4299_msg2, compact_otp4359_msg1,
+ compact_otp4920_msg0, compact_otp4920_msg1,
+ compact_otp4920_msg2, compact_otp4920_msg3,
+ compact_otp4920_msg4, compact_otp4920_msg5,
+ compact_otp4920_msg6, compact_otp4920_msg7,
+ compact_otp4920_msg8, compact_otp4920_msg9,
+ compact_otp4920_msg10, compact_otp4920_msg11,
+ compact_otp4920_msg12, compact_otp4920_msg20,
+ compact_otp4920_msg21, compact_otp4920_msg22,
+ compact_otp4920_msg23, compact_otp4920_msg24,
+ compact_otp4920_msg25, compact_otp5186_msg01,
+ compact_otp5186_msg02, compact_otp5186_msg03,
+ compact_otp5186_msg04, compact_otp5186_msg05,
+ compact_otp5186_msg06, compact_otp5793_msg01,
+ compact_otp5993_msg01, compact_otp5993_msg02,
+ compact_otp5993_msg03, compact_otp6017_msg01,
+ compact_otp6017_msg02, compact_otp6017_msg03]},
+ {flex_compact_tickets, [],
+ flex_compact_tickets_cases()},
+ {pretty_tickets, [],
+ [pretty_otp4632_msg1, pretty_otp4632_msg2,
+ pretty_otp4632_msg3, pretty_otp4632_msg4,
+ pretty_otp4710_msg1, pretty_otp4710_msg2,
+ pretty_otp4945_msg1, pretty_otp4945_msg2,
+ pretty_otp4945_msg3, pretty_otp4945_msg4,
+ pretty_otp4945_msg5, pretty_otp4945_msg6,
+ pretty_otp4949_msg1, pretty_otp4949_msg2,
+ pretty_otp4949_msg3, pretty_otp5042_msg1,
+ pretty_otp5068_msg1, pretty_otp5085_msg1,
+ pretty_otp5085_msg2, pretty_otp5085_msg3,
+ pretty_otp5085_msg4, pretty_otp5085_msg5,
+ pretty_otp5085_msg6, pretty_otp5085_msg7,
+ pretty_otp5085_msg8, pretty_otp5600_msg1,
+ pretty_otp5600_msg2, pretty_otp5601_msg1,
+ pretty_otp5793_msg01, pretty_otp5882_msg01,
+ pretty_otp6490_msg01, pretty_otp6490_msg02,
+ pretty_otp6490_msg03, pretty_otp6490_msg04,
+ pretty_otp6490_msg05, pretty_otp6490_msg06,
+ pretty_otp7671_msg01, pretty_otp7671_msg02,
+ pretty_otp7671_msg03, pretty_otp7671_msg04,
+ pretty_otp7671_msg05, pretty_otp8114_msg01]},
+ {flex_pretty_tickets, [], flex_pretty_tickets_cases()}].
+
+init_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_init(Config);
+init_per_group(flex_compact_tickets, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_compact, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_pretty, Config) ->
+ flex_pretty_init(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(flex_compact_tickets, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_compact, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_pretty, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
+flex_pretty_cases() ->
+ [flex_pretty_test_msgs].
+
+
+flex_compact_cases() ->
+ [flex_compact_test_msgs, flex_compact_dm_timers1,
+ flex_compact_dm_timers2, flex_compact_dm_timers3,
+ flex_compact_dm_timers4, flex_compact_dm_timers5,
+ flex_compact_dm_timers6, flex_compact_dm_timers7,
+ flex_compact_dm_timers8].
%% Support for per_bin was added to ASN.1 as of version
%% 1.3.2 (R8). And later merged into 1.3.1.3 (R7). These
%% releases are identical (as far as I know).
%%
-per_bin(suite) ->
- [
- per_bin_test_msgs
- ].
-
-
-erl_dist_m(suite) ->
- [
- erl_dist_m_test_msgs
- ].
-
-tickets(suite) ->
- [
- compact_tickets,
- flex_compact_tickets,
- pretty_tickets,
- flex_pretty_tickets
- ].
-
-compact_tickets(suite) ->
- [
- compact_otp4011_msg1,
- compact_otp4011_msg2,
- compact_otp4011_msg3,
- compact_otp4013_msg1,
- compact_otp4085_msg1,
- compact_otp4085_msg2,
- compact_otp4280_msg1,
- compact_otp4299_msg1,
- compact_otp4299_msg2,
- compact_otp4359_msg1,
- compact_otp4920_msg0,
- compact_otp4920_msg1,
- compact_otp4920_msg2,
- compact_otp4920_msg3,
- compact_otp4920_msg4,
- compact_otp4920_msg5,
- compact_otp4920_msg6,
- compact_otp4920_msg7,
- compact_otp4920_msg8,
- compact_otp4920_msg9,
- compact_otp4920_msg10,
- compact_otp4920_msg11,
- compact_otp4920_msg12,
- compact_otp4920_msg20,
- compact_otp4920_msg21,
- compact_otp4920_msg22,
- compact_otp4920_msg23,
- compact_otp4920_msg24,
- compact_otp4920_msg25,
- compact_otp5186_msg01,
- compact_otp5186_msg02,
- compact_otp5186_msg03,
- compact_otp5186_msg04,
- compact_otp5186_msg05,
- compact_otp5186_msg06,
- compact_otp5793_msg01,
- compact_otp5993_msg01,
- compact_otp5993_msg02,
- compact_otp5993_msg03,
- compact_otp6017_msg01,
- compact_otp6017_msg02,
- compact_otp6017_msg03
- ].
-
-flex_compact_tickets(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_tickets_cases(),
- flex_compact_finish}}.
+flex_compact_tickets_cases() ->
+ [flex_compact_otp7431_msg01, flex_compact_otp7431_msg02,
+ flex_compact_otp7431_msg03, flex_compact_otp7431_msg04,
+ flex_compact_otp7431_msg05, flex_compact_otp7431_msg06,
+ flex_compact_otp7431_msg07].
+
+flex_pretty_tickets_cases() ->
+ [flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1,
+ flex_pretty_otp5085_msg2, flex_pretty_otp5085_msg3,
+ flex_pretty_otp5085_msg4, flex_pretty_otp5085_msg5,
+ flex_pretty_otp5085_msg6, flex_pretty_otp5085_msg7,
+ flex_pretty_otp5085_msg8, flex_pretty_otp5600_msg1,
+ flex_pretty_otp5600_msg2, flex_pretty_otp5601_msg1,
+ flex_pretty_otp5793_msg01, flex_pretty_otp7431_msg01,
+ flex_pretty_otp7431_msg02, flex_pretty_otp7431_msg03,
+ flex_pretty_otp7431_msg04, flex_pretty_otp7431_msg05,
+ flex_pretty_otp7431_msg06, flex_pretty_otp7431_msg07].
-flex_compact_tickets_cases() ->
- [
- flex_compact_otp7431_msg01,
- flex_compact_otp7431_msg02,
- flex_compact_otp7431_msg03,
- flex_compact_otp7431_msg04,
- flex_compact_otp7431_msg05,
- flex_compact_otp7431_msg06,
- flex_compact_otp7431_msg07
- ].
-
-
-pretty_tickets(suite) ->
- [
- pretty_otp4632_msg1,
- pretty_otp4632_msg2,
- pretty_otp4632_msg3,
- pretty_otp4632_msg4,
- pretty_otp4710_msg1,
- pretty_otp4710_msg2,
- pretty_otp4945_msg1,
- pretty_otp4945_msg2,
- pretty_otp4945_msg3,
- pretty_otp4945_msg4,
- pretty_otp4945_msg5,
- pretty_otp4945_msg6,
- pretty_otp4949_msg1,
- pretty_otp4949_msg2,
- pretty_otp4949_msg3,
- pretty_otp5042_msg1,
- pretty_otp5068_msg1,
- pretty_otp5085_msg1,
- pretty_otp5085_msg2,
- pretty_otp5085_msg3,
- pretty_otp5085_msg4,
- pretty_otp5085_msg5,
- pretty_otp5085_msg6,
- pretty_otp5085_msg7,
- pretty_otp5085_msg8,
- pretty_otp5600_msg1,
- pretty_otp5600_msg2,
- pretty_otp5601_msg1,
- pretty_otp5793_msg01,
- pretty_otp5882_msg01,
- pretty_otp6490_msg01,
- pretty_otp6490_msg02,
- pretty_otp6490_msg03,
- pretty_otp6490_msg04,
- pretty_otp6490_msg05,
- pretty_otp6490_msg06,
- pretty_otp7671_msg01,
- pretty_otp7671_msg02,
- pretty_otp7671_msg03,
- pretty_otp7671_msg04,
- pretty_otp7671_msg05,
- pretty_otp8114_msg01
- ].
+%% ----
-flex_pretty_tickets(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_tickets_cases(),
- flex_pretty_finish}}.
-
-flex_pretty_tickets_cases() ->
- [
- flex_pretty_otp5042_msg1,
- flex_pretty_otp5085_msg1,
- flex_pretty_otp5085_msg2,
- flex_pretty_otp5085_msg3,
- flex_pretty_otp5085_msg4,
- flex_pretty_otp5085_msg5,
- flex_pretty_otp5085_msg6,
- flex_pretty_otp5085_msg7,
- flex_pretty_otp5085_msg8,
- flex_pretty_otp5600_msg1,
- flex_pretty_otp5600_msg2,
- flex_pretty_otp5601_msg1,
- flex_pretty_otp5793_msg01,
- flex_pretty_otp7431_msg01,
- flex_pretty_otp7431_msg02,
- flex_pretty_otp7431_msg03,
- flex_pretty_otp7431_msg04,
- flex_pretty_otp7431_msg05,
- flex_pretty_otp7431_msg06,
- flex_pretty_otp7431_msg07
- ].
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_codec_prev3b_test.erl b/lib/megaco/test/megaco_codec_prev3b_test.erl
index b5fe4d2038..eaab8f37c1 100644
--- a/lib/megaco/test/megaco_codec_prev3b_test.erl
+++ b/lib/megaco/test/megaco_codec_prev3b_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,22 +36,16 @@
-export([t/0, t/1]).
--export([all/1,
-
- text/1,
-
- pretty/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
+
pretty_test_msgs/1,
-
- compact/1,
+
compact_test_msgs/1,
- flex_pretty/1,
flex_pretty_init/1,
flex_pretty_finish/1,
flex_pretty_test_msgs/1,
-
- flex_compact/1,
+
flex_compact_init/1,
flex_compact_finish/1,
flex_compact_test_msgs/1,
@@ -64,32 +58,21 @@
flex_compact_dm_timers6/1,
flex_compact_dm_timers7/1,
flex_compact_dm_timers8/1,
-
- binary/1,
- bin/1,
bin_test_msgs/1,
-
- ber/1,
+
ber_test_msgs/1,
-
- ber_bin/1,
+
ber_bin_test_msgs/1,
-
- per/1,
+
per_test_msgs/1,
-
- per_bin/1,
+
per_bin_test_msgs/1,
-
- erl_dist/1,
- erl_dist_m/1,
+
erl_dist_m_test_msgs/1,
tickets/0,
- tickets/1,
-
- compact_tickets/1,
+
compact_otp4011_msg1/1,
compact_otp4011_msg2/1,
compact_otp4011_msg3/1,
@@ -133,8 +116,7 @@
compact_otp6017_msg01/1,
compact_otp6017_msg02/1,
compact_otp6017_msg03/1,
-
- flex_compact_tickets/1,
+
flex_compact_otp7431_msg01/1,
flex_compact_otp7431_msg02/1,
flex_compact_otp7431_msg03/1,
@@ -142,8 +124,7 @@
flex_compact_otp7431_msg05/1,
flex_compact_otp7431_msg06/1,
flex_compact_otp7431_msg07/1,
-
- pretty_tickets/1,
+
pretty_otp4632_msg1/1,
pretty_otp4632_msg2/1,
pretty_otp4632_msg3/1,
@@ -190,8 +171,7 @@
pretty_otp7671_msg04/1,
pretty_otp7671_msg05/1,
pretty_otp8114_msg01/1,
-
- flex_pretty_tickets/1,
+
flex_pretty_otp5042_msg1/1,
flex_pretty_otp5085_msg1/1,
flex_pretty_otp5085_msg2/1,
@@ -217,7 +197,7 @@
flex_pretty_otp7431_msg06/1,
flex_pretty_otp7431_msg07/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([display_text_messages/0, generate_text_messages/0]).
@@ -281,31 +261,6 @@ expand([Case|Cases], Acc) ->
expand(Cases, [Case|Acc])
end.
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
-
%% ----
@@ -324,276 +279,170 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary,
- erl_dist,
- tickets
- ].
-
-text(suite) ->
- [
- pretty,
- flex_pretty,
- compact,
- flex_compact
- ].
-
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per,
- per_bin
- ].
-
-erl_dist(suite) ->
- [
- erl_dist_m
- ].
-
-pretty(suite) ->
- [
- pretty_test_msgs
- ].
-
-
-compact(suite) ->
- [
- compact_test_msgs
- ].
-
-
-flex_pretty(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_cases(), flex_pretty_finish}}.
-
-flex_pretty_cases() ->
- [
- flex_pretty_test_msgs
- ].
-
-flex_compact(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_cases(), flex_compact_finish}}.
-
-flex_compact_cases() ->
- [
- flex_compact_test_msgs,
- flex_compact_dm_timers1,
- flex_compact_dm_timers2,
- flex_compact_dm_timers3,
- flex_compact_dm_timers4,
- flex_compact_dm_timers5,
- flex_compact_dm_timers6,
- flex_compact_dm_timers7,
- flex_compact_dm_timers8
- ].
-
-
-bin(suite) ->
- [
- bin_test_msgs
- ].
-
-
-ber(suite) ->
- [
- ber_test_msgs
- ].
-
-
-ber_bin(suite) ->
- [
- ber_bin_test_msgs
- ].
-
-
-per(suite) ->
- [
- per_test_msgs
- ].
-
+all() ->
+ [{group, text}, {group, binary}, {group, erl_dist},
+ {group, tickets}].
+
+groups() ->
+ [{text, [],
+ [{group, pretty}, {group, flex_pretty},
+ {group, compact}, {group, flex_compact}]},
+ {binary, [],
+ [{group, bin}, {group, ber}, {group, ber_bin},
+ {group, per}, {group, per_bin}]},
+ {erl_dist, [], [{group, erl_dist_m}]},
+ {pretty, [], [pretty_test_msgs]},
+ {compact, [], [compact_test_msgs]},
+ {flex_pretty, [], flex_pretty_cases()},
+ {flex_compact, [], flex_compact_cases()},
+ {bin, [], [bin_test_msgs]}, {ber, [], [ber_test_msgs]},
+ {ber_bin, [], [ber_bin_test_msgs]},
+ {per, [], [per_test_msgs]},
+ {per_bin, [], [per_bin_test_msgs]},
+ {erl_dist_m, [], [erl_dist_m_test_msgs]},
+ {tickets, [],
+ [{group, compact_tickets},
+ {group, flex_compact_tickets}, {group, pretty_tickets},
+ {group, flex_pretty_tickets}]},
+ {compact_tickets, [],
+ [compact_otp4011_msg1, compact_otp4011_msg2,
+ compact_otp4011_msg3, compact_otp4013_msg1,
+ compact_otp4085_msg1, compact_otp4085_msg2,
+ compact_otp4280_msg1, compact_otp4299_msg1,
+ compact_otp4299_msg2, compact_otp4359_msg1,
+ compact_otp4920_msg0, compact_otp4920_msg1,
+ compact_otp4920_msg2, compact_otp4920_msg3,
+ compact_otp4920_msg4, compact_otp4920_msg5,
+ compact_otp4920_msg6, compact_otp4920_msg7,
+ compact_otp4920_msg8, compact_otp4920_msg9,
+ compact_otp4920_msg10, compact_otp4920_msg11,
+ compact_otp4920_msg12, compact_otp4920_msg20,
+ compact_otp4920_msg21, compact_otp4920_msg22,
+ compact_otp4920_msg23, compact_otp4920_msg24,
+ compact_otp4920_msg25, compact_otp5186_msg01,
+ compact_otp5186_msg02, compact_otp5186_msg03,
+ compact_otp5186_msg04, compact_otp5186_msg05,
+ compact_otp5186_msg06, compact_otp5793_msg01,
+ compact_otp5836_msg01, compact_otp5993_msg01,
+ compact_otp5993_msg02, compact_otp5993_msg03,
+ compact_otp6017_msg01, compact_otp6017_msg02,
+ compact_otp6017_msg03]},
+ {flex_compact_tickets, [],
+ flex_compact_tickets_cases()},
+ {pretty_tickets, [],
+ [pretty_otp4632_msg1, pretty_otp4632_msg2,
+ pretty_otp4632_msg3, pretty_otp4632_msg4,
+ pretty_otp4710_msg1, pretty_otp4710_msg2,
+ pretty_otp4945_msg1, pretty_otp4945_msg2,
+ pretty_otp4945_msg3, pretty_otp4945_msg4,
+ pretty_otp4945_msg5, pretty_otp4945_msg6,
+ pretty_otp4949_msg1, pretty_otp4949_msg2,
+ pretty_otp4949_msg3, pretty_otp5042_msg1,
+ pretty_otp5068_msg1, pretty_otp5085_msg1,
+ pretty_otp5085_msg2, pretty_otp5085_msg3,
+ pretty_otp5085_msg4, pretty_otp5085_msg5,
+ pretty_otp5085_msg6, pretty_otp5085_msg7,
+ pretty_otp5085_msg8, pretty_otp5600_msg1,
+ pretty_otp5600_msg2, pretty_otp5601_msg1,
+ pretty_otp5793_msg01, pretty_otp5803_msg01,
+ pretty_otp5803_msg02, pretty_otp5805_msg01,
+ pretty_otp5836_msg01, pretty_otp5882_msg01,
+ pretty_otp6490_msg01, pretty_otp6490_msg02,
+ pretty_otp6490_msg03, pretty_otp6490_msg04,
+ pretty_otp6490_msg05, pretty_otp6490_msg06,
+ pretty_otp7671_msg01, pretty_otp7671_msg02,
+ pretty_otp7671_msg03, pretty_otp7671_msg04,
+ pretty_otp7671_msg05, pretty_otp8114_msg01]},
+ {flex_pretty_tickets, [], flex_pretty_tickets_cases()}].
+
+init_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_init(Config);
+init_per_group(flex_compact_tickets, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_compact, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_pretty, Config) ->
+ flex_pretty_init(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(flex_compact_tickets, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_compact, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_pretty, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
+flex_pretty_cases() ->
+ [flex_pretty_test_msgs].
+
+flex_compact_cases() ->
+ [flex_compact_test_msgs, flex_compact_dm_timers1,
+ flex_compact_dm_timers2, flex_compact_dm_timers3,
+ flex_compact_dm_timers4, flex_compact_dm_timers5,
+ flex_compact_dm_timers6, flex_compact_dm_timers7,
+ flex_compact_dm_timers8].
%% Support for per_bin was added to ASN.1 as of version
%% 1.3.2 (R8). And later merged into 1.3.1.3 (R7). These
%% releases are identical (as far as I know).
%%
-per_bin(suite) ->
- [
- per_bin_test_msgs
- ].
-
-
-erl_dist_m(suite) ->
- [
- erl_dist_m_test_msgs
- ].
-
-tickets(suite) ->
- [
- compact_tickets,
- flex_compact_tickets,
- pretty_tickets,
- flex_pretty_tickets
- ].
+flex_compact_tickets_cases() ->
+ [flex_compact_otp7431_msg01, flex_compact_otp7431_msg02,
+ flex_compact_otp7431_msg03, flex_compact_otp7431_msg04,
+ flex_compact_otp7431_msg05, flex_compact_otp7431_msg06,
+ flex_compact_otp7431_msg07].
+
+flex_pretty_tickets_cases() ->
+ [flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1,
+ flex_pretty_otp5085_msg2, flex_pretty_otp5085_msg3,
+ flex_pretty_otp5085_msg4, flex_pretty_otp5085_msg5,
+ flex_pretty_otp5085_msg6, flex_pretty_otp5085_msg7,
+ flex_pretty_otp5085_msg8, flex_pretty_otp5600_msg1,
+ flex_pretty_otp5600_msg2, flex_pretty_otp5601_msg1,
+ flex_pretty_otp5793_msg01, flex_pretty_otp5803_msg01,
+ flex_pretty_otp5803_msg02, flex_pretty_otp5805_msg01,
+ flex_pretty_otp5836_msg01, flex_pretty_otp7431_msg01,
+ flex_pretty_otp7431_msg02, flex_pretty_otp7431_msg03,
+ flex_pretty_otp7431_msg04, flex_pretty_otp7431_msg05,
+ flex_pretty_otp7431_msg06, flex_pretty_otp7431_msg07].
-compact_tickets(suite) ->
- [
- compact_otp4011_msg1,
- compact_otp4011_msg2,
- compact_otp4011_msg3,
- compact_otp4013_msg1,
- compact_otp4085_msg1,
- compact_otp4085_msg2,
- compact_otp4280_msg1,
- compact_otp4299_msg1,
- compact_otp4299_msg2,
- compact_otp4359_msg1,
- compact_otp4920_msg0,
- compact_otp4920_msg1,
- compact_otp4920_msg2,
- compact_otp4920_msg3,
- compact_otp4920_msg4,
- compact_otp4920_msg5,
- compact_otp4920_msg6,
- compact_otp4920_msg7,
- compact_otp4920_msg8,
- compact_otp4920_msg9,
- compact_otp4920_msg10,
- compact_otp4920_msg11,
- compact_otp4920_msg12,
- compact_otp4920_msg20,
- compact_otp4920_msg21,
- compact_otp4920_msg22,
- compact_otp4920_msg23,
- compact_otp4920_msg24,
- compact_otp4920_msg25,
- compact_otp5186_msg01,
- compact_otp5186_msg02,
- compact_otp5186_msg03,
- compact_otp5186_msg04,
- compact_otp5186_msg05,
- compact_otp5186_msg06,
- compact_otp5793_msg01,
- compact_otp5836_msg01,
- compact_otp5993_msg01,
- compact_otp5993_msg02,
- compact_otp5993_msg03,
- compact_otp6017_msg01,
- compact_otp6017_msg02,
- compact_otp6017_msg03
- ].
-
-flex_compact_tickets(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_tickets_cases(),
- flex_compact_finish}}.
-
-flex_compact_tickets_cases() ->
- [
- flex_compact_otp7431_msg01,
- flex_compact_otp7431_msg02,
- flex_compact_otp7431_msg03,
- flex_compact_otp7431_msg04,
- flex_compact_otp7431_msg05,
- flex_compact_otp7431_msg06,
- flex_compact_otp7431_msg07
- ].
-
-pretty_tickets(suite) ->
- [
- pretty_otp4632_msg1,
- pretty_otp4632_msg2,
- pretty_otp4632_msg3,
- pretty_otp4632_msg4,
- pretty_otp4710_msg1,
- pretty_otp4710_msg2,
- pretty_otp4945_msg1,
- pretty_otp4945_msg2,
- pretty_otp4945_msg3,
- pretty_otp4945_msg4,
- pretty_otp4945_msg5,
- pretty_otp4945_msg6,
- pretty_otp4949_msg1,
- pretty_otp4949_msg2,
- pretty_otp4949_msg3,
- pretty_otp5042_msg1,
- pretty_otp5068_msg1,
- pretty_otp5085_msg1,
- pretty_otp5085_msg2,
- pretty_otp5085_msg3,
- pretty_otp5085_msg4,
- pretty_otp5085_msg5,
- pretty_otp5085_msg6,
- pretty_otp5085_msg7,
- pretty_otp5085_msg8,
- pretty_otp5600_msg1,
- pretty_otp5600_msg2,
- pretty_otp5601_msg1,
- pretty_otp5793_msg01,
- pretty_otp5803_msg01,
- pretty_otp5803_msg02,
- pretty_otp5805_msg01,
- pretty_otp5836_msg01,
- pretty_otp5882_msg01,
- pretty_otp6490_msg01,
- pretty_otp6490_msg02,
- pretty_otp6490_msg03,
- pretty_otp6490_msg04,
- pretty_otp6490_msg05,
- pretty_otp6490_msg06,
- pretty_otp7671_msg01,
- pretty_otp7671_msg02,
- pretty_otp7671_msg03,
- pretty_otp7671_msg04,
- pretty_otp7671_msg05,
- pretty_otp8114_msg01
- ].
+%% ----
-flex_pretty_tickets(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_tickets_cases(),
- flex_pretty_finish}}.
-
-flex_pretty_tickets_cases() ->
- [
- flex_pretty_otp5042_msg1,
- flex_pretty_otp5085_msg1,
- flex_pretty_otp5085_msg2,
- flex_pretty_otp5085_msg3,
- flex_pretty_otp5085_msg4,
- flex_pretty_otp5085_msg5,
- flex_pretty_otp5085_msg6,
- flex_pretty_otp5085_msg7,
- flex_pretty_otp5085_msg8,
- flex_pretty_otp5600_msg1,
- flex_pretty_otp5600_msg2,
- flex_pretty_otp5601_msg1,
- flex_pretty_otp5793_msg01,
- flex_pretty_otp5803_msg01,
- flex_pretty_otp5803_msg02,
- flex_pretty_otp5805_msg01,
- flex_pretty_otp5836_msg01,
- flex_pretty_otp7431_msg01,
- flex_pretty_otp7431_msg02,
- flex_pretty_otp7431_msg03,
- flex_pretty_otp7431_msg04,
- flex_pretty_otp7431_msg05,
- flex_pretty_otp7431_msg06,
- flex_pretty_otp7431_msg07
- ].
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_codec_prev3c_test.erl b/lib/megaco/test/megaco_codec_prev3c_test.erl
index 813d0cf57d..7f9c0fe4e7 100644
--- a/lib/megaco/test/megaco_codec_prev3c_test.erl
+++ b/lib/megaco/test/megaco_codec_prev3c_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,22 +37,17 @@
-export([t/0, t/1]).
--export([all/1,
-
- text/1,
-
- pretty/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
+
pretty_test_msgs/1,
-
- compact/1,
+
compact_test_msgs/1,
-
- flex_pretty/1,
+
flex_pretty_init/1,
flex_pretty_finish/1,
flex_pretty_test_msgs/1,
- flex_compact/1,
+
flex_compact_init/1,
flex_compact_finish/1,
flex_compact_test_msgs/1,
@@ -65,32 +60,21 @@
flex_compact_dm_timers6/1,
flex_compact_dm_timers7/1,
flex_compact_dm_timers8/1,
-
- binary/1,
- bin/1,
bin_test_msgs/1,
-
- ber/1,
+
ber_test_msgs/1,
-
- ber_bin/1,
+
ber_bin_test_msgs/1,
-
- per/1,
+
per_test_msgs/1,
-
- per_bin/1,
+
per_bin_test_msgs/1,
-
- erl_dist/1,
- erl_dist_m/1,
+
erl_dist_m_test_msgs/1,
tickets/0,
- tickets/1,
-
- compact_tickets/1,
+
compact_otp4011_msg1/1,
compact_otp4011_msg2/1,
compact_otp4011_msg3/1,
@@ -133,8 +117,7 @@
compact_otp6017_msg01/1,
compact_otp6017_msg02/1,
compact_otp6017_msg03/1,
-
- flex_compact_tickets/1,
+
flex_compact_otp4299_msg1/1,
flex_compact_otp7431_msg01/1,
flex_compact_otp7431_msg02/1,
@@ -143,8 +126,7 @@
flex_compact_otp7431_msg05/1,
flex_compact_otp7431_msg06/1,
flex_compact_otp7431_msg07/1,
-
- pretty_tickets/1,
+
pretty_otp4632_msg1/1,
pretty_otp4632_msg2/1,
pretty_otp4632_msg3/1,
@@ -191,8 +173,7 @@
pretty_otp7671_msg04/1,
pretty_otp7671_msg05/1,
pretty_otp8114_msg01/1,
-
- flex_pretty_tickets/1,
+
flex_pretty_otp5042_msg1/1,
flex_pretty_otp5085_msg1/1,
flex_pretty_otp5085_msg2/1,
@@ -218,7 +199,7 @@
flex_pretty_otp7431_msg06/1,
flex_pretty_otp7431_msg07/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([display_text_messages/0, generate_text_messages/0]).
@@ -286,32 +267,6 @@ expand([Case|Cases], Acc) ->
expand(Cases, [Case|Acc])
end.
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
-
-
%% ----
t() -> megaco_test_lib:t(?MODULE).
@@ -329,279 +284,169 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary,
- erl_dist,
- tickets
- ].
-
-text(suite) ->
- [
- pretty,
- flex_pretty,
- compact,
- flex_compact
- ].
-
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per,
- per_bin
- ].
-
-erl_dist(suite) ->
- [
- erl_dist_m
- ].
-
-pretty(suite) ->
- [
- pretty_test_msgs
- ].
-
-
-compact(suite) ->
- [
- compact_test_msgs
- ].
-
-
-flex_pretty(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_cases(), flex_pretty_finish}}.
-
-flex_pretty_cases() ->
- [
- flex_pretty_test_msgs
- ].
-
-flex_compact(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_cases(), flex_compact_finish}}.
-
-flex_compact_cases() ->
- [
- flex_compact_test_msgs,
- flex_compact_dm_timers1,
- flex_compact_dm_timers2,
- flex_compact_dm_timers3,
- flex_compact_dm_timers4,
- flex_compact_dm_timers5,
- flex_compact_dm_timers6,
- flex_compact_dm_timers7,
- flex_compact_dm_timers8
- ].
-
-
-bin(suite) ->
- [
- bin_test_msgs
- ].
-
-
-ber(suite) ->
- [
- ber_test_msgs
- ].
-
-
-ber_bin(suite) ->
- [
- ber_bin_test_msgs
- ].
-
-
-per(suite) ->
- [
- per_test_msgs
- ].
-
+all() ->
+ [{group, text}, {group, binary}, {group, erl_dist},
+ {group, tickets}].
+
+groups() ->
+ [{text, [],
+ [{group, pretty}, {group, flex_pretty},
+ {group, compact}, {group, flex_compact}]},
+ {binary, [],
+ [{group, bin}, {group, ber}, {group, ber_bin},
+ {group, per}, {group, per_bin}]},
+ {erl_dist, [], [{group, erl_dist_m}]},
+ {pretty, [], [pretty_test_msgs]},
+ {compact, [], [compact_test_msgs]},
+ {flex_pretty, [], flex_pretty_cases()},
+ {flex_compact, [], flex_compact_cases()},
+ {bin, [], [bin_test_msgs]}, {ber, [], [ber_test_msgs]},
+ {ber_bin, [], [ber_bin_test_msgs]},
+ {per, [], [per_test_msgs]},
+ {per_bin, [], [per_bin_test_msgs]},
+ {erl_dist_m, [], [erl_dist_m_test_msgs]},
+ {tickets, [],
+ [{group, compact_tickets},
+ {group, flex_compact_tickets}, {group, pretty_tickets},
+ {group, flex_pretty_tickets}]},
+ {compact_tickets, [],
+ [compact_otp4011_msg1, compact_otp4011_msg2,
+ compact_otp4011_msg3, compact_otp4013_msg1,
+ compact_otp4085_msg1, compact_otp4085_msg2,
+ compact_otp4280_msg1, compact_otp4299_msg1,
+ compact_otp4359_msg1, compact_otp4920_msg0,
+ compact_otp4920_msg1, compact_otp4920_msg2,
+ compact_otp4920_msg3, compact_otp4920_msg4,
+ compact_otp4920_msg5, compact_otp4920_msg6,
+ compact_otp4920_msg7, compact_otp4920_msg8,
+ compact_otp4920_msg9, compact_otp4920_msg10,
+ compact_otp4920_msg11, compact_otp4920_msg12,
+ compact_otp4920_msg20, compact_otp4920_msg21,
+ compact_otp4920_msg22, compact_otp4920_msg23,
+ compact_otp4920_msg24, compact_otp4920_msg25,
+ compact_otp5186_msg01, compact_otp5186_msg02,
+ compact_otp5186_msg03, compact_otp5186_msg04,
+ compact_otp5186_msg05, compact_otp5186_msg06,
+ compact_otp5793_msg01, compact_otp5836_msg01,
+ compact_otp5993_msg01, compact_otp5993_msg02,
+ compact_otp5993_msg03, compact_otp6017_msg01,
+ compact_otp6017_msg02, compact_otp6017_msg03]},
+ {flex_compact_tickets, [],
+ flex_compact_tickets_cases()},
+ {pretty_tickets, [],
+ [pretty_otp4632_msg1, pretty_otp4632_msg2,
+ pretty_otp4632_msg3, pretty_otp4632_msg4,
+ pretty_otp4710_msg1, pretty_otp4710_msg2,
+ pretty_otp4945_msg1, pretty_otp4945_msg2,
+ pretty_otp4945_msg3, pretty_otp4945_msg4,
+ pretty_otp4945_msg5, pretty_otp4945_msg6,
+ pretty_otp4949_msg1, pretty_otp4949_msg2,
+ pretty_otp4949_msg3, pretty_otp5042_msg1,
+ pretty_otp5068_msg1, pretty_otp5085_msg1,
+ pretty_otp5085_msg2, pretty_otp5085_msg3,
+ pretty_otp5085_msg4, pretty_otp5085_msg5,
+ pretty_otp5085_msg6, pretty_otp5085_msg7,
+ pretty_otp5085_msg8, pretty_otp5600_msg1,
+ pretty_otp5600_msg2, pretty_otp5601_msg1,
+ pretty_otp5793_msg01, pretty_otp5803_msg01,
+ pretty_otp5803_msg02, pretty_otp5805_msg01,
+ pretty_otp5836_msg01, pretty_otp5882_msg01,
+ pretty_otp6490_msg01, pretty_otp6490_msg02,
+ pretty_otp6490_msg03, pretty_otp6490_msg04,
+ pretty_otp6490_msg05, pretty_otp6490_msg06,
+ pretty_otp7671_msg01, pretty_otp7671_msg02,
+ pretty_otp7671_msg03, pretty_otp7671_msg04,
+ pretty_otp7671_msg05, pretty_otp8114_msg01]},
+ {flex_pretty_tickets, [], flex_pretty_tickets_cases()}].
+
+init_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_init(Config);
+init_per_group(flex_compact_tickets, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_compact, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_pretty, Config) ->
+ flex_pretty_init(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(flex_compact_tickets, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_compact, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_pretty, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
+flex_pretty_cases() ->
+ [flex_pretty_test_msgs].
+
+flex_compact_cases() ->
+ [flex_compact_test_msgs, flex_compact_dm_timers1,
+ flex_compact_dm_timers2, flex_compact_dm_timers3,
+ flex_compact_dm_timers4, flex_compact_dm_timers5,
+ flex_compact_dm_timers6, flex_compact_dm_timers7,
+ flex_compact_dm_timers8].
%% Support for per_bin was added to ASN.1 as of version
%% 1.3.2 (R8). And later merged into 1.3.1.3 (R7). These
%% releases are identical (as far as I know).
%%
-per_bin(suite) ->
- [
- per_bin_test_msgs
- ].
-
-
-erl_dist_m(suite) ->
- [
- erl_dist_m_test_msgs
- ].
-
-tickets(suite) ->
- [
- compact_tickets,
- flex_compact_tickets,
- pretty_tickets,
- flex_pretty_tickets
- ].
-
-
-compact_tickets(suite) ->
- [
- compact_otp4011_msg1,
- compact_otp4011_msg2,
- compact_otp4011_msg3,
- compact_otp4013_msg1,
- compact_otp4085_msg1,
- compact_otp4085_msg2,
- compact_otp4280_msg1,
- compact_otp4299_msg1,
- compact_otp4359_msg1,
- compact_otp4920_msg0,
- compact_otp4920_msg1,
- compact_otp4920_msg2,
- compact_otp4920_msg3,
- compact_otp4920_msg4,
- compact_otp4920_msg5,
- compact_otp4920_msg6,
- compact_otp4920_msg7,
- compact_otp4920_msg8,
- compact_otp4920_msg9,
- compact_otp4920_msg10,
- compact_otp4920_msg11,
- compact_otp4920_msg12,
- compact_otp4920_msg20,
- compact_otp4920_msg21,
- compact_otp4920_msg22,
- compact_otp4920_msg23,
- compact_otp4920_msg24,
- compact_otp4920_msg25,
- compact_otp5186_msg01,
- compact_otp5186_msg02,
- compact_otp5186_msg03,
- compact_otp5186_msg04,
- compact_otp5186_msg05,
- compact_otp5186_msg06,
- compact_otp5793_msg01,
- compact_otp5836_msg01,
- compact_otp5993_msg01,
- compact_otp5993_msg02,
- compact_otp5993_msg03,
- compact_otp6017_msg01,
- compact_otp6017_msg02,
- compact_otp6017_msg03
- ].
-
-
-flex_compact_tickets(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_tickets_cases(),
- flex_compact_finish}}.
-flex_compact_tickets_cases() ->
- [
- flex_compact_otp4299_msg1,
- flex_compact_otp7431_msg01,
- flex_compact_otp7431_msg02,
- flex_compact_otp7431_msg03,
- flex_compact_otp7431_msg04,
- flex_compact_otp7431_msg05,
- flex_compact_otp7431_msg06,
- flex_compact_otp7431_msg07
- ].
-
-
-pretty_tickets(suite) ->
- [
- pretty_otp4632_msg1,
- pretty_otp4632_msg2,
- pretty_otp4632_msg3,
- pretty_otp4632_msg4,
- pretty_otp4710_msg1,
- pretty_otp4710_msg2,
- pretty_otp4945_msg1,
- pretty_otp4945_msg2,
- pretty_otp4945_msg3,
- pretty_otp4945_msg4,
- pretty_otp4945_msg5,
- pretty_otp4945_msg6,
- pretty_otp4949_msg1,
- pretty_otp4949_msg2,
- pretty_otp4949_msg3,
- pretty_otp5042_msg1,
- pretty_otp5068_msg1,
- pretty_otp5085_msg1,
- pretty_otp5085_msg2,
- pretty_otp5085_msg3,
- pretty_otp5085_msg4,
- pretty_otp5085_msg5,
- pretty_otp5085_msg6,
- pretty_otp5085_msg7,
- pretty_otp5085_msg8,
- pretty_otp5600_msg1,
- pretty_otp5600_msg2,
- pretty_otp5601_msg1,
- pretty_otp5793_msg01,
- pretty_otp5803_msg01,
- pretty_otp5803_msg02,
- pretty_otp5805_msg01,
- pretty_otp5836_msg01,
- pretty_otp5882_msg01,
- pretty_otp6490_msg01,
- pretty_otp6490_msg02,
- pretty_otp6490_msg03,
- pretty_otp6490_msg04,
- pretty_otp6490_msg05,
- pretty_otp6490_msg06,
- pretty_otp7671_msg01,
- pretty_otp7671_msg02,
- pretty_otp7671_msg03,
- pretty_otp7671_msg04,
- pretty_otp7671_msg05,
- pretty_otp8114_msg01
- ].
+flex_compact_tickets_cases() ->
+ [flex_compact_otp4299_msg1, flex_compact_otp7431_msg01,
+ flex_compact_otp7431_msg02, flex_compact_otp7431_msg03,
+ flex_compact_otp7431_msg04, flex_compact_otp7431_msg05,
+ flex_compact_otp7431_msg06, flex_compact_otp7431_msg07].
+
+flex_pretty_tickets_cases() ->
+ [flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1,
+ flex_pretty_otp5085_msg2, flex_pretty_otp5085_msg3,
+ flex_pretty_otp5085_msg4, flex_pretty_otp5085_msg5,
+ flex_pretty_otp5085_msg6, flex_pretty_otp5085_msg7,
+ flex_pretty_otp5085_msg8, flex_pretty_otp5600_msg1,
+ flex_pretty_otp5600_msg2, flex_pretty_otp5601_msg1,
+ flex_pretty_otp5793_msg01, flex_pretty_otp5803_msg01,
+ flex_pretty_otp5803_msg02, flex_pretty_otp5805_msg01,
+ flex_pretty_otp5836_msg01, flex_pretty_otp7431_msg01,
+ flex_pretty_otp7431_msg02, flex_pretty_otp7431_msg03,
+ flex_pretty_otp7431_msg04, flex_pretty_otp7431_msg05,
+ flex_pretty_otp7431_msg06, flex_pretty_otp7431_msg07].
+%% ----
-flex_pretty_tickets(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_tickets_cases(),
- flex_pretty_finish}}.
-
-flex_pretty_tickets_cases() ->
- [
- flex_pretty_otp5042_msg1,
- flex_pretty_otp5085_msg1,
- flex_pretty_otp5085_msg2,
- flex_pretty_otp5085_msg3,
- flex_pretty_otp5085_msg4,
- flex_pretty_otp5085_msg5,
- flex_pretty_otp5085_msg6,
- flex_pretty_otp5085_msg7,
- flex_pretty_otp5085_msg8,
- flex_pretty_otp5600_msg1,
- flex_pretty_otp5600_msg2,
- flex_pretty_otp5601_msg1,
- flex_pretty_otp5793_msg01,
- flex_pretty_otp5803_msg01,
- flex_pretty_otp5803_msg02,
- flex_pretty_otp5805_msg01,
- flex_pretty_otp5836_msg01,
- flex_pretty_otp7431_msg01,
- flex_pretty_otp7431_msg02,
- flex_pretty_otp7431_msg03,
- flex_pretty_otp7431_msg04,
- flex_pretty_otp7431_msg05,
- flex_pretty_otp7431_msg06,
- flex_pretty_otp7431_msg07
- ].
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_codec_test.erl b/lib/megaco/test/megaco_codec_test.erl
index d247959cc5..8391024c3f 100644
--- a/lib/megaco/test/megaco_codec_test.erl
+++ b/lib/megaco/test/megaco_codec_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,8 +36,8 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
init() ->
process_flag(trap_exit, true),
@@ -47,17 +47,24 @@ init() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- codec
- ].
-
-codec(suite) ->
- [{megaco_codec_mini_test, all},
- {megaco_codec_v1_test, all},
- {megaco_codec_v2_test, all},
- {megaco_codec_prev3a_test, all},
- {megaco_codec_prev3b_test, all},
- {megaco_codec_prev3c_test, all},
- {megaco_codec_v3_test, all}].
+all() ->
+ [{group, codec}].
+
+groups() ->
+ [{codec, [],
+ [{megaco_codec_mini_test, all},
+ {megaco_codec_v1_test, all},
+ {megaco_codec_v2_test, all},
+ {megaco_codec_prev3a_test, all},
+ {megaco_codec_prev3b_test, all},
+ {megaco_codec_prev3c_test, all},
+ {megaco_codec_v3_test, all}]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
diff --git a/lib/megaco/test/megaco_codec_test_lib.erl b/lib/megaco/test/megaco_codec_test_lib.erl
index 66e8a52a24..0a903f5617 100644
--- a/lib/megaco/test/megaco_codec_test_lib.erl
+++ b/lib/megaco/test/megaco_codec_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_codec_v1_test.erl b/lib/megaco/test/megaco_codec_v1_test.erl
index 7f2af37282..3a548c4d9e 100644
--- a/lib/megaco/test/megaco_codec_v1_test.erl
+++ b/lib/megaco/test/megaco_codec_v1_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco.hrl").
-include_lib("megaco/include/megaco_message_v1.hrl").
-include("megaco_test_lib.hrl").
@@ -38,22 +42,16 @@
-export([t/0, t/1]).
--export([all/1,
-
- text/1,
-
- pretty/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
+
pretty_test_msgs/1,
-
- compact/1,
+
compact_test_msgs/1,
-
- flex_pretty/1,
+
flex_pretty_init/1,
flex_pretty_finish/1,
flex_pretty_test_msgs/1,
-
- flex_compact/1,
+
flex_compact_init/1,
flex_compact_finish/1,
flex_compact_test_msgs/1,
@@ -63,32 +61,21 @@
flex_compact_dm_timers4/1,
flex_compact_dm_timers5/1,
flex_compact_dm_timers6/1,
-
- binary/1,
- bin/1,
bin_test_msgs/1,
-
- ber/1,
+
ber_test_msgs/1,
-
- ber_bin/1,
+
ber_bin_test_msgs/1,
-
- per/1,
+
per_test_msgs/1,
-
- per_bin/1,
+
per_bin_test_msgs/1,
-
- erl_dist/1,
- erl_dist_m/1,
+
erl_dist_m_test_msgs/1,
tickets/0,
- tickets/1,
-
- compact_tickets/1,
+
compact_otp4011_msg1/1,
compact_otp4011_msg2/1,
compact_otp4011_msg3/1,
@@ -131,8 +118,7 @@
compact_otp6017_msg01/1,
compact_otp6017_msg02/1,
compact_otp6017_msg03/1,
-
- flex_compact_tickets/1,
+
flex_compact_otp7431_msg01a/1,
flex_compact_otp7431_msg01b/1,
flex_compact_otp7431_msg02/1,
@@ -141,8 +127,7 @@
flex_compact_otp7431_msg05/1,
flex_compact_otp7431_msg06/1,
flex_compact_otp7431_msg07/1,
-
- pretty_tickets/1,
+
pretty_otp4632_msg1/1,
pretty_otp4632_msg2/1,
pretty_otp4632_msg3/1,
@@ -184,7 +169,6 @@
pretty_otp7671_msg04/1,
pretty_otp7671_msg05/1,
- flex_pretty_tickets/1,
flex_pretty_otp5042_msg1/1,
flex_pretty_otp5085_msg1/1,
flex_pretty_otp5085_msg2/1,
@@ -205,7 +189,7 @@
flex_pretty_otp7431_msg06/1,
flex_pretty_otp7431_msg07/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([display_text_messages/0, generate_text_messages/0]).
@@ -451,31 +435,6 @@ expand([Case|Cases], Acc) ->
expand(Cases, [Case|Acc])
end.
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
-
%% ----
@@ -494,265 +453,210 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary,
- erl_dist,
- tickets
- ].
-
-text(suite) ->
- [
- pretty,
- flex_pretty,
- compact,
- flex_compact
- ].
-
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per,
- per_bin
- ].
-
-erl_dist(suite) ->
- [
- erl_dist_m
- ].
-
-pretty(suite) ->
- [
- pretty_test_msgs
- ].
-
-
-compact(suite) ->
- [
- compact_test_msgs
- ].
-
-
-flex_pretty(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_cases(), flex_pretty_finish}}.
-
-flex_pretty_cases() ->
- [
- flex_pretty_test_msgs
- ].
-
-
-flex_compact(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_cases(), flex_compact_finish}}.
-
-flex_compact_cases() ->
- [
- flex_compact_test_msgs,
-
- flex_compact_dm_timers1,
- flex_compact_dm_timers2,
- flex_compact_dm_timers3,
- flex_compact_dm_timers4,
- flex_compact_dm_timers5,
- flex_compact_dm_timers6
- ].
-
-
-bin(suite) ->
- [
- bin_test_msgs
- ].
-
-
-ber(suite) ->
+all() ->
[
- ber_test_msgs
- ].
-
-
-ber_bin(suite) ->
- [
- ber_bin_test_msgs
- ].
-
-
-per(suite) ->
- [
- per_test_msgs
+ {group, text},
+ {group, binary},
+ {group, erl_dist},
+ {group, tickets}
].
+groups() ->
+ [{text, [], [{group, pretty},
+ {group, flex_pretty},
+ {group, compact},
+ {group, flex_compact}]},
+ {binary, [], [{group, bin},
+ {group, ber},
+ {group, ber_bin},
+ {group, per},
+ {group, per_bin}]},
+ {erl_dist, [], [{group, erl_dist_m}]},
+ {pretty, [], [pretty_test_msgs]},
+ {compact, [], [compact_test_msgs]},
+ {flex_pretty, [], flex_pretty_cases()},
+ {flex_compact, [], flex_compact_cases()},
+ {bin, [], [bin_test_msgs]},
+ {ber, [], [ber_test_msgs]},
+ {ber_bin, [], [ber_bin_test_msgs]},
+ {per, [], [per_test_msgs]},
+ {per_bin, [], [per_bin_test_msgs]},
+ {erl_dist_m, [], [erl_dist_m_test_msgs]},
+ {tickets, [], [{group, compact_tickets},
+ {group, pretty_tickets},
+ {group, flex_compact_tickets},
+ {group, flex_pretty_tickets}]},
+ {compact_tickets, [], [compact_otp4011_msg1,
+ compact_otp4011_msg2,
+ compact_otp4011_msg3,
+ compact_otp4013_msg1,
+ compact_otp4085_msg1,
+ compact_otp4085_msg2,
+ compact_otp4280_msg1,
+ compact_otp4299_msg1,
+ compact_otp4299_msg2,
+ compact_otp4359_msg1,
+ compact_otp4920_msg0,
+ compact_otp4920_msg1,
+ compact_otp4920_msg2,
+ compact_otp4920_msg3,
+ compact_otp4920_msg4,
+ compact_otp4920_msg5,
+ compact_otp4920_msg6,
+ compact_otp4920_msg7,
+ compact_otp4920_msg8,
+ compact_otp4920_msg9,
+ compact_otp4920_msg10,
+ compact_otp4920_msg11,
+ compact_otp4920_msg12,
+ compact_otp4920_msg20,
+ compact_otp4920_msg21,
+ compact_otp4920_msg22,
+ compact_otp4920_msg23,
+ compact_otp4920_msg24,
+ compact_otp4920_msg25,
+ compact_otp5186_msg01,
+ compact_otp5186_msg02,
+ compact_otp5186_msg03,
+ compact_otp5186_msg04,
+ compact_otp5186_msg05,
+ compact_otp5186_msg06,
+ compact_otp5793_msg01,
+ compact_otp5993_msg01,
+ compact_otp5993_msg02,
+ compact_otp5993_msg03,
+ compact_otp6017_msg01,
+ compact_otp6017_msg02,
+ compact_otp6017_msg03]},
+ {flex_compact_tickets, [], flex_compact_tickets_cases()},
+ {pretty_tickets, [], [pretty_otp4632_msg1,
+ pretty_otp4632_msg2,
+ pretty_otp4632_msg3,
+ pretty_otp4632_msg4,
+ pretty_otp4710_msg1,
+ pretty_otp4710_msg2,
+ pretty_otp4945_msg1,
+ pretty_otp4945_msg2,
+ pretty_otp4945_msg3,
+ pretty_otp4945_msg4,
+ pretty_otp4945_msg5,
+ pretty_otp4945_msg6,
+ pretty_otp4949_msg1,
+ pretty_otp4949_msg2,
+ pretty_otp4949_msg3,
+ pretty_otp5042_msg1,
+ pretty_otp5068_msg1,
+ pretty_otp5085_msg1,
+ pretty_otp5085_msg2,
+ pretty_otp5085_msg3,
+ pretty_otp5085_msg4,
+ pretty_otp5085_msg5,
+ pretty_otp5085_msg6,
+ pretty_otp5085_msg7,
+ pretty_otp5600_msg1,
+ pretty_otp5600_msg2,
+ pretty_otp5601_msg1,
+ pretty_otp5793_msg01,
+ pretty_otp5882_msg01,
+ pretty_otp6490_msg01,
+ pretty_otp6490_msg02,
+ pretty_otp6490_msg03,
+ pretty_otp6490_msg04,
+ pretty_otp6490_msg05,
+ pretty_otp6490_msg06,
+ pretty_otp7671_msg01,
+ pretty_otp7671_msg02,
+ pretty_otp7671_msg03,
+ pretty_otp7671_msg04,
+ pretty_otp7671_msg05]},
+ {flex_pretty_tickets, [], flex_pretty_tickets_cases()}].
+
+init_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_init(Config);
+init_per_group(flex_compact_tickets, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_compact, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_pretty, Config) ->
+ flex_pretty_init(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(flex_compact_tickets, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_compact, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_pretty, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
+flex_pretty_cases() ->
+ [flex_pretty_test_msgs].
+
+flex_compact_cases() ->
+ [flex_compact_test_msgs, flex_compact_dm_timers1,
+ flex_compact_dm_timers2, flex_compact_dm_timers3,
+ flex_compact_dm_timers4, flex_compact_dm_timers5,
+ flex_compact_dm_timers6].
%% Support for per_bin was added to ASN.1 as of version
%% 1.3.2 (R8). And later merged into 1.3.1.3 (R7). These
%% releases are identical (as far as I know).
%%
-per_bin(suite) ->
- [
- per_bin_test_msgs
- ].
-
-erl_dist_m(suite) ->
- [
- erl_dist_m_test_msgs
- ].
-tickets(suite) ->
- [
- compact_tickets,
- pretty_tickets,
- flex_compact_tickets,
- flex_pretty_tickets
- ].
+flex_compact_tickets_cases() ->
+ [flex_compact_otp7431_msg01a,
+ flex_compact_otp7431_msg01b, flex_compact_otp7431_msg02,
+ flex_compact_otp7431_msg03, flex_compact_otp7431_msg04,
+ flex_compact_otp7431_msg05, flex_compact_otp7431_msg06,
+ flex_compact_otp7431_msg07].
+
+flex_pretty_tickets_cases() ->
+ [flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1,
+ flex_pretty_otp5085_msg2, flex_pretty_otp5085_msg3,
+ flex_pretty_otp5085_msg4, flex_pretty_otp5085_msg5,
+ flex_pretty_otp5085_msg6, flex_pretty_otp5085_msg7,
+ flex_pretty_otp5600_msg1, flex_pretty_otp5600_msg2,
+ flex_pretty_otp5601_msg1, flex_pretty_otp5793_msg01,
+ flex_pretty_otp7431_msg01, flex_pretty_otp7431_msg02,
+ flex_pretty_otp7431_msg03, flex_pretty_otp7431_msg04,
+ flex_pretty_otp7431_msg05, flex_pretty_otp7431_msg06,
+ flex_pretty_otp7431_msg07].
+%% ----
-compact_tickets(suite) ->
- [
- compact_otp4011_msg1,
- compact_otp4011_msg2,
- compact_otp4011_msg3,
- compact_otp4013_msg1,
- compact_otp4085_msg1,
- compact_otp4085_msg2,
- compact_otp4280_msg1,
- compact_otp4299_msg1,
- compact_otp4299_msg2,
- compact_otp4359_msg1,
- compact_otp4920_msg0,
- compact_otp4920_msg1,
- compact_otp4920_msg2,
- compact_otp4920_msg3,
- compact_otp4920_msg4,
- compact_otp4920_msg5,
- compact_otp4920_msg6,
- compact_otp4920_msg7,
- compact_otp4920_msg8,
- compact_otp4920_msg9,
- compact_otp4920_msg10,
- compact_otp4920_msg11,
- compact_otp4920_msg12,
- compact_otp4920_msg20,
- compact_otp4920_msg21,
- compact_otp4920_msg22,
- compact_otp4920_msg23,
- compact_otp4920_msg24,
- compact_otp4920_msg25,
- compact_otp5186_msg01,
- compact_otp5186_msg02,
- compact_otp5186_msg03,
- compact_otp5186_msg04,
- compact_otp5186_msg05,
- compact_otp5186_msg06,
- compact_otp5793_msg01,
- compact_otp5993_msg01,
- compact_otp5993_msg02,
- compact_otp5993_msg03,
- compact_otp6017_msg01,
- compact_otp6017_msg02,
- compact_otp6017_msg03
- ].
-
-flex_compact_tickets(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_tickets_cases(),
- flex_compact_finish}}.
-
-flex_compact_tickets_cases() ->
- [
- flex_compact_otp7431_msg01a,
- flex_compact_otp7431_msg01b,
- flex_compact_otp7431_msg02,
- flex_compact_otp7431_msg03,
- flex_compact_otp7431_msg04,
- flex_compact_otp7431_msg05,
- flex_compact_otp7431_msg06,
- flex_compact_otp7431_msg07
- ].
-
-
-pretty_tickets(suite) ->
- [
- pretty_otp4632_msg1,
- pretty_otp4632_msg2,
- pretty_otp4632_msg3,
- pretty_otp4632_msg4,
- pretty_otp4710_msg1,
- pretty_otp4710_msg2,
- pretty_otp4945_msg1,
- pretty_otp4945_msg2,
- pretty_otp4945_msg3,
- pretty_otp4945_msg4,
- pretty_otp4945_msg5,
- pretty_otp4945_msg6,
- pretty_otp4949_msg1,
- pretty_otp4949_msg2,
- pretty_otp4949_msg3,
- pretty_otp5042_msg1,
- pretty_otp5068_msg1,
- pretty_otp5085_msg1,
- pretty_otp5085_msg2,
- pretty_otp5085_msg3,
- pretty_otp5085_msg4,
- pretty_otp5085_msg5,
- pretty_otp5085_msg6,
- pretty_otp5085_msg7,
- pretty_otp5600_msg1,
- pretty_otp5600_msg2,
- pretty_otp5601_msg1,
- pretty_otp5793_msg01,
- pretty_otp5882_msg01,
- pretty_otp6490_msg01,
- pretty_otp6490_msg02,
- pretty_otp6490_msg03,
- pretty_otp6490_msg04,
- pretty_otp6490_msg05,
- pretty_otp6490_msg06,
- pretty_otp7671_msg01,
- pretty_otp7671_msg02,
- pretty_otp7671_msg03,
- pretty_otp7671_msg04,
- pretty_otp7671_msg05
- ].
-
-flex_pretty_tickets(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_tickets_cases(),
- flex_pretty_finish}}.
-
-flex_pretty_tickets_cases() ->
- [
- flex_pretty_otp5042_msg1,
- flex_pretty_otp5085_msg1,
- flex_pretty_otp5085_msg2,
- flex_pretty_otp5085_msg3,
- flex_pretty_otp5085_msg4,
- flex_pretty_otp5085_msg5,
- flex_pretty_otp5085_msg6,
- flex_pretty_otp5085_msg7,
- flex_pretty_otp5600_msg1,
- flex_pretty_otp5600_msg2,
- flex_pretty_otp5601_msg1,
- flex_pretty_otp5793_msg01,
- flex_pretty_otp7431_msg01,
- flex_pretty_otp7431_msg02,
- flex_pretty_otp7431_msg03,
- flex_pretty_otp7431_msg04,
- flex_pretty_otp7431_msg05,
- flex_pretty_otp7431_msg06,
- flex_pretty_otp7431_msg07
- ].
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_codec_v2_test.erl b/lib/megaco/test/megaco_codec_v2_test.erl
index 1df1c6c93b..c3a80febba 100644
--- a/lib/megaco/test/megaco_codec_v2_test.erl
+++ b/lib/megaco/test/megaco_codec_v2_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,22 +37,16 @@
-export([t/0, t/1]).
--export([all/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
- text/1,
-
- pretty/1,
pretty_test_msgs/1,
- compact/1,
compact_test_msgs/1,
-
- flex_pretty/1,
+
flex_pretty_init/1,
flex_pretty_finish/1,
flex_pretty_test_msgs/1,
-
- flex_compact/1,
+
flex_compact_init/1,
flex_compact_finish/1,
flex_compact_test_msgs/1,
@@ -65,32 +59,21 @@
flex_compact_dm_timers6/1,
flex_compact_dm_timers7/1,
flex_compact_dm_timers8/1,
-
- binary/1,
- bin/1,
bin_test_msgs/1,
- ber/1,
ber_test_msgs/1,
-
- ber_bin/1,
+
ber_bin_test_msgs/1,
-
- per/1,
+
per_test_msgs/1,
-
- per_bin/1,
+
per_bin_test_msgs/1,
-
- erl_dist/1,
- erl_dist_m/1,
+
erl_dist_m_test_msgs/1,
tickets/0,
- tickets/1,
-
- compact_tickets/1,
+
compact_otp4011_msg1/1,
compact_otp4011_msg2/1,
compact_otp4011_msg3/1,
@@ -143,8 +126,7 @@
compact_otp7534_msg01/1,
compact_otp7576_msg01/1,
compact_otp7671_msg01/1,
-
- flex_compact_tickets/1,
+
flex_compact_otp7138_msg01/1,
flex_compact_otp7138_msg02/1,
flex_compact_otp7431_msg01/1,
@@ -160,8 +142,7 @@
flex_compact_otp7534_msg01/1,
flex_compact_otp7573_msg01/1,
flex_compact_otp7576_msg01/1,
-
- pretty_tickets/1,
+
pretty_otp4632_msg1/1,
pretty_otp4632_msg2/1,
pretty_otp4632_msg3/1,
@@ -204,7 +185,6 @@
pretty_otp7671_msg04/1,
pretty_otp7671_msg05/1,
- flex_pretty_tickets/1,
flex_pretty_otp5042_msg1/1,
flex_pretty_otp5085_msg1/1,
flex_pretty_otp5085_msg2/1,
@@ -225,7 +205,7 @@
flex_pretty_otp7431_msg06/1,
flex_pretty_otp7431_msg07/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([display_text_messages/0, generate_text_messages/0]).
@@ -431,31 +411,7 @@ expand([Case|Cases], Acc) ->
expand(Cases, [Case|Acc])
end.
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
-
+
%% ----
@@ -474,284 +430,202 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary,
- erl_dist,
- tickets
- ].
-
-text(suite) ->
- [
- pretty,
- flex_pretty,
- compact,
- flex_compact
- ].
-
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per,
- per_bin
- ].
-
-erl_dist(suite) ->
- [
- erl_dist_m
- ].
-
-pretty(suite) ->
- [
- pretty_test_msgs
- ].
-
-
-compact(suite) ->
- [
- compact_test_msgs
- ].
+all() ->
+[{group, text}, {group, binary}, {group, erl_dist},
+ {group, tickets}].
+
+groups() ->
+ [{text, [],
+ [{group, pretty}, {group, flex_pretty},
+ {group, compact}, {group, flex_compact}]},
+ {binary, [],
+ [{group, bin}, {group, ber}, {group, ber_bin},
+ {group, per}, {group, per_bin}]},
+ {erl_dist, [], [{group, erl_dist_m}]},
+ {pretty, [], [pretty_test_msgs]},
+ {compact, [], [compact_test_msgs]},
+ {flex_pretty, [], flex_pretty_cases()},
+ {flex_compact, [], flex_compact_cases()},
+ {bin, [], [bin_test_msgs]}, {ber, [], [ber_test_msgs]},
+ {ber_bin, [], [ber_bin_test_msgs]},
+ {per, [], [per_test_msgs]},
+ {per_bin, [], [per_bin_test_msgs]},
+ {erl_dist_m, [], [erl_dist_m_test_msgs]},
+ {tickets, [],
+ [{group, compact_tickets}, {group, pretty_tickets},
+ {group, flex_compact_tickets},
+ {group, flex_pretty_tickets}]},
+ {compact_tickets, [],
+ [compact_otp4011_msg1, compact_otp4011_msg2,
+ compact_otp4011_msg3, compact_otp4013_msg1,
+ compact_otp4085_msg1, compact_otp4085_msg2,
+ compact_otp4280_msg1, compact_otp4299_msg1,
+ compact_otp4299_msg2, compact_otp4359_msg1,
+ compact_otp4920_msg0, compact_otp4920_msg1,
+ compact_otp4920_msg2, compact_otp4920_msg3,
+ compact_otp4920_msg4, compact_otp4920_msg5,
+ compact_otp4920_msg6, compact_otp4920_msg7,
+ compact_otp4920_msg8, compact_otp4920_msg9,
+ compact_otp4920_msg10, compact_otp4920_msg11,
+ compact_otp4920_msg12, compact_otp4920_msg20,
+ compact_otp4920_msg21, compact_otp4920_msg22,
+ compact_otp4920_msg23, compact_otp4920_msg24,
+ compact_otp4920_msg25, compact_otp5186_msg01,
+ compact_otp5186_msg02, compact_otp5186_msg03,
+ compact_otp5186_msg04, compact_otp5186_msg05,
+ compact_otp5186_msg06, compact_otp5290_msg01,
+ compact_otp5290_msg02, compact_otp5793_msg01,
+ compact_otp5993_msg01, compact_otp5993_msg02,
+ compact_otp5993_msg03, compact_otp6017_msg01,
+ compact_otp6017_msg02, compact_otp6017_msg03,
+ compact_otp7138_msg01, compact_otp7138_msg02,
+ compact_otp7457_msg01, compact_otp7457_msg02,
+ compact_otp7457_msg03, compact_otp7534_msg01,
+ compact_otp7576_msg01, compact_otp7671_msg01]},
+ {flex_compact_tickets, [],
+ flex_compact_tickets_cases()},
+ {pretty_tickets, [],
+ [pretty_otp4632_msg1, pretty_otp4632_msg2,
+ pretty_otp4632_msg3, pretty_otp4632_msg4,
+ pretty_otp4710_msg1, pretty_otp4710_msg2,
+ pretty_otp4945_msg1, pretty_otp4945_msg2,
+ pretty_otp4945_msg3, pretty_otp4945_msg4,
+ pretty_otp4945_msg5, pretty_otp4945_msg6,
+ pretty_otp4949_msg1, pretty_otp4949_msg2,
+ pretty_otp4949_msg3, pretty_otp5042_msg1,
+ pretty_otp5068_msg1, pretty_otp5085_msg1,
+ pretty_otp5085_msg2, pretty_otp5085_msg3,
+ pretty_otp5085_msg4, pretty_otp5085_msg5,
+ pretty_otp5085_msg6, pretty_otp5085_msg7,
+ pretty_otp5600_msg1, pretty_otp5600_msg2,
+ pretty_otp5601_msg1, pretty_otp5793_msg01,
+ pretty_otp5882_msg01, pretty_otp6490_msg01,
+ pretty_otp6490_msg02, pretty_otp6490_msg03,
+ pretty_otp6490_msg04, pretty_otp6490_msg05,
+ pretty_otp6490_msg06, pretty_otp7249_msg01,
+ pretty_otp7671_msg01, pretty_otp7671_msg02,
+ pretty_otp7671_msg03, pretty_otp7671_msg04,
+ pretty_otp7671_msg05]},
+ {flex_pretty_tickets, [], flex_pretty_tickets_cases()}].
+
+init_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_init(Config);
+init_per_group(flex_compact_tickets, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_compact, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_pretty, Config) ->
+ flex_pretty_init(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(flex_compact_tickets, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_compact, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_pretty, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
+
+
+
+
+
+
+flex_pretty_cases() ->
+[flex_pretty_test_msgs].
+
+
+flex_compact_cases() ->
+[flex_compact_test_msgs, flex_compact_dm_timers1,
+ flex_compact_dm_timers2, flex_compact_dm_timers3,
+ flex_compact_dm_timers4, flex_compact_dm_timers5,
+ flex_compact_dm_timers6, flex_compact_dm_timers7,
+ flex_compact_dm_timers8].
-flex_pretty(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_cases(), flex_pretty_finish}}.
-flex_pretty_cases() ->
- [
- flex_pretty_test_msgs
- ].
-flex_compact(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_cases(), flex_compact_finish}}.
-flex_compact_cases() ->
- [
- flex_compact_test_msgs,
- flex_compact_dm_timers1,
- flex_compact_dm_timers2,
- flex_compact_dm_timers3,
- flex_compact_dm_timers4,
- flex_compact_dm_timers5,
- flex_compact_dm_timers6,
- flex_compact_dm_timers7,
- flex_compact_dm_timers8
- ].
-bin(suite) ->
- [
- bin_test_msgs
- ].
-ber(suite) ->
- [
- ber_test_msgs
- ].
-
-
-ber_bin(suite) ->
- [
- ber_bin_test_msgs
- ].
-
-
-per(suite) ->
- [
- per_test_msgs
- ].
-
%% Support for per_bin was added to ASN.1 as of version
%% 1.3.2 (R8). And later merged into 1.3.1.3 (R7). These
%% releases are identical (as far as I know).
%%
-per_bin(suite) ->
- [
- per_bin_test_msgs
- ].
-erl_dist_m(suite) ->
- [
- erl_dist_m_test_msgs
- ].
-tickets(suite) ->
- [
- compact_tickets,
- pretty_tickets,
- flex_compact_tickets,
- flex_pretty_tickets
- ].
-compact_tickets(suite) ->
- [
- compact_otp4011_msg1,
- compact_otp4011_msg2,
- compact_otp4011_msg3,
- compact_otp4013_msg1,
- compact_otp4085_msg1,
- compact_otp4085_msg2,
- compact_otp4280_msg1,
- compact_otp4299_msg1,
- compact_otp4299_msg2,
- compact_otp4359_msg1,
- compact_otp4920_msg0,
- compact_otp4920_msg1,
- compact_otp4920_msg2,
- compact_otp4920_msg3,
- compact_otp4920_msg4,
- compact_otp4920_msg5,
- compact_otp4920_msg6,
- compact_otp4920_msg7,
- compact_otp4920_msg8,
- compact_otp4920_msg9,
- compact_otp4920_msg10,
- compact_otp4920_msg11,
- compact_otp4920_msg12,
- compact_otp4920_msg20,
- compact_otp4920_msg21,
- compact_otp4920_msg22,
- compact_otp4920_msg23,
- compact_otp4920_msg24,
- compact_otp4920_msg25,
- compact_otp5186_msg01,
- compact_otp5186_msg02,
- compact_otp5186_msg03,
- compact_otp5186_msg04,
- compact_otp5186_msg05,
- compact_otp5186_msg06,
- compact_otp5290_msg01,
- compact_otp5290_msg02,
- compact_otp5793_msg01,
- compact_otp5993_msg01,
- compact_otp5993_msg02,
- compact_otp5993_msg03,
- compact_otp6017_msg01,
- compact_otp6017_msg02,
- compact_otp6017_msg03,
- compact_otp7138_msg01,
- compact_otp7138_msg02,
- compact_otp7457_msg01,
- compact_otp7457_msg02,
- compact_otp7457_msg03,
- compact_otp7534_msg01,
- compact_otp7576_msg01,
- compact_otp7671_msg01
- ].
-flex_compact_tickets(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_tickets_cases(),
- flex_compact_finish}}.
-flex_compact_tickets_cases() ->
- [
- flex_compact_otp7138_msg01,
- flex_compact_otp7138_msg02,
- flex_compact_otp7431_msg01,
- flex_compact_otp7431_msg02,
- flex_compact_otp7431_msg03,
- flex_compact_otp7431_msg04,
- flex_compact_otp7431_msg05,
- flex_compact_otp7431_msg06,
- flex_compact_otp7431_msg07,
- flex_compact_otp7138_msg02,
- flex_compact_otp7457_msg01,
- flex_compact_otp7457_msg02,
- flex_compact_otp7457_msg03,
- flex_compact_otp7534_msg01,
- flex_compact_otp7573_msg01,
- flex_compact_otp7576_msg01
- ].
+flex_compact_tickets_cases() ->
+[flex_compact_otp7138_msg01, flex_compact_otp7138_msg02,
+ flex_compact_otp7431_msg01, flex_compact_otp7431_msg02,
+ flex_compact_otp7431_msg03, flex_compact_otp7431_msg04,
+ flex_compact_otp7431_msg05, flex_compact_otp7431_msg06,
+ flex_compact_otp7431_msg07, flex_compact_otp7138_msg02,
+ flex_compact_otp7457_msg01, flex_compact_otp7457_msg02,
+ flex_compact_otp7457_msg03, flex_compact_otp7534_msg01,
+ flex_compact_otp7573_msg01, flex_compact_otp7576_msg01].
-pretty_tickets(suite) ->
- [
- pretty_otp4632_msg1,
- pretty_otp4632_msg2,
- pretty_otp4632_msg3,
- pretty_otp4632_msg4,
- pretty_otp4710_msg1,
- pretty_otp4710_msg2,
- pretty_otp4945_msg1,
- pretty_otp4945_msg2,
- pretty_otp4945_msg3,
- pretty_otp4945_msg4,
- pretty_otp4945_msg5,
- pretty_otp4945_msg6,
- pretty_otp4949_msg1,
- pretty_otp4949_msg2,
- pretty_otp4949_msg3,
- pretty_otp5042_msg1,
- pretty_otp5068_msg1,
- pretty_otp5085_msg1,
- pretty_otp5085_msg2,
- pretty_otp5085_msg3,
- pretty_otp5085_msg4,
- pretty_otp5085_msg5,
- pretty_otp5085_msg6,
- pretty_otp5085_msg7,
- pretty_otp5600_msg1,
- pretty_otp5600_msg2,
- pretty_otp5601_msg1,
- pretty_otp5793_msg01,
- pretty_otp5882_msg01,
- pretty_otp6490_msg01,
- pretty_otp6490_msg02,
- pretty_otp6490_msg03,
- pretty_otp6490_msg04,
- pretty_otp6490_msg05,
- pretty_otp6490_msg06,
- pretty_otp7249_msg01,
- pretty_otp7671_msg01,
- pretty_otp7671_msg02,
- pretty_otp7671_msg03,
- pretty_otp7671_msg04,
- pretty_otp7671_msg05
- ].
-flex_pretty_tickets(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_tickets_cases(),
- flex_pretty_finish}}.
-flex_pretty_tickets_cases() ->
- [
- flex_pretty_otp5042_msg1,
- flex_pretty_otp5085_msg1,
- flex_pretty_otp5085_msg2,
- flex_pretty_otp5085_msg3,
- flex_pretty_otp5085_msg4,
- flex_pretty_otp5085_msg5,
- flex_pretty_otp5085_msg6,
- flex_pretty_otp5085_msg7,
- flex_pretty_otp5600_msg1,
- flex_pretty_otp5600_msg2,
- flex_pretty_otp5601_msg1,
- flex_pretty_otp5793_msg01,
- flex_pretty_otp7431_msg01,
- flex_pretty_otp7431_msg02,
- flex_pretty_otp7431_msg03,
- flex_pretty_otp7431_msg04,
- flex_pretty_otp7431_msg05,
- flex_pretty_otp7431_msg06,
- flex_pretty_otp7431_msg07
- ].
+flex_pretty_tickets_cases() ->
+[flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1,
+ flex_pretty_otp5085_msg2, flex_pretty_otp5085_msg3,
+ flex_pretty_otp5085_msg4, flex_pretty_otp5085_msg5,
+ flex_pretty_otp5085_msg6, flex_pretty_otp5085_msg7,
+ flex_pretty_otp5600_msg1, flex_pretty_otp5600_msg2,
+ flex_pretty_otp5601_msg1, flex_pretty_otp5793_msg01,
+ flex_pretty_otp7431_msg01, flex_pretty_otp7431_msg02,
+ flex_pretty_otp7431_msg03, flex_pretty_otp7431_msg04,
+ flex_pretty_otp7431_msg05, flex_pretty_otp7431_msg06,
+ flex_pretty_otp7431_msg07].
+
+%% ----
+
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_codec_v3_test.erl b/lib/megaco/test/megaco_codec_v3_test.erl
index f49c3a677a..2c35ce13b3 100644
--- a/lib/megaco/test/megaco_codec_v3_test.erl
+++ b/lib/megaco/test/megaco_codec_v3_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,26 +37,15 @@
-export([t/0, t/1]).
--export([all/1,
-
- text/1,
-
- pretty/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
pretty_test_msgs/1,
-
- compact/1,
compact_test_msgs/1,
-
- flex_pretty/1,
flex_pretty_init/1,
flex_pretty_finish/1,
flex_pretty_test_msgs/1,
-
- flex_compact/1,
flex_compact_init/1,
flex_compact_finish/1,
flex_compact_test_msgs/1,
-
flex_compact_dm_timers1/1,
flex_compact_dm_timers2/1,
flex_compact_dm_timers3/1,
@@ -65,32 +54,15 @@
flex_compact_dm_timers6/1,
flex_compact_dm_timers7/1,
flex_compact_dm_timers8/1,
-
- binary/1,
-
- bin/1,
bin_test_msgs/1,
-
- ber/1,
ber_test_msgs/1,
-
- ber_bin/1,
ber_bin_test_msgs/1,
-
- per/1,
per_test_msgs/1,
-
- per_bin/1,
per_bin_test_msgs/1,
-
- erl_dist/1,
- erl_dist_m/1,
erl_dist_m_test_msgs/1,
tickets/0,
- tickets/1,
-
- compact_tickets/1,
+
compact_otp4011_msg1/1,
compact_otp4011_msg2/1,
compact_otp4011_msg3/1,
@@ -133,8 +105,7 @@
compact_otp6017_msg01/1,
compact_otp6017_msg02/1,
compact_otp6017_msg03/1,
-
- flex_compact_tickets/1,
+
flex_compact_otp4299_msg1/1,
flex_compact_otp7431_msg01/1,
flex_compact_otp7431_msg02/1,
@@ -143,9 +114,7 @@
flex_compact_otp7431_msg05/1,
flex_compact_otp7431_msg06/1,
flex_compact_otp7431_msg07/1,
-
-
- pretty_tickets/1,
+
pretty_otp4632_msg1/1,
pretty_otp4632_msg2/1,
pretty_otp4632_msg3/1,
@@ -192,8 +161,7 @@
pretty_otp7671_msg04/1,
pretty_otp7671_msg05/1,
pretty_otp8114_msg01/1,
-
- flex_pretty_tickets/1,
+
flex_pretty_otp5042_msg1/1,
flex_pretty_otp5085_msg1/1,
flex_pretty_otp5085_msg2/1,
@@ -219,7 +187,7 @@
flex_pretty_otp7431_msg06/1,
flex_pretty_otp7431_msg07/1,
- init_per_testcase/2, fin_per_testcase/2]).
+ init_per_testcase/2, end_per_testcase/2]).
-export([display_text_messages/0, generate_text_messages/0]).
@@ -285,31 +253,6 @@ expand([Case|Cases], Acc) ->
expand(Cases, [Case|Acc])
end.
-
-%% ----
-
-tickets() ->
- Flag = process_flag(trap_exit, true),
- Cases = expand(tickets),
- Fun = fun(Case) ->
- C = init_per_testcase(Case, [{tc_timeout,
- timer:minutes(10)}]),
- io:format("Eval ~w~n", [Case]),
- Result =
- case (catch apply(?MODULE, Case, [C])) of
- {'EXIT', Reason} ->
- io:format("~n~p exited:~n ~p~n",
- [Case, Reason]),
- {error, {Case, Reason}};
- Res ->
- Res
- end,
- fin_per_testcase(Case, C),
- Result
- end,
- process_flag(trap_exit, Flag),
- lists:map(Fun, Cases).
-
%% ----
@@ -328,279 +271,174 @@ init_per_testcase(Case, Config) ->
end,
megaco_test_lib:init_per_testcase(Case, C).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
erase(verbosity),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- text,
- binary,
- erl_dist,
- tickets
- ].
-
-text(suite) ->
- [
- pretty,
- flex_pretty,
- compact,
- flex_compact
- ].
-
-binary(suite) ->
- [
- bin,
- ber,
- ber_bin,
- per,
- per_bin
- ].
-
-erl_dist(suite) ->
- [
- erl_dist_m
- ].
-
-pretty(suite) ->
- [
- pretty_test_msgs
- ].
-
-
-compact(suite) ->
- [
- compact_test_msgs
- ].
-
-
-flex_pretty(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_cases(), flex_pretty_finish}}.
+all() ->
+ [{group, text}, {group, binary}, {group, erl_dist},
+ {group, tickets}].
+
+groups() ->
+ [{text, [],
+ [{group, pretty}, {group, flex_pretty},
+ {group, compact}, {group, flex_compact}]},
+ {binary, [],
+ [{group, bin}, {group, ber}, {group, ber_bin},
+ {group, per}, {group, per_bin}]},
+ {erl_dist, [], [{group, erl_dist_m}]},
+ {pretty, [], [pretty_test_msgs]},
+ {compact, [], [compact_test_msgs]},
+ {flex_pretty, [], flex_pretty_cases()},
+ {flex_compact, [], flex_compact_cases()},
+ {bin, [], [bin_test_msgs]}, {ber, [], [ber_test_msgs]},
+ {ber_bin, [], [ber_bin_test_msgs]},
+ {per, [], [per_test_msgs]},
+ {per_bin, [], [per_bin_test_msgs]},
+ {erl_dist_m, [], [erl_dist_m_test_msgs]},
+ {tickets, [],
+ [{group, compact_tickets},
+ {group, flex_compact_tickets}, {group, pretty_tickets},
+ {group, flex_pretty_tickets}]},
+ {compact_tickets, [],
+ [compact_otp4011_msg1, compact_otp4011_msg2,
+ compact_otp4011_msg3, compact_otp4013_msg1,
+ compact_otp4085_msg1, compact_otp4085_msg2,
+ compact_otp4280_msg1, compact_otp4299_msg1,
+ compact_otp4359_msg1, compact_otp4920_msg0,
+ compact_otp4920_msg1, compact_otp4920_msg2,
+ compact_otp4920_msg3, compact_otp4920_msg4,
+ compact_otp4920_msg5, compact_otp4920_msg6,
+ compact_otp4920_msg7, compact_otp4920_msg8,
+ compact_otp4920_msg9, compact_otp4920_msg10,
+ compact_otp4920_msg11, compact_otp4920_msg12,
+ compact_otp4920_msg20, compact_otp4920_msg21,
+ compact_otp4920_msg22, compact_otp4920_msg23,
+ compact_otp4920_msg24, compact_otp4920_msg25,
+ compact_otp5186_msg01, compact_otp5186_msg02,
+ compact_otp5186_msg03, compact_otp5186_msg04,
+ compact_otp5186_msg05, compact_otp5186_msg06,
+ compact_otp5793_msg01, compact_otp5836_msg01,
+ compact_otp5993_msg01, compact_otp5993_msg02,
+ compact_otp5993_msg03, compact_otp6017_msg01,
+ compact_otp6017_msg02, compact_otp6017_msg03]},
+ {flex_compact_tickets, [],
+ flex_compact_tickets_cases()},
+ {pretty_tickets, [],
+ [pretty_otp4632_msg1, pretty_otp4632_msg2,
+ pretty_otp4632_msg3, pretty_otp4632_msg4,
+ pretty_otp4710_msg1, pretty_otp4710_msg2,
+ pretty_otp4945_msg1, pretty_otp4945_msg2,
+ pretty_otp4945_msg3, pretty_otp4945_msg4,
+ pretty_otp4945_msg5, pretty_otp4945_msg6,
+ pretty_otp4949_msg1, pretty_otp4949_msg2,
+ pretty_otp4949_msg3, pretty_otp5042_msg1,
+ pretty_otp5068_msg1, pretty_otp5085_msg1,
+ pretty_otp5085_msg2, pretty_otp5085_msg3,
+ pretty_otp5085_msg4, pretty_otp5085_msg5,
+ pretty_otp5085_msg6, pretty_otp5085_msg7,
+ pretty_otp5085_msg8, pretty_otp5600_msg1,
+ pretty_otp5600_msg2, pretty_otp5601_msg1,
+ pretty_otp5793_msg01, pretty_otp5803_msg01,
+ pretty_otp5803_msg02, pretty_otp5805_msg01,
+ pretty_otp5836_msg01, pretty_otp5882_msg01,
+ pretty_otp6490_msg01, pretty_otp6490_msg02,
+ pretty_otp6490_msg03, pretty_otp6490_msg04,
+ pretty_otp6490_msg05, pretty_otp6490_msg06,
+ pretty_otp7671_msg01, pretty_otp7671_msg02,
+ pretty_otp7671_msg03, pretty_otp7671_msg04,
+ pretty_otp7671_msg05, pretty_otp8114_msg01]},
+ {flex_pretty_tickets, [], flex_pretty_tickets_cases()}].
+
+init_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_init(Config);
+init_per_group(flex_compact_tickets, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_compact, Config) ->
+ flex_compact_init(Config);
+init_per_group(flex_pretty, Config) ->
+ flex_pretty_init(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(flex_pretty_tickets, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(flex_compact_tickets, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_compact, Config) ->
+ flex_compact_finish(Config);
+end_per_group(flex_pretty, Config) ->
+ flex_pretty_finish(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+flex_pretty_cases() ->
+ [flex_pretty_test_msgs].
+
+
+flex_compact_cases() ->
+ [flex_compact_test_msgs, flex_compact_dm_timers1,
+ flex_compact_dm_timers2, flex_compact_dm_timers3,
+ flex_compact_dm_timers4, flex_compact_dm_timers5,
+ flex_compact_dm_timers6, flex_compact_dm_timers7,
+ flex_compact_dm_timers8].
-flex_pretty_cases() ->
- [
- flex_pretty_test_msgs
- ].
-
-flex_compact(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_cases(), flex_compact_finish}}.
-
-flex_compact_cases() ->
- [
- flex_compact_test_msgs,
- flex_compact_dm_timers1,
- flex_compact_dm_timers2,
- flex_compact_dm_timers3,
- flex_compact_dm_timers4,
- flex_compact_dm_timers5,
- flex_compact_dm_timers6,
- flex_compact_dm_timers7,
- flex_compact_dm_timers8
- ].
-
-
-bin(suite) ->
- [
- bin_test_msgs
- ].
-
-
-ber(suite) ->
- [
- ber_test_msgs
- ].
-
-
-ber_bin(suite) ->
- [
- ber_bin_test_msgs
- ].
-
-
-per(suite) ->
- [
- per_test_msgs
- ].
%% Support for per_bin was added to ASN.1 as of version
%% 1.3.2 (R8). And later merged into 1.3.1.3 (R7). These
%% releases are identical (as far as I know).
%%
-per_bin(suite) ->
- [
- per_bin_test_msgs
- ].
-
-
-erl_dist_m(suite) ->
- [
- erl_dist_m_test_msgs
- ].
-
-tickets(suite) ->
- [
- compact_tickets,
- flex_compact_tickets,
- pretty_tickets,
- flex_pretty_tickets
- ].
-
-
-compact_tickets(suite) ->
- [
- compact_otp4011_msg1,
- compact_otp4011_msg2,
- compact_otp4011_msg3,
- compact_otp4013_msg1,
- compact_otp4085_msg1,
- compact_otp4085_msg2,
- compact_otp4280_msg1,
- compact_otp4299_msg1,
- compact_otp4359_msg1,
- compact_otp4920_msg0,
- compact_otp4920_msg1,
- compact_otp4920_msg2,
- compact_otp4920_msg3,
- compact_otp4920_msg4,
- compact_otp4920_msg5,
- compact_otp4920_msg6,
- compact_otp4920_msg7,
- compact_otp4920_msg8,
- compact_otp4920_msg9,
- compact_otp4920_msg10,
- compact_otp4920_msg11,
- compact_otp4920_msg12,
- compact_otp4920_msg20,
- compact_otp4920_msg21,
- compact_otp4920_msg22,
- compact_otp4920_msg23,
- compact_otp4920_msg24,
- compact_otp4920_msg25,
- compact_otp5186_msg01,
- compact_otp5186_msg02,
- compact_otp5186_msg03,
- compact_otp5186_msg04,
- compact_otp5186_msg05,
- compact_otp5186_msg06,
- compact_otp5793_msg01,
- compact_otp5836_msg01,
- compact_otp5993_msg01,
- compact_otp5993_msg02,
- compact_otp5993_msg03,
- compact_otp6017_msg01,
- compact_otp6017_msg02,
- compact_otp6017_msg03
- ].
-flex_compact_tickets(suite) ->
- {req, [],
- {conf, flex_compact_init, flex_compact_tickets_cases(),
- flex_compact_finish}}.
-
-flex_compact_tickets_cases() ->
- [
- flex_compact_otp4299_msg1,
- flex_compact_otp7431_msg01,
- flex_compact_otp7431_msg02,
- flex_compact_otp7431_msg03,
- flex_compact_otp7431_msg04,
- flex_compact_otp7431_msg05,
- flex_compact_otp7431_msg06,
- flex_compact_otp7431_msg07
- ].
-
-
-pretty_tickets(suite) ->
- [
- pretty_otp4632_msg1,
- pretty_otp4632_msg2,
- pretty_otp4632_msg3,
- pretty_otp4632_msg4,
- pretty_otp4710_msg1,
- pretty_otp4710_msg2,
- pretty_otp4945_msg1,
- pretty_otp4945_msg2,
- pretty_otp4945_msg3,
- pretty_otp4945_msg4,
- pretty_otp4945_msg5,
- pretty_otp4945_msg6,
- pretty_otp4949_msg1,
- pretty_otp4949_msg2,
- pretty_otp4949_msg3,
- pretty_otp5042_msg1,
- pretty_otp5068_msg1,
- pretty_otp5085_msg1,
- pretty_otp5085_msg2,
- pretty_otp5085_msg3,
- pretty_otp5085_msg4,
- pretty_otp5085_msg5,
- pretty_otp5085_msg6,
- pretty_otp5085_msg7,
- pretty_otp5085_msg8,
- pretty_otp5600_msg1,
- pretty_otp5600_msg2,
- pretty_otp5601_msg1,
- pretty_otp5793_msg01,
- pretty_otp5803_msg01,
- pretty_otp5803_msg02,
- pretty_otp5805_msg01,
- pretty_otp5836_msg01,
- pretty_otp5882_msg01,
- pretty_otp6490_msg01,
- pretty_otp6490_msg02,
- pretty_otp6490_msg03,
- pretty_otp6490_msg04,
- pretty_otp6490_msg05,
- pretty_otp6490_msg06,
- pretty_otp7671_msg01,
- pretty_otp7671_msg02,
- pretty_otp7671_msg03,
- pretty_otp7671_msg04,
- pretty_otp7671_msg05,
- pretty_otp8114_msg01
- ].
+flex_compact_tickets_cases() ->
+ [flex_compact_otp4299_msg1, flex_compact_otp7431_msg01,
+ flex_compact_otp7431_msg02, flex_compact_otp7431_msg03,
+ flex_compact_otp7431_msg04, flex_compact_otp7431_msg05,
+ flex_compact_otp7431_msg06, flex_compact_otp7431_msg07].
+
+flex_pretty_tickets_cases() ->
+ [flex_pretty_otp5042_msg1, flex_pretty_otp5085_msg1,
+ flex_pretty_otp5085_msg2, flex_pretty_otp5085_msg3,
+ flex_pretty_otp5085_msg4, flex_pretty_otp5085_msg5,
+ flex_pretty_otp5085_msg6, flex_pretty_otp5085_msg7,
+ flex_pretty_otp5085_msg8, flex_pretty_otp5600_msg1,
+ flex_pretty_otp5600_msg2, flex_pretty_otp5601_msg1,
+ flex_pretty_otp5793_msg01, flex_pretty_otp5803_msg01,
+ flex_pretty_otp5803_msg02, flex_pretty_otp5805_msg01,
+ flex_pretty_otp5836_msg01, flex_pretty_otp7431_msg01,
+ flex_pretty_otp7431_msg02, flex_pretty_otp7431_msg03,
+ flex_pretty_otp7431_msg04, flex_pretty_otp7431_msg05,
+ flex_pretty_otp7431_msg06, flex_pretty_otp7431_msg07].
+%% ----
-flex_pretty_tickets(suite) ->
- {req, [],
- {conf, flex_pretty_init, flex_pretty_tickets_cases(),
- flex_pretty_finish}}.
-
-flex_pretty_tickets_cases() ->
- [
- flex_pretty_otp5042_msg1,
- flex_pretty_otp5085_msg1,
- flex_pretty_otp5085_msg2,
- flex_pretty_otp5085_msg3,
- flex_pretty_otp5085_msg4,
- flex_pretty_otp5085_msg5,
- flex_pretty_otp5085_msg6,
- flex_pretty_otp5085_msg7,
- flex_pretty_otp5085_msg8,
- flex_pretty_otp5600_msg1,
- flex_pretty_otp5600_msg2,
- flex_pretty_otp5601_msg1,
- flex_pretty_otp5793_msg01,
- flex_pretty_otp5803_msg01,
- flex_pretty_otp5803_msg02,
- flex_pretty_otp5805_msg01,
- flex_pretty_otp5836_msg01,
- flex_pretty_otp7431_msg01,
- flex_pretty_otp7431_msg02,
- flex_pretty_otp7431_msg03,
- flex_pretty_otp7431_msg04,
- flex_pretty_otp7431_msg05,
- flex_pretty_otp7431_msg06,
- flex_pretty_otp7431_msg07
- ].
+tickets() ->
+ Flag = process_flag(trap_exit, true),
+ Cases = expand(tickets),
+ Fun = fun(Case) ->
+ C = init_per_testcase(Case, [{tc_timeout,
+ timer:minutes(10)}]),
+ io:format("Eval ~w~n", [Case]),
+ Result =
+ case (catch apply(?MODULE, Case, [C])) of
+ {'EXIT', Reason} ->
+ io:format("~n~p exited:~n ~p~n",
+ [Case, Reason]),
+ {error, {Case, Reason}};
+ Res ->
+ Res
+ end,
+ end_per_testcase(Case, C),
+ Result
+ end,
+ process_flag(trap_exit, Flag),
+ lists:map(Fun, Cases).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_config_test.erl b/lib/megaco/test/megaco_config_test.erl
index 9ab1a7d90d..1fc4d09d3b 100644
--- a/lib/megaco/test/megaco_config_test.erl
+++ b/lib/megaco/test/megaco_config_test.erl
@@ -44,9 +44,9 @@ do_init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
-record(command, {id, desc, cmd, verify}).
@@ -58,25 +58,21 @@ fin_per_testcase(Case, Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(suite) ->
- [
- config,
- transaction_id_counter,
- tickets
- ].
-
-transaction_id_counter(suite) ->
- [
- transaction_id_counter_mg,
- transaction_id_counter_mgc
- ].
-
-tickets(suite) ->
- [
- otp_7216,
- otp_8167,
- otp_8183
- ].
+all() ->
+ [config, {group, transaction_id_counter},
+ {group, tickets}].
+
+groups() ->
+ [{transaction_id_counter, [],
+ [transaction_id_counter_mg,
+ transaction_id_counter_mgc]},
+ {tickets, [], [otp_7216, otp_8167, otp_8183]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_digit_map_test.erl b/lib/megaco/test/megaco_digit_map_test.erl
index 22e115278f..d16fb679ae 100644
--- a/lib/megaco/test/megaco_digit_map_test.erl
+++ b/lib/megaco/test/megaco_digit_map_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,54 +36,39 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- tickets
- ],
- Cases.
+all() ->
+ [{group, tickets}].
+
+groups() ->
+ [{tickets, [],
+ [{group, otp_5750}, {group, otp_5799},
+ {group, otp_5826}, {group, otp_7449}]},
+ {otp_5750, [], [otp_5750_01, otp_5750_02]},
+ {otp_5799, [], [otp_5799_01]},
+ {otp_5826, [], [otp_5826_01, otp_5826_02, otp_5826_03]},
+ {otp_7449, [], [otp_7449_1, otp_7449_2]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-tickets(suite) ->
- [
- otp_5750,
- otp_5799,
- otp_5826,
- otp_7449
- ].
-
-
-otp_5750(suite) ->
- [
- otp_5750_01,
- otp_5750_02
- ].
-
-otp_5799(suite) ->
- [
- otp_5799_01
- ].
-
-otp_5826(suite) ->
- [
- otp_5826_01,
- otp_5826_02,
- otp_5826_03
- ].
-
-otp_7449(suite) ->
- [
- otp_7449_1,
- otp_7449_2
- ].
+
+
+
+
+
diff --git a/lib/megaco/test/megaco_examples_test.erl b/lib/megaco/test/megaco_examples_test.erl
index ef15cb1bde..528b61c2af 100644
--- a/lib/megaco/test/megaco_examples_test.erl
+++ b/lib/megaco/test/megaco_examples_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -41,11 +41,11 @@ init_per_testcase(Case, Config) ->
megaco:enable_trace(max, io),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
purge_examples(),
erase(dbg),
megaco:disable_trace(),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
example_modules() ->
[megaco_simple_mg, megaco_simple_mgc].
@@ -70,13 +70,18 @@ purge_examples() ->
%% Top test case
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(doc) ->
- ["Run all examples mentioned in the documentation",
- "Are really all examples covered?"];
-all(suite) ->
- [
- simple
- ].
+all() ->
+ [simple].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
simple(suite) ->
[];
diff --git a/lib/megaco/test/megaco_flex_test.erl b/lib/megaco/test/megaco_flex_test.erl
index 3dbcf53e7a..d7fc8eacb5 100644
--- a/lib/megaco/test/megaco_flex_test.erl
+++ b/lib/megaco/test/megaco_flex_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,10 +32,10 @@
-export([
t/0, t/1,
- init_per_testcase/2, fin_per_testcase/2,
+ init_per_testcase/2, end_per_testcase/2,
- all/1,
- flex_init/1, flex_fin/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
plain/1,
port_exit/1,
@@ -55,26 +55,31 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- plain,
- port_exit,
- garbage_in
- ],
- {req, [], {conf, flex_init, Cases, flex_fin}}.
+all() ->
+ [plain, port_exit, garbage_in].
-flex_init(suite) ->
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(suite) ->
[];
-flex_init(doc) ->
+init_per_suite(doc) ->
[];
-flex_init(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
case megaco_flex_scanner:is_enabled() of
true ->
Config;
@@ -82,9 +87,9 @@ flex_init(Config) when is_list(Config) ->
?SKIP(flex_scanner_not_enabled)
end.
-flex_fin(suite) -> [];
-flex_fin(doc) -> [];
-flex_fin(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
diff --git a/lib/megaco/test/megaco_load_test.erl b/lib/megaco/test/megaco_load_test.erl
index 5a22b7b4ee..5519ca15c6 100644
--- a/lib/megaco/test/megaco_load_test.erl
+++ b/lib/megaco/test/megaco_load_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -99,26 +99,29 @@ do_init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- single_user_light_load,
- single_user_medium_load,
- single_user_heavy_load,
- single_user_extreme_load,
- multi_user_light_load,
- multi_user_medium_load,
- multi_user_heavy_load,
- multi_user_extreme_load
- ],
- Cases.
+all() ->
+ [single_user_light_load,
+ single_user_medium_load, single_user_heavy_load,
+ single_user_extreme_load, multi_user_light_load,
+ multi_user_medium_load, multi_user_heavy_load,
+ multi_user_extreme_load].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_mess_otp8212_test.erl b/lib/megaco/test/megaco_mess_otp8212_test.erl
index 109886ebc4..e074e2f0b3 100644
--- a/lib/megaco/test/megaco_mess_otp8212_test.erl
+++ b/lib/megaco/test/megaco_mess_otp8212_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl
index 368800fa54..ded1506271 100644
--- a/lib/megaco/test/megaco_mess_test.erl
+++ b/lib/megaco/test/megaco_mess_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,13 +34,13 @@
%% -compile(export_all).
-export([
- all/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
+ end_per_testcase/2,
connect/1,
- request_and_reply/1,
+
request_and_reply_plain/1,
request_and_no_reply/1,
request_and_reply_pending_ack_no_pending/1,
@@ -52,13 +52,13 @@
request_and_reply_and_late_ack/1,
trans_req_and_reply_and_req/1,
- pending_ack/1,
+
pending_ack_plain/1,
request_and_pending_and_late_reply/1,
dist/1,
- tickets/1,
+
otp_4359/1,
otp_4836/1,
otp_5805/1,
@@ -67,18 +67,18 @@
otp_6253/1,
otp_6275/1,
otp_6276/1,
- otp_6442/1,
+
otp_6442_resend_request1/1,
otp_6442_resend_request2/1,
otp_6442_resend_reply1/1,
otp_6442_resend_reply2/1,
- otp_6865/1,
+
otp_6865_request_and_reply_plain_extra1/1,
otp_6865_request_and_reply_plain_extra2/1,
otp_7189/1,
otp_7259/1,
otp_7713/1,
- otp_8183/1,
+
otp_8183_request1/1,
otp_8212/1
]).
@@ -337,83 +337,50 @@ init_per_testcase(Case, Config) ->
C = lists:keydelete(tc_timeout, 1, Config),
megaco_test_lib:init_per_testcase(Case, [{tc_timeout, min(1)} |C]).
-% fin_per_testcase(pending_ack = Case, Config) ->
+% end_per_testcase(pending_ack = Case, Config) ->
% erase(dbg),
-% megaco_test_lib:fin_per_testcase(Case, Config);
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+% megaco_test_lib:end_per_testcase(Case, Config);
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- [
- connect,
- request_and_reply,
- pending_ack,
- dist,
-
- %% Tickets last
- tickets
- ].
-
-request_and_reply(suite) ->
- [
- request_and_reply_plain,
- request_and_no_reply,
- request_and_reply_pending_ack_no_pending,
- request_and_reply_pending_ack_one_pending,
- single_trans_req_and_reply,
- single_trans_req_and_reply_sendopts,
- request_and_reply_and_ack,
- request_and_reply_and_no_ack,
- request_and_reply_and_late_ack,
- trans_req_and_reply_and_req
- ].
-
-pending_ack(suite) ->
- [
- pending_ack_plain,
- request_and_pending_and_late_reply
- ].
-
-tickets(suite) ->
- [
- otp_4359,
- otp_4836,
- otp_5805,
- otp_5881,
- otp_5887,
- otp_6253,
- otp_6275,
- otp_6276,
- otp_6442,
- otp_6865,
- otp_7189,
- otp_7259,
- otp_7713,
- otp_8183,
- otp_8212
- ].
-
-otp_6442(suite) ->
- [
- otp_6442_resend_request1,
- otp_6442_resend_request2,
- otp_6442_resend_reply1,
- otp_6442_resend_reply2
- ].
-
-otp_6865(suite) ->
- [
- otp_6865_request_and_reply_plain_extra1,
- otp_6865_request_and_reply_plain_extra2
- ].
-
-otp_8183(suite) ->
- [
- otp_8183_request1
- ].
+all() ->
+ [connect, {group, request_and_reply},
+ {group, pending_ack}, dist, {group, tickets}].
+
+groups() ->
+ [{request_and_reply, [],
+ [request_and_reply_plain, request_and_no_reply,
+ request_and_reply_pending_ack_no_pending,
+ request_and_reply_pending_ack_one_pending,
+ single_trans_req_and_reply,
+ single_trans_req_and_reply_sendopts,
+ request_and_reply_and_ack, request_and_reply_and_no_ack,
+ request_and_reply_and_late_ack,
+ trans_req_and_reply_and_req]},
+ {pending_ack, [],
+ [pending_ack_plain,
+ request_and_pending_and_late_reply]},
+ {tickets, [],
+ [otp_4359, otp_4836, otp_5805, otp_5881, otp_5887,
+ otp_6253, otp_6275, otp_6276, {group, otp_6442},
+ {group, otp_6865}, otp_7189, otp_7259, otp_7713,
+ {group, otp_8183}, otp_8212]},
+ {otp_6442, [],
+ [otp_6442_resend_request1, otp_6442_resend_request2,
+ otp_6442_resend_reply1, otp_6442_resend_reply2]},
+ {otp_6865, [],
+ [otp_6865_request_and_reply_plain_extra1,
+ otp_6865_request_and_reply_plain_extra2]},
+ {otp_8183, [], [otp_8183_request1]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_mess_user_test.erl b/lib/megaco/test/megaco_mess_user_test.erl
index 50284be549..ce682c167b 100644
--- a/lib/megaco/test/megaco_mess_user_test.erl
+++ b/lib/megaco/test/megaco_mess_user_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_test.erl
index 2da6aa3bf3..52d99d1442 100644
--- a/lib/megaco/test/megaco_mib_test.erl
+++ b/lib/megaco/test/megaco_mib_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -63,21 +63,25 @@ init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config)
end.
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- plain,
- connect,
- traffic
- ],
- Cases.
+all() ->
+ [plain, connect, traffic].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_mreq_test.erl b/lib/megaco/test/megaco_mreq_test.erl
index 676acd8a12..1d3f38d50d 100644
--- a/lib/megaco/test/megaco_mreq_test.erl
+++ b/lib/megaco/test/megaco_mreq_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -69,20 +69,24 @@ init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- req_and_rep,
- req_and_pending,
- req_and_cancel
- ],
- Cases.
+all() ->
+ [req_and_rep, req_and_pending, req_and_cancel].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_pending_limit_test.erl b/lib/megaco/test/megaco_pending_limit_test.erl
index 1ca29c195c..233c22f4d2 100644
--- a/lib/megaco/test/megaco_pending_limit_test.erl
+++ b/lib/megaco/test/megaco_pending_limit_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,22 +26,16 @@
-module(megaco_pending_limit_test).
-export([t/0, t/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
--export([all/1,
-
- sent/1,
+-export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
sent_timer_late_reply/1,
sent_timer_exceeded/1,
sent_timer_exceeded_long/1,
sent_resend_late_reply/1,
sent_resend_exceeded/1,
sent_resend_exceeded_long/1,
-
- recv/1,
recv_limit_exceeded1/1,
recv_limit_exceeded2/1,
-
- tickets/1,
otp_4956/1,
otp_5310/1,
otp_5619/1
@@ -139,45 +133,29 @@ init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- [
- sent,
- recv,
-
- %% Tickets last
- tickets
- ].
-
-sent(suite) ->
- [
- sent_timer_late_reply,
- sent_timer_exceeded,
- sent_timer_exceeded_long,
- sent_resend_late_reply,
- sent_resend_exceeded,
- sent_resend_exceeded_long
-
- ].
+all() ->
+ [{group, sent}, {group, recv}, {group, tickets}].
-recv(suite) ->
- [
- recv_limit_exceeded1,
- recv_limit_exceeded2
- ].
+groups() ->
+ [{sent, [],
+ [sent_timer_late_reply, sent_timer_exceeded,
+ sent_timer_exceeded_long, sent_resend_late_reply,
+ sent_resend_exceeded, sent_resend_exceeded_long]},
+ {recv, [],
+ [recv_limit_exceeded1, recv_limit_exceeded2]},
+ {tickets, [], [otp_4956, otp_5310, otp_5619]}].
-tickets(suite) ->
- [
- otp_4956,
- otp_5310,
- otp_5619
- ].
+init_per_group(_GroupName, Config) ->
+ Config.
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% %%%
diff --git a/lib/megaco/test/megaco_profile.erl b/lib/megaco/test/megaco_profile.erl
index 01fa0b5a14..d0b62610e1 100644
--- a/lib/megaco/test/megaco_profile.erl
+++ b/lib/megaco/test/megaco_profile.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_sdp_test.erl b/lib/megaco/test/megaco_sdp_test.erl
index e9bd550518..796a956f23 100644
--- a/lib/megaco/test/megaco_sdp_test.erl
+++ b/lib/megaco/test/megaco_sdp_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,13 +24,12 @@
-module(megaco_sdp_test).
--export([all/1,
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
decode_encode/1,
- tickets/1,
otp8123/1,
- init_per_testcase/2, fin_per_testcase/2,
+ init_per_testcase/2, end_per_testcase/2,
t/0, t/1]).
@@ -46,8 +45,8 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
init_per_testcase(Case, Config) ->
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -55,16 +54,19 @@ fin_per_testcase(Case, Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- [
- decode_encode,
- tickets
- ].
+all() ->
+ [decode_encode, {group, tickets}].
+
+groups() ->
+ [{tickets, [], [otp8123]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-tickets(suite) ->
- [
- otp8123
- ].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_segment_test.erl b/lib/megaco/test/megaco_segment_test.erl
index ef07ee54b1..e4b568119d 100644
--- a/lib/megaco/test/megaco_segment_test.erl
+++ b/lib/megaco/test/megaco_segment_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,10 +24,10 @@
-module(megaco_segment_test).
-export([t/0, t/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
--export([all/1,
+-export([init_per_testcase/2, end_per_testcase/2]).
+-export([all/0,groups/0,init_per_group/2,end_per_group/2,
- send/1,
+
send_segmented_msg_plain1/1,
send_segmented_msg_plain2/1,
send_segmented_msg_plain3/1,
@@ -36,13 +36,11 @@
send_segmented_msg_missing_seg_reply1/1,
send_segmented_msg_missing_seg_reply2/1,
- recv/1,
+
recv_segmented_msg_plain/1,
recv_segmented_msg_ooo_seg/1,
recv_segmented_msg_missing_seg1/1,
- recv_segmented_msg_missing_seg2/1,
-
- tickets/1
+ recv_segmented_msg_missing_seg2/1
]).
@@ -66,45 +64,33 @@ init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- [
- send,
- recv
-
- %% Tickets last
- %% tickets
- ].
-
-send(suite) ->
- [
- send_segmented_msg_plain1,
- send_segmented_msg_plain2,
- send_segmented_msg_plain3,
- send_segmented_msg_plain4,
- send_segmented_msg_ooo1,
- send_segmented_msg_missing_seg_reply1,
- send_segmented_msg_missing_seg_reply2
- ].
-
-recv(suite) ->
- [
- recv_segmented_msg_plain,
- recv_segmented_msg_ooo_seg,
- recv_segmented_msg_missing_seg1,
- recv_segmented_msg_missing_seg2
- ].
-
-tickets(suite) ->
- [
- ].
-
+all() ->
+ [{group, send}, {group, recv}].
+
+groups() ->
+ [{send, [],
+ [send_segmented_msg_plain1, send_segmented_msg_plain2,
+ send_segmented_msg_plain3, send_segmented_msg_plain4,
+ send_segmented_msg_ooo1,
+ send_segmented_msg_missing_seg_reply1,
+ send_segmented_msg_missing_seg_reply2]},
+ {recv, [],
+ [recv_segmented_msg_plain, recv_segmented_msg_ooo_seg,
+ recv_segmented_msg_missing_seg1,
+ recv_segmented_msg_missing_seg2]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_tc_controller.erl b/lib/megaco/test/megaco_tc_controller.erl
index dedf45e321..458bff55e8 100644
--- a/lib/megaco/test/megaco_tc_controller.erl
+++ b/lib/megaco/test/megaco_tc_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_tcp_test.erl b/lib/megaco/test/megaco_tcp_test.erl
index 31c88489fe..013096c385 100644
--- a/lib/megaco/test/megaco_tcp_test.erl
+++ b/lib/megaco/test/megaco_tcp_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -36,25 +36,19 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
-
- start/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
start_normal/1,
start_invalid_opt/1,
start_and_stop/1,
-
- sending/1,
sendreceive/1,
block_unblock/1,
-
- errors/1,
socket_failure/1,
accept_process/1,
accept_supervisor/1,
connection_supervisor/1,
tcp_server/1,
- init_per_testcase/2, fin_per_testcase/2,
+ init_per_testcase/2, end_per_testcase/2,
t/0, t/1
]).
@@ -111,44 +105,32 @@ init_per_testcase(Case, Config) ->
%%----------------------------------------------------------------------
-%% Function: fin_per_testcase/2
+%% Function: end_per_testcase/2
%% Description:
%%----------------------------------------------------------------------
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%======================================================================
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- start,
- sending,
- errors
- ].
-
-start(suite) ->
- [
- start_normal,
- start_invalid_opt,
- start_and_stop
- ].
-
-sending(suite) ->
- [
- sendreceive,
- block_unblock
- ].
-
-errors(suite) ->
- [
- socket_failure,
- accept_process,
- accept_supervisor,
- connection_supervisor,
- tcp_server
- ].
+all() ->
+ [{group, start}, {group, sending}, {group, errors}].
+
+groups() ->
+ [{start, [],
+ [start_normal, start_invalid_opt, start_and_stop]},
+ {sending, [], [sendreceive, block_unblock]},
+ {errors, [],
+ [socket_failure, accept_process, accept_supervisor,
+ connection_supervisor, tcp_server]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%% ------------------ start ------------------------
diff --git a/lib/megaco/test/megaco_test_deliver.erl b/lib/megaco/test/megaco_test_deliver.erl
index 2d0f0c1cbe..ece0a48015 100644
--- a/lib/megaco/test/megaco_test_deliver.erl
+++ b/lib/megaco/test/megaco_test_deliver.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_test_generator.erl b/lib/megaco/test/megaco_test_generator.erl
index 8bbc60e6cd..4fbc86262e 100644
--- a/lib/megaco/test/megaco_test_generator.erl
+++ b/lib/megaco/test/megaco_test_generator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
-behaviour(gen_server).
+-compile({no_auto_import,[error/2]}).
+
+%% ----
+
-export([
start_link/3,
start_link/4,
diff --git a/lib/megaco/test/megaco_test_generator_lib.erl b/lib/megaco/test/megaco_test_generator_lib.erl
index cf0dcaf722..1584605913 100644
--- a/lib/megaco/test/megaco_test_generator_lib.erl
+++ b/lib/megaco/test/megaco_test_generator_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_test_generic_transport.erl b/lib/megaco/test/megaco_test_generic_transport.erl
index 10afa45baa..7a3dbc5317 100644
--- a/lib/megaco/test/megaco_test_generic_transport.erl
+++ b/lib/megaco/test/megaco_test_generic_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl
index 03c04831e8..41f6c2c4cb 100644
--- a/lib/megaco/test/megaco_test_lib.erl
+++ b/lib/megaco/test/megaco_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -146,28 +146,28 @@ tickets(Mod, Func, Config) ->
end,
lists:map(Map, Cases);
- {req, _, {conf, Init, Cases, Finish}} ->
- case (catch Mod:Init(Config)) of
- Conf when is_list(Conf) ->
- io:format("Expand: ~p:~p ...~n", [Mod, Func]),
- Map = fun({M,_}) when is_atom(M) ->
- tickets(M, tickets, Config);
- (F) when is_atom(F) ->
- tickets(Mod, F, Config);
- (Case) -> Case
- end,
- Res = lists:map(Map, Cases),
- (catch Mod:Finish(Conf)),
- Res;
+%% {req, _, {conf, Init, Cases, Finish}} ->
+%% case (catch Mod:Init(Config)) of
+%% Conf when is_list(Conf) ->
+%% io:format("Expand: ~p:~p ...~n", [Mod, Func]),
+%% Map = fun({M,_}) when is_atom(M) ->
+%% tickets(M, tickets, Config);
+%% (F) when is_atom(F) ->
+%% tickets(Mod, F, Config);
+%% (Case) -> Case
+%% end,
+%% Res = lists:map(Map, Cases),
+%% (catch Mod:Finish(Conf)),
+%% Res;
- {'EXIT', {skipped, Reason}} ->
- io:format(" => skipping: ~p~n", [Reason]),
- [{skipped, {Mod, Func}, Reason}];
+%% {'EXIT', {skipped, Reason}} ->
+%% io:format(" => skipping: ~p~n", [Reason]),
+%% [{skipped, {Mod, Func}, Reason}];
- Error ->
- io:format(" => init failed: ~p~n", [Error]),
- [{failed, {Mod, Func}, Error}]
- end;
+%% Error ->
+%% io:format(" => init failed: ~p~n", [Error]),
+%% [{failed, {Mod, Func}, Error}]
+%% end;
{'EXIT', {undef, _}} ->
io:format("Undefined: ~p~n", [{Mod, Func}]),
@@ -252,6 +252,8 @@ alloc_instance_mem_info(Key, InstanceInfo) ->
end.
+t([Case]) when is_atom(Case) ->
+ t(Case);
t(Case) ->
process_flag(trap_exit, true),
MEM = fun() -> case (catch erlang:memory()) of
@@ -266,11 +268,65 @@ t(Case) ->
Res = lists:flatten(t(Case, default_config())),
Alloc2 = alloc_info(),
Mem2 = MEM(),
- %% io:format("Res: ~p~n", [Res]),
display_result(Res, Alloc1, Mem1, Alloc2, Mem2),
Res.
-t({Mod, Fun}, Config) when is_atom(Mod) andalso is_atom(Fun) ->
+
+groups(Mod) when is_atom(Mod) ->
+ try Mod:groups() of
+ Groups when is_list(Groups) ->
+ Groups;
+ BadGroups ->
+ exit({bad_groups, Mod, BadGroups})
+ catch
+ _:_ ->
+ []
+ end.
+
+init_suite(Mod, Config) ->
+ Mod:init_per_suite(Config).
+
+end_suite(Mod, Config) ->
+ Mod:end_per_suite(Config).
+
+init_group(Mod, Group, Config) ->
+ Mod:init_per_group(Group, Config).
+
+end_group(Mod, Group, Config) ->
+ Mod:init_per_group(Group, Config).
+
+%% This is for sub-SUITEs
+t({_Mod, {NewMod, all}, _Groups}, _Config) when is_atom(NewMod) ->
+ t(NewMod);
+t({Mod, {group, Name} = Group, Groups}, Config)
+ when is_atom(Mod) andalso is_atom(Name) andalso is_list(Groups) ->
+ case lists:keysearch(Name, 1, Groups) of
+ {value, {Name, _Props, GroupsAndCases}} ->
+ try init_group(Mod, Name, Config) of
+ Config2 when is_list(Config2) ->
+ Res = [t({Mod, Case, Groups}, Config2) ||
+ Case <- GroupsAndCases],
+ (catch end_group(Mod, Name, Config2)),
+ Res;
+ Error ->
+ io:format(" => group (~w) init failed: ~p~n",
+ [Name, Error]),
+ [{failed, {Mod, Group}, Error}]
+ catch
+ exit:{skipped, SkipReason} ->
+ io:format(" => skipping group: ~p~n", [SkipReason]),
+ [{skipped, {Mod, Group}, SkipReason, 0}];
+ exit:{undef, _} ->
+ [t({Mod, Case, Groups}, Config) ||
+ Case <- GroupsAndCases];
+ T:E ->
+ [{failed, {Mod, Group}, {T,E}, 0}]
+ end;
+ false ->
+ exit({unknown_group, Mod, Name, Groups})
+ end;
+t({Mod, Fun, _}, Config)
+ when is_atom(Mod) andalso is_atom(Fun) ->
case catch apply(Mod, Fun, [suite]) of
[] ->
io:format("Eval: ~p:", [{Mod, Fun}]),
@@ -286,26 +342,6 @@ t({Mod, Fun}, Config) when is_atom(Mod) andalso is_atom(Fun) ->
end,
t(lists:map(Map, Cases), Config);
- {req, _, {conf, Init, Cases, Finish}} ->
- case (catch apply(Mod, Init, [Config])) of
- Conf when is_list(Conf) ->
- io:format("Expand: ~p ...~n", [{Mod, Fun}]),
- Map = fun(Case) when is_atom(Case) -> {Mod, Case};
- (Case) -> Case
- end,
- Res = t(lists:map(Map, Cases), Conf),
- (catch apply(Mod, Finish, [Conf])),
- Res;
-
- {'EXIT', {skipped, Reason}} ->
- io:format(" => skipping: ~p~n", [Reason]),
- [{skipped, {Mod, Fun}, Reason, 0}];
-
- Error ->
- io:format(" => failed: ~p~n", [Error]),
- [{failed, {Mod, Fun}, Error, 0}]
- end;
-
{'EXIT', {undef, _}} ->
io:format("Undefined: ~p~n", [{Mod, Fun}]),
[{nyi, {Mod, Fun}, ok, 0}];
@@ -315,10 +351,38 @@ t({Mod, Fun}, Config) when is_atom(Mod) andalso is_atom(Fun) ->
[{failed, {Mod, Fun}, Error, 0}]
end;
t(Mod, Config) when is_atom(Mod) ->
- Res = t({Mod, all}, Config),
- Res;
-t(Cases, Config) when is_list(Cases) ->
- [t(Case, Config) || Case <- Cases];
+ %% This is assumed to be a test suite, so we start by calling
+ %% the top test suite function(s) (all/0 and groups/0).
+ case (catch Mod:all()) of
+ Cases when is_list(Cases) ->
+ %% The list may contain atoms (actual test cases) and
+ %% group-tuples (a tuple naming a group of test cases).
+ %% A group is defined by the (optional) groups/0 function.
+ Groups = groups(Mod),
+ try init_suite(Mod, Config) of
+ Config2 when is_list(Config2) ->
+ Res = [t({Mod, Case, Groups}, Config2) || Case <- Cases],
+ (catch end_suite(Mod, Config2)),
+ Res;
+ Error ->
+ io:format(" => suite init failed: ~p~n", [Error]),
+ [{failed, {Mod, init_per_suite}, Error}]
+ catch
+ exit:{skipped, SkipReason} ->
+ io:format(" => skipping suite: ~p~n", [SkipReason]),
+ [{skipped, {Mod, init_per_suite}, SkipReason, 0}];
+ exit:{undef, _} ->
+ [t({Mod, Case, Groups}, Config) || Case <- Cases];
+ T:E ->
+ [{failed, {Mod, init_per_suite}, {T,E}, 0}]
+ end;
+ {'EXIT', {undef, _}} ->
+ io:format("Undefined: ~p~n", [{Mod, all}]),
+ [{nyi, {Mod, all}, ok, 0}];
+
+ Crap ->
+ Crap
+ end;
t(Bad, _Config) ->
[{badarg, Bad, ok, 0}].
@@ -333,7 +397,7 @@ eval(Mod, Fun, Config) ->
Config2 = Mod:init_per_testcase(Fun, Config),
Pid = spawn_link(?MODULE, do_eval, [self(), Mod, Fun, Config2]),
R = wait_for_evaluator(Pid, Mod, Fun, Config2, []),
- Mod:fin_per_testcase(Fun, Config2),
+ Mod:end_per_testcase(Fun, Config2),
erase(megaco_test_server),
global:unregister_name(megaco_test_case_sup),
process_flag(trap_exit, Flag),
@@ -495,28 +559,56 @@ do_display_memory([{Key, Mem1}|MemInfo1], MemInfo2) ->
display_result([]) ->
io:format("OK~n", []);
display_result(Res) when is_list(Res) ->
- Ok = [{MF, Time} || {ok, MF, _, Time} <- Res],
- Nyi = [MF || {nyi, MF, _, _Time} <- Res],
- Skipped = [{MF, Reason} || {skipped, MF, Reason, _Time} <- Res],
- Failed = [{MF, Reason} || {failed, MF, Reason, _Time} <- Res],
- Crashed = [{MF, Reason} || {crashed, MF, Reason, _Time} <- Res],
- display_summery(Ok, Nyi, Skipped, Failed, Crashed),
+ Ok = [{MF, Time} || {ok, MF, _, Time} <- Res],
+ Nyi = [MF || {nyi, MF, _, _Time} <- Res],
+ SkippedGrps = [{{M,G}, Reason} ||
+ {skipped, {M, {group, G}}, Reason, _Time} <- Res],
+ SkippedCases = [{MF, Reason} ||
+ {skipped, {_M, F} = MF, Reason, _Time} <- Res,
+ is_atom(F)],
+ FailedGrps = [{{M,G}, Reason} ||
+ {failed, {M, {group, G}}, Reason, _Time} <- Res],
+ FailedCases = [{MF, Reason} ||
+ {failed, {_M, F} = MF, Reason, _Time} <- Res,
+ is_atom(F)],
+ Crashed = [{MF, Reason} || {crashed, MF, Reason, _Time} <- Res],
+ display_summery(Ok, Nyi,
+ SkippedGrps, SkippedCases,
+ FailedGrps, FailedCases,
+ Crashed),
display_ok(Ok),
- display_skipped(Skipped),
- display_failed(Failed),
+ display_skipped("groups", SkippedGrps),
+ display_skipped("test cases", SkippedCases),
+ display_failed("groups", FailedGrps),
+ display_failed("test cases", FailedCases),
display_crashed(Crashed).
-display_summery(Ok, Nyi, Skipped, Failed, Crashed) ->
+display_summery(Ok, Nyi,
+ SkippedGrps, SkippedCases,
+ FailedGrps, FailedCases,
+ Crashed) ->
io:format("~nTest case summery:~n", []),
- display_summery(Ok, "successfull"),
- display_summery(Nyi, "not yet implemented"),
- display_summery(Skipped, "skipped"),
- display_summery(Failed, "failed"),
- display_summery(Crashed, "crashed"),
+ display_summery(Ok, "test case", "successfull"),
+ display_summery(Nyi, "test case", "not yet implemented"),
+ display_summery(SkippedGrps, "group", "skipped"),
+ display_summery(SkippedCases, "test case", "skipped"),
+ display_summery(FailedGrps, "group", "failed"),
+ display_summery(FailedCases, "test case", "failed"),
+ display_summery(Crashed, "test case", "crashed"),
io:format("~n", []).
-display_summery(Res, Info) ->
- io:format(" ~w test cases ~s~n", [length(Res), Info]).
+
+display_summery(Res, Kind, Info) ->
+ Len = length(Res),
+ if
+ Len =:= 1 ->
+ display_summery(Len, Kind ++ " " ++ Info);
+ true ->
+ display_summery(Len, Kind ++ "s " ++ Info)
+ end.
+
+display_summery(Len, Info) ->
+ io:format(" ~w ~s~n", [Len, Info]).
display_ok([]) ->
ok;
@@ -528,20 +620,20 @@ display_ok(Ok) ->
lists:foreach(F, Ok),
io:format("~n", []).
-display_skipped([]) ->
+display_skipped(_, []) ->
ok;
-display_skipped(Skipped) ->
- io:format("Skipped test cases:~n", []),
- F = fun({MF, Reason}) -> io:format(" ~p => ~p~n", [MF, Reason]) end,
+display_skipped(Pre, Skipped) ->
+ io:format("Skipped ~s:~n", [Pre]),
+ F = fun({X, Reason}) -> io:format(" ~p => ~p~n", [X, Reason]) end,
lists:foreach(F, Skipped),
io:format("~n", []).
-display_failed([]) ->
+display_failed(_, []) ->
ok;
-display_failed(Failed) ->
- io:format("Failed test cases:~n", []),
- F = fun({MF, Reason}) -> io:format(" ~p => ~p~n", [MF, Reason]) end,
+display_failed(Pre, Failed) ->
+ io:format("Failed ~s:~n", [Pre]),
+ F = fun({X, Reason}) -> io:format(" ~p => ~p~n", [X, Reason]) end,
lists:foreach(F, Failed),
io:format("~n", []).
@@ -677,11 +769,11 @@ init_per_testcase(_Case, Config) ->
end,
set_kill_timer(Config).
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Name = megaco_global_logger,
case global:whereis_name(Name) of
undefined ->
- io:format("~w:fin_per_testcase -> already un-registered~n",
+ io:format("~w:end_per_testcase -> already un-registered~n",
[?MODULE]),
ok;
Pid when is_pid(Pid) ->
@@ -837,5 +929,5 @@ start_nodes([Node | Nodes], File, Line) ->
start_nodes([], _File, _Line) ->
ok.
-p(F,A) ->
- io:format("~p" ++ F ++ "~n", [self()|A]).
+p(F, A) ->
+ io:format("~p~w:" ++ F ++ "~n", [self(), ?MODULE |A]).
diff --git a/lib/megaco/test/megaco_test_megaco_generator.erl b/lib/megaco/test/megaco_test_megaco_generator.erl
index 5ff7162223..f0c723d2cf 100644
--- a/lib/megaco/test/megaco_test_megaco_generator.erl
+++ b/lib/megaco/test/megaco_test_megaco_generator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,8 @@
-behaviour(megaco_test_generator).
+-compile({no_auto_import,[error/1]}).
+
%% API
-export([
start_link/1, start_link/2,
diff --git a/lib/megaco/test/megaco_test_mg.erl b/lib/megaco/test/megaco_test_mg.erl
index 22b65a1ac6..ecb3cedc83 100644
--- a/lib/megaco/test/megaco_test_mg.erl
+++ b/lib/megaco/test/megaco_test_mg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_test_mgc.erl b/lib/megaco/test/megaco_test_mgc.erl
index 05c482f1af..13c1cebe56 100644
--- a/lib/megaco/test/megaco_test_mgc.erl
+++ b/lib/megaco/test/megaco_test_mgc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/megaco/test/megaco_test_msg_prev3a_lib.erl b/lib/megaco/test/megaco_test_msg_prev3a_lib.erl
index 5ce2ec302b..fad7f29831 100644
--- a/lib/megaco/test/megaco_test_msg_prev3a_lib.erl
+++ b/lib/megaco/test/megaco_test_msg_prev3a_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco_message_prev3a.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_test_msg_prev3b_lib.erl b/lib/megaco/test/megaco_test_msg_prev3b_lib.erl
index be87dc9a41..2f1a093728 100644
--- a/lib/megaco/test/megaco_test_msg_prev3b_lib.erl
+++ b/lib/megaco/test/megaco_test_msg_prev3b_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco_message_prev3b.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_test_msg_prev3c_lib.erl b/lib/megaco/test/megaco_test_msg_prev3c_lib.erl
index 74a05060d0..884e2f2bad 100644
--- a/lib/megaco/test/megaco_test_msg_prev3c_lib.erl
+++ b/lib/megaco/test/megaco_test_msg_prev3c_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco_message_prev3c.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_test_msg_v1_lib.erl b/lib/megaco/test/megaco_test_msg_v1_lib.erl
index 638215e8c1..76665cb575 100644
--- a/lib/megaco/test/megaco_test_msg_v1_lib.erl
+++ b/lib/megaco/test/megaco_test_msg_v1_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,6 +28,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco_message_v1.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_test_msg_v2_lib.erl b/lib/megaco/test/megaco_test_msg_v2_lib.erl
index b680bc869a..66e423284a 100644
--- a/lib/megaco/test/megaco_test_msg_v2_lib.erl
+++ b/lib/megaco/test/megaco_test_msg_v2_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco_message_v2.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_test_msg_v3_lib.erl b/lib/megaco/test/megaco_test_msg_v3_lib.erl
index 7b0d4f7d37..24492167ff 100644
--- a/lib/megaco/test/megaco_test_msg_v3_lib.erl
+++ b/lib/megaco/test/megaco_test_msg_v3_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,10 @@
%% ----
+-compile({no_auto_import,[error/1]}).
+
+%% ----
+
-include_lib("megaco/include/megaco_message_v3.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_test_tcp_generator.erl b/lib/megaco/test/megaco_test_tcp_generator.erl
index e4f27f32f5..3ed4c49bab 100644
--- a/lib/megaco/test/megaco_test_tcp_generator.erl
+++ b/lib/megaco/test/megaco_test_tcp_generator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,6 +26,8 @@
-behaviour(megaco_test_generator).
+-compile({no_auto_import,[error/1]}).
+
%% API
-export([
start_link/1, start_link/2,
diff --git a/lib/megaco/test/megaco_timer_test.erl b/lib/megaco/test/megaco_timer_test.erl
index 8bcfc5a907..9b9103c40b 100644
--- a/lib/megaco/test/megaco_timer_test.erl
+++ b/lib/megaco/test/megaco_timer_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,22 +23,17 @@
%%----------------------------------------------------------------------
-module(megaco_timer_test).
+-compile({no_auto_import,[error/1]}).
+
-export([
t/0, t/1,
- init_per_testcase/2, fin_per_testcase/2,
-
- all/1,
-
- simple/1,
+ init_per_testcase/2, end_per_testcase/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
simple_init/1,
simple_usage/1,
-
- integer_timer/1,
integer_timer_start_and_expire/1,
integer_timer_start_and_stop/1%% ,
-
%% incr_timer/1
-
]).
-export([
@@ -71,49 +66,39 @@ do_init_per_testcase(Case, Config) ->
{ok, _Pid} = megaco_monitor:start_link(),
megaco_test_lib:init_per_testcase(Case, [{monitor_running, true}|Config]).
-fin_per_testcase(Case, Config) ->
- io:format("fin_per_testcase -> entry with"
+end_per_testcase(Case, Config) ->
+ io:format("end_per_testcase -> entry with"
"~n Case: ~p"
"~n Config: ~p"
"~n", [Case, Config]),
process_flag(trap_exit, false),
case lists:keydelete(monitor_running, 1, Config) of
Config ->
- megaco_test_lib:fin_per_testcase(Case, Config);
+ megaco_test_lib:end_per_testcase(Case, Config);
Config2 ->
megaco_monitor:stop(),
- megaco_test_lib:fin_per_testcase(Case, Config2)
+ megaco_test_lib:end_per_testcase(Case, Config2)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- simple,
- integer_timer%% ,
-%% incr_timer
- ],
- Cases.
-
-
-simple(suite) ->
- Cases =
- [
- simple_init,
- simple_usage
- ],
- Cases.
-
-
-integer_timer(suite) ->
- Cases =
- [
- integer_timer_start_and_expire,
- integer_timer_start_and_stop
- ],
- Cases.
+all() ->
+ [{group, simple}, {group, integer_timer}].
+
+groups() ->
+ [{simple, [],
+ [simple_init, simple_usage]},
+%, incr_timer
+ {integer_timer, [],
+ [integer_timer_start_and_expire,
+ integer_timer_start_and_stop]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%% incr_timer(suite) ->
diff --git a/lib/megaco/test/megaco_trans_test.erl b/lib/megaco/test/megaco_trans_test.erl
index 44d4b3fff7..5f564e3bf6 100644
--- a/lib/megaco/test/megaco_trans_test.erl
+++ b/lib/megaco/test/megaco_trans_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -95,78 +95,49 @@ init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
-fin_per_testcase(Case, Config) ->
+end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
- megaco_test_lib:fin_per_testcase(Case, Config).
+ megaco_test_lib:end_per_testcase(Case, Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- [
- ack,
- trans_req,
- trans_req_and_ack,
- pending,
- reply,
-
- tickets
- ].
-
-ack(suite) ->
- [
- single_ack,
- multi_ack_timeout,
- multi_ack_maxcount
- ].
-
-trans_req(suite) ->
- [
- single_trans_req,
- multi_trans_req_timeout,
- multi_trans_req_maxcount1,
- multi_trans_req_maxcount2,
- multi_trans_req_maxsize1,
- multi_trans_req_maxsize2
- ].
-
-trans_req_and_ack(suite) ->
- [
- single_trans_req_and_ack,
- multi_trans_req_and_ack_timeout,
- multi_trans_req_and_ack_ackmaxcount,
- multi_trans_req_and_ack_reqmaxcount,
- multi_trans_req_and_ack_maxsize1,
- multi_trans_req_and_ack_maxsize2
- ].
-
-pending(suite) ->
- [
- single_trans_req_and_pending,
- multi_trans_req_and_pending,
- multi_trans_req_and_ack_and_pending,
- multi_ack_and_pending
- ].
-
-reply(suite) ->
- [
- multi_trans_req_and_reply,
- multi_trans_req_and_ack_and_reply,
- multi_ack_and_reply
- ].
-
-tickets(suite) ->
- [
- otp_7192
- ].
-
-otp_7192(suite) ->
- [
- otp_7192_1,
- otp_7192_2,
- otp_7192_3
- ].
-
+all() ->
+ [{group, ack}, {group, trans_req},
+ {group, trans_req_and_ack}, {group, pending},
+ {group, reply}, {group, tickets}].
+
+groups() ->
+ [{ack, [],
+ [single_ack, multi_ack_timeout, multi_ack_maxcount]},
+ {trans_req, [],
+ [single_trans_req, multi_trans_req_timeout,
+ multi_trans_req_maxcount1, multi_trans_req_maxcount2,
+ multi_trans_req_maxsize1, multi_trans_req_maxsize2]},
+ {trans_req_and_ack, [],
+ [single_trans_req_and_ack,
+ multi_trans_req_and_ack_timeout,
+ multi_trans_req_and_ack_ackmaxcount,
+ multi_trans_req_and_ack_reqmaxcount,
+ multi_trans_req_and_ack_maxsize1,
+ multi_trans_req_and_ack_maxsize2]},
+ {pending, [],
+ [single_trans_req_and_pending,
+ multi_trans_req_and_pending,
+ multi_trans_req_and_ack_and_pending,
+ multi_ack_and_pending]},
+ {reply, [],
+ [multi_trans_req_and_reply,
+ multi_trans_req_and_ack_and_reply,
+ multi_ack_and_reply]},
+ {tickets, [], [{group, otp_7192}]},
+ {otp_7192, [], [otp_7192_1, otp_7192_2, otp_7192_3]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_udp_test.erl b/lib/megaco/test/megaco_udp_test.erl
index 2e2f5465dd..ffbff9b762 100644
--- a/lib/megaco/test/megaco_udp_test.erl
+++ b/lib/megaco/test/megaco_udp_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,22 +34,14 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
-
- start/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
start_normal/1,
start_invalid_opt/1,
start_and_stop/1,
-
- sending/1,
sendreceive/1,
block_unblock/1,
-
- errors/1,
socket_failure/1,
-
- init_per_testcase/2, fin_per_testcase/2,
-
+ init_per_testcase/2, end_per_testcase/2,
t/0, t/1
]).
@@ -104,42 +96,31 @@ init_per_testcase(Case, Config) ->
%%----------------------------------------------------------------------
-%% Function: fin_per_testcase/2
+%% Function: end_per_testcase/2
%% Description:
%%----------------------------------------------------------------------
-fin_per_testcase(Case, Config) ->
- megaco_test_lib:fin_per_testcase(Case, Config).
+end_per_testcase(Case, Config) ->
+ megaco_test_lib:end_per_testcase(Case, Config).
%%======================================================================
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- start,
- sending,
- errors
- ].
-
-start(suite) ->
- [
- start_normal,
- start_invalid_opt,
- start_and_stop
- ].
+all() ->
+ [{group, start}, {group, sending}, {group, errors}].
-sending(suite) ->
- [
- sendreceive,
- block_unblock
+groups() ->
+ [{start, [],
+ [start_normal, start_invalid_opt, start_and_stop]},
+ {sending, [], [sendreceive, block_unblock]},
+ {errors, [], [socket_failure]}].
- ].
+init_per_group(_GroupName, Config) ->
+ Config.
-errors(suite) ->
- [
- socket_failure
- ].
+end_per_group(_GroupName, Config) ->
+ Config.
%% =================================================
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index cf5957460d..5f71712360 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -18,132 +18,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.14.1
-PRE_VSN =
-APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
-
-TICKETS = OTP-8561 OTP-8627
-
-TICKETS_3_14 = OTP-8317 OTP-8323 OTP-8328 OTP-8362 OTP-8403
-
-TICKETS_3_13 = OTP-8205 OTP-8239 OTP-8249
-
-TICKETS_3_12 = OTP-8183 OTP-8212
-
-TICKETS_3_11_3 = OTP-8164 OTP-8167 OTP-8191
-
-TICKETS_3_11_2 = OTP-8123
-
-TICKETS_3_11_1 = OTP-8081 OTP-8114
-
-TICKETS_3_11 = OTP-7302 OTP-7995
-
-TICKETS_3_10_1 = OTP-7926 OTP-7936
-
-TICKETS_3_10_0_1 = OTP-7851
-
-TICKETS_3_10 = OTP-7713 OTP-7743
-
-TICKETS_3_9_4 = OTP-7728 OTP-7733
-
-TICKETS_3_9_3 = OTP-7700
-
-TICKETS_3_9_2 = OTP-7671 OTP-7672
-
-TICKETS_3_9_1_1 = OTP-7614
-
-TICKETS_3_9_1 = OTP-7572 OTP-7573 OTP-7576
-
-TICKETS_3_9 = OTP-7431
-
-TICKETS_3_8_2 = OTP-7534
-
-TICKETS_3_8_1 = OTP-7398 OTP-7417 OTP-7444 OTP-7449 OTP-7455 OTP-7457 OTP-7459
-
-TICKETS_3_8 = OTP-7192 OTP-7228 OTP-7259
-
-TICKETS_3_7_5 = OTP-7286 OTP-7303
-
-TICKETS_3_7_4 = OTP-7249 OTP-7251
-
-TICKETS_3_7_3 = OTP-7168 OTP-7180 OTP-7189 OTP-7216
-
-TICKETS_3_7_2 = OTP-6972 OTP-7138
-
-TICKETS_3_7_1 = OTP-6919 OTP-6971 OTP-6992 OTP-6999 OTP-7000 OTP-7005 OTP-7124
-
-TICKETS_3_7 = OTP-5979 OTP-6753 OTP-6804 OTP-6865 OTP-6919 OTP-6976
-
-TICKETS_3_6_2 = OTP-6921
-
-TICKETS_3_6_1 = OTP-6803
-
-TICKETS_3_6_0_1 = OTP-6704
-
-TICKETS_3_6 = OTP-6185 OTP-6578 OTP-6441 OTP-6442 OTP-6544 OTP-6605 OTP-6609
-
-TICKETS_3_5_3 = OTP-6520 OTP-6549
-
-TICKETS_3_5_2 = OTP-6404 OTP-6422 OTP-6490 OTP-6503
-
-TICKETS_3_5_1 = OTP-6275 OTP-6276
-
-TICKETS_3_5 = OTP-6223 OTP-6253 OTP-6256
-
-TICKETS_3_4_4 = OTP-6181 OTP-6182 OTP-6217 OTP-6219
-
-TICKETS_3_4_3 = OTP-6170 OTP-6171 OTP-6172
-
-TICKETS_3_4_2 = OTP-6148
-
-TICKETS_3_4_1 = OTP-6113
-
-TICKETS_3_4 = \
- OTP-5769 \
- OTP-5980 \
- OTP-6009 \
- OTP-6025 \
- OTP-6028 \
- OTP-6030 \
- OTP-6048 \
- OTP-6051 \
- OTP-6052 \
- OTP-6055 \
- OTP-6089 \
- OTP-6090
-
-TICKETS_3_3_5 = OTP-6108
-
-TICKETS_3_3_4 = OTP-6076
-
-TICKETS_3_3_3 = OTP-6046
-
-TICKETS_3_3_2 = OTP-6017 OTP-6022
-
-TICKETS_3_3_1 = OTP-5993
-
-TICKETS_3_3 = OTP-5965 OTP-5973
-
-TICKETS_3_2_7 = OTP-5948 OTP-5952 OTP-5953
-
-TICKETS_3_2_6 = OTP-5918 OTP-5919 OTP-5920
-
-TICKETS_3_2_5 = OTP-5887
-
-TICKETS_3_2_4 = OTP-5867 OTP-5879 OTP-5880 OTP-5881 OTP-5882 OTP-5885 OTP-5886
-
-TICKETS_3_2_3 = OTP-5826 OTP-5830 OTP-5833 OTP-5836 OTP-5839
-
-TICKETS_3_2_2 = OTP-5799 OTP-5803 OTP-5804 OTP-5805 OTP-5816
-
-TICKETS_3_2_1 = OTP-5725 OTP-5793
-
-TICKETS_3_2 = OTP-5717 OTP-5750
-
-TICKETS_3_1 = OTP-5542 OTP-5597 OTP-5600 OTP-5601 OTP-5619 OTP-5664
-
-TICKETS_3_0_1 = \
- OTP-5401 \
- OTP-5446 \
- OTP-5447
-
+MEGACO_VSN = 3.15.1
+PRE_VSN =
+APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc
index 0714c7b645..473b35b806 100644
--- a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc
+++ b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -235,9 +235,7 @@
<seealso marker="Mnesia_chap3#start_mnesia">Starting Mnesia</seealso>.
</item>
</list>
- <p>Continuing the dialogue with the Erlang shell will produce the following
- the following:
- </p>
+ <p>Continuing the dialogue with the Erlang shell will produce the following:</p>
<pre><![CDATA[
3> company:init().
{atomic,ok}
@@ -418,7 +416,7 @@ In_proj</tcaption>
interchangeably throughout this book.
</p>
<p>A Mnesia table is populated by Mnesia records. For example,
- the tuple <c>{boss, klacke, bjarne}</c> is an record. The
+ the tuple <c>{boss, klacke, bjarne}</c> is a record. The
second element in this tuple is the key. In order to uniquely
identify a table row both the key and the table name is
needed. The term <em>object identifier</em>,
@@ -553,7 +551,7 @@ In_proj</tcaption>
stored in the database:
</p>
<pre>
-\011 mnesia:select(employee, [{#employee{sex = female, name = '$1', _ = '_'},[], ['$1']}]).
+mnesia:select(employee, [{#employee{sex = female, name = '$1', _ = '_'},[], ['$1']}]).
</pre>
<p>Select must always run within an activity such as a
transaction. To be able to call from the shell we might
@@ -587,8 +585,8 @@ In_proj</tcaption>
</p>
<pre>
Q = qlc:q([E#employee.name || E <![CDATA[<-]]> mnesia:table(employee),
-\011 E#employee.sex == female]),
-\011 qlc:e(Q),
+ E#employee.sex == female]),
+ qlc:e(Q),
</pre>
<p>Accessing mnesia tables from a QLC list comprehension must
always be done within a transaction. Consider the following
diff --git a/lib/mnesia/doc/src/Mnesia_chap3.xml b/lib/mnesia/doc/src/Mnesia_chap3.xml
index 9a382bcb5a..5733aedbfd 100644
--- a/lib/mnesia/doc/src/Mnesia_chap3.xml
+++ b/lib/mnesia/doc/src/Mnesia_chap3.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -132,7 +132,7 @@
function changes the format on all records in table
<c>Tab</c>. It applies the argument <c>Fun</c> to all
records in the table. <c>Fun</c> shall be a function which
- takes an record of the old type, and returns the record of the new
+ takes a record of the old type, and returns the record of the new
type. The table key may not be changed.</p>
<code type="none">
-record(old, {key, val}).
@@ -418,8 +418,8 @@ skeppet %<input>erl -sname b -mnesia dir '"/ldisc/scratch/Mnesia.company"'</inpu
type <c>set</c> and <c>bag</c>: </p>
<pre>
f() -> F = fun() ->
-\011 mnesia:write({foo, 1, 2}), mnesia:write({foo, 1, 3}),
-\011 mnesia:read({foo, 1}) end, mnesia:transaction(F). </pre>
+ mnesia:write({foo, 1, 2}), mnesia:write({foo, 1, 3}),
+ mnesia:read({foo, 1}) end, mnesia:transaction(F). </pre>
<p>This transaction will return the list <c>[{foo,1,3}]</c> if
the <c>foo</c> table is of type <c>set</c>. However, list
<c>[{foo,1,2}, {foo,1,3}]</c> will return if the table is
diff --git a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
index 7d89c1b0dd..7e57c7ac02 100644
--- a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
+++ b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -514,13 +514,13 @@ The behavior is undefined if any process perform a write
of the table itself. This is an implementation detail, but remember
the dirty functions are low level functions.
</item>
- <item><c>mnesia:dirty_last(Tab)</c> This function works exactly as
+ <item><c>mnesia:dirty_last(Tab)</c> This function works exactly like
<c>mnesia:dirty_first/1</c> but returns the last object in
Erlang term order for the <c>ordered_set</c> table type. For
all other table types, <c>mnesia:dirty_first/1</c> and
<c>mnesia:dirty_last/1</c> are synonyms.
</item>
- <item><c>mnesia:dirty_prev(Tab, Key)</c> This function works exactly as
+ <item><c>mnesia:dirty_prev(Tab, Key)</c> This function works exactly like
<c>mnesia:dirty_next/2</c> but returns the previous object in
Erlang term order for the ordered_set table type. For
all other table types, <c>mnesia:dirty_next/2</c> and
diff --git a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
index 1c7e3662e1..30a8991465 100644
--- a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
+++ b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
@@ -335,7 +335,7 @@ ok
explicitly be set at table creation. The default is
<c>0</c>, but if <c>n_disc_copies</c> and
<c>n_disc_only_copies</c> also are <c>0</c>,
- <c>n_ram_copies</c>\011will default be set to <c>1</c>.
+ <c>n_ram_copies</c> will default be set to <c>1</c>.
</p>
</item>
<tag><c>{n_disc_copies, Int}</c></tag>
@@ -408,7 +408,7 @@ ok
(a@sam)4> SecProps = [{foreign_key, {prim_dict, sec_val}}].
[{foreign_key,{prim_dict,sec_val}}]
(a@sam)5> mnesia:create_table(sec_dict,
-\011 [{frag_properties, SecProps},
+ [{frag_properties, SecProps},
(a@sam)5> {attributes, [sec_key, sec_val]}]).
{atomic,ok}
(a@sam)6> Write = fun(Rec) -> mnesia:write(Rec) end.
@@ -418,23 +418,23 @@ ok
(a@sam)8> SecKey = 42.
42
(a@sam)9> mnesia:activity(sync_dirty, Write,
-\011\011 [{prim_dict, PrimKey, -11}], mnesia_frag).
+ [{prim_dict, PrimKey, -11}], mnesia_frag).
ok
(a@sam)10> mnesia:activity(sync_dirty, Write,
-\011\011 [{sec_dict, SecKey, PrimKey}], mnesia_frag).
+ [{sec_dict, SecKey, PrimKey}], mnesia_frag).
ok
(a@sam)11> mnesia:change_table_frag(prim_dict, {add_frag, [node()]}).
{atomic,ok}
(a@sam)12> SecRead = fun(PrimKey, SecKey) ->
-\011\011 mnesia:read({sec_dict, PrimKey}, SecKey, read) end.
+ mnesia:read({sec_dict, PrimKey}, SecKey, read) end.
#Fun<erl_eval>
(a@sam)13> mnesia:activity(transaction, SecRead,
-\011\011 [PrimKey, SecKey], mnesia_frag).
+ [PrimKey, SecKey], mnesia_frag).
[{sec_dict,42,11}]
(a@sam)14> Info = fun(Tab, Item) -> mnesia:table_info(Tab, Item) end.
#Fun<erl_eval>
(a@sam)15> mnesia:activity(sync_dirty, Info,
-\011\011 [prim_dict, frag_size], mnesia_frag).
+ [prim_dict, frag_size], mnesia_frag).
[{prim_dict,0},
{prim_dict_frag2,0},
{prim_dict_frag3,0},
@@ -444,7 +444,7 @@ ok
{prim_dict_frag7,0},
{prim_dict_frag8,0}]
(a@sam)16> mnesia:activity(sync_dirty, Info,
-\011\011 [sec_dict, frag_size], mnesia_frag).
+ [sec_dict, frag_size], mnesia_frag).
[{sec_dict,0},
{sec_dict_frag2,0},
{sec_dict_frag3,0},
@@ -1051,7 +1051,7 @@ ok
ActivityID will be received. Note that this event may still be received even
if no table events with a corresponding ActivityID were received, depending on
the tables to which the receiving process is subscribed.</p>
- <p>Dirty operations always only contain one update and thus no activity event is sent.</p>
+ <p>Dirty operations always only contain one update and thus no activity event is sent.</p>
</item>
</taglist>
</section>
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
index 5d3bcf830e..16e78ea0af 100644
--- a/lib/mnesia/doc/src/mnesia.xml
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -799,7 +799,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)
</item>
<item>
<p><c>{local_content, Bool}</c>, where <c>Bool</c> must be
- either <c>true</c> or <c>false</c>. The default value is <c>false</c>.\011 </p>
+ either <c>true</c> or <c>false</c>. The default value is <c>false</c>.</p>
</item>
</list>
<p>For example, the following call creates the <c>person</c> table
@@ -1022,7 +1022,7 @@ mnesia:create_table(person,
<name>dirty_last(Tab) -> Key | exit({aborted, Reason}) </name>
<fsummary>Return the key for the last record in a table.</fsummary>
<desc>
- <p>This function works exactly
+ <p>This function works exactly like
<c>mnesia:dirty_first/1</c> but returns the last object in
Erlang term order for the <c>ordered_set</c> table type. For
all other table types, <c>mnesia:dirty_first/1</c> and
@@ -1063,11 +1063,11 @@ mnesia:create_table(person,
<name>dirty_prev(Tab, Key) -> Key | exit({aborted, Reason}) </name>
<fsummary>Return the previous key in a table. </fsummary>
<desc>
- <p>This function works exactly
+ <p>This function works exactly like
<c>mnesia:dirty_next/2</c> but returns the previous object in
Erlang term order for the ordered_set table type. For
all other table types, <c>mnesia:dirty_next/2</c> and
- <c>mnesia:dirty_prev/2</c> are synonyms.\011 </p>
+ <c>mnesia:dirty_prev/2</c> are synonyms.</p>
</desc>
</func>
<func>
@@ -1334,7 +1334,7 @@ mnesia:create_table(person,
<name>foldr(Function, Acc, Table) -> NewAcc | transaction abort </name>
<fsummary>Call Function for each record in Table </fsummary>
<desc>
- <p>This function works exactly as
+ <p>This function works exactly like
<c>foldl/3</c> but iterates the table in the opposite order
for the <c>ordered_set</c> table type. For
all other table types, <c>foldr/3</c> and
@@ -1512,14 +1512,14 @@ mnesia:create_table(person,
<fsummary>Check if code is running in a transaction.</fsummary>
<desc>
<p>When this function is executed inside a transaction context
- it returns <c>true</c>, otherwise <c>false</c>.</p>
+ it returns <c>true</c>, otherwise <c>false</c>.</p>
</desc>
</func>
<func>
<name>last(Tab) -> Key | transaction abort </name>
<fsummary>Return the key for the last record in a table.</fsummary>
<desc>
- <p>This function works exactly
+ <p>This function works exactly like
<c>mnesia:first/1</c> but returns the last object in
Erlang term order for the <c>ordered_set</c> table type. For
all other table types, <c>mnesia:first/1</c> and
@@ -1698,11 +1698,11 @@ mnesia:create_table(person,
<name>prev(Tab, Key) -> Key | transaction abort </name>
<fsummary>Return the previous key in a table. </fsummary>
<desc>
- <p>This function works exactly
+ <p>This function works exactly like
<c>mnesia:next/2</c> but returns the previous object in
Erlang term order for the ordered_set table type. For
all other table types, <c>mnesia:next/2</c> and
- <c>mnesia:prev/2</c> are synonyms.\011 </p>
+ <c>mnesia:prev/2</c> are synonyms.</p>
</desc>
</func>
<func>
@@ -1891,10 +1891,10 @@ mnesia:create_table(person,
<p>For example to find the names of all male persons with an age over 30 in table
Tab do:</p>
<code type="none">
-\011 MatchHead = #person{name='$1', sex=male, age='$2', _='_'},
-\011 Guard = {'>', '$2', 30},
-\011 Result = '$1',
-\011 mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),
+MatchHead = #person{name='$1', sex=male, age='$2', _='_'},
+Guard = {'>', '$2', 30},
+Result = '$1',
+mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),
</code>
</desc>
</func>
@@ -2835,7 +2835,7 @@ raise(Name, Amount) ->
</func>
<func>
<name>write(Tab, Record, LockKind) -> transaction abort | ok </name>
- <fsummary>Write an record into the database.</fsummary>
+ <fsummary>Write a record into the database.</fsummary>
<desc>
<p>Writes the record <c>Record</c> to the table <c>Tab</c>.
</p>
diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml
index ca03327994..73162c3974 100644
--- a/lib/mnesia/doc/src/mnesia_frag_hash.xml
+++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml
index 966134d508..e08f3a42fc 100644
--- a/lib/mnesia/doc/src/mnesia_registry.xml
+++ b/lib/mnesia/doc/src/mnesia_registry.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1998</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 66242398d9..ccf70b8373 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -37,6 +37,102 @@
bugfixes for every release of Mnesia. Each release of Mnesia
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
+
+ <section><title>Mnesia 4.4.17</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Calling mnesia:first/1 on empty fragmented table works.
+ Thanks Magnus Henoch.</p>
+ <p>
+ Own Id: OTP-9108</p>
+ </item>
+ <item>
+ <p>
+ If Mnesia detects that the network is not fully connected
+ during start, Mnesia will not start until all nodes are
+ reachable.</p>
+ <p>
+ Own Id: OTP-9115 Aux Id: seq-11728 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix issues reported by dialyzer.</p>
+ <p>
+ Own Id: OTP-9107</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.4.16</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Sometimes a 'log_header' record was added to tables when
+ invoking mnesia:restore/2 with the option
+ 'recreate_tables'. Thanks Vance Shipley.</p>
+ <p>
+ Own Id: OTP-8960</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Compiler warnings were eliminated.</p>
+ <p>
+ Own Id: OTP-8855</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.4.15</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Eliminated warnings for auto-imported BIF clashes.</p>
+ <p>
+ Own Id: OTP-8840</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.4.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added mnesia:subscribe(activity) contributed by Bernard
+ Duggan.</p>
+ <p>
+ Own Id: OTP-8519</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
<section><title>Mnesia 4.4.13</title>
diff --git a/lib/mnesia/doc/src/part_notes_history.xml b/lib/mnesia/doc/src/part_notes_history.xml
index 177738623c..e4621dbbf7 100644
--- a/lib/mnesia/doc/src/part_notes_history.xml
+++ b/lib/mnesia/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2004</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/mnesia/examples/mnesia_meter.erl b/lib/mnesia/examples/mnesia_meter.erl
index ea74d8691b..68094c4431 100644
--- a/lib/mnesia/examples/mnesia_meter.erl
+++ b/lib/mnesia/examples/mnesia_meter.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -407,7 +407,7 @@ run(Nodes, Config, FunOverhead) ->
stop(Nodes),
Res.
-run_meter(M, Nodes, FunOverhead) when record(M, meter) ->
+run_meter(M, Nodes, FunOverhead) when is_record(M, meter) ->
io:format(".", []),
case catch init_records(M#meter.init, ?TIMES) of
{atomic, ok} ->
diff --git a/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src
index b3b9297db2..0eff761b61 100644
--- a/lib/mnesia/src/mnesia.appup.src
+++ b/lib/mnesia/src/mnesia.appup.src
@@ -1,69 +1,25 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"4.4.12",
- [
- {update, mnesia, soft, soft_purge, soft_purge, []},
- {update, mnesia_loader, soft, soft_purge, soft_purge, []},
- {update, mnesia_monitor, soft, soft_purge, soft_purge, []},
- {update, mnesia_tm, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.4.11",
- [
- {update, mnesia, soft, soft_purge, soft_purge, []},
- {update, mnesia_loader, soft, soft_purge, soft_purge, []},
- {update, mnesia_monitor, soft, soft_purge, soft_purge, []},
- {update, mnesia_tm, soft, soft_purge, soft_purge, []},
- {update, mnesia_locker, soft, soft_purge, soft_purge, []},
- {update, mnesia_controller, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.4.10",
- [
- {update, mnesia, soft, soft_purge, soft_purge, []},
- {update, mnesia_loader, soft, soft_purge, soft_purge, []},
- {update, mnesia_monitor, soft, soft_purge, soft_purge, []},
- {update, mnesia_tm, soft, soft_purge, soft_purge, []},
- {update, mnesia_locker, soft, soft_purge, soft_purge, []},
- {update, mnesia_controller, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.4.9", [{restart_application, mnesia}]},
- {"4.4.8", [{restart_application, mnesia}]},
- {"4.4.7", [{restart_application, mnesia}]}
+ {"4.4.16",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia_schema, soft, soft_purge, soft_purge, []}
+ ]},
+ {"4.4.15",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia, soft, soft_purge, soft_purge, []},
+ {update, mnesia_dumper, soft, soft_purge, soft_purge, []}
+ ]}
],
[
- {"4.4.12",
- [
- {update, mnesia, soft, soft_purge, soft_purge, []},
- {update, mnesia_loader, soft, soft_purge, soft_purge, []},
- {update, mnesia_monitor, soft, soft_purge, soft_purge, []},
- {update, mnesia_tm, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.4.11",
- [
- {update, mnesia, soft, soft_purge, soft_purge, []},
- {update, mnesia_loader, soft, soft_purge, soft_purge, []},
- {update, mnesia_monitor, soft, soft_purge, soft_purge, []},
- {update, mnesia_tm, soft, soft_purge, soft_purge, []},
- {update, mnesia_locker, soft, soft_purge, soft_purge, []},
- {update, mnesia_controller, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.4.10",
- [
- {update, mnesia, soft, soft_purge, soft_purge, []},
- {update, mnesia_loader, soft, soft_purge, soft_purge, []},
- {update, mnesia_monitor, soft, soft_purge, soft_purge, []},
- {update, mnesia_tm, soft, soft_purge, soft_purge, []},
- {update, mnesia_locker, soft, soft_purge, soft_purge, []},
- {update, mnesia_controller, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.4.9", [{restart_application, mnesia}]},
- {"4.4.8", [{restart_application, mnesia}]},
- {"4.4.7", [{restart_application, mnesia}]}
+ {"4.4.16",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia_schema, soft, soft_purge, soft_purge, []}
+ ]},
+ {"4.4.15",[
+ {update, mnesia_frag, soft, soft_purge, soft_purge, []},
+ {update, mnesia, soft, soft_purge, soft_purge, []},
+ {update, mnesia_dumper, soft, soft_purge, soft_purge, []}
+ ]}
]
}.
diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl
index fb29007780..025b32f506 100644
--- a/lib/mnesia/src/mnesia.erl
+++ b/lib/mnesia/src/mnesia.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -302,7 +302,7 @@ ms() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Activity mgt
--spec(abort/1 :: (_) -> no_return()).
+-spec abort(_) -> no_return().
abort(Reason) ->
exit({aborted, Reason}).
@@ -1835,6 +1835,7 @@ do_dirty_rpc(Tab, Node, M, F, Args) ->
%% Info
%% Info about one table
+-spec table_info(atom(), any()) -> any().
table_info(Tab, Item) ->
case get(mnesia_activity_state) of
undefined ->
@@ -1868,7 +1869,7 @@ any_table_info(Tab, Item) when is_atom(Tab) ->
type ->
case ?catch_val({Tab, setorbag}) of
{'EXIT', _} ->
- bad_info_reply(Tab, Item);
+ abort({no_exists, Tab, Item});
Val ->
Val
end;
@@ -1886,7 +1887,7 @@ any_table_info(Tab, Item) when is_atom(Tab) ->
_ ->
case ?catch_val({Tab, Item}) of
{'EXIT', _} ->
- bad_info_reply(Tab, Item);
+ abort({no_exists, Tab, Item});
Val ->
Val
end
diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl
index 37a8258d74..47dcdad7ac 100644
--- a/lib/mnesia/src/mnesia_bup.erl
+++ b/lib/mnesia/src/mnesia_bup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -65,6 +65,8 @@
default_op = keep_tables
}).
+-type fallback_args() :: #fallback_args{}.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Backup iterator
@@ -108,6 +110,7 @@ iter(R, Header, Schema, Fun, Acc, BupItems) ->
Acc2 = Fun(BupItems, Header, Schema, Acc),
iter(R, Header, Schema, Fun, Acc2, []).
+-spec safe_apply(#restore{}, atom(), list()) -> tuple().
safe_apply(R, write, [_, Items]) when Items =:= [] ->
R;
safe_apply(R, What, Args) ->
@@ -570,6 +573,7 @@ fallback_bup() -> mnesia_lib:dir(fallback_name()).
fallback_tmp_name() -> "FALLBACK.TMP".
%% fallback_full_tmp_name() -> mnesia_lib:dir(fallback_tmp_name()).
+-spec fallback_receiver(pid(), fallback_args()) -> no_return().
fallback_receiver(Master, FA) ->
process_flag(trap_exit, true),
@@ -981,6 +985,7 @@ do_uninstall_fallback(FA) ->
{error, Reason}
end.
+-spec uninstall_fallback_master(pid(), fallback_args()) -> no_return().
uninstall_fallback_master(ClientPid, FA) ->
process_flag(trap_exit, true),
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 9bc480e619..021be8af2a 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -52,6 +52,7 @@
async_dump_log/1,
sync_dump_log/1,
connect_nodes/1,
+ connect_nodes/2,
wait_for_schema_commit_lock/0,
release_schema_commit_lock/0,
create_table/1,
@@ -94,9 +95,11 @@
load_and_reply/2,
send_and_reply/2,
wait_for_tables_init/2,
- connect_nodes2/2
+ connect_nodes2/3
]).
+-compile({no_auto_import,[error/2]}).
+
-import(mnesia_lib, [set/2, add/2]).
-import(mnesia_lib, [fatal/2, error/2, verbose/2, dbg_out/2]).
@@ -420,12 +423,15 @@ try_schedule_late_disc_load(Tabs, Reason, MsgTag) ->
[[Tabs, Reason, MsgTag], AbortReason])
end.
-connect_nodes(Ns) ->
+connect_nodes(Ns) ->
+ connect_nodes(Ns, fun default_merge/1).
+
+connect_nodes(Ns, UserFun) ->
case mnesia:system_info(is_running) of
no ->
{error, {node_not_running, node()}};
yes ->
- Pid = spawn_link(?MODULE,connect_nodes2,[self(),Ns]),
+ Pid = spawn_link(?MODULE,connect_nodes2,[self(),Ns, UserFun]),
receive
{?MODULE, Pid, Res, New} ->
case Res of
@@ -443,7 +449,7 @@ connect_nodes(Ns) ->
end
end.
-connect_nodes2(Father, Ns) ->
+connect_nodes2(Father, Ns, UserFun) ->
Current = val({current, db_nodes}),
abcast([node()|Ns], {merging_schema, node()}),
{NewC, OldC} = mnesia_recover:connect_nodes(Ns),
@@ -451,7 +457,7 @@ connect_nodes2(Father, Ns) ->
New1 = mnesia_lib:intersect(Ns, Connected),
New = New1 -- Current,
process_flag(trap_exit, true),
- Res = try_merge_schema(New),
+ Res = try_merge_schema(New, UserFun),
Msg = {schema_is_merged, [], late_merge, []},
multicall([node()|Ns], Msg),
After = val({current, db_nodes}),
@@ -465,7 +471,7 @@ connect_nodes2(Father, Ns) ->
merge_schema() ->
AllNodes = mnesia_lib:all_nodes(),
- case try_merge_schema(AllNodes) of
+ case try_merge_schema(AllNodes, fun default_merge/1) of
ok ->
schema_is_merged();
{aborted, {throw, Str}} when is_list(Str) ->
@@ -474,8 +480,11 @@ merge_schema() ->
fatal("Failed to merge schema: ~p~n", [Else])
end.
-try_merge_schema(Nodes) ->
- case mnesia_schema:merge_schema() of
+default_merge(F) ->
+ F([]).
+
+try_merge_schema(Nodes, UserFun) ->
+ case mnesia_schema:merge_schema(UserFun) of
{atomic, not_merged} ->
%% No more nodes that we need to merge the schema with
ok;
@@ -488,11 +497,11 @@ try_merge_schema(Nodes) ->
im_running(OldFriends, NewFriends),
im_running(NewFriends, OldFriends),
- try_merge_schema(Nodes);
+ try_merge_schema(Nodes, UserFun);
{atomic, {"Cannot get cstructs", Node, Reason}} ->
dbg_out("Cannot get cstructs, Node ~p ~p~n", [Node, Reason]),
timer:sleep(1000), % Avoid a endless loop look alike
- try_merge_schema(Nodes);
+ try_merge_schema(Nodes, UserFun);
Other ->
Other
end.
@@ -1842,17 +1851,20 @@ reply(ReplyTo, Reply) ->
add_worker(Worker = #dump_log{}, State) ->
InitBy = Worker#dump_log.initiated_by,
Queue = State#state.dumper_queue,
- case lists:keymember(InitBy, #dump_log.initiated_by, Queue) of
- true when Worker#dump_log.opt_reply_to == undefined ->
- %% The same threshold has been exceeded again,
- %% before we have had the possibility to
- %% process the older one.
- DetectedBy = {dump_log, InitBy},
- Event = {mnesia_overload, DetectedBy},
- mnesia_lib:report_system_event(Event);
- _ ->
- ignore
- end,
+ Status =
+ case lists:keymember(InitBy, #dump_log.initiated_by, Queue) of
+ true when Worker#dump_log.opt_reply_to == undefined ->
+ %% The same threshold has been exceeded again,
+ %% before we have had the possibility to
+ %% process the older one.
+ DetectedBy = {dump_log, InitBy},
+ Event = {mnesia_overload, DetectedBy},
+ mnesia_lib:report_system_event(Event),
+ true;
+ _ ->
+ false
+ end,
+ mnesia_recover:log_dump_overload(Status),
Queue2 = Queue ++ [Worker],
State2 = State#state{dumper_queue = Queue2},
opt_start_worker(State2);
diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl
index f669d009c6..644133cf5d 100644
--- a/lib/mnesia/src/mnesia_dumper.erl
+++ b/lib/mnesia/src/mnesia_dumper.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -643,7 +643,7 @@ insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) ->
true -> ignore;
false ->
mnesia_log:open_log(temp,
- mnesia_log:dcl_log_header(),
+ mnesia_log:dcd_log_header(),
Dcd,
false,
false,
diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl
index a2958ab461..9e77fe0b9f 100644
--- a/lib/mnesia/src/mnesia_frag.erl
+++ b/lib/mnesia/src/mnesia_frag.erl
@@ -1,7 +1,7 @@
%%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -209,7 +209,7 @@ first(ActivityId, Opaque, Tab) ->
end
end.
-search_first(ActivityId, Opaque, Tab, N, FH) when N =< FH#frag_state.n_fragments ->
+search_first(ActivityId, Opaque, Tab, N, FH) when N < FH#frag_state.n_fragments ->
NextN = N + 1,
NextFrag = n_to_frag_name(Tab, NextN),
case mnesia:first(ActivityId, Opaque, NextFrag) of
@@ -448,13 +448,15 @@ do_remote_select(_ReplyTo, _Ref, [], _MatchSpec) ->
local_collect(Ref, Pid, Type, LocalMatch, OldSelectFun) ->
receive
- {local_select, Ref, LocalRes} ->
- remote_collect(Ref, Type, LocalRes, LocalMatch, OldSelectFun);
+ {local_select, Ref, ok} ->
+ remote_collect_ok(Ref, Type, LocalMatch, OldSelectFun);
+ {local_select, Ref, {error, Reason}} ->
+ remote_collect_error(Ref, Type, Reason, OldSelectFun);
{'EXIT', Pid, Reason} ->
- remote_collect(Ref, Type, {error, Reason}, [], OldSelectFun)
+ remote_collect_error(Ref, Type, Reason, OldSelectFun)
end.
-remote_collect(Ref, Type, LocalRes = ok, Acc, OldSelectFun) ->
+remote_collect_ok(Ref, Type, Acc, OldSelectFun) ->
receive
{remote_select, Ref, Node, RemoteRes} ->
case RemoteRes of
@@ -463,19 +465,21 @@ remote_collect(Ref, Type, LocalRes = ok, Acc, OldSelectFun) ->
ordered_set -> lists:merge(RemoteMatch, Acc);
_ -> RemoteMatch ++ Acc
end,
- remote_collect(Ref, Type, LocalRes, Matches, OldSelectFun);
+ remote_collect_ok(Ref, Type, Matches, OldSelectFun);
_ ->
- remote_collect(Ref, Type, {error, {node_not_running, Node}}, [], OldSelectFun)
+ Reason = {node_not_running, Node},
+ remote_collect_error(Ref, Type, Reason, OldSelectFun)
end
after 0 ->
Acc
- end;
-remote_collect(Ref, Type, LocalRes = {error, Reason}, _Acc, OldSelectFun) ->
+ end.
+
+remote_collect_error(Ref, Type, Reason, OldSelectFun) ->
receive
{remote_select, Ref, _Node, _RemoteRes} ->
- remote_collect(Ref, Type, LocalRes, [], OldSelectFun)
+ remote_collect_error(Ref, Type, Reason, OldSelectFun)
after 0 ->
- mnesia:abort(Reason)
+ mnesia:abort({error, Reason})
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index 4e6e8a997c..61210d7e55 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl
index dba808e66e..36bcfe8de9 100644
--- a/lib/mnesia/src/mnesia_lib.erl
+++ b/lib/mnesia/src/mnesia_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -113,6 +113,9 @@
mkcore/1,
not_active_here/1,
other_val/2,
+ overload_read/0,
+ overload_read/1,
+ overload_set/2,
pad_name/3,
random_time/2,
read_counter/1,
@@ -396,7 +399,7 @@ other_val(Var, Other) ->
pr_other(Var, Other)
end.
--spec(pr_other/2 :: (_,_) -> no_return()).
+-spec pr_other(_,_) -> no_return().
pr_other(Var, Other) ->
Why =
@@ -551,6 +554,33 @@ cs_to_nodes(Cs) ->
Cs#cstruct.disc_only_copies ++
Cs#cstruct.disc_copies ++
Cs#cstruct.ram_copies.
+
+overload_types() ->
+ [mnesia_tm, mnesia_dump_log].
+
+valid_overload_type(T) ->
+ case lists:member(T, overload_types()) of
+ false ->
+ erlang:error(bad_type);
+ true ->
+ true
+ end.
+
+overload_set(Type, Bool) when is_boolean(Bool) ->
+ valid_overload_type(Type),
+ set({overload, Type}, Bool).
+
+overload_read() ->
+ [{T, overload_read(T)} || T <- overload_types()].
+
+overload_read(T) ->
+ case ?catch_val({overload, T}) of
+ {'EXIT',_} ->
+ valid_overload_type(T),
+ false;
+ Flag when is_boolean(Flag) ->
+ Flag
+ end.
dist_coredump() ->
dist_coredump(all_nodes()).
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index cfa3f171b2..ca0cc79c45 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -49,6 +49,8 @@
system_code_change/4
]).
+-compile({no_auto_import,[error/2]}).
+
-include("mnesia.hrl").
-import(mnesia_lib, [dbg_out/2, error/2, verbose/2]).
@@ -1102,6 +1104,7 @@ do_stop() ->
system_continue(_Parent, _Debug, State) ->
loop(State).
+-spec system_terminate(_, _, _, _) -> no_return().
system_terminate(_Reason, _Parent, _Debug, _State) ->
do_stop().
diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl
index 00ec4740ee..9e804cc4c2 100644
--- a/lib/mnesia/src/mnesia_log.erl
+++ b/lib/mnesia/src/mnesia_log.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -182,6 +182,8 @@
]).
+-compile({no_auto_import,[error/2]}).
+
-include("mnesia.hrl").
-import(mnesia_lib, [val/1, dir/1]).
-import(mnesia_lib, [exists/1, fatal/2, error/2, dbg_out/2]).
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index 5bd93d6b9b..b6eda9ad3a 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -70,6 +70,8 @@
negotiate_protocol_impl/2
]).
+-compile({no_auto_import,[error/2]}).
+
-import(mnesia_lib, [dbg_out/2, verbose/2, error/2, fatal/2, set/2]).
-include("mnesia.hrl").
diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl
index 6c53c2e752..b3eed1de6e 100644
--- a/lib/mnesia/src/mnesia_recover.erl
+++ b/lib/mnesia/src/mnesia_recover.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -36,6 +36,7 @@
incr_trans_tid_serial/0,
init/0,
log_decision/1,
+ log_dump_overload/1,
log_master_nodes/3,
log_mnesia_down/1,
log_mnesia_up/1,
@@ -61,6 +62,7 @@
code_change/3
]).
+-compile({no_auto_import,[error/2]}).
-include("mnesia.hrl").
-import(mnesia_lib, [set/2, verbose/2, error/2, fatal/2]).
@@ -70,6 +72,7 @@
unclear_decision,
unclear_waitfor,
tm_queue_len = 0,
+ log_dump_overload = false,
initiated = false,
early_msgs = []
}).
@@ -277,6 +280,9 @@ mnesia_down(Node) ->
cast({mnesia_down, Node})
end.
+log_dump_overload(Flag) when is_boolean(Flag) ->
+ cast({log_dump_overload, Flag}).
+
log_master_nodes(Args, UseDir, IsRunning) ->
if
IsRunning == yes ->
@@ -818,6 +824,12 @@ handle_cast({announce_all, Nodes}, State) ->
announce_all(Nodes),
{noreply, State};
+handle_cast({log_dump_overload, Flag}, State) when is_boolean(Flag) ->
+ Prev = State#state.log_dump_overload,
+ Overload = Prev orelse Flag,
+ mnesia_lib:overload_set(mnesia_dump_log, Overload),
+ {noreply, State#state{log_dump_overload = Flag}};
+
handle_cast(Msg, State) ->
error("~p got unexpected cast: ~p~n", [?MODULE, Msg]),
{noreply, State}.
@@ -851,12 +863,14 @@ handle_info(check_overload, S) ->
Len > Threshold, Prev > Threshold ->
What = {mnesia_tm, message_queue_len, [Prev, Len]},
mnesia_lib:report_system_event({mnesia_overload, What}),
+ mnesia_lib:overload_set(mnesia_tm, true),
{noreply, S#state{tm_queue_len = 0}};
Len > Threshold ->
{noreply, S#state{tm_queue_len = Len}};
true ->
+ mnesia_lib:overload_set(mnesia_tm, false),
{noreply, S#state{tm_queue_len = 0}}
end;
undefined ->
@@ -905,7 +919,23 @@ terminate(Reason, State) ->
%% Purpose: Upgrade process when its code is to be changed
%% Returns: {ok, NewState}
%%----------------------------------------------------------------------
-code_change(_OldVsn, State, _Extra) ->
+code_change(_OldVsn, {state,
+ Supervisor,
+ Unclear_pid,
+ Unclear_decision,
+ Unclear_waitfor,
+ Tm_queue_len,
+ Initiated,
+ Early_msgs
+ }, _Extra) ->
+ {ok, #state{supervisor = Supervisor,
+ unclear_pid = Unclear_pid,
+ unclear_decision = Unclear_decision,
+ unclear_waitfor = Unclear_waitfor,
+ tm_queue_len = Tm_queue_len,
+ initiated = Initiated,
+ early_msgs = Early_msgs}};
+code_change(_OldVsn, #state{} = State, _Extra) ->
{ok, State}.
%%%----------------------------------------------------------------------
diff --git a/lib/mnesia/src/mnesia_registry.erl b/lib/mnesia/src/mnesia_registry.erl
index 9805d48697..202689ae5e 100644
--- a/lib/mnesia/src/mnesia_registry.erl
+++ b/lib/mnesia/src/mnesia_registry.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -65,6 +65,8 @@
%%%----------------------------------------------------------------------
%% External exports
+%% Avoid warning for local function max/2 clashing with autoimported BIF.
+-compile({no_auto_import,[max/2]}).
-export([start_dump/2, start_restore/2]).
-export([create_table/1, create_table/2]).
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index 354431a296..d1d892a387 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -62,6 +62,7 @@
list2cs/1,
lock_schema/0,
merge_schema/0,
+ merge_schema/1,
move_table/3,
opt_create_dir/2,
prepare_commit/3,
@@ -2650,10 +2651,16 @@ make_dump_tables([]) ->
%% Merge the local schema with the schema on other nodes
merge_schema() ->
- schema_transaction(fun() -> do_merge_schema() end).
+ schema_transaction(fun() -> do_merge_schema([]) end).
+
+merge_schema(UserFun) ->
+ schema_transaction(fun() -> UserFun(fun(Arg) -> do_merge_schema(Arg) end) end).
+
-do_merge_schema() ->
+do_merge_schema(LockTabs0) ->
{_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
+ LockTabs = [{T, tab_to_nodes(T)} || T <- LockTabs0],
+ [get_tid_ts_and_lock(T,write) || {T,_} <- LockTabs],
Connected = val(recover_nodes),
Running = val({current, db_nodes}),
Store = Ts#tidstore.store,
@@ -2665,9 +2672,12 @@ do_merge_schema() ->
mnesia:abort({bad_commit, {missing_lock, Miss}})
end,
case Connected -- Running of
- [Node | _] ->
+ [Node | _] = OtherNodes ->
%% Time for a schema merging party!
mnesia_locker:wlock_no_exist(Tid, Store, schema, [Node]),
+ [mnesia_locker:wlock_no_exist(
+ Tid, Store, T, mnesia_lib:intersect(Ns, OtherNodes))
+ || {T,Ns} <- LockTabs],
case rpc:call(Node, mnesia_controller, get_cstructs, []) of
{cstructs, Cstructs, RemoteRunning1} ->
LockedAlready = Running ++ [Node],
@@ -2676,11 +2686,15 @@ do_merge_schema() ->
if
RemoteRunning /= RemoteRunning1 ->
mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n",
- [node(), RemoteRunning1 -- RemoteRunning]);
+ [node(), RemoteRunning1 -- RemoteRunning]),
+ mnesia:abort({node_not_running, RemoteRunning1 -- RemoteRunning});
true -> ok
end,
NeedsLock = RemoteRunning -- LockedAlready,
mnesia_locker:wlock_no_exist(Tid, Store, schema, NeedsLock),
+ [mnesia_locker:wlock_no_exist(Tid, Store, T,
+ mnesia_lib:intersect(Ns,NeedsLock))
+ || {T,Ns} <- LockTabs],
{value, SchemaCs} =
lists:keysearch(schema, #cstruct.name, Cstructs),
@@ -2714,6 +2728,10 @@ do_merge_schema() ->
not_merged
end.
+tab_to_nodes(Tab) when is_atom(Tab) ->
+ Cs = val({Tab, cstruct}),
+ mnesia_lib:cs_to_nodes(Cs).
+
make_merge_schema(Node, [Cs | Cstructs]) ->
Ops = do_make_merge_schema(Node, Cs),
Ops ++ make_merge_schema(Node, Cstructs);
@@ -3012,7 +3030,9 @@ announce_im_running([N | Ns], SchemaCs) ->
mnesia_lib:add({current, db_nodes}, N),
mnesia_controller:add_active_replica(schema, N, SchemaCs);
false ->
- ignore
+ mnesia_lib:error("Mnesia on ~p could not connect to node ~p~n",
+ [node(), N]),
+ mnesia:abort({node_not_running, N})
end,
announce_im_running(Ns, SchemaCs);
announce_im_running([], _) ->
diff --git a/lib/mnesia/src/mnesia_snmp_hook.erl b/lib/mnesia/src/mnesia_snmp_hook.erl
index 8b4b5231e1..893b39f3c0 100644
--- a/lib/mnesia/src/mnesia_snmp_hook.erl
+++ b/lib/mnesia/src/mnesia_snmp_hook.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/mnesia/src/mnesia_subscr.erl b/lib/mnesia/src/mnesia_subscr.erl
index 93d4a86f7f..415c69d508 100644
--- a/lib/mnesia/src/mnesia_subscr.erl
+++ b/lib/mnesia/src/mnesia_subscr.erl
@@ -43,6 +43,8 @@
code_change/3
]).
+-compile({no_auto_import,[error/2]}).
+
-include("mnesia.hrl").
-import(mnesia_lib, [error/2]).
diff --git a/lib/mnesia/src/mnesia_text.erl b/lib/mnesia/src/mnesia_text.erl
index f1a28bf43d..ab1362f6b6 100644
--- a/lib/mnesia/src/mnesia_text.erl
+++ b/lib/mnesia/src/mnesia_text.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,6 +20,8 @@
%%
-module(mnesia_text).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([parse/1, file/1, load_textfile/1, dump_to_textfile/1]).
load_textfile(File) ->
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index f3ffac5493..bb8e788b40 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1604,6 +1604,7 @@ tell_participants([Pid | Pids], Msg) ->
tell_participants([], _Msg) ->
ok.
+-spec commit_participant(_, _, _, _, _) -> no_return().
%% Trap exit because we can get a shutdown from application manager
commit_participant(Coord, Tid, Bin, DiscNs, RamNs) when is_binary(Bin) ->
process_flag(trap_exit, true),
@@ -2279,6 +2280,7 @@ fixtable(Tab, Lock, Me) ->
system_continue(_Parent, _Debug, State) ->
doit_loop(State).
+-spec system_terminate(_, _, _, _) -> no_return().
system_terminate(_Reason, _Parent, _Debug, State) ->
do_stop(State).
diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile
new file mode 100644
index 0000000000..b165924ef2
--- /dev/null
+++ b/lib/mnesia/test/Makefile
@@ -0,0 +1,118 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ mt \
+ mnesia_SUITE \
+ mnesia_test_lib \
+ mnesia_install_test \
+ mnesia_registry_test \
+ mnesia_config_test \
+ mnesia_frag_test \
+ mnesia_inconsistent_database_test \
+ mnesia_config_backup \
+ mnesia_config_event \
+ mnesia_examples_test \
+ mnesia_nice_coverage_test \
+ mnesia_evil_coverage_test \
+ mnesia_evil_backup \
+ mnesia_trans_access_test \
+ mnesia_dirty_access_test \
+ mnesia_atomicity_test \
+ mnesia_consistency_test \
+ mnesia_isolation_test \
+ mnesia_durability_test \
+ mnesia_recovery_test \
+ mnesia_qlc_test \
+ mnesia_schema_recovery_test \
+ mnesia_measure_test \
+ mnesia_cost \
+ mnesia_dbn_meters
+
+MnesiaExamplesDir := ../examples
+
+ExampleModules = \
+ company \
+ company_o \
+ bup \
+ mnesia_meter \
+ mnesia_tpcb
+ExamplesHrl = \
+ company.hrl \
+ company_o.hrl
+
+ERL_FILES= $(MODULES:%=%.erl) $(ExampleModules:%=$(MnesiaExamplesDir)/%.erl)
+
+HRL_FILES= mnesia_test_lib.hrl $(ExamplesHrl:%=$(MnesiaExamplesDir)/%)
+
+TARGET_FILES= \
+ $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ExampleModules:%=$(EBIN)/%.$(EMULATOR))
+
+INSTALL_PROGS= $(TARGET_FILES)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/mnesia_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+#ERL_COMPILE_FLAGS +=
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(TARGET_FILES)
+
+$(EBIN)/%.beam: $(MnesiaExamplesDir)/%.erl
+ $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(EBIN) $<
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+ $(INSTALL_SCRIPT) mt $(INSTALL_PROGS) $(RELSYSDIR)
+# chmod -R u+w $(RELSYSDIR)
+# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
+
+
diff --git a/lib/mnesia/test/README b/lib/mnesia/test/README
new file mode 100644
index 0000000000..e0ced7399d
--- /dev/null
+++ b/lib/mnesia/test/README
@@ -0,0 +1,107 @@
+This directory contains the test suite of Mnesia.
+Compile it with "erl -make".
+
+Test cases are identified with a {Mod, Fun} tuple that maps
+to a function Mod:Fun(Config), where the test case hopefully
+is implemented. The test suite is organized in a hierarchy
+with {mnesia_SUITE, all} as the top.
+
+The module called mt, implements various convenience functions
+to ease up the execution of test cases. It does also provide
+aliases for some test cases. For example the atom Mod is an
+alias for {Mod, all}, the atom all for {mnesia_SUITE, all},
+evil for mnesia_evil_coverage_test etc.
+
+ mt:struct(TestCase)
+
+ Displays the test case structure from TestCase
+ and downwards the hierarchy. E.g. mt:struct(all)
+ will display the entire test suite.
+
+ mt:t(TestCase), mt:t(TestCase, Config)
+
+ Runs a single test case or a hierarchy of test cases.
+ mt:t(silly) is be a good starter, but you may also
+ try mt:t(all) directly if you feel lucky.
+
+ The identity of the last run test case and the outcome of
+ it is stored on file. mt:t() will re-run the last test case.
+
+ The Config argument contains various configuration
+ parameters for the test cases, such as which nodes that
+ are available for running the test suite. The default
+ settings should be enough for the most. Use mt:read_config()
+ to get the current default setting and change it with
+ mt:write_config(Config).
+
+ mt:doc(TestCase)
+
+ Generates html documentation for the test suite.
+
+In order to be able to run the test suite, the Erlang node must
+be started with the distribution enabled and the code path must
+be set to the mnesia/ebin, mnesia/examples, and mnesia/test
+directories. E.g. the following would do:
+
+ erl -sname a -pa $top/examples -pa $top/src -pa $top/ebin
+
+where $top is the path to the Mnesia installation. Many test
+cases needs 2 or 3 nodes. The node names may explicitly be
+stated as test suite configuration parameters, but by default
+the extra node names are generated. In this example the names
+will be: a, a1 and a2. It is enough to start the first node
+manually, the extra nodes will automatically be started if
+neccessary.
+
+The attached UNIX shell script mt, does not work on all
+platforms, but it may be used as a source for inspiration. It
+starts three Erlang nodes in one xterm's each. The main xterm
+(a@localhost) logs all output in the Erlang shell to a
+file. The file is piped thru grep to easily find successful
+test cases (i.e. test cases that encountered an error).
+
+During development we want to be able to run the test cases
+in the debugger. This demands a little bit of preparations:
+
+ - Start the neccessary number of nodes (normally 3).
+ This may either be done by running the mt script or
+ by starting the main node and then invoke mt:start_nodes()
+ to start the extra nodes with slave.
+
+ - Ensure that the nodes are connected. The easiest way to do
+ this is by invoking mt:ping().
+
+ - Load all files that needs to be interpreted. This is typically
+ all Mnesia files plus the test case. By invoking mnesia:ni()
+ and mnesia:ni([TestModule]) the neccessary modules will be
+ loaded on all CONNECTED nodes.
+
+The test case execution is supervised in order to ensure that no test
+case exceeds its maximum time limit, which by default is 5 minutes.
+When the limit is reached, the running test case gets aborted and the
+test server runs the next test case in line. This behaviour is useful
+when running the entire test suite during the night, but it is really
+annoying during debugging.
+
+ Use the "erl -mnesia_test_timeout" flag to disable the test case
+ time limit mechanism.
+
+Some mechanisms in Mnesia are almost impossible to test with a
+white box technique. In order to be able to write predictable
+test cases which tests the same thing every time it is run,
+Mnesia has been instrumented with debug functions. These may be
+controlled from a test program. For example to verify that the
+commit protocols work it is essential that it is possible to
+ensure that we are able to kill Mnesia in the most critical
+situations. Normally Mnesia is compiled with the debug
+functions disabled and this means that test cases which
+requires this functionality will be skipped. The mnesia:ni(),
+mentioned above, functions ensures that the interpreted code is
+instrumented with Mnesia's debug functionality. The mnesia:nc()
+functions compiles Mnesia with the debug setting enabled.
+
+Happy bug hunting!
+
+ Hakan Mattsson <[email protected]>
+
+
diff --git a/lib/mnesia/test/mnesia.cover b/lib/mnesia/test/mnesia.cover
new file mode 100644
index 0000000000..66ffc06e89
--- /dev/null
+++ b/lib/mnesia/test/mnesia.cover
@@ -0,0 +1,2 @@
+{incl_app,mnesia,details}.
+
diff --git a/lib/mnesia/test/mnesia.spec b/lib/mnesia/test/mnesia.spec
new file mode 100644
index 0000000000..204d1519cb
--- /dev/null
+++ b/lib/mnesia/test/mnesia.spec
@@ -0,0 +1,76 @@
+{suites,"../mnesia_test",all}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [ram_meter],
+ "Takes to long time"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [disc_meter],
+ "Takes to long time"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [disc_only_meter],
+ "Takes to long time"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,[cost],"Takes to long time"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [dbn_meters],
+ "Takes to long time"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [ram_tpcb,disc_tpcb,disc_only_tpcb],
+ "Takes to long time"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [reader_disturbed_by_node_down,writer_disturbed_by_node_down,
+ reader_disturbed_by_node_up,writer_disturbed_by_node_up,
+ reader_disturbed_by_schema_ops,writer_disturbed_by_schema_ops,
+ reader_disturbed_by_checkpoint,writer_disturbed_by_checkpoint,
+ reader_disturbed_by_dump_log,writer_disturbed_by_dump_log,
+ reader_disturbed_by_backup,writer_disturbed_by_backup,
+ reader_disturbed_by_restore,writer_disturbed_by_restore,
+ reader_competing_with_reader,reader_competing_with_writer,
+ writer_competing_with_reader,writer_competing_with_writer],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [measure_resource_consumption,determine_resource_leakage],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [determine_system_limits,performance_at_min_config,
+ performance_at_max_config,performance_at_full_load,
+ resource_consumption_at_min_config,
+ resource_consumption_at_max_config,
+ resource_consumption_at_full_load],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [ram_tpcb,disc_tpcb,disc_only_tpcb],
+ "Takes too much time and memory"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [measure_all_api_functions],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_measure_test,
+ [mnemosyne_vs_mnesia_kernel],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_examples_test,
+ [company],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_config_test,
+ [ignore_fallback_at_startup],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_evil_backup,
+ [local_backup_checkpoint],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_config_test,
+ [max_wait_for_decision],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_recovery_test,
+ [after_full_disc_partition],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_recovery_test,
+ [system_upgrade],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_consistency_test,
+ [consistency_after_change_table_copy_type],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_consistency_test,
+ [consistency_after_transform_table_ram,
+ consistency_after_transform_table_disc,
+ consistency_after_transform_table_disc_only],
+ "Not yet implemented"}.
+{skip_cases,"../mnesia_test",mnesia_consistency_test,
+ [consistency_after_rename_of_node],
+ "Not yet implemented"}.
diff --git a/lib/mnesia/test/mnesia.spec.vxworks b/lib/mnesia/test/mnesia.spec.vxworks
new file mode 100644
index 0000000000..11c01ea3fe
--- /dev/null
+++ b/lib/mnesia/test/mnesia.spec.vxworks
@@ -0,0 +1,362 @@
+{topcase, {dir, "../mnesia_test"}}.
+{require_nodenames, 3}.
+{diskless, true}.
+{skip, {mnesia_measure_test, all, "Too heavy"}}.
+%{mnesia_install_test, silly_durability} 'IMPL'
+%{mnesia_install_test, silly_move} 'IMPL'
+{skip, {mnesia_install_test, silly_upgrade, "Uses disk"}}.
+%{mnesia_install_test, conflict} 'IMPL'
+%{mnesia_install_test, dist} 'IMPL'
+{skip, {mnesia_examples_test, all, "Uses disk"}}.
+{skip, {mnesia_nice_coverage_test, all, "Uses disk"}}.
+
+%{mnesia_evil_coverage_test, system_info} 'IMPL'
+%{mnesia_evil_coverage_test, table_info} 'IMPL'
+%{mnesia_evil_coverage_test, error_description} 'IMPL'
+{skip, {mnesia_evil_coverage_test, db_node_lifecycle, "Uses disk"}}.
+{skip, {mnesia_evil_coverage_test, local_content, "Uses disk"}}.
+%{mnesia_evil_coverage_test, start_and_stop} 'IMPL'
+%{mnesia_evil_coverage_test, transaction} 'IMPL'
+{skip, {mnesia_evil_coverage_test, checkpoint, "Uses disk"}}.
+{skip, {mnesia_evil_backup, backup, "Uses disk"}}.
+{skip, {mnesia_evil_backup, global_backup_checkpoint, "Uses disk"}}.
+{skip, {mnesia_evil_backup, incremental_backup_checkpoint, "Uses disk"}}.
+{skip, {mnesia_evil_backup, local_backup_checkpoint, "Uses disk"}}.
+{skip, {mnesia_evil_backup, selective_backup_checkpoint, "Uses disk"}}.
+{skip, {mnesia_evil_backup, restore_errors, "Uses disk"}}.
+{skip, {mnesia_evil_backup, restore_clear, "Uses disk"}}.
+{skip, {mnesia_evil_backup, restore_keep, "Uses disk"}}.
+{skip, {mnesia_evil_backup, restore_recreate, "Uses disk"}}.
+{skip, {mnesia_evil_backup, traverse_backup, "Uses disk"}}.
+{skip, {mnesia_evil_backup, install_fallback, "Uses disk"}}.
+{skip, {mnesia_evil_backup, uninstall_fallback, "Uses disk"}}.
+{skip, {mnesia_evil_backup, local_fallback, "Uses disk"}}.
+%{mnesia_evil_coverage_test, table_lifecycle} 'IMPL'
+{skip, {mnesia_evil_coverage_test, replica_management, "Uses disk"}}.
+%{mnesia_evil_coverage_test, change_table_access_mode} 'IMPL'
+%{mnesia_evil_coverage_test, change_table_load_order} 'IMPL'
+{skip, {mnesia_evil_coverage_test, set_master_nodes, "Uses disk"}}.
+{skip, {mnesia_evil_coverage_test, offline_set_master_nodes, "Uses disk"}}.
+{skip, {mnesia_evil_coverage_test, replica_location, "Uses disk"}}.
+%{mnesia_evil_coverage_test, add_table_index_ram} 'IMPL'
+{skip, {mnesia_trans_access_test, add_table_index_disc, "Uses disc"}}.
+{skip, {mnesia_trans_access_test, add_table_index_disc_only, "Uses disc"}}.
+%{mnesia_evil_coverage_test, create_live_table_index_ram} 'IMPL'
+{skip, {mnesia_trans_access_test, create_live_table_index_disc, "Uses disc"}}.
+{skip, {mnesia_trans_access_test, create_live_table_index_disc_only, "Uses disc"}}.
+%{mnesia_evil_coverage_test, del_table_index_ram} 'IMPL'
+{skip, {mnesia_trans_access_test, del_table_index_disc, "Uses disc"}}.
+{skip, {mnesia_trans_access_test, del_table_index_disc_only, "Uses disc"}}.
+{skip, {mnesia_trans_access_test, idx_schema_changes_ram, "Uses disk"}}.
+{skip, {mnesia_trans_access_test, idx_schema_changes_disc, "Uses disc"}}.
+{skip, {mnesia_trans_access_test, idx_schema_changes_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_write_ram} 'IMPL'
+
+{skip, {mnesia_dirty_access_test, dirty_write_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_write_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_read_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_read_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_read_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_update_counter_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_update_counter_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_update_counter_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_delete_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_delete_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_delete_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_delete_object_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_delete_object_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_delete_object_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_match_object_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_match_object_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_match_object_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_index_match_object_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_index_match_object_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_index_match_object_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_index_read_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_index_read_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_index_read_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_index_update_set_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_index_update_set_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_index_update_set_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_index_update_bag_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_index_update_bag_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_index_update_bag_disc_only, "Uses disc"}}.
+%{mnesia_dirty_access_test, dirty_iter_ram} 'IMPL'
+{skip, {mnesia_dirty_access_test, dirty_iter_disc, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, dirty_iter_disc_only, "Uses disc"}}.
+{skip, {mnesia_dirty_access_test, admin_tests, "Uses disk"}}.
+
+%{mnesia_trans_access_test, write} 'IMPL'
+%{mnesia_trans_access_test, read} 'IMPL'
+%{mnesia_trans_access_test, wread} 'IMPL'
+%{mnesia_trans_access_test, delete} 'IMPL'
+%{mnesia_trans_access_test, delete_object} 'IMPL'
+%{mnesia_trans_access_test, match_object} 'IMPL'
+%{mnesia_trans_access_test, all_keys} 'IMPL'
+%{mnesia_trans_access_test, index_match_object} 'IMPL'
+%{mnesia_trans_access_test, index_read} 'IMPL'
+%{mnesia_trans_access_test, index_update_set} 'IMPL'
+%{mnesia_trans_access_test, index_update_bag} 'IMPL'
+{skip, {mnesia_evil_coverage_test, dump_tables, "Uses disk"}}.
+{skip, {mnesia_evil_coverage_test, dump_log, "Uses disk"}}.
+%{mnesia_evil_coverage_test, wait_for_tables} 'IMPL'
+{skip, {mnesia_evil_coverage_test, force_load_table, "Uses disk"}}.
+%{mnesia_evil_coverage_test, user_properties} 'IMPL'
+%{mnesia_evil_coverage_test, record_name_dirty_access_ram} 'IMPL'
+{skip, {mnesia_evil_coverage_test, record_name_dirty_access_disc, "Uses disc"}}.
+{skip, {mnesia_evil_coverage_test, record_name_dirty_access_disc_only, "Uses disc"}}.
+%{mnesia_evil_coverage_test, snmp_open_table} 'IMPL'
+%{mnesia_evil_coverage_test, snmp_close_table} 'IMPL'
+%{mnesia_evil_coverage_test, snmp_get_next_index} 'IMPL'
+%{mnesia_evil_coverage_test, snmp_get_row} 'IMPL'
+%{mnesia_evil_coverage_test, snmp_get_mnesia_key} 'IMPL'
+%{mnesia_evil_coverage_test, snmp_update_counter} 'IMPL'
+%{mnesia_evil_coverage_test, info} 'IMPL'
+%{mnesia_evil_coverage_test, schema_0} 'IMPL'
+%{mnesia_evil_coverage_test, schema_1} 'IMPL'
+%{mnesia_evil_coverage_test, view_0} 'IMPL'
+{skip, {mnesia_evil_coverage_test, view_1, "Uses disk"}}.
+{skip, {mnesia_evil_coverage_test, view_2, "Uses disk"}}.
+%{mnesia_evil_coverage_test, lkill} 'IMPL'
+%{mnesia_evil_coverage_test, kill} 'IMPL'
+
+%{mnesia_config_test, access_module} 'IMPL'
+%{mnesia_config_test, auto_repair} 'IMPL'
+{skip, {mnesia_config_test, backup_module, "Uses disk"}}.
+{skip, {mnesia_config_test, dynamic_connect, "Uses disk"}}.
+%{mnesia_config_test, debug} 'IMPL'
+%{mnesia_config_test, dir} 'IMPL'
+{skip, {mnesia_config_test, dump_log_load_regulation, "Uses disk"}}.
+{skip, {mnesia_config_test, dump_log_time_threshold, "Uses disk"}}.
+{skip, {mnesia_config_test, dump_log_write_threshold, "Uses disk"}}.
+{skip, {mnesia_config_test, dump_log_update_in_place, "Uses disk"}}.
+{skip, {mnesia_config_test, embedded_mnemosyne, "Uses Mnemosyne"}}.
+%{mnesia_config_test, event_module} 'IMPL'
+{skip, {mnesia_config_test, ignore_fallback_at_startup, "Not Yet impl"}}.
+%{mnesia_config_test, inconsistent_database} 'IMPL'
+{skip, {mnesia_config_test, max_wait_for_decision, "Not Yet impl"}}.
+{skip, {mnesia_config_test, start_one_disc_full_then_one_disc_less, "Uses disc"}}.
+{skip, {mnesia_config_test, start_first_one_disc_less_then_one_disc_full, "Uses disc"}}.
+%%{skip, {mnesia_config_test, start_first_one_disc_less_then_two_more_disc_less, "Uses disc"}}.
+{skip, {mnesia_config_test, schema_location_and_extra_db_nodes_combinations, "Uses disk"}}.
+{skip, {mnesia_config_test, table_load_to_disc_less_nodes, "Uses disc"}}.
+{skip, {mnesia_config_test, schema_merge, "Uses Disc"}}.
+%{mnesia_config_test, unknown_config} 'IMPL'
+%{mnesia_registry_test, good_dump} 'IMPL'
+%{mnesia_registry_test, bad_dump} 'IMPL'
+
+%{mnesia_atomicity_test, explicit_abort_in_middle_of_trans} 'IMPL'
+%{mnesia_atomicity_test, runtime_error_in_middle_of_trans} 'IMPL'
+%{mnesia_atomicity_test, kill_self_in_middle_of_trans} 'IMPL'
+%{mnesia_atomicity_test, throw_in_middle_of_trans} 'IMPL'
+%{mnesia_atomicity_test, mnesia_down_during_infinite_trans} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_sw_rt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_sw_wt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wr_r} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_sw_sw} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_sw_w} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_sw_wr} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wr_wt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wr_sw} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wr_w} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_r_sw} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_r_w} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_r_wt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_rt_sw} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_rt_w} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_rt_wt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wt_r} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wt_w} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wt_rt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wt_wt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wt_wr} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_wt_sw} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_w_wr} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_w_sw} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_w_r} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_w_w} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_w_rt} 'IMPL'
+%{mnesia_atomicity_test, lock_waiter_w_wt} 'IMPL'
+%{mnesia_atomicity_test, restart_r_one} 'IMPL'
+%{mnesia_atomicity_test, restart_w_one} 'IMPL'
+%{mnesia_atomicity_test, restart_rt_one} 'IMPL'
+%{mnesia_atomicity_test, restart_wt_one} 'IMPL'
+%{mnesia_atomicity_test, restart_wr_one} 'IMPL'
+%{mnesia_atomicity_test, restart_sw_one} 'IMPL'
+%{mnesia_atomicity_test, restart_r_two} 'IMPL'
+%{mnesia_atomicity_test, restart_w_two} 'IMPL'
+%{mnesia_atomicity_test, restart_rt_two} 'IMPL'
+%{mnesia_atomicity_test, restart_wt_two} 'IMPL'
+%{mnesia_atomicity_test, restart_wr_two} 'IMPL'
+%{mnesia_atomicity_test, restart_sw_two} 'IMPL'
+
+%{mnesia_isolation_test, no_conflict} 'IMPL'
+%{mnesia_isolation_test, simple_queue_conflict} 'IMPL'
+%{mnesia_isolation_test, advanced_queue_conflict} 'IMPL'
+%{mnesia_isolation_test, simple_deadlock_conflict} 'IMPL'
+%{mnesia_isolation_test, advanced_deadlock_conflict} 'IMPL'
+%{mnesia_isolation_test, lock_burst} 'IMPL'
+%{mnesia_isolation_test, basic_sticky_functionality} 'IMPL'
+%{mnesia_isolation_test, create_table} 'IMPL'
+%{mnesia_isolation_test, delete_table} 'IMPL'
+%{mnesia_isolation_test, move_table_copy} 'IMPL'
+%{mnesia_isolation_test, add_table_index} 'IMPL'
+%{mnesia_isolation_test, del_table_index} 'IMPL'
+%{mnesia_isolation_test, transform_table} 'IMPL'
+%{mnesia_isolation_test, snmp_open_table} 'IMPL'
+%{mnesia_isolation_test, snmp_close_table} 'IMPL'
+{skip, {mnesia_isolation_test, change_table_copy_type, "Uses disk"}}.
+%{mnesia_isolation_test, change_table_access} 'IMPL'
+%{mnesia_isolation_test, add_table_copy} 'IMPL'
+%{mnesia_isolation_test, del_table_copy} 'IMPL'
+{skip, {mnesia_isolation_test, dump_tables, "Uses disk"}}.
+{skip, {mnesia_isolation_test, extra_admin_tests, "Uses disk"}}.
+%{mnesia_isolation_test, del_table_copy_1} 'IMPL'
+%{mnesia_isolation_test, del_table_copy_2} 'IMPL'
+%{mnesia_isolation_test, del_table_copy_3} 'IMPL'
+%{mnesia_isolation_test, add_table_copy_1} 'IMPL'
+%{mnesia_isolation_test, add_table_copy_2} 'IMPL'
+%{mnesia_isolation_test, add_table_copy_3} 'IMPL'
+%{mnesia_isolation_test, add_table_copy_4} 'IMPL'
+%{mnesia_isolation_test, move_table_copy_1} 'IMPL'
+%{mnesia_isolation_test, move_table_copy_2} 'IMPL'
+%{mnesia_isolation_test, move_table_copy_3} 'IMPL'
+%{mnesia_isolation_test, move_table_copy_4} 'IMPL'
+%{mnesia_isolation_test, dirty_updates_visible_direct} 'IMPL'
+%{mnesia_isolation_test, dirty_reads_regardless_of_trans} 'IMPL'
+%{mnesia_isolation_test, trans_update_invisibible_outside_trans} 'IMPL'
+%{mnesia_isolation_test, trans_update_visible_inside_trans} 'IMPL'
+%{mnesia_isolation_test, write_shadows} 'IMPL'
+%{mnesia_isolation_test, delete_shadows} 'IMPL'
+%{mnesia_isolation_test, write_delete_shadows_bag} 'IMPL'
+
+{skip, {mnesia_durability_test, all, "Uses disk "}}.
+%{mnesia_durability_test, load_local_contents_directly} 'IMPL'
+%{mnesia_durability_test, load_directly_when_all_are_ram_copiesA} 'IMPL'
+%{mnesia_durability_test, load_directly_when_all_are_ram_copiesB} 'IMPL'
+%{skip, {mnesia_durability_test, late_load_when_all_are_ram_copies_on_ram_nodes1, "Uses disk schema"}}.
+%{skip, {mnesia_durability_test, late_load_when_all_are_ram_copies_on_ram_nodes2, "Uses disk schema"}}.
+%{skip, {mnesia_durability_test, load_when_last_replica_becomes_available, "Uses disk"}}.
+%{skip, {mnesia_durability_test, load_when_we_have_down_from_all_other_replica_nodes, "Uses disk"}}.
+%{skip, {mnesia_durability_test, late_load_transforms_into_disc_load, "Uses disc"}}.
+%{mnesia_durability_test, late_load_leads_to_hanging} 'IMPL'
+%{mnesia_durability_test, force_load_when_nobody_intents_to_load} 'IMPL'
+%{mnesia_durability_test, force_load_when_someone_has_decided_to_load} 'IMPL'
+%{mnesia_durability_test, force_load_when_someone_else_already_has_loaded} 'IMPL'
+%{mnesia_durability_test, force_load_when_we_has_loaded} 'IMPL'
+%{mnesia_durability_test, force_load_on_a_non_local_table} 'IMPL'
+%{mnesia_durability_test, force_load_when_the_table_does_not_exist} 'IMPL'
+%{mnesia_durability_test, master_nodes} 'IMPL'
+%{mnesia_durability_test, master_on_non_local_tables} 'IMPL'
+%{mnesia_durability_test, remote_force_load_with_local_master_node} 'IMPL'
+%{mnesia_durability_test, dump_ram_copies} 'IMPL'
+%{skip, {mnesia_durability_test, dump_disc_copies, "Uses disc"}}.
+%{skip, {mnesia_durability_test, dump_disc_only, "Uses disc"}}.
+%{skip, {mnesia_durability_test, durability_of_disc_copies, "Uses disc"}}.
+%{skip, {mnesia_durability_test, durability_of_disc_only_copies, "Uses disc"}}.
+
+{skip, {mnesia_recovery_test, mnesia_down, "Uses Disk"}}.
+%{mnesia_recovery_test, no_master_2} 'IMPL'
+%{mnesia_recovery_test, no_master_3} 'IMPL'
+%{mnesia_recovery_test, one_master_2} 'IMPL'
+%{mnesia_recovery_test, one_master_3} 'IMPL'
+%{mnesia_recovery_test, two_master_2} 'IMPL'
+%{mnesia_recovery_test, two_master_3} 'IMPL'
+%{mnesia_recovery_test, all_master_2} 'IMPL'
+%{mnesia_recovery_test, all_master_3} 'IMPL'
+{skip, {mnesia_recovery_test, mnesia_down_during_startup_disk_ram, "Uses disk"}}.
+%{mnesia_recovery_test, mnesia_down_during_startup_init_ram} 'IMPL'
+{skip, {mnesia_recovery_test, mnesia_down_during_startup_init_disc, "Uses disc"}}.
+{skip, {mnesia_recovery_test, mnesia_down_during_startup_init_disc_only, "Uses disc"}}.
+%{mnesia_recovery_test, mnesia_down_during_startup_tm_ram} 'IMPL'
+{skip, {mnesia_recovery_test, mnesia_down_during_startup_tm_disc, "Uses disc"}}.
+{skip, {mnesia_recovery_test, mnesia_down_during_startup_tm_disc_only, "Uses disc"}}.
+%{mnesia_recovery_test, explicit_stop_during_snmp} 'IMPL'
+
+{skip, {mnesia_recovery_test, schema_trans, "Uses Disk, needs disk log"}}.
+{skip, {mnesia_recovery_test, async_dirty, "Uses disc"}}.
+{skip, {mnesia_recovery_test, sync_dirty, "Uses disc"}}.
+{skip, {mnesia_recovery_test, sym_trans, "Uses disc"}}.
+{skip, {mnesia_recovery_test, asym_trans, "Uses disc"}}.
+
+{skip, {mnesia_recovery_test, after_full_disc_partition, "Not Yet impl"}}.
+{skip, {mnesia_recovery_test, after_corrupt_files, "Uses disk"}}.
+
+%{mnesia_evil_coverage_test, subscriptions} 'IMPL'
+%{mnesia_evil_coverage_test, nested_trans_both_ok} 'IMPL'
+%{mnesia_evil_coverage_test, nested_trans_child_dies} 'IMPL'
+%{mnesia_evil_coverage_test, nested_trans_parent_dies} 'IMPL'
+%{mnesia_evil_coverage_test, nested_trans_both_dies} 'IMPL'
+%{mnesia_evil_coverage_test, mix_of_trans_sync_dirty} 'IMPL'
+%{mnesia_evil_coverage_test, mix_of_trans_async_dirty} 'IMPL'
+%{mnesia_evil_coverage_test, mix_of_trans_ets} 'IMPL'
+
+{skip, {mnesia_recovery_test, disc_less, "Uses disc (on the other nodes)"}}.
+{skip, {mnesia_recovery_test, system_upgrade, "Not Yet impl"}}.
+%{mnesia_consistency_test, consistency_after_restart_1_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_restart_1_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_restart_1_disc_only, "Uses disc"}}.
+%{mnesia_consistency_test, consistency_after_restart_2_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_restart_2_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_restart_2_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_dump_tables_1_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, consistency_after_dump_tables_2_ram, "Uses disk"}}.
+%{mnesia_consistency_test, consistency_after_add_replica_2_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_add_replica_2_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_add_replica_2_disc_only, "Uses disc"}}.
+%{mnesia_consistency_test, consistency_after_add_replica_3_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_add_replica_3_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_add_replica_3_disc_only, "Uses disc"}}.
+%{mnesia_consistency_test, consistency_after_del_replica_2_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_del_replica_2_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_del_replica_2_disc_only, "Uses disc"}}.
+%{mnesia_consistency_test, consistency_after_del_replica_3_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_del_replica_3_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_del_replica_3_disc_only, "Uses disc"}}.
+%{mnesia_consistency_test, consistency_after_move_replica_2_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_move_replica_2_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_move_replica_2_disc_only, "Uses disc"}}.
+%{mnesia_consistency_test, consistency_after_move_replica_3_ram} 'IMPL'
+{skip, {mnesia_consistency_test, consistency_after_move_replica_3_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_move_replica_3_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_transform_table, "Not yet implemented"}}.
+{skip, {mnesia_consistency_test, consistency_after_change_table_copy_type, "Not yet implemented"}}.
+{skip, {mnesia_consistency_test, consistency_after_fallback_2_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, consistency_after_fallback_2_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_fallback_2_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_fallback_3_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, consistency_after_fallback_3_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_fallback_3_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_restore_clear_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, consistency_after_restore_clear_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_restore_clear_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_restore_recreate_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, consistency_after_restore_recreate_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_restore_recreate_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, consistency_after_rename_of_node, "Not yet implemented"}}.
+{skip, {mnesia_consistency_test, updates_during_checkpoint_activation, "Uses disk"}}.
+%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_2_disc, "Uses disc"}}.
+%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_2_disc_only, "Uses disc"}}.
+%%{mnesia_consistency_test, updates_during_checkpoint_activation_3_ram} 'IMPL'
+%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_3_disc, "Uses disc"}}.
+%{skip, {mnesia_consistency_test, updates_during_checkpoint_activation_3_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, updates_during_checkpoint_iteration, "Uses disk"}}.
+%{skip, {mnesia_consistency_test, updates_during_checkpoint_iteration_2_disc, "Uses disc"}}.
+%{skip, {mnesia_consistency_test, updates_during_checkpoint_iteration_2_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, load_table_with_activated_checkpoint_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, load_table_with_activated_checkpoint_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, load_table_with_activated_checkpoint_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, add_table_copy_to_table_with_activated_checkpoint_ram, "Uses disk"}}.
+{skip, {mnesia_consistency_test, add_table_copy_to_table_with_activated_checkpoint_disc, "Uses disc"}}.
+{skip, {mnesia_consistency_test, add_table_copy_to_table_with_activated_checkpoint_disc_only, "Uses disc"}}.
+{skip, {mnesia_consistency_test, inst_fallback_process_dies, "Uses disk"}}.
+{skip, {mnesia_consistency_test, fatal_when_inconsistency, "Uses disk"}}.
+{skip, {mnesia_consistency_test, after_delete, "Uses disk"}}.
+{skip, {mnesia_consistency_test, mnesia_down_during_backup_causes_switch, "Uses disk"}}.
+{skip, {mnesia_consistency_test, mnesia_down_during_backup_causes_abort, "Uses disk"}}.
+%{mnesia_consistency_test, cause_switch_after} 'IMPL'
+%{mnesia_consistency_test, cause_abort_before} 'IMPL'
+%{mnesia_consistency_test, cause_abort_after} 'IMPL'
+%{mnesia_consistency_test, change_schema_before} 'IMPL'
+%{mnesia_consistency_test, change_schema_after} 'IMPL'
+
diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl
new file mode 100644
index 0000000000..8ba8427213
--- /dev/null
+++ b/lib/mnesia/test/mnesia_SUITE.erl
@@ -0,0 +1,162 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_SUITE).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}].
+
+
+%% Verify that Mnesia really is a distributed real-time DBMS.
+%% This is the test suite of the Mnesia DBMS. The test suite
+%% covers many aspects of usage and is indended to be developed
+%% incrementally. The test suite is divided into a hierarchy of test
+%% suites where the leafs actually implements the test cases.
+%% The intention of each test case and sub test suite can be
+%% read in comments where they are implemented or in worst cases
+%% from their long mnemonic names.
+%%
+%% The most simple test case of them all is called 'silly'
+%% and is useful to run now and then, e.g. when some new fatal
+%% bug has been introduced. It may be run even if Mnesia is in
+%% such a bad shape that the test machinery cannot be used.
+%% NB! Invoke the function directly with mnesia_SUITE:silly()
+%% and do not involve the normal test machinery.
+
+all() ->
+ [{group, light}, {group, medium}, {group, heavy},
+ clean_up_suite].
+
+groups() ->
+ %% The 'light' test suite runs a selected set of test suites and is
+ %% intended to be the smallest test suite that is meaningful
+ %% to run. It starts with an installation test (which in essence is the
+ %% 'silly' test case) and then it covers all functions in the API in
+ %% various depths. All configuration parameters and examples are also
+ %% covered.
+ [{light, [],
+ [{group, install}, {group, nice}, {group, evil},
+ {group, mnesia_frag_test, light}, {group, qlc},
+ {group, registry}, {group, config}, {group, examples}]},
+ {install, [], [{mnesia_install_test, all}]},
+ {nice, [], [{mnesia_nice_coverage_test, all}]},
+ {evil, [], [{mnesia_evil_coverage_test, all}]},
+ {qlc, [], [{mnesia_qlc_test, all}]},
+ {registry, [], [{mnesia_registry_test, all}]},
+ {config, [], [{mnesia_config_test, all}]},
+ {examples, [], [{mnesia_examples_test, all}]},
+ %% The 'medium' test suite verfies the ACID (atomicity, consistency
+ %% isolation and durability) properties and various recovery scenarios
+ %% These tests may take quite while to run.
+ {medium, [],
+ [{group, install}, {group, atomicity},
+ {group, isolation}, {group, durability},
+ {group, recovery}, {group, consistency},
+ {group, mnesia_frag_test, medium}]},
+ {atomicity, [], [{mnesia_atomicity_test, all}]},
+ {isolation, [], [{mnesia_isolation_test, all}]},
+ {durability, [], [{mnesia_durability_test, all}]},
+ {recovery, [], [{mnesia_recovery_test, all}]},
+ {consistency, [], [{mnesia_consistency_test, all}]},
+ %% The 'heavy' test suite runs some resource consuming tests and
+ %% benchmarks
+ {heavy, [], [{group, measure}]},
+ {measure, [], [{mnesia_measure_test, all}]},
+ {prediction, [],
+ [{group, mnesia_measure_test, prediction}]},
+ {fairness, [],
+ [{group, mnesia_measure_test, fairness}]},
+ {benchmarks, [],
+ [{group, mnesia_measure_test, benchmarks}]},
+ {consumption, [],
+ [{group, mnesia_measure_test, consumption}]},
+ {scalability, [],
+ [{group, mnesia_measure_test, scalability}]},
+ %% This test suite is an extract of the grand Mnesia suite
+ %% it contains OTP R4B specific test cases
+ {otp_r4b, [],
+ [{mnesia_config_test, access_module},
+ {mnesia_config_test, dump_log_load_regulation},
+ {mnesia_config_test, embedded_mnemosyne},
+ {mnesia_config_test, ignore_fallback_at_startup},
+ {mnesia_config_test, max_wait_for_decision},
+ {mnesia_consistency_test, consistency_after_restore},
+ {mnesia_evil_backup, restore},
+ {mnesia_evil_coverage_test, offline_set_master_nodes},
+ {mnesia_evil_coverage_test, record_name},
+ {mnesia_evil_coverage_test, user_properties},
+ {mnesia_registry_test, all}, {group, otp_2363}]},
+ %% Index on disc only tables
+ {otp_2363, [],
+ [{mnesia_dirty_access_test,
+ dirty_index_match_object_disc_only},
+ {mnesia_dirty_access_test, dirty_index_read_disc_only},
+ {mnesia_dirty_access_test,
+ dirty_index_update_bag_disc_only},
+ {mnesia_dirty_access_test,
+ dirty_index_update_set_disc_only},
+ {mnesia_evil_coverage_test,
+ create_live_table_index_disc_only}]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+silly() ->
+ mnesia_install_test:silly().
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where"
+ "started during the tests"];
+clean_up_suite(suite) ->
+ [];
+clean_up_suite(Config) when is_list(Config)->
+ mnesia:kill(),
+ Slaves = mnesia_test_lib:lookup_config(nodenames, Config),
+ Nodes = lists:delete(node(), Slaves),
+ rpc:multicall(Nodes, erlang, halt, []),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+
+
diff --git a/lib/mnesia/test/mnesia_atomicity_test.erl b/lib/mnesia/test/mnesia_atomicity_test.erl
new file mode 100644
index 0000000000..cf878fc820
--- /dev/null
+++ b/lib/mnesia/test/mnesia_atomicity_test.erl
@@ -0,0 +1,782 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_atomicity_test).
+-author('[email protected]').
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [explicit_abort_in_middle_of_trans,
+ runtime_error_in_middle_of_trans,
+ kill_self_in_middle_of_trans, throw_in_middle_of_trans,
+ {group, mnesia_down_in_middle_of_trans}].
+
+groups() ->
+ [{mnesia_down_in_middle_of_trans, [],
+ [mnesia_down_during_infinite_trans,
+ {group, lock_waiter}, {group, restart_check}]},
+ {lock_waiter, [],
+ [lock_waiter_sw_r, lock_waiter_sw_rt, lock_waiter_sw_wt,
+ lock_waiter_wr_r, lock_waiter_srw_r, lock_waiter_sw_sw,
+ lock_waiter_sw_w, lock_waiter_sw_wr, lock_waiter_sw_srw,
+ lock_waiter_wr_wt, lock_waiter_srw_wt,
+ lock_waiter_wr_sw, lock_waiter_srw_sw, lock_waiter_wr_w,
+ lock_waiter_srw_w, lock_waiter_r_sw, lock_waiter_r_w,
+ lock_waiter_r_wt, lock_waiter_rt_sw, lock_waiter_rt_w,
+ lock_waiter_rt_wt, lock_waiter_wr_wr,
+ lock_waiter_srw_srw, lock_waiter_wt_r, lock_waiter_wt_w,
+ lock_waiter_wt_rt, lock_waiter_wt_wt, lock_waiter_wt_wr,
+ lock_waiter_wt_srw, lock_waiter_wt_sw, lock_waiter_w_wr,
+ lock_waiter_w_srw, lock_waiter_w_sw, lock_waiter_w_r,
+ lock_waiter_w_w, lock_waiter_w_rt, lock_waiter_w_wt]},
+ {restart_check, [],
+ [restart_r_one, restart_w_one, restart_rt_one,
+ restart_wt_one, restart_wr_one, restart_sw_one,
+ restart_r_two, restart_w_two, restart_rt_two,
+ restart_wt_two, restart_wr_two, restart_sw_two]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+explicit_abort_in_middle_of_trans(suite) -> [];
+explicit_abort_in_middle_of_trans(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = explicit_abort_in_middle_of_trans,
+
+ Rec1A = {Tab, 1, a},
+ Rec1B = {Tab, 1, b},
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]}])),
+ %% Start a transaction on one node
+ {success, [A]} = ?start_activities([Node1]),
+
+ %% store an object in the Tab - first tranaction
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1A) % returns ok when successful
+ end,
+ ?match_receive({A, ok}),
+ A ! end_trans,
+ ?match_receive({A, {atomic, end_trans}}),
+
+ %% second transaction: store some new objects and abort before the
+ %% transaction is finished -> the new changes should be invisable
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1B),
+ exit(abort_by_purpose) %does that stop the process A ???
+ end,
+ ?match_receive({A, {aborted, abort_by_purpose}}),
+
+
+ %?match_receive({A, {'EXIT', Pid, normal}}), % A died and sends EXIT
+
+
+ %% Start a second transactionprocess, after the first failed
+ {success, [B]} = ?start_activities([Node1]),
+
+ %% check, whether the interupted transaction had no influence on the db
+ ?start_transactions([B]),
+ B ! fun() ->
+ ?match([Rec1A], mnesia:read({Tab, 1})),
+ ok
+ end,
+ ?match_receive({B, ok}),
+ B ! end_trans,
+ ?match_receive({B, {atomic, end_trans}}),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+runtime_error_in_middle_of_trans(suite) -> [];
+runtime_error_in_middle_of_trans(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = runtime_error_in_middle_of_trans,
+
+ Rec1A = {Tab, 1, a},
+ Rec1B = {Tab, 1, b},
+ Rec1C = {Tab, 1, c},
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]}])),
+ %% Start a transaction on one node
+ {success, [A]} = ?start_activities([Node1]),
+
+ %% store an object in the Tab - first tranaction
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1A) % returns ok when successful
+ end,
+ ?match_receive({A, ok}),
+ A ! end_trans,
+ ?match_receive({A, {atomic, end_trans}}),
+
+ %% second transaction: store some new objects and abort before the
+ %% transaction is finished -> the new changes should be invisable
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1B),
+ erlang:error(foo), % that should provoke a runtime error
+ mnesia:write(Rec1C)
+ end,
+ ?match_receive({A, {aborted, _Reason}}),
+
+ %?match_receive({A, {'EXIT', Msg1}), % A died and sends EXIT
+
+
+ %% Start a second transactionprocess, after the first failed
+ {success, [B]} = ?start_activities([Node1]),
+
+ %% check, whether the interupted transaction had no influence on the db
+ ?start_transactions([B]),
+ B ! fun() ->
+ ?match([Rec1A], mnesia:read({Tab, 1})),
+ ok
+ end,
+ ?match_receive({B, ok}),
+ B ! end_trans,
+ ?match_receive({B, {atomic, end_trans}}),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+kill_self_in_middle_of_trans(suite) -> [];
+kill_self_in_middle_of_trans(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = kill_self_in_middle_of_trans,
+
+ Rec1A = {Tab, 1, a},
+ Rec1B = {Tab, 1, b},
+ Rec1C = {Tab, 1, c},
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]}])),
+ %% Start a transaction on one node
+ {success, [A]} = ?start_activities([Node1]),
+
+ %% store an object in the Tab - first tranaction
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1A) % returns ok when successful
+ end,
+ ?match_receive({A, ok}),
+ A ! end_trans,
+ ?match_receive({A, {atomic, end_trans}}),
+
+ %% second transaction: store some new objects and abort before the
+ %% transaction is finished -> the new changes should be invisable
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1B),
+ exit(self(), kill), % that should kill the process himself
+ % - poor guy !
+ mnesia:write(Rec1C)
+ end,
+ %%
+ %% exit(.., kill) : the transaction can't trap this error - thus no
+ %% proper result can be send by the test server
+
+ % ?match_receive({A, {aborted, Reason}}),
+
+ ?match_receive({'EXIT', _Pid, killed}), % A is killed and sends EXIT
+
+ %% Start a second transactionprocess, after the first failed
+ {success, [B]} = ?start_activities([Node1]),
+
+ %% check, whether the interupted transaction had no influence on the db
+ ?start_transactions([B]),
+ B ! fun() ->
+ ?match([Rec1A], mnesia:read({Tab, 1})),
+ ok
+ end,
+ ?match_receive({B, ok}),
+ B ! end_trans,
+ ?match_receive({B, {atomic, end_trans}}),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+throw_in_middle_of_trans(suite) -> [];
+throw_in_middle_of_trans(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = throw_in_middle_of_trans,
+
+ Rec1A = {Tab, 1, a},
+ Rec1B = {Tab, 1, b},
+ Rec1C = {Tab, 1, c},
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]}])),
+ %% Start a transaction on one node
+ {success, [A]} = ?start_activities([Node1]),
+
+ %% store an object in the Tab - first tranaction
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1A) % returns ok when successful
+ end,
+ ?match_receive({A, ok}),
+ A ! end_trans,
+ ?match_receive({A, {atomic, end_trans}}),
+
+ %% second transaction: store some new objects and abort before the
+ %% transaction is finished -> the new changes should be invisable
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write(Rec1B),
+ throw(exit_transactian_by_a_throw),
+ mnesia:write(Rec1C)
+ end,
+ ?match_receive({A, {aborted, {throw, exit_transactian_by_a_throw}}}),
+ % A ! end_trans, % is A still alive ?
+ % ?match_receive({A, {atomic, end_trans}}), % {'EXIT', Pid, normal}
+
+ %?match_receive({A, {'EXIT', Pid, normal}}), % A died and sends EXIT
+
+ %% Start a second transactionprocess, after the first failed
+ {success, [B]} = ?start_activities([Node1]),
+
+ %% check, whether the interupted transaction had no influence on the db
+ ?start_transactions([B]),
+ B ! fun() ->
+ ?match([Rec1A], mnesia:read({Tab, 1})),
+ ok
+ end,
+ ?match_receive({B, ok}),
+ B ! end_trans,
+ ?match_receive({B, {atomic, end_trans}}),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+mnesia_down_during_infinite_trans(suite) -> [];
+mnesia_down_during_infinite_trans(Config) when is_list(Config) ->
+ [Node1, Node2] = ?acquire_nodes(2, Config),
+ Tab = mnesia_down_during_infinite_trans,
+
+ ?match({atomic, ok},
+ mnesia:create_table([{name, Tab}, {ram_copies, [Node1, Node2]}])),
+ %% Start a transaction on one node
+ {success, [A2, A1]} = ?start_activities([Node2, Node1]),
+ %% Start order of the transactions are important
+ %% We also needs to sync the tid counter
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 1, test_ok}) end)),
+ mnesia_test_lib:start_sync_transactions([A2, A1]),
+
+ %% Obtain a write lock and wait forever
+ RecA = {Tab, 1, test_not_ok},
+ A1 ! fun() -> mnesia:write(RecA) end,
+ ?match_receive({A1, ok}),
+
+ A1 ! fun() -> process_flag(trap_exit, true), timer:sleep(infinity) end,
+ ?match_receive(timeout),
+
+ %% Try to get read lock, but gets queued
+ A2 ! fun() -> mnesia:read({Tab, 1}) end,
+ ?match_receive(timeout),
+
+ %% Kill Mnesia on other node
+ mnesia_test_lib:kill_mnesia([Node1]),
+
+ %% Second transaction gets the read lock
+ ?match_receive({A2, [{Tab, 1, test_ok}]}),
+ exit(A1, kill), % Needed since we trap exit
+
+ ?verify_mnesia([Node2], [Node1]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+lock_waiter_sw_r(suite) -> [];
+lock_waiter_sw_r(Config) when is_list(Config) ->
+ start_lock_waiter(sw, r, Config).
+
+lock_waiter_sw_rt(suite) -> [];
+lock_waiter_sw_rt(Config) when is_list(Config) ->
+ start_lock_waiter(sw, rt, Config).
+
+lock_waiter_sw_wt(suite) -> [];
+lock_waiter_sw_wt(Config) when is_list(Config) ->
+ start_lock_waiter(sw, wt,Config).
+
+lock_waiter_wr_r(suite) -> [];
+lock_waiter_wr_r(Config) when is_list(Config) ->
+ start_lock_waiter(wr, r, Config).
+
+lock_waiter_srw_r(suite) -> [];
+lock_waiter_srw_r(Config) when is_list(Config) ->
+ start_lock_waiter(srw, r, Config).
+
+lock_waiter_sw_sw(suite) -> [];
+lock_waiter_sw_sw(Config) when is_list(Config) ->
+ start_lock_waiter(sw, sw,Config).
+
+lock_waiter_srw_srw(suite) -> [];
+lock_waiter_srw_srw(Config) when is_list(Config) ->
+ start_lock_waiter(srw, srw,Config).
+
+lock_waiter_wr_wr(suite) -> [];
+lock_waiter_wr_wr(Config) when is_list(Config) ->
+ start_lock_waiter(wr, wr,Config).
+
+lock_waiter_sw_w(suite) -> [];
+lock_waiter_sw_w(Config) when is_list(Config) ->
+ start_lock_waiter(sw, w,Config).
+
+lock_waiter_sw_wr(suite) -> [];
+lock_waiter_sw_wr(Config) when is_list(Config) ->
+ start_lock_waiter(sw, wr,Config).
+
+lock_waiter_sw_srw(suite) -> [];
+lock_waiter_sw_srw(Config) when is_list(Config) ->
+ start_lock_waiter(sw, srw,Config).
+
+lock_waiter_wr_wt(suite) -> [];
+lock_waiter_wr_wt(Config) when is_list(Config) ->
+ start_lock_waiter(wr, wt,Config).
+
+lock_waiter_srw_wt(suite) -> [];
+lock_waiter_srw_wt(Config) when is_list(Config) ->
+ start_lock_waiter(srw, wt,Config).
+
+lock_waiter_wr_sw(suite) -> [];
+lock_waiter_wr_sw(Config) when is_list(Config) ->
+ start_lock_waiter(wr, sw,Config).
+
+lock_waiter_srw_sw(suite) -> [];
+lock_waiter_srw_sw(Config) when is_list(Config) ->
+ start_lock_waiter(srw, sw,Config).
+
+lock_waiter_wr_w(suite) -> [];
+lock_waiter_wr_w(Config) when is_list(Config) ->
+ start_lock_waiter(wr, w,Config).
+
+lock_waiter_srw_w(suite) -> [];
+lock_waiter_srw_w(Config) when is_list(Config) ->
+ start_lock_waiter(srw, w,Config).
+
+lock_waiter_r_sw(suite) -> [];
+lock_waiter_r_sw(Config) when is_list(Config) ->
+ start_lock_waiter(r, sw,Config).
+
+lock_waiter_r_w(suite) -> [];
+lock_waiter_r_w(Config) when is_list(Config) ->
+ start_lock_waiter(r, w,Config).
+
+lock_waiter_r_wt(suite) -> [];
+lock_waiter_r_wt(Config) when is_list(Config) ->
+ start_lock_waiter(r, wt,Config).
+
+lock_waiter_rt_sw(suite) -> [];
+lock_waiter_rt_sw(Config) when is_list(Config) ->
+ start_lock_waiter(rt, sw,Config).
+
+lock_waiter_rt_w(suite) -> [];
+lock_waiter_rt_w(Config) when is_list(Config) ->
+ start_lock_waiter(rt, w,Config).
+
+lock_waiter_rt_wt(suite) -> [];
+lock_waiter_rt_wt(Config) when is_list(Config) ->
+ start_lock_waiter(rt, wt,Config).
+
+lock_waiter_wt_r(suite) -> [];
+lock_waiter_wt_r(Config) when is_list(Config) ->
+ start_lock_waiter(wt, r,Config).
+
+lock_waiter_wt_w(suite) -> [];
+lock_waiter_wt_w(Config) when is_list(Config) ->
+ start_lock_waiter(wt, w,Config).
+
+lock_waiter_wt_rt(suite) -> [];
+lock_waiter_wt_rt(Config) when is_list(Config) ->
+ start_lock_waiter(wt, rt,Config).
+
+lock_waiter_wt_wt(suite) -> [];
+lock_waiter_wt_wt(Config) when is_list(Config) ->
+ start_lock_waiter(wt, wt,Config).
+
+lock_waiter_wt_wr(suite) -> [];
+lock_waiter_wt_wr(Config) when is_list(Config) ->
+ start_lock_waiter(wt, wr,Config).
+
+lock_waiter_wt_srw(suite) -> [];
+lock_waiter_wt_srw(Config) when is_list(Config) ->
+ start_lock_waiter(wt, srw,Config).
+
+lock_waiter_wt_sw(suite) -> [];
+lock_waiter_wt_sw(Config) when is_list(Config) ->
+ start_lock_waiter(wt, sw,Config).
+
+lock_waiter_w_wr(suite) -> [];
+lock_waiter_w_wr(Config) when is_list(Config) ->
+ start_lock_waiter(w, wr, Config).
+
+lock_waiter_w_srw(suite) -> [];
+lock_waiter_w_srw(Config) when is_list(Config) ->
+ start_lock_waiter(w, srw, Config).
+
+lock_waiter_w_sw(suite) -> [];
+lock_waiter_w_sw(Config) when is_list(Config) ->
+ start_lock_waiter(w, sw, Config).
+
+lock_waiter_w_r(suite) -> [];
+lock_waiter_w_r(Config) when is_list(Config) ->
+ start_lock_waiter(w, r, Config).
+
+lock_waiter_w_w(suite) -> [];
+lock_waiter_w_w(Config) when is_list(Config) ->
+ start_lock_waiter(w, w, Config).
+
+lock_waiter_w_rt(suite) -> [];
+lock_waiter_w_rt(Config) when is_list(Config) ->
+ start_lock_waiter(w, rt, Config).
+
+lock_waiter_w_wt(suite) -> [];
+lock_waiter_w_wt(Config) when is_list(Config) ->
+ start_lock_waiter(w, wt, Config).
+
+start_lock_waiter(BlockOpA, BlockOpB, Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+
+ TabName = mk_tab_name(lock_waiter_),
+ ?match({atomic, ok}, mnesia:create_table(TabName,
+ [{ram_copies, [N1, N2]}])),
+
+ %% initialize the table with object {1, c} - when there
+ %% is a read transaction, the read will find that value
+ ?match({atomic, ok}, mnesia:sync_transaction(fun() -> mnesia:write({TabName, 1, c}) end)),
+ rpc:call(N2, ?MODULE, sync_tid_release, []),
+
+ Tester = self(),
+ Fun_A =fun() ->
+ NewCounter = incr_restart_counter(),
+ if
+ NewCounter == 1 ->
+ Tester ! go_ahead_test,
+ receive go_ahead -> ok end;
+ true -> ok
+ end,
+ lock_waiter_fun(BlockOpA, TabName, a),
+ NewCounter
+ end,
+
+ %% it's not possible to just spawn the transaction, because
+ %% the result shall be evaluated
+ A = spawn_link(N1, ?MODULE, perform_restarted_transaction, [Fun_A]),
+
+ ?match(ok, receive go_ahead_test -> ok after 10000 -> timeout end),
+
+ mnesia_test_lib:sync_trans_tid_serial([N1, N2]),
+
+ Fun_B = fun() ->
+ lock_waiter_fun(BlockOpB, TabName, b),
+ A ! go_ahead,
+ wait(infinity)
+ end,
+
+ B = spawn_link(N2, mnesia, transaction, [Fun_B, 100]),
+
+ io:format("waiting for A (~p on ~p) to be in the queue ~n", [A, [N1, N2]]),
+ wait_for_a(A, [N1, N2]),
+
+ io:format("Queus ~p~n",
+ [[{N,rpc:call(N, mnesia, system_info, [lock_queue])} || N <- Nodes]]),
+
+ KillNode = node(B),
+ io:format("A was in the queue, time to kill Mnesia on B's node (~p on ~p)~n",
+ [B, KillNode]),
+
+ mnesia_test_lib:kill_mnesia([KillNode]), % kill mnesia of fun B
+
+ %% Read Ops does not need to be restarted
+ ExpectedCounter =
+ if
+ BlockOpA == sw, BlockOpB == w -> 1;
+ BlockOpA == sw, BlockOpB == wt -> 1;
+ BlockOpA == sw, BlockOpB == wr -> 1;
+ BlockOpA == srw, BlockOpB == w -> 1;
+ BlockOpA == srw, BlockOpB == wt -> 1;
+ BlockOpA == srw, BlockOpB == wr -> 1;
+ BlockOpA == r, BlockOpB /= sw -> 1;
+ BlockOpA == rt, BlockOpB /= sw -> 1;
+ true -> 2
+ end,
+ ?match_multi_receive([{'EXIT', A, {atomic, ExpectedCounter}},
+ {'EXIT', B, killed}]),
+
+ %% the expected result depends on the transaction of
+ %% fun A - when that doesn't change the object in the
+ %% table (e.g. it is a read) then the predefined
+ %% value {Tabname, 1, c} is expected to be the result here
+ ExpectedResult =
+ case BlockOpA of
+ w -> {TabName, 1, a};
+ sw ->{TabName, 1, a};
+ _all_other -> {TabName, 1, c}
+ end,
+
+ ?match({atomic, [ExpectedResult]},
+ mnesia:transaction(fun() -> mnesia:read({TabName, 1}) end, 100)),
+ ?verify_mnesia([N1], [N2]).
+
+mk_tab_name(Prefix) ->
+ {Mega, Sec, Micro} = erlang:now(),
+ list_to_atom(lists:concat([Prefix , Mega, '_', Sec, '_', Micro])).
+
+lock_waiter_fun(Op, TabName, Val) ->
+ case Op of
+ rt -> mnesia:read_lock_table(TabName);
+ wt -> mnesia:write_lock_table(TabName);
+ r -> mnesia:read({TabName, 1});
+ w -> mnesia:write({TabName, 1, Val});
+ wr -> mnesia:wread({TabName, 1});
+ srw -> mnesia:read(TabName, 1, sticky_write);
+ sw -> mnesia:s_write({TabName, 1, Val})
+ end.
+
+wait_for_a(Pid, Nodes) ->
+ wait_for_a(Pid, Nodes, 5).
+
+wait_for_a(_P, _N, 0) ->
+ ?error("Timeout while waiting for lock on a~n", []);
+
+wait_for_a(Pid, Nodes, Count) ->
+ %% io:format("WAIT_FOR_A ~p ON ~w ~n", [Pid, Nodes]),
+ List = [rpc:call(N, mnesia, system_info, [lock_queue]) || N <- Nodes],
+ Q = lists:append(List),
+ check_q(Pid, Q, Nodes, Count).
+
+check_q(Pid, [{{_Oid,_Tid}, _Op, Pid, _WFT} | _Tail], _N, _Count) ->
+ ok;
+check_q(Pid, [{_Oid, _Op, Pid, _Tid, _WFT} | _Tail], _N, _Count) ->
+ ok;
+check_q(Pid, [_ | Tail], N, Count) ->
+ check_q(Pid, Tail, N, Count);
+check_q(Pid, [], N, Count) ->
+ timer:sleep(500),
+ wait_for_a(Pid, N, Count - 1).
+
+perform_restarted_transaction (Fun_Trans) ->
+ %% the result of the transaction shall be:
+ %% - undefined (if the transaction was never executed)
+ %% - Times ( number of times that the transaction has been executed)
+
+ Result = mnesia:transaction(Fun_Trans, 100),
+ exit(Result).
+
+%% Returns new val
+incr_restart_counter() ->
+ NewCount =
+ case get(count_restart_of_transaction) of
+ undefined -> 1;
+ OldCount -> OldCount + 1
+ end,
+ put(count_restart_of_transaction, NewCount),
+ NewCount.
+
+wait(Mseconds) ->
+ receive
+ after Mseconds -> ok
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+restart_r_one(suite) -> [];
+restart_r_one(Config) when is_list(Config) ->
+ start_restart_check(r, one, Config).
+
+restart_w_one(suite) -> [];
+restart_w_one(Config) when is_list(Config) ->
+ start_restart_check(w, one, Config).
+
+restart_rt_one(suite) -> [];
+restart_rt_one(Config) when is_list(Config) ->
+ start_restart_check(rt, one, Config).
+
+restart_wt_one(suite) -> [];
+restart_wt_one(Config) when is_list(Config) ->
+ start_restart_check(wt, one, Config).
+
+restart_wr_one(suite) -> [];
+restart_wr_one(Config) when is_list(Config) ->
+ start_restart_check(wr, one, Config).
+
+restart_sw_one(suite) -> [];
+restart_sw_one(Config) when is_list(Config) ->
+ start_restart_check(sw, one, Config).
+
+restart_r_two(suite) -> [];
+restart_r_two(Config) when is_list(Config) ->
+ start_restart_check(r, two, Config).
+
+restart_w_two(suite) -> [];
+restart_w_two(Config) when is_list(Config) ->
+ start_restart_check(w, two, Config).
+
+restart_rt_two(suite) -> [];
+restart_rt_two(Config) when is_list(Config) ->
+ start_restart_check(rt, two, Config).
+
+restart_wt_two(suite) -> [];
+restart_wt_two(Config) when is_list(Config) ->
+ start_restart_check(wt, two, Config).
+
+restart_wr_two(suite) -> [];
+restart_wr_two(Config) when is_list(Config) ->
+ start_restart_check(wr, two, Config).
+
+restart_sw_two(suite) -> [];
+restart_sw_two(Config) when is_list(Config) ->
+ start_restart_check(sw, two, Config).
+
+start_restart_check(RestartOp, ReplicaNeed, Config) ->
+ [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config),
+
+ {TabName, _TabNodes} = create_restart_table(ReplicaNeed, Nodes),
+
+ %% initialize the table with object {1, c} - when there
+ %% is a read transaction, the read will find that value
+ ?match({atomic, ok}, mnesia:sync_transaction(fun() -> mnesia:write({TabName, 1, c}) end)),
+
+ %% Really sync tid_release
+ rpc:multicall([N2,N3], ?MODULE, sync_tid_release, []),
+ Coord = self(),
+
+ Fun_A = fun() ->
+ NewCounter = incr_restart_counter(),
+ case NewCounter of
+ 1 ->
+ mnesia:write({TabName, 1, d}),
+ %% send a message to the test proc
+ Coord ! {self(),fun_a_is_blocked},
+ receive go_ahead -> ok end;
+ _ ->
+ %% the fun will NOT be blocked here
+ restart_fun_A(RestartOp, TabName)
+ end,
+ NewCounter
+ end,
+
+ A = spawn_link(N1, ?MODULE, perform_restarted_transaction, [Fun_A]),
+ ?match_receive({A,fun_a_is_blocked}),
+
+ %% mnesia shall be killed at that node, where A is reading
+ %% the information from
+ kill_where_to_read(TabName, N1, [N2, N3]),
+
+ %% wait some time to let mnesia go down and spread those news around
+ %% fun A shall be able to finish its job before being restarted
+ wait(500),
+ A ! go_ahead,
+
+ %% the sticky write doesnt work on remote nodes !!!
+ ExpectedMsg =
+ case RestartOp of
+ sw when ReplicaNeed == two ->
+ {'EXIT',A,{aborted, {not_local, TabName}}};
+ _all_other ->
+ case ReplicaNeed of
+ one ->
+ {'EXIT',A,{aborted, {no_exists, TabName}}};
+ two ->
+ {'EXIT',A,{atomic, 2}}
+ end
+ end,
+
+ ?match_receive(ExpectedMsg),
+
+ %% now mnesia has to be started again on the node KillNode
+ %% because the next test suite will need it
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [TabName])),
+
+
+ %% the expected result depends on the transaction of
+ %% fun A - when that doesnt change the object in the
+ %% table (e.g. it is a read) then the predefined
+ %% value {Tabname, 1, c} is expected to be the result here
+
+ ExpectedResult =
+ case ReplicaNeed of
+ one ->
+ [];
+ two ->
+ case RestartOp of
+ w -> [{TabName, 1, a}];
+ _ ->[ {TabName, 1, c}]
+ end
+ end,
+
+ ?match({atomic, ExpectedResult},
+ mnesia:transaction(fun() -> mnesia:read({TabName, 1}) end,100)),
+ ?verify_mnesia(Nodes, []).
+
+create_restart_table(ReplicaNeed, [_N1, N2, N3]) ->
+ TabNodes =
+ case ReplicaNeed of
+ one -> [N2];
+ two -> [N2, N3]
+ end,
+ TabName = mk_tab_name(restart_check_),
+ ?match({atomic, ok}, mnesia:create_table(TabName, [{ram_copies, TabNodes}])),
+ {TabName, TabNodes}.
+
+restart_fun_A(Op, TabName) ->
+ case Op of
+ rt -> mnesia:read_lock_table(TabName);
+ wt -> mnesia:write_lock_table(TabName);
+ r -> mnesia:read( {TabName, 1});
+ w -> mnesia:write({TabName, 1, a});
+ wr -> mnesia:wread({TabName, 1});
+ sw -> mnesia:s_write({TabName, 1, a})
+ end.
+
+kill_where_to_read(TabName, N1, Nodes) ->
+ Read = rpc:call(N1,mnesia,table_info, [TabName, where_to_read]),
+ case lists:member(Read, Nodes) of
+ true ->
+ mnesia_test_lib:kill_mnesia([Read]);
+ false ->
+ ?error("Fault while killing Mnesia: ~p~n", [Read]),
+ mnesia_test_lib:kill_mnesia(Nodes)
+ end.
+
+sync_tid_release() ->
+ sys:get_status(whereis(mnesia_tm)),
+ sys:get_status(whereis(mnesia_locker)),
+ ok.
+
diff --git a/lib/mnesia/test/mnesia_config_backup.erl b/lib/mnesia/test/mnesia_config_backup.erl
new file mode 100644
index 0000000000..0916e255e2
--- /dev/null
+++ b/lib/mnesia/test/mnesia_config_backup.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_config_backup).
+-author('[email protected]').
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% This module is used for testing the backup module config parameter.
+%%
+%% This module is an impostor for the mnesia_backup module.
+%%
+%%
+%% Original doc below:
+%%
+%% This module contains one implementation of callback functions
+%% used by Mnesia at backup and restore. The user may however
+%% write an own module the same interface as mnesia_backup and
+%% configure Mnesia so the alternate module performs the actual
+%% accesses to the backup media. This means that the user may put
+%% the backup on medias that Mnesia does not know about, possibly
+%% on hosts where Erlang is not running.
+%%
+%% The OpaqueData argument is never interpreted by other parts of
+%% Mnesia. It is the property of this module. Alternate implementations
+%% of this module may have different interpretations of OpaqueData.
+%% The OpaqueData argument given to open_write/1 and open_read/1
+%% are forwarded directly from the user.
+%%
+%% All functions must return {ok, NewOpaqueData} or {error, Reason}.
+%%
+%% The NewOpaqueData arguments returned by backup callback functions will
+%% be given as input when the next backup callback function is invoked.
+%% If any return value does not match {ok, _} the backup will be aborted.
+%%
+%% The NewOpaqueData arguments returned by restore callback functions will
+%% be given as input when the next restore callback function is invoked
+%% If any return value does not match {ok, _} the restore will be aborted.
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-export([
+ open_write/1, write/2, commit_write/1, abort_write/1,
+ open_read/1, read/1, close_read/1
+ ]).
+
+-record(backup, {name, mode, items}).
+
+open_write(Name) ->
+ file:delete(Name),
+ {ok, #backup{name = Name, mode = write, items = []}}.
+
+write(Opaque, Item) when Opaque#backup.mode == write ->
+ %% Build the list in reverse order
+ {ok, Opaque#backup{items = [Item | Opaque#backup.items]}}.
+
+commit_write(Opaque) when Opaque#backup.mode == write ->
+ Bin = term_to_binary(Opaque#backup.items),
+ case file:write_file(Opaque#backup.name, Bin) of
+ ok ->
+ {ok, Opaque#backup{mode = closed, items = []}};
+ {error, Reason} ->
+ {error, {commit_write, Reason}}
+ end.
+
+abort_write(Opaque) ->
+ {ok, Opaque#backup{mode = closed, items = []}}.
+
+open_read(Name) ->
+ case file:read_file(Name) of
+ {ok, Bin} ->
+ ReverseList = binary_to_term(Bin),
+ List = lists:reverse(ReverseList),
+ {ok, #backup{name = Name, mode = read, items = List}};
+ {error, Reason} ->
+ {error, {open_read, Reason}}
+ end.
+
+read(Opaque) when Opaque#backup.mode == read ->
+ case Opaque#backup.items of
+ [Head | Tail] ->
+ {ok, Opaque#backup{items = Tail}, Head};
+ [] ->
+ {ok, Opaque#backup{mode = closed}, []}
+ end.
+
+close_read(Opaque) ->
+ {ok, Opaque#backup{mode = closed, items = []}}.
diff --git a/lib/mnesia/test/mnesia_config_event.erl b/lib/mnesia/test/mnesia_config_event.erl
new file mode 100644
index 0000000000..832bf94eb9
--- /dev/null
+++ b/lib/mnesia/test/mnesia_config_event.erl
@@ -0,0 +1,74 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_config_event).
+-author('[email protected]').
+
+-behaviour(gen_event).
+
+%%
+%% This module was stolen from Mnesia
+%%
+
+
+%% gen_event callback interface
+-export([init/1, handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+
+init(_Args) ->
+ {ok, []}.
+
+handle_event(Msg, State) ->
+ handle_any_event(Msg, State).
+
+handle_info(Msg, State) ->
+ handle_any_event(Msg, State).
+
+
+handle_call(Msg, State) ->
+ handle_any_event(Msg, State).
+
+
+%% The main...
+
+handle_any_event({get_log, Pid}, State) ->
+ Pid ! {log, State},
+ {ok, State};
+handle_any_event(Msg, State) ->
+ io:format("Got event: ~p~n", [Msg]),
+ {ok, [Msg | State]}.
+
+%%-----------------------------------------------------------------
+%% terminate(Reason, State) ->
+%% AnyVal
+%%-----------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+code_change(_OldVsn, _State, _Extra) ->
+ exit(not_supported).
+
diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl
new file mode 100644
index 0000000000..93510d539c
--- /dev/null
+++ b/lib/mnesia/test/mnesia_config_test.erl
@@ -0,0 +1,1420 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_config_test).
+-author('[email protected]').
+
+-include("mnesia_test_lib.hrl").
+
+-record(test_table,{i,a1,a2,a3}).
+-record(test_table2,{i, b}).
+
+-export([
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+ access_module/1,
+ auto_repair/1,
+ backup_module/1,
+ debug/1,
+ dir/1,
+ dump_log_load_regulation/1,
+
+ dump_log_update_in_place/1,
+ embedded_mnemosyne/1,
+ event_module/1,
+ ignore_fallback_at_startup/1,
+ inconsistent_database/1,
+ max_wait_for_decision/1,
+ send_compressed/1,
+
+ app_test/1,
+
+ schema_merge/1,
+ unknown_config/1,
+
+ dump_log_time_threshold/1,
+ dump_log_write_threshold/1,
+
+ start_one_disc_full_then_one_disc_less/1,
+ start_first_one_disc_less_then_one_disc_full/1,
+ start_first_one_disc_less_then_two_more_disc_less/1,
+ schema_location_and_extra_db_nodes_combinations/1,
+ table_load_to_disc_less_nodes/1,
+
+ dynamic_basic/1,
+ dynamic_ext/1,
+ dynamic_bad/1,
+
+ init_per_testcase/2,
+ end_per_testcase/2,
+ c_nodes/0
+ ]).
+
+-export([check_logs/1]).
+
+-define(init(N, Config),
+ mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema,
+ {reload_appls, [mnesia]}],
+ N, Config, ?FILE, ?LINE)).
+-define(acquire(N, Config),
+ mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema,
+ {reload_appls, [mnesia]},
+ create_schema,
+ {start_appls, [mnesia]}],
+ N, Config, ?FILE, ?LINE)).
+-define(acquire_schema(N, Config),
+ mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema,
+ {reload_appls, [mnesia]},
+ create_schema],
+ N, Config, ?FILE, ?LINE)).
+-define(cleanup(N, Config),
+ mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}],
+ N, Config, ?FILE, ?LINE)).
+-define(trans(Fun),
+ ?match({atomic, ok}, mnesia:transaction(Fun))).
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+all() ->
+ [access_module, auto_repair, backup_module, debug, dir,
+ dump_log_load_regulation, {group, dump_log_thresholds},
+ dump_log_update_in_place, embedded_mnemosyne,
+ event_module, ignore_fallback_at_startup,
+ inconsistent_database, max_wait_for_decision,
+ send_compressed, app_test, {group, schema_config},
+ unknown_config].
+
+groups() ->
+ [{dump_log_thresholds, [],
+ [dump_log_time_threshold, dump_log_write_threshold]},
+ {schema_config, [],
+ [start_one_disc_full_then_one_disc_less,
+ start_first_one_disc_less_then_one_disc_full,
+ start_first_one_disc_less_then_two_more_disc_less,
+ schema_location_and_extra_db_nodes_combinations,
+ table_load_to_disc_less_nodes, schema_merge,
+ {group, dynamic_connect}]},
+ {dynamic_connect, [],
+ [dynamic_basic, dynamic_ext, dynamic_bad]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+access_module(doc) ->
+ ["Replace the activity access module with another module and ",
+ "use it to read and write to some alternate table storage"];
+access_module(suite) -> [];
+access_module(Config) when is_list(Config) ->
+ Nodes = ?acquire_schema(1, Config),
+ ?match(ok, mnesia:start([{access_module, mnesia_frag}])),
+
+ ?match(mnesia_frag, mnesia:system_info(access_module)),
+
+ access_tab(ram_copies, Nodes),
+ case mnesia_test_lib:diskless(Config) of
+ true -> skip;
+ false ->
+ access_tab(disc_copies, Nodes)
+ , access_tab(disc_only_copies, Nodes)
+ end,
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config).
+
+access_tab(Storage, Nodes) ->
+ Tab = list_to_atom(lists:concat([access_tab_, Storage])),
+ RecName = some_access,
+ Attr = val,
+ TabDef = [{Storage, Nodes},
+ {type, bag},
+ {index, [Attr]},
+ {record_name, RecName}],
+ ?match({atomic,ok}, mnesia:create_table(Tab, TabDef)),
+
+ Activity = fun(Kind) ->
+ A = [Kind, Tab, RecName, Attr, Nodes],
+ io:format("kind: ~w, storage: ~w~n", [Kind, Storage]),
+ mnesia:activity(Kind, fun do_access/5, A)
+ end,
+ ModActivity = fun(Kind, M) ->
+ io:format("kind: ~w, storage: ~w. module: ~w~n",
+ [Kind, Storage, M]),
+ A = [Kind, Tab, RecName, Attr, Nodes],
+ mnesia:activity(Kind, fun do_access/5, A, M)
+ end,
+ ?match(ok, Activity(transaction)),
+ ?match(ok, Activity({transaction, 47})),
+ ?match(ok, ModActivity(transaction, mnesia)),
+ ?match(ok, ModActivity(transaction, mnesia_frag)),
+
+ ?match(ok, Activity(async_dirty)),
+ ?match(ok, Activity(sync_dirty)),
+ case Storage of
+ ram_copies ->
+ ?match(ok, Activity(ets));
+ _ ->
+ ignore
+ end.
+
+do_access(Kind, Tab, RecName, Attr, Nodes) ->
+ Tens = lists:sort([{RecName, 1, 10}, {RecName, 3, 10}]),
+ {OptNodes, OptTens} =
+ case Kind of
+ transaction -> {Nodes, Tens};
+ {transaction, _} -> {Nodes, Tens};
+ async_dirty -> {[], Tens};
+ sync_dirty -> {[], Tens};
+ ets -> {[], []}
+ end,
+ ?match(RecName, mnesia:table_info(Tab, record_name)),
+
+ ?match(ok, mnesia:write(Tab, {RecName, 1, 10}, write)),
+ ?match(ok, mnesia:write(Tab, {RecName, 2, 20}, sticky_write)),
+ ?match(ok, mnesia:write(Tab, {RecName, 2, 21}, sticky_write)),
+ ?match(ok, mnesia:write(Tab, {RecName, 2, 22}, write)),
+ ?match(ok, mnesia:write(Tab, {RecName, 3, 10}, write)),
+
+ Twos = [{RecName, 2, 20}, {RecName, 2, 21}, {RecName, 2, 22}],
+ ?match(Twos, lists:sort(mnesia:read(Tab, 2, read))),
+
+ ?match(ok, mnesia:delete_object(Tab, {RecName, 2, 21}, sticky_write)),
+
+ TenPat = {RecName, '_', 10},
+ ?match(Tens, lists:sort(mnesia:match_object(Tab, TenPat, read))),
+ ?match(OptTens, lists:sort(mnesia:index_match_object(Tab, TenPat, Attr, read) )),
+ ?match(OptTens, lists:sort(mnesia:index_read(Tab, 10, Attr))),
+ Keys = [1, 2, 3],
+ ?match(Keys, lists:sort(mnesia:all_keys(Tab))),
+
+ First = mnesia:first(Tab),
+ Mid = mnesia:next(Tab, First),
+ Last = mnesia:next(Tab, Mid),
+ ?match('$end_of_table', mnesia:next(Tab, Last)),
+ ?match(Keys, lists:sort([First,Mid,Last])),
+
+ %% For set and bag these last, prev works as first and next
+ First2 = mnesia:last(Tab),
+ Mid2 = mnesia:prev(Tab, First2),
+ Last2 = mnesia:prev(Tab, Mid2),
+ ?match('$end_of_table', mnesia:prev(Tab, Last2)),
+ ?match(Keys, lists:sort([First2,Mid2,Last2])),
+
+ ?match([ok, ok, ok], [mnesia:delete(Tab, K, write) || K <- Keys]),
+ W = wild_pattern,
+ ?match([], mnesia:match_object(Tab, mnesia:table_info(Tab, W), read)),
+ ?log("Safe fixed ~p~n", [catch ets:info(Tab, safe_fixed)]),
+ ?log("Fixed ~p ~n", [catch ets:info(Tab, fixed)]),
+
+ ?match(OptNodes, mnesia:lock({global, some_lock_item, Nodes}, write)),
+ ?match(OptNodes, mnesia:lock({global, some_lock_item, Nodes}, read)),
+ ?match(OptNodes, mnesia:lock({table, Tab}, read)),
+ ?match(OptNodes, mnesia:lock({table, Tab}, write)),
+
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+auto_repair(doc) ->
+ ["Try the auto_repair mechanism on the various disk_logs and dets files.",
+ "",
+ "The case tests both normal values of the parameter, and also",
+ "one crazy value.",
+ "The test of the real auto_repair functionality is made in the",
+ "dets suite"
+ ];
+auto_repair(suite) -> [];
+auto_repair(Config) when is_list(Config) ->
+ ?init(1, Config),
+ ?match(ok, mnesia:start()), % Check default true
+ ?match(true, mnesia:system_info(auto_repair)),
+ ?match(stopped, mnesia:stop()),
+ ?match(ok, mnesia:start([{auto_repair, true}])),
+ ?match(true, mnesia:system_info(auto_repair)),
+ ?match(stopped, mnesia:stop()),
+ ?match(ok, mnesia:start([{auto_repair, false}])),
+ ?match(false, mnesia:system_info(auto_repair)),
+ ?match(stopped, mnesia:stop()),
+ ?match({error, {bad_type, auto_repair, your_mama}},
+ mnesia:start([{auto_repair, your_mama}])),
+ ?match(stopped, mnesia:stop()),
+ ?cleanup(1, Config),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+backup_module(doc) ->
+ ["Replace the backup module with another module and use it to",
+ "read and write to an alternate backup media, e.g stored in",
+ "the internal state of a simple process."];
+backup_module(suite) -> [];
+backup_module(Config) when is_list(Config) ->
+ Nodes = ?acquire_schema(1, Config),
+ ?match(ok, mnesia:start([{backup_module, mnesia_config_backup}])),
+ ?match({atomic,ok},
+ mnesia:create_table(test_table,
+ [{disc_copies, Nodes},
+ {attributes,
+ record_info(fields,test_table)}])),
+
+ ?match({atomic,ok},
+ mnesia:create_table(test_table2,
+ [{disc_copies, Nodes},
+ {attributes,
+ record_info(fields,test_table2)}])),
+ %% Write in test table
+ ?trans(fun() -> mnesia:write(#test_table{i=1}) end),
+ ?trans(fun() -> mnesia:write(#test_table{i=2}) end),
+
+ %% Write in test table 2
+ ?trans(fun() -> mnesia:write(#test_table2{i=3}) end),
+ ?trans(fun() -> mnesia:write(#test_table2{i=4}) end),
+ mnesia_test_lib:sync_tables(Nodes, [test_table, test_table2]),
+
+ File = whow,
+ %% Now make a backup
+ ?match(ok, mnesia:backup(File)),
+
+ ?match(ok, mnesia:install_fallback(File)),
+
+ %% Now add things
+ ?trans(fun() -> mnesia:write(#test_table{i=2.5}) end),
+ ?trans(fun() -> mnesia:write(#test_table2{i=3.5}) end),
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [test_table, test_table2])),
+
+ %% Now check newly started tables
+ ?match({atomic, [1,2]},
+ mnesia:transaction(fun() -> lists:sort(mnesia:all_keys(test_table)) end)),
+ ?match({atomic, [3,4]},
+ mnesia:transaction(fun() -> lists:sort(mnesia:all_keys(test_table2)) end)),
+
+ file:delete(File),
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+debug(doc) ->
+ ["Try out the four debug levels and ensure that the",
+ "expected events are generated."];
+debug(suite) -> [];
+debug(Config) when is_list(Config) ->
+ Nodes = ?init(1, Config),
+ case application:get_env(mnesia,debug) of
+ undefined ->
+ ?match(none, mnesia:system_info(debug));
+ {ok, false} ->
+ ?match(none, mnesia:system_info(debug));
+ {ok, true} ->
+ ?match(debug, mnesia:system_info(debug));
+ {ok, Env} ->
+ ?match(Env, mnesia:system_info(debug))
+ end,
+
+ ?match(ok, mnesia:start([{debug, verbose}])),
+ ?match(verbose, mnesia:system_info(debug)),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+
+ ?match(ok, mnesia:start([{debug, debug}])),
+ ?match(debug, mnesia:system_info(debug)),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+
+ ?match(ok, mnesia:start([{debug, trace}])),
+ ?match(trace, mnesia:system_info(debug)),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+
+ ?match(ok, mnesia:start([{debug, true}])),
+ ?match(debug, mnesia:system_info(debug)),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+
+ ?match(ok, mnesia:start([{debug, false}])),
+ ?match(none, mnesia:system_info(debug)),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dir(doc) ->
+ ["Try to use alternate Mnesia directories"];
+dir(suite) -> [];
+dir(Config) when is_list(Config) ->
+ Nodes = ?init(1, Config),
+
+ ?match(ok, mnesia:start([{dir, tuff}])),
+ Dir = filename:join([element(2, file:get_cwd()), "tuff"]),
+ ?match(Dir, mnesia:system_info(directory)),
+ mnesia_test_lib:kill_mnesia(Nodes),
+
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dump_log_update_in_place(doc) ->
+ ["Change the update in place policy for the transaction log dumper."];
+dump_log_update_in_place(suite) -> [];
+dump_log_update_in_place(Config) when is_list(Config) ->
+ Nodes = ?acquire(1, Config),
+ ?match(true, mnesia:system_info(dump_log_update_in_place)),
+ ?match({atomic,ok},
+ mnesia:create_table(test_table,
+ [{disc_copies, Nodes},
+ {attributes,
+ record_info(fields,test_table)}])),
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+
+ ?match(ok, mnesia:start([{dump_log_update_in_place, false}])),
+ ?match(false, mnesia:system_info(dump_log_update_in_place)),
+
+ mnesia_test_lib:sync_tables(Nodes, [schema, test_table]),
+
+ %% Now provoke some log dumps
+
+ L = lists:map(
+ fun(Num) ->
+ %% Write something on one end ...
+ mnesia:transaction(
+ fun() ->
+ mnesia:write(#test_table{i=Num}) end
+ ) end,
+ lists:seq(1, 110)),
+
+ L2 = lists:duplicate(110, {atomic, ok}),
+
+ %% If this fails then some of the 110 writes above failed
+ ?match(true, L==L2),
+ if L==L2 -> ok;
+ true ->
+ ?verbose("***** List1 len: ~p, List2 len: ~p~n",
+ [length(L), length(L2)]),
+ ?verbose("L: ~p~nL2:~p~n", [L, L2])
+ end,
+
+ %% If we still can write, then Mnesia is probably alive
+ ?trans(fun() -> mnesia:write(#test_table{i=115}) end),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dump_log_write_threshold(doc)->
+ ["This test case must be rewritten.",
+ "Dump logs are tested by doing transactions, then killing Mnesia and ",
+ "then examining the table data files and see if they are correct.",
+ "The test_table is used as a counter, test_table. is stepped once ",
+ "for each transaction."];
+dump_log_write_threshold(suite)->[];
+dump_log_write_threshold(Config) when is_list(Config) ->
+ [N1] = ?acquire_schema(1, Config),
+
+ Threshold = 3,
+ ?match(ok,mnesia:start([{dump_log_write_threshold, Threshold}])),
+
+ ?match({atomic,ok},
+ mnesia:create_table(test_table,
+ [{disc_copies, [N1]},
+ {attributes,
+ record_info(fields,test_table)}])),
+ ?match(dumped, mnesia:dump_log()),
+
+ ?match(ok, do_trans(2)), % Shall not have dumped
+ check_logs(0),
+
+ ?match(ok, do_trans(Threshold - 2)), % Trigger a dump
+ receive after 1000 -> ok end,
+ check_logs(Threshold),
+
+
+ ?match(ok, do_trans(Threshold - 1)),
+ ?match(dumped, mnesia:dump_log()), %% This should trigger ets2dcd dump
+ check_logs(0), %% and leave no dcl file
+
+ ?match(stopped, mnesia:stop()),
+
+ %% Check bad threshold value
+ ?match({error,{bad_type,dump_log_write_threshold,0}},
+ mnesia:start([{dump_log_write_threshold,0}])),
+
+ ?verify_mnesia([], [N1]),
+ ?cleanup(1, Config),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dump_log_time_threshold(doc)->
+ ["See doc on above."];
+dump_log_time_threshold(suite)->[];
+dump_log_time_threshold(Config) when is_list(Config) ->
+ Nodes = ?acquire_schema(1, Config),
+ Time = 4000,
+
+ %% Check bad threshold value
+ ?match({error,{bad_type,dump_log_time_threshold,0}},
+ mnesia:start([{dump_log_time_threshold,0}])),
+
+
+ ?match(ok,mnesia:start([{dump_log_write_threshold,100},
+ {dump_log_time_threshold, Time}])),
+
+ ?match({atomic,ok},mnesia:create_table(test_table,
+ [{disc_copies, Nodes},
+ {attributes,
+ record_info(fields,
+ test_table)}])),
+
+ %% Check that nothing is dumped when within time threshold
+ ?match(ok, do_trans(1)),
+ check_logs(0),
+
+ ?match(Time, mnesia:system_info(dump_log_time_threshold)),
+
+ %% Check that things get dumped when time threshold exceeded
+ ?match(ok, do_trans(5)),
+ receive after Time+2000 -> ok end,
+ check_logs(6),
+
+ ?verify_mnesia([node()], []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%
+%%
+%% Help functions for dump log
+
+%% Do a transaction N times
+do_trans(0) -> ok;
+do_trans(N) ->
+ Fun = fun() ->
+ XX=incr(),
+ mnesia:write(#test_table{i=XX})
+ end,
+ {atomic, ok} = mnesia:transaction(Fun),
+ do_trans(N-1).
+
+%% An increasing number
+incr() ->
+ case get(bloody_counter) of
+ undefined -> put(bloody_counter, 2), 1;
+ Num -> put(bloody_counter, Num+1)
+ end.
+
+%%
+%% Check that the correct number of transactions have been recorded.
+%%-record(test_table,{i,a1,a2,a3}).
+check_logs(N) ->
+ File = mnesia_lib:tab2dcl(test_table),
+ Args = [{file, File}, {name, testing}, {repair, true}, {mode, read_only}],
+
+ if N == 0 ->
+ ?match(false, mnesia_lib:exists(File));
+ true ->
+ ?match(true, mnesia_lib:exists(File)),
+ ?match({ok, _Log}, disk_log:open(Args)),
+
+ {Cont, Terms} = disk_log:chunk(testing, start),
+ ?match(eof, disk_log:chunk(testing, Cont)),
+ %%?verbose("N: ~p, L: ~p~n", [N, L]),
+ disk_log:close(testing),
+
+ %% Correct number of records in file
+ ?match({N, N}, {N, length(Terms) -1 }) %% Ignore Header
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dump_log_load_regulation(doc) ->
+ ["Test the load regulation of the dumper"];
+dump_log_load_regulation(suite) ->
+ [];
+dump_log_load_regulation(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ Param = dump_log_load_regulation,
+
+ %% Normal
+ NoReg = false,
+ ?match(NoReg, mnesia:system_info(Param)),
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+
+ %% Bad
+ Bad = arne_anka,
+ ?match({error, {bad_type, Param, Bad}},
+ mnesia:start([{Param, Bad}])),
+
+ %% Regulation activated
+ Reg = true,
+ ?match(ok,mnesia:start([{Param, Reg}])),
+ ?match(Reg, mnesia:system_info(Param)),
+
+ Args =
+ [{db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 5},
+ {n_branches, length(Nodes) * 10},
+ {n_accounts_per_branch, 5},
+ {replica_type, disc_copies},
+ {stop_after, timer:seconds(30)},
+ {report_interval, timer:seconds(10)},
+ {use_running_mnesia, true},
+ {reuse_history_id, true}],
+
+ ?match({ok, _}, mnesia_tpcb:start(Args)),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+embedded_mnemosyne(doc) ->
+ ["Start Mnemosyne as an embedded part of Mnesia",
+ "on some of the nodes"];
+embedded_mnemosyne(suite) ->
+ [];
+embedded_mnemosyne(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ Param = embedded_mnemosyne,
+
+ %% Normal
+ NoMnem = false,
+ ?match(NoMnem, mnesia:system_info(Param)),
+ ?match(undefined, whereis(mnemosyne_catalog)),
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+
+ %% Bad
+ Bad = arne_anka,
+ ?match({error, {bad_type, Param, Bad}},
+ mnesia:start([{Param, Bad}])),
+
+ case code:priv_dir(mnemosyne) of
+ {error, _} -> %% No mnemosyne on later systems
+ ok;
+ _ ->
+ %% Mnemosyne as embedded application
+ Mnem = true,
+ ?match(undefined, whereis(mnemosyne_catalog)),
+ ?match(ok,mnesia:start([{Param, Mnem}])),
+ ?match(Mnem, mnesia:system_info(Param)),
+ ?match(Pid when is_pid(Pid), whereis(mnemosyne_catalog)),
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+ ?match(undefined, whereis(mnemosyne_catalog))
+ end,
+ ?verify_mnesia([], Nodes),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ignore_fallback_at_startup(doc) ->
+ ["Start Mnesia without rollback of the database to the fallback. ",
+ "Once Mnesia has been (re)started the installed fallback should",
+ "be handled as a normal active fallback.",
+ "Install a customized event module which disables the termination",
+ "of Mnesia when mnesia_down occurrs with an active fallback."].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+max_wait_for_decision(doc) ->
+ ["Provoke Mnesia to make a forced decision of the outome",
+ "of a heavy weight transaction."].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+send_compressed(doc) -> [];
+send_compressed(suite) -> [];
+send_compressed(Config) ->
+ [N1,N2] = Nodes = ?acquire_nodes(2, Config),
+ ?match({atomic,ok}, mnesia:create_table(t0, [{ram_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t1, [{disc_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t2, [{disc_only_copies,[N1,N2]}])),
+
+ Max = 1000,
+ Create = fun(Tab) -> [mnesia:write({Tab, N, {N, "FILLER-123490878345asdasd"}})
+ || N <- lists:seq(1, Max)],
+ ok
+ end,
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ ?match(ok, mnesia:start([{send_compressed, 9}])),
+ ?match(ok, mnesia:wait_for_tables([t0,t1,t2], 5000)),
+
+ ?match({atomic, ok}, mnesia:transaction(Create, [t0])),
+ ?match({atomic, ok}, mnesia:transaction(Create, [t1])),
+ ?match({atomic, ok}, mnesia:transaction(Create, [t2])),
+
+ ?match([], mnesia_test_lib:start_mnesia([N2], [t0,t1,t2])),
+
+ Verify = fun(Tab) ->
+ [ [{Tab,N,{N,_}}] = mnesia:read(Tab, N) || N <- lists:seq(1, Max)],
+ ok
+ end,
+ ?match({atomic, ok}, rpc:call(N1, mnesia, transaction, [Verify, [t0]])),
+ ?match({atomic, ok}, rpc:call(N1, mnesia, transaction, [Verify, [t1]])),
+ ?match({atomic, ok}, rpc:call(N1, mnesia, transaction, [Verify, [t2]])),
+
+ ?match({atomic, ok}, rpc:call(N2, mnesia, transaction, [Verify, [t0]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, transaction, [Verify, [t1]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, transaction, [Verify, [t2]])),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+app_test(doc) -> [];
+app_test(suite) -> [];
+app_test(_Config) ->
+ ?match(ok,test_server:app_test(mnesia)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+event_module(doc) ->
+ ["Replace the event module with another module and use it as",
+ "receiver of the various system and table events. Provoke",
+ "coverage of all kinds of events."];
+event_module(suite) -> [];
+event_module(Config) when is_list(Config) ->
+ Filter = fun({mnesia_system_event,{mnesia_info, _, _}}) -> false;
+ (_) -> true
+ end,
+
+ [_N1, N2]=Nodes=?acquire_schema(2, Config),
+
+ Def = case mnesia_test_lib:diskless(Config) of
+ true -> [{event_module, mnesia_config_event},
+ {extra_db_nodes, Nodes}];
+ false ->
+ [{event_module, mnesia_config_event}]
+ end,
+
+ ?match({[ok, ok], []}, rpc:multicall(Nodes, mnesia, start, [Def])),
+ receive after 1000 -> ok end,
+ mnesia_event ! {get_log, self()},
+ DebugLog1 = receive
+ {log, L1} -> L1
+ after 10000 -> [timeout]
+ end,
+ ?match([{mnesia_system_event,{mnesia_up,N2}}],
+ lists:filter(Filter, DebugLog1)),
+ mnesia_test_lib:kill_mnesia([N2]),
+ receive after 2000 -> ok end,
+
+ ?match({[ok], []}, rpc:multicall([N2], mnesia, start, [])),
+
+ receive after 1000 -> ok end,
+ mnesia_event ! {get_log, self()},
+ DebugLog = receive
+ {log, L} -> L
+ after 10000 -> [timeout]
+ end,
+ ?match([{mnesia_system_event,{mnesia_up,N2}},
+ {mnesia_system_event,{mnesia_down,N2}},
+ {mnesia_system_event,{mnesia_up, N2}}],
+ lists:filter(Filter, DebugLog)),
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+start_one_disc_full_then_one_disc_less(doc)->
+ ["Start a disk node and then a disk less one. Distribute some",
+ "tables between them."];
+start_one_disc_full_then_one_disc_less(suite) -> [];
+start_one_disc_full_then_one_disc_less(Config) when is_list(Config) ->
+ [N1, N2] = ?init(2, Config),
+ ?match(ok, mnesia:create_schema([N1])),
+ ?match([], mnesia_test_lib:start_mnesia([N1])),
+
+ ?match({atomic, ok}, mnesia:add_table_copy(schema, N2, ram_copies)),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema_location, ram},
+ {extra_db_nodes, [N1]}]])),
+ mnesia_test_lib:sync_tables([N1, N2], [schema]),
+
+ %% Now create some tables
+ ?match({atomic,ok},
+ mnesia:create_table(test_table,
+ [{ram_copies, [N1, N2]},
+ {attributes,
+ record_info(fields,test_table)}])),
+
+ ?match({atomic,ok},
+ rpc:call(
+ N2, mnesia,create_table, [test_table2,
+ [{ram_copies, [N1, N2]},
+ {attributes,
+ record_info(fields,test_table2)}]])),
+
+ %% Write something on one end ...
+ Rec = #test_table{i=55},
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec) end)),
+
+ %% ... and read it in the other
+ ?match({atomic, [Rec]},
+ rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:read({test_table, 55}) end])),
+
+
+ %% Then do the same but start at the other end
+ Rec2 = #test_table2{i=155},
+ ?match({atomic, ok},
+ rpc:call(N2, mnesia, transaction,
+ [fun() ->
+ mnesia:write(Rec2) end
+ ])),
+
+ ?match({atomic, [Rec2]},
+ mnesia:transaction(fun() -> mnesia:read({test_table2, 155}) end)),
+
+ ?verify_mnesia([N1, N2], []),
+ ?cleanup(2, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+start_first_one_disc_less_then_one_disc_full(doc)->
+ ["no_doc"];
+start_first_one_disc_less_then_one_disc_full(suite) -> [];
+start_first_one_disc_less_then_one_disc_full(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?init(2, Config),
+ ?match(ok, mnesia:create_schema([N1])),
+ ?match([], mnesia_test_lib:start_mnesia([N1])),
+
+ ?match({atomic, ok}, mnesia:add_table_copy(schema, N2, ram_copies)),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema_location, ram},
+ {extra_db_nodes, Nodes}]])),
+
+ mnesia_test_lib:sync_tables([N1, N2], [schema]),
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ receive after 2000 -> ok end,
+ ?match([], mnesia_test_lib:start_mnesia(Nodes)),
+
+ mnesia_test_lib:sync_tables([N1, N2], [schema]),
+
+ %% Now create some tables
+ ?match({atomic,ok},
+ rpc:call(
+ N1, mnesia,create_table, [test_table,
+ [%%{disc_copies, [node()]},
+ {ram_copies, [N1, N2]},
+ {attributes,
+ record_info(fields,test_table)}]])),
+ mnesia_test_lib:sync_tables([N1, N2], [test_table]),
+
+ ?match({atomic,ok},
+ rpc:call(
+ N2, mnesia,create_table, [test_table2,
+ [%%{disc_copies, [node()]},
+ {ram_copies, [N1, N2]},
+ {attributes,
+ record_info(fields,test_table2)}]])),
+
+ mnesia_test_lib:sync_tables([N1, N2], [test_table, test_table2]),
+
+ %% Assure tables loaded
+ ?match({[ok, ok], []},
+ rpc:multicall([N1, N2], mnesia, wait_for_tables,
+ [[schema, test_table, test_table2], 10000])),
+
+ %% Write something on one end ...
+ Rec = #test_table{i=55},
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:write(Rec) end])),
+
+ %% ... and read it in the other
+ ?match({atomic, [Rec]},
+ rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:read({test_table, 55}) end])),
+
+ %% Then do the same but start at the other end
+ Rec2 = #test_table2{i=155},
+ ?match({atomic, ok},
+ rpc:call(N2, mnesia, transaction,
+ [fun() ->
+ mnesia:write(Rec2) end
+ ])),
+
+ ?match({atomic, [Rec2]},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:read({test_table2, 155}) end])),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+start_first_one_disc_less_then_two_more_disc_less(doc)->
+ ["no doc"];
+start_first_one_disc_less_then_two_more_disc_less(suite) -> [];
+start_first_one_disc_less_then_two_more_disc_less(Config) when is_list(Config) ->
+ Nodes = [N1, N2, N3] = ?init(3, Config),
+
+ ?match(ok, rpc:call(N1, mnesia, start, [[{schema_location, ram}]])),
+
+ %% Really should use test_lib:mnesia_start for these ones but ...
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia,add_table_copy, [schema, N2, ram_copies])),
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia,add_table_copy, [schema, N3, ram_copies])),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema_location, ram},
+ {extra_db_nodes, [N1]}]])),
+ ?match(ok, rpc:call(N3, mnesia, start, [[{schema_location, ram},
+ {extra_db_nodes, [N1, N2]}]])),
+
+ %% Now create some tables
+ ?match({atomic,ok},
+ rpc:call(
+ N1, mnesia,create_table, [test_table,
+ [%%{disc_copies, [node()]},
+ {ram_copies, [N1, N2, N3]},
+ {attributes,
+ record_info(fields,test_table)}]])),
+
+ %% Assure tables loaded
+ ?match({[ok, ok, ok], []},
+ rpc:multicall([N1, N2, N3], mnesia, wait_for_tables,
+ [[test_table], 1000])),
+
+ %% Write something on one end ...
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:write(#test_table{i=44}) end])),
+
+ %% Force synchronicity
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:write_lock_table(test_table) end])),
+
+ %% ... and read it in the others
+ ?match({[{atomic, [{test_table, 44, _, _, _}]},
+ {atomic, [{test_table, 44, _, _, _}]}], []},
+ rpc:multicall([N2, N3], mnesia, transaction,
+ [fun() -> mnesia:read({test_table, 44}) end])),
+
+ %% Then do the other way around
+ ?match({atomic, ok},
+ rpc:call(N3, mnesia, transaction,
+ [fun() -> mnesia:write(#test_table{i=33}) end])),
+ %% Force synchronicity
+ ?match({atomic, ok},
+ rpc:call(N3, mnesia, transaction,
+ [fun() -> mnesia:write_lock_table(test_table) end])),
+
+ ?match({[{atomic, [{test_table, 44, _, _, _}]},
+ {atomic, [{test_table, 44, _, _, _}]}], []},
+ rpc:multicall([N1, N2], mnesia, transaction,
+ [fun() -> mnesia:read({test_table, 44}) end])),
+
+ mnesia_test_lib:reload_appls([mnesia], Nodes),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+schema_location_and_extra_db_nodes_combinations(doc)->
+ ["Test schema loaction and extra_db_nodes combinations."];
+schema_location_and_extra_db_nodes_combinations(suite) -> [];
+schema_location_and_extra_db_nodes_combinations(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?init(2, Config),
+ ?match(ok, mnesia:create_schema([N1])),
+ ?match([], mnesia_test_lib:start_mnesia([N1])),
+
+ %% Really should use test_lib:mnesia_start for these ones but ...
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia,add_table_copy, [schema, N2, ram_copies])),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema_location, ram},
+ {extra_db_nodes, [N1]}]])),
+
+ %% Assure tables loaded
+ ?match({[ok, ok], []},
+ rpc:multicall([N1, N2], mnesia, wait_for_tables,
+ [[schema], 10000])),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(2, Config),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+table_load_to_disc_less_nodes(doc)->
+ ["Load tables to disc less nodes"];
+table_load_to_disc_less_nodes(suite) -> [];
+table_load_to_disc_less_nodes(Config) when is_list(Config) ->
+ [N1, N2] = ?init(2, Config),
+
+ ?match(ok, rpc:call(N1, mnesia, start, [[{schema_location, ram}]])),
+
+ %% Really should use test_lib:mnesia_start for these ones but ...
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia,add_table_copy, [schema, N2, ram_copies])),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{schema_location, ram},
+ {extra_db_nodes, [N1]}]])),
+
+ %% Now create some tables
+ ?match({atomic,ok},
+ rpc:call(
+ N1, mnesia,create_table, [test_table,
+ [%%{disc_copies, [node()]},
+ {ram_copies, [N1, N2]},
+ {attributes,
+ record_info(fields,test_table)}]])),
+
+ %% Assure tables loaded
+ ?match({[ok, ok], []},
+ rpc:multicall([N1, N2], mnesia, wait_for_tables,
+ [[test_table], 1000])),
+
+ %% Write something on one end ...
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:write(#test_table{i=44}) end])),
+
+ %% Force synchronicity
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:write_lock_table(test_table) end])),
+
+ %% ... and read it in the others
+ ?match({atomic, [{test_table, 44, _, _, _}]},
+ rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:read({test_table, 44}) end])),
+
+ ?cleanup(2, Config),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+schema_merge(doc) ->
+ ["Provoke various schema merge situations.",
+ "Perform various schema updates while some nodes are down,",
+ "stop the started nodes, start the stopped nodes and perform",
+ "schema updates. Now we have a situation were some of the table",
+ "definitions have been changed on two or more nodes independently",
+ "of each other and when Mnesia on the nodes tries to connect",
+ "to each other at restart the schema will be merged.",
+ "Do also try to provoke schema merge situations were the",
+ "schema cannot be merged."];
+
+schema_merge(suite) -> [];
+
+schema_merge(Config) when is_list(Config) ->
+ [N1, N2]=Nodes=?acquire(2,Config),
+
+ mnesia_test_lib:kill_mnesia([N2]),
+ receive after 1000 -> ok end,
+
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ ?match({atomic,ok},
+ rpc:call(
+ N1, mnesia,create_table,
+ [test_table,
+ [{Storage, [N1]},
+ {attributes,
+ record_info(fields,test_table)}]])),
+
+ ?match({atomic, ok},
+ rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:write(#test_table{i=44}) end])),
+
+ mnesia_test_lib:kill_mnesia([N1]),
+ receive after 2000 -> ok end,
+ %% Can't use std start because it waits for schema
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+
+ ?match({atomic,ok},
+ rpc:call(
+ N2, mnesia,create_table,
+ [test_table2,
+ [{Storage, [N2]},
+ {attributes,
+ record_info(fields,test_table2)}]])),
+
+ receive after 5000 -> ok end,
+
+ ?match({atomic, ok},
+ rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:write(#test_table2{i=33}) end])),
+
+ %% Can't use std start because it waits for schema
+ ?match(ok, rpc:call(N1, mnesia, start, [])),
+
+ %% Assure tables loaded
+ ?match({[ok, ok], []},
+ rpc:multicall([N1, N2], mnesia, wait_for_tables,
+ [[schema, test_table, test_table2], 10000])),
+
+ %% ... and read it in the others
+ ?match({[{atomic, [{test_table, 44, _, _, _}]},
+ {atomic, [{test_table, 44, _, _, _}]}], []},
+ rpc:multicall([N1, N2], mnesia, transaction,
+ [fun() -> mnesia:read({test_table, 44}) end])),
+
+ ?match({[{atomic, [{test_table2, 33, _}]},
+ {atomic, [{test_table2, 33, _}]}], []},
+ rpc:multicall([N1, N2], mnesia, transaction,
+ [fun() -> mnesia:read({test_table2, 33}) end])),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(2, Config),
+ ok.
+
+
+-define(connect(Nodes), mnesia:change_config(extra_db_nodes, Nodes)).
+-define(rpc_connect(From, Nodes),
+ rpc:call(From, mnesia, change_config, [extra_db_nodes, Nodes])).
+
+
+sort({ok, NS}) ->
+ {ok, lists:sort(NS)};
+sort(Ns) when is_tuple(Ns) ->
+ Ns;
+sort(NS) when is_list(NS) ->
+ lists:sort(NS).
+
+
+
+
+dynamic_basic(suite) -> [];
+dynamic_basic(Config) when is_list(Config) ->
+ Nodes = [N1, N2, N3] = ?acquire_nodes(3, Config),
+ SNs = lists:sort(Nodes),
+
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{ram_copies, Nodes--[N1]}, {disc_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab2, [{disc_copies, Nodes}])),
+
+ ?match({ok, SNs}, sort(?rpc_connect(N1, Nodes))), %% What shall happen?
+ ?match({ok, []}, sort(?rpc_connect(N1, [nonode@nothosted]))), %% What shall happen?
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match(ok, mnesia:delete_schema([N2])),
+
+ ?match(ok, mnesia:dirty_write({tab1, 1, 1})),
+ ?match(ok, mnesia:dirty_write({tab2, 1, 1})),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes, [N1]}]])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1,tab2],5000])),
+ io:format("Here ~p ~n",[?LINE]),
+ check_storage(N2, N1, [N3]),
+ ?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+ ?match(ok, mnesia:delete_schema([N3])),
+
+ io:format("T1 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
+ ?match(ok, rpc:call(N3, mnesia, start, [])),
+ io:format("T2 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
+ timer:sleep(2000),
+ io:format("T3 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
+ ?match({ok, [N1]}, sort(?rpc_connect(N3, [N1]))),
+ io:format("T4 ~p ~n",[rpc:call(N3,?MODULE,c_nodes,[])]),
+ ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[tab1,tab2],5000])),
+ io:format("Here ~p ~n",[?LINE]),
+ check_storage(N3, N1, [N2]),
+ ?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+ ?match(ok, mnesia:delete_schema([N3])),
+
+ ?match(ok, rpc:call(N3, mnesia, start, [])),
+ ?match({ok, [N3]}, sort(?rpc_connect(N1, [N3]))),
+ ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[tab1,tab2],5000])),
+ io:format("Here ~p ~n",[?LINE]),
+ check_storage(N3, N1, [N2]),
+ ?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+
+ mnesia_test_lib:kill_mnesia([N2]),
+ ?match(ok, mnesia:delete_schema([N2])),
+ ?match({atomic, ok}, mnesia:del_table_copy(schema, N2)),
+
+ % Ok, we have now removed references to node N2 from the other nodes
+ % mnesia should come up now.
+ ?match({atomic, ok}, mnesia:add_table_copy(tab1, N2, ram_copies)),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match({ok, _}, sort(?rpc_connect(N2, [N3]))),
+
+ ?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N3, mnesia, system_info, [running_db_nodes]))),
+
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1], 1000])),
+ ?match([{tab1, 1, 1}], rpc:call(N2, mnesia, dirty_read, [tab1, 1])),
+
+ mnesia_test_lib:kill_mnesia([N2]),
+
+ %%% SYNC!!!
+ timer:sleep(1000),
+
+ ?match([N3,N1], sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match([N3,N1], sort(rpc:call(N3, mnesia, system_info, [running_db_nodes]))),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match({ok, _}, sort(?rpc_connect(N3, [N2]))),
+
+ ?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N3, mnesia, system_info, [running_db_nodes]))),
+
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1], 1000])),
+ ?match([{tab1, 1, 1}], rpc:call(N2, mnesia, dirty_read, [tab1, 1])),
+
+ ?verify_mnesia(Nodes, []),
+%% ?cleanup(3, Config).
+ ok.
+
+c_nodes() ->
+ {mnesia_lib:val({current, db_nodes}),mnesia_lib:val(recover_nodes)}.
+
+
+dynamic_ext(suite) -> [];
+dynamic_ext(Config) when is_list(Config) ->
+ Ns = [N1,N2] = ?acquire_nodes(2, Config),
+ SNs = lists:sort([N1,N2]),
+
+ ?match({atomic, ok}, mnesia:create_table(tab0, [{disc_copies, [N1,N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{ram_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab2, [{disc_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab3, [{disc_only_copies, [N2]}])),
+
+ mnesia_test_lib:kill_mnesia([N2]),
+ ?match(ok, mnesia:delete_schema([N2])),
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes, [N1]}]])),
+
+ ?match(SNs, sort(rpc:call(N1, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab0,tab1,tab2,tab3], 2000])),
+
+ Check = fun({Tab,Storage}) ->
+ ?match(Storage, rpc:call(N2, mnesia, table_info, [Tab, storage_type])),
+ ?match([{N2,Storage}],
+ lists:sort(rpc:call(N2, mnesia, table_info, [Tab, where_to_commit])))
+ end,
+ [Check(Test) || Test <- [{tab1, ram_copies},{tab2, disc_copies},{tab3, disc_only_copies}]],
+
+ T = now(),
+ ?match(ok, mnesia:dirty_write({tab0, 42, T})),
+ ?match(ok, mnesia:dirty_write({tab1, 42, T})),
+ ?match(ok, mnesia:dirty_write({tab2, 42, T})),
+ ?match(ok, mnesia:dirty_write({tab3, 42, T})),
+
+ ?match(stopped, rpc:call(N2, mnesia, stop, [])),
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+ ?match(ok, mnesia:wait_for_tables([tab0,tab1,tab2,tab3], 10000)),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1,tab2,tab3], 100])),
+ ?match([], mnesia:dirty_read({tab1, 41})),
+ ?match([{tab2,42,T}], mnesia:dirty_read({tab2, 42})),
+ ?match([{tab3,42,T}], mnesia:dirty_read({tab3, 42})),
+
+ mnesia_test_lib:kill_mnesia([N2]),
+ ?match(ok, mnesia:delete_schema([N2])),
+
+ ?match(stopped, rpc:call(N1, mnesia, stop, [])),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1,N2]}]])),
+ ?match({timeout,[tab0]}, rpc:call(N2, mnesia, wait_for_tables, [[tab0], 500])),
+
+ ?match(ok, rpc:call(N1, mnesia, start, [[{extra_db_nodes, [N1,N2]}]])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[tab0], 1500])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab0], 1500])),
+ ?match([{tab0,42,T}], mnesia:dirty_read({tab0, 42})),
+ ?match([{tab0,42,T}], rpc:call(N2, mnesia,dirty_read,[{tab0,42}])),
+
+ ?match(stopped, rpc:call(N1, mnesia, stop, [])),
+ mnesia_test_lib:kill_mnesia([N2]),
+ ?match(ok, mnesia:delete_schema([N2])),
+ ?match(ok, rpc:call(N1, mnesia, start, [[{extra_db_nodes, [N1,N2]}]])),
+ ?match({timeout,[tab0]}, rpc:call(N1, mnesia, wait_for_tables, [[tab0], 500])),
+
+ ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1,N2]}]])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[tab0], 1500])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab0], 1500])),
+ ?match([{tab0,42,T}], mnesia:dirty_read({tab0, 42})),
+ ?match([{tab0,42,T}], rpc:call(N2,mnesia,dirty_read,[{tab0,42}])),
+
+ ?verify_mnesia(Ns, []),
+ ok.
+
+check_storage(Me, Orig, Other) ->
+ io:format("Nodes ~p ~p ~p~n",[Me,Orig,Other]),
+ rpc:multicall(Other, sys, status, [mnesia_locker]),
+ rpc:call(Me, sys, status, [mnesia_locker]),
+ rpc:call(Orig, sys, status, [mnesia_locker]),
+ rpc:multicall(Other, sys, status, [mnesia_controller]),
+ rpc:call(Me, sys, status, [mnesia_controller]),
+ rpc:call(Orig, sys, status, [mnesia_controller]),
+ %% Verify disc_copies
+ W2C = lists:sort([{Node,disc_copies} || Node <- [Me,Orig|Other]]),
+ W2W = lists:sort([Me,Orig|Other]),
+ ?match(disc_copies, rpc:call(Orig, mnesia, table_info, [schema, storage_type])),
+ ?match(disc_copies, rpc:call(Me, mnesia, table_info, [schema, storage_type])),
+ ?match(W2C, lists:sort(rpc:call(Orig, mnesia, table_info, [schema, where_to_commit]))),
+ ?match(W2C, lists:sort(rpc:call(Me, mnesia, table_info, [schema, where_to_commit]))),
+
+ ?match(disc_copies, rpc:call(Orig, mnesia, table_info, [tab2, storage_type])),
+ ?match(disc_copies, rpc:call(Me, mnesia, table_info, [tab2, storage_type])),
+ ?match(W2W, lists:sort(rpc:call(Me, mnesia, table_info, [tab2, where_to_write]))),
+ ?match(Me, rpc:call(Me, mnesia, table_info, [tab2, where_to_read])),
+
+ ?match(W2C, lists:sort(rpc:call(Orig, mnesia, table_info, [tab2, where_to_commit]))),
+ ?match(W2C, lists:sort(rpc:call(Me, mnesia, table_info, [tab2, where_to_commit]))),
+
+ ?match([{tab1,1,1}], mnesia:dirty_read(tab1,1)),
+ ?match([{tab2,1,1}], mnesia:dirty_read(tab2,1)),
+ ?match([{tab1,1,1}], rpc:call(Me, mnesia, dirty_read, [tab1,1])),
+ ?match([{tab2,1,1}], rpc:call(Me, mnesia, dirty_read, [tab2,1])),
+
+ ?match(true, rpc:call(Me, mnesia_monitor, use_dir, [])),
+ ?match(disc_copies, rpc:call(Me, mnesia_lib, val, [{schema, storage_type}])),
+
+ mnesia_test_lib:kill_mnesia([Orig]),
+ mnesia_test_lib:kill_mnesia(Other),
+ T = now(),
+ ?match(ok, rpc:call(Me, mnesia, dirty_write, [{tab2, 42, T}])),
+ ?match(stopped, rpc:call(Me, mnesia, stop, [])),
+ ?match(ok, rpc:call(Me, mnesia, start, [])),
+ ?match([], mnesia_test_lib:start_mnesia([Orig|Other], [tab1,tab2])),
+ ?match([{tab2,42,T}], rpc:call(Me, mnesia, dirty_read, [{tab2, 42}])),
+ ?match([{tab2,42,T}], rpc:call(Orig, mnesia, dirty_read, [{tab2, 42}])),
+
+ ?match([{tab1,1,1}], mnesia:dirty_read(tab1,1)),
+ ?match([{tab2,1,1}], mnesia:dirty_read(tab2,1)),
+ ?match([{tab1,1,1}], rpc:call(Me, mnesia, dirty_read, [tab1,1])),
+ ?match([{tab2,1,1}], rpc:call(Me, mnesia, dirty_read, [tab2,1])),
+ ok.
+
+
+dynamic_bad(suite) -> [];
+dynamic_bad(Config) when is_list(Config) ->
+ Ns = [N1, N2, N3] = ?acquire_nodes(3, Config),
+ SNs = lists:sort([N2,N3]),
+
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, N2, ram_copies)),
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, N3, ram_copies)),
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{ram_copies, Ns -- [N1]},
+ {disc_copies, [N1]}])),
+ ?match(ok, mnesia:dirty_write({tab1, 1, 1})),
+
+ mnesia_test_lib:kill_mnesia(Ns),
+ ?match({[ok, ok], []}, rpc:multicall(Ns -- [N1], mnesia, start, [])),
+ ?match({ok, [N2]}, ?rpc_connect(N3, [N2])),
+ ?match(SNs, sort(rpc:call(N2, mnesia, system_info, [running_db_nodes]))),
+ ?match(SNs, sort(rpc:call(N3, mnesia, system_info, [running_db_nodes]))),
+ ?match({badrpc, {'EXIT', {aborted, {no_exists, _, _}}}},
+ rpc:call(N2, mnesia, table_info, [tab1, where_to_read])),
+
+ ?match(ok, mnesia:start()),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[tab1], 1000])),
+ ?match(N2, rpc:call(N2, mnesia, table_info, [tab1, where_to_read])),
+ ?match([{tab1, 1, 1}], rpc:call(N2, mnesia, dirty_read, [tab1, 1])),
+
+ mnesia_test_lib:kill_mnesia(Ns),
+ ?match({[ok, ok], []}, rpc:multicall(Ns -- [N1], mnesia, start, [])),
+ ?match({ok, [N2]}, ?rpc_connect(N3, [N2])),
+ % Make a merge conflict
+ ?match({atomic, ok}, rpc:call(N3, mnesia, create_table, [tab1, []])),
+
+ io:format("We expect a mnesia crash here~n", []),
+ ?match({error,{_, _}}, mnesia:start()),
+
+ ?verify_mnesia(Ns -- [N1], []),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+unknown_config(doc) ->
+ ["Try some unknown configuration parameters and see that expected",
+ "things happens."];
+unknown_config(suite)-> [];
+unknown_config(Config) when is_list(Config) ->
+ ?init(1, Config),
+ %% NOTE: case 1 & 2 below do not respond the same
+ ?match({error, Res} when element(1, Res) == bad_type,
+ mnesia:start([{undefined_config,[]}])),
+ %% Below does not work, but the "correct" behaviour would be to have
+ %% case 1 above to behave as the one below.
+
+ %% in mnesia-1.3 {error,{bad_type,{[],undefined_config}}}
+ ?match({error, Res} when element(1, Res) == bad_type,
+ mnesia:start([{[],undefined_config}])),
+ ?cleanup(1, Config),
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+inconsistent_database(doc) ->
+ ["Replace the event module with another module and use it as",
+ "receiver of the various system and table events. Provoke",
+ "coverage of all kinds of events."];
+inconsistent_database(suite) -> [];
+inconsistent_database(Config) when is_list(Config) ->
+ Nodes = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]}],
+ 2, Config, ?FILE, ?LINE),
+ KillAfter = length(Nodes) * timer:minutes(5),
+ ?acquire_schema(2, Config ++ [{tc_timeout, KillAfter}]),
+
+ Ok = [ok || _N <- Nodes],
+ StartArgs = [{event_module, mnesia_inconsistent_database_test}],
+ ?match({Ok, []}, rpc:multicall(Nodes, mnesia, start, [StartArgs])),
+ ?match([], mnesia_test_lib:kill_mnesia(Nodes)),
+
+ ?match(ok, mnesia_meter:go(ram_copies, Nodes)),
+
+ mnesia_test_lib:reload_appls([mnesia], Nodes),
+ ok.
+
diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl
new file mode 100644
index 0000000000..f38e13f3a2
--- /dev/null
+++ b/lib/mnesia/test/mnesia_consistency_test.erl
@@ -0,0 +1,1512 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_consistency_test).
+-author('[email protected]').
+-compile([export_all]).
+
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [{group, consistency_after_restart},
+ {group, consistency_after_dump_tables},
+ {group, consistency_after_add_replica},
+ {group, consistency_after_del_replica},
+ {group, consistency_after_move_replica},
+ {group, consistency_after_transform_table},
+ consistency_after_change_table_copy_type,
+ {group, consistency_after_fallback},
+ {group, consistency_after_restore},
+ consistency_after_rename_of_node,
+ {group, checkpoint_retainer_consistency},
+ {group, backup_consistency}].
+
+groups() ->
+ [{consistency_after_restart, [],
+ [consistency_after_restart_1_ram,
+ consistency_after_restart_1_disc,
+ consistency_after_restart_1_disc_only,
+ consistency_after_restart_2_ram,
+ consistency_after_restart_2_disc,
+ consistency_after_restart_2_disc_only]},
+ {consistency_after_dump_tables, [],
+ [consistency_after_dump_tables_1_ram,
+ consistency_after_dump_tables_2_ram]},
+ {consistency_after_add_replica, [],
+ [consistency_after_add_replica_2_ram,
+ consistency_after_add_replica_2_disc,
+ consistency_after_add_replica_2_disc_only,
+ consistency_after_add_replica_3_ram,
+ consistency_after_add_replica_3_disc,
+ consistency_after_add_replica_3_disc_only]},
+ {consistency_after_del_replica, [],
+ [consistency_after_del_replica_2_ram,
+ consistency_after_del_replica_2_disc,
+ consistency_after_del_replica_2_disc_only,
+ consistency_after_del_replica_3_ram,
+ consistency_after_del_replica_3_disc,
+ consistency_after_del_replica_3_disc_only]},
+ {consistency_after_move_replica, [],
+ [consistency_after_move_replica_2_ram,
+ consistency_after_move_replica_2_disc,
+ consistency_after_move_replica_2_disc_only,
+ consistency_after_move_replica_3_ram,
+ consistency_after_move_replica_3_disc,
+ consistency_after_move_replica_3_disc_only]},
+ {consistency_after_transform_table, [],
+ [consistency_after_transform_table_ram,
+ consistency_after_transform_table_disc,
+ consistency_after_transform_table_disc_only]},
+ {consistency_after_fallback, [],
+ [consistency_after_fallback_2_ram,
+ consistency_after_fallback_2_disc,
+ consistency_after_fallback_2_disc_only,
+ consistency_after_fallback_3_ram,
+ consistency_after_fallback_3_disc,
+ consistency_after_fallback_3_disc_only]},
+ {consistency_after_restore, [],
+ [consistency_after_restore_clear_ram,
+ consistency_after_restore_clear_disc,
+ consistency_after_restore_clear_disc_only,
+ consistency_after_restore_recreate_ram,
+ consistency_after_restore_recreate_disc,
+ consistency_after_restore_recreate_disc_only]},
+ {checkpoint_retainer_consistency, [],
+ [{group, updates_during_checkpoint_activation},
+ {group, updates_during_checkpoint_iteration},
+ {group, load_table_with_activated_checkpoint},
+ {group,
+ add_table_copy_to_table_with_activated_checkpoint}]},
+ {updates_during_checkpoint_activation, [],
+ [updates_during_checkpoint_activation_2_ram,
+ updates_during_checkpoint_activation_2_disc,
+ updates_during_checkpoint_activation_2_disc_only,
+ updates_during_checkpoint_activation_3_ram,
+ updates_during_checkpoint_activation_3_disc,
+ updates_during_checkpoint_activation_3_disc_only]},
+ {updates_during_checkpoint_iteration, [],
+ [updates_during_checkpoint_iteration_2_ram,
+ updates_during_checkpoint_iteration_2_disc,
+ updates_during_checkpoint_iteration_2_disc_only]},
+ {load_table_with_activated_checkpoint, [],
+ [load_table_with_activated_checkpoint_ram,
+ load_table_with_activated_checkpoint_disc,
+ load_table_with_activated_checkpoint_disc_only]},
+ {add_table_copy_to_table_with_activated_checkpoint, [],
+ [add_table_copy_to_table_with_activated_checkpoint_ram,
+ add_table_copy_to_table_with_activated_checkpoint_disc,
+ add_table_copy_to_table_with_activated_checkpoint_disc_only]},
+ {backup_consistency, [],
+ [{group, interupted_install_fallback},
+ {group, interupted_uninstall_fallback},
+ {group, mnesia_down_during_backup_causes_switch},
+ {group, mnesia_down_during_backup_causes_abort},
+ {group, schema_transactions_during_backup}]},
+ {interupted_install_fallback, [],
+ [inst_fallback_process_dies, fatal_when_inconsistency]},
+ {interupted_uninstall_fallback, [], [after_delete]},
+ {mnesia_down_during_backup_causes_switch, [],
+ [cause_switch_before, cause_switch_after]},
+ {mnesia_down_during_backup_causes_abort, [],
+ [cause_abort_before, cause_abort_after]},
+ {schema_transactions_during_backup, [],
+ [change_schema_before, change_schema_after]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% stolen from mnesia_tpcb.erl:
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Account record, total size must be at least 100 bytes
+
+-define(ACCOUNT_FILLER,
+ {123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234}).
+
+-record(account,
+ {
+ id = 0, %% Unique account id
+ branch_id = 0, %% Branch where the account is held
+ balance = 0, %% Account balance
+ filler = ?ACCOUNT_FILLER %% Gap filler to ensure size >= 100 bytes
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Branch record, total size must be at least 100 bytes
+
+-define(BRANCH_FILLER,
+ {123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Teller record, total size must be at least 100 bytes
+
+-define(TELLER_FILLER,
+ {123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890,
+ 1234567890123456789012345678901234567890123456789012345678}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% History record, total size must be at least 50 bytes
+
+-define(HISTORY_FILLER, 1234567890).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-record(tab_config,
+ {
+ db_nodes = [node()],
+ replica_nodes = [node()],
+ replica_type = ram_copies,
+ use_running_mnesia = false,
+ n_branches = 1,
+ n_tellers_per_branch = 10, %% Must be 10
+ n_accounts_per_branch = 100000, %% Must be 100000
+ branch_filler = ?BRANCH_FILLER,
+ account_filler = ?ACCOUNT_FILLER,
+ teller_filler = ?TELLER_FILLER
+ }).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% stolen from mnesia_tpcb.erl:
+
+list2rec(List, Fields, DefaultTuple) ->
+ [Name|Defaults] = tuple_to_list(DefaultTuple),
+ List2 = list2rec(List, Fields, Defaults, []),
+ list_to_tuple([Name] ++ List2).
+
+list2rec(_List, [], [], Acc) ->
+ Acc;
+list2rec(List, [F|Fields], [D|Defaults], Acc) ->
+ {Val, List2} =
+ case lists:keysearch(F, 1, List) of
+ false ->
+ {D, List};
+ {value, {F, NewVal}} ->
+ {NewVal, lists:keydelete(F, 1, List)}
+ end,
+ list2rec(List2, Fields, Defaults, Acc ++ [Val]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+tpcb_config(ReplicaType, _NodeConfig, Nodes, NoDriverNodes) ->
+ [{n_branches, 10},
+ {n_drivers_per_node, 10},
+ {replica_nodes, Nodes},
+ {driver_nodes, Nodes -- NoDriverNodes},
+ {use_running_mnesia, true},
+ {report_interval, infinity},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {reuse_history_id, true}].
+
+%% Stolen from mnesia_tpcb:dist
+tpcb_config_dist(ReplicaType, _NodeConfig, Nodes, _Config) ->
+ [{db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 10},
+ {n_branches, 1},
+ {use_running_mnesia, true},
+ {n_accounts_per_branch, 10},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(15)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% stolen from mnesia_recovery_test.erl:
+
+receive_messages([]) -> [];
+receive_messages(ListOfMsgs) ->
+ receive
+ {Pid, Msg} ->
+ case lists:member(Msg, ListOfMsgs) of
+ false ->
+ ?warning("I (~p) have received unexpected msg~n ~p ~n",
+ [self(),{Pid, Msg}]),
+ receive_messages(ListOfMsgs);
+ true ->
+ ?verbose("I (~p) got msg ~p from ~p ~n", [self(),Msg, Pid]),
+ [{Pid, Msg} | receive_messages(ListOfMsgs -- [Msg])]
+ end;
+ Else -> ?warning("Recevied unexpected Msg~n ~p ~n", [Else])
+ after timer:minutes(3) ->
+ ?error("Timeout in receive msgs while waiting for ~p~n",
+ [ListOfMsgs])
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_restart_1_ram(suite) -> [];
+consistency_after_restart_1_ram(Config) when is_list(Config) ->
+ consistency_after_restart(ram_copies, 2, Config).
+
+consistency_after_restart_1_disc(suite) -> [];
+consistency_after_restart_1_disc(Config) when is_list(Config) ->
+ consistency_after_restart(disc_copies, 2, Config).
+
+consistency_after_restart_1_disc_only(suite) -> [];
+consistency_after_restart_1_disc_only(Config) when is_list(Config) ->
+ consistency_after_restart(disc_only_copies, 2, Config).
+
+consistency_after_restart_2_ram(suite) -> [];
+consistency_after_restart_2_ram(Config) when is_list(Config) ->
+ consistency_after_restart(ram_copies, 3, Config).
+
+consistency_after_restart_2_disc(suite) -> [];
+consistency_after_restart_2_disc(Config) when is_list(Config) ->
+ consistency_after_restart(disc_copies, 3, Config).
+
+consistency_after_restart_2_disc_only(suite) -> [];
+consistency_after_restart_2_disc_only(Config) when is_list(Config) ->
+ consistency_after_restart(disc_only_copies, 3, Config).
+
+consistency_after_restart(ReplicaType, NodeConfig, Config) ->
+ [Node1 | _] = Nodes = ?acquire_nodes(NodeConfig, Config),
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_restart with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes, [Node1]),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(10)),
+ mnesia_test_lib:kill_mnesia([Node1]),
+ %% Start and wait for tables to be loaded on all nodes
+ timer:sleep(timer:seconds(3)),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes,[account,branch,teller, history])),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_dump_tables_1_ram(suite) -> [];
+consistency_after_dump_tables_1_ram(Config) when is_list(Config) ->
+ consistency_after_dump_tables(ram_copies, 1, Config).
+
+consistency_after_dump_tables_2_ram(suite) -> [];
+consistency_after_dump_tables_2_ram(Config) when is_list(Config) ->
+ consistency_after_dump_tables(ram_copies, 2, Config).
+
+consistency_after_dump_tables(ReplicaType, NodeConfig, Config) ->
+ [Node1 | _] = Nodes = ?acquire_nodes(NodeConfig, Config),
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_dump_tables with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes, []),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun() -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(10)),
+ ?match({atomic, ok}, rpc:call(Node1, mnesia, dump_tables,
+ [[branch, teller, account, history]])),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ timer:sleep(timer:seconds(1)),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes,[account, branch,
+ teller, history])),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_add_replica_2_ram(suite) -> [];
+consistency_after_add_replica_2_ram(Config) when is_list(Config) ->
+ consistency_after_add_replica(ram_copies, 2, Config).
+
+consistency_after_add_replica_2_disc(suite) -> [];
+consistency_after_add_replica_2_disc(Config) when is_list(Config) ->
+ consistency_after_add_replica(disc_copies, 2, Config).
+
+consistency_after_add_replica_2_disc_only(suite) -> [];
+consistency_after_add_replica_2_disc_only(Config) when is_list(Config) ->
+ consistency_after_add_replica(disc_only_copies, 2, Config).
+
+consistency_after_add_replica_3_ram(suite) -> [];
+consistency_after_add_replica_3_ram(Config) when is_list(Config) ->
+ consistency_after_add_replica(ram_copies, 3, Config).
+
+consistency_after_add_replica_3_disc(suite) -> [];
+consistency_after_add_replica_3_disc(Config) when is_list(Config) ->
+ consistency_after_add_replica(disc_copies, 3, Config).
+
+consistency_after_add_replica_3_disc_only(suite) -> [];
+consistency_after_add_replica_3_disc_only(Config) when is_list(Config) ->
+ consistency_after_add_replica(disc_only_copies, 3, Config).
+
+consistency_after_add_replica(ReplicaType, NodeConfig, Config) ->
+ Nodes0 = ?acquire_nodes(NodeConfig, Config),
+ AddNode = lists:last(Nodes0),
+ Nodes = Nodes0 -- [AddNode],
+ Node1 = hd(Nodes),
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_add_replica with ~p on ~p~n",
+ [ReplicaType, Nodes0]),
+ TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes, []),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(10)),
+ ?match({atomic, ok}, mnesia:add_table_copy(account, AddNode, ReplicaType)),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ ?verify_mnesia(Nodes0, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_del_replica_2_ram(suite) -> [];
+consistency_after_del_replica_2_ram(Config) when is_list(Config) ->
+ consistency_after_del_replica(ram_copies, 2, Config).
+
+consistency_after_del_replica_2_disc(suite) -> [];
+consistency_after_del_replica_2_disc(Config) when is_list(Config) ->
+ consistency_after_del_replica(disc_copies, 2, Config).
+
+consistency_after_del_replica_2_disc_only(suite) -> [];
+consistency_after_del_replica_2_disc_only(Config) when is_list(Config) ->
+ consistency_after_del_replica(disc_only_copies, 2, Config).
+
+consistency_after_del_replica_3_ram(suite) -> [];
+consistency_after_del_replica_3_ram(Config) when is_list(Config) ->
+ consistency_after_del_replica(ram_copies, 3, Config).
+
+consistency_after_del_replica_3_disc(suite) -> [];
+consistency_after_del_replica_3_disc(Config) when is_list(Config) ->
+ consistency_after_del_replica(disc_copies, 3, Config).
+
+consistency_after_del_replica_3_disc_only(suite) -> [];
+consistency_after_del_replica_3_disc_only(Config) when is_list(Config) ->
+ consistency_after_del_replica(disc_only_copies, 3, Config).
+
+consistency_after_del_replica(ReplicaType, NodeConfig, Config) ->
+ Nodes = ?acquire_nodes(NodeConfig, Config),
+ Node1 = hd(Nodes),
+ Node2 = lists:last(Nodes),
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_del_replica with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes, []),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(10)),
+ ?match({atomic, ok}, mnesia:del_table_copy(account, Node2)),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_move_replica_2_ram(suite) -> [];
+consistency_after_move_replica_2_ram(Config) when is_list(Config) ->
+ consistency_after_move_replica(ram_copies, 2, Config).
+
+consistency_after_move_replica_2_disc(suite) -> [];
+consistency_after_move_replica_2_disc(Config) when is_list(Config) ->
+ consistency_after_move_replica(disc_copies, 2, Config).
+
+consistency_after_move_replica_2_disc_only(suite) -> [];
+consistency_after_move_replica_2_disc_only(Config) when is_list(Config) ->
+ consistency_after_move_replica(disc_only_copies, 2, Config).
+
+consistency_after_move_replica_3_ram(suite) -> [];
+consistency_after_move_replica_3_ram(Config) when is_list(Config) ->
+ consistency_after_move_replica(ram_copies, 3, Config).
+
+consistency_after_move_replica_3_disc(suite) -> [];
+consistency_after_move_replica_3_disc(Config) when is_list(Config) ->
+ consistency_after_move_replica(disc_copies, 3, Config).
+
+consistency_after_move_replica_3_disc_only(suite) -> [];
+consistency_after_move_replica_3_disc_only(Config) when is_list(Config) ->
+ consistency_after_move_replica(disc_only_copies, 3, Config).
+
+consistency_after_move_replica(ReplicaType, NodeConfig, Config) ->
+ Nodes = ?acquire_nodes(NodeConfig, Config ++ [{tc_timeout, timer:minutes(10)}]),
+ Node1 = hd(Nodes),
+ Node2 = lists:last(Nodes),
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_move_replica with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes -- [Node2], []),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(10)),
+ ?match({atomic, ok}, mnesia:move_table_copy(account, Node1, Node2)),
+ ?log("First move completed from node ~p to ~p ~n", [Node1, Node2]),
+ ?match({atomic, ok}, mnesia:move_table_copy(account, Node2, Node1)),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+consistency_after_transform_table_ram(suite) -> [];
+consistency_after_transform_table_ram(Config) when is_list(Config) ->
+ consistency_after_transform_table(ram_copies, Config).
+
+consistency_after_transform_table_disc(suite) -> [];
+consistency_after_transform_table_disc(Config) when is_list(Config) ->
+ consistency_after_transform_table(disc_copies, Config).
+
+consistency_after_transform_table_disc_only(suite) -> [];
+consistency_after_transform_table_disc_only(Config) when is_list(Config) ->
+ consistency_after_transform_table(disc_only_copies, Config).
+
+consistency_after_transform_table(Type, Config) ->
+ Nodes = [N1, N2,_N3] = ?acquire_nodes(3, Config),
+
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{index, [3]}, {Type, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab2, [{index, [3]}, {Type, [N1,N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab3, [{index, [3]}, {Type, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(empty, [{index, [3]},{Type, Nodes}])),
+
+ Tabs = lists:sort([tab1, tab2, tab3, empty]),
+
+ [[mnesia:dirty_write({Tab, N, N}) || N <- lists:seq(1,10)] ||
+ Tab <- Tabs -- [empty, tab4]],
+ mnesia:dump_log(),
+
+ Ok = lists:duplicate(4, {atomic, ok}),
+ ?match(Ok, [mnesia:transform_table(Tab, fun({T, N, N}) -> {T, N, N, ok} end,
+ [k,a,n]) || Tab <- Tabs]),
+ [?match([k,a,n], mnesia:table_info(Tab, attributes)) || Tab <- Tabs],
+
+ Filter = fun(Tab) -> mnesia:foldl(fun(A, Acc) when size(A) == 3 -> [A|Acc];
+ (A, Acc) when size(A) == 4 -> Acc
+ end, [], Tab)
+ end,
+
+ ?match([[],[],[],[]], [element(2,mnesia:transaction(Filter, [Tab])) || Tab <- Tabs]),
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ mnesia_test_lib:start_mnesia(Nodes, Tabs),
+
+ ?match([Tabs, Tabs, Tabs],
+ [lists:sort(rpc:call(Node, mnesia,system_info, [tables]) -- [schema]) || Node <- Nodes]),
+
+ ?match([[],[],[],[]], [element(2,mnesia:transaction(Filter, [Tab])) || Tab <- Tabs]),
+ [?match([k,a,n], mnesia:table_info(Tab, attributes)) || Tab <- Tabs],
+
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+consistency_after_change_table_copy_type(doc) ->
+ ["Check that the database is consistent after change of copy type.",
+ " While applications are updating the involved tables. "].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_fallback_2_ram(suite) -> [];
+consistency_after_fallback_2_ram(Config) when is_list(Config) ->
+ consistency_after_fallback(ram_copies, 2, Config).
+
+consistency_after_fallback_2_disc(suite) -> [];
+consistency_after_fallback_2_disc(Config) when is_list(Config) ->
+ consistency_after_fallback(disc_copies, 2, Config).
+
+consistency_after_fallback_2_disc_only(suite) -> [];
+consistency_after_fallback_2_disc_only(Config) when is_list(Config) ->
+ consistency_after_fallback(disc_only_copies, 2, Config).
+
+consistency_after_fallback_3_ram(suite) -> [];
+consistency_after_fallback_3_ram(Config) when is_list(Config) ->
+ consistency_after_fallback(ram_copies, 3, Config).
+
+consistency_after_fallback_3_disc(suite) -> [];
+consistency_after_fallback_3_disc(Config) when is_list(Config) ->
+ consistency_after_fallback(disc_copies, 3, Config).
+
+consistency_after_fallback_3_disc_only(suite) -> [];
+consistency_after_fallback_3_disc_only(Config) when is_list(Config) ->
+ consistency_after_fallback(disc_only_copies, 3, Config).
+
+consistency_after_fallback(ReplicaType, NodeConfig, Config) ->
+ %%?verbose("Starting consistency_after_fallback2 at ~p~n", [self()]),
+ Delay = 5,
+ Nodes = ?acquire_nodes(NodeConfig, [{tc_timeout, timer:minutes(10)} | Config]),
+ Node1 = hd(Nodes),
+ %%?verbose("Mnesia info: ~p~n", [mnesia:info()]),
+
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_fallback with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes, []),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(Delay)),
+
+ %% Make a backup
+ ?verbose("Doing backup~n", []),
+ ?match(ok, mnesia:backup(consistency_after_fallback2)),
+
+ %% Install the backup as a fallback
+ ?verbose("Doing fallback~n", []),
+ ?match(ok, mnesia:install_fallback(consistency_after_fallback2)),
+ timer:sleep(timer:seconds(Delay)),
+
+ %% Stop tpcb
+ ?verbose("Stopping TPC-B~n", []),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+
+ %% Stop and then start mnesia and check table consistency
+ %%?verbose("Restarting Mnesia~n", []),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ mnesia_test_lib:start_mnesia(Nodes,[account,branch,teller,history]),
+
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ if
+ ReplicaType == ram_copies ->
+ %% Test that change_table_copy work i.e. no account.dcd file exists.
+ ?match({atomic, ok}, mnesia:change_table_copy_type(account, node(), disc_copies));
+ true ->
+ ignore
+ end,
+ file:delete(consistency_after_fallback2),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency_after_restore_clear_ram(suite) -> [];
+consistency_after_restore_clear_ram(Config) when is_list(Config) ->
+ consistency_after_restore(ram_copies, clear_tables, Config).
+
+consistency_after_restore_clear_disc(suite) -> [];
+consistency_after_restore_clear_disc(Config) when is_list(Config) ->
+ consistency_after_restore(disc_copies, clear_tables, Config).
+
+consistency_after_restore_clear_disc_only(suite) -> [];
+consistency_after_restore_clear_disc_only(Config) when is_list(Config) ->
+ consistency_after_restore(disc_only_copies, clear_tables, Config).
+
+consistency_after_restore_recreate_ram(suite) -> [];
+consistency_after_restore_recreate_ram(Config) when is_list(Config) ->
+ consistency_after_restore(ram_copies, recreate_tables, Config).
+
+consistency_after_restore_recreate_disc(suite) -> [];
+consistency_after_restore_recreate_disc(Config) when is_list(Config) ->
+ consistency_after_restore(disc_copies, recreate_tables, Config).
+
+consistency_after_restore_recreate_disc_only(suite) -> [];
+consistency_after_restore_recreate_disc_only(Config) when is_list(Config) ->
+ consistency_after_restore(disc_only_copies, recreate_tables, Config).
+
+consistency_after_restore(ReplicaType, Op, Config) ->
+ Delay = 1,
+ Nodes = ?acquire_nodes(3, [{tc_timeout, timer:minutes(10)} | Config]),
+ [Node1, Node2, _Node3] = Nodes,
+ File = "cons_backup_restore",
+
+ ?log("consistency_after_restore with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ Tabs = [carA, carB, carC, carD],
+
+ ?match({atomic, ok}, mnesia:create_table(carA, [{ReplicaType, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(carB, [{ReplicaType, Nodes -- [Node1]}])),
+ ?match({atomic, ok}, mnesia:create_table(carC, [{ReplicaType, Nodes -- [Node2]}])),
+ ?match({atomic, ok}, mnesia:create_table(carD, [{ReplicaType, [Node2]}])),
+
+ NList = lists:seq(0, 20),
+ [lists:foreach(fun(E) -> ok = mnesia:dirty_write({Tab, E, 1}) end, NList) ||
+ Tab <- Tabs],
+
+ {ok, Name, _} = mnesia:activate_checkpoint([{max, [schema | Tabs]},
+ {ram_overrides_dump, true}]),
+ ?verbose("Doing backup~n", []),
+ ?match(ok, mnesia:backup_checkpoint(Name, File)),
+ ?match(ok, mnesia:deactivate_checkpoint(Name)),
+
+ [lists:foreach(fun(E) -> ok = mnesia:dirty_write({Tab, E, 2}) end, NList) ||
+ Tab <- Tabs],
+
+ Pids1 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carA, Op]), ok} || _ <- lists:seq(1, 5)],
+ Pids2 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carB, Op]), ok} || _ <- lists:seq(1, 5)],
+ Pids3 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carC, Op]), ok} || _ <- lists:seq(1, 5)],
+ Pids4 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carD, Op]), ok} || _ <- lists:seq(1, 5)],
+
+ AllPids = Pids1 ++ Pids2 ++ Pids3 ++ Pids4,
+
+ Restore = fun(F, Args) ->
+ case mnesia:restore(F, Args) of
+ {atomic, List} -> lists:sort(List);
+ Else -> Else
+ end
+ end,
+
+ timer:sleep(timer:seconds(Delay)), %% Let changers grab locks
+ ?verbose("Doing restore~n", []),
+ ?match(Tabs, Restore(File, [{default_op, Op}])),
+
+ timer:sleep(timer:seconds(Delay)), %% Let em die
+
+ ?match_multi_receive(AllPids),
+
+ case ?match(ok, restore_verify_tabs(Tabs)) of
+ {success, ok} ->
+ file:delete(File);
+ _ ->
+ {T, M, S} = time(),
+ File2 = ?flat_format("consistency_error~w~w~w.BUP", [T, M, S]),
+ file:rename(File, File2)
+ end,
+ ?verify_mnesia(Nodes, []).
+
+change_tab(Father, Tab, Test) ->
+ Key = random:uniform(20),
+ Update = fun() ->
+ case mnesia:read({Tab, Key}) of
+ [{Tab, Key, 1}] ->
+ quit;
+ [{Tab, Key, _N}] ->
+ mnesia:write({Tab, Key, 3})
+ end
+ end,
+ case mnesia:transaction(Update) of
+ {atomic, quit} ->
+ exit(ok);
+ {aborted, {no_exists, Tab}} when Test == recreate_tables ->%% I'll allow this
+ change_tab(Father, Tab, Test);
+ {atomic, ok} ->
+ change_tab(Father, Tab, Test)
+ end.
+
+restore_verify_tabs([Tab | R]) ->
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:foldl(fun({_, _, 1}, ok) ->
+ ok;
+ (Else, Acc) ->
+ [Else|Acc]
+ end, ok, Tab)
+ end)),
+ restore_verify_tabs(R);
+restore_verify_tabs([]) ->
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+consistency_after_rename_of_node(doc) ->
+ ["Skipped because it is an unimportant case."].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+updates_during_checkpoint_activation_2_ram(suite) -> [];
+updates_during_checkpoint_activation_2_ram(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(ram_copies, 2, Config).
+
+updates_during_checkpoint_activation_2_disc(suite) -> [];
+updates_during_checkpoint_activation_2_disc(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(disc_copies, 2, Config).
+
+updates_during_checkpoint_activation_2_disc_only(suite) -> [];
+updates_during_checkpoint_activation_2_disc_only(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(disc_only_copies, 2, Config).
+
+updates_during_checkpoint_activation_3_ram(suite) -> [];
+updates_during_checkpoint_activation_3_ram(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(ram_copies, 3, Config).
+
+updates_during_checkpoint_activation_3_disc(suite) -> [];
+updates_during_checkpoint_activation_3_disc(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(disc_copies, 3, Config).
+
+updates_during_checkpoint_activation_3_disc_only(suite) -> [];
+updates_during_checkpoint_activation_3_disc_only(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(disc_only_copies, 3, Config).
+
+updates_during_checkpoint_activation(ReplicaType,NodeConfig,Config) ->
+ %%?verbose("updates_during_checkpoint_activation2 at ~p~n", [self()]),
+ Delay = 5,
+ Nodes = ?acquire_nodes(NodeConfig, Config),
+ Node1 = hd(Nodes),
+ %%?verbose("Mnesia info: ~p~n", [mnesia:info()]),
+
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("consistency_after_fallback with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config_dist(ReplicaType, NodeConfig, Nodes, Config),
+ %%TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ timer:sleep(timer:seconds(Delay)),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)}]),
+ timer:sleep(timer:seconds(Delay)),
+
+ %% Stop tpcb
+ ?verbose("Stopping TPC-B~n", []),
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+
+ ?match(ok, mnesia:backup_checkpoint(CPName,
+ updates_during_checkpoint_activation2)),
+ timer:sleep(timer:seconds(Delay)),
+
+ ?match(ok, mnesia:install_fallback(updates_during_checkpoint_activation2)),
+
+ %% Stop and then start mnesia and check table consistency
+ %%?verbose("Restarting Mnesia~n", []),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ file:delete(updates_during_checkpoint_activation2),
+ mnesia_test_lib:start_mnesia(Nodes,[account,branch,teller, history]),
+
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+updates_during_checkpoint_iteration_2_ram(suite) -> [];
+updates_during_checkpoint_iteration_2_ram(Config) when is_list(Config) ->
+ updates_during_checkpoint_iteration(ram_copies, 2, Config).
+
+updates_during_checkpoint_iteration_2_disc(suite) -> [];
+updates_during_checkpoint_iteration_2_disc(Config) when is_list(Config) ->
+ updates_during_checkpoint_iteration(disc_copies, 2, Config).
+
+updates_during_checkpoint_iteration_2_disc_only(suite) -> [];
+updates_during_checkpoint_iteration_2_disc_only(Config) when is_list(Config) ->
+ updates_during_checkpoint_iteration(disc_only_copies, 2, Config).
+
+updates_during_checkpoint_iteration(ReplicaType,NodeConfig,Config) ->
+ %?verbose("updates_during_checkpoint_iteration2 at ~p~n", [self()]),
+ Delay = 5,
+ Nodes = ?acquire_nodes(NodeConfig, Config),
+ Node1 = hd(Nodes),
+ %?verbose("Mnesia info: ~p~n", [mnesia:info()]),
+ File = updates_during_checkpoint_iteration2,
+ {success, [A]} = ?start_activities([Node1]),
+ ?log("updates_during_checkpoint_iteration with ~p on ~p~n",
+ [ReplicaType, Nodes]),
+ TpcbConfig = tpcb_config_dist(ReplicaType, NodeConfig, Nodes, Config),
+ %%TpcbConfig = tpcb_config(ReplicaType, NodeConfig, Nodes),
+ TpcbConfigRec = list2rec(TpcbConfig,
+ record_info(fields,tab_config),
+ #tab_config{}),
+ mnesia_tpcb:init(TpcbConfig),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+ A ! fun () -> mnesia:backup_checkpoint(CPName, File) end,
+
+ do_changes_during_backup(TpcbConfigRec),
+
+ ?match_receive({A,ok}),
+
+ timer:sleep(timer:seconds(Delay)),
+ ?match(ok, mnesia:install_fallback(File)),
+ timer:sleep(timer:seconds(Delay)),
+
+ ?match({error,{"Bad balance",_,_}}, mnesia_tpcb:verify_tabs()),
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ mnesia_test_lib:start_mnesia(Nodes,[account,branch,teller, history]),
+
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+
+ ?match(ok, file:delete(File)),
+ ?verify_mnesia(Nodes, []).
+
+do_changes_during_backup(TpcbConfig) ->
+ loop_branches(TpcbConfig#tab_config.n_branches,
+ TpcbConfig#tab_config.n_accounts_per_branch).
+
+loop_branches(N_br,N_acc) when N_br >= 1 ->
+ loop_accounts(N_br,N_acc),
+ loop_branches(N_br-1,N_acc);
+loop_branches(_,_) -> done.
+
+loop_accounts(N_br, N_acc) when N_acc >= 1 ->
+ A = #account{id=N_acc, branch_id=N_br, balance = 4711},
+ ok = mnesia:dirty_write(A),
+ loop_accounts(N_br, N_acc-1);
+
+loop_accounts(_,_) -> done.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+load_table_with_activated_checkpoint_ram(suite) -> [];
+load_table_with_activated_checkpoint_ram(Config) when is_list(Config) ->
+ load_table_with_activated_checkpoint(ram_copies, Config).
+
+load_table_with_activated_checkpoint_disc(suite) -> [];
+load_table_with_activated_checkpoint_disc(Config) when is_list(Config) ->
+ load_table_with_activated_checkpoint(disc_copies, Config).
+
+load_table_with_activated_checkpoint_disc_only(suite) -> [];
+load_table_with_activated_checkpoint_disc_only(Config) when is_list(Config) ->
+ load_table_with_activated_checkpoint(disc_only_copies, Config).
+
+load_table_with_activated_checkpoint(Type, Config) ->
+ Nodes = ?acquire_nodes(2, Config),
+ Node1 = hd(Nodes),
+ Tab = load_test,
+ Def = [{attributes, [key, value]},
+ {Type, Nodes}], %% ??? important that RAM ???
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ timer:sleep(timer:seconds(1)),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+
+ mnesia_test_lib:stop_mnesia([Node1]),
+ mnesia_test_lib:start_mnesia([Node1],[Tab]),
+ %%--- check, whether the checkpiont is attached to both replicas
+ {success, [A,B]} = ?start_activities(Nodes),
+
+ A ! fun () ->
+ mnesia:table_info(Tab,checkpoints)
+ end,
+ ?match_receive({A,[CPName]}),
+
+ B ! fun () ->
+ mnesia:table_info(Tab,checkpoints)
+ end,
+ ?match_receive({B,[CPName]}),
+
+ %%--- check, whether both retainers are consistent
+ ?match(ok, mnesia:dirty_write({Tab, 1, 815})),
+ A ! fun () ->
+ mnesia:backup_checkpoint(CPName, load_table_a)
+ end,
+ ?match_receive({A,ok}),
+ B ! fun () ->
+ mnesia:backup_checkpoint(CPName, load_table_b)
+ end,
+ ?match_receive({B,ok}),
+
+ Mod = mnesia_backup, %% Assume local files
+ List_a = view(load_table_a, Mod),
+ List_b = view(load_table_b, Mod),
+
+ ?match(List_a, List_b),
+
+ ?match(ok,file:delete(load_table_a)),
+ ?match(ok,file:delete(load_table_b)),
+ ?verify_mnesia(Nodes, []).
+
+view(Source, Mod) ->
+ View = fun(Item, Acc) ->
+ ?verbose("tab - item : ~p ~n",[Item]),
+ case Item of
+ {schema, Tab, Cs} -> %% Remove cookie information
+ NewCs = lists:keyreplace(cookie, 1, Cs,
+ {cookie, skip_cookie}),
+ Item2 = {schema, Tab, NewCs},
+ {[Item], [Item2|Acc]};
+ _ ->
+ {[Item], [Item|Acc]}
+ end
+ end,
+ {ok,TabList} =
+ mnesia:traverse_backup(Source, Mod, dummy, read_only, View, []),
+ lists:sort(TabList).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+add_table_copy_to_table_with_activated_checkpoint_ram(suite) -> [];
+add_table_copy_to_table_with_activated_checkpoint_ram(Config) when is_list(Config) ->
+ add_table_copy_to_table_with_activated_checkpoint(ram_copies, Config).
+
+add_table_copy_to_table_with_activated_checkpoint_disc(suite) -> [];
+add_table_copy_to_table_with_activated_checkpoint_disc(Config) when is_list(Config) ->
+ add_table_copy_to_table_with_activated_checkpoint(disc_copies, Config).
+
+add_table_copy_to_table_with_activated_checkpoint_disc_only(suite) -> [];
+add_table_copy_to_table_with_activated_checkpoint_disc_only(Config) when is_list(Config) ->
+ add_table_copy_to_table_with_activated_checkpoint(disc_only_copies, Config).
+
+add_table_copy_to_table_with_activated_checkpoint(Type,Config) ->
+ Nodes = ?acquire_nodes(2, Config),
+ %?verbose("NODES = ~p ~n",[Nodes]),
+ [Node1,Node2] = Nodes,
+
+ Tab = add_test,
+ Def = [{attributes, [key, value]},
+ {Type, [Node1]}], %% ??? important that RAM ???
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+
+ ?match({atomic,ok},mnesia:add_table_copy(Tab,Node2,ram_copies)),
+
+ %%--- check, whether the checkpiont is attached to both replicas
+ {success, [A,B]} = ?start_activities(Nodes),
+
+ A ! fun () ->
+ mnesia:table_info(Tab,checkpoints)
+ end,
+ ?match_receive({A,[CPName]}),
+
+ B ! fun () ->
+ mnesia:table_info(Tab,checkpoints)
+ end,
+ ?match_receive({B,[CPName]}),
+
+ %%--- check, whether both retainers are consistent
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 815})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 815})),
+
+ A ! fun () ->
+ mnesia:backup_checkpoint(CPName, add_table_a)
+ end,
+ ?match_receive({A,ok}),
+ B ! fun () ->
+ mnesia:backup_checkpoint(CPName, add_table_b)
+ end,
+ ?match_receive({B,ok}),
+
+ Mod = mnesia_backup, %% Assume local files
+
+ List_a = view(add_table_a, Mod),
+ List_b = view(add_table_b, Mod),
+
+ ?match(List_a, List_b),
+
+ ?match(ok,file:delete(add_table_a)),
+ ?match(ok, file:delete(add_table_b)),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+inst_fallback_process_dies(suite) ->
+ [];
+inst_fallback_process_dies(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ {success, [A,_B,_C]} = ?start_activities(Nodes),
+
+ TestPid = self(),
+ DebugId = {mnesia_bup, fallback_receiver_loop, pre_swap},
+ DebugFun =
+ fun(PrevContext, _EvalContext) ->
+ ?verbose("fallback_receiver_loop - pre_swap pid ~p #~p~n",
+ [self(),PrevContext]),
+ TestPid ! {self(),fallback_preswap},
+ case receive_messages([fallback_continue]) of
+ [{TestPid,fallback_continue}] ->
+ ?deactivate_debug_fun(DebugId),
+ PrevContext+1
+ end
+ end,
+ ?activate_debug_fun(DebugId, DebugFun, 1),
+
+ Tab = install_table,
+ Def = [{attributes, [key, value]}, {disc_copies, Nodes}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+
+ ?match(ok, mnesia:backup_checkpoint(CPName, install_backup)),
+
+ A ! fun() -> mnesia:install_fallback(install_backup) end,
+ [{AnsPid,fallback_preswap}] = receive_messages([fallback_preswap]),
+ exit(A, kill),
+ AnsPid ! {self(), fallback_continue},
+ ?match_receive({'EXIT', A, killed}),
+ timer:sleep(2000), %% Wait till fallback is installed everywhere
+
+ mnesia_test_lib:kill_mnesia(Nodes),
+ ?verbose("~n---->Mnesia is stopped everywhere<-----~n", []),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes,[Tab])),
+
+ check_data(Nodes, Tab),
+ ?match(ok, file:delete(install_backup)),
+ ?verify_mnesia(Nodes, []).
+
+check_data([N1 | R], Tab) ->
+ ?match([{Tab, 1, 4711}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 2, 42}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])),
+ ?match([{Tab, 3, 256}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])),
+ check_data(R, Tab);
+check_data([], _Tab) ->
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+fatal_when_inconsistency(suite) ->
+ [];
+fatal_when_inconsistency(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+
+ [Node1, Node2, Node3] = Nodes =
+ ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ {success, [A,_B,_C]} = ?start_activities(Nodes),
+
+ TestPid = self(),
+ DebugId = {mnesia_bup, fallback_receiver_loop, pre_swap},
+ DebugFun =
+ fun(PrevContext, _EvalContext) ->
+ ?verbose("fallback_receiver_loop - pre_swap pid ~p #~p~n",
+ [self(),PrevContext]),
+ TestPid ! {self(),fallback_preswap},
+ case receive_messages([fallback_continue]) of
+ [{TestPid,fallback_continue}] ->
+ ?deactivate_debug_fun(DebugId),
+ PrevContext+1
+ end
+ end,
+ ?activate_debug_fun(DebugId, DebugFun, 1),
+
+ Tab = install_table,
+ Def = [{attributes, [key, value]}, {disc_copies, Nodes}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+
+ ?match(ok, mnesia:backup_checkpoint(CPName, install_backup)),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42424242})),
+
+ A ! fun() ->
+ mnesia:install_fallback(install_backup)
+ end,
+
+ [{AnsPid,fallback_preswap}] = receive_messages([fallback_preswap]),
+ exit(AnsPid, kill), %% Kill install-fallback on local node will
+ AnsPid ! {self(), fallback_continue},
+ ?deactivate_debug_fun(DebugId),
+
+ ?match_receive({A,{error,{"Cannot install fallback",
+ {'EXIT',AnsPid,killed}}}}),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ ?verbose("EXPECTING FATAL from 2 nodes WITH CORE DUMP~n", []),
+
+ ?match([], mnesia_test_lib:start_mnesia([Node1],[])),
+ is_running(Node1, yes),
+ ?match([{Node2, mnesia, _}], mnesia_test_lib:start_mnesia([Node2],[])),
+ is_running(Node2, no),
+ ?match([{Node3, mnesia, _}], mnesia_test_lib:start_mnesia([Node3],[])),
+ is_running(Node3, no),
+ mnesia_test_lib:kill_mnesia(Nodes),
+
+ ?match(ok, mnesia:install_fallback(install_backup)),
+ mnesia_test_lib:start_mnesia(Nodes,[Tab]),
+
+ check_data(Nodes, Tab),
+
+ ?match(ok,file:delete(install_backup)),
+ ?verify_mnesia(Nodes, []).
+
+is_running(Node, Shouldbe) ->
+ timer:sleep(1000),
+ Running = rpc:call(Node, mnesia, system_info, [is_running]),
+ case Running of
+ Shouldbe -> ok;
+ _ -> is_running(Node, Shouldbe)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+after_delete(doc) ->
+ ["interrupt the uninstall after deletion of ",
+ "fallback files - there shall be no fallback"];
+after_delete(suite) -> [];
+after_delete(Config) when is_list(Config) ->
+ do_uninstall(Config, post_delete).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%
+
+do_uninstall(Config,DebugPoint) ->
+ ?is_debug_compiled,
+
+ Nodes = ?acquire_nodes(3, Config),
+ %%?verbose("NODES = ~p ~n",[Nodes]),
+
+ {success, [P1,P2,P3]} = ?start_activities(Nodes),
+
+ NP1 = node(P1),
+ NP2 = node(P2),
+
+ {A,B,C} = case node() of
+ NP1 ->
+ %%?verbose("first case ~n"),
+ {P3,P2,P1};
+ NP2 ->
+ %%?verbose("second case ~n"),
+ {P3, P1, P2};
+ _ ->
+ { P1, P2, P3}
+ end,
+
+ Node1 = node(A),
+ Node2 = node(B),
+ Node3 = node(C),
+
+ ?verbose(" A pid:~p node:~p ~n",[A,Node1]),
+ ?verbose(" B pid:~p node:~p ~n",[B,Node2]),
+ ?verbose(" C pid:~p node:~p ~n",[C,Node3]),
+
+
+ TestPid = self(),
+ %%?verbose("TestPid : ~p~n",[TestPid]),
+ DebugId = {mnesia_bup, uninstall_fallback2, DebugPoint},
+ DebugFun = fun(PrevContext, _EvalContext) ->
+ ?verbose("uninstall_fallback pid ~p #~p~n"
+ ,[self(),PrevContext]),
+ TestPid ! {self(),uninstall_predelete},
+ case receive_messages([uninstall_continue]) of
+ [{TestPid,uninstall_continue}] ->
+ ?deactivate_debug_fun(DebugId),
+ %%?verbose("uninstall_fallback continues~n"),
+ PrevContext+1
+ end
+ end,
+ ?remote_activate_debug_fun(Node1,DebugId, DebugFun, 1),
+
+ Tab = install_table,
+ Def = [{attributes, [key, value]},
+ {ram_copies, Nodes}], %% necessary to test different types ???
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+
+ ?match(ok, mnesia:backup_checkpoint(CPName,install_backup)),
+ timer:sleep(timer:seconds(1)),
+
+ A ! fun () ->
+ mnesia:install_fallback(install_backup)
+ end,
+ ?match_receive({A,ok}),
+
+ A ! fun () ->
+ mnesia:uninstall_fallback()
+ end,
+ %%
+ %% catch the debug entry in mnesia and kill one Mnesia node
+ %%
+
+
+ [{AnsPid,uninstall_predelete}] = receive_messages([uninstall_predelete]),
+
+ ?verbose("AnsPid : ~p~n",[AnsPid]),
+
+ mnesia_test_lib:kill_mnesia([Node2]),
+ timer:sleep(timer:seconds(1)),
+
+ AnsPid ! {self(),uninstall_continue},
+
+ ?match_receive({A,ok}),
+
+ mnesia_test_lib:kill_mnesia(Nodes) ,
+ mnesia_test_lib:start_mnesia(Nodes,[Tab]),
+
+ A ! fun () ->
+ R1 = mnesia:dirty_read({Tab,1}),
+ R2 = mnesia:dirty_read({Tab,2}),
+ R3 = mnesia:dirty_read({Tab,3}),
+ {R1,R2,R3}
+ end,
+ ?match_receive({ A, {[],[],[]} }),
+
+ B ! fun () ->
+ R1 = mnesia:dirty_read({Tab,1}),
+ R2 = mnesia:dirty_read({Tab,2}),
+ R3 = mnesia:dirty_read({Tab,3}),
+ {R1,R2,R3}
+ end,
+ ?match_receive({ B, {[],[],[]} }),
+
+ C ! fun () ->
+ R1 = mnesia:dirty_read({Tab,1}),
+ R2 = mnesia:dirty_read({Tab,2}),
+ R3 = mnesia:dirty_read({Tab,3}),
+ {R1,R2,R3}
+ end,
+ ?match_receive({ C, {[],[],[]} }),
+
+ ?match(ok,file:delete(install_backup)),
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%
+
+cause_switch_before(doc) ->
+ ["interrupt the backup before iterating the retainer"];
+cause_switch_before(suite) -> [];
+cause_switch_before(Config) when is_list(Config) ->
+ do_something_during_backup(cause_switch,pre,Config).
+
+%%%%%%%%%%%%%%%
+
+cause_switch_after(doc) ->
+ ["interrupt the backup after iterating the retainer"];
+cause_switch_after(suite) -> [];
+cause_switch_after(Config) when is_list(Config) ->
+ do_something_during_backup(cause_switch,post,Config).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%
+
+cause_abort_before(doc) ->
+ ["interrupt the backup before iterating the retainer"];
+
+cause_abort_before(suite) -> [];
+cause_abort_before(Config) when is_list(Config) ->
+ do_something_during_backup(cause_abort,pre,Config).
+
+%%%%%%%%%%%%%%%%%%
+
+cause_abort_after(doc) ->
+ ["interrupt the backup after iterating the retainer"];
+
+cause_abort_after(suite) -> [];
+cause_abort_after(Config) when is_list(Config) ->
+ do_something_during_backup(cause_abort,post,Config).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%
+
+change_schema_before(doc) ->
+ ["interrupt the backup before iterating the retainer"];
+change_schema_before(suite) -> [];
+change_schema_before(Config) when is_list(Config) ->
+ do_something_during_backup(change_schema,pre,Config).
+
+%%%%%%%%%%%%%%%%
+
+change_schema_after(doc) ->
+ ["interrupt the backup after iterating the retainer"];
+change_schema_after(suite) -> [];
+change_schema_after(Config) when is_list(Config) ->
+ do_something_during_backup(change_schema,post,Config).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+do_something_during_backup(Action,DebugPoint,Config) ->
+ ?is_debug_compiled,
+
+ Nodes = ?acquire_nodes(3, Config),
+
+ {success, [A,B,C]} = ?start_activities(Nodes),
+
+ Node1 = node(A),
+ Node2 = node(B),
+ Node3 = node(C),
+
+ TestPid = self(),
+ %%?verbose("TestPid : ~p~n",[TestPid]),
+
+ Tab = interrupt_table,
+ Bak = interrupt_backup,
+ Def = [{attributes, [key, value]},
+ {ram_copies, [Node2,Node3]}],
+ %% necessary to test different types ???
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+
+
+ DebugId = {mnesia_log, tab_copier, DebugPoint},
+ DebugFun = fun(PrevContext, EvalContext) ->
+ ?verbose("interrupt backup pid ~p #~p ~n context ~p ~n"
+ ,[self(),PrevContext,EvalContext]),
+ TestPid ! {self(),interrupt_backup_pre},
+ global:set_lock({{lock_for_backup, Tab}, self()},
+ Nodes,
+ infinity),
+
+ %%?verbose("interrupt backup - continues ~n"),
+ ?deactivate_debug_fun(DebugId),
+ PrevContext+1
+ end,
+ ?remote_activate_debug_fun(Node1,DebugId, DebugFun, 1),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ {ok, CPName, _NodeList} =
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump,true}]),
+
+ A ! fun () ->
+ %%?verbose("node: ~p pid: ~p ~n",[node(),self()]),
+ mnesia:table_info(Tab,where_to_read)
+ end,
+
+ ReadNode_a = receive { A, ReadNode_a_tmp } -> ReadNode_a_tmp end,
+ ?verbose("ReadNode ~p ~n",[ReadNode_a]),
+
+ global:set_lock({{lock_for_backup, Tab}, self()}, Nodes, infinity),
+
+ A ! fun () -> %% A shall perform the backup, so the test proc is
+ %% able to do further actions in between
+ mnesia:backup_checkpoint(CPName, Bak)
+ end,
+
+ %% catch the debug function of mnesia, stop the backup process
+ %% kill the node ReadNode_a and continue the backup process
+ %% As there is a second replica of the table, the backup shall continue
+
+ case receive_messages([interrupt_backup_pre]) of
+ [{_AnsPid,interrupt_backup_pre}] -> ok
+ end,
+
+ case Action of
+ cause_switch ->
+ mnesia_test_lib:kill_mnesia([ReadNode_a]),
+ timer:sleep(timer:seconds(1));
+ cause_abort ->
+ mnesia_test_lib:kill_mnesia([Node2,Node3]),
+ timer:sleep(timer:seconds(1));
+ change_schema ->
+ Tab2 = second_interrupt_table,
+ Def2 = [{attributes, [key, value]},
+ {ram_copies, Nodes}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2))
+ end,
+
+ %% AnsPid ! {self(),interrupt_backup_continue},
+ global:del_lock({{lock_for_backup, Tab}, self()}, Nodes),
+
+ case Action of
+ cause_abort ->
+
+ %% answer of A when finishing the backup
+ ?match_receive({A,{error, _}}),
+
+ ?match({error,{"Cannot install fallback",_}},
+ mnesia:install_fallback(Bak));
+ _ -> %% cause_switch, change_schema
+
+ ?match_receive({A,ok}), %% answer of A when finishing the backup
+
+ %% send a fun to that node where mnesia is still running
+ WritePid = case ReadNode_a of
+ Node2 -> C; %% node(C) == Node3
+ Node3 -> B
+ end,
+ WritePid ! fun () ->
+ ?match(ok, mnesia:dirty_write({Tab, 1, 815})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 816})),
+ ok
+ end,
+ ?match_receive({ WritePid, ok }),
+ ?match(ok, mnesia:install_fallback(Bak))
+ end,
+
+ %% Stop and then start mnesia and check table consistency
+ %%?verbose("Restarting Mnesia~n", []),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ mnesia_test_lib:start_mnesia(Nodes,[Tab]),
+
+ case Action of
+ cause_switch ->
+ %% the backup should exist
+ cross_check_tables([A,B,C],Tab,{[{Tab,1,4711}],
+ [{Tab,2,42}],
+ [{Tab,3,256}] }),
+ ?match(ok,file:delete(Bak));
+ cause_abort ->
+ %% the backup should NOT exist
+ cross_check_tables([A,B,C],Tab,{[],[],[]}),
+ %% file does not exist
+ ?match({error, _},file:delete(Bak));
+ change_schema ->
+ %% the backup should exist
+ cross_check_tables([A,B,C],Tab,{[{Tab,1,4711}],
+ [{Tab,2,42}],
+ [{Tab,3,256}] }),
+ ?match(ok,file:delete(Bak))
+ end,
+ ?verify_mnesia(Nodes, []).
+
+%% check the contents of the table
+cross_check_tables([],_tab,_elements) -> ok;
+cross_check_tables([Pid|Rest],Tab,{Val1,Val2,Val3}) ->
+ Pid ! fun () ->
+ R1 = mnesia:dirty_read({Tab,1}),
+ R2 = mnesia:dirty_read({Tab,2}),
+ R3 = mnesia:dirty_read({Tab,3}),
+ {R1,R2,R3}
+ end,
+ ?match_receive({ Pid, {Val1, Val2, Val3 } }),
+ cross_check_tables(Rest,Tab,{Val1,Val2,Val3} ).
diff --git a/lib/mnesia/test/mnesia_cost.erl b/lib/mnesia/test/mnesia_cost.erl
new file mode 100644
index 0000000000..3221f46f61
--- /dev/null
+++ b/lib/mnesia/test/mnesia_cost.erl
@@ -0,0 +1,222 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_cost).
+-compile(export_all).
+
+%% This code exercises the mnesia system and produces a bunch
+%% of measurements on what various things cost
+
+-define(TIMES, 1000). %% set to at least 1000 when running for real !!
+
+%% This is the record we perform all ops on in this test
+
+-record(item, {a = 1234,
+ b = foobar,
+ c = "1.2.3.4",
+ d = {'Lennart', 'Hyland'},
+ e = true
+ }).
+
+go() ->
+ go([node() | nodes()]).
+
+go(Nodes) when hd(Nodes) == node() ->
+ {ok, Out} = file:open("MNESIA_COST", write),
+ put(out, Out),
+
+ rpc:multicall(Nodes, mnesia, lkill, []),
+ ok = mnesia:delete_schema(Nodes),
+ ok = mnesia:create_schema(Nodes),
+ rpc:multicall(Nodes, mnesia, start, []),
+ TabDef = [{attributes, record_info(fields, item)}],
+ {atomic, ok} = mnesia:create_table(item, TabDef),
+
+ round("single ram copy", "no index"),
+ {atomic, ok} = mnesia:add_table_index(item, #item.e),
+ round("single ram copy", "One index"),
+
+ {atomic, ok} = mnesia:add_table_index(item, #item.c),
+ round("single ram copy", "Two indexes"),
+
+ {atomic, ok} = mnesia:del_table_index(item, #item.e),
+ {atomic, ok} = mnesia:del_table_index(item, #item.c),
+
+ {atomic, ok} = mnesia:change_table_copy_type(item, node(), disc_copies),
+ round("single disc copy", "no index"),
+
+ {atomic, ok} = mnesia:change_table_copy_type(item, node(), ram_copies),
+
+ case length(Nodes) of
+ Len when Len < 2 ->
+ format("<WARNING> replication skipped. Too few nodes.", []);
+ _Len ->
+ N2 = lists:nth(2, Nodes),
+ {atomic, ok} = mnesia:add_table_copy(item, N2, ram_copies),
+ round("2 replicated ram copy", "no index")
+ end,
+ file:close(Out),
+ erase(out),
+ ok.
+
+round(Replication, Index) ->
+ run(Replication, Index, [write],
+ fun() -> mnesia:write(#item{}) end),
+
+
+ run(Replication, Index, [read],
+ fun() -> mnesia:read({item, 1234}) end),
+
+ run(Replication, Index, [read, write],
+ fun() -> mnesia:read({item, 1234}),
+ mnesia:write(#item{}) end),
+
+ run(Replication, Index, [wread, write],
+ fun() -> mnesia:wread({item, 1234}),
+ mnesia:write(#item{}) end),
+
+
+ run(Replication, Index, [match, write, write, write],
+ fun() -> mnesia:match_object({item, 1, '_', '_', '_', true}),
+ mnesia:write(#item{a =1}),
+ mnesia:write(#item{a =2}),
+ mnesia:write(#item{a =3}) end).
+
+
+format(F, As) ->
+ io:format(get(out), F, As).
+
+run(What, OtherInfo, Ops, F) ->
+ run(t, What, OtherInfo, Ops, F).
+
+run(How, What, OtherInfo, Ops, F) ->
+ T1 = erlang:now(),
+ statistics(runtime),
+ do_times(How, ?TIMES, F),
+ {_, RunTime} = statistics(runtime),
+ T2 = erlang:now(),
+ RealTime = subtr(T1, T2),
+ report(How, What, OtherInfo, Ops, RunTime, RealTime).
+
+report(t, What, OtherInfo, Ops, RunTime, RealTime) ->
+ format("~s, ~s, transaction call ", [What, OtherInfo]),
+ format("Ops is ", []),
+ lists:foreach(fun(Op) -> format("~w-", [Op]) end, Ops),
+
+ format("~n ~w/~w Millisecs/Trans ~w/~w MilliSecs/Operation ~n~n",
+ [RunTime/?TIMES,
+ RealTime/?TIMES,
+ RunTime/(?TIMES*length(Ops)),
+ RealTime/(?TIMES*length(Ops))]);
+
+report(dirty, What, OtherInfo, Ops, RunTime, RealTime) ->
+ format("~s, ~s, dirty calls ", [What, OtherInfo]),
+ format("Ops is ", []),
+ lists:foreach(fun(Op) -> format("~w-", [Op]) end, Ops),
+
+ format("~n ~w/~w Millisecs/Bunch ~w/~w MilliSecs/Operation ~n~n",
+ [RunTime/?TIMES,
+ RealTime/?TIMES,
+ RunTime/(?TIMES*length(Ops)),
+ RealTime/(?TIMES*length(Ops))]).
+
+
+subtr(Before, After) ->
+ E =(element(1,After)*1000000000000
+ +element(2,After)*1000000+element(3,After)) -
+ (element(1,Before)*1000000000000
+ +element(2,Before)*1000000+element(3,Before)),
+ E div 1000.
+
+do_times(t, I, F) ->
+ do_trans_times(I, F);
+do_times(dirty, I, F) ->
+ do_dirty(I, F).
+
+do_trans_times(I, F) when I /= 0 ->
+ {atomic, _} = mnesia:transaction(F),
+ do_trans_times(I-1, F);
+do_trans_times(_,_) -> ok.
+
+do_dirty(I, F) when I /= 0 ->
+ F(),
+ do_dirty(I-1, F);
+do_dirty(_,_) -> ok.
+
+
+
+table_load([N1,N2| _ ] = Ns) ->
+ Nodes = [N1,N2],
+ rpc:multicall(Ns, mnesia, lkill, []),
+ ok = mnesia:delete_schema(Ns),
+ ok = mnesia:create_schema(Nodes),
+ rpc:multicall(Nodes, mnesia, start, []),
+ TabDef = [{disc_copies,[N1]},{ram_copies,[N2]},
+ {attributes,record_info(fields,item)},{record_name,item}],
+ Tabs = [list_to_atom("tab" ++ integer_to_list(I)) || I <- lists:seq(1,400)],
+
+ [mnesia:create_table(Tab,TabDef) || Tab <- Tabs],
+
+%% InitTab = fun(Tab) ->
+%% mnesia:write_lock_table(Tab),
+%% InitRec = fun(Key) -> mnesia:write(Tab,#item{a=Key},write) end,
+%% lists:foreach(InitRec, lists:seq(1,100))
+%% end,
+%%
+%% {Time,{atomic,ok}} = timer:tc(mnesia,transaction, [fun() ->lists:foreach(InitTab, Tabs) end]),
+ mnesia:dump_log(),
+%% io:format("Init took ~p msec ~n", [Time/1000]),
+ rpc:call(N2, mnesia, stop, []), timer:sleep(1000),
+ mnesia:stop(), timer:sleep(500),
+ %% Warmup
+ ok = mnesia:start([{no_table_loaders, 1}]),
+ timer:tc(mnesia, wait_for_tables, [Tabs, infinity]),
+ mnesia:dump_log(),
+ rpc:call(N2, mnesia, dump_log, []),
+ io:format("Initialized ~n",[]),
+
+ mnesia:stop(), timer:sleep(1000),
+ ok = mnesia:start([{no_table_loaders, 1}]),
+ {T1, ok} = timer:tc(mnesia, wait_for_tables, [Tabs, infinity]),
+ io:format("Loading from disc with 1 loader ~p msec~n",[T1/1000]),
+ mnesia:stop(), timer:sleep(1000),
+ ok = mnesia:start([{no_table_loaders, 4}]),
+ {T2, ok} = timer:tc(mnesia, wait_for_tables, [Tabs, infinity]),
+ io:format("Loading from disc with 4 loader ~p msec~n",[T2/1000]),
+
+ %% Warmup
+ rpc:call(N2, ?MODULE, remote_load, [Tabs,4]),
+ io:format("Initialized ~n",[]),
+
+
+ T3 = rpc:call(N2, ?MODULE, remote_load, [Tabs,1]),
+ io:format("Loading from net with 1 loader ~p msec~n",[T3/1000]),
+
+ T4 = rpc:call(N2, ?MODULE, remote_load, [Tabs,4]),
+ io:format("Loading from net with 4 loader ~p msec~n",[T4/1000]),
+
+ ok.
+
+remote_load(Tabs,Loaders) ->
+ ok = mnesia:start([{no_table_loaders, Loaders}]),
+%% io:format("~p ~n", [mnesia_controller:get_info(500)]),
+ {Time, ok} = timer:tc(mnesia, wait_for_tables, [Tabs, infinity]),
+ timer:sleep(1000), mnesia:stop(), timer:sleep(1000),
+ Time.
diff --git a/lib/mnesia/test/mnesia_dbn_meters.erl b/lib/mnesia/test/mnesia_dbn_meters.erl
new file mode 100644
index 0000000000..feaf90ee75
--- /dev/null
+++ b/lib/mnesia/test/mnesia_dbn_meters.erl
@@ -0,0 +1,242 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(mnesia_dbn_meters).
+-export([
+ start/0,
+ local_start/0,
+ distr_start/1,
+ start/3
+ ]).
+
+-record(simple,{key,val=0}).
+-define(key,1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Configuration and start
+
+start() ->
+ local_start(),
+ distr_start(nodes()).
+
+local_start() ->
+ start(one_ram_only,[node()],some_meters()),
+ start(one_disc_only,[node()],some_meters()).
+
+distr_start([]) ->
+ local_only;
+distr_start(OtherNodes) when is_list(OtherNodes) ->
+ start(ram_and_ram,[node()|OtherNodes],some_meters()),
+ start(disc_and_disc,[node()|OtherNodes],some_meters()).
+
+start(Config,Nodes,Meters) ->
+ Attrs = record_info(fields,simple),
+ Schema = [{name,simple},{type,set},{attributes,Attrs}] ++ config(Config,Nodes),
+ L = '====================',
+ io:format("~n~p dbn_meters: ~p ~p~nSchema = ~p.~n~n",[L,Config,L,Schema]),
+ ok = mnesia:delete_schema(Nodes),
+ ok = mnesia:create_schema(Nodes),
+ rpc:multicall(Nodes, mnesia, start, []),
+ {atomic,_} = mnesia:create_table(Schema),
+ lists:foreach(fun report_meter/1,Meters),
+ {atomic, ok} = mnesia:delete_table(simple),
+ rpc:multicall(Nodes, mnesia, stop, []),
+ ok.
+
+config(one_ram_only,[Single|_]) ->
+ [{ram_copies,[Single]}];
+config(ram_and_ram,[Master|[Slave|_]]) ->
+ [{ram_copies,[Master,Slave]}];
+config(one_disc_only,[Single|_]) ->
+ [{disc_copies,[Single]}];
+config(disc_and_disc,[Master|[Slave|_]]) ->
+ [{disc_copies,[Master,Slave]}];
+config(Config,Nodes) ->
+ io:format("<ERROR> Config ~p not supported or too few nodes ~p given~n",[Config,Nodes]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% The various DBN meters
+some_meters() ->
+ [create,
+ open_safe_read,
+ open_dirty_read,
+ get_int,
+ open_update,
+ put_int,
+ put_int_and_copy,
+ dirty_put_int_and_copy,
+ start_trans,
+ commit_one_update,
+ delete,
+ dirty_delete
+ ].
+
+report_meter(Meter) ->
+ Times = 100,
+ Micros = repeat_meter(Meter,{atomic,{0,ignore}},Times) div Times,
+ io:format("\t~-30w ~-10w micro seconds (mean of ~p repetitions)~n",[Meter,Micros,Times]).
+
+repeat_meter(_Meter,{atomic,{Micros,_Result}},0) ->
+ Micros;
+repeat_meter(Meter,{atomic,{Micros,_Result}},Times) when Times > 0 ->
+ repeat_meter(Meter,catch meter(Meter),Times-1) + Micros;
+repeat_meter(Meter,{aborted,Reason},Times) when Times > 0 ->
+ io:format("<ERROR>\t~-20w\t,aborted, because ~p~n",[Meter,Reason]),
+ 0;
+repeat_meter(Meter,{'EXIT',Reason},Times) when Times > 0 ->
+ io:format("<ERROR>\t~-20w\tcrashed, because ~p~n",[Meter,Reason]),
+ 0.
+
+meter(create) ->
+ Key = 1,
+ mnesia:transaction(fun() -> mnesia:delete({simple,Key}) end),
+ Fun = fun() ->
+ BeforeT = erlang:now(),
+ R = mnesia:write(#simple{key=Key}),
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(open_safe_read) ->
+ Key = 2,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ BeforeT = erlang:now(),
+ R = mnesia:read({simple,Key}),
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(open_dirty_read) ->
+ Key = 21,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ BeforeT = erlang:now(),
+ R = mnesia:dirty_read({simple,Key}),
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(get_int) ->
+ Key = 3,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ [Simple] = mnesia:read({simple,Key}),
+ BeforeT = erlang:now(),
+ Int = Simple#simple.val,
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,Int)
+ end,
+ mnesia:transaction(Fun);
+
+meter(open_update) ->
+ Key = 3,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ BeforeT = erlang:now(),
+ R = mnesia:wread({simple,Key}),
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(put_int) ->
+ Key = 4,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ [Simple] = mnesia:wread({simple,Key}),
+ BeforeT = erlang:now(),
+ R = Simple#simple{val=7},
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(put_int_and_copy) ->
+ Key = 5,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ [Simple] = mnesia:wread({simple,Key}),
+ BeforeT = erlang:now(),
+ Simple2 = Simple#simple{val=17},
+ R = mnesia:write(Simple2),
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(dirty_put_int_and_copy) ->
+ Key = 55,
+ mnesia:dirty_write(#simple{key=Key}),
+ [Simple] = mnesia:dirty_read({simple,Key}),
+ BeforeT = erlang:now(),
+ Simple2 = Simple#simple{val=17},
+ R = mnesia:dirty_write(Simple2),
+ AfterT = erlang:now(),
+ {atomic,elapsed_time(BeforeT,AfterT,R)};
+
+meter(start_trans) ->
+ BeforeT = erlang:now(),
+ {atomic,AfterT} = mnesia:transaction(fun() -> erlang:now() end),
+ {atomic,elapsed_time(BeforeT,AfterT,ok)};
+
+meter(commit_one_update) ->
+ Key = 6,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ [Simple] = mnesia:wread({simple,Key}),
+ Simple2 = Simple#simple{val=27},
+ _R = mnesia:write(Simple2),
+ erlang:now()
+ end,
+ {atomic,BeforeT} = mnesia:transaction(Fun),
+ AfterT = erlang:now(),
+ {atomic,elapsed_time(BeforeT,AfterT,ok)};
+
+meter(delete) ->
+ Key = 7,
+ mnesia:transaction(fun() -> mnesia:write(#simple{key=Key}) end),
+ Fun = fun() ->
+ BeforeT = erlang:now(),
+ R = mnesia:delete({simple,Key}),
+ AfterT = erlang:now(),
+ elapsed_time(BeforeT,AfterT,R)
+ end,
+ mnesia:transaction(Fun);
+
+meter(dirty_delete) ->
+ Key = 75,
+ mnesia:dirty_write(#simple{key=Key}),
+ BeforeT = erlang:now(),
+ R = mnesia:dirty_delete({simple,Key}),
+ AfterT = erlang:now(),
+ {atomic, elapsed_time(BeforeT,AfterT,R)}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Calculate the elapsed time
+elapsed_time(BeforeT,AfterT,Result) ->
+ {(element(1,AfterT)*1000000000000
+ +element(2,AfterT)*1000000+element(3,AfterT)) -
+ (element(1,BeforeT)*1000000000000
+ +element(2,BeforeT)*1000000+element(3,BeforeT)),Result}.
diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl
new file mode 100644
index 0000000000..abbdab48c0
--- /dev/null
+++ b/lib/mnesia/test/mnesia_dirty_access_test.erl
@@ -0,0 +1,880 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_dirty_access_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [{group, dirty_write}, {group, dirty_read},
+ {group, dirty_update_counter}, {group, dirty_delete},
+ {group, dirty_delete_object},
+ {group, dirty_match_object}, {group, dirty_index},
+ {group, dirty_iter}, {group, admin_tests}].
+
+groups() ->
+ [{dirty_write, [],
+ [dirty_write_ram, dirty_write_disc,
+ dirty_write_disc_only]},
+ {dirty_read, [],
+ [dirty_read_ram, dirty_read_disc,
+ dirty_read_disc_only]},
+ {dirty_update_counter, [],
+ [dirty_update_counter_ram, dirty_update_counter_disc,
+ dirty_update_counter_disc_only]},
+ {dirty_delete, [],
+ [dirty_delete_ram, dirty_delete_disc,
+ dirty_delete_disc_only]},
+ {dirty_delete_object, [],
+ [dirty_delete_object_ram, dirty_delete_object_disc,
+ dirty_delete_object_disc_only]},
+ {dirty_match_object, [],
+ [dirty_match_object_ram, dirty_match_object_disc,
+ dirty_match_object_disc_only]},
+ {dirty_index, [],
+ [{group, dirty_index_match_object},
+ {group, dirty_index_read},
+ {group, dirty_index_update}]},
+ {dirty_index_match_object, [],
+ [dirty_index_match_object_ram,
+ dirty_index_match_object_disc,
+ dirty_index_match_object_disc_only]},
+ {dirty_index_read, [],
+ [dirty_index_read_ram, dirty_index_read_disc,
+ dirty_index_read_disc_only]},
+ {dirty_index_update, [],
+ [dirty_index_update_set_ram,
+ dirty_index_update_set_disc,
+ dirty_index_update_set_disc_only,
+ dirty_index_update_bag_ram, dirty_index_update_bag_disc,
+ dirty_index_update_bag_disc_only]},
+ {dirty_iter, [],
+ [dirty_iter_ram, dirty_iter_disc,
+ dirty_iter_disc_only]},
+ {admin_tests, [],
+ [del_table_copy_1, del_table_copy_2, del_table_copy_3,
+ add_table_copy_1, add_table_copy_2, add_table_copy_3,
+ add_table_copy_4, move_table_copy_1, move_table_copy_2,
+ move_table_copy_3, move_table_copy_4]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Write records dirty
+
+
+dirty_write_ram(suite) -> [];
+dirty_write_ram(Config) when is_list(Config) ->
+ dirty_write(Config, ram_copies).
+
+dirty_write_disc(suite) -> [];
+dirty_write_disc(Config) when is_list(Config) ->
+ dirty_write(Config, disc_copies).
+
+dirty_write_disc_only(suite) -> [];
+dirty_write_disc_only(Config) when is_list(Config) ->
+ dirty_write(Config, disc_only_copies).
+
+dirty_write(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_write,
+ Def = [{attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match({'EXIT', _}, mnesia:dirty_write([])),
+ ?match({'EXIT', _}, mnesia:dirty_write({Tab, 2})),
+ ?match({'EXIT', _}, mnesia:dirty_write({foo, 2})),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() ->
+ mnesia:dirty_write({Tab, 1, 2}) end)),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Read records dirty
+
+
+dirty_read_ram(suite) -> [];
+dirty_read_ram(Config) when is_list(Config) ->
+ dirty_read(Config, ram_copies).
+
+dirty_read_disc(suite) -> [];
+dirty_read_disc(Config) when is_list(Config) ->
+ dirty_read(Config, disc_copies).
+
+dirty_read_disc_only(suite) -> [];
+dirty_read_disc_only(Config) when is_list(Config) ->
+ dirty_read(Config, disc_only_copies).
+
+dirty_read(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_read,
+ Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match({'EXIT', _}, mnesia:dirty_read([])),
+ ?match({'EXIT', _}, mnesia:dirty_read({Tab})),
+ ?match({'EXIT', _}, mnesia:dirty_read({Tab, 1, 2})),
+ ?match([], mnesia:dirty_read({Tab, 1})),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+ ?match([{Tab, 1, 2}], mnesia:dirty_read({Tab, 1})),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 3})),
+ ?match([{Tab, 1, 2}, {Tab, 1, 3}], mnesia:dirty_read({Tab, 1})),
+
+ ?match({atomic, [{Tab, 1, 2}, {Tab, 1, 3}]},
+ mnesia:transaction(fun() -> mnesia:dirty_read({Tab, 1}) end)),
+
+ ?match(false, mnesia:async_dirty(fun() -> mnesia:is_transaction() end)),
+ ?match(false, mnesia:sync_dirty(fun() -> mnesia:is_transaction() end)),
+ ?match(false, mnesia:ets(fun() -> mnesia:is_transaction() end)),
+ ?match(false, mnesia:activity(async_dirty, fun() -> mnesia:is_transaction() end)),
+ ?match(false, mnesia:activity(sync_dirty, fun() -> mnesia:is_transaction() end)),
+ ?match(false, mnesia:activity(ets, fun() -> mnesia:is_transaction() end)),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Update counter record dirty
+
+
+dirty_update_counter_ram(suite) -> [];
+dirty_update_counter_ram(Config) when is_list(Config) ->
+ dirty_update_counter(Config, ram_copies).
+
+dirty_update_counter_disc(suite) -> [];
+dirty_update_counter_disc(Config) when is_list(Config) ->
+ dirty_update_counter(Config, disc_copies).
+
+dirty_update_counter_disc_only(suite) -> [];
+dirty_update_counter_disc_only(Config) when is_list(Config) ->
+ dirty_update_counter(Config, disc_only_copies).
+
+dirty_update_counter(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_update_counter,
+ Def = [{attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+
+ ?match({'EXIT', _}, mnesia:dirty_update_counter({Tab, 1}, [])),
+ ?match({'EXIT', _}, mnesia:dirty_update_counter({Tab}, 3)),
+ ?match({'EXIT', _}, mnesia:dirty_update_counter({foo, 1}, 3)),
+ ?match(5, mnesia:dirty_update_counter({Tab, 1}, 3)),
+ ?match([{Tab, 1, 5}], mnesia:dirty_read({Tab, 1})),
+
+ ?match({atomic, 8}, mnesia:transaction(fun() ->
+ mnesia:dirty_update_counter({Tab, 1}, 3) end)),
+
+ ?match(1, mnesia:dirty_update_counter({Tab, foo}, 1)),
+ ?match([{Tab, foo,1}], mnesia:dirty_read({Tab,foo})),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Delete record dirty
+
+
+dirty_delete_ram(suite) -> [];
+dirty_delete_ram(Config) when is_list(Config) ->
+ dirty_delete(Config, ram_copies).
+
+dirty_delete_disc(suite) -> [];
+dirty_delete_disc(Config) when is_list(Config) ->
+ dirty_delete(Config, disc_copies).
+
+dirty_delete_disc_only(suite) -> [];
+dirty_delete_disc_only(Config) when is_list(Config) ->
+ dirty_delete(Config, disc_only_copies).
+
+dirty_delete(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_delete,
+ Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match({'EXIT', _}, mnesia:dirty_delete([])),
+ ?match({'EXIT', _}, mnesia:dirty_delete({Tab})),
+ ?match({'EXIT', _}, mnesia:dirty_delete({Tab, 1, 2})),
+ ?match(ok, mnesia:dirty_delete({Tab, 1})),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+ ?match(ok, mnesia:dirty_delete({Tab, 1})),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+ ?match(ok, mnesia:dirty_delete({Tab, 1})),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 2})),
+ ?match({atomic, ok}, mnesia:transaction(fun() ->
+ mnesia:dirty_delete({Tab, 1}) end)),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Delete matching record dirty
+
+
+dirty_delete_object_ram(suite) -> [];
+dirty_delete_object_ram(Config) when is_list(Config) ->
+ dirty_delete_object(Config, ram_copies).
+
+dirty_delete_object_disc(suite) -> [];
+dirty_delete_object_disc(Config) when is_list(Config) ->
+ dirty_delete_object(Config, disc_copies).
+
+dirty_delete_object_disc_only(suite) -> [];
+dirty_delete_object_disc_only(Config) when is_list(Config) ->
+ dirty_delete_object(Config, disc_only_copies).
+
+dirty_delete_object(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_delete_object,
+ Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ OneRec = {Tab, 1, 2},
+ ?match({'EXIT', _}, mnesia:dirty_delete_object([])),
+ ?match({'EXIT', _}, mnesia:dirty_delete_object({Tab})),
+ ?match({'EXIT', _}, mnesia:dirty_delete_object({Tab, 1})),
+ ?match(ok, mnesia:dirty_delete_object(OneRec)),
+ ?match(ok, mnesia:dirty_write(OneRec)),
+ ?match(ok, mnesia:dirty_delete_object(OneRec)),
+ ?match(ok, mnesia:dirty_write(OneRec)),
+ ?match(ok, mnesia:dirty_write(OneRec)),
+ ?match(ok, mnesia:dirty_delete_object(OneRec)),
+
+ ?match(ok, mnesia:dirty_write(OneRec)),
+ ?match({atomic, ok}, mnesia:transaction(fun() ->
+ mnesia:dirty_delete_object(OneRec) end)),
+
+ ?match({'EXIT', {aborted, {bad_type, Tab, _}}}, mnesia:dirty_delete_object(Tab, {Tab, {['_']}, 21})),
+ ?match({'EXIT', {aborted, {bad_type, Tab, _}}}, mnesia:dirty_delete_object(Tab, {Tab, {['$5']}, 21})),
+
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Read matching records dirty
+
+
+dirty_match_object_ram(suite) -> [];
+dirty_match_object_ram(Config) when is_list(Config) ->
+ dirty_match_object(Config, ram_copies).
+
+dirty_match_object_disc(suite) -> [];
+dirty_match_object_disc(Config) when is_list(Config) ->
+ dirty_match_object(Config, disc_copies).
+
+dirty_match_object_disc_only(suite) -> [];
+dirty_match_object_disc_only(Config) when is_list(Config) ->
+ dirty_match_object(Config, disc_only_copies).
+
+dirty_match_object(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_match,
+ Def = [{attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ OneRec = {Tab, 1, 2},
+ OnePat = {Tab, '$1', 2},
+ ?match([], mnesia:dirty_match_object(OnePat)),
+ ?match(ok, mnesia:dirty_write(OneRec)),
+ ?match([OneRec], mnesia:dirty_match_object(OnePat)),
+ ?match({atomic, [OneRec]}, mnesia:transaction(fun() ->
+ mnesia:dirty_match_object(OnePat) end)),
+
+ ?match({'EXIT', _}, mnesia:dirty_match_object({foo, '$1', 2})),
+ ?match({'EXIT', _}, mnesia:dirty_match_object({[], '$1', 2})),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Dirty read matching records by using an index
+
+
+dirty_index_match_object_ram(suite) -> [];
+dirty_index_match_object_ram(Config) when is_list(Config) ->
+ dirty_index_match_object(Config, ram_copies).
+
+dirty_index_match_object_disc(suite) -> [];
+dirty_index_match_object_disc(Config) when is_list(Config) ->
+ dirty_index_match_object(Config, disc_copies).
+
+dirty_index_match_object_disc_only(suite) -> [];
+dirty_index_match_object_disc_only(Config) when is_list(Config) ->
+ dirty_index_match_object(Config, disc_only_copies).
+
+dirty_index_match_object(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_index_match_object,
+ ValPos = 3,
+ BadValPos = ValPos + 1,
+ Def = [{attributes, [k, v]}, {Storage, [Node1]}, {index, [ValPos]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match([], mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos)),
+ OneRec = {Tab, 1, 2},
+ ?match(ok, mnesia:dirty_write(OneRec)),
+
+ ?match([OneRec], mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos)),
+ ?match({'EXIT', _}, mnesia:dirty_index_match_object({Tab, '$1', 2}, BadValPos)),
+ ?match({'EXIT', _}, mnesia:dirty_index_match_object({foo, '$1', 2}, ValPos)),
+ ?match({'EXIT', _}, mnesia:dirty_index_match_object({[], '$1', 2}, ValPos)),
+ ?match({atomic, [OneRec]}, mnesia:transaction(fun() ->
+ mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos) end)),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Read records by using an index
+
+
+dirty_index_read_ram(suite) -> [];
+dirty_index_read_ram(Config) when is_list(Config) ->
+ dirty_index_read(Config, ram_copies).
+
+dirty_index_read_disc(suite) -> [];
+dirty_index_read_disc(Config) when is_list(Config) ->
+ dirty_index_read(Config, disc_copies).
+
+dirty_index_read_disc_only(suite) -> [];
+dirty_index_read_disc_only(Config) when is_list(Config) ->
+ dirty_index_read(Config, disc_only_copies).
+
+dirty_index_read(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_index_read,
+ ValPos = 3,
+ BadValPos = ValPos + 1,
+ Def = [{type, set},
+ {attributes, [k, v]},
+ {Storage, [Node1]},
+ {index, [ValPos]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ OneRec = {Tab, 1, 2},
+ ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match(ok, mnesia:dirty_write(OneRec)),
+ ?match([OneRec], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:dirty_index_read(Tab, 2, ValPos) end)),
+ ?match(42, mnesia:dirty_update_counter({Tab, 1}, 40)),
+ ?match([{Tab,1,42}], mnesia:dirty_read({Tab, 1})),
+ ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match([{Tab, 1, 42}], mnesia:dirty_index_read(Tab, 42, ValPos)),
+
+ ?match({'EXIT', _}, mnesia:dirty_index_read(Tab, 2, BadValPos)),
+ ?match({'EXIT', _}, mnesia:dirty_index_read(foo, 2, ValPos)),
+ ?match({'EXIT', _}, mnesia:dirty_index_read([], 2, ValPos)),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dirty_index_update_set_ram(suite) -> [];
+dirty_index_update_set_ram(Config) when is_list(Config) ->
+ dirty_index_update_set(Config, ram_copies).
+
+dirty_index_update_set_disc(suite) -> [];
+dirty_index_update_set_disc(Config) when is_list(Config) ->
+ dirty_index_update_set(Config, disc_copies).
+
+dirty_index_update_set_disc_only(suite) -> [];
+dirty_index_update_set_disc_only(Config) when is_list(Config) ->
+ dirty_index_update_set(Config, disc_only_copies).
+
+dirty_index_update_set(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = index_test,
+ ValPos = v1,
+ ValPos2 = v3,
+ Def = [{attributes, [k, v1, v2, v3]},
+ {Storage, [Node1]},
+ {index, [ValPos]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ Pat1 = {Tab, '$1', 2, '$2', '$3'},
+ Pat2 = {Tab, '$1', '$2', '$3', '$4'},
+
+ Rec1 = {Tab, 1, 2, 3, 4},
+ Rec2 = {Tab, 2, 2, 13, 14},
+ Rec3 = {Tab, 1, 12, 13, 14},
+ Rec4 = {Tab, 4, 2, 13, 14},
+
+ ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match(ok, mnesia:dirty_write(Rec1)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
+
+ ?match(ok, mnesia:dirty_write(Rec2)),
+ R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2], lists:sort(R1)),
+
+ ?match(ok, mnesia:dirty_write(Rec3)),
+ R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec2], lists:sort(R2)),
+ ?match([Rec2], mnesia:dirty_index_match_object(Pat1, ValPos)),
+
+ {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
+ ?match([Rec3, Rec2], lists:sort(R3)),
+
+ ?match(ok, mnesia:dirty_write(Rec4)),
+ R4 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec2, Rec4], lists:sort(R4)),
+
+ ?match(ok, mnesia:dirty_delete({Tab, 4})),
+ ?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
+
+ ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
+
+ R5 = mnesia:dirty_match_object(Pat2),
+ ?match([Rec3, Rec2, Rec4], lists:sort(R5)),
+
+ R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec2, Rec4], lists:sort(R6)),
+ ?match([], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ R7 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec3, Rec2, Rec4], lists:sort(R7)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ R9 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec2, Rec4], lists:sort(R9)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
+ R10 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec4], lists:sort(R10)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ ?match([Rec4], mnesia:dirty_index_read(Tab, 14, ValPos2)),
+
+ ?match(ok, mnesia:dirty_delete({Tab, 4})),
+ R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1], lists:sort(R11)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ ?match([], mnesia:dirty_index_read(Tab, 14, ValPos2)),
+
+ ?verify_mnesia(Nodes, []).
+
+dirty_index_update_bag_ram(suite) -> [];
+dirty_index_update_bag_ram(Config)when is_list(Config) ->
+ dirty_index_update_bag(Config, ram_copies).
+
+dirty_index_update_bag_disc(suite) -> [];
+dirty_index_update_bag_disc(Config)when is_list(Config) ->
+ dirty_index_update_bag(Config, disc_copies).
+
+dirty_index_update_bag_disc_only(suite) -> [];
+dirty_index_update_bag_disc_only(Config)when is_list(Config) ->
+ dirty_index_update_bag(Config, disc_only_copies).
+
+dirty_index_update_bag(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = index_test,
+ ValPos = v1,
+ ValPos2 = v3,
+ Def = [{type, bag},
+ {attributes, [k, v1, v2, v3]},
+ {Storage, [Node1]},
+ {index, [ValPos]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ Pat1 = {Tab, '$1', 2, '$2', '$3'},
+ Pat2 = {Tab, '$1', '$2', '$3', '$4'},
+
+ Rec1 = {Tab, 1, 2, 3, 4},
+ Rec2 = {Tab, 2, 2, 13, 14},
+ Rec3 = {Tab, 1, 12, 13, 14},
+ Rec4 = {Tab, 4, 2, 13, 4},
+ Rec5 = {Tab, 1, 2, 234, 14},
+
+ %% Simple Index
+ ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match(ok, mnesia:dirty_write(Rec1)),
+ ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
+ R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2], lists:sort(R1)),
+
+ ?match(ok, mnesia:dirty_write(Rec3)),
+ R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2], lists:sort(R2)),
+
+ R3 = mnesia:dirty_index_match_object(Pat1, ValPos),
+ ?match([Rec1, Rec2], lists:sort(R3)),
+
+ R4 = mnesia:dirty_match_object(Pat2),
+ ?match([Rec1, Rec3, Rec2], lists:sort(R4)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ R5 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2, Rec4], lists:sort(R5)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
+ R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2], lists:sort(R6)),
+
+ ?match(ok, mnesia:dirty_delete_object(Rec1)),
+ ?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ R7 = mnesia:dirty_match_object(Pat2),
+ ?match([Rec3, Rec2], lists:sort(R7)),
+
+ %% Two indexies
+ ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
+
+ R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
+
+ R9 = mnesia:dirty_index_read(Tab, 4, ValPos2),
+ ?match([Rec1, Rec4], lists:sort(R9)),
+ R10 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec3, Rec2], lists:sort(R10)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
+ R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec1, Rec5, Rec2, Rec4], lists:sort(R11)),
+ R12 = mnesia:dirty_index_read(Tab, 4, ValPos2),
+ ?match([Rec1, Rec4], lists:sort(R12)),
+ R13 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec5, Rec3, Rec2], lists:sort(R13)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
+ R14 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec5, Rec2, Rec4], lists:sort(R14)),
+ ?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ R15 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec5, Rec3, Rec2], lists:sort(R15)),
+
+ ?match(ok, mnesia:dirty_delete_object(Rec5)),
+ R16 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec2, Rec4], lists:sort(R16)),
+ ?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ R17 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec3, Rec2], lists:sort(R17)),
+
+ ?match(ok, mnesia:dirty_write(Rec1)),
+ ?match(ok, mnesia:dirty_delete({Tab, 1})),
+ R18 = mnesia:dirty_index_read(Tab, 2, ValPos),
+ ?match([Rec2, Rec4], lists:sort(R18)),
+ ?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
+ R19 = mnesia:dirty_index_read(Tab, 14, ValPos2),
+ ?match([Rec2], lists:sort(R19)),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Dirty iteration
+%% dirty_slot, dirty_first, dirty_next
+
+
+dirty_iter_ram(suite) -> [];
+dirty_iter_ram(Config) when is_list(Config) ->
+ dirty_iter(Config, ram_copies).
+
+dirty_iter_disc(suite) -> [];
+dirty_iter_disc(Config) when is_list(Config) ->
+ dirty_iter(Config, disc_copies).
+
+dirty_iter_disc_only(suite) -> [];
+dirty_iter_disc_only(Config) when is_list(Config) ->
+ dirty_iter(Config, disc_only_copies).
+
+dirty_iter(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = dirty_iter,
+ Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match([], all_slots(Tab)),
+ ?match([], all_nexts(Tab)),
+
+ Keys = lists:seq(1, 5),
+ Records = [{Tab, A, B} || A <- Keys, B <- lists:seq(1, 2)],
+ lists:foreach(fun(Rec) -> ?match(ok, mnesia:dirty_write(Rec)) end, Records),
+
+ SortedRecords = lists:sort(Records),
+ ?match(SortedRecords, lists:sort(all_slots(Tab))),
+ ?match(Keys, lists:sort(all_nexts(Tab))),
+
+ ?match({'EXIT', _}, mnesia:dirty_first(foo)),
+ ?match({'EXIT', _}, mnesia:dirty_next(foo, foo)),
+ ?match({'EXIT', _}, mnesia:dirty_slot(foo, 0)),
+ ?match({'EXIT', _}, mnesia:dirty_slot(foo, [])),
+ ?match({atomic, Keys},
+ mnesia:transaction(fun() -> lists:sort(all_nexts(Tab)) end)),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Returns a list of all keys in table
+all_slots(Tab) ->
+ all_slots(Tab, [], 0).
+
+all_slots(_Tab, '$end_of_table', _) ->
+ [];
+all_slots(Tab, PrevRecords, PrevSlot) ->
+ Records = mnesia:dirty_slot(Tab, PrevSlot),
+ PrevRecords ++ all_slots(Tab, Records, PrevSlot + 1).
+
+%% Returns a list of all keys in table
+
+all_nexts(Tab) ->
+ FirstKey = mnesia:dirty_first(Tab),
+ all_nexts(Tab, FirstKey).
+
+all_nexts(_Tab, '$end_of_table') ->
+ [];
+all_nexts(Tab, PrevKey) ->
+ Key = mnesia:dirty_next(Tab, PrevKey),
+ [PrevKey] ++ all_nexts(Tab, Key).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+update_trans(Tab, Key, Acc) ->
+ Update =
+ fun() ->
+ Res = (catch mnesia:read({Tab, Key})),
+ case Res of
+ [{Tab, Key, Extra, Acc}] ->
+ mnesia:write({Tab,Key,Extra, Acc+1});
+ Val ->
+ {read, Val, {acc, Acc}}
+ end
+ end,
+ receive
+ {Pid, quit} -> Pid ! {self(), Acc}
+ after
+ 3 ->
+ case catch mnesia:sync_dirty(Update) of
+ ok ->
+ update_trans(Tab, Key, Acc+1);
+ Else ->
+ ?error("Dirty Operation failed on ~p (update no ~p) with ~p~n"
+ "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n",
+ [node(),
+ Acc,
+ Else,
+ mnesia:table_info(Tab, where_to_read),
+ mnesia:table_info(Tab, where_to_write),
+ mnesia:table_info(Tab, where_to_commit),
+ mnesia:table_info(Tab, storage_type)])
+ end
+ end.
+
+del_table_copy_1(suite) -> [];
+del_table_copy_1(Config) when is_list(Config) ->
+ [_Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
+ del_table(Node2, Node2, Nodes). %Called on same Node as deleted
+del_table_copy_2(suite) -> [];
+del_table_copy_2(Config) when is_list(Config) ->
+ [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
+ del_table(Node1, Node2, Nodes). %Called from other Node
+del_table_copy_3(suite) -> [];
+del_table_copy_3(Config) when is_list(Config) ->
+ [_Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ del_table(Node3, Node2, Nodes). %Called from Node w.o. table
+
+del_table(CallFrom, DelNode, [Node1, Node2, Node3]) ->
+ Tab = schema_ops,
+ Def = [{disc_only_copies, [Node1]}, {ram_copies, [Node2]},
+ {attributes, [key, attr1, attr2]}],
+ ?log("Test case removing table from ~w, with ~w~n", [DelNode, Def]),
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 1000),
+
+ Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
+ Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
+ Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
+
+
+ dbg:tracer(process, {fun(Msg,_) -> tracer(Msg) end, void}),
+ %% dbg:n(Node2),
+ %% dbg:n(Node3),
+ %% dbg:tp('_', []),
+ %% dbg:tpl(dets, [timestamp]),
+ dbg:p(Pid1, [m,c,timestamp]),
+
+ ?match({atomic, ok},
+ rpc:call(CallFrom, mnesia, del_table_copy, [Tab, DelNode])),
+
+ Pid1 ! {self(), quit}, R1 =
+ receive {Pid1, Res1} -> Res1
+ after
+ 5000 -> io:format("~p~n",[process_info(Pid1)]),error
+ end,
+ Pid2 ! {self(), quit}, R2 =
+ receive {Pid2, Res2} -> Res2
+ after
+ 5000 -> error
+ end,
+ Pid3 ! {self(), quit}, R3 =
+ receive {Pid3, Res3} -> Res3
+ after
+ 5000 -> error
+ end,
+ verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
+ ?verify_mnesia([Node1, Node2, Node3], []).
+
+tracer({trace_ts, _, send, Msg, Pid, {_,S,Ms}}) ->
+ io:format("~p:~p ~p >> ~w ~n",[S,Ms,Pid,Msg]);
+tracer({trace_ts, _, 'receive', Msg, {_,S,Ms}}) ->
+ io:format("~p:~p << ~w ~n",[S,Ms,Msg]);
+
+
+tracer(Msg) ->
+ io:format("UMsg ~p ~n",[Msg]),
+ ok.
+
+
+
+add_table_copy_1(suite) -> [];
+add_table_copy_1(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node1, Node3, Nodes, Def).
+%% Not so much diff from 1 but I got a feeling of a bug
+%% should behave exactly the same but just checking the internal ordering
+add_table_copy_2(suite) -> [];
+add_table_copy_2(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node2, Node3, Nodes, Def).
+add_table_copy_3(suite) -> [];
+add_table_copy_3(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node3, Node3, Nodes, Def).
+add_table_copy_4(suite) -> [];
+add_table_copy_4(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_only_copies, [Node1]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node2, Node3, Nodes, Def).
+
+add_table(CallFrom, AddNode, [Node1, Node2, Node3], Def) ->
+ ?log("Test case adding table at ~w, with ~w~n", [AddNode, Def]),
+ Tab = schema_ops,
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 1002),
+
+ Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
+ Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
+ Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
+
+ ?match({atomic, ok}, rpc:call(CallFrom, mnesia, add_table_copy,
+ [Tab, AddNode, ram_copies])),
+ Pid1 ! {self(), quit}, R1 = receive {Pid1, Res1} -> Res1 after 5000 -> error end,
+ Pid2 ! {self(), quit}, R2 = receive {Pid2, Res2} -> Res2 after 5000 -> error end,
+ Pid3 ! {self(), quit}, R3 = receive {Pid3, Res3} -> Res3 after 5000 -> error end,
+ verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
+ ?verify_mnesia([Node1, Node2, Node3], []).
+
+move_table_copy_1(suite) -> [];
+move_table_copy_1(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node1, Node1, Node3, Nodes, Def).
+move_table_copy_2(suite) -> [];
+move_table_copy_2(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node2, Node1, Node3, Nodes, Def).
+move_table_copy_3(suite) -> [];
+move_table_copy_3(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node3, Node1, Node3, Nodes, Def).
+move_table_copy_4(suite) -> [];
+move_table_copy_4(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{ram_copies, [Node1]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node2, Node1, Node3, Nodes, Def).
+
+move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) ->
+ ?log("Test case move table from ~w to ~w, with ~w~n", [FromNode, ToNode, Def]),
+ Tab = schema_ops,
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 1002),
+
+ Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
+ Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
+ Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
+
+ ?match({atomic, ok}, rpc:call(CallFrom, mnesia, move_table_copy,
+ [Tab, FromNode, ToNode])),
+ Pid1 ! {self(), quit},
+ R1 = receive {Pid1, Res1} -> Res1 after 5000 -> ?error("timeout pid1~n", []) end,
+ Pid2 ! {self(), quit},
+ R2 = receive {Pid2, Res2} -> Res2 after 5000 -> ?error("timeout pid2~n", []) end,
+ Pid3 ! {self(), quit},
+ R3 = receive {Pid3, Res3} -> Res3 after 5000 -> ?error("timeout pid3~n", []) end,
+ verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
+ ?verify_mnesia([Node1, Node2, Node3], []).
+
+% Verify consistency between different nodes
+% Due to limitations in the current dirty_ops this can wrong from time to time!
+verify_oids(Tab, N1, N2, N3, R1, R2, R3) ->
+ io:format("DEBUG 1=>~p 2=>~p 3=>~p~n", [R1,R2,R3]),
+ ?match([{_, _, _, R1}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{_, _, _, R1}], rpc:call(N2, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{_, _, _, R1}], rpc:call(N3, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{_, _, _, R2}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])),
+ ?match([{_, _, _, R2}], rpc:call(N2, mnesia, dirty_read, [{Tab, 2}])),
+ ?match([{_, _, _, R2}], rpc:call(N3, mnesia, dirty_read, [{Tab, 2}])),
+ ?match([{_, _, _, R3}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])),
+ ?match([{_, _, _, R3}], rpc:call(N2, mnesia, dirty_read, [{Tab, 3}])),
+ ?match([{_, _, _, R3}], rpc:call(N3, mnesia, dirty_read, [{Tab, 3}])).
+
+insert(_Tab, 0) -> ok;
+insert(Tab, N) when N > 0 ->
+ ok = mnesia:sync_dirty(fun() -> false = mnesia:is_transaction(), mnesia:write({Tab, N, N, 0}) end),
+ insert(Tab, N-1).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/mnesia/test/mnesia_durability_test.erl b/lib/mnesia/test/mnesia_durability_test.erl
new file mode 100644
index 0000000000..55205d1222
--- /dev/null
+++ b/lib/mnesia/test/mnesia_durability_test.erl
@@ -0,0 +1,1447 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_durability_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-record(test_rec,{key,val}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all() ->
+ [{group, load_tables},
+ {group, durability_of_dump_tables},
+ durability_of_disc_copies,
+ durability_of_disc_only_copies].
+
+groups() ->
+ [{load_tables, [],
+ [load_latest_data, load_local_contents_directly,
+ load_directly_when_all_are_ram_copiesA,
+ load_directly_when_all_are_ram_copiesB,
+ {group, late_load_when_all_are_ram_copies_on_ram_nodes},
+ load_when_last_replica_becomes_available,
+ load_when_we_have_down_from_all_other_replica_nodes,
+ late_load_transforms_into_disc_load,
+ late_load_leads_to_hanging,
+ force_load_when_nobody_intents_to_load,
+ force_load_when_someone_has_decided_to_load,
+ force_load_when_someone_else_already_has_loaded,
+ force_load_when_we_has_loaded,
+ force_load_on_a_non_local_table,
+ force_load_when_the_table_does_not_exist,
+ {group, load_tables_with_master_tables}]},
+ {late_load_when_all_are_ram_copies_on_ram_nodes, [],
+ [late_load_when_all_are_ram_copies_on_ram_nodes1,
+ late_load_when_all_are_ram_copies_on_ram_nodes2]},
+ {load_tables_with_master_tables, [],
+ [master_nodes, starting_master_nodes,
+ master_on_non_local_tables,
+ remote_force_load_with_local_master_node]},
+ {durability_of_dump_tables, [],
+ [dump_ram_copies, dump_disc_copies, dump_disc_only]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+load_latest_data(doc) ->
+ ["Base functionality, verify that the latest data is loaded"];
+load_latest_data(suite) -> [];
+load_latest_data(Config) when is_list(Config) ->
+ [N1,N2,N3] = Nodes = ?acquire_nodes(3, Config),
+ %%Create a replicated local table
+ ?match({atomic,ok}, mnesia:create_table(t0, [{disc_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t1, [{disc_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t2, [{disc_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t3, [{disc_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t4, [{disc_copies,[N1,N2]}])),
+ ?match({atomic,ok}, mnesia:create_table(t5, [{disc_copies,[N1,N2]}])),
+ Rec1 = {t1, test, ok},
+ Rec2 = {t1, test, 2},
+
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ ?match(ok, rpc:call(N2, mnesia, dirty_write, [Rec2])),
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+
+ ?match([], mnesia_test_lib:start_mnesia([N1], [])),
+ %% Should wait for N2
+ ?match({timeout, [t1]}, rpc:call(N1, mnesia, wait_for_tables, [[t1], 3000])),
+ ?match([], mnesia_test_lib:start_mnesia([N3], [])),
+ ?match({timeout, [t1]}, rpc:call(N1, mnesia, wait_for_tables, [[t1], 3000])),
+
+
+ ?match([], mnesia_test_lib:start_mnesia([N2], [])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[t1], 3000])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[t1], 3000])),
+ %% We should find the record
+ ?match([Rec2], rpc:call(N1, mnesia, dirty_read, [t1, test])),
+ ?match([Rec2], rpc:call(N2, mnesia, dirty_read, [t1, test])),
+
+ %% ok, lets switch order
+ ?match(ok, mnesia:dirty_delete_object(Rec1)),
+ ?match(ok, mnesia:dirty_delete_object(Rec2)),
+ %% redo
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match(ok, mnesia:dirty_write(Rec1)),
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+
+ ?match([], mnesia_test_lib:start_mnesia([N2], [])),
+ %% Should wait for N1
+ ?match({timeout, [t1]}, rpc:call(N2, mnesia, wait_for_tables, [[t1], 2000])),
+ ?match([], mnesia_test_lib:start_mnesia([N3], [])),
+ ?match({timeout, [t1]}, rpc:call(N2, mnesia, wait_for_tables, [[t1], 2000])),
+ ?match([], mnesia_test_lib:start_mnesia([N1], [])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[t1], 1000])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[t1], 1000])),
+ %% We should find the record
+ ?match([Rec1], rpc:call(N1, mnesia, dirty_read, [t1, test])),
+ ?match([Rec1], rpc:call(N2, mnesia, dirty_read, [t1, test])),
+
+ ?verify_mnesia(Nodes, []).
+
+
+load_local_contents_directly(doc) ->
+ ["Local contents shall always be loaded. Check this by having a local ",
+ "table on two nodes N1, N2, stopping N1 before N2, an then verifying ",
+ "that N1 can start without N2 being started."];
+load_local_contents_directly(suite) -> [];
+load_local_contents_directly(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+ %%Create a replicated local table
+ ?match({atomic,ok},
+ mnesia:create_table(test_rec,
+ [{local_content,true},
+ {disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}]
+ ) ),
+ %%Verify that it has local contents.
+ ?match( true, mnesia:table_info(test_rec,local_content) ),
+ %%Helper Funs
+ Write_one = fun(Value) -> mnesia:write(#test_rec{key=1,val=Value}) end,
+ Read_one = fun(Key) -> mnesia:read( {test_rec, Key}) end,
+ %%Write a value one N1 that we may test against later
+ ?match({atomic,ok},
+ rpc:call( N1, mnesia, transaction, [Write_one,[11]] ) ),
+ %%Stop Mnesia on N1
+ %?match([], mnesia_test_lib:stop_mnesia([N1])),
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+
+ %%Write a value on N2, same key but a different value
+ ?match({atomic,ok},
+ rpc:call( N2, mnesia, transaction, [Write_one,[22]] ) ),
+ %%Stop Mnesia on N2
+ %?match([], mnesia_test_lib:stop_mnesia([N2])),
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+
+ %%Restart Mnesia on N1 verify that we can read from it without
+ %%starting Mnesia on N2.
+ ?match(ok, rpc:call(N1, mnesia, start, [])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[test_rec], 30000])),
+ %%Read back the value
+ ?match( {atomic,[#test_rec{key=1,val=11}]},
+ rpc:call(N1, mnesia, transaction, [Read_one,[1]] ) ),
+ %%Restart Mnesia on N2 and verify the contents there.
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[test_rec], 30000])),
+ ?match( {atomic,[#test_rec{key=1,val=22}]},
+ rpc:call(N2, mnesia, transaction, [Read_one,[1]] ) ),
+ %%Check that the start of Mnesai on N2 did not affect the contents on N1
+ ?match( {atomic,[#test_rec{key=1,val=11}]},
+ rpc:call(N1, mnesia, transaction, [Read_one,[1]] ) ),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+load_directly_when_all_are_ram_copiesA(doc) ->
+ ["Tables that are RAM copies only shall also be loaded directly. ",
+ "1. N1 and N2 has RAM copies of a table, stop N1 before N2. ",
+ "2. When N1 starts he shall have access to the table ",
+ " without having to start N2" ];
+load_directly_when_all_are_ram_copiesA(suite) -> [];
+load_directly_when_all_are_ram_copiesA(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+
+ ?match({atomic,ok},
+ mnesia:create_table(test_rec,
+ [{ram_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}]
+ ) ),
+ ?match( Nodes, mnesia:table_info(test_rec,ram_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_only_copies) ),
+ Write_one = fun(Value) -> mnesia:write(#test_rec{key=2,val=Value}) end,
+ Read_one = fun() -> mnesia:read({test_rec,2}) end,
+ %%Write a value one N1 that we may test against later
+ ?match({atomic,ok},
+ rpc:call( N1, mnesia, transaction, [Write_one,[11]] ) ),
+ %%Stop Mnesia on N1
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ %%Write a value and check result (on N2; not possible on N1
+ %%since Mnesia is stopped there).
+ ?match({atomic,ok}, rpc:call(N2,mnesia,transaction,[Write_one,[22]]) ),
+ ?match({atomic,[#test_rec{key=2,val=22}]},
+ rpc:call(N2,mnesia,transaction,[Read_one]) ),
+ %%Stop Mnesia on N2
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ %%Restart Mnesia on N1 verify that we can access test_rec from
+ %%N1 without starting Mnesia on N2.
+ ?match(ok, rpc:call(N1, mnesia, start, [])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[test_rec], 30000])),
+ ?match({atomic,[]}, rpc:call(N1,mnesia,transaction,[Read_one])),
+ ?match({atomic,ok}, rpc:call(N1,mnesia,transaction,[Write_one,[33]])),
+ ?match({atomic,[#test_rec{key=2,val=33}]},
+ rpc:call(N1,mnesia,transaction,[Read_one])),
+ %%Restart Mnesia on N2 and verify the contents there.
+ ?match([], mnesia_test_lib:start_mnesia([N2], [test_rec])),
+ ?match( {atomic,[#test_rec{key=2,val=33}]},
+ rpc:call(N2, mnesia, transaction, [Read_one] ) ),
+ %%Check that the start of Mnesai on N2 did not affect the contents on N1
+ ?match( {atomic,[#test_rec{key=2,val=33}]},
+ rpc:call(N1, mnesia, transaction, [Read_one] ) ),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+load_directly_when_all_are_ram_copiesB(doc) ->
+ ["Tables that are RAM copies only shall be loaded from a replicat ",
+ "when possible. ",
+ "1. N1 and N2 has RAM copies of a table, stop N1 before N2.",
+ "2. Now start N2 first and then N1, N1 shall then load the table ",
+ " from N2."];
+load_directly_when_all_are_ram_copiesB(suite) -> [];
+load_directly_when_all_are_ram_copiesB(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+ ?match({atomic,ok},
+ mnesia:create_table(test_rec,
+ [{ram_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}]
+ ) ),
+ ?match( Nodes, mnesia:table_info(test_rec,ram_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_only_copies) ),
+ Write_one = fun(Value) -> mnesia:write(#test_rec{key=3,val=Value}) end,
+ Read_one = fun() -> mnesia:read( {test_rec, 3}) end,
+ %%Write a value one N1 that we may test against later
+ ?match({atomic,ok},
+ rpc:call( N1, mnesia, transaction, [Write_one,[11]] ) ),
+ ?match({atomic,[#test_rec{key=3,val=11}]},
+ rpc:call(N2,mnesia,transaction,[Read_one]) ),
+ %%Stop Mnesia on N1
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ %%Write a value and check result (on N2; not possible on N1
+ %%since Mnesia is stopped there).
+ ?match({atomic,ok}, rpc:call(N2,mnesia,transaction,[Write_one,[22]]) ),
+ ?match({atomic,[#test_rec{key=3,val=22}]},
+ rpc:call(N2,mnesia,transaction,[Read_one]) ),
+ %%Stop Mnesia on N2
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ %%Restart Mnesia on N2 verify that we can access test_rec from
+ %%N2 without starting Mnesia on N1.
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[test_rec], 30000])),
+ ?match({atomic,[]}, rpc:call(N2,mnesia,transaction,[Read_one])),
+ ?match({atomic,ok}, rpc:call(N2,mnesia,transaction,[Write_one,[33]])),
+ ?match({atomic,[#test_rec{key=3,val=33}]},
+ rpc:call(N2,mnesia,transaction,[Read_one])),
+ %%Restart Mnesia on N1 and verify the contents there.
+ ?match([], mnesia_test_lib:start_mnesia([N1], [test_rec])),
+ ?match( {atomic,[#test_rec{key=3,val=33}]},
+ rpc:call(N1,mnesia,transaction,[Read_one])),
+ %%Check that the start of Mnesai on N1 did not affect the contents on N2
+ ?match( {atomic,[#test_rec{key=3,val=33}]},
+ rpc:call(N2,mnesia,transaction,[Read_one])),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+late_load_when_all_are_ram_copies_on_ram_nodes1(suite) -> [];
+late_load_when_all_are_ram_copies_on_ram_nodes1(Config) when is_list(Config) ->
+ [N1, N2] = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema,
+ {reload_appls, [mnesia]}],
+ 2, Config, ?FILE, ?LINE),
+ Res = late_load_when_all_are_ram_copies_on_ram_nodes(N1, [N2], Config),
+ mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}],
+ 2, Config, ?FILE, ?LINE),
+ Res.
+
+late_load_when_all_are_ram_copies_on_ram_nodes2(suite) -> [];
+late_load_when_all_are_ram_copies_on_ram_nodes2(Config) when is_list(Config) ->
+ [N1, N2, N3] = mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema,
+ {reload_appls, [mnesia]}],
+ 3, Config, ?FILE, ?LINE),
+ Res = late_load_when_all_are_ram_copies_on_ram_nodes(N1, [N2, N3], Config),
+ mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}],
+ 3, Config, ?FILE, ?LINE),
+ Res.
+
+late_load_when_all_are_ram_copies_on_ram_nodes(DiscNode, RamNs, _Config)
+ when DiscNode == node() ->
+ ?match(ok, mnesia:create_schema([DiscNode])),
+ ?match(ok, mnesia:start()),
+ Nodes = [DiscNode | RamNs],
+ Extra = [{extra_db_nodes, Nodes}],
+ Ok = [ok || _ <- RamNs],
+ ?match({Ok, []}, rpc:multicall(RamNs, mnesia, start, [Extra])),
+ ?match([], wait_until_running(Nodes)),
+
+ LastRam = lists:last(RamNs),
+ %% ?match({atomic, ok},
+ %% mnesia:add_table_copy(schema, LastRam, ram_copies)),
+ Def = [{ram_copies, RamNs}, {attributes, record_info(fields, test_rec)}],
+ ?match({atomic,ok}, mnesia:create_table(test_rec, Def)),
+ ?verify_mnesia(Nodes, []),
+ ?match([], mnesia_test_lib:stop_mnesia(RamNs)),
+ ?match(stopped, mnesia:stop()),
+ ?match(ok, mnesia:start()),
+
+ Rec1 = #test_rec{key=3, val=33},
+ Rec2 = #test_rec{key=4, val=44},
+
+ FirstRam = hd(RamNs),
+ ?match(ok, rpc:call(FirstRam, mnesia, start, [Extra])),
+ ?match(ok, rpc:call(FirstRam, mnesia, wait_for_tables,
+ [[test_rec], 30000])),
+ ?match(ok, rpc:call(FirstRam, mnesia, dirty_write,[Rec1])),
+ ?match(ok, mnesia:wait_for_tables([test_rec], 30000)),
+ mnesia:dirty_write(Rec2),
+
+ if
+ FirstRam /= LastRam ->
+ ?match(ok, rpc:call(LastRam, mnesia, start, [Extra])),
+ ?match(ok, rpc:call(LastRam, mnesia, wait_for_tables,
+ [[test_rec], 30000]));
+ true ->
+ ignore
+ end,
+ ?match([Rec1], rpc:call(LastRam, mnesia, dirty_read, [{test_rec, 3}])),
+ ?match([Rec2], rpc:call(LastRam, mnesia, dirty_read, [{test_rec, 4}])),
+ ?verify_mnesia(Nodes, []).
+
+wait_until_running(Nodes) ->
+ wait_until_running(Nodes, 30).
+
+wait_until_running(Nodes, Times) when Times > 0->
+ Alive = mnesia:system_info(running_db_nodes),
+ case Nodes -- Alive of
+ [] ->
+ [];
+ Remaining ->
+ timer:sleep(timer:seconds(1)),
+ wait_until_running(Remaining, Times - 1)
+ end;
+wait_until_running(Nodes, _) ->
+ Nodes.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+load_when_last_replica_becomes_available(doc) ->
+ ["Check that when all Mnesia nodes die at the same instant, then the ",
+ "replicated table shall be accessible when the last node is started ",
+ "again.",
+ "Checked by cheating. Start Mnesia on N1, N2, N3. Have a table ",
+ "replicated on disc on all three nodes, fill in some transactions, ",
+ "install a fallback. Restart mnesia on all nodes"
+ "This is the cheat and it simulates that all nodes died at the same ",
+ "time. Check that the table is only accessible after the last node ",
+ "has come up."];
+load_when_last_replica_becomes_available(suite) -> [];
+load_when_last_replica_becomes_available(Config) when is_list(Config) ->
+ [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config),
+ ?match({atomic,ok},
+ mnesia:create_table(test_rec,
+ [{disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}]
+ ) ),
+ ?match( [], mnesia:table_info(test_rec,ram_copies) ),
+ ?match( Nodes, mnesia:table_info(test_rec,disc_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_only_copies) ),
+ Write_one = fun(Key,Val)->mnesia:write(#test_rec{key=Key,val=Val}) end,
+ Read_one = fun(Key) ->mnesia:read( {test_rec, Key}) end,
+ %%Write one value from each node.
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[1,11]])),
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[2,22]])),
+ ?match({atomic,ok},rpc:call(N3,mnesia,transaction,[Write_one,[3,33]])),
+ %%Check the values
+ ?match({atomic,[#test_rec{key=1,val=11}]},
+ rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[#test_rec{key=2,val=22}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[#test_rec{key=3,val=33}]},
+ rpc:call(N1,mnesia,transaction,[Read_one,[3]]) ),
+
+ ?match(ok, mnesia:backup("test_last_replica")),
+ ?match(ok, mnesia:install_fallback("test_last_replica")),
+ file:delete("test_last_replica"),
+ %%Stop Mnesia on all three nodes
+ ?match([], mnesia_test_lib:kill_mnesia(Nodes)),
+
+ %%Start Mnesia on one node, make sure that test_rec is not available
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match({timeout,[test_rec]},
+ rpc:call(N2, mnesia, wait_for_tables, [[test_rec], 10000])),
+ ?match(ok, rpc:call(N1, mnesia, start, [])),
+ ?match({timeout,[test_rec]},
+ rpc:call(N1, mnesia, wait_for_tables, [[test_rec], 10000])),
+ %%Start the third node
+ ?match(ok, rpc:call(N3, mnesia, start, [])),
+ %%Make sure that the table is loaded everywhere
+ ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[test_rec], 30000])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[test_rec], 30000])),
+ ?match(ok, rpc:call(N1, mnesia, wait_for_tables, [[test_rec], 30000])),
+
+ %%Check the values
+ ?match({atomic,[#test_rec{key=1,val=11}]},
+ rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[#test_rec{key=2,val=22}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[#test_rec{key=3,val=33}]},
+ rpc:call(N1,mnesia,transaction,[Read_one,[3]]) ),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+load_when_we_have_down_from_all_other_replica_nodes(doc) ->
+ ["The table can be loaded if this node was the last one surviving. ",
+ "Check this by having N1, N2, N3 and a table replicated on all those ",
+ "nodes. Then kill them in the N1, N2, N3 order. Then start N3 and ",
+ "verify that the table is available with correct contents."];
+load_when_we_have_down_from_all_other_replica_nodes(suite) -> [];
+load_when_we_have_down_from_all_other_replica_nodes(Config) when is_list(Config) ->
+ [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config),
+ ?match({atomic,ok},
+ mnesia:create_table(test_rec,
+ [{disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}]
+ ) ),
+ ?match( [], mnesia:table_info(test_rec,ram_copies) ),
+ ?match( Nodes, mnesia:table_info(test_rec,disc_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_only_copies) ),
+ Write_one = fun(Key,Val)->mnesia:write(#test_rec{key=Key,val=Val}) end,
+ Read_one = fun(Key) ->mnesia:read( {test_rec, Key}) end,
+ %%Write one value from each node.
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[1,111]])),
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[2,222]])),
+ ?match({atomic,ok},rpc:call(N3,mnesia,transaction,[Write_one,[3,333]])),
+ %%Check the values
+ ?match({atomic,[#test_rec{key=1,val=111}]},
+ rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[#test_rec{key=2,val=222}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[#test_rec{key=3,val=333}]},
+ rpc:call(N1,mnesia,transaction,[Read_one,[3]]) ),
+ %%Stop Mnesia on all three nodes
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[22,22]])),
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match({atomic,ok},rpc:call(N3,mnesia,transaction,[Write_one,[33,33]])),
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+ ?verbose("Mnesia stoped on all three nodes.~n",[]),
+
+ %%Start Mnesia on N3; wait for 'test_rec' table to load
+ ?match(ok, rpc:call(N3, mnesia, start, [])),
+ ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[test_rec], 30000])),
+
+ %%Check the values
+ ?match({atomic,[#test_rec{key=1,val=111}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[#test_rec{key=2,val=222}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[#test_rec{key=3,val=333}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[3]]) ),
+ ?match({atomic,[#test_rec{key=22,val=22}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[22]]) ),
+ ?match({atomic,[#test_rec{key=33,val=33}]},
+ rpc:call(N3,mnesia,transaction,[Read_one,[33]]) ),
+ ?verify_mnesia([N3], [N1, N2]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+late_load_transforms_into_disc_load(doc) ->
+ ["Difficult case that needs instrumentation of Mnesia.",
+ "A table is force loaded, and Mnesia decides to load it from another ",
+ "Mnesia node because it is avaliable there. The other Mnesia node then ",
+ "dies in mid copy which shall make the first Mnesia node to really ",
+ "force load from disc.",
+ "Check this by starting N1 and N2 and replicating a table between ",
+ "them. Then kill N1 before N2. The idea is to start N2 first, then ",
+ "N1 and then do a force load on N1. This force load will load from ",
+ "N2 BUT N2 must be killed after the decision to load from it has ",
+ "been made. tricky."];
+
+late_load_transforms_into_disc_load(suite) -> [];
+late_load_transforms_into_disc_load(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+
+ {success, [A, B]} = ?start_activities(Nodes),
+
+ ?match(Node1, node(A)),
+ ?match(Node2, node(B)),
+
+ Tab = late_load_table,
+ Def = [{attributes, [key, value]},
+ {disc_copies, Nodes}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 111, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 222, 42})),
+
+ TestPid = self(),
+ DebugId = {mnesia_loader, do_get_network_copy},
+ DebugFun = fun(PrevContext, EvalContext) ->
+ ?verbose("interrupt late load, pid ~p #~p ~n context ~p ~n",
+ [self(),PrevContext,EvalContext]),
+
+ mnesia_test_lib:kill_mnesia([Node2]),
+ TestPid ! {self(),debug_fun_was_called},
+
+ ?verbose("interrupt late_log - continues ~n",[]),
+ ?deactivate_debug_fun(DebugId),
+ PrevContext+1
+ end,
+ ?remote_activate_debug_fun(Node1,DebugId, DebugFun, 1),
+
+ %% kill mnesia on node1
+ mnesia_test_lib:kill_mnesia([Node1]),
+ %% wait a while, so that mnesia is really down
+ timer:sleep(timer:seconds(1)),
+
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab, 222, 815}])),
+
+ %% start Mnesia on node1
+ ?match(ok,mnesia:start()),
+ ?match(yes, mnesia:force_load_table(Tab)),
+ ?match(ok, mnesia:wait_for_tables([Tab],timer:seconds(30))),
+
+ receive_messages([debug_fun_was_called]),
+
+ check_tables([A],[{Tab,111},{Tab,222}],[[{Tab,111,4711}],[{Tab,222,42}]]),
+ ?verify_mnesia([Node1], [Node2]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+late_load_leads_to_hanging(doc) ->
+ ["Difficult case that needs instrumentation of Mnesia.",
+ "A table is loaded, and Mnesia decides to load it from another ",
+ "Mnesia node because it has the latest copy there. ",
+ "The other Mnesia node then ",
+ "dies in mid copy which shall make the first Mnesia node not to ",
+ "force load from disc but to wait for the other node to come up again",
+ "Check this by starting N1 and N2 and replicating a table between ",
+ "them. Then kill N1 before N2. The idea is to start N2 first, then ",
+ "N1. This load will load from ",
+ "N2 BUT N2 must be killed after the decision to load from it has ",
+ "been made. tricky."];
+
+late_load_leads_to_hanging(suite) -> [];
+late_load_leads_to_hanging(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+
+ Tab = late_load_table,
+ Def = [{attributes, [key, value]},
+ {disc_copies, Nodes}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 111, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 222, 42})),
+
+ DebugId = {mnesia_loader, do_get_network_copy},
+ DebugFun = fun(PrevContext, EvalContext) ->
+ ?verbose("interrupt late load, pid ~p #~p ~n context ~p ~n",
+ [self(), PrevContext, EvalContext]),
+ mnesia_test_lib:kill_mnesia([Node2]),
+ ?verbose("interrupt late load - continues ~n",[]),
+ ?deactivate_debug_fun(DebugId),
+ PrevContext+1
+ end,
+
+ ?remote_activate_debug_fun(Node1,DebugId, DebugFun, 1),
+ mnesia_test_lib:kill_mnesia([Node1]),
+ %% wait a while, so that mnesia is really down
+ timer:sleep(timer:seconds(1)),
+
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab, 333, 666}])),
+
+ %% start Mnesia on node1
+ ?match(ok, mnesia:start()),
+
+ ?match({timeout, [Tab]}, mnesia:wait_for_tables([Tab], timer:seconds(2))),
+
+ ?match({'EXIT', {aborted, _}}, mnesia:dirty_read({Tab, 222})),
+ %% mnesia on node1 is waiting for node2 coming up
+
+ ?match(ok, rpc:call(Node2, mnesia, start, [])),
+ ?match(ok, mnesia:wait_for_tables([Tab], timer:seconds(30))),
+ ?match([{Tab, 333, 666}], mnesia:dirty_read({Tab, 333})),
+ ?verify_mnesia([Node2, Node1], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+force_load_when_nobody_intents_to_load(doc) ->
+ ["Normal force load. Start N1 N2, kill in N1, N2 order. Start N1 do ",
+ "force load. Did it work?"];
+force_load_when_nobody_intents_to_load(suite) -> [];
+force_load_when_nobody_intents_to_load(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+ Table = test_rec,
+ Trec1a = #test_rec{key=1,val=111},
+ Trec1b = #test_rec{key=1,val=333},
+ Trec2a = #test_rec{key=2,val=222},
+ Trec3a = #test_rec{key=3,val=333},
+ Trec3b = #test_rec{key=3,val=666},
+
+ ?match({atomic,ok}, rpc:call(N1, mnesia,create_table,
+ [Table,
+ [{disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}
+ ] ] ) ),
+ ?match( [], mnesia:table_info(Table,ram_copies) ),
+ ?match( Nodes, mnesia:table_info(Table,disc_copies) ),
+ ?match( [], mnesia:table_info(Table,disc_only_copies) ),
+ Write_one = fun(Rec) -> mnesia:write(Rec) end,
+ Read_one = fun(Key) -> mnesia:read({Table, Key}) end,
+ %%Write one value
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[Trec1a]])),
+ %%Check it
+ ?match({atomic,[Trec1a]},rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ %%Shut down mnesia on N1
+ ?match([], mnesia_test_lib:stop_mnesia([N1])),
+ %%Write and check value while N1 is down
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[Trec1b]])),
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[Trec2a]])),
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[Trec3a]])),
+ ?match({aborted,{node_not_running,N1}},
+ rpc:call(N1,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[Trec1b]},rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[Trec2a]},rpc:call(N2,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[Trec3a]},rpc:call(N2,mnesia,transaction,[Read_one,[3]]) ),
+ %%Shut down Mnesia on N2
+ ?match([], mnesia_test_lib:stop_mnesia([N2])),
+
+ %%Restart Mnesia on N1
+ ?match(ok, rpc:call(N1, mnesia, start, [])),
+ %%Check that table is not available (waiting for N2)
+ ?match({timeout,[Table]},
+ rpc:call(N1, mnesia, wait_for_tables, [[Table], 3000])),
+
+ %%Force load on N1
+ ?match(yes,rpc:call(N1,mnesia,force_load_table,[Table])),
+ %%Check values
+ ?match({atomic,[Trec1a]},rpc:call(N1,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[]}, rpc:call(N1,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[]}, rpc:call(N1,mnesia,transaction,[Read_one,[3]]) ),
+ %%Write a value for key=3
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[Trec3b]])),
+
+ %%Restart N2 and check values
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[Table], 30000])),
+
+ ?match({atomic,[Trec1a]},rpc:call(N1,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[Trec1a]},rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+
+ ?match({atomic,[]},rpc:call(N1,mnesia,transaction,[Read_one,[2]]) ),
+ ?match({atomic,[]},rpc:call(N2,mnesia,transaction,[Read_one,[2]]) ),
+
+ ?match({atomic,[Trec3b]},rpc:call(N1,mnesia,transaction,[Read_one,[3]]) ),
+ ?match({atomic,[Trec3b]},rpc:call(N2,mnesia,transaction,[Read_one,[3]]) ),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+force_load_when_someone_has_decided_to_load(doc) ->
+ ["Difficult case that needs instrumentation of Mnesia.",
+ "Start N1 and N2, replicate table, kill in N1, N2 order. Start N2 ",
+ "and start N1 before N2 has really loaded the table but after N2 has ",
+ "decided to load it."];
+
+force_load_when_someone_has_decided_to_load(suite) -> [];
+force_load_when_someone_has_decided_to_load(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ {success, [A, B]} = ?start_activities(Nodes),
+ ?match(Node1, node(A)), %% Just to check :)
+ ?match(Node2, node(B)),
+
+ Tab = late_load_table,
+ Def = [{attributes, [key, value]}, {disc_copies, Nodes}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 111, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 222, 42})),
+
+ Self = self(),
+ DebugId = {mnesia_controller, late_disc_load},
+ DebugFun = fun(PrevContext, EvalContext) ->
+ ?verbose("interrupt late disc load,
+ pid ~p #~p ~n context ~p ~n",
+ [self(),PrevContext,EvalContext]),
+ Self ! {self(), fun_in_postion},
+ wait_for_signal(),
+ ?verbose("interrupt late disc load - continues ~n",[]),
+ ?deactivate_debug_fun(DebugId),
+ PrevContext+1
+ end,
+
+ %% kill mnesia on node1
+ mnesia_test_lib:kill_mnesia([Node1]),
+ %% wait a while, so that mnesia is really down
+ timer:sleep(timer:seconds(1)),
+
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab, 222, 815}])),
+ %% kill mnesia on node2
+ mnesia_test_lib:kill_mnesia([Node2]),
+ %% wait a while, so that mnesia is really down
+ timer:sleep(timer:seconds(1)),
+
+ ?remote_activate_debug_fun(Node2,DebugId, DebugFun, 1),
+
+ B ! fun() -> mnesia:start() end,
+ [{Mnesia_Pid, fun_in_postion}] = receive_messages([fun_in_postion]),
+
+ %% start Mnesia on node1
+ A ! fun() -> mnesia:start() end,
+ ?match_receive(timeout),
+% Got some problem with this testcase when we modified mnesia init
+% These test cases are very implementation dependent!
+% A ! fun() -> mnesia:wait_for_tables([Tab], 3000) end,
+% ?match_receive({A, {timeout, [Tab]}}),
+ A ! fun() -> mnesia:force_load_table(Tab) end,
+ ?match_receive(timeout),
+
+ Mnesia_Pid ! continue,
+ ?match_receive({B, ok}),
+ ?match_receive({A, ok}),
+ ?match_receive({A, yes}),
+
+ B ! fun() -> mnesia:wait_for_tables([Tab], 10000) end,
+ ?match_receive({B, ok}),
+ ?match(ok, mnesia:wait_for_tables([Tab], timer:seconds(30))),
+ ?match([{Tab, 222, 815}], mnesia:dirty_read({Tab, 222})),
+ ?verify_mnesia(Nodes, []).
+
+wait_for_signal() ->
+ receive
+ continue -> ok
+ %% Don't eat any other mnesia internal msg's
+ after
+ timer:minutes(2) -> ?error("Timedout in wait_for_signal~n", [])
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+force_load_when_someone_else_already_has_loaded(doc) ->
+ ["Normal case. Do a force load when somebody else has loaded the table. ",
+ "Start N1, N2, kill in N1, N2 order. Start N2 load the table, start N1 ",
+ "force load. Did it work? (i.e: did N1 load the table from N2 as that",
+ "one is the latest version and it is available on N2)"];
+
+force_load_when_someone_else_already_has_loaded(suite) -> [];
+force_load_when_someone_else_already_has_loaded(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+ Table = test_rec,
+ Trec1 = #test_rec{key=1,val=111},
+ Trec2 = #test_rec{key=1,val=222},
+
+ ?match({atomic,ok}, rpc:call(N1, mnesia,create_table,
+ [Table,
+ [{disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}
+ ] ] ) ),
+ ?match( [], mnesia:table_info(Table,ram_copies) ),
+ ?match( Nodes, mnesia:table_info(Table,disc_copies) ),
+ ?match( [], mnesia:table_info(Table,disc_only_copies) ),
+ Write_one = fun(Rec) -> mnesia:write(Rec) end,
+ Read_one = fun(Key) -> mnesia:read({Table, Key}) end,
+ %%Write one value
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[Trec1]])),
+ %%Check it
+ ?match({atomic,[Trec1]},rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ %%Shut down mnesia
+ ?match([], mnesia_test_lib:stop_mnesia([N1])),
+ timer:sleep(500),
+ ?match([], mnesia_test_lib:stop_mnesia([N2])),
+ %%Restart Mnesia on N2;wait for tables to load
+ ?match(ok, rpc:call(N2, mnesia, start, [])),
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [[test_rec], 30000])),
+ %%Write one value
+ ?match({atomic,ok},rpc:call(N2,mnesia,transaction,[Write_one,[Trec2]])),
+ %%Start on N1; force load
+ ?match(ok, rpc:call(N1, mnesia, start, [])),
+ %%Force load from file
+ ?match(yes, rpc:call(N1,mnesia,force_load_table,[Table])),
+ %%Check the value
+ ?match({atomic,[Trec2]},rpc:call(N1,mnesia,transaction,[Read_one,[1]]) ),
+ %% === there must be a Trec2 here !!!!
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+force_load_when_we_has_loaded(doc) ->
+ ["Force load a table we already have loaded"];
+force_load_when_we_has_loaded(suite) -> [];
+force_load_when_we_has_loaded(Config) when is_list(Config) ->
+ [N1] = Nodes = ?acquire_nodes(1, Config),
+ Table = test_rec,
+ Trec1 = #test_rec{key=1,val=111},
+ Trec2 = #test_rec{key=1,val=222},
+
+ ?match({atomic,ok}, rpc:call(N1, mnesia,create_table,
+ [Table,
+ [{disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}
+ ] ] ) ),
+ ?match( [], mnesia:table_info(Table,ram_copies) ),
+ ?match( Nodes, mnesia:table_info(Table,disc_copies) ),
+ ?match( [], mnesia:table_info(Table,disc_only_copies) ),
+ Write_one = fun(Rec) -> mnesia:write(Rec) end,
+ Read_one = fun(Key) -> mnesia:read({Table, Key}) end,
+ %%Write one value
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[Trec1]])),
+ %%Check it
+ ?match({atomic,[Trec1]},rpc:call(N1,mnesia,transaction,[Read_one,[1]]) ),
+ %%Shut down mnesia
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+ %%Restart Mnesia;wait for tables to load
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Table])),
+ %%Write one value
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[Trec2]])),
+ %%Force load from file
+ ?match(yes, rpc:call(N1,mnesia,force_load_table,[Table])),
+ %%Check the value
+ ?match({atomic,[Trec2]},rpc:call(N1,mnesia,transaction,[Read_one,[1]]) ),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+force_load_on_a_non_local_table(doc) ->
+ ["This is NOT allowed, the test case is a negative test",
+ "Force load on a table that isn't replicated on this node."];
+force_load_on_a_non_local_table(suite) -> [];
+force_load_on_a_non_local_table(Config) when is_list(Config) ->
+ [N1, N2, N3] = Nodes = ?acquire_nodes( 3, Config),
+ TableNodes = lists:sublist(Nodes,2),
+ Table = test_rec,
+ Trec1 = #test_rec{key=1,val=11},
+
+ ?match({atomic,ok}, rpc:call(N1, mnesia,create_table,
+ [Table,
+ [{disc_copies,TableNodes},
+ {attributes,record_info(fields,test_rec)}
+ ] ] ) ),
+ ?match( [], mnesia:table_info(Table,ram_copies) ),
+ ?match( TableNodes, mnesia:table_info(Table,disc_copies) ),
+ ?match( [], mnesia:table_info(Table,disc_only_copies) ),
+ Write_one = fun(Rec) -> mnesia:write(Rec) end,
+ Read_one = fun(Key) -> mnesia:read({Table, Key}) end,
+ %%Write one value
+ ?match({atomic,ok},rpc:call(N1,mnesia,transaction,[Write_one,[Trec1]])),
+ %%Check it from the other nodes
+ ?match({atomic,[Trec1]},rpc:call(N2,mnesia,transaction,[Read_one,[1]]) ),
+ ?match({atomic,[Trec1]},rpc:call(N3,mnesia,transaction,[Read_one,[1]]) ),
+
+ %%Make sure that Table is non-local
+ ?match_inverse(N3, rpc:call(N3,mnesia,table_info,[Table,where_to_read])),
+ %%Try to force load it
+ ?match(yes, rpc:call(N3,mnesia,force_load_table,[Table])),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+force_load_when_the_table_does_not_exist(doc) ->
+ ["This is NOT allowed, the test case is a negative test",
+ "Force load on a table that doesn't exist."];
+force_load_when_the_table_does_not_exist(suite) -> [];
+force_load_when_the_table_does_not_exist(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes( 2, Config),
+
+ %%Dummy table
+ ?match({atomic,ok},
+ mnesia:create_table(test_rec,
+ [{disc_copies,Nodes},
+ {attributes,record_info(fields,test_rec)}]
+ ) ),
+ ?match( [], mnesia:table_info(test_rec,ram_copies) ),
+ ?match( Nodes, mnesia:table_info(test_rec,disc_copies) ),
+ ?match( [], mnesia:table_info(test_rec,disc_only_copies) ),
+ Tab = dummy,
+ %%Make sure that Tab is an unknown table
+ ?match( false, lists:member(Tab,mnesia:system_info(tables)) ),
+ ?match( {error, {no_exists, Tab}}, mnesia:force_load_table(Tab) ),
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+-define(SDwrite(Tup), fun() -> mnesia:write(Tup) end).
+
+master_nodes(suite) -> [];
+master_nodes(Config) when is_list(Config) ->
+ [A, B, C] = Nodes = ?acquire_nodes(3, Config),
+ Tab = test_table_master_nodes,
+ ?match({atomic,ok}, mnesia:create_table(Tab, [{disc_copies, Nodes}])),
+
+ %% Test one: Master A and the table should be loaded from A
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [A]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match([{Tab, 1, init}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+ %% Test 2: Master [A,B] and B is Up the table should be loaded from B
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [A, B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match([{Tab, 1, updated}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+ %% Test 3: Master [A,B] and B is down the table should be loaded from A
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [A, B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ mnesia_test_lib:stop_mnesia([B]),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match(ok, rpc:call(B, mnesia, start, [])),
+ ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match([{Tab, 1, init}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, _Unknown}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+ %% Test 4: Master [B] and B is Up the table should be loaded from B
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match([{Tab, 1, updated}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+ %% Test 5: Master [B] and B is down the table should not be loaded
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ mnesia_test_lib:stop_mnesia([B]),
+ ?match({atomic, ok}, rpc:call(C, mnesia, sync_transaction, [?SDwrite({Tab, 1, update_2})])),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 2000])),
+
+ %% Test 6: Force load on table that couldn't be loaded due to master
+ %% table setttings, loads other active replicas i.e. from C
+
+ ?match(yes, rpc:call(A, mnesia, force_load_table, [Tab])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match(ok, rpc:call(B, mnesia, start, [])),
+ ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?match([{Tab, 1, update_2}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, update_2}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, update_2}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+ %% Test 7: Master [B] and B is down the table should not be loaded,
+ %% force_load when there are no active replicas availible
+ %% should generate a load of a local table
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ mnesia_test_lib:stop_mnesia([B, C]),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 2000])),
+
+ ?match(yes, rpc:call(A, mnesia, force_load_table, [Tab])),
+ ?match([{Tab, 1, init}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+
+ ?verify_mnesia([A], [B,C]).
+
+starting_master_nodes(suite) -> [];
+starting_master_nodes(doc) ->
+ ["Complementory to TEST 5 and 6 above, if the master node (B) starts"
+ " and loads the table it should be loaded on the waiting node (A) "];
+starting_master_nodes(Config) when is_list(Config) ->
+ [A, B, C] = Nodes = ?acquire_nodes(3, Config),
+ Tab = starting_master_nodes,
+ ?match({atomic,ok}, mnesia:create_table(Tab, [{disc_copies, Nodes}])),
+ %% Start by checking TEST 5 above.
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+ mnesia_test_lib:stop_mnesia([A]),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ mnesia_test_lib:stop_mnesia([B]),
+ ?match({atomic, ok}, rpc:call(C, mnesia, sync_transaction, [?SDwrite({Tab, 1, update_2})])),
+
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 2000])),
+ %% Start the B node and the table should be loaded on A!
+ ?match(ok, rpc:call(B, mnesia, start, [])),
+ ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+
+ ?verify_mnesia([A,B,C], []).
+
+
+master_on_non_local_tables(suite) -> [];
+master_on_non_local_tables(Config) when is_list(Config) ->
+ [A, B, C] = Nodes = ?acquire_nodes(3, Config),
+ Tab = test_table_non_local,
+ ?match({atomic,ok}, mnesia:create_table(Tab, [{disc_copies, [B, C]}])),
+
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [B]])),
+ ?match({atomic, ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+ %% Test 1: Test that table info are updated when master node comes up
+
+ mnesia_test_lib:stop_mnesia([A, B]),
+ ?match({atomic, ok}, rpc:call(C, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+
+ ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 2000])),
+ ErrorRead = {badrpc,{'EXIT', {aborted,{no_exists,[test_table_non_local,1]}}}},
+ ErrorWrite = {badrpc,{'EXIT', {aborted,{no_exists,test_table_non_local}}}},
+ ?match(ErrorRead, rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match(ErrorWrite, rpc:call(A, mnesia, dirty_write, [{Tab, 1, updated_twice}])),
+
+ ?match(ok, rpc:call(B, mnesia, start, [])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 2000])),
+
+ ?match([{Tab, 1, updated}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match(B, rpc:call(A, mnesia, table_info, [Tab, where_to_read])),
+ ?match({atomic, ok}, rpc:call(A, mnesia, sync_transaction, [?SDwrite({Tab, 1, init})])),
+
+ %% Test 2: Test that table info are updated after force_load
+
+ mnesia_test_lib:stop_mnesia([A, B]),
+ ?match({atomic, ok}, rpc:call(C, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated})])),
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+
+ ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 2000])),
+ ?match(yes, rpc:call(A, mnesia, force_load_table, [Tab])),
+ ?match(C, rpc:call(A, mnesia, table_info, [Tab, where_to_read])),
+
+ ?match([{Tab, 1, updated}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match({atomic, ok}, rpc:call(A, mnesia, sync_transaction, [?SDwrite({Tab, 1, updated_twice})])),
+
+ ?match(ok, rpc:call(B, mnesia, start, [])),
+ ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 10000])),
+
+ ?match([{Tab, 1, updated_twice}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated_twice}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{Tab, 1, updated_twice}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+ ?verify_mnesia(Nodes, []).
+
+remote_force_load_with_local_master_node(doc) ->
+ ["Force load a table on a remote node while the ",
+ "local node is down. Start the local node and ",
+ "verfify that the tables is loaded from disc locally "
+ "if the local node has itself as master node and ",
+ "the remote node has both the local and remote node ",
+ "as master nodes"];
+remote_force_load_with_local_master_node(suite) -> [];
+remote_force_load_with_local_master_node(Config) when is_list(Config) ->
+ [A, B] = Nodes = ?acquire_nodes(2, Config),
+
+ Tab = remote_force_load_with_local_master_node,
+ ?match({atomic,ok}, mnesia:create_table(Tab, [{disc_copies, Nodes}])),
+ ?match(ok, rpc:call(A, mnesia, set_master_nodes, [Tab, [A, B]])),
+ ?match(ok, rpc:call(B, mnesia, set_master_nodes, [Tab, [B]])),
+
+ W = fun(Who) -> mnesia:write({Tab, who, Who}) end,
+ ?match({atomic, ok}, rpc:call(A,mnesia, sync_transaction, [W, [a]])),
+ ?match(stopped, rpc:call(A, mnesia, stop, [])),
+ ?match({atomic, ok}, rpc:call(B, mnesia, sync_transaction, [W, [b]])),
+ ?match(stopped, rpc:call(B, mnesia, stop, [])),
+
+ ?match(ok, rpc:call(A, mnesia, start, [])),
+ ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+ ?match([{Tab, who, a}], rpc:call(A, mnesia, dirty_read, [{Tab, who}])),
+
+ ?match(ok, rpc:call(B, mnesia, start, [])),
+ ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+ ?match([{Tab, who, b}], rpc:call(B, mnesia, dirty_read, [{Tab, who}])),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dump_ram_copies(doc) ->
+ ["Check that ram_copies tables are loaded with the"
+ "contents that had been dumped before Mnesia",
+ "was restarted. " ];
+dump_ram_copies(suite) -> [];
+dump_ram_copies(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, Config),
+ {success, [P1,P2,P3]} = ?start_activities(Nodes),
+
+ NP1 = node(P1),
+ NP2 = node(P2),
+
+ {A,B,C} = case node() of
+ NP1 ->
+ %?verbose("first case ~n"),
+ {P3,P2,P1};
+ NP2 ->
+ %?verbose("second case ~n"),
+ {P3,P1,P2};
+ _ ->
+ {P1,P2,P3}
+ end,
+
+ Node1 = node(A),
+ Node2 = node(B),
+ Node3 = node(C),
+
+ ?verbose(" A pid:~p node:~p ~n",[A,Node1]),
+ ?verbose(" B pid:~p node:~p ~n",[B,Node2]),
+ ?verbose(" C pid:~p node:~p ~n",[C,Node3]),
+
+
+ %% ram copies table on 2 nodes
+
+ Tab = dump_table,
+ Def = [{attributes, [key, value]},
+ {ram_copies, [Node1,Node2]}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ %% dump the table
+
+ ?match( {atomic,ok}, mnesia:dump_tables([Tab])),
+
+ %% perform updates (they shall be lost after kill Mnesia )
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 815})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 915})),
+
+ %% add another replica on node3
+ mnesia:add_table_copy(Tab,Node3,ram_copies),
+
+ %% all 3 replicas shall have the new contents
+ cross_check_tables([A,B,C],Tab,
+ {[{Tab,1,815}],[{Tab,2,915}],[{Tab,3,256}]}),
+
+ %% kill mnesia on node 3
+ mnesia_test_lib:kill_mnesia([Node3]),
+
+ %% wait a while, so that mnesia is really down
+ timer:sleep(timer:seconds(2)),
+
+ mnesia_test_lib:kill_mnesia([Node1,Node2]), %% kill them as well
+ timer:sleep(timer:seconds(2)),
+
+ %% start Mnesia only on node 3
+ ?verbose("starting mnesia on Node3~n",[]),
+
+ %% test_lib:mnesia_start doesnt work, because it waits
+ %% for the schema on all nodes ... ???
+ ?match(ok,rpc:call(Node3,mnesia,start,[]) ),
+ ?match(ok,rpc:call(Node3,mnesia,wait_for_tables,
+ [[Tab],timer:seconds(30)] ) ),
+
+ %% node3 shall have the conents of the dump
+ cross_check_tables([C],Tab,{[{Tab,1,4711}],[{Tab,2,42}],[{Tab,3,256}]}),
+
+ %% start Mnesia on the other 2 nodes, too
+ mnesia_test_lib:start_mnesia([Node1,Node2],[Tab]),
+
+ cross_check_tables([A,B,C],Tab,
+ {[{Tab,1,4711}],[{Tab,2,42}],[{Tab,3,256}]}),
+ ?verify_mnesia(Nodes, []).
+
+%% check the contents of the table
+
+cross_check_tables([],_tab,_elements) -> ok;
+cross_check_tables([Pid|Rest],Tab,{Val1,Val2,Val3}) ->
+ Pid ! fun () ->
+ R1 = mnesia:dirty_read({Tab,1}),
+ R2 = mnesia:dirty_read({Tab,2}),
+ R3 = mnesia:dirty_read({Tab,3}),
+ {R1,R2,R3}
+ end,
+ ?match_receive({ Pid, {Val1, Val2, Val3 } }),
+ cross_check_tables(Rest,Tab,{Val1,Val2,Val3} ).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Should be in evil test suite !!!
+
+dump_disc_copies(doc) ->
+ ["Check that it is not possible to dump disc_copies tables"];
+dump_disc_copies(suite) -> [];
+dump_disc_copies(Config) when is_list(Config) ->
+ do_dump_copies(Config, disc_copies).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Should be in evil test suite !!!
+dump_disc_only(doc) ->
+ ["Check that it is not possible to dump disc_only_copies tables"];
+dump_disc_only(suite) -> [];
+dump_disc_only(Config) when is_list(Config) ->
+ do_dump_copies(Config,disc_only_copies).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+do_dump_copies(Config,Copies) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+
+ Tab = dump_copies,
+ Def = [{attributes, [key, value]},
+ {Copies, [Node1]}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 4711})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 42})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, 256})),
+
+ %% dump the table
+ ?match( {aborted, {"Only allowed on ram_copies",Tab,[Node1]}},
+ mnesia:dump_tables([Tab])),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, 815})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 915})),
+
+ %% kill mnesia on node1
+ mnesia_test_lib:kill_mnesia([Node1]),
+
+ %% wait a while, so that mnesia is really down
+ timer:sleep(timer:seconds(1)),
+
+ mnesia_test_lib:start_mnesia([Node1],[Tab]),
+
+ ?match([{Tab, 1, 815}], mnesia:dirty_read({Tab,1}) ),
+ ?match([{Tab, 2, 915}], mnesia:dirty_read({Tab,2}) ),
+ ?match([{Tab, 3, 256}], mnesia:dirty_read({Tab,3}) ),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+durability_of_disc_copies(doc) ->
+ ["Perform all possible kinds of updates on tables and check"
+ "whether no data is lost after a restart of Mnesia.",
+ "This test is done for disc_copies"];
+
+durability_of_disc_copies(suite) -> [];
+durability_of_disc_copies(Config) when is_list(Config) ->
+ do_disc_durability(Config,disc_copies).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+durability_of_disc_only_copies(doc) ->
+ ["Perform all possible kinds of updates on tables and check"
+ "whether no data is lost after a restart of Mnesia.",
+ "This test is done for disc_only_copies"];
+durability_of_disc_only_copies(suite) -> [];
+durability_of_disc_only_copies(Config) when is_list(Config) ->
+ do_disc_durability(Config,disc_only_copies).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+do_disc_durability(Config,CopyType) ->
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(1)}]),
+ {success, [A,B,C]} = ?start_activities(Nodes),
+
+ Tab_set = disc_durability_set,
+ Def_set = [{attributes, [key, value]},
+ {CopyType, Nodes}],
+
+ Tab_bag = disc_durability_bag,
+ Def_bag = [{attributes, [key, value]},
+ {type, bag},
+ {CopyType, Nodes}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab_set, Def_set)),
+ ?match({atomic, ok}, mnesia:create_table(Tab_bag, Def_bag)),
+
+ %% do updates
+ ?match({atomic, ok},
+ mnesia:transaction(fun()->
+ mnesia:write({Tab_set, 11, 1111}),
+ mnesia:write({Tab_set, 22, 2222}),
+ mnesia:write({Tab_set, 33, 3333}),
+ mnesia:write({Tab_set, 55, 5555})
+ end)),
+ mnesia:dirty_write({Tab_set, 44, 4444}),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun()->
+ mnesia:write({Tab_bag, 11, a_1111}),
+ mnesia:write({Tab_bag, 11, b_1111}),
+ mnesia:write({Tab_bag, 22, a_2222}),
+ mnesia:write({Tab_bag, 22, b_2222}),
+ mnesia:write({Tab_bag, 33, a_3333}),
+ mnesia:write({Tab_bag, 33, b_3333})
+ end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun()-> mnesia:delete({Tab_set, 22}) end)),
+ ?match(ok, mnesia:dirty_delete({Tab_set, 33})),
+ ?match(5558, mnesia:dirty_update_counter({Tab_set, 55}, 3)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun()->
+ mnesia:delete_object({Tab_bag, 22, b_2222})
+ end)),
+ ?match(ok, mnesia:dirty_delete_object({Tab_bag, 33, b_3333})),
+ ?match(10, mnesia:dirty_update_counter({Tab_set, counter}, 10)),
+ ?match({atomic, ok}, % Also syncs update_counter
+ mnesia:sync_transaction(fun() -> mnesia:write({Tab_set,66,6666}) end)),
+
+ Updated = {[[{Tab_set,counter,10}],
+ [{Tab_set,counter,10}],
+ [{Tab_set,counter,10}]],[]},
+ ?match(Updated, rpc:multicall(Nodes, mnesia, dirty_read, [Tab_set,counter])),
+
+ %% kill mnesia on all nodes, start it again and check the data
+ mnesia_test_lib:kill_mnesia(Nodes),
+ mnesia_test_lib:start_mnesia(Nodes,[Tab_set,Tab_bag]),
+
+ ?log("Flushed ~p ~n", [mnesia_test_lib:flush()]), %% Debugging strange msgs..
+ ?log("Processes ~p ~p ~p~n", [A,B,C]),
+ check_tables([A,B,C],
+ [{Tab_set,11}, {Tab_set,22},{Tab_set,33},
+ {Tab_set,44},{Tab_set,55}, {Tab_set,66},
+ {Tab_bag,11}, {Tab_bag,22},{Tab_bag,33},
+ {Tab_set, counter}],
+ [[{Tab_set, 11, 1111}], [], [], [{Tab_set, 44, 4444}],
+ [{Tab_set, 55, 5558}], [{Tab_set, 66, 6666}],
+ lists:sort([{Tab_bag, 11, a_1111},{Tab_bag, 11, b_1111}]),
+ [{Tab_bag, 22, a_2222}], [{Tab_bag, 33, a_3333}],
+ [{Tab_set, counter, 10}]]),
+
+ timer:sleep(1000), %% Debugging strange msgs..
+ ?log("Flushed ~p ~n", [mnesia_test_lib:flush()]),
+ ?verify_mnesia(Nodes, []).
+
+%% check the contents of the table
+%%
+%% all the processes in the PidList shall find all
+%% table entries in ValList
+
+check_tables([],_vallist,_resultList) -> ok;
+check_tables([Pid|Rest],ValList,ResultList) ->
+ Pid ! fun () ->
+ check_values(ValList)
+ end,
+ ?match_receive({ Pid, ResultList }),
+ check_tables(Rest,ValList,ResultList).
+
+check_values([]) -> [];
+check_values([{Tab,Key}|Rest]) ->
+ Ret = lists:sort(mnesia:dirty_read({Tab,Key})),
+ [Ret|check_values(Rest)].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% stolen from mnesia_recovery_test.erl:
+
+receive_messages([]) -> [];
+receive_messages(ListOfMsgs) ->
+ receive
+ timeout ->
+ case lists:member(timeout, ListOfMsgs) of
+ false ->
+ ?warning("I (~p) have received unexpected msg~n ~p ~n",
+ [self(),timeout]),
+ receive_messages(ListOfMsgs);
+ true ->
+ ?verbose("I (~p) got msg ~p ~n", [self(),timeout]),
+ [ timeout | receive_messages(ListOfMsgs -- [timeout])]
+ end;
+
+ {Pid, Msg} ->
+ case lists:member(Msg, ListOfMsgs) of
+ false ->
+ ?warning("I (~p) have received unexpected msg~n ~p ~n",
+ [self(),{Pid, Msg}]),
+ receive_messages(ListOfMsgs);
+ true ->
+ ?verbose("I (~p) got msg ~p from ~p ~n", [self(),Msg, Pid]),
+ [{Pid, Msg} | receive_messages(ListOfMsgs -- [Msg])]
+ end;
+
+ Else -> ?warning("Recevied unexpected Msg~n ~p ~n", [Else])
+ after timer:seconds(40) ->
+ ?error("Timeout in receive msgs while waiting for ~p~n",
+ [ListOfMsgs])
+ end.
+
diff --git a/lib/mnesia/test/mnesia_evil_backup.erl b/lib/mnesia/test/mnesia_evil_backup.erl
new file mode 100644
index 0000000000..63f4146d98
--- /dev/null
+++ b/lib/mnesia/test/mnesia_evil_backup.erl
@@ -0,0 +1,738 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%%----------------------------------------------------------------------
+%%% File : mnesia_evil_backup.erl
+%%% Author : Dan Gudmundsson <dgud@legolas>
+%%% Purpose : Evil backup tests
+%%% Created : 3 Jun 1998 by Dan Gudmundsson <[email protected]>
+%%%----------------------------------------------------------------------
+
+-module(mnesia_evil_backup).
+-author('[email protected]').
+-compile(export_all).
+-include("mnesia_test_lib.hrl").
+
+%%-export([Function/Arity, ...]).
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all() ->
+ [backup, bad_backup, global_backup_checkpoint,
+ {group, restore_tables}, traverse_backup,
+ selective_backup_checkpoint,
+ incremental_backup_checkpoint, install_fallback,
+ uninstall_fallback, local_fallback,
+ sops_with_checkpoint].
+
+groups() ->
+ [{restore_tables, [],
+ [restore_errors, restore_clear, restore_keep,
+ restore_recreate, restore_clear_ram]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+backup(doc) -> ["Checking the interface to the function backup",
+ "We don't check that the backups can be used here",
+ "That is checked in install_fallback and in restore"];
+backup(suite) -> [];
+backup(Config) when is_list(Config) ->
+ [Node1, Node2] = _Nodes = ?acquire_nodes(2, Config),
+ Tab = backup_tab,
+ Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
+ File = "backup_test.BUP",
+ ?match(ok, mnesia:backup(File)),
+
+ File2 = "backup_test2.BUP",
+ Tab2 = backup_tab2,
+ Def2 = [{disc_only_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match(ok, mnesia:backup(File2, mnesia_backup)),
+
+ File3 = "backup_test3.BUP",
+ mnesia_test_lib:kill_mnesia([Node2]),
+ ?match({error, _}, mnesia:backup(File3, mnesia_backup)),
+
+ ?match(ok, file:delete(File)),
+ ?match(ok, file:delete(File2)),
+ ?match({error, _}, file:delete(File3)),
+ ?verify_mnesia([Node1], [Node2]).
+
+
+bad_backup(suite) -> [];
+bad_backup(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = backup_tab,
+ Def = [{disc_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
+ File = "backup_test.BUP",
+ ?match(ok, mnesia:backup(File)),
+ file:write_file(File, "trash", [append]),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_bad})),
+ ?match({atomic,[Tab]}, mnesia:restore(File, [{clear_tables, [Tab]}])),
+ ?match([{Tab,1,test_ok}], mnesia:dirty_read(Tab, 1)),
+
+ ?match(ok, file:delete(File)),
+ ?verify_mnesia([Node1], []).
+
+
+
+global_backup_checkpoint(doc) ->
+ ["Checking the interface to the function backup_checkpoint",
+ "We don't check that the backups can be used here",
+ "That is checked in install_fallback and in restore"];
+global_backup_checkpoint(suite) -> [];
+global_backup_checkpoint(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = backup_cp,
+ Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
+ File = "backup_checkpoint.BUP",
+ File2 = "backup_checkpoint2.BUP",
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
+ ?match({error, _}, mnesia:backup_checkpoint(cp_name, File)),
+ Spec = [{name, cp_name}, {max, mnesia:system_info(tables)}],
+ ?match({ok, _Name, _Ns}, mnesia:activate_checkpoint(Spec)),
+ ?match(ok, mnesia:backup_checkpoint(cp_name, File)),
+ ?match({error, _}, mnesia:backup_checkpoint(cp_name_nonexist, File)),
+ ?match(ok, mnesia:backup_checkpoint(cp_name, File2, mnesia_backup)),
+ ?match({error, _}, file:delete(File)),
+ ?match(ok, file:delete(File2)),
+ ?verify_mnesia(Nodes, []).
+
+
+restore_errors(suite) -> [];
+restore_errors(Config) when is_list(Config) ->
+ [_Node] = ?acquire_nodes(1, Config),
+ ?match({aborted, enoent}, mnesia:restore(notAfile, [])),
+ ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, not_a_list)),
+ ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [test_badarg])),
+ ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{test_badarg, xxx}])),
+ ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{skip_tables, xxx}])),
+ ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{recreate_tables, [schema]}])),
+ ?match({aborted, {badarg, _}}, mnesia:restore(notAfile, [{default_op, asdklasd}])),
+ ok.
+
+restore_clear(suite) -> [];
+restore_clear(Config) when is_list(Config) ->
+ restore(Config, clear_tables).
+
+restore_keep(suite) -> [];
+restore_keep(Config) when is_list(Config) ->
+ restore(Config, keep_tables).
+
+restore_recreate(suite) -> [];
+restore_recreate(Config) when is_list(Config) ->
+ restore(Config, recreate_tables).
+
+check_tab(Records, Line) ->
+ Verify = fun({Table, Key, Val}) ->
+ case catch mnesia:dirty_read({Table, Key}) of
+ [{Table, Key, Val}] -> ok;
+ Else ->
+ mnesia_test_lib:error("Not matching on Node ~p ~n"
+ " Expected ~p~n Actual ~p~n",
+ [node(), {Table, Key, Val}, Else],
+ ?MODULE, Line),
+ exit(error)
+ end;
+ (Recs) ->
+ [{Tab, Key, _}, _] = Recs,
+ SRecs = lists:sort(Recs),
+ R_Recs = lists:sort(catch mnesia:dirty_read({Tab, Key})),
+ case R_Recs of
+ SRecs -> ok;
+ Else ->
+ mnesia_test_lib:error("Not matching on Node ~p ~n"
+ " Expected ~p~n Actual ~p~n",
+ [node(), SRecs, Else],
+ ?MODULE, Line),
+ exit(error)
+ end
+ end,
+ lists:foreach(Verify, Records).
+
+restore(Config, Op) ->
+ [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
+
+ Tab1 = ram_snmp,
+ Def1 = [{snmp, [{key, integer}]}, {ram_copies, [Node1]}],
+ Tab2 = disc_index,
+ Def2 = [{index, [val]}, {disc_copies, [Node1, Node2]}],
+ Tab3 = dionly_bag,
+ Def3 = [{type, bag}, {disc_only_copies, Nodes}],
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+
+ File1 = "restore1.BUP",
+ File2 = "restore2.BUP",
+
+ Restore = fun(O, A) ->
+ case mnesia:restore(O, A) of
+ {atomic, Tabs} when is_list(Tabs) -> {atomic, lists:sort(Tabs)};
+ Other -> Other
+ end
+ end,
+ Tabs = lists:sort([Tab1, Tab2, Tab3]),
+
+ [mnesia:dirty_write({Tab1, N, N+42}) || N <- lists:seq(1, 10)],
+ [mnesia:dirty_write({Tab2, N, N+43}) || N <- lists:seq(1, 10)],
+ [mnesia:dirty_write({Tab3, N, N+44}) || N <- lists:seq(1, 10)],
+
+ Res1 = [{Tab1, N, N+42} || N <- lists:seq(1, 10)],
+ Res2 = [{Tab2, N, N+43} || N <- lists:seq(1, 10)],
+ Res3 = [{Tab3, N, N+44} || N <- lists:seq(1, 10)],
+
+ {ok, Name, _} = mnesia:activate_checkpoint([{min, Tabs}, {ram_overrides_dump, true}]),
+ file:delete(File1),
+
+ %% Test standard Restore on one table on one node
+ ?match(ok, mnesia:backup_checkpoint(Name, File1)),
+ ?match(ok, mnesia:deactivate_checkpoint(Name)),
+ ?match(ok, mnesia:backup(File2)),
+ [mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)],
+ [mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)],
+ [mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)],
+ _Res11 = [{Tab1, N, N+1} || N <- lists:seq(1, 11)],
+ Res21 = [{Tab2, N, N+1} || N <- lists:seq(1, 11)],
+ Res31 = [[{Tab3, N, N+1}, {Tab3, N, N+44}] || N <- lists:seq(1, 10)],
+
+ ?match({atomic, [Tab1]}, Restore(File1, [{Op, [Tab1]},
+ {skip_tables, Tabs -- [Tab1]}])),
+ case Op of
+ keep_tables ->
+ ?match([{Tab1, 11, 12}], mnesia:dirty_read({Tab1, 11}));
+ clear_tables ->
+ ?match([], mnesia:dirty_read({Tab1, 11}));
+ recreate_tables ->
+ ?match([], mnesia:dirty_read({Tab1, 11}))
+ end,
+ [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res21, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res31, ?LINE]) || Node <- Nodes],
+
+ %% Restore all tables on it's nodes
+ mnesia_schema:clear_table(Tab1),
+ mnesia_schema:clear_table(Tab2),
+ mnesia_schema:clear_table(Tab3),
+ [mnesia:dirty_write({Tab1, N, N+1}) || N <- lists:seq(1, 11)],
+ [mnesia:dirty_write({Tab2, N, N+1}) || N <- lists:seq(1, 11)],
+ [mnesia:dirty_write({Tab3, N, N+1}) || N <- lists:seq(1, 11)],
+
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab2, Node1)),
+
+ ?match({ok, Node1}, mnesia:subscribe({table, Tab1})),
+
+ ?match({atomic, Tabs}, Restore(File1, [{default_op, Op},
+ {module, mnesia_backup}])),
+ case Op of
+ clear_tables ->
+ ?match_receive({mnesia_table_event, {delete, {schema, Tab1}, _}}),
+ ?match_receive({mnesia_table_event, {write, {schema, Tab1, _}, _}}),
+ check_subscr(Tab1),
+ [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res3, ?LINE]) || Node <- Nodes],
+ ?match([], mnesia:dirty_read({Tab1, 11})),
+ ?match([], mnesia:dirty_read({Tab2, 11})),
+ ?match([], mnesia:dirty_read({Tab3, 11})),
+ %% Check Index
+ ?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
+ ?match([], mnesia:dirty_index_read(Tab2, 11, val)),
+ %% Check Snmp
+ ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
+ ?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
+ ?match(undefined, mnesia:snmp_get_row(Tab1, [11])),
+ %% Check schema info
+ ?match([Node2], mnesia:table_info(Tab2, where_to_write));
+ keep_tables ->
+ check_subscr(Tab1),
+ [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res31, ?LINE]) || Node <- Nodes],
+ ?match([{Tab1, 11, 12}], mnesia:dirty_read({Tab1, 11})),
+ ?match([{Tab2, 11, 12}], mnesia:dirty_read({Tab2, 11})),
+ ?match([{Tab3, 11, 12}], mnesia:dirty_read({Tab3, 11})),
+ ?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
+ %% Check Index
+ ?match([], mnesia:dirty_index_read(Tab2, 11, val)),
+ ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
+ %% Check Snmp
+ ?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
+ ?match({ok, {Tab1, 11, 12}}, mnesia:snmp_get_row(Tab1, [11])),
+ %% Check schema info
+ ?match([Node2], mnesia:table_info(Tab2, where_to_write));
+ recreate_tables ->
+ check_subscr(Tab1, 0),
+ [rpc:call(Node, ?MODULE, check_tab, [Res1, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res2, ?LINE]) || Node <- Nodes],
+ [rpc:call(Node, ?MODULE, check_tab, [Res3, ?LINE]) || Node <- Nodes],
+ ?match([], mnesia:dirty_read({Tab1, 11})),
+ ?match([], mnesia:dirty_read({Tab2, 11})),
+ ?match([], mnesia:dirty_read({Tab3, 11})),
+ %% Check Index
+ ?match([{Tab2, 10, 53}], mnesia:dirty_index_read(Tab2, 53, val)),
+ ?match([], mnesia:dirty_index_read(Tab2, 11, val)),
+ %% Check Snmp
+ ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
+ ?match({ok, {Tab1, 1, 43}}, mnesia:snmp_get_row(Tab1, [1])),
+ ?match(undefined, mnesia:snmp_get_row(Tab1, [11])),
+ %% Check schema info
+ Ns = lists:sort([Node1, Node2]),
+ ?match(Ns, lists:sort(mnesia:table_info(Tab2, where_to_write)))
+ end,
+ ?match(ok, file:delete(File1)),
+ ?match(ok, file:delete(File2)),
+ ?verify_mnesia(Nodes, []).
+
+
+check_subscr(Tab) ->
+ check_subscr(Tab, 10).
+
+check_subscr(_Tab, 0) ->
+ receive
+ Msg ->
+ ?error("Too many msgs ~p~n", [Msg])
+ after 500 ->
+ ok
+ end;
+check_subscr(Tab, N) ->
+ V = N +42,
+ receive
+ {mnesia_table_event, {write, {Tab, N, V}, _}} ->
+ check_subscr(Tab, N-1)
+ after 500 ->
+ ?error("Missing ~p~n", [{Tab, N, V}])
+ end.
+
+restore_clear_ram(suite) -> [];
+restore_clear_ram(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, [{diskless, true}|Config]),
+
+ ?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
+
+ Write = fun(What) ->
+ mnesia:write({a,1,What}),
+ mnesia:write({a,2,What}),
+ mnesia:write({a,3,What})
+ end,
+ Bup = "restore_clear_ram.BUP",
+
+ ?match({atomic, ok}, mnesia:transaction(Write, [initial])),
+ ?match({ok, _, _}, mnesia:activate_checkpoint([{name,test},
+ {min, [schema, a]},
+ {ram_overrides_dump, true}])),
+ ?match(ok, mnesia:backup_checkpoint(test, Bup)),
+
+ ?match({atomic, ok}, mnesia:transaction(Write, [data])),
+ ?match({atomic, [a]}, mnesia:restore(Bup, [{clear_tables,[a]},{default_op,skip_tables}])),
+
+ restore_clear_ram_loop(100, Nodes, Bup),
+
+ ok.
+
+restore_clear_ram_loop(N, Nodes = [N1,N2,N3], Bup) when N > 0 ->
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+ ?match({_, []}, rpc:multicall([N1,N2], mnesia, start, [[{extra_db_nodes, Nodes}]])),
+ Key = rpc:async_call(N3, mnesia, start, [[{extra_db_nodes, Nodes}]]),
+ ?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
+ ?match({atomic, [a]}, mnesia:restore(Bup, [{clear_tables,[a]},{default_op,skip_tables}])),
+ ?match(ok, rpc:yield(Key)),
+ ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [[a], 3000])),
+ case rpc:multicall(Nodes, mnesia, table_info, [a,size]) of
+ {[3,3,3], []} ->
+ restore_clear_ram_loop(N-1, Nodes, Bup);
+ Error ->
+ ?match(3, Error)
+ end;
+restore_clear_ram_loop(_,_,_) ->
+ ok.
+
+traverse_backup(doc) ->
+ ["Testing the traverse_backup interface, the resulting file is not tested though",
+ "See install_fallback for result using the output file from traverse_backup",
+ "A side effect is that the backup file contents are tested"];
+traverse_backup(suite) -> [];
+traverse_backup(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = backup_tab,
+ Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 4, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 5, test_nok})),
+ File = "_treverse_backup.BUP",
+ File2 = "traverse_backup2.BUP",
+ File3 = "traverse_backup3.BUP",
+ ?match(ok, mnesia:backup(File)),
+
+ Fun = fun({backup_tab, N, _}, Acc) -> {[{backup_tab, N, test_ok}], Acc+1};
+ (Other, Acc) -> {[Other], Acc}
+ end,
+
+ ?match({ok, 5}, mnesia:traverse_backup(File, read_only, Fun, 0)),
+ ?match(ok, file:delete(read_only)),
+
+ ?match({ok, 5}, mnesia:traverse_backup(File, mnesia_backup,
+ dummy, read_only, Fun, 0)),
+
+ ?match({ok, 5}, mnesia:traverse_backup(File, File2, Fun, 0)),
+ ?match({ok, 5}, mnesia:traverse_backup(File2, mnesia_backup,
+ File3, mnesia_backup, Fun, 0)),
+
+ BadFun = fun({bad_tab, _N, asd}, Acc) -> {{error, error}, Acc} end,
+ ?match({error, _}, mnesia:traverse_backup(File, read_only, BadFun, 0)),
+ ?match({error, _}, file:delete(read_only)),
+ ?match(ok, file:delete(File)),
+ ?match(ok, file:delete(File2)),
+ ?match(ok, file:delete(File3)),
+ ?verify_mnesia(Nodes, []).
+
+
+install_fallback(doc) ->
+ ["This tests the install_fallback intf.",
+ "It also verifies that the output from backup_checkpoint and traverse_backup",
+ "is valid"];
+install_fallback(suite) -> [];
+install_fallback(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = fallbacks_test,
+ Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 4, test_nok})),
+ ?match(ok, mnesia:dirty_write({Tab, 5, test_nok})),
+
+ Tab2 = fallbacks_test2,
+ Def2 = [{disc_copies, [node()]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ Tab3 = fallbacks_test3,
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def2)),
+ Fun2 = fun(Key) ->
+ Rec = {Tab2, Key, test_ok},
+ mnesia:dirty_write(Rec),
+ [Rec]
+ end,
+ TabSize3 = 1000,
+ OldRecs2 = [Fun2(K) || K <- lists:seq(1, TabSize3)],
+
+ Spec =[{name, cp_name}, {max, mnesia:system_info(tables)}],
+ ?match({ok, _Name, Nodes}, mnesia:activate_checkpoint(Spec)),
+ ?match(ok, mnesia:dirty_write({Tab, 6, test_nok})),
+ [mnesia:dirty_write({Tab2, K, test_nok}) || K <- lists:seq(1, TabSize3 + 10)],
+ File = "install_fallback.BUP",
+ File2 = "install_fallback2.BUP",
+ File3 = "install_fallback3.BUP",
+ ?match(ok, mnesia:backup_checkpoint(cp_name, File)),
+
+ Fun = fun({T, N, _}, Acc) when T == Tab ->
+ case N rem 2 of
+ 0 ->
+ io:format("write ~p -> ~p~n", [N, T]),
+ {[{T, N, test_ok}], Acc + 1};
+ 1 ->
+ io:format("write ~p -> ~p~n", [N, Tab3]),
+ {[{Tab3, N, test_ok}], Acc + 1}
+ end;
+ ({T, N}, Acc) when T == Tab ->
+ case N rem 2 of
+ 0 ->
+ io:format("delete ~p -> ~p~n", [N, T]),
+ {[{T, N}], Acc + 1};
+ 1 ->
+ io:format("delete ~p -> ~p~n", [N, Tab3]),
+ {[{Tab3, N}], Acc + 1}
+ end;
+ (Other, Acc) ->
+ {[Other], Acc}
+ end,
+ ?match({ok, _}, mnesia:traverse_backup(File, File2, Fun, 0)),
+ ?match(ok, mnesia:install_fallback(File2)),
+
+ mnesia_test_lib:kill_mnesia([Node1, Node2]),
+ timer:sleep(timer:seconds(1)), % Let it die!
+
+ ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab, Tab2, Tab3])),
+
+ % Verify
+ ?match([], mnesia:dirty_read({Tab, 1})),
+ ?match([{Tab3, 1, test_ok}], mnesia:dirty_read({Tab3, 1})),
+ ?match([{Tab, 2, test_ok}], mnesia:dirty_read({Tab, 2})),
+ ?match([], mnesia:dirty_read({Tab3, 2})),
+ ?match([], mnesia:dirty_read({Tab, 3})),
+ ?match([{Tab3, 3, test_ok}], mnesia:dirty_read({Tab3, 3})),
+ ?match([{Tab, 4, test_ok}], mnesia:dirty_read({Tab, 4})),
+ ?match([], mnesia:dirty_read({Tab3, 4})),
+ ?match([], mnesia:dirty_read({Tab, 5})),
+ ?match([{Tab3, 5, test_ok}], mnesia:dirty_read({Tab3, 5})),
+ ?match([], mnesia:dirty_read({Tab, 6})),
+ ?match([], mnesia:dirty_read({Tab3, 6})),
+ ?match([], [mnesia:dirty_read({Tab2, K}) || K <- lists:seq(1, TabSize3)] -- OldRecs2),
+ ?match(TabSize3, mnesia:table_info(Tab2, size)),
+
+ % Check the interface
+ file:delete(File3),
+ ?match({error, _}, mnesia:install_fallback(File3)),
+ ?match({error, _}, mnesia:install_fallback(File2, mnesia_badmod)),
+ ?match(ok, mnesia:install_fallback(File2, mnesia_backup)),
+ ?match(ok, file:delete(File)),
+ ?match(ok, file:delete(File2)),
+ ?match({error, _}, file:delete(File3)),
+ ?verify_mnesia(Nodes, []).
+
+uninstall_fallback(suite) -> [];
+uninstall_fallback(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = uinst_fallbacks_test,
+ File = "uinst_fallback.BUP",
+ File2 = "uinst_fallback2.BUP",
+ Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
+ ?match(ok, mnesia:backup(File)),
+ Fun = fun({T, N, _}, Acc) when T == Tab ->
+ {[{T, N, test_nok}], Acc+1};
+ (Other, Acc) -> {[Other], Acc}
+ end,
+ ?match({ok, _}, mnesia:traverse_backup(File, File2, Fun, 0)),
+ ?match({error, enoent}, mnesia:uninstall_fallback()),
+ ?match(ok, mnesia:install_fallback(File2)),
+ ?match(ok, file:delete(File)),
+ ?match(ok, file:delete(File2)),
+ ?match(ok, mnesia:uninstall_fallback()),
+
+ mnesia_test_lib:kill_mnesia([Node1, Node2]),
+ timer:sleep(timer:seconds(1)), % Let it die!
+ ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab])),
+ ?match([{Tab, 1, test_ok}], mnesia:dirty_read({Tab, 1})),
+ ?verify_mnesia(Nodes, []).
+
+local_fallback(suite) -> [];
+local_fallback(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = local_fallback,
+ File = "local_fallback.BUP",
+ Def = [{disc_copies, Nodes}],
+ Key = foo,
+ Pre = {Tab, Key, pre},
+ Post = {Tab, Key, post},
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write(Pre)),
+ ?match(ok, mnesia:backup(File)),
+ ?match(ok, mnesia:dirty_write(Post)),
+ Local = [{scope, local}],
+ ?match({error, enoent}, mnesia:uninstall_fallback(Local)),
+ ?match(ok, mnesia:install_fallback(File, Local)),
+ ?match(true, mnesia:system_info(fallback_activated)),
+ ?match(ok, mnesia:uninstall_fallback(Local)),
+ ?match(false, mnesia:system_info(fallback_activated)),
+ ?match(ok, mnesia:install_fallback(File, Local)),
+ ?match(true, mnesia:system_info(fallback_activated)),
+
+ ?match(false, rpc:call(Node2, mnesia, system_info , [fallback_activated])),
+ ?match(ok, rpc:call(Node2, mnesia, install_fallback , [File, Local])),
+ ?match([Post], mnesia:dirty_read({Tab, Key})),
+ ?match([Post], rpc:call(Node2, mnesia, dirty_read, [{Tab, Key}])),
+
+ ?match([], mnesia_test_lib:kill_mnesia(Nodes)),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
+ ?match([Pre], mnesia:dirty_read({Tab, Key})),
+ ?match([Pre], rpc:call(Node2, mnesia, dirty_read, [{Tab, Key}])),
+ Dir = rpc:call(Node2, mnesia, system_info , [directory]),
+
+ ?match(ok, mnesia:dirty_write(Post)),
+ ?match([Post], mnesia:dirty_read({Tab, Key})),
+ ?match([], mnesia_test_lib:kill_mnesia([Node2])),
+ ?match(ok, mnesia:install_fallback(File, Local ++ [{mnesia_dir, Dir}])),
+ ?match([], mnesia_test_lib:kill_mnesia([Node1])),
+
+ ?match([], mnesia_test_lib:start_mnesia([Node2], [])),
+ ?match(yes, rpc:call(Node2, mnesia, force_load_table, [Tab])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
+ ?match([Pre], mnesia:dirty_read({Tab, Key})),
+
+ ?match(ok, file:delete(File)),
+ ?verify_mnesia(Nodes, []).
+
+selective_backup_checkpoint(doc) ->
+ ["Perform a selective backup of a checkpoint"];
+selective_backup_checkpoint(suite) -> [];
+selective_backup_checkpoint(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = sel_backup,
+ OmitTab = sel_backup_omit,
+ CpName = sel_cp,
+ Def = [{disc_copies, [Node1, Node2]}],
+ File = "selective_backup_checkpoint.BUP",
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match({atomic, ok}, mnesia:create_table(OmitTab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
+ ?match(ok, mnesia:dirty_write({OmitTab, 1, test_ok})),
+ CpSpec = [{name, CpName}, {max, mnesia:system_info(tables)}],
+ ?match({ok, CpName, _Ns}, mnesia:activate_checkpoint(CpSpec)),
+
+ BupSpec = [{tables, [Tab]}],
+ ?match(ok, mnesia:backup_checkpoint(CpName, File, BupSpec)),
+
+ ?match([schema, sel_backup], bup_tables(File, mnesia_backup)),
+ ?match(ok, file:delete(File)),
+
+ BupSpec2 = [{tables, [Tab, OmitTab]}],
+ ?match(ok, mnesia:backup_checkpoint(CpName, File, BupSpec2)),
+
+ ?match([schema, sel_backup, sel_backup_omit],
+ bup_tables(File, mnesia_backup)),
+ ?match(ok, file:delete(File)),
+ ?verify_mnesia(Nodes, []).
+
+bup_tables(File, Mod) ->
+ Fun = fun(Rec, Tabs) ->
+ Tab = element(1, Rec),
+ Tabs2 = [Tab | lists:delete(Tab, Tabs)],
+ {[Rec], Tabs2}
+ end,
+ case mnesia:traverse_backup(File, Mod, dummy, read_only, Fun, []) of
+ {ok, Tabs} ->
+ lists:sort(Tabs);
+ {error, Reason} ->
+ exit(Reason)
+ end.
+
+incremental_backup_checkpoint(doc) ->
+ ["Perform a incremental backup of a checkpoint"];
+incremental_backup_checkpoint(suite) -> [];
+incremental_backup_checkpoint(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = incr_backup,
+ Def = [{disc_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
+ ?match([ok|_], [mnesia:dirty_write(R) || R <- OldRecs]),
+ OldCpName = old_cp,
+ OldCpSpec = [{name, OldCpName}, {min, [Tab]}],
+ ?match({ok, OldCpName, _Ns}, mnesia:activate_checkpoint(OldCpSpec)),
+
+ BupSpec = [{tables, [Tab]}],
+ OldFile = "old_full_backup.BUP",
+ ?match(ok, mnesia:backup_checkpoint(OldCpName, OldFile, BupSpec)),
+ ?match(OldRecs, bup_records(OldFile, mnesia_backup)),
+ ?match(ok, mnesia:dirty_delete({Tab, 1})),
+ ?match(ok, mnesia:dirty_write({Tab, 2, 2})),
+ ?match(ok, mnesia:dirty_write({Tab, 3, -3})),
+
+ NewCpName = new_cp,
+ NewCpSpec = [{name, NewCpName}, {min, [Tab]}],
+ ?match({ok, NewCpName, _Ns}, mnesia:activate_checkpoint(NewCpSpec)),
+ ?match(ok, mnesia:dirty_write({Tab, 4, 4})),
+
+ NewFile = "new_full_backup.BUP",
+ ?match(ok, mnesia:backup_checkpoint(NewCpName, NewFile, BupSpec)),
+ NewRecs = [{Tab, 2, 2}, {Tab, 3, -3},
+ {Tab, 4, 4}, {Tab, 4}, {Tab, 4, -4}, {Tab, 5, -5}],
+ ?match(NewRecs, bup_records(NewFile, mnesia_backup)),
+
+ DiffFile = "diff_backup.BUP",
+ DiffBupSpec = [{tables, [Tab]}, {incremental, OldCpName}],
+ ?match(ok, mnesia:backup_checkpoint(NewCpName, DiffFile, DiffBupSpec)),
+ DiffRecs = [{Tab, 1}, {Tab, 2}, {Tab, 2, 2}, {Tab, 3}, {Tab, 3, -3},
+ {Tab, 4}, {Tab, 4, 4}, {Tab, 4}, {Tab, 4, -4}],
+ ?match(DiffRecs, bup_records(DiffFile, mnesia_backup)),
+
+ ?match(ok, mnesia:deactivate_checkpoint(OldCpName)),
+ ?match(ok, mnesia:deactivate_checkpoint(NewCpName)),
+ ?match(ok, file:delete(OldFile)),
+ ?match(ok, file:delete(NewFile)),
+ ?match(ok, file:delete(DiffFile)),
+
+ ?verify_mnesia(Nodes, []).
+
+bup_records(File, Mod) ->
+ Fun = fun(Rec, Recs) when element(1, Rec) == schema ->
+ {[Rec], Recs};
+ (Rec, Recs) ->
+ {[Rec], [Rec | Recs]}
+ end,
+ case mnesia:traverse_backup(File, Mod, dummy, read_only, Fun, []) of
+ {ok, Recs} ->
+ lists:keysort(1, lists:keysort(2, lists:reverse(Recs)));
+ {error, Reason} ->
+ exit(Reason)
+ end.
+
+sops_with_checkpoint(doc) ->
+ ["Test schema operations during a checkpoint"];
+sops_with_checkpoint(suite) -> [];
+sops_with_checkpoint(Config) when is_list(Config) ->
+ Ns = ?acquire_nodes(2, Config),
+
+ ?match({ok, cp1, Ns}, mnesia:activate_checkpoint([{name, cp1},{max,mnesia:system_info(tables)}])),
+ Tab = tab,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{disc_copies,Ns}])),
+ OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)],
+ [mnesia:dirty_write(R) || R <- OldRecs],
+
+ ?match({ok, cp2, Ns}, mnesia:activate_checkpoint([{name, cp2},{max,mnesia:system_info(tables)}])),
+ File1 = "cp1_delete_me.BUP",
+ ?match(ok, mnesia:dirty_write({Tab,6,-6})),
+ ?match(ok, mnesia:backup_checkpoint(cp1, File1)),
+ ?match(ok, mnesia:dirty_write({Tab,7,-7})),
+ File2 = "cp2_delete_me.BUP",
+ ?match(ok, mnesia:backup_checkpoint(cp2, File2)),
+
+ ?match(ok, mnesia:deactivate_checkpoint(cp1)),
+ ?match(ok, mnesia:backup_checkpoint(cp2, File1)),
+ ?match(ok, mnesia:dirty_write({Tab,8,-8})),
+
+ ?match({atomic,ok}, mnesia:delete_table(Tab)),
+ ?match({error,_}, mnesia:backup_checkpoint(cp2, File2)),
+ ?match({'EXIT',_}, mnesia:dirty_write({Tab,9,-9})),
+
+ ?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])),
+ Test = fun(N) when N > 5 -> ?error("To many records in backup ~p ~n", [N]);
+ (N) -> case mnesia:dirty_read(Tab,N) of
+ [{Tab,N,B}] when -B =:= N -> ok;
+ Other -> ?error("Not matching ~p ~p~n", [N,Other])
+ end
+ end,
+ [Test(N) || N <- mnesia:dirty_all_keys(Tab)],
+ ?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])),
+
+ file:delete(File1), file:delete(File2),
+
+ ?verify_mnesia(Ns, []).
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
new file mode 100644
index 0000000000..668eba176f
--- /dev/null
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -0,0 +1,2347 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_evil_coverage_test).
+-author('[email protected]').
+-include("mnesia_test_lib.hrl").
+
+-compile([export_all]).
+
+-define(cleanup(N, Config),
+ mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}],
+ N, Config, ?FILE, ?LINE)).
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [system_info, table_info, error_description,
+ db_node_lifecycle, evil_delete_db_node, start_and_stop,
+ checkpoint, table_lifecycle, add_copy_conflict,
+ add_copy_when_going_down, replica_management,
+ schema_availability, local_content,
+ {group, table_access_modifications}, replica_location,
+ {group, table_sync}, user_properties, unsupp_user_props,
+ {group, record_name}, {group, snmp_access},
+ {group, subscriptions}, {group, iteration},
+ {group, debug_support}, sorted_ets,
+ {mnesia_dirty_access_test, all},
+ {mnesia_trans_access_test, all},
+ {mnesia_evil_backup, all}].
+
+groups() ->
+ [{table_access_modifications, [],
+ [change_table_access_mode, change_table_load_order,
+ set_master_nodes, offline_set_master_nodes]},
+ {table_sync, [],
+ [dump_tables, dump_log, wait_for_tables,
+ force_load_table]},
+ {snmp_access, [],
+ [snmp_open_table, snmp_close_table, snmp_get_next_index,
+ snmp_get_row, snmp_get_mnesia_key, snmp_update_counter,
+ snmp_order]},
+ {subscriptions, [],
+ [subscribe_standard, subscribe_extended]},
+ {iteration, [], [foldl]},
+ {debug_support, [],
+ [info, schema_0, schema_1, view_0, view_1, view_2,
+ lkill, kill]},
+ {record_name, [], [{group, record_name_dirty_access}]},
+ {record_name_dirty_access, [],
+ [record_name_dirty_access_ram,
+ record_name_dirty_access_disc,
+ record_name_dirty_access_disc_only]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Get meta info about Mnesia
+
+system_info(suite) -> [];
+system_info(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(all, Config),
+ Ns = ?sort(Nodes),
+ ?match(yes, mnesia:system_info(is_running)),
+ ?match(Ns, ?sort(mnesia:system_info(db_nodes))),
+ ?match(Ns, ?sort(mnesia:system_info(running_db_nodes))),
+ ?match(A when is_atom(A), mnesia:system_info(debug)),
+ ?match(L when is_list(L), mnesia:system_info(directory)),
+ ?match(L when is_list(L), mnesia:system_info(log_version)),
+ ?match({_, _}, mnesia:system_info(schema_version)),
+ ?match(L when is_list(L), mnesia:system_info(tables)),
+ ?match(L when is_list(L), mnesia:system_info(local_tables)),
+ ?match(L when is_list(L), mnesia:system_info(held_locks)),
+ ?match(L when is_list(L), mnesia:system_info(lock_queue)),
+ ?match(L when is_list(L), mnesia:system_info(transactions)),
+ ?match(I when is_integer(I), mnesia:system_info(transaction_failures)),
+ ?match(I when is_integer(I), mnesia:system_info(transaction_commits)),
+ ?match(I when is_integer(I), mnesia:system_info(transaction_restarts)),
+ ?match(L when is_list(L), mnesia:system_info(checkpoints)),
+ ?match(A when is_atom(A), mnesia:system_info(backup_module)),
+ ?match(true, mnesia:system_info(auto_repair)),
+ ?match({_, _}, mnesia:system_info(dump_log_interval)),
+ ?match(A when is_atom(A), mnesia:system_info(dump_log_update_in_place)),
+ ?match(I when is_integer(I), mnesia:system_info(transaction_log_writes)),
+ ?match(I when is_integer(I), mnesia:system_info(send_compressed)),
+ ?match(L when is_list(L), mnesia:system_info(all)),
+ ?match({'EXIT', {aborted, Reason }} when element(1, Reason) == badarg
+ , mnesia:system_info(ali_baba)),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Get meta info about table
+
+table_info(suite) -> [];
+table_info(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+
+ Tab = table_info,
+ Type = bag,
+ ValPos = 3,
+ Attrs = [k, v],
+ Arity = length(Attrs) +1,
+
+ Schema =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{type, Type}, {attributes, Attrs}, {index, [ValPos]},
+ {ram_copies, Nodes}];
+ false ->
+ [{type, Type}, {attributes, Attrs}, {index, [ValPos]},
+ {disc_only_copies, [Node1]}, {ram_copies, [Node2]},
+ {disc_copies, [Node3]}]
+ end,
+ ?match({atomic, ok}, mnesia:create_table(Tab, Schema)),
+
+ Size = 10,
+ Keys = lists:seq(1, Size),
+ Records = [{Tab, A, 7} || A <- Keys],
+ lists:foreach(fun(Rec) -> ?match(ok, mnesia:dirty_write(Rec)) end, Records),
+ ?match(Mem when is_integer(Mem), mnesia:table_info(Tab, memory)),
+ ?match(Size, mnesia:table_info(Tab, size)),
+ ?match(Type, mnesia:table_info(Tab, type)),
+
+ case mnesia_test_lib:diskless(Config) of
+ true ->
+ ?match(Nodes, mnesia:table_info(Tab, ram_copies));
+ false ->
+ ?match([Node3], mnesia:table_info(Tab, mnesia_test_lib:storage_type(disc_copies, Config))),
+ ?match([Node2], mnesia:table_info(Tab, ram_copies)),
+ ?match([Node1], mnesia:table_info(Tab, mnesia_test_lib:storage_type(disc_only_copies, Config)))
+ end,
+ Read = [Node1, Node2, Node3],
+ ?match(true, lists:member(mnesia:table_info(Tab, where_to_read), Read)),
+ Write = ?sort([Node1, Node2, Node3]),
+ ?match(Write, ?sort(mnesia:table_info(Tab, where_to_write))),
+ ?match([ValPos], mnesia:table_info(Tab, index)),
+ ?match(Arity, mnesia:table_info(Tab, arity)),
+ ?match(Attrs, mnesia:table_info(Tab, attributes)),
+ ?match({Tab, '_', '_'}, mnesia:table_info(Tab, wild_pattern)),
+ ?match({atomic, Attrs}, mnesia:transaction(fun() ->
+ mnesia:table_info(Tab, attributes) end)),
+
+ ?match(L when is_list(L), mnesia:table_info(Tab, all)),
+
+ %% Table info when table not loaded
+ ?match({atomic, ok},
+ mnesia:create_table(tab_info, Schema)),
+ ?match(stopped, mnesia:stop()),
+ ?match(stopped, rpc:call(Node2, mnesia, stop, [])),
+ ?match(ok, mnesia:start()),
+ ?match(ok, mnesia:wait_for_tables([tab_info], 5000)),
+ ?match(0, mnesia:table_info(tab_info, size)),
+ ?verify_mnesia([Node1, Node3], [Node2]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Check the error descriptions
+
+error_description(suite) -> [];
+error_description(Config) when is_list(Config) ->
+ ?acquire_nodes(1, Config),
+ Errors = [nested_transaction, badarg, no_transaction, combine_error,
+ bad_index, already_exists, index_exists, no_exists, system_limit,
+ mnesia_down, not_a_db_node, bad_type, node_not_running,
+ truncated_binary_file, active, illegal
+ ],
+ ?match(X when is_atom(X), mnesia:error_description({error, bad_error_msg})),
+ ?match(X when is_tuple(X), mnesia:error_description({'EXIT', pid, bad})),
+ %% This is real error msg
+ ?match(X when is_tuple(X), mnesia:error_description(
+ {error,
+ {"Cannot prepare checkpoint (bad reply)",
+ {{877,957351,758147},a@legolas},
+ {error,{node_not_running,a1@legolas}}}})),
+ check_errors(error, Errors),
+ check_errors(aborted, Errors),
+ check_errors('EXIT', Errors).
+
+check_errors(_Err, []) -> ok;
+check_errors(Err, [Desc|R]) ->
+ ?match(X when is_list(X), mnesia:error_description({Err, Desc})),
+ check_errors(Err, R).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Add and drop db nodes
+
+db_node_lifecycle(suite) -> [];
+db_node_lifecycle(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = AllNodes = ?acquire_nodes(3, Config),
+ Tab = db_node_lifecycle,
+
+ Who = fun(T) ->
+ L1 = mnesia:table_info(T, ram_copies),
+ L2 = mnesia:table_info(T, disc_copies),
+ L3 = mnesia:table_info(T, disc_only_copies),
+ L1 ++ L2 ++ L3
+ end,
+
+ SNs = ?sort(AllNodes),
+
+ Schema = [{name, Tab}, {ram_copies, [Node1, Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ ?match([], mnesia_test_lib:stop_mnesia(AllNodes)),
+ ?match(ok, mnesia:delete_schema(AllNodes)),
+ ?match({error, _}, mnesia:create_schema(foo)),
+ ?match({error, _}, mnesia:create_schema([foo])),
+ ?match({error, _}, mnesia:create_schema([foo@bar])),
+ ?match(ok, mnesia:start()),
+ ?match(false, mnesia:system_info(use_dir)),
+ ?match({atomic, ok}, mnesia:create_table(Tab, [])),
+ ?match({aborted, {has_no_disc, Node1}}, mnesia:dump_tables([Tab])),
+ ?match({aborted, {has_no_disc, Node1}}, mnesia:change_table_copy_type(Tab, node(), disc_copies)),
+ ?match({aborted, {has_no_disc, Node1}}, mnesia:change_table_copy_type(Tab, node(), disc_only_copies)),
+
+ ?match(stopped, mnesia:stop()),
+
+ ?match(ok, mnesia:create_schema(AllNodes)),
+ ?match([], mnesia_test_lib:start_mnesia(AllNodes)),
+
+ ?match([SNs, SNs, SNs],
+ lists:map({lists, sort},
+ element(1, rpc:multicall(AllNodes, mnesia, table_info,
+ [schema, disc_copies])))),
+
+ ?match({aborted, {already_exists, schema, Node2, _}},
+ mnesia:change_table_copy_type(schema, Node2, disc_copies)),
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(schema, Node2, ram_copies)),
+ ?match({aborted, {already_exists, schema, Node2, _}},
+ mnesia:change_table_copy_type(schema, Node2, ram_copies)),
+
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(schema, Node2, disc_copies)),
+
+ ?match([SNs, SNs, SNs],
+ lists:map({lists, sort},
+ element(1, rpc:multicall(AllNodes, mnesia, table_info,
+ [schema, disc_copies])))),
+
+ %% Delete the DB
+
+ Tab2 = disk_tab,
+ Tab3 = not_local,
+ Tab4 = local,
+ Tab5 = remote,
+
+ Tabs = [Schema,
+ [{name, Tab2}, {disc_copies, AllNodes}],
+ [{name, Tab3}, {ram_copies, [Node2, Node3]}],
+ [{name, Tab4}, {disc_only_copies, [Node1]}],
+ [{name, Tab5}, {disc_only_copies, [Node2]}]],
+
+ [?match({atomic, ok}, mnesia:create_table(T)) || T <- Tabs ],
+
+ ?match({aborted, {active, _, Node2}},
+ mnesia:del_table_copy(schema, Node2)),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node1])),
+ ?match({aborted, {node_not_running, Node1}},
+ mnesia:del_table_copy(schema, Node2)),
+
+ ?match([], mnesia_test_lib:start_mnesia([Node1],[Tab2,Tab4])),
+ ?match([], mnesia_test_lib:stop_mnesia([Node2])),
+ ?match({atomic, ok},
+ mnesia:del_table_copy(schema, Node2)),
+
+ %% Check
+ RemNodes = AllNodes -- [Node2],
+
+ ?match(RemNodes, mnesia:system_info(db_nodes)),
+ ?match([Node1], Who(Tab)),
+ ?match(RemNodes, Who(Tab2)),
+ ?match([Node3], Who(Tab3)),
+ ?match([Node1], Who(Tab4)),
+ ?match({'EXIT', {aborted, {no_exists, _, _}}}, Who(Tab5)),
+
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(Tab2, Node3, ram_copies)),
+
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(schema, Node3, ram_copies)),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node3])),
+ ?match({atomic, ok},
+ mnesia:del_table_copy(schema, Node3)),
+ ?match([Node1], mnesia:system_info(db_nodes)),
+ ?match([Node1], Who(Tab)),
+ ?match([Node1], Who(Tab2)),
+ ?match({'EXIT', {aborted, {no_exists, _, _}}}, Who(Tab3)),
+ ?match([Node1], Who(Tab4)),
+ ?match({'EXIT', {aborted, {no_exists, _, _}}}, Who(Tab5)),
+
+ ?verify_mnesia([Node1], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Drop a db node when several disk resident nodes are down
+
+evil_delete_db_node(suite) -> [];
+evil_delete_db_node(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = AllNodes = ?acquire_nodes(3, Config),
+ Tab = evil_delete_db_node,
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{disc_copies, AllNodes}])),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node2, Node3])),
+
+ ?match({atomic, ok}, mnesia:del_table_copy(schema, Node2)),
+
+ RemNodes = AllNodes -- [Node2],
+
+ ?match(RemNodes, mnesia:system_info(db_nodes)),
+ ?match(RemNodes, mnesia:table_info(Tab, disc_copies)),
+
+ ?verify_mnesia([Node1], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Start and stop the system
+
+start_and_stop(suite) -> [];
+start_and_stop(Config) when is_list(Config) ->
+ [Node1 | _] = Nodes = ?acquire_nodes(all, Config),
+
+ ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
+ ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
+ ?match(ok, rpc:call(Node1, mnesia, start, [])),
+ ?match(ok, rpc:call(Node1, mnesia, start, [])),
+ ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
+ ?verify_mnesia(Nodes -- [Node1], [Node1]),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes)),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Checkpoints and backup management
+
+checkpoint(suite) -> [];
+checkpoint(Config) when is_list(Config) ->
+ checkpoint(2, Config),
+ checkpoint(3, Config).
+
+checkpoint(NodeConfig, Config) ->
+ [Node1 | _] = TabNodes = ?acquire_nodes(NodeConfig, Config),
+ CreateTab = fun(Type, N, Ns) ->
+ Tab0 = lists:concat(["local_checkpoint_", Type, N]),
+ Tab = list_to_atom(Tab0),
+ catch mnesia:delete_table(Tab),
+ ?match({atomic, ok},
+ mnesia:create_table(Tab, [{Type, Ns}])),
+ Tab
+ end,
+ CreateTabs = fun(Type, Acc) ->
+ [CreateTab(Type, 1, [hd(TabNodes)]),
+ CreateTab(Type, 2, TabNodes),
+ CreateTab(Type, 3, [lists:last(TabNodes)])] ++
+ Acc
+ end,
+ Types = [ram_copies, disc_copies, disc_only_copies],
+ Tabs = lists:foldl(CreateTabs, [], Types),
+ Recs = ?sort([{T, N, N} || T <- Tabs, N <- lists:seq(1, 10)]),
+ lists:foreach(fun(R) -> ?match(ok, mnesia:dirty_write(R)) end, Recs),
+
+ CpName = a_checkpoint_name,
+ MinArgs = [{name, CpName}, {min, Tabs}, {allow_remote, false}],
+ ?match({error, _}, rpc:call(Node1, mnesia, activate_checkpoint, [MinArgs])),
+
+ MaxArgs = [{name, CpName}, {max, Tabs}, {allow_remote, true}],
+ ?match({ok, CpName, L} when is_list(L),
+ rpc:call(Node1, mnesia, activate_checkpoint, [MaxArgs])),
+ ?match(ok, rpc:call(Node1, mnesia, deactivate_checkpoint, [CpName])),
+
+ Args = [{name, CpName}, {min, Tabs}, {allow_remote, true}],
+ ?match({ok, CpName, L} when is_list(L),
+ rpc:call(Node1, mnesia, activate_checkpoint, [Args])),
+ Recs2 = ?sort([{T, K, 0} || {T, K, _} <- Recs]),
+ lists:foreach(fun(R) -> ?match(ok, mnesia:dirty_write(R)) end, Recs2),
+ ?match(ok, rpc:call(Node1, mnesia, deactivate_checkpoint, [CpName])),
+
+ ?match({error, Reason1 } when element(1, Reason1) == no_exists,
+ mnesia:deactivate_checkpoint(CpName)),
+ ?match({error, Reason2 } when element(1, Reason2) == badarg,
+ mnesia:activate_checkpoint(foo)),
+ ?match({error, Reason3 } when element(1, Reason3) == badarg,
+ mnesia:activate_checkpoint([{foo, foo}])),
+ ?match({error, Reason4 } when element(1, Reason4) == badarg,
+ mnesia:activate_checkpoint([{max, foo}])),
+ ?match({error, Reason5 } when element(1, Reason5) == badarg,
+ mnesia:activate_checkpoint([{min, foo}])),
+ ?match({error, _}, mnesia:activate_checkpoint([{min, [foo@bar]}])),
+ ?match({error, Reason6 } when element(1, Reason6) == badarg,
+ mnesia:activate_checkpoint([{allow_remote, foo}])),
+
+ Fun = fun(Tab) -> ?match({atomic, ok}, mnesia:delete_table(Tab)) end,
+ lists:foreach(Fun, Tabs),
+ ?verify_mnesia(TabNodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Create and delete tables
+
+%% Get meta info about table
+
+-define(vrl, mnesia_test_lib:verify_replica_location).
+
+replica_location(suite) -> [];
+replica_location(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab = replica_location,
+
+ %% Create three replicas
+ Schema = [{name, Tab}, {disc_only_copies, [Node1]},
+ {ram_copies, [Node2]}, {disc_copies, [Node3]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ?match([], ?vrl(Tab, [Node1], [Node2], [Node3], Nodes)),
+
+ %% Delete one replica
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node2)),
+ ?match([], ?vrl(Tab, [Node1], [], [Node3], Nodes)),
+
+ %% Move one replica
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node2)),
+ ?match([], ?vrl(Tab, [Node2], [], [Node3], Nodes)),
+
+ %% Change replica type
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node2, ram_copies)),
+ ?match([], ?vrl(Tab, [], [Node2], [Node3], Nodes)),
+
+ ?verify_mnesia(Nodes, []).
+
+table_lifecycle(suite) -> [];
+table_lifecycle(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+
+ ?match({atomic, ok}, mnesia:create_table([{type, bag},
+ {ram_copies, [Node1]},
+ {attributes, [rajtan, tajtan]},
+ {name, order_of_args}])),
+ ?match([], mnesia:dirty_read({order_of_args, 4711})),
+ ?match({atomic, ok}, mnesia:create_table([{name, already_exists},
+ {ram_copies, [Node1]}])),
+ ?match({aborted, Reason23 } when element(1, Reason23) ==already_exists,
+ mnesia:create_table([{name, already_exists},
+ {ram_copies, [Node1]}])),
+ ?match({aborted, Reason21 } when element(1, Reason21) == bad_type,
+ mnesia:create_table([{name, bad_node}, {ram_copies, ["foo"]}])),
+ ?match({aborted, Reason2} when element(1, Reason2) == bad_type,
+ mnesia:create_table([{name, zero_arity}, {attributes, []}])),
+ ?match({aborted, Reason3} when element(1, Reason3) == badarg,
+ mnesia:create_table([])),
+ ?match({aborted, Reason4} when element(1, Reason4) == badarg,
+ mnesia:create_table(atom)),
+ ?match({aborted, Reason5} when element(1, Reason5) == badarg,
+ mnesia:create_table({cstruct, table_name_as_atom})),
+ ?match({aborted, Reason6 } when element(1, Reason6) == bad_type,
+ mnesia:create_table([{name, no_host}, {ram_copies, foo}])),
+ ?match({aborted, Reason7 } when element(1, Reason7) == bad_type,
+ mnesia:create_table([{name, no_host}, {disc_only_copies, foo}])),
+ ?match({aborted, Reason8} when element(1, Reason8) == bad_type,
+ mnesia:create_table([{name, no_host}, {disc_copies, foo}])),
+
+ CreateFun =
+ fun() -> ?match({aborted, nested_transaction},
+ mnesia:create_table([{name, nested_trans}])), ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(CreateFun)),
+ ?match({atomic, ok}, mnesia:create_table([{name, remote_tab},
+ {ram_copies, [Node2]}])),
+
+ ?match({atomic, ok}, mnesia:create_table([{name, a_brand_new_tab},
+ {ram_copies, [Node1]}])),
+ ?match([], mnesia:dirty_read({a_brand_new_tab, 4711})),
+ ?match({atomic, ok}, mnesia:delete_table(a_brand_new_tab)),
+ ?match({'EXIT', {aborted, Reason31}} when element(1, Reason31) == no_exists,
+ mnesia:dirty_read({a_brand_new_tab, 4711})),
+ ?match({aborted, Reason41} when element(1, Reason41) == no_exists,
+ mnesia:delete_table(a_brand_new_tab)),
+ ?match({aborted, Reason9} when element(1, Reason9) == badarg,
+ mnesia:create_table([])),
+
+ ?match({atomic, ok}, mnesia:create_table([{name, nested_del_trans},
+ {ram_copies, [Node1]}])),
+
+ DeleteFun = fun() -> ?match({aborted, nested_transaction},
+ mnesia:delete_table(nested_del_trans)), ok end,
+ ?match({atomic, ok}, mnesia:transaction(DeleteFun)),
+
+ ?match({aborted, Reason10} when element(1, Reason10) == bad_type,
+ mnesia:create_table([{name, create_with_index}, {index, 2}])),
+ ?match({aborted, Reason32} when element(1, Reason32) == bad_type,
+ mnesia:create_table([{name, create_with_index}, {index, [-1]}])),
+ ?match({aborted, Reason33} when element(1, Reason33) == bad_type,
+ mnesia:create_table([{name, create_with_index}, {index, [0]}])),
+ ?match({aborted, Reason34} when element(1, Reason34) == bad_type,
+ mnesia:create_table([{name, create_with_index}, {index, [1]}])),
+ ?match({aborted, Reason35} when element(1, Reason35) == bad_type,
+ mnesia:create_table([{name, create_with_index}, {index, [2]}])),
+ ?match({atomic, ok},
+ mnesia:create_table([{name, create_with_index}, {index, [3]},
+ {ram_copies, [Node1]}])),
+ ets:new(ets_table, [named_table]),
+
+ ?match({aborted, _}, mnesia:create_table(ets_table, [{ram_copies, Nodes}])),
+
+ ?verify_mnesia(Nodes, []).
+
+add_copy_conflict(suite) -> [];
+add_copy_conflict(doc) ->
+ ["Verify that OTP-5065 doesn't happen again, whitebox testing"];
+add_copy_conflict(Config) when is_list(Config) ->
+ Nodes = [Node1, Node2] =
+ ?acquire_nodes(2, Config ++ [{tc_timeout, timer:minutes(2)}]),
+
+ ?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(b, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(test, [{ram_copies, [Node2]}])),
+ mnesia:stop(),
+ ?match(ok,mnesia:start([{no_table_loaders, 1}])),
+
+ verify_ll_queue(10),
+
+ Self = self(),
+ Test = fun() ->
+ Res = mnesia:add_table_copy(test, Node1, ram_copies),
+ Self ! {test, Res}
+ end,
+ %% Create conflict with loader queue.
+ spawn_link(Test),
+ ?match_receive(timeout),
+ %% Conflict ok
+ mnesia_controller:unblock_controller(),
+
+ ?match_receive({test, {atomic,ok}}),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(1, Config).
+
+verify_ll_queue(0) ->
+ ?error("Couldn't find anything in queue~n",[]);
+verify_ll_queue(N) ->
+ ?match(granted,mnesia_controller:block_controller()),
+ case mnesia_controller:get_info(1000) of
+ {info,{state,_,true,[],_Loader,[],[],[],_,_,_,_,_,_}} ->
+ %% Very slow SMP machines havn't loaded it yet..
+ mnesia_controller:unblock_controller(),
+ timer:sleep(10),
+ verify_ll_queue(N-1);
+ {info,{state,_,true,[],Loader,LL,[],[],_,_,_,_,_,_}} ->
+ io:format("~p~n", [{Loader,LL}]),
+ ?match([_], LL); %% Verify that something is in the loader queue
+ Else ->
+ ?error("No match ~p maybe the internal format has changed~n",[Else])
+ end.
+
+add_copy_when_going_down(suite) -> [];
+add_copy_when_going_down(doc) ->
+ ["Tests abort when node we load from goes down"];
+add_copy_when_going_down(Config) ->
+ [Node1, Node2] =
+ ?acquire_nodes(2, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ ?match({atomic, ok}, mnesia:create_table(a, [{ram_copies, [Node1]}])),
+ %% Grab a write lock
+ WriteAndWait = fun() ->
+ mnesia:write({a,1,1}),
+ receive continue -> ok
+ end
+ end,
+ _Lock = spawn(fun() -> mnesia:transaction(WriteAndWait) end),
+ Tester = self(),
+ spawn_link(fun() -> Res = rpc:call(Node2,mnesia, add_table_copy,
+ [a, Node2, ram_copies]),
+ Tester ! {test, Res}
+ end),
+ %% We have a lock here we should get a timeout
+ ?match_receive(timeout),
+ mnesia_test_lib:kill_mnesia([Node1]),
+ ?match_receive({test,{aborted,_}}),
+ ?verify_mnesia([Node2], []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Add, drop and move replicas, change storage types
+%% Change table layout (only arity change supported)
+
+-record(replica_management, {k, v}).
+-record(new_replica_management, {k, v, extra}).
+
+-define(SS(R), lists:sort(element(1,R))).
+
+replica_management(doc) ->
+ "Add, drop and move replicas, change storage types.";
+replica_management(suite) ->
+ [];
+replica_management(Config) when is_list(Config) ->
+ %% add_table_copy/3, del_table_copy/2, move_table_copy/3,
+ %% change_table_copy_type/3, transform_table/3
+
+ Nodes = [Node1, Node2, Node3] = ?acquire_nodes(3, Config),
+
+ Tab = replica_management,
+ Attrs = record_info(fields, replica_management),
+
+ %%
+ %% Add, delete and change replicas
+ %%
+ ?match({atomic, ok},
+ mnesia:create_table([{name, Tab}, {attributes, Attrs},
+ {ram_copies, [Node1, Node3]}])),
+ [?match(ok, mnesia:dirty_write({Tab, K, K + 2})) || K <-lists:seq(1, 10)],
+ ?match([], ?vrl(Tab, [], [Node1, Node3], [], Nodes)),
+ %% R - -
+ ?match({atomic, ok}, mnesia:dump_tables([Tab])),
+ ?match({aborted, Reason50 } when element(1, Reason50) == combine_error,
+ mnesia:add_table_copy(Tab, Node2, disc_copies)),
+ ?match({aborted, Reason51 } when element(1, Reason51) == combine_error,
+ mnesia:change_table_copy_type(Tab, Node1, disc_copies)),
+ ?match({atomic, ok}, mnesia:clear_table(Tab)),
+ SyncedCheck = fun() ->
+ mnesia:lock({record,Tab,0}, write),
+ ?match([0,0,0], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size])))
+ end,
+ mnesia:transaction(SyncedCheck),
+
+ ?match({[0,0,0], []}, rpc:multicall(Nodes, mnesia, table_info, [Tab, size])),
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node1)),
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node3)),
+ ?match([], ?vrl(Tab, [], [], [], Nodes)),
+ %% - - -
+ ?match({aborted,Reason52} when element(1, Reason52) == no_exists,
+ mnesia:add_table_copy(Tab, Node3, ram_copies)),
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {attributes, Attrs},
+ {disc_copies, [Node1]}])),
+ ?match([], ?vrl(Tab, [], [], [Node1], Nodes)),
+ %% D - -
+ [?match(ok, mnesia:dirty_write({Tab, K, K + 2})) || K <-lists:seq(1, 10)],
+
+ ?match({aborted, Reason53} when element(1, Reason53) == badarg,
+ mnesia:add_table_copy(Tab, Node2, bad_storage_type)),
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node2, disc_only_copies)),
+ ?match([], ?vrl(Tab, [Node2], [], [Node1], Nodes)),
+ ?match([0,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% D DO -
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node3, ram_copies)),
+ ?match([], ?vrl(Tab, [Node2], [Node3], [Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% D DO R
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(Tab, Node1, disc_only_copies)),
+ ?match([], ?vrl(Tab, [Node1, Node2], [Node3], [], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% DO DO R
+ ?match({aborted, Reason54} when element(1, Reason54) == already_exists,
+ mnesia:add_table_copy(Tab, Node3, ram_copies)),
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node1)),
+ ?match([], ?vrl(Tab, [Node2], [Node3], [], Nodes)),
+ %% - DO R
+ ?match({aborted, _}, mnesia:del_table_copy(Tab, Node1)),
+ ?match(Tab, ets:new(Tab, [named_table])),
+ ?match({aborted, _}, mnesia:add_table_copy(Tab, Node1, disc_copies)),
+ ?match(true, ets:delete(Tab)),
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node1, disc_copies)),
+ ?match([], ?vrl(Tab, [Node2], [Node3], [Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% D DO R
+ ?match({atomic, ok},mnesia:change_table_copy_type(Tab, Node3, disc_only_copies)),
+ ?match([], ?vrl(Tab, [Node2, Node3], [], [Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+
+ %% D DO D0
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node3, ram_copies)),
+ ?match([], ?vrl(Tab, [Node2], [Node3], [Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% D DO R
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(Tab, Node2, disc_copies)),
+ ?match([], ?vrl(Tab, [], [Node3], [Node1,Node2], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+
+ %% D D R
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node1, disc_only_copies)),
+ ?match([], ?vrl(Tab, [Node1], [Node3], [Node2], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+
+ %% DO D R
+ ?match(Tab, ets:new(Tab, [named_table])),
+ ?match({aborted, _}, mnesia:change_table_copy_type(Tab, Node1, ram_copies)),
+ ?match(true, ets:delete(Tab)),
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node1, ram_copies)),
+ ?match([], ?vrl(Tab, [], [Node3,Node1], [Node2], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% R D R
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node1, disc_copies)),
+ ?match([], ?vrl(Tab, [], [Node3], [Node2,Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+
+ %% D D R
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node2, disc_only_copies)),
+ ?match([], ?vrl(Tab, [Node2], [Node3], [Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+
+ %% D DO R
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, Node3, disc_only_copies)),
+ ?match([], ?vrl(Tab, [Node2, Node3], [], [Node1], Nodes)),
+ ?match([10,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% D DO DO
+ %% test clear
+ ?match({atomic, ok}, mnesia:clear_table(Tab)),
+ mnesia:transaction(SyncedCheck),
+
+ %% rewrite for rest of testcase
+ [?match(ok, mnesia:dirty_write({Tab, K, K + 2})) || K <-lists:seq(1, 10)],
+
+ %% D DO DO
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node2)),
+ ?match([], ?vrl(Tab, [Node3], [], [Node1], Nodes)),
+ %% D - DO
+ ?match({aborted, Reason55} when element(1, Reason55) == already_exists,
+ mnesia:change_table_copy_type(Tab, Node1, disc_copies)),
+
+ %%
+ %% Move replica
+ %%
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node2)),
+ ?match([], ?vrl(Tab, [Node3], [], [Node2], Nodes)),
+ ?match([0,10,10], ?SS(rpc:multicall(Nodes, mnesia, table_info, [Tab, size]))),
+ %% - D DO
+ ?match({aborted, _}, mnesia:move_table_copy(Tab, Node1, Node2)),
+ ?match([], mnesia_test_lib:stop_mnesia([Node3])),
+ ?match({atomic,ok}, mnesia:transaction(fun() -> mnesia:write({Tab, 43, sync_me}) end)),
+ ?match([], ?vrl(Tab, [Node3], [], [Node2],Nodes -- [Node3])),
+ %% - D DO
+ ?match({aborted,Reason56} when element(1, Reason56) == not_active,
+ mnesia:move_table_copy(Tab, Node3, Node1)),
+ ?match([], ?vrl(Tab, [Node3], [], [Node2],Nodes -- [Node3])),
+ %% DO D -
+ ?match([], mnesia_test_lib:start_mnesia([Node3])),
+ ?match([], ?vrl(Tab, [Node3], [], [Node2], Nodes)),
+ %% DO D -
+
+ %%
+ %% Transformer
+ %%
+
+ NewAttrs = record_info(fields, new_replica_management),
+ Transformer =
+ fun(Rec) when is_record(Rec, replica_management) ->
+ #new_replica_management{k = Rec#replica_management.k,
+ v = Rec#replica_management.v,
+ extra = Rec#replica_management.k * 2}
+ end,
+ ?match({atomic, ok}, mnesia:transform_table(Tab, fun(R) -> R end, Attrs)),
+ ?match({atomic, ok}, mnesia:transform_table(Tab, Transformer, NewAttrs, new_replica_management)),
+
+ ERlist = [#new_replica_management{k = K, v = K+2, extra = K*2} || K <- lists:seq(1, 10)],
+ ARlist = [hd(mnesia:dirty_read(Tab, K)) || K <- lists:seq(1, 10)],
+
+ ?match(ERlist, ARlist),
+
+ ?match({aborted, Reason56} when element(1, Reason56) == bad_type,
+ mnesia:transform_table(Tab, Transformer, 0)),
+ ?match({aborted, Reason57} when element(1, Reason57) == bad_type,
+ mnesia:transform_table(Tab, Transformer, -1)),
+ ?match({aborted, Reason58} when element(1, Reason58) == bad_type,
+ mnesia:transform_table(Tab, Transformer, [])),
+ ?match({aborted, Reason59} when element(1, Reason59) == bad_type,
+ mnesia:transform_table(Tab, no_fun, NewAttrs)),
+ ?match({aborted, Reason59} when element(1, Reason59) == bad_type,
+ mnesia:transform_table(Tab, fun(X) -> X end, NewAttrs, {tuple})),
+
+ %% OTP-3878
+ ?match({atomic, ok}, mnesia:transform_table(Tab, ignore,
+ NewAttrs ++ [dummy])),
+ ?match({atomic, ok}, mnesia:transform_table(Tab, ignore,
+ NewAttrs ++ [dummy], last_rec)),
+
+ ARlist = [hd(mnesia:dirty_read(Tab, K)) || K <- lists:seq(1, 10)],
+ ?match({'EXIT',{aborted,{bad_type,_}}},
+ mnesia:dirty_write(Tab, #new_replica_management{})),
+ ?match(ok, mnesia:dirty_write(Tab, {last_rec, k, v, e, dummy})),
+
+ ?verify_mnesia(Nodes, []).
+
+schema_availability(doc) ->
+ ["Test that schema succeeds (or fails) as intended when some db nodes are down."];
+schema_availability(suite) ->
+ [];
+schema_availability(Config) when is_list(Config) ->
+ [N1, _N2, N3] = Nodes = ?acquire_nodes(3, Config),
+ Tab = schema_availability,
+ Storage = mnesia_test_lib:storage_type(ram_copies, Config),
+ Def1 = [{Storage, [N1, N3]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def1)),
+
+ N = 10,
+ ?match(ok, mnesia:sync_dirty(fun() -> [mnesia:write({Tab, K, K + 2}) || K <- lists:seq(1, N)], ok end)),
+ ?match({[N,0,N], []}, rpc:multicall(Nodes, mnesia, table_info, [Tab, size])),
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+ ?match({[N,0,0], []}, rpc:multicall(Nodes, mnesia, table_info, [Tab, size])),
+
+ ?match([], mnesia_test_lib:start_mnesia([N3], [Tab])),
+ ?match({[N,0,N], []}, rpc:multicall(Nodes, mnesia, table_info, [Tab, size])),
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+
+ ?match({atomic, ok}, mnesia:clear_table(Tab)),
+ ?match({[0,0,0], []}, rpc:multicall(Nodes, mnesia, table_info, [Tab, size])),
+
+ ?match([], mnesia_test_lib:start_mnesia([N3], [Tab])),
+ ?match({[0,0,0], []}, rpc:multicall(Nodes, mnesia, table_info, [Tab, size])),
+
+ ?verify_mnesia(Nodes, []).
+
+-define(badrpc(Tab), {badrpc, {'EXIT', {aborted,{no_exists,Tab}}}}).
+
+local_content(doc) ->
+ ["Test local_content functionality, we want to see that correct"
+ " properties gets propageted correctly between nodes"];
+local_content(suite) -> [];
+local_content(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab1 = local1,
+ Def1 = [{local_content, true}, {ram_copies, Nodes}],
+ Tab2 = local2,
+ Def2 = [{local_content, true}, {disc_copies, [Node1]}],
+ Tab3 = local3,
+ Def3 = [{local_content, true}, {disc_only_copies, [Node1]}],
+ Tab4 = local4,
+ Def4 = [{local_content, true}, {ram_copies, [Node1]}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+ ?match({atomic, ok}, mnesia:create_table(Tab4, Def4)),
+
+ ?match(ok, rpc:call(Node1, mnesia, dirty_write, [{Tab1, 1, Node1}])),
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab1, 1, Node2}])),
+ ?match(ok, rpc:call(Node3, mnesia, dirty_write, [{Tab1, 1, Node3}])),
+ ?match(ok, rpc:call(Node1, mnesia, dirty_write, [{Tab2, 1, Node1}])),
+ ?match(ok, rpc:call(Node1, mnesia, dirty_write, [{Tab3, 1, Node1}])),
+ ?match(ok, rpc:call(Node1, mnesia, dirty_write, [{Tab4, 1, Node1}])),
+
+ ?match(?badrpc(Tab2), rpc:call(Node2, mnesia, dirty_write, [{Tab2, 1, Node2}])),
+ ?match(?badrpc(Tab3), rpc:call(Node2, mnesia, dirty_write, [{Tab3, 1, Node2}])),
+ ?match(?badrpc(Tab4), rpc:call(Node2, mnesia, dirty_write, [{Tab4, 1, Node2}])),
+
+ ?match({atomic, ok}, rpc:call(Node1, mnesia, add_table_copy, [Tab2, Node2, ram_copies])),
+ ?match({atomic, ok}, rpc:call(Node2, mnesia, add_table_copy, [Tab3, Node2, disc_copies])),
+ ?match({atomic, ok}, rpc:call(Node3, mnesia, add_table_copy, [Tab4, Node2, disc_only_copies])),
+ ?match([], rpc:call(Node2, mnesia, dirty_read, [{Tab2, 1}])),
+ ?match([], rpc:call(Node2, mnesia, dirty_read, [{Tab3, 1}])),
+ ?match([], rpc:call(Node2, mnesia, dirty_read, [{Tab4, 1}])),
+
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab2, 1, Node2}])),
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab3, 1, Node2}])),
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab4, 1, Node2}])),
+
+ ?match([{Tab1, 1, Node1}], rpc:call(Node1, mnesia, dirty_read, [{Tab1, 1}])),
+ ?match([{Tab2, 1, Node1}], rpc:call(Node1, mnesia, dirty_read, [{Tab2, 1}])),
+ ?match([{Tab3, 1, Node1}], rpc:call(Node1, mnesia, dirty_read, [{Tab3, 1}])),
+ ?match([{Tab4, 1, Node1}], rpc:call(Node1, mnesia, dirty_read, [{Tab4, 1}])),
+
+ ?match([{Tab1, 1, Node2}], rpc:call(Node2, mnesia, dirty_read, [{Tab1, 1}])),
+ ?match([{Tab2, 1, Node2}], rpc:call(Node2, mnesia, dirty_read, [{Tab2, 1}])),
+ ?match([{Tab3, 1, Node2}], rpc:call(Node2, mnesia, dirty_read, [{Tab3, 1}])),
+ ?match([{Tab4, 1, Node2}], rpc:call(Node2, mnesia, dirty_read, [{Tab4, 1}])),
+
+ ?match([{Tab1, 1, Node3}], rpc:call(Node3, mnesia, dirty_read, [{Tab1, 1}])),
+ ?match(?badrpc([_Tab2, 1]), rpc:call(Node3, mnesia, dirty_read, [{Tab2, 1}])),
+ ?match(?badrpc([_Tab3, 1]), rpc:call(Node3, mnesia, dirty_read, [{Tab3, 1}])),
+ ?match(?badrpc([_Tab4, 1]), rpc:call(Node3, mnesia, dirty_read, [{Tab4, 1}])),
+
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(schema, Node3, ram_copies)),
+ ?match([], mnesia_test_lib:stop_mnesia([Node3])),
+
+ %% Added for OTP-44306
+ ?match(ok, rpc:call(Node3, mnesia, start, [])),
+ ?match({ok, _}, mnesia:change_config(extra_db_nodes, [Node3])),
+
+ mnesia_test_lib:sync_tables([Node3], [Tab1]),
+
+ ?match([], rpc:call(Node3, mnesia, dirty_read, [{Tab1, 1}])),
+
+ ?match({atomic, ok}, rpc:call(Node1, mnesia, clear_table, [Tab1])),
+
+ SyncedCheck = fun(Tab) ->
+ mnesia:lock({record,Tab,0}, write),
+ {OK, []} = rpc:multicall(Nodes, mnesia, table_info, [Tab, size]),
+ OK
+ end,
+ ?match({atomic, [0,1,0]}, mnesia:transaction(SyncedCheck, [Tab1])),
+ ?match({atomic, ok}, rpc:call(Node2, mnesia, clear_table, [Tab2])),
+ ?match({atomic, [1,0,0]}, mnesia:transaction(SyncedCheck, [Tab2])),
+ ?match({atomic, ok}, rpc:call(Node2, mnesia, clear_table, [Tab3])),
+ ?match({atomic, [1,0,0]}, mnesia:transaction(SyncedCheck, [Tab3])),
+
+ ?verify_mnesia(Nodes, []).
+
+
+change_table_access_mode(suite) -> [];
+change_table_access_mode(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab = test_access_mode_tab,
+
+ Def = case mnesia_test_lib:diskless(Config) of
+ true -> [{name, Tab}, {ram_copies, Nodes}];
+ false -> [{name, Tab}, {ram_copies, [Node1]},
+ {disc_copies, [Node2]},
+ {disc_only_copies, [Node3]}]
+ end,
+ ?match({atomic, ok}, mnesia:create_table(Def)),
+
+ Write = fun(What) -> mnesia:write({Tab, 1, What}) end,
+ Read = fun() -> mnesia:read({Tab, 1}) end,
+
+ ?match({atomic, ok}, mnesia:transaction(Write, [test_ok])),
+ %% test read_only
+ ?match({atomic, ok}, mnesia:change_table_access_mode(Tab, read_only)),
+ ?match({aborted, _}, mnesia:transaction(Write, [nok])),
+ ?match({'EXIT', {aborted, _}}, mnesia:dirty_write({Tab, 1, [nok]})),
+ ?match({aborted, _}, rpc:call(Node2, mnesia, transaction, [Write, [nok]])),
+ ?match({aborted, _}, rpc:call(Node3, mnesia, transaction, [Write, [nok]])),
+ ?match({atomic, [{Tab, 1, test_ok}]}, mnesia:transaction(Read)),
+ %% test read_write
+ ?match({atomic, ok}, mnesia:change_table_access_mode(Tab, read_write)),
+ ?match({atomic, ok}, mnesia:transaction(Write, [test_ok1])),
+ ?match({atomic, [{Tab, 1, test_ok1}]}, mnesia:transaction(Read)),
+ ?match({atomic, ok}, rpc:call(Node2, mnesia, transaction, [Write, [test_ok2]])),
+ ?match({atomic, [{Tab, 1, test_ok2}]}, mnesia:transaction(Read)),
+ ?match({atomic, ok}, rpc:call(Node3, mnesia, transaction, [Write, [test_ok3]])),
+ ?match({atomic, [{Tab, 1, test_ok3}]}, mnesia:transaction(Read)),
+
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+
+ Def4 = [{name, Tab}, {access_mode, read_only_bad}],
+ ?match({aborted, {bad_type, _, _}}, mnesia:create_table(Def4)),
+
+ Def2 = [{name, Tab}, {access_mode, read_only}],
+ ?match({atomic, ok}, mnesia:create_table(Def2)),
+ ?match({aborted, _}, mnesia:transaction(Write, [nok])),
+
+ ?match({atomic, ok}, mnesia:change_table_access_mode(Tab, read_write)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+
+ Def3 = [{name, Tab}, {mnesia_test_lib:storage_type(disc_copies, Config),
+ [Node1, Node2]},
+ {access_mode, read_write}],
+ ?match({atomic, ok}, mnesia:create_table(Def3)),
+ ?match({atomic, ok}, mnesia:transaction(Write, [ok])),
+ ?match({atomic, ok}, mnesia:change_table_access_mode(Tab, read_only)),
+ ?match({aborted, _}, mnesia:del_table_copy(Tab, Node2)),
+ ?match({aborted, _}, mnesia:del_table_copy(Tab, Node1)),
+ ?match({aborted, _}, mnesia:delete_table(Tab)),
+ ?match({atomic, ok}, mnesia:change_table_access_mode(Tab, read_write)),
+
+ ?match({aborted, {bad_type, _, _}},
+ mnesia:change_table_access_mode(Tab, strange_atom)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+
+ ?match({aborted, {no_exists, _}},
+ mnesia:change_table_access_mode(err_tab, read_only)),
+ ?match({aborted, {no_exists, _}},
+ mnesia:change_table_access_mode([Tab], read_only)),
+ ?verify_mnesia(Nodes, []).
+
+change_table_load_order(suite) -> [];
+change_table_load_order(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab1 = load_order_tab1,
+ Tab2 = load_order_tab2,
+ Tab3 = load_order_tab3,
+
+ Def = case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{ram_copies, [Node1]},
+ {disc_copies, [Node2]},
+ {disc_only_copies, [Node3]}]
+ end,
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def)),
+
+ ?match({aborted, _}, mnesia:change_table_load_order(Tab1, should_be_integer)),
+ ?match({aborted, _}, mnesia:change_table_load_order(err_tab, 5)),
+ ?match({aborted, _}, mnesia:change_table_load_order([err_tab], 5)),
+ ?match({atomic, ok}, mnesia:change_table_load_order(Tab1, 5)),
+ ?match({atomic, ok}, mnesia:change_table_load_order(Tab2, 4)),
+ ?match({atomic, ok}, mnesia:change_table_load_order(Tab3, 73)),
+
+ ?match({aborted, _}, mnesia:change_table_load_order(schema, -32)),
+
+ ?verify_mnesia(Nodes, []).
+
+set_master_nodes(suite) -> [];
+set_master_nodes(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab1 = master_node_tab1,
+ Tab2 = master_node_tab2,
+ Tab3 = master_node_tab3,
+ Def1 = [{ram_copies, [Node1, Node2]}],
+ Def2 = [{disc_copies, [Node2, Node3]}],
+ Def3 = [{disc_only_copies, [Node3, Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+
+ ?match({error, _}, mnesia:set_master_nodes(schema, ['[email protected]'])),
+ ?match({error, _}, mnesia:set_master_nodes(badtab, [Node2, Node3])),
+ ?match({error, _}, mnesia:set_master_nodes(Tab1, [Node3])),
+ ?match([], mnesia:table_info(Tab1, master_nodes)),
+
+ ?match(ok, mnesia:set_master_nodes(schema, [Node3, Node1])),
+ ?match([Node3, Node1], mnesia:table_info(schema, master_nodes)),
+ ?match(ok, mnesia:set_master_nodes(Tab1, [Node2])),
+ ?match([Node2], mnesia:table_info(Tab1, master_nodes)),
+ ?match(ok, mnesia:set_master_nodes(Tab1, [Node2, Node1])),
+ ?match([Node2, Node1], mnesia:table_info(Tab1, master_nodes)),
+ ?match(ok, mnesia:set_master_nodes(Tab2, [Node2])), % Should set where_to_read to Node2!
+ ?match([Node2], mnesia:table_info(Tab2, master_nodes)),
+ ?match(ok, mnesia:set_master_nodes(Tab3, [Node3])),
+ ?match([Node3], mnesia:table_info(Tab3, master_nodes)),
+ ?match(ok, mnesia:set_master_nodes(Tab3, [])),
+ ?match([], mnesia:table_info(Tab3, master_nodes)),
+
+ ?match(ok, mnesia:set_master_nodes([Node1])),
+ ?match([Node1], mnesia:table_info(schema, master_nodes)),
+ ?match([Node1], mnesia:table_info(Tab1, master_nodes)),
+ ?match([], mnesia:table_info(Tab2, master_nodes)),
+ ?match([Node1], mnesia:table_info(Tab3, master_nodes)),
+
+ ?match(ok, mnesia:set_master_nodes([Node1, Node2])),
+ ?match([Node1, Node2], mnesia:table_info(schema, master_nodes)),
+ ?match([Node1, Node2], mnesia:table_info(Tab1, master_nodes)),
+ ?match([Node2], mnesia:table_info(Tab2, master_nodes)),
+ ?match([Node1], mnesia:table_info(Tab3, master_nodes)),
+
+ ?verify_mnesia(Nodes, []).
+
+offline_set_master_nodes(suite) -> [];
+offline_set_master_nodes(Config) when is_list(Config) ->
+ [Node] = Nodes = ?acquire_nodes(1, Config),
+ Tab1 = offline_master_node_tab1,
+ Tab2 = offline_master_node_tab2,
+ Tab3 = offline_master_node_tab3,
+ Tabs = ?sort([Tab1, Tab2, Tab3]),
+ Def1 = [{ram_copies, [Node]}],
+ Def2 = [{disc_copies, [Node]}],
+ Def3 = [{disc_only_copies, [Node]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+ ?match([], mnesia:system_info(master_node_tables)),
+ ?match([], mnesia_test_lib:stop_mnesia([Node])),
+
+ ?match(ok, mnesia:set_master_nodes(Tab1, [Node])),
+ ?match(ok, mnesia:set_master_nodes(Tab2, [Node])),
+ ?match(ok, mnesia:set_master_nodes(Tab3, [Node])),
+ ?match([], mnesia_test_lib:start_mnesia([Node])),
+ ?match(Tabs, ?sort(mnesia:system_info(master_node_tables))),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node])),
+ ?match(ok, mnesia:set_master_nodes(Tab1, [])),
+ ?match(ok, mnesia:set_master_nodes(Tab2, [])),
+ ?match(ok, mnesia:set_master_nodes(Tab3, [])),
+ ?match([], mnesia_test_lib:start_mnesia([Node])),
+ ?match([], mnesia:system_info(master_node_tables)),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node])),
+ ?match(ok, mnesia:set_master_nodes([Node])),
+ ?match([], mnesia_test_lib:start_mnesia([Node])),
+ AllTabs = ?sort([schema | Tabs]),
+ ?match(AllTabs, ?sort(mnesia:system_info(master_node_tables))),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node])),
+ ?match(ok, mnesia:set_master_nodes([])),
+ ?match([], mnesia_test_lib:start_mnesia([Node])),
+ ?match([], mnesia:system_info(master_node_tables)),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Syncronize table with log or disc
+%%
+
+%% Dump ram tables on disc
+dump_tables(suite) -> [];
+dump_tables(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = dump_tables,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ %% Dump 10 records
+ Size = 10,
+ Keys = lists:seq(1, Size),
+ Records = [{Tab, A, 7} || A <- Keys],
+ lists:foreach(fun(Rec) -> ?match(ok, mnesia:dirty_write(Rec)) end, Records),
+
+ AllKeys = fun() -> ?sort(mnesia:all_keys(Tab)) end,
+
+ ?match({atomic, Keys}, mnesia:transaction(AllKeys)),
+ ?match({atomic, ok}, mnesia:dump_tables([Tab])),
+
+ %% Delete one record
+ ?match(ok, mnesia:dirty_delete({Tab, 5})),
+ Keys2 = lists:delete(5, Keys),
+
+ ?match({atomic, Keys2}, mnesia:transaction(AllKeys)),
+
+ %% Check that all 10 is restored after a stop
+ ?match([], mnesia_test_lib:stop_mnesia([Node1, Node2])),
+ ?match([], mnesia_test_lib:start_mnesia([Node1, Node2])),
+ ?match(ok, mnesia:wait_for_tables([Tab], infinity)),
+
+ ?match({atomic, Keys}, mnesia:transaction(AllKeys)),
+
+ ?match({aborted,Reason} when element(1, Reason) == no_exists,
+ mnesia:dump_tables([foo])),
+ ?verify_mnesia(Nodes, []).
+
+dump_log(suite) -> [];
+dump_log(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = dump_log,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1, Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ Tab1 = dump_log1,
+ Schema1 = [{name, Tab1}, {attributes, [k, v]}, {disc_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema1)),
+ Tab2 = dump_log2,
+ Schema2 = [{name, Tab2}, {attributes, [k, v]}, {disc_only_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema2)),
+
+ ?match(ok, mnesia:dirty_write({Tab, 1, ok})),
+ ?match(ok, mnesia:dirty_write({Tab1, 1, ok})),
+ ?match(ok, mnesia:dirty_write({Tab2, 1, ok})),
+
+ ?match(dumped, mnesia:dump_log()),
+ ?match(dumped, rpc:call(Node2, mnesia, dump_log, [])),
+
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, Node2, ram_copies)),
+ ?match(dumped, rpc:call(Node2, mnesia, dump_log, [])),
+
+ Self = self(),
+ spawn(fun() -> dump_log(100, Self) end),
+ spawn(fun() -> dump_log(100, Self) end),
+
+ ?match(ok, receive finished -> ok after 3000 -> timeout end),
+ ?match(ok, receive finished -> ok after 3000 -> timeout end),
+
+ ?verify_mnesia(Nodes, []).
+
+dump_log(N, Tester) when N > 0 ->
+ mnesia:dump_log(),
+ dump_log(N-1, Tester);
+dump_log(_, Tester) ->
+ Tester ! finished.
+
+
+wait_for_tables(doc) ->
+ ["Intf. test of wait_for_tables, see also force_load_table"];
+wait_for_tables(suite) -> [];
+wait_for_tables(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = wf_tab,
+ Schema = [{name, Tab}, {ram_copies, [Node1, Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ?match(ok, mnesia:wait_for_tables([wf_tab], infinity)),
+ ?match(ok, mnesia:wait_for_tables([], timer:seconds(5))),
+ ?match({timeout, [bad_tab]}, mnesia:wait_for_tables([bad_tab], timer:seconds(5))),
+ ?match(ok, mnesia:wait_for_tables([wf_tab], 0)),
+ ?match({error,_}, mnesia:wait_for_tables([wf_tab], -1)),
+ ?verify_mnesia(Nodes, []).
+
+force_load_table(suite) -> [];
+force_load_table(Config) when is_list(Config) ->
+ [Node1, Node2] = ?acquire_nodes(2, Config),
+ Tab = wf_tab,
+
+ Schema = [{name, Tab}, {disc_copies, [Node1, Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, test_ok})),
+ mnesia_test_lib:kill_mnesia([Node1]),
+ ?match(ok, rpc:call(Node2, mnesia, dirty_write, [{Tab, 1, test_nok}])),
+ mnesia_test_lib:kill_mnesia([Node2]),
+ %% timer:sleep(timer:seconds(5)),
+ ?match(ok, mnesia:start()),
+ ?match({timeout, [Tab]}, mnesia:wait_for_tables([Tab], 5)),
+ ?match({'EXIT', _}, mnesia:dirty_read({Tab, 1})),
+ ?match(yes, mnesia:force_load_table(Tab)),
+ ?match([{Tab, 1, test_ok}], mnesia:dirty_read({Tab, 1})),
+
+ ?match({error, _}, mnesia:force_load_table(error_tab)),
+ ?verify_mnesia([Node1], [Node2]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+user_properties(doc) ->
+ ["Test of reading, writing and deletion of user properties",
+ "Plus initialization of user properties when a table is created",
+ "Do also test mnesia:table_info(Tab, user_properties)"];
+user_properties(suite) -> [];
+user_properties(Config) when is_list(Config) ->
+ [Node] = Nodes = ?acquire_nodes(1, Config),
+ Tab1 = user_properties_1,
+ Tab2 = user_properties_2,
+ Tab3 = user_properties_3,
+ Def1 = [{ram_copies, [Node]}, {user_properties, []}],
+ Def2 = [{mnesia_test_lib:storage_type(disc_copies, Config), [Node]}],
+ Def3 = [{mnesia_test_lib:storage_type(disc_only_copies, Config), [Node]},
+ {user_properties, []}],
+
+ PropKey = my_prop,
+ Prop = {PropKey, some, elements},
+ Prop2 = {PropKey, some, other, elements},
+ YourProp= {your_prop},
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+
+ ?match([], mnesia:table_info(Tab1, user_properties)),
+ ?match([], mnesia:table_info(Tab2, user_properties)),
+ ?match([], mnesia:table_info(Tab3, user_properties)),
+
+ ?match({'EXIT', {no_exists, {Tab1, user_property, PropKey}}},
+ mnesia:read_table_property(Tab1, PropKey)),
+ ?match({'EXIT', {no_exists, {Tab2, user_property, PropKey}}},
+ mnesia:read_table_property(Tab2, PropKey)),
+ ?match({'EXIT', {no_exists, {Tab3, user_property, PropKey}}},
+ mnesia:read_table_property(Tab3, PropKey)),
+
+ ?match({atomic, ok}, mnesia:write_table_property(Tab1, Prop)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab2, Prop)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab3, Prop)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab1, YourProp)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab2, YourProp)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab3, YourProp)),
+
+ ?match(Prop, mnesia:read_table_property(Tab1, PropKey)),
+ ?match(Prop, mnesia:read_table_property(Tab2, PropKey)),
+ ?match(Prop, mnesia:read_table_property(Tab3, PropKey)),
+
+ ?match({atomic, ok}, mnesia:write_table_property(Tab1, Prop2)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab2, Prop2)),
+ ?match({atomic, ok}, mnesia:write_table_property(Tab3, Prop2)),
+ ?match(Prop2, mnesia:read_table_property(Tab1, PropKey)),
+ ?match(Prop2, mnesia:read_table_property(Tab2, PropKey)),
+ ?match(Prop2, mnesia:read_table_property(Tab3, PropKey)),
+
+ ?match({atomic, ok}, mnesia:delete_table_property(Tab1, PropKey)),
+ ?match({atomic, ok}, mnesia:delete_table_property(Tab2, PropKey)),
+ ?match({atomic, ok}, mnesia:delete_table_property(Tab3, PropKey)),
+
+ ?match([YourProp], mnesia:table_info(Tab1, user_properties)),
+ ?match([YourProp], mnesia:table_info(Tab2, user_properties)),
+ ?match([YourProp], mnesia:table_info(Tab3, user_properties)),
+
+ Tab4 = user_properties_4,
+ ?match({atomic, ok},
+ mnesia:create_table(Tab4, [{user_properties, [Prop]}])),
+
+ ?match([Prop], mnesia:table_info(Tab4, user_properties)),
+
+ %% Some error cases
+
+ ?match({aborted, {bad_type, Tab1, {}}},
+ mnesia:write_table_property(Tab1, {})),
+ ?match({aborted, {bad_type, Tab1, ali}},
+ mnesia:write_table_property(Tab1, ali)),
+
+ Tab5 = user_properties_5,
+ ?match({aborted, {bad_type, Tab5, {user_properties, {}}}},
+ mnesia:create_table(Tab5, [{user_properties, {}}])),
+ ?match({aborted, {bad_type, Tab5, {user_properties, ali}}},
+ mnesia:create_table(Tab5, [{user_properties, ali}])),
+ ?match({aborted, {bad_type, Tab5, {user_properties, [{}]}}},
+ mnesia:create_table(Tab5, [{user_properties, [{}]}])),
+ ?match({aborted, {bad_type, Tab5, {user_properties, [ali]}}},
+ mnesia:create_table(Tab5, [{user_properties, [ali]}])),
+
+ ?verify_mnesia(Nodes, []).
+
+
+unsupp_user_props(doc) ->
+ ["Simple test of adding user props in a schema_transaction"];
+unsupp_user_props(suite) -> [];
+unsupp_user_props(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab1 = silly1,
+ Tab2 = silly2,
+ Storage = mnesia_test_lib:storage_type(ram_copies, Config),
+
+ ?match({atomic, ok}, rpc:call(Node1, mnesia,
+ create_table, [Tab1, [{Storage, [Node1]}]])),
+ ?match({atomic, ok}, rpc:call(Node1, mnesia,
+ create_table, [Tab2, [{Storage, [Node1]}]])),
+
+ F1 = fun() ->
+ mnesia_schema:do_write_table_property(
+ silly1, {prop, propval1}),
+ mnesia_schema:do_write_table_property(
+ silly2, {prop, propval2}), % same key as above
+ mnesia_schema:do_write_table_property(
+ schema, {prop, propval3}) % same key as above
+ end,
+ ?match({atomic, ok}, rpc:call(Node1, mnesia_schema,
+ schema_transaction, [F1])),
+
+ ?match([{prop,propval1}], rpc:call(Node1, mnesia,
+ table_info, [silly1, user_properties])),
+ ?match([{prop,propval2}], rpc:call(Node1, mnesia,
+ table_info, [silly2, user_properties])),
+ ?match([{prop,propval3}], rpc:call(Node1, mnesia,
+ table_info, [schema, user_properties])),
+
+ F2 = fun() ->
+ mnesia_schema:do_write_table_property(
+ silly1, {prop, propval1a}),
+ mnesia_schema:do_write_table_property(
+ silly1, {prop, propval1b}) % same key as above
+ end,
+ ?match({atomic, ok}, rpc:call(Node1, mnesia_schema,
+ schema_transaction, [F2])),
+
+ ?match([{prop,propval1b}], rpc:call(Node1, mnesia,
+ table_info,
+ [silly1, user_properties])),
+ ?verify_mnesia([Node1], []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+snmp_open_table(suite) -> [];
+snmp_open_table(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab1 = local_snmp_table,
+
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ Def1 =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{disc_copies, [Node1]}, {ram_copies, [Node2]}]
+ end,
+
+ Tab2 = ext_snmp_table,
+ Def2 = [{Storage, [Node2]}],
+ ErrTab = non_existing_tab,
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab1, [{key, integer}])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab2, [{key, integer}])),
+ ?match({aborted, _}, mnesia:snmp_open_table(ErrTab, [{key, integer}])),
+ ?verify_mnesia(Nodes, []).
+
+snmp_close_table(suite) -> [];
+snmp_close_table(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab1 = local_snmp_table,
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ Def1 =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{disc_copies, [Node1]}, {ram_copies, [Node2]}]
+ end,
+
+ Tab2 = ext_snmp_table,
+ Def2 = [{snmp, [{key, integer}]}, {Storage, [Node2]}],
+ ErrTab = non_existing_tab,
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(no_snmp_tab, [])),
+ add_some_records(Tab1, Tab2, 3),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab1, [{key, integer}])),
+ add_some_records(Tab1, Tab2, 5),
+ ?match({atomic, ok}, mnesia:snmp_close_table(Tab1)),
+
+ Transform = fun(Tab, Key) ->
+ [{T,K,V}] = mnesia:read(Tab, Key, write),
+ mnesia:delete(T,K, write),
+ mnesia:write({T, {K,K}, V, 43+V})
+ end,
+
+ ?match({atomic, ok}, mnesia:transform_table(Tab1, ignore, [key,val,new])),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ mnesia:write_lock_table(Tab1),
+ Keys = mnesia:select(Tab1, [{{'_','$1','_'}, [],
+ ['$1']}]),
+ [Transform(Tab1, Key) || Key <- Keys],
+ ok
+ end)),
+ ?match([{Tab1, {1, 1}, 1, 44}], mnesia:dirty_read(Tab1, {1, 1})),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab1, [{key,{integer,integer}}])),
+
+ ?match({atomic, ok}, mnesia:snmp_close_table(Tab2)),
+ ?match({atomic, ok}, mnesia:transform_table(Tab2, ignore, [key,val,new])),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() ->
+ mnesia:write_lock_table(Tab2),
+ Keys = mnesia:select(Tab2, [{{'_','$1','_'}, [],
+ ['$1']}]),
+ [Transform(Tab2, Key) || Key <- Keys],
+ ok
+ end)),
+
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab2, [{key,{integer,integer}}])),
+
+ %% Should be aborted ????
+ ?match({atomic, ok}, mnesia:snmp_close_table(no_snmp_tab)),
+ ?match({aborted, _}, mnesia:snmp_close_table(ErrTab)),
+ ?verify_mnesia(Nodes, []).
+
+snmp_get_next_index(suite) -> [];
+snmp_get_next_index(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab1 = local_snmp_table,
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ Def1 =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{disc_copies, [Node1]}, {ram_copies, [Node2]}]
+ end,
+
+ Tab2 = ext_snmp_table,
+ Def2 = [{Storage, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab1, [{key, integer}])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab2, [{key, integer}])),
+ add_some_records(Tab1, Tab2, 1),
+ Test =
+ fun() ->
+ %% Test local tables
+ {success, Res11} = ?match({ok, _}, mnesia:snmp_get_next_index(Tab1,[])),
+ {ok, Index11} = Res11,
+ {success, _Res12} =
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab1, Index11)),
+ ?match({'EXIT',_}, mnesia:snmp_get_next_index(Tab1, endOfTable)),
+
+ %% Test external table
+ {success, Res21} =
+ ?match({ok, _}, mnesia:snmp_get_next_index(Tab2, [])),
+ {ok, Index21} = Res21,
+ {success, _Res22} =
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab2, Index21)),
+
+ {ok, Row} = mnesia:snmp_get_row(Tab1, Index11),
+ ?match(ok, mnesia:dirty_delete(Tab1, hd(Index11))),
+
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab1,[])),
+
+ ok = mnesia:dirty_write(Row), %% Reset to coming tests
+
+ %% Test of non existing table
+ %%?match(endOfTable, mnesia:snmp_get_next_index(ErrTab, [])),
+ ok
+ end,
+ ?match(ok, Test()),
+ ?match({atomic,ok}, mnesia:transaction(Test)),
+ ?match(ok, mnesia:sync_dirty(Test)),
+ ?match(ok, mnesia:activity(transaction,Test,mnesia)),
+
+ %%io:format("**********Before ~p~n", [mnesia_lib:val({Tab1,snmp})]),
+ %%io:format(" ~p ~n", [ets:tab2list(mnesia_lib:val({local_snmp_table,{index,snmp}}))]),
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab1, Tab2])),
+ %%io:format("**********After ~p~n", [mnesia_lib:val({Tab1,snmp})]),
+ %%io:format(" ~p ~n", [ets:tab2list(mnesia_lib:val({local_snmp_table,{index,snmp}}))]),
+
+ ?match(ok, Test()),
+ ?match({atomic,ok}, mnesia:transaction(Test)),
+ ?match(ok, mnesia:sync_dirty(Test)),
+ ?match(ok, mnesia:activity(transaction,Test,mnesia)),
+
+ ?verify_mnesia(Nodes, []).
+
+add_some_records(Tab1, Tab2, N) ->
+ Recs1 = [{Tab1, I, I} || I <- lists:reverse(lists:seq(1, N))],
+ Recs2 = [{Tab2, I, I} || I <- lists:reverse(lists:seq(20, 20+N-1))],
+ lists:foreach(fun(R) -> mnesia:dirty_write(R) end, Recs1),
+ Fun = fun(R) -> mnesia:write(R) end,
+ Trans = fun() -> lists:foreach(Fun, Recs2) end,
+ {atomic, ok} = mnesia:transaction(Trans),
+ %% Sync things, so everything gets everywhere!
+ {atomic, ok} = mnesia:sync_transaction(fun() -> mnesia:write(lists:last(Recs1)) end),
+ {atomic, ok} = mnesia:sync_transaction(fun() -> mnesia:write(lists:last(Recs2)) end),
+ ?sort(Recs1 ++ Recs2).
+
+snmp_get_row(suite) -> [];
+snmp_get_row(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab1 = local_snmp_table,
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ Def1 =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{disc_copies, [Node1]}, {ram_copies, [Node2]}]
+ end,
+ Tab2 = ext_snmp_table,
+ Def2 = [{Storage, [Node2]}],
+ Tab3 = snmp_table,
+ Def3 = [{Storage, [Node1]},
+ {attributes, [key, data1, data2]}],
+
+ Setup = fun() ->
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab1, [{key, integer}])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab2, [{key, integer}])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(
+ Tab3, [{key, {fix_string,integer}}])),
+ add_some_records(Tab1, Tab2, 1)
+ end,
+ Clear = fun() ->
+ ?match({atomic, ok}, mnesia:delete_table(Tab1)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab2)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab3))
+ end,
+ Test =
+ fun() ->
+ %% Test local tables
+ {success, Res11} =
+ ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
+ {ok, Index11} = Res11,
+ ?match({ok, {Tab1,1,1}}, mnesia:snmp_get_row(Tab1, Index11)),
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab1, Index11)),
+ ?match({'EXIT',_}, mnesia:snmp_get_row(Tab1, endOfTable)),
+ ?match(undefined, mnesia:snmp_get_row(Tab1, [73])),
+
+ Add = fun() -> mnesia:write({Tab3, {"f_string", 3}, data1, data2}) end,
+ ?match({atomic, ok}, mnesia:transaction(Add)),
+ {success, {ok, Index31}} = ?match({ok, RowIndex31} when is_list(RowIndex31),
+ mnesia:snmp_get_next_index(Tab3,[])),
+ ?match({ok, Row31} when is_tuple(Row31),
+ mnesia:snmp_get_row(Tab3, Index31)),
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab3, Index31)),
+ Del = fun() -> mnesia:delete({Tab3,{"f_string",3}}) end,
+ ?match({atomic, ok}, mnesia:transaction(Del)),
+ ?match(undefined, mnesia:snmp_get_row(Tab3, "f_string" ++ [3])),
+ ?match(undefined, mnesia:snmp_get_row(Tab3, "f_string" ++ [73])),
+
+ %% Test external table
+ {success, Res21} = ?match({ok,[20]}, mnesia:snmp_get_next_index(Tab2, [])),
+ {ok, Index21} = Res21,
+ ?match({ok, Row2} when is_tuple(Row2), mnesia:snmp_get_row(Tab2, Index21)),
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab2, Index21)),
+ %% Test of non existing table
+ %% ?match(endOfTable, mnesia:snmp_get_next_index(ErrTab, [])),
+ ok
+ end,
+ Setup(),
+ ?match(ok, Test()),
+ Clear(), Setup(),
+ ?match({atomic,ok}, mnesia:transaction(Test)),
+ Clear(), Setup(),
+ ?match(ok, mnesia:sync_dirty(Test)),
+ Clear(), Setup(),
+ ?match(ok, mnesia:activity(transaction,Test,mnesia)),
+
+ Clear(), Setup(),
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab1, Tab2])),
+ ?match(ok, Test()),
+ Clear(), Setup(),
+ ?match([], mnesia_test_lib:stop_mnesia(Nodes)),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab1, Tab2])),
+ ?match({atomic,ok}, mnesia:transaction(Test)),
+
+ ?verify_mnesia(Nodes, []).
+
+snmp_get_mnesia_key(suite) -> [];
+snmp_get_mnesia_key(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab1 = local_snmp_table,
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ Def1 =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{disc_copies, [Node1]}, {ram_copies, [Node2]}]
+ end,
+
+ Tab2 = ext_snmp_table,
+ Def2 = [{Storage, [Node2]}],
+
+ Tab3 = fix_string,
+ Setup = fun() ->
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def1)),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab1, [{key, integer}])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab2, [{key, integer}])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab3, [{key, {fix_string,integer}}])),
+
+ add_some_records(Tab1, Tab2, 1)
+ end,
+ Clear = fun() ->
+ ?match({atomic, ok}, mnesia:delete_table(Tab1)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab2)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab3))
+ end,
+ Test =
+ fun() ->
+ %% Test local tables
+ {success, Res11} =
+ ?match({ok, [1]}, mnesia:snmp_get_next_index(Tab1,[])),
+ {ok, Index11} = Res11,
+ ?match({ok, 1}, mnesia:snmp_get_mnesia_key(Tab1, Index11)),
+ %% Test external tables
+ {success, Res21} =
+ ?match({ok, [20]}, mnesia:snmp_get_next_index(Tab2, [])),
+ {ok, Index21} = Res21,
+ ?match({ok, 20}, mnesia:snmp_get_mnesia_key(Tab2, Index21)),
+ ?match(undefined, mnesia:snmp_get_mnesia_key(Tab2, [97])),
+ ?match({'EXIT', _}, mnesia:snmp_get_mnesia_key(Tab2, 97)),
+
+ ?match({atomic,ok}, mnesia:transaction(fun() -> mnesia:delete({Tab1,1}) end)),
+ ?match(undefined, mnesia:snmp_get_mnesia_key(Tab1, Index11)),
+
+ ?match({atomic,ok},mnesia:transaction(fun() -> mnesia:write({Tab1,73,7}) end)),
+ ?match({ok, 73}, mnesia:snmp_get_mnesia_key(Tab1, [73])),
+ ?match({atomic,ok}, mnesia:transaction(fun() -> mnesia:delete({Tab1,73}) end)),
+ ?match(undefined, mnesia:snmp_get_mnesia_key(Tab1, [73])),
+
+ ?match({atomic,ok},mnesia:transaction(fun() -> mnesia:write({Tab3,{"S",5},7}) end)),
+ ?match({ok,{"S",5}}, mnesia:snmp_get_mnesia_key(Tab3, [$S,5])),
+ ?match({atomic,ok},mnesia:transaction(fun() -> mnesia:delete({Tab3,{"S",5}}) end)),
+ ?match(undefined, mnesia:snmp_get_mnesia_key(Tab3, [$S,5])),
+
+ ok
+ end,
+ Setup(),
+ ?match(ok, Test()),
+ Clear(), Setup(),
+ ?match({atomic,ok}, mnesia:transaction(Test)),
+ Clear(), Setup(),
+ ?match(ok, mnesia:sync_dirty(Test)),
+ Clear(), Setup(),
+ ?match(ok, mnesia:activity(transaction,Test,mnesia)),
+ ?verify_mnesia(Nodes, []).
+
+snmp_update_counter(doc) ->
+ ["Verify that counters may be updated for tables with SNMP property"];
+snmp_update_counter(suite) -> [];
+snmp_update_counter(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = snmp_update_counter,
+ Def = [{attributes, [key, value]},
+ {snmp, [{key, integer}]},
+ {ram_copies, [Node1]}
+ ],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ Oid = {Tab, 1},
+ ?match([], mnesia:dirty_read(Oid)),
+ ?match(ok, mnesia:dirty_write({Tab, 1, 1})),
+ ?match([{Tab, _Key, 1}], mnesia:dirty_read(Oid)),
+ ?match(3, mnesia:dirty_update_counter(Oid, 2)),
+ ?match([{Tab, _Key, 3}], mnesia:dirty_read(Oid)),
+ ?verify_mnesia(Nodes, []).
+
+snmp_order(doc) ->
+ ["Verify that sort order is correct in transactions and dirty variants"];
+snmp_order(suite) -> [];
+snmp_order(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = snmp_order,
+ Def = [{attributes, [key, value]},
+ {snmp, [{key, {integer, integer, integer}}]},
+ {ram_copies, [Node1]}
+ ],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ Oid = {Tab, 1},
+ ?match([], mnesia:dirty_read(Oid)),
+ ?match({'EXIT', {aborted, _}}, mnesia:dirty_write({Tab, 1, 1})),
+ [mnesia:dirty_write({Tab, {A,B,2}, default}) ||
+ A <- lists:seq(1, 9, 2),
+ B <- lists:seq(2, 8, 2)],
+
+ Test1 = fun() ->
+ Keys0 = get_keys(Tab, []),
+ ?match(Keys0, lists:sort(Keys0)),
+ ?match([[1,2,2]|_], Keys0),
+ Keys1 = get_keys(Tab, [5]),
+ ?match(Keys1, lists:sort(Keys1)),
+ ?match([[5,2,2]|_], Keys1),
+ Keys2 = get_keys(Tab, [7, 4]),
+ ?match(Keys2, lists:sort(Keys2)),
+ ?match([[7,4,2]|_], Keys2),
+ ok
+ end,
+ ?match(ok, Test1()),
+ ?match({atomic, ok},mnesia:transaction(Test1)),
+ ?match(ok,mnesia:sync_dirty(Test1)),
+
+ Test2 = fun() ->
+ mnesia:write(Tab, {Tab,{0,0,2},updated}, write),
+ mnesia:write(Tab, {Tab,{5,3,2},updated}, write),
+ mnesia:write(Tab, {Tab,{10,10,2},updated}, write),
+ Keys0 = get_keys(Tab, []),
+ ?match([[0,0,2],[1,2,2]|_], Keys0),
+ ?match(Keys0, lists:sort(Keys0)),
+
+ Keys1 = get_keys(Tab, [5]),
+ ?match([[5,2,2],[5,3,2]|_], Keys1),
+ ?match(Keys1, lists:sort(Keys1)),
+
+ Keys2 = get_keys(Tab, [7,4]),
+ ?match([[7,4,2]|_], Keys2),
+ ?match(Keys2, lists:sort(Keys2)),
+ ?match([10,10,2], lists:last(Keys0)),
+ ?match([10,10,2], lists:last(Keys1)),
+ ?match([10,10,2], lists:last(Keys2)),
+
+ ?match([[10,10,2]], get_keys(Tab, [10])),
+ ok
+ end,
+
+ ?match({atomic, ok},mnesia:transaction(Test2)),
+
+ ?verify_mnesia(Nodes, []).
+
+get_keys(Tab, Key) ->
+ case mnesia:snmp_get_next_index(Tab, Key) of
+ endOfTable -> [];
+ {ok, Next} ->
+ [Next|get_keys(Tab, Next)]
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(tab, {i, e1, e2}). % Simple test table
+
+
+subscribe_extended(doc) ->
+ ["Test the extended set of events, test with and without checkpoints. "];
+subscribe_extended(suite) ->
+ [];
+subscribe_extended(Config) when is_list(Config) ->
+ [N1, N2]=Nodes=?acquire_nodes(2, Config),
+ Tab1 = etab,
+ Storage = mnesia_test_lib:storage_type(ram_copies, Config),
+ Def1 = [{Storage, [N1, N2]}, {attributes, record_info(fields, tab)}],
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+
+ Tab2 = bag,
+ Def2 = [{Storage, [N1, N2]},
+ {type, bag},
+ {record_name, Tab1},
+ {attributes, record_info(fields, tab)}],
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+
+ ?match({ok, N1}, mnesia:subscribe({table, Tab1, detailed})),
+ ?match({ok, N1}, mnesia:subscribe({table, Tab2, detailed})),
+
+ ?match({error, {already_exists, _}}, mnesia:subscribe({table, Tab1, simple})),
+ ?match({error, {badarg, {table, Tab1, bad}}}, mnesia:subscribe({table, Tab1, bad})),
+
+ ?match({ok, N1}, mnesia:subscribe(activity)),
+ test_ext_sub(Tab1, Tab2),
+
+ ?match({ok, N1}, mnesia:unsubscribe(activity)),
+ ?match({ok, N1}, mnesia:subscribe({table, Tab1, detailed})),
+ ?match({atomic, ok}, mnesia:clear_table(Tab1)),
+ ?match({mnesia_table_event, {delete, schema, {schema, Tab1}, [{schema, Tab1, _}],_}}, recv_event()),
+ ?match({mnesia_table_event, {write, schema, {schema, Tab1, _}, [], _}}, recv_event()),
+
+ ?match({atomic, ok}, mnesia_schema:clear_table(Tab2)),
+ ?match({mnesia_table_event, {delete, schema, {schema, Tab2}, [{schema, Tab2, _}],_}},
+ recv_event()),
+ ?match({mnesia_table_event, {write, schema, {schema, Tab2, _}, [], _}}, recv_event()),
+
+ ?match({ok, N1}, mnesia:unsubscribe({table, Tab2, detailed})),
+ {ok, _, _} = mnesia:activate_checkpoint([{name, testing},
+ {ram_overrides_dump, true},
+ {max, [Tab1, Tab2]}]),
+ ?match({ok, N1}, mnesia:subscribe({table, Tab2, detailed})),
+ ?match({ok, N1}, mnesia:subscribe(activity)),
+ test_ext_sub(Tab1, Tab2),
+
+ ?verify_mnesia(Nodes, []).
+
+test_ext_sub(Tab1, Tab2) ->
+ %% The basics
+ Rec1 = {Tab1, 1, 0, 0},
+ Rec2 = {Tab1, 1, 1, 0},
+ Rec3 = {Tab1, 2, 1, 0},
+ Rec4 = {Tab1, 2, 2, 0},
+
+ Write = fun(Tab, Rec) ->
+ mnesia:transaction(fun() -> mnesia:write(Tab, Rec, write)
+ end)
+ end,
+ Delete = fun(Tab, Rec) ->
+ mnesia:transaction(fun() -> mnesia:delete(Tab, Rec, write)
+ end)
+ end,
+ DelObj = fun(Tab, Rec) ->
+ mnesia:transaction(fun() -> mnesia:delete_object(Tab, Rec, write)
+ end)
+ end,
+
+ S = self(),
+ D = {dirty, self()},
+ %% SET
+ ?match(ok, mnesia:dirty_write(Rec1)),
+ ?match({mnesia_table_event, {write, Tab1, Rec1, [], D}}, recv_event()),
+ ?match(ok, mnesia:dirty_write(Rec3)),
+ ?match({mnesia_table_event, {write, Tab1, Rec3, [], D}}, recv_event()),
+ ?match(ok, mnesia:dirty_write(Rec1)),
+ ?match({mnesia_table_event, {write, Tab1, Rec1, [Rec1], D}}, recv_event()),
+ ?match({atomic, ok}, Write(Tab1, Rec2)),
+ ?match({mnesia_table_event, {write, Tab1, Rec2, [Rec1], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match(ok, mnesia:dirty_delete({Tab1, 2})),
+ ?match({mnesia_table_event, {delete, Tab1, {Tab1, 2}, [Rec3], D}}, recv_event()),
+ ?match({atomic, ok}, DelObj(Tab1, Rec2)),
+ ?match({mnesia_table_event, {delete, Tab1, Rec2, [Rec2], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+
+ ?match({atomic, ok}, Delete(Tab1, 1)),
+ ?match({mnesia_table_event, {delete, Tab1, {Tab1, 1}, [], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+
+ ?match({ok, _N1}, mnesia:unsubscribe({table, Tab1, detailed})),
+
+ %% BAG
+
+ ?match({atomic, ok}, Write(Tab2, Rec1)),
+ ?match({mnesia_table_event, {write, Tab2, Rec1, [], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, Write(Tab2, Rec4)),
+ ?match({mnesia_table_event, {write, Tab2, Rec4, [], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, Write(Tab2, Rec3)),
+ ?match({mnesia_table_event, {write, Tab2, Rec3, [Rec4], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, Write(Tab2, Rec2)),
+ ?match({mnesia_table_event, {write, Tab2, Rec2, [Rec1], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, Write(Tab2, Rec1)),
+ ?match({mnesia_table_event, {write, Tab2, Rec1, [Rec1, Rec2], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, DelObj(Tab2, Rec2)),
+ ?match({mnesia_table_event, {delete, Tab2, Rec2, [Rec2], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, Delete(Tab2, 1)),
+ ?match({mnesia_table_event, {delete, Tab2, {Tab2, 1}, [Rec1], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ?match({atomic, ok}, Delete(Tab2, 2)),
+ ?match({mnesia_table_event, {delete, Tab2, {Tab2, 2}, [Rec4, Rec3], {tid,_,S}}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()),
+ ok.
+
+
+subscribe_standard(doc) ->
+ ["Tests system events and the orignal table events"];
+subscribe_standard(suite) -> [];
+subscribe_standard(Config) when is_list(Config)->
+ [N1, N2]=?acquire_nodes(2, Config),
+ Tab = tab,
+
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ Def = [{Storage, [N1, N2]}, {attributes, record_info(fields, tab)}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+
+ %% Check system events
+ ?match({ok, N1}, mnesia:subscribe(system)),
+ ?match({ok, N1}, mnesia:subscribe(activity)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match({mnesia_system_event, {mnesia_down, N2}}, recv_event()),
+ ?match(timeout, recv_event()),
+
+ ?match([], mnesia_test_lib:start_mnesia([N2], [Tab])),
+ ?match({mnesia_activity_event, _}, recv_event()),
+ ?match({mnesia_system_event,{mnesia_up, N2}}, recv_event()),
+
+ ?match(true, lists:member(self(), mnesia:system_info(subscribers))),
+ ?match([], mnesia_test_lib:kill_mnesia([N1])),
+ timer:sleep(500),
+ mnesia_test_lib:flush(),
+ ?match([], mnesia_test_lib:start_mnesia([N1], [Tab])),
+ ?match(timeout, recv_event()),
+
+ ?match({ok, N1}, mnesia:subscribe(system)),
+ ?match({error, {already_exists, system}}, mnesia:subscribe(system)),
+ ?match(stopped, mnesia:stop()),
+ ?match({mnesia_system_event, {mnesia_down, N1}}, recv_event()),
+ ?match({error, {node_not_running, N1}}, mnesia:subscribe(system)),
+ ?match([], mnesia_test_lib:start_mnesia([N1, N2], [Tab])),
+
+ %% Check table events
+ ?match({ok, N1}, mnesia:subscribe(activity)),
+ Old_Level = mnesia:set_debug_level(trace),
+ ?match({ok, N1}, mnesia:subscribe({table,Tab})),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(#tab{i=155}) end)),
+ Self = self(),
+ ?match({mnesia_table_event, {write, _, _}}, recv_event()),
+ ?match({mnesia_activity_event, {complete, {tid, _, Self}}}, recv_event()),
+
+ ?match({ok, N1}, mnesia:unsubscribe({table,Tab})),
+ ?match({ok, N1}, mnesia:unsubscribe(activity)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(#tab{i=255}) end)),
+
+ ?match(timeout, recv_event()),
+ mnesia:set_debug_level(Old_Level),
+
+ %% Check deletion of replica
+
+ ?match({ok, N1}, mnesia:subscribe({table,Tab})),
+ ?match({ok, N1}, mnesia:subscribe(activity)),
+ ?match(ok, mnesia:dirty_write(#tab{i=355})),
+ ?match({mnesia_table_event, {write, _, _}}, recv_event()),
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, N1)),
+ ?match({mnesia_activity_event, _}, recv_event()),
+ ?match(ok, mnesia:dirty_write(#tab{i=455})),
+ ?match(timeout, recv_event()),
+
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, N2, N1)),
+ ?match({mnesia_activity_event, _}, recv_event()),
+ ?match({ok, N1}, mnesia:subscribe({table,Tab})),
+ ?match(ok, mnesia:dirty_write(#tab{i=555})),
+ ?match({mnesia_table_event, {write, _, _}}, recv_event()),
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, N1, N2)),
+ ?match({mnesia_activity_event, _}, recv_event()),
+ ?match(ok, mnesia:dirty_write(#tab{i=655})),
+ ?match(timeout, recv_event()),
+
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, N1, ram_copies)),
+ ?match({mnesia_activity_event, _}, recv_event()),
+ ?match({ok, N1}, mnesia:subscribe({table,Tab})),
+ ?match({error, {already_exists, {table,Tab, simple}}},
+ mnesia:subscribe({table,Tab})),
+ ?match(ok, mnesia:dirty_write(#tab{i=755})),
+ ?match({mnesia_table_event, {write, _, _}}, recv_event()),
+
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+ ?match({mnesia_activity_event, _}, recv_event()),
+ ?match(timeout, recv_event()),
+
+ mnesia_test_lib:kill_mnesia([N1]),
+
+ ?verify_mnesia([N2], [N1]).
+
+recv_event() ->
+ receive
+ Event -> Event
+ after 1000 ->
+ timeout
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+foldl(suite) ->
+ [];
+foldl(doc) ->
+ [""];
+foldl(Config) when is_list(Config) ->
+ Nodes = [_N1, N2] = ?acquire_nodes(2, Config),
+ Tab1 = fold_local,
+ Tab2 = fold_remote,
+ Tab3 = fold_ordered,
+ ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}, {type, bag}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes},
+ {type, ordered_set}])),
+
+ Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 10)],
+ Tab2Els = ?sort([{Tab2, 1, 2} | [{Tab2, N, N} || N <- lists:seq(1, 10)]]),
+ Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 10)],
+
+ [mnesia:sync_transaction(fun() -> mnesia:write(E) end) || E <- Tab1Els],
+ [mnesia:sync_transaction(fun() -> mnesia:write(E) end) || E <- Tab2Els],
+ [mnesia:sync_transaction(fun() -> mnesia:write(E) end) || E <- Tab3Els],
+
+ Fold = fun(Tab) ->
+ lists:reverse(mnesia:foldl(fun(E, A) -> [E | A] end, [], Tab))
+ end,
+ Fold2 = fun(Tab, Lock) ->
+ lists:reverse(mnesia:foldl(fun(E, A) -> [E | A] end, [], Tab, Lock))
+ end,
+ Exit = fun(Tab) ->
+ lists:reverse(mnesia:foldl(fun(_E, _A) -> exit(testing) end, [], Tab))
+ end,
+ %% Errors
+ ?match({aborted, _}, mnesia:transaction(Fold, [error])),
+ ?match({aborted, _}, mnesia:transaction(fun(Tab) -> mnesia:foldl(badfun,[],Tab) end,
+ [Tab1])),
+ ?match({aborted, testing}, mnesia:transaction(Exit, [Tab1])),
+ ?match({aborted, _}, mnesia:transaction(Fold2, [Tab1, read_lock])),
+
+ %% Success
+ ?match({atomic, Tab1Els}, sort_res(mnesia:transaction(Fold, [Tab1]))),
+ ?match({atomic, Tab2Els}, sort_res(mnesia:transaction(Fold, [Tab2]))),
+ ?match({atomic, Tab3Els}, mnesia:transaction(Fold, [Tab3])),
+
+ ?match({atomic, Tab1Els}, sort_res(mnesia:transaction(Fold2, [Tab1, read]))),
+ ?match({atomic, Tab1Els}, sort_res(mnesia:transaction(Fold2, [Tab1, write]))),
+
+ ?match(Tab1Els, sort_res(mnesia:sync_dirty(Fold, [Tab1]))),
+ ?match(Tab2Els, sort_res(mnesia:async_dirty(Fold, [Tab2]))),
+
+ ?verify_mnesia(Nodes, []).
+
+sort_res({atomic, List}) ->
+ {atomic, ?sort(List)};
+sort_res(Else) when is_list(Else) ->
+ ?sort(Else);
+sort_res(Else) ->
+ Else.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+info(suite) -> [];
+info(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ ?match(ok, mnesia:info()),
+ ?verify_mnesia(Nodes, []).
+
+schema_0(suite) -> [];
+schema_0(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ ?match(ok, mnesia:schema()),
+ ?verify_mnesia(Nodes, []).
+
+schema_1(suite) -> [];
+schema_1(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ Tab = schema_1,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [])),
+ ?match(ok, mnesia:schema(Tab)),
+ ?verify_mnesia(Nodes, []).
+
+view_0(suite) -> [];
+view_0(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ ?match(ok, mnesia_lib:view()),
+ ?verify_mnesia(Nodes, []).
+
+view_1(suite) -> [];
+view_1(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ BinCore = mnesia_lib:mkcore({crashinfo, "Just testing..."}),
+ CoreFile = lists:concat(["MnesiaCore.", node(), ".view_1.", ?MODULE]),
+ ?match(ok, file:write_file(CoreFile, BinCore)),
+ ?match(ok, mnesia_lib:view(CoreFile)),
+ ?match(ok, file:delete(CoreFile)),
+
+ ?match(stopped, mnesia:stop()),
+ Dir = mnesia:system_info(directory),
+ ?match(eof, mnesia_lib:view(filename:join(Dir, "LATEST.LOG"))),
+ ?match(ok, mnesia_lib:view(filename:join(Dir, "schema.DAT"))),
+ ?verify_mnesia([], Nodes).
+
+view_2(suite) -> [];
+view_2(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ BinCore = mnesia_lib:mkcore({crashinfo, "More testing..."}),
+ File = lists:concat([?MODULE, "view_2.", node()]),
+ ?match(ok, file:write_file(File, BinCore)),
+ ?match(ok, mnesia_lib:view(File, core)),
+ ?match(ok, file:delete(File)),
+
+ ?match(stopped, mnesia:stop()),
+ Dir = mnesia:system_info(directory),
+ ?match(ok, file:rename(filename:join(Dir, "LATEST.LOG"), File)),
+ ?match(eof, mnesia_lib:view(File, log)),
+ ?match(ok, file:delete(File)),
+
+ ?match(ok, file:rename(filename:join(Dir, "schema.DAT"), File)),
+ ?match(ok, mnesia_lib:view(File, dat)),
+ ?match(ok, file:delete(File)),
+ ?verify_mnesia([], Nodes).
+
+lkill(suite) -> [];
+lkill(Config) when is_list(Config) ->
+ [Node1, Node2] = ?acquire_nodes(2, Config),
+
+ ?match(yes, rpc:call(Node1, mnesia, system_info, [is_running])),
+ ?match(yes, rpc:call(Node2, mnesia, system_info, [is_running])),
+ ?match(ok, rpc:call(Node2, mnesia, lkill, [])),
+ ?match(yes, rpc:call(Node1, mnesia, system_info, [is_running])),
+ ?match(no, rpc:call(Node2, mnesia, system_info, [is_running])),
+ ?verify_mnesia([Node1], [Node2]).
+
+kill(suite) -> [];
+kill(Config) when is_list(Config) ->
+ [Node1, Node2] = ?acquire_nodes(2, Config),
+
+ ?match(yes, rpc:call(Node1, mnesia, system_info, [is_running])),
+ ?match(yes, rpc:call(Node2, mnesia, system_info, [is_running])),
+ ?match({_, []}, rpc:call(Node2, mnesia, kill, [])),
+ ?match(no, rpc:call(Node1, mnesia, system_info, [is_running])),
+ ?match(no, rpc:call(Node2, mnesia, system_info, [is_running])),
+ ?verify_mnesia([], [Node1, Node2]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+record_name_dirty_access_ram(suite) ->
+ [];
+record_name_dirty_access_ram(Config) when is_list(Config) ->
+ record_name_dirty_access(ram_copies, Config).
+
+record_name_dirty_access_disc(suite) ->
+ [];
+record_name_dirty_access_disc(Config) when is_list(Config) ->
+ record_name_dirty_access(disc_copies, Config).
+
+record_name_dirty_access_disc_only(suite) ->
+ [];
+record_name_dirty_access_disc_only(Config) when is_list(Config) ->
+ record_name_dirty_access(disc_only_copies, Config).
+
+record_name_dirty_access(Storage, Config) ->
+ [Node1, _Node2] = Nodes = ?acquire_nodes(2, Config),
+
+ List = lists:concat([record_name_dirty_access_, Storage]),
+ Tab = list_to_atom(List),
+ RecName = some_record,
+ Attr = val,
+ TabDef = [{type, bag},
+ {record_name, RecName},
+ {index, [Attr]},
+ {Storage, Nodes}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, TabDef)),
+
+ ?match(RecName, mnesia:table_info(Tab, record_name)),
+
+ ?match(ok, mnesia:dirty_write(Tab, {RecName, 2, 20})),
+ ?match(ok, mnesia:dirty_write(Tab, {RecName, 2, 21})),
+ ?match(ok, mnesia:dirty_write(Tab, {RecName, 2, 22})),
+
+ %% Backup test
+ BupFile = List ++ ".BUP",
+ CpName = cpname,
+ CpArgs = [{name, CpName}, {min, [Tab]}, {ram_overrides_dump, true}],
+ ?match({ok, CpName, _}, mnesia:activate_checkpoint(CpArgs)),
+ ?match(ok, mnesia:backup_checkpoint(CpName, BupFile)),
+ ?match(ok, mnesia:deactivate_checkpoint(CpName)),
+
+ ?match(ok, mnesia:dirty_write(Tab, {RecName, 1, 10})),
+ ?match({ok, Node1}, mnesia:subscribe({table, Tab})),
+ ?match(ok, mnesia:dirty_write(Tab, {RecName, 3, 10})),
+
+ Twos =?sort( [{RecName, 2, 20}, {RecName, 2, 21}, {RecName, 2, 22}]),
+ ?match(Twos, ?sort(mnesia:dirty_read(Tab, 2))),
+
+ ?match(ok, mnesia:dirty_delete_object(Tab, {RecName, 2, 21})),
+
+ Tens = ?sort([{RecName, 1, 10}, {RecName, 3, 10}]),
+ TenPat = {RecName, '_', 10},
+ ?match(Tens, ?sort(mnesia:dirty_match_object(Tab, TenPat))),
+ ?match(Tens, ?sort(mnesia:dirty_select(Tab, [{TenPat, [], ['$_']}]))),
+
+ %% Subscription test
+ E = mnesia_table_event,
+ ?match_receive({E, {write, {Tab, 3, 10}, _}}),
+ ?match_receive({E, {delete_object, {Tab, 2, 21}, _}}),
+ ?match({ok, Node1}, mnesia:unsubscribe({table, Tab})),
+
+ ?match([], mnesia_test_lib:stop_mnesia([Node1])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [Tab])),
+
+ ?match(Tens, ?sort(mnesia:dirty_index_match_object(Tab, TenPat, Attr) )),
+ ?match(Tens, ?sort(mnesia:dirty_index_read(Tab, 10, Attr))),
+
+ ?match([1, 2, 3], ?sort(mnesia:dirty_all_keys(Tab))),
+
+ ?match({ok, Node1}, mnesia:subscribe({table, Tab})),
+ ?match(ok, mnesia:dirty_delete(Tab, 2)),
+ ?match([], mnesia:dirty_read(Tab, 2)),
+
+ ?match_receive({E, {delete, {Tab, 2}, _}}),
+ ?match([], mnesia_test_lib:flush()),
+ ?match({ok, Node1}, mnesia:unsubscribe({table, Tab})),
+
+ %% Restore test
+ ?match({atomic, [Tab]}, mnesia:restore(BupFile, [{recreate_tables, [Tab]}])),
+ ?match(RecName, mnesia:table_info(Tab, record_name)),
+
+ ?match(Twos, ?sort(mnesia:dirty_match_object(Tab, mnesia:table_info(Tab, wild_pattern)))),
+ ?match(Twos, ?sort(mnesia:dirty_select(Tab,
+ [{mnesia:table_info(Tab, wild_pattern),
+ [],['$_']}]))),
+
+ %% Traverse backup test
+
+ Fun = fun(Rec, {Good, Bad}) ->
+ ?verbose("BUP: ~p~n", [Rec]),
+ case Rec of
+ {T, K, V} when T == Tab ->
+ Good2 = Good ++ [{RecName, K, V}],
+ {[Rec], {?sort(Good2), Bad}};
+ {T, K} when T == Tab ->
+ Good2 = [G || G <- Good, element(2, G) /= K],
+ {[Rec], {?sort(Good2), Bad}};
+ _ when element(1, Rec) == schema ->
+ {[Rec], {Good, Bad}};
+ _ ->
+ Bad2 = Bad ++ [Rec],
+ {[Rec], {Good, ?sort(Bad2)}}
+ end
+ end,
+
+ ?match({ok, {Twos, []}}, mnesia:traverse_backup(BupFile, mnesia_backup,
+ dummy, read_only,
+ Fun, {[], []})),
+ ?match(ok, file:delete(BupFile)),
+
+ %% Update counter test
+
+ CounterTab = list_to_atom(lists:concat([Tab, "_counter"])),
+ CounterTabDef = [{record_name, some_counter}],
+ C = my_counter,
+ ?match({atomic, ok}, mnesia:create_table(CounterTab, CounterTabDef)),
+ ?match(some_counter, mnesia:table_info(CounterTab, record_name)),
+ ?match(0, mnesia:dirty_update_counter(CounterTab, gurka, -10)),
+ ?match(10, mnesia:dirty_update_counter(CounterTab, C, 10)),
+ ?match(11, mnesia:dirty_update_counter(CounterTab, C, 1)),
+ ?match(4711, mnesia:dirty_update_counter(CounterTab, C, 4700)),
+ ?match([{some_counter, C, 4711}], mnesia:dirty_read(CounterTab, C)),
+ ?match(0, mnesia:dirty_update_counter(CounterTab, C, -4747)),
+
+ %% Registry tests
+
+ RegTab = list_to_atom(lists:concat([Tab, "_registry"])),
+ RegTabDef = [{record_name, some_reg}],
+ ?match(ok, mnesia_registry:create_table(RegTab, RegTabDef)),
+ ?match(some_reg, mnesia:table_info(RegTab, record_name)),
+ {success, RegRecs} =
+ ?match([_ | _], mnesia_registry_test:dump_registry(node(), RegTab)),
+
+ R = ?sort(RegRecs),
+ ?match(R, ?sort(mnesia_registry_test:restore_registry(node(), RegTab))),
+
+ ?verify_mnesia(Nodes, []).
+
+sorted_ets(suite) ->
+ [];
+sorted_ets(Config) when is_list(Config) ->
+ [N1, N2, N3] = All = ?acquire_nodes(3, Config),
+
+ Tab = sorted_tab,
+ Def = case mnesia_test_lib:diskless(Config) of
+ true -> [{name, Tab}, {type, ordered_set}, {ram_copies, All}];
+ false -> [{name, Tab}, {type, ordered_set},
+ {ram_copies, [N1]},
+ {disc_copies,[N2, N3]}]
+ end,
+
+ ?match({atomic, ok}, mnesia:create_table(Def)),
+ ?match({aborted, _}, mnesia:create_table(fel, [{disc_only_copies, N1}])),
+
+ ?match([ok | _],
+ [mnesia:dirty_write({Tab, {dirty, N}, N}) || N <- lists:seq(1, 10)]),
+ ?match({atomic, _},
+ mnesia:sync_transaction(fun() ->
+ [mnesia:write({Tab, {trans, N}, N}) ||
+ N <- lists:seq(1, 10)]
+ end)),
+
+ List = mnesia:dirty_match_object({Tab, '_', '_'}),
+ ?match(List, ?sort(List)),
+ ?match(List, rpc:call(N2, mnesia, dirty_match_object, [{Tab, '_', '_'}])),
+ ?match(List, rpc:call(N3, mnesia, dirty_match_object, [{Tab, '_', '_'}])),
+
+ mnesia_test_lib:stop_mnesia(All),
+ mnesia_test_lib:start_mnesia(All, [sorted_tab]),
+
+ List = mnesia:dirty_match_object({Tab, '_', '_'}),
+ ?match(List, ?sort(List)),
+ ?match(List, rpc:call(N2, mnesia, dirty_match_object, [{Tab, '_', '_'}])),
+ ?match(List, rpc:call(N3, mnesia, dirty_match_object, [{Tab, '_', '_'}])),
+
+ ?match(List, rpc:call(N3, mnesia, dirty_select, [Tab, [{{Tab, '_', '_'},[],['$_']}]])),
+
+ TransMatch = fun() ->
+ mnesia:write({Tab, {trans, 0}, 0}),
+ mnesia:write({Tab, {trans, 11}, 11}),
+ mnesia:match_object({Tab, '_', '_'})
+ end,
+ TransSelect = fun() ->
+ mnesia:write({Tab, {trans, 0}, 0}),
+ mnesia:write({Tab, {trans, 11}, 11}),
+ mnesia:select(Tab, [{{Tab, '_', '_'},[],['$_']}])
+ end,
+
+ TList = mnesia:transaction(TransMatch),
+ STList = ?sort(TList),
+ ?match(STList, TList),
+ ?match(STList, rpc:call(N2, mnesia, transaction, [TransMatch])),
+ ?match(STList, rpc:call(N3, mnesia, transaction, [TransMatch])),
+
+ TSel = mnesia:transaction(TransSelect),
+ ?match(STList, TSel),
+ ?match(STList, rpc:call(N2, mnesia, transaction, [TransSelect])),
+ ?match(STList, rpc:call(N3, mnesia, transaction, [TransSelect])),
+
+ ?match({atomic, ok}, mnesia:create_table(rec, [{type, ordered_set}])),
+ [ok = mnesia:dirty_write(R) || R <- [{rec,1,1}, {rec,2,1}]],
+ ?match({atomic, ok}, mnesia:add_table_index(rec, 3)),
+ TestIt = fun() ->
+ ok = mnesia:write({rec,1,1}),
+ mnesia:index_read(rec, 1, 3)
+ end,
+ ?match({atomic, [{rec,1,1}, {rec,2,1}]}, mnesia:transaction(TestIt)).
+
+
diff --git a/lib/mnesia/test/mnesia_examples_test.erl b/lib/mnesia/test/mnesia_examples_test.erl
new file mode 100644
index 0000000000..373d47a05a
--- /dev/null
+++ b/lib/mnesia/test/mnesia_examples_test.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_examples_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-define(init(N, Config),
+ mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema],
+ N, Config, ?FILE, ?LINE)).
+
+opt_net_load(ExampleMod) ->
+ opt_net_load([node() | nodes()], ExampleMod, ok).
+
+opt_net_load([Node | Nodes], ExampleMod, Res) ->
+ case rpc:call(Node, ?MODULE, opt_load, [ExampleMod]) of
+ {module, ExampleMod} ->
+ opt_net_load(Nodes, ExampleMod, Res);
+ {error, Reason} ->
+ Error = {opt_net_load, ExampleMod, Node, Reason},
+ opt_net_load(Nodes, ExampleMod, {error, Error});
+ {badrpc, Reason} ->
+ Error = {opt_net_load, ExampleMod, Node, Reason},
+ opt_net_load(Nodes, ExampleMod, {error, Error})
+ end;
+opt_net_load([], _ExampleMod, Res) ->
+ Res.
+
+opt_load(Mod) ->
+ case code:is_loaded(Mod) of
+ {file, _} ->
+ {module, Mod};
+ false ->
+ Abs = filename:join([code:lib_dir(mnesia), examples, Mod]),
+ code:load_abs(Abs)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [bup, company, meter, {group, tpcb}].
+
+groups() ->
+ [{tpcb, [],
+ [replica_test, sticky_replica_test, dist_test,
+ conflict_test, frag_test, frag2_test, remote_test,
+ remote_frag2_test]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+bup(doc) -> ["Run the backup examples in bup.erl"];
+bup(suite) -> [];
+bup(Config) when is_list(Config) ->
+ Nodes = ?init(3, Config),
+ opt_net_load(bup),
+ ?match(ok, bup:test(Nodes)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+company(doc) ->
+ ["Run the company examples in company.erl and company_o.erl"].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+replica_test(suite) -> [];
+replica_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(replica_test, ram_copies))).
+
+sticky_replica_test(suite) -> [];
+sticky_replica_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(sticky_replica_test, ram_copies))).
+
+dist_test(suite) -> [];
+dist_test(Config) when is_list(Config) ->
+ ?init(3, [{tc_timeout, timer:minutes(10)} | Config]),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(dist_test, ram_copies))).
+
+conflict_test(suite) -> [];
+conflict_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(conflict_test, ram_copies))).
+
+frag_test(suite) -> [];
+frag_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(frag_test, ram_copies))).
+
+frag2_test(suite) -> [];
+frag2_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(frag2_test, ram_copies))).
+
+remote_test(suite) -> [];
+remote_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(remote_test, ram_copies))).
+
+remote_frag2_test(suite) -> [];
+remote_frag2_test(Config) when is_list(Config) ->
+ ?init(3, Config),
+ opt_net_load(mnesia_tpcb),
+ ?match({ok, _}, mnesia_tpcb:start(mnesia_tpcb:config(remote_frag2_test, ram_copies))).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+meter(doc) ->
+ ["Run the meter example in mnesia_meter.erl"];
+meter(suite) ->
+ [];
+meter(Config) when is_list(Config) ->
+ [N | _] = ?init(3, Config),
+ opt_net_load(mnesia_meter),
+ ?match(ok, mnesia_meter:go(ram_copies, [N])).
+
+
diff --git a/lib/mnesia/test/mnesia_frag_test.erl b/lib/mnesia/test/mnesia_frag_test.erl
new file mode 100644
index 0000000000..d3f6762af7
--- /dev/null
+++ b/lib/mnesia/test/mnesia_frag_test.erl
@@ -0,0 +1,859 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_frag_test).
+-author('[email protected]').
+-include("mnesia_test_lib.hrl").
+
+-compile([export_all]).
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-define(match_dist(ExpectedRes, Expr),
+ case ?match(ExpectedRes, Expr) of
+
+ mnesia_test_lib:error(Format, Args,?FILE,?LINE)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all() ->
+ [{group, light}, {group, medium}].
+
+groups() ->
+ [{light, [], [{group, nice}, {group, evil}]},
+ {medium, [], [consistency]},
+ {nice, [],
+ [nice_single, nice_multi, nice_access, iter_access]},
+ {evil, [],
+ [evil_create, evil_delete, evil_change, evil_combine,
+ evil_loop, evil_delete_db_node]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+nice_single(suite) -> [];
+nice_single(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+
+ %% Create a table with 2 fragments and 12 records
+ Tab = nice_frag,
+ Props = [{n_fragments, 2}, {node_pool, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{frag_properties, Props}])),
+ Records = [{Tab, N, -N} || N <- lists:seq(1, 12)],
+ [frag_write(Tab, R) || R <- Records],
+ ?match([{Node1, 2}], frag_dist(Tab)),
+ ?match([8, 4], frag_rec_dist(Tab)),
+
+ %% Adding a new node to pool should not affect distribution
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_node, Node2})),
+ Dist = frag_dist(Tab),
+ ?match([{Node2, 0}, {Node1, 2}], Dist),
+ ?match([8, 4], frag_rec_dist(Tab)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})),
+ Dist2 = frag_dist(Tab),
+ ?match([{Node2, 1}, {Node1, 2}], Dist2),
+ ?match([3, 4, 5], frag_rec_dist(Tab)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist2})),
+ Dist3 = frag_dist(Tab),
+ ?match([{Node1, 2}, {Node2, 2}], Dist3),
+ ?match([3, 2, 5, 2], frag_rec_dist(Tab)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist3})),
+ Dist4 = frag_dist(Tab),
+ ?match([{Node2, 2}, {Node1, 3}], Dist4),
+ ?match([_, _, _, _, _], frag_rec_dist(Tab)),
+
+ %% Dropping a node in pool should not affect distribution
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {del_node, Node1})),
+ ?match([{Node2, 2}, {Node1, 3}], frag_dist(Tab)),
+ ?match([_, _, _, _, _], frag_rec_dist(Tab)),
+
+ %% Dropping a fragment
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ Dist5 = frag_dist(Tab),
+ ?match([{Node2, 2}, {Node1, 2}], Dist5),
+ ?match([3, 2, 5, 2], frag_rec_dist(Tab)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist5})),
+ Dist6 = frag_dist(Tab),
+ ?match([{Node2, 3}, {Node1, 2}], Dist6),
+ ?match([_, _, _, _, _], frag_rec_dist(Tab)),
+
+ %% Dropping all fragments but one
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([3, 2, 5, 2], frag_rec_dist(Tab)),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([3, 4, 5], frag_rec_dist(Tab)),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([8, 4], frag_rec_dist(Tab)),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([{Node2, 0}, {Node1, 1}], frag_dist(Tab)),
+ ?match([12], frag_rec_dist(Tab)),
+
+ %% Defragmenting the table clears frag_properties
+ ?match(Len when Len > 0,
+ length(mnesia:table_info(Tab, frag_properties))),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, deactivate)),
+ ?match(0, length(mnesia:table_info(Tab, frag_properties))),
+
+ %% Making the table fragmented again
+ Props2 = [{n_fragments, 1}],
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, Props2})),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, frag_dist(Tab)})),
+ Dist7 = frag_dist(Tab),
+ ?match([{Node1, 1}, {Node2, 1}], Dist7),
+ ?match([8, 4], frag_rec_dist(Tab)),
+
+ %% Deleting the fragmented table
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+ ?match(false, lists:member(Tab, mnesia:system_info(tables))),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+nice_multi(doc) ->
+ ["Extending the nice case with one more node, ",
+ "one more replica and a foreign key"];
+nice_multi(suite) -> [];
+nice_multi(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+
+ %% Create a table with 2 fragments and 8 records
+ Tab = frag_master,
+ Name = frag_rec,
+ Type = case mnesia_test_lib:diskless(Config) of
+ true -> n_ram_copies;
+ false -> n_disc_copies
+ end,
+ Props = [{n_fragments, 2},
+ {Type, 2},
+ {node_pool, [Node2, Node1]}],
+ Def = [{frag_properties, Props},
+ {attributes, [id, data]},
+ {record_name, Name}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ [frag_write(Tab, {Name, Id, -Id}) || Id <- lists:seq(1, 8)],
+ ?match([6, 2], frag_rec_dist(Tab)),
+ ?match([{Node2, 2}, {Node1, 2}], frag_dist(Tab)),
+
+ %% And connect another table to it, via a foreign key
+ TabF = frag_slave,
+ PropsF = [{foreign_key, {Tab, foreign_id}}],
+ DefF = [{frag_properties, PropsF},
+ {attributes, [id, foreign_id]}],
+
+ ?match({atomic, ok}, mnesia:create_table(TabF, DefF)),
+ [frag_write(TabF, {TabF, {Id}, Id}) || Id <- lists:seq(1, 16)],
+ ?match([10, 6], frag_rec_dist(TabF)),
+ ?match([{Node2, 2}, {Node1, 2}], frag_dist(TabF)),
+
+ %% Adding a new node to pool should not affect distribution
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_node, Node3})),
+ Dist = frag_dist(Tab),
+ ?match([{Node3, 0}, {Node2, 2}, {Node1, 2}], Dist),
+ ?match([6, 2], frag_rec_dist(Tab)),
+ DistF = frag_dist(TabF),
+ ?match([{Node3, 0}, {Node2, 2}, {Node1, 2}], DistF),
+ ?match([10, 6], frag_rec_dist(TabF)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})),
+ Dist2 = frag_dist(Tab),
+ ?match([{Node3, 1},{Node1, 2},{Node2,3}], Dist2),
+ ?match([_, _, _], frag_rec_dist(Tab)),
+ DistF2 = frag_dist(TabF),
+ ?match([{Node3, 1},{Node1, 2},{Node2,3}], DistF2),
+ ?match([_, _, _], frag_rec_dist(TabF)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist2})),
+ Dist3 = frag_dist(Tab),
+ ?match([{Node3, 2},{Node2,3},{Node1, 3}], Dist3),
+ ?match([3, 0, 3, 2], frag_rec_dist(Tab)),
+ DistF3 = frag_dist(TabF),
+ ?match([{Node3, 2},{Node2,3},{Node1, 3}], DistF3),
+ ?match([3, 3, 7, 3], frag_rec_dist(TabF)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist3})),
+ Dist4 = frag_dist(Tab),
+ ?match([{Node1, 3}, {Node3, 3},{Node2, 4}], Dist4),
+ ?match([_, _, _, _, _], frag_rec_dist(Tab)),
+ DistF4 = frag_dist(TabF),
+ ?match([{Node1, 3}, {Node3, 3},{Node2, 4}], DistF4),
+ ?match([_, _, _, _, _], frag_rec_dist(TabF)),
+
+ %% Dropping a node in pool should not affect distribution
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {del_node, Node1})),
+ ?match([{Node3, 3},{Node2, 4}, {Node1, 3}], frag_dist(Tab)),
+ ?match([_, _, _, _, _], frag_rec_dist(Tab)),
+ ?match([{Node3, 3},{Node2, 4}, {Node1, 3}], frag_dist(TabF)),
+ ?match([_, _, _, _, _], frag_rec_dist(TabF)),
+
+ %% Dropping a fragment
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ Dist5 = frag_dist(Tab),
+ ?match([{Node3, 2},{Node2,3},{Node1, 3}], Dist5),
+ ?match([3, 0, 3, 2], frag_rec_dist(Tab)),
+ DistF5 = frag_dist(Tab),
+ ?match([{Node3, 2},{Node2,3},{Node1, 3}], DistF5),
+ ?match([3, 3, 7, 3], frag_rec_dist(TabF)),
+
+ %% Add new fragment hopefully on the new node
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist5})),
+ Dist6 = frag_dist(Tab),
+ ?match([{Node3, 3},{Node2, 4},{Node1, 3}], Dist6),
+ ?match([_, _, _, _, _], frag_rec_dist(Tab)),
+ DistF6 = frag_dist(TabF),
+ ?match([{Node3, 3},{Node2, 4},{Node1, 3}], DistF6),
+ ?match([_, _, _, _, _], frag_rec_dist(TabF)),
+
+ %% Dropping all fragments but one
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([3, 0, 3, 2], frag_rec_dist(Tab)),
+ ?match([3, 3, 7, 3], frag_rec_dist(TabF)),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([_, _, _], frag_rec_dist(Tab)),
+ ?match([_, _, _], frag_rec_dist(TabF)),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([6, 2], frag_rec_dist(Tab)),
+ ?match([10, 6], frag_rec_dist(TabF)),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
+ ?match([{Node3, 0}, {Node2, 1}, {Node1, 1}], frag_dist(Tab)),
+ ?match([8], frag_rec_dist(Tab)),
+ ?match([{Node3, 0}, {Node2, 1}, {Node1, 1}], frag_dist(TabF)),
+ ?match([16], frag_rec_dist(TabF)),
+
+ %% Defragmenting the tables clears frag_properties
+ ?match(Len when Len > 0,
+ length(mnesia:table_info(TabF, frag_properties))),
+ ?match({atomic, ok}, mnesia:change_table_frag(TabF, deactivate)),
+ ?match(0, length(mnesia:table_info(TabF, frag_properties))),
+ ?match(Len when Len > 0,
+ length(mnesia:table_info(Tab, frag_properties))),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, deactivate)),
+ ?match(0, length(mnesia:table_info(Tab, frag_properties))),
+
+ %% Making the tables fragmented again
+ Props2 = [{n_fragments, 1}, {node_pool, [Node1, Node2]}],
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, Props2})),
+ ?match({atomic, ok}, mnesia:delete_table(TabF)),
+ ?match({atomic, ok}, mnesia:create_table(TabF, DefF)),
+ [frag_write(TabF, {TabF, {Id}, Id}) || Id <- lists:seq(1, 16)],
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, frag_dist(Tab)})),
+ ?match([{Node1, 2}, {Node2, 2}], frag_dist(Tab)),
+ ?match([6, 2], frag_rec_dist(Tab)),
+ ?match([{Node1, 2}, {Node2, 2}], frag_dist(TabF)),
+ ?match([10, 6], frag_rec_dist(TabF)),
+
+ %% Deleting the fragmented tables
+ ?match({atomic, ok}, mnesia:delete_table(TabF)),
+ ?match(false, lists:member(TabF, mnesia:system_info(tables))),
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+ ?match(false, lists:member(Tab, mnesia:system_info(tables))),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+nice_access(doc) ->
+ ["Cover entire callback interface"];
+nice_access(suite) -> [];
+nice_access(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, Config),
+
+ Tab = frag_access,
+ Pool = lists:sort(Nodes),
+ Props = [{n_fragments, 20},
+ {n_ram_copies, 2},
+ {node_pool, Pool}],
+ Def = [{frag_properties, Props},
+ {type, ordered_set},
+ {index, [val]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ [frag_write(Tab, {Tab, Id, Id}) || Id <- lists:seq(1, 400)],
+
+ %% And connect another table to it, via a foreign key
+ TabF = frag_access_slave,
+ PropsF = [{foreign_key, {Tab, val}}],
+ DefF = [{frag_properties, PropsF},
+ {index, [val]}],
+ ?match({atomic, ok}, mnesia:create_table(TabF, DefF)),
+ [frag_write(TabF, {TabF, Id, Id}) || Id <- lists:seq(1, 400)],
+
+ ?match(done, mnesia:activity(transaction, fun do_access/3, [Tab, Tab, Pool], mnesia_frag)),
+ ?match(done, mnesia:activity(transaction, fun do_access/3, [TabF, Tab, Pool], mnesia_frag)),
+
+ ?verify_mnesia(Nodes, []).
+
+do_access(Tab, Master, Pool) ->
+ ?match(20, mnesia:table_info(Tab, n_fragments)),
+ ?match(Pool, mnesia:table_info(Tab, node_pool)),
+ ?match(2, mnesia:table_info(Tab, n_ram_copies)),
+ ?match(0, mnesia:table_info(Tab, n_disc_copies)),
+ ?match(0, mnesia:table_info(Tab, n_disc_only_copies)),
+ ?match(20, length(mnesia:table_info(Tab, frag_names))),
+ ?match(20, length(mnesia:table_info(Tab, frag_size))),
+ ?match(20, length(mnesia:table_info(Tab, frag_memory))),
+ PoolSize = length(Pool),
+ ?match(PoolSize, length(mnesia:table_info(Tab, frag_dist))),
+ ?match(400, mnesia:table_info(Tab, size)),
+ ?match(I when is_integer(I), mnesia:table_info(Tab, memory)),
+ ?match(Tab, mnesia:table_info(Tab, base_table)),
+
+ Foreign =
+ if
+ Master == Tab ->
+ ?match(undefined, mnesia:table_info(Tab, foreign_key)),
+ ?match([_], mnesia:table_info(Tab, foreigners)),
+ ?match({'EXIT', {aborted, {combine_error, Tab, frag_properties, {foreign_key, undefined}}}},
+ mnesia:read({Tab, 5}, 5, read)),
+ fun({T, _K}) -> T end;
+ true ->
+ ?match({Master, 3}, mnesia:table_info(Tab, foreign_key)),
+ ?match([], mnesia:table_info(Tab, foreigners)),
+ fun({T, K}) -> {T, K} end
+ end,
+
+ Attr = val,
+ ?match(400, mnesia:table_info(Tab, size)),
+ Count = fun(_, N) -> N + 1 end,
+ ?match(400, mnesia:foldl(Count, 0, Tab)),
+ ?match(400, mnesia:foldr(Count, 0, Tab)),
+ ?match(ok, mnesia:write({Tab, [-1], 1})),
+ ?match(401, length(mnesia:match_object(Tab, {Tab, '_', '_'}, read))),
+ ?match(401, length(mnesia:select(Tab, [{{Tab, '_', '$1'}, [], ['$1']}], read))),
+
+ First = mnesia:select(Tab, [{{Tab, '_', '$1'}, [], ['$1']}], 10, read),
+ TestCont = fun('$end_of_table', Total, _This) ->
+ Total;
+ ({Res,Cont1}, Total, This) ->
+ Cont = mnesia:select(Cont1),
+ This(Cont, length(Res) + Total, This)
+ end,
+ ?match(401, TestCont(First, 0, TestCont)),
+
+ %% OTP
+ [_, Frag2|_] = frag_names(Tab),
+ Frag2key = mnesia:dirty_first(Frag2),
+ ?match({[Frag2key],_},mnesia:select(Tab,[{{Tab,Frag2key,'$1'},[],['$1']}],100,read)),
+
+ ?match([{Tab, [-1], 1}], mnesia:read(Foreign({Tab, 1}), [-1], read)),
+ ?match(401, mnesia:foldl(Count, 0, Tab)),
+ ?match(401, mnesia:foldr(Count, 0, Tab)),
+ ?match(ok, mnesia:delete(Foreign({Tab, 2}), 2, write)),
+ ?match([], mnesia:read(Foreign({Tab, 2}), 2, read)),
+ ?match([{Tab, 3, 3}], mnesia:read(Foreign({Tab, 3}), 3, read)),
+ ?match(400, mnesia:foldl(Count, 0, Tab)),
+ ?match(400, mnesia:foldr(Count, 0, Tab)),
+ ?match(ok, mnesia:delete_object({Tab, 3, 3})),
+ ?match([], mnesia:read(Foreign({Tab, 3}), 3, read)),
+ One = lists:sort([{Tab, 1, 1}, {Tab, [-1], 1}]),
+ Pat = {Tab, '$1', 1},
+ ?match(One, lists:sort(mnesia:match_object(Tab, Pat, read))),
+ ?match([1,[-1]], lists:sort(mnesia:select(Tab, [{Pat, [], ['$1']}], read))),
+ ?match([[[-1]]], lists:sort(mnesia:select(Tab, [{Pat, [{is_list, '$1'}], [['$1']]}], read))),
+ ?match([[1, 100]], lists:sort(mnesia:select(Tab, [{Pat, [{is_integer, '$1'}], [['$1',100]]}], read))),
+ ?match([1,[-1]], lists:sort(mnesia:select(Tab, [{Pat, [{is_list, '$1'}], ['$1']},{Pat, [{is_integer, '$1'}], ['$1']}], read))),
+ ?match(One, lists:sort(mnesia:index_match_object(Tab, Pat, Attr, read) )),
+ ?match(One, lists:sort(mnesia:index_read(Tab, 1, Attr))),
+ Keys = mnesia:all_keys(Tab),
+ ?match([-1], lists:max(Keys)), %% OTP-3779
+ ?match(399, length(Keys)),
+ ?match(399, mnesia:foldl(Count, 0, Tab)),
+ ?match(399, mnesia:foldr(Count, 0, Tab)),
+
+ ?match(Pool, lists:sort(mnesia:lock({table, Tab}, write))),
+
+ done.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+iter_access(doc) ->
+ ["Cover table iteration via callback interface"];
+iter_access(suite) -> [];
+iter_access(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, Config),
+
+ Tab = frag_access,
+ Pool = lists:sort(Nodes),
+ Props = [{n_fragments, 20},
+ {n_ram_copies, 2},
+ {node_pool, Pool}],
+ Def = [{frag_properties, Props},
+ {type, ordered_set},
+ {index, [val]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ [frag_write(Tab, {Tab, Id, Id}) || Id <- lists:seq(1, 400)],
+
+ FragNames = frag_names(Tab),
+ RawRead =
+ fun(Frag) ->
+ Node = mnesia:table_info(Frag, where_to_read),
+ {Frag, rpc:call(Node, ets, tab2list, [Frag])}
+ end,
+
+ ?match(done, mnesia:activity(transaction, fun nice_iter_access/3, [Tab, FragNames, RawRead], mnesia_frag)),
+
+ FragNames = frag_names(Tab),
+ [First, Second | _] = FragNames,
+ [Last, LastButOne | _] = lists:reverse(FragNames),
+
+ ?match({atomic, ok}, mnesia:clear_table(First)),
+ ?match({atomic, ok}, mnesia:clear_table(Second)),
+ ?match({atomic, ok}, mnesia:clear_table(lists:nth(8, FragNames))),
+ ?match({atomic, ok}, mnesia:clear_table(lists:nth(9, FragNames))),
+ ?match({atomic, ok}, mnesia:clear_table(lists:nth(10, FragNames))),
+ ?match({atomic, ok}, mnesia:clear_table(lists:nth(11, FragNames))),
+ ?match({atomic, ok}, mnesia:clear_table(LastButOne)),
+ ?match({atomic, ok}, mnesia:clear_table(Last)),
+
+ ?match(done, mnesia:activity(transaction, fun evil_iter_access/3, [Tab, FragNames, RawRead], mnesia_frag)),
+ Size = fun(Table) -> mnesia:table_info(Table, size) end,
+ ?match(true, 0 < mnesia:activity(transaction, Size, [Tab], mnesia_frag)),
+ ?match({atomic, ok}, mnesia:activity(ets, fun() -> mnesia:clear_table(Tab) end, mnesia_frag)),
+ ?match(0, mnesia:activity(transaction, Size, [Tab], mnesia_frag)),
+
+ ?verify_mnesia(Nodes, []).
+
+nice_iter_access(Tab, FragNames, RawRead) ->
+ RawData = ?ignore(lists:map(RawRead, FragNames)),
+ Keys = [K || {_, Recs} <- RawData, {_, K, _} <- Recs],
+ ExpectedFirst = hd(Keys),
+ ?match(ExpectedFirst, mnesia:first(Tab)),
+ ExpectedLast = lists:last(Keys),
+ ?match(ExpectedLast, mnesia:last(Tab)),
+
+ ExpectedAllPrev = ['$end_of_table' | lists:reverse(tl(lists:reverse(Keys)))],
+ ?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
+
+ ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
+ ?match(ExpectedAllNext, lists:map(fun(K) -> mnesia:next(Tab, K) end, Keys)),
+
+ done.
+
+evil_iter_access(Tab, FragNames, RawRead) ->
+ RawData = ?ignore(lists:map(RawRead, FragNames)),
+ Keys = [K || {_, Recs} <- RawData, {_, K, _} <- Recs],
+ ExpectedFirst = hd(Keys),
+ ?match(ExpectedFirst, mnesia:first(Tab)),
+ ExpectedLast = lists:last(Keys),
+ ?match(ExpectedLast, mnesia:last(Tab)),
+
+ ExpectedAllPrev = ['$end_of_table' | lists:reverse(tl(lists:reverse(Keys)))],
+ ?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
+
+ ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
+ ?match(ExpectedAllNext, lists:map(fun(K) -> mnesia:next(Tab, K) end, Keys)),
+
+ done.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+consistency(doc) ->
+ ["Add and delete fragments during TPC-B"];
+consistency(suite) -> [];
+consistency(Config) when is_list(Config) ->
+ ?skip("Not yet implemented (NYI).~n", []),
+ Nodes = ?acquire_nodes(2, Config),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+evil_create(suite) -> [];
+evil_create(Config) when is_list(Config) ->
+ [Node1, _Node2] = Nodes = ?acquire_nodes(2, Config),
+
+ Create = fun(T, D, P) -> mnesia:create_table(T, [{frag_properties, P}| D]) end,
+
+ Tab = evil_create,
+ %% Props in general
+ ?match({aborted, {badarg, Tab, {frag_properties, no_list}}},
+ Create(Tab, [], no_list)),
+ ?match({aborted, {badarg,Tab , [no_tuple]}},
+ Create(Tab, [], [no_tuple])),
+ ?match({aborted,{badarg, Tab, bad_key}},
+ Create(Tab, [], [{bad_key, 7}])),
+
+ %% n_fragments
+ ?match({aborted,{badarg, Tab, [{n_fragments}]}},
+ Create(Tab, [], [{n_fragments}])),
+ ?match({aborted,{badarg, Tab, [{n_fragments, 1, 1}]}},
+ Create(Tab, [], [{n_fragments, 1, 1}])),
+ ?match({aborted, {bad_type,Tab, {n_fragments, a}}},
+ Create(Tab, [], [{n_fragments, a}])),
+ ?match({aborted, {bad_type, Tab, {n_fragments, 0}}},
+ Create(Tab, [], [{n_fragments, 0}])),
+
+ %% *_copies
+ ?match({aborted, {bad_type, Tab, {n_ram_copies, -1}}},
+ Create(Tab, [], [{n_ram_copies, -1}, {n_fragments, 1}])),
+ ?match({aborted, {bad_type, Tab, {n_disc_copies, -1}}},
+ Create(Tab, [], [{n_disc_copies, -1}, {n_fragments, 1}])),
+ ?match({aborted, {bad_type, Tab, {n_disc_only_copies, -1}}},
+ Create(Tab, [], [{n_disc_only_copies, -1}, {n_fragments, 1}])),
+
+ %% node_pool
+ ?match({aborted, {bad_type, Tab, {node_pool, 0}}},
+ Create(Tab, [], [{node_pool, 0}])),
+ ?match({aborted, {combine_error, Tab, "Too few nodes in node_pool"}},
+ Create(Tab, [], [{n_ram_copies, 2}, {node_pool, [Node1]}])),
+
+ %% foreign_key
+ ?match({aborted, {bad_type, Tab, {foreign_key, bad_key}}},
+ Create(Tab, [], [{foreign_key, bad_key}])),
+ ?match({aborted,{bad_type, Tab, {foreign_key, {bad_key}}}},
+ Create(Tab, [], [{foreign_key, {bad_key}}])),
+ ?match({aborted, {no_exists, {bad_tab, frag_properties}}},
+ Create(Tab, [], [{foreign_key, {bad_tab, val}}])),
+ ?match({aborted, {combine_error, Tab, {Tab, val}}},
+ Create(Tab, [], [{foreign_key, {Tab, val}}])),
+ ?match({atomic, ok},
+ Create(Tab, [], [{n_fragments, 1}])),
+
+ ?match({aborted, {already_exists, Tab}},
+ Create(Tab, [], [{n_fragments, 1}])),
+
+ Tab2 = evil_create2,
+ ?match({aborted, {bad_type, no_attr}},
+ Create(Tab2, [], [{foreign_key, {Tab, no_attr}}])),
+ ?match({aborted, {combine_error, Tab2, _, _, _}},
+ Create(Tab2, [], [{foreign_key, {Tab, val}},
+ {node_pool, [Node1]}])),
+ ?match({aborted, {combine_error, Tab2, _, _, _}},
+ Create(Tab2, [], [{foreign_key, {Tab, val}},
+ {n_fragments, 2}])),
+ ?match({atomic, ok},
+ Create(Tab2, [{attributes, [a, b, c]}], [{foreign_key, {Tab, c}}])),
+ Tab3 = evil_create3,
+ ?match({aborted, {combine_error, Tab3, _, _, _}},
+ Create(Tab3, [{attributes, [a, b]}], [{foreign_key, {Tab2, b}}])),
+ ?match({atomic, ok},
+ Create(Tab3, [{attributes, [a, b]}], [{foreign_key, {Tab, b}}])),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+evil_delete(suite) -> [];
+evil_delete(Config) when is_list(Config) ->
+ ?skip("Not yet implemented (NYI).~n", []),
+ Nodes = ?acquire_nodes(2, Config),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+evil_change(suite) -> [];
+evil_change(Config) when is_list(Config) ->
+ [N1,N2,_N3] = Nodes = ?acquire_nodes(3, Config),
+ Create = fun(T, D, P) -> mnesia:create_table(T, [{frag_properties, P}| D]) end,
+ Props1 = [{n_fragments, 2}, {node_pool, [N1]}],
+ Tab1 = evil_change_ram,
+ ?match({atomic, ok}, Create(Tab1, [], Props1)),
+
+ ?match({atomic,ok}, mnesia:change_table_frag(Tab1, {add_frag, Nodes})),
+ Dist10 = frag_dist(Tab1),
+ ?match([{N1,3}], Dist10),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab1, {add_node, N2})),
+ Dist11 = frag_dist(Tab1),
+ ?match([{N2,0},{N1,3}], Dist11),
+ mnesia_test_lib:kill_mnesia([N2]),
+ ?match({aborted,_}, mnesia:change_table_frag(Tab1, {add_frag, [N2,N1]})),
+ ?verbose("~p~n",[frag_dist(Tab1)]),
+ mnesia_test_lib:start_mnesia([N2]),
+
+ Tab2 = evil_change_disc,
+ ?match({atomic,ok}, Create(Tab2,[],[{n_disc_copies,1},{n_fragments,1},{node_pool,[N1,N2]}])),
+ ?verbose("~p~n", [frag_dist(Tab2)]),
+ ?match({atomic,ok}, mnesia:change_table_frag(Tab2, {add_frag, [N1,N2]})),
+ _Dist20 = frag_dist(Tab2),
+ mnesia_test_lib:kill_mnesia([N2]),
+ ?match({atomic,ok}, mnesia:change_table_frag(Tab2, {add_frag, [N1,N2]})),
+ ?match({aborted,_}, mnesia:change_table_frag(Tab2, {add_frag, [N2,N1]})),
+
+ mnesia_test_lib:start_mnesia([N2]),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+evil_combine(doc) -> ["Bug in mnesia_4.1.5. and earlier"];
+evil_combine(suite) -> [];
+evil_combine(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ ?match({atomic, ok},mnesia:create_table(tab1, [{disc_copies, [Node1]},
+ {frag_properties, [{n_fragments, 2},
+ {node_pool, [Node1]},
+ {n_disc_copies, 1}]}])),
+ ?match({atomic, ok},mnesia:create_table(tab2, [{disc_copies, [Node1]}])),
+ mnesia:wait_for_tables([tab1, tab2], infinity),
+
+ Add2 = fun() ->
+ mnesia:transaction(fun() ->
+ mnesia:write({tab2,1,1})
+ end)
+ end,
+ Fun = fun() ->
+ Add2(),
+ mnesia:write({tab1,9,10})
+ end,
+ ?match(ok, mnesia:activity({transaction, 1}, Fun, [], mnesia_frag)),
+
+ Read = fun(T, K) ->
+ mnesia:read(T, K, read)
+ end,
+
+ ?match([{tab1,9,10}],mnesia:activity(async_dirty, Read, [tab1, 9], mnesia_frag)),
+ ?match([{tab2,1,1}],mnesia:activity(async_dirty, Read, [tab2, 1], mnesia_frag)),
+
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+evil_loop(doc) -> ["Test select/[14]"];
+evil_loop(suite) -> [];
+evil_loop(Config) when is_list(Config) ->
+ [Node1,_Node2] = ?acquire_nodes(2, Config),
+ Tab1 = ss_oset,
+ Tab2 = ss_set,
+ Tab3 = ss_bag,
+ Tabs = [Tab1, Tab2, Tab3],
+ RecName = ss,
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab1},
+ {ram_copies, [Node1]},
+ {record_name, RecName},
+ {type, ordered_set}])),
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab2},
+ {record_name, RecName},
+ {ram_copies, [Node1]},
+ {type, set}])),
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab3},
+ {record_name, RecName},
+ {ram_copies, [Node1]},
+ {type, bag}])),
+ Keys = [-3, -2] ++ lists:seq(1, 5, 2) ++ lists:seq(6, 10),
+ Recs = [{RecName, K, K} || K <- Keys],
+ [mnesia:dirty_write(Tab1, R) || R <- Recs],
+ [mnesia:dirty_write(Tab2, R) || R <- Recs],
+ [mnesia:dirty_write(Tab3, R) || R <- Recs],
+
+ Activate =
+ fun(Tab) ->
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, []})),
+ Dist = frag_dist(Tab),
+ ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist}))
+ end,
+
+ Activate(Tab1),
+ Activate(Tab2),
+ Activate(Tab3),
+
+ Match = fun(Tab) -> mnesia:match_object(Tab, {'_', '_', '_'}, write) end,
+ Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end,
+ Trans = fun(Fun, Args) -> mnesia:activity(transaction, Fun, Args, mnesia_frag) end,
+ LoopHelp = fun('$end_of_table',_) ->
+ [];
+ ({Res,Cont},Fun) ->
+ Sel = mnesia:select(Cont),
+ Res ++ Fun(Sel, Fun)
+ end,
+ SelLoop = fun(Table) ->
+ Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read),
+ LoopHelp(Sel, LoopHelp)
+ end,
+
+ R1 = {RecName, 2, 2},
+ R2 = {RecName, 4, 4},
+ R3 = {RecName, 2, 3},
+ R4 = {RecName, 3, 1},
+ R5 = {RecName, 104, 104},
+ W1 = fun(Tab,Search) ->
+ mnesia:write(Tab,R1,write),
+ mnesia:write(Tab,R2,write),
+ Search(Tab)
+ end,
+ S1 = lists:sort([R1, R2| Recs]),
+ ?match(S1, sort_res(Trans(W1, [Tab1, Select]))),
+ ?match(S1, sort_res(Trans(W1, [Tab1, Match]))),
+ ?match(S1, sort_res(Trans(W1, [Tab1, SelLoop]))),
+ ?match(S1, sort_res(Trans(W1, [Tab2, Select]))),
+ ?match(S1, sort_res(Trans(W1, [Tab2, SelLoop]))),
+ ?match(S1, sort_res(Trans(W1, [Tab2, Match]))),
+ ?match(S1, sort_res(Trans(W1, [Tab3, Select]))),
+ ?match(S1, sort_res(Trans(W1, [Tab3, SelLoop]))),
+ ?match(S1, sort_res(Trans(W1, [Tab3, Match]))),
+ [mnesia:dirty_delete_object(Frag, R) || R <- [R1, R2],
+ Tab <- Tabs,
+ Frag <- frag_names(Tab)],
+
+ W2 = fun(Tab, Search) ->
+ mnesia:write(Tab, R3, write),
+ mnesia:write(Tab, R1, write),
+ Search(Tab)
+ end,
+ S2 = lists:sort([R1 | Recs]),
+ S2Bag = lists:sort([R1, R3 | Recs]),
+ io:format("S2 = ~p\n", [S2]),
+ ?match(S2, sort_res(Trans(W2, [Tab1, Select]))),
+ ?match(S2, sort_res(Trans(W2, [Tab1, SelLoop]))),
+ ?match(S2, sort_res(Trans(W2, [Tab1, Match]))),
+ ?match(S2, sort_res(Trans(W2, [Tab2, Select]))),
+ ?match(S2, sort_res(Trans(W2, [Tab2, SelLoop]))),
+ ?match(S2, sort_res(Trans(W2, [Tab2, Match]))),
+ io:format("S2Bag = ~p\n", [S2Bag]),
+ ?match(S2Bag, sort_res(Trans(W2, [Tab3, Select]))),
+ ?match(S2Bag, sort_res(Trans(W2, [Tab3, SelLoop]))),
+ ?match(S2Bag, sort_res(Trans(W2, [Tab3, Match]))),
+
+ W3 = fun(Tab,Search) ->
+ mnesia:write(Tab, R4, write),
+ mnesia:delete(Tab, element(2, R1), write),
+ Search(Tab)
+ end,
+ S3Bag = lists:sort([R4 | lists:delete(R1, Recs)]),
+ S3 = lists:delete({RecName, 3, 3}, S3Bag),
+ ?match(S3, sort_res(Trans(W3, [Tab1, Select]))),
+ ?match(S3, sort_res(Trans(W3, [Tab1, SelLoop]))),
+ ?match(S3, sort_res(Trans(W3, [Tab1, Match]))),
+ ?match(S3, sort_res(Trans(W3, [Tab2, SelLoop]))),
+ ?match(S3, sort_res(Trans(W3, [Tab2, Select]))),
+ ?match(S3, sort_res(Trans(W3, [Tab2, Match]))),
+ ?match(S3Bag, sort_res(Trans(W3, [Tab3, Select]))),
+ ?match(S3Bag, sort_res(Trans(W3, [Tab3, SelLoop]))),
+ ?match(S3Bag, sort_res(Trans(W3, [Tab3, Match]))),
+
+ W4 = fun(Tab,Search) ->
+ mnesia:delete(Tab, -1, write),
+ mnesia:delete(Tab, 4 , write),
+ mnesia:delete(Tab, 17, write),
+ mnesia:delete_object(Tab, {RecName, -1, x}, write),
+ mnesia:delete_object(Tab, {RecName, 4, x}, write),
+ mnesia:delete_object(Tab, {RecName, 42, x}, write),
+ mnesia:delete_object(Tab, R2, write),
+ mnesia:write(Tab, R5, write),
+ Search(Tab)
+ end,
+ S4Bag = lists:sort([R5 | S3Bag]),
+ S4 = lists:sort([R5 | S3]),
+ ?match(S4, sort_res(Trans(W4, [Tab1, Select]))),
+ ?match(S4, sort_res(Trans(W4, [Tab1, SelLoop]))),
+ ?match(S4, sort_res(Trans(W4, [Tab1, Match]))),
+ ?match(S4, sort_res(Trans(W4, [Tab2, Select]))),
+ ?match(S4, sort_res(Trans(W4, [Tab2, SelLoop]))),
+ ?match(S4, sort_res(Trans(W4, [Tab2, Match]))),
+ ?match(S4Bag, sort_res(Trans(W4, [Tab3, Select]))),
+ ?match(S4Bag, sort_res(Trans(W4, [Tab3, SelLoop]))),
+ ?match(S4Bag, sort_res(Trans(W4, [Tab3, Match]))),
+ [mnesia:dirty_delete_object(Tab, R) || R <- [{RecName, 3, 3}, R5], Tab <- Tabs],
+
+ %% hmmm anything more??
+
+ ?verify_mnesia([Node1], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+evil_delete_db_node(doc) ->
+ ["Delete db_node with a repicated table with foreign key"];
+evil_delete_db_node(suite) -> [];
+evil_delete_db_node(Config) when is_list(Config) ->
+ Nodes = lists:sort(?acquire_nodes(2, Config)),
+ Local = node(),
+ Remote = hd(Nodes -- [Local]),
+
+ Type = case mnesia_test_lib:diskless(Config) of
+ true -> n_ram_copies;
+ false -> n_disc_copies
+ end,
+ Tab = frag_master,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{frag_properties, [{Type, 2}, {node_pool, Nodes}]}])),
+ ExtraTab = frag_foreigner,
+ ?match({atomic, ok}, mnesia:create_table(ExtraTab, [{frag_properties, [{foreign_key, {Tab, key}}, {node_pool, Nodes}]}])),
+
+ GetPool = fun(T) ->
+ case lists:keysearch(node_pool, 1, mnesia:table_info (T, frag_properties)) of
+ {value, {node_pool, N}} -> lists:sort(N);
+ false -> []
+ end
+ end,
+ ?match(Nodes, GetPool(Tab)),
+ ?match(Nodes, GetPool(ExtraTab)),
+
+
+ ?match(stopped, rpc:call(Remote, mnesia, stop, [])),
+ ?match({atomic, ok}, mnesia:del_table_copy(schema, Remote)),
+
+ ?match([Local], GetPool(Tab)),
+ ?match([Local], GetPool(ExtraTab)),
+
+ ?verify_mnesia([Local], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Misc convenient helpers
+
+frag_write(Tab, Rec) ->
+ Fun = fun() -> mnesia:write(Tab, Rec, write) end,
+ mnesia:activity(sync_dirty, Fun, mnesia_frag).
+
+frag_dist(Tab) ->
+ Fun = fun() -> mnesia:table_info(Tab, frag_dist) end,
+ mnesia:activity(sync_dirty, Fun, mnesia_frag).
+
+frag_names(Tab) ->
+ Fun = fun() -> mnesia:table_info(Tab, frag_names) end,
+ mnesia:activity(sync_dirty, Fun, mnesia_frag).
+
+frag_rec_dist(Tab) ->
+ Fun = fun() -> mnesia:table_info(Tab, frag_size) end,
+ [Size || {_, Size} <- mnesia:activity(sync_dirty, Fun, mnesia_frag)].
+
+table_size(Tab) ->
+ Node = mnesia:table_info(Tab, where_to_read),
+ rpc:call(Node, mnesia, table_info, [Tab, size]).
+
+sort_res(List) when is_list(List) ->
+ lists:sort(List);
+sort_res(Else) ->
+ Else.
+
+rev_res(List) when is_list(List) ->
+ lists:reverse(List);
+rev_res(Else) ->
+ Else.
diff --git a/lib/mnesia/test/mnesia_inconsistent_database_test.erl b/lib/mnesia/test/mnesia_inconsistent_database_test.erl
new file mode 100644
index 0000000000..c4b6257d5b
--- /dev/null
+++ b/lib/mnesia/test/mnesia_inconsistent_database_test.erl
@@ -0,0 +1,74 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_inconsistent_database_test).
+-author('[email protected]').
+
+-behaviour(gen_event).
+
+%%-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+%% gen_event callback interface
+-export([init/1, handle_event/2, handle_call/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+
+init(_Args) ->
+ ?verbose("~p installed as event_module~n", [?MODULE]),
+ {ok, []}.
+
+handle_event(Msg, State) ->
+ handle_any_event(Msg, State).
+
+handle_info(Msg, State) ->
+ handle_any_event(Msg, State).
+
+
+handle_call(Msg, State) ->
+ handle_any_event(Msg, State).
+
+
+%% The main...
+
+handle_any_event({mnesia_system_event, Event}, State)
+ when element(1, Event) == inconsistent_database ->
+ ?error("Got event: ~p~n", [Event]),
+ {ok, State};
+handle_any_event(Msg, State) ->
+ ?verbose("Got event: ~p~n", [Msg]),
+ {ok, State}.
+
+%%-----------------------------------------------------------------
+%% terminate(Reason, State) ->
+%% AnyVal
+%%-----------------------------------------------------------------
+
+terminate(_Reason, _State) ->
+ ok.
+
+%%----------------------------------------------------------------------
+%% Func: code_change/3
+%% Purpose: Upgrade process when its code is to be changed
+%% Returns: {ok, NewState}
+%%----------------------------------------------------------------------
+code_change(_OldVsn, _State, _Extra) ->
+ exit(not_supported).
+
diff --git a/lib/mnesia/test/mnesia_install_test.erl b/lib/mnesia/test/mnesia_install_test.erl
new file mode 100644
index 0000000000..5d55fcac0e
--- /dev/null
+++ b/lib/mnesia/test/mnesia_install_test.erl
@@ -0,0 +1,331 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_install_test).
+-author('[email protected]').
+
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [silly_durability, silly_move, silly_upgrade].
+
+groups() ->
+ [{stress, [], stress_cases()}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Stepwise of more and more advanced features
+silly() ->
+ Nodes = [node()] ++ nodes(),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ Config = [{nodes, Nodes}],
+ mnesia_test_lib:eval_test_case(?MODULE, silly2, Config).
+
+silly2(Config) when is_list(Config) ->
+ [Node1 | _] = Nodes = ?acquire_nodes(3, Config),
+ mnesia_test_lib:kill_mnesia(Nodes),
+ ?ignore([mnesia:delete_schema([N]) || N <- Nodes]),
+ ?match(ok, mnesia:create_schema([Node1])),
+ ?match(ok, rpc:call(Node1, mnesia, start, [])),
+ ?match(ok, rpc:call(Node1, mnesia, wait_for_tables,
+ [[schema], infinity])),
+ Res = silly_durability(Config),
+ StressFun = fun(F) -> apply(?MODULE, F, [Config]) end,
+ R =
+ case length(Nodes) of
+ L when L > 1 ->
+ Node2 = lists:nth(2, Nodes),
+ AddDb = [schema, Node2, ram_copies],
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia, add_table_copy, AddDb)),
+ Args = [[{extra_db_nodes, [Node1]}]],
+ ?match(ok, rpc:call(Node2, mnesia, start, Args)),
+ ChangeDb = [schema, Node2, disc_copies],
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia, change_table_copy_type,
+ ChangeDb)),
+ ?match([], mnesia_test_lib:sync_tables([Node1, Node2],
+ [schema])),
+ MoveRes = silly_move(Config),
+ UpgradeRes = silly_upgrade(Config),
+ StressRes = [StressFun(F) || F <- stress_cases()],
+ ?verify_mnesia([Node2], []),
+ [Res, MoveRes, UpgradeRes] ++ StressRes;
+ _ ->
+ StressRes = [StressFun(F) || F <- stress_cases()],
+ ?warning("Too few nodes. Perform net_adm:ping(OtherNode) "
+ "and rerun!!!~n", []),
+ [Res | StressRes]
+ end,
+ ?verify_mnesia([Node1], []),
+ R.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+silly_durability(doc) ->
+ ["Simple test of durability"];
+silly_durability(suite) -> [];
+silly_durability(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = silly,
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+
+ ?match({atomic, ok}, rpc:call(Node1, mnesia,
+ create_table, [Tab, [{Storage, [Node1]}]])),
+
+ Read = fun() -> mnesia:read({Tab, a}) end,
+ Write = fun() -> mnesia:write({Tab, a, b}) end,
+
+ ?match({atomic, []},
+ rpc:call(Node1, mnesia, transaction, [Read])),
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia, transaction, [Write])),
+ ?match({atomic, [{Tab, a, b}]},
+ rpc:call(Node1, mnesia, transaction, [Read])),
+
+ ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
+ ?match(ok, rpc:call(Node1, mnesia, start, [])),
+ case mnesia_test_lib:diskless(Config) of
+ true ->
+ skip;
+ false ->
+ ?match(ok, rpc:call(Node1, mnesia, wait_for_tables, [[Tab], infinity])),
+ ?match({atomic, [{Tab, a, b}]},
+ rpc:call(Node1, mnesia, transaction, [Read]))
+ end,
+ ?verify_mnesia([Node1], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+silly_move(doc) ->
+ ["Simple test of movement of a replica from one node to another"];
+silly_move(suite) -> [];
+silly_move(Config) when is_list(Config) ->
+ [Node1, Node2] = ?acquire_nodes(2, Config),
+ Tab = silly_move,
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia,
+ create_table, [Tab, [{ram_copies, [Node2]}]])),
+ ?match([], mnesia_test_lib:sync_tables([Node1, Node2], [Tab])),
+
+ Read = fun() -> mnesia:read({Tab, a}) end,
+ Write = fun() -> mnesia:write({Tab, a, b}) end,
+
+ ?match({atomic, []},
+ rpc:call(Node1, mnesia, transaction, [Read])),
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia, transaction, [Write])),
+ ?match({atomic, [{Tab, a, b}]},
+ rpc:call(Node1, mnesia, transaction, [Read])),
+
+ case mnesia_test_lib:diskless(Config) of
+ true -> skip;
+ false ->
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia,
+ change_table_copy_type, [Tab, Node2, disc_only_copies])),
+ ?match([], mnesia_test_lib:sync_tables([Node1, Node2], [Tab]))
+ end,
+ ?match({atomic, [{Tab, a, b}]}, rpc:call(Node1, mnesia, transaction, [Read])),
+
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia,
+ move_table_copy, [Tab, Node2, Node1])),
+ ?match([], mnesia_test_lib:sync_tables([Node1, Node2], [Tab])),
+ ?match({atomic, [{Tab, a, b}]},
+ rpc:call(Node1, mnesia, transaction, [Read])),
+ ?verify_mnesia([Node1], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+silly_upgrade(doc) ->
+ ["Simple test of a schema upgrade and restore from backup"];
+silly_upgrade(suite) -> [];
+silly_upgrade(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Name = silly_upgrade,
+ Tab1 = silly_upgrade1,
+ Tab2 = silly_upgrade2,
+ Bup = "silly_upgrade.BUP",
+ Bup2 = "silly_upgrade_part.BUP",
+ ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, [{disc_only_copies, Nodes}])),
+
+ CpState = add_some_records(Tab1, Tab2, []),
+ ?match(match, verify_state(Tab1, Tab2, CpState)),
+ file:delete(Bup),
+ ?match(ok, mnesia:backup(Bup)),
+ Args = [{name, Name}, {ram_overrides_dump, true},
+ {min, [Tab1, schema]}, {max, [Tab2]}],
+ ?match({ok, Name, _}, mnesia:activate_checkpoint(Args)),
+
+ IgnoreState = add_more_records(Tab1, Tab2, CpState),
+ ?match(match, verify_state(Tab1, Tab2, IgnoreState)),
+ ?match({mismatch, _, _}, verify_state(Tab1, Tab2, CpState)),
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab2, Node1)),
+ file:delete(Bup2),
+ ?match(ok, mnesia:backup_checkpoint(Name, Bup2)),
+
+ UpgradeState = transform_some_records(Tab1, Tab2, IgnoreState),
+ ?match({mismatch, _, _}, verify_state(Tab1, Tab2, CpState)),
+ ?match({mismatch, _, _}, verify_state(Tab1, Tab2, IgnoreState)),
+ ?match(match, verify_state(Tab1, Tab2, UpgradeState)),
+
+ ?match(ok, mnesia:deactivate_checkpoint(Name)),
+ ?match(match, verify_state(Tab1, Tab2, UpgradeState)),
+
+ ?match(ok, mnesia:install_fallback(Bup2)),
+ file:delete(Bup2),
+ %% Will generate intentional crash, fatal error
+ ?match([], mnesia_test_lib:stop_mnesia([Node2])),
+ wait_till_dead([Node1, Node2]),
+ ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab1, Tab2])),
+ ?match(match, verify_state(Tab1, Tab2, CpState)),
+
+ ?match(ok, mnesia:install_fallback(Bup)),
+ file:delete(Bup),
+ %% Will generate intentional crash, fatal error
+ ?match([], mnesia_test_lib:stop_mnesia([Node1, Node2])),
+ wait_till_dead([Node1, Node2]),
+ ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [Tab1, Tab2])),
+ CpState2 = [X || X <- CpState, element(1, X) /= Tab1],
+ ?match(match, verify_state(Tab1, Tab2, CpState2)),
+ ?verify_mnesia(Nodes, []).
+
+wait_till_dead([]) -> ok;
+wait_till_dead([N|Ns]) ->
+ Apps = rpc:call(N, application, which_applications, []),
+ case lists:keymember(mnesia, 1, Apps) of
+ true ->
+ timer:sleep(10),
+ wait_till_dead([N|Ns]);
+ false ->
+ wait_till_dead(Ns)
+ end.
+
+add_some_records(Tab1, Tab2, Old) ->
+ Recs1 = [{Tab1, I, I} || I <- lists:seq(1, 30)],
+ Recs2 = [{Tab2, I, I} || I <- lists:seq(20, 40)],
+ lists:foreach(fun(R) -> mnesia:dirty_write(R) end, Recs1),
+ Fun = fun(R) -> mnesia:write(R) end,
+ Trans = fun() -> lists:foreach(Fun, Recs2) end,
+ ?match({atomic, _}, mnesia:transaction(Trans)),
+ lists:sort(Old ++ Recs1 ++ Recs2).
+
+add_more_records(Tab1, Tab2, Old) ->
+ Change1 = [{T, K, V+100} || {T, K, V} <- Old, K==23],
+ Change2 = [{T, K, V+100} || {T, K, V} <- Old, K==24],
+ Del = [{T, K} || {T, K, _V} <- Old, K>=25],
+ New = [{Tab1, 50, 50}, {Tab2, 50, 50}],
+ lists:foreach(fun(R) -> mnesia:dirty_write(R) end, Change1),
+ lists:foreach(fun(R) -> mnesia:dirty_delete(R) end, Del),
+ Fun = fun(R) -> mnesia:write(R) end,
+ Trans = fun() -> lists:foreach(Fun, Change2 ++ New) end,
+ ?match({atomic, ok}, mnesia:transaction(Trans)),
+ Recs = [{T, K, V} || {T, K, V} <- Old, K<23] ++ Change1 ++ Change2 ++ New,
+ lists:sort(Recs).
+
+
+verify_state(Tab1, Tab2, Exp) ->
+ Fun = fun() ->
+ Act1 = [mnesia:read({Tab1, K}) || K <- mnesia:all_keys(Tab1)],
+ Act2 = [mnesia:read({Tab2, K}) || K <- mnesia:all_keys(Tab2)],
+ Act = lists:append(Act1) ++ lists:append(Act2),
+ {ok, Act -- Exp, Exp -- Act}
+ end,
+ case mnesia:transaction(Fun) of
+ {atomic, {ok, [], []}} -> match;
+ {atomic, {ok, More, Less}} -> {mismatch, More, Less};
+ {aborted, Reason} -> {error, Reason}
+ end.
+
+transform_some_records(Tab1, _Tab2, Old) ->
+ Fun = fun(Rec) ->
+ list_to_tuple(tuple_to_list(Rec) ++ [4711])
+ end,
+ ?match({atomic, ok},
+ mnesia:transform_table(Tab1, Fun, [key, val, extra])),
+ Filter = fun(Rec) when element(1, Rec) == Tab1 -> {true, Fun(Rec)};
+ (_) -> true
+ end,
+ lists:sort(lists:zf(Filter, Old)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+stress_cases() ->
+[conflict, dist].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dist(doc) ->
+ ["Avoid lock conflicts in order to maximize thruput",
+ "Ten drivers per node, tables replicated to all nodes, lots of branches"];
+dist(suite) -> [];
+dist(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, 10 * 60000}]),
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ ?match({ok, _}, mnesia_tpcb:start(dist_args(Nodes, Storage))).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+conflict(doc) ->
+ ["Provoke a lot of lock conflicts.",
+ "Ten drivers per node, tables replicated to all nodes, single branch"];
+conflict(suite) -> [];
+conflict(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, 10 * 60000}]),
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ ?match({ok, _}, mnesia_tpcb:start(conflict_args(Nodes, Storage))).
+
+conflict_args(Nodes, ReplicaType) ->
+ [{db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 10},
+ {n_branches, 1},
+ {n_accounts_per_branch, 10},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(5)},
+ {report_interval, timer:seconds(10)},
+ {use_running_mnesia, true},
+ {reuse_history_id, true}].
+
+dist_args(Nodes, ReplicaType) ->
+ [{db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 10},
+ {n_branches, length(Nodes) * 100},
+ {n_accounts_per_branch, 10},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(5)},
+ {report_interval, timer:seconds(10)},
+ {use_running_mnesia, true},
+ {reuse_history_id, true}].
+
diff --git a/lib/mnesia/test/mnesia_isolation_test.erl b/lib/mnesia/test/mnesia_isolation_test.erl
new file mode 100644
index 0000000000..3273bc4d40
--- /dev/null
+++ b/lib/mnesia/test/mnesia_isolation_test.erl
@@ -0,0 +1,2355 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_isolation_test).
+-author('[email protected]').
+
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [{group, locking}, {group, visibility}].
+
+groups() ->
+ [{locking, [],
+ [no_conflict, simple_queue_conflict,
+ advanced_queue_conflict, simple_deadlock_conflict,
+ advanced_deadlock_conflict, lock_burst,
+ {group, sticky_locks}, {group, unbound_locking},
+ {group, admin_conflict}, nasty]},
+ {sticky_locks, [], [basic_sticky_functionality]},
+ {unbound_locking, [], [unbound1, unbound2]},
+ {admin_conflict, [],
+ [create_table, delete_table, move_table_copy,
+ add_table_index, del_table_index, transform_table,
+ snmp_open_table, snmp_close_table,
+ change_table_copy_type, change_table_access,
+ add_table_copy, del_table_copy, dump_tables,
+ {group, extra_admin_tests}]},
+ {extra_admin_tests, [],
+ [del_table_copy_1, del_table_copy_2, del_table_copy_3,
+ add_table_copy_1, add_table_copy_2, add_table_copy_3,
+ add_table_copy_4, move_table_copy_1, move_table_copy_2,
+ move_table_copy_3, move_table_copy_4]},
+ {visibility, [],
+ [dirty_updates_visible_direct,
+ dirty_reads_regardless_of_trans,
+ trans_update_invisibible_outside_trans,
+ trans_update_visible_inside_trans, write_shadows,
+ delete_shadows, write_delete_shadows_bag,
+ write_delete_shadows_bag2, {group, iteration},
+ shadow_search, snmp_shadows]},
+ {removed_resources, [], [rr_kill_copy]},
+ {iteration, [], [foldl, first_next]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+no_conflict(suite) -> [];
+no_conflict(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = no_conflict,
+ create_conflict_table(Tab, [Node1]),
+ Fun = fun(OtherOid, Lock1, Lock2) ->
+ %% Start two transactions
+ {success, [B, A]} = ?start_activities([Node1, Node1]),
+ ?start_transactions([B, A]),
+
+ A ! fun() -> Lock1(one_oid(Tab)), ok end,
+ ?match_receive({A, ok}),
+ B ! fun() -> Lock2(OtherOid), ok end,
+ ?match_receive({B, ok}),
+ A ! fun() -> mnesia:abort(ok) end,
+ ?match_receive({A, {aborted, ok}}),
+ B ! fun() -> mnesia:abort(ok) end,
+ ?match_receive({B, {aborted, ok}})
+ end,
+ NoLocks = lock_funs(no_lock, any_granularity),
+ SharedLocks = lock_funs(shared_lock, any_granularity),
+ AnyLocks = lock_funs(any_lock, any_granularity),
+ OneOneFun = fun(Lock1, Lock2) -> Fun(one_oid(Tab), Lock1, Lock2) end,
+ fun_loop(OneOneFun, NoLocks, AnyLocks),
+ fun_loop(OneOneFun, AnyLocks, NoLocks),
+ fun_loop(OneOneFun, SharedLocks, SharedLocks),
+
+ %% Lock different objects
+ OneOtherFun = fun(Lock1, Lock2) -> Fun(other_oid(Tab), Lock1, Lock2) end,
+ OneSharedLocks = lock_funs(shared_lock, one),
+ OneExclusiveLocks = lock_funs(exclusive_lock, one),
+ fun_loop(OneOtherFun, OneSharedLocks, OneExclusiveLocks),
+ fun_loop(OneOtherFun, OneExclusiveLocks, OneSharedLocks),
+ fun_loop(OneOtherFun, OneExclusiveLocks, OneExclusiveLocks),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+simple_queue_conflict(suite) -> [];
+simple_queue_conflict(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = simple_queue_conflict,
+ create_conflict_table(Tab, [Node1]),
+ Fun = fun(OneLock, OtherLock) ->
+ %% Start two transactions
+ {success, [B, A]} = ?start_activities([Node1, Node1]),
+ ?start_transactions([B, A]),
+
+ A ! fun() -> OneLock(one_oid(Tab)), ok end,
+ ?match_receive({A, ok}),
+ B ! fun() -> OtherLock(one_oid(Tab)), ok end,
+ wait_for_lock(B, [Node1], 20), % Max 10 sec
+ A ! end_trans,
+ ?match_multi_receive([{A, {atomic, end_trans}}, {B, ok}]),
+ B ! fun() -> mnesia:abort(ok) end,
+ ?match_receive({B, {aborted, ok}})
+ end,
+ OneSharedLocks = lock_funs(shared_lock, one),
+ AllSharedLocks = lock_funs(shared_lock, all),
+ OneExclusiveLocks = lock_funs(exclusive_lock, one),
+ AllExclusiveLocks = lock_funs(exclusive_lock, all),
+ fun_loop(Fun, OneExclusiveLocks, OneExclusiveLocks),
+ fun_loop(Fun, AllExclusiveLocks, AllExclusiveLocks),
+ fun_loop(Fun, OneExclusiveLocks, AllExclusiveLocks),
+ fun_loop(Fun, AllExclusiveLocks, OneExclusiveLocks),
+ fun_loop(Fun, OneSharedLocks, AllExclusiveLocks),
+ fun_loop(Fun, AllSharedLocks, OneExclusiveLocks),
+ ok.
+
+wait_for_lock(Pid, _Nodes, 0) ->
+ Queue = mnesia:system_info(lock_queue),
+ ?error("Timeout while waiting for lock on Pid ~p in queue ~p~n", [Pid, Queue]);
+wait_for_lock(Pid, Nodes, N) ->
+ rpc:multicall(Nodes, sys, get_status, [mnesia_locker]),
+ List = [rpc:call(Node, mnesia, system_info, [lock_queue]) || Node <- Nodes],
+ Q = lists:append(List),
+ check_q(Pid, Q, Nodes, N).
+
+check_q(Pid, [{_Oid, _Op, Pid, _Tid, _WFT} | _Tail], _N, _Count) -> ok;
+check_q(Pid, [_ | Tail], N, Count) -> check_q(Pid, Tail, N, Count);
+check_q(Pid, [], N, Count) ->
+ timer:sleep(500),
+ wait_for_lock(Pid, N, Count - 1).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+advanced_queue_conflict(suite) -> [];
+advanced_queue_conflict(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = advanced_queue_conflict,
+ create_conflict_table(Tab, [Node1]),
+ OneRec = {Tab, 3, 3},
+ OneOid = {Tab, 3},
+ OtherRec = {Tab, 4, 4},
+ OtherOid = {Tab, 4},
+
+ %% Start four transactions
+ {success, [D, C, B, A]} = ?start_activities(lists:duplicate(4, Node1)),
+ ?start_transactions([D, C, B, A]),
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+
+ %% Acquire some locks
+ A ! fun() -> mnesia:write(OneRec) end,
+ ?match_receive({A, ok}),
+ A ! fun() -> mnesia:read(OneOid) end,
+ ?match_receive({A, [OneRec]}),
+
+ B ! fun() -> mnesia:write(OtherRec) end,
+ ?match_receive({B, ok}),
+ B ! fun() -> mnesia:read(OneOid) end,
+ ?match_receive(timeout),
+
+ C ! fun() -> mnesia:read(OtherOid) end,
+ ?match_receive(timeout),
+ D ! fun() -> mnesia:wread(OtherOid) end,
+ ?match_receive(timeout),
+
+ %% and release them in a certain order
+ A ! end_trans,
+ ?match_multi_receive([{A, {atomic, end_trans}}, {B, [OneRec]}]),
+ B ! end_trans,
+ ?match_multi_receive([{B, {atomic, end_trans}}, {C, [OtherRec]}]),
+ C ! end_trans,
+ ?match_multi_receive([{C, {atomic, end_trans}}, {D, [OtherRec]}]),
+ D ! end_trans,
+ ?match_receive({D, {atomic, end_trans}}),
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+simple_deadlock_conflict(suite) -> [];
+simple_deadlock_conflict(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = simple_deadlock_conflict,
+ create_conflict_table(Tab, [Node1]),
+ Rec = {Tab, 4, 4},
+ Oid = {Tab, 4},
+
+ %% Start two transactions
+ {success, [B, A]} = ?start_activities(lists:duplicate(2, Node1)),
+ mnesia_test_lib:start_transactions([B, A], 0), % A is newest
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+
+ B ! fun() -> mnesia:write(Rec) end,
+ ?match_receive({B, ok}),
+ A ! fun() -> mnesia:read(Oid) end,
+ ?match_receive({A, {aborted, nomore}}),
+ B ! end_trans,
+ ?match_receive({B, {atomic, end_trans}}),
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+advanced_deadlock_conflict(suite) -> [];
+advanced_deadlock_conflict(Config) when is_list(Config) ->
+ [Node1, Node2] = ?acquire_nodes(2, Config),
+ Tab = advanced_deadlock_conflict,
+ create_conflict_table(Tab, [Node2]),
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ Rec = {Tab, 4, 4},
+ Oid = {Tab, 4},
+
+ %% Start two transactions
+ {success, [B, A]} = ?start_activities([Node1, Node2]),
+ mnesia_test_lib:start_sync_transactions([B, A], 0), % A is newest
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+
+ B ! fun() -> mnesia:write(Rec) end,
+ ?match_receive({B, ok}),
+ A ! fun() -> mnesia:read(Oid) end,
+ ?match_receive({A, {aborted, nomore}}),
+ B ! end_trans,
+ ?match_receive({B, {atomic, end_trans}}),
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+one_oid(Tab) -> {Tab, 1}.
+other_oid(Tab) -> {Tab, 2}.
+
+create_conflict_table(Tab, Nodes) ->
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, Nodes},
+ {attributes, [key, val]},
+ {index, [val]}
+ ])),
+ ?match([], mnesia_test_lib:sync_tables(Nodes, [Tab])),
+ init_conflict_table(Tab).
+
+init_conflict_table(Tab) ->
+ Recs = mnesia:dirty_match_object({Tab, '_', '_'}),
+ lists:foreach(fun(R) -> mnesia:dirty_delete_object(R) end, Recs),
+ Keys = [one_oid(Tab), other_oid(Tab)],
+ [mnesia:dirty_write({T, K, K}) || {T, K} <- Keys].
+
+%% Apply Fun for each X and Y
+fun_loop(Fun, Xs, Ys) ->
+ lists:foreach(fun(X) -> lists:foreach(fun(Y) -> do_fun(Fun, X, Y) end, Ys) end, Xs).
+
+do_fun(Fun, X, Y) ->
+ Pid = spawn_link(?MODULE, do_fun, [self(), Fun, X, Y]),
+ receive
+ {done_fun, Pid} -> done_fun
+ end.
+
+do_fun(Monitor, Fun, X, Y) ->
+ ?log("{do_fun ~p~n", [[Fun, X, Y]]),
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ Fun(X, Y),
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ unlink(Monitor),
+ Monitor ! {done_fun, self()},
+ exit(done_fun).
+
+%% Returns a list of fun's
+lock_funs(no_lock, one) ->
+ [
+ fun(Oid) -> mnesia:dirty_read(Oid) end,
+ fun({Tab, Key}) -> mnesia:dirty_write({Tab, Key, Key}) end,
+ fun({Tab, Key}) -> mnesia:dirty_write({Tab, Key, Key}),
+ mnesia:dirty_update_counter({Tab, Key}, 0) end,
+ fun(Oid) -> mnesia:dirty_delete(Oid) end,
+ fun({Tab, Key}) -> mnesia:dirty_delete_object({Tab, Key, Key}) end,
+ fun({Tab, Key}) -> mnesia:dirty_match_object({Tab, Key, Key}) end,
+ fun({Tab, Key}) -> mnesia:dirty_index_match_object({Tab, Key, Key}, val) end,
+ fun({Tab, Key}) -> mnesia:dirty_index_read(Tab, Key, val) end,
+ fun({Tab, Key}) -> mnesia:dirty_index_match_object({Tab, '_', Key}, val) end
+ ];
+lock_funs(no_lock, all) ->
+ [
+ fun({Tab, _}) -> mnesia:dirty_match_object({Tab, '_', '_'}) end,
+ fun({Tab, _}) -> slot_iter(Tab) end,
+ fun({Tab, _}) -> key_iter(Tab) end
+ ];
+lock_funs(shared_lock, one) ->
+
+ [
+ fun(Oid) -> mnesia:read(Oid) end,
+ fun({Tab, Key}) ->
+ init_conflict_table(Tab),
+ mnesia:dirty_delete(other_oid(Tab)),
+ mnesia:match_object({Tab, Key, Key}) end
+ ];
+lock_funs(shared_lock, all) ->
+ [
+ fun({Tab, _}) -> mnesia:read_lock_table(Tab) end,
+ fun({Tab, Key}) -> mnesia:match_object({Tab, '_', Key}) end,
+ fun({Tab, _}) -> mnesia:match_object({Tab, '_', '_'}) end,
+ fun({Tab, _}) -> mnesia:all_keys(Tab) end,
+ fun({Tab, Key}) -> mnesia:index_match_object({Tab, '_', Key}, val) end,
+ fun({Tab, Key}) -> mnesia:index_read(Tab, Key, val) end
+ ];
+lock_funs(exclusive_lock, one) ->
+ [
+ fun(Oid) -> mnesia:wread(Oid) end,
+ fun({Tab, Key}) -> mnesia:write({Tab, Key, Key}) end,
+ fun(Oid) -> mnesia:delete(Oid) end,
+ fun({Tab, Key}) -> mnesia:delete_object({Tab, Key, Key}) end,
+ fun({Tab, Key}) -> mnesia:s_write({Tab, Key, Key}) end,
+ fun(Oid) -> mnesia:s_delete(Oid) end,
+ fun({Tab, Key}) -> mnesia:s_delete_object({Tab, Key, Key}) end
+ ];
+lock_funs(exclusive_lock, all) ->
+ [
+ fun({Tab, _}) -> mnesia:write_lock_table(Tab) end
+ ];
+lock_funs(Compatibility, any_granularity) ->
+ lists:append([lock_funs(Compatibility, Granularity) ||
+ Granularity <- [one, all]]);
+lock_funs(any_lock, Granularity) ->
+ lists:append([lock_funs(Compatibility, Granularity) ||
+ Compatibility <- [no_lock, shared_lock, exclusive_lock]]).
+
+slot_iter(Tab) ->
+ slot_iter(Tab, mnesia:dirty_slot(Tab, 0), 1).
+slot_iter(_Tab, '$end_of_table', _) ->
+ [];
+slot_iter(Tab, Recs, Slot) ->
+ Recs ++ slot_iter(Tab, mnesia:dirty_slot(Tab, Slot), Slot+1).
+
+key_iter(Tab) ->
+ key_iter(Tab, mnesia:dirty_first(Tab)).
+key_iter(_Tab, '$end_of_table') ->
+ [];
+key_iter(Tab, Key) ->
+ [Key | key_iter(Tab, mnesia:dirty_next(Tab, Key))].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+lock_burst(suite) -> [];
+lock_burst(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = burst,
+ ?match({atomic, ok}, mnesia:create_table(Tab,
+ [{attributes, [a, b]},
+ {ram_copies, [Node1]}])),
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ?match(ok, burst_em(Tab, 1000)),
+ ?match([{burst,1,1000}], mnesia:dirty_read(Tab,1)),
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+burst_em(Tab, N) ->
+ spawn_link(?MODULE, burst_counter, [self(), Tab, N]),
+ receive
+ burst_counter_done -> ok
+ end.
+
+burst_counter(Monitor, Tab, N) when N > 0 ->
+ ?match(ok, burst_gen(Tab, N, self())),
+ Monitor ! burst_receiver(N).
+
+burst_receiver(0) ->
+ burst_counter_done;
+burst_receiver(N) ->
+ receive
+ burst_incr_done ->
+ burst_receiver(N-1)
+ end.
+
+burst_gen(_, 0, _) ->
+ ok;
+burst_gen(Tab, N, Father) when is_integer(N), N > 0 ->
+ spawn_link(?MODULE, burst_incr, [Tab, Father]),
+ burst_gen(Tab, N-1, Father).
+
+burst_incr(Tab, Father) ->
+ Fun = fun() ->
+ Val =
+ case mnesia:read({Tab, 1}) of
+ [{Tab, 1, V}] -> V;
+ [] -> 0
+ end,
+ mnesia:write({Tab, 1, Val+1})
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun)),
+ Father ! burst_incr_done.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+basic_sticky_functionality(suite) -> [];
+basic_sticky_functionality(Config) when is_list(Config) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = basic_table,
+ Storage = mnesia_test_lib:storage_type(disc_copies, Config),
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Storage, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(sync, [{ram_copies, Nodes}])),
+ Trans1 = fun() ->
+ ?match(ok, mnesia:s_write({Tab, 1, 2})),
+ ?match([{Tab, 1, 2}], mnesia:read({Tab, 1})),
+ ?match(timeout, receive M -> M after 500 -> timeout end),
+ ?match(ok, mnesia:s_write({Tab, 2, 2})),
+ ?match(ok, mnesia:write({Tab, 42, 4711}))
+ end,
+ Trans2 = fun() ->
+ ?match([{Tab, 1, 2}], mnesia:read({Tab, 1})),
+ ?match(timeout, receive M -> M after 500 -> timeout end),
+ ?match(ok, mnesia:write({Tab, 1, 4711})),
+ ?match(ok, mnesia:s_write({Tab, 2, 4})),
+ ?match(ok, mnesia:delete({Tab, 42}))
+ end,
+ rpc:call(N1, mnesia, transaction, [Trans1]),
+ ?match([{Tab,N1}], rpc:call(N1, ?MODULE, get_sticky, [])),
+ ?match([{Tab,N1}], rpc:call(N2, ?MODULE, get_sticky, [])),
+
+ rpc:call(N2, mnesia, transaction, [Trans2]),
+ ?match([], rpc:call(N1, ?MODULE, get_sticky, [])),
+ ?match([], rpc:call(N2, ?MODULE, get_sticky, [])),
+
+ Slock = fun() -> mnesia:read({sync,sync}),get_sticky() end,
+ ?match({atomic, [{Tab,1, 4711}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+ ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
+ ?match({atomic, [{Tab,N1}]}, rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 3}),Slock() end])),
+ ?match([{Tab,N1}], rpc:call(N2, ?MODULE, get_sticky, [])),
+
+ ?match({atomic,[]}, rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 4}),Slock() end])),
+
+ ?match([], rpc:call(N1, ?MODULE, get_sticky, [])),
+ ?match([], rpc:call(N2, ?MODULE, get_sticky, [])),
+
+ ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 4}),Slock() end])),
+
+ ?match({atomic,[]}, rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 5}),Slock() end])),
+ ?match({atomic,[{Tab,N1}]}, rpc:call(N1, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 5}),Slock() end])),
+ ?match({atomic,[]}, rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 6}),Slock() end])),
+ ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 7}),Slock() end])),
+
+ ?match([{Tab,N2}], get_sticky()),
+ ?match({atomic, [{Tab,1, 7}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+ ?match([{Tab,N2}], get_sticky()),
+ ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
+ ?match([{Tab,N2}], get_sticky()),
+ ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction,
+ [fun() -> mnesia:s_write({Tab, 1, 6}),Slock() end])),
+ ?match([{Tab,N2}], get_sticky()),
+ ?match({atomic, [{Tab,1, 6}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+ ?match([{Tab,N2}], get_sticky()),
+ ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
+ ?match([{Tab,N2}], get_sticky()),
+ ?verify_mnesia(Nodes, []).
+
+get_sticky() ->
+ mnesia_locker ! {get_table, self(), mnesia_sticky_locks},
+ receive {mnesia_sticky_locks, Locks} -> Locks end.
+
+get_held() ->
+ mnesia_locker ! {get_table, self(), mnesia_sticky_locks},
+ receive {mnesia_sticky_locks, Locks} -> Locks end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+unbound1(suite) -> [];
+unbound1(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+
+ ?match({atomic, ok}, mnesia:create_table(ul, [])),
+
+ Tester = self(),
+ Write = fun() ->
+ mnesia:write({ul, {key, {17,42}}, val}),
+ ?log("~p Got write lock waiting...~n", [self()]),
+ Tester ! continue,
+ receive
+ continue ->
+ ok
+ end,
+ ?log("..continuing~n", []),
+ ok
+ end,
+
+ {success, [A]} = ?start_activities([Node1]),
+ ?start_transactions([A]),
+ A ! Write,
+
+ receive continue -> ok end,
+
+ Match = fun() ->
+ case catch mnesia:match_object({ul, {key, {'_', '$0'}}, '_'}) of
+ {'EXIT', What} -> %% Cyclic first time
+ ?log("Cyclic Restarting~n", []),
+ A ! continue,
+ A ! end_trans,
+ exit(What);
+ Res ->
+ ?log("Got match log ~p...~n", [Res]),
+ Res
+ end
+ end,
+ ?match({atomic, [{ul,{key,{17,42}},val}]}, mnesia:transaction(Match)),
+
+ ?match_receive({A, ok}),
+ ?match_receive({A, {atomic, end_trans}}),
+ ok.
+
+unbound2(suite) -> [];
+unbound2(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+
+ ?match({atomic, ok}, mnesia:create_table(ul, [])),
+
+ {success, [B, A]} = ?start_activities([Node1, Node1]),
+
+ Me = self(),
+
+ Write = fun() ->
+ mnesia:write({ul, {key, {17,42}}, val}),
+ ?log("~p Got write lock waiting... Tid ~p ~n",
+ [self(), get(mnesia_activity_state)]),
+ Me ! ok_lock,
+ receive
+ continue ->
+ ok
+ end,
+ ?log("..continuing~n", []),
+ ok
+ end,
+
+ Match = fun() ->
+ receive
+ continueB ->
+ ?log("~p, moving on TID ~p~n",
+ [self(), get(mnesia_activity_state)]),
+ Me ! {self(), continuing}
+ end,
+ case catch mnesia:match_object({ul, {key, {'_', '$0'}},
+ '_'}) of
+ {'EXIT', What} -> %% Cyclic first time
+ ?log("Cyclic Restarting ~p ~n", [What]),
+ {should_not_happen,What};
+ Res ->
+ ?log("Got match log ~p...~n", [Res]),
+ Res
+ end
+ end,
+
+ B ! fun() -> mnesia:transaction(Match) end,
+ timer:sleep(100), %% Let B be started first..
+ A ! fun() -> mnesia:transaction(Write) end,
+
+ receive ok_lock -> ok end,
+
+ B ! continueB,
+ ?match_receive({B, continuing}),
+
+ %% B should now be in lock queue.
+ A ! continue,
+ ?match_receive({A, {atomic, ok}}),
+ ?match_receive({B, {atomic, [{ul,{key,{17,42}},val}]}}),
+ ok.
+
+receiver() ->
+ receive
+ {_Pid, begin_trans} ->
+ receiver();
+ Else ->
+ Else
+ after
+ 10000 ->
+ timeout
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+create_table(suite) -> [];
+create_table(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = c_t_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ DiskMaybe = mnesia_test_lib:storage_type(disc_copies, Config),
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, create_table,
+ [test_tab1, [{DiskMaybe, [ThisNode]}]]]),
+ ?match_multi_receive([{Pid, {atomic, ok}},
+ {'EXIT', Pid, normal}]), %% No Locks! op should be exec.
+
+ Pid2 = spawn_link(?MODULE, op, [self(), mnesia, create_table,
+ [test_tab2, [{ram_copies, [Node2]}]]]),
+
+ ?match_multi_receive([{Pid2, {atomic, ok}},
+ {'EXIT', Pid2, normal}]), %% No Locks! op should be exec.
+
+ A ! end_trans,
+ ?match_receive({A,{atomic,end_trans}}),
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+delete_table(suite) -> [];
+delete_table(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = d_t_tab,
+ Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:read({Tab, 1}) end,
+ ?match_receive({A, [{Tab, 1, 1, 0}]}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, delete_table,
+ [Tab]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+move_table_copy(suite) -> [];
+move_table_copy(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = m_t_c_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 2, 3}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, move_table_copy,
+ [Tab, ThisNode, Node2]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ timer:sleep(500), %% Don't know how to sync this !!!
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ sys:get_status(whereis(mnesia_tm)), % Explicit sync, release locks is async
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+add_table_index(suite) -> [];
+add_table_index(Config) when is_list(Config) ->
+ [ThisNode, _Node2] = ?acquire_nodes(2, Config ++ [{tc_timeout, 60000}]),
+ Tab = a_t_i_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia,
+ add_table_index, [Tab, attr1]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+del_table_index(suite) -> [];
+del_table_index(Config) when is_list(Config) ->
+ [ThisNode, _Node2] = ?acquire_nodes(2, Config),
+ Tab = d_t_i_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, attr1)),
+
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 51, 51, attr2}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, del_table_index,
+ [Tab, attr1]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+ %% Locks released! op should be exec.
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+transform_table(suite) -> [];
+transform_table(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = t_t_tab,
+ Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:read({Tab, 1}) end,
+ ?match_receive({A, [{Tab, 1, 1, 0}]}), %% A is executed
+
+ Transform = fun({Table, Key, Attr1, Attr2}) -> % Need todo a transform
+ {Table, Key, {Attr1, Attr2}} end,
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, transform_table,
+ [Tab, Transform, [key, attr1]]]),
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+snmp_open_table(suite) -> [];
+snmp_open_table(Config) when is_list(Config) ->
+ [ThisNode, _Node2] = ?acquire_nodes(2, Config),
+ Tab = s_o_t_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 1, 100}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, snmp_open_table,
+ [Tab, [{key, integer}]]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ %% Locks released! op should be exec. Can take a while (thats the timeout)
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+snmp_close_table(suite) -> [];
+snmp_close_table(Config) when is_list(Config) ->
+ [ThisNode, _Node2] = ?acquire_nodes(2, Config),
+ Tab = s_c_t_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab, [{key, integer}])),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 1, 100}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, snmp_close_table, [Tab]]),
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ %% Locks released! op should be exec. Can take a while (thats the timeout)
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+change_table_copy_type(suite) -> [];
+change_table_copy_type(Config) when is_list(Config) ->
+ [ThisNode, _Node2] = ?acquire_nodes(2, Config),
+ Tab = c_t_c_t_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+ A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, change_table_copy_type,
+ [Tab, ThisNode, disc_copies]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+change_table_access(suite) -> [];
+change_table_access(Config) when is_list(Config) ->
+ [ThisNode, _Node2] = ?acquire_nodes(2, Config),
+ Tab = c_t_a_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, change_table_access_mode,
+ [Tab, read_only]]),
+
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+add_table_copy(suite) -> [];
+add_table_copy(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = a_t_c_tab,
+ Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+
+ A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, add_table_copy,
+ [Tab, Node2, ram_copies]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+del_table_copy(suite) -> [];
+del_table_copy(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = d_t_c_tab,
+ Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+ A ! fun() -> mnesia:write({Tab, 1, 2, 5}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, del_table_copy,
+ [Tab, ThisNode]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A, {atomic,end_trans}}),
+
+ ?match_receive({Pid, {atomic, ok}}),
+ ?match_receive({'EXIT', Pid, normal}),
+
+ timer:sleep(500), %% Don't know how to sync this !!!
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ sys:get_status(whereis(mnesia_tm)), % Explicit sync, release locks is async
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+dump_tables(suite) -> [];
+dump_tables(Config) when is_list(Config) ->
+ [ThisNode, Node2] = ?acquire_nodes(2, Config),
+ Tab = dump_t_tab,
+ Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 50),
+ {success, [A]} = ?start_activities([ThisNode]),
+ mnesia_test_lib:start_sync_transactions([A], 0),
+ A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
+ ?match_receive({A, ok}), %% A is executed
+
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, dump_tables,
+ [[Tab]]]),
+
+ ?match_receive(timeout), %% op waits for locks occupied by A
+
+ A ! end_trans, %% Kill A, locks should be released
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
+ Msg -> ?match({Pid, {atomic, ok}}, Msg)
+ after
+ timer:seconds(20) -> ?error("Operation timed out", [])
+ end,
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+ ok.
+
+op(Father, Mod, Fun, Args) ->
+ Res = apply(Mod, Fun, Args),
+ Father ! {self(), Res}.
+
+insert(_Tab, 0) -> ok;
+insert(Tab, N) when N > 0 ->
+ ok = mnesia:sync_dirty(fun() -> mnesia:write({Tab, N, N, 0}) end),
+ insert(Tab, N-1).
+
+
+update_own(Tab, Key, Acc) ->
+ Update =
+ fun() ->
+ Res = mnesia:read({Tab, Key}),
+ case Res of
+ [{Tab, Key, Extra, Acc}] ->
+ mnesia:write({Tab,Key,Extra, Acc+1});
+ Val ->
+ {read, Val, {acc, Acc}}
+ end
+ end,
+ receive
+ {Pid, quit} -> Pid ! {self(), Acc}
+ after
+ 0 ->
+ case mnesia:transaction(Update) of
+ {atomic, ok} ->
+ update_own(Tab, Key, Acc+1);
+ Else ->
+ ?error("Trans failed on ~p with ~p~n"
+ "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n",
+ [node(),
+ Else,
+ mnesia:table_info(Tab, where_to_read),
+ mnesia:table_info(Tab, where_to_write),
+ mnesia:table_info(Tab, where_to_commit),
+ mnesia:table_info(Tab, storage_type)])
+ end
+ end.
+
+update_shared(Tab, Me, Acc) ->
+ Update =
+ fun() ->
+ W2R = mnesia:table_info(Tab, where_to_read),
+ Res = mnesia:read({Tab, 0}),
+ case Res of
+ [{Tab, Key, Extra, Val}] when element(Me, Extra) == Acc ->
+ Extra1 = setelement(Me, Extra, Acc+1),
+ Term = {Tab, Key, Extra1, Val+1},
+ ok = mnesia:write(Term),
+% ?log("At ~p: ~p w2r ~p w2w ~p ~n",
+% [node(), Term,
+% mnesia:table_info(Tab, where_to_read),
+ W2W = mnesia:table_info(Tab, where_to_write),
+ W2C = mnesia:table_info(Tab, where_to_commit),
+%% mnesia:table_info(Tab, storage_type)
+% ]),
+ {_Mod, Tid, Ts} = get(mnesia_activity_state),
+ io:format("~p ~p~n", [Tid, ets:tab2list(element(2,Ts))]),
+ {ok,Term,{W2R,W2W,W2C}};
+ Val ->
+ Info = [{acc, Acc}, {me, Me},
+ {tid, element(2, mnesia:get_activity_id())},
+ {locks, mnesia:system_info(held_locks)}],
+ {read, Val, Info}
+ end
+ end,
+ receive
+ {Pid, quit} -> Pid ! {self(), Acc}
+ after
+ 0 ->
+ case mnesia:transaction(Update) of
+ {atomic, {ok,Term,W2}} ->
+ io:format("~p:~p:(~p,~p) ~w@~w~n", [erlang:now(),node(),Me,Acc,Term,W2]),
+ update_shared(Tab, Me, Acc+1);
+ Else ->
+ ?error("Trans failed on ~p with ~p~n"
+ "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n",
+ [node(),
+ Else,
+ mnesia:table_info(Tab, where_to_read),
+ mnesia:table_info(Tab, where_to_write),
+ mnesia:table_info(Tab, where_to_commit),
+ mnesia:table_info(Tab, storage_type)
+ ])
+ end
+ end.
+
+init_admin(Def, N1, N2, N3) ->
+ Tab = schema_ops,
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert(Tab, 1002),
+
+ Pid1 = spawn_link(N1, ?MODULE, update_own, [Tab, 1, 0]),
+ Pid2 = spawn_link(N2, ?MODULE, update_own, [Tab, 2, 0]),
+ Pid3 = spawn_link(N3, ?MODULE, update_own, [Tab, 3, 0]),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 0, {0,0,0}, 0}) end)),
+
+ Pid4 = spawn_link(N1, ?MODULE, update_shared, [Tab, 1, 0]),
+ Pid5 = spawn_link(N2, ?MODULE, update_shared, [Tab, 2, 0]),
+ Pid6 = spawn_link(N3, ?MODULE, update_shared, [Tab, 3, 0]),
+
+ {Pid1, Pid2, Pid3, Pid4, Pid5, Pid6}.
+
+verify_results({P1, P2, P3, P4, P5, P6}) ->
+ Tab = schema_ops, N1 = node(P1), N2 = node(P2), N3 = node(P3),
+
+ try
+ P1 ! {self(), quit},
+ R1 = receive {P1, Res1} -> Res1 after 9000 -> throw({timeout,P1}) end,
+ P2 ! {self(), quit},
+ R2 = receive {P2, Res2} -> Res2 after 9000 -> throw({timeout,P2}) end,
+ P3 ! {self(), quit},
+ R3 = receive {P3, Res3} -> Res3 after 9000 -> throw({timeout,P3}) end,
+
+ P4 ! {self(), quit},
+ R4 = receive {P4, Res4} -> Res4 after 9000 -> throw({timeout,P4}) end,
+ P5 ! {self(), quit},
+ R5 = receive {P5, Res5} -> Res5 after 9000 -> throw({timeout,P5}) end,
+ P6 ! {self(), quit},
+ R6 = receive {P6, Res6} -> Res6 after 9000 -> throw({timeout,P6}) end,
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write_lock_table(Tab) end)),
+ ?log("Should be ~p~n", [R1]),
+ ?match([{_, _, _, R1}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{_, _, _, R1}], rpc:call(N2, mnesia, dirty_read, [{Tab, 1}])),
+ ?match([{_, _, _, R1}], rpc:call(N3, mnesia, dirty_read, [{Tab, 1}])),
+ ?log("Should be ~p~n", [R2]),
+ ?match([{_, _, _, R2}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])),
+ ?match([{_, _, _, R2}], rpc:call(N2, mnesia, dirty_read, [{Tab, 2}])),
+ ?match([{_, _, _, R2}], rpc:call(N3, mnesia, dirty_read, [{Tab, 2}])),
+ ?log("Should be ~p~n", [R3]),
+ ?match([{_, _, _, R3}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])),
+ ?match([{_, _, _, R3}], rpc:call(N2, mnesia, dirty_read, [{Tab, 3}])),
+ ?match([{_, _, _, R3}], rpc:call(N3, mnesia, dirty_read, [{Tab, 3}])),
+
+ Res = R4+R5+R6,
+ ?log("Should be {~p+~p+~p}= ~p~n", [R4, R5, R6, Res]),
+ ?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N1, mnesia, dirty_read, [{Tab, 0}])),
+ ?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N2, mnesia, dirty_read, [{Tab, 0}])),
+ ?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N3, mnesia, dirty_read, [{Tab, 0}]))
+ catch throw:{timeout, Pid} ->
+ mnesia_lib:dist_coredump(),
+ ?error("Timeout ~p ~n", [Pid])
+ end.
+
+
+get_info(Tab) ->
+ Info = mnesia:table_info(Tab, all),
+ mnesia_lib:verbose("~p~n", [Info]).
+
+del_table_copy_1(suite) -> [];
+del_table_copy_1(Config) when is_list(Config) ->
+ [_Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
+ del_table(Node2, Node2, Nodes). %Called on same Node as deleted
+del_table_copy_2(suite) -> [];
+del_table_copy_2(Config) when is_list(Config) ->
+ [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
+ del_table(Node1, Node2, Nodes). %Called from other Node
+del_table_copy_3(suite) -> [];
+del_table_copy_3(Config) when is_list(Config) ->
+ [_Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ del_table(Node3, Node2, Nodes). %Called from Node w.o. table
+
+%%% The actual test
+del_table(CallFrom, DelNode, [Node1, Node2, Node3]) ->
+ Def = [{ram_copies, [Node1]}, {disc_copies, [Node2]},
+ {attributes, [key, attr1, attr2]}],
+ Tab = schema_ops,
+ Pids = init_admin(Def, Node1, Node2, Node3),
+
+ ?log("Call from ~p delete table from ~p ~n", [CallFrom, DelNode]),
+ rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
+
+ ?match({atomic, ok},
+ rpc:call(CallFrom, mnesia, del_table_copy, [Tab, DelNode])),
+
+ verify_results(Pids),
+ rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
+ ?verify_mnesia([Node1, Node2, Node3], []).
+
+add_table_copy_1(suite) -> [];
+add_table_copy_1(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_only_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node1, Node3, Nodes, Def).
+add_table_copy_2(suite) -> [];
+add_table_copy_2(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_only_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node2, Node3, Nodes, Def).
+add_table_copy_3(suite) -> [];
+add_table_copy_3(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_only_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node3, Node3, Nodes, Def).
+add_table_copy_4(suite) -> [];
+add_table_copy_4(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_only_copies, [Node1]},
+ {attributes, [key, attr1, attr2]}],
+ add_table(Node2, Node3, Nodes, Def).
+%%% The actual test
+add_table(CallFrom, AddNode, [Node1, Node2, Node3], Def) ->
+ Pids = init_admin(Def, Node1, Node2, Node3),
+ Tab = schema_ops,
+ ?log("Call from ~p add table to ~p ~n", [CallFrom, AddNode]),
+ rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
+ ?match({atomic, ok}, rpc:call(CallFrom, mnesia, add_table_copy,
+ [Tab, AddNode, ram_copies])),
+ verify_results(Pids),
+ rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
+ ?verify_mnesia([Node1, Node2, Node3], []).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+move_table_copy_1(suite) -> [];
+move_table_copy_1(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node1, Node1, Node3, Nodes, Def).
+move_table_copy_2(suite) -> [];
+move_table_copy_2(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node2, Node1, Node3, Nodes, Def).
+move_table_copy_3(suite) -> [];
+move_table_copy_3(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_copies, [Node1, Node2]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node3, Node1, Node3, Nodes, Def).
+move_table_copy_4(suite) -> [];
+move_table_copy_4(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Def = [{disc_copies, [Node1]},
+ {attributes, [key, attr1, attr2]}],
+ move_table(Node2, Node1, Node3, Nodes, Def).
+%%% The actual test
+move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) ->
+ Pids = init_admin(Def, Node1, Node2, Node3),
+ Tab = schema_ops,
+ ?log("Call from ~p move table from ~p to ~p ~n", [CallFrom, FromNode, ToNode]),
+ rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
+ ?match({atomic, ok}, rpc:call(CallFrom, mnesia, move_table_copy,
+ [Tab, FromNode, ToNode])),
+ verify_results(Pids),
+ rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]),
+ ?verify_mnesia([Node1, Node2, Node3], []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+dirty_updates_visible_direct(doc) ->
+ ["One process can immediately see dirty updates of another"];
+dirty_updates_visible_direct(suite) -> [];
+dirty_updates_visible_direct(Config) when is_list(Config) ->
+ dirty_visibility(outside_trans, Config).
+
+dirty_reads_regardless_of_trans(doc) ->
+ ["Dirty reads are not affected by transaction context"];
+dirty_reads_regardless_of_trans(suite) -> [];
+dirty_reads_regardless_of_trans(Config) when is_list(Config) ->
+ dirty_visibility(inside_trans, Config).
+
+dirty_visibility(Mode, Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = list_to_atom(lists:concat([dirty_visibility, '_', Mode])),
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab}, {ram_copies, [Node1]}])),
+ ValPos = 3,
+
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ %% Start two processes
+ {success, [A]} = ?start_activities([Node1]),
+
+ case Mode of
+ inside_trans ->
+ ?start_transactions([A]),
+ A ! fun() ->
+ mnesia:write({Tab, a, 11}),
+ mnesia:write({Tab, b, 22}),
+ mnesia:write({Tab, c, 1}),
+ mnesia:write({Tab, d, 2}),
+ mnesia:write({Tab, e, 3}),
+ lists:sort(mnesia:all_keys(Tab))
+ end,
+ ?match_receive({A, [a, b, c, d, e]});
+ outside_trans ->
+ ignore
+ end,
+
+ RecA = {Tab, a, 1},
+ PatA = {Tab, '$1', 1},
+ RecB = {Tab, b, 3},
+ PatB = {Tab, '$1', 3},
+ RecB2 = {Tab, b, 2},
+ PatB2 = {Tab, '$1', 2},
+ ?match([], mnesia:dirty_read({Tab, a})),
+ ?match([], mnesia:dirty_read({Tab, b})),
+ ?match([], mnesia:dirty_match_object(PatA)),
+ ?match([], mnesia:dirty_match_object(PatB)),
+ ?match([], mnesia:dirty_match_object(PatB2)),
+ ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)),
+ ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatA, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatB, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatB2, ValPos)),
+ ?match('$end_of_table', mnesia:dirty_first(Tab)),
+
+ %% dirty_write
+ A ! fun() -> mnesia:dirty_write(RecA) end,
+ ?match_receive({A, ok}),
+ ?match([RecA], mnesia:dirty_read({Tab, a})),
+ ?match([RecA], mnesia:dirty_match_object(PatA)),
+ ?match(a, mnesia:dirty_first(Tab)),
+ ?match([RecA], mnesia:dirty_index_read(Tab, 1, ValPos)),
+ ?match([RecA], mnesia:dirty_index_match_object(PatA, ValPos)),
+ ?match('$end_of_table', mnesia:dirty_next(Tab, a)),
+
+ %% dirty_create
+ A ! fun() -> mnesia:dirty_write(RecB) end,
+ ?match_receive({A, ok}),
+ ?match([RecB], mnesia:dirty_read({Tab, b})),
+ ?match([RecB], mnesia:dirty_match_object(PatB)),
+ ?match([RecB], mnesia:dirty_index_read(Tab, 3, ValPos)),
+ ?match([RecB], mnesia:dirty_index_match_object(PatB, ValPos)),
+ ?match('$end_of_table',
+ mnesia:dirty_next(Tab, mnesia:dirty_next(Tab, mnesia:dirty_first(Tab)))),
+
+ %% dirty_update_counter
+ A ! fun() -> mnesia:dirty_update_counter({Tab, b}, -1) end,
+ ?match_receive({A, _}),
+ ?match([RecB2], mnesia:dirty_read({Tab, b})),
+ ?match([], mnesia:dirty_match_object(PatB)),
+ ?match([RecB2], mnesia:dirty_match_object(PatB2)),
+ ?match([RecB2], mnesia:dirty_index_read(Tab, 2, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatB, ValPos)),
+ ?match([RecB2], mnesia:dirty_index_match_object(PatB2, ValPos)),
+ ?match('$end_of_table',
+ mnesia:dirty_next(Tab, mnesia:dirty_next(Tab, mnesia:dirty_first(Tab)))),
+
+ %% dirty_delete
+ A ! fun() -> mnesia:dirty_delete({Tab, b}) end,
+ ?match_receive({A, ok}),
+ ?match([], mnesia:dirty_read({Tab, b})),
+ ?match([], mnesia:dirty_match_object(PatB2)),
+ ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatB2, ValPos)),
+ ?match(a, mnesia:dirty_first(Tab)),
+ ?match('$end_of_table', mnesia:dirty_next(Tab, a)),
+
+ %% dirty_delete_object
+ ?match([RecA], mnesia:dirty_match_object(PatA)),
+ A ! fun() -> mnesia:dirty_delete_object(RecA) end,
+ ?match_receive({A, ok}),
+ ?match([], mnesia:dirty_read({Tab, a})),
+ ?match([], mnesia:dirty_match_object(PatA)),
+ ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatA, ValPos)),
+ ?match('$end_of_table', mnesia:dirty_first(Tab)),
+
+ case Mode of
+ inside_trans ->
+ A ! end_trans,
+ ?match_receive({A, {atomic, end_trans}});
+ outside_trans ->
+ ignore
+ end,
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+trans_update_invisibible_outside_trans(doc) ->
+ ["Updates in a transaction are invisible outside the transaction"];
+trans_update_invisibible_outside_trans(suite) -> [];
+trans_update_invisibible_outside_trans(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = trans_update_invisibible_outside_trans,
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]}])),
+ ValPos = 3,
+ RecA = {Tab, a, 1},
+ PatA = {Tab, '$1', 1},
+ RecB = {Tab, b, 3},
+ PatB = {Tab, '$1', 3},
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Verify =
+ fun() ->
+ ?match([], mnesia:dirty_read({Tab, a})),
+ ?match([], mnesia:dirty_read({Tab, b})),
+ ?match([], mnesia:dirty_match_object(PatA)),
+ ?match([], mnesia:dirty_match_object(PatB)),
+ ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)),
+ ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatA, ValPos)),
+ ?match([], mnesia:dirty_index_match_object(PatB, ValPos)),
+ ?match('$end_of_table', mnesia:dirty_first(Tab))
+ end,
+
+ Fun = fun() ->
+ ?match(ok, mnesia:write(RecA)),
+ Verify(),
+
+ ?match(ok, mnesia:write(RecB)),
+ Verify(),
+
+ ?match(ok, mnesia:delete({Tab, b})),
+ Verify(),
+
+ ?match([RecA], mnesia:match_object(PatA)),
+ Verify(),
+
+ ?match(ok, mnesia:delete_object(RecA)),
+ Verify(),
+ ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun)),
+ Verify(),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+trans_update_visible_inside_trans(doc) ->
+ ["Updates in a transaction are visible in the same transaction"];
+trans_update_visible_inside_trans(suite) -> [];
+trans_update_visible_inside_trans(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = trans_update_visible_inside_trans,
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]}])),
+ ValPos = 3,
+ RecA = {Tab, a, 1},
+ PatA = {Tab, '$1', 1},
+ RecB = {Tab, b, 3},
+ PatB = {Tab, '$1', 3},
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Fun = fun() ->
+ %% write
+ ?match(ok, mnesia:write(RecA)),
+ ?match([RecA], mnesia:read({Tab, a})),
+ ?match([RecA], mnesia:wread({Tab, a})),
+ ?match([RecA], mnesia:match_object(PatA)),
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([RecA], mnesia:index_match_object(PatA, ValPos)),
+ ?match([RecA], mnesia:index_read(Tab, 1, ValPos)),
+
+ %% create
+ ?match(ok, mnesia:write(RecB)),
+ ?match([RecB], mnesia:read({Tab, b})),
+ ?match([RecB], mnesia:wread({Tab, b})),
+ ?match([RecB], mnesia:match_object(PatB)),
+ ?match([RecB], mnesia:index_match_object(PatB, ValPos)),
+ ?match([RecB], mnesia:index_read(Tab, 3, ValPos)),
+
+ %% delete
+ ?match(ok, mnesia:delete({Tab, b})),
+ ?match([], mnesia:read({Tab, b})),
+ ?match([], mnesia:wread({Tab, b})),
+ ?match([], mnesia:match_object(PatB)),
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([], mnesia:index_match_object(PatB, ValPos)),
+ ?match([], mnesia:index_read(Tab, 2, ValPos)),
+ ?match([], mnesia:index_read(Tab, 3, ValPos)),
+
+ %% delete_object
+ ?match(ok, mnesia:delete_object(RecA)),
+ ?match([], mnesia:read({Tab, a})),
+ ?match([], mnesia:wread({Tab, a})),
+ ?match([], mnesia:match_object(PatA)),
+ ?match([], mnesia:all_keys(Tab)),
+ ?match([], mnesia:index_match_object(PatA, ValPos)),
+ ?match([], mnesia:index_read(Tab, 2, ValPos)),
+ ?match([], mnesia:index_read(Tab, 3, ValPos)),
+ ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+write_shadows(doc) ->
+ ["Tests whether the shadow shows the correct object when",
+ "writing to the table"];
+write_shadows(suite) -> [];
+write_shadows(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = write_shadows,
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]},
+ {type, set}])),
+ ValPos = 3,
+ RecA1 = {Tab, a, 1},
+ PatA1 = {Tab, '$1', 1},
+ RecA2 = {Tab, a, 2},
+ PatA2 = {Tab, '$1', 2},
+
+
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Fun1 = fun() ->
+ ?match(ok, mnesia:write(RecA1)),
+ ok
+ end,
+
+ ?match({atomic, ok}, mnesia:transaction(Fun1)),
+
+ Fun2 = fun() ->
+ %% write shadow old write - is the confirmed value visable
+ %% in the shadow ?
+ ?match([RecA1], mnesia:read({Tab, a})),
+ ?match([RecA1], mnesia:wread({Tab, a})),
+ ?match([RecA1], mnesia:match_object(PatA1)),
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([RecA1], mnesia:index_match_object(PatA1, ValPos)),
+ ?match([RecA1], mnesia:index_read(Tab, 1, ValPos)),
+
+ %% write shadow new write - is a new value visable instead
+ %% of the old value ?
+ ?match(ok, mnesia:write(RecA2)),
+
+ ?match([RecA2], mnesia:read({Tab, a})),
+ ?match([RecA2], mnesia:wread({Tab, a})),
+ ?match([RecA2], mnesia:match_object(PatA2)), %% delete shadow old but not new write - is the new value visable
+
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)),
+ ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)),
+ ok
+
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun2)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+delete_shadows(doc) ->
+ ["Test whether the shadow shows the correct object when deleting objects"];
+delete_shadows(suite) -> [];
+delete_shadows(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = delete_shadows,
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]},
+ {type, set}])),
+ ValPos = 3,
+ OidA = {Tab, a},
+ RecA1 = {Tab, a, 1},
+ PatA1 = {Tab, '$1', 1},
+ RecA2 = {Tab, a, 2},
+ PatA2 = {Tab, '$1', 2},
+
+
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Fun1 = fun() ->
+ ?match(ok, mnesia:write(RecA1)),
+ ok
+ end,
+
+ ?match({atomic, ok}, mnesia:transaction(Fun1)),
+
+ Fun2 = fun() ->
+
+
+ %% delete shadow old write - is the confirmed value invisible
+ %% when deleted in the transaction ?
+ ?match(ok, mnesia:delete(OidA)),
+
+ ?match([], mnesia:read({Tab, a})),
+ ?match([], mnesia:wread({Tab, a})),
+ ?match([], mnesia:match_object(PatA1)),
+ ?match([], mnesia:all_keys(Tab)),
+ ?match([], mnesia:index_match_object(PatA1, ValPos)),
+ ?match([], mnesia:index_read(Tab, 1, ValPos)),
+
+ %% delete shadow old but not new write - is the new value visable
+ %% when the old one was deleted ?
+ ?match(ok, mnesia:write(RecA2)),
+
+ ?match([RecA2], mnesia:read({Tab, a})),
+ ?match([RecA2], mnesia:wread({Tab, a})),
+ ?match([RecA2], mnesia:match_object(PatA2)),
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)),
+ ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)),
+
+ %% delete shadow old and new write - is the new value invisable
+ %% when deleted ?
+ ?match(ok, mnesia:delete(OidA)),
+
+ ?match([], mnesia:read({Tab, a})),
+ ?match([], mnesia:wread({Tab, a})),
+ ?match([], mnesia:match_object(PatA2)),
+ ?match([], mnesia:all_keys(Tab)),
+ ?match([], mnesia:index_match_object(PatA2, ValPos)),
+ ?match([], mnesia:index_read(Tab, 2, ValPos)),
+ ok
+
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun2)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+write_delete_shadows_bag(doc) ->
+ ["Test the visibility of written and deleted objects in an bag type table"];
+write_delete_shadows_bag(suite) -> [];
+write_delete_shadows_bag(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = write_delete_shadows_bag,
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]},
+ {type, bag}])),
+ ValPos = 3,
+ OidA = {Tab, a},
+
+ RecA1 = {Tab, a, 1},
+ PatA1 = {Tab, '$1', 1},
+
+ RecA2 = {Tab, a, 2},
+ PatA2 = {Tab, '$1', 2},
+
+ RecA3 = {Tab, a, 3},
+ PatA3 = {Tab, '$1', 3},
+
+ PatA = {Tab, a, '_'},
+
+
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Fun1 = fun() ->
+ ?match(ok, mnesia:write(RecA1)),
+ ?match(ok, mnesia:write(RecA2)),
+ ok
+ end,
+
+ ?match({atomic, ok}, mnesia:transaction(Fun1)),
+
+ Fun2 = fun() ->
+ %% delete shadow old write - is the confirmed value invisible
+ %% when deleted in the transaction ?
+ ?match(ok, mnesia:delete_object(RecA1)),
+
+ ?match([RecA2], mnesia:read({Tab, a})),
+ ?match([RecA2], mnesia:wread({Tab, a})),
+ ?match([RecA2], mnesia:match_object(PatA2)),
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)),
+ ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)),
+
+ ?match(ok, mnesia:delete(OidA)),
+
+ ?match([], mnesia:read({Tab, a})),
+ ?match([], mnesia:wread({Tab, a})),
+ ?match([], mnesia:match_object(PatA1)),
+ ?match([], mnesia:all_keys(Tab)),
+ ?match([], mnesia:index_match_object(PatA1, ValPos)),
+ ?match([], mnesia:index_read(Tab, 1, ValPos)),
+
+ %% delete shadow old but not new write - are both new value visable
+ %% when the old one was deleted ?
+ ?match(ok, mnesia:write(RecA2)),
+ ?match(ok, mnesia:write(RecA3)),
+
+
+ ?match([RecA2, RecA3], lists:sort(mnesia:read({Tab, a}))),
+ ?match([RecA2, RecA3], lists:sort(mnesia:wread({Tab, a}))),
+ ?match([RecA2], mnesia:match_object(PatA2)),
+ ?match([a], mnesia:all_keys(Tab)),
+ ?match([RecA2, RecA3], lists:sort(mnesia:match_object(PatA))),
+ ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)),
+ ?match([RecA3], mnesia:index_match_object(PatA3, ValPos)),
+ ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)),
+
+ %% delete shadow old and new write - is the new value invisable
+ %% when deleted ?
+ ?match(ok, mnesia:delete(OidA)),
+
+ ?match([], mnesia:read({Tab, a})),
+ ?match([], mnesia:wread({Tab, a})),
+ ?match([], mnesia:match_object(PatA2)),
+ ?match([], mnesia:all_keys(Tab)),
+ ?match([], mnesia:index_match_object(PatA2, ValPos)),
+ ?match([], mnesia:index_read(Tab, 2, ValPos)),
+ ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun2)),
+ ok.
+
+write_delete_shadows_bag2(doc) ->
+ ["Test the visibility of written and deleted objects in an bag type table "
+ "and verifies the results"];
+write_delete_shadows_bag2(suite) -> [];
+write_delete_shadows_bag2(Config) when is_list(Config) ->
+
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab = w_d_s_b,
+
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab},
+ {ram_copies, [Node1]},
+ {type, bag}])),
+ Del = fun() ->
+ R1 = mnesia:read({Tab, 1}),
+ mnesia:delete({Tab, 1}),
+ R2 = mnesia:read({Tab, 1}),
+ mnesia:write({Tab, 1, 1}),
+ mnesia:write({Tab, 1, 2}),
+ R3 = mnesia:read({Tab, 1}),
+ {R1, R2, R3}
+ end,
+ DelObj = fun() ->
+ R1 = mnesia:read({Tab, 2}),
+ mnesia:delete_object({Tab, 2, 2}),
+ R2 = mnesia:read({Tab, 2}),
+ mnesia:write({Tab, 2, 1}),
+ mnesia:write({Tab, 2, 2}),
+ R3 = mnesia:read({Tab, 2}),
+ {R1, R2, R3}
+ end,
+ Both1 = [{Tab, 1, 1}, {Tab, 1, 2}],
+ Both2 = [{Tab, 2, 1}, {Tab, 2, 2}],
+ ?match({atomic, {[], [], Both1}}, mnesia:transaction(Del)),
+ ?match({atomic, {Both1, [], Both1}}, mnesia:transaction(Del)),
+ ?match({atomic, Both1}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+ ?match({atomic, {[], [], Both2}}, mnesia:transaction(DelObj)),
+ ?match({atomic, {Both2, [{Tab, 2, 1}], Both2}}, mnesia:transaction(DelObj)),
+ ?match({atomic, Both2}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)),
+ ?verify_mnesia([Node1], []).
+
+shadow_search(doc) ->
+ ["Verifies that ordered_set tables are ordered, and the order is kept"
+ "even when table is shadowed by transaction updates"];
+shadow_search(suite) -> [];
+shadow_search(Config) when is_list(Config) ->
+ [Node1] = ?acquire_nodes(1, Config),
+ Tab1 = ss_oset,
+ Tab2 = ss_set,
+ Tab3 = ss_bag,
+ Tabs = [Tab1,Tab2,Tab3],
+ RecName = ss,
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab1},
+ {ram_copies, [Node1]},
+ {record_name, RecName},
+ {type, ordered_set}])),
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab2},
+ {record_name, RecName},
+ {ram_copies, [Node1]},
+ {type, set}])),
+ ?match({atomic, ok}, mnesia:create_table([{name, Tab3},
+ {record_name, RecName},
+ {ram_copies, [Node1]},
+ {type, bag}])),
+ Recs = [{RecName, K, K} || K <- [1,3,5]],
+ [mnesia:dirty_write(Tab1, R) || R <- Recs],
+ [mnesia:dirty_write(Tab2, R) || R <- Recs],
+ [mnesia:dirty_write(Tab3, R) || R <- Recs],
+
+ Match = fun(Tab) -> mnesia:match_object(Tab, {'_','_','_'}, write) end,
+ Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end,
+% Trans = fun(Fun,Args) -> mnesia:transaction(Fun,Args) end,
+ LoopHelp = fun('$end_of_table',_) -> [];
+ ({Res,Cont},Fun) ->
+ Sel = mnesia:select(Cont),
+ Res ++ Fun(Sel, Fun)
+ end,
+ SelLoop = fun(Table) ->
+ Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read),
+ LoopHelp(Sel,LoopHelp)
+ end,
+
+ R1 = {RecName, 2, 2}, R2 = {RecName, 4, 4},
+ R3 = {RecName, 2, 3}, R4 = {RecName, 3, 1},
+ R5 = {RecName, 104, 104},
+ W1 = fun(Tab,Search) -> mnesia:write(Tab,R1,write),
+ mnesia:write(Tab,R2,write),
+ Search(Tab)
+ end,
+ S1 = lists:sort([R1,R2|Recs]),
+ ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,Select])),
+ ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,Match])),
+ ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,SelLoop])),
+ ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,Select]))),
+ ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,SelLoop]))),
+ ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,Match]))),
+ ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,Select]))),
+ ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,SelLoop]))),
+ ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,Match]))),
+ [mnesia:dirty_delete_object(Tab,R) || R <- [R1,R2], Tab <- Tabs],
+
+ W2 = fun(Tab,Search) ->
+ mnesia:write(Tab,R3,write),
+ mnesia:write(Tab,R1,write),
+ Search(Tab)
+ end,
+ S2 = lists:sort([R1|Recs]),
+ S2Bag = lists:sort([R1,R3|Recs]),
+ ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,Select])),
+ ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,SelLoop])),
+ ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,Match])),
+ ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,Select]))),
+ ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,SelLoop]))),
+ ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,Match]))),
+ ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,Select]))),
+ ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,SelLoop]))),
+ ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,Match]))),
+%% [mnesia:dirty_delete_object(Tab,R) || R <- [R1,R3], Tab <- Tabs],
+
+ W3 = fun(Tab,Search) ->
+ mnesia:write(Tab,R4,write),
+ mnesia:delete(Tab,element(2,R1),write),
+ Search(Tab)
+ end,
+ S3Bag = lists:sort([R4|lists:delete(R1,Recs)]),
+ S3 = lists:delete({RecName,3,3},S3Bag),
+ ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,Select])),
+ ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,SelLoop])),
+ ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,Match])),
+ ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,SelLoop]))),
+ ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,Select]))),
+ ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,Match]))),
+ ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,Select]))),
+ ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,SelLoop]))),
+ ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,Match]))),
+
+ W4 = fun(Tab,Search) ->
+ mnesia:delete(Tab,-1,write),
+ mnesia:delete(Tab,4 ,write),
+ mnesia:delete(Tab,17,write),
+ mnesia:delete_object(Tab,{RecName, -1, x},write),
+ mnesia:delete_object(Tab,{RecName, 4, x},write),
+ mnesia:delete_object(Tab,{RecName, 42, x},write),
+ mnesia:delete_object(Tab,R2,write),
+ mnesia:write(Tab, R5, write),
+ Search(Tab)
+ end,
+ S4Bag = lists:sort([R5|S3Bag]),
+ S4 = lists:sort([R5|S3]),
+ ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,Select])),
+ ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,SelLoop])),
+ ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,Match])),
+ ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,Select]))),
+ ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,SelLoop]))),
+ ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,Match]))),
+ ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,Select]))),
+ ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,SelLoop]))),
+ ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,Match]))),
+ [mnesia:dirty_delete_object(Tab,R) || R <- [{RecName,3,3},R5], Tab <- Tabs],
+
+ %% hmmm anything more??
+
+ ?verify_mnesia([Node1], []).
+
+
+rr_kill_copy(suite) -> [];
+rr_kill_copy(Config) when is_list(Config) ->
+ Ns = ?acquire_nodes(3,Config ++ [{tc_timeout, 60000}]),
+ DeleteMe = fun(_Tab,Where2read) ->
+ ?match([], mnesia_test_lib:kill_mnesia([Where2read]))
+ end,
+ Del = removed_resources(Ns, DeleteMe),
+ ?verify_mnesia(Ns -- [Del], []).
+
+removed_resources([_N1,N2,N3], DeleteRes) ->
+ Tab = del_res,
+ ?match({atomic, ok}, mnesia:create_table(Tab,[{ram_copies, [N2,N3]}])),
+
+ Init = fun() -> [mnesia:write({Tab,Key,Key}) || Key <- lists:seq(0,99)] end,
+ ?match([], [Bad || Bad <- mnesia:sync_dirty(Init), Bad /= ok]),
+
+ Where2Read = mnesia:table_info(Tab, where_to_read),
+ [Keep] = [N2,N3] -- [Where2Read],
+ Tester = self(),
+
+ Conflict = fun() ->
+ %% Read a value..
+ [{Tab,1,Val}] = mnesia:read({Tab,1}),
+ case get(restart) of
+ undefined ->
+ Tester ! {pid_1, self()},
+ %% Wait for sync, the read value have been
+ %% updated and this function should be restarted.
+ receive {Tester,sync} -> ok end,
+ put(restart, restarted);
+ restarted ->
+ ok
+ end,
+ mnesia:write({Tab,1,Val+10})
+ end,
+ Lucky = fun() ->
+ [{Tab,1,Val}] = mnesia:read({Tab,1}),
+ mnesia:write({Tab,1,Val+100})
+ end,
+
+ CPid = spawn_link(fun() -> Tester ! {self(), mnesia:transaction(Conflict)} end),
+
+ %% sync first transaction
+ receive {pid_1, CPid} -> synced end,
+
+ DeleteRes(Tab, Where2Read),
+
+ ?match(Keep, mnesia:table_info(Tab, where_to_read)),
+
+ %% Run the other/Lucky transaction, this should work since
+ %% it won't grab a lock on the conflicting transactions Where2Read node.
+
+ LPid = spawn_link(Keep, fun() -> Tester ! {self(),mnesia:transaction(Lucky)} end),
+ ?match_receive({LPid,{atomic,ok}}),
+
+ %% Continue Transaction no 1
+ CPid ! {self(), sync},
+
+ ?match(ok, receive {CPid,{atomic,ok}} -> ok after 2000 -> process_info(self()) end),
+
+ ?match({atomic,[{del_res,1,111}]}, mnesia:transaction(fun() -> mnesia:read({Tab,1}) end)),
+ Where2Read.
+
+nasty(suite) -> [];
+
+nasty(doc) ->
+ ["Tries to fullfill a rather nasty locking scenario, where we have had "
+ "bugs, the testcase tries a combination of locks in locker queue"];
+
+%% This testcase no longer works as it was intended to show errors when
+%% tablelocks was allowed to be placed in the queue though locks existed
+%% in the queue with less Tid's. This is no longer allowed and the testcase
+%% has been update.
+
+nasty(Config) ->
+ ?acquire_nodes(1, Config),
+ Tab = nasty,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [])),
+ Coord = self(),
+ Write = fun(Key) ->
+ mnesia:write({Tab, Key, write}),
+ Coord ! {write, Key, self(), mnesia:get_activity_id()},
+ receive
+ continue ->
+ ok
+ end,
+ Coord ! {done, {write, Key}, self()}
+ end,
+
+ Update = fun(Key) ->
+ Coord ! {update, Key, self(), mnesia:get_activity_id()},
+ receive
+ continue ->
+ ok
+ end,
+ mnesia:read({Tab, Key}),
+ mnesia:write({Tab, Key, update}),
+ receive
+ continue ->
+ ok
+ end,
+
+ Coord ! {done, {update, Key}, self()}
+ end,
+
+ TabLock = fun() ->
+ Coord ! {tablock, Tab, self(), mnesia:get_activity_id()},
+ receive
+ continue ->
+ ok
+ end,
+ mnesia:lock({table, Tab}, write),
+ Coord ! {done, {tablock, Tab}, self()}
+ end,
+
+ Up = spawn_link(mnesia, transaction, [Update, [0]]),
+ ?match_receive({update, 0, Up, _Tid}),
+ TL = spawn_link(mnesia, transaction, [TabLock]),
+ ?match_receive({tablock, Tab, _Tl, _Tid}),
+ W0 = spawn_link(mnesia, transaction, [Write, [0]]),
+ ?match_receive({write, 0, W0, _Tid}),
+ W1 = spawn_link(mnesia, transaction, [Write, [1]]),
+ ?match_receive({write, 1, W1, _Tid}),
+
+ %% Nothing should be in msg queue!
+ ?match(timeout, receive A -> A after 1000 -> timeout end),
+ Up ! continue, %% Should be queued
+ ?match(timeout, receive A -> A after 1000 -> timeout end),
+ TL ! continue, %% Should be restarted
+% ?match({tablock, _, _, _}, receive A -> A after 1000 -> timeout end),
+ ?match(timeout, receive A -> A after 1000 -> timeout end),
+
+ LQ1 = mnesia_locker:get_lock_queue(),
+ ?match({2, _}, {length(LQ1), LQ1}),
+ W0 ! continue, % Up should be in queue
+ ?match_receive({done, {write, 0}, W0}),
+ ?match_receive({'EXIT', W0, normal}),
+
+ TL ! continue, % Should stay in queue W1
+ ?match(timeout, receive A -> A after 1000 -> timeout end),
+ Up ! continue, % Should stay in queue (TL got higher tid)
+ ?match(timeout, receive A -> A after 1000 -> timeout end),
+
+ LQ2 = mnesia_locker:get_lock_queue(),
+ ?match({2, _}, {length(LQ2), LQ2}),
+
+ W1 ! continue,
+ ?match_receive({done, {write, 1}, W1}),
+ get_exit(W1),
+ get_exit(TL),
+ ?match_receive({done, {tablock,Tab}, TL}),
+ get_exit(Up),
+ ?match_receive({done, {update, 0}, Up}),
+
+ ok.
+
+get_exit(Pid) ->
+ receive
+ {'EXIT', Pid, normal} ->
+ ok
+ after 10000 ->
+ ?error("Timeout EXIT ~p~n", [Pid])
+ end.
+
+
+foldl(doc) ->
+ [""];
+foldl(suite) ->
+ [];
+foldl(Config) when is_list(Config) ->
+ Nodes = [_,N2] = ?acquire_nodes(2, Config),
+ Tab1 = foldl_local,
+ Tab2 = foldl_remote,
+ Tab3 = foldl_ordered,
+ Tab11 = foldr_local,
+ Tab21 = foldr_remote,
+ Tab31 = foldr_ordered,
+ ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}, {type, bag}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes},
+ {type, ordered_set}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab11, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab21, [{ram_copies, [N2]}, {type, bag}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab31, [{ram_copies, Nodes},
+ {type, ordered_set}])),
+
+
+ Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 10)],
+ Tab2Els = [{Tab2, 1, 2} | [{Tab2, N, N} || N <- lists:seq(1, 10)]],
+ Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 10)],
+ Tab11Els = [{Tab11, N, N} || N <- lists:seq(1, 10)],
+ Tab21Els = [{Tab21, 1, 2} | [{Tab21, N, N} || N <- lists:seq(1, 10)]],
+ Tab31Els = [{Tab31, N, N} || N <- lists:seq(1, 10)],
+
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab1Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab2Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab3Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab11Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab21Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab31Els],
+
+ Get = fun(E, A) -> [E | A] end,
+
+ %% Before
+ AddB = fun(Tab, Func) ->
+ mnesia:write({Tab, 0, 0}),
+ mnesia:write({Tab, 1, 0}),
+ mnesia:write({Tab, 11, 0}),
+ mnesia:Func(Get, [], Tab)
+ end,
+ AddT1 = [{Tab1, 0, 0}, {Tab1, 1, 0}] ++ tl(Tab1Els) ++ [{Tab1, 11, 0}],
+ AddT2 = lists:sort([{Tab2, 0, 0}, {Tab2, 1, 0}] ++ Tab2Els ++ [{Tab2, 11, 0}]),
+ AddT3 = [{Tab3, 0, 0}, {Tab3, 1, 0}] ++ tl(Tab3Els) ++ [{Tab3, 11, 0}],
+ AddT11 = [{Tab11, 0, 0}, {Tab11, 1, 0}] ++ tl(Tab11Els) ++ [{Tab11, 11, 0}],
+ AddT21 = lists:sort([{Tab21, 0, 0}, {Tab21, 1, 0}] ++ Tab21Els ++ [{Tab21, 11, 0}]),
+ AddT31 = [{Tab31, 0, 0}, {Tab31, 1, 0}] ++ tl(Tab31Els) ++ [{Tab31, 11, 0}],
+
+ ?match({atomic, AddT1}, sort_res(mnesia:transaction(AddB, [Tab1, foldl]))),
+ ?match({atomic, AddT2}, sort_res(mnesia:transaction(AddB, [Tab2, foldl]))),
+ ?match({atomic, AddT3}, rev_res(mnesia:transaction(AddB, [Tab3, foldl]))),
+ ?match({atomic, AddT11}, sort_res(mnesia:transaction(AddB, [Tab11, foldr]))),
+ ?match({atomic, AddT21}, sort_res(mnesia:transaction(AddB, [Tab21, foldr]))),
+ ?match({atomic, AddT31}, mnesia:transaction(AddB, [Tab31, foldr])),
+
+ ?match({atomic, ok}, mnesia:create_table(copy, [{ram_copies, [N2]},
+ {record_name, Tab1}])),
+ CopyRec = fun(NewRec, Acc) ->
+ %% OTP-5495
+ W = fun() -> mnesia:write(copy, NewRec, write), [NewRec| Acc] end,
+ {atomic,Res} = sort_res(mnesia:transaction(W)),
+ Res
+ end,
+ Copy = fun() ->
+ AddT1 = mnesia:foldl(CopyRec, [], Tab1),
+ AddT1 = sort_res(mnesia:foldl(Get, [], copy))
+ end,
+ ?match({atomic, AddT1}, sort_res(mnesia:transaction(Copy))),
+
+ Del = fun(E, A) -> mnesia:delete_object(E), [E|A] end,
+ DelD = fun(Tab) ->
+ mnesia:write({Tab, 12, 12}),
+ mnesia:delete({Tab, 0}),
+ mnesia:foldr(Del, [], Tab),
+ mnesia:foldl(Get, [], Tab)
+ end,
+ ?match({atomic, []}, sort_res(mnesia:transaction(DelD, [Tab1]))),
+ ?match({atomic, []}, sort_res(mnesia:transaction(DelD, [Tab2]))),
+ ?match({atomic, []}, rev_res(mnesia:transaction(DelD, [Tab3]))),
+
+ ListWrite = fun(Tab) -> %% OTP-3893
+ mnesia:write({Tab, [12], 12}),
+ mnesia:foldr(Get, [], Tab)
+ end,
+ ?match({atomic, [{Tab1, [12], 12}]}, sort_res(mnesia:transaction(ListWrite, [Tab1]))),
+ ?match({atomic, [{Tab2, [12], 12}]}, sort_res(mnesia:transaction(ListWrite, [Tab2]))),
+ ?match({atomic, [{Tab3, [12], 12}]}, rev_res(mnesia:transaction(ListWrite, [Tab3]))),
+
+ ?verify_mnesia(Nodes, []).
+
+sort_res({atomic, List}) when is_list(List) ->
+ {atomic, lists:sort(List)};
+sort_res(Else) when is_list(Else) ->
+ lists:sort(Else);
+sort_res(Else) ->
+ Else.
+
+rev_res({atomic, List}) ->
+ {atomic, lists:reverse(List)};
+rev_res(Else) ->
+ Else.
+
+
+first_next(doc) -> [""];
+first_next(suite) -> [];
+first_next(Config) when is_list(Config) ->
+ Nodes = [_,N2] = ?acquire_nodes(2, Config),
+ Tab1 = local,
+ Tab2 = remote,
+ Tab3 = ordered,
+ Tab4 = bag,
+ Tabs = [Tab1,Tab2,Tab3,Tab4],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes},
+ {type, ordered_set}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab4, [{ram_copies, Nodes},
+ {type, bag}])),
+
+ %% Some Helpers
+ Trans = fun(Fun) -> mnesia:transaction(Fun) end,
+ Continue = fun(first) -> next;
+ (last) -> prev
+ end,
+ LoopHelp = fun('$end_of_table',_,_,_Fun) -> [];
+ (Key,Tab,Op,Fun) ->
+ Next = mnesia:Op(Tab,Key),
+ [Next |Fun(Next,Tab,Op,Fun)]
+ end,
+ Loop = fun(Tab,Start) ->
+ First = mnesia:Start(Tab),
+ Res = [First|LoopHelp(First,Tab,Continue(Start),LoopHelp)],
+ case mnesia:table_info(Tab, type) of
+ ordered_set when Start == first -> Res;
+ ordered_set ->
+ {L1,L2} = lists:split(length(Res)-1,Res),
+ lists:reverse(L1) ++ L2;
+ _ -> lists:sort(Res)
+ end
+ end,
+
+ %% Verify empty tables
+ [?match({atomic, ['$end_of_table']},
+ Trans(fun() -> Loop(Tab,first) end))
+ || Tab <- Tabs],
+ [?match({atomic, ['$end_of_table']},
+ Trans(fun() -> Loop(Tab,last) end))
+ || Tab <- Tabs],
+ %% Verify that trans write is visible inside trans
+ [?match({atomic, [0,10,'$end_of_table']},
+ Trans(fun() ->
+ mnesia:write({Tab,0,0}),
+ mnesia:write({Tab,10,10}),
+ Loop(Tab,first) end))
+ || Tab <- Tabs],
+ [?match({atomic, ['$end_of_table']},
+ Trans(fun() ->
+ mnesia:delete({Tab,0}),
+ mnesia:delete({Tab,10}),
+ Loop(Tab,first) end))
+ || Tab <- Tabs],
+
+ [?match({atomic, [0,10,'$end_of_table']},
+ Trans(fun() ->
+ mnesia:write({Tab,0,0}),
+ mnesia:write({Tab,10,10}),
+ Loop(Tab,last) end))
+ || Tab <- Tabs],
+ [?match({atomic, ['$end_of_table']},
+ Trans(fun() ->
+ mnesia:delete({Tab,0}),
+ mnesia:delete({Tab,10}),
+ Loop(Tab,last) end))
+ || Tab <- Tabs],
+
+ Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 5)],
+ Tab2Els = [{Tab2, N, N} || N <- lists:seq(1, 5)],
+ Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 5)],
+ Tab4Els = [{Tab4, 1, 2} | [{Tab4, N, N} || N <- lists:seq(1, 5)]],
+
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab1Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab2Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab3Els],
+ [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab4Els],
+ Keys = lists:sort(mnesia:dirty_all_keys(Tab1)),
+ R1 = Keys++ ['$end_of_table'],
+ [?match({atomic, R1}, Trans(fun() -> Loop(Tab,first) end))
+ || Tab <- Tabs],
+
+ [?match({atomic, R1}, Trans(fun() -> Loop(Tab,last) end))
+ || Tab <- Tabs],
+ R2 = R1 -- [3],
+
+ [?match({atomic, R2}, Trans(fun() -> mnesia:delete({Tab,3}),Loop(Tab,first) end))
+ || Tab <- Tabs],
+ [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,3,3}),Loop(Tab,first) end))
+ || Tab <- Tabs],
+ [?match({atomic, R2}, Trans(fun() -> mnesia:delete({Tab,3}),Loop(Tab,last) end))
+ || Tab <- Tabs],
+ [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,3,3}),Loop(Tab,last) end))
+ || Tab <- Tabs],
+ [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,4,19}),Loop(Tab,first) end))
+ || Tab <- Tabs],
+ [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,4,4}),Loop(Tab,last) end))
+ || Tab <- Tabs],
+
+ ?verify_mnesia(Nodes, []).
+
+
+snmp_shadows(doc) -> [""];
+snmp_shadows(suite) -> [];
+snmp_shadows(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ Tab = snmp_shadows,
+ io:format("With fixstring~n", []),
+ ?match({atomic, ok}, mnesia:create_table(Tab,[{snmp,[{key,{fix_string,integer}}]}])),
+ snmp_shadows_test(Tab),
+ ?match({atomic, ok}, mnesia:delete_table(Tab)),
+ io:format("Without fixstring~n", []),
+ ?match({atomic, ok}, mnesia:create_table(Tab,[{snmp,[{key,{string,integer}}]}])),
+ snmp_shadows_test(Tab),
+ ?verify_mnesia(Nodes, []).
+
+snmp_shadows_test(Tab) ->
+ [mnesia:dirty_write({Tab, {"string", N}, {N, init}}) || N <- lists:seq(2,8,2)],
+
+ CheckOrder = fun(A={_,_,{_,_,State}}, Prev) ->
+ ?match({true, A, Prev}, {Prev < A, A, Prev}),
+ {State,A}
+ end,
+ R1 = mnesia:sync_dirty(fun() -> loop_snmp(Tab, []) end),
+ lists:mapfoldl(CheckOrder, {[],foo,foo}, R1),
+ R2 = mnesia:transaction(fun() -> loop_snmp(Tab, []) end),
+ ?match({atomic, R1}, R2),
+
+ Shadow = fun() ->
+ ok = mnesia:write({Tab, {"string",1}, {1,update}}),
+ ok = mnesia:write({Tab, {"string",4}, {4,update}}),
+ ok = mnesia:write({Tab, {"string",6}, {6,update}}),
+ ok = mnesia:delete({Tab, {"string",6}}),
+ ok = mnesia:write({Tab, {"string",9}, {9,update}}),
+ ok = mnesia:write({Tab, {"string",3}, {3,update}}),
+ ok = mnesia:write({Tab, {"string",5}, {5,update}}),
+ [Row5] = mnesia:read({Tab, {"string",5}}),
+ ok = mnesia:delete_object(Row5),
+ loop_snmp(Tab, [])
+ end,
+ R3 = mnesia:sync_dirty(Shadow),
+ {L3,_} = lists:mapfoldl(CheckOrder, {[],foo,foo}, R3),
+ ?match([{1,update},{2,init},{3,update},{4,update},{8,init},{9,update}], L3),
+ ?match({atomic, ok}, mnesia:clear_table(Tab)),
+
+ [mnesia:dirty_write({Tab, {"string", N}, {N, init}}) || N <- lists:seq(2,8,2)],
+ {atomic, R3} = mnesia:transaction(Shadow),
+ {L4,_} = lists:mapfoldl(CheckOrder, {[],foo,foo}, R3),
+ ?match([{1,update},{2,init},{3,update},{4,update},{8,init},{9,update}], L4),
+ ok.
+
+loop_snmp(Tab,Prev) ->
+ case mnesia:snmp_get_next_index(Tab,Prev) of
+ {ok, SKey} ->
+ {{ok,Row},_} = {mnesia:snmp_get_row(Tab, SKey),{?LINE,Prev,SKey}},
+ {{ok,MKey},_} = {mnesia:snmp_get_mnesia_key(Tab,SKey),{?LINE,Prev,SKey}},
+ ?match({[Row],Row,SKey,MKey}, {mnesia:read({Tab,MKey}),Row,SKey,MKey}),
+ [{SKey, MKey, Row} | loop_snmp(Tab, SKey)];
+ endOfTable ->
+ []
+ end.
diff --git a/lib/mnesia/test/mnesia_measure_test.erl b/lib/mnesia/test/mnesia_measure_test.erl
new file mode 100644
index 0000000000..e63689d83a
--- /dev/null
+++ b/lib/mnesia/test/mnesia_measure_test.erl
@@ -0,0 +1,152 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_measure_test).
+-author('[email protected]').
+-compile([export_all]).
+
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-define(init(N, Config),
+ mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema],
+ N, Config, ?FILE, ?LINE)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all() ->
+ [{group, prediction}, {group, consumption},
+ {group, scalability}, {group, benchmarks}].
+
+groups() ->
+ [{prediction, [],
+ [reader_disturbed_by_node_down,
+ writer_disturbed_by_node_down,
+ reader_disturbed_by_node_up,
+ writer_disturbed_by_node_up,
+ reader_disturbed_by_schema_ops,
+ writer_disturbed_by_schema_ops,
+ reader_disturbed_by_checkpoint,
+ writer_disturbed_by_checkpoint,
+ reader_disturbed_by_dump_log,
+ writer_disturbed_by_dump_log,
+ reader_disturbed_by_backup, writer_disturbed_by_backup,
+ reader_disturbed_by_restore,
+ writer_disturbed_by_restore, {group, fairness}]},
+ {fairness, [],
+ [reader_competing_with_reader,
+ reader_competing_with_writer,
+ writer_competing_with_reader,
+ writer_competing_with_writer]},
+ {consumption, [],
+ [measure_resource_consumption,
+ determine_resource_leakage]},
+ {scalability, [],
+ [determine_system_limits, performance_at_min_config,
+ performance_at_max_config, performance_at_full_load,
+ resource_consumption_at_min_config,
+ resource_consumption_at_max_config,
+ resource_consumption_at_full_load]},
+ {benchmarks, [],
+ [{group, meter}, cost, dbn_meters,
+ measure_all_api_functions, {group, tpcb},
+ mnemosyne_vs_mnesia_kernel]},
+ {tpcb, [], [ram_tpcb, disc_tpcb, disc_only_tpcb]},
+ {meter, [], [ram_meter, disc_meter, disc_only_meter]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dbn_meters(suite) -> [];
+dbn_meters(Config) when is_list(Config) ->
+ _Nodes = ?init(3, Config),
+ ?match(ok, mnesia_dbn_meters:start()),
+ ok.
+
+
+tpcb(ReplicaType, Config) ->
+ HarakiriDelay = {tc_timeout, timer:minutes(20)},
+ Nodes = ?acquire_nodes(2, Config ++ [HarakiriDelay]),
+ Args = [{n_branches, 2},
+ {n_drivers_per_node, 1},
+ {replica_nodes, Nodes},
+ {driver_nodes, [hd(Nodes)]},
+ {use_running_mnesia, true},
+ {use_sticky_locks, true},
+ {replica_type, ReplicaType}],
+ ?match({ok, _}, mnesia_tpcb:start(Args)),
+ ?verify_mnesia(Nodes, []).
+
+ram_tpcb(suite) -> [];
+ram_tpcb(Config) when is_list(Config) ->
+ tpcb(ram_copies, Config).
+
+disc_tpcb(suite) -> [];
+disc_tpcb(Config) when is_list(Config) ->
+ tpcb(disc_copies, Config).
+
+disc_only_tpcb(suite) -> [];
+disc_only_tpcb(Config) when is_list(Config) ->
+ tpcb(disc_only_copies, Config).
+
+
+ram_meter(suite) -> [];
+ram_meter(Config) when is_list(Config) ->
+ HarakiriDelay = [{tc_timeout, timer:minutes(20)}],
+ Nodes = ?init(3, Config ++ HarakiriDelay),
+ ?match(ok, mnesia_meter:go(ram_copies, Nodes)).
+
+disc_meter(suite) -> [];
+disc_meter(Config) when is_list(Config) ->
+ HarakiriDelay = [{tc_timeout, timer:minutes(20)}],
+ Nodes = ?init(3, Config ++ HarakiriDelay),
+ ?match(ok, mnesia_meter:go(disc_copies, Nodes)).
+
+disc_only_meter(suite) -> [];
+disc_only_meter(Config) when is_list(Config) ->
+ HarakiriDelay = [{tc_timeout, timer:minutes(20)}],
+ Nodes = ?init(3, Config ++ HarakiriDelay),
+ ?match(ok, mnesia_meter:go(disc_only_copies, Nodes)).
+
+cost(suite) -> [];
+cost(Config) when is_list(Config) ->
+ Nodes = ?init(3, Config),
+ ?match(ok, mnesia_cost:go(Nodes)),
+ file:delete("MNESIA_COST").
diff --git a/lib/mnesia/test/mnesia_meter.erl b/lib/mnesia/test/mnesia_meter.erl
new file mode 100644
index 0000000000..68094c4431
--- /dev/null
+++ b/lib/mnesia/test/mnesia_meter.erl
@@ -0,0 +1,465 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Getting started:
+%%
+%% 1 Start one or more distributed Erlang nodes
+%% 2a Connect the nodes, e.g. with net_adm:ping/1
+%% 3a Run mnesia_meter:go()
+%% 3b Run mnesia_meter:go(ReplicaType)
+%% 3c Run mnesia_meter:go(ReplicaType, Nodes)
+
+-module(mnesia_meter).
+-author('[email protected]').
+-export([
+ go/0,
+ go/1,
+ go/2,
+ repeat_meter/2
+ ]).
+
+-record(person, {name, %% atomic, unique key
+ data, %% compound structure
+ married_to, %% name of partner or undefined
+ children}). %% list of children
+
+-record(meter, {desc, init, meter, micros}).
+
+-record(result, {desc, list}).
+
+-define(TIMES, 1000).
+
+go() ->
+ go(ram_copies).
+
+go(ReplicaType) ->
+ go(ReplicaType, [node() | nodes()]).
+
+go(ReplicaType, Nodes) ->
+ {ok, FunOverhead} = tc(fun(_) -> {atomic, ok} end, ?TIMES),
+ Size = size(term_to_binary(#person{})),
+ io:format("A fun apply costs ~p micro seconds. Record size is ~p bytes.~n",
+ [FunOverhead, Size]),
+ Res = go(ReplicaType, Nodes, [], FunOverhead, []),
+ NewRes = rearrange(Res, []),
+ DescHeader = lists:flatten(io_lib:format("~w on ~w", [ReplicaType, Nodes])),
+ ItemHeader = lists:seq(1, length(Nodes)),
+ Header = #result{desc = DescHeader, list = ItemHeader},
+ SepList = ['--------' || _ <- Nodes],
+ Separator = #result{desc = "", list = SepList},
+ display([Separator, Header, Separator | NewRes] ++ [Separator]).
+
+go(_ReplicaType, [], _Config, _FunOverhead, Acc) ->
+ Acc;
+go(ReplicaType, [H | T], OldNodes, FunOverhead, Acc) ->
+ Nodes = [H | OldNodes],
+ Config = [{ReplicaType, Nodes}],
+ Res = run(Nodes, Config, FunOverhead),
+ go(ReplicaType, T, Nodes, FunOverhead, [{ReplicaType, Nodes, Res} | Acc]).
+
+rearrange([{_ReplicaType, _Nodes, Meters} | Tail], Acc) ->
+ Acc2 = [add_meter(M, Acc) || M <- Meters],
+ rearrange(Tail, Acc2);
+rearrange([], Acc) ->
+ Acc.
+
+add_meter(M, Acc) ->
+ case lists:keysearch(M#meter.desc, #result.desc, Acc) of
+ {value, R} ->
+ R#result{list = [M#meter.micros | R#result.list]};
+ false ->
+ #result{desc = M#meter.desc, list = [M#meter.micros]}
+ end.
+
+display(Res) ->
+ MaxDesc = lists:max([length(R#result.desc) || R <- Res]),
+ Format = lists:concat(["! ~-", MaxDesc, "s"]),
+ display(Res, Format, MaxDesc).
+
+display([R | Res], Format, MaxDesc) ->
+ case R#result.desc of
+ "" ->
+ io:format(Format, [lists:duplicate(MaxDesc, "-")]);
+ Desc ->
+ io:format(Format, [Desc])
+ end,
+ display_items(R#result.list, R#result.desc),
+ io:format(" !~n", []),
+ display(Res, Format, MaxDesc);
+display([], _Format, _MaxDesc) ->
+ ok.
+
+display_items([_Item | Items], "") ->
+ io:format(" ! ~s", [lists:duplicate(10, $-)]),
+ display_items(Items, "");
+display_items([Micros | Items], Desc) ->
+ io:format(" ! ~10w", [Micros]),
+ display_items(Items, Desc);
+display_items([], _Desc) ->
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+meters() ->
+ [#meter{desc = "transaction update two records with read and write",
+ init = fun write_records/2,
+ meter = fun update_records/1},
+ #meter{desc = "transaction update two records with wread and write",
+ init = fun write_records/2,
+ meter = fun w_update_records/1},
+ #meter{desc = "transaction update two records with read and s_write",
+ init = fun s_write_records/2,
+ meter = fun s_update_records/1},
+ #meter{desc = "sync_dirty update two records with read and write",
+ init = fun sync_dirty_write_records/2,
+ meter = fun sync_dirty_update_records/1},
+ #meter{desc = "async_dirty update two records with read and write",
+ init = fun async_dirty_write_records/2,
+ meter = fun async_dirty_update_records/1},
+ #meter{desc = "plain fun update two records with dirty_read and dirty_write",
+ init = fun dirty_write_records/2,
+ meter = fun dirty_update_records/1},
+ #meter{desc = "ets update two records with read and write (local only)",
+ init = fun ets_opt_write_records/2,
+ meter = fun ets_update_records/1},
+ #meter{desc = "plain fun update two records with ets:lookup and ets:insert (local only)",
+ init = fun bif_opt_write_records/2,
+ meter = fun bif_update_records/1},
+ #meter{desc = "plain fun update two records with dets:lookup and dets:insert (local only)",
+ init = fun dets_opt_write_records/2,
+ meter = fun dets_update_records/1},
+
+ #meter{desc = "transaction write two records with write",
+ init = fun write_records/2,
+ meter = fun(X) -> write_records(X, 0-X) end},
+ #meter{desc = "transaction write two records with s_write",
+ init = fun s_write_records/2,
+ meter = fun(X) -> s_write_records(X, 0-X) end},
+ #meter{desc = "sync_dirty write two records with write",
+ init = fun sync_dirty_write_records/2,
+ meter = fun(X) -> sync_dirty_write_records(X, 0-X) end},
+ #meter{desc = "async_dirty write two records with write",
+ init = fun async_dirty_write_records/2,
+ meter = fun(X) -> async_dirty_write_records(X, 0-X) end},
+ #meter{desc = "plain fun write two records with dirty_write",
+ init = fun dirty_write_records/2,
+ meter = fun(X) -> dirty_write_records(X, 0-X) end},
+ #meter{desc = "ets write two records with write (local only)",
+ init = fun ets_opt_write_records/2,
+ meter = fun(X) -> ets_write_records(X, 0-X) end},
+ #meter{desc = "plain fun write two records with ets:insert (local only)",
+ init = fun bif_opt_write_records/2,
+ meter = fun(X) -> bif_write_records(X, 0-X) end},
+ #meter{desc = "plain fun write two records with dets:insert (local only)",
+ init = fun dets_opt_write_records/2,
+ meter = fun(X) -> dets_write_records(X, 0-X) end},
+
+ #meter{desc = "transaction read two records with read",
+ init = fun write_records/2,
+ meter = fun(X) -> read_records(X, 0-X) end},
+ #meter{desc = "sync_dirty read two records with read",
+ init = fun sync_dirty_write_records/2,
+ meter = fun(X) -> sync_dirty_read_records(X, 0-X) end},
+ #meter{desc = "async_dirty read two records with read",
+ init = fun async_dirty_write_records/2,
+ meter = fun(X) -> async_dirty_read_records(X, 0-X) end},
+ #meter{desc = "plain fun read two records with dirty_read",
+ init = fun dirty_write_records/2,
+ meter = fun(X) -> dirty_read_records(X, 0-X) end},
+ #meter{desc = "ets read two records with read",
+ init = fun ets_opt_write_records/2,
+ meter = fun(X) -> ets_read_records(X, 0-X) end},
+ #meter{desc = "plain fun read two records with ets:lookup",
+ init = fun bif_opt_write_records/2,
+ meter = fun(X) -> bif_read_records(X, 0-X) end},
+ #meter{desc = "plain fun read two records with dets:lookup",
+ init = fun dets_opt_write_records/2,
+ meter = fun(X) -> dets_read_records(X, 0-X) end}
+ ].
+
+update_fun(Name) ->
+ fun() ->
+ case mnesia:read({person, Name}) of
+ [] ->
+ mnesia:abort(no_such_person);
+ [Pers] ->
+ [Partner] = mnesia:read({person, Pers#person.married_to}),
+ mnesia:write(Pers#person{married_to = undefined}),
+ mnesia:write(Partner#person{married_to = undefined})
+ end
+ end.
+
+update_records(Name) ->
+ mnesia:transaction(update_fun(Name)).
+
+sync_dirty_update_records(Name) ->
+ {atomic, mnesia:sync_dirty(update_fun(Name))}.
+
+async_dirty_update_records(Name) ->
+ {atomic, mnesia:async_dirty(update_fun(Name))}.
+
+ets_update_records(Name) ->
+ {atomic, mnesia:ets(update_fun(Name))}.
+
+w_update_records(Name) ->
+ F = fun() ->
+ case mnesia:wread({person, Name}) of
+ [] ->
+ mnesia:abort(no_such_person);
+ [Pers] ->
+ [Partner] = mnesia:wread({person, Pers#person.married_to}),
+ mnesia:write(Pers#person{married_to = undefined}),
+ mnesia:write(Partner#person{married_to = undefined})
+ end
+ end,
+ mnesia:transaction(F).
+
+s_update_records(Name) ->
+ F = fun() ->
+ case mnesia:read({person, Name}) of
+ [] ->
+ mnesia:abort(no_such_person);
+ [Pers] ->
+ [Partner] = mnesia:read({person, Pers#person.married_to}),
+ mnesia:s_write(Pers#person{married_to = undefined}),
+ mnesia:s_write(Partner#person{married_to = undefined})
+ end
+ end,
+ mnesia:transaction(F).
+
+dirty_update_records(Name) ->
+ case mnesia:dirty_read({person, Name}) of
+ [] ->
+ mnesia:abort(no_such_person);
+ [Pers] ->
+ [Partner] = mnesia:dirty_read({person, Pers#person.married_to}),
+ mnesia:dirty_write(Pers#person{married_to = undefined}),
+ mnesia:dirty_write(Partner#person{married_to = undefined})
+ end,
+ {atomic, ok}.
+
+bif_update_records(Name) ->
+ case ets:lookup(person, Name) of
+ [] ->
+ mnesia:abort(no_such_person);
+ [Pers] ->
+ [Partner] = ets:lookup(person, Pers#person.married_to),
+ ets:insert(person, Pers#person{married_to = undefined}),
+ ets:insert(person, Partner#person{married_to = undefined})
+ end,
+ {atomic, ok}.
+
+dets_update_records(Name) ->
+ case dets:lookup(person, Name) of
+ [] ->
+ mnesia:abort(no_such_person);
+ [Pers] ->
+ [Partner] = dets:lookup(person, Pers#person.married_to),
+ dets:insert(person, Pers#person{married_to = undefined}),
+ dets:insert(person, Partner#person{married_to = undefined})
+ end,
+ {atomic, ok}.
+
+write_records_fun(Pers, Partner) ->
+ fun() ->
+ P = #person{children = [ulla, bella]},
+ mnesia:write(P#person{name = Pers, married_to = Partner}),
+ mnesia:write(P#person{name = Partner, married_to = Pers})
+ end.
+
+write_records(Pers, Partner) ->
+ mnesia:transaction(write_records_fun(Pers, Partner)).
+
+sync_dirty_write_records(Pers, Partner) ->
+ {atomic, mnesia:sync_dirty(write_records_fun(Pers, Partner))}.
+
+async_dirty_write_records(Pers, Partner) ->
+ {atomic, mnesia:async_dirty(write_records_fun(Pers, Partner))}.
+
+ets_write_records(Pers, Partner) ->
+ {atomic, mnesia:ets(write_records_fun(Pers, Partner))}.
+
+s_write_records(Pers, Partner) ->
+ F = fun() ->
+ P = #person{children = [ulla, bella]},
+ mnesia:s_write(P#person{name = Pers, married_to = Partner}),
+ mnesia:s_write(P#person{name = Partner, married_to = Pers})
+ end,
+ mnesia:transaction(F).
+
+dirty_write_records(Pers, Partner) ->
+ P = #person{children = [ulla, bella]},
+ mnesia:dirty_write(P#person{name = Pers, married_to = Partner}),
+ mnesia:dirty_write(P#person{name = Partner, married_to = Pers}),
+ {atomic, ok}.
+
+ets_opt_write_records(Pers, Partner) ->
+ case mnesia:table_info(person, where_to_commit) of
+ [{N, ram_copies}] when N == node() ->
+ ets_write_records(Pers, Partner);
+ _ ->
+ throw(skipped)
+ end.
+
+bif_opt_write_records(Pers, Partner) ->
+ case mnesia:table_info(person, where_to_commit) of
+ [{N, ram_copies}] when N == node() ->
+ bif_write_records(Pers, Partner);
+ _ ->
+ throw(skipped)
+ end.
+
+bif_write_records(Pers, Partner) ->
+ P = #person{children = [ulla, bella]},
+ ets:insert(person, P#person{name = Pers, married_to = Partner}),
+ ets:insert(person, P#person{name = Partner, married_to = Pers}),
+ {atomic, ok}.
+
+dets_opt_write_records(Pers, Partner) ->
+ case mnesia:table_info(person, where_to_commit) of
+ [{N, disc_only_copies}] when N == node() ->
+ dets_write_records(Pers, Partner);
+ _ ->
+ throw(skipped)
+ end.
+
+dets_write_records(Pers, Partner) ->
+ P = #person{children = [ulla, bella]},
+ dets:insert(person, P#person{name = Pers, married_to = Partner}),
+ dets:insert(person, P#person{name = Partner, married_to = Pers}),
+ {atomic, ok}.
+
+read_records_fun(Pers, Partner) ->
+ fun() ->
+ case {mnesia:read({person, Pers}),
+ mnesia:read({person, Partner})} of
+ {[_], [_]} ->
+ ok;
+ _ ->
+ mnesia:abort(no_such_person)
+ end
+ end.
+
+read_records(Pers, Partner) ->
+ mnesia:transaction(read_records_fun(Pers, Partner)).
+
+sync_dirty_read_records(Pers, Partner) ->
+ {atomic, mnesia:sync_dirty(read_records_fun(Pers, Partner))}.
+
+async_dirty_read_records(Pers, Partner) ->
+ {atomic, mnesia:async_dirty(read_records_fun(Pers, Partner))}.
+
+ets_read_records(Pers, Partner) ->
+ {atomic, mnesia:ets(read_records_fun(Pers, Partner))}.
+
+dirty_read_records(Pers, Partner) ->
+ case {mnesia:dirty_read({person, Pers}),
+ mnesia:dirty_read({person, Partner})} of
+ {[_], [_]} ->
+ {atomic, ok};
+ _ ->
+ mnesia:abort(no_such_person)
+ end.
+
+bif_read_records(Pers, Partner) ->
+ case {ets:lookup(person, Pers),
+ ets:lookup(person, Partner)} of
+ {[_], [_]} ->
+ {atomic, ok};
+ _ ->
+ mnesia:abort(no_such_person)
+ end.
+
+dets_read_records(Pers, Partner) ->
+ case {dets:lookup(person, Pers),
+ dets:lookup(person, Partner)} of
+ {[_], [_]} ->
+ {atomic, ok};
+ _ ->
+ mnesia:abort(no_such_person)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+run(Nodes, Config, FunOverhead) ->
+ Meters = meters(),
+ io:format("Run ~w meters with table config: ~w~n", [length(Meters), Config]),
+ rpc:multicall(Nodes, mnesia, lkill, []),
+ start(Nodes, Config),
+ Res = [run_meter(Data, Nodes, FunOverhead) || Data <- Meters],
+ stop(Nodes),
+ Res.
+
+run_meter(M, Nodes, FunOverhead) when is_record(M, meter) ->
+ io:format(".", []),
+ case catch init_records(M#meter.init, ?TIMES) of
+ {atomic, ok} ->
+ rpc:multicall(Nodes, mnesia, dump_log, []),
+ case tc(M#meter.meter, ?TIMES) of
+ {ok, Micros} ->
+ M#meter{micros = lists:max([0, Micros - FunOverhead])};
+ {error, Reason} ->
+ M#meter{micros = Reason}
+ end;
+ Res ->
+ M#meter{micros = Res}
+ end.
+
+start(Nodes, Config) ->
+ mnesia:delete_schema(Nodes),
+ ok = mnesia:create_schema(Nodes),
+ Args = [[{dump_log_write_threshold, ?TIMES div 2},
+ {dump_log_time_threshold, timer:hours(10)}]],
+ lists:foreach(fun(Node) -> rpc:call(Node, mnesia, start, Args) end, Nodes),
+ Attrs = record_info(fields, person),
+ TabDef = [{attributes, Attrs} | Config],
+ {atomic, _} = mnesia:create_table(person, TabDef).
+
+stop(Nodes) ->
+ rpc:multicall(Nodes, mnesia, stop, []).
+
+%% Generate some dummy persons
+init_records(_Fun, 0) ->
+ {atomic, ok};
+init_records(Fun, Times) ->
+ {atomic, ok} = Fun(Times, 0 - Times),
+ init_records(Fun, Times - 1).
+
+tc(Fun, Times) ->
+ case catch timer:tc(?MODULE, repeat_meter, [Fun, Times]) of
+ {Micros, ok} ->
+ {ok, Micros div Times};
+ {_Micros, {error, Reason}} ->
+ {error, Reason};
+ {'EXIT', Reason} ->
+ {error, Reason}
+ end.
+
+%% The meter must return {atomic, ok}
+repeat_meter(Meter, Times) ->
+ repeat_meter(Meter, {atomic, ok}, Times).
+
+repeat_meter(_, {atomic, ok}, 0) ->
+ ok;
+repeat_meter(Meter, {atomic, _Result}, Times) when Times > 0 ->
+ repeat_meter(Meter, Meter(Times), Times - 1);
+repeat_meter(_Meter, Reason, _Times) ->
+ {error, Reason}.
+
diff --git a/lib/mnesia/test/mnesia_nice_coverage_test.erl b/lib/mnesia/test/mnesia_nice_coverage_test.erl
new file mode 100644
index 0000000000..78eab67b11
--- /dev/null
+++ b/lib/mnesia/test/mnesia_nice_coverage_test.erl
@@ -0,0 +1,233 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_nice_coverage_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+-record(nice_tab, {key, val}).
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [nice].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+nice(doc) -> [""];
+nice(suite) -> [];
+nice(Config) when is_list(Config) ->
+ %% The whole test suite is one huge test case for the time beeing
+
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Attrs = record_info(fields, nice_tab),
+
+ initialize(Attrs, Node1),
+ dirty_access(Node1),
+ success_and_fail(),
+ index_mgt(),
+
+ adm(Attrs, Node1, Node2),
+ snmp(Node1, Node2),
+ backup(Node1),
+ ?verify_mnesia(Nodes, []).
+
+initialize(Attrs, Node1) ->
+ ?match(Version when is_list(Version), mnesia:system_info(version)),
+
+ Schema = [{name, nice_tab},
+ {attributes, Attrs}, {ram_copies, [Node1]}],
+
+ ?match({_, _}, mnesia:system_info(schema_version)),
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ ?match(ok, mnesia:info()),
+ ?match(set, mnesia:table_info(nice_tab, type)),
+ ?match(ok, mnesia:schema()),
+ ?match(ok, mnesia:schema(nice_tab)),
+ ok.
+
+dirty_access(Node1) ->
+ TwoThree = #nice_tab{key=23, val=23},
+ TwoFive = #nice_tab{key=25, val=25},
+ ?match([], mnesia:dirty_slot(nice_tab, 0)),
+ ?match(ok, mnesia:dirty_write(TwoThree)),
+ ?match([TwoThree], mnesia:dirty_read({nice_tab, 23})),
+ ?match(ok, mnesia:dirty_write(TwoFive)),
+ ?match(ok, mnesia:dirty_delete_object(TwoFive)),
+
+ ?match(23, mnesia:dirty_first(nice_tab)),
+ ?match('$end_of_table', mnesia:dirty_next(nice_tab, 23)),
+ ?match([TwoThree], mnesia:dirty_match_object(TwoThree)),
+ ?match(ok, mnesia:dirty_delete({nice_tab, 23})),
+
+ CounterSchema = [{ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(nice_counter_tab, CounterSchema)),
+ TwoFour = {nice_counter_tab, 24, 24},
+ ?match(ok, mnesia:dirty_write(TwoFour)),
+ ?match(34, mnesia:dirty_update_counter({nice_counter_tab, 24}, 10)),
+ TF = {nice_counter_tab, 24, 34},
+ ?match([TF], mnesia:dirty_read({nice_counter_tab, 24})),
+ ?match(ok, mnesia:dirty_delete({nice_counter_tab, 24})),
+ ?match(ok, mnesia:dirty_delete_object(TF)),
+ ok.
+
+success_and_fail() ->
+ ?match({atomic, a_good_trans}, mnesia:transaction(fun() ->good_trans()end)),
+
+ BadFun =
+ fun() ->
+ Two = #nice_tab{key=2, val=12},
+ ?match([Two], mnesia:match_object(#nice_tab{key='$1', val=12})),
+ ?match([#nice_tab{key=3, val=13}], mnesia:wread({nice_tab, 3})),
+ ?match(ok, mnesia:delete({nice_tab, 1})),
+ ?match(ok, mnesia:delete_object(Two)),
+ mnesia:abort(bad_trans),
+ ?match(bad, trans)
+ end,
+ ?match({aborted, bad_trans}, mnesia:transaction(BadFun)),
+ ?match(L when is_list(L), mnesia:error_description(no_exists)),
+ ?match({atomic, ok}, mnesia:transaction(fun(A) -> lock(), A end, [ok])),
+ ?match({atomic, ok}, mnesia:transaction(fun(A) -> lock(), A end, [ok], 3)),
+ ok.
+
+good_trans() ->
+ ?match([], mnesia:read(nice_tab, 3)),
+ ?match([], mnesia:read({nice_tab, 3})),
+ ?match(ok, mnesia:write(#nice_tab{key=14, val=4})),
+ ?match([14], mnesia:all_keys(nice_tab)),
+
+ Records = [ #nice_tab{key=K, val=K+10} || K <- lists:seq(1, 10) ],
+ Ok = [ ok || _ <- Records],
+ ?match(Ok, lists:map(fun(R) -> mnesia:write(R) end, Records)),
+ a_good_trans.
+
+
+lock() ->
+ ?match(ok, mnesia:s_write(#nice_tab{key=22, val=22})),
+ ?match(ok, mnesia:read_lock_table(nice_tab)),
+ ?match(ok, mnesia:write_lock_table(nice_tab)),
+ ok.
+
+index_mgt() ->
+ UniversalRec = #nice_tab{key=4711, val=4711},
+ ?match(ok, mnesia:dirty_write(UniversalRec)),
+ ValPos = #nice_tab.val,
+ ?match({atomic, ok}, mnesia:add_table_index(nice_tab, ValPos)),
+
+ IndexFun =
+ fun() ->
+ ?match([UniversalRec],
+ mnesia:index_read(nice_tab, 4711, ValPos)),
+ Pat = #nice_tab{key='$1', val=4711},
+ ?match([UniversalRec],
+ mnesia:index_match_object(Pat, ValPos)),
+ index_trans
+ end,
+ ?match({atomic, index_trans}, mnesia:transaction(IndexFun, infinity)),
+ ?match([UniversalRec],
+ mnesia:dirty_index_read(nice_tab, 4711, ValPos)),
+ ?match([UniversalRec],
+ mnesia:dirty_index_match_object(#nice_tab{key='$1', val=4711}, ValPos)),
+
+ ?match({atomic, ok}, mnesia:del_table_index(nice_tab, ValPos)),
+ ok.
+
+adm(Attrs, Node1, Node2) ->
+ This = node(),
+ ?match({ok, This}, mnesia:subscribe(system)),
+ ?match({atomic, ok},
+ mnesia:add_table_copy(nice_tab, Node2, disc_only_copies)),
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(nice_tab, Node2, ram_copies)),
+ ?match({atomic, ok}, mnesia:del_table_copy(nice_tab, Node1)),
+ ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
+ ?match([], mnesia_test_lib:start_mnesia([Node1, Node2], [nice_tab])),
+ ?match(ok, mnesia:wait_for_tables([schema], infinity)),
+
+ Transformer = fun(Rec) ->
+ list_to_tuple(tuple_to_list(Rec) ++ [initial_value])
+ end,
+ ?match({atomic, ok},
+ mnesia:transform_table(nice_tab, Transformer, Attrs ++ [extra])),
+
+ ?match({atomic, ok}, mnesia:delete_table(nice_tab)),
+ DumpSchema = [{name, nice_tab}, {attributes, Attrs}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(DumpSchema)),
+ ?match({atomic, ok}, mnesia:dump_tables([nice_tab])),
+ ?match({atomic, ok}, mnesia:move_table_copy(nice_tab, Node2, Node1)),
+
+ ?match(yes, mnesia:force_load_table(nice_counter_tab)),
+ ?match(dumped, mnesia:dump_log()),
+ ok.
+
+backup(Node1) ->
+ Tab = backup_nice,
+ Def = [{disc_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match({ok,_,_}, mnesia:activate_checkpoint([{name, cp}, {max, [Tab]}])),
+ File = "nice_backup.BUP",
+ File2 = "nice_backup2.BUP",
+ File3 = "nice_backup3.BUP",
+ ?match(ok, mnesia:backup_checkpoint(cp, File)),
+ ?match(ok, mnesia:backup_checkpoint(cp, File, mnesia_backup)),
+ ?match(ok, mnesia:deactivate_checkpoint(cp)),
+ ?match(ok, mnesia:backup(File)),
+ ?match(ok, mnesia:backup(File, mnesia_backup)),
+
+ Fun = fun(X, Acc) -> {[X], Acc} end,
+ ?match({ok, 0}, mnesia:traverse_backup(File, File2, Fun, 0)),
+ ?match({ok, 0}, mnesia:traverse_backup(File, mnesia_backup, dummy, read_only, Fun, 0)),
+ ?match(ok, mnesia:install_fallback(File)),
+ ?match(ok, mnesia:uninstall_fallback()),
+ ?match(ok, mnesia:install_fallback(File, mnesia_backup)),
+ ?match(ok, mnesia:dump_to_textfile(File3)),
+ ?match({atomic, ok}, mnesia:load_textfile(File3)),
+ ?match(ok, file:delete(File)),
+ ?match(ok, file:delete(File2)),
+ ?match(ok, file:delete(File3)),
+ ok.
+
+snmp(Node1, Node2) ->
+ Tab = nice_snmp,
+ Def = [{disc_copies, [Node1]}, {ram_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match({aborted, {badarg, Tab, _}}, mnesia:snmp_open_table(Tab, [])),
+ ?match({atomic, ok}, mnesia:snmp_open_table(Tab, [{key, integer}])),
+ ?match(endOfTable, mnesia:snmp_get_next_index(Tab, [0])),
+ ?match(undefined, mnesia:snmp_get_row(Tab, [0])),
+ ?match(undefined, mnesia:snmp_get_mnesia_key(Tab, [0])),
+ ?match({atomic, ok}, mnesia:snmp_close_table(Tab)),
+ ok.
+
diff --git a/lib/mnesia/test/mnesia_qlc_test.erl b/lib/mnesia/test/mnesia_qlc_test.erl
new file mode 100644
index 0000000000..141de71d01
--- /dev/null
+++ b/lib/mnesia/test/mnesia_qlc_test.erl
@@ -0,0 +1,477 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_qlc_test).
+
+-compile(export_all).
+
+-export([all/0,groups/0,init_per_group/2,end_per_group/2]).
+
+-include("mnesia_test_lib.hrl").
+-include_lib("stdlib/include/qlc.hrl").
+
+init_per_testcase(Func, Conf) ->
+ setup(Conf),
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+all() ->
+ case code:which(qlc) of
+ non_existing -> [];
+ _ -> all_qlc()
+ end.
+
+groups() ->
+ [{dirty, [],
+ [dirty_nice_ram_copies, dirty_nice_disc_copies,
+ dirty_nice_disc_only_copies]},
+ {trans, [],
+ [trans_nice_ram_copies, trans_nice_disc_copies,
+ trans_nice_disc_only_copies, {group, atomic}]},
+ {atomic, [], [atomic_eval]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+all_qlc() ->
+ [{group, dirty}, {group, trans}, frag, info,
+ mnesia_down].
+
+init_testcases(Type,Config) ->
+ Nodes = [N1,N2] = ?acquire_nodes(2, Config),
+ ?match({atomic, ok}, mnesia:create_table(a, [{Type,[N1]}, {index,[3]}])),
+ ?match({atomic, ok}, mnesia:create_table(b, [{Type,[N2]}])),
+ Write = fun(Id) ->
+ ok = mnesia:write({a, {a,Id}, 100 - Id}),
+ ok = mnesia:write({b, {b,100-Id}, Id})
+ end,
+ All = fun() -> [Write(Id) || Id <- lists:seq(1,10)], ok end,
+ ?match({atomic, ok}, mnesia:sync_transaction(All)),
+ Nodes.
+
+%% Test cases
+
+dirty_nice_ram_copies(Setup) -> dirty_nice(Setup,ram_copies).
+dirty_nice_disc_copies(Setup) -> dirty_nice(Setup,disc_copies).
+dirty_nice_disc_only_copies(Setup) -> dirty_nice(Setup,disc_only_copies).
+
+dirty_nice(suite, _) -> [];
+dirty_nice(doc, _) -> [];
+dirty_nice(Config, Type) when is_list(Config) ->
+ Ns = init_testcases(Type,Config),
+ QA = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(a),"
+ " Val == 90 + Key]">>),
+ QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
+ " Key == 90 + Val]">>),
+ QC = qlc:sort(mnesia:table(a, [{n_objects,1}, {lock,write}, {traverse, select}])),
+ QD = qlc:sort(mnesia:table(a, [{n_objects,1}, {traverse,{select,[{'$1',[],['$1']}]}}])),
+
+ FA = fun() -> qlc:e(QA) end,
+ FB = fun() -> qlc:e(QB) end,
+ FC = fun() -> qlc:e(QC) end,
+ FD = fun() -> qlc:e(QD) end,
+
+ %% Currently unsupported
+ ?match({'EXIT',{aborted,no_transaction}}, FA()),
+ ?match({'EXIT',{aborted,no_transaction}}, FB()),
+ %%
+ CRes = lists:sort(mnesia:dirty_match_object(a, {'_','_','_'})),
+ ?match([{a,{a,5},95}], mnesia:async_dirty(FA)),
+ ?match([{b,{b,95},5}], mnesia:async_dirty(FB)),
+ ?match(CRes, mnesia:async_dirty(FC)),
+ ?match(CRes, mnesia:async_dirty(FD)),
+ ?match([{a,{a,5},95}], mnesia:sync_dirty(FA)),
+ ?match([{b,{b,95},5}], mnesia:sync_dirty(FB)),
+ ?match(CRes, mnesia:sync_dirty(FC)),
+ ?match([{a,{a,5},95}], mnesia:activity(async_dirty, FA)),
+ ?match([{b,{b,95},5}], mnesia:activity(async_dirty, FB)),
+ ?match([{a,{a,5},95}], mnesia:activity(sync_dirty, FA)),
+ ?match([{b,{b,95},5}], mnesia:activity(sync_dirty, FB)),
+ ?match(CRes, mnesia:activity(async_dirty,FC)),
+ case Type of
+ disc_only_copies -> skip;
+ _ ->
+ ?match([{a,{a,5},95}], mnesia:ets(FA)),
+ ?match([{a,{a,5},95}], mnesia:activity(ets, FA))
+ end,
+ ?verify_mnesia(Ns, []).
+
+
+trans_nice_ram_copies(Setup) -> trans_nice(Setup,ram_copies).
+trans_nice_disc_copies(Setup) -> trans_nice(Setup,disc_copies).
+trans_nice_disc_only_copies(Setup) -> trans_nice(Setup,disc_only_copies).
+
+trans_nice(suite, _) -> [];
+trans_nice(doc, _) -> [];
+trans_nice(Config, Type) when is_list(Config) ->
+ Ns = init_testcases(Type,Config),
+ QA = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(a),"
+ " Val == 90 + Key]">>),
+ QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
+ " Key == 90 + Val]">>),
+ QC = handle(recs(),
+ <<"[Q || Q = #a{v=91} <- mnesia:table(a)]"
+ >>),
+
+ QD = qlc:sort(mnesia:table(a, [{n_objects,1}, {lock,write}, {traverse, select}])),
+ QE = qlc:sort(mnesia:table(a, [{n_objects,1}, {traverse,{select,[{'$1',[],['$1']}]}}])),
+
+ DRes = lists:sort(mnesia:dirty_match_object(a, {'_','_','_'})),
+
+ FA = fun() -> qlc:e(QA) end,
+ FB = fun() -> qlc:e(QB) end,
+ FC = fun() -> qlc:e(QC) end,
+ FD = fun() -> qlc:e(QD) end,
+ FE = fun() -> qlc:e(QE) end,
+
+ ?match({atomic,[{a,{a,5},95}]}, mnesia:transaction(FA)),
+ ?match({atomic,[{b,{b,95},5}]}, mnesia:transaction(FB)),
+ ?match({atomic,[{a,{a,9},91}]}, mnesia:transaction(FC)),
+ ?match({atomic,[{a,{a,5},95}]}, mnesia:sync_transaction(FA)),
+ ?match({atomic,[{b,{b,95},5}]}, mnesia:sync_transaction(FB)),
+ ?match({atomic,[{a,{a,9},91}]}, mnesia:sync_transaction(FC)),
+ ?match([{a,{a,5},95}], mnesia:activity(transaction,FA)),
+ ?match([{b,{b,95},5}], mnesia:activity(transaction,FB)),
+ ?match([{a,{a,9},91}], mnesia:activity(transaction,FC)),
+ ?match([{a,{a,5},95}], mnesia:activity(sync_transaction,FA)),
+ ?match([{b,{b,95},5}], mnesia:activity(sync_transaction,FB)),
+ ?match([{a,{a,9},91}], mnesia:activity(sync_transaction,FC)),
+
+ ?match({atomic, DRes}, mnesia:transaction(FD)),
+ ?match({atomic, DRes}, mnesia:transaction(FE)),
+
+ Rest = fun(Cursor,Loop) ->
+ case qlc:next_answers(Cursor, 1) of
+ [] -> [];
+ [A]-> [A|Loop(Cursor,Loop)]
+ end
+ end,
+ Loop = fun() ->
+ Cursor = qlc:cursor(QD),
+ Rest(Cursor,Rest)
+ end,
+ ?match({atomic, DRes}, mnesia:transaction(Loop)),
+
+ ?verify_mnesia(Ns, []).
+
+%% -record(a, {k,v}).
+%% -record(b, {k,v}).
+%% -record(k, {t,v}).
+
+recs() ->
+ <<"-record(a, {k,v}). "
+ "-record(b, {k,v}). "
+ "-record(k, {t,v}). "
+ >>.
+
+
+atomic_eval(suite) -> [];
+atomic_eval(doc) -> [];
+atomic_eval(Config) ->
+ Ns = init_testcases(ram_copies, Config),
+ Q1 = handle(recs(),
+ <<"[Q || Q = #a{k={_,9}} <- mnesia:table(a)]"
+ >>),
+ Eval = fun(Q) ->
+ {qlc:e(Q),
+ mnesia:system_info(held_locks)}
+ end,
+ Self = self(),
+ ?match({[{a,{a,9},91}], [{{a,'______WHOLETABLE_____'},read,{tid,_,Self}}]},
+ ok(Eval,[Q1])),
+
+ Q2 = handle(recs(),
+ <<"[Q || Q = #a{k={a,9}} <- mnesia:table(a)]"
+ >>),
+
+ ?match({[{a,{a,9},91}],[{{a,{a,9}},read,{tid,_,Self}}]},
+ ok(Eval,[Q2])),
+
+ Flush = fun(Loop) -> %% Clean queue
+ receive _ -> Loop(Loop)
+ after 0 -> ok end
+ end,
+
+ Flush(Flush),
+
+ GrabLock = fun(Father) ->
+ mnesia:read(a, {a,9}, write),
+ Father ! locked,
+ receive cont -> ok end end,
+
+ Pid1 = spawn(fun() -> ?match(ok, ok(GrabLock, [Self])) end),
+ ?match(locked,receive locked -> locked after 5000 -> timeout end), %% Wait
+
+ put(count,0),
+ Restart = fun(Locker,Fun) ->
+ Count = get(count),
+ case {Count,(catch Fun())} of
+ {0, {'EXIT', R}} ->
+ Locker ! cont,
+ put(count, Count+1),
+ erlang:yield(),
+ exit(R);
+ Else ->
+ Else
+ end
+ end,
+
+ ?match({1,{[{a,{a,9},91}], [{{a,'______WHOLETABLE_____'},read,{tid,_,Self}}]}},
+ ok(Restart,[Pid1,fun() -> Eval(Q1) end])),
+
+ Pid2 = spawn(fun() -> ?match(ok, ok(GrabLock, [Self])) end),
+ ?match(locked,receive locked -> locked after 5000 -> timeout end), %% Wait
+ put(count,0),
+ ?match({1,{[{a,{a,9},91}],[{{a,{a,9}},read,{tid,_,Self}}]}},
+ ok(Restart,[Pid2, fun() -> Eval(Q2) end])),
+
+%% Basic test
+ Cursor = fun() ->
+ QC = qlc:cursor(Q1),
+ qlc:next_answers(QC)
+ end,
+
+ ?match([{a,{a,9},91}], ok(Cursor, [])),
+ %% Lock
+
+ Pid3 = spawn(fun() -> ?match(ok, ok(GrabLock, [Self])) end),
+ ?match(locked,receive locked -> locked after 5000 -> timeout end), %% Wait
+ put(count,0),
+
+ ?match({1,[{a,{a,9},91}]}, ok(Restart,[Pid3, Cursor])),
+ QC1 = ok(fun() -> qlc:cursor(Q1) end, []),
+ ?match({'EXIT', _}, qlc:next_answers(QC1)),
+ ?match({aborted,_}, ok(fun()->qlc:next_answers(QC1)end,[])),
+ ?verify_mnesia(Ns, []).
+
+
+frag(suite) -> [];
+frag(doc) -> [];
+frag(Config) ->
+ Ns = init_testcases(ram_copies,Config),
+ QA = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(a),"
+ " Val == 90 + Key]">>),
+ QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
+ " Key == 90 + Val]">>),
+
+ Activate =
+ fun(Tab) ->
+ ?match({atomic,ok},mnesia:change_table_frag(Tab, {activate, []})),
+ Dist = mnesia_frag_test:frag_dist(Tab),
+ ?match({atomic,ok},mnesia:change_table_frag(Tab,{add_frag,Dist}))
+ end,
+ Activate(a),
+ Activate(b),
+
+ Fun = fun(Tab) -> mnesia:table_info(Tab, frag_names) end,
+ FTs = mnesia:activity(sync_dirty, Fun, [a], mnesia_frag) ++
+ mnesia:activity(sync_dirty, Fun, [b], mnesia_frag),
+ Size = fun(Tab) -> mnesia:dirty_rpc(Tab, mnesia, table_info, [Tab,size]) end,
+
+ %% Verify that all data doesn't belong to the same frag.
+ ?match([], [{Tab,Size(Tab)} || Tab <- FTs,
+ Size(Tab) =< 0]),
+
+ FA = fun() -> qlc:e(QA) end,
+ FB = fun() -> qlc:e(QB) end,
+ ?match([{a,{a,5},95}], mnesia:activity(transaction,FA,[],mnesia_frag)),
+ ?match([{b,{b,95},5}], mnesia:activity(transaction,FB,[],mnesia_frag)),
+
+ ?verify_mnesia(Ns, []).
+
+info(suite) -> [];
+info(doc) -> [];
+info(Config) ->
+ Ns = init_testcases(ram_copies, Config),
+ Q1 = handle(recs(),
+ <<"[Q || Q = #a{k={_,9}} <- mnesia:table(a)]"
+ >>),
+
+ Q2 = handle(recs(),
+ <<"[Q || Q = #a{k={a,9}} <- mnesia:table(a)]"
+ >>),
+
+ Q3 = handle(recs(),
+ <<"[Q || Q = #a{v=91} <- mnesia:table(a)]"
+ >>),
+
+ %% FIXME compile and check results!
+
+ ?match(ok,io:format("~s~n",[qlc:info(Q1)])),
+ ?match(ok,io:format("~s~n",[qlc:info(Q2)])),
+ ?match(ok,io:format("~s~n",[qlc:info(Q3)])),
+
+ ?verify_mnesia(Ns, []).
+
+ok(Fun,A) ->
+ case mnesia:transaction(Fun,A) of
+ {atomic, R} -> R;
+ E -> E
+ end.
+
+
+mnesia_down(suite) -> [];
+mnesia_down(doc) ->
+ ["Test bug OTP-7968, which crashed mnesia when a"
+ "mnesia_down came after qlc had been invoked"];
+mnesia_down(Config) when is_list(Config) ->
+ [N1,N2] = init_testcases(ram_copies,Config),
+ QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
+ " Val == Key - 90]">>),
+
+ Tester = self(),
+
+ Eval = fun() ->
+ Cursor = qlc:cursor(QB), %% Forces another process
+ Res = qlc:next_answers(Cursor),
+ Tester ! {qlc, self(), Res},
+ {Mod, Tid, Ts} = get(mnesia_activity_state),
+ receive
+ continue ->
+ io:format("Continuing ~p ~p ~n",[self(), {Mod, Tid, Ts}]),
+ io:format("ETS ~p~n",[ets:tab2list(element(2,Ts))]),
+ io:format("~p~n",[process_info(self(),messages)]),
+ Res
+ end
+ end,
+ spawn(fun() -> TransRes = mnesia:transaction(Eval), Tester ! {test,TransRes} end),
+
+ TMInfo = fun() ->
+ TmInfo = mnesia_tm:get_info(5000),
+ mnesia_tm:display_info(user, TmInfo)
+ end,
+ receive
+ {qlc, QPid, QRes} ->
+ ?match([{b,{b,95},5}], QRes),
+ TMInfo(),
+ mnesia_test_lib:kill_mnesia([N2]),
+ %%timer:sleep(1000),
+ QPid ! continue
+ after 2000 ->
+ exit(timeout1)
+ end,
+
+ receive
+ {test, QRes2} ->
+ ?match({atomic, [{b,{b,95},5}]}, QRes2)
+ after 2000 ->
+ exit(timeout2)
+ end,
+
+ ?verify_mnesia([N1], [N2]).
+
+
+nested_qlc(suite) -> [];
+nested_qlc(doc) ->
+ ["Test bug in OTP-7968 (the second problem) where nested"
+ "transaction don't work as expected"];
+nested_qlc(Config) when is_list(Config) ->
+ Ns = init_testcases(ram_copies,Config),
+ Res = as_with_bs(),
+ ?match([_|_], Res),
+ top_as_with_some_bs(10),
+
+ ?verify_mnesia(Ns, []).
+
+
+%% Code from Daniel
+bs_by_a_id(A_id) ->
+ find(qlc:q([ B || B={_,_,F_id} <- mnesia:table(b), F_id == A_id])).
+
+as_with_bs() ->
+ find(qlc:q([ {A,bs_by_a_id(Id)} ||
+ A = {_, {a,Id}, _} <- mnesia:table(a)])).
+
+top_as_with_some_bs(Limit) ->
+ top(
+ qlc:q([ {A,bs_by_a_id(Id)} ||
+ A = {_, {a,Id}, _} <- mnesia:table(a)]),
+ Limit,
+ fun(A1,A2) -> A1 < A2 end
+ ).
+
+% --- utils
+
+find(Q) ->
+ F = fun() -> qlc:e(Q) end,
+ {atomic, Res} = mnesia:transaction(F),
+ Res.
+
+% --- it returns top Limit results from query Q ordered by Order sort function
+top(Q, Limit, Order) ->
+ Do = fun() ->
+ OQ = qlc:sort(Q, [{order,Order}]),
+ QC = qlc:cursor(OQ),
+ Res = qlc:next_answers(QC, Limit),
+ qlc:delete_cursor(QC),
+ Res
+ end,
+ {atomic, Res} = mnesia:transaction(Do),
+ Res.
+
+%% To keep mnesia suite backward compatible,
+%% we compile the queries in runtime when qlc is available
+%% Compiles and returns a handle to a qlc
+handle(Expr) ->
+ handle(<<>>,Expr).
+handle(Records,Expr) ->
+ case catch handle2(Records,Expr) of
+ {ok, Handle} ->
+ Handle;
+ Else ->
+ ?match(ok, Else)
+ end.
+
+handle2(Records,Expr) ->
+ {FN,Mod} = temp_name(),
+ ModStr = list_to_binary("-module(" ++ atom_to_list(Mod) ++ ").\n"),
+ Prog = <<
+ ModStr/binary,
+ "-include_lib(\"stdlib/include/qlc.hrl\").\n",
+ "-export([tmp/0]).\n",
+ Records/binary,"\n",
+ "tmp() ->\n",
+%% " _ = (catch throw(fvalue_not_reset)),"
+ " qlc:q( ",
+ Expr/binary,").\n">>,
+
+ ?match(ok,file:write_file(FN,Prog)),
+ {ok,Forms} = epp:parse_file(FN,"",""),
+ {ok,Mod,Bin} = compile:forms(Forms),
+ code:load_binary(Mod,FN,Bin),
+ {ok, Mod:tmp()}.
+
+setup(Config) ->
+ put(mts_config,Config),
+ put(mts_tf_counter,0).
+
+temp_name() ->
+ Conf = get(mts_config),
+ C = get(mts_tf_counter),
+ put(mts_tf_counter,C+1),
+ {filename:join([proplists:get_value(priv_dir,Conf, "."),
+ "tempfile"++integer_to_list(C)++".tmp"]),
+ list_to_atom("tmp" ++ integer_to_list(C))}.
diff --git a/lib/mnesia/test/mnesia_recovery_test.erl b/lib/mnesia/test/mnesia_recovery_test.erl
new file mode 100644
index 0000000000..625e6e824c
--- /dev/null
+++ b/lib/mnesia/test/mnesia_recovery_test.erl
@@ -0,0 +1,1638 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_recovery_test).
+-author('[email protected]').
+-compile([export_all]).
+
+-include("mnesia_test_lib.hrl").
+-include_lib("kernel/include/file.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-define(receive_messages(Msgs), receive_messages(Msgs, ?FILE, ?LINE)).
+
+% First Some debug logging
+-define(dgb, true).
+-ifdef(dgb).
+-define(dl(X, Y), ?verbose("**TRACING: " ++ X ++ "**~n", Y)).
+-else.
+-define(dl(X, Y), ok).
+-endif.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [{group, mnesia_down}, {group, explicit_stop},
+ coord_dies, {group, schema_trans}, {group, async_dirty},
+ {group, sync_dirty}, {group, sym_trans},
+ {group, asym_trans}, after_full_disc_partition,
+ {group, after_corrupt_files}, disc_less, garb_decision,
+ system_upgrade].
+
+groups() ->
+ [{schema_trans, [],
+ [{mnesia_schema_recovery_test, all}]},
+ {mnesia_down, [],
+ [{group, mnesia_down_during_startup},
+ {group, master_node_tests}, {group, read_during_down},
+ {group, with_checkpoint}, delete_during_start]},
+ {master_node_tests, [],
+ [no_master_2, no_master_3, one_master_2, one_master_3,
+ two_master_2, two_master_3, all_master_2,
+ all_master_3]},
+ {read_during_down, [],
+ [dirty_read_during_down, trans_read_during_down]},
+ {mnesia_down_during_startup, [],
+ [mnesia_down_during_startup_disk_ram,
+ mnesia_down_during_startup_init_ram,
+ mnesia_down_during_startup_init_disc,
+ mnesia_down_during_startup_init_disc_only,
+ mnesia_down_during_startup_tm_ram,
+ mnesia_down_during_startup_tm_disc,
+ mnesia_down_during_startup_tm_disc_only]},
+ {with_checkpoint, [],
+ [with_checkpoint_same, with_checkpoint_other]},
+ {explicit_stop, [], [explicit_stop_during_snmp]},
+ {sym_trans, [],
+ [sym_trans_before_commit_kill_coord_node,
+ sym_trans_before_commit_kill_coord_pid,
+ sym_trans_before_commit_kill_part_after_ask,
+ sym_trans_before_commit_kill_part_before_ask,
+ sym_trans_after_commit_kill_coord_node,
+ sym_trans_after_commit_kill_coord_pid,
+ sym_trans_after_commit_kill_part_after_ask,
+ sym_trans_after_commit_kill_part_do_commit_pre,
+ sym_trans_after_commit_kill_part_do_commit_post]},
+ {sync_dirty, [],
+ [sync_dirty_pre_kill_part,
+ sync_dirty_pre_kill_coord_node,
+ sync_dirty_pre_kill_coord_pid,
+ sync_dirty_post_kill_part,
+ sync_dirty_post_kill_coord_node,
+ sync_dirty_post_kill_coord_pid]},
+ {async_dirty, [],
+ [async_dirty_pre_kill_part,
+ async_dirty_pre_kill_coord_node,
+ async_dirty_pre_kill_coord_pid,
+ async_dirty_post_kill_part,
+ async_dirty_post_kill_coord_node,
+ async_dirty_post_kill_coord_pid]},
+ {asym_trans, [],
+ [asym_trans_kill_part_ask,
+ asym_trans_kill_part_commit_vote,
+ asym_trans_kill_part_pre_commit,
+ asym_trans_kill_part_log_commit,
+ asym_trans_kill_part_do_commit,
+ asym_trans_kill_coord_got_votes,
+ asym_trans_kill_coord_pid_got_votes,
+ asym_trans_kill_coord_log_commit_rec,
+ asym_trans_kill_coord_pid_log_commit_rec,
+ asym_trans_kill_coord_log_commit_dec,
+ asym_trans_kill_coord_pid_log_commit_dec,
+ asym_trans_kill_coord_rec_acc_pre_commit_log_commit,
+ asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit,
+ asym_trans_kill_coord_rec_acc_pre_commit_done_commit,
+ asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit]},
+ {after_corrupt_files, [],
+ [after_corrupt_files_decision_log_head,
+ after_corrupt_files_decision_log_tail,
+ after_corrupt_files_latest_log_head,
+ after_corrupt_files_latest_log_tail,
+ after_corrupt_files_table_dat_head,
+ after_corrupt_files_table_dat_tail,
+ after_corrupt_files_schema_dat_head,
+ after_corrupt_files_schema_dat_tail]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+tpcb_config(ReplicaType, _NodeConfig, Nodes) ->
+ [{n_branches, 5},
+ {n_drivers_per_node, 5},
+ {replica_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {use_running_mnesia, true},
+ {report_interval, infinity},
+ {n_accounts_per_branch, 20},
+ {replica_type, ReplicaType}].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+
+no_master_2(suite) -> [];
+no_master_2(Config) when is_list(Config) -> mnesia_down_2(no, Config).
+
+no_master_3(suite) -> [];
+no_master_3(Config) when is_list(Config) -> mnesia_down_3(no, Config).
+
+one_master_2(suite) -> [];
+one_master_2(Config) when is_list(Config) -> mnesia_down_2(one, Config).
+
+one_master_3(suite) -> [];
+one_master_3(Config) when is_list(Config) -> mnesia_down_3(one, Config).
+
+two_master_2(suite) -> [];
+two_master_2(Config) when is_list(Config) -> mnesia_down_2(two, Config).
+
+two_master_3(suite) -> [];
+two_master_3(Config) when is_list(Config) -> mnesia_down_3(two, Config).
+
+all_master_2(suite) -> [];
+all_master_2(Config) when is_list(Config) -> mnesia_down_2(all, Config).
+
+all_master_3(suite) -> [];
+all_master_3(Config) when is_list(Config) -> mnesia_down_3(all, Config).
+
+mnesia_down_2(Masters, Config) ->
+ Nodes = [N1, N2] = ?acquire_nodes(2, Config),
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab2, [{disc_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab3, [{disc_only_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab4, [{ram_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab5, [{ram_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab6, [{disc_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab7, [{disc_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab8, [{disc_only_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab9, [{disc_only_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab10, [{ram_copies, [N1]}, {disc_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab11, [{ram_copies, [N2]}, {disc_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab12, [{ram_copies, [N1]}, {disc_only_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab13, [{ram_copies, [N2]}, {disc_only_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab14, [{disc_only_copies, [N1]}, {disc_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab15, [{disc_only_copies, [N2]}, {disc_copies, [N1]}])),
+
+ Tabs = [tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8,
+ tab9, tab10, tab11, tab12, tab13, tab14, tab15],
+ [?match(ok, rpc:call(Node, mnesia, wait_for_tables, [Tabs, 10000])) || Node <- Nodes],
+ [insert_data(Tab, 20) || Tab <- Tabs],
+
+ VTabs =
+ case Masters of
+ no ->
+ Tabs -- [tab4, tab5]; % ram copies
+ one ->
+ ?match(ok, rpc:call(N1, mnesia, set_master_nodes, [[N1]])),
+ Tabs -- [tab1, tab4, tab5, tab10, tab12]; % ram_copies
+ two ->
+ ?match(ok, rpc:call(N1, mnesia, set_master_nodes, [Nodes])),
+ Tabs -- [tab4, tab5];
+ all ->
+ [?match(ok, rpc:call(Node, mnesia, set_master_nodes, [[Node]])) || Node <- Nodes],
+ Tabs -- [tab1, tab4, tab5, tab10, tab11, tab12, tab13]
+ end,
+
+ mnesia_test_lib:kill_mnesia([N1]),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ [?match(ok, rpc:call(N1, ?MODULE, verify_data, [Tab, 20])) || Tab <- VTabs],
+ [?match(ok, rpc:call(N2, ?MODULE, verify_data, [Tab, 20])) || Tab <- VTabs],
+ ?verify_mnesia(Nodes, []).
+
+mnesia_down_3(Masters, Config) ->
+ Nodes = [N1, N2, N3] = ?acquire_nodes(3, Config),
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab2, [{disc_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab3, [{disc_only_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab4, [{ram_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab5, [{ram_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab16, [{ram_copies, [N3]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab6, [{disc_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab7, [{disc_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab17, [{disc_copies, [N3]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab8, [{disc_only_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab9, [{disc_only_copies, [N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab18, [{disc_only_copies, [N3]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab10, [{ram_copies, [N1]}, {disc_copies, [N2, N3]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab11, [{ram_copies, [N2]}, {disc_copies, [N3, N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab19, [{ram_copies, [N3]}, {disc_copies, [N1, N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab12, [{ram_copies, [N1]}, {disc_only_copies, [N2, N3]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab13, [{ram_copies, [N2]}, {disc_only_copies, [N3, N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab20, [{ram_copies, [N3]}, {disc_only_copies, [N1, N2]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab14, [{disc_only_copies, [N1]}, {disc_copies, [N2, N3]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab15, [{disc_only_copies, [N2]}, {disc_copies, [N3, N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab21, [{disc_only_copies, [N3]}, {disc_copies, [N1, N2]}])),
+
+ Tabs = [tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8,
+ tab9, tab10, tab11, tab12, tab13, tab14, tab15,
+ tab16, tab17, tab18, tab19, tab20, tab21],
+ [?match(ok, rpc:call(Node, mnesia, wait_for_tables, [Tabs, 10000])) || Node <- Nodes],
+ [insert_data(Tab, 20) || Tab <- Tabs],
+
+ VTabs =
+ case Masters of
+ no ->
+ Tabs -- [tab4, tab5, tab16]; % ram copies
+ one ->
+ ?match(ok, rpc:call(N1, mnesia, set_master_nodes, [[N1]])),
+ Tabs -- [tab1, tab4, tab5, tab16, tab10, tab12]; % ram copies
+ two ->
+ ?match(ok, rpc:call(N1, mnesia, set_master_nodes, [Nodes])),
+ Tabs -- [tab4, tab5, tab16]; % ram copies
+ all ->
+ [?match(ok, rpc:call(Node, mnesia, set_master_nodes, [[Node]])) || Node <- Nodes],
+ Tabs -- [tab1, tab4, tab5, tab16, tab10,
+ tab11, tab19, tab12, tab13, tab20] % ram copies
+ end,
+
+ mnesia_test_lib:kill_mnesia([N1]),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N3])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2, N1])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N2, N3])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ ?match([], mnesia_test_lib:kill_mnesia([N1, N3])),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, Tabs)),
+
+ [?match(ok, rpc:call(N1, ?MODULE, verify_data, [Tab, 20])) || Tab <- VTabs],
+ [?match(ok, rpc:call(N2, ?MODULE, verify_data, [Tab, 20])) || Tab <- VTabs],
+ [?match(ok, rpc:call(N3, ?MODULE, verify_data, [Tab, 20])) || Tab <- VTabs],
+
+ ?verify_mnesia(Nodes, []).
+
+
+
+dirty_read_during_down(suite) ->
+ [];
+dirty_read_during_down(Config) when is_list(Config) ->
+ read_during_down(dirty, Config).
+
+trans_read_during_down(suite) ->
+ [];
+trans_read_during_down(Config) when is_list(Config) ->
+ read_during_down(trans, Config).
+
+
+read_during_down(Op, Config) when is_list(Config) ->
+ Ns = [N1|TNs] = ?acquire_nodes(3, Config),
+ Tabs = [ram, disc, disco],
+
+ ?match({atomic, ok}, mnesia:create_table(ram, [{ram_copies, TNs}])),
+ ?match({atomic, ok}, mnesia:create_table(disc, [{disc_copies, TNs}])),
+ ?match({atomic, ok}, mnesia:create_table(disco, [{disc_only_copies, TNs}])),
+
+ %% Create some work for mnesia_controller when a node goes down
+ [{atomic, ok} = mnesia:create_table(list_to_atom("temp" ++ integer_to_list(N)),
+ [{ram_copies, Ns}]) || N <- lists:seq(1, 50)],
+
+ Write = fun(Tab) -> mnesia:write({Tab, key, val}) end,
+ ?match([ok,ok,ok],
+ [mnesia:sync_dirty(Write, [Tab]) || Tab <- Tabs]),
+
+ Readers = [spawn_link(N1, ?MODULE, reader, [Tab, Op]) || Tab <- Tabs],
+ [_|_] = W2R= [mnesia:table_info(Tab, where_to_read) || Tab <- Tabs],
+ ?log("W2R ~p~n", [W2R]),
+ loop_and_kill_mnesia(10, hd(W2R), Tabs),
+ [Pid ! self() || Pid <- Readers],
+ ?match([ok, ok, ok], [receive ok -> ok after 1000 -> {Pid, mnesia_lib:dist_coredump()} end || Pid <- Readers]),
+ ?verify_mnesia(Ns, []).
+
+reader(Tab, OP) ->
+ Res = case OP of
+ dirty ->
+ catch mnesia:dirty_read({Tab, key});
+ trans ->
+ Read = fun() -> mnesia:read({Tab, key}) end,
+ {_, Temp} = mnesia:transaction(Read),
+ Temp
+ end,
+ case Res of
+ [{Tab, key, val}] -> ok;
+ Else ->
+ ?error("Expected ~p Got ~p ~n", [[{Tab, key, val}], Else]),
+ erlang:error(test_failed)
+ end,
+ receive Pid ->
+ Pid ! ok
+ after 50 ->
+ reader(Tab, OP)
+ end.
+
+loop_and_kill_mnesia(0, _Node, _Tabs) -> ok;
+loop_and_kill_mnesia(N, Node, Tabs) ->
+ mnesia_test_lib:kill_mnesia([Node]),
+ timer:sleep(100),
+ ?match([], mnesia_test_lib:start_mnesia([Node], Tabs)),
+ [KN | _] = W2R= [mnesia:table_info(Tab, where_to_read) || Tab <- Tabs],
+ ?match([KN, KN,KN], W2R),
+ timer:sleep(100),
+ loop_and_kill_mnesia(N-1, KN, Tabs).
+
+
+mnesia_down_during_startup_disk_ram(suite) -> [];
+mnesia_down_during_startup_disk_ram(Config) when is_list(Config)->
+ [Node1, Node2] = ?acquire_nodes(2, Config ++
+ [{tc_timeout, timer:minutes(2)}]),
+ Tab = down_during_startup,
+ Def = [{ram_copies, [Node2]}, {disc_copies, [Node1]}],
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match(ok, mnesia:dirty_write({Tab, 876234, test_ok})),
+ timer:sleep(500),
+ mnesia_test_lib:kill_mnesia([Node1, Node2]),
+ timer:sleep(500),
+ mnesia_test_lib:start_mnesia([Node1, Node2], [Tab]),
+ mnesia_test_lib:kill_mnesia([Node1]),
+ timer:sleep(500),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [Tab])),
+ ?match([{Tab, 876234, test_ok}], mnesia:dirty_read({Tab,876234})),
+ ?verify_mnesia([Node1, Node2], []).
+
+mnesia_down_during_startup_init_ram(suite) -> [];
+mnesia_down_during_startup_init_ram(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ DP = {mnesia_loader, do_get_network_copy},
+ Type = ram_copies,
+ mnesia_down_during_startup2(Config, Type, DP, self()).
+
+mnesia_down_during_startup_init_disc(suite) -> [];
+mnesia_down_during_startup_init_disc(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ DP = {mnesia_loader, do_get_network_copy},
+ Type = disc_copies,
+ mnesia_down_during_startup2(Config, Type, DP, self()).
+
+mnesia_down_during_startup_init_disc_only(suite) -> [];
+mnesia_down_during_startup_init_disc_only(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ DP = {mnesia_loader, do_get_network_copy},
+ Type = disc_only_copies,
+ mnesia_down_during_startup2(Config, Type, DP, self()).
+
+mnesia_down_during_startup_tm_ram(suite) -> [];
+mnesia_down_during_startup_tm_ram(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ DP = {mnesia_tm, init},
+ Type = ram_copies,
+ mnesia_down_during_startup2(Config, Type, DP, self()).
+
+mnesia_down_during_startup_tm_disc(suite) -> [];
+mnesia_down_during_startup_tm_disc(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ DP = {mnesia_tm, init},
+ Type = disc_copies,
+ mnesia_down_during_startup2(Config, Type, DP, self()).
+
+mnesia_down_during_startup_tm_disc_only(suite) -> [];
+mnesia_down_during_startup_tm_disc_only(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ DP = {mnesia_tm, init},
+ Type = disc_only_copies,
+ mnesia_down_during_startup2(Config, Type, DP, self()).
+
+mnesia_down_during_startup2(Config, ReplicaType, Debug_Point, _Father) ->
+ ?log("TC~n mnesia_down_during_startup with type ~w and stops at ~w~n",
+ [ReplicaType, Debug_Point]),
+ Tpcb_tabs = [history,teller,account,branch],
+ Nodes = ?acquire_nodes(2, Config),
+ Node1 = hd(Nodes),
+ {success, [A]} = ?start_activities([Node1]),
+ TpcbConfig = tpcb_config(ReplicaType, 2, Nodes),
+ mnesia_tpcb:init(TpcbConfig),
+ A ! fun () -> mnesia_tpcb:run(TpcbConfig) end,
+ ?match_receive(timeout),
+ timer:sleep(timer:seconds(10)), % Let tpcb run for a while
+ mnesia_tpcb:stop(),
+ ?match(ok, mnesia_tpcb:verify_tabs()),
+ mnesia_test_lib:kill_mnesia([Node1]),
+ timer:sleep(timer:seconds(2)),
+ Self = self(),
+ TestFun = fun(_MnesiaEnv, _EvalEnv) ->
+ ?deactivate_debug_fun(Debug_Point),
+ Self ! fun_done,
+ spawn(mnesia_test_lib, kill_mnesia, [[Node1]])
+ end,
+ ?activate_debug_fun(Debug_Point, TestFun, []), % Kill when debug has been reached
+ mnesia:start(),
+ Res = receive fun_done -> ok after timer:minutes(3) -> timeout end, % Wait till it's killed
+ ?match(ok, Res),
+ ?match(ok, timer:sleep(timer:seconds(2))), % Wait a while, at least till it dies;
+ ?match([], mnesia_test_lib:start_mnesia([Node1], Tpcb_tabs)),
+ ?match(ok, mnesia_tpcb:verify_tabs()), % Verify it
+ ?verify_mnesia(Nodes, []).
+
+
+
+with_checkpoint_same(suite) -> [];
+with_checkpoint_same(Config) when is_list(Config) ->
+ with_checkpoint(Config, same).
+
+with_checkpoint_other(suite) -> [];
+with_checkpoint_other(Config) when is_list(Config) ->
+ with_checkpoint(Config, other).
+
+with_checkpoint(Config, Type) when is_list(Config) ->
+ Nodes = [Node1, Node2] = ?acquire_nodes(2, Config),
+ Kill = case Type of
+ same -> %% Node1 is the one used for creating the checkpoint
+ Node1; %% and which we bring down
+ other ->
+ Node2 %% Here we bring node2 down..
+ end,
+
+ ?match({atomic, ok}, mnesia:create_table(ram, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(disc, [{disc_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(disco, [{disc_only_copies, Nodes}])),
+ Tabs = [ram, disc, disco],
+
+ ?match({ok, sune, _}, mnesia:activate_checkpoint([{name, sune},
+ {max, mnesia:system_info(tables)},
+ {ram_overrides_dump, true}])),
+
+ ?match([], check_retainers(sune, Nodes)),
+
+ ?match(ok, mnesia:deactivate_checkpoint(sune)),
+ ?match([], check_chkp(Nodes)),
+
+ timer:sleep(500), %% Just to help debugging the io:formats now comes in the
+ %% correct order... :-)
+
+ ?match({ok, sune, _}, mnesia:activate_checkpoint([{name, sune},
+ {max, mnesia:system_info(tables)},
+ {ram_overrides_dump, true}])),
+
+ [[mnesia:dirty_write({Tab,Key,Key}) || Key <- lists:seq(1,10)] || Tab <- Tabs],
+
+ mnesia_test_lib:kill_mnesia([Kill]),
+ timer:sleep(100),
+ mnesia_test_lib:start_mnesia([Kill], Tabs),
+ io:format("Mnesia on ~p started~n", [Kill]),
+ ?match([], check_retainers(sune, Nodes)),
+ ?match(ok, mnesia:deactivate_checkpoint(sune)),
+ ?match([], check_chkp(Nodes)),
+
+ case Kill of
+ Node1 ->
+ ignore;
+ Node2 ->
+ mnesia_test_lib:kill_mnesia([Kill]),
+ timer:sleep(500), %% Just to help debugging
+ ?match({ok, sune, _}, mnesia:activate_checkpoint([{name, sune},
+ {max, mnesia:system_info(tables)},
+ {ram_overrides_dump, true}])),
+
+ [[mnesia:dirty_write({Tab,Key,Key+2}) || Key <- lists:seq(1,10)] ||
+ Tab <- Tabs],
+
+ mnesia_test_lib:start_mnesia([Kill], Tabs),
+ io:format("Mnesia on ~p started ~n", [Kill]),
+ ?match([], check_retainers(sune, Nodes)),
+ ?match(ok, mnesia:deactivate_checkpoint(sune)),
+ ?match([], check_chkp(Nodes)),
+ ok
+ end,
+ ?verify_mnesia(Nodes, []).
+
+check_chkp(Nodes) ->
+ {Good, Bad} = rpc:multicall(Nodes, ?MODULE, check, []),
+ lists:flatten(Good ++ Bad).
+
+check() ->
+ [PCP] = ets:match_object(mnesia_gvar, {pending_checkpoint_pids, '_'}),
+ [PC] = ets:match_object(mnesia_gvar, {pending_checkpoints, '_'}),
+ [CPN] = ets:match_object(mnesia_gvar, {checkpoints, '_'}),
+ F = lists:filter(fun({_, []}) -> false; (_W) -> true end,
+ [PCP,PC,CPN]),
+ CPP = ets:match_object(mnesia_gvar, {{checkpoint, '_'}, '_'}),
+ Rt = ets:match_object(mnesia_gvar, {{'_', {retainer, '_'}}, '_'}),
+ F ++ CPP ++ Rt.
+
+
+check_retainers(CHP, Nodes) ->
+ {[R1,R2], []} = rpc:multicall(Nodes, ?MODULE, get_all_retainers, [CHP]),
+ (R1 -- R2) ++ (R2 -- R1).
+
+get_all_retainers(CHP) ->
+ Tabs = mnesia:system_info(local_tables),
+ Iter = fun(Tab) ->
+ {ok, Res} =
+ mnesia_checkpoint:iterate(CHP, Tab, fun(R, A) -> [R|A] end, [],
+ retainer, checkpoint),
+%% io:format("Retainer content ~w ~n", [Res]),
+ Res
+ end,
+ Elements = [Iter(Tab) || Tab <- Tabs],
+ lists:sort(lists:flatten(Elements)).
+
+delete_during_start(doc) ->
+ ["Test that tables can be delete during start, hopefully with tables"
+ " in the loader queue or soon to be"];
+delete_during_start(suite) -> [];
+delete_during_start(Config) when is_list(Config) ->
+ [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config),
+ Tabs = [list_to_atom("tab" ++ integer_to_list(I)) || I <- lists:seq(1, 30)],
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, N2, ram_copies)),
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, N3, ram_copies)),
+
+ [?match({atomic, ok},mnesia:create_table(Tab, [{ram_copies,Nodes}])) || Tab <- Tabs],
+ lists:foldl(fun(Tab, I) ->
+ ?match({atomic, ok},
+ mnesia:change_table_load_order(Tab,I)),
+ I+1
+ end, 1, Tabs),
+ mnesia_test_lib:kill_mnesia([N2,N3]),
+%% timer:sleep(500),
+ ?match({[ok,ok],[]}, rpc:multicall([N2,N3], mnesia,start,
+ [[{extra_db_nodes,[N1]}]])),
+ [Tab1,Tab2,Tab3|_] = Tabs,
+ ?match({atomic, ok}, mnesia:delete_table(Tab1)),
+ ?match({atomic, ok}, mnesia:delete_table(Tab2)),
+
+ ?log("W4T ~p~n", [rpc:multicall([N2,N3], mnesia, wait_for_tables, [[Tab1,Tab2,Tab3],1])]),
+
+ Remain = Tabs--[Tab1,Tab2],
+ ?match(ok, rpc:call(N2, mnesia, wait_for_tables, [Remain,10000])),
+ ?match(ok, rpc:call(N3, mnesia, wait_for_tables, [Remain,10000])),
+
+ ?match(ok, rpc:call(N2, ?MODULE, verify_where2read, [Remain])),
+ ?match(ok, rpc:call(N3, ?MODULE, verify_where2read, [Remain])),
+
+ ?verify_mnesia(Nodes, []).
+
+verify_where2read([Tab|Tabs]) ->
+ true = (node() == mnesia:table_info(Tab,where_to_read)),
+ verify_where2read(Tabs);
+verify_where2read([]) -> ok.
+
+
+%%-------------------------------------------------------------------------------------------
+%% This is a bad implementation, but at least gives a indication if something is wrong
+explicit_stop_during_snmp(suite) -> [];
+explicit_stop_during_snmp(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(2, Config),
+ [Node1, Node2] = Nodes,
+ Tab = snmp_tab,
+ Def = [{attributes, [key, value]},
+ {snmp, [{key, integer}]},
+ {mnesia_test_lib:storage_type(disc_copies, Config),
+ [Node1, Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write({Tab, 1, 1}) end)),
+
+ Do_trans_Pid1 = spawn_link(Node2, ?MODULE, do_trans_loop, [Tab, self()]),
+ Do_trans_Pid2 = spawn_link(?MODULE, do_trans_loop, [Tab, self()]),
+ Start_stop_Pid = spawn_link(?MODULE, start_stop, [Node1, 10, self()]),
+ receive
+ test_done ->
+ ok
+ after timer:minutes(5) ->
+ ?error("test case time out~n", [])
+ end,
+ ?verify_mnesia(Nodes, []),
+ exit(Do_trans_Pid1, kill),
+ exit(Do_trans_Pid2, kill),
+ exit(Start_stop_Pid, kill),
+ ok.
+
+do_trans_loop(Tab, Father) ->
+ %% Do not trap exit
+ do_trans_loop2(Tab, Father).
+do_trans_loop2(Tab, Father) ->
+ Trans =
+ fun() ->
+ [{Tab, 1, Val}] = mnesia:read({Tab, 1}),
+ mnesia:write({Tab, 1, Val + 1})
+ end,
+ case mnesia:transaction(Trans) of
+ {atomic, ok} ->
+ timer:sleep(200),
+ do_trans_loop2(Tab, Father);
+ {aborted, {node_not_running, N}} when N == node() ->
+ timer:sleep(200),
+ do_trans_loop2(Tab, Father);
+ {aborted, {no_exists, Tab}} ->
+ timer:sleep(200),
+ do_trans_loop2(Tab, Father);
+ Else ->
+ ?error("Transaction failed: ~p ~n", [Else]),
+ Father ! test_done,
+ exit(shutdown)
+ end.
+
+start_stop(_Node1, 0, Father) ->
+ Father ! test_done,
+ exit(shutdown);
+start_stop(Node1, N, Father) when N > 0->
+ timer:sleep(timer:seconds(5)),
+ ?match(stopped, rpc:call(Node1, mnesia, stop, [])),
+ timer:sleep(timer:seconds(2)),
+ ?match([], mnesia_test_lib:start_mnesia([Node1])),
+ start_stop(Node1, N-1, Father).
+
+coord_dies(suite) -> [];
+coord_dies(doc) -> [""];
+coord_dies(Config) when is_list(Config) ->
+ Nodes = [N1, N2] = ?acquire_nodes(2, Config),
+ ?match({atomic, ok}, mnesia:create_table(tab1, [{ram_copies, Nodes}])),
+ ?match({atomic, ok}, mnesia:create_table(tab2, [{ram_copies, [N1]}])),
+ ?match({atomic, ok}, mnesia:create_table(tab3, [{ram_copies, [N2]}])),
+ Tester = self(),
+
+ U1 = fun(Tab) ->
+ [{Tab,key,Val}] = mnesia:read(Tab,key,write),
+ mnesia:write({Tab,key, Val+1}),
+ Tester ! {self(),continue},
+ receive
+ continue -> exit(crash)
+ end
+ end,
+ U2 = fun(Tab) ->
+ [{Tab,key,Val}] = mnesia:read(Tab,key,write),
+ mnesia:write({Tab,key, Val+1}),
+ mnesia:transaction(U1, [Tab])
+ end,
+ [mnesia:dirty_write(Tab,{Tab,key,0}) || Tab <- [tab1,tab2,tab3]],
+ Pid1 = spawn(fun() -> mnesia:transaction(U2, [tab1]) end),
+ Pid2 = spawn(fun() -> mnesia:transaction(U2, [tab2]) end),
+ Pid3 = spawn(fun() -> mnesia:transaction(U2, [tab3]) end),
+ [receive {Pid,continue} -> ok end || Pid <- [Pid1,Pid2,Pid3]],
+ Pid1 ! continue, Pid2 ! continue, Pid3 ! continue,
+ ?match({atomic,[{_,key,1}]}, mnesia:transaction(fun() -> mnesia:read({tab1,key}) end)),
+ ?match({atomic,[{_,key,1}]}, mnesia:transaction(fun() -> mnesia:read({tab2,key}) end)),
+ ?match({atomic,[{_,key,1}]}, mnesia:transaction(fun() -> mnesia:read({tab3,key}) end)),
+
+ Pid4 = spawn(fun() -> mnesia:transaction(U2, [tab1]) end),
+ Pid5 = spawn(fun() -> mnesia:transaction(U2, [tab2]) end),
+ Pid6 = spawn(fun() -> mnesia:transaction(U2, [tab3]) end),
+ erlang:monitor(process, Pid4),erlang:monitor(process, Pid5),erlang:monitor(process, Pid6),
+
+ [receive {Pid,continue} -> ok end || Pid <- [Pid4,Pid5,Pid6]],
+ exit(Pid4,crash),
+ ?match_receive({'DOWN',_,_,Pid4, _}),
+ ?match({atomic,[{_,key,1}]}, mnesia:transaction(fun() -> mnesia:read({tab1,key}) end)),
+ exit(Pid5,crash),
+ ?match_receive({'DOWN',_,_,Pid5, _}),
+ ?match({atomic,[{_,key,1}]}, mnesia:transaction(fun() -> mnesia:read({tab2,key}) end)),
+ exit(Pid6,crash),
+ ?match_receive({'DOWN',_,_,Pid6, _}),
+ ?match({atomic,[{_,key,1}]}, mnesia:transaction(fun() -> mnesia:read({tab3,key}) end)),
+
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+%kill_after_debug_point(Config, TestCase, {Debug_node, Debug_Point}, TransFun, Tab)
+
+sym_trans_before_commit_kill_coord_node(suite) -> [];
+sym_trans_before_commit_kill_coord_node(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_before_commit_kill_coord,
+ Def = [{attributes, [key, value]}, {ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_sym}},
+ do_sym_trans, [{Tab, Def}], Nodes).
+
+sym_trans_before_commit_kill_coord_pid(suite) -> [];
+sym_trans_before_commit_kill_coord_pid(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_before_commit_kill_coord,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_sym}},
+ do_sym_trans, [{Tab, Def}], Nodes).
+
+sym_trans_before_commit_kill_part_after_ask(suite) -> [];
+sym_trans_before_commit_kill_part_after_ask(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_before_commit_kill_part_after_ask,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(Part1, {Coord, {mnesia_tm, multi_commit_sym}},
+ do_sym_trans, [{Tab, Def}], Nodes).
+
+sym_trans_before_commit_kill_part_before_ask(suite) -> [];
+sym_trans_before_commit_kill_part_before_ask(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_before_commit_kill_part_before_ask,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, doit_ask_commit}},
+ do_sym_trans, [{Tab, Def}], Nodes).
+
+sym_trans_after_commit_kill_coord_node(suite) -> [];
+sym_trans_after_commit_kill_coord_node(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_after_commit_kill_coord,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_sym, post}},
+ do_sym_trans, [{Tab, Def}], Nodes).
+
+sym_trans_after_commit_kill_coord_pid(suite) -> [];
+sym_trans_after_commit_kill_coord_pid(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_after_commit_kill_coord,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_sym, post}},
+ do_sym_trans, [{Tab,Def}], Nodes).
+
+sym_trans_after_commit_kill_part_after_ask(suite) -> [];
+sym_trans_after_commit_kill_part_after_ask(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_after_commit_kill_part_after_ask,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ kill_after_debug_point(Part1, {Coord, {mnesia_tm, multi_commit_sym, post}},
+ do_sym_trans, [{Tab, Def}], Nodes).
+
+sym_trans_after_commit_kill_part_do_commit_pre(suite) -> [];
+sym_trans_after_commit_kill_part_do_commit_pre(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_after_commit_kill_part_do_commit_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, do_commit, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+sym_trans_after_commit_kill_part_do_commit_post(suite) -> [];
+sym_trans_after_commit_kill_part_do_commit_post(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sym_trans_after_commit_kill_part_do_commit_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, do_commit, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+do_sym_trans([Tab], _Fahter) ->
+ ?dl("Starting SYM_TRANS with active debug fun ", []),
+ Trans = fun() ->
+ [{_,_,Val}] = mnesia:read({Tab, 1}),
+ mnesia:write({Tab, 1, Val+1})
+ end,
+ Res = mnesia:transaction(Trans),
+ case Res of
+ {atomic, ok} -> ok;
+ {aborted, _Reason} -> ok;
+ Else -> ?error("Wrong output from mensia:transaction(FUN):~n ~p~n",
+ [Else])
+ end,
+ ?dl("SYM_TRANSACTION done: ~p (deactiv dbgfun) ", [Res]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+sync_dirty_pre_kill_part(suite) -> [];
+sync_dirty_pre_kill_part(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sync_dirty_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sync_dirty,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, sync_dirty, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+sync_dirty_pre_kill_coord_node(suite) -> [];
+sync_dirty_pre_kill_coord_node(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sync_dirty_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sync_dirty,
+ kill_after_debug_point(Coord, {Part1, {mnesia_tm, sync_dirty, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+sync_dirty_pre_kill_coord_pid(suite) -> [];
+sync_dirty_pre_kill_coord_pid(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sync_dirty_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sync_dirty,
+ kill_after_debug_point(coord_pid, {Part1, {mnesia_tm, sync_dirty, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+sync_dirty_post_kill_part(suite) -> [];
+sync_dirty_post_kill_part(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sync_dirty_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sync_dirty,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, sync_dirty, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+sync_dirty_post_kill_coord_node(suite) -> [];
+sync_dirty_post_kill_coord_node(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sync_dirty_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sync_dirty,
+ kill_after_debug_point(Coord, {Part1, {mnesia_tm, sync_dirty, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+sync_dirty_post_kill_coord_pid(suite) -> [];
+sync_dirty_post_kill_coord_pid(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = sync_dirty_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_sync_dirty,
+ kill_after_debug_point(coord_pid, {Part1, {mnesia_tm, sync_dirty, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+do_sync_dirty([Tab], _Father) ->
+ ?dl("Starting SYNC_DIRTY", []),
+ SYNC = fun() ->
+ [{_,_,Val}] = mnesia:read({Tab, 1}),
+ mnesia:write({Tab, 1, Val+1})
+ end,
+ {_, Res} = ?match(ok, mnesia:sync_dirty(SYNC)),
+ ?dl("SYNC_DIRTY done: ~p ", [Res]),
+ ok.
+
+
+async_dirty_pre_kill_part(suite) -> [];
+async_dirty_pre_kill_part(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = async_dirty_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_async_dirty,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, async_dirty, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+async_dirty_pre_kill_coord_node(suite) -> [];
+async_dirty_pre_kill_coord_node(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = async_dirty_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_async_dirty,
+ kill_after_debug_point(Coord, {Part1, {mnesia_tm, async_dirty, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+async_dirty_pre_kill_coord_pid(suite) -> [];
+async_dirty_pre_kill_coord_pid(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = async_dirty_pre,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_async_dirty,
+ kill_after_debug_point(coord_pid, {Part1, {mnesia_tm, async_dirty, pre}},
+ TransFun, [{Tab, Def}], Nodes).
+
+async_dirty_post_kill_part(suite) -> [];
+async_dirty_post_kill_part(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = async_dirty_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_async_dirty,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, async_dirty, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+async_dirty_post_kill_coord_node(suite) -> [];
+async_dirty_post_kill_coord_node(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = async_dirty_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_async_dirty,
+ kill_after_debug_point(Coord, {Part1, {mnesia_tm, async_dirty, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+async_dirty_post_kill_coord_pid(suite) -> [];
+async_dirty_post_kill_coord_pid(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab = async_dirty_post,
+ Def = [{attributes, [key, value]},{ram_copies, [Part2]},{disc_copies, [Coord, Part1]}],
+ TransFun = do_async_dirty,
+ kill_after_debug_point(coord_pid, {Part1, {mnesia_tm, async_dirty, post}},
+ TransFun, [{Tab, Def}], Nodes).
+
+do_async_dirty([Tab], _Fahter) ->
+ ?dl("Starting ASYNC", []),
+ ASYNC = fun() ->
+ [{_,_,Val}] = mnesia:read({Tab, 1}),
+ mnesia:write({Tab, 1, Val+1})
+ end,
+ {_, Res} = ?match(ok, mnesia:async_dirty(ASYNC)),
+ ?dl("ASYNC done: ~p ", [Res]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+
+asym_trans_kill_part_ask(suite) -> [];
+asym_trans_kill_part_ask(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, doit_ask_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_part_commit_vote(suite) -> [];
+asym_trans_kill_part_commit_vote(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, vote_yes}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_part_pre_commit(suite) -> [];
+asym_trans_kill_part_pre_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, pre_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_part_log_commit(suite) -> [];
+asym_trans_kill_part_log_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, log_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_part_do_commit(suite) -> [];
+asym_trans_kill_part_do_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Part1, {Part1, {mnesia_tm, commit_participant, do_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_got_votes(suite) -> [];
+asym_trans_kill_coord_got_votes(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_asym_got_votes}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_pid_got_votes(suite) -> [];
+asym_trans_kill_coord_pid_got_votes(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_asym_got_votes}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_log_commit_rec(suite) -> [];
+asym_trans_kill_coord_log_commit_rec(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_asym_log_commit_rec}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_pid_log_commit_rec(suite) -> [];
+asym_trans_kill_coord_pid_log_commit_rec(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_asym_log_commit_rec}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_log_commit_dec(suite) -> [];
+asym_trans_kill_coord_log_commit_dec(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, multi_commit_asym_log_commit_dec}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_pid_log_commit_dec(suite) -> [];
+asym_trans_kill_coord_pid_log_commit_dec(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, multi_commit_asym_log_commit_dec}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_rec_acc_pre_commit_log_commit(suite) -> [];
+asym_trans_kill_coord_rec_acc_pre_commit_log_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, rec_acc_pre_commit_log_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit(suite) -> [];
+asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, rec_acc_pre_commit_log_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_rec_acc_pre_commit_done_commit(suite) -> [];
+asym_trans_kill_coord_rec_acc_pre_commit_done_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(Coord, {Coord, {mnesia_tm, rec_acc_pre_commit_done_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit(suite) -> [];
+asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit(Config) when is_list(Config) ->
+ ?is_debug_compiled,
+ Nodes = ?acquire_nodes(3, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ [Coord, Part1, Part2] = Nodes,
+ Tab1 = {asym1, [{ram_copies, [Part2]}, {disc_copies, [Coord]}]},
+ Tab2 = {asym2, [{ram_copies, [Coord]}, {disc_copies, [Part1]}]},
+ TransFun = do_asym_trans,
+ kill_after_debug_point(coord_pid, {Coord, {mnesia_tm, rec_acc_pre_commit_done_commit}},
+ TransFun, [Tab1, Tab2], Nodes).
+
+do_asym_trans([Tab1, Tab2 | _R], Garbhandler) ->
+ ?dl("Starting asym trans ", []),
+ ASym_Trans = fun() ->
+ TidTs = {_Mod, Tid, _Store} =
+ mnesia:get_activity_id(),
+ ?verbose("===> asym_trans: ~w~n", [TidTs]),
+ Garbhandler ! {trans_id, Tid},
+ [{_, _, Val1}] = mnesia:read({Tab1, 1}),
+ [{_, _, Val2}] = mnesia:read({Tab2, 1}),
+ mnesia:write({Tab1, 1, Val1+1}),
+ mnesia:write({Tab2, 1, Val2+1})
+ end,
+ Res = mnesia:transaction(ASym_Trans),
+ case Res of
+ {atomic, ok} -> ok;
+ {aborted, _Reason} -> ok;
+ _Else -> ?error("Wrong output from mensia:transaction(FUN):~n ~p~n", [Res])
+ end,
+ ?dl("Asym trans finished with: ~p ", [Res]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+kill_after_debug_point(Kill, {DebugNode, Debug_Point}, TransFun, TabsAndDefs, Nodes) ->
+ [Coord | _rest] = Nodes,
+
+ Create = fun({Tab, Def}) -> ?match({atomic, ok}, mnesia:create_table(Tab, Def)) end,
+ lists:foreach(Create, TabsAndDefs),
+ Tabs = [T || {T, _} <- TabsAndDefs],
+ Write = fun(Tab) -> ?match(ok, mnesia:dirty_write({Tab, 1, 100})) end,
+ lists:foreach(Write, Tabs),
+
+ Self = self(),
+ SyncFun = fun(_Env1, _Env2) -> % Just Sync with test prog
+ Self ! {self(), fun_in_position},
+ ?dl("SyncFun, sending fun_in_position ", []),
+ receive continue ->
+ ?dl("SyncFun received continue ",[]),
+ ok
+ after timer:seconds(60) ->
+ ?error("Timeout in sync_fun on ~p~n", [node()])
+ end
+ end,
+
+ Garb_handler = spawn_link(?MODULE, garb_handler, [[]]),
+
+ ?remote_activate_debug_fun(DebugNode, Debug_Point, SyncFun, []),
+ ?dl("fun_in_position activated at ~p with ~p", [DebugNode, Debug_Point]),
+ %% Spawn and do the transaction
+ Pid = spawn(Coord, ?MODULE, TransFun, [Tabs, Garb_handler]),
+ %% Wait till all the Nodes are in correct position
+ [{StoppedPid,_}] = ?receive_messages([fun_in_position]),
+ ?dl("Received fun_in_position; Removing the debug funs ~p", [DebugNode]),
+ ?remote_deactivate_debug_fun(DebugNode, Debug_Point),
+
+ case Kill of
+ coord_pid ->
+ ?dl("Intentionally killing pid ~p ", [Pid]),
+ exit(Pid, normal);
+ Node ->
+ mnesia_test_lib:kill_mnesia([Node])
+ end,
+
+ StoppedPid ! continue, %% Send continue, it may still be alive
+
+ %% Start and check that the databases are consistent
+ ?dl("Done, Restarting and verifying result ",[]),
+ case Kill of
+ coord_pid -> ok;
+ _ -> % Ok, mnesia on some node was killed restart it
+ timer:sleep(timer:seconds(3)), %% Just let it have the time to die
+ ?match(ok, rpc:call(Kill, mnesia, start, [[]])),
+ ?match(ok, rpc:call(Kill, mnesia, wait_for_tables, [Tabs, 60000]))
+ end,
+ Trans_res = verify_tabs(Tabs, Nodes),
+ case TransFun of
+ do_asym_trans ->
+ %% Verifies that decisions are garbed, only valid for asym_tran
+ Garb_handler ! {get_tids, self()},
+ Tid_list = receive
+ {tids, List} ->
+ ?dl("Fun rec ~w", [List]),
+ List
+ end,
+ garb_of_decisions(Kill, Nodes, Tid_list, Trans_res);
+ _ ->
+ ignore
+ end,
+ ?verify_mnesia(Nodes, []).
+
+garb_of_decisions(Kill, Nodes, Tid_list, Trans_res) ->
+ [Coord, Part1, Part2] = Nodes,
+ %% Check that decision log is empty on all nodes after the trans is finished
+ verify_garb_decision_log(Nodes, Tid_list),
+ case Trans_res of
+ aborted ->
+ %% Check that aborted trans have not been restarted!!
+ ?match(1, length(Tid_list)),
+ %% Check the transient decision logs
+ %% A transaction should only be aborted in an early stage of
+ %% the trans before the any Node have logged anything
+ verify_garb_transient_logs(Nodes, Tid_list, aborted),
+ %% And only when the coordinator are have died
+ %% Else he would have restarted the transaction
+ ?match(Kill, Coord);
+ updated ->
+ case length(Tid_list) of
+ 1 ->
+ %% If there was only one transaction, it should be logged as
+ %% comitted on every node!
+ [Tid1] = Tid_list,
+ verify_garb_transient_logs(Nodes, [Tid1], committed);
+ 2 ->
+ %% If there is two transaction id, then the first
+ %% TID should have been aborted and the transaction
+ %% restarted with a new TID
+ [Tid1, Tid2] = Tid_list,
+ verify_garb_transient_logs(Nodes, [Tid1], aborted),
+ %% If mnesia is killed on a node i.e Coord and Part1 than they
+ %% won't know about the restarted trans! The rest of the nodes
+ %% should know that the trans was committed
+ case Kill of
+ coord_pid ->
+ verify_garb_transient_logs(Nodes, [Tid2], committed);
+ Coord ->
+ verify_garb_transient_logs([Part1, Part2], [Tid2], committed),
+ verify_garb_transient_logs([Coord], [Tid2], not_found);
+ Part1 ->
+ verify_garb_transient_logs([Coord, Part2], [Tid2], committed),
+ verify_garb_transient_logs([Part1], [Tid2], not_found)
+ end
+ end
+ end.
+
+verify_garb_decision_log([], _Tids) -> ok;
+verify_garb_decision_log([Node|R], Tids) ->
+ Check = fun(Tid) -> %% Node, Tid used in debugging!
+ ?match({{not_found, _}, Node, Tid},
+ {outcome(Tid, [mnesia_decision]), Node, Tid})
+ end,
+ rpc:call(Node, lists, foreach, [Check, Tids]),
+ verify_garb_decision_log(R, Tids).
+
+verify_garb_transient_logs([], _Tids, _) -> ok;
+verify_garb_transient_logs([Node|R], Tids, Exp_Res) ->
+ Check = fun(Tid) ->
+ LatestTab = mnesia_lib:val(latest_transient_decision),
+ PrevTabs = mnesia_lib:val(previous_transient_decisions),
+ case outcome(Tid, [LatestTab | PrevTabs]) of
+ {found, {_, [{_,_Tid, Exp_Res}]}} -> ok;
+ {not_found, _} when Exp_Res == not_found -> ok;
+ {not_found, _} when Exp_Res == aborted -> ok;
+ Else -> ?error("Expected ~p in trans ~p on ~p got ~p~n",
+ [Exp_Res, Tid, Node, Else])
+ end
+ end,
+ rpc:call(Node, lists, foreach, [Check, Tids]),
+ verify_garb_transient_logs(R, Tids, Exp_Res).
+
+outcome(Tid, Tabs) ->
+ outcome(Tid, Tabs, Tabs).
+
+outcome(Tid, [Tab | Tabs], AllTabs) ->
+ case catch ets:lookup(Tab, Tid) of
+ {'EXIT', _} ->
+ outcome(Tid, Tabs, AllTabs);
+ [] ->
+ outcome(Tid, Tabs, AllTabs);
+ Val ->
+ {found, {Tab, Val}}
+ end;
+outcome(_Tid, [], AllTabs) ->
+ {not_found, AllTabs}.
+
+
+verify_tabs([Tab|R], Nodes) ->
+ [_Coord, Part1, Part2 | _rest] = Nodes,
+ Read = fun() -> mnesia:read({Tab, 1}) end,
+ {success, A} = ?match({atomic, _}, mnesia:transaction(Read)),
+ ?match(A, rpc:call(Part1, mnesia, transaction, [Read])),
+ ?match(A, rpc:call(Part2, mnesia, transaction, [Read])),
+ {atomic, [{Tab, 1, Res}]} = A,
+ verify_tabs(R, Nodes, Res).
+
+verify_tabs([], _Nodes, Res) ->
+ case Res of
+ 100 -> aborted;
+ 101 -> updated
+ end;
+
+verify_tabs([Tab | Rest], Nodes, Res) ->
+ [Coord, Part1, Part2 | _rest] = Nodes,
+ Read = fun() -> mnesia:read({Tab, 1}) end,
+ Exp = {atomic, [{Tab, 1, Res}]},
+ ?match(Exp, rpc:call(Coord, mnesia, transaction, [Read])),
+ ?match(Exp, rpc:call(Part1, mnesia, transaction, [Read])),
+ ?match(Exp, rpc:call(Part2, mnesia, transaction, [Read])),
+ verify_tabs(Rest, Nodes, Res).
+
+%% Gather TIDS and send them to requesting process and exit!
+garb_handler(List) ->
+ receive
+ {trans_id, ID} -> garb_handler([ID|List]);
+ {get_tids, Pid} -> Pid ! {tids, lists:reverse(List)}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%
+receive_messages([], _File, _Line) -> [];
+receive_messages(ListOfMsgs, File, Line) ->
+ receive
+ {Pid, Msg} ->
+ case lists:member(Msg, ListOfMsgs) of
+ false ->
+ mnesia_test_lib:log("<>WARNING<>~n"
+ "Received unexpected msg~n ~p ~n"
+ "While waiting for ~p~n",
+ [{Pid, Msg}, ListOfMsgs], File, Line),
+ receive_messages(ListOfMsgs, File, Line);
+ true ->
+ ?dl("Got msg ~p from ~p ", [Msg, node(Pid)]),
+ [{Pid, Msg} | receive_messages(ListOfMsgs -- [Msg], File, Line)]
+ end;
+ Else -> mnesia_test_lib:log("<>WARNING<>~n"
+ "Recevied unexpected or bad formatted msg~n ~p ~n"
+ "While waiting for ~p~n",
+ [Else, ListOfMsgs], File, Line),
+ receive_messages(ListOfMsgs, File, Line)
+ after timer:minutes(2) ->
+ ?error("Timeout in receive msgs while waiting for ~p~n",
+ [ListOfMsgs])
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+after_full_disc_partition(doc) ->
+ ["Verify that the database does not get corrupt",
+ "when Mnesia encounters a full disc partition"].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% interrupted_fallback_start
+%% is implemented in consistency interupted_install_fallback!
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+after_corrupt_files_decision_log_head(suite) -> [];
+after_corrupt_files_decision_log_head(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "DECISION.LOG", head, repair).
+
+after_corrupt_files_decision_log_tail(suite) -> [];
+after_corrupt_files_decision_log_tail(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "DECISION.LOG", tail, repair).
+
+after_corrupt_files_latest_log_head(suite) -> [];
+after_corrupt_files_latest_log_head(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "LATEST.LOG", head, repair).
+
+after_corrupt_files_latest_log_tail(suite) -> [];
+after_corrupt_files_latest_log_tail(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "LATEST.LOG", tail, repair).
+
+after_corrupt_files_table_dat_head(suite) -> [];
+after_corrupt_files_table_dat_head(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "rec_files.DAT", head, crash).
+
+after_corrupt_files_table_dat_tail(suite) -> [];
+after_corrupt_files_table_dat_tail(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "rec_files.DAT", tail, repair).
+
+after_corrupt_files_schema_dat_head(suite) -> [];
+after_corrupt_files_schema_dat_head(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "schema.DAT", head, crash).
+
+after_corrupt_files_schema_dat_tail(suite) -> [];
+after_corrupt_files_schema_dat_tail(Config) when is_list(Config) ->
+ after_corrupt_files(Config, "schema.DAT", tail, crash).
+
+
+
+%%% BUGBUG: We should also write testcase's for autorepair=false i.e.
+%%% not the standard case!
+after_corrupt_files(Config, File, Where, Behaviour) ->
+ [Node] = ?acquire_nodes(1, Config ++ [{tc_timeout, timer:minutes(2)}]),
+ Tab = rec_files,
+ Def = [{disc_only_copies, [Node]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
+ insert_data(Tab, 100),
+ Dir = mnesia:system_info(directory),
+ mnesia_test_lib:kill_mnesia([Node]),
+ timer:sleep(timer:seconds(10)), % Let dets finish whatever it does
+
+ DirFile = Dir ++ "/" ++ File,
+
+ {ok, Fd} = file:open(DirFile, read_write),
+ {ok, FileInfo} = file:read_file_info(DirFile),
+ case Where of
+ head ->
+ ?match({ok, _NewP}, file:position(Fd, {bof, 1})),
+ ?match(ok, file:write(Fd, [255, 255, 255, 255, 255, 255, 255, 255, 254])),
+ ok;
+ tail ->
+ Size = FileInfo#file_info.size,
+ Half = Size div 2,
+
+ ?dl(" Size = ~p Half = ~p ", [Size, Half]),
+ ?match({ok, _NewP}, file:position(Fd, {bof, Half})),
+ ?match(ok, file:truncate(Fd)),
+ ok
+ end,
+ ?match(ok, file:close(Fd)),
+
+ ?warning("++++++SOME OF THE after_corrupt* TEST CASES WILL INTENTIONALLY CRASH MNESIA+++++++~n", []),
+ Pid = spawn_link(?MODULE, mymnesia_start, [self()]),
+ receive
+ {Pid, ok} ->
+ ?match(ok, mnesia:wait_for_tables([schema, Tab], 10000)),
+ ?match(ok, verify_data(Tab, 100)),
+ case mnesia_monitor:get_env(auto_repair) of
+ false ->
+ ?error("Mnesia should have crashed in ~p ~p ~n",
+ [File, Where]);
+ true ->
+ ok
+ end,
+ ?verify_mnesia([Node], []);
+ {Pid, {error, ED}} ->
+ case {mnesia_monitor:get_env(auto_repair), Behaviour} of
+ {true, repair} ->
+ ?error("Mnesia crashed with ~p: in ~p ~p ~n",
+ [ED, File, Where]);
+ _ -> %% Every other can crash!
+ ok
+ end,
+ ?verify_mnesia([], [Node]);
+ Msg ->
+ ?error("~p ~p: Got ~p during start of Mnesia~n",
+ [File, Where, Msg])
+ end.
+
+mymnesia_start(Tester) ->
+ Res = mnesia:start(),
+ unlink(Tester),
+ Tester ! {self(), Res}.
+
+verify_data(_, 0) -> ok;
+verify_data(Tab, N) ->
+ Actual = mnesia:dirty_read({Tab, N}),
+ Expected = [{Tab, N, N}],
+ if
+ Expected == Actual ->
+ verify_data(Tab, N - 1);
+ true ->
+ mnesia:schema(Tab),
+ {not_equal, node(), Expected, Actual}
+ end.
+
+insert_data(_Tab, 0) -> ok;
+insert_data(Tab, N) ->
+ ok = mnesia:sync_dirty(fun() -> mnesia:write({Tab, N, N}) end),
+ insert_data(Tab, N-1).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+disc_less(doc) ->
+ ["Here is a simple test case of a simple recovery of a disc less node. "
+ "However a lot more test cases involving disc less nodes should "
+ "be written"];
+disc_less(suite) -> [];
+disc_less(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ case mnesia_test_lib:diskless(Config) of
+ true -> skip;
+ false ->
+ ?match({atomic, ok}, mnesia:change_table_copy_type(schema, Node3, ram_copies))
+ end,
+ Tab1 = disc_less1,
+ Tab2 = disc_less2,
+ Tab3 = disc_less3,
+ Def1 = [{ram_copies, [Node3]}, {disc_copies, [Node1, Node2]}],
+ Def2 = [{ram_copies, [Node3]}, {disc_copies, [Node1]}],
+ Def3 = [{ram_copies, [Node3]}, {disc_copies, [Node2]}],
+ ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)),
+ ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)),
+ ?match({atomic, ok}, mnesia:create_table(Tab3, Def3)),
+ insert_data(Tab1, 100),
+ insert_data(Tab2, 100),
+ insert_data(Tab3, 100),
+
+ mnesia_test_lib:kill_mnesia([Node1, Node2]),
+ timer:sleep(500),
+ mnesia_test_lib:kill_mnesia([Node3]),
+ ?match(ok, rpc:call(Node1, mnesia, start, [])),
+ ?match(ok, rpc:call(Node2, mnesia, start, [])),
+
+ timer:sleep(500),
+ ?match(ok, rpc:call(Node3, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}]])),
+ ?match(ok, rpc:call(Node3, mnesia, wait_for_tables, [[Tab1, Tab2, Tab3], 20000])),
+
+ ?match(ok, rpc:call(Node3, ?MODULE, verify_data, [Tab1, 100])),
+ ?match(ok, rpc:call(Node3, ?MODULE, verify_data, [Tab2, 100])),
+ ?match(ok, rpc:call(Node3, ?MODULE, verify_data, [Tab3, 100])),
+
+
+ ?match(ok, rpc:call(Node2, ?MODULE, verify_data, [Tab1, 100])),
+ ?match(ok, rpc:call(Node2, ?MODULE, verify_data, [Tab2, 100])),
+ ?match(ok, rpc:call(Node2, ?MODULE, verify_data, [Tab3, 100])),
+
+ ?match(ok, rpc:call(Node1, ?MODULE, verify_data, [Tab1, 100])),
+ ?match(ok, rpc:call(Node1, ?MODULE, verify_data, [Tab2, 100])),
+ ?match(ok, rpc:call(Node1, ?MODULE, verify_data, [Tab3, 100])),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+system_upgrade(doc) ->
+ ["Test on-line and off-line upgrade of the Mnesia application"].
+
+garb_decision(doc) ->
+ ["Test that decisions are garbed correctly."];
+garb_decision(suite) -> [];
+garb_decision(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ check_garb(Nodes),
+ ?match({atomic, ok},mnesia:create_table(a, [{disc_copies, Nodes}])),
+ check_garb(Nodes),
+ ?match({atomic, ok},mnesia:create_table(b, [{ram_copies, Nodes}])),
+ check_garb(Nodes),
+ ?match({atomic, ok},mnesia:create_table(c, [{ram_copies, [Node1, Node3]},
+ {disc_copies, [Node2]}])),
+ check_garb(Nodes),
+ ?match({atomic, ok},mnesia:create_table(d, [{disc_copies, [Node1, Node3]},
+ {ram_copies, [Node2]}])),
+ check_garb(Nodes),
+
+ W = fun(Tab) -> mnesia:write({Tab,1,1}) end,
+ A = fun(Tab) -> mnesia:write({Tab,1,1}), exit(1) end,
+
+ ?match({atomic, ok}, mnesia:transaction(W,[a])),
+ check_garb(Nodes),
+ ?match({atomic, ok}, mnesia:transaction(W,[b])),
+ check_garb(Nodes),
+ ?match({atomic, ok}, mnesia:transaction(W,[c])),
+ check_garb(Nodes),
+ ?match({atomic, ok}, mnesia:transaction(W,[d])),
+ check_garb(Nodes),
+ ?match({aborted,1}, mnesia:transaction(A,[a])),
+ check_garb(Nodes),
+ ?match({aborted,1}, mnesia:transaction(A,[b])),
+ check_garb(Nodes),
+ ?match({aborted,1}, mnesia:transaction(A,[c])),
+ check_garb(Nodes),
+ ?match({aborted,1}, mnesia:transaction(A,[d])),
+ check_garb(Nodes),
+
+ rpc:call(Node2, mnesia, lkill, []),
+ ?match({atomic, ok}, mnesia:transaction(W,[a])),
+ ?match({atomic, ok}, mnesia:transaction(W,[b])),
+ ?match({atomic, ok}, mnesia:transaction(W,[c])),
+ ?match({atomic, ok}, mnesia:transaction(W,[d])),
+ check_garb(Nodes),
+ ?match([], mnesia_test_lib:start_mnesia([Node2])),
+ check_garb(Nodes),
+ timer:sleep(2000),
+ check_garb(Nodes),
+ %%%%%% Check transient_decision logs %%%%%
+
+ ?match(dumped, mnesia:dump_log()), sys:get_status(mnesia_recover), % sync
+ [{atomic, ok} = mnesia:transaction(W,[a]) || _ <- lists:seq(1,30)],
+ ?match(dumped, mnesia:dump_log()), sys:get_status(mnesia_recover), % sync
+ TD0 = mnesia_lib:val(latest_transient_decision),
+ ?match(0, ets:info(TD0, size)),
+ {atomic, ok} = mnesia:transaction(W,[a]),
+ ?match(dumped, mnesia:dump_log()), sys:get_status(mnesia_recover), % sync
+ ?match(TD0, mnesia_lib:val(latest_transient_decision)),
+ [{atomic, ok} = mnesia:transaction(W,[a]) || _ <- lists:seq(1,30)],
+ ?match(dumped, mnesia:dump_log()), sys:get_status(mnesia_recover), % sync
+ ?match(false, TD0 =:= mnesia_lib:val(latest_transient_decision)),
+ ?match(true, lists:member(TD0, mnesia_lib:val(previous_transient_decisions))),
+ ?verify_mnesia(Nodes, []).
+
+check_garb(Nodes) ->
+ rpc:multicall(Nodes, sys, get_status, [mnesia_recover]),
+ ?match({_, []},rpc:multicall(Nodes, erlang, apply, [fun check_garb/0, []])).
+
+check_garb() ->
+ try
+ Ds = ets:tab2list(mnesia_decision),
+ Check = fun({trans_tid,serial, _}) -> false;
+ ({mnesia_down,_,_,_}) -> false;
+ (_Else) -> true
+ end,
+ Node = node(),
+ ?match({Node, []}, {node(), lists:filter(Check, Ds)})
+ catch _:_ -> ok
+ end,
+ ok.
diff --git a/lib/mnesia/test/mnesia_registry_test.erl b/lib/mnesia/test/mnesia_registry_test.erl
new file mode 100644
index 0000000000..cf8da38632
--- /dev/null
+++ b/lib/mnesia/test/mnesia_registry_test.erl
@@ -0,0 +1,142 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_registry_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [good_dump, bad_dump].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+good_dump(doc) ->
+ ["Dump a faked C-node registry"];
+good_dump(suite) -> [];
+good_dump(Config) when is_list(Config) ->
+ [Node] = Nodes = ?acquire_nodes(1, Config),
+ T1 = gordon,
+ ?match(ok, mnesia_registry:create_table(T1)),
+ One = {T1, 1, 0, integer, 0, 10},
+ Two = {T1, "two", 3, integer, 0, 20},
+ Three = {T1, 3, 0, string, 6, "thirty"},
+ ?match(ok, mnesia:dirty_write(One)),
+ ?match(ok, mnesia:dirty_write(Two)),
+ ?match(ok, mnesia:dirty_write(Three)),
+ ?match([One], mnesia:dirty_read({T1, 1})),
+ ?match([_ | _], dump_registry(Node, T1)),
+
+ NewOne = {T1, 1, 0, integer, 0, 1},
+ NewFour = {T1, "4", 1, string, 4, "four"},
+
+ ?match([NewOne], mnesia:dirty_read({T1, 1})),
+ ?match([Two], mnesia:dirty_read({T1, "two"})),
+ ?match([], mnesia:dirty_read({T1, 3})),
+ ?match([NewFour], mnesia:dirty_read({T1, "4"})),
+
+ T2 = blixt,
+ ?match({'EXIT', {aborted, {no_exists, _}}},
+ mnesia:dirty_read({T2, 1})),
+ ?match([_ |_], dump_registry(Node, T2)),
+
+ NewOne2 = setelement(1, NewOne, T2),
+ NewFour2 = setelement(1, NewFour, T2),
+
+ ?match([NewOne2], mnesia:dirty_read({T2, 1})),
+ ?match([], mnesia:dirty_read({T2, "two"})),
+ ?match([], mnesia:dirty_read({T2, 3})),
+ ?match([NewFour2], mnesia:dirty_read({T2, "4"})),
+ ?match([_One2, NewFour2], lists:sort(restore_registry(Node, T2))),
+
+ ?verify_mnesia(Nodes, []).
+
+dump_registry(Node, Tab) ->
+ case rpc:call(Node, mnesia_registry, start_dump, [Tab, self()]) of
+ Pid when is_pid(Pid) ->
+ Pid ! {write, 1, 0, integer, 0, 1},
+ Pid ! {delete, 3},
+ Pid ! {write, "4", 1, string, 4, "four"},
+ Pid ! {commit, self()},
+ receive
+ {ok, Pid} ->
+ [{Tab, "4", 1, string, 4, "four"},
+ {Tab, 1, 0, integer, 0, 1}];
+ {'EXIT', Pid, Reason} ->
+ exit(Reason)
+ end;
+ {badrpc, Reason} ->
+ exit(Reason)
+ end.
+
+restore_registry(Node, Tab) ->
+ case rpc:call(Node, mnesia_registry, start_restore, [Tab, self()]) of
+ {size, Pid, N, _LargestKeySize, _LargestValSize} ->
+ Pid ! {send_records, self()},
+ receive_records(Tab, N);
+ {badrpc, Reason} ->
+ exit(Reason)
+ end.
+
+receive_records(Tab, N) when N > 0 ->
+ receive
+ {restore, KeySize, ValSize, ValType, Key, Val} ->
+ [{Tab, Key, KeySize, ValType, ValSize, Val} | receive_records(Tab, N -1)];
+ {'EXIT', _Pid, Reason} ->
+ exit(Reason)
+ end;
+receive_records(_Tab, 0) ->
+ [].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+bad_dump(doc) ->
+ ["Intentionally fail with the dump of a faked C-node registry"];
+bad_dump(suite) -> [];
+bad_dump(Config) when is_list(Config) ->
+ [Node] = Nodes = ?acquire_nodes(1, Config),
+
+ OldTab = ming,
+ ?match({'EXIT', {aborted, _}}, mnesia_registry:start_restore(no_tab, self())),
+ ?match({atomic, ok}, mnesia:create_table(OldTab, [{attributes, [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q]}])),
+ ?match({'EXIT',{aborted,{bad_type,_}}}, dump_registry(Node, OldTab)),
+ ?match(stopped, mnesia:stop()),
+
+ ?match({'EXIT', {aborted, _}}, mnesia_registry:create_table(down_table)),
+ ?match({'EXIT', {aborted, _}}, mnesia_registry:start_restore(no_tab, self())),
+ ?match({'EXIT', {aborted, _}}, dump_registry(Node, down_dump)),
+
+ ?verify_mnesia([], Nodes).
+
diff --git a/lib/mnesia/test/mnesia_schema_recovery_test.erl b/lib/mnesia/test/mnesia_schema_recovery_test.erl
new file mode 100644
index 0000000000..0fe26efd0b
--- /dev/null
+++ b/lib/mnesia/test/mnesia_schema_recovery_test.erl
@@ -0,0 +1,777 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_schema_recovery_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-define(receive_messages(Msgs), receive_messages(Msgs, ?FILE, ?LINE)).
+
+% First Some debug logging
+-define(dgb, true).
+-ifdef(dgb).
+-define(dl(X, Y), ?verbose("**TRACING: " ++ X ++ "**~n", Y)).
+-else.
+-define(dl(X, Y), ok).
+-endif.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+all() ->
+ [{group, interrupted_before_log_dump},
+ {group, interrupted_after_log_dump}].
+
+groups() ->
+ [{interrupted_before_log_dump, [],
+ [interrupted_before_create_ram,
+ interrupted_before_create_disc,
+ interrupted_before_create_disc_only,
+ interrupted_before_create_nostore,
+ interrupted_before_delete_ram,
+ interrupted_before_delete_disc,
+ interrupted_before_delete_disc_only,
+ interrupted_before_add_ram, interrupted_before_add_disc,
+ interrupted_before_add_disc_only,
+ interrupted_before_add_kill_copier,
+ interrupted_before_move_ram,
+ interrupted_before_move_disc,
+ interrupted_before_move_disc_only,
+ interrupted_before_move_kill_copier,
+ interrupted_before_delcopy_ram,
+ interrupted_before_delcopy_disc,
+ interrupted_before_delcopy_disc_only,
+ interrupted_before_delcopy_kill_copier,
+ interrupted_before_addindex_ram,
+ interrupted_before_addindex_disc,
+ interrupted_before_addindex_disc_only,
+ interrupted_before_delindex_ram,
+ interrupted_before_delindex_disc,
+ interrupted_before_delindex_disc_only,
+ interrupted_before_change_type_ram2disc,
+ interrupted_before_change_type_ram2disc_only,
+ interrupted_before_change_type_disc2ram,
+ interrupted_before_change_type_disc2disc_only,
+ interrupted_before_change_type_disc_only2ram,
+ interrupted_before_change_type_disc_only2disc,
+ interrupted_before_change_type_other_node,
+ interrupted_before_change_schema_type]},
+ {interrupted_after_log_dump, [],
+ [interrupted_after_create_ram,
+ interrupted_after_create_disc,
+ interrupted_after_create_disc_only,
+ interrupted_after_create_nostore,
+ interrupted_after_delete_ram,
+ interrupted_after_delete_disc,
+ interrupted_after_delete_disc_only,
+ interrupted_after_add_ram, interrupted_after_add_disc,
+ interrupted_after_add_disc_only,
+ interrupted_after_add_kill_copier,
+ interrupted_after_move_ram, interrupted_after_move_disc,
+ interrupted_after_move_disc_only,
+ interrupted_after_move_kill_copier,
+ interrupted_after_delcopy_ram,
+ interrupted_after_delcopy_disc,
+ interrupted_after_delcopy_disc_only,
+ interrupted_after_delcopy_kill_copier,
+ interrupted_after_addindex_ram,
+ interrupted_after_addindex_disc,
+ interrupted_after_addindex_disc_only,
+ interrupted_after_delindex_ram,
+ interrupted_after_delindex_disc,
+ interrupted_after_delindex_disc_only,
+ interrupted_after_change_type_ram2disc,
+ interrupted_after_change_type_ram2disc_only,
+ interrupted_after_change_type_disc2ram,
+ interrupted_after_change_type_disc2disc_only,
+ interrupted_after_change_type_disc_only2ram,
+ interrupted_after_change_type_disc_only2disc,
+ interrupted_after_change_type_other_node,
+ interrupted_after_change_schema_type]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+interrupted_before_create_ram(suite) -> [];
+interrupted_before_create_ram(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, dump_schema_op},
+ interrupted_create(Config, ram_copies, all, KillAt).
+
+interrupted_before_create_disc(suite) -> [];
+interrupted_before_create_disc(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, dump_schema_op},
+ interrupted_create(Config, disc_copies, all, KillAt).
+
+interrupted_before_create_disc_only(suite) -> [];
+interrupted_before_create_disc_only(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, dump_schema_op},
+ interrupted_create(Config, disc_only_copies, all, KillAt).
+
+interrupted_before_create_nostore(suite) -> [];
+interrupted_before_create_nostore(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, dump_schema_op},
+ interrupted_create(Config, ram_copies, one, KillAt).
+
+interrupted_after_create_ram(suite) -> [];
+interrupted_after_create_ram(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, post_dump},
+ interrupted_create(Config, ram_copies, all, KillAt).
+
+interrupted_after_create_disc(suite) -> [];
+interrupted_after_create_disc(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, post_dump},
+ interrupted_create(Config, disc_copies, all, KillAt).
+
+interrupted_after_create_disc_only(suite) -> [];
+interrupted_after_create_disc_only(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, post_dump},
+ interrupted_create(Config, disc_only_copies, all, KillAt).
+
+interrupted_after_create_nostore(suite) -> [];
+interrupted_after_create_nostore(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, post_dump},
+ interrupted_create(Config, ram_copies, one, KillAt).
+
+%%% After dump don't need debug point
+interrupted_create(Config, Type, _Where, {mnesia_dumper, post_dump}) ->
+ [Node1] = Nodes = ?acquire_nodes(1, [{tc_timeout, timer:seconds(30)} | Config]),
+ ?match({atomic, ok},mnesia:create_table(itrpt, [{Type, Nodes}])),
+ ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])),
+ ?match(ok, mnesia:dirty_write({itrpt, before, 1})),
+ ?match(ok, mnesia:dirty_write({test, found_in_log, 1})),
+ ?match(stopped, mnesia:stop()),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])),
+ %% Verify
+ ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})),
+ case Type of
+ ram_copies ->
+ ?match([], mnesia:dirty_read({itrpt, before}));
+ _ ->
+ ?match([{itrpt, before, 1}], mnesia:dirty_read({itrpt, before}))
+ end,
+ ?verify_mnesia(Nodes, []);
+interrupted_create(Config, Type, Where, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ {success, [A]} = ?start_activities([Node2]),
+ setup_dbgpoint(KillAt, Node2),
+
+ if %% CREATE TABLE
+ Where == all -> % tables on both nodes
+ A ! fun() -> mnesia:create_table(itrpt, [{Type, Nodes}]) end;
+ true -> % no table on the killed node
+ A ! fun() -> mnesia:create_table(itrpt, [{Type, [Node1]}]) end
+ end,
+
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Node2], [itrpt])),
+ %% Verify
+ ?match(ok, mnesia:dirty_write({itrpt, before, 1})),
+ verify_tab(Node1, Node2),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_delete_ram(suite) -> [];
+interrupted_before_delete_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delete(Config, ram_copies, Debug_Point).
+interrupted_before_delete_disc(suite) -> [];
+interrupted_before_delete_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delete(Config, disc_copies, Debug_Point).
+interrupted_before_delete_disc_only(suite) -> [];
+interrupted_before_delete_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delete(Config, disc_only_copies, Debug_Point).
+
+interrupted_after_delete_ram(suite) -> [];
+interrupted_after_delete_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delete(Config, ram_copies, Debug_Point).
+interrupted_after_delete_disc(suite) -> [];
+interrupted_after_delete_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delete(Config, disc_copies, Debug_Point).
+interrupted_after_delete_disc_only(suite) -> [];
+interrupted_after_delete_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delete(Config, disc_only_copies, Debug_Point).
+
+interrupted_delete(Config, Type, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node2]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+ {_Alive, Kill} = {Node1, Node2},
+ {success, [A]} = ?start_activities([Kill]),
+
+ setup_dbgpoint(KillAt, Kill),
+ A ! fun() -> mnesia:delete_table(Tab) end,
+
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Node2], [])),
+ Bad = {badrpc, {'EXIT', {aborted,{no_exists, Tab, all}}}},
+ ?match(Bad, rpc:call(Node1, mnesia, table_info, [Tab, all])),
+ ?match(Bad, rpc:call(Node2, mnesia, table_info, [Tab, all])),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_add_ram(suite) -> [];
+interrupted_before_add_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_add(Config, ram_copies, kill_reciever, Debug_Point).
+interrupted_before_add_disc(suite) -> [];
+interrupted_before_add_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_add(Config, disc_copies, kill_reciever, Debug_Point).
+interrupted_before_add_disc_only(suite) -> [];
+interrupted_before_add_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point).
+interrupted_before_add_kill_copier(suite) -> [];
+interrupted_before_add_kill_copier(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_add(Config, ram_copies, kill_copier, Debug_Point).
+
+interrupted_after_add_ram(suite) -> [];
+interrupted_after_add_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_add(Config, ram_copies, kill_reciever, Debug_Point).
+interrupted_after_add_disc(suite) -> [];
+interrupted_after_add_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_add(Config, disc_copies, kill_reciever, Debug_Point).
+interrupted_after_add_disc_only(suite) -> [];
+interrupted_after_add_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_add(Config, disc_only_copies, kill_reciever, Debug_Point).
+interrupted_after_add_kill_copier(suite) -> [];
+interrupted_after_add_kill_copier(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_add(Config, ram_copies, kill_copier, Debug_Point).
+
+%%% After dump don't need debug point
+interrupted_add(Config, Type, _Where, {mnesia_dumper, post_dump}) ->
+ [Node1, Node2] = Nodes =
+ ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node2]}, {local_content,true}])),
+ ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])),
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, Node1, Type)),
+ ?match(ok, mnesia:dirty_write({itrpt, before, 1})),
+ ?match(ok, mnesia:dirty_write({test, found_in_log, 1})),
+ ?match(stopped, mnesia:stop()),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])),
+ %% Verify
+ ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})),
+ case Type of
+ ram_copies ->
+ ?match([], mnesia:dirty_read({itrpt, before}));
+ _ ->
+ ?match([{itrpt, before, 1}], mnesia:dirty_read({itrpt, before}))
+ end,
+ ?verify_mnesia(Nodes, []);
+interrupted_add(Config, Type, Who, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes =
+ ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ {_Alive, Kill} =
+ if Who == kill_reciever ->
+ {Node1, Node2};
+ true ->
+ {Node2, Node1}
+ end,
+ {success, [A]} = ?start_activities([Kill]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+
+ setup_dbgpoint(KillAt, Kill),
+
+ A ! fun() -> mnesia:add_table_copy(Tab, Node2, Type) end,
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Kill], [itrpt])),
+ verify_tab(Node1, Node2),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_move_ram(suite) -> [];
+interrupted_before_move_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_move(Config, ram_copies, kill_reciever, Debug_Point).
+interrupted_before_move_disc(suite) -> [];
+interrupted_before_move_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_move(Config, disc_copies, kill_reciever, Debug_Point).
+interrupted_before_move_disc_only(suite) -> [];
+interrupted_before_move_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point).
+interrupted_before_move_kill_copier(suite) -> [];
+interrupted_before_move_kill_copier(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_move(Config, ram_copies, kill_copier, Debug_Point).
+
+interrupted_after_move_ram(suite) -> [];
+interrupted_after_move_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_move(Config, ram_copies, kill_reciever, Debug_Point).
+interrupted_after_move_disc(suite) -> [];
+interrupted_after_move_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_move(Config, disc_copies, kill_reciever, Debug_Point).
+interrupted_after_move_disc_only(suite) -> [];
+interrupted_after_move_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_move(Config, disc_only_copies, kill_reciever, Debug_Point).
+interrupted_after_move_kill_copier(suite) -> [];
+interrupted_after_move_kill_copier(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_move(Config, ram_copies, kill_copier, Debug_Point).
+
+%%% After dump don't need debug point
+interrupted_move(Config, Type, _Where, {mnesia_dumper, post_dump}) ->
+ [Node1, Node2] = Nodes =
+ ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])),
+ ?match(ok, mnesia:dirty_write({itrpt, before, 1})),
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, Node1, Node2)),
+ ?match(ok, mnesia:dirty_write({itrpt, aFter, 1})),
+ ?match(ok, mnesia:dirty_write({test, found_in_log, 1})),
+ ?match(stopped, mnesia:stop()),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])),
+ %% Verify
+ ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})),
+ ?match([{itrpt, before, 1}], mnesia:dirty_read({itrpt, before})),
+ ?match([{itrpt, aFter, 1}], mnesia:dirty_read({itrpt, aFter})),
+ ?verify_mnesia(Nodes, []);
+interrupted_move(Config, Type, Who, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes =
+ ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+
+ {_Alive, Kill} =
+ if Who == kill_reciever ->
+ if Type == ram_copies ->
+ {atomic, ok} = mnesia:dump_tables([Tab]);
+ true ->
+ ignore
+ end,
+ {Node1, Node2};
+ true ->
+ {Node2, Node1}
+ end,
+
+ {success, [A]} = ?start_activities([Kill]),
+
+ setup_dbgpoint(KillAt, Kill),
+ A ! fun() -> mnesia:move_table_copy(Tab, Node1, Node2) end,
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Kill], [itrpt])),
+ verify_tab(Node1, Node2),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_delcopy_ram(suite) -> [];
+interrupted_before_delcopy_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delcopy(Config, ram_copies, kill_reciever, Debug_Point).
+interrupted_before_delcopy_disc(suite) -> [];
+interrupted_before_delcopy_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point).
+interrupted_before_delcopy_disc_only(suite) -> [];
+interrupted_before_delcopy_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point).
+interrupted_before_delcopy_kill_copier(suite) -> [];
+interrupted_before_delcopy_kill_copier(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delcopy(Config, ram_copies, kill_copier, Debug_Point).
+
+interrupted_after_delcopy_ram(suite) -> [];
+interrupted_after_delcopy_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delcopy(Config, ram_copies, kill_reciever, Debug_Point).
+interrupted_after_delcopy_disc(suite) -> [];
+interrupted_after_delcopy_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delcopy(Config, disc_copies, kill_reciever, Debug_Point).
+interrupted_after_delcopy_disc_only(suite) -> [];
+interrupted_after_delcopy_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delcopy(Config, disc_only_copies, kill_reciever, Debug_Point).
+interrupted_after_delcopy_kill_copier(suite) -> [];
+interrupted_after_delcopy_kill_copier(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delcopy(Config, ram_copies, kill_copier, Debug_Point).
+
+
+%%% After dump don't need debug point
+interrupted_delcopy(Config, Type, _Where, {mnesia_dumper, post_dump}) ->
+ [Node1, Node2] = Nodes =
+ ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok},mnesia:create_table(test, [{disc_copies,[Node1]}])),
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1,Node2]}])),
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, Node1)),
+ ?match(ok, mnesia:dirty_write({test, found_in_log, 1})),
+ ?match(stopped, mnesia:stop()),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [test])),
+ %% Verify
+ ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})),
+ ?match([Node2], mnesia:table_info(itrpt,Type)),
+ ?verify_mnesia(Nodes, []);
+interrupted_delcopy(Config, Type, Who, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes =
+ ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1, Node2]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+
+ {_Alive, Kill} =
+ if Who == kill_reciever ->
+ {Node1, Node2};
+ true ->
+ if
+ Type == ram_copies ->
+ {atomic, ok} = mnesia:dump_tables([Tab]);
+ true ->
+ ignore
+ end,
+ {Node2, Node1}
+ end,
+
+ {success, [A]} = ?start_activities([Kill]),
+ setup_dbgpoint(KillAt, Kill),
+ A ! fun() -> mnesia:del_table_copy(Tab, Node2) end,
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Kill], [itrpt])),
+ verify_tab(Node1, Node2),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_addindex_ram(suite) -> [];
+interrupted_before_addindex_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_addindex(Config, ram_copies, Debug_Point).
+interrupted_before_addindex_disc(suite) -> [];
+interrupted_before_addindex_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_addindex(Config, disc_copies, Debug_Point).
+interrupted_before_addindex_disc_only(suite) -> [];
+interrupted_before_addindex_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_addindex(Config, disc_only_copies, Debug_Point).
+
+interrupted_after_addindex_ram(suite) -> [];
+interrupted_after_addindex_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_addindex(Config, ram_copies, Debug_Point).
+interrupted_after_addindex_disc(suite) -> [];
+interrupted_after_addindex_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_addindex(Config, disc_copies, Debug_Point).
+interrupted_after_addindex_disc_only(suite) -> [];
+interrupted_after_addindex_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_addindex(Config, disc_only_copies, Debug_Point).
+
+
+%%% After dump don't need debug point
+interrupted_addindex(Config, Type, {mnesia_dumper, post_dump}) ->
+ [Node1] = Nodes = ?acquire_nodes(1, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic,ok},mnesia:create_table(Tab, [{Type, Nodes}])),
+ ?match({atomic,ok},mnesia:create_table(test, [{disc_copies,[Node1]}])),
+ ?match({atomic,ok}, mnesia:add_table_index(Tab, val)),
+ ?match(ok, mnesia:dirty_write({itrpt, before, 1})),
+ ?match(ok, mnesia:dirty_write({test, found_in_log, 1})),
+ ?match(stopped, mnesia:stop()),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])),
+ %% Verify
+ ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})),
+ case Type of
+ ram_copies ->
+ ?match([], mnesia:dirty_index_read(itrpt, 1, val));
+ _ ->
+ ?match([{itrpt, before, 1}], mnesia:dirty_index_read(itrpt, 1, val))
+ end,
+ ?verify_mnesia(Nodes, []);
+interrupted_addindex(Config, Type, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{Type, [Node1]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+ {_Alive, Kill} = {Node1, Node2},
+ {success, [A]} = ?start_activities([Kill]),
+
+ setup_dbgpoint(KillAt, Kill),
+ A ! fun() -> mnesia:add_table_index(Tab, val) end,
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Node2], [])),
+
+ verify_tab(Node1, Node2),
+ ?match([{Tab, b, a}, {Tab, a, a}],
+ rpc:call(Node1, mnesia, dirty_index_read, [itrpt, a, val])),
+ ?match([{Tab, b, a}, {Tab, a, a}],
+ rpc:call(Node2, mnesia, dirty_index_read, [itrpt, a, val])),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_delindex_ram(suite) -> [];
+interrupted_before_delindex_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delindex(Config, ram_copies, Debug_Point).
+interrupted_before_delindex_disc(suite) -> [];
+interrupted_before_delindex_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delindex(Config, disc_copies, Debug_Point).
+interrupted_before_delindex_disc_only(suite) -> [];
+interrupted_before_delindex_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_delindex(Config, disc_only_copies, Debug_Point).
+
+interrupted_after_delindex_ram(suite) -> [];
+interrupted_after_delindex_ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delindex(Config, ram_copies, Debug_Point).
+interrupted_after_delindex_disc(suite) -> [];
+interrupted_after_delindex_disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delindex(Config, disc_copies, Debug_Point).
+interrupted_after_delindex_disc_only(suite) -> [];
+interrupted_after_delindex_disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_delindex(Config, disc_only_copies, Debug_Point).
+
+%%% After dump don't need debug point
+interrupted_delindex(Config, Type, {mnesia_dumper, post_dump}) ->
+ [Node1] = Nodes = ?acquire_nodes(1, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic,ok},mnesia:create_table(Tab, [{Type, Nodes},{index,[val]}])),
+ ?match({atomic,ok},mnesia:create_table(test, [{disc_copies,[Node1]}])),
+ ?match({atomic,ok}, mnesia:del_table_index(Tab, val)),
+ ?match(ok, mnesia:dirty_write({itrpt, before, 1})),
+ ?match(ok, mnesia:dirty_write({test, found_in_log, 1})),
+ ?match(stopped, mnesia:stop()),
+ ?match([], mnesia_test_lib:start_mnesia([Node1], [itrpt,test])),
+ %% Verify
+ ?match([{test, found_in_log, 1}], mnesia:dirty_read({test, found_in_log})),
+ ?match({'EXIT',{aborted,{badarg,_}}}, mnesia:dirty_index_read(itrpt, 1, val)),
+ ?verify_mnesia(Nodes, []);
+
+interrupted_delindex(Config, Type, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{index, [val]},
+ {Type, [Node1]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+ {_Alive, Kill} = {Node1, Node2},
+ {success, [A]} = ?start_activities([Kill]),
+ setup_dbgpoint(KillAt, Kill),
+ A ! fun() -> mnesia:del_table_index(Tab, val) end,
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia([Node2], [])),
+ verify_tab(Node1, Node2),
+ ?match({badrpc, _}, rpc:call(Node1, mnesia, dirty_index_read, [itrpt, a, val])),
+ ?match({badrpc, _}, rpc:call(Node2, mnesia, dirty_index_read, [itrpt, a, val])),
+ ?match([], rpc:call(Node1, mnesia, table_info, [Tab, index])),
+ ?match([], rpc:call(Node2, mnesia, table_info, [Tab, index])),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_change_type_ram2disc(suite) -> [];
+interrupted_before_change_type_ram2disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point).
+interrupted_before_change_type_ram2disc_only(suite) -> [];
+interrupted_before_change_type_ram2disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point).
+interrupted_before_change_type_disc2ram(suite) -> [];
+interrupted_before_change_type_disc2ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point).
+interrupted_before_change_type_disc2disc_only(suite) -> [];
+interrupted_before_change_type_disc2disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point).
+interrupted_before_change_type_disc_only2ram(suite) -> [];
+interrupted_before_change_type_disc_only2ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point).
+interrupted_before_change_type_disc_only2disc(suite) -> [];
+interrupted_before_change_type_disc_only2disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point).
+interrupted_before_change_type_other_node(suite) -> [];
+interrupted_before_change_type_other_node(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, dump_schema_op},
+ interrupted_change_type(Config, ram_copies, disc_copies, the_other_one, Debug_Point).
+
+interrupted_after_change_type_ram2disc(suite) -> [];
+interrupted_after_change_type_ram2disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, ram_copies, disc_copies, changer, Debug_Point).
+interrupted_after_change_type_ram2disc_only(suite) -> [];
+interrupted_after_change_type_ram2disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, ram_copies, disc_only_copies, changer, Debug_Point).
+interrupted_after_change_type_disc2ram(suite) -> [];
+interrupted_after_change_type_disc2ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, disc_copies, ram_copies, changer, Debug_Point).
+interrupted_after_change_type_disc2disc_only(suite) -> [];
+interrupted_after_change_type_disc2disc_only(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, disc_copies, disc_only_copies, changer, Debug_Point).
+interrupted_after_change_type_disc_only2ram(suite) -> [];
+interrupted_after_change_type_disc_only2ram(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, disc_only_copies, ram_copies, changer, Debug_Point).
+interrupted_after_change_type_disc_only2disc(suite) -> [];
+interrupted_after_change_type_disc_only2disc(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, disc_only_copies, disc_copies, changer, Debug_Point).
+interrupted_after_change_type_other_node(suite) -> [];
+interrupted_after_change_type_other_node(Config) when is_list(Config) ->
+ Debug_Point = {mnesia_dumper, post_dump},
+ interrupted_change_type(Config, ram_copies, disc_copies, the_other_one, Debug_Point).
+
+interrupted_change_type(Config, FromType, ToType, Who, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{FromType, [Node2, Node1]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+
+ {_Alive, Kill} =
+ if Who == changer -> {Node1, Node2};
+ true -> {Node2, Node1}
+ end,
+
+ {success, [A]} = ?start_activities([Kill]),
+ setup_dbgpoint(KillAt, Kill),
+ A ! fun() -> mnesia:change_table_copy_type(Tab, Node2, ToType) end,
+ kill_at_debug(),
+ ?match([], mnesia_test_lib:start_mnesia(Nodes, [itrpt])),
+ verify_tab(Node1, Node2),
+ ?match(FromType, rpc:call(Node1, mnesia, table_info, [Tab, storage_type])),
+ ?match(ToType, rpc:call(Node2, mnesia, table_info, [Tab, storage_type])),
+ ?verify_mnesia(Nodes, []).
+
+interrupted_before_change_schema_type(suite) -> [];
+interrupted_before_change_schema_type(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, dump_schema_op},
+ interrupted_change_schema_type(Config, KillAt).
+
+interrupted_after_change_schema_type(suite) -> [];
+interrupted_after_change_schema_type(Config) when is_list(Config) ->
+ KillAt = {mnesia_dumper, post_dump},
+ interrupted_change_schema_type(Config, KillAt).
+
+-define(cleanup(N, Config),
+ mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}],
+ N, Config, ?FILE, ?LINE)).
+
+interrupted_change_schema_type(Config, KillAt) ->
+ ?is_debug_compiled,
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, [{tc_timeout, timer:seconds(30)} | Config]),
+
+ Tab = itrpt,
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{ram_copies, [Node2, Node1]}])),
+ ?match(ok, mnesia:dirty_write({Tab, before, 1})),
+
+ {success, [A]} = ?start_activities([Node2]),
+ setup_dbgpoint(KillAt, Node2),
+
+ A ! fun() -> mnesia:change_table_copy_type(schema, Node2, ram_copies) end,
+ kill_at_debug(),
+ ?match(ok, rpc:call(Node2, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}]])),
+ ?match(ok, rpc:call(Node2, mnesia, wait_for_tables, [[itrpt, schema], 2000])),
+ ?match(disc_copies, rpc:call(Node1, mnesia, table_info, [schema, storage_type])),
+ ?match(ram_copies, rpc:call(Node2, mnesia, table_info, [schema, storage_type])),
+
+ %% Go back to disc_copies !!
+ {success, [B]} = ?start_activities([Node2]),
+ setup_dbgpoint(KillAt, Node2),
+ B ! fun() -> mnesia:change_table_copy_type(schema, Node2, disc_copies) end,
+ kill_at_debug(),
+
+ ?match(ok, rpc:call(Node2, mnesia, start, [[{extra_db_nodes, [Node1, Node2]}]])),
+ ?match(ok, rpc:call(Node2, mnesia, wait_for_tables, [[itrpt, schema], 2000])),
+ ?match(disc_copies, rpc:call(Node1, mnesia, table_info, [schema, storage_type])),
+ ?match(disc_copies, rpc:call(Node2, mnesia, table_info, [schema, storage_type])),
+
+ ?verify_mnesia(Nodes, []),
+ ?cleanup(2, Config).
+
+%%% Helpers
+verify_tab(Node1, Node2) ->
+ ?match({atomic, ok},
+ rpc:call(Node1, mnesia, transaction, [fun() -> mnesia:dirty_write({itrpt, a, a}) end])),
+ ?match({atomic, ok},
+ rpc:call(Node2, mnesia, transaction, [fun() -> mnesia:dirty_write({itrpt, b, a}) end])),
+ ?match([{itrpt,a,a}], rpc:call(Node1, mnesia, dirty_read, [{itrpt, a}])),
+ ?match([{itrpt,a,a}], rpc:call(Node2, mnesia, dirty_read, [{itrpt, a}])),
+ ?match([{itrpt,b,a}], rpc:call(Node1, mnesia, dirty_read, [{itrpt, b}])),
+ ?match([{itrpt,b,a}], rpc:call(Node2, mnesia, dirty_read, [{itrpt, b}])),
+ ?match([{itrpt,before,1}], rpc:call(Node1, mnesia, dirty_read, [{itrpt, before}])),
+ ?match([{itrpt,before,1}], rpc:call(Node2, mnesia, dirty_read, [{itrpt, before}])).
+
+setup_dbgpoint(DbgPoint, Where) ->
+ Self = self(),
+ TestFun = fun(_, [InitBy]) ->
+ case InitBy of
+ schema_prepare ->
+ ignore;
+ schema_begin ->
+ ignore;
+ _Other ->
+ ?deactivate_debug_fun(DbgPoint),
+ unlink(Self),
+ Self ! {fun_done, node()},
+ timer:sleep(infinity)
+ end
+ end,
+ %% Kill when debug has been reached
+ ?remote_activate_debug_fun(Where, DbgPoint, TestFun, []).
+
+kill_at_debug() ->
+ %% Wait till it's killed
+ receive
+ {fun_done, Node} ->
+ ?match([], mnesia_test_lib:kill_mnesia([Node]))
+ after
+ timer:minutes(1) -> ?error("Timeout in kill_at_debug", [])
+ end.
+
diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl
new file mode 100644
index 0000000000..182c240084
--- /dev/null
+++ b/lib/mnesia/test/mnesia_test_lib.erl
@@ -0,0 +1,1058 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%% Author: Hakan Mattsson [email protected]
+%%% Purpose: Test case support library
+%%%
+%%% This test suite may be run as a part of the Grand Test Suite
+%%% of Erlang. The Mnesia test suite is structured in a hierarchy.
+%%% Each test case is implemented as an exported function with arity 1.
+%%% Test case identifiers must have the following syntax: {Module, Function}.
+%%%
+%%% The driver of the test suite runs in two passes as follows:
+%%% first the test case function is invoked with the atom 'suite' as
+%%% single argument. The returned value is treated as a list of sub
+%%% test cases. If the list of sub test cases is [] the test case
+%%% function is invoked again, this time with a list of nodes as
+%%% argument. If the list of sub test cases is not empty, the test
+%%% case driver applies the algorithm recursively on each element
+%%% in the list.
+%%%
+%%% All test cases are written in such a manner
+%%% that they start to invoke ?acquire_nodes(X, Config)
+%%% in order to prepare the test case execution. When that is
+%%% done, the test machinery ensures that at least X number
+%%% of nodes are connected to each other. If too few nodes was
+%%% specified in the Config, the test case is skipped. If there
+%%% was enough node names in the Config, X of them are selected
+%%% and if some of them happens to be down they are restarted
+%%% via the slave module. When all nodes are up and running a
+%%% disk resident schema is created on all nodes and Mnesia is
+%%% started a on all nodes. This means that all test cases may
+%%% assume that Mnesia is up and running on all acquired nodes.
+%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%
+%%% doc(TestCases)
+%%%
+%%% Generates a test spec from parts of the test case structure
+%%%
+%%% struct(TestCases)
+%%%
+%%% Prints out the test case structure
+%%%
+%%% test(TestCases)
+%%%
+%%% Run parts of the test suite. Uses test/2.
+%%% Reads Config from mnesia_test.config and starts them if neccessary.
+%%% Kills Mnesia and wipes out the Mnesia directories as a starter.
+%%%
+%%% test(TestCases, Config)
+%%%
+%%% Run parts of the test suite on the given Nodes,
+%%% assuming that the nodes are up and running.
+%%% Kills Mnesia and wipes out the Mnesia directories as a starter.
+%%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(mnesia_test_lib).
+-author('[email protected]').
+-export([
+ log/2,
+ log/4,
+ verbose/4,
+ default_config/0,
+ diskless/1,
+ eval_test_case/3,
+ test_driver/2,
+ test_case_evaluator/3,
+ activity_evaluator/1,
+ flush/0,
+ pick_msg/0,
+ start_activities/1,
+ start_transactions/1,
+ start_transactions/2,
+ start_sync_transactions/1,
+ start_sync_transactions/2,
+ sync_trans_tid_serial/1,
+ prepare_test_case/5,
+ select_nodes/4,
+ init_nodes/3,
+ error/4,
+ slave_start_link/0,
+ slave_start_link/1,
+ slave_sup/0,
+
+ start_mnesia/1,
+ start_mnesia/2,
+ start_appls/2,
+ start_appls/3,
+ start_wait/2,
+ storage_type/2,
+ stop_mnesia/1,
+ stop_appls/2,
+ sort/1,
+ kill_mnesia/1,
+ kill_appls/2,
+ verify_mnesia/4,
+ shutdown/0,
+ verify_replica_location/5,
+ lookup_config/2,
+ sync_tables/2,
+ remote_start/3,
+ remote_stop/1,
+ remote_kill/1,
+
+ reload_appls/2,
+
+ remote_activate_debug_fun/6,
+ do_remote_activate_debug_fun/6,
+
+ test/1,
+ test/2,
+ doc/1,
+ struct/1,
+ init_per_testcase/2,
+ end_per_testcase/2,
+ kill_tc/2
+ ]).
+
+-include("mnesia_test_lib.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% included for test server compatibility
+%% assume that all test cases only takes Config as sole argument
+init_per_testcase(_Func, Config) ->
+ global:register_name(mnesia_global_logger, group_leader()),
+ Config.
+
+end_per_testcase(_Func, Config) ->
+ global:unregister_name(mnesia_global_logger),
+ %% Nodes = select_nodes(all, Config, ?FILE, ?LINE),
+ %% rpc:multicall(Nodes, mnesia, lkill, []),
+ Config.
+
+%% Use ?log(Format, Args) as wrapper
+log(Format, Args, LongFile, Line) ->
+ File = filename:basename(LongFile),
+ Format2 = lists:concat([File, "(", Line, ")", ": ", Format]),
+ log(Format2, Args).
+
+log(Format, Args) ->
+ case global:whereis_name(mnesia_global_logger) of
+ undefined ->
+ io:format(user, Format, Args);
+ Pid ->
+ io:format(Pid, Format, Args)
+ end.
+
+verbose(Format, Args, File, Line) ->
+ Arg = mnesia_test_verbose,
+ case get(Arg) of
+ false ->
+ ok;
+ true ->
+ log(Format, Args, File, Line);
+ undefined ->
+ case init:get_argument(Arg) of
+ {ok, List} when is_list(List) ->
+ case lists:last(List) of
+ ["true"] ->
+ put(Arg, true),
+ log(Format, Args, File, Line);
+ _ ->
+ put(Arg, false),
+ ok
+ end;
+ _ ->
+ put(Arg, false),
+ ok
+ end
+ end.
+
+-record('REASON', {file, line, desc}).
+
+error(Format, Args, File, Line) ->
+ global:send(mnesia_global_logger, {failed, File, Line}),
+ Fail = #'REASON'{file = filename:basename(File),
+ line = Line,
+ desc = Args},
+ case global:whereis_name(mnesia_test_case_sup) of
+ undefined ->
+ ignore;
+ Pid ->
+ Pid ! Fail
+%% global:send(mnesia_test_case_sup, Fail),
+ end,
+ log("<>ERROR<>~n" ++ Format, Args, File, Line).
+
+storage_type(Default, Config) ->
+ case diskless(Config) of
+ true ->
+ ram_copies;
+ false ->
+ Default
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+default_config() ->
+ [{nodes, default_nodes()}].
+
+default_nodes() ->
+ mk_nodes(3, []).
+
+mk_nodes(0, Nodes) ->
+ Nodes;
+mk_nodes(N, []) ->
+ mk_nodes(N - 1, [node()]);
+mk_nodes(N, Nodes) when N > 0 ->
+ Head = hd(Nodes),
+ [Name, Host] = node_to_name_and_host(Head),
+ Nodes ++ [mk_node(I, Name, Host) || I <- lists:seq(1, N)].
+
+mk_node(N, Name, Host) ->
+ list_to_atom(lists:concat([Name ++ integer_to_list(N) ++ "@" ++ Host])).
+
+slave_start_link() ->
+ slave_start_link(node()).
+
+slave_start_link(Node) ->
+ [Local, Host] = node_to_name_and_host(Node),
+ {Mega, Sec, Micro} = erlang:now(),
+ List = [Local, "_", Mega, "_", Sec, "_", Micro],
+ Name = list_to_atom(lists:concat(List)),
+ slave_start_link(list_to_atom(Host), Name).
+
+slave_start_link(Host, Name) ->
+ slave_start_link(Host, Name, 10).
+
+slave_start_link(Host, Name, Retries) ->
+ Debug = atom_to_list(mnesia:system_info(debug)),
+ Args = "-mnesia debug " ++ Debug ++
+ " -pa " ++
+ filename:dirname(code:which(?MODULE)) ++
+ " -pa " ++
+ filename:dirname(code:which(mnesia)),
+ case starter(Host, Name, Args) of
+ {ok, NewNode} ->
+ ?match(pong, net_adm:ping(NewNode)),
+ {ok, Cwd} = file:get_cwd(),
+ Path = code:get_path(),
+ ok = rpc:call(NewNode, file, set_cwd, [Cwd]),
+ true = rpc:call(NewNode, code, set_path, [Path]),
+ spawn_link(NewNode, ?MODULE, slave_sup, []),
+ rpc:multicall([node() | nodes()], global, sync, []),
+ {ok, NewNode};
+ {error, Reason} when Retries == 0->
+ {error, Reason};
+ {error, Reason} ->
+ io:format("Could not start slavenode ~p ~p retrying~n",
+ [{Host, Name, Args}, Reason]),
+ timer:sleep(500),
+ slave_start_link(Host, Name, Retries - 1)
+ end.
+
+starter(Host, Name, Args) ->
+ case os:type() of
+ vxworks ->
+ X = test_server:start_node(Name, slave, [{args,Args}]),
+ timer:sleep(5000),
+ X;
+ _ ->
+ slave:start(Host, Name, Args)
+ end.
+
+slave_sup() ->
+ process_flag(trap_exit, true),
+ receive
+ {'EXIT', _, _} ->
+ case os:type() of
+ vxworks ->
+ erlang:halt();
+ _ ->
+ ignore
+ end
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Index the test case structure
+
+doc(TestCases) when is_list(TestCases) ->
+ test(TestCases, suite),
+ SuiteFname = "index.html",
+ io:format("Generating HTML test specification to file: ~s~n",
+ [SuiteFname]),
+ {ok, Fd} = file:open(SuiteFname, [write]),
+ io:format(Fd, "<TITLE>Test specification for ~p</TITLE>.~n", [TestCases]),
+ io:format(Fd, "<H1>Test specification for ~p</H1>~n", [TestCases]),
+ io:format(Fd, "Test cases which not are implemented yet are written in <B>bold face</B>.~n~n", []),
+
+ io:format(Fd, "<BR><BR>~n", []),
+ io:format(Fd, "~n<DL>~n", []),
+ do_doc(Fd, TestCases, []),
+ io:format(Fd, "</DL>~n", []),
+ file:close(Fd);
+doc(TestCases) ->
+ doc([TestCases]).
+
+do_doc(Fd, [H | T], List) ->
+ case H of
+ {Module, TestCase} when is_atom(Module), is_atom(TestCase) ->
+ do_doc(Fd, Module, TestCase, List);
+ TestCase when is_atom(TestCase), List == [] ->
+ do_doc(Fd, mnesia_SUITE, TestCase, List);
+ TestCase when is_atom(TestCase) ->
+ do_doc(Fd, hd(List), TestCase, List)
+ end,
+ do_doc(Fd, T, List);
+do_doc(_, [], _) ->
+ ok.
+
+do_doc(Fd, Module, TestCase, List) ->
+ case get_suite(Module, TestCase) of
+ [] ->
+ %% Implemented leaf test case
+ Head = ?flat_format("<A HREF=~p.html#~p_1>{~p, ~p}</A>}",
+ [Module, TestCase, Module, TestCase]),
+ print_doc(Fd, Module, TestCase, Head);
+ Suite when is_list(Suite) ->
+ %% Test suite
+ Head = ?flat_format("{~p, ~p}", [Module, TestCase]),
+ print_doc(Fd, Module, TestCase, Head),
+ io:format(Fd, "~n<DL>~n", []),
+ do_doc(Fd, Suite, [Module | List]),
+ io:format(Fd, "</DL>~n", []);
+ 'NYI' ->
+ %% Not yet implemented
+ Head = ?flat_format("<B>{~p, ~p}</B>", [Module, TestCase]),
+ print_doc(Fd, Module, TestCase, Head)
+ end.
+
+print_doc(Fd, Mod, Fun, Head) ->
+ case catch (apply(Mod, Fun, [doc])) of
+ {'EXIT', _} ->
+ io:format(Fd, "<DT>~s</DT>~n", [Head]);
+ Doc when is_list(Doc) ->
+ io:format(Fd, "<DT><U>~s</U><BR><DD>~n", [Head]),
+ print_rows(Fd, Doc),
+ io:format(Fd, "</DD><BR><BR>~n", [])
+ end.
+
+print_rows(_Fd, []) ->
+ ok;
+print_rows(Fd, [H | T]) when is_list(H) ->
+ io:format(Fd, "~s~n", [H]),
+ print_rows(Fd, T);
+print_rows(Fd, [H | T]) when is_integer(H) ->
+ io:format(Fd, "~s~n", [[H | T]]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Show the test case structure
+
+struct(TestCases) ->
+ T = test(TestCases, suite),
+ struct(T, "").
+
+struct({Module, TestCase}, Indentation)
+ when is_atom(Module), is_atom(TestCase) ->
+ log("~s{~p, ~p} ...~n", [Indentation, Module, TestCase]);
+struct({Module, TestCase, Other}, Indentation)
+ when is_atom(Module), is_atom(TestCase) ->
+ log("~s{~p, ~p} ~p~n", [Indentation, Module, TestCase, Other]);
+struct([], _) ->
+ ok;
+struct([TestCase | TestCases], Indentation) ->
+ struct(TestCase, Indentation),
+ struct(TestCases, Indentation);
+struct({TestCase, []}, Indentation) ->
+ struct(TestCase, Indentation);
+struct({TestCase, SubTestCases}, Indentation) when is_list(SubTestCases) ->
+ struct(TestCase, Indentation),
+ struct(SubTestCases, Indentation ++ " ").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Execute the test cases
+
+test(TestCases) ->
+ test(TestCases, []).
+
+test(TestCases, suite) when is_list(TestCases) ->
+ test_driver(TestCases, suite);
+test(TestCases, Config) when is_list(TestCases) ->
+ D1 = lists:duplicate(10, $=),
+ D2 = lists:duplicate(10, $ ),
+ log("~n~s TEST CASES: ~p~n ~sCONFIG: ~p~n~n", [D1, TestCases, D2, Config]),
+ test_driver(TestCases, Config);
+test(TestCase, Config) ->
+ test([TestCase], Config).
+
+test_driver([], _Config) ->
+ [];
+test_driver([T|TestCases], Config) ->
+ L1 = test_driver(T, Config),
+ L2 = test_driver(TestCases, Config),
+ [L1|L2];
+test_driver({Module, TestCases}, Config) when is_list(TestCases)->
+ test_driver(default_module(Module, TestCases), Config);
+test_driver({_, {Module, TestCase}}, Config) ->
+ test_driver({Module, TestCase}, Config);
+test_driver({Module, TestCase}, Config) ->
+ Sec = timer:seconds(1) * 1000,
+ case get_suite(Module, TestCase) of
+ [] when Config == suite ->
+ {Module, TestCase, 'IMPL'};
+ [] ->
+ log("Eval test case: ~w~n", [{Module, TestCase}]),
+ {T, Res} =
+ timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]),
+ log("Tested ~w in ~w sec~n", [TestCase, T div Sec]),
+ {T div Sec, Res};
+ Suite when is_list(Suite), Config == suite ->
+ Res = test_driver(default_module(Module, Suite), Config),
+ {{Module, TestCase}, Res};
+ Suite when is_list(Suite) ->
+ log("Expand test case ~w~n", [{Module, TestCase}]),
+ Def = default_module(Module, Suite),
+ {T, Res} = timer:tc(?MODULE, test_driver, [Def, Config]),
+ {T div Sec, {{Module, TestCase}, Res}};
+ 'NYI' when Config == suite ->
+ {Module, TestCase, 'NYI'};
+ 'NYI' ->
+ log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]),
+ {0, {skip, {Module, TestCase}, "NYI"}}
+ end;
+test_driver(TestCase, Config) ->
+ DefaultModule = mnesia_SUITE,
+ log("<>WARNING<> Missing module in test case identifier. "
+ "{~w, ~w} assumed~n", [DefaultModule, TestCase]),
+ test_driver({DefaultModule, TestCase}, Config).
+
+default_module(DefaultModule, TestCases) when is_list(TestCases) ->
+ Fun = fun(T) ->
+ case T of
+ {_, _} -> true;
+ T -> {true, {DefaultModule, T}}
+ end
+ end,
+ lists:zf(Fun, TestCases).
+
+%% Returns a list (possibly empty) or the atom 'NYI'
+get_suite(Mod, Fun) ->
+ case catch (apply(Mod, Fun, [suite])) of
+ {'EXIT', _} -> 'NYI';
+ List when is_list(List) -> List
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+eval_test_case(Mod, Fun, Config) ->
+ flush(),
+ global:register_name(mnesia_test_case_sup, self()),
+ Flag = process_flag(trap_exit, true),
+ Pid = spawn_link(?MODULE, test_case_evaluator, [Mod, Fun, [Config]]),
+ R = wait_for_evaluator(Pid, Mod, Fun, Config),
+ global:unregister_name(mnesia_test_case_sup),
+ process_flag(trap_exit, Flag),
+ R.
+
+flush() ->
+ receive Msg -> [Msg | flush()]
+ after 0 -> []
+ end.
+
+wait_for_evaluator(Pid, Mod, Fun, Config) ->
+ receive
+ {'EXIT', Pid, {test_case_ok, _PidRes}} ->
+ Errors = flush(),
+ Res =
+ case Errors of
+ [] -> ok;
+ Errors -> failed
+ end,
+ {Res, {Mod, Fun}, Errors};
+ {'EXIT', Pid, {skipped, Reason}} ->
+ log("<WARNING> Test case ~w skipped, because ~p~n",
+ [{Mod, Fun}, Reason]),
+ Mod:end_per_testcase(Fun, Config),
+ {skip, {Mod, Fun}, Reason};
+ {'EXIT', Pid, Reason} ->
+ log("<>ERROR<> Eval process ~w exited, because ~p~n",
+ [{Mod, Fun}, Reason]),
+ Mod:end_per_testcase(Fun, Config),
+ {crash, {Mod, Fun}, Reason}
+ end.
+
+test_case_evaluator(Mod, Fun, [Config]) ->
+ NewConfig = Mod:init_per_testcase(Fun, Config),
+ R = apply(Mod, Fun, [NewConfig]),
+ Mod:end_per_testcase(Fun, NewConfig),
+ exit({test_case_ok, R}).
+
+activity_evaluator(Coordinator) ->
+ activity_evaluator_loop(Coordinator),
+ exit(normal).
+
+activity_evaluator_loop(Coordinator) ->
+ receive
+ begin_trans ->
+ transaction(Coordinator, 0);
+ {begin_trans, MaxRetries} ->
+ transaction(Coordinator, MaxRetries);
+ end_trans ->
+ end_trans;
+ Fun when is_function(Fun) ->
+ Coordinator ! {self(), Fun()},
+ activity_evaluator_loop(Coordinator);
+% {'EXIT', Coordinator, Reason} ->
+% Reason;
+ ExitExpr ->
+% ?error("activity_evaluator_loop ~p ~p: exit(~p)~n}", [Coordinator, self(), ExitExpr]),
+ exit(ExitExpr)
+ end.
+
+transaction(Coordinator, MaxRetries) ->
+ Fun = fun() ->
+ Coordinator ! {self(), begin_trans},
+ activity_evaluator_loop(Coordinator)
+ end,
+ Coordinator ! {self(), mnesia:transaction(Fun, MaxRetries)},
+ activity_evaluator_loop(Coordinator).
+
+pick_msg() ->
+ receive
+ Message -> Message
+ after 4000 -> timeout
+ end.
+
+start_activities(Nodes) ->
+ Fun = fun(N) -> spawn_link(N, ?MODULE, activity_evaluator, [self()]) end,
+ Pids = mapl(Fun, Nodes),
+ {success, Pids}.
+
+mapl(Fun, [H|T]) ->
+ Res = Fun(H),
+ [Res|mapl(Fun, T)];
+mapl(_Fun, []) ->
+ [].
+
+diskless(Config) ->
+ case lists:keysearch(diskless, 1, Config) of
+ {value, {diskless, true}} ->
+ true;
+ _Else ->
+ false
+ end.
+
+
+start_transactions(Pids) ->
+ Fun = fun(Pid) ->
+ Pid ! begin_trans,
+ ?match_receive({Pid, begin_trans})
+ end,
+ mapl(Fun, Pids).
+
+start_sync_transactions(Pids) ->
+ Nodes = [node(Pid) || Pid <- Pids],
+ Fun = fun(Pid) ->
+ sync_trans_tid_serial(Nodes),
+ Pid ! begin_trans,
+ ?match_receive({Pid, begin_trans})
+ end,
+ mapl(Fun, Pids).
+
+
+start_transactions(Pids, MaxRetries) ->
+ Fun = fun(Pid) ->
+ Pid ! {begin_trans, MaxRetries},
+ ?match_receive({Pid, begin_trans})
+ end,
+ mapl(Fun, Pids).
+
+start_sync_transactions(Pids, MaxRetries) ->
+ Nodes = [node(Pid) || Pid <- Pids],
+ Fun = fun(Pid) ->
+ sync_trans_tid_serial(Nodes),
+ Pid ! {begin_trans, MaxRetries},
+ ?match_receive({Pid, begin_trans})
+ end,
+ mapl(Fun, Pids).
+
+sync_trans_tid_serial(Nodes) ->
+ Fun = fun() -> mnesia:write_lock_table(schema) end,
+ rpc:multicall(Nodes, mnesia, transaction, [Fun]).
+
+select_nodes(N, Config, File, Line) ->
+ prepare_test_case([], N, Config, File, Line).
+
+prepare_test_case(Actions, N, Config, File, Line) ->
+ NodeList1 = lookup_config(nodes, Config),
+ NodeList2 = lookup_config(nodenames, Config), %% For testserver
+ NodeList3 = append_unique(NodeList1, NodeList2),
+ This = node(),
+ All = [This | lists:delete(This, NodeList3)],
+ Selected = pick_nodes(N, All, File, Line),
+ case diskless(Config) of
+ true ->
+ ok;
+ false ->
+ rpc:multicall(Selected, application, set_env,[mnesia, schema_location, opt_disc])
+ end,
+ do_prepare(Actions, Selected, All, Config, File, Line).
+
+do_prepare([], Selected, _All, _Config, _File, _Line) ->
+ Selected;
+do_prepare([{init_test_case, Appls} | Actions], Selected, All, Config, File, Line) ->
+ set_kill_timer(Config),
+ Started = init_nodes(Selected, File, Line),
+ All2 = append_unique(Started, All),
+ Alive = mnesia_lib:intersect(nodes() ++ [node()], All2),
+ kill_appls(Appls, Alive),
+ process_flag(trap_exit, true),
+ do_prepare(Actions, Started, All2, Config, File, Line);
+do_prepare([delete_schema | Actions], Selected, All, Config, File, Line) ->
+ Alive = mnesia_lib:intersect(nodes() ++ [node()], All),
+ case diskless(Config) of
+ true ->
+ skip;
+ false ->
+ Del = fun(Node) ->
+ case mnesia:delete_schema([Node]) of
+ ok -> ok;
+ {error, {"All nodes not running",_}} ->
+ ok;
+ Else ->
+ ?log("Delete schema error ~p ~n", [Else])
+ end
+ end,
+ lists:foreach(Del, Alive)
+ end,
+ do_prepare(Actions, Selected, All, Config, File, Line);
+do_prepare([create_schema | Actions], Selected, All, Config, File, Line) ->
+ case diskless(Config) of
+ true ->
+ skip;
+ _Else ->
+ case mnesia:create_schema(Selected) of
+ ok ->
+ ignore;
+ BadNodes ->
+ ?fatal("Cannot create Mnesia schema on ~p~n", [BadNodes])
+ end
+ end,
+ do_prepare(Actions, Selected, All, Config, File, Line);
+do_prepare([{start_appls, Appls} | Actions], Selected, All, Config, File, Line) ->
+ case start_appls(Appls, Selected, Config) of
+ [] -> ok;
+ Bad -> ?fatal("Cannot start appls ~p: ~p~n", [Appls, Bad])
+ end,
+ do_prepare(Actions, Selected, All, Config, File, Line);
+do_prepare([{reload_appls, Appls} | Actions], Selected, All, Config, File, Line) ->
+ reload_appls(Appls, Selected),
+ do_prepare(Actions, Selected, All, Config, File, Line).
+
+set_kill_timer(Config) ->
+ case init:get_argument(mnesia_test_timeout) of
+ {ok, _ } -> ok;
+ _ ->
+ Time0 =
+ case lookup_config(tc_timeout, Config) of
+ [] -> timer:minutes(5);
+ ConfigTime when is_integer(ConfigTime) -> ConfigTime
+ end,
+ Mul = try
+ test_server:timetrap_scale_factor()
+ catch _:_ -> 1 end,
+ (catch test_server:timetrap(Mul*Time0 + 1000)),
+ spawn_link(?MODULE, kill_tc, [self(),Time0*Mul])
+ end.
+
+kill_tc(Pid, Time) ->
+ receive
+ after Time ->
+ case process_info(Pid) of
+ undefined -> ok;
+ _ ->
+ ?error("Watchdog in test case timed out "
+ "in ~p min~n", [Time div (1000*60)]),
+ Files = mnesia_lib:dist_coredump(),
+ ?log("Cores dumped to:~n ~p~n", [Files]),
+ %% Genarate erlang crashdumps.
+ %% GenDump = fun(Node) ->
+ %% File = "CRASH_" ++ atom_to_list(Node) ++ ".dump",
+ %% rpc:call(Node, os, putenv, ["ERL_CRASH_DUMP", File]),
+ %% rpc:cast(Node, erlang, halt, ["RemoteTimeTrap"])
+ %% end,
+ %% [GenDump(Node) || Node <- nodes()],
+
+ %% erlang:halt("DebugTimeTrap"),
+ exit(Pid, kill)
+ end
+ end.
+
+
+append_unique([], List) -> List;
+append_unique([H|R], List) ->
+ case lists:member(H, List) of
+ true -> append_unique(R, List);
+ false -> [H | append_unique(R, List)]
+ end.
+
+pick_nodes(all, Nodes, File, Line) ->
+ pick_nodes(length(Nodes), Nodes, File, Line);
+pick_nodes(N, [H | T], File, Line) when N > 0 ->
+ [H | pick_nodes(N - 1, T, File, Line)];
+pick_nodes(0, _Nodes, _File, _Line) ->
+ [];
+pick_nodes(N, [], File, Line) ->
+ ?skip("Test case (~p(~p)) ignored: ~p nodes missing~n",
+ [File, Line, N]).
+
+init_nodes([Node | Nodes], File, Line) ->
+ case net_adm:ping(Node) of
+ pong ->
+ [Node | init_nodes(Nodes, File, Line)];
+ pang ->
+ [Name, Host] = node_to_name_and_host(Node),
+ case slave_start_link(Host, Name) of
+ {ok, Node1} ->
+ Path = code:get_path(),
+ true = rpc:call(Node1, code, set_path, [Path]),
+ [Node1 | init_nodes(Nodes, File, Line)];
+ Other ->
+ ?skip("Test case (~p(~p)) ignored: cannot start node ~p: ~p~n",
+ [File, Line, Node, Other])
+ end
+ end;
+init_nodes([], _File, _Line) ->
+ [].
+
+%% Returns [Name, Host]
+node_to_name_and_host(Node) ->
+ string:tokens(atom_to_list(Node), [$@]).
+
+lookup_config(Key,Config) ->
+ case lists:keysearch(Key,1,Config) of
+ {value,{Key,Val}} ->
+ Val;
+ _ ->
+ []
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start_appls(Appls, Nodes) ->
+ start_appls(Appls, Nodes, [], [schema]).
+
+start_appls(Appls, Nodes, Config) ->
+ start_appls(Appls, Nodes, Config, [schema]).
+
+start_appls([Appl | Appls], Nodes, Config, Tabs) ->
+ {Started, BadStarters} =
+ rpc:multicall(Nodes, ?MODULE, remote_start, [Appl, Config, Nodes]),
+ BadS = [{Node, Appl, Res} || {Node, Res} <- Started, Res /= ok],
+ BadN = [{BadNode, Appl, bad_start} || BadNode <- BadStarters],
+ Bad = BadS ++ BadN,
+ case Appl of
+ mnesia when Bad == [] ->
+ sync_tables(Nodes, Tabs);
+ _ ->
+ ignore
+ end,
+ Bad ++ start_appls(Appls, Nodes, Config, Tabs);
+start_appls([], _Nodes, _Config, _Tabs) ->
+ [].
+
+remote_start(mnesia, Config, Nodes) ->
+ case diskless(Config) of
+ true ->
+ application_controller:set_env(mnesia,
+ extra_db_nodes,
+ Nodes -- [node()]),
+ application_controller:set_env(mnesia,
+ schema_location,
+ ram);
+ false ->
+ application_controller:set_env(mnesia,
+ schema_location,
+ opt_disc),
+ ignore
+ end,
+ {node(), mnesia:start()};
+remote_start(Appl, _Config, _Nodes) ->
+ Res =
+ case application:start(Appl) of
+ {error, {already_started, Appl}} ->
+ ok;
+ Other ->
+ Other
+ end,
+ {node(), Res}.
+
+%% Start Mnesia on all given nodes and wait for specified
+%% tables to be accessible on each node. The atom all means
+%% that we should wait for all tables to be loaded
+%%
+%% Returns a list of error tuples {BadNode, mnesia, Reason}
+start_mnesia(Nodes) ->
+ start_appls([mnesia], Nodes).
+start_mnesia(Nodes, Tabs) when is_list(Nodes) ->
+ start_appls([mnesia], Nodes, [], Tabs).
+
+%% Wait for the tables to be accessible from all nodes in the list
+%% and that all nodes are aware of that the other nodes also ...
+sync_tables(Nodes, Tabs) ->
+ Res = send_wait(Nodes, Tabs, []),
+ if
+ Res == [] ->
+ mnesia:transaction(fun() -> mnesia:write_lock_table(schema) end),
+ Res;
+ true ->
+ Res
+ end.
+
+send_wait([Node | Nodes], Tabs, Pids) ->
+ Pid = spawn_link(Node, ?MODULE, start_wait, [self(), Tabs]),
+ send_wait(Nodes, Tabs, [Pid | Pids]);
+send_wait([], _Tabs, Pids) ->
+ rec_wait(Pids, []).
+
+rec_wait([Pid | Pids], BadRes) ->
+ receive
+ {'EXIT', Pid, R} ->
+ rec_wait(Pids, [{node(Pid), bad_wait, R} | BadRes]);
+ {Pid, ok} ->
+ rec_wait(Pids, BadRes);
+ {Pid, {error, R}} ->
+ rec_wait(Pids, [{node(Pid), bad_wait, R} | BadRes])
+ end;
+rec_wait([], BadRes) ->
+ BadRes.
+
+start_wait(Coord, Tabs) ->
+ process_flag(trap_exit, true),
+ Mon = whereis(mnesia_monitor),
+ case catch link(Mon) of
+ {'EXIT', _} ->
+ unlink(Coord),
+ Coord ! {self(), {error, {node_not_running, node()}}};
+ _ ->
+ Res = start_wait_loop(Tabs),
+ unlink(Mon),
+ unlink(Coord),
+ Coord ! {self(), Res}
+ end.
+
+start_wait_loop(Tabs) ->
+ receive
+ {'EXIT', Pid, Reason} ->
+ {error, {start_wait, Pid, Reason}}
+ after 0 ->
+ case mnesia:wait_for_tables(Tabs, timer:seconds(30)) of
+ ok ->
+ verify_nodes(Tabs);
+ {timeout, BadTabs} ->
+ log("<>WARNING<> Wait for tables ~p: ~p~n", [node(), Tabs]),
+ start_wait_loop(BadTabs);
+ {error, Reason} ->
+ {error, {start_wait, Reason}}
+ end
+ end.
+
+verify_nodes(Tabs) ->
+ verify_nodes(Tabs, 0).
+
+verify_nodes([], _) ->
+ ok;
+
+verify_nodes([Tab| Tabs], N) ->
+ ?match(X when is_atom(X), mnesia_lib:val({Tab, where_to_read})),
+ Nodes = mnesia:table_info(Tab, where_to_write),
+ Copies =
+ mnesia:table_info(Tab, disc_copies) ++
+ mnesia:table_info(Tab, disc_only_copies) ++
+ mnesia:table_info(Tab, ram_copies),
+ Local = mnesia:table_info(Tab, local_content),
+ case Copies -- Nodes of
+ [] ->
+ verify_nodes(Tabs, 0);
+ _Else when Local == true, Nodes /= [] ->
+ verify_nodes(Tabs, 0);
+ Else ->
+ N2 =
+ if
+ N > 20 ->
+ log("<>WARNING<> ~w Waiting for table: ~p on ~p ~n",
+ [node(), Tab, Else]),
+ 0;
+ true -> N+1
+ end,
+ timer:sleep(500),
+ verify_nodes([Tab| Tabs], N2)
+ end.
+
+
+%% Nicely stop Mnesia on all given nodes
+%%
+%% Returns a list of error tuples {BadNode, Reason}
+stop_mnesia(Nodes) when is_list(Nodes) ->
+ stop_appls([mnesia], Nodes).
+
+stop_appls([Appl | Appls], Nodes) when is_list(Nodes) ->
+ {Stopped, BadNodes} = rpc:multicall(Nodes, ?MODULE, remote_stop, [Appl]),
+ BadS =[{Node, Appl, Res} || {Node, Res} <- Stopped, Res /= stopped],
+ BadN =[{BadNode, Appl, bad_node} || BadNode <- BadNodes],
+ BadS ++ BadN ++ stop_appls(Appls, Nodes);
+stop_appls([], _Nodes) ->
+ [].
+
+remote_stop(mnesia) ->
+ {node(), mnesia:stop()};
+remote_stop(Appl) ->
+ {node(), application:stop(Appl)}.
+
+remote_kill([Appl | Appls]) ->
+ catch Appl:lkill(),
+ application:stop(Appl),
+ remote_kill(Appls);
+remote_kill([]) ->
+ ok.
+
+%% Abruptly kill Mnesia on all given nodes
+%% Returns []
+kill_appls(Appls, Nodes) when is_list(Nodes) ->
+ verbose("<>WARNING<> Intentionally killing ~p: ~w...~n",
+ [Appls, Nodes], ?FILE, ?LINE),
+ rpc:multicall(Nodes, ?MODULE, remote_kill, [Appls]),
+ [].
+
+kill_mnesia(Nodes) when is_list(Nodes) ->
+ kill_appls([mnesia], Nodes).
+
+reload_appls([Appl | Appls], Selected) ->
+ kill_appls([Appl], Selected),
+ timer:sleep(1000),
+ Ok = {[ok || _N <- Selected], []},
+ {Ok2temp, Empty} = rpc:multicall(Selected, application, unload, [Appl]),
+ Conv = fun({error,{not_loaded,mnesia}}) -> ok; (Else) -> Else end,
+ Ok2 = {lists:map(Conv, Ok2temp), Empty},
+
+ Ok3 = rpc:multicall(Selected, application, load, [Appl]),
+ if
+ Ok /= Ok2 ->
+ ?fatal("Cannot unload appl ~p: ~p~n", [Appl, Ok2]);
+ Ok /= Ok3 ->
+ ?fatal("Cannot load appl ~p: ~p~n", [Appl, Ok3]);
+ true ->
+ ok
+ end,
+ reload_appls(Appls, Selected);
+reload_appls([], _Selected) ->
+ ok.
+
+shutdown() ->
+ log("<>WARNING<> Intentionally shutting down all nodes... ~p~n",
+ [nodes() ++ [node()]]),
+ rpc:multicall(nodes(), erlang, halt, []),
+ erlang:halt().
+
+verify_mnesia(Ups, Downs, File, Line) when is_list(Ups), is_list(Downs) ->
+ BadUps =
+ [N || N <- Ups, rpc:call(N, mnesia, system_info, [is_running]) /= yes],
+ BadDowns =
+ [N || N <- Downs, rpc:call(N, mnesia, system_info, [is_running]) == yes],
+ if
+ BadUps == [] ->
+ ignore;
+ true ->
+ error("Mnesia is not running as expected: ~p~n",
+ [BadUps], File, Line)
+ end,
+ if
+ BadDowns == [] ->
+ ignore;
+ true ->
+ error("Mnesia is not stopped as expected: ~p~n",
+ [BadDowns], File, Line)
+ end,
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+verify_replica_location(Tab, [], [], [], _) ->
+ ?match({'EXIT', _}, mnesia:table_info(Tab, ram_copies)),
+ ?match({'EXIT', _}, mnesia:table_info(Tab, disc_copies)),
+ ?match({'EXIT', _}, mnesia:table_info(Tab, disc_only_copies)),
+ ?match({'EXIT', _}, mnesia:table_info(Tab, where_to_write)),
+ ?match({'EXIT', _}, mnesia:table_info(Tab, where_to_read)),
+ [];
+
+verify_replica_location(Tab, DiscOnly0, Ram0, Disc0, AliveNodes0) ->
+%% sync_tables(AliveNodes0, [Tab]),
+ AliveNodes = lists:sort(AliveNodes0),
+ DiscOnly = lists:sort(DiscOnly0),
+ Ram = lists:sort(Ram0),
+ Disc = lists:sort(Disc0),
+ Write = ignore_dead(DiscOnly ++ Ram ++ Disc, AliveNodes),
+ Read = ignore_dead(DiscOnly ++ Ram ++ Disc, AliveNodes),
+ This = node(),
+
+ timer:sleep(100),
+
+ S1 = ?match(AliveNodes, lists:sort(mnesia:system_info(running_db_nodes))),
+ S2 = ?match(DiscOnly, lists:sort(mnesia:table_info(Tab, disc_only_copies))),
+ S3 = ?match(Ram, lists:sort(mnesia:table_info(Tab, ram_copies))),
+ S4 = ?match(Disc, lists:sort(mnesia:table_info(Tab, disc_copies))),
+ S5 = ?match(Write, lists:sort(mnesia:table_info(Tab, where_to_write))),
+ S6 = case lists:member(This, Read) of
+ true ->
+ ?match(This, mnesia:table_info(Tab, where_to_read));
+ false ->
+ ?match(true, lists:member(mnesia:table_info(Tab, where_to_read), Read))
+ end,
+ lists:filter(fun({success,_}) -> false; (_) -> true end, [S1,S2,S3,S4,S5,S6]).
+
+ignore_dead(Nodes, AliveNodes) ->
+ Filter = fun(Node) -> lists:member(Node, AliveNodes) end,
+ lists:sort(lists:zf(Filter, Nodes)).
+
+
+remote_activate_debug_fun(N, I, F, C, File, Line) ->
+ Pid = spawn_link(N, ?MODULE, do_remote_activate_debug_fun, [self(), I, F, C, File, Line]),
+ receive
+ {activated, Pid} -> ok;
+ {'EXIT', Pid, Reason} -> {error, Reason}
+ end.
+
+do_remote_activate_debug_fun(From, I, F, C, File, Line) ->
+ mnesia_lib:activate_debug_fun(I, F, C, File, Line),
+ From ! {activated, self()},
+ timer:sleep(infinity). % Dies whenever the test process dies !!
+
+
+sort(L) when is_list(L) ->
+ lists:sort(L);
+sort({atomic, L}) when is_list(L) ->
+ {atomic, lists:sort(L)};
+sort({ok, L}) when is_list(L) ->
+ {ok, lists:sort(L)};
+sort(W) ->
+ W.
diff --git a/lib/mnesia/test/mnesia_test_lib.hrl b/lib/mnesia/test/mnesia_test_lib.hrl
new file mode 100644
index 0000000000..fc377dbd2c
--- /dev/null
+++ b/lib/mnesia/test/mnesia_test_lib.hrl
@@ -0,0 +1,132 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-define(log(Format,Args),mnesia_test_lib:log(Format,Args,?FILE,?LINE)).
+-define(warning(Format,Args),?log("<>WARNING<>~n " ++ Format,Args)).
+-define(error(Format,Args),
+ mnesia_test_lib:error(Format,Args,?FILE,?LINE)).
+-define(verbose(Format,Args),mnesia_test_lib:verbose(Format,Args,?FILE,?LINE)).
+
+-define(fatal(Format,Args),
+ ?error(Format, Args),
+ exit({test_case_fatal, Format, Args, ?FILE, ?LINE})).
+
+-define(skip(Format,Args),
+ ?warning(Format, Args),
+ exit({skipped, ?flat_format(Format, Args)})).
+
+-define(flat_format(Format,Args),
+ lists:flatten(io_lib:format(Format, Args))).
+
+-define(sort(What), mnesia_test_lib:sort(What)).
+
+-define(ignore(Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ ?verbose("ok, ~n Result as expected:~p~n",[AcTuAlReS]),
+ AcTuAlReS
+ end()).
+
+-define(match(ExpectedRes,Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ case AcTuAlReS of
+ ExpectedRes ->
+ ?verbose("ok, ~n Result as expected:~p~n",[AcTuAlReS]),
+ {success,AcTuAlReS};
+ _ ->
+ ?error("Not Matching Actual result was:~n ~p~n",
+ [AcTuAlReS]),
+ {fail,AcTuAlReS}
+ end
+ end()).
+
+-define(match_inverse(NotExpectedRes,Expr),
+ fun() ->
+ AcTuAlReS = (catch (Expr)),
+ case AcTuAlReS of
+ NotExpectedRes ->
+ ?error("Not matching Actual result was:~n ~p~n",
+ [AcTuAlReS]),
+ {fail,AcTuAlReS};
+ _ ->
+ ?verbose("ok, ~n Result as expected: ~p~n",[AcTuAlReS]),
+ {success,AcTuAlReS}
+ end
+ end()).
+
+-define(match_receive(ExpectedMsg),
+ ?match(ExpectedMsg,mnesia_test_lib:pick_msg())).
+
+%% ExpectedMsgs must be completely bound
+-define(match_multi_receive(ExpectedMsgs),
+ fun() ->
+ TmPeXpCtEdMsGs = lists:sort(ExpectedMsgs),
+ ?match(TmPeXpCtEdMsGs,
+ lists:sort(lists:map(fun(_) ->
+ mnesia_test_lib:pick_msg()
+ end,
+ TmPeXpCtEdMsGs)))
+ end()).
+
+-define(start_activities(Nodes),
+ mnesia_test_lib:start_activities(Nodes)).
+
+-define(start_transactions(Pids),
+ mnesia_test_lib:start_transactions(Pids)).
+
+-define(acquire_nodes(N, Config),
+ mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]},
+ delete_schema,
+ create_schema,
+ {start_appls, [mnesia]}],
+ N, Config, ?FILE, ?LINE)).
+
+-define(activate_debug_fun(I, F, C),
+ mnesia_lib:activate_debug_fun(I, F, C, ?FILE, ?LINE)).
+
+-define(remote_activate_debug_fun(N, I, F, C),
+ ?match(ok, mnesia_test_lib:remote_activate_debug_fun(N, I, F, C,
+ ?FILE, ?LINE))).
+
+-define(deactivate_debug_fun(I),
+ mnesia_lib:deactivate_debug_fun(I, ?FILE, ?LINE)).
+
+-define(remote_deactivate_debug_fun(N, I),
+ rpc:call(N, mnesia_lib, deactivate_debug_fun, [I, ?FILE, ?LINE])).
+
+-define(is_debug_compiled,
+ case mnesia_lib:is_debug_compiled() of
+ false ->
+ ?skip("Mnesia is not debug compiled, test case ignored.~n", []);
+ _OhTeR ->
+ ok
+ end).
+
+-define(needs_disc(Config),
+ case mnesia_test_lib:diskless(Config) of
+ false ->
+ ok;
+ true ->
+ ?skip("Must have disc, test case ignored.~n", [])
+ end).
+
+-define(verify_mnesia(Ups, Downs),
+ mnesia_test_lib:verify_mnesia(Ups, Downs, ?FILE, ?LINE)).
diff --git a/lib/mnesia/test/mnesia_tpcb.erl b/lib/mnesia/test/mnesia_tpcb.erl
new file mode 100644
index 0000000000..595412ff24
--- /dev/null
+++ b/lib/mnesia/test/mnesia_tpcb.erl
@@ -0,0 +1,1268 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%
+%% MODULE
+%%
+%% mnesia_tpcb - TPC-B benchmarking of Mnesia
+%%
+%% DESCRIPTION
+%%
+%% The metrics used in the TPC-B benchmark are throughput as measured
+%% in transactions per second (TPS). The benchmark uses a single,
+%% simple update-intensive transaction to load the database system.
+%% The single transaction type provides a simple, repeatable
+%% unit of work, and is designed to exercise the basic components of
+%% a database system.
+%%
+%% The definition of the TPC-B states lots of detailed rules and
+%% conditions that must be fullfilled, e.g. how the ACID (atomicity,
+%% consistency, isolation and durability) properties are verified,
+%% how the random numbers must be distributed, minimum sizes of
+%% the different types of records, minimum duration of the benchmark,
+%% formulas to calculate prices (dollars per tps), disclosure issues
+%% etc. Please, see http://www.tpc.org/ about the nitty gritty details.
+%%
+%% The TPC-B benchmark is stated in terms of a hypothetical bank. The
+%% bank has one or more branches. Each branch has multiple tellers. The
+%% bank has many customers, each with an account. The database represents
+%% the cash position of each entity (branch, teller and account) and a
+%% history of recent transactions run by the bank. The transaction
+%% represents the work done when a customer makes a deposit or a
+%% withdrawal against his account. The transaction is performed by a
+%% teller at some branch.
+%%
+%% Each process that performs TPC-B transactions is called a driver.
+%% Drivers generates teller_id, account_id and delta amount of
+%% money randomly. An account, a teller and a branch are read, their
+%% balances are adjusted and a history record is created. The driver
+%% measures the time for 3 reads, 3 writes and 1 create.
+%%
+%% GETTING STARTED
+%%
+%% Generate tables and run with default configuration:
+%%
+%% mnesia_tpcb:start().
+%%
+%% A little bit more advanced;
+%%
+%% spawn(mnesia_tpcb, start, [[[{n_drivers_per_node, 8}, {stop_after, infinity}]]),
+%% mnesia_tpcb:stop().
+%%
+%% Really advanced;
+%%
+%% mnesia_tpcb:init(([{n_branches, 8}, {replica_type, disc_only_copies}]),
+%% mnesia_tpcb:run(([{n_drivers_per_node, 8}]),
+%% mnesia_tpcb:run(([{n_drivers_per_node, 64}]).
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(mnesia_tpcb).
+-author('[email protected]').
+
+-export([
+ config/2,
+ count_balance/0,
+ driver_init/2,
+ init/1,
+ reporter_init/2,
+ run/1,
+ start/0,
+ start/1,
+ start/2,
+ stop/0,
+ real_trans/5,
+ verify_tabs/0,
+ reply_gen_branch/3,
+ frag_add_delta/7,
+
+ conflict_test/1,
+ dist_test/1,
+ replica_test/1,
+ sticky_replica_test/1,
+ remote_test/1,
+ remote_frag2_test/1
+ ]).
+
+-define(SECOND, 1000000).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Account record, total size must be at least 100 bytes
+
+-define(ACCOUNT_FILLER,
+ {123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234}).
+
+-record(account,
+ {
+ id = 0, % Unique account id
+ branch_id = 0, % Branch where the account is held
+ balance = 0, % Account balance
+ filler = ?ACCOUNT_FILLER % Gap filler to ensure size >= 100 bytes
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Branch record, total size must be at least 100 bytes
+
+-define(BRANCH_FILLER,
+ {123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890}).
+
+-record(branch,
+ {
+ id = 0, % Unique branch id
+ balance = 0, % Total balance of whole branch
+ filler = ?BRANCH_FILLER % Gap filler to ensure size >= 100 bytes
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Teller record, total size must be at least 100 bytes
+
+-define(TELLER_FILLER,
+ {123456789012345678901234567890123456789012345678901234567890,
+ 123456789012345678901234567890123456789012345678901234567890,
+ 1234567890123456789012345678901234567890123456789012345678}).
+
+-record(teller,
+ {
+ id = 0, % Unique teller id
+ branch_id = 0, % Branch where the teller is located
+ balance = 0, % Teller balance
+ filler = ?TELLER_FILLER % Gap filler to ensure size >= 100 bytes
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% History record, total size must be at least 50 bytes
+
+-define(HISTORY_FILLER, 1234567890).
+
+-record(history,
+ {
+ history_id = {0, 0}, % {DriverId, DriverLocalHistoryid}
+ time_stamp = now(), % Time point during active transaction
+ branch_id = 0, % Branch associated with teller
+ teller_id = 0, % Teller invlolved in transaction
+ account_id = 0, % Account updated by transaction
+ amount = 0, % Amount (delta) specified by transaction
+ filler = ?HISTORY_FILLER % Gap filler to ensure size >= 50 bytes
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-record(tab_config,
+ {
+ db_nodes = [node()],
+ n_replicas = 1, % Ignored for non-fragmented tables
+ replica_nodes = [node()],
+ replica_type = ram_copies,
+ use_running_mnesia = false,
+ n_fragments = 0,
+ n_branches = 1,
+ n_tellers_per_branch = 10, % Must be 10
+ n_accounts_per_branch = 100000, % Must be 100000
+ branch_filler = ?BRANCH_FILLER,
+ account_filler = ?ACCOUNT_FILLER,
+ teller_filler = ?TELLER_FILLER
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(run_config,
+ {
+ driver_nodes = [node()],
+ n_drivers_per_node = 1,
+ use_running_mnesia = false,
+ stop_after = timer:minutes(15), % Minimum 15 min
+ report_interval = timer:minutes(1),
+ use_sticky_locks = false,
+ spawn_near_branch = false,
+ activity_type = transaction,
+ reuse_history_id = false
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(time,
+ {
+ n_trans = 0,
+ min_n = 0,
+ max_n = 0,
+ acc_time = 0,
+ max_time = 0
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(driver_state,
+ {
+ driver_id,
+ driver_node,
+ seed,
+ n_local_branches,
+ local_branches,
+ tab_config,
+ run_config,
+ history_id,
+ time = #time{},
+ acc_time = #time{},
+ reuse_history_id
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(reporter_state,
+ {
+ driver_pids,
+ starter_pid,
+ n_iters = 0,
+ prev_tps = 0,
+ curr = #time{},
+ acc = #time{},
+ init_micros,
+ prev_micros,
+ run_config
+ }).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% One driver on each node, table not replicated
+
+config(frag_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {n_branches, length(Nodes)},
+ {n_fragments, length(Nodes)},
+ {replica_nodes, Nodes},
+ {db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% One driver on each node, table replicated to two nodes.
+
+config(frag2_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {n_branches, length(Nodes)},
+ {n_fragments, length(Nodes)},
+ {n_replicas, 2},
+ {replica_nodes, Nodes},
+ {db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% One driver on this node, table replicated to all nodes.
+
+config(replica_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {db_nodes, Nodes},
+ {driver_nodes, [Local]},
+ {replica_nodes, Nodes},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% One driver on this node, table replicated to all nodes.
+
+config(sticky_replica_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {db_nodes, Nodes},
+ {driver_nodes, [node()]},
+ {replica_nodes, Nodes},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {use_sticky_locks, true},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Ten drivers per node, tables replicated to all nodes, lots of branches
+
+config(dist_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 10},
+ {n_branches, 10 * length(Nodes) * 100},
+ {n_accounts_per_branch, 10},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Ten drivers per node, tables replicated to all nodes, single branch
+
+config(conflict_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {db_nodes, Nodes},
+ {driver_nodes, Nodes},
+ {replica_nodes, Nodes},
+ {n_drivers_per_node, 10},
+ {n_branches, 1},
+ {n_accounts_per_branch, 10},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% One driver on this node, table replicated to all other nodes.
+
+config(remote_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {db_nodes, Nodes},
+ {driver_nodes, [Local]},
+ {replica_nodes, Remote},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ];
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% One driver on this node, table replicated to two other nodes.
+
+config(remote_frag2_test, ReplicaType) ->
+ Remote = nodes(),
+ Local = node(),
+ Nodes = [Local | Remote],
+ [
+ {n_branches, length(Remote)},
+ {n_fragments, length(Remote)},
+ {n_replicas, 2},
+ {replica_nodes, Remote},
+ {db_nodes, Nodes},
+ {driver_nodes, [Local]},
+ {n_accounts_per_branch, 100},
+ {replica_type, ReplicaType},
+ {stop_after, timer:minutes(1)},
+ {report_interval, timer:seconds(10)},
+ {reuse_history_id, true}
+ ].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+start(What, ReplicaType) ->
+ spawn_link(?MODULE, start, [config(What, ReplicaType)]).
+
+replica_test(ReplicaType) ->
+ start(replica_test, ReplicaType).
+
+sticky_replica_test(ReplicaType) ->
+ start(sticky_replica_test, ReplicaType).
+
+dist_test(ReplicaType) ->
+ start(dist_test, ReplicaType).
+
+conflict_test(ReplicaType) ->
+ start(conflict_test, ReplicaType).
+
+remote_test(ReplicaType) ->
+ start(remote_test, ReplicaType).
+
+remote_frag2_test(ReplicaType) ->
+ start(remote_frag2_test, ReplicaType).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Args is a list of {Key, Val} tuples where Key is a field name
+%% in either the record tab_config or run_config. Unknown keys are ignored.
+
+start() ->
+ start([]).
+start(Args) ->
+ init(Args),
+ run(Args).
+
+list2rec(List, Fields, DefaultTuple) ->
+ [Name|Defaults] = tuple_to_list(DefaultTuple),
+ List2 = list2rec(List, Fields, Defaults, []),
+ list_to_tuple([Name] ++ List2).
+
+list2rec(_List, [], [], Acc) ->
+ Acc;
+list2rec(List, [F|Fields], [D|Defaults], Acc) ->
+ {Val, List2} =
+ case lists:keysearch(F, 1, List) of
+ false ->
+ {D, List};
+ {value, {F, NewVal}} ->
+ {NewVal, lists:keydelete(F, 1, List)}
+ end,
+ list2rec(List2, Fields, Defaults, Acc ++ [Val]).
+
+stop() ->
+ case whereis(mnesia_tpcb) of
+ undefined ->
+ {error, not_running};
+ Pid ->
+ sync_stop(Pid)
+ end.
+
+sync_stop(Pid) ->
+ Pid ! {self(), stop},
+ receive
+ {Pid, {stopped, Res}} -> Res
+ after timer:minutes(1) ->
+ exit(Pid, kill),
+ {error, brutal_kill}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Initialization
+
+%% Args is a list of {Key, Val} tuples where Key is a field name
+%% in the record tab_config, unknown keys are ignored.
+
+init(Args) ->
+ TabConfig0 = list2rec(Args, record_info(fields, tab_config), #tab_config{}),
+ TabConfig =
+ if
+ TabConfig0#tab_config.n_fragments =:= 0 ->
+ TabConfig0#tab_config{n_replicas = length(TabConfig0#tab_config.replica_nodes)};
+ true ->
+ TabConfig0
+ end,
+ Tags = record_info(fields, tab_config),
+ Fun = fun(F, Pos) -> {{F, element(Pos, TabConfig)}, Pos + 1} end,
+ {List, _} = lists:mapfoldl(Fun, 2, Tags),
+ io:format("TPC-B: Table config: ~p ~n", [List]),
+
+ DbNodes = TabConfig#tab_config.db_nodes,
+ stop(),
+ if
+ TabConfig#tab_config.use_running_mnesia =:= true ->
+ ignore;
+ true ->
+ rpc:multicall(DbNodes, mnesia, lkill, []),
+ case mnesia:delete_schema(DbNodes) of
+ ok ->
+ case mnesia:create_schema(DbNodes) of
+ ok ->
+ {Replies, BadNodes} =
+ rpc:multicall(DbNodes, mnesia, start, []),
+ case [Res || Res <- Replies, Res =/= ok] of
+ [] when BadNodes =:= [] ->
+ ok;
+ BadRes ->
+ io:format("TPC-B: <ERROR> "
+ "Failed to start ~p: ~p~n",
+ [BadNodes, BadRes]),
+ exit({start_failed, BadRes, BadNodes})
+ end;
+ {error, Reason} ->
+ io:format("TPC-B: <ERROR> "
+ "Failed to create schema on disc: ~p~n",
+ [Reason]),
+ exit({create_schema_failed, Reason})
+ end;
+ {error, Reason} ->
+ io:format("TPC-B: <ERROR> "
+ "Failed to delete schema on disc: ~p~n",
+ [Reason]),
+ exit({delete_schema_failed, Reason})
+ end
+ end,
+ gen_tabs(TabConfig).
+
+gen_tabs(TC) ->
+ create_tab(TC, branch, record_info(fields, branch),
+ undefined),
+ create_tab(TC, account, record_info(fields, account),
+ {branch, #account.branch_id}),
+ create_tab(TC, teller, record_info(fields, teller),
+ {branch, #teller.branch_id}),
+ create_tab(TC, history, record_info(fields, history),
+ {branch, #history.branch_id}),
+
+ NB = TC#tab_config.n_branches,
+ NT = TC#tab_config.n_tellers_per_branch,
+ NA = TC#tab_config.n_accounts_per_branch,
+ io:format("TPC-B: Generating ~p branches a ~p bytes~n",
+ [NB, size(term_to_binary(default_branch(TC)))]),
+ io:format("TPC-B: Generating ~p * ~p tellers a ~p bytes~n",
+ [NB, NT, size(term_to_binary(default_teller(TC)))]),
+ io:format("TPC-B: Generating ~p * ~p accounts a ~p bytes~n",
+ [NB, NA, size(term_to_binary(default_account(TC)))]),
+ io:format("TPC-B: Generating 0 history records a ~p bytes~n",
+ [size(term_to_binary(default_history(TC)))]),
+ gen_branches(TC),
+
+ case verify_tabs() of
+ ok ->
+ ignore;
+ {error, Reason} ->
+ io:format("TPC-B: <ERROR> Inconsistent tables: ~w~n",
+ [Reason]),
+ exit({inconsistent_tables, Reason})
+ end.
+
+create_tab(TC, Name, Attrs, _ForeignKey) when TC#tab_config.n_fragments =:= 0 ->
+ Nodes = TC#tab_config.replica_nodes,
+ Type = TC#tab_config.replica_type,
+ Def = [{Type, Nodes}, {attributes, Attrs}],
+ create_tab(Name, Def);
+create_tab(TC, Name, Attrs, ForeignKey) ->
+ NReplicas = TC#tab_config.n_replicas,
+ NodePool = TC#tab_config.replica_nodes,
+ Type = TC#tab_config.replica_type,
+ NF = TC#tab_config.n_fragments,
+ Props = [{n_fragments, NF},
+ {node_pool, NodePool},
+ {n_copies(Type), NReplicas},
+ {foreign_key, ForeignKey}],
+ Def = [{frag_properties, Props},
+ {attributes, Attrs}],
+ create_tab(Name, Def).
+
+create_tab(Name, Def) ->
+ mnesia:delete_table(Name),
+ case mnesia:create_table(Name, Def) of
+ {atomic, ok} ->
+ ok;
+ {aborted, Reason} ->
+ io:format("TPC-B: <ERROR> failed to create table ~w ~w: ~p~n",
+ [Name, Def, Reason]),
+ exit({create_table_failed, Reason})
+ end.
+
+n_copies(Type) ->
+ case Type of
+ ram_copies -> n_ram_copies;
+ disc_copies -> n_disc_copies;
+ disc_only_copies -> n_disc_only_copies
+ end.
+
+gen_branches(TC) ->
+ First = 0,
+ Last = First + TC#tab_config.n_branches - 1,
+ GenPids = gen_branches(TC, First, Last, []),
+ wait_for_gen(GenPids).
+
+wait_for_gen([]) ->
+ ok;
+wait_for_gen(Pids) ->
+ receive
+ {branch_generated, Pid} -> wait_for_gen(lists:delete(Pid, Pids));
+ Exit ->
+ exit({tpcb_failed, Exit})
+ end.
+
+gen_branches(TC, BranchId, Last, UsedNs) when BranchId =< Last ->
+ UsedNs2 = get_branch_nodes(BranchId, UsedNs),
+ Node = hd(UsedNs2),
+ Pid = spawn_link(Node, ?MODULE, reply_gen_branch,
+ [self(), TC, BranchId]),
+ [Pid | gen_branches(TC, BranchId + 1, Last, UsedNs2)];
+gen_branches(_, _, _, _) ->
+ [].
+
+reply_gen_branch(ReplyTo, TC, BranchId) ->
+ gen_branch(TC, BranchId),
+ ReplyTo ! {branch_generated, self()},
+ unlink(ReplyTo).
+
+%% Returns a new list of nodes with the best node as head
+get_branch_nodes(BranchId, UsedNs) ->
+ WriteNs = table_info({branch, BranchId}, where_to_write),
+ WeightedNs = [{n_duplicates(N, UsedNs, 0), N} || N <- WriteNs],
+ [{_, LeastUsed} | _ ] = lists:sort(WeightedNs),
+ [LeastUsed | UsedNs].
+
+n_duplicates(_N, [], Count) ->
+ Count;
+n_duplicates(N, [N | Tail], Count) ->
+ n_duplicates(N, Tail, Count + 1);
+n_duplicates(N, [_ | Tail], Count) ->
+ n_duplicates(N, Tail, Count).
+
+gen_branch(TC, BranchId) ->
+ A = default_account(TC),
+ NA = TC#tab_config.n_accounts_per_branch,
+ FirstA = BranchId * NA,
+ ArgsA = [FirstA, FirstA + NA - 1, BranchId, A],
+ ok = mnesia:activity(async_dirty, fun gen_accounts/4, ArgsA, mnesia_frag),
+
+ T = default_teller(TC),
+ NT = TC#tab_config.n_tellers_per_branch,
+ FirstT = BranchId * NT,
+ ArgsT = [FirstT, FirstT + NT - 1, BranchId, T],
+ ok = mnesia:activity(async_dirty, fun gen_tellers/4, ArgsT, mnesia_frag),
+
+ B = default_branch(TC),
+ FunB = fun() -> mnesia:write(branch, B#branch{id = BranchId}, write) end,
+ ok = mnesia:activity(sync_dirty, FunB, [], mnesia_frag).
+
+gen_tellers(Id, Last, BranchId, T) when Id =< Last ->
+ mnesia:write(teller, T#teller{id = Id, branch_id=BranchId}, write),
+ gen_tellers(Id + 1, Last, BranchId, T);
+gen_tellers(_, _, _, _) ->
+ ok.
+
+gen_accounts(Id, Last, BranchId, A) when Id =< Last ->
+ mnesia:write(account, A#account{id = Id, branch_id=BranchId}, write),
+ gen_accounts(Id + 1, Last, BranchId, A);
+gen_accounts(_, _, _, _) ->
+ ok.
+
+default_branch(TC) -> #branch{filler = TC#tab_config.branch_filler}.
+default_teller(TC) -> #teller{filler = TC#tab_config.teller_filler}.
+default_account(TC) -> #account{filler = TC#tab_config.account_filler}.
+default_history(_TC) -> #history{}.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% Run the benchmark
+
+%% Args is a list of {Key, Val} tuples where Key is a field name
+%% in the record run_config, unknown keys are ignored.
+run(Args) ->
+ RunConfig = list2rec(Args, record_info(fields, run_config), #run_config{}),
+ Tags = record_info(fields, run_config),
+ Fun = fun(F, Pos) -> {{F, element(Pos, RunConfig)}, Pos + 1} end,
+ {List, _} = lists:mapfoldl(Fun, 2, Tags),
+ io:format("TPC-B: Run config: ~p ~n", [List]),
+
+ Pid = spawn_link(?MODULE, reporter_init, [self(), RunConfig]),
+ receive
+ {Pid, {stopped, Res}} ->
+ Res; % Stopped by other process
+ Else ->
+ {tpcb_got, Else}
+ after RunConfig#run_config.stop_after ->
+ sync_stop(Pid)
+ end.
+
+reporter_init(Starter, RC) ->
+ register(mnesia_tpcb, self()),
+ process_flag(trap_exit, true),
+ DbNodes = mnesia:system_info(db_nodes),
+ if
+ RC#run_config.use_running_mnesia =:= true ->
+ ignore;
+ true ->
+ {Replies, BadNodes} =
+ rpc:multicall(DbNodes, mnesia, start, []),
+ case [Res || Res <- Replies, Res =/= ok] of
+ [] when BadNodes =:= [] ->
+ ok;
+ BadRes ->
+ io:format("TPC-B: <ERROR> "
+ "Failed to start ~w: ~p~n",
+ [BadNodes, BadRes]),
+ exit({start_failed, BadRes, BadNodes})
+ end,
+ verify_tabs()
+ end,
+
+ N = table_info(branch, size),
+ NT = table_info(teller, size) div N,
+ NA = table_info(account, size) div N,
+
+ {Type, NF, RepNodes} = table_storage(branch),
+ TC = #tab_config{n_fragments = NF,
+ n_branches = N,
+ n_tellers_per_branch = NT,
+ n_accounts_per_branch = NA,
+ db_nodes = DbNodes,
+ replica_nodes = RepNodes,
+ replica_type = Type
+ },
+ Drivers = start_drivers(RC, TC),
+ Now = now_to_micros(erlang:now()),
+ State = #reporter_state{driver_pids = Drivers,
+ run_config = RC,
+ starter_pid = Starter,
+ init_micros = Now,
+ prev_micros = Now
+ },
+ case catch reporter_loop(State) of
+ {'EXIT', Reason} ->
+ io:format("TPC-B: Abnormal termination: ~p~n", [Reason]),
+ if
+ RC#run_config.use_running_mnesia =:= true ->
+ ignore;
+ true ->
+ rpc:multicall(DbNodes, mnesia, lkill, [])
+ end,
+ unlink(Starter),
+ Starter ! {self(), {stopped, {error, Reason}}}, % To be sure
+ exit(shutdown);
+ {ok, Stopper, State2} ->
+ Time = State2#reporter_state.acc,
+ Res =
+ case verify_tabs() of
+ ok ->
+ {ok, Time};
+ {error, Reason} ->
+ io:format("TPC-B: <ERROR> Inconsistent tables, ~p~n",
+ [{error, Reason}]),
+ {error, Reason}
+ end,
+ if
+ RC#run_config.use_running_mnesia =:= true ->
+ ignore;
+ true ->
+ rpc:multicall(DbNodes, mnesia, stop, [])
+ end,
+ unlink(Starter),
+ Starter ! {self(), {stopped, Res}},
+ if
+ Stopper =/= Starter ->
+ Stopper ! {self(), {stopped, Res}};
+ true ->
+ ignore
+ end,
+ exit(shutdown)
+ end.
+
+table_info(Tab, Item) ->
+ Fun = fun() -> mnesia:table_info(Tab, Item) end,
+ mnesia:activity(sync_dirty, Fun, mnesia_frag).
+
+%% Returns {Storage, NFragments, ReplicaNodes}
+table_storage(Tab) ->
+ case mnesia:table_info(branch, frag_properties) of
+ [] ->
+ NFO = 0,
+ NR = length(mnesia:table_info(Tab, ram_copies)),
+ ND = length(mnesia:table_info(Tab, disc_copies)),
+ NDO = length(mnesia:table_info(Tab, disc_only_copies)),
+ if
+ NR =/= 0 -> {ram_copies, NFO, NR};
+ ND =/= 0 -> {disc_copies, NFO, ND};
+ NDO =/= 0 -> {disc_copies, NFO, NDO}
+ end;
+ Props ->
+ {value, NFO} = lists:keysearch(n_fragments, 1, Props),
+ NR = table_info(Tab, n_ram_copies),
+ ND = table_info(Tab, n_disc_copies),
+ NDO = table_info(Tab, n_disc_only_copies),
+ if
+ NR =/= 0 -> {ram_copies, NFO, NR};
+ ND =/= 0 -> {disc_copies, NFO, ND};
+ NDO =/= 0 -> {disc_copies, NFO, NDO}
+ end
+ end.
+
+reporter_loop(State) ->
+ RC = State#reporter_state.run_config,
+ receive
+ {From, stop} ->
+ {ok, From, call_drivers(State, stop)};
+ {'EXIT', Pid, Reason} when Pid =:= State#reporter_state.starter_pid ->
+ %% call_drivers(State, stop),
+ exit({starter_died, Pid, Reason})
+ after RC#run_config.report_interval ->
+ Iters = State#reporter_state.n_iters,
+ State2 = State#reporter_state{n_iters = Iters + 1},
+ case call_drivers(State2, report) of
+ State3 when State3#reporter_state.driver_pids =/= [] ->
+ State4 = State3#reporter_state{curr = #time{}},
+ reporter_loop(State4);
+ _ ->
+ exit(drivers_died)
+ end
+ end.
+
+call_drivers(State, Msg) ->
+ Drivers = State#reporter_state.driver_pids,
+ lists:foreach(fun(Pid) -> Pid ! {self(), Msg} end, Drivers),
+ State2 = show_report(calc_reports(Drivers, State)),
+ case Msg =:= stop of
+ true ->
+ Acc = State2#reporter_state.acc,
+ Init = State2#reporter_state.init_micros,
+ show_report(State2#reporter_state{n_iters = 0,
+ curr = Acc,
+ prev_micros = Init});
+ false ->
+ ignore
+ end,
+ State2.
+
+calc_reports([], State) ->
+ State;
+calc_reports([Pid|Drivers], State) ->
+ receive
+ {'EXIT', P, Reason} when P =:= State#reporter_state.starter_pid ->
+ exit({starter_died, P, Reason});
+ {'EXIT', Pid, Reason} ->
+ exit({driver_died, Pid, Reason});
+ {Pid, Time} when is_record(Time, time) ->
+ %% io:format("~w: ~w~n", [Pid, Time]),
+ A = add_time(State#reporter_state.acc, Time),
+ C = add_time(State#reporter_state.curr, Time),
+ State2 = State#reporter_state{acc = A, curr = C},
+ calc_reports(Drivers, State2)
+ end.
+
+add_time(Acc, New) ->
+ Acc#time{n_trans = New#time.n_trans + Acc#time.n_trans,
+ min_n = lists:min([New#time.n_trans, Acc#time.min_n] -- [0]),
+ max_n = lists:max([New#time.n_trans, Acc#time.max_n]),
+ acc_time = New#time.acc_time + Acc#time.acc_time,
+ max_time = lists:max([New#time.max_time, Acc#time.max_time])}.
+
+-define(AVOID_DIV_ZERO(_What_), try (_What_) catch _:_ -> 0 end).
+
+show_report(State) ->
+ Now = now_to_micros(erlang:now()),
+ Iters = State#reporter_state.n_iters,
+ Time = State#reporter_state.curr,
+ Max = Time#time.max_time,
+ N = Time#time.n_trans,
+ Avg = ?AVOID_DIV_ZERO(Time#time.acc_time div N),
+ AliveN = length(State#reporter_state.driver_pids),
+ Tps = ?AVOID_DIV_ZERO((?SECOND * AliveN) div Avg),
+ PrevTps= State#reporter_state.prev_tps,
+ {DiffSign, DiffTps} = signed_diff(Iters, Tps, PrevTps),
+ Unfairness = ?AVOID_DIV_ZERO(Time#time.max_n / Time#time.min_n),
+ BruttoAvg = ?AVOID_DIV_ZERO((Now - State#reporter_state.prev_micros) div N),
+%% io:format("n_iters=~p, n_trans=~p, n_drivers=~p, avg=~p, now=~p, prev=~p~n",
+%% [Iters, N, AliveN, BruttoAvg, Now, State#reporter_state.prev_micros]),
+ BruttoTps = ?AVOID_DIV_ZERO(?SECOND div BruttoAvg),
+ case Iters > 0 of
+ true ->
+ io:format("TPC-B: ~p iter ~s~p diff ~p (~p) tps ~p avg micros ~p max micros ~p unfairness~n",
+ [Iters, DiffSign, DiffTps, Tps, BruttoTps, Avg, Max, Unfairness]);
+ false ->
+ io:format("TPC-B: ~p (~p) transactions per second, "
+ "duration of longest transaction was ~p milliseconds~n",
+ [Tps, BruttoTps, Max div 1000])
+ end,
+ State#reporter_state{prev_tps = Tps, prev_micros = Now}.
+
+signed_diff(Iters, Curr, Prev) ->
+ case Iters > 1 of
+ true -> sign(Curr - Prev);
+ false -> sign(0)
+ end.
+
+sign(N) when N > 0 -> {"+", N};
+sign(N) -> {"", N}.
+
+now_to_micros({Mega, Secs, Micros}) ->
+ DT = calendar:now_to_datetime({Mega, Secs, 0}),
+ S = calendar:datetime_to_gregorian_seconds(DT),
+ (S * ?SECOND) + Micros.
+
+start_drivers(RC, TC) ->
+ LastHistoryId = table_info(history, size),
+ Reuse = RC#run_config.reuse_history_id,
+ DS = #driver_state{tab_config = TC,
+ run_config = RC,
+ n_local_branches = 0,
+ local_branches = [],
+ history_id = LastHistoryId,
+ reuse_history_id = Reuse},
+ Nodes = RC#run_config.driver_nodes,
+ NB = TC#tab_config.n_branches,
+ First = 0,
+ AllBranches = lists:seq(First, First + NB - 1),
+ ND = RC#run_config.n_drivers_per_node,
+ Spawn = fun(Spec) ->
+ Node = Spec#driver_state.driver_node,
+ spawn_link(Node, ?MODULE, driver_init, [Spec, AllBranches])
+ end,
+ Specs = [DS#driver_state{driver_id = Id, driver_node = N}
+ || N <- Nodes,
+ Id <- lists:seq(1, ND)],
+ Specs2 = lists:sort(lists:flatten(Specs)),
+ {Specs3, OrphanBranches} = alloc_local_branches(AllBranches, Specs2, []),
+ case length(OrphanBranches) of
+ N when N =< 10 ->
+ io:format("TPC-B: Orphan branches: ~p~n", [OrphanBranches]);
+ N ->
+ io:format("TPC-B: Orphan branches: ~p~n", [N])
+ end,
+ [Spawn(Spec) || Spec <- Specs3].
+
+alloc_local_branches([BranchId | Tail], Specs, OrphanBranches) ->
+ Nodes = table_info({branch, BranchId}, where_to_write),
+ LocalSpecs = [DS || DS <- Specs,
+ lists:member(DS#driver_state.driver_node, Nodes)],
+ case lists:keysort(#driver_state.n_local_branches, LocalSpecs) of
+ [] ->
+ alloc_local_branches(Tail, Specs, [BranchId | OrphanBranches]);
+ [DS | _] ->
+ LocalNB = DS#driver_state.n_local_branches + 1,
+ LocalBranches = [BranchId | DS#driver_state.local_branches],
+ DS2 = DS#driver_state{n_local_branches = LocalNB,
+ local_branches = LocalBranches},
+ Specs2 = Specs -- [DS],
+ Specs3 = [DS2 | Specs2],
+ alloc_local_branches(Tail, Specs3, OrphanBranches)
+ end;
+alloc_local_branches([], Specs, OrphanBranches) ->
+ {Specs, OrphanBranches}.
+
+driver_init(DS, AllBranches) ->
+ Seed = erlang:now(),
+ DS2 =
+ if
+ DS#driver_state.n_local_branches =:= 0 ->
+ DS#driver_state{seed = Seed,
+ n_local_branches = length(AllBranches),
+ local_branches = AllBranches};
+ true ->
+ DS#driver_state{seed = Seed}
+ end,
+ io:format("TPC-B: Driver ~p started as ~p on node ~p with ~p local branches~n",
+ [DS2#driver_state.driver_id, self(), node(), DS2#driver_state.n_local_branches]),
+ driver_loop(DS2).
+
+driver_loop(DS) ->
+ receive
+ {From, report} ->
+ From ! {self(), DS#driver_state.time},
+ Acc = add_time(DS#driver_state.time, DS#driver_state.acc_time),
+ DS2 = DS#driver_state{time=#time{}, acc_time = Acc}, % Reset timer
+ DS3 = calc_trans(DS2),
+ driver_loop(DS3);
+ {From, stop} ->
+ Acc = add_time(DS#driver_state.time, DS#driver_state.acc_time),
+ io:format("TPC-B: Driver ~p (~p) on node ~p stopped: ~w~n",
+ [DS#driver_state.driver_id, self(), node(self()), Acc]),
+ From ! {self(), DS#driver_state.time},
+ unlink(From),
+ exit(stopped)
+ after 0 ->
+ DS2 = calc_trans(DS),
+ driver_loop(DS2)
+ end.
+
+calc_trans(DS) ->
+ {Micros, DS2} = time_trans(DS),
+ Time = DS2#driver_state.time,
+ Time2 = Time#time{n_trans = Time#time.n_trans + 1,
+ acc_time = Time#time.acc_time + Micros,
+ max_time = lists:max([Micros, Time#time.max_time])
+ },
+ case DS#driver_state.reuse_history_id of
+ false ->
+ HistoryId = DS#driver_state.history_id + 1,
+ DS2#driver_state{time=Time2, history_id = HistoryId};
+ true ->
+ DS2#driver_state{time=Time2}
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Generate teller_id, account_id and delta
+%% Time the TPC-B transaction
+time_trans(DS) ->
+ OldSeed = get(random_seed), % Avoid interference with Mnesia
+ put(random_seed, DS#driver_state.seed),
+ Random = random:uniform(),
+ NewSeed = get(random_seed),
+ case OldSeed of
+ undefined -> erase(random_seed);
+ _ -> put(random_seed, OldSeed)
+ end,
+
+ TC = DS#driver_state.tab_config,
+ RC = DS#driver_state.run_config,
+ {Branchid, Args} = random_to_args(Random, DS),
+ {Fun, Mod} = trans_type(TC, RC),
+ {Time, Res} = timer:tc(?MODULE, real_trans, [RC, Branchid, Fun, Args, Mod]),
+
+ case Res of
+ AccountBal when is_integer(AccountBal) ->
+ {Time, DS#driver_state{seed = NewSeed}};
+ Other ->
+ exit({crash, Other, Args, Random, DS})
+ end.
+
+random_to_args(Random, DS) ->
+ DriverId = DS#driver_state.driver_id,
+ TC = DS#driver_state.tab_config,
+ HistoryId = DS#driver_state.history_id,
+ Delta = trunc(Random * 1999998) - 999999, % -999999 <= Delta <= +999999
+
+ Branches = DS#driver_state.local_branches,
+ NB = DS#driver_state.n_local_branches,
+ NT = TC#tab_config.n_tellers_per_branch,
+ NA = TC#tab_config.n_accounts_per_branch,
+ Tmp = trunc(Random * NB * NT),
+ BranchPos = (Tmp div NT) + 1,
+ BranchId =
+ case TC#tab_config.n_fragments of
+ 0 -> BranchPos - 1;
+ _ -> lists:nth(BranchPos, Branches)
+ end,
+ RelativeTellerId = Tmp div NT,
+ TellerId = (BranchId * NT) + RelativeTellerId,
+ {AccountBranchId, AccountId} =
+ if
+ Random >= 0.85, NB > 1 ->
+ %% Pick from a remote account
+ TmpAccountId= trunc(Random * (NB - 1) * NA),
+ TmpAccountBranchId = TmpAccountId div NA,
+ if
+ TmpAccountBranchId =:= BranchId ->
+ {TmpAccountBranchId + 1, TmpAccountId + NA};
+ true ->
+ {TmpAccountBranchId, TmpAccountId}
+ end;
+ true ->
+ %% Pick from a local account
+ RelativeAccountId = trunc(Random * NA),
+ TmpAccountId = (BranchId * NA) + RelativeAccountId,
+ {BranchId, TmpAccountId}
+ end,
+
+ {BranchId, [DriverId, BranchId, TellerId, AccountBranchId, AccountId, HistoryId, Delta]}.
+
+real_trans(RC, BranchId, Fun, Args, Mod) ->
+ Type = RC#run_config.activity_type,
+ case RC#run_config.spawn_near_branch of
+ false ->
+ mnesia:activity(Type, Fun, Args, Mod);
+ true ->
+ Node = table_info({branch, BranchId}, where_to_read),
+ case rpc:call(Node, mnesia, activity, [Type, Fun, Args, Mod]) of
+ {badrpc, Reason} -> exit(Reason);
+ Other -> Other
+ end
+ end.
+
+trans_type(TC, RC) ->
+ if
+ TC#tab_config.n_fragments =:= 0,
+ RC#run_config.use_sticky_locks =:= false ->
+ {fun add_delta/7, mnesia};
+ TC#tab_config.n_fragments =:= 0,
+ RC#run_config.use_sticky_locks =:= true ->
+ {fun sticky_add_delta/7, mnesia};
+ TC#tab_config.n_fragments > 0,
+ RC#run_config.use_sticky_locks =:= false ->
+ {fun frag_add_delta/7, mnesia_frag}
+ end.
+
+%%
+%% Runs the TPC-B defined transaction and returns NewAccountBalance
+%%
+
+add_delta(DriverId, BranchId, TellerId, _AccountBranchId, AccountId, HistoryId, Delta) ->
+ %% Grab write lock already when the record is read
+
+ %% Add delta to branch balance
+ [B] = mnesia:read(branch, BranchId, write),
+ NewB = B#branch{balance = B#branch.balance + Delta},
+ ok = mnesia:write(branch, NewB, write),
+
+ %% Add delta to teller balance
+ [T] = mnesia:read(teller, TellerId, write),
+ NewT = T#teller{balance = T#teller.balance + Delta},
+ ok = mnesia:write(teller, NewT, write),
+
+ %% Add delta to account balance
+ [A] = mnesia:read(account, AccountId, write),
+ NewA = A#account{balance = A#account.balance + Delta},
+ ok = mnesia:write(account, NewA, write),
+
+ %% Append to history log
+ History = #history{history_id = {DriverId, HistoryId},
+ account_id = AccountId,
+ teller_id = TellerId,
+ branch_id = BranchId,
+ amount = Delta
+ },
+ ok = mnesia:write(history, History, write),
+
+ %% Return account balance
+ NewA#account.balance.
+
+sticky_add_delta(DriverId, BranchId, TellerId, _AccountBranchId, AccountId, HistoryId, Delta) ->
+ %% Grab orinary read lock when the record is read
+ %% Grab sticky write lock when the record is written
+ %% This transaction would benefit of an early stick_write lock at read
+
+ %% Add delta to branch balance
+ [B] = mnesia:read(branch, BranchId, read),
+ NewB = B#branch{balance = B#branch.balance + Delta},
+ ok = mnesia:write(branch, NewB, sticky_write),
+
+ %% Add delta to teller balance
+ [T] = mnesia:read(teller, TellerId, read),
+ NewT = T#teller{balance = T#teller.balance + Delta},
+ ok = mnesia:write(teller, NewT, sticky_write),
+
+ %% Add delta to account balance
+ [A] = mnesia:read(account, AccountId, read),
+ NewA = A#account{balance = A#account.balance + Delta},
+ ok = mnesia:write(account, NewA, sticky_write),
+
+ %% Append to history log
+ History = #history{history_id = {DriverId, HistoryId},
+ account_id = AccountId,
+ teller_id = TellerId,
+ branch_id = BranchId,
+ amount = Delta
+ },
+ ok = mnesia:write(history, History, sticky_write),
+
+ %% Return account balance
+ NewA#account.balance.
+
+frag_add_delta(DriverId, BranchId, TellerId, AccountBranchId, AccountId, HistoryId, Delta) ->
+ %% Access fragmented table
+ %% Grab write lock already when the record is read
+
+ %% Add delta to branch balance
+ [B] = mnesia:read(branch, BranchId, write),
+ NewB = B#branch{balance = B#branch.balance + Delta},
+ ok = mnesia:write(NewB),
+
+ %% Add delta to teller balance
+ [T] = mnesia:read({teller, BranchId}, TellerId, write),
+ NewT = T#teller{balance = T#teller.balance + Delta},
+ ok = mnesia:write(NewT),
+
+ %% Add delta to account balance
+ %%io:format("frag_add_delta(~p): ~p\n", [node(), {account, BranchId, AccountId}]),
+ [A] = mnesia:read({account, AccountBranchId}, AccountId, write),
+ NewA = A#account{balance = A#account.balance + Delta},
+ ok = mnesia:write(NewA),
+
+ %% Append to history log
+ History = #history{history_id = {DriverId, HistoryId},
+ account_id = AccountId,
+ teller_id = TellerId,
+ branch_id = BranchId,
+ amount = Delta
+ },
+ ok = mnesia:write(History),
+
+ %% Return account balance
+ NewA#account.balance.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Verify table consistency
+
+verify_tabs() ->
+ Nodes = mnesia:system_info(running_db_nodes),
+ case lists:member(node(), Nodes) of
+ true ->
+ Tabs = [branch, teller, account, history],
+ io:format("TPC-B: Verifying tables: ~w~n", [Tabs]),
+ rpc:multicall(Nodes, mnesia, wait_for_tables, [Tabs, infinity]),
+
+ Fun = fun() ->
+ mnesia:write_lock_table(branch),
+ mnesia:write_lock_table(teller),
+ mnesia:write_lock_table(account),
+ mnesia:write_lock_table(history),
+ {Res, BadNodes} =
+ rpc:multicall(Nodes, ?MODULE, count_balance, []),
+ check_balance(Res, BadNodes)
+ end,
+ case mnesia:transaction(Fun) of
+ {atomic, Res} -> Res;
+ {aborted, Reason} -> {error, Reason}
+ end;
+ false ->
+ {error, "Must be initiated from a running db_node"}
+ end.
+
+%% Returns a list of {Table, Node, Balance} tuples
+%% Assumes that no updates are performed
+
+-record(summary, {table, node, balance, size}).
+
+count_balance() ->
+ [count_balance(branch, #branch.balance),
+ count_balance(teller, #teller.balance),
+ count_balance(account, #account.balance)].
+
+count_balance(Tab, BalPos) ->
+ Frags = table_info(Tab, frag_names),
+ count_balance(Tab, Frags, 0, 0, BalPos).
+
+count_balance(Tab, [Frag | Frags], Bal, Size, BalPos) ->
+ First = mnesia:dirty_first(Frag),
+ {Bal2, Size2} = count_frag_balance(Frag, First, Bal, Size, BalPos),
+ count_balance(Tab, Frags, Bal2, Size2, BalPos);
+count_balance(Tab, [], Bal, Size, _BalPos) ->
+ #summary{table = Tab, node = node(), balance = Bal, size = Size}.
+
+count_frag_balance(_Frag, '$end_of_table', Bal, Size, _BalPos) ->
+ {Bal, Size};
+count_frag_balance(Frag, Key, Bal, Size, BalPos) ->
+ [Record] = mnesia:dirty_read({Frag, Key}),
+ Bal2 = Bal + element(BalPos, Record),
+ Next = mnesia:dirty_next(Frag, Key),
+ count_frag_balance(Frag, Next, Bal2, Size + 1, BalPos).
+
+check_balance([], []) ->
+ mnesia:abort({"No balance"});
+check_balance(Summaries, []) ->
+ [One | Rest] = lists:flatten(Summaries),
+ Balance = One#summary.balance,
+ %% Size = One#summary.size,
+ case [S || S <- Rest, S#summary.balance =/= Balance] of
+ [] ->
+ ok;
+ BadSummaries ->
+ mnesia:abort({"Bad balance", One, BadSummaries})
+ end;
+check_balance(_, BadNodes) ->
+ mnesia:abort({"Bad nodes", BadNodes}).
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
new file mode 100644
index 0000000000..55ba4dd761
--- /dev/null
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -0,0 +1,1233 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(mnesia_trans_access_test).
+-author('[email protected]').
+-compile([export_all]).
+-include("mnesia_test_lib.hrl").
+
+init_per_testcase(Func, Conf) ->
+ mnesia_test_lib:init_per_testcase(Func, Conf).
+
+end_per_testcase(Func, Conf) ->
+ mnesia_test_lib:end_per_testcase(Func, Conf).
+
+-define(receive_messages(Msgs), mnesia_recovery_test:receive_messages(Msgs, ?FILE, ?LINE)).
+
+% First Some debug logging
+-define(dgb, true).
+-ifdef(dgb).
+-define(dl(X, Y), ?verbose("**TRACING: " ++ X ++ "**~n", Y)).
+-else.
+-define(dl(X, Y), ok).
+-endif.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+all() ->
+ [write, read, wread, delete, delete_object,
+ match_object, select, select14, all_keys, transaction,
+ {group, nested_activities}, {group, index_tabs},
+ {group, index_lifecycle}].
+
+groups() ->
+ [{nested_activities, [],
+ [basic_nested, {group, nested_transactions},
+ mix_of_nested_activities]},
+ {nested_transactions, [],
+ [nested_trans_both_ok, nested_trans_child_dies,
+ nested_trans_parent_dies, nested_trans_both_dies]},
+ {index_tabs, [],
+ [index_match_object, index_read, {group, index_update},
+ index_write]},
+ {index_update, [],
+ [index_update_set, index_update_bag]},
+ {index_lifecycle, [],
+ [add_table_index_ram, add_table_index_disc,
+ add_table_index_disc_only, create_live_table_index_ram,
+ create_live_table_index_disc,
+ create_live_table_index_disc_only, del_table_index_ram,
+ del_table_index_disc, del_table_index_disc_only,
+ {group, idx_schema_changes}]},
+ {idx_schema_changes, [],
+ [idx_schema_changes_ram, idx_schema_changes_disc,
+ idx_schema_changes_disc_only]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%% Write records
+
+write(suite) -> [];
+write(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = write,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:write([]) end)),
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 2}) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:write({foo, 2}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:write({Tab, 1, 2})),
+ ?verify_mnesia(Nodes, []).
+
+%% Read records
+
+read(suite) -> [];
+read(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = read,
+ Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ OneRec = {Tab, 1, 2},
+ TwoRec = {Tab, 1, 3},
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:read([]) end)),
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:read({Tab}) end)),
+ ?match({aborted, {bad_type, _}}
+ , mnesia:transaction(fun() -> mnesia:read(OneRec) end)),
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
+ ?match({atomic, [OneRec, TwoRec]},
+ mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:read({Tab, 1})),
+ ?verify_mnesia(Nodes, []).
+
+%% Read records and set write lock
+
+wread(suite) -> [];
+wread(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = wread,
+ Schema = [{name, Tab}, {type, set}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ OneRec = {Tab, 1, 2},
+ TwoRec = {Tab, 1, 3},
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:wread([]) end)),
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:wread({Tab}) end)),
+ ?match({aborted, {bad_type, _}}
+ , mnesia:transaction(fun() -> mnesia:wread(OneRec) end)),
+
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
+ ?match({atomic, [TwoRec]},
+ mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:wread({Tab, 1})),
+ ?verify_mnesia(Nodes, []).
+
+%% Delete record
+
+delete(suite) -> [];
+delete(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = delete,
+ Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:delete([]) end)),
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:delete({Tab}) end)),
+ ?match({aborted, {bad_type, _}}
+ , mnesia:transaction(fun() -> mnesia:delete({Tab, 1, 2}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:delete({Tab, 1})),
+ ?verify_mnesia(Nodes, []).
+
+%% Delete matching record
+
+delete_object(suite) -> [];
+delete_object(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = delete_object,
+ Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ OneRec = {Tab, 1, 2},
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:delete_object([]) end)),
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:delete_object({Tab}) end)),
+ ?match({aborted, {bad_type, _}},
+ mnesia:transaction(fun() -> mnesia:delete_object({Tab, 1}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:delete_object(OneRec)),
+
+ ?match({aborted, {bad_type, Tab, _}},
+ mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['_']}, 21}) end)),
+ ?match({aborted, {bad_type, Tab, _}},
+ mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['$5']}, 21}) end)),
+
+ ?verify_mnesia(Nodes, []).
+
+%% Read matching records
+
+match_object(suite) -> [];
+match_object(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = match,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ OneRec = {Tab, 1, 2},
+ OnePat = {Tab, '$1', 2},
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:match_object(OnePat) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:match_object(OnePat) end)),
+
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:match_object({foo, '$1', 2}) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:match_object({[], '$1', 2}) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:match_object(OnePat)),
+ ?verify_mnesia(Nodes, []).
+
+%% select
+select(suite) -> [];
+select(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = match,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ OneRec = {Tab, 1, 2},
+ TwoRec = {Tab, 2, 3},
+ OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:select(Tab, OnePat) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:select(Tab, OnePat) end)),
+
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:select(Tab, {match, '$1', 2}) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:select(Tab, [{'_', [], '$1'}]) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:select(Tab, OnePat)),
+ ?verify_mnesia(Nodes, []).
+
+
+%% more select
+select14(suite) -> [];
+select14(Config) when is_list(Config) ->
+ [Node1,Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab1 = select14_ets,
+ Tab2 = select14_dets,
+ Tab3 = select14_remote,
+ Tab4 = select14_remote_dets,
+ Schemas = [[{name, Tab1}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ [{name, Tab2}, {attributes, [k, v]}, {disc_only_copies, [Node1]}],
+ [{name, Tab3}, {attributes, [k, v]}, {ram_copies, [Node2]}],
+ [{name, Tab4}, {attributes, [k, v]}, {disc_only_copies, [Node2]}]],
+ [?match({atomic, ok}, mnesia:create_table(Schema)) || Schema <- Schemas],
+
+ %% Some Helpers
+ Trans = fun(Fun) -> mnesia:transaction(Fun) end,
+ LoopHelp = fun('$end_of_table',_) -> [];
+ ({Recs,Cont},Fun) ->
+ Sel = mnesia:select(Cont),
+ Recs ++ Fun(Sel, Fun)
+ end,
+ Loop = fun(Table,Pattern) ->
+ Sel = mnesia:select(Table, Pattern, 1, read),
+ Res = LoopHelp(Sel,LoopHelp),
+ case mnesia:table_info(Table, type) of
+ ordered_set -> Res;
+ _ -> lists:sort(Res)
+ end
+ end,
+ Test =
+ fun(Tab) ->
+ OneRec = {Tab, 1, 2},
+ TwoRec = {Tab, 2, 3},
+ OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
+ All = [OneRec,TwoRec],
+ AllPat = [{'_', [], ['$_']}],
+
+ ?match({atomic, []}, Trans(fun() -> Loop(Tab, OnePat) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
+ ?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)),
+ ?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)),
+
+ {atomic,{_, Cont}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end),
+ ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(Cont) end)),
+
+ ?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, {match, '$1', 2},1,read) end)),
+ ?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, [{'_', [], '$1'}],1,read) end)),
+ ?match({aborted, _}, Trans(fun() -> mnesia:select(sune) end)),
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:select(Tab, OnePat,1,read)),
+ ?match({aborted, {badarg,sune}},
+ Trans(fun() -> mnesia:select(sune) end))
+ end,
+ Test(Tab1),
+ Test(Tab2),
+ Test(Tab3),
+ Test(Tab4),
+ ?verify_mnesia(Nodes, []).
+
+
+%% Pick all keys from table
+
+all_keys(suite) ->[];
+all_keys(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = all_keys,
+ Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ Write = fun() -> mnesia:write({Tab, 14, 4}) end,
+ AllKeys = fun() -> mnesia:all_keys(Tab) end,
+
+ ?match({atomic, []}, mnesia:transaction(AllKeys)),
+
+ ?match({atomic, ok}, mnesia:transaction(Write)),
+ ?match({atomic, [14]}, mnesia:transaction(AllKeys)),
+
+ ?match({atomic, ok}, mnesia:transaction(Write)),
+ ?match({atomic, [14]}, mnesia:transaction(AllKeys)),
+
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:all_keys(foo) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:all_keys([]) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:all_keys(Tab)),
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Use and misuse transactions
+
+transaction(suite) -> [];
+transaction(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ ?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end)),
+ ?match({aborted, _}, mnesia:transaction(no_fun)),
+ ?match({aborted, _}, mnesia:transaction(?MODULE, no_fun, [foo])),
+
+ {success, [A, B, C, D, E, F, G, H]} =
+ ?start_activities(lists:duplicate(8, Node1)),
+ ?start_transactions([A, B, C, D, E, F, G, H]),
+
+ A ! fun() -> mnesia:abort(abort_bad_trans) end,
+ ?match_receive({A, {aborted, abort_bad_trans}}),
+
+ B ! fun() -> erlang:error(exit_here) end,
+ ?match_receive({B, {aborted, _}}),
+
+ C ! fun() -> throw(throw_bad_trans) end,
+ ?match_receive({C, {aborted, {throw, throw_bad_trans}}}),
+
+ D ! fun() -> exit(exit_bad_trans) end,
+ ?match_receive({D, {aborted, exit_bad_trans}}),
+
+ E ! fun() -> exit(normal) end,
+ ?match_receive({E, {aborted, normal}}),
+
+ F ! fun() -> exit(abnormal) end,
+ ?match_receive({F, {aborted, abnormal}}),
+
+ G ! fun() -> exit(G, abnormal) end,
+ ?match_receive({'EXIT', G, abnormal}),
+
+ H ! fun() -> exit(H, kill) end,
+ ?match_receive({'EXIT', H, killed}),
+
+ ?match({atomic, ali_baba},
+ mnesia:transaction(fun() -> ali_baba end, infinity)),
+ ?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end, 1)),
+ ?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end, 0)),
+ ?match({aborted, Reason8} when element(1, Reason8) == badarg, mnesia:transaction(fun() -> ali_baba end, -1)),
+ ?match({aborted, Reason1} when element(1, Reason1) == badarg, mnesia:transaction(fun() -> ali_baba end, foo)),
+ Fun = fun() ->
+ ?match(true, mnesia:is_transaction()),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> ?match(true, mnesia:is_transaction()),ok end)), ok end,
+ ?match({atomic, ok}, mnesia:transaction(Fun)),
+ ?verify_mnesia(Nodes, []).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% ensure that nested transactions behave correctly
+%% We create a particular table that is used by this test only
+-record(ntab, {a, b}).
+basic_nested(doc) -> ["Test the basic functionality of nested transactions"];
+basic_nested(suite) -> [];
+basic_nested(Config) when is_list(Config) ->
+ Nodes = ?acquire_nodes(3, Config),
+ Args = [{ram_copies, Nodes},
+ {attributes, record_info(fields, ntab)}],
+ ?match({atomic, ok}, mnesia:create_table(ntab, Args)),
+ do_nested(top),
+ case mnesia_test_lib:diskless(Config) of
+ false ->
+ lists:foreach(fun(N) ->
+ ?match({atomic, ok},
+ mnesia:change_table_copy_type(ntab, N, disc_only_copies))
+ end, Nodes),
+ do_nested(top);
+ true ->
+ skip
+ end,
+ ?verify_mnesia(Nodes, []).
+
+do_nested(How) ->
+ F1 = fun() ->
+ mnesia:write(#ntab{a= 1}),
+ mnesia:write(#ntab{a= 2})
+ end,
+ F2 = fun() ->
+ mnesia:read({ntab, 1})
+ end,
+ ?match({atomic, ok}, mnesia:transaction(F1)),
+ ?match({atomic, _}, mnesia:transaction(F2)),
+
+ ?match({atomic, {aborted, _}},
+ mnesia:transaction(fun() -> n_f1(),
+ mnesia:transaction(fun() -> n_f2() end)
+ end)),
+
+ ?match({atomic, {aborted, _}},
+ mnesia:transaction(fun() -> n_f1(),
+ mnesia:transaction(fun() -> n_f3() end)
+ end)),
+ ?match({atomic, {atomic, [#ntab{a = 5}]}},
+ mnesia:transaction(fun() -> mnesia:write(#ntab{a = 5}),
+ mnesia:transaction(fun() -> n_f4() end)
+ end)),
+ Cyclic = fun() -> mnesia:abort({cyclic,a,a,a,a,a}) end, %% Ugly
+ NodeNotR = fun() -> mnesia:abort({node_not_running, testNode}) end,
+
+ TestAbort = fun(Fun) ->
+ case get(restart_counter) of
+ undefined ->
+ put(restart_counter, 1),
+ Fun();
+ _ ->
+ erase(restart_counter),
+ ok
+ end
+ end,
+
+ ?match({atomic,{atomic,ok}},
+ mnesia:transaction(fun()->mnesia:transaction(TestAbort,
+ [Cyclic])end)),
+
+ ?match({atomic,{atomic,ok}},
+ mnesia:transaction(fun()->mnesia:transaction(TestAbort,
+ [NodeNotR])end)),
+
+ %% Now try the restart thingie
+ case How of
+ top ->
+ Pids = [spawn(?MODULE, do_nested, [{spawned, self()}]),
+ spawn(?MODULE, do_nested, [{spawned, self()}]),
+ spawn(?MODULE, do_nested, [{spawned, self()}]),
+ spawn(?MODULE, do_nested, [{spawned, self()}])],
+ ?match({info, _, _}, mnesia_tm:get_info(2000)),
+ lists:foreach(fun(P) -> receive
+ {P, ok} -> ok
+ end
+ end, Pids),
+ ?match([], [Tab || Tab <- ets:all(), mnesia_trans_store == ets:info(Tab, name)]);
+
+ {spawned, Pid} ->
+ ?match({info, _, _}, mnesia_tm:get_info(2000)),
+ Pid ! {self(), ok},
+ exit(normal)
+ end.
+
+
+n_f1() ->
+ mnesia:read({ntab, 1}),
+ mnesia:write(#ntab{a = 3}).
+
+n_f2() ->
+ mnesia:write(#ntab{a = 4}),
+ erlang:error(exit_here).
+
+n_f3() ->
+ mnesia:write(#ntab{a = 4}),
+ throw(funky).
+
+n_f4() ->
+ mnesia:read({ntab, 5}).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+nested_trans_both_ok(suite) -> [];
+nested_trans_both_ok(Config) when is_list(Config) ->
+ nested_transactions(Config, ok, ok).
+
+nested_trans_child_dies(suite) -> [];
+nested_trans_child_dies(Config) when is_list(Config) ->
+ nested_transactions(Config, abort, ok).
+
+nested_trans_parent_dies(suite) -> [];
+nested_trans_parent_dies(Config) when is_list(Config) ->
+ nested_transactions(Config, ok, abort).
+
+nested_trans_both_dies(suite) -> [];
+nested_trans_both_dies(Config) when is_list(Config) ->
+ nested_transactions(Config, abort, abort).
+
+nested_transactions(Config, Child, Father) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab = nested_trans,
+
+ Def =
+ case mnesia_test_lib:diskless(Config) of
+ true ->
+ [{name, Tab}, {ram_copies, Nodes}];
+ false ->
+ [{name, Tab}, {ram_copies, [Node1]},
+ {disc_copies, [Node2]}, {disc_only_copies, [Node3]}]
+ end,
+
+ ?match({atomic, ok}, mnesia:create_table(Def)),
+ ?match(ok, mnesia:dirty_write({Tab, father, not_updated})),
+ ?match(ok, mnesia:dirty_write({Tab, child, not_updated})),
+
+ ChildOk = fun() -> mnesia:write({Tab, child, updated}) end,
+ ChildAbort = fun() ->
+ mnesia:write({Tab, child, updated}),
+ erlang:error(exit_here)
+ end,
+
+ Child_Fun = % Depending of test case
+ case Child of
+ ok -> ChildOk;
+ abort -> ChildAbort
+ end,
+
+ FatherOk = fun() -> mnesia:transaction(Child_Fun),
+ mnesia:write({Tab, father, updated})
+ end,
+
+ FatherAbort = fun() -> mnesia:transaction(Child_Fun),
+ mnesia:write({Tab, father, updated}),
+ erlang:error(exit_here)
+ end,
+
+ {FatherRes, ChildRes} = % Depending of test case
+ case Father of
+ ok -> ?match({atomic, ok}, mnesia:transaction(FatherOk)),
+ case Child of
+ ok -> {[{Tab, father, updated}], [{Tab, child, updated}]};
+ _ -> {[{Tab, father, updated}], [{Tab, child, not_updated}]}
+ end;
+ abort -> ?match({aborted, _}, mnesia:transaction(FatherAbort)),
+ {[{Tab, father, not_updated}], [{Tab, child, not_updated}]}
+ end,
+
+ %% Syncronize things!!
+ ?match({atomic, ok}, mnesia:sync_transaction(fun() -> mnesia:write({Tab, sync, sync}) end)),
+
+ ?match(ChildRes, rpc:call(Node1, mnesia, dirty_read, [{Tab, child}])),
+ ?match(ChildRes, rpc:call(Node2, mnesia, dirty_read, [{Tab, child}])),
+ ?match(ChildRes, rpc:call(Node3, mnesia, dirty_read, [{Tab, child}])),
+
+ ?match(FatherRes, rpc:call(Node1, mnesia, dirty_read, [{Tab, father}])),
+ ?match(FatherRes, rpc:call(Node2, mnesia, dirty_read, [{Tab, father}])),
+ ?match(FatherRes, rpc:call(Node3, mnesia, dirty_read, [{Tab, father}])),
+ ?verify_mnesia(Nodes, []).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+mix_of_nested_activities(doc) ->
+ ["Verify that dirty operations in a transaction are handled like ",
+ "normal transactions"];
+mix_of_nested_activities(suite) -> [];
+mix_of_nested_activities(Config) when is_list(Config) ->
+ [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
+ Tab = tab,
+
+ Def =
+ case mnesia_test_lib:diskless(Config) of
+ true -> [{ram_copies, Nodes}];
+ false ->
+ [{ram_copies, [Node1]},
+ {disc_copies, [Node2]},
+ {disc_only_copies, [Node3]}]
+ end,
+
+ ?match({atomic, ok}, mnesia:create_table(Tab, [{type,bag}|Def])),
+ Activities = [transaction, sync_transaction,
+ ets, async_dirty, sync_dirty],
+ %% Make a test for all 3000 combinations
+ Tests = [[A,B,C,D,E] ||
+ A <- Activities,
+ B <- Activities,
+ C <- Activities,
+ D <- Activities,
+ E <- Activities],
+ Foreach =
+ fun(Test,No) ->
+ Result = lists:reverse(Test),
+ ?match({No,Result},{No,catch apply_op({Tab,No},Test)}),
+ No+1
+ end,
+ lists:foldl(Foreach, 0, Tests),
+ ?verify_mnesia(Nodes, []).
+
+apply_op(Oid,[Type]) ->
+ check_res(Type,mnesia:Type(fun() -> [Type|read_op(Oid)] end));
+apply_op(Oid = {Tab,Key},[Type|Next]) ->
+ check_res(Type,mnesia:Type(fun() ->
+ Prev = read_op(Oid),
+ mnesia:write({Tab,Key,[Type|Prev]}),
+ apply_op(Oid,Next)
+ end)).
+
+check_res(transaction, {atomic,Res}) ->
+ Res;
+check_res(sync_transaction, {atomic,Res}) ->
+ Res;
+check_res(async_dirty, Res) when is_list(Res) ->
+ Res;
+check_res(sync_dirty, Res) when is_list(Res) ->
+ Res;
+check_res(ets, Res) when is_list(Res) ->
+ Res;
+check_res(Type,Res) ->
+ ?match(bug,{Type,Res}).
+
+read_op(Oid) ->
+ case lists:reverse(mnesia:read(Oid)) of
+ [] -> [];
+ [{_,_,Ops}|_] ->
+ Ops
+ end.
+
+
+%% Read matching records by using an index
+
+index_match_object(suite) -> [];
+index_match_object(Config) when is_list(Config) ->
+ [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = index_match_object,
+ Schema = [{name, Tab}, {attributes, [k, v, e]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = 3,
+ BadValPos = ValPos + 2,
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:index_match_object({Tab, '$1', 2}, ValPos) end)),
+ OneRec = {Tab, {1, 1}, 2, {1, 1}},
+ OnePat = {Tab, '$1', 2, '_'},
+ BadPat = {Tab, '$1', '$2', '_'}, %% See ref guide
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+
+ Imatch = fun(Patt, Pos) ->
+ mnesia:transaction(fun() -> lists:sort(mnesia:index_match_object(Patt, Pos)) end)
+ end,
+ ?match({atomic, [OneRec]}, Imatch(OnePat, ValPos)),
+ ?match({aborted, _}, Imatch(OnePat, BadValPos)),
+ ?match({aborted, _}, Imatch({foo, '$1', 2, '_'}, ValPos)),
+ ?match({aborted, _}, Imatch({[], '$1', 2, '_'}, ValPos)),
+ ?match({aborted, _}, Imatch(BadPat, ValPos)),
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:index_match_object(OnePat, ValPos)),
+
+ Another = {Tab, {3,1}, 2, {4,4}},
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Another) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, {4, 4}, 3, {4, 4}}) end)),
+
+ ?match({atomic, [OneRec]}, Imatch({Tab, {1,1}, 2, {1,1}}, ValPos)),
+ ?match({atomic, [OneRec]}, Imatch({Tab, {1,1}, 2, '$1'}, ValPos)),
+ ?match({atomic, [OneRec]}, Imatch({Tab, '$1', 2, {1,1}}, ValPos)),
+ ?match({atomic, [OneRec]}, Imatch({Tab, '$1', 2, '$1'}, ValPos)),
+ ?match({atomic, [OneRec]}, Imatch({Tab, {1, '$1'}, 2, '_'}, ValPos)),
+ ?match({atomic, [OneRec]}, Imatch({Tab, {'$2', '$1'}, 2, {'_', '$1'}}, ValPos)),
+ ?match({atomic, [OneRec, Another]}, Imatch({Tab, '_', 2, '_'}, ValPos)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 4, 5, {7, 4}}) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write({Tab, 7, 5, {7, 5}}) end)),
+
+ ?match({atomic, [{Tab, 4, 5, {7, 4}}]}, Imatch({Tab, '$1', 5, {'_', '$1'}}, ValPos)),
+
+ ?match({atomic, [OneRec]}, rpc:call(Node2, mnesia, transaction,
+ [fun() ->
+ lists:sort(mnesia:index_match_object({Tab, {1,1}, 2,
+ {1,1}}, ValPos))
+ end])),
+ ?verify_mnesia(Nodes, []).
+
+%% Read records by using an index
+
+index_read(suite) -> [];
+index_read(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = index_read,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = 3,
+ BadValPos = ValPos + 1,
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ OneRec = {Tab, 1, 2},
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, BadValPos) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:index_read(foo, 2, ValPos) end)),
+ ?match({aborted, _},
+ mnesia:transaction(fun() -> mnesia:index_read([], 2, ValPos) end)),
+
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:index_read(Tab, 2, ValPos)),
+ ?verify_mnesia(Nodes, []).
+
+index_update_set(suite) -> [];
+index_update_set(Config)when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = index_test,
+ Schema = [{name, Tab}, {attributes, [k, v1, v2, v3]}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = v1,
+ ValPos2 = v3,
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Pat1 = {Tab, '$1', 2, '$2', '$3'},
+ Pat2 = {Tab, '$1', '$2', '$3', '$4'},
+
+ Rec1 = {Tab, 1, 2, 3, 4},
+ Rec2 = {Tab, 2, 2, 13, 14},
+ Rec3 = {Tab, 1, 12, 13, 14},
+ Rec4 = {Tab, 4, 2, 13, 14},
+
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ ?match({atomic, [Rec1]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
+ {atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2], lists:sort(R1)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
+ {atomic, R2} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec2], lists:sort(R2)),
+ ?match({atomic, [Rec2]},
+ mnesia:transaction(fun() -> mnesia:index_match_object(Pat1, ValPos) end)),
+
+ {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
+ ?match([Rec3, Rec2], lists:sort(R3)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ {atomic, R4} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec2, Rec4], lists:sort(R4)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
+ ?match({atomic, [Rec2]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+
+ ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
+
+ {atomic, R5} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
+ ?match([Rec3, Rec2, Rec4], lists:sort(R5)),
+
+ {atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec2, Rec4], lists:sort(R6)),
+
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
+ {atomic, R7} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec3, Rec2, Rec4], lists:sort(R7)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ {atomic, R8} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
+ ?match({atomic, [Rec1]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
+ {atomic, R9} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec2, Rec4], lists:sort(R9)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
+ {atomic, R10} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec4], lists:sort(R10)),
+ ?match({atomic, [Rec1]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
+ ?match({atomic, [Rec4]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
+ {atomic, R11} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1], lists:sort(R11)),
+ ?match({atomic, [Rec1]},mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
+ ?match({atomic, []},mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end)),
+
+ ?verify_mnesia(Nodes, []).
+
+index_update_bag(suite) -> [];
+index_update_bag(Config)when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = index_test,
+ Schema = [{name, Tab},
+ {type, bag},
+ {attributes, [k, v1, v2, v3]},
+ {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = v1,
+ ValPos2 = v3,
+
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+
+ Pat1 = {Tab, '$1', 2, '$2', '$3'},
+ Pat2 = {Tab, '$1', '$2', '$3', '$4'},
+
+ Rec1 = {Tab, 1, 2, 3, 4},
+ Rec2 = {Tab, 2, 2, 13, 14},
+ Rec3 = {Tab, 1, 12, 13, 14},
+ Rec4 = {Tab, 4, 2, 13, 4},
+ Rec5 = {Tab, 1, 2, 234, 14},
+
+ %% Simple Index
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ ?match({atomic, [Rec1]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
+ {atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2], lists:sort(R1)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
+ {atomic, R2} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2], lists:sort(R2)),
+
+ {atomic, R3} = mnesia:transaction(fun() -> mnesia:index_match_object(Pat1, ValPos) end),
+ ?match([Rec1, Rec2], lists:sort(R3)),
+
+ {atomic, R4} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
+ ?match([Rec1, Rec3, Rec2], lists:sort(R4)),
+
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ {atomic, R5} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2, Rec4], lists:sort(R5)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
+ {atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2], lists:sort(R6)),
+
+ %% OTP-6587 Needs some whitebox testing to see that the index table is cleaned correctly
+
+ [IPos] = mnesia_lib:val({Tab,index}),
+ ITab = mnesia_lib:val({index_test,{index, IPos}}),
+ io:format("~n Index ~p @ ~p => ~p ~n~n",[IPos,ITab, ets:tab2list(ITab)]),
+ ?match([{2,1},{2,2},{12,1}], ets:tab2list(ITab)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
+ {atomic, R60} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1,Rec5,Rec2], lists:sort(R60)),
+
+ ?match([{2,1},{2,2},{12,1}], ets:tab2list(ITab)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec3) end)),
+ {atomic, R61} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1,Rec5,Rec2], lists:sort(R61)),
+ {atomic, R62} = mnesia:transaction(fun() -> mnesia:index_read(Tab,12, ValPos) end),
+ ?match([], lists:sort(R62)),
+ ?match([{2,1},{2,2}], ets:tab2list(ITab)),
+
+ %% reset for rest of testcase
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
+ {atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2], lists:sort(R6)),
+ %% OTP-6587
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
+ ?match({atomic, [Rec2]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
+ {atomic, R7} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
+ ?match([Rec3, Rec2], lists:sort(R7)),
+
+ %% Two indexies
+ ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
+
+ {atomic, R8} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
+
+ {atomic, R9} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end),
+ ?match([Rec1, Rec4], lists:sort(R9)),
+ {atomic, R10} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec3, Rec2], lists:sort(R10)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
+ {atomic, R11} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec1, Rec5, Rec2, Rec4], lists:sort(R11)),
+ {atomic, R12} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end),
+ ?match([Rec1, Rec4], lists:sort(R12)),
+ {atomic, R13} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec5, Rec3, Rec2], lists:sort(R13)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
+ {atomic, R14} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec5, Rec2, Rec4], lists:sort(R14)),
+ ?match({atomic, [Rec4]},
+ mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
+ {atomic, R15} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec5, Rec3, Rec2], lists:sort(R15)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
+ {atomic, R16} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec2, Rec4], lists:sort(R16)),
+ ?match({atomic, [Rec4]}, mnesia:transaction(fun()->mnesia:index_read(Tab, 4, ValPos2) end)),
+ {atomic, R17} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec3, Rec2], lists:sort(R17)),
+
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
+ {atomic, R18} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
+ ?match([Rec2, Rec4], lists:sort(R18)),
+ ?match({atomic, [Rec4]}, mnesia:transaction(fun()->mnesia:index_read(Tab, 4, ValPos2) end)),
+ {atomic, R19} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
+ ?match([Rec2], lists:sort(R19)),
+
+ ?verify_mnesia(Nodes, []).
+
+
+index_write(suite) -> [];
+index_write(doc) -> ["See ticket OTP-8072"];
+index_write(Config)when is_list(Config) ->
+ Nodes = ?acquire_nodes(1, Config),
+ mnesia:create_table(a, [{index, [val]}]),
+ mnesia:create_table(counter, []),
+
+ CreateIfNonExist =
+ fun(Index) ->
+ case mnesia:index_read(a, Index, 3) of
+ [] ->
+ Id = mnesia:dirty_update_counter(counter, id, 1),
+ New = {a, Id, Index},
+ mnesia:write(New),
+ New;
+ [Found] ->
+ Found
+ end
+ end,
+
+ Trans = fun(A) ->
+ mnesia:transaction(CreateIfNonExist, [A])
+ %% This works better most of the time
+ %% And it is allowed to fail since it's dirty
+ %% mnesia:async_dirty(CreateIfNonExist, [A])
+ end,
+
+ Self = self(),
+ Update = fun() ->
+ Res = lists:map(Trans, lists:seq(1,10)),
+ Self ! {self(), Res}
+ end,
+
+ Pids = [spawn(Update) || _ <- lists:seq(1,5)],
+
+ Gather = fun(Pid, Acc) -> receive {Pid, Res} -> [Res|Acc] end end,
+ Results = lists:foldl(Gather, [], Pids),
+ Expected = hd(Results),
+ Check = fun(Res) -> ?match(Expected, Res) end,
+ lists:foreach(Check, Results),
+ ?verify_mnesia(Nodes, []).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Add and drop indecies
+
+
+add_table_index_ram(suite) -> [];
+add_table_index_ram(Config) when is_list(Config) ->
+ add_table_index(Config, ram_copies).
+
+add_table_index_disc(suite) -> [];
+add_table_index_disc(Config) when is_list(Config) ->
+ add_table_index(Config, disc_copies).
+
+add_table_index_disc_only(suite) -> [];
+add_table_index_disc_only(Config) when is_list(Config) ->
+ add_table_index(Config, disc_only_copies).
+
+%% Add table index
+
+add_table_index(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = add_table_index,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = 3,
+ BadValPos = ValPos + 1,
+ ?match({aborted, Reason41 } when element(1, Reason41) == bad_type,
+ mnesia:add_table_index(Tab, BadValPos)),
+ ?match({aborted,Reason42 } when element(1, Reason42) == bad_type,
+ mnesia:add_table_index(Tab, 2)),
+ ?match({aborted, Reason43 } when element(1, Reason43) == bad_type,
+ mnesia:add_table_index(Tab, 1)),
+ ?match({aborted, Reason44 } when element(1, Reason44) == bad_type,
+ mnesia:add_table_index(Tab, 0)),
+ ?match({aborted, Reason45 } when element(1, Reason45) == bad_type,
+ mnesia:add_table_index(Tab, -1)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({aborted, Reason46 } when element(1, Reason46) == already_exists,
+ mnesia:add_table_index(Tab, ValPos)),
+
+ NestedFun = fun() ->
+ ?match({aborted, nested_transaction},
+ mnesia:add_table_index(Tab, ValPos)),
+ ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(NestedFun)),
+ ?verify_mnesia(Nodes, []).
+
+create_live_table_index_ram(suite) -> [];
+create_live_table_index_ram(Config) when is_list(Config) ->
+ create_live_table_index(Config, ram_copies).
+
+create_live_table_index_disc(suite) -> [];
+create_live_table_index_disc(Config) when is_list(Config) ->
+ create_live_table_index(Config, disc_copies).
+
+create_live_table_index_disc_only(suite) -> [];
+create_live_table_index_disc_only(Config) when is_list(Config) ->
+ create_live_table_index(Config, disc_only_copies).
+
+create_live_table_index(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = create_live_table_index,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = 3,
+ mnesia:dirty_write({Tab, 1, 2}),
+
+ Fun = fun() ->
+ ?match(ok, mnesia:write({Tab, 2, 2})),
+ ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(Fun)),
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]},
+ mnesia:transaction(fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos))
+ end)),
+ ?verify_mnesia(Nodes, []).
+
+%% Drop table index
+
+del_table_index_ram(suite) ->[];
+del_table_index_ram(Config) when is_list(Config) ->
+ del_table_index(Config, ram_copies).
+
+del_table_index_disc(suite) ->[];
+del_table_index_disc(Config) when is_list(Config) ->
+ del_table_index(Config, disc_copies).
+
+del_table_index_disc_only(suite) ->[];
+del_table_index_disc_only(Config) when is_list(Config) ->
+ del_table_index(Config, disc_only_copies).
+
+del_table_index(Config, Storage) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab = del_table_index,
+ Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+ ValPos = 3,
+ BadValPos = ValPos + 1,
+ ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
+ ?match({aborted,Reason} when element(1, Reason) == no_exists,
+ mnesia:del_table_index(Tab, BadValPos)),
+ ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
+
+ ?match({aborted,Reason1} when element(1, Reason1) == no_exists,
+ mnesia:del_table_index(Tab, ValPos)),
+ NestedFun =
+ fun() ->
+ ?match({aborted, nested_transaction},
+ mnesia:del_table_index(Tab, ValPos)),
+ ok
+ end,
+ ?match({atomic, ok}, mnesia:transaction(NestedFun)),
+ ?verify_mnesia(Nodes, []).
+
+
+idx_schema_changes_ram(suite) -> [];
+idx_schema_changes_ram(Config) when is_list(Config) ->
+ idx_schema_changes(Config, ram_copies).
+idx_schema_changes_disc(suite) -> [];
+idx_schema_changes_disc(Config) when is_list(Config) ->
+ idx_schema_changes(Config, disc_copies).
+idx_schema_changes_disc_only(suite) -> [];
+idx_schema_changes_disc_only(Config) when is_list(Config) ->
+ idx_schema_changes(Config, disc_only_copies).
+
+idx_schema_changes(Config, Storage) ->
+ [N1, N2] = Nodes = ?acquire_nodes(2, Config),
+ Tab = index_schema_changes,
+ Idx = 3,
+ Schema = [{name, Tab}, {index, [Idx]}, {attributes, [k, v]}, {Storage, Nodes}],
+ ?match({atomic, ok}, mnesia:create_table(Schema)),
+
+ {Storage1, Storage2} =
+ case Storage of
+ disc_only_copies ->
+ {ram_copies, disc_copies};
+ disc_copies ->
+ {disc_only_copies, ram_copies};
+ ram_copies ->
+ {disc_copies, disc_only_copies}
+ end,
+
+ Write = fun(N) ->
+ mnesia:write({Tab, N, N+50})
+ end,
+
+ [mnesia:sync_transaction(Write, [N]) || N <- lists:seq(1, 10)],
+ ?match([{Tab, 1, 51}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 51, Idx])),
+ ?match([{Tab, 1, 51}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 51, Idx])),
+
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, N1, Storage1)),
+
+ ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [17]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [18]])),
+
+ ?match([{Tab, 17, 67}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 67, Idx])),
+ ?match([{Tab, 18, 68}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 68, Idx])),
+
+ ?match({atomic, ok}, mnesia:del_table_copy(Tab, N1)),
+ ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [11]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [12]])),
+
+ ?match([{Tab, 11, 61}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 61, Idx])),
+ ?match([{Tab, 12, 62}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 62, Idx])),
+
+ ?match({atomic, ok}, mnesia:move_table_copy(Tab, N2, N1)),
+ ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [19]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [20]])),
+
+ ?match([{Tab, 19, 69}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 69, Idx])),
+ ?match([{Tab, 20, 70}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 70, Idx])),
+
+ ?match({atomic, ok}, mnesia:add_table_copy(Tab, N2, Storage)),
+ ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [13]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [14]])),
+
+ ?match([{Tab, 13, 63}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 63, Idx])),
+ ?match([{Tab, 14, 64}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 64, Idx])),
+
+ ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, N2, Storage2)),
+
+ ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [15]])),
+ ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [16]])),
+
+ ?match([{Tab, 15, 65}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 65, Idx])),
+ ?match([{Tab, 16, 66}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 66, Idx])),
+
+ ?verify_mnesia(Nodes, []).
diff --git a/lib/mnesia/test/mt b/lib/mnesia/test/mt
new file mode 100755
index 0000000000..25243f1149
--- /dev/null
+++ b/lib/mnesia/test/mt
@@ -0,0 +1,60 @@
+#! /bin/sh -f
+# ``The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved via the world wide web at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+# AB. All Rights Reserved.''
+#
+# $Id$
+#
+#
+# Author: Hakan Mattsson <[email protected]>
+# Purpose: Simplified execution of the test suite
+#
+# Usage: mt <args to erlang startup script>
+
+#top=".."
+top="$ERL_TOP/lib/mnesia"
+h=`hostname`
+p="-pa $top/examples -pa $top/ebin -pa $top/test -mnesia_test_verbose true"
+log=test_log$$
+latest=test_log_latest
+args=${1+"$@"}
+erlcmd="erl -sname a $p $args -mnesia_test_timeout"
+erlcmd1="erl -sname a1 $p $args"
+erlcmd2="erl -sname a2 $p $args"
+
+xterm -geometry 70x20+0+550 -T a1 -e $erlcmd1 &
+xterm -geometry 70x20+450+550 -T a2 -e $erlcmd2 &
+
+rm "$latest" 2>/dev/null
+ln -s "$log" "$latest"
+touch "$log"
+
+echo "$erlcmd1"
+echo ""
+echo "$erlcmd2"
+echo ""
+echo "$erlcmd"
+echo ""
+echo "Give the following command in order to see the outcome from node a@$h"":"
+echo ""
+echo " less test_log$$"
+
+ostype=`uname -s`
+if [ "$ostype" = "SunOS" ] ; then
+ /usr/openwin/bin/xterm -geometry 145x40+0+0 -T a -l -lf "$log" -e $erlcmd &
+else
+ xterm -geometry 145x40+0+0 -T a -e script -f -c "$erlcmd" "$log" &
+fi
+tail -f "$log" | egrep 'Eval|<>ERROR|NYI'
+
diff --git a/lib/mnesia/test/mt.erl b/lib/mnesia/test/mt.erl
new file mode 100644
index 0000000000..f69c4a11fd
--- /dev/null
+++ b/lib/mnesia/test/mt.erl
@@ -0,0 +1,262 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%%% Author: Hakan Mattsson [email protected]
+%%% Purpose: Nice shortcuts intended for testing of Mnesia
+%%%
+%%% See the mnesia_SUITE module about the structure of
+%%% the test suite.
+%%%
+%%% See the mnesia_test_lib module about the test case execution.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(mt).
+-author('[email protected]').
+-export([
+ t/0, t/1, t/2, t/3, % Run test cases
+ loop/1, loop/2, loop/3, % loop test cases
+ doc/0, doc/1, % Generate test case doc
+ struct/0, struct/1, % View test suite struct
+ shutdown/0, ping/0, start_nodes/0, % Node admin
+ read_config/0, write_config/1 % Config admin
+ ]).
+
+-include("mnesia_test_lib.hrl").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Aliases for the (sub) test suites
+alias(all) -> mnesia_SUITE;
+alias(atomicity) -> mnesia_atomicity_test;
+alias(backup) -> mnesia_evil_backup;
+alias(config) -> mnesia_config_test;
+alias(consistency) -> mnesia_consistency_test;
+alias(dirty) -> mnesia_dirty_access_test;
+alias(durability) -> mnesia_durability_test;
+alias(evil) -> mnesia_evil_coverage_test;
+alias(qlc) -> mnesia_qlc_test;
+alias(examples) -> mnesia_examples_test;
+alias(frag) -> mnesia_frag_test;
+alias(heavy) -> {mnesia_SUITE, heavy};
+alias(install) -> mnesia_install_test;
+alias(isolation) -> mnesia_isolation_test;
+alias(light) -> {mnesia_SUITE, light};
+alias(measure) -> mnesia_measure_test;
+alias(medium) -> {mnesia_SUITE, medium};
+alias(nice) -> mnesia_nice_coverage_test;
+alias(recover) -> mnesia_recover_test;
+alias(recovery) -> mnesia_recovery_test;
+alias(registry) -> mnesia_registry_test;
+alias(suite) -> mnesia_SUITE;
+alias(trans) -> mnesia_trans_access_test;
+alias(Other) -> Other.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Resolves the name of test suites and test cases
+%% according to the alias definitions. Single atoms
+%% are assumed to be the name of a test suite.
+resolve(Suite0) when is_atom(Suite0) ->
+ case alias(Suite0) of
+ Suite when is_atom(Suite) ->
+ {Suite, all};
+ {Suite, Case} ->
+ {Suite, Case}
+ end;
+resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) ->
+ case alias(Suite0) of
+ Suite when is_atom(Suite) ->
+ {Suite, Case};
+ {Suite, Case2} ->
+ {Suite, Case2}
+ end;
+resolve(List) when is_list(List) ->
+ [resolve(Case) || Case <- List].
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Run one or more test cases
+
+%% Run the default test case with default config
+t() ->
+ t(read_test_case()).
+
+%% Resolve the test case name and run the test case
+%% The test case is noted as default test case
+%% and the outcome of the tests are written to
+%% to a file.
+t(silly) ->
+ mnesia_install_test:silly();
+t(diskless) ->
+ %% Run the default test case with default config,
+ %% but diskless
+ t(read_test_case(), diskless);
+t(Case) ->
+ %% Use the default config
+ t(Case, read_config()).
+
+t(Case, Config) when Config == diskless ->
+ %% Run the test case with default config, but diskless
+ Config2 = [{diskless, true} | read_config()],
+ t(Case, Config2);
+t(Mod, Fun) when is_atom(Mod), is_atom(Fun) ->
+ %% Run the test case with default config
+ t({Mod, Fun}, read_config());
+t(RawCase, Config) when is_list(Config) ->
+ %% Resolve the test case name and run the test case
+ Case = resolve(RawCase),
+ write_test_case(Case),
+ Res = mnesia_test_lib:test(Case, Config),
+ append_test_case_info(Case, Res).
+
+t(Mod, Fun, Config) when Config == diskless ->
+ t({Mod, Fun}, diskless).
+
+config_fname() ->
+ "mnesia_test_case_config".
+
+%% Read default config file
+read_config() ->
+ Fname = config_fname(),
+ mnesia_test_lib:log("Consulting file ~s...~n", [Fname]),
+ case file:consult(Fname) of
+ {ok, Config} ->
+ mnesia_test_lib:log("Read config ~w~n", [Config]),
+ Config;
+ _Error ->
+ Config = mnesia_test_lib:default_config(),
+ mnesia_test_lib:log("<>WARNING<> Using default config: ~w~n", [Config]),
+ Config
+ end.
+
+%% Write new default config file
+write_config(Config) when is_list(Config) ->
+ Fname = config_fname(),
+ {ok, Fd} = file:open(Fname, write),
+ write_list(Fd, Config),
+ file:close(Fd).
+
+write_list(Fd, [H | T]) ->
+ ok = io:format(Fd, "~p.~n",[H]),
+ write_list(Fd, T);
+write_list(_, []) ->
+ ok.
+
+test_case_fname() ->
+ "mnesia_test_case_info".
+
+%% Read name of test case
+read_test_case() ->
+ Fname = test_case_fname(),
+ case file:open(Fname, [read]) of
+ {ok, Fd} ->
+ Res = io:read(Fd, []),
+ file:close(Fd),
+ case Res of
+ {ok, TestCase} ->
+ mnesia_test_lib:log("Using test case ~w from file ~s~n",
+ [TestCase, Fname]),
+ TestCase;
+ {error, _} ->
+ default_test_case(Fname)
+ end;
+ {error, _} ->
+ default_test_case(Fname)
+ end.
+
+default_test_case(Fname) ->
+ TestCase = all,
+ mnesia_test_lib:log("<>WARNING<> Cannot read file ~s, "
+ "using default test case: ~w~n",
+ [Fname, TestCase]),
+ TestCase.
+
+write_test_case(TestCase) ->
+ Fname = test_case_fname(),
+ {ok, Fd} = file:open(Fname, write),
+ ok = io:format(Fd, "~p.~n",[TestCase]),
+ file:close(Fd).
+
+append_test_case_info(TestCase, TestCaseInfo) ->
+ Fname = test_case_fname(),
+ {ok, Fd} = file:open(Fname, [read, write]),
+ ok = io:format(Fd, "~p.~n",[TestCase]),
+ ok = io:format(Fd, "~p.~n",[TestCaseInfo]),
+ file:close(Fd),
+ TestCaseInfo.
+
+%% Generate HTML pages from the test case structure
+doc() ->
+ doc(suite).
+
+doc(Case) ->
+ mnesia_test_lib:doc(resolve(Case)).
+
+%% Display out the test case structure
+struct() ->
+ struct(suite).
+
+struct(Case) ->
+ mnesia_test_lib:struct([resolve(Case)]).
+
+%% Shutdown all nodes with erlang:halt/0
+shutdown() ->
+ mnesia_test_lib:shutdown().
+
+%% Ping all nodes in config spec
+ping() ->
+ Config = read_config(),
+ Nodes = mnesia_test_lib:select_nodes(all, Config, ?FILE, ?LINE),
+ [{N, net_adm:ping(N)} || N <- Nodes].
+
+%% Slave start all nodes in config spec
+start_nodes() ->
+ Config = read_config(),
+ Nodes = mnesia_test_lib:select_nodes(all, Config, ?FILE, ?LINE),
+ mnesia_test_lib:init_nodes(Nodes, ?FILE, ?LINE),
+ ping().
+
+%% loop one testcase /suite until it fails
+
+loop(Case) ->
+ loop_1(Case,-1,read_config()).
+
+loop(M,F) when is_atom(F) ->
+ loop_1({M,F},-1,read_config());
+loop(Case,N) when is_integer(N) ->
+ loop_1(Case, N,read_config()).
+
+loop(M,F,N) when is_integer(N) ->
+ loop_1({M,F},N,read_config()).
+
+loop_1(Case,N,Config) when N /= 0 ->
+ io:format("Loop test ~p ~n", [abs(N)]),
+ case ok_result(Res = t(Case,Config)) of
+ true ->
+ loop_1(Case,N-1,Config);
+ error ->
+ Res
+ end;
+loop_1(_,_,_) ->
+ ok.
+
+ok_result([{_T,{ok,_,_}}|R]) ->
+ ok_result(R);
+ok_result([{_T,{TC,List}}|R]) when is_tuple(TC), is_list(List) ->
+ ok_result(List) andalso ok_result(R);
+ok_result([]) -> true;
+ok_result(_) -> error.
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index 31cc8f8513..5247657b68 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1,16 +1 @@
-
-MNESIA_VSN = 4.4.13
-
-TICKETS = OTP-8402 OTP-8406
-#TICKETS_4.4.12 = OTP-8250
-#TICKETS_4.4.11 = OTP-8074
-#TICKETS_4.4.10 = OTP-7928 OTP-7968 OTP-8002
-#TICKETS_4.4.9 = OTP-7911
-#TICKETS_4.4.8 = OTP-7753 OTP-7835
-#TICKETS_4.4.7 = OTP-7524 OTP-7625
-#TICKETS_4.4.6 = OTP-7585
-#TICKETS_4.4.5 = OTP-7466
-#TICKETS_4.4.4 = OTP-7419
-#TICKETS_4.4.3 = OTP-7340 OTP-7378 OTP-7383
-#TICKETS_4.4.2 = OTP-7205 OTP-7208
-#TICKETS_4.4.1 = OTP-7170
+MNESIA_VSN = 4.4.17
diff --git a/lib/observer/doc/src/crashdump.xml b/lib/observer/doc/src/crashdump.xml
index f8d7641524..b6056c2ed1 100644
--- a/lib/observer/doc/src/crashdump.xml
+++ b/lib/observer/doc/src/crashdump.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2003</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -38,6 +38,10 @@
<description>
<p>The Crashdump Viewer is an HTML based tool for browsing Erlang
crashdumps. Crashdump Viewer runs under the WebTool application.</p>
+
+ <p>See the <seealso marker="crashdump_ug">user's guide</seealso>
+ for more information about how to get started with the Crashdump
+ Viewer.</p>
</description>
<funcs>
<func>
diff --git a/lib/observer/doc/src/crashdump_help.html b/lib/observer/doc/src/crashdump_help.html
index 736a024288..268b9495d6 100644
--- a/lib/observer/doc/src/crashdump_help.html
+++ b/lib/observer/doc/src/crashdump_help.html
@@ -131,7 +131,7 @@ SRC="min_head.gif"></a>
- <a NAME="ets_tables">
+ <a NAME="ets_tables"><a NAME="internal_ets_tables">
<h3>ETS tables</h3>
<p>The ETS table information page shows all ETS table
@@ -304,4 +304,4 @@ Copyright &copy; 1991-2003
</font>
</center>
</body>
-</html> \ No newline at end of file
+</html>
diff --git a/lib/observer/doc/src/crashdump_ug.xml b/lib/observer/doc/src/crashdump_ug.xml
index 9913b30e38..dc65fe5b39 100644
--- a/lib/observer/doc/src/crashdump_ug.xml
+++ b/lib/observer/doc/src/crashdump_ug.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,12 +38,31 @@
<section>
<title>Getting Started</title>
- <p>From an erlang node, start Crashdump Viewer by calling
- <c>crashdump_viewer:start()</c>. This will automatically start
- WebTool and display the web address where WebTool can be
- found. See the documentation for the WebTool application for
- further information about how to use WebTool.
- </p>
+
+ <p>The easiest way to start Crashdump Viewer is to use the
+ provided shell script named <c>cdv</c> with the full path to the
+ erlang crashdump as an argument. The script can be found in the
+ priv directory of the <c>observer</c> application. This starts
+ WebTool, Crashdump Viewer and a web browser, and loads the given
+ file. The browser should then display a page named General
+ Information which shows a short summary of the information in
+ the crashdump.</p>
+
+ <p>The default browser is Internet Explorer on Windows or else
+ Firefox. To use another browser, give the browser's start command
+ as the second argument to <c>cdv</c>. If the given browser name is
+ not known to Crashdump Viewer, the browser argument is executed as
+ a command with the start URL as the only argument.</p>
+
+ <p>Under Windows the batch file <c>cdv.bat</c> can be used.</p>
+
+ <p>It is also possible to start the Crashdump Viewer from within
+ an erlang node by calling <seealso
+ marker="crashdump_viewer#start/0">crashdump_viewer:start/0</seealso>. This
+ will automatically start WebTool and display the web address where
+ WebTool can be found. See the documentation for the WebTool
+ application for further information about how to use WebTool.</p>
+
<p>Point your web browser to the address displayed, and you should
now see the start page of WebTool. At the top of the page, you
will see a link to "CrashDumpViewer". Click this link to get to
@@ -52,15 +71,12 @@
connection to the internet, or you must set no proxy for
localhost.)
</p>
- <p>You can also start WebTool, Crashdump Viewer and a browser in
- one go by running the <c>start_webtool</c> script found in the
- <c>priv</c> directory of the WebTool application, e.g.
- <br></br>
-<c>>start_webtool crashdump_viewer</c></p>
<p>From the start page of Crashdump Viewer, push the "Load
Crashdump" button to load a crashdump into the tool. Then enter
- the filename of the crashdump in the entry field and push the "Ok"
- button.
+ the filename of the crashdump in the entry field and push the
+ "Ok" button. This will bring you to the General Information
+ page, i.e. the same page as the <c>cdv</c> script will open in
+ the browser.
</p>
<p>Crashdumps generated by OTP R9C and later are loaded directly
into the Crashdump Viewer, while dumps from earlier releases first
diff --git a/lib/observer/doc/src/etop.xml b/lib/observer/doc/src/etop.xml
index 1ea67e6864..78047caab3 100644
--- a/lib/observer/doc/src/etop.xml
+++ b/lib/observer/doc/src/etop.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -26,27 +26,33 @@
<title>etop</title>
<prepared>Siri hansen</prepared>
<responsible></responsible>
- <docno>1</docno>
+ <docno></docno>
<approved></approved>
<checked></checked>
- <date>2002-03-27</date>
- <rev>PA1</rev>
- <file>etop.sgml</file>
+ <date></date>
+ <rev></rev>
+ <file></file>
</header>
<module>etop</module>
<modulesummary>Erlang Top is a tool for presenting information about erlang processes similar to the information presented by "top" in UNIX.</modulesummary>
<description>
+
<p><c>etop</c> should be started with the provided scripts
<c>etop</c> and <c>getop</c> for text based and graphical
- presentation respectively. Under Windows the batch files
- <c>etop.bat</c> and <c>getop.bat</c> can be used.
- </p>
- <p>All interaction with <c>etop</c> when running the graphical
- presentation should happen via the menus. For the text based
- presentation the functions described below can be used.
- </p>
- <p>The following configuration parameters exist for <c>etop</c>.
- </p>
+ presentation respectively. This will start a hidden erlang node
+ which connects to the node to be measured. The measured node is
+ given with the <c>-node</c> option. If the measured node has a
+ different cookie than the default cookie for the user who
+ invokes the script, the cookie must be explicitly given witht
+ the <c>-setcookie</c> option.</p>
+
+ <p>Under Windows the batch files <c>etop.bat</c> and
+ <c>getop.bat</c> can be used.</p>
+
+ <p>The following configuration parameters exist for the
+ <c>etop</c> tool. When executing the <c>etop</c> or <c>getop</c>
+ scripts, these parameters can be given as command line options,
+ e.g. <c>getop -node testnode@myhost -setcookie MyCookie</c>.</p>
<taglist>
<tag>node</tag>
<item>The measured node.
@@ -96,6 +102,15 @@ Default: <c>runtime</c> (<c>reductions</c> if
Value: <c>on | off</c> <br></br>
Default: <c>on</c></item>
</taglist>
+
+ <p>All interaction with <c>etop</c> when running the graphical
+ presentation should happen via the menus. For the text based
+ presentation the functions described below can be used.
+ </p>
+
+ <p>See the <seealso marker="etop_ug">user's guide</seealso> for
+ more information about the <c>etop</c> tool.</p>
+
</description>
<funcs>
<func>
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index 48c7c21363..b3b9937f1c 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -31,6 +31,78 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 0.9.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The time needed for loading a crashump into the crashdump
+ viewer would earlier grow exponentially with the size of
+ the crashdump file. Reading a file of 20M would take a
+ couple of minutes, and for a dump of 250M it would take
+ between 1 and 2 hours. This has been solved.</p>
+ <p>
+ Earlier, all processes, timers, funs or ets-tables would
+ be loaded into the memory of the crashdump viewer node
+ before sending it on to the web server. This has been
+ changed and the pages are now sent to the web server in
+ chunks.</p>
+ <p>
+ A security function in newer web browsers prevents a full
+ file path to be sent from an HTML file input field, i.e.
+ the field needed to implement the "Browse" button when
+ loading a file into the crashdump viewer. To overcome
+ this, the file input field is no longer used. Instead a
+ normal text input field is used, and the user needs to
+ manually insert the complete file path. For convenience,
+ a shell script and a batch file are added to the observer
+ application. These can be used to start the
+ crashdump_viewer and a browser and load a file - with the
+ file name given from the command line. The shell script
+ and batch file are called cdv and cdv.bat respectively,
+ and can be found in the priv dir of the observer
+ application.</p>
+ <p>
+ Own Id: OTP-9051 Aux Id: seq11789 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Observer 0.9.8.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The multitrace.erl installation example file is now
+ installed in the examples directory. (Thanks to Peter
+ Lemenkov.)</p>
+ <p>
+ Own Id: OTP-8857</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Observer 0.9.8.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The test suite has been updated for R14A.</p>
+ <p>
+ Own Id: OTP-8708</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 0.9.8.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/observer/doc/src/notes_history.xml b/lib/observer/doc/src/notes_history.xml
index 8c350cd012..2300983131 100644
--- a/lib/observer/doc/src/notes_history.xml
+++ b/lib/observer/doc/src/notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/observer/doc/src/observer_app.xml b/lib/observer/doc/src/observer_app.xml
index aadc325745..e643568a39 100644
--- a/lib/observer/doc/src/observer_app.xml
+++ b/lib/observer/doc/src/observer_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/observer/doc/src/part_notes_history.xml b/lib/observer/doc/src/part_notes_history.xml
index 3f07c3ce20..1ba0875fec 100644
--- a/lib/observer/doc/src/part_notes_history.xml
+++ b/lib/observer/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/observer/doc/src/ttb.xml b/lib/observer/doc/src/ttb.xml
index fcaa1c2504..2c80891925 100644
--- a/lib/observer/doc/src/ttb.xml
+++ b/lib/observer/doc/src/ttb.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/observer/priv/bin/cdv b/lib/observer/priv/bin/cdv
new file mode 100755
index 0000000000..1c44785ac2
--- /dev/null
+++ b/lib/observer/priv/bin/cdv
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+erl -sname cdv -noinput -s crashdump_viewer script_start $@
+
diff --git a/lib/observer/priv/bin/cdv.bat b/lib/observer/priv/bin/cdv.bat
new file mode 100644
index 0000000000..efa8bf8687
--- /dev/null
+++ b/lib/observer/priv/bin/cdv.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+CALL werl -sname cdv -s crashdump_viewer script_start %*
diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile
index dde1ea17be..2d06cb6bc4 100644
--- a/lib/observer/src/Makefile
+++ b/lib/observer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+# Copyright Ericsson AB 2002-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -59,8 +59,10 @@ BINDIR= $(PRIVDIR)/bin
EXECUTABLES= \
$(BINDIR)/etop \
$(BINDIR)/getop \
+ $(BINDIR)/cdv \
$(BINDIR)/etop.bat \
- $(BINDIR)/getop.bat
+ $(BINDIR)/getop.bat \
+ $(BINDIR)/cdv.bat
CDVDIR= $(PRIVDIR)/crashdump_viewer
GIF_FILES= \
$(CDVDIR)/collapsd.gif \
@@ -111,7 +113,8 @@ release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/src
$(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src
$(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/examples
+ $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples
$(INSTALL_DIR) $(RELSYSDIR)/include
$(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include
$(INSTALL_DIR) $(RELSYSDIR)/ebin
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index 978541e470..3b8d17c7d9 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
%% the server started by webtool and the API for the crashdump viewer tool.
%%
%% All functions in the API except configData/0 and start_link/0 are
-%% called from HTML pages via erl_scheme.
+%% called from HTML pages via erl_scheme (mod_esi).
%%
%% Tables
%% ------
@@ -34,18 +34,21 @@
%%
%% cdv_dump_index_table: This table holds all tags read from the crashdump.
%% Each tag indicates where the information about a specific item starts.
-%% The table entry for a tag includes the start and end positions for
-%% this item-information. All tags start with a "=" at the beginning of
+%% The table entry for a tag includes the start position for this
+%% item-information. All tags start with a "=" at the beginning of
%% a line.
%%
%% Process state
%% -------------
%% file: The name of the crashdump currently viewed.
%% procs_summary: Process summary represented by a list of
-%% #proc records. This is used for efficiency reasons when sorting
-%% the process summary table instead of reading all processes from
-%% the dump again.
-%% sorted: atom(), indicated what item was last sorted in process summary.
+%% #proc records. This is used for efficiency reasons when sorting the
+%% process summary table instead of reading all processes from the
+%% dump again. Note that if the dump contains more than
+%% ?max_sort_process_num processes, the sort functionality is not
+%% available, and the procs_summary field in the state will have the
+%% value 'too_many'.
+%% sorted: string(), indicated what item was last sorted in process summary.
%% This is needed so reverse sorting can be done.
%% shared_heap: 'true' if crashdump comes from a system running shared heap,
%% else 'false'.
@@ -54,7 +57,7 @@
%%
%% User API
--export([start/0,stop/0]).
+-export([start/0,stop/0,script_start/0,script_start/1]).
%% Webtool API
-export([configData/0,
@@ -68,26 +71,27 @@
initial_info_frame/2,
toggle/2,
general_info/2,
- processes/2,
+ processes/3,
proc_details/2,
- ports/2,
- ets_tables/2,
- timers/2,
- fun_table/2,
- atoms/2,
+ port/2,
+ ports/3,
+ ets_tables/3,
+ internal_ets_tables/2,
+ timers/3,
+ fun_table/3,
+ atoms/3,
dist_info/2,
- loaded_modules/2,
+ loaded_modules/3,
loaded_mod_details/2,
memory/2,
allocated_areas/2,
allocator_info/2,
hash_tables/2,
index_tables/2,
- sort_procs/2,
+ sort_procs/3,
expand/2,
expand_binary/2,
- expand_memory/2,
- next/2]).
+ expand_memory/2]).
%% gen_server callbacks
@@ -113,24 +117,49 @@
% this, it must be explicitly expanded.
-define(max_display_binary_size,50). % max size of a binary that will be
% directly displayed.
+-define(max_sort_process_num,10000). % Max number of processes that allows
+ % sorting. If more than this number of
+ % processes exist, they will be displayed
+ % in the order they are found in the log.
+-define(items_chunk_size,?max_sort_process_num). % Number of items per chunk
+ % when page of many items
+ % is displayed, e.g. processes,
+ % timers, funs...
+ % Must be equal to
+ % ?max_sort_process_num!
+
+%% All possible tags - use macros in order to avoid misspelling in the code
+-define(allocated_areas,allocated_areas).
+-define(allocator,allocator).
+-define(atoms,atoms).
+-define(binary,binary).
+-define(debug_proc_dictionary,debug_proc_dictionary).
+-define(ende,ende).
+-define(erl_crash_dump,erl_crash_dump).
+-define(ets,ets).
+-define(fu,fu).
+-define(hash_table,hash_table).
+-define(hidden_node,hidden_node).
+-define(index_table,index_table).
+-define(instr_data,instr_data).
+-define(internal_ets,internal_ets).
+-define(loaded_modules,loaded_modules).
+-define(memory,memory).
+-define(mod,mod).
+-define(no_distribution,no_distribution).
+-define(node,node).
+-define(not_connected,not_connected).
+-define(num_atoms,num_atoms).
+-define(old_instr_data,old_instr_data).
+-define(port,port).
+-define(proc,proc).
+-define(proc_dictionary,proc_dictionary).
+-define(proc_heap,proc_heap).
+-define(proc_messages,proc_messages).
+-define(proc_stack,proc_stack).
+-define(timer,timer).
+-define(visible_node,visible_node).
--define(initial_proc_record(Pid),
- #proc{pid=Pid,
- %% msg_q_len, reds and stack_heap are integers because it must
- %% be possible to sort on them. All other fields are strings
- msg_q_len=0,reds=0,stack_heap=0,
- %% for old dumps start_time, parent and number of heap frament
- %% does not exist
- start_time="unknown",
- parent="unknown",
- num_heap_frag="unknown",
- %% current_func can be both "current function" and
- %% "last scheduled in for"
- current_func={"Current Function",?space},
- %% stack_dump, message queue and dictionaries should only be
- %% displayed as a link to "Expand" (if dump is from OTP R9B
- %% or newer)
- _=?space}).
-record(state,{file,procs_summary,sorted,shared_heap=false,
wordsize=4,num_atoms="unknown",binaries,bg_status}).
@@ -177,6 +206,85 @@ stop() ->
webtool:stop().
%%%-----------------------------------------------------------------
+%%% Start crashdump_viewer via the cdv script located in
+%%% $OBSERVER_PRIV_DIR/bin
+script_start() ->
+ usage().
+script_start([File]) ->
+ DefaultBrowser =
+ case os:type() of
+ {win32,_} -> iexplore;
+ _ -> firefox
+ end,
+ script_start([File,DefaultBrowser]);
+script_start([FileAtom,Browser]) ->
+ File = atom_to_list(FileAtom),
+ case filelib:is_regular(File) of
+ true ->
+ io:format("Starting crashdump_viewer...\n"),
+ start(),
+ io:format("Reading crashdump..."),
+ read_file(File),
+ redirect([],[]),
+ io:format("done\n"),
+ start_browser(Browser);
+ false ->
+ io:format("cdv error: the given file does not exist\n"),
+ usage()
+ end.
+
+start_browser(Browser) ->
+ PortStr = integer_to_list(gen_server:call(web_tool,get_port)),
+ Url = "http://localhost:" ++ PortStr ++ ?START_PAGE,
+ {OSType,_} = os:type(),
+ case Browser of
+ none ->
+ ok;
+ iexplore when OSType == win32->
+ io:format("Starting internet explorer...\n"),
+ {ok,R} = win32reg:open(""),
+ Key="\\local_machine\\SOFTWARE\\Microsoft\\IE Setup\\Setup",
+ win32reg:change_key(R,Key),
+ {ok,Val} = win32reg:value(R,"Path"),
+ IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"),
+ os:cmd("\"" ++ IExplore ++ "\" " ++ Url);
+ _ when OSType == win32 ->
+ io:format("Starting ~w...\n",[Browser]),
+ os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url);
+ B when B==firefox; B==mozilla ->
+ io:format("Sending URL to ~w...",[Browser]),
+ BStr = atom_to_list(Browser),
+ SendCmd = BStr ++ " -raise -remote \'openUrl(" ++ Url ++ ")\'",
+ Port = open_port({spawn,SendCmd},[exit_status]),
+ receive
+ {Port,{exit_status,0}} ->
+ io:format("done\n");
+ {Port,{exit_status,_Error}} ->
+ io:format(" not running, starting ~w...\n",[Browser]),
+ os:cmd(BStr ++ " " ++ Url)
+ after 5000 ->
+ io:format(" failed, starting ~w...\n",[Browser]),
+ erlang:port_close(Port),
+ os:cmd(BStr ++ " " ++ Url)
+ end;
+ _ ->
+ io:format("Starting ~w...\n",[Browser]),
+ os:cmd(atom_to_list(Browser) ++ " " ++ Url)
+ end,
+ ok.
+
+usage() ->
+ io:format(
+ "\nusage: cdv file [ browser ]\n"
+ "\tThe \'file\' must be an existing erlang crash dump.\n"
+ "\tDefault browser is \'iexplore\' (Internet Explorer) on Windows\n"
+ "\tor else \'firefox\'.\n",
+ []).
+
+
+
+
+%%%-----------------------------------------------------------------
%%% Return config data used by webtool
configData() ->
Dir = filename:join(code:priv_dir(observer),"crashdump_viewer"),
@@ -266,22 +374,24 @@ toggle(_Env,Input) ->
%%% The following functions are called when menu items are clicked.
general_info(_Env,_Input) ->
call(general_info).
-processes(_Env,_Input) ->
- call(procs_summary).
-ports(_Env,Input) -> % this is also called when a link to a port is clicked
- call({ports,Input}).
-ets_tables(_Env,Input) ->
- call({ets_tables,Input}).
-timers(_Env,Input) ->
- call({timers,Input}).
-fun_table(_Env,_Input) ->
- call(funs).
-atoms(_Env,_Input) ->
- call(atoms).
+processes(SessionId,_Env,_Input) ->
+ call({procs_summary,SessionId}).
+ports(SessionId,_Env,_Input) ->
+ call({ports,SessionId}).
+ets_tables(SessionId,_Env,Input) ->
+ call({ets_tables,SessionId,Input}).
+internal_ets_tables(_Env,_Input) ->
+ call(internal_ets_tables).
+timers(SessionId,_Env,Input) ->
+ call({timers,SessionId,Input}).
+fun_table(SessionId,_Env,_Input) ->
+ call({funs,SessionId}).
+atoms(SessionId,_Env,_Input) ->
+ call({atoms,SessionId}).
dist_info(_Env,_Input) ->
call(dist_info).
-loaded_modules(_Env,_Input) ->
- call(loaded_mods).
+loaded_modules(SessionId,_Env,_Input) ->
+ call({loaded_mods,SessionId}).
loaded_mod_details(_Env,Input) ->
call({loaded_mod_details,Input}).
memory(_Env,_Input) ->
@@ -303,8 +413,13 @@ proc_details(_Env,Input) ->
%%%-----------------------------------------------------------------
%%% Called when one of the headings in the process summary table are
%%% clicked. It sorts the processes by the clicked heading.
-sort_procs(_Env,Input) ->
- call({sort_procs,Input}).
+sort_procs(SessionId,_Env,Input) ->
+ call({sort_procs,SessionId,Input}).
+
+%%%-----------------------------------------------------------------
+%%% Called when a link to a port is clicked.
+port(_Env,Input) ->
+ call({port,Input}).
%%%-----------------------------------------------------------------
%%% Called when the "Expand" link in a call stack (Last Calls) is
@@ -325,11 +440,6 @@ expand_binary(_Env,Input) ->
call({expand_binary,Input}).
%%%-----------------------------------------------------------------
-%%% Called when the "Next" link under atoms is clicked.
-next(_Env,Input) ->
- call({next,Input}).
-
-%%%-----------------------------------------------------------------
%%% Called on regular intervals while waiting for a dump to be read
redirect(_Env,_Input) ->
call(redirect).
@@ -348,7 +458,7 @@ redirect(_Env,_Input) ->
%%--------------------------------------------------------------------
init([]) ->
ets:new(cdv_menu_table,[set,named_table,{keypos,#menu_item.index},public]),
- ets:new(cdv_dump_index_table,[bag,named_table,public]),
+ ets:new(cdv_dump_index_table,[ordered_set,named_table,public]),
{ok, #state{}}.
%%--------------------------------------------------------------------
@@ -373,16 +483,7 @@ handle_call(start_page, _From, State) ->
Reply = crashdump_viewer_html:start_page(),
{reply,Reply,State};
handle_call({read_file,Input}, _From, _State) ->
- {ok,File0} = get_value("path",httpd:parse_query(Input)),
- File =
- case File0 of
- [$"|FileAndSome] ->
- %% Opera adds \"\" around the filename!
- [$"|Elif] = lists:reverse(FileAndSome),
- lists:reverse(Elif);
- _ ->
- File0
- end,
+ {ok,File} = get_value("path",httpd:parse_query(Input)),
spawn_link(fun() -> read_file(File) end),
Status = background_status(reading,File),
Reply = crashdump_viewer_html:redirect(Status),
@@ -399,8 +500,17 @@ handle_call(initial_info_frame,_From,State=#state{file=File}) ->
GenInfo = general_info(File),
NumAtoms = GenInfo#general_info.num_atoms,
{WS,SH} = parse_vsn_str(GenInfo#general_info.system_vsn,4,false),
+ NumProcs = list_to_integer(GenInfo#general_info.num_procs),
+ ProcsSummary =
+ if NumProcs > ?max_sort_process_num -> too_many;
+ true -> State#state.procs_summary
+ end,
+ NewState = State#state{shared_heap=SH,
+ wordsize=WS,
+ num_atoms=NumAtoms,
+ procs_summary=ProcsSummary},
Reply = crashdump_viewer_html:general_info(GenInfo),
- {reply,Reply,State#state{shared_heap=SH,wordsize=WS,num_atoms=NumAtoms}};
+ {reply,Reply,NewState};
handle_call({toggle,Input},_From,State) ->
{ok,Index} = get_value("index",httpd:parse_query(Input)),
do_toggle(list_to_integer(Index)),
@@ -429,7 +539,7 @@ handle_call({expand,Input},_From,State=#state{file=File}) ->
handle_call({expand_memory,Input},_From,State=#state{file=File,binaries=B}) ->
[{"pid",Pid},{"what",What}] = httpd:parse_query(Input),
Reply =
- case truncated_warning([{"=proc",Pid}]) of
+ case truncated_warning([{?proc,Pid}]) of
[] ->
Expanded = expand_memory(File,What,Pid,B),
crashdump_viewer_html:expanded_memory(What,Expanded);
@@ -450,149 +560,129 @@ handle_call({expand_binary,Input},_From,State=#state{file=File}) ->
close(Fd),
Reply=crashdump_viewer_html:expanded_binary(io_lib:format("~p",[Bin])),
{reply,Reply,State};
-handle_call({next,Input},_From,State=#state{file=File}) ->
- [{"pos",Pos},{"num",N},{"start",Start},{"what",What}] =
- httpd:parse_query(Input),
- Tags = related_tags(What),
- TW = truncated_warning(Tags),
- Next = get_next(File,list_to_integer(Pos),list_to_integer(N),
- list_to_integer(Start),What),
- Reply = crashdump_viewer_html:next(Next,TW),
- {reply,Reply,State};
handle_call(general_info,_From,State=#state{file=File}) ->
GenInfo=general_info(File),
Reply = crashdump_viewer_html:general_info(GenInfo),
{reply,Reply,State};
-handle_call(procs_summary,_From,State=#state{file=File,shared_heap=SH}) ->
- ProcsSummary =
- case State#state.procs_summary of
- undefined -> procs_summary(File);
- PS -> PS
- end,
- TW = truncated_warning(["=proc"]),
- Reply = crashdump_viewer_html:procs_summary("pid",ProcsSummary,TW,SH),
- {reply,Reply,State#state{procs_summary=ProcsSummary,sorted="pid"}};
-handle_call({sort_procs,Input}, _From, State=#state{shared_heap=SH}) ->
+handle_call({procs_summary,SessionId},_From,State) ->
+ TW = truncated_warning([?proc]),
+ NewState = procs_summary(SessionId,TW,"pid",State#state{sorted=undefined}),
+ {reply,ok,NewState};
+handle_call({sort_procs,SessionId,Input}, _From, State) ->
{ok,Sort} = get_value("sort",httpd:parse_query(Input)),
- {ProcsSummary,Sorted} = do_sort_procs(Sort,
- State#state.procs_summary,
- State#state.sorted),
- TW = truncated_warning(["=proc"]),
- Reply = crashdump_viewer_html:procs_summary(Sort,ProcsSummary,TW,SH),
- {reply,Reply,State#state{sorted=Sorted}};
+ TW = truncated_warning([?proc]),
+ NewState = procs_summary(SessionId,TW,Sort,State),
+ {reply,ok,NewState};
handle_call({proc_details,Input},_From,State=#state{file=File,shared_heap=SH}) ->
{ok,Pid} = get_value("pid",httpd:parse_query(Input)),
Reply =
case get_proc_details(File,Pid) of
{ok,Proc} ->
- TW = truncated_warning([{"=proc",Pid}]),
+ TW = truncated_warning([{?proc,Pid}]),
crashdump_viewer_html:proc_details(Pid,Proc,TW,SH);
{other_node,Node} ->
- TW = truncated_warning(["=visible_node",
- "=hidden_node",
- "=not_connected"]),
+ TW = truncated_warning([?visible_node,
+ ?hidden_node,
+ ?not_connected]),
crashdump_viewer_html:nods(Node,TW);
not_found ->
crashdump_viewer_html:info_page(["Could not find process: ",
Pid],?space)
end,
{reply, Reply, State};
-handle_call({ports,Input},_From,State=#state{file=File}) ->
+handle_call({port,Input},_From,State=#state{file=File}) ->
+ {ok,P} = get_value("port",httpd:parse_query(Input)),
+ Id = [$#|P],
Reply =
- case get_value("port",httpd:parse_query(Input)) of
- {ok,P} ->
- Id = [$#|P],
- case get_port(File,Id) of
- {ok,PortInfo} ->
- TW = truncated_warning([{"=port",Id}]),
- crashdump_viewer_html:ports(Id,[PortInfo],TW);
- {other_node,Node} ->
- TW = truncated_warning(["=visible_node",
- "=hidden_node",
- "=not_connected"]),
- crashdump_viewer_html:nods(Node,TW);
- not_found ->
- crashdump_viewer_html:info_page(
- ["Could not find port: ",Id],?space)
- end;
- error -> % no port identity in Input - get all ports
- Ports=get_ports(File),
- TW = truncated_warning(["=port"]),
- crashdump_viewer_html:ports("Port Information",Ports,TW)
+ case get_port(File,Id) of
+ {ok,PortInfo} ->
+ TW = truncated_warning([{?port,Id}]),
+ crashdump_viewer_html:port(Id,PortInfo,TW);
+ {other_node,Node} ->
+ TW = truncated_warning([?visible_node,
+ ?hidden_node,
+ ?not_connected]),
+ crashdump_viewer_html:nods(Node,TW);
+ not_found ->
+ crashdump_viewer_html:info_page(
+ ["Could not find port: ",Id],?space)
end,
{reply,Reply,State};
-handle_call({ets_tables,Input},_From,State=#state{file=File,wordsize=WS}) ->
- {Pid,Heading,InternalEts} =
+handle_call({ports,SessionId},_From,State=#state{file=File}) ->
+ TW = truncated_warning([?port]),
+ get_ports(SessionId,File,TW),
+ {reply,ok,State};
+handle_call({ets_tables,SessionId,Input},_From,State=#state{file=File,wordsize=WS}) ->
+ {Pid,Heading} =
case get_value("pid",httpd:parse_query(Input)) of
{ok,P} ->
- {P,["ETS Tables for Process ",P],[]};
+ {P,["ETS Tables for Process ",P]};
error ->
- I = get_internal_ets_tables(File,WS),
- {'_',"ETS Table Information",I}
+ {'$2',"ETS Table Information"}
end,
- EtsTables = get_ets_tables(File,Pid,WS),
- TW = truncated_warning(["=ets"]),
- Reply = crashdump_viewer_html:ets_tables(Heading,EtsTables,InternalEts,TW),
+ TW = truncated_warning([?ets]),
+ get_ets_tables(SessionId,File,Heading,TW,Pid,WS),
+ {reply,ok,State};
+handle_call(internal_ets_tables,_From,State=#state{file=File,wordsize=WS}) ->
+ InternalEts = get_internal_ets_tables(File,WS),
+ TW = truncated_warning([?internal_ets]),
+ Reply = crashdump_viewer_html:internal_ets_tables(InternalEts,TW),
{reply,Reply,State};
-handle_call({timers,Input},_From,State=#state{file=File}) ->
+handle_call({timers,SessionId,Input},_From,State=#state{file=File}) ->
{Pid,Heading} =
case get_value("pid",httpd:parse_query(Input)) of
{ok,P} -> {P,["Timers for Process ",P]};
- error -> {'_',"Timer Information"}
+ error -> {'$2',"Timer Information"}
end,
- Timers=get_timers(File,Pid),
- TW = truncated_warning(["=timer"]),
- Reply = crashdump_viewer_html:timers(Heading,Timers,TW),
- {reply,Reply,State};
+ TW = truncated_warning([?timer]),
+ get_timers(SessionId,File,Heading,TW,Pid),
+ {reply,ok,State};
handle_call(dist_info,_From,State=#state{file=File}) ->
Nods=nods(File),
- TW = truncated_warning(["=visible_node","=hidden_node","=not_connected"]),
+ TW = truncated_warning([?visible_node,?hidden_node,?not_connected]),
Reply = crashdump_viewer_html:nods(Nods,TW),
{reply,Reply,State};
-handle_call(loaded_mods,_From,State=#state{file=File}) ->
- LoadedMods=loaded_mods(File),
- TW = truncated_warning(["=mod"]),
- Reply = crashdump_viewer_html:loaded_mods(LoadedMods,TW),
- {reply,Reply,State};
+handle_call({loaded_mods,SessionId},_From,State=#state{file=File}) ->
+ TW = truncated_warning([?mod]),
+ loaded_mods(SessionId,File,TW),
+ {reply,ok,State};
handle_call({loaded_mod_details,Input},_From,State=#state{file=File}) ->
{ok,Mod} = get_value("mod",httpd:parse_query(Input)),
ModInfo = get_loaded_mod_details(File,Mod),
- TW = truncated_warning([{"=mod",Mod}]),
+ TW = truncated_warning([{?mod,Mod}]),
Reply = crashdump_viewer_html:loaded_mod_details(ModInfo,TW),
{reply,Reply,State};
-handle_call(funs,_From,State=#state{file=File}) ->
- Funs=funs(File),
- TW = truncated_warning(["=fun"]),
- Reply = crashdump_viewer_html:funs(Funs,TW),
- {reply,Reply,State};
-handle_call(atoms,_From,State=#state{file=File,num_atoms=Num}) ->
- Atoms=atoms(File),
- TW = truncated_warning(["=atoms","=num_atoms"]),
- Reply = crashdump_viewer_html:atoms(Atoms,Num,TW),
- {reply,Reply,State};
+handle_call({funs,SessionId},_From,State=#state{file=File}) ->
+ TW = truncated_warning([?fu]),
+ funs(SessionId,File,TW),
+ {reply,ok,State};
+handle_call({atoms,SessionId},_From,State=#state{file=File,num_atoms=Num}) ->
+ TW = truncated_warning([?atoms,?num_atoms]),
+ atoms(SessionId,File,TW,Num),
+ {reply,ok,State};
handle_call(memory,_From,State=#state{file=File}) ->
Memory=memory(File),
- TW = truncated_warning(["=memory"]),
+ TW = truncated_warning([?memory]),
Reply = crashdump_viewer_html:memory(Memory,TW),
{reply,Reply,State};
handle_call(allocated_areas,_From,State=#state{file=File}) ->
AllocatedAreas=allocated_areas(File),
- TW = truncated_warning(["=allocated_areas"]),
+ TW = truncated_warning([?allocated_areas]),
Reply = crashdump_viewer_html:allocated_areas(AllocatedAreas,TW),
{reply,Reply,State};
handle_call(allocator_info,_From,State=#state{file=File}) ->
SlAlloc=allocator_info(File),
- TW = truncated_warning(["=allocator"]),
+ TW = truncated_warning([?allocator]),
Reply = crashdump_viewer_html:allocator_info(SlAlloc,TW),
{reply,Reply,State};
handle_call(hash_tables,_From,State=#state{file=File}) ->
HashTables=hash_tables(File),
- TW = truncated_warning(["=hash_table","=index_table"]),
+ TW = truncated_warning([?hash_table,?index_table]),
Reply = crashdump_viewer_html:hash_tables(HashTables,TW),
{reply,Reply,State};
handle_call(index_tables,_From,State=#state{file=File}) ->
IndexTables=index_tables(File),
- TW = truncated_warning(["=hash_table","=index_table"]),
+ TW = truncated_warning([?hash_table,?index_table]),
Reply = crashdump_viewer_html:index_tables(IndexTables,TW),
{reply,Reply,State}.
@@ -682,9 +772,9 @@ truncated_here(Tag) ->
%% Check if the dump was truncated with the same tag, but earlier id.
-%% Eg if this is {"=proc","<0.30.0>"}, we should warn if the dump was
-%% truncated in {"=proc","<0.29.0>"} or earlier
-truncated_earlier({"=proc",Pid}) ->
+%% Eg if this is {?proc,"<0.30.0>"}, we should warn if the dump was
+%% truncated in {?proc,"<0.29.0>"} or earlier
+truncated_earlier({?proc,Pid}) ->
compare_pid(Pid,get(truncated_proc));
truncated_earlier(_Tag) ->
false.
@@ -718,9 +808,37 @@ open(File) ->
close(Fd) ->
erase(chunk),
file:close(Fd).
+
+%% Set position relative to beginning of file
+%% If position is within the already read Chunk, then adjust 'chunk'
+%% and 'pos' in process dictionary. Else set position in file.
pos_bof(Fd,Pos) ->
+ case get(pos) of
+ undefined ->
+ hard_pos_bof(Fd,Pos);
+ OldPos when Pos>=OldPos ->
+ case get(chunk) of
+ undefined ->
+ hard_pos_bof(Fd,Pos);
+ Chunk ->
+ ChunkSize = byte_size(Chunk),
+ ChunkEnd = OldPos+ChunkSize,
+ if Pos=<ChunkEnd ->
+ Diff = Pos-OldPos,
+ put(pos,Pos),
+ put(chunk,binary:part(Chunk,Diff,ChunkEnd-Pos));
+ true ->
+ hard_pos_bof(Fd,Pos)
+ end
+ end;
+ _ ->
+ hard_pos_bof(Fd,Pos)
+ end.
+
+hard_pos_bof(Fd,Pos) ->
reset_chunk(),
- file:position(Fd,{bof,Pos}).
+ file:position(Fd,{bof,Pos}).
+
get_chunk(Fd) ->
case erase(chunk) of
@@ -979,7 +1097,9 @@ initial_menu() ->
[menu_item(0, {"./general_info","General information"},0),
menu_item(0, {"./processes","Processes"}, 0),
menu_item(0, {"./ports","Ports"}, 0),
- menu_item(0, {"./ets_tables","ETS tables"}, 0),
+ menu_item(2, "ETS tables", 0),
+ menu_item(0, {"./ets_tables","ETS tables"}, 1),
+ menu_item(0, {"./internal_ets_tables","Internal ETS tables"}, 1),
menu_item(0, {"./timers","Timers"}, 0),
menu_item(0, {"./fun_table","Fun table"}, 0),
menu_item(0, {"./atoms","Atoms"}, 0),
@@ -1066,9 +1186,9 @@ read_file(File) ->
{ok,<<$=:8,TagAndRest/binary>>} ->
{Tag,Id,Rest,N1} = tag(Fd,TagAndRest,1),
case Tag of
- "=erl_crash_dump" ->
- ets:delete_all_objects(cdv_dump_index_table),
- ets:insert(cdv_dump_index_table,{Tag,Id,N1+1}),
+ ?erl_crash_dump ->
+ reset_index_table(),
+ insert_index(Tag,Id,N1+1),
put(last_tag,{Tag,""}),
Status = background_status(processing,File),
background_status(Status),
@@ -1107,34 +1227,35 @@ read_file(File) ->
background_done({R,undefined,undefined})
end.
-indexify(Fd,<<"\n=",TagAndRest/binary>>,N) ->
- {Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+2),
- ets:insert(cdv_dump_index_table,{Tag,Id,N1+1}), % +1 to get past newline
- put(last_tag,{Tag,Id}),
- indexify(Fd,Rest,N1);
-indexify(Fd,<<>>,N) ->
- case read(Fd) of
- {ok,Chunk} when is_binary(Chunk) ->
- indexify(Fd,Chunk,N);
- eof ->
- eof
- end;
-indexify(Fd,<<$\n>>,N) ->
- %% This clause is needed in case the chunk ends with a newline and
- %% the next chunk starts with a tag (i.e. "\n=....")
- case read(Fd) of
- {ok,Chunk} when is_binary(Chunk) ->
- indexify(Fd,<<$\n,Chunk/binary>>,N);
- eof ->
- eof
- end;
-indexify(Fd,<<_Char:8,Rest/binary>>,N) ->
- indexify(Fd,Rest,N+1).
+indexify(Fd,Bin,N) ->
+ case binary:match(Bin,<<"\n=">>) of
+ {Start,Len} ->
+ Pos = Start+Len,
+ <<_:Pos/binary,TagAndRest/binary>> = Bin,
+ {Tag,Id,Rest,N1} = tag(Fd,TagAndRest,N+Pos),
+ insert_index(Tag,Id,N1+1), % +1 to get past newline
+ put(last_tag,{Tag,Id}),
+ indexify(Fd,Rest,N1);
+ nomatch ->
+ case read(Fd) of
+ {ok,Chunk0} when is_binary(Chunk0) ->
+ {Chunk,N1} =
+ case binary:last(Bin) of
+ $\n ->
+ {<<$\n,Chunk0/binary>>,N+byte_size(Bin)-1};
+ _ ->
+ {Chunk0,N+byte_size(Bin)}
+ end,
+ indexify(Fd,Chunk,N1);
+ eof ->
+ eof
+ end
+ end.
tag(Fd,Bin,N) ->
tag(Fd,Bin,N,[],[],tag).
tag(_Fd,<<$\n:8,_/binary>>=Rest,N,Gat,Di,_Now) ->
- {[$=|lists:reverse(Gat)],lists:reverse(Di),Rest,N};
+ {tag_to_atom(lists:reverse(Gat)),lists:reverse(Di),Rest,N};
tag(Fd,<<$\r:8,Rest/binary>>,N,Gat,Di,Now) ->
tag(Fd,Rest,N+1,Gat,Di,Now);
tag(Fd,<<$::8,IdAndRest/binary>>,N,Gat,Di,tag) ->
@@ -1148,12 +1269,12 @@ tag(Fd,<<>>,N,Gat,Di,Now) ->
{ok,Chunk} when is_binary(Chunk) ->
tag(Fd,Chunk,N,Gat,Di,Now);
eof ->
- {[$=|lists:reverse(Gat)],lists:reverse(Di),<<>>,N}
+ {tag_to_atom(lists:reverse(Gat)),lists:reverse(Di),<<>>,N}
end.
check_if_truncated() ->
case get(last_tag) of
- {"=end",_} ->
+ {?ende,_} ->
put(truncated,false),
put(truncated_proc,false);
TruncatedTag ->
@@ -1161,32 +1282,29 @@ check_if_truncated() ->
find_truncated_proc(TruncatedTag)
end.
-find_truncated_proc({"=atom",_Id}) ->
+find_truncated_proc({?atoms,_Id}) ->
put(truncated_proc,false);
find_truncated_proc({Tag,Pid}) ->
case is_proc_tag(Tag) of
true ->
put(truncated_proc,Pid);
false ->
- %% This means that the dump is truncated between "=proc" and
- %% "=proc_heap" => memory info is missing for all procs.
+ %% This means that the dump is truncated between ?proc and
+ %% ?proc_heap => memory info is missing for all procs.
put(truncated_proc,"<0.0.0>")
end.
-is_proc_tag(Tag) when Tag=="=proc";
- Tag=="=proc_dictionary";
- Tag=="=proc_messages";
- Tag=="=proc_dictionary";
- Tag=="=debug_proc_dictionary";
- Tag=="=proc_stack";
- Tag=="=proc_heap" ->
+is_proc_tag(Tag) when Tag==?proc;
+ Tag==?proc_dictionary;
+ Tag==?proc_messages;
+ Tag==?proc_dictionary;
+ Tag==?debug_proc_dictionary;
+ Tag==?proc_stack;
+ Tag==?proc_heap ->
true;
is_proc_tag(_) ->
false.
-related_tags("Atoms") ->
- ["=atoms","=num_atoms"].
-
%%% Inform the crashdump_viewer_server that a background job is completed.
background_done(Result) ->
Dict = get(),
@@ -1198,8 +1316,7 @@ background_status(Status) ->
%%%-----------------------------------------------------------------
%%% Functions for reading information from the dump
general_info(File) ->
- [{"=erl_crash_dump",_Id,Start}] =
- ets:lookup(cdv_dump_index_table,"=erl_crash_dump"),
+ [{_Id,Start}] = lookup_index(?erl_crash_dump),
Fd = open(File),
pos_bof(Fd,Start),
Created = case get_rest_of_line(Fd) of
@@ -1207,15 +1324,15 @@ general_info(File) ->
WholeLine -> WholeLine
end,
- GI0 = get_general_info(Fd,#general_info{created=Created,_=?space}),
+ GI0 = get_general_info(Fd,#general_info{created=Created}),
GI = case GI0#general_info.num_atoms of
?space -> GI0#general_info{num_atoms=get_num_atoms(Fd)};
_ -> GI0
end,
{MemTot,MemMax} =
- case ets:lookup(cdv_dump_index_table,"=memory") of
- [{"=memory",_,MemStart}] ->
+ case lookup_index(?memory) of
+ [{_,MemStart}] ->
pos_bof(Fd,MemStart),
Memory = get_meminfo(Fd,[]),
Tot = case lists:keysearch("total",1,Memory) of
@@ -1232,33 +1349,34 @@ general_info(File) ->
end,
close(Fd),
- {NumProcs,NumEts,NumFuns} = count(),
+ {NumProcs,NumEts,NumFuns,NumTimers} = count(),
NodeName =
- case ets:lookup(cdv_dump_index_table,"=node") of
- [{"=node",N,_Start}] ->
+ case lookup_index(?node) of
+ [{N,_Start}] ->
N;
[] ->
- case ets:lookup(cdv_dump_index_table,"=no_distribution") of
+ case lookup_index(?no_distribution) of
[_] -> "nonode@nohost";
[] -> "unknown"
end
end,
InstrInfo =
- case ets:member(cdv_dump_index_table,"=old_instr_data") of
- true ->
- old_instr_data;
- false ->
- case ets:member(cdv_dump_index_table,"=instr_data") of
- true ->
- instr_data;
- false ->
- false
- end
+ case lookup_index(?old_instr_data) of
+ [] ->
+ case lookup_index(?instr_data) of
+ [] ->
+ false;
+ _ ->
+ instr_data
+ end;
+ _ ->
+ old_instr_data
end,
GI#general_info{node_name=NodeName,
num_procs=integer_to_list(NumProcs),
num_ets=integer_to_list(NumEts),
+ num_timers=integer_to_list(NumTimers),
num_fun=integer_to_list(NumFuns),
mem_tot=MemTot,
mem_max=MemMax,
@@ -1285,8 +1403,8 @@ get_general_info(Fd,GenInfo) ->
end.
get_num_atoms(Fd) ->
- case ets:match(cdv_dump_index_table,{"=hash_table","atom_tab",'$1'}) of
- [[Pos]] ->
+ case lookup_index(?hash_table,"atom_tab") of
+ [{_,Pos}] ->
pos_bof(Fd,Pos),
skip_rest_of_line(Fd), % size
skip_rest_of_line(Fd), % used
@@ -1300,10 +1418,10 @@ get_num_atoms(Fd) ->
get_num_atoms2()
end.
get_num_atoms2() ->
- case ets:lookup(cdv_dump_index_table,"=num_atoms") of
+ case lookup_index(?num_atoms) of
[] ->
?space;
- [{"=num_atoms",NA,_Pos}] ->
+ [{NA,_Pos}] ->
%% If dump is translated this will exist
case get(truncated) of
true ->
@@ -1314,43 +1432,70 @@ get_num_atoms2() ->
end.
count() ->
- {ets:select_count(cdv_dump_index_table,count_ms("=proc")),
- ets:select_count(cdv_dump_index_table,count_ms("=ets")),
- ets:select_count(cdv_dump_index_table,count_ms("=fun"))}.
+ {count_index(?proc),count_index(?ets),count_index(?fu),count_index(?timer)}.
-count_ms(Tag) ->
- [{{Tag,'_','_'},[],[true]}].
+%%-----------------------------------------------------------------
+%% Page with all processes
+%%
+%% If there are less than ?max_sort_process_num processes in the dump,
+%% we will store the list of processes in the server state in order to
+%% allow sorting according to the different columns of the
+%% table. Since ?max_sort_process_num=:=?items_chunk_size, there will
+%% never be more than one chunk in this case.
+%%
+%% If there are more than ?max_sort_process_num processes in the dump,
+%% no sorting will be allowed, and the processes must be read (chunk
+%% by chunk) from the file each time the page is opened. This is to
+%% avoid really big data in the server state.
+procs_summary(SessionId,TW,_,State=#state{procs_summary=too_many}) ->
+ chunk_page(SessionId,State#state.file,TW,?proc,processes,
+ {no_sort,State#state.shared_heap},procs_summary_parsefun()),
+ State;
+procs_summary(SessionId,TW,SortOn,State) ->
+ ProcsSummary =
+ case State#state.procs_summary of
+ undefined -> % first time - read from file
+ Fd = open(State#state.file),
+ {PS,_}=lookup_and_parse_index_chunk(first_chunk_pointer(?proc),
+ Fd,procs_summary_parsefun()),
+ close(Fd),
+ PS;
+ PS ->
+ PS
+ end,
+ {SortedPS,NewSorted} = do_sort_procs(SortOn,ProcsSummary,State#state.sorted),
+ HtmlInfo =
+ crashdump_viewer_html:chunk_page(processes,SessionId,TW,
+ {SortOn,State#state.shared_heap},
+ SortedPS),
+ crashdump_viewer_html:chunk(SessionId,done,HtmlInfo),
+ State#state{procs_summary=ProcsSummary,sorted=NewSorted}.
-procs_summary(File) ->
- AllProcs = ets:lookup(cdv_dump_index_table,"=proc"),
- Fd = open(File),
- R = lists:map(fun({"=proc",Pid,Start}) ->
- pos_bof(Fd,Start),
- get_procinfo(Fd,fun main_procinfo/4,
- ?initial_proc_record(Pid))
- end,
- AllProcs),
- close(Fd),
- R.
+procs_summary_parsefun() ->
+ fun(Fd,Pid) ->
+ get_procinfo(Fd,fun main_procinfo/4,#proc{pid=Pid})
+ end.
+%%-----------------------------------------------------------------
+%% Page with one process
get_proc_details(File,Pid) ->
- DumpVsn = ets:lookup_element(cdv_dump_index_table,"=erl_crash_dump",2),
- case ets:match(cdv_dump_index_table,{"=proc",Pid,'$1'}) of
- [[Start]] ->
+ [{DumpVsn,_}] = lookup_index(?erl_crash_dump),
+ case lookup_index(?proc,Pid) of
+ [{_,Start}] ->
Fd = open(File),
pos_bof(Fd,Start),
Proc0 =
case DumpVsn of
"0.0" ->
%% Old version (translated)
- ?initial_proc_record(Pid);
+ #proc{pid=Pid};
_ ->
- (?initial_proc_record(Pid))#proc{
- stack_dump=if_exist("=proc_stack",Pid),
- msg_q=if_exist("=proc_messages",Pid),
- dict=if_exist("=proc_dictionary",Pid),
- debug_dict=if_exist("=debug_proc_dictionary",Pid)}
+ #proc{pid=Pid,
+ stack_dump=if_exist(?proc_stack,Pid),
+ msg_q=if_exist(?proc_messages,Pid),
+ dict=if_exist(?proc_dictionary,Pid),
+ debug_dict=if_exist(?debug_proc_dictionary,Pid)}
end,
Proc = get_procinfo(Fd,fun all_procinfo/4,Proc0),
close(Fd),
@@ -1368,11 +1513,11 @@ get_proc_details(File,Pid) ->
end.
if_exist(Tag,Key) ->
- case ets:select_count(cdv_dump_index_table,[{{Tag,Key,'_'},[],[true]}]) of
+ case count_index(Tag,Key) of
0 ->
Tag1 =
case is_proc_tag(Tag) of
- true -> "=proc";
+ true -> ?proc;
false -> Tag
end,
case truncated_here({Tag1,Key}) of
@@ -1523,13 +1668,14 @@ maybe_other_node(File,Id) ->
N
end,
Ms = ets:fun2ms(
- fun({Tag,Id,Start}) when Tag=:="=visible_node", Id=:=Channel ->
+ fun({{Tag,Start},Ch}) when Tag=:=?visible_node, Ch=:=Channel ->
{"Visible Node",Start};
- ({Tag,Id,Start}) when Tag=:="=hidden_node", Id=:=Channel ->
+ ({{Tag,Start},Ch}) when Tag=:=?hidden_node, Ch=:=Channel ->
{"Hidden Node",Start};
- ({Tag,Id,Start}) when Tag=:="=not_connected", Id=:=Channel ->
+ ({{Tag,Start},Ch}) when Tag=:=?not_connected, Ch=:=Channel ->
{"Not Connected Node",Start}
end),
+
case ets:select(cdv_dump_index_table,Ms) of
[] ->
not_found;
@@ -1540,6 +1686,7 @@ maybe_other_node(File,Id) ->
{other_node,Type,NodeInfo}
end.
+
expand_memory(File,What,Pid,Binaries) ->
Fd = open(File),
put(fd,Fd),
@@ -1548,8 +1695,8 @@ expand_memory(File,What,Pid,Binaries) ->
case What of
"StackDump" -> read_stack_dump(Fd,Pid,Dict);
"MsgQueue" -> read_messages(Fd,Pid,Dict);
- "Dictionary" -> read_dictionary(Fd,"=proc_dictionary",Pid,Dict);
- "DebugDictionary" -> read_dictionary(Fd,"=debug_proc_dictionary",Pid,Dict)
+ "Dictionary" -> read_dictionary(Fd,?proc_dictionary,Pid,Dict);
+ "DebugDictionary" -> read_dictionary(Fd,?debug_proc_dictionary,Pid,Dict)
end,
erase(fd),
close(Fd),
@@ -1559,10 +1706,10 @@ expand_memory(File,What,Pid,Binaries) ->
%%% Read binaries.
%%%
read_binaries(Fd) ->
- AllBinaries = ets:match(cdv_dump_index_table,{"=binary",'$1','$2'}),
+ AllBinaries = lookup_index(?binary),
read_binaries(Fd,AllBinaries, gb_trees:empty()).
-read_binaries(Fd,[[Addr0,Pos]|Bins],Dict0) ->
+read_binaries(Fd,[{Addr0,Pos}|Bins],Dict0) ->
pos_bof(Fd,Pos),
{Addr,_} = get_hex(Addr0),
Dict =
@@ -1603,15 +1750,15 @@ parse_binary(Addr, Line0, Dict) ->
%%%
read_stack_dump(Fd,Pid,Dict) ->
- case ets:match(cdv_dump_index_table,{"=proc_stack",Pid,'$1'}) of
- [[Start]] ->
+ case lookup_index(?proc_stack,Pid) of
+ [{_,Start}] ->
pos_bof(Fd,Start),
read_stack_dump1(Fd,Dict,[]);
[] ->
[]
end.
read_stack_dump1(Fd,Dict,Acc) ->
- %% This function is never called if the dump is truncated in "=proc_heap:Pid"
+ %% This function is never called if the dump is truncated in {?proc_heap,Pid}
case val(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
@@ -1631,15 +1778,15 @@ parse_top(Line0, D) ->
%%%
read_messages(Fd,Pid,Dict) ->
- case ets:match(cdv_dump_index_table,{"=proc_messages",Pid,'$1'}) of
- [[Start]] ->
+ case lookup_index(?proc_messages,Pid) of
+ [{_,Start}] ->
pos_bof(Fd,Start),
read_messages1(Fd,Dict,[]);
[] ->
[]
end.
read_messages1(Fd,Dict,Acc) ->
- %% This function is never called if the dump is truncated in "=proc_heap:Pid"
+ %% This function is never called if the dump is truncated in {?proc_heap,Pid}
case val(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
@@ -1659,15 +1806,15 @@ parse_message(Line0, D) ->
%%%
read_dictionary(Fd,Tag,Pid,Dict) ->
- case ets:match(cdv_dump_index_table,{Tag,Pid,'$1'}) of
- [[Start]] ->
+ case lookup_index(Tag,Pid) of
+ [{_,Start}] ->
pos_bof(Fd,Start),
read_dictionary1(Fd,Dict,[]);
[] ->
[]
end.
read_dictionary1(Fd,Dict,Acc) ->
- %% This function is never called if the dump is truncated in "=proc_heap:Pid"
+ %% This function is never called if the dump is truncated in {?proc_heap,Pid}
case val(Fd) of
"=" ++ _next_tag ->
lists:reverse(Acc);
@@ -1686,8 +1833,8 @@ parse_dictionary(Line0, D) ->
%%%
read_heap(Fd,Pid,Dict0) ->
- case ets:match(cdv_dump_index_table,{"=proc_heap",Pid,'$2'}) of
- [[Pos]] ->
+ case lookup_index(?proc_heap,Pid) of
+ [{_,Pos}] ->
pos_bof(Fd,Pos),
read_heap(Dict0);
[] ->
@@ -1695,7 +1842,7 @@ read_heap(Fd,Pid,Dict0) ->
end.
read_heap(Dict0) ->
- %% This function is never called if the dump is truncated in "=proc_heap:Pid"
+ %% This function is never called if the dump is truncated in {?proc_heap,Pid}
case get(fd) of
end_of_heap ->
Dict0;
@@ -1761,12 +1908,14 @@ do_sort_procs("name",Procs,Sorted) ->
_ -> {Result,"name"}
end.
-
+%%-----------------------------------------------------------------
+%% Page with one port
get_port(File,Port) ->
- case ets:match(cdv_dump_index_table,{"=port",Port,'$1'}) of
- [[Start]] ->
+ case lookup_index(?port,Port) of
+ [{_,Start}] ->
Fd = open(File),
- R = get_portinfo(Fd,Port,Start),
+ pos_bof(Fd,Start),
+ R = get_portinfo(Fd,#port{id=Port}),
close(Fd),
{ok,R};
[] ->
@@ -1781,18 +1930,11 @@ get_port(File,Port) ->
end
end.
-get_ports(File) ->
- Ports = ets:lookup(cdv_dump_index_table,"=port"),
- Fd = open(File),
- R = lists:map(fun({"=port",Id,Start}) -> get_portinfo(Fd,Id,Start) end,
- Ports),
- close(Fd),
- R.
-
-
-get_portinfo(Fd,Id,Start) ->
- pos_bof(Fd,Start),
- get_portinfo(Fd,#port{id=Id,_=?space}).
+%%-----------------------------------------------------------------
+%% Page with all ports
+get_ports(SessionId,File,TW) ->
+ ParseFun = fun(Fd,Id) -> get_portinfo(Fd,#port{id=Id}) end,
+ chunk_page(SessionId,File,TW,?port,ports,[],ParseFun).
get_portinfo(Fd,Port) ->
case line_head(Fd) of
@@ -1802,6 +1944,10 @@ get_portinfo(Fd,Port) ->
get_portinfo(Fd,Port#port{connected=val(Fd)});
"Links" ->
get_portinfo(Fd,Port#port{links=val(Fd)});
+ "Registered as" ->
+ get_portinfo(Fd,Port#port{name=val(Fd)});
+ "Monitors" ->
+ get_portinfo(Fd,Port#port{monitors=val(Fd)});
"Port controls linked-in driver" ->
get_portinfo(Fd,Port#port{controls=["Linked in driver: " |
val(Fd)]});
@@ -1820,30 +1966,12 @@ get_portinfo(Fd,Port) ->
Port
end.
-get_ets_tables(File,Pid,WS) ->
- EtsTables = ets:match_object(cdv_dump_index_table,{"=ets",Pid,'_'}),
- Fd = open(File),
- R = lists:map(fun({"=ets",P,Start}) ->
- get_etsinfo(Fd,P,Start,WS)
- end,
- EtsTables),
- close(Fd),
- R.
-get_internal_ets_tables(File,WS) ->
- InternalEts = ets:match_object(cdv_dump_index_table,
- {"=internal_ets",'_','_'}),
- Fd = open(File),
- R = lists:map(fun({"=internal_ets",Descr,Start}) ->
- {Descr,get_etsinfo(Fd,undefined,Start,WS)}
- end,
- InternalEts),
- close(Fd),
- R.
-
-get_etsinfo(Fd,Pid,Start,WS) ->
- pos_bof(Fd,Start),
- get_etsinfo(Fd,#ets_table{pid=Pid,type="hash",_=?space},WS).
+%%-----------------------------------------------------------------
+%% Page with external ets tables
+get_ets_tables(SessionId,File,Heading,TW,Pid,WS) ->
+ ParseFun = fun(Fd,Id) -> get_etsinfo(Fd,#ets_table{pid=Id},WS) end,
+ chunk_page(SessionId,File,TW,{?ets,Pid},ets_tables,Heading,ParseFun).
get_etsinfo(Fd,EtsTable,WS) ->
case line_head(Fd) of
@@ -1875,26 +2003,32 @@ get_etsinfo(Fd,EtsTable,WS) ->
EtsTable
end.
-get_timers(File,Pid) ->
- Timers = ets:match_object(cdv_dump_index_table,{"=timer",Pid,'$1'}),
+
+%% Internal ets table page
+get_internal_ets_tables(File,WS) ->
+ InternalEts = lookup_index(?internal_ets),
Fd = open(File),
- R = lists:map(fun({"=timer",P,Start}) ->
- get_timerinfo(Fd,P,Start)
- end,
- Timers),
+ R = lists:map(
+ fun({Descr,Start}) ->
+ pos_bof(Fd,Start),
+ {Descr,get_etsinfo(Fd,#ets_table{},WS)}
+ end,
+ InternalEts),
close(Fd),
R.
-get_timerinfo(Fd,Pid,Start) ->
- pos_bof(Fd,Start),
- get_timerinfo(Fd,#timer{pid=Pid,_=?space}).
+%%-----------------------------------------------------------------
+%% Page with list of all timers
+get_timers(SessionId,File,Heading,TW,Pid) ->
+ ParseFun = fun(Fd,Id) -> get_timerinfo_1(Fd,#timer{pid=Id}) end,
+ chunk_page(SessionId,File,TW,{?timer,Pid},timers,Heading,ParseFun).
-get_timerinfo(Fd,Timer) ->
+get_timerinfo_1(Fd,Timer) ->
case line_head(Fd) of
"Message" ->
- get_timerinfo(Fd,Timer#timer{msg=val(Fd)});
+ get_timerinfo_1(Fd,Timer#timer{msg=val(Fd)});
"Time left" ->
- get_timerinfo(Fd,Timer#timer{time=val(Fd)});
+ get_timerinfo_1(Fd,Timer#timer{time=val(Fd)});
"=" ++ _next_tag ->
Timer;
Other ->
@@ -1902,25 +2036,27 @@ get_timerinfo(Fd,Timer) ->
Timer
end.
+%%-----------------------------------------------------------------
+%% Page with information about the erlang distribution
nods(File) ->
- case ets:lookup(cdv_dump_index_table,"=no_distribution") of
+ case lookup_index(?no_distribution) of
[] ->
- V = ets:lookup(cdv_dump_index_table,"=visible_node"),
- H = ets:lookup(cdv_dump_index_table,"=hidden_node"),
- N = ets:lookup(cdv_dump_index_table,"=not_connected"),
+ V = lookup_index(?visible_node),
+ H = lookup_index(?hidden_node),
+ N = lookup_index(?not_connected),
Fd = open(File),
Visible = lists:map(
- fun({"=visible_node",Channel,Start}) ->
+ fun({Channel,Start}) ->
get_nodeinfo(Fd,Channel,Start)
end,
V),
Hidden = lists:map(
- fun({"=hidden_node",Channel,Start}) ->
+ fun({Channel,Start}) ->
get_nodeinfo(Fd,Channel,Start)
end,
H),
NotConnected = lists:map(
- fun({"=not_connected",Channel,Start}) ->
+ fun({Channel,Start}) ->
get_nodeinfo(Fd,Channel,Start)
end,
N),
@@ -1932,7 +2068,7 @@ nods(File) ->
get_nodeinfo(Fd,Channel,Start) ->
pos_bof(Fd,Start),
- get_nodeinfo(Fd,#nod{channel=Channel,_=?space}).
+ get_nodeinfo(Fd,#nod{channel=Channel}).
get_nodeinfo(Fd,Nod) ->
case line_head(Fd) of
@@ -1963,26 +2099,37 @@ get_nodeinfo(Fd,Nod) ->
Nod
end.
-loaded_mods(File) ->
- case ets:lookup(cdv_dump_index_table,"=loaded_modules") of
- [{"=loaded_modules",_,StartTotal}] ->
- Fd = open(File),
- pos_bof(Fd,StartTotal),
- {CC,OC} = get_loaded_mod_totals(Fd,{"unknown","unknown"}),
-
- Mods = ets:lookup(cdv_dump_index_table,"=mod"),
- LM = lists:map(fun({"=mod",M,Start}) ->
- pos_bof(Fd,Start),
- InitLM = #loaded_mod{mod=M,_=?space},
- get_loaded_mod_info(Fd,InitLM,
- fun main_modinfo/3)
- end,
- Mods),
- close(Fd),
- {CC,OC,LM};
- [] ->
- {"unknown","unknown",[]}
- end.
+%%-----------------------------------------------------------------
+%% Page with details about one loaded modules
+get_loaded_mod_details(File,Mod) ->
+ [{_,Start}] = lookup_index(?mod,Mod),
+ Fd = open(File),
+ pos_bof(Fd,Start),
+ InitLM = #loaded_mod{mod=Mod,old_size="No old code exists"},
+ ModInfo = get_loaded_mod_info(Fd,InitLM,fun all_modinfo/3),
+ close(Fd),
+ ModInfo.
+
+%%-----------------------------------------------------------------
+%% Page with list of all loaded modules
+loaded_mods(SessionId,File,TW) ->
+ ParseFun =
+ fun(Fd,Id) ->
+ get_loaded_mod_info(Fd,#loaded_mod{mod=Id},
+ fun main_modinfo/3)
+ end,
+ {CC,OC} =
+ case lookup_index(?loaded_modules) of
+ [{_,StartTotal}] ->
+ Fd = open(File),
+ pos_bof(Fd,StartTotal),
+ R = get_loaded_mod_totals(Fd,{"unknown","unknown"}),
+ close(Fd),
+ R;
+ [] ->
+ {"unknown","unknown"}
+ end,
+ chunk_page(SessionId,File,TW,?mod,loaded_mods,{CC,OC},ParseFun).
get_loaded_mod_totals(Fd,{CC,OC}) ->
case line_head(Fd) of
@@ -1997,16 +2144,6 @@ get_loaded_mod_totals(Fd,{CC,OC}) ->
{CC,OC} % truncated file
end.
-get_loaded_mod_details(File,Mod) ->
- [[Start]] = ets:match(cdv_dump_index_table,{"=mod",Mod,'$1'}),
- Fd = open(File),
- pos_bof(Fd,Start),
- InitLM = #loaded_mod{mod=Mod,old_size="No old code exists",
- _="No information available"},
- ModInfo = get_loaded_mod_info(Fd,InitLM,fun all_modinfo/3),
- close(Fd),
- ModInfo.
-
get_loaded_mod_info(Fd,LM,Fun) ->
case line_head(Fd) of
"Current size" ->
@@ -2073,39 +2210,26 @@ hex_to_dec("A") -> 10;
hex_to_dec(N) -> list_to_integer(N).
+%%-----------------------------------------------------------------
+%% Page with list of all funs
+funs(SessionId,File,TW) ->
+ ParseFun = fun(Fd,_Id) -> get_funinfo(Fd,#fu{}) end,
+ chunk_page(SessionId,File,TW,?fu,funs,[],ParseFun).
-funs(File) ->
- case ets:lookup(cdv_dump_index_table,"=fun") of
- [] ->
- [];
- AllFuns ->
- Fd = open(File),
- R = lists:map(fun({"=fun",_,Start}) ->
- get_funinfo(Fd,Start)
- end,
- AllFuns),
- close(Fd),
- R
- end.
-
-get_funinfo(Fd,Start) ->
- pos_bof(Fd,Start),
- get_funinfo1(Fd,#fu{_=?space}).
-
-get_funinfo1(Fd,Fu) ->
+get_funinfo(Fd,Fu) ->
case line_head(Fd) of
"Module" ->
- get_funinfo1(Fd,Fu#fu{module=val(Fd)});
+ get_funinfo(Fd,Fu#fu{module=val(Fd)});
"Uniq" ->
- get_funinfo1(Fd,Fu#fu{uniq=val(Fd)});
+ get_funinfo(Fd,Fu#fu{uniq=val(Fd)});
"Index" ->
- get_funinfo1(Fd,Fu#fu{index=val(Fd)});
+ get_funinfo(Fd,Fu#fu{index=val(Fd)});
"Address" ->
- get_funinfo1(Fd,Fu#fu{address=val(Fd)});
+ get_funinfo(Fd,Fu#fu{address=val(Fd)});
"Native_address" ->
- get_funinfo1(Fd,Fu#fu{native_address=val(Fd)});
+ get_funinfo(Fd,Fu#fu{native_address=val(Fd)});
"Refc" ->
- get_funinfo1(Fd,Fu#fu{refc=val(Fd)});
+ get_funinfo(Fd,Fu#fu{refc=val(Fd)});
"=" ++ _next_tag ->
Fu;
Other ->
@@ -2113,28 +2237,53 @@ get_funinfo1(Fd,Fu) ->
Fu
end.
-atoms(File) ->
- case ets:lookup(cdv_dump_index_table,"=atoms") of
- [{_atoms,_Id,Start}] ->
+%%-----------------------------------------------------------------
+%% Page with list of all atoms
+atoms(SessionId,File,TW,Num) ->
+ case lookup_index(?atoms) of
+ [{_Id,Start}] ->
Fd = open(File),
pos_bof(Fd,Start),
- R = case get_n_lines_of_tag(Fd,100) of
- {all,N,Lines} ->
- {n_lines,1,N,"Atoms",Lines};
- {part,100,Lines} ->
- {n_lines,1,100,"Atoms",Lines,get(pos)};
- empty ->
- []
- end,
- close(Fd),
- R;
+ case get_atoms(Fd,?items_chunk_size) of
+ {Atoms,Cont} ->
+ crashdump_viewer_html:atoms(SessionId,TW,Num,Atoms),
+ atoms_chunks(Fd,SessionId,Cont);
+ done ->
+ crashdump_viewer_html:atoms(SessionId,TW,Num,done)
+ end;
_ ->
- []
+ crashdump_viewer_html:atoms(SessionId,TW,Num,done)
+ end.
+
+get_atoms(Fd,Number) ->
+ case get_n_lines_of_tag(Fd,Number) of
+ {all,_,Lines} ->
+ close(Fd),
+ {Lines,done};
+ {part,_,Lines} ->
+ {Lines,Number};
+ empty ->
+ close(Fd),
+ done
end.
+atoms_chunks(_Fd,SessionId,done) ->
+ crashdump_viewer_html:atoms_chunk(SessionId,done);
+atoms_chunks(Fd,SessionId,Number) ->
+ case get_atoms(Fd,Number) of
+ {Atoms,Cont} ->
+ crashdump_viewer_html:atoms_chunk(SessionId,Atoms),
+ atoms_chunks(Fd,SessionId,Cont);
+ done ->
+ atoms_chunks(Fd,SessionId,done)
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Page with memory information
memory(File) ->
- case ets:lookup(cdv_dump_index_table,"=memory") of
- [{"=memory",_,Start}] ->
+ case lookup_index(?memory) of
+ [{_,Start}] ->
Fd = open(File),
pos_bof(Fd,Start),
R = get_meminfo(Fd,[]),
@@ -2153,10 +2302,12 @@ get_meminfo(Fd,Acc) ->
Key ->
get_meminfo(Fd,[{Key,val(Fd)}|Acc])
end.
-
+
+%%-----------------------------------------------------------------
+%% Page with information about allocated areas
allocated_areas(File) ->
- case ets:lookup(cdv_dump_index_table,"=allocated_areas") of
- [{"=allocated_areas",_,Start}] ->
+ case lookup_index(?allocated_areas) of
+ [{_,Start}] ->
Fd = open(File),
pos_bof(Fd,Start),
R = get_allocareainfo(Fd,[]),
@@ -2183,14 +2334,16 @@ get_allocareainfo(Fd,Acc) ->
end,
get_allocareainfo(Fd,[AllocInfo|Acc])
end.
-
+
+%%-----------------------------------------------------------------
+%% Page with information about allocators
allocator_info(File) ->
- case ets:lookup(cdv_dump_index_table,"=allocator") of
+ case lookup_index(?allocator) of
[] ->
[];
AllAllocators ->
Fd = open(File),
- R = lists:map(fun({"=allocator",Heading,Start}) ->
+ R = lists:map(fun({Heading,Start}) ->
{Heading,get_allocatorinfo(Fd,Start)}
end,
AllAllocators),
@@ -2220,14 +2373,15 @@ get_all_vals([],Acc) ->
get_all_vals([Char|Rest],Acc) ->
get_all_vals(Rest,[Char|Acc]).
-
+%%-----------------------------------------------------------------
+%% Page with hash table information
hash_tables(File) ->
- case ets:lookup(cdv_dump_index_table,"=hash_table") of
+ case lookup_index(?hash_table) of
[] ->
[];
AllHashTables ->
Fd = open(File),
- R = lists:map(fun({"=hash_table",Name,Start}) ->
+ R = lists:map(fun({Name,Start}) ->
get_hashtableinfo(Fd,Name,Start)
end,
AllHashTables),
@@ -2237,7 +2391,7 @@ hash_tables(File) ->
get_hashtableinfo(Fd,Name,Start) ->
pos_bof(Fd,Start),
- get_hashtableinfo1(Fd,#hash_table{name=Name,_=?space}).
+ get_hashtableinfo1(Fd,#hash_table{name=Name}).
get_hashtableinfo1(Fd,HashTable) ->
case line_head(Fd) of
@@ -2256,13 +2410,15 @@ get_hashtableinfo1(Fd,HashTable) ->
HashTable
end.
+%%-----------------------------------------------------------------
+%% Page with index table information
index_tables(File) ->
- case ets:lookup(cdv_dump_index_table,"=index_table") of
+ case lookup_index(?index_table) of
[] ->
[];
AllIndexTables ->
Fd = open(File),
- R = lists:map(fun({"=index_table",Name,Start}) ->
+ R = lists:map(fun({Name,Start}) ->
get_indextableinfo(Fd,Name,Start)
end,
AllIndexTables),
@@ -2272,7 +2428,7 @@ index_tables(File) ->
get_indextableinfo(Fd,Name,Start) ->
pos_bof(Fd,Start),
- get_indextableinfo1(Fd,#index_table{name=Name,_=?space}).
+ get_indextableinfo1(Fd,#index_table{name=Name}).
get_indextableinfo1(Fd,IndexTable) ->
case line_head(Fd) of
@@ -2284,6 +2440,8 @@ get_indextableinfo1(Fd,IndexTable) ->
get_indextableinfo1(Fd,IndexTable#index_table{limit=val(Fd)});
"rate" ->
get_indextableinfo1(Fd,IndexTable#index_table{rate=val(Fd)});
+ "entries" ->
+ get_indextableinfo1(Fd,IndexTable#index_table{entries=val(Fd)});
"=" ++ _next_tag ->
IndexTable;
Other ->
@@ -2295,6 +2453,8 @@ get_indextableinfo1(Fd,IndexTable) ->
+%%-----------------------------------------------------------------
+%% Expand a set of data which was shown in a truncated form on
get_expanded(File,Pos,Size) ->
Fd = open(File),
R = case file:pread(Fd,Pos,Size) of
@@ -2307,20 +2467,6 @@ get_expanded(File,Pos,Size) ->
R.
-get_next(File,Pos,N0,Start,What) ->
- Fd = open(File),
- pos_bof(Fd,Pos),
- R = case get_n_lines_of_tag(Fd,N0) of
- {all,N,Lines} ->
- {n_lines,Start,N,What,Lines};
- {part,N,Lines} ->
- {n_lines,Start,N,What,Lines,get(pos)}
- end,
- close(Fd),
- R.
-
-
-
replace_all(From,To,[From|Rest],Acc) ->
replace_all(From,To,Rest,[To|Acc]);
replace_all(From,To,[Char|Rest],Acc) ->
@@ -2567,3 +2713,110 @@ get_binary(_N, [], _Acc) ->
cdvbin(Sz,Pos) ->
"#CDVBin<"++integer_to_list(Sz)++","++integer_to_list(Pos)++">".
+
+
+%%-----------------------------------------------------------------
+%% Functions for accessing the cdv_dump_index_table
+reset_index_table() ->
+ ets:delete_all_objects(cdv_dump_index_table).
+
+insert_index(Tag,Id,Pos) ->
+ ets:insert(cdv_dump_index_table,{{Tag,Pos},Id}).
+
+lookup_index(Tag) ->
+ lookup_index(Tag,'$2').
+lookup_index(Tag,Id) ->
+ ets:select(cdv_dump_index_table,[{{{Tag,'$1'},Id},[],[{{Id,'$1'}}]}]).
+
+lookup_index_chunk({'#CDVFirstChunk',Tag,Id}) ->
+ ets:select(cdv_dump_index_table,
+ [{{{Tag,'$1'},Id},[],[{{Id,'$1'}}]}],
+ ?items_chunk_size);
+lookup_index_chunk(Cont) ->
+ ets:select(Cont).
+
+%% Create a tag which can be used instead of an ets Continuation for
+%% the first call to lookup_index_chunk.
+first_chunk_pointer({Tag,Id}) ->
+ {'#CDVFirstChunk',Tag,Id};
+first_chunk_pointer(Tag) ->
+ first_chunk_pointer({Tag,'$2'}).
+
+count_index(Tag) ->
+ ets:select_count(cdv_dump_index_table,[{{{Tag,'_'},'_'},[],[true]}]).
+count_index(Tag,Id) ->
+ ets:select_count(cdv_dump_index_table,[{{{Tag,'_'},Id},[],[true]}]).
+
+
+%%-----------------------------------------------------------------
+%% Convert tags read from crashdump to atoms used as first part of key
+%% in cdv_dump_index_table
+tag_to_atom("allocated_areas") -> ?allocated_areas;
+tag_to_atom("allocator") -> ?allocator;
+tag_to_atom("atoms") -> ?atoms;
+tag_to_atom("binary") -> ?binary;
+tag_to_atom("debug_proc_dictionary") -> ?debug_proc_dictionary;
+tag_to_atom("end") -> ?ende;
+tag_to_atom("erl_crash_dump") -> ?erl_crash_dump;
+tag_to_atom("ets") -> ?ets;
+tag_to_atom("fun") -> ?fu;
+tag_to_atom("hash_table") -> ?hash_table;
+tag_to_atom("hidden_node") -> ?hidden_node;
+tag_to_atom("index_table") -> ?index_table;
+tag_to_atom("instr_data") -> ?instr_data;
+tag_to_atom("internal_ets") -> ?internal_ets;
+tag_to_atom("loaded_modules") -> ?loaded_modules;
+tag_to_atom("memory") -> ?memory;
+tag_to_atom("mod") -> ?mod;
+tag_to_atom("no_distribution") -> ?no_distribution;
+tag_to_atom("node") -> ?node;
+tag_to_atom("not_connected") -> ?not_connected;
+tag_to_atom("num_atoms") -> ?num_atoms;
+tag_to_atom("old_instr_data") -> ?old_instr_data;
+tag_to_atom("port") -> ?port;
+tag_to_atom("proc") -> ?proc;
+tag_to_atom("proc_dictionary") -> ?proc_dictionary;
+tag_to_atom("proc_heap") -> ?proc_heap;
+tag_to_atom("proc_messages") -> ?proc_messages;
+tag_to_atom("proc_stack") -> ?proc_stack;
+tag_to_atom("timer") -> ?timer;
+tag_to_atom("visible_node") -> ?visible_node;
+tag_to_atom(UnknownTag) ->
+ io:format("WARNING: Found unexpected tag:~s~n",[UnknownTag]),
+ list_to_atom(UnknownTag).
+
+%%%-----------------------------------------------------------------
+%%% Create a page by sending chunk by chunk to crashdump_viewer_html
+chunk_page(SessionId,File,TW,What,HtmlCB,HtmlExtra,ParseFun) ->
+ Fd = open(File),
+ case lookup_and_parse_index_chunk(first_chunk_pointer(What),Fd,ParseFun) of
+ done ->
+ crashdump_viewer_html:chunk_page(HtmlCB,SessionId,TW,HtmlExtra,done);
+ {Chunk,Cont} ->
+ HtmlInfo = crashdump_viewer_html:chunk_page(
+ HtmlCB,
+ SessionId,TW,HtmlExtra,Chunk),
+ chunk_page_1(Fd,HtmlInfo,SessionId,ParseFun,
+ lookup_and_parse_index_chunk(Cont,Fd,ParseFun))
+ end.
+
+chunk_page_1(_Fd,HtmlInfo,SessionId,_ParseFun,done) ->
+ crashdump_viewer_html:chunk(SessionId,done,HtmlInfo);
+chunk_page_1(Fd,HtmlInfo,SessionId,ParseFun,{Chunk,Cont}) ->
+ crashdump_viewer_html:chunk(SessionId,Chunk,HtmlInfo),
+ chunk_page_1(Fd,HtmlInfo,SessionId,ParseFun,
+ lookup_and_parse_index_chunk(Cont,Fd,ParseFun)).
+
+lookup_and_parse_index_chunk(Pointer,Fd,ParseFun) ->
+ case lookup_index_chunk(Pointer) of
+ '$end_of_table' ->
+ close(Fd),
+ done;
+ {Chunk,Cont} ->
+ R = lists:map(fun({Id,Start}) ->
+ pos_bof(Fd,Start),
+ ParseFun(Fd,Id)
+ end,
+ Chunk),
+ {R,Cont}
+ end.
diff --git a/lib/observer/src/crashdump_viewer.hrl b/lib/observer/src/crashdump_viewer.hrl
index 6ce727cd3e..466f33b63b 100644
--- a/lib/observer/src/crashdump_viewer.hrl
+++ b/lib/observer/src/crashdump_viewer.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,117 +17,136 @@
%% %CopyrightEnd%
%%
-define(space, "&nbsp;").
+-define(unknown, "unknown").
-record(menu_item,{index,picture,text,depth,children,state,target}).
-record(general_info,
{created,
- slogan,
- system_vsn,
- compile_time,
- taints,
- node_name,
- num_atoms,
- num_procs,
- num_ets,
- num_fun,
- mem_tot,
- mem_max,
- instr_info}).
+ slogan=?space,
+ system_vsn=?space,
+ compile_time=?space,
+ taints=?space,
+ node_name=?space,
+ num_atoms=?space,
+ num_procs=?space,
+ num_ets=?space,
+ num_timers=?space,
+ num_fun=?space,
+ mem_tot=?space,
+ mem_max=?space,
+ instr_info=?space}).
-record(proc,
+ %% Initial data according to the follwoing:
+ %%
+ %% msg_q_len, reds and stack_heap are integers because it must
+ %% be possible to sort on them. All other fields are strings
+ %%
+ %% for old dumps start_time, parent and number of heap frament
+ %% does not exist
+ %%
+ %% current_func can be both "current function" and
+ %% "last scheduled in for"
+ %%
+ %% stack_dump, message queue and dictionaries should only be
+ %% displayed as a link to "Expand" (if dump is from OTP R9B
+ %% or newer)
{pid,
- name,
- init_func,
- parent,
- start_time,
- state,
- current_func,
- msg_q_len,
- msg_q,
- last_calls,
- links,
- prog_count,
- cp,
- arity,
- dict,
- debug_dict,
- reds,
- num_heap_frag,
- heap_frag_data,
- stack_heap,
- old_heap,
- heap_unused,
- old_heap_unused,
- new_heap_start,
- new_heap_top,
- stack_top,
- stack_end,
- old_heap_start,
- old_heap_top,
- old_heap_end,
- stack_dump}).
+ name=?space,
+ init_func=?space,
+ parent=?unknown,
+ start_time=?unknown,
+ state=?space,
+ current_func={"Current Function",?space},
+ msg_q_len=0,
+ msg_q=?space,
+ last_calls=?space,
+ links=?space,
+ prog_count=?space,
+ cp=?space,
+ arity=?space,
+ dict=?space,
+ debug_dict=?space,
+ reds=0,
+ num_heap_frag=?unknown,
+ heap_frag_data=?space,
+ stack_heap=0,
+ old_heap=?space,
+ heap_unused=?space,
+ old_heap_unused=?space,
+ new_heap_start=?space,
+ new_heap_top=?space,
+ stack_top=?space,
+ stack_end=?space,
+ old_heap_start=?space,
+ old_heap_top=?space,
+ old_heap_end=?space,
+ stack_dump=?space}).
-record(port,
{id,
- slot,
- connected,
- links,
- controls}).
+ slot=?space,
+ connected=?space,
+ links=?space,
+ name=?space,
+ monitors=?space,
+ controls=?space}).
-record(ets_table,
{pid,
- slot,
- id,
- name,
- type,
- buckets,
- size,
- memory}).
+ slot=?space,
+ id=?space,
+ name=?space,
+ type="hash",
+ buckets=?space,
+ size=?space,
+ memory=?space}).
-record(timer,
{pid,
- msg,
- time}).
+ msg=?space,
+ time=?space}).
-record(fu,
- {module,
- uniq,
- index,
- address,
- native_address,
- refc}).
+ {module=?space,
+ uniq=?space,
+ index=?space,
+ address=?space,
+ native_address=?space,
+ refc=?space}).
-record(nod,
- {name,
+ {name=?space,
channel,
- controller,
- creation,
- remote_links,
- remote_mon,
- remote_mon_by,
- error}).
+ controller=?space,
+ creation=?space,
+ remote_links=?space,
+ remote_mon=?space,
+ remote_mon_by=?space,
+ error=?space}).
-record(loaded_mod,
{mod,
- current_size,
- current_attrib,
- current_comp_info,
- old_size,
- old_attrib,
- old_comp_info}).
+ current_size=?space,
+ current_attrib=?space,
+ current_comp_info=?space,
+ old_size=?space,
+ old_attrib=?space,
+ old_comp_info=?space}).
-record(hash_table,
{name,
- size,
- used,
- objs,
- depth}).
+ size=?space,
+ used=?space,
+ objs=?space,
+ depth=?space}).
-record(index_table,
{name,
- size,
- used,
- limit,
- rate}).
+ size=?space,
+ used=?space,
+ limit=?space,
+ rate=?space,
+ entries=?space}).
diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl
index 5e7bbf62a0..24a80b1916 100644
--- a/lib/observer/src/crashdump_viewer_html.erl
+++ b/lib/observer/src/crashdump_viewer_html.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,25 +32,23 @@
general_info/1,
pretty_info_page/2,
info_page/2,
- procs_summary/4,
proc_details/4,
expanded_memory/2,
expanded_binary/1,
- next/2,
- ports/3,
- timers/3,
- ets_tables/4,
+ port/3,
+ internal_ets_tables/2,
nods/2,
- loaded_mods/2,
loaded_mod_details/2,
- funs/2,
- atoms/3,
+ atoms/4,
+ atoms_chunk/2,
memory/2,
allocated_areas/2,
allocator_info/2,
hash_tables/2,
index_tables/2,
- error/2]).
+ error/2,
+ chunk_page/5,
+ chunk/3]).
-include("crashdump_viewer.hrl").
@@ -79,23 +77,20 @@ read_file_frame() ->
read_file_frame_body() ->
- Entry =
- case webtool:is_localhost() of
- true -> [input("TYPE=file NAME=browse SIZE=40"),
- input("TYPE=hidden NAME=path")];
- false -> input("TYPE=text NAME=path SIZE=60")
- end,
+ %% Using a plain text input field instead of a file input field
+ %% (e.g. <INPUT TYPE=file NAME=pathj SIZE=40">) because most
+ %% browsers can not forward the full path from this dialog even if
+ %% the browser is running on localhost (Ref 'fakepath'-problem)
+ Entry = input("TYPE=text NAME=path SIZE=60"),
Form =
form(
- "NAME=read_file_form METHOD=post ACTION= \"./read_file\"",
+ "NAME=read_file_form METHOD=post ACTION=\"./read_file\"",
table(
"BORDER=0",
[tr(td("COLSPAN=2","Enter file to analyse")),
tr(
[td(Entry),
- td("ALIGN=center",
- input("TYPE=submit onClick=\"path.value=browse.value;\""
- "VALUE=Ok"))])])),
+ td("ALIGN=center",input("TYPE=submit VALUE=Ok"))])])),
table(
"WIDTH=100% HEIGHT=60%",
tr("VALIGN=middle",
@@ -235,6 +230,8 @@ general_info_body(Heading,GenInfo) ->
td(GenInfo#general_info.num_procs)]),
tr([th("ALIGN=left BGCOLOR=\"#8899AA\"","ETS tables"),
td(GenInfo#general_info.num_ets)]),
+ tr([th("ALIGN=left BGCOLOR=\"#8899AA\"","Timers"),
+ td(GenInfo#general_info.num_timers)]),
tr([th("ALIGN=left BGCOLOR=\"#8899AA\"","Funs"),
td(GenInfo#general_info.num_fun)])]),
case GenInfo#general_info.instr_info of
@@ -295,60 +292,6 @@ pretty_info_body(Heading,Info) ->
pre(pretty_format(Info))].
%%%-----------------------------------------------------------------
-%%% Make table with summary of process information
-procs_summary(Sorted,ProcsSummary,TW,SharedHeap) ->
- Heading = "Process Information",
- header(Heading,
- body(
- procs_summary_body(Heading,ProcsSummary,TW,Sorted,SharedHeap))).
-
-procs_summary_body(Heading,[],TW,_Sorted,_SharedHeap) ->
- [h1(Heading),
- warn(TW),
- "No processes were found\n"];
-procs_summary_body(Heading,ProcsSummary,TW,Sorted,SharedHeap) ->
- MemHeading =
- if SharedHeap ->
- "Stack";
- true ->
- "Stack+heap"
- end,
-
- [heading(Heading,"processes"),
- warn(TW),
- table(
- "BORDER=4 CELLPADDING=4",
- [tr(
- [summary_table_head("pid","Pid",Sorted),
- summary_table_head("name_func","Name/Spawned as",Sorted),
- summary_table_head("state","State",Sorted),
- summary_table_head("reds","Reductions",Sorted),
- summary_table_head("mem",MemHeading,Sorted),
- summary_table_head("msg_q_len","MsgQ Length",Sorted)]) |
- lists:map(fun(Proc) -> procs_summary_table(Proc) end,ProcsSummary)])].
-
-summary_table_head(Sorted,Text,Sorted) ->
- %% Mark the sorted column (bigger and italic)
- th(font("SIZE=\"+1\"",em(href("./sort_procs?sort="++Sorted,Text))));
-summary_table_head(SortOn,Text,_Sorted) ->
- th(href("./sort_procs?sort="++SortOn,Text)).
-
-procs_summary_table(Proc) ->
- #proc{pid=Pid,name=Name,state=State,
- reds=Reds,stack_heap=Mem0,msg_q_len=MsgQLen}=Proc,
- Mem = case Mem0 of
- -1 -> "unknown";
- _ -> integer_to_list(Mem0)
- end,
- tr(
- [td(href(["./proc_details?pid=",Pid],Pid)),
- td(Name),
- td(State),
- td("ALIGN=right",integer_to_list(Reds)),
- td("ALIGN=right",Mem),
- td("ALIGN=right",integer_to_list(MsgQLen))]).
-
-%%%-----------------------------------------------------------------
%%% Print details for one process
proc_details(Pid,Proc,TW,SharedHeap) ->
Script =
@@ -594,83 +537,33 @@ expanded_binary_body(Heading,Bin) ->
href("javascript:history.go(-1)","BACK")].
%%%-----------------------------------------------------------------
-%%% Print table of ports
-ports(Heading,Ports,TW) ->
- header(Heading,body(ports_body(Heading,Ports,TW))).
+%%% Print info for one port
+port(Heading,Port,TW) ->
+ header(Heading,body(port_body(Heading,Port,TW))).
-ports_body(Heading,[],TW) ->
- [h1(Heading),
- warn(TW),
- "No ports were found\n"];
-ports_body(Heading,Ports,TW) ->
+port_body(Heading,Port,TW) ->
[heading(Heading,"ports"),
warn(TW),
table(
"BORDER=4 CELLPADDING=4",
- [tr(
- [th("Id"),
- th("Slot"),
- th("Connected"),
- th("Links"),
- th("Controls")]) |
- lists:map(fun(Port) -> ports_table(Port) end, Ports)])].
+ [tr([th(Head) || Head <- port_table_head()]), ports_table(Port)])].
-ports_table(Port) ->
- #port{id=Id,slot=Slot,connected=Connected,links=Links,
- controls=Controls}=Port,
- tr(
- [td(Id),
- td("ALIGHT=right",Slot),
- td(href_proc_port(Connected)),
- td(href_proc_port(Links)),
- td(Controls)]).
-
%%%-----------------------------------------------------------------
-%%% Print table of ETS tables
-ets_tables(Heading,EtsTables,InternalEts,TW) ->
- header(Heading,body(ets_tables_body(Heading,EtsTables,InternalEts,TW))).
+%%% Print table of internal ETS tables
+internal_ets_tables(InternalEts,TW) ->
+ Heading = "Internal ETS tables",
+ header(Heading,body(internal_ets_tables_body(Heading,InternalEts,TW))).
-ets_tables_body(Heading,[],InternalEts,TW) ->
+internal_ets_tables_body(Heading,[],TW) ->
[h1(Heading),
warn(TW),
- "No ETS tables were found\n" |
- internal_ets_tables_table(InternalEts)];
-ets_tables_body(Heading,EtsTables,InternalEts,TW) ->
- [heading(Heading,"ets_tables"),
+ "No internal ETS tables were found\n"];
+internal_ets_tables_body(Heading,InternalEts,TW) ->
+ [heading(Heading,"internal_ets_tables"),
warn(TW),
table(
"BORDER=4 CELLPADDING=4",
[tr(
- [th("Owner"),
- th("Slot"),
- th("Id"),
- th("Name"),
- th("Type"),
- th("Buckets"),
- th("Objects"),
- th("Memory (bytes)")]) |
- lists:map(fun(EtsTable) -> ets_tables_table(EtsTable) end,
- EtsTables)]) |
- internal_ets_tables_table(InternalEts)].
-
-ets_tables_table(EtsTable) ->
- #ets_table{pid=Pid,slot=Slot,id=Id,name=Name,type=Type,
- buckets=Buckets,size=Size,memory=Memory} = EtsTable,
- tr(
- [td(href_proc_port(Pid)),
- td(Slot),
- td(Id),
- td(Name),
- td(Type),
- td("ALIGN=right",Buckets),
- td("ALIGN=right",Size),
- td("ALIGN=right",Memory)]).
-
-internal_ets_tables_table(InternalEtsTables) ->
- [h2("Internal ETS tables"),
- table(
- "BORDER=4 CELLPADDING=4",
- [tr(
[th("Description"),
th("Id"),
th("Name"),
@@ -681,7 +574,7 @@ internal_ets_tables_table(InternalEtsTables) ->
lists:map(fun(InternalEtsTable) ->
internal_ets_tables_table1(InternalEtsTable)
end,
- InternalEtsTables)])].
+ InternalEts)])].
internal_ets_tables_table1({Descr,InternalEtsTable}) ->
#ets_table{id=Id,name=Name,type=Type,buckets=Buckets,
@@ -696,33 +589,6 @@ internal_ets_tables_table1({Descr,InternalEtsTable}) ->
td("ALIGN=right",Memory)]).
%%%-----------------------------------------------------------------
-%%% Print table of timers
-timers(Heading,Timers,TW) ->
- header(Heading,body(timers_body(Heading,Timers,TW))).
-
-timers_body(Heading,[],TW) ->
- [h1(Heading),
- warn(TW),
- "No timers were found\n"];
-timers_body(Heading,Timers,TW) ->
- [heading(Heading,"timers"),
- warn(TW),
- table(
- "BORDER=4 CELLPADDING=4",
- [tr(
- [th("Owner"),
- th("Message"),
- th("Time left")]) |
- lists:map(fun(Timer) -> timers_table(Timer) end, Timers)])].
-
-timers_table(Timer) ->
- #timer{pid=Pid,msg=Msg,time=Time}=Timer,
- tr(
- [td(href_proc_port(Pid)),
- td(Msg),
- td("ALIGN=right",Time)]).
-
-%%%-----------------------------------------------------------------
%%% Print table of nodes in distribution
nods(Nods,TW) ->
header("Distribution Information",body(nodes_body(Nods,TW))).
@@ -826,33 +692,6 @@ format_extra_info(Error) ->
?space -> "";
_ -> font("COLOR=\"#FF0000\"",["ERROR: ",Error,"\n"])
end.
-%%%-----------------------------------------------------------------
-%%% Print loaded modules information
-loaded_mods({CC,OC,LM},TW) ->
- Heading = "Loaded Modules Information",
- header(Heading,body(loaded_mods_body(Heading,CC,OC,LM,TW))).
-
-loaded_mods_body(Heading,"unknown","unknown",[],TW) ->
- [h1(Heading),
- warn(TW),
- "No loaded modules information was found\n"];
-loaded_mods_body(Heading,CC,OC,LM,TW) ->
- [heading(Heading,"loaded_modules"),
- warn(TW),
- p([b("Current code: "),CC," bytes",br(),
- b("Old code: "),OC," bytes"]),
- table(
- "BORDER=4 CELLPADDING=4",
- [tr([th("Module"),
- th("Current size (bytes)"),
- th("Old size (bytes)")]) |
- lists:map(fun(Mod) -> loaded_mods_table(Mod) end,LM)])].
-
-loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) ->
- tr([td(href(["loaded_mod_details?mod=",Mod],Mod)),
- td("ALIGN=right",CS),
- td("ALIGN=right",OS)]).
-
%%%-----------------------------------------------------------------
%%% Print detailed information about one module
@@ -882,107 +721,33 @@ loaded_mod_details_body(ModInfo,TW) ->
%%%-----------------------------------------------------------------
-%%% Print table of funs
-funs(Funs,TW) ->
- Heading = "Fun Information",
- header(Heading,body(funs_body(Heading,Funs,TW))).
-
-funs_body(Heading,[],TW) ->
- [h1(Heading),
- warn(TW),
- "No Fun information was found\n"];
-funs_body(Heading,Funs,TW) ->
- [heading(Heading,"funs"),
- warn(TW),
- table(
- "BORDER=4 CELLPADDING=4",
- [tr(
- [th("Module"),
- th("Uniq"),
- th("Index"),
- th("Address"),
- th("Native_address"),
- th("Refc")]) |
- lists:map(fun(Fun) -> funs_table(Fun) end, Funs)])].
-
-funs_table(Fu) ->
- #fu{module=Module,uniq=Uniq,index=Index,address=Address,
- native_address=NativeAddress,refc=Refc}=Fu,
- tr(
- [td(Module),
- td("ALIGN=right",Uniq),
- td("ALIGN=right",Index),
- td(Address),
- td(NativeAddress),
- td("ALIGN=right",Refc)]).
-
-%%%-----------------------------------------------------------------
%%% Print atoms
-atoms(Atoms,Num,TW) ->
+atoms(SessionId,TW,Num,FirstChunk) ->
Heading = "Atoms",
- header(Heading,body(atoms_body(Heading,Atoms,Num,TW))).
-
-atoms_body(Heading,[],Num,TW) ->
- [h1(Heading),
- warn(TW),
- "No atoms were found in log",br(),
- "Total number of atoms in node was ", Num, br()];
-atoms_body(Heading,Atoms,Num,TW) ->
- [heading(Heading,"atoms"),
- warn(TW),
- "Total number of atoms in node was ", Num,
- br(),
- "The last created atom is shown first",
- br(),br() |
- n_first(Atoms)].
-
-n_first({n_lines,Start,N,What,Lines,Pos}) ->
- NextHref = next_href(N,What,Pos,Start),
- [What," number ",integer_to_list(Start),"-",integer_to_list(Start+N-1),
- br(),
- NextHref,
- pre(Lines),
- NextHref];
-n_first({n_lines,_Start,_N,_What,Lines}) ->
- [pre(Lines)].
-
-%%%-----------------------------------------------------------------
-%%% Print next N lines of "something"
-next(NLines,TW) ->
- header(element(4,NLines),body(next_body(NLines,TW))).
-
-next_body({n_lines,Start,N,What,Lines,Pos},TW) ->
- PrefHref = prev_href(),
- NextHref = next_href(N,What,Pos,Start),
- [warn(TW),
- What," number ",integer_to_list(Start),"-",integer_to_list(Start+N-1),
- br(),
- PrefHref,
- ?space,
- NextHref,
- pre(Lines),
- PrefHref,
- ?space,
- NextHref];
-next_body({n_lines,Start,N,What,Lines},TW) ->
- PrefHref = prev_href(),
- [warn(TW),
- What," number ",integer_to_list(Start),"-",integer_to_list(Start+N-1),
- br(),
- PrefHref,
- pre(Lines),
- PrefHref].
-
-
-prev_href() ->
- href("javascript:history.back()",["Previous"]).
-
-next_href(N,What,Pos,Start) ->
- href(["./next?pos=",integer_to_list(Pos),
- "&num=",integer_to_list(N),
- "&start=",integer_to_list(Start+N),
- "&what=",What],
- "Next").
+ case FirstChunk of
+ done ->
+ deliver_first(SessionId,[start_html_page(Heading),
+ h1(Heading),
+ warn(TW),
+ "No atoms were found in log",br(),
+ "Total number of atoms in node was ", Num,
+ br()]);
+ _ ->
+ deliver_first(SessionId,[start_html_page(Heading),
+ heading(Heading,"atoms"),
+ warn(TW),
+ "Total number of atoms in node was ", Num,
+ br(),
+ "The last created atom is shown first",
+ br(),
+ start_pre()]),
+ atoms_chunk(SessionId,FirstChunk)
+ end.
+
+atoms_chunk(SessionId,done) ->
+ deliver(SessionId,[stop_pre(),stop_html_page()]);
+atoms_chunk(SessionId,Atoms) ->
+ deliver(SessionId,Atoms).
%%%-----------------------------------------------------------------
%%% Print memory information
@@ -1120,52 +885,92 @@ index_tables_body(Heading,IndexTables,TW) ->
th("Size"),
th("Limit"),
th("Used"),
- th("Rate")]) |
+ th("Rate"),
+ th("Entries")]) |
lists:map(fun(IndexTable) -> index_tables_table(IndexTable) end,
IndexTables)])].
index_tables_table(IndexTable) ->
- #index_table{name=Name,size=Size,limit=Limit,used=Used,rate=Rate} =
- IndexTable,
+ #index_table{name=Name,size=Size,limit=Limit,used=Used,
+ rate=Rate,entries=Entries} = IndexTable,
tr(
[td(Name),
td("ALIGN=right",Size),
td("ALIGN=right",Limit),
td("ALIGN=right",Used),
- td("ALIGN=right",Rate)]).
+ td("ALIGN=right",Rate),
+ td("ALIGN=right",Entries)]).
%%%-----------------------------------------------------------------
%%% Internal library
+start_html_page(Title) ->
+ [only_http_header(),
+ start_html(),
+ only_html_header(Title),
+ start_html_body()].
+
+stop_html_page() ->
+ [stop_html_body(),
+ stop_html()].
+
+only_http_header() ->
+ ["Pragma:no-cache\r\n",
+ "Content-type: text/html\r\n\r\n"].
+
+only_html_header(Title) ->
+ only_html_header(Title,"").
+only_html_header(Title,JavaScript) ->
+ ["<HEAD>\n",
+ "<TITLE>", Title, "</TITLE>\n",
+ JavaScript,
+ "</HEAD>\n"].
+
+start_html() ->
+ "<HTML>\n".
+stop_html() ->
+ "</HTML>".
+start_html_body() ->
+ "<BODY BGCOLOR=\"#FFFFFF\">\n".
+stop_html_body() ->
+ "</BODY>\n".
+
header(Body) ->
header("","",Body).
header(Title,Body) ->
header(Title,"",Body).
header(Title,JavaScript,Body) ->
- ["Pragma:no-cache\r\n",
- "Content-type: text/html\r\n\r\n",
+ [only_http_header(),
html_header(Title,JavaScript,Body)].
html_header(Title,JavaScript,Body) ->
- ["<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Title, "</TITLE>\n",
- JavaScript,
- "</HEAD>\n",
+ [start_html(),
+ only_html_header(Title,JavaScript),
Body,
- "</HTML>"].
+ stop_html()].
body(Text) ->
- ["<BODY BGCOLOR=\"#FFFFFF\">\n",
+ [start_html_body(),
Text,
- "<\BODY>\n"].
+ stop_html_body()].
frameset(Args,Frames) ->
["<FRAMESET ",Args,">\n", Frames, "\n</FRAMESET>\n"].
frame(Args) ->
["<FRAME ",Args, ">\n"].
+start_visible_table() ->
+ start_table("BORDER=\"4\" CELLPADDING=\"4\"").
+start_visible_table(ColTitles) ->
+ [start_visible_table(),
+ tr([th(ColTitle) || ColTitle <- ColTitles])].
+
+start_table(Args) ->
+ ["<TABLE ", Args, ">\n"].
+stop_table() ->
+ "</TABLE>\n".
+
table(Args,Text) ->
- ["<TABLE ", Args, ">\n", Text, "\n</TABLE>\n"].
+ [start_table(Args), Text, stop_table()].
tr(Text) ->
["<TR>\n", Text, "\n</TR>\n"].
tr(Args,Text) ->
@@ -1183,8 +988,12 @@ b(Text) ->
["<B>",Text,"</B>"].
em(Text) ->
["<EM>",Text,"</EM>\n"].
+start_pre() ->
+ "<PRE>".
+stop_pre() ->
+ "</PRE>".
pre(Text) ->
- ["<PRE>",Text,"</PRE>"].
+ [start_pre(),Text,stop_pre()].
href(Link,Text) ->
["<A HREF=\"",Link,"\">",Text,"</A>"].
href(Args,Link,Text) ->
@@ -1199,8 +1008,6 @@ input(Args) ->
["<INPUT ", Args, ">\n"].
h1(Text) ->
["<H1>",Text,"</H1>\n"].
-h2(Text) ->
- ["<H2>",Text,"</H2>\n"].
font(Args,Text) ->
["<FONT ",Args,">\n",Text,"\n</FONT>\n"].
p(Text) ->
@@ -1223,7 +1030,7 @@ href_proc_port([$#,$F,$u,$n,$<|T],Acc) ->
href_proc_port([$#,$P,$o,$r,$t,$<|T],Acc) ->
{[$#|Port]=HashPort,Rest} = to_gt(T,[$;,$t,$l,$&,$t,$r,$o,$P,$#]),
href_proc_port(Rest,[href("TARGET=\"main\"",
- ["./ports?port=",Port],HashPort)|Acc]);
+ ["./port?port=",Port],HashPort)|Acc]);
href_proc_port([$<,$<|T],Acc) ->
%% No links to binaries
href_proc_port(T,[$;,$t,$l,$&,$;,$t,$l,$&|Acc]);
@@ -1243,7 +1050,7 @@ href_proc_port([$",$#,$C,$D,$V,$P,$o,$r,$t,$<|T],Acc) ->
%% Port written by crashdump_viewer:parse_term(...)
{[$#|Port]=HashPort,[$"|Rest]} = to_gt(T,[$;,$t,$l,$&,$t,$r,$o,$P,$#]),
href_proc_port(Rest,[href("TARGET=\"main\"",
- ["./ports?port=",Port],HashPort)|Acc]);
+ ["./port?port=",Port],HashPort)|Acc]);
href_proc_port([$",$#,$C,$D,$V,$P,$i,$d,$<|T],Acc) ->
%% Pid written by crashdump_viewer:parse_term(...)
{Pid,[$"|Rest]} = to_gt(T,[$;,$t,$l,$&]),
@@ -1422,7 +1229,7 @@ replace_insrt("'trsni$'"++Rest,[H|T],Acc) -> % the list is reversed here!
"&lt;" ++ _Pid ->
href("TARGET=\"main\"",["./proc_details?pid=",H],H);
"#Port&lt;" ++ Port ->
- href("TARGET=\"main\"",["./ports?port=","Port&lt;"++Port],H);
+ href("TARGET=\"main\"",["./port?port=","Port&lt;"++Port],H);
"#" ++ _other ->
H
end,
@@ -1431,3 +1238,173 @@ replace_insrt([H|T],Insrt,Acc) ->
replace_insrt(T,Insrt,[H|Acc]);
replace_insrt([],[],Acc) ->
Acc.
+
+%%%-----------------------------------------------------------------
+%%% Create a page with one table by delivering chunk by chunk to
+%%% inets. crashdump_viewer first calls chunk_page/5 once, then
+%%% chunk/3 multiple times until all data is delivered.
+chunk_page(processes,SessionId,TW,{Sorted,SharedHeap},FirstChunk) ->
+ Columns = procs_summary_table_head(Sorted,SharedHeap),
+ chunk_page(SessionId, "Process Information", TW, FirstChunk,
+ "processes", Columns, fun procs_summary_table/1);
+chunk_page(ports,SessionId,TW,_,FirstChunk) ->
+ chunk_page(SessionId, "Port Information", TW, FirstChunk,
+ "ports", port_table_head(), fun ports_table/1);
+chunk_page(ets_tables,SessionId,TW,Heading,FirstChunk) ->
+ Columns = ["Owner",
+ "Slot",
+ "Id",
+ "Name",
+ "Type",
+ "Buckets",
+ "Objects",
+ "Memory (bytes)"],
+ chunk_page(SessionId, Heading, TW, FirstChunk,
+ "ets_tables", Columns, fun ets_tables_table/1);
+chunk_page(timers,SessionId,TW,Heading,FirstChunk) ->
+ chunk_page(SessionId, Heading, TW, FirstChunk, "timers",
+ ["Owner","Message","Time left"], fun timers_table/1);
+chunk_page(loaded_mods,SessionId,TW,{CC,OC},FirstChunk) ->
+ TotalsInfo = p([b("Current code: "),CC," bytes",br(),
+ b("Old code: "),OC," bytes"]),
+ Columns = ["Module","Current size (bytes)","Old size (bytes)"],
+ chunk_page(SessionId, "Loaded Modules Information", TW, FirstChunk,
+ "loaded_modules", TotalsInfo,Columns, fun loaded_mods_table/1);
+chunk_page(funs,SessionId, TW, _, FirstChunk) ->
+ Columns = ["Module",
+ "Uniq",
+ "Index",
+ "Address",
+ "Native_address",
+ "Refc"],
+ chunk_page(SessionId, "Fun Information", TW, FirstChunk,
+ "funs", Columns, fun funs_table/1).
+
+chunk_page(SessionId,Heading,TW,FirstChunk,Type,TableColumns,TableFun) ->
+ chunk_page(SessionId,Heading,TW,FirstChunk,Type,[],TableColumns,TableFun).
+chunk_page(SessionId,Heading,TW,done,Type,_TotalsInfo,_TableColumns,_TableFun) ->
+ no_info_found(SessionId,Heading,TW,Type);
+chunk_page(SessionId,Heading,TW,FirstChunk,Type,TotalsInfo,TableColumns,TableFun) ->
+ deliver_first(SessionId,[start_html_page(Heading),
+ heading(Heading,Type),
+ warn(TW),
+ TotalsInfo,
+ start_visible_table(TableColumns)]),
+ chunk(SessionId,FirstChunk,TableFun),
+ TableFun.
+
+no_info_found(SessionId, Heading, TW, Type) ->
+ Info = ["No ", Type, " were found\n"],
+ deliver_first(SessionId,[start_html_page(Heading),
+ h1(Heading),
+ warn(TW),
+ Info,
+ stop_html_page()]).
+
+chunk(SessionId, done, _TableFun) ->
+ deliver(SessionId,[stop_table(),stop_html_page()]);
+chunk(SessionId, Items, TableFun) ->
+ deliver(SessionId, [lists:map(TableFun, Items),
+ stop_table(), %! Will produce an empty table at the end
+ start_visible_table()]). % of the page :(
+
+%%%-----------------------------------------------------------------
+%%% Deliver part of a page to inets
+%%% The first part, which includes the HTTP header, must always be
+%%% delivered as a string (i.e. no binaries). The rest of the page is
+%%% better delivered as binaries in order to avoid data copying.
+deliver_first(SessionId,String) ->
+ mod_esi:deliver(SessionId,String).
+deliver(SessionId,IoList) ->
+ mod_esi:deliver(SessionId,[list_to_binary(IoList)]).
+
+
+%%%-----------------------------------------------------------------
+%%% Page specific stuff for chunk pages
+procs_summary_table_head(Sorted,SharedHeap) ->
+ MemHeading =
+ if SharedHeap ->
+ "Stack";
+ true ->
+ "Stack+heap"
+ end,
+ [procs_summary_table_head("pid","Pid",Sorted),
+ procs_summary_table_head("name_func","Name/Spawned as",Sorted),
+ procs_summary_table_head("state","State",Sorted),
+ procs_summary_table_head("reds","Reductions",Sorted),
+ procs_summary_table_head("mem",MemHeading,Sorted),
+ procs_summary_table_head("msg_q_len","MsgQ Length",Sorted)].
+
+procs_summary_table_head(_,Text,no_sort) ->
+ Text;
+procs_summary_table_head(Sorted,Text,Sorted) ->
+ %% Mark the sorted column (bigger and italic)
+ font("SIZE=\"+1\"",em(href("./sort_procs?sort="++Sorted,Text)));
+procs_summary_table_head(SortOn,Text,_Sorted) ->
+ href("./sort_procs?sort="++SortOn,Text).
+
+procs_summary_table(Proc) ->
+ #proc{pid=Pid,name=Name,state=State,
+ reds=Reds,stack_heap=Mem0,msg_q_len=MsgQLen}=Proc,
+ Mem = case Mem0 of
+ -1 -> "unknown";
+ _ -> integer_to_list(Mem0)
+ end,
+ tr(
+ [td(href(["./proc_details?pid=",Pid],Pid)),
+ td(Name),
+ td(State),
+ td("ALIGN=right",integer_to_list(Reds)),
+ td("ALIGN=right",Mem),
+ td("ALIGN=right",integer_to_list(MsgQLen))]).
+
+port_table_head() ->
+ ["Id","Slot","Connected","Links","Name","Monitors","Controls"].
+
+ports_table(Port) ->
+ #port{id=Id,slot=Slot,connected=Connected,links=Links,name=Name,
+ monitors=Monitors,controls=Controls}=Port,
+ tr(
+ [td(Id),
+ td("ALIGN=right",Slot),
+ td(href_proc_port(Connected)),
+ td(href_proc_port(Links)),
+ td(Name),
+ td(href_proc_port(Monitors)),
+ td(Controls)]).
+
+ets_tables_table(EtsTable) ->
+ #ets_table{pid=Pid,slot=Slot,id=Id,name=Name,type=Type,
+ buckets=Buckets,size=Size,memory=Memory} = EtsTable,
+ tr(
+ [td(href_proc_port(Pid)),
+ td(Slot),
+ td(Id),
+ td(Name),
+ td(Type),
+ td("ALIGN=right",Buckets),
+ td("ALIGN=right",Size),
+ td("ALIGN=right",Memory)]).
+
+timers_table(Timer) ->
+ #timer{pid=Pid,msg=Msg,time=Time}=Timer,
+ tr(
+ [td(href_proc_port(Pid)),
+ td(Msg),
+ td("ALIGN=right",Time)]).
+
+loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) ->
+ tr([td(href(["loaded_mod_details?mod=",Mod],Mod)),
+ td("ALIGN=right",CS),
+ td("ALIGN=right",OS)]).
+
+funs_table(Fu) ->
+ #fu{module=Module,uniq=Uniq,index=Index,address=Address,
+ native_address=NativeAddress,refc=Refc}=Fu,
+ tr(
+ [td(Module),
+ td("ALIGN=right",Uniq),
+ td("ALIGN=right",Index),
+ td(Address),
+ td(NativeAddress),
+ td("ALIGN=right",Refc)]).
diff --git a/lib/observer/test/Makefile b/lib/observer/test/Makefile
index 6f1430b00a..6073e6ea00 100644
--- a/lib/observer/test/Makefile
+++ b/lib/observer/test/Makefile
@@ -53,7 +53,7 @@ EBIN = .
make_emakefile:
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) \
- $(MODULES) >> $(EMAKEFILE)
+ $(MODULES) > $(EMAKEFILE)
tests debug opt: make_emakefile
cd $(ERL_TOP)/lib/test_server/src && \
@@ -80,7 +80,7 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) observer.spec observer.dynspec $(EMAKEFILE) \
+ $(INSTALL_DATA) observer.spec $(EMAKEFILE) \
$(COVERFILE) $(ERL_FILES) \
$(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index fcf383dc2e..fdc4a2f1ff 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,12 +20,13 @@
-module(crashdump_viewer_SUITE).
%% Test functions
--export([all/1,translate/1,start/1,fini/1,load_file/1,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ translate/1,start/1,fini/1,load_file/1,
non_existing/1,not_a_crashdump/1,old_crashdump/1]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include_lib("kernel/include/file.hrl").
@@ -46,16 +47,28 @@ end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [translate,{conf,start,[load_file,non_existing,not_a_crashdump,
- old_crashdump],fini}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [translate, load_file, non_existing, not_a_crashdump,
+ old_crashdump].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_suite(doc) ->
["Create a lot of crashdumps which can be used in the testcases below"];
init_per_suite(Config) when is_list(Config) ->
Dog = ?t:timetrap(?default_timeout),
application:start(inets), % will be using the http client later
- http:set_options([{ipv6,disabled}]),
+ httpc:set_options([{ipfamily,inet6fb4}]),
DataDir = ?config(data_dir,Config),
Rels = [R || R <- [r12b,r13b], ?t:is_release_available(R)] ++ [current],
io:format("Creating crash dumps for the following releases: ~p", [Rels]),
@@ -99,7 +112,7 @@ start(Config) when is_list(Config) ->
undefined = whereis(crashdump_viewer_server),
undefined = whereis(web_tool),
Url = cdv_url(Port,"start_page"),
- {error,_} = http:request(get,{Url,[]},[],[]),
+ {error,_} = httpc:request(Url),
% exit(whereis(httpc_manager),kill),
?t:timetrap_cancel(AngryDog),
ok.
@@ -233,7 +246,7 @@ cdv_url(Port,Link) ->
"http://localhost:" ++ Port ++ "/cdv_erl/crashdump_viewer/" ++ Link.
request_sync(Method,HTTPReqCont) ->
- case http:request(Method,
+ case httpc:request(Method,
HTTPReqCont,
[{timeout,30000}],
[{full_result, false}]) of
@@ -241,13 +254,13 @@ request_sync(Method,HTTPReqCont) ->
Html;
{ok,{Code,Html}} ->
io:format("~s\n", [Html]),
- io:format("Received ~w from http:request(...) with\nMethod=~w\n"
+ io:format("Received ~w from httpc:request(...) with\nMethod=~w\n"
"HTTPReqCont=~p\n",
[Code,Method,HTTPReqCont]),
?t:fail();
Other ->
io:format(
- "Received ~w from http:request(...) with\nMethod=~w\n"
+ "Received ~w from httpc:request(...) with\nMethod=~w\n"
"HTTPReqCont=~p\n",
[Other,Method,HTTPReqCont]),
?t:fail()
@@ -401,16 +414,17 @@ special(Port,File) ->
_ ->
ok
end;
- ".250atoms" ->
- Html1 = contents(Port,"atoms"),
- NextLink1 = next_link(Html1),
- "Atoms" = title(Html1),
- Html2 = contents(Port,NextLink1),
- NextLink2 = next_link(Html2),
- "Atoms" = title(Html2),
- Html3 = contents(Port,NextLink2),
- "" = next_link(Html3),
- "Atoms" = title(Html3);
+ %%! No longer needed - all atoms are shown on one page!!
+ %% ".250atoms" ->
+ %% Html1 = contents(Port,"atoms"),
+ %% NextLink1 = next_link(Html1),
+ %% "Atoms" = title(Html1),
+ %% Html2 = contents(Port,NextLink1),
+ %% NextLink2 = next_link(Html2),
+ %% "Atoms" = title(Html2),
+ %% Html3 = contents(Port,NextLink2),
+ %% "" = next_link(Html3),
+ %% "Atoms" = title(Html3);
_ ->
ok
end,
@@ -483,27 +497,27 @@ expand_binary_link(Html) ->
end.
-next_link(Html) ->
- case Html of
- "<A HREF=\"./next?pos=" ++ Rest ->
- "next?pos=" ++ string:sub_word(Rest,1,$");
- [_H|T] ->
- next_link(T);
- [] ->
- []
- end.
+%% next_link(Html) ->
+%% case Html of
+%% "<A HREF=\"./next?pos=" ++ Rest ->
+%% "next?pos=" ++ string:sub_word(Rest,1,$");
+%% [_H|T] ->
+%% next_link(T);
+%% [] ->
+%% []
+%% end.
toggle_menu(Port) ->
- Html = contents(Port,"toggle?index=10"),
+ Html = contents(Port,"toggle?index=4"),
check_toggle(Html).
check_toggle(Html) ->
case Html of
- "<A HREF=\"./toggle?index=10\"><IMG SRC=\"/crashdump_viewer/collapsd.gif\"" ++ _ ->
+ "<A HREF=\"./toggle?index=4\"><IMG SRC=\"/crashdump_viewer/collapsd.gif\"" ++ _ ->
collapsed;
- "<A HREF=\"./toggle?index=10\"><IMG SRC=\"/crashdump_viewer/exploded.gif\"" ++ _ ->
+ "<A HREF=\"./toggle?index=4\"><IMG SRC=\"/crashdump_viewer/exploded.gif\"" ++ _ ->
exploded;
[_H|T] ->
check_toggle(T)
@@ -530,10 +544,10 @@ expand_link(Html) ->
port_details(Port) ->
- Port1 = contents(Port,"ports?port=Port<0.1>"),
+ Port1 = contents(Port,"port?port=Port<0.1>"),
"#Port<0.1>" = title(Port1),
- Port0 = contents(Port,"ports?port=Port<0.0>"),
+ Port0 = contents(Port,"port?port=Port<0.0>"),
"Could not find port: #Port<0.0>" = title(Port0).
is_truncated(File) ->
@@ -655,7 +669,7 @@ rename(From,To) ->
end.
check_complete(File) ->
- check_complete1(File,5).
+ check_complete1(File,10).
check_complete1(_File,0) ->
{error,enoent};
diff --git a/lib/observer/test/etop_SUITE.erl b/lib/observer/test/etop_SUITE.erl
index 54f4a78e69..a0782ea809 100644
--- a/lib/observer/test/etop_SUITE.erl
+++ b/lib/observer/test/etop_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,22 +20,42 @@
-module(etop_SUITE).
%% Test functions
--export([all/1,text/1,text_tracing_off/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,text/1,text_tracing_off/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(1)).
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
-all(suite) -> [text,text_tracing_off].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [text, text_tracing_off].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
text(suite) ->
[];
diff --git a/lib/observer/test/observer.cover b/lib/observer/test/observer.cover
index 47770ba839..fafb718840 100644
--- a/lib/observer/test/observer.cover
+++ b/lib/observer/test/observer.cover
@@ -1,2 +1,4 @@
-{exclude,[multitrace]}.
-{include,[observer_backend]}.
+{incl_app,observer,details}.
+
+{excl_mods,observer,[multitrace]}.
+{incl_mods,observer,[observer_backend]}.
diff --git a/lib/observer/test/observer.spec b/lib/observer/test/observer.spec
index 801eb80607..3b4b5da28c 100644
--- a/lib/observer/test/observer.spec
+++ b/lib/observer/test/observer.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../observer_test"}}.
-
+{suites,"../observer_test",all}.
diff --git a/lib/observer/test/observer_SUITE.erl b/lib/observer/test/observer_SUITE.erl
index 3e9522c7a4..8dea0d8ea8 100644
--- a/lib/observer/test/observer_SUITE.erl
+++ b/lib/observer/test/observer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,10 +18,11 @@
%%
-module(observer_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases
@@ -39,9 +40,27 @@ end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[app_file].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
app_file(suite) ->
[];
app_file(doc) ->
diff --git a/lib/observer/test/ttb_SUITE.erl b/lib/observer/test/ttb_SUITE.erl
index 6da5e36b29..24b4a22aa9 100644
--- a/lib/observer/test/ttb_SUITE.erl
+++ b/lib/observer/test/ttb_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,14 +21,16 @@
-compile(export_all).
%% Test functions
--export([all/1,file/1,file_no_pi/1,file_fetch/1,wrap/1,wrap_merge/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ file/1,file_no_pi/1,file_fetch/1,wrap/1,wrap_merge/1,
wrap_merge_fetch_format/1,write_config1/1,write_config2/1,
write_config3/1,history/1,write_trace_info/1,seq_trace/1,
diskless/1,otp_4967_1/1,otp_4967_2/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([foo/0]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(1)).
@@ -36,15 +38,34 @@ init_per_testcase(_Case, Config) ->
ttb:stop(),
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
-all(suite) -> [file,file_no_pi,file_fetch,wrap,wrap_merge,
- wrap_merge_fetch_format,write_config1,write_config2,
- write_config3,history,write_trace_info,seq_trace,diskless,
- otp_4967_1,otp_4967_2].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [file, file_no_pi, file_fetch, wrap, wrap_merge,
+ wrap_merge_fetch_format, write_config1, write_config2,
+ write_config3, history, write_trace_info, seq_trace,
+ diskless, otp_4967_1, otp_4967_2].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
file(suite) ->
[];
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index ff06fb992d..14c8f54ba3 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 0.9.8.2
+OBSERVER_VSN = 0.9.9
diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c
index c9627e9d05..d61ce940c3 100644
--- a/lib/odbc/c_src/odbcserver.c
+++ b/lib/odbc/c_src/odbcserver.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2010. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -108,8 +108,8 @@
#if defined WIN32
#include <winsock2.h>
-/* #include <ws2tcpip.h > When we can support a newer c-compiler*/
#include <windows.h>
+#include <ws2tcpip.h >
#include <fcntl.h>
#include <sql.h>
#include <sqlext.h>
@@ -472,7 +472,7 @@ static db_result_msg db_connect(byte *args, db_state *state)
&stringlength2ptr, SQL_DRIVER_NOPROMPT);
if (!sql_success(result)) {
- diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state));
+ diagnos = get_diagnos(SQL_HANDLE_DBC, connection_handle(state));
strcat((char *)diagnos.error_msg,
" Connection to database failed.");
msg = encode_error_message(diagnos.error_msg);
@@ -1599,7 +1599,7 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar
break;
case SQL_C_TYPE_TIMESTAMP:
ts = (TIMESTAMP_STRUCT*) param->values.string;
- ei_decode_tuple_header(buffer, index, &val);
+ ei_decode_tuple_header(buffer, index, &size);
ei_decode_long(buffer, index, &val);
ts[j].year = (SQLUSMALLINT)val;
ei_decode_long(buffer, index, &val);
@@ -1727,74 +1727,48 @@ static byte * receive_erlang_port_msg(void)
}
/* ------------- Socket communication functions --------------------------*/
-#define USE_IPV4
-#ifdef UNIX
-#define SOCKET int
-#endif
-#if defined WIN32 || defined USE_IPV4
-/* Currently only an old windows compiler is supported so we do not have ipv6
- capabilities */
+#if defined(WIN32)
static SOCKET connect_to_erlang(const char *port)
-{
- SOCKET sock;
- struct sockaddr_in sin;
-
- sock = socket(AF_INET, SOCK_STREAM, 0);
-
- memset(&sin, 0, sizeof(sin));
- sin.sin_port = htons ((unsigned short)atoi(port));
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
- close_socket(sock);
- DO_EXIT(EXIT_SOCKET_CONNECT);
- }
- return sock;
-}
#elif defined(UNIX)
static int connect_to_erlang(const char *port)
+#endif
{
- int sock;
-
- struct addrinfo hints;
- struct addrinfo *erlang_ai, *first;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
-
- if (getaddrinfo("localhost", port, &hints, &first) != 0) {
- DO_EXIT(EXIT_FAILURE);
- }
+#if defined(WIN32)
+ SOCKET sock;
+#elif defined(UNIX)
+ int sock;
+#endif
+ struct sockaddr_in sin;
+
+#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_ADDR) && defined(AF_INET6)
+ struct sockaddr_in6 sin6;
+
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_port = htons ((unsigned short)atoi(port));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = in6addr_loopback;
- for (erlang_ai = first; erlang_ai; erlang_ai = erlang_ai->ai_next) {
+ if (connect(sock, (struct sockaddr*)&sin6, sizeof(sin6)) == 0) {
+ return sock;
+ }
+ close_socket(sock);
+#endif
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_port = htons ((unsigned short)atoi(port));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- sock = socket(erlang_ai->ai_family, erlang_ai->ai_socktype,
- erlang_ai->ai_protocol);
- if (sock < 0)
- continue;
- if (connect(sock, (struct sockaddr*)erlang_ai->ai_addr,
- erlang_ai->ai_addrlen) < 0) {
- close(sock);
- sock = -1;
- continue;
- } else {
- break;
+ if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
+ close_socket(sock);
+ DO_EXIT(EXIT_SOCKET_CONNECT);
}
- }
- freeaddrinfo(first);
-
- if (sock < 0){
- close_socket(sock);
- DO_EXIT(EXIT_SOCKET_CONNECT);
- }
-
- return sock;
+ return sock;
}
-#endif
#ifdef WIN32
static void close_socket(SOCKET socket)
@@ -2177,9 +2151,9 @@ static void init_param_column(param_array *params, byte *buffer, int *index,
params->type.sql = SQL_TYPE_TIMESTAMP;
params->type.len = sizeof(TIMESTAMP_STRUCT);
params->type.c = SQL_C_TYPE_TIMESTAMP;
- params->type.col_size = (SQLUINTEGER)19;//;sizeof(TIMESTAMP_STRUCT);
+ params->type.col_size = (SQLUINTEGER)COL_SQL_TIMESTAMP;
params->values.string =
- (TIMESTAMP_STRUCT *)safe_malloc(num_param_values * params->type.len);
+ (byte *)safe_malloc(num_param_values * params->type.len);
break;
case USER_FLOAT:
params->type.sql = SQL_FLOAT;
diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h
index e6d8df1f58..3e2b22ab7d 100644
--- a/lib/odbc/c_src/odbcserver.h
+++ b/lib/odbc/c_src/odbcserver.h
@@ -98,6 +98,7 @@
#define COL_SQL_REAL 7
#define COL_SQL_DOUBLE 15
#define COL_SQL_TINYINT 4
+#define COL_SQL_TIMESTAMP 19
/* Types of parameters given to param_query*/
#define USER_SMALL_INT 1
diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in
index 94e8a214d4..2369e16813 100644
--- a/lib/odbc/configure.in
+++ b/lib/odbc/configure.in
@@ -118,11 +118,18 @@ AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"]))
dnl Checks for header files.
AC_HEADER_STDC
-AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h])
+AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h winsock2.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_SIZE_T
+AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_addr], [], [],
+ [#if HAVE_WINSOCK2_H
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+ #else
+ #include <netinet/in.h>
+ #endif])
dnl Checks for library functions.
AC_CHECK_FUNCS([memset socket])
diff --git a/lib/odbc/doc/src/databases.xml b/lib/odbc/doc/src/databases.xml
index 9776736909..a6ba0e5245 100644
--- a/lib/odbc/doc/src/databases.xml
+++ b/lib/odbc/doc/src/databases.xml
@@ -258,7 +258,7 @@ when p >= 16 </cell>
that contains more than one SQL query. For example, the
following SQLServer-specific statement creates a procedure that
returns a result set containing information about employees
- that work at the department and and a result set listing the
+ that work at the department and a result set listing the
customers of that department. </p>
<code type="none">
CREATE PROCEDURE DepartmentInfo (@DepartmentID INT) AS
diff --git a/lib/odbc/doc/src/notes.xml b/lib/odbc/doc/src/notes.xml
index 09d78c3248..b88c7cf1cd 100644
--- a/lib/odbc/doc/src/notes.xml
+++ b/lib/odbc/doc/src/notes.xml
@@ -31,7 +31,46 @@
<p>This document describes the changes made to the odbc application.
</p>
- <section><title>ODBC 2.10.8</title>
+ <section><title>ODBC 2.10.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Better error messages for connection issues.</p>
+ <p>
+ Own Id: OTP-9111</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Ipv6 is now supported on Windows as well as on UNIX for
+ internal socket communication. (ODBC uses sockets instead
+ of the "Erlang port pipes" as some ODBC-drivers are known
+ to mess with stdin/stdout.) </p>
+ <p>
+ Loopback address constants are used when connecting the
+ c-side to the erlang-side over local socket API avoiding
+ getaddrinfo problems, and the {ip, loopback} option is
+ added as a listen option on the erlang-side. Also cleaned
+ up the TIME_STAMP contribution.</p>
+ <p>
+ Own Id: OTP-8917</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>ODBC 2.10.8</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src
index e95e542ff5..2a6667ccd3 100644
--- a/lib/odbc/src/odbc.appup.src
+++ b/lib/odbc/src/odbc.appup.src
@@ -1 +1,8 @@
-{"%VSN%", [],[]}
+%% -*- erlang -*-
+{"%VSN%",
+ [
+ {"2.10.9", [{restart_application, ssl}]}
+ ],
+ [
+ {"2.10.9", [{restart_application, ssl}]}
+ ]}.
diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl
index eb27a471ec..83d9f33102 100644
--- a/lib/odbc/src/odbc.erl
+++ b/lib/odbc/src/odbc.erl
@@ -441,10 +441,12 @@ init(Args) ->
{ok, ListenSocketSup} =
gen_tcp:listen(0, [Inet, binary, {packet, ?LENGTH_INDICATOR_SIZE},
- {active, false}, {nodelay, true}]),
+ {active, false}, {nodelay, true},
+ {ip, loopback}]),
{ok, ListenSocketOdbc} =
gen_tcp:listen(0, [Inet, binary, {packet, ?LENGTH_INDICATOR_SIZE},
- {active, false}, {nodelay, true}]),
+ {active, false}, {nodelay, true},
+ {ip, loopback}]),
%% Start the port program (a c program) that utilizes the odbc driver
case os:find_executable(?SERVERPROG, ?SERVERDIR) of
diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile
new file mode 100644
index 0000000000..ec2bcc67b5
--- /dev/null
+++ b/lib/odbc/test/Makefile
@@ -0,0 +1,114 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+
+INCLUDES= -I. -I$(ERL_TOP)/lib/test_server/include/ -I$(ERL_TOP)/lib/odbc/src
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ odbc_start_SUITE \
+ odbc_connect_SUITE \
+ odbc_query_SUITE \
+ odbc_data_type_SUITE \
+ odbc_test_lib \
+ oracle \
+ sqlserver \
+ postgres
+
+EBIN = .
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+HRL_FILES= odbc_test.hrl\
+
+TARGET_FILES= \
+ $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+SPEC_FILES = odbc.spec
+COVER_FILE = odbc.cover
+
+EMAKEFILE = Emakefile
+MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
+
+ifeq ($(MAKE_EMAKE),)
+BUILDTARGET = $(TARGET_FILES)
+RELTEST_FILES = $(SPEC_FILES) $(SOURCE)
+else
+BUILDTARGET = emakebuild
+RELTEST_FILES = $(EMAKEFILE) $(SPEC_FILES) $(SOURCE)
+endif
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/odbc_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_COMPILE_FLAGS += $(INCLUDES) \
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(BUILDTARGET)
+
+targets: $(TARGET_FILES)
+
+.PHONY: emakebuild
+
+emakebuild: $(EMAKEFILE)
+
+$(EMAKEFILE):
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE)
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+
+
+release_docs_spec:
+
+
+
+
+
+
+
diff --git a/lib/odbc/test/README b/lib/odbc/test/README
new file mode 100644
index 0000000000..1f3c659e28
--- /dev/null
+++ b/lib/odbc/test/README
@@ -0,0 +1,86 @@
+-------------------------------------------------------------------------
+ TEST SUITE REQUIREMENTS
+-------------------------------------------------------------------------
+As third party products are involved when using ODBC you will have to
+setup your own test environment to be able to run the ODBC test
+suites.
+
+You need to install a database such as postgres, sql-server, oracle
+etc, and ODBC-drivers for that database.
+
+Then you need to setup a test database, however you do not
+need to create any tables that will be done by the test suites.
+The test suites will also remove all tables that it creates when
+the test is complete.
+
+-------------------------------------------------------------------------
+ERLANG FILES YOU MAY NEED TO CHANGE
+-------------------------------------------------------------------------
+
+A remote database management system has a callback module to handle
+possible differences in data type handling etc, the callback module
+also defines the ODBC connection string. Currently available callback
+modules are postgres.erl, sqlserver.erl and oracle.erl. Depending on
+how you set things up you might want to edit the connection string in
+the callback module or even add your own callback module.
+
+The callback module used in each test case is defined by the ?RDBMS
+macro defined in odbc_test.hrl so you might need to change this to
+suite your purposes.
+
+-------------------------------------------------------------------------
+EXAMPLE
+-------------------------------------------------------------------------
+
+As an example say we have the database odbctestdb, with
+the user odbctest that has the password Sesame. The database
+runs on the host myhost.
+
+UINX/LINUX
+-----------
+
+Set up a database and install the unixODBC drivers.
+Then the unix/linux user that should run the test suits needs an .odbc.ini
+file to map connection data. For example ODBC connection string:
+"DSN=Postgres;UID=odbctest" will need an .odbc.ini entry that looks
+something like this:
+
+--- Start example of .odbc.ini ----
+
+[Postgres]
+Driver=/usr/lib/psqlodbc.so
+Description=Postgres driver
+ServerName=myhost
+Database=odbctestdb
+Port=5432
+LogonID=odbctest
+Password=Sesame
+
+---End example of .odbc.ini ------------
+
+
+WINDOWS MOST FLAVORS
+--------------------
+
+There will be a "ODBC data source administrator" tool under
+Control Panel -> Administrative Tools, use this to set up
+your database. Choose to connect with SQL Server authentication.
+As odbc connection string use: "DSN=odbctestdb;UID=odbctest;PWD=Sesame"
+
+
+> %CopyrightBegin%
+>
+> Copyright Ericsson AB 2010. All Rights Reserved.
+>
+> The contents of this file are subject to the Erlang Public License,
+> Version 1.1, (the "License"); you may not use this file except in
+> compliance with the License. You should have received a copy of the
+> Erlang Public License along with this software. If not, it can be
+> retrieved online at http://www.erlang.org/.
+>
+> Software distributed under the License is distributed on an "AS IS"
+> basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+> the License for the specific language governing rights and limitations
+> under the License.
+>
+> %CopyrightEnd%
diff --git a/lib/odbc/test/odbc.cover b/lib/odbc/test/odbc.cover
new file mode 100644
index 0000000000..1acca281fb
--- /dev/null
+++ b/lib/odbc/test/odbc.cover
@@ -0,0 +1,2 @@
+{incl_app,odbc,details}.
+
diff --git a/lib/odbc/test/odbc.dynspec b/lib/odbc/test/odbc.dynspec
new file mode 100644
index 0000000000..bb15edceed
--- /dev/null
+++ b/lib/odbc/test/odbc.dynspec
@@ -0,0 +1,31 @@
+%% -*- erlang -*-
+%% You can test this file using this command.
+%% file:script("odbc.dynspec", [{'Os',"Unix"}]).
+
+Exists =
+fun() ->
+ case code:lib_dir(odbc) of
+ {error,bad_name} ->
+ false;
+ P ->
+ %% Make sure that the odbc directory really
+ %% contains the application (and not only documentation).
+ case filelib:is_file(filename:join(P, "ebin/odbc.beam")) of
+ false -> false;
+ true ->
+ %% We know that we don't have any odbc libraries
+ %% installed on this computer.
+ {ok,Host} = inet:gethostname(),
+ Host =/= "netsim200"
+ end
+ end
+end,
+case Exists() of
+ false ->
+ NoOdbc = "No odbc application",
+ [{skip, {odbc_connect_SUITE, NoOdbc}},
+ {skip, {odbc_data_type_SUITE, NoOdbc}},
+ {skip, {odbc_query_SUITE, NoOdbc}}];
+ true ->
+ []
+end.
diff --git a/lib/odbc/test/odbc.spec b/lib/odbc/test/odbc.spec
new file mode 100644
index 0000000000..edaf821c91
--- /dev/null
+++ b/lib/odbc/test/odbc.spec
@@ -0,0 +1,25 @@
+{suites,"../odbc_test",all}.
+{skip_cases,"../odbc_test",odbc_data_type_SUITE,
+ [varchar_upper_limit],
+ "Known bug in database"}.
+{skip_cases,"../odbc_test",odbc_data_type_SUITE,
+ [text_upper_limit],
+ "Consumes too much resources"}.
+{skip_cases,"../odbc_test",odbc_data_type_SUITE,
+ [bit_true],
+ "Not supported by driver"}.
+{skip_cases,"../odbc_test",odbc_data_type_SUITE,
+ [bit_false],
+ "Not supported by driver"}.
+{skip_cases,"../odbc_test",odbc_query_SUITE,
+ [multiple_select_result_sets],
+ "Not supported by driver"}.
+{skip_cases,"../odbc_test",odbc_query_SUITE,
+ [multiple_mix_result_sets],
+ "Not supported by driver"}.
+{skip_cases,"../odbc_test",odbc_query_SUITE,
+ [multiple_result_sets_error],
+ "Not supported by driver"}.
+{skip_cases,"../odbc_test",odbc_query_SUITE,
+ [param_insert_tiny_int],
+ "Not supported by driver"}.
diff --git a/lib/odbc/test/odbc.spec.win b/lib/odbc/test/odbc.spec.win
new file mode 100644
index 0000000000..1fd349d2c3
--- /dev/null
+++ b/lib/odbc/test/odbc.spec.win
@@ -0,0 +1,5 @@
+{topcase, {dir, "../odbc_test"}}.
+{skip, {odbc_data_type_SUITE, big_int_lower_limit, "Not supported by sqlserver 7.0"}}.
+{skip, {odbc_data_type_SUITE, big_int_upper_limit, "Not supported by sqlserver7.0"}}.
+{skip, {odbc_data_type_SUITE, text_upper_limit, "Consumes too much resources"}}.
+
diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl
new file mode 100644
index 0000000000..6a2268f40e
--- /dev/null
+++ b/lib/odbc/test/odbc_connect_SUITE.erl
@@ -0,0 +1,823 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(odbc_connect_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include("test_server_line.hrl").
+-include("odbc_test.hrl").
+
+-define(MAX_SEQ_TIMEOUTS, 10).
+
+%%--------------------------------------------------------------------
+%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
+%% Arg - doc | suite
+%% Doc - string()
+%% Case - atom()
+%% Name of a test case function.
+%% Comment - string()
+%% Description: Returns documentation/test cases in this test suite
+%% or a skip tuple if the platform is not supported.
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case odbc_test_lib:odbc_check() of
+ ok ->
+ [not_exist_db, commit, rollback, not_explicit_commit,
+ no_c_node, port_dies, control_process_dies,
+ {group, client_dies}, connect_timeout, timeout,
+ many_timeouts, timeout_reset, disconnect_on_timeout,
+ connection_closed, disable_scrollable_cursors,
+ return_rows_as_lists, api_missuse];
+ Other -> {skip, Other}
+ end.
+
+groups() ->
+ [{client_dies, [],
+ [client_dies_normal, client_dies_timeout,
+ client_dies_error]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ application:start(odbc),
+ case catch odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]) of
+ {ok, Ref} ->
+ odbc:disconnect(Ref),
+ [{tableName, odbc_test_lib:unique_table_name()} | Config];
+ _ ->
+ {skip, "ODBC is not properly setup"}
+ end.
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ application:stop(odbc),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(Case, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config) ->
+ test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
+ Dog = test_server:timetrap(?default_timeout),
+ Temp = lists:keydelete(connection_ref, 1, Config),
+ NewConfig = lists:keydelete(watchdog, 1, Temp),
+ [{watchdog, Dog} | NewConfig].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(Case, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, Config) ->
+ %% Clean up if needed
+ Table = ?config(tableName, Config),
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+ io:format("Drop table: ~p ~p~n", [Table, Result]),
+ odbc:disconnect(Ref),
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+commit(doc)->
+ ["Test the use of explicit commit"];
+commit(suite) -> [];
+commit(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+
+ Table = ?config(tableName, Config),
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10))"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1,'bar')"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+
+ ok = odbc:commit(Ref, commit),
+ UpdateResult = ?RDBMS:update_result(),
+ UpdateResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'bar' WHERE ID = 1"),
+ ok = odbc:commit(Ref, commit, ?TIMEOUT),
+ InsertResult = ?RDBMS:insert_result(),
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT', {function_clause, _}} =
+ (catch odbc:commit(Ref, commit, -1)),
+
+ ok = odbc:disconnect(Ref),
+
+ ok.
+%%-------------------------------------------------------------------------
+
+rollback(doc)->
+ ["Test the use of explicit rollback"];
+rollback(suite) -> [];
+rollback(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10))"),
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+ ok = odbc:commit(Ref, commit),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+ ok = odbc:commit(Ref, rollback),
+ InsertResult = ?RDBMS:insert_result(),
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+ ok = odbc:commit(Ref, rollback, ?TIMEOUT),
+ InsertResult = ?RDBMS:insert_result(),
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+
+ {'EXIT', {function_clause, _}} =
+ (catch odbc:commit(Ref, rollback, -1)),
+
+ ok = odbc:disconnect(Ref),
+ ok.
+
+%%-------------------------------------------------------------------------
+not_explicit_commit(doc) ->
+ ["Test what happens if you try using commit on a auto_commit connection."];
+not_explicit_commit(suite) -> [];
+not_explicit_commit(_Config) ->
+ {ok, Ref} =
+ odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}]),
+ {error, _} = odbc:commit(Ref, commit),
+ ok = odbc:disconnect(Ref),
+ ok.
+
+%%-------------------------------------------------------------------------
+not_exist_db(doc) ->
+ ["Tests valid data format but invalid data in the connection parameters."];
+not_exist_db(suite) -> [];
+not_exist_db(_Config) ->
+ {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", []),
+ %% So that the odbc control server can be stoped "in the correct way"
+ test_server:sleep(100),
+ ok.
+
+%%-------------------------------------------------------------------------
+no_c_node(doc) ->
+ "Test what happens if the port-program can not be found";
+no_c_node(suite) -> [];
+no_c_node(_Config) ->
+ process_flag(trap_exit, true),
+ Dir = filename:nativename(filename:join(code:priv_dir(odbc),
+ "bin")),
+ FileName1 = filename:nativename(os:find_executable("odbcserver",
+ Dir)),
+ FileName2 = filename:nativename(filename:join(Dir, "odbcsrv")),
+ ok = file:rename(FileName1, FileName2),
+ Result =
+ case catch odbc:connect(?RDBMS:connection_string(), []) of
+ {error, port_program_executable_not_found} ->
+ ok;
+ Else ->
+ Else
+ end,
+
+ ok = file:rename(FileName2, FileName1),
+ ok = Result.
+%%------------------------------------------------------------------------
+
+port_dies(doc) ->
+ "Tests what happens if the port program dies";
+port_dies(suite) -> [];
+port_dies(_Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ {status, _} = process_info(Ref, status),
+ process_flag(trap_exit, true),
+ Port = lists:last(erlang:ports()),
+ exit(Port, kill),
+ %% Wait for exit_status from port 5000 ms (will not get a exit
+ %% status in this case), then wait a little longer to make sure
+ %% the port and the controlprocess has had time to terminate.
+ test_server:sleep(7000),
+ undefined = process_info(Ref, status),
+ ok.
+
+%%-------------------------------------------------------------------------
+control_process_dies(doc) ->
+ "Tests what happens if the Erlang control process dies";
+control_process_dies(suite) -> [];
+control_process_dies(_Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ process_flag(trap_exit, true),
+ Port = lists:last(erlang:ports()),
+ {connected, Ref} = erlang:port_info(Port, connected),
+ exit(Ref, kill),
+ test_server:sleep(100),
+ undefined = erlang:port_info(Port, connected),
+ %% Check for c-program still running, how?
+ ok.
+
+%%-------------------------------------------------------------------------
+
+%%-------------------------------------------------------------------------
+client_dies_normal(doc) ->
+ ["Client dies with reason normal."];
+client_dies_normal(suite) -> [];
+client_dies_normal(Config) when is_list(Config) ->
+ Pid = spawn(?MODULE, client_normal, [self()]),
+
+ MonitorReference =
+ receive
+ {dbRef, Ref} ->
+ MRef = erlang:monitor(process, Ref),
+ Pid ! continue,
+ MRef
+ end,
+
+ receive
+ {'DOWN', MonitorReference, _Type, _Object, _Info} ->
+ ok
+ after 5000 ->
+ test_server:fail(control_process_not_stopped)
+ end.
+
+client_normal(Pid) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ Pid ! {dbRef, Ref},
+ receive
+ continue ->
+ ok
+ end,
+ exit(self(), normal).
+
+
+%%-------------------------------------------------------------------------
+client_dies_timeout(doc) ->
+ ["Client dies with reason timeout."];
+client_dies_timeout(suite) -> [];
+client_dies_timeout(Config) when is_list(Config) ->
+ Pid = spawn(?MODULE, client_timeout, [self()]),
+
+ MonitorReference =
+ receive
+ {dbRef, Ref} ->
+ MRef = erlang:monitor(process, Ref),
+ Pid ! continue,
+ MRef
+ end,
+
+ receive
+ {'DOWN', MonitorReference, _Type, _Object, _Info} ->
+ ok
+ after 5000 ->
+ test_server:fail(control_process_not_stopped)
+ end.
+
+client_timeout(Pid) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ Pid ! {dbRef, Ref},
+ receive
+ continue ->
+ ok
+ end,
+ exit(self(), timeout).
+
+
+%%-------------------------------------------------------------------------
+client_dies_error(doc) ->
+ ["Client dies with reason error."];
+client_dies_error(suite) -> [];
+client_dies_error(Config) when is_list(Config) ->
+ Pid = spawn(?MODULE, client_error, [self()]),
+
+ MonitorReference =
+ receive
+ {dbRef, Ref} ->
+ MRef = erlang:monitor(process, Ref),
+ Pid ! continue,
+ MRef
+ end,
+
+ receive
+ {'DOWN', MonitorReference, _Type, _Object, _Info} ->
+ ok
+ after 5000 ->
+ test_server:fail(control_process_not_stopped)
+ end.
+
+client_error(Pid) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ Pid ! {dbRef, Ref},
+ receive
+ continue ->
+ ok
+ end,
+ exit(self(), error).
+
+
+%%-------------------------------------------------------------------------
+connect_timeout(doc) ->
+ ["Test the timeout for the connect function."];
+connect_timeout(suite) -> [];
+connect_timeout(Config) when is_list(Config) ->
+ {'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(),
+ [{timeout, 0}])),
+ ok.
+%%-------------------------------------------------------------------------
+timeout(doc) ->
+ ["Test that timeouts don't cause unwanted behavior sush as receiving"
+ " an anwser to a previously tiemed out query."];
+timeout(suite) -> [];
+timeout(Config) when is_list(Config) ->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ ok = odbc:commit(Ref, commit),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'baz')"),
+
+ Pid = spawn_link(?MODULE, update_table_timeout, [Table, 5000, self()]),
+
+ receive
+ timout_occurred ->
+ ok = odbc:commit(Ref, commit),
+ Pid ! continue
+ end,
+
+ receive
+ altered ->
+ ok
+ end,
+
+ {selected, Fields, [{"foobar"}]} =
+ odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 1"),
+ ["DATA"] = odbc_test_lib:to_upper(Fields),
+
+ ok = odbc:commit(Ref, commit),
+ ok = odbc:disconnect(Ref),
+ ok.
+
+
+update_table_timeout(Table, TimeOut, Pid) ->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1",
+
+ case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of
+ {'EXIT', timeout} ->
+ Pid ! timout_occurred;
+ {updated, 1} ->
+ test_server:fail(database_locker_failed)
+ end,
+
+ receive
+ continue ->
+ ok
+ end,
+
+ %% Make sure we receive the correct result and not the answer
+ %% to the previous query.
+ {selected, Fields, [{"baz"}]} =
+ odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"),
+ ["DATA"] = odbc_test_lib:to_upper(Fields),
+
+ {updated, 1} = odbc:sql_query(Ref, UpdateQuery, TimeOut),
+
+ ok = odbc:commit(Ref, commit),
+
+ Pid ! altered,
+
+ ok = odbc:disconnect(Ref),
+
+ ok.
+%%-------------------------------------------------------------------------
+many_timeouts(doc) ->
+ ["Tests that many consecutive timeouts lead to that the connection "
+ "is shutdown."];
+many_timeouts(suite) -> [];
+many_timeouts(Config) when is_list(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ ok = odbc:commit(Ref, commit),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+
+ _Pid = spawn_link(?MODULE, update_table_many_timeouts,
+ [Table, 5000, self()]),
+
+ receive
+ many_timeouts_occurred ->
+ ok
+ end,
+
+ ok = odbc:commit(Ref, commit),
+ ok = odbc:disconnect(Ref),
+ ok.
+
+
+update_table_many_timeouts(Table, TimeOut, Pid) ->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1",
+
+ ok = loop_many_timouts(Ref, UpdateQuery, TimeOut),
+
+ Pid ! many_timeouts_occurred,
+
+ ok = odbc:disconnect(Ref),
+ ok.
+
+
+loop_many_timouts(Ref, UpdateQuery, TimeOut) ->
+ case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of
+ {'EXIT',timeout} ->
+ loop_many_timouts(Ref, UpdateQuery, TimeOut);
+ {updated, 1} ->
+ test_server:fail(database_locker_failed);
+ {error, connection_closed} ->
+ ok
+ end.
+%%-------------------------------------------------------------------------
+timeout_reset(doc) ->
+ ["Check that the number of consecutive timouts is reset to 0 when "
+ "a successful call to the database is made."];
+timeout_reset(suite) -> [];
+timeout_reset(Config) when is_list(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ ok = odbc:commit(Ref, commit),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'baz')"),
+
+
+ Pid = spawn_link(?MODULE, update_table_timeout_reset,
+ [Table, 5000, self()]),
+
+ receive
+ many_timeouts_occurred ->
+ ok
+ end,
+
+ ok = odbc:commit(Ref, commit),
+ Pid ! continue,
+
+ receive
+ altered ->
+ ok
+ end,
+
+ {selected, Fields, [{"foobar"}]} =
+ odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 1"),
+ ["DATA"] = odbc_test_lib:to_upper(Fields),
+
+ ok = odbc:commit(Ref, commit),
+ ok = odbc:disconnect(Ref),
+ ok.
+
+update_table_timeout_reset(Table, TimeOut, Pid) ->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1",
+
+ ok = loop_timout_reset(Ref, UpdateQuery, TimeOut,
+ ?MAX_SEQ_TIMEOUTS-1),
+
+ Pid ! many_timeouts_occurred,
+
+ receive
+ continue ->
+ ok
+ end,
+
+ {selected, Fields, [{"baz"}]} =
+ odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"),
+ ["DATA"] = odbc_test_lib:to_upper(Fields),
+
+ {updated,1} = odbc:sql_query(Ref, UpdateQuery, TimeOut),
+
+ ok = odbc:commit(Ref, commit),
+
+ Pid ! altered,
+
+ ok = odbc:disconnect(Ref),
+
+ ok.
+
+loop_timout_reset(_, _, _, 0) ->
+ ok;
+
+loop_timout_reset(Ref, UpdateQuery, TimeOut, NumTimeouts) ->
+ case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of
+ {'EXIT',timeout} ->
+ loop_timout_reset(Ref, UpdateQuery,
+ TimeOut, NumTimeouts - 1);
+ {updated, 1} ->
+ test_server:fail(database_locker_failed);
+ {error, connection_closed} ->
+ test_server:fail(connection_closed_premature)
+ end.
+
+%%-------------------------------------------------------------------------
+
+disconnect_on_timeout(doc) ->
+ ["Check that disconnect after a time out works properly"];
+disconnect_on_timeout(suite) -> [];
+disconnect_on_timeout(Config) when is_list(Config) ->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ ok = odbc:commit(Ref, commit),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+
+
+ _Pid = spawn_link(?MODULE, update_table_disconnect_on_timeout,
+ [Table, 5000, self()]),
+ receive
+ ok ->
+ ok = odbc:commit(Ref, commit);
+ nok ->
+ test_server:fail(database_locker_failed)
+ end.
+
+update_table_disconnect_on_timeout(Table, TimeOut, Pid) ->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{auto_commit, off}]),
+ UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1",
+
+ case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of
+ {'EXIT', timeout} ->
+ ok = odbc:disconnect(Ref),
+ Pid ! ok;
+ {updated, 1} ->
+ Pid ! nok
+ end.
+
+%%-------------------------------------------------------------------------
+connection_closed(doc) ->
+ ["Checks that you get an appropriate error message if you try to"
+ " use a connection that has been closed"];
+connection_closed(suite) -> [];
+connection_closed(Config) when is_list(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+
+ Table = ?config(tableName, Config),
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA char(10), PRIMARY KEY(ID))"),
+
+ ok = odbc:disconnect(Ref),
+
+ {error, connection_closed} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+ {error, connection_closed} =
+ odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+ {error, connection_closed} = odbc:first(Ref),
+ {error, connection_closed} = odbc:last(Ref),
+ {error, connection_closed} = odbc:next(Ref),
+ {error, connection_closed} = odbc:prev(Ref),
+ {error, connection_closed} = odbc:select(Ref, next, 3),
+ {error, connection_closed} = odbc:commit(Ref, commit),
+ ok.
+
+%%-------------------------------------------------------------------------
+disable_scrollable_cursors(doc) ->
+ ["Test disabling of scrollable cursors."];
+disable_scrollable_cursors(suite) -> [];
+disable_scrollable_cursors(Config) when is_list(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{scrollable_cursors, off}]),
+
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ {ok, _} = odbc:select_count(Ref, "SELECT ID FROM " ++ Table),
+
+ NextResult = ?RDBMS:selected_ID(1, next),
+
+ test_server:format("Expected: ~p~n", [NextResult]),
+
+ Result = odbc:next(Ref),
+ test_server:format("Got: ~p~n", [Result]),
+ NextResult = Result,
+
+ {error, scrollable_cursors_disabled} = odbc:first(Ref),
+ {error, scrollable_cursors_disabled} = odbc:last(Ref),
+ {error, scrollable_cursors_disabled} = odbc:prev(Ref),
+ {error, scrollable_cursors_disabled} =
+ odbc:select(Ref, {relative, 2}, 5),
+ {error, scrollable_cursors_disabled} =
+ odbc:select(Ref, {absolute, 2}, 5),
+
+ {selected, _ColNames,[]} = odbc:select(Ref, next, 1),
+ ok.
+
+%%-------------------------------------------------------------------------
+return_rows_as_lists(doc)->
+ ["Test the option that a row may be returned as a list instead "
+ "of a tuple. Too be somewhat backward compatible."];
+return_rows_as_lists(suite) -> [];
+return_rows_as_lists(Config) when is_list(Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{tuple_row, off}]),
+
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(2,'foo')"),
+
+ ListRows = ?RDBMS:selected_list_rows(),
+ ListRows =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ First = ?RDBMS:first_list_rows(),
+ Last = ?RDBMS:last_list_rows(),
+ Prev = ?RDBMS:prev_list_rows(),
+ Next = ?RDBMS:next_list_rows(),
+
+ Last = odbc:last(Ref),
+ Prev = odbc:prev(Ref),
+ First = odbc:first(Ref),
+ Next = odbc:next(Ref),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+api_missuse(doc)->
+ ["Test that behaviour of the control process if the api is abused"];
+api_missuse(suite) -> [];
+api_missuse(Config) when is_list(Config)->
+
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ %% Serious programming fault, connetion will be shut down
+ gen_server:call(Ref, {self(), foobar, 10}, infinity),
+ test_server:sleep(10),
+ undefined = process_info(Ref, status),
+
+ {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), []),
+ %% Serious programming fault, connetion will be shut down
+ gen_server:cast(Ref2, {self(), foobar, 10}),
+ test_server:sleep(10),
+ undefined = process_info(Ref2, status),
+
+ {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), []),
+ %% Could be an innocent misstake the connection lives.
+ Ref3 ! foobar,
+ test_server:sleep(10),
+ {status, _} = process_info(Ref3, status),
+ ok.
+
diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl
new file mode 100644
index 0000000000..bfb1e4b329
--- /dev/null
+++ b/lib/odbc/test/odbc_data_type_SUITE.erl
@@ -0,0 +1,1499 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(odbc_data_type_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
+-include("test_server_line.hrl").
+-include("odbc_test.hrl").
+
+%%--------------------------------------------------------------------
+%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
+%% Arg - doc | suite
+%% Doc - string()
+%% Case - atom()
+%% Name of a test case function.
+%% Comment - string()
+%% Description: Returns documentation/test cases in this test suite
+%% or a skip tuple if the platform is not supported.
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case odbc_test_lib:odbc_check() of
+ ok ->
+ [{group, char}, {group, int}, {group, floats},
+ {group, dec_and_num}, timestamp];
+ Other -> {skip, Other}
+ end.
+
+groups() ->
+ [{char, [],
+ [char_fixed_lower_limit, char_fixed_upper_limit,
+ char_fixed_padding, varchar_lower_limit,
+ varchar_upper_limit, varchar_no_padding,
+ text_lower_limit, text_upper_limit, unicode]},
+ {binary_char, [],
+ [binary_char_fixed_lower_limit,
+ binary_char_fixed_upper_limit,
+ binary_char_fixed_padding, binary_varchar_lower_limit,
+ binary_varchar_upper_limit, binary_varchar_no_padding,
+ binary_text_lower_limit, binary_text_upper_limit,
+ unicode]},
+ {int, [],
+ [tiny_int_lower_limit, tiny_int_upper_limit,
+ small_int_lower_limit, small_int_upper_limit,
+ int_lower_limit, int_upper_limit, big_int_lower_limit,
+ big_int_upper_limit, bit_false, bit_true]},
+ {floats, [],
+ [float_lower_limit, float_upper_limit, float_zero,
+ real_zero]},
+ {dec_and_num, [],
+ [dec_long, dec_double, dec_bignum, num_long, num_double,
+ num_bignum]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ application:start(odbc),
+ [{tableName, odbc_test_lib:unique_table_name()} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ application:stop(odbc),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(Case, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(Case, Config) ->
+ case atom_to_list(Case) of
+ "binary" ++ _ ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{binary_strings, on}]);
+ "unicode" ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(),
+ [{binary_strings, on}]);
+ _ ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [])
+ end,
+ Dog = test_server:timetrap(?default_timeout),
+ Temp = lists:keydelete(connection_ref, 1, Config),
+ NewConfig = lists:keydelete(watchdog, 1, Temp),
+ [{watchdog, Dog}, {connection_ref, Ref} | NewConfig].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(Case, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, Config) ->
+ Ref = ?config(connection_ref, Config),
+ ok = odbc:disconnect(Ref),
+ %% Clean up if needed
+ Table = ?config(tableName, Config),
+ {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), []),
+ odbc:sql_query(NewRef, "DROP TABLE " ++ Table),
+ odbc:disconnect(NewRef),
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+
+char_fixed_lower_limit(doc) ->
+ ["Tests fixed length char data type lower boundaries."];
+char_fixed_lower_limit(suite) ->
+ [];
+char_fixed_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Below limit
+ {error, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ (?RDBMS:fixed_char_min() - 1))),
+ %% Lower limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ ?RDBMS:fixed_char_min())),
+
+ %% Right length data
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:fixed_char_min())
+ ++ "')"),
+ %% Select data
+ {selected, Fields,[{"a"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:fixed_char_min()
+ + 1))
+ ++ "')"),
+ ok.
+%%-------------------------------------------------------------------------
+
+char_fixed_upper_limit(doc) ->
+ ["Tests fixed length char data type upper boundaries."];
+char_fixed_upper_limit(suite) ->
+ [];
+char_fixed_upper_limit(Config) when is_list(Config) ->
+
+ case ?RDBMS of
+ postgres ->
+ {skip, "Limit unknown"};
+ _ ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Upper limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ ?RDBMS:fixed_char_max())),
+ {updated, _} =
+ odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ ?RDBMS:fixed_char_max())
+ ++ "')"),
+ %% Select data
+ {selected, Fields, [{CharStr}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = length(CharStr) == ?RDBMS:fixed_char_max(),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:fixed_char_max()
+ + 1))
+ ++ "')"),
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ %% Above limit
+ {error, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ (?RDBMS:fixed_char_max() + 1))),
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+char_fixed_padding(doc) ->
+ ["Tests that data that is shorter than the given size is padded "
+ "with blanks."];
+char_fixed_padding(suite) ->
+ [];
+char_fixed_padding(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Data should be padded with blanks
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ ?RDBMS:fixed_char_max())),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ ?RDBMS:fixed_char_min())
+ ++ "')"),
+
+ {selected, Fields, [{CharStr}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = length(CharStr) == ?RDBMS:fixed_char_max(),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+%%-------------------------------------------------------------------------
+
+varchar_lower_limit(doc) ->
+ ["Tests variable length char data type lower boundaries."];
+varchar_lower_limit(suite) ->
+ [];
+varchar_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Below limit
+ {error, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_min() - 1)),
+ %% Lower limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_min())),
+
+ %% Right length data
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:var_char_min())
+ ++ "')"),
+ %% Select data
+ {selected, Fields, [{"a"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:var_char_min()+1))
+ ++ "')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+varchar_upper_limit(doc) ->
+ ["Tests variable length char data type upper boundaries."];
+varchar_upper_limit(suite) ->
+ [];
+varchar_upper_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ case ?RDBMS of
+ oracle ->
+ {skip, "Known bug in database"};
+ postgres ->
+ {skip, "Limit unknown"};
+ _ ->
+ %% Upper limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_max())),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ ?RDBMS:var_char_max())
+ ++ "')"),
+
+ {selected, Fields, [{CharStr}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = length(CharStr) == ?RDBMS:var_char_max(),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:var_char_max()+1))
+ ++ "')"),
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ %% Above limit
+ {error, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ (?RDBMS:var_char_max() + 1))),
+ ok
+ end.
+%%-------------------------------------------------------------------------
+
+varchar_no_padding(doc) ->
+ ["Tests that data that is shorter than the given max size is not padded "
+ "with blanks."];
+varchar_no_padding(suite) ->
+ [];
+varchar_no_padding(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Data should NOT be padded with blanks
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_max())),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:var_char_min())
+ ++ "')"),
+
+ {selected, Fields, [{CharStr}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = length(CharStr) /= ?RDBMS:var_char_max(),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+text_lower_limit(doc) ->
+ ["Tests 'long' char data type lower boundaries."];
+text_lower_limit(suite) ->
+ [];
+text_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_text_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:text_min())
+ ++ "')"),
+
+ {selected, Fields, [{"a"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+text_upper_limit(doc) ->
+ [];
+text_upper_limit(suite) ->
+ [];
+text_upper_limit(Config) when is_list(Config) ->
+
+ {skip,"Consumes too much resources" }.
+%% Ref = ?config(connection_ref, Config),
+%% Table = ?config(tableName, Config),
+
+%% {updated, _} = % Value == 0 || -1 driver dependent!
+%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+%% ?RDBMS:create_text_table()),
+%% {updated, _} =
+%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+%% "'" ++ string:chars($a, ?RDBMS:text_max())
+%% ++ "')"),
+
+%% {selected, Fields, [{CharStr}]} =
+%% odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+%% length(CharStr) == ?RDBMS:text_max(),
+%% ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+%% {error, _} =
+%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+%% "'" ++ string:chars($a, (?RDBMS:text_max()+1))
+%% ++ "')"),
+%% ok.
+
+%%-------------------------------------------------------------------------
+
+binary_char_fixed_lower_limit(doc) ->
+ ["Tests fixed length char data type lower boundaries."];
+binary_char_fixed_lower_limit(suite) ->
+ [];
+binary_char_fixed_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Below limit
+ {error, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ (?RDBMS:fixed_char_min() - 1))),
+ %% Lower limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ ?RDBMS:fixed_char_min())),
+
+ %% Right length data
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:fixed_char_min())
+ ++ "')"),
+ %% Select data
+ {selected, Fields,[{<<"a">>}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:fixed_char_min()
+ + 1))
+ ++ "')"),
+ ok.
+%%-------------------------------------------------------------------------
+
+binary_char_fixed_upper_limit(doc) ->
+ ["Tests fixed length char data type upper boundaries."];
+binary_char_fixed_upper_limit(suite) ->
+ [];
+binary_char_fixed_upper_limit(Config) when is_list(Config) ->
+
+ case ?RDBMS of
+ postgres ->
+ {skip, "Limit unknown"};
+ _ ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Upper limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ ?RDBMS:fixed_char_max())),
+ {updated, _} =
+ odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ ?RDBMS:fixed_char_max())
+ ++ "')"),
+ %% Select data
+ {selected, Fields, [{CharBin}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = size(CharBin) == ?RDBMS:fixed_char_max(),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:fixed_char_max()
+ + 1))
+ ++ "')"),
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ %% Above limit
+ {error, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ (?RDBMS:fixed_char_max() + 1))),
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+binary_char_fixed_padding(doc) ->
+ ["Tests that data that is shorter than the given size is padded "
+ "with blanks."];
+binary_char_fixed_padding(suite) ->
+ [];
+binary_char_fixed_padding(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Data should be padded with blanks
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_fixed_char_table(
+ ?RDBMS:fixed_char_max())),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ ?RDBMS:fixed_char_min())
+ ++ "')"),
+
+ {selected, Fields, [{CharBin}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = size(CharBin) == ?RDBMS:fixed_char_max(),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+%%-------------------------------------------------------------------------
+
+binary_varchar_lower_limit(doc) ->
+ ["Tests variable length char data type lower boundaries."];
+binary_varchar_lower_limit(suite) ->
+ [];
+binary_varchar_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Below limit
+ {error, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_min() - 1)),
+ %% Lower limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_min())),
+
+ %% Right length data
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:var_char_min())
+ ++ "')"),
+ %% Select data
+ {selected, Fields, [{<<"a">>}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:var_char_min()+1))
+ ++ "')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+binary_varchar_upper_limit(doc) ->
+ ["Tests variable length char data type upper boundaries."];
+binary_varchar_upper_limit(suite) ->
+ [];
+binary_varchar_upper_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ case ?RDBMS of
+ oracle ->
+ {skip, "Known bug in database"};
+ postgres ->
+ {skip, "Limit unknown"};
+ _ ->
+ %% Upper limit
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_max())),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ ?RDBMS:var_char_max())
+ ++ "')"),
+
+ {selected, Fields, [{CharBin}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = size(CharBin) == ?RDBMS:var_char_max(),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Too long data
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a,
+ (?RDBMS:var_char_max()+1))
+ ++ "')"),
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ %% Above limit
+ {error, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ (?RDBMS:var_char_max() + 1))),
+ ok
+ end.
+%%-------------------------------------------------------------------------
+
+binary_varchar_no_padding(doc) ->
+ ["Tests that data that is shorter than the given max size is not padded "
+ "with blanks."];
+binary_varchar_no_padding(suite) ->
+ [];
+binary_varchar_no_padding(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ %% Data should NOT be padded with blanks
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_var_char_table(
+ ?RDBMS:var_char_max())),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:var_char_min())
+ ++ "')"),
+
+ {selected, Fields, [{CharBin}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ true = size(CharBin) /= ?RDBMS:var_char_max(),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+binary_text_lower_limit(doc) ->
+ ["Tests 'long' char data type lower boundaries."];
+binary_text_lower_limit(suite) ->
+ [];
+binary_text_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_text_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ string:chars($a, ?RDBMS:text_min())
+ ++ "')"),
+
+ {selected, Fields, [{<<"a">>}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+binary_text_upper_limit(doc) ->
+ [];
+binary_text_upper_limit(suite) ->
+ [];
+binary_text_upper_limit(Config) when is_list(Config) ->
+
+ {skip,"Consumes too much resources" }.
+%% Ref = ?config(connection_ref, Config),
+%% Table = ?config(tableName, Config),
+
+%% {updated, _} = % Value == 0 || -1 driver dependent!
+%% odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+%% ?RDBMS:create_text_table()),
+%% {updated, _} =
+%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+%% "'" ++ string:chars($a, ?RDBMS:text_max())
+%% ++ "')"),
+
+%% {selected, Fields, [{CharBin}]} =
+%% odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+%% size(CharBin) == ?RDBMS:text_max(),
+%% ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+%% {error, _} =
+%% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+%% "'" ++ string:chars($a, (?RDBMS:text_max()+1))
+%% ++ "')"),
+%% ok.
+
+
+%%-------------------------------------------------------------------------
+
+
+%%-------------------------------------------------------------------------
+
+tiny_int_lower_limit(doc) ->
+ ["Tests integer of type tinyint."];
+tiny_int_lower_limit(suite) ->
+ [];
+tiny_int_lower_limit(Config) when is_list(Config) ->
+ case ?RDBMS of
+ postgres ->
+ {skip, "Type tiniyint not supported"};
+ _ ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_tiny_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:tiny_int_min())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:tiny_int_min_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:tiny_int_min()
+ - 1)
+ ++ "')"),
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+tiny_int_upper_limit(doc) ->
+ ["Tests integer of type tinyint."];
+tiny_int_upper_limit(suite) ->
+ [];
+tiny_int_upper_limit(Config) when is_list(Config) ->
+ case ?RDBMS of
+ postgres ->
+ {skip, "Type tiniyint not supported"};
+ _ ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_tiny_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:tiny_int_max())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:tiny_int_max_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:tiny_int_max()
+ + 1)
+ ++ "')"),
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+small_int_lower_limit(doc) ->
+ ["Tests integer of type smallint."];
+small_int_lower_limit(suite) ->
+ [];
+small_int_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_small_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:small_int_min())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:small_int_min_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:small_int_min()
+ - 1)
+ ++ "')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+small_int_upper_limit(doc) ->
+ ["Tests integer of type smallint."];
+small_int_upper_limit(suite) ->
+ [];
+small_int_upper_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_small_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:small_int_max())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:small_int_max_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:small_int_max()
+ + 1)
+ ++ "')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+int_lower_limit(doc) ->
+ ["Tests integer of type int."];
+int_lower_limit(suite) ->
+ [];
+int_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:int_min())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:int_min_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:int_min() - 1)
+ ++ "')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+int_upper_limit(doc) ->
+ ["Tests integer of type int."];
+int_upper_limit(suite) ->
+ [];
+int_upper_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:int_max())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:int_max_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:int_max() + 1)
+ ++ "')"),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+big_int_lower_limit(doc) ->
+ ["Tests integer of type bigint"];
+big_int_lower_limit(suite) ->
+ [];
+big_int_lower_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_big_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:big_int_min())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:big_int_min_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:big_int_min()
+ - 1)
+ ++ "')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+big_int_upper_limit(doc) ->
+ ["Tests integer of type bigint."];
+big_int_upper_limit(suite) ->
+ [];
+big_int_upper_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_big_int_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:big_int_max())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:big_int_max_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:big_int_max()
+ + 1)
+ ++ "')"),
+ ok.
+%%-------------------------------------------------------------------------
+
+bit_false(doc) ->
+ [""];
+bit_false(suite) ->
+ [];
+bit_false(Config) when is_list(Config) ->
+ case ?RDBMS of
+ oracle ->
+ {skip, "Not supported by driver"};
+ _ ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_bit_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:bit_false())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:bit_false_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(-1)
+ ++ "')"),
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+bit_true(doc) ->
+ [""];
+bit_true(suite) ->
+ [];
+bit_true(Config) when is_list(Config) ->
+ case ?RDBMS of
+ oracle ->
+ {skip, "Not supported by driver"};
+ _ ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_bit_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(?RDBMS:bit_true())
+ ++ "')"),
+
+ SelectResult = ?RDBMS:bit_true_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ integer_to_list(-1)
+ ++ "')"),
+ ok
+ end.
+
+%%-------------------------------------------------------------------------
+
+
+%%-------------------------------------------------------------------------
+float_lower_limit(doc) ->
+ [""];
+float_lower_limit(suite) ->
+ [];
+float_lower_limit(Config) when is_list(Config) ->
+
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_float_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ float_to_list(
+ ?RDBMS:float_min())
+ ++ "')"),
+ {selected,[_ColName],[{MinFloat}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ true = ?RDBMS:float_min() == MinFloat,
+
+ case ?RDBMS of
+ oracle ->
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_float_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "INSERT INTO " ++ Table ++" VALUES(" ++
+ ?RDBMS:float_underflow() ++ ")"),
+
+ SelectResult = ?RDBMS:float_zero_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table);
+ _ ->
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ ?RDBMS:float_underflow() ++ ")")
+ end,
+ ok.
+
+
+%%-------------------------------------------------------------------------
+float_upper_limit(doc) ->
+ [""];
+float_upper_limit(suite) ->
+ [];
+float_upper_limit(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_float_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ "'" ++ float_to_list(
+ ?RDBMS:float_max())
+ ++ "')"),
+
+
+ {selected,[_ColName],[{MaxFloat}]}
+ = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+
+ true = ?RDBMS:float_max() == MaxFloat,
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++
+ ?RDBMS:float_overflow() ++ ")"),
+ ok.
+
+%%-------------------------------------------------------------------------
+float_zero(doc) ->
+ ["Test the float value zero."];
+float_zero(suite) ->
+ [];
+float_zero(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_float_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES('0')"),
+
+ SelectResult = ?RDBMS:float_zero_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ok.
+%%-------------------------------------------------------------------------
+real_zero(doc) ->
+ ["Test the real value zero."];
+real_zero(suite) ->
+ [];
+real_zero(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ case ?RDBMS of
+ oracle ->
+ {skip, "Not supported in Oracle"};
+ _ ->
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_real_table()),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES('0')"),
+
+ SelectResult = ?RDBMS:real_zero_selected(),
+ SelectResult =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ok
+ end.
+%%-------------------------------------------------------------------------
+%%------------------------------------------------------------------------
+dec_long(doc) ->
+ [""];
+dec_long(suit) ->
+ [];
+dec_long(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (9,0))"),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields, [{2}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+%%------------------------------------------------------------------------
+dec_double(doc) ->
+ [""];
+dec_double(suit) ->
+ [];
+dec_double(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (10,0))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields, [{2.00000}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (15,0))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields1, [{2.00000}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields1),
+
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (15, 1))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields2, [{1.60000}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields2),
+ ok.
+
+%%------------------------------------------------------------------------
+dec_bignum(doc) ->
+ [""];
+dec_bignum(suit) ->
+ [];
+dec_bignum(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (16,0))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields, [{"2"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (16,1))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields1, [{"1.6"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields1),
+ ok.
+%%------------------------------------------------------------------------
+num_long(doc) ->
+ [""];
+num_long(suit) ->
+ [];
+num_long(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (9,0))"),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.5)"),
+
+ {selected, Fields, [{2}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+ ok.
+%%------------------------------------------------------------------------
+num_double(doc) ->
+ [""];
+num_double(suit) ->
+ [];
+num_double(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (10,0))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields, [{2.0000}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (15,0))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields1, [{2.0000}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields1),
+
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (15,1))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields2, [{1.6000}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields2),
+ ok.
+%%------------------------------------------------------------------------
+num_bignum(doc) ->
+ [""];
+num_bignum(suit) ->
+ [];
+num_bignum(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (16,0))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields, [{"2"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ %% Clean up
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ "(FIELD DECIMAL (16,1))"),
+ {updated, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1.6)"),
+
+ {selected, Fields1, [{"1.6"}]} =
+ odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table),
+ ["FIELD"] = odbc_test_lib:to_upper(Fields1),
+ ok.
+
+%%------------------------------------------------------------------------
+unicode(doc) ->
+ ["Test unicode support"];
+unicode(suit) ->
+ [];
+unicode(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_unicode_table()),
+
+ Latin1Data = ["���������",
+ "testasdf",
+ "Row 3",
+ "Row 4",
+ "Row 5",
+ "Row 6",
+ "Row 7",
+ "Row 8",
+ "Row 9",
+ "Row 10",
+ "Row 11",
+ "Row 12"],
+
+ case ?RDBMS of
+ sqlserver ->
+ w_char_support_win(Ref, Table, Latin1Data);
+ postgres ->
+ direct_utf8(Ref, Table, Latin1Data);
+ oracle ->
+ {skip, "not currently supported"}
+ end.
+
+w_char_support_win(Ref, Table, Latin1Data) ->
+ UnicodeIn = lists:map(fun(S) ->
+ unicode:characters_to_binary(S,latin1,{utf16,little})
+ end,
+ Latin1Data),
+
+ test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]),
+
+ {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)",
+ [{{sql_wvarchar,50},UnicodeIn}]),
+
+ {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
+
+ test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+
+ Result = lists:map(fun({Unicode}) ->
+ unicode:characters_to_list(Unicode,{utf16,little})
+ end,
+ UnicodeOut),
+ Latin1Data = Result.
+
+
+direct_utf8(Ref, Table, Latin1Data) ->
+ UnicodeIn = lists:map(fun(String) ->
+ unicode:characters_to_binary(String,latin1,utf8)
+ end,
+ Latin1Data),
+
+ test_server:format("UnicodeIn: ~p ~n",[UnicodeIn]),
+ {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)",
+ [{{sql_varchar,50}, UnicodeIn}]),
+
+ {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table),
+
+ test_server:format("UnicodeOut: ~p~n", [UnicodeOut]),
+
+ Result = lists:map(fun({Char}) ->
+ unicode:characters_to_list(Char,utf8)
+ end, UnicodeOut),
+
+ test_server:format("Result: ~p ~n", [Result]),
+
+ Latin1Data = Result.
+
+%%------------------------------------------------------------------------
+timestamp(doc) ->
+ [""];
+timestamp(suit) ->
+ [];
+timestamp(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_timestamp_table()),
+
+ Data = [calendar:local_time(),
+ {{2009,6,17},{20,54,59}},
+ {{2009,6,18},{20,54,59}},
+ {{2009,6,19},{20,54,59}},
+ {{2009,6,20},{20,54,59}},
+ {{2009,6,21},{20,54,59}}],
+
+ {updated, _} = odbc:param_query(Ref,"INSERT INTO " ++ Table ++ "(FIELD) values(?)",
+ [{sql_timestamp,Data}]),
+
+ %%% Crate list or database table rows
+ TimeStamps = lists:map(fun(Value) -> {Value} end, Data),
+
+ {selected,_, TimeStamps} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table).
diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl
new file mode 100644
index 0000000000..8b8d1e7a40
--- /dev/null
+++ b/lib/odbc/test/odbc_query_SUITE.erl
@@ -0,0 +1,1453 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(odbc_query_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include("test_server_line.hrl").
+-include("odbc_test.hrl").
+
+%%--------------------------------------------------------------------
+%% all(Arg) -> [Doc] | [Case] | {skip, Comment}
+%% Arg - doc | suite
+%% Doc - string()
+%% Case - atom()
+%% Name of a test case function.
+%% Comment - string()
+%% Description: Returns documentation/test cases in this test suite
+%% or a skip tuple if the platform is not supported.
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case odbc_test_lib:odbc_check() of
+ ok ->
+ [sql_query, first, last, next, prev, select_count,
+ select_next, select_relative, select_absolute,
+ create_table_twice, delete_table_twice, duplicate_key,
+ not_connection_owner, no_result_set, query_error,
+ multiple_select_result_sets, multiple_mix_result_sets,
+ multiple_result_sets_error,
+ {group, parameterized_queries}, {group, describe_table},
+ delete_nonexisting_row];
+ Other -> {skip, Other}
+ end.
+
+groups() ->
+ [{parameterized_queries, [],
+ [{group, param_integers}, param_insert_decimal,
+ param_insert_numeric, {group, param_insert_string},
+ param_insert_float, param_insert_real,
+ param_insert_double, param_insert_mix, param_update,
+ param_delete, param_select]},
+ {param_integers, [],
+ [param_insert_tiny_int, param_insert_small_int,
+ param_insert_int, param_insert_integer]},
+ {param_insert_string, [],
+ [param_insert_char, param_insert_character,
+ param_insert_char_varying,
+ param_insert_character_varying]},
+ {describe_table, [],
+ [describe_integer, describe_string, describe_floating,
+ describe_dec_num, describe_no_such_table]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) when is_list(Config) ->
+ application:start(odbc),
+ [{tableName, odbc_test_lib:unique_table_name()}| Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ application:stop(odbc),
+ ok.
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(Case, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initiation before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_testcase(_Case, Config) ->
+ {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []),
+ Dog = test_server:timetrap(?default_timeout),
+ Temp = lists:keydelete(connection_ref, 1, Config),
+ NewConfig = lists:keydelete(watchdog, 1, Temp),
+ [{watchdog, Dog}, {connection_ref, Ref} | NewConfig].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(Case, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(_Case, Config) ->
+ Ref = ?config(connection_ref, Config),
+ ok = odbc:disconnect(Ref),
+ %% Clean up if needed
+ Table = ?config(tableName, Config),
+ {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), []),
+ odbc:sql_query(NewRef, "DROP TABLE " ++ Table),
+ odbc:disconnect(NewRef),
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Test cases starts here.
+%%-------------------------------------------------------------------------
+sql_query(doc)->
+ ["Test the common cases"];
+sql_query(suite) -> [];
+sql_query(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10))"),
+
+ {updated, Count} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ true = odbc_test_lib:check_row_count(1, Count),
+
+ InsertResult = ?RDBMS:insert_result(),
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {updated, NewCount} =
+ odbc:sql_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foo' WHERE ID = 1"),
+
+ true = odbc_test_lib:check_row_count(1, NewCount),
+
+ UpdateResult = ?RDBMS:update_result(),
+ UpdateResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {updated, NewCount1} = odbc:sql_query(Ref, "DELETE FROM " ++ Table ++
+ " WHERE ID = 1"),
+
+ true = odbc_test_lib:check_row_count(1, NewCount1),
+
+ {selected, Fields, []} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["ID","DATA"] = odbc_test_lib:to_upper(Fields),
+ ok.
+
+%%-------------------------------------------------------------------------
+select_count(doc) ->
+ ["Tests select_count/[2,3]'s timeout, "
+ " select_count's functionality will be better tested by other tests "
+ " such as first."];
+select_count(sute) -> [];
+select_count(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ true = odbc_test_lib:check_row_count(1, Count),
+ {ok, _} =
+ odbc:select_count(Ref, "SELECT * FROM " ++ Table, ?TIMEOUT),
+ {'EXIT', {function_clause, _}} =
+ (catch odbc:select_count(Ref, "SELECT * FROM ", -1)),
+ ok.
+%%-------------------------------------------------------------------------
+first(doc) ->
+ ["Tests first/[1,2]"];
+first(suite) -> [];
+first(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ true = odbc_test_lib:check_row_count(1, Count),
+ {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ true = odbc_test_lib:check_row_count(1, NewCount),
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+
+ FirstResult = ?RDBMS:selected_ID(1, first),
+ FirstResult = odbc:first(Ref),
+ FirstResult = odbc:first(Ref, ?TIMEOUT),
+ {'EXIT', {function_clause, _}} = (catch odbc:first(Ref, -1)),
+ ok.
+
+%%-------------------------------------------------------------------------
+last(doc) ->
+ ["Tests last/[1,2]"];
+last(suite) -> [];
+last(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ true = odbc_test_lib:check_row_count(1, Count),
+ {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ true = odbc_test_lib:check_row_count(1, NewCount),
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ LastResult = ?RDBMS:selected_ID(2, last),
+ LastResult = odbc:last(Ref),
+
+ LastResult = odbc:last(Ref, ?TIMEOUT),
+ {'EXIT', {function_clause, _}} = (catch odbc:last(Ref, -1)),
+ ok.
+
+%%-------------------------------------------------------------------------
+next(doc) ->
+ ["Tests next/[1,2]"];
+next(suite) -> [];
+next(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ true = odbc_test_lib:check_row_count(1, Count),
+ {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ true = odbc_test_lib:check_row_count(1, NewCount),
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ NextResult = ?RDBMS:selected_ID(1, next),
+ NextResult = odbc:next(Ref),
+ NextResult2 = ?RDBMS:selected_ID(2, next),
+ NextResult2 = odbc:next(Ref, ?TIMEOUT),
+ {'EXIT', {function_clause, _}} = (catch odbc:next(Ref, -1)),
+ ok.
+%%-------------------------------------------------------------------------
+prev(doc) ->
+ ["Tests prev/[1,2]"];
+prev(suite) -> [];
+prev(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, Count} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ true = odbc_test_lib:check_row_count(1, Count),
+ {updated, NewCount} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ true = odbc_test_lib:check_row_count(1, NewCount),
+
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ odbc:last(Ref), % Position cursor last so there will be a prev
+ PrevResult = ?RDBMS:selected_ID(1, prev),
+ PrevResult = odbc:prev(Ref),
+
+ odbc:last(Ref), % Position cursor last so there will be a prev
+ PrevResult = odbc:prev(Ref, ?TIMEOUT),
+ {'EXIT', {function_clause, _}} = (catch odbc:prev(Ref, -1)),
+ ok.
+%%-------------------------------------------------------------------------
+select_next(doc) ->
+ ["Tests select/[4,5] with CursorRelation = next "];
+select_next(suit) -> [];
+select_next(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(3)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(4)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(5)"),
+
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ SelectResult1 = ?RDBMS:selected_next_N(1),
+ SelectResult1 = odbc:select(Ref, next, 3),
+
+ %% Test that selecting stops at the end of the result set
+ SelectResult2 = ?RDBMS:selected_next_N(2),
+ SelectResult2 = odbc:select(Ref, next, 3, ?TIMEOUT),
+ {'EXIT',{function_clause, _}} =
+ (catch odbc:select(Ref, next, 2, -1)),
+
+ %% If you try fetching data beyond the the end of result set,
+ %% you get an empty list.
+ {selected, Fields, []} = odbc:select(Ref, next, 1),
+
+ ["ID"] = odbc_test_lib:to_upper(Fields),
+ ok.
+
+%%-------------------------------------------------------------------------
+select_relative(doc) ->
+ ["Tests select/[4,5] with CursorRelation = relative "];
+select_relative(suit) -> [];
+select_relative(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(3)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(4)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(5)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(6)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(7)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(8)"),
+
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ SelectResult1 = ?RDBMS:selected_relative_N(1),
+ SelectResult1 = odbc:select(Ref, {relative, 2}, 3),
+
+ %% Test that selecting stops at the end of the result set
+ SelectResult2 = ?RDBMS:selected_relative_N(2),
+ SelectResult2 = odbc:select(Ref, {relative, 3}, 3, ?TIMEOUT),
+ {'EXIT',{function_clause, _}} =
+ (catch odbc:select(Ref, {relative, 3} , 2, -1)),
+ ok.
+
+%%-------------------------------------------------------------------------
+select_absolute(doc) ->
+ ["Tests select/[4,5] with CursorRelation = absolute "];
+select_absolute(suit) -> [];
+select_absolute(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer)"),
+
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(3)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(4)"),
+ {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(5)"),
+ {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table),
+
+ SelectResult1 = ?RDBMS:selected_absolute_N(1),
+ SelectResult1 = odbc:select(Ref, {absolute, 1}, 3),
+
+ %% Test that selecting stops at the end of the result set
+ SelectResult2 = ?RDBMS:selected_absolute_N(2),
+ SelectResult2 = odbc:select(Ref, {absolute, 1}, 6, ?TIMEOUT),
+ {'EXIT',{function_clause, _}} =
+ (catch odbc:select(Ref, {absolute, 1}, 2, -1)),
+ ok.
+
+%%-------------------------------------------------------------------------
+create_table_twice(doc) ->
+ ["Test what happens if you try to create the same table twice."];
+create_table_twice(suite) -> [];
+create_table_twice(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10))"),
+ {error, Error} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10))"),
+ is_driver_error(Error),
+ ok.
+
+%%-------------------------------------------------------------------------
+delete_table_twice(doc) ->
+ ["Test what happens if you try to delete the same table twice."];
+delete_table_twice(suite) -> [];
+delete_table_twice(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10))"),
+ {updated, _} = odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+ {error, Error} = odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+ is_driver_error(Error),
+ ok.
+
+%-------------------------------------------------------------------------
+duplicate_key(doc) ->
+ ["Test what happens if you try to use the same key twice"];
+duplicate_key(suit) -> [];
+duplicate_key(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA char(10), PRIMARY KEY(ID))"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ {error, Error} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'foo')"),
+ is_driver_error(Error),
+ ok.
+
+%%-------------------------------------------------------------------------
+not_connection_owner(doc) ->
+ ["Test what happens if a process that did not start the connection"
+ " tries to acess it."];
+not_connection_owner(suite) -> [];
+not_connection_owner(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ spawn_link(?MODULE, not_owner, [self(), Ref, Table]),
+
+ receive
+ continue ->
+ ok
+ end.
+
+not_owner(Pid, Ref, Table) ->
+ {error, process_not_owner_of_odbc_connection} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ " (ID integer)"),
+
+ {error, process_not_owner_of_odbc_connection} =
+ odbc:disconnect(Ref),
+
+ Pid ! continue.
+
+%%-------------------------------------------------------------------------
+no_result_set(doc) ->
+ ["Tests what happens if you try to use a function that needs an "
+ "associated result set when there is none."];
+no_result_set(suite) -> [];
+no_result_set(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+
+ {error, result_set_does_not_exist} = odbc:first(Ref),
+ {error, result_set_does_not_exist} = odbc:last(Ref),
+ {error, result_set_does_not_exist} = odbc:next(Ref),
+ {error, result_set_does_not_exist} = odbc:prev(Ref),
+ {error, result_set_does_not_exist} = odbc:select(Ref, next, 1),
+ {error, result_set_does_not_exist} =
+ odbc:select(Ref, {absolute, 2}, 1),
+ {error, result_set_does_not_exist} =
+ odbc:select(Ref, {relative, 2}, 1),
+ ok.
+%%-------------------------------------------------------------------------
+query_error(doc) ->
+ ["Test what happens if there is an error in the query."];
+query_error(suite) ->
+ [];
+query_error(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA char(10), PRIMARY KEY(ID))"),
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"),
+
+ {error, _} =
+ odbc:sql_query(Ref, "INSERT ONTO " ++ Table ++ " VALUES(1,'bar')"),
+ ok.
+
+%%-------------------------------------------------------------------------
+multiple_select_result_sets(doc) ->
+ ["Test what happens if you have a batch of select queries."];
+multiple_select_result_sets(suite) ->
+ [];
+multiple_select_result_sets(Config) when is_list(Config) ->
+ case ?RDBMS of
+ sqlserver ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), "
+ "PRIMARY KEY(ID))"),
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1,'bar')"),
+
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2, 'foo')"),
+
+ MultipleResult = ?RDBMS:multiple_select(),
+
+ MultipleResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table ++
+ "; SELECT DATA FROM "++ Table ++
+ " WHERE ID=2"),
+ ok;
+ _ ->
+ {skip, "multiple result_set not supported"}
+ end.
+
+%%-------------------------------------------------------------------------
+multiple_mix_result_sets(doc) ->
+ ["Test what happens if you have a batch of select and other type of"
+ " queries."];
+multiple_mix_result_sets(suite) ->
+ [];
+multiple_mix_result_sets(Config) when is_list(Config) ->
+ case ?RDBMS of
+ sqlserver ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), "
+ "PRIMARY KEY(ID))"),
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1,'bar')"),
+
+ MultipleResult = ?RDBMS:multiple_mix(),
+
+ MultipleResult =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(2,'foo'); UPDATE " ++ Table ++
+ " SET DATA = 'foobar' WHERE ID =1;SELECT "
+ "* FROM "
+ ++ Table ++ ";DELETE FROM " ++ Table ++
+ " WHERE ID =1; SELECT DATA FROM " ++ Table),
+ ok;
+ _ ->
+ {skip, "multiple result_set not supported"}
+ end.
+%%-------------------------------------------------------------------------
+multiple_result_sets_error(doc) ->
+ ["Test what happens if one of the batched queries fails."];
+multiple_result_sets_error(suite) ->
+ [];
+multiple_result_sets_error(Config) when is_list(Config) ->
+ case ?RDBMS of
+ sqlserver ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID integer, DATA varchar(10), "
+ "PRIMARY KEY(ID))"),
+ {updated, 1} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1,'bar')"),
+
+ {error, Error} =
+ odbc:sql_query(Ref, "INSERT INTO " ++ Table ++
+ " VALUES(1,'foo'); SELECT * FROM " ++ Table),
+ is_driver_error(Error),
+
+ {error, NewError} =
+ odbc:sql_query(Ref, "SELECT * FROM "
+ ++ Table ++ ";INSERT INTO " ++ Table ++
+ " VALUES(1,'foo')"),
+ is_driver_error(NewError),
+ ok;
+ _ ->
+ {skip, "multiple result_set not supported"}
+ end.
+
+%%-------------------------------------------------------------------------
+
+%%-------------------------------------------------------------------------
+%%-------------------------------------------------------------------------
+param_insert_tiny_int(doc)->
+ ["Test insertion of tiny ints by parameterized queries."];
+param_insert_tiny_int(suite) ->
+ [];
+param_insert_tiny_int(Config) when is_list(Config) ->
+ case ?RDBMS of
+ sqlserver ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD TINYINT)"),
+
+ {updated, Count} =
+ odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_tinyint, [1, 2]}],
+ ?TIMEOUT),%Make sure to test timeout clause
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_tiny_int(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_tinyint, [1, "2"]}])),
+ ok;
+ _ ->
+ {skip, "Type tiniyint not supported"}
+ end.
+%%-------------------------------------------------------------------------
+param_insert_small_int(doc)->
+ ["Test insertion of small ints by parameterized queries."];
+param_insert_small_int(suite) ->
+ [];
+param_insert_small_int(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD SMALLINT)"),
+
+ {updated, Count} =
+ odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)", [{sql_smallint, [1, 2]}],
+ ?TIMEOUT), %% Make sure to test timeout clause
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_small_int(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_smallint, [1, "2"]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_int(doc)->
+ ["Test insertion of ints by parameterized queries."];
+param_insert_int(suite) ->
+ [];
+param_insert_int(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD INT)"),
+
+ Int = ?RDBMS:small_int_max() + 1,
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_integer, [1, Int]}]),
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_int(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_integer, [1, "2"]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_integer(doc)->
+ ["Test insertion of integers by parameterized queries."];
+param_insert_integer(suite) ->
+ [];
+param_insert_integer(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD INTEGER)"),
+
+ Int = ?RDBMS:small_int_max() + 1,
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_integer, [1, Int]}]),
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_int(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_integer, [1, 2.3]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_decimal(doc)->
+ ["Test insertion of decimal numbers by parameterized queries."];
+param_insert_decimal(suite) ->
+ [];
+param_insert_decimal(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD DECIMAL (3,0))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_decimal, 3, 0}, [1, 2]}]),
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_decimal(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_decimal, 3, 0}, [1, "2"]}])),
+
+
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD DECIMAL (3,1))"),
+
+ {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_decimal, 3, 1}, [0.25]}]),
+ true = odbc_test_lib:check_row_count(1, NewCount),
+
+ {selected, Fields, [{Value}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fields),
+
+ odbc_test_lib:match_float(Value, 0.3, 0.01),
+
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_numeric(doc)->
+ ["Test insertion of numeric numbers by parameterized queries."];
+param_insert_numeric(suite) ->
+ [];
+param_insert_numeric(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD NUMERIC (3,0))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_numeric,3,0}, [1, 2]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_numeric(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_decimal, 3, 0}, [1, "2"]}])),
+
+ odbc:sql_query(Ref, "DROP TABLE " ++ Table),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD NUMERIC (3,1))"),
+
+ {updated, NewCount} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_numeric, 3, 1}, [0.25]}]),
+
+ true = odbc_test_lib:check_row_count(1, NewCount),
+
+ {selected, Fileds, [{Value}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ odbc_test_lib:match_float(Value, 0.3, 0.01),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+%%-------------------------------------------------------------------------
+param_insert_char(doc)->
+ ["Test insertion of fixed length string by parameterized queries."];
+param_insert_char(suite) ->
+ [];
+param_insert_char(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD CHAR (10))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_char, 10},
+ ["foofoofoof", "0123456789"]}]),
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ {selected,Fileds,[{"foofoofoof"}, {"0123456789"}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_char, 10},
+ ["foo", "01234567890"]}]),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_char, 10}, ["1", 2.3]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_character(doc)->
+ ["Test insertion of fixed length string by parameterized queries."];
+param_insert_character(suite) ->
+ [];
+param_insert_character(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD CHARACTER (10))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_char, 10},
+ ["foofoofoof", "0123456789"]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ {selected, Fileds, [{"foofoofoof"}, {"0123456789"}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_char, 10},
+ ["foo", "01234567890"]}]),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_char, 10}, ["1", 2]}])),
+ ok.
+
+%%------------------------------------------------------------------------
+param_insert_char_varying(doc)->
+ ["Test insertion of variable length strings by parameterized queries."];
+param_insert_char_varying(suite) ->
+ [];
+param_insert_char_varying(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD CHAR VARYING(10))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_varchar, 10},
+ ["foo", "0123456789"]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ {selected, Fileds, [{"foo"}, {"0123456789"}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_varchar, 10},
+ ["foo", "01234567890"]}]),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_varchar, 10}, ["1", 2.3]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_character_varying(doc)->
+ ["Test insertion of variable length strings by parameterized queries."];
+param_insert_character_varying(suite) ->
+ [];
+param_insert_character_varying(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD CHARACTER VARYING(10))"),
+
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_varchar, 10},
+ ["foo", "0123456789"]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ {selected, Fileds, [{"foo"}, {"0123456789"}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ {error, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_varchar, 10},
+ ["foo", "01234567890"]}]),
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_varchar, 10}, ["1", 2]}])),
+ ok.
+%%-------------------------------------------------------------------------
+param_insert_float(doc)->
+ ["Test insertion of floats by parameterized queries."];
+param_insert_float(suite) ->
+ [];
+param_insert_float(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD FLOAT(5))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_float,5}, [1.3, 1.2]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ {selected, Fileds, [{Float1},{Float2}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ case (odbc_test_lib:match_float(Float1, 1.3, 0.000001) and
+ odbc_test_lib:match_float(Float2, 1.2, 0.000001)) of
+ true ->
+ ok;
+ false ->
+ test_server:fail(float_numbers_do_not_match)
+ end,
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{{sql_float, 5}, [1.0, "2"]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_real(doc)->
+ ["Test insertion of real numbers by parameterized queries."];
+param_insert_real(suite) ->
+ [];
+param_insert_real(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD REAL)"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_real, [1.3, 1.2]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ %_InsertResult = ?RDBMS:param_select_real(),
+
+ {selected, Fileds, [{Real1},{Real2}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ case (odbc_test_lib:match_float(Real1, 1.3, 0.000001) and
+ odbc_test_lib:match_float(Real2, 1.2, 0.000001)) of
+ true ->
+ ok;
+ false ->
+ test_server:fail(real_numbers_do_not_match)
+ end,
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_real,[1.0, "2"]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_double(doc)->
+ ["Test insertion of doubles by parameterized queries."];
+param_insert_double(suite) ->
+ [];
+param_insert_double(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (FIELD DOUBLE PRECISION)"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_double, [1.3, 1.2]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ {selected, Fileds, [{Double1},{Double2}]} =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+
+ ["FIELD"] = odbc_test_lib:to_upper(Fileds),
+
+ case (odbc_test_lib:match_float(Double1, 1.3, 0.000001) and
+ odbc_test_lib:match_float(Double2, 1.2, 0.000001)) of
+ true ->
+ ok;
+ false ->
+ test_server:fail(double_numbers_do_not_match)
+ end,
+
+ {'EXIT',{badarg,odbc,param_query,'Params'}} =
+ (catch odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(FIELD) VALUES(?)",
+ [{sql_double, [1.0, "2"]}])),
+ ok.
+
+%%-------------------------------------------------------------------------
+param_insert_mix(doc)->
+ ["Test insertion of a mixture of datatypes by parameterized queries."];
+param_insert_mix(suite) ->
+ [];
+param_insert_mix(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID INTEGER, DATA CHARACTER VARYING(10),"
+ " PRIMARY KEY(ID))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2]},
+ {{sql_varchar, 10}, ["foo", "bar"]}]),
+
+ true = odbc_test_lib:check_row_count(2, Count),
+
+ InsertResult = ?RDBMS:param_select_mix(),
+
+ InsertResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+ ok.
+%%-------------------------------------------------------------------------
+param_update(doc)->
+ ["Test parameterized update query."];
+param_update(suite) ->
+ [];
+param_update(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID INTEGER, DATA CHARACTER VARYING(10),"
+ " PRIMARY KEY(ID))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2, 3]},
+ {{sql_varchar, 10},
+ ["foo", "bar", "baz"]}]),
+
+ true = odbc_test_lib:check_row_count(3, Count),
+
+ {updated, NewCount} = odbc:param_query(Ref, "UPDATE " ++ Table ++
+ " SET DATA = 'foobar' WHERE ID = ?",
+ [{sql_integer, [1, 2]}]),
+
+ true = odbc_test_lib:check_row_count(2, NewCount),
+
+ UpdateResult = ?RDBMS:param_update(),
+
+ UpdateResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+ ok.
+
+%%-------------------------------------------------------------------------
+delete_nonexisting_row(doc) -> % OTP-5759
+ ["Make a delete...where with false conditions (0 rows deleted). ",
+ "This used to give an error message (see ticket OTP-5759)."];
+delete_nonexisting_row(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table
+ ++ " (ID INTEGER, DATA CHARACTER VARYING(10))"),
+ {updated, Count} =
+ odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2, 3]},
+ {{sql_varchar, 10}, ["foo", "bar", "baz"]}]),
+
+ true = odbc_test_lib:check_row_count(3, Count),
+
+ {updated, NewCount} =
+ odbc:sql_query(Ref, "DELETE FROM " ++ Table ++ " WHERE ID = 8"),
+
+ true = odbc_test_lib:check_row_count(0, NewCount),
+
+ {updated, _} =
+ odbc:sql_query(Ref, "DROP TABLE "++ Table),
+
+ ok.
+
+%%-------------------------------------------------------------------------
+param_delete(doc) ->
+ ["Test parameterized delete query."];
+param_delete(suite) ->
+ [];
+param_delete(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID INTEGER, DATA CHARACTER VARYING(10),"
+ " PRIMARY KEY(ID))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2, 3]},
+ {{sql_varchar, 10},
+ ["foo", "bar", "baz"]}]),
+ true = odbc_test_lib:check_row_count(3, Count),
+
+ {updated, NewCount} = odbc:param_query(Ref, "DELETE FROM " ++ Table ++
+ " WHERE ID = ?",
+ [{sql_integer, [1, 2]}]),
+
+ true = odbc_test_lib:check_row_count(2, NewCount),
+
+ UpdateResult = ?RDBMS:param_delete(),
+
+ UpdateResult =
+ odbc:sql_query(Ref, "SELECT * FROM " ++ Table),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+param_select(doc) ->
+ ["Test parameterized select query."];
+param_select(suite) ->
+ [];
+param_select(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (ID INTEGER, DATA CHARACTER VARYING(10),"
+ " PRIMARY KEY(ID))"),
+
+ {updated, Count} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++
+ "(ID, DATA) VALUES(?, ?)",
+ [{sql_integer, [1, 2, 3]},
+ {{sql_varchar, 10},
+ ["foo", "bar", "foo"]}]),
+
+ true = odbc_test_lib:check_row_count(3, Count),
+
+ SelectResult = ?RDBMS:param_select(),
+
+ SelectResult = odbc:param_query(Ref, "SELECT * FROM " ++ Table ++
+ " WHERE DATA = ?",
+ [{{sql_varchar, 10}, ["foo"]}]),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+%%-------------------------------------------------------------------------
+describe_integer(doc) ->
+ ["Test describe_table/[2,3] for integer columns."];
+describe_integer(suite) ->
+ [];
+describe_integer(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (int1 SMALLINT, int2 INT, int3 INTEGER)"),
+
+ Decs = ?RDBMS:describe_integer(),
+ %% Make sure to test timeout clause
+ Decs = odbc:describe_table(Ref, Table, ?TIMEOUT),
+ ok.
+
+%%-------------------------------------------------------------------------
+describe_string(doc) ->
+ ["Test describe_table/[2,3] for string columns."];
+describe_string(suite) ->
+ [];
+describe_string(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (str1 char(10), str2 character(10), "
+ "str3 CHAR VARYING(10), str4 "
+ "CHARACTER VARYING(10))"),
+
+ Decs = ?RDBMS:describe_string(),
+
+ Decs = odbc:describe_table(Ref, Table),
+ ok.
+
+%%-------------------------------------------------------------------------
+describe_floating(doc) ->
+ ["Test describe_table/[2,3] for floting columns."];
+describe_floating(suite) ->
+ [];
+describe_floating(Config) when is_list(Config) ->
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (f FLOAT(5), r REAL, "
+ "d DOUBLE PRECISION)"),
+
+ Decs = ?RDBMS:describe_floating(),
+
+ Decs = odbc:describe_table(Ref, Table),
+ ok.
+
+%%-------------------------------------------------------------------------
+describe_dec_num(doc) ->
+ ["Test describe_table/[2,3] for decimal and numerical columns"];
+describe_dec_num(suite) ->
+ [];
+describe_dec_num(Config) when is_list(Config) ->
+
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} =
+ odbc:sql_query(Ref,
+ "CREATE TABLE " ++ Table ++
+ " (dec DECIMAL(9,3), num NUMERIC(9,2))"),
+
+ Decs = ?RDBMS:describe_dec_num(),
+
+ Decs = odbc:describe_table(Ref, Table),
+ ok.
+
+
+%%-------------------------------------------------------------------------
+describe_timestamp(doc) ->
+ ["Test describe_table/[2,3] for tinmestap columns"];
+describe_timestamp(suite) ->
+ [];
+describe_timestamp(Config) when is_list(Config) ->
+
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {updated, _} = % Value == 0 || -1 driver dependent!
+ odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++
+ ?RDBMS:create_timestamp_table()),
+
+ Decs = ?RDBMS:describe_timestamp(),
+
+ Decs = odbc:describe_table(Ref, Table),
+ ok.
+
+%%-------------------------------------------------------------------------
+describe_no_such_table(doc) ->
+ ["Test what happens if you try to describe a table that does not exist."];
+describe_no_such_table(suite) ->
+ [];
+describe_no_such_table(Config) when is_list(Config) ->
+
+ Ref = ?config(connection_ref, Config),
+ Table = ?config(tableName, Config),
+
+ {error, _ } = odbc:describe_table(Ref, Table),
+ ok.
+
+%%-------------------------------------------------------------------------
+%% Internal functions
+%%-------------------------------------------------------------------------
+
+is_driver_error(Error) ->
+ case is_list(Error) of
+ true ->
+ test_server:format("Driver error ~p~n", [Error]),
+ ok;
+ false ->
+ test_server:fail(Error)
+ end.
diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl
new file mode 100644
index 0000000000..65b990133f
--- /dev/null
+++ b/lib/odbc/test/odbc_start_SUITE.erl
@@ -0,0 +1,153 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(odbc_start_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include("test_server_line.hrl").
+-include("odbc_test.hrl").
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ case code:which(odbc) of
+ non_existing ->
+ {skip, "No ODBC built"};
+ _ ->
+ [{tableName, odbc_test_lib:unique_table_name()} | Config]
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ok.
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initialization before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config0) ->
+ test_server:format("ODBCINI = ~p~n", [os:getenv("ODBCINI")]),
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = test_server:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case odbc_test_lib:odbc_check() of
+ ok -> [start];
+ Other -> {skip, Other}
+ end.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+
+start(doc) ->
+ ["Test start/stop of odbc"];
+start(suite) ->
+ [];
+start(Config) when is_list(Config) ->
+ {error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(), []),
+ odbc:start(),
+ case odbc:connect(?RDBMS:connection_string(), []) of
+ {ok, Ref0} ->
+ ok = odbc:disconnect(Ref0),
+ odbc:stop(),
+ {error,odbc_not_started} =
+ odbc:connect(?RDBMS:connection_string(), []),
+ start_odbc(transient),
+ start_odbc(permanent);
+ {error, odbc_not_started} ->
+ test_server:fail(start_failed);
+ Error ->
+ test_server:format("Connection failed: ~p~n", [Error]),
+ {skip, "ODBC is not properly setup"}
+ end.
+
+start_odbc(Type) ->
+ ok = odbc:start(Type),
+ case odbc:connect(?RDBMS:connection_string(), []) of
+ {ok, Ref} ->
+ ok = odbc:disconnect(Ref),
+ odbc:stop();
+ {error, odbc_not_started} ->
+ test_server:fail(start_failed)
+ end.
diff --git a/lib/odbc/test/odbc_test.hrl b/lib/odbc/test/odbc_test.hrl
new file mode 100644
index 0000000000..87f50043db
--- /dev/null
+++ b/lib/odbc/test/odbc_test.hrl
@@ -0,0 +1,37 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+
+% Default timetrap timeout (set in init_per_testcase).
+% This should be set relatively high (10-15 times the expected
+% max testcasetime).
+-define(default_timeout, ?t:minutes(10)).
+
+-define(RDBMS, case os:type() of
+ {unix, sunos} ->
+ postgres;
+ {unix,linux} ->
+ postgres;
+ {win32, _} ->
+ sqlserver
+ end).
+
+-define(TIMEOUT, 100000).
+
+
diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl
new file mode 100644
index 0000000000..012eb96e43
--- /dev/null
+++ b/lib/odbc/test/odbc_test_lib.erl
@@ -0,0 +1,77 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(odbc_test_lib).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("odbc_test.hrl").
+-include("test_server.hrl").
+
+unique_table_name() ->
+ lists:reverse(lists:foldl(fun($@, Acc) -> [$t, $A |Acc] ;
+ (X, Acc) -> [X |Acc] end,
+ [], atom_to_list(node()))).
+
+match_float(Float, Match, Delta) ->
+ (Float < Match + Delta) and (Float > Match - Delta).
+
+odbc_check() ->
+ case erlang:system_info(wordsize) of
+ 4 ->
+ case test_server:os_type() of
+ {unix, sunos} ->
+ ok;
+ {unix, linux} ->
+ ok;
+ {win32, _} ->
+ ok;
+ Other ->
+ lists:flatten(
+ io_lib:format("Platform not supported: ~w",
+ [Other]))
+ end;
+ Other ->
+ case os:type() of
+ {unix, linux} ->
+ ok;
+ Platform ->
+ lists:flatten(
+ io_lib:format("Word on platform ~w size"
+ " ~w not supported", [Other,
+ Platform]))
+ end
+ end.
+
+check_row_count(Count, Count) ->
+ test_server:format("Correct row count Count: ~p~n", [Count]),
+ true;
+check_row_count(_, undefined) ->
+ test_server:format("Undefined row count ~n", []),
+ true;
+check_row_count(Expected, Count) ->
+ test_server:format("Incorrect row count Expected ~p Got ~p~n",
+ [Expected, Count]),
+ false.
+
+to_upper(List) ->
+ lists:map(fun(Str) -> string:to_upper(Str) end, List).
diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl
new file mode 100644
index 0000000000..ebf6dbb6bf
--- /dev/null
+++ b/lib/odbc/test/oracle.erl
@@ -0,0 +1,246 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(oracle).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%-------------------------------------------------------------------------
+connection_string() ->
+ "DSN=Oracle8;UID=odbctest".
+
+%-------------------------------------------------------------------------
+insert_result() ->
+ {selected,["ID","DATA"],[{"1","bar"}]}.
+
+update_result() ->
+ {selected,["ID","DATA"],[{"1","foo"}]}.
+
+selected_ID(N, next) ->
+ {selected,["ID"],[{integer_to_list(N)}]};
+
+selected_ID(_, _) ->
+ {error, driver_does_not_support_function}.
+
+selected_next_N(1)->
+ {selected,["ID"],
+ [{"1"},
+ {"2"},
+ {"3"}]};
+
+selected_next_N(2)->
+ {selected,["ID"],
+ [{"4"},
+ {"5"}]}.
+
+selected_relative_N(_)->
+ {error, driver_does_not_support_function}.
+
+selected_absolute_N(_)->
+ {error, driver_does_not_support_function}.
+
+selected_list_rows() ->
+ {selected,["ID", "DATA"],[["1", "bar"],["2","foo"]]}.
+
+first_list_rows() ->
+ {error, driver_does_not_support_function}.
+last_list_rows() ->
+ {error, driver_does_not_support_function}.
+prev_list_rows() ->
+ {error, driver_does_not_support_function}.
+next_list_rows() ->
+ {selected,["ID","DATA"],[["1","bar"]]}.
+
+%% In case we get a better oracle driver that support this some day .....
+multiple_select()->
+ [{selected,["ID", "DATA"],[{"1", "bar"},{"2", "foo"}]},
+ {selected,["ID"],[{"foo"}]}].
+
+multiple_mix()->
+ [{updated, 1},{updated, 1},
+ {selected,["ID", "DATA"],[{"1", "foobar"},{"2", "foo"}]},
+ {updated, 1}, {selected,["DATA"],[{"foo"}]}].
+
+%-------------------------------------------------------------------------
+fixed_char_min() ->
+ 1.
+fixed_char_max() ->
+ 2000. %% Should be 255 acording to manual but empirical tests say 2000
+
+create_fixed_char_table(Size) ->
+ " (FIELD char(" ++ integer_to_list(Size) ++ "))".
+
+%-------------------------------------------------------------------------
+var_char_min() ->
+ 1.
+var_char_max() ->
+ 2000.
+
+create_var_char_table(Size) ->
+ " (FIELD varchar2(" ++ integer_to_list(Size) ++ "))".
+
+%-------------------------------------------------------------------------
+text_min() ->
+ 1.
+text_max() ->
+ 2147483646. % 2147483647. %% 2^31 - 1
+
+create_text_table() ->
+ " (FIELD long)". %Oracle long is variable length char data
+
+%-------------------------------------------------------------------------
+create_unicode_table() ->
+ " (FIELD nvarchar(50))".
+
+%-------------------------------------------------------------------------
+create_timestamp_table() ->
+ " (FIELD DATETIME)".
+
+%-------------------------------------------------------------------------
+tiny_int_min() ->
+ -999.
+tiny_int_max() ->
+ 999.
+
+create_tiny_int_table() ->
+ " (FIELD number(3, 0))".
+
+tiny_int_min_selected() ->
+ {selected,["FIELD"],[{-999}]}.
+
+tiny_int_max_selected() ->
+ {selected,["FIELD"], [{999}]}.
+
+%-------------------------------------------------------------------------
+small_int_min() ->
+ -99999.
+small_int_max() ->
+ 99999.
+
+create_small_int_table() ->
+ " (FIELD number(5, 0))".
+
+small_int_min_selected() ->
+ {selected,["FIELD"],[{-99999}]}.
+
+small_int_max_selected() ->
+ {selected,["FIELD"], [{99999}]}.
+
+%-------------------------------------------------------------------------
+int_min() ->
+ -999999999.
+int_max() ->
+ 999999999.
+
+create_int_table() ->
+ " (FIELD number(9, 0))".
+
+int_min_selected() ->
+ {selected,["FIELD"],[{-999999999}]}.
+
+int_max_selected() ->
+ {selected,["FIELD"], [{999999999}]}.
+
+%-------------------------------------------------------------------------
+big_int_min() ->
+ -99999999999999999999999999999999999999.
+
+big_int_max() ->
+ 99999999999999999999999999999999999999.
+
+create_big_int_table() ->
+ " (FIELD number(38,0))".
+
+big_int_min_selected() ->
+ {selected,["FIELD"], [{"-99999999999999999999999999999999999999"}]}.
+
+big_int_max_selected() ->
+ {selected,["FIELD"], [{"99999999999999999999999999999999999999"}]}.
+
+%-------------------------------------------------------------------------
+float_min() ->
+ 1.40129846432481707e-45.
+
+float_max() ->
+ 3.40282346638528860e+38.
+
+create_float_table() ->
+ " (FIELD float(32))".
+
+float_underflow() ->
+ "'4.94065645841246544e-324'".
+float_overflow() ->
+ "'1.79769313486231570e+308'".
+
+float_zero_selected() ->
+ {selected,["FIELD"],[{0.00000e+0}]}.
+
+%-------------------------------------------------------------------------
+param_select_small_int() ->
+ {selected,["FIELD"],[{"1"}, {"2"}]}.
+
+param_select_int() ->
+ Int = small_int_max() + 1,
+ {selected,["FIELD"],[{"1"}, {integer_to_list(Int)}]}.
+
+param_select_decimal() ->
+ {selected,["FIELD"],[{1},{2}]}.
+
+param_select_numeric() ->
+ {selected,["FIELD"],[{1},{2}]}.
+
+param_select_float() ->
+ {selected,["FIELD"],[{1.30000},{1.20000}]}.
+
+param_select_real() ->
+ {selected,["FIELD"],[{1.30000},{1.20000}]}.
+
+param_select_double() ->
+ {selected,["FIELD"],[{1.30000},{1.20000}]}.
+
+param_select_mix() ->
+ {selected,["ID","DATA"],[{"1", "foo"}, {"2", "bar"}]}.
+
+param_update() ->
+ {selected,["ID","DATA"],[{"1", "foobar"}, {"2", "foobar"}, {"3", "baz"}]}.
+
+param_delete() ->
+ {selected,["ID","DATA"],[{"3", "baz"}]}.
+
+param_select() ->
+ {selected,["ID","DATA"],[{"1", "foo"},{"3", "foo"}]}.
+
+%-------------------------------------------------------------------------
+describe_integer() ->
+ {ok,[{"INT1",{sql_decimal,38,0}},{"INT2",{sql_decimal,38,0}},
+ {"INT3",{sql_decimal,38,0}}]}.
+
+describe_string() ->
+ {ok,[{"STR1",{sql_char,10}},
+ {"STR2",{sql_char,10}},
+ {"STR3",{sql_varchar,10}},
+ {"STR4",{sql_varchar,10}}]}.
+
+describe_floating() ->
+ {ok,[{"F",sql_double},{"R",sql_double},{"D",sql_double}]}.
+describe_dec_num() ->
+ {ok,[{"DEC",{sql_decimal,9,3}},{"NUM",{sql_decimal,9,2}}]}.
diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl
new file mode 100644
index 0000000000..169ca26e43
--- /dev/null
+++ b/lib/odbc/test/postgres.erl
@@ -0,0 +1,294 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(postgres).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%-------------------------------------------------------------------------
+connection_string() ->
+ case test_server:os_type() of
+ {unix, sunos} ->
+ "DSN=Postgres;UID=odbctest";
+ {unix, linux} ->
+ Size = erlang:system_info(wordsize),
+ linux_dist_connection_string(Size)
+ end.
+
+linux_dist_connection_string(4) ->
+ case linux_dist() of
+ "ubuntu" ->
+ "DSN=PostgresLinuxUbuntu;UID=odbctest";
+ _ ->
+ "DSN=PostgresLinux;UID=odbctest"
+ end;
+
+linux_dist_connection_string(_) ->
+ "DSN=PostgresLinux64;UID=odbctest".
+
+linux_dist() ->
+ case file:read_file("/etc/issue") of
+ {ok, Binary} ->
+ [Dist | _ ] = string:tokens(binary_to_list(Binary), " "),
+ string:to_lower(Dist);
+ {error, _} ->
+ other
+ end.
+
+
+%-------------------------------------------------------------------------
+insert_result() ->
+ {selected,["id","data"],[{1,"bar"}]}.
+
+update_result() ->
+ {selected,["id","data"],[{1,"foo"}]}.
+
+selected_ID(N, next) ->
+ {selected,["id"],[{N}]};
+
+selected_ID(_, _) ->
+ {error, driver_does_not_support_function}.
+
+selected_next_N(1)->
+ {selected,["id"],
+ [{1},
+ {2},
+ {3}]};
+
+selected_next_N(2)->
+ {selected,["id"],
+ [{4},
+ {5}]}.
+
+selected_relative_N(_)->
+ {error, driver_does_not_support_function}.
+
+selected_absolute_N(_)->
+ {error, driver_does_not_support_function}.
+
+selected_list_rows() ->
+ {selected,["id", "data"],[[1, "bar"],[2,"foo"]]}.
+
+first_list_rows() ->
+ {error, driver_does_not_support_function}.
+last_list_rows() ->
+ {error, driver_does_not_support_function}.
+prev_list_rows() ->
+ {error, driver_does_not_support_function}.
+next_list_rows() ->
+ {selected,["id","data"],[[1,"bar"]]}.
+
+%% In case we get a better postgres driver that support this some day .....
+multiple_select()->
+ [{selected,["id", "data"],[{1, "bar"},{2, "foo"}]},
+ {selected,["id"],[{"foo"}]}].
+
+multiple_mix()->
+ [{updated, 1},{updated, 1},
+ {selected,["id", "data"],[{1, "foobar"},{2, "foo"}]},
+ {updated, 1}, {selected,["data"],[{"foo"}]}].
+
+%-------------------------------------------------------------------------
+fixed_char_min() ->
+ 1.
+fixed_char_max() ->
+ 2000.
+
+create_fixed_char_table(Size) ->
+ " (FIELD char(" ++ integer_to_list(Size) ++ "))".
+
+%-------------------------------------------------------------------------
+var_char_min() ->
+ 1.
+var_char_max() ->
+ 2000.
+
+create_var_char_table(Size) ->
+ " (FIELD varchar(" ++ integer_to_list(Size) ++ "))".
+
+%-------------------------------------------------------------------------
+text_min() ->
+ 1.
+text_max() ->
+ 2147483646. % 2147483647. %% 2^31 - 1
+
+create_text_table() ->
+ " (FIELD text)".
+
+%-------------------------------------------------------------------------
+create_unicode_table() ->
+ " (FIELD text)".
+
+%-------------------------------------------------------------------------
+create_timestamp_table() ->
+ " (FIELD TIMESTAMP)".
+
+%-------------------------------------------------------------------------
+small_int_min() ->
+ -32768.
+small_int_max() ->
+ 32767.
+
+create_small_int_table() ->
+ " (FIELD smallint)".
+
+small_int_min_selected() ->
+ {selected,["field"],[{-32768}]}.
+
+small_int_max_selected() ->
+ {selected,["field"], [{32767}]}.
+
+%-------------------------------------------------------------------------
+int_min() ->
+ -2147483648.
+int_max() ->
+ 2147483647.
+
+create_int_table() ->
+ " (FIELD int)".
+
+int_min_selected() ->
+ {selected,["field"],[{-2147483648}]}.
+
+int_max_selected() ->
+ {selected,["field"], [{2147483647}]}.
+
+%-------------------------------------------------------------------------
+big_int_min() ->
+ -9223372036854775808.
+
+big_int_max() ->
+ 9223372036854775807.
+
+create_big_int_table() ->
+ " (FIELD bigint )".
+
+big_int_min_selected() ->
+ {selected,["field"], [{"-9223372036854775808"}]}.
+
+big_int_max_selected() ->
+ {selected,["field"], [{"9223372036854775807"}]}.
+
+%-------------------------------------------------------------------------
+bit_false() ->
+ 0.
+bit_true() ->
+ 1.
+
+create_bit_table() ->
+ " (FIELD bit)".
+
+bit_false_selected() ->
+ {selected,["field"],[{"0"}]}.
+
+bit_true_selected() ->
+ {selected,["field"], [{"1"}]}.
+
+%-------------------------------------------------------------------------
+float_min() ->
+ 1.79e-307.
+float_max() ->
+ 1.79e+308.
+
+create_float_table() ->
+ " (FIELD float)".
+
+float_underflow() ->
+ "1.80e-308".
+float_overflow() ->
+ "1.80e+308".
+
+float_zero_selected() ->
+ {selected,["field"],[{0.00000e+0}]}.
+
+%-------------------------------------------------------------------------
+real_min() ->
+ -3.40e+38.
+real_max() ->
+ 3.40e+38.
+
+real_underflow() ->
+ "-3.41e+38".
+
+real_overflow() ->
+ "3.41e+38".
+
+create_real_table() ->
+ " (FIELD real)".
+
+real_zero_selected() ->
+ {selected,["field"],[{0.00000e+0}]}.
+
+%-------------------------------------------------------------------------
+param_select_small_int() ->
+ {selected,["field"],[{1}, {2}]}.
+
+param_select_int() ->
+ Int = small_int_max() + 1,
+ {selected,["field"],[{1}, {Int}]}.
+
+param_select_decimal() ->
+ {selected,["field"],[{1},{2}]}.
+
+param_select_numeric() ->
+ {selected,["field"],[{1},{2}]}.
+
+param_select_float() ->
+ {selected,["field"],[{1.30000},{1.20000}]}.
+
+param_select_real() ->
+ {selected,["field"],[{1.30000},{1.20000}]}.
+
+param_select_double() ->
+ {selected,["field"],[{1.30000},{1.20000}]}.
+
+param_select_mix() ->
+ {selected,["id","data"],[{1, "foo"}, {2, "bar"}]}.
+
+param_update() ->
+ {selected,["id","data"],[{3, "baz"},{1, "foobar"}, {2, "foobar"}]}.
+
+param_delete() ->
+ {selected,["id","data"],[{3, "baz"}]}.
+
+param_select() ->
+ {selected,["id","data"],[{1, "foo"},{3, "foo"}]}.
+
+%-------------------------------------------------------------------------
+describe_integer() ->
+ {ok,[{"int1",sql_smallint},
+ {"int2",sql_integer},
+ {"int3",sql_integer}]}.
+
+describe_string() ->
+ {ok,[{"str1",{sql_char,10}},
+ {"str2",{sql_char,10}},
+ {"str3",{sql_varchar,10}},
+ {"str4",{sql_varchar,10}}]}.
+
+describe_floating() ->
+ {ok,[{"f",sql_real},{"r",sql_real},{"d",{sql_float,15}}]}.
+describe_dec_num() ->
+ {ok,[{"dec",{sql_numeric,9,3}},{"num",{sql_numeric,9,2}}]}.
+
+describe_timestamp() ->
+ {ok, [{"field", sql_timestamp}]}.
diff --git a/lib/odbc/test/sqlserver.erl b/lib/odbc/test/sqlserver.erl
new file mode 100644
index 0000000000..e3fe30e0bc
--- /dev/null
+++ b/lib/odbc/test/sqlserver.erl
@@ -0,0 +1,298 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(sqlserver).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+%-------------------------------------------------------------------------
+connection_string() ->
+ "DSN=sql-server;UID=odbctest;PWD=gurka".
+
+%-------------------------------------------------------------------------
+insert_result() ->
+ {selected,["ID","DATA"],[{1,"bar"}]}.
+
+update_result() ->
+ {selected,["ID","DATA"],[{1,"foo"}]}.
+
+selected_ID(N, _) ->
+ {selected,["ID"],[{N}]}.
+
+selected_next_N(1)->
+ {selected,["ID"],
+ [{1},
+ {2},
+ {3}]};
+
+selected_next_N(2)->
+ {selected,["ID"],
+ [{4},
+ {5}]}.
+
+selected_relative_N(1)->
+ {selected,["ID"],
+ [{2},
+ {3},
+ {4}]};
+
+selected_relative_N(2)->
+ {selected,["ID"],
+ [{7},
+ {8}]}.
+
+selected_absolute_N(1)->
+ {selected,["ID"],
+ [{1},
+ {2},
+ {3}]};
+
+selected_absolute_N(2)->
+ {selected,["ID"],
+ [{1},
+ {2},
+ {3},
+ {4},
+ {5}]}.
+
+selected_list_rows() ->
+ {selected,["ID", "DATA"],[[1, "bar"],[2,"foo"]]}.
+
+first_list_rows() ->
+ {selected,["ID", "DATA"],[[1, "bar"]]}.
+last_list_rows() ->
+ {selected,["ID", "DATA"],[[2, "foo"]]}.
+prev_list_rows() ->
+ {selected,["ID", "DATA"],[[1, "bar"]]}.
+next_list_rows() ->
+ {selected,["ID", "DATA"],[[2, "foo"]]}.
+
+multiple_select()->
+ [{selected,["ID", "DATA"],[{1, "bar"},{2, "foo"}]},
+ {selected,["DATA"],[{"foo"}]}].
+
+multiple_mix()->
+ [{updated, 1},{updated, 1},
+ {selected,["ID", "DATA"],[{1, "foobar"},{2, "foo"}]},
+ {updated, 1}, {selected,["DATA"],[{"foo"}]}].
+
+%-------------------------------------------------------------------------
+fixed_char_min() ->
+ 1.
+
+fixed_char_max() ->
+ 8000.
+
+create_fixed_char_table(Size) ->
+ " (FIELD char(" ++ integer_to_list(Size) ++ "))".
+
+%-------------------------------------------------------------------------
+var_char_min() ->
+ 1.
+var_char_max() ->
+ 8000.
+
+create_var_char_table(Size) ->
+ " (FIELD varchar(" ++ integer_to_list(Size) ++ "))".
+%-------------------------------------------------------------------------
+text_min() ->
+ 1.
+text_max() ->
+ 2147483647. %% 2^31 - 1
+
+create_text_table() ->
+ " (FIELD text)".
+
+%-------------------------------------------------------------------------
+create_unicode_table() ->
+ " (FIELD nvarchar(50))".
+
+%-------------------------------------------------------------------------
+create_timestamp_table() ->
+ " (FIELD DATETIME)".
+
+%-------------------------------------------------------------------------
+tiny_int_min() ->
+ 0.
+tiny_int_max() ->
+ 255.
+
+create_tiny_int_table() ->
+ " (FIELD tinyint)".
+
+tiny_int_min_selected() ->
+ {selected,["FIELD"],[{tiny_int_min()}]}.
+
+tiny_int_max_selected() ->
+ {selected,["FIELD"], [{tiny_int_max()}]}.
+
+%-------------------------------------------------------------------------
+small_int_min() ->
+ -32768. % -2^15
+small_int_max() ->
+ 32767. % 2^15-1
+
+create_small_int_table() ->
+ " (FIELD smallint)".
+
+small_int_min_selected() ->
+ {selected,["FIELD"],[{small_int_min()}]}.
+
+small_int_max_selected() ->
+ {selected,["FIELD"], [{small_int_max()}]}.
+
+%-------------------------------------------------------------------------
+int_min() ->
+ -2147483648. % -2^31
+int_max() ->
+ 2147483647. % 2^31-1
+
+create_int_table() ->
+ " (FIELD int)".
+
+int_min_selected() ->
+ {selected,["FIELD"],[{int_min()}]}.
+
+int_max_selected() ->
+ {selected,["FIELD"], [{int_max()}]}.
+
+%-------------------------------------------------------------------------
+big_int_min() ->
+ -9223372036854775808. % -2^63
+big_int_max() ->
+ 9223372036854775807. % 2^63-1
+
+create_big_int_table() ->
+ " (FIELD bigint)".
+
+big_int_min_selected() ->
+ {selected,["FIELD"],[{integer_to_list(big_int_min())}]}.
+
+big_int_max_selected() ->
+ {selected,["FIELD"], [{integer_to_list(big_int_max())}]}.
+
+%-------------------------------------------------------------------------
+bit_false() ->
+ 0.
+bit_true() ->
+ 1.
+
+create_bit_table() ->
+ " (FIELD bit)".
+
+bit_false_selected() ->
+ {selected,["FIELD"],[{false}]}.
+
+bit_true_selected() ->
+ {selected,["FIELD"], [{true}]}.
+%-------------------------------------------------------------------------
+float_min() ->
+ -1.79e+308.
+float_max() ->
+ 1.79e+308.
+
+float_underflow() ->
+ "'-1.80e+308'".
+
+float_overflow() ->
+ "'-1.80e+308'".
+
+create_float_table() ->
+ " (FIELD float)".
+
+float_zero_selected() ->
+ {selected,["FIELD"],[{0.00000e+0}]}.
+%-------------------------------------------------------------------------
+real_min() ->
+ -3.40e+38.
+real_max() ->
+ 3.40e+38.
+
+real_underflow() ->
+ -3.41e+38.
+
+real_overflow() ->
+ 3.41e+38.
+
+create_real_table() ->
+ " (FIELD real)".
+
+real_zero_selected() ->
+ {selected,["FIELD"],[{0.00000e+0}]}.
+%-------------------------------------------------------------------------
+param_select_tiny_int() ->
+ {selected,["FIELD"],[{1}, {2}]}.
+
+param_select_small_int() ->
+ {selected,["FIELD"],[{1}, {2}]}.
+
+param_select_int() ->
+ Int = small_int_max() + 1,
+ {selected,["FIELD"],[{1}, {Int}]}.
+
+param_select_decimal() ->
+ {selected,["FIELD"],[{1},{2}]}.
+
+param_select_numeric() ->
+ {selected,["FIELD"],[{1},{2}]}.
+
+param_select_float() ->
+ {selected,["FIELD"],[{1.30000},{1.20000}]}.
+
+param_select_real() ->
+ {selected,["FIELD"],[{1.30000},{1.20000}]}.
+
+param_select_double() ->
+ {selected,["FIELD"],[{1.30000},{1.20000}]}.
+
+param_select_mix() ->
+ {selected,["ID","DATA"],[{1, "foo"}, {2, "bar"}]}.
+
+param_update() ->
+ {selected,["ID","DATA"],[{1, "foobar"}, {2, "foobar"}, {3, "baz"}]}.
+
+param_delete() ->
+ {selected,["ID","DATA"],[{3, "baz"}]}.
+
+param_select() ->
+ {selected,["ID","DATA"],[{1, "foo"},{3, "foo"}]}.
+
+%-------------------------------------------------------------------------
+
+describe_integer() ->
+ {ok,[{"int1", sql_smallint},{"int2", sql_integer},
+ {"int3", sql_integer}]}.
+
+describe_string() ->
+ {ok,[{"str1",{sql_char,10}},
+ {"str2",{sql_char,10}},
+ {"str3",{sql_varchar,10}},
+ {"str4",{sql_varchar,10}}]}.
+
+describe_floating() ->
+ {ok,[{"f", sql_real},{"r", sql_real}, {"d", {sql_float, 53}}]}.
+
+describe_dec_num() ->
+ {ok,[{"dec",{sql_decimal,9,3}},{"num",{sql_numeric,9,2}}]}.
+
+describe_timestamp() ->
+ {ok, [{"field", sql_timestamp}]}.
diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk
index d813466437..42a51be33e 100644
--- a/lib/odbc/vsn.mk
+++ b/lib/odbc/vsn.mk
@@ -1,23 +1 @@
-ODBC_VSN = 2.10.8
-
-TICKETS = OTP-7452 OTP-8511
-
-TICKETS_2.10.6 = \
- OTP-8250 \
- OTP-8291
-
-TICKETS_2.10.5 = \
- OTP-7978
-
-TICKETS_2.10.4 = \
- OTP-7720 \
- OTP-7721
-
-TICKETS_2.10.3 = \
- OTP-7418
-TICKETS_2.10.2 = \
- OTP-7297
-TICKETS_2.10.1 = \
- OTP-7019 \
- OTP-7294 \
- OTP-7307
+ODBC_VSN = 2.10.10
diff --git a/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl b/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl
index 7792839e22..768653c898 100644
--- a/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl
+++ b/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -536,8 +536,15 @@ lookup(_, _Ctx) ->
receive_msg(Socket, Acc, Timeout) ->
receive
{tcp_closed, Socket} ->
- [_Header, Body] = re:split(Acc,"\r\n\r\n",[{return,list}]),
- Body;
+ case re:split(Acc,"\r\n\r\n",[{return,list}]) of
+ [_Header, Body] ->
+ Body;
+ What ->
+ orber:dbg("[~p] orber_cosnaming_utils:receive_msg();~n"
+ "HTTP server closed the connection before sending a complete reply: ~p.",
+ [?LINE, What], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end;
{tcp, Socket, Response} ->
receive_msg(Socket, Acc ++ Response, Timeout);
{tcp_error, Socket, Reason} ->
diff --git a/lib/orber/doc/src/CosNaming_BindingIterator.xml b/lib/orber/doc/src/CosNaming_BindingIterator.xml
index 83972a6009..2ae9871bb9 100644
--- a/lib/orber/doc/src/CosNaming_BindingIterator.xml
+++ b/lib/orber/doc/src/CosNaming_BindingIterator.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/CosNaming_NamingContextExt.xml b/lib/orber/doc/src/CosNaming_NamingContextExt.xml
index ef091bcd35..72e1f497ae 100644
--- a/lib/orber/doc/src/CosNaming_NamingContextExt.xml
+++ b/lib/orber/doc/src/CosNaming_NamingContextExt.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2000</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/Module_Interface.xml b/lib/orber/doc/src/Module_Interface.xml
index 85f19ccf49..7686419fdd 100644
--- a/lib/orber/doc/src/Module_Interface.xml
+++ b/lib/orber/doc/src/Module_Interface.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/any.xml b/lib/orber/doc/src/any.xml
index 6ba1a96561..390002669a 100644
--- a/lib/orber/doc/src/any.xml
+++ b/lib/orber/doc/src/any.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1998</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml
index a97ad65f0e..964ae3e92d 100644
--- a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml
+++ b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -445,7 +445,19 @@ void op(in myEnum a);</cell>
<section>
<title>Struct Data Type</title>
<p>A <c>struct</c> may have Basic, Template, Scoped Names and Constructed
- types as members.</p>
+ types as members. By using forward declaration we can define a recursive struct:</p>
+ <code type="none"><![CDATA[
+struct myStruct; // Forward declaration
+typedef sequence<myStruct> myStructSeq;
+struct myStruct {
+ myStructSeq chain;
+};
+
+// Deprecated definition (anonymous) not supported by IC
+struct myStruct {
+ sequence<myStruct> chain;
+};
+ ]]></code>
</section>
<section>
@@ -510,6 +522,25 @@ union LongUnion2 switch(long) {
default: boolean DefaultValue;
};
</code>
+ <p>In the same way as structs, unions can be recursive if forward
+ declaration is used (anonymous types is deprecated and not supported):</p>
+ <code type="none"><![CDATA[
+// Forward declaration
+union myUnion;
+typedef sequence<myUnion>myUnionSeq;
+union myUnion switch (long) {
+ case 1 : myUnionSeq chain;
+ default: boolean DefaultValue;
+};
+ ]]></code>
+
+ <note>
+ <p>Recursive types (union and struct) require Light IFR. I.e. the
+ IC option {light_ifr, true} is used and that Orber is configured in such a way that
+ Light IFR is activated. Recursive TypeCode is currently not supported, which is
+ why these cannot be encapsulated in an any data type.</p>
+ </note>
+
</section>
<warning>
<p>Every field in, for example, a struct must be initiated. Otherwise
@@ -890,7 +921,7 @@ attribute long RWAttribute;
object internal state with its object reference. The object internal state is
an Erlang term which has a format defined by the user.</p>
<note>
- <p>It is is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).</p>
+ <p>It is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).</p>
</note>
<p>A function call will invoke an operation. The first
parameter of the function should be the object reference and then
diff --git a/lib/orber/doc/src/corba_object.xml b/lib/orber/doc/src/corba_object.xml
index 810f06dbba..e0f9a9f503 100644
--- a/lib/orber/doc/src/corba_object.xml
+++ b/lib/orber/doc/src/corba_object.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/fixed.xml b/lib/orber/doc/src/fixed.xml
index 7c59071b49..8f23a32c8f 100644
--- a/lib/orber/doc/src/fixed.xml
+++ b/lib/orber/doc/src/fixed.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/intro_part.xml b/lib/orber/doc/src/intro_part.xml
index 3f429eeb87..bd783331f2 100644
--- a/lib/orber/doc/src/intro_part.xml
+++ b/lib/orber/doc/src/intro_part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml
index 1013d370e4..589123ef73 100644
--- a/lib/orber/doc/src/notes.xml
+++ b/lib/orber/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2010</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -33,7 +33,86 @@
</header>
<section>
+ <title>Orber 3.6.20</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Eliminated Dialyzer warnings when using exit or throw.</p>
+ <p>
+ Own Id: OTP-9050 Aux Id:</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Orber 3.6.19</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Partial support for recursive structs and unions.
+ Only available for the erl_corba backend and requires
+ that Light IFR is used. I.e. the IC option {light_ifr, true}
+ and that Orber is configured in such a way that Light IFR
+ is activated. Recursive TypeCode is currently not supported.</p>
+ <p>
+ Own Id: OTP-8868 Aux Id: seq11633</p>
+ </item>
+ </list>
+ </section>
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The SSL option {ssl_imp, old} was not used if ssl_generation was
+ set to 2. Only R14B was affected by this.</p>
+ <p>Own Id: OTP-8994 Aux Id: seq11747</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Orber 3.6.18</title>
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>A corbaloc http string could return an EXIT message, instead
+ of a system exception, if the HTTP server closed the socket
+ without returning a complete message. I.e. header and a body
+ containing a stringified IOR.</p>
+ <p>Own Id: OTP-8900 Aux Id: seq11704</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Orber 3.6.17</title>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>
+ Eliminated warnings for auto-imported BIF clashes.</p>
+ <p>
+ Own Id: OTP-8840</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
<title>Orber 3.6.16</title>
+
<section>
<title>Improvements and New Features</title>
<list type="bulleted">
@@ -41,16 +120,17 @@
<p>
Test suites published.</p>
<p>
- Own Id: OTP-8543 Aux Id:</p>
+ Own Id: OTP-8543O Aux Id:</p>
</item>
</list>
</section>
+
<section>
<title>Fixed Bugs and Malfunctions</title>
<list type="bulleted">
<item>
<p>Added missing trailing bracket to define in hrl-file.</p>
- <p>Own id: OTP-8489 Aux Id:</p>
+ <p>Own Id: OTP-8489 Aux Id:</p>
</item>
</list>
</section>
@@ -89,11 +169,11 @@
<list type="bulleted">
<item>
<p>Removed superfluous VT in the documentation.</p>
- <p>Own id: OTP-8353 Aux Id:</p>
+ <p>Own Id: OTP-8353 Aux Id:</p>
</item>
<item>
<p>Removed superfluous backslash in the documentation.</p>
- <p>Own id: OTP-8354 Aux Id:</p>
+ <p>Own Id: OTP-8354 Aux Id:</p>
</item>
</list>
</section>
@@ -125,7 +205,7 @@
<item>
<p>Obsolete guards, e.g. record vs is_record, has been changed
to avoid compiler warnings.</p>
- <p>Own id: OTP-7987</p>
+ <p>Own Id: OTP-7987</p>
</item>
</list>
</section>
@@ -143,7 +223,7 @@
Naming Service (INS) instead. INS is a part of the OMG
standard specification.</p>
<p>*** POTENTIAL INCOMPATIBILITY ***</p>
- <p>Own id: OTP-7906 Aux Id: seq11243</p>
+ <p>Own Id: OTP-7906 Aux Id: seq11243</p>
</item>
</list>
</section>
@@ -157,7 +237,7 @@
<list type="bulleted">
<item>
<p>Updated file headers.</p>
- <p>Own id: OTP-7837</p>
+ <p>Own Id: OTP-7837</p>
</item>
</list>
</section>
@@ -171,7 +251,7 @@
<list type="bulleted">
<item>
<p>Documentation source included in open source releases.</p>
- <p>Own id: OTP-7595</p>
+ <p>Own Id: OTP-7595</p>
</item>
</list>
</section>
@@ -185,11 +265,11 @@
<list type="bulleted">
<item>
<p>Updated file headers.</p>
- <p>Own id: OTP-7011</p>
+ <p>Own Id: OTP-7011</p>
</item>
<item>
<p>Now compliant with the new behavior of stdlib.</p>
- <p>Own id: OTP-7030 Aux Id: seq10827</p>
+ <p>Own Id: OTP-7030 Aux Id: seq10827</p>
</item>
</list>
</section>
diff --git a/lib/orber/doc/src/orber_acl.xml b/lib/orber/doc/src/orber_acl.xml
index 441001894a..c844b99702 100644
--- a/lib/orber/doc/src/orber_acl.xml
+++ b/lib/orber/doc/src/orber_acl.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2005</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/orber_tc.xml b/lib/orber/doc/src/orber_tc.xml
index 5d7f6368dd..a6141dd5bb 100644
--- a/lib/orber/doc/src/orber_tc.xml
+++ b/lib/orber/doc/src/orber_tc.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1998</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/doc/src/tools_debugging_part.xml b/lib/orber/doc/src/tools_debugging_part.xml
index edab8ad0d4..9aae7bc06f 100644
--- a/lib/orber/doc/src/tools_debugging_part.xml
+++ b/lib/orber/doc/src/tools_debugging_part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/orber/include/ifr_types.hrl b/lib/orber/include/ifr_types.hrl
index 144ec7f8a1..324b32bd4f 100644
--- a/lib/orber/include/ifr_types.hrl
+++ b/lib/orber/include/ifr_types.hrl
@@ -1,9 +1,9 @@
%%--------------------------------------------------------------------
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
diff --git a/lib/orber/src/cdr_decode.erl b/lib/orber/src/cdr_decode.erl
index 9d30098940..36ef6ce02f 100644
--- a/lib/orber/src/cdr_decode.erl
+++ b/lib/orber/src/cdr_decode.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -898,9 +898,13 @@ dec_sequence_struct(Version, Message, N, TypeCodeList, Len, ByteOrder, Buff, C,
{Seq, Rest2, Len2, NewC2} = dec_sequence_struct(Version, Rest1, N - 1, TypeCodeList, Len1, ByteOrder,
Buff, NewC, Name),
{[list_to_tuple([Name |Struct]) | Seq], Rest2, Len2, NewC2}.
-dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList, Len, _ByteOrder, _Buff, C, _Name) ->
+
+
+dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList,
+ Len, _ByteOrder, _Buff, C, _Name) ->
{[], Message, Len, C};
-dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, Len, ByteOrder, Buff, C, Name) ->
+dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList,
+ Len, ByteOrder, Buff, C, Name) when is_list(ElementList) ->
{Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Message, Len, ByteOrder, Buff, C),
Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default,
@@ -916,7 +920,20 @@ dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, Len, Byte
DiscrTC, Default, ElementList,
Len2, ByteOrder,
Buff, NewC3, Name),
- {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4}.
+ {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4};
+dec_sequence_union(Version, Message, N, _DiscrTC, _Default, Module,
+ Len, ByteOrder, Buff, C, Name) when is_atom(Module) ->
+ case catch Module:tc() of
+ {tk_union, _, _, DiscrTC, Default, ElementList} ->
+ dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList,
+ Len, ByteOrder, Buff, C, Name);
+ What ->
+ orber:dbg("[~p] ~p:dec_sequence_union(~p). Union module doesn't exist or incorrect.",
+ [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
+ corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
+ end.
+
+
%% A special case; when something is encapsulated (i.e. sent as octet-sequence)
%% we sometimes don not want the result to be converted to a list.
@@ -993,14 +1010,16 @@ dec_wstring(Version, Message, Len, ByteOrder, Buff, C) ->
%% Func: dec_union/9
%%-----------------------------------------------------------------
%% ## NEW IIOP 1.2 ##
-dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C) ->
+dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes,
+ Len, ByteOrder, Buff, C) ->
{Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C),
{Value, Rest2, Len2, NewC3} = dec_union(Version, Label, ElementList, Default,
Rest1, Len1, ByteOrder, Buff, NewC),
{{Name, Label, Value}, Rest2, Len2, NewC3};
-dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C) ->
+dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len,
+ ByteOrder, Buff, C) when is_list(ElementList) ->
{Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C),
Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default,
Rest1, Len1, ByteOrder, Buff, NewC),
@@ -1012,7 +1031,20 @@ dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, ByteOrde
X
end,
Name = ifrid_to_name(IFRId, ?IFR_UnionDef),
- {{Name, Label, Value}, Rest2, Len2, NewC3}.
+ {{Name, Label, Value}, Rest2, Len2, NewC3};
+dec_union(Version, IFRId, _, _DiscrTC, _Default, Module, Bytes, Len,
+ ByteOrder, Buff, C) when is_atom(Module) ->
+ case catch Module:tc() of
+ {tk_union, _, Name, DiscrTC, Default, ElementList} ->
+ dec_union(Version, IFRId, Name, DiscrTC, Default, ElementList, Bytes, Len,
+ ByteOrder, Buff, C);
+ What ->
+ orber:dbg("[~p] ~p:dec_union(~p). Union module doesn't exist or incorrect.",
+ [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
+ corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
+ end.
+
+
dec_union(_, _, [], Default, Message, Len, _, _Buff, C) when Default < 0 ->
{undefined, Message, Len, C};
@@ -1047,7 +1079,16 @@ dec_struct1(_, [], Message, Len, _ByteOrder, _, C) ->
dec_struct1(Version, [{_ElemName, ElemType} | TypeCodeList], Message, Len, ByteOrder, Buff, C) ->
{Element, Rest, Len1, NewC} = dec_type(ElemType, Version, Message, Len, ByteOrder, Buff, C),
{Struct, Rest1, Len2, NewC2} = dec_struct1(Version, TypeCodeList, Rest, Len1, ByteOrder, Buff, NewC),
- {[Element |Struct], Rest1, Len2, NewC2}.
+ {[Element |Struct], Rest1, Len2, NewC2};
+dec_struct1(Version, Module, Message, Len, ByteOrder, Buff, C) ->
+ case catch Module:tc() of
+ {tk_struct, _, _, TypeCodeList} ->
+ dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C);
+ What ->
+ orber:dbg("[~p] ~p:dec_struct1(~p). Struct module doesn't exist or incorrect.",
+ [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
+ corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
+ end.
ifrid_to_name([], Type) ->
orber:dbg("[~p] ~p:ifrid_to_name([], ~p). No Id supplied.",
@@ -1232,7 +1273,9 @@ get_user_exception_type(TypeId) ->
%%-----------------------------------------------------------------
dec_type_code(Version, Message, Len, ByteOrder, Buff, C) ->
{TypeNo, Message1, Len1, NewC} = dec_type('tk_ulong', Version, Message, Len, ByteOrder, Buff, C),
- dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC).
+ TC = dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC),
+ erase(orber_indirection),
+ TC.
%%-----------------------------------------------------------------
%% Func: dec_type_code/5
@@ -1441,13 +1484,22 @@ dec_type_code(33, Version, Message, Len, ByteOrder, Buff, C) ->
{"name", {'tk_string', 0}}]},
Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex),
{{'tk_local_interface', RepId, Name}, Message1, Len1, NewC};
-dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) -> %% placeholder
+dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) ->
{Indirection, Message1, Len1, NewC} =
dec_type('tk_long', Version, Message, Len, ByteOrder, Buff, C),
Position = C+Indirection,
- <<_:Position/binary, SubBuff/binary>> = Buff,
- {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position),
- {TC, Message1, Len1, NewC};
+ case put(orber_indirection, Position) of
+ Position ->
+%% {{'none', Indirection}, Message1, Len1, NewC};
+ %% Recursive TypeCode. Break the loop.
+ orber:dbg("[~p] cdr_decode:dec_type_code(~p); Recursive TC not supported.",
+ [?LINE,Position], ?DEBUG_LEVEL),
+ corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO});
+ _ ->
+ <<_:Position/binary, SubBuff/binary>> = Buff,
+ {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position),
+ {TC, Message1, Len1, NewC}
+ end;
dec_type_code(Type, _, _, _, _, _, _) ->
orber:dbg("[~p] cdr_decode:dec_type_code(~p); No match.",
[?LINE, Type], ?DEBUG_LEVEL),
diff --git a/lib/orber/src/cdr_encode.erl b/lib/orber/src/cdr_encode.erl
index 3ecb8833f5..eaf3c5b7dc 100644
--- a/lib/orber/src/cdr_encode.erl
+++ b/lib/orber/src/cdr_encode.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -815,11 +815,21 @@ enc_wstring(Env, String, MaxLength, Bytes, Len) ->
%%-----------------------------------------------------------------
%% Func: enc_union/5
%%-----------------------------------------------------------------
-enc_union(Env, {_, Label, Value}, DiscrTC, Default, TypeCodeList, Bytes, Len) ->
+enc_union(Env, {_, Label, Value}, DiscrTC, Default, TypeCodeList,
+ Bytes, Len) when is_list(TypeCodeList) ->
{ByteSequence, Len1} = enc_type(DiscrTC, Env, Label, Bytes, Len),
Label2 = stringify_enum(DiscrTC,Label),
enc_union2(Env, {Label2, Value},TypeCodeList, Default,
- ByteSequence, Len1, undefined).
+ ByteSequence, Len1, undefined);
+enc_union(Env, Value, _DiscrTC, _Default, Module, Bytes, Len) when is_atom(Module) ->
+ case catch Module:tc() of
+ {tk_union, _, _, DiscrTC, Default, ElementList} ->
+ enc_union(Env, Value, DiscrTC, Default, ElementList, Bytes, Len);
+ What ->
+ orber:dbg("[~p] ~p:enc_union(~p). Union module doesn't exist or incorrect.",
+ [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
+ corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
+ end.
enc_union2(_Env, _What, [], Default, Bytes, Len, _) when Default < 0 ->
{Bytes, Len};
@@ -840,9 +850,19 @@ stringify_enum(_, Label) ->
%%-----------------------------------------------------------------
%% Func: enc_struct/4
%%-----------------------------------------------------------------
-enc_struct(Env, Struct, TypeCodeList, Bytes, Len) ->
+enc_struct(Env, Struct, TypeCodeList, Bytes, Len) when is_list(TypeCodeList) ->
[_Name | StructList] = tuple_to_list(Struct),
- enc_struct1(Env, StructList, TypeCodeList, Bytes, Len).
+ enc_struct1(Env, StructList, TypeCodeList, Bytes, Len);
+enc_struct(Env, Struct, Module, Bytes, Len) ->
+ [Module | StructList] = tuple_to_list(Struct),
+ case catch Module:tc() of
+ {tk_struct, _, _, TypeCodeList} ->
+ enc_struct1(Env, StructList, TypeCodeList, Bytes, Len);
+ What ->
+ orber:dbg("[~p] ~p:enc_struct([], ~p). Struct module doesn't exist or incorrect.",
+ [?LINE, ?MODULE, What], ?DEBUG_LEVEL),
+ corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE})
+ end.
enc_struct1(_Env, [], [], Bytes, Len) ->
{Bytes, Len};
diff --git a/lib/orber/src/corba.erl b/lib/orber/src/corba.erl
index ea1363742c..ecec768544 100644
--- a/lib/orber/src/corba.erl
+++ b/lib/orber/src/corba.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -620,6 +620,8 @@ get_pid(Objkey) ->
%% Returns : Throws the exception.
%% Description:
%%----------------------------------------------------------------------
+%% To avoid dialyzer warnings due to the use of exit/throw.
+-spec raise(term()) -> no_return().
raise(E) ->
throw({'EXCEPTION', E}).
@@ -629,6 +631,8 @@ raise(E) ->
%% Returns : Throws the exception.
%% Description:
%%----------------------------------------------------------------------
+%% To avoid dialyzer warnings due to the use of exit/throw.
+-spec raise_with_state(term(), term()) -> no_return().
raise_with_state(E, State) ->
throw({reply, {'EXCEPTION', E}, State}).
diff --git a/lib/orber/src/orber.app.src b/lib/orber/src/orber.app.src
index fe911d65a4..88df4162b6 100644
--- a/lib/orber/src/orber.app.src
+++ b/lib/orber/src/orber.app.src
@@ -101,7 +101,7 @@
orber_iiop_insup, orber_init, orber_reqno,
orber_objkeyserver, orber_iiop_socketsup,
orber_iiop_pm, orber_env]},
- {applications, [stdlib, kernel]},
+ {applications, [stdlib, kernel, mnesia]},
{env, []},
{mod, {orber, []}}
]}.
diff --git a/lib/orber/src/orber.erl b/lib/orber/src/orber.erl
index c3d37ad1fb..665b3cb383 100644
--- a/lib/orber/src/orber.erl
+++ b/lib/orber/src/orber.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1027,12 +1027,18 @@ remove_node(Node) when is_atom(Node) ->
remove_tables(Tables, Node) ->
- remove_tables(Tables, Node, []).
+ case remove_tables(Tables, Node, []) of
+ ok ->
+ ok;
+ {error, Node, Failed} ->
+ ?EFORMAT("orber:remove_node(~p) failed. Unable to remove table(s): ~p",
+ [Node, Failed])
+ end.
-remove_tables([], _, []) -> ok;
+remove_tables([], _, []) ->
+ ok;
remove_tables([], Node, Failed) ->
- ?EFORMAT("orber:remove_node(~p) failed. Unable to remove table(s): ~p",
- [Node, Failed]);
+ {error, Node, Failed};
remove_tables([T1|Trest], Node, Failed) ->
case mnesia:del_table_copy(T1, Node) of
{atomic, ok} ->
@@ -1041,8 +1047,6 @@ remove_tables([T1|Trest], Node, Failed) ->
remove_tables(Trest, Node, [{T1, Reason}|Failed])
end.
-
-
%%-----------------------------------------------------------------
%% Internal interface functions
%%-----------------------------------------------------------------
diff --git a/lib/orber/src/orber_socket.erl b/lib/orber/src/orber_socket.erl
index af6df01b7d..ec2cf8f42a 100644
--- a/lib/orber/src/orber_socket.erl
+++ b/lib/orber/src/orber_socket.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,7 +37,7 @@
%%-----------------------------------------------------------------
-export([start/0, connect/4, listen/3, listen/4, accept/2, accept/3, write/3,
controlling_process/3, close/2, peername/2, sockname/2,
- peerdata/2, peercert/2, peercert/3, sockdata/2, setopts/3,
+ peerdata/2, peercert/2, sockdata/2, setopts/3,
clear/2, shutdown/3, post_accept/2, post_accept/3]).
%%-----------------------------------------------------------------
@@ -366,14 +366,6 @@ peercert(Type, _Socket) ->
[?LINE, Type], ?DEBUG_LEVEL),
{error, ebadsocket}.
-peercert(ssl, Socket, Opts) ->
- ssl:peercert(Socket, Opts);
-peercert(Type, _Socket, Opts) ->
- orber:dbg("[~p] orber_socket:peercert(~p, ~p);~n"
- "Only available for SSL sockets.",
- [?LINE, Type, Opts], ?DEBUG_LEVEL),
- {error, ebadsocket}.
-
%%-----------------------------------------------------------------
%% Get peerdata
%%
@@ -496,27 +488,17 @@ check_port(Port, _, _) ->
%%-----------------------------------------------------------------
%% Check Options.
-%% We need this as a work-around since the SSL-app doesn't allow us
-%% to pass 'inet' as an option. Also needed for R9B :-(
check_options(normal, Options, _Generation) ->
- case orber:ip_version() of
- inet ->
- Options;
- inet6 ->
- %% Necessary for R9B. Should be [orber:ip_version()|Options];
- [inet6|Options]
- end;
+ [orber:ip_version()|Options];
check_options(ssl, Options, Generation) ->
case orber:ip_version() of
inet when Generation > 2 ->
[{ssl_imp, new}|Options];
inet ->
- Options;
+ [{ssl_imp, old}|Options];
inet6 when Generation > 2 ->
[{ssl_imp, new}, inet6|Options];
inet6 ->
- %% Will fail until SSL supports this option.
- %% Note, we want this happen!
- [inet6|Options]
+ [{ssl_imp, old}, inet6|Options]
end.
diff --git a/lib/orber/src/orber_tb.erl b/lib/orber/src/orber_tb.erl
index 0dd2d95bc8..e6d5ee4400 100644
--- a/lib/orber/src/orber_tb.erl
+++ b/lib/orber/src/orber_tb.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,8 @@
%%----------------------------------------------------------------------
%% External exports
%%----------------------------------------------------------------------
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([wait_for_tables/1, wait_for_tables/2, wait_for_tables/3,
is_loaded/0, is_loaded/1, is_running/0, is_running/1,
info/2, error/2, unique/1, keysearch/2, keysearch/3]).
diff --git a/lib/orber/test/Makefile b/lib/orber/test/Makefile
index 4601e84d2c..b682bcf24b 100644
--- a/lib/orber/test/Makefile
+++ b/lib/orber/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -34,6 +34,7 @@ RELSYSDIR = $(RELEASE_PATH)/orber_test
# Target Specs
# ----------------------------------------------------
TEST_SPEC_FILE = orber.spec
+COVER_FILE = orber.cover
IDL_FILES = \
@@ -120,7 +121,11 @@ GEN_MOD_TEST_SERVER = \
orber_test_server_uni \
orber_test_server_uni_d \
orber_test_timeout_server \
- orber_parent_inherrit
+ orber_parent_inherrit \
+ orber_test_server_rec_struct \
+ orber_test_server_rec_struct_seq \
+ orber_test_server_rec_union \
+ orber_test_server_rec_union_seq
GEN_HRL_TEST_SERVER = \
oe_orber_test_server.hrl \
@@ -218,10 +223,10 @@ release_docs_spec:
release_tests_spec: tests
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \
+ $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) $(COVER_FILE) \
$(ERL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
$(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR)
$(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \
$(RELSYSDIR)/$(IDLOUTDIR)
diff --git a/lib/orber/test/cdrcoding_10_SUITE.erl b/lib/orber/test/cdrcoding_10_SUITE.erl
index d5d030538f..54ad92cf7e 100644
--- a/lib/orber/test/cdrcoding_10_SUITE.erl
+++ b/lib/orber/test/cdrcoding_10_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +27,7 @@
-include("idl_output/Module.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -36,12 +36,11 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -49,13 +48,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-cases() ->
- [types, reply, cancel_request, close_connection, message_error].
+all() ->
+ cases().
+
+groups() ->
+ [{types, [],
+ [do_register, null_type, void_type, principal_type,
+ objref_type, struct_type, union_type, string_type,
+ array_type, any_type, typecode_type, alias_type,
+ exception_type, do_unregister]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [{group, types}, reply, cancel_request,
+ close_connection, message_error].
%% request, locate_request, locate_reply].
%%-----------------------------------------------------------------
@@ -69,14 +83,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
orber:jump_start(0),
if
is_list(Config) ->
@@ -85,7 +99,7 @@ init_all(Config) when is_list(Config) ->
exit("Config not a list")
end.
-finish_all(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
orber:jump_stop(),
Config.
@@ -94,11 +108,6 @@ finish_all(Config) when is_list(Config) ->
%% Description: Just testing the complex types, the others are
%% tested in the cdrlib SUITE.
%%-----------------------------------------------------------------
-types(doc) -> ["Description", "more description"];
-types(suite) -> [do_register, null_type, void_type, principal_type,
- objref_type, struct_type, union_type, string_type,
- array_type, any_type, typecode_type, alias_type,
- exception_type, do_unregister].
%types(Config) when list(Config) ->
% 'oe_orber_test':'oe_register'(),
% null_type(),
diff --git a/lib/orber/test/cdrcoding_11_SUITE.erl b/lib/orber/test/cdrcoding_11_SUITE.erl
index d62fe6eb3a..29b3e33069 100644
--- a/lib/orber/test/cdrcoding_11_SUITE.erl
+++ b/lib/orber/test/cdrcoding_11_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +27,7 @@
-include("idl_output/Module.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -36,12 +36,11 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -49,13 +48,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-cases() ->
- [types, reply, cancel_request, close_connection, message_error].
+all() ->
+ cases().
+
+groups() ->
+ [{types, [],
+ [do_register, null_type, void_type, principal_type,
+ objref_type, struct_type, union_type, string_type,
+ array_type, any_type, typecode_type, alias_type,
+ exception_type, do_unregister]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [{group, types}, reply, cancel_request,
+ close_connection, message_error].
%% request, locate_request, locate_reply].
%%-----------------------------------------------------------------
@@ -69,14 +83,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
orber:jump_start(0),
if
is_list(Config) ->
@@ -85,7 +99,7 @@ init_all(Config) when is_list(Config) ->
exit("Config not a list")
end.
-finish_all(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
orber:jump_stop(),
Config.
@@ -94,11 +108,6 @@ finish_all(Config) when is_list(Config) ->
%% Description: Just testing the complex types, the others are
%% tested in the cdrlib SUITE.
%%-----------------------------------------------------------------
-types(doc) -> ["Description", "more description"];
-types(suite) -> [do_register, null_type, void_type, principal_type,
- objref_type, struct_type, union_type, string_type,
- array_type, any_type, typecode_type, alias_type,
- exception_type, do_unregister].
%types(Config) when list(Config) ->
% 'oe_orber_test':'oe_register'(),
% null_type(),
diff --git a/lib/orber/test/cdrcoding_12_SUITE.erl b/lib/orber/test/cdrcoding_12_SUITE.erl
index 18e8eaa08a..dd9b98434d 100644
--- a/lib/orber/test/cdrcoding_12_SUITE.erl
+++ b/lib/orber/test/cdrcoding_12_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-module(cdrcoding_12_SUITE).
-include("idl_output/Module.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -37,12 +37,11 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -50,13 +49,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-cases() ->
- [types, reply, cancel_request, close_connection, message_error].
+all() ->
+ cases().
+
+groups() ->
+ [{types, [],
+ [do_register, null_type, void_type, principal_type,
+ objref_type, struct_type, union_type, string_type,
+ array_type, any_type, typecode_type, alias_type,
+ exception_type, do_unregister]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [{group, types}, reply, cancel_request,
+ close_connection, message_error].
%% request, locate_request, locate_reply].
%%-----------------------------------------------------------------
@@ -70,14 +84,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
orber:jump_start(0),
if
is_list(Config) ->
@@ -86,7 +100,7 @@ init_all(Config) when is_list(Config) ->
exit("Config not a list")
end.
-finish_all(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
orber:jump_stop(),
Config.
@@ -95,11 +109,6 @@ finish_all(Config) when is_list(Config) ->
%% Description: Just testing the complex types, the others are
%% tested in the cdrlib SUITE.
%%-----------------------------------------------------------------
-types(doc) -> ["Description", "more description"];
-types(suite) -> [do_register, null_type, void_type, principal_type,
- objref_type, struct_type, union_type, string_type,
- array_type, any_type, typecode_type, alias_type,
- exception_type, do_unregister].
do_register(doc) -> [];
do_register(suite) -> [];
diff --git a/lib/orber/test/cdrlib_SUITE.erl b/lib/orber/test/cdrlib_SUITE.erl
index fa2d7f2a30..012d76b786 100644
--- a/lib/orber/test/cdrlib_SUITE.erl
+++ b/lib/orber/test/cdrlib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,19 +25,19 @@
%%-----------------------------------------------------------------
-module(cdrlib_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(3)).
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -45,10 +45,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) ->
- [short, ushort, long, ulong, longlong, ulonglong, boolean, character, octet,
- float, double, enum].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [short, ushort, long, ulong, longlong, ulonglong,
+ boolean, character, octet, float, double, enum].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
@@ -59,7 +76,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/corba_SUITE.erl b/lib/orber/test/corba_SUITE.erl
index dae8fcbefc..17a9f5fcdf 100644
--- a/lib/orber/test/corba_SUITE.erl
+++ b/lib/orber/test/corba_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(corba_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -51,7 +51,7 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -64,18 +64,29 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for the CORBA/BOA/Object/orber interfaces", ""];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [exception_info_api, corba_api, object_api, orber_api,
- orber_objectkeys_api, orber_pseudo_objects, callback_ok_api,
- callback_arity_api, callback_module_api, callback_function_api,
- callback_precond_api, callback_postcond_api, callback_exit_api,
- callback_badarith_api, callback_case_clause_api,
- callback_function_clause_api].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [exception_info_api, corba_api, object_api, orber_api,
+ orber_objectkeys_api, orber_pseudo_objects,
+ callback_ok_api, callback_arity_api,
+ callback_module_api, callback_function_api,
+ callback_precond_api, callback_postcond_api,
+ callback_exit_api, callback_badarith_api,
+ callback_case_clause_api, callback_function_clause_api].
%% boa_api, request, locate_request, locate_reply].
@@ -90,14 +101,14 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
corba:orb_init([{orber_debug_level, 10}, {giop_version, {1,2}},
{iiop_port, 0}]),
mnesia:delete_schema([node()]),
@@ -112,7 +123,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
application:stop(orber),
application:stop(mnesia),
mnesia:delete_schema([node()]),
diff --git a/lib/orber/test/csiv2_SUITE.erl b/lib/orber/test/csiv2_SUITE.erl
index 8103fd81ac..95cd8c56b3 100644
--- a/lib/orber/test/csiv2_SUITE.erl
+++ b/lib/orber/test/csiv2_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(csiv2_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -272,8 +272,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
% code_CertificateChain_api/1,
% code_AttributeCertChain_api/1,
% code_VerifyingCertChain_api/1,
@@ -316,46 +317,26 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for multi orber interfaces using CSIv2"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% NOTE - the fragment test cases must bu first since we explicitly set a request
%% id. Otherwise, the request-id counter would be increased and we cannot know
%% what it is.
-cases() ->
- [
-% code_CertificateChain_api,
-% code_AttributeCertChain_api,
-% code_VerifyingCertChain_api,
-% code_AttributeCertificate_api,
-% code_Certificate_api,
-% code_TBSCertificate_api,
-% code_CertificateSerialNumber_api,
-% code_Version_api,
-% code_AlgorithmIdentifier_api,
-% code_Name_api,
-% code_RDNSequence_api,
-% code_RelativeDistinguishedName_api,
-% code_AttributeTypeAndValue_api,
-% code_Attribute_api,
-% code_Validity_api,
-% code_SubjectPublicKeyInfo_api,
-% code_UniqueIdentifier_api,
-% code_Extensions_api,
-% code_Extension_api,
-% code_AttributeCertificateInfo_api,
-% code_AttCertVersion_api,
-% code_Holder_api,
-% code_AttCertIssuer_api,
-% code_AttCertValidityPeriod_api,
-% code_V2Form_api,
-% code_IssuerSerial_api,
-% code_ObjectDigestInfo_api,
-% code_OpenSSL509_api,
- ssl_server_peercert_api,
- ssl_client_peercert_api].
+cases() ->
+ [ssl_server_peercert_api, ssl_client_peercert_api].
%%-----------------------------------------------------------------
%% Init and cleanup functions.
@@ -370,7 +351,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
oe_orber_test_server:oe_unregister(),
orber:jump_stop(),
Path = code:which(?MODULE),
@@ -379,15 +360,15 @@ fin_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
- if
- is_list(Config) ->
- Config;
- true ->
- exit("Config not a list")
+init_per_suite(Config) ->
+ case orber_test_lib:ssl_version() of
+ no_ssl ->
+ {skip,"SSL is not installed!"};
+ _ ->
+ Config
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
%%-----------------------------------------------------------------
@@ -694,8 +675,8 @@ ssl_server_peercert_api(_Config) ->
{ok, Socket} =
?match({ok, _}, fake_client_ORB(ssl, ServerHost, ServerPort, SSLOptions)),
{ok, _PeerCert} = ?match({ok, _}, orber_socket:peercert(ssl, Socket)),
- ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
- ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
+%% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
+%% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
% ?match({ok, #'Certificate'{}},
% 'OrberCSIv2':decode('Certificate', PeerCert)),
destroy_fake_ORB(ssl, Socket),
@@ -734,8 +715,8 @@ ssl_client_peercert_api(_Config) ->
?match(ok, ssl:ssl_accept(Socket)),
{ok, _PeerCert} = ?match({ok, _}, orber_socket:peercert(ssl, Socket)),
- ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
- ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
+%% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [pkix, subject])),
+%% ?match({ok, {rdnSequence, _}}, orber_socket:peercert(ssl, Socket, [ssl, subject])),
% ?match({ok, #'Certificate'{}},
% 'OrberCSIv2':decode('Certificate', PeerCert)),
ssl:close(Socket),
diff --git a/lib/orber/test/data_types_SUITE.erl b/lib/orber/test/data_types_SUITE.erl
index 1feb0b3b58..9d436aaf1b 100644
--- a/lib/orber/test/data_types_SUITE.erl
+++ b/lib/orber/test/data_types_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(data_types_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -48,12 +48,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -61,10 +61,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing more or less complex data types"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[fixed_type, any_type].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -75,7 +92,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
diff --git a/lib/orber/test/generated_SUITE.erl b/lib/orber/test/generated_SUITE.erl
index 1cd1674fc4..a6bcff88dc 100644
--- a/lib/orber/test/generated_SUITE.erl
+++ b/lib/orber/test/generated_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(generated_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -71,7 +71,8 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -84,16 +85,37 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing IC generated files"];
-all(suite) ->
- ['OrberApp_IFR',
- erlang_binary, erlang_pid, erlang_port, erlang_ref,
- 'CosNaming_Binding', 'CosNaming_BindingList', 'CosNaming_Name',
- 'CosNaming_NameComponent', 'CosNaming_NamingContextExt_InvalidAddress',
- 'CosNaming_NamingContext_AlreadyBound', 'CosNaming_NamingContext_CannotProceed',
- 'CosNaming_NamingContext_InvalidName', 'CosNaming_NamingContext_NotEmpty',
- 'CosNaming_NamingContext_NotFound', 'CosNaming_BindingIterator',
- 'CosNaming_NamingContext', 'CosNaming_NamingContextExt'].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['OrberApp_IFR', erlang_binary, erlang_pid, erlang_port,
+ erlang_ref, 'CosNaming_Binding',
+ 'CosNaming_BindingList', 'CosNaming_Name',
+ 'CosNaming_NameComponent',
+ 'CosNaming_NamingContextExt_InvalidAddress',
+ 'CosNaming_NamingContext_AlreadyBound',
+ 'CosNaming_NamingContext_CannotProceed',
+ 'CosNaming_NamingContext_InvalidName',
+ 'CosNaming_NamingContext_NotEmpty',
+ 'CosNaming_NamingContext_NotFound',
+ 'CosNaming_BindingIterator', 'CosNaming_NamingContext',
+ 'CosNaming_NamingContextExt'].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
@@ -103,7 +125,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/iiop_module_do_test_impl.erl b/lib/orber/test/iiop_module_do_test_impl.erl
index bf171a3097..54fcd8239a 100644
--- a/lib/orber/test/iiop_module_do_test_impl.erl
+++ b/lib/orber/test/iiop_module_do_test_impl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/test/iiop_module_test_impl.erl b/lib/orber/test/iiop_module_test_impl.erl
index fe334e1b26..2096c14a23 100644
--- a/lib/orber/test/iiop_module_test_impl.erl
+++ b/lib/orber/test/iiop_module_test_impl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/test/iiop_test_impl.erl b/lib/orber/test/iiop_test_impl.erl
index fd92109c09..234f7c5f73 100644
--- a/lib/orber/test/iiop_test_impl.erl
+++ b/lib/orber/test/iiop_test_impl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/test/interceptors_SUITE.erl b/lib/orber/test/interceptors_SUITE.erl
index 27e23a9433..ade0183ddd 100644
--- a/lib/orber/test/interceptors_SUITE.erl
+++ b/lib/orber/test/interceptors_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(interceptors_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -65,7 +65,8 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -78,10 +79,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing Orber Interceptors"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[local_pseudo, local_default, local_local, local_global].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -96,7 +114,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
oe_orber_test_server:oe_unregister(),
orber:jump_stop(),
Path = code:which(?MODULE),
diff --git a/lib/orber/test/iop_ior_10_SUITE.erl b/lib/orber/test/iop_ior_10_SUITE.erl
index 1000c7f113..58dd1b5dba 100644
--- a/lib/orber/test/iop_ior_10_SUITE.erl
+++ b/lib/orber/test/iop_ior_10_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(iop_ior_10_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -33,7 +33,8 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -46,10 +47,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[encoding, create_and_get_ops].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -59,7 +77,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/iop_ior_11_SUITE.erl b/lib/orber/test/iop_ior_11_SUITE.erl
index 35d01789ee..24b2f66357 100644
--- a/lib/orber/test/iop_ior_11_SUITE.erl
+++ b/lib/orber/test/iop_ior_11_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(iop_ior_11_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -33,7 +33,8 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -46,10 +47,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[encoding, create_and_get_ops].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -59,7 +77,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/iop_ior_12_SUITE.erl b/lib/orber/test/iop_ior_12_SUITE.erl
index 42db130e54..4c6e9ddb91 100644
--- a/lib/orber/test/iop_ior_12_SUITE.erl
+++ b/lib/orber/test/iop_ior_12_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
-module(iop_ior_12_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -34,7 +34,8 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -47,10 +48,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[encoding, create_and_get_ops].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -60,7 +78,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/lname_SUITE.erl b/lib/orber/test/lname_SUITE.erl
index d1f0e7cf0e..6a3bc1fae2 100644
--- a/lib/orber/test/lname_SUITE.erl
+++ b/lib/orber/test/lname_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(lname_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/COSS/CosNaming/lname.hrl").
@@ -34,7 +34,8 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -47,10 +48,27 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[lname_component, lname].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -60,7 +78,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/multi_ORB_SUITE.erl b/lib/orber/test/multi_ORB_SUITE.erl
index d1931f5393..608fb23f3e 100644
--- a/lib/orber/test/multi_ORB_SUITE.erl
+++ b/lib/orber/test/multi_ORB_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(multi_ORB_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -50,8 +50,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1, basic_PI_api/1, multi_orber_api/1,
- init_per_testcase/2, fin_per_testcase/2, multi_pseudo_orber_api/1,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1, basic_PI_api/1, multi_orber_api/1,
+ init_per_testcase/2, end_per_testcase/2, multi_pseudo_orber_api/1,
light_orber_api/1, light_orber2_api/1,
ssl_1_multi_orber_api/1, ssl_2_multi_orber_api/1, ssl_reconfigure_api/1,
iiop_timeout_api/1, iiop_timeout_added_api/1, setup_connection_timeout_api/1,
@@ -86,75 +87,93 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for multi orber interfaces",
- "This suite test intra-ORB communication. There are three scenarios:",
- "* No security at all (multi_orber_api)",
- "* Two secure orbs using ssl (ssl_multi_orb_api)",
- "* One secure and one orb with no security. (ssl_multi_orb_api)"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% NOTE - the fragment test cases must be first since we explicitly set a request
%% id. Otherwise, the request-id counter would be increased and we cannot know
%% what it is.
-cases() ->
- [fragments_server_api,
- fragments_max_server_api,
- fragments_max_server_added_api,
- fragments_client_api,
- flags_added_api,
- bad_fragment_id_client_api,
- bad_giop_header_api,
- bad_id_cancel_request_api,
- implicit_context_api,
- pseudo_implicit_context_api,
+cases() ->
+ [fragments_server_api, fragments_max_server_api,
+ fragments_max_server_added_api, fragments_client_api,
+ flags_added_api, bad_fragment_id_client_api,
+ bad_giop_header_api, bad_id_cancel_request_api,
+ implicit_context_api, pseudo_implicit_context_api,
pseudo_two_implicit_context_api,
implicit_context_roundtrip_api,
- oneway_implicit_context_api,
+ oneway_implicit_context_api,
oneway_pseudo_implicit_context_api,
oneway_pseudo_two_implicit_context_api,
- proxy_interface_api,
- proxy_interface_ipv6_api,
- local_interface_api,
- local_interface_ctx_override_api,
- local_interface_acl_override_api,
- close_connections_api,
+ proxy_interface_api, proxy_interface_ipv6_api,
+ local_interface_api, local_interface_ctx_override_api,
+ local_interface_acl_override_api, close_connections_api,
close_connections_local_interface_api,
close_connections_local_interface_ctx_override_api,
close_connections_alt_iiop_addr_api,
close_connections_multiple_profiles_api,
- multiple_accept_api,
- max_requests_api,
- max_requests_added_api,
- max_connections_api,
- max_packet_size_exceeded_api,
- max_packet_size_ok_api,
- light_ifr_api,
- multi_pseudo_orber_api,
- multi_orber_api,
- light_orber_api,
- light_orber2_api,
- basic_PI_api,
- iiop_timeout_api,
- iiop_timeout_added_api,
- setup_connection_timeout_api,
- setup_multi_connection_timeout_api,
- setup_multi_connection_timeout_attempts_api,
- setup_multi_connection_timeout_random_api,
+ multiple_accept_api, max_requests_api,
+ max_requests_added_api, max_connections_api,
+ max_packet_size_exceeded_api, max_packet_size_ok_api,
+ light_ifr_api, multi_pseudo_orber_api, multi_orber_api,
+ light_orber_api, light_orber2_api, basic_PI_api,
+ iiop_timeout_api, iiop_timeout_added_api,
+ setup_connection_timeout_api,
+ setup_multi_connection_timeout_api,
+ setup_multi_connection_timeout_attempts_api,
+ setup_multi_connection_timeout_random_api,
ssl_1_multi_orber_api,
ssl_1_multi_orber_generation_3_api,
ssl_2_multi_orber_api,
ssl_2_multi_orber_generation_3_api,
- ssl_reconfigure_generation_3_api,
- ssl_reconfigure_api
- ].
+ ssl_reconfigure_generation_3_api, ssl_reconfigure_api].
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
-
+init_per_testcase(TC,Config)
+ when TC =:= ssl_1_multi_orber_api;
+ TC =:= ssl_2_multi_orber_api;
+ TC =:= ssl_reconfigure_api ->
+ init_ssl(Config);
+init_per_testcase(TC,Config)
+ when TC =:= ssl_1_multi_orber_generation_3_api;
+ TC =:= ssl_2_multi_orber_generation_3_api;
+ TC =:= ssl_reconfigure_generation_3_api ->
+ init_ssl_3(Config);
init_per_testcase(_Case, Config) ->
+ init_all(Config).
+
+init_ssl(Config) ->
+ case orber_test_lib:ssl_version() of
+ no_ssl ->
+ {skip,"SSL is not installed!"};
+ _ ->
+ init_all(Config)
+ end.
+
+init_ssl_3(Config) ->
+ case orber_test_lib:ssl_version() of
+ 3 ->
+ init_all(Config);
+ 2 ->
+ {skip,"Could not find the correct SSL version!"};
+ no_ssl ->
+ {skip,"SSL is not installed!"}
+ end.
+
+init_all(Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
Dog=test_server:timetrap(?default_timeout),
@@ -163,7 +182,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
oe_orber_test_server:oe_unregister(),
orber:jump_stop(),
Path = code:which(?MODULE),
@@ -172,7 +191,7 @@ fin_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
if
is_list(Config) ->
Config;
@@ -180,7 +199,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
%%-----------------------------------------------------------------
@@ -1372,7 +1391,7 @@ light_orber2_api(_Config) ->
LocalHost = net_adm:localhost(),
{ok, Node, _Host} =
?match({ok,_,_}, orber_test_lib:js_node([],
- {lightweigth, ["iiop://"++LocalHost++":"++integer_to_list(orber:iiop_port())]})),
+ {lightweight, ["iiop://"++LocalHost++":"++integer_to_list(orber:iiop_port())]})),
?match(ok, orber:info(io)),
?match([_], orber_test_lib:remote_apply(Node, orber_env, get_lightweight_nodes,[])),
@@ -1580,17 +1599,11 @@ ssl_1_multi_orber_api(doc) -> ["SECURE MULTI ORB API tests (SSL depth 1)",
"secure orbs which must raise a NO_PERMISSION exception."];
ssl_1_multi_orber_api(suite) -> [];
ssl_1_multi_orber_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 1, [{iiop_ssl_port, 0}]),
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 1, [{iiop_ssl_port, 0}]),
- ssl_suite(ServerOptions, ClientOptions),
- ok
- end.
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 1, [{iiop_ssl_port, 0}]),
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 1, [{iiop_ssl_port, 0}]),
+ ssl_suite(ServerOptions, ClientOptions).
ssl_1_multi_orber_generation_3_api(doc) -> ["SECURE MULTI ORB API tests (SSL depth 1)",
"This case set up two secure orbs and test if they can",
@@ -1598,24 +1611,14 @@ ssl_1_multi_orber_generation_3_api(doc) -> ["SECURE MULTI ORB API tests (SSL dep
"secure orbs which must raise a NO_PERMISSION exception."];
ssl_1_multi_orber_generation_3_api(suite) -> [];
ssl_1_multi_orber_generation_3_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- case orber_test_lib:ssl_version() of
- 3 ->
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 1, [{ssl_generation, 3},
- {iiop_ssl_port, 0}]),
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 1, [{ssl_generation, 3},
- {iiop_ssl_port, 0}]),
- ssl_suite(ServerOptions, ClientOptions),
- ok;
- _ ->
- {skipped, "Required SSL generation not available"}
- end
- end.
+
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 1, [{ssl_generation, 3},
+ {iiop_ssl_port, 0}]),
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 1, [{ssl_generation, 3},
+ {iiop_ssl_port, 0}]),
+ ssl_suite(ServerOptions, ClientOptions).
%%-----------------------------------------------------------------
@@ -1628,17 +1631,12 @@ ssl_2_multi_orber_api(doc) -> ["SECURE MULTI ORB API tests (SSL depth 2)",
"secure orbs which must raise a NO_PERMISSION exception."];
ssl_2_multi_orber_api(suite) -> [];
ssl_2_multi_orber_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 2, [{iiop_ssl_port, 0}]),
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 2, [{iiop_ssl_port, 0}]),
- ssl_suite(ServerOptions, ClientOptions),
- ok
- end.
+
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 2, [{iiop_ssl_port, 0}]),
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 2, [{iiop_ssl_port, 0}]),
+ ssl_suite(ServerOptions, ClientOptions).
ssl_2_multi_orber_generation_3_api(doc) -> ["SECURE MULTI ORB API tests (SSL depth 2)",
"This case set up two secure orbs and test if they can",
@@ -1646,24 +1644,14 @@ ssl_2_multi_orber_generation_3_api(doc) -> ["SECURE MULTI ORB API tests (SSL dep
"secure orbs which must raise a NO_PERMISSION exception."];
ssl_2_multi_orber_generation_3_api(suite) -> [];
ssl_2_multi_orber_generation_3_api(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- case orber_test_lib:ssl_version() of
- 3 ->
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 2, [{ssl_generation, 3},
- {iiop_ssl_port, 0}]),
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 2, [{ssl_generation, 3},
- {iiop_ssl_port, 0}]),
- ssl_suite(ServerOptions, ClientOptions),
- ok;
- _ ->
- {skipped, "Required SSL generation not available"}
- end
- end.
+
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 2, [{ssl_generation, 3},
+ {iiop_ssl_port, 0}]),
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 2, [{ssl_generation, 3},
+ {iiop_ssl_port, 0}]),
+ ssl_suite(ServerOptions, ClientOptions).
%%-----------------------------------------------------------------
%% API tests for ORB to ORB, ssl security depth 2
%%-----------------------------------------------------------------
@@ -1682,69 +1670,57 @@ ssl_reconfigure_generation_3_api(doc) -> ["SECURE MULTI ORB API tests (SSL depth
"secure orbs which must raise a NO_PERMISSION exception."];
ssl_reconfigure_generation_3_api(suite) -> [];
ssl_reconfigure_generation_3_api(_Config) ->
- case orber_test_lib:ssl_version() of
- 3 ->
- ssl_reconfigure([{ssl_generation, 3}]);
-
- _ ->
- {skipped, "Required SSL generation not available"}
- end.
+ ssl_reconfigure([{ssl_generation, 3}]).
ssl_reconfigure(ExtraSSLOptions) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- IP = orber_test_lib:get_host(),
- Loopback = orber_test_lib:get_loopback_interface(),
- {ok, ServerNode, _ServerHost} =
- ?match({ok,_,_},
- orber_test_lib:js_node([{iiop_port, 0},
- {flags, ?ORB_ENV_LOCAL_INTERFACE},
- {ip_address, IP}|ExtraSSLOptions])),
- orber_test_lib:remote_apply(ServerNode, ssl, start, []),
- orber_test_lib:remote_apply(ServerNode, crypto, start, []),
- orber_test_lib:remote_apply(ServerNode, ssl, seed, ["testing"]),
- ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
- install_test_data,
- [ssl])),
- ?match({ok, _},
- orber_test_lib:remote_apply(ServerNode, orber,
- add_listen_interface,
- [Loopback, normal, [{iiop_port, 5648},
- {iiop_ssl_port, 5649},
- {interceptors, {native, [orber_iiop_tracer_silent]}}|ExtraSSLOptions]])),
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 2, [{flags, ?ORB_ENV_LOCAL_INTERFACE},
- {iiop_port, 5648},
+
+ IP = orber_test_lib:get_host(),
+ Loopback = orber_test_lib:get_loopback_interface(),
+ {ok, ServerNode, _ServerHost} =
+ ?match({ok,_,_},
+ orber_test_lib:js_node([{iiop_port, 0},
+ {flags, ?ORB_ENV_LOCAL_INTERFACE},
+ {ip_address, IP}|ExtraSSLOptions])),
+ orber_test_lib:remote_apply(ServerNode, ssl, start, []),
+ orber_test_lib:remote_apply(ServerNode, crypto, start, []),
+ orber_test_lib:remote_apply(ServerNode, ssl, seed, ["testing"]),
+ ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
+ install_test_data,
+ [ssl])),
+ ?match({ok, _},
+ orber_test_lib:remote_apply(ServerNode, orber,
+ add_listen_interface,
+ [Loopback, normal, [{iiop_port, 5648},
{iiop_ssl_port, 5649},
- {interceptors, {native, [orber_iiop_tracer_silent]}}|ExtraSSLOptions]),
- ?match({ok, _},
- orber_test_lib:remote_apply(ServerNode, orber,
- add_listen_interface,
- [Loopback, ssl, ServerOptions])),
-
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 2, [{iiop_ssl_port, 0}|ExtraSSLOptions]),
- {ok, ClientNode, _ClientHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
-
- ?match(ok, orber_test_lib:remote_apply(ClientNode, orber_test_lib,
- install_test_data,
- [ssl])),
- orber_test_lib:remote_apply(ClientNode, ssl, start, []),
- orber_test_lib:remote_apply(ServerNode, crypto, start, []),
- orber_test_lib:remote_apply(ClientNode, ssl, seed, ["testing"]),
- Obj = ?match(#'IOP_IOR'{},
- orber_test_lib:remote_apply(ClientNode, corba,
- string_to_object, ["corbaname:iiop:1.1@"++Loopback++":5648/NameService#mamba",
- [{context, [#'IOP_ServiceContext'{context_id=?ORBER_GENERIC_CTX_ID,
- context_data = {configuration, ClientOptions}}]}]])),
- ?match(ok, orber_test_lib:remote_apply(ClientNode, orber_test_server,
- print, [Obj])),
-
- ok
- end.
+ {interceptors, {native, [orber_iiop_tracer_silent]}}|ExtraSSLOptions]])),
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 2, [{flags, ?ORB_ENV_LOCAL_INTERFACE},
+ {iiop_port, 5648},
+ {iiop_ssl_port, 5649},
+ {interceptors, {native, [orber_iiop_tracer_silent]}}|ExtraSSLOptions]),
+ ?match({ok, _},
+ orber_test_lib:remote_apply(ServerNode, orber,
+ add_listen_interface,
+ [Loopback, ssl, ServerOptions])),
+
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 2, [{iiop_ssl_port, 0}|ExtraSSLOptions]),
+ {ok, ClientNode, _ClientHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
+
+ ?match(ok, orber_test_lib:remote_apply(ClientNode, orber_test_lib,
+ install_test_data,
+ [ssl])),
+ orber_test_lib:remote_apply(ClientNode, ssl, start, []),
+ orber_test_lib:remote_apply(ServerNode, crypto, start, []),
+ orber_test_lib:remote_apply(ClientNode, ssl, seed, ["testing"]),
+ Obj = ?match(#'IOP_IOR'{},
+ orber_test_lib:remote_apply(ClientNode, corba,
+ string_to_object, ["corbaname:iiop:1.1@"++Loopback++":5648/NameService#mamba",
+ [{context, [#'IOP_ServiceContext'{context_id=?ORBER_GENERIC_CTX_ID,
+ context_data = {configuration, ClientOptions}}]}]])),
+ ?match(ok, orber_test_lib:remote_apply(ClientNode, orber_test_server,
+ print, [Obj])).
diff --git a/lib/orber/test/naming_context_SUITE.erl b/lib/orber/test/naming_context_SUITE.erl
index 4406e01d5a..789aace882 100644
--- a/lib/orber/test/naming_context_SUITE.erl
+++ b/lib/orber/test/naming_context_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(naming_context_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-include_lib("orber/include/corba.hrl").
@@ -35,7 +35,7 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -43,7 +43,8 @@
-export([name_context/1, check_list/1, name_context_ext/1]).
--export([init_all/1, finish_all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_suite/1, end_per_suite/1, init_per_testcase/2,
+ end_per_testcase/2]).
%%-----------------------------------------------------------------
@@ -75,12 +76,22 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-cases() ->
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
[name_context, check_list, name_context_ext].
%%-----------------------------------------------------------------
@@ -95,7 +106,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
orber:jump_stop(),
@@ -103,10 +114,10 @@ fin_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
Config.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
%%-----------------------------------------------------------------
diff --git a/lib/orber/test/orber.cover b/lib/orber/test/orber.cover
new file mode 100644
index 0000000000..807a7c2c6e
--- /dev/null
+++ b/lib/orber/test/orber.cover
@@ -0,0 +1,2 @@
+{incl_app,orber,details}.
+
diff --git a/lib/orber/test/orber.spec b/lib/orber/test/orber.spec
index 9d19ea7fc1..0dd30deade 100644
--- a/lib/orber/test/orber.spec
+++ b/lib/orber/test/orber.spec
@@ -1,19 +1 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-{topcase, {dir, "../orber_test"}}.
+{suites,"../orber_test",all}.
diff --git a/lib/orber/test/orber_SUITE.erl b/lib/orber/test/orber_SUITE.erl
index f54da02c0e..be6ffa201c 100644
--- a/lib/orber/test/orber_SUITE.erl
+++ b/lib/orber/test/orber_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,15 +18,16 @@
%%
%%
-module(orber_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(15)).
-define(application, orber).
% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
-export([app_test/1, undefined_functions/1, install_load_order/1,
@@ -35,17 +36,33 @@
%%
%% all/1
%%
-all(doc) ->
- [];
-all(suite) ->
- [app_test, undefined_functions,
- install_load_order, install_local_content].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test, undefined_functions, install_load_order,
+ install_local_content].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/orber_acl_SUITE.erl b/lib/orber/test/orber_acl_SUITE.erl
index 2c2a768af2..b43a00be19 100644
--- a/lib/orber/test/orber_acl_SUITE.erl
+++ b/lib/orber/test/orber_acl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(orber_acl_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(5)).
@@ -47,7 +47,7 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
@@ -59,15 +59,26 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Testing API for ACL (Access Control List)"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[ipv4_verify, ipv4_range, ipv4_interfaces, ipv4_bm,
ipv6_verify, ipv6_range, ipv6_interfaces, ipv6_bm].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
-init_all(Config) ->
+init_per_suite(Config) ->
if
list(Config) ->
Config;
@@ -75,7 +86,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
@@ -84,7 +95,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/test/orber_firewall_ipv4_in_SUITE.erl b/lib/orber/test/orber_firewall_ipv4_in_SUITE.erl
index 3ac0cb7921..0175409a5b 100644
--- a/lib/orber/test/orber_firewall_ipv4_in_SUITE.erl
+++ b/lib/orber/test/orber_firewall_ipv4_in_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(orber_firewall_ipv4_in_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -49,8 +49,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
deny_port_api/1, deny_port_range_api/1, deny_host_api/1,
deny_peerhost_api/1, allow_port_range_api/1,
allow_host_api/1, allow_peerhost_api/1, check_address_api/1]).
@@ -60,17 +61,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for orber's firewall functionallity."];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% NOTE - the fragment test cases must bu first since we explicitly set a request
%% id. Otherwise, the request-id counter would be increased and we cannot know
%% what it is.
-cases() ->
- [deny_port_api, deny_port_range_api, deny_host_api, deny_peerhost_api,
- allow_port_range_api, allow_host_api, allow_peerhost_api, check_address_api].
+cases() ->
+ [deny_port_api, deny_port_range_api, deny_host_api,
+ deny_peerhost_api, allow_port_range_api, allow_host_api,
+ allow_peerhost_api, check_address_api].
init_per_testcase(_Case, Config) ->
@@ -78,12 +90,12 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
if
is_list(Config) ->
orber:jump_start([{iiop_port, 0},
@@ -93,7 +105,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
orber:jump_stop(),
Config.
diff --git a/lib/orber/test/orber_firewall_ipv4_out_SUITE.erl b/lib/orber/test/orber_firewall_ipv4_out_SUITE.erl
index 193fc72f7c..591b5f5f67 100644
--- a/lib/orber/test/orber_firewall_ipv4_out_SUITE.erl
+++ b/lib/orber/test/orber_firewall_ipv4_out_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(orber_firewall_ipv4_out_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -49,8 +49,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
deny_port_api/1, deny_port_range_api/1, deny_host_api/1,
allow_port_api/1, allow_port_range_api/1, allow_host_api/1,
local_interface_api/1]).
@@ -60,15 +61,25 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for orber's firewall functionallity."];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% NOTE - the fragment test cases must bu first since we explicitly set a request
%% id. Otherwise, the request-id counter would be increased and we cannot know
%% what it is.
-cases() ->
+cases() ->
[deny_port_api, deny_port_range_api, deny_host_api,
allow_port_api, allow_port_range_api, allow_host_api,
local_interface_api].
@@ -79,12 +90,12 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
if
is_list(Config) ->
orber:jump_start([{iiop_port, 0},
@@ -94,7 +105,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
orber:jump_stop(),
Config.
diff --git a/lib/orber/test/orber_firewall_ipv6_in_SUITE.erl b/lib/orber/test/orber_firewall_ipv6_in_SUITE.erl
index 83f48cba0c..10827b6ef5 100644
--- a/lib/orber/test/orber_firewall_ipv6_in_SUITE.erl
+++ b/lib/orber/test/orber_firewall_ipv6_in_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(orber_firewall_ipv6_in_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -49,8 +49,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
deny_port_api/1, deny_port_range_api/1, deny_host_api/1,
deny_peerhost_api/1, allow_port_range_api/1,
allow_host_api/1, allow_peerhost_api/1, check_address_api/1]).
@@ -60,18 +61,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for orber's firewall functionallity."];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% NOTE - the fragment test cases must bu first since we explicitly set a request
%% id. Otherwise, the request-id counter would be increased and we cannot know
%% what it is.
-cases() ->
- [deny_port_api, deny_port_range_api, deny_host_api, deny_peerhost_api,
- allow_port_range_api, allow_host_api, allow_peerhost_api,
- check_address_api].
+cases() ->
+ [deny_port_api, deny_port_range_api, deny_host_api,
+ deny_peerhost_api, allow_port_range_api, allow_host_api,
+ allow_peerhost_api, check_address_api].
init_per_testcase(_Case, Config) ->
@@ -82,13 +93,13 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
orber:jump_stop(),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
case orber_test_lib:version_ok() of
true ->
if
@@ -101,7 +112,7 @@ init_all(Config) ->
Reason
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
diff --git a/lib/orber/test/orber_firewall_ipv6_out_SUITE.erl b/lib/orber/test/orber_firewall_ipv6_out_SUITE.erl
index e1856b9a47..83d22cc487 100644
--- a/lib/orber/test/orber_firewall_ipv6_out_SUITE.erl
+++ b/lib/orber/test/orber_firewall_ipv6_out_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(orber_firewall_ipv6_out_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -49,8 +49,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
deny_port_api/1, deny_port_range_api/1, deny_host_api/1,
allow_port_api/1, allow_port_range_api/1, allow_host_api/1,
local_interface_api/1]).
@@ -60,15 +61,25 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for orber's firewall functionallity."];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% NOTE - the fragment test cases must bu first since we explicitly set a request
%% id. Otherwise, the request-id counter would be increased and we cannot know
%% what it is.
-cases() ->
+cases() ->
[deny_port_api, deny_port_range_api, deny_host_api,
allow_port_api, allow_port_range_api, allow_host_api,
local_interface_api].
@@ -82,13 +93,13 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
orber:jump_stop(),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
case orber_test_lib:version_ok() of
true ->
if
@@ -101,7 +112,7 @@ init_all(Config) ->
Reason
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
diff --git a/lib/orber/test/orber_nat_SUITE.erl b/lib/orber/test/orber_nat_SUITE.erl
index 5b295dd1aa..625f168520 100644
--- a/lib/orber/test/orber_nat_SUITE.erl
+++ b/lib/orber/test/orber_nat_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
-module(orber_nat_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/COSS/CosNaming/CosNaming.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -50,8 +50,9 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1, cases/0, init_all/1, finish_all/1,
- init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
nat_ip_address/1, nat_ip_address_multiple/1,
nat_ip_address_local/1, nat_ip_address_local_local/1,
nat_iiop_port/1, nat_iiop_port_local/1,
@@ -68,32 +69,40 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["API tests for multi orber interfaces",
- "This suite test intra-ORB communication. There are three scenarios:",
- "* No security at all (multi_orber_api)",
- "* Two secure orbs using ssl (ssl_multi_orb_api)",
- "* One secure and one orb with no security. (ssl_multi_orb_api)"];
-all(suite) -> {req,
- [mnesia],
- {conf, init_all, cases(), finish_all}}.
-
-cases() ->
- [
- nat_ip_address,
- nat_ip_address_multiple,
- nat_ip_address_local,
- nat_iiop_port,
- nat_iiop_port_local,
- nat_ip_address_local_local,
- nat_iiop_port_local_local,
- nat_iiop_ssl_port,
- nat_iiop_ssl_port_local
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ cases().
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+cases() ->
+ [nat_ip_address, nat_ip_address_multiple,
+ nat_ip_address_local, nat_iiop_port,
+ nat_iiop_port_local, nat_ip_address_local_local,
+ nat_iiop_port_local_local, nat_iiop_ssl_port,
+ nat_iiop_ssl_port_local].
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
-
+init_per_testcase(TC, Config)
+ when TC =:= nat_iiop_ssl_port;
+ TC =:= nat_iiop_ssl_port_local ->
+ case orber_test_lib:ssl_version() of
+ no_ssl ->
+ {skip,"SSL not installed!"};
+ _ ->
+ init_per_testcase(dummy_tc, Config)
+ end;
init_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
@@ -104,7 +113,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
oe_orber_test_server:oe_unregister(),
orber:jump_stop(),
Path = code:which(?MODULE),
@@ -113,7 +122,7 @@ fin_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-init_all(Config) ->
+init_per_suite(Config) ->
if
is_list(Config) ->
Config;
@@ -121,7 +130,7 @@ init_all(Config) ->
exit("Config not a list")
end.
-finish_all(Config) ->
+end_per_suite(Config) ->
Config.
%%-----------------------------------------------------------------
@@ -266,107 +275,99 @@ nat_iiop_ssl_port(doc) -> ["SECURE MULTI ORB API tests (SSL depth 1)",
"Make sure NAT works for SSL"];
nat_iiop_ssl_port(suite) -> [];
nat_iiop_ssl_port(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- IP = orber_test_lib:get_host(),
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 1, [{iiop_ssl_port, 0},
- {flags, ?ORB_ENV_ENABLE_NAT},
- {ip_address, IP}]),
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 1, [{iiop_ssl_port, 0}]),
- {ok, ServerNode, _ServerHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(ServerOptions)),
- ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
- SSLServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
- NATSSLServerPort = SSLServerPort+1,
- {ok, Ref} = ?match({ok, _},
- orber_test_lib:remote_apply(ServerNode, orber,
- add_listen_interface,
- [IP, ssl, NATSSLServerPort])),
- orber_test_lib:remote_apply(ServerNode, orber_env, configure_override,
- [nat_iiop_ssl_port,
- {local, NATSSLServerPort, [{4001, 43}]}]),
-
- {ok, ClientNode, _ClientHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
- ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
- install_test_data,
- [ssl])),
-
- IOR1 = ?match(#'IOP_IOR'{},
- orber_test_lib:remote_apply(ClientNode, corba,
- string_to_object,
- ["corbaname::1.2@"++IP++":"++
- integer_to_list(ServerPort)++"/NameService#mamba"])),
-
- ?match({'external', {_IP, _Port, _ObjectKey, _Counter, _TP,
- #host_data{protocol = ssl,
- ssl_data = #'SSLIOP_SSL'{port = NATSSLServerPort}}}},
- iop_ior:get_key(IOR1)),
- ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
- uninstall_test_data,
- [ssl])),
- ?match(ok,
- orber_test_lib:remote_apply(ServerNode, orber,
- remove_listen_interface, [Ref])),
- ok
- end.
+
+ IP = orber_test_lib:get_host(),
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 1, [{iiop_ssl_port, 0},
+ {flags, ?ORB_ENV_ENABLE_NAT},
+ {ip_address, IP}]),
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 1, [{iiop_ssl_port, 0}]),
+ {ok, ServerNode, _ServerHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(ServerOptions)),
+ ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
+ SSLServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
+ NATSSLServerPort = SSLServerPort+1,
+ {ok, Ref} = ?match({ok, _},
+ orber_test_lib:remote_apply(ServerNode, orber,
+ add_listen_interface,
+ [IP, ssl, NATSSLServerPort])),
+ orber_test_lib:remote_apply(ServerNode, orber_env, configure_override,
+ [nat_iiop_ssl_port,
+ {local, NATSSLServerPort, [{4001, 43}]}]),
+
+ {ok, ClientNode, _ClientHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
+ ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
+ install_test_data,
+ [ssl])),
+
+ IOR1 = ?match(#'IOP_IOR'{},
+ orber_test_lib:remote_apply(ClientNode, corba,
+ string_to_object,
+ ["corbaname::1.2@"++IP++":"++
+ integer_to_list(ServerPort)++"/NameService#mamba"])),
+
+ ?match({'external', {_IP, _Port, _ObjectKey, _Counter, _TP,
+ #host_data{protocol = ssl,
+ ssl_data = #'SSLIOP_SSL'{port = NATSSLServerPort}}}},
+ iop_ior:get_key(IOR1)),
+ ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
+ uninstall_test_data,
+ [ssl])),
+ ?match(ok,
+ orber_test_lib:remote_apply(ServerNode, orber,
+ remove_listen_interface, [Ref])),
+ ok.
nat_iiop_ssl_port_local(doc) -> ["SECURE MULTI ORB API tests (SSL depth 1)",
"Make sure NAT works for SSL"];
nat_iiop_ssl_port_local(suite) -> [];
nat_iiop_ssl_port_local(_Config) ->
- case os:type() of
- vxworks ->
- {skipped, "No SSL-support for VxWorks."};
- _ ->
- IP = orber_test_lib:get_host(),
- ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
- 1, [{iiop_ssl_port, 0},
- {flags,
- (?ORB_ENV_LOCAL_INTERFACE bor
- ?ORB_ENV_ENABLE_NAT)},
- {ip_address, IP}]),
- ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
- 1, [{iiop_ssl_port, 0}]),
- {ok, ServerNode, _ServerHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(ServerOptions)),
- ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
- SSLServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
- NATSSLServerPort = SSLServerPort+1,
- {ok, Ref} = ?match({ok, _},
- orber_test_lib:remote_apply(ServerNode, orber,
- add_listen_interface,
- [IP, ssl, NATSSLServerPort])),
- orber_test_lib:remote_apply(ServerNode, orber_env, configure_override,
- [nat_iiop_ssl_port,
- {local, NATSSLServerPort, [{NATSSLServerPort, NATSSLServerPort}]}]),
-
- {ok, ClientNode, _ClientHost} =
- ?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
- ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
- install_test_data,
- [ssl])),
-
- IOR1 = ?match(#'IOP_IOR'{},
- orber_test_lib:remote_apply(ClientNode, corba,
- string_to_object,
- ["corbaname::1.2@"++IP++":"++
- integer_to_list(ServerPort)++"/NameService#mamba"])),
-
- ?match({'external', {_IP, _Port, _ObjectKey, _Counter, _TP,
- #host_data{protocol = ssl,
- ssl_data = #'SSLIOP_SSL'{port = NATSSLServerPort}}}},
- iop_ior:get_key(IOR1)),
- ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
- uninstall_test_data,
- [ssl])),
- ?match(ok,
- orber_test_lib:remote_apply(ServerNode, orber,
- remove_listen_interface, [Ref])),
- ok
- end.
+
+ IP = orber_test_lib:get_host(),
+ ServerOptions = orber_test_lib:get_options(iiop_ssl, server,
+ 1, [{iiop_ssl_port, 0},
+ {flags,
+ (?ORB_ENV_LOCAL_INTERFACE bor
+ ?ORB_ENV_ENABLE_NAT)},
+ {ip_address, IP}]),
+ ClientOptions = orber_test_lib:get_options(iiop_ssl, client,
+ 1, [{iiop_ssl_port, 0}]),
+ {ok, ServerNode, _ServerHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(ServerOptions)),
+ ServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_port, []),
+ SSLServerPort = orber_test_lib:remote_apply(ServerNode, orber, iiop_ssl_port, []),
+ NATSSLServerPort = SSLServerPort+1,
+ {ok, Ref} = ?match({ok, _},
+ orber_test_lib:remote_apply(ServerNode, orber,
+ add_listen_interface,
+ [IP, ssl, NATSSLServerPort])),
+ orber_test_lib:remote_apply(ServerNode, orber_env, configure_override,
+ [nat_iiop_ssl_port,
+ {local, NATSSLServerPort, [{NATSSLServerPort, NATSSLServerPort}]}]),
+
+ {ok, ClientNode, _ClientHost} =
+ ?match({ok,_,_}, orber_test_lib:js_node(ClientOptions)),
+ ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
+ install_test_data,
+ [ssl])),
+
+ IOR1 = ?match(#'IOP_IOR'{},
+ orber_test_lib:remote_apply(ClientNode, corba,
+ string_to_object,
+ ["corbaname::1.2@"++IP++":"++
+ integer_to_list(ServerPort)++"/NameService#mamba"])),
+
+ ?match({'external', {_IP, _Port, _ObjectKey, _Counter, _TP,
+ #host_data{protocol = ssl,
+ ssl_data = #'SSLIOP_SSL'{port = NATSSLServerPort}}}},
+ iop_ior:get_key(IOR1)),
+ ?match(ok, orber_test_lib:remote_apply(ServerNode, orber_test_lib,
+ uninstall_test_data,
+ [ssl])),
+ ?match(ok,
+ orber_test_lib:remote_apply(ServerNode, orber,
+ remove_listen_interface, [Ref])),
+ ok.
diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl
index a694dc58c4..ffc13d0e3c 100644
--- a/lib/orber/test/orber_test_lib.erl
+++ b/lib/orber/test/orber_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -95,16 +95,21 @@
%%
%%------------------------------------------------------------
ssl_version() ->
- case catch erlang:system_info(otp_release) of
- Version when is_list(Version) ->
- if
- "R12B" < Version ->
- 3;
- true ->
- 2
- end;
- _ ->
- 2
+ try
+ ssl:module_info(),
+ case catch erlang:system_info(otp_release) of
+ Version when is_list(Version) ->
+ if
+ "R12B" < Version ->
+ 3;
+ true ->
+ 2
+ end;
+ _ ->
+ 2
+ end
+ catch error:undef ->
+ no_ssl
end.
%%------------------------------------------------------------
@@ -126,13 +131,22 @@ version_ok() ->
_ ->
case gen_tcp:listen(0, [{reuseaddr, true}, inet6]) of
{ok, LSock} ->
- gen_tcp:close(LSock),
- true;
+ {ok, Port} = inet:port(LSock),
+ case gen_tcp:connect(Hostname, Port, [inet6]) of
+ {error, _} ->
+ gen_tcp:close(LSock),
+ {skipped, "Inet cannot handle IPv6"};
+ {ok, Socket} ->
+ gen_tcp:close(Socket),
+ gen_tcp:close(LSock),
+ true
+ end;
{error, _} ->
{skipped, "Inet cannot handle IPv6"}
end
end
end.
+
%%------------------------------------------------------------
%% function : get_host
%% Arguments: Family - inet | inet6
@@ -287,9 +301,11 @@ start_ssl(true, Node) ->
start_ssl(_, _) ->
ok.
-start_orber({lightweigth, Options}, Node) ->
+start_orber({lightweight, Options}, Node) ->
+ ok = rpc:call(Node, mnesia, start, []),
ok = rpc:call(Node, orber, start_lightweight, [Options]);
start_orber(lightweight, Node) ->
+ ok = rpc:call(Node, mnesia, start, []),
ok = rpc:call(Node, orber, start_lightweight, []);
start_orber(_, Node) ->
ok = rpc:call(Node, orber, jump_start, []).
@@ -1280,6 +1296,22 @@ test_coding(Obj, Local) ->
?match({'EXCEPTION',{'MARSHAL',_,_,_}},
orber_test_server:
testing_iiop_server_marshal(Obj, "string")),
+
+ RecS = #orber_test_server_rec_struct{chain = [#orber_test_server_rec_struct{chain = []}]},
+ ?match(RecS, orber_test_server:testing_iiop_rec_struct(Obj, RecS)),
+
+ RecU = #orber_test_server_rec_union{label = 'RecursiveType',
+ value = [#orber_test_server_rec_union{label = 'RecursiveType',
+ value = []}]},
+ ?match(RecU, orber_test_server:testing_iiop_rec_union(Obj, RecU)),
+
+%% RecA1 = #any{typecode = unsupported, value = RecS},
+%% RecA2 = #any{typecode = unsupported, value = RecU},
+%% ?match(RecA1,
+%% orber_test_server:testing_iiop_rec_any(Obj, RecA1)),
+%% ?match(RecA2,
+%% orber_test_server:testing_iiop_rec_any(Obj, RecA2)),
+
ok.
%%--------------- Testing Post- & Pre-cond -------------------
diff --git a/lib/orber/test/orber_test_server.idl b/lib/orber/test/orber_test_server.idl
index a88211c941..438c10e19b 100644
--- a/lib/orber/test/orber_test_server.idl
+++ b/lib/orber/test/orber_test_server.idl
@@ -28,7 +28,7 @@ module orber_parent {
};
module orber_test {
-
+
// interface server
interface server : orber_parent::inherrit {
typedef string array[2];
@@ -89,6 +89,23 @@ module orber_test {
const fixed52 fixed52negconst2 = -123.00d;
const fixed52 fixed52negconst3 = -023.00d;
+ struct rec_struct; // Forward declaration
+ typedef sequence<rec_struct> rec_struct_seq;
+ struct rec_struct {
+ rec_struct_seq chain;
+ };
+
+
+ union rec_union; // Forward declaration
+ typedef sequence<rec_union>rec_union_seq;
+
+ enum MyEnum {RecursiveType, NameType};
+
+ union rec_union switch (MyEnum) {
+ case RecursiveType : rec_union_seq chain;
+ case NameType : string aName;
+ };
+
void stop_normal();
void stop_brutal();
@@ -123,6 +140,12 @@ module orber_test {
void testing_iiop_context();
void testing_iiop_server_marshal(inout StrLength6 Str);
+ // Recursive types
+ any testing_iiop_rec_any(in any RecType);
+ rec_struct testing_iiop_rec_struct(in rec_struct RecS);
+ rec_union testing_iiop_rec_union(in rec_union RecU);
+
+
oneway void testing_iiop_oneway_delay(in long Time);
void testing_iiop_twoway_delay(in long Time);
diff --git a/lib/orber/test/orber_test_server_impl.erl b/lib/orber/test/orber_test_server_impl.erl
index 35296cb619..10a9caf242 100644
--- a/lib/orber/test/orber_test_server_impl.erl
+++ b/lib/orber/test/orber_test_server_impl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -55,6 +55,9 @@
testing_iiop_void/2,
testing_iiop_context/2,
testing_iiop_server_marshal/3,
+ testing_iiop_rec_any/3,
+ testing_iiop_rec_struct/3,
+ testing_iiop_rec_union/3,
relay_call/3,
relay_cast/3,
%% Testing pseudo calls.
@@ -197,6 +200,16 @@ testing_iiop_context(_Self, State) ->
testing_iiop_server_marshal(_Self, State, _String) ->
{reply, {ok, false}, State}.
+testing_iiop_rec_any(_Self, State, RAny) ->
+ {reply, RAny, State}.
+
+testing_iiop_rec_struct(_Self, State, RecS) ->
+ {reply, RecS, State}.
+
+testing_iiop_rec_union(_Self, State, RecU) ->
+ {reply, RecU, State}.
+
+
testing_iiop_oneway_delay(_Self, State, Time) ->
timer:sleep(Time),
{noreply, State}.
diff --git a/lib/orber/test/orber_test_timeout_server_impl.erl b/lib/orber/test/orber_test_timeout_server_impl.erl
index 138eb51d92..67ea897fdd 100644
--- a/lib/orber/test/orber_test_timeout_server_impl.erl
+++ b/lib/orber/test/orber_test_timeout_server_impl.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/orber/test/orber_web_SUITE.erl b/lib/orber/test/orber_web_SUITE.erl
index ffa7468853..a3b4d8547d 100644
--- a/lib/orber/test/orber_web_SUITE.erl
+++ b/lib/orber/test/orber_web_SUITE.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
-module(orber_web_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/include/corba.hrl").
-include_lib("orber/src/orber_iiop.hrl").
@@ -65,12 +65,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -78,10 +78,28 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["This suite is for testing the Orber Web API"];
-all(suite) ->
- [menu, configure, info, nameservice, ifr_select, ifr_data,
- create, delete_ctx, add_ctx, delete_obj, server].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [menu, configure, info, nameservice, ifr_select,
+ ifr_data, create, delete_ctx, add_ctx, delete_obj,
+ server].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
@@ -95,7 +113,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
oe_orber_test_server:oe_unregister(),
orber:jump_stop(),
Path = code:which(?MODULE),
diff --git a/lib/orber/test/tc_SUITE.erl b/lib/orber/test/tc_SUITE.erl
index 807a663219..52b7f8852f 100644
--- a/lib/orber/test/tc_SUITE.erl
+++ b/lib/orber/test/tc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%-----------------------------------------------------------------
-module(tc_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("orber/src/orber_iiop.hrl").
-define(default_timeout, ?t:minutes(3)).
@@ -128,12 +128,12 @@
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
--export([]).
-compile(export_all).
%%-----------------------------------------------------------------
@@ -141,19 +141,32 @@
%% Args:
%% Returns:
%%-----------------------------------------------------------------
-all(doc) -> ["Description", "more description"];
-all(suite) ->
- [null, void,
- short, ushort,
- long, ulong,
- longlong, ulonglong,
- boolean, char, wchar, octet,
- float, double, longdouble,
- any, typecode, principal, object_reference,
- struct, union, enum, string, wstring, sequence, array,
- alias, exception, fixed, value, value_box, native,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [null, void, short, ushort, long, ulong, longlong,
+ ulonglong, boolean, char, wchar, octet, float, double,
+ longdouble, any, typecode, principal, object_reference,
+ struct, union, enum, string, wstring, sequence, array,
+ alias, exception, fixed, value, value_box, native,
abstract_interface, indirection, get_tc].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
@@ -163,7 +176,7 @@ init_per_testcase(_Case, Config) ->
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk
index ec349ee189..5f17cda229 100644
--- a/lib/orber/vsn.mk
+++ b/lib/orber/vsn.mk
@@ -1,21 +1,3 @@
-ORBER_VSN = 3.6.16
+ORBER_VSN = 3.6.20
-TICKETS = OTP-8489 \
- OTP-8543
-
-TICKETS_3.6.15 = OTP-8353 \
- OTP-8354 \
- OTP-8374 \
- OTP-8409 \
- OTP-8448
-
-TICKETS_3.6.14 = OTP-8201
-
-TICKETS_3.6.13 = OTP-7987
-
-TICKETS_3.6.12 = OTP-7906
-
-TICKETS_3.6.11 = OTP-7837
-
-TICKETS_3.6.10 = OTP-7595
diff --git a/lib/os_mon/test/Makefile b/lib/os_mon/test/Makefile
new file mode 100644
index 0000000000..a240640f92
--- /dev/null
+++ b/lib/os_mon/test/Makefile
@@ -0,0 +1,92 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES= \
+ os_mon_SUITE \
+ disksup_SUITE \
+ memsup_SUITE \
+ cpu_sup_SUITE \
+ os_mon_mib_SUITE \
+ os_sup_SUITE \
+ os_mon_conf
+
+EBIN = .
+
+HRL_FILES=
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+SOURCE = $(ERL_FILES) $(HRL_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/os_mon_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \
+ -I$(ERL_TOP)/lib/snmp/include
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\
+ > $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) $(RELSYSDIR)
+
+## tar chf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl
new file mode 100644
index 0000000000..d04adbb6d3
--- /dev/null
+++ b/lib/os_mon/test/cpu_sup_SUITE.erl
@@ -0,0 +1,296 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(cpu_sup_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([load_api/1]).
+-export([util_api/1, util_values/1]).
+-export([port/1]).
+-export([terminate/1, unavailable/1, restart/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line ok = application:start(os_mon),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ?line ok = application:stop(os_mon),
+ Config.
+
+init_per_testcase(unavailable, Config) ->
+ terminate(Config),
+ init_per_testcase(dummy, Config);
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+
+end_per_testcase(unavailable, Config) ->
+ restart(Config),
+ end_per_testcase(dummy, Config);
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case test_server:os_type() of
+ {unix, sunos} ->
+ [load_api, util_api, util_values, port, unavailable];
+ {unix, linux} ->
+ [load_api, util_api, util_values, port, unavailable];
+ {unix, _OSname} -> [load_api];
+ _OS -> [unavailable]
+ end.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+load_api(suite) ->
+ [];
+load_api(doc) ->
+ ["Test of load API functions"];
+load_api(Config) when is_list(Config) ->
+
+ %% nprocs()
+ ?line N = cpu_sup:nprocs(),
+ ?line true = is_integer(N),
+ ?line true = N>0,
+
+ %% avg1()
+ ?line Load1 = cpu_sup:avg1(),
+ ?line true = is_integer(Load1),
+ ?line true = Load1>0,
+
+ %% avg5()
+ ?line Load5 = cpu_sup:avg5(),
+ ?line true = is_integer(Load5),
+ ?line true = Load5>0,
+
+ %% avg15()
+ ?line Load15 = cpu_sup:avg15(),
+ ?line true = is_integer(Load15),
+ ?line true = Load15>0,
+
+ ok.
+
+util_api(suite) ->
+ [];
+util_api(doc) ->
+ ["Test of utilization API functions"];
+util_api(Config) when is_list(Config) ->
+ %% Some useful funs when testing util/1
+ BusyP = fun({user, _Share}) -> true;
+ ({nice_user, _Share}) -> true;
+ ({kernel, _Share}) -> true;
+ ({hard_irq, _Share}) -> true;
+ ({soft_irq, _Share}) -> true;
+ (_) -> false
+ end,
+ NonBusyP = fun({wait, _Share}) -> true;
+ ({idle, _Share}) -> true;
+ ({steal, _Share}) -> true;
+ (_) -> false
+ end,
+ Sum = fun({_Tag, X}, Acc) -> Acc+X end,
+
+ %% util()
+ ?line Util1 = cpu_sup:util(),
+ ?line true = is_number(Util1),
+ ?line true = Util1>0,
+ ?line Util2 = cpu_sup:util(),
+ ?line true = is_number(Util2),
+ ?line true = Util2>0,
+
+ %% util([])
+ ?line {all, Busy1, NonBusy1, []} = cpu_sup:util([]),
+ ?line 100.00 = Busy1 + NonBusy1,
+
+ %% util([detailed])
+ ?line {Cpus2, Busy2, NonBusy2, []} = cpu_sup:util([detailed]),
+ ?line true = lists:all(fun(X) -> is_integer(X) end, Cpus2),
+ ?line true = lists:all(BusyP, Busy2),
+ ?line true = lists:all(NonBusyP, NonBusy2),
+ ?line 100.00 = lists:foldl(Sum,0,Busy2)+lists:foldl(Sum,0,NonBusy2),
+
+ %% util([per_cpu])
+ ?line [{Cpu3, Busy3, NonBusy3, []}|_] = cpu_sup:util([per_cpu]),
+ ?line true = is_integer(Cpu3),
+ ?line 100.00 = Busy3 + NonBusy3,
+
+ %% util([detailed, per_cpu])
+ ?line [{Cpu4, Busy4, NonBusy4, []}|_] =
+ cpu_sup:util([detailed, per_cpu]),
+ ?line true = is_integer(Cpu4),
+ ?line true = lists:all(BusyP, Busy2),
+ ?line true = lists:all(NonBusyP, NonBusy2),
+ ?line 100.00 = lists:foldl(Sum,0,Busy4)+lists:foldl(Sum,0,NonBusy4),
+
+ %% bad util/1 calls
+ ?line {'EXIT',{badarg,_}} = (catch cpu_sup:util(detailed)),
+ ?line {'EXIT',{badarg,_}} = (catch cpu_sup:util([detialed])),
+
+ ok.
+
+-define(SPIN_TIME, 1000).
+
+util_values(suite) ->
+ [];
+util_values(doc) ->
+ ["Test utilization values"];
+util_values(Config) when is_list(Config) ->
+
+ Tester = self(),
+ Ref = make_ref(),
+ Loop = fun (L) -> L(L) end,
+ Spinner = fun () ->
+ Looper = spawn_link(fun () -> Loop(Loop) end),
+ receive after ?SPIN_TIME -> ok end,
+ unlink(Looper),
+ exit(Looper, kill),
+ Tester ! Ref
+ end,
+
+ ?line cpu_sup:util(),
+
+ ?line spawn_link(Spinner),
+ ?line receive Ref -> ok end,
+ ?line HighUtil1 = cpu_sup:util(),
+
+ ?line receive after ?SPIN_TIME -> ok end,
+ ?line LowUtil1 = cpu_sup:util(),
+
+ ?line spawn_link(Spinner),
+ ?line receive Ref -> ok end,
+ ?line HighUtil2 = cpu_sup:util(),
+
+ ?line receive after ?SPIN_TIME -> ok end,
+ ?line LowUtil2 = cpu_sup:util(),
+
+ Utils = [{high1,HighUtil1}, {low1,LowUtil1},
+ {high2,HighUtil2}, {low2,LowUtil2}],
+ ?t:format("Utils: ~p~n", [Utils]),
+
+ ?line false = LowUtil1 > HighUtil1,
+ ?line false = LowUtil1 > HighUtil2,
+ ?line false = LowUtil2 > HighUtil1,
+ ?line false = LowUtil2 > HighUtil2,
+
+ ok.
+
+
+% Outdated
+% The portprogram is now restarted if killed, and not by os_mon...
+
+port(suite) ->
+ [];
+port(doc) ->
+ ["Test that cpu_sup handles a terminating port program"];
+port(Config) when is_list(Config) ->
+ case cpu_sup_os_pid() of
+ {ok, PidStr} ->
+ %% Monitor cpu_sup
+ ?line MonRef = erlang:monitor(process, cpu_sup),
+ ?line N1 = cpu_sup:nprocs(),
+ ?line true = N1>0,
+
+ %% Kill the port program
+ case os:cmd("kill -9 " ++ PidStr) of
+ [] ->
+ %% cpu_sup should not terminate
+ receive
+ {'DOWN', MonRef, _, _, Reason} ->
+ ?line ?t:fail({unexpected_exit_reason, Reason})
+ after 3000 ->
+ ok
+ end,
+
+ %% Give cpu_sup time to restart cpu_sup port
+ ?t:sleep(?t:seconds(3)),
+ ?line N2 = cpu_sup:nprocs(),
+ ?line true = N2>0,
+
+ erlang:demonitor(MonRef),
+ ok;
+
+ Line ->
+ erlang:demonitor(MonRef),
+ {skip, {not_killed, Line}}
+ end;
+ _ ->
+ {skip, os_pid_not_found }
+ end.
+
+terminate(suite) ->
+ [];
+terminate(Config) when is_list(Config) ->
+ ?line ok = application:set_env(os_mon, start_cpu_sup, false),
+ ?line ok = supervisor:terminate_child(os_mon_sup, cpu_sup),
+ ok.
+
+unavailable(suite) ->
+ [];
+unavailable(doc) ->
+ ["Test correct behaviour when service is unavailable"];
+unavailable(Config) when is_list(Config) ->
+
+ %% Make sure all API functions return their dummy values
+ ?line 0 = cpu_sup:nprocs(),
+ ?line 0 = cpu_sup:avg1(),
+ ?line 0 = cpu_sup:avg5(),
+ ?line 0 = cpu_sup:avg15(),
+ ?line 0 = cpu_sup:util(),
+ ?line {all,0,0,[]} = cpu_sup:util([]),
+ ?line {all,0,0,[]} = cpu_sup:util([detailed]),
+ ?line {all,0,0,[]} = cpu_sup:util([per_cpu]),
+ ?line {all,0,0,[]} = cpu_sup:util([detailed,per_cpu]),
+
+ ok.
+
+restart(suite) ->
+ [];
+restart(Config) when is_list(Config) ->
+ ?line ok = application:set_env(os_mon, start_cpu_sup, true),
+ ?line {ok, _Pid} = supervisor:restart_child(os_mon_sup, cpu_sup),
+ ok.
+
+%% Aux
+
+cpu_sup_os_pid() ->
+ Str = os:cmd("ps -e | grep '[c]pu_sup'"),
+ case io_lib:fread("~s", Str) of
+ {ok, [Pid], _Rest} -> {ok, Pid};
+ _ -> {error, pid_not_found}
+ end.
diff --git a/lib/os_mon/test/disksup_SUITE.erl b/lib/os_mon/test/disksup_SUITE.erl
new file mode 100644
index 0000000000..c1ff2c6afc
--- /dev/null
+++ b/lib/os_mon/test/disksup_SUITE.erl
@@ -0,0 +1,440 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(disksup_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([api/1, config/1, alarm/1]).
+-export([port/1]).
+-export([terminate/1, unavailable/1, restart/1]).
+-export([otp_5910/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line ok = application:start(os_mon),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ?line ok = application:stop(os_mon),
+ Config.
+
+init_per_testcase(unavailable, Config) ->
+ terminate(Config),
+ init_per_testcase(dummy, Config);
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog,Dog} | Config].
+
+end_per_testcase(unavailable, Config) ->
+ restart(Config),
+ end_per_testcase(dummy, Config);
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ Bugs = [otp_5910],
+ case test_server:os_type() of
+ {unix, sunos} ->
+ [api, config, alarm, port, unavailable] ++ Bugs;
+ {unix, _OSname} -> [api, alarm] ++ Bugs;
+ {win32, _OSname} -> [api, alarm] ++ Bugs;
+ _OS -> [unavailable]
+ end.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+api(suite) ->
+ [];
+api(doc) ->
+ ["Test of API functions"];
+api(Config) when is_list(Config) ->
+
+ %% get_disk_data()
+ ?line [{Id, KByte, Capacity}|_] = disksup:get_disk_data(),
+ ?line true = io_lib:printable_list(Id),
+ ?line true = is_integer(KByte),
+ ?line true = is_integer(Capacity),
+ ?line true = KByte>0,
+ ?line true = Capacity>0,
+
+ %% get_check_interval()
+ ?line 1800000 = disksup:get_check_interval(),
+
+ %% set_check_interval(Minutes)
+ ?line ok = disksup:set_check_interval(20),
+ ?line 1200000 = disksup:get_check_interval(),
+ ?line {'EXIT',{badarg,_}} = (catch disksup:set_check_interval(0.5)),
+ ?line 1200000 = disksup:get_check_interval(),
+ ?line ok = disksup:set_check_interval(30),
+
+ %% get_almost_full_threshold()
+ ?line 80 = disksup:get_almost_full_threshold(),
+
+ %% set_almost_full_threshold(Float)
+ ?line ok = disksup:set_almost_full_threshold(0.90),
+ ?line 90 = disksup:get_almost_full_threshold(),
+ ?line {'EXIT',{badarg,_}} =
+ (catch disksup:set_almost_full_threshold(-0.5)),
+ ?line 90 = disksup:get_almost_full_threshold(),
+ ?line ok = disksup:set_almost_full_threshold(0.80),
+
+ ok.
+
+config(suite) ->
+ [];
+config(doc) ->
+ ["Test configuration"];
+config(Config) when is_list(Config) ->
+
+ %% Change configuration parameters and make sure change is reflected
+ %% when disksup is restarted
+ ?line ok =
+ application:set_env(os_mon, disk_space_check_interval, 29),
+ ?line ok =
+ application:set_env(os_mon, disk_almost_full_threshold, 0.81),
+
+ ?line ok = supervisor:terminate_child(os_mon_sup, disksup),
+ ?line {ok, _Child1} = supervisor:restart_child(os_mon_sup, disksup),
+
+ ?line 1740000 = disksup:get_check_interval(),
+ ?line 81 = disksup:get_almost_full_threshold(),
+
+ %% Also try this with bad parameter values, should be ignored
+ ?line ok =
+ application:set_env(os_mon, disk_space_check_interval, 0.5),
+ ?line ok =
+ application:set_env(os_mon, disk_almost_full_threshold, -0.81),
+
+ ?line ok = supervisor:terminate_child(os_mon_sup, disksup),
+ ?line {ok, _Child2} = supervisor:restart_child(os_mon_sup, disksup),
+
+ ?line 1800000 = disksup:get_check_interval(),
+ ?line 80 = disksup:get_almost_full_threshold(),
+
+ %% Reset configuration parameters
+ ?line ok =
+ application:set_env(os_mon, disk_space_check_interval, 30),
+ ?line ok =
+ application:set_env(os_mon, disk_almost_full_threshold, 0.80),
+
+ ok.
+
+%%----------------------------------------------------------------------
+%% NOTE: The test case is a bit weak as it will fail if the disk usage
+%% changes too much during its course, or if there are timing problems
+%% with the alarm_handler receiving the alarms too late
+%%----------------------------------------------------------------------
+alarm(suite) ->
+ [];
+alarm(doc) ->
+ ["Test that alarms are set and cleared"];
+alarm(Config) when is_list(Config) ->
+
+ %% Find out how many disks exceed the threshold
+ %% and make sure the corresponding number of alarms is set
+ ?line Threshold1 = disksup:get_almost_full_threshold(), % 80
+ ?line Data1 = disksup:get_disk_data(),
+ ?line Over1 = over_threshold(Data1, Threshold1),
+ ?line Alarms1 = get_alarms(),
+ if
+ Over1==length(Alarms1) ->
+ ?line true;
+ true ->
+ dump_info(),
+ ?line ?t:fail({bad_alarms, Threshold1, Data1, Alarms1})
+ end,
+
+ %% Try to find a disk with space usage below Threshold1,
+ %% lower the threshold accordingly and make sure new alarms are set
+ Fun1 = fun({_Id, _Kbyte, Capacity}) ->
+ if
+ Capacity>0, Capacity<Threshold1 -> true;
+ true -> false
+ end
+ end,
+ ?line case until(Fun1, Data1) of
+ {_, _, Cap1} ->
+ Threshold2 = Cap1-1,
+ ?line ok =
+ disksup:set_almost_full_threshold(Threshold2/100),
+ ?line disksup ! timeout, % force a disk check
+ ?line Data2 = disksup:get_disk_data(),
+ ?line Over2 = over_threshold(Data2, Threshold2),
+ ?line Alarms2 = get_alarms(),
+ if
+ Over2==length(Alarms2), Over2>Over1 ->
+ ?line true;
+ true ->
+ dump_info(),
+ ?line ?t:fail({bad_alarms, Threshold2, Data2, Alarms2})
+ end;
+ false ->
+ ?line ignore
+ end,
+
+ %% Find out the highest space usage among all disks
+ %% and try to raise the threshold above this value,
+ %% make sure all alarms are cleared
+ Fun2 = fun({_Id, _Kbyte, Capacity}, MaxAcc) ->
+ if
+ Capacity>MaxAcc -> Capacity;
+ true -> MaxAcc
+ end
+ end,
+ ?line case lists:foldl(Fun2, 0, Data1) of
+ Max when Max<100 ->
+ Threshold3 = Max+1,
+ ?line ok =
+ disksup:set_almost_full_threshold(Threshold3/100),
+ ?line disksup ! timeout, % force a disk check
+ ?line Data3 = disksup:get_disk_data(),
+ ?line Over3 = over_threshold(Data3, Threshold3),
+ ?line Alarms3 = get_alarms(),
+ if
+ Over3==0, length(Alarms3)==0 ->
+ ?line ok;
+ true ->
+ dump_info(),
+ ?line ?t:fail({bad_alarms, Threshold3, Data3, Alarms3})
+ end;
+ 100 ->
+ ?line ignore
+ end,
+
+ %% Reset threshold
+ ?line ok = disksup:set_almost_full_threshold(Threshold1/100),
+
+ ok.
+
+over_threshold(Data, Threshold) ->
+ Data2 = remove_duplicated_disks(lists:keysort(1, Data)),
+ lists:foldl(fun({_Id, _Kbyte, Cap}, N) when Cap>=Threshold ->
+ N+1;
+ (_DiskData, N) ->
+ N
+ end,
+ 0,
+ Data2).
+
+%% On some platforms (for example MontaVista) data for one disk can be
+%% "duplicated":
+%% Linux ppb 2.4.20_mvl31-pcore680 #1 Sun Feb 1 23:12:56 PST 2004 ppc unknown
+%%
+%% MontaVista(R) Linux(R) Professional Edition 3.1
+%%
+%% [ppb:~]> /bin/df -lk
+%% Filesystem 1k-blocks Used Available Use% Mounted on
+%% rootfs 8066141 3023763 4961717 38% /
+%% /dev/root 8066141 3023763 4961717 38% /
+%% tmpfs 192892 0 192892 0% /dev/shm
+%%
+%% disksup:
+%% [{"/",8066141,38}, {"/",8066141,38}, {"/dev/shm",192892,0}]
+%%
+%% disksup will only set ONE alarm for "/".
+%% Therefore the list of disk data must be sorted and duplicated disk
+%% tuples removed before calculating how many alarms should be set, or
+%% the testcase will fail erroneously.
+remove_duplicated_disks([{Id, _, _}, {Id, Kbyte, Cap}|T]) ->
+ remove_duplicated_disks([{Id, Kbyte, Cap}|T]);
+remove_duplicated_disks([H|T]) ->
+ [H|remove_duplicated_disks(T)];
+remove_duplicated_disks([]) ->
+ [].
+
+get_alarms() ->
+ lists:filter(fun({{disk_almost_full, _Disk},_}) -> true;
+ (_) -> false
+ end,
+ alarm_handler:get_alarms()).
+
+until(Fun, [H|T]) ->
+ case Fun(H) of
+ true -> H;
+ false ->
+ until(Fun, T)
+ end;
+until(_Fun, []) ->
+ false.
+
+port(suite) ->
+ [];
+port(doc) ->
+ ["Test that disksup handles a terminating port program"];
+port(Config) when is_list(Config) ->
+ ?line Str = os:cmd("ps -ef | grep '[d]isksup'"),
+ case io_lib:fread("~s ~s", Str) of
+ {ok, [_Uid,Pid], _Rest} ->
+
+ %% Monitor disksup
+ ?line MonRef = erlang:monitor(process, disksup),
+ ?line [{_Disk1,Kbyte1,_Cap1}|_] = disksup:get_disk_data(),
+ ?line true = Kbyte1>0,
+
+ %% Kill the port program
+ case os:cmd("kill -9 " ++ Pid) of
+ [] ->
+
+ %% disksup should now terminate
+ receive
+ {'DOWN', MonRef, _, _, {port_died, _Reason}} ->
+ ok;
+ {'DOWN', MonRef, _, _, Reason} ->
+ ?line ?t:fail({unexpected_exit_reason, Reason})
+ after
+ 3000 ->
+ ?line ?t:fail({still_alive, Str})
+ end,
+
+ %% Give os_mon_sup time to restart disksup
+ ?t:sleep(?t:seconds(3)),
+ ?line [{_Disk2,Kbyte2,_Cap2}|_] =
+ disksup:get_disk_data(),
+ ?line true = Kbyte2>0,
+
+ ok;
+
+ Line ->
+ erlang:demonitor(MonRef),
+ {skip, {not_killed, Line}}
+ end;
+ _ ->
+ {skip, {os_pid_not_found, Str}}
+ end.
+
+terminate(suite) ->
+ [];
+terminate(Config) when is_list(Config) ->
+ ?line ok = application:set_env(os_mon, start_disksup, false),
+ ?line ok = supervisor:terminate_child(os_mon_sup, disksup),
+ ok.
+
+unavailable(suite) ->
+ [];
+unavailable(doc) ->
+ ["Test correct behaviour when service is unavailable"];
+unavailable(Config) when is_list(Config) ->
+
+ %% Make sure all API functions return their dummy values
+ ?line [{"none",0,0}] = disksup:get_disk_data(),
+ ?line 1800000 = disksup:get_check_interval(),
+ ?line ok = disksup:set_check_interval(5),
+ ?line 80 = disksup:get_almost_full_threshold(),
+ ?line ok = disksup:set_almost_full_threshold(0.9),
+
+ ok.
+
+restart(suite) ->
+ [];
+restart(Config) when is_list(Config) ->
+ ?line ok = application:set_env(os_mon, start_disksup, true),
+ ?line {ok, _Pid} = supervisor:restart_child(os_mon_sup, disksup),
+ ok.
+
+otp_5910(suite) ->
+ [];
+otp_5910(doc) ->
+ ["Test that alarms are cleared if disksup crashes or "
+ "if OS_Mon is stopped"];
+otp_5910(Config) when is_list(Config) ->
+
+ %% Make sure disksup sets at least one alarm
+ ?line Data = disksup:get_disk_data(),
+ ?line Threshold0 = disksup:get_almost_full_threshold(),
+ ?line Threshold = case over_threshold(Data, Threshold0) of
+ 0 ->
+ [{_Id,_Kbyte,Cap}|_] = Data,
+ ?line ok = disksup:set_almost_full_threshold((Cap-1)/100),
+ Cap-1;
+ _N ->
+ Threshold0
+ end,
+ ?line ok = application:set_env(os_mon,
+ disk_almost_full_threshold,
+ Threshold/100),
+ ?line disksup ! timeout, % force a disk check
+ ?line Data2 = disksup:get_disk_data(),
+ ?line Over = over_threshold(Data2, Threshold),
+ ?line Alarms = get_alarms(),
+ if
+ Over==0 ->
+ ?line ?t:fail({threshold_too_low, Data2, Threshold});
+ Over==length(Alarms) ->
+ ok;
+ true ->
+ dump_info(),
+ ?line ?t:fail({bad_alarms, Threshold, Data2, Alarms})
+ end,
+
+ %% Kill disksup
+ exit(whereis(disksup), faked_disksup_crash),
+
+ %% Wait a little to make sure disksup has been restarted,
+ %% then make sure the alarms are set once, but not twice
+ ?t:sleep(?t:seconds(1)),
+ ?line Data3 = disksup:get_disk_data(),
+ ?line Alarms2 = get_alarms(),
+ if
+ length(Alarms2)==length(Alarms) ->
+ ok;
+ true ->
+ dump_info(),
+ ?line ?t:fail({bad_alarms, Threshold, Data3, Alarms,Alarms2})
+ end,
+
+ %% Stop OS_Mon and make sure all disksup alarms are cleared
+ ?line ok = application:stop(os_mon),
+ ?t:sleep(?t:seconds(1)),
+ ?line Alarms3 = get_alarms(),
+ if
+ length(Alarms3)==0 ->
+ ok;
+ true ->
+ ?line ?t:fail({alarms_not_cleared, Alarms3})
+ end,
+
+ %% Reset threshold and restart OS_Mon
+ ?line ok = application:set_env(os_mon,
+ disksup_almost_full_threshold, 0.8),
+ ?line ok = disksup:set_almost_full_threshold(0.8),
+ ?line ok = application:start(os_mon),
+
+ ok.
+
+dump_info() ->
+ io:format("Status: ~p~n", [sys:get_status(disksup)]).
diff --git a/lib/os_mon/test/memsup_SUITE.erl b/lib/os_mon/test/memsup_SUITE.erl
new file mode 100644
index 0000000000..1d9ebca51f
--- /dev/null
+++ b/lib/os_mon/test/memsup_SUITE.erl
@@ -0,0 +1,793 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(memsup_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([api/1, alarm1/1, alarm2/1, process/1]).
+-export([config/1, timeout/1, unavailable/1, port/1]).
+-export([otp_5910/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_suite(Config) when is_list(Config) ->
+ ?line ok = application:start(os_mon),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ?line ok = application:stop(os_mon),
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog,Dog} | Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ Config.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ All = case test_server:os_type() of
+ {unix, sunos} ->
+ [api, alarm1, alarm2, process, config, timeout,
+ unavailable, port];
+ {unix, linux} ->
+ [api, alarm1, alarm2, process, timeout];
+ _OS -> [api, alarm1, alarm2, process]
+ end,
+ Bugs = [otp_5910],
+ All ++ Bugs.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+api(suite) ->
+ [];
+api(doc) ->
+ ["Test of API functions"];
+api(Config) when is_list(Config) ->
+
+ %% get_memory_data()
+ ?line RegMemData = memsup:get_memory_data(),
+ case RegMemData of
+ {TotMem, AllBytes, {Pid, PidBytes}} when is_integer(TotMem),
+ is_integer(AllBytes),
+ is_pid(Pid),
+ is_integer(PidBytes) ->
+ ok;
+ {0, 0, _WorstPid} ->
+ ?line ?t:fail(first_data_collection_failed);
+ _ ->
+ ?line ?t:fail({bad_return, RegMemData})
+ end,
+
+ %% get_system_memory_data()
+ ?line ExtMemData = memsup:get_system_memory_data(),
+ Tags = [ total_memory,
+ free_memory,
+ system_total_memory,
+ largest_free,
+ number_of_free,
+ free_swap,
+ total_swap,
+ cached_memory,
+ buffered_memory,
+ shared_memory],
+
+ ?line true = lists:all(fun({Tag,Value}) when is_atom(Tag),
+ is_integer(Value) ->
+ lists:member(Tag, Tags);
+ (_) ->
+ false
+ end,
+ ExtMemData),
+
+ %% get_os_wordsize()
+ ?line ok = case memsup:get_os_wordsize() of
+ 32 -> ok;
+ 64 -> ok;
+ unsupported_os -> ok;
+ _ -> error
+ end,
+
+ %% get_check_interval()
+ ?line 60000 = memsup:get_check_interval(),
+
+ %% set_check_interval(Minutes)
+ ?line ok = memsup:set_check_interval(2),
+ ?line 120000 = memsup:get_check_interval(),
+ ?line {'EXIT',{badarg,_}} =
+ (catch memsup:set_check_interval(0.2)),
+ ?line 120000 = memsup:get_check_interval(),
+ ?line ok = memsup:set_check_interval(1),
+
+ %% get_procmem_high_watermark()
+ ?line 5 = memsup:get_procmem_high_watermark(),
+
+ %% set_procmem_high_watermark()
+ ?line ok = memsup:set_procmem_high_watermark(0.1),
+ ?line 10 = memsup:get_procmem_high_watermark(),
+ ?line {'EXIT',{badarg,_}} =
+ (catch memsup:set_procmem_high_watermark(-0.1)),
+ ?line 10 = memsup:get_procmem_high_watermark(),
+ ?line ok = memsup:set_procmem_high_watermark(0.05),
+
+ %% get_sysmem_high_watermark()
+ ?line 80 = memsup:get_sysmem_high_watermark(),
+
+ %% set_sysmem_high_watermark()
+ ?line ok = memsup:set_sysmem_high_watermark(0.9),
+ ?line 90 = memsup:get_sysmem_high_watermark(),
+ ?line {'EXIT',{badarg,_}} =
+ (catch memsup:set_sysmem_high_watermark(-0.9)),
+ ?line 90 = memsup:get_sysmem_high_watermark(),
+ ?line ok = memsup:set_sysmem_high_watermark(0.8),
+
+ %% get|set_helper_timeout
+ ?line 30 = memsup:get_helper_timeout(),
+ ?line ok = memsup:set_helper_timeout(29),
+ ?line 29 = memsup:get_helper_timeout(),
+ ?line {'EXIT',{badarg,_}} = (catch memsup:set_helper_timeout(31.0)),
+ ?line 29 = memsup:get_helper_timeout(),
+ ok.
+
+%%----------------------------------------------------------------------
+%% NOTE: The test case is a bit weak as it will fail if the memory
+%% usage changes too much during its course.
+%%----------------------------------------------------------------------
+alarm1(suite) ->
+ [];
+alarm1(doc) ->
+ ["Test alarms when memsup_system_only==false"];
+alarm1(Config) when is_list(Config) ->
+
+ %% If system memory usage is too high, the testcase cannot
+ %% be run correctly
+ ?line {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(),
+ io:format("alarm1: Total: ~p, Alloc: ~p~n", [Total, Alloc]),
+ ?line SysUsage = Alloc/Total,
+ if
+ SysUsage>0.99 ->
+ {skip, sys_mem_too_high};
+ true ->
+ alarm1(Config, SysUsage)
+ end.
+
+alarm1(_Config, SysUsage) ->
+ %% Set a long memory check interval, we will force memory checks
+ %% instead
+ ?line ok = memsup:set_check_interval(60),
+
+ %% Check thresholds
+ ?line SysThreshold = (memsup:get_sysmem_high_watermark()/100),
+ ?line ProcThreshold = (memsup:get_procmem_high_watermark()/100),
+
+ %% Check if a system alarm already should be set or not
+ SysP = if
+ SysUsage>SysThreshold -> true;
+ SysUsage=<SysThreshold -> false
+ end,
+
+ %% If system memory is higher than threshold, make sure the system
+ %% alarm is set. Otherwise, make sure it is not set
+ case alarm_set(system_memory_high_watermark) of
+ {true, []} when SysP ->
+ ok;
+ false when not SysP ->
+ ok;
+ _ ->
+ ?line ?t:fail({sys_alarm, SysUsage, SysThreshold})
+ end,
+
+ %% Lower/raise the threshold to clear/set the alarm
+ NewSysThreshold = if
+ SysP ->
+ Value = 1.1*SysUsage,
+ if
+ Value > 0.99 -> 0.99;
+ true -> Value
+ end;
+ not SysP -> 0.9*SysUsage
+ end,
+
+ ?line ok = memsup:set_sysmem_high_watermark(NewSysThreshold),
+
+ %% Initiate and wait for a new data collection
+ ?line ok = force_collection(),
+
+ %% Make sure the alarm is cleared/set
+ ?t:sleep(?t:seconds(5)),
+ case alarm_set(system_memory_high_watermark) of
+ {true, []} when not SysP ->
+ ok;
+ false when SysP ->
+ ok;
+ _ ->
+ ?line ?t:fail({sys_alarm, SysUsage, NewSysThreshold})
+ end,
+
+ %% Reset the threshold to set/clear the alarm again
+ ?line ok = memsup:set_sysmem_high_watermark(SysThreshold),
+ ?line ok = force_collection(),
+ ?t:sleep(?t:seconds(1)),
+ case alarm_set(system_memory_high_watermark) of
+ {true, []} when SysP ->
+ ok;
+ false when not SysP ->
+ ok;
+ _ ->
+ ?line ?t:fail({sys_alarm, SysUsage, SysThreshold})
+ end,
+
+ %% Check memory usage
+ ?line {Total2, _, {WorstPid, PidAlloc}} = memsup:get_memory_data(),
+
+ %% Check if a process alarm already should be set or not
+ PidUsage = PidAlloc/Total2,
+ ProcP = if
+ PidUsage>ProcThreshold -> true;
+ PidUsage=<ProcThreshold -> false
+ end,
+
+ %% Make sure the process alarm is set/not set accordingly
+ case alarm_set(process_memory_high_watermark) of
+ {true, WorstPid} when ProcP ->
+ ok;
+ false when not ProcP ->
+ ok;
+ {true, BadPid1} when ProcP ->
+ ?line ?t:fail({proc_alarm, WorstPid, BadPid1});
+ _ ->
+ ?line ?t:fail({proc_alarm, PidUsage, ProcThreshold})
+ end,
+
+ %% Lower/raise the threshold to clear/set the alarm
+ NewProcThreshold = if
+ ProcP -> 1.1*PidUsage;
+ not ProcP -> 0.9*PidUsage
+ end,
+ ?line ok = memsup:set_procmem_high_watermark(NewProcThreshold),
+ ?line ok = force_collection(),
+ ?t:sleep(?t:seconds(1)),
+ case alarm_set(process_memory_high_watermark) of
+ {true, WorstPid} when not ProcP ->
+ ok;
+ false when ProcP ->
+ ok;
+ {true, BadPid2} when not ProcP ->
+ ?line test_server:fail({proc_alarm, WorstPid, BadPid2});
+ _ ->
+ ?line ?t:fail({proc_alarm, PidUsage, ProcThreshold})
+ end,
+
+ %% Reset the threshold to clear/set the alarm
+ ?line ok = memsup:set_procmem_high_watermark(ProcThreshold),
+ ?line ok = force_collection(),
+ ?t:sleep(?t:seconds(1)),
+ case alarm_set(process_memory_high_watermark) of
+ {true, WorstPid} when ProcP ->
+ ok;
+ false when not ProcP ->
+ ok;
+ {true, BadPid3} when ProcP ->
+ ?line test_server:fail({proc_alarm, WorstPid, BadPid3});
+ _ ->
+ ?line ?t:fail({proc_alarm, PidUsage, ProcThreshold})
+ end,
+
+ %% Reset memory check interval
+ ?line ok = memsup:set_check_interval(1),
+ ok.
+
+alarm2(suite) ->
+ [];
+alarm2(doc) ->
+ ["Test alarms when memsup_system_only==true"];
+alarm2(Config) when is_list(Config) ->
+
+ %% If system memory usage is too high, the testcase cannot
+ %% be run correctly
+ ?line {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(),
+ ?line SysUsage = Alloc/Total,
+ if
+ SysUsage>0.99 ->
+ {skip, sys_mem_too_high};
+ true ->
+ alarm2(Config, SysUsage)
+ end.
+
+alarm2(_Config, _SysUsage) ->
+
+ %% Change memsup_system_only and restart memsup
+ ?line ok = application:set_env(os_mon, memsup_system_only, true),
+ ?line ok = supervisor:terminate_child(os_mon_sup, memsup),
+ ?line {ok, _Memsup1} = supervisor:restart_child(os_mon_sup, memsup),
+
+ %% Set a long memory check interval, we will force memory checks
+ %% instead
+ ?line ok = memsup:set_check_interval(60),
+
+ %% Check data and thresholds
+ ?line {Total, Alloc, undefined} = memsup:get_memory_data(),
+ ?line SysThreshold = (memsup:get_sysmem_high_watermark()/100),
+ ?line true = is_integer(memsup:get_procmem_high_watermark()),
+
+ %% Check if a system alarm already should be set or not
+ ?line SysUsage = Alloc/Total,
+ SysP = if
+ SysUsage>SysThreshold -> true;
+ SysUsage=<SysThreshold -> false
+ end,
+
+ %% If system memory is higher than threshold, make sure the system
+ %% alarm is set. Otherwise, make sure it is not set
+ case alarm_set(system_memory_high_watermark) of
+ {true, []} when SysP ->
+ ok;
+ false when not SysP ->
+ ok;
+ _ ->
+ ?line ?t:fail({sys_alarm, SysUsage, SysThreshold})
+ end,
+
+ %% Lower/raise the threshold to clear/set the alarm
+ NewSysThreshold = if
+ SysP ->
+ Value = 1.1*SysUsage,
+ if
+ Value > 0.99 -> 0.99;
+ true -> Value
+ end;
+ not SysP -> 0.9*SysUsage
+ end,
+
+ ?line ok = memsup:set_sysmem_high_watermark(NewSysThreshold),
+
+ %% Initiate and wait for a new data collection
+ ?line ok = force_collection(),
+
+ %% Make sure the alarm is cleared/set
+ ?t:sleep(?t:seconds(1)),
+ case alarm_set(system_memory_high_watermark) of
+ {true, []} when not SysP ->
+ ok;
+ false when SysP ->
+ ok;
+ _ ->
+ ?line ?t:fail({sys_alarm, SysUsage, NewSysThreshold})
+ end,
+
+ %% Reset the threshold to set/clear the alarm again
+ ?line ok = memsup:set_sysmem_high_watermark(SysThreshold),
+ ?line ok = force_collection(),
+ ?t:sleep(?t:seconds(1)),
+ case alarm_set(system_memory_high_watermark) of
+ {true, []} when SysP ->
+ ok;
+ false when not SysP ->
+ ok;
+ _ ->
+ ?line ?t:fail({sys_alarm, SysUsage, SysThreshold})
+ end,
+
+ %% Reset memsup_system_only and restart memsup
+ %% (memory check interval is then automatically reset)
+ ?line ok = application:set_env(os_mon, memsup_system_only, false),
+ ?line ok = supervisor:terminate_child(os_mon_sup, memsup),
+ ?line {ok, _Memsup2} = supervisor:restart_child(os_mon_sup, memsup),
+
+ ok.
+
+alarm_set(Alarm) ->
+ alarm_set(Alarm, alarm_handler:get_alarms()).
+alarm_set(Alarm, [{Alarm,Data}|_]) ->
+ {true,Data};
+alarm_set(Alarm, [_|T]) ->
+ alarm_set(Alarm, T);
+alarm_set(_Alarm, []) ->
+ false.
+
+process(suite) ->
+ [];
+process(doc) ->
+ ["Make sure memsup discovers a process grown very large"];
+process(Config) when is_list(Config) ->
+
+ %% Set a long memory check interval, we will force memory checks
+ %% instead
+ ?line ok = memsup:set_check_interval(60),
+
+ %% Collect data
+ MemData = memsup:get_memory_data(),
+ io:format("process: memsup:get_memory_data() = ~p~n", [MemData]),
+ ?line {_Total,_Free,{_,Bytes}} = MemData,
+
+ %% Start a new process larger than Worst
+ ?line WorsePid = spawn(fun() -> new_hog(Bytes) end),
+ ?t:sleep(?t:seconds(1)),
+
+ %% Initiate and wait for a new data collection
+ ?line ok = force_collection(),
+
+ %% Check that get_memory_data() returns updated result
+ ?line case memsup:get_memory_data() of
+ {_, _, {WorsePid, _MoreBytes}} ->
+ ok;
+ {_, _, BadWorst} ->
+ ?line ?t:fail({worst_pid, BadWorst})
+ end,
+
+ %% Reset memory check interval
+ ?line exit(WorsePid, done),
+ ?line ok = memsup:set_check_interval(1),
+ ok.
+
+new_hog(Bytes) ->
+ WordSize = erlang:system_info(wordsize),
+ N = (Bytes+200) div WordSize div 2,
+ List = lists:duplicate(N, a),
+ new_hog_1(List).
+
+new_hog_1(List) ->
+ receive
+ _Any -> exit(List)
+ end.
+
+config(suite) ->
+ [];
+config(doc) ->
+ ["Test configuration"];
+config(Config) when is_list(Config) ->
+
+ %% Change configuration parameters and make sure change is reflected
+ %% when memsup is restarted
+ ?line ok = application:set_env(os_mon, memory_check_interval, 2),
+ ?line ok =
+ application:set_env(os_mon, system_memory_high_watermark, 0.9),
+ ?line ok =
+ application:set_env(os_mon, process_memory_high_watermark, 0.1),
+ ?line ok = application:set_env(os_mon, memsup_helper_timeout, 35),
+ ?line ok = application:set_env(os_mon, memsup_system_only, true),
+
+ ?line ok = supervisor:terminate_child(os_mon_sup, memsup),
+ ?line {ok, _Child1} = supervisor:restart_child(os_mon_sup, memsup),
+
+ ?line 120000 = memsup:get_check_interval(),
+ ?line 90 = memsup:get_sysmem_high_watermark(),
+ ?line 10 = memsup:get_procmem_high_watermark(),
+ ?line 35 = memsup:get_helper_timeout(),
+
+ %% Also try this with bad parameter values, should be ignored
+ ?line ok = application:set_env(os_mon, memory_check_interval, 0.2),
+ ?line ok =
+ application:set_env(os_mon, system_memory_high_watermark, -0.9),
+ ?line ok =
+ application:set_env(os_mon, process_memory_high_watermark,-0.1),
+ ?line ok = application:set_env(os_mon, memsup_helper_timeout, 0.35),
+ ?line ok = application:set_env(os_mon, memsup_system_only, arne),
+
+ ?line ok = supervisor:terminate_child(os_mon_sup, memsup),
+ ?line {ok, _Child2} = supervisor:restart_child(os_mon_sup, memsup),
+
+ ?line 60000 = memsup:get_check_interval(),
+ ?line 80 = memsup:get_sysmem_high_watermark(),
+ ?line 5 = memsup:get_procmem_high_watermark(),
+ ?line 30 = memsup:get_helper_timeout(),
+
+ %% Reset configuration parameters
+ ?line ok = application:set_env(os_mon, memory_check_interval, 1),
+ ?line ok =
+ application:set_env(os_mon, system_memory_high_watermark, 0.8),
+ ?line ok =
+ application:set_env(os_mon, process_memory_high_watermark,0.05),
+ ?line ok = application:set_env(os_mon, memsup_helper_timeout, 30),
+ ?line ok = application:set_env(os_mon, memsup_system_only, false),
+
+ ok.
+
+unavailable(suite) ->
+ [];
+unavailable(doc) ->
+ ["Test correct behaviour when service is unavailable"];
+unavailable(Config) when is_list(Config) ->
+
+ %% Close memsup
+ ?line ok = application:set_env(os_mon, start_memsup, false),
+ ?line ok = supervisor:terminate_child(os_mon_sup, memsup),
+
+ %% Make sure all API functions return their dummy values
+ ?line {0,0,{_Pid,0}} = memsup:get_memory_data(),
+ ?line ok = application:set_env(os_mon, memsup_system_only, true),
+ ?line {0,0,undefined} = memsup:get_memory_data(),
+ ?line ok = application:set_env(os_mon, memsup_system_only, false),
+ ?line [] = memsup:get_system_memory_data(),
+ ?line 0 = memsup:get_os_wordsize(),
+ ?line 60000 = memsup:get_check_interval(),
+ ?line ok = memsup:set_check_interval(2),
+ ?line 5 = memsup:get_procmem_high_watermark(),
+ ?line ok = memsup:set_procmem_high_watermark(0.10),
+ ?line 80 = memsup:get_sysmem_high_watermark(),
+ ?line ok = memsup:set_sysmem_high_watermark(0.90),
+ ?line 30 = memsup:get_helper_timeout(),
+ ?line ok = memsup:set_helper_timeout(35),
+
+ %% Start memsup again,
+ ?line ok = application:set_env(os_mon, start_memsup, true),
+ ?line {ok, _Child} = supervisor:restart_child(os_mon_sup, memsup),
+
+ ok.
+
+timeout(suite) ->
+ [];
+timeout(doc) ->
+ ["Test stability of memsup when data collection times out"];
+timeout(Config) when is_list(Config) ->
+
+ %% Set a long memory check interval and memsup_helper timeout,
+ %% we will force memory checks instead and fake timeouts
+ ?line ok = memsup:set_check_interval(60),
+ ?line ok = memsup:set_helper_timeout(3600),
+
+ %% Provoke a timeout during memory collection
+ ?line memsup ! time_to_collect,
+ ?line memsup ! reg_collection_timeout,
+
+ %% Not much we can check though, except that memsup is still running
+ ?line {_,_,_} = memsup:get_memory_data(),
+
+ %% Provoke a timeout during extensive memory collection
+ %% We fake a gen_server:call/2 to be able to send a timeout message
+ %% while the request is being handled
+
+ %% Linux should be handled the same way as solaris.
+
+% TimeoutMsg = case ?t:os_type() of
+% {unix, sunos} -> ext_collection_timeout;
+% {unix, linux} -> reg_collection_timeout
+% end,
+
+ TimeoutMsg = ext_collection_timeout,
+
+ ?line Pid = whereis(memsup),
+ ?line Mref = erlang:monitor(process, Pid),
+ ?line Pid ! {'$gen_call', {self(), Mref}, get_system_memory_data},
+ ?line Pid ! TimeoutMsg,
+ receive
+ {Mref, []} ->
+ erlang:demonitor(Mref),
+ ?line ok;
+ {Mref, Res} ->
+ erlang:demonitor(Mref),
+ ?line ?t:fail({unexpected_result, Res});
+ {'DOWN', Mref, _, _, _} ->
+ ?line ?t:fail(no_result)
+ end,
+
+ %% Reset memory check interval and memsup_helper timeout
+ ?line ok = memsup:set_check_interval(1),
+ ?line ok = memsup:set_helper_timeout(30),
+ ?line memsup ! time_to_collect,
+
+ ?line [_|_] = memsup:get_system_memory_data(),
+
+ ok.
+
+port(suite) ->
+ [];
+port(doc) ->
+ ["Test that memsup handles a terminating port program"];
+port(Config) when is_list(Config) ->
+ ?line Str = os:cmd("ps -e | grep '[m]emsup'"),
+ case io_lib:fread("~s", Str) of
+ {ok, [Pid], _Rest} ->
+
+ %% Monitor memsup
+ ?line MonRef = erlang:monitor(process, memsup),
+ ?line {Total1,_Alloc1,_Worst1} = memsup:get_memory_data(),
+ ?line true = Total1>0,
+
+ %% Kill the port program
+ case os:cmd("kill -9 " ++ Pid) of
+ [] ->
+
+ %% memsup should now terminate
+ receive
+ {'DOWN', MonRef, _, _, {port_died, _Reason}} ->
+ ok;
+ {'DOWN', MonRef, _, _, Reason} ->
+ ?line ?t:fail({unexpected_exit_reason, Reason})
+ after
+ 3000 ->
+ ?line ?t:fail(still_alive)
+ end,
+
+ %% Give os_mon_sup time to restart memsup
+ ?t:sleep(?t:seconds(3)),
+ ?line {Total2,_Alloc2,_Worst2} =
+ memsup:get_memory_data(),
+ ?line true = Total2>0,
+
+ ok;
+
+ Line ->
+ erlang:demonitor(MonRef),
+ {skip, {not_killed, Line}}
+ end;
+ _ ->
+ {skip, {os_pid_not_found, Str}}
+ end.
+
+otp_5910(suite) ->
+ [];
+otp_5910(doc) ->
+ ["Test that alarms are cleared and not set twice"];
+otp_5910(Config) when is_list(Config) ->
+ Alarms =
+ [system_memory_high_watermark, process_memory_high_watermark],
+
+ %% Make sure memsup sets both alarms
+ ?line ok = application:set_env(os_mon, memory_check_interval, 60),
+ ?line ok = memsup:set_check_interval(60),
+ ?line SysThreshold = (memsup:get_sysmem_high_watermark()/100),
+ ?line ProcThreshold = (memsup:get_procmem_high_watermark()/100),
+
+ MemData = memsup:get_memory_data(),
+
+ io:format("otp_5910: memsup:get_memory_data() = ~p~n", [MemData]),
+ ?line {Total, Alloc, {_Pid, _Bytes}} = MemData,
+ ?line Pid = spawn_opt(fun() ->
+ receive
+ die -> ok
+ end
+ end, [{min_heap_size, 1000}]),
+ %% Create a process guaranteed to live, be constant and
+ %% break memsup process limit
+ ?line {memory, Bytes} = erlang:process_info(Pid,memory),
+ ?line SysUsage = Alloc/Total,
+ ?line ProcUsage = Bytes/Total,
+
+ if
+ SysUsage>SysThreshold ->
+ ok;
+ SysUsage=<SysThreshold ->
+ ?line ok = application:set_env(os_mon,
+ sys_mem_high_watermark,
+ 0.5 * SysUsage),
+ ?line ok = memsup:set_sysmem_high_watermark(0.5 * SysUsage)
+ end,
+ if
+ ProcUsage>ProcThreshold ->
+ ok;
+ ProcUsage=<ProcThreshold ->
+ ?line ok = application:set_env(os_mon,
+ proc_mem_high_watermark,
+ 0.5 * ProcUsage),
+ ?line ok = memsup:set_procmem_high_watermark(0.5 *ProcUsage)
+ end,
+ ?line ok = force_collection(),
+ ?t:sleep(?t:seconds(1)),
+ lists:foreach(fun(AlarmId) ->
+ case alarm_set(AlarmId) of
+ {true, _} -> ok;
+ false ->
+ ?line ?t:fail({alarm_not_set,
+ AlarmId})
+ end
+ end,
+ Alarms),
+
+ %% Kill guaranteed process...
+ Pid ! die,
+ %% Kill memsup
+ exit(whereis(memsup), faked_memsup_crash),
+ %% Wait a little to make sure memsup has been restarted,
+ %% then make sure the alarms are set once, but not twice
+ ?t:sleep(?t:seconds(1)),
+ ?line MemUsage = memsup:get_memory_data(),
+ SetAlarms = alarm_handler:get_alarms(),
+ case lists:foldl(fun(system_memory_high_watermark, {S, P}) ->
+ {S+1, P};
+ (process_memory_high_watermark, {S, P}) ->
+ {S, P+1};
+ (_AlarmId, Acc0) ->
+ Acc0
+ end,
+ {0, 0},
+ SetAlarms) of
+ {0, 0} ->
+ ok;
+ _ ->
+ ?line ?t:fail({bad_number_of_alarms, SetAlarms, MemUsage})
+ end,
+
+ %% Stop OS_Mon and make sure all memsup alarms are cleared
+ ?line ok = application:stop(os_mon),
+ ?t:sleep(?t:seconds(1)),
+ lists:foreach(fun(AlarmId) ->
+ case alarm_set(AlarmId) of
+ false -> ok;
+ {true, _} ->
+ ?line ?t:fail({alarm_is_set, AlarmId})
+ end
+ end,
+ Alarms),
+
+ %% Reset configuration and restart OS_Mon
+ ?line ok = application:set_env(os_mon,memory_check_interval,1),
+ ?line ok = application:set_env(os_mon,sys_mem_high_watermark,0.8),
+ ?line ok = application:set_env(os_mon,proc_mem_high_watermark,0.05),
+ ?line ok = application:start(os_mon),
+
+ ok.
+
+%%----------------------------------------------------------------------
+%% Auxiliary
+%%----------------------------------------------------------------------
+
+force_collection() ->
+ erlang:trace(whereis(memsup), true, ['receive']),
+ memsup ! time_to_collect,
+ TimerRef = erlang:send_after(5000, self(), timeout),
+ force_collection(TimerRef).
+
+force_collection(TimerRef) ->
+ receive
+ {trace, _Pid, 'receive', {collected_sys, _Sys}} ->
+ erlang:cancel_timer(TimerRef),
+ erlang:trace(whereis(memsup), false, ['receive']),
+ flush(),
+ ok;
+ {trace, _Pid, 'receive', reg_collection_timeout} ->
+ erlang:cancel_timer(TimerRef),
+ erlang:trace(whereis(memsup), false, ['receive']),
+ flush(),
+ collection_timeout;
+ timout ->
+ erlang:trace(whereis(memsup), false, ['receive']),
+ flush(),
+ timeout;
+ _Msg ->
+ force_collection(TimerRef)
+ end.
+
+flush() ->
+ receive
+ {trace, _, _, _} ->
+ flush();
+ timeout ->
+ flush()
+ after 0 ->
+ ok
+ end.
diff --git a/lib/os_mon/test/os_mon.cover b/lib/os_mon/test/os_mon.cover
new file mode 100644
index 0000000000..aa07391351
--- /dev/null
+++ b/lib/os_mon/test/os_mon.cover
@@ -0,0 +1,2 @@
+{incl_app,os_mon,details}.
+
diff --git a/lib/os_mon/test/os_mon.spec b/lib/os_mon/test/os_mon.spec
new file mode 100644
index 0000000000..d292b258f3
--- /dev/null
+++ b/lib/os_mon/test/os_mon.spec
@@ -0,0 +1 @@
+{suites,"../os_mon_test",all}.
diff --git a/lib/os_mon/test/os_mon_SUITE.erl b/lib/os_mon/test/os_mon_SUITE.erl
new file mode 100644
index 0000000000..f074657d4c
--- /dev/null
+++ b/lib/os_mon/test/os_mon_SUITE.erl
@@ -0,0 +1,108 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(os_mon_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([app_file/1, config/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case test_server:os_type() of
+ {unix, sunos} -> [app_file, config];
+ _OS -> [app_file]
+ end.
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+app_file(suite) ->
+ [];
+app_file(doc) ->
+ ["Testing .app file"];
+app_file(Config) when is_list(Config) ->
+ ?line ok = test_server:app_test(os_mon),
+ ok.
+
+config(suite) ->
+ [];
+config(doc) ->
+ ["Test OS_Mon configuration"];
+config(Config) when is_list(Config) ->
+
+ IsReg = fun(Name) -> is_pid(whereis(Name)) end,
+ IsNotReg = fun(Name) -> undefined == whereis(Name) end,
+
+ ?line ok = application:start(os_mon),
+ ?line true = lists:all(IsReg, [cpu_sup, disksup, memsup]),
+ ?line ok = application:stop(os_mon),
+
+ ?line ok = application:set_env(os_mon, start_cpu_sup, false),
+ ?line ok = application:start(os_mon),
+ ?line true = lists:all(IsReg, [disksup, memsup]),
+ ?line true = IsNotReg(cpu_sup),
+ ?line ok = application:stop(os_mon),
+ ?line ok = application:set_env(os_mon, start_cpu_sup, true),
+
+ ?line ok = application:set_env(os_mon, start_disksup, false),
+ ?line ok = application:start(os_mon),
+ ?line true = lists:all(IsReg, [cpu_sup, memsup]),
+ ?line true = IsNotReg(disksup),
+ ?line ok = application:stop(os_mon),
+ ?line ok = application:set_env(os_mon, start_disksup, true),
+
+ ?line ok = application:set_env(os_mon, start_memsup, false),
+ ?line ok = application:start(os_mon),
+ ?line true = lists:all(IsReg, [cpu_sup, disksup]),
+ ?line true = IsNotReg(memsup),
+ ?line ok = application:stop(os_mon),
+ ?line ok = application:set_env(os_mon, start_memsup, true),
+
+ ok.
diff --git a/lib/os_mon/test/os_mon_conf.erl b/lib/os_mon/test/os_mon_conf.erl
new file mode 100644
index 0000000000..5c1fa43047
--- /dev/null
+++ b/lib/os_mon/test/os_mon_conf.erl
@@ -0,0 +1,28 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(os_mon_conf).
+-export([init/1,fin/1]).
+
+init(Conf) ->
+ RetVal = application:start(os_mon,temporary),
+ [{os_mon,RetVal}|Conf].
+
+fin(Conf) ->
+ application:stop(os_mon),
+ lists:keydelete(os_mon,1,Conf).
diff --git a/lib/os_mon/test/os_mon_mib_SUITE.erl b/lib/os_mon/test/os_mon_mib_SUITE.erl
new file mode 100644
index 0000000000..4bd256a3f7
--- /dev/null
+++ b/lib/os_mon/test/os_mon_mib_SUITE.erl
@@ -0,0 +1,742 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(os_mon_mib_SUITE).
+
+%-define(STANDALONE,1).
+
+-ifdef(STANDALONE).
+-define(line,erlang:display({line,?LINE}),).
+-define(config(A,B), config(A,B)).
+-else.
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("os_mon/include/OTP-OS-MON-MIB.hrl").
+-include_lib("snmp/include/snmp_types.hrl").
+-endif.
+
+% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
+
+
+% Test cases must be exported.
+-export([update_load_table/1]).
+
+-export([get_mem_sys_mark/1, get_mem_proc_mark/1, get_disk_threshold/1,
+ get_load_table/1, get_disk_table/1,
+ real_snmp_request/1, load_unload/1]).
+
+-export([sys_tot_mem/1, sys_used_mem/1, large_erl_process/1,
+ large_erl_process_mem/1, cpu_load/1, cpu_load5/1, cpu_load15/1,
+ os_wordsize/1, sys_tot_mem64/1, sys_used_mem64/1,
+ large_erl_process_mem64/1, disk_descr/1, disk_kbytes/1,
+ disk_capacity/1]).
+
+-export([]).
+-export([otp_6351/1, otp_7441/1]).
+
+-define(TRAP_UDP, 5000).
+-define(AGENT_UDP, 4000).
+-define(CONF_FILE_VER, [v2]).
+-define(SYS_NAME, "Test os_mon_mibs").
+-define(MAX_MSG_SIZE, 484).
+-define(ENGINE_ID, "mgrEngine").
+-define(MGR_PORT, 5001).
+
+%%---------------------------------------------------------------------
+-ifdef(STANDALONE).
+-export([run/0]).
+run() ->
+ catch init_per_suite([]),
+ Ret = (catch update_load_table([])),
+ catch end_per_suite([]),
+ Ret.
+-else.
+
+init_per_testcase(_Case, Config) when is_list(Config) ->
+ Dog = test_server:timetrap(test_server:minutes(6)),
+ [{watchdog, Dog}|Config].
+
+end_per_testcase(_Case, Config) when is_list(Config) ->
+ Dog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ Config.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [load_unload, get_mem_sys_mark, get_mem_proc_mark,
+ get_disk_threshold, get_load_table,
+ {group, get_next_load_table}, get_disk_table,
+ {group, get_next_disk_table}, real_snmp_request,
+ update_load_table, {group, tickets}].
+
+groups() ->
+ [{tickets, [], [otp_6351, otp_7441]},
+ {get_next_load_table, [],
+ [sys_tot_mem, sys_used_mem, large_erl_process,
+ large_erl_process_mem, cpu_load, cpu_load5, cpu_load15,
+ os_wordsize, sys_tot_mem64, sys_used_mem64,
+ large_erl_process_mem64]},
+ {get_next_disk_table, [],
+ [disk_descr, disk_kbytes, disk_capacity]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+-endif.
+%%---------------------------------------------------------------------
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initiation before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ?line application:start(sasl),
+ ?line application:start(mnesia),
+ ?line application:start(os_mon),
+
+ %% Create initial configuration data for the snmp application
+ ?line PrivDir = ?config(priv_dir, Config),
+ ?line ConfDir = filename:join(PrivDir, "conf"),
+ ?line DbDir = filename:join(PrivDir,"db"),
+ ?line MgrDir = filename:join(PrivDir,"mgr"),
+
+ ?line file:make_dir(ConfDir),
+ ?line file:make_dir(DbDir),
+ ?line file:make_dir(MgrDir),
+
+ {ok, HostName} = inet:gethostname(),
+ {ok, Addr} = inet:getaddr(HostName, inet),
+
+ ?line snmp_config:write_agent_snmp_files(ConfDir, ?CONF_FILE_VER,
+ tuple_to_list(Addr), ?TRAP_UDP,
+ tuple_to_list(Addr),
+ ?AGENT_UDP, ?SYS_NAME),
+
+ ?line snmp_config:write_manager_snmp_files(MgrDir, tuple_to_list(Addr),
+ ?MGR_PORT, ?MAX_MSG_SIZE,
+ ?ENGINE_ID, [], [], []),
+
+ %% To make sure application:set_env is not overwritten by any
+ %% app-file settings.
+ ?line ok = application:load(snmp),
+
+ ?line application:set_env(snmp, agent, [{db_dir, DbDir},
+ {config, [{dir, ConfDir}]},
+ {agent_type, master},
+ {agent_verbosity, trace},
+ {net_if, [{verbosity, trace}]}]),
+ ?line application:set_env(snmp, manager, [{config, [{dir, MgrDir},
+ {db_dir, MgrDir},
+ {verbosity, trace}]},
+ {server, [{verbosity, trace}]},
+ {net_if, [{verbosity, trace}]},
+ {versions, [v1, v2, v3]}]),
+ application:start(snmp),
+
+ %% Load the mibs that should be tested
+ otp_mib:load(snmp_master_agent),
+ os_mon_mib:load(snmp_master_agent),
+
+ [{agent_ip, Addr}| Config].
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ ConfDir = filename:join(PrivDir,"conf"),
+ DbDir = filename:join(PrivDir,"db"),
+ MgrDir = filename:join(PrivDir, "mgr"),
+
+ %% Uload mibs
+ snmpa:unload_mibs(snmp_master_agent,["OTP-OS-MON-MIB"]),
+ otp_mib:unload(snmp_master_agent),
+
+ %% Clean up
+ application:stop(snmp),
+ application:stop(mnesia),
+ application:stop(os_mon),
+
+ del_dir(ConfDir),
+ del_dir(DbDir),
+ (catch del_dir(MgrDir)),
+ ok.
+
+%%---------------------------------------------------------------------
+%% Test cases
+%%---------------------------------------------------------------------
+load_unload(doc) ->
+ ["Test to unload and the reload the OTP.mib "];
+load_unload(suite) -> [];
+load_unload(Config) when list(Config) ->
+ ?line os_mon_mib:unload(snmp_master_agent),
+ ?line os_mon_mib:load(snmp_master_agent),
+ ok.
+%%---------------------------------------------------------------------
+
+update_load_table(doc) ->
+ ["check os_mon_mib:update_load_table error handling"];
+update_load_table(suite) ->
+ [];
+update_load_table(Config) when is_list(Config) ->
+ ?line Node = start_node(),
+ ?line ok = rpc:call(Node,application,start,[sasl]),
+ ?line ok = rpc:call(Node,application,start,[os_mon]),
+ ?line ok = os_mon_mib:update_load_table(),
+ ?line rpc:call(Node,application,stop,[os_mon]),
+ ?line ok = os_mon_mib:update_load_table(),
+ ?line stop_node(Node),
+ ok.
+
+otp_6351(doc) ->
+ ["like update_load_table, when memsup_system_only==true"];
+otp_6351(suite) ->
+ [];
+otp_6351(Config) when is_list(Config) ->
+ ?line Node = start_node(),
+ ?line ok = rpc:call(Node,application,start,[sasl]),
+ ?line ok = rpc:call(Node,application,load,[os_mon]),
+ ?line ok = rpc:call(Node,application,set_env,
+ [os_mon,memsup_system_only,true]),
+ ?line ok = rpc:call(Node,application,start,[os_mon]),
+ ?line Res = rpc:call(Node,os_mon_mib,get_load,[Node]),
+ if
+ is_tuple(Res), element(1, Res)==loadTable ->
+ ?line ok;
+ true ->
+ ?line ?t:fail(Res)
+ end,
+ ?line rpc:call(Node,application,stop,[os_mon]),
+ ?line stop_node(Node),
+ ok.
+
+
+
+
+%%---------------------------------------------------------------------
+get_mem_sys_mark(doc) ->
+ ["Simulates a get call to test the instrumentation function "
+ "for the loadMemorySystemWatermark variable."];
+get_mem_sys_mark(suite) ->
+ [];
+get_mem_sys_mark(Config) when is_list(Config) ->
+ case os_mon_mib:mem_sys_mark(get) of
+ {value, SysMark} when is_integer(SysMark) ->
+ ok;
+ _ ->
+ ?line test_server:fail(sys_mark_value_not_integer)
+ end.
+%%---------------------------------------------------------------------
+get_mem_proc_mark(doc) ->
+ ["Simulates a get call to test the instrumentation function "
+ "for the loadMemoryErlProcWatermark variable."];
+get_mem_proc_mark(suite) ->
+ [];
+get_mem_proc_mark(Config) when is_list(Config) ->
+ case os_mon_mib:mem_proc_mark(get) of
+ {value, ProcMark} when is_integer(ProcMark) ->
+ ok;
+ _ ->
+ ?line test_server:fail(proc_mark_value_not_integer)
+ end.
+%%---------------------------------------------------------------------
+get_disk_threshold(doc) ->
+ ["Simulates a get call to test the instrumentation function "
+ "for the diskAlmostFullThreshold variable."];
+get_disk_threshold(suite) ->
+ [];
+get_disk_threshold(Config) when is_list(Config) ->
+ case os_mon_mib:disk_threshold(get) of
+ {value, ProcMark} when is_integer(ProcMark) ->
+ ok;
+ _ ->
+ ?line test_server:fail(disk_threshold_value_not_integer)
+ end.
+%%---------------------------------------------------------------------
+
+%%% Note that when we have a string key, as in loadTable, the
+%%% instrumentation will deal with the [length(String), String]. We
+%%% have to know about this, when short cutting SNMP and calling
+%%% instrumentation functions directly as done in most test cases in
+%%% this test suite
+
+get_load_table(doc) ->
+ ["Simulates get calls to test the instrumentation function "
+ "for the loadTable"];
+get_load_table(suite) ->
+ [];
+get_load_table(Config) when is_list(Config) ->
+
+ NodeStr = atom_to_list(node()),
+ NodeLen = length(NodeStr),
+
+ {_, _, {Pid, _}} = memsup:get_memory_data(),
+ PidStr = lists:flatten(io_lib:format("~w", [Pid])),
+ ?line [{value, NodeStr},{value, PidStr}] =
+ os_mon_mib:load_table(get, [NodeLen | NodeStr],
+ [?loadErlNodeName, ?loadLargestErlProcess]),
+
+ ?line Values = os_mon_mib:load_table(get, [NodeLen | NodeStr] ,
+ [?loadSystemTotalMemory,
+ ?loadSystemUsedMemory,
+ ?loadLargestErlProcessUsedMemory,
+ ?loadCpuLoad,
+ ?loadCpuLoad5,
+ ?loadCpuLoad15,
+ ?loadOsWordsize,
+ ?loadSystemTotalMemory64,
+ ?loadSystemUsedMemory64,
+ ?loadLargestErlProcessUsedMemory64]),
+
+ IsInt = fun({value, Val}) when is_integer(Val) ->
+ true;
+ (_) ->
+ false
+ end,
+
+ NewValues = lists:filter(IsInt, Values),
+
+ case length(NewValues) of
+ 10 ->
+ ok;
+ _ ->
+ ?line test_server:fail(value_not_integer)
+ end,
+
+ ?line [{noValue,noSuchInstance}, {noValue,noSuchInstance},
+ {noValue,noSuchInstance}, {noValue,noSuchInstance},
+ {noValue,noSuchInstance}, {noValue,noSuchInstance},
+ {noValue,noSuchInstance}, {noValue,noSuchInstance},
+ {noValue,noSuchInstance}, {noValue,noSuchInstance},
+ {noValue,noSuchInstance}, {noValue,noSuchInstance}] =
+ os_mon_mib:load_table(get, [3, 102, 111, 111],
+ [?loadErlNodeName,
+ ?loadSystemTotalMemory,
+ ?loadSystemUsedMemory,
+ ?loadLargestErlProcess,
+ ?loadLargestErlProcessUsedMemory,
+ ?loadCpuLoad,
+ ?loadCpuLoad5,
+ ?loadCpuLoad15,
+ ?loadOsWordsize,
+ ?loadSystemTotalMemory64,
+ ?loadSystemUsedMemory64,
+ ?loadLargestErlProcessUsedMemory64]),
+
+ ok.
+%%---------------------------------------------------------------------
+
+sys_tot_mem(doc) ->
+ [];
+sys_tot_mem(suite) ->
+ [];
+sys_tot_mem(Config) when is_list(Config) ->
+ ?line [{[?loadSystemTotalMemory, Len | NodeStr], Mem}] =
+ os_mon_mib:load_table(get_next, [], [?loadSystemTotalMemory]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Mem of
+ Mem when is_integer(Mem) ->
+ ok;
+ _ ->
+ ?line test_server:fail(sys_tot_mem_value_not_integer)
+ end.
+
+sys_used_mem(doc) ->
+ [];
+sys_used_mem(suite) -> [];
+sys_used_mem(Config) when is_list(Config) ->
+ ?line [{[?loadSystemUsedMemory, Len | NodeStr], Mem}] =
+ os_mon_mib:load_table(get_next,[], [?loadSystemUsedMemory]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Mem of
+ Mem when is_integer(Mem) ->
+ ok;
+ _ ->
+ ?line test_server:fail(sys_used_mem_value_not_integer)
+ end.
+
+large_erl_process(doc) ->
+ [];
+large_erl_process(suite) ->
+ [];
+large_erl_process(Config) when is_list(Config) ->
+ {_, _, {Pid, _}} = memsup:get_memory_data(),
+ PidStr = lists:flatten(io_lib:format("~w", [Pid])),
+ ?line [{[?loadLargestErlProcess, Len | NodeStr], PidStr}] =
+ os_mon_mib:load_table(get_next,[], [?loadLargestErlProcess]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+ ok.
+
+large_erl_process_mem(doc) ->
+ [];
+large_erl_process_mem(suite) ->
+ [];
+large_erl_process_mem(Config) when is_list(Config) ->
+
+ ?line [{[?loadLargestErlProcessUsedMemory, Len | NodeStr], Mem}] =
+ os_mon_mib:load_table(get_next,[],
+ [?loadLargestErlProcessUsedMemory]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Mem of
+ Mem when is_integer(Mem) ->
+ ok;
+ _ ->
+ ?line test_server:fail(erl_pid_mem_value_not_integer)
+ end.
+
+cpu_load(doc) ->
+ [];
+cpu_load(suite) ->
+ [];
+cpu_load(Config) when list(Config) ->
+ ?line [{[?loadCpuLoad, Len | NodeStr], Load}] =
+ os_mon_mib:load_table(get_next,[], [?loadCpuLoad]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Load of
+ Load when is_integer(Load) ->
+ ok;
+ _ ->
+ ?line test_server:fail(cpu_load_value_not_integer)
+ end.
+
+cpu_load5(doc) ->
+ [];
+cpu_load5(suite) ->
+ [];
+cpu_load5(Config) when is_list(Config) ->
+ ?line [{[?loadCpuLoad5, Len | NodeStr], Load}] =
+ os_mon_mib:load_table(get_next,[], [?loadCpuLoad5]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Load of
+ Load when is_integer(Load) ->
+ ok;
+ _ ->
+ ?line test_server:fail(cpu_load5_value_not_integer)
+ end.
+
+cpu_load15(doc) ->
+ [];
+cpu_load15(suite) ->
+ [];
+cpu_load15(Config) when is_list(Config) ->
+ ?line [{[?loadCpuLoad15, Len | NodeStr], Load}] =
+ os_mon_mib:load_table(get_next,[], [?loadCpuLoad15]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Load of
+ Load when is_integer(Load) ->
+ ok;
+ _ ->
+ ?line test_server:fail(cpu_load15_value_not_integer)
+ end.
+
+os_wordsize(doc) ->
+ [];
+os_wordsize(suite) ->
+ [];
+os_wordsize(Config) when is_list(Config) ->
+ ?line [{[?loadOsWordsize, Len | NodeStr], Wordsize}] =
+ os_mon_mib:load_table(get_next,[], [?loadOsWordsize]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Wordsize of
+ Wordsize when is_integer(Wordsize) ->
+ ok;
+ _ ->
+ ?line test_server:fail(os_wordsize_value_not_integer)
+ end.
+
+sys_tot_mem64(doc) ->
+ [];
+sys_tot_mem64(suite) ->
+ [];
+sys_tot_mem64(Config) when is_list(Config) ->
+ ?line [{[?loadSystemTotalMemory64, Len | NodeStr], Mem}] =
+ os_mon_mib:load_table(get_next, [], [?loadSystemTotalMemory64]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Mem of
+ Mem when is_integer(Mem) ->
+ ok;
+ _ ->
+ ?line test_server:fail(sys_tot_mem_value_not_integer)
+ end.
+
+sys_used_mem64(doc) ->
+ [];
+sys_used_mem64(suite) -> [];
+sys_used_mem64(Config) when is_list(Config) ->
+ ?line [{[?loadSystemUsedMemory64, Len | NodeStr], Mem}] =
+ os_mon_mib:load_table(get_next,[], [?loadSystemUsedMemory64]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Mem of
+ Mem when is_integer(Mem) ->
+ ok;
+ _ ->
+ ?line test_server:fail(sys_used_mem_value_not_integer)
+ end.
+
+large_erl_process_mem64(doc) ->
+ [];
+large_erl_process_mem64(suite) ->
+ [];
+large_erl_process_mem64(Config) when is_list(Config) ->
+
+ ?line [{[?loadLargestErlProcessUsedMemory64, Len | NodeStr], Mem}] =
+ os_mon_mib:load_table(get_next,[],
+ [?loadLargestErlProcessUsedMemory64]),
+ ?line Len = length(NodeStr),
+ ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]),
+
+ case Mem of
+ Mem when is_integer(Mem) ->
+ ok;
+ _ ->
+ ?line test_server:fail(erl_pid_mem_value_not_integer)
+ end.
+%%---------------------------------------------------------------------
+get_disk_table(doc) ->
+ ["Simulates get calls to test the instrumentation function "
+ "for the diskTable."];
+get_disk_table(suite) ->
+ [];
+get_disk_table(Config) when is_list(Config) ->
+
+ DiskData = disksup:get_disk_data(),
+ DiskDataLen = length(DiskData),
+
+ if
+ DiskDataLen > 0 ->
+ ?line [{value, Value}] =
+ os_mon_mib:disk_table(get, [1,1], [?diskDescr]),
+
+ case is_list(Value) of
+ true ->
+ ok;
+ false ->
+ ?line test_server:fail(value_not_a_string)
+ end,
+
+ ?line Values = os_mon_mib:disk_table(get, [1,1],
+ [?diskId,
+ ?diskKBytes,
+ ?diskCapacity]),
+
+ IsInt = fun({value, Val}) when is_integer(Val) ->
+ true;
+ (_) ->
+ false
+ end,
+
+ NewValues = lists:filter(IsInt, Values),
+
+ case length(NewValues) of
+ 3 ->
+ ok;
+ _ ->
+ ?line test_server:fail(value_not_integer)
+ end
+ end,
+
+ ?line [{noValue,noSuchInstance}, {noValue,noSuchInstance},
+ {noValue,noSuchInstance}, {noValue,noSuchInstance}] =
+ os_mon_mib:disk_table(get, [1, DiskDataLen + 1], [?diskId,
+ ?diskDescr,
+ ?diskKBytes,
+ ?diskCapacity]),
+
+ ok.
+
+%%---------------------------------------------------------------------
+
+disk_descr(doc) ->
+ [];
+disk_descr(suite) ->
+ [];
+disk_descr(Config) when is_list(Config) ->
+ ?line [{[?diskDescr, 1,1], Descr}] =
+ os_mon_mib:disk_table(get_next, [], [?diskDescr]),
+
+ case Descr of
+ Descr when is_list(Descr) ->
+ ok;
+ _ ->
+ ?line test_server:fail(disk_descr_value_not_a_string)
+ end.
+
+disk_kbytes(doc) ->
+ [];
+disk_kbytes(suite) -> [];
+disk_kbytes(Config) when is_list(Config) ->
+ ?line [{[?diskKBytes, 1,1], Kbytes}] =
+ os_mon_mib:disk_table(get_next,[], [?diskKBytes]),
+
+ case Kbytes of
+ Kbytes when is_integer(Kbytes) ->
+ ok;
+ _ ->
+ ?line test_server:fail(disk_kbytes_value_not_integer)
+ end.
+
+
+disk_capacity(doc) ->
+ [];
+disk_capacity(suite) -> [];
+disk_capacity(Config) when is_list(Config) ->
+ ?line [{[?diskCapacity, 1,1], Capacity}] =
+ os_mon_mib:disk_table(get_next,[], [?diskCapacity]),
+
+ case Capacity of
+ Capacity when is_integer(Capacity) ->
+ ok;
+ _ ->
+ ?line test_server:fail(disk_capacity_value_not_integer)
+ end.
+
+%%---------------------------------------------------------------------
+real_snmp_request(doc) ->
+ ["Starts an snmp manager and sends a real snmp-reques. i.e. "
+ "sends a udp message on the correct format."];
+real_snmp_request(suite) -> [];
+real_snmp_request(Config) when list(Config) ->
+ Agent_ip = ?config(agent_ip, Config),
+
+ ?line ok = snmpm:register_user(os_mon_mib_test, snmpm_user_default, []),
+ ?line ok = snmpm:register_agent(os_mon_mib_test, Agent_ip, ?AGENT_UDP),
+
+ NodStr = atom_to_list(node()),
+ Len = length(NodStr),
+ {_, _, {Pid, _}} = memsup:get_memory_data(),
+ PidStr = lists:flatten(io_lib:format("~w", [Pid])),
+ io:format("FOO: ~p~n", [PidStr]),
+ ?line ok = snmp_get(Agent_ip,
+ [?loadEntry ++
+ [?loadLargestErlProcess, Len | NodStr]],
+ PidStr),
+ ?line ok = snmp_get_next(Agent_ip,
+ [?loadEntry ++
+ [?loadSystemUsedMemory, Len | NodStr]],
+ ?loadEntry ++ [?loadSystemUsedMemory + 1, Len
+ | NodStr], PidStr),
+ ?line ok = snmp_set(Agent_ip, [?loadEntry ++
+ [?loadLargestErlProcess, Len | NodStr]],
+ s, "<0.101.0>"),
+ ok.
+
+otp_7441(doc) ->
+ ["Starts an snmp manager and requests total memory. Was previously
+ integer32 which was errornous on 64 bit machines."];
+otp_7441(suite) ->
+ [];
+otp_7441(Config) when is_list(Config) ->
+ Agent_ip = ?config(agent_ip, Config),
+
+
+ NodStr = atom_to_list(node()),
+ Len = length(NodStr),
+ Oids = [Oid|_] = [?loadEntry ++ [?loadSystemTotalMemory, Len | NodStr]],
+ ?line { ok, {noError,0,[#varbind{oid = Oid, variabletype = 'Unsigned32'}]}, _} =
+ snmpm:g(os_mon_mib_test, Agent_ip, ?AGENT_UDP, Oids),
+
+ ok.
+
+%%---------------------------------------------------------------------
+%% Internal functions
+%%---------------------------------------------------------------------
+-ifdef(STANDALONE).
+config(priv_dir,_) ->
+ "/tmp".
+
+start_node() ->
+ Host = hd(tl(string:tokens(atom_to_list(node()),"@"))),
+ {ok,Node} = slave:start(Host,testnisse),
+ net_adm:ping(testnisse),
+ Node.
+
+
+stop_node(Node) ->
+ rpc:call(Node,erlang,halt,[]).
+-else.
+start_node() ->
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line {ok,Node} = test_server:start_node(testnisse, slave,
+ [{args, " -pa " ++ Pa}]),
+ Node.
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
+-endif.
+
+del_dir(Dir) ->
+ io:format("Deleting: ~s~n",[Dir]),
+ {ok, Files} = file:list_dir(Dir),
+ FullPathFiles = lists:map(fun(File) -> filename:join(Dir, File) end,
+ Files),
+ lists:foreach({file, delete}, FullPathFiles),
+ file:del_dir(Dir).
+
+%%---------------------------------------------------------------------
+snmp_get(Agent_ip, Oids = [Oid |_], Result) ->
+ ?line {ok,{noError,0,[#varbind{oid = Oid,
+ variabletype = 'OCTET STRING',
+ value = Result}]}, _} =
+ snmpm:g(os_mon_mib_test, Agent_ip, ?AGENT_UDP, Oids),
+ ok.
+
+snmp_get_next(Agent_ip, Oids, NextOid, Result) ->
+ ?line {ok,{noError,0,[#varbind{oid = NextOid,
+ variabletype = 'OCTET STRING',
+ value = Result}]},_} =
+ snmpm:gn(os_mon_mib_test, Agent_ip, ?AGENT_UDP, Oids),
+ ok.
+
+snmp_set(Agent_ip, Oid, ValuType, Value) ->
+ ?line {ok, {notWritable, _, _}, _} =
+ snmpm:s(os_mon_mib_test,Agent_ip,?AGENT_UDP,[{Oid, ValuType, Value}]),
+ ok.
diff --git a/lib/os_mon/test/os_sup_SUITE.erl b/lib/os_mon/test/os_sup_SUITE.erl
new file mode 100644
index 0000000000..61005f5ca0
--- /dev/null
+++ b/lib/os_mon/test/os_sup_SUITE.erl
@@ -0,0 +1,200 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(os_sup_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([message/1]).
+-export([config/1, port/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+-define(TAG, test_tag).
+-define(MFA, {?MODULE, test_mfa, [?TAG]}).
+
+-export([test_mfa/2]).
+
+init_per_suite(Config) when is_list(Config) ->
+ spawn(fun() -> message_receptor() end),
+ ?line application:load(os_mon),
+ ?line ok = application:set_env(os_mon, start_os_sup, true),
+ ?line ok = application:set_env(os_mon, os_sup_mfa, ?MFA),
+ ?line ok = application:set_env(os_mon, os_sup_enable, false),
+ ?line ok = application:start(os_mon),
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ ?line application:stop(os_mon),
+ ?line ok = application:set_env(os_mon, start_os_sup, false),
+ MFA = {os_sup, error_report, [std_error]},
+ ?line ok = application:set_env(os_mon, os_sup_mfa, MFA),
+ ?line ok = application:set_env(os_mon, os_sup_enable, true),
+ ?line exit(whereis(message_receptor), done),
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{watchdog,Dog} | Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case test_server:os_type() of
+ {unix, sunos} -> [message, config, port];
+ {win32, _OSname} -> [message];
+ OS ->
+ Str = io_lib:format("os_sup not available for ~p",
+ [OS]),
+ {skip, lists:flatten(Str)}
+ end.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+message(suite) ->
+ [];
+message(doc) ->
+ ["Test OS message handling"];
+message(Config) when is_list(Config) ->
+
+ %% Fake an OS message
+ Data = "10H11386278426HSystem4HTest5HError5HTesto",
+ ?line os_sup_server ! {faked_port, {data, Data}},
+
+ %% Check with message_receptor that it has been received
+ ?t:sleep(?t:seconds(1)),
+ Msg =
+ case ?t:os_type() of
+ {unix, sunos} ->
+ {?TAG, Data};
+ {win32, _} ->
+ {?TAG,{{1138,627842,0},"System","Test","Error","Testo"}}
+ end,
+ ?line message_receptor ! {check, self(), Msg},
+ receive
+ {result, true} ->
+ ok;
+ {result, Rec} ->
+ ?t:fail({no_message, Rec})
+ end,
+
+ ok.
+
+config(suite) ->
+ [];
+config(doc) ->
+ ["Test configuration"];
+config(Config) when is_list(Config) ->
+
+ %% os_sup_enable==true and os_sup_own/os_sup_syslogconf cannot
+ %% be tested as test_server is not running is root
+
+ %% os_sup_mfa is already tested, sort of (in init_per_suite)
+
+ %% os_sup_errortag should be tested, however
+
+ ok.
+
+port(suite) ->
+ [];
+port(doc) ->
+ ["Test that os_sup handles a terminating port program"];
+port(Config) when is_list(Config) ->
+ ?line Str = os:cmd("ps -e | grep '[f]errule'"),
+ case io_lib:fread("~s", Str) of
+ {ok, [Pid], _Rest} ->
+
+ %% Monitor os_sup_server
+ ?line MonRef = erlang:monitor(process, os_sup_server),
+
+ %% Kill the port program
+ case os:cmd("kill -9 " ++ Pid) of
+ [] ->
+
+ %% os_sup_server should now terminate
+ receive
+ {'DOWN', MonRef, _, _, {port_died, _Reason}} ->
+ ok;
+ {'DOWN', MonRef, _, _, Reason} ->
+ ?line ?t:fail({unexpected_exit_reason, Reason})
+ after
+ 3000 ->
+ ?line ?t:fail(still_alive)
+ end,
+
+ %% Give os_mon_sup time to restart os_sup
+ ?t:sleep(?t:seconds(3)),
+ ?line true = is_pid(whereis(os_sup_server)),
+
+ ok;
+
+ Line ->
+ erlang:demonitor(MonRef),
+ {skip, {not_killed, Line}}
+ end;
+ _ ->
+ {skip, {os_pid_not_found}}
+ end.
+
+%%----------------------------------------------------------------------
+%% Auxiliary
+%%----------------------------------------------------------------------
+
+test_mfa(Message, Tag) ->
+ message_receptor ! {Tag, Message}.
+
+message_receptor() ->
+ register(message_receptor, self()),
+ message_receptor([]).
+
+message_receptor(Received) ->
+ receive
+ %% Check if a certain message has been received
+ {check, From, Msg} ->
+ case lists:member(Msg, Received) of
+ true ->
+ From ! {result, true},
+ message_receptor(lists:delete(Msg, Received));
+ false ->
+ From ! {result, Received},
+ message_receptor(Received)
+ end;
+
+ %% Save all other messages
+ Msg ->
+ message_receptor([Msg|Received])
+ end.
diff --git a/lib/parsetools/doc/src/notes.xml b/lib/parsetools/doc/src/notes.xml
index 8a6f2c2714..77b3a1a657 100644
--- a/lib/parsetools/doc/src/notes.xml
+++ b/lib/parsetools/doc/src/notes.xml
@@ -30,6 +30,58 @@
</header>
<p>This document describes the changes made to the Parsetools application.</p>
+<section><title>Parsetools 2.0.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> The formating of Yecc's error messages has been
+ improved. (Thanks to Joe Armstrong.) </p>
+ <p>
+ Own Id: OTP-8919</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Parsetools 2.0.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Running HiPE-compiled Yecc parsers no longer results
+ in a <c>function_clause</c> error.</p>
+ <p>
+ Own Id: OTP-8771</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Parsetools 2.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Yecc failed to report reduce/reduce conflicts where
+ one of the reductions involved the root symbol. This bug
+ has been fixed. (Thanks to Manolis Papadakis.)</p>
+ <p>
+ Own Id: OTP-8483</p>
+ </item>
+ <item>
+ <p>A bug introduced in Parsetools 1.4.4 (R12B-2) has been
+ fixed. (Thanks to Manolis Papadakis.)</p>
+ <p>
+ Own Id: OTP-8486</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Parsetools 2.0.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/parsetools/include/yeccpre.hrl b/lib/parsetools/include/yeccpre.hrl
index 33a103d95f..80a3afbdb6 100644
--- a/lib/parsetools/include/yeccpre.hrl
+++ b/lib/parsetools/include/yeccpre.hrl
@@ -26,8 +26,8 @@
parse(Tokens) ->
yeccpars0(Tokens, {no_func, no_line}, 0, [], []).
--spec parse_and_scan({function() | {atom(), atom()}, [_]} | {atom(), atom(), [_]}) ->
- yecc_ret().
+-spec parse_and_scan({function() | {atom(), atom()}, [_]}
+ | {atom(), atom(), [_]}) -> yecc_ret().
parse_and_scan({F, A}) -> % Fun or {M, F}
yeccpars0([], {{F, A}, no_line}, 0, [], []);
parse_and_scan({M, F, A}) ->
@@ -44,7 +44,7 @@ format_error(Message) ->
%% To be used in grammar files to throw an error message to the parser
%% toplevel. Doesn't have to be exported!
--compile({nowarn_unused_function,{return_error,2}}).
+-compile({nowarn_unused_function, return_error/2}).
-spec return_error(integer(), any()) -> no_return().
return_error(Line, Message) ->
throw({error, {Line, ?MODULE, Message}}).
@@ -57,10 +57,7 @@ yeccpars0(Tokens, Tzr, State, States, Vstack) ->
error: Error ->
Stacktrace = erlang:get_stacktrace(),
try yecc_error_type(Error, Stacktrace) of
- {syntax_error, Token} ->
- yeccerror(Token);
- {missing_in_goto_table=Tag, Symbol, State} ->
- Desc = {Symbol, State, Tag},
+ Desc ->
erlang:raise(error, {yecc_bug, ?CODE_VERSION, Desc},
Stacktrace)
catch _:_ -> erlang:raise(error, Error, Stacktrace)
@@ -70,13 +67,15 @@ yeccpars0(Tokens, Tzr, State, States, Vstack) ->
Error
end.
-yecc_error_type(function_clause, [{?MODULE,F,[State,_,_,_,Token,_,_]} | _]) ->
+yecc_error_type(function_clause, [{?MODULE,F,ArityOrArgs} | _]) ->
case atom_to_list(F) of
- "yeccpars2" ++ _ ->
- {syntax_error, Token};
"yeccgoto_" ++ SymbolL ->
{ok,[{atom,_,Symbol}],_} = erl_scan:string(SymbolL),
- {missing_in_goto_table, Symbol, State}
+ State = case ArityOrArgs of
+ [S,_,_,_,_,_,_] -> S;
+ _ -> state_is_unknown
+ end,
+ {Symbol, State, missing_in_goto_table}
end.
yeccpars1([Token | Tokens], Tzr, State, States, Vstack) ->
@@ -141,11 +140,13 @@ yecctoken_end_location(Token) ->
yecctoken_location(Token)
end.
+-compile({nowarn_unused_function, yeccerror/1}).
yeccerror(Token) ->
Text = yecctoken_to_string(Token),
Location = yecctoken_location(Token),
{error, {Location, ?MODULE, ["syntax error before: ", Text]}}.
+-compile({nowarn_unused_function, yecctoken_to_string/1}).
yecctoken_to_string(Token) ->
case catch erl_scan:token_info(Token, text) of
{text, Txt} -> Txt;
@@ -158,6 +159,7 @@ yecctoken_location(Token) ->
_ -> element(2, Token)
end.
+-compile({nowarn_unused_function, yecctoken2string/1}).
yecctoken2string({atom, _, A}) -> io_lib:write(A);
yecctoken2string({integer,_,N}) -> io_lib:write(N);
yecctoken2string({float,_,F}) -> io_lib:write(F);
@@ -165,7 +167,7 @@ yecctoken2string({char,_,C}) -> io_lib:write_char(C);
yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]);
yecctoken2string({string,_,S}) -> io_lib:write_unicode_string(S);
yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A);
-yecctoken2string({_Cat, _, Val}) -> io_lib:write(Val);
+yecctoken2string({_Cat, _, Val}) -> io_lib:format("~p",[Val]);
yecctoken2string({dot, _}) -> "'.'";
yecctoken2string({'$end', _}) ->
[];
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index fd494eaf06..d0b4b9efe7 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -35,7 +35,7 @@
-export([compile/3,file/1,file/2,format_error/1]).
-import(lists, [member/2,reverse/1,sort/1,delete/2,
- keysearch/3,keysort/2,keydelete/3,keyfind/3,
+ keysort/2,keydelete/3,keyfind/3,
map/2,foldl/3,foreach/2,flatmap/2]).
-import(string, [substr/2,substr/3,span/2]).
-import(ordsets, [is_element/2,add_element/2,union/2]).
@@ -182,18 +182,18 @@ options(Options0, [Key|Keys], L) when is_list(Options0) ->
false ->
Options0
end,
- V = case keysearch(Key, 1, Options) of
- {value, {Key, Filename0}} when Key =:= includefile;
- Key =:= scannerfile ->
+ V = case lists:keyfind(Key, 1, Options) of
+ {Key, Filename0} when Key =:= includefile;
+ Key =:= scannerfile ->
case is_filename(Filename0) of
no ->
badarg;
Filename ->
{ok,[{Key,Filename}]}
end;
- {value,{Key,Bool}} when Bool; not Bool ->
- {ok,[{Key, Bool}]};
- {value,{Key, _}} ->
+ {Key, Bool} = KB when is_boolean(Bool) ->
+ {ok, [KB]};
+ {Key, _} ->
badarg;
false ->
{ok,[{Key,default_option(Key)}]}
@@ -231,8 +231,7 @@ atom_option(verbose) -> {verbose,true};
atom_option(Key) -> Key.
is_filename(T) ->
- try filename:flatten(T) of
- Filename -> Filename
+ try filename:flatten(T)
catch error: _ -> no
end.
@@ -320,10 +319,10 @@ filenames(File, Opts, St0) ->
St1 = St0#leex{xfile=Xfile,
opts=Opts,
module=Module},
- {value,{includefile,Ifile0}} = keysearch(includefile, 1, Opts),
+ {includefile,Ifile0} = lists:keyfind(includefile, 1, Opts),
Ifile = inc_file_name(Ifile0),
%% Test for explicit scanner file.
- {value,{scannerfile,Ofile}} = keysearch(scannerfile, 1, Opts),
+ {scannerfile,Ofile} = lists:keyfind(scannerfile, 1, Opts),
if
Ofile =:= [] ->
St1#leex{efile=filename:join(Dir, Efile),
@@ -495,7 +494,7 @@ parse_rule(S, Line, Atoks, Ms, N, St) ->
end.
var_used(Name, Toks) ->
- case keyfind(Name, 3, Toks) of
+ case lists:keyfind(Name, 3, Toks) of
{var,_,Name} -> true; %It's the var we want
_ -> false
end.
@@ -629,7 +628,7 @@ re_seq(Cs0, Sn0, St) ->
{Rs,Sn1,Cs1} -> {{seq,Rs},Sn1,Cs1}
end.
-re_seq1([C|_]=Cs0, Sn0, St) when C /= $|, C /= $) ->
+re_seq1([C|_]=Cs0, Sn0, St) when C =/= $|, C =/= $) ->
{L,Sn1,Cs1} = re_repeat(Cs0, Sn0, St),
{Rs,Sn2,Cs2} = re_seq1(Cs1, Sn1, St),
{[L|Rs],Sn2,Cs2};
@@ -751,9 +750,9 @@ re_char_class("[:" ++ Cs0, Cc, #leex{posix=true}=St) ->
{Pcl,":]" ++ Cs1} -> re_char_class(Cs1, [{posix,Pcl}|Cc], St);
{_,Cs1} -> parse_error({posix_cc,string_between(Cs0, Cs1)})
end;
-re_char_class([C1|Cs0], Cc, St) when C1 /= $] ->
+re_char_class([C1|Cs0], Cc, St) when C1 =/= $] ->
case re_char(C1, Cs0) of
- {Cf,[$-,C2|Cs1]} when C2 /= $] ->
+ {Cf,[$-,C2|Cs1]} when C2 =/= $] ->
case re_char(C2, Cs1) of
{Cl,Cs2} when Cf < Cl ->
re_char_class(Cs2, [{range,Cf,Cl}|Cc], St);
@@ -998,7 +997,7 @@ pack_crs([{C1,C2},{C3,C4}|Crs]) when C2 >= C3, C2 < C4 ->
%% C1 C2
%% C3 C4
pack_crs([{C1,C4}|Crs]);
-pack_crs([{C1,C2},{C3,C4}|Crs]) when C2 + 1 == C3 ->
+pack_crs([{C1,C2},{C3,C4}|Crs]) when C2 + 1 =:= C3 ->
%% C1 C2
%% C3 C4
pack_crs([{C1,C4}|Crs]);
@@ -1055,7 +1054,7 @@ build_dfa(Set, Us, N, Ts, Ms, NFA) ->
%% List of all transition sets.
Crs0 = [Cr || S <- Set,
{Crs,_St} <- (element(S, NFA))#nfa_state.edges,
- Crs /= epsilon, % Not an epsilon transition
+ Crs =/= epsilon, % Not an epsilon transition
Cr <- Crs ],
Crs1 = lists:usort(Crs0), % Must remove duplicates!
%% Build list of disjoint test ranges.
@@ -1072,7 +1071,7 @@ disjoint_crs([{_C1,C2}=Cr1,{C3,_C4}=Cr2|Crs]) when C2 < C3 ->
%% C1 C2
%% C3 C4
[Cr1|disjoint_crs([Cr2|Crs])];
-disjoint_crs([{C1,C2},{C3,C4}|Crs]) when C1 == C3 ->
+disjoint_crs([{C1,C2},{C3,C4}|Crs]) when C1 =:= C3 ->
%% C1 C2
%% C3 C4
[{C1,C2}|disjoint_crs(add_element({C2+1,C4}, Crs))];
@@ -1080,7 +1079,7 @@ disjoint_crs([{C1,C2},{C3,C4}|Crs]) when C1 < C3, C2 >= C3, C2 < C4 ->
%% C1 C2
%% C3 C4
[{C1,C3-1}|disjoint_crs(union([{C3,C2},{C2+1,C4}], Crs))];
-disjoint_crs([{C1,C2},{C3,C4}|Crs]) when C1 < C3, C2 == C4 ->
+disjoint_crs([{C1,C2},{C3,C4}|Crs]) when C1 < C3, C2 =:= C4 ->
%% C1 C2
%% C3 C4
[{C1,C3-1}|disjoint_crs(add_element({C3,C4}, Crs))];
@@ -1093,7 +1092,7 @@ disjoint_crs([]) -> [].
build_dfa([Cr|Crs], Set, Us, N, Ts, Ms, NFA) ->
case eclosure(move(Set, Cr, NFA), NFA) of
- S when S /= [] ->
+ S when S =/= [] ->
case dfa_state_exist(S, Us, Ms) of
{yes,T} ->
build_dfa(Crs, Set, Us, N, store(Cr, T, Ts), Ms, NFA);
@@ -1110,11 +1109,11 @@ build_dfa([], _, Us, N, Ts, _, _) ->
%% dfa_state_exist(Set, Unmarked, Marked) -> {yes,State} | no.
dfa_state_exist(S, Us, Ms) ->
- case keysearch(S, #dfa_state.nfa, Us) of
- {value,#dfa_state{no=T}} -> {yes,T};
+ case lists:keyfind(S, #dfa_state.nfa, Us) of
+ #dfa_state{no=T} -> {yes,T};
false ->
- case keysearch(S, #dfa_state.nfa, Ms) of
- {value,#dfa_state{no=T}} -> {yes,T};
+ case lists:keyfind(S, #dfa_state.nfa, Ms) of
+ #dfa_state{no=T} -> {yes,T};
false -> no
end
end.
@@ -1129,7 +1128,7 @@ eclosure(Sts, NFA) -> eclosure(Sts, NFA, []).
eclosure([St|Sts], NFA, Ec) ->
#nfa_state{edges=Es} = element(St, NFA),
eclosure([ N || {epsilon,N} <- Es,
- not is_element(N, Ec) ] ++ Sts,
+ not is_element(N, Ec) ] ++ Sts,
NFA, add_element(St, Ec));
eclosure([], _, Ec) -> Ec.
@@ -1137,7 +1136,7 @@ move(Sts, Cr, NFA) ->
%% io:fwrite("move1: ~p\n", [{Sts,Cr}]),
[ St || N <- Sts,
{Crs,St} <- (element(N, NFA))#nfa_state.edges,
- Crs /= epsilon, % Not an epsilon transition
+ Crs =/= epsilon, % Not an epsilon transition
in_crs(Cr, Crs) ].
in_crs({C1,C2}, [{C3,C4}|_Crs]) when C1 >= C3, C2 =< C4 -> true;
@@ -1436,7 +1435,7 @@ pack_trans([{{$\n,Cl},S}|Trs], Pt) ->
pack_trans([{{Cf,Cl},S}|Trs], Pt) when Cf < $\n, Cl > $\n ->
pack_trans([{{Cf,$\n-1},S},{{$\n+1,Cl},S}|Trs], [{$\n,S}|Pt]);
%% Small ranges become singletons.
-pack_trans([{{Cf,Cl},S}|Trs], Pt) when Cl == Cf + 1 ->
+pack_trans([{{Cf,Cl},S}|Trs], Pt) when Cl =:= Cf + 1 ->
pack_trans(Trs, [{Cf,S},{Cl,S}|Pt]);
pack_trans([Tr|Trs], Pt) -> % The default uninteresting case
pack_trans(Trs, Pt ++ [Tr]);
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index c4a47d008f..4119e2631b 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -1875,8 +1875,12 @@ format_conflict({Symbol, N, Reduce, Confl}) ->
%% - "__Stack" has been substituted for "Stack";
%% - several states can share yeccpars2_S_cont(), which reduces code size;
%% - instead if calling lists:nthtail() matching code is emitted.
+%%
+%% "1.4", parsetools-2.0.4:
+%% - yeccerror() is called when a syntax error is found (as in version 1.1).
+%% - the include file yeccpre.hrl has been changed.
--define(CODE_VERSION, "1.3").
+-define(CODE_VERSION, "1.4").
-define(YECC_BUG(M, A),
iolist_to_binary([" erlang:error({yecc_bug,\"",?CODE_VERSION,"\",",
io_lib:fwrite(M, A), "}).\n\n"])).
@@ -2106,13 +2110,11 @@ output_call_to_includefile(NewState, St) ->
fwrite(St, <<" yeccpars1(S, ~w, Ss, Stack, T, Ts, Tzr)">>,
[NewState]).
-output_state_actions_fini(State, #yecc{includefile_version = {1,1}}=St0) ->
- %% Backward compatibility.
+output_state_actions_fini(State, St0) ->
+ %% Backward compatible.
St10 = delim(St0, false),
St = fwrite(St10, <<"yeccpars2_~w(_, _, _, _, T, _, _) ->\n">>, [State]),
- fwrite(St, <<" yeccerror(T).\n\n">>, []);
-output_state_actions_fini(_State, St) ->
- fwrite(St, <<".\n\n">>, []).
+ fwrite(St, <<" yeccerror(T).\n\n">>, []).
output_reduce(St0, State, Terminal0,
#reduce{rule_nmbr = RuleNmbr,
@@ -2416,7 +2418,7 @@ include1(Line, Inport, Outport, Nmbr_of_lines) ->
include1(io:get_line(Inport, ''), Inport, Outport, Nmbr_of_lines + Incr).
includefile_version([]) ->
- {1,2};
+ {1,4};
includefile_version(Includefile) ->
case epp:open(Includefile, []) of
{ok, Epp} ->
@@ -2432,7 +2434,7 @@ includefile_version(Includefile) ->
parse_file(Epp) ->
case epp:parse_erl_form(Epp) of
{ok, {function,_Line,yeccpars1,7,_Clauses}} ->
- {1,2};
+ {1,4};
{eof,_Line} ->
{1,1};
_Form ->
diff --git a/lib/parsetools/src/yeccparser.erl b/lib/parsetools/src/yeccparser.erl
index 415547b4ce..63127802ee 100644
--- a/lib/parsetools/src/yeccparser.erl
+++ b/lib/parsetools/src/yeccparser.erl
@@ -38,16 +38,16 @@ line_of(Token) ->
-type yecc_ret() :: {'error', _} | {'ok', _}.
--spec parse(_) -> yecc_ret().
+-spec parse(Tokens :: list()) -> yecc_ret().
parse(Tokens) ->
- yeccpars0(Tokens, false).
+ yeccpars0(Tokens, {no_func, no_line}, 0, [], []).
--spec parse_and_scan({function() | {atom(), atom()}, [_]} | {atom(), atom(), [_]}) ->
- yecc_ret().
+-spec parse_and_scan({function() | {atom(), atom()}, [_]}
+ | {atom(), atom(), [_]}) -> yecc_ret().
parse_and_scan({F, A}) -> % Fun or {M, F}
- yeccpars0([], {F, A});
+ yeccpars0([], {{F, A}, no_line}, 0, [], []);
parse_and_scan({M, F, A}) ->
- yeccpars0([], {{M, F}, A}).
+ yeccpars0([], {{{M, F}, A}, no_line}, 0, [], []).
-spec format_error(any()) -> [char() | list()].
format_error(Message) ->
@@ -60,54 +60,58 @@ format_error(Message) ->
%% To be used in grammar files to throw an error message to the parser
%% toplevel. Doesn't have to be exported!
--compile({nowarn_unused_function,{return_error,2}}).
+-compile({nowarn_unused_function, return_error/2}).
-spec return_error(integer(), any()) -> no_return().
return_error(Line, Message) ->
throw({error, {Line, ?MODULE, Message}}).
--define(CODE_VERSION, "1.3").
+-define(CODE_VERSION, "1.4").
-yeccpars0(Tokens, MFA) ->
- try yeccpars1(Tokens, MFA, 0, [], [])
+yeccpars0(Tokens, Tzr, State, States, Vstack) ->
+ try yeccpars1(Tokens, Tzr, State, States, Vstack)
catch
error: Error ->
Stacktrace = erlang:get_stacktrace(),
try yecc_error_type(Error, Stacktrace) of
- {syntax_error, Token} ->
- yeccerror(Token);
- {missing_in_goto_table=Tag, Symbol, State} ->
- Desc = {Symbol, State, Tag},
+ Desc ->
erlang:raise(error, {yecc_bug, ?CODE_VERSION, Desc},
- Stacktrace)
+ Stacktrace)
catch _:_ -> erlang:raise(error, Error, Stacktrace)
end;
- throw: {error, {_Line, ?MODULE, _M}} = Error ->
- Error % probably from return_error/2
+ %% Probably thrown from return_error/2:
+ throw: {error, {_Line, ?MODULE, _M}} = Error ->
+ Error
end.
-yecc_error_type(function_clause, [{?MODULE,F,[State,_,_,_,Token,_,_]} | _]) ->
+yecc_error_type(function_clause, [{?MODULE,F,ArityOrArgs} | _]) ->
case atom_to_list(F) of
- "yeccpars2" ++ _ ->
- {syntax_error, Token};
"yeccgoto_" ++ SymbolL ->
{ok,[{atom,_,Symbol}],_} = erl_scan:string(SymbolL),
- {missing_in_goto_table, Symbol, State}
+ State = case ArityOrArgs of
+ [S,_,_,_,_,_,_] -> S;
+ _ -> state_is_unknown
+ end,
+ {Symbol, State, missing_in_goto_table}
end.
-yeccpars1([Token | Tokens], Tokenizer, State, States, Vstack) ->
- yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens,
- Tokenizer);
-yeccpars1([], {F, A}, State, States, Vstack) ->
+yeccpars1([Token | Tokens], Tzr, State, States, Vstack) ->
+ yeccpars2(State, element(1, Token), States, Vstack, Token, Tokens, Tzr);
+yeccpars1([], {{F, A},_Line}, State, States, Vstack) ->
case apply(F, A) of
- {ok, Tokens, _Endline} ->
- yeccpars1(Tokens, {F, A}, State, States, Vstack);
- {eof, _Endline} ->
- yeccpars1([], false, State, States, Vstack);
+ {ok, Tokens, Endline} ->
+ yeccpars1(Tokens, {{F, A}, Endline}, State, States, Vstack);
+ {eof, Endline} ->
+ yeccpars1([], {no_func, Endline}, State, States, Vstack);
{error, Descriptor, _Endline} ->
{error, Descriptor}
end;
-yeccpars1([], false, State, States, Vstack) ->
- yeccpars2(State, '$end', States, Vstack, {'$end', 999999}, [], false).
+yeccpars1([], {no_func, no_line}, State, States, Vstack) ->
+ Line = 999999,
+ yeccpars2(State, '$end', States, Vstack, yecc_end(Line), [],
+ {no_func, Line});
+yeccpars1([], {no_func, Endline}, State, States, Vstack) ->
+ yeccpars2(State, '$end', States, Vstack, yecc_end(Endline), [],
+ {no_func, Endline}).
%% yeccpars1/7 is called from generated code.
%%
@@ -115,48 +119,73 @@ yeccpars1([], false, State, States, Vstack) ->
%% yeccpars1/7 can be found by parsing the file without following
%% include directives. yecc will otherwise assume that an old
%% yeccpre.hrl is included (one which defines yeccpars1/5).
-yeccpars1(State1, State, States, Vstack, Stack1, [Token | Tokens],
- Tokenizer) ->
+yeccpars1(State1, State, States, Vstack, Token0, [Token | Tokens], Tzr) ->
yeccpars2(State, element(1, Token), [State1 | States],
- [Stack1 | Vstack], Token, Tokens, Tokenizer);
-yeccpars1(State1, State, States, Vstack, Stack1, [], {F, A}) ->
- case apply(F, A) of
- {ok, Tokens, _Endline} ->
- yeccpars1(State1, State, States, Vstack, Stack1, Tokens, {F, A});
- {eof, _Endline} ->
- yeccpars1(State1, State, States, Vstack, Stack1, [], false);
- {error, Descriptor, _Endline} ->
- {error, Descriptor}
- end;
-yeccpars1(State1, State, States, Vstack, Stack1, [], false) ->
- yeccpars2(State, '$end', [State1 | States], [Stack1 | Vstack],
- {'$end', 999999}, [], false).
+ [Token0 | Vstack], Token, Tokens, Tzr);
+yeccpars1(State1, State, States, Vstack, Token0, [], {{_F,_A}, _Line}=Tzr) ->
+ yeccpars1([], Tzr, State, [State1 | States], [Token0 | Vstack]);
+yeccpars1(State1, State, States, Vstack, Token0, [], {no_func, no_line}) ->
+ Line = yecctoken_end_location(Token0),
+ yeccpars2(State, '$end', [State1 | States], [Token0 | Vstack],
+ yecc_end(Line), [], {no_func, Line});
+yeccpars1(State1, State, States, Vstack, Token0, [], {no_func, Line}) ->
+ yeccpars2(State, '$end', [State1 | States], [Token0 | Vstack],
+ yecc_end(Line), [], {no_func, Line}).
+
+%% For internal use only.
+yecc_end({Line,_Column}) ->
+ {'$end', Line};
+yecc_end(Line) ->
+ {'$end', Line}.
+
+yecctoken_end_location(Token) ->
+ try
+ {text, Str} = erl_scan:token_info(Token, text),
+ {line, Line} = erl_scan:token_info(Token, line),
+ Parts = re:split(Str, "\n"),
+ Dline = length(Parts) - 1,
+ Yline = Line + Dline,
+ case erl_scan:token_info(Token, column) of
+ {column, Column} ->
+ Col = byte_size(lists:last(Parts)),
+ {Yline, Col + if Dline =:= 0 -> Column; true -> 1 end};
+ undefined ->
+ Yline
+ end
+ catch _:_ ->
+ yecctoken_location(Token)
+ end.
-% For internal use only.
yeccerror(Token) ->
- Text = case catch erl_scan:token_info(Token, text) of
- {text, Txt} -> Txt;
- _ -> yecctoken2string(Token)
- end,
- Location = case catch erl_scan:token_info(Token, location) of
- {location, Loc} -> Loc;
- _ -> element(2, Token)
- end,
+ Text = yecctoken_to_string(Token),
+ Location = yecctoken_location(Token),
{error, {Location, ?MODULE, ["syntax error before: ", Text]}}.
+yecctoken_to_string(Token) ->
+ case catch erl_scan:token_info(Token, text) of
+ {text, Txt} -> Txt;
+ _ -> yecctoken2string(Token)
+ end.
+
+yecctoken_location(Token) ->
+ case catch erl_scan:token_info(Token, location) of
+ {location, Loc} -> Loc;
+ _ -> element(2, Token)
+ end.
+
yecctoken2string({atom, _, A}) -> io_lib:write(A);
yecctoken2string({integer,_,N}) -> io_lib:write(N);
yecctoken2string({float,_,F}) -> io_lib:write(F);
yecctoken2string({char,_,C}) -> io_lib:write_char(C);
yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]);
yecctoken2string({string,_,S}) -> io_lib:write_unicode_string(S);
-yecctoken2string({reserved_symbol, _, A}) -> io_lib:format("~w", [A]);
-yecctoken2string({_Cat, _, Val}) -> io_lib:format("~w", [Val]);
+yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A);
+yecctoken2string({_Cat, _, Val}) -> io_lib:write(Val);
yecctoken2string({dot, _}) -> "'.'";
yecctoken2string({'$end', _}) ->
[];
yecctoken2string({Other, _}) when is_atom(Other) ->
- io_lib:format("~w", [Other]);
+ io_lib:write(Other);
yecctoken2string(Other) ->
io_lib:write(Other).
@@ -164,7 +193,7 @@ yecctoken2string(Other) ->
--file("yeccparser.erl", 168).
+-file("yeccparser.erl", 196).
yeccpars2(0=S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_0(S, Cat, Ss, Stack, T, Ts, Tzr);
@@ -248,7 +277,9 @@ yeccpars2_0(S, integer, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_0(S, reserved_word, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 8, Ss, Stack, T, Ts, Tzr);
yeccpars2_0(S, var, Ss, Stack, T, Ts, Tzr) ->
- yeccpars1(S, 9, Ss, Stack, T, Ts, Tzr).
+ yeccpars1(S, 9, Ss, Stack, T, Ts, Tzr);
+yeccpars2_0(_, _, _, _, T, _, _) ->
+ yeccerror(T).
yeccpars2_1(S, atom, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 6, Ss, Stack, T, Ts, Tzr);
@@ -267,10 +298,14 @@ yeccpars2_2(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_grammar(hd(Ss), Cat, Ss, Stack, T, Ts, Tzr).
yeccpars2_3(S, '->', Ss, Stack, T, Ts, Tzr) ->
- yeccpars1(S, 10, Ss, Stack, T, Ts, Tzr).
+ yeccpars1(S, 10, Ss, Stack, T, Ts, Tzr);
+yeccpars2_3(_, _, _, _, T, _, _) ->
+ yeccerror(T).
-yeccpars2_4(_S, '$end', _Ss, Stack, _T, _Ts, _Tzr) ->
- {ok, hd(Stack)}.
+yeccpars2_4(_S, '$end', _Ss, Stack, _T, _Ts, _Tzr) ->
+ {ok, hd(Stack)};
+yeccpars2_4(_, _, _, _, T, _, _) ->
+ yeccerror(T).
yeccpars2_5(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_grammar(hd(Ss), Cat, Ss, Stack, T, Ts, Tzr).
@@ -317,7 +352,9 @@ yeccpars2_13(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_symbols(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr).
yeccpars2_14(S, dot, Ss, Stack, T, Ts, Tzr) ->
- yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr).
+ yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr);
+yeccpars2_14(_, _, _, _, T, _, _) ->
+ yeccerror(T).
yeccpars2_15(S, '->', Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 18, Ss, Stack, T, Ts, Tzr);
@@ -338,7 +375,9 @@ yeccpars2_15(S, reserved_word, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_15(S, string, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 26, Ss, Stack, T, Ts, Tzr);
yeccpars2_15(S, var, Ss, Stack, T, Ts, Tzr) ->
- yeccpars1(S, 27, Ss, Stack, T, Ts, Tzr).
+ yeccpars1(S, 27, Ss, Stack, T, Ts, Tzr);
+yeccpars2_15(_, _, _, _, T, _, _) ->
+ yeccerror(T).
yeccpars2_16(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
[_|Nss] = Ss,
@@ -414,10 +453,14 @@ yeccpars2_29(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_rule(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr).
yeccpars2_30(S, dot, Ss, Stack, T, Ts, Tzr) ->
- yeccpars1(S, 35, Ss, Stack, T, Ts, Tzr).
+ yeccpars1(S, 35, Ss, Stack, T, Ts, Tzr);
+yeccpars2_30(_, _, _, _, T, _, _) ->
+ yeccerror(T).
yeccpars2_31(S, dot, Ss, Stack, T, Ts, Tzr) ->
- yeccpars1(S, 34, Ss, Stack, T, Ts, Tzr).
+ yeccpars1(S, 34, Ss, Stack, T, Ts, Tzr);
+yeccpars2_31(_, _, _, _, T, _, _) ->
+ yeccerror(T).
yeccpars2_32(S, string, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 32, Ss, Stack, T, Ts, Tzr);
@@ -486,7 +529,7 @@ yeccgoto_tokens(15=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_tokens(17=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_28(_S, Cat, Ss, Stack, T, Ts, Tzr).
--compile({inline,{yeccpars2_6_,1}}).
+-compile({inline,yeccpars2_6_/1}).
-file("yeccgramm.yrl", 44).
yeccpars2_6_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -494,7 +537,7 @@ yeccpars2_6_(__Stack0) ->
symbol ( __1 )
end | __Stack].
--compile({inline,{yeccpars2_7_,1}}).
+-compile({inline,yeccpars2_7_/1}).
-file("yeccgramm.yrl", 45).
yeccpars2_7_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -502,7 +545,7 @@ yeccpars2_7_(__Stack0) ->
symbol ( __1 )
end | __Stack].
--compile({inline,{yeccpars2_8_,1}}).
+-compile({inline,yeccpars2_8_/1}).
-file("yeccgramm.yrl", 46).
yeccpars2_8_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -510,7 +553,7 @@ yeccpars2_8_(__Stack0) ->
symbol ( __1 )
end | __Stack].
--compile({inline,{yeccpars2_9_,1}}).
+-compile({inline,yeccpars2_9_/1}).
-file("yeccgramm.yrl", 43).
yeccpars2_9_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -518,14 +561,14 @@ yeccpars2_9_(__Stack0) ->
symbol ( __1 )
end | __Stack].
--compile({inline,{yeccpars2_11_,1}}).
+-compile({inline,yeccpars2_11_/1}).
-file("yeccgramm.yrl", 40).
yeccpars2_11_(__Stack0) ->
[begin
{ erlang_code , [ { atom , 0 , '$undefined' } ] }
end | __Stack0].
--compile({inline,{yeccpars2_12_,1}}).
+-compile({inline,yeccpars2_12_/1}).
-file("yeccgramm.yrl", 35).
yeccpars2_12_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -533,7 +576,7 @@ yeccpars2_12_(__Stack0) ->
[ __1 ]
end | __Stack].
--compile({inline,{yeccpars2_13_,1}}).
+-compile({inline,yeccpars2_13_/1}).
-file("yeccgramm.yrl", 36).
yeccpars2_13_(__Stack0) ->
[__2,__1 | __Stack] = __Stack0,
@@ -541,7 +584,7 @@ yeccpars2_13_(__Stack0) ->
[ __1 | __2 ]
end | __Stack].
--compile({inline,{yeccpars2_16_,1}}).
+-compile({inline,yeccpars2_16_/1}).
-file("yeccgramm.yrl", 39).
yeccpars2_16_(__Stack0) ->
[__2,__1 | __Stack] = __Stack0,
@@ -549,7 +592,7 @@ yeccpars2_16_(__Stack0) ->
{ erlang_code , __2 }
end | __Stack].
--compile({inline,{yeccpars2_17_,1}}).
+-compile({inline,yeccpars2_17_/1}).
-file("yeccgramm.yrl", 41).
yeccpars2_17_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -557,7 +600,7 @@ yeccpars2_17_(__Stack0) ->
[ __1 ]
end | __Stack].
--compile({inline,{yeccpars2_18_,1}}).
+-compile({inline,yeccpars2_18_/1}).
-file("yeccgramm.yrl", 55).
yeccpars2_18_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -565,7 +608,7 @@ yeccpars2_18_(__Stack0) ->
{ '->' , line_of ( __1 ) }
end | __Stack].
--compile({inline,{yeccpars2_19_,1}}).
+-compile({inline,yeccpars2_19_/1}).
-file("yeccgramm.yrl", 56).
yeccpars2_19_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -573,7 +616,7 @@ yeccpars2_19_(__Stack0) ->
{ ':' , line_of ( __1 ) }
end | __Stack].
--compile({inline,{yeccpars2_24_,1}}).
+-compile({inline,yeccpars2_24_/1}).
-file("yeccgramm.yrl", 53).
yeccpars2_24_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -581,7 +624,7 @@ yeccpars2_24_(__Stack0) ->
{ value_of ( __1 ) , line_of ( __1 ) }
end | __Stack].
--compile({inline,{yeccpars2_25_,1}}).
+-compile({inline,yeccpars2_25_/1}).
-file("yeccgramm.yrl", 54).
yeccpars2_25_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -589,7 +632,7 @@ yeccpars2_25_(__Stack0) ->
{ value_of ( __1 ) , line_of ( __1 ) }
end | __Stack].
--compile({inline,{yeccpars2_28_,1}}).
+-compile({inline,yeccpars2_28_/1}).
-file("yeccgramm.yrl", 42).
yeccpars2_28_(__Stack0) ->
[__2,__1 | __Stack] = __Stack0,
@@ -597,7 +640,7 @@ yeccpars2_28_(__Stack0) ->
[ __1 | __2 ]
end | __Stack].
--compile({inline,{yeccpars2_29_,1}}).
+-compile({inline,yeccpars2_29_/1}).
-file("yeccgramm.yrl", 33).
yeccpars2_29_(__Stack0) ->
[__5,__4,__3,__2,__1 | __Stack] = __Stack0,
@@ -605,7 +648,7 @@ yeccpars2_29_(__Stack0) ->
{ rule , [ __1 | __3 ] , __4 }
end | __Stack].
--compile({inline,{yeccpars2_32_,1}}).
+-compile({inline,yeccpars2_32_/1}).
-file("yeccgramm.yrl", 37).
yeccpars2_32_(__Stack0) ->
[__1 | __Stack] = __Stack0,
@@ -613,7 +656,7 @@ yeccpars2_32_(__Stack0) ->
[ __1 ]
end | __Stack].
--compile({inline,{yeccpars2_33_,1}}).
+-compile({inline,yeccpars2_33_/1}).
-file("yeccgramm.yrl", 38).
yeccpars2_33_(__Stack0) ->
[__2,__1 | __Stack] = __Stack0,
@@ -621,7 +664,7 @@ yeccpars2_33_(__Stack0) ->
[ __1 | __2 ]
end | __Stack].
--compile({inline,{yeccpars2_34_,1}}).
+-compile({inline,yeccpars2_34_/1}).
-file("yeccgramm.yrl", 32).
yeccpars2_34_(__Stack0) ->
[__3,__2,__1 | __Stack] = __Stack0,
@@ -629,7 +672,7 @@ yeccpars2_34_(__Stack0) ->
{ __1 , __2 }
end | __Stack].
--compile({inline,{yeccpars2_35_,1}}).
+-compile({inline,yeccpars2_35_/1}).
-file("yeccgramm.yrl", 31).
yeccpars2_35_(__Stack0) ->
[__3,__2,__1 | __Stack] = __Stack0,
diff --git a/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile
index 19354b87b2..624c4e6975 100644
--- a/lib/parsetools/test/Makefile
+++ b/lib/parsetools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+# Copyright Ericsson AB 2005-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -71,8 +71,8 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) parsetools.spec $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) parsetools.spec parsetools.cover $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 069f780b5e..23ad16f98d 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,17 +30,19 @@
-define(privdir, "leex_SUITE_priv").
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Config)).
-define(privdir, ?config(priv_dir, Config)).
-endif.
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
--export([checks/1,
- file/1, compile/1, syntax/1,
- examples/1,
- pt/1, man/1, ex/1, ex2/1, not_yet/1]).
+-export([
+ file/1, compile/1, syntax/1,
+
+ pt/1, man/1, ex/1, ex2/1, not_yet/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -49,15 +51,33 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) -> [checks, examples].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, checks}, {group, examples}].
+
+groups() ->
+ [{checks, [], [file, compile, syntax]},
+ {examples, [], [pt, man, ex, ex2, not_yet]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-checks(suite) ->
- [file, compile, syntax].
file(doc) ->
"Bad files and options.";
@@ -330,8 +350,6 @@ syntax(Config) when is_list(Config) ->
leex:file(Filename, Ret),
ok.
-examples(suite) ->
- [pt,man,ex,ex2,not_yet].
pt(doc) ->
"Pushing back characters.";
diff --git a/lib/parsetools/test/parsetools.cover b/lib/parsetools/test/parsetools.cover
new file mode 100644
index 0000000000..13f84e3ba6
--- /dev/null
+++ b/lib/parsetools/test/parsetools.cover
@@ -0,0 +1,2 @@
+{incl_app,parsetools,details}.
+
diff --git a/lib/parsetools/test/parsetools.spec b/lib/parsetools/test/parsetools.spec
index 5b34633378..870d57baf1 100644
--- a/lib/parsetools/test/parsetools.spec
+++ b/lib/parsetools/test/parsetools.spec
@@ -1 +1 @@
-{topcase, {dir, "../parsetools_test"}}.
+{suites,"../parsetools_test",all}.
diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl
index 61e2456323..1de87b3bff 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,24 +29,26 @@
-define(privdir, "yecc_SUITE_priv").
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Config)).
-define(privdir, ?config(priv_dir, Config)).
-endif.
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
-export([app_test/1,
- checks/1,
- file/1, syntax/1, compile/1, rules/1, expect/1,
- conflicts/1,
- examples/1,
- empty/1, prec/1, yeccpre/1, lalr/1, old_yecc/1,
- other_examples/1,
- bugs/1,
- otp_5369/1, otp_6362/1, otp_7945/1, otp_8483/1, otp_8486/1,
- improvements/1,
- otp_7292/1, otp_7969/1]).
+
+ file/1, syntax/1, compile/1, rules/1, expect/1,
+ conflicts/1,
+
+ empty/1, prec/1, yeccpre/1, lalr/1, old_yecc/1,
+ other_examples/1,
+
+ otp_5369/1, otp_6362/1, otp_7945/1, otp_8483/1, otp_8486/1,
+
+ otp_7292/1, otp_7969/1, otp_8919/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -55,12 +57,38 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) -> [app_test, checks, examples, bugs, improvements].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test, {group, checks}, {group, examples},
+ {group, bugs}, {group, improvements}].
+
+groups() ->
+ [{checks, [],
+ [file, syntax, compile, rules, expect, conflicts]},
+ {examples, [],
+ [empty, prec, yeccpre, lalr, old_yecc, other_examples]},
+ {bugs, [],
+ [otp_5369, otp_6362, otp_7945, otp_8483, otp_8486]},
+ {improvements, [], [otp_7292, otp_7969, otp_8919]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
app_test(doc) ->
["Tests the applications consistency."];
@@ -70,8 +98,6 @@ app_test(Config) when is_list(Config) ->
?line ok=?t:app_test(parsetools),
ok.
-checks(suite) ->
- [file, syntax, compile, rules, expect, conflicts].
file(doc) ->
"Bad files and options.";
@@ -298,8 +324,8 @@ syntax(Config) when is_list(Config) ->
{_,[{L1,_,{undefined_function,{yeccpars2_2_,1}}},
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[]} = compile:file(Parserfile1, [basic_validation,return]),
- ?line L1 = 24 + SzYeccPre,
- ?line L2 = 31 + SzYeccPre
+ ?line L1 = 28 + SzYeccPre,
+ ?line L2 = 35 + SzYeccPre
end(),
%% Bad macro in action. OTP-7224.
@@ -316,8 +342,8 @@ syntax(Config) when is_list(Config) ->
{_,[{L1,_,{undefined_function,{yeccpars2_2_,1}}},
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[]} = compile:file(Parserfile1, [basic_validation,return]),
- ?line L1 = 24 + SzYeccPre,
- ?line L2 = 31 + SzYeccPre
+ ?line L1 = 28 + SzYeccPre,
+ ?line L2 = 35 + SzYeccPre
end(),
%% Check line numbers. OTP-7224.
@@ -730,8 +756,6 @@ rules(Config) when is_list(Config) ->
?line run(Config, Ts),
ok.
-examples(suite) ->
- [empty, prec, yeccpre, lalr, old_yecc, other_examples].
expect(doc) ->
"Check of expect.";
@@ -1283,8 +1307,6 @@ other_examples(Config) when is_list(Config) ->
?line run(Config, Ts),
ok.
-bugs(suite) ->
- [otp_5369, otp_6362, otp_7945, otp_8483, otp_8486].
otp_5369(doc) ->
"OTP-5369. A bug in parse_and_scan reported on erlang questions.";
@@ -1540,9 +1562,6 @@ otp_8486(Config) when is_list(Config) ->
?line run(Config, Ts),
ok.
-improvements(suite) ->
- [otp_7292, otp_7969].
-
otp_7292(doc) ->
"OTP-7292. Header declarations for edoc.";
otp_7292(suite) -> [];
@@ -1584,8 +1603,8 @@ otp_7292(Config) when is_list(Config) ->
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[{_,[{16,_,{unused_function,{foo,0}}}]}]} =
compile:file(Parserfile1, [basic_validation, return]),
- ?line L1 = 34 + SzYeccPre,
- ?line L2 = 41 + SzYeccPre
+ ?line L1 = 38 + SzYeccPre,
+ ?line L2 = 45 + SzYeccPre
end(),
YeccPre = filename:join(Dir, "yeccpre.hrl"),
@@ -1602,8 +1621,8 @@ otp_7292(Config) when is_list(Config) ->
{L2,_,{bad_inline,{yeccpars2_2_,1}}}]}],
[{_,[{16,_,{unused_function,{foo,0}}}]}]} =
compile:file(Parserfile1, [basic_validation, return]),
- ?line L1 = 33 + SzYeccPre,
- ?line L2 = 40 + SzYeccPre
+ ?line L1 = 37 + SzYeccPre,
+ ?line L2 = 44 + SzYeccPre
end(),
file:delete(YeccPre),
@@ -1773,6 +1792,14 @@ otp_7969(Config) when is_list(Config) ->
?line {error,{{1,11},erl_parse,_}} = erl_parse:parse_and_scan({F6, []}),
ok.
+otp_8919(doc) ->
+ "OTP-8919. Improve formating of Yecc error messages.";
+otp_8919(suite) -> [];
+otp_8919(Config) when is_list(Config) ->
+ {error,{1,Mod,Mess}} = erl_parse:parse([{cat,1,"hello"}]),
+ "syntax error before: \"hello\"" = lists:flatten(Mod:format_error(Mess)),
+ ok.
+
yeccpre_size() ->
yeccpre_size(default_yeccpre()).
diff --git a/lib/parsetools/vsn.mk b/lib/parsetools/vsn.mk
index b1354e89d8..812bf21f03 100644
--- a/lib/parsetools/vsn.mk
+++ b/lib/parsetools/vsn.mk
@@ -1 +1 @@
-PARSETOOLS_VSN = 2.0.2
+PARSETOOLS_VSN = 2.0.5
diff --git a/lib/percept/doc/src/book.xml b/lib/percept/doc/src/book.xml
index acea01ab38..4de6bc4eb1 100644
--- a/lib/percept/doc/src/book.xml
+++ b/lib/percept/doc/src/book.xml
@@ -5,7 +5,7 @@
<header titlestyle="normal">
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/percept/doc/src/egd_ug.xmlsrc b/lib/percept/doc/src/egd_ug.xmlsrc
index 11f7ca6663..d9bece7e07 100644
--- a/lib/percept/doc/src/egd_ug.xmlsrc
+++ b/lib/percept/doc/src/egd_ug.xmlsrc
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/percept/doc/src/notes.xml b/lib/percept/doc/src/notes.xml
index c310a0e598..33bfa7baab 100644
--- a/lib/percept/doc/src/notes.xml
+++ b/lib/percept/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -32,6 +32,21 @@
</header>
<p>This document describes the changes made to the Percept application.</p>
+<section><title>Percept 0.8.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fixes a race condition found in percept_db start/1
+ function. (Thanks to Ahmed Omar) </p>
+ <p>
+ Own Id: OTP-9012</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Percept 0.8.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/percept/doc/src/part.xml b/lib/percept/doc/src/part.xml
index a501ae526f..8053b279d5 100644
--- a/lib/percept/doc/src/part.xml
+++ b/lib/percept/doc/src/part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/percept/doc/src/part_notes.xml b/lib/percept/doc/src/part_notes.xml
index 2580281240..4965e67640 100755
--- a/lib/percept/doc/src/part_notes.xml
+++ b/lib/percept/doc/src/part_notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/percept/doc/src/percept_ug.xmlsrc b/lib/percept/doc/src/percept_ug.xmlsrc
index 1164e26143..af2dfe101a 100644
--- a/lib/percept/doc/src/percept_ug.xmlsrc
+++ b/lib/percept/doc/src/percept_ug.xmlsrc
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/percept/doc/src/ref_man.xml b/lib/percept/doc/src/ref_man.xml
index b25f5b57a3..ac82d9378c 100644
--- a/lib/percept/doc/src/ref_man.xml
+++ b/lib/percept/doc/src/ref_man.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/percept/src/egd.erl b/lib/percept/src/egd.erl
index 4fb5b6c46a..1b26d96728 100644
--- a/lib/percept/src/egd.erl
+++ b/lib/percept/src/egd.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -42,6 +42,7 @@
%%==========================================================================
%% @type egd_image()
+%% @type font()
%% @type point() = {integer(), integer()}
%% @type color()
%% @type render_option() = {render_engine, opaque} | {render_engine, alpha}
diff --git a/lib/percept/src/percept.erl b/lib/percept/src/percept.erl
index f5e0f7e469..3a2d9f7601 100644
--- a/lib/percept/src/percept.erl
+++ b/lib/percept/src/percept.erl
@@ -185,10 +185,27 @@ stop_webserver() ->
undefined ->
{error, not_started};
Pid ->
- Pid ! {self(), get_port},
- receive Port -> ok end,
- Pid ! quit,
- stop_webserver(Port)
+ do_stop([], Pid)
+ end.
+
+do_stop([], Pid)->
+ Pid ! {self(), get_port},
+ Port = receive P -> P end,
+ do_stop(Port, Pid);
+do_stop(Port, [])->
+ case whereis(percept_httpd) of
+ undefined ->
+ {error, not_started};
+ Pid ->
+ do_stop(Port, Pid)
+ end;
+do_stop(Port, Pid)->
+ case find_service_pid_from_port(inets:services_info(), Port) of
+ undefined ->
+ {error, not_started};
+ Pid2 ->
+ Pid ! quit,
+ inets:stop(httpd, Pid2)
end.
%% @spec stop_webserver(integer()) -> ok | {error, not_started}
@@ -196,12 +213,7 @@ stop_webserver() ->
%% @hidden
stop_webserver(Port) ->
- case find_service_pid_from_port(inets:services_info(), Port) of
- undefined ->
- {error, not_started};
- Pid ->
- inets:stop(httpd, Pid)
- end.
+ do_stop(Port,[]).
%%==========================================================================
%%
diff --git a/lib/percept/src/percept_db.erl b/lib/percept/src/percept_db.erl
index edb0d79a29..52e9afb78f 100644
--- a/lib/percept/src/percept_db.erl
+++ b/lib/percept/src/percept_db.erl
@@ -33,7 +33,7 @@
]).
-include("percept.hrl").
-
+-define(STOP_TIMEOUT, 1000).
%%==========================================================================
%%
%% Type definitions
@@ -77,17 +77,32 @@
start() ->
case erlang:whereis(percept_db) of
undefined ->
- Pid = spawn( fun() -> init_percept_db() end),
- erlang:register(percept_db, Pid),
- {started, Pid};
+ {started, do_start()};
PerceptDB ->
- erlang:unregister(percept_db),
- PerceptDB ! {action, stop},
- Pid = spawn( fun() -> init_percept_db() end),
- erlang:register(percept_db, Pid),
- {restarted, Pid}
+ {restarted, restart(PerceptDB)}
end.
+%% @spec restart(pid()) -> pid()
+%% @private
+%% @doc restarts the percept database.
+
+-spec restart(pid())-> pid().
+
+restart(PerceptDB)->
+ stop_sync(PerceptDB),
+ do_start().
+
+%% @spec do_start(pid()) -> pid()
+%% @private
+%% @doc starts the percept database.
+
+-spec do_start()-> pid().
+
+do_start()->
+ Pid = spawn( fun() -> init_percept_db() end),
+ erlang:register(percept_db, Pid),
+ Pid.
+
%% @spec stop() -> not_started | {stopped, Pid}
%% Pid = pid()
%% @doc Stops the percept database.
@@ -103,6 +118,22 @@ stop() ->
{stopped, Pid}
end.
+%% @spec stop_sync(pid()) -> true
+%% @private
+%% @doc Stops the percept database, with a synchronous call.
+
+-spec stop_sync(pid())-> true.
+
+stop_sync(Pid)->
+ MonitorRef = erlang:monitor(process, Pid),
+ stop(),
+ receive
+ {'DOWN', MonitorRef, _Type, Pid, _Info}->
+ true
+ after ?STOP_TIMEOUT->
+ exit(Pid, kill)
+ end.
+
%% @spec insert(tuple()) -> ok
%% @doc Inserts a trace or profile message to the database.
diff --git a/lib/percept/test/Makefile b/lib/percept/test/Makefile
index 0984b02c81..d927386d1c 100644
--- a/lib/percept/test/Makefile
+++ b/lib/percept/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+# Copyright Ericsson AB 2007-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -82,8 +82,8 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) percept.spec $(EMAKEFILE) $(SOURCE) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) percept.spec percept.cover $(EMAKEFILE) $(SOURCE) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/percept/test/egd_SUITE.erl b/lib/percept/test/egd_SUITE.erl
index fde02b47d5..51f090b39c 100644
--- a/lib/percept/test/egd_SUITE.erl
+++ b/lib/percept/test/egd_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,10 +18,10 @@
%%
-module(egd_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -54,16 +54,22 @@ end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
- % Test cases
- [
- image_create_and_destroy,
- image_shape,
- image_primitives,
- image_colors,
- image_font,
- image_png_compliant
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [image_create_and_destroy, image_shape,
+ image_primitives, image_colors, image_font,
+ image_png_compliant].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%----------------------------------------------------------------------
%% Tests
diff --git a/lib/percept/test/percept.cover b/lib/percept/test/percept.cover
new file mode 100644
index 0000000000..8a5ad0a55e
--- /dev/null
+++ b/lib/percept/test/percept.cover
@@ -0,0 +1,2 @@
+{incl_app,percept,details}.
+
diff --git a/lib/percept/test/percept.spec b/lib/percept/test/percept.spec
index 75aacc1fd6..f3ef76bd60 100644
--- a/lib/percept/test/percept.spec
+++ b/lib/percept/test/percept.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../percept_test"}}.
-
+{suites,"../percept_test",all}.
diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl
index ff7cccdaa8..e415d92a04 100644
--- a/lib/percept/test/percept_SUITE.erl
+++ b/lib/percept/test/percept_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,10 +18,10 @@
%%
-module(percept_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -51,12 +51,20 @@ end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
- % Test cases
- [ webserver,
- profile,
- analyze,
- analyze_dist].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [webserver, profile, analyze, analyze_dist].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%----------------------------------------------------------------------
%% Tests
@@ -70,6 +78,10 @@ webserver(Config) when is_list(Config) ->
% Explicit start inets?
?line {started, _, Port} = percept:start_webserver(),
?line ok = percept:stop_webserver(Port),
+ ?line {started, _, _} = percept:start_webserver(),
+ ?line ok = percept:stop_webserver(),
+ ?line {started, _, NewPort} = percept:start_webserver(),
+ ?line ok = percept:stop_webserver(NewPort),
?line application:stop(inets),
ok.
diff --git a/lib/percept/test/percept_db_SUITE.erl b/lib/percept/test/percept_db_SUITE.erl
new file mode 100644
index 0000000000..79be9714ba
--- /dev/null
+++ b/lib/percept/test/percept_db_SUITE.erl
@@ -0,0 +1,76 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(percept_db_SUITE).
+-include("test_server.hrl").
+
+%% Test server specific exports
+-export([all/1]).
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([
+ start/1
+ ]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(2)).
+-define(restarts, 10).
+-define(alive_timeout, 500).
+
+init_per_suite(Config) when is_list(Config) ->
+ Config.
+
+end_per_suite(Config) when is_list(Config) ->
+ Config.
+
+init_per_testcase(_Case, Config) ->
+ Dog = ?t:timetrap(?default_timeout),
+ [{max_size, 300}, {watchdog,Dog} | Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+all(suite) ->
+ % Test cases
+ [start].
+
+%%----------------------------------------------------------------------
+%% Tests
+%%----------------------------------------------------------------------
+
+start(suite) ->
+ [];
+start(doc) ->
+ ["Percept_db start and restart test."];
+start(Config) when is_list(Config) ->
+ ok = restart(?restarts),
+ {stopped, _DB} = percept_db:stop(),
+ ok.
+
+restart(0)->
+ ok;
+restart(N)->
+ {_, DB} = percept_db:start(),
+ timer:sleep(?alive_timeout),
+ true = erlang:is_process_alive(DB),
+ restart(N-1).
diff --git a/lib/percept/vsn.mk b/lib/percept/vsn.mk
index 443d25c78f..2a302991aa 100644
--- a/lib/percept/vsn.mk
+++ b/lib/percept/vsn.mk
@@ -1 +1 @@
-PERCEPT_VSN = 0.8.4
+PERCEPT_VSN = 0.8.5
diff --git a/lib/pman/doc/src/pman.xml b/lib/pman/doc/src/pman.xml
index 2469d141e5..84d5a5772a 100644
--- a/lib/pman/doc/src/pman.xml
+++ b/lib/pman/doc/src/pman.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1
index 2bcacc0990..ad704191a9 100644
--- a/lib/public_key/asn1/OTP-PKIX.asn1
+++ b/lib/public_key/asn1/OTP-PKIX.asn1
@@ -302,18 +302,25 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
-- DSA Keys and Signatures
+
+ DSAParams ::= CHOICE
+ {
+ params Dss-Parms,
+ null NULL
+ }
+
-- SubjectPublicKeyInfo:
dsa PUBLIC-KEY-ALGORITHM-CLASS ::= {
ID id-dsa
- TYPE Dss-Parms -- XXX Must be OPTIONAL
+ TYPE DSAParams -- XXX Must be OPTIONAL
PUBLIC-KEY-TYPE DSAPublicKey }
-- Certificate.signatureAlgorithm
dsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= {
- ID id-dsa-with-sha1
- TYPE NULL } -- XXX Must be empty and not NULL
+ ID id-dsa-with-sha1
+ TYPE DSAParams }
--
-- RSA Keys and Signatures
diff --git a/lib/public_key/doc/src/book.xml b/lib/public_key/doc/src/book.xml
index d3b8c7a2c7..f8d1205e57 100644
--- a/lib/public_key/doc/src/book.xml
+++ b/lib/public_key/doc/src/book.xml
@@ -5,7 +5,7 @@
<header titlestyle="normal">
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/cert_records.xml b/lib/public_key/doc/src/cert_records.xml
index 8cfe57f670..ad4f5812cb 100644
--- a/lib/public_key/doc/src/cert_records.xml
+++ b/lib/public_key/doc/src/cert_records.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,7 +37,7 @@
<p>This chapter briefly describes erlang records derived from asn1
specifications used to handle X509 certificates. The intent is to
describe the data types and not to specify the meaning of each
- component for this we refer you to RFC 3280.
+ component for this we refer you to RFC 5280.
</p>
<p>Use the following include directive to get access to the
@@ -45,11 +45,7 @@
<code> -include_lib("public_key/include/public_key.hrl"). </code>
- <p>The used specification is available in <c>OTP-PKIX.asn1</c>,
- which is an amelioration of
- the <c>PKIX1Explicit88.asn1</c>, <c>PKIX1Implicit88.asn1</c>
- and <c>PKIX1Algorithms88.asn1</c> modules.
- You find all these modules in the <c>asn1</c> subdirectory
+ <p>The used asn1 specifications are available <c>asn1</c> subdirectory
of the application <c>public_key</c>.
</p>
@@ -62,6 +58,9 @@
marker="public_key">public key reference manual </seealso> or
follows here.</p>
+ <p><c>oid() - a tuple of integers
+ as generated by the asn1 compiler.</c></p>
+
<p><c>time() = uct_time() | general_time()</c></p>
<p><c>uct_time() = {utcTime, "YYMMDDHHMMSSZ"} </c></p>
@@ -119,9 +118,31 @@
algorithm, % oid()
parameters % asn1_der_encoded()
}.
+</code>
+
+<code>
+#'OTPCertificate'{
+ tbsCertificate, % #'OTPTBSCertificate'{}
+ signatureAlgorithm, % #'SignatureAlgorithm'
+ signature % {0, binary()} - asn1 compact bitstring
+ }.
+
+#'OTPTBSCertificate'{
+ version, % v1 | v2 | v3
+ serialNumber, % integer()
+ signature, % #'SignatureAlgorithm'
+ issuer, % {rdnSequence, [#AttributeTypeAndValue'{}]}
+ validity, % #'Validity'{}
+ subject, % {rdnSequence, [#AttributeTypeAndValue'{}]}
+ subjectPublicKeyInfo, % #'SubjectPublicKeyInfo'{}
+ issuerUniqueID, % binary() | asn1_novalue
+ subjectUniqueID, % binary() | asn1_novalue
+ extensions % [#'Extension'{}]
+ }.
+
#'SignatureAlgorithm'{
algorithm, % id_signature_algorithm()
- parameters % public_key_params()
+ parameters % asn1_novalue | #'Dss-Parms'{}
}.
</code>
diff --git a/lib/public_key/doc/src/introduction.xml b/lib/public_key/doc/src/introduction.xml
index 71488e435a..8cf11ee10e 100644
--- a/lib/public_key/doc/src/introduction.xml
+++ b/lib/public_key/doc/src/introduction.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 33a424f432..14b43041ce 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2010</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -33,6 +33,159 @@
<rev>A</rev>
<file>notes.xml</file>
</header>
+
+<section><title>Public_Key 0.11</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Allows the public_key module to decode and encode RSA and
+ DSA keys encoded using the SubjectPublicKeyInfo format.
+ When pem_entry_encode is called on an RSA or DSA public
+ key type, the key is wrapped in the SubjectPublicKeyInfo
+ format.</p>
+ <p>
+ Own Id: OTP-9061</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved dialyzer specs.</p>
+ <p>
+ Own Id: OTP-8964</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.9</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Updated ssl to ignore CA certs that violate the asn1-spec
+ for a certificate, and updated public key asn1 spec to
+ handle inherited DSS-params.</p>
+ <p>
+ Own Id: OTP-7884</p>
+ </item>
+ <item>
+ <p>
+ Changed ssl implementation to retain backwards
+ compatibility for old option {verify, 0} that shall be
+ equivalent to {verify, verify_none}, also separate the
+ cases unknown ca and selfsigned peer cert, and restored
+ return value of deprecated function
+ public_key:pem_to_der/1.</p>
+ <p>
+ Own Id: OTP-8858</p>
+ </item>
+ <item>
+ <p>
+ Better handling of v1 and v2 certificates. V1 and v2
+ certificates does not have any extensions so then
+ validate_extensions should just accept that there are
+ none and not end up in missing_basic_constraints clause.</p>
+ <p>
+ Own Id: OTP-8867</p>
+ </item>
+ <item>
+ <p>
+ Changed the verify fun so that it differentiate between
+ the peer certificate and CA certificates by using
+ valid_peer or valid as the second argument to the verify
+ fun. It may not always be trivial or even possible to
+ know when the peer certificate is reached otherwise.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8873</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handling of unknown CA certificates was changed in ssl
+ and public_key to work as intended.</p>
+ <p>
+ Own Id: OTP-8788</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Revise the public_key API - Cleaned up and documented the
+ public_key API to make it useful for general use, also
+ changed ssl to use the new API.</p>
+ <p>
+ Own Id: OTP-8722</p>
+ </item>
+ <item>
+ <p>
+ Added the functionality so that the verification fun will
+ be called when a certificate is considered valid by the
+ path validation to allow access to each certificate in
+ the path to the user application. Also try to verify
+ subject-AltName, if unable to verify it let the
+ application verify it.</p>
+ <p>
+ Own Id: OTP-8825</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Public_Key 0.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Certificates without any extensions could not be handled
+ by public_key.</p>
+ <p>
+ Own Id: OTP-8626</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Code cleanup and minor bugfixes.</p>
+ <p>
+ Own Id: OTP-8649</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 0.6</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/public_key/doc/src/part.xml b/lib/public_key/doc/src/part.xml
index b85fa063ce..c338a71613 100644
--- a/lib/public_key/doc/src/part.xml
+++ b/lib/public_key/doc/src/part.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/part_notes.xml b/lib/public_key/doc/src/part_notes.xml
index 37ca516bc8..f855e76a6d 100644
--- a/lib/public_key/doc/src/part_notes.xml
+++ b/lib/public_key/doc/src/part_notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index dc9a96906f..c5f57214b1 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -1,11 +1,11 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -34,11 +34,7 @@
<modulesummary> API module for public key infrastructure.</modulesummary>
<description>
<p>This module provides functions to handle public key infrastructure
- from RFC 3280 - X.509 certificates (will later be upgraded to RFC 5280)
- and some parts of the PKCS-standard.
- Currently this application is mainly used by the new
- ssl implementation. The API is yet under construction
- and only a few of the functions are currently documented and thereby supported.
+ from RFC 5280 - X.509 certificates and some parts of the PKCS-standard.
</p>
</description>
@@ -60,44 +56,43 @@
<p><em>Data Types </em></p>
- <p><c>boolean() = true | false</c></p>
-
- <p><c>string = [bytes()]</c></p>
-
- <p><c>asn1_der_encoded() = binary() | [bytes()]</c></p>
+ <p><code>boolean() = true | false</code></p>
- <p><c>der_bin() = binary() </c></p>
+ <p><code>string = [bytes()]</code></p>
+
+ <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey'
+ 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</code></p>
- <p><c>oid() - a tuple of integers
- as generated by the asn1 compiler.</c></p>
-
- <p><c>public_key() = rsa_public_key() | dsa_public_key()</c></p>
-
- <p><c>rsa_public_key() = #'RSAPublicKey'{}</c></p>
+ <p><code>pem_entry () = {pki_asn1_type(), binary() %% DER or encrypted DER
+ not_encrypted | {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</code></p>
- <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p>
+ <p><code>rsa_public_key() = #'RSAPublicKey'{}</code></p>
+
+ <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p>
- <p><c>dsa_public_key() = integer() </c></p>
+ <p><code>dsa_public_key() = {integer(), #'Dss-Parms'{}} </code></p>
- <p><c>public_key_params() = dsa_key_params() </c></p>
+ <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p>
- <p><c>dsa_key_params() = #'Dss-Parms'{} </c></p>
+ <p><code>dsa_private_key() = #'DSAPrivateKey'{}</code></p>
- <p><c>private_key() = rsa_private_key() | dsa_private_key()</c></p>
+ <p><code> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </code></p>
- <p><c>rsa_private_key() = #'RSAPrivateKey'{} </c></p>
+ <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
+ | 'rsa_no_padding'</code></p>
- <p><c>dsa_private_key() = #'DSAPrivateKey'{}</c></p>
+ <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p>
- <p><c>x509_certificate() = "#Certificate{}"</c></p>
+ <p><code> dss_digest_type() = 'none' | 'sha' </code></p>
- <p><c>x509_tbs_certificate() = #'TBSCertificate'{} </c></p>
-
-<!-- <p><c>policy_tree() = [Root, Children]</c></p> -->
+ <p><code> ssh_file() = openssh_public_key | rfc4716_public_key |
+ known_hosts | auth_keys </code></p>
+
+<!-- <p><code>policy_tree() = [Root, Children]</code></p> -->
-<!-- <p><c>Root = #policy_tree_node{}</c></p> -->
+<!-- <p><code>Root = #policy_tree_node{}</code></p> -->
-<!-- <p><c>Children = [] | policy_tree()</c></p> -->
+<!-- <p><code>Children = [] | policy_tree()</code></p> -->
<!-- <p> The policy_tree_node record has the following fields:</p> -->
@@ -121,197 +116,357 @@
<!-- that would satisfy this policy in the certificate x+1. </item> -->
<!-- </taglist> -->
</section>
-
-<funcs>
- <func>
- <name>decode_private_key(KeyInfo) -> </name>
- <name>decode_private_key(KeyInfo, Password) -> {ok, PrivateKey} | {error, Reason}</name>
- <fsummary> Decodes an asn1 der encoded private key.</fsummary>
- <type>
- <v> KeyInfo = {KeyType, der_bin(), ChipherInfo} </v>
- <d> As returned from pem_to_der/1 for private keys</d>
- <v> KeyType = rsa_private_key | dsa_private_key </v>
- <v> ChipherInfo = opaque() | no_encryption </v>
- <d> ChipherInfo may contain encryption parameters if the private key is password
- protected, these are opaque to the user just pass the value returned by pem_to_der/1
- to this function.</d>
- <v> Password = string() </v>
- <d>Must be specified if CipherInfo =/= no_encryption</d>
- <v> PrivateKey = private_key() </v>
- <v> Reason = term() </v>
- </type>
- <desc>
- <p>Decodes an asn1 der encoded private key.</p>
- </desc>
+
+<funcs>
+
+ <func>
+ <name>decrypt_private(CipherText, Key [, Options]) -> binary()</name>
+ <fsummary>Public key decryption.</fsummary>
+ <type>
+ <v>CipherText = binary()</v>
+ <v>Key = rsa_private_key()</v>
+ <v>Options = public_crypt_options()</v>
+ </type>
+ <desc>
+ <p>Public key decryption using the private key.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>decrypt_public(CipherText, Key [, Options]) - > binary()</name>
+ <fsummary></fsummary>
+ <type>
+ <v>CipherText = binary()</v>
+ <v>Key = rsa_public_key()</v>
+ <v>Options = public_crypt_options()</v>
+ </type>
+ <desc>
+ <p> Public key decryption using the public key.</p>
+ </desc>
</func>
-
+
<func>
- <name>pem_to_der(File) -> {ok, [Entry]}</name>
- <fsummary>Reads a PEM file and translates it into its asn1 der
- encoded parts.</fsummary>
+ <name>der_decode(Asn1type, Der) -> term()</name>
+ <fsummary> Decodes a public key asn1 der encoded entity.</fsummary>
<type>
- <v>File = path()</v>
- <v>Password = string()</v>
- <v>Entry = {entry_type(), der_bin(), CipherInfo}</v>
- <v> ChipherInfo = opaque() | no_encryption </v>
- <d> ChipherInfo may contain encryption parameters if the private key is password
- protected, these will be handled by the function decode_private_key/2. </d>
- <v>entry_type() = cert | cert_req | rsa_private_key | dsa_private_key |
- dh_params </v>
+ <v>Asn1Type = atom() -</v>
+ <d> Asn1 type present in the public_key applications
+ asn1 specifications.</d>
+ <v>Der = der_encoded()</v>
+ </type>
+ <desc>
+ <p> Decodes a public key asn1 der encoded entity.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>der_encode(Asn1Type, Entity) -> der_encoded()</name>
+ <fsummary> Encodes a public key entity with asn1 DER encoding.</fsummary>
+ <type>
+ <v>Asn1Type = atom()</v>
+ <d> Asn1 type present in the public_key applications
+ asn1 specifications.</d>
+ <v>Entity = term() - The erlang representation of <c> Asn1Type</c></v>
</type>
<desc>
- <p>Reads a PEM file and translates it into its asn1 der
- encoded parts.</p>
+ <p> Encodes a public key entity with asn1 DER encoding.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pem_decode(PemBin) -> [pem_entry()]</name>
+ <fsummary>Decode PEM binary data and return
+ entries as asn1 der encoded entities. </fsummary>
+ <type>
+ <v>PemBin = binary()</v>
+ <d>Example {ok, PemBin} = file:read_file("cert.pem").</d>
+ </type>
+ <desc>
+ <p>Decode PEM binary data and return
+ entries as asn1 der encoded entities.</p>
</desc>
</func>
- <func>
- <name>pkix_decode_cert(Cert, Type) -> {ok, DecodedCert} | {error, Reason}</name>
- <fsummary> Decodes an asn1 der encoded pkix certificate. </fsummary>
- <type>
- <v>Cert = asn1_der_encoded() </v>
- <v>Type = plain | otp</v>
- <v>DecodeCert = x509_certificate() </v>
- <d>When type is specified as otp the asn1 spec OTP-PKIX.asn1 is used to decode known
- extensions and enhance the signature field in
- #'Certificate'{} and '#TBSCertificate'{}. This is currently used by the new ssl
- implementation but not documented and supported for the public_key application.</d>
- <v>Reason = term() </v>
+ <func>
+ <name>pem_encode(PemEntries) -> binary()</name>
+ <fsummary>Creates a PEM binary</fsummary>
+ <type>
+ <v> PemEntries = [pem_entry()] </v>
+ </type>
+ <desc>
+ <p>Creates a PEM binary</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pem_entry_decode(PemEntry [, Password]) -> term()</name>
+ <fsummary>Decodes a pem entry.</fsummary>
+ <type>
+ <v> PemEntry = pem_entry() </v>
+ <v> Password = string() </v>
+ </type>
+ <desc>
+ <p>Decodes a pem entry. pem_decode/1 returns a list of pem
+ entries. Note that if the pem entry is of type
+ 'SubjectPublickeyInfo' it will be further decoded to an
+ rsa_public_key() or dsa_public_key().</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pem_entry_encode(Asn1Type, Entity [,{CipherInfo, Password}]) -> pem_entry()</name>
+ <fsummary> Creates a pem entry that can be fed to pem_encode/1.</fsummary>
+ <type>
+ <v>Asn1Type = pki_asn1_type()</v>
+ <v>Entity = term() - The Erlang representation of
+ <c>Asn1Type</c>. If <c>Asn1Type</c> is 'SubjectPublicKeyInfo'
+ then <c>Entity</c> must be either an rsa_public_key() or a
+ dsa_public_key() and this function will create the appropriate
+ 'SubjectPublicKeyInfo' entry.
+ </v>
+ <v>CipherInfo = {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}</v>
+ <v>Password = string()</v>
+ </type>
+ <desc>
+ <p> Creates a pem entry that can be feed to pem_encode/1.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>encrypt_private(PlainText, Key) -> binary()</name>
+ <fsummary> Public key encryption using the private key.</fsummary>
+ <type>
+ <v>PlainText = binary()</v>
+ <v>Key = rsa_private_key()</v>
+ </type>
+ <desc>
+ <p> Public key encryption using the private key.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>encrypt_public(PlainText, Key) -> binary()</name>
+ <fsummary> Public key encryption using the public key.</fsummary>
+ <type>
+ <v>PlainText = binary()</v>
+ <v>Key = rsa_public_key()</v>
+ </type>
+ <desc>
+ <p> Public key encryption using the public key.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}</name>
+ <fsummary> Decodes an asn1 der encoded pkix x509 certificate.</fsummary>
+ <type>
+ <v>Cert = der_encoded()</v>
+ </type>
+ <desc>
+ <p>Decodes an asn1 der encoded pkix certificate. The otp option
+ will use the customized asn1 specification OTP-PKIX.asn1 for
+ decoding and also recursively decode most of the standard
+ parts.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_encode(Asn1Type, Entity, otp | plain) -> der_encoded()</name>
+ <fsummary>Der encodes a pkix x509 certificate or part of such a
+ certificate.</fsummary>
+ <type>
+ <v>Asn1Type = atom()</v>
+ <d>The asn1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .</d>
+ </type>
+ <desc>
+ <p>Der encodes a pkix x509 certificate or part of such a
+ certificate. This function must be used for encoding certificates or parts of certificates
+ that are decoded/created on the otp format, whereas for the plain format this
+ function will directly call der_encode/2. </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_is_issuer(Cert, IssuerCert) -> boolean()</name>
+ <fsummary> Checks if <c>IssuerCert</c> issued <c>Cert</c> </fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ <v>IssuerCert = der_encode() | #'OTPCertificate'{}</v>
+ </type>
+ <desc>
+ <p> Checks if <c>IssuerCert</c> issued <c>Cert</c> </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_is_fixed_dh_cert(Cert) -> boolean()</name>
+ <fsummary> Checks if a Certificate is a fixed Diffie-Hellman Cert.</fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ </type>
+ <desc>
+ <p> Checks if a Certificate is a fixed Diffie-Hellman Cert.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_is_self_signed(Cert) -> boolean()</name>
+ <fsummary> Checks if a Certificate is self signed.</fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ </type>
+ <desc>
+ <p> Checks if a Certificate is self signed.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_issuer_id(Cert, IssuedBy) -> {ok, IssuerID} | {error, Reason}</name>
+ <fsummary> Returns the issuer id.</fsummary>
+ <type>
+ <v>Cert = der_encode() | #'OTPCertificate'{}</v>
+ <v>IssuedBy = self | other</v>
+ <v>IssuerID = {integer(), {rdnSequence, [#'AttributeTypeAndValue'{}]}}</v>
+ <d>The issuer id consists of the serial number and the issuers name.</d>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p> Returns the issuer id.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>pkix_normalize_name(Issuer) -> Normalized</name>
+ <fsummary>Normalizes a issuer name so that it can be easily
+ compared to another issuer name. </fsummary>
+ <type>
+ <v>Issuer = {rdnSequence,[#'AttributeTypeAndValue'{}]}</v>
+ <v>Normalized = {rdnSequence, [#'AttributeTypeAndValue'{}]}</v>
+ </type>
+ <desc>
+ <p>Normalizes a issuer name so that it can be easily
+ compared to another issuer name.</p>
+ </desc>
+ </func>
+
+ <!-- <func> -->
+ <!-- <name>pkix_path_validation()</name> -->
+ <!-- <fsummary> Performs a basic path validation according to RFC 5280.</fsummary> -->
+ <!-- <type> -->
+ <!-- <v></v> -->
+ <!-- </type> -->
+ <!-- <desc> -->
+ <!-- <p> Performs a basic path validation according to RFC 5280.</p> -->
+ <!-- </desc> -->
+ <!-- </func> -->
+
+
+ <func>
+ <name>pkix_sign(#'OTPTBSCertificate'{}, Key) -> der_encode()</name>
+ <fsummary>Signs certificate.</fsummary>
+ <type>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
</type>
- <desc>
- <p> Decodes an asn1 encoded pkix certificate.</p>
+ <desc>
+ <p>Signs a 'OTPTBSCertificate'. Returns the corresponding
+ der encoded certificate.</p>
</desc>
+ </func>
+
+ <func>
+ <name>pkix_verify(Cert, Key) -> boolean()</name>
+ <fsummary> Verify pkix x.509 certificate signature.</fsummary>
+ <type>
+ <v>Cert = der_encode()</v>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
+ </type>
+ <desc>
+ <p> Verify pkix x.509 certificate signature.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>sign(Msg, DigestType, Key) -> binary()</name>
+ <fsummary> Create digital signature.</fsummary>
+ <type>
+ <v>Msg = binary()</v>
+ <d>The msg is either the binary "plain text" data to be
+ signed or in the case that digest type is <c>none</c>
+ it is the hashed value of "plain text" i.e. the digest.</d>
+ <v>DigestType = rsa_digest_type() | dsa_digest_type()</v>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
+ </type>
+ <desc>
+ <p> Creates a digital signature.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ssh_decode(SshBin, Type) -> [{public_key(), Attributes::list()}]</name>
+ <fsummary>Decodes a ssh file-binary. </fsummary>
+ <type>
+ <v>SshBin = binary()</v>
+ <d>Example {ok, SshBin} = file:read_file("known_hosts").</d>
+ <v> Type = public_key | ssh_file()</v>
+ <d>If <c>Type</c> is <c>public_key</c> the binary may be either
+ a rfc4716 public key or a openssh public key.</d>
+ </type>
+ <desc>
+ <p> Decodes a ssh file-binary. In the case of know_hosts or
+ auth_keys the binary may include one or more lines of the
+ file. Returns a list of public keys and their attributes, possible
+ attribute values depends on the file type represented by the
+ binary.
+ </p>
+
+ <taglist>
+ <tag>rfc4716 attributes - see RFC 4716</tag>
+ <item>{headers, [{string(), utf8_string()}]}</item>
+ <tag>auth_key attributes - see man sshd </tag>
+ <item>{comment, string()}</item>
+ <item>{options, [string()]}</item>
+ <item>{bits, integer()} - In ssh version 1 files</item>
+ <tag>known_host attributes - see man sshd</tag>
+ <item>{hostnames, [string()]}</item>
+ <item>{comment, string()}</item>
+ <item>{bits, integer()} - In ssh version 1 files</item>
+ </taglist>
+
+ </desc>
+ </func>
+
+ <func>
+ <name>ssh_encode([{Key, Attributes}], Type) -> binary()</name>
+ <fsummary> Encodes a list of ssh file entries to a binary.</fsummary>
+ <type>
+ <v>Key = public_key()</v>
+ <v>Attributes = list()</v>
+ <v>Type = ssh_file()</v>
+ </type>
+ <desc>
+ <p>Encodes a list of ssh file entries (public keys and attributes) to a binary. Possible
+ attributes depends on the file type, see <seealso
+ marker="ssh_decode"> ssh_decode/2 </seealso></p>
+ </desc>
+ </func>
+
+ <func>
+ <name>verify(Msg, DigestType, Signature, Key) -> boolean()</name>
+ <fsummary>Verifies a digital signature.</fsummary>
+ <type>
+ <v>Msg = binary()</v>
+ <d>The msg is either the binary "plain text" data
+ or in the case that digest type is <c>none</c>
+ it is the hashed value of "plain text" i.e. the digest.</d>
+ <v>DigestType = rsa_digest_type() | dsa_digest_type()</v>
+ <v>Signature = binary()</v>
+ <v>Key = rsa_public_key() | dsa_public_key()</v>
+ </type>
+ <desc>
+ <p>Verifies a digital signature</p>
+ </desc>
</func>
-<!-- <func> -->
-<!-- <name> pkix_encode_cert(Cert) -> {ok, EncodedCert} | {error, Reason}</name> -->
-<!-- <fsummary>Encodes a certificate record using asn1. </fsummary> -->
-<!-- <type> -->
-<!-- <v>Cert = x509_certificate() </v> -->
-<!-- <v>EncodedCert = asn1_der_encoded() </v> -->
-<!-- <v>Reason = term() </v> -->
-<!-- </type> -->
-<!-- <desc> -->
-<!-- <p> Encodes a certificate record using asn1.</p> -->
-<!-- </desc> -->
-<!-- </func> -->
-
-<!-- <func> -->
-<!-- <name>pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, Result} | {error, Reason}</name> -->
-
-<!-- <fsummary>Performs a basic path validation according to RFC 3280</fsummary> -->
-<!-- <type> -->
-<!-- <v>TrustedCert = asn1_der_encoded()</v> -->
-<!-- <v>CertChain = [asn1_der_encoded()]</v> -->
-<!-- <v>Options = [{Option, Value}]</v> -->
-<!-- <v>Result = {{algorithm(), public_key(), -->
-<!-- public_key_params()}, policy_tree()}</v> -->
-<!-- </type> -->
-
-<!-- <desc> -->
-<!-- <p>Available options are: </p> -->
-<!-- <taglist> -->
-<!-- <tag>{validate_extension_fun, fun()}</tag> -->
-<!-- <item> A fun behaving according to the following outline: -->
-<!-- <code> -->
-<!-- [...] -->
-<!-- ValidateExtensionFun = fun(Extensions, UserState) -> -->
-<!-- validate_extensions(Extensions, UserState, []) -->
-<!-- end, -->
-<!-- [...] -->
-
-<!-- validate_extensions([], UserState, UnknowExtension) -> -->
-<!-- {UserState, UnknowExtension}; -->
-<!-- validate_extensions([#'Extension'{} = Ext | Rest], UserState, UnknowExtension) -> -->
-<!-- case valid_extension(Ext) of -->
-<!-- {true, NewUserState} -> -->
-<!-- validate_extensions(Rest, NewUserState, UnknowExtension); -->
-<!-- unknown -> -->
-<!-- validate_extensions(Rest, UserState, [Ext | UnknowExtension]); -->
-<!-- {false, Reason} -> -->
-<!-- throw(bad_cert, Reason) -->
-<!-- end. -->
-<!-- </code> -->
-
-<!-- </item> -->
-
-<!-- <tag>{policy_set, [oid()]}</tag> -->
-<!-- <item>A set of certificate policy -->
-<!-- identifiers naming the policies that are acceptable to the -->
-<!-- certificate user. If the user is not concerned about -->
-<!-- certificate policy there is no need -->
-<!-- to set this option. Defaults to the -->
-<!-- special value [?anyPolicy]. -->
-<!-- </item> -->
-
-<!-- <tag>{policy_mapping, boolean()}</tag> -->
-<!-- <item>Indicates if policy -->
-<!-- mapping, initially, is allowed in the certification path. -->
-<!-- Defaults to false. -->
-<!-- </item> -->
-
-<!-- <tag> {explicit_policy, boolean()}</tag> -->
-<!-- <item>Indicates if the path, initially, must be -->
-<!-- valid for at least one of the certificate policies in the user -->
-<!-- specified policy set. -->
-<!-- Defaults to false. -->
-<!-- </item> -->
-
-<!-- <tag>{inhibit_any_policy, boolean()}</tag> -->
-<!-- <item>Indicates whether the anyPolicy OID, initially, should -->
-<!-- be processed if it is included in a certificate. -->
-<!-- Defaults to false. -->
-<!-- </item> -->
-
-<!-- </taglist> -->
-
-<!-- <p>Performs a basic path validation according to RFC 3280, -->
-<!-- e.i. signature validation, time validation, issuer validation, -->
-<!-- alternative subject name validation, CRL validation, policy -->
-<!-- validation and checks that no unknown extensions -->
-<!-- are marked as critical. The option <c>validate_extension_fun</c> -->
-<!-- may be used to validate application specific extensions. If -->
-<!-- a validation criteria is found to be invalid the validation process -->
-<!-- will immediately be stopped and this functions will return -->
-<!-- {error, Reason}. -->
-<!-- </p> -->
-<!-- </desc> -->
-<!-- </func> -->
-
-<!-- <func> -->
-<!-- <name>sign(DigestOrTBSCert, Key) -> </name> -->
-<!-- <name>sign(DigestOrTBSCert, Key, KeyParams) -> {ok, SignatureOrDerCert} | {error, Reason}</name> -->
-<!-- <fsummary>Signs Digest/Certificate using Key.</fsummary> -->
-<!-- <type> -->
-<!-- <v>DigestOrTBSCert = binary() | x509_tbs_certificate()</v> -->
-<!-- <v>Key = private_key()</v> -->
-<!-- <v>SignatureORDerCert = binary() | der_bin() </v> -->
-<!-- <v>Reason = term() </v> -->
-<!-- </type> -->
-<!-- <desc> -->
-<!-- <p> Signs Digest/Certificate using Key, in the later -->
-<!-- case a der encoded x509_certificate() will be returned. </p> -->
-<!-- </desc> -->
-<!-- </func> -->
-
-<!-- <func> -->
-<!-- <name>verify_signature(Digest, Signature, Key) -> </name> -->
-<!-- <name>verify_signature(DerCert, Key, KeyParams) -> </name> -->
-<!-- <name>verify_signature(Digest, Signature, Key, Params) -> Verified </name> -->
-<!-- <fsummary> Verifies the signature. </fsummary> -->
-<!-- <type> -->
-<!-- <v>Digest = binary() </v> -->
-<!-- <v>DerCert = der_bin() </v> -->
-<!-- <v>Signature = binary() </v> -->
-<!-- <v>Key = public_key() </v> -->
-<!-- <v>Params = key_params()</v> -->
-<!-- <v>Verified = boolean()</v> -->
-<!-- </type> -->
-<!-- <desc> -->
-<!-- <p> Verifies the signature Signature. If the key is an rsa-key no -->
-<!-- paramters are neeed.</p> -->
-<!-- </desc> -->
-<!-- </func> -->
</funcs>
</erlref>
diff --git a/lib/public_key/doc/src/public_key_records.xml b/lib/public_key/doc/src/public_key_records.xml
index 45b7106859..bb90290266 100644
--- a/lib/public_key/doc/src/public_key_records.xml
+++ b/lib/public_key/doc/src/public_key_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/doc/src/ref_man.xml b/lib/public_key/doc/src/ref_man.xml
index 0f11281d05..285cc36c6f 100644
--- a/lib/public_key/doc/src/ref_man.xml
+++ b/lib/public_key/doc/src/ref_man.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/public_key/include/public_key.hrl b/lib/public_key/include/public_key.hrl
index fbce10f0eb..5f97d80f7e 100644
--- a/lib/public_key/include/public_key.hrl
+++ b/lib/public_key/include/public_key.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,6 +28,17 @@
algorithm,
parameters = asn1_NOVALUE}).
+-define(DEFAULT_VERIFYFUN,
+ {fun(_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []}).
+
-record(path_validation_state, {
valid_policy_tree,
explicit_policy,
@@ -42,7 +53,7 @@
working_public_key_parameters,
working_issuer_name,
max_path_length,
- acc_errors, %% If verify_none option is set
+ verify_fun,
user_state
}).
@@ -59,4 +70,18 @@
interim_reasons_mask
}).
+-type public_key() :: rsa_public_key() | dsa_public_key().
+-type rsa_public_key() :: #'RSAPublicKey'{}.
+-type rsa_private_key() :: #'RSAPrivateKey'{}.
+-type dsa_private_key() :: #'DSAPrivateKey'{}.
+-type dsa_public_key() :: {integer(), #'Dss-Parms'{}}.
+-type pki_asn1_type() :: 'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey'
+ | 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter'
+ | 'SubjectPublicKeyInfo'.
+-type pem_entry() :: {pki_asn1_type(), binary(), %% DER or Encrypted DER
+ not_encrypted | {Cipher :: string(), Salt :: binary()}}.
+-type asn1_type() :: atom(). %% see "OTP-PUB-KEY.hrl
+-type ssh_file() :: openssh_public_key | rfc4716_public_key | known_hosts |
+ auth_keys.
+
-endif. % -ifdef(public_key).
diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile
index c30399f33a..5a24b02d2a 100644
--- a/lib/public_key/src/Makefile
+++ b/lib/public_key/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -41,9 +41,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN)
MODULES = \
public_key \
pubkey_pem \
+ pubkey_ssh \
pubkey_cert \
- pubkey_cert_records \
- pubkey_crypto
+ pubkey_cert_records
HRL_FILES = $(INCLUDE)/public_key.hrl
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index 799e3820d1..5ab9642279 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,14 +23,13 @@
-include("public_key.hrl").
--export([verify_signature/3,
- init_validation_state/3, prepare_for_next_cert/2,
+-export([init_validation_state/3, prepare_for_next_cert/2,
validate_time/3, validate_signature/6,
validate_issuer/4, validate_names/6,
validate_revoked_status/3, validate_extensions/4,
- validate_unknown_extensions/3,
- normalize_general_name/1, digest_type/1, digest/2, is_self_signed/1,
- is_issuer/2, issuer_id/2, is_fixed_dh_cert/1]).
+ normalize_general_name/1, digest_type/1, is_self_signed/1,
+ is_issuer/2, issuer_id/2, is_fixed_dh_cert/1,
+ verify_data/1, verify_fun/4]).
-define(NULL, 0).
@@ -38,10 +37,22 @@
%% Internal application API
%%====================================================================
-verify_signature(DerCert, Key, KeyParams) ->
- {ok, OtpCert} = pubkey_cert_records:decode_cert(DerCert, otp),
- verify_signature(OtpCert, DerCert, Key, KeyParams).
+%%--------------------------------------------------------------------
+-spec verify_data(DER::binary()) -> {md5 | sha, binary(), binary()}.
+%%
+%% Description: Extracts data from DerCert needed to call public_key:verify/4.
+%%--------------------------------------------------------------------
+verify_data(DerCert) ->
+ {ok, OtpCert} = pubkey_cert_records:decode_cert(DerCert),
+ extract_verify_data(OtpCert, DerCert).
+%%--------------------------------------------------------------------
+-spec init_validation_state(#'OTPCertificate'{}, integer(), list()) ->
+ #path_validation_state{}.
+%%
+%% Description: Creates inital version of path_validation_state for
+%% basic path validation of x509 certificates.
+%%--------------------------------------------------------------------
init_validation_state(#'OTPCertificate'{} = OtpCert, DefaultPathLen,
Options) ->
PolicyTree = #policy_tree_node{valid_policy = ?anyPolicy,
@@ -56,16 +67,23 @@ init_validation_state(#'OTPCertificate'{} = OtpCert, DefaultPathLen,
Options, false)),
PolicyMapping = policy_indicator(MaxLen,
proplists:get_value(policy_mapping, Options, false)),
- AccErrors = proplists:get_value(acc_errors, Options, []),
- State = #path_validation_state{max_path_length = MaxLen,
- valid_policy_tree = PolicyTree,
- explicit_policy = ExplicitPolicy,
- inhibit_any_policy = InhibitAnyPolicy,
- policy_mapping = PolicyMapping,
- acc_errors = AccErrors,
+ {VerifyFun, UserState} = proplists:get_value(verify_fun, Options, ?DEFAULT_VERIFYFUN),
+ State = #path_validation_state{max_path_length = MaxLen,
+ valid_policy_tree = PolicyTree,
+ explicit_policy = ExplicitPolicy,
+ inhibit_any_policy = InhibitAnyPolicy,
+ policy_mapping = PolicyMapping,
+ verify_fun = VerifyFun,
+ user_state = UserState,
cert_num = 0},
prepare_for_next_cert(OtpCert, State).
+%%--------------------------------------------------------------------
+-spec prepare_for_next_cert(#'OTPCertificate'{}, #path_validation_state{}) ->
+ #path_validation_state{}.
+%%
+%% Description: Update path_validation_state for next iteration.
+%%--------------------------------------------------------------------
prepare_for_next_cert(OtpCert, ValidationState = #path_validation_state{
working_public_key_algorithm = PrevAlgo,
working_public_key_parameters =
@@ -92,8 +110,14 @@ prepare_for_next_cert(OtpCert, ValidationState = #path_validation_state{
working_issuer_name = Issuer,
cert_num = ValidationState#path_validation_state.cert_num + 1
}.
-
-validate_time(OtpCert, AccErr, Verify) ->
+
+ %%--------------------------------------------------------------------
+-spec validate_time(#'OTPCertificate'{}, term(), fun()) -> term().
+%%
+%% Description: Check that the certificate validity period includes the
+%% current time.
+%%--------------------------------------------------------------------
+validate_time(OtpCert, UserState, VerifyFun) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
{'Validity', NotBeforeStr, NotAfterStr}
= TBSCert#'OTPTBSCertificate'.validity,
@@ -103,34 +127,52 @@ validate_time(OtpCert, AccErr, Verify) ->
case ((NotBefore =< Now) and (Now =< NotAfter)) of
true ->
- AccErr;
+ UserState;
false ->
- not_valid({bad_cert, cert_expired}, Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, cert_expired}, UserState, VerifyFun)
end.
-
-validate_issuer(OtpCert, Issuer, AccErr, Verify) ->
+%%--------------------------------------------------------------------
+-spec validate_issuer(#'OTPCertificate'{}, term(), term(), fun()) -> term().
+%%
+%% Description: Check that the certificate issuer name is the working_issuer_name
+%% in path_validation_state.
+%%--------------------------------------------------------------------
+validate_issuer(OtpCert, Issuer, UserState, VerifyFun) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
case is_issuer(Issuer, TBSCert#'OTPTBSCertificate'.issuer) of
true ->
- AccErr;
+ UserState;
_ ->
- not_valid({bad_cert, invalid_issuer}, Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, invalid_issuer}, UserState, VerifyFun)
end.
-
+%%--------------------------------------------------------------------
+-spec validate_signature(#'OTPCertificate'{}, DER::binary(),
+ term(),term(), term(), fun()) -> term().
+
+%%
+%% Description: Check that the signature on the certificate can be verified using
+%% working_public_key_algorithm, the working_public_key, and
+%% the working_public_key_parameters in path_validation_state.
+%%--------------------------------------------------------------------
validate_signature(OtpCert, DerCert, Key, KeyParams,
- AccErr, Verify) ->
+ UserState, VerifyFun) ->
case verify_signature(OtpCert, DerCert, Key, KeyParams) of
true ->
- AccErr;
+ UserState;
false ->
- not_valid({bad_cert, invalid_signature}, Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, invalid_signature}, UserState, VerifyFun)
end.
-
-validate_names(OtpCert, Permit, Exclude, Last, AccErr, Verify) ->
+%%--------------------------------------------------------------------
+-spec validate_names(#'OTPCertificate'{}, no_constraints | list(), list(),
+ term(), term(), fun())-> term().
+%%
+%% Description: Validate Subject Alternative Name.
+%%--------------------------------------------------------------------
+validate_names(OtpCert, Permit, Exclude, Last, UserState, VerifyFun) ->
case is_self_signed(OtpCert) andalso (not Last) of
true ->
- ok;
+ UserState;
false ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
Subject = TBSCert#'OTPTBSCertificate'.subject,
@@ -143,8 +185,10 @@ validate_names(OtpCert, Permit, Exclude, Last, AccErr, Verify) ->
Name = [{directoryName, Subject}|EmailAddress],
AltNames = case AltSubject of
- undefined -> [];
- _ -> AltSubject#'Extension'.extnValue
+ undefined ->
+ [];
+ _ ->
+ AltSubject#'Extension'.extnValue
end,
case (is_permitted(Name, Permit) andalso
@@ -152,65 +196,74 @@ validate_names(OtpCert, Permit, Exclude, Last, AccErr, Verify) ->
(not is_excluded(Name, Exclude)) andalso
(not is_excluded(AltNames, Exclude))) of
true ->
- AccErr;
+ UserState;
false ->
- not_valid({bad_cert, name_not_permitted},
- Verify, AccErr)
+ verify_fun(OtpCert, {bad_cert, name_not_permitted},
+ UserState, VerifyFun)
end
end.
-
-%% See rfc3280 4.1.2.6 Subject: regarding emails.
-extract_email({rdnSequence, List}) ->
- extract_email2(List).
-extract_email2([[#'AttributeTypeAndValue'{type=?'id-emailAddress',
- value=Mail}]|_]) ->
- [{rfc822Name, Mail}];
-extract_email2([_|Rest]) ->
- extract_email2(Rest);
-extract_email2([]) -> [].
-
-validate_revoked_status(_OtpCert, _Verify, AccErr) ->
- %% true |
+%%--------------------------------------------------------------------
+-spec validate_revoked_status(#'OTPCertificate'{}, term(), fun()) ->
+ term().
+%%
+%% Description: Check if certificate has been revoked.
+%%--------------------------------------------------------------------
+validate_revoked_status(_OtpCert, UserState, _VerifyFun) ->
+ %% TODO: Implement or leave for application?!
+ %% valid |
%% throw({bad_cert, cert_revoked})
- AccErr.
-
-validate_extensions(OtpCert, ValidationState, Verify, AccErr) ->
+ UserState.
+%%--------------------------------------------------------------------
+-spec validate_extensions(#'OTPCertificate'{}, #path_validation_state{},
+ term(), fun())->
+ {#path_validation_state{}, UserState :: term()}.
+%%
+%% Description: Check extensions included in basic path validation.
+%%--------------------------------------------------------------------
+validate_extensions(OtpCert, ValidationState, UserState, VerifyFun) ->
TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
- Extensions = TBSCert#'OTPTBSCertificate'.extensions,
- validate_extensions(Extensions, ValidationState, no_basic_constraint,
- is_self_signed(OtpCert), [], Verify, AccErr).
-
-validate_unknown_extensions([], AccErr, _Verify) ->
- AccErr;
-validate_unknown_extensions([#'Extension'{critical = true} | _],
- AccErr, Verify) ->
- not_valid({bad_cert, unknown_critical_extension}, Verify, AccErr);
-validate_unknown_extensions([#'Extension'{critical = false} | Rest],
- AccErr, Verify) ->
- validate_unknown_extensions(Rest, AccErr, Verify).
-
+ case TBSCert#'OTPTBSCertificate'.version of
+ N when N >= 3 ->
+ Extensions = TBSCert#'OTPTBSCertificate'.extensions,
+ validate_extensions(OtpCert, Extensions,
+ ValidationState, no_basic_constraint,
+ is_self_signed(OtpCert), UserState, VerifyFun);
+ _ -> %% Extensions not present in versions 1 & 2
+ {ValidationState, UserState}
+ end.
+%%--------------------------------------------------------------------
+-spec normalize_general_name({rdnSequence, term()}) -> {rdnSequence, term()}.
+%%
+%% Description: Normalizes a general name so that it can be easily
+%% compared to another genral name.
+%%--------------------------------------------------------------------
normalize_general_name({rdnSequence, Issuer}) ->
- NormIssuer = normalize_general_name(Issuer),
- {rdnSequence, NormIssuer};
-
-normalize_general_name(Issuer) ->
- Normalize = fun([{Description, Type, {printableString, Value}}]) ->
- NewValue = string:to_lower(strip_spaces(Value)),
- {Description, Type, {printableString, NewValue}};
- (Atter) ->
- Atter
- end,
- lists:sort(lists:map(Normalize, Issuer)).
+ NormIssuer = do_normalize_general_name(Issuer),
+ {rdnSequence, NormIssuer}.
+%%--------------------------------------------------------------------
+-spec is_self_signed(#'OTPCertificate'{}) -> boolean().
+%%
+%% Description: Checks if the certificate is self signed.
+%%--------------------------------------------------------------------
is_self_signed(#'OTPCertificate'{tbsCertificate=
#'OTPTBSCertificate'{issuer = Issuer,
subject = Subject}}) ->
is_issuer(Issuer, Subject).
-
+%%--------------------------------------------------------------------
+-spec is_issuer({rdnSequence, term()}, {rdnSequence, term()}) -> boolean().
+%%
+%% Description: Checks if <Issuer> issued <Candidate>.
+%%--------------------------------------------------------------------
is_issuer({rdnSequence, Issuer}, {rdnSequence, Candidate}) ->
is_dir_name(Issuer, Candidate, true).
-
+%%--------------------------------------------------------------------
+-spec issuer_id(#'OTPCertificate'{}, self | other) ->
+ {ok, {integer(), term()}} | {error, issuer_not_found}.
+%%
+%% Description: Extracts the issuer id from a certificate if possible.
+%%--------------------------------------------------------------------
issuer_id(Otpcert, other) ->
TBSCert = Otpcert#'OTPCertificate'.tbsCertificate,
Extensions = extensions_list(TBSCert#'OTPTBSCertificate'.extensions),
@@ -227,7 +280,12 @@ issuer_id(Otpcert, self) ->
SerialNr = TBSCert#'OTPTBSCertificate'.serialNumber,
{ok, {SerialNr, normalize_general_name(Issuer)}}.
-
+%%--------------------------------------------------------------------
+-spec is_fixed_dh_cert(#'OTPCertificate'{}) -> boolean().
+%%
+%% Description: Checks if the certificate can be be used
+%% for DH key agreement.
+%%--------------------------------------------------------------------
is_fixed_dh_cert(#'OTPCertificate'{tbsCertificate =
#'OTPTBSCertificate'{subjectPublicKeyInfo =
SubjectPublicKeyInfo,
@@ -235,31 +293,79 @@ is_fixed_dh_cert(#'OTPCertificate'{tbsCertificate =
Extensions}}) ->
is_fixed_dh_cert(SubjectPublicKeyInfo, extensions_list(Extensions)).
+
+%%--------------------------------------------------------------------
+-spec verify_fun(#'OTPCertificate'{}, {bad_cert, atom()} | {extension, #'Extension'{}}|
+ valid | valid_peer, term(), fun()) -> term().
+%%
+%% Description: Gives the user application the opportunity handle path
+%% validation errors and unknown extensions and optional do other
+%% things with a validated certificate.
+%% --------------------------------------------------------------------
+verify_fun(Otpcert, Result, UserState0, VerifyFun) ->
+ case VerifyFun(Otpcert, Result, UserState0) of
+ {valid,UserState} ->
+ UserState;
+ {fail, Reason} ->
+ case Result of
+ {bad_cert, _} ->
+ throw(Result);
+ _ ->
+ throw({bad_cert, Reason})
+ end;
+ {unknown, UserState} ->
+ case Result of
+ {extension, #'Extension'{critical = true}} ->
+ throw({bad_cert, unknown_critical_extension});
+ _ ->
+ UserState
+ end
+ end.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+do_normalize_general_name(Issuer) ->
+ Normalize = fun([{Description, Type, {printableString, Value}}]) ->
+ NewValue = string:to_lower(strip_spaces(Value)),
+ [{Description, Type, {printableString, NewValue}}];
+ (Atter) ->
+ Atter
+ end,
+ lists:sort(lists:map(Normalize, Issuer)).
+
+%% See rfc3280 4.1.2.6 Subject: regarding emails.
+extract_email({rdnSequence, List}) ->
+ extract_email2(List).
+extract_email2([[#'AttributeTypeAndValue'{type=?'id-emailAddress',
+ value=Mail}]|_]) ->
+ [{rfc822Name, Mail}];
+extract_email2([_|Rest]) ->
+ extract_email2(Rest);
+extract_email2([]) -> [].
extensions_list(asn1_NOVALUE) ->
[];
extensions_list(Extensions) ->
Extensions.
-not_valid(Error, true, _) ->
- throw(Error);
-not_valid(Error, false, AccErrors) ->
- [Error | AccErrors].
-verify_signature(OtpCert, DerCert, Key, KeyParams) ->
- %% Signature is an ASN1 compact bit string
+extract_verify_data(OtpCert, DerCert) ->
{0, Signature} = OtpCert#'OTPCertificate'.signature,
SigAlgRec = OtpCert#'OTPCertificate'.signatureAlgorithm,
SigAlg = SigAlgRec#'SignatureAlgorithm'.algorithm,
- EncTBSCert = encoded_tbs_cert(DerCert),
- verify(SigAlg, EncTBSCert, Signature, Key, KeyParams).
+ PlainText = encoded_tbs_cert(DerCert),
+ DigestType = digest_type(SigAlg),
+ {DigestType, PlainText, Signature}.
-verify(Alg, PlainText, Signature, Key, KeyParams) ->
- public_key:verify_signature(PlainText, digest_type(Alg),
- Signature, Key, KeyParams).
+verify_signature(OtpCert, DerCert, Key, KeyParams) ->
+ {DigestType, PlainText, Signature} = extract_verify_data(OtpCert, DerCert),
+ case Key of
+ #'RSAPublicKey'{} ->
+ public_key:verify(PlainText, DigestType, Signature, Key);
+ _ ->
+ public_key:verify(PlainText, DigestType, Signature, {Key, KeyParams})
+ end.
encoded_tbs_cert(Cert) ->
{ok, PKIXCert} =
@@ -275,13 +381,6 @@ digest_type(?md5WithRSAEncryption) ->
digest_type(?'id-dsa-with-sha1') ->
sha.
-digest(?sha1WithRSAEncryption, Msg) ->
- crypto:sha(Msg);
-digest(?md5WithRSAEncryption, Msg) ->
- crypto:md5(Msg);
-digest(?'id-dsa-with-sha1', Msg) ->
- crypto:sha(Msg).
-
public_key_info(PublicKeyInfo,
#path_validation_state{working_public_key_algorithm =
WorkingAlgorithm,
@@ -295,10 +394,12 @@ public_key_info(PublicKeyInfo,
NewPublicKeyParams =
case PublicKeyParams of
- 'NULL' when WorkingAlgorithm == Algorithm ->
+ {null, 'NULL'} when WorkingAlgorithm == Algorithm ->
WorkingParams;
- _ ->
- PublicKeyParams
+ {params, Params} ->
+ Params;
+ Params ->
+ Params
end,
{Algorithm, PublicKey, NewPublicKeyParams}.
@@ -332,12 +433,6 @@ is_dir_name([[{'AttributeTypeAndValue', Type, What1}]|Rest1],
true -> is_dir_name(Rest1,Rest2,Exact);
false -> false
end;
-is_dir_name([{'AttributeTypeAndValue', Type, What1}|Rest1],
- [{'AttributeTypeAndValue', Type, What2}|Rest2], Exact) ->
- case is_dir_name2(What1,What2) of
- true -> is_dir_name(Rest1,Rest2,Exact);
- false -> false
- end;
is_dir_name(_,[],false) ->
true;
is_dir_name(_,_,_) ->
@@ -382,210 +477,179 @@ select_extension(Id, [_ | Extensions]) ->
select_extension(Id, Extensions).
%% No extensions present
-validate_extensions(asn1_NOVALUE, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- validate_extensions([], ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-validate_extensions([], ValidationState, basic_constraint, _SelfSigned,
- UnknownExtensions, _Verify, AccErr) ->
- {ValidationState, UnknownExtensions, AccErr};
-validate_extensions([], ValidationState =
- #path_validation_state{max_path_length = Len,
- last_cert = Last},
- no_basic_constraint, SelfSigned, UnknownExtensions,
- Verify, AccErr0) ->
+validate_extensions(OtpCert, asn1_NOVALUE, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun) ->
+ validate_extensions(OtpCert, [], ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+
+validate_extensions(_,[], ValidationState, basic_constraint, _SelfSigned,
+ UserState, _) ->
+ {ValidationState, UserState};
+validate_extensions(OtpCert, [], ValidationState =
+ #path_validation_state{max_path_length = Len,
+ last_cert = Last},
+ no_basic_constraint, SelfSigned, UserState0, VerifyFun) ->
case Last of
true when SelfSigned ->
- {ValidationState, UnknownExtensions, AccErr0};
+ {ValidationState, UserState0};
true ->
{ValidationState#path_validation_state{max_path_length = Len - 1},
- UnknownExtensions, AccErr0};
+ UserState0};
%% basic_constraint must appear in certs used for digital sign
%% see 4.2.1.10 in rfc 3280
false ->
- AccErr = not_valid({bad_cert, missing_basic_constraint},
- Verify, AccErr0),
+ UserState = verify_fun(OtpCert, {bad_cert, missing_basic_constraint},
+ UserState0, VerifyFun),
case SelfSigned of
true ->
- {ValidationState, UnknownExtensions, AccErr};
+ {ValidationState, UserState};
false ->
{ValidationState#path_validation_state{max_path_length =
- Len - 1},
- UnknownExtensions, AccErr}
+ Len - 1},
+ UserState}
end
end;
-validate_extensions([#'Extension'{extnID = ?'id-ce-basicConstraints',
+validate_extensions(OtpCert,
+ [#'Extension'{extnID = ?'id-ce-basicConstraints',
extnValue =
- #'BasicConstraints'{cA = true,
- pathLenConstraint = N}} |
+ #'BasicConstraints'{cA = true,
+ pathLenConstraint = N}} |
Rest],
- ValidationState =
- #path_validation_state{max_path_length = Len}, _,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- Length = if SelfSigned -> min(N, Len);
- true -> min(N, Len-1)
+ ValidationState =
+ #path_validation_state{max_path_length = Len}, _,
+ SelfSigned, UserState, VerifyFun) ->
+ Length = if SelfSigned -> erlang:min(N, Len);
+ true -> erlang:min(N, Len-1)
end,
- validate_extensions(Rest,
+ validate_extensions(OtpCert, Rest,
ValidationState#path_validation_state{max_path_length =
- Length},
- basic_constraint, SelfSigned, UnknownExtensions,
- Verify, AccErr);
+ Length},
+ basic_constraint, SelfSigned,
+ UserState, VerifyFun);
%% The pathLenConstraint field is meaningful only if cA is set to
%% TRUE.
-validate_extensions([#'Extension'{extnID = ?'id-ce-basicConstraints',
- extnValue =
- #'BasicConstraints'{cA = false}} |
- Rest], ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-%%
-validate_extensions([#'Extension'{extnID = ?'id-ce-keyUsage',
- extnValue = KeyUse
- } | Rest],
- #path_validation_state{last_cert=Last} = ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue =
+ #'BasicConstraints'{cA = false}} |
+ Rest], ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun) ->
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-keyUsage',
+ extnValue = KeyUse
+ } | Rest],
+ #path_validation_state{last_cert=Last} = ValidationState,
+ ExistBasicCon, SelfSigned,
+ UserState0, VerifyFun) ->
case Last orelse is_valid_key_usage(KeyUse, keyCertSign) of
true ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr0);
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun);
false ->
- AccErr = not_valid({bad_cert, invalid_key_usage}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr)
+ UserState = verify_fun(OtpCert, {bad_cert, invalid_key_usage},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun)
end;
-validate_extensions([#'Extension'{extnID = ?'id-ce-subjectAltName',
- extnValue = Names} | Rest],
- ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-subjectAltName',
+ extnValue = Names,
+ critical = true} = Ext | Rest],
+ ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun) ->
case validate_subject_alt_names(Names) of
- true when Names =/= [] ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr0);
- _ ->
- AccErr =
- not_valid({bad_cert, invalid_subject_altname},
- Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify,
- AccErr)
+ true ->
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun);
+ false ->
+ UserState = verify_fun(OtpCert, {extension, Ext},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun)
end;
-%% This extension SHOULD NOT be marked critical. Its value
-%% does not have to be further validated at this point.
-validate_extensions([#'Extension'{extnID = ?'id-ce-issuerAltName',
- extnValue = _} | Rest],
- ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-%% This extension MUST NOT be marked critical.Its value
-%% does not have to be further validated at this point.
-validate_extensions([#'Extension'{extnID = Id,
- extnValue = _,
- critical = false} | Rest],
- ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr)
- when Id == ?'id-ce-subjectKeyIdentifier';
- Id == ?'id-ce-authorityKeyIdentifier'->
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-validate_extensions([#'Extension'{extnID = ?'id-ce-nameConstraints',
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-nameConstraints',
extnValue = NameConst} | Rest],
ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr) ->
+ ExistBasicCon, SelfSigned, UserState, VerifyFun) ->
Permitted = NameConst#'NameConstraints'.permittedSubtrees,
Excluded = NameConst#'NameConstraints'.excludedSubtrees,
NewValidationState = add_name_constraints(Permitted, Excluded,
ValidationState),
- validate_extensions(Rest, NewValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
+ validate_extensions(OtpCert, Rest, NewValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
-validate_extensions([#'Extension'{extnID = ?'id-ce-certificatePolicies',
- critical = true} | Rest], ValidationState,
- ExistBasicCon, SelfSigned,
- UnknownExtensions, Verify, AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-certificatePolicies',
+ critical = true} = Ext| Rest], ValidationState,
+ ExistBasicCon, SelfSigned, UserState0, VerifyFun) ->
%% TODO: Remove this clause when policy handling is
%% fully implemented
- AccErr =
- not_valid({bad_cert, unknown_critical_extension}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-
-validate_extensions([#'Extension'{extnID = ?'id-ce-certificatePolicies',
- extnValue = #'PolicyInformation'{
- policyIdentifier = Id,
- policyQualifiers = Qualifier}}
- | Rest], #path_validation_state{valid_policy_tree = Tree}
+ UserState = verify_fun(OtpCert, {extension, Ext},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert,Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-certificatePolicies',
+ extnValue = #'PolicyInformation'{
+ policyIdentifier = Id,
+ policyQualifiers = Qualifier}}
+ | Rest], #path_validation_state{valid_policy_tree = Tree}
= ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr) ->
+ ExistBasicCon, SelfSigned, UserState, VerifyFun) ->
%% TODO: Policy imp incomplete
NewTree = process_policy_tree(Id, Qualifier, Tree),
- validate_extensions(Rest,
+ validate_extensions(OtpCert, Rest,
ValidationState#path_validation_state{
valid_policy_tree = NewTree},
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr);
+ ExistBasicCon, SelfSigned, UserState, VerifyFun);
-validate_extensions([#'Extension'{extnID = ?'id-ce-policyConstraints',
- critical = true} | Rest], ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions, Verify,
- AccErr0) ->
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-policyConstraints',
+ critical = true} = Ext | Rest], ValidationState,
+ ExistBasicCon, SelfSigned, UserState0, VerifyFun) ->
%% TODO: Remove this clause when policy handling is
%% fully implemented
- AccErr =
- not_valid({bad_cert, unknown_critical_extension}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
-validate_extensions([#'Extension'{extnID = ?'id-ce-policyConstraints',
- extnValue = #'PolicyConstraints'{
- requireExplicitPolicy = ExpPolicy,
- inhibitPolicyMapping = MapPolicy}}
- | Rest], ValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr) ->
+ UserState = verify_fun(OtpCert, {extension, Ext},
+ UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
+validate_extensions(OtpCert, [#'Extension'{extnID = ?'id-ce-policyConstraints',
+ extnValue = #'PolicyConstraints'{
+ requireExplicitPolicy = ExpPolicy,
+ inhibitPolicyMapping = MapPolicy}}
+ | Rest], ValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun) ->
%% TODO: Policy imp incomplete
- NewValidationState = add_policy_constraints(ExpPolicy, MapPolicy,
+ NewValidationState = add_policy_constraints(ExpPolicy, MapPolicy,
ValidationState),
- validate_extensions(Rest, NewValidationState, ExistBasicCon,
- SelfSigned, UnknownExtensions, Verify, AccErr);
+ validate_extensions(OtpCert, Rest, NewValidationState, ExistBasicCon,
+ SelfSigned, UserState, VerifyFun);
-validate_extensions([Extension | Rest], ValidationState,
- ExistBasicCon, SelfSigned, UnknownExtensions,
- Verify, AccErr) ->
- validate_extensions(Rest, ValidationState, ExistBasicCon, SelfSigned,
- [Extension | UnknownExtensions], Verify, AccErr).
+validate_extensions(OtpCert, [#'Extension'{} = Extension | Rest],
+ ValidationState, ExistBasicCon,
+ SelfSigned, UserState0, VerifyFun) ->
+ UserState = verify_fun(OtpCert, {extension, Extension}, UserState0, VerifyFun),
+ validate_extensions(OtpCert, Rest, ValidationState, ExistBasicCon, SelfSigned,
+ UserState, VerifyFun).
is_valid_key_usage(KeyUse, Use) ->
lists:member(Use, KeyUse).
validate_subject_alt_names([]) ->
- true;
+ false;
validate_subject_alt_names([AltName | Rest]) ->
case is_valid_subject_alt_name(AltName) of
true ->
- validate_subject_alt_names(Rest);
+ true;
false ->
- false
+ validate_subject_alt_names(Rest)
end.
is_valid_subject_alt_name({Name, Value}) when Name == rfc822Name;
@@ -613,14 +677,11 @@ is_valid_subject_alt_name({directoryName, _}) ->
true;
is_valid_subject_alt_name({_, [_|_]}) ->
true;
+is_valid_subject_alt_name({otherName, #'AnotherName'{}}) ->
+ false;
is_valid_subject_alt_name({_, _}) ->
false.
-min(N, M) when N =< M ->
- N;
-min(_, M) ->
- M.
-
is_ip_address(Address) ->
case inet_parse:address(Address) of
{ok, _} ->
@@ -683,10 +744,11 @@ split_auth_path(URIPart) ->
end.
split_uri(UriPart, SplitChar, NoMatchResult, SkipLeft, SkipRight) ->
- case regexp:first_match(UriPart, SplitChar) of
- {match, Match, _} ->
- {string:substr(UriPart, 1, Match - SkipLeft),
- string:substr(UriPart, Match + SkipRight, length(UriPart))};
+ case re:run(UriPart, SplitChar) of
+ {match,[{Start, _}]} ->
+ StrPos = Start + 1,
+ {string:substr(UriPart, 1, StrPos - SkipLeft),
+ string:substr(UriPart, StrPos + SkipRight, length(UriPart))};
nomatch ->
NoMatchResult
end.
@@ -939,7 +1001,7 @@ add_policy_constraints(ExpPolicy, MapPolicy,
policy_constraint(Current, asn1_NOVALUE, _) ->
Current;
policy_constraint(Current, New, CertNum) ->
- min(Current, New + CertNum).
+ erlang:min(Current, New + CertNum).
process_policy_tree(_,_, ?NULL) ->
?NULL;
diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl
index c7d4080adb..b86d7a1f0c 100644
--- a/lib/public_key/src/pubkey_cert_records.erl
+++ b/lib/public_key/src/pubkey_cert_records.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,96 +23,81 @@
-include("public_key.hrl").
--export([decode_cert/2, encode_cert/1, encode_tbs_cert/1, transform/2]).
-
--export([old_decode_cert/2, old_encode_cert/1]). %% Debugging and testing new code.
+-export([decode_cert/1, transform/2, supportedPublicKeyAlgorithms/1]).
%%====================================================================
%% Internal application API
%%====================================================================
-decode_cert(DerCert, plain) ->
- 'OTP-PUB-KEY':decode('Certificate', DerCert);
-decode_cert(DerCert, otp) ->
+%%--------------------------------------------------------------------
+-spec decode_cert(DerCert::binary()) -> {ok, #'OTPCertificate'{}}.
+%%
+%% Description: Recursively decodes a Certificate.
+%%--------------------------------------------------------------------
+decode_cert(DerCert) ->
{ok, Cert} = 'OTP-PUB-KEY':decode('OTPCertificate', DerCert),
- {ok, decode_all_otp(Cert)}.
-
-old_decode_cert(DerCert, otp) ->
- {ok, Cert} = 'OTP-PUB-KEY':decode('Certificate', DerCert),
- {ok, plain_to_otp(Cert)}.
-
-old_encode_cert(Cert) ->
- PlainCert = otp_to_plain(Cert),
- {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', PlainCert),
- list_to_binary(EncCert).
-
-
-encode_cert(Cert = #'Certificate'{}) ->
- {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', Cert),
- list_to_binary(EncCert);
-encode_cert(C = #'OTPCertificate'{tbsCertificate = TBS =
- #'OTPTBSCertificate'{
- issuer=Issuer0,
- subject=Subject0,
- subjectPublicKeyInfo=Spki0,
- extensions=Exts0}
- }) ->
- Issuer = transform(Issuer0,encode),
- Subject = transform(Subject0,encode),
- Spki = encode_supportedPublicKey(Spki0),
- Exts = encode_extensions(Exts0),
- %% io:format("Extensions ~p~n",[Exts]),
- Cert = C#'OTPCertificate'{tbsCertificate=
- TBS#'OTPTBSCertificate'{
- issuer=Issuer, subject=Subject,
- subjectPublicKeyInfo=Spki,
- extensions=Exts}},
- {ok, EncCert} = 'OTP-PUB-KEY':encode('OTPCertificate', Cert),
- list_to_binary(EncCert).
+ #'OTPCertificate'{tbsCertificate = TBS} = Cert,
+ {ok, Cert#'OTPCertificate'{tbsCertificate = decode_tbs(TBS)}}.
-encode_tbs_cert(TBS = #'OTPTBSCertificate'{
- issuer=Issuer0,
- subject=Subject0,
- subjectPublicKeyInfo=Spki0,
- extensions=Exts0}) ->
- Issuer = transform(Issuer0,encode),
- Subject = transform(Subject0,encode),
- Spki = encode_supportedPublicKey(Spki0),
- Exts = encode_extensions(Exts0),
- TBSCert = TBS#'OTPTBSCertificate'{issuer=Issuer,subject=Subject,
- subjectPublicKeyInfo=Spki,extensions=Exts},
- {ok, EncTBSCert} = 'OTP-PUB-KEY':encode('OTPTBSCertificate', TBSCert),
- list_to_binary(EncTBSCert).
+%%--------------------------------------------------------------------
+-spec transform(term(), encode | decode) ->term().
+%%
+%% Description: Transforms between encoded and decode otp formated
+%% certificate parts.
+%%--------------------------------------------------------------------
+
+transform(#'OTPCertificate'{tbsCertificate = TBS} = Cert, encode) ->
+ Cert#'OTPCertificate'{tbsCertificate=encode_tbs(TBS)};
+transform(#'OTPCertificate'{tbsCertificate = TBS} = Cert, decode) ->
+ Cert#'OTPCertificate'{tbsCertificate=decode_tbs(TBS)};
+transform(#'OTPTBSCertificate'{}= TBS, encode) ->
+ encode_tbs(TBS);
+transform(#'OTPTBSCertificate'{}= TBS, decode) ->
+ decode_tbs(TBS);
+transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
+ {ok, Value} =
+ case attribute_type(Id) of
+ Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
+ _UnknownType -> {ok, Value0}
+ end,
+ ATAV#'AttributeTypeAndValue'{value=Value};
+transform(AKI = #'AuthorityKeyIdentifier'{authorityCertIssuer=ACI},Func) ->
+ AKI#'AuthorityKeyIdentifier'{authorityCertIssuer=transform(ACI,Func)};
+transform(List = [{directoryName, _}],Func) ->
+ [{directoryName, transform(Value,Func)} || {directoryName, Value} <- List];
+transform({directoryName, Value},Func) ->
+ {directoryName, transform(Value,Func)};
+transform({rdnSequence, SeqList},Func) when is_list(SeqList) ->
+ {rdnSequence,
+ lists:map(fun(Seq) ->
+ lists:map(fun(Element) -> transform(Element,Func) end, Seq)
+ end, SeqList)};
+transform(#'NameConstraints'{permittedSubtrees=Permitted, excludedSubtrees=Excluded}, Func) ->
+ #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func),
+ excludedSubtrees=transform_sub_tree(Excluded,Func)};
+
+transform(Other,_) ->
+ Other.
%%--------------------------------------------------------------------
-%%% Internal functions
+-spec supportedPublicKeyAlgorithms(Oid::tuple()) -> asn1_type().
+%%
+%% Description: Returns the public key type for an algorithm
+%% identifier tuple as found in SubjectPublicKeyInfo.
+%%
%%--------------------------------------------------------------------
-
-decode_all_otp(C = #'OTPCertificate'{tbsCertificate = TBS =
- #'OTPTBSCertificate'{
- issuer=Issuer0,
- subject=Subject0,
- subjectPublicKeyInfo=Spki0,
- extensions=Exts0}
- }) ->
- Issuer = transform(Issuer0,decode),
- Subject = transform(Subject0,decode),
- Spki = decode_supportedPublicKey(Spki0),
- Exts = decode_extensions(Exts0),
- %% io:format("Extensions ~p~n",[Exts]),
- C#'OTPCertificate'{tbsCertificate=
- TBS#'OTPTBSCertificate'{
- issuer=Issuer, subject=Subject,
- subjectPublicKeyInfo=Spki,extensions=Exts}}.
-
-
-%%% SubjectPublicKey
supportedPublicKeyAlgorithms(?'rsaEncryption') -> 'RSAPublicKey';
supportedPublicKeyAlgorithms(?'id-dsa') -> 'DSAPublicKey';
supportedPublicKeyAlgorithms(?'dhpublicnumber') -> 'DHPublicKey';
supportedPublicKeyAlgorithms(?'id-keyExchangeAlgorithm') -> 'KEA-PublicKey';
supportedPublicKeyAlgorithms(?'id-ecPublicKey') -> 'ECPoint'.
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+%%% SubjectPublicKey
+
decode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA =
#'PublicKeyAlgorithm'{algorithm=Algo},
subjectPublicKey = {0,SPK0}}) ->
@@ -186,33 +171,28 @@ encode_extensions(Exts) ->
end
end, Exts).
-transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
- {ok, Value} =
- case attribute_type(Id) of
- Type when is_atom(Type) -> 'OTP-PUB-KEY':Func(Type, Value0);
- _UnknownType -> {ok, Value0}
- end,
- ATAV#'AttributeTypeAndValue'{value=Value};
-transform(AKI = #'AuthorityKeyIdentifier'{authorityCertIssuer=ACI},Func) ->
- AKI#'AuthorityKeyIdentifier'{authorityCertIssuer=transform(ACI,Func)};
-transform(List = [{directoryName, _}],Func) ->
- [{directoryName, transform(Value,Func)} || {directoryName, Value} <- List];
-transform({directoryName, Value},Func) ->
- {directoryName, transform(Value,Func)};
-transform({rdnSequence, SeqList},Func) when is_list(SeqList) ->
- {rdnSequence,
- lists:map(fun(Seq) ->
- lists:map(fun(Element) -> transform(Element,Func) end, Seq)
- end, SeqList)};
-%% transform(List = [{rdnSequence, _}|_],Func) ->
-%% lists:map(fun(Element) -> transform(Element,Func) end, List);
-transform(#'NameConstraints'{permittedSubtrees=Permitted, excludedSubtrees=Excluded}, Func) ->
- Res = #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func),
- excludedSubtrees=transform_sub_tree(Excluded,Func)},
-%% io:format("~p~n",[Res]),
- Res;
-transform(Other,_) ->
- Other.
+encode_tbs(TBS=#'OTPTBSCertificate'{issuer=Issuer0,
+ subject=Subject0,
+ subjectPublicKeyInfo=Spki0,
+ extensions=Exts0}) ->
+ Issuer = transform(Issuer0,encode),
+ Subject = transform(Subject0,encode),
+ Spki = encode_supportedPublicKey(Spki0),
+ Exts = encode_extensions(Exts0),
+ TBS#'OTPTBSCertificate'{issuer=Issuer, subject=Subject,
+ subjectPublicKeyInfo=Spki,extensions=Exts}.
+
+decode_tbs(TBS = #'OTPTBSCertificate'{issuer=Issuer0,
+ subject=Subject0,
+ subjectPublicKeyInfo=Spki0,
+ extensions=Exts0}) ->
+ Issuer = transform(Issuer0,decode),
+ Subject = transform(Subject0,decode),
+ Spki = decode_supportedPublicKey(Spki0),
+ Exts = decode_extensions(Exts0),
+ TBS#'OTPTBSCertificate'{issuer=Issuer, subject=Subject,
+ subjectPublicKeyInfo=Spki,extensions=Exts}.
+
transform_sub_tree(asn1_NOVALUE,_) -> asn1_NOVALUE;
transform_sub_tree(TreeList,Func) ->
[Tree#'GeneralSubtree'{base=transform(Name,Func)} ||
@@ -236,303 +216,3 @@ attribute_type(?'id-at-pseudonym') -> 'X520Pseudonym';
attribute_type(?'id-domainComponent') -> 'DomainComponent';
attribute_type(?'id-emailAddress') -> 'EmailAddress';
attribute_type(Type) -> Type.
-
-%%% Old code transforms
-
-plain_to_otp(#'Certificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = SigAlg,
- signature = Signature} = Cert) ->
- Cert#'Certificate'{tbsCertificate = plain_to_otp(TBSCert),
- signatureAlgorithm = plain_to_otp(SigAlg),
- signature = plain_to_otp(Signature)};
-
-plain_to_otp(#'TBSCertificate'{signature = Signature,
- issuer = Issuer,
- subject = Subject,
- subjectPublicKeyInfo = SPubKeyInfo,
- extensions = Extensions} = TBSCert) ->
-
- TBSCert#'TBSCertificate'{signature = plain_to_otp(Signature),
- issuer = plain_to_otp(Issuer),
- subject =
- plain_to_otp(Subject),
- subjectPublicKeyInfo =
- plain_to_otp(SPubKeyInfo),
- extensions =
- plain_to_otp_extensions(Extensions)
- };
-
-plain_to_otp(#'AlgorithmIdentifier'{algorithm = Algorithm,
- parameters = Params}) ->
- SignAlgAny =
- #'SignatureAlgorithm-Any'{algorithm = Algorithm,
- parameters = Params},
- {ok, AnyEnc} = 'OTP-PUB-KEY':encode('SignatureAlgorithm-Any',
- SignAlgAny),
- {ok, SignAlg} = 'OTP-PUB-KEY':decode('SignatureAlgorithm',
- list_to_binary(AnyEnc)),
- SignAlg;
-
-plain_to_otp({rdnSequence, SeqList}) when is_list(SeqList) ->
- {rdnSequence,
- lists:map(fun(Seq) ->
- lists:map(fun(Element) ->
- plain_to_otp(Element)
- end,
- Seq)
- end, SeqList)};
-
-plain_to_otp(#'AttributeTypeAndValue'{} = ATAV) ->
- {ok, ATAVEnc} =
- 'OTP-PUB-KEY':encode('AttributeTypeAndValue', ATAV),
- {ok, ATAVDec} = 'OTP-PUB-KEY':decode('OTPAttributeTypeAndValue',
- list_to_binary(ATAVEnc)),
- #'AttributeTypeAndValue'{type = ATAVDec#'OTPAttributeTypeAndValue'.type,
- value =
- ATAVDec#'OTPAttributeTypeAndValue'.value};
-
-plain_to_otp(#'SubjectPublicKeyInfo'{algorithm =
- #'AlgorithmIdentifier'{algorithm
- = Algo,
- parameters =
- Params},
- subjectPublicKey = PublicKey}) ->
-
- AnyAlgo = #'PublicKeyAlgorithm'{algorithm = Algo,
- parameters = Params},
- {0, AnyKey} = PublicKey,
- AnyDec = #'OTPSubjectPublicKeyInfo-Any'{algorithm = AnyAlgo,
- subjectPublicKey = AnyKey},
- {ok, AnyEnc} =
- 'OTP-PUB-KEY':encode('OTPSubjectPublicKeyInfo-Any', AnyDec),
- {ok, InfoDec} = 'OTP-PUB-KEY':decode('OTPOLDSubjectPublicKeyInfo',
- list_to_binary(AnyEnc)),
-
- AlgorithmDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.algorithm,
- AlgoDec = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.algo,
- NewParams = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.parameters,
- PublicKeyDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.subjectPublicKey,
- NewAlgorithmDec =
- #'SubjectPublicKeyInfoAlgorithm'{algorithm = AlgoDec,
- parameters = NewParams},
- #'SubjectPublicKeyInfo'{algorithm = NewAlgorithmDec,
- subjectPublicKey = PublicKeyDec
- };
-
-plain_to_otp(#'Extension'{extnID = ExtID,
- critical = Critical,
- extnValue = Value})
- when ExtID == ?'id-ce-authorityKeyIdentifier';
- ExtID == ?'id-ce-subjectKeyIdentifier';
- ExtID == ?'id-ce-keyUsage';
- ExtID == ?'id-ce-privateKeyUsagePeriod';
- ExtID == ?'id-ce-certificatePolicies';
- ExtID == ?'id-ce-policyMappings';
- ExtID == ?'id-ce-subjectAltName';
- ExtID == ?'id-ce-issuerAltName';
- ExtID == ?'id-ce-subjectDirectoryAttributes';
- ExtID == ?'id-ce-basicConstraints';
- ExtID == ?'id-ce-nameConstraints';
- ExtID == ?'id-ce-policyConstraints';
- ExtID == ?'id-ce-extKeyUsage';
- ExtID == ?'id-ce-cRLDistributionPoints';
- ExtID == ?'id-ce-inhibitAnyPolicy';
- ExtID == ?'id-ce-freshestCRL' ->
- ExtAny = #'Extension-Any'{extnID = ExtID,
- critical = Critical,
- extnValue = Value},
- {ok, AnyEnc} = 'OTP-PUB-KEY':encode('Extension-Any', ExtAny),
- {ok, ExtDec} = 'OTP-PUB-KEY':decode('OTPExtension',
- list_to_binary(AnyEnc)),
-
- ExtValue = plain_to_otp_extension_value(ExtID,
- ExtDec#'OTPExtension'.extnValue),
- #'Extension'{extnID = ExtID,
- critical = ExtDec#'OTPExtension'.critical,
- extnValue = ExtValue};
-
-plain_to_otp(#'Extension'{} = Ext) ->
- Ext;
-
-plain_to_otp(#'AuthorityKeyIdentifier'{} = Ext) ->
- CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer,
- Ext#'AuthorityKeyIdentifier'{authorityCertIssuer =
- plain_to_otp(CertIssuer)};
-
-
-plain_to_otp([{directoryName, Value}]) ->
- [{directoryName, plain_to_otp(Value)}];
-
-plain_to_otp(Value) ->
- Value.
-
-otp_to_plain(#'Certificate'{tbsCertificate = TBSCert,
- signatureAlgorithm = SigAlg,
- signature = Signature} = Cert) ->
- Cert#'Certificate'{tbsCertificate = otp_to_plain(TBSCert),
- signatureAlgorithm =
- otp_to_plain(SigAlg),
- signature = otp_to_plain(Signature)};
-
-otp_to_plain(#'TBSCertificate'{signature = Signature,
- issuer = Issuer,
- subject = Subject,
- subjectPublicKeyInfo = SPubKeyInfo,
- extensions = Extensions} = TBSCert) ->
-
- TBSCert#'TBSCertificate'{signature = otp_to_plain(Signature),
- issuer = otp_to_plain(Issuer),
- subject =
- otp_to_plain(Subject),
- subjectPublicKeyInfo =
- otp_to_plain(SPubKeyInfo),
- extensions = otp_to_plain_extensions(Extensions)
- };
-
-otp_to_plain(#'SignatureAlgorithm'{} = SignAlg) ->
- {ok, EncSignAlg} = 'OTP-PUB-KEY':encode('SignatureAlgorithm', SignAlg),
- {ok, #'SignatureAlgorithm-Any'{algorithm = Algorithm,
- parameters = Params}} =
- 'OTP-PUB-KEY':decode('SignatureAlgorithm-Any',
- list_to_binary(EncSignAlg)),
- #'AlgorithmIdentifier'{algorithm = Algorithm,
- parameters = Params};
-
-otp_to_plain({rdnSequence, SeqList}) when is_list(SeqList) ->
- {rdnSequence,
- lists:map(fun(Seq) ->
- lists:map(fun(Element) ->
- otp_to_plain(Element)
- end,
- Seq)
- end, SeqList)};
-
-otp_to_plain(#'AttributeTypeAndValue'{type = Type, value = Value}) ->
- {ok, ATAVEnc} =
- 'OTP-PUB-KEY':encode('OTPAttributeTypeAndValue',
- #'OTPAttributeTypeAndValue'{type = Type,
- value = Value}),
- {ok, ATAVDec} = 'OTP-PUB-KEY':decode('AttributeTypeAndValue',
- list_to_binary(ATAVEnc)),
- ATAVDec;
-
-otp_to_plain(#'SubjectPublicKeyInfo'{algorithm =
- #'SubjectPublicKeyInfoAlgorithm'{
- algorithm = Algo,
- parameters =
- Params},
- subjectPublicKey = PublicKey}) ->
-
- OtpAlgo = #'OTPOLDSubjectPublicKeyInfo_algorithm'{algo = Algo,
- parameters = Params},
- OtpDec = #'OTPOLDSubjectPublicKeyInfo'{algorithm = OtpAlgo,
- subjectPublicKey = PublicKey},
- {ok, OtpEnc} =
- 'OTP-PUB-KEY':encode('OTPOLDSubjectPublicKeyInfo', OtpDec),
-
- {ok, AnyDec} = 'OTP-PUB-KEY':decode('OTPSubjectPublicKeyInfo-Any',
- list_to_binary(OtpEnc)),
-
- #'OTPSubjectPublicKeyInfo-Any'{algorithm = #'PublicKeyAlgorithm'{
- algorithm = NewAlgo,
- parameters = NewParams},
- subjectPublicKey = Bin} = AnyDec,
-
- #'SubjectPublicKeyInfo'{algorithm =
- #'AlgorithmIdentifier'{
- algorithm = NewAlgo,
- parameters = plain_key_params(NewParams)},
- subjectPublicKey =
- {0, Bin}
- };
-
-otp_to_plain(#'Extension'{extnID = ExtID,
- extnValue = Value} = Ext) ->
- ExtValue =
- otp_to_plain_extension_value(ExtID, Value),
-
- Ext#'Extension'{extnValue = ExtValue};
-
-otp_to_plain(#'AuthorityKeyIdentifier'{} = Ext) ->
- CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer,
- Ext#'AuthorityKeyIdentifier'{authorityCertIssuer =
- otp_to_plain(CertIssuer)};
-
-otp_to_plain([{directoryName, Value}]) ->
- [{directoryName, otp_to_plain(Value)}];
-
-otp_to_plain(Value) ->
- Value.
-
-plain_key_params('NULL') ->
- <<5,0>>;
-plain_key_params(Value) ->
- Value.
-
-plain_to_otp_extension_value(?'id-ce-authorityKeyIdentifier', Value) ->
- plain_to_otp(Value);
-plain_to_otp_extension_value(_, Value) ->
- Value.
-
-plain_to_otp_extensions(Exts) when is_list(Exts) ->
- lists:map(fun(Ext) -> plain_to_otp(Ext) end, Exts).
-
-otp_to_plain_extension_value(?'id-ce-authorityKeyIdentifier', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('AuthorityKeyIdentifier',
- otp_to_plain(Value)),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-subjectKeyIdentifier', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectKeyIdentifier', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-keyUsage', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('KeyUsage', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-privateKeyUsagePeriod', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('PrivateKeyUsagePeriod', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-certificatePolicies', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('CertificatePolicies', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-policyMappings', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyMappings', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-subjectAltName', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectAltName', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-issuerAltName', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('IssuerAltName', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-subjectDirectoryAttributes', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectDirectoryAttributes', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-basicConstraints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('BasicConstraints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-nameConstraints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('NameConstraints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-policyConstraints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyConstraints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-extKeyUsage', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('ExtKeyUsage', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-cRLDistributionPoints', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('CRLDistributionPoints', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-inhibitAnyPolicy', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('InhibitAnyPolicy', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(?'id-ce-freshestCRL', Value) ->
- {ok, Enc} = 'OTP-PUB-KEY':encode('FreshestCRL', Value),
- otp_to_plain_extension_value_format(Enc);
-otp_to_plain_extension_value(_Id, Value) ->
- Value.
-
-otp_to_plain_extension_value_format(Value) ->
- list_to_binary(Value).
-
-otp_to_plain_extensions(Exts) when is_list(Exts) ->
- lists:map(fun(Ext) ->
- otp_to_plain(Ext)
- end, Exts).
diff --git a/lib/public_key/src/pubkey_crypto.erl b/lib/public_key/src/pubkey_crypto.erl
deleted file mode 100644
index 4ab655e977..0000000000
--- a/lib/public_key/src/pubkey_crypto.erl
+++ /dev/null
@@ -1,160 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-%% Description: Functions that call the crypto driver.
-
--module(pubkey_crypto).
-
--include("public_key.hrl").
-
--export([encrypt_public/3, decrypt_private/3,
- encrypt_private/3, decrypt_public/3,
- sign/2, sign/3, verify/5, gen_key/2]).
-
--define(UINT32(X), X:32/unsigned-big-integer).
-
-%%====================================================================
-%% Internal application API
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: encrypt(PlainText, Key, Padding) -> Encrypted
-%%
-%% PlainText = binary()
-%% Key = rsa_public_key() | rsa_private_key()
-%% Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding
-%% Encrypted = binary()
-%%
-%% Description: Public key encrypts PlainText.
-%%--------------------------------------------------------------------
-encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E},
- Padding) ->
- crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],
- Padding);
-encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E},
- Padding) ->
- crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],
- Padding).
-
-encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N,
- publicExponent = E,
- privateExponent = D}, Padding) ->
- crypto:rsa_private_encrypt(PlainText, [crypto:mpint(E),
- crypto:mpint(N),
- crypto:mpint(D)], Padding).
-
-%%--------------------------------------------------------------------
-%% Function: decrypt(CipherText, Key) -> PlainText
-%%
-%% ChipherText = binary()
-%% Key = rsa_private_key()
-%% Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding
-%% PlainText = binary()
-%%
-%% Description: Uses private key to decrypt public key encrypted data.
-%%--------------------------------------------------------------------
-decrypt_private(CipherText,
- #'RSAPrivateKey'{modulus = N,publicExponent = E,
- privateExponent = D},
- Padding) ->
- crypto:rsa_private_decrypt(CipherText,
- [crypto:mpint(E), crypto:mpint(N),
- crypto:mpint(D)], Padding).
-decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E},
- Padding) ->
- crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)],
- Padding);
-decrypt_public(CipherText, #'RSAPrivateKey'{modulus = N, publicExponent = E},
- Padding) ->
- crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)],
- Padding).
-
-%%--------------------------------------------------------------------
-%% Function: sign(PlainText, Key) ->
-%% sign(DigestType, PlainText, Key) -> Signature
-%%
-%% DigestType = sha | md5
-%% PlainText = binary()
-%% Key = rsa_private_key() | dsa_private_key()
-%% Signature = binary()
-%%
-%% Description: Signs PlainText using Key.
-%%--------------------------------------------------------------------
-sign(PlainText, Digest) ->
- sign(sha, PlainText, Digest).
-
-sign(DigestType, PlainText, #'RSAPrivateKey'{modulus = N, publicExponent = E,
- privateExponent = D}) ->
- crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E),
- crypto:mpint(N),
- crypto:mpint(D)]);
-
-sign(sha, PlainText, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) ->
- crypto:dss_sign(sized_binary(PlainText),
- [crypto:mpint(P), crypto:mpint(Q),
- crypto:mpint(G), crypto:mpint(X)]).
-
-%%--------------------------------------------------------------------
-%% Function: verify(DigestType, PlainText, Signature, Key) -> true | false
-%%
-%% DigestType = sha | md5
-%% PlainText = binary()
-%% Signature = binary()
-%% Key = rsa_public_key() | dsa_public_key()
-%%
-%% Description: Verifies the signature <Signature>.
-%%--------------------------------------------------------------------
-verify(DigestType, PlainText, Signature,
- #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}, _) ->
- crypto:rsa_verify(DigestType,
- sized_binary(PlainText),
- sized_binary(Signature),
- [crypto:mpint(Exp), crypto:mpint(Mod)]);
-
-verify(sha, PlainText, Signature, Key, #'Dss-Parms'{p = P, q = Q, g = G}) ->
- crypto:dss_verify(sized_binary(PlainText),
- sized_binary(Signature),
- [crypto:mpint(P), crypto:mpint(Q),
- crypto:mpint(G), crypto:mpint(Key)]).
-
-
-%%--------------------------------------------------------------------
-%% Function: gen_key(Type, Params) ->
-%% Type = diffie_hellman
-%% Params = [P,G] | [Y, P, G]
-%% Description: Generates keys.
-%% -----------------------------------------------------------------
-gen_key(diffie_hellman, [Y, P, G]) ->
- crypto:dh_generate_key(crypto:mpint(Y), [crypto:mpint(P),
- crypto:mpint(G)]);
-gen_key(diffie_hellman, [P, G]) ->
- crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]).
-
-%%% TODO: Support rsa, dss key_gen
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-sized_binary(Binary) when is_binary(Binary) ->
- Size = size(Binary),
- <<?UINT32(Size), Binary/binary>>;
-sized_binary(List) ->
- sized_binary(list_to_binary(List)).
-
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index 9fc17b6f73..c26815bc04 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,10 @@
-module(pubkey_pem).
--export([read_file/1, read_file/2, write_file/2, decode/2]).
+-include("public_key.hrl").
+
+-export([encode/1, decode/1, decipher/2, cipher/3]).
+%% Backwards compatibility
-export([decode_key/2]).
-define(ENCODED_LINE_LENGTH, 64).
@@ -48,28 +51,84 @@
%%====================================================================
%% Internal application API
%%====================================================================
-read_file(File) ->
- read_file(File, no_passwd).
-read_file(File, Passwd) ->
- {ok, Bin} = file:read_file(File),
- decode(Bin, Passwd).
+%%--------------------------------------------------------------------
+-spec decode(binary()) -> [pem_entry()].
+%%
+%% Description: Decodes a PEM binary.
+%%--------------------------------------------------------------------
+decode(Bin) ->
+ decode_pem_entries(split_bin(Bin), []).
-write_file(File, Ds) ->
- file:write_file(File, encode_file(Ds)).
+%%--------------------------------------------------------------------
+-spec encode([pem_entry()]) -> iolist().
+%%
+%% Description: Encodes a list of PEM entries.
+%%--------------------------------------------------------------------
+encode(PemEntries) ->
+ encode_pem_entries(PemEntries).
-decode_key({_Type, Bin, not_encrypted}, _) ->
- Bin;
-decode_key({_Type, Bin, {Chipher,Salt}}, Password) ->
- decode_key(Bin, Password, Chipher, Salt).
+%%--------------------------------------------------------------------
+-spec decipher({pki_asn1_type(), DerEncrypted::binary(),{Cipher :: string(),
+ Salt :: binary()}},
+ string()) -> Der::binary().
+%%
+%% Description: Deciphers a decrypted pem entry.
+%%--------------------------------------------------------------------
+decipher({_, DecryptDer, {Cipher,Salt}}, Password) ->
+ decode_key(DecryptDer, Password, Cipher, Salt).
-decode(Bin, Passwd) ->
- decode_file(split_bin(Bin), Passwd).
+%%--------------------------------------------------------------------
+-spec cipher(Der::binary(),{Cipher :: string(), Salt :: binary()} ,
+ string()) -> binary().
+%%
+%% Description: Ciphers a PEM entry
+%%--------------------------------------------------------------------
+cipher(Der, {Cipher,Salt}, Password)->
+ encode_key(Der, Password, Cipher, Salt).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+encode_pem_entries(Entries) ->
+ [encode_pem_entry(Entry) || Entry <- Entries].
+encode_pem_entry({Type, Der, not_encrypted}) ->
+ StartStr = pem_start(Type),
+ [StartStr, "\n", b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"];
+encode_pem_entry({Type, Der, {Cipher, Salt}}) ->
+ StartStr = pem_start(Type),
+ [StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n",
+ b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"].
+
+decode_pem_entries([], Entries) ->
+ lists:reverse(Entries);
+decode_pem_entries([<<>>], Entries) ->
+ lists:reverse(Entries);
+decode_pem_entries([<<>> | Lines], Entries) ->
+ decode_pem_entries(Lines, Entries);
+decode_pem_entries([Start| Lines], Entries) ->
+ case pem_end(Start) of
+ undefined ->
+ decode_pem_entries(Lines, Entries);
+ _End ->
+ {Entry, RestLines} = join_entry(Lines, []),
+ decode_pem_entries(RestLines, [decode_pem_entry(Start, Entry) | Entries])
+ end.
+
+decode_pem_entry(Start, [<<"Proc-Type: 4,ENCRYPTED", _/binary>>, Line | Lines]) ->
+ Type = asn1_type(Start),
+ Cs = erlang:iolist_to_binary(Lines),
+ Decoded = base64:mime_decode(Cs),
+ [_, DekInfo0] = string:tokens(binary_to_list(Line), ": "),
+ [Cipher, Salt] = string:tokens(DekInfo0, ","),
+ {Type, Decoded, {Cipher, unhex(Salt)}};
+decode_pem_entry(Start, Lines) ->
+ Type = asn1_type(Start),
+ Cs = erlang:iolist_to_binary(Lines),
+ Decoded = base64:mime_decode(Cs),
+ {Type, Decoded, not_encrypted}.
+
split_bin(Bin) ->
split_bin(0, Bin).
@@ -85,82 +144,22 @@ split_bin(N, Bin) ->
split_bin(N+1, Bin)
end.
-decode_file(Bin, Passwd) ->
- decode_file(Bin, [], [Passwd]).
-
-decode_file([<<"-----BEGIN CERTIFICATE REQUEST-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, cert_req, Info);
-decode_file([<<"-----BEGIN CERTIFICATE-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, cert, Info);
-decode_file([<<"-----BEGIN RSA PRIVATE KEY-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, rsa_private_key, Info);
-decode_file([<<"-----BEGIN DSA PRIVATE KEY-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, dsa_private_key, Info);
-decode_file([<<"-----BEGIN DH PARAMETERS-----", _/binary>>|Rest], Ens, Info) ->
- decode_file2(Rest, [], Ens, dh_params, Info);
-decode_file([_|Rest], Ens, Info) ->
- decode_file(Rest, Ens, Info);
-decode_file([], Ens, _Info) ->
- {ok, lists:reverse(Ens)}.
-
-decode_file2([<<"Proc-Type: 4,ENCRYPTED", _/binary>>| Rest0], RLs, Ens, Tag, Info0) ->
- [InfoLine|Rest] = Rest0,
- Info = dek_info(InfoLine, Info0),
- decode_file2(Rest, RLs, Ens, Tag, Info);
-decode_file2([<<"-----END", _/binary>>| Rest], RLs, Ens, Tag, Info0) ->
- Cs = erlang:iolist_to_binary(lists:reverse(RLs)),
- Bin = base64:mime_decode(Cs),
- case Info0 of
- [Password, Cipher, SaltHex | Info1] ->
- Salt = unhex(SaltHex),
- Enc = {Cipher, Salt},
- Decoded = decode_key(Bin, Password, Cipher, Salt),
- decode_file(Rest, [{Tag, Decoded, Enc}| Ens], Info1);
- _ ->
- decode_file(Rest, [{Tag, Bin, not_encrypted}| Ens], Info0)
- end;
-decode_file2([L|Rest], RLs, Ens, Tag, Info0) ->
- decode_file2(Rest, [L|RLs], Ens, Tag, Info0);
-decode_file2([], _, Ens, _, _) ->
- {ok, lists:reverse(Ens)}.
-
-%% TODO Support same as decode_file
-encode_file(Ds) ->
- lists:map(
- fun({cert, Bin}) ->
- %% PKIX (X.509)
- ["-----BEGIN CERTIFICATE-----\n",
- b64encode_and_split(Bin),
- "-----END CERTIFICATE-----\n\n"];
- ({cert_req, Bin}) ->
- %% PKCS#10
- ["-----BEGIN CERTIFICATE REQUEST-----\n",
- b64encode_and_split(Bin),
- "-----END CERTIFICATE REQUEST-----\n\n"];
- ({rsa_private_key, Bin}) ->
- %% PKCS#?
- ["XXX Following key assumed not encrypted\n",
- "-----BEGIN RSA PRIVATE KEY-----\n",
- b64encode_and_split(Bin),
- "-----END RSA PRIVATE KEY-----\n\n"]
- end, Ds).
-
-dek_info(Line0, Info) ->
- Line = binary_to_list(Line0),
- [_, DekInfo0] = string:tokens(Line, ": "),
- DekInfo1 = string:tokens(DekInfo0, ",\n"),
- Info ++ DekInfo1.
+b64encode_and_split(Bin) ->
+ split_lines(base64:encode(Bin)).
-unhex(S) ->
- unhex(S, []).
+split_lines(<<Text:?ENCODED_LINE_LENGTH/binary>>) ->
+ [Text];
+split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
+ [Text, $\n | split_lines(Rest)];
+split_lines(Bin) ->
+ [Bin].
-unhex("", Acc) ->
- list_to_binary(lists:reverse(Acc));
-unhex([D1, D2 | Rest], Acc) ->
- unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
+%% Ignore white space at end of line
+join_entry([<<"-----END ", _/binary>>| Lines], Entry) ->
+ {lists:reverse(Entry), Lines};
+join_entry([Line | Lines], Entry) ->
+ join_entry(Lines, [Line | Entry]).
-decode_key(Data, no_passwd, _Alg, _Salt) ->
- Data;
decode_key(Data, Password, "DES-CBC", Salt) ->
Key = password_to_key(Password, Salt, 8),
IV = Salt,
@@ -171,6 +170,16 @@ decode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
<<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data).
+encode_key(Data, Password, "DES-CBC", Salt) ->
+ Key = password_to_key(Password, Salt, 8),
+ IV = Salt,
+ crypto:des_cbc_encrypt(Key, IV, Data);
+encode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
+ Key = password_to_key(Password, Salt, 24),
+ IV = Salt,
+ <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
+ crypto:des_ede3_cbc_encrypt(Key1, Key2, Key3, IV, Data).
+
password_to_key(Data, Salt, KeyLen) ->
<<Key:KeyLen/binary, _/binary>> =
password_to_key(<<>>, Data, Salt, KeyLen, <<>>),
@@ -182,11 +191,69 @@ password_to_key(Prev, Data, Salt, Len, Acc) ->
M = crypto:md5([Prev, Data, Salt]),
password_to_key(M, Data, Salt, Len - size(M), <<Acc/binary, M/binary>>).
-b64encode_and_split(Bin) ->
- split_lines(base64:encode(Bin)).
+unhex(S) ->
+ unhex(S, []).
-split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
- [Text, $\n | split_lines(Rest)];
-split_lines(Bin) ->
- [Bin, $\n].
+unhex("", Acc) ->
+ list_to_binary(lists:reverse(Acc));
+unhex([D1, D2 | Rest], Acc) ->
+ unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
+
+hexify(L) -> [[hex_byte(B)] || B <- binary_to_list(L)].
+
+hex_byte(B) when B < 16#10 -> ["0", erlang:integer_to_list(B, 16)];
+hex_byte(B) -> erlang:integer_to_list(B, 16).
+
+pem_start('Certificate') ->
+ <<"-----BEGIN CERTIFICATE-----">>;
+pem_start('RSAPrivateKey') ->
+ <<"-----BEGIN RSA PRIVATE KEY-----">>;
+pem_start('RSAPublicKey') ->
+ <<"-----BEGIN RSA PUBLIC KEY-----">>;
+pem_start('SubjectPublicKeyInfo') ->
+ <<"-----BEGIN PUBLIC KEY-----">>;
+pem_start('DSAPrivateKey') ->
+ <<"-----BEGIN DSA PRIVATE KEY-----">>;
+pem_start('DHParameter') ->
+ <<"-----BEGIN DH PARAMETERS-----">>.
+pem_end(<<"-----BEGIN CERTIFICATE-----">>) ->
+ <<"-----END CERTIFICATE-----">>;
+pem_end(<<"-----BEGIN RSA PRIVATE KEY-----">>) ->
+ <<"-----END RSA PRIVATE KEY-----">>;
+pem_end(<<"-----BEGIN RSA PUBLIC KEY-----">>) ->
+ <<"-----END RSA PUBLIC KEY-----">>;
+pem_end(<<"-----BEGIN PUBLIC KEY-----">>) ->
+ <<"-----END PUBLIC KEY-----">>;
+pem_end(<<"-----BEGIN DSA PRIVATE KEY-----">>) ->
+ <<"-----END DSA PRIVATE KEY-----">>;
+pem_end(<<"-----BEGIN DH PARAMETERS-----">>) ->
+ <<"-----END DH PARAMETERS-----">>;
+pem_end(_) ->
+ undefined.
+
+asn1_type(<<"-----BEGIN CERTIFICATE-----">>) ->
+ 'Certificate';
+asn1_type(<<"-----BEGIN RSA PRIVATE KEY-----">>) ->
+ 'RSAPrivateKey';
+asn1_type(<<"-----BEGIN RSA PUBLIC KEY-----">>) ->
+ 'RSAPublicKey';
+asn1_type(<<"-----BEGIN PUBLIC KEY-----">>) ->
+ 'SubjectPublicKeyInfo';
+asn1_type(<<"-----BEGIN DSA PRIVATE KEY-----">>) ->
+ 'DSAPrivateKey';
+asn1_type(<<"-----BEGIN DH PARAMETERS-----">>) ->
+ 'DHParameter'.
+
+pem_decrypt() ->
+ <<"Proc-Type: 4,ENCRYPTED">>.
+pem_decrypt_info(Cipher, Salt) ->
+ io_lib:format("DEK-Info: ~s,~s", [Cipher, lists:flatten(hexify(Salt))]).
+
+%%--------------------------------------------------------------------
+%%% Deprecated
+%%--------------------------------------------------------------------
+decode_key({_Type, Bin, not_encrypted}, _) ->
+ Bin;
+decode_key({_Type, Bin, {Chipher,Salt}}, Password) ->
+ decode_key(Bin, Password, Chipher, Salt).
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
new file mode 100644
index 0000000000..f342eab159
--- /dev/null
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -0,0 +1,431 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2011-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(pubkey_ssh).
+
+-include("public_key.hrl").
+
+-export([decode/2, encode/2]).
+
+-define(UINT32(X), X:32/unsigned-big-integer).
+%% Max encoded line length is 72, but conformance examples use 68
+%% Comment from rfc 4716: "The following are some examples of public
+%% key files that are compliant (note that the examples all wrap
+%% before 72 bytes to meet IETF document requirements; however, they
+%% are still compliant.)" So we choose to use 68 also.
+-define(ENCODED_LINE_LENGTH, 68).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
+%%
+%% Description: Decodes a ssh file-binary.
+%%--------------------------------------------------------------------
+decode(Bin, public_key)->
+ case binary:match(Bin, begin_marker()) of
+ nomatch ->
+ openssh_decode(Bin, openssh_public_key);
+ _ ->
+ rfc4716_decode(Bin)
+ end;
+decode(Bin, rfc4716_public_key) ->
+ rfc4716_decode(Bin);
+decode(Bin, Type) ->
+ openssh_decode(Bin, Type).
+
+%%--------------------------------------------------------------------
+-spec encode([{public_key(), Attributes::list()}], ssh_file()) ->
+ binary().
+%%
+%% Description: Encodes a list of ssh file entries.
+%%--------------------------------------------------------------------
+encode(Entries, Type) ->
+ erlang:iolist_to_binary(lists:map(fun({Key, Attributes}) ->
+ do_encode(Type, Key, Attributes)
+ end, Entries)).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+begin_marker() ->
+ <<"---- BEGIN SSH2 PUBLIC KEY ----">>.
+end_marker() ->
+ <<"---- END SSH2 PUBLIC KEY ----">>.
+
+rfc4716_decode(Bin) ->
+ Lines = binary:split(Bin, <<"\n">>, [global]),
+ do_rfc4716_decode(Lines, []).
+
+do_rfc4716_decode([<<"---- BEGIN SSH2 PUBLIC KEY ----", _/binary>> | Lines], Acc) ->
+ do_rfc4716_decode(Lines, Acc);
+%% Ignore empty lines before or after begin/end - markers.
+do_rfc4716_decode([<<>> | Lines], Acc) ->
+ do_rfc4716_decode(Lines, Acc);
+do_rfc4716_decode([], Acc) ->
+ lists:reverse(Acc);
+do_rfc4716_decode(Lines, Acc) ->
+ {Headers, PubKey, Rest} = rfc4716_decode_lines(Lines, []),
+ case Headers of
+ [_|_] ->
+ do_rfc4716_decode(Rest, [{PubKey, [{headers, Headers}]} | Acc]);
+ _ ->
+ do_rfc4716_decode(Rest, [{PubKey, []} | Acc])
+ end.
+
+rfc4716_decode_lines([Line | Lines], Acc) ->
+ case binary:last(Line) of
+ $\\ ->
+ NewLine = binary:replace(Line,<<"\\">>, hd(Lines), []),
+ rfc4716_decode_lines([NewLine | tl(Lines)], Acc);
+ _ ->
+ rfc4716_decode_line(Line, Lines, Acc)
+ end.
+
+rfc4716_decode_line(Line, Lines, Acc) ->
+ case binary:split(Line, <<":">>) of
+ [Tag, Value] ->
+ rfc4716_decode_lines(Lines, [{string_decode(Tag), unicode_decode(Value)} | Acc]);
+ _ ->
+ {Body, Rest} = join_entry([Line | Lines], []),
+ {lists:reverse(Acc), rfc4716_pubkey_decode(base64:mime_decode(Body)), Rest}
+ end.
+
+join_entry([<<"---- END SSH2 PUBLIC KEY ----", _/binary>>| Lines], Entry) ->
+ {lists:reverse(Entry), Lines};
+join_entry([Line | Lines], Entry) ->
+ join_entry(Lines, [Line | Entry]).
+
+
+rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
+ ?UINT32(SizeE), E:SizeE/binary,
+ ?UINT32(SizeN), N:SizeN/binary>>) when Type == <<"ssh-rsa">> ->
+ #'RSAPublicKey'{modulus = erlint(SizeN, N),
+ publicExponent = erlint(SizeE, E)};
+
+rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
+ ?UINT32(SizeP), P:SizeP/binary,
+ ?UINT32(SizeQ), Q:SizeQ/binary,
+ ?UINT32(SizeG), G:SizeG/binary,
+ ?UINT32(SizeY), Y:SizeY/binary>>) when Type == <<"ssh-dss">> ->
+ {erlint(SizeY, Y),
+ #'Dss-Parms'{p = erlint(SizeP, P),
+ q = erlint(SizeQ, Q),
+ g = erlint(SizeG, G)}}.
+
+openssh_decode(Bin, FileType) ->
+ Lines = binary:split(Bin, <<"\n">>, [global]),
+ do_openssh_decode(FileType, Lines, []).
+
+do_openssh_decode(_, [], Acc) ->
+ lists:reverse(Acc);
+%% Ignore empty lines
+do_openssh_decode(FileType, [<<>> | Lines], Acc) ->
+ do_openssh_decode(FileType, Lines, Acc);
+%% Ignore lines that start with #
+do_openssh_decode(FileType,[<<"#", _/binary>> | Lines], Acc) ->
+ do_openssh_decode(FileType, Lines, Acc);
+do_openssh_decode(auth_keys = FileType, [Line | Lines], Acc) ->
+ Split = binary:split(Line, <<" ">>, [global]),
+ case mend_split(Split, []) of
+ %% ssh2
+ [Options, KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, string_decode(Comment)},
+ {options, comma_list_decode(Options)}]}
+ | Acc]);
+
+ [KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, string_decode(Comment)}]} | Acc]);
+ %% ssh1
+ [Options, Bits, Exponent, Modulus, Comment] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, string_decode(Comment)},
+ {options, comma_list_decode(Options)},
+ {bits, integer_decode(Bits)}]} | Acc]);
+ [Bits, Exponent, Modulus, Comment] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, string_decode(Comment)},
+ {bits, integer_decode(Bits)}]} | Acc])
+ end;
+
+do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) ->
+ case binary:split(Line, <<" ">>, [global]) of
+ %% ssh 2
+ [HostNames, KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{hostnames, comma_list_decode(HostNames)}]}| Acc]);
+ [HostNames, KeyType, Base64Enc, Comment] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, string_decode(Comment)},
+ {hostnames, comma_list_decode(HostNames)}]} | Acc]);
+ %% ssh 1
+ [HostNames, Bits, Exponent, Modulus, Comment] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, string_decode(Comment)},
+ {hostnames, comma_list_decode(HostNames)},
+ {bits, integer_decode(Bits)}]} | Acc]);
+ [HostNames, Bits, Exponent, Modulus] ->
+ do_openssh_decode(FileType, Lines,
+ [{ssh1_rsa_pubkey_decode(Modulus, Exponent),
+ [{comment, []},
+ {hostnames, comma_list_decode(HostNames)},
+ {bits, integer_decode(Bits)}]} | Acc])
+ end;
+
+do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) ->
+ case binary:split(Line, <<" ">>, [global]) of
+ [KeyType, Base64Enc, Comment0] when KeyType == <<"ssh-rsa">>;
+ KeyType == <<"ssh-dss">> ->
+ Comment = string:strip(binary_to_list(Comment0), right, $\n),
+ do_openssh_decode(FileType, Lines,
+ [{openssh_pubkey_decode(KeyType, Base64Enc),
+ [{comment, Comment}]} | Acc])
+ end.
+
+
+openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) ->
+ <<?UINT32(StrLen), _:StrLen/binary,
+ ?UINT32(SizeE), E:SizeE/binary,
+ ?UINT32(SizeN), N:SizeN/binary>>
+ = base64:mime_decode(Base64Enc),
+ #'RSAPublicKey'{modulus = erlint(SizeN, N),
+ publicExponent = erlint(SizeE, E)};
+
+openssh_pubkey_decode(<<"ssh-dss">>, Base64Enc) ->
+ <<?UINT32(StrLen), _:StrLen/binary,
+ ?UINT32(SizeP), P:SizeP/binary,
+ ?UINT32(SizeQ), Q:SizeQ/binary,
+ ?UINT32(SizeG), G:SizeG/binary,
+ ?UINT32(SizeY), Y:SizeY/binary>>
+ = base64:mime_decode(Base64Enc),
+ {erlint(SizeY, Y),
+ #'Dss-Parms'{p = erlint(SizeP, P),
+ q = erlint(SizeQ, Q),
+ g = erlint(SizeG, G)}}.
+
+erlint(MPIntSize, MPIntValue) ->
+ Bits= MPIntSize * 8,
+ <<Integer:Bits/integer>> = MPIntValue,
+ Integer.
+
+ssh1_rsa_pubkey_decode(MBin, EBin) ->
+ #'RSAPublicKey'{modulus = integer_decode(MBin),
+ publicExponent = integer_decode(EBin)}.
+
+integer_decode(BinStr) ->
+ list_to_integer(binary_to_list(BinStr)).
+
+string_decode(BinStr) ->
+ binary_to_list(BinStr).
+
+unicode_decode(BinStr) ->
+ unicode:characters_to_list(BinStr).
+
+comma_list_decode(BinOpts) ->
+ CommaList = binary:split(BinOpts, <<",">>, [global]),
+ lists:map(fun(Item) ->
+ binary_to_list(Item)
+ end, CommaList).
+
+do_encode(rfc4716_public_key, Key, Attributes) ->
+ rfc4716_encode(Key, proplists:get_value(headers, Attributes, []), []);
+
+do_encode(Type, Key, Attributes) ->
+ openssh_encode(Type, Key, Attributes).
+
+rfc4716_encode(Key, [],[]) ->
+ erlang:iolist_to_binary([begin_marker(),"\n",
+ split_lines(base64:encode(ssh2_pubkey_encode(Key))),
+ "\n", end_marker(), "\n"]);
+rfc4716_encode(Key, [], [_|_] = Acc) ->
+ erlang:iolist_to_binary([begin_marker(), "\n",
+ lists:reverse(Acc),
+ split_lines(base64:encode(ssh2_pubkey_encode(Key))),
+ "\n", end_marker(), "\n"]);
+rfc4716_encode(Key, [ Header | Headers], Acc) ->
+ LinesStr = rfc4716_encode_header(Header),
+ rfc4716_encode(Key, Headers, [LinesStr | Acc]).
+
+rfc4716_encode_header({Tag, Value}) ->
+ TagLen = length(Tag),
+ ValueLen = length(Value),
+ case TagLen + 1 + ValueLen of
+ N when N > ?ENCODED_LINE_LENGTH ->
+ NumOfChars = ?ENCODED_LINE_LENGTH - (TagLen + 1),
+ {First, Rest} = lists:split(NumOfChars, Value),
+ [Tag,":" , First, [$\\], "\n", rfc4716_encode_value(Rest) , "\n"];
+ _ ->
+ [Tag, ":", Value, "\n"]
+ end.
+
+rfc4716_encode_value(Value) ->
+ case length(Value) of
+ N when N > ?ENCODED_LINE_LENGTH ->
+ {First, Rest} = lists:split(?ENCODED_LINE_LENGTH, Value),
+ [First, [$\\], "\n", rfc4716_encode_value(Rest)];
+ _ ->
+ Value
+ end.
+
+openssh_encode(openssh_public_key, Key, Attributes) ->
+ Comment = proplists:get_value(comment, Attributes),
+ Enc = base64:encode(ssh2_pubkey_encode(Key)),
+ erlang:iolist_to_binary([key_type(Key), " ", Enc, " ", Comment, "\n"]);
+
+openssh_encode(auth_keys, Key, Attributes) ->
+ Comment = proplists:get_value(comment, Attributes, ""),
+ Options = proplists:get_value(options, Attributes, undefined),
+ Bits = proplists:get_value(bits, Attributes, undefined),
+ case Bits of
+ undefined ->
+ openssh_ssh2_auth_keys_encode(Options, Key, Comment);
+ _ ->
+ openssh_ssh1_auth_keys_encode(Options, Bits, Key, Comment)
+ end;
+openssh_encode(known_hosts, Key, Attributes) ->
+ Comment = proplists:get_value(comment, Attributes, ""),
+ Hostnames = proplists:get_value(hostnames, Attributes),
+ Bits = proplists:get_value(bits, Attributes, undefined),
+ case Bits of
+ undefined ->
+ openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment);
+ _ ->
+ openssh_ssh1_known_hosts_encode(Hostnames, Bits, Key, Comment)
+ end.
+
+openssh_ssh2_auth_keys_encode(undefined, Key, Comment) ->
+ erlang:iolist_to_binary([key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]);
+openssh_ssh2_auth_keys_encode(Options, Key, Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Options, []), " ",
+ key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]).
+
+openssh_ssh1_auth_keys_encode(undefined, Bits,
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ erlang:iolist_to_binary([integer_to_list(Bits), " ", integer_to_list(E), " ", integer_to_list(N),
+ line_end(Comment)]);
+openssh_ssh1_auth_keys_encode(Options, Bits,
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Options, []), " ", integer_to_list(Bits),
+ " ", integer_to_list(E), " ", integer_to_list(N), line_end(Comment)]).
+
+openssh_ssh2_know_hosts_encode(Hostnames, Key, Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Hostnames, []), " ",
+ key_type(Key)," ", base64:encode(ssh2_pubkey_encode(Key)), line_end(Comment)]).
+
+openssh_ssh1_known_hosts_encode(Hostnames, Bits,
+ #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Comment) ->
+ erlang:iolist_to_binary([comma_list_encode(Hostnames, [])," ", integer_to_list(Bits)," ",
+ integer_to_list(E)," ", integer_to_list(N), line_end(Comment)]).
+
+line_end("") ->
+ "\n";
+line_end(Comment) ->
+ [" ", Comment, "\n"].
+
+key_type(#'RSAPublicKey'{}) ->
+ <<"ssh-rsa">>;
+key_type({_, #'Dss-Parms'{}}) ->
+ <<"ssh-dss">>.
+
+comma_list_encode([Option], []) ->
+ Option;
+comma_list_encode([Option], Acc) ->
+ Acc ++ "," ++ Option;
+comma_list_encode([Option | Rest], []) ->
+ comma_list_encode(Rest, Option);
+comma_list_encode([Option | Rest], Acc) ->
+ comma_list_encode(Rest, Acc ++ "," ++ Option).
+
+ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
+ TypeStr = <<"ssh-rsa">>,
+ StrLen = size(TypeStr),
+ EBin = crypto:mpint(E),
+ NBin = crypto:mpint(N),
+ <<?UINT32(StrLen), TypeStr:StrLen/binary,
+ EBin/binary,
+ NBin/binary>>;
+ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
+ TypeStr = <<"ssh-dss">>,
+ StrLen = size(TypeStr),
+ PBin = crypto:mpint(P),
+ QBin = crypto:mpint(Q),
+ GBin = crypto:mpint(G),
+ YBin = crypto:mpint(Y),
+ <<?UINT32(StrLen), TypeStr:StrLen/binary,
+ PBin/binary,
+ QBin/binary,
+ GBin/binary,
+ YBin/binary>>.
+
+mend_split([Part1, Part2 | Rest] = List, Acc) ->
+ case option_end(Part1, Part2) of
+ true ->
+ lists:reverse(Acc) ++ List;
+ false ->
+ case length(binary:matches(Part1, <<"\"">>)) of
+ N when N rem 2 == 0 ->
+ mend_split(Rest, [Part1 | Acc]);
+ _ ->
+ mend_split([<<Part1/binary, Part2/binary>> | Rest], Acc)
+ end
+ end.
+
+option_end(Part1, Part2) ->
+ (is_key_field(Part1) orelse is_bits_field(Part1))
+ orelse
+ (is_key_field(Part2) orelse is_bits_field(Part2)).
+
+is_key_field(<<"ssh-dss">>) ->
+ true;
+is_key_field(<<"ssh-rsa">>) ->
+ true;
+is_key_field(_) ->
+ false.
+
+is_bits_field(Part) ->
+ try list_to_integer(binary_to_list(Part)) of
+ _ ->
+ true
+ catch _:_ ->
+ false
+ end.
+
+split_lines(<<Text:?ENCODED_LINE_LENGTH/binary>>) ->
+ [Text];
+split_lines(<<Text:?ENCODED_LINE_LENGTH/binary, Rest/binary>>) ->
+ [Text, $\n | split_lines(Rest)];
+split_lines(Bin) ->
+ [Bin].
diff --git a/lib/public_key/src/public_key.app.src b/lib/public_key/src/public_key.app.src
index edede7c874..1963bd05d4 100644
--- a/lib/public_key/src/public_key.app.src
+++ b/lib/public_key/src/public_key.app.src
@@ -1,10 +1,9 @@
{application, public_key,
[{description, "Public key infrastructure"},
{vsn, "%VSN%"},
- {modules, [
- public_key,
- pubkey_pem,
- pubkey_crypto,
+ {modules, [ public_key,
+ pubkey_pem,
+ pubkey_ssh,
pubkey_cert,
pubkey_cert_records,
'OTP-PUB-KEY'
@@ -13,4 +12,5 @@
{registered, []},
{env, []}
]
-}. \ No newline at end of file
+}.
+
diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src
index 46e5ecca33..c65ac7bc99 100644
--- a/lib/public_key/src/public_key.appup.src
+++ b/lib/public_key/src/public_key.appup.src
@@ -1,39 +1,49 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"0.5",
+ {"0.10",
[
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []}
+ ]
+ },
+ {"0.9",
+ [
+ {update, public_key, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
},
- {"0.4",
+ {"0.8",
[
+ {update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []},
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
- ]
+ ]
}
],
[
- {"0.5",
+ {"0.10",
[
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []}
+ ]
+ },
+ {"0.9",
+ [
+ {update, public_key, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
},
- {"0.4",
+ {"0.8",
[
+ {update, 'OTP-PUB-KEY', soft, soft_purge, soft_purge, []},
{update, public_key, soft, soft_purge, soft_purge, []},
- {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
- {update, pubkey_crypto, soft, soft_purge, soft_purge, []},
{update, pubkey_pem, soft, soft_purge, soft_purge, []},
+ {update, pubkey_cert_records, soft, soft_purge, soft_purge, []},
{update, pubkey_cert, soft, soft_purge, soft_purge, []}
]
}
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 157e76bb21..2901020e83 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,240 +23,423 @@
-include("public_key.hrl").
--export([decode_private_key/1, decode_private_key/2, decode_dhparams/1,
- decrypt_private/2, decrypt_private/3, encrypt_public/2,
- encrypt_public/3, decrypt_public/2, decrypt_public/3,
- encrypt_private/2, encrypt_private/3, gen_key/1, sign/2, sign/3,
- verify_signature/3, verify_signature/4, verify_signature/5,
- pem_to_der/1, pem_to_der/2, der_to_pem/2,
- pkix_decode_cert/2, pkix_encode_cert/1, pkix_transform/2,
- pkix_is_self_signed/1, pkix_is_fixed_dh_cert/1,
+-export([pem_decode/1, pem_encode/1,
+ der_decode/2, der_encode/2,
+ pem_entry_decode/1,
+ pem_entry_decode/2,
+ pem_entry_encode/2,
+ pem_entry_encode/3,
+ pkix_decode_cert/2, pkix_encode/3,
+ encrypt_private/2, encrypt_private/3,
+ decrypt_private/2, decrypt_private/3,
+ encrypt_public/2, encrypt_public/3,
+ decrypt_public/2, decrypt_public/3,
+ sign/3, verify/4,
+ pkix_sign/2, pkix_verify/2,
+ pkix_is_self_signed/1,
+ pkix_is_fixed_dh_cert/1,
+ pkix_is_issuer/2,
pkix_issuer_id/2,
- pkix_is_issuer/2, pkix_normalize_general_name/1,
- pkix_path_validation/3
+ pkix_normalize_name/1,
+ pkix_path_validation/3,
+ ssh_decode/2, ssh_encode/2
]).
+%% Deprecated
+-export([decode_private_key/1, decode_private_key/2, pem_to_der/1]).
+
+-deprecated({pem_to_der, 1, next_major_release}).
+-deprecated({decode_private_key, 1, next_major_release}).
+-deprecated({decode_private_key, 2, next_major_release}).
+
+-type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding'
+ | 'rsa_no_padding'.
+-type public_crypt_options() :: [{rsa_pad, rsa_padding()}].
+-type rsa_digest_type() :: 'md5' | 'sha'.
+-type dss_digest_type() :: 'none' | 'sha'.
+
+-define(UINT32(X), X:32/unsigned-big-integer).
+-define(DER_NULL, <<5, 0>>).
+
%%====================================================================
%% API
%%====================================================================
-
%%--------------------------------------------------------------------
-%% Function: decode_private_key(KeyInfo [,Password]) ->
-%% {ok, PrivateKey} | {error, Reason}
+-spec pem_decode(binary()) -> [pem_entry()].
%%
-%% KeyInfo = {Type, der_bin(), ChipherInfo} - as returned from
-%% pem_to_der/[1,2] for private keys
-%% Type = rsa_private_key | dsa_private_key
-%% ChipherInfo = opaque() | no_encryption
-%%
-%% Description: Decodes an asn1 der encoded private key.
+%% Description: Decode PEM binary data and return
+%% entries as asn1 der encoded entities.
%%--------------------------------------------------------------------
-decode_private_key(KeyInfo) ->
- decode_private_key(KeyInfo, no_passwd).
-
-decode_private_key(KeyInfo = {rsa_private_key, _, _}, Password) ->
- DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
- 'OTP-PUB-KEY':decode('RSAPrivateKey', DerEncoded);
-decode_private_key(KeyInfo = {dsa_private_key, _, _}, Password) ->
- DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
- 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded).
-
+pem_decode(PemBin) when is_binary(PemBin) ->
+ pubkey_pem:decode(PemBin).
%%--------------------------------------------------------------------
-%% Function: decode_dhparams(DhParamInfo) ->
-%% {ok, DhParams} | {error, Reason}
-%%
-%% DhParamsInfo = {Type, der_bin(), ChipherInfo} - as returned from
-%% pem_to_der/[1,2] for DH parameters.
-%% Type = dh_params
-%% ChipherInfo = opaque() | no_encryption
+-spec pem_encode([pem_entry()]) -> binary().
%%
-%% Description: Decodes an asn1 der encoded DH parameters.
+%% Description: Creates a PEM binary.
%%--------------------------------------------------------------------
-decode_dhparams({dh_params, DerEncoded, not_encrypted}) ->
- 'OTP-PUB-KEY':decode('DHParameter', DerEncoded).
+pem_encode(PemEntries) when is_list(PemEntries) ->
+ iolist_to_binary(pubkey_pem:encode(PemEntries)).
%%--------------------------------------------------------------------
-%% Function: decrypt_private(CipherText, Key) ->
-%% decrypt_private(CipherText, Key, Options) -> PlainTex
-%% decrypt_public(CipherText, Key) ->
-%% decrypt_public(CipherText, Key, Options) -> PlainTex
-%%
-%% CipherText = binary()
-%% Key = rsa_key()
-%% PlainText = binary()
-%%
-%% Description: Decrypts <CipherText>.
+-spec pem_entry_decode(pem_entry(), [string()]) -> term().
+%
+%% Description: Decodes a pem entry. pem_decode/1 returns a list of
+%% pem entries.
+%%--------------------------------------------------------------------
+pem_entry_decode({'SubjectPublicKeyInfo', Der, _}) ->
+ {_, {'AlgorithmIdentifier', AlgId, Params}, {0, Key0}}
+ = der_decode('SubjectPublicKeyInfo', Der),
+ KeyType = pubkey_cert_records:supportedPublicKeyAlgorithms(AlgId),
+ case KeyType of
+ 'RSAPublicKey' ->
+ der_decode(KeyType, Key0);
+ 'DSAPublicKey' ->
+ {params, DssParams} = der_decode('DSAParams', Params),
+ {der_decode(KeyType, Key0), DssParams}
+ end;
+pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type),
+ is_binary(Der) ->
+ der_decode(Asn1Type, Der).
+pem_entry_decode({Asn1Type, Der, not_encrypted}, _) when is_atom(Asn1Type),
+ is_binary(Der) ->
+ der_decode(Asn1Type, Der);
+pem_entry_decode({Asn1Type, CryptDer, {Cipher, Salt}} = PemEntry,
+ Password) when is_atom(Asn1Type),
+ is_binary(CryptDer),
+ is_list(Cipher),
+ is_binary(Salt),
+ erlang:byte_size(Salt) == 8
+ ->
+ Der = pubkey_pem:decipher(PemEntry, Password),
+ der_decode(Asn1Type, Der).
+
+%%--------------------------------------------------------------------
+-spec pem_entry_encode(pki_asn1_type(), term()) -> pem_entry().
+-spec pem_entry_encode(pki_asn1_type(), term(),
+ {{Cipher :: string(), Salt :: binary()}, string()}) ->
+ pem_entry().
+%
+%% Description: Creates a pem entry that can be feed to pem_encode/1.
+%%--------------------------------------------------------------------
+pem_entry_encode('SubjectPublicKeyInfo', Entity=#'RSAPublicKey'{}) ->
+ Der = der_encode('RSAPublicKey', Entity),
+ Spki = {'SubjectPublicKeyInfo',
+ {'AlgorithmIdentifier', ?'rsaEncryption', ?DER_NULL}, {0, Der}},
+ pem_entry_encode('SubjectPublicKeyInfo', Spki);
+pem_entry_encode('SubjectPublicKeyInfo',
+ {DsaInt, Params=#'Dss-Parms'{}}) when is_integer(DsaInt) ->
+ KeyDer = der_encode('DSAPublicKey', DsaInt),
+ ParamDer = der_encode('DSAParams', {params, Params}),
+ Spki = {'SubjectPublicKeyInfo',
+ {'AlgorithmIdentifier', ?'id-dsa', ParamDer}, {0, KeyDer}},
+ pem_entry_encode('SubjectPublicKeyInfo', Spki);
+pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) ->
+ Der = der_encode(Asn1Type, Entity),
+ {Asn1Type, Der, not_encrypted}.
+pem_entry_encode(Asn1Type, Entity,
+ {{Cipher, Salt}= CipherInfo, Password}) when is_atom(Asn1Type),
+ is_list(Cipher),
+ is_binary(Salt),
+ erlang:byte_size(Salt) == 8,
+ is_list(Password)->
+ Der = der_encode(Asn1Type, Entity),
+ DecryptDer = pubkey_pem:cipher(Der, CipherInfo, Password),
+ {Asn1Type, DecryptDer, CipherInfo}.
+
+%%--------------------------------------------------------------------
+-spec der_decode(asn1_type(), Der::binary()) -> term().
+%%
+%% Description: Decodes a public key asn1 der encoded entity.
+%%--------------------------------------------------------------------
+der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) ->
+ try
+ {ok, Decoded} = 'OTP-PUB-KEY':decode(Asn1Type, Der),
+ Decoded
+ catch
+ error:{badmatch, {error, _}} = Error ->
+ erlang:error(Error)
+ end.
+
+%%--------------------------------------------------------------------
+-spec der_encode(asn1_type(), term()) -> Der::binary().
+%%
+%% Description: Encodes a public key entity with asn1 DER encoding.
+%%--------------------------------------------------------------------
+der_encode(Asn1Type, Entity) when is_atom(Asn1Type) ->
+ try
+ {ok, Encoded} = 'OTP-PUB-KEY':encode(Asn1Type, Entity),
+ iolist_to_binary(Encoded)
+ catch
+ error:{badmatch, {error, _}} = Error ->
+ erlang:error(Error)
+ end.
+
+%%--------------------------------------------------------------------
+-spec pkix_decode_cert(Cert::binary(), plain | otp) ->
+ #'Certificate'{} | #'OTPCertificate'{}.
+%%
+%% Description: Decodes an asn1 der encoded pkix certificate. The otp
+%% option will use the customized asn1 specification OTP-PKIX.asn1 for
+%% decoding and also recursively decode most of the standard
+%% extensions.
+%% --------------------------------------------------------------------
+pkix_decode_cert(DerCert, plain) when is_binary(DerCert) ->
+ der_decode('Certificate', DerCert);
+pkix_decode_cert(DerCert, otp) when is_binary(DerCert) ->
+ try
+ {ok, #'OTPCertificate'{}= Cert} =
+ pubkey_cert_records:decode_cert(DerCert),
+ Cert
+ catch
+ error:{badmatch, {error, _}} = Error ->
+ erlang:error(Error)
+ end.
+
+%%--------------------------------------------------------------------
+-spec pkix_encode(asn1_type(), term(), otp | plain) -> Der::binary().
+%%
+%% Description: Der encodes a certificate or part of a certificate.
+%% This function must be used for encoding certificates or parts of certificates
+%% that are decoded with the otp format, whereas for the plain format this
+%% function will only call der_encode/2.
+%%--------------------------------------------------------------------
+pkix_encode(Asn1Type, Term, plain) when is_atom(Asn1Type) ->
+ der_encode(Asn1Type, Term);
+
+pkix_encode(Asn1Type, Term0, otp) when is_atom(Asn1Type) ->
+ Term = pubkey_cert_records:transform(Term0, encode),
+ der_encode(Asn1Type, Term).
+
+%%--------------------------------------------------------------------
+-spec decrypt_private(CipherText :: binary(), rsa_private_key()) ->
+ PlainText :: binary().
+-spec decrypt_private(CipherText :: binary(), rsa_private_key(),
+ public_crypt_options()) -> PlainText :: binary().
+%%
+%% Description: Public key decryption using the private key.
%%--------------------------------------------------------------------
decrypt_private(CipherText, Key) ->
decrypt_private(CipherText, Key, []).
-decrypt_private(CipherText, Key, Options) ->
- Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
- pubkey_crypto:decrypt_private(CipherText, Key, Padding).
-decrypt_public(CipherText, Key) ->
- decrypt_public(CipherText, Key, []).
-decrypt_public(CipherText, Key, Options) ->
+decrypt_private(CipherText,
+ #'RSAPrivateKey'{modulus = N,publicExponent = E,
+ privateExponent = D},
+ Options) when is_binary(CipherText),
+ is_list(Options) ->
Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
- pubkey_crypto:decrypt_public(CipherText, Key, Padding).
+ crypto:rsa_private_decrypt(CipherText,
+ [crypto:mpint(E), crypto:mpint(N),
+ crypto:mpint(D)], Padding).
%%--------------------------------------------------------------------
-%% Function: encrypt_public(PlainText, Key, Options) -> CipherText
-%% encrypt_private(PlainText, Key, Options) -> CipherText
-%%
-%% PlainText = iolist()
-%% Key = rsa_private_key()
-%% CipherText = binary()
+-spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key()) ->
+ PlainText :: binary().
+-spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key(),
+ public_crypt_options()) -> PlainText :: binary().
+%% NOTE: The rsa_private_key() is not part of the documented API it is
+%% here for testing purposes, in a real situation this is not a relevant
+%% thing to do.
%%
-%% Description: Encrypts <Plain>
+%% Description: Public key decryption using the public key.
%%--------------------------------------------------------------------
-encrypt_public(PlainText, Key) ->
- encrypt_public(PlainText, Key, []).
-encrypt_public(PlainText, Key, Options) ->
- Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_padding),
- pubkey_crypto:encrypt_public(PlainText, Key, Padding).
+decrypt_public(CipherText, Key) ->
+ decrypt_public(CipherText, Key, []).
-encrypt_private(PlainText, Key) ->
- encrypt_private(PlainText, Key, []).
-encrypt_private(PlainText, Key, Options) ->
- Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_padding),
- pubkey_crypto:encrypt_private(PlainText, Key, Padding).
+decrypt_public(CipherText, #'RSAPublicKey'{modulus = N, publicExponent = E},
+ Options) when is_binary(CipherText), is_list(Options) ->
+ decrypt_public(CipherText, N,E, Options);
-%%--------------------------------------------------------------------
-%% Function: gen_key(Params) -> Keys
-%%
-%% Params = #'DomainParameters'{} - Currently only supported option
-%% Keys = {PublicDHKey = integer(), PrivateDHKey = integer()}
-%%
-%% Description: Generates keys. Currently supports Diffie-Hellman keys.
-%%--------------------------------------------------------------------
-gen_key(#'DHParameter'{prime = P, base = G}) when is_integer(P),
- is_integer(G) ->
- pubkey_crypto:gen_key(diffie_hellman, [P, G]).
+decrypt_public(CipherText,#'RSAPrivateKey'{modulus = N, publicExponent = E},
+ Options) when is_binary(CipherText), is_list(Options) ->
+ decrypt_public(CipherText, N,E, Options).
%%--------------------------------------------------------------------
-%% Function: pem_to_der(CertSource) ->
-%% pem_to_der(CertSource, Password) -> {ok, [Entry]} |
-%% {error, Reason}
-%%
-%% CertSource = File | CertData
-%% CertData = binary()
-%% File = path()
-%% Entry = {entry_type(), der_bin(), ChipherInfo}
-%% ChipherInfo = opague() | no_encryption
-%% der_bin() = binary()
-%% entry_type() = cert | cert_req | rsa_private_key | dsa_private_key
-%% dh_params
+-spec encrypt_public(PlainText :: binary(), rsa_public_key() | rsa_private_key()) ->
+ CipherText :: binary().
+-spec encrypt_public(PlainText :: binary(), rsa_public_key() | rsa_private_key(),
+ public_crypt_options()) -> CipherText :: binary().
+
+%% NOTE: The rsa_private_key() is not part of the documented API it is
+%% here for testing purposes, in a real situation this is not a relevant
+%% thing to do.
%%
-%% Description: decode PEM binary data or a PEM file and return
-%% entries as asn1 der encoded entities. Currently supported entry
-%% types are certificates, certificate requests, rsa private keys and
-%% dsa private keys. In the case of a key entry ChipherInfo will be
-%% private keys and Diffie Hellam parameters .In the case of a key
-%% entry ChipherInfo will be used by decode_private_key/2 if the key
-%% is protected by a password.
+%% Description: Public key encryption using the public key.
%%--------------------------------------------------------------------
-pem_to_der(CertSource) ->
- pem_to_der(CertSource, no_passwd).
+encrypt_public(PlainText, Key) ->
+ encrypt_public(PlainText, Key, []).
-pem_to_der(File, Password) when is_list(File) ->
- pubkey_pem:read_file(File, Password);
-pem_to_der(PemBin, Password) when is_binary(PemBin) ->
- pubkey_pem:decode(PemBin, Password).
+encrypt_public(PlainText, #'RSAPublicKey'{modulus=N,publicExponent=E},
+ Options) when is_binary(PlainText), is_list(Options) ->
+ encrypt_public(PlainText, N,E, Options);
-der_to_pem(File, TypeDerList) ->
- pubkey_pem:write_file(File, TypeDerList).
+encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E},
+ Options) when is_binary(PlainText), is_list(Options) ->
+ encrypt_public(PlainText, N,E, Options).
%%--------------------------------------------------------------------
-%% Function: pkix_decode_cert(BerCert, Type) -> {ok, Cert} | {error, Reason}
-%%
-%% BerCert = binary()
-%% Type = plain | otp
-%% Cert = certificate()
+-spec encrypt_private(PlainText :: binary(), rsa_private_key()) ->
+ CipherText :: binary().
+-spec encrypt_private(PlainText :: binary(), rsa_private_key(),
+ public_crypt_options()) -> CipherText :: binary().
%%
-%% Description: Decodes an asn1 ber encoded pkix certificate.
-%% otp - Uses OTP-PKIX.asn1 to decode known extensions and
-%% enhance the signature field in #'Certificate'{} and '#TBSCertificate'{}.
+%% Description: Public key encryption using the private key.
%%--------------------------------------------------------------------
-pkix_decode_cert(BinCert, Type) ->
- pubkey_cert_records:decode_cert(BinCert, Type).
+encrypt_private(PlainText, Key) ->
+ encrypt_private(PlainText, Key, []).
+
+encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N,
+ publicExponent = E,
+ privateExponent = D},
+ Options) when is_binary(PlainText), is_list(Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ crypto:rsa_private_encrypt(PlainText, [crypto:mpint(E),
+ crypto:mpint(N),
+ crypto:mpint(D)], Padding).
%%--------------------------------------------------------------------
-%% Function: pkix_encode_cert(Cert) -> {ok, binary()} | {error, Reason}
-%%
-%% Cert = #'Certificate'{}
+-spec sign(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
+ rsa_private_key() |
+ dsa_private_key()) -> Signature :: binary().
%%
-%% Description: Encodes a certificate record using asn1.
+%% Description: Create digital signature.
%%--------------------------------------------------------------------
-pkix_encode_cert(Cert) ->
- pubkey_cert_records:encode_cert(Cert).
+sign(PlainText, DigestType, #'RSAPrivateKey'{modulus = N, publicExponent = E,
+ privateExponent = D})
+ when is_binary(PlainText),
+ (DigestType == md5 orelse
+ DigestType == sha) ->
+ crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E),
+ crypto:mpint(N),
+ crypto:mpint(D)]);
+
+sign(Digest, none, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X})
+ when is_binary(Digest)->
+ crypto:dss_sign(none, Digest,
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(X)]);
+
+sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X})
+ when is_binary(PlainText) ->
+ crypto:dss_sign(sized_binary(PlainText),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(X)]).
+
+%%--------------------------------------------------------------------
+-spec verify(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(),
+ Signature :: binary(), rsa_public_key()
+ | dsa_public_key()) -> boolean().
+%%
+%% Description: Verifies a digital signature.
+%%--------------------------------------------------------------------
+verify(PlainText, DigestType, Signature,
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp})
+ when is_binary (PlainText), DigestType == sha; DigestType == md5 ->
+ crypto:rsa_verify(DigestType,
+ sized_binary(PlainText),
+ sized_binary(Signature),
+ [crypto:mpint(Exp), crypto:mpint(Mod)]);
+
+verify(Digest, none, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
+ when is_integer(Key), is_binary(Digest), is_binary(Signature) ->
+ crypto:dss_verify(none,
+ Digest,
+ sized_binary(Signature),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(Key)]);
+
+verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}})
+ when is_integer(Key), is_binary(PlainText), is_binary(Signature) ->
+ crypto:dss_verify(sized_binary(PlainText),
+ sized_binary(Signature),
+ [crypto:mpint(P), crypto:mpint(Q),
+ crypto:mpint(G), crypto:mpint(Key)]).
%%--------------------------------------------------------------------
-%% Function: pkix_transform(CertPart, Op) -> TransformedCertPart
-%%
-%% CertPart = pkix part data
-%% Op = encode | decode
+-spec pkix_sign(#'OTPTBSCertificate'{},
+ rsa_private_key() | dsa_private_key()) -> Der::binary().
%%
-%% Description: Transform parts of a pkix certificate between 'plain' format
-%% and the internal 'otp' format, see pkix_decode_cert/2.
-%% Decode transforms from 'plain' to 'otp' and encode from 'otp' to 'plain'
-%% format.
+%% Description: Sign a pkix x.509 certificate. Returns the corresponding
+%% der encoded 'Certificate'{}
%%--------------------------------------------------------------------
-pkix_transform(CertPart, Op) ->
- pubkey_cert_records:transform(CertPart, Op).
+pkix_sign(#'OTPTBSCertificate'{signature =
+ #'SignatureAlgorithm'{algorithm = Alg}
+ = SigAlg} = TBSCert, Key) ->
+
+ Msg = pkix_encode('OTPTBSCertificate', TBSCert, otp),
+ DigestType = pubkey_cert:digest_type(Alg),
+ Signature = sign(Msg, DigestType, Key),
+ Cert = #'OTPCertificate'{tbsCertificate= TBSCert,
+ signatureAlgorithm = SigAlg,
+ signature = {0, Signature}
+ },
+ pkix_encode('OTPCertificate', Cert, otp).
%%--------------------------------------------------------------------
-%% Function: pkix_path_validation(TrustedCert, CertChain, Options) ->
-%% {ok, {{algorithm(), public_key(), public_key_params()} policy_tree()}} |
-%% {error, Reason}
+-spec pkix_verify(Cert::binary(), rsa_public_key()|
+ dsa_public_key()) -> boolean().
%%
-%% Description: Performs a bacis path validation according to RFC 3280.
+%% Description: Verify pkix x.509 certificate signature.
%%--------------------------------------------------------------------
-pkix_path_validation(TrustedCert, CertChain, Options)
- when is_binary(TrustedCert) ->
- {ok, OtpCert} = pkix_decode_cert(TrustedCert, otp),
- pkix_path_validation(OtpCert, CertChain, Options);
+pkix_verify(DerCert, {Key, #'Dss-Parms'{}} = DSAKey)
+ when is_binary(DerCert), is_integer(Key) ->
+ {DigestType, PlainText, Signature} = pubkey_cert:verify_data(DerCert),
+ verify(PlainText, DigestType, Signature, DSAKey);
-pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
- when is_list(CertChain), is_list(Options) ->
- MaxPathDefault = length(CertChain),
- ValidationState = pubkey_cert:init_validation_state(TrustedCert,
- MaxPathDefault,
- Options),
- Fun = proplists:get_value(validate_extensions_fun, Options,
- fun(Extensions, State, _, AccError) ->
- {Extensions, State, AccError}
- end),
- Verify = proplists:get_value(verify, Options, true),
- path_validation(CertChain, ValidationState, Fun, Verify).
-%%--------------------------------------------------------------------
-%% Function: pkix_is_fixed_dh_cert(Cert) -> true | false
+pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey)
+ when is_binary(DerCert) ->
+ {DigestType, PlainText, Signature} = pubkey_cert:verify_data(DerCert),
+ verify(PlainText, DigestType, Signature, RSAKey).
+
+%%--------------------------------------------------------------------
+-spec pkix_is_issuer(Cert::binary()| #'OTPCertificate'{},
+ IssuerCert::binary()|
+ #'OTPCertificate'{}) -> boolean().
%%
-%% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert
+%% Description: Checks if <IssuerCert> issued <Cert>.
%%--------------------------------------------------------------------
-pkix_is_fixed_dh_cert(#'OTPCertificate'{} = OTPCert) ->
- pubkey_cert:is_fixed_dh_cert(OTPCert);
-pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
- pkix_is_fixed_dh_cert(OtpCert).
+pkix_is_issuer(Cert, IssuerCert) when is_binary(Cert) ->
+ OtpCert = pkix_decode_cert(Cert, otp),
+ pkix_is_issuer(OtpCert, IssuerCert);
+pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) ->
+ OtpIssuerCert = pkix_decode_cert(IssuerCert, otp),
+ pkix_is_issuer(Cert, OtpIssuerCert);
+pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
+ #'OTPCertificate'{tbsCertificate = Candidate}) ->
+ pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer,
+ Candidate#'OTPTBSCertificate'.subject).
%%--------------------------------------------------------------------
-%% Function: pkix_is_self_signed(Cert) -> true | false
+-spec pkix_is_self_signed(Cert::binary()| #'OTPCertificate'{}) -> boolean().
%%
%% Description: Checks if a Certificate is self signed.
%%--------------------------------------------------------------------
pkix_is_self_signed(#'OTPCertificate'{} = OTPCert) ->
pubkey_cert:is_self_signed(OTPCert);
pkix_is_self_signed(Cert) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ OtpCert = pkix_decode_cert(Cert, otp),
pkix_is_self_signed(OtpCert).
-
+
%%--------------------------------------------------------------------
-%% Function: pkix_issuer_id(Cert) -> {ok, {SerialNr, Issuer}} | {error, Reason}
-%%
-%% Cert = asn1_der_encoded() | 'OTPCertificate'{}
+-spec pkix_is_fixed_dh_cert(Cert::binary()| #'OTPCertificate'{}) -> boolean().
%%
-%% Description: Returns the issuer id.
+%% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert.
+%%--------------------------------------------------------------------
+pkix_is_fixed_dh_cert(#'OTPCertificate'{} = OTPCert) ->
+ pubkey_cert:is_fixed_dh_cert(OTPCert);
+pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) ->
+ OtpCert = pkix_decode_cert(Cert, otp),
+ pkix_is_fixed_dh_cert(OtpCert).
+
+%%--------------------------------------------------------------------
+-spec pkix_issuer_id(Cert::binary()| #'OTPCertificate'{},
+ IssuedBy :: self | other) ->
+ {ok, {SerialNr :: integer(),
+ Issuer :: {rdnSequence,
+ [#'AttributeTypeAndValue'{}]}}}
+ | {error, Reason :: term()}.
+%
+%% Description: Returns the issuer id.
%%--------------------------------------------------------------------
pkix_issuer_id(#'OTPCertificate'{} = OtpCert, self) ->
pubkey_cert:issuer_id(OtpCert, self);
@@ -265,151 +448,142 @@ pkix_issuer_id(#'OTPCertificate'{} = OtpCert, other) ->
pubkey_cert:issuer_id(OtpCert, other);
pkix_issuer_id(Cert, Signed) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ OtpCert = pkix_decode_cert(Cert, otp),
pkix_issuer_id(OtpCert, Signed).
%%--------------------------------------------------------------------
-%% Function: pkix_is_issuer(Cert, IssuerCert) -> true | false
+-spec pkix_normalize_name({rdnSequence,
+ [#'AttributeTypeAndValue'{}]}) ->
+ {rdnSequence,
+ [#'AttributeTypeAndValue'{}]}.
%%
-%% Cert = asn1_der_encoded() | 'OTPCertificate'{}
-%% IssuerCert = asn1_der_encoded() | 'OTPCertificate'{}
-%%
-%% Description: Checks if <IssuerCert> issued <Cert>.
+%% Description: Normalizes a issuer name so that it can be easily
+%% compared to another issuer name.
%%--------------------------------------------------------------------
-pkix_is_issuer(Cert, IssuerCert) when is_binary(Cert) ->
- {ok, OtpCert} = pkix_decode_cert(Cert, otp),
- pkix_is_issuer(OtpCert, IssuerCert);
+pkix_normalize_name(Issuer) ->
+ pubkey_cert:normalize_general_name(Issuer).
-pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) ->
- {ok, OtpIssuerCert} = pkix_decode_cert(IssuerCert, otp),
- pkix_is_issuer(Cert, OtpIssuerCert);
+%%--------------------------------------------------------------------
+-spec pkix_path_validation(Cert::binary()| #'OTPCertificate'{} | atom(),
+ CertChain :: [binary()] ,
+ Options :: list()) ->
+ {ok, {PublicKeyInfo :: term(),
+ PolicyTree :: term()}} |
+ {error, {bad_cert, Reason :: term()}}.
+%% Description: Performs a basic path validation according to RFC 5280.
+%%--------------------------------------------------------------------
+pkix_path_validation(PathErr, [Cert | Chain], Options0) when is_atom(PathErr)->
+ {VerifyFun, Userstat0} =
+ proplists:get_value(verify_fun, Options0, ?DEFAULT_VERIFYFUN),
+ Otpcert = pkix_decode_cert(Cert, otp),
+ Reason = {bad_cert, PathErr},
+ try VerifyFun(Otpcert, Reason, Userstat0) of
+ {valid, Userstate} ->
+ Options = proplists:delete(verify_fun, Options0),
+ pkix_path_validation(Otpcert, Chain, [{verify_fun,
+ {VerifyFun, Userstate}}| Options]);
+ {fail, _} ->
+ {error, Reason}
+ catch
+ _:_ ->
+ {error, Reason}
+ end;
+pkix_path_validation(TrustedCert, CertChain, Options) when
+ is_binary(TrustedCert) -> OtpCert = pkix_decode_cert(TrustedCert,
+ otp), pkix_path_validation(OtpCert, CertChain, Options);
-pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
- #'OTPCertificate'{tbsCertificate = Candidate}) ->
- pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer,
- Candidate#'OTPTBSCertificate'.subject).
-
-%%--------------------------------------------------------------------
-%% Function: pkix_normalize_general_name(Issuer) ->
-%%
-%% Issuer = general_name() - see PKIX
-%%
-%% Description: Normalizes a general name so that it can be easily
-%% compared to another genral name.
-%%--------------------------------------------------------------------
-pkix_normalize_general_name(Issuer) ->
- pubkey_cert:normalize_general_name(Issuer).
+pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
+ when is_list(CertChain), is_list(Options) ->
+ MaxPathDefault = length(CertChain),
+ ValidationState = pubkey_cert:init_validation_state(TrustedCert,
+ MaxPathDefault,
+ Options),
+ path_validation(CertChain, ValidationState).
%%--------------------------------------------------------------------
-%% Function:sign(Msg, Key) -> {ok, Signature}
-%% sign(Msg, Key, KeyParams) -> {ok, Signature}
-%%
-%% Msg = binary() | #'TBSCertificate'{}
-%% Key = private_key()
-%% KeyParams = key_params()
-%% Signature = binary()
+-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
%%
-%% Description: Signs plaintext Msg or #TBSCertificate{}, in the later
-%% case a der encoded "#Certificate{}" will be returned.
+%% Description: Decodes a ssh file-binary. In the case of know_hosts
+%% or auth_keys the binary may include one or more lines of the
+%% file. Returns a list of public keys and their attributes, possible
+%% attribute values depends on the file type represented by the
+%% binary.
%%--------------------------------------------------------------------
-sign(Msg, #'RSAPrivateKey'{} = Key) when is_binary(Msg) ->
- pubkey_crypto:sign(Msg, Key);
-
-sign(Msg, #'DSAPrivateKey'{} = Key) when is_binary(Msg) ->
- pubkey_crypto:sign(Msg, Key);
-
-sign(#'OTPTBSCertificate'{signature = #'SignatureAlgorithm'{algorithm = Alg}
- = SigAlg} = TBSCert, Key) ->
- Msg = pubkey_cert_records:encode_tbs_cert(TBSCert),
- DigestType = pubkey_cert:digest_type(Alg),
- Signature = pubkey_crypto:sign(DigestType, Msg, Key),
- Cert = #'OTPCertificate'{tbsCertificate= TBSCert,
- signatureAlgorithm = SigAlg,
- signature = {0, Signature}
- },
- pkix_encode_cert(Cert).
-
-sign(DigestType, Msg, Key) ->
- pubkey_crypto:sign(DigestType, Msg, Key).
+ssh_decode(SshBin, Type) when is_binary(SshBin),
+ Type == public_key;
+ Type == rfc4716_public_key;
+ Type == openssh_public_key;
+ Type == auth_keys;
+ Type == known_hosts ->
+ pubkey_ssh:decode(SshBin, Type).
%%--------------------------------------------------------------------
-%% Function: verify_signature(PlainText, DigestType, Signature, Key) ->
-%% verify_signature(PlainText, DigestType,
-%% Signature, Key, KeyParams) ->
-%% verify_signature(DerCert, Key, KeyParams) ->
-%%
-%% PlainText = binary()
-%% DigestType = md5 | sha
-%% DerCert = asn1_der_encoded()
-%% Signature = binary()
-%% Key = public_key()
-%% KeyParams = key_params()
-%% Verified = boolean()
-%%
-%% Description: Verifies the signature <Signature>.
-%%--------------------------------------------------------------------
-verify_signature(PlainText, DigestType, Signature, #'RSAPublicKey'{} = Key)
- when is_binary(PlainText), is_binary(Signature), DigestType == sha;
- DigestType == md5 ->
- pubkey_crypto:verify(DigestType, PlainText, Signature, Key, undefined).
-
-verify_signature(PlainText, DigestType, Signature, #'RSAPublicKey'{} = Key,
- KeyParams)
- when is_binary(PlainText), is_binary(Signature), DigestType == sha;
- DigestType == md5 ->
- pubkey_crypto:verify(DigestType, PlainText, Signature, Key, KeyParams);
-verify_signature(PlainText, sha, Signature, Key, #'Dss-Parms'{} = KeyParams)
- when is_binary(PlainText), is_binary(Signature), is_integer(Key) ->
- pubkey_crypto:verify(sha, PlainText, Signature, Key, KeyParams).
-
-verify_signature(DerCert, Key, #'Dss-Parms'{} = KeyParams)
- when is_binary(DerCert), is_integer(Key) ->
- pubkey_cert:verify_signature(DerCert, Key, KeyParams);
-verify_signature(DerCert, #'RSAPublicKey'{} = Key, KeyParams)
- when is_binary(DerCert) ->
- pubkey_cert:verify_signature(DerCert, Key, KeyParams).
+-spec ssh_encode([{public_key(), Attributes::list()}], ssh_file()) ->
+ binary().
+%% Description: Encodes a list of ssh file entries (public keys and
+%% attributes) to a binary. Possible attributes depends on the file
+%% type.
+%%--------------------------------------------------------------------
+ssh_encode(Entries, Type) when is_list(Entries),
+ Type == rfc4716_public_key;
+ Type == openssh_public_key;
+ Type == auth_keys;
+ Type == known_hosts ->
+ pubkey_ssh:encode(Entries, Type).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+encrypt_public(PlainText, N, E, Options)->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ crypto:rsa_public_encrypt(PlainText, [crypto:mpint(E),crypto:mpint(N)],
+ Padding).
+
+decrypt_public(CipherText, N,E, Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ crypto:rsa_public_decrypt(CipherText,[crypto:mpint(E), crypto:mpint(N)],
+ Padding).
+
path_validation([], #path_validation_state{working_public_key_algorithm
= Algorithm,
working_public_key =
PublicKey,
working_public_key_parameters
= PublicKeyParams,
- valid_policy_tree = Tree,
- acc_errors = AccErrors
- }, _, _) ->
- {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree, AccErrors}};
+ valid_policy_tree = Tree
+ }) ->
+ {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree}};
path_validation([DerCert | Rest], ValidationState = #path_validation_state{
- max_path_length = Len},
- Fun, Verify) when Len >= 0 ->
- try validate(DerCert,
- ValidationState#path_validation_state{last_cert=Rest=:=[]},
- Fun, Verify) of
+ max_path_length = Len}) when Len >= 0 ->
+ try validate(DerCert,
+ ValidationState#path_validation_state{last_cert=Rest=:=[]}) of
#path_validation_state{} = NewValidationState ->
- path_validation(Rest, NewValidationState, Fun, Verify)
+ path_validation(Rest, NewValidationState)
catch
throw:Reason ->
{error, Reason}
end;
-path_validation(_, _, _, true) ->
- {error, {bad_cert, max_path_length_reached}};
-
-path_validation(_, #path_validation_state{working_public_key_algorithm
- = Algorithm,
- working_public_key =
- PublicKey,
- working_public_key_parameters
- = PublicKeyParams,
- valid_policy_tree = Tree,
- acc_errors = AccErrors
- }, _, false) ->
- {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree,
- [{bad_cert, max_path_length_reached}|AccErrors]}}.
+path_validation([DerCert | _] = Path,
+ #path_validation_state{user_state = UserState0,
+ verify_fun = VerifyFun} =
+ ValidationState) ->
+ Reason = {bad_cert, max_path_length_reached},
+ OtpCert = pkix_decode_cert(DerCert, otp),
+ try VerifyFun(OtpCert, Reason, UserState0) of
+ {valid, UserState} ->
+ path_validation(Path,
+ ValidationState#path_validation_state{
+ max_path_length = 0,
+ user_state = UserState});
+ {fail, _} ->
+ {error, Reason}
+ catch
+ _:_ ->
+ {error, Reason}
+ end.
validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
working_public_key = Key,
@@ -419,38 +593,58 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
excluded_subtrees = Exclude,
last_cert = Last,
user_state = UserState0,
- acc_errors = AccErr0} =
- ValidationState0, ValidateExtensionFun, Verify) ->
- {ok, OtpCert} = pkix_decode_cert(DerCert, otp),
- %% All validate functions will throw {bad_cert, Reason} if they
- %% fail and Verify = true if Verify = false errors
- %% will be accumulated in the validationstate
- AccErr1 = pubkey_cert:validate_time(OtpCert, AccErr0, Verify),
-
- AccErr2 = pubkey_cert:validate_issuer(OtpCert, Issuer, AccErr1, Verify),
-
- AccErr3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
- AccErr2, Verify),
- AccErr4 =
- pubkey_cert:validate_revoked_status(OtpCert, Verify, AccErr3),
+ verify_fun = VerifyFun} =
+ ValidationState0) ->
+
+ OtpCert = pkix_decode_cert(DerCert, otp),
+
+ UserState1 = pubkey_cert:validate_time(OtpCert, UserState0, VerifyFun),
+
+ UserState2 = pubkey_cert:validate_issuer(OtpCert, Issuer, UserState1, VerifyFun),
+
+ UserState3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
+ UserState2,VerifyFun),
+
+ UserState4 = pubkey_cert:validate_revoked_status(OtpCert, UserState3, VerifyFun),
- {ValidationState1, UnknownExtensions0, AccErr5} =
- pubkey_cert:validate_extensions(OtpCert, ValidationState0, Verify,
- AccErr4),
- %% We want the key_usage extension to be checked before we validate
+ {ValidationState1, UserState5} =
+ pubkey_cert:validate_extensions(OtpCert, ValidationState0, UserState4,
+ VerifyFun),
+
+ %% We want the key_usage extension to be checked before we validate
%% the signature.
- AccErr6 =
- pubkey_cert:validate_signature(OtpCert, DerCert, Key, KeyParams,
- AccErr5, Verify),
-
- {UnknownExtensions, UserState, AccErr7} =
- ValidateExtensionFun(UnknownExtensions0, UserState0, Verify, AccErr6),
-
- %% Check that all critical extensions have been handled
- AccErr =
- pubkey_cert:validate_unknown_extensions(UnknownExtensions, AccErr7,
- Verify),
+ UserState6 = pubkey_cert:validate_signature(OtpCert, DerCert,
+ Key, KeyParams, UserState5, VerifyFun),
+ UserState = case Last of
+ false ->
+ pubkey_cert:verify_fun(OtpCert, valid, UserState6, VerifyFun);
+ true ->
+ pubkey_cert:verify_fun(OtpCert, valid_peer,
+ UserState6, VerifyFun)
+ end,
+
ValidationState =
- ValidationState1#path_validation_state{user_state = UserState,
- acc_errors = AccErr},
+ ValidationState1#path_validation_state{user_state = UserState},
+
pubkey_cert:prepare_for_next_cert(OtpCert, ValidationState).
+
+sized_binary(Binary) ->
+ Size = size(Binary),
+ <<?UINT32(Size), Binary/binary>>.
+
+%%--------------------------------------------------------------------
+%%% Deprecated functions
+%%--------------------------------------------------------------------
+pem_to_der(CertSource) ->
+ {ok, Bin} = file:read_file(CertSource),
+ {ok, pubkey_pem:decode(Bin)}.
+
+decode_private_key(KeyInfo) ->
+ decode_private_key(KeyInfo, no_passwd).
+
+decode_private_key(KeyInfo = {'RSAPrivateKey', _, _}, Password) ->
+ DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
+ 'OTP-PUB-KEY':decode('RSAPrivateKey', DerEncoded);
+decode_private_key(KeyInfo = {'DSAPrivateKey', _, _}, Password) ->
+ DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
+ 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded).
diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile
index c7215020c7..6889ae9a8a 100644
--- a/lib/public_key/test/Makefile
+++ b/lib/public_key/test/Makefile
@@ -28,6 +28,7 @@ INCLUDES= -I. -I ../include
# ----------------------------------------------------
MODULES= \
+ erl_make_certs \
public_key_SUITE \
pkits_SUITE
@@ -40,6 +41,9 @@ TARGET_FILES= \
SPEC_FILES = public_key.spec
+COVER_FILE = public_key.cover
+
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -74,9 +78,9 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(HRL_FILES)$(RELSYSDIR)
+ $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR)
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/public_key/test/erl_make_certs.erl b/lib/public_key/test/erl_make_certs.erl
new file mode 100644
index 0000000000..8b01ca3ad4
--- /dev/null
+++ b/lib/public_key/test/erl_make_certs.erl
@@ -0,0 +1,421 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Create test certificates
+
+-module(erl_make_certs).
+-include_lib("public_key/include/public_key.hrl").
+
+-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]).
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% @doc Create and return a der encoded certificate
+%% Option Default
+%% -------------------------------------------------------
+%% digest sha1
+%% validity {date(), date() + week()}
+%% version 3
+%% subject [] list of the following content
+%% {name, Name}
+%% {email, Email}
+%% {city, City}
+%% {state, State}
+%% {org, Org}
+%% {org_unit, OrgUnit}
+%% {country, Country}
+%% {serial, Serial}
+%% {title, Title}
+%% {dnQualifer, DnQ}
+%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created)
+%% (obs IssuerKey migth be {Key, Password}
+%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key
+%%
+%%
+%% (OBS: The generated keys are for testing only)
+%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
+%% @end
+%%--------------------------------------------------------------------
+
+make_cert(Opts) ->
+ SubjectPrivateKey = get_key(Opts),
+ {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
+ Cert = public_key:pkix_sign(TBSCert, IssuerKey),
+ true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
+ {Cert, encode_key(SubjectPrivateKey)}.
+
+%%--------------------------------------------------------------------
+%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
+%% @spec (::string(), ::string(), {Cert,Key}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
+ ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
+ [{'Certificate', Cert, not_encrypted}]),
+ ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
+
+%%--------------------------------------------------------------------
+%% @doc Creates a rsa key (OBS: for testing only)
+%% the size are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_rsa(Size) when is_integer(Size) ->
+ Key = gen_rsa2(Size),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Verifies cert signatures
+%% @spec (::binary(), ::tuple()) -> ::boolean()
+%% @end
+%%--------------------------------------------------------------------
+verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
+ Key = decode_key(DerKey),
+ case Key of
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
+ public_key:pkix_verify(DerEncodedCert,
+ #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
+ public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_key(Opts) ->
+ case proplists:get_value(key, Opts) of
+ undefined -> make_key(rsa, Opts);
+ rsa -> make_key(rsa, Opts);
+ dsa -> make_key(dsa, Opts);
+ Key ->
+ Password = proplists:get_value(password, Opts, no_passwd),
+ decode_key(Key, Password)
+ end.
+
+decode_key({Key, Pw}) ->
+ decode_key(Key, Pw);
+decode_key(Key) ->
+ decode_key(Key, no_passwd).
+
+
+decode_key(#'RSAPublicKey'{} = Key,_) ->
+ Key;
+decode_key(#'RSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'DSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(PemEntry = {_,_,_}, Pw) ->
+ public_key:pem_entry_decode(PemEntry, Pw);
+decode_key(PemBin, Pw) ->
+ [KeyInfo] = public_key:pem_decode(PemBin),
+ decode_key(KeyInfo, Pw).
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+
+make_tbs(SubjectKey, Opts) ->
+ Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
+
+ IssuerProp = proplists:get_value(issuer, Opts, true),
+ {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey),
+
+ {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
+
+ SignAlgo = #'SignatureAlgorithm'{algorithm = Algo,
+ parameters = Parameters},
+ Subject = case IssuerProp of
+ true -> %% Is a Root Ca
+ Issuer;
+ _ ->
+ subject(proplists:get_value(subject, Opts),false)
+ end,
+
+ {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
+ signature = SignAlgo,
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Subject,
+ subjectPublicKeyInfo = publickey(SubjectKey),
+ version = Version,
+ extensions = extensions(Opts)
+ }, IssuerKey}.
+
+issuer(true, Opts, SubjectKey) ->
+ %% Self signed
+ {subject(proplists:get_value(subject, Opts), true), SubjectKey};
+issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
+ {issuer_der(Issuer), decode_key(IssuerKey)};
+issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
+ {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File),
+ {issuer_der(Cert), decode_key(IssuerKey)}.
+
+issuer_der(Issuer) ->
+ Decoded = public_key:pkix_decode_cert(Issuer, otp),
+ #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
+ #'OTPTBSCertificate'{subject=Subject} = Tbs,
+ Subject.
+
+subject(undefined, IsRootCA) ->
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end,
+ Opts = [{email, User ++ "@erlang.org"},
+ {name, User},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+ subject(Opts);
+subject(Opts, _) ->
+ subject(Opts).
+
+subject(SubjectOpts) when is_list(SubjectOpts) ->
+ Encode = fun(Opt) ->
+ {Type,Value} = subject_enc(Opt),
+ [#'AttributeTypeAndValue'{type=Type, value=Value}]
+ end,
+ {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
+
+%% Fill in the blanks
+subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
+subject_enc({email, Email}) -> {?'id-emailAddress', Email};
+subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
+subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}};
+subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
+subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
+subject_enc({country, Country}) -> {?'id-at-countryName', Country};
+subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial};
+subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}};
+subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ};
+subject_enc(Other) -> Other.
+
+
+extensions(Opts) ->
+ case proplists:get_value(extensions, Opts, []) of
+ false ->
+ asn1_NOVALUE;
+ Exts ->
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
+ end.
+
+default_extensions(Exts) ->
+ Def = [{key_usage,undefined},
+ {subject_altname, undefined},
+ {issuer_altname, undefined},
+ {basic_constraints, default},
+ {name_constraints, undefined},
+ {policy_constraints, undefined},
+ {ext_key_usage, undefined},
+ {inhibit_any, undefined},
+ {auth_key_id, undefined},
+ {subject_key_id, undefined},
+ {policy_mapping, undefined}],
+ Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
+ Exts ++ lists:foldl(Filter, Def, Exts).
+
+extension({_, undefined}) -> [];
+extension({basic_constraints, Data}) ->
+ case Data of
+ default ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true},
+ critical=true};
+ false ->
+ [];
+ Len when is_integer(Len) ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
+ critical=true};
+ _ ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = Data}
+ end;
+extension({Id, Data, Critical}) ->
+ #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
+
+
+publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
+ Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = Public};
+publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
+ parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}.
+
+validity(Opts) ->
+ DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
+ DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
+ {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
+ Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
+ #'Validity'{notBefore={generalTime, Format(DefFrom)},
+ notAfter ={generalTime, Format(DefTo)}}.
+
+sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'sha1WithRSAEncryption';
+ sha512 -> ?'sha512WithRSAEncryption';
+ sha384 -> ?'sha384WithRSAEncryption';
+ sha256 -> ?'sha256WithRSAEncryption';
+ md5 -> ?'md5WithRSAEncryption';
+ md2 -> ?'md2WithRSAEncryption'
+ end,
+ {Type, 'NULL'};
+sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
+ {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}.
+
+make_key(rsa, _Opts) ->
+ %% (OBS: for testing only)
+ gen_rsa2(64);
+make_key(dsa, _Opts) ->
+ gen_dsa2(128, 20). %% Bytes i.e. {1024, 160}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% RSA key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
+ 47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
+
+gen_rsa2(Size) ->
+ P = prime(Size),
+ Q = prime(Size),
+ N = P*Q,
+ Tot = (P - 1) * (Q - 1),
+ [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
+ {D1,D2} = extended_gcd(E, Tot),
+ D = erlang:max(D1,D2),
+ case D < E of
+ true ->
+ gen_rsa2(Size);
+ false ->
+ {Co1,Co2} = extended_gcd(Q, P),
+ Co = erlang:max(Co1,Co2),
+ #'RSAPrivateKey'{version = 'two-prime',
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ exponent1 = D rem (P-1),
+ exponent2 = D rem (Q-1),
+ coefficient = Co
+ }
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(crypto:mpint(P), 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ crypto:erlint(prime_odd(Rand, 0)).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ NotPrime = crypto:erlint(Rand),
+ prime_odd(crypto:mpint(NotPrime+2), N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
+ case crypto:mod_exp(CoPrime, Candidate, Candidate) of
+ CoPrime -> is_prime(Candidate, Test-1);
+ _ -> false
+ end.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
+
+odd_rand(Min,Max) ->
+ Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
+ BitSkip = (Sz+4)*8-1,
+ case Rand of
+ Odd = <<_:BitSkip, 1:1>> -> Odd;
+ Even = <<_:BitSkip, 0:1>> ->
+ crypto:mpint(crypto:erlint(Even)+1)
+ end.
+
+extended_gcd(A, B) ->
+ case A rem B of
+ 0 ->
+ {0, 1};
+ N ->
+ {X, Y} = extended_gcd(B, N),
+ {Y, X-Y*(A div B)}
+ end.
+
+pem_to_der(File) ->
+ {ok, PemBin} = file:read_file(File),
+ public_key:pem_decode(PemBin).
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
diff --git a/lib/public_key/test/pkits_SUITE.erl b/lib/public_key/test/pkits_SUITE.erl
index 5d58b39e26..660af4e8ab 100644
--- a/lib/public_key/test/pkits_SUITE.erl
+++ b/lib/public_key/test/pkits_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,8 +25,8 @@
-compile(export_all).
-%%-include_lib("public_key/include/public_key.hrl").
--include("public_key.hrl").
+-include_lib("public_key/include/public_key.hrl").
+%%-include("public_key.hrl").
-define(error(Format,Args), error(Format,Args,?FILE,?LINE)).
-define(warning(Format,Args), warning(Format,Args,?FILE,?LINE)).
@@ -43,25 +43,24 @@
-define(NIST6, "2.16.840.1.101.3.2.1.48.6").
%%
-all(doc) ->
- ["PKITS tests for RFC3280 compliance"];
-all(suite) ->
- [signature_verification,
- validity_periods,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [signature_verification, validity_periods,
verifying_name_chaining,
- %% basic_certificate_revocation_tests,
verifying_paths_with_self_issued_certificates,
- verifying_basic_constraints,
- key_usage,
-%% certificate_policies,
-%% require_explicit_policy,
-%% policy_mappings,
-%% inhibit_policy_mapping,
-%% inhibit_any_policy,
- name_constraints,
-%% distribution_points,
-%% delta_crls,
- private_certificate_extensions].
+ verifying_basic_constraints, key_usage,
+ name_constraints, private_certificate_extensions].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
signature_verification(doc) -> [""];
signature_verification(suite) -> [];
@@ -129,7 +128,6 @@ private_certificate_extensions(Config) when is_list(Config) ->
run(private_certificate_extensions()).
run() ->
- catch crypto:start(),
Tests =
[signature_verification(),
validity_periods(),
@@ -187,9 +185,9 @@ run([],_) -> ok.
read_certs(Test) ->
File = test_file(Test),
%% io:format("Read ~p ",[File]),
- {ok, Ders} = public_key:pem_to_der(File),
+ Ders = erl_make_certs:pem_to_der(File),
%% io:format("Ders ~p ~n",[length(Ders)]),
- [Cert || {cert,Cert,not_encrypted} <- Ders].
+ [Cert || {'Certificate', Cert, not_encrypted} <- Ders].
test_file(Test) ->
file(?CONV, lists:append(string:tokens(Test, " -")) ++ ".pem").
@@ -581,17 +579,21 @@ init_per_testcase(_Func, Config) ->
put(datadir, Datadir),
Config.
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
%% Nodes = select_nodes(all, Config, ?FILE, ?LINE),
%% rpc:multicall(Nodes, mnesia, lkill, []),
Config.
init_per_suite(Config) ->
- crypto:start(),
- Config.
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
end_per_suite(_Config) ->
- crypto:stop().
+ application:stop(crypto).
error(Format, Args, File0, Line) ->
File = filename:basename(File0),
diff --git a/lib/public_key/test/public_key.cover b/lib/public_key/test/public_key.cover
new file mode 100644
index 0000000000..ec00814578
--- /dev/null
+++ b/lib/public_key/test/public_key.cover
@@ -0,0 +1,4 @@
+{incl_app,public_key,details}.
+
+
+{excl_mods, public_key, ['OTP-PUB-KEY']}.
diff --git a/lib/public_key/test/public_key.spec b/lib/public_key/test/public_key.spec
index dee9ad44ed..1749822c2d 100644
--- a/lib/public_key/test/public_key.spec
+++ b/lib/public_key/test/public_key.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../public_key_test"}}.
-
+{suites,"../public_key_test",all}.
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 8cc36e490d..b11e4d092a 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,10 +23,10 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include_lib("test_server/include/test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include_lib("test_server/include/test_server_line.hrl").
--include("public_key.hrl").
+-include_lib("public_key/include/public_key.hrl").
-define(TIMEOUT, 120000). % 2 min
@@ -41,9 +41,12 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- crypto:start(),
- Config.
-
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
%% Config - [tuple()]
@@ -51,7 +54,7 @@ init_per_suite(Config) ->
%% Description: Cleanup after the whole suite
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
- crypto:stop().
+ application:stop(crypto).
%%--------------------------------------------------------------------
%% Function: init_per_testcase(TestCase, Config) -> Config
@@ -96,21 +99,34 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test the public_key rsa functionality"];
-
-all(suite) ->
- [app,
- pem_to_der,
- decode_private_key
-%% encrypt_decrypt,
-%% rsa_verify
-%% dsa_verify_sign,
-%% pkix_encode_decode,
-%% pkix_verify_sign,
-%% pkix_path_validation
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app,
+ {group, pem_decode_encode},
+ {group, ssh_public_key_decode_encode},
+ encrypt_decrypt,
+ {group, sign_verify},
+ pkix, pkix_path_validation, deprecated].
+
+groups() ->
+ [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem,
+ dh_pem, cert_pem]},
+ {ssh_public_key_decode_encode, [],
+ [ssh_rsa_public_key, ssh_dsa_public_key, ssh_rfc4716_rsa_comment,
+ ssh_rfc4716_dsa_comment, ssh_rfc4716_rsa_subject, ssh_known_hosts,
+ ssh_auth_keys, ssh1_known_hosts, ssh1_auth_keys, ssh_openssh_public_key_with_comment,
+ ssh_openssh_public_key_long_header]},
+ {sign_verify, [], [rsa_sign_verify, dsa_sign_verify]}
].
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%% Test cases starts here.
%%--------------------------------------------------------------------
@@ -118,144 +134,606 @@ app(doc) ->
"Test that the public_key app file is ok";
app(suite) ->
[];
-app(Config) when list(Config) ->
+app(Config) when is_list(Config) ->
ok = test_server:app_test(public_key).
-pem_to_der(doc) ->
- ["Check that supported PEM files are decoded into the expected entry type"];
-pem_to_der(suite) ->
+%%--------------------------------------------------------------------
+
+dsa_pem(doc) ->
+ [""];
+dsa_pem(suite) ->
[];
-pem_to_der(Config) when is_list(Config) ->
+dsa_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
- {ok,[{dsa_private_key, _, not_encrypted}]} =
- public_key:pem_to_der(filename:join(Datadir, "dsa.pem")),
- {ok,[{rsa_private_key, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "client_key.pem")),
- {ok,[{rsa_private_key, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem")),
- {ok,[{rsa_private_key, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem"), "abcd1234"),
- {ok, Bin0} = file:read_file(filename:join(Datadir, "rsa.pem")),
- {ok, [{rsa_private_key, _, _}]} = public_key:pem_to_der(Bin0, "abcd1234"),
-
- {ok,[{dh_params, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "dh.pem")),
- {ok,[{cert, _, not_encrypted}]} =
- public_key:pem_to_der(filename:join(Datadir, "client_cert.pem")),
- {ok,[{cert_req, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "req.pem")),
- {ok,[{cert, _, _}, {cert, _, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "cacerts.pem")),
-
- {ok, Bin1} = file:read_file(filename:join(Datadir, "cacerts.pem")),
- {ok, [{cert, _, _}, {cert, _, _}]} = public_key:pem_to_der(Bin1),
+
+ [{'DSAPrivateKey', DerDSAKey, not_encrypted} = Entry0 ] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
+
+ DSAKey = public_key:der_decode('DSAPrivateKey', DerDSAKey),
+
+ DSAKey = public_key:pem_entry_decode(Entry0),
+
+ {ok, DSAPubPem} = file:read_file(filename:join(Datadir, "dsa_pub.pem")),
+ [{'SubjectPublicKeyInfo', _, _} = PubEntry0] =
+ public_key:pem_decode(DSAPubPem),
+ DSAPubKey = public_key:pem_entry_decode(PubEntry0),
+ true = check_entry_type(DSAPubKey, 'DSAPublicKey'),
+ PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey),
+ DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem),
+ DSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])).
+
+%%--------------------------------------------------------------------
+
+rsa_pem(doc) ->
+ [""];
+rsa_pem(suite) ->
+ [];
+rsa_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'RSAPrivateKey', DerRSAKey, not_encrypted} = Entry0 ] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")),
+
+ RSAKey0 = public_key:der_decode('RSAPrivateKey', DerRSAKey),
+
+ RSAKey0 = public_key:pem_entry_decode(Entry0),
- ok.
+ [{'RSAPrivateKey', _, {_,_}} = Entry1] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "rsa.pem")),
+
+ true = check_entry_type(public_key:pem_entry_decode(Entry1, "abcd1234"),
+ 'RSAPrivateKey'),
+
+ {ok, RSAPubPem} = file:read_file(filename:join(Datadir, "rsa_pub.pem")),
+ [{'SubjectPublicKeyInfo', _, _} = PubEntry0] =
+ public_key:pem_decode(RSAPubPem),
+ RSAPubKey = public_key:pem_entry_decode(PubEntry0),
+ true = check_entry_type(RSAPubKey, 'RSAPublicKey'),
+ PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey),
+ RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem),
+ RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])),
+
+ {ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")),
+ [{'RSAPublicKey', _, _} = PubEntry1] =
+ public_key:pem_decode(RSARawPem),
+ RSAPubKey = public_key:pem_entry_decode(PubEntry1),
+ RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem),
+ RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])).
+
%%--------------------------------------------------------------------
-decode_private_key(doc) ->
- ["Check that private keys are decode to the expected key type."];
-decode_private_key(suite) ->
+
+encrypted_pem(doc) ->
+ [""];
+encrypted_pem(suite) ->
[];
-decode_private_key(Config) when is_list(Config) ->
+encrypted_pem(Config) when is_list(Config) ->
Datadir = ?config(data_dir, Config),
- {ok,[DsaKey = {dsa_private_key, _DsaKey, _}]} =
- public_key:pem_to_der(filename:join(Datadir, "dsa.pem")),
- {ok,[RsaKey = {rsa_private_key, _RsaKey,_}]} =
- public_key:pem_to_der(filename:join(Datadir, "client_key.pem")),
- {ok,[ProtectedRsaKey1 = {rsa_private_key, _ProtectedRsaKey1,_}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem"), "abcd1234"),
- {ok,[ProtectedRsaKey2 = {rsa_private_key, _ProtectedRsaKey2,_}]} =
- public_key:pem_to_der(filename:join(Datadir, "rsa.pem")),
- {ok, #'DSAPrivateKey'{}} = public_key:decode_private_key(DsaKey),
- {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(RsaKey),
- {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(ProtectedRsaKey1),
- {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(ProtectedRsaKey2, "abcd1234"),
- ok.
+ [{'RSAPrivateKey', DerRSAKey, not_encrypted}] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "client_key.pem")),
+
+ RSAKey = public_key:der_decode('RSAPrivateKey', DerRSAKey),
+
+ Salt0 = crypto:rand_bytes(8),
+ Entry0 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
+ {{"DES-EDE3-CBC", Salt0}, "1234abcd"}),
+ RSAKey = public_key:pem_entry_decode(Entry0,"1234abcd"),
+ Des3KeyFile = filename:join(Datadir, "des3_client_key.pem"),
+ erl_make_certs:der_to_pem(Des3KeyFile, [Entry0]),
+ [{'RSAPrivateKey', _, {"DES-EDE3-CBC", Salt0}}] =
+ erl_make_certs:pem_to_der(Des3KeyFile),
+
+ Salt1 = crypto:rand_bytes(8),
+ Entry1 = public_key:pem_entry_encode('RSAPrivateKey', RSAKey,
+ {{"DES-CBC", Salt1}, "4567efgh"}),
+ DesKeyFile = filename:join(Datadir, "des_client_key.pem"),
+ erl_make_certs:der_to_pem(DesKeyFile, [Entry1]),
+ [{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] =
+ erl_make_certs:pem_to_der(DesKeyFile),
+ true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"),
+ 'RSAPrivateKey').
+
+%%--------------------------------------------------------------------
+
+dh_pem(doc) ->
+ [""];
+dh_pem(suite) ->
+ [];
+dh_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ [{'DHParameter', DerDH, not_encrypted} = Entry] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "dh.pem")),
+
+ erl_make_certs:der_to_pem(filename:join(Datadir, "new_dh.pem"), [Entry]),
+
+ DHParameter = public_key:der_decode('DHParameter', DerDH),
+ DHParameter = public_key:pem_entry_decode(Entry),
+
+ Entry = public_key:pem_entry_encode('DHParameter', DHParameter).
+
+%%--------------------------------------------------------------------
+cert_pem(doc) ->
+ [""];
+cert_pem(suite) ->
+ [];
+cert_pem(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ [Entry0] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
+
+ [{'Certificate', DerCert, not_encrypted} = Entry7] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")),
+
+ Cert = public_key:der_decode('Certificate', DerCert),
+ Cert = public_key:pem_entry_decode(Entry7),
+
+ CertEntries = [{'Certificate', _, not_encrypted} = CertEntry0,
+ {'Certificate', _, not_encrypted} = CertEntry1] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")),
+
+ ok = erl_make_certs:der_to_pem(filename:join(Datadir, "wcacerts.pem"), CertEntries),
+ ok = erl_make_certs:der_to_pem(filename:join(Datadir, "wdsa.pem"), [Entry0]),
+
+ NewCertEntries = erl_make_certs:pem_to_der(filename:join(Datadir, "wcacerts.pem")),
+ true = lists:member(CertEntry0, NewCertEntries),
+ true = lists:member(CertEntry1, NewCertEntries),
+ [Entry0] = erl_make_certs:pem_to_der(filename:join(Datadir, "wdsa.pem")).
+
+%%--------------------------------------------------------------------
+ssh_rsa_public_key(doc) ->
+ "";
+ssh_rsa_public_key(suite) ->
+ [];
+ssh_rsa_public_key(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_pub")),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, public_key),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(RSARawSsh2, rfc4716_public_key),
+
+ {ok, RSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_rsa_pub")),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, public_key),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(RSARawOpenSsh, openssh_public_key),
+
+ %% Can not check EncodedSSh == RSARawSsh2 and EncodedOpenSsh
+ %% = RSARawOpenSsh as line breakpoints may differ
+
+ EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key),
+ EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key),
+
+ [{PubKey, Attributes1}] =
+ public_key:ssh_decode(EncodedSSh, public_key),
+ [{PubKey, Attributes2}] =
+ public_key:ssh_decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+
+ssh_dsa_public_key(doc) ->
+ "";
+ssh_dsa_public_key(suite) ->
+ [];
+ssh_dsa_public_key(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_pub")),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, public_key),
+ [{PubKey, Attributes1}] = public_key:ssh_decode(DSARawSsh2, rfc4716_public_key),
+
+ {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_pub")),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, public_key),
+ [{PubKey, Attributes2}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key),
+
+ %% Can not check EncodedSSh == DSARawSsh2 and EncodedOpenSsh
+ %% = DSARawOpenSsh as line breakpoints may differ
+
+ EncodedSSh = public_key:ssh_encode([{PubKey, Attributes1}], rfc4716_public_key),
+ EncodedOpenSsh = public_key:ssh_encode([{PubKey, Attributes2}], openssh_public_key),
+
+ [{PubKey, Attributes1}] =
+ public_key:ssh_decode(EncodedSSh, public_key),
+ [{PubKey, Attributes2}] =
+ public_key:ssh_decode(EncodedOpenSsh, public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_rsa_comment(doc) ->
+ "Test comment header and rsa key";
+ssh_rfc4716_rsa_comment(suite) ->
+ [];
+ssh_rfc4716_rsa_comment(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_rsa_comment_pub")),
+ [{#'RSAPublicKey'{} = PubKey, Attributes}] =
+ public_key:ssh_decode(RSARawSsh2, public_key),
+
+ Headers = proplists:get_value(headers, Attributes),
+
+ Value = proplists:get_value("Comment", Headers, undefined),
+ true = Value =/= undefined,
+ RSARawSsh2 = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_dsa_comment(doc) ->
+ "Test comment header and dsa key";
+ssh_rfc4716_dsa_comment(suite) ->
+ [];
+ssh_rfc4716_dsa_comment(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, DSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_dsa_comment_pub")),
+ [{{_, #'Dss-Parms'{}} = PubKey, Attributes}] =
+ public_key:ssh_decode(DSARawSsh2, public_key),
+
+ Headers = proplists:get_value(headers, Attributes),
+
+ Value = proplists:get_value("Comment", Headers, undefined),
+ true = Value =/= undefined,
+
+ %% Can not check Encoded == DSARawSsh2 as line continuation breakpoints may differ
+ Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key),
+ [{PubKey, Attributes}] =
+ public_key:ssh_decode(Encoded, public_key).
+
+%%--------------------------------------------------------------------
+ssh_rfc4716_rsa_subject(doc) ->
+ "Test another header value than comment";
+ssh_rfc4716_rsa_subject(suite) ->
+ [];
+ssh_rfc4716_rsa_subject(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, RSARawSsh2} = file:read_file(filename:join(Datadir, "ssh2_subject_pub")),
+ [{#'RSAPublicKey'{} = PubKey, Attributes}] =
+ public_key:ssh_decode(RSARawSsh2, public_key),
+
+ Headers = proplists:get_value(headers, Attributes),
+
+ Value = proplists:get_value("Subject", Headers, undefined),
+ true = Value =/= undefined,
+
+ %% Can not check Encoded == RSARawSsh2 as line continuation breakpoints may differ
+ Encoded = public_key:ssh_encode([{PubKey, Attributes}], rfc4716_public_key),
+ [{PubKey, Attributes}] =
+ public_key:ssh_decode(Encoded, public_key).
+
+%%--------------------------------------------------------------------
+ssh_known_hosts(doc) ->
+ "";
+ssh_known_hosts(suite) ->
+ [];
+ssh_known_hosts(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "known_hosts")),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ public_key:ssh_decode(SshKnownHosts, known_hosts),
+
+ Value1 = proplists:get_value(hostnames, Attributes1, undefined),
+ Value2 = proplists:get_value(hostnames, Attributes2, undefined),
+ true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+ Encoded = public_key:ssh_encode(Decoded, known_hosts),
+ Decoded = public_key:ssh_decode(Encoded, known_hosts).
+
+%%--------------------------------------------------------------------
+
+ssh1_known_hosts(doc) ->
+ "";
+ssh1_known_hosts(suite) ->
+ [];
+ssh1_known_hosts(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshKnownHosts} = file:read_file(filename:join(Datadir, "ssh1_known_hosts")),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ public_key:ssh_decode(SshKnownHosts, known_hosts),
+
+ Value1 = proplists:get_value(hostnames, Attributes1, undefined),
+ Value2 = proplists:get_value(hostnames, Attributes2, undefined),
+ true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+ Encoded = public_key:ssh_encode(Decoded, known_hosts),
+ Decoded = public_key:ssh_decode(Encoded, known_hosts).
+
+%%--------------------------------------------------------------------
+ssh_auth_keys(doc) ->
+ "";
+ssh_auth_keys(suite) ->
+ [];
+ssh_auth_keys(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "auth_keys")),
+ [{#'RSAPublicKey'{}, Attributes1}, {{_, #'Dss-Parms'{}}, _Attributes2}] = Decoded =
+ public_key:ssh_decode(SshAuthKeys, auth_keys),
+
+ Value1 = proplists:get_value(options, Attributes1, undefined),
+ true = Value1 =/= undefined,
+
+ Encoded = public_key:ssh_encode(Decoded, auth_keys),
+ Decoded = public_key:ssh_decode(Encoded, auth_keys).
+
+%%--------------------------------------------------------------------
+ssh1_auth_keys(doc) ->
+ "";
+ssh1_auth_keys(suite) ->
+ [];
+ssh1_auth_keys(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, SshAuthKeys} = file:read_file(filename:join(Datadir, "ssh1_auth_keys")),
+ [{#'RSAPublicKey'{}, Attributes1}, {#'RSAPublicKey'{}, Attributes2}] = Decoded =
+ public_key:ssh_decode(SshAuthKeys, auth_keys),
+
+ Value1 = proplists:get_value(bits, Attributes1, undefined),
+ Value2 = proplists:get_value(bits, Attributes2, undefined),
+ true = (Value1 =/= undefined) and (Value2 =/= undefined),
+
+ Encoded = public_key:ssh_encode(Decoded, auth_keys),
+ Decoded = public_key:ssh_decode(Encoded, auth_keys).
+
+%%--------------------------------------------------------------------
+ssh_openssh_public_key_with_comment(doc) ->
+ "Test that emty lines and lines starting with # are ignored";
+ssh_openssh_public_key_with_comment(suite) ->
+ [];
+ssh_openssh_public_key_with_comment(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok, DSARawOpenSsh} = file:read_file(filename:join(Datadir, "openssh_dsa_with_comment_pub")),
+ [{{_, #'Dss-Parms'{}}, _}] = public_key:ssh_decode(DSARawOpenSsh, openssh_public_key).
+
+%%--------------------------------------------------------------------
+ssh_openssh_public_key_long_header(doc) ->
+ "Test that long headers are handled";
+ssh_openssh_public_key_long_header(suite) ->
+ [];
+ssh_openssh_public_key_long_header(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+
+ {ok,RSARawOpenSsh} = file:read_file(filename:join(Datadir, "ssh_rsa_long_header_pub")),
+ [{#'RSAPublicKey'{}, _}] = Decoded = public_key:ssh_decode(RSARawOpenSsh, public_key),
+
+ Encoded = public_key:ssh_encode(Decoded, rfc4716_public_key),
+ Decoded = public_key:ssh_decode(Encoded, rfc4716_public_key).
+
%%--------------------------------------------------------------------
encrypt_decrypt(doc) ->
[""];
encrypt_decrypt(suite) ->
[];
encrypt_decrypt(Config) when is_list(Config) ->
- RSAPrivateKey = #'RSAPrivateKey'{publicExponent = 17,
- modulus = 3233,
- privateExponent = 2753,
- prime1 = 61,
- prime2 = 53,
- version = 'two-prime'},
- Msg = <<0,123>>,
- {ok, Encrypted} = public_key:encrypt(Msg, RSAPrivateKey, [{block_type, 2}]),
- test_server:format("Expected 855, Encrypted ~p ~n", [Encrypted]),
+ {PrivateKey, _DerKey} = erl_make_certs:gen_rsa(64),
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = PrivateKey,
+ PublicKey = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp},
+ Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
+ RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey),
+ Msg = public_key:decrypt_public(RsaEncrypted, PublicKey),
+ Msg = public_key:decrypt_public(RsaEncrypted, PrivateKey),
+ RsaEncrypted2 = public_key:encrypt_public(Msg, PublicKey),
+ RsaEncrypted3 = public_key:encrypt_public(Msg, PrivateKey),
+ Msg = public_key:decrypt_private(RsaEncrypted2, PrivateKey),
+ Msg = public_key:decrypt_private(RsaEncrypted3, PrivateKey),
+
ok.
+
+%%--------------------------------------------------------------------
+rsa_sign_verify(doc) ->
+ ["Checks that we can sign and verify rsa signatures."];
+rsa_sign_verify(suite) ->
+ [];
+rsa_sign_verify(Config) when is_list(Config) ->
+ Ca = {_, CaKey} = erl_make_certs:make_cert([]),
+ {Cert1, _} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]),
+ PrivateRSA = #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} =
+ public_key:pem_entry_decode(CaKey),
+ PublicRSA = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp},
+ true = public_key:pkix_verify(Cert1, PublicRSA),
+
+ Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
+ RSASign = public_key:sign(Msg, sha, PrivateRSA),
+ true = public_key:verify(Msg, sha, RSASign, PublicRSA),
+ false = public_key:verify(<<1:8, Msg/binary>>, sha, RSASign, PublicRSA),
+ false = public_key:verify(Msg, sha, <<1:8, RSASign/binary>>, PublicRSA),
+
+ RSASign1 = public_key:sign(Msg, md5, PrivateRSA),
+ true = public_key:verify(Msg, md5, RSASign1, PublicRSA).
+%%--------------------------------------------------------------------
+dsa_sign_verify(doc) ->
+ ["Checks that we can sign and verify dsa signatures."];
+dsa_sign_verify(suite) ->
+ [];
+dsa_sign_verify(Config) when is_list(Config) ->
+ Ca = erl_make_certs:make_cert([]),
+ CertInfo = {_,CertKey1} = erl_make_certs:make_cert([{key, dsa}, {issuer, Ca}]),
+ {Cert2,_CertKey} = erl_make_certs:make_cert([{issuer, CertInfo}]),
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X} =
+ public_key:pem_entry_decode(CertKey1),
+ true = public_key:pkix_verify(Cert2, {Y, #'Dss-Parms'{p=P, q=Q, g=G}}),
+ Datadir = ?config(data_dir, Config),
+ [DsaKey = {'DSAPrivateKey', _, _}] =
+ erl_make_certs:pem_to_der(filename:join(Datadir, "dsa.pem")),
+ DSAPrivateKey = public_key:pem_entry_decode(DsaKey),
+ #'DSAPrivateKey'{p=P1, q=Q1, g=G1, y=Y1, x=_X1} = DSAPrivateKey,
+ Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")),
+ DSASign = public_key:sign(Msg, sha, DSAPrivateKey),
+ DSAPublicKey = Y1,
+ DSAParams = #'Dss-Parms'{p=P1, q=Q1, g=G1},
+ true = public_key:verify(Msg, sha, DSASign, {DSAPublicKey, DSAParams}),
+ false = public_key:verify(<<1:8, Msg/binary>>, sha, DSASign,
+ {DSAPublicKey, DSAParams}),
+ false = public_key:verify(Msg, sha, <<1:8, DSASign/binary>>,
+ {DSAPublicKey, DSAParams}),
+
+ Digest = crypto:sha(Msg),
+ DigestSign = public_key:sign(Digest, none, DSAPrivateKey),
+ true = public_key:verify(Digest, none, DigestSign, {DSAPublicKey, DSAParams}),
+ <<_:8, RestDigest/binary>> = Digest,
+ false = public_key:verify(<<1:8, RestDigest/binary>>, none, DigestSign,
+ {DSAPublicKey, DSAParams}),
+ false = public_key:verify(Digest, none, <<1:8, DigestSign/binary>>,
+ {DSAPublicKey, DSAParams}).
+%%--------------------------------------------------------------------
+pkix(doc) ->
+ "Misc pkix tests not covered elsewhere";
+pkix(suite) ->
+ [];
+pkix(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ Certs0 = erl_make_certs:pem_to_der(filename:join(Datadir, "cacerts.pem")),
+ Certs1 = erl_make_certs:pem_to_der(filename:join(Datadir, "client_cert.pem")),
+ TestTransform = fun({'Certificate', CertDer, not_encrypted}) ->
+ PlainCert = public_key:pkix_decode_cert(CertDer, plain),
+ OtpCert = public_key:pkix_decode_cert(CertDer, otp),
+ CertDer =
+ public_key:pkix_encode('OTPCertificate', OtpCert, otp),
+ CertDer =
+ public_key:pkix_encode('Certificate', PlainCert, plain),
+ OTPTBS = OtpCert#'OTPCertificate'.tbsCertificate,
+ OTPSubj = OTPTBS#'OTPTBSCertificate'.subject,
+ DNEncoded = public_key:pkix_encode('Name', OTPSubj, otp),
+ PlainTBS = PlainCert#'Certificate'.tbsCertificate,
+ Subj2 = PlainTBS#'TBSCertificate'.subject,
+ DNEncoded = public_key:pkix_encode('Name', Subj2, plain),
+ false = public_key:pkix_is_fixed_dh_cert(CertDer)
+ end,
+ [TestTransform(Cert) || Cert <- Certs0 ++ Certs1],
+ true = public_key:pkix_is_self_signed(element(2,hd(Certs0))),
+ false = public_key:pkix_is_self_signed(element(2,hd(Certs1))),
-%% Datadir = ?config(data_dir, Config),
-%% {ok,[{rsa_private_key, EncKey}]} =
-%% public_key:pem_to_der(filename:join(Datadir, "server_key.pem")),
-%% {ok, Key} = public_key:decode_private_key(EncKey, rsa),
-%% RSAPublicKey = #'RSAPublicKey'{publicExponent =
-%% Key#'RSAPrivateKey'.publicExponent,
-%% modulus = Key#'RSAPrivateKey'.modulus},
-%% {ok, Msg} = file:read_file(filename:join(Datadir, "msg.txt")),
-%% Hash = crypto:sha(Msg),
-%% {ok, Encrypted} = public_key:encrypt(Hash, Key, [{block_type, 2}]),
-%% test_server:format("Encrypted ~p", [Encrypted]),
-%% {ok, Decrypted} = public_key:decrypt(Encrypted,
-%% RSAPublicKey, [{block_type, 1}]),
-%% test_server:format("Encrypted ~p", [Decrypted]),
-%% true = Encrypted == Decrypted.
-
+ CaIds = [element(2, public_key:pkix_issuer_id(Cert, self)) ||
+ {'Certificate', Cert, _} <- Certs0],
+ {ok, IssuerId = {_, _IssuerName}} =
+ public_key:pkix_issuer_id(element(2,hd(Certs1)), other),
+
+ true = lists:member(IssuerId, CaIds),
+
+ %% Should be normalized allready
+ TestStr = {rdnSequence,
+ [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"ERLANGCA"}}],
+ [{'AttributeTypeAndValue', {2,5,4,3},{printableString," erlang ca "}}]]},
+ VerifyStr = {rdnSequence,
+ [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}],
+ [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlangca"}}]]},
+ VerifyStr = public_key:pkix_normalize_name(TestStr),
+
+ ok.
%%--------------------------------------------------------------------
-rsa_verify(doc) ->
- ["Cheks that we can verify an rsa signature."];
-rsa_verify(suite) ->
+pkix_path_validation(doc) ->
+ "Misc pkix tests not covered elsewhere";
+pkix_path_validation(suite) ->
[];
-rsa_verify(Config) when is_list(Config) ->
- Datadir = ?config(data_dir, Config),
+pkix_path_validation(Config) when is_list(Config) ->
+ CaK = {Trusted,_} =
+ erl_make_certs:make_cert([{key, dsa},
+ {subject, [
+ {name, "Public Key"},
+ {?'id-at-name', {printableString, "public_key"}},
+ {?'id-at-pseudonym', {printableString, "pubkey"}},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}
+ ]}
+ ]),
+ ok = erl_make_certs:write_pem("./", "public_key_cacert", CaK),
+
+ CertK1 = {Cert1, _} = erl_make_certs:make_cert([{issuer, CaK}]),
+ CertK2 = {Cert2,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {digest, md5}, {extensions, false}]),
+ ok = erl_make_certs:write_pem("./", "public_key_cert", CertK2),
- {ok,[{cert, DerCert}]} =
- public_key:pem_to_der(filename:join(Datadir, "server_cert.pem")),
+ {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1], []),
- {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp),
+ {error, {bad_cert,invalid_issuer}} =
+ public_key:pkix_path_validation(Trusted, [Cert2], []),
- {0, Signature} = OTPCert#'Certificate'.signature,
- TBSCert = OTPCert#'Certificate'.tbsCertificate,
+ {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1, Cert2], []),
+ {error, issuer_not_found} = public_key:pkix_issuer_id(Cert2, other),
- #'TBSCertificate'{subjectPublicKeyInfo = Info} = TBSCert,
-
- #'SubjectPublicKeyInfo'{subjectPublicKey = RSAPublicKey} = Info,
-
- EncTBSCert = encoded_tbs_cert(DerCert),
- Digest = crypto:sha(EncTBSCert),
-
- public_key:verify_signature(Digest, Signature, RSAPublicKey).
-
-
-%% Signature is generated in the following way (in datadir):
-%% openssl dgst -sha1 -binary -out rsa_signature -sign server_key.pem msg.txt
-%%{ok, Signature} = file:read_file(filename:join(Datadir, "rsa_signature")),
-%%{ok, Signature} = file:read_file(filename:join(Datadir, "rsa_signature")),
-%% {ok, Msg} = file:read_file(filename:join(Datadir, "msg.txt")),
-%% Digest = crypto:sha(Msg),
-%% {ok,[{rsa_private_key, EncKey}]} =
-%% public_key:pem_to_der(filename:join(Datadir, "server_key.pem")),
-%% {ok, Key} = public_key:decode_private_key(EncKey, rsa),
-%% RSAPublicKey = #'RSAPublicKey'{publicExponent =
-%% Key#'RSAPrivateKey'.publicExponent,
-%% modulus = Key#'RSAPrivateKey'.modulus},
-
-encoded_tbs_cert(Cert) ->
- {ok, PKIXCert} =
- 'OTP-PUB-KEY':decode_TBSCert_exclusive(Cert),
- {'Certificate',
- {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert,
- EncodedTBSCert.
+ CertK3 = {Cert3,_} = erl_make_certs:make_cert([{issuer, CertK1},
+ {extensions, [{basic_constraints, false}]}]),
+ {Cert4,_} = erl_make_certs:make_cert([{issuer, CertK3}]),
+ {error, {bad_cert,missing_basic_constraint}} =
+ public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], []),
+
+ VerifyFunAndState0 = {fun(_,{bad_cert, missing_basic_constraint}, UserState) ->
+ {valid, UserState};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+ {ok, _} =
+ public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4],
+ [{verify_fun, VerifyFunAndState0}]),
+
+ {error, {bad_cert, unknown_ca}} =
+ public_key:pkix_path_validation(unknown_ca, [Cert1, Cert3, Cert4], []),
+
+ VerifyFunAndState1 =
+ {fun(_,{bad_cert, unknown_ca}, UserState) ->
+ {valid, UserState};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState}
+ end, []},
+
+ {ok, _} =
+ public_key:pkix_path_validation(unknown_ca, [Cert1], [{verify_fun,
+ VerifyFunAndState1}]),
+ ok.
+
+%%--------------------------------------------------------------------
+deprecated(doc) ->
+ ["Check deprecated functions."];
+deprecated(suite) ->
+ [];
+deprecated(Config) when is_list(Config) ->
+ Datadir = ?config(data_dir, Config),
+ {ok, [DsaKey = {'DSAPrivateKey', _DsaKey, _}]} =
+ public_key:pem_to_der(filename:join(Datadir, "dsa.pem")),
+ {ok, [RsaKey = {'RSAPrivateKey', _RsaKey,_}]} =
+ public_key:pem_to_der(filename:join(Datadir, "client_key.pem")),
+ {ok, [ProtectedRsaKey = {'RSAPrivateKey', _ProtectedRsaKey,_}]} =
+ public_key:pem_to_der(filename:join(Datadir, "rsa.pem")),
+
+ {ok, #'DSAPrivateKey'{}} = public_key:decode_private_key(DsaKey),
+ {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(RsaKey),
+ {ok, #'RSAPrivateKey'{}} = public_key:decode_private_key(ProtectedRsaKey, "abcd1234"),
+ ok.
+
+%%--------------------------------------------------------------------
+
+check_entry_type(#'DSAPrivateKey'{}, 'DSAPrivateKey') ->
+ true;
+check_entry_type(#'RSAPrivateKey'{}, 'RSAPrivateKey') ->
+ true;
+check_entry_type(#'RSAPublicKey'{}, 'RSAPublicKey') ->
+ true;
+check_entry_type({_Int, #'Dss-Parms'{}}, 'DSAPublicKey') when is_integer(_Int) ->
+ true;
+check_entry_type(#'DHParameter'{}, 'DHParameter') ->
+ true;
+check_entry_type(#'Certificate'{}, 'Certificate') ->
+ true;
+check_entry_type(_,_) ->
+ false.
+strip_ending_newlines(Bin) ->
+ string:strip(binary_to_list(Bin), right, 10).
diff --git a/lib/public_key/test/public_key_SUITE_data/auth_keys b/lib/public_key/test/public_key_SUITE_data/auth_keys
new file mode 100644
index 0000000000..0c4b47edde
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/auth_keys
@@ -0,0 +1,3 @@
+command="dump /home",no-pty,no-port-forwarding ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem b/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem
new file mode 100644
index 0000000000..d3635e5b20
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/dsa_pub.pem
@@ -0,0 +1,12 @@
+-----BEGIN PUBLIC KEY-----
+MIIBtzCCASwGByqGSM44BAEwggEfAoGBALez5tklY5CdFeTMos899pA6i4u4uCts
+zgBzrdBk6cl5FVqzdzWMGTQiynnTpGsrOESinzP06Ip+pG15We2OORwgvCxD/W95
+aCiN0/+MdiXqlsmboBARMzsa+SmBENN3gF/+tuuEAFzOXU1q2cmEywRLyfbM2KIB
+VE/TChWYw2eRAhUA1R64VvcQ90XA8SOKVDmMA0dBzukCgYEAlLMYP0pbgBlgHQVO
+3/avAHlWNrIq52Lxk7SdPJWgMvPjTK9Z6sv88kxsCcydtjvO439j1yqcwk50GQc+
+86ktBWWz93/HkIdnFyqafef4mmWvm2Uq6ClQKS+A0Asfaj8Mys+HUMiI+qsfdjRb
+yIpwb7MX1nsVdsKzALnZNMW27A0DgYQAAoGAfEIAb3mLjtFfiF/tsZb4/DGHdWSb
+6Ir0hFkoBUZ9ymBO70wlfZVSQGs240kZtOMpAOpJL1Dy8oH6PUQ+JyacwZIo8fdq
+19/Kwm6CPrpaEhzErmMvwT2CZJYZ+HOk55ljLkVCiyG7MzEj2+odLKym9yoQsbsJ
+olHzIRpkLk45y4c=
+-----END PUBLIC KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/known_hosts b/lib/public_key/test/public_key_SUITE_data/known_hosts
new file mode 100644
index 0000000000..30fc3b1fe8
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/known_hosts
@@ -0,0 +1,3 @@
+hostname.domain.com,192.168.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM=
+
+|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA1XY18+zA8VNK2YkzygOkMqUxHSTfxT1Xxx8CgDZgcQH8HUhPssW5ttvG8nKetlPQZAVk1C4WkWS1y5b3ekBhZTIxocp9Joc6V1+f2EOfO2mSLRwB16RGrdw6q7msrBXTC/dl+hF45kMMzVNzqxnSMVOa0sEPK2zK6Sg3Vi9fCSM= [email protected]
diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub
new file mode 100644
index 0000000000..a765ba8189
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub
new file mode 100644
index 0000000000..d5a34a3f78
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/openssh_dsa_with_comment_pub
@@ -0,0 +1,3 @@
+#This should be ignored!!
+
+ssh-dss AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbETW6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdHYI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5cvwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGfJ0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAAvioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACBAN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HSn24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub b/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub
new file mode 100644
index 0000000000..0a0838db40
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/openssh_rsa_pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmtisaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRuLDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368+dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNWjeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4fuKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV225JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRMIB+X+OTUUI8= dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem b/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem
new file mode 100644
index 0000000000..cbe81343f7
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/rsa_pub.pem
@@ -0,0 +1,4 @@
+-----BEGIN PUBLIC KEY-----
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANRiyZg0uci74Nc6mnqZ8AoDl88aT7x6
+JA0MfgHIHzteEj7Qg+lE5QxMGAafurVE5vqoHkDfwk4uzzsCAJuz91MCAwEAAQ==
+-----END PUBLIC KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem b/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem
new file mode 100644
index 0000000000..3b9d7568ff
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/rsa_pub_key.pem
@@ -0,0 +1,4 @@
+-----BEGIN RSA PUBLIC KEY-----
+MEgCQQDUYsmYNLnIu+DXOpp6mfAKA5fPGk+8eiQNDH4ByB87XhI+0IPpROUMTBgG
+n7q1ROb6qB5A38JOLs87AgCbs/dTAgMBAAE=
+-----END RSA PUBLIC KEY-----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
new file mode 100644
index 0000000000..c91f4e4679
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_auth_keys
@@ -0,0 +1,3 @@
+1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+
+command="dump /home",no-pty,no-port-forwarding 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
new file mode 100644
index 0000000000..ec668fe05b
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh1_known_hosts
@@ -0,0 +1,2 @@
+hostname.domain.com,192.168.0.1 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663 dhopson@VMUbuntu-DSH
+hostname2.domain.com,192.168.0.2 1024 35 794430685278501116412873221867658581245241426828503388129294124540165981586596106773643485704743298698207838825035605868404742682423919455523383721081589378970796492944950066480951790660582889972423189943567111507801410254720228911513553205592856585541922662924268445466959576882300405064708497308004255650466014242855505233634626075778108365396568863197935915425650388910408127232583533503834009244199384570662092164277923946411149853110048365318587554141774139652307149492021035538341281427025252592933784473453522113124752189378715431529801894015739903371171585194505182320772654217490509848165365152457990491089951560694728469571221819385402117009544812199223715540348068497710535492913376699508575875577554607325905000745578091554027803374110357015655416894607641289462159580964951182385869168785183135763253784745647466464331174922663455073627501620274348748413309761116542324505123795743603781806636788810617169341018091186028310551725315297135354426735951943325476221811539822892501042385411792050504283745898099390893596941969752683246939665141002098430129617772928840718016009187577151479855846883928332010147501182201528575840364152774917950524127063432334646746291719251739989499132767590205934821590545762802261107691663
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub
new file mode 100644
index 0000000000..ca5089dbd7
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_comment_pub
@@ -0,0 +1,13 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is my public key for use on \
+servers which I don't like.
+AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET
+W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH
+YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c
+vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf
+J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA
+vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB
+AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS
+n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5
+sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub
new file mode 100644
index 0000000000..a5e38be81a
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_dsa_pub
@@ -0,0 +1,12 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: DSA Public Key for use with MyIsp
+AAAAB3NzaC1kc3MAAACBAPY8ZOHY2yFSJA6XYC9HRwNHxaehvx5wOJ0rzZdzoSOXxbET
+W6ToHv8D1UJ/z+zHo9Fiko5XybZnDIaBDHtblQ+Yp7StxyltHnXF1YLfKD1G4T6JYrdH
+YI14Om1eg9e4NnCRleaqoZPF3UGfZia6bXrGTQf3gJq2e7Yisk/gF+1VAAAAFQDb8D5c
+vwHWTZDPfX0D2s9Rd7NBvQAAAIEAlN92+Bb7D4KLYk3IwRbXblwXdkPggA4pfdtW9vGf
+J0/RHd+NjB4eo1D+0dix6tXwYGN7PKS5R/FXPNwxHPapcj9uL1Jn2AWQ2dsknf+i/FAA
+vioUPkmdMc0zuWoSOEsSNhVDtX3WdvVcGcBq9cetzrtOKWOocJmJ80qadxTRHtUAAACB
+AN7CY+KKv1gHpRzFwdQm7HK9bb1LAo2KwaoXnadFgeptNBQeSXG1vO+JsvphVMBJc9HS
+n24VYtYtsMu74qXviYjziVucWKjjKEb11juqnF0GDlB3VVmxHLmxnAz643WK42Z7dLM5
+sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub
new file mode 100644
index 0000000000..e4d446147c
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_comment_pub
@@ -0,0 +1,7 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: "1024-bit RSA, converted from OpenSSH by [email protected]"
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub
new file mode 100644
index 0000000000..761088b517
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_rsa_pub
@@ -0,0 +1,13 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+AAAAB3NzaC1yc2EAAAABIwAAAgEAwrr66r8n6B8Y0zMF3dOpXEapIQD9DiYQ6D6/zwor9o
+39jSkHNiMMER/GETBbzP83LOcekm02aRjo55ArO7gPPVvCXbrirJu9pkm4AC4BBre5xSLS
+7soyzwbigFruM8G63jSXqpHqJ/ooi168sKMC2b0Ncsi+JlTfNYlDXJVLKEeZgZOInQyMmt
+isaDTUQWTIv1snAizf4iIYENuAkGYGNCL77u5Y5VOu5eQipvFajTnps9QvUx/zdSFYn9e2
+sulWM3Bxc/S4IJ67JWHVRpfJxGi3hinRBH8WQdXuUwdJJTiJHKPyYrrM7Q6Xq4TOMFtcRu
+LDC6u3BXM1L0gBvHPNOnD5l2Lp5EjUkQ9CBf2j4A4gfH+iWQZyk08esAG/iwArAVxkl368
++dkbMWOXL8BN4x5zYgdzoeypQZZ2RKH780MCTSo4WQ19DP8pw+9q3bSFC9H3xYAxrKAJNW
+jeTUJOTrTe+mWXXU770gYyQTxa2ycnYrlZucn1S3vsvn6eq7NZZ8NRbyv1n15Ocg+nHK4f
+uKOrwPhU3NbKQwtjb0Wsxx1gAmQqIOLTpAdsrAauPxC7TPYA5qQVCphvimKuhQM/1gMV22
+5JrnjspVlthCzuFYUjXOKC3wxz6FFEtwnXu3uC5bVVkmkNadJmD21gD23yk4BraGXVYpRM
+IB+X+OTUUI8=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub b/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub
new file mode 100644
index 0000000000..8b8ccda8ba
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh2_subject_pub
@@ -0,0 +1,8 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Subject: me
+Comment: 1024-bit rsa, created by [email protected] Mon Jan 15 \
+08:31:24 2001
+AAAAB3NzaC1yc2EAAAABJQAAAIEAiPWx6WM4lhHNedGfBpPJNPpZ7yKu+dnn1SJejgt4
+596k6YjzGGphH2TUxwKzxcKDKKezwkpfnxPkSMkuEspGRt/aZZ9wa++Oi7Qkr8prgHc4
+soW6NUlfDzpvZK2H5E7eQaSeP3SAwGmQKUFHCddNaP0L+hM7zhFNzjFvpaMgJw0=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub
new file mode 100644
index 0000000000..7b42ced93e
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_comment_pub
@@ -0,0 +1,9 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is an example of a very very very very looooooooooooo\
+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\
+commment
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub
new file mode 100644
index 0000000000..7b42ced93e
--- /dev/null
+++ b/lib/public_key/test/public_key_SUITE_data/ssh_rsa_long_header_pub
@@ -0,0 +1,9 @@
+---- BEGIN SSH2 PUBLIC KEY ----
+Comment: This is an example of a very very very very looooooooooooo\
+ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong\
+commment
+x-command: /home/me/bin/lock-in-guest.sh
+AAAAB3NzaC1yc2EAAAABIwAAAIEA1on8gxCGJJWSRT4uOrR13mUaUk0hRf4RzxSZ1zRb
+YYFw8pfGesIFoEuVth4HKyF8k1y4mRUnYHP1XNMNMJl1JcEArC2asV8sHf6zSPVffozZ
+5TT4SfsUu/iKy9lUcCfXzwre4WWZSXXcPff+EHtWshahu3WzBdnGxm5Xoi89zcE=
+---- END SSH2 PUBLIC KEY ----
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index 8c4e4127b2..c99fd6fee1 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1,11 +1 @@
-PUBLIC_KEY_VSN = 0.7
-
-TICKETS = OTP-8626
-
-#TICKETS_0.6 = OTP-7046 \
-# OTP-8553
-#TICKETS_0.5 = OTP-8372
-#TICKETS_0.4 = OTP-8250
-#TICKETS_0.3 = OTP-8100 OTP-8142
-#TICKETS_0.2 = OTP-7860
-#TICKETS_0.1 = OTP-7637 \ No newline at end of file
+PUBLIC_KEY_VSN = 0.11
diff --git a/lib/reltool/bin/reltool.escript b/lib/reltool/bin/reltool.escript
index 0dcd5ad1e9..0dcd5ad1e9 100644..100755
--- a/lib/reltool/bin/reltool.escript
+++ b/lib/reltool/bin/reltool.escript
diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml
index 524d728901..a791f2ce03 100644
--- a/lib/reltool/doc/src/notes.xml
+++ b/lib/reltool/doc/src/notes.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2009</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,6 +37,95 @@
thus constitutes one section in this document. The title of each
section is the version number of Reltool.</p>
+ <section><title>Reltool 0.5.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The reltool module contained two seriously erroneous
+ specs which caused bogus warnings when dialyzing reltool
+ and some correct code of users. These were fixed (specs
+ for start_link/1 and eval_server/3)</p>
+ <p>
+ - Code cleanups and simplifications - Fix a bug in the
+ calculation of circular dependencies - Eliminate two
+ dialyzer warnings - Put files alphabetically</p>
+ <p>
+ Own Id: OTP-9120</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Reltool 0.5.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added function <c>zip:foldl/3</c> to iterate over zip
+ archives.</p>
+ <p>
+ Added functions to create and extract escripts. See
+ <c>escript:create/2</c> and <c>escript:extract/2</c>.</p>
+ <p>
+ The undocumented function <c>escript:foldl/3</c> has been
+ removed. The same functionality can be achieved with the
+ more flexible functions <c>escript:extract/2</c> and
+ <c>zip:foldl/3</c>.</p>
+ <p>
+ Record fields has been annotated with type info. Source
+ files as been adapted to fit within 80 chars and trailing
+ whitespace has been removed.</p>
+ <p>
+ Own Id: OTP-8521</p>
+ </item>
+ <item>
+ <p>A new escript, called <c>reltool</c>, has been
+ introduced in order to simplify the usage of the reltool
+ application from makefiles.</p>
+ <p>The handling of applications included in releases has
+ been improved. Applications that are required to be
+ started before other applications in a release are now
+ automatically included in the release. The <c>kernel</c>
+ and <c>stdlib</c> applications are always included as
+ they are mandatory.</p>
+ <p>Applications that are (explicitly or implicitly)
+ included in a release are now automatically included as
+ if they were explicitly included with the incl_cond
+ flag.</p>
+ <p>A new <c>embedded_app_type</c> option has been
+ introduced. It is intended to be used for embedded
+ systems where all included applications must be loaded
+ from the boot script, as these systems does not utilize
+ dynamic code loading. If <c>embedded_app_type </c> is set
+ to something else than <c>undefined</c>, all included
+ applications will be included in both the release as well
+ as in the boot script. If the <c>profile</c> is
+ <c>embedded</c> the <c>embedded_app_type</c> option
+ defaults to <c>load</c>.</p>
+ <p>A new function called <c>reltool:get_status/1</c> has
+ been introduced. It returns status about the
+ configuration in the server.</p>
+ <p>The API functions that may take <c>PidOrOptions</c> as
+ input and actually gets <c>Options</c> does now print out
+ warnings.</p>
+ <p>The internal error handling has been improved. For
+ example <c>{error,Reason}</c> is always returned in case
+ of errors even when the server dies.</p>
+ <p><c>app</c> and <c>appup</c> files has been added as
+ well as a corresponding test suite.</p>
+ <p>Various cleanups has been made in the code and in the
+ documentation.</p>
+ <p>
+ Own Id: OTP-8590</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
<section><title>Reltool 0.5.3</title>
diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml
index 0c2b7d2a2b..31e15e34e7 100644
--- a/lib/reltool/doc/src/reltool.xml
+++ b/lib/reltool/doc/src/reltool.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2009</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -48,8 +48,8 @@
<c>root_dir</c> is the root directory of the analysed system and
it defaults to the system executing <c>reltool</c>. Applications
may also be located outside <c>root_dir</c>. <c>lib_dirs</c>
- defines additional library directories where applications
- additional may reside and it defaults to the the directories
+ defines library directories where additional applications
+ may reside and it defaults to the directories
listed by the operating system environment variable
<c>ERL_LIBS</c>. See the module <c>code</c> for more info.
Finally single modules and entire applications may be read from
@@ -58,10 +58,10 @@
<p>Some configuration parameters control the behavior of Reltool
on system (<c>sys</c>) level. Others provide control on
application (<c>app</c>) level and yet others are on module
- (<c>mod</c>) level. Module level parameters overrides application
- level parameters and application level parameters overrides system
+ (<c>mod</c>) level. Module level parameters override application
+ level parameters and application level parameters override system
level parameters. Escript <c>escript</c> level parameters
- overrides system level parameters.</p>
+ override system level parameters.</p>
<p>The following top level <c>options</c> are supported:</p>
@@ -74,7 +74,7 @@
a name of a <c>file</c> containing a sys tuple.</p>
</item>
- <tag><c>trap_exit></c></tag>
+ <tag><c>trap_exit</c></tag>
<item>
<p>This option controls the error handling behavior of
<c>reltool</c>. By default the window processes traps
@@ -123,10 +123,10 @@
defaults to <c>all</c> which means that if an application is
included (either explicitly or implicitly) all modules in that
application will be included. This implies that both modules
- that exists on the <c>ebin</c> directory of the application,
+ that exist in the <c>ebin</c> directory of the application,
as well as modules that are named in the <c>app</c> file will
be included. If the parameter is set to <c>ebin</c>, both
- modules on the <c>ebin</c> directory and derived modules are
+ modules in the <c>ebin</c> directory and derived modules are
included. If the parameter is set to <c>app</c>, both modules
in the <c>app</c> file and derived modules are included.
<c>derived</c> means that only modules that are used by other
@@ -138,13 +138,13 @@
<item>
<p>This parameter controls the application and escript
inclusion policy. It defaults to <c>derived</c> which means
- that the applications that not have any explicit
+ that the applications that do not have any explicit
<c>incl_cond</c> setting, will only be included if any other
(explicitly or implicitly included) application uses it. The
value <c>include</c> implies that all applications and
- escripts that that not have any explicit <c>incl_cond</c>
+ escripts that do not have any explicit <c>incl_cond</c>
setting will be included. <c>exclude</c> implies that all
- applications and escripts) that that not have any explicit
+ applications and escripts) that do not have any explicit
<c>incl_cond</c> setting will be excluded.</p>
</item>
@@ -158,7 +158,7 @@
<tag><c>rel</c></tag>
<item>
<p>Release specific configuration. Each release maps to a
- <c>rel</c>, <c>script</c> and <c>boot </c> file. See the
+ <c>rel</c>, <c>script</c> and <c>boot</c> file. See the
module <c>systools</c> for more info about the details. Each
release has a name, a version and a set of applications with a
few release specific parameters such as type and included
@@ -168,7 +168,7 @@
<tag><c>relocatable</c></tag>
<item>
<p>This parameter controls whether the <c>erl</c> executable
- in the target system automatically should determine where it
+ in the target system should automatically determine where it
is installed or if it should use a hardcoded path to the
installation. In the latter case the target system must be
installed with <c>reltool:install/2</c> before it can be
@@ -182,7 +182,7 @@
<p>The creation of the specification for a target system is
performed in two steps. In the first step a complete
specification is generated. It will likely contain much more
- files than you are interested in your customized target
+ files than you are interested in in your customized target
system. In the second step the specification will be filtered
according to your filters. There you have the ability to
specify filters per application as well as system wide
@@ -224,12 +224,12 @@
<tag><c>incl_sys_filters</c></tag>
<item>
<p>This parameter normally contains a list of regular
- expressions that controls which files in the system that
+ expressions that controls which files in the system
should be included. Each file in the target system must match
at least one of the listed regular expressions in order to be
included. Further the files may not match any filter in
<c>excl_sys_filters</c> in order to be included. Which
- application files that should be included are controlled with
+ application files should be included is controlled with
the parameters <c>incl_app_filters</c> and
<c>excl_app_filters</c>. This parameter defaults to
<c>[".*"]</c>.</p>
@@ -238,8 +238,8 @@
<tag><c>excl_sys_filters</c></tag>
<item>
<p>This parameter normally contains a list of regular
- expressions that controls which files in the system that not
- should be included in the target system. In order to be
+ expressions that controls which files in the system should
+ not be included in the target system. In order to be
included, a file must match some filter in
<c>incl_sys_filters</c> but not any filter in
<c>excl_sys_filters</c>. This parameter defaults to
@@ -260,7 +260,7 @@
<item>
<p>This parameter normally contains a list of regular
expressions that controls which application specific files
- that not should be included in the target system. In order to
+ should not be included in the target system. In order to
be included, a file must match some filter in
<c>incl_app_filters</c> but not any filter in
<c>excl_app_filters</c>. This parameter defaults to
@@ -271,8 +271,8 @@
<item>
<p>This parameter normally contains a list of regular
expressions that controls which top level directories in an
- application that should be included in an archive file (as
- opposed of beeing included as a regular directory outside the
+ application should be included in an archive file (as
+ opposed to being included as a regular directory outside the
archive). Each top directory in the application must match at
least one of the listed regular expressions in order to be
included. Further the files may not match any filter in
@@ -284,7 +284,7 @@
<item>
<p>This parameter normally contains a list of regular
expressions that controls which top level directories in an
- application that not should be included in an archive file. In
+ application should not be included in an archive file. In
order to be included in the application archive, a top
directory must match some filter in <c>incl_archive_filters</c>
but not any filter in <c>excl_archive_filters</c>. This
@@ -295,15 +295,15 @@
<item>
<p>This parameter contains a list of options that are given to
<c>zip:create/3</c> when application specific files are
- packaged into an archive. All options are not supported. The
- most useful options in this context, are the ones that
- controls which types of files that should be compressed. This
+ packaged into an archive. Only a subset of the options are
+ supported. The most useful options in this context are the ones
+ that control which types of files should be compressed. This
parameter defaults to <c>[]</c>.</p>
</item>
</taglist>
- <p>On application (<c>escript</c>) level,the following options are
+ <p>On application (<c>escript</c>) level, the following options are
supported:</p>
<taglist>
@@ -314,7 +314,7 @@
</item>
</taglist>
- <p>On application (<c>app</c>) level,the following options are
+ <p>On application (<c>app</c>) level, the following options are
supported:</p>
<taglist>
@@ -322,8 +322,8 @@
<item>
<p>The version of the application. In an installed system there may
exist several versions of an application. The <c>vsn</c> parameter
- controls which version of the application that will be choosen. If it
- is omitted, the latest version will be choosen.</p>
+ controls which version of the application will be chosen. If it
+ is omitted, the latest version will be chosen.</p>
</item>
<tag><c>mod</c></tag>
<item>
@@ -380,7 +380,7 @@
</item>
</taglist>
- <p>On module (<c>mod</c>) level,the following options are
+ <p>On module (<c>mod</c>) level, the following options are
supported:</p>
<taglist>
@@ -391,9 +391,9 @@
will be used to control whether the module is included or not. The
value of <c>incl_cond</c> overrides the module inclusion policy.
<c>include</c> implies that the module is included, while
- <c>exclude</c> implies that the module not is included.
- <c>derived</c> implies that the is included if any included uses the
- module.</p>
+ <c>exclude</c> implies that the module is not included.
+ <c>derived</c> implies that the module is included if it is used
+ by any other included module.</p>
</item>
<tag><c>debug_info</c></tag>
<item>
@@ -546,12 +546,12 @@ target_spec() = [target_spec()]
files are by default copied to the target system. The
<c>releases</c> directory contains generated <c>rel</c>,
<c>script</c>, and <c>boot</c> files. The <c>lib</c> directory
- contains the applications. Which applications that are included
+ contains the applications. Which applications are included
and if they should be customized (archived, stripped from debug
info etc.) is specified with various configuration
parameters. The files in the <c>bin</c> directory are copied
from the <c>erts-vsn/bin</c> directory, but only those files
- that was originally included in <c>bin</c> directory of the
+ that were originally included in the <c>bin</c> directory of the
source system.</p>
<p>If the configuration parameter <c>relocatable</c> was set to
@@ -584,10 +584,10 @@ target_spec() = [target_spec()]
<v>Reason = reason()</v>
</type>
<desc><p>Get reltool configuration. Normally, only the explicit
- configuration parameters with values that differs from their
+ configuration parameters with values that differ from their
defaults are interesting. But the builtin default values can be
returned by setting <c>InclDefaults</c> to <c>true</c>. The
- derived configuration can be return by setting
+ derived configuration can be returned by setting
<c>InclDerived</c> to <c>true</c>.</p></desc>
</func>
@@ -705,7 +705,7 @@ target_spec() = [target_spec()]
<v>Reason = reason()</v>
</type>
<desc><p>Start a server process with options. The server process
- identity can be given as argument to several other functions in the
+ identity can be given as an argument to several other functions in the
API.</p></desc>
</func>
diff --git a/lib/reltool/doc/src/reltool_examples.xml b/lib/reltool/doc/src/reltool_examples.xml
index bce9413b52..19a3f37819 100644
--- a/lib/reltool/doc/src/reltool_examples.xml
+++ b/lib/reltool/doc/src/reltool_examples.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2009</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/reltool/doc/src/reltool_usage.xml b/lib/reltool/doc/src/reltool_usage.xml
index 885828d1f0..d128e80a77 100644
--- a/lib/reltool/doc/src/reltool_usage.xml
+++ b/lib/reltool/doc/src/reltool_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2009</year>
- <year>2009</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -44,7 +44,7 @@
<section>
<title>System window</title>
<p>The system window is started with the function
- <c>reltool:start/1</c>. At startup the tool will process the all
+ <c>reltool:start/1</c>. At startup the tool will process all
<c>beam</c> files and <c>app</c> files in order to find out dependencies
between applications and their modules. Once all this information has been
derived, it will be possible to explore the tool.</p>
@@ -67,29 +67,29 @@
<section>
<title>Libraries</title>
<p>On the library page it is possible to control which sources
- that the tool will use. The page is organized as a tree which
+ the tool will use. The page is organized as a tree which
can be expanded and collapsed by clicking on the little symbol
in the beginning of the expandable/collapsible lines.</p>
<p>The <c>Root directory</c> can be edited by selecting the
line where the path of the root directory is displayed and
- clicking with the right mouse button. Choose edit in the menu
- that pops up. </p>
+ clicking the right mouse button. Choose edit in the menu
+ that pops up.</p>
<p>Library directories can be added, edited or deleted. This
is done by selecting the line where the path to a library
- directory is displayed and clicking with the right mouse
+ directory is displayed and clicking the right mouse
button. Choose add, edit or delete in the menu that pops
up. New library directories can also be added by selecting the
- line <c>Library directories</c> and clicking with the right
+ line <c>Library directories</c> and clicking the right
mouse button. Choose add in the menu that pops up.</p>
<p>Escript files can be added, edited or deleted. This is done
by selecting the line where the path to an escript file is
- displayed and clicking with the right mouse button. Choose
+ displayed and clicking the right mouse button. Choose
add, edit or delete in the menu that pops up. New escripts can
also be added by selecting the line <c>Escript files</c> and
- clicking with the right mouse button. Choose add in the menu
+ clicking the right mouse button. Choose add in the menu
that pops up.</p>
<p>When libraries and escripts are expanded, the names of
@@ -102,7 +102,7 @@
<p>On the system settings page it is possible to control some
global settings that are used as defaults for all
applications. Set the <c>Application inclusion policy</c> to
- <c>include</c> to include all applications that not are
+ <c>include</c> to include all applications that are not
explicitly excluded. See <c>incl_cond</c> (application
inclusion) and <c>mod_cond</c> (module inclusion) in the
reference manual for the module <c>reltool</c> for more
@@ -131,14 +131,14 @@
<p>The symbols in front of the application names are intended
to describe the status of the application. There are error
- symbols and warning symbols that means that there are
- something that needs attention. The tick symbol means that the
+ and warning symbols to signalize that there is
+ something which needs attention. The tick symbol means that the
application is included or derived and no problem has been
detected. The cross symbol means that the application is
excluded or available and no problem has been
detected. Applications with error symbols are listed first in
- each category, then comes the warnings and the normal ones
- (ticks and crosses) are found at the end.</p>
+ each category and are followed by the warnings and the
+ normal ones (ticks and crosses) at the end.</p>
<p>Double click on an application to launch its application
window.</p>
@@ -175,8 +175,8 @@
</item>
<item>
<p><c>Save configuration</c> - Saves the current
- configuration to file. Normally, only the explictit
- configuration parameters with values that differs from their
+ configuration to file. Normally, only the explicit
+ configuration parameters with values that differ from their
defaults are saved. But the configuration with or without
default values and with or without derived values may also
be saved.</p>
@@ -204,21 +204,21 @@
<p>It is possible to perform some limited manipulations of the
graph. Nodes can be moved, selected, locked or deleted. Move a
single node or the entire graph by moving the mouse while the
- left mouse button is pressed. A node is can be locked into a fix
+ left mouse button is pressed. A node can be locked into a fix
position by holding down the shift button when the left mouse
button is released. Select several nodes by moving the mouse
- while the control key and the left mouse button i
+ while the control key and the left mouse button are
pressed. Selected nodes can be locked, unlocked or deleted by
- klicking on a suitable button.</p>
+ clicking on a suitable button.</p>
<p>The algorithm that is used to draw a graph with as few
crossed links as possible is called force graph. A force graph
- consists of nodes and directed link between nodes. Each node is
+ consists of nodes and directed links between nodes. Each node is
associated with a repulsive force that pushes nodes away from
each other. This force can be adjusted with the left slider or
with the mouse wheel. Each link is associated with an attractive
- force that pulls the nodes nearer each other. This force can be
- adjusted with the right slider. If this force becomes to strong,
+ force that pulls the nodes nearer to each other. This force can be
+ adjusted with the right slider. If this force becomes too strong,
the graph will be unstable. The third parameter that can be
adjusted is the length of the links. It is adjusted with the
middle slider.</p>
@@ -256,7 +256,7 @@
<p>Select version of the application in the <c>Source selection
policy</c> part of the page. By default the latest version of the
application is selected, but it is possible to override this by
- explicitly select another version.</p>
+ explicitly selecting another version.</p>
<p>By default the <c>Application inclusion policy</c> on system
level is used for all applications. Set the value to
@@ -272,10 +272,10 @@
want actually used modules to be included. Set it to <c>app</c> if
you, besides derived modules, also want the modules listed in the
app file to be included. Set it to <c>ebin</c> if you, besides
- derived modules, also want the modules that exists as beam files
- on the ebin directory to be included. Set it to <c>all</c> if you
+ derived modules, also want the modules that exist as beam files
+ in the ebin directory to be included. Set it to <c>all</c> if you
want all modules to be included, that is the union of modules
- found on the ebin directory and listed in the app file.</p>
+ found in the ebin directory and listed in the app file.</p>
<p>The application settings page is rather incomplete.</p>
</section>
@@ -299,16 +299,16 @@
undone.</p>
<p>The symbols in front of the module names are intended to
- describe the status of the module. There are error symbols
- and warning symbols that means that there are something that needs
+ describe the status of the module. There are error and
+ and warning symbols to signalize that there is something that needs
attention. The tick symbol means that the module is included
or derived and no problem has been detected. The cross symbol
means that the module is excluded or available and no problem
has been detected. Modules with error symbols are listed
- first in each category, then comes the warnings and the normal
- ones (ticks and crosses) are found at the end.</p>
+ first in each category and are followed by warnings and the
+ normal ones (ticks and crosses) at the end.</p>
- <p>Double click on an module to launch its module window.</p>
+ <p>Double click on a module to launch its module window.</p>
</section>
@@ -323,7 +323,7 @@
<c>app</c> file. If the application includes other applications,
these are listed under <c>Included</c>. These applications are
listed in the <c>included_applications</c> part of the <c>app</c>
- file. If the application uses modules other applications, these
+ file. If the application uses other applications, these
are listed under <c>Uses</c>.</p>
<p>Double click on an application name to launch an application window.</p>
@@ -336,7 +336,7 @@
<p>There are two categories of modules on the <c>Module
dependencies</c> page. If the module is used by other modules,
these are listed under <c>Modules used by others</c>. If the
- module uses modules other modules, these are listed under <c>Used
+ module uses other modules, these are listed under <c>Used
modules</c>.</p>
<p>Double click on an module name to launch a module window.</p>
@@ -365,8 +365,8 @@
<p>There are two categories of modules on the <c>Dependencies</c>
page. If the module is used by other modules, these are listed
- under <c>Modules used by others</c>. If the module uses modules
- other modules, these are listed under <c>Used modules</c>.</p>
+ under <c>Modules used by others</c>. If the module uses other
+ modules, these are listed under <c>Used modules</c>.</p>
<p>Double click on an module name to launch a module window.</p>
@@ -378,7 +378,7 @@
<p>On the <c>Code</c> page the Erlang source code is displayed. It
is possible to search forwards and backwards for text in the
module. Enter a regular expression in the <c>Find</c> field and
- press enter. It is also possible to goto a certain line on the
+ press enter. It is also possible to go to a certain line in the
module. The <c>Back</c> button can be used to go back to the
previous position.</p>
diff --git a/lib/reltool/src/reltool.app.src b/lib/reltool/src/reltool.app.src
index b80753e8fc..4188f341f1 100644
--- a/lib/reltool/src/reltool.app.src
+++ b/lib/reltool/src/reltool.app.src
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,8 +22,8 @@
{vsn, "%VSN%"},
{modules,
[
- reltool_app_win,
reltool,
+ reltool_app_win,
reltool_fgraph,
reltool_fgraph_win,
reltool_mod_win,
diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl
index 9dd0a24f46..54eb1ca9e1 100644
--- a/lib/reltool/src/reltool.erl
+++ b/lib/reltool/src/reltool.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -38,26 +38,26 @@ start() ->
%% Start main window process
-spec start(options()) -> {ok, window_pid()} | {error, reason()}.
-start(Options)when is_list(Options) ->
+start(Options) when is_list(Options) ->
case start_link(Options) of
- {ok, WinPid} ->
+ {ok, WinPid} = OK ->
unlink(WinPid),
- {ok, WinPid};
- Other->
- Other
+ OK;
+ {error, _Reason} = Error ->
+ Error
end.
%% Start main window process with wx debugging enabled
--spec debug() -> {ok, window_pid()} | {error, reason()}.
+-spec debug() -> {ok, window_pid()} | {error, reason()}.
debug() ->
start([{wx_debug, 2}]).
%% Start main window process with options
--spec start_link(options()) -> {ok, window_pid() | {error, reason()}}.
+-spec start_link(options()) -> {ok, window_pid()} | {error, reason()}.
start_link(Options) when is_list(Options) ->
case reltool_sys_win:start_link(Options) of
- {ok, WinPid} ->
- {ok, WinPid};
+ {ok, _WinPid} = OK ->
+ OK;
{error, Reason} ->
{error, lists:flatten(io_lib:format("~p", [Reason]))}
end.
@@ -76,8 +76,8 @@ start_server(Options) ->
-spec get_server(window_pid()) -> {ok, server_pid()} | {error, reason()}.
get_server(WinPid) ->
case reltool_sys_win:get_server(WinPid) of
- {ok, ServerPid} ->
- {ok, ServerPid};
+ {ok, _ServerPid} = OK ->
+ OK;
{error, Reason} ->
{error, lists:flatten(io_lib:format("~p", [Reason]))}
end.
@@ -96,9 +96,9 @@ stop(Pid) when is_pid(Pid) ->
end.
%% Internal library function
--spec eval_server(server(), boolean(), fun((server_pid()) -> term())) ->
- {ok, server_pid()} | {error, reason()}.
-eval_server(Pid, DisplayWarnings, Fun)
+-spec eval_server(server(), boolean(), fun((server_pid()) -> Ret)) ->
+ Ret | {error, reason()} when Ret :: term().
+eval_server(Pid, _DisplayWarnings, Fun)
when is_pid(Pid) ->
Fun(Pid);
eval_server(Options, DisplayWarnings, Fun)
@@ -107,8 +107,8 @@ eval_server(Options, DisplayWarnings, Fun)
Res = case start_server(Options) of
{ok, Pid} ->
apply_fun(Pid, DisplayWarnings, Fun);
- {error, Reason} ->
- {error, Reason}
+ {error, _Reason} = Error ->
+ Error
end,
process_flag(trap_exit, TrapExit),
Res.
@@ -122,21 +122,18 @@ apply_fun(Pid, true, Fun) ->
{ok, Warnings} ->
[io:format("~p: ~s\n", [?APPLICATION, W]) || W <- Warnings],
apply_fun(Pid, false, Fun);
- {error, Reason} ->
+ {error, _Reason} = Error ->
stop(Pid),
- {error, Reason}
+ Error
end.
%% Get status about the configuration
-type warning() :: string().
--spec get_status(server()) ->
- {ok, [warning()]} | {error, reason()}.
+-spec get_status(server()) -> {ok, [warning()]} | {error, reason()}.
get_status(PidOrOptions)
when is_pid(PidOrOptions); is_list(PidOrOptions) ->
eval_server(PidOrOptions, false,
- fun(Pid) ->
- reltool_server:get_status(Pid)
- end).
+ fun(Pid) -> reltool_server:get_status(Pid) end).
%% Get reltool configuration
-spec get_config(server()) -> {ok, config()} | {error, reason()}.
diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl
index 1a34ced89d..93f47f6381 100644
--- a/lib/reltool/src/reltool.hrl
+++ b/lib/reltool/src/reltool.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,8 +20,8 @@
-define(MISSING_APP_NAME, '*MISSING*').
-define(MISSING_APP_TEXT, "*MISSING*").
--type file() :: string().
--type dir() :: string().
+-type file() :: file:filename().
+-type dir() :: file:filename().
%% app - Include all modules in app file
%% ebin - Include all modules on ebin directory
%% derived - Include only those modules that others are dependent on
@@ -48,7 +48,7 @@
-type mod_name() :: atom().
-type app_name() :: atom().
-type app_vsn() :: string(). % e.g. "4.7"
--type app_label() :: string().% e.g. "mnesia" or "mnesia-4.7"
+-type app_label() :: string(). % e.g. "mnesia" or "mnesia-4.7"
-type app_type() :: permanent | transient | temporary | load | none.
-type incl_app() :: app_name().
-type emu_name() :: string().
@@ -93,10 +93,10 @@
| {escript, escript_file(), [escript()]}
| {app, app_name(), [app()]}.
-type config() :: {sys, [sys()]}.
--type option() :: {wx_debug, term()} |
- {trap_exit, boolean()} |
- config() |
- {config, config() | file()}.
+-type option() :: {wx_debug, term()}
+ | {trap_exit, boolean()}
+ | config()
+ | {config, config() | file()}.
-type options() :: [option()].
-type server_pid() :: pid().
-type window_pid() :: pid().
@@ -110,32 +110,30 @@
-type top_dir() :: file().
-type top_file() :: file().
-type target_spec() :: [target_spec()]
- | {create_dir, base_dir(), [target_spec()]}
- | {create_dir, base_dir(), top_dir(), [target_spec()]}
- | {archive, base_file(), [archive_opt()], [target_spec()]}
- | {copy_file, base_file()}
- | {copy_file, base_file(), top_file()}
- | {write_file, base_file(), iolist()}
- | {strip_beam_file, base_file()}.
--type target_dir() :: dir().
--type incl_defaults() :: boolean().
--type incl_derived() :: boolean().
--type ets_tab() :: term().
--type status() :: missing | ok.
+ | {create_dir, base_dir(), [target_spec()]}
+ | {create_dir, base_dir(), top_dir(), [target_spec()]}
+ | {archive, base_file(), [archive_opt()], [target_spec()]}
+ | {copy_file, base_file()}
+ | {copy_file, base_file(), top_file()}
+ | {write_file, base_file(), iolist()}
+ | {strip_beam_file, base_file()}.
+-type target_dir() :: dir().
+-type incl_defaults() :: boolean().
+-type incl_derived() :: boolean().
+-type status() :: missing | ok.
-record(common,
{
sys_debug :: term(),
wx_debug :: term(),
trap_exit :: boolean(),
- app_tab :: ets_tab(),
- mod_tab :: ets_tab(),
- mod_used_by_tab :: ets_tab()
- }).
-
+ app_tab :: ets:tab(),
+ mod_tab :: ets:tab(),
+ mod_used_by_tab :: ets:tab()
+ }).
-record(mod,
- {%% Static
+ { %% Static
name :: mod_name(),
app_name :: app_name(),
incl_cond :: incl_cond() | undefined,
@@ -144,13 +142,12 @@
is_ebin_mod :: boolean(),
uses_mods :: [mod_name()],
exists :: boolean(),
-
%% Dynamic
status :: status(),
used_by_mods :: [mod_name()],
is_pre_included :: boolean() | undefined,
is_included :: boolean() | undefined
- }).
+ }).
-record(app_info,
{
@@ -166,10 +163,12 @@
env = [] :: [{atom(), term()}],
mod = undefined :: {mod_name(), [term()]} | undefined,
start_phases = undefined :: [{atom(), term()}] | undefined
- }).
+ }).
+
+-record(regexp, {source, compiled}).
-record(app,
- {%% Static info
+ { %% Static info
name :: app_name(),
is_escript :: boolean(),
use_selected_vsn :: boolean() | undefined,
@@ -188,10 +187,10 @@
debug_info :: debug_info() | undefined,
app_file :: app_file() | undefined,
app_type :: app_type() | undefined,
- incl_app_filters :: incl_app_filters(),
- excl_app_filters :: excl_app_filters(),
- incl_archive_filters :: incl_archive_filters(),
- excl_archive_filters :: excl_archive_filters(),
+ incl_app_filters :: [#regexp{}],
+ excl_app_filters :: [#regexp{}],
+ incl_archive_filters :: [#regexp{}],
+ excl_archive_filters :: [#regexp{}],
archive_opts :: [archive_opt()],
%% Dynamic
@@ -203,13 +202,13 @@
is_pre_included :: boolean(),
is_included :: boolean(),
rels :: [rel_name()]
- }).
+ }).
-record(rel_app,
{
- name :: app_name(),
- app_type :: app_type(),
- incl_apps :: [incl_app()]
+ name :: app_name(),
+ app_type :: app_type() | undefined,
+ incl_apps = [] :: [incl_app()]
}).
-record(rel,
@@ -217,11 +216,10 @@
name :: rel_name(),
vsn :: rel_vsn(),
rel_apps :: [#rel_app{}]
- }).
+ }).
-record(sys,
- {
- %% Sources
+ { %% Sources
root_dir :: dir(),
lib_dirs :: [dir()],
escripts :: [file()],
@@ -234,21 +232,19 @@
rels :: [#rel{}],
emu_name :: emu_name(),
profile :: profile(),
- incl_sys_filters :: incl_sys_filters(),
- excl_sys_filters :: excl_sys_filters(),
- incl_app_filters :: incl_app_filters(),
- excl_app_filters :: excl_app_filters(),
- incl_archive_filters :: incl_archive_filters(),
- excl_archive_filters :: excl_archive_filters(),
+ incl_sys_filters :: [#regexp{}],
+ excl_sys_filters :: [#regexp{}],
+ incl_app_filters :: [#regexp{}],
+ excl_app_filters :: [#regexp{}],
+ incl_archive_filters :: [#regexp{}],
+ excl_archive_filters :: [#regexp{}],
archive_opts :: [archive_opt()],
relocatable :: boolean(),
rel_app_type :: app_type(),
embedded_app_type :: app_type() | undefined,
app_file :: app_file(),
debug_info :: debug_info()
- }).
-
--record(regexp, {source, compiled}).
+ }).
-define(ERR_IMAGE, 0).
-define(WARN_IMAGE, 1).
@@ -275,7 +271,7 @@
-define(DEFAULT_INCL_ARCHIVE_FILTERS, [".*"]).
-define(DEFAULT_EXCL_ARCHIVE_FILTERS, ["^include\$", "^priv\$"]).
--define(DEFAULT_ARCHIVE_OPTS, []).
+-define(DEFAULT_ARCHIVE_OPTS, []).
-define(DEFAULT_INCL_SYS_FILTERS, [".*"]).
-define(DEFAULT_EXCL_SYS_FILTERS, []).
@@ -305,5 +301,5 @@
"^erts.*/bin/(start|escript|to_erl|run_erl)(|\\.exe)\$",
"^erts.*/bin/.*(debug|pdb)"]).
-define(STANDALONE_INCL_APP_FILTERS, ["^ebin",
- "^priv"]).
+ "^priv"]).
-define(STANDALONE_EXCL_APP_FILTERS, ["^ebin/.*\\.appup\$"]).
diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl
index f68c61fd6f..e1c2fa5100 100644
--- a/lib/reltool/src/reltool_mod_win.erl
+++ b/lib/reltool/src/reltool_mod_win.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -334,9 +334,9 @@ find_regular_bin(App, Mod) ->
ActiveDir = App#app.active_dir,
SrcDir = filename:join([ActiveDir, "src"]),
ModStr = atom_to_list(Mod#mod.name),
- Base = ModStr ++ ".erl",
- Find = fun(F, _Acc) -> file:read_file(F) end,
- case filelib:fold_files(SrcDir, Base, true, Find, {error, enoent}) of
+ Base = "^" ++ ModStr ++ "\\.erl$",
+ Find = fun(F, _Acc) -> throw(file:read_file(F)) end,
+ case catch filelib:fold_files(SrcDir, Base, true, Find, {error, enoent}) of
{ok, Bin} ->
Bin;
{error, enoent} ->
@@ -744,7 +744,7 @@ create_editor(Parent) ->
wxStyledTextCtrl:styleSetFont(Ed, Style, FixedFont),
wxStyledTextCtrl:styleSetForeground(Ed, Style, Color)
end,
- [SetStyle(Style) || Style <- Styles],
+ lists:foreach(fun (Style) -> SetStyle(Style) end, Styles),
wxStyledTextCtrl:setKeyWords(Ed, 0, keyWords()),
%% Margins Markers
diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl
index 039ad56aa8..9743289ca6 100644
--- a/lib/reltool/src/reltool_server.erl
+++ b/lib/reltool/src/reltool_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -498,8 +498,8 @@ more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc, Status) ->
true ->
more_apps_in_rels(RelApps, Apps, Acc, Status);
false ->
- case lists:keysearch(AppName, #app.name, Apps) of
- {value, #app{info = #app_info{applications = InfoApps}}} ->
+ case lists:keyfind(AppName, #app.name, Apps) of
+ #app{info = #app_info{applications = InfoApps}} ->
Extra = [{RelName, N} || N <- InfoApps],
{Acc2, Status2} =
more_apps_in_rels(Extra, Apps, [RA | Acc], Status),
@@ -743,9 +743,9 @@ mod_propagate_is_used_by(_C, []) ->
read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) ->
{Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl),
Status =
- case lists:keysearch(missing, #mod.status, Mods2) of
- {value, _} -> missing;
- false -> ok
+ case lists:keymember(missing, #mod.status, Mods2) of
+ true -> missing;
+ false -> ok
end,
UsesMods = [M#mod.uses_mods || M <- Mods2, M#mod.is_included =:= true],
UsesMods2 = lists:usort(lists:flatten(UsesMods)),
@@ -820,22 +820,14 @@ filter_app(A) ->
A#app.use_selected_vsn =:= undefined ->
false;
true ->
- {Dir, Dirs} =
+ {Dir, Dirs, OptVsn} =
case A#app.use_selected_vsn of
undefined ->
- {shrinked, []};
+ {shrinked, [], undefined};
false ->
- {shrinked, []};
+ {shrinked, [], undefined};
true ->
- {A#app.active_dir, [A#app.active_dir]};
- _ when A#app.is_escript ->
- {A#app.active_dir, [A#app.active_dir]}
- end,
- OptVsn =
- case A#app.use_selected_vsn of
- undefined -> undefined;
- false -> undefined;
- true -> A#app.vsn
+ {A#app.active_dir, [A#app.active_dir], A#app.vsn}
end,
{true, A#app{active_dir = Dir,
sorted_dirs = Dirs,
@@ -1087,8 +1079,8 @@ missing_mod(ModName, AppName) ->
add_mod_config(Mods, ModConfigs) ->
AddConfig =
fun(Config, Acc) ->
- case lists:keysearch(Config#mod.name, #mod.name, Mods) of
- {value, M} ->
+ case lists:keyfind(Config#mod.name, #mod.name, Mods) of
+ #mod{} = M ->
M2 = M#mod{incl_cond = Config#mod.incl_cond},
lists:keystore(Config#mod.name, #mod.name, Acc, M2);
false ->
@@ -1179,10 +1171,10 @@ read_config(OldSys, {sys, KeyVals}, Status) ->
end,
NewSys2 = NewSys#sys{apps = lists:sort(Apps),
rels = lists:sort(Rels)},
- case lists:keysearch(NewSys2#sys.boot_rel,
+ case lists:keymember(NewSys2#sys.boot_rel,
#rel.name,
NewSys2#sys.rels) of
- {value, _} ->
+ true ->
{NewSys2, Status2};
false ->
Text2 = lists:concat(["Release " ++ NewSys2#sys.boot_rel,
@@ -1326,7 +1318,7 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) ->
Val =:= none;
Val =:= undefined ->
{Sys#sys{embedded_app_type = Val}, Status};
- app_file when Val =:= keep; Val =:= strip, Val =:= all ->
+ app_file when Val =:= keep; Val =:= strip; Val =:= all ->
{Sys#sys{app_file = Val}, Status};
debug_info when Val =:= keep; Val =:= strip ->
{Sys#sys{debug_info = Val}, Status};
@@ -1418,27 +1410,27 @@ decode(#mod{} = Mod, [{Key, Val} | KeyVals], Status) ->
end,
decode(Mod2, KeyVals, Status2);
decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) ->
- RA =
+ {ValidTypesAssigned, RA} =
case RelApp of
Name when is_atom(Name) ->
- #rel_app{name = Name, app_type = undefined, incl_apps = []};
+ {true, #rel_app{name = Name}};
{Name, Type} when is_atom(Name) ->
- #rel_app{name = Name, app_type = Type, incl_apps = []};
+ {is_type(Type), #rel_app{name = Name, app_type = Type}};
{Name, InclApps} when is_atom(Name), is_list(InclApps) ->
- #rel_app{name = Name,
- app_type = undefined,
- incl_apps = InclApps};
+ VI = lists:all(fun erlang:is_atom/1, InclApps),
+ {VI, #rel_app{name = Name, incl_apps = InclApps}};
{Name, Type, InclApps} when is_atom(Name), is_list(InclApps) ->
- #rel_app{name = Name, app_type = Type, incl_apps = InclApps};
+ VT = is_type(Type),
+ VI = lists:all(fun erlang:is_atom/1, InclApps),
+ {VT andalso VI,
+ #rel_app{name = Name, app_type = Type, incl_apps = InclApps}};
_ ->
- #rel_app{incl_apps = []}
+ {false, #rel_app{incl_apps = []}}
end,
- IsType = is_type(RA#rel_app.app_type),
- NonAtoms = [IA || IA <- RA#rel_app.incl_apps, not is_atom(IA)],
- if
- IsType, NonAtoms =:= [] ->
+ case ValidTypesAssigned of
+ true ->
decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals, Status);
- true ->
+ false ->
Text = lists:flatten(io_lib:format("~p", [RelApp])),
Status2 =
reltool_utils:return_first_error(Status,
@@ -1542,10 +1534,9 @@ check_rel(RelName, RelApps, Status) ->
patch_erts_version(RootDir, Apps, Status) ->
AppName = erts,
- case lists:keysearch(AppName, #app.name, Apps) of
- {value, Erts} ->
+ case lists:keyfind(AppName, #app.name, Apps) of
+ #app{vsn = Vsn} = Erts ->
LocalRoot = code:root_dir(),
- Vsn = Erts#app.vsn,
if
LocalRoot =:= RootDir, Vsn =:= "" ->
Vsn2 = erlang:system_info(version),
@@ -1773,20 +1764,20 @@ files_to_apps(_Escript, [], Acc, _Apps, _OldApps, Status) ->
{lists:keysort(#app.name, Acc), Status}.
merge_escript_app(AppName, Dir, Info, Mods, Apps, OldApps, Status) ->
- case lists:keysearch(AppName, #app.name, OldApps) of
- {value, App} ->
- ok;
- false ->
- App = default_app(AppName, Dir)
- end,
- App2 = App#app{is_escript = true,
- label = filename:basename(Dir, ".escript"),
- info = Info,
- mods = Mods,
- active_dir = Dir,
- sorted_dirs = [Dir]},
- case lists:keysearch(AppName, #app.name, Apps) of
- {value, _} ->
+ App1 = case lists:keyfind(AppName, #app.name, OldApps) of
+ #app{} = App ->
+ App;
+ false ->
+ default_app(AppName, Dir)
+ end,
+ App2 = App1#app{is_escript = true,
+ label = filename:basename(Dir, ".escript"),
+ info = Info,
+ mods = Mods,
+ active_dir = Dir,
+ sorted_dirs = [Dir]},
+ case lists:keymember(AppName, #app.name, Apps) of
+ true ->
Error = lists:concat([AppName, ": Application name clash. ",
"Escript ", Dir," contains application ",
AppName, "."]),
@@ -1804,12 +1795,15 @@ merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) ->
%% Initate app
Apps2 = sort_app_dirs(Apps),
Apps4 =
- case lists:keysearch(Name, #app.name, Apps) of
+ case lists:keyfind(Name, #app.name, Apps) of
false ->
- case lists:keysearch(Name, #app.name, OldApps) of
- {value, OldApp} when OldApp#app.active_dir =:= Dir ->
+ case lists:keyfind(Name, #app.name, OldApps) of
+ false ->
+ App = default_app(Name, Dir),
+ [App | Apps2];
+ #app{active_dir = Dir} = OldApp ->
[OldApp | Apps2];
- {value, OldApp} ->
+ OldApp ->
App =
case filter_app(OldApp) of
{true, NewApp} ->
@@ -1818,12 +1812,9 @@ merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) ->
false ->
default_app(Name, Dir)
end,
- [App | Apps2];
- false ->
- App = default_app(Name, Dir),
[App | Apps2]
end;
- {value, OldApp} ->
+ OldApp ->
Apps3 = lists:keydelete(Name, #app.name, Apps2),
App = OldApp#app{sorted_dirs = [Dir | OldApp#app.sorted_dirs]},
[App | Apps3]
diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl
index dd6f75b9fc..0fcf89a360 100644
--- a/lib/reltool/src/reltool_target.erl
+++ b/lib/reltool/src/reltool_target.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -90,11 +90,11 @@ do_gen_config(#sys{root_dir = RootDir,
debug_info = DebugInfo},
InclDefs) ->
ErtsItems =
- case lists:keysearch(erts, #app.name, Apps) of
- {value, Erts} ->
- [{erts, do_gen_config(Erts, InclDefs)}];
- false ->
- []
+ case lists:keyfind(erts, #app.name, Apps) of
+ false ->
+ [];
+ Erts ->
+ [{erts, do_gen_config(Erts, InclDefs)}]
end,
AppsItems =
[do_gen_config(A, InclDefs)
@@ -521,7 +521,6 @@ sort_apps([#app{name = Name, info = Info} = App | Apps],
Visited,
[],
[]),
-
Missing1 = NotFnd1 ++ NotFnd2 ++ Missing,
case Uses ++ Incs of
[] ->
@@ -533,7 +532,7 @@ sort_apps([#app{name = Name, info = Info} = App | Apps],
%% The apps in L must be started before the app.
%% Check if we have already taken care of some app in L,
%% in that case we have a circular dependency.
- NewCircular = [N1 || N1 <- L, N2 <- Visited, N1 =:= N2],
+ NewCircular = [N || #app{name = N} <- L, N2 <- Visited, N =:= N2],
Circular1 = case NewCircular of
[] -> Circular;
_ -> [Name | NewCircular] ++ Circular
@@ -558,9 +557,9 @@ sort_apps([], Missing, Circular, _) ->
[make_set(Circular), make_set(Missing)]).
find_all(CheckingApp, [Name | Names], Apps, Visited, Found, NotFound) ->
- case lists:keysearch(Name, #app.name, Apps) of
- {value, #app{info = Info} = App} ->
- %% It is OK to have a dependecy like
+ case lists:keyfind(Name, #app.name, Apps) of
+ #app{info = Info} = App ->
+ %% It is OK to have a dependency like
%% X includes Y, Y uses X.
case lists:member(CheckingApp, Info#app_info.incl_apps) of
true ->
@@ -1232,7 +1231,7 @@ do_eval_spec({strip_beam, File}, _OrigSourceDir, SourceDir, TargetDir) ->
reltool_utils:write_file(TargetFile, BeamBin2).
cleanup_spec(List, TargetDir) when is_list(List) ->
- lists:foreach(fun(F)-> cleanup_spec(F, TargetDir) end, List);
+ lists:foreach(fun(F) -> cleanup_spec(F, TargetDir) end, List);
%% cleanup_spec({source_dir, _SourceDir, Spec}, TargetDir) ->
%% cleanup_spec(Spec, TargetDir);
cleanup_spec({create_dir, Dir, Files}, TargetDir) ->
@@ -1444,8 +1443,8 @@ subst([], _Vars, Result) ->
subst_var([$%| Rest], Vars, Result, VarAcc) ->
Key = lists:reverse(VarAcc),
- case lists:keysearch(Key, 1, Vars) of
- {value, {Key, Value}} ->
+ case lists:keyfind(Key, 1, Vars) of
+ {Key, Value} ->
subst(Rest, Vars, lists:reverse(Value, Result));
false ->
subst(Rest, Vars, [$% | VarAcc ++ [$% | Result]])
diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile
index 34781ae720..62fe05238b 100644
--- a/lib/reltool/test/Makefile
+++ b/lib/reltool/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
+# Copyright Ericsson AB 2009-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -73,9 +73,10 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) reltool.spec $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
- $(INSTALL_PROGRAM) rtt $(INSTALL_PROGS) $(RELSYSDIR)
-# chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)
+ $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR)
+ $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR)
+# chmod -R u+w $(RELSYSDIR)
# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/reltool/test/reltool.cover b/lib/reltool/test/reltool.cover
new file mode 100644
index 0000000000..ca425b9f98
--- /dev/null
+++ b/lib/reltool/test/reltool.cover
@@ -0,0 +1,2 @@
+{incl_app,reltool,details}.
+
diff --git a/lib/reltool/test/reltool.spec b/lib/reltool/test/reltool.spec
index 252232e09d..2995720105 100644
--- a/lib/reltool/test/reltool.spec
+++ b/lib/reltool/test/reltool.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../reltool_test"}}.
-
+{suites,"../reltool_test",all}.
diff --git a/lib/reltool/test/reltool_app_SUITE.erl b/lib/reltool/test/reltool_app_SUITE.erl
index f8433f73d0..a6e00cde08 100644
--- a/lib/reltool/test/reltool_app_SUITE.erl
+++ b/lib/reltool/test/reltool_app_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,35 +45,36 @@ init_per_suite(Config) ->
end_per_suite(Config) ->
reltool_test_lib:end_per_suite(Config).
+init_per_testcase(undef_funcs=Case, Config) ->
+ case test_server:is_debug() of
+ true ->
+ {skip,"Debug-compiled emulator -- far too slow"};
+ false ->
+ Config2 = [{tc_timeout, timer:minutes(10)} | Config],
+ reltool_test_lib:init_per_testcase(Case, Config2)
+ end;
init_per_testcase(Case, Config) ->
- Config2 =
- case Case of
- undef_funcs ->
- [{tc_timeout, timer:minutes(10)} | Config];
- _ ->
- Config
- end,
- reltool_test_lib:init_per_testcase(Case, Config2).
+ reltool_test_lib:init_per_testcase(Case, Config).
end_per_testcase(Func,Config) ->
reltool_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Case, Config) ->
- reltool_test_lib:end_per_testcase(Case, Config).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all() ->
- all(suite).
-
-all(suite) ->
- [
- fields,
- modules,
- export_all,
- app_depend,
- undef_funcs
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [fields, modules, export_all, app_depend, undef_funcs].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl
index faf1bdbba2..b77560db94 100644
--- a/lib/reltool/test/reltool_server_SUITE.erl
+++ b/lib/reltool/test/reltool_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,8 +18,9 @@
-module(reltool_server_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -42,25 +43,27 @@ init_per_testcase(Func,Config) ->
reltool_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
reltool_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- reltool_test_lib:end_per_testcase(Func,Config).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- start_server,
- set_config,
- create_release,
- create_script,
- create_target,
- create_embedded,
- create_standalone,
- create_old_target
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start_server, set_config, create_release,
+ create_script, create_target, create_embedded,
+ create_standalone, create_old_target,
+ otp_9135].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% The test cases
@@ -108,6 +111,37 @@ set_config(_Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% OTP-9135, test that app_file option can be set to all | keep | strip
+
+otp_9135(TestInfo) when is_atom(TestInfo) ->
+ reltool_test_lib:tc_info(TestInfo);
+otp_9135(_Config) ->
+ Libs = lists:sort(erl_libs()),
+ StrippedDefaultSys =
+ case Libs of
+ [] -> [];
+ _ -> {lib_dirs, Libs}
+ end,
+
+ Config1 = {sys,[{app_file, keep}]}, % this is the default
+ {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, Config1}])),
+ ?m({ok, {sys,StrippedDefaultSys}}, reltool:get_config(Pid1)),
+ ?m(ok, reltool:stop(Pid1)),
+
+ Config2 = {sys,[{app_file, strip}]},
+ {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, Config2}])),
+ ExpectedConfig2 = StrippedDefaultSys++[{app_file,strip}],
+ ?m({ok, {sys,ExpectedConfig2}}, reltool:get_config(Pid2)),
+ ?m(ok, reltool:stop(Pid2)),
+
+ Config3 = {sys,[{app_file, all}]},
+ {ok, Pid3} = ?msym({ok, _}, reltool:start_server([{config, Config3}])),
+ ExpectedConfig3 = StrippedDefaultSys++[{app_file,all}],
+ ?m({ok, {sys,ExpectedConfig3}}, reltool:get_config(Pid3)),
+ ?m(ok, reltool:stop(Pid3)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Generate releases
create_release(TestInfo) when is_atom(TestInfo) ->
diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl
index 5390b0a75e..b8bcbcd009 100644
--- a/lib/reltool/test/reltool_test_lib.erl
+++ b/lib/reltool/test/reltool_test_lib.erl
@@ -97,7 +97,7 @@ wx_init_per_suite(Config) ->
exit({skipped, "Can not test on MacOSX"});
{unix, _} ->
io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
- case proplists:get_value(xserver, Config, none) of
+ case ct:get_config(xserver, none) of
none -> ignore;
Server -> os:putenv("DISPLAY", Server)
end;
@@ -295,7 +295,7 @@ eval_test_case(Mod, Fun, Config) ->
test_case_evaluator(Mod, Fun, [Config]) ->
NewConfig = Mod:init_per_testcase(Fun, Config),
R = apply(Mod, Fun, [NewConfig]),
- Mod:fin_per_testcase(Fun, NewConfig),
+ Mod:end_per_testcase(Fun, NewConfig),
exit({test_case_ok, R}).
wait_for_evaluator(Pid, Mod, Fun, Config) ->
@@ -311,12 +311,12 @@ wait_for_evaluator(Pid, Mod, Fun, Config) ->
{'EXIT', Pid, {skipped, Reason}} ->
log("<WARNING> Test case ~w skipped, because ~p~n",
[{Mod, Fun}, Reason]),
- Mod:fin_per_testcase(Fun, Config),
+ Mod:end_per_testcase(Fun, Config),
{skip, {Mod, Fun}, Reason};
{'EXIT', Pid, Reason} ->
log("<ERROR> Eval process ~w exited, because\n\t~p~n",
[{Mod, Fun}, Reason]),
- Mod:fin_per_testcase(Fun, Config),
+ Mod:end_per_testcase(Fun, Config),
{crash, {Mod, Fun}, Reason}
end.
diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl
index 2e2b355e07..424bc7d189 100644
--- a/lib/reltool/test/reltool_wx_SUITE.erl
+++ b/lib/reltool/test/reltool_wx_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,8 +18,9 @@
-module(reltool_wx_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -36,16 +37,22 @@ init_per_testcase(Func,Config) ->
reltool_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
reltool_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- reltool_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- start_all_windows
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start_all_windows].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% The test cases
diff --git a/lib/reltool/test/rtt.erl b/lib/reltool/test/rtt.erl
index 6755b8400f..437009e26a 100644
--- a/lib/reltool/test/rtt.erl
+++ b/lib/reltool/test/rtt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk
index b0561a6110..484f84788d 100644
--- a/lib/reltool/vsn.mk
+++ b/lib/reltool/vsn.mk
@@ -1,27 +1 @@
-# This is an -*-makefile-*- file.
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2009-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-
-RELTOOL_VSN = 0.5.3
-
-TICKETS = OTP-8057
-TICKETS_0_5_2 = OTP-8254
-TICKETS_0_5_1 = OTP-8199
-TICKETS_0_5 = OTP-7949
-TICKETS_0_2_2 = OTP-7999
-TICKETS_2_2_1 = OTP-7840
-TICKETS_0_2 = OTP-7805
+RELTOOL_VSN = 0.5.5
diff --git a/lib/runtime_tools/c_src/trace_file_drv.c b/lib/runtime_tools/c_src/trace_file_drv.c
index 482fcc0288..668f6f4af3 100644
--- a/lib/runtime_tools/c_src/trace_file_drv.c
+++ b/lib/runtime_tools/c_src/trace_file_drv.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ * Copyright Ericsson AB 1999-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -520,7 +520,7 @@ static int do_write(FILETYPE fd, unsigned char *buff, int siz) {
*/
static int my_write(TraceFileData *data, unsigned char *buff, int siz)
{
- int wrote, w;
+ int wrote;
if (data->buff_siz - data->buff_pos >= siz) {
memcpy(data->buff + data->buff_pos, buff, siz);
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 9eb13727c7..b27a3a0996 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -31,6 +31,59 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.8.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ When a big number of trace patterns are set by inviso the
+ Erlang VM could get unresponsive for several seconds.
+ This is now corrected.</p>
+ <p>
+ Own Id: OTP-9048 Aux Id: seq11480 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.8.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Minor corrections and removal of a temporary workaround.</p>
+ <p>
+ Own Id: OTP-8755 Aux Id: seq-11628, seq-11639 </p>
+ </item>
+ <item>
+ <p>
+ Small fix in inviso_autostart_server.</p>
+ <p>
+ Own Id: OTP-8783 Aux Id: seq11628 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Runtime_Tools 1.8.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Miscellaneous updates.</p>
+ <p>
+ Own Id: OTP-8705</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.8.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/runtime_tools/doc/src/notes_history.xml b/lib/runtime_tools/doc/src/notes_history.xml
index 587d935e0a..8fe27f619c 100644
--- a/lib/runtime_tools/doc/src/notes_history.xml
+++ b/lib/runtime_tools/doc/src/notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/runtime_tools/doc/src/part_notes_history.xml b/lib/runtime_tools/doc/src/part_notes_history.xml
index cdd727780c..2ce1a5de05 100644
--- a/lib/runtime_tools/doc/src/part_notes_history.xml
+++ b/lib/runtime_tools/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/runtime_tools/doc/src/runtime_tools_app.xml b/lib/runtime_tools/doc/src/runtime_tools_app.xml
index e31c8cb5f7..1fd61b84d8 100644
--- a/lib/runtime_tools/doc/src/runtime_tools_app.xml
+++ b/lib/runtime_tools/doc/src/runtime_tools_app.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/runtime_tools/src/inviso_autostart.erl b/lib/runtime_tools/src/inviso_autostart.erl
index 134133ad1f..787292e244 100644
--- a/lib/runtime_tools/src/inviso_autostart.erl
+++ b/lib/runtime_tools/src/inviso_autostart.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -59,17 +59,10 @@ autostart(_AutoModArgs) ->
case try_load_module(FileNames) of
ok ->
autostart_apply(M,F);
+
false -> % No such module available
"inviso_autostart.config"
end;
- {ok,{gettia_asc,asc_file}} -> % Uggly hack to not have to change in GSN-CPS.
- case try_load_module(["/tmp/DPE_COMMONLOG/gettia_asc",
- "/tmp/DPE_COMMONLOG/gettia_overload"]) of
- ok ->
- autostart_apply(gettia_asc,asc_file);
- false -> % No such module available
- false
- end;
{ok,{M,F}} -> % Use M:F(node())
autostart_apply(M,F);
{ok,no_autostart} ->
diff --git a/lib/runtime_tools/src/inviso_autostart_server.erl b/lib/runtime_tools/src/inviso_autostart_server.erl
index 5af96e4e39..1e352822f4 100644
--- a/lib/runtime_tools/src/inviso_autostart_server.erl
+++ b/lib/runtime_tools/src/inviso_autostart_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -84,7 +84,7 @@ init(ArgsFromConfig) ->
case get_tracerdata_opts(ArgsFromConfig) of
{ok,TracerData} -> % Otherwise we can not start a trace!
case inviso_rt:init_tracing(TracerData) of
- {ok,_} -> % Ok, tracing has been initiated.
+ {ok,_Response} -> % Ok, tracing has been initiated.
case get_cmdfiles_opts(ArgsFromConfig) of
{ok,CmdFiles} -> % List of cmd-files.
Bindings=get_initialbindings_opts(ArgsFromConfig),
@@ -164,11 +164,11 @@ interpret_cmd_files([{FileName,LocalBindings}|Rest],GlobalBindings,Translations,
Bindings=join_local_and_global_vars(LocalBindings,GlobalBindings),
interpret_cmd_files_1(FileName,Bindings,Translations,Dbg),
interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg);
-interpret_cmd_files([FileName|Rest],GlobalBindings,Translations,Dbg) ->
- interpret_cmd_files_1(FileName,GlobalBindings,Translations,Dbg),
- interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg);
interpret_cmd_files([],_,_,_) -> % Done, return nothing significant!
- true.
+ true;
+interpret_cmd_files(FileName,GlobalBindings,Translations,Dbg) ->
+ interpret_cmd_files_1(FileName,GlobalBindings,Translations,Dbg).
+% interpret_cmd_files(Rest,GlobalBindings,Translations,Dbg).
%% This is "inline" inviso calls.
interpret_cmd_files_1({inviso,F,Args},Bindings,Translations,Dbg) ->
diff --git a/lib/runtime_tools/src/inviso_rt.erl b/lib/runtime_tools/src/inviso_rt.erl
index dfab70b42e..ac7ac2a584 100644
--- a/lib/runtime_tools/src/inviso_rt.erl
+++ b/lib/runtime_tools/src/inviso_rt.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1422,7 +1422,17 @@ do_set_trace_patterns(Args,Flags) ->
do_set_trace_patterns_2([{M,F,Arity,MS}|Rest],Flags,Replies) -> % Option-less.
do_set_trace_patterns_2([{M,F,Arity,MS,[]}|Rest],Flags,Replies);
-do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) ->
+do_set_trace_patterns_2(Mlist = [{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M) ->
+ case length(Mlist) rem 10 of
+ 0 ->
+ timer:sleep(100);
+ _ ->
+ ok
+ end,
+ %% sleep 100 ms for every 10:th element in the list to let other
+ %% processes run since this is a potentially
+ %% heavy operation that might result in an unresponsive Erlang VM for
+ %% several seconds otherwise
case load_module_on_option(M,Opts) of
true -> % Already present, loaded or no option!
case catch erlang:trace_pattern({M,F,Arity},MS,Flags) of
@@ -1438,30 +1448,11 @@ do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_atom(M
do_set_trace_patterns_2(Rest,Flags,[0|Replies])
end;
do_set_trace_patterns_2([{M,F,Arity,MS,Opts}|Rest],Flags,Replies) when is_list(M) ->
- case check_pattern_parameters(void,F,Arity,MS) of % We don't want to repeat bad params.
- ok ->
- case inviso_rt_lib:expand_regexp(M,Opts) of % Get a list of real modulnames.
- Mods when is_list(Mods) ->
- MoreReplies=
- do_set_trace_patterns_2(lists:map(fun(Mod)->
- {Mod,F,Arity,MS,Opts}
- end,
- Mods),
- Flags,
- Replies),
- do_set_trace_patterns_2(Rest,Flags,MoreReplies);
- {error,Reason} ->
- do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies])
- end;
- error -> % Bad pattern parameters.
- do_set_trace_patterns_2(Rest,
- Flags,
- [{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies])
- end;
+ do_set_trace_patterns_2([{{void,M},F,Arity,MS,Opts}|Rest],Flags,Replies);
do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies)
when is_list(Dir),is_list(M) ->
- case check_pattern_parameters(void,F,Arity,MS) of % We don't want to repeat bad params.
- ok ->
+ case check_pattern_parameters('_',F,Arity,MS) of % We don't want to repeat bad params.
+ true ->
case inviso_rt_lib:expand_regexp(Dir,M,Opts) of % Get a list of real modulnames.
Mods when is_list(Mods) ->
MoreReplies=
@@ -1475,7 +1466,7 @@ do_set_trace_patterns_2([{{Dir,M},F,Arity,MS,Opts}|Rest],Flags,Replies)
{error,Reason} ->
do_set_trace_patterns_2(Rest,Flags,[{error,Reason}|Replies])
end;
- error -> % Bad pattern parameters.
+ false -> % Bad pattern parameters.
do_set_trace_patterns_2(Rest,
Flags,
[{error,{bad_trace_args,{M,F,Arity,MS}}}|Replies])
@@ -2174,21 +2165,20 @@ check_flags_2([Faulty|_],_Flags) -> {error,{bad_flag,Faulty}}.
%% the function is to avoid to get multiple error return values in the return
%% list for a pattern used together with a regexp expanded module name.
check_pattern_parameters(Mod,Func,Arity,MS) ->
- if
- (Mod=='_') and (Func=='_') and (Arity=='_') and
- (is_list(MS) or (MS==true) or (MS==false)) ->
- ok;
- (is_atom(Mod) and (Mod/='_')) and (Func=='_') and (Arity=='_') and
- (is_list(MS) or (MS==true) or (MS==false)) ->
- ok;
- (is_atom(Mod) and (Mod/='_')) and
- (is_atom(Func) and (Func/='_')) and
- ((Arity=='_') or is_integer(Arity)) and
- (is_list(MS) or (MS==true) or (MS==false)) ->
- ok;
- true ->
- error
- end.
+ MSresult = check_MS(MS),
+ MFAresult = check_MFA(Mod,Func,Arity),
+ MFAresult and MSresult.
+
+check_MS(MS) when is_list(MS) -> true;
+check_MS(true) -> true;
+check_MS(false) -> true.
+
+check_MFA('_','_','_') -> true;
+check_MFA(Mod,'_','_') when is_atom(Mod) -> true;
+check_MFA(Mod,'_',A) when is_atom(Mod), is_integer(A) -> false;
+check_MFA(Mod,F,'_') when is_atom(Mod), is_atom(F) -> true;
+check_MFA(Mod,F,A) when is_atom(Mod), is_atom(F), is_integer(A) -> true.
+
%% -----------------------------------------------------------------------------
%% Help function finding out if Mod is loaded, and if not, if it can successfully
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile
new file mode 100644
index 0000000000..cfaf420d65
--- /dev/null
+++ b/lib/runtime_tools/test/Makefile
@@ -0,0 +1,65 @@
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+MODULES = \
+ runtime_tools_SUITE \
+ inviso_testmodule1_foo \
+ inviso_SUITE \
+ dbg_SUITE \
+ erts_alloc_config_SUITE
+
+ERL_FILES= $(MODULES:%=%.erl)
+
+TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+INSTALL_PROGS= $(TARGET_FILES)
+
+EMAKEFILE=Emakefile
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/runtime_tools_test
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+ERL_MAKE_FLAGS +=
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+
+EBIN = .
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+make_emakefile:
+ $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\
+ > $(EMAKEFILE)
+
+tests debug opt: make_emakefile
+ erl $(ERL_MAKE_FLAGS) -make
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: make_emakefile
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) runtime_tools.spec runtime_tools.cover $(ERL_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(EMAKEFILE) runtime_tools.cover $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
+ @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
new file mode 100644
index 0000000000..bd908c1f3a
--- /dev/null
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -0,0 +1,922 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(dbg_SUITE).
+
+%% Test functions
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ big/1, tiny/1, simple/1, message/1, distributed/1,
+ ip_port/1, file_port/1, file_port2/1, file_port_schedfix/1,
+ ip_port_busy/1, wrap_port/1, wrap_port_time/1,
+ with_seq_trace/1, dead_suspend/1, local_trace/1,
+ saved_patterns/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+-export([tracee1/1, tracee2/1]).
+-export([dummy/0, exported/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog=test_server:timetrap(?default_timeout),
+ [{watchdog, Dog}|Config].
+end_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [big, tiny, simple, message, distributed, ip_port,
+ file_port, file_port2, file_port_schedfix, ip_port_busy,
+ wrap_port, wrap_port_time, with_seq_trace, dead_suspend,
+ local_trace, saved_patterns].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+big(suite) -> [];
+big(doc) -> ["Rudimentary interface test"];
+big(Config) when is_list(Config) ->
+ ?line {ok,OldCurDir} = file:get_cwd(),
+ Datadir=?config(data_dir, Config),
+ Privdir=?config(priv_dir, Config),
+ ?line ok=file:set_cwd(Privdir),
+ try
+ %% make sure dbg is stopped (and returns correctly)
+ ?line ok = dbg:stop(),
+
+ %% compile test module and make sure it is loaded.
+ ?line {ok,Mod} = compile:file(Datadir++"/dbg_test",[trace]),
+ ?line code:purge(dbg_test),
+ ?line {module, Mod}=code:load_file(dbg_test),
+
+ %% run/debug a named test function.
+ ?line Pid = spawn_link(dbg_test, loop, [Config]),
+ ?line true = register(dbg_test_loop, Pid),
+ ?line {ok,_} = dbg:tracer(),
+ ?line {ok,[{matched, _node, 1}]} = dbg:p(dbg_test_loop, [m,p,c]),
+ ?line ok = dbg:c(dbg_test, test, [Config]),
+ ?line ok = dbg:i(),
+ ?line dbg_test_loop ! {dbg_test, stop},
+ unregister(dbg_test_loop),
+ ?line ok = dbg:stop(),
+
+ %% run/debug a Pid.
+ ?line Pid2=spawn_link(dbg_test,loop,[Config]),
+ ?line {ok,_} = dbg:tracer(),
+ ?line {ok,[{matched, _node, 1}]} = dbg:p(Pid2,[s,r,p]),
+ ?line ok = dbg:c(dbg_test, test, [Config]),
+ ?line ok = dbg:i(),
+ ?line Pid2 ! {dbg_test, stop},
+
+ ?line ok=file:set_cwd(OldCurDir)
+ after
+ ?line dbg:stop()
+ end,
+ ok.
+
+
+tiny(suite) -> [];
+tiny(doc) -> ["Rudimentary interface test"];
+tiny(Config) when is_list(Config) ->
+ ?line {ok,OldCurDir} = file:get_cwd(),
+ Datadir=?config(data_dir, Config),
+ Privdir=?config(priv_dir, Config),
+ ?line ok=file:set_cwd(Privdir),
+ try
+ %% compile test module and make sure it is loaded.
+ ?line {ok, Mod} = compile:file(Datadir++"/dbg_test",[trace]),
+ ?line code:purge(dbg_test),
+ ?line {module, Mod}=code:load_file(dbg_test),
+
+ ?line Pid=spawn_link(dbg_test,loop,[Config]),
+ if
+ is_pid(Pid) ->
+ ?line dbg:tracer(),
+ ?line {ok,[{matched, _node, 1}]} = dbg:p(Pid,[s,r,m,p,c]),
+ ?line ok = dbg:c(dbg_test,test,[Config]),
+ ?line ok = dbg:i(),
+ ?line Pid ! {dbg_test, stop};
+ true ->
+ ?line ok=file:set_cwd(OldCurDir),
+ ?t:fail("Could not spawn external test process.~n"),
+ failure
+ end
+ after
+ ?line ok = dbg:stop(),
+ ?line ok = file:set_cwd(OldCurDir)
+ end,
+ ok.
+
+simple(suite) ->
+ [];
+simple(doc) ->
+ ["Simple interface test with own handler"];
+simple(Config) when is_list(Config) ->
+ try
+ ?line start(),
+ ?line dbg:p(self(),call),
+ ?line dbg:tp(dbg,ltp,[]),
+ ?line dbg:ltp(),
+ ?line stop(),
+ ?line S = self(),
+ ?line [{trace,S,call,{dbg,ltp,[]}}] = flush()
+ after
+ ?line dbg:stop()
+ end,
+ ok.
+
+message(suite) ->
+ [];
+message(doc) ->
+ ["Simple interface test with pam code that appends a message"];
+message(Config) when is_list(Config) ->
+ ?line {ok, _} = start(),
+ try
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line {ok, X} = dbg:tp(dbg,ltp,[{'_',[],[{message, {self}}]}]),
+ ?line {value, {saved, Saved}} = lists:keysearch(saved, 1, X),
+ ?line {ok, Y} = dbg:tp(dbg,ln,Saved),
+ ?line {value, {saved, Saved}} = lists:keysearch(saved, 1, Y),
+ ?line ok = dbg:ltp(),
+ ?line ok = dbg:ln()
+ after
+ ?line stop()
+ end,
+ ?line S = self(),
+ ?line [{trace,S,call,{dbg,ltp,[]},S},
+ {trace,S,call,{dbg,ln,[]},S}] = flush(),
+ ok.
+
+distributed(suite) ->
+ [];
+distributed(doc) ->
+ ["Simple test of distributed tracing"];
+distributed(Config) when is_list(Config) ->
+ ?line {ok, _} = start(),
+ ?line Node = start_slave(),
+ try
+ ?line RexPid = rpc:call(Node, erlang, whereis, [rex]),
+ ?line RexPidList = pid_to_list(RexPid),
+ ?line {ok, Node} = dbg:n(Node),
+ ?line {ok, X} = dbg:p(all,call),
+ ?line {value, {matched, Node, _}} = lists:keysearch(Node, 2, X),
+ ?line {ok, Y} = dbg:p(RexPidList, s),
+ ?line {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Y),
+ ?line {ok, Z} = dbg:tp(dbg,ltp,[]),
+ ?line {value, {matched, Node, 1}} = lists:keysearch(Node, 2, Z),
+ ?line dbg:cn(Node),
+ ?line dbg:tp(dbg,ln,[]),
+ ?line ok = rpc:call(Node, dbg, ltp, []),
+ ?line ok = rpc:call(Node, dbg, ln, []),
+ ?line ok = dbg:ln(),
+ ?line S = self(),
+ ?line {TraceSend, TraceCall} =
+ lists:partition(fun ({trace,RP,send,_,_}) when RP =:= RexPid -> true;
+ (_) -> false end,
+ flush()),
+ ?line [_|_] = TraceSend,
+ ?line [{trace,Pid,call,{dbg,ltp,[]}},
+ {trace,S,call,{dbg,ln,[]}}] = TraceCall,
+ ?line Node = node(Pid),
+ %%
+ ?line stop()
+ after
+ ?line stop_slave(Node),
+ ?line stop()
+ end,
+ ok.
+
+
+local_trace(suite) ->
+ [];
+local_trace(doc) ->
+ ["Tests tracing of local function calls."];
+local_trace(Config) when is_list(Config) ->
+ ?line {ok, _} = start(),
+ try
+ ?line S = self(),
+ ?line %% Split "<X.Y.Z>" into {X, Y, Z}
+ ?line "<"++L1 = L = pid_to_list(S),
+ ?line NoDot = fun ($.) -> false; (_) -> true end,
+ ?line {LX,"."++L2} = lists:splitwith(NoDot, L1),
+ ?line {LY,"."++L3} = lists:splitwith(NoDot, L2),
+ ?line ">"++L4 = lists:reverse(L3),
+ ?line LZ = lists:reverse(L4),
+ ?line X = 0 = list_to_integer(LX),
+ ?line Y = list_to_integer(LY),
+ ?line Z = list_to_integer(LZ),
+ ?line XYZ = {X, Y, Z},
+ ?line io:format("Self = ~w = ~w~n", [S,XYZ]),
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(S,call),
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(XYZ,call),
+ if Z =:= 0 ->
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(Y,call);
+ true -> ok
+ end,
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(L,call),
+ ?line {ok, _} = dbg:tpl(?MODULE,not_exported,[]),
+ ?line 4 = not_exported(2),
+ ?line [{trace,S,call,{?MODULE,not_exported,[2]}}] = flush(),
+ ?line {ok, _} = dbg:tp(?MODULE,exported,[]),
+ ?line 4 = ?MODULE:exported(2),
+ ?line [{trace,S,call,{?MODULE,exported,[2]}},
+ {trace,S,call,{?MODULE,not_exported,[2]}}] = flush(),
+ ?line {ok, _} = dbg:ctpl(?MODULE),
+ ?line 4 = ?MODULE:exported(2),
+ ?line [{trace,S,call,{?MODULE,exported,[2]}}] = flush(),
+ ?line {ok, _} = dbg:tpl(?MODULE,not_exported,[]),
+ ?line {ok, _} = dbg:ctp(?MODULE),
+ ?line 4 = ?MODULE:exported(2),
+ ?line [] = flush(),
+ ?line {ok, _} = dbg:tpl(?MODULE,not_exported,x),
+ ?line catch ?MODULE:exported(x),
+ ?line [{trace,S,call,{dbg_SUITE,not_exported,[x]}},
+ {trace,S,exception_from,
+ {dbg_SUITE,not_exported,1},
+ {error,badarith}}] = flush()
+ after
+ ?line stop()
+ end,
+ ok.
+
+saved_patterns(suite) ->
+ [];
+saved_patterns(doc) ->
+ ["Tests saving of match_spec's."];
+saved_patterns(Config) when is_list(Config) ->
+ ?line dbg:stop(),
+ ?line {ok,[{saved,1}]} =
+ dbg:tp(dbg,ctp,1,[{'_',[],[{message, blahonga}]}]),
+ ?line {ok,[{saved,2}]} =
+ dbg:tp(dbg,ctp,1,[{['_'],[],[{message, blahonga}]}]),
+ ?line Privdir=?config(priv_dir, Config),
+ ?line file:make_dir(Privdir),
+ ?line File = filename:join([Privdir, "blahonga.ms"]),
+ ?line dbg:wtp(File),
+ ?line dbg:stop(),
+ ?line dbg:ctp('_','_','_'),
+ ?line {ok, _} = start(),
+ try
+ ?line dbg:rtp(File),
+ ?line {ok,[{matched,_node,1},{saved,1}]} = dbg:tp(dbg,ltp,0,1),
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line dbg:ltp(),
+ ?line S = self(),
+ ?line [{trace,S,call,{dbg,ltp,[]},blahonga}] = flush()
+ after
+ ?line stop()
+ end,
+ ok.
+
+
+
+not_exported(N) ->
+ N * 2.
+
+exported(N) ->
+ not_exported(N).
+
+ip_port(suite) ->
+ [];
+ip_port(doc) ->
+ ["Test tracing to IP port"];
+ip_port(Config) when is_list(Config) ->
+ ?line stop(),
+ ?line Port = dbg:trace_port(ip, 0),
+ ?line {ok, _} = dbg:tracer(port, Port),
+ try
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
+ ?line {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y),
+ ?line ok = dbg:ltp(),
+ ?line ok = dbg:ln(),
+ ?line {ok, IpPort} = dbg:trace_port_control(get_listen_port),
+ ?line io:format("IpPort = ~p~n", [IpPort]),
+ ?line dbg:trace_client(ip, IpPort, {fun myhandler/2, self()}),
+ ?line S = self(),
+ ?line [{trace,S,call,{dbg,ltp,[]},S},
+ {trace,S,call,{dbg,ln,[]},hej}] = flush()
+ after
+ ?line stop()
+ end,
+ ok.
+
+
+
+ip_port_busy(suite) ->
+ [];
+ip_port_busy(doc) ->
+ ["Test that the dbg server does not hang if the tracer don't start ",
+ "(OTP-3592)"];
+ip_port_busy(Config) when is_list(Config) ->
+ ?line stop(),
+ ?line Tracer = dbg:trace_port(ip, 4745),
+ ?line Port = Tracer(),
+ ?line {error, Reason} = dbg:tracer(port, Tracer),
+ try
+ ?line io:format("Error reason = ~p~n", [Reason]),
+ ?line true = port_close(Port)
+ after
+ ?line dbg:stop()
+ end,
+ ?line ok.
+
+
+
+file_port(suite) ->
+ [];
+file_port(doc) ->
+ ["Test tracing to file port (simple)"];
+file_port(Config) when is_list(Config) ->
+ ?line stop(),
+ ?line {A,B,C} = erlang:now(),
+ ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
+ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ ?line Port = dbg:trace_port(file, FName),
+ ?line {ok, _} = dbg:tracer(port, Port),
+ try
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line {ok, X} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
+ ?line {ok, Y} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y),
+ ?line ok = dbg:ltp(),
+ ?line ok = dbg:ln(),
+ ?line stop(),
+ ?line dbg:trace_client(file, FName, {fun myhandler/2, self()}),
+ ?line S = self(),
+ ?line [{trace,S,call,{dbg,ltp,[]},S},
+ {trace,S,call,{dbg,ln,[]},hej},
+ end_of_trace] = flush()
+ after
+ ?line stop(),
+ ?line file:delete(FName)
+ end,
+ ok.
+
+file_port2(suite) ->
+ [];
+file_port2(doc) ->
+ ["Test tracing to file port with 'follow_file'"];
+file_port2(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ {skipped, "VxWorks NFS cache ruins it all."};
+ _ ->
+ ?line stop(),
+ ?line {A,B,C} = erlang:now(),
+ ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
+ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ %% Ok, lets try with flush and follow_file, not a chance on VxWorks
+ %% with NFS caching...
+ ?line Port2 = dbg:trace_port(file, FName),
+ ?line {ok, _} = dbg:tracer(port, Port2),
+ try
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line {ok, _} = dbg:tp(dbg, ltp,[{'_',[],[{message, {self}}]}]),
+ ?line {ok, _} = dbg:tp(dbg, ln, [{'_',[],[{message, hej}]}]),
+ ?line ok = dbg:ltp(),
+ ?line ok = dbg:flush_trace_port(),
+ ?line dbg:trace_client(follow_file, FName,
+ {fun myhandler/2, self()}),
+ ?line S = self(),
+ ?line [{trace,S,call,{dbg,ltp,[]},S}] = flush(),
+ ?line ok = dbg:ln(),
+ ?line ok = dbg:flush_trace_port(),
+ ?line receive after 1000 -> ok end, %% Polls every second...
+ ?line [{trace,S,call,{dbg,ln,[]},hej}] = flush(),
+ ?line stop(),
+ ?line [] = flush()
+ after
+ ?line stop(),
+ ?line file:delete(FName)
+ end,
+ ok
+ end.
+
+file_port_schedfix(suite) ->
+ [];
+file_port_schedfix(doc) ->
+ ["Test that the scheduling timestamp fix for trace flag 'running' works."];
+file_port_schedfix(Config) when is_list(Config) ->
+ ?line case (catch erlang:system_info(smp_support)) of
+ true ->
+ {skip, "No schedule fix on SMP"};
+ _ ->
+ try
+ file_port_schedfix1(Config)
+ after
+ dbg:stop()
+ end
+ end.
+file_port_schedfix1(Config) when is_list(Config) ->
+ ?line stop(),
+ ?line {A,B,C} = erlang:now(),
+ ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++
+ "-" ++ integer_to_list(B) ++ "-" ++ integer_to_list(C),
+ ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ %%
+ ?line Port = dbg:trace_port(file, {FName, wrap, ".wraplog", 8*1024, 4}),
+ ?line {ok, _} = dbg:tracer(port, Port),
+ ?line {ok,[{matched,_node,0}]} = dbg:p(new,[running,procs,send,timestamp]),
+ %%
+ %% Generate the trace data
+ %%
+ %% This starts 3 processes that sends a message to each other in a ring,
+ %% 4 laps. Prior to sending the message to the next in the ring, each
+ %% process send 8 messages to itself, just to generate some trace data,
+ %% and to lower the possibility that the trace log wraps just after
+ %% a schedule out message (which would not burden any process and hence
+ %% not show up in the result)
+ %%
+ %% The wrap file trace is used because it burns a lot of time when the
+ %% driver swaps files, a lot more than the regular file trace. The test
+ %% case is dimensioned so that the log fills two files and just starts
+ %% on the third (out of four wrap files). This gives two file swaps,
+ %% and there are three processes, so one process will NOT be burdened.
+ %% The criterion for trace success is then that the max process
+ %% execution time must not be more than twice the min process
+ %% execution time. Wallclock. A normal result is about 10 times more
+ %% without schedule in - schedule out compensation (OTP-3938).
+ %%
+ ?line ok = token_volleyball(3, 4, 8),
+ %%
+ ?line {ok,[{matched,_,_}]} = dbg:p(all, [clear]),
+ ?line stop(),
+ % Some debug code to run on all platforms, for finding the fault on genny
+ % Dont touch please /PaN
+ ?line io:format("Trace dump by PaN BEGIN~n"),
+ ?line dbg:trace_client(file,{FName, wrap, ".wraplog"},{fun(end_of_trace,Pid)-> Pid ! done; (Mesg,Pid) -> io:format("~w~n",[Mesg]),Pid end,self()}),
+ receive done -> ok end,
+ ?line io:format("Trace dump by PaN END~n"),
+ %%
+ %% Get the trace result
+ %%
+ ?line Tag = make_ref(),
+ ?line dbg:trace_client(file, {FName, wrap, ".wraplog"},
+ {fun schedstat_handler/2, {self(), Tag, []}}),
+ ?line Result =
+ receive
+ {Tag, D} ->
+ lists:map(
+ fun({Pid, {A1, B1, C1}}) ->
+ {Pid, C1/1000000 + B1 + A1*1000000}
+ end,
+ D)
+ end,
+ ?line ok = io:format("Result=~p", [Result]),
+% erlang:display({?MODULE, ?LINE, Result}),
+ %%
+ %% Analyze the result
+ %%
+ ?line {Min, Max} =
+ lists:foldl(
+ fun({_Pid, M}, {Mi, Ma}) ->
+ {if M < Mi -> M; true -> Mi end,
+ if M > Ma -> M; true -> Ma end}
+ end,
+ {void, 0},
+ Result),
+ % More PaN debug
+ ?line io:format("Min = ~f, Max = ~f~n",[Min,Max]),
+ %%
+ %% Cleanup
+ %%
+ ?line ToBeDeleted = filelib:wildcard(FName++"*"++".wraplog"),
+ ?line lists:map({file, delete}, ToBeDeleted),
+% io:format("ToBeDeleted=~p", [ToBeDeleted]),
+ %%
+ %% Present the result
+ %%
+ P = (Max / Min - 1) * 100,
+ BottomLine = lists:flatten(io_lib:format("~.2f %", [P])),
+ if P > 100 ->
+ Reason = {BottomLine, '>', "100%"},
+ erlang:display({file_port_schedfix, fail, Reason}),
+ test_server:fail(Reason);
+ true ->
+ {comment, BottomLine}
+ end.
+
+wrap_port(suite) ->
+ [];
+wrap_port(doc) ->
+ ["Test tracing to wrapping file port"];
+wrap_port(Config) when is_list(Config) ->
+ ?line Self = self(),
+ ?line stop(),
+ ?line {A,B,C} = erlang:now(),
+ ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
+ integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-",
+ ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ ?line FNameWildcard = FName++"*"++".trace",
+ %% WrapSize=0 and WrapCnt=11 will force the trace to wrap after
+ %% every trace message, and to contain only the last 10 entries
+ %% after trace stop since the last file will be empty waiting
+ %% for its first trace message.
+ ?line WrapSize = 0,
+ ?line WrapCnt = 11,
+ ?line WrapFilesSpec = {FName, wrap, ".trace", WrapSize, WrapCnt},
+ ?line wrap_port_init(WrapFilesSpec),
+ %% The number of iterations, N, is tested to place wrap the log,
+ %% giving a gap in the filename sequence at index 3.
+ %% This should be a difficult case for
+ %% the trace_client file sorting functionality.
+ N = 7,
+ ?line lists:foreach(
+ fun(Cnt) ->
+ ?MODULE:tracee1(Cnt),
+ ?MODULE:tracee2(Cnt)
+ end,
+ lists:seq(1, N)),
+ ?line stop(),
+ try
+ ?line Files1 = filelib:wildcard(FNameWildcard),
+ ?line io:format("~p~n", [Files1]),
+ ?line Tc1 = dbg:trace_client(file, WrapFilesSpec,
+ {fun myhandler/2, {wait_for_go,Self}}),
+ ?line Tref1 = erlang:monitor(process, Tc1),
+ Tc1 ! {go,Self},
+ ?line [{'DOWN',Tref1,_,_,normal},
+ end_of_trace
+ |Result] = lists:reverse(flush()),
+ ?line M = N - (WrapCnt-1) div 2,
+ ?line M = wrap_port_result(Result, Self, N),
+ %%
+ %% Start a new wrap log with the same name to verify that
+ %% all files are cleared at wrap log start. Only produce
+ %% two trace messages to also place the gap at index 3,
+ %% so the trace log will be misinterpreted.
+ %%
+ ?line wrap_port_init(WrapFilesSpec),
+ ?line Files2 = filelib:wildcard(FNameWildcard),
+ ?line io:format("~p~n", [Files2]),
+ ?line -1 = ?MODULE:tracee1(-1),
+ ?line -1 = ?MODULE:tracee2(-1),
+ ?line stop(),
+ ?line Files = filelib:wildcard(FNameWildcard),
+ ?line io:format("~p~n", [Files]),
+ ?line Tc2 = dbg:trace_client(file, WrapFilesSpec,
+ {fun myhandler/2, {wait_for_go,Self}}),
+ ?line Tref2 = erlang:monitor(process, Tc2),
+ Tc2 ! {go,Self},
+ ?line [{trace,Self,call,{?MODULE,tracee1,[-1]},Self},
+ {trace,Self,call,{?MODULE,tracee2,[-1]},hej},
+ end_of_trace,
+ {'DOWN',Tref2,_,_,normal}] = flush(),
+ %%
+ ?line lists:map(fun(F) -> file:delete(F) end, Files)
+ after
+ ?line stop()
+ end,
+ ok.
+
+wrap_port_init(WrapFilesSpec) ->
+ ?line Port = dbg:trace_port(file, WrapFilesSpec),
+ ?line {ok, _} = dbg:tracer(port, Port),
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]),
+ ?line {value, {saved, _Saved}} = lists:keysearch(saved, 1, X),
+ ?line {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]),
+ ?line {value, {saved, _}} = lists:keysearch(saved, 1, Y),
+ ok.
+
+tracee1(X) ->
+ X.
+
+tracee2(X) ->
+ X.
+
+wrap_port_result([], _S, M) ->
+ M;
+wrap_port_result([{trace, S, call, {?MODULE, tracee2, [M]}, hej},
+ {trace, S, call, {?MODULE, tracee1, [M]}, S} | Tail],
+ S,
+ M) ->
+ wrap_port_result(Tail, S, M-1).
+
+
+wrap_port_time(suite) ->
+ [];
+wrap_port_time(doc) ->
+ ["Test tracing to time limited wrapping file port"];
+wrap_port_time(Config) when is_list(Config) ->
+ ?line stop(),
+ ?line {A,B,C} = erlang:now(),
+ ?line FTMP = atom_to_list(?MODULE) ++ integer_to_list(A) ++ "-" ++
+ integer_to_list(B) ++ "-" ++ integer_to_list(C) ++ "-",
+ ?line FName = filename:join([?config(data_dir, Config), FTMP]),
+ %% WrapTime=2 and WrapCnt=4 will force the trace to wrap after
+ %% every 2 seconds, and to contain between 3*2 and 4*2 seconds
+ %% of trace entries.
+ ?line WrapFilesSpec = {FName, wrap, ".trace", {time, 2000}, 4},
+ ?line Port = dbg:trace_port(file, WrapFilesSpec),
+ ?line {ok, _} = dbg:tracer(port, Port),
+ try
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line {ok, X} = dbg:tp(?MODULE, tracee1,[{'_',[],[{message, {self}}]}]),
+ ?line {value, {saved, _Saved1}} = lists:keysearch(saved, 1, X),
+ ?line {ok, Y} = dbg:tp(?MODULE, tracee2, [{'_',[],[{message, hej}]}]),
+ ?line {value, {saved, _Saved2}} = lists:keysearch(saved, 1, Y),
+ %% The delays in the iterations places two trace messages in each
+ %% trace file, but the last which is empty waiting for its first
+ %% trace message. The number of iterations is chosen so that
+ %% one trace file has been wasted, and therefore the first pair
+ %% of trace messages.
+ ?line lists:foreach(
+ fun(Cnt) ->
+ receive after 1000 -> ok end,
+ ?MODULE:tracee1(Cnt),
+ ?MODULE:tracee2(Cnt),
+ receive after 1100 -> ok end
+ end,
+ lists:seq(1, 4)),
+ ?line stop(),
+ ?line Files = filelib:wildcard(FName ++ "*" ++ ".trace"),
+ ?line io:format("~p~n", [Files]),
+ ?line dbg:trace_client(file, WrapFilesSpec, {fun myhandler/2, self()}),
+ ?line S = self(),
+ ?line [{trace, S, call, {?MODULE, tracee1, [2]}, S},
+ {trace, S, call, {?MODULE, tracee2, [2]}, hej},
+ {trace, S, call, {?MODULE, tracee1, [3]}, S},
+ {trace, S, call, {?MODULE, tracee2, [3]}, hej},
+ {trace, S, call, {?MODULE, tracee1, [4]}, S},
+ {trace, S, call, {?MODULE, tracee2, [4]}, hej},
+ end_of_trace] = flush(),
+ ?line lists:map(fun(F) -> file:delete(F) end, Files)
+ after
+ ?line stop()
+ end,
+ ok.
+
+with_seq_trace(suite) ->
+ [];
+with_seq_trace(doc) ->
+ ["Test ordinary tracing combined with seq_trace"];
+with_seq_trace(Config) when is_list(Config) ->
+ try
+ ?line {ok, Server} = start(),
+ ?line {ok, Tracer} = dbg:get_tracer(),
+ ?line {ok, X} = dbg:tp(dbg, get_tracer, [{[],[],
+ [{set_seq_token, send, true}]}]),
+ ?line {value, {saved, _}} = lists:keysearch(saved, 1, X),
+ ?line {ok, [{matched, _node, 1}]} = dbg:p(self(),call),
+ ?line seq_trace:set_system_tracer(Tracer),
+ ?line dbg:get_tracer(),
+ receive
+ after 1 ->
+ ok
+ end,
+ ?line S = self(),
+ ?line ThisNode = node(),
+ ?line [{trace,S,call,{dbg,get_tracer,[]}},
+ {seq_trace,0,{send,_,S,Server,{S,{get_tracer,ThisNode}}}},
+ {seq_trace,0,{send,_,Server,S,{dbg,{ok,Tracer}}}}] =
+ flush()
+ after
+ ?line stop()
+ end,
+ ok.
+
+dead_suspend(suite) ->
+ [];
+dead_suspend(doc) ->
+ ["Test that trace messages concerning a now dead process does "
+ "not crash dbg."];
+
+dead_suspend(Config) when is_list(Config) ->
+ ?line start(),
+ try
+ survived = run_dead_suspend()
+ after
+ ?line stop()
+ end.
+
+run_dead_suspend() ->
+ dbg:p(new, call),
+ dbg:tp(?MODULE, dummy, []),
+ spawn(?MODULE, dummy, []),
+ spawn(?MODULE, dummy, []),
+ spawn(?MODULE, dummy, []),
+ spawn(?MODULE, dummy, []),
+ spawn(?MODULE, dummy, []),
+ receive after 1000 -> ok end,
+ case whereis(dbg) of
+ undefined ->
+ died;
+ _ ->
+ survived
+ end.
+
+dummy() ->
+ ok.
+
+
+%%
+%% Support functions
+%%
+
+start_slave() ->
+ {A, B, C} = now(),
+ Name = "asdkxlkmd" ++ integer_to_list(A+B+C),
+ {ok, Node} = test_server:start_node(Name,slave,[]),
+ ok = wait_node(Node, 15),
+ Node.
+
+stop_slave(Node) ->
+ test_server:stop_node(Node).
+
+wait_node(_,0) ->
+ no;
+wait_node(Node, N) ->
+ case net_adm:ping(Node) of
+ pong ->
+ ok;
+ pang ->
+ receive
+ after 1000 ->
+ ok
+ end,
+ wait_node(Node, N - 1)
+ end.
+
+myhandler(Message, {wait_for_go,Pid}) ->
+ receive
+ {go,Pid} ->
+ myhandler(Message, Pid)
+ end;
+myhandler(Message, Relay) ->
+ Relay ! Message,
+ case Message of
+ end_of_trace ->
+ ok;
+ _ ->
+ Relay
+ end.
+
+flush() ->
+ flush([]).
+flush(Acc) ->
+ receive
+ X ->
+ flush(Acc ++ [X])
+ after 1000 ->
+ Acc
+ end.
+
+start() ->
+ stop(),
+ dbg:tracer(process, {fun myhandler/2, self()}).
+
+stop() ->
+ dbg:stop().
+
+
+
+schedstat_handler(TraceMsg, {Parent, Tag, Data} = State) ->
+ case TraceMsg of
+ {trace_ts, Pid, in, _, Ts} ->
+ NewData =
+ case lists:keysearch(Pid, 1, Data) of
+ {value, {Pid, Acc}} ->
+ [{Pid, Acc, Ts} | lists:keydelete(Pid, 1, Data)];
+ false ->
+ [{Pid, {0, 0, 0}, Ts} | Data];
+ Other ->
+ exit(Parent, {?MODULE, ?LINE, Other}),
+ erlang:display({?MODULE, ?LINE, Other}),
+ Data
+ end,
+ {Parent, Tag, NewData};
+ {trace_ts, Pid, out, _, {A3, B3, C3}} ->
+ NewData =
+ case lists:keysearch(Pid, 1, Data) of
+ {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} ->
+ [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} |
+ lists:keydelete(Pid, 1, Data)];
+ Other ->
+ exit(Parent, {?MODULE, ?LINE, Other}),
+ erlang:display({?MODULE, ?LINE, Other}),
+ Data
+ end,
+ {Parent, Tag, NewData};
+ {trace_ts, Pid, exit, normal, {A3, B3, C3}} ->
+ NewData =
+ case lists:keysearch(Pid, 1, Data) of
+ {value, {Pid, {A1, B1, C1}, {A2, B2, C2}}} ->
+ [{Pid, {A3-A2+A1, B3-B2+B1, C3-C2+C1}} |
+ lists:keydelete(Pid, 1, Data)];
+ {value, {Pid, _Acc}} ->
+ Data;
+ false ->
+ [{Pid, {0, 0, 0}} | Data];
+ Other ->
+ exit(Parent, {?MODULE, ?LINE, Other}),
+ erlang:display({?MODULE, ?LINE, Other}),
+ Data
+ end,
+ {Parent, Tag, NewData};
+ {trace_ts, _Pid, send, _Msg, _OtherPid, _Ts} ->
+ State;
+ end_of_trace ->
+ Parent ! {Tag, Data},
+ State
+ end.
+
+
+
+pass_token(Token, Next, Loops) ->
+ receive
+ {Token, 1} = Msg ->
+ sendloop(Loops),
+ Next ! Msg;
+ {Token, _Cnt} = Msg->
+ sendloop(Loops),
+ Next ! Msg,
+ pass_token(Token, Next, Loops)
+ end.
+
+pass_token(Token, Final, Cnt, Loops) ->
+ receive
+ {Token, start, Next} ->
+ sendloop(Loops),
+ Msg = {Token, Cnt},
+ Next ! Msg,
+ pass_token(Token, Final, Next, Cnt, Loops)
+ end.
+
+pass_token(Token, Final, Next, Cnt, Loops) ->
+ receive
+ {Token, 1} ->
+ sendloop(Loops),
+ Msg = {Token, done},
+ Final ! Msg;
+ {Token, Cnt} ->
+ sendloop(Loops),
+ NextCnt = Cnt-1,
+ Msg = {Token, NextCnt},
+ Next ! Msg,
+ pass_token(Token, Final, Next, NextCnt, Loops)
+ end.
+
+sendloop(Loops) ->
+ sendloop(make_ref(), Loops).
+
+sendloop(_Tag, 0) ->
+ ok;
+sendloop(Tag, Loops) ->
+ self() ! {Tag, Loops},
+ receive {Tag, Loops} -> ok end,
+ sendloop(Tag, Loops-1).
+
+token_volleyball(N, Cnt, Loops)
+ when is_integer(N), N >= 1, is_integer(Cnt), Cnt >= 1,
+ is_integer(Loops), Loops >= 0 ->
+ Self = self(),
+ Token = make_ref(),
+ Last = spawn_link(fun() -> pass_token(Token, Self, Cnt, Loops) end),
+ First = token_volleyball(Token, Last, N-1, Loops),
+ Last ! {Token, start, First},
+ receive {Token, done} -> ok end.
+
+token_volleyball(Token, Next, 1, Loops) ->
+ spawn_link(fun() -> pass_token(Token, Next, Loops) end);
+token_volleyball(Token, Next, N, Loops) ->
+ Pid = spawn_link(fun() -> pass_token(Token, Next, Loops) end),
+ token_volleyball(Token, Pid, N-1, Loops).
diff --git a/lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl b/lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl
new file mode 100644
index 0000000000..2edbf6f99a
--- /dev/null
+++ b/lib/runtime_tools/test/dbg_SUITE_data/dbg_test.erl
@@ -0,0 +1,87 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%%----------------------------------------------------------------------
+%%% Purpose : A priority queue.
+%%%----------------------------------------------------------------------
+%%% This module implements a priority queue as defined in
+%%% "Priority Queues and the STL" by Mark Nelson in Dr.Dobb's Journal, Jan 1996
+%%% see http://web2.airmail.net/markn/articles/pq_stl/priority.htm for more
+%%% information. (A heap implementation is planned aswell)
+%%%----------------------------------------------------------------------
+%%% The items of the queue is kept priority sorted, and because of that,
+%%% a push() operation costs more than a pop() operation (wich only
+%%% needs to return the top item of the queue(read: list)).
+%%%----------------------------------------------------------------------
+%%% The priority queue can be deceptively nice to use when creating for
+%%% example a Huffman coding tree.
+%%% See http://web2.airmail.net/markn/articles/pq_stl/priority.htm or
+%%% Dr.Dobb's Journal Jan, 96 for more information on this.
+%%%----------------------------------------------------------------------
+
+%%% Used here strictly as something for dbg_SUITE to run.
+
+-module(dbg_test).
+-export([test/1, loop/1]).
+-export([new/0, push/3, pop/1]).
+
+loop(Config) ->
+ test(Config),
+ receive
+ {dbg_test, stop} ->
+ ok;
+ Other ->
+ loop(Config)
+ end,
+ ok.
+
+test(Config) ->
+ Q1=new(),
+ Q2=push(Q1, "monkey", 3),
+ Q3=push(Q2, "banana", 4),
+ Q4=push(Q3, "jungle", 2),
+ Q5=push(Q4, "world", 5),
+ Q6=push(Q5, "universe",6),
+ Q7=push(Q6, "peanut", 1),
+% io:format("~p~n",[Q7]),
+ {Itm, Q8}=pop(Q7),
+ ok.
+
+%% Returns a new priority queue.
+new() ->
+ [].
+
+%% Pushes a new item with a set priority into the queue.
+push(Queue, Itm, Pri) ->
+ insert(Queue, Itm, Pri, []).
+
+%% Pops the item with the highest priority out of the queue.
+pop([{Itm, Pri}|Queue]) ->
+ {Itm, Queue}.
+
+%% --- -- -
+%% Support functions.
+insert([], Itm, Pri, NewQ) ->
+ lists:flatten([lists:reverse(NewQ)|[{Itm, Pri}]]);
+% Itm>QItm>NewQ>Queue
+insert([{QItm,QPri}|Queue], Itm, Pri, NewQ) when Pri>QPri->
+ A = [{Itm, Pri}|[{QItm, QPri}]],
+ lists:flatten([[A|NewQ]|Queue]);
+insert([QItm|Rest], Itm, Pri, NewQ) ->
+ insert(Rest, Itm, Pri, [QItm|NewQ]).
+%% --- -- -
diff --git a/lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl b/lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl
new file mode 100644
index 0000000000..85faf620aa
--- /dev/null
+++ b/lib/runtime_tools/test/dbg_SUITE_data/exref_td.erl
@@ -0,0 +1,50 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(exref_td).
+
+-export([a/1,b/1,c/1,d/1,e/1,my_analyse/1]).
+
+a("edcba") ->
+ true;
+a(_) ->
+ lists:reverse("abcde").
+
+b(_) ->
+ a(nil).
+
+c(_) ->
+ nonexistentmodule:f().
+
+d(_) ->
+ lists:nonexistentfunction().
+
+e(_) ->
+ [a(x),b(x),c(x)].
+
+localfuncnotcalled() ->
+ true.
+
+localfunccalledbyf(A) ->
+ A.
+
+f() ->
+ lists:filter(fun localfunccalledbyf/1,"aaabba"),
+ F = fun localfuncnotcalled/0.
+
+my_analyse(Graph) -> ok.
diff --git a/lib/runtime_tools/test/erts_alloc_config_SUITE.erl b/lib/runtime_tools/test/erts_alloc_config_SUITE.erl
new file mode 100644
index 0000000000..8ea04e1767
--- /dev/null
+++ b/lib/runtime_tools/test/erts_alloc_config_SUITE.erl
@@ -0,0 +1,226 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erts_alloc_config_SUITE).
+
+%-define(line_trace, 1).
+
+-include_lib("test_server/include/test_server.hrl").
+
+%-compile(export_all).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
+
+%% Testcases
+-export([basic/1]).
+
+%% internal export
+-export([make_basic_config/1]).
+
+-define(DEFAULT_TIMEOUT, ?t:minutes(2)).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_testcase(Case, Config) when is_list(Config) ->
+ [{testcase, Case},
+ {watchdog, ?t:timetrap(?DEFAULT_TIMEOUT)},
+ {erl_flags_env, save_env()} | Config].
+
+end_per_testcase(_Case, Config) when is_list(Config) ->
+ ?t:timetrap_cancel(?config(watchdog, Config)),
+ restore_env(?config(erl_flags_env, Config)),
+ ok.
+
+%%%
+%%% The test cases ------------------------------------------------------------
+%%%
+
+basic(doc) -> [];
+basic(suite) -> [];
+basic(Config) when is_list(Config) ->
+ ?line ErtsAllocConfig = privfile("generated", Config),
+
+ SbctMod = " +MBsbct 1024 +MHsbct 4096",
+
+ %% Make sure we have enabled allocators
+ ZFlgs = case os:getenv("ERL_ZFLAGS") of
+ FlgString when is_list(FlgString) ->
+ FlgString;
+ _ ->
+ ""
+ end ++ " +Mea max +Mea config",
+
+ ?line os:putenv("ERL_ZFLAGS", ZFlgs ++ SbctMod),
+
+ ?line {ok, Node1} = start_node(Config),
+ ?line ok = rpc:call(Node1, ?MODULE, make_basic_config, [ErtsAllocConfig]),
+ ?line stop_node(Node1),
+
+ ?line display_file(ErtsAllocConfig),
+
+ ?line ManualConfig = privfile("manual", Config),
+ ?line {ok, IOD} = file:open(ManualConfig, [write]),
+ ?line io:format(IOD, "~s", ["+MBsbct 2048"]),
+ ?line file:close(IOD),
+ ?line display_file(ManualConfig),
+
+ ?line os:putenv("ERL_ZFLAGS", ZFlgs),
+
+ ?line {ok, Node2} = start_node(Config,
+ "-args_file " ++ ErtsAllocConfig
+ ++ " -args_file " ++ ManualConfig),
+
+ ?line {_, _, _, Cfg} = rpc:call(Node2, erlang, system_info, [allocator]),
+
+ ?line stop_node(Node2),
+
+ ?line {value,{binary_alloc, BCfg}} = lists:keysearch(binary_alloc, 1, Cfg),
+ ?line {value,{sbct, 2097152}} = lists:keysearch(sbct, 1, BCfg),
+ ?line {value,{eheap_alloc, HCfg}} = lists:keysearch(eheap_alloc, 1, Cfg),
+ ?line {value,{sbct, 4194304}} = lists:keysearch(sbct, 1, HCfg),
+
+ ?line ok.
+
+make_basic_config(ErtsAllocConfig) ->
+ %% Save some different scenarios
+ Tester = self(),
+ SSBegun = make_ref(),
+ SSDone = make_ref(),
+ SSFun = fun (F) ->
+ receive
+ SSDone ->
+ ok = erts_alloc_config:save_scenario(),
+ Tester ! SSDone
+ after 500 ->
+ ok = erts_alloc_config:save_scenario(),
+ F(F)
+ end
+ end,
+ SS = spawn_link(fun () ->
+ ok = erts_alloc_config:save_scenario(),
+ Tester ! SSBegun,
+ SSFun(SSFun)
+ end),
+ receive SSBegun -> ok end,
+ Ref = make_ref(),
+ Tab = ets:new(?MODULE, [bag, public]),
+ Ps = lists:map(
+ fun (_) ->
+ spawn_link(
+ fun () ->
+ ets:insert(Tab,
+ {self(),
+ lists:seq(1, 1000)}),
+ receive after 1000 -> ok end,
+ Tester ! {Ref, self()}
+ end)
+ end,
+ lists:seq(1, 10000)),
+ lists:foreach(fun (P) -> receive {Ref, P} -> ok end end, Ps),
+ ets:delete(Tab),
+ SS ! SSDone,
+ receive SSDone -> ok end,
+
+ ok = erts_alloc_config:make_config(ErtsAllocConfig).
+
+
+
+%%
+%% Utils ----------------------------------------------------------------------
+%%
+
+display_file(FileName) ->
+ ?t:format("filename: ~s~n", [FileName]),
+ {ok, Bin} = file:read_file(FileName),
+ io:format("~s", [binary_to_list(Bin)]),
+ ?t:format("eof: ~s~n", [FileName]),
+ ok.
+
+mk_name(Config) when is_list(Config) ->
+ {A, B, C} = now(),
+ list_to_atom(atom_to_list(?MODULE)
+ ++ "-" ++ atom_to_list(?config(testcase, Config))
+ ++ "-" ++ integer_to_list(A)
+ ++ "-" ++ integer_to_list(B)
+ ++ "-" ++ integer_to_list(C)).
+
+start_node(Config) ->
+ start_node(Config, "").
+
+start_node(Config, Args) ->
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line ?t:start_node(mk_name(Config),
+ slave,
+ [{args, "-pa " ++ Pa ++ " " ++ Args}]).
+
+stop_node(Node) ->
+ ?line true = ?t:stop_node(Node).
+
+privfile(Name, Config) ->
+ filename:join([?config(priv_dir, Config),
+ atom_to_list(?config(testcase, Config)) ++ "." ++ Name]).
+
+save_env() ->
+ {erl_flags,
+ os:getenv("ERL_AFLAGS"),
+ os:getenv("ERL_FLAGS"),
+ os:getenv("ERL_"++erlang:system_info(otp_release)++"_FLAGS"),
+ os:getenv("ERL_ZFLAGS")}.
+
+restore_env(EVar, false) when is_list(EVar) ->
+ restore_env(EVar, "");
+restore_env(EVar, "") when is_list(EVar) ->
+ case os:getenv(EVar) of
+ false -> ok;
+ "" -> ok;
+ " " -> ok;
+ _ -> os:putenv(EVar, " ")
+ end;
+restore_env(EVar, Value) when is_list(EVar), is_list(Value) ->
+ case os:getenv(EVar) of
+ Value -> ok;
+ _ -> os:putenv(EVar, Value)
+ end.
+
+restore_env({erl_flags, AFlgs, Flgs, RFlgs, ZFlgs}) ->
+ restore_env("ERL_AFLAGS", AFlgs),
+ restore_env("ERL_FLAGS", Flgs),
+ restore_env("ERL_"++erlang:system_info(otp_release)++"_FLAGS", RFlgs),
+ restore_env("ERL_ZFLAGS", ZFlgs),
+ ok.
diff --git a/lib/runtime_tools/test/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl
new file mode 100644
index 0000000000..3ae8d34dd6
--- /dev/null
+++ b/lib/runtime_tools/test/inviso_SUITE.erl
@@ -0,0 +1,2831 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% Description:
+%% Test suite for inviso (basic parts, i.e not inviso tools). Note that
+%% inviso basic parts have modules in both the runtime_tools and
+%% inviso applications.
+%%
+%% Authors:
+%% Ann-Marie L�f, [email protected]
+%% Lennart �hman, [email protected]
+%% -----------------------------------------------------------------------------
+
+-module(inviso_SUITE).
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-define(l,?line).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic_dist_trace_1, basic_dist_trace_2,
+ basic_dist_trace_3, basic_dist_trace_ti_1,
+ basic_dist_trace_ti_2, basic_dist_trace_ti_3,
+ suspend_dist_trace_ti_1, suspend_dist_trace_ti_2,
+ meta_cleanfunc_dist_1, basic_handlerfun_dist_1,
+ delete_log_dist_1, autostart_dist_1, autostart_dist_2,
+ autostart_dist_3, running_alone_dist_1,
+ running_alone_dist_2, running_alone_dist_3,
+ running_alone_dist_4, running_alone_dist_5,
+ overload_dist_1, overload_dist_2, overload_dist_3,
+ overload_dist_4, overload_dist_5, subscribe_dist_1,
+ lfm_trace_dist_1, lfm_trace_ti_dist_2,
+ handle_logfile_sort_wrapset, fetch_log_dist_trace_1,
+ fetch_log_dist_trace_2, fetch_log_dist_trace_3,
+ fetch_log_dist_error_1, fetch_log_dist_error_2,
+ expand_regexp_dist_1, only_loaded_dist_1].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+init_per_suite(Config) ->
+ %% No never know who skrewed up this node before this suite! :-)
+ erlang:trace_pattern({'_','_','_'},[],[local]),
+ erlang:trace_pattern({'_','_','_'},[],[global]),
+ erlang:trace(all,false,[all]),
+
+ ?l ok=application:start(runtime_tools),
+ Config.
+
+end_per_suite(_Config) ->
+ ?l ok=application:stop(runtime_tools).
+
+
+%% For each distributed testcase, we need two other distributed nodes to run the
+%% runtime components on. Since they are freshly started every time there is no
+%% need to clean them up first.
+init_per_testcase(_Case,Config) ->
+ ?l TH=test_server:timetrap(100000),
+ ?l {ok,Node1}=test_server:start_node(inviso1,peer,[]),
+ ?l {ok,Node2}=test_server:start_node(inviso2,peer,[]),
+ ?l SuiteDir=filename:dirname(code:which(?MODULE)),
+
+ %% Otherwise peer nodes will not find this module!
+ ?l true=rpc:call(Node1,code,add_patha,[SuiteDir]),
+ ?l true=rpc:call(Node2,code,add_patha,[SuiteDir]),
+
+ ?l start_side_effect_logger(node()),
+ ?l start_side_effect_logger(Node1),
+ ?l start_side_effect_logger(Node2),
+
+
+ %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT
+% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
+% ?l rpc:call(Node1,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
+% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/runtime_tools/ebin"]),
+% ?l rpc:call(Node2,code,add_patha,["/clearcase/otp/tools/inviso/ebin"]),
+
+% %% SPECIAL FOR MY PRIVATE TEST ENVIROMENT, windows.
+% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
+% ?l rpc:call(Node1,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
+% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/runtime_tools/ebin"]),
+% ?l rpc:call(Node2,code,add_patha,["Z:/DATA/PROJECTS/inviso_project/inviso/ebin"]),
+
+ ?l ok=rpc:call(Node1,application,start,[runtime_tools]),
+ ?l ok=rpc:call(Node2,application,start,[runtime_tools]),
+ ?l timer:sleep(100), % Problem with autostarted runtime.
+ %% The following is a test that the inviso_rt processes which are autostarted
+ %% are now gone.
+
+ ?l ok=poll(rpc,call,[Node1,erlang,whereis,[inviso_rt]],undefined,20),
+ ?l ok=poll(rpc,call,[Node2,erlang,whereis,[inviso_rt]],undefined,20),
+
+% ?l ok=poll(rpc,call,[Node1,supervisor,which_children,[runtime_tools_sup]],[],20),
+% ?l ok=poll(rpc,call,[Node2,supervisor,which_children,[runtime_tools_sup]],[],20),
+ NewConfig1=insert_remotenode_config(inviso1,Node1,Config),
+ NewConfig2=insert_remotenode_config(inviso2,Node2,NewConfig1),
+ insert_timetraphandle_config(TH,NewConfig2).
+%% -----------------------------------------------------------------------------
+
+end_per_testcase(Case,Config) ->
+ ?l test_server:stop_node(get_remotenode_config(inviso1,Config)),
+ ?l test_server:stop_node(get_remotenode_config(inviso2,Config)),
+
+ case whereis(inviso_c) of
+ undefined -> % Should not exist.
+ true;
+ Pid when is_pid(Pid) -> % But if it exists...
+ exit(Pid,kill), % Remove it!
+ io:format("Had to kill the control component in end_per_testcase,~p.~n",[Case])
+ end,
+ case whereis(inviso_rt) of
+ undefined -> % Should not exist.
+ true;
+ Pid2 when is_pid(Pid2) -> % But if it exists...
+ exit(Pid2,kill), % Remove it!
+ io:format("Had to kill local runtime component in end_per_testcase,~p.~n",[Case])
+ end,
+ ?l process_killer([inviso_test_proc,
+ inviso_tab_proc,
+ inviso_collector_proc,
+ global_inviso_test_proc]),
+ ?l test_server:timetrap_cancel(get_timetraphandle_config(Config)),
+
+ NewConfig1=remove_remotenode_config(inviso1,Config),
+ NewConfig2=remove_remotenode_config(inviso2,NewConfig1),
+ remove_timetraphandle_config(NewConfig2).
+%% -----------------------------------------------------------------------------
+
+%% ==============================================================================
+%% Testcases.
+%% ==============================================================================
+
+%% TEST CASE: Basic, distributed, trace only.
+basic_dist_trace_1(suite) -> [];
+basic_dist_trace_1(doc) ->
+ ["Basic case, start of distributed tracing, using only trac."];
+basic_dist_trace_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
+ "tf1_"++
+ atom_to_list(N)
+ ])}} end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+ activate_local_tracing(Nodes),
+ deactivate_local_tracing(Nodes),
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+%% TEST CASE: Basic, distributed, activate global tracing for functions in modules
+%% pointed out using a regexp. No tracing will be done.
+basic_dist_trace_2(suite) -> [];
+basic_dist_trace_2(doc) ->
+ [""];
+basic_dist_trace_2(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
+ "tf1a_"++
+ atom_to_list(N)
+ ])}} end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+ Funcs1=activate_global_tracing_regexp(Nodes),
+ deactivate_global_tracing_regexp(Nodes,Funcs1),
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Basic, distributed, activate global tracing for functions in modules
+%% pointed out using a dir-regexp. No tracing will be done.
+basic_dist_trace_3(suite) -> [];
+basic_dist_trace_3(doc) ->
+ [""];
+basic_dist_trace_3(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,{file,filename:join([PrivDir,
+ "tf1b_"++
+ atom_to_list(N)
+ ])}} end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+ Funcs1=activate_global_tracing_regexp_dir(Nodes),
+ deactivate_global_tracing_regexp_dir(Nodes,Funcs1),
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Basic, distributed, trace and ti.
+basic_dist_trace_ti_1(suite) -> [];
+basic_dist_trace_ti_1(doc) ->
+ [""];
+basic_dist_trace_ti_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf2_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_meta_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))),
+ ?l true=(is_pid(whereis(inviso_rt_meta))),
+ deactivate_meta_tracing(Nodes),
+ deactivate_local_tracing(Nodes),
+ stop_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
+ ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3),
+ stop(Nodes),
+ timer:sleep(200), % Give it time to terminate.
+ ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
+ ?l undefined=whereis(inviso_rt_meta), % Still gone.
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% Test CASE: Testing that the tpm_tracer functionality works. That is appending
+%% {tracer,Tracer} to a meta match spec.
+basic_dist_trace_ti_2(suite) -> [];
+basic_dist_trace_ti_2(doc) ->
+ [""];
+basic_dist_trace_ti_2(Config) when is_list(Config) ->
+ case erlang:system_info(version) of
+ "5.4"++_ -> % Perhaps not perfect, but work now :-)
+ {skip,"Old emulator"};
+ _ ->
+ basic_dist_trace_ti_2_do(Config)
+ end.
+
+basic_dist_trace_ti_2_do(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf3_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_deactivate_meta_tracing_tracer(Nodes),
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Basic, distributed, trace and ti, where we try to use ctp_all to
+%% check that all global and local patterns are removed but that meta patterns
+%% remain.
+%% This test also checks that if the meta tracer is terminated an error value
+%% is generated when trying to do meta tracing at that node.
+basic_dist_trace_ti_3(suite) -> [];
+basic_dist_trace_ti_3(doc) ->
+ [""];
+basic_dist_trace_ti_3(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf4_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_global_tracing(Nodes),
+ activate_meta_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))),
+ ?l true=(is_pid(whereis(inviso_rt_meta))),
+ ?l {ok,NodeResults1}=inviso:ctp_all(Nodes), % Removes local and global patterns.
+ ?l true=check_noderesults(Nodes,ok,NodeResults1),
+ ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,which,1},traced],{traced,false}),
+ ?l true=check_on_nodes(Nodes,erlang,trace_info,[{code,get_path,0},traced],{traced,false}),
+ %% But meta patters shall remain.
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{lists,module_info,0},meta_match_spec],
+ fun({meta_match_spec,L})when length(L)>0 ->true end),
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{lists,module_info,0},meta],
+ fun({meta,P})when is_pid(P) ->
+ P=rpc:call(node(P),erlang,whereis,[inviso_rt_meta]),
+ true
+ end),
+ %% Now kill the meta tracer somewhere and try to activate meta tracing.
+ ?l [ANode|_]=Nodes,
+ ?l AMetaPid=rpc:call(ANode,erlang,whereis,[inviso_rt_meta]),
+ ?l rpc:call(ANode,erlang,exit,[AMetaPid,kill]),
+ ?l {ok,NodeResults2}=inviso:tpm(Nodes,math,pi,0,[],void),
+ ?l {value,{ANode,{error,_}}}=lists:keysearch(ANode,1,NodeResults2),
+
+ ?l stop_tracing(Nodes),
+ ?l stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% -----------------------------------------------------------------------------
+%% Test cases for SUSPEND
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: In this test case a trace with ti is started. Trace flags are set,
+%% trace patterns are set and meta trace patterns. We then check that the trace
+%% flags and the meta patterns are removed when tracing suspended.
+%% The suspension is cancelled and we check that it is possible to reactivate
+%% tracing by setting the process flags and meta patterns again.
+suspend_dist_trace_ti_1(suite) -> [];
+suspend_dist_trace_ti_1(doc) ->
+ [""];
+suspend_dist_trace_ti_1(Config) when is_list(Config) ->
+ ?l RemoteNodes=get_remotenodes_config(Config),
+ ?l Nodes=[node()|RemoteNodes],
+ ?l PrivDir=filename:join(?config(priv_dir,Config),""),
+ ?l TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf_suspend1_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ ?l TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_meta_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))),
+ ?l true=(is_pid(whereis(inviso_rt_meta))),
+ %% Set some trace flags on some newly started test procs.
+ activate_traceflags(Nodes),
+
+ %% Now suspend the tracing on all nodes. That shall result in the removal
+ %% of trace flags and meta trace patterns, but not local trace patterns.
+ ?l {ok,NodeResults1}=inviso:suspend(Nodes,test),
+ ?l true=check_noderesults(Nodes,ok,NodeResults1),
+ %% Trace flags gone?
+ ?l TestProcs=lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_test_proc]) end,Nodes),
+ ?l lists:foreach(fun(P)->
+ {flags,[]}=
+ rpc:call(node(P),erlang,trace_info,[P,flags])
+ end,
+ TestProcs),
+ %% Meta patterns shall be gone too, but local functions still there.
+ ?l lists:foreach(fun(N)->
+ {meta,false}=
+ rpc:call(N,
+ erlang,
+ trace_info,
+ [{math,module_info,1},meta]),
+ {traced,local}=
+ rpc:call(N,
+ erlang,
+ trace_info,
+ [{code,which,1},traced])
+ end,
+ Nodes),
+
+ %% Try to activate trace flags, trace patterns and meta tracing while
+ %% suspended. Should not succeed of course!
+ ?l ThisNode=node(),
+ ?l {ok,[{ThisNode,{error,suspended}}]}=
+ inviso:tf([ThisNode],inviso_test_proc,[call]),
+ ?l {ok,[{ThisNode,{error,suspended}}]}=
+ inviso:tpl([ThisNode],math,module_info,1,[]),
+ ?l {ok,[{ThisNode,{error,suspended}}]}=
+ inviso:init_tpm([ThisNode],
+ math,
+ module_info,
+ 1,
+ {?MODULE,tpm_init_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_call_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_return_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
+
+ %% Now we want to cancel suspension and see that we can reactivate tracing.
+ ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes),
+ ?l true=check_noderesults(Nodes,ok,NodeResults2),
+
+ ?l {ok,NodeResults3}=
+ inviso:init_tpm(math,
+ module_info,
+ 1,
+ {?MODULE,tpm_init_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_call_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_return_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
+ ?l true=check_noderesults(Nodes,ok,NodeResults3),
+ ?l {ok,NodeResults5}=
+ inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults5),
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{math,module_info,1},meta_match_spec],
+ {meta_match_spec,[{'_',[],[{return_trace}]}]}),
+ ?l {ok,NodeResults6}=inviso:tf(Nodes,inviso_test_proc,[call]),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults6),
+
+ %deactivate_meta_tracing(Nodes),
+ %deactivate_local_tracing(Nodes),
+ stop_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
+ ?l ok=poll(erlang,whereis,[inviso_rt_meta],undefined,3),
+ stop(Nodes),
+ ?l timer:sleep(200), % Give it time to terminate.
+ ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
+ ?l undefined=whereis(inviso_rt_meta), % Still gone.
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: In this test case a trace with ti is started. Trace flags are set,
+%% trace patterns are set and meta trace patterns. We then suspend tracing at
+%% all nodes, then stop tracing which shall be allowed. We then try to initiate
+%% tracing again which shall not be possible.
+suspend_dist_trace_ti_2(suite) -> [];
+suspend_dist_trace_ti_2(doc) ->
+ [""];
+suspend_dist_trace_ti_2(Config) when is_list(Config) ->
+ ?l RemoteNodes=get_remotenodes_config(Config),
+ ?l Nodes=[node()|RemoteNodes],
+ ?l PrivDir=filename:join(?config(priv_dir,Config),""),
+ ?l TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf_suspend2_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ ?l TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_meta_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))),
+ ?l true=(is_pid(whereis(inviso_rt_meta))),
+ %% Set some trace flags on some newly started test procs.
+ activate_traceflags(Nodes),
+
+ %% Now suspend the tracing on all nodes. That shall result in the removal
+ %% of trace flags and meta trace patterns, but not local trace patterns.
+ ?l {ok,NodeResults1}=inviso:suspend(Nodes,test),
+ ?l true=check_noderesults(Nodes,ok,NodeResults1),
+
+ %% Now stop tracing.
+ ?l {ok,NodeResults3}=inviso:stop_tracing(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,idle},NodeResults3),
+ %% Now try to initiate tracing again.
+ ThisNode=node(),
+ ?l {ok,[{ThisNode,{error,suspended}}]}=
+ inviso:init_tracing([ThisNode],
+ [{trace,{file,filename:join([PrivDir,"tf_suspend3_"++
+ atom_to_list(ThisNode)])}},
+ {ti,{file,{filename:join([PrivDir,"tf_suspend3_"++
+ atom_to_list(ThisNode)])}}}]),
+
+ %% Cancel the suspension and initiate tracing again.
+ ?l {ok,NodeResults2}=inviso:cancel_suspension(Nodes),
+ ?l true=check_noderesults(Nodes,ok,NodeResults2),
+ ?l TracerDataFun2=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf_suspend4_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ ?l TracerDataList2=lists:map(TracerDataFun2,Nodes),
+ ?l {ok,NodeResults4}=inviso:init_tracing(TracerDataList2),
+ ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok},{ti_log,ok}]},NodeResults4),
+ stop_tracing(Nodes),
+ ?l true=(is_pid(whereis(inviso_rt))), % Shall still be running.
+ stop(Nodes),
+ ?l timer:sleep(200), % Give it time to terminate.
+ ?l ok=poll(erlang,whereis,[inviso_rt],undefined,3),% Shall be gone now.
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+
+%% TEST CASE: This test case tests that the clean function removes (prosumed)
+%% expired data from the internal public-loopdata structure in the inviso_rt_meta
+%% process.
+meta_cleanfunc_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"mcf1_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ %% Now initialize meta tracing, but the call_func is a bit "fixed".
+ ?l {ok,NodeResults1}=
+ inviso:tpm(Nodes,math,module_info,1,[],
+ {?MODULE,meta_cleanfunc_initfunc_1},
+ {?MODULE,meta_cleanfunc_callfunc_1},
+ void,void),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults1),
+ %% Nothing in the "our" part of the public loop data.
+ ?l true=check_on_nodes(Nodes,
+ inviso_rt_meta,get_state,[inviso_rt_meta],
+ fun({ok,_LD,{{_,[]},_}})->true end),
+ ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[exports]) end,Nodes),
+ %% Check that it has been added to the public loopdata structure.
+ ?l true=check_on_nodes(Nodes,
+ ?MODULE,poll,[inviso_rt_meta,
+ get_state,
+ [inviso_rt_meta],
+ fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})->
+ true;
+ (_)->false
+ end,
+ 20],
+ ok),
+ %% While we wait for 60 seconds to pass, we test a few other things.
+ ?l {ok,NodeResults2}=
+ inviso:tpm(Nodes,?MODULE,slowfunction2,0,[{'_',[],[{return_trace}]}],
+ {?MODULE,meta_cleanfunc_initfunc_2},
+ {?MODULE,meta_cleanfunc_callfunc_2},
+ {?MODULE,meta_cleanfunc_returnfunc_2},
+ void),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults2),
+ ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,slowfunction,[]) end,Nodes),
+ %% Believe it or not but slowfunction is still running, in its own process,
+ %% we are therefore free now to examine the meta tracer.
+ ?l true=check_on_nodes(Nodes,
+ ?MODULE,poll,[inviso_rt_meta,
+ get_state,
+ [inviso_rt_meta],
+ fun({ok,_LD,{{[],Tuples},_}})->
+ {value,_}=
+ lists:keysearch(meta_cleanfunc_test2,
+ 1,
+ Tuples),
+ {value,_}=
+ lists:keysearch(meta_cleanfunc_test1,
+ 1,
+ Tuples),
+ true;
+ (_)->
+ false
+ end,
+ 20],
+ ok),
+ %% Now we wait for slowfunction to return and that the meta_cleanfunc_test2
+ %% to be removed from public loopdata strucuture.
+ ?l timer:sleep(10000),
+ %% The only thing remaining should be the meta_cleanfunc_test1 which will not
+ %% go away for less than that the clean functionality removes it.
+ ?l true=check_on_nodes(Nodes,
+ ?MODULE,poll,[inviso_rt_meta,
+ get_state,
+ [inviso_rt_meta],
+ fun({ok,_LD,{{_,[{meta_cleanfunc_test1,_Now}]},_}})->
+ true;
+ (_)->
+ false
+ end,
+ 20],
+ ok),
+ %% Wait for the clean function to clean meta_cleanfunc_test1 away.
+ ?l timer:sleep(51000), % Shall be gone after 5 seconds.
+ ?l true=check_on_nodes(Nodes,
+ ?MODULE,poll,[inviso_rt_meta,
+ get_state,
+ [inviso_rt_meta],
+ fun({ok,_LD,{{_,[]},_}})->true;
+ (_)->false
+ end,
+ 20],
+ ok),
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+
+%% This function acts as tpm initialization function when we are going to test
+%% that the clean function works. Note that we here assume standard public loop
+%% datastructure.
+meta_cleanfunc_initfunc_1(_M,_F,_Arity,{E1,_E2}) ->
+ {ok,{E1,[]},void}.
+%% Function that is supposed to be called when the meta traced function is
+%% called.
+meta_cleanfunc_callfunc_1(_Pid,_Args,{{E1,E2},Global}) ->
+ {ok,{{E1,[{meta_cleanfunc_test1,now()}|E2]},Global},void}.
+
+meta_cleanfunc_initfunc_2(_M,_F,_Arity,PublLD) ->
+ {ok,PublLD,void}.
+meta_cleanfunc_callfunc_2(_Pid,_Args,{{E1,E2},Global}) ->
+ {ok,{{E1,[{meta_cleanfunc_test2,now()}|E2]},Global},void}.
+meta_cleanfunc_returnfunc_2(_Pid,_,{{E1,E2},Global}) ->
+ {value,_}=lists:keysearch(meta_cleanfunc_test2,1,E2),
+ {ok,{{E1,lists:keydelete(meta_cleanfunc_test2,1,E2)},Global},void}.
+
+slowfunction() ->
+ spawn(?MODULE,slowfunction1,[]).
+slowfunction1() ->
+ slowfunction2(). % Meta trace on this function call.
+slowfunction2() ->
+ timer:sleep(2000),
+ true.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Testing that a runtime component can be started instructing it
+%% to use a handler fun. Checks that the handler fun is called if a trace
+%% message comes in.
+basic_handlerfun_dist_1(suite) -> [];
+basic_handlerfun_dist_1(doc) ->
+ [""];
+basic_handlerfun_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l lists:foreach(fun(N)->rpc:call(N,ets,insert,[inviso_sideeffect_tab,{bhf1,0}]) end,
+ Nodes),
+ TracerDataFun=
+ fun(N)->{N,{fun basic_handlerfun_dist_1_fun/2,inviso_sideeffect_tab}} end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_traceflags(Nodes),
+ ?l lists:foreach(fun(N)->[{bhf1,0}]=
+ rpc:call(N,ets,lookup,[inviso_sideeffect_tab,bhf1])
+ end,
+ Nodes),
+ ?l inviso_test_proc ! {apply,code,which,[lists]},
+ ok=poll(ets,lookup,[inviso_sideeffect_tab,bhf1],[{bhf1,1}],20),
+ deactivate_traceflags(Nodes),
+ deactivate_local_tracing(Nodes),
+ stop_tracing(Nodes),
+ timer:sleep(100),
+ ?l [{bhf1,1}]=ets:lookup(inviso_sideeffect_tab,bhf1),
+ stop(Nodes),
+ ok.
+
+%% Function used as handler fun for testcase above.
+basic_handlerfun_dist_1_fun(_Msg,TId) ->
+ ets:update_counter(TId,bhf1,1),
+ TId.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Here we test that delete_log removes the files at the involved
+%% runtime nodes. In this case we test that we remove logs according to last
+%% used tracer data.
+delete_log_dist_1(suite) -> [];
+delete_log_dist_1(doc) -> [""];
+delete_log_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"dl1_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ ?l Files=lists:map(fun({N,TD})->
+ ?l {value,{_,{_,TraceFile}}}=lists:keysearch(trace,1,TD),
+ ?l {value,{_,{_,TiFile}}}=lists:keysearch(ti,1,TD),
+ ?l {N,{TraceFile,TiFile}}
+ end,
+ TracerDataList),
+ io:format("The Files is:~w~n",[Files]),
+ ?l {ok,NodeResults1}=inviso:delete_log(Nodes), % Should not work!
+ ?l true=check_noderesults(Nodes,{error,tracing},NodeResults1),
+ stop_tracing(Nodes),
+ %% Files still here.
+ ?l lists:foreach(fun({N,{F1,F2}})->
+ ?l {ok,_}=rpc:call(N,file,read_file_info,[F1]),
+ ?l {ok,_}=rpc:call(N,file,read_file_info,[F2])
+ end,
+ Files),
+ ?l {ok,NodeResults2}=inviso:delete_log(Nodes),
+ ?l true=check_noderesults(Nodes,
+ fun({_N,{ok,LogInfos}})->
+ ?l {value,{_,[{ok,_FName1}]}}=
+ lists:keysearch(trace_log,1,LogInfos),
+ ?l {value,{_,[{ok,_FName2}]}}=
+ lists:keysearch(ti_log,1,LogInfos),
+ true
+ end,
+ NodeResults2),
+ %% The files shall be gone now.
+ ?l lists:foreach(fun({N,{F1,F2}})->
+ ?l {error,enoent}=rpc:call(N,file,read_file_info,[F1]),
+ ?l {error,enoent}=rpc:call(N,file,read_file_info,[F2])
+ end,
+ Files),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+%% TEST CASE: Test of the autostart behaviour of the runtime component.
+%% Here we test that a runtime component is started according to the autostart.conf
+%% file. Note that the repeat parameter is set to 2.
+autostart_dist_1(suite) -> [];
+autostart_dist_1(doc) ->
+ [""];
+autostart_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ AutoConfFile=filename:join(PrivDir,"autostart1.conf"),
+ [RNode|_]=RemoteNodes,
+ ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
+ ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
+ inviso_autostart_conf,
+ AutoConfFile]),
+ ?l {ok,FD}=file:open(AutoConfFile,[write]),
+ ?l ok=io:format(FD,"~w.~n~w.~n",[{repeat,2},{tag,c_ref}]),
+ ?l file:close(FD),
+ ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
+ timer:sleep(1000),
+ ?l P1=rpc:call(RNode,erlang,whereis,[inviso_rt]),
+ ?l true=is_pid(P1),
+ ?l rpc:call(RNode,erlang,exit,[P1,kill]),
+ ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
+ ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
+ timer:sleep(1000),
+ ?l P2=rpc:call(RNode,erlang,whereis,[inviso_rt]),
+ ?l true=is_pid(P2),
+ ?l rpc:call(RNode,erlang,exit,[P2,kill]),
+ ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
+ ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
+ timer:sleep(1000),
+ ?l undefined=rpc:call(RNode,erlang,whereis,[inviso_rt]),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of autostart. Here we focus on that an autostarted
+%% runtime component actually follows the trace case command file and
+%% initiates tracing.
+autostart_dist_2(suite) -> [];
+autostart_dist_2(doc) ->
+ [""];
+autostart_dist_2(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ AutoConfFile=filename:join(PrivDir,"autostart2.conf"),
+ [RNode|_]=RemoteNodes,
+ ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
+ ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
+ inviso_autostart_conf,
+ AutoConfFile]),
+ ?l CmdFileName=filename:join(PrivDir,"autostart_cmd_as1"),
+ ?l {ok,FD}=file:open(CmdFileName,[write]),
+ ?l ok=io:format(FD,
+ "inviso:tpl(Nodes,M,F,Arity,[]).~n"
+ "inviso:tf(Nodes,inviso_test_proc,[call]).~n",
+ []),
+ ?l file:close(FD),
+ ?l TraceFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)]),
+ ?l TiFileName=filename:join([PrivDir,"as1_"++atom_to_list(RNode)++".ti"]),
+ ?l inviso_as_lib:setup_autostart(RNode,
+ 2,
+ [],
+ [{trace,{file,TraceFileName}},
+ {ti,{file,TiFileName}}],
+ [[CmdFileName]],
+ [{'M',code},{'F',which},{'Arity',1}],
+ [{{inviso,tpl,5},{inviso_rt,tpl,{erlang,tl}}},
+ {{inviso,tf,3},{inviso_rt,tf,{erlang,tl}}}]),
+ ?l TestP=spawn(RNode,?MODULE,test_proc_init,[]),
+ ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
+ ?l timer:sleep(1000),
+ ?l {ok,_}=file:read_file_info(TraceFileName),
+ ?l {ok,_}=file:read_file_info(TiFileName),
+ ?l true=is_pid(P=rpc:call(RNode,erlang,whereis,[inviso_rt])),
+ ?l ok=poll(rpc,call,[RNode,erlang,trace_info,[{code,which,1},traced]],{traced,local},10),
+ ?l {flags,[call]}=rpc:call(RNode,erlang,trace_info,[TestP,flags]),
+ ?l rpc:call(RNode,erlang,exit,[P,kill]),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Here we test that an autostarted runtime component with a dependency
+%% to a specific control component tries to connect to that control component
+%% during its start-up.
+autostart_dist_3(suite) -> [];
+autostart_dist_3(doc) ->
+ [""];
+autostart_dist_3(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ AutoConfFile=filename:join(PrivDir,"autostart3.conf"),
+ [RNode|_]=RemoteNodes,
+ ?l ok=rpc:call(RNode,application,stop,[runtime_tools]),
+ ?l ok=rpc:call(RNode,application,set_env,[runtime_tools,
+ inviso_autostart_conf,
+ AutoConfFile]),
+ ?l {ok,FD}=file:open(AutoConfFile,[write]),
+ ?l ok=io:format(FD,"~w.~n~w.~n~w.~n",
+ [{options,[{dependency,{infinity,node()}}]},{repeat,2},{tag,c_ref}]),
+ ?l file:close(FD),
+ %% Now start inviso at this node here for the runtime to connect.
+ ?l {ok,_Pid}=inviso:start(),
+ ?l ok=poll(erlang,whereis,[inviso_c],fun(P) when is_pid(P)->true;(_)->false end,10),
+ %% Make the runtime component start.
+ ?l ok=rpc:call(RNode,application,start,[runtime_tools]),
+ ?l ok=poll(rpc,call,[RNode,erlang,whereis,[inviso_rt]],
+ fun(P) when is_pid(P)->true;(_)->false end,10),
+ %% Check that the runtime component started.
+ ?l ok=poll(inviso,get_status,[[RNode]],{ok,[{RNode,{ok,{new,running}}}]},20),
+% ?l {ok,[{RNode,{ok,{new,running}}}]}=inviso:get_status([RNode]),
+ stop([RNode]),
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+
+%% TEST CASE: Test of the dependency mechanism in the runtime component.
+%% Default behaviour is dependency=infinity, i.e the runtime components remains.
+%% We also test here that we can reconnect to the runtime.
+running_alone_dist_1(suite) -> [];
+running_alone_dist_1(doc) ->
+ [""];
+running_alone_dist_1(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l shutdown=inviso:stop(), % Stop the control component!
+ ?l undefined=whereis(inviso_c),
+ timer:sleep(3000), % How long shall we wait? :-)
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
+ Nodes),
+ ?l {ok,_Pid2}=inviso:start(),
+ ?l {ok,NodeResults2}=inviso:add_nodes(Nodes,b_ref,[]),
+ ?l true=check_noderesults(Nodes,{ok,{adopted,new,running,a_ref}},NodeResults2),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the dependency mechanism in the runtime component.
+%% Test that the runtime components terminates after the specified 5000 ms.
+running_alone_dist_2(suite) -> [];
+running_alone_dist_2(doc) ->
+ [""];
+running_alone_dist_2(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,5000}]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l shutdown=inviso:stop(), % Stop the control component!
+ ?l undefined=whereis(inviso_c),
+ timer:sleep(2000),
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
+ Nodes),
+ timer:sleep(4000), % Now they shall be dead!
+ ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
+ Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the dependency mechanism in the runtime component.
+%% Test that the runtime components terminates after the specified 5000 ms.
+running_alone_dist_3(suite) -> [];
+running_alone_dist_3(doc) ->
+ [""];
+running_alone_dist_3(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[{dependency,1000}]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:change_options(Nodes,[{dependency,5000}]),
+ ?l true=check_noderesults(Nodes,ok,NodeResults2),
+ ?l shutdown=inviso:stop(), % Stop the control component!
+ ?l undefined=whereis(inviso_c),
+ timer:sleep(3000),
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
+ Nodes),
+ timer:sleep(3000), % Now they shall be dead!
+ ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
+ Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the dependency mechanism in the runtime component.
+%% Test that the runtime components terminates after the specified 5000 ms,
+%% like we did in running_alone_dist_2. But now we also start tracing and checks
+%% that all inviso processes actually disappears when the time-out is reached.
+running_alone_dist_4(suite) -> [];
+running_alone_dist_4(doc) ->
+ [""];
+running_alone_dist_4(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ %% Start some tracing!
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra4"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf_ra4_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,
+ [{dependency,5000}],
+ TracerDataList,
+ {ok,[{trace_log,ok},{ti_log,ok}]}),
+
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
+ Nodes),
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
+ Nodes),
+ %% Stop control component and wait for the runtimes to terminate after
+ %% running alone timer has expired.
+ ?l shutdown=inviso:stop(), % Stop the control component!
+ ?l undefined=whereis(inviso_c),
+ timer:sleep(2000),
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
+ Nodes),
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
+ Nodes),
+ timer:sleep(4000), % Now they shall be dead!
+ ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
+ Nodes),
+ ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,
+ Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the dependency mechanism in the runtime component.
+%% Test that the runtime components terminates imeediately when the control
+%% component is stopped. Check that all processes are gone.
+running_alone_dist_5(suite) -> [];
+running_alone_dist_5(doc) ->
+ [""];
+running_alone_dist_5(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ %% Start some tracing!
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataFun=
+ fun(N)->{N,[{trace,{file,filename:join([PrivDir,"tf_ra5"++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,"tf_ra5_"++atom_to_list(N)++".ti"])}}]}
+ end,
+ TracerDataList=lists:map(TracerDataFun,Nodes),
+ start_and_init_tracing2(Nodes,
+ [{dependency,0}],
+ TracerDataList,
+ {ok,[{trace_log,ok},{ti_log,ok}]}),
+
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt])) end,
+ Nodes),
+ ?l lists:foreach(fun(N)->true=is_pid(rpc:call(N,erlang,whereis,[inviso_rt_meta])) end,
+ Nodes),
+ %% Stop control component and check that all runtime component processes have
+ %% terminate more or less immediately afterwards, since dependency==0.
+ ?l shutdown=inviso:stop(), % Stop the control component!
+ timer:sleep(100),
+ ?l undefined=whereis(inviso_c),
+ timer:sleep(500),
+ ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt]) end,
+ Nodes),
+ ?l lists:foreach(fun(N)->undefined=rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,
+ Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the overload protection mechanism. The mechanism checks
+%% for overload using the callback approximately at the interval specified.
+%% Check that it does not start protection until start of tracing.
+overload_dist_1(suite) -> [];
+overload_dist_1(doc) ->
+ [""];
+overload_dist_1(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl1,0}]) end,
+ Nodes), % Initiate the counter.
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
+ a_ref,
+ [{overload,{{?MODULE,overload1},500}}]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ timer:sleep(1000), % Give the loadcheck time to perform.
+ ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl1), % Nothing should have happened.
+
+ %% Overload check shall not start until we start tracing.
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,[{trace,
+ {file,filename:join([PrivDir,
+ "tf_ovl1."++atom_to_list(N)
+ ])}}]}
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
+ ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
+ timer:sleep(1500), % Give the loadcheck time to perform.
+ ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl1),
+ ?l true=(N>=2), % After 1,5 seconds, at least 2 checks.
+
+ %% Now change options and remove overload checking!
+ ?l {ok,NodeResults3}=inviso:change_options(Nodes,[overload]),
+ ?l true=check_noderesults(Nodes,ok,NodeResults3),
+ ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1),
+ timer:sleep(1000),
+ ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl1), % No more loadchecks!
+
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the overload protection mechanism. In this case we focus
+%% in that the init and remove functions are carried out at change_options and
+%% when starting and stoping the runtime component.
+overload_dist_2(suite) -> [];
+overload_dist_2(doc) ->
+ [""];
+overload_dist_2(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
+ a_ref,
+ [{overload,{{?MODULE,overload2},
+ 500,
+ {?MODULE,overload2i,[]},
+ {?MODULE,overload2r,[]}}}]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl2),
+
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,[{trace,
+ {file,filename:join([PrivDir,
+ "tf_ovl2."++atom_to_list(N)
+ ])}}]}
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
+ ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
+ timer:sleep(1500), % Give the loadcheck time to perform.
+ ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl2),
+ io:format("� is:~p~n",[N]),
+ ?l true=(N>=2), % After 1,5 seconds, at least 2 checks.
+ ?l {ok,NodeResults3}=inviso:change_options(Nodes,[{overload,{{?MODULE,overload3},
+ 500,
+ {?MODULE,overload3i,[]},
+ {?MODULE,overload3r,[]}}}]),
+ ?l true=check_noderesults(Nodes,ok,NodeResults3),
+ ?l []=ets:lookup(inviso_sideeffect_tab,ovl2),
+ timer:sleep(1500),
+ ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl3),
+ ?l true=(N2>=2), % After 1,5 seconds, at least 2 checks.
+ stop_tracing(Nodes),
+ ?l []=ets:lookup(inviso_sideeffect_tab,ovl3r), % Remove function shall not be called.
+ ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3),
+ timer:sleep(1000), % Check that overloadchecking has stopped.
+ ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl3),
+ stop(Nodes),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl3r],[{ovl3r,done}],20),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Test of the overload protections mechanism. Here we focus on testing
+%% that if overload is reached tracing is really suspended.
+overload_dist_3(suite) -> [];
+overload_dist_3(doc) ->
+ [""];
+overload_dist_3(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=
+ lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir,
+ "tf_ovl3."++atom_to_list(N)])}},
+ {ti,{file,filename:join([PrivDir,
+ "tf_ovl3_ti."++atom_to_list(N)])}}]}
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->
+ true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl4,0}])
+ end,
+ Nodes),
+ start_and_init_tracing2(Nodes,
+ [{overload,{{?MODULE,overload4},500}}],
+ TracerDataList,
+ {ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_meta_tracing(Nodes),
+ activate_traceflags(Nodes),
+ timer:sleep(600),
+ ?l [{_,N1}]=ets:lookup(inviso_sideeffect_tab,ovl4),
+ ?l true=(N1>=1), % Overload check has been done!
+ ?l Node=node(),
+ ?l {ok,[{Node,{ok,{tracing,running}}}]}=inviso:get_status([node()]),
+ ?l true=ets:insert(inviso_sideeffect_tab,{ovl4_suspend,true}),
+ timer:sleep(600),
+ ?l {ok,[{Node,{ok,{tracing,{suspended,test}}}}]}=inviso:get_status([node()]),
+ ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4),
+ ?l {flags,[]}=erlang:trace_info(whereis(inviso_test_proc),flags),
+ ?l {meta,false}=erlang:trace_info({lists,module_info,0},meta),
+ ?l {traced,local}=erlang:trace_info({code,which,1},traced),
+ ?l true=(is_pid(whereis(inviso_rt_meta))),
+ ?l true=ets:delete(inviso_sideeffect_tab,ovl4_suspend),
+ timer:sleep(600),
+ ?l [{_,N2}]=ets:lookup(inviso_sideeffect_tab,ovl4), % No checking while suspended!
+ ?l {ok,[{Node,ok}]}=inviso:cancel_suspension([node()]),
+ ?l {ok,NodeResults1}=inviso:get_status(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,{tracing,running}},NodeResults1),
+ timer:sleep(600),
+ ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl4),
+ ?l true=(N3>N2),
+ ?l deactivate_local_tracing(Nodes),
+ ?l stop_tracing(Nodes),
+ ?l stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE. Test that the overload mechanism is triggered by to the runtime
+%% component incomming messages, and nothing else.
+overload_dist_4(suite) -> [];
+overload_dist_4(doc) ->
+ [""];
+overload_dist_4(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
+ a_ref,
+ [{overload,{{?MODULE,overload5},
+ infinity,
+ {?MODULE,overload5i,[]},
+ {?MODULE,overload5r,[]}}}]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,ovl5),
+
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,[{trace,
+ {file,filename:join([PrivDir,
+ "tf_ovl4."++atom_to_list(N)
+ ])}}]}
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
+ ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
+ timer:sleep(2000), % Give the loadcheck time to perform.
+ ?l [{_,N}]=ets:lookup(inviso_sideeffect_tab,ovl5),
+ ?l true=(N==0), % And nothing shall have happend!
+ %% Now we send a message to the inviso_rt, then the load check function
+ %% shall be called.
+ ?l whereis(inviso_rt) ! test_of_loadcheck,
+ timer:sleep(200), % Make sure the inviso_rt gets scheduled.
+ ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,ovl5),
+ stop_tracing(Nodes),
+ ?l []=ets:lookup(inviso_sideeffect_tab,ovl5r), % Remove function shall not be called.
+ ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5),
+ ?l whereis(inviso_rt) ! test_of_loadcheck,
+ timer:sleep(1000), % Check that overloadchecking has stopped.
+ ?l [{_,N3}]=ets:lookup(inviso_sideeffect_tab,ovl5),
+ stop(Nodes),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl5r],[{ovl5r,done}],20),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE. Test that the overload mechanism correctly calculates remaining time
+%% to next load check if a message comes into the runtime component "interupting"
+%% the waiting for loadcheck timeout. (Loadcheck timeout is implemented as an after
+%% in the receive).
+overload_dist_5(suite) -> [];
+overload_dist_5(doc) ->
+ [""];
+overload_dist_5(Config) when is_list(Config) ->
+ ?l {ok,_Pid1}=inviso:start(), % Start a control component.
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l lists:foreach(fun(N)->true=rpc:call(N,ets,insert,[inviso_sideeffect_tab,{ovl6,0}]) end,
+ Nodes), % Initiate the counter.
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,
+ a_ref,
+ [{overload,{{?MODULE,overload6},1000}}]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ %% Overload check shall not start until we start tracing.
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,[{trace,
+ {file,filename:join([PrivDir,
+ "tf_ovl5."++atom_to_list(N)
+ ])}}]}
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=inviso:init_tracing(TracerDataList),
+ ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults2),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,ovl6],[{ovl6,2}],25),
+ %% Now we know that exactly 2 checks have been made. Try to Distract the runtime :-)
+ ?l inviso_rt:state(whereis(inviso_rt)), % Make it have to receive a message.
+ timer:sleep(500),
+ ?l [{_,2}]=ets:lookup(inviso_sideeffect_tab,ovl6), % Should still be 2.
+ timer:sleep(600),
+ ?l [{_,3}]=ets:lookup(inviso_sideeffect_tab,ovl6), % We expect yet one check.
+ timer:sleep(1100),
+ ?l [{_,4}]=ets:lookup(inviso_sideeffect_tab,ovl6),
+
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+%% TEST CASE: Test of the subscription mechanism.
+subscribe_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ Pid=spawn(?MODULE,inviso_msg_collector,[]),
+ CtrlPid=whereis(inviso_c),
+
+ ?l {ok,_Pid}=inviso:start(), % Start a control component.
+ ?l ok=inviso:subscribe(Pid),
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,[]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:get_status(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
+ check_msg_collector(Nodes,
+ fun({inviso_event,CP,_,{connected,N,{_Tag,{idle,running}}}})
+ when CP==CtrlPid ->
+ {true,N};
+ (_) ->
+ false
+ end,
+ 13),
+ TracerDataList=lists:map(fun(N)->{N,{file,
+ filename:join([PrivDir,
+ "tf_sub1"++atom_to_list(N)])}}
+ end,
+ Nodes),
+ ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList),
+ ?l true=check_noderesults(Nodes,{ok,[{trace_log,ok}]},NodeResults3),
+ check_msg_collector(Nodes,
+ fun({inviso_event,CP,_,{state_change,N,{tracing,running}}})
+ when CP==CtrlPid ->
+ {true,N};
+ (_) ->
+ false
+ end,
+ 13),
+ ?l {ok,NodeResults4}=inviso:suspend(Nodes,test),
+ ?l true=check_noderesults(Nodes,ok,NodeResults4),
+ check_msg_collector(Nodes,
+ fun({inviso_event,CP,_,{state_change,N,{tracing,{suspended,test}}}})
+ when CP==CtrlPid ->
+ {true,N};
+ (_) ->
+ false
+ end,
+ 13),
+ ?l [RNode|_]=RemoteNodes,
+ ?l RInvisoPid=rpc:call(RNode,erlang,whereis,[inviso_rt]),
+ ?l rpc:call(RNode,erlang,exit,[RInvisoPid,kill]),
+ check_msg_collector([RNode],
+ fun({inviso_event,CP,_,{disconnected,N,_Info}})
+ when CP==CtrlPid ->
+ {true,N};
+ (_) ->
+ false
+ end,
+ 11),
+
+ ?l {ok,_NodeResults5}=inviso:stop_tracing(Nodes),
+ ?l {ok,_NodeResults6}=inviso:stop_nodes(Nodes),
+ ?l shutdown=inviso:stop(),
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+%% TEST CASE: fetch_log test of single straight trace_log file in distributed
+%% environment.
+fetch_log_dist_trace_1(suite) -> [];
+fetch_log_dist_trace_1(doc) ->
+ ["fetch_log test of single straight trace_log file in distributed"
+ "environment."];
+fetch_log_dist_trace_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=lists:map(fun(N)->{N,[{trace,{file,filename:join([PrivDir,
+ "testfile1."++
+ atom_to_list(N)
+ ])}}]} end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+
+ %% Put some output in the logs.
+ ?l inviso:tp(Nodes,math,module_info,0,[]),
+ ?l inviso:tf(Nodes,all,[call]),
+ ?l lists:foreach(fun(N)->rpc:call(N,math,module_info,[]) end,Nodes),
+
+ stop_tracing(Nodes),
+ {H,M,S}=time(),
+ FetchToDir=filename:join([PrivDir,
+ "fetch_log_test1_"++integer_to_list(H)++"_"++
+ integer_to_list(M)++"_"++integer_to_list(S)]),
+ ?l ok=file:make_dir(FetchToDir),
+ ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"),
+ io:format("~p~n",[NodeResults]),
+ ?l true=check_noderesults(RemoteNodes,
+ fun({N,{complete,[{trace_log,[{ok,File}]},{ti_log,[]}]}}) ->
+ ?l File="p1testfile1."++atom_to_list(N),
+ true;
+ (_)->
+ false
+ end,
+ NodeResults),
+ ?l ON=filename:join(PrivDir,"testfile1."),
+ ?l FN=filename:join(FetchToDir,"p1testfile1."),
+ ?l lists:foreach(fun(N)->
+ {ok,#file_info{size=Size}}=
+ file:read_file_info(ON++atom_to_list(N)),
+ {ok,#file_info{size=Size}}=
+ file:read_file_info(FN++atom_to_list(N))
+ end,
+ RemoteNodes),
+ %% Now we wish to see that we get an incomplete if we try to fetch to a
+ %% directory that does not exist.
+ ?l FetchToErrorDir=filename:join([PrivDir,nonexistingingdir]),
+ ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,FetchToErrorDir,"p1"),
+ ?l io:format("NodeResults2:~w~n",[NodeResults2]),
+ ?l true=check_noderesults(RemoteNodes,
+ fun({_,{incomplete,_}}) ->
+ true;
+ (_)->
+ false
+ end,
+ NodeResults2),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+fetch_log_dist_trace_2(suite) -> [];
+fetch_log_dist_trace_2(doc) ->
+ [""];
+fetch_log_dist_trace_2(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+
+ {H,M,S}=time(),
+ ?l Name="wrap"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S),
+ ?l BaseName=filename:join(PrivDir,Name),
+ Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}},
+ {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]}
+ end,
+ ?l TracerDataList=lists:map(Fun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes),
+
+ stop_tracing(Nodes),
+ FetchToDir=filename:join([PrivDir,
+ "fetch_log_test2_"++integer_to_list(H)++"_"++
+ integer_to_list(M)++"_"++integer_to_list(S)]),
+ ?l ok=file:make_dir(FetchToDir),
+ ?l {ok,NodeResults}=inviso:fetch_log(RemoteNodes,FetchToDir,"p1"),
+ io:format("~p~n",[NodeResults]),
+ CheckFun=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
+ Fun2=fun({ok,File}) ->
+ {match,1,_}=
+ regexp:first_match(File,
+ "^"++"p1"++Name++atom_to_list(N)),
+ true;
+ (_) ->
+ false
+ end,
+ ?l true=lists:all(Fun2,FileResults1),
+ ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti",
+ true;
+ (_)->
+ false
+ end,
+ ?l true=check_noderesults(RemoteNodes,CheckFun,NodeResults),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+fetch_log_dist_trace_3(suite) -> [];
+fetch_log_dist_trace_3(doc) ->
+ [""];
+fetch_log_dist_trace_3(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+
+ {H,M,S}=time(),
+ ?l Name="wrap2_"++integer_to_list(H)++"_"++integer_to_list(M)++"_"++integer_to_list(S),
+ ?l BaseName=filename:join(PrivDir,Name),
+ Fun=fun(N)->{N,[{trace,{file,{BaseName++atom_to_list(N),wrap,".log",512,2}}},
+ {ti,{file,BaseName++"_ti_"++atom_to_list(N)++".ti"}}]}
+ end,
+ ?l TracerDataList=lists:map(Fun,Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ fill_and_reach_two_wrapfiles(PrivDir,"^"++Name,Nodes),
+
+ stop_tracing(Nodes),
+ FetchToDir=filename:join([PrivDir,
+ "fetch_log_test3_"++integer_to_list(H)++"_"++
+ integer_to_list(M)++"_"++integer_to_list(S)]),
+ ?l ok=file:make_dir(FetchToDir),
+ ?l {ok,NodeResults1}=inviso:list_logs(Nodes),
+ CheckFun=fun({N,{ok,[{trace_log,PrivDir2,[F1,F2]},{ti_log,PrivDir2,[F3]}]}})->
+ PrivDir2=PrivDir,
+ RegExp="^"++Name++atom_to_list(N)++"[0-9]+"++"\.log",
+ {match,1,_}=regexp:first_match(F1,RegExp),
+ {match,1,_}=regexp:first_match(F2,RegExp),
+ F3=Name++"_ti_"++atom_to_list(N)++".ti",
+ true;
+ (_) ->
+ false
+ end,
+ ?l true=check_noderesults(Nodes,CheckFun,NodeResults1),
+ ?l NodeFileSpecList=lists:map(fun({N,{ok,L}})->{N,L} end,
+ lists:keydelete(node(),1,NodeResults1)),
+ ?l {ok,NodeResults2}=inviso:fetch_log(NodeFileSpecList,FetchToDir,"p1"),
+io:format("~p~n",[NodeResults2]),
+ CheckFun2=fun({N,{complete,[{trace_log,FileResults1},{ti_log,[{ok,TiFile}]}]}}) ->
+ Fun2=fun({ok,File}) ->
+ {match,1,_}=
+ regexp:first_match(File,
+ "^"++"p1"++Name++atom_to_list(N)),
+ true;
+ (_) ->
+ false
+ end,
+ ?l true=lists:all(Fun2,FileResults1),
+ ?l TiFile="p1"++Name++"_ti_"++atom_to_list(N)++".ti",
+ true;
+ (_)->
+ false
+ end,
+ ?l true=check_noderesults(RemoteNodes,CheckFun2,NodeResults2),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+fetch_log_dist_error_1(suite) -> [];
+fetch_log_dist_error_1(doc) ->
+ [""];
+fetch_log_dist_error_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ ?l {ok,_Pid}=inviso:start(), % Start a control component.
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:fetch_log(RemoteNodes,"foo","bar"),
+io:format("~p~n",[NodeResults2]),
+ ?l true=check_noderesults(RemoteNodes,
+ fun({_N,{error,no_tracerdata}})->true;
+ (_)->false
+ end,
+ NodeResults2),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+fetch_log_dist_error_2(suite) -> [];
+fetch_log_dist_error_2(doc) ->
+ [""];
+fetch_log_dist_error_2(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ ?l {ok,_Pid}=inviso:start(), % Start a control component.
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l NodeLogList=lists:map(fun(N)->{N,[{trace_log,
+ PrivDir,
+ ["f1,fil","f2.fil"]},
+ {ti_log,
+ PrivDir,
+ ["f.ti"]}]}
+ end,
+ RemoteNodes),
+ ?l {ok,NodeResults2}=inviso:fetch_log(NodeLogList,"foo","bar"),
+ io:format("~p~n",[NodeResults2]),
+ ?l true=check_noderesults(RemoteNodes,
+ fun({_N,{incomplete,_}}) ->
+ true;
+ (_) ->
+ false
+ end,
+ NodeResults2),
+ ?l NodeTracerData=lists:map(fun(N)->{N,
+ [{trace,{file,filename:join(PrivDir,"foo")}},
+ {ti,{file,filename:join(PrivDir,"bar.ti")}}]}
+ end,
+ RemoteNodes),
+ {ok,NodeResults3}=inviso:fetch_log(NodeTracerData,"foo","bar"),
+ io:format("~p~n",[NodeResults3]),
+%% This should work this way. Now it says complete [], which is not entirely
+%% incorrect. But to follow the sematics of when fetching named files should
+%% say incomplete.
+%% Must do some rework to make that work. No real danger leaving it this way
+%% for now.
+% ?l true=check_noderesults(RemoteNodes,
+% fun({_N,{incomplete,_}}) ->
+% true;
+% (_) ->
+% false
+% end,
+% NodeResults3),
+ stop(Nodes),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: This case tests that the log file merger merges files in the
+%% correct order, based on the timestamps.
+lfm_trace_dist_1(suite) -> [];
+lfm_trace_dist_1(doc) ->
+ [""];
+lfm_trace_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ [RNode1,RNode2|_]=RemoteNodes,
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=
+ lists:map(fun(N)->{N,{file,filename:join([PrivDir,"lfm1_"++atom_to_list(N)])}} end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_traceflags(Nodes),
+
+ {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ inviso_test_proc ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ inviso_test_proc ! {apply,code,which,[lists]},
+
+ deactivate_traceflags(Nodes),
+ deactivate_local_tracing(Nodes),
+ stop_tracing(Nodes),
+ stop(Nodes),
+
+ DestFile=filename:join(PrivDir,"lfm1_out.txt"),
+ ?l {ok,6}=
+ inviso_lfm:merge([{node(),
+ [{trace_log,
+ [filename:join(PrivDir,"lfm1_"++atom_to_list(node()))]}]},
+ {RNode1,
+ [{trace_log,
+ [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode1))]}]},
+ {RNode2,
+ [{trace_log,
+ [filename:join(PrivDir,"lfm1_"++atom_to_list(RNode2))]}]}],
+ DestFile),
+ ?l {ok,FD}=file:open(DestFile,[read]),
+ ?l S1=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode2),S1),
+ ?l S2=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode1),S2),
+ ?l S3=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode1),S3),
+ ?l S4=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(node()),S4),
+ ?l S5=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode2),S5),
+ ?l S6=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(node()),S6),
+ ?l file:close(FD),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: Testing to the full extent that pid-mappings work with both
+%% local and global registration. Also checks that pidmappings can be removed
+%% and that consequently the mappings in the resulting merged file stops.
+lfm_trace_ti_dist_2(suite) -> [];
+lfm_trace_ti_dist_2(doc) ->
+ [""];
+lfm_trace_ti_dist_2(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ [RNode1,RNode2|_]=RemoteNodes,
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=
+ lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"lfm2_"++atom_to_list(N))}},
+ {ti,{file,filename:join(PrivDir,"lfm2_ti_"++atom_to_list(N))}}]}
+ end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok},{ti_log,ok}]}),
+ activate_local_tracing(Nodes),
+ activate_meta_tracing(Nodes),
+ activate_traceflags(Nodes),
+
+ {inviso_test_proc,RNode2} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ {inviso_test_proc,RNode1} ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ inviso_test_proc ! {apply,code,which,[lists]},
+ timer:sleep(300),
+
+ P2=spawn(RNode2,?MODULE,test_proc_loop,[]),
+ P1=spawn(RNode1,?MODULE,test_proc_loop,[]),
+ P0=spawn_link(?MODULE,test_proc_loop,[]),
+ ThisNode=node(),
+ ?l {ok,[{ThisNode,{ok,[1]}}]}=inviso:tf([node()],P0,[call,timestamp]),
+ ?l {ok,[{RNode1,{ok,[1]}}]}=inviso:tf([RNode1],P1,[call,timestamp]),
+ ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P2,[call,timestamp]),
+ P2 ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ P1 ! {apply,code,which,[lists]},
+ timer:sleep(300),
+ P0 ! {apply,code,which,[lists]},
+ timer:sleep(300),
+
+ P3=spawn(RNode2,?MODULE,test_proc_loop,[]),
+ ?l yes=global:register_name(inviso_test_proc_globalname,P3),
+ ?l {ok,[{RNode2,{ok,[1]}}]}=inviso:tf([RNode2],P3,[call,timestamp]),
+ timer:sleep(300),
+ P3 ! {apply,code,which,[lists]},
+ timer:sleep(300),
+
+ P4=rpc:call(RNode1,erlang,whereis,[inviso_test_proc]),
+ ?l true=rpc:call(RNode1,erlang,unregister,[inviso_test_proc]),
+ timer:sleep(300),
+ P4 ! {apply,code,which,[lists]},
+ timer:sleep(300),
+
+ ?l true=rpc:call(RNode1,erlang,register,[inviso_test_proc,P4]),
+
+ ?l global:unregister_name(inviso_test_proc_globalname),
+ timer:sleep(300),
+ ?l P3 ! {apply,code,which,[lists]},
+ timer:sleep(300),
+
+ deactivate_traceflags(Nodes),
+ deactivate_local_tracing(Nodes),
+ stop_tracing(Nodes),
+ stop(Nodes),
+
+ DestFile=filename:join(PrivDir,"lfm2_out.txt"),
+ ?l {ok,10}=
+ inviso_lfm:merge([
+ {node(),
+ [{trace_log,
+ [filename:join(PrivDir,"lfm2_"++atom_to_list(node()))]},
+ {ti_log,
+ [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(node()))]}]},
+ {RNode1,
+ [{trace_log,
+ [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode1))]},
+ {ti_log,
+ [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode1))]}]},
+ {RNode2,
+ [{trace_log,
+ [filename:join(PrivDir,"lfm2_"++atom_to_list(RNode2))]},
+ {ti_log,
+ [filename:join(PrivDir,"lfm2_ti_"++atom_to_list(RNode2))]}]}
+ ],
+ DestFile),
+ ?l {ok,FD}=file:open(DestFile,[read]),
+ ?l S1=io:get_line(FD,""),
+io:format("S1 is:~p~n",[S1]),
+ ?l true=lists:prefix(atom_to_list(RNode2)++" [inviso_test_proc",S1),
+ ?l S2=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S2),
+ ?l S3=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode1)++" [inviso_test_proc",S3),
+ ?l S4=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(node())++" [inviso_test_proc",S4),
+ ?l S5=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode2)++" []",S5),
+ ?l S6=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode1)++" []",S6),
+ ?l S7=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(node())++" []",S7),
+ ?l S8=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode2)++" [{global,inviso_test_proc_globalname}]",S8),
+ ?l S9=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode1)++" []",S9),
+ ?l S10=io:get_line(FD,""),
+ ?l true=lists:prefix(atom_to_list(RNode2)++" []",S10),
+ ?l file:close(FD),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: This tests that the wrapset sorter works.
+handle_logfile_sort_wrapset(suite) -> [];
+handle_logfile_sort_wrapset(doc) ->
+ [""];
+handle_logfile_sort_wrapset(Config) when is_list(Config) ->
+ File0="prefix10.fil",
+ File1="prefix11.fil",
+ File2="prefix12.fil",
+ File3="prefix13.fil",
+ ?l [File0,File1,File2,File3]=
+ inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File1,File0,File3]),
+ File5="prefix15.fil",
+ ?l [File5,File0,File1,File2,File3]=
+ inviso_lfm_tpfreader:handle_logfile_sort_wrapset([File2,File5,File1,File0,File3]),
+ ok.
+%% -----------------------------------------------------------------------------
+
+%% TEST CASE: This case tests that the regexp mechanism in the inviso_rt_lib can
+%% find modules using regexps and that its only_loaded mechanism works.
+%% This test case can not be run when using cover because cover will make the
+%% modules no longer loaded from the path containing "runtime_tools".
+expand_regexp_dist_1(suite) -> [];
+expand_regexp_dist_1(doc) ->
+ [""];
+expand_regexp_dist_1(Config) when is_list(Config) ->
+ case ?t:is_cover() of
+ true ->
+ {skip,"Cover is running"};
+ false ->
+ expand_regexp_dist_1_nocover(Config)
+ end.
+
+expand_regexp_dist_1_nocover(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ [RNode1|_]=RemoteNodes,
+ ?l NodeResults1=inviso_rt_lib:expand_regexp(Nodes,"^inviso_rt.*",[]),
+ ?l L1=length(Nodes),
+ ?l L1=length(NodeResults1),
+ ?l true=lists:all(fun({_,Mods})->
+ ?l 3=length(Mods),
+ ?l true=lists:member(inviso_rt,Mods),
+ ?l true=lists:member(inviso_rt_lib,Mods),
+ ?l true=lists:member(inviso_rt_meta,Mods),
+ true;
+ (_) ->
+ false
+ end,
+ NodeResults1),
+ %% Check the dir-option. In the following inviso_tool_lib shall not be found.
+ ?l NodeResults2=inviso_rt_lib:expand_regexp(Nodes,"runtime_tools","invi.*lib.*",[]),
+?l io:format("NodeResults2:~w~n",[NodeResults2]),
+ ?l L1=length(NodeResults2), % Same number of nodes replying.
+ ?l true=lists:all(fun({_,Mods})->
+ 2=length(Mods),
+ true=lists:member(inviso_as_lib,Mods),
+ true=lists:member(inviso_rt_lib,Mods),
+ true;
+ (_) ->
+ false
+ end,
+ NodeResults2),
+ ?l [{RNode1,[]}]=
+ inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[only_loaded]),
+ ?l [{RNode1,[inviso_testmodule1_foo]}]=
+ inviso_rt_lib:expand_regexp([RNode1],"^inviso_testmodule1.*",[]),
+ ok.
+%% -----------------------------------------------------------------------------
+
+
+only_loaded_dist_1(suite) -> [];
+only_loaded_dist_1(doc) ->
+ [""];
+only_loaded_dist_1(Config) when is_list(Config) ->
+ RemoteNodes=get_remotenodes_config(Config),
+ Nodes=[node()|RemoteNodes],
+ [RNode1|_]=RemoteNodes,
+ PrivDir=filename:join(?config(priv_dir,Config),""),
+ TracerDataList=
+ lists:map(fun(N)->{N,[{trace,{file,filename:join(PrivDir,"ol_1_"++atom_to_list(N))}}]}
+ end,
+ Nodes),
+ start_and_init_tracing2(Nodes,[],TracerDataList,{ok,[{trace_log,ok}]}),
+ ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]),
+ ?l {ok,[{RNode1,{ok,[0]}}]}=
+ inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[only_loaded]),
+ ?l false=rpc:call(RNode1,erlang,module_loaded,[inviso_testmodule1_foo]),
+ ?l {ok,[{RNode1,{ok,[3]}}]}=
+ inviso:tpl([RNode1],inviso_testmodule1_foo,'_','_',[],[]),
+ stop_tracing(Nodes),
+ stop(Nodes),
+ ok.
+
+
+%% ==============================================================================
+%% Common functions setting up inviso.
+%% ==============================================================================
+
+%% Starts controlcomponent and adds runtime components on the nodes specified.
+%% Also initiates tracing on the nodes.
+start_and_init_tracing1(Nodes,Options,TracerData,Reply) when is_list(Nodes) ->
+ ?l {ok,_Pid}=inviso:start(), % Start a control component.
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options),
+ io:format("~p~n",[NodeResults1]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:get_status(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
+ ?l {ok,NodeResults3}=inviso:init_tracing(Nodes,TracerData),
+ ?l true=check_noderesults(Nodes,Reply,NodeResults3),
+ ok.
+start_and_init_tracing2(Nodes,Options,TracerDataList,Reply) ->
+ ?l {ok,_Pid}=inviso:start(), % Start a control component.
+ ?l {ok,NodeResults1}=inviso:add_nodes(Nodes,a_ref,Options),
+ io:format("~p~n",[NodeResults1]),
+ ?l true=check_noderesults(Nodes,{ok,new},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:get_status(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,{new,running}},NodeResults2),
+ ?l {ok,NodeResults4}=inviso:get_tracerdata(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,no_tracerdata},NodeResults4),
+ ?l {ok,NodeResults3}=inviso:init_tracing(TracerDataList),
+ io:format("Tracerdatalist:~p~n",[TracerDataList]),
+ ?l true=check_noderesults(Nodes,Reply,NodeResults3),
+
+ ?l Fun1=fun({N,{ok,TD}}) when is_list(TD)->
+ ?l {value,{trace,Trace}}=lists:keysearch(trace,1,TD),
+ ?l {value,{N,TD2}}=lists:keysearch(N,1,TracerDataList),
+ ?l true=lists:member({trace,Trace},TD2),
+ %% Check that the trace file really exists.
+ ?l case Trace of % Trace={file,FilePortParameters}
+ {file,FileName1} when is_list(FileName1) ->
+ ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName1]);
+ _ -> % This should be extended with more cases.
+ true
+ end,
+ ?l case lists:keysearch(ti,1,TD2) of
+ {value,{_,Ti}} -> % Ok, we have ti too.
+ ?l {value,{_,Ti}}=lists:keysearch(ti,1,TD),
+ ?l FileName2=element(2,Ti),
+ ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName2]),
+ true;
+ false -> % No ti, we are done now.
+ true
+ end;
+ ({N,{ok,{file,FileName}}}) ->
+ ?l {value,{N,{file,FileName}}}=lists:keysearch(N,1,TracerDataList),
+ ?l {ok,_}=rpc:call(N,file,read_file_info,[FileName]),
+ true;
+ ({N,{ok,LogTD}}) -> % The case using a fun.
+ ?l {value,{N,LogTD}}=lists:keysearch(N,1,TracerDataList),
+ true
+ end,
+ ?l {ok,NodeResults5}=inviso:get_tracerdata(Nodes),
+ ?l true=check_noderesults(Nodes,Fun1,NodeResults5),
+ ok.
+%% ------------------------------------------------------------------------------
+
+%% Stops tracing on Nodes.
+stop_tracing(Nodes) when is_list(Nodes) ->
+ ?l {ok,NodeResults1}=inviso:stop_tracing(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,idle},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:get_status(Nodes),
+ ?l true=check_noderesults(Nodes,{ok,{idle,running}},NodeResults2),
+ %% The implementation says that the meta tracer shall be stopped when
+ %% tracing is stopped. Check that.
+ ?l lists:foreach(fun(N)->
+ ok=poll(erlang,whereis,[inviso_rt_meta],undefined,20)
+ end,
+ Nodes).
+%% ------------------------------------------------------------------------------
+
+%% Stops the runtime components on Nodes and stops the control component at this
+%% Erlang node.
+stop(Nodes) when is_list(Nodes) ->
+ ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(P) when is_pid(P)->true end),
+ ?l {ok,NodeResults}=inviso:stop_nodes(Nodes),
+ ?l true=check_noderesults(Nodes,ok,NodeResults),
+ ?l true=check_on_nodes(Nodes,erlang,whereis,[inviso_rt],fun(undefined)->true end),
+ ?l true=is_pid(whereis(inviso_c)),
+ ?l shutdown=inviso:stop(),
+ ?l ok=poll(erlang,whereis,[inviso_c],undefined,20).
+%% ------------------------------------------------------------------------------
+
+%% Help function activating local tracing.
+activate_local_tracing(Nodes) when is_list(Nodes) ->
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,which,1},traced],
+ {traced,false}),
+ ?l {ok,NodeResults}=inviso:tpl(Nodes,code,which,1,[]),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults),
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,which,1},traced],
+ {traced,local}).
+%% ------------------------------------------------------------------------------
+
+%% Help function activating global tracing.
+activate_global_tracing(Nodes) when is_list(Nodes) ->
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,get_path,0},traced],
+ {traced,false}),
+ ?l {ok,NodeResults}=inviso:tp(Nodes,code,get_path,0,[]),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,[1]}})->true end,NodeResults),
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,get_path,0},traced],
+ {traced,global}).
+%% ------------------------------------------------------------------------------
+
+
+%% Help function activating local tracing and using a regexp to point out modules.
+%% Returns the structure of modules and functions that were activated. Must be used
+%% when deactivating.
+activate_global_tracing_regexp(Nodes) when is_list(Nodes) ->
+ %% First find out which modules will be effected.
+ ?l Mods1=inviso_rt_lib:expand_regexp("application.*",[]),
+ ?l true=(length(Mods1)>1), % Should find more than one module!
+ ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1),
+ %% Check that these functions are not traced.
+ io:format("Modules:~w~n",[Mods1]),
+ ?l {ok,NodeResults}=inviso:tp(Nodes,"application.*",'_','_',[],[]),
+ io:format("Here 2~w~n",[NodeResults]),
+ ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
+ io:format("Here 3~n",[]),
+ %% Check again!
+ ?l lists:foreach(fun({M,Funcs})->
+ lists:foreach(fun({F,Arity})->
+ true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{M,F,Arity},traced],
+ {traced,global})
+ end,
+ Funcs)
+ end,
+ Funcs1),
+ Funcs1.
+%% ------------------------------------------------------------------------------
+
+%% Help function as above but uses the dir feature as well.
+activate_global_tracing_regexp_dir(Nodes) when is_list(Nodes) ->
+ %% First find out which modules will be effected.
+ ?l Mods1=inviso_rt_lib:expand_regexp(".*kernel.*","application.*",[]),
+ ?l true=(length(Mods1)>1), % Should find more than one module!
+ ?l Funcs1=lists:foldl(fun(M,Acc)->[{M,M:module_info(exports)}|Acc] end,[],Mods1),
+ %% Check that these functions are not traced.
+ io:format("Modules:~w~n",[Mods1]),
+ ?l {ok,NodeResults}=inviso:tp(Nodes,{".*kernel.*","application.*"},'_','_',[],[]),
+ io:format("Here 2~w~n",[NodeResults]),
+ ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
+ io:format("Here 3~n",[]),
+ %% Check again!
+ ?l lists:foreach(fun({M,Funcs})->
+ lists:foreach(fun({F,Arity})->
+ true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{M,F,Arity},traced],
+ {traced,global})
+ end,
+ Funcs)
+ end,
+ Funcs1),
+ Funcs1.
+%% ------------------------------------------------------------------------------
+
+deactivate_local_tracing(Nodes) when is_list(Nodes) ->
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,which,1},traced],
+ {traced,local}),
+ ?l {ok,NodeResults}=inviso:ctpl(Nodes,code,'_','_'),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults),
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,which,1},traced],
+ {traced,false}).
+%% ------------------------------------------------------------------------------
+
+deactivate_global_tracing(Nodes) when is_list(Nodes) ->
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,get_path,0},traced],
+ {traced,global}),
+ ?l {ok,NodeResults}=inviso:ctp(Nodes,code,'_','_'),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,[N]}})when is_integer(N)->true end,NodeResults),
+ ?l true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{code,get_path,0},traced],
+ {traced,false}).
+%% ------------------------------------------------------------------------------
+
+
+%% Function deactivating the functions activated by activate_global_tracing_regexp/1.
+deactivate_global_tracing_regexp(Nodes,Funcs1) ->
+ ?l lists:foreach(fun({M,Funcs})->
+ lists:foreach(fun({F,Arity})->
+ true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{M,F,Arity},traced],
+ {traced,global})
+ end,
+ Funcs)
+ end,
+ Funcs1),
+ ?l {ok,NodeResults}=inviso:ctp(Nodes,"application.*",'_','_'),
+ ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
+ io:format("Noderesult from deactivate;~w~n",[NodeResults]),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
+ ?l lists:foreach(fun({M,Funcs})->
+ lists:foreach(fun({F,Arity})->
+ true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{M,F,Arity},traced],
+ {traced,false})
+ end,
+ Funcs)
+ end,
+ Funcs1).
+%% ------------------------------------------------------------------------------
+
+%% Function deactivating the functions activated by activate_global_tracing_regexp_dir/1.
+deactivate_global_tracing_regexp_dir(Nodes,Funcs1) ->
+ ?l lists:foreach(fun({M,Funcs})->
+ lists:foreach(fun({F,Arity})->
+ true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{M,F,Arity},traced],
+ {traced,global})
+ end,
+ Funcs)
+ end,
+ Funcs1),
+ ?l {ok,NodeResults}=inviso:ctp(Nodes,{".*kernel.*","application.*"},'_','_'),
+ ?l N=lists:foldl(fun({_,L1},A1)->lists:foldl(fun(_,A2)->A2+1 end,A1,L1) end,0,Funcs1),
+ io:format("Noderesult from deactivate;~w~n",[NodeResults]),
+ ?l true=check_noderesults(Nodes,fun({_,{ok,L}})-> N==lists:sum(L) end,NodeResults),
+ ?l lists:foreach(fun({M,Funcs})->
+ lists:foreach(fun({F,Arity})->
+ true=check_on_nodes(Nodes,
+ erlang,
+ trace_info,
+ [{M,F,Arity},traced],
+ {traced,false})
+ end,
+ Funcs)
+ end,
+ Funcs1).
+%% ------------------------------------------------------------------------------
+
+%% Help function which starts the inviso_test_proc on all nodes and then sets
+%% the call flag on that process.
+activate_traceflags(Nodes) ->
+ ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
+ ?l lists:foreach(fun(N)->
+ P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
+ {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags])
+ end,
+ Nodes),
+ ?l {ok,NodeResults}=inviso:tf(Nodes,inviso_test_proc,[call,timestamp]),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults),
+ ?l lists:foreach(fun(N)->
+ P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
+ {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]),
+ true=lists:member(call,Flags),
+ true=lists:member(timestamp,Flags)
+ end,
+ Nodes),
+ %% Now try a globally registered process.
+ ?l [ANode|_]=Nodes,
+ ?l GPid=spawn(ANode,?MODULE,global_test_proc_init,[]),
+ ?l ok=poll(global,whereis_name,[global_inviso_test_proc],
+ fun(P) when is_pid(P)->true;(_)->false end,
+ 10),
+ ?l {ok,NodeResults2}=
+ inviso:tf(Nodes,{global,global_inviso_test_proc},[call,timestamp]),
+ ?l true=check_noderesults(Nodes,
+ fun({N,{ok,[1]}}) when N==ANode->true;
+ ({_,{ok,[0]}})->true;
+ (_)->false
+ end,
+ NodeResults2),
+ ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]),
+ ?l 2=length(Flags2),
+ ?l true=lists:member(call,Flags2),
+ ?l true=lists:member(timestamp,Flags2),
+ true.
+%% ------------------------------------------------------------------------------
+
+deactivate_traceflags(Nodes) ->
+ ?l lists:foreach(fun(N)->
+ P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
+ {flags,Flags}=rpc:call(N,erlang,trace_info,[P,flags]),
+ true=lists:member(call,Flags),
+ true=lists:member(timestamp,Flags)
+ end,
+ Nodes),
+ ?l {ok,NodeResults}=inviso:ctf(Nodes,inviso_test_proc,[call,timestamp]),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults),
+ ?l lists:foreach(fun(N)->
+ P=rpc:call(N,erlang,whereis,[inviso_test_proc]),
+ {flags,[]}=rpc:call(N,erlang,trace_info,[P,flags])
+ end,
+ Nodes),
+ ?l GPid=global:whereis_name(global_inviso_test_proc),
+ ?l ANode=node(GPid),
+ ?l {flags,Flags2}=rpc:call(ANode,erlang,trace_info,[GPid,flags]),
+ ?l 2=length(Flags2),
+ ?l {ok,NodeResults2}=inviso:ctf(Nodes,{global,global_inviso_test_proc},[call,timestamp]),
+ ?l true=check_noderesults(Nodes,
+ fun({N,{ok,[1]}}) when N==ANode->true;
+ ({_,{ok,[0]}})->true;
+ (_)->false
+ end,
+ NodeResults2).
+%% ------------------------------------------------------------------------------
+
+
+activate_meta_tracing(Nodes) ->
+ ?l {ok,NodeResults1}=inviso:tpm_localnames(),
+ ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults1),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,register,2},meta])
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta])
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=inviso:tpm_globalnames(),
+ ?l true=check_noderesults(Nodes,{{ok,1},{ok,1}},NodeResults2),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,
+ erlang,
+ trace_info,
+ [{global,handle_call,3},meta])
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,
+ erlang,
+ trace_info,
+ [{global,delete_global_name,2},meta])
+ end,
+ Nodes),
+
+ ?l lists:foreach(fun(N)->true=rpc:call(N,
+ ets,
+ insert,
+ [inviso_sideeffect_tab,{tpm_init_func1,0}]),
+ true=rpc:call(N,
+ ets,
+ insert,
+ [inviso_sideeffect_tab,{tpm_call_func1,0}]),
+ true=rpc:call(N,
+ ets,
+ insert,
+ [inviso_sideeffect_tab,{tpm_return_func1,0}])
+ end,
+ Nodes),
+ ?l {ok,NodeResults3}=
+ inviso:init_tpm(lists,
+ module_info,
+ 0,
+ {?MODULE,tpm_init_func1},
+ {?MODULE,tpm_call_func1},
+ {?MODULE,tpm_return_func1},
+ {?MODULE,tpm_remove_func1}),
+ ?l true=check_noderesults(Nodes,ok,NodeResults3),
+ ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
+ ?l {ok,NodeResults3a}=
+ inviso:init_tpm(lists,
+ module_info,
+ 0,
+ {?MODULE,tpm_init_func1},
+ {?MODULE,tpm_call_func1},
+ {?MODULE,tpm_return_func1},
+ {?MODULE,tpm_remove_func1}),
+ ?l true=check_noderesults(Nodes,{error,already_initiated},NodeResults3a),
+% %% Try more forbidden things. Wildcards not allowed in meta tracing!
+% ?l {ok,NodeResults3b}=inviso:tpm(Nodes,lists,'_',0,[{'_',[],[{return_trace}]}]),
+% io:format("The noderesults3b is:~w~n",[NodeResults3b]),
+% ?l true=check_noderesults(Nodes,{error,bad_mfa},NodeResults3b),
+ ?l {ok,NodeResults3c}=inviso:tpm(Nodes,lists,module_info,0,[{'_',[],[{return_trace}]}]),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults3c),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta])
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20),
+ ?l lists:foreach(fun(N)->rpc:call(N,lists,module_info,[]) end,Nodes),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,2}],20),
+
+ ?l {ok,NodeResults4}=
+ inviso:init_tpm(math,
+ module_info,
+ 1,
+ {?MODULE,tpm_init_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_call_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_return_func2}, % Does not exist on purpose.
+ {?MODULE,tpm_remove_func2}), % Does not exist on purpose.
+ ?l true=check_noderesults(Nodes,ok,NodeResults4),
+ ?l {ok,NodeResults5}=
+ inviso:tpm_ms(math,module_info,1,ms1,[{'_',[],[{return_trace}]}]),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults5),
+ ?l lists:foreach(fun(N)->{meta_match_spec,[{'_',[],[{return_trace}]}]}=
+ rpc:call(N,erlang,trace_info,[{math,module_info,1},
+ meta_match_spec])
+ end,
+ Nodes),
+
+ ?l {ok,NodeResults6}=inviso:tpm_ms(math,module_info,1,ms2,[{[exports],[],[]}]),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults6),
+ ?l lists:foreach(fun(N)->{meta_match_spec,[{[exports],[],[]},{'_',[],[{return_trace}]}]}=
+ rpc:call(N,erlang,trace_info,[{math,module_info,1},
+ meta_match_spec])
+ end,
+ Nodes),
+ ?l {ok,NodeResults7}=inviso:tpm_ms(math,module_info,1,ms3,[{[attributes],[],[]}]),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults7),
+ ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]},
+ {[exports],[],[]},
+ {'_',[],[{return_trace}]}]}=
+ rpc:call(N,erlang,trace_info,[{math,module_info,1},
+ meta_match_spec])
+ end,
+ Nodes),
+ ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms2),
+ ?l true=check_noderesults(Nodes,ok,NodeResults8),
+ ?l lists:foreach(fun(N)->{meta_match_spec,[{[attributes],[],[]},
+ {'_',[],[{return_trace}]}]}=
+ rpc:call(N,erlang,trace_info,[{math,module_info,1},
+ meta_match_spec])
+ end,
+ Nodes),
+ ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]),
+ ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms3),
+ ?l io:format("whereis:~w~n",[lists:map(fun(N)->rpc:call(N,erlang,whereis,[inviso_rt_meta]) end,Nodes)]),
+ ?l {ok,NodeResults8}=inviso:ctpm_ms(math,module_info,1,ms1),
+ ?l lists:foreach(fun(N)->{meta_match_spec,false}=
+ rpc:call(N,erlang,trace_info,[{math,module_info,1},
+ meta_match_spec])
+ end,
+ Nodes),
+
+ %% Now try to do this with exception tracing instead.
+ %% Reset the side effect tables.
+ ?l lists:foreach(fun(N)->true=rpc:call(N,
+ ets,
+ insert,
+ [inviso_sideeffect_tab,{tpm_init_func1,0}]),
+ true=rpc:call(N,
+ ets,
+ insert,
+ [inviso_sideeffect_tab,{tpm_call_func1,0}]),
+ true=rpc:call(N,
+ ets,
+ insert,
+ [inviso_sideeffect_tab,{tpm_return_func1,0}])
+ end,
+ Nodes),
+ ?l {ok,NodeResults9}=
+ inviso:init_tpm(?MODULE,
+ failing_function,
+ 1,
+ {?MODULE,tpm_init_func1},
+ {?MODULE,tpm_call_func1},
+ {?MODULE,tpm_return_func1},
+ {?MODULE,tpm_remove_func1}),
+ ?l true=check_noderesults(Nodes,ok,NodeResults9),
+ ?l [{_,1}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
+ ?l {ok,NodeResults10}=inviso:tpm(Nodes,?MODULE,failing_function,1,[{'_',[],[{exception_trace}]}]),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults10),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,erlang,trace_info,[{?MODULE,failing_function,1},meta])
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[nofailure]) end,Nodes),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,1}],20),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,1}],20),
+ ?l lists:foreach(fun(N)->rpc:call(N,?MODULE,failing_function,[failure]) end,Nodes),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_call_func1],[{tpm_call_func1,2}],20),
+ ?l ok=poll(ets,lookup,[inviso_sideeffect_tab,tpm_return_func1],[{tpm_return_func1,3}],20),
+
+ ok.
+%% ------------------------------------------------------------------------------
+
+%% This function is for testing that appending the tracer to a trace action term
+%% works.
+activate_deactivate_meta_tracing_tracer(Nodes) ->
+ ?l {ok,NodeResults}=
+ inviso:tpm_tracer(Nodes,lists,module_info,0,[{'_',[],[{trace,[all],[call]}]}],void),
+ ?l true=check_noderesults(Nodes,{ok,1},NodeResults),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]),
+ {meta_match_spec,[{'_',[],[{trace,[all],Enable}]}]}=
+ rpc:call(N,erlang,trace_info,[{lists,module_info,0},
+ meta_match_spec]),
+ true=list_search(Enable,fun({{tracer,P}}) when is_port(P)->true;
+ (_) -> false
+ end)
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=
+ inviso:ctpm(Nodes,lists,module_info,0),
+ ?l true=check_noderesults(Nodes,ok,NodeResults2),
+ ?l lists:foreach(fun(N)->{meta,false}=
+ rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta])
+ end,
+ Nodes),
+ ok.
+%% ------------------------------------------------------------------------------
+
+deactivate_meta_tracing(Nodes) ->
+ ?l lists:foreach(fun(N)->{meta,P}=
+ rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]),
+ true=is_pid(P)
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->{meta,P}=
+ rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta]),
+ true=is_pid(P)
+ end,
+ Nodes),
+ ?l {ok,NodeResults1}=inviso:ctpm_localnames(),
+ ?l lists:foreach(fun(N)->{meta,false}=
+ rpc:call(N,erlang,trace_info,[{erlang,register,2},meta]) end,
+ Nodes),
+ ?l lists:foreach(fun(N)->{meta,false}=
+ rpc:call(N,erlang,trace_info,[{erlang,unregister,1},meta])
+ end,
+ Nodes),
+ ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1),
+
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,
+ erlang,
+ trace_info,
+ [{global,handle_call,3},meta])
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->P=rpc:call(N,erlang,whereis,[inviso_rt_meta]),
+ {meta,P}=rpc:call(N,
+ erlang,
+ trace_info,
+ [{global,delete_global_name,2},meta])
+ end,
+ Nodes),
+ ?l {ok,NodeResults1b}=inviso:ctpm_globalnames(),
+ ?l true=check_noderesults(Nodes,{ok,ok},NodeResults1b),
+ ?l lists:foreach(fun(N)->
+ {meta,false}=rpc:call(N,
+ erlang,
+ trace_info,
+ [{global,handle_call,3},meta])
+ end,
+ Nodes),
+ ?l lists:foreach(fun(N)->
+ {meta,false}=rpc:call(N,
+ erlang,
+ trace_info,
+ [{global,delete_global_name,2},meta])
+ end,
+ Nodes),
+
+ ?l lists:foreach(fun(N)->{meta,P}=
+ rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]),
+ true=is_pid(P)
+ end,
+ Nodes),
+ ?l {ok,NodeResults2}=inviso:ctpm(lists,module_info,0),
+ ?l true=check_noderesults(Nodes,ok,NodeResults2),
+ ?l lists:foreach(fun(N)->{meta,false}=
+ rpc:call(N,erlang,trace_info,[{lists,module_info,0},meta]) end,
+ Nodes),
+ ?l [{_,0}]=ets:lookup(inviso_sideeffect_tab,tpm_init_func1),
+ ?l {ok,NodeResults3}=inviso:ctpm(math,module_info,1),
+ ?l true=check_noderesults(Nodes,ok,NodeResults3),
+ ok.
+%% ------------------------------------------------------------------------------
+
+%% Functions acting as callbacks for testing the meta tracing mechanisms.
+tpm_init_func1(_M,_F,_Arity,PublLD) ->
+ ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,1),
+ {ok,PublLD,void}.
+tpm_call_func1(_Pid,{call,_Args,_TS},PublLD) ->
+ ets:update_counter(inviso_sideeffect_tab,tpm_call_func1,1),
+ {ok,PublLD,void}.
+tpm_return_func1(_Pid,{return_from,_ReturnVal,_TS},PublLD) ->
+ ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
+ {ok,PublLD,void};
+tpm_return_func1(_Pid,{exception_from,_ReturnVal,_TS},PublLD) ->
+ ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
+ ets:update_counter(inviso_sideeffect_tab,tpm_return_func1,1),
+ {ok,PublLD,void}.
+tpm_remove_func1(_M,_F,_Arity,PublLD) ->
+ ets:update_counter(inviso_sideeffect_tab,tpm_init_func1,-1),
+ {ok,PublLD}.
+%% ------------------------------------------------------------------------------
+
+
+%% Help function which traces on a function and makes function calls until there
+%% are two files in the wrap-set.
+fill_and_reach_two_wrapfiles(PrivDir,RegExp,Nodes) ->
+ ?l lists:foreach(fun(N)->spawn(N,?MODULE,test_proc_init,[]) end,Nodes),
+ ?l {ok,NodeResults1}=inviso:tpl(Nodes,?MODULE,test_function,0,[]),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults1),
+ ?l {ok,NodeResults2}=inviso:tf(Nodes,inviso_test_proc,[call]),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults2),
+ fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Nodes),
+ ?l {ok,NodeResults3}=inviso:ctf(Nodes,inviso_test_proc,[call]),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults3),
+ ?l {ok,NodeResults4}=inviso:ctpl(Nodes,?MODULE,test_function,0),
+ ?l true=check_noderesults(Nodes,{ok,[1]},NodeResults4),
+ ok.
+
+fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,[Node|Rest]) ->
+ ?l ok=rpc:call(Node,?MODULE,fill_and_reach_two_wrapfiles_3,[PrivDir,RegExp]),
+ fill_and_reach_two_wrapfiles_2(PrivDir,RegExp,Rest);
+fill_and_reach_two_wrapfiles_2(_,_,[]) ->
+ ok.
+
+fill_and_reach_two_wrapfiles_3(Dir,RegExp) ->
+ ok=send_to_test_proc({apply,?MODULE,test_function,[]},
+ fun reach_two_wraps_stopfun/1,
+ {Dir,RegExp++atom_to_list(node())},
+ 100).
+
+%% Help function intended to be used as fun in a send_to_test_proc/4 call.
+%% The function lists the content of Dir and looks for occurancies of String.
+%% If two files containing the string String are found, 'done' is returned.
+%% Otherwise 'continue'.
+reach_two_wraps_stopfun({Dir,RegExp}) ->
+ case file:list_dir(Dir) of
+ {ok,FileNames} ->
+ case how_many_files_regexp(FileNames,RegExp,0) of
+ {ok,2} ->
+ done;
+ _ ->
+ continue
+ end;
+ {error,_Reason} ->
+ error
+ end.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% Help function for the overload tests. These functions are used as callbacks.
+%% ------------------------------------------------------------------------------
+
+overload1(_) ->
+ ets:update_counter(inviso_sideeffect_tab,ovl1,1),
+ ok.
+%% This function is used when timeout occurs inside the runtime component.
+%% That is it is time to check for overload.
+overload2({timeout,overload2i_data}) ->
+ ets:update_counter(inviso_sideeffect_tab,ovl2,1),
+ ok.
+overload2i() ->
+ ets:insert(inviso_sideeffect_tab,{ovl2,0}),
+ {ok,overload2i_data}.
+overload2r(overload2i_data) ->
+ ets:delete(inviso_sideeffect_tab,ovl2).
+
+%% This function is used when timeout occurs inside the runtime component.
+%% That is it is time to check for overload.
+overload3({timeout,overload3i_data}) ->
+ ets:update_counter(inviso_sideeffect_tab,ovl3,1),
+ ok;
+overload3(_) -> % Must handle garbage too.
+ ignore.
+overload3i() ->
+ ets:insert(inviso_sideeffect_tab,{ovl3,0}),
+ {ok,overload3i_data}.
+overload3r(overload3i_data) ->
+ ets:insert(inviso_sideeffect_tab,{ovl3r,done}),
+ ets:delete(inviso_sideeffect_tab,ovl3).
+
+overload4(_) ->
+ case ets:lookup(inviso_sideeffect_tab,ovl4_suspend) of
+ [] -> % We are supposed to be running.
+ ets:update_counter(inviso_sideeffect_tab,ovl4,1),
+ ok;
+ [_] ->
+ {suspend,test}
+ end.
+
+%% This function is used when overload check is done by icomming message.
+overload5({msg,{test_of_loadcheck,overload5i_data}}) ->
+ ets:update_counter(inviso_sideeffect_tab,ovl5,1),
+ ok;
+overload5(_) ->
+ ignore.
+overload5i() ->
+ ets:insert(inviso_sideeffect_tab,{ovl5,0}),
+ {ok,overload5i_data}.
+overload5r(overload5i_data) ->
+ ets:delete(inviso_sideeffect_tab,ovl5),
+ ets:insert(inviso_sideeffect_tab,{ovl5r,done});
+overload5r(X) ->
+ erlang:display({'***',overload5r,X}).
+
+overload6(_) ->
+ ets:update_counter(inviso_sideeffect_tab,ovl6,1),
+ ok.
+%% ------------------------------------------------------------------------------
+
+%% ------------------------------------------------------------------------------
+%% Help function for the subscription tests. These function implements a collector
+%% process which will subscribe to inviso_events from the control component.
+%% ------------------------------------------------------------------------------
+
+%% Function which can be used to check if an inviso_event has arrived. The function
+%% takes a fun which tests the messages.
+check_msg_collector([],_,_) ->
+ true;
+check_msg_collector(_,_,0) ->
+ false;
+check_msg_collector(Nodes,Fun,T) ->
+ Ref=make_ref(),
+ inviso_collector_proc ! {fetch_message,self(),Ref,Fun},
+ receive
+ {inviso,Ref,{true,Node}} ->
+ check_msg_collector(lists:delete(Node,Nodes),Fun,T-1);
+ {inviso,Ref,false} ->
+ timer:sleep(100),
+ check_msg_collector(Nodes,Fun,T-1)
+ end.
+
+%% Spawn on this function to get a subscriber.
+inviso_msg_collector() ->
+ register(inviso_collector_proc,self()),
+ inviso_msg_collector_loop([]).
+
+inviso_msg_collector_loop(Msgs) ->
+ receive
+ {fetch_message,From,Ref,Fun} ->
+ {NewMsgs,Reply}=inviso_msg_collector_selector(Msgs,Fun,[]),
+ From ! {inviso,Ref,Reply},
+ inviso_msg_collector_loop(NewMsgs);
+ Msg ->
+ inviso_msg_collector_loop([Msg|Msgs])
+ end.
+
+inviso_msg_collector_selector([M|Rest],Fun,Accum) ->
+ case Fun(M) of
+ {true,X} ->
+ {Rest++Accum,{true,X}};
+ _ ->
+ inviso_msg_collector_selector(Rest,Fun,[M|Accum])
+ end;
+inviso_msg_collector_selector([],_,Accum) ->
+ {Accum,false}.
+%% ------------------------------------------------------------------------------
+
+
+%% ==============================================================================
+%% Help functions
+%% ==============================================================================
+
+list_search([E|Rest],Fun) ->
+ case Fun(E) of
+ true ->
+ true;
+ false ->
+ list_search(Rest,Fun)
+ end;
+list_search([],_Fun) ->
+ false.
+%% ------------------------------------------------------------------------------
+
+%% Help function checking that there is a Result for each node in Nodes.
+%% Returns 'true' if successful.
+check_noderesults(Nodes,Fun,[{Node,Result}|Rest]) when is_function(Fun) ->
+ case Fun({Node,Result}) of
+ true ->
+ case lists:member(Node,Nodes) of
+ true ->
+ check_noderesults(lists:delete(Node,Nodes),Fun,Rest);
+ false -> % Not good.
+ unknown_node_in_returnvalue
+ end;
+ _ ->
+ illegal_result
+ end;
+check_noderesults(Nodes,Result,[{Node,Result}|Rest]) ->
+ case lists:member(Node,Nodes) of
+ true ->
+ check_noderesults(lists:delete(Node,Nodes),Result,Rest);
+ false -> % Not good.
+ unknown_node_in_returnvalue
+ end;
+check_noderesults([],_,[]) ->
+ true;
+check_noderesults(X,Y,Z) ->
+ io:format("Bad arguments to check noderesults:~w~n~w~n~w~n",[X,Y,Z]),
+ false.
+%% ------------------------------------------------------------------------------
+
+%% Help function doing rpc on all nodes in Nodes calling M:F. Returns 'true' if
+%% successful.
+check_on_nodes([Node|Rest],M,F,Args,Result) when Node==node() ->
+ if
+ is_function(Result) ->
+ ?l true=Result(apply(M,F,Args));
+ true ->
+ ?l Result=apply(M,F,Args)
+ end,
+ check_on_nodes(Rest,M,F,Args,Result);
+check_on_nodes([Node|Rest],M,F,Args,Result) ->
+ if
+ is_function(Result) ->
+ ?l true=Result(rpc:call(Node,M,F,Args));
+ true ->
+ ?l Result=rpc:call(Node,M,F,Args)
+ end,
+ check_on_nodes(Rest,M,F,Args,Result);
+check_on_nodes([],_,_,_,_) ->
+ true.
+%% ------------------------------------------------------------------------------
+
+%% Help function which given a list of files searches through it and returns
+%% how many satisfies the RegExp.
+%% Returns {ok,N}.
+how_many_files_regexp([],_,N) ->
+ {ok,N};
+how_many_files_regexp([FName|Rest],RegExp,N) ->
+ case regexp:first_match(FName,RegExp) of
+ {match,1,_} ->
+ how_many_files_regexp(Rest,RegExp,N+1);
+ nomatch ->
+ how_many_files_regexp(Rest,RegExp,N);
+ {error,Reason} ->
+ test_server:fail(Reason)
+ end.
+%% ------------------------------------------------------------------------------
+
+%% Help function killing a bunch of registered processes.
+process_killer([RegName|Rest]) ->
+ case whereis(RegName) of
+ undefined ->
+ case global:whereis_name(RegName) of
+ undefined ->
+ process_killer(Rest);
+ P when is_pid(P) ->
+ if
+ node()==node(P) ->
+ exit(P,kill);
+ true ->
+ true
+ end,
+ process_killer(Rest)
+ end;
+ P when is_pid(P) ->
+ exit(P,kill),
+ process_killer(Rest)
+ end;
+process_killer([]) ->
+ true.
+%% ------------------------------------------------------------------------------
+
+%% Help function which waits for a function call to become Result. This is useful
+%% if what we are waiting for can happend independantly of indications we have
+%% access to.
+poll(_,_,_,_,0) ->
+ error;
+poll(M,F,Args,Result,Times) ->
+ try apply(M,F,Args) of
+ What when is_function(Result) ->
+ case Result(What) of
+ true ->
+ ok;
+ _ ->
+ timer:sleep(100),
+ poll(M,F,Args,Result,Times-1)
+ end;
+ Result ->
+ ok;
+ _ ->
+ timer:sleep(100),
+ poll(M,F,Args,Result,Times-1)
+ catch
+ error:Reason ->
+ io:format("Apply in suite-function poll/5 failed, ~w~n",[Reason]),
+ timer:sleep(100),
+ poll(M,F,Args,Result,Times-1)
+ end.
+%% ------------------------------------------------------------------------------
+
+insert_remotenode_config(Name,Node,Config) ->
+ [{remotenode,{Name,Node}}|Config].
+%% ------------------------------------------------------------------------------
+
+insert_timetraphandle_config(Handle,Config) ->
+ [{timetraphandle,Handle}|Config].
+%% ------------------------------------------------------------------------------
+
+get_remotenode_config(Name, [{remotenode, {Name, Node}}| _Cs]) ->
+ Node;
+get_remotenode_config(Name, [_ | Cs]) ->
+ get_remotenode_config(Name, Cs);
+get_remotenode_config(Name, []) ->
+ exit({no_remotenode, Name}).
+
+%% ------------------------------------------------------------------------------
+
+get_timetraphandle_config(Config) ->
+ {value,{_,Handle}}=lists:keysearch(timetraphandle,1,Config),
+ Handle.
+%% ------------------------------------------------------------------------------
+
+get_remotenodes_config([{remotenode,{_Name,Node}}|Config]) ->
+ [Node|get_remotenodes_config(Config)];
+get_remotenodes_config([_|Config]) ->
+ get_remotenodes_config(Config);
+get_remotenodes_config([]) ->
+ [].
+%% ------------------------------------------------------------------------------
+
+remove_remotenode_config(Name, [{remotenode, {Name, _}} | Cs]) ->
+ Cs;
+remove_remotenode_config(Name, [C | Cs]) ->
+ [C | remove_remotenode_config(Name, Cs)];
+remove_remotenode_config(_Name, []) ->
+ [].
+
+%% ------------------------------------------------------------------------------
+
+remove_timetraphandle_config(Config) ->
+ lists:keydelete(timetraphandle,1,Config).
+%% ------------------------------------------------------------------------------
+
+%% This function can be meta traced in order to check that exception_trace works.
+%% Must be exported.
+failing_function(nofailure) ->
+ true;
+failing_function(failure) ->
+ exit(failure).
+%% ------------------------------------------------------------------------------
+
+%% ==============================================================================
+%% Code for a test process which can be started.
+%% ==============================================================================
+
+test_proc_init() ->
+ register(inviso_test_proc,self()),
+ test_proc_loop().
+
+test_proc_loop() ->
+ receive
+ {apply,M,F,Args} ->
+ apply(M,F,Args),
+ test_proc_loop();
+ X ->
+ io:format("Got ~w~n",[X]),
+ test_proc_loop()
+ end.
+
+global_test_proc_init() ->
+ global:register_name(global_inviso_test_proc,self()),
+ test_proc_loop().
+%% ------------------------------------------------------------------------------
+
+send_to_test_proc(_,_,_,0) ->
+ error;
+send_to_test_proc(Msg,Fun,FunArg,N) ->
+ inviso_test_proc ! Msg,
+ case Fun(FunArg) of
+ done ->
+ ok;
+ error ->
+ test_server:fail(send_to_test_proc);
+ _ ->
+ send_to_test_proc(Msg,Fun,FunArg,N-1)
+ end.
+%% ------------------------------------------------------------------------------
+
+
+%% This function is here to be traced on by the inviso_test_proc. Must be exported.
+test_function() ->
+ 1+1.
+%% ------------------------------------------------------------------------------
+
+
+%% ==============================================================================
+%% Code for a test side effect table process.
+%% ==============================================================================
+
+%% The side effect logger is a process owning a public ETS table. The idea is that
+%% various callback functions can write in the table when called. In that way
+%% correct calling of the call-backs can be verified.
+start_side_effect_logger(Node) ->
+ ?l true=is_pid(spawn(Node,?MODULE,side_effect_logger_proc,[])),
+ ?l ok=poll(rpc,call,[Node,ets,lookup,[inviso_sideeffect_tab,foo]],[],20).
+
+%% This one must be exported.
+side_effect_logger_proc() ->
+ register(inviso_tab_proc,self()), % So we can kill it later.
+ ets:new(inviso_sideeffect_tab,[public,named_table]),
+ side_effect_logger_proc_2().
+
+side_effect_logger_proc_2() ->
+ receive
+ _X -> % This process is not expecting anything!
+ side_effect_logger_proc_2()
+ end.
+%% ------------------------------------------------------------------------------
diff --git a/lib/runtime_tools/test/inviso_testmodule1_foo.erl b/lib/runtime_tools/test/inviso_testmodule1_foo.erl
new file mode 100644
index 0000000000..a7a22cad39
--- /dev/null
+++ b/lib/runtime_tools/test/inviso_testmodule1_foo.erl
@@ -0,0 +1,9 @@
+-module(inviso_testmodule1_foo).
+
+-compile(export_all).
+
+%% The purpose of this module is simply to have a module that is
+%% guaranteed not loaded.
+
+foo() ->
+ true.
diff --git a/lib/runtime_tools/test/runtime_tools.cover b/lib/runtime_tools/test/runtime_tools.cover
new file mode 100644
index 0000000000..ef850bc377
--- /dev/null
+++ b/lib/runtime_tools/test/runtime_tools.cover
@@ -0,0 +1,3 @@
+{incl_app,runtime_tools,details}.
+
+{excl_mods, runtime_tools, [observer_backend]}.
diff --git a/lib/runtime_tools/test/runtime_tools.spec b/lib/runtime_tools/test/runtime_tools.spec
new file mode 100644
index 0000000000..0a24232be8
--- /dev/null
+++ b/lib/runtime_tools/test/runtime_tools.spec
@@ -0,0 +1 @@
+{suites,"../runtime_tools_test",all}.
diff --git a/lib/runtime_tools/test/runtime_tools_SUITE.erl b/lib/runtime_tools/test/runtime_tools_SUITE.erl
new file mode 100644
index 0000000000..b26f3dd881
--- /dev/null
+++ b/lib/runtime_tools/test/runtime_tools_SUITE.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(runtime_tools_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+%% Test server specific exports
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([app_file/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_file].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+app_file(suite) ->
+ [];
+app_file(doc) ->
+ ["Testing .app file"];
+app_file(Config) when is_list(Config) ->
+ ?line ok = ?t:app_test(runtime_tools),
+ ok.
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index 4bbdef19de..6ed98f697e 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.8.3
+RUNTIME_TOOLS_VSN = 1.8.5
diff --git a/lib/sasl/doc/src/alarm_handler.xml b/lib/sasl/doc/src/alarm_handler.xml
index e4501ce5f0..87be6d2a9e 100644
--- a/lib/sasl/doc/src/alarm_handler.xml
+++ b/lib/sasl/doc/src/alarm_handler.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml
index 5182889710..89bcf23b5e 100644
--- a/lib/sasl/doc/src/appup.xml
+++ b/lib/sasl/doc/src/appup.xml
@@ -4,7 +4,7 @@
<fileref>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -174,11 +174,19 @@
<c>remove</c> and <c>purge</c>.</p>
<pre>
{add_application, Application}
+{add_application, Application, Type}
Application = atom()
+ Type = permanent | transient | temporary | load | none
</pre>
<p>Adding an application means that the modules defined by
the <c>modules</c> key in the <c>.app</c> file are loaded using
- <c>add_module</c>, then the application is started.</p>
+ <c>add_module</c>.</p>
+ <p><c>Type</c> defaults to <c>permanent</c> and specifies the start type
+ of the application. If <c>Type = permanent | transient | temporary</c>,
+ the application will be loaded and started in the corresponding way,
+ see <c>application(3)</c>. If <c>Type = load</c>, the application will
+ only be loaded. If <c>Type = none</c>, the application will be neither
+ loaded nor started, although the code for its modules will be loaded.</p>
<pre>
{remove_application, Application}
Application = atom()
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index e528af2522..7941e371a0 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -30,6 +30,39 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 2.1.9.3</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Honor start type in .rel files when building relup files</p>
+ <p>
+ Previously, relup file always included an
+ application:start(Application, permanent) apply
+ instruction for every application that appear in the
+ UpTo/DowFrom release file, whatever their start type in
+ the release file.</p>
+ <p>
+ The new implementation fixes this bug by honoring the
+ start type according to the rel(5) format. If the start
+ type is none, no apply line is included in the relup. If
+ the start type is load, the relup includes instruction to
+ only load the application. Otherwise, the relup includes
+ an instruction to start the application to the according
+ type.</p>
+ <p>
+ The fix is implemented by adding a new parameter to the
+ add_application high level appup instruction. This new
+ parameter is documented in appup(5).</p>
+ <p>
+ Own Id: OTP-9097</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 2.1.9.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/sasl/doc/src/part_notes_history.xml b/lib/sasl/doc/src/part_notes_history.xml
index 2726d73684..d8d48bfd46 100644
--- a/lib/sasl/doc/src/part_notes_history.xml
+++ b/lib/sasl/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml
index 108f5e7f3e..470adf3c03 100644
--- a/lib/sasl/doc/src/rel.xml
+++ b/lib/sasl/doc/src/rel.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/doc/src/relup.xml b/lib/sasl/doc/src/relup.xml
index f7d9fcdd42..7aba7e58ba 100644
--- a/lib/sasl/doc/src/relup.xml
+++ b/lib/sasl/doc/src/relup.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/doc/src/script.xml b/lib/sasl/doc/src/script.xml
index 6bac07d106..17cc64f08e 100644
--- a/lib/sasl/doc/src/script.xml
+++ b/lib/sasl/doc/src/script.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/sasl/doc/src/systools.xml b/lib/sasl/doc/src/systools.xml
index 296553bb12..883c9c372b 100644
--- a/lib/sasl/doc/src/systools.xml
+++ b/lib/sasl/doc/src/systools.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -130,7 +130,7 @@
<fsummary>Generate a boot script <c>.script/.boot</c>.</fsummary>
<type>
<v>Name = string()</v>
- <v>Opt = no_module_tests | {path,[Dir]} | local | {variables,[Var]} | exref | {exref,[App]}] | silent | {outdir,Dir}</v>
+ <v>Opt = src_tests | {path,[Dir]} | local | {variables,[Var]} | exref | {exref,[App]}] | silent | {outdir,Dir}</v>
<v>&nbsp;Dir = string()</v>
<v>&nbsp;Var = {VarName,Prefix}</v>
<v>&nbsp;&nbsp;VarName = Prefix = string()</v>
@@ -174,15 +174,13 @@
the applications.</p>
</item>
<item>
- <p>There should no duplicated modules, that is, modules with
+ <p>There should be no duplicated modules, that is, modules with
the same name but belonging to different applications.</p>
</item>
<item>
- <p>A warning is issued if the source code for a module is
- missing or newer than the object code. <br></br>
-
- If the <c>no_module_tests</c> option is specified, this
- check is omitted.</p>
+ <p>If the <c>src_tests</c> option is specified, a
+ warning is issued if the source code for a module is
+ missing or newer than the object code.</p>
</item>
</list>
<p>The applications are sorted according to the dependencies
@@ -242,7 +240,7 @@
<fsummary>Create a release package.</fsummary>
<type>
<v>Name = string()</v>
- <v>Opt = {dirs,[IncDir]} | {path,[Dir]} | {variables,[Var]} | {var_tar,VarTar} | {erts,Dir} | no_module_tests | exref | {exref,[App]} | silent | {outdir,Dir}</v>
+ <v>Opt = {dirs,[IncDir]} | {path,[Dir]} | {variables,[Var]} | {var_tar,VarTar} | {erts,Dir} | src_tests | exref | {exref,[App]} | silent | {outdir,Dir}</v>
<v>&nbsp;Dir = string()</v>
<v>&nbsp;IncDir = src | include | atom()</v>
<v>&nbsp;Var = {VarName,PreFix}</v>
@@ -330,7 +328,7 @@ myapp-1/ebin/myapp.app
system <c>{erts,Dir}</c> is copied to <c>erts-ErtsVsn/bin</c>.</p>
<p>All checks performed with the <c>make_script</c> function
are performed before the release package is created. The
- <c>no_module_tests</c> and <c>exref</c> options are also
+ <c>src_tests</c> and <c>exref</c> options are also
valid here.</p>
<p>The return value and the handling of errors and warnings
are the same as described for <c>make_script</c> above.</p>
diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl
index 4c43277848..b60aa847df 100644
--- a/lib/sasl/src/release_handler.erl
+++ b/lib/sasl/src/release_handler.erl
@@ -791,7 +791,7 @@ check_rel(Root, RelFile, Masters) ->
check_rel(Root, RelFile, LibDirs, Masters) ->
case consult(RelFile, Masters) of
{ok, [RelData]} ->
- check_rel_data(RelData, Root, LibDirs);
+ check_rel_data(RelData, Root, LibDirs, Masters);
{ok, _} ->
throw({error, {bad_rel_file, RelFile}});
{error, Reason} when is_tuple(Reason) ->
@@ -800,7 +800,8 @@ check_rel(Root, RelFile, LibDirs, Masters) ->
throw({error, {FileError, RelFile}})
end.
-check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
+check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs,
+ Masters) ->
Libs2 =
lists:map(fun(LibSpec) ->
Lib = element(1, LibSpec),
@@ -810,7 +811,7 @@ check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
case lists:keysearch(Lib, 1, LibDirs) of
{value, {_Lib, _Vsn, Dir}} ->
Path = filename:join(Dir,LibName),
- check_path(Path),
+ check_path(Path, Masters),
Path;
_ ->
filename:join([Root, "lib", LibName])
@@ -820,19 +821,34 @@ check_rel_data({release, {Name, Vsn}, {erts, EVsn}, Libs}, Root, LibDirs) ->
Libs),
#release{name = Name, vsn = Vsn, erts_vsn = EVsn,
libs = Libs2, status = unpacking};
-check_rel_data(RelData, _Root, _LibDirs) ->
+check_rel_data(RelData, _Root, _LibDirs, _Masters) ->
throw({error, {bad_rel_data, RelData}}).
check_path(Path) ->
- case file:read_file_info(Path) of
- {ok, Info} when Info#file_info.type==directory ->
- ok;
- {ok, _Info} ->
- throw({error, {not_a_directory, Path}});
- {error, _Reason} ->
- throw({error, {no_such_directory, Path}})
- end.
-
+ check_path_response(Path, file:read_file_info(Path)).
+check_path(Path, false) -> check_path(Path);
+check_path(Path, Masters) -> check_path_master(Masters, Path).
+
+%%-----------------------------------------------------------------
+%% check_path at any master node.
+%% If the path does not exist or is not a directory
+%% at one node it should not exist at any other node either.
+%%-----------------------------------------------------------------
+check_path_master([Master|Ms], Path) ->
+ case rpc:call(Master, file, read_file_info, [Path]) of
+ {badrpc, _} -> consult_master(Ms, Path);
+ Res -> check_path_response(Path, Res)
+ end;
+check_path_master([], _Path) ->
+ {error, no_master}.
+
+check_path_response(_Path, {ok, Info}) when Info#file_info.type==directory ->
+ ok;
+check_path_response(Path, {ok, _Info}) ->
+ throw({error, {not_a_directory, Path}});
+check_path_response(Path, {error, _Reason}) ->
+ throw({error, {no_such_directory, Path}}).
+
do_check_install_release(RelDir, Vsn, Releases, Masters) ->
case lists:keysearch(Vsn, #release.vsn, Releases) of
{value, #release{status = current}} ->
diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl
index 9c0edf4e99..8d050fb7b0 100644
--- a/lib/sasl/src/release_handler_1.erl
+++ b/lib/sasl/src/release_handler_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
%% External exports
-export([eval_script/3, eval_script/4, check_script/2]).
--export([get_vsn/1]). %% exported because used in a test case
+-export([get_current_vsn/1]). %% exported because used in a test case
-record(eval_state, {bins = [], stopped = [], suspended = [], apps = [],
libdirs, unpurged = [], vsns = [], newlibs = [],
@@ -223,7 +223,7 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) ->
FName = filename:join(Ebin, File),
case erl_prim_loader:get_file(FName) of
{ok, Bin, FName2} ->
- NVsns = add_new_vsn(Mod, FName2, Vsns),
+ NVsns = add_new_vsn(Mod, Bin, Vsns),
{[{Mod, Bin, FName2} | Bins],NVsns};
error ->
throw({error, {no_such_file,FName}})
@@ -609,17 +609,17 @@ sync_nodes(Id, Nodes) ->
add_old_vsn(Mod, Vsns) ->
case lists:keysearch(Mod, 1, Vsns) of
{value, {Mod, undefined, NewVsn}} ->
- OldVsn = get_vsn(code:which(Mod)),
+ OldVsn = get_current_vsn(Mod),
lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
{value, {Mod, _OldVsn, _NewVsn}} ->
Vsns;
false ->
- OldVsn = get_vsn(code:which(Mod)),
+ OldVsn = get_current_vsn(Mod),
[{Mod, OldVsn, undefined} | Vsns]
end.
-add_new_vsn(Mod, File, Vsns) ->
- NewVsn = get_vsn(File),
+add_new_vsn(Mod, Bin, Vsns) ->
+ NewVsn = get_vsn(Bin),
case lists:keysearch(Mod, 1, Vsns) of
{value, {Mod, OldVsn, undefined}} ->
lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn});
@@ -627,17 +627,35 @@ add_new_vsn(Mod, File, Vsns) ->
[{Mod, undefined, NewVsn} | Vsns]
end.
-
+%%-----------------------------------------------------------------
+%% Func: get_current_vsn/1
+%% Args: Mod = atom()
+%% Purpose: This function returns the equivalent of
+%% beam_lib:version(code:which(Mod)), but it will also handle the
+%% case when using erl_prim_loader loader different from 'efile'.
+%% The reason for not using the Binary from the 'bins' or the
+%% version directly from the 'vsns' state field is that these are
+%% updated already by load_object_code, and this function is called
+%% from load and remove.
+%% Returns: Vsn = term()
+%%-----------------------------------------------------------------
+get_current_vsn(Mod) ->
+ File = code:which(Mod),
+ case erl_prim_loader:get_file(File) of
+ {ok, Bin, _File2} ->
+ get_vsn(Bin);
+ error ->
+ throw({error, {no_such_file, File}})
+ end.
%%-----------------------------------------------------------------
%% Func: get_vsn/1
-%% Args: File = string()
+%% Args: Bin = binary()
%% Purpose: Finds the version attribute of a module.
-%% Returns: Vsn
-%% Vsn = term()
+%% Returns: Vsn = term()
%%-----------------------------------------------------------------
-get_vsn(File) ->
- {ok, {_Mod, Vsn}} = beam_lib:version(File),
+get_vsn(Bin) ->
+ {ok, {_Mod, Vsn}} = beam_lib:version(Bin),
case misc_supp:is_string(Vsn) of
true ->
Vsn;
diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl
index 20a142c763..7489ee58d2 100644
--- a/lib/sasl/src/systools_make.erl
+++ b/lib/sasl/src/systools_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,7 +50,7 @@
%% the applications are found.
%%
%% New options: {path,Path} can contain wildcards
-%% no_module_tests
+%% src_tests
%% {variables,[{Name,AbsString}]}
%% {machine, jam | beam | vee}
%% exref | {exref, [AppName]}
@@ -82,8 +82,7 @@ make_script(RelName, Output, Flags) when is_list(RelName),
Path0 = get_path(Flags),
Path1 = mk_path(Path0), % expand wildcards etc.
Path = make_set(Path1 ++ code:get_path()),
- ModTestP = {not member(no_module_tests, Flags),
- xref_p(Flags)},
+ ModTestP = {member(src_tests, Flags),xref_p(Flags)},
case get_release(RelName, Path, ModTestP, machine(Flags)) of
{ok, Release, Appls, Warnings} ->
case generate_script(Output,Release,Appls,Flags) of
@@ -155,7 +154,7 @@ return({error,Mod,Error},_,Flags) ->
%% should be included in the release package and there it can be found.
%%
%% New options: {path,Path} can contain wildcards
-%% no_module_tests
+%% src_tests
%% exref | {exref, [AppName]}
%% {variables,[{Name,AbsString}]}
%% {machine, jam | beam | vee}
@@ -190,8 +189,7 @@ make_tar(RelName, Flags) when is_list(RelName), is_list(Flags) ->
Path0 = get_path(Flags),
Path1 = mk_path(Path0),
Path = make_set(Path1 ++ code:get_path()),
- ModTestP = {not member(no_module_tests, Flags),
- xref_p(Flags)},
+ ModTestP = {member(src_tests, Flags),xref_p(Flags)},
case get_release(RelName, Path, ModTestP, machine(Flags)) of
{ok, Release, Appls, Warnings} ->
case catch mk_tar(RelName, Release, Appls, Flags, Path1) of
@@ -218,7 +216,7 @@ make_tar(RelName, Flags) ->
%% {ok, #release, [{{Name,Vsn},#application}], Warnings} | {error, What}
get_release(File, Path) ->
- get_release(File, Path, true, false).
+ get_release(File, Path, {false,false}, false).
get_release(File, Path, ModTestP) ->
get_release(File, Path, ModTestP, false).
@@ -771,36 +769,40 @@ get_mod_vsn([]) ->
%% Use the module extension of the running machine as extension for
%% the checked modules.
-check_mods(Modules, Appls, Path, {true, XrefP}, Machine) ->
- Ext = objfile_extension(Machine),
- IncPath = create_include_path(Appls, Path),
- Res = append(map(fun(ModT) ->
- {Mod,_Vsn,App,_,Dir} = ModT,
- case check_mod(Mod,App,Dir,Ext,IncPath) of
- ok ->
- [];
- {error, Error} ->
- [{error,{Error, ModT}}];
- {warning, Warn} ->
- [{warning,{Warn,ModT}}]
- end
- end,
- Modules)),
- Res2 = Res ++ check_xref(Appls, Path, XrefP),
+check_mods(Modules, Appls, Path, {SrcTestP, XrefP}, Machine) ->
+ SrcTestRes = check_src(Modules, Appls, Path, SrcTestP, Machine),
+ XrefRes = check_xref(Appls, Path, XrefP),
+ Res = SrcTestRes ++ XrefRes,
case filter(fun({error, _}) -> true;
(_) -> false
end,
- Res2) of
+ Res) of
[] ->
{ok, filter(fun({warning, _}) -> true;
(_) -> false
end,
- Res2)};
+ Res)};
Errors ->
{error, Errors}
- end;
-check_mods(_, _, _, _, _) ->
- {ok, []}.
+ end.
+
+check_src(Modules, Appls, Path, true, Machine) ->
+ Ext = objfile_extension(Machine),
+ IncPath = create_include_path(Appls, Path),
+ append(map(fun(ModT) ->
+ {Mod,_Vsn,App,_,Dir} = ModT,
+ case check_mod(Mod,App,Dir,Ext,IncPath) of
+ ok ->
+ [];
+ {error, Error} ->
+ [{error,{Error, ModT}}];
+ {warning, Warn} ->
+ [{warning,{Warn,ModT}}]
+ end
+ end,
+ Modules));
+check_src(_, _, _, _, _) ->
+ [].
check_xref(_Appls, _Path, false) ->
[];
@@ -1853,11 +1855,11 @@ cas([silent | Args], {Path, _Sil, Loc, Test, Var, Mach,
cas([local | Args], {Path, Sil, _Loc, Test, Var, Mach,
Xref, XrefApps, X}) ->
cas(Args, {Path, Sil, local, Test, Var, Mach, Xref, XrefApps, X});
-%%% no_module_tests ----------------------------------------------------
-cas([no_module_tests | Args], {Path, Sil, Loc, _Test, Var, Mach,
- Xref, XrefApps, X}) ->
+%%% src_tests -------------------------------------------------------
+cas([src_tests | Args], {Path, Sil, Loc, _Test, Var, Mach,
+ Xref, XrefApps, X}) ->
cas(Args,
- {Path, Sil, Loc, no_module_tests, Var, Mach, Xref, XrefApps,X});
+ {Path, Sil, Loc, src_tests, Var, Mach, Xref, XrefApps,X});
%%% variables ----------------------------------------------------------
cas([{variables, V} | Args], {Path, Sil, Loc, Test, Var, Mach,
Xref, XrefApps, X}) when is_list(V) ->
@@ -1896,6 +1898,10 @@ cas([{outdir, Dir} | Args], {Path, Sil, Loc, Test, Var, Mach,
cas([otp_build | Args], {Path, Sil, Loc, Test, Var, Mach,
Xref, XrefApps, X}) ->
cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X});
+%%% no_module_tests (kept for backwards compatibility, but ignored) ----
+cas([no_module_tests | Args], {Path, Sil, Loc, Test, Var, Mach,
+ Xref, XrefApps, X}) ->
+ cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,X});
%%% ERROR --------------------------------------------------------------
cas([Y | Args], {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps, X}) ->
cas(Args, {Path, Sil, Loc, Test, Var, Mach, Xref, XrefApps,X++[Y]}).
@@ -1935,10 +1941,10 @@ cat([{dirs, D} | Args], {Path, Sil, Dirs, Erts, Test,
cat([{erts, E} | Args], {Path, Sil, Dirs, _Erts, Test,
Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(E)->
cat(Args, {Path, Sil, Dirs, E, Test, Var, VarTar, Mach, Xref, XrefApps, X});
-%%% no_module_tests ----------------------------------------------------
-cat([no_module_tests | Args], {Path, Sil, Dirs, Erts, _Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
- cat(Args, {Path, Sil, Dirs, Erts, no_module_tests, Var, VarTar, Mach,
- Xref, XrefApps, X});
+%%% src_tests ----------------------------------------------------
+cat([src_tests | Args], {Path, Sil, Dirs, Erts, _Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, src_tests, Var, VarTar, Mach,
+ Xref, XrefApps, X});
%%% variables ----------------------------------------------------------
cat([{variables, V} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) when is_list(V) ->
case check_vars(V) of
@@ -1982,6 +1988,9 @@ cat([{outdir, Dir} | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xre
%%% otp_build (secret, not documented) ---------------------------------
cat([otp_build | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
+%%% no_module_tests (kept for backwards compatibility, but ignored) ----
+cat([no_module_tests | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
+ cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X});
%%% ERROR --------------------------------------------------------------
cat([Y | Args], {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X}) ->
cat(Args, {Path, Sil, Dirs, Erts, Test, Var, VarTar, Mach, Xref, XrefApps, X++[Y]}).
diff --git a/lib/sasl/src/systools_rc.erl b/lib/sasl/src/systools_rc.erl
index 23d1a52b66..daadb79967 100644
--- a/lib/sasl/src/systools_rc.erl
+++ b/lib/sasl/src/systools_rc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,7 +34,7 @@
%% {add_module, Mod, [Mod]}
%% {remove_module, Mod, PrePurge, PostPurge, [Mod]}
%% {restart_application, Appl}
-%% {add_application, Appl}
+%% {add_application, Appl, Type}
%% {remove_application, Appl}
%%
%% Low-level
@@ -109,6 +109,8 @@ expand_script([I|Script]) ->
{delete_module, Mod} ->
[{remove, {Mod, brutal_purge, brutal_purge}},
{purge, [Mod]}];
+ {add_application, Application} ->
+ {add_application, Application, permanent};
_ ->
I
end,
@@ -317,14 +319,18 @@ translate_independent_instrs(Before, After, Appls, PreAppls) ->
translate_application_instrs(Script, Appls, PreAppls) ->
%% io:format("Appls ~n~p~n",[Appls]),
L = lists:map(
- fun({add_application, Appl}) ->
+ fun({add_application, Appl, Type}) ->
case lists:keysearch(Appl, #application.name, Appls) of
{value, Application} ->
Mods =
remove_vsn(Application#application.modules),
+ ApplyL = case Type of
+ none -> [];
+ load -> [{apply, {application, load, [Appl]}}];
+ _ -> [{apply, {application, start, [Appl, Type]}}]
+ end,
[{add_module, M, []} || M <- Mods] ++
- [{apply, {application, start,
- [Appl, permanent]}}];
+ ApplyL;
false ->
throw({error, {no_such_application, Appl}})
end;
@@ -750,8 +756,9 @@ check_op({remove_module, Mod, PrePurge, PostPurge, Mods}) ->
lists:foreach(fun(M) -> check_mod(M) end, Mods);
check_op({remove_application, Appl}) ->
check_appl(Appl);
-check_op({add_application, Appl}) ->
- check_appl(Appl);
+check_op({add_application, Appl, Type}) ->
+ check_appl(Appl),
+ check_start_type(Type);
check_op({restart_application, Appl}) ->
check_appl(Appl);
check_op(restart) -> ok;
@@ -839,6 +846,13 @@ check_node(Node) -> throw({error, {bad_node, Node}}).
check_appl(Appl) when is_atom(Appl) -> ok;
check_appl(Appl) -> throw({error, {bad_application, Appl}}).
+check_start_type(none) -> ok;
+check_start_type(load) -> ok;
+check_start_type(temporary) -> ok;
+check_start_type(transient) -> ok;
+check_start_type(permanent) -> ok;
+check_start_type(T) -> throw({error, {bad_start_type, T}}).
+
check_func(Func) when is_atom(Func) -> ok;
check_func(Func) -> throw({error, {bad_func, Func}}).
diff --git a/lib/sasl/src/systools_relup.erl b/lib/sasl/src/systools_relup.erl
index 177d50be80..ec5486226c 100644
--- a/lib/sasl/src/systools_relup.erl
+++ b/lib/sasl/src/systools_relup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -179,8 +179,7 @@ check_opts([]) ->
[].
do_mk_relup(TopRelFile, BaseUpRelDcs, BaseDnRelDcs, Path, Opts) ->
- ModTest = false,
- case systools_make:get_release(to_list(TopRelFile), Path, ModTest) of
+ case systools_make:get_release(to_list(TopRelFile), Path) of
%%
%% TopRel = #release
%% NameVsnApps = [{{Name, Vsn}, #application}]
@@ -246,9 +245,8 @@ foreach_baserel_up(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
{RUs4, Ws4} =
check_for_emulator_restart(TopRel, BaseRel, RUs3, Ws3, Opts),
- ModTest = false,
BaseApps =
- case systools_make:get_release(BaseRelFile, Path, ModTest) of
+ case systools_make:get_release(BaseRelFile, Path) of
{ok, _, NameVsnApps, _Warns} ->
lists:map(fun({_,App}) -> App end, NameVsnApps);
Other1 ->
@@ -283,9 +281,8 @@ foreach_baserel_dn(TopRel, TopApps, [BaseRelDc|BaseRelDcs], Path, Opts,
%%
{RUs1, Ws1} = collect_appup_scripts(dn, TopApps, BaseRel, Ws, []),
- ModTest = false,
{BaseApps, Ws2} =
- case systools_make:get_release(BaseRelFile, Path, ModTest) of
+ case systools_make:get_release(BaseRelFile, Path) of
%%
%% NameVsnApps = [{{Name, Vsn}, #application}]
{ok, _, NameVsnApps, Warns} ->
@@ -370,10 +367,10 @@ collect_appup_scripts(_, [], _, Ws, RUs) -> {RUs, Ws}.
%% ToApps = [#application]
%%
create_add_app_scripts(FromRel, ToRel, RU0s, W0s) ->
- AddedNs = [N || {N, _V, _T} <- ToRel#release.applications,
+ AddedNs = [{N, T} || {N, _V, T} <- ToRel#release.applications,
not lists:keymember(N, 1, FromRel#release.applications)],
%% io:format("Added apps: ~p~n", [AddedNs]),
- RUs = [[{add_application, N}] || N <- AddedNs],
+ RUs = [[{add_application, N, T}] || {N, T} <- AddedNs],
{RUs ++ RU0s, W0s}.
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index d01a9bc4f1..8112d145dd 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 2.1.9.2
+SASL_VSN = 2.1.9.3
diff --git a/lib/snmp/doc/man1/.gitignore b/lib/snmp/doc/man1/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/snmp/doc/man1/.gitignore
diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile
index e8d9efb148..35ed63e103 100644
--- a/lib/snmp/doc/src/Makefile
+++ b/lib/snmp/doc/src/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -67,12 +67,15 @@ XML_OUTPUT = $(XML_FILES:%.xml=%.latex.xmls_output) \
INFO_FILE = ../../info
+#HTML_REF1_FILES = $(XML_REF1_FILES:%.xml=$(HTMLDIR)/%.html)
HTML_REF3_FILES = $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html)
HTML_REF6_FILES = $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html)
HTML_CHAP_FILES = $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
-EXTRA_FILES = summary.html.src \
+EXTRA_FILES = \
+ summary.html.src \
$(DEFAULT_HTML_FILES) \
+ $(HTML_REF1_FILES) \
$(HTML_REF3_FILES) \
$(HTML_REF6_FILES) \
$(HTML_CHAP_FILES)
@@ -80,6 +83,7 @@ EXTRA_FILES = summary.html.src \
MAN7DIR = $(DOCDIR)/man7
+MAN1_FILES = $(MAN1DIR)/snmpc.1
MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6)
MAN7_FILES = $(MIB_FILES:$(MIBSDIR)/%.mib=$(MAN7DIR)/%.7)
@@ -95,6 +99,7 @@ else
TEX_FILES_BOOK = \
$(BOOK_FILES:%.xml=%.tex)
TEX_FILES_REF_MAN = \
+ $(XML_REF1_FILES:%.xml=%.tex) \
$(XML_REF3_FILES:%.xml=%.tex) \
$(XML_REF6_FILES:%.xml=%.tex) \
$(XML_APPLICATION_FILES:%.xml=%.tex)
@@ -169,7 +174,7 @@ ps: $(TOP_PS_FILE)
html: $(HTML_FILES) $(TOP_HTML_FILES) gifs
-html2: gifs $(TOP_HTML_FILES) $(HTML_FILES) $(HTML_REF3_FILES) $(HTML_REF6_FILES) $(HTML_CHAP_FILES)
+html2: gifs $(TOP_HTML_FILES) $(HTML_FILES) $(HTML_REF1_FILES) $(HTML_REF3_FILES) $(HTML_REF6_FILES) $(HTML_CHAP_FILES)
clean: clean_tex clean_html clean_man clean_docs
@@ -195,7 +200,9 @@ endif
$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk # Create top make file
sed -e 's;%VSN%;$(VSN);' $< > $@ # inserting version number
-man: man3 man6 man7
+man: man1 man3 man6 man7
+
+man1: $(MAN1_FILES)
man3: $(MAN3_FILES)
@@ -213,6 +220,7 @@ clean_pdf:
clean_man:
@echo "cleaning man:"
+ rm -f $(MAN1DIR)/*
rm -f $(MAN3DIR)/*
rm -f $(MAN6DIR)/*
rm -f $(MAN7DIR)/*
@@ -233,6 +241,11 @@ $(MAN7DIR)/%.7: $(MIBSDIR)/%.mib
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
+
+$(MAN1DIR)/snmpc.1: snmpc_cmd.xml
+ date=`date +"%B %e %Y"`; \
+ xsltproc --output "$@" --stringparam company "Ericsson AB" --stringparam docgen "$(DOCGEN)" --stringparam gendate "$$date" --stringparam appname "$(APPLICATION)" --stringparam appver "$(VSN)" --xinclude -path $(DOCGEN)/priv/docbuilder_dtd -path $(DOCGEN)/priv/dtd_man_entities $(DOCGEN)/priv/xsl/db_man.xsl $<
+
include $(ERL_TOP)/make/otp_release_targets.mk
ifdef DOCSUPPORT
@@ -244,6 +257,8 @@ release_docs_spec: docs
$(INSTALL_DATA) $(HTMLDIR)/* \
$(RELSYSDIR)/doc/html
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man1
+ $(INSTALL_DATA) $(MAN1DIR)/* $(RELEASE_PATH)/man/man1
$(INSTALL_DIR) $(RELEASE_PATH)/man/man3
$(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3
$(INSTALL_DIR) $(RELEASE_PATH)/man/man6
@@ -269,7 +284,9 @@ release_docs_spec: docs
$(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
$(RELSYSDIR)/doc/html
$(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
- $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man1
+ $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man
$(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
$(INSTALL_DIR) $(RELEASE_PATH)/man/man6
$(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6
@@ -286,6 +303,10 @@ release_spec:
ifdef DOCSUPPORT
info: info_xml info_man info_html
+ @echo "MAN1DIR: $(MAN1DIR)"
+ @echo "MAN3DIR: $(MAN3DIR)"
+ @echo "MAN6DIR: $(MAN6DIR)"
+ @echo "MAN7DIR: $(MAN7DIR)"
else
info: info_xml info_man info_html info_tex
@echo "DVI2PS = $(DVI2PS)"
@@ -297,6 +318,7 @@ endif
info_man:
@echo "man files:"
+ @echo "MAN1_FILES = $(MAN1_FILES)"
@echo "MAN3_FILES = $(MAN3_FILES)"
@echo "MAN6_FILES = $(MAN6_FILES)"
@echo "MAN7_FILES = $(MAN7_FILES)"
@@ -305,6 +327,7 @@ info_man:
info_xml:
@echo "xml files:"
+# @echo "XML_REF1_FILES = $(XML_REF1_FILES)"
@echo "XML_REF3_FILES = $(XML_REF3_FILES)"
@echo "XML_REF6_FILES = $(XML_REF6_FILES)"
@echo "XML_PART_FILES = $(XML_PART_FILES)"
@@ -333,6 +356,7 @@ info_html:
@echo ""
@echo "DEFAULT_HTML_FILES = $(DEFAULT_HTML_FILES)"
@echo ""
+# @echo "HTML_REF1_FILES = $(HTML_REF1_FILES)"
@echo "HTML_REF3_FILES = $(HTML_REF3_FILES)"
@echo "HTML_REF6_FILES = $(HTML_REF6_FILES)"
@echo "HTML_CHAP_FILES = $(HTML_CHAP_FILES)"
diff --git a/lib/snmp/doc/src/depend.mk b/lib/snmp/doc/src/depend.mk
index bf9833274d..20a523dd8c 100644
--- a/lib/snmp/doc/src/depend.mk
+++ b/lib/snmp/doc/src/depend.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -48,6 +48,7 @@ $(HTMLDIR)/ref_man.html: \
snmp_app.xml \
snmp.xml \
snmpc.xml \
+ snmpc_cmd.xml \
snmpa.xml \
snmpa_conf.xml \
snmpa_discovery_handler.xml \
diff --git a/lib/snmp/doc/src/files.mk b/lib/snmp/doc/src/files.mk
index 293fb52ce0..bd94cd6bac 100644
--- a/lib/snmp/doc/src/files.mk
+++ b/lib/snmp/doc/src/files.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2001-2009. All Rights Reserved.
+# Copyright Ericsson AB 2001-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,9 @@ XML_APPLICATION_FILES = \
XML_APP_REF3_FILES = \
snmp.xml
+XML_COMP_REF1_FILES = \
+ snmpc_cmd.xml
+
XML_COMP_REF3_FILES = \
snmpc.xml
@@ -62,6 +65,9 @@ XML_MANAGER_REF3_FILES = \
snmpm_network_interface_filter.xml \
snmpm_user.xml
+XML_REF1_FILES = \
+ $(XML_COMP_REF1_FILES)
+
XML_REF3_FILES = \
$(XML_APP_REF3_FILES) \
$(XML_COMP_REF3_FILES) \
@@ -98,12 +104,13 @@ XML_CHAPTER_FILES = \
BOOK_FILES = book.xml
-XML_FILES = $(BOOK_FILES) \
- $(XML_CHAPTER_FILES) \
- $(XML_PART_FILES) \
- $(XML_REF6_FILES) \
- $(XML_REF3_FILES) \
- $(XML_APPLICATION_FILES)
+XML_FILES = $(BOOK_FILES) \
+ $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) \
+ $(XML_REF1_FILES) \
+ $(XML_REF3_FILES) \
+ $(XML_REF6_FILES) \
+ $(XML_APPLICATION_FILES)
GIF_FILES = book.gif \
getnext1.gif \
diff --git a/lib/snmp/doc/src/make.dep b/lib/snmp/doc/src/make.dep
index ccd01b9d3a..223e197f25 100644
--- a/lib/snmp/doc/src/make.dep
+++ b/lib/snmp/doc/src/make.dep
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -52,7 +52,7 @@ book.dvi: book.tex part.tex ref_man.tex snmp.tex snmp_advanced_agent.tex \
snmpa_notification_delivery_info_receiver.tex \
snmpa_notification_filter.tex \
snmpa_supervisor.tex \
- snmpc.tex snmpm.tex snmpm_conf.tex snmpm_mpd.tex \
+ snmpc.tex snmpc_cmd.tex snmpm.tex snmpm_conf.tex snmpm_mpd.tex \
snmpm_network_interface.tex snmpm_network_interface_filter.tex \
snmpm_user.tex
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index eb7c9db6ba..2efeb8ae3f 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>1996</year><year>2010</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>SNMP Release Notes</title>
@@ -33,6 +33,218 @@
</header>
<section>
+ <title>SNMP Development Toolkit 4.19</title>
+ <p>Version 4.19 supports code replacement in runtime from/to
+ version 4.18.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+<!--
+ <p>-</p>
+-->
+ <list type="bulleted">
+ <item>
+ <p>[compiler] Added support for textual convention
+ <c>AGENT-CAPABILITIES</c> and "full" support for textual
+ convention MODULE-COMPLIANCE, both defined by the SNMPv2-CONF
+ mib.</p>
+ <p>The <c>reference</c> and <c>modules</c> part(s) are
+ stored in the <c>assocList</c> of the mib-entry (<c>me</c>)
+ record.
+ Only handled <em>if</em> the option(s) <c>agent_capabilities</c>
+ and <c>module_compliance</c> (respectively) are provided to the
+ compiler. </p>
+ <p>See <seealso marker="snmpc#compile">compile/2</seealso>
+ for more info. </p>
+ <p>For backward compatibillity, the MIBs provided with
+ this application are <em>not</em> compiled with these
+ options. </p>
+ <p>Own Id: OTP-8966</p>
+ </item>
+
+ <item>
+ <p>[agent] Added a "complete" set of (snmp) table and variable
+ print functions, for each mib handled by the SNMP (agent)
+ application. This will be usefull when debugging a running agent.</p>
+ <p>See
+ <seealso marker="snmpa#print_mib_info">print_mib_info/0</seealso>,
+ <seealso marker="snmpa#print_mib_tables">print_mib_tables/0</seealso>
+ and
+ <seealso marker="snmpa#print_mib_variables">print_mib_variables/0</seealso>
+ for more info. </p>
+ <p>Own Id: OTP-8977</p>
+ </item>
+
+ <item>
+ <p>[compiler] Added a MIB compiler (frontend) escript,
+ <c>snmpc</c>. </p>
+ <p>Own Id: OTP-9004</p>
+ </item>
+
+ </list>
+ </section>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+<!--
+ <p>-</p>
+-->
+ <list type="bulleted">
+ <item>
+ <p>[agent] For the table vacmAccessTable,
+ when performing the is_set_ok and set operation(s),
+ all values of the vacmAccessSecurityModel column was
+ incorrectly translated to <c>any</c>. </p>
+<!--
+that is when calling:
+snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols).
+-->
+ <p>Own Id: OTP-8980</p>
+ </item>
+
+ <item>
+ <p>[agent] When calling
+ <seealso marker="snmp_view_based_acm_mib#reconfigure">snmp_view_based_acm_mib:reconfigure/1</seealso>
+ on a running node, the table <c>vacmAccessTable</c> was not properly
+ cleaned.
+ This meant that if some entries in the vacm.conf file was removed
+ (compared to the <c>current</c> config),
+ while others where modified and/or added, the removed entrie(s)
+ would still exist in the <c>vacmAccessTable</c> table. </p>
+ <p>Own Id: OTP-8981</p>
+ <p>Aux Id: Seq 11750</p>
+ </item>
+
+ </list>
+ </section>
+
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+ </section>
+
+ </section> <!-- 4.19 -->
+
+ <section>
+ <title>SNMP Development Toolkit 4.18</title>
+ <p>Version 4.18 supports code replacement in runtime from/to
+ version 4.17.1 and 4.17.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <list type="bulleted">
+ <item>
+ <p>Prepared for R14B release.</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <p>-</p>
+<!--
+ <list type="bulleted">
+ <item>
+ <p>[agent] When the function FilterMod:accept_recv/2 returned false
+ the SNMP agent stopped collecting messages from UDP.</p>
+ <p>Own Id: OTP-8761</p>
+ </item>
+ </list>
+-->
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+ </section>
+ </section> <!-- 4.18 -->
+
+
+ <section>
+ <title>SNMP Development Toolkit 4.17.1</title>
+ <p>Version 4.17.1 supports code replacement in runtime from/to
+ version 4.17, 4.16.2, 4.16.1, 4.16, 4.15, 4.14 and 4.13.5.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <p>-</p>
+ </section>
+
+ <section>
+ <title>Reported Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>When the function FilterMod:accept_recv/2
+ returned false the SNMP agent stopped collecting
+ messages from UDP.</p>
+ <p>Own Id: OTP-8761</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+ </section>
+ </section> <!-- 4.17.1 -->
+
+
+ <section>
+ <title>SNMP Development Toolkit 4.17</title>
+ <p>Version 4.17 supports code replacement in runtime from/to
+ version 4.16.2, 4.16.1, 4.16, 4.15, 4.14 and 4.13.5.</p>
+
+ <section>
+ <title>Improvements and new features</title>
+ <!--
+ <p>-</p>
+ -->
+ <list type="bulleted">
+ <item>
+ <p>[agent] Added very basic support for multiple SNMPv3
+ EngineIDs in a single agent. See
+ <seealso marker="snmpa#send_notification">send_notification/7</seealso>,
+ <seealso marker="snmpa_mpd#process_packet">process_packet/7</seealso>,
+ <seealso marker="snmpa_mpd#generate_response_msg">generate_response_msg/6</seealso> or
+ <seealso marker="snmpa_mpd#generate_msg">generate_msg/6</seealso>
+ for more info. </p>
+
+ <p>Own Id: OTP-8478</p>
+ </item>
+
+ </list>
+
+ </section>
+
+ <section>
+ <title>Reported Fixed Bugs and Malfunctions</title>
+ <p>-</p>
+
+ <!--
+ <list type="bulleted">
+ <item>
+ <p>The config utility
+ (<seealso marker="snmp#config">snmp:config/0</seealso>)
+ generated a default notify.conf
+ with a bad name for the starndard trap entry (was "stadard trap",
+ but should have been "standard trap"). This has been corrected. </p>
+ <p>Kenji Rikitake</p>
+ <p>Own Id: OTP-8433</p>
+ </item>
+
+ </list>
+ -->
+
+ </section>
+
+ <section>
+ <title>Incompatibilities</title>
+ <p>-</p>
+ </section>
+ </section> <!-- 4.17 -->
+
+
+ <section>
<title>SNMP Development Toolkit 4.16.2</title>
<p>Version 4.16.2 supports code replacement in runtime from/to
version 4.16.1, 4.16, 4.15, 4.14 and 4.13.5.</p>
@@ -60,6 +272,12 @@
<p>Own Id: OTP-8594</p>
</item>
+ <item>
+ <p>Auto [agent] Changed default value for the MIB server cache.
+ GC is now on by default. </p>
+ <p>Own Id: OTP-8648</p>
+ </item>
+
</list>
</section>
@@ -83,6 +301,15 @@
<p>Own Id: OTP-8595</p>
</item>
+ <item>
+ <p>[manager] Raise condition causing the manager server process to
+ crash. Unregistering an agent while traffic (set/get-operations)
+ is ongoing could cause a crash in the manager server process
+ (raise condition). </p>
+ <p>Own Id: OTP-8646</p>
+ <p>Aux Id: Seq 11585</p>
+ </item>
+
</list>
</section>
diff --git a/lib/snmp/doc/src/ref_man.xml b/lib/snmp/doc/src/ref_man.xml
index 1ae5a8205b..92e8927f6d 100644
--- a/lib/snmp/doc/src/ref_man.xml
+++ b/lib/snmp/doc/src/ref_man.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE application SYSTEM "application.dtd">
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -61,6 +61,7 @@
<xi:include href="snmp_user_based_sm_mib.xml"/>
<xi:include href="snmp_view_based_acm_mib.xml"/>
<xi:include href="snmpc.xml"/>
+ <xi:include href="snmpc_cmd.xml"/>
<xi:include href="snmpm.xml"/>
<xi:include href="snmpm_conf.xml"/>
<xi:include href="snmpm_mpd.xml"/>
diff --git a/lib/snmp/doc/src/snmp_agent_config_files.xml b/lib/snmp/doc/src/snmp_agent_config_files.xml
index 0bab563f87..b62269d506 100644
--- a/lib/snmp/doc/src/snmp_agent_config_files.xml
+++ b/lib/snmp/doc/src/snmp_agent_config_files.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -178,11 +178,12 @@
<c>community.conf</c>. It must be present if the agent is
configured for SNMPv1 or SNMPv2c.
</p>
+ <p>An SNMP <em>community</em> is a relationship between an SNMP
+ agent and a set of SNMP managers that defines authentication, access
+ control and proxy characteristics. </p>
<p>The corresponding table is <c>snmpCommunityTable</c> in the
- SNMP-COMMUNITY-MIB.
- </p>
- <p>Each entry is a term:
- </p>
+ SNMP-COMMUNITY-MIB. </p>
+ <p>Each entry is a term: </p>
<p><c>{CommunityIndex, CommunityName, SecurityName, ContextName, TransportTag}.</c></p>
<list type="bulleted">
<item><c>CommunityIndex</c> is a non-empty string.
diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml
index 57eb87a759..694e619da1 100644
--- a/lib/snmp/doc/src/snmp_app.xml
+++ b/lib/snmp/doc/src/snmp_app.xml
@@ -346,7 +346,7 @@
<p>Defines if the mib server shall perform cache gc automatically or
leave it to the user (see
<seealso marker="snmpa#gc_mibs_cache">gc_mibs_cache/0,1,2,3</seealso>). </p>
- <p>Default is <c>false</c>.</p>
+ <p>Default is <c>true</c>.</p>
</item>
<tag><c><![CDATA[mibs_cache_age() = integer() > 0 <optional>]]></c></tag>
diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml
index 5bd36305fc..fc8562b638 100644
--- a/lib/snmp/doc/src/snmp_config.xml
+++ b/lib/snmp/doc/src/snmp_config.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>1997</year><year>2010</year>
+ <year>1997</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -343,7 +343,7 @@
<p>Defines if the mib server shall perform cache gc automatically or
leave it to the user (see
<seealso marker="snmpa#gc_mibs_cache">gc_mibs_cache/0,1,2,3</seealso>). </p>
- <p>Default is <c>false</c>.</p>
+ <p>Default is <c>true</c>.</p>
</item>
<tag><c><![CDATA[mibs_cache_age() = integer() > 0 <optional>]]></c></tag>
@@ -1004,36 +1004,16 @@ ok
</taglist>
<p>Another usefull way to debug the agent is to pretty-print the content of
- some of the (MIB-) tables handled directly by the agent. This can be done
- for the following tables: </p>
- <taglist>
- <tag><c><![CDATA[snmpCommunityTable]]></c></tag>
- <item>
- <p><c><![CDATA[snmp_community_mib:snmpCommunityTable(print).]]></c></p>
- </item>
-
- <tag><c><![CDATA[snmpNotifyTable]]></c></tag>
- <item>
- <p><c><![CDATA[snmp_notification_mib:snmpNotifyTable(print).]]></c></p>
- </item>
-
- <tag><c><![CDATA[snmpTargetAddrTable]]></c></tag>
- <item>
- <p><c><![CDATA[snmp_target_mib:snmpTargetAddrTable(print).]]></c></p>
- </item>
-
- <tag><c><![CDATA[snmpTargetParamsTable]]></c></tag>
- <item>
- <p><c><![CDATA[snmp_target_mib:snmpTargetParamsTable(print).]]></c></p>
- </item>
-
- <tag><c><![CDATA[usmUserTable]]></c></tag>
- <item>
- <p><c><![CDATA[snmp_user_based_sm_mib:usmUserTable(print).]]></c></p>
- </item>
-
- </taglist>
-
+ all the tables and/or variables handled directly by the agent.
+ This can be done by simply calling: </p>
+ <p><c><![CDATA[snmpa:print_mib_info()]]></c></p>
+ <p>See
+ <seealso marker="snmpa#print_mib_info">print_mib_info/0</seealso>,
+ <seealso marker="snmpa#print_mib_tables">print_mib_tables/0</seealso>
+ or
+ <seealso marker="snmpa#print_mib_variables">print_mib_variables/0</seealso>
+ for more info. </p>
+
</section>
</chapter>
diff --git a/lib/snmp/doc/src/snmp_view_based_acm_mib.xml b/lib/snmp/doc/src/snmp_view_based_acm_mib.xml
index ffea256608..d595f6b93b 100644
--- a/lib/snmp/doc/src/snmp_view_based_acm_mib.xml
+++ b/lib/snmp/doc/src/snmp_view_based_acm_mib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -38,7 +38,10 @@
SNMP-VIEW-BASED-ACM-MIB, and functions for configuring the database.
</p>
<p>The configuration files are described in the SNMP User's Manual.</p>
+
+ <marker id="configure"></marker>
</description>
+
<funcs>
<func>
<name>configure(ConfDir) -> void()</name>
@@ -48,27 +51,24 @@
</type>
<desc>
<p>This function is called from the supervisor at system
- start-up.
- </p>
+ start-up. </p>
<p>Inserts all data in the configuration files into the
- database and destroys all old rows with StorageType
- <c>volatile</c>. The rows created from the configuration file
- will have StorageType <c>nonVolatile</c>.
- </p>
- <p>All <c>snmp</c> counters are set to zero.
- </p>
+ database and destroys all old rows with StorageType
+ <c>volatile</c>. The rows created from the configuration file
+ will have StorageType <c>nonVolatile</c>. </p>
+ <p>All <c>snmp</c> counters are set to zero. </p>
<p>If an error is found in the configuration file, it is
- reported using the function <c>config_err/2</c> of the error
- report module, and the function fails with the reason
- <c>configuration_error</c>.
- </p>
+ reported using the function <c>config_err/2</c> of the error
+ report module, and the function fails with the reason
+ <c>configuration_error</c>. </p>
<p><c>ConfDir</c> is a string which points to the directory
- where the configuration files are found.
- </p>
- <p>The configuration file read is: <c>vacm.conf</c>.
- </p>
+ where the configuration files are found. </p>
+ <p>The configuration file read is: <c>vacm.conf</c>. </p>
+
+ <marker id="reconfigure"></marker>
</desc>
</func>
+
<func>
<name>reconfigure(ConfDir) -> void()</name>
<fsummary>Configure the SNMP-VIEW-BASED-ACM-MIB</fsummary>
@@ -88,18 +88,20 @@
<p>All <c>snmp</c> counters are set to zero.
</p>
<p>If an error is found in the configuration file, it is
- reported using the function <c>config_err/2</c> of the error
- report module, and the function fails with the reason
+ reported using the function
+ <seealso marker="snmpa_error#config_err">config_err/2</seealso>
+ of the error report module, and the function fails with the reason
<c>configuration_error</c>.
</p>
<p><c>ConfDir</c> is a string which points to the directory
where the configuration files are found.
</p>
- <p>The configuration file read is: <c>vacm.conf</c>.
- <marker id="add_sec2group"></marker>
-</p>
+ <p>The configuration file read is: <c>vacm.conf</c>. </p>
+
+ <marker id="add_sec2group"></marker>
</desc>
</func>
+
<func>
<name>add_sec2group(SecModel, SecName, GroupName) -> Ret</name>
<fsummary>Add one security to group definition</fsummary>
@@ -113,10 +115,13 @@
</type>
<desc>
<p>Adds a security to group definition to the agent config.
- Equivalent to one vacmSecurityToGroup-line in the <c>vacm.conf</c> file.</p>
+ Equivalent to one vacmSecurityToGroup-line in the
+ <c>vacm.conf</c> file.</p>
+
<marker id="delete_sec2group"></marker>
</desc>
</func>
+
<func>
<name>delete_sec2group(Key) -> Ret</name>
<fsummary>Delete one security to group definition</fsummary>
@@ -127,9 +132,11 @@
</type>
<desc>
<p>Delete a security to group definition from the agent config.</p>
+
<marker id="add_access"></marker>
</desc>
</func>
+
<func>
<name>add_access(GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV) -> Ret</name>
<fsummary>Add one access definition</fsummary>
@@ -148,10 +155,12 @@
</type>
<desc>
<p>Adds a access definition to the agent config.
- Equivalent to one vacmAccess-line in the <c>vacm.conf</c> file.</p>
- <marker id="delete_access"></marker>
+ Equivalent to one vacmAccess-line in the <c>vacm.conf</c> file.</p>
+
+ <marker id="delete_access"></marker>
</desc>
</func>
+
<func>
<name>delete_access(Key) -> Ret</name>
<fsummary>Delete one access definition</fsummary>
@@ -161,10 +170,12 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Delete a access definition from the agent config.</p>
- <marker id="add_view_tree_fam"></marker>
+ <p>Delete a access definition from the agent config.</p>
+
+ <marker id="add_view_tree_fam"></marker>
</desc>
</func>
+
<func>
<name>add_view_tree_fam(ViewIndex, SubTree, Status, Mask) -> Ret</name>
<fsummary>Add one view tree family definition</fsummary>
@@ -178,11 +189,14 @@
<v>Reason = term()</v>
</type>
<desc>
- <p>Adds a view tree family definition to the agent config.
- Equivalent to one vacmViewTreeFamily-line in the <c>vacm.conf</c> file.</p>
- <marker id="delete_view_tree_fam"></marker>
+ <p>Adds a view tree family definition to the agent config.
+ Equivalent to one vacmViewTreeFamily-line in the
+ <c>vacm.conf</c> file.</p>
+
+ <marker id="delete_view_tree_fam"></marker>
</desc>
</func>
+
<func>
<name>delete_view_tree_fam(Key) -> Ret</name>
<fsummary>Delete one view tree family definition</fsummary>
diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml
index b3661ae9b0..1d680e80f5 100644
--- a/lib/snmp/doc/src/snmpa.xml
+++ b/lib/snmp/doc/src/snmpa.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>snmpa</title>
@@ -648,6 +648,20 @@ notification_delivery_info() = #snmpa_notification_delivery_info{}
<desc>
<p>Disable the mib server cache. </p>
+ <marker id="which_mibs_cache_size"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>which_mibs_cache_size() -> void()</name>
+ <name>which_mibs_cache_size(Agent) -> void()</name>
+ <fsummary>The size of the mib server cache</fsummary>
+ <type>
+ <v>Agent = pid() | atom()</v>
+ </type>
+ <desc>
+ <p>Retreive the size of the mib server cache. </p>
+
<marker id="gc_mibs_cache"></marker>
</desc>
</func>
@@ -867,6 +881,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
<name>send_notification(Agent, Notification, Receiver, Varbinds)</name>
<name>send_notification(Agent, Notification, Receiver, NotifyName, Varbinds)</name>
<name>send_notification(Agent, Notification, Receiver, NotifyName, ContextName, Varbinds) -> void() </name>
+ <name>send_notification(Agent, Notification, Receiver, NotifyName, ContextName, Varbinds, LocalEngineID) -> void() </name>
<fsummary>Send a notification</fsummary>
<type>
<v>Agent = pid() | atom()</v>
@@ -888,6 +903,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
<v>OID = oid()</v>
<v>Value = term()</v>
<v>RowIndex = [int()]</v>
+ <v>LocalEngineID = string()</v>
</type>
<desc>
<p>Sends the notification <c>Notification</c> to the
@@ -1027,6 +1043,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
<item><c>{?sysLocation_instance, "upstairs"}</c> (provided
that the generated <c>.hrl</c> file is included)</item>
</list>
+
<p>If a variable in the notification is a table element, the
<c>RowIndex</c> for the element must be given in the
<c>Varbinds</c> list. In this case, the OBJECT IDENTIFIER sent
@@ -1034,15 +1051,27 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
element. This OBJECT IDENTIFIER could be used in a get
operation later.
</p>
+
<p>This function is asynchronous, and does not return any
information. If an error occurs, <c>user_err/2</c> of the error
report module is called and the notification is discarded.
</p>
+ <note>
+ <p>Note that the use of the LocalEngineID argument is only intended
+ for special cases, if the agent is to "emulate" multiple EngineIDs!
+ By default, the agent uses the value of <c>SnmpEngineID</c>
+ (see SNMP-FRAMEWORK-MIB). </p>
+ </note>
+
+<!--
<marker id="send_trap"></marker>
+-->
+ <marker id="discovery"></marker>
</desc>
</func>
+<!--
<func>
<name>send_trap(Agent,Trap,Community)</name>
<name>send_trap(Agent,Trap,Community,Varbinds) -> void()</name>
@@ -1114,6 +1143,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
<marker id="discovery"></marker>
</desc>
</func>
+-->
<func>
<name>discovery(TargetName, Notification) -> {ok, ManagerEngineID} | {error, Reason}</name>
@@ -1203,7 +1233,7 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
</type>
<desc>
<p>Restart the worker process of a multi-threaded agent.</p>
- <p>This is a utility function, that can be usefull when
+ <p>This is a utility function, that can be useful when
e.g. debugging instrumentation functions.</p>
<marker id="restart_set_worker"></marker>
@@ -1219,9 +1249,42 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).
</type>
<desc>
<p>Restart the set worker process of a multi-threaded agent.</p>
- <p>This is a utility function, that can be usefull when
+ <p>This is a utility function, that can be useful when
e.g. debugging instrumentation functions.</p>
+ <marker id="print_mib_info"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>print_mib_info() -> void()</name>
+ <fsummary>Print mib info</fsummary>
+ <desc>
+ <p>Prints the content of all the (snmp) tables and variables
+ for all mibs handled by the snmp agent. </p>
+
+ <marker id="print_mib_tables"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>print_mib_tables() -> void()</name>
+ <fsummary>Print mib tables</fsummary>
+ <desc>
+ <p>Prints the content of all the (snmp) tables
+ for all mibs handled by the snmp agent. </p>
+
+ <marker id="print_mib_variables"></marker>
+ </desc>
+ </func>
+
+ <func>
+ <name>print_mib_variables() -> void()</name>
+ <fsummary>Print mib variables</fsummary>
+ <desc>
+ <p>Prints the content of all the (snmp) variables
+ for all mibs handled by the snmp agent. </p>
+
<marker id="verbosity"></marker>
</desc>
</func>
diff --git a/lib/snmp/doc/src/snmpa_error.xml b/lib/snmp/doc/src/snmpa_error.xml
index a7312e8b24..4dbafdfbb7 100644
--- a/lib/snmp/doc/src/snmpa_error.xml
+++ b/lib/snmp/doc/src/snmpa_error.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2002</year><year>2009</year>
+ <year>2002</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -51,6 +51,8 @@
<c>error_report_mod</c>, see
<seealso marker="snmp_config#configuration_params">configuration parameters</seealso>.
</p>
+
+ <marker id="config_err"></marker>
</description>
<funcs>
<func>
@@ -67,8 +69,11 @@
</p>
<p><c>Format</c> and <c>Args</c> are as in
<c>io:format(Format, Args)</c>.</p>
+
+ <marker id="user_err"></marker>
</desc>
</func>
+
<func>
<name>user_err(Format, Args) -> void()</name>
<fsummary>Called if a user related error occurs</fsummary>
diff --git a/lib/snmp/doc/src/snmpa_mpd.xml b/lib/snmp/doc/src/snmpa_mpd.xml
index ea5bde8956..202e6b5661 100644
--- a/lib/snmp/doc/src/snmpa_mpd.xml
+++ b/lib/snmp/doc/src/snmpa_mpd.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>snmpa_mpd</title>
@@ -63,15 +63,19 @@
</func>
<func>
- <name>process_packet(Packet, TDomain, TAddress, State) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
+ <name>process_packet(Packet, TDomain, TAddress, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
+ <name>process_packet(Packet, TDomain, TAddress, LocalEngineID, State, NoteStore, Log) -> {ok, Vsn, Pdu, PduMS, ACMData} | {discarded, Reason} | {discovery, DiscoPacket}</name>
<fsummary>Process a packet received from the network</fsummary>
<type>
<v>Packet = binary()</v>
<v>TDomain = snmpUDPDomain</v>
<v>TAddress = {Ip, Udp}</v>
+ <v>LocalEngineID = string()</v>
<v>Ip = {integer(), integer(), integer(), integer()}</v>
<v>Udp = integer()</v>
<v>State = mpd_state()</v>
+ <v>NoteStore = pid()</v>
+ <v>Log = snmp_log()</v>
<v>Vsn = 'version-1' | 'version-2' | 'version-3'</v>
<v>Pdu = #pdu</v>
<v>PduMs = integer()</v>
@@ -84,18 +88,27 @@
decryption as necessary. The return values should be passed the
agent.</p>
+ <note>
+ <p>Note that the use of the LocalEngineID argument is only intended
+ for special cases, if the agent is to "emulate" multiple EngineIDs!
+ By default, the agent uses the value of <c>SnmpEngineID</c>
+ (see SNMP-FRAMEWORK-MIB). </p>
+ </note>
+
<marker id="generate_response_msg"></marker>
</desc>
</func>
<func>
- <name>generate_response_msg(Vsn, RePdu, Type, ACMData) -> {ok, Packet} | {discarded, Reason}</name>
+ <name>generate_response_msg(Vsn, RePdu, Type, ACMData, Log) -> {ok, Packet} | {discarded, Reason}</name>
+ <name>generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log) -> {ok, Packet} | {discarded, Reason}</name>
<fsummary>Generate a response packet to be sent to the network</fsummary>
<type>
<v>Vsn = 'version-1' | 'version-2' | 'version-3'</v>
<v>RePdu = #pdu</v>
<v>Type = atom()</v>
<v>ACMData = acm_data()</v>
+ <v>LocalEngineID = string()</v>
<v>Packet = binary()</v>
</type>
<desc>
@@ -103,17 +116,27 @@
network. <c>Type</c> is the <c>#pdu.type</c> of the original
request.</p>
+ <note>
+ <p>Note that the use of the LocalEngineID argument is only intended
+ for special cases, if the agent is to "emulate" multiple EngineIDs!
+ By default, the agent uses the value of <c>SnmpEngineID</c>
+ (see SNMP-FRAMEWORK-MIB). </p>
+ </note>
+
<marker id="generate_msg"></marker>
</desc>
</func>
<func>
- <name>generate_msg(Vsn, Pdu, MsgData, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name>
+ <name>generate_msg(Vsn, NoteStore, Pdu, MsgData, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name>
+ <name>generate_msg(Vsn, NoteStore, Pdu, MsgData, LocalEngineID, To) -> {ok, PacketsAndAddresses} | {discarded, Reason}</name>
<fsummary>Generate a request message to be sent to the network</fsummary>
<type>
<v>Vsn = 'version-1' | 'version-2' | 'version-3'</v>
+ <v>NoteStore = pid()</v>
<v>Pdu = #pdu</v>
<v>MsgData = msg_data()</v>
+ <v>LocalEngineID = string()</v>
<v>To = [dest_addrs()]</v>
<v>PacketsAndAddresses = [{TDomain, TAddress, Packet}]</v>
<v>TDomain = snmpUDPDomain</v>
@@ -136,6 +159,13 @@
also received from the requests mentioned above.
</p>
+ <note>
+ <p>Note that the use of the LocalEngineID argument is only intended
+ for special cases, if the agent is to "emulate" multiple EngineIDs!
+ By default, the agent uses the value of <c>SnmpEngineID</c>
+ (see SNMP-FRAMEWORK-MIB). </p>
+ </note>
+
<marker id="discarded_pdu"></marker>
</desc>
</func>
diff --git a/lib/snmp/doc/src/snmpc.xml b/lib/snmp/doc/src/snmpc.xml
index fbd0950c69..771629492d 100644
--- a/lib/snmp/doc/src/snmpc.xml
+++ b/lib/snmp/doc/src/snmpc.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>2004</year><year>2010</year>
+ <year>2004</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,6 +37,7 @@
<p>The module <c>snmpc</c> contains interface functions to the
SNMP toolkit MIB compiler.</p>
+ <marker id="compile"></marker>
</description>
<funcs>
@@ -47,7 +48,7 @@
<type>
<v>File = string()</v>
<v>Options = [opt()]</v>
- <v>opt() = db() | relaxed_row_name_assign_check() | deprecated() | description() | reference() | group_check() | i() | il() | imports() | module() | module_identity() | outdir() | no_defs() | verbosity() | warnings()</v>
+ <v>opt() = db() | relaxed_row_name_assign_check() | deprecated() | description() | reference() | group_check() | i() | il() | imports() | module() | module_identity() | module_compliance() | agent_capabilities() | outdir() | no_defs() | verbosity() | warnings()</v>
<v>db() = {db, volatile|persistent|mnesia}</v>
<v>deprecated() = {deprecated, bool()}</v>
<v>relaxed_row_name_assign_check() = relaxed_row_name_assign_check</v>
@@ -59,6 +60,8 @@
<v>imports() = imports</v>
<v>module() = {module, atom()}</v>
<v>module_identity() = module_identity</v>
+ <v>module_compliance() = module_compliance</v>
+ <v>agent_capabilities() = agent_capabilities</v>
<v>no_defs() = no_defs</v>
<v>outdir() = {outdir, dir()}</v>
<v>verbosity() = {verbosity, silence|warning|info|log|debug|trace}</v>
@@ -77,6 +80,7 @@
be used for the default instrumentation. </p>
<p>Default is <c>volatile</c>. </p>
</item>
+
<item>
<p>The option <c>deprecated</c> specifies if a deprecated
definition should be kept or not. If the option is
@@ -84,6 +88,7 @@
definitions. </p>
<p>Default is <c>true</c>. </p>
</item>
+
<item>
<p>The option <c>relaxed_row_name_assign_check</c>, if present,
specifies that the row name assign check shall not be done
@@ -94,12 +99,14 @@
<p>By default it is not included, but if this option is present
it will be. </p>
</item>
+
<item>
<p>The option <c>description</c> specifies if the text
of the DESCRIPTION field will be included or not. </p>
<p>By default it is not included, but if this option is
present it will be. </p>
</item>
+
<item>
<p>The option <c>reference</c> specifies if the text
of the REFERENCE field, when found in a table definition,
@@ -108,18 +115,21 @@
it will be. The reference text will be placed in the allocList
field of the mib-entry record (#me{}) for the table. </p>
</item>
+
<item>
<p>The option <c>group_check</c> specifies whether the
mib compiler should check the OBJECT-GROUP macro and
the NOTIFICATION-GROUP macro for correctness or not. </p>
<p>Default is <c>true</c>. </p>
</item>
+
<item>
<p>The option <c>i</c> specifies the path to search for
imported (compiled) MIB files. The directories should be
strings with a trailing directory delimiter. </p>
<p>Default is <c>["./"]</c>. </p>
</item>
+
<item>
<p>The option <c>il</c> (include_lib) also specifies a
list of directories to search for imported MIBs. It
@@ -132,11 +142,13 @@
<c><![CDATA[<snmp-home>/priv/mibs/]]></c>
are always listed last in the include path. </p>
</item>
+
<item>
<p>The option <c>imports</c>, if present, specifies that
the IMPORT statement of the MIB shall be included in the
compiled mib. </p>
</item>
+
<item>
<p>The option <c>module</c>, if present, specifies the
name of a module which implements all instrumentation
@@ -145,11 +157,29 @@
functions must be the same as the corresponding managed
object it implements. </p>
</item>
+
<item>
<p>The option <c>module_identity</c>, if present, specifies
that the info part of the MODULE-IDENTITY statement of the MIB
shall be included in the compiled mib. </p>
</item>
+
+ <item>
+ <p>The option <c>module_compliance</c>, if present, specifies
+ that the MODULE-COMPLIANCE statement of the MIB shall be included
+ (with a mib-entry record) in the compiled mib. The mib-entry record
+ of the module-compliance will contain <c>reference</c> and <c>module</c>
+ part(s) this info in the <c>assocList</c> field). </p>
+ </item>
+
+ <item>
+ <p>The option <c>agent_capabilities</c>, if present, specifies
+ that the AGENT-CAPABILITIES statement of the MIB shall be included
+ (with a mib-entry record) in the compiled mib. The mib-entry record
+ of the agent-capabilitie will contain <c>reference</c> and <c>modules</c>
+ part(s) this info in the <c>assocList</c> field). </p>
+ </item>
+
<item>
<p>The option <c>no_defs</c>, if present, specifies
that if a managed object does not have an instrumentation
@@ -157,6 +187,7 @@
be used, instead this is reported as an error, and the
compilation aborts. </p>
</item>
+
<item>
<p>The option <c>verbosity</c> specifies the verbosity of
the SNMP mib compiler. I.e. if warning, info, log, debug
@@ -166,11 +197,13 @@
option <c>verbosity</c> is <c>silence</c>, warning messages will
still be shown. </p>
</item>
+
<item>
<p>The option <c>warnings</c> specifies whether warning
messages should be shown. </p>
<p>Default is <c>true</c>. </p>
</item>
+
</list>
<p>The MIB compiler understands both SMIv1 and SMIv2 MIBs. It
uses the <c>MODULE-IDENTITY</c> statement to determine if the MIB is
@@ -185,8 +218,11 @@
have to be specified to <c>erlc</c> using the syntax
<c>+term</c>. See <c>erlc(1)</c> for details.
</p>
+
+ <marker id="is_consistent"></marker>
</desc>
</func>
+
<func>
<name>is_consistent(Mibs) -> ok | {error, Reason}</name>
<fsummary>Check for OID conflicts between MIBs</fsummary>
@@ -198,8 +234,11 @@
<p>Checks for multiple usage of object identifiers and traps
between MIBs.
</p>
+
+ <marker id="mib_to_hrl"></marker>
</desc>
</func>
+
<func>
<name>mib_to_hrl(MibName) -> ok | {error, Reason}</name>
<fsummary>Generate constants for the objects in the MIB</fsummary>
diff --git a/lib/snmp/doc/src/snmpc_cmd.xml b/lib/snmp/doc/src/snmpc_cmd.xml
new file mode 100644
index 0000000000..9358382a10
--- /dev/null
+++ b/lib/snmp/doc/src/snmpc_cmd.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE comref SYSTEM "comref.dtd">
+
+<comref>
+ <header>
+ <copyright>
+ <year>2011</year><year>2011</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>snmpc</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>snmpc_cmd.xml</file>
+ </header>
+ <com>snmpc(command)</com>
+ <comsummary>SNMP MIB compiler frontend</comsummary>
+ <description>
+ <p>The <c><![CDATA[snmpc]]></c> program provides a way to run
+ the SNMP MIB compiler of the Erlang system. </p>
+ </description>
+
+ <funcs>
+ <func>
+ <name>snmpc [options] file.mib | file.bin</name>
+ <fsummary>Compile MIBs</fsummary>
+ <desc>
+ <p><c><![CDATA[snmpc]]></c> compile a SNMP MIB file,
+ see <seealso marker="snmpc#compile">compile/1,2</seealso> for
+ more info. </p>
+ <p>It can also be used to generate a header file (.hrl)
+ with definitions of Erlang constants for the objects in
+ the MIB, see
+ <seealso marker="snmpc#mib_to_hrl">mib_to_hrl/1</seealso>. </p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>Compiler options</title>
+ <p>The following options are supported (note that most of these relate
+ to the compilation of the MIB file):</p>
+ <taglist>
+ <tag>--help</tag>
+ <item>
+ <p>Prints help info.</p>
+ </item>
+
+ <tag>--version</tag>
+ <item>
+ <p>Prints application and mib format version.</p>
+ </item>
+
+ <tag>--verbosity <em>verbosity</em></tag>
+ <item>
+ <p>Print debug info. </p>
+ <p><c>verbosity</c> = <c>trace</c> | <c>debug</c> | <c>log</c> | <c>info</c> | <c>silence</c></p>
+ <p>Defaults to <c>silence</c>.</p>
+ </item>
+
+ <tag>--warnings</tag>
+ <item>
+ <p>Print warning messages. </p>
+ </item>
+
+ <tag>--o <em>directory</em></tag>
+ <item>
+ <p>The directory where the compiler should place the output files.
+ If not specified, output files will be placed in the current working
+ directory.</p>
+ </item>
+
+ <tag>--i <em>Directory</em></tag>
+ <item>
+ <p>Specifies the path to search for imported (compiled) MIB files.
+ By default, the current working directory is always included. </p>
+ <p>This option can be present several times, each time specifying
+ <em>one</em> path. </p>
+ </item>
+
+ <tag>--il <em>Directory</em></tag>
+ <item>
+ <p>This option (include_lib), specifies a list of directories to
+ search for imported MIBs. It assumes that the first element in
+ the directory name corresponds to an OTP application. The compiler
+ will find the current installed version. For example, the value
+ ["snmp/mibs/"] will be replaced by ["snmp-3.1.1/mibs/"] (or what
+ the current version may be in the system). The current directory
+ and the "snmp-home"/priv/mibs/ are always listed last in the
+ include path. </p>
+ </item>
+
+ <tag>--sgc</tag>
+ <item>
+ <p>This option (skip group check), if present, disables the
+ group check of the mib compiler.
+ That is, should the OBJECT-GROUP and the NOTIFICATION-GROUP
+ macro(s) be checked for correctness or not. </p>
+ </item>
+
+ <tag>--dep</tag>
+ <item>
+ <p>Keep deprecated definition(s).
+ If not specified the compiler will ignore deprecated definitions. </p>
+ </item>
+
+ <tag>--desc</tag>
+ <item>
+ <p>The DESCRIPTION field will be included. </p>
+ </item>
+
+ <tag>--ref</tag>
+ <item>
+ <p>The REFERENCE field will be included. </p>
+ </item>
+
+ <tag>--imp</tag>
+ <item>
+ <p>The IMPORTS field will be included. </p>
+ </item>
+
+ <tag>--mi</tag>
+ <item>
+ <p>The MODULE-IDENTITY field will be included. </p>
+ </item>
+
+ <tag>--mc</tag>
+ <item>
+ <p>The MODULE-COMPLIANCE field will be included. </p>
+ </item>
+
+ <tag>--ac</tag>
+ <item>
+ <p>The AGENT-CAPABILITIES field will be included. </p>
+ </item>
+
+ <tag>--mod <em>module</em></tag>
+ <item>
+ <p>The module which implements all the instrumentation functions. </p>
+ <p>The name of all instrumentation functions must be the
+ same as the corresponding managed object it implements. </p>
+ </item>
+
+ <tag>--nd</tag>
+ <item>
+ <p>The default instrumentation functions will <em>not</em> be
+ used if a managed object have no instrumentation function.
+ Instead this will be reported as an error, and the compilation
+ aborts. </p>
+ </item>
+
+ <tag>--rrnac</tag>
+ <item>
+ <p>This option, if present, specifies that the row name assign check
+ shall not be done strictly according to the SMI (which allows only
+ the value 1). </p>
+ <p>With this option, all values greater than zero is allowed (>= 1).
+ This means that the error will be converted to a warning. </p>
+ <p>By default it is not included, but if this option is present
+ it will be. </p>
+ </item>
+
+ </taglist>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p><seealso marker="erlc">erlc(1)</seealso>,
+ <seealso marker="compiler:compile">compile(3)</seealso>,
+ <seealso marker="snmp:snmpc">snmpc(3)</seealso></p>
+ </section>
+</comref>
+
diff --git a/lib/snmp/examples/ex2/snmp_ex2_manager.erl b/lib/snmp/examples/ex2/snmp_ex2_manager.erl
index 79cfd94469..ff873327bc 100644
--- a/lib/snmp/examples/ex2/snmp_ex2_manager.erl
+++ b/lib/snmp/examples/ex2/snmp_ex2_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,6 +25,8 @@
-behaviour(gen_server).
-behaviour(snmpm_user).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([start_link/0, start_link/1, stop/0,
agent/2,
sync_get/2,
diff --git a/lib/snmp/examples/ex2/snmp_ex2_simple_standard_test.erl b/lib/snmp/examples/ex2/snmp_ex2_simple_standard_test.erl
index 16fe79d1a5..81939dd614 100644
--- a/lib/snmp/examples/ex2/snmp_ex2_simple_standard_test.erl
+++ b/lib/snmp/examples/ex2/snmp_ex2_simple_standard_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,6 +22,8 @@
-module(snmp_ex2_simple_standard_test).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([start/0, start/1, start/3]).
-include_lib("snmp/include/snmp_types.hrl").
diff --git a/lib/snmp/include/snmp_types.hrl b/lib/snmp/include/snmp_types.hrl
index 1fd6d153c9..4adb24361c 100644
--- a/lib/snmp/include/snmp_types.hrl
+++ b/lib/snmp/include/snmp_types.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -192,7 +192,7 @@
%%----------------------------------------------------------------------
-record(mib,
{misc = [],
- mib_format_version = "3.1",
+ mib_format_version = "3.2",
name = "",
module_identity, %% Not in SMIv1, and only with +module_identity
mes = [],
diff --git a/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in
index b85a8b0767..7aefb0ea34 100644
--- a/lib/snmp/mibs/Makefile.in
+++ b/lib/snmp/mibs/Makefile.in
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+# Copyright Ericsson AB 1996-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -108,20 +108,28 @@ TARGET_FILES = \
# FLAGS
# ----------------------------------------------------
-SNMP_FLAGS += -pa ../ebin +version
+SNMP_FLAGS += -pa ../ebin +version
ifneq ($(MIBS_VERBOSITY),)
-SNMP_FLAGS += +'{verbosity,$(MIBS_VERBOSITY)}'
+SNMP_FLAGS += +'{verbosity, $(MIBS_VERBOSITY)}'
endif
-ifneq ($(MIBS_REFERENCE),)
+ifeq ($(MIBS_REFERENCE),true)
SNMP_FLAGS += +reference
endif
-ifneq ($(MIBS_OPTIONS),)
+ifeq ($(MIBS_OPTIONS),true)
SNMP_FLAGS += +options
endif
+ifeq ($(MIBS_MC),true)
+SNMP_FLAGS += +module_compliance
+endif
+
+ifeq ($(MIBS_AC),true)
+SNMP_FLAGS += +agent_capabilities
+endif
+
# ----------------------------------------------------
# Targets
@@ -148,6 +156,14 @@ conf:
cd ..; $(MAKE) conf
info:
+ @echo "MIBS_REFERENCE = $(MIBS_REFERENCE)"
+ @echo ""
+ @echo "MIBS_OPTIONS = $(MIBS_OPTIONS)"
+ @echo ""
+ @echo "MIBS_MC = $(MIBS_MC)"
+ @echo ""
+ @echo "MIBS_AC = $(MIBS_AC)"
+ @echo ""
@echo "SNMP_FLAGS = $(SNMP_FLAGS)"
@echo ""
@echo "MIBS = $(MIBS)"
diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl
index a2ee7bf0c9..5644a43345 100644
--- a/lib/snmp/src/agent/snmp_community_mib.erl
+++ b/lib/snmp/src/agent/snmp_community_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,8 @@
%%
-module(snmp_community_mib).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1,
snmpCommunityTable/1, snmpCommunityTable/3,
snmpTargetAddrExtTable/3,
@@ -334,6 +336,8 @@ get_target_addr_ext_mms(TDomain, TAddress, Key) ->
get_target_addr_ext_mms(TDomain, TAddress, NextKey)
end
end.
+
+
%%-----------------------------------------------------------------
%% Instrumentation Functions
%%-----------------------------------------------------------------
@@ -345,7 +349,7 @@ snmpCommunityTable(print) ->
PrintRow =
fun(Prefix, Row) ->
lists:flatten(
- io_lib:format("~sIndex: ~p"
+ io_lib:format("~sIndex: ~p"
"~n~sName: ~p"
"~n~sSecurityName: ~p"
"~n~sContextEngineID: ~p"
diff --git a/lib/snmp/src/agent/snmp_framework_mib.erl b/lib/snmp/src/agent/snmp_framework_mib.erl
index 0916e2ec74..0d7866d94d 100644
--- a/lib/snmp/src/agent/snmp_framework_mib.erl
+++ b/lib/snmp/src/agent/snmp_framework_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,6 +37,8 @@
%%% over all known contexts.
%%%-----------------------------------------------------------------
%% External exports
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([init/0, configure/1]).
-export([intContextTable/1, intContextTable/3,
intAgentUDPPort/1, intAgentIpAddress/1,
@@ -371,15 +373,27 @@ intAgentUDPPort(Op) ->
intAgentIpAddress(Op) ->
snmp_generic:variable_func(Op, db(intAgentIpAddress)).
+snmpEngineID(print) ->
+ VarAndValue = [{snmpEngineID, snmpEngineID(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
snmpEngineID(Op) ->
snmp_generic:variable_func(Op, db(snmpEngineID)).
+snmpEngineMaxMessageSize(print) ->
+ VarAndValue = [{snmpEngineMaxMessageSize, snmpEngineMaxMessageSize(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
snmpEngineMaxMessageSize(Op) ->
snmp_generic:variable_func(Op, db(snmpEngineMaxMessageSize)).
+snmpEngineBoots(print) ->
+ VarAndValue = [{snmpEngineBoots, snmpEngineBoots(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
snmpEngineBoots(Op) ->
snmp_generic:variable_func(Op, db(snmpEngineBoots)).
+snmpEngineTime(print) ->
+ VarAndValue = [{snmpEngineTime, snmpEngineTime(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
snmpEngineTime(get) ->
{value, get_engine_time()}.
diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl
index 508aa090d9..06afa68d96 100644
--- a/lib/snmp/src/agent/snmp_generic.erl
+++ b/lib/snmp/src/agent/snmp_generic.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,8 @@
%%
-module(snmp_generic).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([variable_func/2, variable_func/3, variable_get/1, variable_set/2]).
-export([table_func/2, table_func/4,
table_set_row/5, table_set_cols/3, table_set_cols/4,
diff --git a/lib/snmp/src/agent/snmp_notification_mib.erl b/lib/snmp/src/agent/snmp_notification_mib.erl
index 16e43f05d7..1cd69b430f 100644
--- a/lib/snmp/src/agent/snmp_notification_mib.erl
+++ b/lib/snmp/src/agent/snmp_notification_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,8 @@
%%
-module(snmp_notification_mib).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1, invalidate_cache/0,
snmpNotifyTable/1, snmpNotifyTable/3,
snmpNotifyFilterTable/3, snmpNotifyFilterProfileTable/3,
diff --git a/lib/snmp/src/agent/snmp_standard_mib.erl b/lib/snmp/src/agent/snmp_standard_mib.erl
index 3928a8afe6..b6834d278c 100644
--- a/lib/snmp/src/agent/snmp_standard_mib.erl
+++ b/lib/snmp/src/agent/snmp_standard_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,11 +33,35 @@
-define(disabled, 2).
%% External exports
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1, reset/0, sys_up_time/0, sys_up_time/1,
snmp_enable_authen_traps/1, snmp_enable_authen_traps/2,
sys_object_id/1, sys_object_id/2, sys_or_table/3,
variable_func/1, variable_func/2,
inc/1, inc/2]).
+-export([sysDescr/1, sysContact/1, sysName/1, sysLocation/1,
+ sysServices/1, sysUpTime/1, snmpEnableAuthenTraps/1,
+ sysObjectID/1,
+ snmpInPkts/1, snmpOutPkts/1,
+ snmpInBadVersions/1,
+ snmpInBadCommunityNames/1, snmpInBadCommunityUses/1,
+ snmpInASNParseErrs/1,
+ snmpInTooBigs/1,
+ snmpInNoSuchNames/1, snmpInBadValues/1,
+ snmpInReadOnlys/1, snmpInGenErrs/1,
+ snmpInTotalReqVars/1, snmpInTotalSetVars/1,
+ snmpInGetRequests/1, snmpInSetRequests/1,
+ snmpInGetNexts/1,
+ snmpInGetResponses/1, snmpInTraps/1,
+ snmpOutTooBigs/1,
+ snmpOutNoSuchNames/1,
+ snmpOutBadValues/1,
+ snmpOutGenErrs/1,
+ snmpOutGetRequests/1, snmpOutSetRequests/1,
+ snmpOutGetNexts/1,
+ snmpOutGetResponses/1,
+ snmpOutTraps/1]).
-export([dummy/1, snmp_set_serial_no/1, snmp_set_serial_no/2]).
-export([add_agent_caps/2, del_agent_caps/1, get_agent_caps/0]).
-export([check_standard/1]).
@@ -200,18 +224,257 @@ variable_func(get, Name) ->
inc(Name) -> inc(Name, 1).
inc(Name, N) -> ets:update_counter(snmp_agent_table, Name, N).
+
+sysDescr(print) ->
+ VarAndValue = [{sysDescr, sysDescr(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
+sysDescr(get) ->
+ VarDB = db(sysDescr),
+ snmp_generic:variable_get(VarDB).
+
+
+sysContact(print) ->
+ VarAndValue = [{sysContact, sysContact(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
+sysContact(get) ->
+ VarDB = db(sysContact),
+ snmp_generic:variable_get(VarDB).
+
+
+sysName(print) ->
+ VarAndValue = [{sysName, sysName(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
+sysName(get) ->
+ VarDB = db(sysName),
+ snmp_generic:variable_get(VarDB).
+
+
+sysLocation(print) ->
+ VarAndValue = [{sysLocation, sysLocation(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
+sysLocation(get) ->
+ VarDB = db(sysLocation),
+ snmp_generic:variable_get(VarDB).
+
+
+sysServices(print) ->
+ VarAndValue = [{sysServices, sysServices(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
+sysServices(get) ->
+ VarDB = db(sysServices),
+ snmp_generic:variable_get(VarDB).
+
+
+snmpInPkts(print) ->
+ gen_counter(print, snmpInPkts);
+snmpInPkts(get) ->
+ gen_counter(get, snmpInPkts).
+
+
+snmpOutPkts(print) ->
+ gen_counter(print, snmpOutPkts);
+snmpOutPkts(get) ->
+ gen_counter(get, snmpOutPkts).
+
+
+snmpInASNParseErrs(print) ->
+ gen_counter(print, snmpInASNParseErrs);
+snmpInASNParseErrs(get) ->
+ gen_counter(get, snmpInASNParseErrs).
+
+
+snmpInBadCommunityNames(print) ->
+ gen_counter(print, snmpInBadCommunityNames);
+snmpInBadCommunityNames(get) ->
+ gen_counter(get, snmpInBadCommunityNames).
+
+
+snmpInBadCommunityUses(print) ->
+ gen_counter(print, snmpInBadCommunityUses);
+
+snmpInBadCommunityUses(get) ->
+ gen_counter(get, snmpInBadCommunityUses).
+
+
+snmpInBadVersions(print) ->
+ gen_counter(print, snmpInBadVersions);
+snmpInBadVersions(get) ->
+ gen_counter(get, snmpInBadVersions).
+
+
+snmpInTooBigs(print) ->
+ gen_counter(print, snmpInTooBigs);
+snmpInTooBigs(get) ->
+ gen_counter(get, snmpInTooBigs).
+
+
+snmpInNoSuchNames(print) ->
+ gen_counter(print, snmpInNoSuchNames);
+snmpInNoSuchNames(get) ->
+ gen_counter(get, snmpInNoSuchNames).
+
+
+snmpInBadValues(print) ->
+ gen_counter(print, snmpInBadValues);
+snmpInBadValues(get) ->
+ gen_counter(get, snmpInBadValues).
+
+
+snmpInReadOnlys(print) ->
+ gen_counter(print, snmpInReadOnlys);
+snmpInReadOnlys(get) ->
+ gen_counter(get, snmpInReadOnlys).
+
+
+snmpInGenErrs(print) ->
+ gen_counter(print, snmpInGenErrs);
+snmpInGenErrs(get) ->
+ gen_counter(get, snmpInGenErrs).
+
+
+snmpInTotalReqVars(print) ->
+ gen_counter(print, snmpInTotalReqVars);
+snmpInTotalReqVars(get) ->
+ gen_counter(get, snmpInTotalReqVars).
+
+
+snmpInTotalSetVars(print) ->
+ gen_counter(print, snmpInTotalSetVars);
+snmpInTotalSetVars(get) ->
+ gen_counter(get, snmpInTotalSetVars).
+
+
+snmpInGetRequests(print) ->
+ gen_counter(print, snmpInGetRequests);
+snmpInGetRequests(get) ->
+ gen_counter(get, snmpInGetRequests).
+
+
+snmpInSetRequests(print) ->
+ gen_counter(print, snmpInSetRequests);
+snmpInSetRequests(get) ->
+ gen_counter(get, snmpInSetRequests).
+
+
+snmpInGetNexts(print) ->
+ gen_counter(print, snmpInGetNexts);
+snmpInGetNexts(get) ->
+ gen_counter(get, snmpInGetNexts).
+
+
+snmpInGetResponses(print) ->
+ gen_counter(print, snmpInGetResponses);
+snmpInGetResponses(get) ->
+ gen_counter(get, snmpInGetResponses).
+
+
+snmpInTraps(print) ->
+ gen_counter(print, snmpInTraps);
+snmpInTraps(get) ->
+ gen_counter(get, snmpInTraps).
+
+
+snmpOutTooBigs(print) ->
+ gen_counter(print, snmpOutTooBigs);
+snmpOutTooBigs(get) ->
+ gen_counter(get, snmpOutTooBigs).
+
+
+snmpOutNoSuchNames(print) ->
+ gen_counter(print, snmpOutNoSuchNames);
+snmpOutNoSuchNames(get) ->
+ gen_counter(get, snmpOutNoSuchNames).
+
+
+snmpOutBadValues(print) ->
+ gen_counter(print, snmpOutBadValues);
+snmpOutBadValues(get) ->
+ gen_counter(get, snmpOutBadValues).
+
+
+snmpOutGenErrs(print) ->
+ gen_counter(print, snmpOutGenErrs);
+snmpOutGenErrs(get) ->
+ gen_counter(get, snmpOutGenErrs).
+
+
+snmpOutGetRequests(print) ->
+ gen_counter(print, snmpOutGetRequests);
+snmpOutGetRequests(get) ->
+ gen_counter(get, snmpOutGetRequests).
+
+
+snmpOutSetRequests(print) ->
+ gen_counter(print, snmpOutSetRequests);
+snmpOutSetRequests(get) ->
+ gen_counter(get, snmpOutSetRequests).
+
+
+snmpOutGetNexts(print) ->
+ gen_counter(print, snmpOutGetNexts);
+snmpOutGetNexts(get) ->
+ gen_counter(get, snmpOutGetNexts).
+
+
+snmpOutGetResponses(print) ->
+ gen_counter(print, snmpOutGetResponses);
+snmpOutGetResponses(get) ->
+ gen_counter(get, snmpOutGetResponses).
+
+
+snmpOutTraps(print) ->
+ gen_counter(print, snmpOutTraps);
+snmpOutTraps(get) ->
+ gen_counter(get, snmpOutTraps).
+
+
+gen_counter(print, Counter) ->
+ Val = gen_counter(get, Counter),
+ VarAndValue = [{Counter, Val}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
+gen_counter(get, Counter) ->
+ variable_func(get, Counter).
+
+
%%-----------------------------------------------------------------
%% This is the instrumentation function for sysUpTime.
%%-----------------------------------------------------------------
+sysUpTime(print) ->
+ sys_up_time(print);
+sysUpTime(get) ->
+ sys_up_time(get).
+
sys_up_time() ->
snmpa:sys_up_time().
+sys_up_time(print) ->
+ VarAndValue = [{sysUpTime, sys_up_time(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
sys_up_time(get) ->
{value, snmpa:sys_up_time()}.
+
%%-----------------------------------------------------------------
%% This is the instrumentation function for snmpEnableAuthenTraps
%%-----------------------------------------------------------------
+
+snmpEnableAuthenTraps(print) ->
+ snmp_enable_authen_traps(print);
+snmpEnableAuthenTraps(get) ->
+ snmp_enable_authen_traps(get).
+
+
+snmp_enable_authen_traps(print) ->
+ VarAndValue = [{snmpEnableAuthenTraps, snmp_enable_authen_traps(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
snmp_enable_authen_traps(new) ->
snmp_generic:variable_func(new, db(snmpEnableAuthenTraps));
@@ -224,9 +487,19 @@ snmp_enable_authen_traps(get) ->
snmp_enable_authen_traps(set, NewVal) ->
snmp_generic:variable_func(set, NewVal, db(snmpEnableAuthenTraps)).
+
%%-----------------------------------------------------------------
-%% This is the instrumentation function for sysObjectId
+%% This is the instrumentation function for sysObjectID
%%-----------------------------------------------------------------
+sysObjectID(print) ->
+ sys_object_id(print);
+sysObjectID(get) ->
+ sys_object_id(get).
+
+sys_object_id(print) ->
+ VarAndValue = [{sysObjectID, sys_object_id(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
sys_object_id(new) ->
snmp_generic:variable_func(new, db(sysObjectID));
@@ -239,6 +512,7 @@ sys_object_id(get) ->
sys_object_id(set, NewVal) ->
snmp_generic:variable_func(set, NewVal, db(sysObjectID)).
+
%%-----------------------------------------------------------------
%% This is a dummy instrumentation function for objects like
%% snmpTrapOID, that is accessible-for-notify, with different
@@ -247,6 +521,7 @@ sys_object_id(set, NewVal) ->
%%-----------------------------------------------------------------
dummy(_Op) -> ok.
+
%%-----------------------------------------------------------------
%% This is the instrumentation function for snmpSetSerialNo.
%% It is always volatile.
diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl
index a3ac67b533..270a5fd5b6 100644
--- a/lib/snmp/src/agent/snmp_target_mib.erl
+++ b/lib/snmp/src/agent/snmp_target_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,8 @@
%%
-module(snmp_target_mib).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1,
snmpTargetSpinLock/1, snmpTargetSpinLock/2,
snmpTargetAddrTable/1, snmpTargetAddrTable/3,
@@ -509,6 +511,10 @@ set_target_engine_id(TargetAddrName, EngineId) ->
%%-----------------------------------------------------------------
%% Instrumentation Functions
%%-----------------------------------------------------------------
+snmpTargetSpinLock(print) ->
+ VarAndValue = [{snmpTargetSpinLock, snmpTargetSpinLock(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
snmpTargetSpinLock(new) ->
snmp_generic:variable_func(new, {snmpTargetSpinLock, volatile}),
{A1,A2,A3} = erlang:now(),
@@ -589,12 +595,9 @@ snmpTargetAddrTable(print) ->
?'snmpTargetAddrRowStatus_active' -> active;
_ -> undefined
end,
- Prefix,
- element(?snmpTargetAddrEngineId, Row),
- Prefix,
- element(?snmpTargetAddrTMask, Row),
- Prefix,
- element(?snmpTargetAddrMMS, Row)]))
+ Prefix, element(?snmpTargetAddrEngineId, Row),
+ Prefix, element(?snmpTargetAddrTMask, Row),
+ Prefix, element(?snmpTargetAddrMMS, Row)]))
end,
snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow);
%% Op == new | delete
diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
index 7b881f888c..69cebc858b 100644
--- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
+++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,20 @@
%%
-module(snmp_user_based_sm_mib).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1,
usmUserSpinLock/1, usmUserSpinLock/2,
usmUserTable/1, usmUserTable/3,
table_next/2,
is_engine_id_known/1, get_user/2, get_user_from_security_name/2,
mk_key_change/3, mk_key_change/5, extract_new_key/3, mk_random/1]).
+-export([usmStatsUnsupportedSecLevels/1,
+ usmStatsNotInTimeWindows/1,
+ usmStatsUnknownUserNames/1,
+ usmStatsUnknownEngineIDs/1,
+ usmStatsWrongDigests/1,
+ usmStatsDecryptionErrors/1]).
-export([add_user/1, add_user/13, delete_user/1]).
%% Internal
@@ -301,6 +309,54 @@ gc_tabs() ->
%%-----------------------------------------------------------------
%% Counter functions
%%-----------------------------------------------------------------
+
+usmStatsUnsupportedSecLevels(print) ->
+ VarAndValue = [{usmStatsUnsupportedSecLevels,
+ usmStatsUnsupportedSecLevels(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+usmStatsUnsupportedSecLevels(get) ->
+ get_counter(usmStatsUnsupportedSecLevels).
+
+usmStatsNotInTimeWindows(print) ->
+ VarAndValue = [{usmStatsNotInTimeWindows, usmStatsNotInTimeWindows(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+usmStatsNotInTimeWindows(get) ->
+ get_counter(usmStatsNotInTimeWindows).
+
+usmStatsUnknownUserNames(print) ->
+ VarAndValue = [{usmStatsUnknownUserNames, usmStatsUnknownUserNames(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+usmStatsUnknownUserNames(get) ->
+ get_counter(usmStatsUnknownUserNames).
+
+usmStatsUnknownEngineIDs(print) ->
+ VarAndValue = [{usmStatsUnknownEngineIDs, usmStatsUnknownEngineIDs(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+usmStatsUnknownEngineIDs(get) ->
+ get_counter(usmStatsUnknownEngineIDs).
+
+usmStatsWrongDigests(print) ->
+ VarAndValue = [{usmStatsWrongDigests, usmStatsWrongDigests(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+usmStatsWrongDigests(get) ->
+ get_counter(usmStatsWrongDigests).
+
+usmStatsDecryptionErrors(print) ->
+ VarAndValue = [{usmStatsDecryptionErrors, usmStatsDecryptionErrors(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+usmStatsDecryptionErrors(get) ->
+ get_counter(usmStatsDecryptionErrors).
+
+
+get_counter(Name) ->
+ case (catch ets:lookup(snmp_agent_table, Name)) of
+ [{_, Val}] ->
+ {value, Val};
+ _ ->
+ genErr
+ end.
+
+
init_vars() -> lists:map(fun maybe_create_var/1, vars()).
maybe_create_var(Var) ->
@@ -321,6 +377,7 @@ vars() ->
usmStatsDecryptionErrors
].
+
%%-----------------------------------------------------------------
%% API functions
%%-----------------------------------------------------------------
@@ -372,6 +429,11 @@ get_user_from_security_name(EngineID, SecName) ->
%%-----------------------------------------------------------------
%% Instrumentation Functions
%%-----------------------------------------------------------------
+
+usmUserSpinLock(print) ->
+ VarAndValue = [{usmUserSpinLock, usmUserSpinLock(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
usmUserSpinLock(new) ->
snmp_generic:variable_func(new, {usmUserSpinLock, volatile}),
{A1,A2,A3} = erlang:now(),
diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
index 873ab00545..28469a7b4e 100644
--- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
+++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,6 +18,8 @@
%%
-module(snmp_view_based_acm_mib).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([configure/1, reconfigure/1, table_next/2, get/3]).
-export([vacmAccessTable/1, vacmAccessTable/3,
@@ -131,7 +133,6 @@ check_vacm({vacmSecurityToGroup, SecModel, SecName, GroupName}) ->
{ok, SecM} = snmp_conf:check_sec_model(SecModel, []),
snmp_conf:check_string(SecName),
snmp_conf:check_string(GroupName),
-
Vacm = {SecM, SecName, GroupName,
?'StorageType_nonVolatile', ?'RowStatus_active'},
{ok, {vacmSecurityToGroup, Vacm}};
@@ -179,15 +180,22 @@ init_tabs(Sec2Group, Access, View) ->
snmpa_local_db:table_delete(db(vacmSecurityToGroupTable)),
snmpa_local_db:table_create(db(vacmSecurityToGroupTable)),
init_sec2group_table(Sec2Group),
+
+ ?vdebug("create vacm access table",[]),
+ snmpa_vacm:cleanup(),
init_access_table(Access),
+
?vdebug("create vacm view-tree-family table",[]),
snmpa_local_db:table_delete(db(vacmViewTreeFamilyTable)),
snmpa_local_db:table_create(db(vacmViewTreeFamilyTable)),
- init_view_table(View).
+ init_view_table(View),
+
+ ?vdebug("table(s) initiated",[]),
+ ok.
init_sec2group_table([Row | T]) ->
-% ?vtrace("init security-to-group table: "
-% "~n Row: ~p",[Row]),
+%% ?vtrace("init security-to-group table: "
+%% "~n Row: ~p",[Row]),
Key1 = element(1, Row),
Key2 = element(2, Row),
Key = [Key1, length(Key2) | Key2],
@@ -196,12 +204,12 @@ init_sec2group_table([Row | T]) ->
init_sec2group_table([]) -> true.
init_access_table([{GN, Prefix, Model, Level, Row} | T]) ->
-% ?vtrace("init access table: "
-% "~n GN: ~p"
-% "~n Prefix: ~p"
-% "~n Model: ~p"
-% "~n Level: ~p"
-% "~n Row: ~p",[GN, Prefix, Model, Level, Row]),
+%% ?vtrace("init access table: "
+%% "~n GN: ~p"
+%% "~n Prefix: ~p"
+%% "~n Model: ~p"
+%% "~n Level: ~p"
+%% "~n Row: ~p",[GN, Prefix, Model, Level, Row]),
Key = [length(GN) | GN] ++ [length(Prefix) | Prefix] ++ [Model, Level],
snmpa_vacm:insert([{Key, Row}], false),
init_access_table(T);
@@ -209,8 +217,8 @@ init_access_table([]) ->
snmpa_vacm:dump_table().
init_view_table([Row | T]) ->
-% ?vtrace("init view table: "
-% "~n Row: ~p",[Row]),
+%% ?vtrace("init view table: "
+%% "~n Row: ~p",[Row]),
Key1 = element(1, Row),
Key2 = element(2, Row),
Key = [length(Key1) | Key1] ++ [length(Key2) | Key2],
@@ -346,6 +354,49 @@ vacmContextTable(Op, Arg1, Arg2) ->
snmp_framework_mib:intContextTable(Op, Arg1, Arg2).
+vacmSecurityToGroupTable(print) ->
+ Table = vacmSecurityToGroupTable,
+ DB = db(Table),
+ FOI = foi(Table),
+ PrintRow =
+ fun(Prefix, Row) ->
+ lists:flatten(
+ io_lib:format("~sSecurityModel: ~p (~w)"
+ "~n~sSecurityName: ~p"
+ "~n~sGroupName: ~p"
+ "~n~sStorageType: ~p (~w)"
+ "~n~sStatus: ~p (~w)",
+ [Prefix, element(?vacmSecurityModel, Row),
+ case element(?vacmSecurityModel, Row) of
+ ?SEC_ANY -> any;
+ ?SEC_V1 -> v1;
+ ?SEC_V2C -> v2c;
+ ?SEC_USM -> usm;
+ _ -> undefined
+ end,
+ Prefix, element(?vacmSecurityName, Row),
+ Prefix, element(?vacmGroupName, Row),
+ Prefix, element(?vacmSecurityToGroupStorageType, Row),
+ case element(?vacmSecurityToGroupStorageType, Row) of
+ ?'vacmSecurityToGroupStorageType_readOnly' -> readOnly;
+ ?'vacmSecurityToGroupStorageType_permanent' -> permanent;
+ ?'vacmSecurityToGroupStorageType_nonVolatile' -> nonVolatile;
+ ?'vacmSecurityToGroupStorageType_volatile' -> volatile;
+ ?'vacmSecurityToGroupStorageType_other' -> other;
+ _ -> undefined
+ end,
+ Prefix, element(?vacmSecurityToGroupStatus, Row),
+ case element(?vacmSecurityToGroupStatus, Row) of
+ ?'vacmSecurityToGroupStatus_destroy' -> destroy;
+ ?'vacmSecurityToGroupStatus_createAndWait' -> createAndWait;
+ ?'vacmSecurityToGroupStatus_createAndGo' -> createAndGo;
+ ?'vacmSecurityToGroupStatus_notReady' -> notReady;
+ ?'vacmSecurityToGroupStatus_notInService' -> notInService;
+ ?'vacmSecurityToGroupStatus_active' -> active;
+ _ -> undefined
+ end]))
+ end,
+ snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow);
vacmSecurityToGroupTable(Op) ->
snmp_generic:table_func(Op, db(vacmSecurityToGroupTable)).
@@ -400,13 +451,13 @@ verify_vacmSecurityToGroupTable_cols([{Col, Val0}|Cols], Acc) ->
verify_vacmSecurityToGroupTable_col(?vacmSecurityModel, Model) ->
case Model of
any -> ?SEC_ANY;
- v1 -> ?SEC_ANY;
- v2c -> ?SEC_ANY;
- usm -> ?SEC_ANY;
+ v1 -> ?SEC_V1;
+ v2c -> ?SEC_V2C;
+ usm -> ?SEC_USM;
?SEC_ANY -> ?SEC_ANY;
- ?SEC_V1 -> ?SEC_ANY;
- ?SEC_V2C -> ?SEC_ANY;
- ?SEC_USM -> ?SEC_ANY;
+ ?SEC_V1 -> ?SEC_V1;
+ ?SEC_V2C -> ?SEC_V2C;
+ ?SEC_USM -> ?SEC_USM;
_ ->
?vlog("verification of vacmSecurityModel(~w) ~p failed",
[?vacmSecurityModel, Model]),
@@ -443,6 +494,49 @@ verify_vacmSecurityToGroupTable_col(_, Val) ->
%% {RowIndex, {Col4, Col5, ..., Col9}}
%%
%%-----------------------------------------------------------------
+vacmAccessTable(print) ->
+ %% M�ste jag g�ra om alla entrien till {RowIdx, Row}?
+ TableInfo = get_table(vacmAccessTable),
+ PrintRow =
+ fun(Prefix, Row) ->
+ lists:flatten(
+ io_lib:format("~sContextMatch: ~p (~w)"
+ "~n~sReadViewName: ~p"
+ "~n~sWriteViewName: ~p"
+ "~n~sNotifyViewName: ~p"
+ "~n~sStorageType: ~p (~w)"
+ "~n~sStatus: ~p (~w)",
+ [Prefix, element(?vacmAccessContextMatch-3, Row),
+ case element(?vacmAccessContextMatch-3, Row) of
+ ?vacmAccessContextMatch_exact -> exact;
+ ?vacmAccessContextMatch_prefix -> prefix;
+ _ -> undefined
+ end,
+ Prefix, element(?vacmAccessReadViewName-3, Row),
+ Prefix, element(?vacmAccessWriteViewName-3, Row),
+ Prefix, element(?vacmAccessNotifyViewName-3, Row),
+ Prefix, element(?vacmAccessStorageType-3, Row),
+ case element(?vacmAccessStorageType-3, Row) of
+ ?vacmAccessStorageType_other -> other ;
+ ?vacmAccessStorageType_volatile -> volatile;
+ ?vacmAccessStorageType_nonVolatile -> nonVolatile;
+ ?vacmAccessStorageType_permanent -> permanent;
+ ?vacmAccessStorageType_readOnly -> readOnly;
+ _ -> undefined
+ end,
+ Prefix, element(?vacmAccessStatus-3, Row),
+ case element(?vacmAccessStatus-3, Row) of
+ ?vacmAccessStatus_destroy -> destroy;
+ ?vacmAccessStatus_createAndWait -> createAndWait;
+ ?vacmAccessStatus_createAndGo -> createAndGo;
+ ?vacmAccessStatus_notReady -> notReady;
+ ?vacmAccessStatus_notInService -> notInService;
+ ?vacmAccessStatus_active -> active;
+ _ -> undefined
+ end
+ ]))
+ end,
+ snmpa_mib_lib:print_table(vacmAccessTable, {ok, TableInfo}, PrintRow);
vacmAccessTable(_Op) ->
ok.
vacmAccessTable(get, RowIndex, Cols) ->
@@ -538,24 +632,24 @@ verify_vacmAccessTable_col(?vacmAccessContextPrefix, Pref) ->
verify_vacmAccessTable_col(?vacmAccessSecurityModel, Model) ->
case Model of
any -> ?SEC_ANY;
- v1 -> ?SEC_ANY;
- v2c -> ?SEC_ANY;
- usm -> ?SEC_ANY;
+ v1 -> ?SEC_V1;
+ v2c -> ?SEC_V2C;
+ usm -> ?SEC_USM;
?SEC_ANY -> ?SEC_ANY;
- ?SEC_V1 -> ?SEC_ANY;
- ?SEC_V2C -> ?SEC_ANY;
- ?SEC_USM -> ?SEC_ANY;
+ ?SEC_V1 -> ?SEC_V1;
+ ?SEC_V2C -> ?SEC_V2C;
+ ?SEC_USM -> ?SEC_USM;
_ ->
wrongValue(?vacmAccessSecurityModel)
end;
verify_vacmAccessTable_col(?vacmAccessSecurityLevel, Level) ->
case Level of
- noAuthNoPriv -> 1;
- authNoPriv -> 2;
- authPriv -> 3;
- 1 -> 1;
- 2 -> 2;
- 3 -> 3;
+ noAuthNoPriv -> ?vacmAccessSecurityLevel_noAuthNoPriv;
+ authNoPriv -> ?vacmAccessSecurityLevel_authNoPriv;
+ authPriv -> ?vacmAccessSecurityLevel_authPriv;
+ ?vacmAccessSecurityLevel_noAuthNoPriv -> ?vacmAccessSecurityLevel_noAuthNoPriv;
+ ?vacmAccessSecurityLevel_authNoPriv -> ?vacmAccessSecurityLevel_authNoPriv;
+ ?vacmAccessSecurityLevel_authPriv -> ?vacmAccessSecurityLevel_authPriv;
_ -> wrongValue(?vacmAccessSecurityLevel)
end;
verify_vacmAccessTable_col(?vacmAccessContextMatch, Match) ->
@@ -662,6 +756,7 @@ do_get_next(RowIndex, Cols) ->
end
end.
+
%%-----------------------------------------------------------------
%% Functions to manipulate vacmAccessRows.
%%-----------------------------------------------------------------
@@ -694,29 +789,76 @@ split_cols([Col | Cols], PreCols) when Col =< 3 ->
split_cols(Cols, PreCols) ->
{PreCols, Cols}.
+vacmViewSpinLock(print) ->
+ VarAndValue = [{vacmViewSpinLock, vacmViewSpinLock(get)}],
+ snmpa_mib_lib:print_variables(VarAndValue);
+
vacmViewSpinLock(new) ->
- snmp_generic:variable_func(new, {vacmViewSpinLock, volatile}),
+ snmp_generic:variable_func(new, volatile_db(vacmViewSpinLock)),
{A1,A2,A3} = erlang:now(),
random:seed(A1,A2,A3),
Val = random:uniform(2147483648) - 1,
- snmp_generic:variable_func(set, Val, {vacmViewSpinLock, volatile});
+ snmp_generic:variable_func(set, Val, volatile_db(vacmViewSpinLock));
vacmViewSpinLock(delete) ->
ok;
vacmViewSpinLock(get) ->
- snmp_generic:variable_func(get, {vacmViewSpinLock, volatile}).
+ snmp_generic:variable_func(get, volatile_db(vacmViewSpinLock)).
vacmViewSpinLock(is_set_ok, NewVal) ->
- case snmp_generic:variable_func(get, {vacmViewSpinLock, volatile}) of
+ case snmp_generic:variable_func(get, volatile_db(vacmViewSpinLock)) of
{value, NewVal} -> noError;
_ -> inconsistentValue
end;
vacmViewSpinLock(set, NewVal) ->
snmp_generic:variable_func(set, (NewVal + 1) rem 2147483648,
- {vacmViewSpinLock, volatile}).
-
-
+ volatile_db(vacmViewSpinLock)).
+
+
+vacmViewTreeFamilyTable(print) ->
+ Table = vacmViewTreeFamilyTable,
+ DB = db(Table),
+ FOI = foi(Table),
+ PrintRow =
+ fun(Prefix, Row) ->
+ lists:flatten(
+ io_lib:format("~sViewName: ~p"
+ "~n~sSubtree: ~p"
+ "~n~sMask: ~p"
+ "~n~sType: ~p (~w)"
+ "~n~sStorageType: ~p (~w)"
+ "~n~sStatus: ~p (~w)",
+ [Prefix, element(?vacmViewTreeFamilyViewName, Row),
+ Prefix, element(?vacmViewTreeFamilySubtree, Row),
+ Prefix, element(?vacmViewTreeFamilyMask, Row),
+ Prefix, element(?vacmViewTreeFamilyType, Row),
+ case element(?vacmViewTreeFamilyType, Row) of
+ ?vacmViewTreeFamilyType_included -> included;
+ ?vacmViewTreeFamilyType_excluded -> excluded;
+ _ -> undefined
+ end,
+ Prefix, element(?vacmViewTreeFamilyStorageType, Row),
+ case element(?vacmViewTreeFamilyStorageType, Row) of
+ ?vacmViewTreeFamilyStorageType_readOnly -> readOnly;
+ ?vacmViewTreeFamilyStorageType_permanent -> permanent;
+ ?vacmViewTreeFamilyStorageType_nonVolatile -> nonVolatile;
+ ?vacmViewTreeFamilyStorageType_volatile -> volatile;
+ ?vacmViewTreeFamilyStorageType_other -> other;
+ _ -> undefined
+ end,
+ Prefix, element(?vacmViewTreeFamilyStatus, Row),
+ case element(?vacmViewTreeFamilyStatus, Row) of
+ ?vacmViewTreeFamilyStatus_destroy -> destroy;
+ ?vacmViewTreeFamilyStatus_createAndWait -> createAndWait;
+ ?vacmViewTreeFamilyStatus_createAndGo -> createAndGo;
+ ?vacmViewTreeFamilyStatus_notReady -> notReady;
+ ?vacmViewTreeFamilyStatus_notInService -> notInService;
+ ?vacmViewTreeFamilyStatus_active -> active;
+ _ -> undefined
+ end]))
+ end,
+ snmpa_mib_lib:print_table(Table, DB, FOI, PrintRow);
vacmViewTreeFamilyTable(Op) ->
snmp_generic:table_func(Op, db(vacmViewTreeFamilyTable)).
vacmViewTreeFamilyTable(get_next, RowIndex, Cols) ->
@@ -793,7 +935,25 @@ table_next(Name, RestOid) ->
snmp_generic:table_next(db(Name), RestOid).
-db(X) -> snmpa_agent:db(X).
+get_table(vacmAccessTable) ->
+ do_get_vacmAccessTable([], []).
+
+do_get_vacmAccessTable(Key0, Acc) ->
+ case snmpa_vacm:get_next_row(Key0) of
+ {Key, _Row} = Entry ->
+ do_get_vacmAccessTable(Key, [Entry | Acc]);
+ false ->
+ lists:reverse(Acc)
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Wrappers
+%%-----------------------------------------------------------------
+
+db(X) -> snmpa_agent:db(X).
+volatile_db(X) -> {X, volatile}.
+
fa(vacmSecurityToGroupTable) -> ?vacmGroupName;
fa(vacmViewTreeFamilyTable) -> ?vacmViewTreeFamilyMask.
diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl
index a113bba3a7..22fbd33add 100644
--- a/lib/snmp/src/agent/snmpa.erl
+++ b/lib/snmp/src/agent/snmpa.erl
@@ -47,6 +47,7 @@
mib_of/1, mib_of/2,
me_of/1, me_of/2,
invalidate_mibs_cache/0, invalidate_mibs_cache/1,
+ which_mibs_cache_size/0, which_mibs_cache_size/1,
enable_mibs_cache/0, enable_mibs_cache/1,
disable_mibs_cache/0, disable_mibs_cache/1,
gc_mibs_cache/0, gc_mibs_cache/1, gc_mibs_cache/2, gc_mibs_cache/3,
@@ -60,7 +61,7 @@
register_subagent/3, unregister_subagent/2,
send_notification/3, send_notification/4, send_notification/5,
- send_notification/6,
+ send_notification/6, send_notification/7,
send_trap/3, send_trap/4,
discovery/2, discovery/3, discovery/4, discovery/5, discovery/6,
@@ -104,6 +105,8 @@
set_request_limit/1, set_request_limit/2
]).
+-export([print_mib_info/0, print_mib_tables/0, print_mib_variables/0]).
+
-include("snmpa_atl.hrl").
-define(EXTRA_INFO, undefined).
@@ -282,6 +285,186 @@ whereis_mib(Agent, Mib) when is_atom(Mib) ->
%% -
+mibs_info() ->
+ [
+ {snmp_standard_mib,
+ [],
+ [
+ sysDescr,
+ sysObjectID,
+ sysContact,
+ sysName,
+ sysLocation,
+ sysServices,
+ snmpEnableAuthenTraps,
+ sysUpTime,
+ snmpInPkts,
+ snmpOutPkts,
+ snmpInBadVersions,
+ snmpInBadCommunityNames,
+ snmpInBadCommunityUses,
+ snmpInASNParseErrs,
+ snmpInTooBigs,
+ snmpInNoSuchNames,
+ snmpInBadValues,
+ snmpInReadOnlys,
+ snmpInGenErrs,
+ snmpInTotalReqVars,
+ snmpInTotalSetVars,
+ snmpInGetRequests,
+ snmpInSetRequests,
+ snmpInGetNexts,
+ snmpInGetResponses,
+ snmpInTraps,
+ snmpOutTooBigs,
+ snmpOutNoSuchNames,
+ snmpOutBadValues,
+ snmpOutGenErrs,
+ snmpOutGetRequests,
+ snmpOutSetRequests,
+ snmpOutGetNexts,
+ snmpOutGetResponses,
+ snmpOutTraps
+ ]
+ },
+ {snmp_framework_mib,
+ [
+ ],
+ [
+ snmpEngineID,
+ snmpEngineBoots,
+ snmpEngineTime,
+ snmpEngineMaxMessageSize
+ ]
+ },
+ {snmp_view_based_acm_mib,
+ [
+ vacmAccessTable,
+ vacmSecurityToGroupTable,
+ vacmViewTreeFamilyTable
+ ],
+ [
+ vacmViewSpinLock
+ ]
+ },
+ {snmp_target_mib,
+ [
+ snmpTargetAddrTable,
+ snmpTargetParamsTable
+ ],
+ [
+ snmpTargetSpinLock
+ ]
+ },
+ {snmp_community_mib,
+ [
+ snmpCommunityTable
+ ],
+ []
+ },
+ {snmp_notification_mib,
+ [
+ snmpNotifyTable
+ ],
+ []},
+ {snmp_user_based_sm_mib,
+ [
+ usmUserTable
+ ],
+ [
+ usmUserSpinLock,
+ usmStatsUnsupportedSecLevels,
+ usmStatsNotInTimeWindows,
+ usmStatsUnknownUserNames,
+ usmStatsUnknownEngineIDs,
+ usmStatsWrongDigests,
+ usmStatsDecryptionErrors
+ ]
+ }
+ ].
+
+print_mib_info() ->
+ MibsInfo = mibs_info(),
+ print_mib_info(MibsInfo).
+
+print_mib_info([]) ->
+ io:format("~n", []),
+ ok;
+print_mib_info([{Mod, Tables, Variables} | MibsInfo]) ->
+ io:format("~n** ~s ** ~n~n", [make_pretty_mib(Mod)]),
+ print_mib_variables2(Mod, Variables),
+ print_mib_tables2(Mod, Tables),
+ io:format("~n", []),
+ print_mib_info(MibsInfo).
+
+
+print_mib_tables() ->
+ Tables = [{Mod, Tabs} || {Mod, Tabs, _Vars} <- mibs_info()],
+ print_mib_tables(Tables).
+
+print_mib_tables([]) ->
+ ok;
+print_mib_tables([{Mod, Tabs}|MibTabs])
+ when is_atom(Mod) andalso is_list(Tabs) ->
+ print_mib_tables(Mod, Tabs),
+ print_mib_tables(MibTabs);
+print_mib_tables([_|MibTabs]) ->
+ print_mib_tables(MibTabs).
+
+print_mib_tables(_Mod, [] = _Tables) ->
+ ok;
+print_mib_tables(Mod, Tables) ->
+ io:format("~n** ~s ** ~n~n", [make_pretty_mib(Mod)]),
+ print_mib_tables2(Mod, Tables),
+ io:format("~n", []).
+
+print_mib_tables2(Mod, Tables) ->
+ [(catch Mod:Table(print)) || Table <- Tables].
+
+
+print_mib_variables() ->
+ Variables = [{Mod, Vars} || {Mod, _Tabs, Vars} <- mibs_info()],
+ print_mib_variables(Variables).
+
+print_mib_variables([]) ->
+ ok;
+print_mib_variables([{Mod, Vars}|MibVars])
+ when is_atom(Mod) andalso is_list(Vars) ->
+ print_mib_variables(Mod, Vars),
+ print_mib_variables(MibVars);
+print_mib_variables([_|MibVars]) ->
+ print_mib_variables(MibVars).
+
+print_mib_variables(_Mod, [] = _Vars) ->
+ ok;
+print_mib_variables(Mod, Vars) ->
+ io:format("~n** ~s ** ~n~n", [make_pretty_mib(Mod)]),
+ print_mib_variables2(Mod, Vars),
+ io:format("~n", []).
+
+print_mib_variables2(Mod, Variables) ->
+ Vars = [{Var, (catch Mod:Var(get))} || Var <- Variables],
+ snmpa_mib_lib:print_variables(Vars).
+
+
+make_pretty_mib(snmp_view_based_acm_mib) ->
+ "SNMP-VIEW-BASED-ACM-MIB";
+make_pretty_mib(snmp_target_mib) ->
+ "SNMP-TARGET-MIB";
+make_pretty_mib(snmp_community_mib) ->
+ "SNMP-COMMUNITY-MIB";
+make_pretty_mib(snmp_notification_mib) ->
+ "SNMP-NOTIFICATION-MIB";
+make_pretty_mib(snmp_user_based_sm_mib) ->
+ "SNMP-USER-BASED-SM-MIB";
+make_pretty_mib(snmp_framework_mib) ->
+ "SNMP-FRAMEWORK-MIB";
+make_pretty_mib(Mod) ->
+ atom_to_list(Mod).
+
+
+%% -
+
mib_of(Oid) ->
snmpa_agent:mib_of(Oid).
@@ -302,6 +485,13 @@ invalidate_mibs_cache(Agent) ->
snmpa_agent:invalidate_mibs_cache(Agent).
+which_mibs_cache_size() ->
+ which_mibs_cache_size(snmp_master_agent).
+
+which_mibs_cache_size(Agent) ->
+ snmpa_agent:which_mibs_cache_size(Agent).
+
+
enable_mibs_cache() ->
enable_mibs_cache(snmp_master_agent).
@@ -415,14 +605,23 @@ send_notification(Agent, Notification, Recv, Varbinds) ->
send_notification(Agent, Notification, Recv, NotifyName, Varbinds) ->
send_notification(Agent, Notification, Recv, NotifyName, "", Varbinds).
-send_notification(Agent, Notification, Recv,
- NotifyName, ContextName, Varbinds)
+send_notification(Agent, Notification, Recv, NotifyName,
+ ContextName, Varbinds)
when (is_list(NotifyName) andalso
is_list(ContextName) andalso
is_list(Varbinds)) ->
snmpa_agent:send_trap(Agent, Notification, NotifyName,
ContextName, Recv, Varbinds).
+send_notification(Agent, Notification, Recv,
+ NotifyName, ContextName, Varbinds, LocalEngineID)
+ when (is_list(NotifyName) andalso
+ is_list(ContextName) andalso
+ is_list(Varbinds) andalso
+ is_list(LocalEngineID)) ->
+ snmpa_agent:send_trap(Agent, Notification, NotifyName,
+ ContextName, Recv, Varbinds, LocalEngineID).
+
%% Kept for backwards compatibility
send_trap(Agent, Trap, Community) ->
send_notification(Agent, Trap, no_receiver, Community, "", []).
diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl
index fb04fca632..f70885b2ec 100644
--- a/lib/snmp/src/agent/snmpa_agent.erl
+++ b/lib/snmp/src/agent/snmpa_agent.erl
@@ -30,7 +30,7 @@
-export([subagent_set/2,
load_mibs/2, unload_mibs/2, which_mibs/1, whereis_mib/2, info/1,
register_subagent/3, unregister_subagent/2,
- send_trap/6,
+ send_trap/6, send_trap/7,
register_notification_filter/5,
unregister_notification_filter/2,
which_notification_filter/1,
@@ -48,6 +48,7 @@
get/2, get/3, get_next/2, get_next/3]).
-export([mib_of/1, mib_of/2, me_of/1, me_of/2,
invalidate_mibs_cache/1,
+ which_mibs_cache_size/1,
enable_mibs_cache/1, disable_mibs_cache/1,
gc_mibs_cache/1, gc_mibs_cache/2, gc_mibs_cache/3,
enable_mibs_cache_autogc/1, disable_mibs_cache_autogc/1,
@@ -64,7 +65,7 @@
%% Internal exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, tr_var/2, tr_varbind/1,
- handle_pdu/7, worker/2, worker_loop/1, do_send_trap/6]).
+ handle_pdu/7, worker/2, worker_loop/1, do_send_trap/7]).
-ifndef(default_verbosity).
-define(default_verbosity,silence).
@@ -245,6 +246,10 @@ disable_mibs_cache(Agent) ->
call(Agent, {mibs_cache_request, disable_cache}).
+which_mibs_cache_size(Agent) ->
+ call(Agent, {mibs_cache_request, cache_size}).
+
+
enable_mibs_cache_autogc(Agent) ->
call(Agent, {mibs_cache_request, enable_autogc}).
@@ -524,14 +529,15 @@ which_notification_filter(Agent) ->
send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) ->
?d("send_trap -> entry with"
- "~n self(): ~p"
- "~n Agent: ~p [~p]"
- "~n Trap: ~p"
- "~n NotifyName: ~p"
- "~n CtxName: ~p"
- "~n Recv: ~p"
- "~n Varbinds: ~p",
- [self(), Agent, wis(Agent), Trap, NotifyName, CtxName, Recv, Varbinds]),
+ "~n self(): ~p"
+ "~n Agent: ~p [~p]"
+ "~n Trap: ~p"
+ "~n NotifyName: ~p"
+ "~n CtxName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p",
+ [self(), Agent, wis(Agent),
+ Trap, NotifyName, CtxName, Recv, Varbinds]),
Msg = {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds},
case (wis(Agent) =:= self()) of
false ->
@@ -540,6 +546,27 @@ send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) ->
Agent ! Msg
end.
+send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) ->
+ ?d("send_trap -> entry with"
+ "~n self(): ~p"
+ "~n Agent: ~p [~p]"
+ "~n Trap: ~p"
+ "~n NotifyName: ~p"
+ "~n CtxName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p"
+ "~n LocalEngineID: ~p",
+ [self(), Agent, wis(Agent),
+ Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID]),
+ Msg =
+ {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID},
+ case (wis(Agent) =:= self()) of
+ false ->
+ call(Agent, Msg);
+ true ->
+ Agent ! Msg
+ end.
+
%% -- Discovery functions --
@@ -626,6 +653,7 @@ wis(Pid) when is_pid(Pid) ->
wis(Atom) when is_atom(Atom) ->
whereis(Atom).
+
forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds) ->
Agent ! {forward_trap, TrapRecord, NotifyName, CtxName, Recv, Varbinds}.
@@ -719,14 +747,15 @@ handle_info(worker_available, S) ->
handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) ->
?vlog("[handle_info] send trap request:"
- "~n Trap: ~p"
- "~n NotifyName: ~p"
- "~n ContextName: ~p"
- "~n Recv: ~p"
- "~n Varbinds: ~p",
- [Trap,NotifyName,ContextName,Recv,Varbinds]),
+ "~n Trap: ~p"
+ "~n NotifyName: ~p"
+ "~n ContextName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p",
+ [Trap, NotifyName, ContextName, Recv, Varbinds]),
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
case catch handle_send_trap(S, Trap, NotifyName, ContextName,
- Recv, Varbinds) of
+ Recv, Varbinds, LocalEngineID) of
{ok, NewS} ->
{noreply, NewS};
{'EXIT', R} ->
@@ -736,17 +765,39 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) ->
{noreply, S}
end;
-handle_info({forward_trap, TrapRecord, NotifyName, ContextName,
- Recv, Varbinds},S) ->
+handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds,
+ LocalEngineID}, S) ->
+ ?vlog("[handle_info] send trap request:"
+ "~n Trap: ~p"
+ "~n NotifyName: ~p"
+ "~n ContextName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p"
+ "~n LocalEngineID: ~p",
+ [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]),
+ case catch handle_send_trap(S, Trap, NotifyName, ContextName,
+ Recv, Varbinds, LocalEngineID) of
+ {ok, NewS} ->
+ {noreply, NewS};
+ {'EXIT', R} ->
+ ?vinfo("Trap not sent:~n ~p", [R]),
+ {noreply, S};
+ _ ->
+ {noreply, S}
+ end;
+
+handle_info({forward_trap, TrapRecord, NotifyName, ContextName,
+ Recv, Varbinds}, S) ->
?vlog("[handle_info] forward trap request:"
- "~n TrapRecord: ~p"
- "~n NotifyName: ~p"
- "~n ContextName: ~p"
- "~n Recv: ~p"
- "~n Varbinds: ~p",
- [TrapRecord,NotifyName,ContextName,Recv,Varbinds]),
+ "~n TrapRecord: ~p"
+ "~n NotifyName: ~p"
+ "~n ContextName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p",
+ [TrapRecord, NotifyName, ContextName, Recv, Varbinds]),
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName,
- Recv, Varbinds)) of
+ Recv, Varbinds, LocalEngineID)) of
{ok, NewS} ->
{noreply, NewS};
{'EXIT', R} ->
@@ -856,17 +907,52 @@ handle_call(restart_set_worker, _From, #state{set_worker = Pid} = S) ->
ok
end,
{reply, ok, S};
+
handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds},
_From, S) ->
?vlog("[handle_call] send trap request:"
- "~n Trap: ~p"
- "~n NotifyName: ~p"
- "~n ContextName: ~p"
- "~n Recv: ~p"
- "~n Varbinds: ~p",
- [Trap,NotifyName,ContextName,Recv,Varbinds]),
+ "~n Trap: ~p"
+ "~n NotifyName: ~p"
+ "~n ContextName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p",
+ [Trap, NotifyName, ContextName, Recv, Varbinds]),
+ LocalEngineID =
+ case S#state.type of
+ master_agent ->
+ ?DEFAULT_LOCAL_ENGINE_ID;
+ _ ->
+ %% subagent -
+ %% we don't need this, eventually the trap sent request
+ %% will reach the master-agent and then it will look up
+ %% the proper engine id.
+ ignore
+ end,
+ case (catch handle_send_trap(S, Trap, NotifyName, ContextName,
+ Recv, Varbinds, LocalEngineID)) of
+ {ok, NewS} ->
+ {reply, ok, NewS};
+ {'EXIT', Reason} ->
+ ?vinfo("Trap not sent:~n ~p", [Reason]),
+ {reply, {error, {send_failed, Reason}}, S};
+ _ ->
+ ?vinfo("Trap not sent", []),
+ {reply, {error, send_failed}, S}
+ end;
+
+handle_call({send_trap, Trap, NotifyName,
+ ContextName, Recv, Varbinds, LocalEngineID},
+ _From, S) ->
+ ?vlog("[handle_call] send trap request:"
+ "~n Trap: ~p"
+ "~n NotifyName: ~p"
+ "~n ContextName: ~p"
+ "~n Recv: ~p"
+ "~n Varbinds: ~p"
+ "~n LocalEngineID: ~p",
+ [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]),
case (catch handle_send_trap(S, Trap, NotifyName, ContextName,
- Recv, Varbinds)) of
+ Recv, Varbinds, LocalEngineID)) of
{ok, NewS} ->
{reply, ok, NewS};
{'EXIT', Reason} ->
@@ -876,8 +962,10 @@ handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds},
?vinfo("Trap not sent", []),
{reply, {error, send_failed}, S}
end;
+
handle_call({discovery,
- TargetName, Notification, ContextName, Vbs, DiscoHandler, ExtraInfo},
+ TargetName, Notification, ContextName, Vbs, DiscoHandler,
+ ExtraInfo},
From,
#state{disco = undefined} = S) ->
?vlog("[handle_call] initiate discovery process:"
@@ -1219,6 +1307,8 @@ handle_mibs_cache_request(MibServer, Req) ->
snmpa_mib:gc_cache(MibServer, Age);
{gc_cache, Age, GcLimit} ->
snmpa_mib:gc_cache(MibServer, Age, GcLimit);
+ cache_size ->
+ snmpa_mib:which_cache_size(MibServer);
enable_cache ->
snmpa_mib:enable_cache(MibServer);
disable_cache ->
@@ -1432,17 +1522,20 @@ spawn_thread(Vsn, Pdu, PduMS, ACMData, Address, Extra) ->
Args = [Vsn, Pdu, PduMS, ACMData, Address, Extra, Dict],
proc_lib:spawn_link(?MODULE, handle_pdu, Args).
-spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, V) ->
+spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID) ->
Dict = get(),
proc_lib:spawn_link(?MODULE, do_send_trap,
- [TrapRec, NotifyName, ContextName, Recv, V, Dict]).
+ [TrapRec, NotifyName, ContextName,
+ Recv, Vbs, LocalEngineID, Dict]).
-do_send_trap(TrapRec, NotifyName, ContextName, Recv, V, Dict) ->
+do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID, Dict) ->
lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict),
put(sname,trap_sender_short_name(get(sname))),
?vlog("starting",[]),
- snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, Recv, V,
- get(net_if)).
+ snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID, get(net_if)).
worker(Master, Dict) ->
lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict),
@@ -1457,17 +1550,22 @@ worker_loop(Master) ->
handle_pdu(Vsn, Pdu, PduMS, ACMData, Address, Extra),
Master ! worker_available;
- %% Old style message
- {MibView, Vsn, Pdu, PduMS, ACMData, AgentData, Extra} ->
- ?vtrace("worker_loop -> received (old) request", []),
- do_handle_pdu(MibView, Vsn, Pdu, PduMS, ACMData, AgentData, Extra),
+ %% We don't trap exits!
+ {TrapRec, NotifyName, ContextName, Recv, Vbs} ->
+ ?vtrace("worker_loop -> send trap:"
+ "~n ~p", [TrapRec]),
+ snmpa_trap:send_trap(TrapRec, NotifyName,
+ ContextName, Recv, Vbs, get(net_if)),
Master ! worker_available;
- {TrapRec, NotifyName, ContextName, Recv, V} -> % We don't trap exits!
+ %% We don't trap exits!
+ {send_trap,
+ TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID} ->
?vtrace("worker_loop -> send trap:"
"~n ~p", [TrapRec]),
snmpa_trap:send_trap(TrapRec, NotifyName,
- ContextName, Recv, V, get(net_if)),
+ ContextName, Recv, Vbs, LocalEngineID,
+ get(net_if)),
Master ! worker_available;
{verbosity, Verbosity} ->
@@ -1616,13 +1714,15 @@ handle_acm_error(Vsn, Reason, Pdu, ACMData, Address, Extra) ->
end.
-handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds) ->
+handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds,
+ LocalEngineID) ->
?vtrace("handle_send_trap -> entry with"
- "~n S#state.type: ~p"
- "~n TrapName: ~p"
- "~n NotifyName: ~p"
- "~n ContextName: ~p",
- [S#state.type, TrapName, NotifyName, ContextName]),
+ "~n S#state.type: ~p"
+ "~n TrapName: ~p"
+ "~n NotifyName: ~p"
+ "~n ContextName: ~p"
+ "~n LocalEngineID: ~p",
+ [S#state.type, TrapName, NotifyName, ContextName, LocalEngineID]),
case snmpa_trap:construct_trap(TrapName, Varbinds) of
{ok, TrapRecord, VarList} ->
?vtrace("handle_send_trap -> construction complete: "
@@ -1639,7 +1739,8 @@ handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds) ->
?vtrace("handle_send_trap -> "
"[master] handle send trap",[]),
maybe_send_trap(S, TrapRecord, NotifyName,
- ContextName, Recv, VarList)
+ ContextName, Recv, VarList,
+ LocalEngineID)
end;
error ->
error
@@ -1676,7 +1777,8 @@ maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S,
maybe_send_trap(#state{nfilters = NFs} = S,
- TrapRec, NotifyName, ContextName, Recv, Varbinds) ->
+ TrapRec, NotifyName, ContextName, Recv, Varbinds,
+ LocalEngineID) ->
?vtrace("maybe_send_trap -> entry with"
"~n NFs: ~p", [NFs]),
case filter_notification(NFs, [], TrapRec) of
@@ -1693,39 +1795,45 @@ maybe_send_trap(#state{nfilters = NFs} = S,
?vtrace("maybe_send_trap -> send trap:"
"~n ~p", [TrapRec2]),
do_handle_send_trap(S, TrapRec2,
- NotifyName, ContextName, Recv, Varbinds);
+ NotifyName, ContextName, Recv, Varbinds,
+ LocalEngineID);
{send, Removed, TrapRec2} ->
?vtrace("maybe_send_trap -> send trap:"
"~n ~p", [TrapRec2]),
NFs2 = del_notification_filter(Removed, NFs),
do_handle_send_trap(S#state{nfilters = NFs2}, TrapRec2,
- NotifyName, ContextName, Recv, Varbinds)
+ NotifyName, ContextName, Recv, Varbinds,
+ LocalEngineID)
end.
-do_handle_send_trap(S, TrapRec, NotifyName, ContextName, Recv, Varbinds) ->
- V = snmpa_trap:try_initialise_vars(get(mibserver), Varbinds),
+do_handle_send_trap(S, TrapRec, NotifyName, ContextName, Recv, Varbinds,
+ LocalEngineID) ->
+ Vbs = snmpa_trap:try_initialise_vars(get(mibserver), Varbinds),
case S#state.type of
subagent ->
forward_trap(S#state.parent, TrapRec, NotifyName, ContextName,
- Recv, V),
+ Recv, Vbs),
{ok, S};
master_agent when S#state.multi_threaded =:= false ->
?vtrace("do_handle_send_trap -> send trap:"
"~n ~p", [TrapRec]),
snmpa_trap:send_trap(TrapRec, NotifyName, ContextName,
- Recv, V, get(net_if)),
+ Recv, Vbs, LocalEngineID, get(net_if)),
{ok, S};
master_agent when S#state.worker_state =:= busy ->
%% Main worker busy => create new worker
?vtrace("do_handle_send_trap -> main worker busy: "
"spawn a trap sender", []),
- spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, V),
+ spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID),
{ok, S};
master_agent ->
%% Send to main worker
?vtrace("do_handle_send_trap -> send to main worker",[]),
- S#state.worker ! {TrapRec, NotifyName, ContextName, Recv, V},
+ S#state.worker ! {send_trap,
+ TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID},
{ok, S#state{worker_state = busy}}
end.
diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl
index b14a0c806c..b4fc716b3e 100644
--- a/lib/snmp/src/agent/snmpa_conf.erl
+++ b/lib/snmp/src/agent/snmpa_conf.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,6 +19,8 @@
-module(snmpa_conf).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([
%% agent.conf
agent_entry/2,
diff --git a/lib/snmp/src/agent/snmpa_internal.hrl b/lib/snmp/src/agent/snmpa_internal.hrl
index a33a6809dc..9fa874f119 100644
--- a/lib/snmp/src/agent/snmpa_internal.hrl
+++ b/lib/snmp/src/agent/snmpa_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -22,6 +22,8 @@
-include_lib("snmp/src/app/snmp_internal.hrl").
+-define(DEFAULT_LOCAL_ENGINE_ID, snmp_framework_mib:get_engine_id()).
+
-define(snmpa_info(F, A), ?snmp_info("agent", F, A)).
-define(snmpa_warning(F, A), ?snmp_warning("agent", F, A)).
-define(snmpa_error(F, A), ?snmp_error("agent", F, A)).
diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl
index 370989d0be..ce90db18b3 100644
--- a/lib/snmp/src/agent/snmpa_mib.erl
+++ b/lib/snmp/src/agent/snmpa_mib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(snmpa_mib).
@@ -55,7 +55,7 @@
-define(NO_CACHE, no_mibs_cache).
-define(DEFAULT_CACHE_USAGE, true).
-define(CACHE_GC_TICKTIME, timer:minutes(1)).
--define(DEFAULT_CACHE_AUTOGC, false).
+-define(DEFAULT_CACHE_AUTOGC, true).
-define(DEFAULT_CACHE_GCLIMIT, 100).
-define(DEFAULT_CACHE_AGE, timer:minutes(10)).
-define(CACHE_GC_TRIGGER, cache_gc_trigger).
diff --git a/lib/snmp/src/agent/snmpa_mib_lib.erl b/lib/snmp/src/agent/snmpa_mib_lib.erl
index 441228b9ee..078e681945 100644
--- a/lib/snmp/src/agent/snmpa_mib_lib.erl
+++ b/lib/snmp/src/agent/snmpa_mib_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,8 @@
-module(snmpa_mib_lib).
-export([table_cre_row/3, table_del_row/2]).
--export([get_table/2, print_table/3, print_table/4, print_tables/1]).
+-export([get_table/2]).
+-export([print_variables/1, print_table/3, print_table/4, print_tables/1]).
-export([gc_tab/3, gc_tab/5]).
-include("SNMPv2-TC.hrl").
@@ -81,31 +82,69 @@ get_table(NameDb, FOI, Oid, Acc) ->
end.
+print_variables(Variables) when is_list(Variables) ->
+ Variables2 = print_variables_prefixify(Variables),
+ lists:foreach(fun({Variable, ValueResult, Prefix}) ->
+ print_variable(Variable, ValueResult, Prefix)
+ end, Variables2),
+ ok.
+
+print_variable(Variable, {value, Val}, Prefix) when is_atom(Variable) ->
+ io:format("~w~s=> ~p~n", [Variable, Prefix, Val]);
+print_variable(Variable, Error, Prefix) when is_atom(Variable) ->
+ io:format("~w~s=> [e] ~p~n", [Variable, Prefix, Error]).
+
+print_variables_prefixify(Variables) ->
+ MaxVarLength = print_variables_maxlength(Variables),
+ print_variables_prefixify(Variables, MaxVarLength, []).
+
+print_variables_prefixify([], _MaxVarLength, Acc) ->
+ lists:reverse(Acc);
+print_variables_prefixify([{Var, Res}|Variables], MaxVarLength, Acc) ->
+ Prefix = make_variable_print_prefix(Var, MaxVarLength),
+ print_variables_prefixify(Variables, MaxVarLength,
+ [{Var, Res, Prefix}|Acc]).
+
+make_variable_print_prefix(Var, MaxVarLength) ->
+ lists:duplicate(MaxVarLength - length(atom_to_list(Var)) + 1, $ ).
+
+print_variables_maxlength(Variables) ->
+ print_variables_maxlength(Variables, 0).
+
+print_variables_maxlength([], MaxLength) ->
+ MaxLength;
+print_variables_maxlength([{Var, _}|Variables], MaxLength) when is_atom(Var) ->
+ VarLen = length(atom_to_list(Var)),
+ if
+ VarLen > MaxLength ->
+ print_variables_maxlength(Variables, VarLen);
+ true ->
+ print_variables_maxlength(Variables, MaxLength)
+ end.
+
+
print_tables(Tables) when is_list(Tables) ->
lists:foreach(fun({Table, DB, FOI, PrintRow}) ->
print_table(Table, DB, FOI, PrintRow)
end, Tables),
ok.
-%% print_table(Table, DB, FOI, PrintRow) ->
-%% TableInfo = get_table(DB(Table), FOI(Table)),
-%% print_table(Table, TableInfo, PrintRow),
-%% ok.
-
print_table(Table, DB, FOI, PrintRow) ->
TableInfo = get_table(DB, FOI),
print_table(Table, TableInfo, PrintRow).
print_table(Table, TableInfo, PrintRow) when is_function(PrintRow, 2) ->
- io:format("~w => ~n", [Table]),
+ io:format("~w =>", [Table]),
do_print_table(TableInfo, PrintRow).
+do_print_table({ok, [] = _TableInfo}, _PrintRow) ->
+ io:format(" -~n", []);
do_print_table({ok, TableInfo}, PrintRow) when is_function(PrintRow, 2) ->
+ io:format("~n", []),
lists:foreach(fun({RowIdx, Row}) ->
io:format(" ~w => ~n~s~n",
[RowIdx, PrintRow(" ", Row)])
- end, TableInfo),
- io:format("~n", []);
+ end, TableInfo);
do_print_table({error, {invalid_rowindex, BadRowIndex, []}}, _PrintRow) ->
io:format("Error: Bad rowindex ~w~n", [BadRowIndex]);
do_print_table({error, {invalid_rowindex, BadRowIndex, TableInfo}}, PrintRow) ->
diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl
index 2e09286b87..fd75b98f84 100644
--- a/lib/snmp/src/agent/snmpa_mpd.erl
+++ b/lib/snmp/src/agent/snmpa_mpd.erl
@@ -1,27 +1,28 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(snmpa_mpd).
-export([init/1, reset/0, inc/1, counters/0,
discarded_pdu/1,
- process_packet/6,
- generate_response_msg/5, generate_msg/5,
+ process_packet/6, process_packet/7,
+ generate_response_msg/5, generate_response_msg/6,
+ generate_msg/5, generate_msg/6,
generate_discovery_msg/4,
process_taddrs/1,
generate_req_id/0]).
@@ -34,6 +35,7 @@
-define(VMODULE,"MPD").
-include("snmp_verbosity.hrl").
+-include("snmpa_internal.hrl").
-define(empty_msg_size, 24).
@@ -120,6 +122,12 @@ reset() ->
%% section 4.2.1 in rfc2272)
%%-----------------------------------------------------------------
process_packet(Packet, TDomain, TAddress, State, NoteStore, Log) ->
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ process_packet(Packet, TDomain, TAddress, LocalEngineID,
+ State, NoteStore, Log).
+
+process_packet(Packet, TDomain, TAddress, LocalEngineID,
+ State, NoteStore, Log) ->
inc(snmpInPkts),
case catch snmp_pdus:dec_message_only(binary_to_list(Packet)) of
@@ -127,15 +135,17 @@ process_packet(Packet, TDomain, TAddress, State, NoteStore, Log) ->
when State#state.v1 =:= true ->
?vlog("v1, community: ~s", [Community]),
HS = ?empty_msg_size + length(Community),
- v1_v2c_proc('version-1', NoteStore, Community, TDomain, TAddress,
- Data, HS, Log, Packet);
+ v1_v2c_proc('version-1', NoteStore, Community,
+ TDomain, TAddress,
+ LocalEngineID, Data, HS, Log, Packet);
#message{version = 'version-2', vsn_hdr = Community, data = Data}
when State#state.v2c =:= true ->
?vlog("v2c, community: ~s", [Community]),
HS = ?empty_msg_size + length(Community),
- v1_v2c_proc('version-2', NoteStore, Community, TDomain, TAddress,
- Data, HS, Log, Packet);
+ v1_v2c_proc('version-2', NoteStore, Community,
+ TDomain, TAddress,
+ LocalEngineID, Data, HS, Log, Packet);
#message{version = 'version-3', vsn_hdr = V3Hdr, data = Data}
when State#state.v3 =:= true ->
@@ -143,7 +153,9 @@ process_packet(Packet, TDomain, TAddress, State, NoteStore, Log) ->
[V3Hdr#v3_hdr.msgID,
V3Hdr#v3_hdr.msgFlags,
V3Hdr#v3_hdr.msgSecurityModel]),
- v3_proc(NoteStore, Packet, TDomain, TAddress, V3Hdr, Data, Log);
+ v3_proc(NoteStore, Packet,
+ TDomain, TAddress,
+ LocalEngineID, V3Hdr, Data, Log);
{'EXIT', {bad_version, Vsn}} ->
?vtrace("exit: bad version: ~p",[Vsn]),
@@ -170,10 +182,11 @@ discarded_pdu(Variable) -> inc(Variable).
%%-----------------------------------------------------------------
%% Handles a Community based message (v1 or v2c).
%%-----------------------------------------------------------------
-v1_v2c_proc(Vsn, NoteStore, Community, snmpUDPDomain, {Ip, Udp},
+v1_v2c_proc(Vsn, NoteStore, Community, snmpUDPDomain,
+ {Ip, Udp}, LocalEngineID,
Data, HS, Log, Packet) ->
TAddress = tuple_to_list(Ip) ++ [Udp div 256, Udp rem 256],
- AgentMS = snmp_framework_mib:get_engine_max_message_size(),
+ AgentMS = get_engine_max_message_size(LocalEngineID),
MgrMS = snmp_community_mib:get_target_addr_ext_mms(?snmpUDPDomain,
TAddress),
PduMS = case MgrMS of
@@ -220,10 +233,10 @@ v1_v2c_proc(Vsn, NoteStore, Community, snmpUDPDomain, {Ip, Udp},
{discarded, trap_pdu}
end;
v1_v2c_proc(_Vsn, _NoteStore, _Community, snmpUDPDomain, TAddress,
- _Data, _HS, _Log, _Packet) ->
+ _LocalEngineID, _Data, _HS, _Log, _Packet) ->
{discarded, {badarg, TAddress}};
v1_v2c_proc(_Vsn, _NoteStore, _Community, TDomain, _TAddress,
- _Data, _HS, _Log, _Packet) ->
+ _LocalEngineID, _Data, _HS, _Log, _Packet) ->
{discarded, {badarg, TDomain}}.
sec_model('version-1') -> ?SEC_V1;
@@ -234,15 +247,19 @@ sec_model('version-2') -> ?SEC_V2C.
%% Handles a SNMPv3 Message, following the procedures in rfc2272,
%% section 4.2 and 7.2
%%-----------------------------------------------------------------
-v3_proc(NoteStore, Packet, _TDomain, _TAddress, V3Hdr, Data, Log) ->
- case (catch v3_proc(NoteStore, Packet, V3Hdr, Data, Log)) of
+v3_proc(NoteStore, Packet, _TDomain, _TAddress, LocalEngineID,
+ V3Hdr, Data, Log) ->
+ case (catch v3_proc(NoteStore, Packet, LocalEngineID, V3Hdr, Data, Log)) of
{'EXIT', Reason} ->
exit(Reason);
Result ->
Result
end.
-v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
+v3_proc(NoteStore, Packet, LocalEngineID, V3Hdr, Data, Log) ->
+ ?vtrace("v3_proc -> entry with"
+ "~n LocalEngineID: ~p",
+ [LocalEngineID]),
%% 7.2.3
#v3_hdr{msgID = MsgID,
msgMaxSize = MMS,
@@ -250,7 +267,7 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
msgSecurityModel = MsgSecurityModel,
msgSecurityParameters = SecParams,
hdr_size = HdrSize} = V3Hdr,
- ?vdebug("v3_proc -> version 3 message header:"
+ ?vdebug("v3_proc -> version 3 message header [7.2.3]:"
"~n msgID = ~p"
"~n msgMaxSize = ~p"
"~n msgFlags = ~p"
@@ -263,17 +280,19 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
SecLevel = check_sec_level(MsgFlags),
IsReportable = snmp_misc:is_reportable(MsgFlags),
%% 7.2.6
- ?vtrace("v3_proc -> "
+ ?vtrace("v3_proc -> [7.2.6]"
"~n SecModule = ~p"
"~n SecLevel = ~p"
"~n IsReportable = ~p",
- [SecModule,SecLevel,IsReportable]),
+ [SecModule, SecLevel, IsReportable]),
SecRes = (catch SecModule:process_incoming_msg(Packet, Data,
- SecParams, SecLevel)),
+ SecParams, SecLevel,
+ LocalEngineID)),
?vtrace("v3_proc -> message processing result: "
"~n SecRes: ~p", [SecRes]),
{SecEngineID, SecName, ScopedPDUBytes, SecData, DiscoOrPlain} =
- check_sec_module_result(SecRes, V3Hdr, Data, IsReportable, Log),
+ check_sec_module_result(SecRes, V3Hdr, Data,
+ LocalEngineID, IsReportable, Log),
?vtrace("v3_proc -> "
"~n DiscoOrPlain: ~w"
"~n SecEngineID: ~w"
@@ -311,7 +330,7 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
Log(PDU#pdu.type, Packet)
end,
%% Make sure a get_bulk doesn't get too big.
- AgentMS = snmp_framework_mib:get_engine_max_message_size(),
+ AgentMS = get_engine_max_message_size(LocalEngineID),
%% PduMMS is supposed to be the maximum total length of the response
%% PDU we can send. From the MMS, we need to subtract everything before
%% the PDU, i.e. Message and ScopedPDU.
@@ -415,8 +434,8 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
throw({discarded, received_v2_trap});
Type ->
%% 7.2.13
- SnmpEngineID = snmp_framework_mib:get_engine_id(),
- ?vtrace("v3_proc -> SnmpEngineID = ~w", [SnmpEngineID]),
+ SnmpEngineID = LocalEngineID,
+ ?vtrace("v3_proc -> 7.2.13", []),
case SecEngineID of
SnmpEngineID when (DiscoOrPlain =:= discovery) ->
%% This is a discovery step 2 message!
@@ -429,6 +448,7 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
ContextName,
SecData,
PDU,
+ LocalEngineID,
Log);
SnmpEngineID when (DiscoOrPlain =:= plain) ->
@@ -444,17 +464,18 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
%% 4.2.2.1.2
NIsReportable = snmp_misc:is_reportable_pdu(Type),
Val = inc(snmpUnknownPDUHandlers),
- ErrorInfo = {#varbind{oid = ?snmpUnknownPDUHandlers,
- variabletype = 'Counter32',
- value = Val},
- SecName,
- [{securityLevel, SecLevel},
- {contextEngineID, ContextEngineID},
- {contextName, ContextName}]},
+ ErrorInfo =
+ {#varbind{oid = ?snmpUnknownPDUHandlers,
+ variabletype = 'Counter32',
+ value = Val},
+ SecName,
+ [{securityLevel, SecLevel},
+ {contextEngineID, ContextEngineID},
+ {contextName, ContextName}]},
case generate_v3_report_msg(MsgID,
MsgSecurityModel,
- Data, ErrorInfo,
- Log) of
+ Data, LocalEngineID,
+ ErrorInfo, Log) of
{ok, Report} when NIsReportable =:= true ->
{discarded, snmpUnknownPDUHandlers, Report};
_ ->
@@ -473,6 +494,7 @@ v3_proc(NoteStore, Packet, V3Hdr, Data, Log) ->
ContextName,
SecData,
PDU,
+ LocalEngineID,
Log);
_ ->
@@ -501,7 +523,7 @@ check_sec_level(Unknown) ->
inc(snmpInvalidMsgs),
throw({discarded, snmpInvalidMsgs}).
-check_sec_module_result(Res, V3Hdr, Data, IsReportable, Log) ->
+check_sec_module_result(Res, V3Hdr, Data, LocalEngineID, IsReportable, Log) ->
case Res of
{ok, X} ->
X;
@@ -516,7 +538,7 @@ check_sec_module_result(Res, V3Hdr, Data, IsReportable, Log) ->
#v3_hdr{msgID = MsgID, msgSecurityModel = MsgSecModel} = V3Hdr,
Pdu = get_scoped_pdu(Data),
case generate_v3_report_msg(MsgID, MsgSecModel, Pdu,
- ErrorInfo, Log) of
+ LocalEngineID, ErrorInfo, Log) of
{ok, Report} ->
throw({discarded, {securityError, Reason}, Report});
{discarded, _SomeOtherReason} ->
@@ -545,8 +567,15 @@ get_scoped_pdu(D) ->
generate_response_msg(Vsn, RePdu, Type, ACMData, Log) ->
generate_response_msg(Vsn, RePdu, Type, ACMData, Log, 1).
+generate_response_msg(Vsn, RePdu, Type, ACMData, Log, N) when is_integer(N) ->
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log, N);
+generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log) ->
+ generate_response_msg(Vsn, RePdu, Type, ACMData, LocalEngineID, Log, 1).
+
generate_response_msg(Vsn, RePdu, Type,
{community, _SecModel, Community, _IpUdp},
+ LocalEngineID,
Log, _) ->
case catch snmp_pdus:enc_pdu(RePdu) of
{'EXIT', Reason} ->
@@ -555,8 +584,9 @@ generate_response_msg(Vsn, RePdu, Type,
[RePdu, Community, Reason]),
{discarded, Reason};
PduBytes ->
- Message = #message{version = Vsn, vsn_hdr = Community,
- data = PduBytes},
+ Message = #message{version = Vsn,
+ vsn_hdr = Community,
+ data = PduBytes},
case catch list_to_binary(
snmp_pdus:enc_message_only(Message)) of
{'EXIT', Reason} ->
@@ -565,7 +595,7 @@ generate_response_msg(Vsn, RePdu, Type,
[RePdu, Community, Reason]),
{discarded, Reason};
Packet ->
- MMS = snmp_framework_mib:get_engine_max_message_size(),
+ MMS = get_engine_max_message_size(LocalEngineID),
case size(Packet) of
Len when Len =< MMS ->
Log(Type, Packet),
@@ -584,6 +614,7 @@ generate_response_msg(Vsn, RePdu, Type,
generate_response_msg(Vsn, RePdu, Type,
{v3, MsgID, MsgSecurityModel, SecName, SecLevel,
ContextEngineID, ContextName, SecData},
+ LocalEngineID,
Log, N) ->
%% rfc2272: 7.1 steps 6-8
ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID,
@@ -596,7 +627,7 @@ generate_response_msg(Vsn, RePdu, Type,
[RePdu, ContextName, Reason]),
{discarded, Reason};
ScopedPDUBytes ->
- AgentMS = snmp_framework_mib:get_engine_max_message_size(),
+ AgentMS = get_engine_max_message_size(LocalEngineID),
V3Hdr = #v3_hdr{msgID = MsgID,
msgMaxSize = AgentMS,
msgFlags = snmp_misc:mk_msg_flags(Type, SecLevel),
@@ -611,13 +642,14 @@ generate_response_msg(Vsn, RePdu, Type,
?SEC_USM ->
snmpa_usm
end,
- SecEngineID = snmp_framework_mib:get_engine_id(),
+ SecEngineID = LocalEngineID,
?vtrace("generate_response_msg -> SecEngineID: ~w", [SecEngineID]),
case (catch SecModule:generate_outgoing_msg(Message,
SecEngineID,
SecName,
SecData,
- SecLevel)) of
+ SecLevel,
+ LocalEngineID)) of
{'EXIT', Reason} ->
config_err("~p (message: ~p)", [Reason, Message]),
{discarded, Reason};
@@ -668,12 +700,14 @@ generate_response_msg(Vsn, RePdu, Type,
SecName, SecLevel,
ContextEngineID,
ContextName,
- SecData}, Log, N+1)
+ SecData},
+ LocalEngineID, Log, N+1)
end
end
end.
-generate_v3_report_msg(MsgID, MsgSecurityModel, Data, ErrorInfo, Log) ->
+generate_v3_report_msg(MsgID, MsgSecurityModel, Data, LocalEngineID,
+ ErrorInfo, Log) ->
{Varbind, SecName, Opts} = ErrorInfo,
ReqId =
if
@@ -689,7 +723,7 @@ generate_v3_report_msg(MsgID, MsgSecurityModel, Data, ErrorInfo, Log) ->
error_index = 0,
varbinds = [Varbind]},
SecLevel = snmp_misc:get_option(securityLevel, Opts, 0),
- SnmpEngineID = snmp_framework_mib:get_engine_id(),
+ SnmpEngineID = LocalEngineID,
ContextEngineID =
snmp_misc:get_option(contextEngineID, Opts, SnmpEngineID),
ContextName = snmp_misc:get_option(contextName, Opts, ""),
@@ -697,7 +731,8 @@ generate_v3_report_msg(MsgID, MsgSecurityModel, Data, ErrorInfo, Log) ->
generate_response_msg('version-3', Pdu, report,
{v3, MsgID, MsgSecurityModel, SecName, SecLevel,
- ContextEngineID, ContextName, SecData}, Log).
+ ContextEngineID, ContextName, SecData},
+ LocalEngineID, Log).
%% req_id(#scopedPdu{data = #pdu{request_id = ReqId}}) ->
%% ?vtrace("Report ReqId: ~p",[ReqId]),
@@ -719,7 +754,8 @@ generate_discovery1_report_msg(MsgID, MsgSecurityModel,
SecName, SecLevel,
ContextEngineID, ContextName,
{SecData, Oid, Value},
- #pdu{request_id = ReqId}, Log) ->
+ #pdu{request_id = ReqId},
+ LocalEngineID, Log) ->
?vtrace("generate_discovery1_report_msg -> entry with"
"~n ReqId: ~p"
"~n Value: ~p", [ReqId, Value]),
@@ -734,7 +770,8 @@ generate_discovery1_report_msg(MsgID, MsgSecurityModel,
varbinds = [Varbind]},
case generate_response_msg('version-3', PduOut, report,
{v3, MsgID, MsgSecurityModel, SecName, SecLevel,
- ContextEngineID, ContextName, SecData}, Log) of
+ ContextEngineID, ContextName, SecData},
+ LocalEngineID, Log) of
{ok, Packet} ->
{discovery, Packet};
Error ->
@@ -745,7 +782,8 @@ generate_discovery1_report_msg(MsgID, MsgSecurityModel,
generate_discovery2_report_msg(MsgID, MsgSecurityModel,
SecName, SecLevel,
ContextEngineID, ContextName,
- SecData, #pdu{request_id = ReqId}, Log) ->
+ SecData, #pdu{request_id = ReqId},
+ LocalEngineID, Log) ->
?vtrace("generate_discovery2_report_msg -> entry with"
"~n ReqId: ~p", [ReqId]),
SecModule = get_security_module(MsgSecurityModel),
@@ -757,7 +795,8 @@ generate_discovery2_report_msg(MsgID, MsgSecurityModel,
varbinds = [Vb]},
case generate_response_msg('version-3', PduOut, report,
{v3, MsgID, MsgSecurityModel, SecName, SecLevel,
- ContextEngineID, ContextName, SecData}, Log) of
+ ContextEngineID, ContextName, SecData},
+ LocalEngineID, Log) of
{ok, Packet} ->
{discovery, Packet};
Error ->
@@ -816,7 +855,11 @@ set_vb_null([]) ->
%% Executed when a message that isn't a response is generated, i.e.
%% a trap or an inform.
%%-----------------------------------------------------------------
-generate_msg(Vsn, _NoteStore, Pdu, {community, Community}, To) ->
+generate_msg(Vsn, NoteStore, Pdu, ACMData, To) ->
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ generate_msg(Vsn, NoteStore, Pdu, ACMData, LocalEngineID, To).
+
+generate_msg(Vsn, _NoteStore, Pdu, {community, Community}, LocalEngineID, To) ->
Message = #message{version = Vsn, vsn_hdr = Community, data = Pdu},
case catch list_to_binary(snmp_pdus:enc_message(Message)) of
{'EXIT', Reason} ->
@@ -825,7 +868,7 @@ generate_msg(Vsn, _NoteStore, Pdu, {community, Community}, To) ->
[Pdu, Community, Reason]),
{discarded, Reason};
Packet ->
- AgentMax = snmp_framework_mib:get_engine_max_message_size(),
+ AgentMax = get_engine_max_message_size(LocalEngineID),
case size(Packet) of
Len when Len =< AgentMax ->
{ok, mk_v1_v2_packet_list(To, Packet, Len, Pdu)};
@@ -838,9 +881,9 @@ generate_msg(Vsn, _NoteStore, Pdu, {community, Community}, To) ->
end
end;
generate_msg('version-3', NoteStore, Pdu,
- {v3, ContextEngineID, ContextName}, To) ->
- %% rfc2272: 7.1.6
- ScopedPDU = #scopedPdu{contextEngineID = ContextEngineID,
+ {v3, ContextEngineID, ContextName}, LocalEngineID, To) ->
+ %% rfc2272: 7.1 step 6
+ ScopedPDU = #scopedPdu{contextEngineID = LocalEngineID,
contextName = ContextName,
data = Pdu},
case (catch snmp_pdus:enc_scoped_pdu(ScopedPDU)) of
@@ -851,7 +894,8 @@ generate_msg('version-3', NoteStore, Pdu,
{discarded, Reason};
ScopedPDUBytes ->
{ok, mk_v3_packet_list(NoteStore, To, ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName)}
+ ContextEngineID, ContextName,
+ LocalEngineID)}
end.
@@ -1094,17 +1138,21 @@ mk_msg_flags(PduType, SecLevel) ->
mk_v3_packet_entry(NoteStore, Domain, Addr,
{SecModel, SecName, SecLevel, TargetAddrName},
- ScopedPDUBytes, Pdu, ContextEngineID, ContextName) ->
- %% 7.1.7
- ?vtrace("mk_v3_packet_entry -> entry - 7.1.7", []),
- MsgID = generate_msg_id(),
- PduType = Pdu#pdu.type,
- MsgFlags = mk_msg_flags(PduType, SecLevel),
+ ScopedPDUBytes, Pdu, _ContextEngineID, ContextName,
+ LocalEngineID) ->
+ %% rfc2272 7.1 step 77
+ ?vtrace("mk_v3_packet_entry -> entry - RFC2272-7.1:7", []),
+ MsgVersion = 'version-3', % 7.1:7a
+ MsgID = generate_msg_id(), % 7.1:7b
+ MaxMsgSz = get_max_message_size(), % 7.1:7c
+ PduType = Pdu#pdu.type,
+ MsgFlags = mk_msg_flags(PduType, SecLevel), % 7.1:7d
+ MsgSecModel = SecModel, % 7.1:7e
V3Hdr = #v3_hdr{msgID = MsgID,
- msgMaxSize = get_max_message_size(),
+ msgMaxSize = MaxMsgSz,
msgFlags = MsgFlags,
- msgSecurityModel = SecModel},
- Message = #message{version = 'version-3',
+ msgSecurityModel = MsgSecModel},
+ Message = #message{version = MsgVersion,
vsn_hdr = V3Hdr,
data = ScopedPDUBytes},
SecModule =
@@ -1113,12 +1161,21 @@ mk_v3_packet_entry(NoteStore, Domain, Addr,
snmpa_usm
end,
+ %%
+ %% 7.1:8 - If the PDU is from the Response Class or the Internal Class
+ %% securityEngineID = snmpEngineID (local/source)
+ %% 7.1:9 - If the PDU is from the Unconfirmed Class
+ %% securityEngineID = snmpEngineID (local/source)
+ %% else
+ %% securityEngineID = targetEngineID (remote/destination)
+ %%
+
%% 7.1.9a
?vtrace("mk_v3_packet_entry -> sec engine id - 7.1.9a", []),
SecEngineID =
case PduType of
'snmpv2-trap' ->
- snmp_framework_mib:get_engine_id();
+ LocalEngineID;
_ ->
%% This is the implementation dependent target engine id
%% procedure.
@@ -1141,8 +1198,9 @@ mk_v3_packet_entry(NoteStore, Domain, Addr,
?vdebug("mk_v3_packet_entry -> secEngineID: ~p", [SecEngineID]),
%% 7.1.9b
- case catch SecModule:generate_outgoing_msg(Message, SecEngineID,
- SecName, [], SecLevel) of
+ case (catch SecModule:generate_outgoing_msg(Message, SecEngineID,
+ SecName, [], SecLevel,
+ LocalEngineID)) of
{'EXIT', Reason} ->
config_err("~p (message: ~p)", [Reason, Message]),
skip;
@@ -1169,7 +1227,7 @@ mk_v3_packet_entry(NoteStore, Domain, Addr,
sec_model = SecModel,
sec_name = SecName,
sec_level = SecLevel,
- ctx_engine_id = ContextEngineID,
+ ctx_engine_id = LocalEngineID,
ctx_name = ContextName,
disco = false,
req_id = Pdu#pdu.request_id},
@@ -1180,15 +1238,16 @@ mk_v3_packet_entry(NoteStore, Domain, Addr,
mk_v3_packet_list(NoteStore, To,
- ScopedPDUBytes, Pdu, ContextEngineID, ContextName) ->
+ ScopedPDUBytes, Pdu, ContextEngineID, ContextName,
+ LocalEngineID) ->
mk_v3_packet_list(NoteStore, To,
ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName, []).
+ ContextEngineID, ContextName, LocalEngineID, []).
mk_v3_packet_list(_, [],
_ScopedPDUBytes, _Pdu,
_ContextEngineID, _ContextName,
- Acc) ->
+ _LocalEngineID, Acc) ->
lists:reverse(Acc);
%% This clause is for backward compatibillity reasons
@@ -1196,20 +1255,21 @@ mk_v3_packet_list(_, [],
mk_v3_packet_list(NoteStore,
[{{?snmpUDPDomain, [A,B,C,D,U1,U2]}, SecData} | T],
ScopedPDUBytes, Pdu, ContextEngineID, ContextName,
- Acc) ->
+ LocalEngineID, Acc) ->
case mk_v3_packet_entry(NoteStore,
snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}, SecData,
ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName) of
+ ContextEngineID, ContextName, LocalEngineID) of
skip ->
mk_v3_packet_list(NoteStore, T,
ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName,
+ ContextEngineID, ContextName, LocalEngineID,
Acc);
{ok, Entry} ->
mk_v3_packet_list(NoteStore, T,
ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName, [Entry | Acc])
+ ContextEngineID, ContextName, LocalEngineID,
+ [Entry | Acc])
end;
%% This is the new clause
@@ -1218,11 +1278,11 @@ mk_v3_packet_list(NoteStore,
mk_v3_packet_list(NoteStore,
[{{Domain, Addr}, SecData} | T],
ScopedPDUBytes, Pdu, ContextEngineID, ContextName,
- Acc) ->
+ LocalEngineID, Acc) ->
case mk_v3_packet_entry(NoteStore,
Domain, Addr, SecData,
ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName) of
+ ContextEngineID, ContextName, LocalEngineID) of
skip ->
mk_v3_packet_list(NoteStore, T,
ScopedPDUBytes, Pdu,
@@ -1230,7 +1290,8 @@ mk_v3_packet_list(NoteStore,
{ok, Entry} ->
mk_v3_packet_list(NoteStore, T,
ScopedPDUBytes, Pdu,
- ContextEngineID, ContextName, [Entry | Acc])
+ ContextEngineID, ContextName,
+ LocalEngineID, [Entry | Acc])
end.
@@ -1253,6 +1314,9 @@ gen(Id) ->
get_target_engine_id(TargetAddrName) ->
snmp_target_mib:get_target_engine_id(TargetAddrName).
+get_engine_max_message_size(_LocalEngineID) ->
+ snmp_framework_mib:get_engine_max_message_size().
+
sec_module(?SEC_USM) ->
snmpa_usm.
diff --git a/lib/snmp/src/agent/snmpa_net_if.erl b/lib/snmp/src/agent/snmpa_net_if.erl
index 3a91cf4033..97a7a63dee 100644
--- a/lib/snmp/src/agent/snmpa_net_if.erl
+++ b/lib/snmp/src/agent/snmpa_net_if.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
+%%
%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
-%%
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(snmpa_net_if).
@@ -478,12 +478,13 @@ update_req_counter_outgoing(#state{limit = Limit, rcnt = RCnt} = S,
S#state{rcnt = NewRCnt}.
-maybe_handle_recv(#state{filter = FilterMod} = S,
+maybe_handle_recv(#state{usock = Sock, filter = FilterMod} = S,
Ip, Port, Packet) ->
case (catch FilterMod:accept_recv(Ip, Port)) of
false ->
%% Drop the received packet
inc(netIfMsgInDrops),
+ active_once(Sock),
S;
_ ->
handle_recv(S, Ip, Port, Packet)
diff --git a/lib/snmp/src/agent/snmpa_target_cache.erl b/lib/snmp/src/agent/snmpa_target_cache.erl
index 6fdecacc68..2aa35aa46a 100644
--- a/lib/snmp/src/agent/snmpa_target_cache.erl
+++ b/lib/snmp/src/agent/snmpa_target_cache.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,8 @@
-behaviour(gen_server).
%% External exports
+%% Avoid warning for local function demonitor/1 clashing with autoimported BIF.
+-compile({no_auto_import,[demonitor/1]}).
-export([start_link/2, stop/0, verbosity/1]).
-export([
diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl
index b1096b1135..450cb2e9f4 100644
--- a/lib/snmp/src/agent/snmpa_trap.erl
+++ b/lib/snmp/src/agent/snmpa_trap.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(snmpa_trap).
@@ -23,14 +23,18 @@
%%%-----------------------------------------------------------------
%% External exports
-export([construct_trap/2,
- try_initialise_vars/2, send_trap/6]).
+ try_initialise_vars/2,
+ send_trap/6, send_trap/7]).
-export([send_discovery/5]).
%% Internal exports
--export([init_v2_inform/9, init_v3_inform/9, send_inform/6]).
+-export([init_v2_inform/9,
+ init_v3_inform/9, init_v3_inform/10,
+ send_inform/6]).
-export([init_discovery_inform/12, send_discovery_inform/5]).
-include("snmp_types.hrl").
+-include("snmpa_internal.hrl").
-include("SNMPv2-MIB.hrl").
-include("SNMPv2-TM.hrl").
-include("SNMPv2-TC.hrl").
@@ -331,13 +335,20 @@ make_varbind_list(Varbinds) ->
%% SnmpTargetAddrTable (using the Tag).
%%-----------------------------------------------------------------
send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, NetIf) ->
- (catch do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, NetIf)).
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID, NetIf).
+
+send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, NetIf) ->
+ (catch do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID, NetIf)).
-do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, NetIf) ->
+do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs,
+ LocalEngineID, NetIf) ->
VarbindList = make_varbind_list(Vbs),
Dests = find_dests(NotifyName),
send_trap_pdus(Dests, ContextName, {TrapRec, VarbindList}, [], [], [],
- Recv, NetIf).
+ Recv, LocalEngineID, NetIf).
send_discovery(TargetName, Record, ContextName, Vbs, NetIf) ->
case find_dest(TargetName) of
@@ -619,7 +630,9 @@ send_discovery_inform(Parent, Timeout, Retry, Msg, NetIf) ->
%%-----------------------------------------------------------------
send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel},
Type} | T],
- ContextName,{TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, NetIf) ->
+ ContextName,
+ {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv,
+ LocalEngineID, NetIf) ->
?vdebug("send trap pdus: "
"~n Destination address: ~p"
"~n Target name: ~p"
@@ -634,7 +647,7 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel},
case check_all_varbinds(TrapRec, Vbs, MibView) of
true when MpModel =:= ?MP_V1 ->
?vtrace("send_trap_pdus -> v1 mp model",[]),
- ContextEngineId = snmp_framework_mib:get_engine_id(),
+ ContextEngineId = LocalEngineID,
case snmp_community_mib:vacm2community({SecName,
ContextEngineId,
ContextName},
@@ -644,16 +657,18 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel},
[element(2, DestAddr)]),
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
[{DestAddr, Community} | V1Res],
- V2Res, V3Res, Recv, NetIf);
+ V2Res, V3Res, Recv,
+ LocalEngineID, NetIf);
undefined ->
?vdebug("No community found for v1 dest: ~p",
[element(2, DestAddr)]),
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
- V1Res, V2Res, V3Res, Recv, NetIf)
+ V1Res, V2Res, V3Res, Recv,
+ LocalEngineID, NetIf)
end;
true when MpModel =:= ?MP_V2C ->
?vtrace("send_trap_pdus -> v2c mp model",[]),
- ContextEngineId = snmp_framework_mib:get_engine_id(),
+ ContextEngineId = LocalEngineID,
case snmp_community_mib:vacm2community({SecName,
ContextEngineId,
ContextName},
@@ -664,12 +679,13 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel},
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
V1Res,
[{DestAddr, Community, Type}|V2Res],
- V3Res, Recv, NetIf);
+ V3Res, Recv, LocalEngineID, NetIf);
undefined ->
?vdebug("No community found for v2c dest: ~p",
[element(2, DestAddr)]),
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
- V1Res, V2Res, V3Res, Recv, NetIf)
+ V1Res, V2Res, V3Res, Recv,
+ LocalEngineID, NetIf)
end;
true when MpModel =:= ?MP_V3 ->
?vtrace("send_trap_pdus -> v3 mp model",[]),
@@ -678,18 +694,20 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel},
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
V1Res, V2Res,
[{DestAddr, MsgData, Type} | V3Res],
- Recv, NetIf);
+ Recv, LocalEngineID, NetIf);
true ->
?vlog("bad MpModel ~p for dest ~p",
[MpModel, element(2, DestAddr)]),
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
- V1Res, V2Res, V3Res, Recv, NetIf);
+ V1Res, V2Res, V3Res, Recv,
+ LocalEngineID, NetIf);
_ ->
?vlog("no access for dest: "
"~n ~p in target ~p",
[element(2, DestAddr), TargetName]),
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
- V1Res, V2Res, V3Res, Recv, NetIf)
+ V1Res, V2Res, V3Res, Recv,
+ LocalEngineID, NetIf)
end;
{discarded, Reason} ->
?vlog("mib view error ~p for"
@@ -697,10 +715,10 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel},
"~n SecName: ~w",
[Reason, element(2, DestAddr), SecName]),
send_trap_pdus(T, ContextName, {TrapRec, Vbs},
- V1Res, V2Res, V3Res, Recv, NetIf)
+ V1Res, V2Res, V3Res, Recv, LocalEngineID, NetIf)
end;
send_trap_pdus([], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res,
- Recv, NetIf) ->
+ Recv, LocalEngineID, NetIf) ->
SysUpTime = snmp_standard_mib:sys_up_time(),
?vdebug("send trap pdus with sysUpTime ~p", [SysUpTime]),
InformRecvs = get_inform_recvs(V2Res ++ V3Res),
@@ -708,7 +726,8 @@ send_trap_pdus([], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res,
deliver_recv(Recv, snmp_targets, InformTargets),
send_v1_trap(TrapRec, V1Res, Vbs, NetIf, SysUpTime),
send_v2_trap(TrapRec, V2Res, Vbs, Recv, NetIf, SysUpTime),
- send_v3_trap(TrapRec, V3Res, Vbs, Recv, NetIf, SysUpTime, ContextName).
+ send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, NetIf,
+ SysUpTime, ContextName).
send_v1_trap(_TrapRec, [], _Vbs, _NetIf, _SysUpTime) ->
ok;
@@ -762,21 +781,25 @@ send_v2_trap(TrapRec, V2Res, Vbs, Recv, NetIf, SysUpTime) ->
do_send_v2_trap(TrapRecvs, IVbs, NetIf),
do_send_v2_inform(InformRecvs, IVbs, Recv, NetIf).
-send_v3_trap(_TrapRec, [], _Vbs, _Recv, _NetIf, _SysUpTime, _ContextName) ->
+send_v3_trap(_TrapRec, [], _Vbs, _Recv, _LocalEngineID,
+ _NetIf, _SysUpTime, _ContextName) ->
ok;
-send_v3_trap(TrapRec, V3Res, Vbs, Recv, NetIf, SysUpTime, ContextName) ->
+send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID,
+ NetIf, SysUpTime, ContextName) ->
?vdebug("prepare to send v3 trap",[]),
{_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), % v2 refers to SMIv2;
- TrapRecvs = get_trap_recvs(V3Res), % same SMI for v3
+ TrapRecvs = get_trap_recvs(V3Res), % same SMI for v3
InformRecvs = get_inform_recvs(V3Res),
do_send_v3_trap(TrapRecvs, ContextName, IVbs, NetIf),
- do_send_v3_inform(InformRecvs, ContextName, IVbs, Recv, NetIf).
+ do_send_v3_inform(InformRecvs, ContextName, IVbs, Recv,
+ LocalEngineID, NetIf).
mk_v2_trap(#notification{oid = Oid}, Vbs, SysUpTime) ->
?vtrace("make v2 notification '~p'",[Oid]),
mk_v2_notif(Oid, Vbs, SysUpTime);
-mk_v2_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, Vbs, SysUpTime) ->
+mk_v2_trap(#trap{enterpriseoid = Enter, specificcode = Spec},
+ Vbs, SysUpTime) ->
%% Use alg. in rfc1908 to map a v1 trap to a v2 trap
?vtrace("make v2 trap for '~p' with ~p",[Enter,Spec]),
{Oid,Enterp} =
@@ -845,16 +868,16 @@ do_send_v3_trap(Recvs, ContextName, Vbs, NetIf) ->
end, Recvs),
ok.
-do_send_v3_inform([], _ContextName, _Vbs, _Recv, _NetIf) ->
+do_send_v3_inform([], _ContextName, _Vbs, _Recv, _LocalEngineID, _NetIf) ->
ok;
-do_send_v3_inform(Recvs, ContextName, Vbs, Recv, NetIf) ->
+do_send_v3_inform(Recvs, ContextName, Vbs, Recv, LocalEngineID, NetIf) ->
lists:foreach(
fun({Addr, MsgData, Timeout, Retry}) ->
?vtrace("~n start inform sender to send v3 inform to ~p",
[Addr]),
proc_lib:spawn_link(?MODULE, init_v3_inform,
[{Addr, MsgData}, Timeout, Retry, Vbs,
- Recv, NetIf, ContextName,
+ Recv, LocalEngineID, NetIf, ContextName,
get(verbosity), get(sname)])
end,
Recvs).
@@ -874,7 +897,13 @@ init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community,V,S) ->
%% New process
-init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, ContextName,V,S) ->
+init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, ContextName, V, S) ->
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID,
+ NetIf, ContextName, V, S).
+
+init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID,
+ NetIf, ContextName, V, S) ->
%% Make a new Inform for each recipient; they need unique
%% request-ids!
put(verbosity,V),
@@ -882,7 +911,7 @@ init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, ContextName,V,S) ->
?vdebug("~n starting with timeout = ~p and retry = ~p",
[Timeout,Retry]),
InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), % Yes, v2
- ContextEngineId = snmp_framework_mib:get_engine_id(),
+ ContextEngineId = LocalEngineID,
Msg = {send_pdu_req, 'version-3', InformPdu,
{v3, ContextEngineId, ContextName}, [Addr], self()},
?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf).
diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl
index b94294844b..f35d1f1916 100644
--- a/lib/snmp/src/agent/snmpa_usm.erl
+++ b/lib/snmp/src/agent/snmpa_usm.erl
@@ -18,9 +18,13 @@
%%
-module(snmpa_usm).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([
- process_incoming_msg/4,
- generate_outgoing_msg/5,
+ process_incoming_msg/4, process_incoming_msg/5,
+ generate_outgoing_msg/5, generate_outgoing_msg/6,
generate_discovery_msg/4, generate_discovery_msg/5,
current_statsNotInTimeWindows_vb/0
]).
@@ -33,6 +37,7 @@
-define(VMODULE,"A-USM").
-include("snmp_verbosity.hrl").
+-include("snmpa_internal.hrl").
%%-----------------------------------------------------------------
@@ -58,7 +63,11 @@
%%-----------------------------------------------------------------
process_incoming_msg(Packet, Data, SecParams, SecLevel) ->
- TermDiscoEnabled = is_terminating_discovery_enabled(),
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ process_incoming_msg(Packet, Data, SecParams, SecLevel, LocalEngineID).
+
+process_incoming_msg(Packet, Data, SecParams, SecLevel, LocalEngineID) ->
+ TermDiscoEnabled = is_terminating_discovery_enabled(),
TermTriggerUsername = terminating_trigger_username(),
%% 3.2.1
?vtrace("process_incoming_msg -> check security parms: 3.2.1",[]),
@@ -124,7 +133,7 @@ process_incoming_msg(Packet, Data, SecParams, SecLevel) ->
"~n ~p",[UsmUser]),
DiscoOrPlain = authenticate_incoming(Packet,
UsmSecParams, UsmUser,
- SecLevel),
+ SecLevel, LocalEngineID),
%% 3.2.8
?vtrace("process_incoming_msg -> "
"decrypt scoped data: 3.2.8",[]),
@@ -166,7 +175,8 @@ process_discovery_msg(MsgAuthEngineID, Data, SecLevel) ->
end.
-authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel) ->
+authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel,
+ LocalEngineID) ->
%% 3.2.6
?vtrace("authenticate_incoming -> 3.2.6", []),
AuthProtocol = element(?usmUserAuthProtocol, UsmUser),
@@ -190,7 +200,8 @@ authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel) ->
SecName,
MsgAuthEngineID,
MsgAuthEngineBoots,
- MsgAuthEngineTime) of
+ MsgAuthEngineTime,
+ LocalEngineID) of
discovery ->
discovery;
true ->
@@ -205,15 +216,15 @@ authenticate_incoming(Packet, UsmSecParams, UsmUser, SecLevel) ->
plain
end.
-authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime) ->
+authoritative(SecName, MsgAuthEngineBoots, MsgAuthEngineTime, LocalEngineID) ->
?vtrace("authoritative -> entry with"
"~n SecName: ~p"
"~n MsgAuthEngineBoots: ~p"
"~n MsgAuthEngineTime: ~p",
[SecName, MsgAuthEngineBoots, MsgAuthEngineTime]),
- SnmpEngineBoots = snmp_framework_mib:get_engine_boots(),
+ SnmpEngineBoots = get_local_engine_boots(LocalEngineID),
?vtrace("authoritative -> SnmpEngineBoots: ~p", [SnmpEngineBoots]),
- SnmpEngineTime = snmp_framework_mib:get_engine_time(),
+ SnmpEngineTime = get_local_engine_time(LocalEngineID),
?vtrace("authoritative -> SnmpEngineTime: ~p", [SnmpEngineTime]),
InTimeWindow =
if
@@ -320,11 +331,12 @@ non_authoritative(SecName,
end.
-is_auth(?usmNoAuthProtocol, _, _, _, SecName, _, _, _) -> % 3.2.5
+is_auth(?usmNoAuthProtocol, _, _, _, SecName, _, _, _, _) -> % 3.2.5
error(usmStatsUnsupportedSecLevels,
?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464
is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName,
- MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime) ->
+ MsgAuthEngineID, MsgAuthEngineBoots, MsgAuthEngineTime,
+ LocalEngineID) ->
TermDiscoEnabled = is_terminating_discovery_enabled(),
TermDiscoStage2 = terminating_discovery_stage2(),
IsAuth = auth_in(AuthProtocol, AuthKey, AuthParams, Packet),
@@ -334,7 +346,7 @@ is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName,
%% 3.2.7
?vtrace("is_auth -> "
"retrieve EngineBoots and EngineTime: 3.2.7",[]),
- SnmpEngineID = snmp_framework_mib:get_engine_id(),
+ SnmpEngineID = LocalEngineID,
?vtrace("is_auth -> SnmpEngineID: ~p", [SnmpEngineID]),
case MsgAuthEngineID of
SnmpEngineID when ((MsgAuthEngineBoots =:= 0) andalso
@@ -351,12 +363,14 @@ is_auth(AuthProtocol, AuthKey, AuthParams, Packet, SecName,
%% This will *always* result in the manager *not*
%% beeing in timewindow
authoritative(SecName,
- MsgAuthEngineBoots, MsgAuthEngineTime);
+ MsgAuthEngineBoots, MsgAuthEngineTime,
+ LocalEngineID);
SnmpEngineID -> %% 3.2.7a
?vtrace("is_auth -> we are authoritative: 3.2.7a", []),
authoritative(SecName,
- MsgAuthEngineBoots, MsgAuthEngineTime);
+ MsgAuthEngineBoots, MsgAuthEngineTime,
+ LocalEngineID);
_ -> %% 3.2.7b - we're non-authoritative
?vtrace("is_auth -> we are non-authoritative: 3.2.7b",[]),
@@ -418,12 +432,19 @@ try_decrypt(?usmAesCfb128Protocol,
generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
+ LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID,
+ generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel,
+ LocalEngineID).
+
+generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel,
+ LocalEngineID) ->
%% 3.1.1
?vtrace("generate_outgoing_msg -> [3.1.1] entry with"
- "~n SecEngineID: ~p"
- "~n SecName: ~p"
- "~n SecLevel: ~w",
- [SecEngineID, SecName, SecLevel]),
+ "~n SecEngineID: ~p"
+ "~n SecName: ~p"
+ "~n SecLevel: ~w"
+ "~n LocalEngineID: ~p",
+ [SecEngineID, SecName, SecLevel, LocalEngineID]),
{UserName, AuthProtocol, PrivProtocol, AuthKey, PrivKey} =
case SecData of
[] -> % 3.1.1b
@@ -439,7 +460,7 @@ generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
element(?usmUserPrivKey, User)};
{_, Name,_,_,_,_,_,_,_,_,_,_,_, RowStatus,_,_} ->
?vdebug("generate_outgoing_msg -> "
- "found user ~p with wrong row status: ~p",
+ "found not active user ~p: ~p",
[Name, RowStatus]),
error(unknownSecurityName);
_ ->
@@ -460,7 +481,7 @@ generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
ScopedPduBytes = Message#message.data,
{ScopedPduData, MsgPrivParams} =
encrypt(ScopedPduBytes, PrivProtocol, PrivKey, SecLevel),
- SnmpEngineID = snmp_framework_mib:get_engine_id(),
+ SnmpEngineID = LocalEngineID,
?vtrace("generate_outgoing_msg -> SnmpEngineID: ~p [3.1.6]",
[SnmpEngineID]),
%% 3.1.6
@@ -474,8 +495,8 @@ generate_outgoing_msg(Message, SecEngineID, SecName, SecData, SecLevel) ->
{get_engine_boots(SecEngineID),
get_engine_time(SecEngineID)};
_ ->
- {snmp_framework_mib:get_engine_boots(),
- snmp_framework_mib:get_engine_time()}
+ {get_local_engine_boots(SnmpEngineID),
+ get_local_engine_time(SnmpEngineID)}
end,
%% 3.1.5 - 3.1.7
?vtrace("generate_outgoing_msg -> [3.1.5 - 3.1.7]",[]),
@@ -681,6 +702,19 @@ current_statsNotInTimeWindows_vb() ->
value = get_counter(usmStatsNotInTimeWindows)}.
+
+%%-----------------------------------------------------------------
+%% Future profing...
+%%-----------------------------------------------------------------
+
+get_local_engine_boots(_LocalEngineID) ->
+ snmp_framework_mib:get_engine_boots().
+
+get_local_engine_time(_LocalEngineID) ->
+ snmp_framework_mib:get_engine_time().
+
+
+
%%-----------------------------------------------------------------
%% We cache the local values of all non-auth engines we know.
%% Keep the values in the snmp_agent_table.
diff --git a/lib/snmp/src/agent/snmpa_vacm.erl b/lib/snmp/src/agent/snmpa_vacm.erl
index 2eacea4301..892dc265f1 100644
--- a/lib/snmp/src/agent/snmpa_vacm.erl
+++ b/lib/snmp/src/agent/snmpa_vacm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
-export([get_mib_view/5]).
-export([init/1, init/2, backup/1]).
-export([delete/1, get_row/1, get_next_row/1, insert/1, insert/2,
- dump_table/0]).
+ cleanup/0, dump_table/0]).
-include("SNMPv2-TC.hrl").
-include("SNMP-VIEW-BASED-ACM-MIB.hrl").
@@ -256,6 +256,11 @@ delete(Key) ->
ets:delete(snmpa_vacm, Key),
dump_table().
+
+cleanup() ->
+ ets:delete_all_objects(snmpa_vacm),
+ dump_table().
+
dump_table(true) ->
dump_table();
dump_table(_) ->
diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src
index aa3410fea3..de0e5d6e14 100644
--- a/lib/snmp/src/app/snmp.appup.src
+++ b/lib/snmp/src/app/snmp.appup.src
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -22,84 +22,30 @@
%% ----- U p g r a d e -------------------------------------------------------
[
- {"4.16.1",
- [
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
- {load_module, snmp_pdus, soft_purge, soft_purge, []}
- ]
- },
- {"4.16",
- [
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpm_net_if, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.15",
+ {"4.18",
[
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpm_net_if, {advanced, upgrade_from_pre_4_16},
- soft_purge, soft_purge, [snmpm_config, snmp_log]},
- {update, snmpa_net_if, {advanced, upgrade_from_pre_4_16},
- soft_purge, soft_purge, [snmpa_agent, snmp_log]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
-
- {update, snmpm_config, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.14",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpa_net_if, {advanced, upgrade_from_pre_4_16},
- soft_purge, soft_purge, [snmpa_agent, snmp_log]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
-
- {load_module, snmpm_user, soft_purge, soft_purge, []},
- {load_module, snmpm_user_default, soft_purge, soft_purge, [snmpm_user]},
- {update, snmpm_net_if, {advanced, upgrade_from_pre_4_16},
- soft_purge, soft_purge, [snmpm_config, snmp_log]},
- {update, snmpm_config, soft, soft_purge, soft_purge, []},
- {update, snmpm_server, soft, soft_purge, soft_purge,
- [snmpm_user_default]}
- ]
- },
- {"4.13.5",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa_mib_data, soft_purge, soft_purge, []},
- {load_module, snmpa, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpa_net_if, {advanced, upgrade_from_pre_4_16},
- soft_purge, soft_purge, [snmpa_agent, snmp_log]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
-
- {load_module, snmpm_user, soft_purge, soft_purge, []},
- {load_module, snmpm_user_default, soft_purge, soft_purge, [snmpm_user]},
- {update, snmpm_net_if, {advanced, upgrade_from_pre_4_14},
- soft_purge, soft_purge, [snmpm_config, snmp_log]},
- {update, snmpm_config, soft, soft_purge, soft_purge, []},
- {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_user_default]},
- {add_module, snmpm_net_if_filter},
- {add_module, snmpm_network_interface_filter}
+ {load_module, snmp_misc, soft_purge, soft_purge, []},
+ {load_module, snmpa_vacm, soft_purge, soft_purge, []},
+ {load_module, snmpa, soft_purge, soft_purge,
+ [snmp_community_mib,
+ snmp_framework_mib,
+ snmp_standard_mib,
+ snmp_target_mib,
+ snmp_user_based_sm_mib,
+ snmp_view_based_acm_mib]},
+ {load_module, snmp_community_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_framework_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_standard_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_target_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib, snmpa_vacm]},
+ {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}
]
}
],
@@ -107,86 +53,30 @@
%% ------D o w n g r a d e ---------------------------------------------------
[
- {"4.16.1",
- [
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
- {load_module, snmp_pdus, soft_purge, soft_purge, []}
- ]
- },
- {"4.16",
+ {"4.18",
[
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpm_net_if, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.15",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpa_net_if, {advanced, downgrade_to_pre_4_16},
- soft_purge, soft_purge, [snmpa_agent, snmp_log]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
-
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpm_net_if, {advanced, downgrade_to_pre_4_16},
- soft_purge, soft_purge, [snmpm_config, snmp_log]},
- {update, snmpm_config, soft, soft_purge, soft_purge, []}
- ]
- },
- {"4.14",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpa_net_if, {advanced, downgrade_to_pre_4_16},
- soft_purge, soft_purge, [snmpa_agent, snmp_log]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
-
- {load_module, snmpm_user, soft_purge, soft_purge, []},
- {load_module, snmpm_user_default, soft_purge, soft_purge, [snmpm_user]},
- {update, snmpm_net_if, {advanced, downgrade_to_pre_4_16},
- soft_purge, soft_purge, [snmpm_config, snmp_log]},
- {update, snmpm_config, soft, soft_purge, soft_purge, []},
- {update, snmpm_server, soft, soft_purge, soft_purge,
- [snmpm_user_default]}
- ]
- },
- {"4.13.5",
- [
- {load_module, snmp_pdus, soft_purge, soft_purge, []},
- {load_module, snmpa_mib_data, soft_purge, soft_purge, []},
- {load_module, snmp_config, soft_purge, soft_purge, []},
- {load_module, snmpa, soft_purge, soft_purge, [snmp_log]},
- {load_module, snmp_log, soft_purge, soft_purge, []},
- {load_module, snmpa_general_db, soft_purge, soft_purge, []},
- {update, snmpa_net_if, {advanced, downgrade_to_pre_4_16},
- soft_purge, soft_purge, [snmpa_agent, snmp_log]},
- {update, snmpa_agent, soft, soft_purge, soft_purge, []},
- {load_module, snmpa_usm, soft_purge, soft_purge, [snmp_usm]},
- {load_module, snmp_usm, soft_purge, soft_purge, []},
-
- {load_module, snmpm_user, soft_purge, soft_purge, []},
- {load_module, snmpm_user_default, soft_purge, soft_purge, [snmpm_user]},
- {update, snmpm_net_if, {advanced, downgrade_to_pre_4_14},
- soft_purge, soft_purge, [snmpm_config, snmp_log]},
- {update, snmpm_config, soft, soft_purge, soft_purge, []},
- {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_user_default]},
-
- {remove, {snmpm_net_if_filter, soft_purge, brutal_purge}},
- {remove, {snmpm_network_interface_filter, soft_purge, brutal_purge}}
+ {load_module, snmp_misc, soft_purge, soft_purge, []},
+ {load_module, snmpa_vacm, soft_purge, soft_purge, []},
+ {load_module, snmpa, soft_purge, soft_purge,
+ [snmp_community_mib,
+ snmp_framework_mib,
+ snmp_standard_mib,
+ snmp_target_mib,
+ snmp_user_based_sm_mib,
+ snmp_view_based_acm_mib]},
+ {load_module, snmp_community_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_framework_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_standard_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_target_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib]},
+ {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge,
+ [snmpa_mib_lib, snmpa_vacm]},
+ {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}
]
}
]
diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile
index 4be60e1835..0ceaf276a6 100644
--- a/lib/snmp/src/compile/Makefile
+++ b/lib/snmp/src/compile/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -20,6 +20,7 @@
include $(ERL_TOP)/make/target.mk
EBIN = ../../ebin
+BIN = ../../bin
include $(ERL_TOP)/make/$(TARGET)/otp.mk
@@ -44,9 +45,11 @@ RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN)
include modules.mk
+ESCRIPT_BIN = $(ESCRIPT_SRC:%.src=$(BIN)/%)
+
ERL_FILES = $(MODULES:%=%.erl)
-TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ESCRIPT_BIN)
GENERATED_PARSER = $(PARSER_MODULE:%=%.erl)
@@ -97,8 +100,12 @@ info:
@echo ""
@echo "EBIN: $(EBIN)"
@echo ""
+ @echo "ESCRIPT_SRC: $(ESCRIPT_SRC)"
+ @echo "ESCRIPT_BIN: $(ESCRIPT_BIN)"
+ @echo ""
@echo ""
+
# ----------------------------------------------------
# Special Build Targets
# ----------------------------------------------------
@@ -107,6 +114,7 @@ parser: $(PARSER_TARGET)
$(GENERATED_PARSER): $(PARSER_SRC)
+
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -115,9 +123,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/src
$(INSTALL_DIR) $(RELSYSDIR)/src/compiler
- $(INSTALL_DATA) $(PARSER_SRC) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/compiler
+ $(INSTALL_DATA) $(ESCRIPT_SRC) $(PARSER_SRC) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/compiler
$(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+ $(INSTALL_DIR) $(RELSYSDIR)/bin
+ $(INSTALL_SCRIPT) $(ESCRIPT_BIN) $(RELSYSDIR)/bin
release_docs_spec:
diff --git a/lib/snmp/src/compile/depend.mk b/lib/snmp/src/compile/depend.mk
index 75af1bf293..f7084f8bcd 100644
--- a/lib/snmp/src/compile/depend.mk
+++ b/lib/snmp/src/compile/depend.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -44,3 +44,6 @@ $(EBIN)/snmpc_mib_gram.$(EMULATOR): \
../../include/snmp_types.hrl \
snmpc_mib_gram.erl
+$(BIN)/snmpc: snmpc.src
+ $(PERL) -p -e 's?%VSN%?$(VSN)? ' < $< > $@
+ chmod 755 $@
diff --git a/lib/snmp/src/compile/modules.mk b/lib/snmp/src/compile/modules.mk
index 6365b0e694..399e4f865e 100644
--- a/lib/snmp/src/compile/modules.mk
+++ b/lib/snmp/src/compile/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2009. All Rights Reserved.
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,9 @@ PARSER_SRC = snmpc_mib_gram.yrl
PARSER_MODULE = $(PARSER_SRC:%.yrl=%)
+ESCRIPT_SRC = \
+ snmpc.src
+
MODULES = \
$(PARSER_MODULE) \
snmpc \
diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl
index a7f2cdc2bc..195c238184 100644
--- a/lib/snmp/src/compile/snmpc.erl
+++ b/lib/snmp/src/compile/snmpc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -112,6 +112,8 @@ compile(FileName) ->
%% description
%% reference
%% imports
+%% agent_capabilities
+%% module_compliance
%% module_identity
%% {module, string()}
%% no_defs
@@ -203,6 +205,10 @@ get_options([imports|Opts], Formats, Args) ->
get_options(Opts, ["~n imports"|Formats], Args);
get_options([module_identity|Opts], Formats, Args) ->
get_options(Opts, ["~n module_identity"|Formats], Args);
+get_options([module_compliance|Opts], Formats, Args) ->
+ get_options(Opts, ["~n module_compliance"|Formats], Args);
+get_options([agent_capabilities|Opts], Formats, Args) ->
+ get_options(Opts, ["~n agent_capabilities"|Formats], Args);
get_options([relaxed_row_name_assign_check|Opts], Formats, Args) ->
get_options(Opts, ["~n relaxed_row_name_assign_check"|Formats], Args);
get_options([_|Opts], Formats, Args) ->
@@ -288,6 +294,10 @@ check_options([imports| T]) ->
check_options(T);
check_options([module_identity| T]) ->
check_options(T);
+check_options([module_compliance| T]) ->
+ check_options(T);
+check_options([agent_capabilities| T]) ->
+ check_options(T);
check_options([relaxed_row_name_assign_check| T]) ->
check_options(T);
check_options([{module, M} | T]) when is_atom(M) ->
@@ -315,6 +325,12 @@ get_description(Options) ->
get_reference(Options) ->
get_bool_option(reference, Options).
+get_agent_capabilities(Options) ->
+ get_bool_option(agent_capabilities, Options).
+
+get_module_compliance(Options) ->
+ get_bool_option(module_compliance, Options).
+
get_relaxed_row_name_assign_check(Options) ->
lists:member(relaxed_row_name_assign_check, Options).
@@ -387,10 +403,12 @@ get_verbosity(Options) ->
init(From, MibFileName, Options) ->
{A,B,C} = now(),
random:seed(A,B,C),
- put(options, Options),
- put(verbosity, get_verbosity(Options)),
- put(description, get_description(Options)),
- put(reference, get_reference(Options)),
+ put(options, Options),
+ put(verbosity, get_verbosity(Options)),
+ put(description, get_description(Options)),
+ put(reference, get_reference(Options)),
+ put(agent_capabilities, get_agent_capabilities(Options)),
+ put(module_compliance, get_module_compliance(Options)),
File = filename:rootname(MibFileName, ".mib"),
put(filename, filename:basename(File ++ ".mib")),
R = case catch c_impl(File) of
@@ -876,12 +894,12 @@ definitions_loop([{#mc_object_type{name = NameOfEntry,
definitions_loop([{#mc_notification{name = TrapName,
status = deprecated}, Line}|T],
- false) ->
+ #dldata{deprecated = false} = Data) ->
?vinfo2("defloop -> notification ~w is deprecated => ignored",
[TrapName], Line),
update_status(TrapName, deprecated),
ensure_macro_imported('NOTIFICATION-TYPE', Line),
- definitions_loop(T, false);
+ definitions_loop(T, Data);
definitions_loop([{#mc_notification{name = TrapName,
status = obsolete}, Line}|T],
@@ -921,10 +939,96 @@ definitions_loop([{#mc_notification{name = TrapName,
snmpc_lib:add_cdata(#cdata.traps, [Notif]),
definitions_loop(T, Data);
-definitions_loop([{#mc_module_compliance{name = Name},Line}|T], Data) ->
- ?vlog2("defloop -> module_compliance:"
- "~n Name: ~p", [Name], Line),
+definitions_loop([{#mc_agent_capabilities{name = Name,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ modules = Mods,
+ name_assign = {Parent, SubIdx}},Line}|T], Data) ->
+ ?vlog2("defloop -> agent_capabilities ~p:"
+ "~n Status: ~p"
+ "~n Desc: ~p"
+ "~n Ref: ~p"
+ "~n Mods: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [Name, Status, Desc, Ref, Mods, Parent, SubIdx], Line),
+ ensure_macro_imported('AGENT-CAPABILITIES', Line),
+ case get(agent_capabilities) of
+ true ->
+ update_status(Name, Status),
+ snmpc_lib:register_oid(Line, Name, Parent, SubIdx),
+ NewME = snmpc_lib:makeInternalNode2(false, Name),
+ Description = make_description(Desc),
+ Reference =
+ case Ref of
+ undefined ->
+ [];
+ _ ->
+ [{reference, Ref}]
+ end,
+ Modules =
+ case Mods of
+ undefined ->
+ [];
+ [] ->
+ [];
+ _ ->
+ [{modules, Mods}]
+ end,
+ AssocList = Reference ++ Modules,
+ NewME2 = NewME#me{description = Description,
+ assocList = AssocList},
+ snmpc_lib:add_cdata(#cdata.mes, [NewME2]);
+ _ ->
+ ok
+ end,
+ definitions_loop(T, Data);
+
+definitions_loop([{#mc_module_compliance{name = Name,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ modules = Mods,
+ name_assign = {Parent, SubIdx}},Line}|T], Data) ->
+ ?vlog2("defloop -> module_compliance: ~p"
+ "~n Status: ~p"
+ "~n Desc: ~p"
+ "~n Ref: ~p"
+ "~n Mods: ~p"
+ "~n Parent: ~p"
+ "~n SubIndex: ~p",
+ [Name, Status, Desc, Ref, Mods, Parent, SubIdx], Line),
ensure_macro_imported('MODULE-COMPLIANCE', Line),
+ case get(module_compliance) of
+ true ->
+ update_status(Name, Status),
+ snmpc_lib:register_oid(Line, Name, Parent, SubIdx),
+ NewME = snmpc_lib:makeInternalNode2(false, Name),
+ Description = make_description(Desc),
+ Reference =
+ case Ref of
+ undefined ->
+ [];
+ _ ->
+ [{reference, Ref}]
+ end,
+ Modules =
+ case Mods of
+ undefined ->
+ [];
+ [] ->
+ [];
+ _ ->
+ [{modules, Mods}]
+ end,
+ AssocList = Reference ++ Modules,
+ NewME2 = NewME#me{description = Description,
+ assocList = AssocList},
+ snmpc_lib:add_cdata(#cdata.mes, [NewME2]);
+ _ ->
+ ok
+ end,
definitions_loop(T, Data);
definitions_loop([{#mc_object_group{name = Name,
@@ -1328,22 +1432,26 @@ save(Filename, MibName, Options) ->
parse(FileName) ->
+%% ?vtrace("parse -> start tokenizer for ~p", [FileName]),
case snmpc_tok:start_link(reserved_words(),
[{file, FileName ++ ".mib"},
{forget_stringdata, true}]) of
{error,ReasonStr} ->
snmpc_lib:error(lists:flatten(ReasonStr),[]);
{ok, TokPid} ->
+%% ?vtrace("parse -> tokenizer start, now get tokens", []),
Toks = snmpc_tok:get_all_tokens(TokPid),
+%% ?vtrace("parse -> tokens: ~p", [Toks]),
set_version(Toks),
- %% io:format("parse -> lexical analysis: ~n~p~n", [Toks]),
- %% t("parse -> lexical analysis: ~n~p", [Toks]),
+ %% ?vtrace("parse -> lexical analysis: ~n~p", [Toks]),
CDataArg =
case lists:keysearch(module, 1, get(options)) of
{value, {module, M}} -> {module, M};
_ -> {file, FileName ++ ".funcs"}
end,
put(cdata,snmpc_lib:make_cdata(CDataArg)),
+%% ?vtrace("parse -> stop tokenizer and then do the actual parse",
+%% []),
snmpc_tok:stop(TokPid),
Res = if
is_list(Toks) ->
@@ -1351,7 +1459,7 @@ parse(FileName) ->
true ->
Toks
end,
- %% t("parse -> parsed: ~n~p", [Res]),
+%% ?vtrace("parse -> parsed result: ~n~p", [Res]),
case Res of
{ok, PData} ->
{ok, PData};
@@ -1443,6 +1551,10 @@ reserved_words() ->
'NOTIFICATION-GROUP',
'NOTIFICATIONS',
'MODULE-COMPLIANCE',
+ 'AGENT-CAPABILITIES',
+ 'PRODUCT-RELEASE',
+ 'SUPPORTS',
+ 'INCLUDES',
'MODULE',
'MANDATORY-GROUPS',
'GROUP',
diff --git a/lib/snmp/src/compile/snmpc.hrl b/lib/snmp/src/compile/snmpc.hrl
index eb896cde6b..1c0808d065 100644
--- a/lib/snmp/src/compile/snmpc.hrl
+++ b/lib/snmp/src/compile/snmpc.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -103,16 +103,75 @@
).
+-record(mc_agent_capabilities,
+ {name,
+ product_release,
+ status,
+ description,
+ reference,
+ modules,
+ name_assign
+ }
+ ).
+
+-record(mc_ac_module,
+ {name,
+ groups,
+ variation
+ }
+ ).
+
+-record(mc_ac_object_variation,
+ {name,
+ syntax,
+ write_syntax,
+ access,
+ creation,
+ default_value,
+ description
+ }
+ ).
+
+-record(mc_ac_notification_variation,
+ {name,
+ access,
+ description
+ }
+ ).
+
+
-record(mc_module_compliance,
{name,
status,
description,
reference,
- module,
+ modules,
name_assign
}
).
+-record(mc_mc_compliance_group,
+ {name,
+ description
+ }
+ ).
+
+-record(mc_mc_object,
+ {name,
+ syntax,
+ write_syntax,
+ access,
+ description
+ }
+ ).
+
+-record(mc_mc_module,
+ {name,
+ mandatory,
+ compliance
+ }
+ ).
+
-record(mc_object_group,
{name,
diff --git a/lib/snmp/src/compile/snmpc.src b/lib/snmp/src/compile/snmpc.src
new file mode 100644
index 0000000000..5f9b154bfa
--- /dev/null
+++ b/lib/snmp/src/compile/snmpc.src
@@ -0,0 +1,381 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%% SNMP MIB compiler frontend
+%%
+
+-mode(compile).
+
+-include_lib("kernel/include/file.hrl").
+-include_lib("snmp/include/snmp_types.hrl").
+
+
+-record(state,
+ {
+ version = "%VSN%",
+ mfv,
+ file, % .mib or .bin depending on which are compiled
+ outdir = "./",
+ db = volatile,
+ include_dirs = ["./"],
+ include_lib_dirs = [],
+ deprecated = false,
+ group_check = true,
+ description = false,
+ reference = false,
+ imports = false,
+ module_identity = false,
+ module_compliance = false,
+ agent_capabilities = false,
+ module,
+ no_defaults = false,
+ relaxed_row_name_assigne_check = false,
+ %% The default verbosity (silence) will be filled in
+ %% during argument processing.
+ verbosity,
+ warnings = false
+ }).
+
+
+%% ------------------------------------------------------------------------
+%% Valid arguments:
+%% --o Dir [defaults to "./"]
+%% --i Dir [defaults to "./"]
+%% --il Dir
+%% --sgc
+%% --db DB [defaults to volatile]
+%% --dep
+%% --desc
+%% --ref
+%% --imp
+%% --mi
+%% --mc
+%% --ac
+%% --mod Mod
+%% --nd
+%% --rrnac
+%% --version
+%% --verbosity V
+%% --warnings
+main(Args) when is_list(Args) ->
+ case (catch process_args(Args)) of
+ ok ->
+ usage();
+ {ok, State} when is_record(State, state) ->
+ compile(State);
+ {ok, Str} when is_list(Str) ->
+ io:format("~s~n~n", [Str]),
+ halt(1);
+ {error, ReasonStr} ->
+ usage(ReasonStr)
+ end;
+main(_) ->
+ usage().
+
+compile(State) ->
+ %% io:format("snmpc: ~p~n", [State]),
+ case mk_file(State) of
+ {mib, File} ->
+ Options = mk_mib_options(State),
+ case mib2bin(File, Options) of
+ {ok, _BinFileName} ->
+ ok;
+ {error, Reason} ->
+ io:format("ERROR: Failed compiling mib: "
+ "~n ~p~n", [Reason]),
+ halt(1)
+ end;
+ {bin, File} ->
+ Options = mk_hrl_options(State),
+ case bin2hrl(File, Options) of
+ ok ->
+ ok;
+ {error, Reason} ->
+ io:format("ERROR: Failed generating hrl from mib: "
+ "~n ~p~n", [Reason]),
+ halt(1)
+ end
+ end.
+
+mib2bin(MibFileName, Options) ->
+ snmpc:compile(MibFileName, Options).
+
+bin2hrl(BinFileName, {OutDir, Verbosity}) ->
+ MibName = filename:basename(BinFileName),
+ BinFile = BinFileName ++ ".bin",
+ HrlFile = filename:join(OutDir, MibName) ++ ".hrl",
+ put(verbosity, Verbosity),
+ snmpc_mib_to_hrl:convert(BinFile, HrlFile, MibName).
+
+
+mk_file(#state{file = MIB}) ->
+ DirName = filename:dirname(MIB),
+ case filename:extension(MIB) of
+ ".mib" ->
+ BaseName = filename:basename(MIB, ".mib"),
+ {mib, filename:join(DirName, BaseName)};
+ ".bin" ->
+ BaseName = filename:basename(MIB, ".bin"),
+ {bin, filename:join(DirName, BaseName)};
+ BadExt ->
+ e(lists:flatten(io_lib:format("Unsupported file type: ~s", [BadExt])))
+ end.
+
+mk_mib_options(#state{outdir = OutDir,
+ db = DB,
+ include_dirs = IDs,
+ include_lib_dirs = ILDs,
+ deprecated = Dep,
+ group_check = GC,
+ description = Desc,
+ reference = Ref,
+ imports = Imp,
+ module_identity = MI,
+ module_compliance = MC,
+ agent_capabilities = AC,
+ module = Mod,
+ no_defaults = ND,
+ relaxed_row_name_assigne_check = RRNAC,
+ %% The default verbosity (silence) will be filled in
+ %% during argument processing.
+ verbosity = V,
+ warnings = W}) ->
+ [{outdir, OutDir},
+ {db, DB},
+ {i, IDs},
+ {il, ILDs},
+ {group_check, GC},
+ {verbosity, V},
+ {warnings, W},
+ {deprecated, Dep}] ++
+ if
+ (Mod =/= undefined) ->
+ [{module, Mod}];
+ true ->
+ []
+ end ++
+ maybe_option(ND, no_defs) ++
+ maybe_option(RRNAC, relaxed_row_name_assign_check) ++
+ maybe_option(Desc, description) ++
+ maybe_option(Ref, reference) ++
+ maybe_option(Imp, imports) ++
+ maybe_option(MI, module_identity) ++
+ maybe_option(MC, module_compliance) ++
+ maybe_option(AC, agent_capabilities).
+
+maybe_option(true, Opt) -> [Opt];
+maybe_option(_, _) -> [].
+
+
+mk_hrl_options(#state{outdir = OutDir,
+ verbosity = Verbosity}) ->
+ {OutDir, Verbosity}.
+
+
+process_args([]) ->
+ e("No input file");
+process_args(Args) ->
+ #mib{mib_format_version = MFV} = #mib{},
+ State = #state{},
+ process_args(Args, State#state{mfv = MFV}).
+
+process_args([], #state{verbosity = Verbosity0, file = MIB} = State) ->
+ if
+ (MIB =:= undefined) ->
+ e("No input file");
+ true ->
+ Verbosity =
+ case Verbosity0 of
+ undefined ->
+ silence;
+ _ ->
+ Verbosity0
+ end,
+ IPath = lists:reverse(State#state.include_dirs),
+ IlPath = lists:reverse(State#state.include_lib_dirs),
+ {ok, State#state{verbosity = Verbosity,
+ include_dirs = IPath,
+ include_lib_dirs = IlPath}}
+ end;
+process_args(["--help"|_Args], _State) ->
+ ok;
+process_args(["--version"|_Args], #state{version = Version, mfv = MFV} = _State) ->
+ {ok, lists:flatten(io_lib:format("snmpc ~s (~s)", [Version, MFV]))};
+process_args(["--verbosity", Verbosity0|Args], #state{verbosity = V} = State)
+ when (V =:= undefined) ->
+ Verbosity = list_to_atom(Verbosity0),
+ case lists:member(Verbosity, [trace,debug,log,info,silence]) of
+ true ->
+ process_args(Args, State#state{verbosity = Verbosity});
+ false ->
+ e(lists:flatten(io_lib:format("Unknown verbosity: ~s", [Verbosity0])))
+ end;
+process_args(["--verbosity"|_Args], #state{verbosity = V})
+ when (V =/= undefined) ->
+ e(lists:flatten(io_lib:format("Verbosity already set to ~w", [V])));
+process_args(["--warnings"|Args], State) ->
+ process_args(Args, State#state{warnings = true});
+process_args(["--o", Dir|Args], State) ->
+ case (catch file:read_file_info(Dir)) of
+ {ok, #file_info{type = directory}} ->
+ process_args(Args, State#state{outdir = Dir});
+ {ok, #file_info{type = BadType}} ->
+ e(lists:flatten(io_lib:format("Not a directory: ~p (~w)", [Dir, BadType])));
+ _ ->
+ e(lists:flatten(io_lib:format("Bad directory: ~p", [Dir])))
+ end;
+process_args(["--i", Dir|Args], State) ->
+ case (catch file:read_file_info(Dir)) of
+ {ok, #file_info{type = directory}} ->
+ IPath = [Dir | State#state.include_dirs],
+ process_args(Args, State#state{include_dirs = IPath});
+ {ok, #file_info{type = BadType}} ->
+ e(lists:flatten(io_lib:format("Not a directory: ~p (~w)", [Dir, BadType])));
+ _ ->
+ e(lists:flatten(io_lib:format("Bad directory: ~p", [Dir])))
+ end;
+process_args(["--il", Dir|Args], State) ->
+ case (catch file:read_file_info(Dir)) of
+ {ok, #file_info{type = directory}} ->
+ IlPath = [Dir | State#state.include_lib_dirs],
+ process_args(Args, State#state{include_lib_dirs = IlPath});
+ {ok, #file_info{type = BadType}} ->
+ e(lists:flatten(io_lib:format("Not a directory: ~p (~w)", [Dir, BadType])));
+ _ ->
+ e(lists:flatten(io_lib:format("Bad directory: ~p", [Dir])))
+ end;
+process_args(["--db", DB0|Args], State) ->
+ DB = list_to_atom(DB0),
+ case lists:member(DB, [volatile,persistent,mnesia]) of
+ true ->
+ process_args(Args, State#state{db = DB});
+ false ->
+ e(lists:flatten(io_lib:format("Invalid db: ~s", [DB0])))
+ end;
+process_args(["--dep"|Args], State) ->
+ process_args(Args, State#state{deprecated = true});
+process_args(["--sgc"|Args], State) ->
+ process_args(Args, State#state{group_check = false});
+process_args(["--desc"|Args], State) ->
+ process_args(Args, State#state{description = true});
+process_args(["--ref"|Args], State) ->
+ process_args(Args, State#state{reference = true});
+process_args(["--imp"|Args], State) ->
+ process_args(Args, State#state{imports = true});
+process_args(["--mi"|Args], State) ->
+ process_args(Args, State#state{module_identity = true});
+process_args(["--mod", Module0|Args], #state{module = M} = State)
+ when (M =:= undefined) ->
+ Module = list_to_atom(Module0),
+ process_args(Args, State#state{module = Module});
+process_args(["--mod"|_Args], #state{module = M})
+ when (M =/= undefined) ->
+ e(lists:flatten(io_lib:format("Module already set to ~w", [M])));
+process_args(["--nd"|Args], State) ->
+ process_args(Args, State#state{no_defaults = true});
+process_args(["--rrnac"|Args], State) ->
+ process_args(Args, State#state{relaxed_row_name_assigne_check = true});
+process_args([MIB], State) ->
+ Ext = filename:extension(MIB),
+ if
+ ((Ext =:= ".mib") orelse (Ext =:= ".bin")) ->
+ case (catch file:read_file_info(MIB)) of
+ {ok, #file_info{type = regular}} ->
+ process_args([], State#state{file = MIB});
+ {ok, #file_info{type = BadType}} ->
+ e(lists:flatten(io_lib:format("~s not a file: ~w", [MIB, BadType])));
+ {error, enoent} ->
+ e(lists:flatten(io_lib:format("No such file: ~s", [MIB])));
+ _ ->
+ e(lists:flatten(io_lib:format("Bad file: ~s", [MIB])))
+ end;
+ true ->
+ e(lists:flatten(io_lib:format("Unknown option: ~s", [MIB])))
+ end;
+process_args([Arg|Args], _State) when Args =/= [] ->
+ e(lists:flatten(io_lib:format("Unknown option: ~s", [Arg]))).
+
+usage(ReasonStr) ->
+ io:format("ERROR: ~s~n", [ReasonStr]),
+ usage().
+
+usage() ->
+ io:format("Usage: snmpc [options] MIB.mib|MIB.bin"
+ "~nCompile a MIB (.mib -> .bin) or generate an erlang header "
+ "~nfile from a compiled MIB file (.bin -> .hrl)"
+ "~nOptions:"
+ "~n --help - Prints this info."
+ "~n --version - Prints compiler version."
+ "~n --verbosity <verbosity> - Print debug info."
+ "~n verbosity = trace | debug | log | info | silence"
+ "~n Defaults to silence."
+ "~n --warnings - Print warning messages."
+ "~n --o <output dir> - The output dir."
+ "~n Defaults to current working dir."
+ "~n --i <include dir> - Add this dir to the list of dirs that will be"
+ "~n searched for imported (compiled) MIB files."
+ "~n The current workin dir will always be included. "
+ "~n --il <include_lib dir> - Add this dir to the list of dirs that will be"
+ "~n searched for imported (compiled) MIB files."
+ "~n It assumes that the first element in the dir name"
+ "~n correspond to an OTP application. For example snmp/mibs/"
+ "~n The current workin dir and the <snmp-home>/priv/mibs "
+ "~n are always listed last the includ path. "
+ "~n --db <DB> - Database to used for the default instrumentation."
+ "~n Defaults to volatile."
+ "~n --sgc - This option (skip group check), if present, disables "
+ "~n the \"group check\" of the mib compiler. "
+ "~n That is, should the OBJECT-GROUP and the NOTIFICATION-GROUP "
+ "~n macro(s) be checked for correctness or not. "
+ "~n By default the check is done. "
+ "~n --dep - Keep deprecated definition(s)."
+ "~n If not specified the compiler will ignore"
+ "~n deprecated definitions."
+ "~n --desc - The DESCRIPTION field will be included."
+ "~n --ref - The REFERENCE field will be included."
+ "~n --imp - The IMPORTS field will be included."
+ "~n --mi - The MODULE-IDENTITY field will be included."
+ "~n --mc - The MODULE-COMPLIANCE field will be included."
+ "~n --ac - The AGENT-CAPABILITIES field will be included."
+ "~n --mod <module> - The module which implements all the instrumentation"
+ "~n functions. "
+ "~n The name of all instrumentation functions must"
+ "~n be the same as the corresponding managed object"
+ "~n it implements."
+ "~n --nd - The default instrumentation functions will *not* be used"
+ "~n if a managed object have no instrumentation function. "
+ "~n Instead this will be reported as an error, and the "
+ "~n compilation aborts. "
+ "~n --rrnac - This option, if present, specifies that the row name "
+ "~n assign check shall not be done strictly according to"
+ "~n the SMI (which allows only the value 1). "
+ "~n With this option, all values greater than zero is allowed"
+ "~n (>= 1). This means that the error will be converted to "
+ "~n a warning. "
+ "~n By default it is not included, but if this option is "
+ "~n present it will be. "
+ "~n "
+ "~n", []),
+ halt(1).
+
+
+e(Reason) ->
+ throw({error, Reason}).
+
diff --git a/lib/snmp/src/compile/snmpc_lib.erl b/lib/snmp/src/compile/snmpc_lib.erl
index 4e5bc69f81..4f71c47bfa 100644
--- a/lib/snmp/src/compile/snmpc_lib.erl
+++ b/lib/snmp/src/compile/snmpc_lib.erl
@@ -20,6 +20,8 @@
-module(snmpc_lib).
%% API
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([test_father/4, make_ASN1type/1, import/1, makeInternalNode2/2,
is_consistent/1, resolve_defval/1, make_variable_info/1,
check_trap_name/3, make_table_info/4, get_final_mib/2, set_dir/2,
@@ -304,7 +306,10 @@ import_mib({{'SNMPv2-TC', ImportsFromMib},Line}) ->
Macros = ['TEXTUAL-CONVENTION'],
import_built_in_loop(ImportsFromMib,Nodes,Types,Macros,'SNMPv2-TC',Line);
import_mib({{'SNMPv2-CONF', ImportsFromMib},Line}) ->
- Macros = ['OBJECT-GROUP','NOTIFICATION-GROUP','MODULE-COMPLIANCE'],
+ Macros = ['OBJECT-GROUP',
+ 'NOTIFICATION-GROUP',
+ 'MODULE-COMPLIANCE',
+ 'AGENT-CAPABILITIES'],
import_built_in_loop(ImportsFromMib,[],[],Macros,'SNMPv2-CONF',Line);
import_mib({{'RFC1155-SMI', ImportsFromMib},Line}) ->
Nodes = [makeInternalNode(internet, [1,3,6,1]),
diff --git a/lib/snmp/src/compile/snmpc_mib_gram.yrl b/lib/snmp/src/compile/snmpc_mib_gram.yrl
index 1957f52936..74b9ddaa25 100644
--- a/lib/snmp/src/compile/snmpc_mib_gram.yrl
+++ b/lib/snmp/src/compile/snmpc_mib_gram.yrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -59,6 +59,7 @@ newtypename
objectidentifier
objectname
objecttypev1
+prodrel
range_num
referpart
size
@@ -79,7 +80,7 @@ revisions
listofdefinitionsv2
mibid
last_updated
-oranization
+organization
contact_info
revision
revision_string
@@ -101,19 +102,31 @@ textualconvention
objectgroup
notificationgroup
modulecompliance
-modulepart
-modules
-module
-modulenamepart
-mandatorypart
-compliancepart
-compliances
-compliance
-compliancegroup
-object
+mc_modulepart
+mc_modules
+mc_module
+mc_modulenamepart
+mc_mandatorypart
+mc_compliancepart
+mc_compliances
+mc_compliance
+mc_compliancegroup
+mc_object
+mc_accesspart
+agentcapabilities
+ac_status
+ac_modulepart
+ac_modules
+ac_module
+ac_modulenamepart
+ac_variationpart
+ac_variations
+ac_variation
+ac_accesspart
+ac_access
+ac_creationpart
syntaxpart
writesyntaxpart
-accesspart
fsyntax
defbitsvalue
defbitsnames
@@ -161,6 +174,12 @@ integer variable atom string quote '{' '}' '::=' ':' '=' ',' '.' '(' ')' ';' '|'
'CONTACT-INFO'
'MODULE-IDENTITY'
'NOTIFICATION-TYPE'
+'PRODUCT-RELEASE'
+'AGENT-CAPABILITIES'
+'INCLUDES'
+'SUPPORTS'
+'VARIATION'
+'CREATION-REQUIRES'
'MODULE-COMPLIANCE'
'OBJECT-GROUP'
'NOTIFICATION-GROUP'
@@ -212,8 +231,8 @@ mib -> mibname 'DEFINITIONS' implies 'BEGIN'
defs = Defs}.
v1orv2 -> moduleidentity listofdefinitionsv2 :
- {v2_mib, ['$1'|lists:reverse('$2')]}.
-v1orv2 -> listofdefinitions : {v1_mib, lists:reverse('$1')}.
+ {v2_mib, ['$1'|lreverse(v1orv2_mod, '$2')]}.
+v1orv2 -> listofdefinitions : {v1_mib, lreverse(v1orv2_list, '$1')}.
definition -> objectidentifier : '$1'.
definition -> objecttypev1 : '$1'.
@@ -231,7 +250,7 @@ imports -> imports_from_one_mib : ['$1'].
imports -> imports_from_one_mib imports : ['$1' | '$2'].
imports_from_one_mib -> listofimports 'FROM' variable :
- {{val('$3'), lists:reverse('$1')}, line_of('$2')}.
+ {{val('$3'), lreverse(imports_from_one_mib, '$1')}, line_of('$2')}.
listofimports -> import_stuff : ['$1'].
listofimports -> listofimports ',' import_stuff : ['$3' | '$1'].
@@ -251,6 +270,8 @@ import_stuff -> 'MODULE-IDENTITY'
: ensure_ver(2,'$1'), {builtin, 'MODULE-IDENTITY'}.
import_stuff -> 'NOTIFICATION-TYPE'
: ensure_ver(2,'$1'), {builtin, 'NOTIFICATION-TYPE'}.
+import_stuff -> 'AGENT-CAPABILITIES'
+ : ensure_ver(2,'$1'), {builtin, 'AGENT-CAPABILITIES'}.
import_stuff -> 'MODULE-COMPLIANCE'
: ensure_ver(2,'$1'), {builtin, 'MODULE-COMPLIANCE'}.
import_stuff -> 'NOTIFICATION-GROUP'
@@ -296,7 +317,7 @@ import_stuff -> 'TAddress'
traptype -> objectname 'TRAP-TYPE' 'ENTERPRISE' objectname varpart
description referpart implies integer :
- Trap = make_trap('$1', '$4', lists:reverse('$5'),
+ Trap = make_trap('$1', '$4', lreverse(traptype, '$5'),
'$6', '$7', val('$9')),
{Trap, line_of('$2')}.
@@ -324,7 +345,7 @@ newtype -> newtypename implies syntax :
{NT, line_of('$2')}.
tableentrydefinition -> newtypename implies 'SEQUENCE' '{' fields '}' :
- Seq = make_sequence('$1', lists:reverse('$5')),
+ Seq = make_sequence('$1', lreverse(tableentrydefinition, '$5')),
{Seq, line_of('$3')}.
% returns: list of {<fieldname>, <asn1_type>}
@@ -408,9 +429,9 @@ variables -> variables ',' objectname : ['$3' | '$1'].
implies -> '::=' : '$1'.
implies -> ':' ':' '=' : w("Sloppy asignment on line ~p", [line_of('$1')]), '$1'.
-descriptionfield -> string : lists:reverse(val('$1')).
+descriptionfield -> string : lreverse(descriptionfield, val('$1')).
descriptionfield -> '$empty' : undefined.
-description -> 'DESCRIPTION' string : lists:reverse(val('$2')).
+description -> 'DESCRIPTION' string : lreverse(description, val('$2')).
description -> '$empty' : undefined.
displaypart -> 'DISPLAY-HINT' string : display_hint('$2') .
@@ -418,7 +439,7 @@ displaypart -> '$empty' : undefined .
% returns: {indexes, undefined}
% | {indexes, IndexList} where IndexList is a list of aliasnames.
-indexpartv1 -> 'INDEX' '{' indextypesv1 '}' : {indexes, lists:reverse('$3')}.
+indexpartv1 -> 'INDEX' '{' indextypesv1 '}' : {indexes, lreverse(indexpartv1, '$3')}.
indexpartv1 -> '$empty' : {indexes, undefined}.
indextypesv1 -> indextypev1 : ['$1'].
@@ -436,14 +457,16 @@ parentintegers -> atom '(' integer ')' parentintegers : [val('$3') | '$5'].
defvalpart -> 'DEFVAL' '{' integer '}' : {defval, val('$3')}.
defvalpart -> 'DEFVAL' '{' atom '}' : {defval, val('$3')}.
defvalpart -> 'DEFVAL' '{' '{' defbitsvalue '}' '}' : {defval, '$4'}.
-defvalpart -> 'DEFVAL' '{' quote atom '}'
- : {defval, make_defval_for_string(line_of('$1'), lists:reverse(val('$3')),
- val('$4'))}.
-defvalpart -> 'DEFVAL' '{' quote variable '}'
- : {defval, make_defval_for_string(line_of('$1'), lists:reverse(val('$3')),
- val('$4'))}.
-defvalpart -> 'DEFVAL' '{' string '}'
- : {defval, lists:reverse(val('$3'))}.
+defvalpart -> 'DEFVAL' '{' quote atom '}' :
+ {defval, make_defval_for_string(line_of('$1'),
+ lreverse(defvalpart_quote_atom, val('$3')),
+ val('$4'))}.
+defvalpart -> 'DEFVAL' '{' quote variable '}' :
+ {defval, make_defval_for_string(line_of('$1'),
+ lreverse(defvalpart_quote_variable, val('$3')),
+ val('$4'))}.
+defvalpart -> 'DEFVAL' '{' string '}' :
+ {defval, lreverse(defvalpart_string, val('$3'))}.
defvalpart -> '$empty' : undefined.
defbitsvalue -> defbitsnames : '$1'.
@@ -461,7 +484,7 @@ accessv1 -> atom: accessv1('$1').
statusv1 -> atom : statusv1('$1').
-referpart -> 'REFERENCE' string : lists:reverse(val('$2')).
+referpart -> 'REFERENCE' string : lreverse(referpart, val('$2')).
referpart -> '$empty' : undefined.
@@ -471,7 +494,7 @@ referpart -> '$empty' : undefined.
%%----------------------------------------------------------------------
moduleidentity -> mibid 'MODULE-IDENTITY'
'LAST-UPDATED' last_updated
- 'ORGANIZATION' oranization
+ 'ORGANIZATION' organization
'CONTACT-INFO' contact_info
'DESCRIPTION' descriptionfield
revisionpart nameassign :
@@ -480,20 +503,20 @@ moduleidentity -> mibid 'MODULE-IDENTITY'
{MI, line_of('$2')}.
mibid -> atom : val('$1').
-last_updated -> string : lists:reverse(val('$1')) .
-oranization -> string : lists:reverse(val('$1')) .
-contact_info -> string : lists:reverse(val('$1')) .
+last_updated -> string : lreverse(last_updated, val('$1')) .
+organization -> string : lreverse(organization, val('$1')) .
+contact_info -> string : lreverse(contact_info, val('$1')) .
revisionpart -> '$empty' : [] .
-revisionpart -> revisions : lists:reverse('$1') .
+revisionpart -> revisions : lreverse(revisionpart, '$1') .
revisions -> revision : ['$1'] .
revisions -> revisions revision : ['$2' | '$1'] .
revision -> 'REVISION' revision_string 'DESCRIPTION' revision_desc :
make_revision('$2', '$4') .
-revision_string -> string : lists:reverse(val('$1')) .
-revision_desc -> string : lists:reverse(val('$1')) .
+revision_string -> string : lreverse(revision_string, val('$1')) .
+revision_desc -> string : lreverse(revision_desc, val('$1')) .
definitionv2 -> objectidentifier : '$1'.
definitionv2 -> objecttypev2 : '$1'.
@@ -505,6 +528,7 @@ definitionv2 -> notification : '$1'.
definitionv2 -> objectgroup : '$1'.
definitionv2 -> notificationgroup : '$1'.
definitionv2 -> modulecompliance : '$1'.
+definitionv2 -> agentcapabilities : '$1'.
listofdefinitionsv2 -> '$empty' : [] .
listofdefinitionsv2 -> listofdefinitionsv2 definitionv2 : ['$2' | '$1'].
@@ -535,46 +559,127 @@ notificationgroup -> objectname 'NOTIFICATION-GROUP' 'NOTIFICATIONS' '{'
{NG, line_of('$2')}.
modulecompliance -> objectname 'MODULE-COMPLIANCE' 'STATUS' statusv2
- description referpart modulepart nameassign :
+ description referpart mc_modulepart nameassign :
+%% io:format("modulecompliance -> "
+%% "~n '$1': ~p"
+%% "~n '$4': ~p"
+%% "~n '$5': ~p"
+%% "~n '$6': ~p"
+%% "~n '$7': ~p"
+%% "~n '$8': ~p"
+%% "~n", ['$1', '$4', '$5', '$6', '$7', '$8']),
MC = make_module_compliance('$1', '$4', '$5', '$6',
'$7', '$8'),
+%% io:format("modulecompliance -> "
+%% "~n MC: ~p"
+%% "~n", [MC]),
{MC, line_of('$2')}.
-modulepart -> '$empty'.
-modulepart -> modules.
-modules -> module.
-modules -> modules module.
+agentcapabilities -> objectname 'AGENT-CAPABILITIES'
+ 'PRODUCT-RELEASE' prodrel
+ 'STATUS' ac_status
+ description referpart ac_modulepart nameassign :
+ AC = make_agent_capabilities('$1', '$4', '$6', '$7',
+ '$8', '$9', '$10'),
+ {AC, line_of('$2')}.
+
+prodrel -> string : lreverse(prodrel, val('$1')).
+
+ac_status -> atom : ac_status('$1').
+
+ac_modulepart -> ac_modules :
+ lreverse(ac_modulepart, '$1').
+ac_modulepart -> '$empty' :
+ [].
+
+ac_modules -> ac_module :
+ ['$1'].
+ac_modules -> ac_module ac_modules :
+ ['$1' | '$2'].
+
+ac_module -> 'SUPPORTS' ac_modulenamepart 'INCLUDES' '{' objects '}' ac_variationpart :
+ make_ac_module('$2', '$5', '$7').
+
+ac_modulenamepart -> mibname : '$1'.
+ac_modulenamepart -> '$empty' : undefined.
+
+ac_variationpart -> '$empty' :
+ [].
+ac_variationpart -> ac_variations :
+ lreverse(ac_variationpart, '$1').
+
+ac_variations -> ac_variation :
+ ['$1'].
+ac_variations -> ac_variation ac_variations :
+ ['$1' | '$2'].
+
+%% ac_variation -> ac_objectvariation.
+%% ac_variation -> ac_notificationvariation.
+
+ac_variation -> 'VARIATION' objectname syntaxpart writesyntaxpart ac_accesspart ac_creationpart defvalpart description :
+ make_ac_variation('$2', '$3', '$4', '$5', '$6', '$7', '$8').
+
+ac_accesspart -> 'ACCESS' ac_access : '$2'.
+ac_accesspart -> '$empty' : undefined.
+
+ac_access -> atom: ac_access('$1').
+
+ac_creationpart -> 'CREATION-REQUIRES' '{' objects '}' :
+ lreverse(ac_creationpart, '$3').
+ac_creationpart -> '$empty' :
+ [].
+
+mc_modulepart -> '$empty' :
+ [].
+mc_modulepart -> mc_modules :
+ lreverse(mc_modulepart, '$1').
+
+mc_modules -> mc_module :
+ ['$1'].
+mc_modules -> mc_module mc_modules :
+ ['$1' | '$2'].
-module -> 'MODULE' modulenamepart mandatorypart compliancepart.
+mc_module -> 'MODULE' mc_modulenamepart mc_mandatorypart mc_compliancepart :
+ make_mc_module('$2', '$3', '$4').
-modulenamepart -> mibname.
-modulenamepart -> '$empty'.
+mc_modulenamepart -> mibname : '$1'.
+mc_modulenamepart -> '$empty' : undefined.
-mandatorypart -> 'MANDATORY-GROUPS' '{' objects '}'.
-mandatorypart -> '$empty'.
+mc_mandatorypart -> 'MANDATORY-GROUPS' '{' objects '}' :
+ lreverse(mc_mandatorypart, '$3').
+mc_mandatorypart -> '$empty' :
+ [].
-compliancepart -> compliances.
-compliancepart -> '$empty'.
+mc_compliancepart -> mc_compliances :
+ lreverse(mc_compliancepart, '$1').
+mc_compliancepart -> '$empty' :
+ [].
-compliances -> compliance.
-compliances -> compliances compliance.
+mc_compliances -> mc_compliance :
+ ['$1'].
+mc_compliances -> mc_compliance mc_compliances :
+ ['$1' | '$2'].
-compliance -> compliancegroup.
-compliance -> object.
+mc_compliance -> mc_compliancegroup :
+ '$1'.
+mc_compliance -> mc_object :
+ '$1'.
-compliancegroup -> 'GROUP' objectname description.
+mc_compliancegroup -> 'GROUP' objectname description :
+ make_mc_compliance_group('$2', '$3').
-object -> 'OBJECT' objectname syntaxpart writesyntaxpart accesspart description.
+mc_object -> 'OBJECT' objectname syntaxpart writesyntaxpart mc_accesspart description :
+ make_mc_object('$2', '$3', '$4', '$5', '$6').
-syntaxpart -> 'SYNTAX' syntax.
-syntaxpart -> '$empty'.
+syntaxpart -> 'SYNTAX' syntax : '$2'.
+syntaxpart -> '$empty' : undefined.
-writesyntaxpart -> 'WRITE-SYNTAX' syntax.
-writesyntaxpart -> '$empty'.
+writesyntaxpart -> 'WRITE-SYNTAX' syntax : '$2'.
+writesyntaxpart -> '$empty' : undefined.
-accesspart -> 'MIN-ACCESS' accessv2.
-accesspart -> '$empty'.
+mc_accesspart -> 'MIN-ACCESS' accessv2 : '$2'.
+mc_accesspart -> '$empty' : undefined.
objecttypev2 -> objectname 'OBJECT-TYPE'
'SYNTAX' syntax
@@ -589,7 +694,7 @@ objecttypev2 -> objectname 'OBJECT-TYPE'
'$11', '$12', Kind, '$15'),
{OT, line_of('$2')}.
-indexpartv2 -> 'INDEX' '{' indextypesv2 '}' : {indexes, lists:reverse('$3')}.
+indexpartv2 -> 'INDEX' '{' indextypesv2 '}' : {indexes, lreverse(indexpartv2, '$3')}.
indexpartv2 -> 'AUGMENTS' '{' entry '}' : {augments, '$3'}.
indexpartv2 -> '$empty' : {indexes, undefined}.
@@ -614,7 +719,7 @@ notification -> objectname 'NOTIFICATION-TYPE' objectspart
Not = make_notification('$1','$3','$5', '$7', '$8', '$9'),
{Not, line_of('$2')}.
-objectspart -> 'OBJECTS' '{' objects '}' : lists:reverse('$3').
+objectspart -> 'OBJECTS' '{' objects '}' : lreverse(objectspart, '$3').
objectspart -> '$empty' : [].
objects -> objectname : ['$1'].
@@ -655,6 +760,14 @@ statusv2(Tok) ->
"syntax error before: " ++ atom_to_list(Else))
end.
+ac_status(Tok) ->
+ case val(Tok) of
+ current -> current;
+ obsolete -> obsolete;
+ Else -> return_error(line_of(Tok),
+ "syntax error before: " ++ atom_to_list(Else))
+ end.
+
accessv1(Tok) ->
case val(Tok) of
'read-only' -> 'read-only';
@@ -676,6 +789,18 @@ accessv2(Tok) ->
"syntax error before: " ++ atom_to_list(Else))
end.
+ac_access(Tok) ->
+ case val(Tok) of
+ 'not-implemented' -> 'not-implemented'; % only for notifications
+ 'accessible-for-notify' -> 'accessible-for-notify';
+ 'read-only' -> 'read-only';
+ 'read-write' -> 'read-write';
+ 'read-create' -> 'read-create';
+ 'write-only' -> 'write-only'; % for backward-compatibility only
+ Else -> return_error(line_of(Tok),
+ "syntax error before: " ++ atom_to_list(Else))
+ end.
+
%% ---------------------------------------------------------------------
%% Various basic record build functions
%% ---------------------------------------------------------------------
@@ -744,14 +869,79 @@ make_notification(Name, Vars, Status, Desc, Ref, NA) ->
reference = Ref,
name_assign = NA}.
-make_module_compliance(Name, Status, Desc, Ref, Mod, NA) ->
+make_agent_capabilities(Name, ProdRel, Status, Desc, Ref, Mods, NA) ->
+ #mc_agent_capabilities{name = Name,
+ product_release = ProdRel,
+ status = Status,
+ description = Desc,
+ reference = Ref,
+ modules = Mods,
+ name_assign = NA}.
+
+make_ac_variation(Name,
+ undefined = _Syntax,
+ undefined = _WriteSyntax,
+ Access,
+ undefined = _Creation,
+ undefined = _DefVal,
+ Desc) ->
+%% io:format("make_ac_variation -> entry with"
+%% "~n Name: ~p"
+%% "~n Access: ~p"
+%% "~n Desc: ~p"
+%% "~n", [Name, Access, Desc]),
+ #mc_ac_notification_variation{name = Name,
+ access = Access,
+ description = Desc};
+
+make_ac_variation(Name, Syntax, WriteSyntax, Access, Creation, DefVal, Desc) ->
+%% io:format("make_ac_variation -> entry with"
+%% "~n Name: ~p"
+%% "~n Syntax: ~p"
+%% "~n WriteSyntax: ~p"
+%% "~n Access: ~p"
+%% "~n Creation: ~p"
+%% "~n DefVal: ~p"
+%% "~n Desc: ~p"
+%% "~n", [Name, Syntax, WriteSyntax, Access, Creation, DefVal, Desc]),
+ #mc_ac_object_variation{name = Name,
+ syntax = Syntax,
+ write_syntax = WriteSyntax,
+ access = Access,
+ creation = Creation,
+ default_value = DefVal,
+ description = Desc}.
+
+make_ac_module(Name, Grps, Var) ->
+ #mc_ac_module{name = Name,
+ groups = Grps,
+ variation = Var}.
+
+
+make_module_compliance(Name, Status, Desc, Ref, Mods, NA) ->
#mc_module_compliance{name = Name,
status = Status,
description = Desc,
reference = Ref,
- module = Mod,
+ modules = Mods,
name_assign = NA}.
+make_mc_module(Name, Mand, Compl) ->
+ #mc_mc_module{name = Name,
+ mandatory = Mand,
+ compliance = Compl}.
+
+make_mc_compliance_group(Name, Desc) ->
+ #mc_mc_compliance_group{name = Name,
+ description = Desc}.
+
+make_mc_object(Name, Syntax, WriteSyntax, Access, Desc) ->
+ #mc_mc_object{name = Name,
+ syntax = Syntax,
+ write_syntax = WriteSyntax,
+ access = Access,
+ description = Desc}.
+
make_object_group(Name, Objs, Status, Desc, Ref, NA) ->
#mc_object_group{name = Name,
objects = Objs,
@@ -968,6 +1158,12 @@ filter_v2imports(_,Type) -> {type, Type}.
w(F, A) ->
?vwarning(F, A).
-%i(F, A) ->
-% io:format("~w:" ++ F ++ "~n", [?MODULE|A]).
+lreverse(_Tag, L) when is_list(L) ->
+ lists:reverse(L);
+lreverse(Tag, X) ->
+ exit({bad_list, Tag, X}).
+
+
+%% i(F, A) ->
+%% io:format("~w:" ++ F ++ "~n", [?MODULE|A]).
diff --git a/lib/snmp/src/compile/snmpc_mib_to_hrl.erl b/lib/snmp/src/compile/snmpc_mib_to_hrl.erl
index 07bd29231b..e8c46a0521 100644
--- a/lib/snmp/src/compile/snmpc_mib_to_hrl.erl
+++ b/lib/snmp/src/compile/snmpc_mib_to_hrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,8 @@
-include("snmpc_lib.hrl").
%% External exports
--export([convert/1, compile/3]).
+-export([convert/1, convert/3, compile/3]).
+
%%-----------------------------------------------------------------
%% Func: convert/1
diff --git a/lib/snmp/src/compile/snmpc_tok.erl b/lib/snmp/src/compile/snmpc_tok.erl
index 6b99e7ae43..e238b256d0 100644
--- a/lib/snmp/src/compile/snmpc_tok.erl
+++ b/lib/snmp/src/compile/snmpc_tok.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,6 +37,8 @@
-export([null_get_line/0, format_error/1, terminate/2, handle_call/3, init/1,
test/0]).
+-include("snmpc_lib.hrl").
+
%%----------------------------------------------------------------------
%% Reserved_words: list of KeyWords. Example: ['IF', 'BEGIN', ..., 'GOTO']
@@ -130,6 +132,10 @@ test() ->
'current','deprecated','not-accessible','obsolete',
'read-create','read-only','read-write', 'IMPORTS', 'FROM',
'MODULE-COMPLIANCE',
+ 'AGENT-CAPABILITIES',
+ 'PRODUCT-RELEASE',
+ 'SUPPORTS',
+ 'INCLUDES',
'DisplayString',
'PhysAddress',
'MacAddress',
@@ -225,6 +231,7 @@ get_all_tokens(Str,Toks) ->
case catch tokenise(Str) of
{error, ErrorInfo} -> {error, ErrorInfo};
{Token, RestChars} when is_tuple(Token) ->
+ %% ?vtrace("get_all_tokens -> Token: ~p", [Token]),
get_all_tokens(RestChars, [Token|Toks])
end.
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 141addf440..5b6321b4c3 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,6 +24,8 @@
%%----------------------------------------------------------------------
%% User interface
+%% Avoid warning for local function demonitor/1 clashing with autoimported BIF.
+-compile({no_auto_import,[demonitor/1]}).
-export([
%%
%% Management API
diff --git a/lib/snmp/src/manager/snmpm_conf.erl b/lib/snmp/src/manager/snmpm_conf.erl
index 75f9c09477..e50508c489 100644
--- a/lib/snmp/src/manager/snmpm_conf.erl
+++ b/lib/snmp/src/manager/snmpm_conf.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,6 +21,8 @@
-include_lib("kernel/include/file.hrl").
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([
%% manager.conf
manager_entry/2,
diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl
index b976e8f568..fd6da3e71a 100644
--- a/lib/snmp/src/manager/snmpm_config.erl
+++ b/lib/snmp/src/manager/snmpm_config.erl
@@ -28,6 +28,8 @@
-behaviour(gen_server).
%% External exports
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([start_link/1, stop/0, is_started/0]).
-export([register_user/4, unregister_user/1,
which_users/0,
diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl
index d76ad20051..7712370d28 100644
--- a/lib/snmp/src/manager/snmpm_mpd.erl
+++ b/lib/snmp/src/manager/snmpm_mpd.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -257,11 +257,11 @@ process_v3_msg(NoteStore, Msg, Hdr, Data, Addr, Port, Log) ->
end,
?vlog("7.2.7"
- "~n ContextEngineID: \"~s\" "
+ "~n ContextEngineID: ~p "
"~n context: \"~s\" ",
[CtxEngineID, CtxName]),
if
- SecLevel == 3 -> % encrypted message - log decrypted pdu
+ SecLevel =:= 3 -> % encrypted message - log decrypted pdu
Log({Hdr, ScopedPDUBytes});
true -> % otherwise, log binary
Log(Msg)
@@ -338,7 +338,8 @@ process_v3_msg(NoteStore, Msg, Hdr, Data, Addr, Port, Log) ->
SnmpEngineID = get_engine_id(),
case SecEngineID of
SnmpEngineID -> % 7.2.13.b
- ?vtrace("valid securityEngineID: ~p", [SecEngineID]),
+ ?vtrace("7.2.13d - valid securityEngineID: ~p",
+ [SecEngineID]),
%% 4.2.2.1.1 - we don't handle proxys yet => we only
%% handle CtxEngineID to ourselves
%% Check that we actually know of an agent with this
@@ -353,7 +354,9 @@ process_v3_msg(NoteStore, Msg, Hdr, Data, Addr, Port, Log) ->
{MsgID, MsgSecModel, SecName, SecLevel,
CtxEngineID, CtxName, SecData},
{ok, 'version-3', PDU, PduMMS, ACMData};
- _ ->
+ UnknownEngineID ->
+ ?vtrace("4.2.2.1.2 - UnknownEngineId: ~p",
+ [UnknownEngineID]),
%% 4.2.2.1.2
NIsReportable = snmp_misc:is_reportable_pdu(Type),
Val = inc(snmpUnknownPDUHandlers),
@@ -377,7 +380,8 @@ process_v3_msg(NoteStore, Msg, Hdr, Data, Addr, Port, Log) ->
end
end;
_ -> % 7.2.13.a
- ?vinfo("invalid securityEngineID: ~p",[SecEngineID]),
+ ?vinfo("7.2.13a - invalid securityEngineID: ~p",
+ [SecEngineID]),
discard({badSecurityEngineID, SecEngineID})
end;
diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl
index 30aacc0ec3..d64b5b1d53 100644
--- a/lib/snmp/src/manager/snmpm_server.erl
+++ b/lib/snmp/src/manager/snmpm_server.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -2804,16 +2804,16 @@ agent_data(TargetName, CtxName) ->
agent_data(TargetName, CtxName, Config) ->
case snmpm_config:agent_info(TargetName, all) of
{ok, Info} ->
- {value, {_, Version}} = lists:keysearch(version, 1, Info),
+ Version = agent_data_item(version, Info),
MsgData =
case Version of
v3 ->
DefSecModel = agent_data_item(sec_model, Info),
DefSecName = agent_data_item(sec_name, Info),
DefSecLevel = agent_data_item(sec_level, Info),
-
+
EngineId = agent_data_item(engine_id, Info),
-
+
SecModel = agent_data_item(sec_model,
Config,
DefSecModel),
@@ -2829,7 +2829,7 @@ agent_data(TargetName, CtxName, Config) ->
_ ->
DefComm = agent_data_item(community, Info),
DefSecModel = agent_data_item(sec_model, Info),
-
+
Comm = agent_data_item(community,
Config,
DefComm),
@@ -2848,8 +2848,12 @@ agent_data(TargetName, CtxName, Config) ->
end.
agent_data_item(Item, Info) ->
- {value, {_, Val}} = lists:keysearch(Item, 1, Info),
- Val.
+ case lists:keysearch(Item, 1, Info) of
+ {value, {_, Val}} ->
+ Val;
+ false ->
+ throw({error, {not_found, Item, Info}})
+ end.
agent_data_item(Item, Info, Default) ->
case lists:keysearch(Item, 1, Info) of
diff --git a/lib/snmp/src/manager/snmpm_usm.erl b/lib/snmp/src/manager/snmpm_usm.erl
index 8cb3062d4b..449127844a 100644
--- a/lib/snmp/src/manager/snmpm_usm.erl
+++ b/lib/snmp/src/manager/snmpm_usm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,10 @@
-module(snmpm_usm).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([init/0,
reset/0,
process_incoming_msg/4, generate_outgoing_msg/5]).
diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl
index 63762ac17b..4d2f5d8f92 100644
--- a/lib/snmp/src/misc/snmp_conf.erl
+++ b/lib/snmp/src/misc/snmp_conf.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,6 +23,8 @@
%% External exports
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([read_files/2, read/2]).
%% Basic (type) check functions
diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl
index c066680160..25350e08cb 100644
--- a/lib/snmp/src/misc/snmp_config.erl
+++ b/lib/snmp/src/misc/snmp_config.erl
@@ -22,6 +22,8 @@
-include_lib("kernel/include/file.hrl").
-include("snmp_types.hrl").
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([config/0]).
-export([write_config_file/4, append_config_file/4, read_config_file/3]).
diff --git a/lib/snmp/src/misc/snmp_usm.erl b/lib/snmp/src/misc/snmp_usm.erl
index 3508f9e1c2..df2c1f0b18 100644
--- a/lib/snmp/src/misc/snmp_usm.erl
+++ b/lib/snmp/src/misc/snmp_usm.erl
@@ -19,6 +19,8 @@
-module(snmp_usm).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([passwd2localized_key/3, localize_key/3]).
-export([auth_in/4, auth_out/4, set_msg_auth_params/3]).
-export([des_encrypt/3, des_decrypt/3]).
diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile
index 86af2460f5..b7975024b4 100644
--- a/lib/snmp/test/Makefile
+++ b/lib/snmp/test/Makefile
@@ -227,7 +227,7 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(RELTEST_FILES) $(COVER_SPEC_FILE) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
tar cf - snmp_test_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/snmp/test/klas3.erl b/lib/snmp/test/klas3.erl
index a5ce2af8c5..ec78d19dbb 100644
--- a/lib/snmp/test/klas3.erl
+++ b/lib/snmp/test/klas3.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk
index 6a0c3e9481..eacc749b53 100644
--- a/lib/snmp/test/modules.mk
+++ b/lib/snmp/test/modules.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2004-2010. All Rights Reserved.
+# Copyright Ericsson AB 2004-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -62,6 +62,8 @@ COMPILER_MIB_FILES = \
OTP8574-MIB
MIB_FILES = \
+ AC-TEST-MIB.mib \
+ MC-TEST-MIB.mib \
OLD-SNMPEA-MIB.mib \
OLD-SNMPEA-MIB-v2.mib \
Klas1.mib \
diff --git a/lib/snmp/test/sa.erl b/lib/snmp/test/sa.erl
index ad3ccce08f..fee50c0e8c 100644
--- a/lib/snmp/test/sa.erl
+++ b/lib/snmp/test/sa.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/snmp.cover b/lib/snmp/test/snmp.cover
index 027dce68c1..a2e7dd978f 100644
--- a/lib/snmp/test/snmp.cover
+++ b/lib/snmp/test/snmp.cover
@@ -1,5 +1,7 @@
%% -*- erlang -*-
-{exclude,
+{incl_app,snmp,details}.
+
+{excl_mods,snmp,
[snmp_index,
snmpa_error_io,
snmpa_authentication_service,
diff --git a/lib/snmp/test/snmp.spec b/lib/snmp/test/snmp.spec
index 0af52c139e..88ae0145f0 100644
--- a/lib/snmp/test/snmp.spec
+++ b/lib/snmp/test/snmp.spec
@@ -1 +1 @@
-{topcase, {dir, "../snmp_test"}}.
+{suites,"../snmp_test",all}.
diff --git a/lib/snmp/test/snmp_SUITE.erl b/lib/snmp/test/snmp_SUITE.erl
index f560e36663..b6d72da2fa 100644
--- a/lib/snmp/test/snmp_SUITE.erl
+++ b/lib/snmp/test/snmp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,27 +19,14 @@
-module(snmp_SUITE).
--export([all/1,
- init_per_testcase/2, fin_per_testcase/2
+-export([all/0,
+ suite/0,
+ groups/0,
+ init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2
]).
--export([app/1, compiler/1, misc/1, agent/1, manager/1]).
-
--export([
- app_test/1,
- appup_test/1,
- compiler_test/1,
- conf_test/1,
- pdus_test/1,
- log_test/1,
- note_store_test/1,
- mibs_test/1,
- nfilter_test/1,
- agent_test/1,
- manager_config_test/1,
- manager_user_test/1,
- manager_test/1
- ]).
%%
%% -----
@@ -48,110 +35,60 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
-all(doc) ->
- ["Test suites for the snmp application.",
- "There are eight different sub test-suites."];
-
-all(suite) ->
- [
- app,
- compiler,
- misc,
- agent,
- manager
-
- ].
-
-app(suite) ->
- [
- app_test,
- appup_test
- ].
-
-compiler(suite) ->
- [
- compiler_test
- ].
-
-misc(suite) ->
- [
- conf_test,
- pdus_test,
- log_test,
- note_store_test
- ].
-
-agent(suite) ->
- [
- mibs_test,
- nfilter_test,
- agent_test
- ].
-
-manager(suite) ->
- [
- manager_config_test,
- manager_user_test,
- manager_test
- ].
-
-
-app_test(suite) ->
- [{snmp_app_test, all}].
-
-
-appup_test(suite) ->
- [{snmp_appup_test, all}].
-
-
-compiler_test(suite) ->
- [{snmp_compiler_test, all}].
-
-
-conf_test(suite) ->
- [{snmp_conf_test, all}].
-
-
-pdus_test(suite) ->
- [{snmp_pdus_test, all}].
-
-
-log_test(suite) ->
- [{snmp_log_test, all}].
-
-
-note_store_test(suite) ->
- [{snmp_note_store_test, all}].
-
-
-mibs_test(suite) ->
- [{snmp_agent_mibs_test, all}].
-
-
-nfilter_test(suite) ->
- [{snmp_agent_nfilter_test, all}].
-
-
-agent_test(suite) ->
- [{snmp_agent_test, all}].
-
-
-manager_config_test(suite) ->
- [{snmp_manager_config_test, all}].
-
-
-manager_user_test(suite) ->
- [{snmp_manager_user_test, all}].
+suite() ->
+ [{ct_hooks, [ts_install_cth]}].
+
+all() ->
+ [{group, app},
+ {group, compiler},
+ {group, misc},
+ {group, agent},
+ {group, manager}].
+
+groups() ->
+ [{app, [], [{group, app_test},
+ {group, appup_test}]},
+ {compiler, [], [{group, compiler_test}]},
+ {misc, [], [{group, conf_test},
+ {group, pdus_test},
+ {group, log_test},
+ {group, note_store_test}]},
+ {agent, [], [{group, mibs_test},
+ {group, nfilter_test},
+ {group, agent_test}]},
+ {manager, [], [{group, manager_config_test},
+ {group, manager_user_test},
+ {group, manager_test}]},
+ {app_test, [], [{snmp_app_test, all}]},
+ {appup_test, [], [{snmp_appup_test, all}]},
+ {compiler_test, [], [{snmp_compiler_test, all}]},
+ {conf_test, [], [{snmp_conf_test, all}]},
+ {pdus_test, [], [{snmp_pdus_test, all}]},
+ {log_test, [], [{snmp_log_test, all}]},
+ {note_store_test, [], [{snmp_note_store_test, all}]},
+ {mibs_test, [], [{snmp_agent_mibs_test, all}]},
+ {nfilter_test, [], [{snmp_agent_nfilter_test, all}]},
+ {agent_test, [], [{snmp_agent_test, all}]},
+ {manager_config_test, [], [{snmp_manager_config_test, all}]},
+ {manager_user_test, [], [{snmp_manager_user_test, all}]},
+ {manager_test, [], [{snmp_manager_test, all}]}].
+
+init_per_suite(Config) ->
+ Config.
+end_per_suite(_Config) ->
+ ok.
-manager_test(suite) ->
- [{snmp_manager_test, all}].
+init_per_group(_GroupName, Config) ->
+ Config.
+end_per_group(_GroupName, Config) ->
+ Config.
diff --git a/lib/snmp/test/snmp_agent_bl_test.erl b/lib/snmp/test/snmp_agent_bl_test.erl
index 4608d90201..b17489a755 100644
--- a/lib/snmp/test/snmp_agent_bl_test.erl
+++ b/lib/snmp/test/snmp_agent_bl_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -89,7 +89,7 @@ init_per_testcase(_Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:minutes(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
Config.
diff --git a/lib/snmp/test/snmp_agent_mibs_test.erl b/lib/snmp/test/snmp_agent_mibs_test.erl
index 5f1ff53a79..3e48130fac 100644
--- a/lib/snmp/test/snmp_agent_mibs_test.erl
+++ b/lib/snmp/test/snmp_agent_mibs_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-include_lib("snmp/include/snmp_types.hrl").
-include_lib("snmp/include/SNMP-COMMUNITY-MIB.hrl").
@@ -39,12 +39,12 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2,
- init_all/1, finish_all/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+ init_per_suite/1, end_per_suite/1,
start_and_stop/1,
- size_check/1,
+
size_check_ets/1,
size_check_dets/1,
size_check_mnesia/1,
@@ -58,8 +58,6 @@
%%----------------------------------------------------------------------
%% Internal exports
%%----------------------------------------------------------------------
--export([
- ]).
%%----------------------------------------------------------------------
%% Macros
@@ -100,20 +98,20 @@ init_per_testcase(cache_test, Config) when is_list(Config) ->
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(size_check_dets, Config) when is_list(Config) ->
+end_per_testcase(size_check_dets, Config) when is_list(Config) ->
Dir = ?config(dets_dir, Config),
?line ok = ?DEL_DIR(Dir),
lists:keydelete(dets_dir, 1, Config);
-fin_per_testcase(size_check_mnesia, Config) when is_list(Config) ->
+end_per_testcase(size_check_mnesia, Config) when is_list(Config) ->
mnesia_stop(),
Dir = ?config(mnesia_dir, Config),
?line ok = ?DEL_DIR(Dir),
lists:keydelete(mnesia_dir, 1, Config);
-fin_per_testcase(cache_test, Config) when is_list(Config) ->
+end_per_testcase(cache_test, Config) when is_list(Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
Config;
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
@@ -121,20 +119,25 @@ fin_per_testcase(_Case, Config) when is_list(Config) ->
%% Test case definitions
%%======================================================================
-all(suite) ->
- {conf, init_all, cases(), finish_all}.
+all() ->
+cases().
+
+groups() ->
+ [{size_check, [],
+ [size_check_ets, size_check_dets, size_check_mnesia]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-cases() ->
- [
- start_and_stop,
- load_unload,
- size_check,
- me_lookup,
- which_mib,
- cache_test
- ].
+cases() ->
+[start_and_stop, load_unload, {group, size_check},
+ me_lookup, which_mib, cache_test].
-init_all(Config) when is_list(Config) ->
+init_per_suite(Config) when is_list(Config) ->
%% Data dir points wrong
DataDir0 = ?config(data_dir, Config),
DataDir1 = filename:split(filename:absname(DataDir0)),
@@ -142,7 +145,7 @@ init_all(Config) when is_list(Config) ->
DataDir = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]),
[{snmp_data_dir, DataDir ++ "/"}|Config].
-finish_all(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
lists:keydelete(snmp_data_dir, 1, Config).
@@ -217,12 +220,6 @@ load_unload(Config) when is_list(Config) ->
%% ---------------------------------------------------------------------
-size_check(suite) ->
- [
- size_check_ets,
- size_check_dets,
- size_check_mnesia
- ].
size_check_ets(suite) ->
[];
diff --git a/lib/snmp/test/snmp_agent_ms_test.erl b/lib/snmp/test/snmp_agent_ms_test.erl
index 3a3a790e6a..1f34f1c8d1 100644
--- a/lib/snmp/test/snmp_agent_ms_test.erl
+++ b/lib/snmp/test/snmp_agent_ms_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-define(application, snmp).
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
@@ -83,39 +83,165 @@
_ -> V3
end).
-all(suite) -> {req,
- [mnesia, distribution,
- {local_slave_nodes, 2}, {time, 360}],
- [{conf, init_all, cases(), finish_all}]}.
+all() ->
+[cases()].
+
+groups() ->
+ [{mib_storage, [],
+ [{group, mib_storage_ets}, {group, mib_storage_dets},
+ {group, mib_storage_mnesia},
+ {group, mib_storage_size_check_ets},
+ {group, mib_storage_size_check_dets},
+ {group, mib_storage_size_check_mnesia},
+ {group, mib_storage_varm_dets},
+ {group, mib_storage_varm_mnesia}]},
+ {mib_storage_ets, [], mib_storage_ets_cases()},
+ {mib_storage_dets, [], mib_storage_dets_cases()},
+ {mib_storage_mnesia, [], mib_storage_mnesia_cases()},
+ {mib_storage_size_check_ets, [],
+ mse_size_check_cases()},
+ {mib_storage_size_check_dets, [],
+ msd_size_check_cases()},
+ {mib_storage_size_check_mnesia, [],
+ msm_size_check_cases()},
+ {mib_storage_varm_dets, [],
+ varm_mib_storage_dets_cases()},
+ {mib_storage_varm_mnesia, [],
+ varm_mib_storage_mnesia_cases()},
+ {test_v1, [], v1_cases()}, {test_v2, [], v2_cases()},
+ {test_v1_v2, [], v1_v2_cases()},
+ {test_v3, [], v3_cases()},
+ {test_multi_threaded, [], mt_cases()},
+ {multiple_reqs, [], mul_cases()},
+ {multiple_reqs_2, [], mul_cases_2()},
+ {v2_inform, [], [v2_inform_i]},
+ {v3_security, [],
+ [v3_crypto_basic, v3_md5_auth, v3_sha_auth,
+ v3_des_priv]},
+ {standard_mibs, [],
+ [snmp_standard_mib, snmp_community_mib,
+ snmp_framework_mib, snmp_target_mib,
+ snmp_notification_mib, snmp_view_based_acm_mib]},
+ {standard_mibs_2, [],
+ [snmpv2_mib_2, snmp_community_mib_2,
+ snmp_framework_mib_2, snmp_target_mib_2,
+ snmp_notification_mib_2, snmp_view_based_acm_mib_2]},
+ {standard_mibs_3, [],
+ [snmpv2_mib_3, snmp_framework_mib_3, snmp_mpd_mib_3,
+ snmp_target_mib_3, snmp_notification_mib_3,
+ snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3]},
+ {reported_bugs, [],
+ [otp_1128, otp_1129, otp_1131, otp_1162, otp_1222,
+ otp_1298, otp_1331, otp_1338, otp_1342, otp_2776,
+ otp_2979, otp_3187, otp_3725]},
+ {reported_bugs_2, [],
+ [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
+ otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
+ otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2]},
+ {reported_bugs_3, [],
+ [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
+ otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
+ otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
+ otp_3542]},
+ {tickets, [], [{group, otp_4394}]},
+ {otp_4394, [], [otp_4394_test]}].
+
+init_per_group(otp_4394, Config) ->
+ init_otp_4394(Config);
+init_per_group(v2_inform, Config) ->
+ init_v2_inform(Config);
+init_per_group(multiple_reqs_2, Config) ->
+ init_mul(Config);
+init_per_group(multiple_reqs, Config) ->
+ init_mul(Config);
+init_per_group(test_multi_threaded, Config) ->
+ init_mt(Config);
+init_per_group(test_v3, Config) ->
+ init_v3(Config);
+init_per_group(test_v1_v2, Config) ->
+ init_v1_v2(Config);
+init_per_group(test_v2, Config) ->
+ init_v2(Config);
+init_per_group(test_v1, Config) ->
+ init_v1(Config);
+init_per_group(mib_storage_varm_mnesia, Config) ->
+ init_varm_mib_storage_mnesia(Config);
+init_per_group(mib_storage_varm_dets, Config) ->
+ init_varm_mib_storage_dets(Config);
+init_per_group(mib_storage_size_check_mnesia, Config) ->
+ init_size_check_msm(Config);
+init_per_group(mib_storage_size_check_dets, Config) ->
+ init_size_check_msd(Config);
+init_per_group(mib_storage_size_check_ets, Config) ->
+ init_size_check_mse(Config);
+init_per_group(mib_storage_mnesia, Config) ->
+ init_mib_storage_mnesia(Config);
+init_per_group(mib_storage_dets, Config) ->
+ init_mib_storage_dets(Config);
+init_per_group(mib_storage_ets, Config) ->
+ init_mib_storage_ets(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(otp_4394, Config) ->
+ finish_otp_4394(Config);
+end_per_group(v2_inform, Config) ->
+ finish_v2_inform(Config);
+end_per_group(multiple_reqs_2, Config) ->
+ finish_mul(Config);
+end_per_group(multiple_reqs, Config) ->
+ finish_mul(Config);
+end_per_group(test_multi_threaded, Config) ->
+ finish_mt(Config);
+end_per_group(test_v3, Config) ->
+ finish_v3(Config);
+end_per_group(test_v1_v2, Config) ->
+ finish_v1_v2(Config);
+end_per_group(test_v2, Config) ->
+ finish_v2(Config);
+end_per_group(test_v1, Config) ->
+ finish_v1(Config);
+end_per_group(mib_storage_varm_mnesia, Config) ->
+ finish_varm_mib_storage_mnesia(Config);
+end_per_group(mib_storage_varm_dets, Config) ->
+ finish_varm_mib_storage_dets(Config);
+end_per_group(mib_storage_size_check_mnesia, Config) ->
+ finish_size_check_msm(Config);
+end_per_group(mib_storage_size_check_dets, Config) ->
+ finish_size_check_msd(Config);
+end_per_group(mib_storage_size_check_ets, Config) ->
+ finish_size_check_mse(Config);
+end_per_group(mib_storage_mnesia, Config) ->
+ finish_mib_storage_mnesia(Config);
+end_per_group(mib_storage_dets, Config) ->
+ finish_mib_storage_dets(Config);
+end_per_group(mib_storage_ets, Config) ->
+ finish_mib_storage_ets(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:minutes(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
Config.
-cases() ->
- case ?OSTYPE() of
- vxworks ->
- %% No crypto app, so skip v3 testcases
- [
- app_info,
- test_v1, test_v2, test_v1_v2,
- test_multi_threaded,
- mib_storage,
- tickets];
- _Else ->
- [
- app_info,
- test_v1, test_v2, test_v1_v2, test_v3,
- test_multi_threaded,
- mib_storage,
- tickets
- ]
- end.
+cases() ->
+case ?OSTYPE() of
+ vxworks ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_multi_threaded},
+ {group, mib_storage}, {group, tickets}];
+ _Else ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}]
+end.
%%%-----------------------------------------------------------------
@@ -460,144 +586,56 @@ delete_mib_storage_mnesia_tables() ->
%% <base>, and a second version <base>_2. There may be several
%% versions as well, <base>_N.
%%-----------------------------------------------------------------
-mib_storage(suite) -> [
- mib_storage_ets,
- mib_storage_dets,
- mib_storage_mnesia,
- mib_storage_size_check_ets,
- mib_storage_size_check_dets,
- mib_storage_size_check_mnesia,
- mib_storage_varm_dets,
- mib_storage_varm_mnesia
- ].
-
-mib_storage_ets(suite) -> {req, [], {conf, init_mib_storage_ets,
- mib_storage_ets_cases(),
- finish_mib_storage_ets}}.
-
-mib_storage_dets(suite) -> {req, [], {conf, init_mib_storage_dets,
- mib_storage_dets_cases(),
- finish_mib_storage_dets}}.
-
-mib_storage_mnesia(suite) -> {req, [], {conf, init_mib_storage_mnesia,
- mib_storage_mnesia_cases(),
- finish_mib_storage_mnesia}}.
-
-mib_storage_size_check_ets(suite) ->
- {req, [], {conf,
- init_size_check_mse,
- mse_size_check_cases(),
- finish_size_check_mse}}.
-
-mib_storage_size_check_dets(suite) ->
- {req, [], {conf,
- init_size_check_msd,
- msd_size_check_cases(),
- finish_size_check_msd}}.
-
-mib_storage_size_check_mnesia(suite) ->
- {req, [], {conf,
- init_size_check_msm,
- msm_size_check_cases(),
- finish_size_check_msm}}.
-
-mib_storage_varm_dets(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_dets,
- varm_mib_storage_dets_cases(),
- finish_varm_mib_storage_dets}}.
-
-mib_storage_varm_mnesia(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_mnesia,
- varm_mib_storage_mnesia_cases(),
- finish_varm_mib_storage_mnesia}}.
-
-mib_storage_ets_cases() ->
- [
- mse_simple,
- mse_v1_processing,
- mse_big,
- mse_big2,
- mse_loop_mib,
- mse_api,
- mse_sa_register,
- mse_v1_trap,
- mse_sa_error,
- mse_next_across_sa,
- mse_undo,
- mse_standard_mib,
- mse_community_mib,
- mse_framework_mib,
- mse_target_mib,
- mse_notification_mib,
- mse_view_based_acm_mib,
- mse_sparse_table,
- mse_me_of,
- mse_mib_of].
-
-mib_storage_dets_cases() ->
- [
- msd_simple,
- msd_v1_processing,
- msd_big,
- msd_big2,
- msd_loop_mib,
- msd_api,
- msd_sa_register,
- msd_v1_trap,
- msd_sa_error,
- msd_next_across_sa,
- msd_undo,
- msd_standard_mib,
- msd_community_mib,
- msd_framework_mib,
- msd_target_mib,
- msd_notification_mib,
- msd_view_based_acm_mib,
- msd_sparse_table,
- msd_me_of,
- msd_mib_of
- ].
-
-mib_storage_mnesia_cases() ->
- [
- msm_simple,
- msm_v1_processing,
- msm_big,
- msm_big2,
- msm_loop_mib,
- msm_api,
- msm_sa_register,
- msm_v1_trap,
- msm_sa_error,
- msm_next_across_sa,
- msm_undo,
- msm_standard_mib,
- msm_community_mib,
- msm_framework_mib,
- msm_target_mib,
- msm_notification_mib,
- msm_view_based_acm_mib,
- msm_sparse_table,
- msm_me_of,
- msm_mib_of
- ].
-
-mse_size_check_cases() ->
- [mse_size_check].
-
-msd_size_check_cases() ->
- [msd_size_check].
-
-msm_size_check_cases() ->
- [msm_size_check].
-
-varm_mib_storage_dets_cases() ->
- [msd_varm_mib_start].
-
-varm_mib_storage_mnesia_cases() ->
- [msm_varm_mib_start].
+
+
+
+
+
+
+
+
+
+mib_storage_ets_cases() ->
+[mse_simple, mse_v1_processing, mse_big, mse_big2,
+ mse_loop_mib, mse_api, mse_sa_register, mse_v1_trap,
+ mse_sa_error, mse_next_across_sa, mse_undo,
+ mse_standard_mib, mse_community_mib, mse_framework_mib,
+ mse_target_mib, mse_notification_mib,
+ mse_view_based_acm_mib, mse_sparse_table, mse_me_of,
+ mse_mib_of].
+
+mib_storage_dets_cases() ->
+[msd_simple, msd_v1_processing, msd_big, msd_big2,
+ msd_loop_mib, msd_api, msd_sa_register, msd_v1_trap,
+ msd_sa_error, msd_next_across_sa, msd_undo,
+ msd_standard_mib, msd_community_mib, msd_framework_mib,
+ msd_target_mib, msd_notification_mib,
+ msd_view_based_acm_mib, msd_sparse_table, msd_me_of,
+ msd_mib_of].
+
+mib_storage_mnesia_cases() ->
+[msm_simple, msm_v1_processing, msm_big, msm_big2,
+ msm_loop_mib, msm_api, msm_sa_register, msm_v1_trap,
+ msm_sa_error, msm_next_across_sa, msm_undo,
+ msm_standard_mib, msm_community_mib, msm_framework_mib,
+ msm_target_mib, msm_notification_mib,
+ msm_view_based_acm_mib, msm_sparse_table, msm_me_of,
+ msm_mib_of].
+
+mse_size_check_cases() ->
+[mse_size_check].
+
+msd_size_check_cases() ->
+[msd_size_check].
+
+msm_size_check_cases() ->
+[msm_size_check].
+
+varm_mib_storage_dets_cases() ->
+[msd_varm_mib_start].
+
+varm_mib_storage_mnesia_cases() ->
+[msm_varm_mib_start].
init_mib_storage_ets(Config) when list(Config) ->
?LOG("init_mib_storage_ets -> entry", []),
@@ -1099,20 +1137,14 @@ app_dir(App) ->
end.
-test_v1(suite) -> {req, [], {conf, init_v1, v1_cases(), finish_v1}}.
%v1_cases() -> [loop_mib];
-v1_cases() ->
- [simple,
- db_notify_client,
- v1_processing, big, big2, loop_mib,
- api, subagent, mnesia, multiple_reqs,
- sa_register, v1_trap, sa_error, next_across_sa, undo, reported_bugs,
- standard_mibs, sparse_table, cnt_64,
- opaque,
- % opaque].
-
- change_target_addr_config].
+v1_cases() ->
+[simple, db_notify_client, v1_processing, big, big2,
+ loop_mib, api, subagent, mnesia, {group, multiple_reqs},
+ sa_register, v1_trap, sa_error, next_across_sa, undo,
+ {group, reported_bugs}, {group, standard_mibs},
+ sparse_table, cnt_64, opaque, change_target_addr_config].
init_v1(Config) when list(Config) ->
?line SaNode = ?config(snmp_sa, Config),
@@ -1129,15 +1161,15 @@ finish_v1(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v2(suite) -> {req, [], {conf, init_v2, v2_cases(), finish_v2}}.
%v2_cases() -> [loop_mib_2];
-v2_cases() ->
- [simple_2, v2_processing, big_2, big2_2, loop_mib_2,
- api_2, subagent_2, mnesia_2,
- multiple_reqs_2, sa_register_2, v2_trap, v2_inform, sa_error_2,
- next_across_sa_2, undo_2, reported_bugs_2, standard_mibs_2,
- v2_types, implied, sparse_table_2, cnt_64_2, opaque_2, v2_caps].
+v2_cases() ->
+[simple_2, v2_processing, big_2, big2_2, loop_mib_2,
+ api_2, subagent_2, mnesia_2, {group, multiple_reqs_2},
+ sa_register_2, v2_trap, {group, v2_inform}, sa_error_2,
+ next_across_sa_2, undo_2, {group, reported_bugs_2},
+ {group, standard_mibs_2}, v2_types, implied,
+ sparse_table_2, cnt_64_2, opaque_2, v2_caps].
init_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1154,10 +1186,9 @@ finish_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v1_v2(suite) -> {req, [], {conf, init_v1_v2, v1_v2_cases(), finish_v1_v2}}.
-v1_v2_cases() ->
- [simple_bi].
+v1_v2_cases() ->
+[simple_bi].
init_v1_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1174,16 +1205,16 @@ finish_v1_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v3(suite) -> {req, [], {conf, init_v3, v3_cases(), finish_v3}}.
%v3_cases() -> [loop_mib_3];
-v3_cases() ->
- [simple_3, v3_processing,
- big_3, big2_3, api_3, subagent_3, mnesia_3, loop_mib_3,
- multiple_reqs_3, sa_register_3, v3_trap, v3_inform, sa_error_3,
- next_across_sa_3, undo_3, reported_bugs_3, standard_mibs_3,
- v3_security,
- v2_types_3, implied_3, sparse_table_3, cnt_64_3, opaque_3, v2_caps_3].
+v3_cases() ->
+[simple_3, v3_processing, big_3, big2_3, api_3,
+ subagent_3, mnesia_3, loop_mib_3, multiple_reqs_3,
+ sa_register_3, v3_trap, v3_inform, sa_error_3,
+ next_across_sa_3, undo_3, {group, reported_bugs_3},
+ {group, standard_mibs_3}, {group, v3_security},
+ v2_types_3, implied_3, sparse_table_3, cnt_64_3,
+ opaque_3, v2_caps_3].
init_v3(Config) when list(Config) ->
%% Make sure crypto works, otherwise start_agent will fail
@@ -1221,10 +1252,9 @@ finish_v3(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_multi_threaded(suite) -> {req, [], {conf, init_mt, mt_cases(), finish_mt}}.
-mt_cases() ->
- [multi_threaded, mt_trap].
+mt_cases() ->
+[multi_threaded, mt_trap].
init_mt(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1739,21 +1769,19 @@ mnesia_2(X) -> mnesia(X).
mnesia_3(X) -> mnesia(X).
-multiple_reqs(suite) ->
- {req, [], {conf, init_mul, mul_cases(), finish_mul}}.
-mul_cases() ->
- [mul_get, mul_get_err, mul_next, mul_next_err, mul_set_err].
+mul_cases() ->
+[mul_get, mul_get_err, mul_next, mul_next_err,
+ mul_set_err].
-multiple_reqs_2(suite) ->
- {req, [], {conf, init_mul, mul_cases_2(), finish_mul}}.
multiple_reqs_3(_X) ->
{req, [], {conf, init_mul, mul_cases_3(), finish_mul}}.
-mul_cases_2() ->
- [mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2, mul_set_err_2].
+mul_cases_2() ->
+[mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2,
+ mul_set_err_2].
mul_cases_3() ->
@@ -1939,8 +1967,6 @@ v2_trap(Config) when list(Config) ->
v3_trap(X) ->
v2_trap(X).
-v2_inform(suite) ->
- {req, [], {conf, init_v2_inform, [v2_inform_i], finish_v2_inform}}.
v3_inform(_X) ->
%% v2_inform(X).
@@ -2112,7 +2138,6 @@ v3_processing(Config) when list(Config) ->
%% accomplished by the first inform sent. That one will generate a
%% report, which makes it in sync. The notification-generating
%% application times out, and send again. This time it'll work.
-v3_security(suite) -> [v3_crypto_basic, v3_md5_auth, v3_sha_auth, v3_des_priv].
v3_crypto_basic(suite) -> [];
v3_crypto_basic(_Config) ->
@@ -3591,22 +3616,8 @@ bad_return() ->
%%% Note that many of the functions in the standard mib is
%%% already tested by the normal tests.
%%%-----------------------------------------------------------------
-standard_mibs(suite) ->
- [snmp_standard_mib, snmp_community_mib,
- snmp_framework_mib,
- snmp_target_mib, snmp_notification_mib,
- snmp_view_based_acm_mib].
-
-standard_mibs_2(suite) ->
- [snmpv2_mib_2, snmp_community_mib_2,
- snmp_framework_mib_2,
- snmp_target_mib_2, snmp_notification_mib_2,
- snmp_view_based_acm_mib_2].
-
-standard_mibs_3(suite) ->
- [snmpv2_mib_3,snmp_framework_mib_3, snmp_mpd_mib_3,
- snmp_target_mib_3, snmp_notification_mib_3,
- snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3].
+
+
%%-----------------------------------------------------------------
%% For this test, the agent is configured for v1.
@@ -4527,27 +4538,12 @@ loop_it_2(Oid, N) ->
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
-reported_bugs(suite) ->
- [otp_1128, otp_1129, otp_1131, otp_1162,
- otp_1222, otp_1298, otp_1331, otp_1338,
- otp_1342, otp_2776, otp_2979, otp_3187, otp_3725].
-reported_bugs_2(suite) ->
- [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
- otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
- otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2].
-reported_bugs_3(suite) ->
- [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
- otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
- otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
- otp_3542].
%% These are (ticket) test cases where the initiation has to be done
%% individually.
-tickets(suite) ->
- [otp_4394].
%%-----------------------------------------------------------------
%% Ticket: OTP-1128
@@ -4971,10 +4967,6 @@ otp_3725_test(MaNode) ->
%%-----------------------------------------------------------------
-otp_4394(suite) -> {req, [], {conf,
- init_otp_4394,
- [otp_4394_test],
- finish_otp_4394}}.
init_otp_4394(Config) when list(Config) ->
?DBG("init_otp_4394 -> entry with"
diff --git a/lib/snmp/test/snmp_agent_mt_test.erl b/lib/snmp/test/snmp_agent_mt_test.erl
index 8d5a57f58d..4f125c0017 100644
--- a/lib/snmp/test/snmp_agent_mt_test.erl
+++ b/lib/snmp/test/snmp_agent_mt_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-define(application, snmp).
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
@@ -83,39 +83,165 @@
_ -> V3
end).
-all(suite) -> {req,
- [mnesia, distribution,
- {local_slave_nodes, 2}, {time, 360}],
- [{conf, init_all, cases(), finish_all}]}.
+all() ->
+[cases()].
+
+groups() ->
+ [{mib_storage, [],
+ [{group, mib_storage_ets}, {group, mib_storage_dets},
+ {group, mib_storage_mnesia},
+ {group, mib_storage_size_check_ets},
+ {group, mib_storage_size_check_dets},
+ {group, mib_storage_size_check_mnesia},
+ {group, mib_storage_varm_dets},
+ {group, mib_storage_varm_mnesia}]},
+ {mib_storage_ets, [], mib_storage_ets_cases()},
+ {mib_storage_dets, [], mib_storage_dets_cases()},
+ {mib_storage_mnesia, [], mib_storage_mnesia_cases()},
+ {mib_storage_size_check_ets, [],
+ mse_size_check_cases()},
+ {mib_storage_size_check_dets, [],
+ msd_size_check_cases()},
+ {mib_storage_size_check_mnesia, [],
+ msm_size_check_cases()},
+ {mib_storage_varm_dets, [],
+ varm_mib_storage_dets_cases()},
+ {mib_storage_varm_mnesia, [],
+ varm_mib_storage_mnesia_cases()},
+ {test_v1, [], v1_cases()}, {test_v2, [], v2_cases()},
+ {test_v1_v2, [], v1_v2_cases()},
+ {test_v3, [], v3_cases()},
+ {test_multi_threaded, [], mt_cases()},
+ {multiple_reqs, [], mul_cases()},
+ {multiple_reqs_2, [], mul_cases_2()},
+ {v2_inform, [], [v2_inform_i]},
+ {v3_security, [],
+ [v3_crypto_basic, v3_md5_auth, v3_sha_auth,
+ v3_des_priv]},
+ {standard_mibs, [],
+ [snmp_standard_mib, snmp_community_mib,
+ snmp_framework_mib, snmp_target_mib,
+ snmp_notification_mib, snmp_view_based_acm_mib]},
+ {standard_mibs_2, [],
+ [snmpv2_mib_2, snmp_community_mib_2,
+ snmp_framework_mib_2, snmp_target_mib_2,
+ snmp_notification_mib_2, snmp_view_based_acm_mib_2]},
+ {standard_mibs_3, [],
+ [snmpv2_mib_3, snmp_framework_mib_3, snmp_mpd_mib_3,
+ snmp_target_mib_3, snmp_notification_mib_3,
+ snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3]},
+ {reported_bugs, [],
+ [otp_1128, otp_1129, otp_1131, otp_1162, otp_1222,
+ otp_1298, otp_1331, otp_1338, otp_1342, otp_2776,
+ otp_2979, otp_3187, otp_3725]},
+ {reported_bugs_2, [],
+ [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
+ otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
+ otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2]},
+ {reported_bugs_3, [],
+ [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
+ otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
+ otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
+ otp_3542]},
+ {tickets, [], [{group, otp_4394}]},
+ {otp_4394, [], [otp_4394_test]}].
+
+init_per_group(otp_4394, Config) ->
+ init_otp_4394(Config);
+init_per_group(v2_inform, Config) ->
+ init_v2_inform(Config);
+init_per_group(multiple_reqs_2, Config) ->
+ init_mul(Config);
+init_per_group(multiple_reqs, Config) ->
+ init_mul(Config);
+init_per_group(test_multi_threaded, Config) ->
+ init_mt(Config);
+init_per_group(test_v3, Config) ->
+ init_v3(Config);
+init_per_group(test_v1_v2, Config) ->
+ init_v1_v2(Config);
+init_per_group(test_v2, Config) ->
+ init_v2(Config);
+init_per_group(test_v1, Config) ->
+ init_v1(Config);
+init_per_group(mib_storage_varm_mnesia, Config) ->
+ init_varm_mib_storage_mnesia(Config);
+init_per_group(mib_storage_varm_dets, Config) ->
+ init_varm_mib_storage_dets(Config);
+init_per_group(mib_storage_size_check_mnesia, Config) ->
+ init_size_check_msm(Config);
+init_per_group(mib_storage_size_check_dets, Config) ->
+ init_size_check_msd(Config);
+init_per_group(mib_storage_size_check_ets, Config) ->
+ init_size_check_mse(Config);
+init_per_group(mib_storage_mnesia, Config) ->
+ init_mib_storage_mnesia(Config);
+init_per_group(mib_storage_dets, Config) ->
+ init_mib_storage_dets(Config);
+init_per_group(mib_storage_ets, Config) ->
+ init_mib_storage_ets(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(otp_4394, Config) ->
+ finish_otp_4394(Config);
+end_per_group(v2_inform, Config) ->
+ finish_v2_inform(Config);
+end_per_group(multiple_reqs_2, Config) ->
+ finish_mul(Config);
+end_per_group(multiple_reqs, Config) ->
+ finish_mul(Config);
+end_per_group(test_multi_threaded, Config) ->
+ finish_mt(Config);
+end_per_group(test_v3, Config) ->
+ finish_v3(Config);
+end_per_group(test_v1_v2, Config) ->
+ finish_v1_v2(Config);
+end_per_group(test_v2, Config) ->
+ finish_v2(Config);
+end_per_group(test_v1, Config) ->
+ finish_v1(Config);
+end_per_group(mib_storage_varm_mnesia, Config) ->
+ finish_varm_mib_storage_mnesia(Config);
+end_per_group(mib_storage_varm_dets, Config) ->
+ finish_varm_mib_storage_dets(Config);
+end_per_group(mib_storage_size_check_mnesia, Config) ->
+ finish_size_check_msm(Config);
+end_per_group(mib_storage_size_check_dets, Config) ->
+ finish_size_check_msd(Config);
+end_per_group(mib_storage_size_check_ets, Config) ->
+ finish_size_check_mse(Config);
+end_per_group(mib_storage_mnesia, Config) ->
+ finish_mib_storage_mnesia(Config);
+end_per_group(mib_storage_dets, Config) ->
+ finish_mib_storage_dets(Config);
+end_per_group(mib_storage_ets, Config) ->
+ finish_mib_storage_ets(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:minutes(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
Config.
-cases() ->
- case ?OSTYPE() of
- vxworks ->
- %% No crypto app, so skip v3 testcases
- [
- app_info,
- test_v1, test_v2, test_v1_v2,
- test_multi_threaded,
- mib_storage,
- tickets];
- _Else ->
- [
- app_info,
- test_v1, test_v2, test_v1_v2, test_v3,
- test_multi_threaded,
- mib_storage,
- tickets
- ]
- end.
+cases() ->
+case ?OSTYPE() of
+ vxworks ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_multi_threaded},
+ {group, mib_storage}, {group, tickets}];
+ _Else ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}]
+end.
%%%-----------------------------------------------------------------
@@ -460,144 +586,56 @@ delete_mib_storage_mnesia_tables() ->
%% <base>, and a second version <base>_2. There may be several
%% versions as well, <base>_N.
%%-----------------------------------------------------------------
-mib_storage(suite) -> [
- mib_storage_ets,
- mib_storage_dets,
- mib_storage_mnesia,
- mib_storage_size_check_ets,
- mib_storage_size_check_dets,
- mib_storage_size_check_mnesia,
- mib_storage_varm_dets,
- mib_storage_varm_mnesia
- ].
-
-mib_storage_ets(suite) -> {req, [], {conf, init_mib_storage_ets,
- mib_storage_ets_cases(),
- finish_mib_storage_ets}}.
-
-mib_storage_dets(suite) -> {req, [], {conf, init_mib_storage_dets,
- mib_storage_dets_cases(),
- finish_mib_storage_dets}}.
-
-mib_storage_mnesia(suite) -> {req, [], {conf, init_mib_storage_mnesia,
- mib_storage_mnesia_cases(),
- finish_mib_storage_mnesia}}.
-
-mib_storage_size_check_ets(suite) ->
- {req, [], {conf,
- init_size_check_mse,
- mse_size_check_cases(),
- finish_size_check_mse}}.
-
-mib_storage_size_check_dets(suite) ->
- {req, [], {conf,
- init_size_check_msd,
- msd_size_check_cases(),
- finish_size_check_msd}}.
-
-mib_storage_size_check_mnesia(suite) ->
- {req, [], {conf,
- init_size_check_msm,
- msm_size_check_cases(),
- finish_size_check_msm}}.
-
-mib_storage_varm_dets(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_dets,
- varm_mib_storage_dets_cases(),
- finish_varm_mib_storage_dets}}.
-
-mib_storage_varm_mnesia(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_mnesia,
- varm_mib_storage_mnesia_cases(),
- finish_varm_mib_storage_mnesia}}.
-
-mib_storage_ets_cases() ->
- [
- mse_simple,
- mse_v1_processing,
- mse_big,
- mse_big2,
- mse_loop_mib,
- mse_api,
- mse_sa_register,
- mse_v1_trap,
- mse_sa_error,
- mse_next_across_sa,
- mse_undo,
- mse_standard_mib,
- mse_community_mib,
- mse_framework_mib,
- mse_target_mib,
- mse_notification_mib,
- mse_view_based_acm_mib,
- mse_sparse_table,
- mse_me_of,
- mse_mib_of].
-
-mib_storage_dets_cases() ->
- [
- msd_simple,
- msd_v1_processing,
- msd_big,
- msd_big2,
- msd_loop_mib,
- msd_api,
- msd_sa_register,
- msd_v1_trap,
- msd_sa_error,
- msd_next_across_sa,
- msd_undo,
- msd_standard_mib,
- msd_community_mib,
- msd_framework_mib,
- msd_target_mib,
- msd_notification_mib,
- msd_view_based_acm_mib,
- msd_sparse_table,
- msd_me_of,
- msd_mib_of
- ].
-
-mib_storage_mnesia_cases() ->
- [
- msm_simple,
- msm_v1_processing,
- msm_big,
- msm_big2,
- msm_loop_mib,
- msm_api,
- msm_sa_register,
- msm_v1_trap,
- msm_sa_error,
- msm_next_across_sa,
- msm_undo,
- msm_standard_mib,
- msm_community_mib,
- msm_framework_mib,
- msm_target_mib,
- msm_notification_mib,
- msm_view_based_acm_mib,
- msm_sparse_table,
- msm_me_of,
- msm_mib_of
- ].
-
-mse_size_check_cases() ->
- [mse_size_check].
-
-msd_size_check_cases() ->
- [msd_size_check].
-
-msm_size_check_cases() ->
- [msm_size_check].
-
-varm_mib_storage_dets_cases() ->
- [msd_varm_mib_start].
-
-varm_mib_storage_mnesia_cases() ->
- [msm_varm_mib_start].
+
+
+
+
+
+
+
+
+
+mib_storage_ets_cases() ->
+[mse_simple, mse_v1_processing, mse_big, mse_big2,
+ mse_loop_mib, mse_api, mse_sa_register, mse_v1_trap,
+ mse_sa_error, mse_next_across_sa, mse_undo,
+ mse_standard_mib, mse_community_mib, mse_framework_mib,
+ mse_target_mib, mse_notification_mib,
+ mse_view_based_acm_mib, mse_sparse_table, mse_me_of,
+ mse_mib_of].
+
+mib_storage_dets_cases() ->
+[msd_simple, msd_v1_processing, msd_big, msd_big2,
+ msd_loop_mib, msd_api, msd_sa_register, msd_v1_trap,
+ msd_sa_error, msd_next_across_sa, msd_undo,
+ msd_standard_mib, msd_community_mib, msd_framework_mib,
+ msd_target_mib, msd_notification_mib,
+ msd_view_based_acm_mib, msd_sparse_table, msd_me_of,
+ msd_mib_of].
+
+mib_storage_mnesia_cases() ->
+[msm_simple, msm_v1_processing, msm_big, msm_big2,
+ msm_loop_mib, msm_api, msm_sa_register, msm_v1_trap,
+ msm_sa_error, msm_next_across_sa, msm_undo,
+ msm_standard_mib, msm_community_mib, msm_framework_mib,
+ msm_target_mib, msm_notification_mib,
+ msm_view_based_acm_mib, msm_sparse_table, msm_me_of,
+ msm_mib_of].
+
+mse_size_check_cases() ->
+[mse_size_check].
+
+msd_size_check_cases() ->
+[msd_size_check].
+
+msm_size_check_cases() ->
+[msm_size_check].
+
+varm_mib_storage_dets_cases() ->
+[msd_varm_mib_start].
+
+varm_mib_storage_mnesia_cases() ->
+[msm_varm_mib_start].
init_mib_storage_ets(Config) when list(Config) ->
?LOG("init_mib_storage_ets -> entry", []),
@@ -1099,20 +1137,14 @@ app_dir(App) ->
end.
-test_v1(suite) -> {req, [], {conf, init_v1, v1_cases(), finish_v1}}.
%v1_cases() -> [loop_mib];
-v1_cases() ->
- [simple,
- db_notify_client,
- v1_processing, big, big2, loop_mib,
- api, subagent, mnesia, multiple_reqs,
- sa_register, v1_trap, sa_error, next_across_sa, undo, reported_bugs,
- standard_mibs, sparse_table, cnt_64,
- opaque,
- % opaque].
-
- change_target_addr_config].
+v1_cases() ->
+[simple, db_notify_client, v1_processing, big, big2,
+ loop_mib, api, subagent, mnesia, {group, multiple_reqs},
+ sa_register, v1_trap, sa_error, next_across_sa, undo,
+ {group, reported_bugs}, {group, standard_mibs},
+ sparse_table, cnt_64, opaque, change_target_addr_config].
init_v1(Config) when list(Config) ->
?line SaNode = ?config(snmp_sa, Config),
@@ -1129,15 +1161,15 @@ finish_v1(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v2(suite) -> {req, [], {conf, init_v2, v2_cases(), finish_v2}}.
%v2_cases() -> [loop_mib_2];
-v2_cases() ->
- [simple_2, v2_processing, big_2, big2_2, loop_mib_2,
- api_2, subagent_2, mnesia_2,
- multiple_reqs_2, sa_register_2, v2_trap, v2_inform, sa_error_2,
- next_across_sa_2, undo_2, reported_bugs_2, standard_mibs_2,
- v2_types, implied, sparse_table_2, cnt_64_2, opaque_2, v2_caps].
+v2_cases() ->
+[simple_2, v2_processing, big_2, big2_2, loop_mib_2,
+ api_2, subagent_2, mnesia_2, {group, multiple_reqs_2},
+ sa_register_2, v2_trap, {group, v2_inform}, sa_error_2,
+ next_across_sa_2, undo_2, {group, reported_bugs_2},
+ {group, standard_mibs_2}, v2_types, implied,
+ sparse_table_2, cnt_64_2, opaque_2, v2_caps].
init_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1154,10 +1186,9 @@ finish_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v1_v2(suite) -> {req, [], {conf, init_v1_v2, v1_v2_cases(), finish_v1_v2}}.
-v1_v2_cases() ->
- [simple_bi].
+v1_v2_cases() ->
+[simple_bi].
init_v1_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1174,16 +1205,16 @@ finish_v1_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v3(suite) -> {req, [], {conf, init_v3, v3_cases(), finish_v3}}.
%v3_cases() -> [loop_mib_3];
-v3_cases() ->
- [simple_3, v3_processing,
- big_3, big2_3, api_3, subagent_3, mnesia_3, loop_mib_3,
- multiple_reqs_3, sa_register_3, v3_trap, v3_inform, sa_error_3,
- next_across_sa_3, undo_3, reported_bugs_3, standard_mibs_3,
- v3_security,
- v2_types_3, implied_3, sparse_table_3, cnt_64_3, opaque_3, v2_caps_3].
+v3_cases() ->
+[simple_3, v3_processing, big_3, big2_3, api_3,
+ subagent_3, mnesia_3, loop_mib_3, multiple_reqs_3,
+ sa_register_3, v3_trap, v3_inform, sa_error_3,
+ next_across_sa_3, undo_3, {group, reported_bugs_3},
+ {group, standard_mibs_3}, {group, v3_security},
+ v2_types_3, implied_3, sparse_table_3, cnt_64_3,
+ opaque_3, v2_caps_3].
init_v3(Config) when list(Config) ->
%% Make sure crypto works, otherwise start_agent will fail
@@ -1221,10 +1252,9 @@ finish_v3(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_multi_threaded(suite) -> {req, [], {conf, init_mt, mt_cases(), finish_mt}}.
-mt_cases() ->
- [multi_threaded, mt_trap].
+mt_cases() ->
+[multi_threaded, mt_trap].
init_mt(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1739,21 +1769,19 @@ mnesia_2(X) -> mnesia(X).
mnesia_3(X) -> mnesia(X).
-multiple_reqs(suite) ->
- {req, [], {conf, init_mul, mul_cases(), finish_mul}}.
-mul_cases() ->
- [mul_get, mul_get_err, mul_next, mul_next_err, mul_set_err].
+mul_cases() ->
+[mul_get, mul_get_err, mul_next, mul_next_err,
+ mul_set_err].
-multiple_reqs_2(suite) ->
- {req, [], {conf, init_mul, mul_cases_2(), finish_mul}}.
multiple_reqs_3(_X) ->
{req, [], {conf, init_mul, mul_cases_3(), finish_mul}}.
-mul_cases_2() ->
- [mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2, mul_set_err_2].
+mul_cases_2() ->
+[mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2,
+ mul_set_err_2].
mul_cases_3() ->
@@ -1939,8 +1967,6 @@ v2_trap(Config) when list(Config) ->
v3_trap(X) ->
v2_trap(X).
-v2_inform(suite) ->
- {req, [], {conf, init_v2_inform, [v2_inform_i], finish_v2_inform}}.
v3_inform(_X) ->
%% v2_inform(X).
@@ -2112,7 +2138,6 @@ v3_processing(Config) when list(Config) ->
%% accomplished by the first inform sent. That one will generate a
%% report, which makes it in sync. The notification-generating
%% application times out, and send again. This time it'll work.
-v3_security(suite) -> [v3_crypto_basic, v3_md5_auth, v3_sha_auth, v3_des_priv].
v3_crypto_basic(suite) -> [];
v3_crypto_basic(_Config) ->
@@ -3591,22 +3616,8 @@ bad_return() ->
%%% Note that many of the functions in the standard mib is
%%% already tested by the normal tests.
%%%-----------------------------------------------------------------
-standard_mibs(suite) ->
- [snmp_standard_mib, snmp_community_mib,
- snmp_framework_mib,
- snmp_target_mib, snmp_notification_mib,
- snmp_view_based_acm_mib].
-
-standard_mibs_2(suite) ->
- [snmpv2_mib_2, snmp_community_mib_2,
- snmp_framework_mib_2,
- snmp_target_mib_2, snmp_notification_mib_2,
- snmp_view_based_acm_mib_2].
-
-standard_mibs_3(suite) ->
- [snmpv2_mib_3,snmp_framework_mib_3, snmp_mpd_mib_3,
- snmp_target_mib_3, snmp_notification_mib_3,
- snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3].
+
+
%%-----------------------------------------------------------------
%% For this test, the agent is configured for v1.
@@ -4527,27 +4538,12 @@ loop_it_2(Oid, N) ->
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
-reported_bugs(suite) ->
- [otp_1128, otp_1129, otp_1131, otp_1162,
- otp_1222, otp_1298, otp_1331, otp_1338,
- otp_1342, otp_2776, otp_2979, otp_3187, otp_3725].
-reported_bugs_2(suite) ->
- [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
- otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
- otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2].
-reported_bugs_3(suite) ->
- [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
- otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
- otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
- otp_3542].
%% These are (ticket) test cases where the initiation has to be done
%% individually.
-tickets(suite) ->
- [otp_4394].
%%-----------------------------------------------------------------
%% Ticket: OTP-1128
@@ -4971,10 +4967,6 @@ otp_3725_test(MaNode) ->
%%-----------------------------------------------------------------
-otp_4394(suite) -> {req, [], {conf,
- init_otp_4394,
- [otp_4394_test],
- finish_otp_4394}}.
init_otp_4394(Config) when list(Config) ->
?DBG("init_otp_4394 -> entry with"
diff --git a/lib/snmp/test/snmp_agent_nfilter_test.erl b/lib/snmp/test/snmp_agent_nfilter_test.erl
index 269c7c96c9..f08060cee3 100644
--- a/lib/snmp/test/snmp_agent_nfilter_test.erl
+++ b/lib/snmp/test/snmp_agent_nfilter_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
@@ -33,8 +33,8 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2
+ all/0,
+ init_per_testcase/2, end_per_testcase/2
]).
%%----------------------------------------------------------------------
@@ -58,14 +58,14 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
%%======================================================================
%% Test case definitions
%%======================================================================
-all(_) ->
- ?SKIP(not_yet_implemented).
+all() ->
+ {skip,not_yet_implemented}.
%%======================================================================
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index af0581150a..692d29fda0 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -28,7 +28,7 @@
-define(application, snmp).
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
@@ -85,18 +85,171 @@
end).
-all(suite) ->
- Reqs = [mnesia, distribution, {local_slave_nodes, 2}, {time, 360}],
- Conf1 = [{conf, init_all, cases(), finish_all}],
- Conf2 = [tickets2],
- {req, Reqs, Conf1 ++ Conf2}.
+all() ->
+ Reqs = [mnesia, distribution, {local_slave_nodes, 2},
+ {time, 360}],
+ Conf1 = [{group, all_tcs}],
+ Conf2 = [{group, tickets2}],
+ Conf1 ++ Conf2.
+
+groups() ->
+ [{all_tcs, [], cases()},
+ {mib_storage, [],
+ [{group, mib_storage_ets}, {group, mib_storage_dets},
+ {group, mib_storage_mnesia},
+ {group, mib_storage_size_check_ets},
+ {group, mib_storage_size_check_dets},
+ {group, mib_storage_size_check_mnesia},
+ {group, mib_storage_varm_dets},
+ {group, mib_storage_varm_mnesia}]},
+ {mib_storage_ets, [], mib_storage_ets_cases()},
+ {mib_storage_dets, [], mib_storage_dets_cases()},
+ {mib_storage_mnesia, [], mib_storage_mnesia_cases()},
+ {mib_storage_size_check_ets, [],
+ mse_size_check_cases()},
+ {mib_storage_size_check_dets, [],
+ msd_size_check_cases()},
+ {mib_storage_size_check_mnesia, [],
+ msm_size_check_cases()},
+ {mib_storage_varm_dets, [],
+ varm_mib_storage_dets_cases()},
+ {mib_storage_varm_mnesia, [],
+ varm_mib_storage_mnesia_cases()},
+ {misc, [], misc_cases()}, {test_v1, [], v1_cases()},
+ {test_v2, [], v2_cases()},
+ {test_v1_v2, [], v1_v2_cases()},
+ {test_v3, [], v3_cases()},
+ {test_multi_threaded, [], mt_cases()},
+ {multiple_reqs, [], mul_cases()},
+ {multiple_reqs_2, [], mul_cases_2()},
+ {v2_inform, [], [v2_inform_i]},
+ {v3_security, [],
+ [v3_crypto_basic, v3_md5_auth, v3_sha_auth,
+ v3_des_priv]},
+ {standard_mibs, [],
+ [snmp_standard_mib, snmp_community_mib,
+ snmp_framework_mib, snmp_target_mib,
+ snmp_notification_mib, snmp_view_based_acm_mib]},
+ {standard_mibs_2, [],
+ [snmpv2_mib_2, snmp_community_mib_2,
+ snmp_framework_mib_2, snmp_target_mib_2,
+ snmp_notification_mib_2, snmp_view_based_acm_mib_2]},
+ {standard_mibs_3, [],
+ [snmpv2_mib_3, snmp_framework_mib_3, snmp_mpd_mib_3,
+ snmp_target_mib_3, snmp_notification_mib_3,
+ snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3]},
+ {reported_bugs, [],
+ [otp_1128, otp_1129, otp_1131, otp_1162, otp_1222,
+ otp_1298, otp_1331, otp_1338, otp_1342, otp_2776,
+ otp_2979, otp_3187, otp_3725]},
+ {reported_bugs_2, [],
+ [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
+ otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
+ otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2]},
+ {reported_bugs_3, [],
+ [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
+ otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
+ otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
+ otp_3542]},
+ {tickets1, [], [{group, otp_4394}, {group, otp_7157}]},
+ {tickets2, [], [otp8395]},
+ {otp_4394, [], [otp_4394_test]},
+ {otp_7157, [],
+ begin Reqs = [], Conf = [otp_7157_test], Conf end}].
+
+init_per_group(all_tcs, Config) ->
+ init_all(Config);
+init_per_group(otp_7157, Config) ->
+ init_otp_7157(Config);
+init_per_group(otp_4394, Config) ->
+ init_otp_4394(Config);
+init_per_group(v2_inform, Config) ->
+ init_v2_inform(Config);
+init_per_group(multiple_reqs_2, Config) ->
+ init_mul(Config);
+init_per_group(multiple_reqs, Config) ->
+ init_mul(Config);
+init_per_group(test_multi_threaded, Config) ->
+ init_mt(Config);
+init_per_group(test_v3, Config) ->
+ init_v3(Config);
+init_per_group(test_v1_v2, Config) ->
+ init_v1_v2(Config);
+init_per_group(test_v2, Config) ->
+ init_v2(Config);
+init_per_group(test_v1, Config) ->
+ init_v1(Config);
+init_per_group(misc, Config) ->
+ init_misc(Config);
+init_per_group(mib_storage_varm_mnesia, Config) ->
+ init_varm_mib_storage_mnesia(Config);
+init_per_group(mib_storage_varm_dets, Config) ->
+ init_varm_mib_storage_dets(Config);
+init_per_group(mib_storage_size_check_mnesia, Config) ->
+ init_size_check_msm(Config);
+init_per_group(mib_storage_size_check_dets, Config) ->
+ init_size_check_msd(Config);
+init_per_group(mib_storage_size_check_ets, Config) ->
+ init_size_check_mse(Config);
+init_per_group(mib_storage_mnesia, Config) ->
+ init_mib_storage_mnesia(Config);
+init_per_group(mib_storage_dets, Config) ->
+ init_mib_storage_dets(Config);
+init_per_group(mib_storage_ets, Config) ->
+ init_mib_storage_ets(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(all_tcs, Config) ->
+ finish_all(Config);
+end_per_group(otp_7157, Config) ->
+ finish_otp_7157(Config);
+end_per_group(otp_4394, Config) ->
+ finish_otp_4394(Config);
+end_per_group(v2_inform, Config) ->
+ finish_v2_inform(Config);
+end_per_group(multiple_reqs_2, Config) ->
+ finish_mul(Config);
+end_per_group(multiple_reqs, Config) ->
+ finish_mul(Config);
+end_per_group(test_multi_threaded, Config) ->
+ finish_mt(Config);
+end_per_group(test_v3, Config) ->
+ finish_v3(Config);
+end_per_group(test_v1_v2, Config) ->
+ finish_v1_v2(Config);
+end_per_group(test_v2, Config) ->
+ finish_v2(Config);
+end_per_group(test_v1, Config) ->
+ finish_v1(Config);
+end_per_group(misc, Config) ->
+ finish_misc(Config);
+end_per_group(mib_storage_varm_mnesia, Config) ->
+ finish_varm_mib_storage_mnesia(Config);
+end_per_group(mib_storage_varm_dets, Config) ->
+ finish_varm_mib_storage_dets(Config);
+end_per_group(mib_storage_size_check_mnesia, Config) ->
+ finish_size_check_msm(Config);
+end_per_group(mib_storage_size_check_dets, Config) ->
+ finish_size_check_msd(Config);
+end_per_group(mib_storage_size_check_ets, Config) ->
+ finish_size_check_mse(Config);
+end_per_group(mib_storage_mnesia, Config) ->
+ finish_mib_storage_mnesia(Config);
+end_per_group(mib_storage_dets, Config) ->
+ finish_mib_storage_dets(Config);
+end_per_group(mib_storage_ets, Config) ->
+ finish_mib_storage_ets(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(otp8395 = Case, Config) when is_list(Config) ->
?DBG("init_per_testcase -> entry with"
"~n Case: ~p"
"~n Config: ~p", [Case, Config]),
- Config2 = init_per_testcase2(Case, init_suite(Config)),
+ Config2 = init_per_testcase2(Case, init_per_suite(Config)),
otp8395({init, Config2});
init_per_testcase(otp_7157_test = _Case, Config) when is_list(Config) ->
?DBG("init_per_testcase -> entry with"
@@ -123,10 +276,10 @@ init_per_testcase(_Case, Config) when is_list(Config) ->
Dog = ?WD_START(?MINS(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(otp8395, Config) when is_list(Config) ->
+end_per_testcase(otp8395, Config) when is_list(Config) ->
otp8395({fin, Config});
-fin_per_testcase(_Case, Config) when is_list(Config) ->
- ?DBG("fin_per_testcase -> entry with"
+end_per_testcase(_Case, Config) when is_list(Config) ->
+ ?DBG("end_per_testcase -> entry with"
"~n Case: ~p"
"~n Config: ~p", [_Case, Config]),
Dog = ?config(watchdog, Config),
@@ -134,8 +287,8 @@ fin_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-init_suite(Config) ->
- ?DBG("init_suite -> entry with"
+init_per_suite(Config) ->
+ ?DBG("init_per_suite -> entry with"
"~n Config: ~p", [Config]),
%% Suite root dir for test suite
@@ -170,12 +323,12 @@ init_suite(Config) ->
{mib_dir, MibDir},
{std_mib_dir, StdMibDir} | Config1],
- ?DBG("init_suite -> done when"
+ ?DBG("init_per_suite -> done when"
"~n Config2: ~p", [Config2]),
Config2.
%% end_per_suite(Config) ->
-end_suite(Config) ->
+end_per_suite(Config) ->
Config.
fix_data_dir(Config) ->
@@ -220,35 +373,22 @@ init_per_testcase2(Case, Config) ->
{sub_agent_top_dir, SubAgentTopDir},
{manager_top_dir, ManagerTopDir} | Config].
-fin_per_testcase2(_Case, Config) ->
+end_per_testcase2(_Case, Config) ->
Config.
-cases() ->
- case ?OSTYPE() of
- vxworks ->
- %% No crypto app, so skip v3 testcases
- [
- misc,
- test_v1,
- test_v2,
- test_v1_v2,
- test_multi_threaded,
- mib_storage,
- tickets1
- ];
- _Else ->
- [
- misc,
- test_v1,
- test_v2,
- test_v1_v2,
- test_v3,
- test_multi_threaded,
- mib_storage,
- tickets1
- ]
- end.
+cases() ->
+case ?OSTYPE() of
+ vxworks ->
+ [{group, misc}, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_multi_threaded},
+ {group, mib_storage}, {group, tickets1}];
+ _Else ->
+ [{group, misc}, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets1}]
+end.
%%%-----------------------------------------------------------------
@@ -355,144 +495,56 @@ delete_mib_storage_mnesia_tables() ->
%% <base>, and a second version <base>_2. There may be several
%% versions as well, <base>_N.
%%-----------------------------------------------------------------
-mib_storage(suite) -> [
- mib_storage_ets,
- mib_storage_dets,
- mib_storage_mnesia,
- mib_storage_size_check_ets,
- mib_storage_size_check_dets,
- mib_storage_size_check_mnesia,
- mib_storage_varm_dets,
- mib_storage_varm_mnesia
- ].
-
-mib_storage_ets(suite) -> {req, [], {conf, init_mib_storage_ets,
- mib_storage_ets_cases(),
- finish_mib_storage_ets}}.
-
-mib_storage_dets(suite) -> {req, [], {conf, init_mib_storage_dets,
- mib_storage_dets_cases(),
- finish_mib_storage_dets}}.
-
-mib_storage_mnesia(suite) -> {req, [], {conf, init_mib_storage_mnesia,
- mib_storage_mnesia_cases(),
- finish_mib_storage_mnesia}}.
-
-mib_storage_size_check_ets(suite) ->
- {req, [], {conf,
- init_size_check_mse,
- mse_size_check_cases(),
- finish_size_check_mse}}.
-
-mib_storage_size_check_dets(suite) ->
- {req, [], {conf,
- init_size_check_msd,
- msd_size_check_cases(),
- finish_size_check_msd}}.
-
-mib_storage_size_check_mnesia(suite) ->
- {req, [], {conf,
- init_size_check_msm,
- msm_size_check_cases(),
- finish_size_check_msm}}.
-
-mib_storage_varm_dets(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_dets,
- varm_mib_storage_dets_cases(),
- finish_varm_mib_storage_dets}}.
-
-mib_storage_varm_mnesia(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_mnesia,
- varm_mib_storage_mnesia_cases(),
- finish_varm_mib_storage_mnesia}}.
-
-mib_storage_ets_cases() ->
- [
- mse_simple,
- mse_v1_processing,
- mse_big,
- mse_big2,
- mse_loop_mib,
- mse_api,
- mse_sa_register,
- mse_v1_trap,
- mse_sa_error,
- mse_next_across_sa,
- mse_undo,
- mse_standard_mib,
- mse_community_mib,
- mse_framework_mib,
- mse_target_mib,
- mse_notification_mib,
- mse_view_based_acm_mib,
- mse_sparse_table,
- mse_me_of,
- mse_mib_of].
-
-mib_storage_dets_cases() ->
- [
- msd_simple,
- msd_v1_processing,
- msd_big,
- msd_big2,
- msd_loop_mib,
- msd_api,
- msd_sa_register,
- msd_v1_trap,
- msd_sa_error,
- msd_next_across_sa,
- msd_undo,
- msd_standard_mib,
- msd_community_mib,
- msd_framework_mib,
- msd_target_mib,
- msd_notification_mib,
- msd_view_based_acm_mib,
- msd_sparse_table,
- msd_me_of,
- msd_mib_of
- ].
-
-mib_storage_mnesia_cases() ->
- [
- msm_simple,
- msm_v1_processing,
- msm_big,
- msm_big2,
- msm_loop_mib,
- msm_api,
- msm_sa_register,
- msm_v1_trap,
- msm_sa_error,
- msm_next_across_sa,
- msm_undo,
- msm_standard_mib,
- msm_community_mib,
- msm_framework_mib,
- msm_target_mib,
- msm_notification_mib,
- msm_view_based_acm_mib,
- msm_sparse_table,
- msm_me_of,
- msm_mib_of
- ].
-
-mse_size_check_cases() ->
- [mse_size_check].
-
-msd_size_check_cases() ->
- [msd_size_check].
-
-msm_size_check_cases() ->
- [msm_size_check].
-
-varm_mib_storage_dets_cases() ->
- [msd_varm_mib_start].
-
-varm_mib_storage_mnesia_cases() ->
- [msm_varm_mib_start].
+
+
+
+
+
+
+
+
+
+mib_storage_ets_cases() ->
+[mse_simple, mse_v1_processing, mse_big, mse_big2,
+ mse_loop_mib, mse_api, mse_sa_register, mse_v1_trap,
+ mse_sa_error, mse_next_across_sa, mse_undo,
+ mse_standard_mib, mse_community_mib, mse_framework_mib,
+ mse_target_mib, mse_notification_mib,
+ mse_view_based_acm_mib, mse_sparse_table, mse_me_of,
+ mse_mib_of].
+
+mib_storage_dets_cases() ->
+[msd_simple, msd_v1_processing, msd_big, msd_big2,
+ msd_loop_mib, msd_api, msd_sa_register, msd_v1_trap,
+ msd_sa_error, msd_next_across_sa, msd_undo,
+ msd_standard_mib, msd_community_mib, msd_framework_mib,
+ msd_target_mib, msd_notification_mib,
+ msd_view_based_acm_mib, msd_sparse_table, msd_me_of,
+ msd_mib_of].
+
+mib_storage_mnesia_cases() ->
+[msm_simple, msm_v1_processing, msm_big, msm_big2,
+ msm_loop_mib, msm_api, msm_sa_register, msm_v1_trap,
+ msm_sa_error, msm_next_across_sa, msm_undo,
+ msm_standard_mib, msm_community_mib, msm_framework_mib,
+ msm_target_mib, msm_notification_mib,
+ msm_view_based_acm_mib, msm_sparse_table, msm_me_of,
+ msm_mib_of].
+
+mse_size_check_cases() ->
+[mse_size_check].
+
+msd_size_check_cases() ->
+[msd_size_check].
+
+msm_size_check_cases() ->
+[msm_size_check].
+
+varm_mib_storage_dets_cases() ->
+[msd_varm_mib_start].
+
+varm_mib_storage_mnesia_cases() ->
+[msm_varm_mib_start].
init_mib_storage_ets(Config) when is_list(Config) ->
?LOG("init_mib_storage_ets -> entry", []),
@@ -975,8 +1027,6 @@ mib_of(Oid, ExpectedMibName) ->
end.
-misc(suite) ->
- {req, [], {conf, init_misc, misc_cases(), finish_misc}}.
init_misc(Config) ->
init_v1(Config).
@@ -984,11 +1034,8 @@ init_misc(Config) ->
finish_misc(Config) ->
finish_v1(Config).
-misc_cases() ->
- [
- app_info,
- info_test
- ].
+misc_cases() ->
+[app_info, info_test].
app_info(suite) -> [];
app_info(Config) when is_list(Config) ->
@@ -1021,34 +1068,14 @@ app_dir(App) ->
end.
-test_v1(suite) -> {req, [], {conf, init_v1, v1_cases(), finish_v1}}.
%v1_cases() -> [loop_mib];
-v1_cases() ->
- [
- simple,
- db_notify_client,
- v1_processing,
- big,
- big2,
- loop_mib,
- api,
- subagent,
- mnesia,
- multiple_reqs,
- sa_register,
- v1_trap,
- sa_error,
- next_across_sa,
- undo,
- reported_bugs,
- standard_mibs,
- sparse_table,
- cnt_64,
- opaque,
-
- change_target_addr_config
- ].
+v1_cases() ->
+[simple, db_notify_client, v1_processing, big, big2,
+ loop_mib, api, subagent, mnesia, {group, multiple_reqs},
+ sa_register, v1_trap, sa_error, next_across_sa, undo,
+ {group, reported_bugs}, {group, standard_mibs},
+ sparse_table, cnt_64, opaque, change_target_addr_config].
init_v1(Config) when is_list(Config) ->
?line SaNode = ?config(snmp_sa, Config),
@@ -1065,34 +1092,14 @@ finish_v1(Config) when is_list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v2(suite) -> {req, [], {conf, init_v2, v2_cases(), finish_v2}}.
-
-v2_cases() ->
- [
- simple_2,
- v2_processing,
- big_2,
- big2_2,
- loop_mib_2,
- api_2,
- subagent_2,
- mnesia_2,
- multiple_reqs_2,
- sa_register_2,
- v2_trap,
- v2_inform,
- sa_error_2,
- next_across_sa_2,
- undo_2,
- reported_bugs_2,
- standard_mibs_2,
- v2_types,
- implied,
- sparse_table_2,
- cnt_64_2,
- opaque_2,
- v2_caps
- ].
+
+v2_cases() ->
+[simple_2, v2_processing, big_2, big2_2, loop_mib_2,
+ api_2, subagent_2, mnesia_2, {group, multiple_reqs_2},
+ sa_register_2, v2_trap, {group, v2_inform}, sa_error_2,
+ next_across_sa_2, undo_2, {group, reported_bugs_2},
+ {group, standard_mibs_2}, v2_types, implied,
+ sparse_table_2, cnt_64_2, opaque_2, v2_caps].
init_v2(Config) when is_list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1109,11 +1116,9 @@ finish_v2(Config) when is_list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v1_v2(suite) ->
- {req, [], {conf, init_v1_v2, v1_v2_cases(), finish_v1_v2}}.
-v1_v2_cases() ->
- [simple_bi].
+v1_v2_cases() ->
+[simple_bi].
init_v1_v2(Config) when is_list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1130,35 +1135,15 @@ finish_v1_v2(Config) when is_list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v3(suite) -> {req, [], {conf, init_v3, v3_cases(), finish_v3}}.
-
-v3_cases() ->
- [
- simple_3,
- v3_processing,
- big_3,
- big2_3,
- api_3,
- subagent_3,
- mnesia_3,
- loop_mib_3,
- multiple_reqs_3,
- sa_register_3,
- v3_trap,
- v3_inform,
- sa_error_3,
- next_across_sa_3,
- undo_3,
- reported_bugs_3,
- standard_mibs_3,
- v3_security,
- v2_types_3,
- implied_3,
- sparse_table_3,
- cnt_64_3,
- opaque_3,
- v2_caps_3
- ].
+
+v3_cases() ->
+[simple_3, v3_processing, big_3, big2_3, api_3,
+ subagent_3, mnesia_3, loop_mib_3, multiple_reqs_3,
+ sa_register_3, v3_trap, v3_inform, sa_error_3,
+ next_across_sa_3, undo_3, {group, reported_bugs_3},
+ {group, standard_mibs_3}, {group, v3_security},
+ v2_types_3, implied_3, sparse_table_3, cnt_64_3,
+ opaque_3, v2_caps_3].
init_v3(Config) when is_list(Config) ->
%% Make sure crypto works, otherwise start_agent will fail
@@ -1196,11 +1181,9 @@ finish_v3(Config) when is_list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_multi_threaded(suite) ->
- {req, [], {conf, init_mt, mt_cases(), finish_mt}}.
-mt_cases() ->
- [multi_threaded, mt_trap].
+mt_cases() ->
+[multi_threaded, mt_trap].
init_mt(Config) when is_list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1690,21 +1673,19 @@ mnesia_2(X) -> ?P(mnesia_2), mnesia(X).
mnesia_3(X) -> ?P(mnesia_3), mnesia(X).
-multiple_reqs(suite) ->
- {req, [], {conf, init_mul, mul_cases(), finish_mul}}.
-mul_cases() ->
- [mul_get, mul_get_err, mul_next, mul_next_err, mul_set_err].
+mul_cases() ->
+[mul_get, mul_get_err, mul_next, mul_next_err,
+ mul_set_err].
-multiple_reqs_2(suite) ->
- {req, [], {conf, init_mul, mul_cases_2(), finish_mul}}.
multiple_reqs_3(_X) ->
{req, [], {conf, init_mul, mul_cases_3(), finish_mul}}.
-mul_cases_2() ->
- [mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2, mul_set_err_2].
+mul_cases_2() ->
+[mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2,
+ mul_set_err_2].
mul_cases_3() ->
@@ -1929,8 +1910,6 @@ v3_trap(Config) when is_list(Config) ->
?P(v3_trap),
trap2(Config).
-v2_inform(suite) ->
- {req, [], {conf, init_v2_inform, [v2_inform_i], finish_v2_inform}}.
v3_inform(_X) ->
%% v2_inform(X).
@@ -1977,7 +1956,8 @@ inform_i(Config) ->
?P1("unload TestTrap & TestTrapv2..."),
?line unload_master("TestTrap"),
- ?line unload_master("TestTrapv2").
+ ?line unload_master("TestTrapv2"),
+ ok.
v3_inform_i(X) ->
%% <CONDITIONAL-SKIP>
@@ -2189,13 +2169,6 @@ v3_processing(Config) when is_list(Config) ->
%% accomplished by the first inform sent. That one will generate a
%% report, which makes it in sync. The notification-generating
%% application times out, and send again. This time it'll work.
-v3_security(suite) ->
- [
- v3_crypto_basic,
- v3_md5_auth,
- v3_sha_auth,
- v3_des_priv
- ].
v3_crypto_basic(suite) -> [];
v3_crypto_basic(_Config) ->
@@ -3446,7 +3419,7 @@ do_mul_set_err() ->
?line ?v1_2(expect(2, noSuchName, 1, any),
expect(2, [{[friendsEntry, [2,3]], noSuchInstance}])),
g([NewKeyc4]),
- ?line ?v1_2(expect(3, noSuchName, 1, any),
+ ?line ?v1_2(expect(3, noSuchName, 1, any),
expect(3, [{NewKeyc4, noSuchInstance}])).
%% Req. SA-MIB
@@ -3457,10 +3430,10 @@ sa_mib() ->
?line expect(2, [{[sa, [1,0]], "sa_test"}]).
ma_trap1(MA) ->
- snmpa:send_trap(MA, testTrap2, "standard trap"),
+ ok = snmpa:send_trap(MA, testTrap2, "standard trap"),
?line expect(1, trap, [system], 6, 1, [{[system, [4,0]],
"{mbj,eklas}@erlang.ericsson.se"}]),
- snmpa:send_trap(MA, testTrap1, "standard trap"),
+ ok = snmpa:send_trap(MA, testTrap1, "standard trap"),
?line expect(2, trap, [1,2,3] , 1, 0, [{[system, [4,0]],
"{mbj,eklas}@erlang.ericsson.se"}]).
@@ -3509,7 +3482,8 @@ ma_v2_trap1(MA) ->
?DBG("ma_v2_traps -> send standard trap: testTrapv21",[]),
snmpa:send_trap(MA, testTrapv21, "standard trap"),
?line expect(2, v2trap, [{[sysUpTime, 0], any},
- {[snmpTrapOID, 0], ?snmp ++ [1]}]).
+ {[snmpTrapOID, 0], ?snmp ++ [1]}]),
+ ok.
ma_v2_trap2(MA) ->
snmpa:send_trap(MA,testTrapv22,"standard trap",[{sysContact,"pelle"}]),
@@ -3517,7 +3491,7 @@ ma_v2_trap2(MA) ->
{[snmpTrapOID, 0], ?system ++ [0,1]},
{[system, [4,0]], "pelle"}]).
-%% Note: This test case takes a while... actually a couple of minutes.
+%% Note: This test case takes a while... actually a couple of minutes.
ma_v2_inform1(MA) ->
?DBG("ma_v2_inform1 -> entry with"
"~n MA = ~p => "
@@ -4042,36 +4016,8 @@ bad_return() ->
%%% Note that many of the functions in the standard mib is
%%% already tested by the normal tests.
%%%-----------------------------------------------------------------
-standard_mibs(suite) ->
- [
- snmp_standard_mib,
- snmp_community_mib,
- snmp_framework_mib,
- snmp_target_mib,
- snmp_notification_mib,
- snmp_view_based_acm_mib
- ].
-
-standard_mibs_2(suite) ->
- [
- snmpv2_mib_2,
- snmp_community_mib_2,
- snmp_framework_mib_2,
- snmp_target_mib_2,
- snmp_notification_mib_2,
- snmp_view_based_acm_mib_2
- ].
-
-standard_mibs_3(suite) ->
- [
- snmpv2_mib_3,
- snmp_framework_mib_3,
- snmp_mpd_mib_3,
- snmp_target_mib_3,
- snmp_notification_mib_3,
- snmp_view_based_acm_mib_3,
- snmp_user_based_sm_mib_3
- ].
+
+
%%-----------------------------------------------------------------
%% For this test, the agent is configured for v1.
@@ -5115,70 +5061,14 @@ loop_it_2(Oid, N) ->
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
-reported_bugs(suite) ->
- [
- otp_1128,
- otp_1129,
- otp_1131,
- otp_1162,
- otp_1222,
- otp_1298,
- otp_1331,
- otp_1338,
- otp_1342,
- otp_2776,
- otp_2979,
- otp_3187,
- otp_3725
- ].
-
-reported_bugs_2(suite) ->
- [
- otp_1128_2,
- otp_1129_2,
- otp_1131_2,
- otp_1162_2,
- otp_1222_2,
- otp_1298_2,
- otp_1331_2,
- otp_1338_2,
- otp_1342_2,
- otp_2776_2,
- otp_2979_2,
- otp_3187_2
- ].
-
-reported_bugs_3(suite) ->
- [
- otp_1128_3,
- otp_1129_3,
- otp_1131_3,
- otp_1162_3,
- otp_1222_3,
- otp_1298_3,
- otp_1331_3,
- otp_1338_3,
- otp_1342_3,
- otp_2776_3,
- otp_2979_3,
- otp_3187_3,
- otp_3542
- ].
+
+
%% These are (ticket) test cases where the initiation has to be done
%% individually.
-tickets1(suite) ->
- [
- otp_4394,
- otp_7157
- ].
-tickets2(suite) ->
- [
- otp8395
- ].
@@ -5258,7 +5148,35 @@ otp_1131_2(X) -> ?P(otp_1131_2), otp_1131(X).
otp_1131_3(X) ->
%% <CONDITIONAL-SKIP>
- Skippable = [{unix, [darwin]}],
+ %% This is intended to catch Montavista Linux 4.0/ppc (2.6.5)
+ %% Montavista Linux looks like a Debian distro (/etc/issue)
+ LinuxVersionVerify =
+ fun() ->
+ case os:cmd("uname -m") of
+ "ppc" ++ _ ->
+ case file:read_file_info("/etc/issue") of
+ {ok, _} ->
+ case os:cmd("grep -i montavista /etc/issue") of
+ Info when (is_list(Info) andalso
+ (length(Info) > 0)) ->
+ case os:version() of
+ {2, 6, 10} ->
+ true;
+ _ ->
+ false
+ end;
+ _ -> % Maybe plain Debian or Ubuntu
+ false
+ end;
+ _ ->
+ %% Not a Debian based distro
+ false
+ end;
+ _ ->
+ false
+ end
+ end,
+ Skippable = [{unix, [darwin, {linux, LinuxVersionVerify}]}],
Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
?NON_PC_TC_MAYBE_SKIP(X, Condition),
%% </CONDITIONAL-SKIP>
@@ -5631,10 +5549,6 @@ otp_3725_test(MaNode) ->
%%-----------------------------------------------------------------
-otp_4394(suite) -> {req, [], {conf,
- init_otp_4394,
- [otp_4394_test],
- finish_otp_4394}}.
init_otp_4394(Config) when is_list(Config) ->
?DBG("init_otp_4394 -> entry with"
@@ -5728,10 +5642,6 @@ otp_4394_test1() ->
%%-----------------------------------------------------------------
-otp_7157(suite) ->
- Reqs = [],
- Conf = [{conf, init_otp_7157, [otp_7157_test], finish_otp_7157}],
- {req, Reqs, Conf}.
init_otp_7157(Config) when is_list(Config) ->
%% <CONDITIONAL-SKIP>
@@ -6219,12 +6129,15 @@ verify_old_info([Key|Keys], Info) ->
is(S) -> [length(S) | S].
try_test(Func) ->
+ ?P2("try test ~w...", [Func]),
snmp_agent_test_lib:try_test(?MODULE, Func).
try_test(Func, A) ->
+ ?P2("try test ~w...", [Func]),
snmp_agent_test_lib:try_test(?MODULE, Func, A).
try_test(Func, A, Opts) ->
+ ?P2("try test ~w...", [Func]),
snmp_agent_test_lib:try_test(?MODULE, Func, A, Opts).
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 31b375efa9..9e89aa889c 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -421,7 +421,7 @@ start_agent(Config, Vsns, Opts) ->
?LOG("start_agent -> entry (~p) with"
"~n Config: ~p"
"~n Vsns: ~p"
- "~n Opts: ~p",[node(), Config, Vsns, Opts]),
+ "~n Opts: ~p", [node(), Config, Vsns, Opts]),
?line AgentDir = ?config(agent_dir, Config),
?line SaNode = ?config(snmp_sa, Config),
diff --git a/lib/snmp/test/snmp_agent_v1_test.erl b/lib/snmp/test/snmp_agent_v1_test.erl
index 52ac6cf58f..737bb25cc3 100644
--- a/lib/snmp/test/snmp_agent_v1_test.erl
+++ b/lib/snmp/test/snmp_agent_v1_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -103,7 +103,7 @@ init_per_testcase(_Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:minutes(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
Config.
diff --git a/lib/snmp/test/snmp_agent_v2_test.erl b/lib/snmp/test/snmp_agent_v2_test.erl
index eca66dc30d..dc94c18ad9 100644
--- a/lib/snmp/test/snmp_agent_v2_test.erl
+++ b/lib/snmp/test/snmp_agent_v2_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-define(application, snmp).
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
@@ -83,39 +83,165 @@
_ -> V3
end).
-all(suite) -> {req,
- [mnesia, distribution,
- {local_slave_nodes, 2}, {time, 360}],
- [{conf, init_all, cases(), finish_all}]}.
+all() ->
+[cases()].
+
+groups() ->
+ [{mib_storage, [],
+ [{group, mib_storage_ets}, {group, mib_storage_dets},
+ {group, mib_storage_mnesia},
+ {group, mib_storage_size_check_ets},
+ {group, mib_storage_size_check_dets},
+ {group, mib_storage_size_check_mnesia},
+ {group, mib_storage_varm_dets},
+ {group, mib_storage_varm_mnesia}]},
+ {mib_storage_ets, [], mib_storage_ets_cases()},
+ {mib_storage_dets, [], mib_storage_dets_cases()},
+ {mib_storage_mnesia, [], mib_storage_mnesia_cases()},
+ {mib_storage_size_check_ets, [],
+ mse_size_check_cases()},
+ {mib_storage_size_check_dets, [],
+ msd_size_check_cases()},
+ {mib_storage_size_check_mnesia, [],
+ msm_size_check_cases()},
+ {mib_storage_varm_dets, [],
+ varm_mib_storage_dets_cases()},
+ {mib_storage_varm_mnesia, [],
+ varm_mib_storage_mnesia_cases()},
+ {test_v1, [], v1_cases()}, {test_v2, [], v2_cases()},
+ {test_v1_v2, [], v1_v2_cases()},
+ {test_v3, [], v3_cases()},
+ {test_multi_threaded, [], mt_cases()},
+ {multiple_reqs, [], mul_cases()},
+ {multiple_reqs_2, [], mul_cases_2()},
+ {v2_inform, [], [v2_inform_i]},
+ {v3_security, [],
+ [v3_crypto_basic, v3_md5_auth, v3_sha_auth,
+ v3_des_priv]},
+ {standard_mibs, [],
+ [snmp_standard_mib, snmp_community_mib,
+ snmp_framework_mib, snmp_target_mib,
+ snmp_notification_mib, snmp_view_based_acm_mib]},
+ {standard_mibs_2, [],
+ [snmpv2_mib_2, snmp_community_mib_2,
+ snmp_framework_mib_2, snmp_target_mib_2,
+ snmp_notification_mib_2, snmp_view_based_acm_mib_2]},
+ {standard_mibs_3, [],
+ [snmpv2_mib_3, snmp_framework_mib_3, snmp_mpd_mib_3,
+ snmp_target_mib_3, snmp_notification_mib_3,
+ snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3]},
+ {reported_bugs, [],
+ [otp_1128, otp_1129, otp_1131, otp_1162, otp_1222,
+ otp_1298, otp_1331, otp_1338, otp_1342, otp_2776,
+ otp_2979, otp_3187, otp_3725]},
+ {reported_bugs_2, [],
+ [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
+ otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
+ otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2]},
+ {reported_bugs_3, [],
+ [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
+ otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
+ otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
+ otp_3542]},
+ {tickets, [], [{group, otp_4394}]},
+ {otp_4394, [], [otp_4394_test]}].
+
+init_per_group(otp_4394, Config) ->
+ init_otp_4394(Config);
+init_per_group(v2_inform, Config) ->
+ init_v2_inform(Config);
+init_per_group(multiple_reqs_2, Config) ->
+ init_mul(Config);
+init_per_group(multiple_reqs, Config) ->
+ init_mul(Config);
+init_per_group(test_multi_threaded, Config) ->
+ init_mt(Config);
+init_per_group(test_v3, Config) ->
+ init_v3(Config);
+init_per_group(test_v1_v2, Config) ->
+ init_v1_v2(Config);
+init_per_group(test_v2, Config) ->
+ init_v2(Config);
+init_per_group(test_v1, Config) ->
+ init_v1(Config);
+init_per_group(mib_storage_varm_mnesia, Config) ->
+ init_varm_mib_storage_mnesia(Config);
+init_per_group(mib_storage_varm_dets, Config) ->
+ init_varm_mib_storage_dets(Config);
+init_per_group(mib_storage_size_check_mnesia, Config) ->
+ init_size_check_msm(Config);
+init_per_group(mib_storage_size_check_dets, Config) ->
+ init_size_check_msd(Config);
+init_per_group(mib_storage_size_check_ets, Config) ->
+ init_size_check_mse(Config);
+init_per_group(mib_storage_mnesia, Config) ->
+ init_mib_storage_mnesia(Config);
+init_per_group(mib_storage_dets, Config) ->
+ init_mib_storage_dets(Config);
+init_per_group(mib_storage_ets, Config) ->
+ init_mib_storage_ets(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(otp_4394, Config) ->
+ finish_otp_4394(Config);
+end_per_group(v2_inform, Config) ->
+ finish_v2_inform(Config);
+end_per_group(multiple_reqs_2, Config) ->
+ finish_mul(Config);
+end_per_group(multiple_reqs, Config) ->
+ finish_mul(Config);
+end_per_group(test_multi_threaded, Config) ->
+ finish_mt(Config);
+end_per_group(test_v3, Config) ->
+ finish_v3(Config);
+end_per_group(test_v1_v2, Config) ->
+ finish_v1_v2(Config);
+end_per_group(test_v2, Config) ->
+ finish_v2(Config);
+end_per_group(test_v1, Config) ->
+ finish_v1(Config);
+end_per_group(mib_storage_varm_mnesia, Config) ->
+ finish_varm_mib_storage_mnesia(Config);
+end_per_group(mib_storage_varm_dets, Config) ->
+ finish_varm_mib_storage_dets(Config);
+end_per_group(mib_storage_size_check_mnesia, Config) ->
+ finish_size_check_msm(Config);
+end_per_group(mib_storage_size_check_dets, Config) ->
+ finish_size_check_msd(Config);
+end_per_group(mib_storage_size_check_ets, Config) ->
+ finish_size_check_mse(Config);
+end_per_group(mib_storage_mnesia, Config) ->
+ finish_mib_storage_mnesia(Config);
+end_per_group(mib_storage_dets, Config) ->
+ finish_mib_storage_dets(Config);
+end_per_group(mib_storage_ets, Config) ->
+ finish_mib_storage_ets(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:minutes(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
Config.
-cases() ->
- case ?OSTYPE() of
- vxworks ->
- %% No crypto app, so skip v3 testcases
- [
- app_info,
- test_v1, test_v2, test_v1_v2,
- test_multi_threaded,
- mib_storage,
- tickets];
- _Else ->
- [
- app_info,
- test_v1, test_v2, test_v1_v2, test_v3,
- test_multi_threaded,
- mib_storage,
- tickets
- ]
- end.
+cases() ->
+case ?OSTYPE() of
+ vxworks ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_multi_threaded},
+ {group, mib_storage}, {group, tickets}];
+ _Else ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}]
+end.
%%%-----------------------------------------------------------------
@@ -460,144 +586,56 @@ delete_mib_storage_mnesia_tables() ->
%% <base>, and a second version <base>_2. There may be several
%% versions as well, <base>_N.
%%-----------------------------------------------------------------
-mib_storage(suite) -> [
- mib_storage_ets,
- mib_storage_dets,
- mib_storage_mnesia,
- mib_storage_size_check_ets,
- mib_storage_size_check_dets,
- mib_storage_size_check_mnesia,
- mib_storage_varm_dets,
- mib_storage_varm_mnesia
- ].
-
-mib_storage_ets(suite) -> {req, [], {conf, init_mib_storage_ets,
- mib_storage_ets_cases(),
- finish_mib_storage_ets}}.
-
-mib_storage_dets(suite) -> {req, [], {conf, init_mib_storage_dets,
- mib_storage_dets_cases(),
- finish_mib_storage_dets}}.
-
-mib_storage_mnesia(suite) -> {req, [], {conf, init_mib_storage_mnesia,
- mib_storage_mnesia_cases(),
- finish_mib_storage_mnesia}}.
-
-mib_storage_size_check_ets(suite) ->
- {req, [], {conf,
- init_size_check_mse,
- mse_size_check_cases(),
- finish_size_check_mse}}.
-
-mib_storage_size_check_dets(suite) ->
- {req, [], {conf,
- init_size_check_msd,
- msd_size_check_cases(),
- finish_size_check_msd}}.
-
-mib_storage_size_check_mnesia(suite) ->
- {req, [], {conf,
- init_size_check_msm,
- msm_size_check_cases(),
- finish_size_check_msm}}.
-
-mib_storage_varm_dets(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_dets,
- varm_mib_storage_dets_cases(),
- finish_varm_mib_storage_dets}}.
-
-mib_storage_varm_mnesia(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_mnesia,
- varm_mib_storage_mnesia_cases(),
- finish_varm_mib_storage_mnesia}}.
-
-mib_storage_ets_cases() ->
- [
- mse_simple,
- mse_v1_processing,
- mse_big,
- mse_big2,
- mse_loop_mib,
- mse_api,
- mse_sa_register,
- mse_v1_trap,
- mse_sa_error,
- mse_next_across_sa,
- mse_undo,
- mse_standard_mib,
- mse_community_mib,
- mse_framework_mib,
- mse_target_mib,
- mse_notification_mib,
- mse_view_based_acm_mib,
- mse_sparse_table,
- mse_me_of,
- mse_mib_of].
-
-mib_storage_dets_cases() ->
- [
- msd_simple,
- msd_v1_processing,
- msd_big,
- msd_big2,
- msd_loop_mib,
- msd_api,
- msd_sa_register,
- msd_v1_trap,
- msd_sa_error,
- msd_next_across_sa,
- msd_undo,
- msd_standard_mib,
- msd_community_mib,
- msd_framework_mib,
- msd_target_mib,
- msd_notification_mib,
- msd_view_based_acm_mib,
- msd_sparse_table,
- msd_me_of,
- msd_mib_of
- ].
-
-mib_storage_mnesia_cases() ->
- [
- msm_simple,
- msm_v1_processing,
- msm_big,
- msm_big2,
- msm_loop_mib,
- msm_api,
- msm_sa_register,
- msm_v1_trap,
- msm_sa_error,
- msm_next_across_sa,
- msm_undo,
- msm_standard_mib,
- msm_community_mib,
- msm_framework_mib,
- msm_target_mib,
- msm_notification_mib,
- msm_view_based_acm_mib,
- msm_sparse_table,
- msm_me_of,
- msm_mib_of
- ].
-
-mse_size_check_cases() ->
- [mse_size_check].
-
-msd_size_check_cases() ->
- [msd_size_check].
-
-msm_size_check_cases() ->
- [msm_size_check].
-
-varm_mib_storage_dets_cases() ->
- [msd_varm_mib_start].
-
-varm_mib_storage_mnesia_cases() ->
- [msm_varm_mib_start].
+
+
+
+
+
+
+
+
+
+mib_storage_ets_cases() ->
+[mse_simple, mse_v1_processing, mse_big, mse_big2,
+ mse_loop_mib, mse_api, mse_sa_register, mse_v1_trap,
+ mse_sa_error, mse_next_across_sa, mse_undo,
+ mse_standard_mib, mse_community_mib, mse_framework_mib,
+ mse_target_mib, mse_notification_mib,
+ mse_view_based_acm_mib, mse_sparse_table, mse_me_of,
+ mse_mib_of].
+
+mib_storage_dets_cases() ->
+[msd_simple, msd_v1_processing, msd_big, msd_big2,
+ msd_loop_mib, msd_api, msd_sa_register, msd_v1_trap,
+ msd_sa_error, msd_next_across_sa, msd_undo,
+ msd_standard_mib, msd_community_mib, msd_framework_mib,
+ msd_target_mib, msd_notification_mib,
+ msd_view_based_acm_mib, msd_sparse_table, msd_me_of,
+ msd_mib_of].
+
+mib_storage_mnesia_cases() ->
+[msm_simple, msm_v1_processing, msm_big, msm_big2,
+ msm_loop_mib, msm_api, msm_sa_register, msm_v1_trap,
+ msm_sa_error, msm_next_across_sa, msm_undo,
+ msm_standard_mib, msm_community_mib, msm_framework_mib,
+ msm_target_mib, msm_notification_mib,
+ msm_view_based_acm_mib, msm_sparse_table, msm_me_of,
+ msm_mib_of].
+
+mse_size_check_cases() ->
+[mse_size_check].
+
+msd_size_check_cases() ->
+[msd_size_check].
+
+msm_size_check_cases() ->
+[msm_size_check].
+
+varm_mib_storage_dets_cases() ->
+[msd_varm_mib_start].
+
+varm_mib_storage_mnesia_cases() ->
+[msm_varm_mib_start].
init_mib_storage_ets(Config) when list(Config) ->
?LOG("init_mib_storage_ets -> entry", []),
@@ -1099,20 +1137,14 @@ app_dir(App) ->
end.
-test_v1(suite) -> {req, [], {conf, init_v1, v1_cases(), finish_v1}}.
%v1_cases() -> [loop_mib];
-v1_cases() ->
- [simple,
- db_notify_client,
- v1_processing, big, big2, loop_mib,
- api, subagent, mnesia, multiple_reqs,
- sa_register, v1_trap, sa_error, next_across_sa, undo, reported_bugs,
- standard_mibs, sparse_table, cnt_64,
- opaque,
- % opaque].
-
- change_target_addr_config].
+v1_cases() ->
+[simple, db_notify_client, v1_processing, big, big2,
+ loop_mib, api, subagent, mnesia, {group, multiple_reqs},
+ sa_register, v1_trap, sa_error, next_across_sa, undo,
+ {group, reported_bugs}, {group, standard_mibs},
+ sparse_table, cnt_64, opaque, change_target_addr_config].
init_v1(Config) when list(Config) ->
?line SaNode = ?config(snmp_sa, Config),
@@ -1129,15 +1161,15 @@ finish_v1(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v2(suite) -> {req, [], {conf, init_v2, v2_cases(), finish_v2}}.
%v2_cases() -> [loop_mib_2];
-v2_cases() ->
- [simple_2, v2_processing, big_2, big2_2, loop_mib_2,
- api_2, subagent_2, mnesia_2,
- multiple_reqs_2, sa_register_2, v2_trap, v2_inform, sa_error_2,
- next_across_sa_2, undo_2, reported_bugs_2, standard_mibs_2,
- v2_types, implied, sparse_table_2, cnt_64_2, opaque_2, v2_caps].
+v2_cases() ->
+[simple_2, v2_processing, big_2, big2_2, loop_mib_2,
+ api_2, subagent_2, mnesia_2, {group, multiple_reqs_2},
+ sa_register_2, v2_trap, {group, v2_inform}, sa_error_2,
+ next_across_sa_2, undo_2, {group, reported_bugs_2},
+ {group, standard_mibs_2}, v2_types, implied,
+ sparse_table_2, cnt_64_2, opaque_2, v2_caps].
init_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1154,10 +1186,9 @@ finish_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v1_v2(suite) -> {req, [], {conf, init_v1_v2, v1_v2_cases(), finish_v1_v2}}.
-v1_v2_cases() ->
- [simple_bi].
+v1_v2_cases() ->
+[simple_bi].
init_v1_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1174,16 +1205,16 @@ finish_v1_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v3(suite) -> {req, [], {conf, init_v3, v3_cases(), finish_v3}}.
%v3_cases() -> [loop_mib_3];
-v3_cases() ->
- [simple_3, v3_processing,
- big_3, big2_3, api_3, subagent_3, mnesia_3, loop_mib_3,
- multiple_reqs_3, sa_register_3, v3_trap, v3_inform, sa_error_3,
- next_across_sa_3, undo_3, reported_bugs_3, standard_mibs_3,
- v3_security,
- v2_types_3, implied_3, sparse_table_3, cnt_64_3, opaque_3, v2_caps_3].
+v3_cases() ->
+[simple_3, v3_processing, big_3, big2_3, api_3,
+ subagent_3, mnesia_3, loop_mib_3, multiple_reqs_3,
+ sa_register_3, v3_trap, v3_inform, sa_error_3,
+ next_across_sa_3, undo_3, {group, reported_bugs_3},
+ {group, standard_mibs_3}, {group, v3_security},
+ v2_types_3, implied_3, sparse_table_3, cnt_64_3,
+ opaque_3, v2_caps_3].
init_v3(Config) when list(Config) ->
%% Make sure crypto works, otherwise start_agent will fail
@@ -1221,10 +1252,9 @@ finish_v3(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_multi_threaded(suite) -> {req, [], {conf, init_mt, mt_cases(), finish_mt}}.
-mt_cases() ->
- [multi_threaded, mt_trap].
+mt_cases() ->
+[multi_threaded, mt_trap].
init_mt(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1739,21 +1769,19 @@ mnesia_2(X) -> mnesia(X).
mnesia_3(X) -> mnesia(X).
-multiple_reqs(suite) ->
- {req, [], {conf, init_mul, mul_cases(), finish_mul}}.
-mul_cases() ->
- [mul_get, mul_get_err, mul_next, mul_next_err, mul_set_err].
+mul_cases() ->
+[mul_get, mul_get_err, mul_next, mul_next_err,
+ mul_set_err].
-multiple_reqs_2(suite) ->
- {req, [], {conf, init_mul, mul_cases_2(), finish_mul}}.
multiple_reqs_3(_X) ->
{req, [], {conf, init_mul, mul_cases_3(), finish_mul}}.
-mul_cases_2() ->
- [mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2, mul_set_err_2].
+mul_cases_2() ->
+[mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2,
+ mul_set_err_2].
mul_cases_3() ->
@@ -1939,8 +1967,6 @@ v2_trap(Config) when list(Config) ->
v3_trap(X) ->
v2_trap(X).
-v2_inform(suite) ->
- {req, [], {conf, init_v2_inform, [v2_inform_i], finish_v2_inform}}.
v3_inform(_X) ->
%% v2_inform(X).
@@ -2112,7 +2138,6 @@ v3_processing(Config) when list(Config) ->
%% accomplished by the first inform sent. That one will generate a
%% report, which makes it in sync. The notification-generating
%% application times out, and send again. This time it'll work.
-v3_security(suite) -> [v3_crypto_basic, v3_md5_auth, v3_sha_auth, v3_des_priv].
v3_crypto_basic(suite) -> [];
v3_crypto_basic(_Config) ->
@@ -3591,22 +3616,8 @@ bad_return() ->
%%% Note that many of the functions in the standard mib is
%%% already tested by the normal tests.
%%%-----------------------------------------------------------------
-standard_mibs(suite) ->
- [snmp_standard_mib, snmp_community_mib,
- snmp_framework_mib,
- snmp_target_mib, snmp_notification_mib,
- snmp_view_based_acm_mib].
-
-standard_mibs_2(suite) ->
- [snmpv2_mib_2, snmp_community_mib_2,
- snmp_framework_mib_2,
- snmp_target_mib_2, snmp_notification_mib_2,
- snmp_view_based_acm_mib_2].
-
-standard_mibs_3(suite) ->
- [snmpv2_mib_3,snmp_framework_mib_3, snmp_mpd_mib_3,
- snmp_target_mib_3, snmp_notification_mib_3,
- snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3].
+
+
%%-----------------------------------------------------------------
%% For this test, the agent is configured for v1.
@@ -4527,27 +4538,12 @@ loop_it_2(Oid, N) ->
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
-reported_bugs(suite) ->
- [otp_1128, otp_1129, otp_1131, otp_1162,
- otp_1222, otp_1298, otp_1331, otp_1338,
- otp_1342, otp_2776, otp_2979, otp_3187, otp_3725].
-reported_bugs_2(suite) ->
- [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
- otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
- otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2].
-reported_bugs_3(suite) ->
- [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
- otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
- otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
- otp_3542].
%% These are (ticket) test cases where the initiation has to be done
%% individually.
-tickets(suite) ->
- [otp_4394].
%%-----------------------------------------------------------------
%% Ticket: OTP-1128
@@ -4971,10 +4967,6 @@ otp_3725_test(MaNode) ->
%%-----------------------------------------------------------------
-otp_4394(suite) -> {req, [], {conf,
- init_otp_4394,
- [otp_4394_test],
- finish_otp_4394}}.
init_otp_4394(Config) when list(Config) ->
?DBG("init_otp_4394 -> entry with"
diff --git a/lib/snmp/test/snmp_agent_v3_test.erl b/lib/snmp/test/snmp_agent_v3_test.erl
index 823c914136..266be72878 100644
--- a/lib/snmp/test/snmp_agent_v3_test.erl
+++ b/lib/snmp/test/snmp_agent_v3_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-define(application, snmp).
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
@@ -83,39 +83,165 @@
_ -> V3
end).
-all(suite) -> {req,
- [mnesia, distribution,
- {local_slave_nodes, 2}, {time, 360}],
- [{conf, init_all, cases(), finish_all}]}.
+all() ->
+[cases()].
+
+groups() ->
+ [{mib_storage, [],
+ [{group, mib_storage_ets}, {group, mib_storage_dets},
+ {group, mib_storage_mnesia},
+ {group, mib_storage_size_check_ets},
+ {group, mib_storage_size_check_dets},
+ {group, mib_storage_size_check_mnesia},
+ {group, mib_storage_varm_dets},
+ {group, mib_storage_varm_mnesia}]},
+ {mib_storage_ets, [], mib_storage_ets_cases()},
+ {mib_storage_dets, [], mib_storage_dets_cases()},
+ {mib_storage_mnesia, [], mib_storage_mnesia_cases()},
+ {mib_storage_size_check_ets, [],
+ mse_size_check_cases()},
+ {mib_storage_size_check_dets, [],
+ msd_size_check_cases()},
+ {mib_storage_size_check_mnesia, [],
+ msm_size_check_cases()},
+ {mib_storage_varm_dets, [],
+ varm_mib_storage_dets_cases()},
+ {mib_storage_varm_mnesia, [],
+ varm_mib_storage_mnesia_cases()},
+ {test_v1, [], v1_cases()}, {test_v2, [], v2_cases()},
+ {test_v1_v2, [], v1_v2_cases()},
+ {test_v3, [], v3_cases()},
+ {test_multi_threaded, [], mt_cases()},
+ {multiple_reqs, [], mul_cases()},
+ {multiple_reqs_2, [], mul_cases_2()},
+ {v2_inform, [], [v2_inform_i]},
+ {v3_security, [],
+ [v3_crypto_basic, v3_md5_auth, v3_sha_auth,
+ v3_des_priv]},
+ {standard_mibs, [],
+ [snmp_standard_mib, snmp_community_mib,
+ snmp_framework_mib, snmp_target_mib,
+ snmp_notification_mib, snmp_view_based_acm_mib]},
+ {standard_mibs_2, [],
+ [snmpv2_mib_2, snmp_community_mib_2,
+ snmp_framework_mib_2, snmp_target_mib_2,
+ snmp_notification_mib_2, snmp_view_based_acm_mib_2]},
+ {standard_mibs_3, [],
+ [snmpv2_mib_3, snmp_framework_mib_3, snmp_mpd_mib_3,
+ snmp_target_mib_3, snmp_notification_mib_3,
+ snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3]},
+ {reported_bugs, [],
+ [otp_1128, otp_1129, otp_1131, otp_1162, otp_1222,
+ otp_1298, otp_1331, otp_1338, otp_1342, otp_2776,
+ otp_2979, otp_3187, otp_3725]},
+ {reported_bugs_2, [],
+ [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
+ otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
+ otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2]},
+ {reported_bugs_3, [],
+ [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
+ otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
+ otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
+ otp_3542]},
+ {tickets, [], [{group, otp_4394}]},
+ {otp_4394, [], [otp_4394_test]}].
+
+init_per_group(otp_4394, Config) ->
+ init_otp_4394(Config);
+init_per_group(v2_inform, Config) ->
+ init_v2_inform(Config);
+init_per_group(multiple_reqs_2, Config) ->
+ init_mul(Config);
+init_per_group(multiple_reqs, Config) ->
+ init_mul(Config);
+init_per_group(test_multi_threaded, Config) ->
+ init_mt(Config);
+init_per_group(test_v3, Config) ->
+ init_v3(Config);
+init_per_group(test_v1_v2, Config) ->
+ init_v1_v2(Config);
+init_per_group(test_v2, Config) ->
+ init_v2(Config);
+init_per_group(test_v1, Config) ->
+ init_v1(Config);
+init_per_group(mib_storage_varm_mnesia, Config) ->
+ init_varm_mib_storage_mnesia(Config);
+init_per_group(mib_storage_varm_dets, Config) ->
+ init_varm_mib_storage_dets(Config);
+init_per_group(mib_storage_size_check_mnesia, Config) ->
+ init_size_check_msm(Config);
+init_per_group(mib_storage_size_check_dets, Config) ->
+ init_size_check_msd(Config);
+init_per_group(mib_storage_size_check_ets, Config) ->
+ init_size_check_mse(Config);
+init_per_group(mib_storage_mnesia, Config) ->
+ init_mib_storage_mnesia(Config);
+init_per_group(mib_storage_dets, Config) ->
+ init_mib_storage_dets(Config);
+init_per_group(mib_storage_ets, Config) ->
+ init_mib_storage_ets(Config);
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(otp_4394, Config) ->
+ finish_otp_4394(Config);
+end_per_group(v2_inform, Config) ->
+ finish_v2_inform(Config);
+end_per_group(multiple_reqs_2, Config) ->
+ finish_mul(Config);
+end_per_group(multiple_reqs, Config) ->
+ finish_mul(Config);
+end_per_group(test_multi_threaded, Config) ->
+ finish_mt(Config);
+end_per_group(test_v3, Config) ->
+ finish_v3(Config);
+end_per_group(test_v1_v2, Config) ->
+ finish_v1_v2(Config);
+end_per_group(test_v2, Config) ->
+ finish_v2(Config);
+end_per_group(test_v1, Config) ->
+ finish_v1(Config);
+end_per_group(mib_storage_varm_mnesia, Config) ->
+ finish_varm_mib_storage_mnesia(Config);
+end_per_group(mib_storage_varm_dets, Config) ->
+ finish_varm_mib_storage_dets(Config);
+end_per_group(mib_storage_size_check_mnesia, Config) ->
+ finish_size_check_msm(Config);
+end_per_group(mib_storage_size_check_dets, Config) ->
+ finish_size_check_msd(Config);
+end_per_group(mib_storage_size_check_ets, Config) ->
+ finish_size_check_mse(Config);
+end_per_group(mib_storage_mnesia, Config) ->
+ finish_mib_storage_mnesia(Config);
+end_per_group(mib_storage_dets, Config) ->
+ finish_mib_storage_dets(Config);
+end_per_group(mib_storage_ets, Config) ->
+ finish_mib_storage_ets(Config);
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:minutes(6)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
Config.
-cases() ->
- case ?OSTYPE() of
- vxworks ->
- %% No crypto app, so skip v3 testcases
- [
- app_info,
- test_v1, test_v2, test_v1_v2,
- test_multi_threaded,
- mib_storage,
- tickets];
- _Else ->
- [
- app_info,
- test_v1, test_v2, test_v1_v2, test_v3,
- test_multi_threaded,
- mib_storage,
- tickets
- ]
- end.
+cases() ->
+case ?OSTYPE() of
+ vxworks ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_multi_threaded},
+ {group, mib_storage}, {group, tickets}];
+ _Else ->
+ [app_info, {group, test_v1}, {group, test_v2},
+ {group, test_v1_v2}, {group, test_v3},
+ {group, test_multi_threaded}, {group, mib_storage},
+ {group, tickets}]
+end.
%%%-----------------------------------------------------------------
@@ -460,144 +586,56 @@ delete_mib_storage_mnesia_tables() ->
%% <base>, and a second version <base>_2. There may be several
%% versions as well, <base>_N.
%%-----------------------------------------------------------------
-mib_storage(suite) -> [
- mib_storage_ets,
- mib_storage_dets,
- mib_storage_mnesia,
- mib_storage_size_check_ets,
- mib_storage_size_check_dets,
- mib_storage_size_check_mnesia,
- mib_storage_varm_dets,
- mib_storage_varm_mnesia
- ].
-
-mib_storage_ets(suite) -> {req, [], {conf, init_mib_storage_ets,
- mib_storage_ets_cases(),
- finish_mib_storage_ets}}.
-
-mib_storage_dets(suite) -> {req, [], {conf, init_mib_storage_dets,
- mib_storage_dets_cases(),
- finish_mib_storage_dets}}.
-
-mib_storage_mnesia(suite) -> {req, [], {conf, init_mib_storage_mnesia,
- mib_storage_mnesia_cases(),
- finish_mib_storage_mnesia}}.
-
-mib_storage_size_check_ets(suite) ->
- {req, [], {conf,
- init_size_check_mse,
- mse_size_check_cases(),
- finish_size_check_mse}}.
-
-mib_storage_size_check_dets(suite) ->
- {req, [], {conf,
- init_size_check_msd,
- msd_size_check_cases(),
- finish_size_check_msd}}.
-
-mib_storage_size_check_mnesia(suite) ->
- {req, [], {conf,
- init_size_check_msm,
- msm_size_check_cases(),
- finish_size_check_msm}}.
-
-mib_storage_varm_dets(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_dets,
- varm_mib_storage_dets_cases(),
- finish_varm_mib_storage_dets}}.
-
-mib_storage_varm_mnesia(suite) ->
- {req, [], {conf,
- init_varm_mib_storage_mnesia,
- varm_mib_storage_mnesia_cases(),
- finish_varm_mib_storage_mnesia}}.
-
-mib_storage_ets_cases() ->
- [
- mse_simple,
- mse_v1_processing,
- mse_big,
- mse_big2,
- mse_loop_mib,
- mse_api,
- mse_sa_register,
- mse_v1_trap,
- mse_sa_error,
- mse_next_across_sa,
- mse_undo,
- mse_standard_mib,
- mse_community_mib,
- mse_framework_mib,
- mse_target_mib,
- mse_notification_mib,
- mse_view_based_acm_mib,
- mse_sparse_table,
- mse_me_of,
- mse_mib_of].
-
-mib_storage_dets_cases() ->
- [
- msd_simple,
- msd_v1_processing,
- msd_big,
- msd_big2,
- msd_loop_mib,
- msd_api,
- msd_sa_register,
- msd_v1_trap,
- msd_sa_error,
- msd_next_across_sa,
- msd_undo,
- msd_standard_mib,
- msd_community_mib,
- msd_framework_mib,
- msd_target_mib,
- msd_notification_mib,
- msd_view_based_acm_mib,
- msd_sparse_table,
- msd_me_of,
- msd_mib_of
- ].
-
-mib_storage_mnesia_cases() ->
- [
- msm_simple,
- msm_v1_processing,
- msm_big,
- msm_big2,
- msm_loop_mib,
- msm_api,
- msm_sa_register,
- msm_v1_trap,
- msm_sa_error,
- msm_next_across_sa,
- msm_undo,
- msm_standard_mib,
- msm_community_mib,
- msm_framework_mib,
- msm_target_mib,
- msm_notification_mib,
- msm_view_based_acm_mib,
- msm_sparse_table,
- msm_me_of,
- msm_mib_of
- ].
-
-mse_size_check_cases() ->
- [mse_size_check].
-
-msd_size_check_cases() ->
- [msd_size_check].
-
-msm_size_check_cases() ->
- [msm_size_check].
-
-varm_mib_storage_dets_cases() ->
- [msd_varm_mib_start].
-
-varm_mib_storage_mnesia_cases() ->
- [msm_varm_mib_start].
+
+
+
+
+
+
+
+
+
+mib_storage_ets_cases() ->
+[mse_simple, mse_v1_processing, mse_big, mse_big2,
+ mse_loop_mib, mse_api, mse_sa_register, mse_v1_trap,
+ mse_sa_error, mse_next_across_sa, mse_undo,
+ mse_standard_mib, mse_community_mib, mse_framework_mib,
+ mse_target_mib, mse_notification_mib,
+ mse_view_based_acm_mib, mse_sparse_table, mse_me_of,
+ mse_mib_of].
+
+mib_storage_dets_cases() ->
+[msd_simple, msd_v1_processing, msd_big, msd_big2,
+ msd_loop_mib, msd_api, msd_sa_register, msd_v1_trap,
+ msd_sa_error, msd_next_across_sa, msd_undo,
+ msd_standard_mib, msd_community_mib, msd_framework_mib,
+ msd_target_mib, msd_notification_mib,
+ msd_view_based_acm_mib, msd_sparse_table, msd_me_of,
+ msd_mib_of].
+
+mib_storage_mnesia_cases() ->
+[msm_simple, msm_v1_processing, msm_big, msm_big2,
+ msm_loop_mib, msm_api, msm_sa_register, msm_v1_trap,
+ msm_sa_error, msm_next_across_sa, msm_undo,
+ msm_standard_mib, msm_community_mib, msm_framework_mib,
+ msm_target_mib, msm_notification_mib,
+ msm_view_based_acm_mib, msm_sparse_table, msm_me_of,
+ msm_mib_of].
+
+mse_size_check_cases() ->
+[mse_size_check].
+
+msd_size_check_cases() ->
+[msd_size_check].
+
+msm_size_check_cases() ->
+[msm_size_check].
+
+varm_mib_storage_dets_cases() ->
+[msd_varm_mib_start].
+
+varm_mib_storage_mnesia_cases() ->
+[msm_varm_mib_start].
init_mib_storage_ets(Config) when list(Config) ->
?LOG("init_mib_storage_ets -> entry", []),
@@ -1099,20 +1137,14 @@ app_dir(App) ->
end.
-test_v1(suite) -> {req, [], {conf, init_v1, v1_cases(), finish_v1}}.
%v1_cases() -> [loop_mib];
-v1_cases() ->
- [simple,
- db_notify_client,
- v1_processing, big, big2, loop_mib,
- api, subagent, mnesia, multiple_reqs,
- sa_register, v1_trap, sa_error, next_across_sa, undo, reported_bugs,
- standard_mibs, sparse_table, cnt_64,
- opaque,
- % opaque].
-
- change_target_addr_config].
+v1_cases() ->
+[simple, db_notify_client, v1_processing, big, big2,
+ loop_mib, api, subagent, mnesia, {group, multiple_reqs},
+ sa_register, v1_trap, sa_error, next_across_sa, undo,
+ {group, reported_bugs}, {group, standard_mibs},
+ sparse_table, cnt_64, opaque, change_target_addr_config].
init_v1(Config) when list(Config) ->
?line SaNode = ?config(snmp_sa, Config),
@@ -1129,15 +1161,15 @@ finish_v1(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v2(suite) -> {req, [], {conf, init_v2, v2_cases(), finish_v2}}.
%v2_cases() -> [loop_mib_2];
-v2_cases() ->
- [simple_2, v2_processing, big_2, big2_2, loop_mib_2,
- api_2, subagent_2, mnesia_2,
- multiple_reqs_2, sa_register_2, v2_trap, v2_inform, sa_error_2,
- next_across_sa_2, undo_2, reported_bugs_2, standard_mibs_2,
- v2_types, implied, sparse_table_2, cnt_64_2, opaque_2, v2_caps].
+v2_cases() ->
+[simple_2, v2_processing, big_2, big2_2, loop_mib_2,
+ api_2, subagent_2, mnesia_2, {group, multiple_reqs_2},
+ sa_register_2, v2_trap, {group, v2_inform}, sa_error_2,
+ next_across_sa_2, undo_2, {group, reported_bugs_2},
+ {group, standard_mibs_2}, v2_types, implied,
+ sparse_table_2, cnt_64_2, opaque_2, v2_caps].
init_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1154,10 +1186,9 @@ finish_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v1_v2(suite) -> {req, [], {conf, init_v1_v2, v1_v2_cases(), finish_v1_v2}}.
-v1_v2_cases() ->
- [simple_bi].
+v1_v2_cases() ->
+[simple_bi].
init_v1_v2(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1174,16 +1205,16 @@ finish_v1_v2(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_v3(suite) -> {req, [], {conf, init_v3, v3_cases(), finish_v3}}.
%v3_cases() -> [loop_mib_3];
-v3_cases() ->
- [simple_3, v3_processing,
- big_3, big2_3, api_3, subagent_3, mnesia_3, loop_mib_3,
- multiple_reqs_3, sa_register_3, v3_trap, v3_inform, sa_error_3,
- next_across_sa_3, undo_3, reported_bugs_3, standard_mibs_3,
- v3_security,
- v2_types_3, implied_3, sparse_table_3, cnt_64_3, opaque_3, v2_caps_3].
+v3_cases() ->
+[simple_3, v3_processing, big_3, big2_3, api_3,
+ subagent_3, mnesia_3, loop_mib_3, multiple_reqs_3,
+ sa_register_3, v3_trap, v3_inform, sa_error_3,
+ next_across_sa_3, undo_3, {group, reported_bugs_3},
+ {group, standard_mibs_3}, {group, v3_security},
+ v2_types_3, implied_3, sparse_table_3, cnt_64_3,
+ opaque_3, v2_caps_3].
init_v3(Config) when list(Config) ->
%% Make sure crypto works, otherwise start_agent will fail
@@ -1221,10 +1252,9 @@ finish_v3(Config) when list(Config) ->
delete_files(C1),
lists:keydelete(vsn, 1, C1).
-test_multi_threaded(suite) -> {req, [], {conf, init_mt, mt_cases(), finish_mt}}.
-mt_cases() ->
- [multi_threaded, mt_trap].
+mt_cases() ->
+[multi_threaded, mt_trap].
init_mt(Config) when list(Config) ->
SaNode = ?config(snmp_sa, Config),
@@ -1739,21 +1769,19 @@ mnesia_2(X) -> mnesia(X).
mnesia_3(X) -> mnesia(X).
-multiple_reqs(suite) ->
- {req, [], {conf, init_mul, mul_cases(), finish_mul}}.
-mul_cases() ->
- [mul_get, mul_get_err, mul_next, mul_next_err, mul_set_err].
+mul_cases() ->
+[mul_get, mul_get_err, mul_next, mul_next_err,
+ mul_set_err].
-multiple_reqs_2(suite) ->
- {req, [], {conf, init_mul, mul_cases_2(), finish_mul}}.
multiple_reqs_3(_X) ->
{req, [], {conf, init_mul, mul_cases_3(), finish_mul}}.
-mul_cases_2() ->
- [mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2, mul_set_err_2].
+mul_cases_2() ->
+[mul_get_2, mul_get_err_2, mul_next_2, mul_next_err_2,
+ mul_set_err_2].
mul_cases_3() ->
@@ -1939,8 +1967,6 @@ v2_trap(Config) when list(Config) ->
v3_trap(X) ->
v2_trap(X).
-v2_inform(suite) ->
- {req, [], {conf, init_v2_inform, [v2_inform_i], finish_v2_inform}}.
v3_inform(_X) ->
%% v2_inform(X).
@@ -2112,7 +2138,6 @@ v3_processing(Config) when list(Config) ->
%% accomplished by the first inform sent. That one will generate a
%% report, which makes it in sync. The notification-generating
%% application times out, and send again. This time it'll work.
-v3_security(suite) -> [v3_crypto_basic, v3_md5_auth, v3_sha_auth, v3_des_priv].
v3_crypto_basic(suite) -> [];
v3_crypto_basic(_Config) ->
@@ -3591,22 +3616,8 @@ bad_return() ->
%%% Note that many of the functions in the standard mib is
%%% already tested by the normal tests.
%%%-----------------------------------------------------------------
-standard_mibs(suite) ->
- [snmp_standard_mib, snmp_community_mib,
- snmp_framework_mib,
- snmp_target_mib, snmp_notification_mib,
- snmp_view_based_acm_mib].
-
-standard_mibs_2(suite) ->
- [snmpv2_mib_2, snmp_community_mib_2,
- snmp_framework_mib_2,
- snmp_target_mib_2, snmp_notification_mib_2,
- snmp_view_based_acm_mib_2].
-
-standard_mibs_3(suite) ->
- [snmpv2_mib_3,snmp_framework_mib_3, snmp_mpd_mib_3,
- snmp_target_mib_3, snmp_notification_mib_3,
- snmp_view_based_acm_mib_3, snmp_user_based_sm_mib_3].
+
+
%%-----------------------------------------------------------------
%% For this test, the agent is configured for v1.
@@ -4527,27 +4538,12 @@ loop_it_2(Oid, N) ->
%%% Testing of reported bugs and other tickets.
%%%-----------------------------------------------------------------
-reported_bugs(suite) ->
- [otp_1128, otp_1129, otp_1131, otp_1162,
- otp_1222, otp_1298, otp_1331, otp_1338,
- otp_1342, otp_2776, otp_2979, otp_3187, otp_3725].
-reported_bugs_2(suite) ->
- [otp_1128_2, otp_1129_2, otp_1131_2, otp_1162_2,
- otp_1222_2, otp_1298_2, otp_1331_2, otp_1338_2,
- otp_1342_2, otp_2776_2, otp_2979_2, otp_3187_2].
-reported_bugs_3(suite) ->
- [otp_1128_3, otp_1129_3, otp_1131_3, otp_1162_3,
- otp_1222_3, otp_1298_3, otp_1331_3, otp_1338_3,
- otp_1342_3, otp_2776_3, otp_2979_3, otp_3187_3,
- otp_3542].
%% These are (ticket) test cases where the initiation has to be done
%% individually.
-tickets(suite) ->
- [otp_4394].
%%-----------------------------------------------------------------
%% Ticket: OTP-1128
@@ -4971,10 +4967,6 @@ otp_3725_test(MaNode) ->
%%-----------------------------------------------------------------
-otp_4394(suite) -> {req, [], {conf,
- init_otp_4394,
- [otp_4394_test],
- finish_otp_4394}}.
init_otp_4394(Config) when list(Config) ->
?DBG("init_otp_4394 -> entry with"
diff --git a/lib/snmp/test/snmp_app_test.erl b/lib/snmp/test/snmp_app_test.erl
index 5c5a5285a0..bc62c8d530 100644
--- a/lib/snmp/test/snmp_app_test.erl
+++ b/lib/snmp/test/snmp_app_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,8 +23,9 @@
-module(snmp_app_test).
-export([
- all/1, init_suite/1, fin_suite/1,
- init_per_testcase/2, fin_per_testcase/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2, init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
fields/1,
modules/1,
@@ -32,7 +33,7 @@
app_depend/1,
undef_funcs/1,
- start_and_stop/1,
+
start_and_stop_empty/1,
start_and_stop_with_agent/1,
start_and_stop_with_manager/1,
@@ -44,25 +45,34 @@
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- Cases =
- [
- fields,
- modules,
- exportall,
- app_depend,
- undef_funcs,
- start_and_stop
- ],
- {conf, init_suite, Cases, fin_suite}.
-
-init_suite(Config) when is_list(Config) ->
+all() ->
+Cases = [fields, modules, exportall, app_depend,
+ undef_funcs, {group, start_and_stop}],
+ Cases.
+
+groups() ->
+ [{start_and_stop, [],
+ [start_and_stop_empty, start_and_stop_with_agent,
+ start_and_stop_with_manager,
+ start_and_stop_with_agent_and_manager,
+ start_epmty_and_then_agent_and_manager_and_stop,
+ start_with_agent_and_then_manager_and_stop,
+ start_with_manager_and_then_agent_and_stop]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) when is_list(Config) ->
?DISPLAY_SUITE_INFO(),
PrivDir = ?config(priv_dir, Config),
TopDir = filename:join(PrivDir, app),
@@ -97,9 +107,9 @@ is_app(App) ->
{error, {invalid_format, Error}}
end.
-fin_suite(suite) -> [];
-fin_suite(doc) -> [];
-fin_suite(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
@@ -112,7 +122,7 @@ init_per_testcase(undef_funcs, Config) ->
init_per_testcase(_Case, Config) ->
Config.
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Config.
@@ -290,6 +300,25 @@ undef_funcs(Config) when is_list(Config) ->
xref:stop(XRef),
analyze_undefined_function_calls(Undefs, Mods, []).
+valid_undef(crypto = CalledMod) ->
+ case (catch CalledMod:version()) of
+ Version when is_list(Version) ->
+ %% The called module was crypto and the version
+ %% function returns a valid value.
+ %% This means that the function is
+ %% actually undefined...
+ true;
+ _ ->
+ %% The called module was crypto but the version
+ %% function does *not* return a valid value.
+ %% This means the crypto was not actually not
+ %% build, which is an case snmp handles.
+ false
+ end;
+valid_undef(_) ->
+ true.
+
+
analyze_undefined_function_calls([], _, []) ->
ok;
analyze_undefined_function_calls([], _, AppUndefs) ->
@@ -302,14 +331,25 @@ analyze_undefined_function_calls([{{Mod, _F, _A}, _C} = AppUndef|Undefs],
{Calling,Called} = AppUndef,
{Mod1,Func1,Ar1} = Calling,
{Mod2,Func2,Ar2} = Called,
- io:format("undefined function call: "
- "~n ~w:~w/~w calls ~w:~w/~w~n",
- [Mod1,Func1,Ar1,Mod2,Func2,Ar2]),
- analyze_undefined_function_calls(Undefs, AppModules,
- [AppUndef|AppUndefs]);
+ %% If the called module is crypto, then we will *not*
+ %% fail if crypto is not built (since crypto is actually
+ %% not built for all platforms)
+ case valid_undef(Mod2) of
+ true ->
+ io:format("undefined function call: "
+ "~n ~w:~w/~w calls ~w:~w/~w~n",
+ [Mod1,Func1,Ar1,Mod2,Func2,Ar2]),
+ analyze_undefined_function_calls(
+ Undefs, AppModules, [AppUndef|AppUndefs]);
+ false ->
+ io:format("skipping ~p (calling ~w:~w/~w)~n",
+ [Mod, Mod2, Func2, Ar2]),
+ analyze_undefined_function_calls(Undefs,
+ AppModules, AppUndefs)
+ end;
false ->
- io:format("dropping ~p~n", [Mod]),
- analyze_undefined_function_calls(Undefs, AppModules, AppUndefs)
+ io:format("dropping ~p~n", [Mod]),
+ analyze_undefined_function_calls(Undefs, AppModules, AppUndefs)
end.
%% This function is used simply to avoid cut-and-paste errors later...
@@ -319,16 +359,6 @@ undef_funcs_make_name(App, PostFix) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-start_and_stop(suite) ->
- [
- start_and_stop_empty,
- start_and_stop_with_agent,
- start_and_stop_with_manager,
- start_and_stop_with_agent_and_manager,
- start_epmty_and_then_agent_and_manager_and_stop,
- start_with_agent_and_then_manager_and_stop,
- start_with_manager_and_then_agent_and_stop
- ].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/snmp/test/snmp_appup_mgr.erl b/lib/snmp/test/snmp_appup_mgr.erl
index 271d6a2847..6648ce9dbe 100644
--- a/lib/snmp/test/snmp_appup_mgr.erl
+++ b/lib/snmp/test/snmp_appup_mgr.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/snmp_appup_test.erl b/lib/snmp/test/snmp_appup_test.erl
index 18509526cf..99994a2410 100644
--- a/lib/snmp/test/snmp_appup_test.erl
+++ b/lib/snmp/test/snmp_appup_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,32 +23,46 @@
-module(snmp_appup_test).
-export([
- all/1, init_suite/1, fin_suite/1,
- init_per_testcase/2, fin_per_testcase/2,
+ all/0,
+ groups/0, init_per_group/2, end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2,
appup_file/1
]).
--include("test_server.hrl").
+-compile({no_auto_import, [error/1]}).
+
+-include_lib("common_test/include/ct.hrl").
-include("snmp_test_lib.hrl").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
+all() ->
Cases =
[
appup_file
],
- {conf, init_suite, Cases, fin_suite}.
+ Cases.
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-init_suite(suite) -> [];
-init_suite(doc) -> [];
-init_suite(Config) when is_list(Config) ->
+init_per_suite(suite) -> [];
+init_per_suite(doc) -> [];
+init_per_suite(Config) when is_list(Config) ->
PrivDir = ?config(priv_dir, Config),
TopDir = filename:join(PrivDir, appup),
case file:make_dir(TopDir) of
@@ -76,9 +90,9 @@ file_name(App, Ext) ->
filename:join([LibDir, "ebin", atom_to_list(App) ++ Ext]).
-fin_suite(suite) -> [];
-fin_suite(doc) -> [];
-fin_suite(Config) when is_list(Config) ->
+end_per_suite(suite) -> [];
+end_per_suite(doc) -> [];
+end_per_suite(Config) when is_list(Config) ->
Config.
@@ -88,7 +102,7 @@ fin_suite(Config) when is_list(Config) ->
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
diff --git a/lib/snmp/test/snmp_compiler_test.erl b/lib/snmp/test/snmp_compiler_test.erl
index ad77b01362..2e6020ae7a 100644
--- a/lib/snmp/test/snmp_compiler_test.erl
+++ b/lib/snmp/test/snmp_compiler_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-include_lib("snmp/include/snmp_types.hrl").
@@ -37,15 +37,17 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2,
+ all/0,
+ groups/0, init_per_group/2, end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
description/1,
oid_conflicts/1,
imports/1,
module_identity/1,
+ agent_capabilities/1,
+ module_compliance/1,
- tickets/1,
otp_6150/1,
otp_8574/1,
otp_8595/1
@@ -78,9 +80,9 @@ init_per_testcase(_Case, Config) when is_list(Config) ->
MibDir = join(lists:reverse(["snmp_test_data"|RL])),
CompDir = join(Dir, "comp_dir/"),
?line ok = file:make_dir(CompDir),
- [{comp_dir, CompDir},{mib_dir, MibDir}|Config].
+ [{comp_dir, CompDir}, {mib_dir, MibDir} | Config].
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
CompDir = ?config(comp_dir, Config),
?line ok = ?DEL_DIR(CompDir),
lists:keydelete(comp_dir, 1, Config).
@@ -90,21 +92,27 @@ fin_per_testcase(_Case, Config) when is_list(Config) ->
%% Test case definitions
%%======================================================================
-all(suite) ->
+all() ->
[
- description,
- oid_conflicts,
- imports,
+ description,
+ oid_conflicts,
+ imports,
module_identity,
- tickets
+ agent_capabilities,
+ module_compliance,
+ {group, tickets}
].
-tickets(suite) ->
- [
- otp_6150,
- otp_8574,
- otp_8595
- ].
+groups() ->
+ [{tickets, [], [otp_6150, otp_8574, otp_8595]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%======================================================================
@@ -169,6 +177,88 @@ module_identity(Config) when is_list(Config) ->
?SKIP(not_yet_implemented).
+agent_capabilities(suite) ->
+ [];
+agent_capabilities(Config) when is_list(Config) ->
+ put(tname,agent_capabilities),
+ p("starting with Config: ~p~n", [Config]),
+
+ SnmpPrivDir = code:priv_dir(snmp),
+ SnmpMibsDir = join(SnmpPrivDir, "mibs"),
+ OtpMibsPrivDir = code:priv_dir(otp_mibs),
+ OtpMibsMibsDir = join(OtpMibsPrivDir, "mibs"),
+ Dir = ?config(mib_dir, Config),
+ AcMib = join(Dir,"AC-TEST-MIB.mib"),
+ ?line {ok, MibFile1} = snmpc:compile(AcMib, [options,
+ version,
+ {i, [SnmpMibsDir, OtpMibsMibsDir]},
+ {outdir, Dir},
+ {verbosity, trace}]),
+ ?line {ok, Mib1} = snmp_misc:read_mib(MibFile1),
+ ?line {ok, MibFile2} = snmpc:compile(AcMib, [options,
+ version,
+ agent_capabilities,
+ {i, [SnmpMibsDir, OtpMibsMibsDir]},
+ {outdir, Dir},
+ {verbosity, trace}]),
+ ?line {ok, Mib2} = snmp_misc:read_mib(MibFile2),
+ MEDiff = Mib2#mib.mes -- Mib1#mib.mes,
+ %% This is a rather pathetic test, but it is somthing...
+ io:format("agent_capabilities -> "
+ "~n MEDiff: ~p"
+ "~n Mib1: ~p"
+ "~n Mib2: ~p"
+ "~n", [MEDiff, Mib1, Mib2]),
+ case length(MEDiff) of
+ 2 ->
+ ok;
+ _BadLen ->
+ exit({unexpected_mes, MEDiff})
+ end,
+ ok.
+
+
+module_compliance(suite) ->
+ [];
+module_compliance(Config) when is_list(Config) ->
+ put(tname,module_compliance),
+ p("starting with Config: ~p~n", [Config]),
+
+ SnmpPrivDir = code:priv_dir(snmp),
+ SnmpMibsDir = join(SnmpPrivDir, "mibs"),
+ OtpMibsPrivDir = code:priv_dir(otp_mibs),
+ OtpMibsMibsDir = join(OtpMibsPrivDir, "mibs"),
+ Dir = ?config(mib_dir, Config),
+ AcMib = join(Dir,"MC-TEST-MIB.mib"),
+ ?line {ok, MibFile1} = snmpc:compile(AcMib, [options,
+ version,
+ {i, [SnmpMibsDir, OtpMibsMibsDir]},
+ {outdir, Dir},
+ {verbosity, trace}]),
+ ?line {ok, Mib1} = snmp_misc:read_mib(MibFile1),
+ ?line {ok, MibFile2} = snmpc:compile(AcMib, [options,
+ version,
+ module_compliance,
+ {i, [SnmpMibsDir, OtpMibsMibsDir]},
+ {outdir, Dir},
+ {verbosity, trace}]),
+ ?line {ok, Mib2} = snmp_misc:read_mib(MibFile2),
+ MEDiff = Mib2#mib.mes -- Mib1#mib.mes,
+ %% This is a rather pathetic test, but it is somthing...
+ io:format("agent_capabilities -> "
+ "~n MEDiff: ~p"
+ "~n Mib1: ~p"
+ "~n Mib2: ~p"
+ "~n", [MEDiff, Mib1, Mib2]),
+ case length(MEDiff) of
+ 1 ->
+ ok;
+ _BadLen ->
+ exit({unexpected_mes, MEDiff})
+ end,
+ ok.
+
+
otp_6150(suite) ->
[];
otp_6150(Config) when is_list(Config) ->
@@ -257,7 +347,7 @@ LAST-UPDATED \"0005290000Z\"
Ericsson Utvecklings AB
Open System
Box 1505
-SE-125 25 �LVSJ�\"
+SE-125 25 ÄLVSJÖ\"
DESCRIPTION
\" Objects for management \"
diff --git a/lib/snmp/test/snmp_conf_test.erl b/lib/snmp/test/snmp_conf_test.erl
index d2f9631947..c4341d8d7e 100644
--- a/lib/snmp/test/snmp_conf_test.erl
+++ b/lib/snmp/test/snmp_conf_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-include_lib("snmp/include/STANDARD-MIB.hrl").
@@ -37,8 +37,8 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
check_mandatory/1,
check_integer1/1,
@@ -80,32 +80,28 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
%%======================================================================
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- check_mandatory,
- check_integer1,
- check_integer2,
- check_string1,
- check_string2,
- check_atom,
- check_ip,
- check_taddress,
- check_packet_size,
- check_oid,
- check_sec_model1,
- check_sec_model2,
- check_sec_level,
- check_timer,
-
- read,
- read_files
- ].
+all() ->
+[check_mandatory, check_integer1, check_integer2,
+ check_string1, check_string2, check_atom, check_ip,
+ check_taddress, check_packet_size, check_oid,
+ check_sec_model1, check_sec_model2, check_sec_level,
+ check_timer, read, read_files].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%======================================================================
diff --git a/lib/snmp/test/snmp_log_test.erl b/lib/snmp/test/snmp_log_test.erl
index 91bdc3e849..b692017407 100644
--- a/lib/snmp/test/snmp_log_test.erl
+++ b/lib/snmp/test/snmp_log_test.erl
@@ -29,7 +29,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
@@ -40,19 +40,19 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- init_per_testcase/2, fin_per_testcase/2,
+ init_per_testcase/2, end_per_testcase/2,
- all/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
open_and_close/1,
- open_write_and_close/1,
+
open_write_and_close1/1,
open_write_and_close2/1,
open_write_and_close3/1,
open_write_and_close4/1,
- log_to_io/1,
+
log_to_io1/1,
log_to_io2/1,
- log_to_txt/1,
+
log_to_txt1/1,
log_to_txt2/1,
log_to_txt3/1
@@ -97,7 +97,7 @@ init_per_testcase(Case, Config) when is_list(Config) ->
Dog = ?WD_START(?MINS(5)),
[{log_dir, CaseDir}, {watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
%% Leave the dirs created above (enable debugging of the test case(s))
Dog = ?config(watchdog, Config),
?WD_STOP(Dog),
@@ -108,37 +108,30 @@ fin_per_testcase(_Case, Config) when is_list(Config) ->
%% Test case definitions
%%======================================================================
%% ?SKIP(not_yet_implemented).
-all(suite) ->
- [
- open_and_close,
- open_write_and_close,
- log_to_io,
- log_to_txt
- ].
-
-
-open_write_and_close(suite) ->
- [
- open_write_and_close1,
- open_write_and_close2,
- open_write_and_close3,
- open_write_and_close4
- ].
-
-
-log_to_io(suite) ->
- [
- log_to_io1,
- log_to_io2
- ].
-
-
-log_to_txt(suite) ->
- [
- log_to_txt1,
- log_to_txt2,
- log_to_txt3
- ].
+all() ->
+[open_and_close, {group, open_write_and_close},
+ {group, log_to_io}, {group, log_to_txt}].
+
+groups() ->
+ [{open_write_and_close, [],
+ [open_write_and_close1, open_write_and_close2,
+ open_write_and_close3, open_write_and_close4]},
+ {log_to_io, [], [log_to_io1, log_to_io2]},
+ {log_to_txt, [],
+ [log_to_txt1, log_to_txt2, log_to_txt3]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
+
+
+
%%======================================================================
diff --git a/lib/snmp/test/snmp_manager_config_test.erl b/lib/snmp/test/snmp_manager_config_test.erl
index d5dc1387f7..4498d506f3 100644
--- a/lib/snmp/test/snmp_manager_config_test.erl
+++ b/lib/snmp/test/snmp_manager_config_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-include_lib("snmp/src/manager/snmpm_usm.hrl").
@@ -42,10 +42,10 @@
%% -compile(export_all).
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
- start_and_stop/1,
+
simple_start_and_stop/1,
start_without_mandatory_opts1/1,
@@ -58,36 +58,36 @@
start_with_invalid_agents_conf_file1/1,
start_with_invalid_usm_conf_file1/1,
- normal_op/1,
+
- system/1,
+
simple_system_op/1,
- users/1,
+
register_user_using_file/1,
register_user_using_function/1,
register_user_failed_using_function1/1,
- agents/1,
+
register_agent_using_file/1,
register_agent_using_function/1,
register_agent_failed_using_function1/1,
- usm_users/1,
+
register_usm_user_using_file/1,
register_usm_user_using_function/1,
register_usm_user_failed_using_function1/1,
update_usm_user_info/1,
- counter/1,
+
create_and_increment/1,
- stats_counter/1,
+
stats_create_and_increment/1,
- tickets/1,
+
otp_7219/1,
- otp_8395/1,
+
otp_8395_1/1,
otp_8395_2/1,
otp_8395_3/1,
@@ -150,8 +150,8 @@ init_per_testcase(Case, Config) when is_list(Config) ->
{manager_log_dir, MgrLogDir} | Config].
-fin_per_testcase(Case, Config) when is_list(Config) ->
- p("fin_per_testcase -> Case: ~p", [Case]),
+end_per_testcase(Case, Config) when is_list(Config) ->
+ p("end_per_testcase -> Case: ~p", [Case]),
%% The cleanup is removed due to some really discusting NFS behaviour...
%% CaseTopDir = ?config(manager_dir, Config),
%% ?line ok = ?DEL_DIR(CaseTopDir),
@@ -163,33 +163,60 @@ fin_per_testcase(Case, Config) when is_list(Config) ->
%%======================================================================
% all(doc) ->
% "The top snmp manager config test case";
-all(suite) ->
- [
- start_and_stop,
- normal_op,
- tickets
- ].
+all() ->
+[{group, start_and_stop}, {group, normal_op},
+ {group, tickets}].
+
+groups() ->
+ [{start_and_stop, [],
+ [simple_start_and_stop,
+ start_without_mandatory_opts1,
+ start_without_mandatory_opts2,
+ start_with_all_valid_opts, start_with_unknown_opts,
+ start_with_incorrect_opts,
+ start_with_invalid_manager_conf_file1,
+ start_with_invalid_users_conf_file1,
+ start_with_invalid_agents_conf_file1,
+ start_with_invalid_usm_conf_file1]},
+ {normal_op, [],
+ [{group, system},
+ {group, agents},
+ {group, users},
+ {group, usm_users},
+ {group, counter},
+ {group, stats_counter}]},
+ {system, [], [simple_system_op]},
+ {users, [],
+ [register_user_using_file,
+ register_user_using_function,
+ register_user_failed_using_function1]},
+ {agents, [],
+ [register_agent_using_file,
+ register_agent_using_function,
+ register_agent_failed_using_function1]},
+ {usm_users, [],
+ [register_usm_user_using_file,
+ register_usm_user_using_function,
+ register_usm_user_failed_using_function1,
+ update_usm_user_info]},
+ {counter, [], [create_and_increment]},
+ {stats_counter, [], [stats_create_and_increment]},
+ {tickets, [], [otp_7219, {group, otp_8395}]},
+ {otp_8395, [],
+ [otp_8395_1, otp_8395_2, otp_8395_3, otp_8395_4]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%======================================================================
%% Test functions
%%======================================================================
-start_and_stop(doc) ->
- "A collection of start and stop tests";
-start_and_stop(suite) ->
- [
- simple_start_and_stop,
- start_without_mandatory_opts1,
- start_without_mandatory_opts2,
- start_with_all_valid_opts,
- start_with_unknown_opts,
- start_with_incorrect_opts,
- start_with_invalid_manager_conf_file1,
- start_with_invalid_users_conf_file1,
- start_with_invalid_agents_conf_file1,
- start_with_invalid_usm_conf_file1
- ].
%%
@@ -794,7 +821,10 @@ start_with_invalid_users_conf_file1(Conf) when is_list(Conf) ->
p("start"),
process_flag(trap_exit, true),
ConfDir = ?config(manager_conf_dir, Conf),
- DbDir = ?config(manager_db_dir, Conf),
+ DbDir = ?config(manager_db_dir, Conf),
+
+ verify_dir_existing(conf, ConfDir),
+ verify_dir_existing(db, DbDir),
Opts = [{versions, [v1]},
{config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}],
@@ -895,7 +925,10 @@ start_with_invalid_agents_conf_file1(Conf) when is_list(Conf) ->
p("start"),
process_flag(trap_exit, true),
ConfDir = ?config(manager_conf_dir, Conf),
- DbDir = ?config(manager_db_dir, Conf),
+ DbDir = ?config(manager_db_dir, Conf),
+
+ verify_dir_existing(conf, ConfDir),
+ verify_dir_existing(db, DbDir),
Opts = [{versions, [v1]},
{config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}],
@@ -1641,29 +1674,12 @@ start_with_invalid_usm_conf_file1(Conf) when is_list(Conf) ->
%% ---
%%
-normal_op(doc) ->
- "A collection of tests for normal operation";
-normal_op(suite) ->
- [
- system,
- agents,
- users,
- usm_users,
- counter,
- stats_counter
- ].
%%
%% ---
%%
-system(doc) ->
- "Various system related operations with the snmp manager config";
-system(suite) ->
- [
- simple_system_op
- ].
simple_system_op(suite) -> [];
simple_system_op(doc) ->
@@ -1702,14 +1718,6 @@ simple_system_op(Conf) when is_list(Conf) ->
%% ---
%%
-users(doc) ->
- "Various users related operations with the snmp manager config";
-users(suite) ->
- [
- register_user_using_file,
- register_user_using_function,
- register_user_failed_using_function1
- ].
%%
@@ -1764,14 +1772,6 @@ register_user_failed_using_function1(Conf) when is_list(Conf) ->
%% ---
%%
-agents(doc) ->
- "Various agents related operations with the snmp manager config";
-agents(suite) ->
- [
- register_agent_using_file,
- register_agent_using_function,
- register_agent_failed_using_function1
- ].
%%
@@ -1950,15 +1950,6 @@ register_agent_failed_using_function1(Conf) when is_list(Conf) ->
%% ---
%%
-usm_users(doc) ->
- "Various USM users related operations with the snmp manager config";
-usm_users(suite) ->
- [
- register_usm_user_using_file,
- register_usm_user_using_function,
- register_usm_user_failed_using_function1,
- update_usm_user_info
- ].
%%
@@ -2042,7 +2033,6 @@ register_usm_user_using_file(Conf) when is_list(Conf) ->
%% --
p("done"),
ok.
-%% ?SKIP(not_yet_implemented).
%%
@@ -2208,12 +2198,6 @@ update_usm_user_info(Conf) when is_list(Conf) ->
%% ---
%%
-counter(doc) ->
- "Various counter related operations with the snmp manager config";
-counter(suite) ->
- [
- create_and_increment
- ].
%%
@@ -2258,13 +2242,6 @@ create_and_increment(Conf) when is_list(Conf) ->
%% ---
%%
-stats_counter(doc) ->
- "Various statistic counter related operations with the "
- "snmp manager config";
-stats_counter(suite) ->
- [
- stats_create_and_increment
- ].
%%
@@ -2323,11 +2300,6 @@ loop(N, _, F) when (N > 0) andalso is_function(F) ->
%% Ticket test-cases
%%======================================================================
-tickets(suite) ->
- [
- otp_7219,
- otp_8395
- ].
otp_7219(suite) ->
@@ -2379,13 +2351,6 @@ otp_7219(Config) when is_list(Config) ->
-otp_8395(suite) ->
- [
- otp_8395_1,
- otp_8395_2,
- otp_8395_3,
- otp_8395_4
- ].
otp_8395_1(suite) -> [];
otp_8395_1(doc) ->
@@ -2696,9 +2661,21 @@ write_usm_conf2(Dir, Str) ->
write_conf_file(Dir, File, Str) ->
- ?line {ok, Fd} = file:open(filename:join(Dir, File), write),
- ?line ok = io:format(Fd, "~s", [Str]),
- file:close(Fd).
+ case file:open(filename:join(Dir, File), write) of
+ {ok, Fd} ->
+ ?line ok = io:format(Fd, "~s", [Str]),
+ file:close(Fd);
+ {error, Reason} ->
+ Info =
+ [{dir, Dir, case (catch file:read_file_info(Dir)) of
+ {ok, FI} ->
+ FI;
+ _ ->
+ undefined
+ end},
+ {file, File}],
+ exit({failed_writing_conf_file, Info, Reason})
+ end.
maybe_start_crypto() ->
@@ -2724,6 +2701,17 @@ maybe_stop_crypto() ->
%% ------
+verify_dir_existing(DirName, Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit({non_existing_dir, DirName, Dir, Reason})
+ end.
+
+
+%% ------
+
str(X) ->
lists:flatten(io_lib:format("~w", [X])).
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 518b8b34de..50836db731 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -31,7 +31,7 @@
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
-include("snmp_test_data/Test2.hrl").
@@ -43,10 +43,10 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
- start_and_stop_tests/1,
+
simple_start_and_stop1/1,
simple_start_and_stop2/1,
simple_start_and_monitor_crash1/1,
@@ -54,49 +54,49 @@
notify_started01/1,
notify_started02/1,
- user_tests/1,
+
register_user1/1,
- agent_tests/1,
+
register_agent1/1,
register_agent2/1,
- misc_tests/1,
+
info/1,
- request_tests/1,
+
- get_tests/1,
+
simple_sync_get1/1,
simple_sync_get2/1,
simple_async_get1/1,
simple_async_get2/1,
- get_next_tests/1,
+
simple_sync_get_next1/1,
simple_sync_get_next2/1,
simple_async_get_next1/1,
simple_async_get_next2/1,
- set_tests/1,
+
simple_sync_set1/1,
simple_sync_set2/1,
simple_async_set1/1,
simple_async_set2/1,
- bulk_tests/1,
+
simple_sync_get_bulk1/1,
simple_sync_get_bulk2/1,
simple_async_get_bulk1/1,
simple_async_get_bulk2/1,
- misc_request_tests/1,
+
misc_async1/1,
misc_async2/1,
discovery/1,
- event_tests/1,
+
trap1/1,
trap2/1,
@@ -109,10 +109,10 @@
report/1,
- tickets/1,
- otp8015/1,
+
+
otp8015_1/1,
- otp8395/1,
+
otp8395_1/1
]).
@@ -289,18 +289,18 @@ init_per_testcase3(Case, Config) ->
Config
end.
-fin_per_testcase(Case, Config) when is_list(Config) ->
+end_per_testcase(Case, Config) when is_list(Config) ->
?DBG("fin [~w] Nodes [1]: ~p", [Case, erlang:nodes()]),
Dog = ?config(watchdog, Config),
?WD_STOP(Dog),
Conf1 = lists:keydelete(watchdog, 1, Config),
- Conf2 = fin_per_testcase2(Case, Conf1),
+ Conf2 = end_per_testcase2(Case, Conf1),
?DBG("fin [~w] Nodes [2]: ~p", [Case, erlang:nodes()]),
%% TopDir = ?config(top_dir, Conf2),
%% ?DEL_DIR(TopDir),
Conf2.
-fin_per_testcase2(Case, Config) ->
+end_per_testcase2(Case, Config) ->
OldApiCases =
[
simple_sync_get1,
@@ -359,118 +359,64 @@ fin_per_testcase2(Case, Config) ->
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- start_and_stop_tests,
- misc_tests,
- user_tests,
- agent_tests,
- request_tests,
- event_tests,
- discovery,
- tickets
- ].
-
-start_and_stop_tests(suite) ->
- [
- simple_start_and_stop1,
- simple_start_and_stop2,
- simple_start_and_monitor_crash1,
- simple_start_and_monitor_crash2,
- notify_started01,
- notify_started02
- ].
-
-misc_tests(suite) ->
- [
- info
- ].
-
-user_tests(suite) ->
- [
- register_user1
- ].
-
-agent_tests(suite) ->
- [
- register_agent1,
- register_agent2
- ].
-
-request_tests(suite) ->
- [
- get_tests,
- get_next_tests,
- set_tests,
- bulk_tests,
- misc_request_tests
- ].
-
-get_tests(suite) ->
- [
- simple_sync_get1,
- simple_sync_get2,
- simple_async_get1,
- simple_async_get2
- ].
-
-get_next_tests(suite) ->
- [
- simple_sync_get_next1,
- simple_sync_get_next2,
- simple_async_get_next1,
- simple_async_get_next2
- ].
-
-set_tests(suite) ->
- [
- simple_sync_set1,
- simple_sync_set2,
- simple_async_set1,
- simple_async_set2
- ].
-
-bulk_tests(suite) ->
- [
- simple_sync_get_bulk1,
- simple_sync_get_bulk2,
- simple_async_get_bulk1,
- simple_async_get_bulk2
- ].
-
-misc_request_tests(suite) ->
- [
- misc_async1,
- misc_async2
- ].
-
-event_tests(suite) ->
- [
- trap1,
- trap2,
- inform1,
- inform2,
- inform3,
- inform4,
- inform_swarm,
- report
- ].
-
-tickets(suite) ->
- [
- otp8015,
- otp8395
- ].
-
-otp8015(suite) ->
- [
- otp8015_1
- ].
-
-otp8395(suite) ->
- [
- otp8395_1
- ].
+all() ->
+[{group, start_and_stop_tests}, {group, misc_tests},
+ {group, user_tests}, {group, agent_tests},
+ {group, request_tests}, {group, event_tests}, discovery,
+ {group, tickets}].
+
+groups() ->
+ [{start_and_stop_tests, [],
+ [simple_start_and_stop1, simple_start_and_stop2,
+ simple_start_and_monitor_crash1,
+ simple_start_and_monitor_crash2, notify_started01,
+ notify_started02]},
+ {misc_tests, [], [info]},
+ {user_tests, [], [register_user1]},
+ {agent_tests, [], [register_agent1, register_agent2]},
+ {request_tests, [],
+ [{group, get_tests}, {group, get_next_tests},
+ {group, set_tests}, {group, bulk_tests},
+ {group, misc_request_tests}]},
+ {get_tests, [],
+ [simple_sync_get1, simple_sync_get2, simple_async_get1,
+ simple_async_get2]},
+ {get_next_tests, [],
+ [simple_sync_get_next1, simple_sync_get_next2,
+ simple_async_get_next1, simple_async_get_next2]},
+ {set_tests, [],
+ [simple_sync_set1, simple_sync_set2, simple_async_set1,
+ simple_async_set2]},
+ {bulk_tests, [],
+ [simple_sync_get_bulk1, simple_sync_get_bulk2,
+ simple_async_get_bulk1, simple_async_get_bulk2]},
+ {misc_request_tests, [], [misc_async1, misc_async2]},
+ {event_tests, [],
+ [trap1, trap2, inform1, inform2, inform3, inform4,
+ inform_swarm, report]},
+ {tickets, [], [{group, otp8015}, {group, otp8395}]},
+ {otp8015, [], [otp8015_1]}, {otp8395, [], [otp8395_1]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
%%======================================================================
@@ -795,6 +741,35 @@ notify_started02(suite) -> [];
notify_started02(Config) when is_list(Config) ->
process_flag(trap_exit, true),
put(tname,ns02),
+
+ %% <CONDITIONAL-SKIP>
+ %% The point of this is to catch machines running
+ %% SLES9 (2.6.5)
+ LinuxVersionVerify =
+ fun() ->
+ case os:cmd("uname -m") of
+ "i686" ++ _ ->
+%% io:format("found an i686 machine, "
+%% "now check version~n", []),
+ case os:version() of
+ {2, 6, Rev} when Rev >= 16 ->
+ true;
+ {2, Min, _} when Min > 6 ->
+ true;
+ {Maj, _, _} when Maj > 2 ->
+ true;
+ _ ->
+ false
+ end;
+ _ ->
+ true
+ end
+ end,
+ Skippable = [{unix, [{linux, LinuxVersionVerify}]}],
+ Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
+ ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
+ %% </CONDITIONAL-SKIP>
+
p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl
index 07b56bde39..b0e192344d 100644
--- a/lib/snmp/test/snmp_manager_user.erl
+++ b/lib/snmp/test/snmp_manager_user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/snmp_manager_user_old.erl b/lib/snmp/test/snmp_manager_user_old.erl
index b53514d699..edffc80dd4 100755
--- a/lib/snmp/test/snmp_manager_user_old.erl
+++ b/lib/snmp/test/snmp_manager_user_old.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/snmp_manager_user_test.erl b/lib/snmp/test/snmp_manager_user_test.erl
index 24ed3b0b73..fefa1ad713 100644
--- a/lib/snmp/test/snmp_manager_user_test.erl
+++ b/lib/snmp/test/snmp_manager_user_test.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -26,7 +26,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("snmp_test_lib.hrl").
@@ -36,10 +36,10 @@
%% -compile(export_all).
-export([
- all/1,
- init_per_testcase/2, fin_per_testcase/2,
+all/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+
- register_user/1,
simple_register_and_unregister1/1,
simple_register_and_unregister2/1,
simple_register_and_unregister3/1,
@@ -62,7 +62,7 @@
register_monitor_request_and_crash3/1,
register_monitor_request_and_crash4/1,
- tickets/1,
+
otp7902/1
]).
@@ -123,8 +123,8 @@ init_per_testcase(Case, Config) when is_list(Config) ->
{manager_log_dir, MgrLogDir} | Config].
-fin_per_testcase(Case, Config) when is_list(Config) ->
- p("fin_per_testcase -> Case: ~p", [Case]),
+end_per_testcase(Case, Config) when is_list(Config) ->
+ p("end_per_testcase -> Case: ~p", [Case]),
% MgrTopDir = ?config(manager_dir, Config),
% ?DEL_DIR(MgrTopDir),
Config.
@@ -134,42 +134,41 @@ fin_per_testcase(Case, Config) when is_list(Config) ->
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- register_user,
- tickets
- ].
-
-register_user(suite) ->
- [
- simple_register_and_unregister1,
- simple_register_and_unregister2,
- simple_register_and_unregister3,
- register_and_crash1,
- register_and_crash2,
- register_and_crash3,
- register_request_and_crash1,
- register_request_and_crash2,
- register_request_and_crash3,
- simple_register_monitor_and_unregister1,
- simple_register_monitor_and_unregister2,
- simple_register_monitor_and_unregister3,
- register_monitor_and_crash1,
- register_monitor_and_crash2,
- register_monitor_and_crash3,
- register_monitor_and_crash4,
- register_monitor_and_crash5,
- register_monitor_request_and_crash1,
- register_monitor_request_and_crash2,
- register_monitor_request_and_crash3,
- register_monitor_request_and_crash4
- ].
-
-
-tickets(suite) ->
- [
- otp7902
- ].
+all() ->
+[{group, register_user}, {group, tickets}].
+
+groups() ->
+ [{register_user, [],
+ [simple_register_and_unregister1,
+ simple_register_and_unregister2,
+ simple_register_and_unregister3, register_and_crash1,
+ register_and_crash2, register_and_crash3,
+ register_request_and_crash1,
+ register_request_and_crash2,
+ register_request_and_crash3,
+ simple_register_monitor_and_unregister1,
+ simple_register_monitor_and_unregister2,
+ simple_register_monitor_and_unregister3,
+ register_monitor_and_crash1,
+ register_monitor_and_crash2,
+ register_monitor_and_crash3,
+ register_monitor_and_crash4,
+ register_monitor_and_crash5,
+ register_monitor_request_and_crash1,
+ register_monitor_request_and_crash2,
+ register_monitor_request_and_crash3,
+ register_monitor_request_and_crash4]},
+ {tickets, [], [otp7902]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+
+
%%======================================================================
@@ -822,10 +821,39 @@ register_monitor_and_crash3(doc) ->
"Start a single user process, "
"register-monitor one user and register one user, "
"crash the single user process.";
-register_monitor_and_crash3(Conf) when is_list(Conf) ->
+register_monitor_and_crash3(Conf) when is_list(Conf) ->
+ process_flag(trap_exit, true),
put(tname,rlac3),
+
+ %% <CONDITIONAL-SKIP>
+ %% The point of this is to catch machines running
+ %% SLES9 (2.6.5)
+ LinuxVersionVerify =
+ fun() ->
+ case os:cmd("uname -m") of
+ "i686" ++ _ ->
+%% io:format("found an i686 machine, "
+%% "now check version~n", []),
+ case os:version() of
+ {2, 6, Rev} when Rev >= 16 ->
+ true;
+ {2, Min, _} when Min > 6 ->
+ true;
+ {Maj, _, _} when Maj > 2 ->
+ true;
+ _ ->
+ false
+ end;
+ _ ->
+ true
+ end
+ end,
+ Skippable = [{unix, [{linux, LinuxVersionVerify}]}],
+ Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
+ ?NON_PC_TC_MAYBE_SKIP(Conf, Condition),
+ %% </CONDITIONAL-SKIP>
+
p("start"),
- process_flag(trap_exit, true),
ConfDir = ?config(manager_conf_dir, Conf),
DbDir = ?config(manager_db_dir, Conf),
diff --git a/lib/snmp/test/snmp_manager_user_test_lib.erl b/lib/snmp/test/snmp_manager_user_test_lib.erl
index a49fe93178..bf8fff7c4c 100644
--- a/lib/snmp/test/snmp_manager_user_test_lib.erl
+++ b/lib/snmp/test/snmp_manager_user_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/snmp_note_store_test.erl b/lib/snmp/test/snmp_note_store_test.erl
index 8686a47468..24ba88f986 100644
--- a/lib/snmp/test/snmp_note_store_test.erl
+++ b/lib/snmp/test/snmp_note_store_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,7 +25,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("snmp_test_lib.hrl").
@@ -33,8 +33,8 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- init_per_testcase/2, fin_per_testcase/2,
- all/1,
+ init_per_testcase/2, end_per_testcase/2,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
start_and_stop/1,
notes/1,
info/1,
@@ -63,20 +63,24 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
%%======================================================================
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- start_and_stop,
- notes,
- info,
- garbage_in
-
- ].
+all() ->
+[start_and_stop, notes, info, garbage_in].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%======================================================================
diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl
index 6dc5b779aa..ef510ad62e 100644
--- a/lib/snmp/test/snmp_pdus_test.erl
+++ b/lib/snmp/test/snmp_pdus_test.erl
@@ -25,7 +25,7 @@
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("snmp_test_lib.hrl").
-include_lib("snmp/include/snmp_types.hrl").
@@ -34,11 +34,11 @@
%% External exports
%%----------------------------------------------------------------------
-export([
- all/1,
- tickets/1,
+ all/0,groups/0,init_per_group/2,end_per_group/2,
+
otp7575/1,
otp8563/1,
- init_per_testcase/2, fin_per_testcase/2
+ init_per_testcase/2, end_per_testcase/2
]).
@@ -64,23 +64,26 @@
init_per_testcase(_Case, Config) when is_list(Config) ->
Config.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
Config.
%%======================================================================
%% Test case definitions
%%======================================================================
-all(suite) ->
- [
- tickets
- ].
-
-tickets(suite) ->
- [
- otp7575,
- otp8563
- ].
+all() ->
+[{group, tickets}].
+
+groups() ->
+ [{tickets, [], [otp7575, otp8563]}].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
diff --git a/lib/snmp/test/snmp_test_data/AC-TEST-MIB.mib b/lib/snmp/test/snmp_test_data/AC-TEST-MIB.mib
new file mode 100644
index 0000000000..58defbe1cf
--- /dev/null
+++ b/lib/snmp/test/snmp_test_data/AC-TEST-MIB.mib
@@ -0,0 +1,131 @@
+--
+-- AC-TEST-MIB.mib
+-- MIB generated by MG-SOFT Visual MIB Builder Version 5.0 Build 250
+-- Tuesday, November 30, 2010 at 23:03:18
+--
+
+ AC-TEST-MIB DEFINITIONS ::= BEGIN
+
+ IMPORTS
+ otpExpr
+ FROM OTP-REG
+ OBJECT-GROUP, AGENT-CAPABILITIES
+ FROM SNMPv2-CONF
+ Integer32, OBJECT-TYPE, MODULE-IDENTITY, OBJECT-IDENTITY
+ FROM SNMPv2-SMI;
+
+
+ acTestModule MODULE-IDENTITY
+ LAST-UPDATED "201011302230Z" -- November 30, 2010 at 22:30 GMT
+ ORGANIZATION
+ "Ac Test Co."
+ CONTACT-INFO
+ DESCRIPTION
+ "Ac Test module."
+ ::= { reg 1 }
+
+
+
+--
+-- Node definitions
+--
+
+ acTest OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Test area."
+ ::= { otpExpr 4321 }
+
+
+ reg OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Registrations."
+ ::= { acTest 1 }
+
+
+ mib OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Objects."
+ ::= { acTest 2 }
+
+
+ someObject OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Description."
+ ::= { mib 1 }
+
+
+ oneMore OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Description."
+ ::= { mib 2 }
+
+
+ grp OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Groups
+ ."
+ ::= { acTest 3 }
+
+
+ basicGrp OBJECT-GROUP
+ OBJECTS { someObject }
+ STATUS current
+ DESCRIPTION
+ "Basic set of objects."
+ ::= { grp 1 }
+
+
+ allObjects OBJECT-GROUP
+ OBJECTS { someObject, oneMore }
+ STATUS current
+ DESCRIPTION
+ "Complete set."
+ ::= { grp 2 }
+
+
+ cap OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Capabilities."
+ ::= { acTest 5 }
+
+
+ basicAgent AGENT-CAPABILITIES
+ PRODUCT-RELEASE
+ "Product release v1."
+ STATUS current
+ DESCRIPTION
+ "Basic agent."
+ SUPPORTS AC-TEST-MIB
+ INCLUDES { basicGrp }
+ ::= { cap 1 }
+
+
+ fullAgent AGENT-CAPABILITIES
+ PRODUCT-RELEASE
+ "Product release v2."
+ STATUS current
+ DESCRIPTION
+ "Full featured agent."
+ SUPPORTS AC-TEST-MIB
+ INCLUDES { allObjects }
+ ::= { cap 2 }
+
+
+
+ END
+
+--
+-- AC-TEST-MIB.mib
+--
diff --git a/lib/snmp/test/snmp_test_data/MC-TEST-MIB.mib b/lib/snmp/test/snmp_test_data/MC-TEST-MIB.mib
new file mode 100644
index 0000000000..cadaa6f891
--- /dev/null
+++ b/lib/snmp/test/snmp_test_data/MC-TEST-MIB.mib
@@ -0,0 +1,173 @@
+MC-TEST-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ otpExpr
+ FROM OTP-REG
+ MODULE-IDENTITY, OBJECT-TYPE,
+ mib-2, NOTIFICATION-TYPE, OBJECT-IDENTITY
+ FROM SNMPv2-SMI
+ TDomain, TAddress, DisplayString, TEXTUAL-CONVENTION,
+ AutonomousType, RowPointer, TimeStamp,
+ RowStatus, StorageType
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP
+ FROM SNMPv2-CONF;
+
+mcTestModule MODULE-IDENTITY
+ LAST-UPDATED "9605160000Z"
+ ORGANIZATION "MC Test Co."
+ CONTACT-INFO
+ DESCRIPTION
+ "MC Test module."
+ ::= { reg 1 }
+
+mcObjects OBJECT IDENTIFIER ::= { mcTestModule 1 }
+
+-- MIB contains one group
+
+mcMisc OBJECT IDENTIFIER ::= { mcObjects 1 }
+mcGeneral OBJECT IDENTIFIER ::= { mcObjects 2 }
+
+
+mcTest OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Test area."
+ ::= { otpExpr 4322 }
+
+
+reg OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "Registrations."
+ ::= { mcTest 1 }
+
+
+mcTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF McEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This table contains one row per physical entity. There is
+ always at least one row for an 'overall' physical entity."
+ ::= { mcMisc 1 }
+
+mcEntry OBJECT-TYPE
+ SYNTAX McEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table entry..."
+ INDEX { mcIndex }
+ ::= { mcTable 1 }
+
+McEntry ::= SEQUENCE {
+ mcIndex INTEGER,
+ mcName DisplayString,
+ mcStorageType StorageType,
+ mcRowStatus RowStatus
+}
+
+mcIndex OBJECT-TYPE
+ SYNTAX INTEGER
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The index for this entry."
+ ::= { mcEntry 1 }
+
+mcName OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of... "
+ ::= { mcEntry 2 }
+
+
+mcStorageType OBJECT-TYPE
+ SYNTAX StorageType
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "The storage type for this conceptual row."
+ DEFVAL { nonVolatile }
+ ::= { mcEntry 3 }
+
+mcRowStatus OBJECT-TYPE
+ SYNTAX RowStatus
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "The status of this conceptual row..."
+ ::= { mcEntry 4 }
+
+
+-- last change time stamp for the whole MIB
+mcTimeStamp OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The sysUpTime value when of the last time *anything* in the
+ MIB was changed. "
+ ::= { mcGeneral 1 }
+
+-- Entity MIB Trap Definitions
+mcTraps OBJECT IDENTIFIER ::= { mcTestModule 2 }
+mcTrapPrefix OBJECT IDENTIFIER ::= { mcTraps 0 }
+
+mcConfigChange NOTIFICATION-TYPE
+ STATUS current
+ DESCRIPTION
+ "An mcConfigChange trap is sent when the value of
+ entLastChangeTime changes..."
+ ::= { mcTrapPrefix 1 }
+
+-- conformance information
+mcConformance OBJECT IDENTIFIER ::= { mcTestModule 3 }
+
+mcCompliances OBJECT IDENTIFIER ::= { mcConformance 1 }
+mcGroups OBJECT IDENTIFIER ::= { mcConformance 2 }
+
+-- compliance statements
+
+
+mcCompliance MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for SNMP entities which implement
+ the MC Test MIB."
+ MODULE -- this module
+ MANDATORY-GROUPS { mcGeneralGroup,
+ mcNotificationsGroup }
+ ::= { mcCompliances 1 }
+
+-- MIB groupings
+
+mcGeneralGroup OBJECT-GROUP
+ OBJECTS {
+ mcName,
+ mcStorageType,
+ mcRowStatus,
+ mcTimeStamp
+ }
+ STATUS current
+ DESCRIPTION
+ "The collection of objects which are used to represent
+ general information..."
+ ::= { mcGroups 1 }
+
+mcNotificationsGroup NOTIFICATION-GROUP
+ NOTIFICATIONS { mcConfigChange }
+ STATUS current
+ DESCRIPTION
+ "The collection of notifications..."
+ ::= { mcGroups 2 }
+
+
+END
+
+
+
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 2586b66a13..54839d989b 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -172,7 +172,17 @@ os_based_skip(Skippable) when is_list(Skippable) ->
{value, {OsFam, OsName}} ->
true;
{value, {OsFam, OsNames}} when is_list(OsNames) ->
- lists:member(OsName, OsNames);
+ case lists:member(OsName, OsNames) of
+ true ->
+ true;
+ false ->
+ case lists:keymember(OsName, 1, OsNames) of
+ {value, {OsName, Check}} when is_function(Check) ->
+ Check();
+ _ ->
+ false
+ end
+ end;
_ ->
false
end
diff --git a/lib/snmp/test/snmp_test_manager.erl b/lib/snmp/test/snmp_test_manager.erl
index 9d9c52ef8d..4cc6d36acc 100644
--- a/lib/snmp/test/snmp_test_manager.erl
+++ b/lib/snmp/test/snmp_test_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/snmp_test_mgr_misc.erl b/lib/snmp/test/snmp_test_mgr_misc.erl
index ef1ba0b948..fc6dedd96d 100644
--- a/lib/snmp/test/snmp_test_mgr_misc.erl
+++ b/lib/snmp/test/snmp_test_mgr_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,6 +33,8 @@
%% internal exports
-export([init_packet/10]).
+-compile({no_auto_import, [error/2]}).
+
-define(SNMP_USE_V3, true).
-include_lib("snmp/include/snmp_types.hrl").
diff --git a/lib/snmp/test/snmp_test_server.erl b/lib/snmp/test/snmp_test_server.erl
index d0a5185452..ffbd2126a3 100644
--- a/lib/snmp/test/snmp_test_server.erl
+++ b/lib/snmp/test/snmp_test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -33,7 +33,7 @@
fatal_skip/3,
init_per_testcase/2,
- fin_per_testcase/2
+ end_per_testcase/2
]).
-include("snmp_test_lib.hrl").
@@ -229,7 +229,7 @@ eval(Mod, Fun, Config) ->
Eval = fun() -> do_eval(Self, Mod, Fun, Config2) end,
Pid = spawn_link(Eval),
R = wait_for_evaluator(Pid, Mod, Fun, Config2, []),
- Mod:fin_per_testcase(Fun, Config2),
+ Mod:end_per_testcase(Fun, Config2),
global:unregister_name(?TEST_CASE_SUP),
process_flag(trap_exit, Flag),
R.
@@ -361,7 +361,7 @@ init_per_testcase(_Case, Config) ->
global:register_name(?GLOBAL_LOGGER, group_leader()),
Config.
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
global:unregister_name(?GLOBAL_LOGGER),
ok.
diff --git a/lib/snmp/test/snmp_test_suite.erl b/lib/snmp/test/snmp_test_suite.erl
index a6e203eba3..77aaa508ad 100644
--- a/lib/snmp/test/snmp_test_suite.erl
+++ b/lib/snmp/test/snmp_test_suite.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@ behaviour_info(callbacks) ->
[
{all, 1},
{init_per_testcase, 2},
- {fin_per_testcase, 2}
+ {end_per_testcase, 2}
];
behaviour_info(_Other) ->
undefined.
diff --git a/lib/snmp/test/test1.erl b/lib/snmp/test/test1.erl
index b26b03d4ce..23cfaf6aaa 100644
--- a/lib/snmp/test/test1.erl
+++ b/lib/snmp/test/test1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/test2.erl b/lib/snmp/test/test2.erl
index dc010cfa11..a33208af7b 100644
--- a/lib/snmp/test/test2.erl
+++ b/lib/snmp/test/test2.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/snmp/test/test_config/.gitignore b/lib/snmp/test/test_config/.gitignore
new file mode 100644
index 0000000000..fc2d5dbadf
--- /dev/null
+++ b/lib/snmp/test/test_config/.gitignore
@@ -0,0 +1,19 @@
+# Sys config files (Generated)
+/sys.config
+/sys-agent.config
+/sys-manager.config
+
+# Agent config files (Generated)
+/agent/agent.conf
+/agent/community.conf
+/agent/context.conf
+/agent/notify.conf
+/agent/standard.conf
+/agent/target_addr.conf
+/agent/target_params.conf
+/agent/usm.conf
+/agent/vacm.conf
+
+# Manager config files (Generated)
+/manager/manager.conf
+/manager/usm.conf
diff --git a/lib/snmp/test/test_config/Makefile b/lib/snmp/test/test_config/Makefile
new file mode 100644
index 0000000000..d7bebbc431
--- /dev/null
+++ b/lib/snmp/test/test_config/Makefile
@@ -0,0 +1,199 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+
+VSN = $(SNMP_VSN)
+
+
+# ----------------------------------------------------
+# Configured variables
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+include modules.mk
+
+ERL_TARGETS = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+SYS_CONFIG_SRCS = $(SYS_CONFIG_FILES:%=%.src)
+AGENT_CONFIG_SRCS = $(AGENT_CONFIG_FILES:%=%.src)
+MANAGER_CONFIG_SRCS = $(MANAGER_CONFIG_FILES:%=%.src)
+
+CONFIG_FILES = \
+ $(SYS_CONFIG_FILES) \
+ $(AGENT_CONFIG_FILES) \
+ $(MANAGER_CONFIG_FILES)
+
+TARGETS = \
+ $(ERL_TARGETS) \
+ $(CONFIG_FILES)
+
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+ifeq ($(TESTROOT),)
+TESTROOT=/tmp
+endif
+RELSYSDIR = $(TESTROOT)
+
+
+# ----------------------------------------------------
+# FLAGS AND VARIABLES
+# ----------------------------------------------------
+
+EBIN = .
+
+ERL_COMPILE_FLAGS += +'{parse_transform,sys_pre_attributes}' \
+ +'{attribute,insert,app_vsn,$(APP_VSN)}'
+
+ifeq ($(ADDR),)
+ADDR = $(shell erl -noshell -s snmp_test_config ip_address -s init stop)
+endif
+
+ifeq ($(TARGET_NAME_PRE),)
+TARGET_NAME_PRE = $(shell erl -noshell -s snmp_test_config ip_address2 -s init stop)
+endif
+
+ifeq ($(SYS_CONTACT),)
+SYS_CONTACT = [email protected]
+endif
+
+ifeq ($(SYS_LOCATION),)
+SYS_LOCATION = Erlang/OTP
+endif
+
+ifeq ($(SYS_NAME),)
+SYS_NAME = FOO
+endif
+
+ifeq ($(AGENT_ENGINE_ID),)
+AGENT_ENGINE_ID = Agent engine of $(USER)
+endif
+
+ifeq ($(AGENT_USM_ENGINE_ID),)
+AGENT_USM_ENGINE_ID = $(AGENT_ENGINE_ID)
+endif
+
+ifeq ($(MANAGER_ENGINE_ID),)
+MANAGER_ENGINE_ID = Manager engine of $(USER)
+endif
+
+ifeq ($(MANAGER_USM_ENGINE_ID),)
+MANAGER_USM_ENGINE_ID = $(MANAGER_ENGINE_ID)
+endif
+
+
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(TARGETS)
+
+clean:
+ rm -f $(CONFIG_FILES)
+ rm -f $(ERL_TARGETS)
+ rm -f core
+
+docs:
+
+%.config: %.config.src
+ @echo "$< -> $@"
+ $(PERL) -p -e 's?%DIR%?$(RELSYSDIR)? ' < $< > $@
+
+agent/%.conf: agent/%.conf.src
+ @echo "$< -> $@"
+ sed -e 's?%ADDR%?$(ADDR)? ' \
+ -e 's?%SYS_CONTACT%?$(SYS_CONTACT)? ' \
+ -e 's?%SYS_LOCATION%?$(SYS_LOCATION)? ' \
+ -e 's?%SYS_NAME%?$(SYS_NAME)? ' \
+ -e 's?%TARGET_NAME_PRE%?$(TARGET_NAME_PRE)? ' \
+ -e 's?%ENGINE_ID%?\"$(AGENT_ENGINE_ID)\"? ' \
+ -e 's?%USM_ENGINE_ID%?\"$(AGENT_USM_ENGINE_ID)\"? ' < $< > $@
+
+manager/%.conf: manager/%.conf.src
+ @echo "$< -> $@"
+ sed -e 's?%ADDR%?$(ADDR)? ' \
+ -e 's?%ENGINE_ID%?\"$(MANAGER_ENGINE_ID)\"? ' \
+ -e 's?%USM_ENGINE_ID%?\"$(MANAGER_USM_ENGINE_ID)\"? ' < $< > $@
+
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+
+release_tests_spec: clean opt
+ $(INSTALL_DIR) $(RELSYSDIR)
+ chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELSYSDIR)/agent
+ chmod -f -R u+w $(RELSYSDIR)/agent
+ $(INSTALL_DIR) $(RELSYSDIR)/agent/conf
+ chmod -f -R u+w $(RELSYSDIR)/agent/conf
+ $(INSTALL_DIR) $(RELSYSDIR)/agent/db
+ chmod -f -R u+w $(RELSYSDIR)/agent/db
+ $(INSTALL_DIR) $(RELSYSDIR)/agent/log
+ chmod -f -R u+w $(RELSYSDIR)/agent/log
+ $(INSTALL_DIR) $(RELSYSDIR)/manager
+ chmod -f -R u+w $(RELSYSDIR)/manager
+ $(INSTALL_DIR) $(RELSYSDIR)/manager/conf
+ chmod -f -R u+w $(RELSYSDIR)/manager/conf
+ $(INSTALL_DIR) $(RELSYSDIR)/manager/db
+ chmod -f -R u+w $(RELSYSDIR)/manager/db
+ $(INSTALL_DIR) $(RELSYSDIR)/manager/log
+ chmod -f -R u+w $(RELSYSDIR)/manager/log
+ $(INSTALL_DATA) $(SYS_CONFIG_FILES) $(RELSYSDIR)
+ $(INSTALL_DATA) $(AGENT_CONFIG_FILES) $(RELSYSDIR)/agent/conf
+ $(INSTALL_DATA) $(MANAGER_CONFIG_FILES) $(RELSYSDIR)/manager/conf
+
+release_docs_spec:
+
+
+info:
+ @echo ""
+ @echo "RELSYSDIR = $(RELSYSDIR)"
+ @echo ""
+ @echo "SYS_CONFIG_SRCS = $(SYS_CONFIG_SRCS)"
+ @echo "SYS_CONFIG_FILES = $(SYS_CONFIG_FILES)"
+ @echo ""
+ @echo "AGENT_CONFIG_SRCS = $(AGENT_CONFIG_SRCS)"
+ @echo "AGENT_CONFIG_FILES = $(AGENT_CONFIG_FILES)"
+ @echo ""
+ @echo "MANAGER_CONFIG_SRCS = $(MANAGER_CONFIG_SRCS)"
+ @echo "MANAGER_CONFIG_FILES = $(MANAGER_CONFIG_FILES)"
+ @echo ""
+ @echo "ADDR = $(ADDR)"
+ @echo "TARGET_NAME_PRE = $(TARGET_NAME_PRE)"
+ @echo ""
+
+
diff --git a/lib/snmp/test/test_config/agent/agent.conf.src b/lib/snmp/test/test_config/agent/agent.conf.src
new file mode 100644
index 0000000000..1fe95cc72d
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/agent.conf.src
@@ -0,0 +1,19 @@
+%% This file defines the Agent local configuration info
+%% The data is inserted into the snmpEngine* variables defined
+%% in SNMP-FRAMEWORK-MIB, and the intAgent* variables defined
+%% in OTP-SNMPEA-MIB.
+%% Each row is a 2-tuple:
+%% {AgentVariable, Value}.
+%% For example
+%% {intAgentUDPPort, 4000}.
+%% The ip address for the agent is sent as id in traps.
+%% {intAgentIpAddress, [127,42,17,5]}.
+%% {snmpEngineID, "agentEngine"}.
+%% {snmpEngineMaxMessageSize, 484}.
+%%
+
+
+{intAgentUDPPort, 4000}.
+{intAgentIpAddress, %ADDR%}.
+{snmpEngineID, %ENGINE_ID%}.
+{snmpEngineMaxMessageSize, 484}.
diff --git a/lib/snmp/test/test_config/agent/community.conf.src b/lib/snmp/test/test_config/agent/community.conf.src
new file mode 100644
index 0000000000..8dccb929c9
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/community.conf.src
@@ -0,0 +1,15 @@
+%% This file defines the community info which maps to VACM parameters.
+%% The data is inserted into the snmpCommunityTable defined
+%% in SNMP-COMMUNITY-MIB.
+%% Each row is a 5-tuple:
+%% {CommunityIndex, CommunityName, SecurityName, ContextName, TransportTag}.
+%% For example
+%% {"1", "public", "initial", "", ""}.
+%% {"2", "secret", "secret_name", "", "tag"}.
+%% {"3", "bridge1", "initial", "bridge1", ""}.
+%%
+
+
+{"public", "public", "initial", "", ""}.
+{"all-rights", "all-rights", "all-rights", "", ""}.
+{"standard trap", "standard trap", "initial", "", ""}.
diff --git a/lib/snmp/test/test_config/agent/context.conf.src b/lib/snmp/test/test_config/agent/context.conf.src
new file mode 100644
index 0000000000..ea8b5a97eb
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/context.conf.src
@@ -0,0 +1,14 @@
+%% This file defines the contexts known to the agent.
+%% The data is inserted into the vacmContextTable defined
+%% in SNMP-VIEW-BASED-ACM-MIB.
+%% Each row is a string:
+%% ContextName.
+%%
+%% The empty string is the default context.
+%% For example
+%% "bridge1".
+%% "bridge2".
+%%
+
+
+"".
diff --git a/lib/snmp/test/test_config/agent/notify.conf.src b/lib/snmp/test/test_config/agent/notify.conf.src
new file mode 100644
index 0000000000..164fd25b95
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/notify.conf.src
@@ -0,0 +1,13 @@
+%% This file defines the notification parameters.
+%% The data is inserted into the snmpNotifyTable defined
+%% in SNMP-NOTIFICATION-MIB.
+%% The Name is used as CommunityString for v1 and v2c.
+%% Each row is a 3-tuple:
+%% {Name, Tag, Type}.
+%% For example
+%% {"standard trap", "std_trap", trap}.
+%% {"standard inform", "std_inform", inform}.
+%%
+
+
+{"stadard_trap", "std_trap", trap}.
diff --git a/lib/snmp/test/test_config/agent/standard.conf.src b/lib/snmp/test/test_config/agent/standard.conf.src
new file mode 100644
index 0000000000..31e04e7695
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/standard.conf.src
@@ -0,0 +1,21 @@
+%% This file defines the STANDARD-MIB info.
+%% Each row is a 2-tuple:
+%% {StandardVariable, Value}.
+%% For example
+%% {sysDescr, "Erlang SNMP agent"}.
+%% {sysObjectID, [1,2,3]}.
+%% {sysContact, "[email protected]"}.
+%% {sysName, "test"}.
+%% {sysLocation, "erlang"}.
+%% {sysServices, 72}.
+%% {snmpEnableAuthenTraps, enabled}.
+%%
+
+
+{sysDescr, "Erlang SNMP agent"}.
+{sysObjectID, [1,2,3]}.
+{sysContact, "%SYS_CONTACT%"}.
+{sysLocation, "%SYS_LOCATION%"}.
+{sysServices, 72}.
+{snmpEnableAuthenTraps, disabled}.
+{sysName, "%SYS_NAME%"}.
diff --git a/lib/snmp/test/test_config/agent/target_addr.conf.src b/lib/snmp/test/test_config/agent/target_addr.conf.src
new file mode 100644
index 0000000000..740df74ecf
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/target_addr.conf.src
@@ -0,0 +1,21 @@
+%% This file defines the target address parameters.
+%% The data is inserted into the snmpTargetAddrTable defined
+%% in SNMP-TARGET-MIB, and in the snmpTargetAddrExtTable defined
+%% in SNMP-COMMUNITY-MIB.
+%% Each row is a 10-tuple:
+%% {Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId,
+%% TMask, MaxMessageSize}.
+%% The EngineId value is only used if Inform-Requests are sent to this
+%% target. If Informs are not sent, this value is ignored, and can be
+%% e.g. an empty string. However, if Informs are sent, it is essential
+%% that the value of EngineId matches the value of the target's
+%% actual snmpEngineID.
+%% For example
+%% {"1.2.3.4 v1", [1,2,3,4], 162,
+%% 1500, 3, "std_inform", "otp_v2", "",
+%% [127,0,0,0], 2048}.
+%%
+
+
+{"%TARGET_NAME_PRE% v2", %ADDR%, 5000, 1500, 3, "std_trap", "target_v2", "", [], 2048}.
+{"%TARGET_NAME_PRE% v2.2", %ADDR%, 5000, 1500, 3, "std_inform", "target_v2", "", [], 2048}.
diff --git a/lib/snmp/test/test_config/agent/target_params.conf.src b/lib/snmp/test/test_config/agent/target_params.conf.src
new file mode 100644
index 0000000000..a4a535baa2
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/target_params.conf.src
@@ -0,0 +1,11 @@
+%% This file defines the target parameters.
+%% The data is inserted into the snmpTargetParamsTable defined
+%% in SNMP-TARGET-MIB.
+%% Each row is a 5-tuple:
+%% {Name, MPModel, SecurityModel, SecurityName, SecurityLevel}.
+%% For example
+%% {"target_v3", v3, usm, "", noAuthNoPriv}.
+%%
+
+
+{"target_v2", v2c, v2c, "initial", noAuthNoPriv}.
diff --git a/lib/snmp/test/test_config/agent/usm.conf.src b/lib/snmp/test/test_config/agent/usm.conf.src
new file mode 100644
index 0000000000..0409084048
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/usm.conf.src
@@ -0,0 +1,17 @@
+%% This file defines the security parameters for the user-based
+%% security model.
+%% The data is inserted into the usmUserTable defined
+%% in SNMP-USER-BASED-SM-MIB.
+%% Each row is a 13-tuple:
+%% {EngineID, UserName, SecName, Clone, AuthP, AuthKeyC, OwnAuthKeyC,
+%% PrivP, PrivKeyC, OwnPrivKeyC, Public, AuthKey, PrivKey}.
+%% For example
+%% {"agentEngine", "initial", "initial", zeroDotZero,
+%% usmNoAuthProtocol, "", "", usmNoPrivProtocol, "", "", "",
+%% "", ""}.
+%%
+
+
+{%USM_ENGINE_ID%, "initial", "initial", zeroDotZero, usmHMACMD5AuthProtocol, "", "", usmNoPrivProtocol, "", "", "", [160,66,33,136,178,59,246,214,102,63,131,131,54,14,221,177], ""}.
+{%USM_ENGINE_ID%, "templateMD5", "templateMD5", zeroDotZero, usmHMACMD5AuthProtocol, "", "", usmNoPrivProtocol, "", "", "", [160,66,33,136,178,59,246,214,102,63,131,131,54,14,221,177], ""}.
+{%USM_ENGINE_ID%, "templateSHA", "templateSHA", zeroDotZero, usmHMACSHAAuthProtocol, "", "", usmNoPrivProtocol, "", "", "", [199,94,239,13,229,135,141,77,124,129,65,189,230,240,115,163,239,15,13,242], ""}.
diff --git a/lib/snmp/test/test_config/agent/vacm.conf.src b/lib/snmp/test/test_config/agent/vacm.conf.src
new file mode 100644
index 0000000000..86271443ad
--- /dev/null
+++ b/lib/snmp/test/test_config/agent/vacm.conf.src
@@ -0,0 +1,27 @@
+%% This file defines the Mib Views.
+%% The data is inserted into the vacm* tables defined
+%% in SNMP-VIEW-BASED-ACM-MIB.
+%% Each row is one of 3 tuples; one for each table in the MIB:
+%% {vacmSecurityToGroup, SecModel, SecName, GroupName}.
+%% {vacmAccess, GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV}.
+%% {vacmViewTreeFamily, ViewIndex, ViewSubtree, ViewStatus, ViewMask}.
+%% For example
+%% {vacmSecurityToGroup, v2c, "initial", "initial"}.
+%% {vacmSecurityToGroup, usm, "initial", "initial"}.
+%% read/notify access to system
+%% {vacmAccess, "initial", "", any, noAuthNoPriv, exact,
+%% "system", "", "system"}.
+%% {vacmViewTreeFamily, "system", [1,3,6,1,2,1,1], included, null}.
+%% {vacmViewTreeFamily, "exmib", [1,3,6,1,3], included, null}. % for EX1-MIB
+%% {vacmViewTreeFamily, "internet", [1,3,6,1], included, null}.
+%%
+
+
+{vacmSecurityToGroup, v2c, "initial", "initial"}.
+{vacmSecurityToGroup, v2c, "all-rights", "all-rights"}.
+{vacmAccess, "initial", "", any, noAuthNoPriv, exact, "restricted", "", "restricted"}.
+{vacmAccess, "initial", "", usm, authNoPriv, exact, "internet", "internet", "internet"}.
+{vacmAccess, "initial", "", usm, authPriv, exact, "internet", "internet", "internet"}.
+{vacmAccess, "all-rights", "", any, noAuthNoPriv, exact, "internet", "internet", "internet"}.
+{vacmViewTreeFamily, "restricted", [1,3,6,1], included, null}.
+{vacmViewTreeFamily, "internet", [1,3,6,1], included, null}.
diff --git a/lib/snmp/test/test_config/manager/manager.conf.src b/lib/snmp/test/test_config/manager/manager.conf.src
new file mode 100644
index 0000000000..c38a61b13c
--- /dev/null
+++ b/lib/snmp/test/test_config/manager/manager.conf.src
@@ -0,0 +1,16 @@
+%% This file was generated by snmp_config (version-4.9.3) 2007-06-29 13:35:05
+%% This file defines the Manager local configuration info
+%% Each row is a 2-tuple:
+%% {Variable, Value}.
+%% For example
+%% {port, 5000}.
+%% {address, [127,42,17,5]}.
+%% {engine_id, "managerEngine"}.
+%% {max_message_size, 484}.
+%%
+
+
+{port, 5000}.
+{address, %ADDR%}.
+{engine_id, %ENGINE_ID%}.
+{max_message_size, 484}.
diff --git a/lib/snmp/test/test_config/manager/usm.conf.src b/lib/snmp/test/test_config/manager/usm.conf.src
new file mode 100644
index 0000000000..a558c86710
--- /dev/null
+++ b/lib/snmp/test/test_config/manager/usm.conf.src
@@ -0,0 +1,9 @@
+%% This file was generated by snmp_config (version-4.9.3) 2007-06-29 13:35:05
+%% This file defines the usm users the manager handles
+%% Each row is a 6 or 7-tuple:
+%% {EngineID, UserName, AuthP, AuthKey, PrivP, PrivKey}
+%% {EngineID, UserName, SecName, AuthP, AuthKey, PrivP, PrivKey}
+%%
+
+{%USM_ENGINE_ID%, "initial", usmNoAuthProtocol, "", usmNoPrivProtocol, ""}.
+
diff --git a/lib/snmp/test/test_config/modules.mk b/lib/snmp/test/test_config/modules.mk
new file mode 100644
index 0000000000..3d084cef01
--- /dev/null
+++ b/lib/snmp/test/test_config/modules.mk
@@ -0,0 +1,41 @@
+#-*-makefile-*- ; force emacs to enter makefile-mode
+
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2004-2010. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+
+SYS_CONFIG_FILES = \
+ sys.config \
+ sys-agent.config \
+ sys-manager.config
+
+AGENT_CONFIG_FILES = \
+ agent/agent.conf \
+ agent/community.conf \
+ agent/context.conf \
+ agent/notify.conf \
+ agent/standard.conf \
+ agent/target_addr.conf \
+ agent/target_params.conf \
+ agent/usm.conf \
+ agent/vacm.conf
+
+MANAGER_CONFIG_FILES = \
+ manager/manager.conf \
+ manager/usm.conf
+
+MODULES = \
+ snmp_test_config
diff --git a/lib/snmp/test/test_config/snmp_test_config.erl b/lib/snmp/test/test_config/snmp_test_config.erl
new file mode 100644
index 0000000000..550a276c4c
--- /dev/null
+++ b/lib/snmp/test/test_config/snmp_test_config.erl
@@ -0,0 +1,32 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(snmp_test_config).
+
+-export([ip_address/0, ip_address2/0]).
+
+ip_address() ->
+ {ok, Hostname} = inet:gethostname(),
+ {ok, Address} = inet:getaddr(Hostname, inet),
+ io:format("~w", [tuple_to_list(Address)]).
+
+ip_address2() ->
+ {ok, Hostname} = inet:gethostname(),
+ {ok, {A1, A2, A3, A4}} = inet:getaddr(Hostname, inet),
+ io:format("~w.~w.~w.~w", [A1, A2, A3, A4]).
diff --git a/lib/snmp/test/test_config/sys-agent.config.src b/lib/snmp/test/test_config/sys-agent.config.src
new file mode 100644
index 0000000000..46a458203d
--- /dev/null
+++ b/lib/snmp/test/test_config/sys-agent.config.src
@@ -0,0 +1,43 @@
+%% This is an example sys config file for starting the snmp application
+%% with only a agent running.
+[{snmp,
+ [
+ {agent,
+ [
+ {priority, normal},
+ {versions, [v1,v2,v3]},
+ {db_dir, "%DIR%/agent/db"},
+ {mib_storage, ets},
+%% {agent_mib_storage, volatile},
+ {agent_mib_storage, persistent},
+ {target_cache, [{verbosity,silence}]},
+ {symbolic_store, [{verbosity,silence}]},
+ {local_db, [{repair,true},{auto_save,5000},{verbosity,silence}]},
+ {error_report_module, snmpa_error_logger},
+ {agent_type, master},
+ {agent_verbosity, trace},
+ {audit_trail_log, [{type, read},
+ {dir, "%DIR%/agent/log"},
+ {size, {10240,10}}]},
+ {config, [{dir, "%DIR%/agent/conf"},
+ {force_load, true},
+ {verbosity, trace}]},
+ {multi_threaded, true},
+ {mib_server, [{mibentry_override, false},
+ {trapentry_override, false},
+ {cache, true},
+ {verbosity, trace}]},
+ {note_store, [{timeout,30000}, {verbosity,silence}]},
+ {supervisor, [{verbosity,silence}]},
+ {net_if, [{module, snmpa_net_if},
+ {verbosity, silence},
+ {options, [{bind_to, true},
+ {no_reuse, false},
+ {req_limit, infinity},
+ {sndbuf, 32000},
+ {recbuf, 32000}]}]}
+ ]
+ }
+ ]
+ }
+].
diff --git a/lib/snmp/test/test_config/sys-manager.config.src b/lib/snmp/test/test_config/sys-manager.config.src
new file mode 100644
index 0000000000..4366263084
--- /dev/null
+++ b/lib/snmp/test/test_config/sys-manager.config.src
@@ -0,0 +1,35 @@
+%% This is an example sys config file for starting the snmp application
+%% with only a manager running.
+[{snmp,
+ [
+ {manager,
+ [
+ {priority, normal},
+ {versions, [v1,v2,v3]},
+ {config, [{dir, "%DIR%/manager/conf"},
+ {verbosity, trace},
+ {db_dir, "%DIR%/manager/db"},
+ {repair, true},
+ {auto_save, 5000}]},
+ {inform_request_behaviour, user},
+ {mibs, []},
+ {server, [{timeout, 30000},
+ {verbosity, trace}]},
+ {note_store, [{timeout,30000},
+ {verbosity,silence}]},
+ {audit_trail_log, [{type, read},
+ {dir, "%DIR%/manager/log"},
+ {size, {10240,10}}]},
+ {net_if, [{module,snmpm_net_if},
+ {verbosity, trace},
+ {options, [{bind_to, true},
+ {no_reuse, false},
+% {sndbuf, 32000},
+ {recbuf, 45000}]}]},
+ {def_user_mod, snmpm_user_default},
+ {def_user_data, undefined}
+ ]
+ }
+ ]
+ }
+].
diff --git a/lib/snmp/test/test_config/sys.config.src b/lib/snmp/test/test_config/sys.config.src
new file mode 100644
index 0000000000..b2cd399883
--- /dev/null
+++ b/lib/snmp/test/test_config/sys.config.src
@@ -0,0 +1,68 @@
+%% This is an example sys config file for starting the snmp application
+%% with both an agent and a manager running.
+[{snmp,
+ [
+ {agent,
+ [
+ {priority, normal},
+ {versions, [v1,v2,v3]},
+ {db_dir, "%DIR%/agent/db"},
+ {mib_storage, ets},
+ {agent_mib_storage, volatile},
+ {target_cache, [{verbosity,silence}]},
+ {symbolic_store, [{verbosity,silence}]},
+ {local_db, [{repair,true},{auto_save,5000},{verbosity,silence}]},
+ {error_report_module, snmpa_error_logger},
+ {agent_type, master},
+ {agent_verbosity, silence},
+ {audit_trail_log, [{type, read},
+ {dir, "%DIR%/agent/log"},
+ {size, {10240,10}}]},
+ {config, [{dir, "%DIR%/agent/conf"},
+ {force_load, true},
+ {verbosity, silence}]},
+ {multi_threaded, false},
+ {mib_server, [{mibentry_override, false},
+ {trapentry_override, false},
+ {verbosity, silence}]},
+ {note_store, [{timeout,30000},{verbosity,silence}]},
+ {net_if, [{module, snmpa_net_if},
+ {verbosity, silence},
+ {options, [{bind_to, true},
+ {no_reuse, false},
+ {req_limit, infinity},
+ {sndbuf, 32000},
+ {recbuf, 32000}]}]}
+ ]
+ },
+ {manager,
+ [
+ {priority, normal},
+ {versions, [v1,v2,v3]},
+ {config, [{dir, "%DIR%/manager/conf"},
+ {verbosity, silence},
+ {db_dir, "%DIR%/manager/db"},
+ {repair, true},
+ {auto_save, 5000}]},
+ {inform_request_behaviour, auto},
+ {mibs, []},
+ {server, [{timeout, 30000},
+ {verbosity, silence}]},
+ {note_store, [{timeout, 30000},
+ {verbosity, silence}]},
+ {audit_trail_log, [{type, read},
+ {dir, "%DIR%/manager/log"},
+ {size, {10240,10}}]},
+ {net_if, [{module,snmpm_net_if},
+ {verbosity, silence},
+ {options, [{bind_to, true},
+ {no_reuse, false},
+ {recbuf, 33000},
+ {sndbuf, 34000}]}]},
+ {def_user_mod, snmpm_user_default},
+ {def_user_data, undefined}
+ ]
+ }
+ ]
+ }
+].
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index 2fccc733e6..e70c97dcb8 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -17,162 +17,6 @@
#
# %CopyrightEnd%
-SNMP_VSN = 4.16.2
+SNMP_VSN = 4.19
PRE_VSN =
APP_VSN = "snmp-$(SNMP_VSN)$(PRE_VSN)"
-
-TICKETS = OTP-8563 OTP-8574 OTP-8594 OTP-8595
-
-TICKETS_4_16_1 = OTP-8480 OTP-8481
-
-TICKETS_4_16 = \
- OTP-8395 \
- OTP-8433 \
- OTP-8442
-
-TICKETS_4_15 = OTP-8229 OTP-8249
-
-TICKETS_4_14 = OTP-8223 OTP-8228 OTP-8237
-
-TICKETS_4_13_5 = OTP-8116 OTP-8120 OTP-8181 OTP-8182
-
-TICKETS_4_13_4 = OTP-8044 OTP-8062 OTP-8098
-
-TICKETS_4_13_3 = OTP-8015 OTP-8020
-
-TICKETS_4_13_2 = OTP-7961 OTP-7977 OTP-7983 OTP-7989
-
-TICKETS_4_13_1 = OTP-7902
-
-TICKETS_4_13 = OTP-7571 OTP-7735 OTP-7836 OTP-7851
-
-TICKETS_4_12_2 = OTP-7868
-
-TICKETS_4_12_1 = OTP-7695 OTP-7698
-
-TICKETS_4_12 = OTP-7346 OTP-7525
-
-TICKETS_4_11_2 = OTP-7570 OTP-7575
-
-TICKETS_4_11_1 = OTP-7390 OTP-7412 OTP-7426 OTP-7432
-
-TICKETS_4_11 = OTP-7201 OTP-7287 OTP-7319 OTP-7369 OTP-7371 OTP-7377 OTP-7381
-
-TICKETS_4_10_3 = OTP-7219
-
-TICKETS_4_10_2 = OTP-7152 OTP-7153 OTP-7157 OTP-7158 OTP-7159 OTP-7160
-
-TICKETS_4_10_1 = OTP-7083 OTP-7109 OTP-7110 OTP-7119 OTP-7121 OTP-7123
-
-TICKETS_4_10 = OTP-6649 OTP-6841 OTP-6898 OTP-6945
-
-TICKETS_4_9_6 = OTP-6840 OTP-6843
-
-TICKETS_4_9_5 = OTP-6805 OTP-6815
-
-TICKETS_4_9_4 = OTP-6784 OTP-6771
-
-TICKETS_4_9_3 = OTP-6605 OTP-6712 OTP-6713
-
-TICKETS_4_9_2 = OTP-6571
-
-TICKETS_4_9_1 = OTP-6566 OTP-6569
-
-TICKETS_4_9 = \
- OTP-6317 \
- OTP-6318 \
- OTP-6383 \
- OTP-6487 \
- OTP-6515 \
- OTP-6518 \
- OTP-6529 \
- OTP-6532 \
- OTP-6533 \
- OTP-6540
-
-TICKETS_4_8_4 = OTP-6408
-
-TICKETS_4_8_3 = OTP-6337 OTP-6340
-
-TICKETS_4_8_2 = OTP-6214 OTP-6247 OTP-6293
-
-TICKETS_4_8_1 = OTP-6176 OTP-6177
-
-TICKETS_4_8 = OTP-6137 OTP-6149 OTP-6150 OTP-6164
-
-TICKETS_4_7_4 = \
- OTP-6042 \
- OTP-6044 \
- OTP-6049 \
- OTP-6062 \
- OTP-6068 \
- OTP-6074 \
- OTP-6077 \
- OTP-6081
-
-TICKETS_4_7_3 = \
- OTP-6031 \
- OTP-6032
-
-TICKETS_4_7_2 = \
- OTP-5992 \
- OTP-6024
-
-TICKETS_4_7_1 = \
- OTP-5963 \
- OTP-5968 \
- OTP-5969
-
-TICKETS_4_7 = \
- OTP-5870 \
- OTP-5934 \
- OTP-5935 \
- OTP-5937
-
-TICKETS_4_6_1 = \
- OTP-5834 \
- OTP-5838
-
-TICKETS_4_6 = \
- OTP-5763 \
- OTP-5771 \
- OTP-5787 \
- OTP-5797 \
- OTP-5829
-
-TICKETS_4_5 = \
- OTP-5581 \
- OTP-5726 \
- OTP-5727 \
- OTP-5732 \
- OTP-5733 \
- OTP-5740 \
- OTP-5742
-
-TICKETS_4_4_1 = \
- OTP-5719 \
- OTP-5720
-
-TICKETS_4_4 = \
- OTP-5666 \
- OTP-5668 \
- OTP-5669 \
- OTP-5675 \
- OTP-5676 \
- OTP-5678 \
- OTP-5703
-
-TICKETS_4_3 = \
- OTP-5636 \
- OTP-5637 \
- OTP-5490
-
-TICKETS_4_2 = \
- OTP-5574 \
- OTP-5578 \
- OTP-5579 \
- OTP-5580 \
- OTP-5590 \
- OTP-5591 \
- OTP-5592
-
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 67a226f726..af667b1a71 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2010</year>
+ <year>2004</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,7 +29,120 @@
<file>notes.xml</file>
</header>
- <section><title>Ssh 2.0</title>
+<section><title>Ssh 2.0.4</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>In some cases SSH returned {error, normal} when a channel was terminated
+ unexpectedly. This has now been changed to {error, channel_closed}.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8987 Aux Id: seq11748</p>
+ </item>
+ <item>
+ <p>
+ SSH did not handle the error reason enetunreach
+ when trying to open a IPv6 connection.</p>
+ <p>
+ Own Id: OTP-9031</p>
+ </item>
+ </list>
+ </section>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ It is now possible to use SSH to sign and verify binary data.</p>
+ <p>
+ Own Id: OTP-8986</p>
+ </item>
+ <item>
+ <p>
+ SSH now ensures that the .ssh directory exists before trying
+ to access files located in that directory.</p>
+ <p>
+ Own Id: OTP-9010</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+<section><title>Ssh 2.0.3</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The fix regarding OTP-8849 was not included in the
+ previous version as stated.</p>
+ <p>
+ Own Id: OTP-8918</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+<section><title>Ssh 2.0.2</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The ssh_system_sup did not catch noproc and shutdown
+ messages.</p>
+ <p>
+ Own Id: OTP-8863</p>
+ </item>
+ <item>
+ <p>
+ In some cases a crash report was generated when a
+ connection was closing down. This was caused by a race
+ condition between two processes.</p>
+ <p>
+ Own Id: OTP-8881 Aux Id: seq11656, seq11648 </p>
+ </item>
+ </list>
+ </section>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ SSH no longer use deprecated public_key functions.</p>
+ <p>
+ Own Id: OTP-8849</p>
+ </item>
+ </list>
+ </section>
+ </section>
+ <section><title>Ssh 2.0.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ SSH in some cases terminated channels with reason normal
+ when it should have been shutdown.</p>
+ <p>
+ Own Id: OTP-8714</p>
+ </item>
+ <item>
+ <p>
+ SSH in some cases generated a crash report when a channel
+ was closed in a normal way.</p>
+ <p>
+ Own Id: OTP-8735 Aux Id: seq11615 </p>
+ </item>
+ <item>
+ <p>
+ The processes ssh_subsystem_sup and one ssh_channel_sup
+ was not terminated when a connection was closed.</p>
+ <p>
+ Own Id: OTP-8807</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ssh 2.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -37,6 +150,18 @@
<p>The function ssh:connect/4 was not exported.</p>
<p>Own Id: OTP-8550 Aux Id:</p>
</item>
+ <item>
+ <p>Aligned error message with used version (SSH_FX_FAILURE vs
+ SSH_FX_NOT_A_DIRECTORY, the latter introduced in version 6).</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>Own Id: OTP-8644 Aux Id: seq11574</p>
+ </item>
+ <item>
+ <p>Resolved race condition when another connection is started
+ before a channel is opened in the first connection.</p>
+ <p>Own Id: OTP-8645 Aux Id: seq11577</p>
+ </item>
</list>
</section>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 71e6b2cd3d..2c5096a25f 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -283,6 +283,22 @@
</func>
<func>
+ <name>sign_data(Data, Algorithm) -> Signature | {error, Reason}</name>
+ <fsummary> </fsummary>
+ <type>
+ <v> Data = binary()</v>
+ <v> Algorithm = "ssh-rsa"</v>
+ <v> Signature = binary()</v>
+ <v> Reason = term()</v>
+ </type>
+ <desc>
+ <p>Signs the supplied binary using the SSH key.
+ </p>
+ </desc>
+ </func>
+
+
+ <func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
<fsummary>Starts the Ssh application. </fsummary>
@@ -339,6 +355,22 @@
by the listener up and running.</p>
</desc>
</func>
+
+ <func>
+ <name>verify_data(Data, Signature, Algorithm) -> ok | {error, Reason}</name>
+ <fsummary> </fsummary>
+ <type>
+ <v> Data = binary()</v>
+ <v> Algorithm = "ssh-rsa"</v>
+ <v> Signature = binary()</v>
+ <v> Reason = term()</v>
+ </type>
+ <desc>
+ <p>Verifies the supplied binary against the binary signature.
+ </p>
+ </desc>
+ </func>
+
</funcs>
</erlref>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 499cbbeabe..9942306b93 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src
index 21f7508555..501da8ceb9 100644
--- a/lib/ssh/src/ssh.appup.src
+++ b/lib/ssh/src/ssh.appup.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,9 +19,35 @@
{"%VSN%",
[
+ {"2.0.3", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_rsa, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []},
+ {load_module, ssh_transport, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
+ {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_rsa, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []},
+ {load_module, ssh_transport, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
+ {"2.0.1", [{restart_application, ssh}]}
],
[
- ]
+ {"2.0.3", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_rsa, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []},
+ {load_module, ssh_transport, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
+ {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []},
+ {load_module, ssh, soft_purge, soft_purge, []},
+ {load_module, ssh_rsa, soft_purge, soft_purge, []},
+ {load_module, ssh_acceptor, soft_purge, soft_purge, []},
+ {load_module, ssh_transport, soft_purge, soft_purge, []},
+ {load_module, ssh_connection_manager, soft_purge, soft_purge, []}]},
+ {"2.0.1", [{restart_application, ssh}]}
+ ]
}.
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 994c77436a..cada109df0 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -30,6 +30,8 @@
stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
shell/1, shell/2, shell/3]).
+-export([sign_data/2, verify_data/3]).
+
%%--------------------------------------------------------------------
%% Function: start([, Type]) -> ok
%%
@@ -94,11 +96,17 @@ connect(Host, Port, Options, Timeout) ->
do_demonitor(MRef, Manager),
{error, Other};
{'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) ->
+ error_logger:warning_report([{ssh, connect},
+ {diagnose,
+ "Connection was closed before properly set up."},
+ {host, Host},
+ {port, Port},
+ {reason, Reason}]),
receive %% Clear EXIT message from queue
{'EXIT', Manager, _What} ->
- {error, Reason}
+ {error, channel_closed}
after 0 ->
- {error, Reason}
+ {error, channel_closed}
end
after Timeout ->
do_demonitor(MRef, Manager),
@@ -239,6 +247,43 @@ shell(Host, Port, Options) ->
Error
end.
+
+%%--------------------------------------------------------------------
+%% Function: sign_data(Data, Algorithm) -> binary() |
+%% {error, Reason}
+%%
+%% Data = binary()
+%% Algorithm = "ssh-rsa"
+%%
+%% Description: Use SSH key to sign data.
+%%--------------------------------------------------------------------
+sign_data(Data, Algorithm) when is_binary(Data) ->
+ case ssh_file:private_identity_key(Algorithm,[]) of
+ {ok, Key} when Algorithm == "ssh-rsa" ->
+ ssh_rsa:sign(Key, Data);
+ Error ->
+ Error
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: verify_data(Data, Signature, Algorithm) -> ok |
+%% {error, Reason}
+%%
+%% Data = binary()
+%% Signature = binary()
+%% Algorithm = "ssh-rsa"
+%%
+%% Description: Use SSH signature to verify data.
+%%--------------------------------------------------------------------
+verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signature) ->
+ case ssh_file:public_identity_key(Algorithm, []) of
+ {ok, Key} when Algorithm == "ssh-rsa" ->
+ ssh_rsa:verify(Key, Data, Signature);
+ Error ->
+ Error
+ end.
+
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 57229daa27..59fbd24cf5 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -55,6 +55,10 @@ acceptor_init(Parent, Port, Address, SockOpts, Opts, AcceptTimeout) ->
do_socket_listen(Callback, Port, Opts) ->
case Callback:listen(Port, Opts) of
+ {error, nxdomain} ->
+ Callback:listen(Port, lists:delete(inet6, Opts));
+ {error, enetunreach} ->
+ Callback:listen(Port, lists:delete(inet6, Opts));
{error, eafnosupport} ->
Callback:listen(Port, lists:delete(inet6, Opts));
Other ->
@@ -79,11 +83,11 @@ acceptor_loop(Callback, Port, Address, Opts, ListenSocket, AcceptTimeout) ->
handle_connection(Callback, Address, Port, Options, Socket) ->
SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- ssh_system_sup:start_subsystem(SystemSup, Options),
+ {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
{ok, Pid} =
ssh_connection_controler:start_manager_child(ConnectionSup,
- [server, Socket, Options]),
+ [server, Socket, Options, SubSysSup]),
Callback:controlling_process(Socket, Pid),
SshOpts = proplists:get_value(ssh_opts, Options),
Pid ! {start_connection, server, [Address, Port, Socket, SshOpts]}.
diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl
index 8a49a44a6c..dcb2d69290 100644
--- a/lib/ssh/src/ssh_channel.erl
+++ b/lib/ssh/src/ssh_channel.erl
@@ -238,9 +238,19 @@ handle_info(Msg, #state{cm = ConnectionManager, channel_cb = Module,
{noreply, State#state{channel_state = ChannelState}};
{ok, ChannelState, Timeout} ->
{noreply, State#state{channel_state = ChannelState}, Timeout};
+ {stop, Reason, ChannelState} when is_atom(Reason)->
+ {stop, Reason, State#state{close_sent = true,
+ channel_state = ChannelState}};
{stop, ChannelId, ChannelState} ->
- ssh_connection:close(ConnectionManager, ChannelId),
- {stop, normal, State#state{close_sent = true,
+ Reason =
+ case Msg of
+ {'EXIT', _Pid, shutdown} ->
+ shutdown;
+ _ ->
+ normal
+ end,
+ (catch ssh_connection:close(ConnectionManager, ChannelId)),
+ {stop, Reason, State#state{close_sent = true,
channel_state = ChannelState}}
end.
diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl
index 2764ea2e43..cb78acb84c 100644
--- a/lib/ssh/src/ssh_cli.erl
+++ b/lib/ssh/src/ssh_cli.erl
@@ -327,7 +327,7 @@ window_change(Tty, OldTty, Buf)
{[], Buf};
window_change(Tty, OldTty, {Buf, BufTail, Col}) ->
M1 = move_cursor(Col, 0, OldTty),
- N = max(Tty#ssh_pty.width - OldTty#ssh_pty.width, 0) * 2,
+ N = erlang:max(Tty#ssh_pty.width - OldTty#ssh_pty.width, 0) * 2,
S = lists:reverse(Buf, [BufTail | lists:duplicate(N, $ )]),
M2 = move_cursor(length(Buf) + length(BufTail) + N, Col, Tty),
{[M1, S | M2], {Buf, BufTail, Col}}.
@@ -398,10 +398,6 @@ nthtail(0, A) -> A;
nthtail(N, [_ | A]) when N > 0 -> nthtail(N-1, A);
nthtail(_, _) -> [].
-%%% utils
-max(A, B) when A > B -> A;
-max(_A, B) -> B.
-
ifelse(Cond, A, B) ->
case Cond of
true -> A;
@@ -419,14 +415,12 @@ start_shell(ConnectionManager, State) ->
Shell = State#state.shell,
ShellFun = case is_function(Shell) of
true ->
+ {ok, User} =
+ ssh_userreg:lookup_user(ConnectionManager),
case erlang:fun_info(Shell, arity) of
{arity, 1} ->
- {ok, User} =
- ssh_userreg:lookup_user(ConnectionManager),
fun() -> Shell(User) end;
{arity, 2} ->
- {ok, User} =
- ssh_userreg:lookup_user(ConnectionManager),
{ok, PeerAddr} =
ssh_connection_manager:peer_addr(ConnectionManager),
fun() -> Shell(User, PeerAddr) end;
@@ -441,10 +435,28 @@ start_shell(ConnectionManager, State) ->
State#state{group = Group, buf = empty_buf()}.
start_shell(_ConnectionManager, Cmd, #state{exec={M, F, A}} = State) ->
- Group = group:start(self(), {M, F, A++[Cmd]}, [{echo,false}]),
+ Group = group:start(self(), {M, F, A++[Cmd]}, [{echo, false}]),
+ State#state{group = Group, buf = empty_buf()};
+start_shell(ConnectionManager, Cmd, #state{exec=Shell} = State) when is_function(Shell) ->
+ {ok, User} =
+ ssh_userreg:lookup_user(ConnectionManager),
+ ShellFun =
+ case erlang:fun_info(Shell, arity) of
+ {arity, 1} ->
+ fun() -> Shell(Cmd) end;
+ {arity, 2} ->
+ fun() -> Shell(Cmd, User) end;
+ {arity, 3} ->
+ {ok, PeerAddr} =
+ ssh_connection_manager:peer_addr(ConnectionManager),
+ fun() -> Shell(Cmd, User, PeerAddr) end;
+ _ ->
+ Shell
+ end,
+ Echo = get_echo(State#state.pty),
+ Group = group:start(self(), ShellFun, [{echo,Echo}]),
State#state{group = Group, buf = empty_buf()}.
-
% Pty can be undefined if the client never sets any pty options before
% starting the shell.
get_echo(undefined) ->
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 57bb141c60..34d4ff8fc1 100755
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -260,5 +260,6 @@
address,
port,
options,
- exec
+ exec,
+ sub_system_supervisor
}).
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index b9827c90ea..7b9e9185bf 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -946,13 +946,12 @@ encode_ip(Addr) when is_list(Addr) ->
end
end.
-start_channel(Address, Port, Cb, Id, Args) ->
- start_channel(Address, Port, Cb, Id, Args, undefined).
+start_channel(Cb, Id, Args, SubSysSup) ->
+ start_channel(Cb, Id, Args, SubSysSup, undefined).
-start_channel(Address, Port, Cb, Id, Args, Exec) ->
+start_channel(Cb, Id, Args, SubSysSup, Exec) ->
ChildSpec = child_spec(Cb, Id, Args, Exec),
- SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- ChannelSup = ssh_system_sup:channel_supervisor(SystemSup),
+ ChannelSup =ssh_subsystem_sup:channel_supervisor(SubSysSup),
ssh_channel_sup:start_child(ChannelSup, ChildSpec).
%%--------------------------------------------------------------------
@@ -1017,18 +1016,19 @@ start_cli(#connection{address = Address, port = Port, cli_spec = {Fun, [Shell]},
{ok, Pid}
end;
-start_cli(#connection{address = Address, port = Port,
- cli_spec = {CbModule, Args}, exec = Exec}, ChannelId) ->
- start_channel(Address, Port, CbModule, ChannelId, Args, Exec).
+start_cli(#connection{cli_spec = {CbModule, Args}, exec = Exec,
+ sub_system_supervisor = SubSysSup}, ChannelId) ->
+ start_channel(CbModule, ChannelId, Args, SubSysSup, Exec).
start_subsytem(BinName, #connection{address = Address, port = Port,
- options = Options},
+ options = Options,
+ sub_system_supervisor = SubSysSup},
#channel{local_id = ChannelId, remote_id = RemoteChannelId},
ReplyMsg) ->
Name = binary_to_list(BinName),
case check_subsystem(Name, Options) of
{Callback, Opts} when is_atom(Callback), Callback =/= none ->
- start_channel(Address, Port, Callback, ChannelId, Opts);
+ start_channel(Callback, ChannelId, Opts, SubSysSup);
{Other, _} when Other =/= none ->
handle_backwards_compatibility(Other, self(),
ChannelId, RemoteChannelId,
diff --git a/lib/ssh/src/ssh_connection_controler.erl b/lib/ssh/src/ssh_connection_controler.erl
index 990541f8d6..ca3e62dc83 100644
--- a/lib/ssh/src/ssh_connection_controler.erl
+++ b/lib/ssh/src/ssh_connection_controler.erl
@@ -99,8 +99,8 @@ terminate(_Reason, #state{}) ->
handle_call({handler, Pid, [Role, Socket, Opts]}, _From, State) ->
{ok, Handler} = ssh_connection_handler:start_link(Role, Pid, Socket, Opts),
{reply, {ok, Handler}, State#state{handler = Handler}};
-handle_call({manager, [server = Role, Socket, Opts]}, _From, State) ->
- {ok, Manager} = ssh_connection_manager:start_link([Role, Socket, Opts]),
+handle_call({manager, [server = Role, Socket, Opts, SubSysSup]}, _From, State) ->
+ {ok, Manager} = ssh_connection_manager:start_link([Role, Socket, Opts, SubSysSup]),
{reply, {ok, Manager}, State#state{manager = Manager}};
handle_call({manager, [client = Role | Opts]}, _From, State) ->
{ok, Manager} = ssh_connection_manager:start_link([Role, Opts]),
@@ -126,8 +126,8 @@ handle_cast(_, State) ->
%% handle_info(ssh_connected, State) ->
%% {stop, normal, State};
%% Servant termination.
-handle_info({'EXIT', _Pid, normal}, State) ->
- {stop, normal, State}.
+handle_info({'EXIT', _Pid, Reason}, State) ->
+ {stop, Reason, State}.
%%-----------------------------------------------------------------
%% Func: code_change/3
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 822ef8f8f9..0ba11b0a26 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -527,7 +527,7 @@ handle_info({Protocol, Socket, Data}, Statename,
%% Implementations SHOULD decrypt the length after receiving the
%% first 8 (or cipher block size, whichever is larger) bytes of a
%% packet. (RFC 4253: Section 6 - Binary Packet Protocol)
- case size(EncData0) + size(Data) >= max(8, BlockSize) of
+ case size(EncData0) + size(Data) >= erlang:max(8, BlockSize) of
true ->
{Ssh, SshPacketLen, DecData, EncData} =
@@ -705,11 +705,19 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
Byte == ?SSH_MSG_CHANNEL_REQUEST;
Byte == ?SSH_MSG_CHANNEL_SUCCESS;
Byte == ?SSH_MSG_CHANNEL_FAILURE ->
- ssh_connection_manager:event(Pid, Msg),
- State = generate_event_new_state(State0, EncData),
- next_packet(State),
- {next_state, StateName, State};
+ try
+ ssh_connection_manager:event(Pid, Msg),
+ State = generate_event_new_state(State0, EncData),
+ next_packet(State),
+ {next_state, StateName, State}
+ catch
+ exit:{noproc, _Reason} ->
+ Report = io_lib:format("~p Connection Handler terminated: ~p~n",
+ [self(), Pid]),
+ error_logger:info_report(Report),
+ {stop, normal, State0}
+ end;
generate_event(Msg, StateName, State0, EncData) ->
Event = ssh_bits:decode(Msg),
State = generate_event_new_state(State0, EncData),
@@ -758,11 +766,6 @@ after_new_keys(#state{renegotiate = false,
ssh_params = #ssh{role = server}} = State) ->
{userauth, State}.
-max(N, M) when N > M ->
- N;
-max(_, M) ->
- M.
-
handle_ssh_packet_data(RemainingSshPacketLen, DecData, EncData, StateName,
State) ->
EncSize = size(EncData),
diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl
index a2effc177e..9bfd5270da 100644
--- a/lib/ssh/src/ssh_connection_manager.erl
+++ b/lib/ssh/src/ssh_connection_manager.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
+%%
%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
-%%
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
@@ -146,6 +146,8 @@ adjust_window(ConnectionManager, Channel, Bytes) ->
close(ConnectionManager, ChannelId) ->
try call(ConnectionManager, {close, ChannelId}) of
ok ->
+ ok;
+ {error, channel_closed} ->
ok
catch
exit:{noproc, _} ->
@@ -155,6 +157,8 @@ close(ConnectionManager, ChannelId) ->
stop(ConnectionManager) ->
try call(ConnectionManager, stop) of
ok ->
+ ok;
+ {error, channel_closed} ->
ok
catch
exit:{noproc, _} ->
@@ -178,7 +182,7 @@ send_eof(ConnectionManager, ChannelId) ->
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
-init([server, _Socket, Opts]) ->
+init([server, _Socket, Opts, SubSysSup]) ->
process_flag(trap_exit, true),
ssh_bits:install_messages(ssh_connection:messages()),
Cache = ssh_channel:cache_create(),
@@ -187,7 +191,8 @@ init([server, _Socket, Opts]) ->
channel_id_seed = 0,
port_bindings = [],
requests = [],
- channel_pids = []},
+ channel_pids = [],
+ sub_system_supervisor = SubSysSup},
opts = Opts,
connected = false}};
@@ -400,7 +405,7 @@ handle_call({close, ChannelId}, _,
end;
handle_call(stop, _, #state{role = _client,
- client = ChannelPid,
+ client = _ChannelPid,
connection = Pid} = State) ->
DisconnectMsg =
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
@@ -555,13 +560,18 @@ handle_info({'EXIT', _, _}, State) ->
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(Reason, #state{connection_state =
- #connection{requests = Requests},
+ #connection{requests = Requests,
+ sub_system_supervisor = SubSysSup},
opts = Opts}) ->
SSHOpts = proplists:get_value(ssh_opts, Opts),
disconnect_fun(Reason, SSHOpts),
(catch lists:foreach(fun({_, From}) ->
gen_server:reply(From, {error, connection_closed})
end, Requests)),
+ Address = proplists:get_value(address, Opts),
+ Port = proplists:get_value(port, Opts),
+ SystemSup = ssh_system_sup:system_supervisor(Address, Port),
+ ssh_system_sup:stop_subsystem(SystemSup, SubSysSup),
ok.
%%--------------------------------------------------------------------
@@ -592,7 +602,9 @@ call(Pid, Msg, Timeout) ->
Result
catch
exit:{timeout, _} ->
- {error, timeout}
+ {error, timeout};
+ exit:{normal, _} ->
+ {error, channel_closed}
end.
cast(Pid, Msg) ->
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index 5572349fe7..12180f56bb 100755
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,14 +27,16 @@
-include("PKCS-1.hrl").
-include("DSS.hrl").
+-include_lib("kernel/include/file.hrl").
+
-export([public_host_dsa_key/2,private_host_dsa_key/2,
public_host_rsa_key/2,private_host_rsa_key/2,
public_host_key/2,private_host_key/2,
lookup_host_key/3, add_host_key/3, % del_host_key/2,
lookup_user_key/3, ssh_dir/2, file_name/3]).
--export([private_identity_key/2]).
-%% , public_identity_key/2,
+-export([private_identity_key/2,
+ public_identity_key/2]).
%% identity_keys/2]).
-export([encode_public_key/1, decode_public_key_v2/2]).
@@ -43,6 +45,9 @@
-define(DBG_PATHS, true).
+-define(PERM_700, 8#700).
+-define(PERM_644, 8#644).
+
%% API
public_host_dsa_key(Type, Opts) ->
File = file_name(Type, "ssh_host_dsa_key.pub", Opts),
@@ -113,8 +118,10 @@ do_lookup_host_key(Host, Alg, Opts) ->
add_host_key(Host, Key, Opts) ->
Host1 = add_ip(replace_localhost(Host)),
- case file:open(file_name(user, "known_hosts", Opts),[write,append]) of
+ KnownHosts = file_name(user, "known_hosts", Opts),
+ case file:open(KnownHosts, [write,append]) of
{ok, Fd} ->
+ ok = file:change_mode(KnownHosts, ?PERM_644),
Res = add_key_fd(Fd, Host1, Key),
file:close(Fd),
Res;
@@ -140,6 +147,11 @@ private_identity_key(Alg, Opts) ->
Path = file_name(user, identity_key_filename(Alg), Opts),
read_private_key_v2(Path, Alg).
+public_identity_key(Alg, Opts) ->
+ Path = file_name(user, identity_key_filename(Alg) ++ ".pub", Opts),
+ read_public_key_v2(Path, Alg).
+
+
read_public_key_v2(File, Type) ->
case file:read_file(File) of
{ok,Bin} ->
@@ -198,12 +210,17 @@ read_public_key_v1(File) ->
%% pem_type("ssh-rsa") -> "RSA".
read_private_key_v2(File, Type) ->
- case catch (public_key:pem_to_der(File)) of
- {ok, [{_, Bin, not_encrypted}]} ->
- decode_private_key_v2(Bin, Type);
- Error -> %% Note we do not handle password encrypted keys at the moment
- {error, Error}
- end.
+ case file:read_file(File) of
+ {ok, PemBin} ->
+ case catch (public_key:pem_decode(PemBin)) of
+ [{_, Bin, not_encrypted}] ->
+ decode_private_key_v2(Bin, Type);
+ Error -> %% Note we do not handle password encrypted keys at the moment
+ {error, Error}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
%% case file:read_file(File) of
%% {ok,Bin} ->
%% case read_pem(binary_to_list(Bin), pem_type(Type)) of
@@ -527,4 +544,7 @@ file_name(Type, Name, Opts) ->
default_user_dir()->
{ok,[[Home|_]]} = init:get_argument(home),
- filename:join(Home, ".ssh").
+ UserDir = filename:join(Home, ".ssh"),
+ ok = filelib:ensure_dir(filename:join(UserDir, "dummy")),
+ ok = file:change_mode(UserDir, ?PERM_700),
+ UserDir.
diff --git a/lib/ssh/src/ssh_rsa.erl b/lib/ssh/src/ssh_rsa.erl
index e27cdcf7bd..91b8285b2e 100755
--- a/lib/ssh/src/ssh_rsa.erl
+++ b/lib/ssh/src/ssh_rsa.erl
@@ -202,8 +202,7 @@ rsassa_pkcs1_v1_5_verify(Public=#ssh_key { public={N,_E}}, Mb, Sb) ->
case emsa_pkcs1_v1_5_encode(Mb, K) of
EM -> ok;
_S ->
- io:format("S: ~p~n", [_S]),
- {error, invalid_signature} % exit(invalid_signature)
+ {error, invalid_signature}
end.
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index dc789092dd..da91817fd7 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -242,7 +242,8 @@ handle_op(?SSH_FXP_REALPATH, ReqId,
end;
handle_op(?SSH_FXP_OPENDIR, ReqId,
<<?UINT32(RLen), RPath:RLen/binary>>,
- State0 = #state{file_handler = FileMod, file_state = FS0}) ->
+ State0 = #state{xf = #ssh_xfer{vsn = Vsn},
+ file_handler = FileMod, file_state = FS0}) ->
RelPath = binary_to_list(RPath),
AbsPath = relate_file_name(RelPath, State0),
@@ -250,10 +251,14 @@ handle_op(?SSH_FXP_OPENDIR, ReqId,
{IsDir, FS1} = FileMod:is_dir(AbsPath, FS0),
State1 = State0#state{file_state = FS1},
case IsDir of
- false ->
+ false when Vsn > 5 ->
ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_NOT_A_DIRECTORY,
"Not a directory"),
State1;
+ false ->
+ ssh_xfer:xf_send_status(XF, ReqId, ?SSH_FX_FAILURE,
+ "Not a directory"),
+ State1;
true ->
add_handle(State1, XF, ReqId, directory, {RelPath,unread})
end;
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 40db2e4adf..920baaadef 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -33,7 +33,8 @@
stop_system/2, system_supervisor/2,
subsystem_supervisor/1, channel_supervisor/1,
connection_supervisor/1,
- acceptor_supervisor/1, start_subsystem/2, restart_subsystem/2, restart_acceptor/2]).
+ acceptor_supervisor/1, start_subsystem/2, restart_subsystem/2,
+ restart_acceptor/2, stop_subsystem/2]).
%% Supervisor callback
-export([init/1]).
@@ -83,6 +84,23 @@ start_subsystem(SystemSup, Options) ->
Spec = ssh_subsystem_child_spec(Options),
supervisor:start_child(SystemSup, Spec).
+stop_subsystem(SystemSup, SubSys) ->
+ case catch lists:keyfind(SubSys, 2, supervisor:which_children(SystemSup)) of
+ false ->
+ {error, not_found};
+ {Id, _, _, _} ->
+ spawn(fun() -> supervisor:terminate_child(SystemSup, Id),
+ supervisor:delete_child(SystemSup, Id) end),
+ ok;
+ {'EXIT', {noproc, _}} ->
+ %% Already terminated; probably shutting down.
+ ok;
+ {'EXIT', {shutdown, _}} ->
+ %% Already shutting down.
+ ok
+ end.
+
+
restart_subsystem(Address, Port) ->
SysSupName = make_name(Address, Port),
SubSysName = id(ssh_subsystem_sup, Address, Port),
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index e79ccdda0c..de3e29e2f1 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -169,6 +169,8 @@ do_connect(Callback, Address, Port, SocketOpts, Timeout) ->
Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout);
{error, eafnosupport} ->
Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout);
+ {error, enetunreach} ->
+ Callback:connect(Address, Port, lists:delete(inet6, Opts), Timeout);
Other ->
Other
end.
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 38a82ff32d..51f9f47446 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,82 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 2.0
+SSH_VSN = 2.0.4
APP_VSN = "ssh-$(SSH_VSN)"
-TICKETS = OTP-8524 \
- OTP-8534 \
- OTP-8535 \
- OTP-8550 \
- OTP-8596
-
-TICKETS_1.1.8 = OTP-8356 \
- OTP-8401
-
-TICKETS_1.1.7 = OTP-8121 \
- OTP-8277 \
- OTP-8278 \
- OTP-8201
-
-TICKETS_1.1.6 = OTP-8110 \
- OTP-8162 \
- OTP-8173 \
- OTP-8174 \
- OTP-8175 \
- OTP-8176
-
-TICKETS_1.1.5 = OTP-8159 \
- OTP-8160 \
- OTP-8161
-
-TICKETS_1.1.4 = OTP-8071
-
-TICKETS_1.1.3 = OTP-7996 \
- OTP-8034 \
- OTP-8035
-
-TICKETS_1.1.2 = OTP-7914 \
- OTP-7917 \
- OTP-7918 \
- OTP-7921 \
- OTP-7919 \
- OTP-7930 \
- OTP-7957
-
-TICKETS_1.1.1 = OTP-7828 \
- OTP-7795 \
- OTP-7807 \
- OTP-7808 \
- OTP-7809
-
-TICKETS_1.1 = OTP-7676 \
- OTP-7683 \
- OTP-7685 \
- OTP-7766 \
- OTP-7767 \
- OTP-7768 \
- OTP-7770 \
- OTP-7456 \
- OTP-7769 \
- OTP-7516 \
- OTP-7645 \
-
-TICKETS_1.0.2 = \
- OTP-7141\
-
-TICKETS_1.0.1 = \
- OTP-7318 \
- OTP-7305 \
- OTP-7564 \
- OTP-7565 \
- OTP-7566 \
-
-TICKETS_1.0 = \
- OTP-7485 \
- OTP-7504 \
- OTP-7356 \
- OTP-7502 \
- OTP-7503
-
-TICKETS_0.9.9.6 = \
- OTP-7246 \
- OTP-7247 \ \ No newline at end of file
diff --git a/lib/ssl/Makefile b/lib/ssl/Makefile
index a3dec8da38..daad7dc3e6 100644
--- a/lib/ssl/Makefile
+++ b/lib/ssl/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
@@ -24,22 +24,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
#
# Macros
#
-ifeq ($(findstring win32,$(TARGET)),win32)
-ifeq ($(HOST_OS),)
-HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess)
-endif
-ifeq ($(findstring solaris,$(HOST_OS)),solaris)
-SKIP_BUILDING_BINARIES := true
-endif
-else
-SKIP_BUILDING_BINARIES := false
-endif
-
-ifeq ($(SKIP_BUILDING_BINARIES), true)
-SUB_DIRECTORIES = pkix src c_src doc/src
-else
-SUB_DIRECTORIES = pkix src c_src doc/src examples/certs examples/src
-endif
+
+SUB_DIRECTORIES = src c_src doc/src examples/certs examples/src
include vsn.mk
VSN = $(SSL_VSN)
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index fa263d28ab..3119d37af0 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = refman.xml
-XML_REF3_FILES = ssl.xml new_ssl.xml
+XML_REF3_FILES = ssl.xml old_ssl.xml ssl_session_cache_api.xml
XML_REF6_FILES = ssl_app.xml
XML_PART_FILES = release_notes.xml usersguide.xml
@@ -45,9 +45,7 @@ XML_CHAPTER_FILES = \
ssl_protocol.xml \
using_ssl.xml \
pkix_certs.xml \
- create_certs.xml \
ssl_distribution.xml \
- licenses.xml \
notes.xml
BOOK_FILES = book.xml
diff --git a/lib/ssl/doc/src/book.xml b/lib/ssl/doc/src/book.xml
index 9122addb74..ecfb915b44 100644
--- a/lib/ssl/doc/src/book.xml
+++ b/lib/ssl/doc/src/book.xml
@@ -4,7 +4,7 @@
<book xmlns:xi="http://www.w3.org/2001/XInclude">
<header titlestyle="normal">
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -28,9 +28,6 @@
<rev>A</rev>
<file>book.sgml</file>
</header>
- <insidecover>
- <include file="insidecover"></include>
- </insidecover>
<pagetext>SSL Application</pagetext>
<preamble>
<contents level="2"></contents>
diff --git a/lib/ssl/doc/src/create_certs.xml b/lib/ssl/doc/src/create_certs.xml
deleted file mode 100644
index 79cc8a0537..0000000000
--- a/lib/ssl/doc/src/create_certs.xml
+++ /dev/null
@@ -1,148 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2003</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Creating Certificates</title>
- <prepared>UAB/F/P Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-06-16</date>
- <rev>A</rev>
- <file>create_certs.xml</file>
- </header>
- <p>Here we consider the creation of example certificates.
- </p>
-
- <section>
- <title>The openssl Command</title>
- <p>The <c>openssl</c> command is a utility that comes with the
- OpenSSL distribution. It provides a variety of subcommands. Each
- subcommand is invoked as</p>
- <code type="none"><![CDATA[
- openssl subcmd <options and arguments> ]]></code>
- <p>where <c>subcmd</c> denotes the subcommand in question.
- </p>
- <p>We shall use the following subcommands to create certificates for
- the purpose of testing Erlang/OTP SSL:
- </p>
- <list type="bulleted">
- <item><em>req</em> to create certificate requests and a
- self-signed certificates,
- </item>
- <item><em>ca</em> to create certificates from certificate requests.</item>
- </list>
- <p>We create the following certificates:
- </p>
- <list type="bulleted">
- <item>the <em>erlangCA</em> root certificate (a self-signed
- certificate), </item>
- <item>the <em>otpCA</em> certificate signed by the <em>erlangCA</em>, </item>
- <item>a client certificate signed by the <em>otpCA</em>, and</item>
- <item>a server certificate signed by the <em>otpCA</em>.</item>
- </list>
-
- <section>
- <title>The openssl configuration file</title>
- <p>An <c>openssl</c> configuration file consist of a number of
- sections, where each section starts with one line containing
- <c>[ section_name ]</c>, where <c>section_name</c> is the name
- of the section. The first section of the file is either
- unnamed, or is named <c>[ default ]</c>. For further details
- see the OpenSSL config(5) manual page.
- </p>
- <p>The required sections for the subcommands we are going to
- use are as follows:
- </p>
- <table>
- <row>
- <cell align="left" valign="middle">subcommand</cell>
- <cell align="left" valign="middle">required/default section</cell>
- <cell align="left" valign="middle">override command line option</cell>
- <cell align="left" valign="middle">configuration file option</cell>
- </row>
- <row>
- <cell align="left" valign="middle">req</cell>
- <cell align="left" valign="middle">[req]</cell>
- <cell align="left" valign="middle">-</cell>
- <cell align="left" valign="middle"><c>-config FILE</c></cell>
- </row>
- <row>
- <cell align="left" valign="middle">ca</cell>
- <cell align="left" valign="middle">[ca]</cell>
- <cell align="left" valign="middle"><c>-name section</c></cell>
- <cell align="left" valign="middle"><c>-config FILE</c></cell>
- </row>
- <tcaption>openssl subcommands to use</tcaption>
- </table>
- </section>
-
- <section>
- <title>Creating the Erlang root CA</title>
- <p>The Erlang root CA is created with the command</p>
- <code type="none">
- openssl req -new -x509 -config /some/path/req.cnf \\
- -keyout /some/path/key.pem -out /some/path/cert.pem </code>
- <p>where the option <c>-new</c> indicates that we want to create
- a new certificate request and the option <c>-x509</c> implies
- that a self-signed certificate is created.
- </p>
- </section>
-
- <section>
- <title>Creating the OTP CA</title>
- <p>The OTP CA is created by first creating a certificate request
- with the command</p>
- <code type="none">
- openssl req -new -config /some/path/req.cnf \\
- -keyout /some/path/key.pem -out /some/path/req.pem </code>
- <p>and the ask the Erlang CA to sign it:</p>
- <code type="none">
- openssl ca -batch -notext -config /some/path/req.cnf \\
- -extensions ca_cert -in /some/path/req.pem -out /some/path/cert.pem </code>
- <p>where the option <c>-extensions</c> refers to a section in the
- configuration file saying that it should create a CA certificate,
- and not a plain user certificate.
- </p>
- <p>The <c>client</c> and <c>server</c> certificates are created
- similarly, except that the option <c>-extensions</c> then has the
- value <c>user_cert</c>.
- </p>
- </section>
- </section>
-
- <section>
- <title>An Example</title>
- <p>The following module <c>create_certs</c> is used by the Erlang/OTP
- SSL application for generating certificates to be used in tests. The
- source code is also found in <c>ssl-X.Y.Z/examples/certs/src</c>.
- </p>
- <p>The purpose of the <c>create_certs:all/1</c> function is to make
- it possible to provide from the <c>erl</c> command line, the
- full path name of the <c>openssl</c> command.
- </p>
- <p>Note that the module creates temporary OpenSSL configuration files
- for the <c>req</c> and <c>ca</c> subcommands.
- </p>
- <codeinclude file="../../examples/certs/src/make_certs.erl" tag="" type="erl"></codeinclude>
- </section>
-</chapter>
-
-
diff --git a/lib/ssl/doc/src/insidecover.xml b/lib/ssl/doc/src/insidecover.xml
deleted file mode 100644
index 4f3f5e5951..0000000000
--- a/lib/ssl/doc/src/insidecover.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE bookinsidecover SYSTEM "bookinsidecover.dtd">
-
-<bookinsidecover>
-The Erlang/OTP SSL application includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/). Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. <br></br>
-This product includes cryptographic software written by Eric Young ([email protected]). This product includes software written by Tim Hudson ([email protected]). Copyright (C) 1995-1998 Eric Young ([email protected]). All rights reserved. <br></br>
-For further OpenSSL and SSLeay license information se the chapter <bold>Licenses</bold>
-. <vfill></vfill>
- <br></br>
- <tt>http://www.erlang.org</tt>
- <br></br>
-</bookinsidecover>
-
-
diff --git a/lib/ssl/doc/src/licenses.xml b/lib/ssl/doc/src/licenses.xml
deleted file mode 100644
index 0969f9ad6e..0000000000
--- a/lib/ssl/doc/src/licenses.xml
+++ /dev/null
@@ -1,156 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2003</year><year>2009</year>
- <holder>Ericsson AB. All Rights Reserved.</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved online at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- </legalnotice>
-
- <title>Licenses</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-05-26</date>
- <rev>A</rev>
- <file>licenses.xml</file>
- </header>
- <p> <marker id="licenses"></marker>
-This chapter contains in extenso versions
- of the OpenSSL and SSLeay licenses.
- </p>
-
- <section>
- <title>OpenSSL License</title>
- <code type="none">
-/* ====================================================================
- * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- * software must display the following acknowledgment:
- * "This product includes software developed by the OpenSSL Project
- * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- * endorse or promote products derived from this software without
- * prior written permission. For written permission, please contact
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- * nor may "OpenSSL" appear in their names without prior written
- * permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- * acknowledgment:
- * "This product includes software developed by the OpenSSL Project
- * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * ([email protected]). This product includes software written by Tim
- * Hudson ([email protected]).
- *
- */ </code>
- </section>
-
- <section>
- <title>SSLeay License</title>
- <code type="none">
-/* Copyright (C) 1995-1998 Eric Young ([email protected])
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young ([email protected]).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to. The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code. The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson ([email protected]).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * "This product includes cryptographic software written by
- * Eric Young ([email protected])"
- * The word 'cryptographic' can be left out if the rouines from the library
- * being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- * the apps directory (application code) you must include an acknowledgement:
- * "This product includes software written by Tim Hudson ([email protected])"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed. i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
- */ </code>
- </section>
-</chapter>
-
-
diff --git a/lib/ssl/doc/src/make.dep b/lib/ssl/doc/src/make.dep
deleted file mode 100644
index 2ff81bee1f..0000000000
--- a/lib/ssl/doc/src/make.dep
+++ /dev/null
@@ -1,30 +0,0 @@
-# ----------------------------------------------------
-# >>>> Do not edit this file <<<<
-# This file was automaticly generated by
-# /home/otp/bin/docdepend
-# ----------------------------------------------------
-
-
-# ----------------------------------------------------
-# TeX files that the DVI file depend on
-# ----------------------------------------------------
-
-book.dvi: book.tex create_certs.tex licenses.tex new_ssl.tex \
- pkix_certs.tex refman.tex ssl.tex ssl_app.tex \
- ssl_distribution.tex ssl_protocol.tex usersguide.tex \
- using_ssl.tex
-
-# ----------------------------------------------------
-# Source inlined when transforming from source to LaTeX
-# ----------------------------------------------------
-
-book.tex: refman.xml
-
-create_certs.tex: ../../examples/certs/src/make_certs.erl
-
-using_ssl.tex: ../../examples/src/client_server.erl
-
-pkix_certs.tex: ../../../../system/doc/definitions/cite.defs
-
-ssl_protocol.tex: ../../../../system/doc/definitions/cite.defs
-
diff --git a/lib/ssl/doc/src/new_ssl.xml b/lib/ssl/doc/src/new_ssl.xml
deleted file mode 100644
index ab6e112a35..0000000000
--- a/lib/ssl/doc/src/new_ssl.xml
+++ /dev/null
@@ -1,685 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE erlref SYSTEM "erlref.dtd">
-
-<erlref>
- <header>
- <copyright>
- <year>1999</year>
- <year>2007</year>
- <holder>Ericsson AB, All Rights Reserved</holder>
- </copyright>
- <legalnotice>
- The contents of this file are subject to the Erlang Public License,
- Version 1.1, (the "License"); you may not use this file except in
- compliance with the License. You should have received a copy of the
- Erlang Public License along with this software. If not, it can be
- retrieved aniline's at http://www.erlang.org/.
-
- Software distributed under the License is distributed on an "AS IS"
- basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
- the License for the specific language governing rights and limitations
- under the License.
-
- The Initial Developer of the Original Code is Ericsson AB.
- </legalnotice>
-
- <title>ssl</title>
- <prepared>Ingela Anderton Andin</prepared>
- <responsible>Ingela Anderton Andin</responsible>
- <docno></docno>
- <approved></approved>
- <checked></checked>
- <date>2003-03-25</date>
- <rev></rev>
- <file>new_ssl.xml</file>
- </header>
- <module>new_ssl</module>
- <modulesummary>Interface Functions for Secure Socket Layer</modulesummary>
- <description>
- <p>This module contains interface functions to the Secure Socket
- Layer.
- </p>
- </description>
-
- <section>
- <title>NEW SSL</title>
-
- <p>This manual page describes functions that are defined
- in the ssl module and represents the new ssl implementation
- that coexists with the old one, as the new implementation
- is not yet complete enough to replace the old one.</p>
-
- <p>The new implementation can be
- accessed by providing the option {ssl_imp, new} to the
- ssl:connect and ssl:listen functions.</p>
-
- <p>The new implementation is Erlang based and all logic
- is in Erlang and only payload encryption calculations are
- done in C via the crypto application. The main reason for
- making a new implementation is that the old solution was
- very crippled as the control of the ssl-socket was deep
- down in openssl making it hard if not impossible to
- support all inet options, ipv6 and upgrade of a tcp
- connection to a ssl connection. This version has a
- few limitations that will be removed before the ssl-4.0
- release. Main differences and limitations are listed below.</p>
-
- <list type="bulleted">
- <item>New ssl requires the crypto
- application.</item>
- <item>The option reuseaddr is
- supported and the default value is false as in gen_tcp.
- Old ssl is patched to accept that the option is set to
- true to provide a smoother migration between the
- versions. In old ssl the option is hard coded to
- true.</item>
- <item>ssl:version/0 is replaced by
- ssl:versions/0</item>
- <item>ssl:ciphers/0 is replaced by
- ssl:cipher_suites/0</item>
- <item>ssl:pid/1 is a
- meaningless function in new ssl and will be deprecated in
- ssl-4.0 until it is removed it will return a valid but
- meaningless pid.</item>
- <item>New API functions are
- ssl:shutdown/2, ssl:cipher_suites/[0,1] and
- ssl:versions/0</item>
- <item>CRL and policy certificate
- extensions are not supported yet. </item>
- <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
- <item>For security reasons sslv2 is not supported.</item>
- </list>
-
- </section>
-
- <section>
- <title>COMMON DATA TYPES</title>
- <p>The following data types are used in the functions below:
- </p>
-
- <p><c>boolean() = true | false</c></p>
-
- <p><c>property() = atom()</c></p>
-
- <p><c>option() = socketoption() | ssloption() | transportoption()</c></p>
-
- <p><c>socketoption() = [{property(), term()}] - defaults to
- [{mode,list},{packet, 0},{header, 0},{active, true}].
- </c></p>
-
- <p>For valid options
- see <seealso marker="kernel:inet">inet(3) </seealso> and
- <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>.
- </p>
-
- <p> <c>ssloption() = {verify, verify_type()} |
- {fail_if_no_peer_cert, boolean()}
- {depth, integer()} |
- {certfile, path()} | {keyfile, path()} | {password, string()} |
- {cacertfile, path()} | {dhfile, path()} | {ciphers, ciphers()} |
- {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
- </c></p>
-
- <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag}
- - defaults to {gen_tcp, tcp, tcp_closed}. Ssl may be
- run over any reliable transport protocol that has
- an equivalent API to gen_tcp's.</c></p>
-
- <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallbackModule =
- atom()</c>
- </p> <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataTag =
- atom() - tag used in socket data message.</c></p>
- <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClosedTag = atom() - tag used in
- socket close message.</c></p>
-
- <p><c>verify_type() = verify_none | verify_peer</c></p>
-
- <p><c>path() = string() - representing a file path.</c></p>
-
- <p><c>host() = hostname() | ipaddress()</c></p>
-
- <p><c>hostname() = string()</c></p>
-
- <p><c>
- ip_address() = {N1,N2,N3,N4} % IPv4
- | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 </c></p>
-
- <p><c>sslsocket() - opaque to the user. </c></p>
-
- <p><c>protocol() = sslv3 | tlsv1 </c></p>
-
- <p><c>ciphers() = [ciphersuite()] | sting() (according to old API)</c></p>
-
- <p><c>ciphersuite() =
- {key_exchange(), cipher(), hash(), exportable()}</c></p>
-
- <p><c>key_exchange() = rsa | dh_dss | dh_rsa | dh_anon | dhe_dss
- | dhe_rsa | krb5 | KeyExchange_export
- </c></p>
-
- <p><c>cipher() = rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc'
- des40_cbc | dh_dss | aes_128_cbc | aes_256_cbc |
- rc2_cbc_40 | rc4_40 </c></p>
-
- <p> <c>hash() = md5 | sha
- </c></p>
-
- <p> <c>exportable() = export | no_export | ignore
- </c></p>
-
- <p><c>ssl_imp() = new | old - default is old.</c></p>
-
- </section>
-
-<section>
- <title>SSL OPTION DESCRIPTIONS</title>
-
- <taglist>
- <tag>{verify, verify_type()}</tag>
- <item> If <c>verify_none</c> is specified x509-certificate
- path validation errors at the client side
- will not automatically cause the connection to fail, as
- it will if the verify type is <c>verify_peer</c>. See also
- the option verify_fun.
- Servers only do the path validation if <c>verify_peer</c> is set to
- true, as it then will
- send a certificate request to
- the client (this message is not sent if the verify option is
- <c>verify_none</c>) and you may then also want to specify
- the option <c>fail_if_no_peer_cert</c>.
- </item>
-
- <tag>{fail_if_no_peer_cert, boolean()}</tag>
- <item>Used together with {verify, verify_peer} by a ssl server.
- If set to true,
- the server will fail if the client does not have a certificate
- to send, e.i sends a empty certificate, if set to false it will
- only fail if the client sends a invalid certificate (an empty
- certificate is considered valid).
- </item>
-
- <tag>{verify_fun, fun(ErrorList) -> boolean()}</tag>
- <item>Used by the ssl client to determine if
- x509-certificate path validations errors are acceptable or
- if the connection should fail. Defaults to:
-
-<code>
-fun(ErrorList) ->
- case lists:foldl(fun({bad_cert,unknown_ca}, Acc) ->
- Acc;
- (Other, Acc) ->
- [Other | Acc]
- end, [], ErrorList) of
- [] ->
- true;
- [_|_] ->
- false
- end
-end
-</code>
- I.e. by default if the only error found was that the CA-certificate
- holder was unknown this will be accepted.
-
- Possible errors in the error list are:
- {bad_cert, cert_expired}, {bad_cert, invalid_issuer},
- {bad_cert, invalid_signature}, {bad_cert, name_not_permitted},
- {bad_cert, unknown_ca},
- {bad_cert, cert_expired}, {bad_cert, invalid_issuer},
- {bad_cert, invalid_signature}, {bad_cert, name_not_permitted},
- {bad_cert, cert_revoked} (not implemented yet),
- {bad_cert, unknown_critical_extension} or {bad_cert, term()} (Will
- be relevant later when an option is added for the user to be able to verify application specific extensions.)
- </item>
-
- <tag>{depth, integer()}</tag>
- <item>Specifies the maximum
- verification depth, i.e. how far in a chain of certificates the
- verification process can proceed before the verification is
- considered to fail. Peer certificate = 0, CA certificate = 1,
- higher level CA certificate = 2, etc. The value 2 thus means
- that a chain can at most contain peer cert, CA cert, next CA
- cert, and an additional CA cert. The default value is 1.
- </item>
-
- <tag>{certfile, path()}</tag>
- <item>Path to a file containing the
- user's certificate. Optional for clients but note
- that some servers requires that the client can certify
- itself. </item>
- <tag>{keyfile, path()}</tag>
- <item>Path to file containing user's
- private PEM encoded key. As PEM-files may contain several
- entries this option defaults to the same file as given by
- certfile option.</item>
- <tag>{password, string()}</tag>
- <item>String containing the user's password.
- Only used if the private keyfile is password protected.
- </item>
- <tag>{cacertfile, path()}</tag>
- <item>Path to file containing PEM encoded
- CA certificates (trusted certificates used for verifying a peer
- certificate). May be omitted if you do not want to verify
- the peer.</item>
-
- <tag>{dhfile, path()}</tag>
- <item>Path to file containing PEM encoded Diffie Hellman parameters,
- for the server to use if a cipher suite using Diffie Hellman key exchange
- is negotiated. If not specified hardcode parameters will be used.
- </item>
-
- <tag>{ciphers, ciphers()}</tag>
- <item>The function <c>ciphers_suites/0</c> can
- be used to find all available ciphers.
- </item>
-
- <tag>{ssl_imp, ssl_imp()}</tag>
- <item>Specify which ssl implementation you want to use.
- </item>
-
- <tag>{reuse_sessions, boolean()}</tag>
- <item>Specifies if ssl sessions should be reused
- when possible.
- </item>
-
- <tag>{reuse_session, fun(SuggestedSessionId,
- PeerCert, Compression, CipherSuite) -> boolean()}</tag>
- <item>Enables the ssl server to have a local policy
- for deciding if a session should be reused or not,
- only meaning full if <c>reuse_sessions</c> is set to true.
- SuggestedSessionId is a binary(), PeerCert is a DER encoded
- certificate, Compression is an enumeration integer
- and CipherSuite of type ciphersuite().
- </item>
- </taglist>
- </section>
-
- <section>
- <title>General</title>
-
- <p>When a ssl socket is in active mode (the default), data from the
- socket is delivered to the owner of the socket in the form of
- messages:
- </p>
- <list type="bulleted">
- <item>{ssl, Socket, Data}
- </item>
- <item>{ssl_closed, Socket}
- </item>
- <item>
- {ssl_error, Socket, Reason}
- </item>
- </list>
-
- <p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The
- default value for a <c>Timeout</c> argument is <c>infinity</c>.
- </p>
- </section>
-
- <funcs>
- <func>
- <name>cipher_suites() -></name>
- <name>cipher_suites(Type) -> ciphers()</name>
- <fsummary> Returns a list of supported cipher suites</fsummary>
- <type>
- <v>Type = erlang | openssl</v>
-
- </type>
- <desc><p>Returns a list of supported cipher suites.
- cipher_suites() is equivalent to cipher_suites(erlang).
- Type openssl is provided for backwards compatibility with
- old ssl that used openssl.
- </p>
- </desc>
- </func>
-
- <func>
- <name>connect(Socket, SslOptions) -> </name>
- <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket}
- | {error, Reason}</name>
- <fsummary> Upgrades a gen_tcp, or
- equivalent, connected socket to a ssl socket. </fsummary>
- <type>
- <v>Socket = socket()</v>
- <v>SslOptions = [ssloption()]</v>
- <v>Timeout = integer() | infinity</v>
- <v>SslSocket = sslsocket()</v>
- <v>Reason = term()</v>
- </type>
- <desc> <p>Upgrades a gen_tcp, or equivalent,
- connected socket to a ssl socket e.i performs the
- client-side ssl handshake.</p>
- </desc>
- </func>
-
- <func>
- <name>connect(Host, Port, Options) -></name>
- <name>connect(Host, Port, Options, Timeout) ->
- {ok, SslSocket} | {error, Reason}</name>
- <fsummary>Opens an ssl connection to Host, Port. </fsummary>
- <type>
- <v>Host = host()</v>
- <v>Port = integer()</v>
- <v>Options = [option()]</v>
- <v>Timeout = integer() | infinity</v>
- <v>SslSocket = sslsocket()</v>
- <v>Reason = term()</v>
- </type>
- <desc> <p>Opens an ssl connection to Host, Port.</p> </desc>
- </func>
-
- <func>
- <name>close(SslSocket) -> ok | {error, Reason}</name>
- <fsummary>Close a ssl connection</fsummary>
- <type>
- <v>SslSocket = sslsocket()</v>
- <v>Reason = term()</v>
- </type>
- <desc><p>Close a ssl connection.</p>
- </desc>
- </func>
-
- <func>
- <name>controlling_process(SslSocket, NewOwner) ->
- ok | {error, Reason}</name>
-
- <fsummary>Assigns a new controlling process to the
- ssl-socket.</fsummary>
-
- <type>
- <v>SslSocket = sslsocket()</v>
- <v>NewOwner = pid()</v>
- <v>Reason = term()</v>
- </type>
- <desc><p>Assigns a new controlling process to the ssl-socket. A
- controlling process is the owner of a ssl-socket, and receives
- all messages from the socket.</p>
- </desc>
- </func>
-
- <func>
- <name>connection_info(SslSocket) ->
- {ok, {ProtocolVersion, CipherSuite}} | {error, Reason} </name>
- <fsummary>Returns the negotiated protocol version and cipher suite.
- </fsummary>
- <type>
- <v>CipherSuite = ciphersuite()</v>
- <v>ProtocolVersion = protocol()</v>
- </type>
- <desc><p>Returns the negotiated protocol version and cipher suite.</p>
- </desc>
- </func>
-
- <func>
- <name>getopts(Socket) -> </name>
- <name>getopts(Socket, OptionNames) ->
- {ok, [socketoption()]} | {error, Reason}</name>
- <fsummary>Get the value of the specified options.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>OptionNames = [property()]</v>
- </type>
- <desc>
- <p>Get the value of the specified socket options, if no
- options are specified all options are returned.
- </p>
- </desc>
- </func>
-
- <func>
- <name>listen(Port, Options) ->
- {ok, ListenSocket} | {error, Reason}</name>
- <fsummary>Creates a ssl listen socket.</fsummary>
- <type>
- <v>Port = integer()</v>
- <v>Options = options()</v>
- <v>ListenSocket = sslsocket()</v>
- </type>
- <desc>
- <p>Creates a ssl listen socket.</p>
- </desc>
- </func>
-
- <func>
- <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
- <fsummary>Return the peer certificate.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Cert = binary()</v>
- <v>Subject = term()</v>
- </type>
- <desc>
- <p>The peer certificate is returned as a DER encoded binary.
- The certificate can be decoded with <c>public_key:pkix_decode_cert/2</c>.
- </p>
- </desc>
- </func>
- <func>
- <name>peername(Socket) -> {ok, {Address, Port}} |
- {error, Reason}</name>
- <fsummary>Return peer address and port.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Address = ipaddress()</v>
- <v>Port = integer()</v>
- </type>
- <desc>
- <p>Returns the address and port number of the peer.</p>
- </desc>
- </func>
-
- <func>
- <name>recv(Socket, Length) -> </name>
- <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error,
- Reason}</name>
- <fsummary>Receive data on a socket.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Length = integer()</v>
- <v>Timeout = integer()</v>
- <v>Data = [char()] | binary()</v>
- </type>
- <desc>
- <p>This function receives a packet from a socket in passive
- mode. A closed socket is indicated by a return value
- <c>{error, closed}</c>.</p>
- <p>The <c>Length</c> argument is only meaningful when
- the socket is in <c>raw</c> mode and denotes the number of
- bytes to read. If <c>Length</c> = 0, all available bytes are
- returned. If <c>Length</c> &gt; 0, exactly <c>Length</c>
- bytes are returned, or an error; possibly discarding less
- than <c>Length</c> bytes of data when the socket gets closed
- from the other side.</p>
- <p>The optional <c>Timeout</c> parameter specifies a timeout in
- milliseconds. The default value is <c>infinity</c>.</p>
- </desc>
- </func>
-
- <func>
- <name>renegotiate(Socket) -> ok | {error, Reason}</name>
- <fsummary> Initiates a new handshake.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- </type>
- <desc><p>Initiates a new handshake. A notable return value is
- <c>{error, renegotiation_rejected}</c> indicating that the peer
- refused to go through with the renegotiation but the connection
- is still active using the previously negotiated session.</p>
- </desc>
- </func>
-
- <func>
- <name>send(Socket, Data) -> ok | {error, Reason}</name>
- <fsummary>Write data to a socket.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Data = iolist() | binary()</v>
- </type>
- <desc>
- <p>Writes <c>Data</c> to <c>Socket</c>. </p>
- <p>A notable return value is <c>{error, closed}</c> indicating that
- the socket is closed.</p>
- </desc>
- </func>
- <func>
- <name>setopts(Socket, Options) -> ok | {error, Reason}</name>
- <fsummary>Set socket options.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Options = [socketoption]()</v>
- </type>
- <desc>
- <p>Sets options according to <c>Options</c> for the socket
- <c>Socket</c>. </p>
- </desc>
- </func>
-
- <func>
- <name>shutdown(Socket, How) -> ok | {error, Reason}</name>
- <fsummary>Immediately close a socket</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>How = read | write | read_write</v>
- <v>Reason = reason()</v>
- </type>
- <desc>
- <p>Immediately close a socket in one or two directions.</p>
- <p><c>How == write</c> means closing the socket for writing,
- reading from it is still possible.</p>
- <p>To be able to handle that the peer has done a shutdown on
- the write side, the <c>{exit_on_close, false}</c> option
- is useful.</p>
- </desc>
- </func>
-
- <func>
- <name>ssl_accept(ListenSocket) -> </name>
- <name>ssl_accept(ListenSocket, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Perform server-side SSL handshake</fsummary>
- <type>
- <v>ListenSocket = sslsocket()</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p>The <c>ssl_accept</c> function establish the SSL connection
- on the server side. It should be called directly after
- <c>transport_accept</c>, in the spawned server-loop.</p>
- </desc>
- </func>
-
- <func>
- <name>ssl_accept(ListenSocket, SslOptions) -> </name>
- <name>ssl_accept(ListenSocket, SslOptions, Timeout) -> {ok, Socket} | {error, Reason}</name>
- <fsummary>Perform server-side SSL handshake</fsummary>
- <type>
- <v>ListenSocket = socket()</v>
- <v>SslOptions = ssloptions()</v>
- <v>Timeout = integer()</v>
- <v>Reason = term()</v>
- </type>
- <desc>
- <p> Upgrades a gen_tcp, or
- equivalent, socket to a ssl socket e.i performs the
- ssl server-side handshake.</p>
- <p><note>Note that the listen socket should be in {active, false} mode
- before telling the client that the server is ready to upgrade
- and calling this function, otherwise the upgrade may
- or may not succeed depending on timing.</note></p>
- </desc>
- </func>
-
- <func>
- <name>sockname(Socket) -> {ok, {Address, Port}} |
- {error, Reason}</name>
- <fsummary>Return the local address and port.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Address = ipaddress()</v>
- <v>Port = integer()</v>
- </type>
- <desc>
- <p>Returns the local address and port number of the socket
- <c>Socket</c>.</p>
- </desc>
- </func>
-
- <func>
- <name>start() -> </name>
- <name>start(Type) -> ok | {error, Reason}</name>
- <fsummary>Starts the Ssl application. </fsummary>
- <type>
- <v>Type = permanent | transient | temporary</v>
- </type>
- <desc>
- <p>Starts the Ssl application. Default type
- is temporary.
- <seealso marker="kernel:application">application(3)</seealso></p>
- </desc>
- </func>
- <func>
- <name>stop() -> ok </name>
- <fsummary>Stops the Ssl application.</fsummary>
- <desc>
- <p>Stops the Ssl application.
- <seealso marker="kernel:application">application(3)</seealso></p>
- </desc>
- </func>
-
- <func>
- <name>transport_accept(Socket) -></name>
- <name>transport_accept(Socket, Timeout) ->
- {ok, NewSocket} | {error, Reason}</name>
- <fsummary>Accept an incoming connection and
- prepare for <c>ssl_accept</c></fsummary>
- <type>
- <v>Socket = NewSocket = sslsocket()</v>
- <v>Timeout = integer()</v>
- <v>Reason = reason()</v>
- </type>
- <desc>
- <p>Accepts an incoming connection request on a listen socket.
- <c>ListenSocket</c> must be a socket returned from
- <c>listen/2</c>. The socket returned should be passed to
- <c>ssl_accept</c> to complete ssl handshaking and
- establishing the connection.</p>
- <warning>
- <p>The socket returned can only be used with <c>ssl_accept</c>,
- no traffic can be sent or received before that call.</p>
- </warning>
- <p>The accepted socket inherits the options set for
- <c>ListenSocket</c> in <c>listen/2</c>.</p>
- <p>The default
- value for <c>Timeout</c> is <c>infinity</c>. If
- <c>Timeout</c> is specified, and no connection is accepted
- within the given time, <c>{error, timeout}</c> is
- returned.</p>
- </desc>
- </func>
-
- <func>
- <name>versions() ->
- [{SslAppVer, SupportedSslVer, AvailableSslVsn}]</name>
- <fsummary>Returns version information relevant for the
- ssl application.</fsummary>
- <type>
- <v>SslAppVer = string()</v>
- <v>SupportedSslVer = [protocol()]</v>
- <v>AvailableSslVsn = [protocol()]</v>
- </type>
- <desc>
- <p>
- Returns version information relevant for the
- ssl application.</p>
- </desc>
- </func>
- </funcs>
-
- <section>
- <title>SEE ALSO</title>
- <p><seealso marker="kernel:inet">inet(3) </seealso> and
- <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>
- </p>
- </section>
-
-</erlref>
-
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 9d13427677..52ee9c086a 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2010</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -30,6 +30,355 @@
</header>
<p>This document describes the changes made to the SSL application.
</p>
+
+ <section><title>SSL 4.1.4</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Reduced memory footprint of an ssl connection.</p>
+ <p>
+ Handshake hashes, premaster secret and "public_key_info"
+ does not need to be saved when the connection has been
+ established. The own certificate is no longer duplicated
+ in the state.</p>
+ <p>
+ Own Id: OTP-9021</p>
+ </item>
+ <item>
+ <p>
+ Add the option {hibernate_after, int()} to ssl:connect
+ and ssl:listen</p>
+ <p>
+ Own Id: OTP-9106</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 4.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed error in cache-handling fix from ssl-4.1.2</p>
+ <p>
+ Own Id: OTP-9018 Aux Id: seq11739 </p>
+ </item>
+ <item>
+ <p>
+ Verification of a critical extended_key_usage-extension
+ corrected</p>
+ <p>
+ Own Id: OTP-9029 Aux Id: seq11541 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 4.1.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The ssl application caches certificate files, it will now
+ invalidate cache entries if the diskfile is changed.</p>
+ <p>
+ Own Id: OTP-8965 Aux Id: seq11739 </p>
+ </item>
+ <item>
+ <p>
+ Now runs the terminate function before returning from the
+ call made by ssl:close/1, as before the caller of
+ ssl:close/1 could get problems with the reuseaddr option.</p>
+ <p>
+ Own Id: OTP-8992</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 4.1.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Correct handling of client certificate verify message
+ When checking the client certificate verify message the
+ server used the wrong algorithm identifier to determine
+ the signing algorithm, causing a function clause error in
+ the public_key application when the key-exchange
+ algorithm and the public key algorithm of the client
+ certificate happen to differ.</p>
+ <p>
+ Own Id: OTP-8897</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ For testing purposes ssl now also support some anonymous
+ cipher suites when explicitly configured to do so.</p>
+ <p>
+ Own Id: OTP-8870</p>
+ </item>
+ <item>
+ <p>
+ Sends an error alert instead of crashing if a crypto
+ function for the selected cipher suite fails.</p>
+ <p>
+ Own Id: OTP-8930 Aux Id: seq11720 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 4.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Updated ssl to ignore CA certs that violate the asn1-spec
+ for a certificate, and updated public key asn1 spec to
+ handle inherited DSS-params.</p>
+ <p>
+ Own Id: OTP-7884</p>
+ </item>
+ <item>
+ <p>
+ Changed ssl implementation to retain backwards
+ compatibility for old option {verify, 0} that shall be
+ equivalent to {verify, verify_none}, also separate the
+ cases unknown ca and selfsigned peer cert, and restored
+ return value of deprecated function
+ public_key:pem_to_der/1.</p>
+ <p>
+ Own Id: OTP-8858</p>
+ </item>
+ <item>
+ <p>
+ Changed the verify fun so that it differentiate between
+ the peer certificate and CA certificates by using
+ valid_peer or valid as the second argument to the verify
+ fun. It may not always be trivial or even possible to
+ know when the peer certificate is reached otherwise.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8873</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 4.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The server now verifies the client certificate verify
+ message correctly, instead of causing a case-clause.</p>
+ <p>
+ Own Id: OTP-8721</p>
+ </item>
+ <item>
+ <p>
+ The client hello message now always include ALL available
+ cipher suites (or those specified by the ciphers option).
+ Previous implementation would filter them based on the
+ client certificate key usage extension (such filtering
+ only makes sense for the server certificate).</p>
+ <p>
+ Own Id: OTP-8772</p>
+ </item>
+ <item>
+ <p>
+ Fixed handling of the option {mode, list} that was broken
+ for some packet types for instance line.</p>
+ <p>
+ Own Id: OTP-8785</p>
+ </item>
+ <item>
+ <p>
+ Empty packets were not delivered to the client.</p>
+ <p>
+ Own Id: OTP-8790</p>
+ </item>
+ <item>
+ <p> Building in a source tree without prebuilt platform
+ independent build results failed on the SSL examples
+ when: </p> <list><item> cross building. This has been
+ solved by not building the SSL examples during a cross
+ build. </item><item> building on Windows. </item></list>
+ <p>
+ Own Id: OTP-8791</p>
+ </item>
+ <item>
+ <p>
+ Fixed a handshake error which occurred on some ssl
+ implementations.</p>
+ <p>
+ Own Id: OTP-8793</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Revise the public_key API - Cleaned up and documented the
+ public_key API to make it useful for general use, also
+ changed ssl to use the new API.</p>
+ <p>
+ Own Id: OTP-8722</p>
+ </item>
+ <item>
+ <p>
+ Added support for inputing certificates and keys directly
+ in DER format these options will override the pem-file
+ options if specified.</p>
+ <p>
+ Own Id: OTP-8723</p>
+ </item>
+ <item>
+ <p>
+ To gain interoperability ssl will not check for padding
+ errors when using TLS 1.0. It is first in TLS 1.1 that
+ checking the padding is an requirement.</p>
+ <p>
+ Own Id: OTP-8740</p>
+ </item>
+ <item>
+ <p>
+ Changed the semantics of the verify_fun option in the
+ ssl-application so that it takes care of both application
+ handling of path validation errors and verification of
+ application specific extensions. This means that it is
+ now possible for the server application in verify_peer
+ mode to handle path validation errors. This change moved
+ some functionality earlier in ssl to the public_key
+ application.</p>
+ <p>
+ Own Id: OTP-8770</p>
+ </item>
+ <item>
+ <p>
+ Added the functionality so that the verification fun will
+ be called when a certificate is considered valid by the
+ path validation to allow access to each certificate in
+ the path to the user application. Also try to verify
+ subject-AltName, if unable to verify it let the
+ application verify it.</p>
+ <p>
+ Own Id: OTP-8825</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 4.0</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ New ssl now support client/server-certificates signed by
+ dsa keys.</p>
+ <p>
+ Own Id: OTP-8587</p>
+ </item>
+ <item>
+ <p>
+ Ssl has now switched default implementation and removed
+ deprecated certificate handling. All certificate handling
+ is done by the public_key application.</p>
+ <p>
+ Own Id: OTP-8695</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+
+ <section><title>SSL 3.11.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed handling of several ssl/tls packets arriving at the
+ same time. This was broken during a refactoring of the
+ code.</p>
+ <p>
+ Own Id: OTP-8679</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Added missing checks for padding and Mac value. Removed
+ code for export ciphers and DH certificates as we decided
+ not to support them.</p>
+ <p>
+ Own Id: OTP-7047</p>
+ </item>
+ <item>
+ <p>
+ New ssl will no longer return esslerrssl to be backwards
+ compatible with old ssl as this hids infomation from the
+ user. format_error/1 has been updated to support new ssl.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-7049</p>
+ </item>
+ <item>
+ <p>
+ New ssl now supports secure renegotiation as described by
+ RFC 5746.</p>
+ <p>
+ Own Id: OTP-8568</p>
+ </item>
+ <item>
+ <p>
+ Alert handling has been improved to better handle
+ unexpected but valid messages and the implementation is
+ also changed to avoid timing related issues that could
+ cause different error messages depending on network
+ latency. Packet handling was sort of broken but would
+ mostly work as expected when socket was in binary mode.
+ This has now been fixed.</p>
+ <p>
+ Own Id: OTP-8588</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 3.11</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -733,7 +1082,7 @@
<title>Fixed Bugs and Malfunctions</title>
<list type="bulleted">
<item>
- <p>When a file descriptor was marked for closing, and and
+ <p>When a file descriptor was marked for closing, and
end-of-file condition had already been detected, the file
descriptor was never closed.</p>
<p>Own Id: OTP-5093 Aux Id: seq8806 </p>
diff --git a/lib/ssl/doc/src/old_ssl.xml b/lib/ssl/doc/src/old_ssl.xml
new file mode 100644
index 0000000000..0d2e1afdbd
--- /dev/null
+++ b/lib/ssl/doc/src/old_ssl.xml
@@ -0,0 +1,709 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1999</year><year>2010</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>ssl</title>
+ <prepared>Peter H&ouml;gfeldt</prepared>
+ <responsible>Peter H&ouml;gfeldt</responsible>
+ <docno></docno>
+ <approved>Peter H&ouml;gfeldt</approved>
+ <checked></checked>
+ <date>2003-03-25</date>
+ <rev>D</rev>
+ <file>old_ssl.xml</file>
+ </header>
+ <module>old_ssl</module>
+ <modulesummary>Interface Functions for Secure Socket Layer</modulesummary>
+ <description>
+ <p>This module contains interface functions to the Secure Socket Layer.</p>
+ </description>
+
+ <section>
+ <title>General</title>
+
+ <p>This manual page describes functions that are defined
+ in the ssl module and represents the old ssl implementation
+ that coexists with the new one until it has been
+ totally phased out. </p>
+
+ <p>The old implementation can be
+ accessed by providing the option {ssl_imp, old} to the
+ ssl:connect and ssl:listen functions.</p>
+
+ <p>The reader is advised to also read the <c>ssl(6)</c> manual page
+ describing the SSL application.
+ </p>
+ <warning>
+ <p>It is strongly advised to seed the random generator after
+ the ssl application has been started (see <c>seed/1</c>
+ below), and before any connections are established. Although
+ the port program interfacing to the ssl libraries does a
+ "random" seeding of its own in order to make everything work
+ properly, that seeding is by no means random for the world
+ since it has a constant value which is known to everyone
+ reading the source code of the port program.</p>
+ </warning>
+ </section>
+
+ <section>
+ <title>Common data types</title>
+ <p>The following datatypes are used in the functions below:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>options() = [option()]</c></p>
+ </item>
+ <item>
+ <p><c>option() = socketoption() | ssloption()</c></p>
+ </item>
+ <item>
+ <p><c>socketoption() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {port, integer()}</c></p>
+ </item>
+ <item>
+ <p><c>ssloption() = {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</c></p>
+ </item>
+ <item>
+ <p><c>packettype()</c> (see inet(3))</p>
+ </item>
+ <item>
+ <p><c>activetype()</c> (see inet(3))</p>
+ </item>
+ <item>
+ <p><c>reason() = atom() | {atom(), string()}</c></p>
+ </item>
+ <item>
+ <p><c>bytes() = [byte()]</c></p>
+ </item>
+ <item>
+ <p><c>string() = [byte()]</c></p>
+ </item>
+ <item>
+ <p><c>byte() = 0 | 1 | 2 | ... | 255</c></p>
+ </item>
+ <item>
+ <p><c>code() = 0 | 1 | 2</c></p>
+ </item>
+ <item>
+ <p><c>depth() = byte()</c></p>
+ </item>
+ <item>
+ <p><c>address() = hostname() | ipstring() | ipaddress()</c></p>
+ </item>
+ <item>
+ <p><c>ipaddress() = ipstring() | iptuple()</c></p>
+ </item>
+ <item>
+ <p><c>hostname() = string()</c></p>
+ </item>
+ <item>
+ <p><c>ipstring() = string()</c></p>
+ </item>
+ <item>
+ <p><c>iptuple() = {byte(), byte(), byte(), byte()}</c></p>
+ </item>
+ <item>
+ <p><c>sslsocket()</c></p>
+ </item>
+ <item>
+ <p><c>protocol() = sslv2 | sslv3 | tlsv1</c></p>
+ </item>
+ <item>
+ <p><c></c></p>
+ </item>
+ </list>
+ <p>The socket option <c>{backlog, integer()}</c> is for
+ <c>listen/2</c> only, and the option <c>{port, integer()}</c>
+ is for <c>connect/3/4</c> only.
+ </p>
+ <p>The following socket options are set by default: <c>{mode, list}</c>, <c>{packet, 0}</c>, <c>{header, 0}</c>, <c>{nodelay, false}</c>, <c>{active, true}</c>, <c>{backlog, 5}</c>,
+ <c>{ip, {0,0,0,0}}</c>, and <c>{port, 0}</c>.
+ </p>
+ <p>Note that the options <c>{mode, binary}</c> and <c>binary</c>
+ are equivalent. Similarly <c>{mode, list}</c> and the absence of
+ option <c>binary</c> are equivalent.
+ </p>
+ <p>The ssl options are for setting specific SSL parameters as follows:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{verify, code()}</c> Specifies type of verification:
+ 0 = do not verify peer; 1 = verify peer, 2 = verify peer,
+ fail if no peer certificate. The default value is 0.
+ </p>
+ </item>
+ <item>
+ <p><c>{depth, depth()}</c> Specifies the maximum
+ verification depth, i.e. how far in a chain of certificates
+ the verification process can proceed before the verification
+ is considered to fail.
+ </p>
+ <p>Peer certificate = 0, CA certificate = 1, higher level CA
+ certificate = 2, etc. The value 2 thus means that a chain
+ can at most contain peer cert, CA cert, next CA cert, and an
+ additional CA cert.
+ </p>
+ <p>The default value is 1.
+ </p>
+ </item>
+ <item>
+ <p><c>{certfile, path()}</c> Path to a file containing the
+ user's certificate.
+ chain of PEM encoded certificates.</p>
+ </item>
+ <item>
+ <p><c>{keyfile, path()}</c> Path to file containing user's
+ private PEM encoded key.</p>
+ </item>
+ <item>
+ <p><c>{password, string()}</c> String containing the user's
+ password. Only used if the private keyfile is password protected.</p>
+ </item>
+ <item>
+ <p><c>{cacertfile, path()}</c> Path to file containing PEM encoded
+ CA certificates (trusted certificates used for verifying a peer
+ certificate).</p>
+ </item>
+ <item>
+ <p><c>{ciphers, string()}</c> String of ciphers as a colon
+ separated list of ciphers. The function <c>ciphers/0</c> can
+ be used to find all available ciphers.</p>
+ </item>
+ </list>
+ <p>The type <c>sslsocket()</c> is opaque to the user.
+ </p>
+ <p>The owner of a socket is the one that created it by a call to
+ <c>transport_accept/[1,2]</c>, <c>connect/[3,4]</c>,
+ or <c>listen/2</c>.
+ </p>
+ <p>When a socket is in active mode (the default), data from the
+ socket is delivered to the owner of the socket in the form of
+ messages:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{ssl, Socket, Data}</c></p>
+ </item>
+ <item>
+ <p><c>{ssl_closed, Socket}</c></p>
+ </item>
+ <item>
+ <p><c>{ssl_error, Socket, Reason}</c></p>
+ </item>
+ </list>
+ <p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The
+ default value for a <c>Timeout</c> argument is <c>infinity</c>.
+ </p>
+ <p>Functions listed below may return the value <c>{error, closed}</c>, which only indicates that the SSL socket is
+ considered closed for the operation in question. It is for
+ instance possible to have <c>{error, closed}</c> returned from
+ an call to <c>send/2</c>, and a subsequent call to <c>recv/3</c>
+ returning <c>{ok, Data}</c>.
+ </p>
+ <p>Hence a return value of <c>{error, closed}</c> must not be
+ interpreted as if the socket was completely closed. On the
+ contrary, in order to free all resources occupied by an SSL
+ socket, <c>close/1</c> must be called, or else the process owning
+ the socket has to terminate.
+ </p>
+ <p>For each SSL socket there is an Erlang process representing the
+ socket. When a socket is opened, that process links to the
+ calling client process. Implementations that want to detect
+ abnormal exits from the socket process by receiving <c>{'EXIT', Pid, Reason}</c> messages, should use the function <c>pid/1</c>
+ to retrieve the process identifier from the socket, in order to
+ be able to match exit messages properly.</p>
+ </section>
+ <funcs>
+ <func>
+ <name>ciphers() -> {ok, string()} | {error, enotstarted}</name>
+ <fsummary>Get supported ciphers.</fsummary>
+ <desc>
+ <p>Returns a string consisting of colon separated cipher
+ designations that are supported by the current SSL library
+ implementation.
+ </p>
+ <p>The SSL application has to be started to return the string
+ of ciphers.</p>
+ </desc>
+ </func>
+ <func>
+ <name>close(Socket) -> ok | {error, Reason}</name>
+ <fsummary>Close a socket returned by <c>transport_accept/[1,2]</c>, <c>connect/3/4</c>, or <c>listen/2</c>.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ </type>
+ <desc>
+ <p>Closes a socket returned by <c>transport_accept/[1,2]</c>,
+ <c>connect/[3,4]</c>, or <c>listen/2</c></p>
+ </desc>
+ </func>
+ <func>
+ <name>connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}</name>
+ <name>connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}</name>
+ <fsummary>Connect to <c>Port</c>at <c>Address</c>.</fsummary>
+ <type>
+ <v>Address = address()</v>
+ <v>Port = integer()</v>
+ <v>Options = [connect_option()]</v>
+ <v>connect_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {ip, ipaddress()} | {port, integer()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v>
+ <v>Timeout = integer()</v>
+ <v>Socket = sslsocket()</v>
+ </type>
+ <desc>
+ <p>Connects to <c>Port</c> at <c>Address</c>. If the optional
+ <c>Timeout</c> argument is specified, and a connection could not
+ be established within the given time, <c>{error, timeout}</c> is
+ returned. The default value for <c>Timeout</c> is <c>infinity</c>.
+ </p>
+ <p>The <c>ip</c> and <c>port</c> options are for binding to a
+ particular <em>local</em> address and port, respectively.</p>
+ </desc>
+ </func>
+ <func>
+ <name>connection_info(Socket) -> {ok, {Protocol, Cipher}} | {error, Reason}</name>
+ <fsummary>Get current protocol version and cipher.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Protocol = protocol()</v>
+ <v>Cipher = string()</v>
+ </type>
+ <desc>
+ <p>Gets the chosen protocol version and cipher for an established
+ connection (accepted och connected). </p>
+ </desc>
+ </func>
+ <func>
+ <name>controlling_process(Socket, NewOwner) -> ok | {error, Reason}</name>
+ <fsummary>Assign a new controlling process to the socket.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>NewOwner = pid()</v>
+ </type>
+ <desc>
+ <p>Assigns a new controlling process to <c>Socket</c>. A controlling
+ process is the owner of a socket, and receives all messages from
+ the socket.</p>
+ </desc>
+ </func>
+ <func>
+ <name>format_error(ErrorCode) -> string()</name>
+ <fsummary>Return an error string.</fsummary>
+ <type>
+ <v>ErrorCode = term()</v>
+ </type>
+ <desc>
+ <p>Returns a diagnostic string describing an error.</p>
+ </desc>
+ </func>
+ <func>
+ <name>getopts(Socket, OptionsTags) -> {ok, Options} | {error, Reason}</name>
+ <fsummary>Get options set for socket</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>OptionTags = [optiontag()]()</v>
+ </type>
+ <desc>
+ <p>Returns the options the tags of which are <c>OptionTags</c> for
+ for the socket <c>Socket</c>. </p>
+ </desc>
+ </func>
+ <func>
+ <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name>
+ <fsummary>Set up a socket to listen on a port on the local host.</fsummary>
+ <type>
+ <v>Port = integer()</v>
+ <v>Options = [listen_option()]</v>
+ <v>listen_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v>
+ <v>ListenSocket = sslsocket()</v>
+ </type>
+ <desc>
+ <p>Sets up a socket to listen on port <c>Port</c> at the local host.
+ If <c>Port</c> is zero, <c>listen/2</c> picks an available port
+ number (use <c>port/1</c> to retrieve it).
+ </p>
+ <p>The listen queue size defaults to 5. If a different value is
+ wanted, the option <c>{backlog, Size}</c> should be added to the
+ list of options.
+ </p>
+ <p>An empty <c>Options</c> list is considered an error, and
+ <c>{error, enooptions}</c> is returned.
+ </p>
+ <p>The returned <c>ListenSocket</c> can only be used in calls to
+ <c>transport_accept/[1,2]</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
+ <fsummary>Return the peer certificate.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Cert = binary()()</v>
+ <v>Subject = term()()</v>
+ </type>
+ <desc>
+ <p>Returns the DER encoded peer certificate, the certificate can be decoded with
+ <c>public_key:pkix_decode_cert/2</c>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>peername(Socket) -> {ok, {Address, Port}} | {error, Reason}</name>
+ <fsummary>Return peer address and port.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Address = ipaddress()</v>
+ <v>Port = integer()</v>
+ </type>
+ <desc>
+ <p>Returns the address and port number of the peer.</p>
+ </desc>
+ </func>
+ <func>
+ <name>pid(Socket) -> pid()</name>
+ <fsummary>Return the pid of the socket process.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ </type>
+ <desc>
+ <p>Returns the pid of the socket process. The returned pid should
+ only be used for receiving exit messages.</p>
+ </desc>
+ </func>
+ <func>
+ <name>recv(Socket, Length) -> {ok, Data} | {error, Reason}</name>
+ <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error, Reason}</name>
+ <fsummary>Receive data on socket.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Length = integer() >= 0</v>
+ <v>Timeout = integer()</v>
+ <v>Data = bytes() | binary()</v>
+ </type>
+ <desc>
+ <p>Receives data on socket <c>Socket</c> when the socket is in
+ passive mode, i.e. when the option <c>{active, false}</c>
+ has been specified.
+ </p>
+ <p>A notable return value is <c>{error, closed}</c> which
+ indicates that the socket is closed.
+ </p>
+ <p>A positive value of the <c>Length</c> argument is only
+ valid when the socket is in raw mode (option <c>{packet, 0}</c> is set, and the option <c>binary</c> is <em>not</em>
+ set); otherwise it should be set to 0, whence all available
+ bytes are returned.
+ </p>
+ <p>If the optional <c>Timeout</c> parameter is specified, and
+ no data was available within the given time, <c>{error, timeout}</c> is returned. The default value for
+ <c>Timeout</c> is <c>infinity</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>seed(Data) -> ok | {error, Reason}</name>
+ <fsummary>Seed the ssl random generator.</fsummary>
+ <type>
+ <v>Data = iolist() | binary()</v>
+ </type>
+ <desc>
+ <p>Seeds the ssl random generator.
+ </p>
+ <p>It is strongly advised to seed the random generator after
+ the ssl application has been started, and before any
+ connections are established. Although the port program
+ interfacing to the OpenSSL libraries does a "random" seeding
+ of its own in order to make everything work properly, that
+ seeding is by no means random for the world since it has a
+ constant value which is known to everyone reading the source
+ code of the seeding.
+ </p>
+ <p>A notable return value is <c>{error, edata}}</c> indicating that
+ <c>Data</c> was not a binary nor an iolist.</p>
+ </desc>
+ </func>
+ <func>
+ <name>send(Socket, Data) -> ok | {error, Reason}</name>
+ <fsummary>Write data to a socket.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Data = iolist() | binary()</v>
+ </type>
+ <desc>
+ <p>Writes <c>Data</c> to <c>Socket</c>. </p>
+ <p>A notable return value is <c>{error, closed}</c> indicating that
+ the socket is closed.</p>
+ </desc>
+ </func>
+ <func>
+ <name>setopts(Socket, Options) -> ok | {error, Reason}</name>
+ <fsummary>Set socket options.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Options = [socketoption]()</v>
+ </type>
+ <desc>
+ <p>Sets options according to <c>Options</c> for the socket
+ <c>Socket</c>. </p>
+ </desc>
+ </func>
+ <func>
+ <name>ssl_accept(Socket) -> ok | {error, Reason}</name>
+ <name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Perform server-side SSL handshake and key exchange</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = atom()</v>
+ </type>
+ <desc>
+ <p>The <c>ssl_accept</c> function establish the SSL connection
+ on the server side. It should be called directly after
+ <c>transport_accept</c>, in the spawned server-loop.</p>
+ <p>Note that the ssl connection is not complete until <c>ssl_accept</c>
+ has returned <c>true</c>, and if an error is returned, the socket
+ is unavailable and for instance <c>close/1</c> will crash.</p>
+ </desc>
+ </func>
+ <func>
+ <name>sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}</name>
+ <fsummary>Return the local address and port.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Address = ipaddress()</v>
+ <v>Port = integer()</v>
+ </type>
+ <desc>
+ <p>Returns the local address and port number of the socket
+ <c>Socket</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>transport_accept(Socket) -> {ok, NewSocket} | {error, Reason}</name>
+ <name>transport_accept(Socket, Timeout) -> {ok, NewSocket} | {error, Reason}</name>
+ <fsummary>Accept an incoming connection and prepare for <c>ssl_accept</c></fsummary>
+ <type>
+ <v>Socket = NewSocket = sslsocket()</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = atom()</v>
+ </type>
+ <desc>
+ <p>Accepts an incoming connection request on a listen socket.
+ <c>ListenSocket</c> must be a socket returned from <c>listen/2</c>.
+ The socket returned should be passed to <c>ssl_accept</c> to
+ complete ssl handshaking and establishing the connection.</p>
+ <warning>
+ <p>The socket returned can only be used with <c>ssl_accept</c>,
+ no traffic can be sent or received before that call.</p>
+ </warning>
+ <p>The accepted socket inherits the options set for <c>ListenSocket</c>
+ in <c>listen/2</c>.</p>
+ <p>The default value for <c>Timeout</c> is <c>infinity</c>. If
+ <c>Timeout</c> is specified, and no connection is accepted within
+ the given time, <c>{error, timeout}</c> is returned.</p>
+ </desc>
+ </func>
+ <func>
+ <name>version() -> {ok, {SSLVsn, CompVsn, LibVsn}}</name>
+ <fsummary>Return the version of SSL.</fsummary>
+ <type>
+ <v>SSLVsn = CompVsn = LibVsn = string()()</v>
+ </type>
+ <desc>
+ <p>Returns the SSL application version (<c>SSLVsn</c>), the library
+ version used when compiling the SSL application port program
+ (<c>CompVsn</c>), and the actual library version used when
+ dynamically linking in runtime (<c>LibVsn</c>).
+ </p>
+ <p>If the SSL application has not been started, <c>CompVsn</c> and
+ <c>LibVsn</c> are empty strings.
+ </p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>ERRORS</title>
+ <p>The possible error reasons and the corresponding diagnostic strings
+ returned by <c>format_error/1</c> are either the same as those defined
+ in the <c>inet(3)</c> reference manual, or as follows:
+ </p>
+ <taglist>
+ <tag><c>closed</c></tag>
+ <item>
+ <p>Connection closed for the operation in question.
+ </p>
+ </item>
+ <tag><c>ebadsocket</c></tag>
+ <item>
+ <p>Connection not found (internal error).
+ </p>
+ </item>
+ <tag><c>ebadstate</c></tag>
+ <item>
+ <p>Connection not in connect state (internal error).
+ </p>
+ </item>
+ <tag><c>ebrokertype</c></tag>
+ <item>
+ <p>Wrong broker type (internal error).
+ </p>
+ </item>
+ <tag><c>ecacertfile</c></tag>
+ <item>
+ <p>Own CA certificate file is invalid.
+ </p>
+ </item>
+ <tag><c>ecertfile</c></tag>
+ <item>
+ <p>Own certificate file is invalid.
+ </p>
+ </item>
+ <tag><c>echaintoolong</c></tag>
+ <item>
+ <p>The chain of certificates provided by peer is too long.
+ </p>
+ </item>
+ <tag><c>ecipher</c></tag>
+ <item>
+ <p>Own list of specified ciphers is invalid.
+ </p>
+ </item>
+ <tag><c>ekeyfile</c></tag>
+ <item>
+ <p>Own private key file is invalid.
+ </p>
+ </item>
+ <tag><c>ekeymismatch</c></tag>
+ <item>
+ <p>Own private key does not match own certificate.
+ </p>
+ </item>
+ <tag><c>enoissuercert</c></tag>
+ <item>
+ <p>Cannot find certificate of issuer of certificate provided
+ by peer.
+ </p>
+ </item>
+ <tag><c>enoservercert</c></tag>
+ <item>
+ <p>Attempt to do accept without having set own certificate.
+ </p>
+ </item>
+ <tag><c>enotlistener</c></tag>
+ <item>
+ <p>Attempt to accept on a non-listening socket.
+ </p>
+ </item>
+ <tag><c>enoproxysocket</c></tag>
+ <item>
+ <p>No proxy socket found (internal error).
+ </p>
+ </item>
+ <tag><c>enooptions</c></tag>
+ <item>
+ <p>The list of options is empty.
+ </p>
+ </item>
+ <tag><c>enotstarted</c></tag>
+ <item>
+ <p>The SSL application has not been started.
+ </p>
+ </item>
+ <tag><c>eoptions</c></tag>
+ <item>
+ <p>Invalid list of options.
+ </p>
+ </item>
+ <tag><c>epeercert</c></tag>
+ <item>
+ <p>Certificate provided by peer is in error.
+ </p>
+ </item>
+ <tag><c>epeercertexpired</c></tag>
+ <item>
+ <p>Certificate provided by peer has expired.
+ </p>
+ </item>
+ <tag><c>epeercertinvalid</c></tag>
+ <item>
+ <p>Certificate provided by peer is invalid.
+ </p>
+ </item>
+ <tag><c>eselfsignedcert</c></tag>
+ <item>
+ <p>Certificate provided by peer is self signed.
+ </p>
+ </item>
+ <tag><c>esslaccept</c></tag>
+ <item>
+ <p>Server SSL handshake procedure between client and server failed.
+ </p>
+ </item>
+ <tag><c>esslconnect</c></tag>
+ <item>
+ <p>Client SSL handshake procedure between client and server failed.
+ </p>
+ </item>
+ <tag><c>esslerrssl</c></tag>
+ <item>
+ <p>SSL protocol failure. Typically because of a fatal alert
+ from peer.
+ </p>
+ </item>
+ <tag><c>ewantconnect</c></tag>
+ <item>
+ <p>Protocol wants to connect, which is not supported in
+ this version of the SSL application.
+ </p>
+ </item>
+ <tag><c>ex509lookup</c></tag>
+ <item>
+ <p>Protocol wants X.509 lookup, which is not supported in
+ this version of the SSL application.
+ </p>
+ </item>
+ <tag><c>{badcall, Call}</c></tag>
+ <item>
+ <p>Call not recognized for current mode (active or passive) and
+ state of socket.
+ </p>
+ </item>
+ <tag><c>{badcast, Cast}</c></tag>
+ <item>
+ <p>Call not recognized for current mode (active or passive) and
+ state of socket.
+ </p>
+ </item>
+ <tag><c>{badinfo, Info}</c></tag>
+ <item>
+ <p>Call not recognized for current mode (active or passive) and
+ state of socket.
+ </p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p>gen_tcp(3), inet(3) public_key(3) </p>
+ </section>
+
+</erlref>
+
+
diff --git a/lib/ssl/doc/src/refman.xml b/lib/ssl/doc/src/refman.xml
index 3ad5a01b46..68f84660f3 100644
--- a/lib/ssl/doc/src/refman.xml
+++ b/lib/ssl/doc/src/refman.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>SSL Reference Manual</title>
@@ -45,7 +45,8 @@
</description>
<xi:include href="ssl_app.xml"/>
<xi:include href="ssl.xml"/>
- <xi:include href="new_ssl.xml"/>
+ <xi:include href="old_ssl.xml"/>
+ <xi:include href="ssl_session_cache_api.xml"/>
</application>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 217eb791d0..cd5c9281cd 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE erlref SYSTEM "erlref.dtd">
<erlref>
<header>
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,355 +13,520 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
- </legalnotice>
+ </legalnotice>
<title>ssl</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <responsible>Peter H&ouml;gfeldt</responsible>
- <docno></docno>
- <approved>Peter H&ouml;gfeldt</approved>
- <checked></checked>
- <date>2003-03-25</date>
- <rev>D</rev>
- <file>ssl.sgml</file>
+ <file>ssl.xml</file>
</header>
<module>ssl</module>
<modulesummary>Interface Functions for Secure Socket Layer</modulesummary>
<description>
- <p>This module contains interface functions to the Secure Socket Layer.</p>
+ <p>This module contains interface functions to the Secure Socket
+ Layer.
+ </p>
</description>
+
+ <section>
+ <title>SSL</title>
+ <list type="bulleted">
+ <item>ssl requires the crypto an public_key applications.</item>
+ <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
+ <item>For security reasons sslv2 is not supported.</item>
+ <item>Ephemeral Diffie-Hellman cipher suites are supported
+ but not Diffie Hellman Certificates cipher suites.</item>
+ <item>Export cipher suites are not supported as the
+ U.S. lifted its export restrictions in early 2000.</item>
+ <item>CRL and policy certificate
+ extensions are not supported yet. </item>
+ </list>
+
+ </section>
+
<section>
- <title>General</title>
+ <title>COMMON DATA TYPES</title>
+ <p>The following data types are used in the functions below:
+ </p>
+
+ <p><c>boolean() = true | false</c></p>
+
+ <p><c>property() = atom()</c></p>
+
+ <p><c>option() = socketoption() | ssloption() | transportoption()</c></p>
+
+ <p><c>socketoption() = [{property(), term()}] - defaults to
+ [{mode,list},{packet, 0},{header, 0},{active, true}].
+ </c></p>
+
+ <p>For valid options
+ see <seealso marker="kernel:inet">inet(3) </seealso> and
+ <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>.
+ </p>
+
+ <p> <c>ssloption() = {verify, verify_type()} |
+ {verify_fun, {fun(), term()}} |
+ {fail_if_no_peer_cert, boolean()}
+ {depth, integer()} |
+ {cert, der_encoded()}| {certfile, path()} |
+ {key, der_encoded()} | {keyfile, path()} | {password, string()} |
+ {cacerts, [der_encoded()]} | {cacertfile, path()} |
+ |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} |
+ {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
+ </c></p>
- <p>There is a new implementation of ssl available in
- this module but until it is 100 % complete, so that it can replace
- the old implementation in all aspects it will be
- described here <seealso marker="new_ssl"> new ssl API </seealso></p>
+ <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag}
+ - defaults to {gen_tcp, tcp, tcp_closed}. Ssl may be
+ run over any reliable transport protocol that has
+ an equivalent API to gen_tcp's.</c></p>
+
+ <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallbackModule =
+ atom()</c>
+ </p> <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataTag =
+ atom() - tag used in socket data message.</c></p>
+ <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClosedTag = atom() - tag used in
+ socket close message.</c></p>
+
+ <p><c>verify_type() = verify_none | verify_peer</c></p>
+
+ <p><c>path() = string() - representing a file path.</c></p>
+
+ <p><c>der_encoded() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p>
+
+ <p><c>host() = hostname() | ipaddress()</c></p>
+
+ <p><c>hostname() = string()</c></p>
+
+ <p><c>
+ ip_address() = {N1,N2,N3,N4} % IPv4
+ | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 </c></p>
+
+ <p><c>sslsocket() - opaque to the user. </c></p>
+
+ <p><c>protocol() = sslv3 | tlsv1 </c></p>
+
+ <p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p>
+
+ <p><c>ciphersuite() =
+ {key_exchange(), cipher(), hash()}</c></p>
+
+ <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon
+ </c></p>
+
+ <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc'
+ | aes_128_cbc | aes_256_cbc </c></p>
+
+ <p> <c>hash() = md5 | sha
+ </c></p>
+
+ <p><c>ssl_imp() = new | old - default is new.</c></p>
- <p>The reader is advised to also read the <c>ssl(6)</c> manual page
- describing the SSL application.
- </p>
- <warning>
- <p>It is strongly advised to seed the random generator after
- the ssl application has been started (see <c>seed/1</c>
- below), and before any connections are established. Although
- the port program interfacing to the ssl libraries does a
- "random" seeding of its own in order to make everything work
- properly, that seeding is by no means random for the world
- since it has a constant value which is known to everyone
- reading the source code of the port program.</p>
- </warning>
</section>
<section>
- <title>Common data types</title>
- <p>The following datatypes are used in the functions below:
- </p>
- <list type="bulleted">
- <item>
- <p><c>options() = [option()]</c></p>
- </item>
- <item>
- <p><c>option() = socketoption() | ssloption()</c></p>
- </item>
- <item>
- <p><c>socketoption() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {port, integer()}</c></p>
- </item>
- <item>
- <p><c>ssloption() = {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</c></p>
- </item>
- <item>
- <p><c>packettype()</c> (see inet(3))</p>
- </item>
- <item>
- <p><c>activetype()</c> (see inet(3))</p>
- </item>
- <item>
- <p><c>reason() = atom() | {atom(), string()}</c></p>
- </item>
- <item>
- <p><c>bytes() = [byte()]</c></p>
- </item>
- <item>
- <p><c>string() = [byte()]</c></p>
- </item>
- <item>
- <p><c>byte() = 0 | 1 | 2 | ... | 255</c></p>
- </item>
- <item>
- <p><c>code() = 0 | 1 | 2</c></p>
- </item>
- <item>
- <p><c>depth() = byte()</c></p>
- </item>
- <item>
- <p><c>address() = hostname() | ipstring() | ipaddress()</c></p>
+ <title>SSL OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title>
+
+ <p>Options described here are options that are have the same
+ meaning in the client and the server.
+ </p>
+
+ <taglist>
+
+ <tag>{cert, der_encoded()}</tag>
+ <item> The DER encoded users certificate. If this option
+ is supplied it will override the certfile option.</item>
+
+ <tag>{certfile, path()}</tag>
+ <item>Path to a file containing the user's certificate.</item>
+
+ <tag>{key, der_encoded()}</tag>
+ <item> The DER encoded users private key. If this option
+ is supplied it will override the keyfile option.</item>
+
+ <tag>{keyfile, path()}</tag>
+ <item>Path to file containing user's
+ private PEM encoded key. As PEM-files may contain several
+ entries this option defaults to the same file as given by
+ certfile option.</item>
+
+ <tag>{password, string()}</tag>
+ <item>String containing the user's password.
+ Only used if the private keyfile is password protected.
</item>
- <item>
- <p><c>ipaddress() = ipstring() | iptuple()</c></p>
+
+ <tag>{cacerts, [der_encoded()]}</tag>
+ <item> The DER encoded trusted certificates. If this option
+ is supplied it will override the cacertfile option.</item>
+
+ <tag>{cacertfile, path()}</tag>
+ <item>Path to file containing PEM encoded
+ CA certificates (trusted certificates used for verifying a peer
+ certificate). May be omitted if you do not want to verify
+ the peer.</item>
+
+ <tag>{ciphers, ciphers()}</tag>
+ <item>The cipher suites that should be supported. The function
+ <c>cipher_suites/0</c> can be used to find all available
+ ciphers. Additionally some anonymous cipher suites ({dh_anon,
+ rc4_128, md5}, {dh_anon, des_cbc, sha}, {dh_anon,
+ '3des_ede_cbc', sha}, {dh_anon, aes_128_cbc, sha}, {dh_anon,
+ aes_256_cbc, sha}) are supported for testing purposes and will
+ only work if explicitly enabled by this option and they are supported/enabled
+ by the peer also.
</item>
- <item>
- <p><c>hostname() = string()</c></p>
+
+ <tag>{ssl_imp, ssl_imp()}</tag>
+ <item>Specify which ssl implementation you want to use. Defaults to
+ new.
</item>
- <item>
- <p><c>ipstring() = string()</c></p>
+
+ <tag>{secure_renegotiate, boolean()}</tag>
+ <item>Specifies if to reject renegotiation attempt that does
+ not live up to RFC 5746. By default secure_renegotiate is
+ set to false i.e. secure renegotiation will be used if possible
+ but it will fallback to unsecure renegotiation if the peer
+ does not support RFC 5746.
</item>
- <item>
- <p><c>iptuple() = {byte(), byte(), byte(), byte()}</c></p>
+
+ <tag>{depth, integer()}</tag>
+ <item>Specifies the maximum
+ verification depth, i.e. how far in a chain of certificates the
+ verification process can proceed before the verification is
+ considered to fail. Peer certificate = 0, CA certificate = 1,
+ higher level CA certificate = 2, etc. The value 2 thus means
+ that a chain can at most contain peer cert, CA cert, next CA
+ cert, and an additional CA cert. The default value is 1.
</item>
+
+ <tag>{verify_fun, {Verifyfun :: fun(), InitialUserState :: term()}}</tag>
<item>
- <p><c>sslsocket()</c></p>
+ <p>The verification fun should be defined as:</p>
+
+ <code>
+fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
+ {extension, #'Extension'{}}, InitialUserState :: term()) ->
+ {valid, UserState :: term()} | {valid_peer, UserState :: term()} |
+ {fail, Reason :: term()} | {unknown, UserState :: term()}.
+ </code>
+
+ <p>The verify fun will be called during the X509-path
+ validation when an error or an extension unknown to the ssl
+ application is encountered. Additionally it will be called
+ when a certificate is considered valid by the path validation
+ to allow access to each certificate in the path to the user
+ application. Note that the it will differentiate between the
+ peer certificate and CA certificates by using valid_peer or
+ valid as the second argument to the verify fun. See <seealso
+ marker="public_key:cert_records">the public_key User's
+ Guide</seealso> for definition of #'OTPCertificate'{} and
+ #'Extension'{}.</p>
+
+ <p>If the verify callback fun returns {fail, Reason}, the
+ verification process is immediately stopped and an alert is
+ sent to the peer and the TLS/SSL handshake is terminated. If
+ the verify callback fun returns {valid, UserState}, the
+ verification process is continued. If the verify callback fun
+ always returns {valid, UserState}, the TLS/SSL handshake will
+ not be terminated with respect to verification failures and
+ the connection will be established. If called with an
+ extension unknown to the user application the return value
+ {unknown, UserState} should be used.</p>
+
+ <p>The default verify_fun option in verify_peer mode:</p>
+
+ <code>
+{fun(_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []}
+ </code>
+
+ <p>The default verify_fun option in verify_none mode:</p>
+
+ <code>
+{fun(_,{bad_cert, _}, UserState) ->
+ {valid, UserState};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []}
+ </code>
+
+<p>Possible path validation errors: </p>
+
+<p> {bad_cert, cert_expired}, {bad_cert, invalid_issuer}, {bad_cert, invalid_signature}, {bad_cert, unknown_ca}, {bad_cert, name_not_permitted}, {bad_cert, missing_basic_constraint}, {bad_cert, invalid_key_usage}</p>
</item>
- <item>
- <p><c>protocol() = sslv2 | sslv3 | tlsv1</c></p>
+
+ <tag>{hibernate_after, integer()|undefined}</tag>
+ <item>When an integer-value is specified, the <code>ssl_connection</code>
+ will go into hibernation after the specified number of milliseconds
+ of inactivity, thus reducing its memory footprint. When
+ <code>undefined</code> is specified (this is the default), the process
+ will never go into hibernation.
</item>
- <item>
- <p><c></c></p>
+ </taglist>
+
+ </section>
+
+ <section>
+ <title>SSL OPTION DESCRIPTIONS - CLIENT SIDE</title>
+
+ <p>Options described here are client specific or has a slightly different
+ meaning in the client than in the server.</p>
+
+ <taglist>
+ <tag>{verify, verify_type()}</tag>
+ <item> In verify_none mode the default behavior will be to
+ allow all x509-path validation errors. See also the verify_fun
+ option.
</item>
- </list>
- <p>The socket option <c>{backlog, integer()}</c> is for
- <c>listen/2</c> only, and the option <c>{port, integer()}</c>
- is for <c>connect/3/4</c> only.
- </p>
- <p>The following socket options are set by default: <c>{mode, list}</c>, <c>{packet, 0}</c>, <c>{header, 0}</c>, <c>{nodelay, false}</c>, <c>{active, true}</c>, <c>{backlog, 5}</c>,
- <c>{ip, {0,0,0,0}}</c>, and <c>{port, 0}</c>.
- </p>
- <p>Note that the options <c>{mode, binary}</c> and <c>binary</c>
- are equivalent. Similarly <c>{mode, list}</c> and the absence of
- option <c>binary</c> are equivalent.
- </p>
- <p>The ssl options are for setting specific SSL parameters as follows:
- </p>
- <list type="bulleted">
- <item>
- <p><c>{verify, code()}</c> Specifies type of verification:
- 0 = do not verify peer; 1 = verify peer, 2 = verify peer,
- fail if no peer certificate. The default value is 0.
- </p>
+ <tag>{reuse_sessions, boolean()}</tag>
+ <item>Specifies if client should try to reuse sessions
+ when possible.
</item>
- <item>
- <p><c>{depth, depth()}</c> Specifies the maximum
- verification depth, i.e. how far in a chain of certificates
- the verification process can proceed before the verification
- is considered to fail.
- </p>
- <p>Peer certificate = 0, CA certificate = 1, higher level CA
- certificate = 2, etc. The value 2 thus means that a chain
- can at most contain peer cert, CA cert, next CA cert, and an
- additional CA cert.
- </p>
- <p>The default value is 1.
- </p>
+
+ </taglist>
+ </section>
+
+ <section>
+ <title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title>
+
+ <p>Options described here are server specific or has a slightly different
+ meaning in the server than in the client.</p>
+
+ <taglist>
+
+ <tag>{dh, der_encoded()}</tag>
+ <item>The DER encoded Diffie Hellman parameters. If this option
+ is supplied it will override the dhfile option.
</item>
- <item>
- <p><c>{certfile, path()}</c> Path to a file containing the
- user's certificate.
- chain of PEM encoded certificates.</p>
+
+ <tag>{dhfile, path()}</tag>
+ <item>Path to file containing PEM encoded Diffie Hellman parameters,
+ for the server to use if a cipher suite using Diffie Hellman key exchange
+ is negotiated. If not specified default parameters will be used.
</item>
- <item>
- <p><c>{keyfile, path()}</c> Path to file containing user's
- private PEM encoded key.</p>
+
+ <tag>{verify, verify_type()}</tag>
+ <item>Servers only do the x509-path validation in verify_peer
+ mode, as it then will send a certificate request to the client
+ (this message is not sent if the verify option is verify_none)
+ and you may then also want to specify the option
+ fail_if_no_peer_cert.
</item>
- <item>
- <p><c>{password, string()}</c> String containing the user's
- password. Only used if the private keyfile is password protected.</p>
+
+ <tag>{fail_if_no_peer_cert, boolean()}</tag>
+ <item>Used together with {verify, verify_peer} by a ssl server.
+ If set to true, the server will fail if the client does not have
+ a certificate to send, i.e. sends a empty certificate, if set to
+ false it will only fail if the client sends a invalid
+ certificate (an empty certificate is considered valid).
</item>
- <item>
- <p><c>{cacertfile, path()}</c> Path to file containing PEM encoded
- CA certificates (trusted certificates used for verifying a peer
- certificate).</p>
+
+ <tag>{reuse_sessions, boolean()}</tag>
+ <item>Specifies if the server should agree to reuse sessions
+ when the clients request to do so. See also the reuse_session
+ option.
</item>
- <item>
- <p><c>{ciphers, string()}</c> String of ciphers as a colon
- separated list of ciphers. The function <c>ciphers/0</c> can
- be used to find all available ciphers.</p>
+
+ <tag>{reuse_session, fun(SuggestedSessionId,
+ PeerCert, Compression, CipherSuite) -> boolean()}</tag>
+ <item>Enables the ssl server to have a local policy
+ for deciding if a session should be reused or not,
+ only meaning full if <c>reuse_sessions</c> is set to true.
+ SuggestedSessionId is a binary(), PeerCert is a DER encoded
+ certificate, Compression is an enumeration integer
+ and CipherSuite of type ciphersuite().
</item>
- </list>
- <p>The type <c>sslsocket()</c> is opaque to the user.
- </p>
- <p>The owner of a socket is the one that created it by a call to
- <c>transport_accept/[1,2]</c>, <c>connect/[3,4]</c>,
- or <c>listen/2</c>.
- </p>
- <p>When a socket is in active mode (the default), data from the
+
+ </taglist>
+ </section>
+
+ <section>
+ <title>General</title>
+
+ <p>When a ssl socket is in active mode (the default), data from the
socket is delivered to the owner of the socket in the form of
messages:
- </p>
+ </p>
<list type="bulleted">
- <item>
- <p><c>{ssl, Socket, Data}</c></p>
+ <item>{ssl, Socket, Data}
</item>
- <item>
- <p><c>{ssl_closed, Socket}</c></p>
+ <item>{ssl_closed, Socket}
</item>
<item>
- <p><c>{ssl_error, Socket, Reason}</c></p>
+ {ssl_error, Socket, Reason}
</item>
</list>
+
<p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The
default value for a <c>Timeout</c> argument is <c>infinity</c>.
- </p>
- <p>Functions listed below may return the value <c>{error, closed}</c>, which only indicates that the SSL socket is
- considered closed for the operation in question. It is for
- instance possible to have <c>{error, closed}</c> returned from
- an call to <c>send/2</c>, and a subsequent call to <c>recv/3</c>
- returning <c>{ok, Data}</c>.
- </p>
- <p>Hence a return value of <c>{error, closed}</c> must not be
- interpreted as if the socket was completely closed. On the
- contrary, in order to free all resources occupied by an SSL
- socket, <c>close/1</c> must be called, or else the process owning
- the socket has to terminate.
- </p>
- <p>For each SSL socket there is an Erlang process representing the
- socket. When a socket is opened, that process links to the
- calling client process. Implementations that want to detect
- abnormal exits from the socket process by receiving <c>{'EXIT', Pid, Reason}</c> messages, should use the function <c>pid/1</c>
- to retrieve the process identifier from the socket, in order to
- be able to match exit messages properly.</p>
+ </p>
</section>
+
<funcs>
<func>
- <name>ciphers() -> {ok, string()} | {error, enotstarted}</name>
- <fsummary>Get supported ciphers.</fsummary>
- <desc>
- <p>Returns a string consisting of colon separated cipher
- designations that are supported by the current SSL library
- implementation.
- </p>
- <p>The SSL application has to be started to return the string
- of ciphers.</p>
- </desc>
+ <name>cipher_suites() -></name>
+ <name>cipher_suites(Type) -> ciphers()</name>
+ <fsummary> Returns a list of supported cipher suites</fsummary>
+ <type>
+ <v>Type = erlang | openssl</v>
+
+ </type>
+ <desc><p>Returns a list of supported cipher suites.
+ cipher_suites() is equivalent to cipher_suites(erlang).
+ Type openssl is provided for backwards compatibility with
+ old ssl that used openssl.
+ </p>
+ </desc>
</func>
+
<func>
- <name>close(Socket) -> ok | {error, Reason}</name>
- <fsummary>Close a socket returned by <c>transport_accept/[1,2]</c>, <c>connect/3/4</c>, or <c>listen/2</c>.</fsummary>
+ <name>connect(Socket, SslOptions) -> </name>
+ <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket}
+ | {error, Reason}</name>
+ <fsummary> Upgrades a gen_tcp, or
+ equivalent, connected socket to a ssl socket. </fsummary>
<type>
- <v>Socket = sslsocket()</v>
+ <v>Socket = socket()</v>
+ <v>SslOptions = [ssloption()]</v>
+ <v>Timeout = integer() | infinity</v>
+ <v>SslSocket = sslsocket()</v>
+ <v>Reason = term()</v>
</type>
- <desc>
- <p>Closes a socket returned by <c>transport_accept/[1,2]</c>,
- <c>connect/[3,4]</c>, or <c>listen/2</c></p>
- </desc>
+ <desc> <p>Upgrades a gen_tcp, or equivalent,
+ connected socket to a ssl socket i.e. performs the
+ client-side ssl handshake.</p>
+ </desc>
</func>
+
<func>
- <name>connect(Address, Port, Options) -> {ok, Socket} | {error, Reason}</name>
- <name>connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason}</name>
- <fsummary>Connect to <c>Port</c>at <c>Address</c>.</fsummary>
+ <name>connect(Host, Port, Options) -></name>
+ <name>connect(Host, Port, Options, Timeout) ->
+ {ok, SslSocket} | {error, Reason}</name>
+ <fsummary>Opens an ssl connection to Host, Port. </fsummary>
<type>
- <v>Address = address()</v>
- <v>Port = integer()</v>
- <v>Options = [connect_option()]</v>
- <v>connect_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {nodelay, boolean()} | {active, activetype()} | {ip, ipaddress()} | {port, integer()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v>
- <v>Timeout = integer()</v>
- <v>Socket = sslsocket()</v>
+ <v>Host = host()</v>
+ <v>Port = integer()</v>
+ <v>Options = [option()]</v>
+ <v>Timeout = integer() | infinity</v>
+ <v>SslSocket = sslsocket()</v>
+ <v>Reason = term()</v>
</type>
- <desc>
- <p>Connects to <c>Port</c> at <c>Address</c>. If the optional
- <c>Timeout</c> argument is specified, and a connection could not
- be established within the given time, <c>{error, timeout}</c> is
- returned. The default value for <c>Timeout</c> is <c>infinity</c>.
- </p>
- <p>The <c>ip</c> and <c>port</c> options are for binding to a
- particular <em>local</em> address and port, respectively.</p>
- </desc>
+ <desc> <p>Opens an ssl connection to Host, Port.</p> </desc>
</func>
+
<func>
- <name>connection_info(Socket) -> {ok, {Protocol, Cipher}} | {error, Reason}</name>
- <fsummary>Get current protocol version and cipher.</fsummary>
+ <name>close(SslSocket) -> ok | {error, Reason}</name>
+ <fsummary>Close a ssl connection</fsummary>
<type>
- <v>Socket = sslsocket()</v>
- <v>Protocol = protocol()</v>
- <v>Cipher = string()</v>
+ <v>SslSocket = sslsocket()</v>
+ <v>Reason = term()</v>
</type>
- <desc>
- <p>Gets the chosen protocol version and cipher for an established
- connection (accepted och connected). </p>
+ <desc><p>Close a ssl connection.</p>
</desc>
</func>
+
+ <func>
+ <name>controlling_process(SslSocket, NewOwner) ->
+ ok | {error, Reason}</name>
+
+ <fsummary>Assigns a new controlling process to the
+ ssl-socket.</fsummary>
+
+ <type>
+ <v>SslSocket = sslsocket()</v>
+ <v>NewOwner = pid()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc><p>Assigns a new controlling process to the ssl-socket. A
+ controlling process is the owner of a ssl-socket, and receives
+ all messages from the socket.</p>
+ </desc>
+ </func>
+
<func>
- <name>controlling_process(Socket, NewOwner) -> ok | {error, Reason}</name>
- <fsummary>Assign a new controlling process to the socket.</fsummary>
+ <name>connection_info(SslSocket) ->
+ {ok, {ProtocolVersion, CipherSuite}} | {error, Reason} </name>
+ <fsummary>Returns the negotiated protocol version and cipher suite.
+ </fsummary>
<type>
- <v>Socket = sslsocket()</v>
- <v>NewOwner = pid()</v>
+ <v>CipherSuite = ciphersuite()</v>
+ <v>ProtocolVersion = protocol()</v>
</type>
- <desc>
- <p>Assigns a new controlling process to <c>Socket</c>. A controlling
- process is the owner of a socket, and receives all messages from
- the socket.</p>
+ <desc><p>Returns the negotiated protocol version and cipher suite.</p>
</desc>
</func>
- <func>
- <name>format_error(ErrorCode) -> string()</name>
+
+ <func>
+ <name>format_error(Reason) -> string()</name>
<fsummary>Return an error string.</fsummary>
<type>
- <v>ErrorCode = term()</v>
+ <v>Reason = term()</v>
</type>
<desc>
- <p>Returns a diagnostic string describing an error.</p>
+ <p>Presents the error returned by an ssl function as a printable string.</p>
</desc>
</func>
+
<func>
- <name>getopts(Socket, OptionsTags) -> {ok, Options} | {error, Reason}</name>
- <fsummary>Get options set for socket</fsummary>
+ <name>getopts(Socket) -> </name>
+ <name>getopts(Socket, OptionNames) ->
+ {ok, [socketoption()]} | {error, Reason}</name>
+ <fsummary>Get the value of the specified options.</fsummary>
<type>
- <v>Socket = sslsocket()</v>
- <v>OptionTags = [optiontag()]()</v>
+ <v>Socket = sslsocket()</v>
+ <v>OptionNames = [property()]</v>
</type>
<desc>
- <p>Returns the options the tags of which are <c>OptionTags</c> for
- for the socket <c>Socket</c>. </p>
+ <p>Get the value of the specified socket options, if no
+ options are specified all options are returned.
+ </p>
</desc>
</func>
+
<func>
- <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name>
- <fsummary>Set up a socket to listen on a port on the local host.</fsummary>
+ <name>listen(Port, Options) ->
+ {ok, ListenSocket} | {error, Reason}</name>
+ <fsummary>Creates a ssl listen socket.</fsummary>
<type>
- <v>Port = integer()</v>
- <v>Options = [listen_option()]</v>
- <v>listen_option() = {mode, list} | {mode, binary} | binary | {packet, packettype()} | {header, integer()} | {active, activetype()} | {backlog, integer()} | {ip, ipaddress()} | {verify, code()} | {depth, depth()} | {certfile, path()} | {keyfile, path()} | {password, string()} | {cacertfile, path()} | {ciphers, string()}</v>
- <v>ListenSocket = sslsocket()</v>
+ <v>Port = integer()</v>
+ <v>Options = options()</v>
+ <v>ListenSocket = sslsocket()</v>
</type>
<desc>
- <p>Sets up a socket to listen on port <c>Port</c> at the local host.
- If <c>Port</c> is zero, <c>listen/2</c> picks an available port
- number (use <c>port/1</c> to retrieve it).
- </p>
- <p>The listen queue size defaults to 5. If a different value is
- wanted, the option <c>{backlog, Size}</c> should be added to the
- list of options.
- </p>
- <p>An empty <c>Options</c> list is considered an error, and
- <c>{error, enooptions}</c> is returned.
- </p>
- <p>The returned <c>ListenSocket</c> can only be used in calls to
- <c>transport_accept/[1,2]</c>.</p>
+ <p>Creates a ssl listen socket.</p>
</desc>
</func>
+
<func>
- <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
+ <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
<fsummary>Return the peer certificate.</fsummary>
- <type>
+ <type>
<v>Socket = sslsocket()</v>
- <v>Cert = binary()()</v>
- <v>Subject = term()()</v>
+ <v>Cert = binary()</v>
</type>
<desc>
- <p>Returns the DER encoded peer certificate, the certificate can be decoded with
- <c>public_key:pkix_decode_cert/2</c>.
- </p>
+ <p>The peer certificate is returned as a DER encoded binary.
+ The certificate can be decoded with <c>public_key:pkix_decode_cert/2</c>.
+ </p>
</desc>
</func>
<func>
- <name>peername(Socket) -> {ok, {Address, Port}} | {error, Reason}</name>
+ <name>peername(Socket) -> {ok, {Address, Port}} |
+ {error, Reason}</name>
<fsummary>Return peer address and port.</fsummary>
<type>
<v>Socket = sslsocket()</v>
@@ -372,67 +537,47 @@
<p>Returns the address and port number of the peer.</p>
</desc>
</func>
+
<func>
- <name>pid(Socket) -> pid()</name>
- <fsummary>Return the pid of the socket process.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- </type>
- <desc>
- <p>Returns the pid of the socket process. The returned pid should
- only be used for receiving exit messages.</p>
- </desc>
- </func>
- <func>
- <name>recv(Socket, Length) -> {ok, Data} | {error, Reason}</name>
- <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error, Reason}</name>
- <fsummary>Receive data on socket.</fsummary>
+ <name>recv(Socket, Length) -> </name>
+ <name>recv(Socket, Length, Timeout) -> {ok, Data} | {error,
+ Reason}</name>
+ <fsummary>Receive data on a socket.</fsummary>
<type>
<v>Socket = sslsocket()</v>
- <v>Length = integer() >= 0</v>
+ <v>Length = integer()</v>
<v>Timeout = integer()</v>
- <v>Data = bytes() | binary()</v>
+ <v>Data = [char()] | binary()</v>
</type>
<desc>
- <p>Receives data on socket <c>Socket</c> when the socket is in
- passive mode, i.e. when the option <c>{active, false}</c>
- has been specified.
- </p>
- <p>A notable return value is <c>{error, closed}</c> which
- indicates that the socket is closed.
- </p>
- <p>A positive value of the <c>Length</c> argument is only
- valid when the socket is in raw mode (option <c>{packet, 0}</c> is set, and the option <c>binary</c> is <em>not</em>
- set); otherwise it should be set to 0, whence all available
- bytes are returned.
- </p>
- <p>If the optional <c>Timeout</c> parameter is specified, and
- no data was available within the given time, <c>{error, timeout}</c> is returned. The default value for
- <c>Timeout</c> is <c>infinity</c>.</p>
+ <p>This function receives a packet from a socket in passive
+ mode. A closed socket is indicated by a return value
+ <c>{error, closed}</c>.</p>
+ <p>The <c>Length</c> argument is only meaningful when
+ the socket is in <c>raw</c> mode and denotes the number of
+ bytes to read. If <c>Length</c> = 0, all available bytes are
+ returned. If <c>Length</c> &gt; 0, exactly <c>Length</c>
+ bytes are returned, or an error; possibly discarding less
+ than <c>Length</c> bytes of data when the socket gets closed
+ from the other side.</p>
+ <p>The optional <c>Timeout</c> parameter specifies a timeout in
+ milliseconds. The default value is <c>infinity</c>.</p>
</desc>
</func>
+
<func>
- <name>seed(Data) -> ok | {error, Reason}</name>
- <fsummary>Seed the ssl random generator.</fsummary>
+ <name>renegotiate(Socket) -> ok | {error, Reason}</name>
+ <fsummary> Initiates a new handshake.</fsummary>
<type>
- <v>Data = iolist() | binary()</v>
+ <v>Socket = sslsocket()</v>
</type>
- <desc>
- <p>Seeds the ssl random generator.
- </p>
- <p>It is strongly advised to seed the random generator after
- the ssl application has been started, and before any
- connections are established. Although the port program
- interfacing to the OpenSSL libraries does a "random" seeding
- of its own in order to make everything work properly, that
- seeding is by no means random for the world since it has a
- constant value which is known to everyone reading the source
- code of the seeding.
- </p>
- <p>A notable return value is <c>{error, edata}}</c> indicating that
- <c>Data</c> was not a binary nor an iolist.</p>
+ <desc><p>Initiates a new handshake. A notable return value is
+ <c>{error, renegotiation_rejected}</c> indicating that the peer
+ refused to go through with the renegotiation but the connection
+ is still active using the previously negotiated session.</p>
</desc>
</func>
+
<func>
<name>send(Socket, Data) -> ok | {error, Reason}</name>
<fsummary>Write data to a socket.</fsummary>
@@ -458,26 +603,65 @@
<c>Socket</c>. </p>
</desc>
</func>
+
<func>
- <name>ssl_accept(Socket) -> ok | {error, Reason}</name>
- <name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Perform server-side SSL handshake and key exchange</fsummary>
+ <name>shutdown(Socket, How) -> ok | {error, Reason}</name>
+ <fsummary>Immediately close a socket</fsummary>
<type>
<v>Socket = sslsocket()</v>
+ <v>How = read | write | read_write</v>
+ <v>Reason = reason()</v>
+ </type>
+ <desc>
+ <p>Immediately close a socket in one or two directions.</p>
+ <p><c>How == write</c> means closing the socket for writing,
+ reading from it is still possible.</p>
+ <p>To be able to handle that the peer has done a shutdown on
+ the write side, the <c>{exit_on_close, false}</c> option
+ is useful.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name>ssl_accept(ListenSocket) -> </name>
+ <name>ssl_accept(ListenSocket, Timeout) -> ok | {error, Reason}</name>
+ <fsummary>Perform server-side SSL handshake</fsummary>
+ <type>
+ <v>ListenSocket = sslsocket()</v>
<v>Timeout = integer()</v>
- <v>Reason = atom()</v>
+ <v>Reason = term()</v>
</type>
<desc>
<p>The <c>ssl_accept</c> function establish the SSL connection
on the server side. It should be called directly after
<c>transport_accept</c>, in the spawned server-loop.</p>
- <p>Note that the ssl connection is not complete until <c>ssl_accept</c>
- has returned <c>true</c>, and if an error is returned, the socket
- is unavailable and for instance <c>close/1</c> will crash.</p>
</desc>
</func>
+
+ <func>
+ <name>ssl_accept(ListenSocket, SslOptions) -> </name>
+ <name>ssl_accept(ListenSocket, SslOptions, Timeout) -> {ok, Socket} | {error, Reason}</name>
+ <fsummary>Perform server-side SSL handshake</fsummary>
+ <type>
+ <v>ListenSocket = socket()</v>
+ <v>SslOptions = ssloptions()</v>
+ <v>Timeout = integer()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p> Upgrades a gen_tcp, or
+ equivalent, socket to a ssl socket i.e. performs the
+ ssl server-side handshake.</p>
+ <p><warning>Note that the listen socket should be in {active, false} mode
+ before telling the client that the server is ready to upgrade
+ and calling this function, otherwise the upgrade may
+ or may not succeed depending on timing.</warning></p>
+ </desc>
+ </func>
+
<func>
- <name>sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}</name>
+ <name>sockname(Socket) -> {ok, {Address, Port}} |
+ {error, Reason}</name>
<fsummary>Return the local address and port.</fsummary>
<type>
<v>Socket = sslsocket()</v>
@@ -489,217 +673,84 @@
<c>Socket</c>.</p>
</desc>
</func>
+
+ <func>
+ <name>start() -> </name>
+ <name>start(Type) -> ok | {error, Reason}</name>
+ <fsummary>Starts the Ssl application. </fsummary>
+ <type>
+ <v>Type = permanent | transient | temporary</v>
+ </type>
+ <desc>
+ <p>Starts the Ssl application. Default type
+ is temporary.
+ <seealso marker="kernel:application">application(3)</seealso></p>
+ </desc>
+ </func>
+ <func>
+ <name>stop() -> ok </name>
+ <fsummary>Stops the Ssl application.</fsummary>
+ <desc>
+ <p>Stops the Ssl application.
+ <seealso marker="kernel:application">application(3)</seealso></p>
+ </desc>
+ </func>
+
<func>
- <name>transport_accept(Socket) -> {ok, NewSocket} | {error, Reason}</name>
- <name>transport_accept(Socket, Timeout) -> {ok, NewSocket} | {error, Reason}</name>
- <fsummary>Accept an incoming connection and prepare for <c>ssl_accept</c></fsummary>
+ <name>transport_accept(Socket) -></name>
+ <name>transport_accept(Socket, Timeout) ->
+ {ok, NewSocket} | {error, Reason}</name>
+ <fsummary>Accept an incoming connection and
+ prepare for <c>ssl_accept</c></fsummary>
<type>
<v>Socket = NewSocket = sslsocket()</v>
<v>Timeout = integer()</v>
- <v>Reason = atom()</v>
+ <v>Reason = reason()</v>
</type>
<desc>
<p>Accepts an incoming connection request on a listen socket.
- <c>ListenSocket</c> must be a socket returned from <c>listen/2</c>.
- The socket returned should be passed to <c>ssl_accept</c> to
- complete ssl handshaking and establishing the connection.</p>
+ <c>ListenSocket</c> must be a socket returned from
+ <c>listen/2</c>. The socket returned should be passed to
+ <c>ssl_accept</c> to complete ssl handshaking and
+ establishing the connection.</p>
<warning>
<p>The socket returned can only be used with <c>ssl_accept</c>,
no traffic can be sent or received before that call.</p>
</warning>
- <p>The accepted socket inherits the options set for <c>ListenSocket</c>
- in <c>listen/2</c>.</p>
- <p>The default value for <c>Timeout</c> is <c>infinity</c>. If
- <c>Timeout</c> is specified, and no connection is accepted within
- the given time, <c>{error, timeout}</c> is returned.</p>
+ <p>The accepted socket inherits the options set for
+ <c>ListenSocket</c> in <c>listen/2</c>.</p>
+ <p>The default
+ value for <c>Timeout</c> is <c>infinity</c>. If
+ <c>Timeout</c> is specified, and no connection is accepted
+ within the given time, <c>{error, timeout}</c> is
+ returned.</p>
</desc>
</func>
+
<func>
- <name>version() -> {ok, {SSLVsn, CompVsn, LibVsn}}</name>
- <fsummary>Return the version of SSL.</fsummary>
+ <name>versions() ->
+ [{SslAppVer, SupportedSslVer, AvailableSslVsn}]</name>
+ <fsummary>Returns version information relevant for the
+ ssl application.</fsummary>
<type>
- <v>SSLVsn = CompVsn = LibVsn = string()()</v>
+ <v>SslAppVer = string()</v>
+ <v>SupportedSslVer = [protocol()]</v>
+ <v>AvailableSslVsn = [protocol()]</v>
</type>
<desc>
- <p>Returns the SSL application version (<c>SSLVsn</c>), the library
- version used when compiling the SSL application port program
- (<c>CompVsn</c>), and the actual library version used when
- dynamically linking in runtime (<c>LibVsn</c>).
- </p>
- <p>If the SSL application has not been started, <c>CompVsn</c> and
- <c>LibVsn</c> are empty strings.
- </p>
+ <p>
+ Returns version information relevant for the
+ ssl application.</p>
</desc>
</func>
- </funcs>
-
- <section>
- <title>ERRORS</title>
- <p>The possible error reasons and the corresponding diagnostic strings
- returned by <c>format_error/1</c> are either the same as those defined
- in the <c>inet(3)</c> reference manual, or as follows:
- </p>
- <taglist>
- <tag><c>closed</c></tag>
- <item>
- <p>Connection closed for the operation in question.
- </p>
- </item>
- <tag><c>ebadsocket</c></tag>
- <item>
- <p>Connection not found (internal error).
- </p>
- </item>
- <tag><c>ebadstate</c></tag>
- <item>
- <p>Connection not in connect state (internal error).
- </p>
- </item>
- <tag><c>ebrokertype</c></tag>
- <item>
- <p>Wrong broker type (internal error).
- </p>
- </item>
- <tag><c>ecacertfile</c></tag>
- <item>
- <p>Own CA certificate file is invalid.
- </p>
- </item>
- <tag><c>ecertfile</c></tag>
- <item>
- <p>Own certificate file is invalid.
- </p>
- </item>
- <tag><c>echaintoolong</c></tag>
- <item>
- <p>The chain of certificates provided by peer is too long.
- </p>
- </item>
- <tag><c>ecipher</c></tag>
- <item>
- <p>Own list of specified ciphers is invalid.
- </p>
- </item>
- <tag><c>ekeyfile</c></tag>
- <item>
- <p>Own private key file is invalid.
- </p>
- </item>
- <tag><c>ekeymismatch</c></tag>
- <item>
- <p>Own private key does not match own certificate.
- </p>
- </item>
- <tag><c>enoissuercert</c></tag>
- <item>
- <p>Cannot find certificate of issuer of certificate provided
- by peer.
- </p>
- </item>
- <tag><c>enoservercert</c></tag>
- <item>
- <p>Attempt to do accept without having set own certificate.
- </p>
- </item>
- <tag><c>enotlistener</c></tag>
- <item>
- <p>Attempt to accept on a non-listening socket.
- </p>
- </item>
- <tag><c>enoproxysocket</c></tag>
- <item>
- <p>No proxy socket found (internal error).
- </p>
- </item>
- <tag><c>enooptions</c></tag>
- <item>
- <p>The list of options is empty.
- </p>
- </item>
- <tag><c>enotstarted</c></tag>
- <item>
- <p>The SSL application has not been started.
- </p>
- </item>
- <tag><c>eoptions</c></tag>
- <item>
- <p>Invalid list of options.
- </p>
- </item>
- <tag><c>epeercert</c></tag>
- <item>
- <p>Certificate provided by peer is in error.
- </p>
- </item>
- <tag><c>epeercertexpired</c></tag>
- <item>
- <p>Certificate provided by peer has expired.
- </p>
- </item>
- <tag><c>epeercertinvalid</c></tag>
- <item>
- <p>Certificate provided by peer is invalid.
- </p>
- </item>
- <tag><c>eselfsignedcert</c></tag>
- <item>
- <p>Certificate provided by peer is self signed.
- </p>
- </item>
- <tag><c>esslaccept</c></tag>
- <item>
- <p>Server SSL handshake procedure between client and server failed.
- </p>
- </item>
- <tag><c>esslconnect</c></tag>
- <item>
- <p>Client SSL handshake procedure between client and server failed.
- </p>
- </item>
- <tag><c>esslerrssl</c></tag>
- <item>
- <p>SSL protocol failure. Typically because of a fatal alert
- from peer.
- </p>
- </item>
- <tag><c>ewantconnect</c></tag>
- <item>
- <p>Protocol wants to connect, which is not supported in
- this version of the SSL application.
- </p>
- </item>
- <tag><c>ex509lookup</c></tag>
- <item>
- <p>Protocol wants X.509 lookup, which is not supported in
- this version of the SSL application.
- </p>
- </item>
- <tag><c>{badcall, Call}</c></tag>
- <item>
- <p>Call not recognized for current mode (active or passive) and
- state of socket.
- </p>
- </item>
- <tag><c>{badcast, Cast}</c></tag>
- <item>
- <p>Call not recognized for current mode (active or passive) and
- state of socket.
- </p>
- </item>
- <tag><c>{badinfo, Info}</c></tag>
- <item>
- <p>Call not recognized for current mode (active or passive) and
- state of socket.
- </p>
- </item>
- </taglist>
- </section>
-
+ </funcs>
+
<section>
<title>SEE ALSO</title>
- <p>gen_tcp(3), inet(3) public_key(3) </p>
+ <p><seealso marker="kernel:inet">inet(3) </seealso> and
+ <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>
+ </p>
</section>
-
-</erlref>
+</erlref>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index ae8bd87781..2ba6f48611 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,45 +13,20 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>ssl</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <responsible>Peter H&ouml;gfeldt</responsible>
- <docno></docno>
- <approved>Peter H&ouml;gfeldt</approved>
- <checked>Peter H&ouml;gfeldt</checked>
- <date>2005-03-10</date>
- <rev>E</rev>
<file>ssl_app.sgml</file>
</header>
<app>ssl</app>
- <appsummary>The SSL Application</appsummary>
- <description>
- <p>The Secure Socket Layer (SSL) application provides secure
- socket communication over TCP/IP.
- </p>
- </description>
-
- <section>
- <title>Warning</title>
- <p>In previous versions of Erlang/OTP SSL it was advised, as a
- work-around, to set the operating system environment variable
- <c>SSL_CERT_FILE</c> to point at a file containing CA
- certificates. That variable is no longer needed, and is not
- recognised by Erlang/OTP SSL any more.
- </p>
- <p>However, the OpenSSL package does interpret that environment
- variable. Hence a setting of that variable might have
- unpredictable effects on the Erlang/OTP SSL application. It is
- therefore adviced to not used that environment variable at all.</p>
- </section>
+ <appsummary>The SSL application provides secure communication over
+ sockets.</appsummary>
<section>
<title>Environment</title>
@@ -61,115 +36,43 @@
</p>
<p>Note that the environment parameters can be set on the command line,
for instance,</p>
- <p><c>erl ... -ssl protocol_version '[sslv2,sslv3]' ...</c>.
+ <p><c>erl ... -ssl protocol_version '[sslv3, tlsv1]' ...</c>.
</p>
<taglist>
- <tag><c><![CDATA[ephemeral_rsa = true | false <optional>]]></c></tag>
- <item>
- <p>Enables all SSL servers (those that listen and accept)
- to use ephemeral RSA key generation when a clients connect with
- weak handshake cipher specifications, that need equally weak
- ciphers from the server (i.e. obsolete restrictions on export
- ciphers). Default is <c>false</c>.
- </p>
- </item>
- <tag><c><![CDATA[debug = true | false <optional>]]></c></tag>
- <item>
- <p>Causes debug information to be written to standard
- output. Default is <c>false</c>.
- </p>
- </item>
- <tag><c><![CDATA[debugdir = path() | false <optional>]]></c></tag>
- <item>
- <p>Causes debug information output controlled by <c>debug</c>
- and <c>msgdebug</c> to be printed to a file named
- <c><![CDATA[ssl_esock.<pid>.log]]></c> in the directory specified by
- <c>debugdir</c>, where <c><![CDATA[<pid>]]></c> is the operating system
- specific textual representation of the process identifier
- of the external port program of the SSL application. Default
- is <c>false</c>, i.e. no log file is produced.
- </p>
- </item>
- <tag><c><![CDATA[msgdebug = true | false <optional>]]></c></tag>
+ <tag><c><![CDATA[protocol_version = [sslv3|tlsv1] <optional>]]></c>.</tag>
<item>
- <p>Sets <c>debug = true</c> and causes also the contents
- of low level messages to be printed to standard output.
- Default is <c>false</c>.
- </p>
+ <p>Protocol that will be supported by started clients and
+ servers. If this option is not set it will default to all
+ protocols currently supported by the erlang ssl application.
+ Note that this option may be overridden by the version option
+ to ssl:connect/[2,3] and ssl:listen/2.
+ </p>
</item>
- <tag><c><![CDATA[port_program = string() | false <optional>]]></c></tag>
- <item>
- <p>Name of port program. The default is <c>ssl_esock</c>.
- </p>
- </item>
- <tag><c><![CDATA[protocol_version = [sslv2|sslv3|tlsv1] <optional>]]></c>.</tag>
+
+ <tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag>
<item>
- <p>Name of protocols to use. If this option is not set,
- all protocols are assumed, i.e. the default value is
- <c>[sslv2, sslv3, tlsv1]</c>.
- </p>
+ <p>The lifetime of session data in seconds.
+ </p>
</item>
- <tag><c><![CDATA[proxylsport = integer() | false <optional>]]></c></tag>
+
+ <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag>
<item>
- <p>Define the port number of the listen port of the
- SSL port program. Almost never is this option needed.
+ <p>
+ Name of session cache callback module that implements
+ the ssl_session_cache_api behavior, defaults to
+ ssl_session_cache.erl.
</p>
</item>
- <tag><c><![CDATA[proxylsbacklog = integer() | false <optional>]]></c></tag>
+
+ <tag><c><![CDATA[session_cb_init_args = list() <optional>]]></c></tag>
<item>
- <p>Set the listen queue size of the listen port of the
- SSL port program. The default is 128.
- </p>
+ <p>
+ List of arguments to the init function in session cache
+ callback module, defaults to [].
+ </p>
</item>
- </taglist>
- </section>
- <section>
- <title>OpenSSL libraries</title>
- <p>The current implementation of the Erlang SSL application is
- based on the <em>OpenSSL</em> package version 0.9.7 or higher.
- There are source and binary releases on the web.
- </p>
- <p>Source releases of OpenSSL can be downloaded from the <url href="http://www.openssl.org">OpenSSL</url> project home page,
- or mirror sites listed there.
- </p>
- <p>The same URL also contains links to some compiled binaries and
- libraries of OpenSSL (see the <c>Related/Binaries</c> menu) of
- which the <url href="http://www.shininglightpro.com/search.php?searchname=Win32+OpenSSL">Shining Light Productions Win32 and OpenSSL</url> pages are of
- interest for the Win32 user.
- </p>
- <p>For some Unix flavours there are binary packages available
- on the net.
- </p>
- <p>If you cannot find a suitable binary OpenSSL package, you
- have to fetch an OpenSSL source release and compile it.
- </p>
- <p>You then have to compile and install the libraries
- <c>libcrypto.so</c> and <c>libssl.so</c> (Unix), or the
- libraries <c>libeay32.dll</c> and <c>ssleay32.dll</c> (Win32).
- </p>
- <p>For Unix The <c>ssl_esock</c> port program is delivered linked
- to OpenSSL libraries in <c>/usr/local/lib</c>, but the default
- dynamic linking will also accept libraries in <c>/lib</c> and
- <c>/usr/lib</c>.
- </p>
- <p>If that is not applicable to the particular Unix operating
- system used, the example <c>Makefile</c> in the SSL
- <c>priv/obj</c> directory, should be used as a guide to
- relinking the final version of the port program.
- </p>
- <p>For <c>Win32</c> it is only required that the libraries can be
- found from the <c>PATH</c> environment variable, or that they
- reside in the appropriate <c>SYSTEM32</c> directory; hence no
- particular relinking is need. Hence no example <c>Makefile</c>
- for Win32 is provided.</p>
- </section>
-
- <section>
- <title>Restrictions</title>
- <p>Users must be aware of export restrictions and patent rights
- concerning cryptographic software.
- </p>
+ </taglist>
</section>
<section>
@@ -178,5 +81,3 @@
</section>
</appref>
-
-
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index c743cd67a3..7bcc12eb5f 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -32,7 +32,13 @@
<file>ssl_distribution.xml</file>
</header>
<p>This chapter describes how the Erlang distribution can use
- SSL to get additional verification and security.</p>
+ SSL to get additional verification and security.
+
+ <note><p>Note this
+ documentation is written for the old ssl implementation and
+ will be updated for the new one once this functionality is
+ supported by the new implementation.</p></note>
+ </p>
<section>
<title>Introduction</title>
@@ -49,7 +55,7 @@
all participating Erlang nodes in a distributed system must use
this distribution module.</p>
<p>The security depends on how the connections are set up, one can
- use key files or certificates to just get a crypted
+ use key files or certificates to just get a encrypted
connection. One can also make the SSL package verify the
certificates of other nodes to get additional security.
Cookies are however always used as they can be used to
@@ -173,7 +179,7 @@ Eshell V5.0 (abort with ^G)
<c>certfile</c> can (and usually needs to) be specified as
<c>client_certfile</c> and <c>server_certfile</c>. The
<c>client_certfile</c> is used when the distribution initiates a
- connection to another node and the <c>server_cerfile</c> is used
+ connection to another node and the <c>server_certfile</c> is used
when accepting a connection from a remote node. </p>
<p>The command line argument for specifying the SSL options is named
<c>-ssl_dist_opt</c> and should be followed by an even number of
diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml
index 3dc2332795..6936408881 100644
--- a/lib/ssl/doc/src/ssl_protocol.xml
+++ b/lib/ssl/doc/src/ssl_protocol.xml
@@ -1,10 +1,10 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,337 +13,138 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
- <title>The SSL Protocol</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-04-28</date>
- <rev>PA2</rev>
+ <title>Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL)</title>
<file>ssl_protocol.xml</file>
</header>
- <p>Here we provide a short introduction to the SSL protocol. We only
- consider those part of the protocol that are important from a
- programming point of view.
- </p>
- <p>For a very good general introduction to SSL and TLS see the book
- <cite id="rescorla"></cite>.
- </p>
- <p><em>Outline:</em></p>
- <list type="bulleted">
- <item>Two types of connections - connection: handshake, data transfer, and
- shutdown -
- SSL/TLS protocol - server must have certificate - what the the
- server sends to the client - client may verify the server -
- server may ask client for certificate - what the client sends to
- the server - server may then verify the client - verification -
- certificate chains - root certificates - public keys - key
- agreement - purpose of certificate - references</item>
- </list>
+
+ <p>The erlang ssl application currently supports SSL 3.0 and TLS 1.0
+ RFC 2246, and will in the future also support later versions of TLS.
+ SSL 2.0 is not supported.
+ </p>
- <section>
- <title>SSL Connections</title>
- <p>The SSL protocol is implemented on top of the TCP/IP protocol.
- From an endpoint view it also has the same type of connections
- as that protocol, almost always created by calls to socket
- interface functions <em>listen</em>, <em>accept</em> and
- <em>connect</em>. The endpoints are <em>servers</em> and
- <em>clients</em>.
- </p>
- <p>A <em>server</em><em>listen</em>s for connections on a
- specific address and port. This is done once. The server then
- <em>accept</em>s each connections on that same address and
- port. This is typically done indefinitely many times.
- </p>
- <p>A <em>client</em> connects to a server on a specific address
- and port. For each purpose this is done once.
- </p>
- <p>For a plain TCP/IP connection the establishment of a connection
- (through an accept or a connect) is followed by data transfer between
- the client and server, finally ended by a connection close.
- </p>
- <p>An SSL connection also consists of data transfer and connection
- close, However, the data transfer contains encrypted data, and
- in order to establish the encryption parameters, the data
- transfer is preceded by an SSL <em>handshake</em>. In this
- handshake the server plays a dominant role, and the main
- instrument used in achieving a valid SSL connection is the
- server's <em>certificate</em>. We consider certificates in the
- next section, and the SSL handshake in a subsequent section.</p>
- </section>
+ <p>By default erlang ssl is run over the TCP/IP protocol even
+ though you could plug in an other reliable transport protocol
+ with the same API as gen_tcp.</p>
+
+ <p>If a client and server wants to use an upgrade mechanism, such as
+ defined by RFC2817, to upgrade a regular TCP/IP connection to a ssl
+ connection the erlang ssl API supports this. This can be useful for
+ things such as supporting HTTP and HTTPS on the same port and
+ implementing virtual hosting.
+ </p>
<section>
- <title>Certificates</title>
- <p>A certificate is similar to a driver's license, or a
- passport. The holder of the certificate is called the
- <em>subject</em>. First of all the certificate identifies the
- subject in terms of the name of the subject, its postal address,
- country name, company name (if applicable), etc.
- </p>
- <p>Although a driver's license is always issued by a well-known and
- distinct authority, a certificate may have an <em>issuer</em>
- that is not so well-known. Therefore a certificate also always
- contains information on the issuer of the certificate. That
- information is of the same type as the information on the
- subject. The issuer of a certificate also signs the certificate
- with a <em>digital signature</em> (the signature is an inherent
- part of the certificate), which allow others to verify that the
- issuer really is the issuer of the certificate.
- </p>
- <p>Now that a certificate can be checked by verifying the
- signature of the issuer, the question is how to trust the
- issuer. The answer to this question is to require that there is
- a certificate for the issuer as well. That issuer has in turn an
- issuer, which must also have a certificate, and so on. This
- <em>certificate chain</em> has to have en end, which then must
- be a certificate that is trusted by other means. We shall cover
- this problem of <em>authentication</em> in a subsequent
- section.
- </p>
+ <title>Security overview</title>
+
+ <p>To achieve authentication and privacy the client and server will
+ perform a TLS Handshake procedure before transmitting or receiving
+ any data. During the handshake they agree on a protocol version and
+ cryptographic algorithms, they generate shared secrets using public
+ key cryptographics and optionally authenticate each other with
+ digital certificates.</p>
</section>
-
+
<section>
- <title>Encryption Algorithms</title>
- <p>An encryption algorithm is a mathematical algorithm for
- encryption and decryption of messages (arrays of bytes,
- say). The algorithm as such is always required to be publicly
- known, otherwise its strength cannot be evaluated, and hence it
- cannot be used reliably. The secrecy of an encrypted message is
- not achieved by the secrecy of the algorithm used, but by the
- secrecy of the <em>keys</em> used as input to the encryption and
- decryption algorithms. For an account of cryptography in general
- see <cite id="schneier"></cite>.
- </p>
- <p>There are two classes of encryption algorithms: <em>symmetric key</em> algorithms and <em>public key</em> algorithms. Both
- types of algorithms are used in the SSL protocol.
- </p>
- <p>In the sequel we assume holders of keys keep them secret (except
- public keys) and that they in that sense are trusted. How a
- holder of a secret key is proved to be the one it claims to be
- is a question of <em>authentication</em>, which, in the context
- of the SSL protocol, is described in a section further below.
- </p>
-
- <section>
- <title>Symmetric Key Algorithms</title>
- <p>A <em>symmetric key</em> algorithm has one key only. The key
- is used for both encryption and decryption. Obviously the key
- of a symmetric key algorithm must always be kept secret by the
- users of the key. DES is an example of a symmetric key
- algorithm.
- </p>
- <p>Symmetric key algorithms are fast compared to public key
- algorithms. They are therefore typically used for encrypting
- bulk data.
- </p>
- </section>
-
- <section>
- <title>Public Key Algorithms</title>
- <p>A <em>public key</em> algorithm has two keys. Any of the two
- keys can be used for encryption. A message encrypted with one
- of the keys, can only be decrypted with the other key. One of
- the keys is public (known to the world), while the other key
- is private (i.e. kept secret) by the owner of the two keys.
- </p>
- <p>RSA is an example of a public key algorithm.
- </p>
- <p>Public key algorithms are slow compared to symmetric key
- algorithms, and they are therefore seldom used for bulk data
- encryption. They are therefore only used in cases where the
- fact that one key is public and the other is private, provides
- features that cannot be provided by symmetric algorithms.
- </p>
- </section>
-
- <section>
- <title>Digital Signature Algorithms</title>
- <p>An interesting feature of a public key algorithm is that its
- public and private keys can both be used for encryption.
- Anyone can use the public key to encrypt a message, and send
- that message to the owner of the private key, and be sure of
- that only the holder of the private key can decrypt the
- message.
- </p>
- <p>On the other hand, the owner of the private key can encrypt a
- message with the private key, thus obtaining an encrypted
- message that can decrypted by anyone having the public key.
- </p>
- <p>The last approach can be used as a digital signature
- algorithm. The holder of the private key signs an array of
- bytes by performing a specified well-known <em>message digest algorithm</em> to compute a hash of the array, encrypts the
- hash value with its private key, an then presents the original
- array, the name of the digest algorithm, and the encryption of
- the hash value as a <em>signed array of bytes</em>.
- </p>
- <p>Now anyone having the public key, can decrypt the encrypted
- hash value with that key, compute the hash with the specified
- digest algorithm, and check that the hash values compare equal
- in order to verify that the original array was indeed signed
- by the holder of the private key.
- </p>
- <p>What we have accounted for so far is by no means all that can
- be said about digital signatures (see <cite id="schneier"></cite>for
- further details).
- </p>
- </section>
-
- <section>
- <title>Message Digests Algorithms</title>
- <p>A message digest algorithm is a hash function that accepts
- an array bytes of arbitrary but finite length of input, and
- outputs an array of bytes of fixed length. Such an algorithm
- is also required to be very hard to invert.
- </p>
- <p>MD5 (16 bytes output) and SHA1 (20 bytes output) are examples
- of message digest algorithms.
- </p>
- </section>
+ <title>Data Privacy and Integrity</title>
+
+ <p>A <em>symmetric key</em> algorithm has one key only. The key is
+ used for both encryption and decryption. These algorithms are fast
+ compared to public key algorithms (using two keys, a public and a
+ private one) and are therefore typically used for encrypting bulk
+ data.
+ </p>
+
+ <p>The keys for the symmetric encryption are generated uniquely
+ for each connection and are based on a secret negotiated
+ in the TLS handshake. </p>
+
+ <p>The TLS handshake protocol and data transfer is run on top of
+ the TLS Record Protocol that uses a keyed-hash MAC (Message
+ Authenticity Code), or HMAC, to protect the message's data
+ integrity. From the TLS RFC "A Message Authentication Code is a
+ one-way hash computed from a message and some secret data. It is
+ difficult to forge without knowing the secret data. Its purpose is
+ to detect if the message has been altered."
+ </p>
+
</section>
- <section>
- <title>SSL Handshake</title>
- <p>The main purpose of the handshake performed before an an SSL
- connection is established is to negotiate the encryption
- algorithm and key to be used for the bulk data transfer between
- the client and the server. We are writing <em>the</em> key,
- since the algorithm to choose for bulk encryption one of the
- symmetric algorithms.
- </p>
- <p>There is thus only one key to agree upon, and obviously that
- key has to be kept secret between the client and the server. To
- obtain that the handshake has to be encrypted as well.
- </p>
- <p>The SSL protocol requires that the server always sends its
- certificate to the client in the beginning of the handshake. The
- client then retrieves the server's public key from the
- certificate, which means that the client can use the server's
- public key to encrypt messages to the server, and the server can
- decrypt those messages with its private key. Similarly, the
- server can encrypt messages to the client with its private key,
- and the client can decrypt messages with the server's public
- key. It is thus is with the server's public and private keys
- that messages in the handshake are encrypted and decrypted, and
- hence the key agreed upon for symmetric encryption of bulk data
- can be kept secret (there are more things to consider to really
- keep it secret, see <cite id="rescorla"></cite>).
- </p>
- <p>The above indicates that the server does not care who is
- connecting, and that only the client has the possibility to
- properly identify the server based on the server's certificate.
- That is indeed true in the minimal use of the protocol, but it
- is possible to instruct the server to request the certificate of
- the client, in order to have a means to identify the client, but
- it is by no means required to establish an SSL connection.
- </p>
- <p>If a server request the client certificate, it verifies, as a
- part of the protocol, that the client really holds the private
- key of the certificate by sending the client a string of bytes
- to encrypt with its private key, which the server then decrypts
- with the client's public key, the result of which is compared
- with the original string of bytes (a similar procedure is always
- performed by the client when it has received the server's
- certificate).
- </p>
- <p>The way clients and servers <em>authenticate</em> each other,
- i.e. proves that their respective peers are what they claim to
- be, is the topic of the next section.
- </p>
- </section>
+ <section>
+ <title>Digital Certificates</title>
+ <p>A certificate is similar to a driver's license, or a
+ passport. The holder of the certificate is called the
+ <em>subject</em>. The certificate is signed
+ with the private key of the issuer of the certificate. A chain
+ of trust is build by having the issuer in its turn being
+ certified by an other certificate and so on until you reach the
+ so called root certificate that is self signed i.e. issued
+ by itself.</p>
+
+ <p>Certificates are issued by <em>certification
+ authorities</em> (<em>CA</em>s) only. There are a handful of
+ top CAs in the world that issue root certificates. You can
+ examine the certificates of several of them by clicking
+ through the menus of your web browser.
+ </p>
+ </section>
+
+ <section>
+ <title>Authentication of Sender</title>
+
+ <p>Authentication of the sender is done by public key path
+ validation as defined in RFC 3280. Simplified that means that
+ each certificate in the certificate chain is issued by the one
+ before, the certificates attributes are valid ones, and the
+ root cert is a trusted cert that is present in the trusted
+ certs database kept by the peer.</p>
+
+ <p>The server will always send a certificate chain as part of
+ the TLS handshake, but the client will only send one if
+ the server requests it. If the client does not have
+ an appropriate certificate it may send an "empty" certificate
+ to the server.</p>
+
+ <p>The client may choose to accept some path evaluation errors
+ for instance a web browser may ask the user if they want to
+ accept an unknown CA root certificate. The server, if it request
+ a certificate, will on the other hand not accept any path validation
+ errors. It is configurable if the server should accept
+ or reject an "empty" certificate as response to
+ a certificate request.</p>
+ </section>
+
+ <section>
+ <title>TLS Sessions</title>
+
+ <p>From the TLS RFC "A TLS session is an association between a
+ client and a server. Sessions are created by the handshake
+ protocol. Sessions define a set of cryptographic security
+ parameters, which can be shared among multiple
+ connections. Sessions are used to avoid the expensive negotiation
+ of new security parameters for each connection."</p>
- <section>
- <title>Authentication</title>
- <p>As we have already seen the reception of a certificate from a
- peer is not enough to prove that the peer is authentic. More
- certificates are needed, and we have to consider how certificates
- are issued and on what grounds.
- </p>
- <p>Certificates are issued by <em>certification authorities</em>
- (<em>CA</em>s) only. They issue certificates both for other CAs
- and ordinary users (which are not CAs).
- </p>
- <p>Certain CAs are <em>top CAs</em>, i.e. they do not have a
- certificate issued by another CA. Instead they issue their own
- certificate, where the subject and issuer part of the
- certificate are identical (such a certificate is called a
- self-signed certificate). A top CA has to be well-known, and has
- to have a publicly available policy telling on what grounds it
- issues certificates.
- </p>
- <p>There are a handful of top CAs in the world. You can examine the
- certificates of several of them by clicking through the menus of
- your web browser.
- </p>
- <p>A top CA typically issues certificates for other CAs, called
- <em>intermediate CAs</em>, but possibly also to ordinary users. Thus
- the certificates derivable from a top CA constitute a tree, where
- the leaves of the tree are ordinary user certificates.
- </p>
- <p>A <em>certificate chain</em> is an ordered sequence of
- certificates, <c>C1, C2, ..., Cn</c>, say, where <c>C1</c> is a
- top CA certificate, and where <c>Cn</c> is an ordinary user
- certificate, and where the holder of <c>C1</c> is the issuer of
- <c>C2</c>, the holder of <c>C2</c> is the issuer of <c>C3</c>,
- ..., and the holder of <c>Cn-1</c> is the issuer of <c>Cn</c>,
- the ordinary user certificate. The holders of <c>C2, C3, ..., Cn-1</c> are then intermediate CAs.
- </p>
- <p>Now to verify that a certificate chain is unbroken we have to
- take the public key from each certificate <c>Ck</c>, and apply
- that key to decrypt the signature of certificate <c>Ck-1</c>,
- thus obtaining the message digest computed by the holder of the
- <c>Ck</c> certificate, compute the real message digest of the
- <c>Ck-1</c> certificate and compare the results. If they compare
- equal the link of the chain between <c>Ck</c> and <c>Ck-1</c> is
- considered to unbroken. This is done for each link k = 1, 2,
- ..., n-1. If all links are found to be unbroken, the user
- certificate <c>Cn</c> is considered authenticated.
- </p>
+ <p>Session data is by default kept by the ssl application in a
+ memory storage hence session data will be lost at application
+ restart or takeover. Users may define their own callback module
+ to handle session data storage if persistent data storage is
+ required. Session data will also be invalidated after 24 hours
+ from it was saved, for security reasons. It is of course
+ possible to configure the amount of time the session data should be
+ saved.</p>
- <section>
- <title>Trusted Certificates</title>
- <p>Now that there is a way to authenticate a certificate by
- checking that all links of a certificate chain are unbroken,
- the question is how you can be sure to trust the certificates
- in the chain, and in particular the top CA certificate of the
- chain.
- </p>
- <p>To provide an answer to that question consider the
- perspective of a client, which have just received the
- certificate of the server. In order to authenticate the server
- the client has to construct a certificate chain and to prove
- that the chain is unbroken. The client has to have a set of CA
- certificates (top CA or intermediate CA certificates) not
- obtained from the server, but obtained by other means. Those
- certificates are kept <c>locally</c> by the client, and are
- trusted by the client.
- </p>
- <p>More specifically, the client does not really have to have
- top CA certificates in its local storage. In order to
- authenticate a server it is sufficient for the client to
- posses the trusted certificate of the issuer of the server
- certificate.
- </p>
- <p>Now that is not the whole story. A server can send an
- (incomplete) certificate chain to its client, and then the
- task of the client is to construct a certificate chain that
- begins with a trusted certificate and ends with the server's
- certificate. (A client can also send a chain to its server,
- provided the server requested the client's certificate.)
- </p>
- <p>All this means that an unbroken certificate chain begins with
- a trusted certificate (top CA or not), and ends with the peer
- certificate. That is the end of the chain is obtained from the
- peer, but the beginning of the chain is obtained from local
- storage, which is considered trusted.
- </p>
- </section>
- </section>
-</chapter>
+ <p>Ssl clients will by default try to reuse an available session,
+ ssl servers will by default agree to reuse sessions when clients
+ ask to do so.</p>
+
+ </section>
+ </chapter>
diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml
new file mode 100644
index 0000000000..e0b07961fb
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="iso-8859-1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1999</year><year>2010</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+ <title>ssl</title>
+ <file>ssl_session_cache_api.xml</file>
+ </header>
+ <module>ssl_session_cache_api</module>
+ <modulesummary>Defines the API for the TLS session cache so
+ that the data storage scheme can be replaced by
+ defining a new callback module implementing this API.</modulesummary>
+
+ <section>
+ <title>Common Data Types</title>
+
+ <p>The following data types are used in the functions below:
+ </p>
+
+ <p><c>cache_ref() = opaque()</c></p>
+
+ <p><c>key() = {partialkey(), session_id()}</c></p>
+
+ <p><c>partialkey() = opaque()</c></p>
+
+ <p><c>session_id() = binary()</c></p>
+
+ <p><c>session() = opaque()</c></p>
+
+ </section>
+
+ <funcs>
+
+ <func>
+ <name>delete(Cache, Key) -> _</name>
+ <fsummary></fsummary>
+ <type>
+ <v> Cache = cache_ref()</v>
+ <v> Key = key()</v>
+ </type>
+ <desc>
+ <p> Deletes a cache entry. Will only be called from the cache
+ handling process.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>foldl(Fun, Acc0, Cache) -> Acc</name>
+ <fsummary></fsummary>
+ <type>
+ <v></v>
+ </type>
+ <desc>
+ <p>Calls Fun(Elem, AccIn) on successive elements of the
+ cache, starting with AccIn == Acc0. Fun/2 must return a new
+ accumulator which is passed to the next call. The function returns
+ the final value of the accumulator. Acc0 is returned if the cache is
+ empty.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>init() -> opaque() </name>
+ <fsummary>Return cache reference</fsummary>
+ <type>
+ <v></v>
+ </type>
+ <desc>
+ <p>Performs possible initializations of the cache and returns
+ a reference to it that will be used as parameter to the other
+ api functions. Will be called by the cache handling processes
+ init function, hence putting the same requirements on it as
+ a normal process init function.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>lookup(Cache, Key) -> Entry</name>
+ <fsummary> Looks up a cache entry.</fsummary>
+ <type>
+ <v> Cache = cache_ref()</v>
+ <v> Key = key()</v>
+ <v> Entry = session() | undefined </v>
+ </type>
+ <desc>
+ <p>Looks up a cache entry. Should be callable from any
+ process.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>select_session(Cache, PartialKey) -> [session()]</name>
+ <fsummary>>Selects sessions that could be reused.</fsummary>
+ <type>
+ <v> Cache = cache_ref()</v>
+ <v> PartialKey = partialkey()</v>
+ <v> Session = session()</v>
+ </type>
+ <desc>
+ <p>Selects sessions that could be reused. Should be callable
+ from any process.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>terminate(Cache) -> _</name>
+ <fsummary>Called by the process that handles the cache when it
+ is about to terminate.</fsummary>
+ <type>
+ <v>Cache = term() - as returned by init/0</v>
+ </type>
+ <desc>
+ <p>Takes care of possible cleanup that is needed when the
+ cache handling process terminates.
+ </p>
+ </desc>
+ </func>
+
+ <func>
+ <name>update(Cache, Key, Session) -> _</name>
+ <fsummary> Caches a new session or updates a already cached one.</fsummary>
+ <type>
+ <v> Cache = cache_ref()</v>
+ <v> Key = key()</v>
+ <v> Session = session()</v>
+ </type>
+ <desc>
+ <p> Caches a new session or updates a already cached one. Will
+ only be called from the cache handling process.
+ </p>
+ </desc>
+ </func>
+
+ </funcs>
+
+</erlref>
diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml
index 98071f5742..6528c00a0b 100644
--- a/lib/ssl/doc/src/usersguide.xml
+++ b/lib/ssl/doc/src/usersguide.xml
@@ -4,7 +4,7 @@
<part xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,43 +13,27 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>SSL User's Guide</title>
<prepared>OTP Team</prepared>
- <docno></docno>
<date>2003-05-26</date>
- <rev>B</rev>
<file>usersguide.sgml</file>
</header>
<description>
<p>The <em>SSL</em> application provides secure communication over
sockets.
</p>
- <p>This product includes software developed by the OpenSSL Project for
- use in the OpenSSL Toolkit (http://www.openssl.org/).
- </p>
- <p>This product includes cryptographic software written by Eric Young
- </p>
- <p>This product includes software written by Tim Hudson
- </p>
- <p>For full OpenSSL and SSLeay license texts, see <seealso marker="licenses#licenses">Licenses</seealso>.
- </p>
</description>
<xi:include href="ssl_protocol.xml"/>
<xi:include href="using_ssl.xml"/>
- <xi:include href="pkix_certs.xml"/>
- <xi:include href="create_certs.xml"/>
<xi:include href="ssl_distribution.xml"/>
- <xi:include href="licenses.xml"/>
</part>
diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml
index ba74dcfef4..605290b6f9 100644
--- a/lib/ssl/doc/src/using_ssl.xml
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,93 +21,129 @@
</legalnotice>
- <title>Using the SSL application</title>
- <prepared>Peter H&ouml;gfeldt</prepared>
- <docno></docno>
- <date>2003-04-23</date>
- <rev>PA2</rev>
+ <title>Using the SSL API</title>
<file>using_ssl.xml</file>
</header>
- <p>Here we provide an introduction to using the Erlang/OTP SSL
- application, which is accessed through the <c>ssl</c> interface
- module.
- </p>
- <p>We also present example code in the Erlang module
- <c>client_server</c>, also provided in the directory
- <c>ssl-X.Y.Z/examples</c>, with source code in <c>src</c> and the
- compiled module in <c>ebin</c> of that directory.
- </p>
<section>
- <title>The ssl Module</title>
- <p>The <c>ssl</c> module provides the user interface to the Erlang/OTP
- SSL application. The interface functions provided are very similar
- to those provided by the <c>gen_tcp</c> and <c>inet</c> modules.
- </p>
- <p>Servers use the interface functions <c>listen</c> and
- <c>accept</c>. The <c>listen</c> function specifies a TCP port
- to to listen to, and each call to the <c>accept</c> function
- establishes an incoming connection.
- </p>
- <p>Clients use the <c>connect</c> function which specifies the address
- and port of a server to connect to, and a successful call establishes
- such a connection.
- </p>
- <p>The <c>listen</c> and <c>connect</c> functions have almost all
- the options that the corresponding functions in <c>gen_tcp/</c> have,
- but there are also additional options specific to the SSL protocol.
- </p>
- <p>The most important SSL specific option is the <c>cacertfile</c>
- option which specifies a local file containing trusted CA
- certificates which are and used for peer authentication. This
- option is used by clients and servers in case they want to
- authenticate their peers.
- </p>
- <p>The <c>certfile</c> option specifies a local path to a file
- containing the certificate of the holder of the connection
- endpoint. In case of a server endpoint this option is mandatory
- since the contents of the sever certificate is needed in the
- the handshake preceding the establishment of a connection.
- </p>
- <p>Similarly, the <c>keyfile</c> option points to a local file
- containing the private key of the holder of the endpoint. If the
- <c>certfile</c> option is present, this option has to be
- specified as well, unless the private key is provided in the
- same file as specified by the <c>certfile</c> option (a
- certificate and a private key can thus coexist in the same file).
- </p>
- <p>The <c>verify</c> option specifies how the peer should be verified:
- </p>
- <taglist>
- <tag>0</tag>
- <item>Do not verify the peer,</item>
- <tag>1</tag>
- <item>Verify peer,</item>
- <tag>2</tag>
- <item>Verify peer, fail the verification if the peer has no
- certificate. </item>
- </taglist>
- <p>The <c>depth</c> option specifies the maximum length of the
- verification certificate chain. Depth = 0 means the peer
- certificate, depth = 1 the CA certificate, depth = 2 the next CA
- certificate etc. If the verification process does not find a
- trusted CA certificate within the maximum length, the verification
- fails.
- </p>
- <p>The <c>ciphers</c> option specifies which ciphers to use (a
- string of colon separated cipher names). To obtain a list of
- available ciphers, evaluate the <c>ssl:ciphers/0</c> function
- (the SSL application has to be running).
- </p>
- </section>
+ <title>General information</title>
+ <p>To see relevant version information for ssl you can
+ call ssl:versions/0</p>
+
+ <p>To see all supported cipher suites
+ call ssl:cipher_suites/0. Note that available cipher suites
+ for a connection will depend on your certificate. It is also
+ possible to specify a specific cipher suite(s) that you
+ want your connection to use. Default is to use the strongest
+ available.</p>
- <section>
- <title>A Client-Server Example</title>
- <p>Here is a simple client server example.
- </p>
- <codeinclude file="../../examples/src/client_server.erl" tag="" type="erl"></codeinclude>
</section>
-</chapter>
-
-
+
+ <section>
+ <title>Setting up connections</title>
+
+ <p>Here follows some small example of how to set up client/server connections
+ using the erlang shell. The returned value of the sslsocket has been abbreviated with
+ <c>[...]</c> as it can be fairly large and is opaque.</p>
+
+ <section>
+ <title>Minmal example</title>
+
+ <note><p> The minimal setup is not the most secure setup of ssl.</p>
+ </note>
+
+ <p> Start server side</p>
+ <code type="erl">1 server> ssl:start().
+ok</code>
+
+ <p>Create a ssl listen socket</p>
+ <code type="erl">2 server> {ok, ListenSocket} =
+ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]).
+{ok,{sslsocket, [...]}}</code>
+
+ <p>Do a transport accept on the ssl listen socket</p>
+ <code type="erl">3 server> {ok, Socket} = ssl:transport_accept(ListenSocket).
+{ok,{sslsocket, [...]}}</code>
+ <p>Start client side</p>
+ <code type="erl">1 client> ssl:start().
+ok</code>
+
+ <code type="erl">2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity).
+{ok,{sslsocket, [...]}}</code>
+
+ <p>Do the ssl handshake</p>
+ <code type="erl">4 server> ok = ssl:ssl_accept(Socket).
+ok</code>
+
+ <p>Send a messag over ssl</p>
+ <code type="erl">5 server> ssl:send(Socket, "foo").
+ok</code>
+
+ <p>Flush the shell message queue to see that we got the message
+ sent on the server side</p>
+ <code type="erl">3 client> flush().
+Shell got {ssl,{sslsocket,[...]},"foo"}
+ok</code>
+ </section>
+
+ <section>
+ <title>Upgrade example</title>
+
+ <note><p> To upgrade a TCP/IP connection to a ssl connection the
+ client and server have to aggre to do so. Agreement
+ may be accompliced by using a protocol such the one used by HTTP
+ specified in RFC 2817.</p> </note>
+
+ <p>Start server side</p>
+ <code type="erl">1 server> ssl:start().
+ok</code>
+
+ <p>Create a normal tcp listen socket</p>
+ <code type="erl">2 server> {ok, ListenSocket} = gen_tcp:listen(9999, [{reuseaddr, true}]).
+{ok, #Port&lt;0.475&gt;}</code>
+
+ <p>Accept client connection</p>
+ <code type="erl">3 server> {ok, Socket} = gen_tcp:accept(ListenSocket).
+{ok, #Port&lt;0.476&gt;}</code>
+
+ <p>Start client side</p>
+ <code type="erl">1 client> ssl:start().
+ok</code>
+
+ <code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code>
+
+ <p>Make sure active is set to false before trying
+ to upgrade a connection to a ssl connection, otherwhise
+ ssl handshake messages may be deliverd to the wrong process.</p>
+ <code type="erl">4 server> inet:setopts(Socket, [{active, false}]).
+ok</code>
+
+ <p>Do the ssl handshake.</p>
+ <code type="erl">5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"},
+{certfile, "cert.pem"}, {keyfile, "key.pem"}]).
+{ok,{sslsocket,[...]}}</code>
+
+ <p> Upgrade to a ssl connection. Note that the client and server
+ must agree upon the upgrade and the server must call
+ ssl:accept/2 before the client calls ssl:connect/3.</p>
+ <code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"},
+{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity).
+{ok,{sslsocket,[...]}}</code>
+
+ <p>Send a messag over ssl</p>
+ <code type="erl">4 client> ssl:send(SSLSocket, "foo").
+ok</code>
+
+ <p>Set active true on the ssl socket</p>
+ <code type="erl">4 server> ssl:setopts(SSLSocket, [{active, true}]).
+ok</code>
+
+ <p>Flush the shell message queue to see that we got the message
+ sent on the client side</p>
+ <code type="erl">5 server> flush().
+Shell got {ssl,{sslsocket,[...]},"foo"}
+ok</code>
+ </section>
+ </section>
+ </chapter>
diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile
index 121fcc6950..a4f067ade6 100644
--- a/lib/ssl/examples/certs/Makefile
+++ b/lib/ssl/examples/certs/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+# Copyright Ericsson AB 2003-2010. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -21,4 +21,41 @@
# Invoke with GNU make or clearmake -C gnu.
#
-include $(ERL_TOP)/make/run_make.mk
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(SSL_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN)
+
+TARGET_FILES=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+clean:
+ rm -fr $(TARGET_FILES) *~ *.beam
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/examples/certs
+ tar cf - etc | \
+ (cd $(RELSYSDIR)/examples/certs; tar xf -)
+ chmod -R ug+rw $(RELSYSDIR)/examples
+release_docs_spec:
diff --git a/lib/ssl/examples/certs/Makefile.in b/lib/ssl/examples/certs/Makefile.in
deleted file mode 100644
index 4ea7aaf6dc..0000000000
--- a/lib/ssl/examples/certs/Makefile.in
+++ /dev/null
@@ -1,80 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-#
-
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-include ../../vsn.mk
-VSN=$(SSL_VSN)
-
-RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN)
-
-EBIN = ebin
-ETC = etc
-SRC = src
-
-OPENSSL_CMD = @OPENSSL_CMD@
-
-# We are generating more files than in the following list, but we take
-# there existence as successful execution of make rules
-
-PEMS = cacerts.pem cert.pem key.pem
-
-PEMFILES = $(PEMS:%=$(ETC)/client/%) $(PEMS:%=$(ETC)/server/%)
-
-debug opt: $(PEMFILES)
-
-$(PEMFILES): done
-
-done: $(EBIN)/make_certs.beam
- erl -noinput -pa $(EBIN) -run make_certs all $(OPENSSL_CMD) \
- -s erlang halt
- echo >done
-
-$(EBIN)/make_certs.beam: $(SRC)/make_certs.erl
- cd src; erlc -W -o ../$(EBIN) make_certs.erl
-
-clean:
- rm -fr $(EBIN)/* $(SRC)/*~ $(SRC)/*.beam $(ETC) done \
- stderr.txt erl_crash.dump *~
-
-docs:
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/examples/certs
- tar cf - Makefile ebin etc rnd src | \
- (cd $(RELSYSDIR)/examples/certs; tar xf -)
- chmod -f -R ug+rw $(RELSYSDIR)/examples
-
-release_docs_spec:
-
-
-
-
-
-
-
-
diff --git a/lib/ssl/examples/certs/etc/client/cacerts.pem b/lib/ssl/examples/certs/etc/client/cacerts.pem
new file mode 100644
index 0000000000..cb19d3d41e
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/client/cacerts.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIICizCCAfSgAwIBAgIFdMMs9fEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI
+ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD
+VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS
+BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw
+MDAwMDBaMH0xETAPBgNVBAMTCGVybGFuZ0NBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0
+ZXJAZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEP
+MA0GA1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAgmHw2xApZqdzZOOPTzwHr1hRYd1OqbLOsXbAq6kJ
+Kuu+qe5jAlMF3vnUhiHomuZeNZVJe3SP+JfBt3BHMjm2CLChCuNgfctKURMlEc/L
+xo8fO1Jk9MD5mbG2Utx3m3gM6Liwt9fHVABlCTyB6/jXrK1tYpEG5CrwUXyy8Htl
+jHECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAl
+0tMEXWPgzXTpDuNmuKh6aGq9CuExUuEXXQQWPThzEuluA3aHFmObziQlMY1+KeO1
+AL0kpx0Yhvju/rfAJ+OF6MMni6hJoKlYTVml+fCY89A3nmY1rJHJavjHp0OIPGxh
+4Sr+EcjROkqe8jE0DmbwmM6lzpwSJscxte+V6HvGRw==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIICiDCCAfGgAwIBAgIFSHyFNTEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI
+ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD
+VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS
+BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw
+MDAwMDBaMHoxDjAMBgNVBAMTBW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJA
+ZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0G
+A1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAjEt9iy365+mTialKDKb3l2QPg71yavJA1ZC6aGC14X7x
+KCm1FhUYsVKOlWjmC1VYJiCS01gvKqMXiogreHJGM93E+URlKkOm9kmOWQwLfFb8
+JLzafPi3/8TUdjl8UuIDHyPsoQiM2ZBDUVWezfl+CBsTYFO3U4Lqf9OKbCxTF78C
+AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAv6vHw
+wK3MvxzlhDJIx7rUasOYJDZJyOt71KdOKeA7+ocbvDIblmV7sTbe3oQNqbSATZ6H
+RUqHZdPhKIZ9wjEBSKdBTL8rc0TvbztMvd+i0rkTCL/bspQYchA2zCcjgkWqpaN4
+OhOjQR1+9/ntmaU/r5Ca7KmrXEf5XSQIGLSMag==
+-----END CERTIFICATE-----
+
diff --git a/lib/ssl/examples/certs/etc/client/cert.pem b/lib/ssl/examples/certs/etc/client/cert.pem
new file mode 100644
index 0000000000..a2f53aaf82
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/client/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIChzCCAfCgAwIBAgIGAIsapa8BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
+BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
+BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
+VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
+MDAwWjB7MQ8wDQYDVQQDEwZjbGllbnQxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
+cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
+VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSV
+wC+n0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53
+h2Zr3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAG8t6f1A
+PF7xayGxtUpG2r6W5ETylC3ZIKPS2kfJk9aYi7AZNTp7/xTU6SgqvFBN8aBPzxCD
+4jHrSNC8DSb4X1x9uimarb6qdZDHEdij+DRAd2eygJHZxEf7+8B4Fx34thQeU9hZ
+S1Izke5AlsyFMkvB7h0anE4k9BfuU70vl6v5
+-----END CERTIFICATE-----
+
diff --git a/lib/ssl/examples/certs/etc/client/key.pem b/lib/ssl/examples/certs/etc/client/key.pem
new file mode 100644
index 0000000000..4d55b08f4c
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/client/key.pem
@@ -0,0 +1,16 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCTFBPkOO98fDY3j6MIxIGKp+rampfIay50Lx4+EnCnRSSVwC+n
+0VVmP7V5SGFJpuXJzN0hvqPUWOOjiMTNlNRaGy0pqu2oMXWAPLOxHWL1wT53h2Zr
+3FUNU/N0Rvnkttse1KZJ9uYCLKUiuXXsv2rR62nH3OhRIiBHSAcSv0NRWwIDAQAB
+AoGACdIVYe/LTeydUihtInC8lZ2QuPgJmoBNocRjqJFipEihoL4scHAx25n1bBvB
+I0HZphffzBkGp28oBAtl2LRPWXqu527unc/RWRfLMqSK1xNSq1DxD1a30zkrZPna
+QiV65vEJuNSJTtlDy/Zqc/BVZXCpxWlzYQedZgkmf0Qse8ECQQCmaz02Yur8zC9f
+eSQKU5OSzGw3bSIumEzziCfHdTheK6MEoccf5TCAyLXhZwA7QlKja4tFXfeyVxws
+/LlnUJN9AkEA4j+xnOeYUyGKXL5i+BAbnqpI4MzPiq+IoCYkaRlD/wAws24r5HNI
+ZQmEHWqD/NNzOf/A2XuyLtMiTGJPW/DftwJBAKKpJP6Ytuh6xz8BUCnLwO12Y7vV
+LtjuQiCzD3aUa5EYA9HOMqxJPxxRkf0LyR0i2VUkE8+sZiPpov+R0cJa7p0CQQCj
+40GUiArGRSiF7/+e84QeVfl+pb29F1QftiFv5DZmFEwy3Z572KpbTh5edJbxYHY6
+UDHxGHJFCvnwXNJhpkVXAkBJqfEfiMJ3Q/E5Gpf3sQizacouW92iiN8ojlF1oB80
+t34RysJH7SgI3gdMhTribCo2UUaV0StjR6yodPN+TB2J
+-----END RSA PRIVATE KEY-----
+
diff --git a/lib/ssl/examples/certs/etc/erlangCA/cert.pem b/lib/ssl/examples/certs/etc/erlangCA/cert.pem
new file mode 100644
index 0000000000..c4386494dc
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/erlangCA/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICizCCAfSgAwIBAgIFdMMs9fEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI
+ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD
+VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS
+BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw
+MDAwMDBaMH0xETAPBgNVBAMTCGVybGFuZ0NBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0
+ZXJAZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEP
+MA0GA1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAgmHw2xApZqdzZOOPTzwHr1hRYd1OqbLOsXbAq6kJ
+Kuu+qe5jAlMF3vnUhiHomuZeNZVJe3SP+JfBt3BHMjm2CLChCuNgfctKURMlEc/L
+xo8fO1Jk9MD5mbG2Utx3m3gM6Liwt9fHVABlCTyB6/jXrK1tYpEG5CrwUXyy8Htl
+jHECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAl
+0tMEXWPgzXTpDuNmuKh6aGq9CuExUuEXXQQWPThzEuluA3aHFmObziQlMY1+KeO1
+AL0kpx0Yhvju/rfAJ+OF6MMni6hJoKlYTVml+fCY89A3nmY1rJHJavjHp0OIPGxh
+4Sr+EcjROkqe8jE0DmbwmM6lzpwSJscxte+V6HvGRw==
+-----END CERTIFICATE-----
+
diff --git a/lib/ssl/examples/certs/etc/otpCA/cert.pem b/lib/ssl/examples/certs/etc/otpCA/cert.pem
new file mode 100644
index 0000000000..8610621695
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/otpCA/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICiDCCAfGgAwIBAgIFSHyFNTEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI
+ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD
+VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS
+BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw
+MDAwMDBaMHoxDjAMBgNVBAMTBW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJA
+ZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0G
+A1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAjEt9iy365+mTialKDKb3l2QPg71yavJA1ZC6aGC14X7x
+KCm1FhUYsVKOlWjmC1VYJiCS01gvKqMXiogreHJGM93E+URlKkOm9kmOWQwLfFb8
+JLzafPi3/8TUdjl8UuIDHyPsoQiM2ZBDUVWezfl+CBsTYFO3U4Lqf9OKbCxTF78C
+AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAv6vHw
+wK3MvxzlhDJIx7rUasOYJDZJyOt71KdOKeA7+ocbvDIblmV7sTbe3oQNqbSATZ6H
+RUqHZdPhKIZ9wjEBSKdBTL8rc0TvbztMvd+i0rkTCL/bspQYchA2zCcjgkWqpaN4
+OhOjQR1+9/ntmaU/r5Ca7KmrXEf5XSQIGLSMag==
+-----END CERTIFICATE-----
+
diff --git a/lib/ssl/examples/certs/etc/server/cacerts.pem b/lib/ssl/examples/certs/etc/server/cacerts.pem
new file mode 100644
index 0000000000..cb19d3d41e
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/server/cacerts.pem
@@ -0,0 +1,34 @@
+-----BEGIN CERTIFICATE-----
+MIICizCCAfSgAwIBAgIFdMMs9fEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI
+ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD
+VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS
+BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw
+MDAwMDBaMH0xETAPBgNVBAMTCGVybGFuZ0NBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0
+ZXJAZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEP
+MA0GA1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG
+9w0BAQEFAAOBjQAwgYkCgYEAgmHw2xApZqdzZOOPTzwHr1hRYd1OqbLOsXbAq6kJ
+Kuu+qe5jAlMF3vnUhiHomuZeNZVJe3SP+JfBt3BHMjm2CLChCuNgfctKURMlEc/L
+xo8fO1Jk9MD5mbG2Utx3m3gM6Liwt9fHVABlCTyB6/jXrK1tYpEG5CrwUXyy8Htl
+jHECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAl
+0tMEXWPgzXTpDuNmuKh6aGq9CuExUuEXXQQWPThzEuluA3aHFmObziQlMY1+KeO1
+AL0kpx0Yhvju/rfAJ+OF6MMni6hJoKlYTVml+fCY89A3nmY1rJHJavjHp0OIPGxh
+4Sr+EcjROkqe8jE0DmbwmM6lzpwSJscxte+V6HvGRw==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIICiDCCAfGgAwIBAgIFSHyFNTEwDQYJKoZIhvcNAQEFBQAwfTERMA8GA1UEAxMI
+ZXJsYW5nQ0ExIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBlcmxhbmcub3JnMRIwEAYD
+VQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYDVQQKEwZlcmxhbmcxFDAS
+BgNVBAsTC3Rlc3RpbmcgZGVwMCIYDzIwMTAwOTAxMDAwMDAwWhgPMjAyNTA4Mjgw
+MDAwMDBaMHoxDjAMBgNVBAMTBW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJA
+ZXJsYW5nLm9yZzESMBAGA1UEBxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0G
+A1UEChMGZXJsYW5nMRQwEgYDVQQLEwt0ZXN0aW5nIGRlcDCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEAjEt9iy365+mTialKDKb3l2QPg71yavJA1ZC6aGC14X7x
+KCm1FhUYsVKOlWjmC1VYJiCS01gvKqMXiogreHJGM93E+URlKkOm9kmOWQwLfFb8
+JLzafPi3/8TUdjl8UuIDHyPsoQiM2ZBDUVWezfl+CBsTYFO3U4Lqf9OKbCxTF78C
+AwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAv6vHw
+wK3MvxzlhDJIx7rUasOYJDZJyOt71KdOKeA7+ocbvDIblmV7sTbe3oQNqbSATZ6H
+RUqHZdPhKIZ9wjEBSKdBTL8rc0TvbztMvd+i0rkTCL/bspQYchA2zCcjgkWqpaN4
+OhOjQR1+9/ntmaU/r5Ca7KmrXEf5XSQIGLSMag==
+-----END CERTIFICATE-----
+
diff --git a/lib/ssl/examples/certs/etc/server/cert.pem b/lib/ssl/examples/certs/etc/server/cert.pem
new file mode 100644
index 0000000000..f26adb7f5c
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/server/cert.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIChzCCAfCgAwIBAgIGANUxXM9BMA0GCSqGSIb3DQEBBQUAMHoxDjAMBgNVBAMT
+BW90cENBMSAwHgYJKoZIhvcNAQkBFhF0ZXN0ZXJAZXJsYW5nLm9yZzESMBAGA1UE
+BxMJU3RvY2tob2xtMQswCQYDVQQGEwJTRTEPMA0GA1UEChMGZXJsYW5nMRQwEgYD
+VQQLEwt0ZXN0aW5nIGRlcDAiGA8yMDEwMDkwMTAwMDAwMFoYDzIwMjUwODI4MDAw
+MDAwWjB7MQ8wDQYDVQQDEwZzZXJ2ZXIxIDAeBgkqhkiG9w0BCQEWEXRlc3RlckBl
+cmxhbmcub3JnMRIwEAYDVQQHEwlTdG9ja2hvbG0xCzAJBgNVBAYTAlNFMQ8wDQYD
+VQQKEwZlcmxhbmcxFDASBgNVBAsTC3Rlc3RpbmcgZGVwMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9
+Adq67k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ
+4UAtNHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQID
+AQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBAGF5Pfwk
+QDdwJup/mVITPxbBls4Yl7anDooUQsq8066lA1g54H/PRfXscGkyCFGh1ifXvf1L
+psMRoBAdDHL/wSJplk3rRavkC94eBgnTFZmfKL6844g1j53yameiYL8IEVExYMBg
+/XGyc0qwq57WT8B/K4aElrvlBlQ0wF3wN54M
+-----END CERTIFICATE-----
+
diff --git a/lib/ssl/examples/certs/etc/server/key.pem b/lib/ssl/examples/certs/etc/server/key.pem
new file mode 100644
index 0000000000..c1392ca557
--- /dev/null
+++ b/lib/ssl/examples/certs/etc/server/key.pem
@@ -0,0 +1,16 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCf4Htxr99lLs5W8QQw7jdakqyAkIjOW4aqH8sr4va4SvZ9Adq6
+7k8jMHefCVZo+F8x4cwsBgB4aWzFIGBnvFTi6YsH27XW7f9O9IPCej8fdhRZ4UAt
+NHa253buOWpDGla2JmIdkmfFvXFJycMIKbG5tYilVXoWKBMKmCwWaXz0nQIDAQAB
+AoGAQIlma0r6W6bcRj4+Wd4fXCFvHuq5Psu1fYEeC5Yvz8761xVjjSfbrDHJZ9pm
+FjOEgedK+s5lbDXqYVyjbdyZSugStBRocSmbG8SQHcAsxR2ZIkNzX2hYzB+lslWo
+T3YJojDyB134O7XJznCu+ZFXP86jyJ1JT6k6a+OIHcwnJ+ECQQDYn57dY4Px3mEd
+VBLStN3YkRF5oFyT+xk7IaKeLLB6n4gCnoVbBoHut7PFbPYPzoNzEwPk3MQKDIHb
+Kig3S5CpAkEAvPA1VmoJWAlN6kUi+F2L8HXEArzE8x7vwdsslrwMKUe4dFS+ZC/7
+5iDOaxcZ7TYkCgwzBt341++DCgP6j3fY1QJBALB6AcOcwi52m6l4B8mu3ZkEPjdX
+BHTuONTqhv/TqoaLlxODL2NDvvDKqeMp7KBd/srt79swW2lQXS4+fvrlTdkCQQCm
+zxj4O1QWkthkfje6ubSkTwUIOatUzrp1F9GNH2dJRtX2dx9FCwxGCC7WY6XzRXqa
+GF0wsedSllbGD+82nWQlAkAicMGqCqRq4hKR/cVmFatOqKVWCVkx6OFF2FhuiI5Z
+h5eIOPGCt8dVRs1P9DNSld/D98Sfm65m85z8BtXovvYV
+-----END RSA PRIVATE KEY-----
+
diff --git a/lib/ssl/examples/certs/rnd/RAND b/lib/ssl/examples/certs/rnd/RAND
deleted file mode 100644
index 70997bd01f..0000000000
--- a/lib/ssl/examples/certs/rnd/RAND
+++ /dev/null
Binary files differ
diff --git a/lib/ssl/examples/certs/src/make_certs.erl b/lib/ssl/examples/certs/src/make_certs.erl
index c374836568..fe267bed28 100644
--- a/lib/ssl/examples/certs/src/make_certs.erl
+++ b/lib/ssl/examples/certs/src/make_certs.erl
@@ -1,261 +1,48 @@
-%% The purpose of this module is to create example certificates for
-%% testing.
-%% Run it as:
-%%
-%% erl -noinput -run make_certs all "/path/to/openssl" -s erlang halt
-%%
+%% The purpose of this module is to log how the example certs where created,
+%% it requires erl_make_certs found in the test directory.
-module(make_certs).
--export([all/0, all/1]).
-
--record(dn, {commonName,
- organizationalUnitName = "Erlang OTP",
- organizationName = "Ericsson AB",
- localityName = "Stockholm",
- countryName = "SE",
- emailAddress = "[email protected]"}).
+-export([all/0]).
all() ->
- all(["openssl"]).
-
-all([OpenSSLCmd]) ->
- Root = filename:dirname(filename:dirname((code:which(?MODULE)))),
- %% io:fwrite("Root : ~s~n", [Root]),
- NRoot = filename:join([Root, "etc"]),
- file:make_dir(NRoot),
- create_rnd(Root, "etc"), % For all requests
- rootCA(NRoot, OpenSSLCmd, "erlangCA"),
- intermediateCA(NRoot, OpenSSLCmd, "otpCA", "erlangCA"),
- endusers(NRoot, OpenSSLCmd, "otpCA", ["client", "server"]),
- collect_certs(NRoot, ["erlangCA", "otpCA"], ["client", "server"]),
- remove_rnd(Root, "etc").
-
-rootCA(Root, OpenSSLCmd, Name) ->
- create_ca_dir(Root, Name, ca_cnf(Name)),
- DN = #dn{commonName = Name},
- create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)),
- ok.
-
-intermediateCA(Root, OpenSSLCmd, CA, ParentCA) ->
- CA = "otpCA",
- create_ca_dir(Root, CA, ca_cnf(CA)),
- CARoot = filename:join([Root, CA]),
- DN = #dn{commonName = CA},
- CnfFile = filename:join([CARoot, "req.cnf"]),
- file:write_file(CnfFile, req_cnf(DN)),
- KeyFile = filename:join([CARoot, "private", "key.pem"]),
- ReqFile = filename:join([CARoot, "req.pem"]),
- create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
- CertFile = filename:join([CARoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile).
-
-endusers(Root, OpenSSLCmd, CA, Users) ->
- lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users).
-
-enduser(Root, OpenSSLCmd, CA, User) ->
- UsrRoot = filename:join([Root, User]),
- file:make_dir(UsrRoot),
- CnfFile = filename:join([UsrRoot, "req.cnf"]),
- DN = #dn{commonName = User},
- file:write_file(CnfFile, req_cnf(DN)),
- KeyFile = filename:join([UsrRoot, "key.pem"]),
- ReqFile = filename:join([UsrRoot, "req.pem"]),
- create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
- CertFile = filename:join([UsrRoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFile).
-
-collect_certs(Root, CAs, Users) ->
- Bins = lists:foldr(
- fun(CA, Acc) ->
- File = filename:join([Root, CA, "cert.pem"]),
- {ok, Bin} = file:read_file(File),
- [Bin, "\n" | Acc]
- end, [], CAs),
- lists:foreach(
- fun(User) ->
- File = filename:join([Root, User, "cacerts.pem"]),
- file:write_file(File, Bins)
- end, Users).
-
-create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
- CARoot = filename:join([Root, CAName]),
- CnfFile = filename:join([CARoot, "req.cnf"]),
- file:write_file(CnfFile, Cnf),
- KeyFile = filename:join([CARoot, "private", "key.pem"]),
- CertFile = filename:join([CARoot, "cert.pem"]),
- Cmd = [OpenSSLCmd, " req"
- " -new"
- " -x509"
- " -config ", CnfFile,
- " -keyout ", KeyFile,
- " -out ", CertFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
-
-create_ca_dir(Root, CAName, Cnf) ->
- CARoot = filename:join([Root, CAName]),
- file:make_dir(CARoot),
- create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
- create_rnd(Root, filename:join([CAName, "private"])),
- create_files(CARoot, [{"serial", "01\n"},
- {"index.txt", ""},
- {"ca.cnf", Cnf}]).
-
-create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
- Cmd = [OpenSSLCmd, " req"
- " -new"
- " -config ", CnfFile,
- " -keyout ", KeyFile,
- " -out ", ReqFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
-
-sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
- CACnfFile = filename:join([Root, CA, "ca.cnf"]),
- Cmd = [OpenSSLCmd, " ca"
- " -batch"
- " -notext"
- " -config ", CACnfFile,
- " -extensions ", CertType,
- " -in ", ReqFile,
- " -out ", CertFile],
- Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ LongTime = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+15*365),
+ Validity = {date(), LongTime},
+ Subject = [{email, "[email protected]"},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+
+ RootCa = erl_make_certs:make_cert([{validity, Validity}, {subject, [{name, "erlangCA"}|Subject]}]),
+ ImedCa = erl_make_certs:make_cert([{issuer, RootCa}, {validity, Validity},
+ {subject, [{name, "otpCA"}|Subject]}]),
+ ClientCa = erl_make_certs:make_cert([{issuer, ImedCa}, {validity, Validity},
+ {subject, [{name, "client"}|Subject]}]),
+ ServerCa = erl_make_certs:make_cert([{issuer, ImedCa}, {validity, Validity},
+ {subject, [{name, "server"}|Subject]}]),
+
+ Root0 = filename:dirname(filename:dirname((code:which(?MODULE)))),
+ Root = filename:join([Root0, "etc"]), file:make_dir(Root),
+ CaPath = filename:join([Root, "erlangCA"]), file:make_dir(CaPath),
+ IPath = filename:join([Root, "otpCA"]), file:make_dir(IPath),
+ CPath = filename:join([Root, "client"]), file:make_dir(CPath),
+ SPath = filename:join([Root, "server"]), file:make_dir(SPath),
+
+ erl_make_certs:write_pem(CaPath,"cert", RootCa),
+ erl_make_certs:write_pem(IPath, "cert", ImedCa),
+
+ {ok, CaBin0} = file:read_file(filename:join(CaPath, "cert.pem")),
+ {ok, CaBin1} = file:read_file(filename:join(IPath, "cert.pem")),
+ CaBin = <<CaBin0/binary, CaBin1/binary>>,
+
+ erl_make_certs:write_pem(CPath, "cert", ClientCa),
+ ok = file:write_file(filename:join(CPath, "cacerts.pem"), CaBin),
+ erl_make_certs:write_pem(SPath, "cert", ServerCa),
+ ok = file:write_file(filename:join(SPath, "cacerts.pem"), CaBin),
-%%
-%% Misc
-%%
-
-create_dirs(Root, Dirs) ->
- lists:foreach(fun(Dir) ->
- file:make_dir(filename:join([Root, Dir])) end,
- Dirs).
-
-create_files(Root, NameContents) ->
- lists:foreach(
- fun({Name, Contents}) ->
- file:write_file(filename:join([Root, Name]), Contents) end,
- NameContents).
-
-create_rnd(Root, Dir) ->
- From = filename:join([Root, "rnd", "RAND"]),
- To = filename:join([Root, Dir, "RAND"]),
- file:copy(From, To).
-
-remove_rnd(Root, Dir) ->
- File = filename:join([Root, Dir, "RAND"]),
- file:delete(File).
-
-cmd(Cmd, Env) ->
- FCmd = lists:flatten(Cmd),
- Port = open_port({spawn, FCmd}, [stream, eof, exit_status,
- {env, Env}]),
- eval_cmd(Port).
-
-eval_cmd(Port) ->
- receive
- {Port, {data, _}} ->
- eval_cmd(Port);
- {Port, eof} ->
- ok
- end,
- receive
- {Port, {exit_status, Status}} when Status /= 0 ->
- %% io:fwrite("exit status: ~w~n", [Status]),
- erlang:halt(Status)
- after 0 ->
- ok
- end.
-
-%%
-%% Contents of configuration files
-%%
-
-req_cnf(DN) ->
- ["# Purpose: Configuration for requests (end users and CAs)."
- "\n"
- "ROOTDIR = $ENV::ROOTDIR\n"
- "\n"
-
- "[req]\n"
- "input_password = secret\n"
- "output_password = secret\n"
- "default_bits = 1024\n"
- "RANDFILE = $ROOTDIR/RAND\n"
- "encrypt_key = no\n"
- "default_md = sha1\n"
- "#string_mask = pkix\n"
- "x509_extensions = ca_ext\n"
- "prompt = no\n"
- "distinguished_name= name\n"
- "\n"
-
- "[name]\n"
- "commonName = ", DN#dn.commonName, "\n"
- "organizationalUnitName = ", DN#dn.organizationalUnitName, "\n"
- "organizationName = ", DN#dn.organizationName, "\n"
- "localityName = ", DN#dn.localityName, "\n"
- "countryName = ", DN#dn.countryName, "\n"
- "emailAddress = ", DN#dn.emailAddress, "\n"
- "\n"
-
- "[ca_ext]\n"
- "basicConstraints = critical, CA:true\n"
- "keyUsage = cRLSign, keyCertSign\n"
- "subjectKeyIdentifier = hash\n"
- "subjectAltName = email:copy\n"].
-
-
-ca_cnf(CA) ->
- ["# Purpose: Configuration for CAs.\n"
- "\n"
- "ROOTDIR = $ENV::ROOTDIR\n"
- "default_ca = ca\n"
- "\n"
-
- "[ca]\n"
- "dir = $ROOTDIR/", CA, "\n"
- "certs = $dir/certs\n"
- "crl_dir = $dir/crl\n"
- "database = $dir/index.txt\n"
- "new_certs_dir = $dir/newcerts\n"
- "certificate = $dir/cert.pem\n"
- "serial = $dir/serial\n"
- "crl = $dir/crl.pem\n"
- "private_key = $dir/private/key.pem\n"
- "RANDFILE = $dir/private/RAND\n"
- "\n"
- "x509_extensions = user_cert\n"
- "default_days = 3600\n"
- "default_md = sha1\n"
- "preserve = no\n"
- "policy = policy_match\n"
- "\n"
-
- "[policy_match]\n"
- "commonName = supplied\n"
- "organizationalUnitName = optional\n"
- "organizationName = match\n"
- "countryName = match\n"
- "localityName = match\n"
- "emailAddress = supplied\n"
- "\n"
-
- "[user_cert]\n"
- "basicConstraints = CA:false\n"
- "keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
- "subjectKeyIdentifier = hash\n"
- "authorityKeyIdentifier = keyid,issuer:always\n"
- "subjectAltName = email:copy\n"
- "issuerAltName = issuer:copy\n"
- "\n"
-
- "[ca_cert]\n"
- "basicConstraints = critical,CA:true\n"
- "keyUsage = cRLSign, keyCertSign\n"
- "subjectKeyIdentifier = hash\n"
- "authorityKeyIdentifier = keyid:always,issuer:always\n"
- "subjectAltName = email:copy\n"
- "issuerAltName = issuer:copy\n"].
-
+ file:delete(filename:join(CaPath, "cert_key.pem")),
+ file:delete(filename:join(IPath, "cert_key.pem")),
+ file:rename(filename:join(CPath, "cert_key.pem"), filename:join(CPath, "key.pem")),
+ file:rename(filename:join(SPath, "cert_key.pem"), filename:join(SPath, "key.pem")),
+ ok.
diff --git a/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile
index 46c0507b3a..ae5881d49b 100644
--- a/lib/ssl/examples/src/Makefile
+++ b/lib/ssl/examples/src/Makefile
@@ -66,7 +66,7 @@ release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/examples/src
$(INSTALL_DIR) $(RELSYSDIR)/examples/ebin
(cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -))
- chmod -f -R ug+w $(RELSYSDIR)/examples
+ chmod -R ug+w $(RELSYSDIR)/examples
release_docs_spec:
diff --git a/lib/ssl/pkix/Makefile b/lib/ssl/pkix/Makefile
deleted file mode 100644
index 260361c025..0000000000
--- a/lib/ssl/pkix/Makefile
+++ /dev/null
@@ -1,121 +0,0 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2003-2009. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-#
-
-include $(ERL_TOP)/make/target.mk
-include $(ERL_TOP)/make/$(TARGET)/otp.mk
-
-# ----------------------------------------------------
-# Application version
-# ----------------------------------------------------
-include ../vsn.mk
-VSN=$(SSL_VSN)
-
-# ----------------------------------------------------
-# Release directory specification
-# ----------------------------------------------------
-RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN)
-
-# ----------------------------------------------------
-# Common Macros
-# ----------------------------------------------------
-
-.SUFFIXES: .asn1
-.PRECIOUS: %.erl
-
-ASN_TOP = OTP-PKIX
-ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \
- PKIXAttributeCertificate SSL-PKIX
-ASN_ASNS = $(ASN_MODULES:%=%.asn1)
-ASN_ERLS = $(ASN_TOP).erl
-ASN_HRLS = $(ASN_TOP).hrl
-ASN_CONFIGS = OTP-PKIX.asn1config
-ASN_DBS = $(ASN_MODULES:%=%.asn1db)
-ASN_TABLES = $(ASN_MODULES:%=%.table)
-
-GEN_MODULES = ssl_pkix_oid $(ORBER_TMP_FIX_ERL)
-GEN_ERLS = $(GEN_MODULES:%=%.erl)
-ERL_MODULES = $(ASN_TOP) $(GEN_MODULES)
-
-TARGET_FILES= $(ERL_MODULES:%=$(EBIN)/%.$(EMULATOR))
-
-HRL_FILES = $(ASN_HRLS:%=$(INCLUDE)/%)
-
-ORBER_TMP_FIX_HRL = PKIX1Algorithms88.hrl PKIX1Explicit88.hrl \
- PKIX1Implicit88.hrl PKIXAttributeCertificate.hrl
-
-INCLUDE = ../include
-EBIN = ../ebin
-
-# ----------------------------------------------------
-# FLAGS
-# ----------------------------------------------------
-EXTRA_ERLC_FLAGS =
-ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS)
-
-ASN_FLAGS = -bber_bin +der +compact_bit_string +optimize +noobj +asn1config +inline
-
-# ----------------------------------------------------
-# Targets
-# ----------------------------------------------------
-
-debug opt: $(TARGET_FILES) $(HRL_FILES)
-
-clean:
- -rm -f $(ASN_ERLS) $(GEN_ERLS) $(ASN_HRLS) $(HRL_FILES) $(ASN_DBS) \
- $(ASN_TABLES) $(TARGET_FILES) *.beam *~
-
-docs:
-
-%.erl: %.set.asn
- erlc $(ASN_FLAGS) $<
-
-ssl_pkix_oid.erl: mk_ssl_pkix_oid.beam $(EBIN)/OTP-PKIX.beam
- erl -pa $(EBIN) -noshell -s mk_ssl_pkix_oid make -s erlang halt
-
-$(HRL_FILES): $(ASN_HRLS)
- cp -p $(ASN_HRLS) $(INCLUDE)
-
-# ----------------------------------------------------
-# Release Target
-# ----------------------------------------------------
-include $(ERL_TOP)/make/otp_release_targets.mk
-
-release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/include
- $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include
- $(INSTALL_DIR) $(RELSYSDIR)/pkix
- $(INSTALL_DATA) $(ASN_ASNS) $(ASN_ERLS) $(ASN_HRLS) $(ASN_CONFIGS) \
- $(ORBER_TMP_FIX_HRL) $(GEN_ERLS) mk_ssl_pkix_oid.erl $(RELSYSDIR)/pkix
- $(INSTALL_DIR) $(RELSYSDIR)/ebin
- $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
-
-release_docs_spec:
-
-#
-# Dependencies
-
-$(EBIN)/OTP-PKIX.beam: OTP-PKIX.erl OTP-PKIX.hrl
-OTP-PKIX.erl OTP-PKIX.hrl: OTP-PKIX.asn1db
-OTP-PKIX.asn1db: PKIX1Algorithms88.asn1 \
- PKIX1Explicit88.asn1 \
- PKIX1Implicit88.asn1 \
- PKIXAttributeCertificate.asn1 \
- SSL-PKIX.asn1
diff --git a/lib/ssl/pkix/OTP-PKIX.asn1config b/lib/ssl/pkix/OTP-PKIX.asn1config
deleted file mode 100644
index 0caa158f52..0000000000
--- a/lib/ssl/pkix/OTP-PKIX.asn1config
+++ /dev/null
@@ -1,2 +0,0 @@
-{exclusive_decode,{'OTP-PKIX',
- [{decode_TBSCert_exclusive,['Certificate',[{tbsCertificate,undecoded}]]}]}}.
diff --git a/lib/ssl/pkix/OTP-PKIX.set.asn b/lib/ssl/pkix/OTP-PKIX.set.asn
deleted file mode 100644
index 1c3483d519..0000000000
--- a/lib/ssl/pkix/OTP-PKIX.set.asn
+++ /dev/null
@@ -1,6 +0,0 @@
-SSL-PKIX.asn1
-PKIX1Explicit88.asn1
-PKIX1Implicit88.asn1
-PKIXAttributeCertificate.asn1
-PKIX1Algorithms88.asn1
-PKCS-1.asn1
diff --git a/lib/ssl/pkix/PKCS-1.asn1 b/lib/ssl/pkix/PKCS-1.asn1
deleted file mode 100755
index 547cc2e072..0000000000
--- a/lib/ssl/pkix/PKCS-1.asn1
+++ /dev/null
@@ -1,54 +0,0 @@
-PKCS-1 {
- iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)
- modules(0) pkcs-1(1)
-}
-
-
-DEFINITIONS IMPLICIT TAGS ::= BEGIN
-
--- EXPORTS ALL --
-
-IMPORTS
- AlgorithmIdentifier
- FROM PKIX1Explicit88 {iso(1) identified-organization(3)
- dod(6) internet(1) security(5) mechanisms(5)
- pkix(7) id-mod(0) id-pkix1-explicit-88(1)} ;
-
-pkcs-1 OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
-
-RSAPrivateKey ::= SEQUENCE {
- version Version,
- modulus INTEGER, -- n
- publicExponent INTEGER, -- e
- privateExponent INTEGER, -- d
- prime1 INTEGER, -- p
- prime2 INTEGER, -- q
- exponent1 INTEGER, -- d mod (p-1)
- exponent2 INTEGER, -- d mod (q-1)
- coefficient INTEGER, -- (inverse of q) mod p
- otherPrimeInfos OtherPrimeInfos OPTIONAL
-}
-
-Version ::= INTEGER { two-prime(0), multi(1) }
- (CONSTRAINED BY {
- -- version must be multi if otherPrimeInfos present --
- })
-
-OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
-
-OtherPrimeInfo ::= SEQUENCE {
- prime INTEGER, -- ri
- exponent INTEGER, -- di
- coefficient INTEGER -- ti
-}
-
-DigestInfo ::= SEQUENCE {
- digestAlgorithm DigestAlgorithmIdentifier,
- digest OCTET STRING
-}
-
-DigestAlgorithmIdentifier ::= AlgorithmIdentifier
-
-END -- PKCS1Definitions
-
diff --git a/lib/ssl/pkix/PKIX1Algorithms88.asn1 b/lib/ssl/pkix/PKIX1Algorithms88.asn1
deleted file mode 100644
index e78de69b0e..0000000000
--- a/lib/ssl/pkix/PKIX1Algorithms88.asn1
+++ /dev/null
@@ -1,274 +0,0 @@
- PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6)
- internet(1) security(5) mechanisms(5) pkix(7) id-mod(0)
- id-mod-pkix1-algorithms(17) }
-
- DEFINITIONS EXPLICIT TAGS ::= BEGIN
-
- -- EXPORTS All;
-
- -- IMPORTS NONE;
-
- --
- -- One-way Hash Functions
- --
-
- md2 OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) rsadsi(113549)
- digestAlgorithm(2) 2 }
-
- md5 OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) rsadsi(113549)
- digestAlgorithm(2) 5 }
-
- id-sha1 OBJECT IDENTIFIER ::= {
- iso(1) identified-organization(3) oiw(14) secsig(3)
- algorithms(2) 26 }
-
- --
- -- DSA Keys and Signatures
- --
-
- -- OID for DSA public key
-
- id-dsa OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 }
-
- -- encoding for DSA public key
-
- DSAPublicKey ::= INTEGER -- public key, y
-
- Dss-Parms ::= SEQUENCE {
- p INTEGER,
- q INTEGER,
- g INTEGER }
-
- -- OID for DSA signature generated with SHA-1 hash
-
- id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 }
-
- -- encoding for DSA signature generated with SHA-1 hash
-
- Dss-Sig-Value ::= SEQUENCE {
- r INTEGER,
- s INTEGER }
-
- --
- -- RSA Keys and Signatures
- --
-
- -- arc for RSA public key and RSA signature OIDs
-
- pkcs-1 OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
-
- -- OID for RSA public keys
-
- rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 }
-
- -- OID for RSA signature generated with MD2 hash
-
- md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
-
- -- OID for RSA signature generated with MD5 hash
-
- md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
-
- -- OID for RSA signature generated with SHA-1 hash
-
- sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
-
- -- encoding for RSA public key
-
- RSAPublicKey ::= SEQUENCE {
- modulus INTEGER, -- n
- publicExponent INTEGER } -- e
-
- --
- -- Diffie-Hellman Keys
- --
-
- dhpublicnumber OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) ansi-x942(10046)
- number-type(2) 1 }
-
- -- encoding for DSA public key
-
- DHPublicKey ::= INTEGER -- public key, y = g^x mod p
-
- DomainParameters ::= SEQUENCE {
- p INTEGER, -- odd prime, p=jq +1
- g INTEGER, -- generator, g
- q INTEGER, -- factor of p-1
- j INTEGER OPTIONAL, -- subgroup factor, j>= 2
- validationParms ValidationParms OPTIONAL }
-
- ValidationParms ::= SEQUENCE {
- seed BIT STRING,
- pgenCounter INTEGER }
-
- --
- -- KEA Keys
- --
-
- id-keyExchangeAlgorithm OBJECT IDENTIFIER ::=
- { 2 16 840 1 101 2 1 1 22 }
-
- KEA-Parms-Id ::= OCTET STRING
-
- --
- -- Elliptic Curve Keys, Signatures, and Curves
- --
-
- ansi-X9-62 OBJECT IDENTIFIER ::= {
- iso(1) member-body(2) us(840) 10045 }
-
- FieldID ::= SEQUENCE { -- Finite field
- fieldType OBJECT IDENTIFIER,
- parameters ANY DEFINED BY fieldType }
-
- -- Arc for ECDSA signature OIDS
-
- id-ecSigType OBJECT IDENTIFIER ::= { ansi-X9-62 signatures(4) }
-
- -- OID for ECDSA signatures with SHA-1
-
- ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { id-ecSigType 1 }
-
- -- OID for an elliptic curve signature
- -- format for the value of an ECDSA signature value
-
- ECDSA-Sig-Value ::= SEQUENCE {
- r INTEGER,
- s INTEGER }
-
- -- recognized field type OIDs are defined in the following arc
-
- id-fieldType OBJECT IDENTIFIER ::= { ansi-X9-62 fieldType(1) }
-
- -- where fieldType is prime-field, the parameters are of type Prime-p
-
- prime-field OBJECT IDENTIFIER ::= { id-fieldType 1 }
-
- Prime-p ::= INTEGER -- Finite field F(p), where p is an odd prime
-
- -- where fieldType is characteristic-two-field, the parameters are
- -- of type Characteristic-two
-
- characteristic-two-field OBJECT IDENTIFIER ::= { id-fieldType 2 }
-
- Characteristic-two ::= SEQUENCE {
- m INTEGER, -- Field size 2^m
- basis OBJECT IDENTIFIER,
- parameters ANY DEFINED BY basis }
-
- -- recognized basis type OIDs are defined in the following arc
-
- id-characteristic-two-basis OBJECT IDENTIFIER ::= {
- characteristic-two-field basisType(3) }
-
- -- gnbasis is identified by OID gnBasis and indicates
- -- parameters are NULL
-
- gnBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 1 }
-
- -- parameters for this basis are NULL
-
- -- trinomial basis is identified by OID tpBasis and indicates
- -- parameters of type Pentanomial
-
- tpBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 2 }
-
- -- Trinomial basis representation of F2^m
- -- Integer k for reduction polynomial xm + xk + 1
-
- Trinomial ::= INTEGER
-
- -- for pentanomial basis is identified by OID ppBasis and indicates
- -- parameters of type Pentanomial
-
- ppBasis OBJECT IDENTIFIER ::= { id-characteristic-two-basis 3 }
-
- -- Pentanomial basis representation of F2^m
- -- reduction polynomial integers k1, k2, k3
- -- f(x) = x**m + x**k3 + x**k2 + x**k1 + 1
-
- Pentanomial ::= SEQUENCE {
- k1 INTEGER,
- k2 INTEGER,
- k3 INTEGER }
-
- -- The object identifiers gnBasis, tpBasis and ppBasis name
- -- three kinds of basis for characteristic-two finite fields
-
- FieldElement ::= OCTET STRING -- Finite field element
-
- ECPoint ::= OCTET STRING -- Elliptic curve point
-
- -- Elliptic Curve parameters may be specified explicitly,
- -- specified implicitly through a "named curve", or
- -- inherited from the CA
-
- EcpkParameters ::= CHOICE {
- ecParameters ECParameters,
- namedCurve OBJECT IDENTIFIER,
- implicitlyCA NULL }
-
- ECParameters ::= SEQUENCE { -- Elliptic curve parameters
- version ECPVer,
- fieldID FieldID,
- curve Curve,
- base ECPoint, -- Base point G
- order INTEGER, -- Order n of the base point
- cofactor INTEGER OPTIONAL } -- The integer h = #E(Fq)/n
-
- ECPVer ::= INTEGER {ecpVer1(1)}
-
- Curve ::= SEQUENCE {
- a FieldElement, -- Elliptic curve coefficient a
- b FieldElement, -- Elliptic curve coefficient b
- seed BIT STRING OPTIONAL }
-
- id-publicKeyType OBJECT IDENTIFIER ::= { ansi-X9-62 keyType(2) }
-
- id-ecPublicKey OBJECT IDENTIFIER ::= { id-publicKeyType 1 }
-
- -- Named Elliptic Curves in ANSI X9.62.
-
- ellipticCurve OBJECT IDENTIFIER ::= { ansi-X9-62 curves(3) }
-
- c-TwoCurve OBJECT IDENTIFIER ::= {
- ellipticCurve characteristicTwo(0) }
-
- c2pnb163v1 OBJECT IDENTIFIER ::= { c-TwoCurve 1 }
- c2pnb163v2 OBJECT IDENTIFIER ::= { c-TwoCurve 2 }
- c2pnb163v3 OBJECT IDENTIFIER ::= { c-TwoCurve 3 }
- c2pnb176w1 OBJECT IDENTIFIER ::= { c-TwoCurve 4 }
- c2tnb191v1 OBJECT IDENTIFIER ::= { c-TwoCurve 5 }
- c2tnb191v2 OBJECT IDENTIFIER ::= { c-TwoCurve 6 }
- c2tnb191v3 OBJECT IDENTIFIER ::= { c-TwoCurve 7 }
- c2onb191v4 OBJECT IDENTIFIER ::= { c-TwoCurve 8 }
- c2onb191v5 OBJECT IDENTIFIER ::= { c-TwoCurve 9 }
- c2pnb208w1 OBJECT IDENTIFIER ::= { c-TwoCurve 10 }
- c2tnb239v1 OBJECT IDENTIFIER ::= { c-TwoCurve 11 }
- c2tnb239v2 OBJECT IDENTIFIER ::= { c-TwoCurve 12 }
- c2tnb239v3 OBJECT IDENTIFIER ::= { c-TwoCurve 13 }
- c2onb239v4 OBJECT IDENTIFIER ::= { c-TwoCurve 14 }
- c2onb239v5 OBJECT IDENTIFIER ::= { c-TwoCurve 15 }
- c2pnb272w1 OBJECT IDENTIFIER ::= { c-TwoCurve 16 }
- c2pnb304w1 OBJECT IDENTIFIER ::= { c-TwoCurve 17 }
- c2tnb359v1 OBJECT IDENTIFIER ::= { c-TwoCurve 18 }
- c2pnb368w1 OBJECT IDENTIFIER ::= { c-TwoCurve 19 }
- c2tnb431r1 OBJECT IDENTIFIER ::= { c-TwoCurve 20 }
-
- primeCurve OBJECT IDENTIFIER ::= { ellipticCurve prime(1) }
-
- prime192v1 OBJECT IDENTIFIER ::= { primeCurve 1 }
- prime192v2 OBJECT IDENTIFIER ::= { primeCurve 2 }
- prime192v3 OBJECT IDENTIFIER ::= { primeCurve 3 }
- prime239v1 OBJECT IDENTIFIER ::= { primeCurve 4 }
- prime239v2 OBJECT IDENTIFIER ::= { primeCurve 5 }
- prime239v3 OBJECT IDENTIFIER ::= { primeCurve 6 }
- prime256v1 OBJECT IDENTIFIER ::= { primeCurve 7 }
-
- END
diff --git a/lib/ssl/pkix/PKIX1Algorithms88.hrl b/lib/ssl/pkix/PKIX1Algorithms88.hrl
deleted file mode 100644
index a11793618d..0000000000
--- a/lib/ssl/pkix/PKIX1Algorithms88.hrl
+++ /dev/null
@@ -1,94 +0,0 @@
-%% Generated by the Erlang ASN.1 compiler version:1.4.4.8
-%% Purpose: Erlang record definitions for each named and unnamed
-%% SEQUENCE and SET, and macro definitions for each value
-%% definition,in module PKIX1Algorithms88
-
-
-
--record('Dss-Parms',{
-p, q, g}).
-
--record('Dss-Sig-Value',{
-r, s}).
-
--record('RSAPublicKey',{
-modulus, publicExponent}).
-
--record('DomainParameters',{
-p, g, q, j = asn1_NOVALUE, validationParms = asn1_NOVALUE}).
-
--record('ValidationParms',{
-seed, pgenCounter}).
-
--record('FieldID',{
-fieldType, parameters}).
-
--record('ECDSA-Sig-Value',{
-r, s}).
-
--record('Characteristic-two',{
-m, basis, parameters}).
-
--record('Pentanomial',{
-k1, k2, k3}).
-
--record('ECParameters',{
-version, fieldID, curve, base, order, cofactor = asn1_NOVALUE}).
-
--record('Curve',{
-a, b, seed = asn1_NOVALUE}).
-
--define('md2', {1,2,840,113549,2,2}).
--define('md5', {1,2,840,113549,2,5}).
--define('id-sha1', {1,3,14,3,2,26}).
--define('id-dsa', {1,2,840,10040,4,1}).
--define('id-dsa-with-sha1', {1,2,840,10040,4,3}).
--define('pkcs-1', {1,2,840,113549,1,1}).
--define('rsaEncryption', {1,2,840,113549,1,1,1}).
--define('md2WithRSAEncryption', {1,2,840,113549,1,1,2}).
--define('md5WithRSAEncryption', {1,2,840,113549,1,1,4}).
--define('sha1WithRSAEncryption', {1,2,840,113549,1,1,5}).
--define('dhpublicnumber', {1,2,840,10046,2,1}).
--define('id-keyExchangeAlgorithm', {2,16,840,1,101,2,1,1,22}).
--define('ansi-X9-62', {1,2,840,10045}).
--define('id-ecSigType', {1,2,840,10045,4}).
--define('ecdsa-with-SHA1', {1,2,840,10045,4,1}).
--define('id-fieldType', {1,2,840,10045,1}).
--define('prime-field', {1,2,840,10045,1,1}).
--define('characteristic-two-field', {1,2,840,10045,1,2}).
--define('id-characteristic-two-basis', {1,2,840,10045,1,2,3}).
--define('gnBasis', {1,2,840,10045,1,2,3,1}).
--define('tpBasis', {1,2,840,10045,1,2,3,2}).
--define('ppBasis', {1,2,840,10045,1,2,3,3}).
--define('id-publicKeyType', {1,2,840,10045,2}).
--define('id-ecPublicKey', {1,2,840,10045,2,1}).
--define('ellipticCurve', {1,2,840,10045,3}).
--define('c-TwoCurve', {1,2,840,10045,3,0}).
--define('c2pnb163v1', {1,2,840,10045,3,0,1}).
--define('c2pnb163v2', {1,2,840,10045,3,0,2}).
--define('c2pnb163v3', {1,2,840,10045,3,0,3}).
--define('c2pnb176w1', {1,2,840,10045,3,0,4}).
--define('c2tnb191v1', {1,2,840,10045,3,0,5}).
--define('c2tnb191v2', {1,2,840,10045,3,0,6}).
--define('c2tnb191v3', {1,2,840,10045,3,0,7}).
--define('c2onb191v4', {1,2,840,10045,3,0,8}).
--define('c2onb191v5', {1,2,840,10045,3,0,9}).
--define('c2pnb208w1', {1,2,840,10045,3,0,10}).
--define('c2tnb239v1', {1,2,840,10045,3,0,11}).
--define('c2tnb239v2', {1,2,840,10045,3,0,12}).
--define('c2tnb239v3', {1,2,840,10045,3,0,13}).
--define('c2onb239v4', {1,2,840,10045,3,0,14}).
--define('c2onb239v5', {1,2,840,10045,3,0,15}).
--define('c2pnb272w1', {1,2,840,10045,3,0,16}).
--define('c2pnb304w1', {1,2,840,10045,3,0,17}).
--define('c2tnb359v1', {1,2,840,10045,3,0,18}).
--define('c2pnb368w1', {1,2,840,10045,3,0,19}).
--define('c2tnb431r1', {1,2,840,10045,3,0,20}).
--define('primeCurve', {1,2,840,10045,3,1}).
--define('prime192v1', {1,2,840,10045,3,1,1}).
--define('prime192v2', {1,2,840,10045,3,1,2}).
--define('prime192v3', {1,2,840,10045,3,1,3}).
--define('prime239v1', {1,2,840,10045,3,1,4}).
--define('prime239v2', {1,2,840,10045,3,1,5}).
--define('prime239v3', {1,2,840,10045,3,1,6}).
--define('prime256v1', {1,2,840,10045,3,1,7}).
diff --git a/lib/ssl/pkix/PKIX1Explicit88.asn1 b/lib/ssl/pkix/PKIX1Explicit88.asn1
deleted file mode 100644
index 9b8068fed0..0000000000
--- a/lib/ssl/pkix/PKIX1Explicit88.asn1
+++ /dev/null
@@ -1,619 +0,0 @@
-PKIX1Explicit88 { iso(1) identified-organization(3) dod(6) internet(1)
- security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit(18) }
-
-DEFINITIONS EXPLICIT TAGS ::=
-
-BEGIN
-
--- EXPORTS ALL --
-
--- IMPORTS NONE --
-
--- UNIVERSAL Types defined in 1993 and 1998 ASN.1
--- and required by this specification
-
--- UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING
- -- UniversalString is defined in ASN.1:1993
-
--- BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING
- -- BMPString is the subtype of UniversalString and models
- -- the Basic Multilingual Plane of ISO/IEC/ITU 10646-1
-
--- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
- -- The content of this type conforms to RFC 2279.
-
--- PKIX specific OIDs
-
-id-pkix OBJECT IDENTIFIER ::=
- { iso(1) identified-organization(3) dod(6) internet(1)
- security(5) mechanisms(5) pkix(7) }
-
--- PKIX arcs
-
-id-pe OBJECT IDENTIFIER ::= { id-pkix 1 }
- -- arc for private certificate extensions
-id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
- -- arc for policy qualifier types
-id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
- -- arc for extended key purpose OIDS
-id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
- -- arc for access descriptors
-
--- policyQualifierIds for Internet policy qualifiers
-
-id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
- -- OID for CPS qualifier
-id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
- -- OID for user notice qualifier
-
--- access descriptor definitions
-
-id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
-id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
-id-ad-timeStamping OBJECT IDENTIFIER ::= { id-ad 3 }
-id-ad-caRepository OBJECT IDENTIFIER ::= { id-ad 5 }
-
--- attribute data types
-
-Attribute ::= SEQUENCE {
- type AttributeType,
- values SET OF AttributeValue }
- -- at least one value is required
-
-AttributeType ::= OBJECT IDENTIFIER
-
-AttributeValue ::= ANY
-
-AttributeTypeAndValue ::= SEQUENCE {
- type AttributeType,
- value AttributeValue }
-
--- suggested naming attributes: Definition of the following
--- information object set may be augmented to meet local
--- requirements. Note that deleting members of the set may
--- prevent interoperability with conforming implementations.
--- presented in pairs: the AttributeType followed by the
--- type definition for the corresponding AttributeValue
---Arc for standard naming attributes
-id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 }
-
--- Naming attributes of type X520name
-
-id-at-name AttributeType ::= { id-at 41 }
-id-at-surname AttributeType ::= { id-at 4 }
-id-at-givenName AttributeType ::= { id-at 42 }
-id-at-initials AttributeType ::= { id-at 43 }
-id-at-generationQualifier AttributeType ::= { id-at 44 }
-
-X520name ::= CHOICE {
- teletexString TeletexString (SIZE (1..ub-name)),
- printableString PrintableString (SIZE (1..ub-name)),
- universalString UniversalString (SIZE (1..ub-name)),
- utf8String UTF8String (SIZE (1..ub-name)),
- bmpString BMPString (SIZE (1..ub-name)) }
-
--- Naming attributes of type X520CommonName
-
-id-at-commonName AttributeType ::= { id-at 3 }
-
-X520CommonName ::= CHOICE {
- teletexString TeletexString (SIZE (1..ub-common-name)),
- printableString PrintableString (SIZE (1..ub-common-name)),
- universalString UniversalString (SIZE (1..ub-common-name)),
- utf8String UTF8String (SIZE (1..ub-common-name)),
- bmpString BMPString (SIZE (1..ub-common-name)) }
-
--- Naming attributes of type X520LocalityName
-
-id-at-localityName AttributeType ::= { id-at 7 }
-
-X520LocalityName ::= CHOICE {
- teletexString TeletexString (SIZE (1..ub-locality-name)),
- printableString PrintableString (SIZE (1..ub-locality-name)),
- universalString UniversalString (SIZE (1..ub-locality-name)),
- utf8String UTF8String (SIZE (1..ub-locality-name)),
- bmpString BMPString (SIZE (1..ub-locality-name)) }
-
--- Naming attributes of type X520StateOrProvinceName
-
-id-at-stateOrProvinceName AttributeType ::= { id-at 8 }
-
-X520StateOrProvinceName ::= CHOICE {
- teletexString TeletexString (SIZE (1..ub-state-name)),
- printableString PrintableString (SIZE (1..ub-state-name)),
- universalString UniversalString (SIZE (1..ub-state-name)),
- utf8String UTF8String (SIZE (1..ub-state-name)),
- bmpString BMPString (SIZE(1..ub-state-name)) }
-
--- Naming attributes of type X520OrganizationName
-
-id-at-organizationName AttributeType ::= { id-at 10 }
-
-X520OrganizationName ::= CHOICE {
- teletexString TeletexString
- (SIZE (1..ub-organization-name)),
- printableString PrintableString
- (SIZE (1..ub-organization-name)),
- universalString UniversalString
- (SIZE (1..ub-organization-name)),
- utf8String UTF8String
- (SIZE (1..ub-organization-name)),
- bmpString BMPString
- (SIZE (1..ub-organization-name)) }
-
--- Naming attributes of type X520OrganizationalUnitName
-
-id-at-organizationalUnitName AttributeType ::= { id-at 11 }
-
-X520OrganizationalUnitName ::= CHOICE {
- teletexString TeletexString
- (SIZE (1..ub-organizational-unit-name)),
- printableString PrintableString
- (SIZE (1..ub-organizational-unit-name)),
- universalString UniversalString
- (SIZE (1..ub-organizational-unit-name)),
- utf8String UTF8String
- (SIZE (1..ub-organizational-unit-name)),
- bmpString BMPString
- (SIZE (1..ub-organizational-unit-name)) }
-
--- Naming attributes of type X520Title
-
-id-at-title AttributeType ::= { id-at 12 }
-
-X520Title ::= CHOICE {
- teletexString TeletexString (SIZE (1..ub-title)),
- printableString PrintableString (SIZE (1..ub-title)),
- universalString UniversalString (SIZE (1..ub-title)),
- utf8String UTF8String (SIZE (1..ub-title)),
- bmpString BMPString (SIZE (1..ub-title)) }
-
--- Naming attributes of type X520dnQualifier
-
-id-at-dnQualifier AttributeType ::= { id-at 46 }
-
-X520dnQualifier ::= PrintableString
-
--- Naming attributes of type X520countryName (digraph from IS 3166)
-
-id-at-countryName AttributeType ::= { id-at 6 }
-
-X520countryName ::= PrintableString (SIZE (2))
-
--- Naming attributes of type X520SerialNumber
-
-id-at-serialNumber AttributeType ::= { id-at 5 }
-
-X520SerialNumber ::= PrintableString (SIZE (1..ub-serial-number))
-
--- Naming attributes of type X520Pseudonym
-
-id-at-pseudonym AttributeType ::= { id-at 65 }
-
-X520Pseudonym ::= CHOICE {
- teletexString TeletexString (SIZE (1..ub-pseudonym)),
- printableString PrintableString (SIZE (1..ub-pseudonym)),
- universalString UniversalString (SIZE (1..ub-pseudonym)),
- utf8String UTF8String (SIZE (1..ub-pseudonym)),
- bmpString BMPString (SIZE (1..ub-pseudonym)) }
-
--- Naming attributes of type DomainComponent (from RFC 2247)
-
-id-domainComponent AttributeType ::=
- { 0 9 2342 19200300 100 1 25 }
-
-DomainComponent ::= IA5String
-
--- Legacy attributes
-
-pkcs-9 OBJECT IDENTIFIER ::=
- { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
-
-id-emailAddress AttributeType ::= { pkcs-9 1 }
-
-EmailAddress ::= IA5String (SIZE (1..ub-emailaddress-length))
-
--- naming data types --
-
-Name ::= CHOICE { -- only one possibility for now --
- rdnSequence RDNSequence }
-
-RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
-
-DistinguishedName ::= RDNSequence
-
-RelativeDistinguishedName ::=
- SET SIZE (1 .. MAX) OF AttributeTypeAndValue
-
--- Directory string type --
-
-DirectoryString ::= CHOICE {
- teletexString TeletexString (SIZE (1..MAX)),
- printableString PrintableString (SIZE (1..MAX)),
- universalString UniversalString (SIZE (1..MAX)),
- utf8String UTF8String (SIZE (1..MAX)),
- bmpString BMPString (SIZE (1..MAX)) }
-
--- certificate and CRL specific structures begin here
-
-Certificate ::= SEQUENCE {
- tbsCertificate TBSCertificate,
- signatureAlgorithm AlgorithmIdentifier,
- signature BIT STRING }
-
-TBSCertificate ::= SEQUENCE {
- version [0] Version DEFAULT v1,
- serialNumber CertificateSerialNumber,
- signature AlgorithmIdentifier,
- issuer Name,
- validity Validity,
- subject Name,
- subjectPublicKeyInfo SubjectPublicKeyInfo,
- issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version MUST be v2 or v3
- subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version MUST be v2 or v3
- extensions [3] Extensions OPTIONAL
- -- If present, version MUST be v3 -- }
-
-Version ::= INTEGER { v1(0), v2(1), v3(2) }
-
-CertificateSerialNumber ::= INTEGER
-
-Validity ::= SEQUENCE {
- notBefore Time,
- notAfter Time }
-
-Time ::= CHOICE {
- utcTime UTCTime,
- generalTime GeneralizedTime }
-
-UniqueIdentifier ::= BIT STRING
-
-SubjectPublicKeyInfo ::= SEQUENCE {
- algorithm AlgorithmIdentifier,
- subjectPublicKey BIT STRING }
-
-Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
-
-Extension ::= SEQUENCE {
- extnID OBJECT IDENTIFIER,
- critical BOOLEAN DEFAULT FALSE,
- extnValue OCTET STRING }
-
--- CRL structures
-
-CertificateList ::= SEQUENCE {
- tbsCertList TBSCertList,
- signatureAlgorithm AlgorithmIdentifier,
- signature BIT STRING }
-
-TBSCertList ::= SEQUENCE {
- version Version OPTIONAL,
- -- if present, MUST be v2
- signature AlgorithmIdentifier,
- issuer Name,
- thisUpdate Time,
- nextUpdate Time OPTIONAL,
- revokedCertificates SEQUENCE OF SEQUENCE {
- userCertificate CertificateSerialNumber,
- revocationDate Time,
- crlEntryExtensions Extensions OPTIONAL
- -- if present, MUST be v2
- } OPTIONAL,
- crlExtensions [0] Extensions OPTIONAL }
- -- if present, MUST be v2
-
--- Version, Time, CertificateSerialNumber, and Extensions were
--- defined earlier for use in the certificate structure
-
-AlgorithmIdentifier ::= SEQUENCE {
- algorithm OBJECT IDENTIFIER,
- parameters ANY DEFINED BY algorithm OPTIONAL }
- -- contains a value of the type
- -- registered for use with the
- -- algorithm object identifier value
-
--- X.400 address syntax starts here
-
-ORAddress ::= SEQUENCE {
- built-in-standard-attributes BuiltInStandardAttributes,
- built-in-domain-defined-attributes
- BuiltInDomainDefinedAttributes OPTIONAL,
- -- see also teletex-domain-defined-attributes
- extension-attributes ExtensionAttributes OPTIONAL }
-
--- Built-in Standard Attributes
-
-BuiltInStandardAttributes ::= SEQUENCE {
- country-name CountryName OPTIONAL,
- administration-domain-name AdministrationDomainName OPTIONAL,
- network-address [0] IMPLICIT NetworkAddress OPTIONAL,
- -- see also extended-network-address
- terminal-identifier [1] IMPLICIT TerminalIdentifier OPTIONAL,
- private-domain-name [2] PrivateDomainName OPTIONAL,
- organization-name [3] IMPLICIT OrganizationName OPTIONAL,
- -- see also teletex-organization-name
- numeric-user-identifier [4] IMPLICIT NumericUserIdentifier
- OPTIONAL,
- personal-name [5] IMPLICIT PersonalName OPTIONAL,
- -- see also teletex-personal-name
- organizational-unit-names [6] IMPLICIT OrganizationalUnitNames
- OPTIONAL }
- -- see also teletex-organizational-unit-names
-
-CountryName ::= [APPLICATION 1] CHOICE {
- x121-dcc-code NumericString
- (SIZE (ub-country-name-numeric-length)),
- iso-3166-alpha2-code PrintableString
- (SIZE (ub-country-name-alpha-length)) }
-
-AdministrationDomainName ::= [APPLICATION 2] CHOICE {
- numeric NumericString (SIZE (0..ub-domain-name-length)),
- printable PrintableString (SIZE (0..ub-domain-name-length)) }
-
-NetworkAddress ::= X121Address -- see also extended-network-address
-
-X121Address ::= NumericString (SIZE (1..ub-x121-address-length))
-
-TerminalIdentifier ::= PrintableString (SIZE
-(1..ub-terminal-id-length))
-
-PrivateDomainName ::= CHOICE {
- numeric NumericString (SIZE (1..ub-domain-name-length)),
- printable PrintableString (SIZE (1..ub-domain-name-length)) }
-
-OrganizationName ::= PrintableString
- (SIZE (1..ub-organization-name-length))
- -- see also teletex-organization-name
-
-NumericUserIdentifier ::= NumericString
- (SIZE (1..ub-numeric-user-id-length))
-
-PersonalName ::= SET {
- surname [0] IMPLICIT PrintableString
- (SIZE (1..ub-surname-length)),
- given-name [1] IMPLICIT PrintableString
- (SIZE (1..ub-given-name-length)) OPTIONAL,
- initials [2] IMPLICIT PrintableString
- (SIZE (1..ub-initials-length)) OPTIONAL,
- generation-qualifier [3] IMPLICIT PrintableString
- (SIZE (1..ub-generation-qualifier-length))
- OPTIONAL }
- -- see also teletex-personal-name
-
-OrganizationalUnitNames ::= SEQUENCE SIZE (1..ub-organizational-units)
- OF OrganizationalUnitName
- -- see also teletex-organizational-unit-names
-
-OrganizationalUnitName ::= PrintableString (SIZE
- (1..ub-organizational-unit-name-length))
-
--- Built-in Domain-defined Attributes
-
-BuiltInDomainDefinedAttributes ::= SEQUENCE SIZE
- (1..ub-domain-defined-attributes) OF
- BuiltInDomainDefinedAttribute
-
-BuiltInDomainDefinedAttribute ::= SEQUENCE {
- type PrintableString (SIZE
- (1..ub-domain-defined-attribute-type-length)),
- value PrintableString (SIZE
- (1..ub-domain-defined-attribute-value-length)) }
-
--- Extension Attributes
-
-ExtensionAttributes ::= SET SIZE (1..ub-extension-attributes) OF
- ExtensionAttribute
-
-ExtensionAttribute ::= SEQUENCE {
- extension-attribute-type [0] IMPLICIT INTEGER
- (0..ub-extension-attributes),
- extension-attribute-value [1]
- ANY DEFINED BY extension-attribute-type }
-
--- Extension types and attribute values
-
-common-name INTEGER ::= 1
-
-CommonName ::= PrintableString (SIZE (1..ub-common-name-length))
-
-teletex-common-name INTEGER ::= 2
-
-TeletexCommonName ::= TeletexString (SIZE (1..ub-common-name-length))
-
-teletex-organization-name INTEGER ::= 3
-
-TeletexOrganizationName ::=
- TeletexString (SIZE (1..ub-organization-name-length))
-
-teletex-personal-name INTEGER ::= 4
-
-TeletexPersonalName ::= SET {
- surname [0] IMPLICIT TeletexString
- (SIZE (1..ub-surname-length)),
- given-name [1] IMPLICIT TeletexString
- (SIZE (1..ub-given-name-length)) OPTIONAL,
- initials [2] IMPLICIT TeletexString
- (SIZE (1..ub-initials-length)) OPTIONAL,
- generation-qualifier [3] IMPLICIT TeletexString
- (SIZE (1..ub-generation-qualifier-length))
- OPTIONAL }
-
-teletex-organizational-unit-names INTEGER ::= 5
-
-TeletexOrganizationalUnitNames ::= SEQUENCE SIZE
- (1..ub-organizational-units) OF TeletexOrganizationalUnitName
-
-TeletexOrganizationalUnitName ::= TeletexString
- (SIZE (1..ub-organizational-unit-name-length))
-
-pds-name INTEGER ::= 7
-
-PDSName ::= PrintableString (SIZE (1..ub-pds-name-length))
-
-physical-delivery-country-name INTEGER ::= 8
-
-PhysicalDeliveryCountryName ::= CHOICE {
- x121-dcc-code NumericString (SIZE
-(ub-country-name-numeric-length)),
- iso-3166-alpha2-code PrintableString
- (SIZE (ub-country-name-alpha-length)) }
-
-postal-code INTEGER ::= 9
-
-PostalCode ::= CHOICE {
- numeric-code NumericString (SIZE (1..ub-postal-code-length)),
- printable-code PrintableString (SIZE (1..ub-postal-code-length)) }
-
-physical-delivery-office-name INTEGER ::= 10
-
-PhysicalDeliveryOfficeName ::= PDSParameter
-
-physical-delivery-office-number INTEGER ::= 11
-
-PhysicalDeliveryOfficeNumber ::= PDSParameter
-
-extension-OR-address-components INTEGER ::= 12
-
-ExtensionORAddressComponents ::= PDSParameter
-
-physical-delivery-personal-name INTEGER ::= 13
-
-PhysicalDeliveryPersonalName ::= PDSParameter
-
-physical-delivery-organization-name INTEGER ::= 14
-
-PhysicalDeliveryOrganizationName ::= PDSParameter
-
-extension-physical-delivery-address-components INTEGER ::= 15
-
-ExtensionPhysicalDeliveryAddressComponents ::= PDSParameter
-
-unformatted-postal-address INTEGER ::= 16
-
-UnformattedPostalAddress ::= SET {
- printable-address SEQUENCE SIZE (1..ub-pds-physical-address-lines)
- OF PrintableString (SIZE (1..ub-pds-parameter-length))
- OPTIONAL,
- teletex-string TeletexString
- (SIZE (1..ub-unformatted-address-length)) OPTIONAL }
-
-street-address INTEGER ::= 17
-
-StreetAddress ::= PDSParameter
-
-post-office-box-address INTEGER ::= 18
-
-PostOfficeBoxAddress ::= PDSParameter
-
-poste-restante-address INTEGER ::= 19
-
-PosteRestanteAddress ::= PDSParameter
-
-unique-postal-name INTEGER ::= 20
-
-UniquePostalName ::= PDSParameter
-
-local-postal-attributes INTEGER ::= 21
-
-LocalPostalAttributes ::= PDSParameter
-
-PDSParameter ::= SET {
- printable-string PrintableString
- (SIZE(1..ub-pds-parameter-length)) OPTIONAL,
- teletex-string TeletexString
- (SIZE(1..ub-pds-parameter-length)) OPTIONAL }
-
-extended-network-address INTEGER ::= 22
-
-ExtendedNetworkAddress ::= CHOICE {
- e163-4-address SEQUENCE {
- number [0] IMPLICIT NumericString
- (SIZE (1..ub-e163-4-number-length)),
- sub-address [1] IMPLICIT NumericString
- (SIZE (1..ub-e163-4-sub-address-length))
- OPTIONAL },
- psap-address [0] IMPLICIT PresentationAddress }
-
-PresentationAddress ::= SEQUENCE {
- pSelector [0] EXPLICIT OCTET STRING OPTIONAL,
- sSelector [1] EXPLICIT OCTET STRING OPTIONAL,
- tSelector [2] EXPLICIT OCTET STRING OPTIONAL,
- nAddresses [3] EXPLICIT SET SIZE (1..MAX) OF OCTET STRING }
-
-terminal-type INTEGER ::= 23
-
-TerminalType ::= INTEGER {
- telex (3),
- teletex (4),
- g3-facsimile (5),
- g4-facsimile (6),
- ia5-terminal (7),
- videotex (8) } (0..ub-integer-options)
-
--- Extension Domain-defined Attributes
-
-teletex-domain-defined-attributes INTEGER ::= 6
-
-TeletexDomainDefinedAttributes ::= SEQUENCE SIZE
- (1..ub-domain-defined-attributes) OF TeletexDomainDefinedAttribute
-
-TeletexDomainDefinedAttribute ::= SEQUENCE {
- type TeletexString
- (SIZE (1..ub-domain-defined-attribute-type-length)),
- value TeletexString
- (SIZE (1..ub-domain-defined-attribute-value-length)) }
-
--- specifications of Upper Bounds MUST be regarded as mandatory
--- from Annex B of ITU-T X.411 Reference Definition of MTS Parameter
--- Upper Bounds
-
--- Upper Bounds
-ub-name INTEGER ::= 32768
-ub-common-name INTEGER ::= 64
-ub-locality-name INTEGER ::= 128
-ub-state-name INTEGER ::= 128
-ub-organization-name INTEGER ::= 64
-ub-organizational-unit-name INTEGER ::= 64
-ub-title INTEGER ::= 64
-ub-serial-number INTEGER ::= 64
-ub-match INTEGER ::= 128
-ub-emailaddress-length INTEGER ::= 128
-ub-common-name-length INTEGER ::= 64
-ub-country-name-alpha-length INTEGER ::= 2
-ub-country-name-numeric-length INTEGER ::= 3
-ub-domain-defined-attributes INTEGER ::= 4
-ub-domain-defined-attribute-type-length INTEGER ::= 8
-ub-domain-defined-attribute-value-length INTEGER ::= 128
-ub-domain-name-length INTEGER ::= 16
-ub-extension-attributes INTEGER ::= 256
-ub-e163-4-number-length INTEGER ::= 15
-ub-e163-4-sub-address-length INTEGER ::= 40
-ub-generation-qualifier-length INTEGER ::= 3
-ub-given-name-length INTEGER ::= 16
-ub-initials-length INTEGER ::= 5
-ub-integer-options INTEGER ::= 256
-ub-numeric-user-id-length INTEGER ::= 32
-ub-organization-name-length INTEGER ::= 64
-ub-organizational-unit-name-length INTEGER ::= 32
-ub-organizational-units INTEGER ::= 4
-ub-pds-name-length INTEGER ::= 16
-ub-pds-parameter-length INTEGER ::= 30
-ub-pds-physical-address-lines INTEGER ::= 6
-ub-postal-code-length INTEGER ::= 16
-ub-pseudonym INTEGER ::= 128
-ub-surname-length INTEGER ::= 40
-ub-terminal-id-length INTEGER ::= 24
-ub-unformatted-address-length INTEGER ::= 180
-ub-x121-address-length INTEGER ::= 16
-
--- Note - upper bounds on string types, such as TeletexString, are
--- measured in characters. Excepting PrintableString or IA5String, a
--- significantly greater number of octets will be required to hold
--- such a value. As a minimum, 16 octets, or twice the specified
--- upper bound, whichever is the larger, should be allowed for
--- TeletexString. For UTF8String or UniversalString at least four
--- times the upper bound should be allowed.
-
-END
diff --git a/lib/ssl/pkix/PKIX1Explicit88.hrl b/lib/ssl/pkix/PKIX1Explicit88.hrl
deleted file mode 100644
index 5940c1e245..0000000000
--- a/lib/ssl/pkix/PKIX1Explicit88.hrl
+++ /dev/null
@@ -1,163 +0,0 @@
-%% Generated by the Erlang ASN.1 compiler version:1.4.4.8
-%% Purpose: Erlang record definitions for each named and unnamed
-%% SEQUENCE and SET, and macro definitions for each value
-%% definition,in module PKIX1Explicit88
-
-
-
--record('Attribute',{
-type, values}).
-
--record('AttributeTypeAndValue',{
-type, value}).
-
--record('Certificate',{
-tbsCertificate, signatureAlgorithm, signature}).
-
--record('TBSCertificate',{
-version = asn1_DEFAULT, serialNumber, signature, issuer, validity, subject, subjectPublicKeyInfo, issuerUniqueID = asn1_NOVALUE, subjectUniqueID = asn1_NOVALUE, extensions = asn1_NOVALUE}).
-
--record('Validity',{
-notBefore, notAfter}).
-
--record('SubjectPublicKeyInfo',{
-algorithm, subjectPublicKey}).
-
--record('Extension',{
-extnID, critical = asn1_DEFAULT, extnValue}).
-
--record('CertificateList',{
-tbsCertList, signatureAlgorithm, signature}).
-
--record('TBSCertList',{
-version = asn1_NOVALUE, signature, issuer, thisUpdate, nextUpdate = asn1_NOVALUE, revokedCertificates = asn1_NOVALUE, crlExtensions = asn1_NOVALUE}).
-
--record('TBSCertList_revokedCertificates_SEQOF',{
-userCertificate, revocationDate, crlEntryExtensions = asn1_NOVALUE}).
-
--record('AlgorithmIdentifier',{
-algorithm, parameters = asn1_NOVALUE}).
-
--record('ORAddress',{
-'built-in-standard-attributes', 'built-in-domain-defined-attributes' = asn1_NOVALUE, 'extension-attributes' = asn1_NOVALUE}).
-
--record('BuiltInStandardAttributes',{
-'country-name' = asn1_NOVALUE, 'administration-domain-name' = asn1_NOVALUE, 'network-address' = asn1_NOVALUE, 'terminal-identifier' = asn1_NOVALUE, 'private-domain-name' = asn1_NOVALUE, 'organization-name' = asn1_NOVALUE, 'numeric-user-identifier' = asn1_NOVALUE, 'personal-name' = asn1_NOVALUE, 'organizational-unit-names' = asn1_NOVALUE}).
-
--record('PersonalName',{
-surname, 'given-name' = asn1_NOVALUE, initials = asn1_NOVALUE, 'generation-qualifier' = asn1_NOVALUE}).
-
--record('BuiltInDomainDefinedAttribute',{
-type, value}).
-
--record('ExtensionAttribute',{
-'extension-attribute-type', 'extension-attribute-value'}).
-
--record('TeletexPersonalName',{
-surname, 'given-name' = asn1_NOVALUE, initials = asn1_NOVALUE, 'generation-qualifier' = asn1_NOVALUE}).
-
--record('UnformattedPostalAddress',{
-'printable-address' = asn1_NOVALUE, 'teletex-string' = asn1_NOVALUE}).
-
--record('PDSParameter',{
-'printable-string' = asn1_NOVALUE, 'teletex-string' = asn1_NOVALUE}).
-
--record('ExtendedNetworkAddress_e163-4-address',{
-number, 'sub-address' = asn1_NOVALUE}).
-
--record('PresentationAddress',{
-pSelector = asn1_NOVALUE, sSelector = asn1_NOVALUE, tSelector = asn1_NOVALUE, nAddresses}).
-
--record('TeletexDomainDefinedAttribute',{
-type, value}).
-
--define('id-pkix', {1,3,6,1,5,5,7}).
--define('id-pe', {1,3,6,1,5,5,7,1}).
--define('id-qt', {1,3,6,1,5,5,7,2}).
--define('id-kp', {1,3,6,1,5,5,7,3}).
--define('id-ad', {1,3,6,1,5,5,7,48}).
--define('id-qt-cps', {1,3,6,1,5,5,7,2,1}).
--define('id-qt-unotice', {1,3,6,1,5,5,7,2,2}).
--define('id-ad-ocsp', {1,3,6,1,5,5,7,48,1}).
--define('id-ad-caIssuers', {1,3,6,1,5,5,7,48,2}).
--define('id-ad-timeStamping', {1,3,6,1,5,5,7,48,3}).
--define('id-ad-caRepository', {1,3,6,1,5,5,7,48,5}).
--define('id-at', {2,5,4}).
--define('id-at-name', {2,5,4,41}).
--define('id-at-surname', {2,5,4,4}).
--define('id-at-givenName', {2,5,4,42}).
--define('id-at-initials', {2,5,4,43}).
--define('id-at-generationQualifier', {2,5,4,44}).
--define('id-at-commonName', {2,5,4,3}).
--define('id-at-localityName', {2,5,4,7}).
--define('id-at-stateOrProvinceName', {2,5,4,8}).
--define('id-at-organizationName', {2,5,4,10}).
--define('id-at-organizationalUnitName', {2,5,4,11}).
--define('id-at-title', {2,5,4,12}).
--define('id-at-dnQualifier', {2,5,4,46}).
--define('id-at-countryName', {2,5,4,6}).
--define('id-at-serialNumber', {2,5,4,5}).
--define('id-at-pseudonym', {2,5,4,65}).
--define('id-domainComponent', {0,9,2342,19200300,100,1,25}).
--define('pkcs-9', {1,2,840,113549,1,9}).
--define('id-emailAddress', {1,2,840,113549,1,9,1}).
--define('common-name', 1).
--define('teletex-common-name', 2).
--define('teletex-organization-name', 3).
--define('teletex-personal-name', 4).
--define('teletex-organizational-unit-names', 5).
--define('pds-name', 7).
--define('physical-delivery-country-name', 8).
--define('postal-code', 9).
--define('physical-delivery-office-name', 10).
--define('physical-delivery-office-number', 11).
--define('extension-OR-address-components', 12).
--define('physical-delivery-personal-name', 13).
--define('physical-delivery-organization-name', 14).
--define('extension-physical-delivery-address-components', 15).
--define('unformatted-postal-address', 16).
--define('street-address', 17).
--define('post-office-box-address', 18).
--define('poste-restante-address', 19).
--define('unique-postal-name', 20).
--define('local-postal-attributes', 21).
--define('extended-network-address', 22).
--define('terminal-type', 23).
--define('teletex-domain-defined-attributes', 6).
--define('ub-name', 32768).
--define('ub-common-name', 64).
--define('ub-locality-name', 128).
--define('ub-state-name', 128).
--define('ub-organization-name', 64).
--define('ub-organizational-unit-name', 64).
--define('ub-title', 64).
--define('ub-serial-number', 64).
--define('ub-match', 128).
--define('ub-emailaddress-length', 128).
--define('ub-common-name-length', 64).
--define('ub-country-name-alpha-length', 2).
--define('ub-country-name-numeric-length', 3).
--define('ub-domain-defined-attributes', 4).
--define('ub-domain-defined-attribute-type-length', 8).
--define('ub-domain-defined-attribute-value-length', 128).
--define('ub-domain-name-length', 16).
--define('ub-extension-attributes', 256).
--define('ub-e163-4-number-length', 15).
--define('ub-e163-4-sub-address-length', 40).
--define('ub-generation-qualifier-length', 3).
--define('ub-given-name-length', 16).
--define('ub-initials-length', 5).
--define('ub-integer-options', 256).
--define('ub-numeric-user-id-length', 32).
--define('ub-organization-name-length', 64).
--define('ub-organizational-unit-name-length', 32).
--define('ub-organizational-units', 4).
--define('ub-pds-name-length', 16).
--define('ub-pds-parameter-length', 30).
--define('ub-pds-physical-address-lines', 6).
--define('ub-postal-code-length', 16).
--define('ub-pseudonym', 128).
--define('ub-surname-length', 40).
--define('ub-terminal-id-length', 24).
--define('ub-unformatted-address-length', 180).
--define('ub-x121-address-length', 16).
diff --git a/lib/ssl/pkix/PKIX1Implicit88.asn1 b/lib/ssl/pkix/PKIX1Implicit88.asn1
deleted file mode 100644
index ced270baf6..0000000000
--- a/lib/ssl/pkix/PKIX1Implicit88.asn1
+++ /dev/null
@@ -1,349 +0,0 @@
-PKIX1Implicit88 { iso(1) identified-organization(3) dod(6) internet(1)
- security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit(19) }
-
-DEFINITIONS IMPLICIT TAGS ::=
-
-BEGIN
-
--- EXPORTS ALL --
-
-IMPORTS
- id-pe, id-kp, id-qt-unotice, id-qt-cps,
- -- delete following line if "new" types are supported --
- -- BMPString,
- -- UTF8String, end "new" types --
- ORAddress, Name, RelativeDistinguishedName,
- CertificateSerialNumber, Attribute, DirectoryString
- FROM PKIX1Explicit88 { iso(1) identified-organization(3)
- dod(6) internet(1) security(5) mechanisms(5) pkix(7)
- id-mod(0) id-pkix1-explicit(18) };
-
-
--- ISO arc for standard certificate and CRL extensions
-
-id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29}
-
--- authority key identifier OID and syntax
-
-id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
-
-AuthorityKeyIdentifier ::= SEQUENCE {
- keyIdentifier [0] KeyIdentifier OPTIONAL,
- authorityCertIssuer [1] GeneralNames OPTIONAL,
- authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
- -- authorityCertIssuer and authorityCertSerialNumber MUST both
- -- be present or both be absent
-
-KeyIdentifier ::= OCTET STRING
-
--- subject key identifier OID and syntax
-
-id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 }
-
-SubjectKeyIdentifier ::= KeyIdentifier
-
--- key usage extension OID and syntax
-
-id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
-
-KeyUsage ::= BIT STRING {
- digitalSignature (0),
- nonRepudiation (1),
- keyEncipherment (2),
- dataEncipherment (3),
- keyAgreement (4),
- keyCertSign (5),
- cRLSign (6),
- encipherOnly (7),
- decipherOnly (8) }
-
--- private key usage period extension OID and syntax
-
-id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 }
-
-PrivateKeyUsagePeriod ::= SEQUENCE {
- notBefore [0] GeneralizedTime OPTIONAL,
- notAfter [1] GeneralizedTime OPTIONAL }
- -- either notBefore or notAfter MUST be present
-
--- certificate policies extension OID and syntax
-
-id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
-
-anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }
-
-CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
-
-PolicyInformation ::= SEQUENCE {
- policyIdentifier CertPolicyId,
- policyQualifiers SEQUENCE SIZE (1..MAX) OF
- PolicyQualifierInfo OPTIONAL }
-
-CertPolicyId ::= OBJECT IDENTIFIER
-
-PolicyQualifierInfo ::= SEQUENCE {
- policyQualifierId PolicyQualifierId,
- qualifier ANY DEFINED BY policyQualifierId }
-
--- Implementations that recognize additional policy qualifiers MUST
--- augment the following definition for PolicyQualifierId
-
-PolicyQualifierId ::=
- OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
-
--- CPS pointer qualifier
-
-CPSuri ::= IA5String
-
--- user notice qualifier
-
-UserNotice ::= SEQUENCE {
- noticeRef NoticeReference OPTIONAL,
- explicitText DisplayText OPTIONAL}
-
-NoticeReference ::= SEQUENCE {
- organization DisplayText,
- noticeNumbers SEQUENCE OF INTEGER }
-
-DisplayText ::= CHOICE {
- ia5String IA5String (SIZE (1..200)),
- visibleString VisibleString (SIZE (1..200)),
- bmpString BMPString (SIZE (1..200)),
- utf8String UTF8String (SIZE (1..200)) }
-
--- policy mapping extension OID and syntax
-
-id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
-
-PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE {
- issuerDomainPolicy CertPolicyId,
- subjectDomainPolicy CertPolicyId }
-
--- subject alternative name extension OID and syntax
-
-id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
-
-SubjectAltName ::= GeneralNames
-
-GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
-
-GeneralName ::= CHOICE {
- otherName [0] AnotherName,
- rfc822Name [1] IA5String,
- dNSName [2] IA5String,
- x400Address [3] ORAddress,
- directoryName [4] Name,
- ediPartyName [5] EDIPartyName,
- uniformResourceIdentifier [6] IA5String,
- iPAddress [7] OCTET STRING,
- registeredID [8] OBJECT IDENTIFIER }
-
--- AnotherName replaces OTHER-NAME ::= TYPE-IDENTIFIER, as
--- TYPE-IDENTIFIER is not supported in the '88 ASN.1 syntax
-
-AnotherName ::= SEQUENCE {
- type-id OBJECT IDENTIFIER,
- value [0] EXPLICIT ANY DEFINED BY type-id }
-
-EDIPartyName ::= SEQUENCE {
- nameAssigner [0] DirectoryString OPTIONAL,
- partyName [1] DirectoryString }
-
--- issuer alternative name extension OID and syntax
-
-id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 }
-
-IssuerAltName ::= GeneralNames
-
-id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 }
-
-SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
-
--- basic constraints extension OID and syntax
-
-id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
-
-BasicConstraints ::= SEQUENCE {
- cA BOOLEAN DEFAULT FALSE,
- pathLenConstraint INTEGER (0..MAX) OPTIONAL }
-
--- name constraints extension OID and syntax
-
-id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
-
-NameConstraints ::= SEQUENCE {
- permittedSubtrees [0] GeneralSubtrees OPTIONAL,
- excludedSubtrees [1] GeneralSubtrees OPTIONAL }
-
-GeneralSubtrees ::= SEQUENCE SIZE (1..MAX) OF GeneralSubtree
-
-GeneralSubtree ::= SEQUENCE {
- base GeneralName,
- minimum [0] BaseDistance DEFAULT 0,
- maximum [1] BaseDistance OPTIONAL }
-
-BaseDistance ::= INTEGER (0..MAX)
-
--- policy constraints extension OID and syntax
-
-id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
-
-PolicyConstraints ::= SEQUENCE {
- requireExplicitPolicy [0] SkipCerts OPTIONAL,
- inhibitPolicyMapping [1] SkipCerts OPTIONAL }
-
-SkipCerts ::= INTEGER (0..MAX)
-
--- CRL distribution points extension OID and syntax
-
-id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31}
-
-CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
-
-DistributionPoint ::= SEQUENCE {
- distributionPoint [0] DistributionPointName OPTIONAL,
- reasons [1] ReasonFlags OPTIONAL,
- cRLIssuer [2] GeneralNames OPTIONAL }
-
-DistributionPointName ::= CHOICE {
- fullName [0] GeneralNames,
- nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
-
-ReasonFlags ::= BIT STRING {
- unused (0),
- keyCompromise (1),
- cACompromise (2),
- affiliationChanged (3),
- superseded (4),
- cessationOfOperation (5),
- certificateHold (6),
- privilegeWithdrawn (7),
- aACompromise (8) }
-
--- extended key usage extension OID and syntax
-
-id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37}
-
-ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
-
-
-KeyPurposeId ::= OBJECT IDENTIFIER
-
--- permit unspecified key uses
-
-anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
-
--- extended key purpose OIDs
-
-id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
-id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
-id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
-id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
-id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
-id-kp-OCSPSigning OBJECT IDENTIFIER ::= { id-kp 9 }
-
--- inhibit any policy OID and syntax
-
-id-ce-inhibitAnyPolicy OBJECT IDENTIFIER ::= { id-ce 54 }
-
-InhibitAnyPolicy ::= SkipCerts
-
--- freshest (delta)CRL extension OID and syntax
-
-id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 }
-
-FreshestCRL ::= CRLDistributionPoints
-
--- authority info access
-
-id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
-
-AuthorityInfoAccessSyntax ::=
- SEQUENCE SIZE (1..MAX) OF AccessDescription
-
-AccessDescription ::= SEQUENCE {
- accessMethod OBJECT IDENTIFIER,
- accessLocation GeneralName }
-
--- subject info access
-
-id-pe-subjectInfoAccess OBJECT IDENTIFIER ::= { id-pe 11 }
-
-SubjectInfoAccessSyntax ::=
- SEQUENCE SIZE (1..MAX) OF AccessDescription
-
--- CRL number extension OID and syntax
-
-id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 }
-
-CRLNumber ::= INTEGER (0..MAX)
-
--- issuing distribution point extension OID and syntax
-
-id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
-
-IssuingDistributionPoint ::= SEQUENCE {
- distributionPoint [0] DistributionPointName OPTIONAL,
- onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE,
- onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE,
- onlySomeReasons [3] ReasonFlags OPTIONAL,
- indirectCRL [4] BOOLEAN DEFAULT FALSE,
- onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
-
-id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
-
-BaseCRLNumber ::= CRLNumber
-
--- CRL reasons extension OID and syntax
-
-id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 }
-
-CRLReason ::= ENUMERATED {
- unspecified (0),
- keyCompromise (1),
- cACompromise (2),
- affiliationChanged (3),
- superseded (4),
- cessationOfOperation (5),
- certificateHold (6),
- removeFromCRL (8),
- privilegeWithdrawn (9),
- aACompromise (10) }
-
--- certificate issuer CRL entry extension OID and syntax
-
-id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 }
-
-CertificateIssuer ::= GeneralNames
-
--- hold instruction extension OID and syntax
-
-id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 }
-
-HoldInstructionCode ::= OBJECT IDENTIFIER
-
--- ANSI x9 holdinstructions
-
--- ANSI x9 arc holdinstruction arc
-
-holdInstruction OBJECT IDENTIFIER ::=
- {joint-iso-itu-t(2) member-body(2) us(840) x9cm(10040) 2}
-
--- ANSI X9 holdinstructions referenced by this standard
-
-id-holdinstruction-none OBJECT IDENTIFIER ::=
- {holdInstruction 1} -- deprecated
-
-id-holdinstruction-callissuer OBJECT IDENTIFIER ::=
- {holdInstruction 2}
-
-id-holdinstruction-reject OBJECT IDENTIFIER ::=
- {holdInstruction 3}
-
--- invalidity date CRL entry extension OID and syntax
-
-id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 }
-
-InvalidityDate ::= GeneralizedTime
-
-END
diff --git a/lib/ssl/pkix/PKIX1Implicit88.hrl b/lib/ssl/pkix/PKIX1Implicit88.hrl
deleted file mode 100644
index 8fa1836284..0000000000
--- a/lib/ssl/pkix/PKIX1Implicit88.hrl
+++ /dev/null
@@ -1,93 +0,0 @@
-%% Generated by the Erlang ASN.1 compiler version:1.4.4.8
-%% Purpose: Erlang record definitions for each named and unnamed
-%% SEQUENCE and SET, and macro definitions for each value
-%% definition,in module PKIX1Implicit88
-
-
-
--record('AuthorityKeyIdentifier',{
-keyIdentifier = asn1_NOVALUE, authorityCertIssuer = asn1_NOVALUE, authorityCertSerialNumber = asn1_NOVALUE}).
-
--record('PrivateKeyUsagePeriod',{
-notBefore = asn1_NOVALUE, notAfter = asn1_NOVALUE}).
-
--record('PolicyInformation',{
-policyIdentifier, policyQualifiers = asn1_NOVALUE}).
-
--record('PolicyQualifierInfo',{
-policyQualifierId, qualifier}).
-
--record('UserNotice',{
-noticeRef = asn1_NOVALUE, explicitText = asn1_NOVALUE}).
-
--record('NoticeReference',{
-organization, noticeNumbers}).
-
--record('PolicyMappings_SEQOF',{
-issuerDomainPolicy, subjectDomainPolicy}).
-
--record('AnotherName',{
-'type-id', value}).
-
--record('EDIPartyName',{
-nameAssigner = asn1_NOVALUE, partyName}).
-
--record('BasicConstraints',{
-cA = asn1_DEFAULT, pathLenConstraint = asn1_NOVALUE}).
-
--record('NameConstraints',{
-permittedSubtrees = asn1_NOVALUE, excludedSubtrees = asn1_NOVALUE}).
-
--record('GeneralSubtree',{
-base, minimum = asn1_DEFAULT, maximum = asn1_NOVALUE}).
-
--record('PolicyConstraints',{
-requireExplicitPolicy = asn1_NOVALUE, inhibitPolicyMapping = asn1_NOVALUE}).
-
--record('DistributionPoint',{
-distributionPoint = asn1_NOVALUE, reasons = asn1_NOVALUE, cRLIssuer = asn1_NOVALUE}).
-
--record('AccessDescription',{
-accessMethod, accessLocation}).
-
--record('IssuingDistributionPoint',{
-distributionPoint = asn1_NOVALUE, onlyContainsUserCerts = asn1_DEFAULT, onlyContainsCACerts = asn1_DEFAULT, onlySomeReasons = asn1_NOVALUE, indirectCRL = asn1_DEFAULT, onlyContainsAttributeCerts = asn1_DEFAULT}).
-
--define('id-ce', {2,5,29}).
--define('id-ce-authorityKeyIdentifier', {2,5,29,35}).
--define('id-ce-subjectKeyIdentifier', {2,5,29,14}).
--define('id-ce-keyUsage', {2,5,29,15}).
--define('id-ce-privateKeyUsagePeriod', {2,5,29,16}).
--define('id-ce-certificatePolicies', {2,5,29,32}).
--define('anyPolicy', {2,5,29,32,0}).
--define('id-ce-policyMappings', {2,5,29,33}).
--define('id-ce-subjectAltName', {2,5,29,17}).
--define('id-ce-issuerAltName', {2,5,29,18}).
--define('id-ce-subjectDirectoryAttributes', {2,5,29,9}).
--define('id-ce-basicConstraints', {2,5,29,19}).
--define('id-ce-nameConstraints', {2,5,29,30}).
--define('id-ce-policyConstraints', {2,5,29,36}).
--define('id-ce-cRLDistributionPoints', {2,5,29,31}).
--define('id-ce-extKeyUsage', {2,5,29,37}).
--define('anyExtendedKeyUsage', {2,5,29,37,0}).
--define('id-kp-serverAuth', {1,3,6,1,5,5,7,3,1}).
--define('id-kp-clientAuth', {1,3,6,1,5,5,7,3,2}).
--define('id-kp-codeSigning', {1,3,6,1,5,5,7,3,3}).
--define('id-kp-emailProtection', {1,3,6,1,5,5,7,3,4}).
--define('id-kp-timeStamping', {1,3,6,1,5,5,7,3,8}).
--define('id-kp-OCSPSigning', {1,3,6,1,5,5,7,3,9}).
--define('id-ce-inhibitAnyPolicy', {2,5,29,54}).
--define('id-ce-freshestCRL', {2,5,29,46}).
--define('id-pe-authorityInfoAccess', {1,3,6,1,5,5,7,1,1}).
--define('id-pe-subjectInfoAccess', {1,3,6,1,5,5,7,1,11}).
--define('id-ce-cRLNumber', {2,5,29,20}).
--define('id-ce-issuingDistributionPoint', {2,5,29,28}).
--define('id-ce-deltaCRLIndicator', {2,5,29,27}).
--define('id-ce-cRLReasons', {2,5,29,21}).
--define('id-ce-certificateIssuer', {2,5,29,29}).
--define('id-ce-holdInstructionCode', {2,5,29,23}).
--define('holdInstruction', {2,2,840,10040,2}).
--define('id-holdinstruction-none', {2,2,840,10040,2,1}).
--define('id-holdinstruction-callissuer', {2,2,840,10040,2,2}).
--define('id-holdinstruction-reject', {2,2,840,10040,2,3}).
--define('id-ce-invalidityDate', {2,5,29,24}).
diff --git a/lib/ssl/pkix/PKIXAttributeCertificate.asn1 b/lib/ssl/pkix/PKIXAttributeCertificate.asn1
deleted file mode 100644
index 7d93e6b37e..0000000000
--- a/lib/ssl/pkix/PKIXAttributeCertificate.asn1
+++ /dev/null
@@ -1,189 +0,0 @@
- PKIXAttributeCertificate {iso(1) identified-organization(3) dod(6)
- internet(1) security(5) mechanisms(5) pkix(7) id-mod(0)
- id-mod-attribute-cert(12)}
-
- DEFINITIONS IMPLICIT TAGS ::=
-
- BEGIN
-
- -- EXPORTS ALL --
-
- IMPORTS
-
- -- IMPORTed module OIDs MAY change if [PKIXPROF] changes
- -- PKIX Certificate Extensions
- Attribute, AlgorithmIdentifier, CertificateSerialNumber,
- Extensions, UniqueIdentifier,
- id-pkix, id-pe, id-kp, id-ad, id-at
- FROM PKIX1Explicit88 {iso(1) identified-organization(3)
- dod(6) internet(1) security(5) mechanisms(5)
- pkix(7) id-mod(0) id-pkix1-explicit-88(1)}
-
- GeneralName, GeneralNames, id-ce
- FROM PKIX1Implicit88 {iso(1) identified-organization(3)
- dod(6) internet(1) security(5) mechanisms(5)
- pkix(7) id-mod(0) id-pkix1-implicit-88(2)} ;
-
- id-pe-ac-auditIdentity OBJECT IDENTIFIER ::= { id-pe 4 }
- id-pe-aaControls OBJECT IDENTIFIER ::= { id-pe 6 }
- id-pe-ac-proxying OBJECT IDENTIFIER ::= { id-pe 10 }
- id-ce-targetInformation OBJECT IDENTIFIER ::= { id-ce 55 }
-
- id-aca OBJECT IDENTIFIER ::= { id-pkix 10 }
- id-aca-authenticationInfo OBJECT IDENTIFIER ::= { id-aca 1 }
- id-aca-accessIdentity OBJECT IDENTIFIER ::= { id-aca 2 }
- id-aca-chargingIdentity OBJECT IDENTIFIER ::= { id-aca 3 }
- id-aca-group OBJECT IDENTIFIER ::= { id-aca 4 }
- -- { id-aca 5 } is reserved
- id-aca-encAttrs OBJECT IDENTIFIER ::= { id-aca 6 }
-
- id-at-role OBJECT IDENTIFIER ::= { id-at 72}
- id-at-clearance OBJECT IDENTIFIER ::=
- { joint-iso-ccitt(2) ds(5) module(1)
- selected-attribute-types(5) clearance (55) }
-
- -- Uncomment this if using a 1988 level ASN.1 compiler
- -- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
-
- AttributeCertificate ::= SEQUENCE {
- acinfo AttributeCertificateInfo,
- signatureAlgorithm AlgorithmIdentifier,
- signatureValue BIT STRING
- }
-
- AttributeCertificateInfo ::= SEQUENCE {
- version AttCertVersion, -- version is v2
- holder Holder,
- issuer AttCertIssuer,
- signature AlgorithmIdentifier,
- serialNumber CertificateSerialNumber,
- attrCertValidityPeriod AttCertValidityPeriod,
- attributes SEQUENCE OF Attribute,
- issuerUniqueID UniqueIdentifier OPTIONAL,
- extensions Extensions OPTIONAL
- }
-
- AttCertVersion ::= INTEGER { v2(1) }
-
- Holder ::= SEQUENCE {
- baseCertificateID [0] IssuerSerial OPTIONAL,
- -- the issuer and serial number of
- -- the holder's Public Key Certificate
- entityName [1] GeneralNames OPTIONAL,
- -- the name of the claimant or role
- objectDigestInfo [2] ObjectDigestInfo OPTIONAL
- -- used to directly authenticate the
- -- holder, for example, an executable
- }
-
- ObjectDigestInfo ::= SEQUENCE {
- digestedObjectType ENUMERATED {
- publicKey (0),
- publicKeyCert (1),
- otherObjectTypes (2) },
- -- otherObjectTypes MUST NOT
- -- MUST NOT be used in this profile
- otherObjectTypeID OBJECT IDENTIFIER OPTIONAL,
- digestAlgorithm AlgorithmIdentifier,
- objectDigest BIT STRING
- }
-
- AttCertIssuer ::= CHOICE {
- v1Form GeneralNames, -- MUST NOT be used in this
- -- profile
- v2Form [0] V2Form -- v2 only
- }
-
- V2Form ::= SEQUENCE {
- issuerName GeneralNames OPTIONAL,
- baseCertificateID [0] IssuerSerial OPTIONAL,
- objectDigestInfo [1] ObjectDigestInfo OPTIONAL
- -- issuerName MUST be present in this profile
- -- baseCertificateID and objectDigestInfo MUST
- -- NOT be present in this profile
- }
-
- IssuerSerial ::= SEQUENCE {
- issuer GeneralNames,
- serial CertificateSerialNumber,
- issuerUID UniqueIdentifier OPTIONAL
- }
-
- AttCertValidityPeriod ::= SEQUENCE {
- notBeforeTime GeneralizedTime,
- notAfterTime GeneralizedTime
- }
-
- Targets ::= SEQUENCE OF Target
-
- Target ::= CHOICE {
- targetName [0] GeneralName,
- targetGroup [1] GeneralName,
- targetCert [2] TargetCert
- }
-
- TargetCert ::= SEQUENCE {
- targetCertificate IssuerSerial,
- targetName GeneralName OPTIONAL,
- certDigestInfo ObjectDigestInfo OPTIONAL
- }
-
- IetfAttrSyntax ::= SEQUENCE {
- policyAuthority[0] GeneralNames OPTIONAL,
- values SEQUENCE OF CHOICE {
- octets OCTET STRING,
- oid OBJECT IDENTIFIER,
- string UTF8String
- }
- }
-
- SvceAuthInfo ::= SEQUENCE {
- service GeneralName,
- ident GeneralName,
- authInfo OCTET STRING OPTIONAL
- }
-
- RoleSyntax ::= SEQUENCE {
- roleAuthority [0] GeneralNames OPTIONAL,
- roleName [1] GeneralName
- }
-
- Clearance ::= SEQUENCE {
- policyId [0] OBJECT IDENTIFIER,
- classList [1] ClassList DEFAULT {unclassified},
- securityCategories
- [2] SET OF SecurityCategory OPTIONAL
- }
-
- ClassList ::= BIT STRING {
- unmarked (0),
- unclassified (1),
- restricted (2),
- confidential (3),
- secret (4),
- topSecret (5)
- }
-
- SecurityCategory ::= SEQUENCE {
- type [0] IMPLICIT OBJECT IDENTIFIER,
- value [1] ANY DEFINED BY type
- }
-
- AAControls ::= SEQUENCE {
- pathLenConstraint INTEGER (0..MAX) OPTIONAL,
- permittedAttrs [0] AttrSpec OPTIONAL,
- excludedAttrs [1] AttrSpec OPTIONAL,
- permitUnSpecified BOOLEAN DEFAULT TRUE
- }
-
- AttrSpec::= SEQUENCE OF OBJECT IDENTIFIER
-
- ACClearAttrs ::= SEQUENCE {
- acIssuer GeneralName,
- acSerial INTEGER,
- attrs SEQUENCE OF Attribute
- }
-
- ProxyInfo ::= SEQUENCE OF Targets
-
- END
diff --git a/lib/ssl/pkix/PKIXAttributeCertificate.hrl b/lib/ssl/pkix/PKIXAttributeCertificate.hrl
deleted file mode 100644
index 99389c4852..0000000000
--- a/lib/ssl/pkix/PKIXAttributeCertificate.hrl
+++ /dev/null
@@ -1,64 +0,0 @@
-%% Generated by the Erlang ASN.1 compiler version:1.4.4.8
-%% Purpose: Erlang record definitions for each named and unnamed
-%% SEQUENCE and SET, and macro definitions for each value
-%% definition,in module PKIXAttributeCertificate
-
-
-
--record('AttributeCertificate',{
-acinfo, signatureAlgorithm, signatureValue}).
-
--record('AttributeCertificateInfo',{
-version, holder, issuer, signature, serialNumber, attrCertValidityPeriod, attributes, issuerUniqueID = asn1_NOVALUE, extensions = asn1_NOVALUE}).
-
--record('Holder',{
-baseCertificateID = asn1_NOVALUE, entityName = asn1_NOVALUE, objectDigestInfo = asn1_NOVALUE}).
-
--record('ObjectDigestInfo',{
-digestedObjectType, otherObjectTypeID = asn1_NOVALUE, digestAlgorithm, objectDigest}).
-
--record('V2Form',{
-issuerName = asn1_NOVALUE, baseCertificateID = asn1_NOVALUE, objectDigestInfo = asn1_NOVALUE}).
-
--record('IssuerSerial',{
-issuer, serial, issuerUID = asn1_NOVALUE}).
-
--record('AttCertValidityPeriod',{
-notBeforeTime, notAfterTime}).
-
--record('TargetCert',{
-targetCertificate, targetName = asn1_NOVALUE, certDigestInfo = asn1_NOVALUE}).
-
--record('IetfAttrSyntax',{
-policyAuthority = asn1_NOVALUE, values}).
-
--record('SvceAuthInfo',{
-service, ident, authInfo = asn1_NOVALUE}).
-
--record('RoleSyntax',{
-roleAuthority = asn1_NOVALUE, roleName}).
-
--record('Clearance',{
-policyId, classList = asn1_DEFAULT, securityCategories = asn1_NOVALUE}).
-
--record('SecurityCategory',{
-type, value}).
-
--record('AAControls',{
-pathLenConstraint = asn1_NOVALUE, permittedAttrs = asn1_NOVALUE, excludedAttrs = asn1_NOVALUE, permitUnSpecified = asn1_DEFAULT}).
-
--record('ACClearAttrs',{
-acIssuer, acSerial, attrs}).
-
--define('id-pe-ac-auditIdentity', {1,3,6,1,5,5,7,1,4}).
--define('id-pe-aaControls', {1,3,6,1,5,5,7,1,6}).
--define('id-pe-ac-proxying', {1,3,6,1,5,5,7,1,10}).
--define('id-ce-targetInformation', {2,5,29,55}).
--define('id-aca', {1,3,6,1,5,5,7,10}).
--define('id-aca-authenticationInfo', {1,3,6,1,5,5,7,10,1}).
--define('id-aca-accessIdentity', {1,3,6,1,5,5,7,10,2}).
--define('id-aca-chargingIdentity', {1,3,6,1,5,5,7,10,3}).
--define('id-aca-group', {1,3,6,1,5,5,7,10,4}).
--define('id-aca-encAttrs', {1,3,6,1,5,5,7,10,6}).
--define('id-at-role', {2,5,4,72}).
--define('id-at-clearance', {2,5,1,5,55}).
diff --git a/lib/ssl/pkix/README b/lib/ssl/pkix/README
deleted file mode 100644
index 8be2c15de5..0000000000
--- a/lib/ssl/pkix/README
+++ /dev/null
@@ -1,49 +0,0 @@
-The files
-
- PKIX1Algorithms88.asn1
- PKIX1Explicit88.asn1
- PKIX1Implicit88.asn1
- PKIXAttributeCertificate.asn1
-
-are from RFCs 3279, 3280 and 3281.
-
-We have edited PKIX1Explicit88.asn1, PKIX1Implicit88.asn1, and
-PKIXAttributeCertificate.asn1 as follows:
-
-
-1. Removal of definition of UniversalString and BMPString:
-
-diff -r1.1 PKIX1Explicit88.asn1
-15c15
-< UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING
----
-> -- UniversalString ::= [UNIVERSAL 28] IMPLICIT OCTET STRING
-18c18
-< BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING
----
-> -- BMPString ::= [UNIVERSAL 30] IMPLICIT OCTET STRING
-
-
-2. Removal of definition of BMPString:
-
-diff -r1.1 PKIX1Implicit88.asn1
-13c13,14
-< BMPString, UTF8String, -- end "new" types --
----
-> -- BMPString,
-> UTF8String, -- end "new" types --
-
-
-3. Addition of definition of UTF8String, and correction of a typo.
-
-diff -r1.1 PKIXAttributeCertificate.asn1
-46c46
-< -- UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
----
-> UTF8String ::= [UNIVERSAL 12] IMPLICIT OCTET STRING
-55c55
-< version AttCertVersion -- version is v2,
----
-> version AttCertVersion, -- version is v2
-
-PKIX1Algorithms88.asn1 is unchanged.
diff --git a/lib/ssl/pkix/SSL-PKIX.asn1 b/lib/ssl/pkix/SSL-PKIX.asn1
deleted file mode 100644
index ea6333f953..0000000000
--- a/lib/ssl/pkix/SSL-PKIX.asn1
+++ /dev/null
@@ -1,704 +0,0 @@
-SSL-PKIX {iso(1) identified-organization(3) dod(6) internet(1)
- private(4) enterprices(1) ericsson(193) otp(19) ssl(10)
- pkix1(1)}
-
-DEFINITIONS EXPLICIT TAGS ::=
-
-BEGIN
-
--- EXPORTS ALL
-
-IMPORTS
- -- Certificate (parts of)
- Version,
- CertificateSerialNumber,
- --AlgorithmIdentifier,
- Validity,
- UniqueIdentifier,
-
- -- AttribyteTypeAndValue
- Name,
- AttributeType,
- id-at-name,
- id-at-surname,
- id-at-givenName,
- id-at-initials,
- id-at-generationQualifier, X520name,
- id-at-commonName, X520CommonName,
- id-at-localityName, X520LocalityName,
- id-at-stateOrProvinceName, X520StateOrProvinceName,
- id-at-organizationName, X520OrganizationName,
- id-at-organizationalUnitName, X520OrganizationalUnitName,
- id-at-title, X520Title,
- id-at-dnQualifier, X520dnQualifier,
- id-at-countryName, X520countryName,
- id-at-serialNumber, X520SerialNumber,
- id-at-pseudonym, X520Pseudonym,
- id-domainComponent, DomainComponent,
- id-emailAddress, EmailAddress,
-
- -- Extension Attributes
- common-name, CommonName,
- teletex-common-name, TeletexCommonName,
- teletex-personal-name, TeletexPersonalName,
- pds-name, PDSName,
- physical-delivery-country-name, PhysicalDeliveryCountryName,
- postal-code, PostalCode,
- physical-delivery-office-name, PhysicalDeliveryOfficeName,
- physical-delivery-office-number, PhysicalDeliveryOfficeNumber,
- extension-OR-address-components, ExtensionORAddressComponents,
- physical-delivery-personal-name, PhysicalDeliveryPersonalName,
- physical-delivery-organization-name, PhysicalDeliveryOrganizationName,
- extension-physical-delivery-address-components,
- ExtensionPhysicalDeliveryAddressComponents,
- unformatted-postal-address, UnformattedPostalAddress,
- street-address, StreetAddress,
- post-office-box-address, PostOfficeBoxAddress,
- poste-restante-address, PosteRestanteAddress,
- unique-postal-name, UniquePostalName,
- local-postal-attributes, LocalPostalAttributes,
- extended-network-address, ExtendedNetworkAddress,
- terminal-type, TerminalType,
- teletex-domain-defined-attributes, TeletexDomainDefinedAttributes
-
- FROM PKIX1Explicit88 { iso(1) identified-organization(3) dod(6)
- internet(1) security(5) mechanisms(5) pkix(7) id-mod(0)
- id-pkix1-explicit(18) }
-
- -- Extensions
- id-ce-authorityKeyIdentifier, AuthorityKeyIdentifier,
- id-ce-subjectKeyIdentifier, SubjectKeyIdentifier,
- id-ce-keyUsage, KeyUsage,
- id-ce-privateKeyUsagePeriod, PrivateKeyUsagePeriod,
- id-ce-certificatePolicies, CertificatePolicies,
- id-ce-policyMappings, PolicyMappings,
- id-ce-subjectAltName, SubjectAltName,
- id-ce-issuerAltName, IssuerAltName,
- id-ce-subjectDirectoryAttributes, SubjectDirectoryAttributes,
- id-ce-basicConstraints, BasicConstraints,
- id-ce-nameConstraints, NameConstraints,
- id-ce-policyConstraints, PolicyConstraints,
- id-ce-cRLDistributionPoints, CRLDistributionPoints,
- id-ce-extKeyUsage, ExtKeyUsageSyntax,
- id-ce-inhibitAnyPolicy, InhibitAnyPolicy,
- id-ce-freshestCRL, FreshestCRL,
- id-pe-authorityInfoAccess, AuthorityInfoAccessSyntax,
- id-pe-subjectInfoAccess, SubjectInfoAccessSyntax,
- id-ce-cRLNumber, CRLNumber,
- id-ce-issuingDistributionPoint, IssuingDistributionPoint,
- id-ce-deltaCRLIndicator, BaseCRLNumber,
- id-ce-cRLReasons, CRLReason,
- id-ce-certificateIssuer, CertificateIssuer,
- id-ce-holdInstructionCode, HoldInstructionCode,
- id-ce-invalidityDate, InvalidityDate
-
- FROM PKIX1Implicit88 { iso(1) identified-organization(3) dod(6)
- internet(1) security(5) mechanisms(5) pkix(7) id-mod(0)
- id-pkix1-implicit(19) }
-
- --Keys and Signatures
- id-dsa, Dss-Parms, DSAPublicKey,
- id-dsa-with-sha1,
- md2WithRSAEncryption,
- md5WithRSAEncryption,
- sha1WithRSAEncryption,
- rsaEncryption, RSAPublicKey,
- dhpublicnumber, DomainParameters, DHPublicKey,
- id-keyExchangeAlgorithm, KEA-Parms-Id, --KEA-PublicKey,
- ecdsa-with-SHA1,
- prime-field, Prime-p,
- characteristic-two-field, --Characteristic-two,
- gnBasis,
- tpBasis, Trinomial,
- ppBasis, Pentanomial,
- id-ecPublicKey, EcpkParameters, ECPoint
- FROM PKIX1Algorithms88 { iso(1) identified-organization(3) dod(6)
- internet(1) security(5) mechanisms(5) pkix(7) id-mod(0)
- id-mod-pkix1-algorithms(17) };
-
---
--- Certificate
---
-
-SSLCertificate ::= SEQUENCE {
- tbsCertificate TBSCertificate,
- signatureAlgorithm SignatureAlgorithm,
- signature BIT STRING }
-
-SSLTBSCertificate ::= SEQUENCE {
- version [0] Version DEFAULT v1,
- serialNumber CertificateSerialNumber,
- signature SignatureAlgorithm,
- issuer Name,
- validity Validity,
- subject Name,
- subjectPublicKeyInfo SubjectPublicKeyInfo,
- issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version MUST be v2 or v3
- subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version MUST be v2 or v3
- extensions [3] Extensions OPTIONAL
- -- If present, version MUST be v3 -- }
-
-
--- Attribute type and values
---
-
-ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= CLASS {
- &id AttributeType UNIQUE,
- &Type }
- WITH SYNTAX {
- ID &id
- TYPE &Type }
-
-SSLAttributeTypeAndValue ::= SEQUENCE {
- type ATTRIBUTE-TYPE-AND-VALUE-CLASS.&id
- ({SupportedAttributeTypeAndValues}),
- value ATTRIBUTE-TYPE-AND-VALUE-CLASS.&Type
- ({SupportedAttributeTypeAndValues}{@type}) }
-
-SupportedAttributeTypeAndValues ATTRIBUTE-TYPE-AND-VALUE-CLASS ::=
- { name | surname | givenName | initials | generationQualifier |
- commonName | localityName | stateOrProvinceName | organizationName |
- organizationalUnitName | title | dnQualifier | countryName |
- serialNumber | pseudonym | domainComponent | emailAddress }
-
-name ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-name
- TYPE X520name }
-
-surname ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-surname
- TYPE X520name }
-
-givenName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-givenName
- TYPE X520name }
-
-initials ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-initials
- TYPE X520name }
-
-generationQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-generationQualifier
- TYPE X520name }
-
-commonName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-commonName
- TYPE X520CommonName }
-
-localityName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-localityName
- TYPE X520LocalityName }
-
-stateOrProvinceName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-stateOrProvinceName
- TYPE X520StateOrProvinceName }
-
-organizationName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-organizationName
- TYPE X520OrganizationName }
-
-organizationalUnitName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-organizationalUnitName
- TYPE X520OrganizationalUnitName }
-
-title ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-title
- TYPE X520Title }
-
-dnQualifier ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-dnQualifier
- TYPE X520dnQualifier }
-
-countryName ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-countryName
- TYPE X520countryName }
-
-serialNumber ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-serialNumber
- TYPE X520SerialNumber }
-
-pseudonym ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-at-pseudonym
- TYPE X520Pseudonym }
-
-domainComponent ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-domainComponent
- TYPE DomainComponent }
-
-emailAddress ATTRIBUTE-TYPE-AND-VALUE-CLASS ::= {
- ID id-emailAddress
- TYPE EmailAddress }
-
---
--- Signature and Public Key Algorithms
---
-
-SSLSubjectPublicKeyInfo ::= SEQUENCE {
- algorithm SEQUENCE {
- algo PUBLIC-KEY-ALGORITHM-CLASS.&id
- ({SupportedPublicKeyAlgorithms}),
- parameters PUBLIC-KEY-ALGORITHM-CLASS.&Type
- ({SupportedPublicKeyAlgorithms}{@.algo})
- OPTIONAL
- },
- subjectPublicKey PUBLIC-KEY-ALGORITHM-CLASS.&PublicKeyType
- ({SupportedPublicKeyAlgorithms}{@algorithm.algo}) }
-
--- The following is needed for conversion of SubjectPublicKeyInfo.
-
-SSLSubjectPublicKeyInfo-Any ::= SEQUENCE {
- algorithm PublicKeyAlgorithm,
- subjectPublicKey ANY }
-
-
-SIGNATURE-ALGORITHM-CLASS ::= CLASS {
- &id OBJECT IDENTIFIER UNIQUE,
- &Type OPTIONAL }
- WITH SYNTAX {
- ID &id
- [TYPE &Type] }
-
-PUBLIC-KEY-ALGORITHM-CLASS ::= CLASS {
- &id OBJECT IDENTIFIER UNIQUE,
- &Type OPTIONAL,
- &PublicKeyType OPTIONAL }
- WITH SYNTAX {
- ID &id
- [TYPE &Type]
- [PUBLIC-KEY-TYPE &PublicKeyType] }
-
-SignatureAlgorithm ::= SEQUENCE {
- algorithm SIGNATURE-ALGORITHM-CLASS.&id
- ({SupportedSignatureAlgorithms}),
- parameters SIGNATURE-ALGORITHM-CLASS.&Type
- ({SupportedSignatureAlgorithms}{@algorithm})
- OPTIONAL }
-
-SignatureAlgorithm-Any ::= SEQUENCE {
- algorithm OBJECT IDENTIFIER,
- parameters ANY OPTIONAL }
-
-PublicKeyAlgorithm ::= SEQUENCE {
- algorithm PUBLIC-KEY-ALGORITHM-CLASS.&id
- ({SupportedPublicKeyAlgorithms}),
- parameters PUBLIC-KEY-ALGORITHM-CLASS.&Type
- ({SupportedPublicKeyAlgorithms}{@algorithm})
- OPTIONAL }
-
-SupportedSignatureAlgorithms SIGNATURE-ALGORITHM-CLASS ::= {
- dsa-with-sha1 | md2-with-rsa-encryption |
- md5-with-rsa-encryption | sha1-with-rsa-encryption |
- ecdsa-with-sha1 }
-
-SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= {
- dsa | rsa-encryption | dh | kea | ec-public-key }
-
- -- DSA Keys and Signatures
-
- -- SubjectPublicKeyInfo:
-
- dsa PUBLIC-KEY-ALGORITHM-CLASS ::= {
- ID id-dsa
- TYPE Dss-Parms -- XXX Must be OPTIONAL
- PUBLIC-KEY-TYPE DSAPublicKey }
-
- -- Certificate.signatureAlgorithm
-
- dsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= {
- ID id-dsa-with-sha1
- TYPE NULL } -- XXX Must be empty and not NULL
-
- --
- -- RSA Keys and Signatures
- --
-
- -- Certificate.signatureAlgorithm
-
- md2-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
- ID md2WithRSAEncryption
- TYPE NULL }
-
- md5-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
- ID md5WithRSAEncryption
- TYPE NULL }
-
- sha1-with-rsa-encryption SIGNATURE-ALGORITHM-CLASS ::= {
- ID sha1WithRSAEncryption
- TYPE NULL }
-
- -- Certificate.signature
- -- See PKCS #1 (RFC 2313). XXX
-
- -- SubjectPublicKeyInfo:
-
- rsa-encryption PUBLIC-KEY-ALGORITHM-CLASS ::= {
- ID rsaEncryption
- TYPE NULL
- PUBLIC-KEY-TYPE RSAPublicKey }
-
- --
- -- Diffie-Hellman Keys
- --
-
- -- SubjectPublicKeyInfo:
-
- dh PUBLIC-KEY-ALGORITHM-CLASS ::= {
- ID dhpublicnumber
- TYPE DomainParameters
- PUBLIC-KEY-TYPE DHPublicKey }
-
- -- There are no Diffie-Hellman signature algorithms
-
- --
- -- KEA Keys
- --
-
- -- SubjectPublicKeyInfo:
-
- KEA-PublicKey ::= INTEGER
-
- kea PUBLIC-KEY-ALGORITHM-CLASS ::= {
- ID id-keyExchangeAlgorithm
- TYPE KEA-Parms-Id
- PUBLIC-KEY-TYPE KEA-PublicKey }
-
- -- There are no KEA signature algorithms
-
- --
- -- Elliptic Curve Keys, Signatures, and Curves
- --
-
- -- Certificate.signatureAlgorithm
-
- ecdsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= {
- ID ecdsa-with-SHA1
- TYPE NULL } -- XXX Must be empty and not NULL
-
- FIELD-ID-CLASS ::= CLASS {
- &id OBJECT IDENTIFIER UNIQUE,
- &Type }
- WITH SYNTAX {
- ID &id
- TYPE &Type }
-
- SSLFieldID ::= SEQUENCE { -- Finite field
- fieldType FIELD-ID-CLASS.&id({SupportedFieldIds}),
- parameters FIELD-ID-CLASS.&Type({SupportedFieldIds}{@fieldType}) }
-
- SupportedFieldIds FIELD-ID-CLASS ::= {
- field-prime-field | field-characteristic-two }
-
- field-prime-field FIELD-ID-CLASS ::= {
- ID prime-field
- TYPE Prime-p }
-
- CHARACTERISTIC-TWO-CLASS ::= CLASS {
- &id OBJECT IDENTIFIER UNIQUE,
- &Type }
- WITH SYNTAX {
- ID &id
- TYPE &Type }
-
- SSLCharacteristic-two ::= SEQUENCE { -- Finite field
- m INTEGER, -- Field size 2^m
- basis CHARACTERISTIC-TWO-CLASS.&id({SupportedCharacteristicTwos}),
- parameters CHARACTERISTIC-TWO-CLASS.&Type
- ({SupportedCharacteristicTwos}{@basis}) }
-
- SupportedCharacteristicTwos CHARACTERISTIC-TWO-CLASS ::= {
- gn-basis | tp-basis | pp-basis }
-
- field-characteristic-two FIELD-ID-CLASS ::= {
- ID characteristic-two-field
- TYPE Characteristic-two }
-
- gn-basis CHARACTERISTIC-TWO-CLASS ::= {
- ID gnBasis
- TYPE NULL }
-
- tp-basis CHARACTERISTIC-TWO-CLASS ::= {
- ID tpBasis
- TYPE Trinomial }
-
- pp-basis CHARACTERISTIC-TWO-CLASS ::= {
- ID ppBasis
- TYPE Pentanomial }
-
- -- SubjectPublicKeyInfo.algorithm
-
- ec-public-key PUBLIC-KEY-ALGORITHM-CLASS ::= {
- ID id-ecPublicKey
- TYPE EcpkParameters
- PUBLIC-KEY-TYPE ECPoint }
-
---
--- Extension Attributes
---
-
-EXTENSION-ATTRIBUTE-CLASS ::= CLASS {
- &id INTEGER UNIQUE,
- &Type }
- WITH SYNTAX {
- ID &id
- TYPE &Type }
-
-SSLExtensionAttributes ::= SET SIZE (1..MAX) OF ExtensionAttribute
-
--- XXX Below we should have extension-attribute-type and extension-
--- attribute-value but Erlang ASN1 does not like it.
-SSLExtensionAttribute ::= SEQUENCE {
- extensionAttributeType [0] IMPLICIT EXTENSION-ATTRIBUTE-CLASS.&id
- ({SupportedExtensionAttributes}),
- extensionAttributeValue [1] EXTENSION-ATTRIBUTE-CLASS.&Type
- ({SupportedExtensionAttributes}{@extensionAttributeType}) }
-
-SupportedExtensionAttributes EXTENSION-ATTRIBUTE-CLASS ::= {
- x400-common-name |
- x400-teletex-common-name |
- x400-teletex-personal-name |
- x400-pds-name |
- x400-physical-delivery-country-name |
- x400-postal-code |
- x400-physical-delivery-office-name |
- x400-physical-delivery-office-number |
- x400-extension-OR-address-components |
- x400-physical-delivery-personal-name |
- x400-physical-delivery-organization-name |
- x400-extension-physical-delivery-address-components |
- x400-unformatted-postal-address |
- x400-street-address |
- x400-post-office-box-address |
- x400-poste-restante-address |
- x400-unique-postal-name |
- x400-local-postal-attributes |
- x400-extended-network-address |
- x400-terminal-type |
- x400-teletex-domain-defined-attributes }
-
--- Extension types and attribute values
-
-x400-common-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID common-name
- TYPE CommonName }
-
-x400-teletex-common-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID teletex-common-name
- TYPE TeletexCommonName }
-
-x400-teletex-personal-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID teletex-personal-name
- TYPE TeletexPersonalName }
-
-x400-pds-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID pds-name
- TYPE PDSName }
-
-x400-physical-delivery-country-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID physical-delivery-country-name
- TYPE PhysicalDeliveryCountryName }
-
-x400-postal-code EXTENSION-ATTRIBUTE-CLASS ::= {
- ID postal-code
- TYPE PostalCode }
-
-x400-physical-delivery-office-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID physical-delivery-office-name
- TYPE PhysicalDeliveryOfficeName }
-
-x400-physical-delivery-office-number EXTENSION-ATTRIBUTE-CLASS ::= {
- ID physical-delivery-office-number
- TYPE PhysicalDeliveryOfficeNumber }
-
-x400-extension-OR-address-components EXTENSION-ATTRIBUTE-CLASS ::= {
- ID extension-OR-address-components
- TYPE ExtensionORAddressComponents }
-
-x400-physical-delivery-personal-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID physical-delivery-personal-name
- TYPE PhysicalDeliveryPersonalName }
-
-x400-physical-delivery-organization-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID physical-delivery-organization-name
- TYPE PhysicalDeliveryOrganizationName }
-
-x400-extension-physical-delivery-address-components
- EXTENSION-ATTRIBUTE-CLASS ::= {
- ID extension-physical-delivery-address-components
- TYPE ExtensionPhysicalDeliveryAddressComponents }
-
-x400-unformatted-postal-address EXTENSION-ATTRIBUTE-CLASS ::= {
- ID unformatted-postal-address
- TYPE UnformattedPostalAddress }
-
-x400-street-address EXTENSION-ATTRIBUTE-CLASS ::= {
- ID street-address
- TYPE StreetAddress }
-
-x400-post-office-box-address EXTENSION-ATTRIBUTE-CLASS ::= {
- ID post-office-box-address
- TYPE PostOfficeBoxAddress }
-
-x400-poste-restante-address EXTENSION-ATTRIBUTE-CLASS ::= {
- ID poste-restante-address
- TYPE PosteRestanteAddress }
-
-x400-unique-postal-name EXTENSION-ATTRIBUTE-CLASS ::= {
- ID unique-postal-name
- TYPE UniquePostalName }
-
-x400-local-postal-attributes EXTENSION-ATTRIBUTE-CLASS ::= {
- ID local-postal-attributes
- TYPE LocalPostalAttributes }
-
-x400-extended-network-address EXTENSION-ATTRIBUTE-CLASS ::= {
- ID extended-network-address
- TYPE ExtendedNetworkAddress }
-
-x400-terminal-type EXTENSION-ATTRIBUTE-CLASS ::= {
- ID terminal-type
- TYPE TerminalType }
-
-x400-teletex-domain-defined-attributes EXTENSION-ATTRIBUTE-CLASS ::= {
- ID teletex-domain-defined-attributes
- TYPE TeletexDomainDefinedAttributes }
-
--- Extensions
-
-SSLExtensions ::= SEQUENCE SIZE (1..MAX) OF Extension
-
-EXTENSION-CLASS ::= CLASS {
- &id OBJECT IDENTIFIER UNIQUE,
- &Type OPTIONAL}
- WITH SYNTAX {
- ID &id
- [TYPE &Type] }
-
-SSLExtension ::= SEQUENCE {
- extnID EXTENSION-CLASS.&id({SupportedExtensions}),
- critical BOOLEAN DEFAULT FALSE,
- extnValue EXTENSION-CLASS.&Type({SupportedExtensions}{@extnID}) }
-
--- The following is needed for conversion between Extension and Extension-Cd
-
-ObjId ::= OBJECT IDENTIFIER
-Boolean ::= BOOLEAN
-Any ::= ANY
-
-Extension-Any ::= SEQUENCE {
- extnID OBJECT IDENTIFIER,
- critical BOOLEAN DEFAULT FALSE,
- extnValue ANY }
-
-SupportedExtensions EXTENSION-CLASS ::= { authorityKeyIdentifier |
- subjectKeyIdentifier | keyUsage | privateKeyUsagePeriod |
- certificatePolicies | policyMappings | subjectAltName |
- issuerAltName | subjectDirectoryAttributes | basicConstraints |
- nameConstraints | policyConstraints | cRLDistributionPoints |
- extKeyUsage | inhibitAnyPolicy | freshestCRL | authorityInfoAccess |
- subjectInfoAccess | cRLNumber | issuingDistributionPoint |
- deltaCRLIndicator | cRLReasons | certificateIssuer |
- holdInstructionCode | invalidityDate }
-
-authorityKeyIdentifier EXTENSION-CLASS ::= {
- ID id-ce-authorityKeyIdentifier
- TYPE AuthorityKeyIdentifier }
-
-subjectKeyIdentifier EXTENSION-CLASS ::= {
- ID id-ce-subjectKeyIdentifier
- TYPE SubjectKeyIdentifier }
-
-keyUsage EXTENSION-CLASS ::= {
- ID id-ce-keyUsage
- TYPE KeyUsage }
-
-privateKeyUsagePeriod EXTENSION-CLASS ::= {
- ID id-ce-privateKeyUsagePeriod
- TYPE PrivateKeyUsagePeriod }
-
-certificatePolicies EXTENSION-CLASS ::= {
- ID id-ce-certificatePolicies
- TYPE CertificatePolicies }
-
-policyMappings EXTENSION-CLASS ::= {
- ID id-ce-policyMappings
- TYPE PolicyMappings }
-
-subjectAltName EXTENSION-CLASS ::= {
- ID id-ce-subjectAltName
- TYPE SubjectAltName }
-
-issuerAltName EXTENSION-CLASS ::= {
- ID id-ce-issuerAltName
- TYPE IssuerAltName }
-
-subjectDirectoryAttributes EXTENSION-CLASS ::= {
- ID id-ce-subjectDirectoryAttributes
- TYPE SubjectDirectoryAttributes }
-
-basicConstraints EXTENSION-CLASS ::= {
- ID id-ce-basicConstraints
- TYPE BasicConstraints }
-
-nameConstraints EXTENSION-CLASS ::= {
- ID id-ce-nameConstraints
- TYPE NameConstraints }
-
-policyConstraints EXTENSION-CLASS ::= {
- ID id-ce-policyConstraints
- TYPE PolicyConstraints }
-
-cRLDistributionPoints EXTENSION-CLASS ::= {
- ID id-ce-cRLDistributionPoints
- TYPE CRLDistributionPoints }
-
-extKeyUsage EXTENSION-CLASS ::= {
- ID id-ce-extKeyUsage
- TYPE ExtKeyUsageSyntax }
-
-inhibitAnyPolicy EXTENSION-CLASS ::= {
- ID id-ce-inhibitAnyPolicy
- TYPE InhibitAnyPolicy }
-
-freshestCRL EXTENSION-CLASS ::= {
- ID id-ce-freshestCRL
- TYPE FreshestCRL }
-
-authorityInfoAccess EXTENSION-CLASS ::= {
- ID id-pe-authorityInfoAccess
- TYPE AuthorityInfoAccessSyntax }
-
-subjectInfoAccess EXTENSION-CLASS ::= {
- ID id-pe-subjectInfoAccess
- TYPE SubjectInfoAccessSyntax }
-
-cRLNumber EXTENSION-CLASS ::= {
- ID id-ce-cRLNumber
- TYPE CRLNumber }
-
-issuingDistributionPoint EXTENSION-CLASS ::= {
- ID id-ce-issuingDistributionPoint
- TYPE IssuingDistributionPoint }
-
-deltaCRLIndicator EXTENSION-CLASS ::= {
- ID id-ce-deltaCRLIndicator
- TYPE BaseCRLNumber }
-
-cRLReasons EXTENSION-CLASS ::= {
- ID id-ce-cRLReasons
- TYPE CRLReason }
-
-certificateIssuer EXTENSION-CLASS ::= {
- ID id-ce-certificateIssuer
- TYPE CertificateIssuer }
-
-holdInstructionCode EXTENSION-CLASS ::= {
- ID id-ce-holdInstructionCode
- TYPE HoldInstructionCode }
-
-invalidityDate EXTENSION-CLASS ::= {
- ID id-ce-invalidityDate
- TYPE InvalidityDate }
-
-END
diff --git a/lib/ssl/pkix/mk_ssl_pkix_oid.erl b/lib/ssl/pkix/mk_ssl_pkix_oid.erl
deleted file mode 100644
index 06edc5113a..0000000000
--- a/lib/ssl/pkix/mk_ssl_pkix_oid.erl
+++ /dev/null
@@ -1,94 +0,0 @@
--module(mk_ssl_pkix_oid).
-
--export([make/0]).
-
--define(PKIX_MODULES, ['OTP-PKIX']).
-
-make() ->
- {ok, Fd} = file:open("ssl_pkix_oid.erl", [write]),
- io:fwrite(Fd, "%%% File: ssl_pkix_oid.erl\n"
- "%%% NB This file has been automatically generated by "
- "mk_ssl_pkix_oid.\n"
- "%%% Do not edit it.\n\n", []),
- io:fwrite(Fd, "-module(ssl_pkix_oid).\n", []),
- io:fwrite(Fd, "-export([id2atom/1, atom2id/1, all_atoms/0, "
- "all_ids/0]).\n\n", []),
-
-
- AIds0 = get_atom_ids(?PKIX_MODULES),
-
- AIds1 = modify_atoms(AIds0),
- gen_id2atom(Fd, AIds1),
- gen_atom2id(Fd, AIds1),
- gen_all(Fd, AIds1),
- file:close(Fd).
-
-get_atom_ids(Ms) ->
- get_atom_ids(Ms, []).
-
-get_atom_ids([], AIdss) ->
- lists:flatten(AIdss);
-get_atom_ids([M| Ms], AIdss) ->
- {value, {exports, Exports}} =
- lists:keysearch(exports, 1, M:module_info()),
- As = lists:zf(
- fun ({info, 0}) -> false;
- ({module_info, 0}) -> false;
- ({encoding_rule, 0}) -> false;
- ({F, 0}) ->
- case atom_to_list(F) of
- %% Remove upper-bound (ub-) functions
- "ub-" ++ _Rest ->
- false;
- _ ->
- {true, F}
- end;
- (_) -> false
- end, Exports),
- AIds = lists:map(fun(F) -> {F, M:F()} end, As),
- get_atom_ids(Ms, [AIds| AIdss]).
-
-modify_atoms(AIds) ->
- F = fun({A, I}) ->
- NAS = case atom_to_list(A) of
- "id-" ++ Rest ->
- Rest;
- Any ->
- Any
- end,
- {list_to_atom(NAS), I} end,
- lists:map(F, AIds).
-
-gen_id2atom(Fd, AIds0) ->
- AIds1 = lists:keysort(2, AIds0),
- Txt = join(";\n",
- lists:map(
- fun({Atom, Id}) ->
- io_lib:fwrite("id2atom(~p) ->\n ~p", [Id, Atom])
- end, AIds1)),
- io:fwrite(Fd, "~s;\nid2atom(Any)->\n Any.\n\n", [Txt]).
-
-gen_atom2id(Fd, AIds0) ->
- AIds1 = lists:keysort(1, AIds0),
- Txt = join(";\n",
- lists:map(
- fun({Atom, Id}) ->
- io_lib:fwrite("atom2id(~p) ->\n ~p", [Atom, Id])
- end, AIds1)),
- io:fwrite(Fd, "~s;\natom2id(Any)->\n Any.\n\n", [Txt]).
-
-gen_all(Fd, AIds) ->
- Atoms = lists:sort([A || {A, _} <- AIds]),
- Ids = lists:sort([I || {_, I} <- AIds]),
- F = fun(X) -> io_lib:fwrite(" ~w", [X]) end,
- ATxt = "all_atoms() ->\n" ++ join(",\n", lists:map(F, Atoms)),
- io:fwrite(Fd, "~s.\n\n", [ATxt]),
- ITxt = "all_ids() ->\n" ++ join(",\n", lists:map(F, Ids)),
- io:fwrite(Fd, "~s.\n\n", [ITxt]).
-
-join(Sep, [H1, H2| T]) ->
- [H1, Sep| join(Sep, [H2| T])];
-join(_Sep, [H1]) ->
- H1;
-join(_, []) ->
- [].
diff --git a/lib/ssl/pkix/prebuild.skip b/lib/ssl/pkix/prebuild.skip
deleted file mode 100644
index ffe82be68b..0000000000
--- a/lib/ssl/pkix/prebuild.skip
+++ /dev/null
@@ -1,5 +0,0 @@
-PKIX1Algorithms88.asn1db
-PKIXAttributeCertificate.asn1db
-PKIX1Explicit88.asn1db
-SSL-PKIX.asn1db
-PKIX1Implicit88.asn1db
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index fabf8a4e0d..7514ad2aa2 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
@@ -46,9 +46,6 @@ MODULES= \
ssl_server \
ssl_sup \
ssl_prim \
- ssl_pkix \
- ssl_pem \
- ssl_base64 \
inet_ssl_dist \
ssl_certificate\
ssl_certificate_db\
@@ -71,8 +68,6 @@ INTERNAL_HRL_FILES = \
ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \
ssl_record.hrl
-PUBLIC_HRL_FILES = ssl_pkix.hrl
-
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR))
@@ -85,15 +80,12 @@ APP_TARGET= $(EBIN)/$(APP_FILE)
APPUP_SRC= $(APPUP_FILE).src
APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
-INCLUDE = ../include
-
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
EXTRA_ERLC_FLAGS = +warn_unused_vars
ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
-pz $(ERL_TOP)/lib/public_key/ebin \
- -I$(INCLUDE) \
$(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\"
@@ -101,7 +93,7 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \
# Targets
# ----------------------------------------------------
-debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(PUBLIC_HRL_FILES)
+debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
clean:
rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET)
@@ -113,9 +105,6 @@ $(APP_TARGET): $(APP_SRC) ../vsn.mk
$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
-$(PUBLIC_HRL_FILES):
- cp -f $(PUBLIC_HRL_FILES) $(INCLUDE)
-
docs:
# ----------------------------------------------------
@@ -126,8 +115,6 @@ include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)/src
$(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src
- $(INSTALL_DIR) $(RELSYSDIR)/include
- $(INSTALL_DATA) $(PUBLIC_HRL_FILES) $(RELSYSDIR)/include
$(INSTALL_DIR) $(RELSYSDIR)/ebin
$(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \
$(APPUP_TARGET) $(RELSYSDIR)/ebin
diff --git a/lib/ssl/src/inet_ssl_dist.erl b/lib/ssl/src/inet_ssl_dist.erl
index f62aefd35a..6c0fbc0618 100644
--- a/lib/ssl/src/inet_ssl_dist.erl
+++ b/lib/ssl/src/inet_ssl_dist.erl
@@ -1,8 +1,8 @@
-%%<copyright>
-%% <year>2000-2008</year>
-%% <holder>Ericsson AB, All Rights Reserved</holder>
-%%</copyright>
-%%<legalnotice>
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
@@ -14,8 +14,9 @@
%% the License for the specific language governing rights and limitations
%% under the License.
%%
-%% The Initial Developer of the Original Code is Ericsson AB.
-%%</legalnotice>
+%% %CopyrightEnd%
+%%
+
%%
-module(inet_ssl_dist).
@@ -135,6 +136,9 @@ accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
[self(), AcceptPid, Socket, MyNode,
Allowed, SetupTime]).
+%% Suppress dialyzer warning, we do not really care about old ssl code
+%% as we intend to remove it.
+-spec(do_accept(_,_,_,_,_,_) -> no_return()).
do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
process_flag(priority, max),
receive
@@ -167,8 +171,8 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
ssl_prim:getll(S)
end,
f_address = fun get_remote_id/2,
- mf_tick = {?MODULE, tick},
- mf_getstat = {?MODULE,getstat}
+ mf_tick = fun ?MODULE:tick/1,
+ mf_getstat = fun ?MODULE:getstat/1
},
dist_util:handshake_other_started(HSData);
{false,IP} ->
@@ -204,6 +208,9 @@ setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
LongOrShortNames,
SetupTime]).
+%% Suppress dialyzer warning, we do not really care about old ssl code
+%% as we intend to remove it.
+-spec(do_setup(_,_,_,_,_,_) -> no_return()).
do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
process_flag(priority, max),
?trace("~p~n",[{inet_ssl_dist,self(),setup,Node}]),
@@ -258,8 +265,8 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
protocol = ssl,
family = inet}
end,
- mf_tick = {?MODULE, tick},
- mf_getstat = {?MODULE,getstat},
+ mf_tick = fun ?MODULE:tick/1,
+ mf_getstat = fun ?MODULE:getstat/1,
request_type = Type
},
dist_util:handshake_we_started(HSData);
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 2a7d451341..b9716786e6 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -7,10 +7,6 @@
ssl_server,
ssl_broker,
ssl_broker_sup,
- ssl_base64,
- ssl_pem,
- ssl_pkix,
- ssl_pkix_oid,
ssl_prim,
inet_ssl_dist,
ssl_tls1,
@@ -28,11 +24,10 @@
ssl_cipher,
ssl_certificate_db,
ssl_certificate,
- ssl_alert,
- 'OTP-PKIX'
+ ssl_alert
]},
{registered, [ssl_sup, ssl_server, ssl_broker_sup]},
- {applications, [kernel, stdlib]},
+ {applications, [crypto, public_key, kernel, stdlib]},
{env, []},
{mod, {ssl_app, []}}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index e8ae6846aa..d3e426f254 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,26 +1,17 @@
%% -*- erlang -*-
{"%VSN%",
[
- {"3.10", [{restart_application, ssl}]},
- {"3.10.1", [{restart_application, ssl}]},
- {"3.10.2", [{restart_application, ssl}]},
- {"3.10.3", [{restart_application, ssl}]},
- {"3.10.4", [{restart_application, ssl}]},
- {"3.10.5", [{restart_application, ssl}]},
- {"3.10.6", [{restart_application, ssl}]},
- {"3.10.7", [{restart_application, ssl}]},
- {"3.10.8", [{restart_application, ssl}]},
- {"3.10.9", [{restart_application, ssl}]}
+ {"4.1.3", [{restart_application, ssl}]},
+ {"4.1.2", [{restart_application, ssl}]},
+ {"4.1.1", [{restart_application, ssl}]},
+ {"4.1", [{restart_application, ssl}]},
+ {"4.0.1", [{restart_application, ssl}]}
],
[
- {"3.10", [{restart_application, ssl}]},
- {"3.10.1", [{restart_application, ssl}]},
- {"3.10.2", [{restart_application, ssl}]},
- {"3.10.3", [{restart_application, ssl}]},
- {"3.10.4", [{restart_application, ssl}]},
- {"3.10.5", [{restart_application, ssl}]},
- {"3.10.6", [{restart_application, ssl}]},
- {"3.10.8", [{restart_application, ssl}]},
- {"3.10.9", [{restart_application, ssl}]}
+ {"4.1.3", [{restart_application, ssl}]},
+ {"4.1.2", [{restart_application, ssl}]},
+ {"4.1.1", [{restart_application, ssl}]},
+ {"4.1", [{restart_application, ssl}]},
+ {"4.0.1", [{restart_application, ssl}]}
]}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 95cd92ee60..7b1fda4cf9 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -34,10 +34,14 @@
%% Should be deprecated as soon as old ssl is removed
%%-deprecated({pid, 1, next_major_release}).
+-deprecated({peercert, 2, next_major_release}).
-include("ssl_int.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
+-include("ssl_cipher.hrl").
+
+-include_lib("public_key/include/public_key.hrl").
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
@@ -45,24 +49,47 @@
inet_ssl, %% inet options for internal ssl socket
cb %% Callback info
}).
-
-%%--------------------------------------------------------------------
-%% Function: start([, Type]) -> ok
-%%
-%% Type = permanent | transient | temporary
-%% Vsns = [Vsn]
-%% Vsn = ssl3 | tlsv1 | 'tlsv1.1'
+-type option() :: socketoption() | ssloption() | transportoption().
+-type socketoption() :: [{property(), term()}]. %% See gen_tcp and inet
+-type property() :: atom().
+-type ssloption() :: {verify, verify_type()} |
+ {verify_fun, {fun(), InitialUserState::term()}} |
+ {fail_if_no_peer_cert, boolean()} | {depth, integer()} |
+ {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} |
+ {keyfile, path()} | {password, string()} | {cacerts, [Der::binary()]} |
+ {cacertfile, path()} | {dh, Der::binary()} | {dhfile, path()} |
+ {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
+ {reuse_session, fun()} | {hibernate_after, integer()|undefined}.
+
+-type verify_type() :: verify_none | verify_peer.
+-type path() :: string().
+-type ciphers() :: [erl_cipher_suite()] |
+ string(). % (according to old API)
+-type ssl_imp() :: new | old.
+
+-type transportoption() :: {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}.
+
+
+%%--------------------------------------------------------------------
+-spec start() -> ok | {error, reason()}.
+-spec start(permanent | transient | temporary) -> ok | {error, reason()}.
%%
-%% Description: Starts the ssl application. Default type
+%% Description: Utility function that starts the ssl,
+%% crypto and public_key applications. Default type
%% is temporary. see application(3)
%%--------------------------------------------------------------------
start() ->
+ application:start(crypto),
+ application:start(public_key),
application:start(ssl).
+
start(Type) ->
+ application:start(crypto, Type),
+ application:start(public_key, Type),
application:start(ssl, Type).
%%--------------------------------------------------------------------
-%% Function: stop() -> ok
+-spec stop() -> ok.
%%
%% Description: Stops the ssl application.
%%--------------------------------------------------------------------
@@ -70,7 +97,13 @@ stop() ->
application:stop(ssl).
%%--------------------------------------------------------------------
-%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket}
+-spec connect(host() | port(), [option()]) -> {ok, #sslsocket{}} |
+ {error, reason()}.
+-spec connect(host() | port(), [option()] | port_num(), timeout() | list()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+-spec connect(host() | port(), port_num(), list(), timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
+
%%
%% Description: Connect to a ssl server.
%%--------------------------------------------------------------------
@@ -96,13 +129,13 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
{error, Reason}
end;
-connect(Address, Port, Options) ->
- connect(Address, Port, Options, infinity).
+connect(Host, Port, Options) ->
+ connect(Host, Port, Options, infinity).
-connect(Address, Port, Options0, Timeout) ->
- case proplists:get_value(ssl_imp, Options0, old) of
+connect(Host, Port, Options0, Timeout) ->
+ case proplists:get_value(ssl_imp, Options0, new) of
new ->
- new_connect(Address, Port, Options0, Timeout);
+ new_connect(Host, Port, Options0, Timeout);
old ->
%% Allow the option reuseaddr to be present
%% so that new and old ssl can be run by the same
@@ -110,20 +143,21 @@ connect(Address, Port, Options0, Timeout) ->
%% that hardcodes reuseaddr to true in its portprogram.
Options1 = proplists:delete(reuseaddr, Options0),
Options = proplists:delete(ssl_imp, Options1),
- old_connect(Address, Port, Options, Timeout);
+ old_connect(Host, Port, Options, Timeout);
Value ->
{error, {eoptions, {ssl_imp, Value}}}
end.
%%--------------------------------------------------------------------
-%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason}
+-spec listen(port_num(), [option()]) ->{ok, #sslsocket{}} | {error, reason()}.
+
%%
%% Description: Creates a ssl listen socket.
%%--------------------------------------------------------------------
listen(_Port, []) ->
{error, enooptions};
listen(Port, Options0) ->
- case proplists:get_value(ssl_imp, Options0, old) of
+ case proplists:get_value(ssl_imp, Options0, new) of
new ->
new_listen(Port, Options0);
old ->
@@ -139,7 +173,10 @@ listen(Port, Options0) ->
end.
%%--------------------------------------------------------------------
-%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}.
+-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}} |
+ {error, reason()}.
+-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} |
+ {error, reason()}.
%%
%% Description: Performs transport accept on a ssl listen socket
%%--------------------------------------------------------------------
@@ -177,8 +214,10 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) ->
ssl_broker:transport_accept(Pid, ListenSocket, Timeout).
%%--------------------------------------------------------------------
-%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} |
-%% {error, Reason}
+-spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}.
+-spec ssl_accept(#sslsocket{} | port(), timeout()| [option()]) ->
+ ok | {ok, #sslsocket{}} | {error, reason()}.
+-spec ssl_accept(port(), [option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on a ssl listen socket. e.i. performs
%% ssl handshake.
@@ -205,14 +244,14 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
{ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} ->
{ok, Port} = inet:port(Socket),
ssl_connection:ssl_accept(Port, Socket,
- {SslOpts, EmOpts},
- self(), CbInfo, Timeout)
+ {SslOpts, EmOpts},
+ self(), CbInfo, Timeout)
catch
Error = {error, _Reason} -> Error
end.
%%--------------------------------------------------------------------
-%% Function: close() -> ok
+-spec close(#sslsocket{}) -> term().
%%
%% Description: Close a ssl connection
%%--------------------------------------------------------------------
@@ -225,7 +264,7 @@ close(Socket = #sslsocket{}) ->
ssl_broker:close(Socket).
%%--------------------------------------------------------------------
-%% Function: send(Socket, Data) -> ok | {error, Reason}
+-spec send(#sslsocket{}, iolist()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
@@ -237,7 +276,8 @@ send(#sslsocket{} = Socket, Data) ->
ssl_broker:send(Socket, Data).
%%--------------------------------------------------------------------
-%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason}
+-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}.
+-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}.
%%
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
@@ -251,8 +291,8 @@ recv(Socket = #sslsocket{}, Length, Timeout) ->
ssl_broker:recv(Socket, Length, Timeout).
%%--------------------------------------------------------------------
-%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason}
-%%
+-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}.
+%%
%% Description: Changes process that receives the messages when active = true
%% or once.
%%--------------------------------------------------------------------
@@ -265,11 +305,8 @@ controlling_process(Socket, NewOwner) when is_pid(NewOwner) ->
ssl_broker:controlling_process(Socket, NewOwner).
%%--------------------------------------------------------------------
-%% Function: connection_info(Socket) -> {ok, {Protocol, CipherSuite}} |
-%% {error, Reason}
-%% Protocol = sslv3 | tlsv1 | tlsv1.1
-%% CipherSuite = {KeyExchange, Chipher, Hash, Exportable}
-%%
+-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} |
+ {error, reason()}.
%%
%% Description: Returns ssl protocol and cipher used for the connection
%%--------------------------------------------------------------------
@@ -281,9 +318,9 @@ connection_info(#sslsocket{} = Socket) ->
ssl_broker:connection_info(Socket).
%%--------------------------------------------------------------------
-%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason}
+-spec peercert(#sslsocket{}) ->{ok, der_cert()} | {error, reason()}.
%%
-%% Description:
+%% Description: Returns the peercert.
%%--------------------------------------------------------------------
peercert(Socket) ->
peercert(Socket, []).
@@ -293,14 +330,7 @@ peercert(#sslsocket{pid = Pid, fd = new_ssl}, Opts) ->
{ok, undefined} ->
{error, no_peercert};
{ok, BinCert} ->
- PKOpts = [case Opt of ssl -> otp; pkix -> plain end ||
- Opt <- Opts, Opt =:= ssl orelse Opt =:= pkix],
- case PKOpts of
- [Opt] ->
- public_key:pkix_decode_cert(BinCert, Opt);
- [] ->
- {ok, BinCert}
- end;
+ decode_peercert(BinCert, Opts);
{error, Reason} ->
{error, Reason}
end;
@@ -309,15 +339,44 @@ peercert(#sslsocket{} = Socket, Opts) ->
ensure_old_ssl_started(),
case ssl_broker:peercert(Socket) of
{ok, Bin} ->
- ssl_pkix:decode_cert(Bin, Opts);
+ decode_peercert(Bin, Opts);
{error, Reason} ->
{error, Reason}
end.
+
+decode_peercert(BinCert, Opts) ->
+ PKOpts = [case Opt of ssl -> otp; pkix -> plain end ||
+ Opt <- Opts, Opt =:= ssl orelse Opt =:= pkix],
+ case PKOpts of
+ [Opt] ->
+ select_part(Opt, public_key:pkix_decode_cert(BinCert, Opt), Opts);
+ [] ->
+ {ok, BinCert}
+ end.
+
+select_part(otp, Cert, Opts) ->
+ case lists:member(subject, Opts) of
+ true ->
+ TBS = Cert#'OTPCertificate'.tbsCertificate,
+ {ok, TBS#'OTPTBSCertificate'.subject};
+ false ->
+ {ok, Cert}
+ end;
+
+select_part(plain, Cert, Opts) ->
+ case lists:member(subject, Opts) of
+ true ->
+ TBS = Cert#'Certificate'.tbsCertificate,
+ {ok, TBS#'TBSCertificate'.subject};
+ false ->
+ {ok, Cert}
+ end.
+
%%--------------------------------------------------------------------
-%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason}
+-spec peername(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}.
%%
-%% Description:
+%% Description: same as inet:peername/1.
%%--------------------------------------------------------------------
peername(#sslsocket{fd = new_ssl, pid = Pid}) ->
ssl_connection:peername(Pid);
@@ -327,9 +386,10 @@ peername(#sslsocket{} = Socket) ->
ssl_broker:peername(Socket).
%%--------------------------------------------------------------------
-%% Function: cipher_suites() ->
-%%
-%% Description:
+-spec cipher_suites() -> [erl_cipher_suite()].
+-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()].
+
+%% Description: Returns all supported cipher suites.
%%--------------------------------------------------------------------
cipher_suites() ->
cipher_suites(erlang).
@@ -343,7 +403,7 @@ cipher_suites(openssl) ->
[ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)].
%%--------------------------------------------------------------------
-%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason}
+-spec getopts(#sslsocket{}, [atom()]) -> {ok, [{atom(), term()}]}| {error, reason()}.
%%
%% Description:
%%--------------------------------------------------------------------
@@ -356,7 +416,7 @@ getopts(#sslsocket{} = Socket, Options) ->
ssl_broker:getopts(Socket, Options).
%%--------------------------------------------------------------------
-%% Function: setopts(Socket, Options) -> ok | {error, Reason}
+-spec setopts(#sslsocket{}, [{atom(), term()}]) -> ok | {error, reason()}.
%%
%% Description:
%%--------------------------------------------------------------------
@@ -371,8 +431,8 @@ setopts(#sslsocket{} = Socket, Options) ->
ssl_broker:setopts(Socket, Options).
%%---------------------------------------------------------------
-%% Function: shutdown(Socket, How) -> ok | {error, Reason}
-%%
+-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}.
+%%
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}, How) ->
@@ -381,8 +441,8 @@ shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) ->
ssl_connection:shutdown(Pid, How).
%%--------------------------------------------------------------------
-%% Function: sockname(Socket) -> {ok, {Address, Port}} | {error, Reason}
-%%
+-spec sockname(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}.
+%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) ->
@@ -396,9 +456,9 @@ sockname(#sslsocket{} = Socket) ->
ssl_broker:sockname(Socket).
%%---------------------------------------------------------------
-%% Function: seed(Data) -> ok | {error, edata}
+-spec seed(term()) ->term().
%%
-%% Description:
+%% Description: Only used by old ssl.
%%--------------------------------------------------------------------
%% TODO: crypto:seed ?
seed(Data) ->
@@ -406,20 +466,17 @@ seed(Data) ->
ssl_server:seed(Data).
%%---------------------------------------------------------------
-%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason}
+-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}.
%%
-%% Description:
+%% Description: Returns list of session info currently [{session_id, session_id(),
+%% {cipher_suite, cipher_suite()}]
%%--------------------------------------------------------------------
session_info(#sslsocket{pid = Pid, fd = new_ssl}) ->
ssl_connection:session_info(Pid).
%%---------------------------------------------------------------
-%% Function: versions() -> [{SslAppVer, SupportedSslVer, AvailableSslVsn}]
-%%
-%% SslAppVer = string() - t.ex: ssl-4.0
-%% SupportedSslVer = [SslVer]
-%% AvailableSslVsn = [SSLVer]
-%% SSLVer = sslv3 | tlsv1 | 'tlsv1.1'
+-spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} |
+ {available, [tls_atom_version()]}].
%%
%% Description: Returns a list of relevant versions.
%%--------------------------------------------------------------------
@@ -430,9 +487,105 @@ versions() ->
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+%%---------------------------------------------------------------
+-spec renegotiate(#sslsocket{}) -> ok | {error, reason()}.
+%%
+%% Description: Initiates a renegotiation.
+%%--------------------------------------------------------------------
renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
ssl_connection:renegotiation(Pid).
+%%---------------------------------------------------------------
+-spec format_error({error, term()}) -> list().
+%%
+%% Description: Creates error string.
+%%--------------------------------------------------------------------
+format_error({error, Reason}) ->
+ format_error(Reason);
+format_error(Reason) when is_list(Reason) ->
+ Reason;
+format_error(closed) ->
+ "The connection is closed";
+format_error(ecacertfile) ->
+ "Own CA certificate file is invalid.";
+format_error(ecertfile) ->
+ "Own certificate file is invalid.";
+format_error(ekeyfile) ->
+ "Own private key file is invalid.";
+format_error(esslaccept) ->
+ "Server SSL handshake procedure between client and server failed.";
+format_error(esslconnect) ->
+ "Client SSL handshake procedure between client and server failed.";
+format_error({eoptions, Options}) ->
+ lists:flatten(io_lib:format("Error in options list: ~p~n", [Options]));
+
+%%%%%%%%%%%% START OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+format_error(ebadsocket) ->
+ "Connection not found (internal error).";
+format_error(ebadstate) ->
+ "Connection not in connect state (internal error).";
+format_error(ebrokertype) ->
+ "Wrong broker type (internal error).";
+format_error(echaintoolong) ->
+ "The chain of certificates provided by peer is too long.";
+format_error(ecipher) ->
+ "Own list of specified ciphers is invalid.";
+format_error(ekeymismatch) ->
+ "Own private key does not match own certificate.";
+format_error(enoissuercert) ->
+ "Cannot find certificate of issuer of certificate provided by peer.";
+format_error(enoservercert) ->
+ "Attempt to do accept without having set own certificate.";
+format_error(enotlistener) ->
+ "Attempt to accept on a non-listening socket.";
+format_error(enoproxysocket) ->
+ "No proxy socket found (internal error or max number of file "
+ "descriptors exceeded).";
+format_error(enooptions) ->
+ "List of options is empty.";
+format_error(enotstarted) ->
+ "The SSL application has not been started.";
+format_error(eoptions) ->
+ "Invalid list of options.";
+format_error(epeercert) ->
+ "Certificate provided by peer is in error.";
+format_error(epeercertexpired) ->
+ "Certificate provided by peer has expired.";
+format_error(epeercertinvalid) ->
+ "Certificate provided by peer is invalid.";
+format_error(eselfsignedcert) ->
+ "Certificate provided by peer is self signed.";
+format_error(esslerrssl) ->
+ "SSL protocol failure. Typically because of a fatal alert from peer.";
+format_error(ewantconnect) ->
+ "Protocol wants to connect, which is not supported in this "
+ "version of the SSL application.";
+format_error(ex509lookup) ->
+ "Protocol wants X.509 lookup, which is not supported in this "
+ "version of the SSL application.";
+format_error({badcall, _Call}) ->
+ "Call not recognized for current mode (active or passive) and state "
+ "of socket.";
+format_error({badcast, _Cast}) ->
+ "Call not recognized for current mode (active or passive) and state "
+ "of socket.";
+
+format_error({badinfo, _Info}) ->
+ "Call not recognized for current mode (active or passive) and state "
+ "of socket.";
+
+%%%%%%%%%%%%%%%%%% END OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+format_error(Error) ->
+ case (catch inet:format_error(Error)) of
+ "unkknown POSIX" ++ _ ->
+ no_format(Error);
+ {'EXIT', _} ->
+ no_format(Error);
+ Other ->
+ Other
+ end.
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -488,59 +641,68 @@ old_listen(Port, Options) ->
{ok, Pid} = ssl_broker:start_broker(listener),
ssl_broker:listen(Pid, Port, Options).
-handle_options(Opts0, Role) ->
+handle_options(Opts0, _Role) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
ReuseSessionFun = fun(_, _, _, _) -> true end,
- AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc;
- (Other, Acc) -> [Other | Acc]
- end,
-
- VerifyFun =
- fun(ErrorList) ->
- case lists:foldl(AcceptBadCa, [], ErrorList) of
- [] -> true;
- [_|_] -> false
- end
- end,
+ DefaultVerifyNoneFun =
+ {fun(_,{bad_cert, _}, UserState) ->
+ {valid, UserState};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+
+ VerifyNoneFun = handle_option(verify_fun, Opts, DefaultVerifyNoneFun),
- UserFailIfNoPeerCert = validate_option(fail_if_no_peer_cert,
- proplists:get_value(fail_if_no_peer_cert, Opts, false)),
+ UserFailIfNoPeerCert = handle_option(fail_if_no_peer_cert, Opts, false),
+ UserVerifyFun = handle_option(verify_fun, Opts, undefined),
+ CaCerts = handle_option(cacerts, Opts, undefined),
- {Verify, FailIfNoPeerCert, CaCertDefault} =
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun} =
%% Handle 0, 1, 2 for backwards compatibility
case proplists:get_value(verify, Opts, verify_none) of
0 ->
- {verify_none, false, ca_cert_default(verify_none, Role)};
+ {verify_none, false,
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
1 ->
- {verify_peer, false, ca_cert_default(verify_peer, Role)};
+ {verify_peer, false,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
2 ->
- {verify_peer, true, ca_cert_default(verify_peer, Role)};
+ {verify_peer, true,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
verify_none ->
- {verify_none, false, ca_cert_default(verify_none, Role)};
+ {verify_none, false,
+ ca_cert_default(verify_none, VerifyNoneFun, CaCerts), VerifyNoneFun};
verify_peer ->
- {verify_peer, UserFailIfNoPeerCert, ca_cert_default(verify_peer, Role)};
+ {verify_peer, UserFailIfNoPeerCert,
+ ca_cert_default(verify_peer, UserVerifyFun, CaCerts), UserVerifyFun};
Value ->
throw({error, {eoptions, {verify, Value}}})
- end,
+ end,
CertFile = handle_option(certfile, Opts, ""),
SSLOptions = #ssl_options{
versions = handle_option(versions, Opts, []),
verify = validate_option(verify, Verify),
- verify_fun = handle_option(verify_fun, Opts, VerifyFun),
+ verify_fun = VerifyFun,
fail_if_no_peer_cert = FailIfNoPeerCert,
verify_client_once = handle_option(verify_client_once, Opts, false),
- validate_extensions_fun = handle_option(validate_extensions_fun, Opts, undefined),
depth = handle_option(depth, Opts, 1),
+ cert = handle_option(cert, Opts, undefined),
certfile = CertFile,
- keyfile = handle_option(keyfile, Opts, CertFile),
key = handle_option(key, Opts, undefined),
+ keyfile = handle_option(keyfile, Opts, CertFile),
password = handle_option(password, Opts, ""),
+ cacerts = CaCerts,
cacertfile = handle_option(cacertfile, Opts, CaCertDefault),
+ dh = handle_option(dh, Opts, undefined),
dhfile = handle_option(dhfile, Opts, undefined),
ciphers = handle_option(ciphers, Opts, []),
%% Server side option
@@ -548,16 +710,17 @@ handle_options(Opts0, Role) ->
reuse_sessions = handle_option(reuse_sessions, Opts, true),
secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
- debug = handle_option(debug, Opts, [])
+ debug = handle_option(debug, Opts, []),
+ hibernate_after = handle_option(hibernate_after, Opts, undefined)
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
- SslOptions = [versions, verify, verify_fun, validate_extensions_fun,
+ SslOptions = [versions, verify, verify_fun,
fail_if_no_peer_cert, verify_client_once,
- depth, certfile, keyfile,
- key, password, cacertfile, dhfile, ciphers,
+ depth, cert, certfile, key, keyfile,
+ password, cacerts, cacertfile, dh, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cb_info, renegotiate_at, secure_renegotiate],
+ cb_info, renegotiate_at, secure_renegotiate, hibernate_after],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -579,7 +742,25 @@ validate_option(ssl_imp, Value) when Value == new; Value == old ->
validate_option(verify, Value)
when Value == verify_none; Value == verify_peer ->
Value;
-validate_option(verify_fun, Value) when is_function(Value) ->
+validate_option(verify_fun, undefined) ->
+ undefined;
+%% Backwards compatibility
+validate_option(verify_fun, Fun) when is_function(Fun) ->
+ {fun(_,{bad_cert, _} = Reason, OldFun) ->
+ case OldFun([Reason]) of
+ true ->
+ {valid, OldFun};
+ false ->
+ {fail, Reason}
+ end;
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, Fun};
+validate_option(verify_fun, {Fun, _} = Value) when is_function(Fun) ->
Value;
validate_option(fail_if_no_peer_cert, Value)
when Value == true; Value == false ->
@@ -587,29 +768,38 @@ validate_option(fail_if_no_peer_cert, Value)
validate_option(verify_client_once, Value)
when Value == true; Value == false ->
Value;
-
-validate_option(validate_extensions_fun, Value) when Value == undefined; is_function(Value) ->
- Value;
validate_option(depth, Value) when is_integer(Value),
Value >= 0, Value =< 255->
Value;
-validate_option(certfile, Value) when is_list(Value) ->
+validate_option(cert, Value) when Value == undefined;
+ is_binary(Value) ->
Value;
-validate_option(keyfile, Value) when is_list(Value) ->
+validate_option(certfile, Value) when Value == undefined; is_list(Value) ->
Value;
-validate_option(key, Value) when Value == undefined;
- is_tuple(Value) ->
- %% element(1, Value)=='RSAPrivateKey' ->
+
+validate_option(key, undefined) ->
+ undefined;
+validate_option(key, {KeyType, Value}) when is_binary(Value),
+ KeyType == rsa;
+ KeyType == dsa ->
+ {KeyType, Value};
+validate_option(keyfile, Value) when is_list(Value) ->
Value;
validate_option(password, Value) when is_list(Value) ->
Value;
+validate_option(cacerts, Value) when Value == undefined;
+ is_list(Value) ->
+ Value;
%% certfile must be present in some cases otherwhise it can be set
%% to the empty string.
validate_option(cacertfile, undefined) ->
"";
validate_option(cacertfile, Value) when is_list(Value), Value =/= "" ->
Value;
+validate_option(dh, Value) when Value == undefined;
+ is_binary(Value) ->
+ Value;
validate_option(dhfile, undefined = Value) ->
Value;
validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
@@ -633,10 +823,14 @@ validate_option(secure_renegotiate, Value) when Value == true;
Value == false ->
Value;
validate_option(renegotiate_at, Value) when is_integer(Value) ->
- min(Value, ?DEFAULT_RENEGOTIATE_AT);
+ erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT);
validate_option(debug, Value) when is_list(Value); Value == true ->
Value;
+validate_option(hibernate_after, undefined) ->
+ undefined;
+validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
+ Value;
validate_option(Opt, Value) ->
throw({error, {eoptions, {Opt, Value}}}).
@@ -667,14 +861,16 @@ validate_inet_option(active, Value)
validate_inet_option(_, _) ->
ok.
-ca_cert_default(verify_none, _) ->
+%% The option cacerts overrides cacertsfile
+ca_cert_default(_,_, [_|_]) ->
undefined;
-%% Client may leave verification up to the user
-ca_cert_default(verify_peer, client) ->
+ca_cert_default(verify_none, _, _) ->
undefined;
-%% Server that wants to verify_peer must have
+ca_cert_default(verify_peer, {Fun,_}, _) when is_function(Fun) ->
+ undefined;
+%% Server that wants to verify_peer and has no verify_fun must have
%% some trusted certs.
-ca_cert_default(verify_peer, server) ->
+ca_cert_default(verify_peer, undefined, _) ->
"".
emulated_options() ->
@@ -718,11 +914,14 @@ emulated_options([], Inet,Emulated) ->
cipher_suites(Version, []) ->
ssl_cipher:suites(Version);
-cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) ->
+cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %% Backwards compatibility
+ Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher, Hash, _} <- Ciphers0],
+ cipher_suites(Version, Ciphers);
+cipher_suites(Version, [{_,_,_}| _] = Ciphers0) ->
Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0],
cipher_suites(Version, Ciphers);
cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) ->
- Supported = ssl_cipher:suites(Version),
+ Supported = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites(),
case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of
[] ->
Supported;
@@ -738,92 +937,6 @@ cipher_suites(Version, Ciphers0) ->
Ciphers = [ssl_cipher:openssl_suite(C) || C <- string:tokens(Ciphers0, ":")],
cipher_suites(Version, Ciphers).
-format_error({error, Reason}) ->
- format_error(Reason);
-format_error(Reason) when is_list(Reason) ->
- Reason;
-format_error(closed) ->
- "The connection is closed";
-format_error(ecacertfile) ->
- "Own CA certificate file is invalid.";
-format_error(ecertfile) ->
- "Own certificate file is invalid.";
-format_error(ekeyfile) ->
- "Own private key file is invalid.";
-format_error(esslaccept) ->
- "Server SSL handshake procedure between client and server failed.";
-format_error(esslconnect) ->
- "Client SSL handshake procedure between client and server failed.";
-format_error({eoptions, Options}) ->
- lists:flatten(io_lib:format("Error in options list: ~p~n", [Options]));
-
-%%%%%%%%%%%% START OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-format_error(ebadsocket) ->
- "Connection not found (internal error).";
-format_error(ebadstate) ->
- "Connection not in connect state (internal error).";
-format_error(ebrokertype) ->
- "Wrong broker type (internal error).";
-format_error(echaintoolong) ->
- "The chain of certificates provided by peer is too long.";
-format_error(ecipher) ->
- "Own list of specified ciphers is invalid.";
-format_error(ekeymismatch) ->
- "Own private key does not match own certificate.";
-format_error(enoissuercert) ->
- "Cannot find certificate of issuer of certificate provided by peer.";
-format_error(enoservercert) ->
- "Attempt to do accept without having set own certificate.";
-format_error(enotlistener) ->
- "Attempt to accept on a non-listening socket.";
-format_error(enoproxysocket) ->
- "No proxy socket found (internal error or max number of file "
- "descriptors exceeded).";
-format_error(enooptions) ->
- "List of options is empty.";
-format_error(enotstarted) ->
- "The SSL application has not been started.";
-format_error(eoptions) ->
- "Invalid list of options.";
-format_error(epeercert) ->
- "Certificate provided by peer is in error.";
-format_error(epeercertexpired) ->
- "Certificate provided by peer has expired.";
-format_error(epeercertinvalid) ->
- "Certificate provided by peer is invalid.";
-format_error(eselfsignedcert) ->
- "Certificate provided by peer is self signed.";
-format_error(esslerrssl) ->
- "SSL protocol failure. Typically because of a fatal alert from peer.";
-format_error(ewantconnect) ->
- "Protocol wants to connect, which is not supported in this "
- "version of the SSL application.";
-format_error(ex509lookup) ->
- "Protocol wants X.509 lookup, which is not supported in this "
- "version of the SSL application.";
-format_error({badcall, _Call}) ->
- "Call not recognized for current mode (active or passive) and state "
- "of socket.";
-format_error({badcast, _Cast}) ->
- "Call not recognized for current mode (active or passive) and state "
- "of socket.";
-
-format_error({badinfo, _Info}) ->
- "Call not recognized for current mode (active or passive) and state "
- "of socket.";
-
-%%%%%%%%%%%%%%%%%% END OLD SSL format_error %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-format_error(Error) ->
- case (catch inet:format_error(Error)) of
- "unkknown POSIX" ++ _ ->
- no_format(Error);
- {'EXIT', _} ->
- no_format(Error);
- Other ->
- Other
- end.
-
no_format(Error) ->
lists:flatten(io_lib:format("No format string for error: \"~p\" available.", [Error])).
@@ -860,10 +973,6 @@ version() ->
end,
{ok, {SSLVsn, CompVsn, LibVsn}}.
-min(N,M) when N < M ->
- N;
-min(_, M) ->
- M.
%% Only used to remove exit messages from old ssl
%% First is a nonsense clause to provide some
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index db9a883654..eb1228afa4 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -32,6 +32,17 @@
-export([alert_txt/1, reason_code/2]).
+%%====================================================================
+%% Internal application API
+%%====================================================================
+%%--------------------------------------------------------------------
+-spec reason_code(#alert{}, client | server) -> closed | esslconnect |
+ esslaccept | string().
+%%
+%% Description: Returns the error reason that will be returned to the
+%% user.
+%%--------------------------------------------------------------------
+
reason_code(#alert{description = ?CLOSE_NOTIFY}, _) ->
closed;
reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) ->
@@ -41,10 +52,19 @@ reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) ->
reason_code(#alert{description = Description}, _) ->
description_txt(Description).
+%%--------------------------------------------------------------------
+-spec alert_txt(#alert{}) -> string().
+%%
+%% Description: Returns the error string for given alert.
+%%--------------------------------------------------------------------
+
alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}}) ->
Mod ++ ":" ++ integer_to_list(Line) ++ ":" ++
level_txt(Level) ++" "++ description_txt(Description).
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
level_txt(?WARNING) ->
"Warning:";
level_txt(?FATAL) ->
@@ -96,8 +116,3 @@ description_txt(?USER_CANCELED) ->
"user canceled";
description_txt(?NO_RENEGOTIATION) ->
"no renegotiation".
-
-
-
-
-
diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl
index 6ca1c42631..c9f81726b9 100644
--- a/lib/ssl/src/ssl_app.erl
+++ b/lib/ssl/src/ssl_app.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,14 +27,16 @@
-export([start/2, stop/1]).
-%% start/2(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} |
-%% {error, Reason}
-%%
+%%--------------------------------------------------------------------
+-spec start(normal | {takeover, node()} | {failover, node()}, list()) ->
+ ignore | {ok, pid()} | {error, term()}.
+%%--------------------------------------------------------------------
start(_Type, _StartArgs) ->
ssl_sup:start_link().
-%% stop(State) -> void()
-%%
+%--------------------------------------------------------------------
+-spec stop(term())-> ok.
+%%--------------------------------------------------------------------
stop(_State) ->
ok.
diff --git a/lib/ssl/src/ssl_base64.erl b/lib/ssl/src/ssl_base64.erl
deleted file mode 100644
index cfc42407e8..0000000000
--- a/lib/ssl/src/ssl_base64.erl
+++ /dev/null
@@ -1,129 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-
-%%% Purpose : Base 64 encoding and decoding.
-
--module(ssl_base64).
-
--export([encode/1, encode_split/1, decode/1, join_decode/1]).
-
--define(st(X,A), ((X-A+256) div 256)).
--define(CHARS, 64).
-
-%% A PEM encoding consists of characters A-Z, a-z, 0-9, +, / and
-%% =. Each character encodes a 6 bits value from 0 to 63 (A = 0, / =
-%% 63); = is a padding character.
-%%
-
-%%
-%% encode(Bytes|Binary) -> Chars
-%%
-%% Take 3 bytes a time (3 x 8 = 24 bits), and make 4 characters out of
-%% them (4 x 6 = 24 bits).
-%%
-encode(Bs) when is_list(Bs) ->
- encode(list_to_binary(Bs));
-encode(<<B:3/binary, Bs/binary>>) ->
- <<C1:6, C2:6, C3:6, C4:6>> = B,
- [enc(C1), enc(C2), enc(C3), enc(C4)| encode(Bs)];
-encode(<<B:2/binary>>) ->
- <<C1:6, C2:6, C3:6, _:6>> = <<B/binary, 0>>,
- [enc(C1), enc(C2), enc(C3), $=];
-encode(<<B:1/binary>>) ->
- <<C1:6, C2:6, _:12>> = <<B/binary, 0, 0>>,
- [enc(C1), enc(C2), $=, $=];
-encode(<<>>) ->
- [].
-
-%%
-%% encode_split(Bytes|Binary) -> Lines
-%%
-%% The encoding is divided into lines separated by <NL>, and each line
-%% is precisely 64 characters long (excluding the <NL> characters,
-%% except the last line which 64 characters long or shorter. <NL> may
-%% follow the last line.
-%%
-encode_split(Bs) ->
- split(encode(Bs)).
-
-%%
-%% decode(Chars) -> Binary
-%%
-decode(Cs) ->
- list_to_binary(decode1(Cs)).
-
-decode1([C1, C2, $=, $=]) ->
- <<B1, _:16>> = <<(dec(C1)):6, (dec(C2)):6, 0:12>>,
- [B1];
-decode1([C1, C2, C3, $=]) ->
- <<B1, B2, _:8>> = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(0)):6>>,
- [B1, B2];
-decode1([C1, C2, C3, C4| Cs]) ->
- Bin = <<(dec(C1)):6, (dec(C2)):6, (dec(C3)):6, (dec(C4)):6>>,
- [Bin| decode1(Cs)];
-decode1([]) ->
- [].
-
-%%
-%% join_decode(Lines) -> Binary
-%%
-%% Remove <NL> before decoding.
-%%
-join_decode(Cs) ->
- decode(join(Cs)).
-
-%%
-%% Locals
-%%
-
-%% enc/1 and dec/1
-%%
-%% Mapping: 0-25 -> A-Z, 26-51 -> a-z, 52-61 -> 0-9, 62 -> +, 63 -> /
-%%
-enc(C) ->
- 65 + C + 6*?st(C,26) - 75*?st(C,52) -15*?st(C,62) + 3*?st(C,63).
-
-dec(C) ->
- 62*?st(C,43) + ?st(C,47) + (C-59)*?st(C,48) - 69*?st(C,65) - 6*?st(C,97).
-
-%% split encoding into lines
-%%
-split(Cs) ->
- split(Cs, ?CHARS).
-
-split([], _N) ->
- [$\n];
-split(Cs, 0) ->
- [$\n| split(Cs, ?CHARS)];
-split([C| Cs], N) ->
- [C| split(Cs, N-1)].
-
-%% join lines of encodings
-%%
-join([$\r, $\n| Cs]) ->
- join(Cs);
-join([$\n| Cs]) ->
- join(Cs);
-join([C| Cs]) ->
- [C| join(Cs)];
-join([]) ->
- [].
-
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 686e90a70c..8c0c2bfa5d 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -28,91 +28,155 @@
-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
--include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([trusted_cert_and_path/3,
+-export([trusted_cert_and_path/2,
certificate_chain/2,
file_to_certificats/1,
- validate_extensions/6]).
+ validate_extension/3,
+ is_valid_extkey_usage/2,
+ is_valid_key_usage/2,
+ select_extension/2,
+ extensions_list/1,
+ signature_type/1
+ ]).
%%====================================================================
%% Internal application API
%%====================================================================
-trusted_cert_and_path(CertChain, CertDbRef, Verify) ->
- [Cert | RestPath] = lists:reverse(CertChain),
- {ok, OtpCert} = public_key:pkix_decode_cert(Cert, otp),
- IssuerAnPath =
+%%--------------------------------------------------------------------
+-spec trusted_cert_and_path([der_cert()], certdb_ref()) ->
+ {der_cert() | unknown_ca, [der_cert()]}.
+%%
+%% Description: Extracts the root cert (if not presents tries to
+%% look it up, if not found {bad_cert, unknown_ca} will be added verification
+%% errors. Returns {RootCert, Path, VerifyErrors}
+%%--------------------------------------------------------------------
+trusted_cert_and_path(CertChain, CertDbRef) ->
+ Path = [Cert | _] = lists:reverse(CertChain),
+ OtpCert = public_key:pkix_decode_cert(Cert, otp),
+ SignedAndIssuerID =
case public_key:pkix_is_self_signed(OtpCert) of
true ->
{ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
- {IssuerId, RestPath};
- false ->
+ {self, IssuerId};
+ false ->
case public_key:pkix_issuer_id(OtpCert, other) of
{ok, IssuerId} ->
- {IssuerId, [Cert | RestPath]};
+ {other, IssuerId};
{error, issuer_not_found} ->
case find_issuer(OtpCert, no_candidate) of
{ok, IssuerId} ->
- {IssuerId, [Cert | RestPath]};
+ {other, IssuerId};
Other ->
- {Other, RestPath}
+ Other
end
end
end,
- case IssuerAnPath of
- {{error, issuer_not_found}, _ } ->
- %% The root CA was not sent and can not be found, we fail if verify = true
- not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA), Verify, {Cert, RestPath});
- {{SerialNr, Issuer}, Path} ->
- case ssl_certificate_db:lookup_trusted_cert(CertDbRef,
- SerialNr, Issuer) of
+ case SignedAndIssuerID of
+ {error, issuer_not_found} ->
+ %% The root CA was not sent and can not be found.
+ {unknown_ca, Path};
+ {self, _} when length(Path) == 1 ->
+ {selfsigned_peer, Path};
+ {_ ,{SerialNr, Issuer}} ->
+ case ssl_manager:lookup_trusted_cert(CertDbRef, SerialNr, Issuer) of
{ok, {BinCert,_}} ->
- {BinCert, Path, []};
+ {BinCert, Path};
_ ->
- %% Fail if verify = true
- not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA),
- Verify, {Cert, RestPath})
+ %% Root CA could not be verified
+ {unknown_ca, Path}
end
end.
-
+%%--------------------------------------------------------------------
+-spec certificate_chain(undefined | binary(), certdb_ref()) ->
+ {error, no_cert} | {ok, [der_cert()]}.
+%%
+%% Description: Return the certificate chain to send to peer.
+%%--------------------------------------------------------------------
certificate_chain(undefined, _CertsDbRef) ->
{error, no_cert};
certificate_chain(OwnCert, CertsDbRef) ->
- {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp),
+ ErlCert = public_key:pkix_decode_cert(OwnCert, otp),
certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]).
-
-file_to_certificats(File) ->
+%%--------------------------------------------------------------------
+-spec file_to_certificats(string()) -> [der_cert()].
+%%
+%% Description: Return list of DER encoded certificates.
+%%--------------------------------------------------------------------
+file_to_certificats(File) ->
{ok, List} = ssl_manager:cache_pem_file(File),
- [Bin || {cert, Bin, not_encrypted} <- List].
-
-
-%% Validates ssl/tls specific extensions
-validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) ->
- {UnknownExtensions, ValidationState, AccErr};
-
-validate_extensions([#'Extension'{extnID = ?'id-ce-extKeyUsage',
- extnValue = KeyUse,
- critical = true} | Rest],
- ValidationState, UnknownExtensions, Verify, AccErr0, Role) ->
+ [Bin || {'Certificate', Bin, not_encrypted} <- List].
+%%--------------------------------------------------------------------
+-spec validate_extension(term(), #'Extension'{} | {bad_cert, atom()} | valid,
+ term()) -> {valid, term()} |
+ {fail, tuple()} |
+ {unknown, term()}.
+%%
+%% Description: Validates ssl/tls specific extensions
+%%--------------------------------------------------------------------
+validate_extension(_,{extension, #'Extension'{extnID = ?'id-ce-extKeyUsage',
+ extnValue = KeyUse}}, Role) ->
case is_valid_extkey_usage(KeyUse, Role) of
true ->
- validate_extensions(Rest, ValidationState, UnknownExtensions,
- Verify, AccErr0, Role);
+ {valid, Role};
false ->
- AccErr =
- not_valid_extension({bad_cert, invalid_ext_key_usage}, Verify, AccErr0),
- validate_extensions(Rest, ValidationState, UnknownExtensions, Verify, AccErr, Role)
+ {fail, {bad_cert, invalid_ext_key_usage}}
end;
+validate_extension(_, {bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+validate_extension(_, {extension, _}, Role) ->
+ {unknown, Role};
+validate_extension(_, valid, Role) ->
+ {valid, Role};
+validate_extension(_, valid_peer, Role) ->
+ {valid, Role}.
+
+%%--------------------------------------------------------------------
+-spec is_valid_key_usage(list(), term()) -> boolean().
+%%
+%% Description: Checks if Use is a valid key usage.
+%%--------------------------------------------------------------------
+is_valid_key_usage(KeyUse, Use) ->
+ lists:member(Use, KeyUse).
+
+%%--------------------------------------------------------------------
+-spec select_extension(term(), list()) -> undefined | #'Extension'{}.
+%%
+%% Description: Selects the extension identified by Id if present in
+%% a list of extensions.
+%%--------------------------------------------------------------------
+select_extension(_, []) ->
+ undefined;
+select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) ->
+ Extension;
+select_extension(Id, [_ | Extensions]) ->
+ select_extension(Id, Extensions).
+
+%%--------------------------------------------------------------------
+-spec extensions_list(asn1_NOVALUE | list()) -> list().
+%%
+%% Description: Handles that
+%%--------------------------------------------------------------------
+extensions_list(asn1_NOVALUE) ->
+ [];
+extensions_list(Extensions) ->
+ Extensions.
+
+%%--------------------------------------------------------------------
+-spec signature_type(term()) -> rsa | dsa .
+%%
+%% Description:
+%%--------------------------------------------------------------------
+signature_type(RSA) when RSA == ?sha1WithRSAEncryption;
+ RSA == ?md5WithRSAEncryption ->
+ rsa;
+signature_type(?'id-dsa-with-sha1') ->
+ dsa.
-validate_extensions([Extension | Rest], ValidationState, UnknownExtensions,
- Verify, AccErr, Role) ->
- validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions],
- Verify, AccErr, Role).
-
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -148,10 +212,10 @@ certificate_chain(_CertsDbRef, Chain, _SerialNr, _Issuer, true) ->
{ok, lists:reverse(Chain)};
certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
- case ssl_certificate_db:lookup_trusted_cert(CertsDbRef,
+ case ssl_manager:lookup_trusted_cert(CertsDbRef,
SerialNr, Issuer) of
{ok, {IssuerCert, ErlCert}} ->
- {ok, ErlCert} = public_key:pkix_decode_cert(IssuerCert, otp),
+ ErlCert = public_key:pkix_decode_cert(IssuerCert, otp),
certificate_chain(ErlCert, IssuerCert,
CertsDbRef, [IssuerCert | Chain]);
_ ->
@@ -164,7 +228,7 @@ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
end.
find_issuer(OtpCert, PrevCandidateKey) ->
- case ssl_certificate_db:issuer_candidate(PrevCandidateKey) of
+ case ssl_manager:issuer_candidate(PrevCandidateKey) of
no_more_candidates ->
{error, issuer_not_found};
{Key, {_Cert, ErlCertCandidate}} ->
@@ -176,22 +240,9 @@ find_issuer(OtpCert, PrevCandidateKey) ->
end
end.
-not_valid(Alert, true, _) ->
- throw(Alert);
-not_valid(_, false, {ErlCert, Path}) ->
- {ErlCert, Path, [{bad_cert, unknown_ca}]}.
-
is_valid_extkey_usage(KeyUse, client) ->
%% Client wants to verify server
is_valid_key_usage(KeyUse,?'id-kp-serverAuth');
is_valid_extkey_usage(KeyUse, server) ->
%% Server wants to verify client
is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
-
-is_valid_key_usage(KeyUse, Use) ->
- lists:member(Use, KeyUse).
-
-not_valid_extension(Error, true, _) ->
- throw(Error);
-not_valid_extension(Error, false, AccErrors) ->
- [Error | AccErrors].
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index b8c3c6f6b7..3eceefa304 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,20 +22,21 @@
%%----------------------------------------------------------------------
-module(ssl_certificate_db).
-
+-include("ssl_internal.hrl").
-include_lib("public_key/include/public_key.hrl").
-export([create/0, remove/1, add_trusted_certs/3,
remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1,
- lookup_cached_certs/1, cache_pem_file/3]).
+ lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]).
+
+-type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}.
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: create() -> Db
-%% Db = term() - Reference to the crated database
+-spec create() -> certdb_ref().
%%
%% Description: Creates a new certificate db.
%% Note: lookup_trusted_cert/3 may be called from any process but only
@@ -47,8 +48,7 @@ create() ->
ets:new(ssl_pid_to_file, [bag, private])].
%%--------------------------------------------------------------------
-%% Function: delete(Db) -> _
-%% Db = Database refererence as returned by create/0
+-spec remove(certdb_ref()) -> term().
%%
%% Description: Removes database db
%%--------------------------------------------------------------------
@@ -56,11 +56,9 @@ remove(Dbs) ->
lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs).
%%--------------------------------------------------------------------
-%% Function: lookup_trusted_cert(Ref, SerialNumber, Issuer) -> {BinCert,DecodedCert}
-%% Ref = ref()
-%% SerialNumber = integer()
-%% Issuer = {rdnSequence, IssuerAttrs}
-%% BinCert = binary()
+-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) ->
+ undefined | {ok, {der_cert(), #'OTPCertificate'{}}}.
+
%%
%% Description: Retrives the trusted certificate identified by
%% <SerialNumber, Issuer>. Ref is used as it is specified
@@ -78,16 +76,16 @@ lookup_cached_certs(File) ->
ets:lookup(certificate_db_name(), {file, File}).
%%--------------------------------------------------------------------
-%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref}
-%% Pid = pid()
-%% File = string()
-%% Db = Database refererence as returned by create/0
-%% Ref = ref()
+-spec add_trusted_certs(pid(), string() | {der, list()}, certdb_ref()) -> {ok, certdb_ref()}.
%%
%% Description: Adds the trusted certificates from file <File> to the
%% runtime database. Returns Ref that should be handed to lookup_trusted_cert
%% together with the cert serialnumber and issuer.
%%--------------------------------------------------------------------
+add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) ->
+ NewRef = make_ref(),
+ add_certs_from_der(DerList, NewRef, CerDb),
+ {ok, NewRef};
add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
Ref = case lookup(File, FileToRefDb) of
undefined ->
@@ -101,20 +99,39 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
end,
insert(Pid, File, PidToFileDb),
{ok, Ref}.
-
%%--------------------------------------------------------------------
-%% Function: cache_pem_file(Pid, File, Db) -> FileContent
+-spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term().
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
-cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) ->
- Res = {ok, Content} = public_key:pem_to_der(File),
- insert({file, File}, Content, CertsDb),
+cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) ->
+ {ok, PemBin} = file:read_file(File),
+ Content = public_key:pem_decode(PemBin),
+ insert({file, File}, {Time, Content}, CertsDb),
insert(Pid, File, PidToFileDb),
- Res.
+ {ok, Content}.
+
+%--------------------------------------------------------------------
+-spec uncache_pem_file(string(), certdb_ref()) -> no_return().
+%%
+%% Description: If a cached file is no longer valid (changed on disk)
+%% we must terminate the connections using the old file content, and
+%% when those processes are finish the cache will be cleaned. It is
+%% a rare but possible case a new ssl client/server is started with
+%% a filename with the same name as previously started client/server
+%% but with different content.
+%% --------------------------------------------------------------------
+uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) ->
+ Pids = select(PidToFileDb, [{{'$1', File},[],['$$']}]),
+ lists:foreach(fun([Pid]) ->
+ exit(Pid, shutdown)
+ end, Pids).
+
+
%%--------------------------------------------------------------------
-%% Function: remove_trusted_certs(Pid, Db) -> _
+-spec remove_trusted_certs(pid(), certdb_ref()) -> term().
+
%%
%% Description: Removes trusted certs originating from
%% the file associated to Pid from the runtime database.
@@ -144,15 +161,13 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
end.
%%--------------------------------------------------------------------
-%% Function: issuer_candidate() -> {Key, Candidate} | no_more_candidates
+-spec issuer_candidate(no_candidate | cert_key() | {file, term()}) ->
+ {cert_key(),{der_cert(), #'OTPCertificate'{}}} | no_more_candidates.
%%
-%% Candidate
-%%
-%%
%% Description: If a certificat does not define its issuer through
%% the extension 'ce-authorityKeyIdentifier' we can
%% try to find the issuer in the database over known
-%% certificates.
+%% certificates.
%%--------------------------------------------------------------------
issuer_candidate(no_candidate) ->
Db = certificate_db_name(),
@@ -179,6 +194,22 @@ issuer_candidate(PrevCandidateKey) ->
end.
%%--------------------------------------------------------------------
+-spec lookup(term(), term()) -> term() | undefined.
+%%
+%% Description: Looks up an element in a certificat <Db>.
+%%--------------------------------------------------------------------
+lookup(Key, Db) ->
+ case ets:lookup(Db, Key) of
+ [] ->
+ undefined;
+ Contents ->
+ Pick = fun({_, Data}) -> Data;
+ ({_,_,Data}) -> Data
+ end,
+ [Pick(Data) || Data <- Contents]
+ end.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
certificate_db_name() ->
@@ -196,29 +227,32 @@ ref_count(Key, Db,N) ->
delete(Key, Db) ->
_ = ets:delete(Db, Key).
-lookup(Key, Db) ->
- case ets:lookup(Db, Key) of
- [] ->
- undefined;
- Contents ->
- Pick = fun({_, Data}) -> Data;
- ({_,_,Data}) -> Data
- end,
- [Pick(Data) || Data <- Contents]
- end.
+select(Db, MatchSpec)->
+ ets:select(Db, MatchSpec).
remove_certs(Ref, CertsDb) ->
ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}).
+add_certs_from_der(DerList, Ref, CertsDb) ->
+ Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
+ [Add(Cert) || Cert <- DerList].
+
add_certs_from_file(File, Ref, CertsDb) ->
- Decode = fun(Cert) ->
- {ok, ErlCert} = public_key:pkix_decode_cert(Cert, otp),
- TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
- SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
- Issuer = public_key:pkix_normalize_general_name(
- TBSCertificate#'OTPTBSCertificate'.issuer),
- insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb)
- end,
- {ok,Der} = public_key:pem_to_der(File),
- [Decode(Cert) || {cert, Cert, not_encrypted} <- Der].
+ Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end,
+ {ok, PemBin} = file:read_file(File),
+ PemEntries = public_key:pem_decode(PemBin),
+ [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries].
+add_certs(Cert, Ref, CertsDb) ->
+ try ErlCert = public_key:pkix_decode_cert(Cert, otp),
+ TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate,
+ SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber,
+ Issuer = public_key:pkix_normalize_name(
+ TBSCertificate#'OTPTBSCertificate'.issuer),
+ insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb)
+ catch
+ error:_ ->
+ Report = io_lib:format("SSL WARNING: Ignoring a CA cert as "
+ "it could not be correctly decoded.~n", []),
+ error_logger:info_report(Report)
+ end.
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 3d3d11b7f3..72f02a4362 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -28,27 +28,25 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
--include("ssl_debug.hrl").
+-include("ssl_alert.hrl").
+-include_lib("public_key/include/public_key.hrl").
-export([security_parameters/2, suite_definition/1,
- decipher/4, cipher/4,
- suite/1, suites/1,
- openssl_suite/1, openssl_suite_name/1]).
+ decipher/5, cipher/4,
+ suite/1, suites/1, anonymous_suites/0,
+ openssl_suite/1, openssl_suite_name/1, filter/2]).
-compile(inline).
%%--------------------------------------------------------------------
-%% Function: security_parameters(CipherSuite, SecParams) ->
-%% #security_parameters{}
-%%
-%% CipherSuite - as defined in ssl_cipher.hrl
-%% SecParams - #security_parameters{}
+-spec security_parameters(cipher_suite(), #security_parameters{}) ->
+ #security_parameters{}.
%%
%% Description: Returns a security parameters record where the
%% cipher values has been updated according to <CipherSuite>
%%-------------------------------------------------------------------
security_parameters(CipherSuite, SecParams) ->
- { _, Cipher, Hash, Exportable} = suite_definition(CipherSuite),
+ { _, Cipher, Hash} = suite_definition(CipherSuite),
SecParams#security_parameters{
cipher_suite = CipherSuite,
bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
@@ -58,19 +56,14 @@ security_parameters(CipherSuite, SecParams) ->
key_material_length = key_material(Cipher),
iv_size = iv_size(Cipher),
mac_algorithm = mac_algorithm(Hash),
- hash_size = hash_size(Hash),
- exportable = Exportable}.
+ hash_size = hash_size(Hash)}.
%%--------------------------------------------------------------------
-%% Function: cipher(Method, CipherState, Mac, Data) ->
-%% {Encrypted, UpdateCipherState}
+-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) ->
+ {binary(), #cipher_state{}}.
%%
-%% Method - integer() (as defined in ssl_cipher.hrl)
-%% CipherState, UpdatedCipherState - #cipher_state{}
-%% Data, Encrypted - binary()
-%%
-%% Description: Encrypts the data and the mac using method, updating
-%% the cipher state
+%% Description: Encrypts the data and the MAC using chipher described
+%% by cipher_enum() and updating the cipher state
%%-------------------------------------------------------------------
cipher(?NULL, CipherState, <<>>, Fragment) ->
GenStreamCipherList = [Fragment, <<>>],
@@ -81,20 +74,12 @@ cipher(?RC4, CipherState, Mac, Fragment) ->
S -> S
end,
GenStreamCipherList = [Fragment, Mac],
-
- ?DBG_HEX(GenStreamCipherList),
- ?DBG_HEX(State0),
{State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList),
- ?DBG_HEX(T),
{T, CipherState#cipher_state{state = State1}};
cipher(?DES, CipherState, Mac, Fragment) ->
block_cipher(fun(Key, IV, T) ->
crypto:des_cbc_encrypt(Key, IV, T)
end, block_size(des_cbc), CipherState, Mac, Fragment);
-cipher(?DES40, CipherState, Mac, Fragment) ->
- block_cipher(fun(Key, IV, T) ->
- crypto:des_cbc_encrypt(Key, IV, T)
- end, block_size(des_cbc), CipherState, Mac, Fragment);
cipher(?'3DES', CipherState, Mac, Fragment) ->
block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_encrypt(K1, K2, K3, IV, T)
@@ -104,101 +89,94 @@ cipher(?AES, CipherState, Mac, Fragment) ->
crypto:aes_cbc_128_encrypt(Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_encrypt(Key, IV, T)
- end, block_size(aes_128_cbc), CipherState, Mac, Fragment);
+ end, block_size(aes_128_cbc), CipherState, Mac, Fragment).
%% cipher(?IDEA, CipherState, Mac, Fragment) ->
%% block_cipher(fun(Key, IV, T) ->
%% crypto:idea_cbc_encrypt(Key, IV, T)
%% end, block_size(idea_cbc), CipherState, Mac, Fragment);
-cipher(?RC2, CipherState, Mac, Fragment) ->
- block_cipher(fun(Key, IV, T) ->
- crypto:rc2_40_cbc_encrypt(Key, IV, T)
- end, block_size(rc2_cbc_40), CipherState, Mac, Fragment).
block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0,
Mac, Fragment) ->
TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
{PaddingLength, Padding} = get_padding(TotSz, BlockSz),
L = [Fragment, Mac, PaddingLength, Padding],
- ?DBG_HEX(Key),
- ?DBG_HEX(IV),
- ?DBG_HEX(L),
T = Fun(Key, IV, L),
- ?DBG_HEX(T),
NextIV = next_iv(T, IV),
{T, CS0#cipher_state{iv=NextIV}}.
%%--------------------------------------------------------------------
-%% Function: decipher(Method, CipherState, Mac, Data) ->
-%% {Decrypted, UpdateCipherState}
-%%
-%% Method - integer() (as defined in ssl_cipher.hrl)
-%% CipherState, UpdatedCipherState - #cipher_state{}
-%% Data, Encrypted - binary()
+-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) ->
+ {binary(), binary(), #cipher_state{}} | #alert{}.
%%
-%% Description: Decrypts the data and the mac using method, updating
-%% the cipher state
+%% Description: Decrypts the data and the MAC using cipher described
+%% by cipher_enum() and updating the cipher state.
%%-------------------------------------------------------------------
-decipher(?NULL, _HashSz, CipherState, Fragment) ->
+decipher(?NULL, _HashSz, CipherState, Fragment, _) ->
{Fragment, <<>>, CipherState};
-decipher(?RC4, HashSz, CipherState, Fragment) ->
- ?DBG_TERM(CipherState#cipher_state.key),
+decipher(?RC4, HashSz, CipherState, Fragment, _) ->
State0 = case CipherState#cipher_state.state of
undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
S -> S
end,
- ?DBG_HEX(State0),
- ?DBG_HEX(Fragment),
- {State1, T} = crypto:rc4_encrypt_with_state(State0, Fragment),
- ?DBG_HEX(T),
- GSC = generic_stream_cipher_from_bin(T, HashSz),
- #generic_stream_cipher{content=Content, mac=Mac} = GSC,
- {Content, Mac, CipherState#cipher_state{state=State1}};
-decipher(?DES, HashSz, CipherState, Fragment) ->
- block_decipher(fun(Key, IV, T) ->
- crypto:des_cbc_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment);
-decipher(?DES40, HashSz, CipherState, Fragment) ->
+ try crypto:rc4_encrypt_with_state(State0, Fragment) of
+ {State, Text} ->
+ GSC = generic_stream_cipher_from_bin(Text, HashSz),
+ #generic_stream_cipher{content = Content, mac = Mac} = GSC,
+ {Content, Mac, CipherState#cipher_state{state = State}}
+ catch
+ _:_ ->
+ %% This is a DECRYPTION_FAILED but
+ %% "differentiating between bad_record_mac and decryption_failed
+ %% alerts may permit certain attacks against CBC mode as used in
+ %% TLS [CBCATT]. It is preferable to uniformly use the
+ %% bad_record_mac alert to hide the specific type of the error."
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+
+decipher(?DES, HashSz, CipherState, Fragment, Version) ->
block_decipher(fun(Key, IV, T) ->
crypto:des_cbc_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment);
-decipher(?'3DES', HashSz, CipherState, Fragment) ->
+ end, CipherState, HashSz, Fragment, Version);
+decipher(?'3DES', HashSz, CipherState, Fragment, Version) ->
block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
crypto:des3_cbc_decrypt(K1, K2, K3, IV, T)
- end, CipherState, HashSz, Fragment);
-decipher(?AES, HashSz, CipherState, Fragment) ->
+ end, CipherState, HashSz, Fragment, Version);
+decipher(?AES, HashSz, CipherState, Fragment, Version) ->
block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
crypto:aes_cbc_128_decrypt(Key, IV, T);
(Key, IV, T) when byte_size(Key) =:= 32 ->
crypto:aes_cbc_256_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment);
-%% decipher(?IDEA, HashSz, CipherState, Fragment) ->
+ end, CipherState, HashSz, Fragment, Version).
+%% decipher(?IDEA, HashSz, CipherState, Fragment, Version) ->
%% block_decipher(fun(Key, IV, T) ->
%% crypto:idea_cbc_decrypt(Key, IV, T)
-%% end, CipherState, HashSz, Fragment);
-decipher(?RC2, HashSz, CipherState, Fragment) ->
- block_decipher(fun(Key, IV, T) ->
- crypto:rc2_40_cbc_decrypt(Key, IV, T)
- end, CipherState, HashSz, Fragment).
+%% end, CipherState, HashSz, Fragment, Version);
block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0,
- HashSz, Fragment) ->
- ?DBG_HEX(Key),
- ?DBG_HEX(IV),
- ?DBG_HEX(Fragment),
- T = Fun(Key, IV, Fragment),
- ?DBG_HEX(T),
- GBC = generic_block_cipher_from_bin(T, HashSz),
- ok = check_padding(GBC), %% TODO kolla ocks�...
- Content = GBC#generic_block_cipher.content,
- Mac = GBC#generic_block_cipher.mac,
- CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
- {Content, Mac, CipherState1}.
-
+ HashSz, Fragment, Version) ->
+ try Fun(Key, IV, Fragment) of
+ Text ->
+ GBC = generic_block_cipher_from_bin(Text, HashSz),
+ case is_correct_padding(GBC, Version) of
+ true ->
+ Content = GBC#generic_block_cipher.content,
+ Mac = GBC#generic_block_cipher.mac,
+ CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
+ {Content, Mac, CipherState1};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end
+ catch
+ _:_ ->
+ %% This is a DECRYPTION_FAILED but
+ %% "differentiating between bad_record_mac and decryption_failed
+ %% alerts may permit certain attacks against CBC mode as used in
+ %% TLS [CBCATT]. It is preferable to uniformly use the
+ %% bad_record_mac alert to hide the specific type of the error."
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end.
%%--------------------------------------------------------------------
-%% Function: suites(Version) -> [Suite]
-%%
-%% Version = version()
-%% Suite = binary() from ssl_cipher.hrl
+-spec suites(tls_version()) -> [cipher_suite()].
%%
%% Description: Returns a list of supported cipher suites.
%%--------------------------------------------------------------------
@@ -208,294 +186,138 @@ suites({3, N}) when N == 1; N == 2 ->
ssl_tls1:suites().
%%--------------------------------------------------------------------
-%% Function: suite_definition(CipherSuite) ->
-%% {KeyExchange, Cipher, Hash, Exportable}
-%%
+-spec anonymous_suites() -> [cipher_suite()].
%%
-%% CipherSuite - as defined in ssl_cipher.hrl
-%% KeyExchange - rsa | dh_dss | dh_rsa | dh_anon | dhe_dss | dhe_rsa
-%% krb5 | *_export (old ssl)
-%% Cipher - null | rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc'
-%% des40_cbc | dh_dss | aes_128_cbc | aes_256_cbc |
-%% rc2_cbc_40 | rc4_40
-%% Hash - null | md5 | sha
-%% Exportable - export | no_export | ignore(?)
+%% Description: Returns a list of the anonymous cipher suites, only supported
+%% if explicitly set by user. Intended only for testing.
+%%--------------------------------------------------------------------
+anonymous_suites() ->
+ [?TLS_DH_anon_WITH_RC4_128_MD5,
+ ?TLS_DH_anon_WITH_DES_CBC_SHA,
+ ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_128_CBC_SHA,
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA].
+
+%%--------------------------------------------------------------------
+-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
%%
-%% Description: Returns a security parameters record where the
-%% cipher values has been updated according to <CipherSuite>
-%% Note: since idea is unsupported on the openssl version used by
-%% crypto (as of OTP R12B), we've commented away the idea stuff
+%% Description: Return erlang cipher suite definition.
+%% Note: Currently not supported suites are commented away.
+%% They should be supported or removed in the future.
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
- {null, null, null, ignore};
-suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
- {rsa, null, md5, ignore};
-suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
- {rsa, null, sha, ignore};
-suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> % ok
- {rsa, rc4_128, md5, no_export};
-suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> % ok
- {rsa, rc4_128, sha, no_export};
-%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> % unsupported
-%% {rsa, idea_cbc, sha, no_export};
-suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> % ok
- {rsa, des_cbc, sha, no_export};
+ {null, null, null};
+%% suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
+%% {rsa, null, md5};
+%% suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
+%% {rsa, null, sha};
+suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->
+ {rsa, rc4_128, md5};
+suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->
+ {rsa, rc4_128, sha};
+%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
+%% {rsa, idea_cbc, sha};
+suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->
+ {rsa, des_cbc, sha};
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {rsa, '3des_ede_cbc', sha, no_export};
-suite_definition(?TLS_DH_DSS_WITH_DES_CBC_SHA) ->
- {dh_dss, des_cbc, sha, no_export};
-suite_definition(?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dh_dss, '3des_ede_cbc', sha, no_export};
-suite_definition(?TLS_DH_RSA_WITH_DES_CBC_SHA) ->
- {dh_rsa, des_cbc, sha, no_export};
-suite_definition(?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dh_rsa, '3des_ede_cbc', sha, no_export};
+ {rsa, '3des_ede_cbc', sha};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha, no_export};
+ {dhe_dss, des_cbc, sha};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_dss, '3des_ede_cbc', sha, no_export};
+ {dhe_dss, '3des_ede_cbc', sha};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
- {dhe_rsa, des_cbc, sha, no_export};
+ {dhe_rsa, des_cbc, sha};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
- {dhe_rsa, '3des_ede_cbc', sha, no_export};
-suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
- {dh_anon, rc4_128, md5, no_export};
-suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
- {dh_anon, des40_cbc, sha, no_export};
-suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
- {dh_anon, '3des_ede_cbc', sha, no_export};
+ {dhe_rsa, '3des_ede_cbc', sha};
%%% TSL V1.1 AES suites
-suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> % ok
- {rsa, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_DH_DSS_WITH_AES_128_CBC_SHA) ->
- {dh_dss, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_DH_RSA_WITH_AES_128_CBC_SHA) ->
- {dh_rsa, aes_128_cbc, sha, ignore};
+suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
+ {rsa, aes_128_cbc, sha};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
- {dhe_dss, aes_128_cbc, sha, ignore};
+ {dhe_dss, aes_128_cbc, sha};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
- {dhe_rsa, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
- {dh_anon, aes_128_cbc, sha, ignore};
-suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> % ok
- {rsa, aes_256_cbc, sha, ignore};
-suite_definition(?TLS_DH_DSS_WITH_AES_256_CBC_SHA) ->
- {dh_dss, aes_256_cbc, sha, ignore};
-suite_definition(?TLS_DH_RSA_WITH_AES_256_CBC_SHA) ->
- {dh_rsa, aes_256_cbc, sha, ignore};
+ {dhe_rsa, aes_128_cbc, sha};
+suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
+ {rsa, aes_256_cbc, sha};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
- {dhe_dss, aes_256_cbc, sha, ignore};
+ {dhe_dss, aes_256_cbc, sha};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
- {dhe_rsa, aes_256_cbc, sha, ignore};
+ {dhe_rsa, aes_256_cbc, sha};
+
+%%% DH-ANON deprecated by TLS spec and not available
+%%% by default, but good for testing purposes.
+suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
+ {dh_anon, rc4_128, md5};
+suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
+ {dh_anon, des_cbc, sha};
+suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
+ {dh_anon, '3des_ede_cbc', sha};
+suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
+ {dh_anon, aes_128_cbc, sha};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
- {dh_anon, aes_256_cbc, sha, ignore};
-
-%% TSL V1.1 KRB SUITES
-suite_definition(?TLS_KRB5_WITH_DES_CBC_SHA) ->
- {krb5, des_cbc, sha, ignore};
-suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_SHA) ->
- {krb5, '3des_ede_cbc', sha, ignore};
-suite_definition(?TLS_KRB5_WITH_RC4_128_SHA) ->
- {krb5, rc4_128, sha, ignore};
-%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_SHA) ->
-%% {krb5, idea_cbc, sha, ignore};
-suite_definition(?TLS_KRB5_WITH_DES_CBC_MD5) ->
- {krb5, des_cbc, md5, ignore};
-suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_MD5) ->
- {krb5, '3des_ede_cbc', md5, ignore};
-suite_definition(?TLS_KRB5_WITH_RC4_128_MD5) ->
- {krb5, rc4_128, md5, ignore};
-%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_MD5) ->
-%% {krb5, idea_cbc, md5, ignore};
-
-suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
- {rsa, rc4_56, md5, export};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
- {rsa, rc2_cbc_56, md5, export};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
- {rsa, des_cbc, sha, export};
-suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
- {dhe_dss, des_cbc, sha, export};
-suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
- {rsa, rc4_56, sha, export};
-suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
- {dhe_dss, rc4_56, sha, export};
-suite_definition(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
- {dhe_dss, rc4_128, sha, export};
-
-%% Export suites TLS 1.0 OR SSLv3-only servers.
-suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA) ->
- {krb5_export, des40_cbc, sha, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA) ->
- {krb5_export, rc2_cbc_40, sha, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_SHA) ->
- {krb5_export, des40_cbc, sha, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5) ->
- {krb5_export, des40_cbc, md5, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5) ->
- {krb5_export, rc2_cbc_40, md5, export};
-suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_MD5) ->
- {krb5_export, rc2_cbc_40, md5, export};
-suite_definition(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) -> % ok
- {rsa, rc4_40, md5, export};
-suite_definition(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) -> % ok
- {rsa, rc2_cbc_40, md5, export};
-suite_definition(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- {rsa, des40_cbc, sha, export};
-suite_definition(?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
- {dh_dss, des40_cbc, sha, export};
-suite_definition(?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- {dh_rsa, des40_cbc, sha, export};
-suite_definition(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
- {dhe_dss, des40_cbc, sha, export};
-suite_definition(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- {dhe_rsa, des40_cbc, sha, export};
-suite_definition(?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5) ->
- {dh_anon, rc4_40, md5, export};
-suite_definition(?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA) ->
- {dh_anon, des40_cbc, sha, export}.
+ {dh_anon, aes_256_cbc, sha}.
+
+%%--------------------------------------------------------------------
+-spec suite(erl_cipher_suite()) -> cipher_suite().
+%%
+%% Description: Return TLS cipher suite definition.
+%%--------------------------------------------------------------------
%% TLS v1.1 suites
-suite({rsa, null, md5, ignore}) ->
- ?TLS_RSA_WITH_NULL_MD5;
-suite({rsa, null, sha, ignore}) ->
- ?TLS_RSA_WITH_NULL_SHA;
-suite({rsa, rc4_128, md5, no_export}) ->
+%%suite({rsa, null, md5}) ->
+%% ?TLS_RSA_WITH_NULL_MD5;
+%%suite({rsa, null, sha}) ->
+%% ?TLS_RSA_WITH_NULL_SHA;
+suite({rsa, rc4_128, md5}) ->
?TLS_RSA_WITH_RC4_128_MD5;
-suite({rsa, rc4_128, sha, no_export}) ->
+suite({rsa, rc4_128, sha}) ->
?TLS_RSA_WITH_RC4_128_SHA;
-%% suite({rsa, idea_cbc, sha, no_export}) ->
+%% suite({rsa, idea_cbc, sha}) ->
%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
-suite({rsa, des_cbc, sha, no_export}) ->
+suite({rsa, des_cbc, sha}) ->
?TLS_RSA_WITH_DES_CBC_SHA;
-suite({rsa, '3des_ede_cbc', sha, no_export}) ->
+suite({rsa, '3des_ede_cbc', sha}) ->
?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dh_dss, des_cbc, sha, no_export}) ->
- ?TLS_DH_DSS_WITH_DES_CBC_SHA;
-suite({dh_dss, '3des_ede_cbc', sha, no_export}) ->
- ?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA;
-suite({dh_rsa, des_cbc, sha, no_export}) ->
- ?TLS_DH_RSA_WITH_DES_CBC_SHA;
-suite({dh_rsa, '3des_ede_cbc', sha, no_export}) ->
- ?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dhe_dss, des_cbc, sha, no_export}) ->
+suite({dhe_dss, des_cbc, sha}) ->
?TLS_DHE_DSS_WITH_DES_CBC_SHA;
-suite({dhe_dss, '3des_ede_cbc', sha, no_export}) ->
+suite({dhe_dss, '3des_ede_cbc', sha}) ->
?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
-suite({dhe_rsa, des_cbc, sha, no_export}) ->
+suite({dhe_rsa, des_cbc, sha}) ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
-suite({dhe_rsa, '3des_ede_cbc', sha, no_export}) ->
+suite({dhe_rsa, '3des_ede_cbc', sha}) ->
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
-suite({dh_anon, rc4_128, md5, no_export}) ->
+suite({dh_anon, rc4_128, md5}) ->
?TLS_DH_anon_WITH_RC4_128_MD5;
-suite({dh_anon, des40_cbc, sha, no_export}) ->
+suite({dh_anon, des_cbc, sha}) ->
?TLS_DH_anon_WITH_DES_CBC_SHA;
-suite({dh_anon, '3des_ede_cbc', sha, no_export}) ->
+suite({dh_anon, '3des_ede_cbc', sha}) ->
?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
%%% TSL V1.1 AES suites
-suite({rsa, aes_128_cbc, sha, ignore}) ->
+suite({rsa, aes_128_cbc, sha}) ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-suite({dh_dss, aes_128_cbc, sha, ignore}) ->
- ?TLS_DH_DSS_WITH_AES_128_CBC_SHA;
-suite({dh_rsa, aes_128_cbc, sha, ignore}) ->
- ?TLS_DH_RSA_WITH_AES_128_CBC_SHA;
-suite({dhe_dss, aes_128_cbc, sha, ignore}) ->
+suite({dhe_dss, aes_128_cbc, sha}) ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
-suite({dhe_rsa, aes_128_cbc, sha, ignore}) ->
+suite({dhe_rsa, aes_128_cbc, sha}) ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
-suite({dh_anon, aes_128_cbc, sha, ignore}) ->
+suite({dh_anon, aes_128_cbc, sha}) ->
?TLS_DH_anon_WITH_AES_128_CBC_SHA;
-suite({rsa, aes_256_cbc, sha, ignore}) ->
+suite({rsa, aes_256_cbc, sha}) ->
?TLS_RSA_WITH_AES_256_CBC_SHA;
-suite({dh_dss, aes_256_cbc, sha, ignore}) ->
- ?TLS_DH_DSS_WITH_AES_256_CBC_SHA;
-suite({dh_rsa, aes_256_cbc, sha, ignore}) ->
- ?TLS_DH_RSA_WITH_AES_256_CBC_SHA;
-suite({dhe_dss, aes_256_cbc, sha, ignore}) ->
+suite({dhe_dss, aes_256_cbc, sha}) ->
?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
-suite({dhe_rsa, aes_256_cbc, sha, ignore}) ->
+suite({dhe_rsa, aes_256_cbc, sha}) ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
-suite({dh_anon, aes_256_cbc, sha, ignore}) ->
- ?TLS_DH_anon_WITH_AES_256_CBC_SHA;
-
-%% TSL V1.1 KRB SUITES
-suite({krb5, des_cbc, sha, ignore}) ->
- ?TLS_KRB5_WITH_DES_CBC_SHA;
-suite({krb5_cbc, '3des_ede_cbc', sha, ignore}) ->
- ?TLS_KRB5_WITH_3DES_EDE_CBC_SHA;
-suite({krb5, rc4_128, sha, ignore}) ->
- ?TLS_KRB5_WITH_RC4_128_SHA;
-%% suite({krb5_cbc, idea_cbc, sha, ignore}) ->
-%% ?TLS_KRB5_WITH_IDEA_CBC_SHA;
-suite({krb5_cbc, md5, ignore}) ->
- ?TLS_KRB5_WITH_DES_CBC_MD5;
-suite({krb5_ede_cbc, des_cbc, md5, ignore}) ->
- ?TLS_KRB5_WITH_3DES_EDE_CBC_MD5;
-suite({krb5_128, rc4_128, md5, ignore}) ->
- ?TLS_KRB5_WITH_RC4_128_MD5;
-%% suite({krb5, idea_cbc, md5, ignore}) ->
-%% ?TLS_KRB5_WITH_IDEA_CBC_MD5;
-
-%% Export suites TLS 1.0 OR SSLv3-only servers.
-suite({rsa, rc4_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
-suite({rsa, rc2_cbc_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
-suite({rsa, des40_cbc, sha, export}) ->
- ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({rsa, rc4_56, md5, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
-suite({rsa, rc2_cbc_56, md5, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
-suite({rsa, des_cbc, sha, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
-suite({dhe_dss, des_cbc, sha, export}) ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
-suite({rsa, rc4_56, sha, export}) ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
-suite({dhe_dss, rc4_56, sha, export}) ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
-suite({dhe_dss, rc4_128, sha, export}) ->
- ?TLS_DHE_DSS_WITH_RC4_128_SHA;
-suite({krb5_export, des40_cbc, sha, export}) ->
- ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA;
-suite({krb5_export, rc2_cbc_40, sha, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA;
-suite({krb5_export, rc4_cbc_40, sha, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC4_40_SHA;
-suite({krb5_export, des40_cbc, md5, export}) ->
- ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5;
-suite({krb5_export, rc2_cbc_40, md5, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5;
-suite({krb5_export, rc4_cbc_40, md5, export}) ->
- ?TLS_KRB5_EXPORT_WITH_RC4_40_MD5;
-suite({rsa_export, rc4_cbc_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
-suite({rsa_export, rc2_cbc_40, md5, export}) ->
- ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
-suite({rsa_export, des40_cbc, sha, export}) ->
- ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({dh_dss_export, des40_cbc, sha, export}) ->
- ?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA;
-suite({dh_rsa_export, des40_cbc, sha, export}) ->
- ?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({dhe_dss_export, des40_cbc, sha, export}) ->
- ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
-suite({dhe_rsa_export, des40_cbc, sha, export}) ->
- ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
-suite({dh_anon_export, rc4_40, md5, export}) ->
- ?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5;
-suite({dh_anon_export, des40_cbc, sha, export}) ->
- ?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA.
-
+suite({dh_anon, aes_256_cbc, sha}) ->
+ ?TLS_DH_anon_WITH_AES_256_CBC_SHA.
+%%--------------------------------------------------------------------
+-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite().
+%%
+%% Description: Return TLS cipher suite definition.
+%%--------------------------------------------------------------------
%% translate constants <-> openssl-strings
-%% TODO: Is there a pattern in the nameing
-%% that is useable to make a nicer function defention?
-
openssl_suite("DHE-RSA-AES256-SHA") ->
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("DHE-DSS-AES256-SHA") ->
@@ -514,46 +336,21 @@ openssl_suite("DHE-DSS-AES128-SHA") ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
openssl_suite("AES128-SHA") ->
?TLS_RSA_WITH_AES_128_CBC_SHA;
-%% TODO: Do we want to support this?
-%% openssl_suite("DHE-DSS-RC4-SHA") ->
-%% ?TLS_DHE_DSS_WITH_RC4_128_SHA;
%%openssl_suite("IDEA-CBC-SHA") ->
%% ?TLS_RSA_WITH_IDEA_CBC_SHA;
openssl_suite("RC4-SHA") ->
?TLS_RSA_WITH_RC4_128_SHA;
openssl_suite("RC4-MD5") ->
?TLS_RSA_WITH_RC4_128_MD5;
-%% TODO: Do we want to support this?
-openssl_suite("EXP1024-RC4-MD5") ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
-openssl_suite("EXP1024-RC2-CBC-MD5") ->
- ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
-openssl_suite("EXP1024-DES-CBC-SHA") ->
- ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
-openssl_suite("EXP1024-DHE-DSS-DES-CBC-SHA") ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
-openssl_suite("EXP1024-RC4-SHA") ->
- ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
-openssl_suite("EXP1024-DHE-DSS-RC4-SHA") ->
- ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
-openssl_suite("DHE-DSS-RC4-SHA") ->
- ?TLS_DHE_DSS_WITH_RC4_128_SHA;
-
openssl_suite("EDH-RSA-DES-CBC-SHA") ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA;
openssl_suite("DES-CBC-SHA") ->
- ?TLS_RSA_WITH_DES_CBC_SHA;
-openssl_suite("EXP-EDH-RSA-DES-CBC-SHA") ->
- ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
-openssl_suite("EXP-EDH-DSS-DES-CBC-SHA") ->
- ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
-openssl_suite("EXP-DES-CBC-SHA") ->
- ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
-openssl_suite("EXP-RC2-CBC-MD5") ->
- ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
-openssl_suite("EXP-RC4-MD5") ->
- ?TLS_RSA_EXPORT_WITH_RC4_40_MD5.
-
+ ?TLS_RSA_WITH_DES_CBC_SHA.
+%%--------------------------------------------------------------------
+-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite().
+%%
+%% Description: Return openssl cipher suite name.
+%%-------------------------------------------------------------------
openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
"DHE-RSA-AES256-SHA";
openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
@@ -582,37 +379,28 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
"EDH-RSA-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) ->
"DES-CBC-SHA";
-openssl_suite_name(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- "EXP-EDH-RSA-DES-CBC-SHA";
-openssl_suite_name(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
- "EXP-EDH-DSS-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
- "EXP-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) ->
- "EXP-RC2-CBC-MD5";
-openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) ->
- "EXP-RC4-MD5";
-
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
- "EXP1024-RC4-MD5";
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
- "EXP1024-RC2-CBC-MD5";
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
- "EXP1024-DES-CBC-SHA";
-openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
- "EXP1024-DHE-DSS-DES-CBC-SHA";
-openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
- "EXP1024-RC4-SHA";
-openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
- "EXP1024-DHE-DSS-RC4-SHA";
-openssl_suite_name(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
- "DHE-DSS-RC4-SHA";
-
%% No oppenssl name
openssl_suite_name(Cipher) ->
suite_definition(Cipher).
%%--------------------------------------------------------------------
+-spec filter(undefined | binary(), [cipher_suite()]) -> [cipher_suite()].
+%%
+%% Description: .
+%%-------------------------------------------------------------------
+filter(undefined, Ciphers) ->
+ Ciphers;
+filter(DerCert, Ciphers) ->
+ OtpCert = public_key:pkix_decode_cert(DerCert, otp),
+ SigAlg = OtpCert#'OTPCertificate'.signatureAlgorithm,
+ case ssl_certificate:signature_type(SigAlg#'SignatureAlgorithm'.algorithm) of
+ rsa ->
+ filter_rsa(OtpCert, Ciphers -- dsa_signed_suites());
+ dsa ->
+ Ciphers -- rsa_signed_suites()
+ end.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -621,15 +409,8 @@ bulk_cipher_algorithm(null) ->
%% Not supported yet
%% bulk_cipher_algorithm(idea_cbc) ->
%% ?IDEA;
-bulk_cipher_algorithm(Cipher) when Cipher == rc2_cbc_40;
- Cipher == rc2_cbc_56 ->
- ?RC2;
-bulk_cipher_algorithm(Cipher) when Cipher == rc4_40;
- Cipher == rc4_56;
- Cipher == rc4_128 ->
+bulk_cipher_algorithm(rc4_128) ->
?RC4;
-bulk_cipher_algorithm(des40_cbc) ->
- ?DES40;
bulk_cipher_algorithm(des_cbc) ->
?DES;
bulk_cipher_algorithm('3des_ede_cbc') ->
@@ -639,15 +420,10 @@ bulk_cipher_algorithm(Cipher) when Cipher == aes_128_cbc;
?AES.
type(Cipher) when Cipher == null;
- Cipher == rc4_40;
- Cipher == rc4_56;
Cipher == rc4_128 ->
?STREAM;
type(Cipher) when Cipher == idea_cbc;
- Cipher == rc2_cbc_40;
- Cipher == rc2_cbc_56;
- Cipher == des40_cbc;
Cipher == des_cbc;
Cipher == '3des_ede_cbc';
Cipher == aes_128_cbc;
@@ -659,13 +435,6 @@ key_material(null) ->
key_material(Cipher) when Cipher == idea_cbc;
Cipher == rc4_128 ->
16;
-key_material(Cipher) when Cipher == rc2_cbc_56;
- Cipher == rc4_56 ->
- 7;
-key_material(Cipher) when Cipher == rc2_cbc_40;
- Cipher == rc4_40;
- Cipher == des40_cbc ->
- 5;
key_material(des_cbc) ->
8;
key_material('3des_ede_cbc') ->
@@ -678,14 +447,9 @@ key_material(aes_256_cbc) ->
expanded_key_material(null) ->
0;
expanded_key_material(Cipher) when Cipher == idea_cbc;
- Cipher == rc2_cbc_40;
- Cipher == rc2_cbc_56;
- Cipher == rc4_40;
- Cipher == rc4_56;
Cipher == rc4_128 ->
16;
-expanded_key_material(Cipher) when Cipher == des_cbc;
- Cipher == des40_cbc ->
+expanded_key_material(Cipher) when Cipher == des_cbc ->
8;
expanded_key_material('3des_ede_cbc') ->
24;
@@ -696,13 +460,7 @@ expanded_key_material(Cipher) when Cipher == aes_128_cbc;
effective_key_bits(null) ->
0;
-effective_key_bits(Cipher) when Cipher == rc2_cbc_40;
- Cipher == rc4_40;
- Cipher == des40_cbc ->
- 40;
-effective_key_bits(Cipher) when Cipher == rc2_cbc_56;
- Cipher == rc4_56;
- Cipher == des_cbc ->
+effective_key_bits(des_cbc) ->
56;
effective_key_bits(Cipher) when Cipher == idea_cbc;
Cipher == rc4_128;
@@ -714,17 +472,12 @@ effective_key_bits(aes_256_cbc) ->
256.
iv_size(Cipher) when Cipher == null;
- Cipher == rc4_40;
- Cipher == rc4_56;
Cipher == rc4_128 ->
0;
iv_size(Cipher) ->
block_size(Cipher).
block_size(Cipher) when Cipher == idea_cbc;
- Cipher == rc2_cbc_40;
- Cipher == rc2_cbc_56;
- Cipher == des40_cbc;
Cipher == des_cbc;
Cipher == '3des_ede_cbc' ->
8;
@@ -763,9 +516,18 @@ generic_stream_cipher_from_bin(T, HashSz) ->
#generic_stream_cipher{content=Content,
mac=Mac}.
-check_padding(_GBC) ->
- ok.
-
+is_correct_padding(_, {3, 0}) ->
+ true;
+%% For interoperability reasons we do not check the padding in TLS 1.0 as it
+%% is not strictly required and breaks interopability with for instance
+%% Google.
+is_correct_padding(_, {3, 1}) ->
+ true;
+%% Padding must be check in TLS 1.1 and after
+is_correct_padding(#generic_block_cipher{padding_length = Len, padding = Padding}, _) ->
+ list_to_binary(lists:duplicate(Len, Len)) == Padding.
+
+
get_padding(Length, BlockSize) ->
get_padding_aux(BlockSize, Length rem BlockSize).
@@ -782,3 +544,51 @@ next_iv(Bin, IV) ->
<<_:FirstPart/binary, NextIV:IVSz/binary>> = Bin,
NextIV.
+rsa_signed_suites() ->
+ dhe_rsa_suites() ++ rsa_suites().
+
+dhe_rsa_suites() ->
+ [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_DES_CBC_SHA].
+
+rsa_suites() ->
+ [?TLS_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_128_CBC_SHA,
+ %%?TLS_RSA_WITH_IDEA_CBC_SHA,
+ ?TLS_RSA_WITH_RC4_128_SHA,
+ ?TLS_RSA_WITH_RC4_128_MD5,
+ ?TLS_RSA_WITH_DES_CBC_SHA].
+
+dsa_signed_suites() ->
+ dhe_dss_suites().
+
+dhe_dss_suites() ->
+ [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA].
+
+filter_rsa(OtpCert, RsaCiphers) ->
+ TBSCert = OtpCert#'OTPCertificate'.tbsCertificate,
+ TBSExtensions = TBSCert#'OTPTBSCertificate'.extensions,
+ Extensions = ssl_certificate:extensions_list(TBSExtensions),
+ case ssl_certificate:select_extension(?'id-ce-keyUsage', Extensions) of
+ undefined ->
+ RsaCiphers;
+ #'Extension'{extnValue = KeyUse} ->
+ Result = filter_rsa_suites(keyEncipherment,
+ KeyUse, RsaCiphers, rsa_suites()),
+ filter_rsa_suites(digitalSignature,
+ KeyUse, Result, dhe_rsa_suites())
+ end.
+
+filter_rsa_suites(Use, KeyUse, CipherSuits, RsaSuites) ->
+ case ssl_certificate:is_valid_key_usage(KeyUse, Use) of
+ true ->
+ CipherSuits;
+ false ->
+ CipherSuits -- RsaSuites
+ end.
diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl
index d282cbd780..8bd68cc190 100644
--- a/lib/ssl/src/ssl_cipher.hrl
+++ b/lib/ssl/src/ssl_cipher.hrl
@@ -26,6 +26,14 @@
-ifndef(ssl_cipher).
-define(ssl_cipher, true).
+-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc'
+ | aes_128_cbc | aes_256_cbc.
+-type hash() :: null | sha | md5.
+-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
+-type cipher_suite() :: binary().
+-type cipher_enum() :: integer().
+-type openssl_cipher_suite() :: string().
+
%%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with
% SSL record protocol
@@ -57,7 +65,7 @@
%% TLS_NULL_WITH_NULL_NULL = { 0x00,0x00 };
-define(TLS_NULL_WITH_NULL_NULL, <<?BYTE(16#00), ?BYTE(16#00)>>).
-%%% The following CipherSuite definitions require that the server
+%%% The following cipher suite definitions require that the server
%%% provide an RSA certificate that can be used for key exchange. The
%%% server may request either an RSA or a DSS signature-capable
%%% certificate in the certificate request message.
@@ -68,24 +76,15 @@
%% TLS_RSA_WITH_NULL_SHA = { 0x00,0x02 };
-define(TLS_RSA_WITH_NULL_SHA, <<?BYTE(16#00), ?BYTE(16#02)>>).
-%% TLS_RSA_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x03 };
--define(TLS_RSA_EXPORT_WITH_RC4_40_MD5, <<?BYTE(16#00), ?BYTE(16#03)>>).
-
%% TLS_RSA_WITH_RC4_128_MD5 = { 0x00,0x04 };
-define(TLS_RSA_WITH_RC4_128_MD5, <<?BYTE(16#00), ?BYTE(16#04)>>).
%% TLS_RSA_WITH_RC4_128_SHA = { 0x00,0x05 };
-define(TLS_RSA_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#05)>>).
-%% TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00,0x06 };
--define(TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, <<?BYTE(16#00), ?BYTE(16#06)>>).
-
%% TLS_RSA_WITH_IDEA_CBC_SHA = { 0x00,0x07 };
-define(TLS_RSA_WITH_IDEA_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#07)>>).
-%% TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x08 };
--define(TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#08)>>).
-
%% TLS_RSA_WITH_DES_CBC_SHA = { 0x00,0x09 };
-define(TLS_RSA_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#09)>>).
@@ -106,51 +105,33 @@
%%% provided by the client must use the parameters (group and
%%% generator) described by the server.
-%% TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0B };
--define(TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0B)>>).
-
%% TLS_DH_DSS_WITH_DES_CBC_SHA = { 0x00,0x0C };
-define(TLS_DH_DSS_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0C)>>).
%% TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x0D };
-define(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0D)>>).
-%% TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x0E };
--define(TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0E)>>).
-
%% TLS_DH_RSA_WITH_DES_CBC_SHA = { 0x00,0x0F };
-define(TLS_DH_RSA_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#0F)>>).
%% TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x10 };
-define(TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#10)>>).
-%% TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x11 };
--define(TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#11)>>).
-
%% TLS_DHE_DSS_WITH_DES_CBC_SHA = { 0x00,0x12 };
-define(TLS_DHE_DSS_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#12)>>).
%% TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = { 0x00,0x13 };
-define(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#13)>>).
-%% TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x14 };
--define(TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#14)>>).
-
%% TLS_DHE_RSA_WITH_DES_CBC_SHA = { 0x00,0x15 };
-define(TLS_DHE_RSA_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#15)>>).
%% TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = { 0x00,0x16 };
-define(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#16)>>).
-%% TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x17 };
--define(TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, <<?BYTE(16#00), ?BYTE(16#17)>>).
-
%% TLS_DH_anon_WITH_RC4_128_MD5 = { 0x00,0x18 };
-define(TLS_DH_anon_WITH_RC4_128_MD5, <<?BYTE(16#00),?BYTE(16#18)>>).
-%% TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = { 0x00,0x19 };
--define(TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#19)>>).
-
%% TLS_DH_anon_WITH_DES_CBC_SHA = { 0x00,0x1A };
-define(TLS_DH_anon_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#1A)>>).
@@ -222,35 +203,7 @@
%% TLS_KRB5_WITH_IDEA_CBC_MD5 = { 0x00,0x25 };
-define(TLS_KRB5_WITH_IDEA_CBC_MD5, <<?BYTE(16#00), ?BYTE(16#25)>>).
-%% TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = { 0x00,0x26 };
--define(TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA, <<?BYTE(16#00), ?BYTE(16#26)>>).
-
-%% TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = { 0x00,0x27 };
--define(TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA, <<?BYTE(16#00), ?BYTE(16#27)>>).
-
-%% TLS_KRB5_EXPORT_WITH_RC4_40_SHA = { 0x00,0x28 };
--define(TLS_KRB5_EXPORT_WITH_RC4_40_SHA, <<?BYTE(16#00), ?BYTE(16#28)>>).
-
-%% TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = { 0x00,0x29 };
--define(TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5, <<?BYTE(16#00), ?BYTE(16#29)>>).
-
-%% TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = { 0x00,0x2A };
--define(TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5, <<?BYTE(16#00), ?BYTE(16#2A)>>).
-
-%% TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = { 0x00,0x2B };
--define(TLS_KRB5_EXPORT_WITH_RC4_40_MD5, <<?BYTE(16#00), ?BYTE(16#2B)>>).
-
-%% Additional TLS ciphersuites from draft-ietf-tls-56-bit-ciphersuites-00.txt
-
--define(TLS_RSA_EXPORT1024_WITH_RC4_56_MD5, <<?BYTE(16#00), ?BYTE(16#60)>>).
--define(TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5, <<?BYTE(16#00), ?BYTE(16#61)>>).
--define(TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#62)>>).
--define(TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#63)>>).
--define(TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, <<?BYTE(16#00), ?BYTE(16#64)>>).
--define(TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA, <<?BYTE(16#00), ?BYTE(16#65)>>).
--define(TLS_DHE_DSS_WITH_RC4_128_SHA, <<?BYTE(16#00), ?BYTE(16#66)>>).
-
-%% RFC 5746 - Not a real ciphersuite used to signal empty "renegotiation_info" extension
+%% RFC 5746 - Not a real cipher suite used to signal empty "renegotiation_info" extension
%% to avoid handshake failure from old servers that do not ignore
%% hello extension data as they should.
-define(TLS_EMPTY_RENEGOTIATION_INFO_SCSV, <<?BYTE(16#00), ?BYTE(16#FF)>>).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index a4eaf03086..574e1e9468 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,6 @@
-behaviour(gen_fsm).
--include("ssl_debug.hrl").
-include("ssl_handshake.hrl").
-include("ssl_alert.hrl").
-include("ssl_record.hrl").
@@ -39,7 +38,7 @@
-include_lib("public_key/include/public_key.hrl").
%% Internal application API
--export([send/2, send/3, recv/3, connect/7, ssl_accept/6, handshake/2,
+-export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2,
socket_control/3, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
peer_certificate/1, sockname/1, peername/1, renegotiation/1]).
@@ -65,16 +64,16 @@
ssl_options, % #ssl_options{}
socket_options, % #socket_options{}
connection_states, % #connection_states{} from ssl_record.hrl
+ tls_packets = [], % Not yet handled decode ssl/tls packets.
tls_record_buffer, % binary() buffer of incomplete records
tls_handshake_buffer, % binary() buffer of incomplete handshakes
%% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary())
tls_handshake_hashes, % see above
tls_cipher_texts, % list() received but not deciphered yet
- own_cert, % binary()
- session, % #session{} from ssl_handshake.erl
+ session, % #session{} from ssl_handshake.hrl
session_cache, %
session_cache_cb, %
- negotiated_version, % #protocol_version{}
+ negotiated_version, % tls_version()
supported_protocol_versions, % [atom()]
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
@@ -87,59 +86,75 @@
from, % term(), where to reply
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
-%% tls_buffer, % Keeps a lookahead one packet if available
log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
recv_during_renegotiation, %boolean()
- send_queue % queue()
+ send_queue, % queue()
+ terminated = false %
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
#'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME,
base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}).
+-type state_name() :: hello | abbreviated | certify | cipher | connection.
+-type gen_fsm_state_return() :: {next_state, state_name(), #state{}} |
+ {next_state, state_name(), #state{}, timeout()} |
+ {stop, term(), #state{}}.
+
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: send(Pid, Data) -> ok | {error, Reason}
+-spec send(pid(), iolist()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
%%--------------------------------------------------------------------
send(Pid, Data) ->
- sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
-send(Pid, Data, Timeout) ->
- sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
+ sync_send_all_state_event(Pid, {application_data,
+ erlang:iolist_to_binary(Data)}, infinity).
+
%%--------------------------------------------------------------------
-%% Function: recv(Socket, Length Timeout) -> {ok, Data} | {error, reason}
+-spec recv(pid(), integer(), timeout()) ->
+ {ok, binary() | list()} | {error, reason()}.
%%
%% Description: Receives data when active = false
%%--------------------------------------------------------------------
recv(Pid, Length, Timeout) ->
sync_send_all_state_event(Pid, {recv, Length}, Timeout).
%%--------------------------------------------------------------------
-%% Function: : connect(Host, Port, Socket, Options,
-%% User, CbInfo, Timeout) -> {ok, Socket}
+-spec connect(host(), port_num(), port(), {#ssl_options{}, #socket_options{}},
+ pid(), tuple(), timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Connect to a ssl server.
%%--------------------------------------------------------------------
connect(Host, Port, Socket, Options, User, CbInfo, Timeout) ->
- start_fsm(client, Host, Port, Socket, Options, User, CbInfo,
- Timeout).
+ try start_fsm(client, Host, Port, Socket, Options, User, CbInfo,
+ Timeout)
+ catch
+ exit:{noproc, _} ->
+ {error, ssl_not_started}
+ end.
%%--------------------------------------------------------------------
-%% Function: accept(Port, Socket, Opts, User,
-%% CbInfo, Timeout) -> {ok, Socket} | {error, Reason}
+-spec ssl_accept(port_num(), port(), {#ssl_options{}, #socket_options{}},
+ pid(), tuple(), timeout()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Performs accept on a ssl listen socket. e.i. performs
%% ssl handshake.
%%--------------------------------------------------------------------
ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) ->
- start_fsm(server, "localhost", Port, Socket, Opts, User,
- CbInfo, Timeout).
+ try start_fsm(server, "localhost", Port, Socket, Opts, User,
+ CbInfo, Timeout)
+ catch
+ exit:{noproc, _} ->
+ {error, ssl_not_started}
+ end.
%%--------------------------------------------------------------------
-%% Function: handshake(SslSocket, Timeout) -> ok | {error, Reason}
+-spec handshake(#sslsocket{}, timeout()) -> ok | {error, reason()}.
%%
%% Description: Starts ssl handshake.
%%--------------------------------------------------------------------
@@ -151,7 +166,8 @@ handshake(#sslsocket{pid = Pid}, Timeout) ->
Error
end.
%--------------------------------------------------------------------
-%% Function: socket_control(Pid) -> {ok, SslSocket} | {error, Reason}
+-spec socket_control(port(), pid(), atom()) ->
+ {ok, #sslsocket{}} | {error, reason()}.
%%
%% Description: Set the ssl process to own the accept socket
%%--------------------------------------------------------------------
@@ -164,7 +180,7 @@ socket_control(Socket, Pid, CbModule) ->
end.
%%--------------------------------------------------------------------
-%% Function: close() -> ok
+-spec close(pid()) -> ok | {error, reason()}.
%%
%% Description: Close a ssl connection
%%--------------------------------------------------------------------
@@ -177,7 +193,7 @@ close(ConnectionPid) ->
end.
%%--------------------------------------------------------------------
-%% Function: shutdown(Socket, How) -> ok | {error, Reason}
+-spec shutdown(pid(), atom()) -> ok | {error, reason()}.
%%
%% Description: Same as gen_tcp:shutdown/2
%%--------------------------------------------------------------------
@@ -185,7 +201,7 @@ shutdown(ConnectionPid, How) ->
sync_send_all_state_event(ConnectionPid, {shutdown, How}).
%%--------------------------------------------------------------------
-%% Function: new_user(ConnectionPid, User) -> ok | {error, Reason}
+-spec new_user(pid(), pid()) -> ok | {error, reason()}.
%%
%% Description: Changes process that receives the messages when active = true
%% or once.
@@ -193,30 +209,28 @@ shutdown(ConnectionPid, How) ->
new_user(ConnectionPid, User) ->
sync_send_all_state_event(ConnectionPid, {new_user, User}).
%%--------------------------------------------------------------------
-%% Function: sockname(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason}
+-spec sockname(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}.
%%
%% Description: Same as inet:sockname/1
%%--------------------------------------------------------------------
sockname(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, sockname).
%%--------------------------------------------------------------------
-%% Function: peername(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason}
+-spec peername(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}.
%%
%% Description: Same as inet:peername/1
%%--------------------------------------------------------------------
peername(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, peername).
%%--------------------------------------------------------------------
-%% Function: get_opts(ConnectionPid, OptTags) -> {ok, Options} | {error, Reason}
+-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}.
%%
%% Description: Same as inet:getopts/2
%%--------------------------------------------------------------------
-get_opts({ListenSocket, {_SslOpts, SockOpts}, _}, OptTags) ->
- get_socket_opts(ListenSocket, OptTags, SockOpts, []);
get_opts(ConnectionPid, OptTags) ->
sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}).
%%--------------------------------------------------------------------
-%% Function: setopts(Socket, Options) -> ok | {error, Reason}
+-spec set_opts(pid(), list()) -> ok | {error, reason()}.
%%
%% Description: Same as inet:setopts/2
%%--------------------------------------------------------------------
@@ -224,8 +238,7 @@ set_opts(ConnectionPid, Options) ->
sync_send_all_state_event(ConnectionPid, {set_opts, Options}).
%%--------------------------------------------------------------------
-%% Function: info(ConnectionPid) -> {ok, {Protocol, CipherSuite}} |
-%% {error, Reason}
+-spec info(pid()) -> {ok, {atom(), tuple()}} | {error, reason()}.
%%
%% Description: Returns ssl protocol and cipher used for the connection
%%--------------------------------------------------------------------
@@ -233,7 +246,7 @@ info(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, info).
%%--------------------------------------------------------------------
-%% Function: session_info(ConnectionPid) -> {ok, PropList} | {error, Reason}
+-spec session_info(pid()) -> {ok, list()} | {error, reason()}.
%%
%% Description: Returns info about the ssl session
%%--------------------------------------------------------------------
@@ -241,7 +254,7 @@ session_info(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, session_info).
%%--------------------------------------------------------------------
-%% Function: peercert(ConnectionPid) -> {ok, Cert} | {error, Reason}
+-spec peer_certificate(pid()) -> {ok, binary()| undefined} | {error, reason()}.
%%
%% Description: Returns the peer cert
%%--------------------------------------------------------------------
@@ -249,7 +262,7 @@ peer_certificate(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, peer_certificate).
%%--------------------------------------------------------------------
-%% Function: renegotiation(ConnectionPid) -> ok | {error, Reason}
+-spec renegotiation(pid()) -> ok | {error, reason()}.
%%
%% Description: Starts a renegotiation of the ssl session.
%%--------------------------------------------------------------------
@@ -261,7 +274,8 @@ renegotiation(ConnectionPid) ->
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+-spec start_link(atom(), host(), port_num(), port(), list(), pid(), tuple()) ->
+ {ok, pid()} | ignore | {error, reason()}.
%%
%% Description: Creates a gen_fsm process which calls Module:init/1 to
%% initialize. To ensure a synchronized start-up procedure, this function
@@ -275,39 +289,36 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
%% gen_fsm callbacks
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, StateName, State} |
-%% {ok, StateName, State, Timeout} |
-%% ignore |
-%% {stop, StopReason}
+-spec init(list()) -> {ok, state_name(), #state{}, timeout()} | {stop, term()}.
+%% Possible return values not used now.
+%% | {ok, state_name(), #state{}} |
+%% ignore
%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
%% gen_fsm:start_link/3,4, this function is called by the new process to
%% initialize.
%%--------------------------------------------------------------------
-init([Role, Host, Port, Socket, {SSLOpts, _} = Options,
+init([Role, Host, Port, Socket, {SSLOpts0, _} = Options,
User, CbInfo]) ->
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
Hashes0 = ssl_handshake:init_hashes(),
- try ssl_init(SSLOpts, Role) of
+ try ssl_init(SSLOpts0, Role) of
{ok, Ref, CacheRef, OwnCert, Key, DHParams} ->
+ Session = State0#state.session,
State = State0#state{tls_handshake_hashes = Hashes0,
- own_cert = OwnCert,
+ session = Session#session{own_certificate = OwnCert},
cert_db_ref = Ref,
session_cache = CacheRef,
private_key = Key,
diffie_hellman_params = DHParams},
- {ok, hello, State}
+ {ok, hello, State, get_timeout(State)}
catch
throw:Error ->
{stop, Error}
end.
%%--------------------------------------------------------------------
-%% Function:
-%% state_name(Event, State) -> {next_state, NextStateName, NextState}|
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}
+%% -spec state_name(event(), #state{}) -> gen_fsm_state_return()
%%
%% Description:There should be one instance of this function for each
%% possible state name. Whenever a gen_fsm receives an event sent
@@ -316,14 +327,18 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options,
%% the event. It is also called if a timeout occurs.
%%
%%--------------------------------------------------------------------
+-spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(),
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
hello(start, #state{host = Host, port = Port, role = client,
ssl_options = SslOpts,
+ session = #session{own_certificate = Cert} = Session0,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates,
- renegotiation = {Renegotiation, _}}
- = State0) ->
+ renegotiation = {Renegotiation, _}} = State0) ->
Hello = ssl_handshake:client_hello(Host, Port,
- ConnectionStates, SslOpts, Renegotiation),
+ ConnectionStates,
+ SslOpts, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
Hashes0 = ssl_handshake:init_hashes(),
@@ -332,13 +347,13 @@ hello(start, #state{host = Host, port = Port, role = client,
Transport:send(Socket, BinMsg),
State1 = State0#state{connection_states = CS2,
negotiated_version = Version, %% Requested version
- session =
- #session{session_id = Hello#client_hello.session_id,
- is_resumable = false},
- tls_handshake_hashes = Hashes1},
+ session =
+ Session0#session{session_id = Hello#client_hello.session_id,
+ is_resumable = false},
+ tls_handshake_hashes = Hashes1},
{Record, State} = next_record(State1),
next_state(hello, Record, State);
-
+
hello(start, #state{role = server} = State0) ->
{Record, State} = next_record(State0),
next_state(hello, Record, State);
@@ -349,49 +364,29 @@ hello(#hello_request{}, #state{role = client} = State0) ->
hello(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression} = Hello,
- #state{session = Session0 = #session{session_id = OldId},
+ #state{session = #session{session_id = OldId},
connection_states = ConnectionStates0,
role = client,
negotiated_version = ReqVersion,
- host = Host, port = Port,
renegotiation = {Renegotiation, _},
- ssl_options = SslOptions,
- session_cache = Cache,
- session_cache_cb = CacheCb} = State0) ->
-
+ ssl_options = SslOptions} = State0) ->
case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
- {Version, NewId, ConnectionStates1} ->
- {KeyAlgorithm, _, _, _} =
+ {Version, NewId, ConnectionStates} ->
+ {KeyAlgorithm, _, _} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
- State1 = State0#state{key_algorithm = KeyAlgorithm,
- negotiated_version = Version,
- connection_states = ConnectionStates1,
- premaster_secret = PremasterSecret},
+ State = State0#state{key_algorithm = KeyAlgorithm,
+ negotiated_version = Version,
+ connection_states = ConnectionStates,
+ premaster_secret = PremasterSecret},
case ssl_session:is_new(OldId, NewId) of
true ->
- Session = Session0#session{session_id = NewId,
- cipher_suite = CipherSuite,
- compression_method = Compression},
- {Record, State} = next_record(State1#state{session = Session}),
- next_state(certify, Record, State);
+ handle_new_session(NewId, CipherSuite, Compression, State);
false ->
- Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}),
- case ssl_handshake:master_secret(Version, Session,
- ConnectionStates1, client) of
- {_, ConnectionStates2} ->
- {Record, State} =
- next_record(State1#state{
- connection_states = ConnectionStates2,
- session = Session}),
- next_state(abbreviated, Record, State);
- #alert{} = Alert ->
- handle_own_alert(Alert, Version, hello, State1),
- {stop, normal, State1}
- end
+ handle_resumed_session(NewId, State#state{connection_states = ConnectionStates})
end;
#alert{} = Alert ->
handle_own_alert(Alert, ReqVersion, hello, State0),
@@ -400,14 +395,13 @@ hello(#server_hello{cipher_suite = CipherSuite,
hello(Hello = #client_hello{client_version = ClientVersion},
State = #state{connection_states = ConnectionStates0,
- port = Port, session = Session0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
renegotiation = {Renegotiation, _},
session_cache = Cache,
session_cache_cb = CacheCb,
ssl_options = SslOpts}) ->
-
case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
- ConnectionStates0}, Renegotiation) of
+ ConnectionStates0, Cert}, Renegotiation) of
{Version, {Type, Session}, ConnectionStates} ->
do_server_hello(Type, State#state{connection_states =
ConnectionStates,
@@ -418,14 +412,20 @@ hello(Hello = #client_hello{client_version = ClientVersion},
{stop, normal, State}
end;
+hello(timeout, State) ->
+ { next_state, hello, State, hibernate };
+
hello(Msg, State) ->
handle_unexpected_message(Msg, hello, State).
-
+%%--------------------------------------------------------------------
+-spec abbreviated(#hello_request{} | #finished{} | term(),
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
abbreviated(#hello_request{}, State0) ->
{Record, State} = next_record(State0),
next_state(hello, Record, State);
-abbreviated(Finished = #finished{verify_data = Data},
+abbreviated(#finished{verify_data = Data} = Finished,
#state{role = server,
negotiated_version = Version,
tls_handshake_hashes = Hashes,
@@ -443,7 +443,7 @@ abbreviated(Finished = #finished{verify_data = Data},
{stop, normal, State}
end;
-abbreviated(Finished = #finished{verify_data = Data},
+abbreviated(#finished{verify_data = Data} = Finished,
#state{role = client, tls_handshake_hashes = Hashes0,
session = #session{master_secret = MasterSecret},
negotiated_version = Version,
@@ -463,9 +463,17 @@ abbreviated(Finished = #finished{verify_data = Data},
{stop, normal, State}
end;
+abbreviated(timeout, State) ->
+ { next_state, abbreviated, State, hibernate };
+
abbreviated(Msg, State) ->
handle_unexpected_message(Msg, abbreviated, State).
+%%--------------------------------------------------------------------
+-spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} |
+ #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(),
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
certify(#hello_request{}, State0) ->
{Record, State} = next_record(State0),
next_state(hello, Record, State);
@@ -494,8 +502,7 @@ certify(#certificate{} = Cert,
ssl_options = Opts} = State) ->
case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth,
Opts#ssl_options.verify,
- Opts#ssl_options.verify_fun,
- Opts#ssl_options.validate_extensions_fun, Role) of
+ Opts#ssl_options.verify_fun, Role) of
{PeerCert, PublicKeyInfo} ->
handle_peer_cert(PeerCert, PublicKeyInfo,
State#state{client_certificate_requested = false});
@@ -507,7 +514,7 @@ certify(#certificate{} = Cert,
certify(#server_key_exchange{} = KeyExchangeMsg,
#state{role = client, negotiated_version = Version,
key_algorithm = Alg} = State0)
- when Alg == dhe_dss; Alg == dhe_rsa ->%%Not imp:Alg == dh_anon;Alg == krb5 ->
+ when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon ->
case handle_server_key(KeyExchangeMsg, State0) of
#state{} = State1 ->
{Record, State} = next_record(State1),
@@ -518,13 +525,9 @@ certify(#server_key_exchange{} = KeyExchangeMsg,
{stop, normal, State0}
end;
-certify(#server_key_exchange{},
- State = #state{role = client, negotiated_version = Version,
- key_algorithm = Alg})
- when Alg == rsa; Alg == dh_dss; Alg == dh_rsa ->
- Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, certify_server_key_exchange, State),
- {stop, normal, State};
+certify(#server_key_exchange{} = Msg,
+ #state{role = client, key_algorithm = rsa} = State) ->
+ handle_unexpected_message(Msg, certify_server_keyexchange, State);
certify(#certificate_request{}, State0) ->
{Record, State} = next_record(State0#state{client_certificate_requested = true}),
@@ -536,7 +539,7 @@ certify(#server_hello_done{},
connection_states = ConnectionStates0,
negotiated_version = Version,
premaster_secret = undefined,
- role = client} = State0) ->
+ role = client} = State0) ->
case ssl_handshake:master_secret(Version, Session,
ConnectionStates0, client) of
{MasterSecret, ConnectionStates1} ->
@@ -568,82 +571,68 @@ certify(#server_hello_done{},
{stop, normal, State0}
end;
-certify(#client_key_exchange{},
- State = #state{role = server,
- client_certificate_requested = true,
- ssl_options = #ssl_options{fail_if_no_peer_cert = true},
- negotiated_version = Version}) ->
+certify(#client_key_exchange{} = Msg,
+ #state{role = server,
+ client_certificate_requested = true,
+ ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State) ->
%% We expect a certificate here
- Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version,
- certify_server_waiting_certificate, State),
- {stop, normal, State};
+ handle_unexpected_message(Msg, certify_client_key_exchange, State);
-
-certify(#client_key_exchange{exchange_keys
- = #encrypted_premaster_secret{premaster_secret
- = EncPMS}},
- #state{negotiated_version = Version,
- connection_states = ConnectionStates0,
- session = Session0,
- private_key = Key} = State0) ->
- try ssl_handshake:decrypt_premaster_secret(EncPMS, Key) of
- PremasterSecret ->
- case ssl_handshake:master_secret(Version, PremasterSecret,
- ConnectionStates0, server) of
- {MasterSecret, ConnectionStates} ->
- Session = Session0#session{master_secret = MasterSecret},
- State1 = State0#state{connection_states = ConnectionStates,
- session = Session},
- {Record, State} = next_record(State1),
- next_state(cipher, Record, State);
- #alert{} = Alert ->
- handle_own_alert(Alert, Version,
- certify_client_key_exchange, State0),
- {stop, normal, State0}
- end
+certify(#client_key_exchange{exchange_keys = Keys},
+ State = #state{key_algorithm = KeyAlg, negotiated_version = Version}) ->
+ try
+ certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State)
catch
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify_client_key_exchange,
- State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify_client_key_exchange, State),
+ {stop, normal, State}
end;
-certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{
- dh_public = ClientPublicDhKey}},
- #state{negotiated_version = Version,
- diffie_hellman_params = #'DHParameter'{prime = P,
- base = G},
- diffie_hellman_keys = {_, ServerDhPrivateKey},
- role = Role,
- session = Session,
- connection_states = ConnectionStates0} = State0) ->
-
- PMpint = crypto:mpint(P),
- GMpint = crypto:mpint(G),
- PremasterSecret = crypto:dh_compute_key(mpint_binary(ClientPublicDhKey),
- ServerDhPrivateKey,
- [PMpint, GMpint]),
-
+certify(timeout, State) ->
+ { next_state, certify, State, hibernate };
+
+certify(Msg, State) ->
+ handle_unexpected_message(Msg, certify, State).
+
+certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS},
+ #state{negotiated_version = Version,
+ connection_states = ConnectionStates0,
+ session = Session0,
+ private_key = Key} = State0) ->
+ PremasterSecret = ssl_handshake:decrypt_premaster_secret(EncPMS, Key),
case ssl_handshake:master_secret(Version, PremasterSecret,
- ConnectionStates0, Role) of
+ ConnectionStates0, server) of
{MasterSecret, ConnectionStates} ->
- State1 = State0#state{session =
- Session#session{master_secret
- = MasterSecret},
- connection_states = ConnectionStates},
-
+ Session = Session0#session{master_secret = MasterSecret},
+ State1 = State0#state{connection_states = ConnectionStates,
+ session = Session},
{Record, State} = next_record(State1),
next_state(cipher, Record, State);
#alert{} = Alert ->
- handle_own_alert(Alert, Version,
+ handle_own_alert(Alert, Version,
certify_client_key_exchange, State0),
{stop, normal, State0}
end;
-certify(Msg, State) ->
- handle_unexpected_message(Msg, certify, State).
+certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey},
+ #state{negotiated_version = Version,
+ diffie_hellman_params = #'DHParameter'{prime = P,
+ base = G},
+ diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) ->
+ case dh_master_secret(crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of
+ #state{} = State1 ->
+ {Record, State} = next_record(State1),
+ next_state(cipher, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version,
+ certify_client_key_exchange, State0),
+ {stop, normal, State0}
+ end.
+%%--------------------------------------------------------------------
+-spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(),
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
cipher(#hello_request{}, State0) ->
{Record, State} = next_record(State0),
next_state(hello, Record, State);
@@ -653,12 +642,10 @@ cipher(#certificate_verify{signature = Signature},
public_key_info = PublicKeyInfo,
negotiated_version = Version,
session = #session{master_secret = MasterSecret},
- key_algorithm = Algorithm,
tls_handshake_hashes = Hashes
} = State0) ->
case ssl_handshake:certificate_verify(Signature, PublicKeyInfo,
- Version, MasterSecret,
- Algorithm, Hashes) of
+ Version, MasterSecret, Hashes) of
valid ->
{Record, State} = next_record(State0),
next_state(cipher, Record, State);
@@ -674,51 +661,39 @@ cipher(#finished{verify_data = Data} = Finished,
role = Role,
session = #session{master_secret = MasterSecret}
= Session0,
- tls_handshake_hashes = Hashes0,
- connection_states = ConnectionStates0} = State) ->
-
+ tls_handshake_hashes = Hashes0} = State) ->
case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
MasterSecret, Hashes0) of
verified ->
Session = register_session(Role, Host, Port, Session0),
- case Role of
- client ->
- ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
- next_state_connection(cipher, ack_connection(State#state{session = Session,
- connection_states = ConnectionStates}));
- server ->
- ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
- {ConnectionStates, Hashes} =
- finalize_handshake(State#state{
- connection_states = ConnectionStates1,
- session = Session}, cipher),
- next_state_connection(cipher, ack_connection(State#state{connection_states =
- ConnectionStates,
- session = Session,
- tls_handshake_hashes =
- Hashes}))
- end;
+ cipher_role(Role, Data, Session, State);
#alert{} = Alert ->
handle_own_alert(Alert, Version, cipher, State),
{stop, normal, State}
end;
+cipher(timeout, State) ->
+ { next_state, cipher, State, hibernate };
+
cipher(Msg, State) ->
handle_unexpected_message(Msg, cipher, State).
-
+%%--------------------------------------------------------------------
+-spec connection(#hello_request{} | #client_hello{} | term(),
+ #state{}) -> gen_fsm_state_return().
+%%--------------------------------------------------------------------
connection(#hello_request{}, #state{host = Host, port = Port,
socket = Socket,
+ session = #session{own_certificate = Cert},
ssl_options = SslOpts,
negotiated_version = Version,
transport_cb = Transport,
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _},
tls_handshake_hashes = Hashes0} = State0) ->
-
- Hello = ssl_handshake:client_hello(Host, Port,
- ConnectionStates0, SslOpts, Renegotiation),
+ Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0,
+ SslOpts, Renegotiation, Cert),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
@@ -730,33 +705,30 @@ connection(#hello_request{}, #state{host = Host, port = Port,
connection(#client_hello{} = Hello, #state{role = server} = State) ->
hello(Hello, State);
+connection(timeout, State) ->
+ {next_state, connection, State, hibernate};
+
connection(Msg, State) ->
handle_unexpected_message(Msg, connection, State).
%%--------------------------------------------------------------------
-%% Function:
-%% handle_event(Event, StateName, State) -> {next_state, NextStateName,
-%% NextState} |
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}
+-spec handle_event(term(), state_name(), #state{}) -> term().
+%% As it is not currently used gen_fsm_state_return() makes
+%% dialyzer unhappy!
+%%
%% Description: Whenever a gen_fsm receives an event sent using
%% gen_fsm:send_all_state_event/2, this function is called to handle
-%% the event.
+%% the event. Not currently used!
%%--------------------------------------------------------------------
handle_event(_Event, StateName, State) ->
- {next_state, StateName, State}.
+ {next_state, StateName, State, get_timeout(State)}.
%%--------------------------------------------------------------------
-%% Function:
-%% handle_sync_event(Event, From, StateName,
-%% State) -> {next_state, NextStateName, NextState} |
-%% {next_state, NextStateName, NextState,
-%% Timeout} |
-%% {reply, Reply, NextStateName, NextState}|
-%% {reply, Reply, NextStateName, NextState,
-%% Timeout} |
-%% {stop, Reason, NewState} |
-%% {stop, Reason, Reply, NewState}
+-spec handle_sync_event(term(), from(), state_name(), #state{}) ->
+ gen_fsm_state_return() |
+ {reply, reply(), state_name(), #state{}} |
+ {reply, reply(), state_name(), #state{}, timeout()} |
+ {stop, reason(), reply(), #state{}}.
+%%
%% Description: Whenever a gen_fsm receives an event sent using
%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
%% the event.
@@ -779,7 +751,8 @@ handle_sync_event({application_data, Data0}, From, connection,
{Msgs, [], ConnectionStates} ->
Result = Transport:send(Socket, Msgs),
{reply, Result,
- connection, State#state{connection_states = ConnectionStates}};
+ connection, State#state{connection_states = ConnectionStates},
+ get_timeout(State)};
{Msgs, RestData, ConnectionStates} ->
if
Msgs =/= [] ->
@@ -792,12 +765,14 @@ handle_sync_event({application_data, Data0}, From, connection,
renegotiation = {true, internal}})
end
catch throw:Error ->
- {reply, Error, connection, State}
+ {reply, Error, connection, State, get_timeout(State)}
end;
handle_sync_event({application_data, Data}, From, StateName,
#state{send_queue = Queue} = State) ->
%% In renegotiation priorities handshake, send data when handshake is finished
- {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}};
+ {next_state, StateName,
+ State#state{send_queue = queue:in({From, Data}, Queue)},
+ get_timeout(State)};
handle_sync_event(start, From, hello, State) ->
hello(start, State#state{from = From});
@@ -811,19 +786,35 @@ handle_sync_event(start, From, hello, State) ->
%% here to make sure it is the users problem and not owers if
%% they upgrade a active socket.
handle_sync_event(start, _, connection, State) ->
- {reply, connected, connection, State};
+ {reply, connected, connection, State, get_timeout(State)};
handle_sync_event(start, From, StateName, State) ->
- {next_state, StateName, State#state{from = From}};
+ {next_state, StateName, State#state{from = From}, get_timeout(State)};
-handle_sync_event(close, _, _StateName, State) ->
- {stop, normal, ok, State};
+handle_sync_event(close, _, StateName, State) ->
+ %% Run terminate before returning
+ %% so that the reuseaddr inet-option will work
+ %% as intended.
+ (catch terminate(user_close, StateName, State)),
+ {stop, normal, ok, State#state{terminated = true}};
-handle_sync_event({shutdown, How}, _, StateName,
- #state{transport_cb = CbModule,
+handle_sync_event({shutdown, How0}, _, StateName,
+ #state{transport_cb = Transport,
+ negotiated_version = Version,
+ connection_states = ConnectionStates,
socket = Socket} = State) ->
- case CbModule:shutdown(Socket, How) of
+ case How0 of
+ How when How == write; How == both ->
+ Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+ {BinMsg, _} =
+ encode_alert(Alert, Version, ConnectionStates),
+ Transport:send(Socket, BinMsg);
+ _ ->
+ ok
+ end,
+
+ case Transport:shutdown(Socket, How0) of
ok ->
- {reply, ok, StateName, State};
+ {reply, ok, StateName, State, get_timeout(State)};
Error ->
{stop, normal, Error, State}
end;
@@ -834,30 +825,33 @@ handle_sync_event({recv, N}, From, connection = StateName, State0) ->
%% Doing renegotiate wait with handling request until renegotiate is
%% finished. Will be handled by next_state_connection/2.
handle_sync_event({recv, N}, From, StateName, State) ->
- {next_state, StateName, State#state{bytes_to_read = N, from = From,
- recv_during_renegotiation = true}};
+ {next_state, StateName,
+ State#state{bytes_to_read = N, from = From,
+ recv_during_renegotiation = true},
+ get_timeout(State)};
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
NewMon = erlang:monitor(process, User),
erlang:demonitor(OldMon, [flush]),
- {reply, ok, StateName, State#state{user_application = {NewMon,User}}};
+ {reply, ok, StateName, State#state{user_application = {NewMon,User}},
+ get_timeout(State)};
handle_sync_event({get_opts, OptTags}, _From, StateName,
#state{socket = Socket,
socket_options = SockOpts} = State) ->
OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []),
- {reply, OptsReply, StateName, State};
+ {reply, OptsReply, StateName, State, get_timeout(State)};
handle_sync_event(sockname, _From, StateName,
#state{socket = Socket} = State) ->
SockNameReply = inet:sockname(Socket),
- {reply, SockNameReply, StateName, State};
+ {reply, SockNameReply, StateName, State, get_timeout(State)};
handle_sync_event(peername, _From, StateName,
#state{socket = Socket} = State) ->
PeerNameReply = inet:peername(Socket),
- {reply, PeerNameReply, StateName, State};
+ {reply, PeerNameReply, StateName, State, get_timeout(State)};
handle_sync_event({set_opts, Opts0}, _From, StateName,
#state{socket_options = Opts1,
@@ -867,27 +861,27 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
State1 = State0#state{socket_options = Opts},
if
Opts#socket_options.active =:= false ->
- {reply, ok, StateName, State1};
+ {reply, ok, StateName, State1, get_timeout(State1)};
Buffer =:= <<>>, Opts1#socket_options.active =:= false ->
%% Need data, set active once
{Record, State2} = next_record_if_active(State1),
case next_state(StateName, Record, State2) of
- {next_state, StateName, State} ->
- {reply, ok, StateName, State};
+ {next_state, StateName, State, Timeout} ->
+ {reply, ok, StateName, State, Timeout};
{stop, Reason, State} ->
{stop, Reason, State}
end;
Buffer =:= <<>> ->
%% Active once already set
- {reply, ok, StateName, State1};
+ {reply, ok, StateName, State1, get_timeout(State1)};
true ->
case application_data(<<>>, State1) of
Stop = {stop,_,_} ->
Stop;
{Record, State2} ->
case next_state(StateName, Record, State2) of
- {next_state, StateName, State} ->
- {reply, ok, StateName, State};
+ {next_state, StateName, State, Timeout} ->
+ {reply, ok, StateName, State, Timeout};
{stop, Reason, State} ->
{stop, Reason, State}
end
@@ -898,7 +892,7 @@ handle_sync_event(renegotiate, From, connection, State) ->
renegotiate(State#state{renegotiation = {true, From}});
handle_sync_event(renegotiate, _, StateName, State) ->
- {reply, {error, already_renegotiating}, StateName, State};
+ {reply, {error, already_renegotiating}, StateName, State, get_timeout(State)};
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
@@ -906,26 +900,26 @@ handle_sync_event(info, _, StateName,
AtomVersion = ssl_record:protocol_version(Version),
{reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}},
- StateName, State};
+ StateName, State, get_timeout(State)};
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
{cipher_suite, ssl_cipher:suite_definition(Suite)}],
- StateName, State};
+ StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
- {reply, {ok, Cert}, StateName, State}.
+ {reply, {ok, Cert}, StateName, State, get_timeout(State)}.
%%--------------------------------------------------------------------
-%% Function:
-%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
-%% {next_state, NextStateName, NextState,
-%% Timeout} |
-%% {stop, Reason, NewState}
+-spec handle_info(msg(),state_name(), #state{}) ->
+ {next_state, state_name(), #state{}}|
+ {next_state, state_name(), #state{}, timeout()} |
+ {stop, reason(), #state{}}.
+%%
%% Description: This function is called by a gen_fsm when it receives any
%% other message than a synchronous or asynchronous event
%% (or a system message).
@@ -982,37 +976,43 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _,
handle_info(Msg, StateName, State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
error_logger:info_report(Report),
- {next_state, StateName, State}.
+ {next_state, StateName, State, get_timeout(State)}.
%%--------------------------------------------------------------------
-%% Function: terminate(Reason, StateName, State) -> void()
+-spec terminate(reason(), state_name(), #state{}) -> term().
+%%
%% Description:This function is called by a gen_fsm when it is about
%% to terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_fsm terminates with
%% Reason. The return value is ignored.
%%--------------------------------------------------------------------
-terminate(_Reason, connection, #state{negotiated_version = Version,
+terminate(_, _, #state{terminated = true}) ->
+ %% Happens when user closes the connection using ssl:close/1
+ %% we want to guarantee that Transport:close has been called
+ %% when ssl:close/1 returns.
+ ok;
+terminate(Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate}) ->
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY),
- Version, ConnectionStates),
+ BinAlert = terminate_alert(Reason, Version, ConnectionStates),
Transport:send(Socket, BinAlert),
- workaround_transport_delivery_problems(Socket, Transport),
+ workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket);
-terminate(_Reason, _StateName, #state{transport_cb = Transport,
+terminate(Reason, _StateName, #state{transport_cb = Transport,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate}) ->
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- workaround_transport_delivery_problems(Socket, Transport),
+ workaround_transport_delivery_problems(Socket, Transport, Reason),
Transport:close(Socket).
%%--------------------------------------------------------------------
-%% Function:
+-spec code_change(term(), state_name(), #state{}, list()) -> {ok, state_name(), #state{}}.
+%%
%% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
@@ -1024,116 +1024,121 @@ code_change(_OldVsn, StateName, State, _Extra) ->
%%--------------------------------------------------------------------
start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo,
Timeout) ->
- case ssl_connection_sup:start_child([Role, Host, Port, Socket,
- Opts, User, CbInfo]) of
- {ok, Pid} ->
- case socket_control(Socket, Pid, CbModule) of
- {ok, SslSocket} ->
- case handshake(SslSocket, Timeout) of
- ok ->
- {ok, SslSocket};
- {error, Reason} ->
- {error, Reason}
- end;
- {error, Reason} ->
- {error, Reason}
- end;
- {error, Reason} ->
- {error, Reason}
+ try
+ {ok, Pid} = ssl_connection_sup:start_child([Role, Host, Port, Socket,
+ Opts, User, CbInfo]),
+ {ok, SslSocket} = socket_control(Socket, Pid, CbModule),
+ ok = handshake(SslSocket, Timeout),
+ {ok, SslSocket}
+ catch
+ error:{badmatch, {error, _} = Error} ->
+ Error
end.
-
+
ssl_init(SslOpts, Role) ->
{ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role),
PrivateKey =
init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
- DHParams = init_diffie_hellman(SslOpts#ssl_options.dhfile, Role),
+ DHParams = init_diffie_hellman(SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
{ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}.
-init_certificates(#ssl_options{cacertfile = CACertFile,
- certfile = CertFile}, Role) ->
-
- case ssl_manager:connection_init(CACertFile, Role) of
- {ok, CertDbRef, CacheRef} ->
- init_certificates(CertDbRef, CacheRef, CertFile, Role);
- {error, {badmatch, _Error}} ->
- Report = io_lib:format("SSL: Error ~p Initializing: ~p ~n",
- [_Error, CACertFile]),
- error_logger:error_report(Report),
- throw(ecacertfile);
- {error, _Error} ->
- Report = io_lib:format("SSL: Error ~p Initializing: ~p ~n",
- [_Error, CACertFile]),
- error_logger:error_report(Report),
- throw(ecacertfile)
- end.
-init_certificates(CertDbRef, CacheRef, CertFile, client) ->
+init_certificates(#ssl_options{cacerts = CaCerts,
+ cacertfile = CACertFile,
+ certfile = CertFile,
+ cert = Cert}, Role) ->
+ {ok, CertDbRef, CacheRef} =
+ try
+ Certs = case CaCerts of
+ undefined ->
+ CACertFile;
+ _ ->
+ {der, CaCerts}
+ end,
+ {ok, _, _} = ssl_manager:connection_init(Certs, Role)
+ catch
+ Error:Reason ->
+ handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile,
+ erlang:get_stacktrace())
+ end,
+ init_certificates(Cert, CertDbRef, CacheRef, CertFile, Role).
+
+init_certificates(undefined, CertDbRef, CacheRef, "", _) ->
+ {ok, CertDbRef, CacheRef, undefined};
+
+init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) ->
try
[OwnCert] = ssl_certificate:file_to_certificats(CertFile),
{ok, CertDbRef, CacheRef, OwnCert}
- catch _E:_R ->
+ catch _Error:_Reason ->
{ok, CertDbRef, CacheRef, undefined}
end;
-init_certificates(CertDbRef, CacheRef, CertFile, server) ->
- try
+init_certificates(undefined, CertDbRef, CacheRef, CertFile, server) ->
+ try
[OwnCert] = ssl_certificate:file_to_certificats(CertFile),
{ok, CertDbRef, CacheRef, OwnCert}
- catch
- _E:{badmatch, _R={error,_}} ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, CertFile,
- erlang:get_stacktrace()]),
- error_logger:error_report(Report),
- throw(ecertfile);
- _E:_R ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, CertFile,
- erlang:get_stacktrace()]),
- error_logger:error_report(Report),
- throw(ecertfile)
- end.
-
-init_private_key(undefined, "", _Password, client) ->
+ catch
+ Error:Reason ->
+ handle_file_error(?LINE, Error, Reason, CertFile, ecertfile,
+ erlang:get_stacktrace())
+ end;
+init_certificates(Cert, CertDbRef, CacheRef, _, _) ->
+ {ok, CertDbRef, CacheRef, Cert}.
+
+init_private_key(undefined, "", _Password, _Client) ->
undefined;
init_private_key(undefined, KeyFile, Password, _) ->
- try
- {ok, List} = ssl_manager:cache_pem_file(KeyFile),
- [Der] = [Der || Der = {PKey, _ , _} <- List,
- PKey =:= rsa_private_key orelse
- PKey =:= dsa_private_key],
- {ok, Decoded} = public_key:decode_private_key(Der,Password),
- Decoded
- catch
- _E:{badmatch, _R={error,_}} ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, KeyFile,
- erlang:get_stacktrace()]),
- error_logger:error_report(Report),
- throw(ekeyfile);
- _E:_R ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
- [?LINE, _E,_R, KeyFile,
- erlang:get_stacktrace()]),
- error_logger:error_report(Report),
- throw(ekeyfile)
+ try
+ {ok, List} = ssl_manager:cache_pem_file(KeyFile),
+ [PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List,
+ PKey =:= 'RSAPrivateKey' orelse
+ PKey =:= 'DSAPrivateKey'],
+ public_key:pem_entry_decode(PemEntry, Password)
+ catch
+ Error:Reason ->
+ handle_file_error(?LINE, Error, Reason, KeyFile, ekeyfile,
+ erlang:get_stacktrace())
end;
-init_private_key(PrivateKey, _, _,_) ->
- PrivateKey.
-init_diffie_hellman(_, client) ->
+init_private_key({rsa, PrivateKey}, _, _,_) ->
+ public_key:der_decode('RSAPrivateKey', PrivateKey);
+init_private_key({dsa, PrivateKey},_,_,_) ->
+ public_key:der_decode('DSAPrivateKey', PrivateKey).
+
+-spec(handle_file_error(_,_,_,_,_,_) -> no_return()).
+handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) ->
+ file_error(Line, Error, Reason, File, Throw, Stack);
+handle_file_error(Line, Error, Reason, File, Throw, Stack) ->
+ file_error(Line, Error, Reason, File, Throw, Stack).
+
+-spec(file_error(_,_,_,_,_,_) -> no_return()).
+file_error(Line, Error, Reason, File, Throw, Stack) ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
+ [Line, Error, Reason, File, Stack]),
+ error_logger:error_report(Report),
+ throw(Throw).
+
+init_diffie_hellman(Params, _,_) when is_binary(Params)->
+ public_key:der_decode('DHParameter', Params);
+init_diffie_hellman(_,_, client) ->
undefined;
-init_diffie_hellman(undefined, _) ->
+init_diffie_hellman(_,undefined, _) ->
?DEFAULT_DIFFIE_HELLMAN_PARAMS;
-init_diffie_hellman(DHParamFile, server) ->
- {ok, List} = ssl_manager:cache_pem_file(DHParamFile),
- case [Der || Der = {dh_params, _ , _} <- List] of
- [Der] ->
- {ok, Decoded} = public_key:decode_dhparams(Der),
- Decoded;
- [] ->
- ?DEFAULT_DIFFIE_HELLMAN_PARAMS
+init_diffie_hellman(_, DHParamFile, server) ->
+ try
+ {ok, List} = ssl_manager:cache_pem_file(DHParamFile),
+ case [Entry || Entry = {'DHParameter', _ , _} <- List] of
+ [Entry] ->
+ public_key:pem_entry_decode(Entry);
+ [] ->
+ ?DEFAULT_DIFFIE_HELLMAN_PARAMS
+ end
+ catch
+ Error:Reason ->
+ handle_file_error(?LINE, Error, Reason,
+ DHParamFile, edhfile, erlang:get_stacktrace())
end.
sync_send_all_state_event(FsmPid, Event) ->
@@ -1147,6 +1152,8 @@ sync_send_all_state_event(FsmPid, Event, Timeout) ->
exit:{timeout, _} ->
{error, timeout};
exit:{normal, _} ->
+ {error, closed};
+ exit:{shutdown, _} ->
{error, closed}
end.
@@ -1166,7 +1173,7 @@ certify_client(#state{client_certificate_requested = true, role = client,
transport_cb = Transport,
negotiated_version = Version,
cert_db_ref = CertDbRef,
- own_cert = OwnCert,
+ session = #session{own_certificate = OwnCert},
socket = Socket,
tls_handshake_hashes = Hashes0} = State) ->
Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client),
@@ -1182,41 +1189,44 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
connection_states = ConnectionStates0,
transport_cb = Transport,
negotiated_version = Version,
- own_cert = OwnCert,
socket = Socket,
- key_algorithm = KeyAlg,
private_key = PrivateKey,
- session = #session{master_secret = MasterSecret},
+ session = #session{master_secret = MasterSecret,
+ own_certificate = OwnCert},
tls_handshake_hashes = Hashes0} = State) ->
+
case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret,
- Version, KeyAlg,
- PrivateKey, Hashes0) of
- ignore -> %% No key or cert or fixed_diffie_hellman
- State;
- Verified ->
+ Version, PrivateKey, Hashes0) of
+ #certificate_verify{} = Verified ->
{BinVerified, ConnectionStates1, Hashes1} =
- encode_handshake(Verified, KeyAlg, Version,
+ encode_handshake(Verified, Version,
ConnectionStates0, Hashes0),
Transport:send(Socket, BinVerified),
State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1}
+ tls_handshake_hashes = Hashes1};
+ ignore ->
+ State;
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify, State)
+
end;
verify_client_cert(#state{client_certificate_requested = false} = State) ->
State.
do_server_hello(Type, #state{negotiated_version = Version,
- session = Session,
+ session = #session{session_id = SessId} = Session,
connection_states = ConnectionStates0,
renegotiation = {Renegotiation, _}}
= State0) when is_atom(Type) ->
+
ServerHello =
- ssl_handshake:server_hello(Session#session.session_id, Version,
+ ssl_handshake:server_hello(SessId, Version,
ConnectionStates0, Renegotiation),
State1 = server_hello(ServerHello, State0),
case Type of
new ->
- do_server_hello(ServerHello, State1);
+ new_server_hello(ServerHello, State1);
resumed ->
ConnectionStates1 = State1#state.connection_states,
case ssl_handshake:master_secret(Version, Session,
@@ -1235,9 +1245,9 @@ do_server_hello(Type, #state{negotiated_version = Version,
handle_own_alert(Alert, Version, hello, State1),
{stop, normal, State1}
end
- end;
+ end.
-do_server_hello(#server_hello{cipher_suite = CipherSuite,
+new_server_hello(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression,
session_id = SessionId},
#state{session = Session0,
@@ -1257,6 +1267,33 @@ do_server_hello(#server_hello{cipher_suite = CipherSuite,
{stop, normal, State0}
end.
+handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) ->
+ Session = Session0#session{session_id = NewId,
+ cipher_suite = CipherSuite,
+ compression_method = Compression},
+ {Record, State} = next_record(State0#state{session = Session}),
+ next_state(certify, Record, State).
+
+handle_resumed_session(SessId, #state{connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ host = Host, port = Port,
+ session_cache = Cache,
+ session_cache_cb = CacheCb} = State0) ->
+ Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}),
+ case ssl_handshake:master_secret(Version, Session,
+ ConnectionStates0, client) of
+ {_, ConnectionStates1} ->
+ {Record, State} =
+ next_record(State0#state{
+ connection_states = ConnectionStates1,
+ session = Session}),
+ next_state(abbreviated, Record, State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, hello, State0),
+ {stop, normal, State0}
+ end.
+
+
client_certify_and_key_exchange(#state{negotiated_version = Version} =
State0) ->
try do_client_certify_and_key_exchange(State0) of
@@ -1290,8 +1327,7 @@ server_hello(ServerHello, #state{transport_cb = Transport,
connection_states = ConnectionStates0,
tls_handshake_hashes = Hashes0} = State) ->
CipherSuite = ServerHello#server_hello.cipher_suite,
- {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite),
- %% Version = ServerHello#server_hello.server_version, TODO ska kontrolleras
+ {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
@@ -1312,15 +1348,17 @@ server_hello_done(#state{transport_cb = Transport,
Transport:send(Socket, BinHelloDone),
State#state{connection_states = NewConnectionStates,
tls_handshake_hashes = NewHashes}.
-
-certify_server(#state{transport_cb = Transport,
- socket = Socket,
- negotiated_version = Version,
- connection_states = ConnectionStates,
- tls_handshake_hashes = Hashes,
- cert_db_ref = CertDbRef,
- own_cert = OwnCert} = State) ->
+certify_server(#state{key_algorithm = dh_anon} = State) ->
+ State;
+
+certify_server(#state{transport_cb = Transport,
+ socket = Socket,
+ negotiated_version = Version,
+ connection_states = ConnectionStates,
+ tls_handshake_hashes = Hashes,
+ cert_db_ref = CertDbRef,
+ session = #session{own_certificate = OwnCert}} = State) ->
case ssl_handshake:certificate(OwnCert, CertDbRef, server) of
CertMsg = #certificate{} ->
{BinCertMsg, NewConnectionStates, NewHashes} =
@@ -1333,21 +1371,10 @@ certify_server(#state{transport_cb = Transport,
throw(Alert)
end.
-key_exchange(#state{role = server, key_algorithm = Algo} = State)
- when Algo == rsa;
- Algo == dh_dss;
- Algo == dh_rsa ->
+key_exchange(#state{role = server, key_algorithm = rsa} = State) ->
State;
-
-%% Remove or uncomment when we decide if to support export cipher suites
-%%key_exchange(#state{role = server, key_algorithm = rsa_export} = State) ->
- %% TODO when the public key in the server certificate is
- %% less than or equal to 512 bits in length dont send key_exchange
- %% but do it otherwise
-%% State;
-
key_exchange(#state{role = server, key_algorithm = Algo,
- diffie_hellman_params = Params,
+ diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params,
private_key = PrivateKey,
connection_states = ConnectionStates0,
negotiated_version = Version,
@@ -1356,11 +1383,9 @@ key_exchange(#state{role = server, key_algorithm = Algo,
transport_cb = Transport
} = State)
when Algo == dhe_dss;
- Algo == dhe_dss_export;
Algo == dhe_rsa;
- Algo == dhe_rsa_export ->
-
- Keys = public_key:gen_key(Params),
+ Algo == dh_anon ->
+ Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]),
ConnectionState =
ssl_record:pending_connection_state(ConnectionStates0, read),
SecParams = ConnectionState#connection_state.security_parameters,
@@ -1377,11 +1402,6 @@ key_exchange(#state{role = server, key_algorithm = Algo,
diffie_hellman_keys = Keys,
tls_handshake_hashes = Hashes1};
-
-%% key_algorithm = dh_anon is not supported. Should be by default disabled
-%% if support is implemented and then we need a key_exchange clause for it
-%% here.
-
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = rsa,
@@ -1396,7 +1416,6 @@ key_exchange(#state{role = client,
Transport:send(Socket, BinMsg),
State#state{connection_states = ConnectionStates1,
tls_handshake_hashes = Hashes1};
-
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = Algorithm,
@@ -1405,9 +1424,8 @@ key_exchange(#state{role = client,
socket = Socket, transport_cb = Transport,
tls_handshake_hashes = Hashes0} = State)
when Algorithm == dhe_dss;
- Algorithm == dhe_dss_export;
Algorithm == dhe_rsa;
- Algorithm == dhe_rsa_export ->
+ Algorithm == dh_anon ->
Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
@@ -1415,9 +1433,6 @@ key_exchange(#state{role = client,
State#state{connection_states = ConnectionStates1,
tls_handshake_hashes = Hashes1}.
-%% key_algorithm = dh_rsa | dh_dss are not supported. If we want to
-%% support it we need a key_exchange clause for it here.
-
rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
when Algorithm == ?rsaEncryption;
Algorithm == ?md2WithRSAEncryption;
@@ -1429,20 +1444,6 @@ rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
rsa_key_exchange(_, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
-%% Uncomment if we decide to support cipher suites with key_algorithm
-%% dh_rsa and dh_dss. Could also be removed if we decide support for
-%% this will not be needed. Not supported by openssl!
-%% dh_key_exchange(OwnCert, DhKeys, true) ->
-%% case public_key:pkix_is_fixed_dh_cert(OwnCert) of
-%% true ->
-%% ssl_handshake:key_exchange(client, fixed_diffie_hellman);
-%% false ->
-%% {DhPubKey, _} = DhKeys,
-%% ssl_handshake:key_exchange(client, {dh, DhPubKey})
-%% end;
-%% dh_key_exchange(_, {DhPubKey, _}, false) ->
-%% ssl_handshake:key_exchange(client, {dh, DhPubKey}).
-
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
cert_db_ref = CertDbRef,
@@ -1500,23 +1501,30 @@ save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbrev
save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) ->
ssl_record:set_server_verify_data(current_write, Data, ConnectionStates).
+handle_server_key(#server_key_exchange{params =
+ #server_dh_params{dh_p = P,
+ dh_g = G,
+ dh_y = ServerPublicDhKey},
+ signed_params = <<>>},
+ #state{key_algorithm = dh_anon} = State) ->
+ dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
+
handle_server_key(
#server_key_exchange{params =
#server_dh_params{dh_p = P,
dh_g = G,
dh_y = ServerPublicDhKey},
signed_params = Signed},
- #state{session = Session, negotiated_version = Version, role = Role,
- public_key_info = PubKeyInfo,
+ #state{public_key_info = PubKeyInfo,
key_algorithm = KeyAlgo,
- connection_states = ConnectionStates0} = State) ->
+ connection_states = ConnectionStates} = State) ->
PLen = size(P),
GLen = size(G),
YLen = size(ServerPublicDhKey),
ConnectionState =
- ssl_record:pending_connection_state(ConnectionStates0, read),
+ ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = ConnectionState#connection_state.security_parameters,
#security_parameters{client_random = ClientRandom,
server_random = ServerRandom} = SecParams,
@@ -1530,52 +1538,70 @@ handle_server_key(
case verify_dh_params(Signed, Hash, PubKeyInfo) of
true ->
- PMpint = mpint_binary(P),
- GMpint = mpint_binary(G),
- Keys = {_, ClientDhPrivateKey} =
- crypto:dh_generate_key([PMpint,GMpint]),
- PremasterSecret =
- crypto:dh_compute_key(mpint_binary(ServerPublicDhKey),
- ClientDhPrivateKey, [PMpint, GMpint]),
- case ssl_handshake:master_secret(Version, PremasterSecret,
- ConnectionStates0, Role) of
- {MasterSecret, ConnectionStates} ->
- State#state{diffie_hellman_keys = Keys,
- session =
- Session#session{master_secret
- = MasterSecret},
- connection_states = ConnectionStates};
- #alert{} = Alert ->
- Alert
- end;
+ dh_master_secret(P, G, ServerPublicDhKey, undefined, State);
false ->
- ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)
+ ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) ->
+verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) ->
case public_key:decrypt_public(Signed, PubKey,
[{rsa_pad, rsa_pkcs1_padding}]) of
- Hash ->
+ Hashes ->
true;
_ ->
false
+ end;
+verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) ->
+ public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}).
+
+dh_master_secret(Prime, Base, PublicDhKey, undefined, State) ->
+ PMpint = mpint_binary(Prime),
+ GMpint = mpint_binary(Base),
+ Keys = {_, PrivateDhKey} =
+ crypto:dh_generate_key([PMpint,GMpint]),
+ dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys});
+
+dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey,
+ #state{session = Session,
+ negotiated_version = Version, role = Role,
+ connection_states = ConnectionStates0} = State) ->
+ PremasterSecret =
+ crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey,
+ [PMpint, GMpint]),
+ case ssl_handshake:master_secret(Version, PremasterSecret,
+ ConnectionStates0, Role) of
+ {MasterSecret, ConnectionStates} ->
+ State#state{
+ session =
+ Session#session{master_secret = MasterSecret},
+ connection_states = ConnectionStates};
+ #alert{} = Alert ->
+ Alert
end.
+cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
+ ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0),
+ next_state_connection(cipher, ack_connection(State#state{session = Session,
+ connection_states = ConnectionStates}));
+
+cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) ->
+ ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0),
+ {ConnectionStates, Hashes} =
+ finalize_handshake(State#state{connection_states = ConnectionStates1,
+ session = Session}, cipher),
+ next_state_connection(cipher, ack_connection(State#state{connection_states =
+ ConnectionStates,
+ session = Session,
+ tls_handshake_hashes =
+ Hashes})).
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
- ?DBG_TERM(Alert),
ssl_record:encode_alert_record(Alert, Version, ConnectionStates).
encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) ->
- ?DBG_TERM(#change_cipher_spec{}),
ssl_record:encode_change_cipher_spec(Version, ConnectionStates).
-encode_handshake(HandshakeRec, Version, ConnectionStates, Hashes) ->
- encode_handshake(HandshakeRec, undefined, Version,
- ConnectionStates, Hashes).
-
-encode_handshake(HandshakeRec, SigAlg, Version, ConnectionStates0, Hashes0) ->
- ?DBG_TERM(HandshakeRec),
- Frag = ssl_handshake:encode_handshake(HandshakeRec, Version, SigAlg),
+encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) ->
+ Frag = ssl_handshake:encode_handshake(HandshakeRec, Version),
Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag),
{E, ConnectionStates1} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
@@ -1633,8 +1659,6 @@ application_data(Data, #state{user_application = {_Mon, Pid},
true -> <<Buffer0/binary, Data/binary>>
end,
case get_data(SOpts, BytesToRead, Buffer1) of
- {ok, <<>>, Buffer} -> % no reply, we need more data
- next_record(State0#state{user_data_buffer = Buffer});
{ok, ClientData, Buffer} -> % Send data
SocketOpt = deliver_app_data(SOpts, ClientData, Pid, From),
State = State0#state{user_data_buffer = Buffer,
@@ -1650,12 +1674,16 @@ application_data(Data, #state{user_application = {_Mon, Pid},
true -> %% We have more data
application_data(<<>>, State)
end;
+ {more, Buffer} -> % no reply, we need more data
+ next_record(State0#state{user_data_buffer = Buffer});
{error,_Reason} -> %% Invalid packet in packet mode
deliver_packet_error(SOpts, Buffer1, Pid, From),
{stop, normal, State0}
end.
%% Picks ClientData
+get_data(_, _, <<>>) ->
+ {more, <<>>};
get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
when Raw =:= raw; Raw =:= 0 -> %% Raw Mode
if
@@ -1668,13 +1696,13 @@ get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
{ok, Data, Rest};
true ->
%% Passive Mode not enough data
- {ok, <<>>, Buffer}
+ {more, Buffer}
end;
get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
PacketOpts = [{packet_size, Size}],
case decode_packet(Type, Buffer, PacketOpts) of
{more, _} ->
- {ok, <<>>, Buffer};
+ {more, Buffer};
Decoded ->
Decoded
end.
@@ -1733,11 +1761,13 @@ format_packet_error(#socket_options{active = _, mode = Mode}, Data) ->
format_reply(binary, _, N, Data) when N > 0 -> % Header mode
header(N, Data);
-format_reply(binary, _, _, Data) -> Data;
-format_reply(list, Packet, _, Data) when is_integer(Packet); Packet == raw ->
- binary_to_list(Data);
+format_reply(binary, _, _, Data) ->
+ Data;
+format_reply(list, Packet, _, Data)
+ when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers} ->
+ Data;
format_reply(list, _,_, Data) ->
- Data.
+ binary_to_list(Data).
header(0, <<>>) ->
<<>>;
@@ -1749,13 +1779,7 @@ header(N, Binary) ->
<<?BYTE(ByteN), NewBinary/binary>> = Binary,
[ByteN | header(N-1, NewBinary)].
-%% tcp_closed
-send_or_reply(false, _Pid, undefined, _Data) ->
- Report = io_lib:format("SSL(debug): Unexpected Data ~p ~n",[_Data]),
- error_logger:error_report(Report),
- erlang:error({badarg, _Pid, undefined, _Data}),
- ok;
-send_or_reply(false, _Pid, From, Data) ->
+send_or_reply(false, _Pid, From, Data) when From =/= undefined ->
gen_fsm:reply(From, Data);
send_or_reply(_, Pid, _From, Data) ->
send_user(Pid, Data).
@@ -1768,17 +1792,32 @@ opposite_role(server) ->
send_user(Pid, Msg) ->
Pid ! Msg.
+handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet]} = State) ->
+ FsmReturn = {next_state, StateName, State#state{tls_packets = []}},
+ Handle(Packet, FsmReturn);
+
+handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]} = State0) ->
+ FsmReturn = {next_state, StateName, State0#state{tls_packets = Packets}},
+ case Handle(Packet, FsmReturn) of
+ {next_state, NextStateName, State, _Timeout} ->
+ handle_tls_handshake(Handle, NextStateName, State);
+ {stop, _,_} = Stop ->
+ Stop
+ end.
+
+next_state(_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
+ handle_own_alert(Alert, Version, decipher_error, State),
+ {stop, normal, State};
+
next_state(Next, no_record, State) ->
- {next_state, Next, State};
+ {next_state, Next, State, get_timeout(State)};
next_state(Next, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, State) ->
Alerts = decode_alerts(EncAlerts),
- handle_alerts(Alerts, {next_state, Next, State});
+ handle_alerts(Alerts, {next_state, Next, State, get_timeout(State)});
next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
- State0 = #state{key_algorithm = KeyAlg,
- tls_handshake_buffer = Buf0,
- negotiated_version = Version}) ->
+ State0 = #state{tls_handshake_buffer = Buf0, negotiated_version = Version}) ->
Handle =
fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
%% This message should not be included in handshake
@@ -1801,9 +1840,9 @@ next_state(StateName, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
(_, StopState) -> StopState
end,
try
- {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
- Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}},
- lists:foldl(Handle, Start, Packets)
+ {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0),
+ State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf},
+ handle_tls_handshake(Handle, StateName, State)
catch throw:#alert{} = Alert ->
handle_own_alert(Alert, Version, StateName, State0),
{stop, normal, State0}
@@ -1813,13 +1852,12 @@ next_state(StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State
case application_data(Data, State0) of
Stop = {stop,_,_} ->
Stop;
- {Record, State} ->
+ {Record, State} ->
next_state(StateName, Record, State)
end;
next_state(StateName, #ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = <<1>>} =
_ChangeCipher,
#state{connection_states = ConnectionStates0} = State0) ->
- ?DBG_TERM(_ChangeCipher),
ConnectionStates1 =
ssl_record:activate_pending_connection_state(ConnectionStates0, read),
{Record, State} = next_record(State0#state{connection_states = ConnectionStates1}),
@@ -1840,13 +1878,19 @@ next_tls_record(Data, #state{tls_record_buffer = Buf0,
Alert
end.
-next_record(#state{tls_cipher_texts = [], socket = Socket} = State) ->
+next_record(#state{tls_packets = [], tls_cipher_texts = [], socket = Socket} = State) ->
inet:setopts(Socket, [{active,once}]),
{no_record, State};
-next_record(#state{tls_cipher_texts = [CT | Rest],
+next_record(#state{tls_packets = [], tls_cipher_texts = [CT | Rest],
connection_states = ConnStates0} = State) ->
- {Plain, ConnStates} = ssl_record:decode_cipher_text(CT, ConnStates0),
- {Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}}.
+ case ssl_record:decode_cipher_text(CT, ConnStates0) of
+ {Plain, ConnStates} ->
+ {Plain, State#state{tls_cipher_texts = Rest, connection_states = ConnStates}};
+ #alert{} = Alert ->
+ {Alert, State}
+ end;
+next_record(State) ->
+ {no_record, State}.
next_record_if_active(State =
#state{socket_options =
@@ -1890,14 +1934,22 @@ next_state_connection(StateName, #state{send_queue = Queue0,
next_state_is_connection(State)
end.
+%% In next_state_is_connection/1: clear tls_handshake_hashes,
+%% premaster_secret and public_key_info (only needed during handshake)
+%% to reduce memory foot print of a connection.
next_state_is_connection(State =
#state{recv_during_renegotiation = true, socket_options =
#socket_options{active = false}}) ->
- passive_receive(State#state{recv_during_renegotiation = false}, connection);
+ passive_receive(State#state{recv_during_renegotiation = false,
+ premaster_secret = undefined,
+ public_key_info = undefined,
+ tls_handshake_hashes = {<<>>, <<>>}}, connection);
next_state_is_connection(State0) ->
{Record, State} = next_record_if_active(State0),
- next_state(connection, Record, State).
+ next_state(connection, Record, State#state{premaster_secret = undefined,
+ public_key_info = undefined,
+ tls_handshake_hashes = {<<>>, <<>>}}).
register_session(_, _, _, #session{is_resumable = true} = Session) ->
Session; %% Already registered
@@ -2013,37 +2065,22 @@ handle_alerts([], Result) ->
handle_alerts(_, {stop, _, _} = Stop) ->
%% If it is a fatal alert immediately close
Stop;
-handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
+handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
-handle_alert(#alert{level = ?FATAL} = Alert, connection,
- #state{from = From, user_application = {_Mon, Pid},
- log_alert = Log,
- host = Host, port = Port, session = Session,
- role = Role, socket_options = Opts} = State) ->
- invalidate_session(Role, Host, Port, Session),
- log_alert(Log, connection, Alert),
- alert_user(Opts#socket_options.active, Pid, From, Alert, Role),
- {stop, normal, State};
-
-handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- connection, #state{from = From,
- role = Role,
- user_application = {_Mon, Pid},
- socket_options = Opts} = State) ->
- alert_user(Opts#socket_options.active, Pid, From, Alert, Role),
- {stop, normal, State};
-
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
#state{from = From, host = Host, port = Port, session = Session,
- log_alert = Log, role = Role} = State) ->
+ user_application = {_Mon, Pid},
+ log_alert = Log, role = Role, socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
log_alert(Log, StateName, Alert),
- alert_user(From, Alert, Role),
+ alert_user(StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
+
handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
- _, #state{from = From, role = Role} = State) ->
- alert_user(From, Alert, Role),
+ StateName, #state{from = From, role = Role,
+ user_application = {_Mon, Pid}, socket_options = Opts} = State) ->
+ alert_user(StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
@@ -2066,6 +2103,11 @@ handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, Sta
{Record, State} = next_record(State0),
next_state(StateName, Record, State).
+alert_user(connection, Opts, Pid, From, Alert, Role) ->
+ alert_user(Opts#socket_options.active, Pid, From, Alert, Role);
+alert_user(_, _, _, From, Alert, Role) ->
+ alert_user(From, Alert, Role).
+
alert_user(From, Alert, Role) ->
alert_user(false, no_pid, From, Alert, Role).
@@ -2085,13 +2127,13 @@ alert_user(Active, Pid, From, Alert, Role) ->
{ssl_error, sslsocket(), ReasonCode})
end.
-log_alert(true, StateName, Alert) ->
+log_alert(true, Info, Alert) ->
Txt = ssl_alert:alert_txt(Alert),
- error_logger:format("SSL: ~p: ~s\n", [StateName, Txt]);
+ error_logger:format("SSL: ~p: ~s\n", [Info, Txt]);
log_alert(false, _, _) ->
ok.
-handle_own_alert(Alert, Version, StateName,
+handle_own_alert(Alert, Version, Info,
#state{transport_cb = Transport,
socket = Socket,
from = User,
@@ -2101,25 +2143,24 @@ handle_own_alert(Alert, Version, StateName,
try %% Try to tell the other side
{BinMsg, _} =
encode_alert(Alert, Version, ConnectionStates),
+ linux_workaround_transport_delivery_problems(Alert, Socket),
Transport:send(Socket, BinMsg)
catch _:_ -> %% Can crash if we are in a uninitialized state
ignore
end,
try %% Try to tell the local user
- log_alert(Log, StateName, Alert),
+ log_alert(Log, Info, Alert),
alert_user(User, Alert, Role)
catch _:_ ->
ok
end.
-handle_unexpected_message(_Msg, StateName, #state{negotiated_version = Version} = State) ->
+handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, StateName, State),
+ handle_own_alert(Alert, Version, {Info, Msg}, State),
{stop, normal, State}.
-make_premaster_secret({MajVer, MinVer}, Alg) when Alg == rsa;
- Alg == dh_dss;
- Alg == dh_rsa ->
+make_premaster_secret({MajVer, MinVer}, rsa) ->
Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
<<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
make_premaster_secret(_, _) ->
@@ -2155,7 +2196,7 @@ renegotiate(#state{role = server,
negotiated_version = Version,
connection_states = ConnectionStates0} = State0) ->
HelloRequest = ssl_handshake:hello_request(),
- Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined),
+ Frag = ssl_handshake:encode_handshake(HelloRequest, Version),
Hs0 = ssl_handshake:init_hashes(),
{BinMsg, ConnectionStates} =
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
@@ -2175,7 +2216,38 @@ notify_renegotiater({true, From}) when not is_atom(From) ->
notify_renegotiater(_) ->
ok.
-workaround_transport_delivery_problems(Socket, Transport) ->
+terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown;
+ Reason == user_close ->
+ {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+ Version, ConnectionStates),
+ BinAlert;
+terminate_alert(_, Version, ConnectionStates) ->
+ {BinAlert, _} = encode_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
+ Version, ConnectionStates),
+ BinAlert.
+
+workaround_transport_delivery_problems(_,_, user_close) ->
+ ok;
+workaround_transport_delivery_problems(Socket, Transport, _) ->
+ %% Standard trick to try to make sure all
+ %% data sent to to tcp port is really sent
+ %% before tcp port is closed so that the peer will
+ %% get a correct error message.
inet:setopts(Socket, [{active, false}]),
Transport:shutdown(Socket, write),
Transport:recv(Socket, 0).
+
+linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) ->
+ case os:type() of
+ {unix, linux} ->
+ inet:setopts(Socket, [{nodelay, true}]);
+ _ ->
+ ok
+ end;
+linux_workaround_transport_delivery_problems(_, _) ->
+ ok.
+
+get_timeout(#state{ssl_options=#ssl_options{hibernate_after=undefined}}) ->
+ infinity;
+get_timeout(#state{ssl_options=#ssl_options{hibernate_after=HibernateAfter}}) ->
+ HibernateAfter.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 54938e0fbc..1f4c44d115 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,36 +28,34 @@
-include("ssl_cipher.hrl").
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
--include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/5, server_hello/4, hello/4,
- hello_request/0, certify/7, certificate/3,
- client_certificate_verify/6,
- certificate_verify/6, certificate_request/2,
- key_exchange/2, server_key_exchange_hash/2, finished/4,
- verify_connection/5,
- get_tls_handshake/4,
- server_hello_done/0, sig_alg/1,
- encode_handshake/3, init_hashes/0,
- update_hashes/2, decrypt_premaster_secret/2]).
+-export([master_secret/4, client_hello/6, server_hello/4, hello/4,
+ hello_request/0, certify/6, certificate/3,
+ client_certificate_verify/5, certificate_verify/5,
+ certificate_request/2, key_exchange/2, server_key_exchange_hash/2,
+ finished/4, verify_connection/5, get_tls_handshake/2,
+ decode_client_key/3, server_hello_done/0,
+ encode_handshake/2, init_hashes/0, update_hashes/2,
+ decrypt_premaster_secret/2]).
+
+-type tls_handshake() :: #client_hello{} | #server_hello{} |
+ #server_hello_done{} | #certificate{} | #certificate_request{} |
+ #client_key_exchange{} | #finished{} | #certificate_verify{} |
+ #hello_request{}.
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: client_hello(Host, Port, ConnectionStates, SslOpts) ->
-%% #client_hello{}
-%% Host
-%% Port
-%% ConnectionStates = #connection_states{}
-%% SslOpts = #ssl_options{}
+-spec client_hello(host(), port_num(), #connection_states{},
+ #ssl_options{}, boolean(), der_cert()) -> #client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
- ciphers = Ciphers}
- = SslOpts, Renegotiation) ->
+ ciphers = UserSuites}
+ = SslOpts, Renegotiation, OwnCert) ->
Fun = fun(Version) ->
ssl_record:protocol_version(Version)
@@ -65,8 +63,9 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
-
- Id = ssl_manager:client_session_id(Host, Port, SslOpts),
+ Ciphers = available_suites(UserSuites, Version),
+
+ Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert),
#client_hello{session_id = Id,
client_version = Version,
@@ -78,13 +77,8 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions,
}.
%%--------------------------------------------------------------------
-%% Function: server_hello(SessionId, Version,
-%% ConnectionStates, Renegotiation) -> #server_hello{}
-%% SessionId
-%% Version
-%% ConnectionStates
-%% Renegotiation
-%%
+-spec server_hello(session_id(), tls_version(), #connection_states{},
+ boolean()) -> #server_hello{}.
%%
%% Description: Creates a server hello message.
%%--------------------------------------------------------------------
@@ -102,7 +96,7 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) ->
}.
%%--------------------------------------------------------------------
-%% Function: hello_request() -> #hello_request{}
+-spec hello_request() -> #hello_request{}.
%%
%% Description: Creates a hello request message sent by server to
%% trigger renegotiation.
@@ -111,15 +105,12 @@ hello_request() ->
#hello_request{}.
%%--------------------------------------------------------------------
-%% Function: hello(Hello, Info, Renegotiation) ->
-%% {Version, Id, NewConnectionStates} |
-%% #alert{}
-%%
-%% Hello = #client_hello{} | #server_hello{}
-%% Info = ConnectionStates | {Port, #ssl_options{}, Session,
-%% Cahce, CahceCb, ConnectionStates}
-%% ConnectionStates = #connection_states{}
-%% Renegotiation = boolean()
+-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
+ #connection_states{} | {port_num(), #session{}, cache_ref(),
+ atom(), #connection_states{}, binary()},
+ boolean()) -> {tls_version(), session_id(), #connection_states{}}|
+ {tls_version(), {resumed | new, #session{}},
+ #connection_states{}} | #alert{}.
%%
%% Description: Handles a recieved hello message
%%--------------------------------------------------------------------
@@ -128,16 +119,21 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
session_id = SessionId, renegotiation_info = Info},
#ssl_options{secure_renegotiate = SecureRenegotation},
ConnectionStates0, Renegotiation) ->
-
- case handle_renegotiation_info(client, Info, ConnectionStates0,
- Renegotiation, SecureRenegotation, []) of
- {ok, ConnectionStates1} ->
- ConnectionStates =
- hello_pending_connection_states(client, CipherSuite, Random,
- Compression, ConnectionStates1),
- {Version, SessionId, ConnectionStates};
- #alert{} = Alert ->
- Alert
+
+ case ssl_record:is_acceptable_version(Version) of
+ true ->
+ case handle_renegotiation_info(client, Info, ConnectionStates0,
+ Renegotiation, SecureRenegotation, []) of
+ {ok, ConnectionStates1} ->
+ ConnectionStates =
+ hello_pending_connection_states(client, CipherSuite, Random,
+ Compression, ConnectionStates1),
+ {Version, SessionId, ConnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+ false ->
+ ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
hello(#client_hello{client_version = ClientVersion, random = Random,
@@ -145,14 +141,14 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
renegotiation_info = Info} = Hello,
#ssl_options{versions = Versions,
secure_renegotiate = SecureRenegotation} = SslOpts,
- {Port, Session0, Cache, CacheCb, ConnectionStates0}, Renegotiation) ->
+ {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
Version = select_version(ClientVersion, Versions),
case ssl_record:is_acceptable_version(Version) of
true ->
{Type, #session{cipher_suite = CipherSuite,
compression_method = Compression} = Session}
= select_session(Hello, Port, Session0, Version,
- SslOpts, Cache, CacheCb),
+ SslOpts, Cache, CacheCb, Cert),
case CipherSuite of
no_suite ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY);
@@ -177,71 +173,56 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
end.
%%--------------------------------------------------------------------
-%% Function: certify(Certs, CertDbRef, MaxPathLen) ->
-%% {PeerCert, PublicKeyInfo} | #alert{}
-%%
-%% Certs = #certificate{}
-%% CertDbRef = reference()
-%% MaxPathLen = integer() | nolimit
+-spec certify(#certificate{}, term(), integer() | nolimit,
+ verify_peer | verify_none, {fun(), term},
+ client | server) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
-certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
- MaxPathLen, Verify, VerifyFun, ValidateFun, Role) ->
+certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
+ MaxPathLen, _Verify, VerifyFunAndState, Role) ->
[PeerCert | _] = ASN1Certs,
- VerifyBool = verify_bool(Verify),
- ValidateExtensionFun =
- case ValidateFun of
+ ValidationFunAndState =
+ case VerifyFunAndState of
undefined ->
- fun(Extensions, ValidationState, Verify0, AccError) ->
- ssl_certificate:validate_extensions(Extensions, ValidationState,
- [], Verify0, AccError, Role)
- end;
- Fun ->
- fun(Extensions, ValidationState, Verify0, AccError) ->
- {NewExtensions, NewValidationState, NewAccError}
- = ssl_certificate:validate_extensions(Extensions, ValidationState,
- [], Verify0, AccError, Role),
- Fun(NewExtensions, NewValidationState, Verify0, NewAccError)
- end
+ {fun(OtpCert, ExtensionOrError, SslState) ->
+ ssl_certificate:validate_extension(OtpCert,
+ ExtensionOrError, SslState)
+ end, Role};
+ {Fun, UserState0} ->
+ {fun(OtpCert, ExtensionOrError, {SslState, UserState}) ->
+ case ssl_certificate:validate_extension(OtpCert,
+ ExtensionOrError,
+ SslState) of
+ {valid, NewSslState} ->
+ {valid, {NewSslState, UserState}};
+ {fail, Reason} ->
+ apply_user_fun(Fun, OtpCert, Reason, UserState,
+ SslState);
+ {unknown, _} ->
+ apply_user_fun(Fun, OtpCert,
+ ExtensionOrError, UserState, SslState)
+ end
+ end, {Role, UserState0}}
end,
- try
- %% Allow missing root_cert and check that with VerifyFun
- ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef, false) of
- {TrustedErlCert, CertPath, VerifyErrors} ->
- Result = public_key:pkix_path_validation(TrustedErlCert,
- CertPath,
- [{max_path_length,
- MaxPathLen},
- {verify, VerifyBool},
- {validate_extensions_fun,
- ValidateExtensionFun},
- {acc_errors,
- VerifyErrors}]),
- case Result of
- {error, Reason} ->
- path_validation_alert(Reason, Verify);
- {ok, {PublicKeyInfo,_, []}} ->
- {PeerCert, PublicKeyInfo};
- {ok, {PublicKeyInfo,_, AccErrors = [Error | _]}} ->
- case VerifyFun(AccErrors) of
- true ->
- {PeerCert, PublicKeyInfo};
- false ->
- path_validation_alert(Error, Verify)
- end
- end
- catch
- throw:Alert ->
- Alert
+
+ {TrustedErlCert, CertPath} =
+ ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef),
+
+ case public_key:pkix_path_validation(TrustedErlCert,
+ CertPath,
+ [{max_path_length,
+ MaxPathLen},
+ {verify_fun, ValidationFunAndState}]) of
+ {ok, {PublicKeyInfo,_}} ->
+ {PeerCert, PublicKeyInfo};
+ {error, Reason} ->
+ path_validation_alert(Reason)
end.
-
+
%%--------------------------------------------------------------------
-%% Function: certificate(OwnCert, CertDbRef, Role) -> #certificate{}
-%%
-%% OwnCert = binary()
-%% CertDbRef = term() as returned by ssl_certificate_db:create()
+-spec certificate(der_cert(), term(), client | server) -> #certificate{} | #alert{}.
%%
%% Description: Creates a certificate message.
%%--------------------------------------------------------------------
@@ -253,7 +234,7 @@ certificate(OwnCert, CertDbRef, client) ->
{error, _} ->
%% If no suitable certificate is available, the client
%% SHOULD send a certificate message containing no
- %% certificates. (chapter 7.4.6. rfc 4346)
+ %% certificates. (chapter 7.4.6. RFC 4346)
[]
end,
#certificate{asn1_certificates = Chain};
@@ -267,57 +248,62 @@ certificate(OwnCert, CertDbRef, server) ->
end.
%%--------------------------------------------------------------------
-%% Function: client_certificate_verify(Cert, ConnectionStates) ->
-%% #certificate_verify{} | ignore
-%% Cert = #'OTPcertificate'{}
-%% ConnectionStates = #connection_states{}
+-spec client_certificate_verify(undefined | der_cert(), binary(),
+ tls_version(), private_key(),
+ {{binary(), binary()},{binary(), binary()}}) ->
+ #certificate_verify{} | ignore | #alert{}.
%%
%% Description: Creates a certificate_verify message, called by the client.
%%--------------------------------------------------------------------
-client_certificate_verify(undefined, _, _, _, _, _) ->
+client_certificate_verify(undefined, _, _, _, _) ->
ignore;
-client_certificate_verify(_, _, _, _, undefined, _) ->
+client_certificate_verify(_, _, _, undefined, _) ->
ignore;
-client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm,
+client_certificate_verify(OwnCert, MasterSecret, Version,
PrivateKey, {Hashes0, _}) ->
case public_key:pkix_is_fixed_dh_cert(OwnCert) of
true ->
- ignore;
+ ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
false ->
Hashes =
calc_certificate_verify(Version, MasterSecret,
- Algorithm, Hashes0),
+ alg_oid(PrivateKey), Hashes0),
Signed = digitally_signed(Hashes, PrivateKey),
#certificate_verify{signature = Signed}
end.
%%--------------------------------------------------------------------
-%% Function: certificate_verify(Signature, PublicKeyInfo) -> valid | #alert{}
-%%
-%% Signature = binary()
-%% PublicKeyInfo = {Algorithm, PublicKey, PublicKeyParams}
+-spec certificate_verify(binary(), public_key_info(), tls_version(),
+ binary(), {_, {binary(), binary()}}) -> valid | #alert{}.
%%
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
-certificate_verify(Signature, {_, PublicKey, _}, Version,
- MasterSecret, Algorithm, {_, Hashes0})
- when Algorithm == rsa;
- Algorithm == dh_rsa;
- Algorithm == dhe_rsa ->
+certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version,
+ MasterSecret, {_, Hashes0}) ->
Hashes = calc_certificate_verify(Version, MasterSecret,
Algorithm, Hashes0),
- case public_key:decrypt_public(Signature, PublicKey,
+ case public_key:decrypt_public(Signature, PublicKey,
[{rsa_pad, rsa_pkcs1_padding}]) of
Hashes ->
valid;
_ ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
+ end;
+certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version,
+ MasterSecret, {_, Hashes0}) ->
+ Hashes = calc_certificate_verify(Version, MasterSecret,
+ Algorithm, Hashes0),
+ case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of
+ true ->
+ valid;
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE)
end.
-%% TODO dsa clause
+
%%--------------------------------------------------------------------
-%% Function: certificate_request(ConnectionStates, CertDbRef) ->
-%% #certificate_request{}
+-spec certificate_request(#connection_states{}, certdb_ref()) ->
+ #certificate_request{}.
%%
%% Description: Creates a certificate_request message, called by the server.
%%--------------------------------------------------------------------
@@ -333,11 +319,12 @@ certificate_request(ConnectionStates, CertDbRef) ->
}.
%%--------------------------------------------------------------------
-%% Function: key_exchange(Role, Secret, Params) ->
-%% #client_key_exchange{} | #server_key_exchange{}
-%%
-%% Secret -
-%% Params -
+-spec key_exchange(client | server,
+ {premaster_secret, binary(), public_key_info()} |
+ {dh, binary()} |
+ {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(),
+ binary(), binary(), private_key()}) ->
+ #client_key_exchange{} | #server_key_exchange{}.
%%
%% Description: Creates a keyexchange message.
%%--------------------------------------------------------------------
@@ -346,20 +333,13 @@ key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) ->
encrypted_premaster_secret(Secret, PublicKey),
#client_key_exchange{exchange_keys = EncPremasterSecret};
-%% Uncomment if dh_rsa and dh_dss cipher suites should
-%% be supported.
-%% key_exchange(client, fixed_diffie_hellman) ->
-%% #client_key_exchange{exchange_keys =
-%% #client_diffie_hellman_public{
-%% dh_public = <<>>
-%% }};
key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
#client_key_exchange{
exchange_keys = #client_diffie_hellman_public{
dh_public = PublicKey}
};
-key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _},
+key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _},
#'DHParameter'{prime = P, base = G},
KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) ->
<<?UINT32(_), PBin/binary>> = crypto:mpint(P),
@@ -368,28 +348,28 @@ key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _},
GLen = byte_size(GBin),
YLen = byte_size(PublicKey),
ServerDHParams = #server_dh_params{dh_p = PBin,
- dh_g = GBin, dh_y = PublicKey},
-
- Hash =
- server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
- ServerRandom/binary,
- ?UINT16(PLen), PBin/binary,
- ?UINT16(GLen), GBin/binary,
- ?UINT16(YLen), PublicKey/binary>>),
- Signed = digitally_signed(Hash, PrivateKey),
- #server_key_exchange{params = ServerDHParams,
- signed_params = Signed}.
+ dh_g = GBin, dh_y = PublicKey},
+
+ case KeyAlgo of
+ dh_anon ->
+ #server_key_exchange{params = ServerDHParams,
+ signed_params = <<>>};
+ _ ->
+ Hash =
+ server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ ?UINT16(PLen), PBin/binary,
+ ?UINT16(GLen), GBin/binary,
+ ?UINT16(YLen), PublicKey/binary>>),
+ Signed = digitally_signed(Hash, PrivateKey),
+ #server_key_exchange{params = ServerDHParams,
+ signed_params = Signed}
+ end.
%%--------------------------------------------------------------------
-%% Function: master_secret(Version, Session/PremasterSecret,
-%% ConnectionStates, Role) ->
-%% {MasterSecret, NewConnectionStates} | #alert{}
-%% Version = #protocol_version{}
-%% Session = #session{} (session contains master secret)
-%% PremasterSecret = binary()
-%% ConnectionStates = #connection_states{}
-%% Role = client | server
-%%
+-spec master_secret(tls_version(), #session{} | binary(), #connection_states{},
+ client | server) -> {binary(), #connection_states{}} | #alert{}.
+%%
%% Description: Sets or calculates the master secret and calculate keys,
%% updating the pending connection states. The Mastersecret and the update
%% connection states are returned or an alert if the calculation fails.
@@ -426,9 +406,8 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) ->
end.
%%--------------------------------------------------------------------
-%% Function: finished(Version, Role, MacSecret, Hashes) -> #finished{}
-%%
-%% ConnectionStates = #connection_states{}
+-spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) ->
+ #finished{}.
%%
%% Description: Creates a handshake finished message
%%-------------------------------------------------------------------
@@ -437,15 +416,8 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
calc_finished(Version, Role, MasterSecret, Hashes)}.
%%--------------------------------------------------------------------
-%% Function: verify_connection(Finished, Role,
-%% MasterSecret, Hashes) -> verified | #alert{}
-%%
-%% Finished = #finished{}
-%% Role = client | server - the role of the process that sent the finished
-%% message.
-%% MasterSecret = binary()
-%% Hashes = binary() - {md5_hash, sha_hash}
-%%
+-spec verify_connection(tls_version(), #finished{}, client | server, binary(),
+ {_, {binary(), binary()}}) -> verified | #alert{}.
%%
%% Description: Checks the ssl handshake finished message to verify
%% the connection.
@@ -453,92 +425,153 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes
verify_connection(Version, #finished{verify_data = Data},
Role, MasterSecret, {_, {MD5, SHA}}) ->
%% use the previous hashes
- ?DBG_HEX(crypto:md5_final(MD5)),
- ?DBG_HEX(crypto:sha_final(SHA)),
case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of
Data ->
verified;
- _E ->
- ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
+ _ ->
+ ?ALERT_REC(?FATAL, ?DECRYPT_ERROR)
end.
-
+%%--------------------------------------------------------------------
+-spec server_hello_done() -> #server_hello_done{}.
+%%
+%% Description: Creates a server hello done message.
+%%--------------------------------------------------------------------
server_hello_done() ->
#server_hello_done{}.
%%--------------------------------------------------------------------
-%% Function: encode_handshake(HandshakeRec) -> BinHandshake
-%% HandshakeRec = #client_hello | #server_hello{} | server_hello_done |
-%% #certificate{} | #client_key_exchange{} | #finished{} |
-%% #client_certify_request{}
+-spec encode_handshake(tls_handshake(), tls_version()) -> iolist().
%%
-%% encode a handshake packet to binary
+%% Description: Encode a handshake packet to binary
%%--------------------------------------------------------------------
-encode_handshake(Package, Version, KeyAlg) ->
- SigAlg = sig_alg(KeyAlg),
- {MsgType, Bin} = enc_hs(Package, Version, SigAlg),
+encode_handshake(Package, Version) ->
+ {MsgType, Bin} = enc_hs(Package, Version),
Len = byte_size(Bin),
[MsgType, ?uint24(Len), Bin].
%%--------------------------------------------------------------------
-%% Function: get_tls_handshake(Data, Buffer) -> Result
-%% Result = {[#handshake{}], [Raw], NewBuffer}
-%% Data = Buffer = NewBuffer = Raw = binary()
+-spec get_tls_handshake(binary(), binary() | iolist()) ->
+ {[tls_handshake()], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
-%% and returns it as a list of #handshake, also returns leftover
+%% and returns it as a list of handshake messages, also returns leftover
%% data.
%%--------------------------------------------------------------------
-get_tls_handshake(Data, <<>>, KeyAlg, Version) ->
- get_tls_handshake_aux(Data, KeyAlg, Version, []);
-get_tls_handshake(Data, Buffer, KeyAlg, Version) ->
- get_tls_handshake_aux(list_to_binary([Buffer, Data]),
- KeyAlg, Version, []).
+get_tls_handshake(Data, <<>>) ->
+ get_tls_handshake_aux(Data, []);
+get_tls_handshake(Data, Buffer) ->
+ get_tls_handshake_aux(list_to_binary([Buffer, Data]), []).
-get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
- Body:Length/binary,Rest/binary>>, KeyAlg,
- Version, Acc) ->
- Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Type, Body, key_exchange_alg(KeyAlg), Version),
- get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]);
-get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) ->
- {lists:reverse(Acc), Data}.
+%%--------------------------------------------------------------------
+-spec decode_client_key(binary(), key_algo(), tls_version()) ->
+ #encrypted_premaster_secret{} | #client_diffie_hellman_public{}.
+%%
+%% Description: Decode client_key data and return appropriate type
+%%--------------------------------------------------------------------
+decode_client_key(ClientKey, Type, Version) ->
+ dec_client_key(ClientKey, key_exchange_alg(Type), Version).
+
+%%--------------------------------------------------------------------
+-spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}.
+
+%%
+%% Description: Calls crypto hash (md5 and sha) init functions to
+%% initalize the hash context.
+%%--------------------------------------------------------------------
+init_hashes() ->
+ T = {crypto:md5_init(), crypto:sha_init()},
+ {T, T}.
+
+%%--------------------------------------------------------------------
+-spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) ->
+ {{binary(), binary()}, {binary(), binary()}}.
+%%
+%% Description: Calls crypto hash (md5 and sha) update functions to
+%% update the hash context with Data.
+%%--------------------------------------------------------------------
+update_hashes(Hashes, % special-case SSL2 client hello
+ <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>) ->
+ update_hashes(Hashes,
+ <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
+ ?UINT16(CSLength), ?UINT16(0),
+ ?UINT16(CDLength),
+ CipherSuites:CSLength/binary,
+ ChallengeData:CDLength/binary>>);
+update_hashes({{MD50, SHA0}, _Prev}, Data) ->
+ {MD51, SHA1} = {crypto:md5_update(MD50, Data),
+ crypto:sha_update(SHA0, Data)},
+ {{MD51, SHA1}, {MD50, SHA0}}.
+
+%%--------------------------------------------------------------------
+-spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary().
+
+%%
+%% Description: Public key decryption using the private key.
+%%--------------------------------------------------------------------
+decrypt_premaster_secret(Secret, RSAPrivateKey) ->
+ try public_key:decrypt_private(Secret, RSAPrivateKey,
+ [{rsa_pad, rsa_pkcs1_padding}])
+ catch
+ _:_ ->
+ throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR))
+ end.
+
+%%--------------------------------------------------------------------
+-spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary().
+
+%%
+%% Description: Calculate server key exchange hash
+%%--------------------------------------------------------------------
+server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
+ Algorithm == dhe_rsa ->
+ MD5 = crypto:md5(Value),
+ SHA = crypto:sha(Value),
+ <<MD5/binary, SHA/binary>>;
+
+server_key_exchange_hash(dhe_dss, Value) ->
+ crypto:sha(Value).
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-verify_bool(verify_peer) ->
- true;
-verify_bool(verify_none) ->
- false.
+get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
+ Body:Length/binary,Rest/binary>>, Acc) ->
+ Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
+ H = dec_hs(Type, Body),
+ get_tls_handshake_aux(Rest, [{H,Raw} | Acc]);
+get_tls_handshake_aux(Data, Acc) ->
+ {lists:reverse(Acc), Data}.
-path_validation_alert({bad_cert, cert_expired}, _) ->
+path_validation_alert({bad_cert, cert_expired}) ->
?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED);
-path_validation_alert({bad_cert, invalid_issuer}, _) ->
+path_validation_alert({bad_cert, invalid_issuer}) ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, invalid_signature} , _) ->
+path_validation_alert({bad_cert, invalid_signature}) ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, name_not_permitted}, _) ->
+path_validation_alert({bad_cert, name_not_permitted}) ->
?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
-path_validation_alert({bad_cert, unknown_critical_extension}, _) ->
+path_validation_alert({bad_cert, unknown_critical_extension}) ->
?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE);
-path_validation_alert({bad_cert, cert_revoked}, _) ->
+path_validation_alert({bad_cert, cert_revoked}) ->
?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED);
-path_validation_alert(_, _) ->
+path_validation_alert({bad_cert, selfsigned_peer}) ->
+ ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE);
+path_validation_alert({bad_cert, unknown_ca}) ->
+ ?ALERT_REC(?FATAL, ?UNKNOWN_CA);
+path_validation_alert(_) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE).
select_session(Hello, Port, Session, Version,
- #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb) ->
+ #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) ->
SuggestedSessionId = Hello#client_hello.session_id,
SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId,
- SslOpts),
+ SslOpts, Cert),
- Suites = case UserSuites of
- [] ->
- ssl_cipher:suites(Version);
- _ ->
- UserSuites
- end,
-
+ Suites = available_suites(Cert, UserSuites, Version),
case ssl_session:is_new(SuggestedSessionId, SessionId) of
true ->
CipherSuite =
@@ -552,7 +585,17 @@ select_session(Hello, Port, Session, Version,
{resumed, CacheCb:lookup(Cache, {Port, SessionId})}
end.
+available_suites(UserSuites, Version) ->
+ case UserSuites of
+ [] ->
+ ssl_cipher:suites(Version);
+ _ ->
+ UserSuites
+ end.
+available_suites(ServerCert, UserSuites, Version) ->
+ ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version)).
+
cipher_suites(Suites, false) ->
[?TLS_EMPTY_RENEGOTIATION_INFO_SCSV | Suites];
cipher_suites(Suites, true) ->
@@ -725,15 +768,13 @@ master_secret(Version, MasterSecret, #security_parameters{
hash_size = HashSize,
key_material_length = KML,
expanded_key_material_length = EKML,
- iv_size = IVS,
- exportable = Exportable},
+ iv_size = IVS},
ConnectionStates, Role) ->
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV} =
- setup_keys(Version, Exportable, MasterSecret, ServerRandom,
+ setup_keys(Version, MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS),
- ?DBG_HEX(ClientWriteKey),
- ?DBG_HEX(ClientIV),
+
ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates),
ConnStates2 =
ssl_record:set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret,
@@ -746,7 +787,7 @@ master_secret(Version, MasterSecret, #security_parameters{
ServerCipherState, Role)}.
-dec_hs(?HELLO_REQUEST, <<>>, _, _) ->
+dec_hs(?HELLO_REQUEST, <<>>) ->
#hello_request{};
%% Client hello v2.
@@ -756,10 +797,7 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor),
?UINT16(CSLength), ?UINT16(0),
?UINT16(CDLength),
CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>,
- _, _) ->
- ?DBG_HEX(CipherSuites),
- ?DBG_HEX(CipherSuites),
+ ChallengeData:CDLength/binary>>) ->
#client_hello{client_version = {Major, Minor},
random = ssl_ssl2:client_random(ChallengeData, CDLength),
session_id = 0,
@@ -771,8 +809,7 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
?UINT16(Cs_length), CipherSuites:Cs_length/binary,
?BYTE(Cm_length), Comp_methods:Cm_length/binary,
- Extensions/binary>>,
- _, _) ->
+ Extensions/binary>>) ->
RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions),
undefined),
@@ -787,7 +824,7 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
- Cipher_suite:2/binary, ?BYTE(Comp_method)>>, _, _) ->
+ Cipher_suite:2/binary, ?BYTE(Comp_method)>>) ->
#server_hello{
server_version = {Major,Minor},
random = Random,
@@ -799,7 +836,7 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID:SID_length/binary,
Cipher_suite:2/binary, ?BYTE(Comp_method),
- ?UINT16(ExtLen), Extensions:ExtLen/binary>>, _, _) ->
+ ?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []),
undefined),
@@ -810,53 +847,49 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
renegotiation_info = RenegotiationInfo};
-dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) ->
+dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
-%% Uncomment if support for export ciphers is added.
-%% dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary,
-%% ?UINT16(ExpLen), Exp:ExpLen/binary,
-%% ?UINT16(_), Sig/binary>>,
-%% ?KEY_EXCHANGE_RSA, _) ->
-%% #server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
-%% rsa_exponent = Exp},
-%% signed_params = Sig};
+
+dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
+ ?UINT16(GLen), G:GLen/binary,
+ ?UINT16(YLen), Y:YLen/binary,
+ ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon
+ #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
+ dh_y = Y},
+ signed_params = <<>>};
dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
- ?UINT16(_), Sig/binary>>,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ ?UINT16(Len), Sig:Len/binary>>) ->
#server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
dh_y = Y},
signed_params = Sig};
dec_hs(?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
- ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>, _, _) ->
- %% TODO: maybe we should chop up CertAuths into a list?
+ ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) ->
#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths};
-dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) ->
+dec_hs(?SERVER_HELLO_DONE, <<>>) ->
#server_hello_done{};
-dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)->
+dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)->
#certificate_verify{signature = Signature};
-dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
- PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
- #client_key_exchange{exchange_keys = PreSecret};
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>,
- ?KEY_EXCHANGE_RSA, _) ->
- PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
- #client_key_exchange{exchange_keys = PreSecret};
-dec_hs(?CLIENT_KEY_EXCHANGE, <<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- %% TODO: Should check whether the cert already contains a suitable DH-key (7.4.7.2)
- throw(?ALERT_REC(?FATAL, implicit_public_value_encoding));
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
- ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #client_key_exchange{exchange_keys =
- #client_diffie_hellman_public{dh_public = DH_Y}};
-dec_hs(?FINISHED, VerifyData, _, _) ->
+dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) ->
+ #client_key_exchange{exchange_keys = PKEPMS};
+dec_hs(?FINISHED, VerifyData) ->
#finished{verify_data = VerifyData};
-dec_hs(_, _, _, _) ->
+dec_hs(_, _) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
+ #encrypted_premaster_secret{premaster_secret = PKEPMS};
+dec_client_key(<<?UINT16(_), PKEPMS/binary>>, ?KEY_EXCHANGE_RSA, _) ->
+ #encrypted_premaster_secret{premaster_secret = PKEPMS};
+dec_client_key(<<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ throw(?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE));
+dec_client_key(<<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
+ #client_diffie_hellman_public{dh_public = DH_Y}.
+
dec_hello_extensions(<<>>) ->
[];
dec_hello_extensions(<<?UINT16(ExtLen), Extensions:ExtLen/binary>>) ->
@@ -894,14 +927,6 @@ encrypted_premaster_secret(Secret, RSAPublicKey) ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE))
end.
-decrypt_premaster_secret(Secret, RSAPrivateKey) ->
- try public_key:decrypt_private(Secret, RSAPrivateKey,
- [{rsa_pad, rsa_pkcs1_padding}])
- catch
- _:_ ->
- throw(?ALERT_REC(?FATAL, ?DECRYPTION_FAILED))
- end.
-
%% encode/decode stream of certificate data to/from list of certificate data
certs_to_list(ASN1Certs) ->
certs_to_list(ASN1Certs, []).
@@ -917,14 +942,14 @@ certs_from_list(ACList) ->
<<?UINT24(CertLen), Cert/binary>>
end || Cert <- ACList]).
-enc_hs(#hello_request{}, _Version, _) ->
+enc_hs(#hello_request{}, _Version) ->
{?HELLO_REQUEST, <<>>};
enc_hs(#client_hello{client_version = {Major, Minor},
random = Random,
session_id = SessionID,
cipher_suites = CipherSuites,
compression_methods = CompMethods,
- renegotiation_info = RenegotiationInfo}, _Version, _) ->
+ renegotiation_info = RenegotiationInfo}, _Version) ->
SIDLength = byte_size(SessionID),
BinCompMethods = list_to_binary(CompMethods),
CmLength = byte_size(BinCompMethods),
@@ -942,31 +967,20 @@ enc_hs(#server_hello{server_version = {Major, Minor},
session_id = Session_ID,
cipher_suite = Cipher_suite,
compression_method = Comp_method,
- renegotiation_info = RenegotiationInfo}, _Version, _) ->
+ renegotiation_info = RenegotiationInfo}, _Version) ->
SID_length = byte_size(Session_ID),
Extensions = hello_extensions(RenegotiationInfo),
ExtensionsBin = enc_hello_extensions(Extensions),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
-enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version, _) ->
+enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) ->
ASN1Certs = certs_from_list(ASN1CertList),
ACLen = erlang:iolist_size(ASN1Certs),
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
-%% Uncomment if support for export ciphers is added.
-%% enc_hs(#server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
-%% rsa_exponent = Exp},
-%% signed_params = SignedParams}, _Version, _) ->
-%% ModLen = byte_size(Mod),
-%% ExpLen = byte_size(Exp),
-%% SignedLen = byte_size(SignedParams),
-%% {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen),Mod/binary,
-%% ?UINT16(ExpLen), Exp/binary,
-%% ?UINT16(SignedLen), SignedParams/binary>>
-%% };
enc_hs(#server_key_exchange{params = #server_dh_params{
dh_p = P, dh_g = G, dh_y = Y},
- signed_params = SignedParams}, _Version, _) ->
+ signed_params = SignedParams}, _Version) ->
PLen = byte_size(P),
GLen = byte_size(G),
YLen = byte_size(Y),
@@ -978,21 +992,21 @@ enc_hs(#server_key_exchange{params = #server_dh_params{
};
enc_hs(#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths},
- _Version, _) ->
+ _Version) ->
CertTypesLen = byte_size(CertTypes),
CertAuthsLen = byte_size(CertAuths),
{?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes/binary,
?UINT16(CertAuthsLen), CertAuths/binary>>
};
-enc_hs(#server_hello_done{}, _Version, _) ->
+enc_hs(#server_hello_done{}, _Version) ->
{?SERVER_HELLO_DONE, <<>>};
-enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version, _) ->
+enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) ->
{?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)};
-enc_hs(#certificate_verify{signature = BinSig}, _, _) ->
+enc_hs(#certificate_verify{signature = BinSig}, _) ->
EncSig = enc_bin_sig(BinSig),
{?CERTIFICATE_VERIFY, EncSig};
-enc_hs(#finished{verify_data = VerifyData}, _Version, _) ->
+enc_hs(#finished{verify_data = VerifyData}, _Version) ->
{?FINISHED, VerifyData}.
enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) ->
@@ -1031,29 +1045,6 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest
Len = InfoLen +1,
enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>).
-init_hashes() ->
- T = {crypto:md5_init(), crypto:sha_init()},
- {T, T}.
-
-update_hashes(Hashes, % special-case SSL2 client hello
- <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>) ->
- update_hashes(Hashes,
- <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor),
- ?UINT16(CSLength), ?UINT16(0),
- ?UINT16(CDLength),
- CipherSuites:CSLength/binary,
- ChallengeData:CDLength/binary>>);
-update_hashes({{MD50, SHA0}, _Prev}, Data) ->
- ?DBG_HEX(Data),
- {MD51, SHA1} = {crypto:md5_update(MD50, Data),
- crypto:sha_update(SHA0, Data)},
- ?DBG_HEX(crypto:md5_final(MD51)),
- ?DBG_HEX(crypto:sha_final(SHA1)),
- {{MD51, SHA1}, {MD50, SHA0}}.
from_3bytes(Bin3) ->
from_3bytes(Bin3, []).
@@ -1073,25 +1064,21 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) ->
certificate_types({KeyExchange, _, _, _})
when KeyExchange == rsa;
- KeyExchange == dh_dss;
- KeyExchange == dh_rsa;
KeyExchange == dhe_dss;
KeyExchange == dhe_rsa ->
<<?BYTE(?RSA_SIGN), ?BYTE(?DSS_SIGN)>>;
certificate_types(_) ->
- %%TODO: Is this a good default,
- %% is there a case where we like to request
- %% a RSA_FIXED_DH or DSS_FIXED_DH
<<?BYTE(?RSA_SIGN)>>.
certificate_authorities(CertDbRef) ->
Authorities = certificate_authorities_from_db(CertDbRef),
Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
- Subj = public_key:pkix_transform(OTPSubj, encode),
- {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj),
- DNEncodedBin = iolist_to_binary(DNEncoded),
+ DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp),
+ %%Subj = public_key:pkix_transform(OTPSubj, encode),
+ %% {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj),
+ %% DNEncodedBin = iolist_to_binary(DNEncoded),
DNEncodedLen = byte_size(DNEncodedBin),
<<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
end,
@@ -1101,7 +1088,7 @@ certificate_authorities_from_db(CertDbRef) ->
certificate_authorities_from_db(CertDbRef, no_candidate, []).
certificate_authorities_from_db(CertDbRef, PrevKey, Acc) ->
- case ssl_certificate_db:issuer_candidate(PrevKey) of
+ case ssl_manager:issuer_candidate(PrevKey) of
no_more_candidates ->
lists:reverse(Acc);
{{CertDbRef, _, _} = Key, Cert} ->
@@ -1111,13 +1098,12 @@ certificate_authorities_from_db(CertDbRef, PrevKey, Acc) ->
certificate_authorities_from_db(CertDbRef, Key, Acc)
end.
-digitally_signed(Hashes, #'RSAPrivateKey'{} = Key) ->
- public_key:encrypt_private(Hashes, Key,
+digitally_signed(Hash, #'RSAPrivateKey'{} = Key) ->
+ public_key:encrypt_private(Hash, Key,
[{rsa_pad, rsa_pkcs1_padding}]);
-digitally_signed(Hashes, #'DSAPrivateKey'{} = Key) ->
- public_key:sign(Hashes, Key).
-
-
+digitally_signed(Hash, #'DSAPrivateKey'{} = Key) ->
+ public_key:sign(Hash, none, Key).
+
calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) ->
ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom);
@@ -1125,20 +1111,15 @@ calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom)
when N == 1; N == 2 ->
ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom).
-setup_keys({3,0}, Exportable, MasterSecret,
+setup_keys({3,0}, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) ->
- ssl_ssl3:setup_keys(Exportable, MasterSecret, ServerRandom,
+ ssl_ssl3:setup_keys(MasterSecret, ServerRandom,
ClientRandom, HashSize, KML, EKML, IVS);
-setup_keys({3,1}, _Exportable, MasterSecret,
+setup_keys({3,1}, MasterSecret,
ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) ->
ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
- KML, IVS);
-
-setup_keys({3,2}, _Exportable, MasterSecret,
- ServerRandom, ClientRandom, HashSize, KML, _EKML, _IVS) ->
- ssl_tls1:setup_keys(MasterSecret, ServerRandom,
- ClientRandom, HashSize, KML).
+ KML, IVS).
calc_finished({3, 0}, Role, MasterSecret, Hashes) ->
ssl_ssl3:finished(Role, MasterSecret, Hashes);
@@ -1152,36 +1133,6 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes)
when N == 1; N == 2 ->
ssl_tls1:certificate_verify(Algorithm, Hashes).
-server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
- Algorithm == dh_rsa;
- Algorithm == dhe_rsa ->
- MD5Context = crypto:md5_init(),
- NewMD5Context = crypto:md5_update(MD5Context, Value),
- MD5 = crypto:md5_final(NewMD5Context),
-
- SHAContext = crypto:sha_init(),
- NewSHAContext = crypto:sha_update(SHAContext, Value),
- SHA = crypto:sha_final(NewSHAContext),
-
- <<MD5/binary, SHA/binary>>;
-
-server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss;
- Algorithm == dhe_dss ->
-
- SHAContext = crypto:sha_init(),
- NewSHAContext = crypto:sha_update(SHAContext, Value),
- crypto:sha_final(NewSHAContext).
-
-
-sig_alg(dh_anon) ->
- ?SIGNATURE_ANONYMOUS;
-sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa ->
- ?SIGNATURE_RSA;
-sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss ->
- ?SIGNATURE_DSA;
-sig_alg(_) ->
- ?NULL.
-
key_exchange_alg(rsa) ->
?KEY_EXCHANGE_RSA;
key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
@@ -1189,3 +1140,18 @@ key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
?KEY_EXCHANGE_DIFFIE_HELLMAN;
key_exchange_alg(_) ->
?NULL.
+
+apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) ->
+ case Fun(OtpCert, ExtensionOrError, UserState0) of
+ {valid, UserState} ->
+ {valid, {SslState, UserState}};
+ {fail, _} = Fail ->
+ Fail;
+ {unknown, UserState} ->
+ {unknown, {SslState, UserState}}
+ end.
+
+alg_oid(#'RSAPrivateKey'{}) ->
+ ?'rsaEncryption';
+alg_oid(#'DSAPrivateKey'{}) ->
+ ?'id-dsa'.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 74fba3786c..fb0ebac7d1 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,9 +26,16 @@
-ifndef(ssl_handshake).
-define(ssl_handshake, true).
+-include_lib("public_key/include/public_key.hrl").
+
+-type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'.
+-type public_key_params() :: #'Dss-Parms'{} | term().
+-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
+
-record(session, {
session_id,
peer_certificate,
+ own_certificate,
compression_method,
cipher_suite,
master_secret,
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index fdc0c33750..c28daa271e 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,10 +19,29 @@
%%
-
-ifndef(ssl_internal).
-define(ssl_internal, true).
+-include_lib("public_key/include/public_key.hrl").
+
+-type reason() :: term().
+-type reply() :: term().
+-type msg() :: term().
+-type from() :: term().
+-type host() :: string() | tuple().
+-type port_num() :: integer().
+-type session_id() :: 0 | binary().
+-type tls_version() :: {integer(), integer()}.
+-type tls_atom_version() :: sslv3 | tlsv1.
+-type cache_ref() :: term().
+-type certdb_ref() :: term().
+-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon.
+-type der_cert() :: binary().
+-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}.
+-type issuer() :: tuple().
+-type serialnumber() :: integer().
+-type cert_key() :: {reference(), integer(), issuer()}.
+
%% basic binary constructors
-define(BOOLEAN(X), X:8/unsigned-big-integer).
-define(BYTE(X), X:8/unsigned-big-integer).
@@ -61,10 +80,13 @@
validate_extensions_fun,
depth, % integer()
certfile, % file()
+ cert, % der_encoded()
keyfile, % file()
- key, %
+ key, % der_encoded()
password, %
+ cacerts, % [der_encoded()]
cacertfile, % file()
+ dh, % der_encoded()
dhfile, % file()
ciphers, %
%% Local policy for the server if it want's to reuse the session
@@ -76,7 +98,11 @@
reuse_sessions, % boolean()
renegotiate_at,
secure_renegotiate,
- debug %
+ debug,
+ hibernate_after % undefined if not hibernating,
+ % or number of ms of inactivity
+ % after which ssl_connection will
+ % go into hibernation
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 0151426d43..f845b1ecc0 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -24,10 +24,13 @@
-module(ssl_manager).
-behaviour(gen_server).
+-include("ssl_internal.hrl").
+
%% Internal application API
--export([start_link/0, start_link/1,
+-export([start_link/1,
connection_init/2, cache_pem_file/1,
- lookup_trusted_cert/3, client_session_id/3, server_session_id/3,
+ lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4,
+ server_session_id/4,
register_session/2, register_session/3, invalidate_session/2,
invalidate_session/3]).
@@ -40,6 +43,7 @@
-include("ssl_handshake.hrl").
-include("ssl_internal.hrl").
+-include_lib("kernel/include/file.hrl").
-record(state, {
session_cache,
@@ -58,63 +62,84 @@
%% API
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
+-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}.
+%%
%% Description: Starts the server
%%--------------------------------------------------------------------
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
start_link(Opts) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []).
%%--------------------------------------------------------------------
-%% Function:
-%% Description:
+-spec connection_init(string()| {der, list()}, client | server) -> {ok, reference(), cache_ref()}.
+%%
+%% Description: Do necessary initializations for a new connection.
%%--------------------------------------------------------------------
-connection_init(TrustedcertsFile, Role) ->
- call({connection_init, TrustedcertsFile, Role}).
-
-cache_pem_file(File) ->
- case ssl_certificate_db:lookup_cached_certs(File) of
- [{_,Content}] ->
- {ok, Content};
- [] ->
- call({cache_pem, File})
- end.
-
+connection_init(Trustedcerts, Role) ->
+ call({connection_init, Trustedcerts, Role}).
+%%--------------------------------------------------------------------
+-spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}.
+%%
+%% Description: Cach a pem file and return its content.
%%--------------------------------------------------------------------
-%% Function:
-%% Description:
+cache_pem_file(File) ->
+ try file:read_file_info(File) of
+ {ok, #file_info{mtime = LastWrite}} ->
+ cache_pem_file(File, LastWrite)
+ catch
+ _:Reason ->
+ {error, Reason}
+ end.
%%--------------------------------------------------------------------
-lookup_trusted_cert(SerialNumber, Issuer, Ref) ->
+-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) ->
+ undefined |
+ {ok, {der_cert(), #'OTPCertificate'{}}}.
+%%
+%% Description: Lookup the trusted cert with Key = {reference(),
+%% serialnumber(), issuer()}.
+%% --------------------------------------------------------------------
+lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer).
-
%%--------------------------------------------------------------------
-%% Function:
-%% Description:
+-spec issuer_candidate(cert_key() | no_candidate) ->
+ {cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates.
+%%
+%% Description: Return next issuer candidate.
%%--------------------------------------------------------------------
-client_session_id(Host, Port, SslOpts) ->
- call({client_session_id, Host, Port, SslOpts}).
-
+issuer_candidate(PrevCandidateKey) ->
+ ssl_certificate_db:issuer_candidate(PrevCandidateKey).
%%--------------------------------------------------------------------
-%% Function:
-%% Description:
+-spec client_session_id(host(), port_num(), #ssl_options{},
+ der_cert() | undefined) -> session_id().
+%%
+%% Description: Select a session id for the client.
%%--------------------------------------------------------------------
-server_session_id(Port, SuggestedSessionId, SslOpts) ->
- call({server_session_id, Port, SuggestedSessionId, SslOpts}).
+client_session_id(Host, Port, SslOpts, OwnCert) ->
+ call({client_session_id, Host, Port, SslOpts, OwnCert}).
%%--------------------------------------------------------------------
-%% Function:
-%% Description:
+-spec server_session_id(host(), port_num(), #ssl_options{}, der_cert()) -> session_id().
+%%
+%% Description: Select a session id for the server.
+%%--------------------------------------------------------------------
+server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) ->
+ call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}).
+
+%%--------------------------------------------------------------------
+-spec register_session(port_num(), #session{}) -> ok.
+-spec register_session(host(), port_num(), #session{}) -> ok.
+%%
+%% Description: Make the session available for reuse.
%%--------------------------------------------------------------------
register_session(Host, Port, Session) ->
cast({register_session, Host, Port, Session}).
register_session(Port, Session) ->
cast({register_session, Port, Session}).
-
%%--------------------------------------------------------------------
-%% Function:
-%% Description:
+-spec invalidate_session(port_num(), #session{}) -> ok.
+-spec invalidate_session(host(), port_num(), #session{}) -> ok.
+%%
+%% Description: Make the session unavilable for reuse.
%%--------------------------------------------------------------------
invalidate_session(Host, Port, Session) ->
cast({invalidate_session, Host, Port, Session}).
@@ -127,34 +152,36 @@ invalidate_session(Port, Session) ->
%%====================================================================
%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
+-spec init(list()) -> {ok, #state{}}.
+%% Possible return values not used now.
+%% | {ok, #state{}, timeout()} | ignore | {stop, term()}.
+%%
%% Description: Initiates the server
%%--------------------------------------------------------------------
-init(Opts) ->
+init([Opts]) ->
process_flag(trap_exit, true),
- CacheCb = proplists:get_value(session_cache, Opts, ssl_session_cache),
+ CacheCb = proplists:get_value(session_cb, Opts, ssl_session_cache),
SessionLifeTime =
proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
CertDb = ssl_certificate_db:create(),
- SessionCache = CacheCb:init(),
+ SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])),
Timer = erlang:send_after(SessionLifeTime * 1000,
self(), validate_sessions),
{ok, #state{certificate_db = CertDb,
session_cache = SessionCache,
session_cache_cb = CacheCb,
- session_lifetime = SessionLifeTime ,
+ session_lifetime = SessionLifeTime,
session_validation_timer = Timer}}.
%%--------------------------------------------------------------------
-%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
-%% {reply, Reply, State, Timeout} |
-%% {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, Reply, State} |
-%% {stop, Reason, State}
+-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}.
+%% Possible return values not used now.
+%% {reply, reply(), #state{}, timeout()} |
+%% {noreply, #state{}} |
+%% {noreply, #state{}, timeout()} |
+%% {stop, reason(), reply(), #state{}} |
+%% {stop, reason(), #state{}}.
+%%
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({{connection_init, "", _Role}, Pid}, _From,
@@ -163,52 +190,55 @@ handle_call({{connection_init, "", _Role}, Pid}, _From,
Result = {ok, make_ref(), Cache},
{reply, Result, State};
-handle_call({{connection_init, TrustedcertsFile, _Role}, Pid}, _From,
+handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From,
#state{certificate_db = Db,
session_cache = Cache} = State) ->
erlang:monitor(process, Pid),
Result =
try
- {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, TrustedcertsFile, Db),
+ {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db),
{ok, Ref, Cache}
catch
- _:{badmatch, Error} ->
- {error, Error};
- _E:_R ->
- {error, {_R,erlang:get_stacktrace()}}
+ _:Reason ->
+ {error, Reason}
end,
{reply, Result, State};
-handle_call({{client_session_id, Host, Port, SslOpts}, _}, _,
+handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _,
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
- Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb),
+ Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert),
{reply, Id, State};
-handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts}, _},
+handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _},
_, #state{session_cache_cb = CacheCb,
session_cache = Cache,
session_lifetime = LifeTime} = State) ->
Id = ssl_session:id(Port, SuggestedSessionId, SslOpts,
- Cache, CacheCb, LifeTime),
+ Cache, CacheCb, LifeTime, OwnCert),
{reply, Id, State};
-handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) ->
- try ssl_certificate_db:cache_pem_file(Pid,File,Db) of
+handle_call({{cache_pem, File, LastWrite}, Pid}, _,
+ #state{certificate_db = Db} = State) ->
+ try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of
Result ->
{reply, Result, State}
- catch _:{badmatch, Reason} ->
- {reply, Reason, State};
- _:Reason ->
+ catch
+ _:Reason ->
{reply, {error, Reason}, State}
end;
-
-handle_call(_,_, State) ->
- {reply, ok, State}.
+handle_call({{recache_pem, File, LastWrite}, Pid}, From,
+ #state{certificate_db = Db} = State) ->
+ ssl_certificate_db:uncache_pem_file(File, Db),
+ cast({recache_pem, File, LastWrite, Pid, From}),
+ {noreply, State}.
+
%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
+-spec handle_cast(msg(), #state{}) -> {noreply, #state{}}.
+%% Possible return values not used now.
+%% | {noreply, #state{}, timeout()} |
+%% {stop, reason(), #state{}}.
+%%
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({register_session, Host, Port, Session},
@@ -239,12 +269,28 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}},
#state{session_cache = Cache,
session_cache_cb = CacheCb} = State) ->
CacheCb:delete(Cache, {Port, ID}),
- {noreply, State}.
+ {noreply, State};
+
+handle_cast({recache_pem, File, LastWrite, Pid, From},
+ #state{certificate_db = [_, FileToRefDb, _]} = State0) ->
+ case ssl_certificate_db:lookup(File, FileToRefDb) of
+ undefined ->
+ {reply, Msg, State} = handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0),
+ gen_server:reply(From, Msg),
+ {noreply, State};
+ _ -> %% Send message to self letting cleanup messages be handled
+ %% first so that no reference to the old version of file
+ %% exists when we cache the new one.
+ cast({recache_pem, File, LastWrite, Pid, From}),
+ {noreply, State0}
+ end.
%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
+-spec handle_info(msg(), #state{}) -> {noreply, #state{}}.
+%% Possible return values not used now.
+%% |{noreply, #state{}, timeout()} |
+%% {stop, reason(), #state{}}.
+%%
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(validate_sessions, #state{session_cache_cb = CacheCb,
@@ -264,12 +310,14 @@ handle_info({'EXIT', _, _}, State) ->
handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) ->
{noreply, State};
+handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) ->
+ handle_info({remove_trusted_certs, Pid}, State);
handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) ->
erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(),
{remove_trusted_certs, Pid}),
{noreply, State};
handle_info({remove_trusted_certs, Pid},
- State = #state{certificate_db = Db}) ->
+ #state{certificate_db = Db} = State) ->
ssl_certificate_db:remove_trusted_certs(Pid, Db),
{noreply, State};
@@ -277,7 +325,8 @@ handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
+-spec terminate(reason(), #state{}) -> term().
+%%
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
@@ -293,7 +342,8 @@ terminate(_Reason, #state{certificate_db = Db,
ok.
%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
+-spec code_change(term(), #state{}, list()) -> {ok, #state{}}.
+%%
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
@@ -332,10 +382,22 @@ init_session_validator([Cache, CacheCb, LifeTime]) ->
CacheCb:foldl(fun session_validation/2,
LifeTime, Cache).
-session_validation({{Host, Port, _}, Session}, LifeTime) ->
+session_validation({{{Host, Port}, _}, Session}, LifeTime) ->
validate_session(Host, Port, Session, LifeTime),
LifeTime;
session_validation({{Port, _}, Session}, LifeTime) ->
validate_session(Port, Session, LifeTime),
LifeTime.
-
+
+cache_pem_file(File, LastWrite) ->
+ case ssl_certificate_db:lookup_cached_certs(File) of
+ [{_, {Mtime, Content}}] ->
+ case LastWrite of
+ Mtime ->
+ {ok, Content};
+ _ ->
+ call({recache_pem, File, LastWrite})
+ end;
+ [] ->
+ call({cache_pem, File, LastWrite})
+ end.
diff --git a/lib/ssl/src/ssl_pem.erl b/lib/ssl/src/ssl_pem.erl
deleted file mode 100644
index 0a1bf0f32a..0000000000
--- a/lib/ssl/src/ssl_pem.erl
+++ /dev/null
@@ -1,147 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-
--module(ssl_pem).
-
-%%% Purpose: Reading and writing of PEM type encoded files for SSL.
-
-%% NB write_file/2 is only preliminary.
-
-%% PEM encoded files have the following structure:
-%%
-%% <text>
-%% -----BEGIN SOMETHING-----<CR><LF>
-%% <Base64 encoding line><CR><LF>
-%% <Base64 encoding line><CR><LF>
-%% ...
-%% -----END SOMETHING-----<CR><LF>
-%% <text>
-%%
-%% A file can contain several BEGIN/END blocks. Text lines between
-%% blocks are ignored.
-
--export([read_file/1, read_file/2, write_file/2]).
-
-%% Read a PEM file and return each decoding as a binary.
-
-read_file(File) ->
- read_file(File, no_passwd).
-
-read_file(File, Passwd) ->
- {ok, Fd} = file:open(File, [read]),
- Result = decode_file(Fd, Passwd),
- file:close(Fd),
- Result.
-
-decode_file(Fd, Passwd) ->
- decode_file(Fd, [], [], notag, [Passwd]).
-
-decode_file(Fd, _RLs, Ens, notag, Info) ->
- case io:get_line(Fd, "") of
- "-----BEGIN CERTIFICATE REQUEST-----" ++ _ ->
- decode_file(Fd, [], Ens, cert_req, Info);
- "-----BEGIN CERTIFICATE-----" ++ _ ->
- decode_file(Fd, [], Ens, cert, Info);
- "-----BEGIN RSA PRIVATE KEY-----" ++ _ ->
- decode_file(Fd, [], Ens, rsa_private_key, Info);
- eof ->
- {ok, lists:reverse(Ens)};
- _ ->
- decode_file(Fd, [], Ens, notag, Info)
- end;
-decode_file(Fd, RLs, Ens, Tag, Info0) ->
- case io:get_line(Fd, "") of
- "Proc-Type: 4,ENCRYPTED"++_ ->
- Info = dek_info(Fd, Info0),
- decode_file(Fd, RLs, Ens, Tag, Info);
- "-----END" ++ _ -> % XXX sloppy
- Cs = lists:flatten(lists:reverse(RLs)),
- Bin = ssl_base64:join_decode(Cs),
- case Info0 of
- [Password, Cipher, SaltHex | Info1] ->
- Decoded = decode_key(Bin, Password, Cipher, unhex(SaltHex)),
- decode_file(Fd, [], [{Tag, Decoded}| Ens], notag, Info1);
- _ ->
- decode_file(Fd, [], [{Tag, Bin}| Ens], notag, Info0)
- end;
- eof ->
- {ok, lists:reverse(Ens)};
- L ->
- decode_file(Fd, [L|RLs], Ens, Tag, Info0)
- end.
-
-dek_info(Fd, Info) ->
- Line = io:get_line(Fd, ""),
- [_, DekInfo0] = string:tokens(Line, ": "),
- DekInfo1 = string:tokens(DekInfo0, ",\n"),
- Info ++ DekInfo1.
-
-unhex(S) ->
- unhex(S, []).
-
-unhex("", Acc) ->
- lists:reverse(Acc);
-unhex([D1, D2 | Rest], Acc) ->
- unhex(Rest, [erlang:list_to_integer([D1, D2], 16) | Acc]).
-
-decode_key(Data, Password, "DES-CBC", Salt) ->
- Key = password_to_key(Password, Salt, 8),
- IV = Salt,
- crypto:des_cbc_decrypt(Key, IV, Data);
-decode_key(Data, Password, "DES-EDE3-CBC", Salt) ->
- Key = password_to_key(Password, Salt, 24),
- IV = Salt,
- <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key,
- crypto:des_ede3_cbc_decrypt(Key1, Key2, Key3, IV, Data).
-
-write_file(File, Ds) ->
- file:write_file(File, encode_file(Ds)).
-
-encode_file(Ds) ->
- [encode_file_1(D) || D <- Ds].
-
-encode_file_1({cert, Bin}) ->
- %% PKIX (X.509)
- ["-----BEGIN CERTIFICATE-----\n",
- ssl_base64:encode_split(Bin),
- "-----END CERTIFICATE-----\n\n"];
-encode_file_1({cert_req, Bin}) ->
- %% PKCS#10
- ["-----BEGIN CERTIFICATE REQUEST-----\n",
- ssl_base64:encode_split(Bin),
- "-----END CERTIFICATE REQUEST-----\n\n"];
-encode_file_1({rsa_private_key, Bin}) ->
- %% PKCS#?
- ["XXX Following key assumed not encrypted\n",
- "-----BEGIN RSA PRIVATE KEY-----\n",
- ssl_base64:encode_split(Bin),
- "-----END RSA PRIVATE KEY-----\n\n"].
-
-password_to_key(Data, Salt, KeyLen) ->
- <<Key:KeyLen/binary, _/binary>> =
- password_to_key(<<>>, Data, Salt, KeyLen, <<>>),
- Key.
-
-password_to_key(_, _, _, Len, Acc) when Len =< 0 ->
- Acc;
-password_to_key(Prev, Data, Salt, Len, Acc) ->
- M = crypto:md5([Prev, Data, Salt]),
- password_to_key(M, Data, Salt, Len - byte_size(M), <<Acc/binary, M/binary>>).
diff --git a/lib/ssl/src/ssl_pkix.erl b/lib/ssl/src/ssl_pkix.erl
deleted file mode 100644
index 8f540f74ad..0000000000
--- a/lib/ssl/src/ssl_pkix.erl
+++ /dev/null
@@ -1,307 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%% Purpose : API module for decoding of certificates.
-
--module(ssl_pkix).
-
--include("ssl_pkix.hrl").
-
--export([decode_cert_file/1, decode_cert_file/2,
- decode_cert/1, decode_cert/2, encode_cert/1, encoded_tbs_cert/1,
- signature_digest/1, decode_rsa_keyfile/2]).
-
-%% The public API is dprecated by public_key and
-%% the internal application API is no longer used ssl.
-%% So this file can be compleatly removed in R14.
--deprecated({decode_cert_file, 1, next_major_release}).
--deprecated({decode_cert_file, 2, next_major_release}).
--deprecated({decode_cert, 1, next_major_release}).
--deprecated({decode_cert, 2, next_major_release}).
-
-%%====================================================================
-%% API
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: decode_cert_file(File, <Opts>) -> {ok, Cert} | {ok, [Cert]}
-%%
-%% File = string()
-%% Opts = [Opt]
-%% Opt = pem | ssl | pkix - ssl and pkix are mutual exclusive
-%% Cert = term()
-%%
-%% Description: Decodes certificats found in file <File>.
-%% If the options list is empty the certificate is
-%% returned as a DER encoded binary, i.e. {ok, Bin} is returned, where
-%% Bin> is the provided input. The options pkix and ssl imply that the
-%% certificate is returned as a parsed ASN.1 structure in the form of
-%% an Erlang term. The ssl option gives a more elaborate return
-%% structure, with more explicit information. In particular object
-%% identifiers are replaced by atoms. The option subject implies that
-%% only the subject's distinguished name part of the certificate is
-%% returned. It can only be used together with the option pkix or the
-%% option ssl.
-%%--------------------------------------------------------------------
-decode_cert_file(File) ->
- decode_cert_file(File, []).
-
-decode_cert_file(File, Opts) ->
- case lists:member(pem, Opts) of
- true ->
- {ok, List} = ssl_pem:read_file(File),
- Certs = [Bin || {cert, Bin} <- List],
- NewOpts = lists:delete(pem, Opts),
- Fun = fun(Cert) ->
- {ok, Decoded} = decode_cert(Cert, NewOpts),
- Decoded
- end,
- case lists:map(Fun, Certs) of
- [DecodedCert] ->
- {ok, DecodedCert};
- DecodedCerts ->
- {ok, DecodedCerts}
- end;
- false ->
- {ok, Bin} = file:read_file(File),
- decode_cert(Bin, Opts)
- end.
-%%--------------------------------------------------------------------
-%% Function: decode_cert(Bin, <Opts>) -> {ok, Cert}
-%% Bin - binary()
-%% Opts = [Opt]
-%% Opt = ssl | pkix | subject - ssl and pkix are mutual exclusive
-%% Cert = term()
-%%
-%% Description: If the options list is empty the certificate is
-%% returned as a DER encoded binary, i.e. {ok, Bin} is returned, where
-%% Bin> is the provided input. The options pkix and ssl imply that the
-%% certificate is returned as a parsed ASN.1 structure in the form of
-%% an Erlang term. The ssl option gives a more elaborate return
-%% structure, with more explicit information. In particular object
-%% identifiers are replaced by atoms. The option subject implies that
-%% only the subject's distinguished name part of the certificate is
-%% returned. It can only be used together with the option pkix or the
-%% option ssl.
-%%--------------------------------------------------------------------
-decode_cert(Bin) ->
- decode_cert(Bin, []).
-
-decode_cert(Bin, []) when is_binary(Bin) ->
- {ok, Bin};
-decode_cert(Bin, Opts) when is_binary(Bin) ->
-
- {ok, Cert} = 'OTP-PKIX':decode('Certificate', Bin),
-
- case {lists:member(ssl, Opts), lists:member(pkix, Opts)} of
- {true, false} ->
- cert_return(transform(Cert, ssl), Opts);
- {false, true} ->
- cert_return(transform(Cert, pkix), Opts);
- _ ->
- {error, eoptions}
- end.
-
-encode_cert(#'Certificate'{} = Cert) ->
- {ok, List} = 'OTP-PKIX':encode('Certificate', Cert),
- list_to_binary(List).
-
-decode_rsa_keyfile(KeyFile, Password) ->
- {ok, List} = ssl_pem:read_file(KeyFile, Password),
- [PrivatKey] = [Bin || {rsa_private_key, Bin} <- List],
- 'OTP-PKIX':decode('RSAPrivateKey', PrivatKey).
-
-%%====================================================================
-%% Application internal API
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: encoded_tbs_cert(Cert) -> PKXCert
-%%
-%% Cert = binary() - Der encoded
-%% PKXCert = binary() - Der encoded
-%%
-%% Description: Extracts the binary TBSCert from the binary Certificate.
-%%--------------------------------------------------------------------
-encoded_tbs_cert(Cert) ->
- {ok, PKIXCert} =
- 'OTP-PKIX':decode_TBSCert_exclusive(Cert),
- {'Certificate',
- {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert,
- EncodedTBSCert.
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-
-cert_return(Cert, Opts) ->
- case lists:member(subject, Opts) of
- true ->
- {ok, get_subj(Cert)};
- false ->
- {ok, Cert}
- end.
-
-
-%% Transfrom from PKIX1-Explicit88 to SSL-PKIX.
-
-transform(#'Certificate'{signature = Signature,
- signatureAlgorithm = SignatureAlgorithm,
- tbsCertificate = TbsCertificate} = Cert, Type) ->
- Cert#'Certificate'{tbsCertificate = transform(TbsCertificate, Type),
- signatureAlgorithm = transform(SignatureAlgorithm, Type),
- signature = transform(Signature, Type)};
-
-%% -record('TBSCertificate',{
-%% version = asn1_DEFAULT, serialNumber, signature, issuer, validity, subject,
-%% subjectPublicKeyInfo, issuerUniqueID = asn1_NOVALUE,
-%% subjectUniqueID = asn1_NOVALUE, extensions = asn1_NOVALUE}).
-
-transform(#'TBSCertificate'{signature = Signature, issuer = Issuer,
- subject = Subject, extensions = Extensions,
- subjectPublicKeyInfo = SPKInfo} = TBSCert, Type) ->
- TBSCert#'TBSCertificate'{signature = transform(Signature, Type),
- issuer = transform(Issuer, Type),
- subject = transform(Subject, Type),
- subjectPublicKeyInfo = transform(SPKInfo, Type),
- extensions = transform_extensions(Extensions, Type)
- };
-
-transform(#'AlgorithmIdentifier'{algorithm = Algorithm,
- parameters = Params}, ssl) ->
- SignAlgAny =
- #'SignatureAlgorithm-Any'{algorithm = Algorithm, parameters = Params},
- {ok, AnyEnc} = 'OTP-PKIX':encode('SignatureAlgorithm-Any', SignAlgAny),
- {ok, SignAlgCd} = 'OTP-PKIX':decode('SignatureAlgorithm',
- list_to_binary(AnyEnc)),
- NAlgo = ssl_pkix_oid:id2atom(SignAlgCd#'SignatureAlgorithm'.algorithm),
- SignAlgCd#'SignatureAlgorithm'{algorithm = NAlgo};
-
-transform({rdnSequence, Lss}, Type) when is_list(Lss) ->
- {rdnSequence, [[transform(L, Type) || L <- Ls] || Ls <- Lss]};
-transform({rdnSequence, Lss}, _) ->
- {rdnSequence, Lss};
-
-transform(#'AttributeTypeAndValue'{} = ATAV, ssl) ->
- {ok, ATAVEnc} =
- 'OTP-PKIX':encode('AttributeTypeAndValue', ATAV),
- {ok, ATAVDec} = 'OTP-PKIX':decode('SSLAttributeTypeAndValue',
- list_to_binary(ATAVEnc)),
- AttrType = ATAVDec#'SSLAttributeTypeAndValue'.type,
- #'AttributeTypeAndValue'{type = ssl_pkix_oid:id2atom(AttrType),
- value =
- ATAVDec#'SSLAttributeTypeAndValue'.value};
-
-transform(#'AttributeTypeAndValue'{} = Att, pkix) ->
- Att;
-
-%% -record('SubjectPublicKeyInfo',{
-%% algorithm, subjectPublicKey}).
-%%
-%% -record('SubjectPublicKeyInfo_algorithm',{
-%% algo, parameters = asn1_NOVALUE}).
-%%
-%% -record('SubjectPublicKeyInfo-Any',{
-%% algorithm, subjectPublicKey}).
-%%
-%% -record('PublicKeyAlgorithm',{
-%% algorithm, parameters = asn1_NOVALUE}).
-
-transform(#'SubjectPublicKeyInfo'{subjectPublicKey = SubjectPublicKey,
- algorithm = Algorithm}, ssl) ->
- %% Transform from SubjectPublicKeyInfo (PKIX1Explicit88)
- %% to SubjectPublicKeyInfo-Any (SSL-PKIX).
- Algo = Algorithm#'AlgorithmIdentifier'.algorithm,
- Parameters = Algorithm#'AlgorithmIdentifier'.parameters,
- AlgorithmAny = #'PublicKeyAlgorithm'{algorithm = Algo,
- parameters = Parameters},
- {0, Bin} = SubjectPublicKey,
- SInfoAny = #'SSLSubjectPublicKeyInfo-Any'{algorithm = AlgorithmAny,
- subjectPublicKey = Bin},
-
- %% Encode according to SubjectPublicKeyInfo-Any, and decode according
- %% to SubjectPublicKeyInfo.
- {ok, AnyEnc} =
- 'OTP-PKIX':encode('SSLSubjectPublicKeyInfo-Any', SInfoAny),
- {ok, SInfoCd} = 'OTP-PKIX':decode('SSLSubjectPublicKeyInfo',
- list_to_binary(AnyEnc)),
- %% Replace object identifier by atom
- AlgorithmCd = SInfoCd#'SSLSubjectPublicKeyInfo'.algorithm,
- AlgoCd = AlgorithmCd#'SSLSubjectPublicKeyInfo_algorithm'.algo,
- Params = AlgorithmCd#'SSLSubjectPublicKeyInfo_algorithm'.parameters,
- Key = SInfoCd#'SSLSubjectPublicKeyInfo'.subjectPublicKey,
- NAlgoCd = ssl_pkix_oid:id2atom(AlgoCd),
- NAlgorithmCd =
- #'SubjectPublicKeyInfo_algorithm'{algorithm = NAlgoCd,
- parameters = Params},
- #'SubjectPublicKeyInfo'{algorithm = NAlgorithmCd,
- subjectPublicKey = Key
- };
-transform(#'SubjectPublicKeyInfo'{} = SInfo, pkix) ->
- SInfo;
-
-transform(#'Extension'{extnID = ExtnID} = Ext, ssl) ->
- NewExtID = ssl_pkix_oid:id2atom(ExtnID),
- ExtAny = setelement(1, Ext, 'Extension-Any'),
- {ok, AnyEnc} = 'OTP-PKIX':encode('Extension-Any', ExtAny),
- {ok, ExtCd} = 'OTP-PKIX':decode('SSLExtension', list_to_binary(AnyEnc)),
-
- ExtValue = transform_extension_value(NewExtID,
- ExtCd#'SSLExtension'.extnValue,
- ssl),
- #'Extension'{extnID = NewExtID,
- critical = ExtCd#'SSLExtension'.critical,
- extnValue = ExtValue};
-
-transform(#'Extension'{extnID = ExtnID, extnValue = ExtnValue} = Ext, pkix) ->
- NewExtID = ssl_pkix_oid:id2atom(ExtnID),
- ExtValue = transform_extension_value(NewExtID, ExtnValue, pkix),
- Ext#'Extension'{extnValue = ExtValue};
-
-transform(#'AuthorityKeyIdentifier'{authorityCertIssuer = CertIssuer} = Ext,
- Type) ->
- Ext#'AuthorityKeyIdentifier'{authorityCertIssuer =
- transform(CertIssuer, Type)};
-
-transform([{directoryName, Value}], Type) ->
- [{directoryName, transform(Value, Type)}];
-
-transform(X, _) ->
- X.
-
-transform_extension_value('ce-authorityKeyIdentifier', Value, Type) ->
- transform(Value, Type);
-transform_extension_value(_, Value, _) ->
- Value.
-
-transform_extensions(Exts, Type) when is_list(Exts) ->
- [transform(Ext, Type) || Ext <- Exts];
-transform_extensions(Exts, _) ->
- Exts.
-
-get_subj(Cert) ->
- (Cert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject.
-
-signature_digest(BinSignature) ->
- case (catch 'OTP-PKIX':decode('DigestInfo', BinSignature)) of
- {ok, DigestInfo} ->
- list_to_binary(DigestInfo#'DigestInfo'.digest);
- _ ->
- {error, decode_error}
- end.
diff --git a/lib/ssl/src/ssl_pkix.hrl b/lib/ssl/src/ssl_pkix.hrl
deleted file mode 100644
index a8463369f6..0000000000
--- a/lib/ssl/src/ssl_pkix.hrl
+++ /dev/null
@@ -1,81 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
-
--ifndef(ssl_pkix).
--define(ssl_pkix, true).
-
--include("OTP-PKIX.hrl").
-
-%% The following commented out records are currently defined in OTP-PKIX.hrl
-%% and are considered a public interface through ssl_pkix.hrl.
-%% NOTE do not include OTP-PKIX.hrl it is an generated file
-%% and may change but the following records will still be
-%% availanble from this file.
-
-% -record('Certificate', {
-% tbsCertificate,
-% signatureAlgorithm,
-% signature}).
-
-% -record('TBSCertificate', {
-% version = asn1_DEFAULT,
-% serialNumber,
-% signature,
-% issuer,
-% validity,
-% subject,
-% subjectPublicKeyInfo,
-% issuerUniqueID = asn1_NOVALUE,
-% subjectUniqueID = asn1_NOVALUE,
-% extensions = asn1_NOVALUE}).
-
-% -record('AttributeTypeAndValue', {
-% type,
-% value}).
-
-% -record('SubjectPublicKeyInfo', {
-% algorithm,
-% subjectPublicKey}).
-
--record('SubjectPublicKeyInfo_algorithm', {
- algorithm,
- parameters = asn1_NOVALUE}).
-
-% -record('FieldID', {
-% fieldType,
-% parameters}).
-
-% -record('Characteristic-two', {
-% m,
-% basis,
-% parameters}).
-
-% -record('ExtensionAttribute', {
-% extensionAttributeType,
-% extensionAttributeValue}).
-
-% -record('Extension', {
-% extnID,
-% critical = asn1_DEFAULT,
-% extnValue}).
-
--endif. % -ifdef(ssl_pkix).
-
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index f9f915f13d..f1c0073965 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -29,7 +29,7 @@
-include("ssl_internal.hrl").
-include("ssl_alert.hrl").
-include("ssl_handshake.hrl").
--include("ssl_debug.hrl").
+-include("ssl_cipher.hrl").
%% Connection state handling
-export([init_connection_states/1,
@@ -65,10 +65,9 @@
%%====================================================================
%% Internal application API
%%====================================================================
+
%%--------------------------------------------------------------------
-%% Function: init_connection_states(Role) -> #connection_states{}
-%% Role = client | server
-%% Random = binary()
+-spec init_connection_states(client | server) -> #connection_states{}.
%%
%% Description: Creates a connection_states record with appropriate
%% values for the initial SSL connection setup.
@@ -84,9 +83,8 @@ init_connection_states(Role) ->
}.
%%--------------------------------------------------------------------
-%% Function: current_connection_state(States, Type) -> #connection_state{}
-%% States = #connection_states{}
-%% Type = read | write
+-spec current_connection_state(#connection_states{}, read | write) ->
+ #connection_state{}.
%%
%% Description: Returns the instance of the connection_state record
%% that is currently defined as the current conection state.
@@ -99,9 +97,8 @@ current_connection_state(#connection_states{current_write = Current},
Current.
%%--------------------------------------------------------------------
-%% Function: pending_connection_state(States, Type) -> #connection_state{}
-%% States = #connection_states{}
-%% Type = read | write
+-spec pending_connection_state(#connection_states{}, read | write) ->
+ #connection_state{}.
%%
%% Description: Returns the instance of the connection_state record
%% that is currently defined as the pending conection state.
@@ -114,14 +111,11 @@ pending_connection_state(#connection_states{pending_write = Pending},
Pending.
%%--------------------------------------------------------------------
-%% Function: update_security_params(Params, States) ->
-%% #connection_states{}
-%% Params = #security_parameters{}
-%% States = #connection_states{}
+-spec update_security_params(#security_parameters{}, #security_parameters{},
+ #connection_states{}) -> #connection_states{}.
%%
%% Description: Creates a new instance of the connection_states record
-%% where the pending states gets its security parameters
-%% updated to <Params>.
+%% where the pending states gets its security parameters updated.
%%--------------------------------------------------------------------
update_security_params(ReadParams, WriteParams, States =
#connection_states{pending_read = Read,
@@ -134,14 +128,10 @@ update_security_params(ReadParams, WriteParams, States =
WriteParams}
}.
%%--------------------------------------------------------------------
-%% Function: set_mac_secret(ClientWriteMacSecret,
-%% ServerWriteMacSecret, Role, States) ->
-%% #connection_states{}
-%% MacSecret = binary()
-%% States = #connection_states{}
-%% Role = server | client
+-spec set_mac_secret(binary(), binary(), client | server,
+ #connection_states{}) -> #connection_states{}.
%%
-%% update the mac_secret field in pending connection states
+%% Description: update the mac_secret field in pending connection states
%%--------------------------------------------------------------------
set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) ->
set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States);
@@ -158,12 +148,9 @@ set_mac_secret(ReadMacSecret, WriteMacSecret,
%%--------------------------------------------------------------------
-%% Function: set_master_secret(MasterSecret, States) ->
-%% #connection_states{}
-%% MacSecret =
-%% States = #connection_states{}
+-spec set_master_secret(binary(), #connection_states{}) -> #connection_states{}.
%%
-%% Set master_secret in pending connection states
+%% Description: Set master_secret in pending connection states
%%--------------------------------------------------------------------
set_master_secret(MasterSecret,
States = #connection_states{pending_read = Read,
@@ -179,12 +166,9 @@ set_master_secret(MasterSecret,
States#connection_states{pending_read = Read1, pending_write = Write1}.
%%--------------------------------------------------------------------
-%% Function: set_renegotiation_flag(Flag, States) ->
-%% #connection_states{}
-%% Flag = boolean()
-%% States = #connection_states{}
+-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}.
%%
-%% Set master_secret in pending connection states
+%% Description: Set secure_renegotiation in pending connection states
%%--------------------------------------------------------------------
set_renegotiation_flag(Flag, #connection_states{
current_read = CurrentRead0,
@@ -202,13 +186,11 @@ set_renegotiation_flag(Flag, #connection_states{
pending_write = PendingWrite}.
%%--------------------------------------------------------------------
-%% Function: set_client_verify_data(State, Data, States) ->
-%% #connection_states{}
-%% State = atom()
-%% Data = binary()
-%% States = #connection_states{}
+-spec set_client_verify_data(current_read | current_write | current_both,
+ binary(), #connection_states{})->
+ #connection_states{}.
%%
-%% Set verify data in connection states.
+%% Description: Set verify data in connection states.
%%--------------------------------------------------------------------
set_client_verify_data(current_read, Data,
#connection_states{current_read = CurrentRead0,
@@ -234,15 +216,12 @@ set_client_verify_data(current_both, Data,
CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data},
ConnectionStates#connection_states{current_read = CurrentRead,
current_write = CurrentWrite}.
-
%%--------------------------------------------------------------------
-%% Function: set_server_verify_data(State, Data, States) ->
-%% #connection_states{}
-%% State = atom()
-%% Data = binary()
-%% States = #connection_states{}
+-spec set_server_verify_data(current_read | current_write | current_both,
+ binary(), #connection_states{})->
+ #connection_states{}.
%%
-%% Set verify data in pending connection states.
+%% Description: Set verify data in pending connection states.
%%--------------------------------------------------------------------
set_server_verify_data(current_write, Data,
#connection_states{pending_read = PendingRead0,
@@ -272,10 +251,8 @@ set_server_verify_data(current_both, Data,
current_write = CurrentWrite}.
%%--------------------------------------------------------------------
-%% Function: activate_pending_connection_state(States, Type) ->
-%% #connection_states{}
-%% States = #connection_states{}
-%% Type = read | write
+-spec activate_pending_connection_state(#connection_states{}, read | write) ->
+ #connection_states{}.
%%
%% Description: Creates a new instance of the connection_states record
%% where the pending state of <Type> has been activated.
@@ -307,11 +284,9 @@ activate_pending_connection_state(States =
}.
%%--------------------------------------------------------------------
-%% Function: set_pending_cipher_state(States, ClientState,
-%% ServerState, Role) ->
-%% #connection_states{}
-%% ClientState = ServerState = #cipher_state{}
-%% States = #connection_states{}
+-spec set_pending_cipher_state(#connection_states{}, #cipher_state{},
+ #cipher_state{}, client | server) ->
+ #connection_states{}.
%%
%% Description: Set the cipher state in the specified pending connection state.
%%--------------------------------------------------------------------
@@ -330,12 +305,10 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
pending_write = Write#connection_state{cipher_state = ClientState}}.
%%--------------------------------------------------------------------
-%% Function: get_tls_record(Data, Buffer) -> Result
-%% Result = {[#tls_compressed{}], NewBuffer}
-%% Data = Buffer = NewBuffer = binary()
-%%
-%% Description: given old buffer and new data from TCP, packs up a records
-%% and returns it as a list of #tls_compressed, also returns leftover
+-spec get_tls_records(binary(), binary()) -> {[binary()], binary()} | #alert{}.
+%%
+%% Description: Given old buffer and new data from TCP, packs up a records
+%% and returns it as a list of tls_compressed binaries also returns leftover
%% data
%%--------------------------------------------------------------------
get_tls_records(Data, <<>>) ->
@@ -398,8 +371,8 @@ get_tls_records_aux(Data, Acc) ->
{lists:reverse(Acc), Data}.
%%--------------------------------------------------------------------
-%% Function: protocol_version(Version) -> #protocol_version{}
-%% Version = atom()
+-spec protocol_version(tls_atom_version() | tls_version()) ->
+ tls_version() | tls_atom_version().
%%
%% Description: Creates a protocol version record from a version atom
%% or vice versa.
@@ -410,19 +383,16 @@ protocol_version(tlsv1) ->
{3, 1};
protocol_version(sslv3) ->
{3, 0};
-protocol_version(sslv2) ->
+protocol_version(sslv2) -> %% Backwards compatibility
{2, 0};
protocol_version({3, 2}) ->
'tlsv1.1';
protocol_version({3, 1}) ->
tlsv1;
protocol_version({3, 0}) ->
- sslv3;
-protocol_version({2, 0}) ->
- sslv2.
+ sslv3.
%%--------------------------------------------------------------------
-%% Function: protocol_version(Version1, Version2) -> #protocol_version{}
-%% Version1 = Version2 = #protocol_version{}
+-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version().
%%
%% Description: Lowes protocol version of two given versions
%%--------------------------------------------------------------------
@@ -437,8 +407,7 @@ lowest_protocol_version(Version = {M,_},
lowest_protocol_version(_,Version) ->
Version.
%%--------------------------------------------------------------------
-%% Function: protocol_version(Versions) -> #protocol_version{}
-%% Versions = [#protocol_version{}]
+-spec highest_protocol_version([tls_version()]) -> tls_version().
%%
%% Description: Highest protocol version present in a list
%%--------------------------------------------------------------------
@@ -460,14 +429,13 @@ highest_protocol_version(_, [Version | Rest]) ->
highest_protocol_version(Version, Rest).
%%--------------------------------------------------------------------
-%% Function: supported_protocol_versions() -> Versions
-%% Versions = [#protocol_version{}]
-%%
+-spec supported_protocol_versions() -> [tls_version()].
+%%
%% Description: Protocol versions supported
%%--------------------------------------------------------------------
supported_protocol_versions() ->
Fun = fun(Version) ->
- protocol_version(Version)
+ protocol_version(Version)
end,
case application:get_env(ssl, protocol_version) of
undefined ->
@@ -475,14 +443,20 @@ supported_protocol_versions() ->
{ok, []} ->
lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS);
{ok, Vsns} when is_list(Vsns) ->
- lists:map(Fun, Vsns);
+ Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)),
+ supported_protocol_versions(Versions);
{ok, Vsn} ->
- [Fun(Vsn)]
+ Versions = lists:filter(fun is_acceptable_version/1, [Fun(Vsn)]),
+ supported_protocol_versions(Versions)
end.
+supported_protocol_versions([]) ->
+ ?DEFAULT_SUPPORTED_VERSIONS;
+supported_protocol_versions([_|_] = Vsns) ->
+ Vsns.
+
%%--------------------------------------------------------------------
-%% Function: is_acceptable_version(Version) -> true | false
-%% Version = #protocol_version{}
+-spec is_acceptable_version(tls_version()) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
%%--------------------------------------------------------------------
@@ -493,7 +467,7 @@ is_acceptable_version(_) ->
false.
%%--------------------------------------------------------------------
-%% Function: compressions() -> binary()
+-spec compressions() -> [binary()].
%%
%% Description: return a list of compressions supported (currently none)
%%--------------------------------------------------------------------
@@ -501,8 +475,8 @@ compressions() ->
[?byte(?NULL)].
%%--------------------------------------------------------------------
-%% Function: decode_cipher_text(CipherText, ConnectionStates0) ->
-%% {Plain, ConnectionStates}
+-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) ->
+ {#ssl_tls{}, #connection_states{}}| #alert{}.
%%
%% Description: Decode cipher text
%%--------------------------------------------------------------------
@@ -511,13 +485,77 @@ decode_cipher_text(CipherText, ConnnectionStates0) ->
#connection_state{compression_state = CompressionS0,
security_parameters = SecParams} = ReadState0,
CompressAlg = SecParams#security_parameters.compression_algorithm,
- {Compressed, ReadState1} = decipher(CipherText, ReadState0),
- {Plain, CompressionS1} = uncompress(CompressAlg,
- Compressed, CompressionS0),
- ConnnectionStates = ConnnectionStates0#connection_states{
- current_read = ReadState1#connection_state{
- compression_state = CompressionS1}},
- {Plain, ConnnectionStates}.
+ case decipher(CipherText, ReadState0) of
+ {Compressed, ReadState1} ->
+ {Plain, CompressionS1} = uncompress(CompressAlg,
+ Compressed, CompressionS0),
+ ConnnectionStates = ConnnectionStates0#connection_states{
+ current_read = ReadState1#connection_state{
+ compression_state = CompressionS1}},
+ {Plain, ConnnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end.
+%%--------------------------------------------------------------------
+-spec encode_data(iolist(), tls_version(), #connection_states{}, integer()) ->
+ {iolist(), iolist(), #connection_states{}}.
+%%
+%% Description: Encodes data to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt)
+ when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) ->
+ case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of
+ {renegotiate, Data} ->
+ {[], Data, ConnectionStates};
+ {Msg, CS} ->
+ {Msg, [], CS}
+ end;
+
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) ->
+ Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048),
+ encode_data(Data, Version, ConnectionStates, RenegotiateAt);
+
+encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) ->
+ {ConnectionStates, EncodedMsg, NotEncdedData} =
+ lists:foldl(fun(B, {CS0, Encoded, Rest}) ->
+ case encode_plain_text(?APPLICATION_DATA,
+ Version, B, CS0, RenegotiateAt) of
+ {renegotiate, NotEnc} ->
+ {CS0, Encoded, [NotEnc | Rest]};
+ {Enc, CS1} ->
+ {CS1, [Enc | Encoded], Rest}
+ end
+ end, {ConnectionStates0, [], []}, Data),
+ {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}.
+
+%%--------------------------------------------------------------------
+-spec encode_handshake(iolist(), tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a handshake message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_handshake(Frag, Version, ConnectionStates) ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
+
+%%--------------------------------------------------------------------
+-spec encode_alert_record(#alert{}, tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes an alert message to send on the ssl-socket.
+%%--------------------------------------------------------------------
+encode_alert_record(#alert{level = Level, description = Description},
+ Version, ConnectionStates) ->
+ encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
+ ConnectionStates).
+
+%%--------------------------------------------------------------------
+-spec encode_change_cipher_spec(tls_version(), #connection_states{}) ->
+ {iolist(), #connection_states{}}.
+%%
+%% Description: Encodes a change_cipher_spec-message to send on the ssl socket.
+%%--------------------------------------------------------------------
+encode_change_cipher_spec(Version, ConnectionStates) ->
+ encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
%%--------------------------------------------------------------------
%%% Internal functions
@@ -532,12 +570,10 @@ initial_connection_state(ConnectionEnd) ->
}.
initial_security_params(ConnectionEnd) ->
- #security_parameters{connection_end = ConnectionEnd,
- bulk_cipher_algorithm = ?NULL,
- mac_algorithm = ?NULL,
- compression_algorithm = ?NULL,
- cipher_type = ?NULL
- }.
+ SecParams = #security_parameters{connection_end = ConnectionEnd,
+ compression_algorithm = ?NULL},
+ ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL,
+ SecParams).
empty_connection_state(ConnectionEnd) ->
SecParams = empty_security_params(ConnectionEnd),
@@ -573,43 +609,6 @@ split_bin(Bin, ChunkSize, Acc) ->
lists:reverse(Acc, [Bin])
end.
-encode_data(Frag, Version, ConnectionStates, RenegotiateAt)
- when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) ->
- case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of
- {renegotiate, Data} ->
- {[], Data, ConnectionStates};
- {Msg, CS} ->
- {Msg, [], CS}
- end;
-
-encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) ->
- Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048),
- encode_data(Data, Version, ConnectionStates, RenegotiateAt);
-
-encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) ->
- {ConnectionStates, EncodedMsg, NotEncdedData} =
- lists:foldl(fun(B, {CS0, Encoded, Rest}) ->
- case encode_plain_text(?APPLICATION_DATA,
- Version, B, CS0, RenegotiateAt) of
- {renegotiate, NotEnc} ->
- {CS0, Encoded, [NotEnc | Rest]};
- {Enc, CS1} ->
- {CS1, [Enc | Encoded], Rest}
- end
- end, {ConnectionStates0, [], []}, Data),
- {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}.
-
-encode_handshake(Frag, Version, ConnectionStates) ->
- encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
-
-encode_alert_record(#alert{level = Level, description = Description},
- Version, ConnectionStates) ->
- encode_plain_text(?ALERT, Version, <<?BYTE(Level), ?BYTE(Description)>>,
- ConnectionStates).
-
-encode_change_cipher_spec(Version, ConnectionStates) ->
- encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
-
encode_plain_text(Type, Version, Data, ConnectionStates, RenegotiateAt) ->
#connection_states{current_write =
#connection_state{sequence_number = Num}} = ConnectionStates,
@@ -643,29 +642,35 @@ encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
cipher(Type, Version, Fragment, CS0) ->
Length = erlang:iolist_size(Fragment),
- {Hash, CS1=#connection_state{cipher_state = CipherS0,
+ {MacHash, CS1=#connection_state{cipher_state = CipherS0,
security_parameters=
#security_parameters{bulk_cipher_algorithm =
BCA}
}} =
hash_and_bump_seqno(CS0, Type, Version, Length, Fragment),
- ?DBG_HEX(Fragment),
- {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, Hash, Fragment),
- ?DBG_HEX(Ciphered),
+ {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment),
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) ->
SP = CS0#connection_state.security_parameters,
- BCA = SP#security_parameters.bulk_cipher_algorithm, % eller Cipher?
+ BCA = SP#security_parameters.bulk_cipher_algorithm,
HashSz = SP#security_parameters.hash_size,
CipherS0 = CS0#connection_state.cipher_state,
- {T, Mac, CipherS1} = ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment),
- CS1 = CS0#connection_state{cipher_state = CipherS1},
- TLength = size(T),
- {Hash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, Fragment),
- ok = check_hash(Hash, Mac),
- {TLS#ssl_tls{fragment = T}, CS2}.
+ case ssl_cipher:decipher(BCA, HashSz, CipherS0, Fragment, Version) of
+ {T, Mac, CipherS1} ->
+ CS1 = CS0#connection_state{cipher_state = CipherS1},
+ TLength = size(T),
+ {MacHash, CS2} = hash_and_bump_seqno(CS1, Type, Version, TLength, T),
+ case is_correct_mac(Mac, MacHash) of
+ true ->
+ {TLS#ssl_tls{fragment = T}, CS2};
+ false ->
+ ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC)
+ end;
+ #alert{} = Alert ->
+ Alert
+ end.
uncompress(?NULL, Data = #ssl_tls{type = _Type,
version = _Version,
@@ -686,10 +691,12 @@ hash_and_bump_seqno(#connection_state{sequence_number = SeqNo,
Length, Fragment),
{Hash, CS0#connection_state{sequence_number = SeqNo+1}}.
-check_hash(_, _) ->
- ok. %% TODO check this
+is_correct_mac(Mac, Mac) ->
+ true;
+is_correct_mac(_M,_H) ->
+ false.
-mac_hash(?NULL, {_,_}, _MacSecret, _SeqNo, _Type,
+mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type,
_Length, _Fragment) ->
<<>>;
mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) ->
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index bcb10daf69..dc4b7a711c 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,15 +28,14 @@
-include("ssl_internal.hrl").
%% Internal application API
--export([is_new/2, id/3, id/6, valid_session/2]).
+-export([is_new/2, id/4, id/7, valid_session/2]).
-define(GEN_UNIQUE_ID_MAX_TRIES, 10).
+-type seconds() :: integer().
+
%%--------------------------------------------------------------------
-%% Function: is_new(ClientSuggestedId, ServerDecidedId) -> true | false
-%%
-%% ClientSuggestedId = binary()
-%% ServerDecidedId = binary()
+-spec is_new(session_id(), session_id()) -> boolean().
%%
%% Description: Checks if the session id decided by the server is a
%% new or resumed sesion id.
@@ -45,23 +44,18 @@ is_new(<<>>, _) ->
true;
is_new(SessionId, SessionId) ->
false;
-is_new(_, _) ->
+is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
-%% Function: id(ClientInfo, Cache, CacheCb) -> SessionId
-%%
-%% ClientInfo = {HostIP, Port, SslOpts}
-%% HostIP = ipadress()
-%% Port = integer()
-%% CacheCb = atom()
-%% SessionId = binary()
+-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(),
+ undefined | binary()) -> binary().
%%
%% Description: Should be called by the client side to get an id
%% for the client hello message.
%%--------------------------------------------------------------------
-id(ClientInfo, Cache, CacheCb) ->
- case select_session(ClientInfo, Cache, CacheCb) of
+id(ClientInfo, Cache, CacheCb, OwnCert) ->
+ case select_session(ClientInfo, Cache, CacheCb, OwnCert) of
no_session ->
<<>>;
SessionId ->
@@ -69,36 +63,27 @@ id(ClientInfo, Cache, CacheCb) ->
end.
%%--------------------------------------------------------------------
-%% Function: id(Port, SuggestedSessionId, ReuseFun, CacheCb,
-%% SecondLifeTime) -> SessionId
-%%
-%% Port = integer()
-%% SuggestedSessionId = SessionId = binary()
-%% ReuseFun = fun(SessionId, PeerCert, Compression, CipherSuite) ->
-%% true | false
-%% CacheCb = atom()
+-spec id(port_num(), binary(), #ssl_options{}, cache_ref(),
+ atom(), seconds(), binary()) -> binary().
%%
%% Description: Should be called by the server side to get an id
%% for the server hello message.
%%--------------------------------------------------------------------
-id(Port, <<>>, _, Cache, CacheCb, _) ->
+id(Port, <<>>, _, Cache, CacheCb, _, _) ->
new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb);
id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled,
reuse_session = ReuseFun},
- Cache, CacheCb, SecondLifeTime) ->
+ Cache, CacheCb, SecondLifeTime, OwnCert) ->
case is_resumable(SuggestedSessionId, Port, ReuseEnabled,
- ReuseFun, Cache, CacheCb, SecondLifeTime) of
+ ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of
true ->
SuggestedSessionId;
false ->
new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb)
end.
%%--------------------------------------------------------------------
-%% Function: valid_session(Session, LifeTime) -> true | false
-%%
-%% Session = #session{}
-%% LifeTime = integer() - seconds
+-spec valid_session(#session{}, seconds()) -> boolean().
%%
%% Description: Check that the session has not expired
%%--------------------------------------------------------------------
@@ -109,19 +94,20 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-select_session({HostIP, Port, SslOpts}, Cache, CacheCb) ->
+select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
Sessions = CacheCb:select_session(Cache, {HostIP, Port}),
- select_session(Sessions, SslOpts).
+ select_session(Sessions, SslOpts, OwnCert).
-select_session([], _) ->
+select_session([], _, _) ->
no_session;
select_session(Sessions, #ssl_options{ciphers = Ciphers,
- reuse_sessions = ReuseSession}) ->
+ reuse_sessions = ReuseSession}, OwnCert) ->
IsResumable =
fun(Session) ->
ReuseSession andalso (Session#session.is_resumable) andalso
lists:member(Session#session.cipher_suite, Ciphers)
+ andalso (OwnCert == Session#session.own_certificate)
end,
case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of
[] ->
@@ -129,7 +115,7 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers,
List ->
hd(List)
end.
-
+
%% If we can not generate a not allready in use session ID in
%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The
%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which
@@ -156,14 +142,16 @@ new_id(Port, Tries, Cache, CacheCb) ->
end.
is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache,
- CacheCb, SecondLifeTime) ->
+ CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
+ own_certificate = SessionOwnCert,
compression_method = Compression,
is_resumable = Is_resumable,
peer_certificate = PeerCert} = Session ->
ReuseEnabled
andalso Is_resumable
+ andalso (OwnCert == SessionOwnCert)
andalso valid_session(Session, SecondLifeTime)
andalso ReuseFun(SuggestedSessionId, PeerCert,
Compression, CipherSuite);
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
index 4a60892235..823bf7acfa 100644
--- a/lib/ssl/src/ssl_session_cache.erl
+++ b/lib/ssl/src/ssl_session_cache.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -22,23 +22,24 @@
-behaviour(ssl_session_cache_api).
--export([init/0, terminate/1, lookup/2, update/3, delete/2, foldl/3,
- select_session/2]).
+-include("ssl_handshake.hrl").
+-include("ssl_internal.hrl").
+
+-export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3,
+ select_session/2]).
+
+-type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}.
%%--------------------------------------------------------------------
-%% Function: init() -> Cache
-%%
-%% Cache - Reference to the cash (opaque)
+-spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque)
%%
%% Description: Return table reference. Called by ssl_manager process.
%%--------------------------------------------------------------------
-init() ->
+init(_) ->
ets:new(cache_name(), [set, protected]).
%%--------------------------------------------------------------------
-%% Function: terminate(Cache) ->
-%%
-%% Cache - as returned by create/0
+-spec terminate(cache_ref()) -> any(). %%
%%
%% Description: Handles cache table at termination of ssl manager.
%%--------------------------------------------------------------------
@@ -46,9 +47,7 @@ terminate(Cache) ->
ets:delete(Cache).
%%--------------------------------------------------------------------
-%% Function: lookup(Cache, Key) -> Session | undefined
-%% Cache - as returned by create/0
-%% Session = #session{}
+-spec lookup(cache_ref(), key()) -> #session{} | undefined.
%%
%% Description: Looks up a cach entry. Should be callable from any
%% process.
@@ -62,9 +61,7 @@ lookup(Cache, Key) ->
end.
%%--------------------------------------------------------------------
-%% Function: update(Cache, Key, Session) -> _
-%% Cache - as returned by create/0
-%% Session = #session{}
+-spec update(cache_ref(), key(), #session{}) -> any().
%%
%% Description: Caches a new session or updates a already cached one.
%% Will only be called from the ssl_manager process.
@@ -73,11 +70,7 @@ update(Cache, Key, Session) ->
ets:insert(Cache, {Key, Session}).
%%--------------------------------------------------------------------
-%% Function: delete(Cache, HostIP, Port, Id) -> _
-%% Cache - as returned by create/0
-%% HostIP = Host = string() | ipadress()
-%% Port = integer()
-%% Id =
+-spec delete(cache_ref(), key()) -> any().
%%
%% Description: Delets a cache entry.
%% Will only be called from the ssl_manager process.
@@ -86,28 +79,19 @@ delete(Cache, Key) ->
ets:delete(Cache, Key).
%%--------------------------------------------------------------------
-%% Function: foldl(Fun, Acc0, Cache) -> Acc
-%%
-%% Fun - fun()
-%% Acc0 - term()
-%% Cache - cache_ref()
-%%
+-spec foldl(fun(), term(), cache_ref()) -> term().
%%
%% Description: Calls Fun(Elem, AccIn) on successive elements of the
%% cache, starting with AccIn == Acc0. Fun/2 must return a new
%% accumulator which is passed to the next call. The function returns
-%% the final value of the accumulator. Acc0 is returned if the cache is
-%% empty.
-%% Should be callable from any process
+%% the final value of the accumulator. Acc0 is returned if the cache
+%% is empty.Should be callable from any process
%%--------------------------------------------------------------------
foldl(Fun, Acc0, Cache) ->
ets:foldl(Fun, Acc0, Cache).
%%--------------------------------------------------------------------
-%% Function: select_session(Cache, PartialKey) -> [Sessions]
-%%
-%% Cache - as returned by create/0
-%% PartialKey - opaque Key = {PartialKey, SessionId}
+-spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}].
%%
%% Description: Selects a session that could be reused. Should be callable
%% from any process.
@@ -119,6 +103,5 @@ select_session(Cache, PartialKey) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-
cache_name() ->
ssl_otp_session_cache.
diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl
index d2e846e9fd..f8416bf327 100644
--- a/lib/ssl/src/ssl_session_cache_api.erl
+++ b/lib/ssl/src/ssl_session_cache_api.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -25,7 +25,7 @@
behaviour_info(callbacks) ->
[
- {init, 0},
+ {init, 1},
{terminate, 1},
{lookup, 2},
{update, 3},
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index df809ce275..f2926b2d2f 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -25,12 +25,11 @@
-module(ssl_ssl3).
-include("ssl_cipher.hrl").
--include("ssl_debug.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl"). % MD5 and SHA
-export([master_secret/3, finished/3, certificate_verify/3,
- mac_hash/6, setup_keys/8,
+ mac_hash/6, setup_keys/7,
suites/0]).
-compile(inline).
@@ -38,10 +37,9 @@
%% Internal application API
%%====================================================================
+-spec master_secret(binary(), binary(), binary()) -> binary().
+
master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
- ?DBG_HEX(PremasterSecret),
- ?DBG_HEX(ClientRandom),
- ?DBG_HEX(ServerRandom),
%% draft-ietf-tls-ssl-version3-00 - 6.2.2
%% key_block =
%% MD5(master_secret + SHA(`A' + master_secret +
@@ -53,9 +51,10 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) ->
%% MD5(master_secret + SHA(`CCC' + master_secret +
%% ServerHello.random +
%% ClientHello.random)) + [...];
- B = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
- ?DBG_HEX(B),
- B.
+ Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48),
+ Block.
+
+-spec finished(client | server, binary(), {binary(), binary()}) -> binary().
finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished
@@ -75,8 +74,9 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash),
<<MD5/binary, SHA/binary>>.
-certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash})
- when Algorithm == rsa; Algorithm == dh_rsa; Algorithm == dhe_rsa ->
+-spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary().
+
+certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) ->
%% md5_hash
%% MD5(master_secret + pad_2 +
%% MD5(handshake_messages + master_secret + pad_1));
@@ -88,35 +88,31 @@ certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash})
SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash),
<<MD5/binary, SHA/binary>>;
-certificate_verify(Algorithm, MasterSecret, {_, SHAHash})
- when Algorithm == dh_dss; Algorithm == dhe_dss ->
+certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) ->
%% sha_hash
%% SHA(master_secret + pad_2 +
%% SHA(handshake_messages + master_secret + pad_1));
handshake_hash(?SHA, MasterSecret, undefined, SHAHash).
+-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary().
+
mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) ->
%% draft-ietf-tls-ssl-version3-00 - 5.2.3.1
%% hash(MAC_write_secret + pad_2 +
%% hash(MAC_write_secret + pad_1 + seq_num +
%% SSLCompressed.type + SSLCompressed.length +
%% SSLCompressed.fragment));
- case Method of
- ?NULL -> ok;
- _ ->
- ?DBG_HEX(Mac_write_secret),
- ?DBG_HEX(hash(Method, Fragment)),
- ok
- end,
Mac = mac_hash(Method, Mac_write_secret,
[<<?UINT64(Seq_num), ?BYTE(Type),
?UINT16(Length)>>, Fragment]),
- ?DBG_HEX(Mac),
Mac.
-setup_keys(Exportable, MasterSecret, ServerRandom, ClientRandom,
- HS, KML, _EKML, IVS)
- when Exportable == no_export; Exportable == ignore ->
+-spec setup_keys(binary(), binary(), binary(),
+ integer(), integer(), term(), integer()) ->
+ {binary(), binary(), binary(),
+ binary(), binary(), binary()}.
+
+setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) ->
KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom,
2*(HS+KML+IVS)),
%% draft-ietf-tls-ssl-version3-00 - 6.2.2
@@ -130,86 +126,26 @@ setup_keys(Exportable, MasterSecret, ServerRandom, ClientRandom,
<<ClientWriteMacSecret:HS/binary, ServerWriteMacSecret:HS/binary,
ClientWriteKey:KML/binary, ServerWriteKey:KML/binary,
ClientIV:IVS/binary, ServerIV:IVS/binary>> = KeyBlock,
- ?DBG_HEX(ClientWriteMacSecret),
- ?DBG_HEX(ServerWriteMacSecret),
- ?DBG_HEX(ClientWriteKey),
- ?DBG_HEX(ServerWriteKey),
- ?DBG_HEX(ClientIV),
- ?DBG_HEX(ServerIV),
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, ClientIV, ServerIV};
-
-setup_keys(export, MasterSecret, ServerRandom, ClientRandom,
- HS, KML, EKML, IVS) ->
- KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom,
- 2*(HS+KML)),
- %% draft-ietf-tls-ssl-version3-00 - 6.2.2
- %% Exportable encryption algorithms (for which
- %% CipherSpec.is_exportable is true) require additional processing as
- %% follows to derive their final write keys:
-
- %% final_client_write_key = MD5(client_write_key +
- %% ClientHello.random +
- %% ServerHello.random);
- %% final_server_write_key = MD5(server_write_key +
- %% ServerHello.random +
- %% ClientHello.random);
+ ServerWriteKey, ClientIV, ServerIV}.
- %% Exportable encryption algorithms derive their IVs from the random
- %% messages:
- %% client_write_IV = MD5(ClientHello.random + ServerHello.random);
- %% server_write_IV = MD5(ServerHello.random + ClientHello.random);
-
- <<ClientWriteMacSecret:HS/binary, ServerWriteMacSecret:HS/binary,
- ClientWriteKey:KML/binary, ServerWriteKey:KML/binary>> = KeyBlock,
- <<ClientIV:IVS/binary, _/binary>> =
- hash(?MD5, [ClientRandom, ServerRandom]),
- <<ServerIV:IVS/binary, _/binary>> =
- hash(?MD5, [ServerRandom, ClientRandom]),
- <<FinalClientWriteKey:EKML/binary, _/binary>> =
- hash(?MD5, [ClientWriteKey, ClientRandom, ServerRandom]),
- <<FinalServerWriteKey:EKML/binary, _/binary>> =
- hash(?MD5, [ServerWriteKey, ServerRandom, ClientRandom]),
- ?DBG_HEX(ClientWriteMacSecret),
- ?DBG_HEX(ServerWriteMacSecret),
- ?DBG_HEX(FinalClientWriteKey),
- ?DBG_HEX(FinalServerWriteKey),
- ?DBG_HEX(ClientIV),
- ?DBG_HEX(ServerIV),
- {ClientWriteMacSecret, ServerWriteMacSecret, FinalClientWriteKey,
- FinalServerWriteKey, ClientIV, ServerIV}.
+-spec suites() -> [cipher_suite()].
suites() ->
[
- %% TODO: uncomment when supported
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this?
- %% ?TLS_RSA_WITH_IDEA_CBC_SHA, Not supported: in later openssl version than OTP requires
-
+ %%?TLS_RSA_WITH_IDEA_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
- %%?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5,
- %%?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5,
- %%?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA,
- %%?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA,
- %%?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,
- %%?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA,
-
?TLS_RSA_WITH_DES_CBC_SHA
- %% ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
- %% ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
- %% ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
- %%?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
- %%?TLS_RSA_EXPORT_WITH_RC4_40_MD5
].
%%--------------------------------------------------------------------
@@ -269,8 +205,7 @@ handshake_hash(Method, MasterSecret, Sender, HandshakeHash) ->
hash(Method, [MasterSecret, pad_2(Method), InnerHash]).
get_sender(client) -> "CLNT";
-get_sender(server) -> "SRVR";
-get_sender(none) -> "".
+get_sender(server) -> "SRVR".
generate_keyblock(MasterSecret, ServerRandom, ClientRandom, WantedLength) ->
gen(MasterSecret, [MasterSecret, ServerRandom, ClientRandom],
diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl
index bd5a02417a..316ed8a4e9 100644
--- a/lib/ssl/src/ssl_sup.erl
+++ b/lib/ssl/src/ssl_sup.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -32,16 +32,18 @@
%%%=========================================================================
%%% API
%%%=========================================================================
+
+-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
+
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-%% init([]) -> {ok, {SupFlags, [ChildSpec]}}
-%%
-init([]) ->
-
+-spec init([]) -> {ok, {SupFlags :: tuple(), [ChildSpec :: tuple()]}}.
+
+init([]) ->
%% OLD ssl - moved start to ssl.erl only if old
%% ssl is acctualy run!
%%Child1 = {ssl_server, {ssl_server, start_link, []},
@@ -67,7 +69,7 @@ init([]) ->
session_and_cert_manager_child_spec() ->
Opts = manager_opts(),
Name = ssl_manager,
- StartFunc = {ssl_manager, start_link, Opts},
+ StartFunc = {ssl_manager, start_link, [Opts]},
Restart = permanent,
Shutdown = 4000,
Modules = [ssl_manager],
@@ -86,11 +88,12 @@ connection_manager_child_spec() ->
manager_opts() ->
CbOpts = case application:get_env(ssl, session_cb) of
- {ok, Cb} when is_atom(Cb) ->
- [{session_cb, Cb}];
- _ ->
- []
- end,
+ {ok, Cb} when is_atom(Cb) ->
+ InitArgs = session_cb_init_args(),
+ [{session_cb, Cb}, {session_cb_init_args, InitArgs}];
+ _ ->
+ []
+ end,
case application:get_env(ssl, session_lifetime) of
{ok, Time} when is_integer(Time) ->
[{session_lifetime, Time}| CbOpts];
@@ -98,3 +101,10 @@ manager_opts() ->
CbOpts
end.
+session_cb_init_args() ->
+ case application:get_env(ssl, session_cb_init_args) of
+ {ok, Args} when is_list(Args) ->
+ Args;
+ _ ->
+ []
+ end.
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index ce9a135168..5f9850c386 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -27,15 +27,16 @@
-include("ssl_cipher.hrl").
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
--include("ssl_debug.hrl").
-export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7,
- setup_keys/5, setup_keys/6, suites/0]).
+ setup_keys/6, suites/0]).
%%====================================================================
%% Internal application API
%%====================================================================
+-spec master_secret(binary(), binary(), binary()) -> binary().
+
master_secret(PreMasterSecret, ClientRandom, ServerRandom) ->
%% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret,
%% "master secret", ClientHello.random +
@@ -43,6 +44,8 @@ master_secret(PreMasterSecret, ClientRandom, ServerRandom) ->
prf(PreMasterSecret, <<"master secret">>,
[ClientRandom, ServerRandom], 48).
+-spec finished(client | server, binary(), {binary(), binary()}) -> binary().
+
finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
%% RFC 2246 & 4346 - 7.4.9. Finished
%% struct {
@@ -56,18 +59,20 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) ->
SHA = hash_final(?SHA, SHAHash),
prf(MasterSecret, finished_label(Role), [MD5, SHA], 12).
+-spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary().
-certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa;
- Algorithm == dh_rsa;
- Algorithm == dhe_rsa ->
+certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) ->
MD5 = hash_final(?MD5, MD5Hash),
SHA = hash_final(?SHA, SHAHash),
<<MD5/binary, SHA/binary>>;
-certificate_verify(Algorithm, {_, SHAHash}) when Algorithm == dh_dss;
- Algorithm == dhe_dss ->
+certificate_verify(?'id-dsa', {_, SHAHash}) ->
hash_final(?SHA, SHAHash).
-
+
+-spec setup_keys(binary(), binary(), binary(), integer(),
+ integer(), integer()) -> {binary(), binary(), binary(),
+ binary(), binary(), binary()}.
+
setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
KeyMatLen, IVSize) ->
%% RFC 2246 - 6.3. Key calculation
@@ -92,26 +97,30 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize,
{ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
ServerWriteKey, ClientIV, ServerIV}.
-setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) ->
- %% RFC 4346 - 6.3. Key calculation
- %% key_block = PRF(SecurityParameters.master_secret,
- %% "key expansion",
- %% SecurityParameters.server_random +
- %% SecurityParameters.client_random);
- %% Then the key_block is partitioned as follows:
- %% client_write_MAC_secret[SecurityParameters.hash_size]
- %% server_write_MAC_secret[SecurityParameters.hash_size]
- %% client_write_key[SecurityParameters.key_material_length]
- %% server_write_key[SecurityParameters.key_material_length]
- WantedLength = 2 * (HashSize + KeyMatLen),
- KeyBlock = prf(MasterSecret, "key expansion",
- [ServerRandom, ClientRandom], WantedLength),
- <<ClientWriteMacSecret:HashSize/binary,
- ServerWriteMacSecret:HashSize/binary,
- ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>>
- = KeyBlock,
- {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
- ServerWriteKey, undefined, undefined}.
+%% TLS v1.1 uncomment when supported.
+%% setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) ->
+%% %% RFC 4346 - 6.3. Key calculation
+%% %% key_block = PRF(SecurityParameters.master_secret,
+%% %% "key expansion",
+%% %% SecurityParameters.server_random +
+%% %% SecurityParameters.client_random);
+%% %% Then the key_block is partitioned as follows:
+%% %% client_write_MAC_secret[SecurityParameters.hash_size]
+%% %% server_write_MAC_secret[SecurityParameters.hash_size]
+%% %% client_write_key[SecurityParameters.key_material_length]
+%% %% server_write_key[SecurityParameters.key_material_length]
+%% WantedLength = 2 * (HashSize + KeyMatLen),
+%% KeyBlock = prf(MasterSecret, "key expansion",
+%% [ServerRandom, ClientRandom], WantedLength),
+%% <<ClientWriteMacSecret:HashSize/binary,
+%% ServerWriteMacSecret:HashSize/binary,
+%% ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>>
+%% = KeyBlock,
+%% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey,
+%% ServerWriteKey, undefined, undefined}.
+
+-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(),
+ integer(), binary()) -> binary().
mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
Length, Fragment) ->
@@ -119,51 +128,30 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
%% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type +
%% TLSCompressed.version + TLSCompressed.length +
%% TLSCompressed.fragment));
- case Method of
- ?NULL -> ok;
- _ ->
- ?DBG_HEX(Mac_write_secret),
- ?DBG_HEX(hash(Method, Fragment)),
- ok
- end,
Mac = hmac_hash(Method, Mac_write_secret,
[<<?UINT64(Seq_num), ?BYTE(Type),
?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>,
Fragment]),
- ?DBG_HEX(Mac),
Mac.
+-spec suites() -> [cipher_suite()].
+
suites() ->
[
- %% TODO: uncomment when supported
?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
?TLS_RSA_WITH_AES_256_CBC_SHA,
?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this?
- %% ?TLS_RSA_WITH_IDEA_CBC_SHA,
+ %%?TLS_RSA_WITH_IDEA_CBC_SHA,
?TLS_RSA_WITH_RC4_128_SHA,
?TLS_RSA_WITH_RC4_128_MD5,
- %%?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5,
- %%?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5,
- %%?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA,
- %%?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA,
- %%?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA,
- %%?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA,
- %%?TLS_DHE_DSS_WITH_RC4_128_SHA,
- %%?TLS_DHE_RSA_WITH_DES_CBC_SHA,
- %% EDH-DSS-DES-CBC-SHA TODO: ??
+ ?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
- %% ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
- %% ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
- %%?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA,
- %%?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
- %%?TLS_RSA_EXPORT_WITH_RC4_40_MD5
].
%%--------------------------------------------------------------------
@@ -245,7 +233,3 @@ hash_final(?MD5, Conntext) ->
crypto:md5_final(Conntext);
hash_final(?SHA, Conntext) ->
crypto:sha_final(Conntext).
-
-
-
-
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index bd86120c98..53b2223035 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2011. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
@@ -40,6 +40,7 @@ MODULES = \
ssl_packet_SUITE \
ssl_payload_SUITE \
ssl_to_openssl_SUITE \
+ ssl_session_cache_SUITE \
ssl_test_MACHINE \
old_ssl_active_SUITE \
old_ssl_active_once_SUITE \
@@ -50,7 +51,8 @@ MODULES = \
old_ssl_protocol_SUITE \
old_transport_accept_SUITE \
old_ssl_dist_SUITE \
- make_certs
+ make_certs\
+ erl_make_certs
ERL_FILES = $(MODULES:%=%.erl)
@@ -58,12 +60,11 @@ ERL_FILES = $(MODULES:%=%.erl)
HRL_FILES = ssl_test_MACHINE.hrl
HRL_FILES_SRC = \
- ssl_pkix.hrl \
+ ssl_int.hrl \
ssl_alert.hrl \
ssl_handshake.hrl
-HRL_FILES_INC = \
- OTP-PKIX.hrl
+HRL_FILES_INC =
HRL_FILES_NEEDED_IN_TEST = \
$(HRL_FILES_SRC:%=../src/%) \
@@ -126,8 +127,8 @@ release_spec: opt
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) $(RELSYSDIR)
- $(INSTALL_DATA) ssl.spec $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) ssl.spec ssl.cover $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
new file mode 100644
index 0000000000..8b01ca3ad4
--- /dev/null
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -0,0 +1,421 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Create test certificates
+
+-module(erl_make_certs).
+-include_lib("public_key/include/public_key.hrl").
+
+-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]).
+-compile(export_all).
+
+%%--------------------------------------------------------------------
+%% @doc Create and return a der encoded certificate
+%% Option Default
+%% -------------------------------------------------------
+%% digest sha1
+%% validity {date(), date() + week()}
+%% version 3
+%% subject [] list of the following content
+%% {name, Name}
+%% {email, Email}
+%% {city, City}
+%% {state, State}
+%% {org, Org}
+%% {org_unit, OrgUnit}
+%% {country, Country}
+%% {serial, Serial}
+%% {title, Title}
+%% {dnQualifer, DnQ}
+%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created)
+%% (obs IssuerKey migth be {Key, Password}
+%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key
+%%
+%%
+%% (OBS: The generated keys are for testing only)
+%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
+%% @end
+%%--------------------------------------------------------------------
+
+make_cert(Opts) ->
+ SubjectPrivateKey = get_key(Opts),
+ {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
+ Cert = public_key:pkix_sign(TBSCert, IssuerKey),
+ true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
+ {Cert, encode_key(SubjectPrivateKey)}.
+
+%%--------------------------------------------------------------------
+%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
+%% @spec (::string(), ::string(), {Cert,Key}) -> ok
+%% @end
+%%--------------------------------------------------------------------
+write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
+ ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
+ [{'Certificate', Cert, not_encrypted}]),
+ ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
+
+%%--------------------------------------------------------------------
+%% @doc Creates a rsa key (OBS: for testing only)
+%% the size are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_rsa(Size) when is_integer(Size) ->
+ Key = gen_rsa2(Size),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()}
+%% @end
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% @doc Verifies cert signatures
+%% @spec (::binary(), ::tuple()) -> ::boolean()
+%% @end
+%%--------------------------------------------------------------------
+verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
+ Key = decode_key(DerKey),
+ case Key of
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
+ public_key:pkix_verify(DerEncodedCert,
+ #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
+ public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_key(Opts) ->
+ case proplists:get_value(key, Opts) of
+ undefined -> make_key(rsa, Opts);
+ rsa -> make_key(rsa, Opts);
+ dsa -> make_key(dsa, Opts);
+ Key ->
+ Password = proplists:get_value(password, Opts, no_passwd),
+ decode_key(Key, Password)
+ end.
+
+decode_key({Key, Pw}) ->
+ decode_key(Key, Pw);
+decode_key(Key) ->
+ decode_key(Key, no_passwd).
+
+
+decode_key(#'RSAPublicKey'{} = Key,_) ->
+ Key;
+decode_key(#'RSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'DSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(PemEntry = {_,_,_}, Pw) ->
+ public_key:pem_entry_decode(PemEntry, Pw);
+decode_key(PemBin, Pw) ->
+ [KeyInfo] = public_key:pem_decode(PemBin),
+ decode_key(KeyInfo, Pw).
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+
+make_tbs(SubjectKey, Opts) ->
+ Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
+
+ IssuerProp = proplists:get_value(issuer, Opts, true),
+ {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey),
+
+ {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
+
+ SignAlgo = #'SignatureAlgorithm'{algorithm = Algo,
+ parameters = Parameters},
+ Subject = case IssuerProp of
+ true -> %% Is a Root Ca
+ Issuer;
+ _ ->
+ subject(proplists:get_value(subject, Opts),false)
+ end,
+
+ {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
+ signature = SignAlgo,
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Subject,
+ subjectPublicKeyInfo = publickey(SubjectKey),
+ version = Version,
+ extensions = extensions(Opts)
+ }, IssuerKey}.
+
+issuer(true, Opts, SubjectKey) ->
+ %% Self signed
+ {subject(proplists:get_value(subject, Opts), true), SubjectKey};
+issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
+ {issuer_der(Issuer), decode_key(IssuerKey)};
+issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
+ {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File),
+ {issuer_der(Cert), decode_key(IssuerKey)}.
+
+issuer_der(Issuer) ->
+ Decoded = public_key:pkix_decode_cert(Issuer, otp),
+ #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
+ #'OTPTBSCertificate'{subject=Subject} = Tbs,
+ Subject.
+
+subject(undefined, IsRootCA) ->
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end,
+ Opts = [{email, User ++ "@erlang.org"},
+ {name, User},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+ subject(Opts);
+subject(Opts, _) ->
+ subject(Opts).
+
+subject(SubjectOpts) when is_list(SubjectOpts) ->
+ Encode = fun(Opt) ->
+ {Type,Value} = subject_enc(Opt),
+ [#'AttributeTypeAndValue'{type=Type, value=Value}]
+ end,
+ {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
+
+%% Fill in the blanks
+subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
+subject_enc({email, Email}) -> {?'id-emailAddress', Email};
+subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
+subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}};
+subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
+subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
+subject_enc({country, Country}) -> {?'id-at-countryName', Country};
+subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial};
+subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}};
+subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ};
+subject_enc(Other) -> Other.
+
+
+extensions(Opts) ->
+ case proplists:get_value(extensions, Opts, []) of
+ false ->
+ asn1_NOVALUE;
+ Exts ->
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
+ end.
+
+default_extensions(Exts) ->
+ Def = [{key_usage,undefined},
+ {subject_altname, undefined},
+ {issuer_altname, undefined},
+ {basic_constraints, default},
+ {name_constraints, undefined},
+ {policy_constraints, undefined},
+ {ext_key_usage, undefined},
+ {inhibit_any, undefined},
+ {auth_key_id, undefined},
+ {subject_key_id, undefined},
+ {policy_mapping, undefined}],
+ Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
+ Exts ++ lists:foldl(Filter, Def, Exts).
+
+extension({_, undefined}) -> [];
+extension({basic_constraints, Data}) ->
+ case Data of
+ default ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true},
+ critical=true};
+ false ->
+ [];
+ Len when is_integer(Len) ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
+ critical=true};
+ _ ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = Data}
+ end;
+extension({Id, Data, Critical}) ->
+ #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
+
+
+publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
+ Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = Public};
+publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
+ parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}.
+
+validity(Opts) ->
+ DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
+ DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
+ {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
+ Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
+ #'Validity'{notBefore={generalTime, Format(DefFrom)},
+ notAfter ={generalTime, Format(DefTo)}}.
+
+sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'sha1WithRSAEncryption';
+ sha512 -> ?'sha512WithRSAEncryption';
+ sha384 -> ?'sha384WithRSAEncryption';
+ sha256 -> ?'sha256WithRSAEncryption';
+ md5 -> ?'md5WithRSAEncryption';
+ md2 -> ?'md2WithRSAEncryption'
+ end,
+ {Type, 'NULL'};
+sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
+ {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}.
+
+make_key(rsa, _Opts) ->
+ %% (OBS: for testing only)
+ gen_rsa2(64);
+make_key(dsa, _Opts) ->
+ gen_dsa2(128, 20). %% Bytes i.e. {1024, 160}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% RSA key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
+ 47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
+
+gen_rsa2(Size) ->
+ P = prime(Size),
+ Q = prime(Size),
+ N = P*Q,
+ Tot = (P - 1) * (Q - 1),
+ [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
+ {D1,D2} = extended_gcd(E, Tot),
+ D = erlang:max(D1,D2),
+ case D < E of
+ true ->
+ gen_rsa2(Size);
+ false ->
+ {Co1,Co2} = extended_gcd(Q, P),
+ Co = erlang:max(Co1,Co2),
+ #'RSAPrivateKey'{version = 'two-prime',
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ exponent1 = D rem (P-1),
+ exponent2 = D rem (Q-1),
+ coefficient = Co
+ }
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p–1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(crypto:mpint(P), 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ crypto:erlint(prime_odd(Rand, 0)).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ NotPrime = crypto:erlint(Rand),
+ prime_odd(crypto:mpint(NotPrime+2), N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
+ case crypto:mod_exp(CoPrime, Candidate, Candidate) of
+ CoPrime -> is_prime(Candidate, Test-1);
+ _ -> false
+ end.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
+
+odd_rand(Min,Max) ->
+ Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
+ BitSkip = (Sz+4)*8-1,
+ case Rand of
+ Odd = <<_:BitSkip, 1:1>> -> Odd;
+ Even = <<_:BitSkip, 0:1>> ->
+ crypto:mpint(crypto:erlint(Even)+1)
+ end.
+
+extended_gcd(A, B) ->
+ case A rem B of
+ 0 ->
+ {0, 1};
+ N ->
+ {X, Y} = extended_gcd(B, N),
+ {Y, X-Y*(A div B)}
+ end.
+
+pem_to_der(File) ->
+ {ok, PemBin} = file:read_file(File),
+ public_key:pem_decode(PemBin).
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 0cdf33c3e2..693289990c 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -90,8 +90,10 @@ enduser(Root, OpenSSLCmd, CA, User) ->
KeyFile = filename:join([UsrRoot, "key.pem"]),
ReqFile = filename:join([UsrRoot, "req.pem"]),
create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
- CertFile = filename:join([UsrRoot, "cert.pem"]),
- sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFile).
+ CertFileAllUsage = filename:join([UsrRoot, "cert.pem"]),
+ sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFileAllUsage),
+ CertFileDigitalSigOnly = filename:join([UsrRoot, "digital_signature_only_cert.pem"]),
+ sign_req(Root, OpenSSLCmd, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly).
collect_certs(Root, CAs, Users) ->
Bins = lists:foldr(
@@ -255,6 +257,7 @@ ca_cnf(CA) ->
"RANDFILE = $dir/private/RAND\n"
"\n"
"x509_extensions = user_cert\n"
+ "unique_subject = no\n"
"default_days = 3600\n"
"default_md = sha1\n"
"preserve = no\n"
@@ -279,6 +282,15 @@ ca_cnf(CA) ->
"issuerAltName = issuer:copy\n"
"\n"
+ "[user_cert_digital_signature_only]\n"
+ "basicConstraints = CA:false\n"
+ "keyUsage = digitalSignature\n"
+ "subjectKeyIdentifier = hash\n"
+ "authorityKeyIdentifier = keyid,issuer:always\n"
+ "subjectAltName = email:copy\n"
+ "issuerAltName = issuer:copy\n"
+ "\n"
+
"[ca_cert]\n"
"basicConstraints = critical,CA:true\n"
"keyUsage = cRLSign, keyCertSign\n"
diff --git a/lib/ssl/test/old_ssl_active_SUITE.erl b/lib/ssl/test/old_ssl_active_SUITE.erl
index 010596f351..52ff0bcc5d 100644
--- a/lib/ssl/test/old_ssl_active_SUITE.erl
+++ b/lib/ssl/test/old_ssl_active_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,11 +20,10 @@
%%
-module(old_ssl_active_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
- config/1,
- finish/1,
+ end_per_testcase/2,
cinit_return_chkclose/1,
sinit_return_chkclose/1,
cinit_big_return_chkclose/1,
@@ -40,7 +39,7 @@
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
-define(MANYCONNS, ssl_test_MACHINE:many_conns()).
@@ -49,33 +48,35 @@ init_per_testcase(_Case, Config) ->
WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of ssl.erl interface in active mode.";
-all(suite) ->
- {conf,
- config,
- [cinit_return_chkclose,
- sinit_return_chkclose,
- cinit_big_return_chkclose,
- sinit_big_return_chkclose,
- cinit_big_echo_chkclose,
- cinit_huge_echo_chkclose,
- sinit_big_echo_chkclose,
- cinit_few_echo_chkclose,
- cinit_many_echo_chkclose,
- cinit_cnocert],
- finish}.
-
-config(doc) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [cinit_return_chkclose, sinit_return_chkclose,
+ cinit_big_return_chkclose, sinit_big_return_chkclose,
+ cinit_big_echo_chkclose, cinit_huge_echo_chkclose,
+ sinit_big_echo_chkclose, cinit_few_echo_chkclose,
+ cinit_many_echo_chkclose, cinit_cnocert].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains, and record the number of available "
"file descriptors";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
case os:type() of
{unix, _} ->
@@ -87,18 +88,25 @@ config(Config) ->
%% operating system, version of OTP, Erts, kernel and stdlib.
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto!"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no mission other than closing the conf case";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
cinit_return_chkclose(doc) ->
diff --git a/lib/ssl/test/old_ssl_active_once_SUITE.erl b/lib/ssl/test/old_ssl_active_once_SUITE.erl
index 6224b17aa7..c7beadb301 100644
--- a/lib/ssl/test/old_ssl_active_once_SUITE.erl
+++ b/lib/ssl/test/old_ssl_active_once_SUITE.erl
@@ -1,30 +1,29 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_ssl_active_once_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
- config/1,
- finish/1,
+ end_per_testcase/2,
server_accept_timeout/1,
cinit_return_chkclose/1,
sinit_return_chkclose/1,
@@ -40,7 +39,7 @@
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
-define(MANYCONNS, ssl_test_MACHINE:many_conns()).
@@ -49,48 +48,57 @@ init_per_testcase(_Case, Config) ->
WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of ssl.erl interface in passive mode.";
-all(suite) ->
- {conf,
- config,
- [server_accept_timeout,
- cinit_return_chkclose,
- sinit_return_chkclose,
- cinit_big_return_chkclose,
- sinit_big_return_chkclose,
- cinit_big_echo_chkclose,
- cinit_huge_echo_chkclose,
- sinit_big_echo_chkclose,
- cinit_few_echo_chkclose,
- cinit_many_echo_chkclose,
- cinit_cnocert],
- finish}.
-
-config(doc) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [server_accept_timeout, cinit_return_chkclose,
+ sinit_return_chkclose, cinit_big_return_chkclose,
+ sinit_big_return_chkclose, cinit_big_echo_chkclose,
+ cinit_huge_echo_chkclose, sinit_big_echo_chkclose,
+ cinit_few_echo_chkclose, cinit_many_echo_chkclose,
+ cinit_cnocert].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains.";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no mission other than closing the conf case";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
server_accept_timeout(doc) ->
diff --git a/lib/ssl/test/old_ssl_dist_SUITE.erl b/lib/ssl/test/old_ssl_dist_SUITE.erl
index 56209c3530..4544fb616a 100644
--- a/lib/ssl/test/old_ssl_dist_SUITE.erl
+++ b/lib/ssl/test/old_ssl_dist_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -29,39 +29,55 @@
%%%-------------------------------------------------------------------
-module(old_ssl_dist_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(DEFAULT_TIMETRAP_SECS, 240).
-define(AWAIT_SLL_NODE_UP_TIMEOUT, 30000).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([init_per_suite/1,
end_per_suite/1,
init_per_testcase/2,
- fin_per_testcase/2]).
+ end_per_testcase/2]).
-export([cnct2tstsrvr/1]).
-export([basic/1]).
-record(node_handle, {connection_handler, socket, name, nodename}).
-all(doc) ->
- [];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[basic].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
init_per_suite(Config) ->
- add_ssl_opts_config(Config).
+ try crypto:start() of
+ ok ->
+ add_ssl_opts_config(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
end_per_suite(Config) ->
+ application:stop(crypto),
Config.
init_per_testcase(Case, Config) when list(Config) ->
Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMETRAP_SECS)),
[{watchdog, Dog},{testcase, Case}|Config].
-fin_per_testcase(_Case, Config) when list(Config) ->
+end_per_testcase(_Case, Config) when list(Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
@@ -254,7 +270,8 @@ mk_node_cmdline(ListenPort, Name, Args) ->
Prog ++ " "
++ Static ++ " "
++ NameSw ++ " " ++ Name ++ " "
- ++ "-pa " ++ Pa ++ " "
+ ++ "-pa " ++ Pa ++ " "
+ ++ "-run application start crypto -run application start public_key "
++ "-run " ++ atom_to_list(?MODULE) ++ " cnct2tstsrvr "
++ host_name() ++ " "
++ integer_to_list(ListenPort) ++ " "
@@ -524,23 +541,10 @@ add_ssl_opts_config(Config) ->
KrnlDir = filename:join([LibDir, "kernel-" ++ KRNL_VSN]),
{ok, _} = file:read_file_info(StdlDir),
{ok, _} = file:read_file_info(KrnlDir),
- SSL_VSN = case lists:keysearch(ssl, 1, Apps) of
- {value, {ssl, _, VSN}} ->
- VSN;
- _ ->
- application:start(ssl),
- try
- {value,
- {ssl,
- _,
- VSN}} = lists:keysearch(ssl,
- 1,
- application:which_applications()),
- VSN
- after
- application:stop(ssl)
- end
- end,
+ SSL_VSN = vsn(ssl),
+ VSN_CRYPTO = vsn(crypto),
+ VSN_PKEY = vsn(public_key),
+
SslDir = filename:join([LibDir, "ssl-" ++ SSL_VSN]),
{ok, _} = file:read_file_info(SslDir),
%% We are using an installed otp system, create the boot script.
@@ -552,6 +556,8 @@ add_ssl_opts_config(Config) ->
" {erts, \"~s\"},~n"
" [{kernel, \"~s\"},~n"
" {stdlib, \"~s\"},~n"
+ " {crypto, \"~s\"},~n"
+ " {public_key, \"~s\"},~n"
" {ssl, \"~s\"}]}.~n",
[case catch erlang:system_info(otp_release) of
{'EXIT', _} -> "R11B";
@@ -560,6 +566,8 @@ add_ssl_opts_config(Config) ->
erlang:system_info(version),
KRNL_VSN,
STDL_VSN,
+ VSN_CRYPTO,
+ VSN_PKEY,
SSL_VSN]),
ok = file:close(RelFile),
ok = systools:make_script(Script, []),
@@ -593,3 +601,17 @@ success(Config) ->
{value, {comment, _} = Res} -> Res;
_ -> ok
end.
+
+vsn(App) ->
+ application:start(App),
+ try
+ {value,
+ {ssl,
+ _,
+ VSN}} = lists:keysearch(App,
+ 1,
+ application:which_applications()),
+ VSN
+ after
+ application:stop(ssl)
+ end.
diff --git a/lib/ssl/test/old_ssl_misc_SUITE.erl b/lib/ssl/test/old_ssl_misc_SUITE.erl
index 55d1b71025..ea03e83867 100644
--- a/lib/ssl/test/old_ssl_misc_SUITE.erl
+++ b/lib/ssl/test/old_ssl_misc_SUITE.erl
@@ -1,37 +1,36 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_ssl_misc_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
- config/1,
- finish/1,
+ end_per_testcase/2,
seed/1,
app/1
]).
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
-define(MANYCONNS, 5).
@@ -40,39 +39,52 @@ init_per_testcase(_Case, Config) ->
WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of misc in ssl.erl interface.";
-all(suite) ->
- {conf,
- config,
- [seed, app],
- finish
- }.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-config(doc) ->
+all() ->
+ [seed, app].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains.";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto!"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no mission other than closing the conf case";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
seed(doc) ->
diff --git a/lib/ssl/test/old_ssl_passive_SUITE.erl b/lib/ssl/test/old_ssl_passive_SUITE.erl
index 4cb8c1f0cd..7b54fe876a 100644
--- a/lib/ssl/test/old_ssl_passive_SUITE.erl
+++ b/lib/ssl/test/old_ssl_passive_SUITE.erl
@@ -1,30 +1,29 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_ssl_passive_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1,
+ end_per_suite/1, init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
- config/1,
- finish/1,
+ end_per_testcase/2,
server_accept_timeout/1,
cinit_return_chkclose/1,
sinit_return_chkclose/1,
@@ -40,7 +39,7 @@
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
-define(MANYCONNS, ssl_test_MACHINE:many_conns()).
@@ -49,47 +48,56 @@ init_per_testcase(_Case, Config) ->
WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of ssl.erl interface in passive mode.";
-all(suite) ->
- {conf,
- config,
- [server_accept_timeout,
- cinit_return_chkclose,
- sinit_return_chkclose,
- cinit_big_return_chkclose,
- sinit_big_return_chkclose,
- cinit_big_echo_chkclose,
- sinit_big_echo_chkclose,
- cinit_few_echo_chkclose,
- cinit_many_echo_chkclose,
- cinit_cnocert],
- finish}.
-
-config(doc) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [server_accept_timeout, cinit_return_chkclose,
+ sinit_return_chkclose, cinit_big_return_chkclose,
+ sinit_big_return_chkclose, cinit_big_echo_chkclose,
+ sinit_big_echo_chkclose, cinit_few_echo_chkclose,
+ cinit_many_echo_chkclose, cinit_cnocert].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains.";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no mission other than closing the conf case";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
server_accept_timeout(doc) ->
diff --git a/lib/ssl/test/old_ssl_peer_cert_SUITE.erl b/lib/ssl/test/old_ssl_peer_cert_SUITE.erl
index f0b8db2607..ee19bad175 100644
--- a/lib/ssl/test/old_ssl_peer_cert_SUITE.erl
+++ b/lib/ssl/test/old_ssl_peer_cert_SUITE.erl
@@ -1,30 +1,29 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_ssl_peer_cert_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
- config/1,
- finish/1,
+ end_per_testcase/2,
cinit_plain/1,
cinit_both_verify/1,
cinit_cnocert/1
@@ -32,7 +31,7 @@
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
@@ -40,40 +39,52 @@ init_per_testcase(_Case, Config) ->
WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of ssl verification and peer certificate retrieval.";
-all(suite) ->
- {conf,
- config,
- [cinit_plain,
- cinit_both_verify,
- cinit_cnocert],
- finish}.
-
-config(doc) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [cinit_plain, cinit_both_verify, cinit_cnocert].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains.";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no mission other than closing the conf case";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
cinit_plain(doc) ->
diff --git a/lib/ssl/test/old_ssl_protocol_SUITE.erl b/lib/ssl/test/old_ssl_protocol_SUITE.erl
index 7bde5d6749..9b9937c210 100644
--- a/lib/ssl/test/old_ssl_protocol_SUITE.erl
+++ b/lib/ssl/test/old_ssl_protocol_SUITE.erl
@@ -1,32 +1,34 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_ssl_protocol_SUITE).
--export([all/1, init_per_testcase/2, fin_per_testcase/2, config/1,
- finish/1, sslv2/1, sslv3/1, tlsv1/1, sslv2_sslv3/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+ sslv2/1, sslv3/1, tlsv1/1, sslv2_sslv3/1,
sslv2_tlsv1/1, sslv3_tlsv1/1, sslv2_sslv3_tlsv1/1]).
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
@@ -34,39 +36,53 @@ init_per_testcase(_Case, Config) ->
WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of configuration protocol_version.";
-all(suite) ->
- {conf,
- config,
- [sslv2, sslv3, tlsv1, sslv2_sslv3, sslv2_tlsv1, sslv3_tlsv1,
- sslv2_sslv3_tlsv1],
- finish}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-config(doc) ->
+all() ->
+ [sslv2, sslv3, tlsv1, sslv2_sslv3, sslv2_tlsv1,
+ sslv3_tlsv1, sslv2_sslv3_tlsv1].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains.";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no other purpose than closing the conf case.";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
%%%%%
diff --git a/lib/ssl/test/old_ssl_verify_SUITE.erl b/lib/ssl/test/old_ssl_verify_SUITE.erl
index 5db964526f..4c11ea6850 100644
--- a/lib/ssl/test/old_ssl_verify_SUITE.erl
+++ b/lib/ssl/test/old_ssl_verify_SUITE.erl
@@ -1,37 +1,36 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_ssl_verify_SUITE).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
- config/1,
- finish/1,
+ end_per_testcase/2,
cinit_both_verify/1,
cinit_cnocert/1
]).
-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
test_server_only/6]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("ssl_test_MACHINE.hrl").
@@ -39,39 +38,52 @@ init_per_testcase(_Case, Config) ->
WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
[{watchdog, WatchDog}| Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test of ssl.erl interface in active mode.";
-all(suite) ->
- {conf,
- config,
- [cinit_both_verify,
- cinit_cnocert],
- finish}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
-config(doc) ->
+all() ->
+ [cinit_both_verify, cinit_cnocert].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(doc) ->
"Want to se what Config contains.";
-config(suite) ->
+init_per_suite(suite) ->
[];
-config(Config) ->
+init_per_suite(Config) ->
io:format("Config: ~p~n", [Config]),
%% Check if SSL exists. If this case fails, all other cases are skipped
- case ssl:start() of
- ok -> ssl:stop();
- {error, {already_started, _}} -> ssl:stop();
- Error -> ?t:fail({failed_starting_ssl,Error})
- end,
- Config.
-
-finish(doc) ->
+ case catch crypto:start() of
+ ok ->
+ application:start(public_key),
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config;
+ _Else ->
+ {skip,"Could not start crypto"}
+ end.
+
+end_per_suite(doc) ->
"This test case has no mission other than closing the conf case";
-finish(suite) ->
+end_per_suite(suite) ->
[];
-finish(Config) ->
+end_per_suite(Config) ->
+ crypto:stop(),
Config.
cinit_both_verify(doc) ->
diff --git a/lib/ssl/test/old_transport_accept_SUITE.erl b/lib/ssl/test/old_transport_accept_SUITE.erl
index 4bb09cee19..6f0c8e456b 100644
--- a/lib/ssl/test/old_transport_accept_SUITE.erl
+++ b/lib/ssl/test/old_transport_accept_SUITE.erl
@@ -1,34 +1,35 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%%
-module(old_transport_accept_SUITE).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
%% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
-define(application, ssh).
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
+ end_per_testcase/2,
config/1,
echo_once/1,
echo_twice/1,
@@ -43,15 +44,37 @@ init_per_testcase(_Case, Config) ->
[{watchdog, WatchDog}, {protomod, gen_tcp}, {serialize_accept, true}|
Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
WatchDog = ?config(watchdog, Config),
test_server:timetrap_cancel(WatchDog).
-all(doc) ->
- "Test transport_accept and ssl_accept";
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[config, echo_once, echo_twice, close_before_ssl_accept].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ application:stop(crypto),
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
config(doc) ->
"Want to se what Config contains.";
config(suite) ->
@@ -224,12 +247,9 @@ tolerant_server_loop(Client, LSock, Msg, N) ->
tolerant_server_loop(Client, LSock, Msg, N-1).
app() ->
- case application:get_application(ssl) of
- undefined ->
- application:start(ssl);
- _ ->
- ok
- end.
+ crypto:start(),
+ application:start(public_key),
+ ssl:start().
start_node(Kind, Params) ->
S = atom_to_list(?MODULE)++"_" ++ atom_to_list(Kind),
diff --git a/lib/ssl/test/ssl.cover b/lib/ssl/test/ssl.cover
index e8daa363c5..60774cc0f1 100644
--- a/lib/ssl/test/ssl.cover
+++ b/lib/ssl/test/ssl.cover
@@ -1,19 +1,21 @@
-{exclude, [ssl_pkix_oid,
- 'PKIX1Algorithms88',
- 'PKIX1Explicit88',
- 'PKIX1Implicit88',
- 'PKIXAttributeCertificate',
- 'SSL-PKIX',
- ssl_pem,
- ssl_pkix,
- ssl_base64,
- ssl_broker,
- ssl_broker_int,
- ssl_broker_sup,
- ssl_debug,
- ssl_server,
- ssl_prim,
- inet_ssl_dist,
- 'OTP-PKIX'
+{incl_app,ssl,details}.
+
+{excl_mods, ssl, [ssl_pkix_oid,
+ 'PKIX1Algorithms88',
+ 'PKIX1Explicit88',
+ 'PKIX1Implicit88',
+ 'PKIXAttributeCertificate',
+ 'SSL-PKIX',
+ ssl_pem,
+ ssl_pkix,
+ ssl_base64,
+ ssl_broker,
+ ssl_broker_int,
+ ssl_broker_sup,
+ ssl_debug,
+ ssl_server,
+ ssl_prim,
+ inet_ssl_dist,
+ 'OTP-PKIX'
]}.
diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec
index 6ef4fb73db..fc7c1bbb82 100644
--- a/lib/ssl/test/ssl.spec
+++ b/lib/ssl/test/ssl.spec
@@ -1 +1 @@
-{topcase, {dir, "../ssl_test"}}.
+{suites,"../ssl_test",all}.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 9afcbd9113..4f0907027f 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -1,13 +1,13 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
+%% retrieved online at http://www.erlang.org/.2
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
@@ -24,22 +24,19 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_int.hrl").
+
-define('24H_in_sec', 86400).
-define(TIMEOUT, 60000).
+-define(LONG_TIMEOUT, 600000).
-define(EXPIRE, 10).
-define(SLEEP, 500).
-
--behaviour(ssl_session_cache_api).
-
-%% For the session cache tests
--export([init/0, terminate/1, lookup/2, update/3,
- delete/2, foldl/3, select_session/2]).
-
%% Test server callback functions
%%--------------------------------------------------------------------
%% Function: init_per_suite(Config) -> Config
@@ -50,15 +47,25 @@
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
-init_per_suite(Config) ->
- crypto:start(),
- ssl:start(),
- Result =
- (catch make_certs:all(?config(data_dir, Config),
- ?config(priv_dir, Config))),
- test_server:format("Make certs ~p~n", [Result]),
- ssl_test_lib:cert_options(Config).
-
+init_per_suite(Config0) ->
+ Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+
+ %% make rsa certs using oppenssl
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ test_server:format("Make certs ~p~n", [Result]),
+
+ Config1 = ssl_test_lib:make_dsa_cert(Config0),
+ Config = ssl_test_lib:cert_options(Config1),
+ [{watchdog, Dog} | Config]
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
%% Config - [tuple()]
@@ -67,7 +74,7 @@ init_per_suite(Config) ->
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
ssl:stop(),
- crypto:stop().
+ application:stop(crypto).
%%--------------------------------------------------------------------
%% Function: init_per_testcase(TestCase, Config) -> Config
@@ -83,11 +90,11 @@ end_per_suite(_Config) ->
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
init_per_testcase(session_cache_process_list, Config) ->
- init_customized_session_cache(Config);
+ init_customized_session_cache(list, Config);
init_per_testcase(session_cache_process_mnesia, Config) ->
mnesia:start(),
- init_customized_session_cache(Config);
+ init_customized_session_cache(mnesia, Config);
init_per_testcase(reuse_session_expired, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
@@ -98,17 +105,53 @@ init_per_testcase(reuse_session_expired, Config0) ->
ssl:start(),
[{watchdog, Dog} | Config];
+init_per_testcase(no_authority_key_identifier, Config) ->
+ %% Clear cach so that root cert will not
+ %% be found.
+ ssl:stop(),
+ ssl:start(),
+ Config;
+
+init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3;
+ TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3;
+ TestCase == ciphers_dsa_signed_certs_ssl3;
+ TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3 ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, sslv3),
+ ssl:start(),
+ Config;
+
+init_per_testcase(protocol_versions, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ %% For backwards compatibility sslv2 should be filtered out.
+ application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
+ ssl:start(),
+ Config;
+
+init_per_testcase(empty_protocol_versions, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, []),
+ ssl:start(),
+ Config;
+
+init_per_testcase(different_ca_peer_sign, Config0) ->
+ ssl_test_lib:make_mix_cert(Config0);
+
init_per_testcase(_TestCase, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
Dog = test_server:timetrap(?TIMEOUT),
- [{watchdog, Dog} | Config].
+ [{watchdog, Dog} | Config].
-init_customized_session_cache(Config0) ->
+init_customized_session_cache(Type, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
Dog = test_server:timetrap(?TIMEOUT),
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_cb, ?MODULE),
+ application:set_env(ssl, session_cb_init_args, [Type]),
ssl:start(),
[{watchdog, Dog} | Config].
@@ -125,11 +168,22 @@ end_per_testcase(session_cache_process_list, Config) ->
end_per_testcase(default_action, Config);
end_per_testcase(session_cache_process_mnesia, Config) ->
application:unset_env(ssl, session_cb),
+ application:unset_env(ssl, session_cb_init_args),
mnesia:stop(),
+ ssl:stop(),
+ ssl:start(),
end_per_testcase(default_action, Config);
end_per_testcase(reuse_session_expired, Config) ->
application:unset_env(ssl, session_lifetime),
end_per_testcase(default_action, Config);
+end_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3;
+ TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3;
+ TestCase == ciphers_dsa_signed_certs_ssl3;
+ TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3;
+ TestCase == protocol_versions;
+ TestCase == empty_protocol_versions->
+ application:unset_env(ssl, protocol_version),
+ end_per_testcase(default_action, Config);
end_per_testcase(_TestCase, Config) ->
Dog = ?config(watchdog, Config),
case Dog of
@@ -147,36 +201,69 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test the basic ssl functionality"];
-
-all(suite) ->
- [app, connection_info, controlling_process, controller_dies,
- client_closes_socket,
- peercert, connect_dist,
- peername, sockname, socket_options, misc_ssl_options, versions, cipher_suites,
- upgrade, upgrade_with_timeout, tcp_connect,
- ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown,
- shutdown_write, shutdown_both, shutdown_error, ciphers,
- send_close, close_transport_accept, dh_params,
- server_verify_peer_passive,
- server_verify_peer_active, server_verify_peer_active_once,
- server_verify_none_passive, server_verify_none_active,
- server_verify_none_active_once, server_verify_no_cacerts,
- server_require_peer_cert_ok, server_require_peer_cert_fail,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app, alerts, connection_info, protocol_versions,
+ empty_protocol_versions, controlling_process,
+ controller_dies, client_closes_socket, peercert,
+ connect_dist, peername, sockname, socket_options,
+ misc_ssl_options, versions, cipher_suites, upgrade,
+ upgrade_with_timeout, tcp_connect, ipv6, ekeyfile,
+ ecertfile, ecacertfile, eoptions, shutdown,
+ shutdown_write, shutdown_both, shutdown_error,
+ ciphers_rsa_signed_certs, ciphers_rsa_signed_certs_ssl3,
+ ciphers_rsa_signed_certs_openssl_names,
+ ciphers_rsa_signed_certs_openssl_names_ssl3,
+ ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_ssl3,
+ ciphers_dsa_signed_certs_openssl_names,
+ ciphers_dsa_signed_certs_openssl_names_ssl3,
+ anonymous_cipher_suites,
+ default_reject_anonymous,
+ send_close,
+ close_transport_accept, dh_params,
+ server_verify_peer_passive, server_verify_peer_active,
+ server_verify_peer_active_once,
+ server_verify_none_passive, server_verify_none_active,
+ server_verify_none_active_once,
+ server_verify_no_cacerts, server_require_peer_cert_ok,
+ server_require_peer_cert_fail,
server_verify_client_once_passive,
server_verify_client_once_active,
server_verify_client_once_active_once,
- client_verify_none_passive,
- client_verify_none_active, client_verify_none_active_once
- %%, session_cache_process_list, session_cache_process_mnesia
- ,reuse_session, reuse_session_expired, server_does_not_want_to_reuse_session,
- client_renegotiate, server_renegotiate, client_renegotiate_reused_session,
+ client_verify_none_passive, client_verify_none_active,
+ client_verify_none_active_once,
+ reuse_session,
+ reuse_session_expired,
+ server_does_not_want_to_reuse_session,
+ client_renegotiate, server_renegotiate,
+ client_renegotiate_reused_session,
server_renegotiate_reused_session,
- client_no_wrap_sequence_number, server_no_wrap_sequence_number,
- extended_key_usage, validate_extensions_fun
+ client_no_wrap_sequence_number,
+ server_no_wrap_sequence_number, extended_key_usage_verify_peer,
+ extended_key_usage_verify_none,
+ no_authority_key_identifier, invalid_signature_client,
+ invalid_signature_server, cert_expired,
+ client_with_cert_cipher_suites_handshake,
+ unknown_server_ca_fail, der_input,
+ unknown_server_ca_accept_verify_none,
+ unknown_server_ca_accept_verify_peer,
+ unknown_server_ca_accept_backwardscompatibilty,
+ %%different_ca_peer_sign,
+ no_reuses_session_server_restart_new_cert,
+ no_reuses_session_server_restart_new_cert_file, reuseaddr,
+ hibernate
].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Test cases starts here.
%%--------------------------------------------------------------------
app(doc) ->
@@ -185,7 +272,31 @@ app(suite) ->
[];
app(Config) when is_list(Config) ->
ok = test_server:app_test(ssl).
-
+%%--------------------------------------------------------------------
+alerts(doc) ->
+ "Test ssl_alert:alert_txt/1";
+alerts(suite) ->
+ [];
+alerts(Config) when is_list(Config) ->
+ Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC,
+ ?DECRYPTION_FAILED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE,
+ ?HANDSHAKE_FAILURE, ?BAD_CERTIFICATE, ?UNSUPPORTED_CERTIFICATE,
+ ?CERTIFICATE_REVOKED,?CERTIFICATE_EXPIRED, ?CERTIFICATE_UNKNOWN,
+ ?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR,
+ ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION,
+ ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED,
+ ?NO_RENEGOTIATION],
+ Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) |
+ [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]],
+ lists:foreach(fun(Alert) ->
+ case ssl_alert:alert_txt(Alert) of
+ Txt when is_list(Txt) ->
+ ok;
+ Other ->
+ test_server:fail({unexpected, Other})
+ end
+ end, Alerts).
+%%--------------------------------------------------------------------
connection_info(doc) ->
["Test the API function ssl:connection_info/1"];
connection_info(suite) ->
@@ -214,7 +325,7 @@ connection_info(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha,no_export}}},
+ ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha}}},
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
@@ -226,6 +337,48 @@ connection_info_result(Socket) ->
%%--------------------------------------------------------------------
+protocol_versions(doc) ->
+ ["Test to set a list of protocol versions in app environment."];
+
+protocol_versions(suite) ->
+ [];
+
+protocol_versions(Config) when is_list(Config) ->
+ basic_test(Config).
+
+empty_protocol_versions(doc) ->
+ ["Test to set an empty list of protocol versions in app environment."];
+
+empty_protocol_versions(suite) ->
+ [];
+
+empty_protocol_versions(Config) when is_list(Config) ->
+ basic_test(Config).
+
+
+basic_test(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
controlling_process(doc) ->
["Test API function controlling_process/2"];
@@ -283,7 +436,7 @@ controlling_process_result(Socket, Pid, Msg) ->
ssl:send(Socket, Msg),
no_result_msg.
-
+%%--------------------------------------------------------------------
controller_dies(doc) ->
["Test that the socket is closed after controlling process dies"];
controller_dies(suite) -> [];
@@ -423,9 +576,7 @@ client_closes_socket(Config) when is_list(Config) ->
_Client = spawn_link(Connect),
- ssl_test_lib:check_result(Server, {error,closed}),
-
- ssl_test_lib:close(Server).
+ ssl_test_lib:check_result(Server, {error,closed}).
%%--------------------------------------------------------------------
@@ -452,8 +603,8 @@ peercert(Config) when is_list(Config) ->
{options, ClientOpts}]),
CertFile = proplists:get_value(certfile, ServerOpts),
- {ok, [{cert, BinCert, _}]} = public_key:pem_to_der(CertFile),
- {ok, ErlCert} = public_key:pkix_decode_cert(BinCert, otp),
+ [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile),
+ ErlCert = public_key:pkix_decode_cert(BinCert, otp),
ServerMsg = {{error, no_peercert}, {error, no_peercert}},
ClientMsg = {{ok, BinCert}, {ok, ErlCert}},
@@ -598,9 +749,12 @@ cipher_suites(suite) ->
[];
cipher_suites(Config) when is_list(Config) ->
- MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha,no_export},
+ MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha},
[_|_] = Suites = ssl:cipher_suites(),
- true = lists:member(MandatoryCipherSuite, Suites).
+ true = lists:member(MandatoryCipherSuite, Suites),
+ Suites = ssl:cipher_suites(erlang),
+ [_|_] =ssl:cipher_suites(openssl).
+
%%--------------------------------------------------------------------
socket_options(doc) ->
["Test API function getopts/2 and setopts/2"];
@@ -635,9 +789,15 @@ socket_options(Config) when is_list(Config) ->
{options, ClientOpts}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
-
+
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+
+ {ok, Listen} = ssl:listen(0, ServerOpts),
+ {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]),
+ ok = ssl:setopts(Listen, [{mode, binary}]),
+ {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]),
+ {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]),
+ ssl:close(Listen).
socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
%% Test get/set emulated opts
@@ -646,6 +806,8 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
{ok, NewValues} = ssl:getopts(Socket, NewOptions),
%% Test get/set inet opts
{ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]),
+ ssl:setopts(Socket, [{nodelay, true}]),
+ {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]),
ok.
%%--------------------------------------------------------------------
@@ -731,6 +893,7 @@ send_recv(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
send_close(doc) ->
[""];
@@ -757,8 +920,7 @@ send_close(Config) when is_list(Config) ->
ok = ssl:send(SslS, "Hello world"),
{ok,<<"Hello world">>} = ssl:recv(SslS, 11),
gen_tcp:close(TcpS),
- {error, _} = ssl:send(SslS, "Hello world"),
- ssl_test_lib:close(Server).
+ {error, _} = ssl:send(SslS, "Hello world").
%%--------------------------------------------------------------------
close_transport_accept(doc) ->
@@ -933,8 +1095,7 @@ tcp_connect(Config) when is_list(Config) ->
{Server, {error, Error}} ->
test_server:format("Error ~p", [Error])
end
- end,
- ssl_test_lib:close(Server).
+ end.
dummy(_Socket) ->
@@ -943,6 +1104,8 @@ dummy(_Socket) ->
exit(kill).
%%--------------------------------------------------------------------
+ipv6() ->
+ [{require, ipv6_hosts}].
ipv6(doc) ->
["Test ipv6."];
ipv6(suite) ->
@@ -950,7 +1113,7 @@ ipv6(suite) ->
ipv6(Config) when is_list(Config) ->
{ok, Hostname0} = inet:gethostname(),
- case lists:member(list_to_atom(Hostname0), ?config(ipv6_hosts, Config)) of
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
true ->
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -1024,13 +1187,13 @@ ecertfile(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
- {options, ServerBadOpts}]),
+ {options, ServerBadOpts}]),
Port = ssl_test_lib:inet_port(Server),
Client =
ssl_test_lib:start_client_error([{node, ClientNode},
- {port, Port}, {host, Hostname},
+ {port, Port}, {host, Hostname},
{from, self()},
{options, ClientOpts}]),
@@ -1039,13 +1202,13 @@ ecertfile(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-ecacertfile(doc) ->
+ecacertfile(doc) ->
["Test what happens with an invalid cacert file"];
-ecacertfile(suite) ->
+ecacertfile(suite) ->
[];
-ecacertfile(Config) when is_list(Config) ->
+ecacertfile(Config) when is_list(Config) ->
ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)],
ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)],
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1120,7 +1283,6 @@ eoptions(Config) when is_list(Config) ->
{verify_fun, function},
{fail_if_no_peer_cert, 0},
{verify_client_once, 1},
- {validate_extensions_fun, function},
{depth, four},
{certfile, 'cert.pem'},
{keyfile,'key.pem' },
@@ -1272,20 +1434,142 @@ shutdown_error(Config) when is_list(Config) ->
ok = ssl:close(Listen),
{error, closed} = ssl:shutdown(Listen, read_write).
-%%--------------------------------------------------------------------
-ciphers(doc) ->
- [""];
+%%-------------------------------------------------------------------
+ciphers_rsa_signed_certs(doc) ->
+ ["Test all rsa ssl cipher suites in highest support ssl/tls version"];
+
+ciphers_rsa_signed_certs(suite) ->
+ [];
+
+ciphers_rsa_signed_certs(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl_test_lib:rsa_suites(),
+ test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, rsa).
+
+ciphers_rsa_signed_certs_ssl3(doc) ->
+ ["Test all rsa ssl cipher suites in ssl3"];
+
+ciphers_rsa_signed_certs_ssl3(suite) ->
+ [];
+
+ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version({3,0}),
+
+ Ciphers = ssl_test_lib:rsa_suites(),
+ test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, rsa).
+
+ciphers_rsa_signed_certs_openssl_names(doc) ->
+ ["Test all rsa ssl cipher suites in highest support ssl/tls version"];
-ciphers(suite) ->
+ciphers_rsa_signed_certs_openssl_names(suite) ->
[];
-ciphers(Config) when is_list(Config) ->
+ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Ciphers = ssl_test_lib:openssl_rsa_suites(),
+ test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, rsa).
- Ciphers = ssl:cipher_suites(),
+
+ciphers_rsa_signed_certs_openssl_names_ssl3(doc) ->
+ ["Test all dsa ssl cipher suites in ssl3"];
+
+ciphers_rsa_signed_certs_openssl_names_ssl3(suite) ->
+ [];
+
+ciphers_rsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) ->
+ Version = ssl_record:protocol_version({3,0}),
+ Ciphers = ssl_test_lib:openssl_rsa_suites(),
+ run_suites(Ciphers, Version, Config, rsa).
+
+
+ciphers_dsa_signed_certs(doc) ->
+ ["Test all dsa ssl cipher suites in highest support ssl/tls version"];
+
+ciphers_dsa_signed_certs(suite) ->
+ [];
+
+ciphers_dsa_signed_certs(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl_test_lib:dsa_suites(),
+ test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, dsa).
+
+ciphers_dsa_signed_certs_ssl3(doc) ->
+ ["Test all dsa ssl cipher suites in ssl3"];
+
+ciphers_dsa_signed_certs_ssl3(suite) ->
+ [];
+
+ciphers_dsa_signed_certs_ssl3(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version({3,0}),
+
+ Ciphers = ssl_test_lib:dsa_suites(),
+ test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, dsa).
+
+
+ciphers_dsa_signed_certs_openssl_names(doc) ->
+ ["Test all dsa ssl cipher suites in highest support ssl/tls version"];
+
+ciphers_dsa_signed_certs_openssl_names(suite) ->
+ [];
+
+ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl_test_lib:openssl_dsa_suites(),
+ test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]),
+ run_suites(Ciphers, Version, Config, dsa).
+
+
+ciphers_dsa_signed_certs_openssl_names_ssl3(doc) ->
+ ["Test all dsa ssl cipher suites in ssl3"];
+
+ciphers_dsa_signed_certs_openssl_names_ssl3(suite) ->
+ [];
+
+ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) ->
+ Version = ssl_record:protocol_version({3,0}),
+ Ciphers = ssl_test_lib:openssl_dsa_suites(),
+ run_suites(Ciphers, Version, Config, dsa).
+
+anonymous_cipher_suites(doc)->
+ ["Test the anonymous ciphersuites"];
+anonymous_cipher_suites(suite) ->
+ [];
+anonymous_cipher_suites(Config) when is_list(Config) ->
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Ciphers = ssl_test_lib:anonymous_suites(),
+ run_suites(Ciphers, Version, Config, anonymous).
+
+run_suites(Ciphers, Version, Config, Type) ->
+ {ClientOpts, ServerOpts} =
+ case Type of
+ rsa ->
+ {?config(client_opts, Config),
+ ?config(server_opts, Config)};
+ dsa ->
+ {?config(client_opts, Config),
+ ?config(server_dsa_opts, Config)};
+ anonymous ->
+ %% No certs in opts!
+ {?config(client_opts, Config),
+ ?config(server_anon, Config)}
+ end,
+
Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config) end,
+ cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
Ciphers),
case lists:flatten(Result) of
[] ->
@@ -1294,49 +1578,73 @@ ciphers(Config) when is_list(Config) ->
test_server:format("Cipher suite errors: ~p~n", [Error]),
test_server:fail(cipher_suite_failed_see_test_case_log)
end.
-
-cipher(CipherSuite, Version, Config) ->
- process_flag(trap_exit, true),
+
+erlang_cipher_suite(Suite) when is_list(Suite)->
+ ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite));
+erlang_cipher_suite(Suite) ->
+ Suite.
+
+cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
+ %% process_flag(trap_exit, true),
test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlangCipherSuite = erlang_cipher_suite(CipherSuite),
+
+ ConnectionInfo = {ok, {Version, ErlangCipherSuite}},
+
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, connection_info_result, []}},
+ {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
- {mfa, {?MODULE, connection_info_result, []}},
+ {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
{options,
[{ciphers,[CipherSuite]} |
ClientOpts]}]),
-
- ServerMsg = ClientMsg = {ok, {Version, CipherSuite}},
-
- Result = ssl_test_lib:wait_for_result(Server, ServerMsg,
- Client, ClientMsg),
+
+ Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok),
+
ssl_test_lib:close(Server),
- receive
- {'EXIT', Server, normal} ->
- ok
- end,
ssl_test_lib:close(Client),
- receive
- {'EXIT', Client, normal} ->
- ok
- end,
- process_flag(trap_exit, false),
+
case Result of
ok ->
[];
Error ->
- [{CipherSuite, Error}]
+ [{ErlangCipherSuite, Error}]
end.
%%--------------------------------------------------------------------
+default_reject_anonymous(doc)->
+ ["Test that by default anonymous cipher suites are rejected "];
+default_reject_anonymous(suite) ->
+ [];
+default_reject_anonymous(Config) when is_list(Config) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ [Cipher | _] = ssl_test_lib:anonymous_suites(),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options,
+ [{ciphers,[Cipher]} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, "insufficient security"},
+ Client, {error, "insufficient security"}).
+
+%%--------------------------------------------------------------------
reuse_session(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -1344,7 +1652,6 @@ reuse_session(suite) ->
[];
reuse_session(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1352,13 +1659,13 @@ reuse_session(Config) when is_list(Config) ->
Server =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, session_info_result, []}},
- {options, ServerOpts}]),
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Client0 =
ssl_test_lib:start_client([{node, ClientNode},
{port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
+ {mfa, {ssl_test_lib, no_result, []}},
{from, self()}, {options, ClientOpts}]),
SessionInfo =
receive
@@ -1366,16 +1673,16 @@ reuse_session(Config) when is_list(Config) ->
Info
end,
- Server ! listen,
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
test_server:sleep(?SLEEP),
Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {?MODULE, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
receive
{Client1, SessionInfo} ->
ok;
@@ -1385,10 +1692,10 @@ reuse_session(Config) when is_list(Config) ->
test_server:fail(session_not_reused)
end,
- Server ! listen,
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
Client2 =
- ssl_test_lib:start_client([{node, ClientNode},
+ ssl_test_lib:start_client([{node, ClientNode},
{port, Port}, {host, Hostname},
{mfa, {?MODULE, session_info_result, []}},
{from, self()}, {options, [{reuse_sessions, false}
@@ -1402,10 +1709,6 @@ reuse_session(Config) when is_list(Config) ->
end,
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client0),
- ssl_test_lib:close(Client1),
- ssl_test_lib:close(Client2),
-
Server1 =
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -1417,7 +1720,7 @@ reuse_session(Config) when is_list(Config) ->
Client3 =
ssl_test_lib:start_client([{node, ClientNode},
{port, Port1}, {host, Hostname},
- {mfa, {?MODULE, session_info_result, []}},
+ {mfa, {ssl_test_lib, no_result, []}},
{from, self()}, {options, ClientOpts}]),
SessionInfo1 =
@@ -1426,7 +1729,7 @@ reuse_session(Config) when is_list(Config) ->
Info1
end,
- Server1 ! listen,
+ Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
test_server:sleep(?SLEEP),
@@ -1442,14 +1745,16 @@ reuse_session(Config) when is_list(Config) ->
test_server:fail(
session_reused_when_session_reuse_disabled_by_server);
{Client4, _Other} ->
+ test_server:format("OTHER: ~p ~n", [_Other]),
ok
end,
-
+
ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2),
ssl_test_lib:close(Client3),
- ssl_test_lib:close(Client4),
- process_flag(trap_exit, false).
-
+ ssl_test_lib:close(Client4).
session_info_result(Socket) ->
ssl:session_info(Socket).
@@ -1462,7 +1767,6 @@ reuse_session_expired(suite) ->
[];
reuse_session_expired(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -1483,8 +1787,8 @@ reuse_session_expired(Config) when is_list(Config) ->
{Server, Info} ->
Info
end,
-
- Server ! listen,
+
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
test_server:sleep(?SLEEP),
@@ -1504,7 +1808,7 @@ reuse_session_expired(Config) when is_list(Config) ->
end,
Server ! listen,
-
+
%% Make sure session is unregistered due to expiration
test_server:sleep((?EXPIRE+1) * 1000),
@@ -1519,12 +1823,12 @@ reuse_session_expired(Config) when is_list(Config) ->
{Client2, _} ->
ok
end,
-
+ process_flag(trap_exit, false),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client0),
ssl_test_lib:close(Client1),
- ssl_test_lib:close(Client2),
- process_flag(trap_exit, false).
+ ssl_test_lib:close(Client2).
+
%%--------------------------------------------------------------------
server_does_not_want_to_reuse_session(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -1557,10 +1861,11 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
Info
end,
- Server ! listen,
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
%% Make sure session is registered
test_server:sleep(?SLEEP),
+ ssl_test_lib:close(Client0),
Client1 =
ssl_test_lib:start_client([{node, ClientNode},
@@ -1573,11 +1878,9 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
{Client1, _Other} ->
ok
end,
-
+
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client0),
- ssl_test_lib:close(Client1),
- process_flag(trap_exit, false).
+ ssl_test_lib:close(Client1).
%%--------------------------------------------------------------------
@@ -1744,6 +2047,7 @@ server_verify_none_active_once(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
%%--------------------------------------------------------------------
server_verify_client_once_passive(doc) ->
@@ -1771,7 +2075,7 @@ server_verify_client_once_passive(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client0, ok),
ssl_test_lib:close(Client0),
- Server ! listen,
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
@@ -1809,7 +2113,7 @@ server_verify_client_once_active(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client0, ok),
ssl_test_lib:close(Client0),
- Server ! listen,
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
@@ -1820,7 +2124,6 @@ server_verify_client_once_active(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client1).
-
%%--------------------------------------------------------------------
server_verify_client_once_active_once(doc) ->
@@ -1848,18 +2151,17 @@ server_verify_client_once_active_once(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client0, ok),
ssl_test_lib:close(Client0),
- Server ! listen,
-
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, result_ok, []}},
- {options, [{active, once} | ClientOpts]}]),
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, result_ok, []}},
+ {options, [{active, once} | ClientOpts]}]),
ssl_test_lib:check_result(Client1, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client1).
-
+
%%--------------------------------------------------------------------
server_verify_no_cacerts(doc) ->
@@ -1867,9 +2169,8 @@ server_verify_no_cacerts(doc) ->
server_verify_no_cacerts(suite) ->
[];
-
server_verify_no_cacerts(Config) when is_list(Config) ->
- ServerOpts = ServerOpts = ?config(server_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
@@ -1933,9 +2234,7 @@ server_require_peer_cert_fail(Config) when is_list(Config) ->
{options, [{active, false} | BadClientOpts]}]),
ssl_test_lib:check_result(Server, {error, esslaccept},
- Client, {error, esslconnect}),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ Client, {error, esslconnect}).
%%--------------------------------------------------------------------
@@ -2017,14 +2316,7 @@ client_verify_none_active_once(Config) when is_list(Config) ->
{mfa, {?MODULE, send_recv_result_active_once, []}},
{options, [{active, once} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
- %% TODO: send message to test process to make sure
- %% verifyfun has beeen run as it has the same behavior as
- %% the default fun
- VerifyFun = fun([{bad_cert, unknown_ca}]) ->
- true;
- (_) ->
- false
- end,
+
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
{from, self()},
@@ -2032,16 +2324,13 @@ client_verify_none_active_once(Config) when is_list(Config) ->
send_recv_result_active_once,
[]}},
{options, [{active, once},
- {verify, verify_none},
- {verify_fun, VerifyFun}
+ {verify, verify_none}
| ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-
-
%%--------------------------------------------------------------------
client_renegotiate(doc) ->
["Test ssl:renegotiate/1 on client."];
@@ -2050,7 +2339,6 @@ client_renegotiate(suite) ->
[];
client_renegotiate(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -2073,11 +2361,9 @@ client_renegotiate(Config) when is_list(Config) ->
{options, [{reuse_sessions, false} | ClientOpts]}]),
ssl_test_lib:check_result(Client, ok, Server, ok),
-
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ ssl_test_lib:close(Client).
+
%%--------------------------------------------------------------------
server_renegotiate(doc) ->
["Test ssl:renegotiate/1 on server."];
@@ -2086,7 +2372,6 @@ server_renegotiate(suite) ->
[];
server_renegotiate(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -2109,8 +2394,7 @@ server_renegotiate(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- ok.
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
client_renegotiate_reused_session(doc) ->
@@ -2120,7 +2404,6 @@ client_renegotiate_reused_session(suite) ->
[];
client_renegotiate_reused_session(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -2143,11 +2426,8 @@ client_renegotiate_reused_session(Config) when is_list(Config) ->
{options, [{reuse_sessions, true} | ClientOpts]}]),
ssl_test_lib:check_result(Client, ok, Server, ok),
-
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
server_renegotiate_reused_session(doc) ->
["Test ssl:renegotiate/1 on server when the ssl session will be reused."];
@@ -2156,7 +2436,6 @@ server_renegotiate_reused_session(suite) ->
[];
server_renegotiate_reused_session(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -2179,9 +2458,7 @@ server_renegotiate_reused_session(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- ok.
-
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
client_no_wrap_sequence_number(doc) ->
["Test that erlang client will renegotiate session when",
@@ -2193,7 +2470,6 @@ client_no_wrap_sequence_number(suite) ->
[];
client_no_wrap_sequence_number(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -2218,11 +2494,8 @@ client_no_wrap_sequence_number(Config) when is_list(Config) ->
{renegotiate_at, N} | ClientOpts]}]),
ssl_test_lib:check_result(Client, ok),
-
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
server_no_wrap_sequence_number(doc) ->
["Test that erlang server will renegotiate session when",
@@ -2234,7 +2507,6 @@ server_no_wrap_sequence_number(suite) ->
[];
server_no_wrap_sequence_number(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
ClientOpts = ?config(client_opts, Config),
@@ -2258,45 +2530,160 @@ server_no_wrap_sequence_number(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok),
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- ok.
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+extended_key_usage_verify_peer(doc) ->
+ ["Test cert that has a critical extended_key_usage extension in verify_peer mode"];
+
+extended_key_usage_verify_peer(suite) ->
+ [];
+
+extended_key_usage_verify_peer(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = public_key:pem_entry_decode(KeyEntry),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"),
+ [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
+ ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp),
+ ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']},
+ ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate,
+ ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions =
+ [ServerExtKeyUsageExt |
+ ServerExtensions]},
+ NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"),
+ [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
+ ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
+ ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']},
+ ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
+ ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions =
+ [ClientExtKeyUsageExt |
+ ClientExtensions]},
+ NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | NewClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
-extended_key_usage(doc) ->
- ["Test cert that has a critical extended_key_usage extension"];
+extended_key_usage_verify_none(doc) ->
+ ["Test cert that has a critical extended_key_usage extension in verify_none mode"];
-extended_key_usage(suite) ->
+extended_key_usage_verify_none(suite) ->
[];
-extended_key_usage(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
+extended_key_usage_verify_none(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = public_key:pem_entry_decode(KeyEntry),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join(PrivDir, "server/new_cert.pem"),
+ [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
+ ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp),
+ ServerExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']},
+ ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate,
+ ServerExtensions = ServerOTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewServerOTPTbsCert = ServerOTPTbsCert#'OTPTBSCertificate'{extensions =
+ [ServerExtKeyUsageExt |
+ ServerExtensions]},
+ NewServerDerCert = public_key:pkix_sign(NewServerOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join(PrivDir, "client/new_cert.pem"),
+ [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
+ ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
+ ClientExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-clientAuth']},
+ ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
+ ClientExtensions = ClientOTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewClientOTPTbsCert = ClientOTPTbsCert#'OTPTBSCertificate'{extensions =
+ [ClientExtKeyUsageExt |
+ ClientExtensions]},
+ NewClientDerCert = public_key:pkix_sign(NewClientOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{verify, verify_none} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{verify, verify_none} | NewClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+no_authority_key_identifier(doc) ->
+ ["Test cert that does not have authorityKeyIdentifier extension"
+ " but are present in trusted certs db."];
+
+no_authority_key_identifier(suite) ->
+ [];
+no_authority_key_identifier(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
ServerOpts = ?config(server_opts, Config),
PrivDir = ?config(priv_dir, Config),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- NewCertFile = filename:join(PrivDir, "cert.pem"),
-
- {ok, [{cert, DerCert, _}]} = public_key:pem_to_der(CertFile),
-
- {ok, [KeyInfo]} = public_key:pem_to_der(KeyFile),
-
- {ok, Key} = public_key:decode_private_key(KeyInfo),
-
- {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp),
-
- ExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']},
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = public_key:pem_entry_decode(KeyEntry),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ NewCertFile = filename:join(PrivDir, "server/new_cert.pem"),
+ [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ OTPCert = public_key:pkix_decode_cert(DerCert, otp),
OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate,
-
Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewExtensions = delete_authority_key_extension(Extensions, []),
+ NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = NewExtensions},
- NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = [ExtKeyUsageExt |Extensions]},
+ test_server:format("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]),
- NewDerCert = public_key:sign(NewOTPTbsCert, Key),
-
- public_key:der_to_pem(NewCertFile, [{cert, NewDerCert}]),
-
+ NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]),
NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)],
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
@@ -2310,45 +2697,666 @@ extended_key_usage(Config) when is_list(Config) ->
{host, Hostname},
{from, self()},
{mfa, {?MODULE, send_recv_result_active, []}},
- {options, ClientOpts}]),
+ {options, [{verify, verify_peer} | ClientOpts]}]),
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+delete_authority_key_extension([], Acc) ->
+ lists:reverse(Acc);
+delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest],
+ Acc) ->
+ delete_authority_key_extension(Rest, Acc);
+delete_authority_key_extension([Head | Rest], Acc) ->
+ delete_authority_key_extension(Rest, [Head | Acc]).
+
%%--------------------------------------------------------------------
-validate_extensions_fun(doc) ->
- ["Test that it is possible to specify a validate_extensions_fun"];
-validate_extensions_fun(suite) ->
+invalid_signature_server(doc) ->
+ ["Test server with invalid signature"];
+
+invalid_signature_server(suite) ->
[];
-validate_extensions_fun(Config) when is_list(Config) ->
+invalid_signature_server(Config) when is_list(Config) ->
ClientOpts = ?config(client_verification_opts, Config),
ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "server/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = public_key:pem_entry_decode(KeyEntry),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join(PrivDir, "server/invalid_cert.pem"),
+ [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
+ ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp),
+ ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate,
+ NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Fun = fun(Extensions, State, _, AccError) ->
- {Extensions, State, AccError}
- end,
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, NewServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, {error, "bad certificate"},
+ Client, {error,"bad certificate"}).
+
+%%--------------------------------------------------------------------
+
+invalid_signature_client(doc) ->
+ ["Test server with invalid signature"];
+
+invalid_signature_client(suite) ->
+ [];
+
+invalid_signature_client(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "client/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = public_key:pem_entry_decode(KeyEntry),
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join(PrivDir, "client/invalid_cert.pem"),
+ [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
+ ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
+ ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
+ NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
- {options, [{validate_extensions_fun, Fun},
- {verify, verify_peer} | ServerOpts]}]),
+ {options, [{verify, verify_peer} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, NewClientOpts}]),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, send_recv_result_active, []}},
- {options,[{validate_extensions_fun, Fun} | ClientOpts]}]),
+ tcp_delivery_workaround(Server, {error, "bad certificate"},
+ Client, {error,"bad certificate"}).
+
+tcp_delivery_workaround(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ {Client, {error,closed}} ->
+ test_server:format("client got close");
+ Unexpected ->
+ test_server:fail(Unexpected)
+ end;
+ {Client, ClientMsg} ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ Unexpected ->
+ test_server:fail(Unexpected)
+ end;
+ {Client, {error,closed}} ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ Unexpected ->
+ test_server:fail(Unexpected)
+ end;
+ {Server, {error,closed}} ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ {Client, {error,closed}} ->
+ test_server:format("client got close"),
+ ok;
+ Unexpected ->
+ test_server:fail(Unexpected)
+ end;
+ Unexpected ->
+ test_server:fail(Unexpected)
+ end.
+%%--------------------------------------------------------------------
+cert_expired(doc) ->
+ ["Test server with invalid signature"];
+
+cert_expired(suite) ->
+ [];
+
+cert_expired(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = public_key:pem_entry_decode(KeyEntry),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join(PrivDir, "server/expired_cert.pem"),
+ [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
+ OTPCert = public_key:pkix_decode_cert(DerCert, otp),
+ OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate,
+
+ {Year, Month, Day} = date(),
+ {Hours, Min, Sec} = time(),
+ NotBeforeStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-2,
+ two_digits_str(Month),
+ two_digits_str(Day),
+ two_digits_str(Hours),
+ two_digits_str(Min),
+ two_digits_str(Sec)])),
+ NotAfterStr = lists:flatten(io_lib:format("~p~s~s~s~s~sZ",[Year-1,
+ two_digits_str(Month),
+ two_digits_str(Day),
+ two_digits_str(Hours),
+ two_digits_str(Min),
+ two_digits_str(Sec)])),
+ NewValidity = {'Validity', {generalTime, NotBeforeStr}, {generalTime, NotAfterStr}},
+
+ test_server:format("Validity: ~p ~n NewValidity: ~p ~n",
+ [OTPTbsCert#'OTPTBSCertificate'.validity, NewValidity]),
+
+ NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{validity = NewValidity},
+ NewServerDerCert = public_key:pkix_sign(NewOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
- ssl_test_lib:check_result(Server, ok, Client, ok),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, NewServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, {error, "certificate expired"},
+ Client, {error, "certificate expired"}).
+
+two_digits_str(N) when N < 10 ->
+ lists:flatten(io_lib:format("0~p", [N]));
+two_digits_str(N) ->
+ lists:flatten(io_lib:format("~p", [N])).
+
+%%--------------------------------------------------------------------
+
+client_with_cert_cipher_suites_handshake(doc) ->
+ ["Test that client with a certificate without keyEncipherment usage "
+ " extension can connect to a server with restricted cipher suites "];
+
+client_with_cert_cipher_suites_handshake(suite) ->
+ [];
+
+client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts_digital_signature_only, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, [{active, true},
+ {ciphers, ssl_test_lib:rsa_non_signed_suites()}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, [{active, true}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+unknown_server_ca_fail(doc) ->
+ ["Test that the client fails if the ca is unknown in verify_peer mode"];
+unknown_server_ca_fail(suite) ->
+ [];
+unknown_server_ca_fail(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, [test_to_update_user_state | UserState]};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ no_result, []}},
+ {options,
+ [{verify, verify_peer},
+ {verify_fun, FunAndState}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error,"unknown ca"},
+ Client, {error, "unknown ca"}).
+
+%%--------------------------------------------------------------------
+unknown_server_ca_accept_verify_none(doc) ->
+ ["Test that the client succeds if the ca is unknown in verify_none mode"];
+unknown_server_ca_accept_verify_none(suite) ->
+ [];
+unknown_server_ca_accept_verify_none(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options,
+ [{verify, verify_none}| ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+unknown_server_ca_accept_verify_peer(doc) ->
+ ["Test that the client succeds if the ca is unknown in verify_peer mode"
+ " with a verify_fun that accepts the unknown ca error"];
+unknown_server_ca_accept_verify_peer(suite) ->
+ [];
+unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) ->
+ {valid, UserState};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options,
+ [{verify, verify_peer},
+ {verify_fun, FunAndState}| ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+unknown_server_ca_accept_backwardscompatibilty(doc) ->
+ ["Test that old style verify_funs will work"];
+unknown_server_ca_accept_backwardscompatibilty(suite) ->
+ [];
+unknown_server_ca_accept_backwardscompatibilty(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc;
+ (Other, Acc) -> [Other | Acc]
+ end,
+ VerifyFun =
+ fun(ErrorList) ->
+ case lists:foldl(AcceptBadCa, [], ErrorList) of
+ [] -> true;
+ [_|_] -> false
+ end
+ end,
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options,
+ [{verify, verify_peer},
+ {verify_fun, VerifyFun}| ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+der_input(doc) ->
+ ["Test to input certs and key as der"];
+
+der_input(suite) ->
+ [];
+
+der_input(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir, Config),
+ DHParamFile = filename:join(DataDir, "dHParam.pem"),
+
+ SeverVerifyOpts = ?config(server_verification_opts, Config),
+ {ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
+ SeverVerifyOpts]),
+ ClientVerifyOpts = ?config(client_verification_opts, Config),
+ {ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
+ ClientVerifyOpts]),
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
+ {dh, DHParams},
+ {cert, ServerCert}, {key, ServerKey}, {cacerts, ServerCaCerts}],
+ ClientOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
+ {dh, DHParams},
+ {cert, ClientCert}, {key, ClientKey}, {cacerts, ClientCaCerts}],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+der_input_opts(Opts) ->
+ Certfile = proplists:get_value(certfile, Opts),
+ CaCertsfile = proplists:get_value(cacertfile, Opts),
+ Keyfile = proplists:get_value(keyfile, Opts),
+ Dhfile = proplists:get_value(dhfile, Opts),
+ [{_, Cert, _}] = ssl_test_lib:pem_to_der(Certfile),
+ [{_, Key, _}] = ssl_test_lib:pem_to_der(Keyfile),
+ [{_, DHParams, _}] = ssl_test_lib:pem_to_der(Dhfile),
+ CaCerts =
+ lists:map(fun(Entry) ->
+ {_, CaCert, _} = Entry,
+ CaCert
+ end, ssl_test_lib:pem_to_der(CaCertsfile)),
+ {Cert, {rsa, Key}, CaCerts, DHParams}.
+
+%%--------------------------------------------------------------------
+%% different_ca_peer_sign(doc) ->
+%% ["Check that a CA can have a different signature algorithm than the peer cert."];
+
+%% different_ca_peer_sign(suite) ->
+%% [];
+
+%% different_ca_peer_sign(Config) when is_list(Config) ->
+%% ClientOpts = ?config(client_mix_opts, Config),
+%% ServerOpts = ?config(server_mix_verify_opts, Config),
+
+%% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+%% Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+%% {from, self()},
+%% {mfa, {?MODULE, send_recv_result_active_once, []}},
+%% {options, [{active, once},
+%% {verify, verify_peer} | ServerOpts]}]),
+%% Port = ssl_test_lib:inet_port(Server),
+
+%% Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+%% {host, Hostname},
+%% {from, self()},
+%% {mfa, {?MODULE,
+%% send_recv_result_active_once,
+%% []}},
+%% {options, [{active, once},
+%% {verify, verify_peer}
+%% | ClientOpts]}]),
+
+%% ssl_test_lib:check_result(Server, ok, Client, ok),
+%% ssl_test_lib:close(Server),
+%% ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+no_reuses_session_server_restart_new_cert(doc) ->
+ ["Check that a session is not reused if the server is restarted with a new cert."];
+
+no_reuses_session_server_restart_new_cert(suite) ->
+ [];
+
+no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
+
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ DsaServerOpts = ?config(server_dsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, DsaServerOpts}]),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ test_server:fail(session_reused_when_server_has_new_cert);
+ {Client1, _Other} ->
+ ok
+ end,
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+no_reuses_session_server_restart_new_cert_file(doc) ->
+ ["Check that a session is not reused if a server is restarted with a new "
+ "cert contained in a file with the same name as the old cert."];
+
+no_reuses_session_server_restart_new_cert_file(suite) ->
+ [];
+
+no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ DsaServerOpts = ?config(server_dsa_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ NewServerOpts = new_config(PrivDir, ServerOpts),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, NewServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ %% Make sure session is registered and we get
+ %% new file time stamp when calling new_config!
+ test_server:sleep(?SLEEP* 2),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+
+ NewServerOpts = new_config(PrivDir, DsaServerOpts),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, NewServerOpts}]),
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ test_server:fail(session_reused_when_server_has_new_cert);
+ {Client1, _Other} ->
+ ok
+ end,
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+reuseaddr(doc) ->
+ [""];
+
+reuseaddr(suite) ->
+ [];
+
+reuseaddr(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+ test_server:sleep(?SLEEP),
+ ssl_test_lib:close(Server),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server1, ok, Client1, ok),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+
+hibernate(doc) ->
+ ["Check that an SSL connection that is started with option "
+ "{hibernate_after, 1000} indeed hibernates after 1000ms of "
+ "inactivity"];
+
+hibernate(suite) ->
+ [];
+
+hibernate(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {Client, #sslsocket{pid=Pid}} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{hibernate_after, 1000}|ClientOpts]}]),
+
+ { current_function, { _M, _F, _A } } =
+ process_info(Pid, current_function),
+
+ timer:sleep(1100),
+
+ { current_function, { erlang, hibernate, 3} } =
+ process_info(Pid, current_function),
+
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
@@ -2393,7 +3401,27 @@ renegotiate_reuse_session(Socket, Data) ->
%% Make sure session is registerd
test_server:sleep(?SLEEP),
renegotiate(Socket, Data).
-
+
+new_config(PrivDir, ServerOpts0) ->
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts0),
+ CertFile = proplists:get_value(certfile, ServerOpts0),
+ KeyFile = proplists:get_value(keyfile, ServerOpts0),
+ NewCaCertFile = filename:join(PrivDir, "new_ca.pem"),
+ NewCertFile = filename:join(PrivDir, "new_cert.pem"),
+ NewKeyFile = filename:join(PrivDir, "new_key.pem"),
+ file:copy(CaCertFile, NewCaCertFile),
+ file:copy(CertFile, NewCertFile),
+ file:copy(KeyFile, NewKeyFile),
+ ServerOpts1 = proplists:delete(cacertfile, ServerOpts0),
+ ServerOpts2 = proplists:delete(certfile, ServerOpts1),
+ ServerOpts = proplists:delete(keyfile, ServerOpts2),
+
+ {ok, PEM} = file:read_file(NewCaCertFile),
+ test_server:format("CA file content: ~p~n", [public_key:pem_decode(PEM)]),
+
+ [{cacertfile, NewCaCertFile}, {certfile, NewCertFile},
+ {keyfile, NewKeyFile} | ServerOpts].
+
session_cache_process_list(doc) ->
["Test reuse of sessions (short handshake)"];
@@ -2411,128 +3439,34 @@ session_cache_process_mnesia(Config) when is_list(Config) ->
session_cache_process(mnesia,Config).
session_cache_process(Type,Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- setup_session_cb(Type),
+ reuse_session(Config).
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, session_info_result, []}},
- {options,
- [{session_cache_cb, ?MODULE}|
- ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
- SessionInfo =
- receive
- {Server, Info} ->
- Info
- end,
-
- Server ! listen,
-
- %% Make sure session is registered
- test_server:sleep(?SLEEP),
-
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {?MODULE, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
- receive
- {Client1, SessionInfo} ->
- ok;
- {Client1, Other} ->
- test_server:format("Expected: ~p, Unexpected: ~p~n",
- [SessionInfo, Other]),
- test_server:fail(session_not_reused)
- end,
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client0),
- ssl_test_lib:close(Client1),
-
- Server1 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, session_info_result, []}},
- {options,
- [{reuse_sessions, false} | ServerOpts]}]),
- Port1 = ssl_test_lib:inet_port(Server1),
-
- Client3 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port1}, {host, Hostname},
- {mfa, {?MODULE, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
- SessionInfo1 =
- receive
- {Server1, Info1} ->
- Info1
- end,
-
- Server1 ! listen,
-
- %% Make sure session is registered
- test_server:sleep(?SLEEP),
-
- Client4 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port1}, {host, Hostname},
- {mfa, {?MODULE, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
- receive
- {Client4, SessionInfo1} ->
- test_server:fail(
- session_reused_when_session_reuse_disabled_by_server);
- {Client4, _Other} ->
- ok
- end,
-
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client3),
- ssl_test_lib:close(Client4),
- process_flag(trap_exit, false).
-
-setup_session_cb(Type) ->
- ssl_test = ets:new(ssl_test,[named_table, set,public]),
- ets:insert(ssl_test, {type,Type}).
-
-session_cb() ->
- [{type,Type}] = ets:lookup(ssl_test, type),
- Type.
-
-init() ->
- io:format("~p~n",[?LINE]),
- case session_cb() of
+init([Type]) ->
+ ets:new(ssl_test, [named_table, public, set]),
+ ets:insert(ssl_test, {type, Type}),
+ case Type of
list ->
spawn(fun() -> session_loop([]) end);
mnesia ->
mnesia:start(),
- {atomic,ok} = mnesia:create_table(sess_cache, [])
+ {atomic,ok} = mnesia:create_table(sess_cache, []),
+ sess_cache
end.
+session_cb() ->
+ [{type, Type}] = ets:lookup(ssl_test, type),
+ Type.
+
terminate(Cache) ->
- io:format("~p~n",[?LINE]),
case session_cb() of
list ->
Cache ! terminate;
mnesia ->
- {atomic,ok} = mnesia:delete_table(sess_cache, [])
+ catch {atomic,ok} =
+ mnesia:delete_table(sess_cache)
end.
-lookup(Cache, Key) ->
- io:format("~p~n",[?LINE]),
+lookup(Cache, Key) ->
case session_cb() of
list ->
Cache ! {self(), lookup, Key},
@@ -2542,13 +3476,14 @@ lookup(Cache, Key) ->
mnesia:read(sess_cache,
Key, read)
end) of
- {atomic, [Session]} -> Session;
- _ -> undefined
+ {atomic, [{sess_cache, Key, Value}]} ->
+ Value;
+ _ ->
+ undefined
end
- end.
+ end.
update(Cache, Key, Value) ->
- io:format("~p~n",[?LINE]),
case session_cb() of
list ->
Cache ! {update, Key, Value};
@@ -2556,12 +3491,11 @@ update(Cache, Key, Value) ->
{atomic, ok} =
mnesia:transaction(fun() ->
mnesia:write(sess_cache,
- Key, Value)
+ {sess_cache, Key, Value}, write)
end)
end.
delete(Cache, Key) ->
- io:format("~p~n",[?LINE]),
case session_cb() of
list ->
Cache ! {delete, Key};
@@ -2573,7 +3507,6 @@ delete(Cache, Key) ->
end.
foldl(Fun, Acc, Cache) ->
- io:format("~p~n",[?LINE]),
case session_cb() of
list ->
Cache ! {self(),foldl,Fun,Acc},
@@ -2587,15 +3520,17 @@ foldl(Fun, Acc, Cache) ->
end.
select_session(Cache, PartialKey) ->
- io:format("~p~n",[?LINE]),
case session_cb() of
list ->
Cache ! {self(),select_session, PartialKey},
- receive {Cache, Res} -> Res end;
+ receive
+ {Cache, Res} ->
+ Res
+ end;
mnesia ->
Sel = fun() ->
mnesia:select(Cache,
- [{{{PartialKey,'$1'}, '$2'},
+ [{{sess_cache,{PartialKey,'$1'}, '$2'},
[],['$$']}])
end,
{atomic, Res} = mnesia:transaction(Sel),
@@ -2615,7 +3550,8 @@ session_loop(Sess) ->
end,
session_loop(Sess);
{update, Key, Value} ->
- session_loop([{Key,Value}|Sess]);
+ NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)],
+ session_loop(NewSess);
{delete, Key} ->
session_loop(lists:keydelete(Key,1,Sess));
{Pid,foldl,Fun,Acc} ->
@@ -2623,15 +3559,17 @@ session_loop(Sess) ->
Pid ! {self(), Res},
session_loop(Sess);
{Pid,select_session,PKey} ->
- Sel = fun({{Head, _},Session}, Acc) when Head =:= PKey ->
- [Session|Acc];
+ Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 ->
+ [[Id, Session]|Acc];
(_,Acc) ->
Acc
- end,
- Pid ! {self(), lists:foldl(Sel, [], Sess)},
+ end,
+ Sessions = lists:foldl(Sel, [], Sess),
+ Pid ! {self(), Sessions},
session_loop(Sess)
end.
+
erlang_ssl_receive(Socket, Data) ->
receive
{ssl, Socket, Data} ->
@@ -2642,4 +3580,3 @@ erlang_ssl_receive(Socket, Data) ->
after ?SLEEP * 3 ->
test_server:fail({did_not_get, Data})
end.
-
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
index a019e660e9..d22d5d2954 100644
--- a/lib/ssl/test/ssl_packet_SUITE.erl
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(BYTE(X), X:8/unsigned-big-integer).
-define(UINT16(X), X:16/unsigned-big-integer).
@@ -53,14 +53,18 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- crypto:start(),
- ssl:start(),
- Result =
- (catch make_certs:all(?config(data_dir, Config),
- ?config(priv_dir, Config))),
- test_server:format("Make certs ~p~n", [Result]),
- ssl_test_lib:cert_options(Config).
-
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ test_server:format("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
%% Config - [tuple()]
@@ -69,7 +73,7 @@ init_per_suite(Config) ->
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
ssl:stop(),
- crypto:stop().
+ application:stop(crypto).
%%--------------------------------------------------------------------
%% Function: init_per_testcase(TestCase, Config) -> Config
@@ -114,50 +118,56 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test that erlang:decode_packet/3 seems to be handled correctly."
- "We only use the most basic packet types in our tests as testing of"
- "the packet types are for inet to verify"
- ];
-
-all(suite) ->
- [packet_raw_passive_many_small,
- packet_0_passive_many_small, packet_1_passive_many_small,
- packet_2_passive_many_small, packet_4_passive_many_small,
- packet_raw_passive_some_big, packet_0_passive_some_big,
- packet_1_passive_some_big,
- packet_2_passive_some_big, packet_4_passive_some_big,
- packet_raw_active_once_many_small,
- packet_0_active_once_many_small, packet_1_active_once_many_small,
- packet_2_active_once_many_small, packet_4_active_once_many_small,
- packet_raw_active_once_some_big,
- packet_0_active_once_some_big, packet_1_active_once_some_big,
- packet_2_active_once_some_big, packet_4_active_once_some_big,
- packet_raw_active_many_small, packet_0_active_many_small,
- packet_1_active_many_small,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [packet_raw_passive_many_small,
+ packet_0_passive_many_small,
+ packet_1_passive_many_small,
+ packet_2_passive_many_small,
+ packet_4_passive_many_small,
+ packet_raw_passive_some_big, packet_0_passive_some_big,
+ packet_1_passive_some_big, packet_2_passive_some_big,
+ packet_4_passive_some_big,
+ packet_raw_active_once_many_small,
+ packet_0_active_once_many_small,
+ packet_1_active_once_many_small,
+ packet_2_active_once_many_small,
+ packet_4_active_once_many_small,
+ packet_raw_active_once_some_big,
+ packet_0_active_once_some_big,
+ packet_1_active_once_some_big,
+ packet_2_active_once_some_big,
+ packet_4_active_once_some_big,
+ packet_raw_active_many_small,
+ packet_0_active_many_small, packet_1_active_many_small,
packet_2_active_many_small, packet_4_active_many_small,
- packet_raw_active_some_big, packet_0_active_some_big,
- packet_1_active_some_big, packet_2_active_some_big,
- packet_4_active_some_big,
- packet_send_to_large,
+ packet_raw_active_some_big, packet_0_active_some_big,
+ packet_1_active_some_big, packet_2_active_some_big,
+ packet_4_active_some_big, packet_send_to_large,
packet_wait_passive, packet_wait_active,
packet_baddata_passive, packet_baddata_active,
packet_size_passive, packet_size_active,
- packet_cdr_decode,
- packet_http_decode,
- packet_http_decode_list,
- packet_http_bin_decode_multi,
- packet_line_decode,
- packet_asn1_decode,
- packet_tpkt_decode,
- %packet_fcgi_decode,
- packet_sunrm_decode,
- header_decode_one_byte,
- header_decode_two_bytes,
+ packet_cdr_decode, packet_cdr_decode_list,
+ packet_http_decode, packet_http_decode_list,
+ packet_http_bin_decode_multi, packet_http_error_passive,
+ packet_line_decode, packet_line_decode_list,
+ packet_asn1_decode, packet_asn1_decode_list,
+ packet_tpkt_decode, packet_tpkt_decode_list,
+ packet_sunrm_decode, packet_sunrm_decode_list,
+ header_decode_one_byte, header_decode_two_bytes,
header_decode_two_bytes_one_sent,
- header_decode_two_bytes_two_sent
+ header_decode_two_bytes_two_sent].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
- ].
%% Test cases starts here.
%%--------------------------------------------------------------------
@@ -1428,7 +1438,7 @@ packet_size_passive(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
packet_cdr_decode(doc) ->
- ["Test setting the packet option {packet, cdr}"];
+ ["Test setting the packet option {packet, cdr}, {mode, binary}"];
packet_cdr_decode(suite) ->
[];
packet_cdr_decode(Config) when is_list(Config) ->
@@ -1462,8 +1472,44 @@ packet_cdr_decode(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+packet_cdr_decode_list(doc) ->
+ ["Test setting the packet option {packet, cdr} {mode, list}"];
+packet_cdr_decode_list(suite) ->
+ [];
+packet_cdr_decode_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% A valid cdr packet
+ Data = [71,73,79,80,1,2,2,1,0,0,0,41,0,0,0,0,0,0,0,0,0,0,0,1,78,
+ 69,79,0,0,0,0,2,0,10,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,4,49],
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_packet_decode,
+ [Data]}},
+ {options, [{active, true}, list,
+ {packet, cdr}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_packet_decode,
+ [Data]}},
+ {options, [{active, true}, {packet, cdr},
+ list | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
packet_http_decode(doc) ->
- ["Test setting the packet option {packet, http} {mode, binary}"];
+ ["Test setting the packet option {packet, http} {mode, binary} "
+ "(Body will be binary http strings are lists)"];
packet_http_decode(suite) ->
[];
@@ -1484,7 +1530,7 @@ packet_http_decode(Config) when is_list(Config) ->
{from, self()},
{mfa, {?MODULE, server_http_decode,
[Response]}},
- {options, [{active, true}, binary,
+ {options, [{active, true},binary,
{packet, http} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
@@ -1493,7 +1539,7 @@ packet_http_decode(Config) when is_list(Config) ->
{from, self()},
{mfa, {?MODULE, client_http_decode,
[Request]}},
- {options, [{active, true}, binary,
+ {options, [{active, true}, binary,
{packet, http} |
ClientOpts]}]),
@@ -1547,7 +1593,8 @@ client_http_decode(Socket, HttpRequest) ->
%%--------------------------------------------------------------------
packet_http_decode_list(doc) ->
- ["Test setting the packet option {packet, http}, {mode, list}"];
+ ["Test setting the packet option {packet, http}, {mode, list}"
+ "(Body will be litst too)"];
packet_http_decode_list(suite) ->
[];
packet_http_decode_list(Config) when is_list(Config) ->
@@ -1694,9 +1741,74 @@ client_http_bin_decode(Socket, HttpRequest, Count) when Count > 0 ->
client_http_bin_decode(Socket, HttpRequest, Count - 1);
client_http_bin_decode(_, _, _) ->
ok.
+
+%%--------------------------------------------------------------------
+packet_http_error_passive(doc) ->
+ ["Test setting the packet option {packet, http}, {active, false}"
+ " with a incorrect http header." ];
+packet_http_error_passive(suite) ->
+ [];
+packet_http_error_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Request = "GET / HTTP/1.1\r\n"
+ "host: www.example.com\r\n"
+ "user-agent HttpTester\r\n"
+ "\r\n",
+ Response = "HTTP/1.1 200 OK\r\n"
+ "\r\n"
+ "Hello!",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_http_decode_error,
+ [Response]}},
+ {options, [{active, false}, binary,
+ {packet, http} |
+ ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_http_decode_list,
+ [Request]}},
+ {options, [{active, true}, list,
+ {packet, http} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+server_http_decode_error(Socket, HttpResponse) ->
+ assert_packet_opt(Socket, http),
+
+ {ok, {http_request, 'GET', _, {1,1}}} = ssl:recv(Socket, 0),
+
+ assert_packet_opt(Socket, http),
+
+ {ok, {http_header, _, 'Host', _, "www.example.com"}} = ssl:recv(Socket, 0),
+ assert_packet_opt(Socket, http),
+
+ {ok, {http_error, _}} = ssl:recv(Socket, 0),
+
+ assert_packet_opt(Socket, http),
+
+ {ok, http_eoh} = ssl:recv(Socket, 0),
+
+ assert_packet_opt(Socket, http),
+ ok = ssl:send(Socket, HttpResponse),
+ ok.
+
+
%%--------------------------------------------------------------------
packet_line_decode(doc) ->
- ["Test setting the packet option {packet, line}"];
+ ["Test setting the packet option {packet, line}, {mode, binary}"];
packet_line_decode(suite) ->
[];
packet_line_decode(Config) when is_list(Config) ->
@@ -1730,30 +1842,44 @@ packet_line_decode(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
-server_line_packet_decode(Socket, Lines) ->
- receive
- {ssl, Socket, <<"Line ends here.\n">>} -> ok;
- Other1 -> exit({?LINE, Other1})
- end,
- receive
- {ssl, Socket, <<"Now it is a new line.\n">>} -> ok;
- Other2 -> exit({?LINE, Other2})
- end,
- ok = ssl:send(Socket, Lines).
+packet_line_decode_list(doc) ->
+ ["Test setting the packet option {packet, line}, {mode, list}"];
+packet_line_decode_list(suite) ->
+ [];
+packet_line_decode_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:flatten(io_lib:format("Line ends here.~n"
+ "Now it is a new line.~n", [])),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ server_line_packet_decode,
+ [Data]}},
+ {options, [{active, true}, list,
+ {packet, line}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ client_line_packet_decode,
+ [Data]}},
+ {options, [{active, true},
+ {packet, line},
+ list | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
-client_line_packet_decode(Socket, Lines) ->
- <<P1:10/binary, P2/binary>> = Lines,
- ok = ssl:send(Socket, P1),
- ok = ssl:send(Socket, P2),
- receive
- {ssl, Socket, <<"Line ends here.\n">>} -> ok;
- Other1 -> exit({?LINE, Other1})
- end,
- receive
- {ssl, Socket, <<"Now it is a new line.\n">>} -> ok;
- Other2 -> exit({?LINE, Other2})
- end.
%%--------------------------------------------------------------------
@@ -1769,7 +1895,7 @@ packet_asn1_decode(Config) when is_list(Config) ->
File = proplists:get_value(certfile, ServerOpts),
%% A valid asn1 BER packet (DER is stricter BER)
- {ok,[{cert, Data, _}]} = public_key:pem_to_der(File),
+ [{'Certificate', Data, _}] = ssl_test_lib:pem_to_der(File),
Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
{from, self()},
@@ -1793,6 +1919,44 @@ packet_asn1_decode(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+packet_asn1_decode_list(doc) ->
+ ["Test setting the packet option {packet, asn1}"];
+packet_asn1_decode_list(suite) ->
+ [];
+packet_asn1_decode_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ File = proplists:get_value(certfile, ServerOpts),
+
+ %% A valid asn1 BER packet (DER is stricter BER)
+ [{'Certificate', BinData, _}] = ssl_test_lib:pem_to_der(File),
+
+ Data = binary_to_list(BinData),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_packet_decode,
+ [Data]}},
+ {options, [{active, true}, list,
+ {packet, asn1}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_packet_decode,
+ [Data]}},
+ {options, [{active, true}, {packet, asn1},
+ list | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
packet_tpkt_decode(doc) ->
["Test setting the packet option {packet, tpkt}"];
packet_tpkt_decode(suite) ->
@@ -1825,6 +1989,38 @@ packet_tpkt_decode(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+packet_tpkt_decode_list(doc) ->
+ ["Test setting the packet option {packet, tpkt}"];
+packet_tpkt_decode_list(suite) ->
+ [];
+packet_tpkt_decode_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = binary_to_list(list_to_binary(add_tpkt_header("TPKT data"))),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_packet_decode,
+ [Data]}},
+ {options, [{active, true}, list,
+ {packet, tpkt}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_packet_decode,
+ [Data]}},
+ {options, [{active, true}, {packet, tpkt},
+ list | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
@@ -1894,6 +2090,39 @@ packet_sunrm_decode(Config) when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_sunrm_decode_list(doc) ->
+ ["Test setting the packet option {packet, sunrm}"];
+packet_sunrm_decode_list(suite) ->
+ [];
+packet_sunrm_decode_list(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = binary_to_list(list_to_binary([<<11:32>>, "Hello world"])),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_packet_decode,
+ [Data]}},
+ {options, [{active, true}, list,
+ {packet, sunrm}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_packet_decode,
+ [Data]}},
+ {options, [{active, true}, {packet, sunrm},
+ list | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
header_decode_one_byte(doc) ->
@@ -2036,21 +2265,29 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
%% Internal functions
-send_raw(_,_, 0) ->
+send_raw(Socket,_, 0) ->
+ ssl:send(Socket, <<>>),
no_result_msg;
send_raw(Socket, Data, N) ->
ssl:send(Socket, Data),
send_raw(Socket, Data, N-1).
-passive_raw(_, _, 0) ->
+passive_raw(Socket, _, 0) ->
+ {error, timeout} = ssl:recv(Socket, 0, 500),
ok;
passive_raw(Socket, Data, N) ->
Length = length(Data),
{ok, Data} = ssl:recv(Socket, Length),
passive_raw(Socket, Data, N-1).
-passive_recv_packet(_, _, 0) ->
- ok;
+passive_recv_packet(Socket, _, 0) ->
+ case ssl:recv(Socket, 0) of
+ {ok, []} ->
+ {error, timeout} = ssl:recv(Socket, 0, 500),
+ ok;
+ Other ->
+ {other, Other, ssl:session_info(Socket), 0}
+ end;
passive_recv_packet(Socket, Data, N) ->
case ssl:recv(Socket, 0) of
{ok, Data} ->
@@ -2059,7 +2296,8 @@ passive_recv_packet(Socket, Data, N) ->
{other, Other, ssl:session_info(Socket), N}
end.
-send(_,_, 0) ->
+send(Socket,_, 0) ->
+ ssl:send(Socket, <<>>),
no_result_msg;
send(Socket, Data, N) ->
case ssl:send(Socket, [Data]) of
@@ -2073,6 +2311,7 @@ send_incomplete(Socket, Data, N) ->
send_incomplete(Socket, Data, N, <<>>).
send_incomplete(Socket, _Data, 0, Prev) ->
ssl:send(Socket, Prev),
+ ssl:send(Socket, [?uint32(0)]),
no_result_msg;
send_incomplete(Socket, Data, N, Prev) ->
Length = size(Data),
@@ -2101,8 +2340,13 @@ active_once_raw(Socket, Data, N, Acc) ->
end
end.
-active_once_packet(_,_, 0) ->
- ok;
+active_once_packet(Socket,_, 0) ->
+ receive
+ {ssl, Socket, []} ->
+ ok;
+ {ssl, Socket, Other} ->
+ {other, Other, ssl:session_info(Socket), 0}
+ end;
active_once_packet(Socket, Data, N) ->
receive
{ssl, Socket, Data} ->
@@ -2114,7 +2358,7 @@ active_once_packet(Socket, Data, N) ->
active_raw(Socket, Data, N) ->
active_raw(Socket, Data, N, []).
-active_raw(_, _, 0, _) ->
+active_raw(_Socket, _, 0, _) ->
ok;
active_raw(Socket, Data, N, Acc) ->
receive
@@ -2129,8 +2373,13 @@ active_raw(Socket, Data, N, Acc) ->
end
end.
-active_packet(_, _, 0) ->
- ok;
+active_packet(Socket, _, 0) ->
+ receive
+ {ssl, Socket, []} ->
+ ok;
+ Other ->
+ {other, Other, ssl:session_info(Socket), 0}
+ end;
active_packet(Socket, Data, N) ->
receive
{ssl, Socket, Data} ->
@@ -2154,8 +2403,14 @@ server_packet_decode(Socket, Packet) ->
end,
ok = ssl:send(Socket, Packet).
-client_packet_decode(Socket, Packet) ->
+client_packet_decode(Socket, Packet) when is_binary(Packet)->
<<P1:10/binary, P2/binary>> = Packet,
+ client_packet_decode(Socket, P1, P2, Packet);
+client_packet_decode(Socket, [Head | Tail] = Packet) ->
+ client_packet_decode(Socket, [Head], Tail, Packet).
+
+client_packet_decode(Socket, P1, P2, Packet) ->
+ test_server:format("Packet: ~p ~n", [Packet]),
ok = ssl:send(Socket, P1),
ok = ssl:send(Socket, P2),
receive
@@ -2175,7 +2430,7 @@ server_header_decode(Socket, Packet, Result) ->
end,
ok = ssl:send(Socket, Packet),
receive
- {ssl, Socket, Result} -> ok;
+ {ssl, Socket, Result} -> ok;
Other2 -> exit({?LINE, Other2})
end,
ok = ssl:send(Socket, Packet).
@@ -2191,6 +2446,44 @@ client_header_decode(Socket, Packet, Result) ->
{ssl, Socket, Result} -> ok;
Other2 -> exit({?LINE, Other2})
end.
+
+server_line_packet_decode(Socket, Packet) when is_binary(Packet) ->
+ [L1, L2] = string:tokens(binary_to_list(Packet), "\n"),
+ server_line_packet_decode(Socket, list_to_binary(L1 ++ "\n"), list_to_binary(L2 ++ "\n"), Packet);
+server_line_packet_decode(Socket, Packet) ->
+ [L1, L2] = string:tokens(Packet, "\n"),
+ server_line_packet_decode(Socket, L1 ++ "\n", L2 ++ "\n", Packet).
+
+server_line_packet_decode(Socket, L1, L2, Packet) ->
+ receive
+ {ssl, Socket, L1} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ receive
+ {ssl, Socket, L2} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ ok = ssl:send(Socket, Packet).
+
+client_line_packet_decode(Socket, Packet) when is_binary(Packet)->
+ <<P1:10/binary, P2/binary>> = Packet,
+ [L1, L2] = string:tokens(binary_to_list(Packet), "\n"),
+ client_line_packet_decode(Socket, P1, P2, list_to_binary(L1 ++ "\n"), list_to_binary(L2 ++ "\n"));
+client_line_packet_decode(Socket, [Head | Tail] = Packet) ->
+ [L1, L2] = string:tokens(Packet, "\n"),
+ client_line_packet_decode(Socket, [Head], Tail, L1 ++ "\n", L2 ++ "\n").
+
+client_line_packet_decode(Socket, P1, P2, L1, L2) ->
+ ok = ssl:send(Socket, P1),
+ ok = ssl:send(Socket, P2),
+ receive
+ {ssl, Socket, L1} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ receive
+ {ssl, Socket, L2} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end.
add_tpkt_header(Data) when is_binary(Data) ->
L = size(Data) + 4,
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index a0aa92bdf2..24e86b3913 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -22,7 +22,7 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(TIMEOUT, 600000).
@@ -37,11 +37,15 @@
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- crypto:start(),
- ssl:start(),
- make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)),
- ssl_test_lib:cert_options(Config).
-
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
%%--------------------------------------------------------------------
%% Function: end_per_suite(Config) -> _
%% Config - [tuple()]
@@ -50,7 +54,7 @@ init_per_suite(Config) ->
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
ssl:stop(),
- crypto:stop().
+ application:stop(crypto).
%%--------------------------------------------------------------------
%% Function: init_per_testcase(TestCase, Config) -> Config
@@ -95,24 +99,30 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test payload over ssl in all socket modes, active, active_once,"
- "and passive mode."];
-
-all(suite) ->
- [server_echos_passive_small, server_echos_active_once_small,
- server_echos_active_small,
- client_echos_passive_small, client_echos_active_once_small,
- client_echos_active_small,
- server_echos_passive_big, server_echos_active_once_big,
- server_echos_active_big,
- client_echos_passive_big, client_echos_active_once_big,
- client_echos_active_big,
- server_echos_passive_huge, server_echos_active_once_huge,
- server_echos_active_huge,
- client_echos_passive_huge, client_echos_active_once_huge,
- client_echos_active_huge
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [server_echos_passive_small,
+ server_echos_active_once_small,
+ server_echos_active_small, client_echos_passive_small,
+ client_echos_active_once_small,
+ client_echos_active_small, server_echos_passive_big,
+ server_echos_active_once_big, server_echos_active_big,
+ client_echos_passive_big, client_echos_active_once_big,
+ client_echos_active_big, server_echos_passive_huge,
+ server_echos_active_once_huge, server_echos_active_huge,
+ client_echos_passive_huge,
+ client_echos_active_once_huge, client_echos_active_huge].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Test cases starts here.
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
new file mode 100644
index 0000000000..a43b9ab586
--- /dev/null
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -0,0 +1,317 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.2
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_session_cache_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(SLEEP, 500).
+-define(TIMEOUT, 60000).
+-define(LONG_TIMEOUT, 600000).
+-behaviour(ssl_session_cache_api).
+
+%% For the session cache tests
+-export([init/1, terminate/1, lookup/2, update/3,
+ delete/2, foldl/3, select_session/2]).
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization before the whole suite
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%%--------------------------------------------------------------------
+init_per_suite(Config0) ->
+ Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+
+ %% make rsa certs using oppenssl
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ test_server:format("Make certs ~p~n", [Result]),
+
+ Config1 = ssl_test_lib:make_dsa_cert(Config0),
+ Config = ssl_test_lib:cert_options(Config1),
+ [{watchdog, Dog} | Config]
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: end_per_suite(Config) -> _
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after the whole suite
+%%--------------------------------------------------------------------
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, Config) -> Config
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%%
+%% Description: Initialization before each test case
+%%
+%% Note: This function is free to add any key/value pairs to the Config
+%% variable, but should NOT alter/remove any existing entries.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(session_cache_process_list, Config) ->
+ init_customized_session_cache(list, Config);
+
+init_per_testcase(session_cache_process_mnesia, Config) ->
+ mnesia:start(),
+ init_customized_session_cache(mnesia, Config);
+
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = test_server:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+init_customized_session_cache(Type, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = test_server:timetrap(?TIMEOUT),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_cb, ?MODULE),
+ application:set_env(ssl, session_cb_init_args, [Type]),
+ ssl:start(),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, Config) -> _
+%% Case - atom()
+%% Name of the test case that is about to be run.
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Cleanup after each test case
+%%--------------------------------------------------------------------
+end_per_testcase(session_cache_process_list, Config) ->
+ application:unset_env(ssl, session_cb),
+ end_per_testcase(default_action, Config);
+end_per_testcase(session_cache_process_mnesia, Config) ->
+ application:unset_env(ssl, session_cb),
+ application:unset_env(ssl, session_cb_init_args),
+ mnesia:kill(),
+ ssl:stop(),
+ ssl:start(),
+ end_per_testcase(default_action, Config);
+end_per_testcase(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [session_cache_process_list,
+ session_cache_process_mnesia].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+session_cache_process_list(doc) ->
+ ["Test reuse of sessions (short handshake)"];
+
+session_cache_process_list(suite) ->
+ [];
+session_cache_process_list(Config) when is_list(Config) ->
+ session_cache_process(list,Config).
+%%--------------------------------------------------------------------
+session_cache_process_mnesia(doc) ->
+ ["Test reuse of sessions (short handshake)"];
+
+session_cache_process_mnesia(suite) ->
+ [];
+session_cache_process_mnesia(Config) when is_list(Config) ->
+ session_cache_process(mnesia,Config).
+
+
+%%--------------------------------------------------------------------
+%%% Session cache API callbacks
+%%--------------------------------------------------------------------
+
+init([Type]) ->
+ ets:new(ssl_test, [named_table, public, set]),
+ ets:insert(ssl_test, {type, Type}),
+ case Type of
+ list ->
+ spawn(fun() -> session_loop([]) end);
+ mnesia ->
+ mnesia:start(),
+ {atomic,ok} = mnesia:create_table(sess_cache, []),
+ sess_cache
+ end.
+
+session_cb() ->
+ [{type, Type}] = ets:lookup(ssl_test, type),
+ Type.
+
+terminate(Cache) ->
+ case session_cb() of
+ list ->
+ Cache ! terminate;
+ mnesia ->
+ catch {atomic,ok} =
+ mnesia:delete_table(sess_cache)
+ end.
+
+lookup(Cache, Key) ->
+ case session_cb() of
+ list ->
+ Cache ! {self(), lookup, Key},
+ receive {Cache, Res} -> Res end;
+ mnesia ->
+ case mnesia:transaction(fun() ->
+ mnesia:read(sess_cache,
+ Key, read)
+ end) of
+ {atomic, [{sess_cache, Key, Value}]} ->
+ Value;
+ _ ->
+ undefined
+ end
+ end.
+
+update(Cache, Key, Value) ->
+ case session_cb() of
+ list ->
+ Cache ! {update, Key, Value};
+ mnesia ->
+ {atomic, ok} =
+ mnesia:transaction(fun() ->
+ mnesia:write(sess_cache,
+ {sess_cache, Key, Value}, write)
+ end)
+ end.
+
+delete(Cache, Key) ->
+ case session_cb() of
+ list ->
+ Cache ! {delete, Key};
+ mnesia ->
+ {atomic, ok} =
+ mnesia:transaction(fun() ->
+ mnesia:delete(sess_cache, Key)
+ end)
+ end.
+
+foldl(Fun, Acc, Cache) ->
+ case session_cb() of
+ list ->
+ Cache ! {self(),foldl,Fun,Acc},
+ receive {Cache, Res} -> Res end;
+ mnesia ->
+ Foldl = fun() ->
+ mnesia:foldl(Fun, Acc, sess_cache)
+ end,
+ {atomic, Res} = mnesia:transaction(Foldl),
+ Res
+ end.
+
+select_session(Cache, PartialKey) ->
+ case session_cb() of
+ list ->
+ Cache ! {self(),select_session, PartialKey},
+ receive
+ {Cache, Res} ->
+ Res
+ end;
+ mnesia ->
+ Sel = fun() ->
+ mnesia:select(Cache,
+ [{{sess_cache,{PartialKey,'$1'}, '$2'},
+ [],['$$']}])
+ end,
+ {atomic, Res} = mnesia:transaction(Sel),
+ Res
+ end.
+
+session_loop(Sess) ->
+ receive
+ terminate ->
+ ok;
+ {Pid, lookup, Key} ->
+ case lists:keysearch(Key,1,Sess) of
+ {value, {Key,Value}} ->
+ Pid ! {self(), Value};
+ _ ->
+ Pid ! {self(), undefined}
+ end,
+ session_loop(Sess);
+ {update, Key, Value} ->
+ NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)],
+ session_loop(NewSess);
+ {delete, Key} ->
+ session_loop(lists:keydelete(Key,1,Sess));
+ {Pid,foldl,Fun,Acc} ->
+ Res = lists:foldl(Fun, Acc,Sess),
+ Pid ! {self(), Res},
+ session_loop(Sess);
+ {Pid,select_session,PKey} ->
+ Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 ->
+ [[Id, Session]|Acc];
+ (_,Acc) ->
+ Acc
+ end,
+ Sessions = lists:foldl(Sel, [], Sess),
+ Pid ! {self(), Sessions},
+ session_loop(Sess)
+ end.
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+
+session_cache_process(_Type,Config) when is_list(Config) ->
+ ssl_basic_SUITE:reuse_session(Config).
diff --git a/lib/ssl/test/ssl_test_MACHINE.erl b/lib/ssl/test/ssl_test_MACHINE.erl
index e75f7079ed..e0ffa15d80 100644
--- a/lib/ssl/test/ssl_test_MACHINE.erl
+++ b/lib/ssl/test/ssl_test_MACHINE.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -60,10 +60,12 @@ many_conns_1() ->
%%
mk_ssl_cert_opts(_Config) ->
Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
- COpts = [{cacertfile, filename:join([Dir, "client", "cacerts.pem"])},
+ COpts = [{ssl_imp, old},
+ {cacertfile, filename:join([Dir, "client", "cacerts.pem"])},
{certfile, filename:join([Dir, "client", "cert.pem"])},
{keyfile, filename:join([Dir, "client", "key.pem"])}],
- SOpts = [{cacertfile, filename:join([Dir, "server", "cacerts.pem"])},
+ SOpts = [{ssl_imp, old},
+ {cacertfile, filename:join([Dir, "server", "cacerts.pem"])},
{certfile, filename:join([Dir, "server", "cert.pem"])},
{keyfile, filename:join([Dir, "server", "key.pem"])}],
{ok, {COpts, SOpts}}.
@@ -225,11 +227,13 @@ start_ssl(Nodes, Config) ->
ok.
do_start(Env) ->
+ application:start(crypto),
+ application:start(public_key),
application:load(ssl),
lists:foreach(
fun({Par, Val}) -> application:set_env(ssl, Par, Val) end, Env),
- application:start(ssl),
- application:start(crypto).
+ application:start(ssl).
+
%%
%% start_node(Name) -> {ok, Node}
@@ -542,7 +546,7 @@ get_active(St) ->
listen(St, LPort) ->
case St#st.protomod of
ssl ->
- ssl:listen(LPort, St#st.sockopts ++ St#st.sslopts);
+ ssl:listen(LPort, [{ssl_imp, old} | St#st.sockopts ++ St#st.sslopts]);
gen_tcp ->
gen_tcp:listen(LPort, St#st.sockopts)
end.
@@ -584,7 +588,8 @@ connect(St, Host, Port) ->
case St#st.protomod of
ssl ->
- case ssl:connect(Host, Port, St#st.sockopts ++ St#st.sslopts,
+ case ssl:connect(Host, Port,
+ [{ssl_imp, old} | St#st.sockopts ++ St#st.sslopts],
St#st.timeout) of
{ok, Sock} ->
{ok, LPort} = ssl:sockname(Sock),
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 46b6eb401d..40bbdf1dbd 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -81,14 +81,20 @@ run_server(ListenSocket, Opts) ->
no_result_msg ->
ok;
Msg ->
- test_server:format("Msg: ~p ~n", [Msg]),
+ test_server:format("Server Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg}
end,
- receive
+ receive
listen ->
run_server(ListenSocket, Opts);
+ {listen, MFA} ->
+ run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]);
close ->
- ok = rpc:call(Node, ssl, close, [AcceptSocket])
+ test_server:format("Server closing ~p ~n", [self()]),
+ Result = rpc:call(Node, ssl, close, [AcceptSocket], 500),
+ test_server:format("Result ~p ~n", [Result]);
+ {ssl_closed, _} ->
+ ok
end.
%%% To enable to test with s_client -reconnect
@@ -122,12 +128,14 @@ remove_close_msg(ReconnectTimes) ->
remove_close_msg(ReconnectTimes -1)
end.
-
start_client(Args) ->
- Result = spawn_link(?MODULE, run_client, [Args]),
+ Result = spawn_link(?MODULE, run_client, [lists:delete(return_socket, Args)]),
receive
- connected ->
- Result
+ { connected, Socket } ->
+ case lists:member(return_socket, Args) of
+ true -> { Result, Socket };
+ false -> Result
+ end
end.
run_client(Opts) ->
@@ -139,7 +147,7 @@ run_client(Opts) ->
test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
case rpc:call(Node, ssl, connect, [Host, Port, Options]) of
{ok, Socket} ->
- Pid ! connected,
+ Pid ! { connected, Socket },
test_server:format("Client: connected~n", []),
%% In specail cases we want to know the client port, it will
%% be indicated by sending {port, 0} in options list!
@@ -151,19 +159,30 @@ run_client(Opts) ->
no_result_msg ->
ok;
Msg ->
+ test_server:format("Client Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg}
end,
- receive
+ receive
close ->
- ok = rpc:call(Node, ssl, close, [Socket])
+ test_server:format("Client closing~n", []),
+ rpc:call(Node, ssl, close, [Socket]);
+ {ssl_closed, Socket} ->
+ ok
end;
{error, Reason} ->
- test_server:format("Client: connection failed: ~p ~n", [Reason]),
+ test_server:format("Client: connection failed: ~p ~n", [Reason]),
Pid ! {self(), {error, Reason}}
end.
close(Pid) ->
- Pid ! close.
+ test_server:format("Close ~p ~n", [Pid]),
+ Monitor = erlang:monitor(process, Pid),
+ Pid ! close,
+ receive
+ {'DOWN', Monitor, process, Pid, Reason} ->
+ erlang:demonitor(Monitor),
+ test_server:format("Pid: ~p down due to:~p ~n", [Pid, Reason])
+ end.
check_result(Server, ServerMsg, Client, ClientMsg) ->
receive
@@ -208,47 +227,27 @@ check_result(Pid, Msg) ->
test_server:fail(Reason)
end.
-check_result_ignore_renegotiation_reject(Pid, Msg) ->
- receive
- {Pid, fail_session_fatal_alert_during_renegotiation} ->
- test_server:comment("Server rejected old renegotiation"),
- ok;
- {ssl_error, _, esslconnect} ->
- test_server:comment("Server rejected old renegotiation"),
- ok;
- {Pid, Msg} ->
- ok;
- {Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
- check_result(Pid,Msg);
- Unexpected ->
- Reason = {{expected, {Pid, Msg}},
- {got, Unexpected}},
- test_server:fail(Reason)
- end.
-
-
wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
receive
{Server, ServerMsg} ->
receive
{Client, ClientMsg} ->
- ok;
- Unexpected ->
- Unexpected
+ ok
+ %% Unexpected ->
+ %% Unexpected
end;
{Client, ClientMsg} ->
receive
{Server, ServerMsg} ->
- ok;
- Unexpected ->
- Unexpected
+ ok
+ %% Unexpected ->
+ %% Unexpected
end;
{Port, {data,Debug}} when is_port(Port) ->
io:format("openssl ~s~n",[Debug]),
- wait_for_result(Server, ServerMsg, Client, ClientMsg);
- Unexpected ->
- Unexpected
+ wait_for_result(Server, ServerMsg, Client, ClientMsg)
+ %% Unexpected ->
+ %% Unexpected
end.
@@ -258,9 +257,9 @@ wait_for_result(Pid, Msg) ->
ok;
{Port, {data,Debug}} when is_port(Port) ->
io:format("openssl ~s~n",[Debug]),
- wait_for_result(Pid,Msg);
- Unexpected ->
- Unexpected
+ wait_for_result(Pid,Msg)
+ %% Unexpected ->
+ %% Unexpected
end.
cert_options(Config) ->
@@ -268,6 +267,8 @@ cert_options(Config) ->
"client", "cacerts.pem"]),
ClientCertFile = filename:join([?config(priv_dir, Config),
"client", "cert.pem"]),
+ ClientCertFileDigitalSignatureOnly = filename:join([?config(priv_dir, Config),
+ "client", "digital_signature_only_cert.pem"]),
ServerCaCertFile = filename:join([?config(priv_dir, Config),
"server", "cacerts.pem"]),
ServerCertFile = filename:join([?config(priv_dir, Config),
@@ -292,8 +293,13 @@ cert_options(Config) ->
{certfile, ClientCertFile},
{keyfile, ClientKeyFile},
{ssl_imp, new}]},
+ {client_verification_opts_digital_signature_only, [{cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFileDigitalSignatureOnly},
+ {keyfile, ClientKeyFile},
+ {ssl_imp, new}]},
{server_opts, [{ssl_imp, new},{reuseaddr, true},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]},
{server_verification_opts, [{ssl_imp, new},{reuseaddr, true},
{cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
@@ -318,6 +324,58 @@ cert_options(Config) ->
| Config].
+make_dsa_cert(Config) ->
+
+ {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, dsa, ""),
+ {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa, dsa, ""),
+ [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ServerCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {server_dsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile},
+ {verify, verify_peer}]},
+ {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
+ | Config].
+
+
+make_mix_cert(Config) ->
+ {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa,
+ rsa, "mix"),
+ {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa,
+ rsa, "mix"),
+ [{server_mix_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ServerCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {server_mix_verify_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile},
+ {verify, verify_peer}]},
+ {client_mix_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
+ | Config].
+
+make_cert_files(RoleStr, Config, Alg1, Alg2, Prefix) ->
+ Alg1Str = atom_to_list(Alg1),
+ Alg2Str = atom_to_list(Alg2),
+ CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, Alg1}]),
+ {Cert, CertKey} = erl_make_certs:make_cert([{key, Alg2}, {issuer, CaInfo}]),
+ CaCertFile = filename:join([?config(priv_dir, Config),
+ RoleStr, Prefix ++ Alg1Str ++ "_cacerts.pem"]),
+ CertFile = filename:join([?config(priv_dir, Config),
+ RoleStr, Prefix ++ Alg2Str ++ "_cert.pem"]),
+ KeyFile = filename:join([?config(priv_dir, Config),
+ RoleStr, Prefix ++ Alg2Str ++ "_key.pem"]),
+
+ der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
+ der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
+ der_to_pem(KeyFile, [CertKey]),
+ {CaCertFile, CertFile, KeyFile}.
+
+
start_upgrade_server(Args) ->
Result = spawn_link(?MODULE, run_upgrade_server, [Args]),
receive
@@ -355,10 +413,12 @@ run_upgrade_server(Opts) ->
end,
{Module, Function, Args} = proplists:get_value(mfa, Opts),
Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]),
+ test_server:format("Upgrade Server Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg},
receive
close ->
- ok = rpc:call(Node, ssl, close, [SslAcceptSocket])
+ test_server:format("Upgrade Server closing~n", []),
+ rpc:call(Node, ssl, close, [SslAcceptSocket])
end
catch error:{badmatch, Error} ->
Pid ! {self(), Error}
@@ -388,10 +448,12 @@ run_upgrade_client(Opts) ->
test_server:format("apply(~p, ~p, ~p)~n",
[Module, Function, [SslSocket | Args]]),
Msg = rpc:call(Node, Module, Function, [SslSocket | Args]),
+ test_server:format("Upgrade Client Msg: ~p ~n", [Msg]),
Pid ! {self(), Msg},
receive
close ->
- ok = rpc:call(Node, ssl, close, [SslSocket])
+ test_server:format("Upgrade Client closing~n", []),
+ rpc:call(Node, ssl, close, [SslSocket])
end.
start_upgrade_server_error(Args) ->
@@ -529,3 +591,82 @@ send_selected_port(Pid, 0, Socket) ->
Pid ! {self(), {port, NewPort}};
send_selected_port(_,_,_) ->
ok.
+
+rsa_suites() ->
+ lists:filter(fun({dhe_dss, _, _}) ->
+ false;
+ (_) ->
+ true
+ end,
+ ssl:cipher_suites()).
+
+rsa_non_signed_suites() ->
+ lists:filter(fun({rsa, _, _}) ->
+ true;
+ (_) ->
+ false
+ end,
+ ssl:cipher_suites()).
+
+dsa_suites() ->
+ lists:filter(fun({dhe_dss, _, _}) ->
+ true;
+ (_) ->
+ false
+ end,
+ ssl:cipher_suites()).
+
+
+openssl_rsa_suites() ->
+ Ciphers = ssl:cipher_suites(openssl),
+ lists:filter(fun(Str) ->
+ case re:run(Str,"DSS",[]) of
+ nomatch ->
+ true;
+ _ ->
+ false
+ end
+ end, Ciphers).
+
+openssl_dsa_suites() ->
+ Ciphers = ssl:cipher_suites(openssl),
+ lists:filter(fun(Str) ->
+ case re:run(Str,"DSS",[]) of
+ nomatch ->
+ false;
+ _ ->
+ true
+ end
+ end, Ciphers).
+
+anonymous_suites() ->
+ [{dh_anon, rc4_128, md5},
+ {dh_anon, des_cbc, sha},
+ {dh_anon, '3des_ede_cbc', sha},
+ {dh_anon, aes_128_cbc, sha},
+ {dh_anon, aes_256_cbc, sha}].
+
+pem_to_der(File) ->
+ {ok, PemBin} = file:read_file(File),
+ public_key:pem_decode(PemBin).
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
+
+cipher_result(Socket, Result) ->
+ Result = ssl:connection_info(Socket),
+ test_server:format("Successfull connect: ~p~n", [Result]),
+ %% Importante to send two packets here
+ %% to properly test "cipher state" handling
+ ssl:send(Socket, "Hello\n"),
+ receive
+ {ssl, Socket, "Hello\n"} ->
+ ssl:send(Socket, " world\n"),
+ receive
+ {ssl, Socket, " world\n"} ->
+ ok
+ end;
+ Other ->
+ {unexpected, Other}
+ end.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 03466aec6f..64a6a9eaf8 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,15 +24,15 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("test_server.hrl").
--include("test_server_line.hrl").
--include("ssl_pkix.hrl").
+-include_lib("common_test/include/ct.hrl").
-define(TIMEOUT, 120000).
+-define(LONG_TIMEOUT, 600000).
-define(SLEEP, 1000).
-define(OPENSSL_RENEGOTIATE, "r\n").
-define(OPENSSL_QUIT, "Q\n").
-define(OPENSSL_GARBAGE, "P\n").
+-define(EXPIRE, 10).
%% Test server callback functions
%%--------------------------------------------------------------------
@@ -44,18 +44,26 @@
%% Note: This function is free to add any key/value pairs to the Config
%% variable, but should NOT alter/remove any existing entries.
%%--------------------------------------------------------------------
-init_per_suite(Config) ->
+init_per_suite(Config0) ->
+ Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2),
case os:find_executable("openssl") of
false ->
{skip, "Openssl not found"};
_ ->
- crypto:start(),
- ssl:start(),
- Result =
- (catch make_certs:all(?config(data_dir, Config),
- ?config(priv_dir, Config))),
- test_server:format("Make certs ~p~n", [Result]),
- ssl_test_lib:cert_options(Config)
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ test_server:format("Make certs ~p~n", [Result]),
+ Config1 = ssl_test_lib:make_dsa_cert(Config0),
+ Config = ssl_test_lib:cert_options(Config1),
+ [{watchdog, Dog} | Config]
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
end.
%%--------------------------------------------------------------------
@@ -66,7 +74,7 @@ init_per_suite(Config) ->
%%--------------------------------------------------------------------
end_per_suite(_Config) ->
ssl:stop(),
- crypto:stop().
+ application:stop(crypto).
%%--------------------------------------------------------------------
%% Function: init_per_testcase(TestCase, Config) -> Config
@@ -81,6 +89,15 @@ end_per_suite(_Config) ->
%% variable, but should NOT alter/remove any existing entries.
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
+init_per_testcase(expired_session, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ ssl:start(),
+ [{watchdog, Dog} | Config];
+
init_per_testcase(TestCase, Config0) ->
Config = lists:keydelete(watchdog, 1, Config0),
Dog = ssl_test_lib:timetrap(?TIMEOUT),
@@ -103,14 +120,20 @@ special_init(_, Config) ->
%% A list of key/value pairs, holding the test case configuration.
%% Description: Cleanup after each test case
%%--------------------------------------------------------------------
-end_per_testcase(_TestCase, Config) ->
+end_per_testcase(reuse_session_expired, Config) ->
+ application:unset_env(ssl, session_lifetime),
+ end_per_testcase(default_action, Config);
+
+end_per_testcase(default_action, Config) ->
Dog = ?config(watchdog, Config),
case Dog of
undefined ->
ok;
_ ->
test_server:timetrap_cancel(Dog)
- end.
+ end;
+end_per_testcase(_, Config) ->
+ end_per_testcase(default_action, Config).
%%--------------------------------------------------------------------
%% Function: all(Clause) -> TestCases
@@ -120,30 +143,43 @@ end_per_testcase(_TestCase, Config) ->
%% Name of a test case.
%% Description: Returns a list of all test cases in this test suite
%%--------------------------------------------------------------------
-all(doc) ->
- ["Test erlangs ssl against openssl"];
+suite() -> [{ct_hooks,[ts_install_cth]}].
-all(suite) ->
- [erlang_client_openssl_server,
+all() ->
+ [erlang_client_openssl_server,
erlang_server_openssl_client,
+ tls1_erlang_client_openssl_server_dsa_cert,
+ tls1_erlang_server_openssl_client_dsa_cert,
+ ssl3_erlang_client_openssl_server_dsa_cert,
+ ssl3_erlang_server_openssl_client_dsa_cert,
erlang_server_openssl_client_reuse_session,
erlang_client_openssl_server_renegotiate,
erlang_client_openssl_server_no_wrap_sequence_number,
erlang_server_openssl_client_no_wrap_sequence_number,
erlang_client_openssl_server_no_server_ca_cert,
- ssl3_erlang_client_openssl_server,
+ ssl3_erlang_client_openssl_server,
ssl3_erlang_server_openssl_client,
ssl3_erlang_client_openssl_server_client_cert,
ssl3_erlang_server_openssl_client_client_cert,
ssl3_erlang_server_erlang_client_client_cert,
- tls1_erlang_client_openssl_server,
+ tls1_erlang_client_openssl_server,
tls1_erlang_server_openssl_client,
tls1_erlang_client_openssl_server_client_cert,
tls1_erlang_server_openssl_client_client_cert,
tls1_erlang_server_erlang_client_client_cert,
- ciphers,
- erlang_client_bad_openssl_server
- ].
+ ciphers_rsa_signed_certs, ciphers_dsa_signed_certs,
+ erlang_client_bad_openssl_server, expired_session,
+ ssl2_erlang_server_openssl_client].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% Test cases starts here.
%%--------------------------------------------------------------------
@@ -229,6 +265,185 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
+tls1_erlang_client_openssl_server_dsa_cert(doc) ->
+ ["Test erlang server with openssl client"];
+tls1_erlang_client_openssl_server_dsa_cert(suite) ->
+ [];
+tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_dsa_opts, Config),
+ ServerOpts = ?config(server_dsa_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1 -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+tls1_erlang_server_openssl_client_dsa_cert(doc) ->
+ ["Test erlang server with openssl client"];
+tls1_erlang_server_openssl_client_dsa_cert(suite) ->
+ [];
+tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_dsa_opts, Config),
+ ServerOpts = ?config(server_dsa_verify_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+ CaCertFile = proplists:get_value(cacertfile, ClientOpts),
+ CertFile = proplists:get_value(certfile, ClientOpts),
+ KeyFile = proplists:get_value(keyfile, ClientOpts),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -tls1 -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+ssl3_erlang_client_openssl_server_dsa_cert(doc) ->
+ ["Test erlang server with openssl client"];
+ssl3_erlang_client_openssl_server_dsa_cert(suite) ->
+ [];
+ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_dsa_opts, Config),
+ ServerOpts = ?config(server_dsa_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+ssl3_erlang_server_openssl_client_dsa_cert(doc) ->
+ ["Test erlang server with openssl client"];
+ssl3_erlang_server_openssl_client_dsa_cert(suite) ->
+ [];
+ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_dsa_opts, Config),
+ ServerOpts = ?config(server_dsa_verify_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+ CaCertFile = proplists:get_value(cacertfile, ClientOpts),
+ CertFile = proplists:get_value(certfile, ClientOpts),
+ KeyFile = proplists:get_value(keyfile, ClientOpts),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -ssl3 -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+
+
+%%--------------------------------------------------------------------
+
erlang_server_openssl_client_reuse_session(doc) ->
["Test erlang server with openssl client that reconnects with the"
"same session id, to test reusing of sessions."];
@@ -863,19 +1078,46 @@ tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
-ciphers(doc) ->
- [""];
+ciphers_rsa_signed_certs(doc) ->
+ ["Test cipher suites that uses rsa certs"];
+
+ciphers_rsa_signed_certs(suite) ->
+ [];
+
+ciphers_rsa_signed_certs(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl_test_lib:rsa_suites(),
+ run_suites(Ciphers, Version, Config, rsa).
+
+
+ciphers_dsa_signed_certs(doc) ->
+ ["Test cipher suites that uses dsa certs"];
-ciphers(suite) ->
+ciphers_dsa_signed_certs(suite) ->
[];
-ciphers(Config) when is_list(Config) ->
+ciphers_dsa_signed_certs(Config) when is_list(Config) ->
Version =
ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Ciphers = ssl:cipher_suites(),
+ Ciphers = ssl_test_lib:dsa_suites(),
+ run_suites(Ciphers, Version, Config, dsa).
+
+run_suites(Ciphers, Version, Config, Type) ->
+ {ClientOpts, ServerOpts} =
+ case Type of
+ rsa ->
+ {?config(client_opts, Config),
+ ?config(server_opts, Config)};
+ dsa ->
+ {?config(client_opts, Config),
+ ?config(server_dsa_opts, Config)}
+ end,
+
Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config) end,
+ cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
Ciphers),
case lists:flatten(Result) of
[] ->
@@ -884,12 +1126,10 @@ ciphers(Config) when is_list(Config) ->
test_server:format("Cipher suite errors: ~p~n", [Error]),
test_server:fail(cipher_suite_failed_see_test_case_log)
end.
-
-cipher(CipherSuite, Version, Config) ->
+
+cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, true),
test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
{ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Port = ssl_test_lib:inet_port(node()),
@@ -897,33 +1137,43 @@ cipher(CipherSuite, Version, Config) ->
KeyFile = proplists:get_value(keyfile, ServerOpts),
Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
-
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
+
test_server:format("openssl cmd: ~p~n", [Cmd]),
OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
wait_for_openssl_server(),
+ ConnectionInfo = {ok, {Version, CipherSuite}},
+
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {?MODULE, connection_info_result, []}},
- {options,
- [{ciphers,[CipherSuite]} |
- ClientOpts]}]),
-
- ClientMsg = {ok, {Version, CipherSuite}},
-
- Result = ssl_test_lib:wait_for_result(Client, ClientMsg),
+ {from, self()},
+ {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
+
+ port_command(OpenSslPort, "Hello\n"),
+
+ receive
+ {Port, {data, _}} when is_port(Port) ->
+ ok
+ after 500 ->
+ test_server:format("Time out on openssl port, check that"
+ " the messages Hello and world are received"
+ " during close of port" , []),
+ ok
+ end,
+
+ port_command(OpenSslPort, " world\n"),
+
+ Result = ssl_test_lib:wait_for_result(Client, ok),
close_port(OpenSslPort),
%% Clean close down!
ssl_test_lib:close(Client),
- receive
- {'EXIT', Client, normal} ->
- ok
- end,
Return = case Result of
ok ->
@@ -991,6 +1241,100 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
close_port(OpensslPort),
process_flag(trap_exit, false),
ok.
+
+%%--------------------------------------------------------------------
+
+expired_session(doc) ->
+ ["Test our ssl client handling of expired sessions. Will make"
+ "better code coverage of the ssl_manager module"];
+
+expired_session(suite) ->
+ [];
+
+expired_session(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ ssl_test_lib:close(Client0),
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ ssl_test_lib:close(Client1),
+ %% Make sure session is unregistered due to expiration
+ test_server:sleep((?EXPIRE+1) * 1000),
+
+ Client2 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ close_port(OpensslPort),
+ ssl_test_lib:close(Client2),
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
+ssl2_erlang_server_openssl_client(doc) ->
+ ["Test that ssl v2 clients are rejected"];
+ssl2_erlang_server_openssl_client(suite) ->
+ [];
+ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -ssl2 -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, {error,"protocol version"}),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+
%%--------------------------------------------------------------------
erlang_ssl_receive(Socket, Data) ->
@@ -1030,8 +1374,7 @@ delayed_send(Socket, [ErlData, OpenSslData]) ->
erlang_ssl_receive(Socket, OpenSslData).
close_port(Port) ->
- port_command(Port, ?OPENSSL_QUIT),
- %%catch port_command(Port, "quit\n"),
+ catch port_command(Port, ?OPENSSL_QUIT),
close_loop(Port, 500, false).
close_loop(Port, Time, SentClose) ->
@@ -1083,7 +1426,9 @@ wait_for_openssl_server() ->
check_sane_openssl_renegotaite(Config) ->
case os:cmd("openssl version") of
- "OpenSSL 0.9.8l" ++ _ ->
+ "OpenSSL 0.9.8" ++ _ ->
+ {skip, "Known renegotiation bug in OppenSSL"};
+ "OpenSSL 0.9.7" ++ _ ->
{skip, "Known renegotiation bug in OppenSSL"};
_ ->
Config
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index e3db7008e3..2f1edfa186 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1,56 +1 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-SSL_VSN = 3.11.1
-
-TICKETS = OTP-8588 \
- OTP-8568 \
- OTP-7049
-
-#TICKETS_3.11 = OTP-8517 \
-# OTP-7046 \
-# OTP-8557 \
-# OTP-8560 \
-# OTP-8545 \
-# OTP-8554
-
-#TICKETS_3.10.9 = OTP-8510
-
-#TICKETS_3.10.8 = OTP-8372 OTP-8441 OTP-8459
-#TICKETS_3.10.7 = OTP-8260 OTP-8218 OTP-8250
-
-#TICKETS_3.10.6 = OTP-8275
-
-#TICKETS_3.10.5 = OTP-8224 OTP-8244
-
-#TICKETS_3.10.4 = OTP-8137
-
-#TICKETS_3.10.3 = OTP-8011
-#TICKETS_3.10.2 = OTP-7963
-
-# TICKETS_3.10.1 = OTP-7878 \
-# OTP-7656 \
-# OTP-7870 \
-# OTP-7871
-
-# TICKETS_3.10 = OTP-7258 \
-# OTP-6894 \
-# OTP-7037 \
-# OTP-7039 \
-# OTP-7150
+SSL_VSN = 4.1.4
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile
index 13b9b2ff18..b558697d63 100644
--- a/lib/stdlib/doc/src/Makefile
+++ b/lib/stdlib/doc/src/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved online at http://www.erlang.org/.
-#
+#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
-#
+#
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
@@ -40,6 +40,7 @@ XML_REF3_FILES = \
array.xml \
base64.xml \
beam_lib.xml \
+ binary.xml \
c.xml \
calendar.xml \
dets.xml \
diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml
index 27308e02f3..adc411e272 100644
--- a/lib/stdlib/doc/src/beam_lib.xml
+++ b/lib/stdlib/doc/src/beam_lib.xml
@@ -341,6 +341,7 @@ chunkref() = chunkname() | chunkid()</code>
<v>Beam1 = Beam2 = beam()</v>
<v>Reason = {modules_different, Module1, Module2}</v>
<v>&nbsp;&nbsp;| {chunks_different, ChunkId}</v>
+ <v>&nbsp;&nbsp;| different_chunks</v>
<v>&nbsp;&nbsp;| Reason1 -- see info/1</v>
<v>&nbsp;Module1 = Module2 = atom()</v>
<v>&nbsp;ChunkId = chunkid()</v>
diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml
new file mode 100644
index 0000000000..c81023862e
--- /dev/null
+++ b/lib/stdlib/doc/src/binary.xml
@@ -0,0 +1,729 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2009</year>
+ <year>2010</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved on line at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>binary</title>
+ <prepared>Patrik Nyblom</prepared>
+ <responsible>Kenneth Lundin</responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2010-05-05</date>
+ <rev>A</rev>
+ <file>binary.xml</file>
+ </header>
+ <module>binary</module>
+ <modulesummary>Library for handling binary data</modulesummary>
+ <description>
+
+ <p>This module contains functions for manipulating byte-oriented
+ binaries. Although the majority of functions could be implemented
+ using bit-syntax, the functions in this library are highly
+ optimized and are expected to either execute faster or consume
+ less memory (or both) than a counterpart written in pure Erlang.</p>
+
+ <p>The module is implemented according to the EEP (Erlang Enhancement Proposal) 31.</p>
+
+ <note>
+ <p>
+ The library handles byte-oriented data. Bitstrings that are not
+ binaries (does not contain whole octets of bits) will result in a <c>badarg</c>
+ exception being thrown from any of the functions in this
+ module.
+ </p>
+ </note>
+
+
+ </description>
+ <section>
+ <title>DATA TYPES</title>
+ <code type="none">
+ cp()
+ - Opaque data-type representing a compiled search-pattern. Guaranteed to be a tuple()
+ to allow programs to distinguish it from non precompiled search patterns.
+ </code>
+ <code type="none">
+ part() = {Start,Length}
+ Start = int()
+ Length = int()
+ - A representaion of a part (or range) in a binary. Start is a
+ zero-based offset into a binary() and Length is the length of
+ that part. As input to functions in this module, a reverse
+ part specification is allowed, constructed with a negative
+ Length, so that the part of the binary begins at Start +
+ Length and is -Length long. This is useful for referencing the
+ last N bytes of a binary as {size(Binary), -N}. The functions
+ in this module always return part()'s with positive Length.
+ </code>
+ </section>
+ <funcs>
+ <func>
+ <name>at(Subject, Pos) -> int()</name>
+ <fsummary>Returns the byte at a specific position in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pos = int() >= 0</v>
+ </type>
+ <desc>
+
+ <p>Returns the byte at position <c>Pos</c> (zero-based) in the binary
+ <c>Subject</c> as an integer. If <c>Pos</c> &gt;= <c>byte_size(Subject)</c>,
+ a <c>badarg</c>
+ exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>bin_to_list(Subject) -> list()</name>
+ <fsummary>Convert a binary to a list of integers</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+ <p>The same as <c>bin_to_list(Subject,{0,byte_size(Subject)})</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>bin_to_list(Subject, PosLen) -> list()</name>
+ <fsummary>Convert a binary to a list of integers</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>PosLen = part()</v>
+ </type>
+ <desc>
+
+ <p>Converts <c>Subject</c> to a list of <c>int()</c>s, each representing
+ the value of one byte. The <c>part()</c> denotes which part of the
+ <c>binary()</c> to convert. Example:</p>
+
+<code>
+1> binary:bin_to_list(&lt;&lt;"erlang"&gt;&gt;,{1,3}).
+"rla"
+%% or [114,108,97] in list notation.
+</code>
+ <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception is raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name>bin_to_list(Subject, Pos, Len) -> list()</name>
+ <fsummary>Convert a binary to a list of integers</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pos = int()</v>
+ <v>Len = int()</v>
+ </type>
+ <desc>
+ <p>The same as<c> bin_to_list(Subject,{Pos,Len})</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>compile_pattern(Pattern) -> cp()</name>
+ <fsummary>Pre-compiles a binary search pattern</fsummary>
+ <type>
+ <v>Pattern = binary() | [ binary() ]</v>
+ </type>
+ <desc>
+
+ <p>Builds an internal structure representing a compilation of a
+ search-pattern, later to be used in the <seealso marker="#match-3">match/3</seealso>,
+ <seealso marker="#matches-3">matches/3</seealso>,
+ <seealso marker="#split-3">split/3</seealso> or
+ <seealso marker="#replace-4">replace/4</seealso>
+ functions. The <c>cp()</c> returned is guaranteed to be a
+ <c>tuple()</c> to allow programs to distinguish it from non
+ pre-compiled search patterns</p>
+
+ <p>When a list of binaries is given, it denotes a set of
+ alternative binaries to search for. I.e if
+ <c>[&lt;&lt;"functional"&gt;&gt;,&lt;&lt;"programming"&gt;&gt;]</c>
+ is given as <c>Pattern</c>, this
+ means "either <c>&lt;&lt;"functional"&gt;&gt;</c> or
+ <c>&lt;&lt;"programming"&gt;&gt;</c>". The pattern is a set of
+ alternatives; when only a single binary is given, the set has
+ only one element. The order of alternatives in a pattern is not significant.</p>
+
+ <p>The list of binaries used for search alternatives shall be flat and proper.</p>
+
+ <p>If <c>Pattern</c> is not a binary or a flat proper list of binaries with length &gt; 0,
+ a <c>badarg</c> exception will be raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>copy(Subject) -> binary()</name>
+ <fsummary>Creates a duplicate of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+ <p>The same as <c>copy(Subject, 1)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>copy(Subject,N) -> binary()</name>
+ <fsummary>Duplicates a binary N times and creates a new</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>N = int() >= 0</v>
+ </type>
+ <desc>
+ <p>Creates a binary with the content of <c>Subject</c> duplicated <c>N</c> times.</p>
+
+ <p>This function will always create a new binary, even if <c>N =
+ 1</c>. By using <c>copy/1</c> on a binary referencing a larger binary, one
+ might free up the larger binary for garbage collection.</p>
+
+ <note>
+ <p>By deliberately copying a single binary to avoid referencing
+ a larger binary, one might, instead of freeing up the larger
+ binary for later garbage collection, create much more binary
+ data than needed. Sharing binary data is usually good. Only in
+ special cases, when small parts reference large binaries and the
+ large binaries are no longer used in any process, deliberate
+ copying might be a good idea.</p> </note>
+
+ <p>If <c>N</c> &lt; <c>0</c>, a <c>badarg</c> exception is raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name>decode_unsigned(Subject) -> Unsigned</name>
+ <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Unsigned = int() >= 0</v>
+ </type>
+ <desc>
+ <p>The same as <c>decode_unsigned(Subject,big)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>decode_unsigned(Subject, Endianess) -> Unsigned</name>
+ <fsummary>Decode a whole binary into an integer of arbitrary size</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Endianess = big | little</v>
+ <v>Unsigned = int() >= 0</v>
+ </type>
+ <desc>
+
+ <p>Converts the binary digit representation, in big or little
+ endian, of a positive integer in <c>Subject</c> to an Erlang <c>int()</c>.</p>
+
+ <p>Example:</p>
+
+ <code>
+1> binary:decode_unsigned(&lt;&lt;169,138,199&gt;&gt;,big).
+11111111
+ </code>
+ </desc>
+ </func>
+ <func>
+ <name>encode_unsigned(Unsigned) -> binary()</name>
+ <fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
+ <type>
+ <v>Unsigned = int() >= 0</v>
+ </type>
+ <desc>
+ <p>The same as <c>encode_unsigned(Unsigned,big)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>encode_unsigned(Unsigned,Endianess) -> binary()</name>
+ <fsummary>Encodes an unsigned integer into the minimal binary</fsummary>
+ <type>
+ <v>Unsigned = int() >= 0</v>
+ <v>Endianess = big | little</v>
+ </type>
+ <desc>
+
+ <p>Converts a positive integer to the smallest possible
+ representation in a binary digit representation, either big
+ or little endian.</p>
+
+ <p>Example:</p>
+
+ <code>
+1> binary:encode_unsigned(11111111,big).
+&lt;&lt;169,138,199&gt;&gt;
+ </code>
+ </desc>
+ </func>
+ <func>
+ <name>first(Subject) -> int()</name>
+ <fsummary>Returns the first byte of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+
+ <p>Returns the first byte of the binary <c>Subject</c> as an integer. If the
+ size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>last(Subject) -> int()</name>
+ <fsummary>Returns the last byte of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ </type>
+ <desc>
+
+ <p>Returns the last byte of the binary <c>Subject</c> as an integer. If the
+ size of <c>Subject</c> is zero, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>list_to_bin(ByteList) -> binary()</name>
+ <fsummary>Convert a list of integers and binaries to a binary</fsummary>
+ <type>
+ <v>ByteList = iodata() (see module erlang)</v>
+ </type>
+ <desc>
+ <p>Works exactly as <c>erlang:list_to_binary/1</c>, added for completeness.</p>
+ </desc>
+ </func>
+ <func>
+ <name>longest_common_prefix(Binaries) -> int()</name>
+ <fsummary>Returns length of longest common prefix for a set of binaries</fsummary>
+ <type>
+ <v>Binaries = [ binary() ]</v>
+ </type>
+ <desc>
+
+ <p>Returns the length of the longest common prefix of the
+ binaries in the list <c>Binaries</c>. Example:</p>
+
+<code>
+1> binary:longest_common_prefix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"ergonomy"&gt;&gt;]).
+2
+2> binary:longest_common_prefix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"perl"&gt;&gt;]).
+0
+</code>
+
+ <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
+ </desc>
+ </func>
+ <func>
+ <name>longest_common_suffix(Binaries) -> int()</name>
+ <fsummary>Returns length of longest common suffix for a set of binaries</fsummary>
+ <type>
+ <v>Binaries = [ binary() ]</v>
+ </type>
+ <desc>
+
+ <p>Returns the length of the longest common suffix of the
+ binaries in the list <c>Binaries</c>. Example:</p>
+
+<code>
+1> binary:longest_common_suffix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"fang"&gt;&gt;]).
+3
+2> binary:longest_common_suffix([&lt;&lt;"erlang"&gt;&gt;,&lt;&lt;"perl"&gt;&gt;]).
+0
+</code>
+
+ <p>If <c>Binaries</c> is not a flat list of binaries, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>match(Subject, Pattern) -> Found | <c>nomatch</c></name>
+ <fsummary>Searches for the first match of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = part()</v>
+ </type>
+ <desc>
+ <p>The same as <c>match(Subject, Pattern, [])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>match(Subject,Pattern,Options) -> Found | <c>nomatch</c></name>
+ <fsummary>Searches for the first match of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = part()</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = {scope, part()}</v>
+ </type>
+ <desc>
+
+ <p>Searches for the first occurrence of <c>Pattern</c> in <c>Subject</c> and
+ returns the position and length.</p>
+
+ <p>The function will return <c>{Pos,Length}</c> for the binary
+ in <c>Pattern</c> starting at the lowest position in
+ <c>Subject</c>, Example:</p>
+
+<code>
+1> binary:match(&lt;&lt;"abcde"&gt;&gt;, [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"cd"&gt;&gt;],[]).
+{1,4}
+</code>
+
+ <p>Even though <c>&lt;&lt;"cd"&gt;&gt;</c> ends before
+ <c>&lt;&lt;"bcde"&gt;&gt;</c>, <c>&lt;&lt;"bcde"&gt;&gt;</c>
+ begins first and is therefore the first match. If two
+ overlapping matches begin at the same position, the longest is
+ returned.</p>
+
+ <p>Summary of the options:</p>
+
+ <taglist>
+ <tag>{scope, {Start, Length}}</tag>
+ <item><p>Only the given part is searched. Return values still have
+ offsets from the beginning of <c>Subject</c>. A negative <c>Length</c> is
+ allowed as described in the <c>TYPES</c> section of this manual.</p></item>
+ </taglist>
+
+ <p>If none of the strings in
+ <c>Pattern</c> is found, the atom <c>nomatch</c> is returned.</p>
+
+ <p>For a description of <c>Pattern</c>, see
+ <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
+
+ <p>If <c>{scope, {Start,Length}}</c> is given in the options
+ such that <c>Start</c> is larger than the size of
+ <c>Subject</c>, <c>Start + Length</c> is less than zero or
+ <c>Start + Length</c> is larger than the size of
+ <c>Subject</c>, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>matches(Subject, Pattern) -> Found</name>
+ <fsummary>Searches for all matches of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = [ part() ] | []</v>
+ </type>
+ <desc>
+ <p>The same as <c>matches(Subject, Pattern, [])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>matches(Subject,Pattern,Options) -> Found</name>
+ <fsummary>Searches for all matches of a pattern in a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Found = [ part() ] | []</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = {scope, part()}</v>
+ </type>
+ <desc>
+
+ <p>Works like match, but the <c>Subject</c> is searched until
+ exhausted and a list of all non-overlapping parts matching
+ <c>Pattern</c> is returned (in order). </p>
+
+ <p>The first and longest match is preferred to a shorter,
+ which is illustrated by the following example:</p>
+
+<code>
+1> binary:matches(&lt;&lt;"abcde"&gt;&gt;,
+ [&lt;&lt;"bcde"&gt;&gt;,&lt;&lt;"bc"&gt;&gt;>,&lt;&lt;"de"&gt;&gt;],[]).
+[{1,4}]
+</code>
+
+ <p>The result shows that &lt;&lt;bcde"&gt;&gt; is selected instead of the
+ shorter match &lt;&lt;"bc"&gt;&gt; (which would have given raise to one
+ more match,&lt;&lt;"de"&gt;&gt;). This corresponds to the behavior of posix
+ regular expressions (and programs like awk), but is not
+ consistent with alternative matches in re (and Perl), where
+ instead lexical ordering in the search pattern selects which
+ string matches.</p>
+
+ <p>If none of the strings in pattern is found, an empty list is returned.</p>
+
+ <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso> and for a
+ description of available options, see <seealso marker="#match-3">match/3</seealso>.</p>
+
+ <p>If <c>{scope, {Start,Length}}</c> is given in the options such that
+ <c>Start</c> is larger than the size of <c>Subject</c>, <c>Start + Length</c> is
+ less than zero or <c>Start + Length</c> is larger than the size of
+ <c>Subject</c>, a <c>badarg</c> exception is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>part(Subject, PosLen) -> binary()</name>
+ <fsummary>Extracts a part of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>PosLen = part()</v>
+ </type>
+ <desc>
+
+ <p>Extracts the part of the binary <c>Subject</c> described by <c>PosLen</c>.</p>
+
+ <p>Negative length can be used to extract bytes at the end of a binary:</p>
+
+<code>
+1> Bin = &lt;&lt;1,2,3,4,5,6,7,8,9,10&gt;&gt;.
+2> binary:part(Bin,{byte_size(Bin), -5}).
+&lt;&lt;6,7,8,9,10&gt;&gt;
+</code>
+
+ <note>
+ <p><seealso marker="#part-2">part/2</seealso>and <seealso
+ marker="#part-3">part/3</seealso> are also available in the
+ <c>erlang</c> module under the names <c>binary_part/2</c> and
+ <c>binary_part/3</c>. Those BIFs are allowed in guard tests.</p>
+ </note>
+
+ <p>If <c>PosLen</c> in any way references outside the binary, a <c>badarg</c> exception
+ is raised.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>part(Subject, Pos, Len) -> binary()</name>
+ <fsummary>Extracts a part of a binary</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pos = int()</v>
+ <v>Len = int()</v>
+ </type>
+ <desc>
+ <p>The same as <c>part(Subject, {Pos, Len})</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>referenced_byte_size(binary()) -> int()</name>
+ <fsummary>Determines the size of the actual binary pointed out by a sub-binary</fsummary>
+ <desc>
+
+ <p>If a binary references a larger binary (often described as
+ being a sub-binary), it can be useful to get the size of the
+ actual referenced binary. This function can be used in a program
+ to trigger the use of <c>copy/1</c>. By copying a binary, one might
+ dereference the original, possibly large, binary which a smaller
+ binary is a reference to.</p>
+
+ <p>Example:</p>
+
+ <code>
+store(Binary, GBSet) ->
+ NewBin =
+ case binary:referenced_byte_size(Binary) of
+ Large when Large > 2 * byte_size(Binary) ->
+ binary:copy(Binary);
+ _ ->
+ Binary
+ end,
+ gb_sets:insert(NewBin,GBSet).
+ </code>
+
+ <p>In this example, we chose to copy the binary content before
+ inserting it in the <c>gb_set()</c> if it references a binary more than
+ twice the size of the data we're going to keep. Of course
+ different rules for when copying will apply to different
+ programs.</p>
+
+ <p>Binary sharing will occur whenever binaries are taken apart,
+ this is the fundamental reason why binaries are fast,
+ decomposition can always be done with O(1) complexity. In rare
+ circumstances this data sharing is however undesirable, why this
+ function together with <c>copy/1</c> might be useful when optimizing
+ for memory use.</p>
+
+ <p>Example of binary sharing:</p>
+
+ <code>
+1> A = binary:copy(&lt;&lt;1&gt;&gt;,100).
+&lt;&lt;1,1,1,1,1 ...
+2> byte_size(A).
+100
+3> binary:referenced_byte_size(A)
+100
+4> &lt;&lt;_:10/binary,B:10/binary,_/binary&gt;&gt; = A.
+&lt;&lt;1,1,1,1,1 ...
+5> byte_size(B).
+10
+6> binary:referenced_byte_size(B)
+100
+ </code>
+
+ <note>
+ <p>Binary data is shared among processes. If another process
+ still references the larger binary, copying the part this
+ process uses only consumes more memory and will not free up the
+ larger binary for garbage collection. Use this kind of intrusive
+ functions with extreme care, and only if a real problem is
+ detected.</p>
+ </note>
+
+ </desc>
+ </func>
+ <func>
+ <name>replace(Subject,Pattern,Replacement) -> Result</name>
+ <fsummary>Replaces bytes in a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Replacement = binary()</v>
+ <v>Result = binary()</v>
+ </type>
+ <desc>
+ <p>The same as <c>replace(Subject,Pattern,Replacement,[])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>replace(Subject,Pattern,Replacement,Options) -> Result</name>
+ <fsummary>Replaces bytes in a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Replacement = binary()</v>
+ <v>Result = binary()</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = global | {scope, part()} | {insert_replaced, InsPos}</v>
+ <v>InsPos = OnePos | [ OnePos ]</v>
+ <v>OnePos = int() =&lt; byte_size(Replacement)</v>
+ </type>
+ <desc>
+
+ <p>Constructs a new binary by replacing the parts in
+ <c>Subject</c> matching <c>Pattern</c> with the content of
+ <c>Replacement</c>.</p>
+
+ <p>If the matching sub-part of <c>Subject</c> giving raise to the
+ replacement is to be inserted in the result, the option
+ <c>{insert_replaced, InsPos}</c> will insert the matching part into
+ <c>Replacement</c> at the given position (or positions) before actually
+ inserting <c>Replacement</c> into the <c>Subject</c>. Example:</p>
+
+<code>
+1> binary:replace(&lt;&lt;"abcde"&gt;&gt;,&lt;&lt;"b"&gt;&gt;,&lt;&lt;"[]"&gt;&gt;,[{insert_replaced,1}]).
+&lt;&lt;"a[b]cde"&gt;&gt;
+2> binary:replace(&lt;&lt;"abcde"&gt;&gt;,[&lt;&lt;"b"&gt;&gt;,&lt;&lt;"d"&gt;&gt;],&lt;&lt;"[]"&gt;&gt;,
+ [global,{insert_replaced,1}]).
+&lt;&lt;"a[b]c[d]e"&gt;&gt;
+3> binary:replace(&lt;&lt;"abcde"&gt;&gt;,[&lt;&lt;"b"&gt;&gt;,&lt;&lt;"d"&gt;&gt;],&lt;&lt;"[]"&gt;&gt;,
+ [global,{insert_replaced,[1,1]}]).
+&lt;&lt;"a[bb]c[dd]e"&gt;&gt;
+4> binary:replace(&lt;&lt;"abcde"&gt;&gt;,[&lt;&lt;"b"&gt;&gt;,&lt;&lt;"d"&gt;&gt;],&lt;&lt;"[-]"&gt;&gt;,
+ [global,{insert_replaced,[1,2]}]).
+&lt;&lt;"a[b-b]c[d-d]e"&gt;&gt;
+</code>
+
+ <p>If any position given in <c>InsPos</c> is greater than the size of the replacement binary, a <c>badarg</c> exception is raised.</p>
+
+ <p>The options <c>global</c> and <c>{scope, part()}</c> work as for <seealso marker="#split-3">split/3</seealso>. The return type is always a <c>binary()</c>.</p>
+
+ <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>split(Subject,Pattern) -> Parts</name>
+ <fsummary>Splits a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Parts = [ binary() ]</v>
+ </type>
+ <desc>
+ <p>The same as <c>split(Subject, Pattern, [])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>split(Subject,Pattern,Options) -> Parts</name>
+ <fsummary>Splits a binary according to a pattern</fsummary>
+ <type>
+ <v>Subject = binary()</v>
+ <v>Pattern = binary() | [ binary() ] | cp()</v>
+ <v>Parts = [ binary() ]</v>
+ <v>Options = [ Option ]</v>
+ <v>Option = {scope, part()} | trim | global</v>
+ </type>
+ <desc>
+
+ <p>Splits Binary into a list of binaries based on Pattern. If
+ the option global is not given, only the first occurrence of
+ Pattern in Subject will give rise to a split.</p>
+
+ <p>The parts of Pattern actually found in Subject are not included in the result.</p>
+
+ <p>Example:</p>
+
+<code>
+1> binary:split(&lt;&lt;1,255,4,0,0,0,2,3&gt;&gt;, [&lt;&lt;0,0,0&gt;&gt;,&lt;&lt;2&gt;&gt;],[]).
+[&lt;&lt;1,255,4&gt;&gt;, &lt;&lt;2,3&gt;&gt;]
+2> binary:split(&lt;&lt;0,1,0,0,4,255,255,9&gt;&gt;, [&lt;&lt;0,0&gt;&gt;, &lt;&lt;255,255&gt;&gt;],[global]).
+[&lt;&lt;0,1&gt;&gt;,&lt;&lt;4&gt;&gt;,&lt;&lt;9&gt;&gt;]
+</code>
+
+ <p>Summary of options:</p>
+ <taglist>
+
+ <tag>{scope, part()}</tag>
+
+ <item><p>Works as in <seealso marker="#match-3">match/3</seealso> and
+ <seealso marker="#matches-3">matches/3</seealso>. Note that
+ this only defines the scope of the search for matching strings,
+ it does not cut the binary before splitting. The bytes before
+ and after the scope will be kept in the result. See example
+ below.</p></item>
+
+ <tag>trim</tag>
+
+ <item><p>Removes trailing empty parts of the result (as does trim in <c>re:split/3</c>)</p></item>
+
+ <tag>global</tag>
+
+ <item><p>Repeats the split until the <c>Subject</c> is
+ exhausted. Conceptually the global option makes split work on
+ the positions returned by <seealso marker="#matches-3">matches/3</seealso>,
+ while it normally
+ works on the position returned by
+ <seealso marker="#match-3">match/3</seealso>.</p></item>
+
+ </taglist>
+
+ <p>Example of the difference between a scope and taking the
+ binary apart before splitting:</p>
+
+<code>
+1> binary:split(&lt;&lt;"banana"&gt;&gt;,[&lt;&lt;"a"&gt;&gt;],[{scope,{2,3}}]).
+[&lt;&lt;"ban"&gt;&gt;,&lt;&lt;"na"&gt;&gt;]
+2> binary:split(binary:part(&lt;&lt;"banana"&gt;&gt;,{2,3}),[&lt;&lt;"a"&gt;&gt;],[]).
+[&lt;&lt;"n"&gt;&gt;,&lt;&lt;"n"&gt;&gt;]
+</code>
+
+ <p>The return type is always a list of binaries that are all
+ referencing <c>Subject</c>. This means that the data in <c>Subject</c> is not
+ actually copied to new binaries and that <c>Subject</c> cannot be
+ garbage collected until the results of the split are no longer
+ referenced.</p>
+
+ <p>For a description of <c>Pattern</c>, see <seealso marker="#compile_pattern-1">compile_pattern/1</seealso>.</p>
+
+ </desc>
+ </func>
+ </funcs>
+</erlref>
diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml
index 36f0c03162..075c7f9c78 100644
--- a/lib/stdlib/doc/src/calendar.xml
+++ b/lib/stdlib/doc/src/calendar.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -63,6 +63,14 @@
given as local time, they must be converted to universal time, in
order to get the correct value of the elapsed time between epochs.
Use of the function <c>time_difference/2</c> is discouraged.</p>
+ <p>There exists different definitions for the week of the year.
+ The calendar module contains a week of the year implementation
+ which conforms to the ISO 8601 standard. Since the week number for
+ a given date can fall on the previous, the current or on the next
+ year it is important to provide the information which year is it
+ together with the week number. The function <c>iso_week_number/0</c>
+ and <c>iso_week_number/1</c> returns a tuple of the year and the
+ week number.</p>
</description>
<section>
@@ -154,6 +162,30 @@ time() = {Hour, Minute, Second}
</desc>
</func>
<func>
+ <name>iso_week_number() -> IsoWeekNumber</name>
+ <fsummary>Compute the iso week number for the actual date</fsummary>
+ <type>
+ <v>IsoWeekNumber = {int(), int()}</v>
+ </type>
+ <desc>
+ <p>This function returns the tuple {Year, WeekNum} representing
+ the iso week number for the actual date. For determining the
+ actual date, the function <c>local_time/0</c> is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name>iso_week_number(Date) -> IsoWeekNumber</name>
+ <fsummary>Compute the iso week number for the given date</fsummary>
+ <type>
+ <v>Date = date()</v>
+ <v>IsoWeekNumber = {int(), int()}</v>
+ </type>
+ <desc>
+ <p>This function returns the tuple {Year, WeekNum} representing
+ the iso week number for the given date.</p>
+ </desc>
+ </func>
+ <func>
<name>last_day_of_the_month(Year, Month) -> int()</name>
<fsummary>Compute the number of days in a month</fsummary>
<desc>
diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml
index 8d1398d3b7..b002af6616 100644
--- a/lib/stdlib/doc/src/dets.xml
+++ b/lib/stdlib/doc/src/dets.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -109,7 +109,7 @@ bool() = true | false
file() = string()
int() = integer() >= 0
keypos() = integer() >= 1
-name() = atom() | ref()
+name() = atom() | reference()
no_slots() = integer() >= 0 | default
object() = tuple()
object_cont() = tuple()
@@ -759,7 +759,7 @@ ok
<fsummary>Open an existing Dets table.</fsummary>
<type>
<v>FileName = file()</v>
- <v>Reference = ref()</v>
+ <v>Reference = reference()</v>
</type>
<desc>
<p>Opens an existing table. If the table has not been properly
@@ -816,7 +816,7 @@ ok
</item>
<item>
<p><c>{max_no_slots, no_slots()}</c>, the maximum number
- of slots that will be used. The default value is 2 M, and
+ of slots that will be used. The default value as well as
the maximal value is 32 M. Note that a higher value may
increase the fragmentation of the table, and conversely,
that a smaller value may decrease the fragmentation, at
diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml
index ebcd2eed09..40e61d7d33 100644
--- a/lib/stdlib/doc/src/dict.xml
+++ b/lib/stdlib/doc/src/dict.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -165,8 +165,8 @@ dictionary()
<v>Dict = dictionary()</v>
</type>
<desc>
- <p>This function converts the key/value list <c>List</c> to a
- dictionary.</p>
+ <p>This function converts the <c>Key</c> - <c>Value</c> list
+ <c>List</c> to a dictionary.</p>
</desc>
</func>
<func>
@@ -270,7 +270,7 @@ merge(Fun, D1, D2) ->
<v>Dict1 = Dict2 = dictionary()</v>
</type>
<desc>
- <p>Update the a value in a dictionary by calling <c>Fun</c> on
+ <p>Update a value in a dictionary by calling <c>Fun</c> on
the value to get a new value. An exception is generated if
<c>Key</c> is not present in the dictionary.</p>
</desc>
@@ -285,7 +285,7 @@ merge(Fun, D1, D2) ->
<v>Dict1 = Dict2 = dictionary()</v>
</type>
<desc>
- <p>Update the a value in a dictionary by calling <c>Fun</c> on
+ <p>Update a value in a dictionary by calling <c>Fun</c> on
the value to get a new value. If <c>Key</c> is not present
in the dictionary then <c>Initial</c> will be stored as
the first value. For example <c>append/3</c> could be defined
diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml
index 7fb03e7c50..c93248493f 100644
--- a/lib/stdlib/doc/src/erl_expand_records.xml
+++ b/lib/stdlib/doc/src/erl_expand_records.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2005</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml
index 7c821d2efc..cfb18ec131 100644
--- a/lib/stdlib/doc/src/erl_id_trans.xml
+++ b/lib/stdlib/doc/src/erl_id_trans.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2010</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -70,7 +70,8 @@
<section>
<title>See Also</title>
- <p><seealso marker="erl_parse">erl_parse(3)</seealso>, compile(3).</p>
+ <p><seealso marker="erl_parse">erl_parse(3)</seealso>,
+ <seealso marker="compiler:compile">compile(3)</seealso>.</p>
</section>
</erlref>
diff --git a/lib/stdlib/doc/src/erl_internal.xml b/lib/stdlib/doc/src/erl_internal.xml
index 906b95deb7..732d77c3ae 100644
--- a/lib/stdlib/doc/src/erl_internal.xml
+++ b/lib/stdlib/doc/src/erl_internal.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml
index 6a7d37765c..8639d678fa 100644
--- a/lib/stdlib/doc/src/erl_lint.xml
+++ b/lib/stdlib/doc/src/erl_lint.xml
@@ -96,8 +96,8 @@
<p>The <c>AbsForms</c> of a module which comes from a file
that is read through <c>epp</c>, the Erlang pre-processor,
can come from many files. This means that any references to
- errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso> The warnings and
- errors returned have the following format:
+ errors must include the file name (see <seealso marker="epp">epp(3)</seealso>, or parser <seealso marker="erl_parse">erl_parse(3)</seealso>).
+ The warnings and errors returned have the following format:
</p>
<code type="none">
[{FileName2,[ErrorInfo]}] </code>
diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml
index ae8a8afd5c..18b592deea 100644
--- a/lib/stdlib/doc/src/erl_parse.xml
+++ b/lib/stdlib/doc/src/erl_parse.xml
@@ -39,7 +39,7 @@
expressions, or terms. The Abstract Format is described in the ERTS
User's Guide.
Note that a token list must end with the <em>dot</em> token in order
- to be acceptable to the parse functions (see erl_scan).</p>
+ to be acceptable to the parse functions (see <seealso marker="erl_scan">erl_scan(3)</seealso>).</p>
</description>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml
index 6b15c5afd3..1fdda48893 100644
--- a/lib/stdlib/doc/src/erl_pp.xml
+++ b/lib/stdlib/doc/src/erl_pp.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index ee1befc882..746f94d3f4 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -56,8 +56,8 @@
Even if there are no references to a table from any process, it
will not automatically be destroyed unless the owner process
terminates. It can be destroyed explicitly by using
- <c>delete/1</c>.</p>
- <p>Since R13B01, table ownership can be transferred at process termination
+ <c>delete/1</c>. The default owner is the process that created the
+ table. Table ownership can be transferred at process termination
by using the <seealso marker="#heir">heir</seealso> option or explicitly
by calling <seealso marker="#give_away/3">give_away/3</seealso>.</p>
<p>Some implementation details:</p>
@@ -82,11 +82,15 @@
<c>float()</c> that extends to the same value, hence the key
<c>1</c> and the key <c>1.0</c> are regarded as equal in an
<c>ordered_set</c> table.</p>
- <p>In general, the functions below will exit with reason
- <c>badarg</c> if any argument is of the wrong format, or if the
- table identifier is invalid.</p>
</description>
-
+ <section>
+ <title>Failure</title>
+ <p>In general, the functions below will exit with reason
+ <c>badarg</c> if any argument is of the wrong format, if the
+ table identifier is invalid or if the operation is denied due to
+ table access rights (<seealso marker="#protected">protected</seealso>
+ or <seealso marker="#private">private</seealso>).</p>
+ </section>
<section><marker id="concurrency"></marker>
<title>Concurrency</title>
<p>This module provides some limited support for concurrent access.
@@ -481,6 +485,9 @@ Error: fun containing local Erlang function calls
<item><c>Item=protection, Value=public|protected|private</c> <br></br>
The table access rights.</item>
+ <item><c>Item=compressed, Value=true|false</c> <br></br>
+
+ Indicates if the table is compressed or not.</item>
</list>
</desc>
</func>
@@ -947,9 +954,10 @@ ets:select(Table,MatchSpec),</code>
<type>
<v>Name = atom()</v>
<v>Options = [Option]</v>
- <v>&nbsp;Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | {write_concurrency,bool()}</v>
+ <v>&nbsp;Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks</v>
<v>&nbsp;&nbsp;Type = set | ordered_set | bag | duplicate_bag</v>
<v>&nbsp;&nbsp;Access = public | protected | private</v>
+ <v>&nbsp;&nbsp;Tweaks = {write_concurrency,bool()} | {read_concurrency,bool()} | compressed</v>
<v>&nbsp;&nbsp;Pos = int()</v>
<v>&nbsp;&nbsp;HeirData = term()</v>
</type>
@@ -963,7 +971,7 @@ ets:select(Table,MatchSpec),</code>
table is named or not. If one or more options are left out,
the default values are used. This means that not specifying
any options (<c>[]</c>) is the same as specifying
- <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false}]</c>.</p>
+ <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false},{read_concurrency,false}]</c>.</p>
<list type="bulleted">
<item>
<p><c>set</c>
@@ -1002,12 +1010,14 @@ ets:select(Table,MatchSpec),</code>
Any process may read or write to the table.</p>
</item>
<item>
+ <marker id="protected"></marker>
<p><c>protected</c>
The owner process can read and write to the table. Other
processes can only read the table. This is the default
setting for the access rights.</p>
</item>
<item>
+ <marker id="private"></marker>
<p><c>private</c>
Only the owner process can read or write to the table.</p>
</item>
@@ -1039,15 +1049,22 @@ ets:select(Table,MatchSpec),</code>
the owner terminates.</p>
</item>
<item>
+ <marker id="new_2_write_concurrency"></marker>
<p><c>{write_concurrency,bool()}</c>
- Performance tuning. Default is <c>false</c>, which means that the table
- is optimized towards concurrent read access. An operation that
+ Performance tuning. Default is <c>false</c>, in which case an operation that
mutates (writes to) the table will obtain exclusive access,
blocking any concurrent access of the same table until finished.
If set to <c>true</c>, the table is optimized towards concurrent
write access. Different objects of the same table can be mutated
(and read) by concurrent processes. This is achieved to some degree
- at the expense of single access and concurrent reader performance.
+ at the expense of sequential access and concurrent reader performance.
+ The <c>write_concurrency</c> option can be combined with the
+ <seealso marker="#new_2_read_concurrency">read_concurrency</seealso>
+ option. You typically want to combine these when large concurrent
+ read bursts and large concurrent write bursts are common (see the
+ documentation of the
+ <seealso marker="#new_2_read_concurrency">read_concurrency</seealso>
+ option for more information).
Note that this option does not change any guarantees about
<seealso marker="#concurrency">atomicy and isolation</seealso>.
Functions that makes such promises over several objects (like
@@ -1055,6 +1072,38 @@ ets:select(Table,MatchSpec),</code>
<p>Table type <c>ordered_set</c> is not affected by this option in current
implementation.</p>
</item>
+ <item>
+ <marker id="new_2_read_concurrency"></marker>
+ <p><c>{read_concurrency,bool()}</c>
+ Performance tuning. Default is <c>false</c>. When set to
+ <c>true</c>, the table is optimized for concurrent read
+ operations. When this option is enabled on a runtime system with
+ SMP support, read operations become much cheaper; especially on
+ systems with multiple physical processors. However, switching
+ between read and write operations becomes more expensive. You
+ typically want to enable this option when concurrent read
+ operations are much more frequent than write operations, or when
+ concurrent reads and writes comes in large read and write
+ bursts (i.e., lots of reads not interrupted by writes, and lots
+ of writes not interrupted by reads). You typically do
+ <em>not</em> want to enable this option when the common access
+ pattern is a few read operations interleaved with a few write
+ operations repeatedly. In this case you will get a performance
+ degradation by enabling this option. The <c>read_concurrency</c>
+ option can be combined with the
+ <seealso marker="#new_2_write_concurrency">write_concurrency</seealso>
+ option. You typically want to combine these when large concurrent
+ read bursts and large concurrent write bursts are common.</p>
+ </item>
+ <item>
+ <marker id="new_2_compressed"></marker>
+ <p><c>compressed</c>
+ If this option is present, the table data will be stored in a more compact format to
+ consume less memory. The downside is that it will make table operations slower.
+ Especially operations that need to inspect entire objects,
+ such as <c>match</c> and <c>select</c>, will get much slower. The key element
+ is not compressed in current implementation.</p>
+ </item>
</list>
</desc>
</func>
@@ -1355,6 +1404,28 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
+ <name>select_count(Tab, MatchSpec) -> NumMatched</name>
+ <fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary>
+ <type>
+ <v>Tab = tid() | atom()</v>
+ <v>Object = tuple()</v>
+ <v>MatchSpec = match_spec()</v>
+ <v>NumMatched = integer()</v>
+ </type>
+ <desc>
+ <p>Matches the objects in the table <c>Tab</c> using a
+ <seealso marker="#match_spec">match_spec</seealso>. If the
+ match_spec returns <c>true</c> for an object, that object
+ considered a match and is counted. For any other result from
+ the match_spec the object is not considered a match and is
+ therefore not counted.</p>
+ <p>The function could be described as a <c>match_delete/2</c>
+ that does not actually delete any elements, but only counts
+ them.</p>
+ <p>The function returns the number of objects matched.</p>
+ </desc>
+ </func>
+ <func>
<name>select_delete(Tab, MatchSpec) -> NumDeleted</name>
<fsummary>Match the objects in an ETS table against a match_spec and deletes objects where the match_spec returns 'true'</fsummary>
<type>
@@ -1381,25 +1452,82 @@ is_integer(X), is_integer(Y), X + Y < 4711]]></code>
</desc>
</func>
<func>
- <name>select_count(Tab, MatchSpec) -> NumMatched</name>
- <fsummary>Match the objects in an ETS table against a match_spec and returns the number of objects for which the match_spec returned 'true'</fsummary>
+ <name>select_reverse(Tab, MatchSpec) -> [Match]</name>
+ <fsummary>Match the objects in an ETS table against a match_spec.</fsummary>
<type>
<v>Tab = tid() | atom()</v>
- <v>Object = tuple()</v>
+ <v>Match = term()</v>
<v>MatchSpec = match_spec()</v>
- <v>NumMatched = integer()</v>
</type>
<desc>
- <p>Matches the objects in the table <c>Tab</c> using a
- <seealso marker="#match_spec">match_spec</seealso>. If the
- match_spec returns <c>true</c> for an object, that object
- considered a match and is counted. For any other result from
- the match_spec the object is not considered a match and is
- therefore not counted.</p>
- <p>The function could be described as a <c>match_delete/2</c>
- that does not actually delete any elements, but only counts
- them.</p>
- <p>The function returns the number of objects matched.</p>
+
+ <p>Works like <c>select/2</c>, but returns the list in reverse
+ order for the <c>ordered_set</c> table type. For all other table
+ types, the return value is identical to that of <c>select/2</c>.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>select_reverse(Tab, MatchSpec, Limit) -> {[Match],Continuation} | '$end_of_table'</name>
+ <fsummary>Match the objects in an ETS table against a match_spec and returns part of the answers.</fsummary>
+ <type>
+ <v>Tab = tid() | atom()</v>
+ <v>Match = term()</v>
+ <v>MatchSpec = match_spec()</v>
+ <v>Continuation = term()</v>
+ </type>
+ <desc>
+
+ <p>Works like <c>select/3</c>, but for the <c>ordered_set</c>
+ table type, traversing is done starting at the last object in
+ Erlang term order and moves towards the first. For all other
+ table types, the return value is identical to that of
+ <c>select/3</c>.</p>
+
+ <p>Note that this is <em>not</em> equivalent to
+ reversing the result list of a <c>select/3</c> call, as the result list
+ is not only reversed, but also contains the last <c>Limit</c>
+ matching objects in the table, not the first.</p>
+
+ </desc>
+ </func>
+ <func>
+ <name>select_reverse(Continuation) -> {[Match],Continuation} | '$end_of_table'</name>
+ <fsummary>Continue matching objects in an ETS table.</fsummary>
+ <type>
+ <v>Match = term()</v>
+ <v>Continuation = term()</v>
+ </type>
+ <desc>
+
+ <p>Continues a match started with
+ <c>ets:select_reverse/3</c>. If the table is an
+ <c>ordered_set</c>, the traversal of the table will continue
+ towards objects with keys earlier in the Erlang term order. The
+ returned list will also contain objects with keys in reverse
+ order.</p>
+
+ <p>For all other table types, the behaviour is exatly that of <c>select/1</c>.</p>
+ <p>Example:</p>
+ <code>
+1> T = ets:new(x,[ordered_set]).
+2> [ ets:insert(T,{N}) || N &lt;- lists:seq(1,10) ].
+...
+3> {R0,C0} = ets:select_reverse(T,[{'_',[],['$_']}],4).
+...
+4> R0.
+[{10},{9},{8},{7}]
+5> {R1,C1} = ets:select_reverse(C0).
+...
+6> R1.
+[{6},{5},{4},{3}]
+7> {R2,C2} = ets:select_reverse(C1).
+...
+8> R2.
+[{2},{1}]
+9> '$end_of_table' = ets:select_reverse(C2).
+...
+ </code>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml
index c1c4ca9350..fab68ae77c 100644
--- a/lib/stdlib/doc/src/filelib.xml
+++ b/lib/stdlib/doc/src/filelib.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -36,14 +36,23 @@
<description>
<p>This module contains utilities on a higher level than the <c>file</c>
module.</p>
+ <p>The module supports Unicode file names, so that it will match against regular expressions given in Unicode and that it will find and process raw file names (i.e. files named in a way that does not confirm to the expected encoding).</p>
+ <p>If the VM operates in Unicode file naming mode on a machine with transparent file naming, the <c>fun()</c> provided to <c>fold_files/5</c> needs to be prepared to handle binary file names.</p>
+ <p>For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p>
</description>
<section>
<title>DATA TYPES</title>
<code type="none">
-filename() = string() | atom() | DeepList
-dirname() = filename()
-DeepList = [char() | atom() | DeepList]</code>
+filename() = string() | atom() | DeepList | RawFilename
+ DeepList = [char() | atom() | DeepList]
+ RawFilename = binary()
+ If VM is in unicode filename mode, string() and char() are allowed to be > 255.
+ RawFilename is a filename not subject to Unicode translation, meaning that it
+ can contain characters not conforming to the Unicode encoding expected from the
+ filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode
+ filename mode).
+dirname() = filename()</code>
</section>
<funcs>
@@ -90,6 +99,18 @@ DeepList = [char() | atom() | DeepList]</code>
If <c>Recursive</c> is true all sub-directories to <c>Dir</c>
are processed. The regular expression matching is done on just
the filename without the directory part.</p>
+
+ <p>If Unicode file name translation is in effect and the file
+ system is completely transparent, file names that cannot be
+ interpreted as Unicode may be encountered, in which case the
+ <c>fun()</c> must be prepared to handle raw file names
+ (i.e. binaries). If the regular expression contains
+ codepoints beyond 255, it will not match file names that do
+ not conform to the expected character encoding (i.e. are not
+ encoded in valid UTF-8).</p>
+
+ <p>For more information about raw file names, see the
+ <seealso marker="kernel:file">file</seealso> module.</p>
</desc>
</func>
<func>
@@ -160,6 +181,12 @@ DeepList = [char() | atom() | DeepList]</code>
<p>Matches any number of characters up to the end of
the filename, the next dot, or the next slash.</p>
</item>
+ <tag>[Character1,Character2,...]</tag>
+ <item>
+ <p>Matches any of the characters listed. Two characters
+ separated by a hyphen will match a range of characters.
+ Example: <c>[A-Z]</c> will match any uppercase letter.</p>
+ </item>
<tag>{Item,...}</tag>
<item>
<p>Alternation. Matches one of the alternatives.</p>
diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml
index 0cf82fa48b..cdee6e4a81 100644
--- a/lib/stdlib/doc/src/filename.xml
+++ b/lib/stdlib/doc/src/filename.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1997</year><year>2009</year>
+ <year>1997</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -43,13 +43,22 @@
only, even if the arguments contain back slashes. Use
<c>join/1</c> to normalize a file name by removing redundant
directory separators.</p>
+ <p>The module supports raw file names in the way that if a binary is present, or the file name cannot be interpreted according to the return value of
+ <seealso marker="kernel:file#native_name_encoding/0">file:native_name_encoding/0</seealso>, a raw file name will also be returned. For example filename:join/1 provided with a path component being a binary (and also not being possible to interpret under the current native file name encoding) will result in a raw file name being returned (the join operation will have been performed of course). For more information about raw file names, see the <seealso marker="kernel:file">file</seealso> module.</p>
</description>
<section>
<title>DATA TYPES</title>
<code type="none">
-name() = string() | atom() | DeepList
- DeepList = [char() | atom() | DeepList]</code>
+name() = string() | atom() | DeepList | RawFilename
+ DeepList = [char() | atom() | DeepList]
+ RawFilename = binary()
+ If VM is in unicode filename mode, string() and char() are allowed to be > 255.
+ RawFilename is a filename not subject to Unicode translation, meaning that it
+ can contain characters not conforming to the Unicode encoding expected from the
+ filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode
+ filename mode).
+ </code>
</section>
<funcs>
<func>
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index df09294de6..2234a62ac3 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_event</title>
@@ -630,12 +630,66 @@ gen_event:stop -----> Module:terminate/2
<p>The function should return the updated internal state.</p>
</desc>
</func>
+ <func>
+ <name>Module:format_status(Opt, [PDict, State]) -> Status</name>
+ <fsummary>Optional function for providing a term describing the
+ current event handler state.</fsummary>
+ <type>
+ <v>Opt = normal | terminate</v>
+ <v>PDict = [{Key, Value}]</v>
+ <v>State = term()</v>
+ <v>Status = term()</v>
+ </type>
+ <desc>
+ <note>
+ <p>This callback is optional, so event handler modules need
+ not export it. If a handler does not export this function,
+ the gen_event module uses the handler state directly for
+ the purposes described below.</p>
+ </note>
+ <p>This function is called by a gen_event process when:</p>
+ <list typed="bulleted">
+ <item>One
+ of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
+ is invoked to get the gen_event status. <c>Opt</c> is set
+ to the atom <c>normal</c> for this case.</item>
+ <item>The event handler terminates abnormally and gen_event
+ logs an error. <c>Opt</c> is set to the
+ atom <c>terminate</c> for this case.</item>
+ </list>
+ <p>This function is useful for customising the form and
+ appearance of the event handler state for these cases. An
+ event handler callback module wishing to customise
+ the <c>sys:get_status/1,2</c> return value as well as how
+ its state appears in termination error logs exports an
+ instance of <c>format_status/2</c> that returns a term
+ describing the current state of the event handler.</p>
+ <p><c>PDict</c> is the current value of the gen_event's
+ process dictionary.</p>
+ <p><c>State</c> is the internal state of the event
+ handler.</p>
+ <p>The function should return <c>Status</c>, a term that
+ customises the details of the current state of the event
+ handler. Any term is allowed for <c>Status</c>. The
+ gen_event module uses <c>Status</c> as follows:</p>
+ <list typed="bulleted">
+ <item>When <c>sys:get_status/1,2</c> is called, gen_event
+ ensures that its return value contains <c>Status</c> in
+ place of the event handler's actual state term.</item>
+ <item>When an event handler terminates abnormally, gen_event
+ logs <c>Status</c> in place of the event handler's actual
+ state term.</item>
+ </list>
+ <p>One use for this function is to return compact alternative
+ state representations to avoid having large state terms
+ printed in logfiles.</p>
+ </desc>
+ </func>
</funcs>
<section>
<title>SEE ALSO</title>
- <p><seealso marker="supervisor">supervisor(3)</seealso>,
+ <p><seealso marker="supervisor">supervisor(3)</seealso>,
<seealso marker="sys">sys(3)</seealso></p>
</section>
</erlref>
-
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 739cd0bffd..d15383c621 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_fsm</title>
@@ -730,33 +730,58 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
</desc>
</func>
<func>
- <name>Module:format_status(normal, [PDict, StateData]) -> Status</name>
+ <name>Module:format_status(Opt, [PDict, StateData]) -> Status</name>
<fsummary>Optional function for providing a term describing the
current gen_fsm status.</fsummary>
<type>
+ <v>Opt = normal | terminate</v>
<v>PDict = [{Key, Value}]</v>
<v>StateData = term()</v>
- <v>Status = [term()]</v>
+ <v>Status = term()</v>
</type>
<desc>
- <p><em>This callback is optional, so callback modules need not
- export it. The gen_fsm module provides a default
- implementation of this function that returns the callback
- module state data.</em></p>
- <p>This function is called by a gen_fsm process when one
- of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
- is invoked to get the gen_fsm status. A callback module
- wishing to customise the <c>sys:get_status/1,2</c> return
- value exports an instance of <c>format_status/2</c> that
- returns a term describing the current status of the
- gen_fsm.</p>
+ <note>
+ <p>This callback is optional, so callback modules need not
+ export it. The gen_fsm module provides a default
+ implementation of this function that returns the callback
+ module state data.</p>
+ </note>
+ <p>This function is called by a gen_fsm process when:</p>
+ <list typed="bulleted">
+ <item>One
+ of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
+ is invoked to get the gen_fsm status. <c>Opt</c> is set to
+ the atom <c>normal</c> for this case.</item>
+ <item>The gen_fsm terminates abnormally and logs an
+ error. <c>Opt</c> is set to the atom <c>terminate</c> for
+ this case.</item>
+ </list>
+ <p>This function is useful for customising the form and
+ appearance of the gen_fsm status for these cases. A callback
+ module wishing to customise the <c>sys:get_status/1,2</c>
+ return value as well as how its status appears in
+ termination error logs exports an instance
+ of <c>format_status/2</c> that returns a term describing the
+ current status of the gen_fsm.</p>
<p><c>PDict</c> is the current value of the gen_fsm's
process dictionary.</p>
<p><c>StateData</c> is the internal state data of the
gen_fsm.</p>
- <p>The function should return <c>Status</c>, a list of one or
- more terms that customise the details of the current state
- and status of the gen_fsm.</p>
+ <p>The function should return <c>Status</c>, a term that
+ customises the details of the current state and status of
+ the gen_fsm. There are no restrictions on the
+ form <c>Status</c> can take, but for
+ the <c>sys:get_status/1,2</c> case (when <c>Opt</c>
+ is <c>normal</c>), the recommended form for
+ the <c>Status</c> value is <c>[{data, [{"StateData",
+ Term}]}]</c> where <c>Term</c> provides relevant details of
+ the gen_fsm state data. Following this recommendation isn't
+ required, but doing so will make the callback module status
+ consistent with the rest of the <c>sys:get_status/1,2</c>
+ return value.</p>
+ <p>One use for this function is to return compact alternative
+ state data representations to avoid having large state terms
+ printed in logfiles.</p>
</desc>
</func>
</funcs>
@@ -770,4 +795,3 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<seealso marker="sys">sys(3)</seealso></p>
</section>
</erlref>
-
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index 30c04d1d52..1045766e01 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>gen_server</title>
@@ -599,32 +599,57 @@ gen_server:abcast -----> Module:handle_cast/2
</desc>
</func>
<func>
- <name>Module:format_status(normal, [PDict, State]) -> Status</name>
+ <name>Module:format_status(Opt, [PDict, State]) -> Status</name>
<fsummary>Optional function for providing a term describing the
current gen_server status.</fsummary>
<type>
+ <v>Opt = normal | terminate</v>
<v>PDict = [{Key, Value}]</v>
<v>State = term()</v>
- <v>Status = [term()]</v>
+ <v>Status = term()</v>
</type>
<desc>
- <p><em>This callback is optional, so callback modules need not
- export it. The gen_server module provides a default
- implementation of this function that returns the callback
- module state.</em></p>
- <p>This function is called by a gen_server process when one
+ <note>
+ <p>This callback is optional, so callback modules need not
+ export it. The gen_server module provides a default
+ implementation of this function that returns the callback
+ module state.</p>
+ </note>
+ <p>This function is called by a gen_server process when:</p>
+ <list typed="bulleted">
+ <item>One
of <seealso marker="sys#get_status/1">sys:get_status/1,2</seealso>
- is invoked to get the gen_server status. A callback module
- wishing to customise the <c>sys:get_status/1,2</c> return
- value exports an instance of <c>format_status/2</c> that
- returns a term describing the current status of the
- gen_server.</p>
+ is invoked to get the gen_server status. <c>Opt</c> is set
+ to the atom <c>normal</c> for this case.</item>
+ <item>The gen_server terminates abnormally and logs an
+ error. <c>Opt</c> is set to the atom <c>terminate</c> for this
+ case.</item>
+ </list>
+ <p>This function is useful for customising the form and
+ appearance of the gen_server status for these cases. A
+ callback module wishing to customise
+ the <c>sys:get_status/1,2</c> return value as well as how
+ its status appears in termination error logs exports an
+ instance of <c>format_status/2</c> that returns a term
+ describing the current status of the gen_server.</p>
<p><c>PDict</c> is the current value of the gen_server's
process dictionary.</p>
<p><c>State</c> is the internal state of the gen_server.</p>
- <p>The function should return <c>Status</c>, a list of one or
- more terms that customise the details of the current state
- and status of the gen_server.</p>
+ <p>The function should return <c>Status</c>, a term that
+ customises the details of the current state and status of
+ the gen_server. There are no restrictions on the
+ form <c>Status</c> can take, but for
+ the <c>sys:get_status/1,2</c> case (when <c>Opt</c>
+ is <c>normal</c>), the recommended form for
+ the <c>Status</c> value is <c>[{data, [{"State",
+ Term}]}]</c> where <c>Term</c> provides relevant details of
+ the gen_server state. Following this recommendation isn't
+ required, but doing so will make the callback module status
+ consistent with the rest of the <c>sys:get_status/1,2</c>
+ return value.</p>
+ <p>One use for this function is to return compact alternative
+ state representations to avoid having large state terms
+ printed in logfiles.</p>
</desc>
</func>
</funcs>
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index efbb1fc078..41e3e92c59 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2010</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -81,7 +81,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</section>
<funcs>
<func>
- <name>columns([IoDevice]) -> {ok,int()} | {error, enotsup}</name>
+ <name>columns() -> {ok,int()} | {error, enotsup}</name>
+ <name>columns(IoDevice) -> {ok,int()} | {error, enotsup}</name>
<fsummary>Get the number of columns of a device</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -94,7 +95,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>put_chars([IoDevice,] IoData) -> ok</name>
+ <name>put_chars(IoData) -> ok</name>
+ <name>put_chars(IoDevice, IoData) -> ok</name>
<fsummary>Write a list of characters</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -106,7 +108,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>nl([IoDevice]) -> ok</name>
+ <name>nl() -> ok</name>
+ <name>nl(IoDevice) -> ok</name>
<fsummary>Write a newline</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -116,7 +119,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>get_chars([IoDevice,] Prompt, Count) -> Data | eof</name>
+ <name>get_chars(Prompt, Count) -> Data | eof</name>
+ <name>get_chars(IoDevice, Prompt, Count) -> Data | eof</name>
<fsummary>Read a specified number of characters</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -150,7 +154,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>get_line([IoDevice,] Prompt) -> Data | eof | {error,Reason}</name>
+ <name>get_line(Prompt) -> Data | eof | {error,Reason}</name>
+ <name>get_line(IoDevice, Prompt) -> Data | eof | {error,Reason}</name>
<fsummary>Read a line</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -183,7 +188,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>getopts([IoDevice]) -> Opts</name>
+ <name>getopts() -> Opts</name>
+ <name>getopts(IoDevice) -> Opts</name>
<fsummary>Get the supported options and values from an I/O-server</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -210,7 +216,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>setopts([IoDevice,] Opts) -> ok | {error, Reason}</name>
+ <name>setopts(Opts) -> ok | {error, Reason}</name>
+ <name>setopts(IoDevice, Opts) -> ok | {error, Reason}</name>
<fsummary>Set options</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -281,7 +288,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>write([IoDevice,] Term) -> ok</name>
+ <name>write(Term) -> ok</name>
+ <name>write(IoDevice, Term) -> ok</name>
<fsummary>Write a term</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -293,7 +301,8 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</desc>
</func>
<func>
- <name>read([IoDevice,] Prompt) -> Result</name>
+ <name>read(Prompt) -> Result</name>
+ <name>read(IoDevice, Prompt) -> Result</name>
<fsummary>Read a term</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -356,9 +365,11 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
</func>
<func>
<name>fwrite(Format) -></name>
- <name>fwrite([IoDevice,] Format, Data) -> ok</name>
+ <name>fwrite(Format, Data) -> ok</name>
+ <name>fwrite(IoDevice, Format, Data) -> ok</name>
<name>format(Format) -></name>
- <name>format([IoDevice,] Format, Data) -> ok</name>
+ <name>format(Format, Data) -> ok</name>
+ <name>format(IoDevice, Format, Data) -> ok</name>
<fsummary>Write formatted output</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -464,9 +475,9 @@ ok</pre>
<p>Prints the argument with the <c>string</c> syntax. The
argument is, if no Unicode translation modifier is present, an
<seealso marker="erts:erlang#iolist_definition">I/O list</seealso>, a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is chardata(), meaning that binaries are in UTF-8. The characters
- are printed without quotes. In this format, the printed
- argument is truncated to the given precision and field
- width.</p>
+ are printed without quotes. The string is first truncated
+ by the given precision and then padded and justified
+ to the given field width. The default precision is the field width.</p>
<p>This format can be used for printing any object and
truncating the output so it fits a specified field:</p>
<pre>
@@ -475,6 +486,8 @@ ok</pre>
ok
4> <input>io:fwrite("|~10s|~n", [io_lib:write({hey, hey, hey})]).</input>
|{hey,hey,h|
+5> <input>io:fwrite("|~-10.8s|~n", [io_lib:write({hey, hey, hey})]).</input>
+|{hey,hey |
ok</pre>
<p>A list with integers larger than 255 is considered an error if the Unicode translation modifier is not given:</p>
<pre>
@@ -660,7 +673,8 @@ ok
</desc>
</func>
<func>
- <name>fread([IoDevice,] Prompt, Format) -> Result</name>
+ <name>fread(Prompt, Format) -> Result</name>
+ <name>fread(IoDevice, Prompt, Format) -> Result</name>
<fsummary>Read formatted input</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -820,7 +834,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
</desc>
</func>
<func>
- <name>rows([IoDevice]) -> {ok,int()} | {error, enotsup}</name>
+ <name>rows() -> {ok,int()} | {error, enotsup}</name>
+ <name>rows(IoDevice) -> {ok,int()} | {error, enotsup}</name>
<fsummary>Get the number of rows of a device</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -834,7 +849,8 @@ enter><input>:</input> <input>alan</input> <input>:</input> <input>joe</in
</func>
<func>
<name>scan_erl_exprs(Prompt) -></name>
- <name>scan_erl_exprs([IoDevice,] Prompt, StartLine) -> Result</name>
+ <name>scan_erl_exprs(Prompt, StartLine) -> Result</name>
+ <name>scan_erl_exprs(IoDevice, Prompt, StartLine) -> Result</name>
<fsummary>Read and tokenize Erlang expressions</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -877,7 +893,8 @@ enter><input>1.0er.</input>
</func>
<func>
<name>scan_erl_form(Prompt) -></name>
- <name>scan_erl_form([IoDevice,] Prompt, StartLine) -> Result</name>
+ <name>scan_erl_form(Prompt, StartLine) -> Result</name>
+ <name>scan_erl_form(IoDevice, Prompt, StartLine) -> Result</name>
<fsummary>Read and tokenize an Erlang form</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -900,7 +917,8 @@ enter><input>1.0er.</input>
</func>
<func>
<name>parse_erl_exprs(Prompt) -></name>
- <name>parse_erl_exprs([IoDevice,] Prompt, StartLine) -> Result</name>
+ <name>parse_erl_exprs(Prompt, StartLine) -> Result</name>
+ <name>parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result</name>
<fsummary>Read, tokenize and parse Erlang expressions</fsummary>
<type>
<v>IoDevice = io_device()</v>
@@ -943,7 +961,8 @@ enter><input>abc("hey".</input>
</func>
<func>
<name>parse_erl_form(Prompt) -></name>
- <name>parse_erl_form([IoDevice,] Prompt, StartLine) -> Result</name>
+ <name>parse_erl_form(Prompt, StartLine) -> Result</name>
+ <name>parse_erl_form(IoDevice, Prompt, StartLine) -> Result</name>
<fsummary>Read, tokenize and parse an Erlang form</fsummary>
<type>
<v>IoDevice = io_device()</v>
diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml
index b52e862a5c..3e8ab1affc 100644
--- a/lib/stdlib/doc/src/io_protocol.xml
+++ b/lib/stdlib/doc/src/io_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2009</year>
+ <year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -79,7 +79,7 @@ sends the reply to.</item>
io_reply. The io-module in the Erlang standard library simply uses the pid()
of the io_server as the ReplyAs datum, but a more complicated client
could have several outstanding io-requests to the same server and
-would then use i.e. a ref() or something else to differentiate among
+would then use i.e. a reference() or something else to differentiate among
the incoming io_reply's. The ReplyAs element should be considered
opaque by the io_server. Note that the pid() of the server is not
explicitly present in the io_reply. The reply can be sent from any
diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml
index a273a2301f..92c4eb4f4c 100644
--- a/lib/stdlib/doc/src/lists.xml
+++ b/lib/stdlib/doc/src/lists.xml
@@ -48,7 +48,7 @@
<item><p>if x <c>F</c> y and y <c>F</c> x then x = y (<c>F</c>
is antisymmetric);</p>
</item>
- <item><p>if x <c>F</c> y and and y <c>F</c> z then x <c>F</c> z
+ <item><p>if x <c>F</c> y and y <c>F</c> z then x <c>F</c> z
(<c>F</c> is transitive);</p>
</item>
<item><p>x <c>F</c> y or y <c>F</c> x (<c>F</c> is total).</p>
@@ -220,7 +220,7 @@
follows:</p>
<code type="none">
flatmap(Fun, List1) ->
- append(map(Fun, List1))</code>
+ append(map(Fun, List1)).</code>
<p>Example:</p>
<pre>
> <input>lists:flatmap(fun(X)->[X,X] end, [a,b,c]).</input>
@@ -523,7 +523,7 @@ flatmap(Fun, List1) ->
<v>&nbsp;A = B = term()</v>
</type>
<desc>
- <p><c>mapfold</c> combines the operations of <c>map/2</c> and
+ <p><c>mapfoldl</c> combines the operations of <c>map/2</c> and
<c>foldl/3</c> into one pass. An example, summing
the elements in a list and double them at the same time:</p>
<pre>
@@ -543,7 +543,7 @@ flatmap(Fun, List1) ->
<v>&nbsp;A = B = term()</v>
</type>
<desc>
- <p><c>mapfold</c> combines the operations of <c>map/2</c> and
+ <p><c>mapfoldr</c> combines the operations of <c>map/2</c> and
<c>foldr/3</c> into one pass.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/log_mf_h.xml b/lib/stdlib/doc/src/log_mf_h.xml
index 198a55a63b..f8e11339a7 100644
--- a/lib/stdlib/doc/src/log_mf_h.xml
+++ b/lib/stdlib/doc/src/log_mf_h.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml
index 990a6b4024..02e4d6e495 100644
--- a/lib/stdlib/doc/src/math.xml
+++ b/lib/stdlib/doc/src/math.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 28a3719d38..8cd499f960 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -30,6 +30,648 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 1.17.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Two bugs in io:format for ~F.~Ps has been corrected. When
+ length(S) >= abs(F) > P, the precision P was incorrectly
+ ignored. When F == P > lenght(S) the result was
+ incorrectly left adjusted. Bug found by Ali Yakout who
+ also provided a fix.</p>
+ <p>
+ Own Id: OTP-8989 Aux Id: seq11741 </p>
+ </item>
+ <item>
+ <p>Fix exception generation in the io module
+ <p>
+ Some functions did not generate correct badarg exception
+ on a badarg exception.</p></p>
+ <p>
+ Own Id: OTP-9045</p>
+ </item>
+ <item>
+ <p>
+ Fixes to the dict and orddict module documentation</p>
+ <p>
+ Fixed grammar and one inconsistency (Key - Value instead
+ of key/value, since everywhere else the former is used).
+ (thanks to Filipe David Manana)</p>
+ <p>
+ Own Id: OTP-9083</p>
+ </item>
+ <item>
+ <p>
+ Add ISO week number calculation functions to the calendar
+ module in stdlib</p>
+ <p>
+ This new feature adds the missing week number function to
+ the calendar module of the stdlib application. The
+ implementation conforms to the ISO 8601 standard. The new
+ feature has been implemented tested and documented
+ (thanks to Imre Horvath).</p>
+ <p>
+ Own Id: OTP-9087</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Implement the 'MAY' clauses from RFC4648 regarding the
+ pad character to make mime_decode() and
+ mime_decode_to_string() functions more tolerant of badly
+ padded base64. The RFC is quoted below for easy
+ reference.</p>
+ <p>
+ "RFC4648 Section 3.3 with reference to MIME decoding:
+ Furthermore, such specifications MAY ignore the pad
+ character, "=", treating it as non-alphabet data, if it
+ is present before the end of the encoded data. If more
+ than the allowed number of pad characters is found at the
+ end of the string (e.g., a base 64 string terminated with
+ "==="), the excess pad characters MAY also be ignored."</p>
+ <p>
+ Own Id: OTP-9020</p>
+ </item>
+ <item>
+ <p>
+ Supervisors will no longer save start parameters for
+ temporary processes as they will not be restarted. In the
+ case of simple_one_for_one workers such as ssl-connection
+ processes this will substantial reduce the memory
+ footprint of the supervisor.</p>
+ <p>
+ Own Id: OTP-9064</p>
+ </item>
+ <item>
+ <p>
+ When running escript it is now possible to add the -n
+ flag and the escript will be compiled using +native.</p>
+ <p>
+ Own Id: OTP-9076</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.17.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Several type specifications for standard libraries were
+ wrong in the R14B01 release. This is now corrected. The
+ corrections concern types in re,io,filename and the
+ module erlang itself.</p>
+ <p>
+ Own Id: OTP-9008</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.17.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> When several clients accessed a Dets table
+ simultaneously, one of them calling
+ <c>dets:insert_new/2</c>, the Dets server could crash.
+ Alternatively, under the same conditions, <c>ok</c> was
+ sometimes returned instead of <c>true</c>. (Thanks to
+ John Hughes.) </p>
+ <p>
+ Own Id: OTP-8856</p>
+ </item>
+ <item>
+ <p> When several clients accessed a Dets table
+ simultaneously, inserted or updated objects were
+ sometimes lost due to the Dets file being truncated.
+ (Thanks to John Hughes.) </p>
+ <p>
+ Own Id: OTP-8898</p>
+ </item>
+ <item>
+ <p> When several clients accessed a Dets table
+ simultaneously, modifications of the Dets server's
+ internal state were sometimes thrown away. The symptoms
+ are diverse: error with reason <c>bad_object</c>;
+ inserted objects not returned by <c>lookup()</c>; et
+ cetera. (Thanks to John Hughes.) </p>
+ <p>
+ Own Id: OTP-8899</p>
+ </item>
+ <item>
+ <p> If a Dets table was closed after calling
+ <c>bchunk/2</c>, <c>match/1,3</c>,
+ <c>match_object/1,3</c>, or <c>select/1,3</c> and then
+ opened again, a subsequent call using the returned
+ continuation would normally return a reply. This bug has
+ fixed; now the call fails with reason <c>badarg</c>. </p>
+ <p>
+ Own Id: OTP-8903</p>
+ </item>
+ <item>
+ <p> Cover did not collect coverage data for files such as
+ Yecc parses containing include directives. The bug has
+ been fixed by modifying <c>epp</c>, the Erlang Code
+ Preprocessor. </p>
+ <p>
+ Own Id: OTP-8911</p>
+ </item>
+ <item>
+ <p> If a Dets table with fewer slots than keys was opened
+ and then closed after just a lookup, the contents were no
+ longer well-formed. This bug has been fixed. (Thanks to
+ Matthew Evans.) </p>
+ <p>
+ Own Id: OTP-8923</p>
+ </item>
+ <item>
+ <p>
+ In a supervisor, when it terminates a child, if that
+ child happens to have exited fractionally early, with
+ normal, the supervisor reports this as an error. This
+ should not be reported as an error.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8938 Aux Id: seq11615 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation filelib:wildcard/1,2 now describes the
+ character set syntax for wildcards.</p>
+ <p>
+ Own Id: OTP-8879 Aux Id: seq11683 </p>
+ </item>
+ <item>
+ <p>Buffer overflows have been prevented in <c>erlc</c>,
+ <c>dialyzer</c>, <c>typer</c>, <c>run_test</c>,
+ <c>heart</c>, <c>escript</c>, and <c>erlexec</c>.</p>
+ (Thanks to Michael Santos.)
+ <p>
+ Own Id: OTP-8892</p>
+ </item>
+ <item>
+ <p>
+ Using a float for the number of copies for
+ <c>string:copies/2</c> resulted in an infinite loop. Now
+ it will fail with an exception instead. (Thanks to
+ Michael Santos.)</p>
+ <p>
+ Own Id: OTP-8915</p>
+ </item>
+ <item>
+ <p>
+ New ETS option <c>compressed</c>, to enable a more
+ compact storage format at the expence of heavier table
+ operations. For test and evaluation, <c>erl +ec</c> can
+ be used to force compression on all ETS tables.</p>
+ <p>
+ Own Id: OTP-8922 Aux Id: seq11658 </p>
+ </item>
+ <item>
+ <p> The default maximum number of slots of a Dets table
+ has been changed as to be equal to the maximum number of
+ slots. (Thanks to Richard Carlsson.) </p>
+ <p>
+ Own Id: OTP-8959</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.17.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>reference() has been substituted for ref() in the
+ documentation.</p>
+ <p>
+ Own Id: OTP-8733</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The ms_transform now warns if the fun head shadows
+ surrounding variables (just like the warnings you would
+ get for an ordinary fun in the same context).</p>
+ <p>
+ Own Id: OTP-6759</p>
+ </item>
+ <item>
+ <p>
+ ets:select_reverse/{1,2,3} are now documented.</p>
+ <p>
+ Own Id: OTP-7863</p>
+ </item>
+ <item>
+ <p>
+ Large parts of the <c>ethread</c> library have been
+ rewritten. The <c>ethread</c> library is an Erlang
+ runtime system internal, portable thread library used by
+ the runtime system itself.</p>
+ <p>
+ Most notable improvement is a reader optimized rwlock
+ implementation which dramatically improve the performance
+ of read-lock/read-unlock operations on multi processor
+ systems by avoiding ping-ponging of the rwlock cache
+ lines. The reader optimized rwlock implementation is used
+ by miscellaneous rwlocks in the runtime system that are
+ known to be read-locked frequently, and can be enabled on
+ ETS tables by passing the <seealso
+ marker="stdlib:ets#new_2_read_concurrency">{read_concurrency,
+ true}</seealso> option upon table creation. See the
+ documentation of <seealso
+ marker="stdlib:ets#new/2">ets:new/2</seealso> for more
+ information. The reader optimized rwlock implementation
+ can be fine tuned when starting the runtime system. For
+ more information, see the documentation of the <seealso
+ marker="erts:erl#+rg">+rg</seealso> command line argument
+ of <c>erl</c>.</p>
+ <p>
+ There is also a new implementation of rwlocks that is not
+ optimized for readers. Both implementations interleaves
+ readers and writers during contention as opposed to,
+ e.g., the NPTL (Linux) pthread rwlock implementation
+ which use either a reader or writer preferred strategy.
+ The reader/writer preferred strategy is problematic since
+ it starves threads doing the non-preferred operation.</p>
+ <p>
+ The new rwlock implementations in general performs better
+ in ERTS than common pthread implementations. However, in
+ some extremely heavily contended cases this is not the
+ case. Such heavy contention can more or less only appear
+ on ETS tables. This when multiple processes do very large
+ amounts of write locked operations simultaneously on the
+ same table. Such use of ETS is bad regardless of rwlock
+ implementation, will never scale, and is something we
+ strongly advise against.</p>
+ <p>
+ The new rwlock implementations depend on atomic
+ operations. If no native atomic implementation is found,
+ a fallback solution will be used. Using the fallback
+ implies a performance degradation. That is, it is more
+ important now than before to build OTP with a native
+ atomic implementation.</p>
+ <p>
+ The <c>ethread</c> library contains native atomic
+ implementations for, x86 (32 and 64 bit), powerpc (32
+ bit), sparc V9 (32 and 64 bit), and tilera (32 bit). On
+ other hardware gcc's builtin support for atomic memory
+ access will be used if such exists. If no such support is
+ found, <c>configure</c> will warn about no atomic
+ implementation available.</p>
+ <p>
+ The <c>ethread</c> library can now also use the
+ <c>libatomic_ops</c> library for atomic memory accesses.
+ This makes it possible for the Erlang runtime system to
+ utilize optimized native atomic operations on more
+ platforms than before. If <c>configure</c> warns about no
+ atomic implementation available, try using the
+ <c>libatomic_ops</c> library. Use the <seealso
+ marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--with-libatomic_ops=PATH</seealso>
+ <c>configure</c> command line argument when specifying
+ where the <c>libatomic_ops</c> installation is located.
+ The <c>libatomic_ops</c> library can be downloaded from:
+ <url
+ href="http://www.hpl.hp.com/research/linux/atomic_ops/">http://www.hpl.hp.com/research/linux/atomic_ops/</url></p>
+ <p>
+ The changed API of the <c>ethread</c> library has also
+ caused modifications in the Erlang runtime system.
+ Preparations for the to come "delayed deallocation"
+ feature has also been done since it depends on the
+ <c>ethread</c> library.</p>
+ <p>
+ <em>Note</em>: When building for x86, the <c>ethread</c>
+ library will now use instructions that first appeared on
+ the pentium 4 processor. If you want the runtime system
+ to be compatible with older processors (back to 486) you
+ need to pass the <seealso
+ marker="doc/installation_guide:INSTALL#How-to-Build-and-Install-ErlangOTP_A-Closer-Look-at-the-individual-Steps_Configuring">--enable-ethread-pre-pentium4-compatibility</seealso>
+ <c>configure</c> command line argument when configuring
+ the system.</p>
+ <p>
+ Own Id: OTP-8544</p>
+ </item>
+ <item>
+ <p>
+ Some Built In Functions (BIFs) from the module erlang was
+ never made autoimported for backward compatibility
+ reasons. As local functions now override autoimports, new
+ autoimports is no longer a problem, why the following
+ BIFs are finally made autoimported: monitor/2, monitor/3,
+ demonitor/2, demonitor/3, error/1, error/2,
+ integer_to_list/2, list_to_integer/2.</p>
+ <p>
+ Own Id: OTP-8763</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>STDLIB 1.17</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The Erlang code preprocessor (<c>epp</c>) sent extra
+ messages on the form <c>{eof,Location}</c> to the client
+ when parsing the <c>file</c> attribute. This bug,
+ introduced in R11B, has been fixed.</p>
+ <p>
+ Own Id: OTP-8470</p>
+ </item>
+ <item>
+ <p>The abstract type 'fun' could not be printed by the
+ Erlang pretty printer (<c>erl_pp</c>). This bug has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-8473</p>
+ </item>
+ <item>
+ <p>The function <c>erl_scan:reserved_word/1</c> no longer
+ returns <c>true</c> when given the word <c>spec</c>. This
+ bug was introduced in STDLIB-1.15.3 (R12B-3).</p>
+ <p>
+ Own Id: OTP-8567</p>
+ </item>
+ <item>
+ <p>The documentation of <c>lists:keysort/2</c> states
+ that the sort is stable.</p>
+ <p>
+ Own Id: OTP-8628 Aux Id: seq11576 </p>
+ </item>
+ <item>
+ <p>
+ The shell's line editing has been improved to more
+ resemble the behaviour of readline and other shells.
+ (Thanks to Dave Peticolas)</p>
+ <p>
+ Own Id: OTP-8635</p>
+ </item>
+ <item>
+ <p>The Erlang code preprocessor (<c>epp</c>) did not
+ correctly handle premature end-of-input when defining
+ macros. This bug, introduced in STDLIB 1.16, has been
+ fixed.</p>
+ <p>
+ Own Id: OTP-8665 Aux Id: OTP-7810 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The module binary from EEP31 (and EEP9) is implemented.</p>
+ <p>
+ Own Id: OTP-8217</p>
+ </item>
+ <item>
+ <p>The erlang pretty printer (<c>erl_pp</c>) no longer
+ quotes atoms in types.</p>
+ <p>
+ Own Id: OTP-8501</p>
+ </item>
+ <item>
+ <p>The Erlang code preprocessor (<c>epp</c>) now
+ considers records with no fields as typed.</p>
+ <p>
+ Own Id: OTP-8503</p>
+ </item>
+ <item>
+ <p>
+ Added function <c>zip:foldl/3</c> to iterate over zip
+ archives.</p>
+ <p>
+ Added functions to create and extract escripts. See
+ <c>escript:create/2</c> and <c>escript:extract/2</c>.</p>
+ <p>
+ The undocumented function <c>escript:foldl/3</c> has been
+ removed. The same functionality can be achieved with the
+ more flexible functions <c>escript:extract/2</c> and
+ <c>zip:foldl/3</c>.</p>
+ <p>
+ Record fields has been annotated with type info. Source
+ files as been adapted to fit within 80 chars and trailing
+ whitespace has been removed.</p>
+ <p>
+ Own Id: OTP-8521</p>
+ </item>
+ <item>
+ <p>The Erlang parser no longer duplicates the singleton
+ type <c>undefined</c> in the type of record fields
+ without initial value.</p>
+ <p>
+ Own Id: OTP-8522</p>
+ </item>
+ <item>
+ <p>A regular expression with many levels of parenthesis
+ could cause a buffer overflow. That has been corrected.
+ (Thanks to Michael Santos.)</p>
+ <p>
+ Own Id: OTP-8539</p>
+ </item>
+ <item>
+ <p>When defining macros the closing right parenthesis
+ before the dot is now mandatory.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8562</p>
+ </item>
+ <item>
+ <p>
+ Some properties of a compiled re pattern are defined to
+ allow for guard tests.</p>
+ <p>
+ Own Id: OTP-8577</p>
+ </item>
+ <item>
+ <p>Local and imported functions now override the
+ auto-imported BIFs when the names clash. The pre R14
+ behaviour was that auto-imported BIFs would override
+ local functions. To avoid that old programs change
+ behaviour, the following will generate an error:</p>
+ <list><item><p>Doing a call without explicit module name
+ to a local function having a name clashing with the name
+ of an auto-imported BIF that was present (and
+ auto-imported) before OTP R14A</p></item>
+ <item><p>Explicitly importing a function having a name
+ clashing with the name of an autoimported BIF that was
+ present (and autoimported) before OTP R14A</p></item>
+ <item><p>Using any form of the old compiler directive
+ <c>nowarn_bif_clash</c></p></item> </list> <p>If the BIF
+ was added or auto-imported in OTP R14A or later,
+ overriding it with an import or a local function will
+ only result in a warning,</p> <p>To resolve clashes, you
+ can either use the explicit module name <c>erlang</c> to
+ call the BIF, or you can remove the auto-import of that
+ specific BIF by using the new compiler directive
+ <c>-compile({no_auto_import,[F/A]}).</c>, which makes all
+ calls to the local or imported function without explicit
+ module name pass without warnings or errors.</p> <p>The
+ change makes it possible to add auto-imported BIFs
+ without breaking or silently changing old code in the
+ future. However some current code ingeniously utilizing
+ the old behaviour or the <c>nowarn_bif_clash</c> compiler
+ directive, might need changing to be accepted by the
+ compiler.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8579</p>
+ </item>
+ <item>
+ <p>The undocumented, unsupport, and deprecated function
+ <c>lists:flat_length/1</c> has been removed.</p>
+ <p>
+ Own Id: OTP-8584</p>
+ </item>
+ <item>
+ <p>
+ A bug in re that could cause certain regular expression
+ matches never to terminate is corrected. (Thanks to
+ Michael Santos and Gordon Guthrie.)</p>
+ <p>
+ Own Id: OTP-8589</p>
+ </item>
+ <item>
+ <p>Nested records can now be accessed without
+ parenthesis. See the Reference Manual for examples.
+ (Thanks to YAMASHINA Hio and Tuncer Ayaz.)</p>
+ <p>
+ Own Id: OTP-8597</p>
+ </item>
+ <item>
+ <p><c>receive</c> statements that can only read out a
+ newly created reference are now specially optimized so
+ that it will execute in constant time regardless of the
+ number of messages in the receive queue for the process.
+ That optimization will benefit calls to
+ <c>gen_server:call()</c>. (See <c>gen:do_call/4</c> for
+ an example of a receive statement that will be
+ optimized.)</p>
+ <p>
+ Own Id: OTP-8623</p>
+ </item>
+ <item>
+ <p>The beam_lib:cmp/2 function now compares BEAM files in
+ stricter way. The BEAM files will be considered different
+ if there are any changes except in the compilation
+ information ("CInf") chunk. beam_lib:cmp/2 used to ignore
+ differences in the debug information (significant for
+ Dialyzer) and other chunks that did not directly change
+ the run-time behavior.</p>
+ <p>
+ Own Id: OTP-8625</p>
+ </item>
+ <item>
+ <p>
+ When a gen_server, gen_fsm process, or gen_event
+ terminates abnormally, sometimes the text representation
+ of the process state can occupy many lines of the error
+ log, depending on the definition of the state term. A
+ mechanism to trim out parts of the state from the log has
+ been added (using a format_status/2 callback). See the
+ documentation.</p>
+ <p>
+ Own Id: OTP-8630</p>
+ </item>
+ <item>
+ <p>
+ Calling <c>sys:get_status()</c> for processes that have
+ globally registered names that were not atoms would cause
+ a crash. Corrected. (Thanks to Steve Vinoski.)</p>
+ <p>
+ Own Id: OTP-8656</p>
+ </item>
+ <item>
+ <p>The Erlang scanner has been augmented with two new
+ tokens: <c>..</c> and <c>...</c>.</p>
+ <p>
+ Own Id: OTP-8657</p>
+ </item>
+ <item>
+ <p>Expressions evaluating to integers can now be used in
+ types and function specifications where hitherto only
+ integers were allowed ("Erlang_Integer").</p>
+ <p>
+ Own Id: OTP-8664</p>
+ </item>
+ <item>
+ <p>The compiler optimizes record operations better.</p>
+ <p>
+ Own Id: OTP-8668</p>
+ </item>
+ <item>
+ <p>
+ The recently added BIFs erlang:min/2, erlang:max/2 and
+ erlang:port_command/3 are now auto-imported (as they were
+ originally intended to be). Due to the recent compiler
+ change (OTP-8579), the only impact on old code defining
+ it's own min/2, max/2 or port_command/3 functions will be
+ a warning, the local functions will still be used. The
+ warning can be removed by using
+ -compile({no_auto_import,[min/2,max/2,port_command/3]}).
+ in the source file.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8669 Aux Id: OTP-8579 </p>
+ </item>
+ <item>
+ <p>
+ Now, binary_to_term/2 is auto-imported. This will cause a
+ compile warning if and only if a module has got a local
+ function with that name.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8671</p>
+ </item>
+ <item>
+ <p>
+ The predefined builtin type tid() has been removed.
+ Instead, ets:tid() should be used.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8687</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 1.16.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml
index 08c808f822..1b8b74534b 100644
--- a/lib/stdlib/doc/src/orddict.xml
+++ b/lib/stdlib/doc/src/orddict.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2000</year><year>2009</year>
+ <year>2000</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -172,8 +172,8 @@ ordered_dictionary()
<v>Orddict = ordered_dictionary()</v>
</type>
<desc>
- <p>This function converts the key/value list <c>List</c> to a
- dictionary.</p>
+ <p>This function converts the <c>Key</c> - <c>Value</c> list
+ <c>List</c> to a dictionary.</p>
</desc>
</func>
<func>
@@ -277,7 +277,7 @@ merge(Fun, D1, D2) ->
<v>Orddict1 = Orddict2 = ordered_dictionary()</v>
</type>
<desc>
- <p>Update the a value in a dictionary by calling <c>Fun</c> on
+ <p>Update a value in a dictionary by calling <c>Fun</c> on
the value to get a new value. An exception is generated if
<c>Key</c> is not present in the dictionary.</p>
</desc>
@@ -292,7 +292,7 @@ merge(Fun, D1, D2) ->
<v>Orddict1 = Orddict2 = ordered_dictionary()</v>
</type>
<desc>
- <p>Update the a value in a dictionary by calling <c>Fun</c> on
+ <p>Update a value in a dictionary by calling <c>Fun</c> on
the value to get a new value. If <c>Key</c> is not present
in the dictionary then <c>Initial</c> will be stored as
the first value. For example <c>append/3</c> could be defined
diff --git a/lib/stdlib/doc/src/part_notes_history.xml b/lib/stdlib/doc/src/part_notes_history.xml
index 744b009583..5e055ee606 100644
--- a/lib/stdlib/doc/src/part_notes_history.xml
+++ b/lib/stdlib/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/pg.xml b/lib/stdlib/doc/src/pg.xml
index 66b9702ae0..b174d4f7d4 100644
--- a/lib/stdlib/doc/src/pg.xml
+++ b/lib/stdlib/doc/src/pg.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml
index 4d2a0e0995..9091035392 100644
--- a/lib/stdlib/doc/src/re.xml
+++ b/lib/stdlib/doc/src/re.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -37,29 +37,24 @@
<modulesummary>Perl like regular expressions for Erlang</modulesummary>
<description>
- <p>This module contains functions for regular expression
- matching for strings and binaries.</p>
+ <p>This module contains regular expression matching functions for
+ strings and binaries.</p>
<p>The regular expression syntax and semantics resemble that of
- Perl. This library in many ways replaces the old regexp library
- written purely in Erlang, as it has a richer syntax as well as
- many more options. The library is also faster than the
- older regexp implementation.</p>
-
- <p>Although the library's matching algorithms are currently based
- on the PCRE library, it is not to be viewed as an Erlang to PCRE
- mapping. Only parts of the PCRE library is interfaced and the re
- library in some ways extend PCRE. The PCRE documentation contains
- many parts of no interest to the Erlang programmer, why only the
- relevant part of the documentation is included here. There should
- bee no need to go directly to the PCRE library documentation.</p>
+ Perl. This library replaces the deprecated pure-Erlang regexp
+ library; it has a richer syntax, more options and is faster.</p>
+
+ <p>The library's matching algorithms are currently based on the
+ PCRE library, but not all of the PCRE library is interfaced and
+ some parts of the library go beyond what PCRE offers. The sections of
+ the PCRE documentation which are relevant to this module are included
+ here.</p>
<note>
- <p>The Erlang literal syntax for strings give special
- meaning to the &quot;\&quot; (backslash) character. To literally write
- a regular expression or a replacement string containing a
- backslash in your code or in the shell, two backslashes have to be written:
- &quot;\\&quot;.</p>
+ <p>The Erlang literal syntax for strings uses the &quot;\&quot;
+ (backslash) character as an escape code. You need to escape
+ backslashes in literal strings, both in your code and in the shell,
+ with an additional backslash, i.e.: &quot;\\&quot;.</p>
</note>
@@ -72,7 +67,7 @@
- a binary is allowed as the tail of the list</code>
<code type="none">
unicode_binary() = binary() with characters encoded in UTF-8 coding standard
- unicode_char() = integer() representing valid unicode codepoint
+ unicode_char() = integer() representing a valid unicode codepoint
chardata() = charlist() | unicode_binary()
@@ -80,7 +75,11 @@
- a unicode_binary is allowed as the tail of the list</code>
<code type="none">
- mp() = Opaque datatype containing a compiled regular expression.</code>
+ mp() = Opaque datatype containing a compiled regular expression.
+ - The mp() is guaranteed to be a tuple() having the atom
+ 're_pattern' as its first element, to allow for matching in
+ guards. The arity of the tuple() or the content of the other fields
+ may change in future releases.</code>
</section>
<funcs>
<func>
@@ -128,7 +127,7 @@
<tag><c>dollar_endonly</c></tag>
<item>A dollar metacharacter in the pattern matches only at the end of the subject string. Without this option, a dollar also matches immediately before a newline at the end of the string (but not before any other newlines). The <c>dollar_endonly</c> option is ignored if <c>multiline</c> is given. There is no equivalent option in Perl, and no way to set it within a pattern.</item>
<tag><c>dotall</c></tag>
- <item>A dot maturate in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) option setting. A negative class such as [^a] always matches newline characters, independent of the setting of this option.</item>
+ <item>A dot in the pattern matches all characters, including those that indicate newline. Without it, a dot does not match when the current position is at a newline. This option is equivalent to Perl's /s option, and it can be changed within a pattern by a (?s) option setting. A negative class such as [^a] always matches newline characters, independent of this option's setting.</item>
<tag><c>extended</c></tag>
<item>Whitespace data characters in the pattern are ignored except when escaped or inside a character class. Whitespace does not include the VT character (ASCII 11). In addition, characters between an unescaped # outside a character class and the next newline, inclusive, are also ignored. This is equivalent to Perl's /x option, and it can be changed within a pattern by a (?x) option setting.
@@ -210,9 +209,10 @@ This option makes it possible to include comments inside complicated patterns. N
or as a pre compiled <c>mp()</c> in which case it is executed
against the subject directly.</p>
- <p>When compilation is involved, the exception <c>badarg</c> is thrown if
- a compilation error occurs. To locate the error in the regular
- expression, use the function <c>re:compile/2</c> to get more information.</p>
+ <p>When compilation is involved, the exception <c>badarg</c> is
+ thrown if a compilation error occurs. Call <c>re:compile/2</c>
+ to get information about the location of the error in the
+ regular expression.</p>
<p>If the regular expression is previously compiled, the option
list can only contain the options <c>anchored</c>,
@@ -242,7 +242,7 @@ This option makes it possible to include comments inside complicated patterns. N
how captured substrings are to be returned (as index tuples,
lists or binaries). The <c>capture</c> option makes the function
quite flexible and powerful. The different options are described
- in detail below</p>
+ in detail below.</p>
<p>If the capture options describe that no substring capturing
at all is to be done (<c>{capture, none}</c>), the function will
@@ -252,7 +252,7 @@ This option makes it possible to include comments inside complicated patterns. N
be done either by specifying <c>none</c> or an empty list as
<c>ValueSpec</c>.</p>
- <p>A description of all the options relevant for execution follows:</p>
+ <p>The options relevant for execution are:</p>
<taglist>
<tag><c>anchored</c></tag>
@@ -266,27 +266,25 @@ This option makes it possible to include comments inside complicated patterns. N
<tag><c>global</c></tag>
<item>
- <p>Implements global (repetitive) search as the <c>g</c> flag in
- i.e. Perl. Each match found is returned as a separate
+ <p>Implements global (repetitive) search (the <c>g</c> flag in
+ Perl). Each match is returned as a separate
<c>list()</c> containing the specific match as well as any
matching subexpressions (or as specified by the <c>capture
option</c>). The <c>Captured</c> part of the return value will
- hence be a <c>list()</c> of <c>list()</c>'s when this
+ hence be a <c>list()</c> of <c>list()</c>s when this
option is given.</p>
- <p>When the regular expression matches an empty string, the
- behaviour might seem non-intuitive, why the behaviour requites
- some clarifying. With the global option, <c>re:run/3</c>
- handles empty matches in the same way as Perl, meaning that a
- match at any point giving an empty string (with length 0) will
- be retried with the options
- <c>[anchored, notempty]</c> as well. If that
- search gives a result of length &gt; 0, the result is included.
- An example:</p>
+ <p>The interaction of the global option with a regular
+ expression which matches an empty string surprises some users.
+ When the global option is given, <c>re:run/3</c> handles empty
+ matches in the same way as Perl: a zero-length match at any
+ point will be retried with the options <c>[anchored,
+ notempty]</c> as well. If that search gives a result of length
+ &gt; 0, the result is included. For example:</p>
<code> re:run("cat","(|at)",[global]).</code>
- <p>The matching will be performed as following:</p>
+ <p>The following matching will be performed:</p>
<taglist>
<tag>At offset <c>0</c></tag>
<item>The regexp <c>(|at)</c> will first match at the initial
@@ -298,11 +296,11 @@ This option makes it possible to include comments inside complicated patterns. N
<item> The search is retried
with the options <c>[anchored, notempty]</c> at the same
position, which does not give any interesting result of longer
- length, why the search position is now advanced to the next
+ length, so the search position is now advanced to the next
character (<c>a</c>).</item>
<tag>At offset <c>1</c></tag>
- <item>Now the search results in
- <c>[{1,0},{1,0}]</c> meaning this search will also be repeated
+ <item>This time, the search results in
+ <c>[{1,0},{1,0}]</c>, so this search will also be repeated
with the extra options.</item>
<tag>At offset <c>1</c> with <c>[anchored, notempty]</c></tag>
<item>Now the <c>ab</c> alternative
@@ -329,16 +327,17 @@ This option makes it possible to include comments inside complicated patterns. N
entire match fails. For example, if the pattern</p>
<code> a?b?</code>
<p>is applied to a string not beginning with "a" or "b", it
- matches the empty string at the start of the subject. With
- <c>notempty</c> given, this match is not valid, so re:run/3 searches
- further into the string for occurrences of "a" or "b".</p>
+ would normally match the empty string at the start of the
+ subject. With the <c>notempty</c> option, this match is not
+ valid, so re:run/3 searches further into the string for
+ occurrences of "a" or "b".</p>
<p>Perl has no direct equivalent of <c>notempty</c>, but it does
make a special case of a pattern match of the empty string
within its split() function, and when using the /g modifier. It
is possible to emulate Perl's behavior after matching a null
string by first trying the match again at the same offset with
- <c>notempty</c> and <c>anchored</c>, and then if that fails by
+ <c>notempty</c> and <c>anchored</c>, and then, if that fails, by
advancing the starting offset (see below) and trying an ordinary
match again.</p>
</item>
@@ -348,7 +347,7 @@ This option makes it possible to include comments inside complicated patterns. N
string is not the beginning of a line, so the circumflex
metacharacter should not match before it. Setting this without
<c>multiline</c> (at compile time) causes circumflex never to
- match. This option affects only the behavior of the circumflex
+ match. This option only affects the behavior of the circumflex
metacharacter. It does not affect \A.</item>
<tag><c>noteol</c></tag>
@@ -384,7 +383,7 @@ This option makes it possible to include comments inside complicated patterns. N
</taglist>
</item>
<tag><c>bsr_anycrlf</c></tag>
- <item>Specifies specifically that \R is to match only the cr, lf or crlf sequences, not the Unicode specific newline characters.(overrides compilation option)</item>
+ <item>Specifies specifically that \R is to match only the cr, lf or crlf sequences, not the Unicode specific newline characters. (overrides compilation option)</item>
<tag><c>bsr_unicode</c></tag>
<item>Specifies specifically that \R is to match all the Unicode newline characters (including crlf etc, the default).(overrides compilation option)</item>
@@ -440,7 +439,7 @@ This option makes it possible to include comments inside complicated patterns. N
<tag><c>none</c></tag>
<item>Do not return matching subpatterns at all, yielding the single atom <c>match</c> as the return value of the function when matching successfully instead of the <c>{match, list()}</c> return. Specifying an empty list gives the same behavior.</item>
</taglist>
- <p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>'s or <c>string()</c>'s to specify the subpatterns to be returned. This deserves an example, consider the following regular expression:</p>
+ <p>The value list is a list of indexes for the subpatterns to return, where index 0 is for all of the pattern, and 1 is for the first explicit capturing subpattern in the regular expression, and so forth. When using named captured subpatterns (see below) in the regular expression, one can use <c>atom()</c>s or <c>string()</c>s to specify the subpatterns to be returned. For example, consider the regular expression:</p>
<code> ".*(abcd).*"</code>
<p>matched against the string ""ABCabcdABC", capturing only the "abcd" part (the first explicit subpattern):</p>
<code> re:run("ABCabcdABC",".*(abcd).*",[{capture,[1]}]).</code>
@@ -451,7 +450,7 @@ This option makes it possible to include comments inside complicated patterns. N
<code> ".*(?&lt;FOO&gt;abcd).*"</code>
<p>With this expression, we could still give the index of the subpattern with the following call:</p>
<code> re:run("ABCabcdABC",".*(?&lt;FOO&gt;abcd).*",[{capture,[1]}]).</code>
- <p>giving the same result as before. But as the subpattern is named, we can also give its name in the value list:</p>
+ <p>giving the same result as before. But, since the subpattern is named, we can also specify its name in the value list:</p>
<code> re:run("ABCabcdABC",".*(?&lt;FOO&gt;abcd).*",[{capture,['FOO']}]).</code>
<p>which would yield the same result as the earlier examples, namely:</p>
<code> {match,[{3,4}]}</code>
@@ -469,15 +468,15 @@ This option makes it possible to include comments inside complicated patterns. N
<item><p>Optionally specifies how captured substrings are to be returned. If omitted, the default of <c>index</c> is used. The <c>Type</c> can be one of the following:</p>
<taglist>
<tag><c>index</c></tag>
- <item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly imagined) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem contra-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item>
+ <item>Return captured substrings as pairs of byte indexes into the subject string and length of the matching string in the subject (as if the subject string was flattened with <c>iolist_to_binary/1</c> or <c>unicode:characters_to_binary/2</c> prior to matching). Note that the <c>unicode</c> option results in <em>byte-oriented</em> indexes in a (possibly virtual) <em>UTF-8 encoded</em> binary. A byte index tuple <c>{0,2}</c> might therefore represent one or two characters when <c>unicode</c> is in effect. This might seem counter-intuitive, but has been deemed the most effective and useful way to way to do it. To return lists instead might result in simpler code if that is desired. This return type is the default.</item>
<tag><c>list</c></tag>
- <item>Return matching substrings as lists of characters (Erlang <c>string()</c>'s). It the <c>unicode</c> option is used in combination with the \C sequence in the regular expression, a captured subpattern can contain bytes that has is not valid UTF-8 (\C matches bytes regardless of character encoding). In that case the <c>list</c> capturing may result in the same types of tuples that <c>unicode:characters_to_list/2</c> can return, namely three-tuples with the tag <c>incomplete</c> or <c>error</c>, the successfully converted characters and the invalid UTF-8 tail of the conversion as a binary. The best strategy is to avoid using the \C sequence when capturing lists.</item>
+ <item>Return matching substrings as lists of characters (Erlang <c>string()</c>s). It the <c>unicode</c> option is used in combination with the \C sequence in the regular expression, a captured subpattern can contain bytes that are not valid UTF-8 (\C matches bytes regardless of character encoding). In that case the <c>list</c> capturing may result in the same types of tuples that <c>unicode:characters_to_list/2</c> can return, namely three-tuples with the tag <c>incomplete</c> or <c>error</c>, the successfully converted characters and the invalid UTF-8 tail of the conversion as a binary. The best strategy is to avoid using the \C sequence when capturing lists.</item>
<tag><c>binary</c></tag>
- <item>Return matching substrings as binaries. If the <c>unicode</c> option is used, these binaries is in UTF-8. If the \C sequence is used together with <c>unicode</c> the binaries may be invalid UTF-8.</item>
+ <item>Return matching substrings as binaries. If the <c>unicode</c> option is used, these binaries are in UTF-8. If the \C sequence is used together with <c>unicode</c> the binaries may be invalid UTF-8.</item>
</taglist>
</item>
</taglist>
- <p>In general, subpatterns that got assigned no value in the match are returned as the tuple <c>{-1,0}</c> when <c>type</c> is <c>index</c>. Unassigned subpatterns are returned as the empty binary or list respectively for other return types. Consider the regular expression:</p>
+ <p>In general, subpatterns that were not assigned a value in the match are returned as the tuple <c>{-1,0}</c> when <c>type</c> is <c>index</c>. Unassigned subpatterns are returned as the empty binary or list, respectively, for other return types. Consider the regular expression:</p>
<code> ".*((?&lt;FOO&gt;abdd)|a(..d)).*"</code>
<p>There are three explicitly capturing subpatterns, where the opening parenthesis position determines the order in the result, hence <c>((?&lt;FOO&gt;abdd)|a(..d))</c> is subpattern index 1, <c>(?&lt;FOO&gt;abdd)</c> is subpattern index 2 and <c>(..d)</c> is subpattern index 3. When matched against the following string:</p>
<code> "ABCabcdABC"</code>
@@ -529,8 +528,8 @@ This option makes it possible to include comments inside complicated patterns. N
<v>NLSpec = cr | crlf | lf | anycrlf | any </v>
</type>
<desc>
- <p>Replaces the matched part of the <c>Subject</c> string with the content of <c>Replacement</c>.</p>
- <p>Options are given as to the <c>re:run/3</c> function except that the <c>capture</c> option of <c>re:run/3</c> is not allowed.
+ <p>Replaces the matched part of the <c>Subject</c> string with the contents of <c>Replacement</c>.</p>
+ <p>The permissible options are the same as for <c>re:run/3</c>, except that the <c>capture</c> option is not allowed.
Instead a <c>{return, ReturnType}</c> is present. The default return type is <c>iodata</c>, constructed in a
way to minimize copying. The <c>iodata</c> result can be used directly in many i/o-operations. If a flat <c>list()</c> is
desired, specify <c>{return, list}</c> and if a binary is preferred, specify <c>{return, binary}</c>.</p>
@@ -540,7 +539,7 @@ This option makes it possible to include comments inside complicated patterns. N
a Unicode <c>charlist()</c>. If compilation is done implicitly
and the <c>unicode</c> compilation option is given to this
function, both the regular expression and the <c>Subject</c>
- should be given as valid Unicode <c>charlist()</c>'s.</p>
+ should be given as valid Unicode <c>charlist()</c>s.</p>
<p>The replacement string can contain the special character
<c>&amp;</c>, which inserts the whole matching expression in the
@@ -550,7 +549,7 @@ This option makes it possible to include comments inside complicated patterns. N
generated by the regular expression, nothing is inserted.</p>
<p>To insert an <c>&amp;</c> or <c>\</c> in the result, precede it
with a <c>\</c>. Note that Erlang already gives a special
- meaning to <c>\</c> in literal strings, why a single <c>\</c>
+ meaning to <c>\</c> in literal strings, so a single <c>\</c>
has to be written as <c>"\\"</c> and therefore a double <c>\</c>
as <c>"\\\\"</c>. Example:</p>
<code> re:replace("abcd","c","[&amp;]",[{return,list}]).</code>
@@ -607,7 +606,7 @@ This option makes it possible to include comments inside complicated patterns. N
a Unicode <c>charlist()</c>. If compilation is done implicitly
and the <c>unicode</c> compilation option is given to this
function, both the regular expression and the <c>Subject</c>
- should be given as valid Unicode <c>charlist()</c>'s.</p>
+ should be given as valid Unicode <c>charlist()</c>s.</p>
<p>The result is given as a list of &quot;strings&quot;, the
preferred datatype given in the <c>return</c> option (default iodata).</p>
@@ -652,25 +651,25 @@ This option makes it possible to include comments inside complicated patterns. N
<p>Here the regular expression matched first the &quot;l&quot;,
causing &quot;Er&quot; to be the first part in the result. When
the regular expression matched, the (only) subexpression was
- bound to the &quot;l&quot;, why the &quot;l&quot; is inserted
+ bound to the &quot;l&quot;, so the &quot;l&quot; is inserted
in the group together with &quot;Er&quot;. The next match is of
the &quot;n&quot;, making &quot;a&quot; the next part to be
- returned. As the subexpression is bound to the substring
+ returned. Since the subexpression is bound to the substring
&quot;n&quot; in this case, the &quot;n&quot; is inserted into
this group. The last group consists of the rest of the string,
as no more matches are found.</p>
<p>By default, all parts of the string, including the empty
- strings are returned from the function. As an example:</p>
+ strings, are returned from the function. For example:</p>
<code> re:split("Erlang","[lg]",[{return,list}]).</code>
- <p>The result will be:</p>
+ <p>will return:</p>
<code> ["Er","an",[]]</code>
- <p>as the matching of the &quot;g&quot; in the end of the string
+ <p>since the matching of the &quot;g&quot; in the end of the string
leaves an empty rest which is also returned. This behaviour
differs from the default behaviour of the split function in
Perl, where empty strings at the end are by default removed. To
@@ -697,10 +696,10 @@ This option makes it possible to include comments inside complicated patterns. N
<p>Note that the last part is &quot;ang&quot;, not
&quot;an&quot;, as we only specified splitting into two parts,
- and the splitting stops when enough parts are given, why the
- result differs from that of <c>trim</c>.</p>
+ and the splitting stops when enough parts are given, which is
+ why the result differs from that of <c>trim</c>.</p>
- <p>More than three parts are not possible with this indata, why</p>
+ <p>More than three parts are not possible with this indata, so</p>
<code> re:split("Erlang","[lg]",[{return,list},{parts,4}]).</code>
@@ -741,7 +740,7 @@ This option makes it possible to include comments inside complicated patterns. N
the parts of the string matching the subexpressions of the
regexp.</p>
<p>The return value from the function will in this case be a
- <c>list()</c> of <c>list()</c>'s. Each sublist begins with the
+ <c>list()</c> of <c>list()</c>s. Each sublist begins with the
string picked out of the subject string, followed by the parts
matching each of the subexpressions in order of occurrence in the
regular expression.</p>
@@ -778,10 +777,8 @@ This option makes it possible to include comments inside complicated patterns. N
<title>PERL LIKE REGULAR EXPRESSIONS SYNTAX</title>
<p>The following sections contain reference material for the
regular expressions used by this module. The regular expression
- reference is taken from the PCRE documentation, but converted as
- needed.</p>
- <p>The documentation is altered where appropriate and where the re
- module behaves differently than the PCRE library.</p>
+ reference is based on the PCRE documentation, with changes in
+ cases where the re module behaves differently to the PCRE library.</p>
</section>
<section><title>PCRE regular expression details</title>
diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml
index f6ae368e92..85aae6151d 100644
--- a/lib/stdlib/doc/src/ref_man.xml
+++ b/lib/stdlib/doc/src/ref_man.xml
@@ -4,7 +4,7 @@
<application xmlns:xi="http://www.w3.org/2001/XInclude">
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>STDLIB Reference Manual</title>
@@ -37,6 +37,7 @@
<xi:include href="array.xml"/>
<xi:include href="base64.xml"/>
<xi:include href="beam_lib.xml"/>
+ <xi:include href="binary.xml"/>
<xi:include href="c.xml"/>
<xi:include href="calendar.xml"/>
<xi:include href="dets.xml"/>
diff --git a/lib/stdlib/doc/src/shell_default.xml b/lib/stdlib/doc/src/shell_default.xml
index 4f8cc6c5bb..f7e7d5388a 100644
--- a/lib/stdlib/doc/src/shell_default.xml
+++ b/lib/stdlib/doc/src/shell_default.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml
index 8c8ae51262..729df1e678 100644
--- a/lib/stdlib/doc/src/sofs.xml
+++ b/lib/stdlib/doc/src/sofs.xml
@@ -210,7 +210,7 @@
X[i] to Y[i] and S a subset of
X[1]&nbsp;&times;&nbsp;...&nbsp;&times;&nbsp;X[n].
The <marker id="multiple_relative_product"></marker><em>multiple
- relative product</em> of TR and and S is defined to be the
+ relative product</em> of TR and S is defined to be the
set {z&nbsp;: z&nbsp;= ((x[1],&nbsp;...,&nbsp;x[n]), (y[1],...,y[n]))
for some (x[1],&nbsp;...,&nbsp;x[n])&nbsp;in&nbsp;S and for some
(x[i],&nbsp;y[i]) in R[i],
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index c696434d49..45fa0847a8 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -156,7 +156,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
the child process to terminate by calling
<c>exit(Child,shutdown)</c> and then wait for an exit signal
with reason <c>shutdown</c> back from the child process. If
- no exit signal is received within the specified time,
+ no exit signal is received within the specified number of milliseconds,
the child process is unconditionally terminated using
<c>exit(Child,kill)</c>.</p>
<p>If the child process is another supervisor, <c>Shutdown</c>
diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml
index b334f57caf..cbd0d9230b 100644
--- a/lib/stdlib/doc/src/supervisor_bridge.xml
+++ b/lib/stdlib/doc/src/supervisor_bridge.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index 10ead62073..efa8922a9d 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -34,7 +34,7 @@
<module>sys</module>
<modulesummary>A Functional Interface to System Messages</modulesummary>
<description>
- <p>This module contains functions for sending system messages used by programs, and messaged used for debugging purposes.
+ <p>This module contains functions for sending system messages used by programs, and messages used for debugging purposes.
</p>
<p>Functions used for implementation of processes
should also understand system messages such as debugging
@@ -243,8 +243,8 @@
customise the value of <c>Misc</c> by exporting
a <c>format_status/2</c> function that contributes
module-specific information;
- see <seealso marker="gen_server#format_status/2">gen_server:format_status/2</seealso>
- and <seealso marker="gen_fsm#format_status/2">gen_fsm:format_status/2</seealso>
+ see <seealso marker="gen_server#Module:format_status/2">gen_server:format_status/2</seealso>
+ and <seealso marker="gen_fsm#Module:format_status/2">gen_fsm:format_status/2</seealso>
for more details.</p>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml
index 0b6807dd6c..cae655f801 100644
--- a/lib/stdlib/doc/src/timer.xml
+++ b/lib/stdlib/doc/src/timer.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -202,18 +202,33 @@
</func>
<func>
<name>tc(Module, Function, Arguments) -> {Time, Value}</name>
- <fsummary>Measure the real time it takes to evaluate <c>apply(Module, Function, Arguments)</c></fsummary>
+ <name>tc(Fun, Arguments) -> {Time, Value}</name>
+ <fsummary>Measure the real time it takes to evaluate <c>apply(Module,
+ Function, Arguments)</c> or <c>apply(Fun, Arguments)</c></fsummary>
<type>
<v>Module = Function = atom()</v>
+ <v>Fun = fun()</v>
<v>Arguments = [term()]</v>
<v>Time = integer() in microseconds</v>
<v>Value = term()</v>
</type>
<desc>
- <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures
- the elapsed real time. Returns <c>{Time, Value}</c>, where
- <c>Time</c> is the elapsed real time in <em>microseconds</em>,
- and <c>Value</c> is what is returned from the apply.</p>
+ <p></p>
+ <taglist>
+ <tag><c>tc/3</c></tag>
+ <item>
+ <p>Evaluates <c>apply(Module, Function, Arguments)</c> and measures
+ the elapsed real time as reported by <c>now/0</c>.
+ Returns <c>{Time, Value}</c>, where
+ <c>Time</c> is the elapsed real time in <em>microseconds</em>,
+ and <c>Value</c> is what is returned from the apply.</p>
+ </item>
+ <tag><c>tc/2</c></tag>
+ <item>
+ <p>Evaluates <c>apply(Fun, Arguments)</c>. Otherwise works
+ like <c>tc/3</c>.</p>
+ </item>
+ </taglist>
</desc>
</func>
<func>
diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml
index 60edd8ade9..e3a25a407b 100644
--- a/lib/stdlib/doc/src/unicode.xml
+++ b/lib/stdlib/doc/src/unicode.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2009</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -40,7 +40,7 @@
<section>
<title>DATA TYPES</title>
- <marker id="charlist_definition"></marker>
+ <marker id="type-charlist"></marker>
<code type="none">
unicode_binary() = binary() with characters encoded in UTF-8 coding standard
unicode_char() = integer() representing valid unicode codepoint
diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml
index f1b0659ea2..416df1f02c 100644
--- a/lib/stdlib/doc/src/unicode_usage.xml
+++ b/lib/stdlib/doc/src/unicode_usage.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2009</year>
+ <year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -168,6 +168,48 @@ Eshell V5.7 (abort with ^G)
<image file="ushell2.gif"><icaption>Unicode characters in allowed and disallowed context</icaption></image>
</section>
<section>
+<title>Unicode file names</title>
+<p>Most modern operating systems support Unicode file names in some way or another. There are several different ways to do this and Erlang by default treats the different approaches differently:</p>
+<taglist>
+<tag>Mandatory Unicode file naming</tag>
+<item>
+<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem have names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows has selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in &quot;Unicode file name translation mode&quot;, meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p>
+<p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p>
+<p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p>
+</item>
+<tag>Transparent file naming</tag>
+<item>
+<p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as &quot;raw file names&quot; if Unicode file naming support is turned on.</p>
+<p>A raw file name is not a list, but a binary. Many non core applications still do not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, do not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p>
+</item>
+</taglist>
+<p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p>
+
+<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, were completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p>
+
+<p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p>
+
+<p>In Unicode file name mode, file names given to the BIF <c>open_port/2</c> with the option <c>{spawn_executable,...}</c> are also interpreted as Unicode. So is the parameter list given in the <c>args</c> option available when using <c>spawn_executable</c>. The UTF-8 translation of arguments can be avoided using binaries, see the discussion about raw file names below.</p>
+
+<p>It is worth noting that the file <c>encoding</c> options given when opening a file has nothing to do with the file <em>name</em> encoding convention. You can very well open files containing UTF-8 but having file names in ISO-latin-1 or vice versa.</p>
+
+<note>Erlang drivers and NIF shared objects still can not be named with names containing codepoints beyond 127. This is a known limitation to be removed in a future release. Erlang modules however can, but it is definitely not a good idea and is still considered experimental.</note>
+
+<section>
+<title>Notes about raw file names and automatic file name conversion</title>
+<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason &quot;raw file names&quot; is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named &quot;bj�rn&quot; in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and have the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named &lt;&lt;&quot;bj�rn&quot;/utf8&gt;&gt;. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named &quot;bj�rn&quot;, one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as &quot;raw&quot; file names, i.e. as binaries.</p>
+<p>The core system of Erlang (kernel and stdlib) accepts raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p>
+<p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p>
+<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The &quot;UTF-8 list&quot; is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p>
+<p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p>
+</section>
+<section>
+<title>Notes about MacOSX</title>
+<p>MacOSXs vfs layer enforces UTF-8 file names in a quite aggressive way. Older versions did this by simply refusing to create non UTF-8 conforming file names, while newer versions replace offending bytes with the sequence &quot;%HH&quot;, where HH is the original character in hexadecimal notation. As Unicode translation is enabled by default on MacOSX, the only way to come up against this is to either start the VM with the <c>+fnl</c> flag or to use a raw file name in <c>latin1</c> encoding. In that case, the file can not be opened with the same name as the one used to create this. The problem is by design in newer versions of MacOSX.</p>
+<p>MacOSX also reorganizes the names of files so that the representation of accents etc is denormalized, i.e. the character <c>�</c> is represented as the codepoints [111,776], where 111 is the character <c>o</c> and 776 is a special accent character. This type of denormalized Unicode is otherwise very seldom used and Erlang normalizes those file names on retrieval, so that denormalized file names is not passed up to the Erlang application. In Erlang the file name &quot;bj�rn&quot; is retrieved as [98,106,246,114,110], not as [98,106,117,776,114,110], even though the file system might think differently.</p>
+</section>
+</section>
+<section>
<title>Unicode-aware modules</title>
<p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p>
<p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p>
diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml
index 4d98a20206..529a70a23d 100644
--- a/lib/stdlib/doc/src/zip.xml
+++ b/lib/stdlib/doc/src/zip.xml
@@ -34,11 +34,11 @@
<module>zip</module>
<modulesummary>Utility for reading and creating 'zip' archives.</modulesummary>
<description>
- <p>The <c>zip</c> module archives and extract files to and from a zip
+ <p>The <c>zip</c> module archives and extracts files to and from a zip
archive. The zip format is specified by the "ZIP Appnote.txt" file
available on PKWare's website www.pkware.com.</p>
<p>The zip module supports zip archive versions up to 6.1. However,
- password-protection and Zip64 is not supported.</p>
+ password-protection and Zip64 are not supported.</p>
<p>By convention, the name of a zip file should end in "<c>.zip</c>".
To abide to the convention, you'll need to add "<c>.zip</c>" yourself
to the name.</p>
@@ -52,7 +52,7 @@
<seealso marker="#unzip_2">unzip/2</seealso> function. (They are
also available as <c>extract</c>.)</p>
<p>To fold a function over all files in a zip archive, use the
- <seealso marker="#foldl_3">foldl_3</seealso>.</p>
+ <seealso marker="#foldl_3">foldl_3</seealso> function.</p>
<p>To return a list of the files in a zip archive, use the
<seealso marker="#list_dir_1">list_dir/1</seealso> or the
<seealso marker="#list_dir_2">list_dir/2</seealso> function. (They
@@ -155,13 +155,13 @@ zip_file() </code>
<p>Files will be compressed using the DEFLATE compression, as
described in the Appnote.txt file. However, files will be
stored without compression if they already are compressed.
- The <c>zip/2</c> and <c>zip/3</c> checks the file extension
+ The <c>zip/2</c> and <c>zip/3</c> functions check the file extension
to see whether the file should be stored without compression.
Files with the following extensions are not compressed:
<c>.Z</c>, <c>.zip</c>, <c>.zoo</c>, <c>.arc</c>, <c>.lzh</c>,
<c>.arj</c>.</p>
<p>It is possible to override the default behavior and
- explicitly control what types of files that should be
+ explicitly control what types of files should be
compressed by using the <c>{compress, What}</c> and
<c>{uncompress, What}</c> options. It is possible to have
several <c>compress</c> and <c>uncompress</c> options. In
@@ -208,7 +208,7 @@ zip_file() </code>
</item>
<tag><c>{compress, What}</c></tag>
<item>
- <p>Controls what types of files that will be
+ <p>Controls what types of files will be
compressed. It is by default set to <c>all</c>. The
following values of <c>What</c> are allowed:</p>
<taglist>
@@ -228,7 +228,7 @@ zip_file() </code>
</item>
<tag><c>{uncompress, What}</c></tag>
<item>
- <p>Controls what types of files that will be uncompressed. It is by
+ <p>Controls what types of files will be uncompressed. It is by
default set to <c>[".Z",".zip",".zoo",".arc",".lzh",".arj"]</c>.
The following values of <c>What</c> are allowed:</p>
<taglist>
@@ -292,7 +292,7 @@ zip_file() </code>
<p>By default, the <c>open/2</c> function will open the
zip file in <c>raw</c> mode, which is faster but does not allow
a remote (erlang) file server to be used. Adding <c>cooked</c>
- to the mode list will override the default and open zip file
+ to the mode list will override the default and open the zip file
without the <c>raw</c> option. The same goes for the files
extracted.</p>
</item>
@@ -301,7 +301,7 @@ zip_file() </code>
<p>By default, all existing files with the same name as file in
the zip archive will be overwritten. With the <c>keep_old_files</c>
option, the <c>unzip/2</c> function will not overwrite any existing
- files. Not that even with the <c>memory</c> option given, which
+ files. Note that even with the <c>memory</c> option given, which
means that no files will be overwritten, files existing will be
excluded from the result.</p>
</item>
@@ -418,7 +418,7 @@ zip_file() </code>
<p>By default, the <c>open/2</c> function will open the
zip file in <c>raw</c> mode, which is faster but does not allow
a remote (erlang) file server to be used. Adding <c>cooked</c>
- to the mode list will override the default and open zip file
+ to the mode list will override the default and open the zip file
without the <c>raw</c> option.</p>
</item>
</taglist>
diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile
index 237818c08b..600303d7e1 100644
--- a/lib/stdlib/src/Makefile
+++ b/lib/stdlib/src/Makefile
@@ -43,6 +43,7 @@ MODULES= \
array \
base64 \
beam_lib \
+ binary \
c \
calendar \
dets \
diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl
index ebef998ee1..a14a72ac6d 100644
--- a/lib/stdlib/src/base64.erl
+++ b/lib/stdlib/src/base64.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -114,7 +114,7 @@ decode(List) when is_list(List) ->
mime_decode(Bin) when is_binary(Bin) ->
mime_decode_binary(<<>>, Bin);
mime_decode(List) when is_list(List) ->
- list_to_binary(mime_decode_l(List)).
+ mime_decode(list_to_binary(List)).
-spec decode_l(string()) -> string().
@@ -125,7 +125,7 @@ decode_l(List) ->
-spec mime_decode_l(string()) -> string().
mime_decode_l(List) ->
- L = strip_illegal(List, []),
+ L = strip_illegal(List, [], 0),
decode(L, []).
%%-------------------------------------------------------------------------
@@ -198,6 +198,9 @@ decode_binary(Result, <<>>) ->
true = is_binary(Result),
Result.
+%% Skipping pad character if not at end of string. Also liberal about
+%% excess padding and skipping of other illegal (non-base64 alphabet)
+%% characters. See section 3.3 of RFC4648
mime_decode_binary(Result, <<0:8,T/bits>>) ->
mime_decode_binary(Result, T);
mime_decode_binary(Result0, <<C:8,T/bits>>) ->
@@ -205,15 +208,27 @@ mime_decode_binary(Result0, <<C:8,T/bits>>) ->
Bits when is_integer(Bits) ->
mime_decode_binary(<<Result0/bits,Bits:6>>, T);
eq ->
- case tail_contains_equal(T) of
- true ->
- Split = byte_size(Result0) - 1,
- <<Result:Split/bytes,_:4>> = Result0,
- Result;
- false ->
- Split = byte_size(Result0) - 1,
- <<Result:Split/bytes,_:2>> = Result0,
- Result
+ case tail_contains_more(T, false) of
+ {<<>>, Eq} ->
+ %% No more valid data.
+ case bit_size(Result0) rem 8 of
+ 0 ->
+ %% '====' is not uncommon.
+ Result0;
+ 4 when Eq ->
+ %% enforce at least one more '=' only ignoring illegals and spacing
+ Split = byte_size(Result0) - 1,
+ <<Result:Split/bytes,_:4>> = Result0,
+ Result;
+ 2 ->
+ %% remove 2 bits
+ Split = byte_size(Result0) - 1,
+ <<Result:Split/bytes,_:2>> = Result0,
+ Result
+ end;
+ {More, _} ->
+ %% More valid data, skip the eq as invalid
+ mime_decode_binary(Result0, More)
end;
_ ->
mime_decode_binary(Result0, T)
@@ -262,31 +277,63 @@ strip_ws(<<$\s,T/binary>>) ->
strip_ws(T);
strip_ws(T) -> T.
-strip_illegal([0|Cs], A) ->
- strip_illegal(Cs, A);
-strip_illegal([C|Cs], A) ->
+%% Skipping pad character if not at end of string. Also liberal about
+%% excess padding and skipping of other illegal (non-base64 alphabet)
+%% characters. See section 3.3 of RFC4648
+strip_illegal([], A, _Cnt) ->
+ A;
+strip_illegal([0|Cs], A, Cnt) ->
+ strip_illegal(Cs, A, Cnt);
+strip_illegal([C|Cs], A, Cnt) ->
case element(C, ?DECODE_MAP) of
- bad -> strip_illegal(Cs, A);
- ws -> strip_illegal(Cs, A);
- eq -> strip_illegal_end(Cs, [$=|A]);
- _ -> strip_illegal(Cs, [C|A])
- end;
-strip_illegal([], A) -> A.
+ bad ->
+ strip_illegal(Cs, A, Cnt);
+ ws ->
+ strip_illegal(Cs, A, Cnt);
+ eq ->
+ case {tail_contains_more(Cs, false), Cnt rem 4} of
+ {{[], _}, 0} ->
+ A; %% Ignore extra =
+ {{[], true}, 2} ->
+ [$=|[$=|A]]; %% 'XX=='
+ {{[], _}, 3} ->
+ [$=|A]; %% 'XXX='
+ {{[H|T], _}, _} ->
+ %% more data, skip equals
+ strip_illegal(T, [H|A], Cnt+1)
+ end;
+ _ ->
+ strip_illegal(Cs, [C|A], Cnt+1)
+ end.
-strip_illegal_end([0|Cs], A) ->
- strip_illegal_end(Cs, A);
-strip_illegal_end([C|Cs], A) ->
+%% Search the tail for more valid data and remember if we saw
+%% another equals along the way.
+tail_contains_more([], Eq) ->
+ {[], Eq};
+tail_contains_more(<<>>, Eq) ->
+ {<<>>, Eq};
+tail_contains_more([C|T]=More, Eq) ->
case element(C, ?DECODE_MAP) of
- bad -> strip_illegal(Cs, A);
- ws -> strip_illegal(Cs, A);
- eq -> [C|A];
- _ -> strip_illegal(Cs, [C|A])
+ bad ->
+ tail_contains_more(T, Eq);
+ ws ->
+ tail_contains_more(T, Eq);
+ eq ->
+ tail_contains_more(T, true);
+ _ ->
+ {More, Eq}
end;
-strip_illegal_end([], A) -> A.
-
-tail_contains_equal(<<$=,_/binary>>) -> true;
-tail_contains_equal(<<_,T/binary>>) -> tail_contains_equal(T);
-tail_contains_equal(<<>>) -> false.
+tail_contains_more(<<C:8,T/bits>> =More, Eq) ->
+ case element(C, ?DECODE_MAP) of
+ bad ->
+ tail_contains_more(T, Eq);
+ ws ->
+ tail_contains_more(T, Eq);
+ eq ->
+ tail_contains_more(T, true);
+ _ ->
+ {More, Eq}
+ end.
%% accessors
b64e(X) ->
diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl
index c71dad6163..74d4ad3da7 100644
--- a/lib/stdlib/src/beam_lib.erl
+++ b/lib/stdlib/src/beam_lib.erl
@@ -19,6 +19,10 @@
-module(beam_lib).
-behaviour(gen_server).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([info/1,
cmp/2,
cmp_dirs/2,
@@ -41,6 +45,8 @@
terminate/2,code_change/3]).
-export([make_crypto_key/2, get_crypto_key/1]). %Utilities used by compiler
+-export_type([attrib_entry/0, compinfo_entry/0, labeled_entry/0]).
+
-import(lists, [append/1, delete/2, foreach/2, keysort/2,
member/2, reverse/1, sort/1, splitwith/2]).
@@ -103,6 +109,7 @@
| info_rsn().
-type cmp_rsn() :: {'modules_different', module(), module()}
| {'chunks_different', chunkid()}
+ | 'different_chunks'
| info_rsn().
%%-------------------------------------------------------------------------
diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl
new file mode 100644
index 0000000000..f6489788b2
--- /dev/null
+++ b/lib/stdlib/src/binary.erl
@@ -0,0 +1,177 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(binary).
+%%
+%% The following functions implemented as BIF's
+%% binary:compile_pattern/1
+%% binary:match/{2,3}
+%% binary:matches/{2,3}
+%% binary:longest_common_prefix/1
+%% binary:longest_common_suffix/1
+%% binary:first/1
+%% binary:last/1
+%% binary:at/2
+%% binary:part/{2,3}
+%% binary:bin_to_list/{1,2,3}
+%% binary:list_to_bin/1
+%% binary:copy/{1,2}
+%% binary:referenced_byte_size/1
+%% binary:decode_unsigned/{1,2}
+%% - Not yet:
+%%
+%% Implemented in this module:
+-export([split/2,split/3,replace/3,replace/4]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% split
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+split(H,N) ->
+ split(H,N,[]).
+split(Haystack,Needles,Options) ->
+ try
+ {Part,Global,Trim} = get_opts_split(Options,{no,false,false}),
+ Moptlist = case Part of
+ no ->
+ [];
+ {A,B} ->
+ [{scope,{A,B}}]
+ end,
+ MList = if
+ Global ->
+ binary:matches(Haystack,Needles,Moptlist);
+ true ->
+ case binary:match(Haystack,Needles,Moptlist) of
+ nomatch -> [];
+ Match -> [Match]
+ end
+ end,
+ do_split(Haystack,MList,0,Trim)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_split(H,[],N,true) when N >= byte_size(H) ->
+ [];
+do_split(H,[],N,_) ->
+ [binary:part(H,{N,byte_size(H)-N})];
+do_split(H,[{A,B}|T],N,Trim) ->
+ case binary:part(H,{N,A-N}) of
+ <<>> ->
+ Rest = do_split(H,T,A+B,Trim),
+ case {Trim, Rest} of
+ {true,[]} ->
+ [];
+ _ ->
+ [<<>> | Rest]
+ end;
+ Oth ->
+ [Oth | do_split(H,T,A+B,Trim)]
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% replace
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+replace(H,N,R) ->
+ replace(H,N,R,[]).
+replace(Haystack,Needles,Replacement,Options) ->
+ try
+ true = is_binary(Replacement), % Make badarg instead of function clause
+ {Part,Global,Insert} = get_opts_replace(Options,{no,false,[]}),
+ Moptlist = case Part of
+ no ->
+ [];
+ {A,B} ->
+ [{scope,{A,B}}]
+ end,
+ MList = if
+ Global ->
+ binary:matches(Haystack,Needles,Moptlist);
+ true ->
+ case binary:match(Haystack,Needles,Moptlist) of
+ nomatch -> [];
+ Match -> [Match]
+ end
+ end,
+ ReplList = case Insert of
+ [] ->
+ Replacement;
+ Y when is_integer(Y) ->
+ splitat(Replacement,0,[Y]);
+ Li when is_list(Li) ->
+ splitat(Replacement,0,lists:sort(Li))
+ end,
+ erlang:iolist_to_binary(do_replace(Haystack,MList,ReplList,0))
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+
+do_replace(H,[],_,N) ->
+ [binary:part(H,{N,byte_size(H)-N})];
+do_replace(H,[{A,B}|T],Replacement,N) ->
+ [binary:part(H,{N,A-N}),
+ if
+ is_list(Replacement) ->
+ do_insert(Replacement, binary:part(H,{A,B}));
+ true ->
+ Replacement
+ end
+ | do_replace(H,T,Replacement,A+B)].
+
+do_insert([X],_) ->
+ [X];
+do_insert([H|T],R) ->
+ [H,R|do_insert(T,R)].
+
+splitat(H,N,[]) ->
+ [binary:part(H,{N,byte_size(H)-N})];
+splitat(H,N,[I|T]) ->
+ [binary:part(H,{N,I-N})|splitat(H,I,T)].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Simple helper functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_opts_split([],{Part,Global,Trim}) ->
+ {Part,Global,Trim};
+get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) ->
+ get_opts_split(T,{{A,B},Global,Trim});
+get_opts_split([global | T],{Part,_Global,Trim}) ->
+ get_opts_split(T,{Part,true,Trim});
+get_opts_split([trim | T],{Part,Global,_Trim}) ->
+ get_opts_split(T,{Part,Global,true});
+get_opts_split(_,_) ->
+ throw(badopt).
+
+get_opts_replace([],{Part,Global,Insert}) ->
+ {Part,Global,Insert};
+get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) ->
+ get_opts_replace(T,{{A,B},Global,Insert});
+get_opts_replace([global | T],{Part,_Global,Insert}) ->
+ get_opts_replace(T,{Part,true,Insert});
+get_opts_replace([{insert_replaced,N} | T],{Part,Global,_Insert}) ->
+ get_opts_replace(T,{Part,Global,N});
+get_opts_replace(_,_) ->
+ throw(badopt).
+
diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl
index e05a1c787f..235ea939a8 100644
--- a/lib/stdlib/src/c.erl
+++ b/lib/stdlib/src/c.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,6 +20,8 @@
%% Utilities to use from shell.
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([help/0,lc/1,c/1,c/2,nc/1,nc/2, nl/1,l/1,i/0,i/1,ni/0,
y/1, y/2,
lc_batch/0, lc_batch/1,
@@ -40,31 +42,31 @@
-spec help() -> 'ok'.
help() ->
- format("bt(Pid) -- stack backtrace for a process\n"
- "c(File) -- compile and load code in <File>\n"
- "cd(Dir) -- change working directory\n"
- "flush() -- flush any messages sent to the shell\n"
- "help() -- help info\n"
- "i() -- information about the system\n"
- "ni() -- information about the networked system\n"
- "i(X,Y,Z) -- information about pid <X,Y,Z>\n"
- "l(Module) -- load or reload module\n"
- "lc([File]) -- compile a list of Erlang modules\n"
- "ls() -- list files in the current directory\n"
- "ls(Dir) -- list files in directory <Dir>\n"
- "m() -- which modules are loaded\n"
- "m(Mod) -- information about module <Mod>\n"
- "memory() -- memory allocation information\n"
- "memory(T) -- memory allocation information of type <T>\n"
- "nc(File) -- compile and load code in <File> on all nodes\n"
- "nl(Module) -- load module on all nodes\n"
- "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n"
- "pwd() -- print working directory\n"
- "q() -- quit - shorthand for init:stop()\n"
- "regs() -- information about registered processes\n"
- "nregs() -- information about all registered processes\n"
- "xm(M) -- cross reference check a module\n"
- "y(File) -- generate a Yecc parser\n").
+ io:put_chars(<<"bt(Pid) -- stack backtrace for a process\n"
+ "c(File) -- compile and load code in <File>\n"
+ "cd(Dir) -- change working directory\n"
+ "flush() -- flush any messages sent to the shell\n"
+ "help() -- help info\n"
+ "i() -- information about the system\n"
+ "ni() -- information about the networked system\n"
+ "i(X,Y,Z) -- information about pid <X,Y,Z>\n"
+ "l(Module) -- load or reload module\n"
+ "lc([File]) -- compile a list of Erlang modules\n"
+ "ls() -- list files in the current directory\n"
+ "ls(Dir) -- list files in directory <Dir>\n"
+ "m() -- which modules are loaded\n"
+ "m(Mod) -- information about module <Mod>\n"
+ "memory() -- memory allocation information\n"
+ "memory(T) -- memory allocation information of type <T>\n"
+ "nc(File) -- compile and load code in <File> on all nodes\n"
+ "nl(Module) -- load module on all nodes\n"
+ "pid(X,Y,Z) -- convert X,Y,Z to a Pid\n"
+ "pwd() -- print working directory\n"
+ "q() -- quit - shorthand for init:stop()\n"
+ "regs() -- information about registered processes\n"
+ "nregs() -- information about all registered processes\n"
+ "xm(M) -- cross reference check a module\n"
+ "y(File) -- generate a Yecc parser\n">>).
%% c(FileName)
%% Compile a file/module.
@@ -657,7 +659,7 @@ portformat(Name, Id, Cmd) ->
pwd() ->
case file:get_cwd() of
{ok, Str} ->
- ok = io:format("~s\n", [Str]);
+ ok = io:format("~ts\n", [fixup_one_bin(Str)]);
{error, _} ->
ok = io:format("Cannot determine current directory\n")
end.
@@ -682,11 +684,27 @@ ls() ->
ls(Dir) ->
case file:list_dir(Dir) of
{ok, Entries} ->
- ls_print(sort(Entries));
+ ls_print(sort(fixup_bin(Entries)));
{error,_E} ->
format("Invalid directory\n")
end.
+fixup_one_bin(X) when is_binary(X) ->
+ L = binary_to_list(X),
+ [ if
+ El > 127 ->
+ $?;
+ true ->
+ El
+ end || El <- L];
+fixup_one_bin(X) ->
+ X.
+fixup_bin([H|T]) ->
+ [fixup_one_bin(H) | fixup_bin(T)];
+fixup_bin([]) ->
+ [].
+
+
ls_print([]) -> ok;
ls_print(L) ->
Width = min([max(lengths(L, [])), 40]) + 5,
@@ -696,7 +714,7 @@ ls_print(X, Width, Len) when Width + Len >= 80 ->
io:nl(),
ls_print(X, Width, 0);
ls_print([H|T], Width, Len) ->
- io:format("~-*s",[Width,H]),
+ io:format("~-*ts",[Width,H]),
ls_print(T, Width, Len+Width);
ls_print([], _, _) ->
io:nl().
diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl
index ddc0666f77..33725d999c 100644
--- a/lib/stdlib/src/calendar.erl
+++ b/lib/stdlib/src/calendar.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,6 +28,8 @@
gregorian_days_to_date/1,
gregorian_seconds_to_datetime/1,
is_leap_year/1,
+ iso_week_number/0,
+ iso_week_number/1,
last_day_of_the_month/2,
local_time/0,
local_time_to_universal_time/1,
@@ -70,6 +72,7 @@
-type second() :: 0..59.
-type daynum() :: 1..7.
-type ldom() :: 28 | 29 | 30 | 31. % last day of month
+-type weeknum() :: 1..53.
-type t_now() :: {non_neg_integer(),non_neg_integer(),non_neg_integer()}.
@@ -77,6 +80,7 @@
-type t_time() :: {hour(),minute(),second()}.
-type t_datetime() :: {t_date(),t_time()}.
-type t_datetime1970() :: {{year1970(),month(),day()},t_time()}.
+-type t_yearweeknum() :: {year(),weeknum()}.
%%----------------------------------------------------------------------
@@ -172,6 +176,42 @@ is_leap_year1(Year) when Year rem 400 =:= 0 ->
is_leap_year1(_) -> false.
+%%
+%% Calculates the iso week number for the current date.
+%%
+-spec iso_week_number() -> t_yearweeknum().
+iso_week_number() ->
+ {Date, _} = local_time(),
+ iso_week_number(Date).
+
+
+%%
+%% Calculates the iso week number for the given date.
+%%
+-spec iso_week_number(t_date()) -> t_yearweeknum().
+iso_week_number({Year, Month, Day}) ->
+ D = date_to_gregorian_days({Year, Month, Day}),
+ W01_1_Year = gregorian_days_of_iso_w01_1(Year),
+ W01_1_NextYear = gregorian_days_of_iso_w01_1(Year + 1),
+ if W01_1_Year =< D andalso D < W01_1_NextYear ->
+ % Current Year Week 01..52(,53)
+ {Year, (D - W01_1_Year) div 7 + 1};
+ D < W01_1_Year ->
+ % Previous Year 52 or 53
+ PWN = case day_of_the_week(Year - 1, 1, 1) of
+ 4 -> 53;
+ _ -> case day_of_the_week(Year - 1, 12, 31) of
+ 4 -> 53;
+ _ -> 52
+ end
+ end,
+ {Year - 1, PWN};
+ W01_1_NextYear =< D ->
+ % Next Year, Week 01
+ {Year + 1, 1}
+ end.
+
+
%% last_day_of_the_month(Year, Month)
%%
%% Returns the number of days in a month.
@@ -377,6 +417,19 @@ dty(Y, D1, D2) when D1 < D2 ->
dty(Y, _D1, D2) ->
{Y, D2}.
+%%
+%% The Gregorian days of the iso week 01 day 1 for a given year.
+%%
+-spec gregorian_days_of_iso_w01_1(year()) -> non_neg_integer().
+gregorian_days_of_iso_w01_1(Year) ->
+ D0101 = date_to_gregorian_days(Year, 1, 1),
+ DOW = day_of_the_week(Year, 1, 1),
+ if DOW =< 4 ->
+ D0101 - DOW + 1;
+ true ->
+ D0101 + 7 - DOW + 1
+ end.
+
%% year_day_to_date(Year, DayOfYear) = {Month, DayOfMonth}
%%
%% Note: 1 is the first day of the month.
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 7f1c13770b..6c91f1efb7 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dets).
@@ -88,6 +88,7 @@
%% Not documented, or not ready for publication.
-export([lookup_keys/2]).
+-export_type([tab_name/0]).
-compile({inline, [{einval,2},{badarg,2},{undefined,1},
{badarg_exit,2},{lookup_reply,2}]}).
@@ -146,6 +147,7 @@
bin, % small chunk not consumed, or 'eof' at end-of-file
alloc, % the part of the file not yet scanned, mostly a binary
tab,
+ proc, % the pid of the Dets process
match_program % true | compiled_match_spec() | undefined
}).
@@ -207,8 +209,6 @@ all() ->
bchunk(Tab, start) ->
badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]);
-bchunk(Tab, #dets_cont{bin = eof, tab = Tab}) ->
- '$end_of_table';
bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) ->
badarg(treq(Tab, {bchunk, State}), [Tab, State]);
bchunk(Tab, Term) ->
@@ -721,11 +721,14 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0;
N =:= default ->
case compile_match_spec(What, Pat) of
{Spec, MP} ->
- case req(dets_server:get_pid(Tab), {match, MP, Spec, N}) of
+ Proc = dets_server:get_pid(Tab),
+ case req(Proc, {match, MP, Spec, N}) of
{done, L} ->
- {L, #dets_cont{tab = Tab, what = What, bin = eof}};
+ {L, #dets_cont{tab = Tab, proc = Proc, what = What,
+ bin = eof}};
{cont, State} ->
- chunk_match(State#dets_cont{what = What, tab = Tab});
+ chunk_match(State#dets_cont{what = What, tab = Tab,
+ proc = Proc});
Error ->
Error
end;
@@ -735,34 +738,28 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0;
init_chunk_match(_Tab, _Pat, _What, _) ->
badarg.
-chunk_match(State) ->
- case catch dets_server:get_pid(State#dets_cont.tab) of
- {'EXIT', _Reason} ->
- badarg;
- _Proc when State#dets_cont.bin =:= eof ->
- '$end_of_table';
- Proc ->
- case req(Proc, {match_init, State}) of
- {cont, {Bins, NewState}} ->
- MP = NewState#dets_cont.match_program,
- case catch do_foldl_bins(Bins, MP) of
- {'EXIT', _} ->
- case ets:is_compiled_ms(MP) of
- true ->
- Bad = dets_utils:bad_object(chunk_match,
- Bins),
- req(Proc, {corrupt, Bad});
- false ->
- badarg
- end;
- [] ->
- chunk_match(NewState);
- Terms ->
- {Terms, NewState}
- end;
- Error ->
- Error
- end
+chunk_match(#dets_cont{proc = Proc}=State) ->
+ case req(Proc, {match_init, State}) of
+ '$end_of_table'=Reply ->
+ Reply;
+ {cont, {Bins, NewState}} ->
+ MP = NewState#dets_cont.match_program,
+ case catch do_foldl_bins(Bins, MP) of
+ {'EXIT', _} ->
+ case ets:is_compiled_ms(MP) of
+ true ->
+ Bad = dets_utils:bad_object(chunk_match, Bins),
+ req(Proc, {corrupt, Bad});
+ false ->
+ badarg
+ end;
+ [] ->
+ chunk_match(NewState);
+ Terms ->
+ {Terms, NewState}
+ end;
+ Error ->
+ Error
end.
do_foldl_bins(Bins, true) ->
@@ -1093,7 +1090,9 @@ do_apply_op(Op, From, Head, N) ->
{N2, H2} when is_record(H2, head), is_integer(N2) ->
open_file_loop(H2, N2);
H2 when is_record(H2, head) ->
- open_file_loop(H2, N)
+ open_file_loop(H2, N);
+ {{more,From1,Op1,N1}, NewHead} ->
+ do_apply_op(Op1, From1, NewHead, N1)
catch
exit:normal ->
exit(normal);
@@ -1362,37 +1361,35 @@ start_auto_save_timer(Head) ->
%% lookup requests in parallel. Evalute delete_object, delete and
%% insert as well.
stream_op(Op, Pid, Pids, Head, N) ->
- stream_op(Head, Pids, [], N, Pid, Op, Head#head.fixed).
+ #head{fixed = Fxd, update_mode = M} = Head,
+ stream_op(Head, Pids, [], N, Pid, Op, Fxd, M).
-stream_loop(Head, Pids, C, N, false = Fxd) ->
+stream_loop(Head, Pids, C, N, false = Fxd, M) ->
receive
?DETS_CALL(From, Message) ->
- stream_op(Head, Pids, C, N, From, Message, Fxd)
+ stream_op(Head, Pids, C, N, From, Message, Fxd, M)
after 0 ->
stream_end(Head, Pids, C, N, no_more)
end;
-stream_loop(Head, Pids, C, N, _Fxd) ->
+stream_loop(Head, Pids, C, N, _Fxd, _M) ->
stream_end(Head, Pids, C, N, no_more).
-stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd) ->
+stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd, M) ->
NC = [{{lookup,Pid},Keys} | C],
- stream_loop(Head, Pids, NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd) ->
+ stream_loop(Head, Pids, NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd, dirty = M) ->
NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {insert_new, _Objects} = Op, Fxd) ->
+ stream_loop(Head, [Pid | Pids], NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd, dirty = M) ->
NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd) ->
+ stream_loop(Head, [Pid | Pids], NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {delete_object, _Os} = Op, Fxd, dirty = M) ->
NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {delete_object, _Objects} = Op, Fxd) ->
- NC = [Op | C],
- stream_loop(Head, [Pid | Pids], NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd) ->
+ stream_loop(Head, [Pid | Pids], NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd, M) ->
NC = [{{lookup,[Pid]},[Key]} | C],
- stream_loop(Head, Pids, NC, N, Fxd);
-stream_op(Head, Pids, C, N, Pid, Op, _Fxd) ->
+ stream_loop(Head, Pids, NC, N, Fxd, M);
+stream_op(Head, Pids, C, N, Pid, Op, _Fxd, _M) ->
stream_end(Head, Pids, C, N, {Pid,Op}).
stream_end(Head, Pids0, C, N, Next) ->
@@ -1437,7 +1434,7 @@ stream_end2([], Ps, no_more, N, C, Head, _Reply) ->
penalty(Head, Ps, C),
{N, Head};
stream_end2([], _Ps, {From, Op}, N, _C, Head, _Reply) ->
- apply_op(Op, From, Head, N).
+ {{more,From,Op,N},Head}.
penalty(H, _Ps, _C) when H#head.fixed =:= false ->
ok;
@@ -1577,13 +1574,18 @@ do_bchunk_init(Head, Tab) ->
L = dets_utils:all_allocated(H2),
C0 = #dets_cont{no_objs = default, bin = <<>>, alloc = L},
BinParms = term_to_binary(Parms),
- {H2, {C0#dets_cont{tab = Tab, what = bchunk}, [BinParms]}}
+ {H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk},
+ [BinParms]}}
end;
{NewHead, _} = HeadError when is_record(NewHead, head) ->
HeadError
end.
%% -> {NewHead, {cont(), [binary()]}} | {NewHead, Error}
+do_bchunk(Head, #dets_cont{proc = Proc}) when Proc =/= self() ->
+ {Head, badarg};
+do_bchunk(Head, #dets_cont{bin = eof}) ->
+ {Head, '$end_of_table'};
do_bchunk(Head, State) ->
case dets_v9:read_bchunks(Head, State#dets_cont.alloc) of
{error, Reason} ->
@@ -1953,6 +1955,8 @@ flookup_keys(Head, Keys) ->
end.
%% -> {NewHead, Result}
+fmatch_init(Head, #dets_cont{bin = eof}) ->
+ {Head, '$end_of_table'};
fmatch_init(Head, C) ->
case scan(Head, C) of
{scan_error, Reason} ->
diff --git a/lib/stdlib/src/dets.hrl b/lib/stdlib/src/dets.hrl
index 6e59770753..fbffc9d008 100644
--- a/lib/stdlib/src/dets.hrl
+++ b/lib/stdlib/src/dets.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,7 @@
%%
-define(DEFAULT_MIN_NO_SLOTS, 256).
--define(DEFAULT_MAX_NO_SLOTS, 2*1024*1024).
+-define(DEFAULT_MAX_NO_SLOTS, 32*1024*1024).
-define(DEFAULT_AUTOSAVE, 3). % minutes
-define(DEFAULT_CACHE, {3000, 14000}). % {delay,size} in {milliseconds,bytes}
diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl
index 1f9f84cd27..af36958c1c 100644
--- a/lib/stdlib/src/dets_v8.erl
+++ b/lib/stdlib/src/dets_v8.erl
@@ -1074,6 +1074,8 @@ wl([], _Type, Del, Lookup, I, Objs) ->
[{Del, Lookup, Objs} | I].
%% -> {NewHead, ok} | {NewHead, Error}
+may_grow(Head, 0, once) ->
+ {Head, ok};
may_grow(Head, _N, _How) when Head#head.fixed =/= false ->
{Head, ok};
may_grow(#head{access = read}=Head, _N, _How) ->
diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl
index 53238e962f..132af01f79 100644
--- a/lib/stdlib/src/dets_v9.erl
+++ b/lib/stdlib/src/dets_v9.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1908,6 +1908,9 @@ write_cache(Head) ->
end.
%% -> {NewHead, ok} | {NewHead, Error}
+may_grow(Head, 0, once) ->
+ %% Do not re-hash if there is a chance that the file is not dirty.
+ {Head, ok};
may_grow(Head, _N, _How) when Head#head.fixed =/= false ->
{Head, ok};
may_grow(#head{access = read}=Head, _N, _How) ->
diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl
index 9bdea671a9..5edc868a94 100644
--- a/lib/stdlib/src/digraph.erl
+++ b/lib/stdlib/src/digraph.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(digraph).
@@ -36,6 +36,8 @@
-export([get_short_path/3, get_short_cycle/2]).
+-export_type([digraph/0, d_type/0, vertex/0]).
+
-record(digraph, {vtab = notable :: ets:tab(),
etab = notable :: ets:tab(),
ntab = notable :: ets:tab(),
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index 6cb441dbed..026bd9038f 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -24,6 +24,7 @@
-export([init/0,start/1,edit_line/2,prefix_arg/1]).
-export([erase_line/1,erase_inp/1,redraw_line/1]).
-export([length_before/1,length_after/1,prompt/1]).
+-export([current_line/1]).
%%-export([expand/1]).
-export([edit_line1/2]).
@@ -421,6 +422,7 @@ over_paren_auto([], _, _, _) ->
%% length_before(Line)
%% length_after(Line)
%% prompt(Line)
+%% current_line(Line)
%% Various functions for accessing bits of a line.
erase_line({line,Pbs,{Bef,Aft},_}) ->
@@ -447,6 +449,9 @@ length_after({line,_,{_Bef,Aft},_}) ->
prompt({line,Pbs,_,_}) ->
Pbs.
+current_line({line,_,{Bef, Aft},_}) ->
+ reverse(Bef, Aft ++ "\n").
+
%% %% expand(CurrentBefore) ->
%% %% {yes,Expansion} | no
%% %% Try to expand the word before as either a module name or a function
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index f144cbb938..e5ccaddbb4 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -33,7 +33,9 @@
%% Epp state record.
-record(epp, {file, %Current file
location, %Current location
+ delta, %Offset from Location (-file)
name="", %Current file name
+ name2="", %-"-, modified by -file
istk=[], %Ifdef stack
sstk=[], %State stack
path=[], %Include-path
@@ -111,6 +113,8 @@ format_error({bad,W}) ->
io_lib:format("badly formed '~s'", [W]);
format_error(missing_parenthesis) ->
io_lib:format("badly formed define: missing closing right parenthesis",[]);
+format_error(premature_end) ->
+ "premature end";
format_error({call,What}) ->
io_lib:format("illegal macro call '~s'",[What]);
format_error({undefined,M,none}) ->
@@ -163,7 +167,7 @@ parse_file(Epp) ->
case normalize_typed_record_fields(Fields) of
{typed, NewFields} ->
[{attribute, La, record, {Record, NewFields}},
- {attribute, La, type,
+ {attribute, La, type,
{{record, Record}, Fields, []}}
|parse_file(Epp)];
not_typed ->
@@ -188,7 +192,7 @@ normalize_typed_record_fields([], NewFields, Typed) ->
true -> {typed, lists:reverse(NewFields)};
false -> not_typed
end;
-normalize_typed_record_fields([{typed_record_field,Field,_}|Rest],
+normalize_typed_record_fields([{typed_record_field,Field,_}|Rest],
NewFields, _Typed) ->
normalize_typed_record_fields(Rest, [Field|NewFields], true);
normalize_typed_record_fields([Field|Rest], NewFields, Typed) ->
@@ -232,8 +236,8 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
epp_reply(Pid, {ok,self()}),
- St = #epp{file=File, location=AtLocation, name=Name,
- path=Path, macs=Ms1, pre_opened = Pre},
+ St = #epp{file=File, location=AtLocation, delta=0, name=Name,
+ name2=Name, path=Path, macs=Ms1, pre_opened = Pre},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
@@ -324,7 +328,7 @@ wait_req_scan(St) ->
wait_req_skip(St, Sis) ->
From = wait_request(St),
skip_toks(From, St, Sis).
-
+
%% enter_file(Path, FileName, IncludeToken, From, EppState)
%% leave_file(From, EppState)
%% Handle entering and leaving included files. Notify caller when the
@@ -356,8 +360,8 @@ enter_file2(NewF, Pname, From, St, AtLocation, ExtraPath) ->
enter_file_reply(From, Pname, Loc, AtLocation),
Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs),
Path = St#epp.path ++ ExtraPath,
- #epp{location=Loc,file=NewF,
- name=Pname,sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
+ #epp{file=NewF,location=Loc,name=Pname,delta=0,
+ sstk=[St|St#epp.sstk],path=Path,macs=Ms}.
enter_file_reply(From, Name, Location, AtLocation) ->
Attr = loc_attr(AtLocation),
@@ -380,23 +384,32 @@ file_name(N) when is_atom(N) ->
leave_file(From, St) ->
case St#epp.istk of
- [I|Cis] ->
+ [I|Cis] ->
epp_reply(From,
- {error,{St#epp.location,epp,
+ {error,{St#epp.location,epp,
{illegal,"unterminated",I}}}),
leave_file(wait_request(St),St#epp{istk=Cis});
[] ->
case St#epp.sstk of
[OldSt|Sts] ->
close_file(St),
- enter_file_reply(From, OldSt#epp.name,
- OldSt#epp.location, OldSt#epp.location),
+ #epp{location=OldLoc, delta=Delta, name=OldName,
+ name2=OldName2} = OldSt,
+ CurrLoc = add_line(OldLoc, Delta),
Ms = dict:store({atom,'FILE'},
- {none,
- [{string,OldSt#epp.location,
- OldSt#epp.name}]},
+ {none,[{string,CurrLoc,OldName2}]},
St#epp.macs),
- wait_req_scan(OldSt#epp{sstk=Sts,macs=Ms});
+ NextSt = OldSt#epp{sstk=Sts,macs=Ms},
+ enter_file_reply(From, OldName, CurrLoc, CurrLoc),
+ case OldName2 =:= OldName of
+ true ->
+ From;
+ false ->
+ NFrom = wait_request(NextSt),
+ enter_file_reply(NFrom, OldName2, OldLoc,
+ neg_line(CurrLoc))
+ end,
+ wait_req_scan(NextSt);
[] ->
epp_reply(From, {eof,St#epp.location}),
wait_req_scan(St)
@@ -491,9 +504,9 @@ scan_extends(_Ts, _As, Ms) -> Ms.
%% scan_define(Tokens, DefineToken, From, EppState)
-scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St)
+scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',Lc}|Toks], _Def, From, St)
when Type =:= atom; Type =:= var ->
- case catch macro_expansion(Toks) of
+ case catch macro_expansion(Toks, Lc) of
Expansion when is_list(Expansion) ->
case dict:find({atom,M}, St#epp.macs) of
{ok, Defs} when is_list(Defs) ->
@@ -608,7 +621,7 @@ scan_undef(_Toks, Undef, From, St) ->
%% scan_include(Tokens, IncludeToken, From, St)
-scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
+scan_include([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}], Inc,
From, St) ->
NewName = expand_var(NewName0),
enter_file(St#epp.path, NewName, Inc, From, St);
@@ -644,7 +657,7 @@ scan_include_lib([{'(',_Llp},{string,_Lf,NewName0},{')',_Lrp},{dot,_Ld}],
case file:open(LibName, [read]) of
{ok,NewF} ->
ExtraPath = [filename:dirname(LibName)],
- wait_req_scan(enter_file2(NewF, LibName, From,
+ wait_req_scan(enter_file2(NewF, LibName, From,
St, Loc, ExtraPath));
{error,_E2} ->
epp_reply(From,
@@ -766,14 +779,15 @@ scan_file([{'(',_Llp},{string,_Ls,Name},{',',_Lc},{integer,_Li,Ln},{')',_Lrp},
Ms = dict:store({atom,'FILE'}, {none,[{string,1,Name}]}, St#epp.macs),
Locf = loc(Tf),
NewLoc = new_location(Ln, St#epp.location, Locf),
- wait_req_scan(St#epp{name=Name,location=NewLoc,macs=Ms});
+ Delta = abs(get_line(element(2, Tf)))-Ln + St#epp.delta,
+ wait_req_scan(St#epp{name2=Name,location=NewLoc,delta=Delta,macs=Ms});
scan_file(_Toks, Tf, From, St) ->
epp_reply(From, {error,{loc(Tf),epp,{bad,file}}}),
wait_req_scan(St).
new_location(Ln, Le, Lf) when is_integer(Lf) ->
Ln+(Le-Lf);
-new_location(Ln, {Le,_}, {Lf,_}) ->
+new_location(Ln, {Le,_}, {Lf,_}) ->
{Ln+(Le-Lf),1}.
%% skip_toks(From, EppState, SkipIstack)
@@ -814,22 +828,23 @@ skip_else(_Else, From, St, Sis) ->
skip_toks(From, St, Sis).
%% macro_pars(Tokens, ArgStack)
-%% macro_expansion(Tokens)
+%% macro_expansion(Tokens, Line)
%% Extract the macro parameters and the expansion from a macro definition.
-macro_pars([{')',_Lp}, {',',_Ld}|Ex], Args) ->
- {ok, {lists:reverse(Args), macro_expansion(Ex)}};
-macro_pars([{var,_,Name}, {')',_Lp}, {',',_Ld}|Ex], Args) ->
+macro_pars([{')',_Lp}, {',',Ld}|Ex], Args) ->
+ {ok, {lists:reverse(Args), macro_expansion(Ex, Ld)}};
+macro_pars([{var,_,Name}, {')',_Lp}, {',',Ld}|Ex], Args) ->
false = lists:member(Name, Args), %Prolog is nice
- {ok, {lists:reverse([Name|Args]), macro_expansion(Ex)}};
+ {ok, {lists:reverse([Name|Args]), macro_expansion(Ex, Ld)}};
macro_pars([{var,_L,Name}, {',',_}|Ts], Args) ->
- false = lists:member(Name, Args),
+ false = lists:member(Name, Args),
macro_pars(Ts, [Name|Args]).
-macro_expansion([{')',_Lp},{dot,_Ld}]) -> [];
-macro_expansion([{dot,Ld}]) -> throw({error,Ld,missing_parenthesis});
-macro_expansion([T|Ts]) ->
- [T|macro_expansion(Ts)].
+macro_expansion([{')',_Lp},{dot,_Ld}], _L0) -> [];
+macro_expansion([{dot,Ld}], _L0) -> throw({error,Ld,missing_parenthesis});
+macro_expansion([T|Ts], _L0) ->
+ [T|macro_expansion(Ts, element(2, T))];
+macro_expansion([], L0) -> throw({error,L0,premature_end}).
%% expand_macros(Tokens, Macros)
%% expand_macro(Tokens, MacroToken, RestTokens)
@@ -1084,11 +1099,11 @@ epp_reply(From, Rep) ->
wait_epp_reply(Epp, Mref) ->
receive
- {epp_reply,Epp,Rep} ->
+ {epp_reply,Epp,Rep} ->
erlang:demonitor(Mref),
receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
Rep;
- {'DOWN',Mref,_,_,E} ->
+ {'DOWN',Mref,_,_,E} ->
receive {epp_reply,Epp,Rep} -> Rep
after 0 -> exit(E)
end
@@ -1129,6 +1144,9 @@ neg_line(L) ->
abs_line(L) ->
erl_scan:set_attribute(line, L, fun(Line) -> abs(Line) end).
+add_line(L, Offset) ->
+ erl_scan:set_attribute(line, L, fun(Line) -> Line+Offset end).
+
start_loc(Line) when is_integer(Line) ->
1;
start_loc({_Line, _Column}) ->
@@ -1145,7 +1163,7 @@ get_line({Line,_Column}) ->
%% mainly aimed at yecc, the parser generator, which uses the -file
%% attribute to get correct lines in messages referring to code
%% supplied by the user (actions etc in .yrl files).
-%%
+%%
%% In a perfect world (read: perfectly implemented applications such
%% as Xref, Cover, Debugger, etc.) it would not be necessary to
%% distinguish -file attributes from epp and the input file. The
@@ -1165,7 +1183,7 @@ get_line({Line,_Column}) ->
%% have been output by epp (corresponding to -include and
%% -include_lib) are kept, but the user's -file attributes are
%% removed. This seems sufficient for now.
-%%
+%%
%% It turns out to be difficult to distinguish -file attributes in the
%% input file from the ones added by epp unless some action is taken.
%% The (less than perfect) solution employed is to let epp assign
@@ -1177,7 +1195,7 @@ get_line({Line,_Column}) ->
interpret_file_attribute(Forms) ->
interpret_file_attr(Forms, 0, []).
-interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
+interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
Delta, Fs) ->
{line, L} = erl_scan:attributes_info(Loc, line),
if
@@ -1188,10 +1206,10 @@ interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms],
%% -include or -include_lib
% true = L =:= Line,
case Fs of
- [_, Delta1, File | Fs1] -> % end of included file
- [Form | interpret_file_attr(Forms, Delta1, [File | Fs1])];
+ [_, File | Fs1] -> % end of included file
+ [Form | interpret_file_attr(Forms, 0, [File | Fs1])];
_ -> % start of included file
- [Form | interpret_file_attr(Forms, 0, [File, Delta | Fs])]
+ [Form | interpret_file_attr(Forms, 0, [File | Fs])]
end
end;
interpret_file_attr([Form0 | Forms], Delta, Fs) ->
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index d9d15e05f8..abff37e4bc 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(erl_compile).
@@ -23,6 +23,8 @@
-export([compile_cmdline/1]).
+-export_type([cmd_line_arg/0]).
+
%% Mapping from extension to {M,F} to run the correct compiler.
compiler(".erl") -> {compile, compile};
diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl
index a38b7639d8..61ce41f714 100644
--- a/lib/stdlib/src/erl_expand_records.erl
+++ b/lib/stdlib/src/erl_expand_records.erl
@@ -95,8 +95,9 @@ forms([F | Fs0], St0) ->
forms([], St) -> {[],St}.
clauses([{clause,Line,H0,G0,B0} | Cs0], St0) ->
- {H,St1} = head(H0, St0),
- {G,St2} = guard(G0, St1),
+ {H1,St1} = head(H0, St0),
+ {G1,St2} = guard(G0, St1),
+ {H,G} = optimize_is_record(H1, G1, St2),
{B,St3} = exprs(B0, St2),
{Cs,St4} = clauses(Cs0, St3),
{[{clause,Line,H,G,B} | Cs],St4};
@@ -800,5 +801,137 @@ imported(F, A, St) ->
error -> no
end.
+%%%
+%%% Replace is_record/3 in guards with matching if possible.
+%%%
+
+optimize_is_record(H0, G0, #exprec{compile=Opts}) ->
+ case opt_rec_vars(G0) of
+ [] ->
+ {H0,G0};
+ Rs0 ->
+ case lists:member(no_is_record_optimization, Opts) of
+ true ->
+ {H0,G0};
+ false ->
+ {H,Rs} = opt_pattern_list(H0, Rs0),
+ G = opt_remove(G0, Rs),
+ {H,G}
+ end
+ end.
+
+
+%% opt_rec_vars(Guards) -> Vars.
+%% Search through the guard expression, looking for
+%% variables referenced in those is_record/3 calls that
+%% will fail the entire guard if they evaluate to 'false'
+%%
+%% In the following code
+%%
+%% f(X, Y, Z) when is_record(X, r1) andalso
+%% (is_record(Y, r2) orelse is_record(Z, r3))
+%%
+%% the entire guard will be false if the record test for
+%% X fails, and the clause can be rewritten to:
+%%
+%% f({r1,...}=X, Y, Z) when true andalso
+%% (is_record(Y, r2) or is_record(Z, r3))
+%%
+opt_rec_vars([G|Gs]) ->
+ Rs = opt_rec_vars_1(G, orddict:new()),
+ opt_rec_vars(Gs, Rs);
+opt_rec_vars([]) -> orddict:new().
+
+opt_rec_vars([G|Gs], Rs0) ->
+ Rs1 = opt_rec_vars_1(G, orddict:new()),
+ Rs = ordsets:intersection(Rs0, Rs1),
+ opt_rec_vars(Gs, Rs);
+opt_rec_vars([], Rs) -> Rs.
+
+opt_rec_vars_1([T|Ts], Rs0) ->
+ Rs = opt_rec_vars_2(T, Rs0),
+ opt_rec_vars_1(Ts, Rs);
+opt_rec_vars_1([], Rs) -> Rs.
+
+opt_rec_vars_2({op,_,'and',A1,A2}, Rs) ->
+ opt_rec_vars_1([A1,A2], Rs);
+opt_rec_vars_2({op,_,'andalso',A1,A2}, Rs) ->
+ opt_rec_vars_1([A1,A2], Rs);
+opt_rec_vars_2({op,_,'orelse',Arg,{atom,_,fail}}, Rs) ->
+ %% Since the second argument guarantees failure,
+ %% it is safe to inspect the first argument.
+ opt_rec_vars_2(Arg, Rs);
+opt_rec_vars_2({call,_,{remote,_,{atom,_,erlang},{atom,_,is_record}},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}, Rs) ->
+ orddict:store(V, {Tag,Sz}, Rs);
+opt_rec_vars_2({call,_,{atom,_,is_record},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}, Rs) ->
+ orddict:store(V, {Tag,Sz}, Rs);
+opt_rec_vars_2(_, Rs) -> Rs.
+
+opt_pattern_list(Ps, Rs) ->
+ opt_pattern_list(Ps, Rs, []).
+
+opt_pattern_list([P0|Ps], Rs0, Acc) ->
+ {P,Rs} = opt_pattern(P0, Rs0),
+ opt_pattern_list(Ps, Rs, [P|Acc]);
+opt_pattern_list([], Rs, Acc) ->
+ {reverse(Acc),Rs}.
+
+opt_pattern({var,_,V}=Var, Rs0) ->
+ case orddict:find(V, Rs0) of
+ {ok,{Tag,Sz}} ->
+ Rs = orddict:store(V, {remove,Tag,Sz}, Rs0),
+ {opt_var(Var, Tag, Sz),Rs};
+ _ ->
+ {Var,Rs0}
+ end;
+opt_pattern({cons,Line,H0,T0}, Rs0) ->
+ {H,Rs1} = opt_pattern(H0, Rs0),
+ {T,Rs} = opt_pattern(T0, Rs1),
+ {{cons,Line,H,T},Rs};
+opt_pattern({tuple,Line,Es0}, Rs0) ->
+ {Es,Rs} = opt_pattern_list(Es0, Rs0),
+ {{tuple,Line,Es},Rs};
+opt_pattern({match,Line,Pa0,Pb0}, Rs0) ->
+ {Pa,Rs1} = opt_pattern(Pa0, Rs0),
+ {Pb,Rs} = opt_pattern(Pb0, Rs1),
+ {{match,Line,Pa,Pb},Rs};
+opt_pattern(P, Rs) -> {P,Rs}.
+
+opt_var({var,Line,_}=Var, Tag, Sz) ->
+ Rp = record_pattern(2, -1, ignore, Sz, Line, [{atom,Line,Tag}]),
+ {match,Line,{tuple,Line,Rp},Var}.
+
+opt_remove(Gs, Rs) ->
+ [opt_remove_1(G, Rs) || G <- Gs].
+
+opt_remove_1(Ts, Rs) ->
+ [opt_remove_2(T, Rs) || T <- Ts].
+
+opt_remove_2({op,L,'and'=Op,A1,A2}, Rs) ->
+ {op,L,Op,opt_remove_2(A1, Rs),opt_remove_2(A2, Rs)};
+opt_remove_2({op,L,'andalso'=Op,A1,A2}, Rs) ->
+ {op,L,Op,opt_remove_2(A1, Rs),opt_remove_2(A2, Rs)};
+opt_remove_2({op,L,'orelse',A1,A2}, Rs) ->
+ {op,L,'orelse',opt_remove_2(A1, Rs),A2};
+opt_remove_2({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}=A, Rs) ->
+ case orddict:find(V, Rs) of
+ {ok,{remove,Tag,Sz}} ->
+ {atom,Line,true};
+ _ ->
+ A
+ end;
+opt_remove_2({call,Line,{atom,_,is_record},
+ [{var,_,V},{atom,_,Tag},{integer,_,Sz}]}=A, Rs) ->
+ case orddict:find(V, Rs) of
+ {ok,{remove,Tag,Sz}} ->
+ {atom,Line,true};
+ _ ->
+ A
+ end;
+opt_remove_2(A, _) -> A.
+
neg_line(L) ->
erl_parse:set_line(L, fun(Line) -> -abs(Line) end).
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 16173d8210..b30b02a96f 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(erl_internal).
@@ -48,7 +48,7 @@
%%
-export([bif/2,bif/3,guard_bif/2,
- type_test/2,new_type_test/2,old_type_test/2]).
+ type_test/2,new_type_test/2,old_type_test/2,old_bif/2]).
-export([arith_op/2,bool_op/2,comp_op/2,list_op/2,send_op/2,op_type/2]).
%%---------------------------------------------------------------------------
@@ -87,6 +87,8 @@ guard_bif(is_reference, 1) -> true;
guard_bif(is_tuple, 1) -> true;
guard_bif(is_record, 2) -> true;
guard_bif(is_record, 3) -> true;
+guard_bif(binary_part, 2) -> true;
+guard_bif(binary_part, 3) -> true;
guard_bif(Name, A) when is_atom(Name), is_integer(A) -> false.
%% Erlang type tests.
@@ -229,11 +231,14 @@ bif(apply, 2) -> true;
bif(apply, 3) -> true;
bif(atom_to_binary, 2) -> true;
bif(atom_to_list, 1) -> true;
+bif(binary_part, 2) -> true;
+bif(binary_part, 3) -> true;
bif(binary_to_atom, 2) -> true;
bif(binary_to_existing_atom, 2) -> true;
bif(binary_to_list, 1) -> true;
bif(binary_to_list, 3) -> true;
bif(binary_to_term, 1) -> true;
+bif(binary_to_term, 2) -> true;
bif(bitsize, 1) -> true;
bif(bit_size, 1) -> true;
bif(bitstring_to_list, 1) -> true;
@@ -242,10 +247,14 @@ bif(check_process_code, 2) -> true;
bif(concat_binary, 1) -> true;
bif(date, 0) -> true;
bif(delete_module, 1) -> true;
+bif(demonitor, 1) -> true;
+bif(demonitor, 2) -> true;
bif(disconnect_node, 1) -> true;
bif(element, 2) -> true;
bif(erase, 0) -> true;
bif(erase, 1) -> true;
+bif(error, 1) -> true;
+bif(error, 2) -> true;
bif(exit, 1) -> true;
bif(exit, 2) -> true;
bif(float, 1) -> true;
@@ -261,6 +270,7 @@ bif(halt, 0) -> true;
bif(halt, 1) -> true;
bif(hd, 1) -> true;
bif(integer_to_list, 1) -> true;
+bif(integer_to_list, 2) -> true;
bif(iolist_size, 1) -> true;
bif(iolist_to_binary, 1) -> true;
bif(is_alive, 0) -> true;
@@ -290,11 +300,16 @@ bif(list_to_bitstring, 1) -> true;
bif(list_to_existing_atom, 1) -> true;
bif(list_to_float, 1) -> true;
bif(list_to_integer, 1) -> true;
+bif(list_to_integer, 2) -> true;
bif(list_to_pid, 1) -> true;
bif(list_to_tuple, 1) -> true;
bif(load_module, 2) -> true;
bif(make_ref, 0) -> true;
+bif(max,2) -> true;
+bif(min,2) -> true;
bif(module_loaded, 1) -> true;
+bif(monitor, 2) -> true;
+bif(monitor, 3) -> true;
bif(monitor_node, 2) -> true;
bif(node, 0) -> true;
bif(node, 1) -> true;
@@ -305,6 +320,7 @@ bif(open_port, 2) -> true;
bif(pid_to_list, 1) -> true;
bif(port_close, 1) -> true;
bif(port_command, 2) -> true;
+bif(port_command, 3) -> true;
bif(port_connect, 2) -> true;
bif(port_control, 3) -> true;
bif(pre_loaded, 0) -> true;
@@ -349,3 +365,134 @@ bif(unlink, 1) -> true;
bif(unregister, 1) -> true;
bif(whereis, 1) -> true;
bif(Name, A) when is_atom(Name), is_integer(A) -> false.
+
+-spec old_bif(Name::atom(), Arity::arity()) -> boolean().
+%% Returns true if erlang:Name/Arity is an old (pre R14) auto-imported BIF, false otherwise.
+%% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF
+%% (meaning implemented in C) or not.
+
+old_bif(abs, 1) -> true;
+old_bif(apply, 2) -> true;
+old_bif(apply, 3) -> true;
+old_bif(atom_to_binary, 2) -> true;
+old_bif(atom_to_list, 1) -> true;
+old_bif(binary_to_atom, 2) -> true;
+old_bif(binary_to_existing_atom, 2) -> true;
+old_bif(binary_to_list, 1) -> true;
+old_bif(binary_to_list, 3) -> true;
+old_bif(binary_to_term, 1) -> true;
+old_bif(bitsize, 1) -> true;
+old_bif(bit_size, 1) -> true;
+old_bif(bitstring_to_list, 1) -> true;
+old_bif(byte_size, 1) -> true;
+old_bif(check_process_code, 2) -> true;
+old_bif(concat_binary, 1) -> true;
+old_bif(date, 0) -> true;
+old_bif(delete_module, 1) -> true;
+old_bif(disconnect_node, 1) -> true;
+old_bif(element, 2) -> true;
+old_bif(erase, 0) -> true;
+old_bif(erase, 1) -> true;
+old_bif(exit, 1) -> true;
+old_bif(exit, 2) -> true;
+old_bif(float, 1) -> true;
+old_bif(float_to_list, 1) -> true;
+old_bif(garbage_collect, 0) -> true;
+old_bif(garbage_collect, 1) -> true;
+old_bif(get, 0) -> true;
+old_bif(get, 1) -> true;
+old_bif(get_keys, 1) -> true;
+old_bif(group_leader, 0) -> true;
+old_bif(group_leader, 2) -> true;
+old_bif(halt, 0) -> true;
+old_bif(halt, 1) -> true;
+old_bif(hd, 1) -> true;
+old_bif(integer_to_list, 1) -> true;
+old_bif(iolist_size, 1) -> true;
+old_bif(iolist_to_binary, 1) -> true;
+old_bif(is_alive, 0) -> true;
+old_bif(is_process_alive, 1) -> true;
+old_bif(is_atom, 1) -> true;
+old_bif(is_boolean, 1) -> true;
+old_bif(is_binary, 1) -> true;
+old_bif(is_bitstr, 1) -> true;
+old_bif(is_bitstring, 1) -> true;
+old_bif(is_float, 1) -> true;
+old_bif(is_function, 1) -> true;
+old_bif(is_function, 2) -> true;
+old_bif(is_integer, 1) -> true;
+old_bif(is_list, 1) -> true;
+old_bif(is_number, 1) -> true;
+old_bif(is_pid, 1) -> true;
+old_bif(is_port, 1) -> true;
+old_bif(is_reference, 1) -> true;
+old_bif(is_tuple, 1) -> true;
+old_bif(is_record, 2) -> true;
+old_bif(is_record, 3) -> true;
+old_bif(length, 1) -> true;
+old_bif(link, 1) -> true;
+old_bif(list_to_atom, 1) -> true;
+old_bif(list_to_binary, 1) -> true;
+old_bif(list_to_bitstring, 1) -> true;
+old_bif(list_to_existing_atom, 1) -> true;
+old_bif(list_to_float, 1) -> true;
+old_bif(list_to_integer, 1) -> true;
+old_bif(list_to_pid, 1) -> true;
+old_bif(list_to_tuple, 1) -> true;
+old_bif(load_module, 2) -> true;
+old_bif(make_ref, 0) -> true;
+old_bif(module_loaded, 1) -> true;
+old_bif(monitor_node, 2) -> true;
+old_bif(node, 0) -> true;
+old_bif(node, 1) -> true;
+old_bif(nodes, 0) -> true;
+old_bif(nodes, 1) -> true;
+old_bif(now, 0) -> true;
+old_bif(open_port, 2) -> true;
+old_bif(pid_to_list, 1) -> true;
+old_bif(port_close, 1) -> true;
+old_bif(port_command, 2) -> true;
+old_bif(port_connect, 2) -> true;
+old_bif(port_control, 3) -> true;
+old_bif(pre_loaded, 0) -> true;
+old_bif(process_flag, 2) -> true;
+old_bif(process_flag, 3) -> true;
+old_bif(process_info, 1) -> true;
+old_bif(process_info, 2) -> true;
+old_bif(processes, 0) -> true;
+old_bif(purge_module, 1) -> true;
+old_bif(put, 2) -> true;
+old_bif(register, 2) -> true;
+old_bif(registered, 0) -> true;
+old_bif(round, 1) -> true;
+old_bif(self, 0) -> true;
+old_bif(setelement, 3) -> true;
+old_bif(size, 1) -> true;
+old_bif(spawn, 1) -> true;
+old_bif(spawn, 2) -> true;
+old_bif(spawn, 3) -> true;
+old_bif(spawn, 4) -> true;
+old_bif(spawn_link, 1) -> true;
+old_bif(spawn_link, 2) -> true;
+old_bif(spawn_link, 3) -> true;
+old_bif(spawn_link, 4) -> true;
+old_bif(spawn_monitor, 1) -> true;
+old_bif(spawn_monitor, 3) -> true;
+old_bif(spawn_opt, 2) -> true;
+old_bif(spawn_opt, 3) -> true;
+old_bif(spawn_opt, 4) -> true;
+old_bif(spawn_opt, 5) -> true;
+old_bif(split_binary, 2) -> true;
+old_bif(statistics, 1) -> true;
+old_bif(term_to_binary, 1) -> true;
+old_bif(term_to_binary, 2) -> true;
+old_bif(throw, 1) -> true;
+old_bif(time, 0) -> true;
+old_bif(tl, 1) -> true;
+old_bif(trunc, 1) -> true;
+old_bif(tuple_size, 1) -> true;
+old_bif(tuple_to_list, 1) -> true;
+old_bif(unlink, 1) -> true;
+old_bif(unregister, 1) -> true;
+old_bif(whereis, 1) -> true;
+old_bif(Name, A) when is_atom(Name), is_integer(A) -> false.
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 94ad560549..cfb9f0ca98 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,7 @@
%% Value.
%% The option handling functions.
--spec bool_option(atom(), atom(), boolean(), [_]) -> boolean().
+-spec bool_option(atom(), atom(), boolean(), [compile:option()]) -> boolean().
bool_option(On, Off, Default, Opts) ->
foldl(fun (Opt, _Def) when Opt =:= On -> true;
@@ -60,6 +60,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
(_Opt, Def) -> Def
end, Default, Opts).
+%% The maximum number of arguments allowed for a function.
+
+-define(MAX_ARGUMENTS, 255).
+
%% The error and warning info structures, {Line,Module,Descriptor},
%% are kept in their seperate fields in the lint state record together
%% with the name of the file (when a new file is entered, marked by
@@ -72,6 +76,10 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%%-define(DEBUGF(X,Y), io:format(X, Y)).
-define(DEBUGF(X,Y), void).
+-type line() :: erl_scan:line(). % a convenient alias
+-type fa() :: {atom(), arity()}. % function+arity
+-type ta() :: {atom(), arity()}. % type+arity
+
%% Usage of records, functions, and imports. The variable table, which
%% is passed on as an argument, holds the usage of variables.
-record(usage, {
@@ -94,9 +102,11 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
mod_imports=dict:new() :: dict(), %Module Imports
compile=[], %Compile flags
records=dict:new() :: dict(), %Record definitions
+ locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned)
+ no_auto=gb_sets:empty() :: gb_set(), %Functions explicitly not autoimported
defined=gb_sets:empty() :: gb_set(), %Defined fuctions
- on_load=[] :: [{atom(),integer()}], %On-load function
- on_load_line=0 :: integer(), %Line for on_load
+ on_load=[] :: [fa()], %On-load function
+ on_load_line=0 :: line(), %Line for on_load
clashes=[], %Exported functions named as BIFs
not_deprecated=[], %Not considered deprecated
func=[], %Current function
@@ -110,10 +120,11 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) ->
%outside any fun or lc
xqlc= false :: boolean(), %true if qlc.hrl included
new = false :: boolean(), %Has user-defined 'new/N'
- called= [], %Called functions
+ called= [] :: [{fa(),line()}], %Called functions
usage = #usage{} :: #usage{},
specs = dict:new() :: dict(), %Type specifications
- types = dict:new() :: dict() %Type definitions
+ types = dict:new() :: dict(), %Type definitions
+ exp_types=gb_sets:empty():: gb_set() %Exported types
}).
-type lint_state() :: #lint{}.
@@ -161,6 +172,9 @@ format_error({bad_nowarn_unused_function,{F,A}}) ->
io_lib:format("function ~w/~w undefined", [F,A]);
format_error({bad_nowarn_bif_clash,{F,A}}) ->
io_lib:format("function ~w/~w undefined", [F,A]);
+format_error(disallowed_nowarn_bif_clash) ->
+ io_lib:format("compile directive nowarn_bif_clash is no longer allowed,~n"
+ " - use explicit module names or -compile({no_auto_import, [F/A]})", []);
format_error({bad_nowarn_deprecated_function,{M,F,A}}) ->
io_lib:format("~w:~w/~w is not a deprecated function", [M,F,A]);
format_error({bad_on_load,Term}) ->
@@ -186,13 +200,21 @@ format_error({define_import,{F,A}}) ->
io_lib:format("defining imported function ~w/~w", [F,A]);
format_error({unused_function,{F,A}}) ->
io_lib:format("function ~w/~w is unused", [F,A]);
-format_error({redefine_bif,{F,A}}) ->
- io_lib:format("defining BIF ~w/~w", [F,A]);
format_error({call_to_redefined_bif,{F,A}}) ->
- io_lib:format("call to ~w/~w will call erlang:~w/~w; "
- "not ~w/~w in this module \n"
- " (add an explicit module name to the call to avoid this error)",
- [F,A,F,A,F,A]);
+ io_lib:format("ambiguous call of overridden auto-imported BIF ~w/~w~n"
+ " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" "
+ "to resolve name clash", [F,A,F,A,F,A]);
+format_error({call_to_redefined_old_bif,{F,A}}) ->
+ io_lib:format("ambiguous call of overridden pre R14 auto-imported BIF ~w/~w~n"
+ " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" "
+ "to resolve name clash", [F,A,F,A,F,A]);
+format_error({redefine_old_bif_import,{F,A}}) ->
+ io_lib:format("import directive overrides pre R14 auto-imported BIF ~w/~w~n"
+ " - use \"-compile({no_auto_import,[~w/~w]}).\" "
+ "to resolve name clash", [F,A,F,A]);
+format_error({redefine_bif_import,{F,A}}) ->
+ io_lib:format("import directive overrides auto-imported BIF ~w/~w~n"
+ " - use \"-compile({no_auto_import,[~w/~w]}).\" to resolve name clash", [F,A,F,A]);
format_error({deprecated, MFA, ReplacementMFA, Rel}) ->
io_lib:format("~s is deprecated and will be removed in ~s; use ~s",
@@ -208,11 +230,17 @@ format_error({obsolete_guard, {F, A}}) ->
io_lib:format("~p/~p obsolete", [F, A]);
format_error({reserved_for_future,K}) ->
io_lib:format("atom ~w: future reserved keyword - rename or quote", [K]);
+format_error({too_many_arguments,Arity}) ->
+ io_lib:format("too many arguments (~w) - "
+ "maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
%% --- patterns and guards ---
format_error(illegal_pattern) -> "illegal pattern";
format_error(illegal_bin_pattern) ->
"binary patterns cannot be matched in parallel using '='";
format_error(illegal_expr) -> "illegal expression";
+format_error({illegal_guard_local_call, {F,A}}) ->
+ io_lib:format("call to local/imported function ~w/~w is illegal in guard",
+ [F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
%% --- exports ---
format_error({explicit_export,F,A}) ->
@@ -242,10 +270,10 @@ format_error({untyped_record,T}) ->
format_error({unbound_var,V}) ->
io_lib:format("variable ~w is unbound", [V]);
format_error({unsafe_var,V,{What,Where}}) ->
- io_lib:format("variable ~w unsafe in ~w ~s",
+ io_lib:format("variable ~w unsafe in ~w ~s",
[V,What,format_where(Where)]);
format_error({exported_var,V,{What,Where}}) ->
- io_lib:format("variable ~w exported from ~w ~s",
+ io_lib:format("variable ~w exported from ~w ~s",
[V,What,format_where(Where)]);
format_error({shadowed_var,V,In}) ->
io_lib:format("variable ~w shadowed in ~w", [V,In]);
@@ -290,22 +318,26 @@ format_error({ill_defined_behaviour_callbacks,Behaviour}) ->
%% --- types and specs ---
format_error({singleton_typevar, Name}) ->
io_lib:format("type variable ~w is only used once (is unbound)", [Name]);
+format_error({bad_export_type, _ETs}) ->
+ io_lib:format("bad export_type declaration", []);
+format_error({duplicated_export_type, {T, A}}) ->
+ io_lib:format("type ~w/~w already exported", [T, A]);
format_error({undefined_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s undefined", [TypeName, gen_type_paren(Arity)]);
format_error({unused_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is unused", [TypeName, gen_type_paren(Arity)]);
format_error({new_builtin_type, {TypeName, Arity}}) ->
io_lib:format("type ~w~s is a new builtin type; "
- "its (re)definition is allowed only until the next release",
+ "its (re)definition is allowed only until the next release",
[TypeName, gen_type_paren(Arity)]);
format_error({builtin_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
+ io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
[TypeName, gen_type_paren(Arity)]);
format_error({renamed_type, OldName, NewName}) ->
io_lib:format("type ~w() is now called ~w(); "
"please use the new name instead", [OldName, NewName]);
format_error({redefine_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s already defined",
+ io_lib:format("type ~w~s already defined",
[TypeName, gen_type_paren(Arity)]);
format_error({type_syntax, Constr}) ->
io_lib:format("bad ~w type", [Constr]);
@@ -354,7 +386,7 @@ pseudolocals() ->
%%
%% Used by erl_eval.erl to check commands.
-%%
+%%
exprs(Exprs, BindingsList) ->
exprs_opt(Exprs, BindingsList, []).
@@ -362,7 +394,7 @@ exprs_opt(Exprs, BindingsList, Opts) ->
{St0,Vs} = foldl(fun({{record,_SequenceNumber,_Name},Attr0}, {St1,Vs1}) ->
Attr = zip_file_and_line(Attr0, "none"),
{attribute_state(Attr, St1),Vs1};
- ({V,_}, {St1,Vs1}) ->
+ ({V,_}, {St1,Vs1}) ->
{St1,[{V,{bound,unused,[]}} | Vs1]}
end, {start("nofile",Opts),[]}, BindingsList),
Vt = orddict:from_list(Vs),
@@ -391,7 +423,7 @@ module(Forms) ->
Opts = compiler_options(Forms),
St = forms(Forms, start("nofile", Opts)),
return_status(St).
-
+
module(Forms, FileName) ->
Opts = compiler_options(Forms),
St = forms(Forms, start(FileName, Opts)),
@@ -506,7 +538,7 @@ pack_errors(Es) ->
%% Sort on line number.
pack_warnings(Ws) ->
- [{File,lists:sort([W || {F,W} <- Ws, F =:= File])} ||
+ [{File,lists:sort([W || {F,W} <- Ws, F =:= File])} ||
File <- lists:usort([F || {F,_} <- Ws])].
%% add_error(ErrorDescriptor, State) -> State'
@@ -516,13 +548,13 @@ pack_warnings(Ws) ->
add_error(E, St) -> St#lint{errors=[{St#lint.file,E}|St#lint.errors]}.
-add_error(FileLine, E, St) ->
+add_error(FileLine, E, St) ->
{File,Location} = loc(FileLine),
add_error({Location,erl_lint,E}, St#lint{file = File}).
add_warning(W, St) -> St#lint{warnings=[{St#lint.file,W}|St#lint.warnings]}.
-add_warning(FileLine, W, St) ->
+add_warning(FileLine, W, St) ->
{File,Location} = loc(FileLine),
add_warning({Location,erl_lint,W}, St#lint{file = File}).
@@ -538,8 +570,12 @@ loc(L) ->
forms(Forms0, St0) ->
Forms = eval_file_attribute(Forms0, St0),
+ Locals = local_functions(Forms),
+ AutoImportSuppressed = auto_import_suppressed(St0#lint.compile),
+ StDeprecated = disallowed_compile_flags(Forms,St0),
%% Line numbers are from now on pairs {File,Line}.
- St1 = includes_qlc_hrl(Forms, St0),
+ St1 = includes_qlc_hrl(Forms, StDeprecated#lint{locals = Locals,
+ no_auto = AutoImportSuppressed}),
St2 = bif_clashes(Forms, St1),
St3 = not_deprecated(Forms, St2),
St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms),
@@ -561,7 +597,7 @@ pre_scan([_ | Fs], St) ->
pre_scan(Fs, St);
pre_scan([], St) ->
St.
-
+
includes_qlc_hrl(Forms, St) ->
%% QLC calls erl_lint several times, sometimes with the compile
%% attribute removed. The file attribute, however, is left as is.
@@ -667,6 +703,8 @@ attribute_state({attribute,L,extends,_M}, St) ->
add_error(L, invalid_extends, St);
attribute_state({attribute,L,export,Es}, St) ->
export(L, Es, St);
+attribute_state({attribute,L,export_type,Es}, St) ->
+ export_type(L, Es, St);
attribute_state({attribute,L,import,Is}, St) ->
import(L, Is, St);
attribute_state({attribute,L,record,{Name,Fields}}, St) ->
@@ -724,27 +762,38 @@ bif_clashes(Forms, St) ->
Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn),
St#lint{clashes=Clashes}.
--spec is_bif_clash(atom(), byte(), lint_state()) -> boolean().
-
-is_bif_clash(_Name, _Arity, #lint{clashes=[]}) ->
- false;
-is_bif_clash(Name, Arity, #lint{clashes=Clashes}) ->
- ordsets:is_element({Name,Arity}, Clashes).
-
%% not_deprecated(Forms, State0) -> State
not_deprecated(Forms, St0) ->
%% There are no line numbers in St0#lint.compile.
- MFAsL = [{MFA,L} ||
+ MFAsL = [{MFA,L} ||
{attribute, L, compile, Args} <- Forms,
{nowarn_deprecated_function, MFAs0} <- lists:flatten([Args]),
MFA <- lists:flatten([MFAs0])],
Nowarn = [MFA || {MFA,_L} <- MFAsL],
- Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL,
+ Bad = [MFAL || {{M,F,A},_L}=MFAL <- MFAsL,
otp_internal:obsolete(M, F, A) =:= no],
St1 = func_line_warning(bad_nowarn_deprecated_function, Bad, St0),
St1#lint{not_deprecated = ordsets:from_list(Nowarn)}.
+%% The nowarn_bif_clash directive is not only deprecated, it's actually an error from R14A
+disallowed_compile_flags(Forms, St0) ->
+ %% There are (still) no line numbers in St0#lint.compile.
+ Errors0 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
+ {attribute,[{line,{_,L}}],compile,nowarn_bif_clash} <- Forms ],
+ Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} ||
+ {attribute,[{line,{_,L}}],compile,{nowarn_bif_clash, {_,_}}} <- Forms ],
+ Disabled = (not is_warn_enabled(bif_clash, St0)),
+ Errors = if
+ Disabled andalso Errors0 =:= [] ->
+ [{St0#lint.file,{erl_lint,disallowed_nowarn_bif_clash}} | St0#lint.errors];
+ Disabled ->
+ Errors0 ++ Errors1 ++ St0#lint.errors;
+ true ->
+ Errors1 ++ St0#lint.errors
+ end,
+ St0#lint{errors=Errors}.
+
%% post_traversal_check(Forms, State0) -> State.
%% Do some further checking after the forms have been traversed and
%% data about calls etc. have been collected.
@@ -862,7 +911,7 @@ check_deprecated(Forms, St0) ->
Bad = [{E,L} || {attribute, L, deprecated, Depr} <- Forms,
D <- lists:flatten([Depr]),
E <- depr_cat(D, X, Mod)],
- foldl(fun ({E,L}, St1) ->
+ foldl(fun ({E,L}, St1) ->
add_error(L, E, St1)
end, St0, Bad).
@@ -912,7 +961,7 @@ check_imports(Forms, St0) ->
true ->
Usage = St0#lint.usage,
Unused = ordsets:subtract(St0#lint.imports, Usage#usage.imported),
- Imports = [{{FA,list_to_atom(package_to_string(Mod))},L}
+ Imports = [{{FA,list_to_atom(package_to_string(Mod))},L}
|| {attribute,L,import,{Mod,Fs}} <- Forms,
FA <- lists:usort(Fs)],
Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2],
@@ -932,7 +981,7 @@ check_unused_functions(Forms, St0) ->
Opts = St1#lint.compile,
case member(export_all, Opts) orelse
not is_warn_enabled(unused_function, St1) of
- true ->
+ true ->
St1;
false ->
Nowarn = nowarn_function(nowarn_unused_function, Opts),
@@ -1003,12 +1052,13 @@ check_option_functions(Forms, Tag0, Type, St0) ->
{Tag, FAs0} <- lists:flatten([Args]),
Tag0 =:= Tag,
FA <- lists:flatten([FAs0])],
- DefFunctions = gb_sets:to_list(St0#lint.defined) -- pseudolocals(),
+ DefFunctions = (gb_sets:to_list(St0#lint.defined) -- pseudolocals()) ++
+ [{F,A} || {{F,A},_} <- orddict:to_list(St0#lint.imports)],
Bad = [{FA,L} || {FA,L} <- FAsL, not member(FA, DefFunctions)],
func_line_error(Type, Bad, St0).
nowarn_function(Tag, Opts) ->
- ordsets:from_list([FA || {Tag1,FAs} <- Opts,
+ ordsets:from_list([FA || {Tag1,FAs} <- Opts,
Tag1 =:= Tag,
FA <- lists:flatten([FAs])]).
@@ -1048,10 +1098,10 @@ check_unused_records(Forms, St0) ->
%% functions count.
Usage = St0#lint.usage,
UsedRecords = sets:to_list(Usage#usage.used_records),
- URecs = foldl(fun (Used, Recs) ->
- dict:erase(Used, Recs)
+ URecs = foldl(fun (Used, Recs) ->
+ dict:erase(Used, Recs)
end, St0#lint.records, UsedRecords),
- Unused = [{Name,FileLine} ||
+ Unused = [{Name,FileLine} ||
{Name,{FileLine,_Fields}} <- dict:to_list(URecs),
element(1, loc(FileLine)) =:= FirstFile],
foldl(fun ({N,L}, St) ->
@@ -1061,18 +1111,19 @@ check_unused_records(Forms, St0) ->
St0
end.
-%% For storing the import list we use the orddict module.
+%% For storing the import list we use the orddict module.
%% We know an empty set is [].
-%% export(Line, Exports, State) -> State.
+-spec export(line(), [fa()], lint_state()) -> lint_state().
%% Mark functions as exported, also as called from the export line.
export(Line, Es, #lint{exports = Es0, called = Called} = St0) ->
- {Es1,C1,St1} =
+ {Es1,C1,St1} =
foldl(fun (NA, {E,C,St2}) ->
St = case gb_sets:is_element(NA, E) of
true ->
- add_warning(Line, {duplicated_export, NA}, St2);
+ Warn = {duplicated_export,NA},
+ add_warning(Line, Warn, St2);
false ->
St2
end,
@@ -1081,8 +1132,31 @@ export(Line, Es, #lint{exports = Es0, called = Called} = St0) ->
{Es0,Called,St0}, Es),
St1#lint{exports = Es1, called = C1}.
-%% import(Line, Imports, State) -> State.
-%% imported(Name, Arity, State) -> {yes,Module} | no.
+-spec export_type(line(), [ta()], lint_state()) -> lint_state().
+%% Mark types as exported; also mark them as used from the export line.
+
+export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) ->
+ UTs0 = Usage#usage.used_types,
+ try foldl(fun ({T,A}=TA, {E,U,St2}) when is_atom(T), is_integer(A) ->
+ St = case gb_sets:is_element(TA, E) of
+ true ->
+ Warn = {duplicated_export_type,TA},
+ add_warning(Line, Warn, St2);
+ false ->
+ St2
+ end,
+ {gb_sets:add_element(TA, E), dict:store(TA, Line, U), St}
+ end,
+ {ETs0,UTs0,St0}, ETs) of
+ {ETs1,UTs1,St1} ->
+ St1#lint{usage = Usage#usage{used_types = UTs1}, exp_types = ETs1}
+ catch
+ error:_ ->
+ add_error(Line, {bad_export_type, ETs}, St0)
+ end.
+
+-type import() :: {module(), [fa()]} | module().
+-spec import(line(), import(), lint_state()) -> lint_state().
import(Line, {Mod,Fs}, St) ->
Mod1 = package_to_string(Mod),
@@ -1094,11 +1168,41 @@ import(Line, {Mod,Fs}, St) ->
St#lint{imports=add_imports(list_to_atom(Mod1), Mfs,
St#lint.imports)};
Efs ->
- foldl(fun (Ef, St0) ->
- add_error(Line, {redefine_import,Ef},
- St0)
+ {Err, St1} =
+ foldl(fun ({bif,{F,A},_}, {Err,St0}) ->
+ %% BifClash - import directive
+ Warn = is_warn_enabled(bif_clash, St0)
+ and (not bif_clash_specifically_disabled(St0,{F,A})),
+ AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}),
+ OldBif = erl_internal:old_bif(F,A),
+ {Err,if
+ Warn and (not AutoImpSup) and OldBif ->
+ add_error
+ (Line,
+ {redefine_old_bif_import, {F,A}},
+ St0);
+ Warn and (not AutoImpSup) ->
+ add_warning
+ (Line,
+ {redefine_bif_import, {F,A}},
+ St0);
+ true ->
+ St0
+ end};
+ (Ef, {_Err,St0}) ->
+ {true,add_error(Line,
+ {redefine_import,Ef},
+ St0)}
end,
- St, Efs)
+ {false,St}, Efs),
+ if
+ not Err ->
+ St1#lint{imports=
+ add_imports(list_to_atom(Mod1), Mfs,
+ St#lint.imports)};
+ true ->
+ St1
+ end
end;
false ->
add_error(Line, {bad_module_name, Mod1}, St)
@@ -1141,13 +1245,15 @@ check_imports(_Line, Fs, Is) ->
add_imports(Mod, Fs, Is) ->
foldl(fun (F, Is0) -> orddict:store(F, Mod, Is0) end, Is, Fs).
+-spec imported(atom(), arity(), lint_state()) -> {'yes',module()} | 'no'.
+
imported(F, A, St) ->
case orddict:find({F,A}, St#lint.imports) of
{ok,Mod} -> {yes,Mod};
error -> no
end.
-%% on_load(Line, Val, State) -> State.
+-spec on_load(line(), fa(), lint_state()) -> lint_state().
%% Check an on_load directive and remember it.
on_load(Line, {Name,Arity}=Fa, #lint{on_load=OnLoad0}=St0)
@@ -1179,7 +1285,7 @@ check_on_load(#lint{defined=Defined,on_load=[{_,0}=Fa],
end;
check_on_load(St) -> St.
-%% call_function(Line, Name, Arity, State) -> State.
+-spec call_function(line(), atom(), arity(), lint_state()) -> lint_state().
%% Add to both called and calls.
call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
@@ -1191,12 +1297,6 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) ->
end,
St#lint{called=[{NA,Line}|Cd], usage=Usage}.
-%% is_function_exported(Name, Arity, State) -> false|true.
-
-is_function_exported(Name, Arity, #lint{exports=Exports,compile=Compile}) ->
- gb_sets:is_element({Name,Arity}, Exports) orelse
- member(export_all, Compile).
-
%% function(Line, Name, Arity, Clauses, State) -> State.
function(Line, instance, _Arity, _Cs, St) when St#lint.global_vt =/= [] ->
@@ -1205,7 +1305,7 @@ function(Line, Name, Arity, Cs, St0) ->
St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}),
clauses(Cs, St1#lint.global_vt, St1).
-%% define_function(Line, Name, Arity, State) -> State.
+-spec define_function(line(), atom(), arity(), lint_state()) -> lint_state().
define_function(Line, Name, Arity, St0) ->
St1 = keyword_warning(Line, Name, St0),
@@ -1214,18 +1314,18 @@ define_function(Line, Name, Arity, St0) ->
true ->
add_error(Line, {redefine_function,NA}, St1);
false ->
- St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)},
- St = case erl_internal:bif(Name, Arity) andalso
- not is_function_exported(Name, Arity, St2) of
- true -> add_warning(Line, {redefine_bif,NA}, St2);
- false -> St2
- end,
- case imported(Name, Arity, St) of
- {yes,_M} -> add_error(Line, {define_import,NA}, St);
- no -> St
+ St2 = function_check_max_args(Line, Arity, St1),
+ St3 = St2#lint{defined=gb_sets:add_element(NA, St2#lint.defined)},
+ case imported(Name, Arity, St3) of
+ {yes,_M} -> add_error(Line, {define_import,NA}, St3);
+ no -> St3
end
end.
+function_check_max_args(Line, Arity, St) when Arity > ?MAX_ARGUMENTS ->
+ add_error(Line, {too_many_arguments,Arity}, St);
+function_check_max_args(_, _, St) -> St.
+
%% clauses([Clause], VarTable, State) -> {VarTable, State}.
clauses(Cs, Vt, St) ->
@@ -1258,7 +1358,7 @@ head([P|Ps], Vt, Old, St0) ->
{vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt1,Bvt2),St2};
head([], _Vt, _Env, St) -> {[],[],St}.
-%% pattern(Pattern, VarTable, Old, BinVarTable, State) ->
+%% pattern(Pattern, VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,BinVarTable,State}.
%% Check pattern return variables. Old is the set of variables used for
%% deciding whether an occurrence is a binding occurrence or a use, and
@@ -1276,7 +1376,7 @@ pattern(P, Vt, St) ->
pattern({var,_Line,'_'}, _Vt, _Old, _Bvt, St) ->
{[],[],St}; %Ignore anonymous variable
-pattern({var,Line,V}, _Vt, Old, Bvt, St) ->
+pattern({var,Line,V}, _Vt, Old, Bvt, St) ->
pat_var(V, Line, Old, Bvt, St);
pattern({char,_Line,_C}, _Vt, _Old, _Bvt, St) -> {[],[],St};
pattern({integer,_Line,_I}, _Vt, _Old, _Bvt, St) -> {[],[],St};
@@ -1294,7 +1394,7 @@ pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) ->
%%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) ->
%% pattern_list(Ps, Vt, Old, Bvt, St);
pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) ->
- {Vt1,St1} =
+ {Vt1,St1} =
check_record(Line, Name, St,
fun (Dfs, St1) ->
pattern_field(Field, Name, Dfs, St1)
@@ -1309,7 +1409,7 @@ pattern({record_field,Line,_,_}=M, _Vt, _Old, _Bvt, St0) ->
end;
pattern({record,Line,Name,Pfs}, Vt, Old, Bvt, St) ->
case dict:find(Name, St#lint.records) of
- {ok,{_Line,Fields}} ->
+ {ok,{_Line,Fields}} ->
St1 = used_record(Name, St),
pattern_fields(Pfs, Name, Fields, Vt, Old, Bvt, St1);
error -> {[],[],add_error(Line, {undefined_record,Name}, St)}
@@ -1369,7 +1469,7 @@ reject_bin_alias({cons,_,H1,T1}, {cons,_,H2,T2}, St0) ->
reject_bin_alias(T1, T2, St);
reject_bin_alias({tuple,_,Es1}, {tuple,_,Es2}, St) ->
reject_bin_alias_list(Es1, Es2, St);
-reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
+reject_bin_alias({record,_,Name1,Pfs1}, {record,_,Name2,Pfs2},
#lint{records=Recs}=St) ->
case {dict:find(Name1, Recs),dict:find(Name2, Recs)} of
{{ok,{_Line1,Fields1}},{ok,{_Line2,Fields2}}} ->
@@ -1451,7 +1551,7 @@ is_pattern_expr_1({op,_Line,Op,A1,A2}) ->
erl_internal:arith_op(Op, 2) andalso all(fun is_pattern_expr/1, [A1,A2]);
is_pattern_expr_1(_Other) -> false.
-%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
+%% pattern_bin([Element], VarTable, Old, BinVarTable, State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
%% Check a pattern group. BinVarTable are used binsize variables.
@@ -1498,7 +1598,7 @@ good_string_size_type(default, Ts) ->
end, Ts);
good_string_size_type(_, _) -> false.
-%% pat_bit_expr(Pattern, OldVarTable, BinVarTable,State) ->
+%% pat_bit_expr(Pattern, OldVarTable, BinVarTable,State) ->
%% {UpdVarTable,UpdBinVarTable,State}.
%% Check pattern bit expression, only allow really valid patterns!
@@ -1513,7 +1613,7 @@ pat_bit_expr(P, _Old, _Bvt, St) ->
false -> {[],[],add_error(element(2, P), illegal_pattern, St)}
end.
-%% pat_bit_size(Size, VarTable, BinVarTable, State) ->
+%% pat_bit_size(Size, VarTable, BinVarTable, State) ->
%% {Value,UpdVarTable,UpdBinVarTable,State}.
%% Check pattern size expression, only allow really valid sizes!
@@ -1596,7 +1696,7 @@ bit_size_check(Line, Size, #bittype{type=Type,unit=Unit}, St) ->
Sz = Unit * Size, %Total number of bits!
St2 = elemtype_check(Line, Type, Sz, St),
{Sz,St2}.
-
+
elemtype_check(_Line, float, 32, St) -> St;
elemtype_check(_Line, float, 64, St) -> St;
elemtype_check(Line, float, _Size, St) ->
@@ -1678,8 +1778,6 @@ gexpr({cons,_Line,H,T}, Vt, St) ->
gexpr_list([H,T], Vt, St);
gexpr({tuple,_Line,Es}, Vt, St) ->
gexpr_list(Es, Vt, St);
-%%gexpr({struct,_Line,_Tag,Es}, Vt, St) ->
-%% gexpr_list(Es, Vt, St);
gexpr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end );
@@ -1710,7 +1808,7 @@ gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
gexpr({call,Line,{atom,_Lr,is_record},[E,R]}, Vt, St0) ->
{Asvt,St1} = gexpr_list([E,R], Vt, St0),
{Asvt,add_error(Line, illegal_guard_expr, St1)};
-gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
+gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
Vt, St0) ->
gexpr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0);
gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,_,_Name},{integer,_,_}]},
@@ -1725,14 +1823,22 @@ gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args}
gexpr({call,Line,{atom,_La,F},As}, Vt, St0) ->
{Asvt,St1} = gexpr_list(As, Vt, St0),
A = length(As),
- case erl_internal:guard_bif(F, A) of
+ %% BifClash - Function called in guard
+ case erl_internal:guard_bif(F, A) andalso no_guard_bif_clash(St1,{F,A}) of
true ->
%% Also check that it is auto-imported.
case erl_internal:bif(F, A) of
true -> {Asvt,St1};
false -> {Asvt,add_error(Line, {explicit_export,F,A}, St1)}
end;
- false -> {Asvt,add_error(Line, illegal_guard_expr, St1)}
+ false ->
+ case is_local_function(St1#lint.locals,{F,A}) orelse
+ is_imported_function(St1#lint.imports,{F,A}) of
+ true ->
+ {Asvt,add_error(Line, {illegal_guard_local_call,{F,A}}, St1)};
+ _ ->
+ {Asvt,add_error(Line, illegal_guard_expr, St1)}
+ end
end;
gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) ->
{Asvt,St1} = gexpr_list(As, Vt, St0),
@@ -1777,7 +1883,7 @@ is_guard_test(E) ->
%% is_guard_test(Expression, Forms) -> boolean().
is_guard_test(Expression, Forms) ->
RecordAttributes = [A || A = {attribute, _, record, _D} <- Forms],
- St0 = foldl(fun(Attr0, St1) ->
+ St0 = foldl(fun(Attr0, St1) ->
Attr = zip_file_and_line(Attr0, "none"),
attribute_state(Attr, St1)
end, start(), RecordAttributes),
@@ -1798,7 +1904,7 @@ is_guard_test2(G, RDs) ->
%% is_guard_expr(Expression) -> boolean().
%% Test if an expression is a guard expression.
-is_guard_expr(E) -> is_gexpr(E, []).
+is_guard_expr(E) -> is_gexpr(E, []).
is_gexpr({var,_L,_V}, _RDs) -> true;
is_gexpr({char,_L,_C}, _RDs) -> true;
@@ -1820,7 +1926,7 @@ is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) ->
is_gexpr({record,L,Name,Inits}, RDs) ->
is_gexpr_fields(Inits, L, Name, RDs);
is_gexpr({bin,_L,Fs}, RDs) ->
- all(fun ({bin_element,_Line,E,Sz,_Ts}) ->
+ all(fun ({bin_element,_Line,E,Sz,_Ts}) ->
is_gexpr(E, RDs) and (Sz =:= default orelse is_gexpr(Sz, RDs))
end, Fs);
is_gexpr({call,_L,{atom,_Lf,F},As}, RDs) ->
@@ -1895,15 +2001,13 @@ expr({bc,_Line,E,Qs}, Vt0, St0) ->
{vtold(Vt,Vt0),St}; %Don't export local variables
expr({tuple,_Line,Es}, Vt, St) ->
expr_list(Es, Vt, St);
-%%expr({struct,Line,Tag,Es}, Vt, St) ->
-%% expr_list(Es, Vt, St);
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
expr({record,Line,Name,Inits}, Vt, St) ->
check_record(Line, Name, St,
- fun (Dfs, St1) ->
- init_fields(Inits, Line, Name, Dfs, Vt, St1)
+ fun (Dfs, St1) ->
+ init_fields(Inits, Line, Name, Dfs, Vt, St1)
end);
expr({record_field,Line,_,_}=M, _Vt, St0) ->
case expand_package(M, St0) of
@@ -1958,8 +2062,11 @@ expr({'fun',Line,Body}, Vt, St) ->
{Bvt, St1} = fun_clauses(Cs, Vt, St),
{vtupdate(Bvt, Vt), St1};
{function,F,A} ->
+ %% BifClash - Fun expression
%% N.B. Only allows BIFs here as well, NO IMPORTS!!
- case erl_internal:bif(F, A) of
+ case ((not is_local_function(St#lint.locals,{F,A})) andalso
+ (erl_internal:bif(F, A) andalso
+ (not is_autoimport_suppressed(St#lint.no_auto,{F,A})))) of
true -> {[],St};
false -> {[],call_function(Line, F, A, St)}
end;
@@ -1969,7 +2076,7 @@ expr({'fun',Line,Body}, Vt, St) ->
expr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) ->
{Rvt,St1} = expr(E, Vt, St0),
{Rvt,exist_record(Ln, Name, St1)};
-expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
+expr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]},
Vt, St0) ->
expr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0);
expr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,is_record}]},As}, Vt, St) ->
@@ -1992,29 +2099,54 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) ->
St1 = keyword_warning(La, F, St0),
{Asvt,St2} = expr_list(As, Vt, St1),
A = length(As),
- case erl_internal:bif(F, A) of
+ IsLocal = is_local_function(St2#lint.locals,{F,A}),
+ IsAutoBif = erl_internal:bif(F, A),
+ AutoSuppressed = is_autoimport_suppressed(St2#lint.no_auto,{F,A}),
+ Warn = is_warn_enabled(bif_clash, St2) and (not bif_clash_specifically_disabled(St2,{F,A})),
+ Imported = imported(F, A, St2),
+ case ((not IsLocal) andalso (Imported =:= no) andalso
+ IsAutoBif andalso (not AutoSuppressed)) of
true ->
St3 = deprecated_function(Line, erlang, F, As, St2),
- {Asvt,case is_warn_enabled(bif_clash, St3) andalso
- is_bif_clash(F, A, St3) of
- false ->
- St3;
- true ->
- add_error(Line, {call_to_redefined_bif,{F,A}}, St3)
- end};
+ {Asvt,St3};
false ->
- {Asvt,case imported(F, A, St2) of
+ {Asvt,case Imported of
{yes,M} ->
St3 = check_remote_function(Line, M, F, As, St2),
U0 = St3#lint.usage,
Imp = ordsets:add_element({{F,A},M},U0#usage.imported),
St3#lint{usage=U0#usage{imported = Imp}};
no ->
- case {F,A} of
- {record_info,2} ->
+ case {F,A} of
+ {record_info,2} ->
check_record_info_call(Line,La,As,St2);
- N when N =:= St2#lint.func -> St2;
- _ -> call_function(Line, F, A, St2)
+ N ->
+ %% BifClash - function call
+ %% Issue these warnings/errors even if it's a recursive call
+ St3 = if
+ (not AutoSuppressed) andalso IsAutoBif andalso Warn ->
+ case erl_internal:old_bif(F,A) of
+ true ->
+ add_error
+ (Line,
+ {call_to_redefined_old_bif, {F,A}},
+ St2);
+ false ->
+ add_warning
+ (Line,
+ {call_to_redefined_bif, {F,A}},
+ St2)
+ end;
+ true ->
+ St2
+ end,
+ %% ...but don't lint recursive calls
+ if
+ N =:= St3#lint.func ->
+ St3;
+ true ->
+ call_function(Line, F, A, St3)
+ end
end
end}
end;
@@ -2155,7 +2287,7 @@ def_fields(Fs0, Name, St0) ->
foldl(fun ({record_field,Lf,{atom,La,F},V}, {Fs,St}) ->
case exist_field(F, Fs) of
true -> {Fs,add_error(Lf, {redefine_field,Name,F}, St)};
- false ->
+ false ->
St1 = St#lint{recdef_top = true},
{_,St2} = expr(V, [], St1),
%% Warnings and errors found are kept, but
@@ -2306,7 +2438,7 @@ init_fields(Ifs, Line, Name, Dfs, Vt0, St0) ->
Defs = init_fields(Ifs, Line, Dfs),
{_,St2} = check_fields(Defs, Name, Dfs, Vt1, St1, fun expr/3),
{Vt1,St1#lint{usage = St2#lint.usage}}.
-
+
ginit_fields(Ifs, Line, Name, Dfs, Vt0, St0) ->
{Vt1,St1} = check_fields(Ifs, Name, Dfs, Vt0, St0, fun gexpr/3),
Defs = init_fields(Ifs, Line, Dfs),
@@ -2316,7 +2448,7 @@ ginit_fields(Ifs, Line, Name, Dfs, Vt0, St0) ->
IllErrs = [E || {_File,{_Line,erl_lint,illegal_guard_expr}}=E <- Errors],
St4 = St1#lint{usage = Usage, errors = IllErrs ++ St1#lint.errors},
{Vt1,St4}.
-
+
%% Default initializations to be carried out
init_fields(Ifs, Line, Dfs) ->
[ {record_field,Lf,{atom,La,F},copy_expr(Di, Line)} ||
@@ -2394,7 +2526,7 @@ check_type({ann_type, _L, [_Var, Type]}, SeenVars, St) ->
check_type(Type, SeenVars, St);
check_type({paren_type, _L, [Type]}, SeenVars, St) ->
check_type(Type, SeenVars, St);
-check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
+check_type({remote_type, L, [{atom, _, Mod}, {atom, _, Name}, Args]},
SeenVars, #lint{module=CurrentMod} = St) ->
St1 =
case (dict:is_key({Name, length(Args)}, default_types())
@@ -2432,7 +2564,7 @@ check_type({type, L, 'fun', [Dom, Range]}, SeenVars, St) ->
check_type({type, -1, product, [Dom, Range]}, SeenVars, St1);
check_type({type, L, range, [From, To]}, SeenVars, St) ->
St1 =
- case {From, To} of
+ case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
{{integer, _, X}, {integer, _, Y}} when X < Y -> St;
_ -> add_error(L, {type_syntax, range}, St)
end,
@@ -2441,8 +2573,8 @@ check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St};
check_type({type, L, binary, [Base, Unit]}, SeenVars, St) ->
St1 =
- case {Base, Unit} of
- {{integer, _, BaseVal},
+ case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
+ {{integer, _, BaseVal},
{integer, _, UnitVal}} when BaseVal >= 0, UnitVal >= 0 -> St;
_ -> add_error(L, {type_syntax, binary}, St)
end,
@@ -2467,7 +2599,13 @@ check_type({type, La, TypeName, Args}, SeenVars, #lint{usage=Usage} = St) ->
UsedTypes = dict:store({TypeName, Arity}, La, OldUsed),
St#lint{usage=Usage#usage{used_types=UsedTypes}}
end,
- check_type({type, -1, product, Args}, SeenVars, St1).
+ check_type({type, -1, product, Args}, SeenVars, St1);
+check_type(I, SeenVars, St) ->
+ case erl_eval:partial_eval(I) of
+ {integer,_ILn,_Integer} -> {SeenVars, St};
+ _Other ->
+ {SeenVars, add_error(element(2, I), {type_syntax, integer}, St)}
+ end.
check_record_types(Line, Name, Fields, SeenVars, St) ->
case dict:find(Name, St#lint.records) of
@@ -2475,12 +2613,12 @@ check_record_types(Line, Name, Fields, SeenVars, St) ->
case lists:all(fun({type, _, field_type, _}) -> true;
(_) -> false
end, Fields) of
- true ->
+ true ->
check_record_types(Fields, Name, DefFields, SeenVars, St, []);
false ->
{SeenVars, add_error(Line, {type_syntax, record}, St)}
end;
- error ->
+ error ->
{SeenVars, add_error(Line, {undefined_record, Name}, St)}
end.
@@ -2563,7 +2701,6 @@ default_types() ->
{set, 0},
{string, 0},
{term, 0},
- {tid, 0},
{timeout, 0},
{var, 1}],
dict:from_list([{T, -1} || T <- DefTypes]).
@@ -2585,7 +2722,6 @@ is_newly_introduced_builtin_type({gb_tree, 0}) -> true; % opaque
is_newly_introduced_builtin_type({iodata, 0}) -> true;
is_newly_introduced_builtin_type({queue, 0}) -> true; % opaque
is_newly_introduced_builtin_type({set, 0}) -> true; % opaque
-is_newly_introduced_builtin_type({tid, 0}) -> true; % opaque
%% R13B01
is_newly_introduced_builtin_type({boolean, 0}) -> true;
is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
@@ -2606,7 +2742,7 @@ spec_decl(Line, MFA0, TypeSpecs, St0 = #lint{specs = Specs, module = Mod}) ->
check_specs([FunType|Left], Arity, St0) ->
{FunType1, CTypes} =
case FunType of
- {type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} ->
+ {type, _, bounded_fun, [FT = {type, _, 'fun', _}, Cs]} ->
Types0 = [T || {type, _, constraint, [_, T]} <- Cs],
{FT, lists:append(Types0)};
{type, _, 'fun', _} = FT -> {FT, []}
@@ -2666,10 +2802,12 @@ add_missing_spec_warnings(Forms, St0, Type) ->
add_warning(L, {missing_spec,FA}, St)
end, St0, Warns).
-check_unused_types(Forms, St = #lint{usage=Usage, types=Types}) ->
+check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) ->
case [File || {attribute,_L,file,{File,_Line}} <- Forms] of
[FirstFile|_] ->
- UsedTypes = Usage#usage.used_types,
+ D = Usage#usage.used_types,
+ L = gb_sets:to_list(ExpTs) ++ dict:fetch_keys(D),
+ UsedTypes = gb_sets:from_list(L),
FoldFun =
fun(_Type, -1, AccSt) ->
%% Default type
@@ -2677,19 +2815,18 @@ check_unused_types(Forms, St = #lint{usage=Usage, types=Types}) ->
(Type, FileLine, AccSt) ->
case loc(FileLine) of
{FirstFile, _} ->
- case dict:is_key(Type, UsedTypes) of
+ case gb_sets:is_member(Type, UsedTypes) of
true -> AccSt;
- false ->
- add_warning(FileLine,
- {unused_type, Type},
- AccSt)
+ false ->
+ Warn = {unused_type,Type},
+ add_warning(FileLine, Warn, AccSt)
end;
_ ->
- %% Don't warn about unused types in include file
+ %% No warns about unused types in include files
AccSt
end
end,
- dict:fold(FoldFun, St, Types);
+ dict:fold(FoldFun, St, Ts);
[] ->
St
end.
@@ -2834,7 +2971,7 @@ fun_clause({clause,_Line,H,G,B}, Vt0, St0) ->
%%
%% used variable has been used
%% unused variable has been bound but not used
-%%
+%%
%% Lines is a list of line numbers where the variable was bound.
%%
%% Report variable errors/warnings as soon as possible and then change
@@ -2864,9 +3001,9 @@ pat_var(V, Line, Vt, Bvt, St) ->
case orddict:find(V, Bvt) of
{ok, {bound,_Usage,Ls}} ->
{[],[{V,{bound,used,Ls}}],St};
- error ->
+ error ->
case orddict:find(V, Vt) of
- {ok,{bound,_Usage,Ls}} ->
+ {ok,{bound,_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],[],St};
{ok,{{unsafe,In},_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],[],
@@ -2919,7 +3056,7 @@ pat_binsize_var(V, Line, Vt, Bvt, St) ->
expr_var(V, Line, Vt, St0) ->
case orddict:find(V, Vt) of
- {ok,{bound,_Usage,Ls}} ->
+ {ok,{bound,_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],St0};
{ok,{{unsafe,In},_Usage,Ls}} ->
{[{V,{bound,used,Ls}}],
@@ -2957,7 +3094,7 @@ check_old_unused_vars(Vt, Vt0, St0) ->
warn_unused_vars(U, Vt, St0).
unused_vars(Vt, Vt0, _St0) ->
- U0 = orddict:filter(fun (V, {_State,unused,_Ls}) ->
+ U0 = orddict:filter(fun (V, {_State,unused,_Ls}) ->
case atom_to_list(V) of
"_"++_ -> false;
_ -> true
@@ -2973,7 +3110,7 @@ warn_unused_vars(U, Vt, St0) ->
false -> St0;
true ->
foldl(fun ({V,{_,unused,Ls}}, St) ->
- foldl(fun (L, St2) ->
+ foldl(fun (L, St2) ->
add_warning(L, {unused_var,V},
St2)
end, St, Ls)
@@ -3073,7 +3210,7 @@ vt_no_unsafe(Vt) -> [V || {_,{S,_U,_L}}=V <- Vt,
-ifdef(NOTUSED).
vunion(Vs1, Vs2) -> ordsets:union(vtnames(Vs1), vtnames(Vs2)).
-vunion(Vss) -> foldl(fun (Vs, Uvs) ->
+vunion(Vss) -> foldl(fun (Vs, Uvs) ->
ordsets:union(vtnames(Vs), Uvs)
end, [], Vss).
@@ -3103,7 +3240,7 @@ modify_line(T, F0) ->
%% Forms.
modify_line1({function,F,A}, _Mf) -> {function,F,A};
modify_line1({function,M,F,A}, _Mf) -> {function,M,F,A};
-modify_line1({attribute,L,record,{Name,Fields}}, Mf) ->
+modify_line1({attribute,L,record,{Name,Fields}}, Mf) ->
{attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}};
modify_line1({attribute,L,spec,{Fun,Types}}, Mf) ->
{attribute,Mf(L),spec,{Fun,modify_line1(Types, Mf)}};
@@ -3118,7 +3255,7 @@ modify_line1({warning,W}, _Mf) -> {warning,W};
modify_line1({error,W}, _Mf) -> {error,W};
%% Expressions.
modify_line1({clauses,Cs}, Mf) -> {clauses,modify_line1(Cs, Mf)};
-modify_line1({typed_record_field,Field,Type}, Mf) ->
+modify_line1({typed_record_field,Field,Type}, Mf) ->
{typed_record_field,modify_line1(Field, Mf),modify_line1(Type, Mf)};
modify_line1({Tag,L}, Mf) -> {Tag,Mf(L)};
modify_line1({Tag,L,E1}, Mf) ->
@@ -3154,7 +3291,7 @@ check_record_info_call(Line,_La,_As,St) ->
has_wildcard_field([{record_field,_Lf,{var,_La,'_'},_Val}|_Fs]) -> true;
has_wildcard_field([_|Fs]) -> has_wildcard_field(Fs);
has_wildcard_field([]) -> false.
-
+
%% check_remote_function(Line, ModuleName, FuncName, [Arg], State) -> State.
%% Perform checks on known remote calls.
@@ -3170,7 +3307,7 @@ check_remote_function(Line, M, F, As, St0) ->
check_qlc_hrl(Line, M, F, As, St) ->
Arity = length(As),
case As of
- [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q,
+ [{lc,_L,_E,_Qs}|_] when M =:= qlc, F =:= q,
Arity < 3, not St#lint.xqlc ->
add_warning(Line, {missing_qlc_hrl, Arity}, St);
_ ->
@@ -3355,11 +3492,11 @@ extract_sequence(3, [$.,_|Fmt], Need) ->
extract_sequence(4, Fmt, Need);
extract_sequence(3, Fmt, Need) ->
extract_sequence(4, Fmt, Need);
-extract_sequence(4, [$t, $c | Fmt], Need) ->
- extract_sequence(5, [$c|Fmt], Need);
-extract_sequence(4, [$t, $s | Fmt], Need) ->
- extract_sequence(5, [$s|Fmt], Need);
-extract_sequence(4, [$t, C | _Fmt], _Need) ->
+extract_sequence(4, [$t, $c | Fmt], Need) ->
+ extract_sequence(5, [$c|Fmt], Need);
+extract_sequence(4, [$t, $s | Fmt], Need) ->
+ extract_sequence(5, [$s|Fmt], Need);
+extract_sequence(4, [$t, C | _Fmt], _Need) ->
{error,"invalid control ~t" ++ [C]};
extract_sequence(4, Fmt, Need) ->
extract_sequence(5, Fmt, Need);
@@ -3437,3 +3574,56 @@ expand_package(M, St0) ->
{error, St1}
end
end.
+
+
+%% Prebuild set of local functions (to override auto-import)
+local_functions(Forms) ->
+ gb_sets:from_list([ {Func,Arity} || {function,_,Func,Arity,_} <- Forms ]).
+%% Predicate to find out if the function is locally defined
+is_local_function(LocalSet,{Func,Arity}) ->
+ gb_sets:is_element({Func,Arity},LocalSet).
+%% Predicate to see if a function is explicitly imported
+is_imported_function(ImportSet,{Func,Arity}) ->
+ case orddict:find({Func,Arity}, ImportSet) of
+ {ok,_Mod} -> true;
+ error -> false
+ end.
+%% Predicate to see if a function is explicitly imported from the erlang module
+is_imported_from_erlang(ImportSet,{Func,Arity}) ->
+ case orddict:find({Func,Arity}, ImportSet) of
+ {ok,erlang} -> true;
+ _ -> false
+ end.
+%% Build set of functions where auto-import is explicitly supressed
+auto_import_suppressed(CompileFlags) ->
+ L0 = [ X || {no_auto_import,X} <- CompileFlags ],
+ L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ],
+ gb_sets:from_list(L1).
+%% Predicate to find out if autoimport is explicitly supressed for a function
+is_autoimport_suppressed(NoAutoSet,{Func,Arity}) ->
+ gb_sets:is_element({Func,Arity},NoAutoSet).
+%% Predicate to find out if a function specific bif-clash supression (old deprecated) is present
+bif_clash_specifically_disabled(St,{F,A}) ->
+ Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile),
+ lists:member({F,A},Nowarn).
+
+%% Predicate to find out if an autoimported guard_bif is not overriden in some way
+%% Guard Bif without module name is disallowed if
+%% * It is overridden by local function
+%% * It is overridden by -import and that import is not of itself (i.e. from module erlang)
+%% * The autoimport is suppressed or it's not reimported by -import directive
+%% Otherwise it's OK (given that it's actually a guard bif and actually is autoimported)
+no_guard_bif_clash(St,{F,A}) ->
+ (
+ (not is_local_function(St#lint.locals,{F,A}))
+ andalso
+ (
+ (not is_imported_function(St#lint.imports,{F,A})) orelse
+ is_imported_from_erlang(St#lint.imports,{F,A})
+ )
+ andalso
+ (
+ (not is_autoimport_suppressed(St#lint.no_auto, {F,A})) orelse
+ is_imported_from_erlang(St#lint.imports,{F,A})
+ )
+ ).
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index 5287f55e59..15b45d72f4 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -47,7 +47,7 @@ opt_bit_size_expr bit_size_expr opt_bit_type_list bit_type_list bit_type
top_type top_type_100 top_types type typed_expr typed_attr_val
type_sig type_sigs type_guard type_guards fun_type fun_type_100 binary_type
type_spec spec_fun typed_exprs typed_record_fields field_types field_type
-bin_base_type bin_unit_type int_type.
+bin_base_type bin_unit_type type_200 type_300 type_400 type_500.
Terminals
char integer float atom string var
@@ -61,7 +61,7 @@ char integer float atom string var
'++' '--'
'==' '/=' '=<' '<' '>=' '>' '=:=' '=/=' '<='
'<<' '>>'
-'!' '=' '::'
+'!' '=' '::' '..' '...'
'spec' % helper
dot.
@@ -120,8 +120,24 @@ top_types -> top_type ',' top_types : ['$1'|'$3'].
top_type -> var '::' top_type_100 : {ann_type, ?line('$1'), ['$1','$3']}.
top_type -> top_type_100 : '$1'.
-top_type_100 -> type : '$1'.
-top_type_100 -> type '|' top_type_100 : lift_unions('$1','$3').
+top_type_100 -> type_200 : '$1'.
+top_type_100 -> type_200 '|' top_type_100 : lift_unions('$1','$3').
+
+type_200 -> type_300 '..' type_300 : {type, ?line('$1'), range,
+ [skip_paren('$1'),
+ skip_paren('$3')]}.
+type_200 -> type_300 : '$1'.
+
+type_300 -> type_300 add_op type_400 : ?mkop2(skip_paren('$1'),
+ '$2', skip_paren('$3')).
+type_300 -> type_400 : '$1'.
+
+type_400 -> type_400 mult_op type_500 : ?mkop2(skip_paren('$1'),
+ '$2', skip_paren('$3')).
+type_400 -> type_500 : '$1'.
+
+type_500 -> prefix_op type : ?mkop1('$1', skip_paren('$2')).
+type_500 -> type : '$1'.
type -> '(' top_type ')' : {paren_type, ?line('$2'), ['$2']}.
type -> var : '$1'.
@@ -135,7 +151,7 @@ type -> atom ':' atom '(' top_types ')' : {remote_type, ?line('$1'),
['$1', '$3', '$5']}.
type -> '[' ']' : {type, ?line('$1'), nil, []}.
type -> '[' top_type ']' : {type, ?line('$1'), list, ['$2']}.
-type -> '[' top_type ',' '.' '.' '.' ']' : {type, ?line('$1'),
+type -> '[' top_type ',' '...' ']' : {type, ?line('$1'),
nonempty_list, ['$2']}.
type -> '{' '}' : {type, ?line('$1'), tuple, []}.
type -> '{' top_types '}' : {type, ?line('$1'), tuple, '$2'}.
@@ -143,19 +159,13 @@ type -> '#' atom '{' '}' : {type, ?line('$1'), record, ['$2']}.
type -> '#' atom '{' field_types '}' : {type, ?line('$1'),
record, ['$2'|'$4']}.
type -> binary_type : '$1'.
-type -> int_type : '$1'.
-type -> int_type '.' '.' int_type : {type, ?line('$1'), range,
- ['$1', '$4']}.
+type -> integer : '$1'.
type -> 'fun' '(' ')' : {type, ?line('$1'), 'fun', []}.
type -> 'fun' '(' fun_type_100 ')' : '$3'.
-int_type -> integer : '$1'.
-int_type -> '-' integer : abstract(-normalise('$2'),
- ?line('$2')).
-
-fun_type_100 -> '(' '.' '.' '.' ')' '->' top_type
+fun_type_100 -> '(' '...' ')' '->' top_type
: {type, ?line('$1'), 'fun',
- [{type, ?line('$1'), any}, '$7']}.
+ [{type, ?line('$1'), any}, '$5']}.
fun_type_100 -> fun_type : '$1'.
fun_type -> '(' ')' '->' top_type : {type, ?line('$1'), 'fun',
@@ -180,9 +190,9 @@ binary_type -> '<<' bin_unit_type '>>' : {type, ?line('$1'),binary,
binary_type -> '<<' bin_base_type ',' bin_unit_type '>>'
: {type, ?line('$1'), binary, ['$2', '$4']}.
-bin_base_type -> var ':' integer : build_bin_type(['$1'], '$3').
+bin_base_type -> var ':' type : build_bin_type(['$1'], '$3').
-bin_unit_type -> var ':' var '*' integer : build_bin_type(['$1', '$3'], '$5').
+bin_unit_type -> var ':' var '*' type : build_bin_type(['$1', '$3'], '$5').
attr_val -> expr : ['$1'].
attr_val -> expr ',' exprs : ['$1' | '$3'].
@@ -607,6 +617,11 @@ lift_unions(T1, {type, _La, union, List}) ->
lift_unions(T1, T2) ->
{type, ?line(T1), union, [T1, T2]}.
+skip_paren({paren_type,_L,[Type]}) ->
+ skip_paren(Type);
+skip_paren(Type) ->
+ Type.
+
build_gen_type({atom, La, tuple}) ->
{type, La, tuple, any};
build_gen_type({atom, La, Name}) ->
@@ -615,7 +630,7 @@ build_gen_type({atom, La, Name}) ->
build_bin_type([{var, _, '_'}|Left], Int) ->
build_bin_type(Left, Int);
build_bin_type([], Int) ->
- Int;
+ skip_paren(Int);
build_bin_type([{var, La, _}|_], _) ->
ret_err(La, "Bad binary type").
@@ -742,7 +757,8 @@ record_fields([{typed,Expr,TypeInfo}|Fields]) ->
{atom, La, _} ->
case has_undefined(TypeInfo) of
false ->
- lift_unions(abstract(undefined, La), TypeInfo);
+ TypeInfo2 = maybe_add_paren(TypeInfo),
+ lift_unions(abstract(undefined, La), TypeInfo2);
true ->
TypeInfo
end
@@ -763,6 +779,11 @@ has_undefined({type,_,union,Ts}) ->
has_undefined(_) ->
false.
+maybe_add_paren({ann_type,L,T}) ->
+ {paren_type,L,[{ann_type,L,T}]};
+maybe_add_paren(T) ->
+ T.
+
term(Expr) ->
try normalise(Expr)
catch _:_R -> ret_err(?line(Expr), "bad attribute")
diff --git a/lib/stdlib/src/erl_posix_msg.erl b/lib/stdlib/src/erl_posix_msg.erl
index fe981b23a7..909cc1d102 100644
--- a/lib/stdlib/src/erl_posix_msg.erl
+++ b/lib/stdlib/src/erl_posix_msg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,143 +24,146 @@
-spec message(atom()) -> string().
-message(e2big) -> "argument list too long";
-message(eacces) -> "permission denied";
-message(eaddrinuse) -> "address already in use";
-message(eaddrnotavail) -> "can't assign requested address";
-message(eadv) -> "advertise error";
-message(eafnosupport) -> "address family not supported by protocol family";
-message(eagain) -> "resource temporarily unavailable";
-message(ealign) -> "EALIGN";
-message(ealready) -> "operation already in progress";
-message(ebade) -> "bad exchange descriptor";
-message(ebadf) -> "bad file number";
-message(ebadfd) -> "file descriptor in bad state";
-message(ebadmsg) -> "not a data message";
-message(ebadr) -> "bad request descriptor";
-message(ebadrpc) -> "RPC structure is bad";
-message(ebadrqc) -> "bad request code";
-message(ebadslt) -> "invalid slot";
-message(ebfont) -> "bad font file format";
-message(ebusy) -> "file busy";
-message(echild) -> "no children";
-message(echrng) -> "channel number out of range";
-message(ecomm) -> "communication error on send";
-message(econnaborted) -> "software caused connection abort";
-message(econnrefused) -> "connection refused";
-message(econnreset) -> "connection reset by peer";
-message(edeadlk) -> "resource deadlock avoided";
-message(edeadlock) -> "resource deadlock avoided";
-message(edestaddrreq) -> "destination address required";
-message(edirty) -> "mounting a dirty fs w/o force";
-message(edom) -> "math argument out of range";
-message(edotdot) -> "cross mount point";
-message(edquot) -> "disk quota exceeded";
-message(eduppkg) -> "duplicate package name";
-message(eexist) -> "file already exists";
-message(efault) -> "bad address in system call argument";
-message(efbig) -> "file too large";
-message(ehostdown) -> "host is down";
-message(ehostunreach) -> "host is unreachable";
-message(eidrm) -> "identifier removed";
-message(einit) -> "initialization error";
-message(einprogress) -> "operation now in progress";
-message(eintr) -> "interrupted system call";
-message(einval) -> "invalid argument";
-message(eio) -> "I/O error";
-message(eisconn) -> "socket is already connected";
-message(eisdir) -> "illegal operation on a directory";
-message(eisnam) -> "is a name file";
-message(elbin) -> "ELBIN";
-message(el2hlt) -> "level 2 halted";
-message(el2nsync) -> "level 2 not synchronized";
-message(el3hlt) -> "level 3 halted";
-message(el3rst) -> "level 3 reset";
-message(elibacc) -> "can not access a needed shared library";
-message(elibbad) -> "accessing a corrupted shared library";
-message(elibexec) -> "can not exec a shared library directly";
-message(elibmax) ->
- "attempting to link in more shared libraries than system limit";
-message(elibscn) -> ".lib section in a.out corrupted";
-message(elnrng) -> "link number out of range";
-message(eloop) -> "too many levels of symbolic links";
-message(emfile) -> "too many open files";
-message(emlink) -> "too many links";
-message(emsgsize) -> "message too long";
-message(emultihop) -> "multihop attempted";
-message(enametoolong) -> "file name too long";
-message(enavail) -> "not available";
-message(enet) -> "ENET";
-message(enetdown) -> "network is down";
-message(enetreset) -> "network dropped connection on reset";
-message(enetunreach) -> "network is unreachable";
-message(enfile) -> "file table overflow";
-message(enoano) -> "anode table overflow";
-message(enobufs) -> "no buffer space available";
-message(enocsi) -> "no CSI structure available";
-message(enodata) -> "no data available";
-message(enodev) -> "no such device";
-message(enoent) -> "no such file or directory";
-message(enoexec) -> "exec format error";
-message(enolck) -> "no locks available";
-message(enolink) -> "link has be severed";
-message(enomem) -> "not enough memory";
-message(enomsg) -> "no message of desired type";
-message(enonet) -> "machine is not on the network";
-message(enopkg) -> "package not installed";
-message(enoprotoopt) -> "bad proocol option";
-message(enospc) -> "no space left on device";
-message(enosr) -> "out of stream resources or not a stream device";
-message(enosym) -> "unresolved symbol name";
-message(enosys) -> "function not implemented";
-message(enotblk) -> "block device required";
-message(enotconn) -> "socket is not connected";
-message(enotdir) -> "not a directory";
-message(enotempty) -> "directory not empty";
-message(enotnam) -> "not a name file";
-message(enotsock) -> "socket operation on non-socket";
-message(enotsup) -> "operation not supported";
-message(enotty) -> "inappropriate device for ioctl";
-message(enotuniq) -> "name not unique on network";
-message(enxio) -> "no such device or address";
-message(eopnotsupp) -> "operation not supported on socket";
-message(eperm) -> "not owner";
-message(epfnosupport) -> "protocol family not supported";
-message(epipe) -> "broken pipe";
-message(eproclim) -> "too many processes";
-message(eprocunavail) -> "bad procedure for program";
-message(eprogmismatch) -> "program version wrong";
-message(eprogunavail) -> "RPC program not available";
-message(eproto) -> "protocol error";
-message(eprotonosupport) -> "protocol not suppored";
-message(eprototype) -> "protocol wrong type for socket";
-message(erange) -> "math result unrepresentable";
-message(erefused) -> "EREFUSED";
-message(eremchg) -> "remote address changed";
-message(eremdev) -> "remote device";
-message(eremote) -> "pathname hit remote file system";
-message(eremoteio) -> "remote i/o error";
-message(eremoterelease) -> "EREMOTERELEASE";
-message(erofs) -> "read-only file system";
-message(erpcmismatch) -> "RPC version is wrong";
-message(erremote) -> "object is remote";
-message(eshutdown) -> "can't send after socket shutdown";
-message(esocktnosupport) -> "socket type not supported";
-message(espipe) -> "invalid seek";
-message(esrch) -> "no such process";
-message(esrmnt) -> "srmount error";
-message(estale) -> "stale remote file handle";
-message(esuccess) -> "Error 0";
-message(etime) -> "timer expired";
-message(etimedout) -> "connection timed out";
-message(etoomanyrefs) -> "too many references: can't splice";
-message(etxtbsy) -> "text file or pseudo-device busy";
-message(euclean) -> "structure needs cleaning";
-message(eunatch) -> "protocol driver not attached";
-message(eusers) -> "too many users";
-message(eversion) -> "version mismatch";
-message(ewouldblock) -> "operation would block";
-message(exdev) -> "cross-domain link";
-message(exfull) -> "message tables full";
-message(nxdomain) -> "non-existing domain";
-message(_) -> "unknown POSIX error".
+message(T) ->
+ binary_to_list(message_1(T)).
+
+message_1(e2big) -> <<"argument list too long">>;
+message_1(eacces) -> <<"permission denied">>;
+message_1(eaddrinuse) -> <<"address already in use">>;
+message_1(eaddrnotavail) -> <<"can't assign requested address">>;
+message_1(eadv) -> <<"advertise error">>;
+message_1(eafnosupport) -> <<"address family not supported by protocol family">>;
+message_1(eagain) -> <<"resource temporarily unavailable">>;
+message_1(ealign) -> <<"EALIGN">>;
+message_1(ealready) -> <<"operation already in progress">>;
+message_1(ebade) -> <<"bad exchange descriptor">>;
+message_1(ebadf) -> <<"bad file number">>;
+message_1(ebadfd) -> <<"file descriptor in bad state">>;
+message_1(ebadmsg) -> <<"not a data message">>;
+message_1(ebadr) -> <<"bad request descriptor">>;
+message_1(ebadrpc) -> <<"RPC structure is bad">>;
+message_1(ebadrqc) -> <<"bad request code">>;
+message_1(ebadslt) -> <<"invalid slot">>;
+message_1(ebfont) -> <<"bad font file format">>;
+message_1(ebusy) -> <<"file busy">>;
+message_1(echild) -> <<"no children">>;
+message_1(echrng) -> <<"channel number out of range">>;
+message_1(ecomm) -> <<"communication error on send">>;
+message_1(econnaborted) -> <<"software caused connection abort">>;
+message_1(econnrefused) -> <<"connection refused">>;
+message_1(econnreset) -> <<"connection reset by peer">>;
+message_1(edeadlk) -> <<"resource deadlock avoided">>;
+message_1(edeadlock) -> <<"resource deadlock avoided">>;
+message_1(edestaddrreq) -> <<"destination address required">>;
+message_1(edirty) -> <<"mounting a dirty fs w/o force">>;
+message_1(edom) -> <<"math argument out of range">>;
+message_1(edotdot) -> <<"cross mount point">>;
+message_1(edquot) -> <<"disk quota exceeded">>;
+message_1(eduppkg) -> <<"duplicate package name">>;
+message_1(eexist) -> <<"file already exists">>;
+message_1(efault) -> <<"bad address in system call argument">>;
+message_1(efbig) -> <<"file too large">>;
+message_1(ehostdown) -> <<"host is down">>;
+message_1(ehostunreach) -> <<"host is unreachable">>;
+message_1(eidrm) -> <<"identifier removed">>;
+message_1(einit) -> <<"initialization error">>;
+message_1(einprogress) -> <<"operation now in progress">>;
+message_1(eintr) -> <<"interrupted system call">>;
+message_1(einval) -> <<"invalid argument">>;
+message_1(eio) -> <<"I/O error">>;
+message_1(eisconn) -> <<"socket is already connected">>;
+message_1(eisdir) -> <<"illegal operation on a directory">>;
+message_1(eisnam) -> <<"is a name file">>;
+message_1(elbin) -> <<"ELBIN">>;
+message_1(el2hlt) -> <<"level 2 halted">>;
+message_1(el2nsync) -> <<"level 2 not synchronized">>;
+message_1(el3hlt) -> <<"level 3 halted">>;
+message_1(el3rst) -> <<"level 3 reset">>;
+message_1(elibacc) -> <<"can not access a needed shared library">>;
+message_1(elibbad) -> <<"accessing a corrupted shared library">>;
+message_1(elibexec) -> <<"can not exec a shared library directly">>;
+message_1(elibmax) ->
+ <<"attempting to link in more shared libraries than system limit">>;
+message_1(elibscn) -> <<".lib section in a.out corrupted">>;
+message_1(elnrng) -> <<"link number out of range">>;
+message_1(eloop) -> <<"too many levels of symbolic links">>;
+message_1(emfile) -> <<"too many open files">>;
+message_1(emlink) -> <<"too many links">>;
+message_1(emsgsize) -> <<"message too long">>;
+message_1(emultihop) -> <<"multihop attempted">>;
+message_1(enametoolong) -> <<"file name too long">>;
+message_1(enavail) -> <<"not available">>;
+message_1(enet) -> <<"ENET">>;
+message_1(enetdown) -> <<"network is down">>;
+message_1(enetreset) -> <<"network dropped connection on reset">>;
+message_1(enetunreach) -> <<"network is unreachable">>;
+message_1(enfile) -> <<"file table overflow">>;
+message_1(enoano) -> <<"anode table overflow">>;
+message_1(enobufs) -> <<"no buffer space available">>;
+message_1(enocsi) -> <<"no CSI structure available">>;
+message_1(enodata) -> <<"no data available">>;
+message_1(enodev) -> <<"no such device">>;
+message_1(enoent) -> <<"no such file or directory">>;
+message_1(enoexec) -> <<"exec format error">>;
+message_1(enolck) -> <<"no locks available">>;
+message_1(enolink) -> <<"link has be severed">>;
+message_1(enomem) -> <<"not enough memory">>;
+message_1(enomsg) -> <<"no message of desired type">>;
+message_1(enonet) -> <<"machine is not on the network">>;
+message_1(enopkg) -> <<"package not installed">>;
+message_1(enoprotoopt) -> <<"bad proocol option">>;
+message_1(enospc) -> <<"no space left on device">>;
+message_1(enosr) -> <<"out of stream resources or not a stream device">>;
+message_1(enosym) -> <<"unresolved symbol name">>;
+message_1(enosys) -> <<"function not implemented">>;
+message_1(enotblk) -> <<"block device required">>;
+message_1(enotconn) -> <<"socket is not connected">>;
+message_1(enotdir) -> <<"not a directory">>;
+message_1(enotempty) -> <<"directory not empty">>;
+message_1(enotnam) -> <<"not a name file">>;
+message_1(enotsock) -> <<"socket operation on non-socket">>;
+message_1(enotsup) -> <<"operation not supported">>;
+message_1(enotty) -> <<"inappropriate device for ioctl">>;
+message_1(enotuniq) -> <<"name not unique on network">>;
+message_1(enxio) -> <<"no such device or address">>;
+message_1(eopnotsupp) -> <<"operation not supported on socket">>;
+message_1(eperm) -> <<"not owner">>;
+message_1(epfnosupport) -> <<"protocol family not supported">>;
+message_1(epipe) -> <<"broken pipe">>;
+message_1(eproclim) -> <<"too many processes">>;
+message_1(eprocunavail) -> <<"bad procedure for program">>;
+message_1(eprogmismatch) -> <<"program version wrong">>;
+message_1(eprogunavail) -> <<"RPC program not available">>;
+message_1(eproto) -> <<"protocol error">>;
+message_1(eprotonosupport) -> <<"protocol not suppored">>;
+message_1(eprototype) -> <<"protocol wrong type for socket">>;
+message_1(erange) -> <<"math result unrepresentable">>;
+message_1(erefused) -> <<"EREFUSED">>;
+message_1(eremchg) -> <<"remote address changed">>;
+message_1(eremdev) -> <<"remote device">>;
+message_1(eremote) -> <<"pathname hit remote file system">>;
+message_1(eremoteio) -> <<"remote i/o error">>;
+message_1(eremoterelease) -> <<"EREMOTERELEASE">>;
+message_1(erofs) -> <<"read-only file system">>;
+message_1(erpcmismatch) -> <<"RPC version is wrong">>;
+message_1(erremote) -> <<"object is remote">>;
+message_1(eshutdown) -> <<"can't send after socket shutdown">>;
+message_1(esocktnosupport) -> <<"socket type not supported">>;
+message_1(espipe) -> <<"invalid seek">>;
+message_1(esrch) -> <<"no such process">>;
+message_1(esrmnt) -> <<"srmount error">>;
+message_1(estale) -> <<"stale remote file handle">>;
+message_1(esuccess) -> <<"Error 0">>;
+message_1(etime) -> <<"timer expired">>;
+message_1(etimedout) -> <<"connection timed out">>;
+message_1(etoomanyrefs) -> <<"too many references: can't splice">>;
+message_1(etxtbsy) -> <<"text file or pseudo-device busy">>;
+message_1(euclean) -> <<"structure needs cleaning">>;
+message_1(eunatch) -> <<"protocol driver not attached">>;
+message_1(eusers) -> <<"too many users">>;
+message_1(eversion) -> <<"version mismatch">>;
+message_1(ewouldblock) -> <<"operation would block">>;
+message_1(exdev) -> <<"cross-domain link">>;
+message_1(exfull) -> <<"message tables full">>;
+message_1(nxdomain) -> <<"non-existing domain">>;
+message_1(_) -> <<"unknown POSIX error">>.
diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl
index 0859bf0466..66c80a45cb 100644
--- a/lib/stdlib/src/erl_pp.erl
+++ b/lib/stdlib/src/erl_pp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -115,7 +115,7 @@ lattribute({attribute,_Line,Name,Arg}, Hook) ->
lattribute(module, {M,Vs}, _Hook) ->
attr("module",[{var,0,pname(M)},
- foldr(fun(V, C) -> {cons,0,{var,0,V},C}
+ foldr(fun(V, C) -> {cons,0,{var,0,V},C}
end, {nil,0}, Vs)]);
lattribute(module, M, _Hook) ->
attr("module", [{var,0,pname(M)}]);
@@ -140,7 +140,7 @@ typeattr(Tag, {TypeName,Type,Args}, _Hook) ->
ltype({ann_type,_Line,[V,T]}) ->
typed(lexpr(V, none), T);
ltype({paren_type,_Line,[T]}) ->
- [$(,ltype(T),$)];
+ [$(,ltype(T),$)];
ltype({type,_Line,union,Ts}) ->
{seq,[],[],[' |'],ltypes(Ts)};
ltype({type,_Line,list,[T]}) ->
@@ -153,7 +153,7 @@ ltype({type,Line,tuple,any}) ->
simple_type({atom,Line,tuple}, []);
ltype({type,_Line,tuple,Ts}) ->
tuple_type(Ts, fun ltype/1);
-ltype({type,_Line,record,[N|Fs]}) ->
+ltype({type,_Line,record,[{atom,_,N}|Fs]}) ->
record_type(N, Fs);
ltype({type,_Line,range,[_I1,_I2]=Es}) ->
expr_list(Es, '..', fun lexpr/2, none);
@@ -174,12 +174,15 @@ ltype({atom,_,T}) ->
ltype(E) ->
lexpr(E, 0, none).
-binary_type({integer,_,Int1}=I1, {integer,_,Int2}=I2) ->
- E1 = [[leaf("_:"),lexpr(I1, 0, none)] || Int1 =/= 0],
- E2 = [[leaf("_:_*"),lexpr(I2, 0, none)] || Int2 =/= 0],
+binary_type(I1, I2) ->
+ B = [[] || {integer,_,0} <- [I1]] =:= [],
+ U = [[] || {integer,_,0} <- [I2]] =:= [],
+ P = max_prec(),
+ E1 = [[leaf("_:"),lexpr(I1, P, none)] || B],
+ E2 = [[leaf("_:_*"),lexpr(I2, P, none)] || U],
{seq,'<<','>>',[$,],E1++E2}.
-record_type({atom,_,Name}, Fields) ->
+record_type(Name, Fields) ->
{first,[record_name(Name)],field_types(Fields)}.
field_types(Fs) ->
@@ -443,7 +446,7 @@ lexpr({op,_,Op,Arg}, Prec, Hook) ->
Ol = leaf(format("~s ", [Op])),
El = [Ol,lexpr(Arg, R, Hook)],
maybe_paren(P, Prec, El);
-lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse';
+lexpr({op,_,Op,Larg,Rarg}, Prec, Hook) when Op =:= 'orelse';
Op =:= 'andalso' ->
%% Breaks lines since R12B.
{L,P,R} = inop_prec(Op),
@@ -555,17 +558,11 @@ record_field({typed_record_field,{record_field,_,F,Val},Type}, Hook) ->
Fl = lexpr(F, L, Hook),
Vl = typed(lexpr(Val, R, Hook), Type),
{list,[{cstep,[Fl,' ='],Vl}]};
-record_field({typed_record_field,Field,Type0}, Hook) ->
- Type = remove_undefined(Type0),
+record_field({typed_record_field,Field,Type}, Hook) ->
typed(record_field(Field, Hook), Type);
record_field({record_field,_,F}, Hook) ->
lexpr(F, 0, Hook).
-remove_undefined({type,L,union,[{atom,_,undefined}|T]}) ->
- {type,L,union,T};
-remove_undefined(T) -> % cannot happen
- T.
-
list({cons,_,H,T}, Es, Hook) ->
list(T, [H|Es], Hook);
list({nil,_}, Es, Hook) ->
@@ -727,15 +724,15 @@ frmt(Item, I) ->
%%% and indentation are inserted between IPs.
%%% - {first,I,IP2}: IP2 follows after I, and is output with an indentation
%%% updated with the width of I.
-%%% - {seq,Before,After,Separator,IPs}: a sequence of Is separated by
-%%% Separator. Before is output before IPs, and the indentation of IPs
+%%% - {seq,Before,After,Separator,IPs}: a sequence of Is separated by
+%%% Separator. Before is output before IPs, and the indentation of IPs
%%% is updated with the width of Before. After follows after IPs.
%%% - {force_nl,ExtraInfo,I}: fun-info (a comment) forces linebreak before I.
%%% - {prefer_nl,Sep,IPs}: forces linebreak between Is unlesss negative
%%% indentation.
%%% - {string,S}: a string.
%%% - {hook,...}, {ehook,...}: hook expressions.
-%%%
+%%%
%%% list, first, seq, force_nl, and prefer_nl all accept IPs, where each
%%% element is either an item or a tuple {step|cstep,I1,I2}. step means
%%% that I2 is output after linebreak and an incremented indentation.
@@ -761,7 +758,7 @@ f({seq,Before,After,Sep,LItems}, I0, ST, WT) ->
{CharsL,SizeL} = unz(CharsSizeL),
{BCharsL,BSizeL} = unz1([BCharsSize]),
Sizes = BSizeL ++ SizeL,
- NSepChars = if
+ NSepChars = if
is_list(Sep), Sep =/= [] ->
erlang:max(0, length(CharsL)-1);
true ->
@@ -876,7 +873,7 @@ nl_indent(I, T) when I > 0 ->
[$\n|spaces(I, T)].
same_line(I0, SizeL, NSepChars) ->
- try
+ try
Size = lists:sum(SizeL) + NSepChars,
true = incr(I0, Size) =< ?MAXLINE,
{yes,Size}
@@ -956,9 +953,9 @@ write_a_string(S, N, Len) ->
-define(N_SPACES, 30).
spacetab() ->
- {[_|L],_} = mapfoldl(fun(_, A) -> {A,[$\s|A]}
+ {[_|L],_} = mapfoldl(fun(_, A) -> {A,[$\s|A]}
end, [], lists:seq(0, ?N_SPACES)),
- list_to_tuple(L).
+ list_to_tuple(L).
spaces(N, T) when N =< ?N_SPACES ->
element(N, T);
@@ -966,7 +963,7 @@ spaces(N, T) ->
[element(?N_SPACES, T)|spaces(N-?N_SPACES, T)].
wordtable() ->
- L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end ||
+ L = [begin {leaf,Sz,S} = leaf(W), {S,Sz} end ||
W <- [" ->"," =","<<",">>","[]","after","begin","case","catch",
"end","fun","if","of","receive","try","when"," ::","..",
" |"]],
diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl
index 1013d54bdc..18f64c46d0 100644
--- a/lib/stdlib/src/erl_scan.erl
+++ b/lib/stdlib/src/erl_scan.erl
@@ -55,18 +55,13 @@
token_info/1,token_info/2,
attributes_info/1,attributes_info/2,set_attribute/3]).
-%%% Local record.
--record(erl_scan,
- {resword_fun=fun reserved_word/1,
- ws=false,
- comment=false,
- text=false}).
+-export_type([error_info/0, line/0, tokens_result/0]).
%%%
-%%% Exported functions
+%%% Defines and type definitions
%%%
--define(COLUMN(C), is_integer(C), C >= 1).
+-define(COLUMN(C), (is_integer(C) andalso C >= 1)).
%% Line numbers less than zero have always been allowed:
-define(ALINE(L), is_integer(L)).
-define(STRING(S), is_list(S)).
@@ -95,6 +90,15 @@
-type error_description() :: term().
-type error_info() :: {location(), module(), error_description()}.
+%%% Local record.
+-record(erl_scan,
+ {resword_fun = fun reserved_word/1 :: resword_fun(),
+ ws = false :: boolean(),
+ comment = false :: boolean(),
+ text = false :: boolean()}).
+
+%%----------------------------------------------------------------------------
+
-spec format_error(Error :: term()) -> string().
format_error({string,Quote,Head}) ->
lists:flatten(["unterminated " ++ string_thing(Quote) ++
@@ -307,10 +311,10 @@ options(Opt) ->
options([Opt]).
opts(Options, [Key|Keys], L) ->
- V = case lists:keysearch(Key, 1, Options) of
- {value,{reserved_word_fun,F}} when ?RESWORDFUN(F) ->
+ V = case lists:keyfind(Key, 1, Options) of
+ {reserved_word_fun,F} when ?RESWORDFUN(F) ->
{ok,F};
- {value,{Key,_}} ->
+ {Key,_} ->
badarg;
false ->
{ok,default_option(Key)}
@@ -333,12 +337,13 @@ expand_opt(O, Os) ->
[O|Os].
attr_info(Attrs, Item) ->
- case catch lists:keysearch(Item, 1, Attrs) of
- {value,{Item,Value}} ->
- {Item,Value};
+ try lists:keyfind(Item, 1, Attrs) of
+ {_Item, _Value} = T ->
+ T;
false ->
- undefined;
- _ ->
+ undefined
+ catch
+ _:_ ->
erlang:error(badarg, [Attrs, Item])
end.
@@ -442,6 +447,14 @@ scan1([$\%=C|Cs], St, Line, Col, Toks) ->
scan_comment(Cs, St, Line, Col, Toks, [C]);
scan1([C|Cs], St, Line, Col, Toks) when ?DIGIT(C) ->
scan_number(Cs, St, Line, Col, Toks, [C]);
+scan1("..."++Cs, St, Line, Col, Toks) ->
+ tok2(Cs, St, Line, Col, Toks, "...", '...', 3);
+scan1(".."=Cs, _St, Line, Col, Toks) ->
+ {more,{Cs,Col,Toks,Line,[],fun scan/6}};
+scan1(".."++Cs, St, Line, Col, Toks) ->
+ tok2(Cs, St, Line, Col, Toks, "..", '..', 2);
+scan1("."=Cs, _St, Line, Col, Toks) ->
+ {more,{Cs,Col,Toks,Line,[],fun scan/6}};
scan1([$.=C|Cs], St, Line, Col, Toks) ->
scan_dot(Cs, St, Line, Col, Toks, [C]);
scan1([$"|Cs], St, Line, Col, Toks) -> %" Emacs
@@ -644,8 +657,6 @@ scan_dot([$\n=C|Cs], St, Line, Col, Toks, Ncs) ->
scan_dot([C|Cs], St, Line, Col, Toks, Ncs) when ?WHITE_SPACE(C) ->
Attrs = attributes(Line, Col, St, Ncs++[C]),
{ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 2)};
-scan_dot([]=Cs, _St, Line, Col, Toks, Ncs) ->
- {more,{Cs,Col,Toks,Line,Ncs,fun scan_dot/6}};
scan_dot(eof=Cs, St, Line, Col, Toks, Ncs) ->
Attrs = attributes(Line, Col, St, Ncs),
{ok,[{dot,Attrs}|Toks],Cs,Line,incr_column(Col, 1)};
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index d26443f277..d67617260e 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,35 +24,41 @@
%% Internal API.
-export([start/0, start/1]).
--include_lib("kernel/include/file.hrl").
+%%-----------------------------------------------------------------------
-define(SHEBANG, "/usr/bin/env escript").
-define(COMMENT, "This is an -*- erlang -*- file").
--record(state, {file,
- module,
+%%-----------------------------------------------------------------------
+
+-type mode() :: 'native' | 'compile' | 'debug' | 'interpret' | 'run'.
+-type source() :: 'archive' | 'beam' | 'text'.
+
+-record(state, {file :: file:filename(),
+ module :: module(),
forms_or_bin,
- source,
- n_errors,
- mode,
- exports_main,
- has_records}).
--record(sections, {type,
- shebang,
- comment,
- emu_args,
- body}).
--record(extract_options, {compile_source}).
+ source :: source(),
+ n_errors :: non_neg_integer(),
+ mode :: mode(),
+ exports_main :: boolean(),
+ has_records :: boolean()}).
-type shebang() :: string().
-type comment() :: string().
-type emu_args() :: string().
--type escript_filename() :: string().
--type filename() :: string().
+
+-record(sections, {type,
+ shebang :: shebang(),
+ comment :: comment(),
+ emu_args :: emu_args(),
+ body}).
+
+-record(extract_options, {compile_source}).
+
-type zip_file() ::
- filename()
- | {filename(), binary()}
- | {filename(), binary(), #file_info{}}.
+ file:filename()
+ | {file:filename(), binary()}
+ | {file:filename(), binary(), file:file_info()}.
-type zip_create_option() :: term().
-type section() ::
shebang
@@ -60,13 +66,15 @@
| comment
| {comment, comment()}
| {emu_args, emu_args()}
- | {source, filename() | binary()}
- | {beam, filename() | binary()}
- | {archive, filename() | binary()}
+ | {source, file:filename() | binary()}
+ | {beam, file:filename() | binary()}
+ | {archive, file:filename() | binary()}
| {archive, [zip_file()], [zip_create_option()]}.
+%%-----------------------------------------------------------------------
+
%% Create a complete escript file with both header and body
--spec create(escript_filename() | binary, [section()]) ->
+-spec create(file:filename() | binary, [section()]) ->
ok | {ok, binary()} | {error, term()}.
create(File, Options) when is_list(Options) ->
@@ -149,7 +157,9 @@ prepare(BadOptions, _) ->
-type section_name() :: shebang | comment | emu_args | body .
-type extract_option() :: compile_source | {section, [section_name()]}.
--spec extract(filename(), [extract_option()]) -> {ok, [section()]} | {error, term()}.
+-spec extract(file:filename(), [extract_option()]) ->
+ {ok, [section()]} | {error, term()}.
+
extract(File, Options) when is_list(File), is_list(Options) ->
try
EO = parse_extract_options(Options,
@@ -239,6 +249,7 @@ normalize_section(Name, Chars) ->
{Name, Chars}.
-spec script_name() -> string().
+
script_name() ->
[ScriptName|_] = init:get_plain_arguments(),
ScriptName.
@@ -248,10 +259,12 @@ script_name() ->
%%
-spec start() -> no_return().
+
start() ->
start([]).
-spec start([string()]) -> no_return().
+
start(EscriptOptions) ->
try
%% Commands run using -run or -s are run in a process
@@ -291,7 +304,11 @@ parse_and_run(File, Args, Options) ->
false ->
case lists:member("i", Options) of
true -> interpret;
- false -> Mode
+ false ->
+ case lists:member("n", Options) of
+ true -> native;
+ false -> Mode
+ end
end
end
end,
@@ -308,6 +325,14 @@ parse_and_run(File, Args, Options) ->
_Other ->
fatal("There were compilation errors.")
end;
+ native ->
+ case compile:forms(FormsOrBin, [report,native]) of
+ {ok, Module, BeamBin} ->
+ {module, Module} = code:load_binary(Module, File, BeamBin),
+ run(Module, Args);
+ _Other ->
+ fatal("There were compilation errors.")
+ end;
debug ->
case compile:forms(FormsOrBin, [report, debug_info]) of
{ok,Module,BeamBin} ->
@@ -484,18 +509,12 @@ find_first_body_line(Fd, HeaderSz0, LineNo, KeepFirst, Sections) ->
classify_line(Line) ->
case Line of
- [$\#, $\! | _] ->
- shebang;
- [$P, $K | _] ->
- archive;
- [$F, $O, $R, $1 | _] ->
- beam;
- [$%, $%, $\! | _] ->
- emu_args;
- [$% | _] ->
- comment;
- _ ->
- undefined
+ "#!" ++ _ -> shebang;
+ "PK" ++ _ -> archive;
+ "FOR1" ++ _ -> beam;
+ "%%!" ++ _ -> emu_args;
+ "%" ++ _ -> comment;
+ _ -> undefined
end.
guess_type(Line) ->
@@ -531,7 +550,6 @@ parse_archive(S, File, HeaderSz) ->
end,
list_to_atom(lists:reverse(RevBase2))
end,
-
S#state{source = archive,
mode = run,
module = Mod,
@@ -564,9 +582,7 @@ parse_beam(S, File, HeaderSz, CheckOnly) ->
forms_or_bin = Bin}
end;
{error, beam_lib, Reason} when is_tuple(Reason) ->
- fatal(element(1, Reason));
- {error, beam_lib, Reason} ->
- fatal(Reason)
+ fatal(element(1, Reason))
end.
parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) ->
@@ -587,8 +603,8 @@ parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) ->
epp_parse_file2(Epp, S2, [ModForm, FileForm], OptModRes);
{error, _} ->
epp_parse_file2(Epp, S2, [FileForm], OptModRes);
- {eof,LastLine} ->
- S#state{forms_or_bin = [FileForm, {eof,LastLine}]}
+ {eof, _LastLine} = Eof ->
+ S#state{forms_or_bin = [FileForm, Eof]}
end,
ok = epp:close(Epp),
ok = file:close(Fd),
@@ -660,7 +676,7 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
{attribute,Ln,mode,NewMode} ->
S2 = S#state{mode = NewMode},
if
- NewMode =:= compile; NewMode =:= interpret; NewMode =:= debug ->
+ NewMode =:= compile; NewMode =:= interpret; NewMode =:= debug; NewMode =:= native ->
epp_parse_file(Epp, S2, [Form | Forms]);
true ->
Args = lists:flatten(io_lib:format("illegal mode attribute: ~p", [NewMode])),
@@ -683,8 +699,8 @@ epp_parse_file2(Epp, S, Forms, Parsed) ->
io:format("~s:~w: ~s\n",
[S#state.file,Ln,Mod:format_error(Args)]),
epp_parse_file(Epp, S#state{n_errors = S#state.n_errors + 1}, [Form | Forms]);
- {eof,LastLine} ->
- S#state{forms_or_bin = lists:reverse([{eof, LastLine} | Forms])}
+ {eof, _LastLine} = Eof ->
+ S#state{forms_or_bin = lists:reverse([Eof | Forms])}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index d7b5dbc636..6e6e949e2c 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -42,10 +42,15 @@
-export([i/0, i/1, i/2, i/3]).
-%%------------------------------------------------------------------------------
+-export_type([tab/0, tid/0]).
+
+%%-----------------------------------------------------------------------------
-type tab() :: atom() | tid().
+%% a similar definition is also in erl_types
+-opaque tid() :: integer().
+
-type ext_info() :: 'md5sum' | 'object_count'.
-type protection() :: 'private' | 'protected' | 'public'.
-type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'.
@@ -63,7 +68,7 @@
-type match_pattern() :: atom() | tuple().
-type match_specs() :: [{match_pattern(), [_], [_]}].
-%%------------------------------------------------------------------------------
+%%-----------------------------------------------------------------------------
%% The following functions used to be found in this module, but
%% are now BIFs (i.e. implemented in C).
@@ -507,7 +512,7 @@ file2tab(File) ->
file2tab(File, Opts) ->
try
- {ok,Verify} = parse_f2t_opts(Opts,false),
+ {ok,Verify,TabArg} = parse_f2t_opts(Opts,false,[]),
Name = make_ref(),
{ok, Major, Minor, FtOptions, MD5State, FullHeader, DLContext} =
case disk_log:open([{name, Name},
@@ -535,7 +540,7 @@ file2tab(File, Opts) ->
true ->
ok
end,
- {ok, Tab, HeadCount} = create_tab(FullHeader),
+ {ok, Tab, HeadCount} = create_tab(FullHeader, TabArg),
StrippedOptions =
case Verify of
true ->
@@ -671,15 +676,17 @@ do_read_and_verify(ReadFun,InitState,Tab,FtOptions,HeadCount,Verify) ->
{ok,Tab}
end.
-parse_f2t_opts([],Verify) ->
- {ok,Verify};
-parse_f2t_opts([{verify, true}|T],_OV) ->
- parse_f2t_opts(T,true);
-parse_f2t_opts([{verify,false}|T],OV) ->
- parse_f2t_opts(T,OV);
-parse_f2t_opts([Unexpected|_],_) ->
+parse_f2t_opts([],Verify,Tab) ->
+ {ok,Verify,Tab};
+parse_f2t_opts([{verify, true}|T],_OV,Tab) ->
+ parse_f2t_opts(T,true,Tab);
+parse_f2t_opts([{verify,false}|T],OV,Tab) ->
+ parse_f2t_opts(T,OV,Tab);
+parse_f2t_opts([{table,Tab}|T],OV,[]) ->
+ parse_f2t_opts(T,OV,Tab);
+parse_f2t_opts([Unexpected|_],_,_) ->
throw({unknown_option,Unexpected});
-parse_f2t_opts(Malformed,_) ->
+parse_f2t_opts(Malformed,_,_) ->
throw({malformed_option,Malformed}).
count_mandatory([]) ->
@@ -855,19 +862,28 @@ load_table(ReadFun, State, Tab) ->
load_table(ReadFun, NewState, Tab)
end.
-create_tab(I) ->
+create_tab(I, TabArg) ->
{name, Name} = lists:keyfind(name, 1, I),
{type, Type} = lists:keyfind(type, 1, I),
{protection, P} = lists:keyfind(protection, 1, I),
{named_table, Val} = lists:keyfind(named_table, 1, I),
{keypos, _Kp} = Keypos = lists:keyfind(keypos, 1, I),
{size, Sz} = lists:keyfind(size, 1, I),
- try
- Tab = ets:new(Name, [Type, P, Keypos | named_table(Val)]),
- {ok, Tab, Sz}
- catch
- _:_ ->
- throw(cannot_create_table)
+ Comp = case lists:keyfind(compressed, 1, I) of
+ {compressed, true} -> [compressed];
+ {compressed, false} -> [];
+ false -> []
+ end,
+ case TabArg of
+ [] ->
+ try
+ Tab = ets:new(Name, [Type, P, Keypos] ++ named_table(Val) ++ Comp),
+ {ok, Tab, Sz}
+ catch _:_ ->
+ throw(cannot_create_table)
+ end;
+ _ ->
+ {ok, TabArg, Sz}
end.
named_table(true) -> [named_table];
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index 3671aecdcb..2cbd6cdae7 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,6 +19,8 @@
%%
-module(eval_bits).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([expr_grp/3,expr_grp/5,match_bits/6,
match_bits/7,bin_gen/6]).
diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl
index e21a0c88f3..2a5b08b581 100644
--- a/lib/stdlib/src/file_sorter.erl
+++ b/lib/stdlib/src/file_sorter.erl
@@ -18,6 +18,8 @@
%%
-module(file_sorter).
+%% Avoid warning for local function error/2 clashing with autoimported BIF.
+-compile({no_auto_import,[error/2]}).
-export([sort/1, sort/2, sort/3,
keysort/2, keysort/3, keysort/4,
merge/2, merge/3,
@@ -191,7 +193,7 @@ options([{format, Format} | L], Opts) when Format =:= binary;
options([{format, binary_term} | L], Opts) ->
options(L, Opts#opts{format = binary_term_fun()});
options([{size, Size} | L], Opts) when is_integer(Size), Size >= 0 ->
- options(L, Opts#opts{size = max(Size, 1)});
+ options(L, Opts#opts{size = erlang:max(Size, 1)});
options([{no_files, NoFiles} | L], Opts) when is_integer(NoFiles),
NoFiles > 1 ->
options(L, Opts#opts{no_files = NoFiles});
@@ -997,10 +999,10 @@ close_read_fun(Fd, FileName, fsort) ->
file:delete(FileName).
read_objs(Fd, FileName, I, L, Bin0, Size0, LSz, W) ->
- Max = max(Size0, ?CHUNKSIZE),
+ Max = erlang:max(Size0, ?CHUNKSIZE),
BSz0 = byte_size(Bin0),
Min = Size0 - BSz0 + W#w.hdlen, % Min > 0
- NoBytes = max(Min, Max),
+ NoBytes = erlang:max(Min, Max),
case read(Fd, FileName, NoBytes, W) of
{ok, Bin} ->
BSz = byte_size(Bin),
@@ -1180,9 +1182,6 @@ make_key2([Kp], T) ->
make_key2([Kp | Kps], T) ->
[element(Kp, T) | make_key2(Kps, T)].
-max(A, B) when A < B -> B;
-max(A, _) -> A.
-
infun(W) ->
W1 = W#w{in = undefined},
try (W#w.in)(read) of
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index 5c5e084e17..c845b61204 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -20,6 +20,8 @@
%% File utilities.
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([wildcard/1, wildcard/2, is_dir/1, is_file/1, is_regular/1,
compile_wildcard/1]).
-export([fold_files/5, last_modified/1, file_size/1, ensure_dir/1]).
@@ -45,14 +47,14 @@ wildcard(Pattern) when is_list(Pattern) ->
?HANDLE_ERROR(do_wildcard(Pattern, file)).
-spec wildcard(file:name(), file:name() | atom()) -> [file:filename()].
-wildcard(Pattern, Cwd) when is_list(Pattern), is_list(Cwd) ->
+wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) ->
?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file));
wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) ->
?HANDLE_ERROR(do_wildcard(Pattern, Mod)).
-spec wildcard(file:name(), file:name(), atom()) -> [file:filename()].
wildcard(Pattern, Cwd, Mod)
- when is_list(Pattern), is_list(Cwd), is_atom(Mod) ->
+ when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) ->
?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)).
-spec is_dir(file:name()) -> boolean().
@@ -116,7 +118,7 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Mod) ->
do_wildcard_comp({compiled_wildcard,[Base|Rest]}, Mod) ->
do_wildcard_1([Base], Rest, Mod).
-do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), is_list(Cwd) ->
+do_wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) ->
do_wildcard_comp(do_compile_wildcard(Pattern), Cwd, Mod).
do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
@@ -125,9 +127,18 @@ do_wildcard_comp({compiled_wildcard,{exists,File}}, Cwd, Mod) ->
_ -> []
end;
do_wildcard_comp({compiled_wildcard,[current|Rest]}, Cwd0, Mod) ->
- Cwd = filename:join([Cwd0]), %Slash away redundant slashes.
- PrefixLen = length(Cwd)+1,
- [lists:nthtail(PrefixLen, N) || N <- do_wildcard_1([Cwd], Rest, Mod)];
+ {Cwd,PrefixLen} = case filename:join([Cwd0]) of
+ Bin when is_binary(Bin) -> {Bin,byte_size(Bin)+1};
+ Other -> {Other,length(Other)+1}
+ end, %Slash away redundant slashes.
+ [
+ if
+ is_binary(N) ->
+ <<_:PrefixLen/binary,Res/binary>> = N,
+ Res;
+ true ->
+ lists:nthtail(PrefixLen, N)
+ end || N <- do_wildcard_1([Cwd], Rest, Mod)];
do_wildcard_comp({compiled_wildcard,[Base|Rest]}, _Cwd, Mod) ->
do_wildcard_1([Base], Rest, Mod).
@@ -164,36 +175,44 @@ do_is_regular(File, Mod) ->
%% If <Recursive> is true all sub-directories to <Dir> are processed
do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) ->
- {ok, Re1} = re:compile(RegExp),
- do_fold_files1(Dir, Re1, Recursive, Fun, Acc, Mod).
+ {ok, Re1} = re:compile(RegExp,[unicode]),
+ do_fold_files1(Dir, Re1, RegExp, Recursive, Fun, Acc, Mod).
-do_fold_files1(Dir, RegExp, Recursive, Fun, Acc, Mod) ->
+do_fold_files1(Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod) ->
case eval_list_dir(Dir, Mod) of
- {ok, Files} -> do_fold_files2(Files, Dir, RegExp, Recursive, Fun, Acc, Mod);
+ {ok, Files} -> do_fold_files2(Files, Dir, RegExp, OrigRE,
+ Recursive, Fun, Acc, Mod);
{error, _} -> Acc
end.
-do_fold_files2([], _Dir, _RegExp, _Recursive, _Fun, Acc, _Mod) ->
+%% OrigRE is not to be compiled as it's for non conforming filenames,
+%% i.e. for filenames that does not comply to the current encoding, which should
+%% be very rare. We use it only in those cases and do not want to precompile.
+do_fold_files2([], _Dir, _RegExp, _OrigRE, _Recursive, _Fun, Acc, _Mod) ->
Acc;
-do_fold_files2([File|T], Dir, RegExp, Recursive, Fun, Acc0, Mod) ->
+do_fold_files2([File|T], Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod) ->
FullName = filename:join(Dir, File),
case do_is_regular(FullName, Mod) of
true ->
- case re:run(File, RegExp, [{capture,none}]) of
+ case (catch re:run(File, if is_binary(File) -> OrigRE;
+ true -> RegExp end,
+ [{capture,none}])) of
match ->
Acc = Fun(FullName, Acc0),
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc, Mod);
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc, Mod);
+ {'EXIT',_} ->
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod);
nomatch ->
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod)
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod)
end;
false ->
case Recursive andalso do_is_dir(FullName, Mod) of
true ->
- Acc1 = do_fold_files1(FullName, RegExp, Recursive,
+ Acc1 = do_fold_files1(FullName, RegExp, OrigRE, Recursive,
Fun, Acc0, Mod),
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc1, Mod);
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc1, Mod);
false ->
- do_fold_files2(T, Dir, RegExp, Recursive, Fun, Acc0, Mod)
+ do_fold_files2(T, Dir, RegExp, OrigRE, Recursive, Fun, Acc0, Mod)
end
end.
@@ -266,6 +285,13 @@ do_wildcard_3(Base, [Pattern|Rest], Result, Mod) ->
do_wildcard_3(Base, [], Result, _Mod) ->
[Base|Result].
+wildcard_4(Pattern, [File|Rest], Base, Result) when is_binary(File) ->
+ case wildcard_5(Pattern, binary_to_list(File)) of
+ true ->
+ wildcard_4(Pattern, Rest, Base, [join(Base, File)|Result]);
+ false ->
+ wildcard_4(Pattern, Rest, Base, Result)
+ end;
wildcard_4(Pattern, [File|Rest], Base, Result) ->
case wildcard_5(Pattern, File) of
true ->
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index 01c06e4596..24abf1e977 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -41,6 +41,9 @@
-include_lib("kernel/include/file.hrl").
+-define(IS_DRIVELETTER(Letter),(((Letter >= $A) andalso (Letter =< $Z)) orelse
+ ((Letter >= $a) andalso (Letter =< $z)))).
+
%% Converts a relative filename to an absolute filename
%% or the filename itself if it already is an absolute filename
%% Note that no attempt is made to create the most beatiful
@@ -57,12 +60,18 @@
%% (for Unix) : absname("/") -> "/"
%% (for WIN32): absname("/") -> "D:/"
--spec absname(file:name()) -> string().
+
+-spec absname(file:name()) -> file:filename().
absname(Name) ->
{ok, Cwd} = file:get_cwd(),
absname(Name, Cwd).
--spec absname(file:name(), string()) -> string().
+-spec absname(file:name(), file:filename()) -> file:filename().
+absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) ->
+ absname(Name,filename_string_to_binary(AbsBase));
+absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) ->
+ absname(filename_string_to_binary(Name),AbsBase);
+
absname(Name, AbsBase) ->
case pathtype(Name) of
relative ->
@@ -77,6 +86,20 @@ absname(Name, AbsBase) ->
%% Handles volumerelative names (on Windows only).
+absname_vr([<<"/">>|Rest1], [Volume|_], _AbsBase) ->
+ %% Absolute path on current drive.
+ join([Volume|Rest1]);
+absname_vr([<<X, $:>>|Rest1], [<<X,_/binary>>|_], AbsBase) ->
+ %% Relative to current directory on current drive.
+ absname(join(Rest1), AbsBase);
+absname_vr([<<X, $:>>|Name], _, _AbsBase) ->
+ %% Relative to current directory on another drive.
+ Dcwd =
+ case file:get_cwd([X, $:]) of
+ {ok, Dir} -> filename_string_to_binary(Dir);
+ {error, _} -> <<X, $:, $/>>
+ end,
+ absname(join(Name), Dcwd);
absname_vr(["/"|Rest1], [Volume|_], _AbsBase) ->
%% Absolute path on current drive.
join([Volume|Rest1]);
@@ -92,41 +115,13 @@ absname_vr([[X, $:]|Name], _, _AbsBase) ->
end,
absname(join(Name), Dcwd).
-%% Joins a relative filename to an absolute base. For VxWorks the
-%% resulting name is fixed to minimize the length by collapsing
-%% ".." directories.
-%% For other systems this is just a join/2, but assumes that
+%% Joins a relative filename to an absolute base.
+%% This is just a join/2, but assumes that
%% AbsBase must be absolute and Name must be relative.
--spec absname_join(string(), file:name()) -> string().
+-spec absname_join(file:filename(), file:name()) -> file:filename().
absname_join(AbsBase, Name) ->
- case major_os_type() of
- vxworks ->
- absname_pretty(AbsBase, split(Name), lists:reverse(split(AbsBase)));
- _Else ->
- join(AbsBase, flatten(Name))
- end.
-
-%% Handles absolute filenames for VxWorks - these are 'pretty-printed',
-%% since a C function call chdir("/erlang/lib/../bin") really sets
-%% cwd to '/erlang/lib/../bin' which also works, but the long term
-%% effect is potentially not so good ...
-%%
-%% absname_pretty("../bin", "/erlang/lib") -> "/erlang/bin"
-%% absname_pretty("../../../..", "/erlang") -> "/erlang"
-
-absname_pretty(Abspath, Relpath, []) ->
- %% AbsBase _must_ begin with a vxworks device name
- {device, _Rest, Dev} = vxworks_first(Abspath),
- absname_pretty(Abspath, Relpath, [lists:reverse(Dev)]);
-absname_pretty(_Abspath, [], AbsBase) ->
- join(lists:reverse(AbsBase));
-absname_pretty(Abspath, [[$.]|Rest], AbsBase) ->
- absname_pretty(Abspath, Rest, AbsBase);
-absname_pretty(Abspath, [[$.,$.]|Rest], [_|AbsRest]) ->
- absname_pretty(Abspath, Rest, AbsRest);
-absname_pretty(Abspath, [First|Rest], AbsBase) ->
- absname_pretty(Abspath, Rest, [First|AbsBase]).
+ join(AbsBase, flatten(Name)).
%% Returns the part of the filename after the last directory separator,
%% or the filename itself if it has no separators.
@@ -136,18 +131,40 @@ absname_pretty(Abspath, [First|Rest], AbsBase) ->
%% basename("/usr/foo/") -> "foo" (trailing slashes ignored)
%% basename("/") -> []
--spec basename(file:name()) -> string().
+-spec basename(file:name()) -> file:filename().
+basename(Name) when is_binary(Name) ->
+ case os:type() of
+ {win32,_} ->
+ win_basenameb(Name);
+ _ ->
+ basenameb(Name,[<<"/">>])
+ end;
+
basename(Name0) ->
Name = flatten(Name0),
{DirSep2, DrvSep} = separators(),
basename1(skip_prefix(Name, DrvSep), [], DirSep2).
+win_basenameb(<<Letter,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter) ->
+ basenameb(Rest,[<<"/">>,<<"\\">>]);
+win_basenameb(O) ->
+ basenameb(O,[<<"/">>,<<"\\">>]).
+basenameb(Bin,Sep) ->
+ Parts = [ X || X <- binary:split(Bin,Sep,[global]),
+ X =/= <<>> ],
+ if
+ Parts =:= [] ->
+ <<>>;
+ true ->
+ lists:last(Parts)
+ end.
+
+
+
basename1([$/|[]], Tail, DirSep2) ->
basename1([], Tail, DirSep2);
basename1([$/|Rest], _Tail, DirSep2) ->
basename1(Rest, [], DirSep2);
-basename1([[_|_]=List|Rest], Tail, DirSep2) ->
- basename1(List++Rest, Tail, DirSep2);
basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) ->
basename1([$/|Rest], Tail, DirSep2);
basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) ->
@@ -155,26 +172,11 @@ basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) ->
basename1([], Tail, _DirSep2) ->
lists:reverse(Tail).
-skip_prefix(Name, false) -> % No prefix for unix, but for VxWorks.
- case major_os_type() of
- vxworks ->
- case vxworks_first(Name) of
- {device, Rest, _Device} ->
- Rest;
- {not_device, _Rest, _First} ->
- Name
- end;
- _Else ->
- Name
- end;
-skip_prefix(Name, DrvSep) ->
- skip_prefix1(Name, DrvSep).
-
-skip_prefix1([L, DrvSep|Name], DrvSep) when is_integer(L) ->
+skip_prefix(Name, false) ->
Name;
-skip_prefix1([L], _) when is_integer(L) ->
- [L];
-skip_prefix1(Name, _) ->
+skip_prefix([L, DrvSep|Name], DrvSep) when ?IS_DRIVELETTER(L) ->
+ Name;
+skip_prefix(Name, _) ->
Name.
%% Returns the last component of the filename, with the given
@@ -190,7 +192,29 @@ skip_prefix1(Name, _) ->
%% rootname(basename("xxx.jam")) -> "xxx"
%% rootname(basename("xxx.erl")) -> "xxx"
--spec basename(file:name(), file:name()) -> string().
+-spec basename(file:name(), file:name()) -> file:filename().
+basename(Name, Ext) when is_binary(Name), is_list(Ext) ->
+ basename(Name,filename_string_to_binary(Ext));
+basename(Name, Ext) when is_list(Name), is_binary(Ext) ->
+ basename(filename_string_to_binary(Name),Ext);
+basename(Name, Ext) when is_binary(Name), is_binary(Ext) ->
+ BName = basename(Name),
+ LAll = byte_size(Name),
+ LN = byte_size(BName),
+ LE = byte_size(Ext),
+ case LN - LE of
+ Neg when Neg < 0 ->
+ BName;
+ Pos ->
+ StartLen = LAll - Pos - LE,
+ case Name of
+ <<_:StartLen/binary,Part:Pos/binary,Ext/binary>> ->
+ Part;
+ _Other ->
+ BName
+ end
+ end;
+
basename(Name0, Ext0) ->
Name = flatten(Name0),
Ext = flatten(Ext0),
@@ -204,7 +228,7 @@ basename([$/|[]], Ext, Tail, DrvSep2) ->
basename([], Ext, Tail, DrvSep2);
basename([$/|Rest], Ext, _Tail, DrvSep2) ->
basename(Rest, Ext, [], DrvSep2);
-basename([$\\|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) ->
+basename([DirSep2|Rest], Ext, Tail, DirSep2) when is_integer(DirSep2) ->
basename([$/|Rest], Ext, Tail, DirSep2);
basename([Char|Rest], Ext, Tail, DrvSep2) when is_integer(Char) ->
basename(Rest, Ext, [Char|Tail], DrvSep2);
@@ -216,24 +240,44 @@ basename([], _Ext, Tail, _DrvSep2) ->
%% Example: dirname("/usr/src/kalle.erl") -> "/usr/src",
%% dirname("kalle.erl") -> "."
--spec dirname(file:name()) -> string().
+-spec dirname(file:name()) -> file:filename().
+dirname(Name) when is_binary(Name) ->
+ {Dsep,Drivesep} = separators(),
+ SList = case Dsep of
+ Sep when is_integer(Sep) ->
+ [ <<Sep>> ];
+ _ ->
+ []
+ end,
+ {XPart0,Dirs} = case Drivesep of
+ X when is_integer(X) ->
+ case Name of
+ <<DL,X,Rest/binary>> when ?IS_DRIVELETTER(DL) ->
+ {<<DL,X>>,Rest};
+ _ ->
+ {<<>>,Name}
+ end;
+ _ ->
+ {<<>>,Name}
+ end,
+ Parts0 = binary:split(Dirs,[<<"/">>|SList],[global]),
+ %% Fairly short lists of parts, OK to reverse twice...
+ Parts = case Parts0 of
+ [] -> [];
+ _ -> lists:reverse(fstrip(tl(lists:reverse(Parts0))))
+ end,
+ XPart = case {Parts,XPart0} of
+ {[],<<>>} ->
+ <<".">>;
+ _ ->
+ XPart0
+ end,
+ dirjoin(Parts,XPart,<<"/">>);
+
dirname(Name0) ->
Name = flatten(Name0),
- case os:type() of
- vxworks ->
- {Devicep, Restname, FirstComp} = vxworks_first(Name),
- case Devicep of
- device ->
- dirname(Restname, FirstComp, [], separators());
- _ ->
- dirname(Name, [], [], separators())
- end;
- _ ->
- dirname(Name, [], [], separators())
- end.
+ dirname(Name, [], [], separators()).
-dirname([[_|_]=List|Rest], Dir, File, Seps) ->
- dirname(List++Rest, Dir, File, Seps);
dirname([$/|Rest], Dir, File, Seps) ->
dirname(Rest, File++Dir, [$/], Seps);
dirname([DirSep|Rest], Dir, File, {DirSep,_}=Seps) when is_integer(DirSep) ->
@@ -258,6 +302,26 @@ dirname([], [DrvSep,Dl], File, {_,DrvSep}) ->
end;
dirname([], Dir, _, _) ->
lists:reverse(Dir).
+
+%% Compatibility with lists variant, remove trailing slashes
+fstrip([<<>>,X|Y]) ->
+ fstrip([X|Y]);
+fstrip(A) ->
+ A.
+
+
+dirjoin([<<>>|T],Acc,Sep) ->
+ dirjoin1(T,<<Acc/binary,"/">>,Sep);
+dirjoin(A,B,C) ->
+ dirjoin1(A,B,C).
+
+dirjoin1([],Acc,_) ->
+ Acc;
+dirjoin1([One],Acc,_) ->
+ <<Acc/binary,One/binary>>;
+dirjoin1([H|T],Acc,Sep) ->
+ dirjoin(T,<<Acc/binary,H/binary,Sep/binary>>,Sep).
+
%% Given a filename string, returns the file extension,
%% including the period. Returns an empty list if there
@@ -268,7 +332,29 @@ dirname([], Dir, _, _) ->
%%
%% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src"
--spec extension(file:name()) -> string().
+-spec extension(file:name()) -> file:filename().
+extension(Name) when is_binary(Name) ->
+ {Dsep,_} = separators(),
+ SList = case Dsep of
+ Sep when is_integer(Sep) ->
+ [ <<Sep>> ];
+ _ ->
+ []
+ end,
+ case binary:matches(Name,[<<".">>]) of
+ [] ->
+ <<>>;
+ List ->
+ {Pos,_} = lists:last(List),
+ <<_:Pos/binary,Part/binary>> = Name,
+ case binary:match(Part,[<<"/">>|SList]) of
+ nomatch ->
+ Part;
+ _ ->
+ <<>>
+ end
+ end;
+
extension(Name0) ->
Name = flatten(Name0),
extension(Name, [], major_os_type()).
@@ -281,8 +367,6 @@ extension([$/|Rest], _Result, OsType) ->
extension(Rest, [], OsType);
extension([$\\|Rest], _Result, win32) ->
extension(Rest, [], win32);
-extension([$\\|Rest], _Result, vxworks) ->
- extension(Rest, [], vxworks);
extension([Char|Rest], Result, OsType) when is_integer(Char) ->
extension(Rest, [Char|Result], OsType);
extension([], Result, _OsType) ->
@@ -290,23 +374,36 @@ extension([], Result, _OsType) ->
%% Joins a list of filenames with directory separators.
--spec join([string()]) -> string().
+-spec join([file:filename()]) -> file:filename().
join([Name1, Name2|Rest]) ->
join([join(Name1, Name2)|Rest]);
join([Name]) when is_list(Name) ->
join1(Name, [], [], major_os_type());
+join([Name]) when is_binary(Name) ->
+ join1b(Name, <<>>, [], major_os_type());
join([Name]) when is_atom(Name) ->
join([atom_to_list(Name)]).
%% Joins two filenames with directory separators.
--spec join(string(), string()) -> string().
+-spec join(file:filename(), file:filename()) -> file:filename().
join(Name1, Name2) when is_list(Name1), is_list(Name2) ->
OsType = major_os_type(),
case pathtype(Name2) of
relative -> join1(Name1, Name2, [], OsType);
_Other -> join1(Name2, [], [], OsType)
end;
+join(Name1, Name2) when is_binary(Name1), is_list(Name2) ->
+ join(Name1,filename_string_to_binary(Name2));
+join(Name1, Name2) when is_list(Name1), is_binary(Name2) ->
+ join(filename_string_to_binary(Name1),Name2);
+join(Name1, Name2) when is_binary(Name1), is_binary(Name2) ->
+ OsType = major_os_type(),
+ case pathtype(Name2) of
+ relative -> join1b(Name1, Name2, [], OsType);
+ _Other -> join1b(Name2, <<>>, [], OsType)
+ end;
+
join(Name1, Name2) when is_atom(Name1) ->
join(atom_to_list(Name1), Name2);
join(Name1, Name2) when is_atom(Name2) ->
@@ -321,8 +418,6 @@ when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
join1(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
join1([$\\|Rest], RelativeName, Result, win32) ->
join1([$/|Rest], RelativeName, Result, win32);
-join1([$\\|Rest], RelativeName, Result, vxworks) ->
- join1([$/|Rest], RelativeName, Result, vxworks);
join1([$/|Rest], RelativeName, [$., $/|Result], OsType) ->
join1(Rest, RelativeName, [$/|Result], OsType);
join1([$/|Rest], RelativeName, [$/|Result], OsType) ->
@@ -344,6 +439,26 @@ join1([Char|Rest], RelativeName, Result, OsType) when is_integer(Char) ->
join1([Atom|Rest], RelativeName, Result, OsType) when is_atom(Atom) ->
join1(atom_to_list(Atom)++Rest, RelativeName, Result, OsType).
+join1b(<<UcLetter, $:, Rest/binary>>, RelativeName, [], win32)
+when is_integer(UcLetter), UcLetter >= $A, UcLetter =< $Z ->
+ join1b(Rest, RelativeName, [$:, UcLetter+$a-$A], win32);
+join1b(<<$\\,Rest/binary>>, RelativeName, Result, win32) ->
+ join1b(<<$/,Rest/binary>>, RelativeName, Result, win32);
+join1b(<<$/,Rest/binary>>, RelativeName, [$., $/|Result], OsType) ->
+ join1b(Rest, RelativeName, [$/|Result], OsType);
+join1b(<<$/,Rest/binary>>, RelativeName, [$/|Result], OsType) ->
+ join1b(Rest, RelativeName, [$/|Result], OsType);
+join1b(<<>>, <<>>, Result, OsType) ->
+ list_to_binary(maybe_remove_dirsep(Result, OsType));
+join1b(<<>>, RelativeName, [$:|Rest], win32) ->
+ join1b(RelativeName, <<>>, [$:|Rest], win32);
+join1b(<<>>, RelativeName, [$/|Result], OsType) ->
+ join1b(RelativeName, <<>>, [$/|Result], OsType);
+join1b(<<>>, RelativeName, Result, OsType) ->
+ join1b(RelativeName, <<>>, [$/|Result], OsType);
+join1b(<<Char,Rest/binary>>, RelativeName, Result, OsType) when is_integer(Char) ->
+ join1b(Rest, RelativeName, [Char|Result], OsType).
+
maybe_remove_dirsep([$/, $:, Letter], win32) ->
[Letter, $:, $/];
maybe_remove_dirsep([$/], _) ->
@@ -357,7 +472,13 @@ maybe_remove_dirsep(Name, _) ->
%% a given base directory, which is is assumed to be normalised
%% by a previous call to join/{1,2}.
--spec append(string(), file:name()) -> string().
+-spec append(file:filename(), file:name()) -> file:filename().
+append(Dir, Name) when is_binary(Dir), is_binary(Name) ->
+ <<Dir/binary,$/:8,Name/binary>>;
+append(Dir, Name) when is_binary(Dir) ->
+ append(Dir,filename_string_to_binary(Name));
+append(Dir, Name) when is_binary(Name) ->
+ append(filename_string_to_binary(Dir),Name);
append(Dir, Name) ->
Dir ++ [$/|Name].
@@ -376,19 +497,14 @@ append(Dir, Name) ->
-spec pathtype(file:name()) -> 'absolute' | 'relative' | 'volumerelative'.
pathtype(Atom) when is_atom(Atom) ->
pathtype(atom_to_list(Atom));
-pathtype(Name) when is_list(Name) ->
+pathtype(Name) when is_list(Name) or is_binary(Name) ->
case os:type() of
{unix, _} -> unix_pathtype(Name);
- {win32, _} -> win32_pathtype(Name);
- vxworks -> case vxworks_first(Name) of
- {device, _Rest, _Dev} ->
- absolute;
- _ ->
- relative
- end;
- {ose,_} -> unix_pathtype(Name)
+ {win32, _} -> win32_pathtype(Name)
end.
+unix_pathtype(<<$/,_/binary>>) ->
+ absolute;
unix_pathtype([$/|_]) ->
absolute;
unix_pathtype([List|Rest]) when is_list(List) ->
@@ -404,6 +520,15 @@ win32_pathtype([Atom|Rest]) when is_atom(Atom) ->
win32_pathtype(atom_to_list(Atom)++Rest);
win32_pathtype([Char, List|Rest]) when is_list(List) ->
win32_pathtype([Char|List++Rest]);
+win32_pathtype(<<$/, $/, _/binary>>) -> absolute;
+win32_pathtype(<<$\\, $/, _/binary>>) -> absolute;
+win32_pathtype(<<$/, $\\, _/binary>>) -> absolute;
+win32_pathtype(<<$\\, $\\, _/binary>>) -> absolute;
+win32_pathtype(<<$/, _/binary>>) -> volumerelative;
+win32_pathtype(<<$\\, _/binary>>) -> volumerelative;
+win32_pathtype(<<_Letter, $:, $/, _/binary>>) -> absolute;
+win32_pathtype(<<_Letter, $:, $\\, _/binary>>) -> absolute;
+win32_pathtype(<<_Letter, $:, _/binary>>) -> volumerelative;
win32_pathtype([$/, $/|_]) -> absolute;
win32_pathtype([$\\, $/|_]) -> absolute;
win32_pathtype([$/, $\\|_]) -> absolute;
@@ -422,7 +547,9 @@ win32_pathtype(_) -> relative.
%% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle"
%% rootname("/jam.src/foo.erl") -> "/jam.src/foo"
--spec rootname(file:name()) -> string().
+-spec rootname(file:name()) -> file:filename().
+rootname(Name) when is_binary(Name) ->
+ list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128
rootname(Name0) ->
Name = flatten(Name0),
rootname(Name, [], [], major_os_type()).
@@ -431,8 +558,6 @@ rootname([$/|Rest], Root, Ext, OsType) ->
rootname(Rest, [$/]++Ext++Root, [], OsType);
rootname([$\\|Rest], Root, Ext, win32) ->
rootname(Rest, [$/]++Ext++Root, [], win32);
-rootname([$\\|Rest], Root, Ext, vxworks) ->
- rootname(Rest, [$/]++Ext++Root, [], vxworks);
rootname([$.|Rest], Root, [], OsType) ->
rootname(Rest, Root, ".", OsType);
rootname([$.|Rest], Root, Ext, OsType) ->
@@ -451,7 +576,13 @@ rootname([], Root, _Ext, _OsType) ->
%% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam"
%% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo"
--spec rootname(file:name(), file:name()) -> string().
+-spec rootname(file:name(), file:name()) -> file:filename().
+rootname(Name, Ext) when is_binary(Name), is_binary(Ext) ->
+ list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext)));
+rootname(Name, Ext) when is_binary(Name) ->
+ rootname(Name,filename_string_to_binary(Ext));
+rootname(Name, Ext) when is_binary(Ext) ->
+ rootname(filename_string_to_binary(Name),Ext);
rootname(Name0, Ext0) ->
Name = flatten(Name0),
Ext = flatten(Ext0),
@@ -471,27 +602,55 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) ->
%% split("foo/bar") -> ["foo", "bar"]
%% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"]
--spec split(file:name()) -> [string()].
+-spec split(file:name()) -> [file:filename()].
+split(Name) when is_binary(Name) ->
+ case os:type() of
+ {win32, _} -> win32_splitb(Name);
+ _ -> unix_splitb(Name)
+ end;
+
split(Name0) ->
Name = flatten(Name0),
case os:type() of
- {unix, _} -> unix_split(Name);
{win32, _} -> win32_split(Name);
- vxworks -> vxworks_split(Name);
- {ose,_} -> unix_split(Name)
+ _ -> unix_split(Name)
end.
-%% If a VxWorks filename starts with '[/\].*[^/\]' '[/\].*:' or '.*:'
-%% that part of the filename is considered a device.
-%% The rest of the name is interpreted exactly as for win32.
-%% XXX - dirty solution to make filename:split([]) return the same thing on
-%% VxWorks as on unix and win32.
-vxworks_split([]) ->
- [];
-vxworks_split(L) ->
- {_Devicep, Rest, FirstComp} = vxworks_first(L),
- split(Rest, [], [lists:reverse(FirstComp)], win32).
+unix_splitb(Name) ->
+ L = binary:split(Name,[<<"/">>],[global]),
+ LL = case L of
+ [<<>>|Rest] ->
+ [<<"/">>|Rest];
+ _ ->
+ L
+ end,
+ [ X || X <- LL, X =/= <<>>].
+
+
+fix_driveletter(Letter0) ->
+ if
+ Letter0 >= $A, Letter0 =< $Z ->
+ Letter0+$a-$A;
+ true ->
+ Letter0
+ end.
+win32_splitb(<<Letter0,$:, Slash, Rest/binary>>) when (((Slash =:= $\\) orelse (Slash =:= $/)) andalso
+ ?IS_DRIVELETTER(Letter0)) ->
+ Letter = fix_driveletter(Letter0),
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<Letter,$:,$/>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(<<Letter0,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter0) ->
+ Letter = fix_driveletter(Letter0),
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<Letter,$:>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(<<Slash,Rest/binary>>) when ((Slash =:= $\\) orelse (Slash =:= $/)) ->
+ L = binary:split(Rest,[<<"/">>,<<"\\">>],[global]),
+ [<<$/>> | [ X || X <- L, X =/= <<>> ]];
+win32_splitb(Name) ->
+ L = binary:split(Name,[<<"/">>,<<"\\">>],[global]),
+ [ X || X <- L, X =/= <<>> ].
+
unix_split(Name) ->
split(Name, [], unix).
@@ -502,8 +661,6 @@ win32_split([X, $\\|Rest]) when is_integer(X) ->
win32_split([X, $/|Rest]);
win32_split([X, Y, $\\|Rest]) when is_integer(X), is_integer(Y) ->
win32_split([X, Y, $/|Rest]);
-win32_split([$/, $/|Rest]) ->
- split(Rest, [], [[$/, $/]]);
win32_split([UcLetter, $:|Rest]) when UcLetter >= $A, UcLetter =< $Z ->
win32_split([UcLetter+$a-$A, $:|Rest]);
win32_split([Letter, $:, $/|Rest]) ->
@@ -528,8 +685,6 @@ split([$/|Rest], Comp, Components, OsType) ->
split(Rest, [], [lists:reverse(Comp)|Components], OsType);
split([Char|Rest], Comp, Components, OsType) when is_integer(Char) ->
split(Rest, [Char|Comp], Components, OsType);
-split([List|Rest], Comp, Components, OsType) when is_list(List) ->
- split(List++Rest, Comp, Components, OsType);
split([], [], Components, _OsType) ->
lists:reverse(Components);
split([], Comp, Components, OsType) ->
@@ -540,7 +695,7 @@ split([], Comp, Components, OsType) ->
%% will be converted to backslashes. On all platforms, the
%% name will be normalized as done by join/1.
--spec nativename(string()) -> string().
+-spec nativename(file:filename()) -> file:filename().
nativename(Name0) ->
Name = join([Name0]), %Normalize.
case os:type() of
@@ -557,13 +712,12 @@ win32_nativename([]) ->
separators() ->
case os:type() of
- {unix, _} -> {false, false};
{win32, _} -> {$\\, $:};
- vxworks -> {$\\, false};
- {ose,_} -> {false, false}
+ _ -> {false, false}
end.
+
%% find_src(Module) --
%% find_src(Module, Rules) --
%%
@@ -733,45 +887,12 @@ major_os_type() ->
OsT -> OsT
end.
-%% Need to take care of the first pathname component separately
-%% due to VxWorks less than good device naming rules.
-%% (i.e. this is VxWorks specific ...)
-%% The following four all starts with device names
-%% elrond:/foo -> elrond:
-%% elrond:\\foo.bar -> elrond:
-%% /DISK1:foo -> /DISK1:
-%% /usr/include -> /usr
-%% This one doesn't:
-%% foo/bar
-
-vxworks_first([]) ->
- {not_device, [], []};
-vxworks_first([$/|T]) ->
- vxworks_first2(device, T, [$/]);
-vxworks_first([$\\|T]) ->
- vxworks_first2(device, T, [$/]);
-vxworks_first([H|T]) when is_list(H) ->
- vxworks_first(H++T);
-vxworks_first([H|T]) ->
- vxworks_first2(not_device, T, [H]).
-
-vxworks_first2(Devicep, [], FirstComp) ->
- {Devicep, [], FirstComp};
-vxworks_first2(Devicep, [$/|T], FirstComp) ->
- {Devicep, [$/|T], FirstComp};
-vxworks_first2(Devicep, [$\\|T], FirstComp) ->
- {Devicep, [$/|T], FirstComp};
-vxworks_first2(_Devicep, [$:|T], FirstComp)->
- {device, T, [$:|FirstComp]};
-vxworks_first2(Devicep, [H|T], FirstComp) when is_list(H) ->
- vxworks_first2(Devicep, H++T, FirstComp);
-vxworks_first2(Devicep, [H|T], FirstComp) ->
- vxworks_first2(Devicep, T, [H|FirstComp]).
-
%% flatten(List)
%% Flatten a list, also accepting atoms.
--spec flatten(file:name()) -> string().
+-spec flatten(file:name()) -> file:filename().
+flatten(Bin) when is_binary(Bin) ->
+ Bin;
flatten(List) ->
do_flatten(List, []).
@@ -785,3 +906,12 @@ do_flatten([], Tail) ->
Tail;
do_flatten(Atom, Tail) when is_atom(Atom) ->
atom_to_list(Atom) ++ flatten(Tail).
+
+filename_string_to_binary(List) ->
+ case unicode:characters_to_binary(flatten(List),unicode,file:native_name_encoding()) of
+ {error,_,_} ->
+ erlang:error(badarg);
+ Bin when is_binary(Bin) ->
+ Bin
+ end.
+
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index 086dc79b46..fc5beb28b0 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -657,7 +657,7 @@ intersection_2([], _, As, S) ->
intersection_2(_, [], As, S) ->
{S, balance_revlist(As, S)}.
--spec intersection([gb_set()]) -> gb_set().
+-spec intersection([gb_set(),...]) -> gb_set().
intersection([S | Ss]) ->
intersection_list(S, Ss).
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 27ff9441e6..b1e9e3a02f 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -677,12 +677,23 @@ report_error(Handler, Reason, State, LastIn, SName) ->
_ ->
Reason
end,
+ Mod = Handler#handler.module,
+ FmtState = case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [get(), State],
+ case catch Mod:format_status(terminate, Args) of
+ {'EXIT', _} -> State;
+ Else -> Else
+ end;
+ _ ->
+ State
+ end,
error_msg("** gen_event handler ~p crashed.~n"
"** Was installed in ~p~n"
"** Last event was: ~p~n"
"** When handler state == ~p~n"
"** Reason == ~p~n",
- [handler(Handler),SName,LastIn,State,Reason1]).
+ [handler(Handler),SName,LastIn,FmtState,Reason1]).
handler(Handler) when not Handler#handler.id ->
Handler#handler.module;
@@ -711,10 +722,20 @@ get_modules(MSL) ->
%%-----------------------------------------------------------------
%% Status information
%%-----------------------------------------------------------------
-format_status(_Opt, StatusData) ->
- [_PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData,
+format_status(Opt, StatusData) ->
+ [PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData,
Header = lists:concat(["Status for event handler ", ServerName]),
+ FmtMSL = [case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [PDict, State],
+ case catch Mod:format_status(Opt, Args) of
+ {'EXIT', _} -> MSL;
+ Else -> MS#handler{state = Else}
+ end;
+ _ ->
+ MS
+ end || #handler{module = Mod, state = State} = MS <- MSL],
[{header, Header},
{data, [{"Status", SysState},
{"Parent", Parent}]},
- {items, {"Installed handlers", MSL}}].
+ {items, {"Installed handlers", FmtMSL}}].
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 9961646418..7d9960b912 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -542,7 +542,18 @@ terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
- error_info(Reason, Name, Msg, StateName, StateData, Debug),
+ FmtStateData =
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [get(), StateData],
+ case catch Mod:format_status(terminate, Args) of
+ {'EXIT', _} -> StateData;
+ Else -> Else
+ end;
+ _ ->
+ StateData
+ end,
+ error_info(Reason,Name,Msg,StateName,FmtStateData,Debug),
exit(Reason)
end
end.
@@ -603,22 +614,27 @@ get_msg(Msg) -> Msg.
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] =
StatusData,
- NameTag = if is_pid(Name) ->
- pid_to_list(Name);
- is_atom(Name) ->
- Name
- end,
- Header = lists:concat(["Status for state machine ", NameTag]),
+ StatusHdr = "Status for state machine",
+ Header = if
+ is_pid(Name) ->
+ lists:concat([StatusHdr, " ", pid_to_list(Name)]);
+ is_atom(Name); is_list(Name) ->
+ lists:concat([StatusHdr, " ", Name]);
+ true ->
+ {StatusHdr, Name}
+ end,
Log = sys:get_debug(log, Debug, []),
- Specfic =
+ DefaultStatus = [{data, [{"StateData", StateData}]}],
+ Specfic =
case erlang:function_exported(Mod, format_status, 2) of
true ->
case catch Mod:format_status(Opt,[PDict,StateData]) of
- {'EXIT', _} -> [{data, [{"StateData", StateData}]}];
- Else -> Else
+ {'EXIT', _} -> DefaultStatus;
+ StatusList when is_list(StatusList) -> StatusList;
+ Else -> [Else]
end;
_ ->
- [{data, [{"StateData", StateData}]}]
+ DefaultStatus
end,
[{header, Header},
{data, [{"Status", SysState},
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index 1c9e5270b6..ac81df9cab 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -705,7 +705,18 @@ terminate(Reason, Name, Msg, Mod, State, Debug) ->
{shutdown,_}=Shutdown ->
exit(Shutdown);
_ ->
- error_info(Reason, Name, Msg, State, Debug),
+ FmtState =
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ Args = [get(), State],
+ case catch Mod:format_status(terminate, Args) of
+ {'EXIT', _} -> State;
+ Else -> Else
+ end;
+ _ ->
+ State
+ end,
+ error_info(Reason, Name, Msg, FmtState, Debug),
exit(Reason)
end
end.
@@ -829,22 +840,27 @@ name_to_pid(Name) ->
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
- NameTag = if is_pid(Name) ->
- pid_to_list(Name);
- is_atom(Name) ->
- Name
- end,
- Header = lists:concat(["Status for generic server ", NameTag]),
+ StatusHdr = "Status for generic server",
+ Header = if
+ is_pid(Name) ->
+ lists:concat([StatusHdr, " ", pid_to_list(Name)]);
+ is_atom(Name); is_list(Name) ->
+ lists:concat([StatusHdr, " ", Name]);
+ true ->
+ {StatusHdr, Name}
+ end,
Log = sys:get_debug(log, Debug, []),
- Specfic =
+ DefaultStatus = [{data, [{"State", State}]}],
+ Specfic =
case erlang:function_exported(Mod, format_status, 2) of
true ->
case catch Mod:format_status(Opt, [PDict, State]) of
- {'EXIT', _} -> [{data, [{"State", State}]}];
- Else -> Else
+ {'EXIT', _} -> DefaultStatus;
+ StatusList when is_list(StatusList) -> StatusList;
+ Else -> [Else]
end;
_ ->
- [{data, [{"State", State}]}]
+ DefaultStatus
end,
[{header, Header},
{data, [{"Status", SysState},
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index 1f8076e864..6aeb076a0b 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(io).
@@ -32,12 +32,15 @@
parse_erl_form/1,parse_erl_form/2,parse_erl_form/3]).
-export([request/1,request/2,requests/1,requests/2]).
+-export_type([device/0, format/0]).
%%-------------------------------------------------------------------------
-type device() :: atom() | pid().
-type prompt() :: atom() | string().
+-type error_description() :: term(). % Whatever the io-server sends.
+-type request_error() :: {'error',error_description()}.
%% XXX: Some uses of line() in this file may need to read erl_scan:location()
-type line() :: pos_integer().
@@ -52,26 +55,12 @@
to_tuple(T) when is_tuple(T) -> T;
to_tuple(T) -> {T}.
-%% Problem: the variables Other, Name and Args may collide with surrounding
-%% ones.
-%% Give extra args to macro, being the variables to use.
--define(O_REQUEST(Io, Request),
- case request(Io, Request) of
- {error, Reason} ->
- [Name | Args] = tuple_to_list(to_tuple(Request)),
- erlang:error(conv_reason(Name, Reason), [Name, Io | Args]);
- Other ->
- Other
- end).
-
o_request(Io, Request, Func) ->
case request(Io, Request) of
{error, Reason} ->
[_Name | Args] = tuple_to_list(to_tuple(Request)),
- {'EXIT',{undef,[_Current|Mfas]}} = (catch erlang:error(undef)),
- MFA = {io, Func, [Io | Args]},
- exit({conv_reason(Func, Reason),[MFA|Mfas]});
-% erlang:error(conv_reason(Name, Reason), [Name, Io | Args]);
+ {'EXIT',{get_stacktrace,[_Current|Mfas]}} = (catch erlang:error(get_stacktrace)),
+ erlang:raise(error, conv_reason(Func, Reason), [{io, Func, [Io | Args]}|Mfas]);
Other ->
Other
end.
@@ -298,32 +287,32 @@ format(Io, Format, Args) ->
%% Scanning Erlang code.
--spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result().
+-spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error().
scan_erl_exprs(Prompt) ->
scan_erl_exprs(default_input(), Prompt, 1).
--spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result().
+-spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error().
scan_erl_exprs(Io, Prompt) ->
scan_erl_exprs(Io, Prompt, 1).
--spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result().
+-spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error().
scan_erl_exprs(Io, Prompt, Pos0) ->
request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}).
--spec scan_erl_form(prompt()) -> erl_scan:tokens_result().
+-spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error().
scan_erl_form(Prompt) ->
scan_erl_form(default_input(), Prompt, 1).
--spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result().
+-spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error().
scan_erl_form(Io, Prompt) ->
scan_erl_form(Io, Prompt, 1).
--spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result().
+-spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error().
scan_erl_form(Io, Prompt, Pos0) ->
request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}).
@@ -334,7 +323,8 @@ scan_erl_form(Io, Prompt, Pos0) ->
-type parse_ret() :: {'ok', erl_parse_expr_list(), line()}
| {'eof', line()}
- | {'error', erl_scan:error_info(), line()}.
+ | {'error', erl_scan:error_info(), line()}
+ | request_error().
-spec parse_erl_exprs(prompt()) -> parse_ret().
@@ -363,7 +353,8 @@ parse_erl_exprs(Io, Prompt, Pos0) ->
-type parse_form_ret() :: {'ok', erl_parse_absform(), line()}
| {'eof', line()}
- | {'error', erl_scan:error_info(), line()}.
+ | {'error', erl_scan:error_info(), line()}
+ | request_error().
-spec parse_erl_form(prompt()) -> parse_form_ret().
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 26f6ec8931..4ca9d079b7 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -75,6 +75,8 @@
collect_line/2, collect_line/3, collect_line/4,
get_until/3, get_until/4]).
+-export_type([chars/0]).
+
%%----------------------------------------------------------------------
%% XXX: overapproximates a deep list of (unicode) characters
diff --git a/lib/stdlib/src/io_lib_format.erl b/lib/stdlib/src/io_lib_format.erl
index eb1885021d..49a00a4ec7 100644
--- a/lib/stdlib/src/io_lib_format.erl
+++ b/lib/stdlib/src/io_lib_format.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -558,28 +558,30 @@ iolist_to_chars(B) when is_binary(B) ->
string(S, none, _Adj, none, _Pad) -> S;
string(S, F, Adj, none, Pad) ->
- N = lists:flatlength(S),
- if N > F -> flat_trunc(S, F);
- N =:= F -> S;
- true -> adjust(S, chars(Pad, F-N), Adj)
- end;
+ string_field(S, F, Adj, lists:flatlength(S), Pad);
string(S, none, _Adj, P, Pad) ->
+ string_field(S, P, left, lists:flatlength(S), Pad);
+string(S, F, Adj, P, Pad) when F >= P ->
N = lists:flatlength(S),
- if N > P -> flat_trunc(S, P);
- N =:= P -> S;
- true -> [S|chars(Pad, P-N)]
- end;
-string(S, F, Adj, F, Pad) ->
- string(S, none, Adj, F, Pad);
-string(S, F, Adj, P, Pad) when F > P ->
- N = lists:flatlength(S),
- if N > F -> flat_trunc(S, F);
- N =:= F -> S;
- N > P -> adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
- N =:= P -> adjust(S, chars(Pad, F-P), Adj);
- true -> adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj)
+ if F > P ->
+ if N > P ->
+ adjust(flat_trunc(S, P), chars(Pad, F-P), Adj);
+ N < P ->
+ adjust([S|chars(Pad, P-N)], chars(Pad, F-P), Adj);
+ true -> % N == P
+ adjust(S, chars(Pad, F-P), Adj)
+ end;
+ true -> % F == P
+ string_field(S, F, Adj, N, Pad)
end.
+string_field(S, F, _Adj, N, _Pad) when N > F ->
+ flat_trunc(S, F);
+string_field(S, F, Adj, N, Pad) when N < F ->
+ adjust(S, chars(Pad, F-N), Adj);
+string_field(S, _, _, _, _) -> % N == F
+ S.
+
%% unprefixed_integer(Int, Field, Adjust, Base, PadChar, Lowercase)
%% -> [Char].
@@ -624,8 +626,8 @@ newline(F, right, _P, _Pad) -> chars($\n, F).
%%
adjust(Data, [], _) -> Data;
-adjust(Data, Pad, left) -> [Data,Pad];
-adjust(Data, Pad, right) -> [Pad,Data].
+adjust(Data, Pad, left) -> [Data|Pad];
+adjust(Data, Pad, right) -> [Pad|Data].
%% Flatten and truncate a deep list to at most N elements.
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index 74316dc730..33553692bc 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(io_lib_fread).
@@ -22,6 +22,8 @@
-export([fread/2,fread/3]).
+-export_type([continuation/0, fread_2_ret/0, fread_3_ret/0]).
+
-import(lists, [reverse/1,reverse/2]).
%%-----------------------------------------------------------------------
diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl
index 857eda8161..c669c1f7c1 100644
--- a/lib/stdlib/src/lists.erl
+++ b/lib/stdlib/src/lists.erl
@@ -18,6 +18,9 @@
%%
-module(lists).
+-compile({no_auto_import,[max/2]}).
+-compile({no_auto_import,[min/2]}).
+
-export([append/2, append/1, subtract/2, reverse/1,
nth/2, nthtail/2, prefix/2, suffix/2, last/1,
seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3,
@@ -347,7 +350,7 @@ sort_1(X, [], R) ->
%% merge(List) -> L
%% merges a list of sorted lists
--spec merge([T]) -> [T].
+-spec merge([[T]]) -> [T].
merge(L) ->
mergel(L, []).
@@ -355,7 +358,7 @@ merge(L) ->
%% merge3(X, Y, Z) -> L
%% merges three sorted lists X, Y and Z
--spec merge3([_], [_], [_]) -> [_].
+-spec merge3([X], [Y], [Z]) -> [(X | Y | Z)].
merge3(L1, [], L3) ->
merge(L1, L3);
@@ -367,7 +370,7 @@ merge3(L1, [H2 | T2], [H3 | T3]) ->
%% rmerge3(X, Y, Z) -> L
%% merges three reversed sorted lists X, Y and Z
--spec rmerge3([_], [_], [_]) -> [_].
+-spec rmerge3([X], [Y], [Z]) -> [(X | Y | Z)].
rmerge3(L1, [], L3) ->
rmerge(L1, L3);
@@ -379,7 +382,7 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) ->
%% merge(X, Y) -> L
%% merges two sorted lists X and Y
--spec merge([_], [_]) -> [_].
+-spec merge([X], [Y]) -> [(X | Y)].
merge(T1, []) ->
T1;
@@ -391,7 +394,7 @@ merge(T1, [H2 | T2]) ->
%% reverse(rmerge(reverse(A),reverse(B))) is equal to merge(I,A,B).
--spec rmerge([_], [_]) -> [_].
+-spec rmerge([X], [Y]) -> [(X | Y)].
rmerge(T1, []) ->
T1;
@@ -417,12 +420,12 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string
%% flatten(List, Tail)
%% Flatten a list, adding optional tail.
--spec flatten([_]) -> [_].
+-spec flatten([term()]) -> [term()].
flatten(List) when is_list(List) ->
do_flatten(List, []).
--spec flatten([_], [_]) -> [_].
+-spec flatten([term()], [term()]) -> [term()].
flatten(List, Tail) when is_list(List), is_list(Tail) ->
do_flatten(List, Tail).
@@ -437,7 +440,7 @@ do_flatten([], Tail) ->
%% flatlength(List)
%% Calculate the length of a list of lists.
--spec flatlength([_]) -> non_neg_integer().
+-spec flatlength([term()]) -> non_neg_integer().
flatlength(List) ->
flatlength(List, 0).
@@ -478,7 +481,7 @@ flatlength([], L) -> L.
% keysearch3(Key, N, T);
%keysearch3(Key, N, []) -> false.
--spec keydelete(_, pos_integer(), [T]) -> [T].
+-spec keydelete(term(), pos_integer(), [T]) -> [T] when T :: tuple().
keydelete(K, N, L) when is_integer(N), N > 0 ->
keydelete3(K, N, L).
@@ -488,7 +491,7 @@ keydelete3(Key, N, [H|T]) ->
[H|keydelete3(Key, N, T)];
keydelete3(_, _, []) -> [].
--spec keyreplace(_, pos_integer(), [_], tuple()) -> [_].
+-spec keyreplace(term(), pos_integer(), [tuple()], tuple()) -> [tuple()].
keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
keyreplace3(K, N, L, New).
@@ -499,7 +502,8 @@ keyreplace3(Key, Pos, [H|T], New) ->
[H|keyreplace3(Key, Pos, T, New)];
keyreplace3(_, _, [], _) -> [].
--spec keytake(_, pos_integer(), [_]) -> {'value', tuple(), [_]} | 'false'.
+-spec keytake(term(), pos_integer(), [tuple()]) ->
+ {'value', tuple(), [tuple()]} | 'false'.
keytake(Key, N, L) when is_integer(N), N > 0 ->
keytake(Key, N, L, []).
@@ -510,7 +514,8 @@ keytake(Key, N, [H|T], L) ->
keytake(Key, N, T, [H|L]);
keytake(_K, _N, [], _L) -> false.
--spec keystore(_, pos_integer(), [_], tuple()) -> [_].
+-spec keystore(term(), pos_integer(), [tuple()], tuple()) -> [tuple(),...].
+
keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
keystore2(K, N, L, New).
@@ -521,7 +526,7 @@ keystore2(Key, N, [H|T], New) ->
keystore2(_Key, _N, [], New) ->
[New].
--spec keysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()).
+-spec keysort(pos_integer(), [T]) -> [T] when T :: tuple().
keysort(I, L) when is_integer(I), I > 0 ->
case L of
@@ -579,7 +584,7 @@ keysort_1(_I, X, _EX, [], R) ->
lists:reverse(R, [X]).
-spec keymerge(pos_integer(), [X], [Y]) ->
- [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()).
+ [R] when X :: tuple(), Y :: tuple(), R :: tuple().
keymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -594,7 +599,7 @@ keymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
%% reverse(rkeymerge(I,reverse(A),reverse(B))) is equal to keymerge(I,A,B).
-spec rkeymerge(pos_integer(), [X], [Y]) ->
- [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()).
+ [R] when X :: tuple(), Y :: tuple(), R :: tuple().
rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -606,7 +611,7 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
lists:reverse(M, [])
end.
--spec ukeysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()).
+-spec ukeysort(pos_integer(), [T]) -> [T] when T :: tuple().
ukeysort(I, L) when is_integer(I), I > 0 ->
case L of
@@ -672,7 +677,7 @@ ukeysort_1(_I, X, _EX, []) ->
[X].
-spec ukeymerge(pos_integer(), [X], [Y]) ->
- [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()).
+ [(X | Y)] when X :: tuple(), Y :: tuple().
ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 ->
case L1 of
@@ -687,7 +692,7 @@ ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 ->
%% reverse(rukeymerge(I,reverse(A),reverse(B))) is equal to ukeymerge(I,A,B).
-spec rukeymerge(pos_integer(), [X], [Y]) ->
- [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()).
+ [(X | Y)] when X :: tuple(), Y :: tuple().
rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
case L2 of
@@ -699,7 +704,7 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 ->
lists:reverse(M, [])
end.
--spec keymap(fun((_) -> _), pos_integer(), [tuple()]) -> [tuple()].
+-spec keymap(fun((term()) -> term()), pos_integer(), [tuple()]) -> [tuple()].
keymap(Fun, Index, [Tup|Tail]) ->
[setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)];
@@ -722,7 +727,7 @@ sort(Fun, [X, Y | T]) ->
fsplit_2(Y, X, Fun, T, [], [])
end.
--spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) ->
lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []);
@@ -731,7 +736,7 @@ merge(Fun, T1, []) when is_function(Fun, 2) ->
%% reverse(rmerge(F,reverse(A),reverse(B))) is equal to merge(F,A,B).
--spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) ->
lists:reverse(rfmerge2_1(T1, H2, Fun, T2, []), []);
@@ -765,7 +770,7 @@ usort_1(Fun, X, [Y | L]) ->
ufsplit_2(Y, L, Fun, [X])
end.
--spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
umerge(Fun, [], T2) when is_function(Fun, 2) ->
T2;
@@ -774,7 +779,7 @@ umerge(Fun, [H1 | T1], T2) when is_function(Fun, 2) ->
%% reverse(rumerge(F,reverse(A),reverse(B))) is equal to umerge(F,A,B).
--spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_].
+-spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)].
rumerge(Fun, T1, []) when is_function(Fun, 2) ->
T1;
@@ -839,7 +844,7 @@ usort_1(X, []) ->
%% umerge(List) -> L
%% merges a list of sorted lists without duplicates, removes duplicates
--spec umerge([T]) -> [T].
+-spec umerge([[T]]) -> [T].
umerge(L) ->
umergel(L).
@@ -848,7 +853,7 @@ umerge(L) ->
%% merges three sorted lists X, Y and Z without duplicates,
%% removes duplicates
--spec umerge3([_], [_], [_]) -> [_].
+-spec umerge3([X], [Y], [Z]) -> [(X | Y | Z)].
umerge3(L1, [], L3) ->
umerge(L1, L3);
@@ -861,7 +866,7 @@ umerge3(L1, [H2 | T2], [H3 | T3]) ->
%% merges three reversed sorted lists X, Y and Z without duplicates,
%% removes duplicates
--spec rumerge3([_], [_], [_]) -> [_].
+-spec rumerge3([X], [Y], [Z]) -> [(X | Y | Z)].
rumerge3(L1, [], L3) ->
rumerge(L1, L3);
@@ -873,7 +878,7 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) ->
%% umerge(X, Y) -> L
%% merges two sorted lists X and Y without duplicates, removes duplicates
--spec umerge([_], [_]) -> [_].
+-spec umerge([X], [Y]) -> [(X | Y)].
umerge([], T2) ->
T2;
@@ -886,7 +891,7 @@ umerge([H1 | T1], T2) ->
%% reverse(rumerge(reverse(A),reverse(B))) is equal to umerge(I,A,B).
--spec rumerge([_], [_]) -> [_].
+-spec rumerge([X], [Y]) -> [(X | Y)].
rumerge(T1, []) ->
T1;
@@ -949,13 +954,13 @@ flatmap(F, [Hd|Tail]) ->
F(Hd) ++ flatmap(F, Tail);
flatmap(F, []) when is_function(F, 1) -> [].
--spec foldl(fun((T, _) -> _), _, [T]) -> _.
+-spec foldl(fun((T, term()) -> term()), term(), [T]) -> term().
foldl(F, Accu, [Hd|Tail]) ->
foldl(F, F(Hd, Accu), Tail);
foldl(F, Accu, []) when is_function(F, 2) -> Accu.
--spec foldr(fun((T, _) -> _), _, [T]) -> _.
+-spec foldr(fun((T, term()) -> term()), term(), [T]) -> term().
foldr(F, Accu, [Hd|Tail]) ->
F(Hd, foldr(F, Accu, Tail));
@@ -995,14 +1000,14 @@ zf(F, [Hd|Tail]) ->
end;
zf(F, []) when is_function(F, 1) -> [].
--spec foreach(F :: fun((T) -> _), List :: [T]) -> 'ok'.
+-spec foreach(F :: fun((T) -> term()), List :: [T]) -> 'ok'.
foreach(F, [Hd|Tail]) ->
F(Hd),
foreach(F, Tail);
foreach(F, []) when is_function(F, 1) -> ok.
--spec mapfoldl(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}.
+-spec mapfoldl(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}.
mapfoldl(F, Accu0, [Hd|Tail]) ->
{R,Accu1} = F(Hd, Accu0),
@@ -1010,7 +1015,7 @@ mapfoldl(F, Accu0, [Hd|Tail]) ->
{[R|Rs],Accu2};
mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}.
--spec mapfoldr(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}.
+-spec mapfoldr(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}.
mapfoldr(F, Accu0, [Hd|Tail]) ->
{Rs,Accu1} = mapfoldr(F, Accu0, Tail),
diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl
index 2729f27e51..5fa5360fa1 100644
--- a/lib/stdlib/src/log_mf_h.erl
+++ b/lib/stdlib/src/log_mf_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -185,13 +185,19 @@ read_index_file(Dir) ->
%%-----------------------------------------------------------------
%% Write the index file. This file contains one binary with
%% the last used filename (an integer).
+%% Write a temporary file and rename it in order to make the update
+%% atomic.
%%-----------------------------------------------------------------
write_index_file(Dir, Index) ->
- case file:open(Dir ++ "/index", [raw, write]) of
+ File = Dir ++ "/index",
+ TmpFile = File ++ ".tmp",
+ case file:open(TmpFile, [raw, write]) of
{ok, Fd} ->
- file:write(Fd, [Index]),
- ok = file:close(Fd);
+ ok = file:write(Fd, [Index]),
+ ok = file:close(Fd),
+ ok = file:rename(TmpFile,File),
+ ok;
_ -> exit(open_index_file)
end.
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 78b1de6e16..b565eb20f4 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,6 +43,7 @@
-define(ERR_GENREMOTECALL,22).
-define(ERR_GENBINCONSTRUCT,23).
-define(ERR_GENDISALLOWEDOP,24).
+-define(WARN_SHADOW_VAR,50).
-define(ERR_GUARDMATCH,?ERR_GENMATCH+?ERROR_BASE_GUARD).
-define(ERR_BODYMATCH,?ERR_GENMATCH+?ERROR_BASE_BODY).
-define(ERR_GUARDLOCALCALL,?ERR_GENLOCALCALL+?ERROR_BASE_GUARD).
@@ -63,8 +64,13 @@
-define(ERR_BODYDISALLOWEDOP,?ERR_GENDISALLOWEDOP+?ERROR_BASE_BODY).
%%
-%% Called by compiler or ets/dbg:fun2ms when errors occur
+%% Called by compiler or ets/dbg:fun2ms when errors/warnings occur
%%
+format_error({?WARN_SHADOW_VAR,Name}) ->
+ lists:flatten(
+ io_lib:format("variable ~p shadowed in ms_transform fun head",
+ [Name]));
+
format_error(?ERR_NOFUN) ->
"Parameter of ets/dbg:fun2ms/1 is not a literal fun";
format_error(?ERR_ETS_HEAD) ->
@@ -182,7 +188,7 @@ format_error(Else) ->
%%
transform_from_shell(Dialect, Clauses, BoundEnvironment) ->
SaveFilename = setup_filename(),
- case catch ms_clause_list(1,Clauses,Dialect) of
+ case catch ms_clause_list(1,Clauses,Dialect,gb_sets:new()) of
{'EXIT',Reason} ->
cleanup_filename(SaveFilename),
exit(Reason);
@@ -207,6 +213,7 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) ->
%%
parse_transform(Forms, _Options) ->
SaveFilename = setup_filename(),
+ %io:format("Forms: ~p~n",[Forms]),
case catch forms(Forms) of
{'EXIT',Reason} ->
cleanup_filename(SaveFilename),
@@ -215,12 +222,31 @@ parse_transform(Forms, _Options) ->
{error, [{cleanup_filename(SaveFilename),
[{Line, ?MODULE, R}]}], []};
Else ->
- cleanup_filename(SaveFilename),
+ %io:format("Transformed into: ~p~n",[Else]),
+ case get_warnings() of
+ [] ->
+ cleanup_filename(SaveFilename),
+ Else;
+ WL ->
+ FName = cleanup_filename(SaveFilename) ,
+ WList = [ {FName, [{L, ?MODULE, R}]} || {L,R} <- WL ],
+ {warning, Else, WList}
+ end
+ end.
+
+get_warnings() ->
+ case get(warnings) of
+ undefined ->
+ [];
+ Else ->
Else
end.
+add_warning(Line,R) ->
+ put(warnings,[{Line,R}| get_warnings()]).
+
setup_filename() ->
- {erase(filename),erase(records)}.
+ {erase(filename),erase(records),erase(warnings)}.
put_filename(Name) ->
put(filename,Name).
@@ -235,7 +261,7 @@ get_records() ->
Else ->
Else
end.
-cleanup_filename({Old,OldRec}) ->
+cleanup_filename({Old,OldRec,OldWarnings}) ->
Ret = case erase(filename) of
undefined ->
"TOP_LEVEL";
@@ -248,6 +274,12 @@ cleanup_filename({Old,OldRec}) ->
Rec ->
put(records,Rec)
end,
+ case OldWarnings of
+ undefined ->
+ erase(warnings);
+ Warn ->
+ put(warnings,Warn)
+ end,
case Old of
undefined ->
Ret;
@@ -285,42 +317,77 @@ form({function,Line,Name0,Arity0,Clauses0}) ->
form(AnyOther) ->
AnyOther.
function(Name, Arity, Clauses0) ->
- Clauses1 = clauses(Clauses0),
+ {Clauses1,_} = clauses(Clauses0,gb_sets:new()),
{Name,Arity,Clauses1}.
-clauses([C0|Cs]) ->
- C1 = clause(C0),
- [C1|clauses(Cs)];
-clauses([]) -> [].
-clause({clause,Line,H0,G0,B0}) ->
- B1 = copy(B0),
- {clause,Line,H0,G0,B1}.
+clauses([C0|Cs],Bound) ->
+ {C1,Bound1} = clause(C0,Bound),
+ {C2,Bound2} = clauses(Cs,Bound1),
+ {[C1|C2],Bound2};
+clauses([],Bound) -> {[],Bound}.
+clause({clause,Line,H0,G0,B0},Bound) ->
+ {H1,Bound1} = copy(H0,Bound),
+ {B1,Bound2} = copy(B0,Bound1),
+ {{clause,Line,H1,G0,B1},Bound2}.
copy({call,Line,{remote,_Line2,{atom,_Line3,ets},{atom,_Line4,fun2ms}},
- As0}) ->
- transform_call(ets,Line,As0);
+ As0},Bound) ->
+ {transform_call(ets,Line,As0,Bound),Bound};
copy({call,Line,{remote,_Line2,{record_field,_Line3,
{atom,_Line4,''},{atom,_Line5,ets}},
- {atom,_Line6,fun2ms}}, As0}) ->
+ {atom,_Line6,fun2ms}}, As0},Bound) ->
%% Packages...
- transform_call(ets,Line,As0);
+ {transform_call(ets,Line,As0,Bound),Bound};
copy({call,Line,{remote,_Line2,{atom,_Line3,dbg},{atom,_Line4,fun2ms}},
- As0}) ->
- transform_call(dbg,Line,As0);
-copy(T) when is_tuple(T) ->
- list_to_tuple(copy_list(tuple_to_list(T)));
-copy(L) when is_list(L) ->
- copy_list(L);
-copy(AnyOther) ->
- AnyOther.
+ As0},Bound) ->
+ {transform_call(dbg,Line,As0,Bound),Bound};
+copy({match,Line,A,B},Bound) ->
+ {B1,Bound1} = copy(B,Bound),
+ {A1,Bound2} = copy(A,Bound),
+ {{match,Line,A1,B1},gb_sets:union(Bound1,Bound2)};
+copy({var,_Line,'_'} = VarDef,Bound) ->
+ {VarDef,Bound};
+copy({var,_Line,Name} = VarDef,Bound) ->
+ Bound1 = gb_sets:add(Name,Bound),
+ {VarDef,Bound1};
+copy({'fun',Line,{clauses,Clauses}},Bound) -> % Dont export bindings from funs
+ {NewClauses,_IgnoredBindings} = copy_list(Clauses,Bound),
+ {{'fun',Line,{clauses,NewClauses}},Bound};
+copy({'case',Line,Of,ClausesList},Bound) -> % Dont export bindings from funs
+ {NewOf,NewBind0} = copy(Of,Bound),
+ {NewClausesList,NewBindings} = copy_case_clauses(ClausesList,NewBind0,[]),
+ {{'case',Line,NewOf,NewClausesList},NewBindings};
+copy(T,Bound) when is_tuple(T) ->
+ {L,Bound1} = copy_list(tuple_to_list(T),Bound),
+ {list_to_tuple(L),Bound1};
+copy(L,Bound) when is_list(L) ->
+ copy_list(L,Bound);
+copy(AnyOther,Bound) ->
+ {AnyOther,Bound}.
-copy_list([H|T]) ->
- [copy(H)|copy_list(T)];
-copy_list([]) ->
- [].
+copy_case_clauses([],Bound,AddSets) ->
+ ReallyAdded = gb_sets:intersection(AddSets),
+ {[],gb_sets:union(Bound,ReallyAdded)};
+copy_case_clauses([{clause,Line,Match,Guard,Clauses}|T],Bound,AddSets) ->
+ {NewMatch,MatchBinds} = copy(Match,Bound),
+ {NewGuard,GuardBinds} = copy(Guard,MatchBinds), %% Really no new binds
+ {NewClauses,AllBinds} = copy(Clauses,GuardBinds),
+ %% To limit the setsizes, I subtract what I had before the case clause
+ %% and add it in the end
+ AddedBinds = gb_sets:subtract(AllBinds,Bound),
+ {NewTail,ExportedBindings} =
+ copy_case_clauses(T,Bound,[AddedBinds | AddSets]),
+ {[{clause,Line,NewMatch,NewGuard,NewClauses}|NewTail],ExportedBindings}.
-transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}]) ->
- ms_clause_list(Line2, ClauseList,Type);
-transform_call(_Type,Line,_NoAbstractFun) ->
+copy_list([H|T],Bound) ->
+ {C1,Bound1} = copy(H,Bound),
+ {C2,Bound2} = copy_list(T,Bound1),
+ {[C1|C2],Bound2};
+copy_list([],Bound) ->
+ {[],Bound}.
+
+transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}],Bound) ->
+ ms_clause_list(Line2, ClauseList,Type,Bound);
+transform_call(_Type,Line,_NoAbstractFun,_) ->
throw({error,Line,?ERR_NOFUN}).
% Fixup semicolons in guards
@@ -329,18 +396,19 @@ ms_clause_expand({clause, Line, Parameters, Guard = [_,_|_], Body}) ->
ms_clause_expand(_Other) ->
false.
-ms_clause_list(Line,[H|T],Type) ->
+ms_clause_list(Line,[H|T],Type,Bound) ->
case ms_clause_expand(H) of
NewHead when is_list(NewHead) ->
- ms_clause_list(Line,NewHead ++ T, Type);
+ ms_clause_list(Line,NewHead ++ T, Type, Bound);
false ->
- {cons, Line, ms_clause(H,Type), ms_clause_list(Line, T,Type)}
+ {cons, Line, ms_clause(H, Type, Bound),
+ ms_clause_list(Line, T, Type, Bound)}
end;
-ms_clause_list(Line,[],_) ->
+ms_clause_list(Line,[],_,_) ->
{nil,Line}.
-ms_clause({clause, Line, Parameters, Guards, Body},Type) ->
+ms_clause({clause, Line, Parameters, Guards, Body},Type,Bound) ->
check_type(Line,Parameters,Type),
- {MSHead,Bindings} = transform_head(Parameters),
+ {MSHead,Bindings} = transform_head(Parameters,Bound),
MSGuards = transform_guards(Line, Guards, Bindings),
MSBody = transform_body(Line,Body,Bindings),
{tuple, Line, [MSHead,MSGuards,MSBody]}.
@@ -627,29 +695,31 @@ tg(Other,B) ->
Element = io_lib:format("unknown element ~w", [Other]),
throw({error,unknown,{?ERR_GENELEMENT+B#tgd.eb,Element}}).
-transform_head([V]) ->
+transform_head([V],OuterBound) ->
Bind = cre_bind(),
- {NewV,NewBind} = toplevel_head_match(V,Bind),
- th(NewV,NewBind).
+ {NewV,NewBind} = toplevel_head_match(V,Bind,OuterBound),
+ th(NewV,NewBind,OuterBound).
-toplevel_head_match({match,_,{var,_,VName},Expr},B) ->
+toplevel_head_match({match,Line,{var,_,VName},Expr},B,OB) ->
+ warn_var_clash(Line,VName,OB),
{Expr,new_bind({VName,'$_'},B)};
-toplevel_head_match({match,_,Expr,{var,_,VName}},B) ->
+toplevel_head_match({match,Line,Expr,{var,_,VName}},B,OB) ->
+ warn_var_clash(Line,VName,OB),
{Expr,new_bind({VName,'$_'},B)};
-toplevel_head_match(Other,B) ->
+toplevel_head_match(Other,B,_OB) ->
{Other,B}.
-th({record,Line,RName,RFields},B) ->
+th({record,Line,RName,RFields},B,OB) ->
% youch...
RDefs = get_records(),
{KeyList0,NewB} = lists:foldl(fun({record_field,_,{atom,_,Key},Value},
{L,B0}) ->
- {NV,B1} = th(Value,B0),
+ {NV,B1} = th(Value,B0,OB),
{[{Key,NV}|L],B1};
({record_field,_,{var,_,'_'},Value},
{L,B0}) ->
- {NV,B1} = th(Value,B0),
+ {NV,B1} = th(Value,B0,OB),
{[{{default},NV}|L],B1};
(_,_) ->
throw({error,Line,{?ERR_HEADBADREC,
@@ -692,9 +762,9 @@ th({record,Line,RName,RFields},B) ->
_ ->
throw({error,Line,{?ERR_HEADBADREC,RName}})
end;
-th({match,Line,_,_},_) ->
+th({match,Line,_,_},_,_) ->
throw({error,Line,?ERR_HEADMATCH});
-th({atom,Line,A},B) ->
+th({atom,Line,A},B,_OB) ->
case atom_to_list(A) of
[$$|NL] ->
case (catch list_to_integer(NL)) of
@@ -706,10 +776,11 @@ th({atom,Line,A},B) ->
_ ->
{{atom,Line,A},B}
end;
-th({bin_element,_Line0,{var, Line, A},_,_},_) ->
+th({bin_element,_Line0,{var, Line, A},_,_},_,_) ->
throw({error,Line,{?ERR_HEADBINMATCH,A}});
-th({var,Line,Name},B) ->
+th({var,Line,Name},B,OB) ->
+ warn_var_clash(Line,Name,OB),
case lkup_bind(Name,B) of
undefined ->
NewB = new_bind(Name,B),
@@ -717,16 +788,24 @@ th({var,Line,Name},B) ->
Trans ->
{{atom,Line,Trans},B}
end;
-th([H|T],B) ->
- {NH,NB} = th(H,B),
- {NT,NNB} = th(T,NB),
+th([H|T],B,OB) ->
+ {NH,NB} = th(H,B,OB),
+ {NT,NNB} = th(T,NB,OB),
{[NH|NT],NNB};
-th(T,B) when is_tuple(T) ->
- {L,NB} = th(tuple_to_list(T),B),
+th(T,B,OB) when is_tuple(T) ->
+ {L,NB} = th(tuple_to_list(T),B,OB),
{list_to_tuple(L),NB};
-th(Nonstruct,B) ->
+th(Nonstruct,B,_OB) ->
{Nonstruct,B}.
+warn_var_clash(Line,Name,OuterBound) ->
+ case gb_sets:is_member(Name,OuterBound) of
+ true ->
+ add_warning(Line,{?WARN_SHADOW_VAR,Name});
+ _ ->
+ ok
+ end.
+
%% Could be more efficient...
check_multi_field(_, _, [], _) ->
ok;
diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl
index c7b52b933e..4e30c9eefd 100644
--- a/lib/stdlib/src/orddict.erl
+++ b/lib/stdlib/src/orddict.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -151,7 +151,7 @@ map(F, [{Key,Val}|D]) ->
[{Key,F(Key, Val)}|map(F, D)];
map(F, []) when is_function(F, 2) -> [].
--spec filter(fun((term(), term()) -> term()), orddict()) -> orddict().
+-spec filter(fun((term(), term()) -> boolean()), orddict()) -> orddict().
filter(F, [{Key,Val}=E|D]) ->
case F(Key, Val) of
diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl
index 05041c15f1..5a1c260703 100644
--- a/lib/stdlib/src/ordsets.erl
+++ b/lib/stdlib/src/ordsets.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,12 +26,14 @@
-export([subtract/2,is_subset/2]).
-export([fold/3,filter/2]).
+-export_type([ordset/1]).
+
-type ordset(T) :: [T].
%% new() -> Set.
%% Return a new empty ordered set.
--spec new() -> ordset(term()).
+-spec new() -> [].
new() -> [].
@@ -84,7 +86,7 @@ is_element(_, []) -> false.
%% add_element(Element, OrdSet) -> OrdSet.
%% Return OrdSet with Element inserted in it.
--spec add_element(term(), ordset(_)) -> ordset(_).
+-spec add_element(E, ordset(T)) -> [T | E,...].
add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)];
add_element(E, [H|_]=Set) when E < H -> [E|Set];
@@ -94,7 +96,7 @@ add_element(E, []) -> [E].
%% del_element(Element, OrdSet) -> OrdSet.
%% Return OrdSet but with Element removed.
--spec del_element(term(), ordset(_)) -> ordset(_).
+-spec del_element(term(), ordset(T)) -> ordset(T).
del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)];
del_element(E, [H|_]=Set) when E < H -> Set;
@@ -104,7 +106,7 @@ del_element(_, []) -> [].
%% union(OrdSet1, OrdSet2) -> OrdSet
%% Return the union of OrdSet1 and OrdSet2.
--spec union(ordset(_), ordset(_)) -> ordset(_).
+-spec union(ordset(T1), ordset(T2)) -> ordset(T1 | T2).
union([E1|Es1], [E2|_]=Set2) when E1 < E2 ->
[E1|union(Es1, Set2)];
@@ -118,7 +120,7 @@ union(Es1, []) -> Es1.
%% union([OrdSet]) -> OrdSet
%% Return the union of the list of ordered sets.
--spec union([ordset(_)]) -> ordset(_).
+-spec union([ordset(T)]) -> ordset(T).
union([S1,S2|Ss]) ->
union1(union(S1, S2), Ss);
@@ -147,7 +149,7 @@ intersection(_, []) ->
%% intersection([OrdSet]) -> OrdSet.
%% Return the intersection of the list of ordered sets.
--spec intersection([ordset(_)]) -> ordset(_).
+-spec intersection([ordset(_),...]) -> ordset(_).
intersection([S1,S2|Ss]) ->
intersection1(intersection(S1, S2), Ss);
@@ -206,7 +208,7 @@ is_subset(_, []) -> false.
%% fold(Fun, Accumulator, OrdSet) -> Accumulator.
%% Fold function Fun over all elements in OrdSet and return Accumulator.
--spec fold(fun((_, _) -> _), _, ordset(_)) -> _.
+-spec fold(fun((T, term()) -> term()), term(), ordset(T)) -> term().
fold(F, Acc, Set) ->
lists:foldl(F, Acc, Set).
@@ -214,7 +216,7 @@ fold(F, Acc, Set) ->
%% filter(Fun, OrdSet) -> OrdSet.
%% Filter OrdSet with Fun.
--spec filter(fun((_) -> boolean()), ordset(_)) -> ordset(_).
+-spec filter(fun((T) -> boolean()), ordset(T)) -> ordset(T).
filter(F, Set) ->
lists:filter(F, Set).
diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl
index f3cfd78d54..5c52dfcbf0 100644
--- a/lib/stdlib/src/otp_internal.erl
+++ b/lib/stdlib/src/otp_internal.erl
@@ -349,14 +349,24 @@ obsolete_1(ssh_sftp, stop, 1) ->
%% Added in R13B01.
obsolete_1(ssl_pkix, decode_cert_file, A) when A =:= 1; A =:= 2 ->
- {deprecated,"deprecated (will be removed in R14B); use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"};
+ {removed,"removed in R14A; use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"};
obsolete_1(ssl_pkix, decode_cert, A) when A =:= 1; A =:= 2 ->
- {deprecated,{public_key,pkix_decode_cert,2},"R14B"};
+ {removed,{public_key,pkix_decode_cert,2},"R14A"};
%% Added in R13B04.
obsolete_1(erlang, concat_binary, 1) ->
{deprecated,{erlang,list_to_binary,1},"R15B"};
-
+
+%% Added in R14A.
+obsolete_1(ssl, peercert, 2) ->
+ {deprecated,"deprecated (will be removed in R15A); use ssl:peercert/1 and public_key:pkix_decode_cert/2 instead"};
+
+%% Added in R14B.
+obsolete_1(public_key, pem_to_der, 1) ->
+ {deprecated,"deprecated (will be removed in R15A); use file:read_file/1 and public_key:pem_decode/1"};
+obsolete_1(public_key, decode_private_key, A) when A =:= 1; A =:= 2 ->
+ {deprecated,{public_key,pem_entry_decode,1},"R15A"};
+
obsolete_1(_, _, _) ->
no.
diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl
index 9aa5e0a71e..4fb64a3353 100644
--- a/lib/stdlib/src/proc_lib.erl
+++ b/lib/stdlib/src/proc_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(proc_lib).
@@ -34,6 +34,8 @@
%% Internal exports.
-export([wake_up/3]).
+-export_type([spawn_option/0]).
+
%%-----------------------------------------------------------------------------
-type priority_level() :: 'high' | 'low' | 'max' | 'normal'.
diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl
index 35d14891f1..6a45e0f868 100644
--- a/lib/stdlib/src/proplists.erl
+++ b/lib/stdlib/src/proplists.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% =====================================================================
@@ -49,6 +49,8 @@
%% ---------------------------------------------------------------------
+-export_type([property/0]).
+
-type property() :: atom() | tuple().
-type aliases() :: [{any(), any()}].
diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl
index 6e48d95973..bc6944e520 100644
--- a/lib/stdlib/src/qlc.erl
+++ b/lib/stdlib/src/qlc.erl
@@ -24,6 +24,8 @@
%% External exports
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([parse_transform/2, transform_from_evaluator/2]).
-export([q/1, q/2]).
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index 296a6b3d23..e2cc9f57ce 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,16 +37,16 @@ split(Subject,RE,Options) ->
{error,_Err} ->
throw(badre);
{PreCompiled, NumSub, RunOpt} ->
- % OK, lets run
+ %% OK, lets run
case re:run(FlatSubject,PreCompiled,RunOpt ++ [global]) of
nomatch ->
case Group of
true ->
convert_any_split_result([[FlatSubject]],
- Convert, Unicode,true);
+ Convert, Unicode, true);
false ->
convert_any_split_result([FlatSubject],
- Convert, Unicode,false)
+ Convert, Unicode, false)
end;
{match, Matches} ->
Res = do_split(FlatSubject, 0, Matches, NumSub,
@@ -69,7 +69,7 @@ split(Subject,RE,Options) ->
erlang:error(badarg,[Subject,RE,Options])
end.
-backstrip_empty(List,false) ->
+backstrip_empty(List, false) ->
do_backstrip_empty(List);
backstrip_empty(List, true) ->
do_backstrip_empty_g(List).
@@ -196,41 +196,36 @@ compile_split(Pat,Options0) when not is_tuple(Pat) ->
end;
compile_split(_,_) ->
throw(badre).
-
-
replace(Subject,RE,Replacement) ->
replace(Subject,RE,Replacement,[]).
+
replace(Subject,RE,Replacement,Options) ->
try
{NewOpt,Convert,Unicode} =
process_repl_params(Options,iodata,false),
FlatSubject = to_binary(Subject, Unicode),
FlatReplacement = to_binary(Replacement, Unicode),
- case do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt) of
- {error,_Err} ->
- throw(badre);
- IoList ->
- case Convert of
- iodata ->
- IoList;
- binary ->
- case Unicode of
- false ->
- iolist_to_binary(IoList);
- true ->
- unicode:characters_to_binary(IoList,unicode)
- end;
- list ->
- case Unicode of
- false ->
- binary_to_list(iolist_to_binary(IoList));
- true ->
- unicode:characters_to_list(IoList,unicode)
- end
- end
- end
+ IoList = do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt),
+ case Convert of
+ iodata ->
+ IoList;
+ binary ->
+ case Unicode of
+ false ->
+ iolist_to_binary(IoList);
+ true ->
+ unicode:characters_to_binary(IoList,unicode)
+ end;
+ list ->
+ case Unicode of
+ false ->
+ binary_to_list(iolist_to_binary(IoList));
+ true ->
+ unicode:characters_to_list(IoList,unicode)
+ end
+ end
catch
throw:badopt ->
erlang:error(badarg,[Subject,RE,Replacement,Options]);
@@ -239,7 +234,7 @@ replace(Subject,RE,Replacement,Options) ->
error:badarg ->
erlang:error(badarg,[Subject,RE,Replacement,Options])
end.
-
+
do_replace(FlatSubject,Subject,RE,Replacement,Options) ->
case re:run(FlatSubject,RE,Options) of
@@ -314,7 +309,7 @@ apply_mlist(Subject,Replacement,Mlist) ->
precomp_repl(<<>>) ->
[];
precomp_repl(<<$\\,X,Rest/binary>>) when X < $1 ; X > $9 ->
- % Escaped character
+ %% Escaped character
case precomp_repl(Rest) of
[BHead | T0] when is_binary(BHead) ->
[<<X,BHead/binary>> | T0];
@@ -524,7 +519,7 @@ process_uparams([H|T],Type) ->
{[H|NL],NType};
process_uparams([],Type) ->
{[],Type}.
-
+
ucompile(RE,Options) ->
try
@@ -548,6 +543,7 @@ urun(Subject,RE,Options) ->
[Subject,RE,Options])),
erlang:raise(error,AnyError,[{Mod,run,L}|Rest])
end.
+
urun2(Subject0,RE0,Options0) ->
{Options,RetType} = case (catch process_uparams(Options0,index)) of
{A,B} ->
@@ -573,7 +569,6 @@ urun2(Subject0,RE0,Options0) ->
_ ->
Ret
end.
-
%% Might be called either with two-tuple (if regexp was already compiled)
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index 3e52c48e42..9d15f01683 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -1,20 +1,20 @@
%% This is an -*- erlang -*- file.
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
{application, stdlib,
@@ -23,6 +23,7 @@
{modules, [array,
base64,
beam_lib,
+ binary,
c,
calendar,
dets,
diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl
index 6636a03f06..264348180f 100644
--- a/lib/stdlib/src/string.erl
+++ b/lib/stdlib/src/string.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -201,7 +201,7 @@ chars(C, 0, Tail) when is_integer(C) ->
-spec copies(string(), non_neg_integer()) -> string().
-copies(CharList, Num) when is_list(CharList), Num >= 0 ->
+copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 ->
copies(CharList, Num, []).
copies(_CharList, 0, R) ->
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index 22269a8d1b..3c5800effa 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
-behaviour(gen_server).
%% External exports
--export([start_link/2,start_link/3,
+-export([start_link/2, start_link/3,
start_child/2, restart_child/2,
delete_child/2, terminate_child/2,
which_children/1, count_children/1,
@@ -33,28 +33,56 @@
-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]).
-export([handle_cast/2]).
+%%--------------------------------------------------------------------------
+
+-export_type([child_spec/0, del_err/0, startchild_ret/0, strategy/0]).
+
+%%--------------------------------------------------------------------------
+
+-type child_id() :: pid() | 'undefined'.
+-type mfargs() :: {module(), atom(), [term()] | undefined}.
+-type modules() :: [module()] | 'dynamic'.
+-type restart() :: 'permanent' | 'transient' | 'temporary'.
+-type shutdown() :: 'brutal_kill' | timeout().
+-type worker() :: 'worker' | 'supervisor'.
+-type sup_name() :: {'local', atom()} | {'global', atom()}.
+-type sup_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid().
+-type child_spec() :: {term(),mfargs(),restart(),shutdown(),worker(),modules()}.
+
+-type strategy() :: 'one_for_all' | 'one_for_one'
+ | 'rest_for_one' | 'simple_one_for_one'.
+
+%%--------------------------------------------------------------------------
+
+-record(child, {% pid is undefined when child is not running
+ pid = undefined :: child_id(),
+ name,
+ mfargs :: mfargs(),
+ restart_type :: restart(),
+ shutdown :: shutdown(),
+ child_type :: worker(),
+ modules = [] :: modules()}).
+-type child() :: #child{}.
+
-define(DICT, dict).
-record(state, {name,
- strategy,
- children = [],
- dynamics = ?DICT:new(),
- intensity,
- period,
+ strategy :: strategy(),
+ children = [] :: [child()],
+ dynamics :: ?DICT() | list(),
+ intensity :: non_neg_integer(),
+ period :: pos_integer(),
restarts = [],
module,
args}).
-
--record(child, {pid = undefined, % pid is undefined when child is not running
- name,
- mfa,
- restart_type,
- shutdown,
- child_type,
- modules = []}).
+-type state() :: #state{}.
-define(is_simple(State), State#state.strategy =:= simple_one_for_one).
+%%--------------------------------------------------------------------------
+
+-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}].
+
behaviour_info(callbacks) ->
[{init,1}];
behaviour_info(_Other) ->
@@ -65,21 +93,40 @@ behaviour_info(_Other) ->
%%% Servers/processes should/could also be built using gen_server.erl.
%%% SupName = {local, atom()} | {global, atom()}.
%%% ---------------------------------------------------
+
+-type startlink_err() :: {'already_started', pid()} | 'shutdown' | term().
+-type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}.
+
+-spec start_link(module(), term()) -> startlink_ret().
start_link(Mod, Args) ->
gen_server:start_link(supervisor, {self, Mod, Args}, []).
+-spec start_link(sup_name(), module(), term()) -> startlink_ret().
start_link(SupName, Mod, Args) ->
gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []).
%%% ---------------------------------------------------
%%% Interface functions.
%%% ---------------------------------------------------
+
+-type info() :: term().
+-type startchild_err() :: 'already_present'
+ | {'already_started', child_id()} | term().
+-type startchild_ret() :: {'ok', child_id()} | {'ok', child_id(), info()}
+ | {'error', startchild_err()}.
+
+-spec start_child(sup_ref(), child_spec() | [term()]) -> startchild_ret().
start_child(Supervisor, ChildSpec) ->
call(Supervisor, {start_child, ChildSpec}).
+-type restart_err() :: 'running' | 'not_found' | 'simple_one_for_one' | term().
+-spec restart_child(sup_ref(), term()) ->
+ {'ok', child_id()} | {'ok', child_id(), info()} | {'error', restart_err()}.
restart_child(Supervisor, Name) ->
call(Supervisor, {restart_child, Name}).
+-type del_err() :: 'running' | 'not_found' | 'simple_one_for_one'.
+-spec delete_child(sup_ref(), term()) -> 'ok' | {'error', del_err()}.
delete_child(Supervisor, Name) ->
call(Supervisor, {delete_child, Name}).
@@ -89,9 +136,13 @@ delete_child(Supervisor, Name) ->
%% Note that the child is *always* terminated in some
%% way (maybe killed).
%%-----------------------------------------------------------------
+
+-type term_err() :: 'not_found' | 'simple_one_for_one'.
+-spec terminate_child(sup_ref(), term()) -> 'ok' | {'error', term_err()}.
terminate_child(Supervisor, Name) ->
call(Supervisor, {terminate_child, Name}).
+-spec which_children(sup_ref()) -> [{term(), child_id(), worker(), modules()}].
which_children(Supervisor) ->
call(Supervisor, which_children).
@@ -101,6 +152,7 @@ count_children(Supervisor) ->
call(Supervisor, Req) ->
gen_server:call(Supervisor, Req, infinity).
+-spec check_childspecs([child_spec()]) -> 'ok' | {'error', term()}.
check_childspecs(ChildSpecs) when is_list(ChildSpecs) ->
case check_startspec(ChildSpecs) of
{ok, _} -> ok;
@@ -113,6 +165,16 @@ check_childspecs(X) -> {error, {badarg, X}}.
%%% Initialize the supervisor.
%%%
%%% ---------------------------------------------------
+
+-type init_sup_name() :: sup_name() | 'self'.
+
+-type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}}
+ | {'bad_start_spec', term()} | {'start_spec', term()}
+ | {'supervisor_data', term()}.
+
+-spec init({init_sup_name(), module(), [term()]}) ->
+ {'ok', state()} | 'ignore' | {'stop', stop_rsn()}.
+
init({SupName, Mod, Args}) ->
process_flag(trap_exit, true),
case Mod:init(Args) of
@@ -130,7 +192,7 @@ init({SupName, Mod, Args}) ->
Error ->
{stop, {bad_return, {Mod, init, Error}}}
end.
-
+
init_children(State, StartSpec) ->
SupName = State#state.name,
case check_startspec(StartSpec) of
@@ -158,12 +220,12 @@ init_dynamic(_State, StartSpec) ->
%%-----------------------------------------------------------------
%% Func: start_children/2
-%% Args: Children = [#child] in start order
-%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
+%% Args: Children = [child()] in start order
+%% SupName = {local, atom()} | {global, atom()} | {pid(), Mod}
%% Purpose: Start all children. The new list contains #child's
%% with pids.
%% Returns: {ok, NChildren} | {error, NChildren}
-%% NChildren = [#child] in termination order (reversed
+%% NChildren = [child()] in termination order (reversed
%% start order)
%%-----------------------------------------------------------------
start_children(Children, SupName) -> start_children(Children, [], SupName).
@@ -182,8 +244,8 @@ start_children([], NChildren, _SupName) ->
{ok, NChildren}.
do_start_child(SupName, Child) ->
- #child{mfa = {M, F, A}} = Child,
- case catch apply(M, F, A) of
+ #child{mfargs = {M, F, Args}} = Child,
+ case catch apply(M, F, Args) of
{ok, Pid} when is_pid(Pid) ->
NChild = Child#child{pid = Pid},
report_progress(NChild, SupName),
@@ -192,7 +254,7 @@ do_start_child(SupName, Child) ->
NChild = Child#child{pid = Pid},
report_progress(NChild, SupName),
{ok, Pid, Extra};
- ignore ->
+ ignore ->
{ok, undefined};
{error, What} -> {error, What};
What -> {error, What}
@@ -211,31 +273,32 @@ do_start_child_i(M, F, A) ->
What ->
{error, What}
end.
-
%%% ---------------------------------------------------
%%%
%%% Callback functions.
%%%
%%% ---------------------------------------------------
+-type call() :: 'which_children' | 'count_children' | {_, _}. % XXX: refine
+-spec handle_call(call(), term(), state()) -> {'reply', term(), state()}.
+
handle_call({start_child, EArgs}, _From, State) when ?is_simple(State) ->
- #child{mfa = {M, F, A}} = hd(State#state.children),
+ Child = hd(State#state.children),
+ #child{mfargs = {M, F, A}} = Child,
Args = A ++ EArgs,
case do_start_child_i(M, F, Args) of
{ok, Pid} ->
- NState = State#state{dynamics =
- ?DICT:store(Pid, Args, State#state.dynamics)},
+ NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
{reply, {ok, Pid}, NState};
{ok, Pid, Extra} ->
- NState = State#state{dynamics =
- ?DICT:store(Pid, Args, State#state.dynamics)},
+ NState = save_dynamic_child(Child#child.restart_type, Pid, Args, State),
{reply, {ok, Pid, Extra}, NState};
What ->
{reply, What, State}
end;
%%% The requests terminate_child, delete_child and restart_child are
-%%% invalid for simple_one_for_one supervisors.
+%%% invalid for simple_one_for_one supervisors.
handle_call({_Req, _Data}, _From, State) when ?is_simple(State) ->
{reply, {error, simple_one_for_one}, State};
@@ -287,28 +350,56 @@ handle_call({terminate_child, Name}, _From, State) ->
{reply, {error, not_found}, State}
end;
-handle_call(which_children, _From, State) when ?is_simple(State) ->
- [#child{child_type = CT, modules = Mods}] = State#state.children,
+handle_call(which_children, _From, #state{children = [#child{restart_type = temporary,
+ child_type = CT,
+ modules = Mods}]} =
+ State) when ?is_simple(State) ->
+ Reply = lists:map(fun(Pid) -> {undefined, Pid, CT, Mods} end, dynamics_db(temporary,
+ State#state.dynamics)),
+ {reply, Reply, State};
+
+handle_call(which_children, _From, #state{children = [#child{restart_type = RType,
+ child_type = CT,
+ modules = Mods}]} =
+ State) when ?is_simple(State) ->
Reply = lists:map(fun({Pid, _}) -> {undefined, Pid, CT, Mods} end,
- ?DICT:to_list(State#state.dynamics)),
+ ?DICT:to_list(dynamics_db(RType, State#state.dynamics))),
{reply, Reply, State};
handle_call(which_children, _From, State) ->
Resp =
lists:map(fun(#child{pid = Pid, name = Name,
child_type = ChildType, modules = Mods}) ->
- {Name, Pid, ChildType, Mods}
+ {Name, Pid, ChildType, Mods}
end,
State#state.children),
{reply, Resp, State};
-handle_call(count_children, _From, State) when ?is_simple(State) ->
- [#child{child_type = CT}] = State#state.children,
+
+handle_call(count_children, _From, #state{children = [#child{restart_type = temporary,
+ child_type = CT}]} = State)
+ when ?is_simple(State) ->
+ {Active, Count} =
+ lists:foldl(fun(Pid, {Alive, Tot}) ->
+ if is_pid(Pid) -> {Alive+1, Tot +1};
+ true -> {Alive, Tot + 1} end
+ end, {0, 0}, dynamics_db(temporary, State#state.dynamics)),
+ Reply = case CT of
+ supervisor -> [{specs, 1}, {active, Active},
+ {supervisors, Count}, {workers, 0}];
+ worker -> [{specs, 1}, {active, Active},
+ {supervisors, 0}, {workers, Count}]
+ end,
+ {reply, Reply, State};
+
+handle_call(count_children, _From, #state{children = [#child{restart_type = RType,
+ child_type = CT}]} = State)
+ when ?is_simple(State) ->
{Active, Count} =
?DICT:fold(fun(Pid, _Val, {Alive, Tot}) ->
if is_pid(Pid) -> {Alive+1, Tot +1};
true -> {Alive, Tot + 1} end
- end, {0, 0}, State#state.dynamics),
+ end, {0, 0}, dynamics_db(RType, State#state.dynamics)),
Reply = case CT of
supervisor -> [{specs, 1}, {active, Active},
{supervisors, Count}, {workers, 0}];
@@ -318,7 +409,6 @@ handle_call(count_children, _From, State) when ?is_simple(State) ->
{reply, Reply, State};
handle_call(count_children, _From, State) ->
-
%% Specs and children are together on the children list...
{Specs, Active, Supers, Workers} =
lists:foldl(fun(Child, Counts) ->
@@ -347,15 +437,19 @@ count_child(#child{pid = Pid, child_type = supervisor},
%%% Hopefully cause a function-clause as there is no API function
%%% that utilizes cast.
+-spec handle_cast('null', state()) -> {'noreply', state()}.
+
handle_cast(null, State) ->
error_logger:error_msg("ERROR: Supervisor received cast-message 'null'~n",
[]),
-
{noreply, State}.
%%
%% Take care of terminated children.
%%
+-spec handle_info(term(), state()) ->
+ {'noreply', state()} | {'stop', 'shutdown', state()}.
+
handle_info({'EXIT', Pid, Reason}, State) ->
case restart_child(Pid, Reason, State) of
{ok, State1} ->
@@ -368,9 +462,12 @@ handle_info(Msg, State) ->
error_logger:error_msg("Supervisor received unexpected message: ~p~n",
[Msg]),
{noreply, State}.
+
%%
%% Terminate this server.
%%
+-spec terminate(term(), state()) -> 'ok'.
+
terminate(_Reason, State) ->
terminate_children(State#state.children, State#state.name),
ok.
@@ -384,6 +481,9 @@ terminate(_Reason, State) ->
%% NOTE: This requires that the init function of the call-back module
%% does not have any side effects.
%%
+-spec code_change(term(), state(), term()) ->
+ {'ok', state()} | {'error', term()}.
+
code_change(_, State, _) ->
case (State#state.module):init(State#state.args) of
{ok, {SupFlags, StartSpec}} ->
@@ -411,7 +511,7 @@ check_flags({Strategy, MaxIntensity, Period}) ->
check_flags(What) ->
{bad_flags, What}.
-update_childspec(State, StartSpec) when ?is_simple(State) ->
+update_childspec(State, StartSpec) when ?is_simple(State) ->
case check_startspec(StartSpec) of
{ok, [Child]} ->
{ok, State#state{children = [Child]}};
@@ -437,7 +537,7 @@ update_childspec1([Child|OldC], Children, KeepOld) ->
update_childspec1(OldC, Children, [Child|KeepOld])
end;
update_childspec1([], Children, KeepOld) ->
- % Return them in (keeped) reverse start order.
+ %% Return them in (kept) reverse start order.
lists:reverse(Children ++ KeepOld).
update_chsp(OldCh, Children) ->
@@ -462,15 +562,11 @@ handle_start_child(Child, State) ->
false ->
case do_start_child(State#state.name, Child) of
{ok, Pid} ->
- Children = State#state.children,
{{ok, Pid},
- State#state{children =
- [Child#child{pid = Pid}|Children]}};
+ save_child(Child#child{pid = Pid}, State)};
{ok, Pid, Extra} ->
- Children = State#state.children,
{{ok, Pid, Extra},
- State#state{children =
- [Child#child{pid = Pid}|Children]}};
+ save_child(Child#child{pid = Pid}, State)};
{error, What} ->
{{error, {What, Child}}, State}
end;
@@ -482,27 +578,26 @@ handle_start_child(Child, State) ->
%%% ---------------------------------------------------
%%% Restart. A process has terminated.
-%%% Returns: {ok, #state} | {shutdown, #state}
+%%% Returns: {ok, state()} | {shutdown, state()}
%%% ---------------------------------------------------
-restart_child(Pid, Reason, State) when ?is_simple(State) ->
- case ?DICT:find(Pid, State#state.dynamics) of
+restart_child(Pid, Reason, #state{children = [Child]} = State) when ?is_simple(State) ->
+ RestartType = Child#child.restart_type,
+ case dynamic_child_args(Pid, dynamics_db(RestartType, State#state.dynamics)) of
{ok, Args} ->
- [Child] = State#state.children,
- RestartType = Child#child.restart_type,
- {M, F, _} = Child#child.mfa,
- NChild = Child#child{pid = Pid, mfa = {M, F, Args}},
+ {M, F, _} = Child#child.mfargs,
+ NChild = Child#child{pid = Pid, mfargs = {M, F, Args}},
do_restart(RestartType, Reason, NChild, State);
error ->
- {ok, State}
+ {ok, State}
end;
+
restart_child(Pid, Reason, State) ->
Children = State#state.children,
- case lists:keysearch(Pid, #child.pid, Children) of
- {value, Child} ->
- RestartType = Child#child.restart_type,
+ case lists:keyfind(Pid, #child.pid, Children) of
+ #child{restart_type = RestartType} = Child ->
do_restart(RestartType, Reason, Child, State);
- _ ->
+ false ->
{ok, State}
end.
@@ -534,8 +629,9 @@ restart(Child, State) ->
end.
restart(simple_one_for_one, Child, State) ->
- #child{mfa = {M, F, A}} = Child,
- Dynamics = ?DICT:erase(Child#child.pid, State#state.dynamics),
+ #child{mfargs = {M, F, A}} = Child,
+ Dynamics = ?DICT:erase(Child#child.pid, dynamics_db(Child#child.restart_type,
+ State#state.dynamics)),
case do_start_child_i(M, F, A) of
{ok, Pid} ->
NState = State#state{dynamics = ?DICT:store(Pid, A, Dynamics)},
@@ -580,9 +676,9 @@ restart(one_for_all, Child, State) ->
%%-----------------------------------------------------------------
%% Func: terminate_children/2
-%% Args: Children = [#child] in termination order
+%% Args: Children = [child()] in termination order
%% SupName = {local, atom()} | {global, atom()} | {pid(),Mod}
-%% Returns: NChildren = [#child] in
+%% Returns: NChildren = [child()] in
%% startup order (reversed termination order)
%%-----------------------------------------------------------------
terminate_children(Children, SupName) ->
@@ -595,14 +691,15 @@ terminate_children([], _SupName, Res) ->
Res.
do_terminate(Child, SupName) when Child#child.pid =/= undefined ->
- case shutdown(Child#child.pid,
- Child#child.shutdown) of
- ok ->
- Child#child{pid = undefined};
- {error, OtherReason} ->
- report_error(shutdown_error, OtherReason, Child, SupName),
- Child#child{pid = undefined}
- end;
+ case shutdown(Child#child.pid, Child#child.shutdown) of
+ ok ->
+ ok;
+ {error, normal} when Child#child.restart_type =/= permanent ->
+ ok;
+ {error, OtherReason} ->
+ report_error(shutdown_error, OtherReason, Child, SupName)
+ end,
+ Child#child{pid = undefined};
do_terminate(Child, _SupName) ->
Child.
@@ -617,7 +714,6 @@ do_terminate(Child, _SupName) ->
%% Returns: ok | {error, OtherReason} (this should be reported)
%%-----------------------------------------------------------------
shutdown(Pid, brutal_kill) ->
-
case monitor_child(Pid) of
ok ->
exit(Pid, kill),
@@ -630,9 +726,7 @@ shutdown(Pid, brutal_kill) ->
{error, Reason} ->
{error, Reason}
end;
-
shutdown(Pid, Time) ->
-
case monitor_child(Pid) of
ok ->
exit(Pid, shutdown), %% Try to shutdown gracefully
@@ -684,8 +778,40 @@ monitor_child(Pid) ->
%%-----------------------------------------------------------------
%% Child/State manipulating functions.
%%-----------------------------------------------------------------
-state_del_child(#child{pid = Pid}, State) when ?is_simple(State) ->
- NDynamics = ?DICT:erase(Pid, State#state.dynamics),
+
+%% Note we do not want to save the parameter list for temporary processes as
+%% they will not be restarted, and hence we do not need this information.
+%% Especially for dynamic children to simple_one_for_one supervisors
+%% it could become very costly as it is not uncommon to spawn
+%% very many such processes.
+save_child(#child{restart_type = temporary,
+ mfargs = {M, F, _}} = Child, #state{children = Children} = State) ->
+ State#state{children = [Child#child{mfargs = {M, F, undefined}} |Children]};
+save_child(Child, #state{children = Children} = State) ->
+ State#state{children = [Child |Children]}.
+
+save_dynamic_child(temporary, Pid, _, #state{dynamics = Dynamics} = State) ->
+ State#state{dynamics = [Pid | dynamics_db(temporary, Dynamics)]};
+save_dynamic_child(RestartType, Pid, Args, #state{dynamics = Dynamics} = State) ->
+ State#state{dynamics = ?DICT:store(Pid, Args, dynamics_db(RestartType, Dynamics))}.
+
+dynamics_db(temporary, undefined) ->
+ [];
+dynamics_db(_, undefined) ->
+ ?DICT:new();
+dynamics_db(_,Dynamics) ->
+ Dynamics.
+
+dynamic_child_args(_, Dynamics) when is_list(Dynamics)->
+ {ok, undefined};
+dynamic_child_args(Pid, Dynamics) ->
+ ?DICT:find(Pid, Dynamics).
+
+state_del_child(#child{pid = Pid, restart_type = temporary}, State) when ?is_simple(State) ->
+ NDynamics = lists:delete(Pid, dynamics_db(temporary, State#state.dynamics)),
+ State#state{dynamics = NDynamics};
+state_del_child(#child{pid = Pid, restart_type = RType}, State) when ?is_simple(State) ->
+ NDynamics = ?DICT:erase(Pid, dynamics_db(RType, State#state.dynamics)),
State#state{dynamics = NDynamics};
state_del_child(Child, State) ->
NChildren = del_child(Child#child.name, State#state.children),
@@ -738,9 +864,9 @@ remove_child(Child, State) ->
%% MaxIntensity = integer()
%% Period = integer()
%% Mod :== atom()
-%% Arsg :== term()
+%% Args :== term()
%% Purpose: Check that Type is of correct type (!)
-%% Returns: {ok, #state} | Error
+%% Returns: {ok, state()} | Error
%%-----------------------------------------------------------------
init_state(SupName, Type, Mod, Args) ->
case catch init_state1(SupName, Type, Mod, Args) of
@@ -755,11 +881,11 @@ init_state1(SupName, {Strategy, MaxIntensity, Period}, Mod, Args) ->
validIntensity(MaxIntensity),
validPeriod(Period),
{ok, #state{name = supname(SupName,Mod),
- strategy = Strategy,
- intensity = MaxIntensity,
- period = Period,
- module = Mod,
- args = Args}};
+ strategy = Strategy,
+ intensity = MaxIntensity,
+ period = Period,
+ module = Mod,
+ args = Args}};
init_state1(_SupName, Type, _, _) ->
{invalid_type, Type}.
@@ -771,26 +897,26 @@ validStrategy(What) -> throw({invalid_strategy, What}).
validIntensity(Max) when is_integer(Max),
Max >= 0 -> true;
-validIntensity(What) -> throw({invalid_intensity, What}).
+validIntensity(What) -> throw({invalid_intensity, What}).
validPeriod(Period) when is_integer(Period),
Period > 0 -> true;
validPeriod(What) -> throw({invalid_period, What}).
-supname(self,Mod) -> {self(),Mod};
-supname(N,_) -> N.
+supname(self, Mod) -> {self(), Mod};
+supname(N, _) -> N.
%%% ------------------------------------------------------
%%% Check that the children start specification is valid.
%%% Shall be a six (6) tuple
%%% {Name, Func, RestartType, Shutdown, ChildType, Modules}
%%% where Name is an atom
-%%% Func is {Mod, Fun, Args} == {atom, atom, list}
+%%% Func is {Mod, Fun, Args} == {atom(), atom(), list()}
%%% RestartType is permanent | temporary | transient
%%% Shutdown = integer() | infinity | brutal_kill
%%% ChildType = supervisor | worker
%%% Modules = [atom()] | dynamic
-%%% Returns: {ok, [#child]} | Error
+%%% Returns: {ok, [child()]} | Error
%%% ------------------------------------------------------
check_startspec(Children) -> check_startspec(Children, []).
@@ -818,14 +944,14 @@ check_childspec(Name, Func, RestartType, Shutdown, ChildType, Mods) ->
validChildType(ChildType),
validShutdown(Shutdown, ChildType),
validMods(Mods),
- {ok, #child{name = Name, mfa = Func, restart_type = RestartType,
+ {ok, #child{name = Name, mfargs = Func, restart_type = RestartType,
shutdown = Shutdown, child_type = ChildType, modules = Mods}}.
validChildType(supervisor) -> true;
validChildType(worker) -> true;
validChildType(What) -> throw({invalid_child_type, What}).
-validName(_Name) -> true.
+validName(_Name) -> true.
validFunc({M, F, A}) when is_atom(M),
is_atom(F),
@@ -923,7 +1049,7 @@ report_error(Error, Reason, Child, SupName) ->
extract_child(Child) ->
[{pid, Child#child.pid},
{name, Child#child.name},
- {mfa, Child#child.mfa},
+ {mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
{child_type, Child#child.child_type}].
diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl
index 6ee837c3e6..b456c5d6c1 100644
--- a/lib/stdlib/src/timer.erl
+++ b/lib/stdlib/src/timer.erl
@@ -22,7 +22,7 @@
send_after/3, send_after/2,
exit_after/3, exit_after/2, kill_after/2, kill_after/1,
apply_interval/4, send_interval/3, send_interval/2,
- cancel/1, sleep/1, tc/3, now_diff/2,
+ cancel/1, sleep/1, tc/2, tc/3, now_diff/2,
seconds/1, minutes/1, hours/1, hms/3]).
-export([start_link/0, start/0,
@@ -33,6 +33,9 @@
%% internal exports for test purposes only
-export([get_status/0]).
+%% types which can be used by other modules
+-export_type([tref/0]).
+
%% Max
-define(MAX_TIMEOUT, 16#0800000).
-define(TIMER_TAB, timer_tab).
@@ -98,6 +101,17 @@ sleep(T) ->
after T -> ok
end.
+
+%%
+%% Measure the execution time (in microseconds) for Fun(Args).
+%%
+-spec tc(function(), [_]) -> {time(), term()}.
+tc(F, A) ->
+ Before = erlang:now(),
+ Val = (catch apply(F, A)),
+ After = erlang:now(),
+ {now_diff(After, Before), Val}.
+
%%
%% Measure the execution time (in microseconds) for an MFA.
%%
diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl
index 09b1deff9c..12bc60623d 100644
--- a/lib/stdlib/src/unicode.erl
+++ b/lib/stdlib/src/unicode.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -25,8 +25,17 @@
%% InEncoding is not {latin1 | unicode | utf8})
%%
--export([characters_to_list/1, characters_to_list_int/2, characters_to_binary/1,characters_to_binary_int/2, characters_to_binary/3,bom_to_encoding/1, encoding_to_bom/1]).
+-export([characters_to_list/1, characters_to_list_int/2,
+ characters_to_binary/1, characters_to_binary_int/2,
+ characters_to_binary/3,
+ bom_to_encoding/1, encoding_to_bom/1]).
+-export_type([encoding/0]).
+
+-type encoding() :: 'latin1' | 'unicode' | 'utf8'
+ | 'utf16' | {'utf16', endian()}
+ | 'utf32' | {'utf32', endian()}.
+-type endian() :: 'big' | 'little'.
characters_to_list(ML) ->
unicode:characters_to_list(ML,unicode).
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index 9beac93eb8..5502c69fa5 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -9,6 +9,8 @@ MODULES= \
array_SUITE \
base64_SUITE \
beam_lib_SUITE \
+ binary_module_SUITE \
+ binref \
c_SUITE \
calendar_SUITE \
dets_SUITE \
@@ -131,9 +133,9 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) stdlib.spec stdlib.spec.vxworks $(EMAKEFILE) \
+ $(INSTALL_DATA) stdlib.spec $(EMAKEFILE) \
$(ERL_FILES) $(COVERFILE) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/stdlib/test/array_SUITE.erl b/lib/stdlib/test/array_SUITE.erl
index e7cfc65be1..1b496bb8ec 100644
--- a/lib/stdlib/test/array_SUITE.erl
+++ b/lib/stdlib/test/array_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,7 +19,7 @@
-module(array_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Default timetrap timeout (set in init_per_testcase).
%% This should be set relatively high (10-15 times the expected
@@ -27,8 +27,9 @@
-define(default_timeout, ?t:seconds(60)).
%% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([
new_test/1,
@@ -64,33 +65,37 @@
%%
%% all/1
%%
-all(doc) ->
- [];
-all(suite) ->
- [new_test,
- fix_test,
- relax_test,
- resize_test,
- set_get_test,
- to_list_test,
- sparse_to_list_test,
- from_list_test,
- to_orddict_test,
- sparse_to_orddict_test,
- from_orddict_test,
- map_test,
- sparse_map_test,
- foldl_test,
- sparse_foldl_test,
- foldr_test,
- sparse_foldr_test
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [new_test, fix_test, relax_test, resize_test,
+ set_get_test, to_list_test, sparse_to_list_test,
+ from_list_test, to_orddict_test, sparse_to_orddict_test,
+ from_orddict_test, map_test, sparse_map_test,
+ foldl_test, sparse_foldl_test, foldr_test,
+ sparse_foldr_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl
index 44742063b3..c64a961ffa 100644
--- a/lib/stdlib/test/base64_SUITE.erl
+++ b/lib/stdlib/test/base64_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,18 +18,19 @@
%%
-module(base64_SUITE).
--author('[email protected]').
--include("test_server.hrl").
+-include_lib("common_test/include/ct.hrl").
-include("test_server_line.hrl").
%% Test server specific exports
--export([all/1, init_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
-export([base64_encode/1, base64_decode/1, base64_otp_5635/1,
base64_otp_6279/1, big/1, illegal/1, mime_decode/1,
- roundtrip/1]).
+ mime_decode_to_string/1, roundtrip/1]).
init_per_testcase(_, Config) ->
Dog = test_server:timetrap(?t:minutes(2)),
@@ -44,14 +45,29 @@ end_per_testcase(_, Config) ->
%%-------------------------------------------------------------------------
%% Test cases starts here.
%%-------------------------------------------------------------------------
-all(doc) ->
- ["Test library functions for base64 encode and decode "
- "(taken from inets/test/http_format_SUITE)"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[base64_encode, base64_decode, base64_otp_5635,
- base64_otp_6279, big, illegal, mime_decode,
+ base64_otp_6279, big, illegal, mime_decode, mime_decode_to_string,
roundtrip].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%-------------------------------------------------------------------------
base64_encode(doc) ->
@@ -59,7 +75,7 @@ base64_encode(doc) ->
base64_encode(suite) ->
[];
base64_encode(Config) when is_list(Config) ->
- %% Two pads
+ %% Two pads
<<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> =
base64:encode("Aladdin:open sesame"),
%% One pad
@@ -77,8 +93,8 @@ base64_decode(doc) ->
base64_decode(suite) ->
[];
base64_decode(Config) when is_list(Config) ->
- %% Two pads
- <<"Aladdin:open sesame">> =
+ %% Two pads
+ <<"Aladdin:open sesame">> =
base64:decode("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="),
%% One pad
<<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>),
@@ -138,20 +154,85 @@ illegal(Config) when is_list(Config) ->
{'EXIT',{function_clause, _}} = (catch base64:decode("()")),
ok.
%%-------------------------------------------------------------------------
+%% mime_decode and mime_decode_to_string have different implementations
+%% so test both with the same input separately. Both functions have
+%% the same implementation for binary/string arguments.
mime_decode(doc) ->
["Test base64:mime_decode/1."];
mime_decode(suite) ->
[];
mime_decode(Config) when is_list(Config) ->
- %% Two pads
- <<"Aladdin:open sesame">> =
+ %% Test correct padding
+ <<"one">> = base64:mime_decode(<<"b25l">>),
+ <<"on">> = base64:mime_decode(<<"b24=">>),
+ <<"o">> = base64:mime_decode(<<"bw==">>),
+ %% Test 1 extra padding
+ <<"one">> = base64:mime_decode(<<"b25l= =">>),
+ <<"on">> = base64:mime_decode(<<"b24== =">>),
+ <<"o">> = base64:mime_decode(<<"bw=== =">>),
+ %% Test 2 extra padding
+ <<"one">> = base64:mime_decode(<<"b25l===">>),
+ <<"on">> = base64:mime_decode(<<"b24====">>),
+ <<"o">> = base64:mime_decode(<<"bw=====">>),
+ %% Test misc embedded padding
+ <<"one">> = base64:mime_decode(<<"b2=5l===">>),
+ <<"on">> = base64:mime_decode(<<"b=24====">>),
+ <<"o">> = base64:mime_decode(<<"b=w=====">>),
+ %% Test misc white space and illegals with embedded padding
+ <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>),
+ <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>),
+ <<"o">> = base64:mime_decode(<<"\nb=w=====">>),
+ %% Two pads
+ <<"Aladdin:open sesame">> =
base64:mime_decode("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="),
- %% One pad, followed by ignored text
- <<"Hello World">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=apa">>),
+ %% One pad to ignore, followed by more text
+ <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
+ %% No pad
+ <<"Aladdin:open sesam">> =
+ base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"),
+ %% Encoded base 64 strings may be divided by non base 64 chars.
+ %% In this cases whitespaces.
+ <<"0123456789!@#0^&*();:<>,. []{}">> =
+ base64:mime_decode(
+ <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>),
+ ok.
+
+%%-------------------------------------------------------------------------
+
+%% Repeat of mime_decode() tests
+mime_decode_to_string(doc) ->
+ ["Test base64:mime_decode_to_string/1."];
+mime_decode_to_string(suite) ->
+ [];
+mime_decode_to_string(Config) when is_list(Config) ->
+ %% Test correct padding
+ "one" = base64:mime_decode_to_string(<<"b25l">>),
+ "on" = base64:mime_decode_to_string(<<"b24=">>),
+ "o" = base64:mime_decode_to_string(<<"bw==">>),
+ %% Test 1 extra padding
+ "one" = base64:mime_decode_to_string(<<"b25l= =">>),
+ "on" = base64:mime_decode_to_string(<<"b24== =">>),
+ "o" = base64:mime_decode_to_string(<<"bw=== =">>),
+ %% Test 2 extra padding
+ "one" = base64:mime_decode_to_string(<<"b25l===">>),
+ "on" = base64:mime_decode_to_string(<<"b24====">>),
+ "o" = base64:mime_decode_to_string(<<"bw=====">>),
+ %% Test misc embedded padding
+ "one" = base64:mime_decode_to_string(<<"b2=5l===">>),
+ "on" = base64:mime_decode_to_string(<<"b=24====">>),
+ "o" = base64:mime_decode_to_string(<<"b=w=====">>),
+ %% Test misc white space and illegals with embedded padding
+ "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>),
+ "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>),
+ "o" = base64:mime_decode_to_string(<<"\nb=w=====">>),
+ %% Two pads
+ "Aladdin:open sesame" =
+ base64:mime_decode_to_string("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="),
+ %% One pad to ignore, followed by more text
+ "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>),
%% No pad
"Aladdin:open sesam" =
base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"),
-
%% Encoded base 64 strings may be divided by non base 64 chars.
%% In this cases whitespaces.
"0123456789!@#0^&*();:<>,. []{}" =
@@ -159,6 +240,7 @@ mime_decode(Config) when is_list(Config) ->
<<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>),
ok.
+%%-------------------------------------------------------------------------
roundtrip(Config) when is_list(Config) ->
Sizes = lists:seq(1, 255) ++ lists:seq(2400-5, 2440),
diff --git a/lib/stdlib/test/beam_lib_SUITE.erl b/lib/stdlib/test/beam_lib_SUITE.erl
index bc867a3770..4ccc863795 100644
--- a/lib/stdlib/test/beam_lib_SUITE.erl
+++ b/lib/stdlib/test/beam_lib_SUITE.erl
@@ -1,6 +1,19 @@
%%
%% %CopyrightBegin%
%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
%% %CopyrightEnd%
%%
-module(beam_lib_SUITE).
@@ -14,25 +27,45 @@
-define(t,test_server).
-define(privdir, "beam_lib_SUITE_priv").
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(privdir, ?config(priv_dir, Conf)).
-endif.
--export([all/1, normal/1, error/1, cmp/1, cmp_literals/1, strip/1, otp_6711/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ normal/1, error/1, cmp/1, cmp_literals/1, strip/1, otp_6711/1,
building/1, md5/1, encrypted_abstr/1, encrypted_abstr_file/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [error, normal, cmp, cmp_literals, strip, otp_6711,
+ building, md5, encrypted_abstr, encrypted_abstr_file].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [error, normal, cmp, cmp_literals, strip, otp_6711, building, md5,
- encrypted_abstr, encrypted_abstr_file].
init_per_testcase(_Case, Config) ->
Dog=?t:timetrap(?t:minutes(2)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
new file mode 100644
index 0000000000..8fb63f33bd
--- /dev/null
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -0,0 +1,1363 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(binary_module_SUITE).
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ interesting/1,random_ref_comp/1,random_ref_sr_comp/1,
+ random_ref_fla_comp/1,parts/1, bin_to_list/1, list_to_bin/1,
+ copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1]).
+
+-export([random_number/1, make_unaligned/1]).
+
+
+
+%%-define(STANDALONE,1).
+
+-ifdef(STANDALONE).
+
+-define(line,erlang:display({?MODULE,?LINE}),).
+
+-else.
+
+-include_lib("test_server/include/test_server.hrl").
+-export([init_per_testcase/2, end_per_testcase/2]).
+% Default timetrap timeout (set in init_per_testcase).
+% Some of these testcases are really heavy...
+-define(default_timeout, ?t:minutes(20)).
+
+-endif.
+
+
+
+-ifdef(STANDALONE).
+-export([run/0]).
+
+run() ->
+ [ apply(?MODULE,X,[[]]) || X <- all(suite) ].
+
+-else.
+
+init_per_testcase(_Case, Config) ->
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{watchdog, Dog} | Config].
+
+end_per_testcase(_Case, Config) ->
+ ?line Dog = ?config(watchdog, Config),
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+-endif.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [interesting, random_ref_fla_comp, random_ref_sr_comp,
+ random_ref_comp, parts, bin_to_list, list_to_bin, copy,
+ referenced, guard, encode_decode, badargs,
+ longest_common_trap].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+-define(MASK_ERROR(EXPR),mask_error((catch (EXPR)))).
+
+
+badargs(doc) ->
+ ["Tests various badarg exceptions in the module"];
+badargs(Config) when is_list(Config) ->
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern([<<1,2,3:3>>])),
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern([<<1,2,3>>|<<1,2>>])),
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern(<<1,2,3:3>>)),
+ ?line badarg = ?MASK_ERROR(binary:compile_pattern(<<>>)),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3:3>>,<<1>>)),
+ ?line badarg = ?MASK_ERROR(binary:matches(<<1,2,3:3>>,<<1>>)),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{0,1},1}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scape,{0,1}}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{0,1,1}}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,0,1}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,[0,1]}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{0.1,1}}])),
+ ?line badarg = ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{1,1.1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{16#FF,
+ 16#FFFFFFFFFFFFFFFF}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,<<1>>,
+ [{scope,{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(<<1,2,3>>,{16#FF,
+ 16#FFFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:part(make_unaligned(<<1,2,3>>),{1,1,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{1,1,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FF,
+ 16#FFFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FFFFFFFFFFFFFFFFFF,
+ -16#7FFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary_part(make_unaligned(<<1,2,3>>),{16#FF,
+ -16#7FFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{16#FF,
+ 16#FFFFFFFFFFFFFFFF})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ -16#7FFFFFFFFFFFFFFF-1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{16#FFFFFFFFFFFFFFFF,
+ 16#7FFFFFFFFFFFFFFF})),
+ ?line [1,2,3] =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,[])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{1,2,3})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{1.0,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3>>,{1,1.0})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3:3>>,{1,1})),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list(<<1,2,3:3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:bin_to_list([1,2,3])),
+
+ ?line nomatch =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,<<1>>,[{scope,{0,0}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,[],[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{ac,<<>>},[{scope,{0,1}}])),
+ ?line {bm,BMMagic} = binary:compile_pattern([<<1,2,3>>]),
+ ?line {ac,ACMagic} = binary:compile_pattern([<<1,2,3>>,<<4,5>>]),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{bm,ACMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:match(<<1,2,3>>,{ac,BMMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,
+ {bm,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:match(<<1,2,3>>,
+ {ac,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line [] =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,<<1>>,[{scope,{0,0}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,<<>>},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,[],[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{ac,<<>>},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{bm,ACMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:matches(<<1,2,3>>,{ac,BMMagic},[{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:matches(<<1,2,3>>,
+ {bm,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:matches(<<1,2,3>>,
+ {ac,ets:match_spec_compile([{'_',[],['$_']}])},
+ [{scope,{0,1}}])),
+ ?line badarg =
+ ?MASK_ERROR(binary:longest_common_prefix(
+ [<<0:10000,1,2,4,1:3>>,
+ <<0:10000,1,2,3>>])),
+ ?line badarg =
+ ?MASK_ERROR(binary:longest_common_suffix(
+ [<<0:10000,1,2,4,1:3>>,
+ <<0:10000,1,2,3>>])),
+ ?line badarg =
+ ?MASK_ERROR(binary:encode_unsigned(-1)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:encode_unsigned(-16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:first(<<1,2,4,1:3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:first([1,2,4])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:last(<<1,2,4,1:3>>)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:last([1,2,4])),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:at(<<1,2,4,1:3>>,2)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:at(<<>>,2)),
+ ?line badarg =
+ ?MASK_ERROR(
+ binary:at([1,2,4],2)),
+ ok.
+
+longest_common_trap(doc) ->
+ ["Whitebox test to force special trap conditions in longest_common_{prefix,suffix}"];
+longest_common_trap(Config) when is_list(Config) ->
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,10)]),
+ erlang:bump_reductions(10000000),
+ ?line _ = binary:longest_common_prefix(
+ [<<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>]),
+ ?line _ = binary:longest_common_prefix(
+ [<<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
+ <<0:10000,1,2,4>>]),
+ erlang:bump_reductions(10000000),
+ ?line _ = binary:longest_common_suffix(
+ [<<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,3,3,0:10000,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
+ <<1,2,4,0:10000>>]),
+ ?line _ = binary:longest_common_suffix(
+ [<<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<1,2,4,0:10000>>,
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0>>,
+ <<1,2,4,0:10000>>]),
+ Subj = subj(),
+ Len = byte_size(Subj),
+ ?line Len = binary:longest_common_suffix(
+ [Subj,Subj,Subj]),
+ ?line io:format("limit was: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,
+ default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,false),
+ ok.
+
+subj() ->
+ Me = self(),
+ spawn(fun() ->
+ X0 = iolist_to_binary([
+ "1234567890",
+ %lists:seq(16#21, 16#7e),
+ lists:duplicate(100, $x)
+ ]),
+ Me ! X0,
+ receive X -> X end
+ end),
+ X0 = receive A -> A end,
+ <<X1:32/binary,_/binary>> = X0,
+ Subject= <<X1/binary>>,
+ Subject.
+
+
+interesting(doc) ->
+ ["Try some interesting patterns"];
+interesting(Config) when is_list(Config) ->
+ X = do_interesting(binary),
+ X = do_interesting(binref).
+
+do_interesting(Module) ->
+ ?line {0,4} = Module:match(<<"123456">>,
+ Module:compile_pattern([<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>])),
+ ?line [{0,4},{5,1}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>])),
+ ?line [{0,4}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>])),
+ ?line [{0,2},{2,2}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"12">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>])),
+ ?line {1,4} = Module:match(<<"123456">>,
+ Module:compile_pattern([<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>])),
+ ?line [{1,4}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>])),
+ ?line [{2,2}] = Module:matches(<<"123456">>,
+ Module:compile_pattern([<<"34">>,<<"34">>,
+ <<"12347">>,<<"2346">>])),
+
+ ?line {0,4} = Module:match(<<"123456">>,
+ [<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>]),
+ ?line [{0,4},{5,1}] = Module:matches(<<"123456">>,
+ [<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>,<<"6">>]),
+ ?line [{0,4}] = Module:matches(<<"123456">>,
+ [<<"12">>,<<"1234">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>]),
+ ?line [{0,2},{2,2}] = Module:matches(<<"123456">>,
+ [<<"12">>,
+ <<"23">>,<<"3">>,
+ <<"34">>,<<"456">>,
+ <<"45">>]),
+ ?line {1,4} = Module:match(<<"123456">>,
+ [<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>]),
+ ?line [{1,4}] = Module:matches(<<"123456">>,
+ [<<"34">>,<<"34">>,
+ <<"12347">>,<<"2345">>]),
+ ?line [{2,2}] = Module:matches(<<"123456">>,
+ [<<"34">>,<<"34">>,
+ <<"12347">>,<<"2346">>]),
+ ?line nomatch = Module:match(<<1,2,3,4>>,<<2>>,[{scope,{0,1}}]),
+ ?line {1,1} = Module:match(<<1,2,3,4>>,<<2>>,[{scope,{0,2}}]),
+ ?line nomatch = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,2}}]),
+ ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,3}}]),
+ ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{0,4}}]),
+ ?line badarg = ?MASK_ERROR(Module:match(<<1,2,3,4>>,<<2,3>>,
+ [{scope,{0,5}}])),
+ ?line {1,2} = Module:match(<<1,2,3,4>>,<<2,3>>,[{scope,{4,-4}}]),
+ ?line {0,3} = Module:match(<<1,2,3,4>>,<<1,2,3>>,[{scope,{4,-4}}]),
+ ?line {0,4} = Module:match(<<1,2,3,4>>,<<1,2,3,4>>,[{scope,{4,-4}}]),
+ ?line badarg = ?MASK_ERROR(Module:match(<<1,2,3,4>>,<<1,2,3,4>>,
+ [{scope,{3,-4}}])),
+ ?line [] = Module:matches(<<1,2,3,4>>,<<2>>,[{scope,{0,1}}]),
+ ?line [{1,1}] = Module:matches(<<1,2,3,4>>,[<<2>>,<<3>>],[{scope,{0,2}}]),
+ ?line [] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,2}}]),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,3}}]),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{0,4}}]),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>],
+ [{scope,{0,3}}]),
+ ?line [{1,2},{3,1}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>],
+ [{scope,{0,4}}]),
+ ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,<<2,3>>,
+ [{scope,{0,5}}])),
+ ?line [{1,2}] = Module:matches(<<1,2,3,4>>,<<2,3>>,[{scope,{4,-4}}]),
+ ?line [{1,2},{3,1}] = Module:matches(<<1,2,3,4>>,[<<2,3>>,<<4>>],
+ [{scope,{4,-4}}]),
+ ?line [{0,3}] = Module:matches(<<1,2,3,4>>,<<1,2,3>>,[{scope,{4,-4}}]),
+ ?line [{0,4}] = Module:matches(<<1,2,3,4>>,<<1,2,3,4>>,[{scope,{4,-4}}]),
+ ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,<<1,2,3,4>>,
+ [{scope,{3,-4}}])),
+ ?line badarg = ?MASK_ERROR(Module:matches(<<1,2,3,4>>,[<<1,2,3,4>>],
+ [{scope,{3,-4}}])),
+ ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,<<4,5>>),
+ ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>]),
+ ?line [<<1,2,3>>,<<6>>,<<8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>],[global]),
+ ?line [<<1,2,3>>,<<6>>,<<>>,<<>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global]),
+ ?line [<<1,2,3>>,<<6>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global,trim]),
+ ?line [<<1,2,3,4,5,6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global,trim,{scope,{0,4}}]),
+ ?line [<<1,2,3>>,<<6,7,8>>] = Module:split(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ [global,trim,{scope,{0,5}}]),
+ ?line badarg = ?MASK_ERROR(
+ Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,trim,{scope,{0,5}}])),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,[]),
+ ?line <<1,2,3,99,6,99,99>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global]),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}}]),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}}]),
+ ?line <<1,2,3,99,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}}]),
+ ?line badarg = ?MASK_ERROR(Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}},
+ {insert,1}])),
+ ?line <<1,2,3,99,4,5,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<99>>,
+ [global,{scope,{0,5}},
+ {insert_replaced,1}]),
+ ?line <<1,2,3,9,4,5,9,6,7,8>> = Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],
+ <<9,9>>,
+ [global,{scope,{0,5}},
+ {insert_replaced,1}]),
+ ?line badarg = ?MASK_ERROR(Module:replace(<<1,2,3,4,5,6,7,8>>,
+ [<<4,5>>,<<7>>,<<8>>],<<>>,
+ [global,{scope,{0,5}},
+ {insert_replaced,1}])),
+ ?line 2 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>]),
+ ?line 2 = Module:longest_common_prefix([<<1,2,4>>,<<1,2>>]),
+ ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1>>]),
+ ?line 0 = Module:longest_common_prefix([<<1,2,4>>,<<>>]),
+ ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>,<<1,3,3>>]),
+ ?line 1 = Module:longest_common_prefix([<<1,2,4>>,<<1,2,3>>,<<1,3,3>>,<<1,2,4>>]),
+ ?line 1251 = Module:longest_common_prefix([<<0:10000,1,2,4>>,
+ <<0:10000,1,2,3>>,
+ <<0:10000,1,3,3>>,
+ <<0:10000,1,2,4>>]),
+ ?line 12501 = Module:longest_common_prefix([<<0:100000,1,2,4>>,
+ <<0:100000,1,2,3>>,
+ <<0:100000,1,3,3>>,
+ <<0:100000,1,2,4>>]),
+ ?line 1251 = Module:longest_common_prefix(
+ [make_unaligned(<<0:10000,1,2,4>>),
+ <<0:10000,1,2,3>>,
+ make_unaligned(<<0:10000,1,3,3>>),
+ <<0:10000,1,2,4>>]),
+ ?line 12501 = Module:longest_common_prefix(
+ [<<0:100000,1,2,4>>,
+ make_unaligned(<<0:100000,1,2,3>>),
+ <<0:100000,1,3,3>>,
+ make_unaligned(<<0:100000,1,2,4>>)]),
+ ?line 1250001 = Module:longest_common_prefix([<<0:10000000,1,2,4>>,
+ <<0:10000000,1,2,3>>,
+ <<0:10000000,1,3,3>>,
+ <<0:10000000,1,2,4>>]),
+ if % Too cruel for the reference implementation
+ Module =:= binary ->
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",
+ [erts_debug:set_internal_state(
+ binary_loop_limit,100)]),
+ ?line 1250001 = Module:longest_common_prefix(
+ [<<0:10000000,1,2,4>>,
+ <<0:10000000,1,2,3>>,
+ <<0:10000000,1,3,3>>,
+ <<0:10000000,1,2,4>>]),
+ ?line io:format("limit was: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,
+ default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,
+ false);
+ true ->
+ ok
+ end,
+ ?line 1 = Module:longest_common_suffix([<<0:100000000,1,2,4,5>>,
+ <<0:100000000,1,2,3,5>>,
+ <<0:100000000,1,3,3,5>>,
+ <<0:100000000,1,2,4,5>>]),
+ ?line 1 = Module:longest_common_suffix([<<1,2,4,5>>,
+ <<0:100000000,1,2,3,5>>,
+ <<0:100000000,1,3,3,5>>,
+ <<0:100000000,1,2,4,5>>]),
+ ?line 1 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4>>]),
+ ?line 2 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5,5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 1 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<5>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<1,2,4,5,5>>,<<>>,
+ <<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<>>,<<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<>>,<<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 2 = Module:longest_common_suffix([<<5,5>>,<<0:100000000,1,3,3,5,5>>,
+ <<0:100000000,1,2,4,5,5>>]),
+ ?line 2 = Module:longest_common_suffix([<<5,5>>,<<5,5>>,<<4,5,5>>]),
+ ?line 2 = Module:longest_common_suffix([<<5,5>>,<<5,5>>,<<5,5>>]),
+ ?line 3 = Module:longest_common_suffix([<<4,5,5>>,<<4,5,5>>,<<4,5,5>>]),
+ ?line 0 = Module:longest_common_suffix([<<>>]),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([apa])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([[<<>>]])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_suffix([[<<0>>,
+ <<1:9>>]])),
+ ?line 0 = Module:longest_common_prefix([<<>>]),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([apa])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([[<<>>]])),
+ ?line badarg = ?MASK_ERROR(Module:longest_common_prefix([[<<0>>,
+ <<1:9>>]])),
+
+ ?line <<1:6,Bin:3/binary,_:2>> = <<1:6,1,2,3,1:2>>,
+ ?line <<1,2,3>> = Bin,
+ ?line 1 = Module:first(Bin),
+ ?line 1 = Module:first(<<1>>),
+ ?line 1 = Module:first(<<1,2,3>>),
+ ?line badarg = ?MASK_ERROR(Module:first(<<>>)),
+ ?line badarg = ?MASK_ERROR(Module:first(apa)),
+ ?line 3 = Module:last(Bin),
+ ?line 1 = Module:last(<<1>>),
+ ?line 3 = Module:last(<<1,2,3>>),
+ ?line badarg = ?MASK_ERROR(Module:last(<<>>)),
+ ?line badarg = ?MASK_ERROR(Module:last(apa)),
+ ?line 1 = Module:at(Bin,0),
+ ?line 1 = Module:at(<<1>>,0),
+ ?line 1 = Module:at(<<1,2,3>>,0),
+ ?line 2 = Module:at(<<1,2,3>>,1),
+ ?line 3 = Module:at(<<1,2,3>>,2),
+ ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,3)),
+ ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,-1)),
+ ?line badarg = ?MASK_ERROR(Module:at(<<1,2,3>>,apa)),
+ ?line "hejsan" = [ Module:at(<<"hejsan">>,I) || I <- lists:seq(0,5) ],
+
+ ?line badarg = ?MASK_ERROR(Module:bin_to_list(<<1,2,3>>,3,-4)),
+ ?line [1,2,3] = ?MASK_ERROR(Module:bin_to_list(<<1,2,3>>,3,-3)),
+
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(<<1,2,1:2>>,big)),
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(<<1,2,1:2>>,little)),
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(apa)),
+ ?line badarg = ?MASK_ERROR(Module:decode_unsigned(125,little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<>>,little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<>>,big)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<0>>,little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(<<0>>,big)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(make_unaligned(<<0>>),
+ little)),
+ ?line 0 = ?MASK_ERROR(Module:decode_unsigned(make_unaligned(<<0>>),big)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned(apa)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned(125.3,little)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned({1},little)),
+ ?line badarg = ?MASK_ERROR(Module:encode_unsigned([1],little)),
+ ?line <<0>> = ?MASK_ERROR(Module:encode_unsigned(0,little)),
+ ?line <<0>> = ?MASK_ERROR(Module:encode_unsigned(0,big)),
+ ok.
+
+encode_decode(doc) ->
+ ["test binary:encode_unsigned/1,2 and binary:decode_unsigned/1,2"];
+encode_decode(Config) when is_list(Config) ->
+ ?line random:seed({1271,769940,559934}),
+ ?line ok = encode_decode_loop({1,200},1000), % Need to be long enough
+ % to create offheap binaries
+ ok.
+
+encode_decode_loop(_Range,0) ->
+ ok;
+encode_decode_loop(Range, X) ->
+ ?line N = random_number(Range),
+ ?line A = binary:encode_unsigned(N),
+ ?line B = binary:encode_unsigned(N,big),
+ ?line C = binref:encode_unsigned(N),
+ ?line D = binref:encode_unsigned(N,big),
+ ?line E = binary:encode_unsigned(N,little),
+ ?line F = binref:encode_unsigned(N,little),
+ ?line G = binary:decode_unsigned(A),
+ ?line H = binary:decode_unsigned(A,big),
+ ?line I = binref:decode_unsigned(A),
+ ?line J = binary:decode_unsigned(E,little),
+ ?line K = binref:decode_unsigned(E,little),
+ ?line L = binary:decode_unsigned(make_unaligned(A)),
+ ?line M = binary:decode_unsigned(make_unaligned(E),little),
+ ?line PaddedBig = <<0:48,A/binary>>,
+ ?line PaddedLittle = <<E/binary,0:48>>,
+ ?line O = binary:decode_unsigned(PaddedBig),
+ ?line P = binary:decode_unsigned(make_unaligned(PaddedBig)),
+ ?line Q = binary:decode_unsigned(PaddedLittle,little),
+ ?line R = binary:decode_unsigned(make_unaligned(PaddedLittle),little),
+ ?line S = binref:decode_unsigned(PaddedLittle,little),
+ ?line T = binref:decode_unsigned(PaddedBig),
+ case (((A =:= B) and (B =:= C) and (C =:= D)) and
+ ((E =:= F)) and
+ ((N =:= G) and (G =:= H) and (H =:= I) and
+ (I =:= J) and (J =:= K) and (K =:= L) and (L =:= M)) and
+ ((M =:= O) and (O =:= P) and (P =:= Q) and (Q =:= R) and
+ (R =:= S) and (S =:= T)))of
+ true ->
+ encode_decode_loop(Range,X-1);
+ _ ->
+ io:format("Failed to encode/decode ~w~n(Results ~p)~n",
+ [N,[A,B,C,D,E,F,G,H,I,J,K,L,M,x,O,P,Q,R,S,T]]),
+ exit(mismatch)
+ end.
+
+guard(doc) ->
+ ["Smoke test of the guard BIFs binary_part/2,3"];
+guard(Config) when is_list(Config) ->
+ {comment, "Guard tests are run in emulator test suite"}.
+
+referenced(doc) ->
+ ["Test refernced_byte_size/1 bif."];
+referenced(Config) when is_list(Config) ->
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size([])),
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size(apa)),
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size({})),
+ ?line badarg = ?MASK_ERROR(binary:referenced_byte_size(1)),
+ ?line A = <<1,2,3>>,
+ ?line B = binary:copy(A,1000),
+ ?line 3 = binary:referenced_byte_size(A),
+ ?line 3000 = binary:referenced_byte_size(B),
+ ?line <<_:8,C:2/binary>> = A,
+ ?line 3 = binary:referenced_byte_size(C),
+ ?line 2 = binary:referenced_byte_size(binary:copy(C)),
+ ?line <<_:7,D:2/binary,_:1>> = A,
+ ?line 2 = binary:referenced_byte_size(binary:copy(D)),
+ ?line 3 = binary:referenced_byte_size(D),
+ ?line <<_:8,E:2/binary,_/binary>> = B,
+ ?line 3000 = binary:referenced_byte_size(E),
+ ?line 2 = binary:referenced_byte_size(binary:copy(E)),
+ ?line <<_:7,F:2/binary,_:1,_/binary>> = B,
+ ?line 2 = binary:referenced_byte_size(binary:copy(F)),
+ ?line 3000 = binary:referenced_byte_size(F),
+ ok.
+
+
+
+list_to_bin(doc) ->
+ ["Test list_to_bin/1 bif"];
+list_to_bin(Config) when is_list(Config) ->
+ %% Just some smoke_tests first, then go nuts with random cases
+ ?line badarg = ?MASK_ERROR(binary:list_to_bin({})),
+ ?line badarg = ?MASK_ERROR(binary:list_to_bin(apa)),
+ ?line badarg = ?MASK_ERROR(binary:list_to_bin(<<"apa">>)),
+ F1 = fun(L) ->
+ ?MASK_ERROR(binref:list_to_bin(L))
+ end,
+ F2 = fun(L) ->
+ ?MASK_ERROR(binary:list_to_bin(L))
+ end,
+ ?line random_iolist:run(1000,F1,F2),
+ ok.
+
+copy(doc) ->
+ ["Test copy/1,2 bif's"];
+copy(Config) when is_list(Config) ->
+ ?line <<1,2,3>> = binary:copy(<<1,2,3>>),
+ ?line RS = random_string({1,10000}),
+ ?line RS = RS2 = binary:copy(RS),
+ ?line false = erts_debug:same(RS,RS2),
+ ?line <<>> = ?MASK_ERROR(binary:copy(<<1,2,3>>,0)),
+ ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3:3>>,2)),
+ ?line badarg = ?MASK_ERROR(binary:copy([],0)),
+ ?line <<>> = ?MASK_ERROR(binary:copy(<<>>,0)),
+ ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3>>,1.0)),
+ ?line badarg = ?MASK_ERROR(binary:copy(<<1,2,3>>,
+ 16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)),
+ ?line <<>> = binary:copy(<<>>,10000),
+ ?line random:seed({1271,769940,559934}),
+ ?line ok = random_copy(3000),
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,10)]),
+ ?line Subj = subj(),
+ ?line XX = binary:copy(Subj,1000),
+ ?line XX = binref:copy(Subj,1000),
+ ?line ok = random_copy(1000),
+ ?line kill_copy_loop(1000),
+ ?line io:format("limit was: ~p~n",
+ [erts_debug:set_internal_state(binary_loop_limit,
+ default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,false),
+ ok.
+
+kill_copy_loop(0) ->
+ ok;
+kill_copy_loop(N) ->
+ {Pid,Ref} = spawn_monitor(fun() ->
+ ok = random_copy(1000)
+ end),
+ receive
+ after 10 ->
+ ok
+ end,
+ exit(Pid,kill),
+ receive
+ {'DOWN',Ref,process,Pid,_} ->
+ kill_copy_loop(N-1)
+ after 1000 ->
+ exit(did_not_die)
+ end.
+
+random_copy(0) ->
+ ok;
+random_copy(N) ->
+ Str = random_string({0,N}),
+ Num = random:uniform(N div 10+1),
+ A = ?MASK_ERROR(binary:copy(Str,Num)),
+ B = ?MASK_ERROR(binref:copy(Str,Num)),
+ C = ?MASK_ERROR(binary:copy(make_unaligned(Str),Num)),
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ random_copy(N-1);
+ _ ->
+ io:format("Failed to pick copy ~s ~p times~n",
+ [Str,Num]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+
+bin_to_list(doc) ->
+ ["Test bin_to_list/1,2,3 bif's"];
+bin_to_list(Config) when is_list(Config) ->
+ %% Just some smoke_tests first, then go nuts with random cases
+ ?line X = <<1,2,3,4,0:1000000,5>>,
+ ?line Y = make_unaligned(X),
+ ?line LX = binary:bin_to_list(X),
+ ?line LX = binary:bin_to_list(X,0,byte_size(X)),
+ ?line LX = binary:bin_to_list(X,byte_size(X),-byte_size(X)),
+ ?line LX = binary:bin_to_list(X,{0,byte_size(X)}),
+ ?line LX = binary:bin_to_list(X,{byte_size(X),-byte_size(X)}),
+ ?line LY = binary:bin_to_list(Y),
+ ?line LY = binary:bin_to_list(Y,0,byte_size(Y)),
+ ?line LY = binary:bin_to_list(Y,byte_size(Y),-byte_size(Y)),
+ ?line LY = binary:bin_to_list(Y,{0,byte_size(Y)}),
+ ?line LY = binary:bin_to_list(Y,{byte_size(Y),-byte_size(Y)}),
+ ?line 1 = hd(LX),
+ ?line 5 = lists:last(LX),
+ ?line 1 = hd(LY),
+ ?line 5 = lists:last(LY),
+ ?line X = list_to_binary(LY),
+ ?line Y = list_to_binary(LY),
+ ?line X = list_to_binary(LY),
+ ?line [5] = lists:nthtail(byte_size(X)-1,LX),
+ ?line [0,5] = lists:nthtail(byte_size(X)-2,LX),
+ ?line [0,5] = lists:nthtail(byte_size(Y)-2,LY),
+ ?line random:seed({1271,769940,559934}),
+ ?line ok = random_bin_to_list(5000),
+ ok.
+
+random_bin_to_list(0) ->
+ ok;
+random_bin_to_list(N) ->
+ Str = random_string({1,N}),
+ Parts0 = random_parts(10,N),
+ Parts1 = Parts0 ++ [ {X+Y,-Y} || {X,Y} <- Parts0 ],
+ [ begin
+ try
+ true = ?MASK_ERROR(binary:bin_to_list(Str,Z)) =:=
+ ?MASK_ERROR(binref:bin_to_list(Str,Z)),
+ true = ?MASK_ERROR(binary:bin_to_list(Str,Z)) =:=
+ ?MASK_ERROR(binary:bin_to_list(make_unaligned(Str),Z))
+ catch
+ _:_ ->
+ io:format("Error, Str = <<\"~s\">>.~nZ = ~p.~n",
+ [Str,Z]),
+ exit(badresult)
+ end
+ end || Z <- Parts1 ],
+ [ begin
+ try
+ true = ?MASK_ERROR(binary:bin_to_list(Str,A,B)) =:=
+ ?MASK_ERROR(binref:bin_to_list(Str,A,B)),
+ true = ?MASK_ERROR(binary:bin_to_list(Str,A,B)) =:=
+ ?MASK_ERROR(binary:bin_to_list(make_unaligned(Str),A,B))
+ catch
+ _:_ ->
+ io:format("Error, Str = <<\"~s\">>.~nA = ~p.~nB = ~p.~n",
+ [Str,A,B]),
+ exit(badresult)
+ end
+ end || {A,B} <- Parts1 ],
+ random_bin_to_list(N-1).
+
+parts(doc) ->
+ ["Test the part/2,3 bif's"];
+parts(Config) when is_list(Config) ->
+ %% Some simple smoke tests to begin with
+ ?line Simple = <<1,2,3,4,5,6,7,8>>,
+ ?line <<1,2>> = binary:part(Simple,0,2),
+ ?line <<1,2>> = binary:part(Simple,{0,2}),
+ ?line Simple = binary:part(Simple,0,8),
+ ?line Simple = binary:part(Simple,{0,8}),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,0,9)),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{0,9})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,1,8)),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{1,8})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{3,-4})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{3.0,1})),
+ ?line badarg = ?MASK_ERROR(
+ binary:part(Simple,{16#FFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+ ,1})),
+ ?line <<2,3,4,5,6,7,8>> = binary:part(Simple,{1,7}),
+ ?line <<2,3,4,5,6,7,8>> = binary:part(Simple,{8,-7}),
+ ?line Simple = binary:part(Simple,{8,-8}),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{1,-8})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{8,-9})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{0,-1})),
+ ?line <<>> = binary:part(Simple,{8,0}),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{9,0})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{-1,0})),
+ ?line badarg = ?MASK_ERROR(binary:part(Simple,{7,2})),
+ ?line <<8>> = binary:part(Simple,{7,1}),
+ ?line random:seed({1271,769940,559934}),
+ ?line random_parts(5000),
+ ok.
+
+
+random_parts(0) ->
+ ok;
+random_parts(N) ->
+ Str = random_string({1,N}),
+ Parts0 = random_parts(10,N),
+ Parts1 = Parts0 ++ [ {X+Y,-Y} || {X,Y} <- Parts0 ],
+ [ begin
+ true = ?MASK_ERROR(binary:part(Str,Z)) =:=
+ ?MASK_ERROR(binref:part(Str,Z)),
+ true = ?MASK_ERROR(binary:part(Str,Z)) =:=
+ ?MASK_ERROR(erlang:binary_part(Str,Z)),
+ true = ?MASK_ERROR(binary:part(Str,Z)) =:=
+ ?MASK_ERROR(binary:part(make_unaligned(Str),Z))
+ end || Z <- Parts1 ],
+ random_parts(N-1).
+
+random_parts(0,_) ->
+ [];
+random_parts(X,N) ->
+ Pos = random:uniform(N),
+ Len = random:uniform((Pos * 12) div 10),
+ [{Pos,Len} | random_parts(X-1,N)].
+
+random_ref_comp(doc) ->
+ ["Test pseudorandomly generated cases against reference imlementation"];
+random_ref_comp(Config) when is_list(Config) ->
+ ?line put(success_counter,0),
+ ?line random:seed({1271,769940,559934}),
+ ?line do_random_match_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_match_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_match_comp3(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_match_comp4(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_matches_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_matches_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_matches_comp3(5,{1,40},{30,1000}),
+ ?line erts_debug:set_internal_state(available_internal_state,true),
+ ?line io:format("oldlimit: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,100)]),
+ ?line do_random_match_comp(5000,{1,40},{30,1000}),
+ ?line do_random_matches_comp3(5,{1,40},{30,1000}),
+ ?line io:format("limit was: ~p~n",[ erts_debug:set_internal_state(binary_loop_limit,default)]),
+ ?line erts_debug:set_internal_state(available_internal_state,false),
+ ok.
+
+random_ref_sr_comp(doc) ->
+ ["Test pseudorandomly generated cases against reference imlementation of split and replace"];
+random_ref_sr_comp(Config) when is_list(Config) ->
+ ?line put(success_counter,0),
+ ?line random:seed({1271,769940,559934}),
+ ?line do_random_split_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_replace_comp(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_split_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ?line do_random_replace_comp2(5000,{1,40},{30,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ok.
+random_ref_fla_comp(doc) ->
+ ["Test pseudorandomly generated cases against reference imlementation of split and replace"];
+random_ref_fla_comp(Config) when is_list(Config) ->
+ ?line put(success_counter,0),
+ ?line random:seed({1271,769940,559934}),
+ ?line do_random_first_comp(5000,{1,1000}),
+ ?line do_random_last_comp(5000,{1,1000}),
+ ?line do_random_at_comp(5000,{1,1000}),
+ io:format("Number of successes: ~p~n",[get(success_counter)]),
+ ok.
+
+do_random_first_comp(0,_) ->
+ ok;
+do_random_first_comp(N,Range) ->
+ S = random_string(Range),
+ A = ?MASK_ERROR(binref:first(S)),
+ B = ?MASK_ERROR(binary:first(S)),
+ C = ?MASK_ERROR(binary:first(make_unaligned(S))),
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ do_random_first_comp(N-1,Range);
+ _ ->
+ io:format("Failed to pick first of ~s~n",
+ [S]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+
+do_random_last_comp(0,_) ->
+ ok;
+do_random_last_comp(N,Range) ->
+ S = random_string(Range),
+ A = ?MASK_ERROR(binref:last(S)),
+ B = ?MASK_ERROR(binary:last(S)),
+ C = ?MASK_ERROR(binary:last(make_unaligned(S))),
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ do_random_last_comp(N-1,Range);
+ _ ->
+ io:format("Failed to pick last of ~s~n",
+ [S]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+do_random_at_comp(0,_) ->
+ ok;
+do_random_at_comp(N,{Min,Max}=Range) ->
+ S = random_string(Range),
+ XMax = Min + ((Max - Min) * 3) div 4,
+ Pos = random_length({Min,XMax}), %% some out of range
+ A = ?MASK_ERROR(binref:at(S,Pos)),
+ B = ?MASK_ERROR(binary:at(S,Pos)),
+ C = ?MASK_ERROR(binary:at(make_unaligned(S),Pos)),
+ if
+ A =/= badarg ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C)} of
+ {true,true} ->
+ do_random_at_comp(N-1,Range);
+ _ ->
+ io:format("Failed to pick last of ~s~n",
+ [S]),
+ io:format("A:~p,~nB:~p,~n,C:~p.~n",
+ [A,B,C]),
+ exit(mismatch)
+ end.
+
+do_random_matches_comp(0,_,_) ->
+ ok;
+do_random_matches_comp(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Needles = [random_string(NeedleRange) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ Haystack = random_string(HaystackRange),
+ true = do_matches_comp(Needles,Haystack),
+ do_random_matches_comp(N-1,NeedleRange,HaystackRange).
+
+do_random_matches_comp2(0,_,_) ->
+ ok;
+do_random_matches_comp2(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_matches_comp(Needles,Haystack),
+ do_random_matches_comp2(N-1,NeedleRange,HaystackRange).
+
+do_random_matches_comp3(0,_,_) ->
+ ok;
+do_random_matches_comp3(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ RefRes = binref:matches(Haystack,Needles),
+ true = do_matches_comp_loop(10000,Needles,Haystack, RefRes),
+ do_random_matches_comp3(N-1,NeedleRange,HaystackRange).
+
+do_matches_comp_loop(0,_,_,_) ->
+ true;
+do_matches_comp_loop(N, Needles, Haystack0,RR) ->
+ DummySize=N*8,
+ Haystack1 = <<0:DummySize,Haystack0/binary>>,
+ RR1=[{X+N,Y} || {X,Y} <- RR],
+ true = do_matches_comp2(Needles,Haystack1,RR1),
+ Haystack2 = <<Haystack0/binary,Haystack1/binary>>,
+ RR2 = RR ++ [{X2+N+byte_size(Haystack0),Y2} || {X2,Y2} <- RR],
+ true = do_matches_comp2(Needles,Haystack2,RR2),
+ do_matches_comp_loop(N-1, Needles, Haystack0,RR).
+
+
+do_matches_comp2(N,H,A) ->
+ C = ?MASK_ERROR(binary:matches(H,N)),
+ case (A =:= C) of
+ true ->
+ true;
+ _ ->
+ io:format("Failed to match ~p (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~n,C:~p.~n",
+ [A,C]),
+ exit(mismatch)
+ end.
+do_matches_comp(N,H) ->
+ A = ?MASK_ERROR(binref:matches(H,N)),
+ B = ?MASK_ERROR(binref:matches(H,binref:compile_pattern(N))),
+ C = ?MASK_ERROR(binary:matches(H,N)),
+ D = ?MASK_ERROR(binary:matches(make_unaligned(H),
+ binary:compile_pattern([make_unaligned2(X) || X <- N]))),
+ if
+ A =/= nomatch ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C),(C =:= D)} of
+ {true,true,true} ->
+ true;
+ _ ->
+ io:format("Failed to match ~p (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p.~n",
+ [A,B,C,D]),
+ exit(mismatch)
+ end.
+
+do_random_match_comp(0,_,_) ->
+ ok;
+do_random_match_comp(N,NeedleRange,HaystackRange) ->
+ Needle = random_string(NeedleRange),
+ Haystack = random_string(HaystackRange),
+ true = do_match_comp(Needle,Haystack),
+ do_random_match_comp(N-1,NeedleRange,HaystackRange).
+
+do_random_match_comp2(0,_,_) ->
+ ok;
+do_random_match_comp2(N,NeedleRange,HaystackRange) ->
+ Haystack = random_string(HaystackRange),
+ Needle = random_substring(NeedleRange,Haystack),
+ true = do_match_comp(Needle,Haystack),
+ do_random_match_comp2(N-1,NeedleRange,HaystackRange).
+
+do_random_match_comp3(0,_,_) ->
+ ok;
+do_random_match_comp3(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_match_comp3(Needles,Haystack),
+ do_random_match_comp3(N-1,NeedleRange,HaystackRange).
+
+do_random_match_comp4(0,_,_) ->
+ ok;
+do_random_match_comp4(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_string(NeedleRange) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_match_comp3(Needles,Haystack),
+ do_random_match_comp4(N-1,NeedleRange,HaystackRange).
+
+do_match_comp(N,H) ->
+ A = ?MASK_ERROR(binref:match(H,N)),
+ B = ?MASK_ERROR(binref:match(H,binref:compile_pattern([N]))),
+ C = ?MASK_ERROR(binary:match(make_unaligned(H),N)),
+ D = ?MASK_ERROR(binary:match(H,binary:compile_pattern([N]))),
+ E = ?MASK_ERROR(binary:match(H,binary:compile_pattern(make_unaligned(N)))),
+ if
+ A =/= nomatch ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C),(C =:= D),(D =:= E)} of
+ {true,true,true,true} ->
+ true;
+ _ ->
+ io:format("Failed to match ~s (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p,E:~p.~n",
+ [A,B,C,D,E]),
+ exit(mismatch)
+ end.
+
+do_match_comp3(N,H) ->
+ A = ?MASK_ERROR(binref:match(H,N)),
+ B = ?MASK_ERROR(binref:match(H,binref:compile_pattern(N))),
+ C = ?MASK_ERROR(binary:match(H,N)),
+ D = ?MASK_ERROR(binary:match(H,binary:compile_pattern(N))),
+ if
+ A =/= nomatch ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case {(A =:= B), (B =:= C),(C =:= D)} of
+ {true,true,true} ->
+ true;
+ _ ->
+ io:format("Failed to match ~s (needle) against ~s (haystack)~n",
+ [N,H]),
+ io:format("A:~p,~nB:~p,~n,C:~p,~n,D:~p.~n",
+ [A,B,C,D]),
+ exit(mismatch)
+ end.
+
+do_random_split_comp(0,_,_) ->
+ ok;
+do_random_split_comp(N,NeedleRange,HaystackRange) ->
+ Haystack = random_string(HaystackRange),
+ Needle = random_substring(NeedleRange,Haystack),
+ true = do_split_comp(Needle,Haystack,[]),
+ true = do_split_comp(Needle,Haystack,[global]),
+ true = do_split_comp(Needle,Haystack,[global,trim]),
+ do_random_split_comp(N-1,NeedleRange,HaystackRange).
+do_random_split_comp2(0,_,_) ->
+ ok;
+do_random_split_comp2(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ true = do_split_comp(Needles,Haystack,[]),
+ true = do_split_comp(Needles,Haystack,[global]),
+ do_random_split_comp2(N-1,NeedleRange,HaystackRange).
+
+do_split_comp(N,H,Opts) ->
+ A = ?MASK_ERROR(binref:split(H,N,Opts)),
+ D = ?MASK_ERROR(binary:split(H,binary:compile_pattern(N),Opts)),
+ if
+ (A =/= [N]) and is_list(A) ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case (A =:= D) of
+ true ->
+ true;
+ _ ->
+ io:format("Failed to split ~n~p ~n(haystack) with ~n~p ~n(needle) "
+ "~nand options ~p~n",
+ [H,N,Opts]),
+ io:format("A:~p,D:~p.~n",
+ [A,D]),
+ exit(mismatch)
+ end.
+
+do_random_replace_comp(0,_,_) ->
+ ok;
+do_random_replace_comp(N,NeedleRange,HaystackRange) ->
+ Haystack = random_string(HaystackRange),
+ Needle = random_substring(NeedleRange,Haystack),
+ Repl = random_string(NeedleRange),
+ Insertat = random_length(NeedleRange), %Sometimes larger than Repl
+ true = do_replace_comp(Needle,Haystack,Repl,[]),
+ true = do_replace_comp(Needle,Haystack,Repl,[global]),
+ true = do_replace_comp(Needle,Haystack,Repl,
+ [global,{insert_replaced,Insertat}]),
+ do_random_replace_comp(N-1,NeedleRange,HaystackRange).
+do_random_replace_comp2(0,_,_) ->
+ ok;
+do_random_replace_comp2(N,NeedleRange,HaystackRange) ->
+ NumNeedles = element(2,HaystackRange) div element(2,NeedleRange),
+ Haystack = random_string(HaystackRange),
+ Needles = [random_substring(NeedleRange,Haystack) ||
+ _ <- lists:duplicate(NumNeedles,a)],
+ Repl = random_string(NeedleRange),
+ Insertat = random_length(NeedleRange), %Sometimes larger than Repl
+ true = do_replace_comp(Needles,Haystack,Repl,[]),
+ true = do_replace_comp(Needles,Haystack,Repl,[global]),
+ true = do_replace_comp(Needles,Haystack,Repl,
+ [global,{insert_replaced,Insertat}]),
+ do_random_replace_comp2(N-1,NeedleRange,HaystackRange).
+
+do_replace_comp(N,H,R,Opts) ->
+ A = ?MASK_ERROR(binref:replace(H,N,R,Opts)),
+ D = ?MASK_ERROR(binary:replace(H,binary:compile_pattern(N),R,Opts)),
+ if
+ (A =/= N) and is_binary(A) ->
+ put(success_counter,get(success_counter)+1);
+ true ->
+ ok
+ end,
+ case (A =:= D) of
+ true ->
+ true;
+ _ ->
+ io:format("Failed to replace ~s (haystack) by ~s (needle) "
+ "inserting ~s (replacement) and options ~p~n",
+ [H,N,R,Opts]),
+ io:format("A:~p,D:~p.~n",
+ [A,D]),
+ exit(mismatch)
+ end.
+
+one_random_number(N) ->
+ M = ((N - 1) rem 10) + 1,
+ element(M,{$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}).
+
+one_random(N) ->
+ M = ((N - 1) rem 68) + 1,
+ element(M,{$a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l,$m,$n,$o,$p,$q,$r,$s,$t,
+ $u,$v,$w,$x,$y,$z,$�,$�,$�,$A,$B,$C,$D,$E,$F,$G,$H,
+ $I,$J,$K,$L,$M,$N,$O,$P,$Q,$R,$S,$T,$U,$V,$W,$X,$Y,$Z,$�,
+ $�,$�,$0,$1,$2,$3,$4,$5,$6,$7,$8,$9}).
+
+random_number({Min,Max}) -> % Min and Max are *length* of number in
+ % decimal positions
+ X = random:uniform(Max - Min + 1) + Min - 1,
+ list_to_integer([one_random_number(random:uniform(10)) || _ <- lists:seq(1,X)]).
+
+
+random_length({Min,Max}) ->
+ random:uniform(Max - Min + 1) + Min - 1.
+random_string({Min,Max}) ->
+ X = random:uniform(Max - Min + 1) + Min - 1,
+ list_to_binary([one_random(random:uniform(68)) || _ <- lists:seq(1,X)]).
+random_substring({Min,Max},Hay) ->
+ X = random:uniform(Max - Min + 1) + Min - 1,
+ Y = byte_size(Hay),
+ Z = if
+ X > Y -> Y;
+ true -> X
+ end,
+ PMax = Y - Z,
+ Pos = random:uniform(PMax + 1) - 1,
+ <<_:Pos/binary,Res:Z/binary,_/binary>> = Hay,
+ Res.
+
+mask_error({'EXIT',{Err,_}}) ->
+ Err;
+mask_error(Else) ->
+ Else.
+
+make_unaligned(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<0:3,Bin0/binary,31:5>>,
+ Sz = byte_size(Bin0),
+ <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
+ Bin.
+make_unaligned2(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<31:5,Bin0/binary,0:3>>,
+ Sz = byte_size(Bin0),
+ <<31:5,Bin:Sz/binary,0:3>> = id(Bin1),
+ Bin.
+
+id(I) -> I.
diff --git a/lib/stdlib/test/binref.erl b/lib/stdlib/test/binref.erl
new file mode 100644
index 0000000000..6d96736ef3
--- /dev/null
+++ b/lib/stdlib/test/binref.erl
@@ -0,0 +1,588 @@
+-module(binref).
+
+-export([compile_pattern/1,match/2,match/3,matches/2,matches/3,
+ split/2,split/3,replace/3,replace/4,first/1,last/1,at/2,
+ part/2,part/3,copy/1,copy/2,encode_unsigned/1,encode_unsigned/2,
+ decode_unsigned/1,decode_unsigned/2,referenced_byte_size/1,
+ longest_common_prefix/1,longest_common_suffix/1,bin_to_list/1,
+ bin_to_list/2,bin_to_list/3,list_to_bin/1]).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% compile_pattern, a dummy
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+compile_pattern(Pattern) when is_binary(Pattern) ->
+ {[Pattern]};
+compile_pattern(Pattern) ->
+ try
+ [ true = is_binary(P) || P <- Pattern ],
+ {Pattern}
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% match and matches
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+match(H,N) ->
+ match(H,N,[]).
+match(Haystack,Needle,Options) when is_binary(Needle) ->
+ match(Haystack,[Needle],Options);
+match(Haystack,{Needles},Options) ->
+ match(Haystack,Needles,Options);
+match(Haystack,Needles,Options) ->
+ try
+ true = is_binary(Haystack) and is_list(Needles), % badarg, not function_clause
+ case get_opts_match(Options,nomatch) of
+ nomatch ->
+ mloop(Haystack,Needles);
+ {A,B} when B > 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ mloop(SubStack,Needles,A,B+A);
+ {A,B} when B < 0 ->
+ Start = A + B,
+ Len = -B,
+ <<_:Start/binary,SubStack:Len/binary,_/binary>> = Haystack,
+ mloop(SubStack,Needles,Start,Len+Start);
+ _ ->
+ nomatch
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+matches(H,N) ->
+ matches(H,N,[]).
+matches(Haystack,Needle,Options) when is_binary(Needle) ->
+ matches(Haystack,[Needle],Options);
+matches(Haystack,{Needles},Options) ->
+ matches(Haystack,Needles,Options);
+matches(Haystack,Needles,Options) ->
+ try
+ true = is_binary(Haystack) and is_list(Needles), % badarg, not function_clause
+ case get_opts_match(Options,nomatch) of
+ nomatch ->
+ msloop(Haystack,Needles);
+ {A,B} when B > 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ msloop(SubStack,Needles,A,B+A);
+ {A,B} when B < 0 ->
+ Start = A + B,
+ Len = -B,
+ <<_:Start/binary,SubStack:Len/binary,_/binary>> = Haystack,
+ msloop(SubStack,Needles,Start,Len+Start);
+ _ ->
+ []
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+mloop(Haystack,Needles) ->
+ mloop(Haystack,Needles,0,byte_size(Haystack)).
+
+mloop(_Haystack,_Needles,N,M) when N >= M ->
+ nomatch;
+mloop(Haystack,Needles,N,M) ->
+ case mloop2(Haystack,Needles,N,nomatch) of
+ nomatch ->
+ % Not found
+ <<_:8,NewStack/binary>> = Haystack,
+ mloop(NewStack,Needles,N+1,M);
+ {N,Len} ->
+ {N,Len}
+ end.
+
+msloop(Haystack,Needles) ->
+ msloop(Haystack,Needles,0,byte_size(Haystack)).
+
+msloop(_Haystack,_Needles,N,M) when N >= M ->
+ [];
+msloop(Haystack,Needles,N,M) ->
+ case mloop2(Haystack,Needles,N,nomatch) of
+ nomatch ->
+ % Not found
+ <<_:8,NewStack/binary>> = Haystack,
+ msloop(NewStack,Needles,N+1,M);
+ {N,Len} ->
+ NewN = N+Len,
+ if
+ NewN >= M ->
+ [{N,Len}];
+ true ->
+ <<_:Len/binary,NewStack/binary>> = Haystack,
+ [{N,Len} | msloop(NewStack,Needles,NewN,M)]
+ end
+ end.
+
+mloop2(_Haystack,[],_N,Res) ->
+ Res;
+mloop2(Haystack,[Needle|Tail],N,Candidate) ->
+ NS = byte_size(Needle),
+ case Haystack of
+ <<Needle:NS/binary,_/binary>> ->
+ NewCandidate = case Candidate of
+ nomatch ->
+ {N,NS};
+ {N,ONS} when ONS < NS ->
+ {N,NS};
+ Better ->
+ Better
+ end,
+ mloop2(Haystack,Tail,N,NewCandidate);
+ _ ->
+ mloop2(Haystack,Tail,N,Candidate)
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% split
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+split(H,N) ->
+ split(H,N,[]).
+split(Haystack,{Needles},Options) ->
+ split(Haystack, Needles, Options);
+split(Haystack,Needles0,Options) ->
+ try
+ Needles = if
+ is_list(Needles0) ->
+ Needles0;
+ is_binary(Needles0) ->
+ [Needles0];
+ true ->
+ exit(badtype)
+ end,
+ {Part,Global,Trim} = get_opts_split(Options,{nomatch,false,false}),
+ {Start,End,NewStack} =
+ case Part of
+ nomatch ->
+ {0,byte_size(Haystack),Haystack};
+ {A,B} when B >= 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ {A,A+B,SubStack};
+ {A,B} when B < 0 ->
+ S = A + B,
+ L = -B,
+ <<_:S/binary,SubStack:L/binary,_/binary>> = Haystack,
+ {S,S+L,SubStack}
+ end,
+ MList = if
+ Global ->
+ msloop(NewStack,Needles,Start,End);
+ true ->
+ case mloop(NewStack,Needles,Start,End) of
+ nomatch ->
+ [];
+ X ->
+ [X]
+ end
+ end,
+ do_split(Haystack,MList,0,Trim)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_split(H,[],N,true) when N >= byte_size(H) ->
+ [];
+do_split(H,[],N,_) ->
+ [part(H,{N,byte_size(H)-N})];
+do_split(H,[{A,B}|T],N,Trim) ->
+ case part(H,{N,A-N}) of
+ <<>> ->
+ Rest = do_split(H,T,A+B,Trim),
+ case {Trim, Rest} of
+ {true,[]} ->
+ [];
+ _ ->
+ [<<>> | Rest]
+ end;
+ Oth ->
+ [Oth | do_split(H,T,A+B,Trim)]
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% replace
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+replace(H,N,R) ->
+ replace(H,N,R,[]).
+replace(Haystack,{Needles},Replacement,Options) ->
+ replace(Haystack,Needles,Replacement,Options);
+
+replace(Haystack,Needles0,Replacement,Options) ->
+ try
+ Needles = if
+ is_list(Needles0) ->
+ Needles0;
+ is_binary(Needles0) ->
+ [Needles0];
+ true ->
+ exit(badtype)
+ end,
+ true = is_binary(Replacement), % Make badarg instead of function clause
+ {Part,Global,Insert} = get_opts_replace(Options,{nomatch,false,[]}),
+ {Start,End,NewStack} =
+ case Part of
+ nomatch ->
+ {0,byte_size(Haystack),Haystack};
+ {A,B} when B >= 0 ->
+ <<_:A/binary,SubStack:B/binary,_/binary>> = Haystack,
+ {A,A+B,SubStack};
+ {A,B} when B < 0 ->
+ S = A + B,
+ L = -B,
+ <<_:S/binary,SubStack:L/binary,_/binary>> = Haystack,
+ {S,S+L,SubStack}
+ end,
+ MList = if
+ Global ->
+ msloop(NewStack,Needles,Start,End);
+ true ->
+ case mloop(NewStack,Needles,Start,End) of
+ nomatch ->
+ [];
+ X ->
+ [X]
+ end
+ end,
+ ReplList = case Insert of
+ [] ->
+ Replacement;
+ Y when is_integer(Y) ->
+ splitat(Replacement,0,[Y]);
+ Li when is_list(Li) ->
+ splitat(Replacement,0,lists:sort(Li))
+ end,
+ erlang:iolist_to_binary(do_replace(Haystack,MList,ReplList,0))
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+
+do_replace(H,[],_,N) ->
+ [part(H,{N,byte_size(H)-N})];
+do_replace(H,[{A,B}|T],Replacement,N) ->
+ [part(H,{N,A-N}),
+ if
+ is_list(Replacement) ->
+ do_insert(Replacement, part(H,{A,B}));
+ true ->
+ Replacement
+ end
+ | do_replace(H,T,Replacement,A+B)].
+
+do_insert([X],_) ->
+ [X];
+do_insert([H|T],R) ->
+ [H,R|do_insert(T,R)].
+
+splitat(H,N,[]) ->
+ [part(H,{N,byte_size(H)-N})];
+splitat(H,N,[I|T]) ->
+ [part(H,{N,I-N})|splitat(H,I,T)].
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% first, last and at
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+first(Subject) ->
+ try
+ <<A:8,_/binary>> = Subject,
+ A
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+last(Subject) ->
+ try
+ N = byte_size(Subject) - 1,
+ <<_:N/binary,A:8>> = Subject,
+ A
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+at(Subject,X) ->
+ try
+ <<_:X/binary,A:8,_/binary>> = Subject,
+ A
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% bin_to_list
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+bin_to_list(Subject) ->
+ try
+ binary_to_list(Subject)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+bin_to_list(Subject,T) ->
+ try
+ {A0,B0} = T,
+ {A,B} = if
+ B0 < 0 ->
+ {A0+B0,-B0};
+ true ->
+ {A0,B0}
+ end,
+ binary_to_list(Subject,A+1,A+B)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+bin_to_list(Subject,A,B) ->
+ try
+ bin_to_list(Subject,{A,B})
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% list_to_bin
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+list_to_bin(List) ->
+ try
+ erlang:list_to_binary(List)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% longest_common_prefix
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+longest_common_prefix(LB) ->
+ try
+ true = is_list(LB) and (length(LB) > 0), % Make badarg instead of function clause
+ do_longest_common_prefix(LB,0)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_longest_common_prefix(LB,X) ->
+ case do_lcp(LB,X,no) of
+ true ->
+ do_longest_common_prefix(LB,X+1);
+ false ->
+ X
+ end.
+do_lcp([],_,_) ->
+ true;
+do_lcp([Bin|_],X,_) when byte_size(Bin) =< X ->
+ false;
+do_lcp([Bin|T],X,no) ->
+ Ch = at(Bin,X),
+ do_lcp(T,X,Ch);
+do_lcp([Bin|T],X,Ch) ->
+ Ch2 = at(Bin,X),
+ if
+ Ch =:= Ch2 ->
+ do_lcp(T,X,Ch);
+ true ->
+ false
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% longest_common_suffix
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+longest_common_suffix(LB) ->
+ try
+ true = is_list(LB) and (length(LB) > 0), % Make badarg instead of function clause
+ do_longest_common_suffix(LB,0)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_longest_common_suffix(LB,X) ->
+ case do_lcs(LB,X,no) of
+ true ->
+ do_longest_common_suffix(LB,X+1);
+ false ->
+ X
+ end.
+do_lcs([],_,_) ->
+ true;
+do_lcs([Bin|_],X,_) when byte_size(Bin) =< X ->
+ false;
+do_lcs([Bin|T],X,no) ->
+ Ch = at(Bin,byte_size(Bin) - 1 - X),
+ do_lcs(T,X,Ch);
+do_lcs([Bin|T],X,Ch) ->
+ Ch2 = at(Bin,byte_size(Bin) - 1 - X),
+ if
+ Ch =:= Ch2 ->
+ do_lcs(T,X,Ch);
+ true ->
+ false
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% part
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+part(Subject,Part) ->
+ try
+ do_part(Subject,Part)
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+part(Subject,Pos,Len) ->
+ part(Subject,{Pos,Len}).
+
+do_part(Bin,{A,B}) when B >= 0 ->
+ <<_:A/binary,Sub:B/binary,_/binary>> = Bin,
+ Sub;
+do_part(Bin,{A,B}) when B < 0 ->
+ S = A + B,
+ L = -B,
+ <<_:S/binary,Sub:L/binary,_/binary>> = Bin,
+ Sub.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% copy
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+copy(Subject) ->
+ copy(Subject,1).
+copy(Subject,N) ->
+ try
+ true = is_integer(N) and (N >= 0) and is_binary(Subject), % Badarg, not function clause
+ erlang:list_to_binary(lists:duplicate(N,Subject))
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% encode_unsigned
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+encode_unsigned(Unsigned) ->
+ encode_unsigned(Unsigned,big).
+encode_unsigned(Unsigned,Endian) ->
+ try
+ true = is_integer(Unsigned) and (Unsigned >= 0),
+ if
+ Unsigned =:= 0 ->
+ <<0>>;
+ true ->
+ case Endian of
+ big ->
+ list_to_binary(do_encode(Unsigned,[]));
+ little ->
+ list_to_binary(do_encode_r(Unsigned))
+ end
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_encode(0,L) ->
+ L;
+do_encode(N,L) ->
+ Byte = N band 255,
+ NewN = N bsr 8,
+ do_encode(NewN,[Byte|L]).
+
+do_encode_r(0) ->
+ [];
+do_encode_r(N) ->
+ Byte = N band 255,
+ NewN = N bsr 8,
+ [Byte|do_encode_r(NewN)].
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% decode_unsigned
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+decode_unsigned(Subject) ->
+ decode_unsigned(Subject,big).
+
+decode_unsigned(Subject,Endian) ->
+ try
+ true = is_binary(Subject),
+ case Endian of
+ big ->
+ do_decode(Subject,0);
+ little ->
+ do_decode_r(Subject,0)
+ end
+ catch
+ _:_ ->
+ erlang:error(badarg)
+ end.
+
+do_decode(<<>>,N) ->
+ N;
+do_decode(<<X:8,Bin/binary>>,N) ->
+ do_decode(Bin,(N bsl 8) bor X).
+
+do_decode_r(<<>>,N) ->
+ N;
+do_decode_r(Bin,N) ->
+ Sz = byte_size(Bin) - 1,
+ <<NewBin:Sz/binary,X>> = Bin,
+ do_decode_r(NewBin, (N bsl 8) bor X).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% referenced_byte_size cannot
+%% be implemented in pure
+%% erlang
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+referenced_byte_size(Bin) when is_binary(Bin) ->
+ erlang:error(not_implemented);
+referenced_byte_size(_) ->
+ erlang:error(badarg).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Simple helper functions
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Option "parsing"
+get_opts_match([],Part) ->
+ Part;
+get_opts_match([{scope,{A,B}} | T],_Part) ->
+ get_opts_match(T,{A,B});
+get_opts_match(_,_) ->
+ throw(badopt).
+
+get_opts_split([],{Part,Global,Trim}) ->
+ {Part,Global,Trim};
+get_opts_split([{scope,{A,B}} | T],{_Part,Global,Trim}) ->
+ get_opts_split(T,{{A,B},Global,Trim});
+get_opts_split([global | T],{Part,_Global,Trim}) ->
+ get_opts_split(T,{Part,true,Trim});
+get_opts_split([trim | T],{Part,Global,_Trim}) ->
+ get_opts_split(T,{Part,Global,true});
+get_opts_split(_,_) ->
+ throw(badopt).
+
+get_opts_replace([],{Part,Global,Insert}) ->
+ {Part,Global,Insert};
+get_opts_replace([{scope,{A,B}} | T],{_Part,Global,Insert}) ->
+ get_opts_replace(T,{{A,B},Global,Insert});
+get_opts_replace([global | T],{Part,_Global,Insert}) ->
+ get_opts_replace(T,{Part,true,Insert});
+get_opts_replace([{insert_replaced,N} | T],{Part,Global,_Insert}) ->
+ get_opts_replace(T,{Part,Global,N});
+get_opts_replace(_,_) ->
+ throw(badopt).
diff --git a/lib/stdlib/test/c_SUITE.erl b/lib/stdlib/test/c_SUITE.erl
index 2edbc7ab4c..25281365be 100644
--- a/lib/stdlib/test/c_SUITE.erl
+++ b/lib/stdlib/test/c_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,18 +17,36 @@
%% %CopyrightEnd%
%%
-module(c_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([c_1/1, c_2/1, c_3/1, c_4/1, nc_1/1, nc_2/1, nc_3/1, nc_4/1,
memory/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-import(c, [c/2, nc/2]).
-all(doc) -> ["Test cases for the 'c' module."];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[c_1, c_2, c_3, c_4, nc_1, nc_2, nc_3, nc_4, memory].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%% Write output to a directory other than current directory:
c_1(doc) ->
diff --git a/lib/stdlib/test/calendar_SUITE.erl b/lib/stdlib/test/calendar_SUITE.erl
index 10fb72c1b1..8192d035ca 100644
--- a/lib/stdlib/test/calendar_SUITE.erl
+++ b/lib/stdlib/test/calendar_SUITE.erl
@@ -18,29 +18,43 @@
%%
-module(calendar_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
gregorian_days/1,
gregorian_seconds/1,
day_of_the_week/1,
day_of_the_week_calibrate/1,
leap_years/1,
last_day_of_the_month/1,
- local_time_to_universal_time_dst/1]).
+ local_time_to_universal_time_dst/1,
+ iso_week_number/1]).
-define(START_YEAR, 1947).
-define(END_YEAR, 2012).
-all(suite) -> [gregorian_days,
- gregorian_seconds,
- day_of_the_week,
- day_of_the_week_calibrate,
- leap_years,
- last_day_of_the_month,
- local_time_to_universal_time_dst];
+suite() -> [{ct_hooks,[ts_install_cth]}].
-all(doc) -> "This is the test suite for calendar.erl".
+all() ->
+ [gregorian_days, gregorian_seconds, day_of_the_week,
+ day_of_the_week_calibrate, leap_years,
+ last_day_of_the_month, local_time_to_universal_time_dst, iso_week_number].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
gregorian_days(doc) ->
"Tests that date_to_gregorian_days and gregorian_days_to_date "
@@ -156,6 +170,15 @@ local_time_to_universal_time_dst_x(Config) when is_list(Config) ->
{comment,"Bug in mktime() in this OS"}
end.
+iso_week_number(doc) ->
+ "Test the iso week number calculation for all three possibilities."
+ " When the date falls on the last week of the previous year,"
+ " when the date falls on a week within the given year and finally,"
+ " when the date falls on the first week of the next year.";
+iso_week_number(suite) ->
+ [];
+iso_week_number(Config) when is_list(Config) ->
+ ?line check_iso_week_number().
%%
%% LOCAL FUNCTIONS
@@ -245,7 +268,12 @@ check_last_day_of_the_month({SYr, SMon}, {EYr, EMon}) when SYr < EYr ->
check_last_day_of_the_month(_, _) ->
ok.
-
+%% check_iso_week_number
+%%
+check_iso_week_number() ->
+ ?line {2004, 53} = calendar:iso_week_number({2005, 1, 1}),
+ ?line {2007, 1} = calendar:iso_week_number({2007, 1, 1}),
+ ?line {2009, 1} = calendar:iso_week_number({2008, 12, 29}).
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 760e610e00..9fcc9e6aaf 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,13 +28,15 @@
-define(privdir(_), "./dets_SUITE_priv").
-define(datadir(_), "./dets_SUITE_data").
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(privdir(Conf), ?config(priv_dir, Conf)).
-define(datadir(Conf), ?config(data_dir, Conf)).
-endif.
--export([all/1, not_run/1, newly_started/1, basic_v8/1, basic_v9/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ not_run/1, newly_started/1, basic_v8/1, basic_v9/1,
open_v8/1, open_v9/1, sets_v8/1, sets_v9/1, bags_v8/1,
bags_v9/1, duplicate_bags_v8/1, duplicate_bags_v9/1,
access_v8/1, access_v9/1, dirty_mark/1, dirty_mark2/1,
@@ -50,13 +52,14 @@
otp_4208/1, otp_4989/1, many_clients/1, otp_4906/1, otp_5402/1,
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
- otp_8070/1]).
+ otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
+ otp_8923/1]).
-export([dets_dirty_loop/0]).
-export([histogram/1, sum_histogram/1, ave_histogram/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%% Internal export.
-export([client/2]).
@@ -82,35 +85,51 @@ init_per_testcase(_Case, Config) ->
Dog=?t:timetrap(?t:minutes(15)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog=?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
case os:type() of
- vxworks ->
- [not_run];
+ vxworks -> [not_run];
_ ->
- {req,[stdlib],
- [basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
- bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
- newly_started, open_file_v8, open_file_v9,
- init_table_v8, init_table_v9, repair_v8, repair_v9,
- access_v8, access_v9, oldbugs_v8, oldbugs_v9,
- unsafe_assumptions, truncated_segment_array_v8,
- truncated_segment_array_v9, dirty_mark, dirty_mark2,
- bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
- fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
- select_v8, select_v9, update_counter, badarg,
- cache_sets_v8, cache_sets_v9, cache_bags_v8,
- cache_bags_v9, cache_duplicate_bags_v8,
- cache_duplicate_bags_v9, otp_4208, otp_4989, many_clients,
- otp_4906, otp_5402, simultaneous_open, insert_new,
- repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738,
- otp_7146, otp_8070]}
+ [basic_v8, basic_v9, open_v8, open_v9, sets_v8, sets_v9,
+ bags_v8, bags_v9, duplicate_bags_v8, duplicate_bags_v9,
+ newly_started, open_file_v8, open_file_v9,
+ init_table_v8, init_table_v9, repair_v8, repair_v9,
+ access_v8, access_v9, oldbugs_v8, oldbugs_v9,
+ unsafe_assumptions, truncated_segment_array_v8,
+ truncated_segment_array_v9, dirty_mark, dirty_mark2,
+ bag_next_v8, bag_next_v9, hash_v8b_v8c, phash, fold_v8,
+ fold_v9, fixtable_v8, fixtable_v9, match_v8, match_v9,
+ select_v8, select_v9, update_counter, badarg,
+ cache_sets_v8, cache_sets_v9, cache_bags_v8,
+ cache_bags_v9, cache_duplicate_bags_v8,
+ cache_duplicate_bags_v9, otp_4208, otp_4989,
+ many_clients, otp_4906, otp_5402, simultaneous_open,
+ insert_new, repair_continuation, otp_5487, otp_6206,
+ otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
+ otp_8899, otp_8903, otp_8923]
end.
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
not_run(suite) -> [];
not_run(Conf) when is_list(Conf) ->
{comment, "Not runnable VxWorks/NFS"}.
@@ -2935,6 +2954,57 @@ ets_init(Tab, N) ->
ets:insert(Tab, {N,N}),
ets_init(Tab, N - 1).
+otp_8898(doc) ->
+ ["OTP-8898. Truncated Dets file."];
+otp_8898(suite) ->
+ [];
+otp_8898(Config) when is_list(Config) ->
+ Tab = otp_8898,
+ ?line FName = filename(Tab, Config),
+
+ Server = self(),
+
+ ?line file:delete(FName),
+ ?line {ok, _} = dets:open_file(Tab,[{file, FName}]),
+ ?line [P1,P2,P3] = new_clients(3, Tab),
+
+ Seq = [{P1,[sync]},{P2,[{lookup,1,[]}]},{P3,[{insert,{1,b}}]}],
+ ?line atomic_requests(Server, Tab, [[]], Seq),
+ ?line true = get_replies([{P1,ok},{P2,ok},{P3,ok}]),
+ ?line ok = dets:close(Tab),
+ ?line {ok, _} = dets:open_file(Tab,[{file, FName}]),
+ ?line file:delete(FName),
+
+ ok.
+
+otp_8899(doc) ->
+ ["OTP-8899. Several clients. Updated Head was ignored."];
+otp_8899(suite) ->
+ [];
+otp_8899(Config) when is_list(Config) ->
+ Tab = many_clients,
+ ?line FName = filename(Tab, Config),
+
+ Server = self(),
+
+ ?line file:delete(FName),
+ ?line {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]),
+ ?line [P1,P2,P3,P4] = new_clients(4, Tab),
+
+ MC = [Tab],
+ Seq6a = [{P1,[{insert,[{used_to_be_skipped_by,match}]},
+ {lookup,1,[{1,a}]}]},
+ {P2,[{verbose,true,MC}]},
+ {P3,[{lookup,1,[{1,a}]}]}, {P4,[{verbose,true,MC}]}],
+ ?line atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq6a),
+ ?line true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]),
+ ?line [{1,a},{2,b},{3,c},{used_to_be_skipped_by,match}] =
+ lists:sort(dets:match_object(Tab, '_')),
+ ?line _ = dets:close(Tab),
+ ?line file:delete(FName),
+
+ ok.
+
many_clients(doc) ->
["Several clients accessing a table simultaneously."];
many_clients(suite) ->
@@ -3071,6 +3141,11 @@ client(S, Tab) ->
eval([], _Tab) ->
ok;
+eval([{verbose,Bool,Expected} | L], Tab) ->
+ ?line case dets:verbose(Bool) of
+ Expected -> eval(L, Tab);
+ Error -> {error, {verbose,Error}}
+ end;
eval([sync | L], Tab) ->
?line case dets:sync(Tab) of
ok -> eval(L, Tab);
@@ -3701,6 +3776,87 @@ otp_8070(Config) when is_list(Config) ->
file:delete(File),
ok.
+otp_8856(doc) ->
+ ["OTP-8856. insert_new() bug."];
+otp_8856(suite) ->
+ [];
+otp_8856(Config) when is_list(Config) ->
+ Tab = otp_8856,
+ File = filename(Tab, Config),
+ file:delete(File),
+ Me = self(),
+ ?line {ok, _} = dets:open_file(Tab, [{type, bag}, {file, File}]),
+ spawn(fun()-> Me ! {1, dets:insert(Tab, [])} end),
+ spawn(fun()-> Me ! {2, dets:insert_new(Tab, [])} end),
+ ?line ok = dets:close(Tab),
+ ?line receive {1, ok} -> ok end,
+ ?line receive {2, true} -> ok end,
+ file:delete(File),
+
+ ?line {ok, _} = dets:open_file(Tab, [{type, set}, {file, File}]),
+ spawn(fun() -> dets:delete(Tab, 0) end),
+ spawn(fun() -> Me ! {3, dets:insert_new(Tab, {0,0})} end),
+ ?line ok = dets:close(Tab),
+ ?line receive {3, true} -> ok end,
+ file:delete(File),
+ ok.
+
+otp_8903(doc) ->
+ ["OTP-8903. bchunk/match/select bug."];
+otp_8903(suite) ->
+ [];
+otp_8903(Config) when is_list(Config) ->
+ Tab = otp_8903,
+ File = filename(Tab, Config),
+ ?line {ok,T} = dets:open_file(bug, [{file,File}]),
+ ?line ok = dets:insert(T, [{1,a},{2,b},{3,c}]),
+ ?line dets:safe_fixtable(T, true),
+ ?line {[_],C1} = dets:match_object(T, '_', 1),
+ ?line {BC1,_D} = dets:bchunk(T, start),
+ ?line ok = dets:close(T),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}),
+ ?line {ok,T} = dets:open_file(bug, [{file,File}]),
+ ?line false = dets:info(T, safe_fixed),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}),
+ ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}),
+ ?line ok = dets:close(T),
+ file:delete(File),
+ ok.
+
+otp_8923(doc) ->
+ ["OTP-8923. rehash due to lookup after initialization."];
+otp_8923(suite) ->
+ [];
+otp_8923(Config) when is_list(Config) ->
+ Tab = otp_8923,
+ File = filename(Tab, Config),
+ %% Create a file with more than 256 keys:
+ file:delete(File),
+ Bin = list_to_binary([ 0 || _ <- lists:seq(1, 400) ]),
+ BigBin = list_to_binary([ 0 ||_ <- lists:seq(1, 4000)]),
+ Ets = ets:new(temp, [{keypos,1}]),
+ ?line [ true = ets:insert(Ets, {C,Bin}) || C <- lists:seq(1, 700) ],
+ ?line true = ets:insert(Ets, {helper_data,BigBin}),
+ ?line true = ets:insert(Ets, {prim_btree,BigBin}),
+ ?line true = ets:insert(Ets, {sec_btree,BigBin}),
+ %% Note: too few slots; re-hash will take place
+ ?line {ok, Tab} = dets:open_file(Tab, [{file,File}]),
+ ?line Tab = ets:to_dets(Ets, Tab),
+ ?line ok = dets:close(Tab),
+ ?line true = ets:delete(Ets),
+
+ ?line {ok,Ref} = dets:open_file(File),
+ ?line [{1,_}] = dets:lookup(Ref, 1),
+ ?line ok = dets:close(Ref),
+
+ ?line {ok,Ref2} = dets:open_file(File),
+ ?line [{helper_data,_}] = dets:lookup(Ref2, helper_data),
+ ?line ok = dets:close(Ref2),
+
+ file:delete(File),
+ ok.
+
%%
%% Parts common to several test cases
%%
diff --git a/lib/stdlib/test/dict_SUITE.erl b/lib/stdlib/test/dict_SUITE.erl
index 6a90870bda..c46fc47b34 100644
--- a/lib/stdlib/test/dict_SUITE.erl
+++ b/lib/stdlib/test/dict_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,21 +22,41 @@
-module(dict_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
create/1,store/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-import(lists, [foldl/3,reverse/1]).
-all(suite) ->
- [create,store].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [create, store].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/stdlib/test/dict_test_lib.erl b/lib/stdlib/test/dict_test_lib.erl
index fd15baa5ff..92a75dad89 100644
--- a/lib/stdlib/test/dict_test_lib.erl
+++ b/lib/stdlib/test/dict_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/digraph_SUITE.erl b/lib/stdlib/test/digraph_SUITE.erl
index 6ef5b1ddef..1d1326d60e 100644
--- a/lib/stdlib/test/digraph_SUITE.erl
+++ b/lib/stdlib/test/digraph_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,19 +23,41 @@
-ifdef(STANDALONE).
-define(line, put(line, ?LINE), ).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-endif.
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([opts/1, degree/1, path/1, cycle/1, misc/1, vertices/1,
- edges/1, data/1, tickets/1, otp_3522/1, otp_3630/1, otp_8066/1]).
+-export([opts/1, degree/1, path/1, cycle/1, vertices/1,
+ edges/1, data/1, otp_3522/1, otp_3630/1, otp_8066/1]).
-export([spawn_graph/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) -> {req, [stdlib], [opts, degree, path, cycle, misc, tickets]}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [opts, degree, path, cycle, {group, misc},
+ {group, tickets}].
+
+groups() ->
+ [{misc, [], [vertices, edges, data]},
+ {tickets, [], [otp_3522, otp_3630, otp_8066]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -147,7 +169,6 @@ cycle(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-misc(suite) -> [vertices, edges, data].
vertices(doc) -> [];
vertices(suite) -> [];
@@ -210,7 +231,6 @@ data(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-tickets(suite) -> [otp_3522, otp_3630, otp_8066].
otp_3522(doc) -> [];
otp_3522(suite) -> [];
diff --git a/lib/stdlib/test/digraph_utils_SUITE.erl b/lib/stdlib/test/digraph_utils_SUITE.erl
index d6d477b388..12c486c25f 100644
--- a/lib/stdlib/test/digraph_utils_SUITE.erl
+++ b/lib/stdlib/test/digraph_utils_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,10 +22,11 @@
-ifdef(debug).
-define(line, put(line, ?LINE), ).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-endif.
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([simple/1, loop/1, isolated/1, topsort/1, subgraph/1,
condensation/1, tree/1]).
@@ -33,8 +34,27 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) -> {req, [stdlib], [simple, loop, isolated, topsort,
- subgraph, condensation, tree]}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [simple, loop, isolated, topsort, subgraph,
+ condensation, tree].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/test/dummy1_h.erl b/lib/stdlib/test/dummy1_h.erl
index 4377d774a3..5b503d5984 100644
--- a/lib/stdlib/test/dummy1_h.erl
+++ b/lib/stdlib/test/dummy1_h.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(dummy1_h).
@@ -21,7 +21,7 @@
%% Test event handler for gen_event_SUITE.erl
-export([init/1, handle_event/2, handle_call/2, handle_info/2,
- terminate/2]).
+ terminate/2, format_status/2]).
init(make_error) ->
{error, my_error};
@@ -67,4 +67,5 @@ terminate(remove_handler, Parent) ->
terminate(_Reason, _State) ->
ok.
-
+format_status(_Opt, [_PDict, _State]) ->
+ "dummy1_h handler state".
diff --git a/lib/stdlib/test/dummy_h.erl b/lib/stdlib/test/dummy_h.erl
index 01eb790a75..7546fe78a0 100644
--- a/lib/stdlib/test/dummy_h.erl
+++ b/lib/stdlib/test/dummy_h.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl
index 613bfd000e..a0e198ce09 100644
--- a/lib/stdlib/test/edlin_expand_SUITE.erl
+++ b/lib/stdlib/test/edlin_expand_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,13 +17,14 @@
%% %CopyrightEnd%
%%
-module(edlin_expand_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([normal/1, quoted_fun/1, quoted_module/1, quoted_both/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -31,16 +32,36 @@
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test cases for edlin_expand."];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[normal, quoted_fun, quoted_module, quoted_both].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ true = code:delete(expand_test),
+ true = code:delete(expand_test1),
+ true = code:delete('ExpandTestCaps'),
+ true = code:delete('ExpandTestCaps1'),
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
normal(doc) ->
[""];
normal(suite) ->
diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl
index 4806b5d361..9b024a5b49 100644
--- a/lib/stdlib/test/epp_SUITE.erl
+++ b/lib/stdlib/test/epp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,14 +17,15 @@
%% %CopyrightEnd%
-module(epp_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([rec_1/1, predef_mac/1,
- upcase_mac/1, upcase_mac_1/1, upcase_mac_2/1,
- variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
+-export([rec_1/1, predef_mac/1,
+ upcase_mac_1/1, upcase_mac_2/1,
+ variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1,
pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1,
otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1,
- otp_8562/1]).
+ otp_8562/1, otp_8665/1, otp_8911/1]).
-export([epp_parse_erl_form/2]).
@@ -39,13 +40,13 @@
-define(config(A,B),config(A,B)).
%% -define(t, test_server).
-define(t, io).
-config(priv_dir, _) ->
+config(priv_dir, _) ->
filename:absname("./epp_SUITE_priv");
config(data_dir, _) ->
filename:absname("./epp_SUITE_data").
-else.
--include("test_server.hrl").
--export([init_per_testcase/2, fin_per_testcase/2]).
+-include_lib("test_server/include/test_server.hrl").
+-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -53,18 +54,36 @@ config(data_dir, _) ->
init_per_testcase(_, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_, Config) ->
+end_per_testcase(_, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-endif.
-all(doc) ->
- ["Test cases for epp."];
-all(suite) ->
- [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362,
- pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130,
- overload_mac, otp_8388, otp_8470, otp_8503, otp_8562].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [rec_1, {group, upcase_mac}, predef_mac,
+ {group, variable}, otp_4870, otp_4871, otp_5362, pmod,
+ not_circular, skip_header, otp_6277, otp_7702, otp_8130,
+ overload_mac, otp_8388, otp_8470, otp_8503, otp_8562,
+ otp_8665, otp_8911].
+
+groups() ->
+ [{upcase_mac, [], [upcase_mac_1, upcase_mac_2]},
+ {variable, [], [variable_1]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
rec_1(doc) ->
["Recursive macros hang or crash epp (OTP-1398)."];
@@ -127,10 +146,6 @@ check_errors([{error, Info} | Rest]) ->
check_errors([_ | Rest]) ->
check_errors(Rest).
-upcase_mac(doc) ->
- ["Check that uppercase macro names are implicitly quoted (OTP-2608)"];
-upcase_mac(suite) ->
- [upcase_mac_1, upcase_mac_2].
upcase_mac_1(doc) ->
[];
@@ -176,10 +191,6 @@ predef_mac(Config) when is_list(Config) ->
end,
ok.
-variable(doc) ->
- ["Check variable as first file component of the include directives."];
-variable(suite) ->
- [variable_1].
variable_1(doc) ->
[];
@@ -192,7 +203,7 @@ variable_1(Config) when is_list(Config) ->
%% variable_1.erl includes variable_1_include.hrl and
%% variable_1_include_dir.hrl.
?line {ok, List} = epp:parse_file(File, [], []),
- ?line {value, {attribute,_,a,{value1,value2}}} =
+ ?line {value, {attribute,_,a,{value1,value2}}} =
lists:keysearch(a,3,List),
ok.
@@ -219,13 +230,13 @@ otp_4871(Config) when is_list(Config) ->
%% Testing crash in erl_scan. Unfortunately there currently is
%% no known way to crash erl_scan so it is emulated by killing the
%% file io server. This assumes lots of things about how
- %% the processes are started and how monitors are set up,
+ %% the processes are started and how monitors are set up,
%% so there are some sanity checks before killing.
?line {ok,Epp} = epp:open(File, []),
timer:sleep(1),
?line {current_function,{epp,_,_}} = process_info(Epp, current_function),
?line {monitored_by,[Io]} = process_info(Epp, monitored_by),
- ?line {current_function,{file_io_server,_,_}} =
+ ?line {current_function,{file_io_server,_,_}} =
process_info(Io, current_function),
?line exit(Io, emulate_crash),
timer:sleep(1),
@@ -302,7 +313,7 @@ otp_5362(Config) when is_list(Config) ->
Back_hrl = [<<"
-file(\"">>,File_Back,<<"\", 2).
">>],
-
+
?line ok = file:write_file(File_Back, Back),
?line ok = file:write_file(File_Back_hrl, list_to_binary(Back_hrl)),
@@ -333,7 +344,7 @@ otp_5362(Config) when is_list(Config) ->
?line ok = file:write_file(File_Change, list_to_binary(Change)),
- ?line {ok, change_5362, ChangeWarnings} =
+ ?line {ok, change_5362, ChangeWarnings} =
compile:file(File_Change, Copts),
?line true = message_compare(
[{File_Change,[{{1002,21},erl_lint,{unused_var,'B'}}]},
@@ -441,9 +452,9 @@ skip_header(Config) when is_list(Config) ->
that should be skipped
-module(epp_test_skip_header).
-export([main/1]).
-
+
main(_) -> ?MODULE.
-
+
">>),
?line {ok, Fd} = file:open(File, [read]),
?line io:get_line(Fd, ''),
@@ -494,9 +505,9 @@ otp_7702(Config) when is_list(Config) ->
t() ->
?RECEIVE(foo, bar).">>,
?line ok = file:write_file(File, Contents),
- ?line {ok, file_7702, []} =
+ ?line {ok, file_7702, []} =
compile:file(File, [debug_info,return,{outdir,Dir}]),
-
+
BeamFile = filename:join(Dir, "file_7702.beam"),
{ok, AC} = beam_lib:chunks(BeamFile, [abstract_code]),
@@ -506,7 +517,7 @@ otp_7702(Config) when is_list(Config) ->
L
end,
Forms2 = [erl_lint:modify_line(Form, Fun) || Form <- Forms],
- ?line
+ ?line
[{attribute,1,file,_},
_,
_,
@@ -637,7 +648,7 @@ otp_8130(Config) when is_list(Config) ->
],
?line [] = run(Config, Ts),
-
+
Cs = [{otp_8130_c1,
<<"-define(M1(A), if\n"
"A =:= 1 -> B;\n"
@@ -681,7 +692,7 @@ otp_8130(Config) when is_list(Config) ->
<<"\n-include_lib(\"$apa/foo.hrl\").\n">>,
{errors,[{{2,2},epp,{include,lib,"$apa/foo.hrl"}}],[]}},
-
+
{otp_8130_c9,
<<"-define(S, ?S).\n"
"t() -> ?S.\n">>,
@@ -775,7 +786,7 @@ otp_8130(Config) when is_list(Config) ->
?line Dir = ?config(priv_dir, Config),
?line File = filename:join(Dir, "otp_8130.erl"),
- ?line ok = file:write_file(File,
+ ?line ok = file:write_file(File,
"-module(otp_8130).\n"
"-define(a, 3.14).\n"
"t() -> ?a.\n"),
@@ -788,7 +799,7 @@ otp_8130(Config) when is_list(Config) ->
?line {eof,_} = epp:scan_erl_form(Epp),
?line ['BASE_MODULE','BASE_MODULE_STRING','BEAM','FILE','LINE',
'MACHINE','MODULE','MODULE_STRING',a] = macs(Epp),
- ?line epp:close(Epp),
+ ?line epp:close(Epp),
%% escript
ModuleStr = "any_name",
@@ -815,7 +826,7 @@ otp_8130(Config) when is_list(Config) ->
PreDefMacros = [{a,1},a],
?line {error,{redefine,a}} = epp:open(File, [], PreDefMacros)
end(),
-
+
?line {error,enoent} = epp:open("no such file", []),
?line {error,enoent} = epp:parse_file("no such file", [], []),
@@ -941,7 +952,7 @@ ifdef(Config) ->
<<"\n-if.\n"
"-endif.\n">>,
{errors,[{{2,2},epp,{'NYI','if'}}],[]}},
-
+
{define_c7,
<<"-ifndef(a).\n"
"-elif.\n"
@@ -1197,6 +1208,52 @@ otp_8562(Config) when is_list(Config) ->
?line [] = compile(Config, Cs),
ok.
+otp_8911(doc) ->
+ ["OTP-8911. -file and file inclusion bug"];
+otp_8911(suite) ->
+ [];
+otp_8911(Config) when is_list(Config) ->
+ ?line {ok, CWD} = file:get_cwd(),
+ ?line ok = file:set_cwd(?config(priv_dir, Config)),
+
+ File = "i.erl",
+ Cont = <<"-module(i).
+ -compile(export_all).
+ -file(\"fil1\", 100).
+ -include(\"i1.erl\").
+ t() ->
+ a.
+ ">>,
+ ?line ok = file:write_file(File, Cont),
+ Incl = <<"-file(\"fil2\", 35).
+ t1() ->
+ b.
+ ">>,
+ File1 = "i1.erl",
+ ?line ok = file:write_file(File1, Incl),
+
+ ?line {ok, i} = cover:compile(File),
+ ?line a = i:t(),
+ ?line {ok,[{{i,6},1}]} = cover:analyse(i, calls, line),
+ ?line cover:stop(),
+
+ file:delete(File),
+ file:delete(File1),
+ ?line file:set_cwd(CWD),
+ ok.
+
+otp_8665(doc) ->
+ ["OTP-8665. Bugfix premature end."];
+otp_8665(suite) ->
+ [];
+otp_8665(Config) when is_list(Config) ->
+ Cs = [{otp_8562,
+ <<"-define(A, a)\n">>,
+ {errors,[{{1,54},epp,premature_end}],[]}}
+ ],
+ ?line [] = compile(Config, Cs),
+ ok.
+
check(Config, Tests) ->
eval_tests(Config, fun check_test/2, Tests).
@@ -1213,7 +1270,7 @@ eval_tests(Config, Fun, Tests) ->
case message_compare(E, Return) of
true ->
BadL;
- false ->
+ false ->
?t:format("~nTest ~p failed. Expected~n ~p~n"
"but got~n ~p~n", [N, E, Return]),
fail()
@@ -1228,9 +1285,9 @@ check_test(Config, Test) ->
?line File = filename:join(PrivDir, Filename),
?line ok = file:write_file(File, Test),
?line case epp:parse_file(File, [PrivDir], []) of
- {ok,Forms} ->
+ {ok,Forms} ->
[E || E={error,_} <- Forms];
- {error,Error} ->
+ {error,Error} ->
Error
end.
@@ -1245,7 +1302,7 @@ compile_test(Config, Test0) ->
{ok, Ws} -> warnings(File, Ws);
Else -> Else
end.
-
+
warnings(File, Ws) ->
case lists:append([W || {F, W} <- Ws, F =:= File]) of
[] -> [];
@@ -1289,7 +1346,7 @@ message_compare(T, T) ->
message_compare(T1, T2) ->
ln(T1) =:= T2.
-%% Replaces locations like {Line,Column} with Line.
+%% Replaces locations like {Line,Column} with Line.
ln({warnings,L}) ->
{warnings,ln0(L)};
ln({errors,EL,WL}) ->
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index c60a558fa1..4b59cee99e 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,7 +17,8 @@
%% %CopyrightEnd%
-module(erl_eval_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([guard_1/1, guard_2/1,
match_pattern/1,
@@ -38,7 +39,8 @@
otp_8133/1,
funs/1,
try_catch/1,
- eval_expr_5/1]).
+ eval_expr_5/1,
+ zero_width/1]).
%%
%% Define to run outside of test server
@@ -57,26 +59,42 @@
config(priv_dir,_) ->
".".
-else.
--include("test_server.hrl").
--export([init_per_testcase/2, fin_per_testcase/2]).
+-include_lib("test_server/include/test_server.hrl").
+-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-endif.
-all(doc) ->
- ["Test cases for the 'erl_eval' module."];
-all(suite) ->
- [guard_1, guard_2, match_pattern, string_plusplus, pattern_expr,
- match_bin, guard_3, guard_4,
- lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543,
- otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [guard_1, guard_2, match_pattern, string_plusplus,
+ pattern_expr, match_bin, guard_3, guard_4, lc,
+ simple_cases, unary_plus, apply_atom, otp_5269,
+ otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
+ otp_8133, funs, try_catch, eval_expr_5, zero_width].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
guard_1(doc) ->
["(OTP-2405)"];
@@ -553,6 +571,17 @@ otp_5269(Config) when is_list(Config) ->
B:A>> <- [<<16:8,19:16>>],
<<X:8>> <- [<<B:8>>]].",
[19]),
+ ?line check(fun() ->
+ (fun (<<A:1/binary, B:8/integer, _C:B/binary>>) ->
+ case A of
+ B -> wrong;
+ _ -> ok
+ end
+ end)(<<1,2,3,4>>) end,
+ "(fun(<<A:1/binary, B:8/integer, _C:B/binary>>) ->"
+ " case A of B -> wrong; _ -> ok end"
+ " end)(<<1, 2, 3, 4>>).",
+ ok),
ok.
otp_6539(doc) ->
@@ -1326,6 +1355,14 @@ eval_expr_5(Config) when is_list(Config) ->
ok
end.
+zero_width(Config) when is_list(Config) ->
+ ?line check(fun() ->
+ {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>),
+ ok
+ end, "begin {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), "
+ "ok end.", ok),
+ ok.
+
%% Check the string in different contexts: as is; in fun; from compiled code.
check(F, String, Result) ->
check1(F, String, Result),
diff --git a/lib/stdlib/test/erl_eval_helper.erl b/lib/stdlib/test/erl_eval_helper.erl
index 7fdbabcb17..6863b40108 100644
--- a/lib/stdlib/test/erl_eval_helper.erl
+++ b/lib/stdlib/test/erl_eval_helper.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl
index 1d621c65df..f8c1ad783c 100644
--- a/lib/stdlib/test/erl_expand_records_SUITE.erl
+++ b/lib/stdlib/test/erl_expand_records_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,15 +27,17 @@
-define(privdir, "erl_expand_records_SUITE_priv").
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(privdir, ?config(priv_dir, Config)).
-endif.
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
-export([abstract_module/1, attributes/1, expr/1, guard/1,
init/1, pattern/1, strict/1, update/1,
- tickets/1, otp_5915/1, otp_7931/1, otp_5990/1,
+ otp_5915/1, otp_7931/1, otp_5990/1,
otp_7078/1, otp_7101/1]).
% Default timetrap timeout (set in init_per_testcase).
@@ -45,14 +47,33 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [abstract_module, attributes, expr, guard, init, pattern,
- strict, update, tickets].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [abstract_module, attributes, expr, guard, init,
+ pattern, strict, update, {group, tickets}].
+
+groups() ->
+ [{tickets, [],
+ [otp_5915, otp_7931, otp_5990, otp_7078, otp_7101]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
abstract_module(doc) ->
"Compile an abstract module.";
@@ -399,8 +420,6 @@ update(Config) when is_list(Config) ->
?line run(Config, Ts),
ok.
-tickets(suite) ->
- [otp_5915, otp_7931, otp_5990, otp_7078, otp_7101].
otp_5915(doc) ->
"Strict record tests in guards.";
diff --git a/lib/stdlib/test/erl_internal_SUITE.erl b/lib/stdlib/test/erl_internal_SUITE.erl
index 8f675c94ec..b6b3c004ea 100644
--- a/lib/stdlib/test/erl_internal_SUITE.erl
+++ b/lib/stdlib/test/erl_internal_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,15 +18,35 @@
%%
-module(erl_internal_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([behav/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [behav].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> [behav].
-define(default_timeout, ?t:minutes(2)).
@@ -34,7 +54,7 @@ init_per_testcase(_Case, Config) ->
?line Dog = test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 8581b496aa..f980d52e4e 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,34 +27,37 @@
-define(privdir, "erl_lint_SUITE_priv").
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Conf)).
-define(privdir, ?config(priv_dir, Conf)).
-endif.
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
-
--export([unused_vars_warn/1,
- unused_vars_warn_basic/1,
- unused_vars_warn_lc/1,
- unused_vars_warn_rec/1,
- unused_vars_warn_fun/1,
- unused_vars_OTP_4858/1,
- export_vars_warn/1,
- shadow_vars/1,
- unused_import/1,
- unused_function/1,
- unsafe_vars/1,unsafe_vars2/1,
- unsafe_vars_try/1,
- guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
- otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
- otp_5917/1, otp_6585/1, otp_6885/1, export_all/1,
- bif_clash/1,
- behaviour_basic/1, behaviour_multiple/1,
- otp_7550/1,
- otp_8051/1,
- format_warn/1,
- on_load/1, on_load_successful/1, on_load_failing/1
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
+
+-export([
+ unused_vars_warn_basic/1,
+ unused_vars_warn_lc/1,
+ unused_vars_warn_rec/1,
+ unused_vars_warn_fun/1,
+ unused_vars_OTP_4858/1,
+ export_vars_warn/1,
+ shadow_vars/1,
+ unused_import/1,
+ unused_function/1,
+ unsafe_vars/1,unsafe_vars2/1,
+ unsafe_vars_try/1,
+ guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
+ otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
+ otp_5917/1, otp_6585/1, otp_6885/1, export_all/1,
+ bif_clash/1,
+ behaviour_basic/1, behaviour_multiple/1,
+ otp_7550/1,
+ otp_8051/1,
+ format_warn/1,
+ on_load_successful/1, on_load_failing/1,
+ too_many_arguments/1
]).
% Default timetrap timeout (set in init_per_testcase).
@@ -64,24 +67,44 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [unused_vars_warn, export_vars_warn,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, unused_vars_warn}, export_vars_warn,
shadow_vars, unused_import, unused_function,
- unsafe_vars, unsafe_vars2, unsafe_vars_try,
- guard, otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
- otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, otp_5878, otp_5917, otp_6585,
- otp_6885, export_all, bif_clash,
- behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn,
- on_load].
+ unsafe_vars, unsafe_vars2, unsafe_vars_try, guard,
+ otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
+ otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,
+ otp_5878, otp_5917, otp_6585, otp_6885, export_all,
+ bif_clash, behaviour_basic, behaviour_multiple,
+ otp_7550, otp_8051, format_warn, {group, on_load},
+ too_many_arguments].
+
+groups() ->
+ [{unused_vars_warn, [],
+ [unused_vars_warn_basic, unused_vars_warn_lc,
+ unused_vars_warn_rec, unused_vars_warn_fun,
+ unused_vars_OTP_4858]},
+ {on_load, [], [on_load_successful, on_load_failing]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-unused_vars_warn(suite) ->
- [unused_vars_warn_basic, unused_vars_warn_lc, unused_vars_warn_rec,
- unused_vars_warn_fun, unused_vars_OTP_4858].
unused_vars_warn_basic(doc) ->
"Warnings for unused variables in some simple cases.";
@@ -1784,6 +1807,9 @@ otp_5362(Config) when is_list(Config) ->
{15,erl_lint,{undefined_field,ok,nix}},
{16,erl_lint,{field_name_is_variable,ok,'Var'}}]}},
+ %% Nowarn_bif_clash has changed behaviour as local functions
+ %% nowdays supersede auto-imported BIFs, why nowarn_bif_clash in itself generates an error
+ %% (OTP-8579) /PaN
{otp_5362_4,
<<"-compile(nowarn_deprecated_function).
-compile(nowarn_bif_clash).
@@ -1795,9 +1821,8 @@ otp_5362(Config) when is_list(Config) ->
warn_deprecated_function,
warn_bif_clash]},
{error,
- [{5,erl_lint,{call_to_redefined_bif,{spawn,1}}}],
- [{3,erl_lint,{redefine_bif,{spawn,1}}},
- {4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2},
+ [{5,erl_lint,{call_to_redefined_old_bif,{spawn,1}}}],
+ [{4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2},
"in a future release"}}]}},
{otp_5362_5,
@@ -1808,8 +1833,8 @@ otp_5362(Config) when is_list(Config) ->
spawn(A).
">>,
{[nowarn_unused_function]},
- {warnings,
- [{3,erl_lint,{redefine_bif,{spawn,1}}}]}},
+ {errors,
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}},
%% The special nowarn_X are not affected by general warn_X.
{otp_5362_6,
@@ -1822,8 +1847,8 @@ otp_5362(Config) when is_list(Config) ->
{[nowarn_unused_function,
warn_deprecated_function,
warn_bif_clash]},
- {warnings,
- [{3,erl_lint,{redefine_bif,{spawn,1}}}]}},
+ {errors,
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}},
{otp_5362_7,
<<"-export([spawn/1]).
@@ -1838,7 +1863,9 @@ otp_5362(Config) when is_list(Config) ->
spawn(A).
">>,
{[nowarn_unused_function]},
- {error,[{4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}],
+ {error,[{3,erl_lint,disallowed_nowarn_bif_clash},
+ {4,erl_lint,disallowed_nowarn_bif_clash},
+ {4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}],
[{5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}},
{5,erl_lint,{bad_nowarn_deprecated_function,{erlang,hash,-1}}},
{5,erl_lint,{bad_nowarn_deprecated_function,{{a,b,c},hash,-1}}}]}
@@ -1865,7 +1892,21 @@ otp_5362(Config) when is_list(Config) ->
t() -> #a{}.
">>,
{[]},
- []}
+ []},
+
+ {otp_5362_10,
+ <<"-compile({nowarn_deprecated_function,{erlang,hash,2}}).
+ -compile({nowarn_bif_clash,{spawn,1}}).
+ -import(x,[spawn/1]).
+ spin(A) ->
+ erlang:hash(A, 3000),
+ spawn(A).
+ ">>,
+ {[nowarn_unused_function,
+ warn_deprecated_function,
+ warn_bif_clash]},
+ {errors,
+ [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}
],
@@ -2234,7 +2275,7 @@ otp_5878(Config) when is_list(Config) ->
{15,erl_lint,{undefined_field,r3,q}},
{17,erl_lint,{undefined_field,r,q}},
{21,erl_lint,illegal_guard_expr},
- {23,erl_lint,illegal_guard_expr}],
+ {23,erl_lint,{illegal_guard_local_call,{l,0}}}],
[]} =
run_test2(Config, Ill1, [warn_unused_record]),
@@ -2389,9 +2430,9 @@ bif_clash(Config) when is_list(Config) ->
N.
">>,
[],
- {errors,[{2,erl_lint,{call_to_redefined_bif,{size,1}}}],[]}},
+ {errors,[{2,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}},
- %% Verify that (some) warnings can be turned off.
+ %% Verify that warnings can not be turned off in the old way.
{clash2,
<<"-export([t/1,size/1]).
t(X) ->
@@ -2400,17 +2441,198 @@ bif_clash(Config) when is_list(Config) ->
size({N,_}) ->
N.
- %% My own abs/1 function works on lists too.
- %% Unfortunately, it is not exported, so there will
- %% be a warning that can't be turned off.
+ %% My own abs/1 function works on lists too. From R14 this really works.
abs([H|T]) when $a =< H, H =< $z -> [H-($a-$A)|abs(T)];
abs([H|T]) -> [H|abs(T)];
abs([]) -> [];
abs(X) -> erlang:abs(X).
">>,
- {[nowarn_bif_clash]},
- {warnings,[{11,erl_lint,{redefine_bif,{abs,1}}},
- {11,erl_lint,{unused_function,{abs,1}}}]}}],
+ {[nowarn_unused_function,nowarn_bif_clash]},
+ {errors,[{erl_lint,disallowed_nowarn_bif_clash}],[]}},
+ %% As long as noone calls an overridden BIF, it's totally OK
+ {clash3,
+ <<"-export([size/1]).
+ size({N,_}) ->
+ N;
+ size(X) ->
+ erlang:size(X).
+ ">>,
+ [],
+ []},
+ %% But this is totally wrong - meaning of the program changed in R14, so this is an error
+ {clash4,
+ <<"-export([size/1]).
+ size({N,_}) ->
+ N;
+ size(X) ->
+ size(X).
+ ">>,
+ [],
+ {errors,[{5,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}},
+ %% For a post R14 bif, its only a warning
+ {clash5,
+ <<"-export([binary_part/2]).
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ {warnings,[{3,erl_lint,{call_to_redefined_bif,{binary_part,2}}}]}},
+ %% If you really mean to call yourself here, you can "unimport" size/1
+ {clash6,
+ <<"-export([size/1]).
+ -compile({no_auto_import,[size/1]}).
+ size([]) ->
+ 0;
+ size({N,_}) ->
+ N;
+ size([_|T]) ->
+ 1+size(T).
+ ">>,
+ [],
+ []},
+ %% Same for the post R14 autoimport warning
+ {clash7,
+ <<"-export([binary_part/2]).
+ -compile({no_auto_import,[binary_part/2]}).
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ []},
+ %% but this doesn't mean the local function is allowed in a guard...
+ {clash8,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ x(X) when binary_part(X,{1,2}) =:= <<1,2>> ->
+ hej.
+ binary_part({B,_},{X,Y}) ->
+ binary_part(B,{X,Y});
+ binary_part(B,{X,Y}) ->
+ binary:part(B,X,Y).
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{illegal_guard_local_call,{binary_part,2}}}],[]}},
+ %% no_auto_import is not like nowarn_bif_clash, it actually removes the autoimport
+ {clash9,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ x(X) ->
+ binary_part(X,{1,2}) =:= <<1,2>>.
+ ">>,
+ [],
+ {errors,[{4,erl_lint,{undefined_function,{binary_part,2}}}],[]}},
+ %% but we could import it again...
+ {clash10,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(erlang,[binary_part/2]).
+ x(X) ->
+ binary_part(X,{1,2}) =:= <<1,2>>.
+ ">>,
+ [],
+ []},
+ %% and actually use it in a guard...
+ {clash11,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(erlang,[binary_part/2]).
+ x(X) when binary_part(X,{0,1}) =:= <<0>> ->
+ binary_part(X,{1,2}) =:= <<1,2>>.
+ ">>,
+ [],
+ []},
+ %% but for non-obvious historical reasons, imported functions cannot be used in
+ %% fun construction without the module name...
+ {clash12,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(erlang,[binary_part/2]).
+ x(X) when binary_part(X,{0,1}) =:= <<0>> ->
+ binary_part(X,{1,2}) =:= fun binary_part/2.
+ ">>,
+ [],
+ {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}},
+ %% Not from erlang and not from anywhere else
+ {clash13,
+ <<"-export([x/1]).
+ -compile({no_auto_import,[binary_part/2]}).
+ -import(x,[binary_part/2]).
+ x(X) ->
+ binary_part(X,{1,2}) =:= fun binary_part/2.
+ ">>,
+ [],
+ {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}},
+ %% ...while real auto-import is OK.
+ {clash14,
+ <<"-export([x/1]).
+ x(X) when binary_part(X,{0,1}) =:= <<0>> ->
+ binary_part(X,{1,2}) =:= fun binary_part/2.
+ ">>,
+ [],
+ []},
+ %% Import directive clashing with old bif is an error, regardless of if it's called or not
+ {clash15,
+ <<"-export([x/1]).
+ -import(x,[abs/1]).
+ x(X) ->
+ binary_part(X,{1,2}).
+ ">>,
+ [],
+ {errors,[{2,erl_lint,{redefine_old_bif_import,{abs,1}}}],[]}},
+ %% For a new BIF, it's only a warning
+ {clash16,
+ <<"-export([x/1]).
+ -import(x,[binary_part/3]).
+ x(X) ->
+ abs(X).
+ ">>,
+ [],
+ {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}},
+ %% And, you cannot redefine already imported things that aren't auto-imported
+ {clash17,
+ <<"-export([x/1]).
+ -import(x,[binary_port/3]).
+ -import(y,[binary_port/3]).
+ x(X) ->
+ abs(X).
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{redefine_import,{{binary_port,3},x}}}],[]}},
+ %% Not with local functions either
+ {clash18,
+ <<"-export([x/1]).
+ -import(x,[binary_port/3]).
+ binary_port(A,B,C) ->
+ binary_part(A,B,C).
+ x(X) ->
+ abs(X).
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{define_import,{binary_port,3}}}],[]}},
+ %% Like clash8: Dont accept a guard if it's explicitly module-name called either
+ {clash19,
+ <<"-export([binary_port/3]).
+ -compile({no_auto_import,[binary_part/3]}).
+ -import(x,[binary_part/3]).
+ binary_port(A,B,C) when x:binary_part(A,B,C) ->
+ binary_part(A,B,C+1).
+ ">>,
+ [],
+ {errors,[{4,erl_lint,illegal_guard_expr}],[]}},
+ %% Not with local functions either
+ {clash20,
+ <<"-export([binary_port/3]).
+ -import(x,[binary_part/3]).
+ binary_port(A,B,C) ->
+ binary_part(A,B,C).
+ ">>,
+ [warn_unused_import],
+ {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}}
+ ],
?line [] = run(Config, Ts),
ok.
@@ -2632,8 +2854,6 @@ format_level(Level, Count, Config) ->
%% Test the -on_load(Name/0) directive.
-on_load(suite) ->
- [on_load_successful, on_load_failing].
on_load_successful(Config) when is_list(Config) ->
Ts = [{on_load_1,
@@ -2714,6 +2934,21 @@ on_load_failing(Config) when is_list(Config) ->
?line [] = run(Config, Ts),
ok.
+too_many_arguments(doc) ->
+ "Test that too many arguments is not accepted.";
+too_many_arguments(suite) -> [];
+too_many_arguments(Config) when is_list(Config) ->
+ Ts = [{too_many_1,
+ <<"f(_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_) -> ok.">>,
+ [],
+ {errors,
+ [{1,erl_lint,{too_many_arguments,256}}],[]}}
+ ],
+
+ ?line [] = run(Config, Ts),
+ ok.
+
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of
diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl
index 66730b7b94..bc811355ab 100644
--- a/lib/stdlib/test/erl_pp_SUITE.erl
+++ b/lib/stdlib/test/erl_pp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,23 +30,25 @@
-define(privdir, "erl_pp_SUITE_priv").
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Config)).
-define(privdir, ?config(priv_dir, Config)).
-endif.
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
--export([expr/1, func/1, call/1, recs/1, try_catch/1, if_then/1,
- receive_after/1, bits/1, head_tail/1, package/1,
- cond1/1, block/1, case1/1, ops/1, messages/1,
- old_mnemosyne_syntax/1,
- attributes/1, import_export/1, misc_attrs/1,
- hook/1,
- neg_indent/1,
- tickets/1,
- otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
- otp_8473/1, otp_8522/1, otp_8567/1]).
+-export([ func/1, call/1, recs/1, try_catch/1, if_then/1,
+ receive_after/1, bits/1, head_tail/1, package/1,
+ cond1/1, block/1, case1/1, ops/1, messages/1,
+ old_mnemosyne_syntax/1,
+ import_export/1, misc_attrs/1,
+ hook/1,
+ neg_indent/1,
+
+ otp_6321/1, otp_6911/1, otp_6914/1, otp_8150/1, otp_8238/1,
+ otp_8473/1, otp_8522/1, otp_8567/1, otp_8664/1, otp_9147/1]).
%% Internal export.
-export([ehook/6]).
@@ -58,17 +60,40 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [expr, attributes, hook, neg_indent, tickets].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, expr}, {group, attributes}, hook, neg_indent,
+ {group, tickets}].
+
+groups() ->
+ [{expr, [],
+ [func, call, recs, try_catch, if_then, receive_after,
+ bits, head_tail, package, cond1, block, case1, ops,
+ messages, old_mnemosyne_syntax]},
+ {attributes, [], [misc_attrs, import_export]},
+ {tickets, [],
+ [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238,
+ otp_8473, otp_8522, otp_8567, otp_8664, otp_9147]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-expr(suite) ->
- [func, call, recs, try_catch, if_then, receive_after, bits, head_tail,
- package, cond1, block, case1, ops, messages, old_mnemosyne_syntax].
func(suite) ->
[];
@@ -564,8 +589,6 @@ old_mnemosyne_syntax(Config) when is_list(Config) ->
ok.
-attributes(suite) ->
- [misc_attrs, import_export].
import_export(suite) ->
[];
@@ -763,9 +786,6 @@ neg_indent(Config) when is_list(Config) ->
ok.
-tickets(suite) ->
- [otp_6321, otp_6911, otp_6914, otp_8150, otp_8238, otp_8473, otp_8522,
- otp_8567].
otp_6321(doc) ->
"OTP_6321. Bug fix of exprs().";
@@ -995,6 +1015,58 @@ otp_8567(Config) when is_list(Config) ->
ok.
+otp_8664(doc) ->
+ "OTP_8664. Types with integer expressions.";
+otp_8664(suite) -> [];
+otp_8664(Config) when is_list(Config) ->
+ FileName = filename('otp_8664.erl', Config),
+ C1 = <<"-module(otp_8664).\n"
+ "-export([t/0]).\n"
+ "-define(A, -3).\n"
+ "-define(B, (?A*(-1 band (((2)))))).\n"
+ "-type t1() :: ?B | ?A.\n"
+ "-type t2() :: ?B-1 .. -?B.\n"
+ "-type t3() :: 9 band (8 - 3) | 1+2 | 5 band 3.\n"
+ "-type b1() :: <<_:_*(3-(-1))>>\n"
+ " | <<_:(-(?B))>>\n"
+ " | <<_:4>>.\n"
+ "-type u() :: 1 .. 2 | 3.. 4 | (8-3) ..6 | 5+0..6.\n"
+ "-type t() :: t1() | t2() | t3() | b1() | u().\n"
+ "-spec t() -> t().\n"
+ "t() -> 3.\n">>,
+ ?line ok = file:write_file(FileName, C1),
+ ?line {ok, _, []} = compile:file(FileName, [return]),
+
+ C2 = <<"-module(otp_8664).\n"
+ "-export([t/0]).\n"
+ "-spec t() -> 9 and 4.\n"
+ "t() -> 0.\n">>,
+ ?line ok = file:write_file(FileName, C2),
+ ?line {error,[{_,[{3,erl_lint,{type_syntax,integer}}]}],_} =
+ compile:file(FileName, [return]),
+
+ ok.
+
+otp_9147(doc) ->
+ "OTP_9147. Create well-formed types when adding 'undefined'.";
+otp_9147(suite) -> [];
+otp_9147(Config) when is_list(Config) ->
+ FileName = filename('otp_9147.erl', Config),
+ C1 = <<"-module(otp_9147).\n"
+ "-export_type([undef/0]).\n"
+ "-record(undef, {f1 :: F1 :: a | b}).\n"
+ "-type undef() :: #undef{}.\n">>,
+ ?line ok = file:write_file(FileName, C1),
+ ?line {ok, _, []} =
+ compile:file(FileName, [return,'P',{outdir,?privdir}]),
+ PFileName = filename('otp_9147.P', Config),
+ ?line {ok, Bin} = file:read_file(PFileName),
+ %% The parentheses around "F1 :: a | b" are new (bugfix).
+ ?line true =
+ lists:member("-record(undef,{f1 :: undefined | (F1 :: a | b)}).",
+ string:tokens(binary_to_list(Bin), "\n")),
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
compile(Config, Tests) ->
diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl
index afeb67eeb1..31a4f94294 100644
--- a/lib/stdlib/test/erl_scan_SUITE.erl
+++ b/lib/stdlib/test/erl_scan_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,9 +17,10 @@
%% %CopyrightEnd%
-module(erl_scan_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([error/1, error_1/1, error_2/1, iso88591/1, otp_7810/1]).
+-export([ error_1/1, error_2/1, iso88591/1, otp_7810/1]).
-import(lists, [nth/2,flatten/1]).
-import(io_lib, [print/1]).
@@ -39,14 +40,14 @@
%% config(data_dir, _) ->
%% ".".
-else.
--include("test_server.hrl").
--export([init_per_testcase/2, fin_per_testcase/2]).
+-include_lib("test_server/include/test_server.hrl").
+-export([init_per_testcase/2, end_per_testcase/2]).
init_per_testcase(_Case, Config) when is_list(Config) ->
?line Dog=test_server:timetrap(test_server:seconds(1200)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -55,15 +56,27 @@ fin_per_testcase(_Case, Config) ->
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
-all(doc) ->
- ["Test cases for the 'erl_scan' module."];
-all(suite) ->
- [error,iso88591,otp_7810].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, error}, iso88591, otp_7810].
+
+groups() ->
+ [{error, [], [error_1, error_2]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-error(doc) ->
- ["Error cases"];
-error(suite) ->
- [error_1, error_2].
error_1(doc) ->
["(OTP-2347)"];
@@ -185,7 +198,7 @@ reserved_words() ->
'andalso', 'orelse', 'end', 'fun', 'if', 'let', 'of',
'query', 'receive', 'when', 'bnot', 'not', 'div',
'rem', 'band', 'and', 'bor', 'bxor', 'bsl', 'bsr',
- 'or', 'xor'] ,
+ 'or', 'xor'],
[begin
?line {RW, true} = {RW, erl_scan:reserved_word(RW)},
S = atom_to_list(RW),
@@ -244,6 +257,9 @@ punctuations() ->
{'\\',1},{'^',1},{'`',1},{'~',1}],
?line test_string("#&*+/:<>?@\\^`~", PTs2),
+ ?line test_string(".. ", [{'..',1}]),
+ ?line test("1 .. 2"),
+ ?line test_string("...", [{'...',1}]),
ok.
comments() ->
diff --git a/lib/stdlib/test/error_logger_forwarder.erl b/lib/stdlib/test/error_logger_forwarder.erl
index 7d99d07860..5703ac769a 100644
--- a/lib/stdlib/test/error_logger_forwarder.erl
+++ b/lib/stdlib/test/error_logger_forwarder.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009. All Rights Reserved.
+%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 77fd190e45..9f95df062b 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,9 +18,10 @@
-module(escript_SUITE).
-export([
- all/1,
+ all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
+ end_per_testcase/2,
basic/1,
errors/1,
strange_name/1,
@@ -31,31 +32,40 @@
epp/1,
create_and_extract/1,
foldl/1,
+ overflow/1,
verify_sections/3
]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
-all(suite) ->
- [
- basic,
- errors,
- strange_name,
- emulator_flags,
- module_script,
- beam_script,
- archive_script,
- epp,
- create_and_extract,
- foldl
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic, errors, strange_name, emulator_flags,
+ module_script, beam_script, archive_script, epp,
+ create_and_extract, foldl, overflow].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?t:minutes(1)),
[{watchdog,Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -736,6 +746,17 @@ emulate_escript_foldl(Fun, Acc, File) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+overflow(Config) when is_list(Config) ->
+ Data = ?config(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ ?line run(Dir, "arg_overflow",
+ [<<"ExitCode:0">>]),
+ ?line run(Dir, "linebuf_overflow",
+ [<<"ExitCode:0">>]),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
run(Dir, Cmd0, Expected0) ->
Expected = iolist_to_binary(expected_output(Expected0, Dir)),
Cmd = case os:type() of
diff --git a/lib/stdlib/test/escript_SUITE_data/arg_overflow b/lib/stdlib/test/escript_SUITE_data/arg_overflow
new file mode 100755
index 0000000000..dd5accc051
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/arg_overflow
@@ -0,0 +1,5 @@
+#! /usr/bin/env escript
+%% -*- erlang -*-
+%%!x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
+main(_) ->
+ halt(0).
diff --git a/lib/stdlib/test/escript_SUITE_data/linebuf_overflow b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow
new file mode 100755
index 0000000000..33133c1ce9
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow
@@ -0,0 +1,5 @@
+#! /usr/bin/env escript
+%% -*- erlang -*-
+%%!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+main(_) ->
+ halt(0).
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 13c87ca005..9d348b5f1a 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,24 +18,25 @@
%%
-module(ets_SUITE).
--export([all/1]).
--export([new/1,default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([default/1,setbag/1,badnew/1,verybadnew/1,named/1,keypos2/1,
privacy/1,privacy_owner/2]).
--export([insert/1,empty/1,badinsert/1]).
--export([lookup/1,time_lookup/1,badlookup/1,lookup_order/1]).
--export([delete/1,delete_elem/1,delete_tab/1,delete_large_tab/1,
+-export([empty/1,badinsert/1]).
+-export([time_lookup/1,badlookup/1,lookup_order/1]).
+-export([delete_elem/1,delete_tab/1,delete_large_tab/1,
delete_large_named_table/1,
evil_delete/1,baddelete/1,match_delete/1,table_leak/1]).
-export([match_delete3/1]).
-export([firstnext/1,firstnext_concurrent/1]).
-export([slot/1]).
--export([match/1, match1/1, match2/1, match_object/1, match_object2/1]).
--export([misc/1, dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
--export([files/1, tab2file/1, tab2file2/1, tab2file3/1, tabfile_ext1/1,
+-export([ match1/1, match2/1, match_object/1, match_object2/1]).
+-export([ dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
+-export([ tab2file/1, tab2file2/1, tabfile_ext1/1,
tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1]).
--export([heavy/1, heavy_lookup/1, heavy_lookup_element/1]).
--export([lookup_element/1, lookup_element_mult/1]).
--export([fold/1]).
+-export([ heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]).
+-export([ lookup_element_mult/1]).
+-export([]).
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
-export([t_delete_object/1, t_init_table/1, t_whitebox/1,
t_delete_all_objects/1, t_insert_list/1, t_test_ms/1,
@@ -59,25 +60,26 @@
-export([otp_7665/1]).
-export([meta_wb/1]).
-export([grow_shrink/1, grow_pseudo_deleted/1, shrink_pseudo_deleted/1]).
--export([meta_smp/1,
+-export([
meta_lookup_unnamed_read/1, meta_lookup_unnamed_write/1,
meta_lookup_named_read/1, meta_lookup_named_write/1,
meta_newdel_unnamed/1, meta_newdel_named/1]).
--export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1, otp_8166/1]).
+-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1,
+ otp_8166/1, otp_8732/1]).
-export([exit_large_table_owner/1,
exit_many_large_table_owner/1,
exit_many_tables_owner/1,
exit_many_many_tables_owner/1]).
-export([write_concurrency/1, heir/1, give_away/1, setopts/1]).
--export([bad_table/1]).
+-export([bad_table/1, types/1]).
--export([init_per_testcase/2, fin_per_testcase/2, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%% Convenience for manual testing
-export([random_test/0]).
% internal exports
-export([dont_make_worse_sub/0, make_better_sub1/0, make_better_sub2/0]).
--export([t_repair_continuation_do/1, default_do/1, t_bucket_disappears_do/1,
+-export([t_repair_continuation_do/1, t_bucket_disappears_do/1,
select_fail_do/1, whitebox_1/1, whitebox_2/1, t_delete_all_objects_do/1,
t_delete_object_do/1, t_init_table_do/1, t_insert_list_do/1,
update_element_opts/1, update_element_opts/4, update_element/4, update_element_do/4,
@@ -89,10 +91,17 @@
match_delete_do/1, match_delete3_do/1, firstnext_do/1,
slot_do/1, match1_do/1, match2_do/1, match_object_do/1, match_object2_do/1,
misc1_do/1, safe_fixtable_do/1, info_do/1, dups_do/1, heavy_lookup_do/1,
- heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1
+ heavy_lookup_element_do/1, member_do/1, otp_5340_do/1, otp_7665_do/1, meta_wb_do/1,
+ do_heavy_concurrent/1, tab2file2_do/2, exit_large_table_owner_do/2,
+ types_do/1, sleeper/0, rpc_externals/0, memory_do/1,
+ ms_tracee_dummy/1, ms_tracee_dummy/2, ms_tracee_dummy/3, ms_tracee_dummy/4
]).
--include("test_server.hrl").
+-export([t_select_reverse/1]).
+
+-include_lib("test_server/include/test_server.hrl").
+
+-define(m(A,B), ?line assert_eq(A,B)).
init_per_testcase(Case, Config) ->
Seed = {S1,S2,S3} = random:seed0(), %now(),
@@ -103,44 +112,80 @@ init_per_testcase(Case, Config) ->
Dog=test_server:timetrap(test_server:minutes(20)),
[{watchdog, Dog}, {test_case, Case} | Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
wait_for_test_procs(true),
test_server:timetrap_cancel(Dog).
-
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, new}, {group, insert}, {group, lookup},
+ {group, delete}, firstnext, firstnext_concurrent, slot,
+ {group, match}, t_match_spec_run,
+ {group, lookup_element}, {group, misc}, {group, files},
+ {group, heavy}, ordered, ordered_match,
+ interface_equality, fixtable_next, fixtable_insert,
+ rename, rename_unnamed, evil_rename, update_element,
+ update_counter, evil_update_counter, partly_bound,
+ match_heavy, {group, fold}, member, t_delete_object,
+ t_init_table, t_whitebox, t_delete_all_objects,
+ t_insert_list, t_test_ms, t_select_delete, t_ets_dets,
+ memory, t_select_reverse, t_bucket_disappears,
+ select_fail, t_insert_new, t_repair_continuation,
+ otp_5340, otp_6338, otp_6842_select_1000, otp_7665,
+ otp_8732, meta_wb, grow_shrink, grow_pseudo_deleted,
+ shrink_pseudo_deleted, {group, meta_smp}, smp_insert,
+ smp_fixed_delete, smp_unfix_fix, smp_select_delete,
+ otp_8166, exit_large_table_owner,
+ exit_many_large_table_owner, exit_many_tables_owner,
+ exit_many_many_tables_owner, write_concurrency, heir,
+ give_away, setopts, bad_table, types].
+
+groups() ->
+ [{new, [],
+ [default, setbag, badnew, verybadnew, named, keypos2,
+ privacy]},
+ {insert, [], [empty, badinsert]},
+ {lookup, [], [time_lookup, badlookup, lookup_order]},
+ {lookup_element, [], [lookup_element_mult]},
+ {delete, [],
+ [delete_elem, delete_tab, delete_large_tab,
+ delete_large_named_table, evil_delete, table_leak,
+ baddelete, match_delete, match_delete3]},
+ {match, [],
+ [match1, match2, match_object, match_object2]},
+ {misc, [],
+ [misc1, safe_fixtable, info, dups, tab2list]},
+ {files, [],
+ [tab2file, tab2file2, tabfile_ext1,
+ tabfile_ext2, tabfile_ext3, tabfile_ext4]},
+ {heavy, [],
+ [heavy_lookup, heavy_lookup_element, heavy_concurrent]},
+ {fold, [],
+ [foldl_ordered, foldr_ordered, foldl, foldr,
+ fold_empty]},
+ {meta_smp, [],
+ [meta_lookup_unnamed_read, meta_lookup_unnamed_write,
+ meta_lookup_named_read, meta_lookup_named_write,
+ meta_newdel_unnamed, meta_newdel_named]}].
+
+init_per_suite(Config) ->
+ Config.
end_per_suite(_Config) ->
stop_spawn_logger(),
catch erts_debug:set_internal_state(available_internal_state, false).
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) ->
- [
- new,insert,lookup,delete,firstnext,firstnext_concurrent,slot,match,
- t_match_spec_run,
- lookup_element, misc,files, heavy,
- ordered, ordered_match, interface_equality,
- fixtable_next, fixtable_insert, rename, rename_unnamed, evil_rename,
- update_element, update_counter, evil_update_counter, partly_bound,
- match_heavy, fold, member,
- t_delete_object, t_init_table, t_whitebox,
- t_delete_all_objects, t_insert_list, t_test_ms,
- t_select_delete, t_ets_dets, memory,
- t_bucket_disappears,
- select_fail,t_insert_new, t_repair_continuation, otp_5340, otp_6338,
- otp_6842_select_1000, otp_7665,
- meta_wb,
- grow_shrink, grow_pseudo_deleted, shrink_pseudo_deleted,
- meta_smp,
- smp_insert, smp_fixed_delete, smp_unfix_fix, smp_select_delete, otp_8166,
- exit_large_table_owner,
- exit_many_large_table_owner,
- exit_many_tables_owner,
- exit_many_many_tables_owner,
- write_concurrency, heir, give_away, setopts,
- bad_table
- ].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -153,7 +198,7 @@ t_bucket_disappears(Config) when is_list(Config) ->
t_bucket_disappears_do(Opts) ->
?line EtsMem = etsmem(),
- ?line ets:new(abcd, [named_table, public, {keypos, 2} | Opts]),
+ ?line ets_new(abcd, [named_table, public, {keypos, 2} | Opts]),
?line ets:insert(abcd, {abcd,1,2}),
?line ets:insert(abcd, {abcd,2,2}),
?line ets:insert(abcd, {abcd,3,2}),
@@ -171,29 +216,180 @@ t_match_spec_run(suite) ->
t_match_spec_run(doc) ->
["Check ets:match_spec_run/2."];
t_match_spec_run(Config) when is_list(Config) ->
+ init_externals(),
?line EtsMem = etsmem(),
- ?line [2,3] = ets:match_spec_run([{1},{2},{3}],
- ets:match_spec_compile(
- [{{'$1'},[{'>','$1',1}],['$1']}])),
+
+ t_match_spec_run_test([{1},{2},{3}],
+ [{{'$1'},[{'>','$1',1}],['$1']}],
+ [2,3]),
+
?line Huge = [{X} || X <- lists:seq(1,2500)],
?line L = lists:seq(2476,2500),
- ?line L = ets:match_spec_run(Huge,
- ets:match_spec_compile(
- [{{'$1'},[{'>','$1',2475}],['$1']}])),
+ t_match_spec_run_test(Huge, [{{'$1'},[{'>','$1',2475}],['$1']}], L),
+
?line L2 = [{X*16#FFFFFFF} || X <- L],
- ?line L2 = ets:match_spec_run(Huge,
- ets:match_spec_compile(
- [{{'$1'},
- [{'>','$1',2475}],
- [{{{'*','$1',16#FFFFFFF}}}]}])),
- ?line [500,1000,1500,2000,2500] =
- ets:match_spec_run(Huge,
- ets:match_spec_compile(
- [{{'$1'},
- [{'=:=',{'rem','$1',500},0}],
- ['$1']}])),
+ t_match_spec_run_test(Huge,
+ [{{'$1'}, [{'>','$1',2475}], [{{{'*','$1',16#FFFFFFF}}}]}],
+ L2),
+
+ t_match_spec_run_test(Huge, [{{'$1'}, [{'=:=',{'rem','$1',500},0}], ['$1']}],
+ [500,1000,1500,2000,2500]),
+
+ %% More matching fun with several match clauses and guards,
+ %% applied to a variety of terms.
+ Fun = fun(Term) ->
+ CTerm = {const, Term},
+
+ N_List = [{Term, "0", "v-element"},
+ {"=hidden_node", "0", Term},
+ {"0", Term, Term},
+ {"something", Term, "something else"},
+ {"guard and res", Term, 872346},
+ {Term, {'and',Term,'again'}, 3.14},
+ {Term, {'and',Term,'again'}, "m&g"},
+ {Term, {'and',Term,'again'}, "m&g&r"},
+ {[{second,Term}, 'and', "tail"], Term, ['and',"tail"]}],
+
+ N_MS = [{{'$1','$2','$3'},
+ [{'=:=','$1',CTerm}, {'=:=','$2',{const,"0"}}],
+ [{{"Guard only for $1",'$3'}}]},
+
+ {{'$3','$1','$4'},
+ [{'=:=','$3',"=hidden_node"}, {'=:=','$1',{const,"0"}}],
+ [{{"Result only for $4",'$4'}}]},
+
+ {{'$2','$1','$1'},
+ [{'=:=','$2',{const,"0"}}],
+ [{{"Match only for $1",'$2'}}]},
+
+ {{'$2',Term,['$3'|'_']},
+ [{is_list,'$2'},{'=:=','$3',$s}],
+ [{{"Matching term",'$2'}}]},
+
+ {{'$1','$2',872346},
+ [{'=:=','$2',CTerm}, {is_list,'$1'}],
+ [{{"Guard and result",'$2'}}]},
+
+ {{'$1', {'and','$1','again'}, '$2'},
+ [{is_float,'$2'}],
+ [{{"Match and result",'$1'}}]},
+
+ {{'$1', {'and','$1','again'}, '$2'},
+ [{'=:=','$1',CTerm}, {'=:=', '$2', "m&g"}],
+ [{{"Match and guard",'$2'}}]},
+
+ {{'$1', {'and','$1','again'}, "m&g&r"},
+ [{'=:=','$1',CTerm}],
+ [{{"Match, guard and result",'$1'}}]},
+
+ {{'$1', '$2', '$3'},
+ [{'=:=','$1',[{{second,'$2'}} | '$3']}],
+ [{{"Building guard"}}]}
+ ],
+
+ N_Result = [{"Guard only for $1", "v-element"},
+ {"Result only for $4", Term},
+ {"Match only for $1", "0"},
+ {"Matching term","something"},
+ {"Guard and result",Term},
+ {"Match and result",Term},
+ {"Match and guard","m&g"},
+ {"Match, guard and result",Term},
+ {"Building guard"}],
+
+ F = fun(N_MS_Perm) ->
+ t_match_spec_run_test(N_List, N_MS_Perm, N_Result)
+ end,
+ repeat_for_permutations(F, N_MS)
+ end,
+
+ test_terms(Fun, skip_refc_check),
+
?line verify_etsmem(EtsMem).
+t_match_spec_run_test(List, MS, Result) ->
+
+ %%io:format("ms = ~p\n",[MS]),
+
+ ?m(Result, ets:match_spec_run(List, ets:match_spec_compile(MS))),
+
+ %% Check that ets:select agree
+ Tab = ets:new(xxx, [bag]),
+ ets:insert(Tab, List),
+ SRes = lists:sort(Result),
+ ?m(SRes, lists:sort(ets:select(Tab, MS))),
+ ets:delete(Tab),
+
+ %% Check that tracing agree
+ Self = self(),
+ {Tracee, MonRef} = spawn_monitor(fun() -> ms_tracee(Self, List) end),
+ receive {Tracee, ready} -> ok end,
+
+ MST = lists:map(fun(Clause) -> ms_clause_ets_to_trace(Clause) end, MS),
+
+ %%io:format("MS = ~p\nMST= ~p\n",[MS,MST]),
+
+ erlang:trace_pattern({?MODULE,ms_tracee_dummy,'_'}, MST , [local]),
+ erlang:trace(Tracee, true, [call]),
+ Tracee ! start,
+ TRes = ms_tracer_collect(Tracee, MonRef, []),
+ %erlang:trace(Tracee, false, [call]),
+ %Tracee ! stop,
+ case TRes of
+ SRes -> ok;
+ _ ->
+ io:format("TRACE MATCH FAILED\n"),
+ io:format("Input = ~p\nMST = ~p\nExpected = ~p\nGot = ~p\n", [List, MST, SRes, TRes]),
+ ?t:fail("TRACE MATCH FAILED")
+ end,
+ ok.
+
+
+
+ms_tracer_collect(Tracee, Ref, Acc) ->
+ receive
+ {trace, Tracee, call, _Args, [Msg]} ->
+ %io:format("trace Args=~p Msg=~p\n", [_Args, Msg]),
+ ms_tracer_collect(Tracee, Ref, [Msg | Acc]);
+
+ {'DOWN', Ref, process, Tracee, _} ->
+ %io:format("monitor DOWN for ~p\n", [Tracee]),
+ TDRef = erlang:trace_delivered(Tracee),
+ ms_tracer_collect(Tracee, TDRef, Acc);
+
+ {trace_delivered, Tracee, Ref} ->
+ %%io:format("trace delivered for ~p\n", [Tracee]),
+ lists:sort(Acc);
+
+ Other ->
+ io:format("Unexpected message = ~p\n", [Other]),
+ ?t:fail("Unexpected tracer msg")
+ end.
+
+
+ms_tracee(Parent, CallArgList) ->
+ %io:format("ms_tracee ~p started with ArgList = ~p\n", [self(), CallArgList]),
+ Parent ! {self(), ready},
+ receive start -> ok end,
+ lists:foreach(fun(Args) ->
+ erlang:apply(?MODULE, ms_tracee_dummy, tuple_to_list(Args))
+ end, CallArgList).
+ %%receive stop -> ok end.
+
+
+
+ms_tracee_dummy(_) -> ok.
+ms_tracee_dummy(_,_) -> ok.
+ms_tracee_dummy(_,_,_) -> ok.
+ms_tracee_dummy(_,_,_,_) -> ok.
+
+ms_clause_ets_to_trace({Head, Guard, Body}) ->
+ {tuple_to_list(Head), Guard, [{message, Body}]}.
+
+assert_eq(A,A) -> ok;
+assert_eq(A,B) ->
+ io:format("FAILED MATCH:\n~p\n =/=\n~p\n",[A,B]),
+ ?t:fail("assert_eq failed").
t_repair_continuation(suite) ->
@@ -209,7 +405,7 @@ t_repair_continuation_do(Opts) ->
?line MS = [{'_',[],[true]}],
?line MS2 = [{{{'$1','_'},'_'},[],['$1']}],
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end,
?line F(1000,F),
?line {_,C} = ets:select(T,MS,5),
@@ -221,7 +417,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) -> ets:insert(T,{N,N}), F(N-1,F) end,
?line F(1000,F),
?line {_,C} = ets:select(T,MS,1001),
@@ -233,7 +429,7 @@ t_repair_continuation_do(Opts) ->
end)(),
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -248,7 +444,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[ordered_set|Opts]),
+ ?line T = ets_new(x,[ordered_set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{{integer_to_list(N),N},N}),
F(N-1,F)
@@ -264,7 +460,7 @@ t_repair_continuation_do(Opts) ->
end)(),
(fun() ->
- ?line T = ets:new(x,[set|Opts]),
+ ?line T = ets_new(x,[set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{N,N}),
F(N-1,F)
@@ -279,7 +475,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[set|Opts]),
+ ?line T = ets_new(x,[set|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -294,7 +490,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[bag|Opts]),
+ ?line T = ets_new(x,[bag|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -309,7 +505,7 @@ t_repair_continuation_do(Opts) ->
?line true = ets:delete(T)
end)(),
(fun() ->
- ?line T = ets:new(x,[duplicate_bag|Opts]),
+ ?line T = ets_new(x,[duplicate_bag|Opts]),
?line F = fun(0,_)->ok;(N,F) ->
ets:insert(T,{integer_to_list(N),N}),
F(N-1,F)
@@ -327,21 +523,22 @@ t_repair_continuation_do(Opts) ->
?line true = ets:is_compiled_ms(ets:match_spec_compile(MS)),
?line verify_etsmem(EtsMem).
-new(suite) -> [default,setbag,badnew,verybadnew,named,keypos2,privacy].
default(doc) ->
- ["Test case to check that a new ets table is defined as a `set' and "
- "`protected'"];
+ ["Check correct default vaules of a new ets table"];
default(suite) -> [];
default(Config) when is_list(Config) ->
%% Default should be set,protected
- repeat_for_opts(default_do).
-
-default_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Def = ets:new(def,Opts),
+ ?line Def = ets_new(def,[]),
?line set = ets:info(Def,type),
?line protected = ets:info(Def,protection),
+ Compressed = erlang:system_info(ets_always_compress),
+ ?line Compressed = ets:info(Def,compressed),
+ Self = self(),
+ ?line Self = ets:info(Def,owner),
+ ?line none = ets:info(Def, heir),
+ ?line false = ets:info(Def,named_table),
?line ets:delete(Def),
?line verify_etsmem(EtsMem).
@@ -355,7 +552,7 @@ select_fail(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
select_fail_do(Opts) ->
- ?line T = ets:new(x,Opts),
+ ?line T = ets_new(x,Opts),
?line ets:insert(T,{a,a}),
?line case (catch
ets:select(T,[{{a,'_'},[],[{snuffla}]}])) of
@@ -378,20 +575,27 @@ select_fail_do(Opts) ->
-define(S(T),ets:info(T,memory)).
-define(TAB_STRUCT_SZ, erts_debug:get_internal_state('DbTable_words')).
--define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap
+%%-define(NORMAL_TAB_STRUCT_SZ, 26). %% SunOS5.8, 32-bit, non smp, private heap
%%
%% The hardcoded expected memory sizes (in words) are the ones we expect on:
%% SunOS5.8, 32-bit, non smp, private heap
%%
-memory(doc) ->
- ["Whitebox test of ets:info(X,memory)"];
-memory(suite) ->
- [];
+memory(doc) -> ["Whitebox test of ets:info(X,memory)"];
+memory(suite) -> [];
memory(Config) when is_list(Config) ->
?line erts_debug:set_internal_state(available_internal_state, true),
?line ok = chk_normal_tab_struct_size(),
- ?line L = [T1,T2,T3,T4] = fill_sets_int(1000),
- ?line XRes1 = adjust_xmem(L, {16862,16072,16072,16078}),
+ repeat_for_opts(memory_do,[compressed]),
+ ?line catch erts_debug:set_internal_state(available_internal_state, false).
+
+memory_do(Opts) ->
+ ?line L = [T1,T2,T3,T4] = fill_sets_int(1000,Opts),
+ XR1 = case mem_mode(T1) of
+ {normal,_} -> {13836,13046,13046,13052}; %{13862,13072,13072,13078};
+ {compressed,4} -> {11041,10251,10251,10252}; %{11067,10277,10277,10278};
+ {compressed,8} -> {10050,9260,9260,9260} %{10076,9286,9286,9286}
+ end,
+ ?line XRes1 = adjust_xmem(L, XR1),
?line Res1 = {?S(T1),?S(T2),?S(T3),?S(T4)},
?line lists:foreach(fun(T) ->
Before = ets:info(T,size),
@@ -402,7 +606,12 @@ memory(Config) when is_list(Config) ->
[Key, ets:info(T,type), Before, ets:info(T,size), Objs])
end,
L),
- ?line XRes2 = adjust_xmem(L, {16849,16060,16048,16054}),
+ XR2 = case mem_mode(T1) of
+ {normal,_} -> {13826,13037,13028,13034}; %{13852,13063,13054,13060};
+ {compressed,4} -> {11031,10242,10233,10234}; %{11057,10268,10259,10260};
+ {compressed,8} -> {10040,9251,9242,9242} %10066,9277,9268,9268}
+ end,
+ ?line XRes2 = adjust_xmem(L, XR2),
?line Res2 = {?S(T1),?S(T2),?S(T3),?S(T4)},
?line lists:foreach(fun(T) ->
Before = ets:info(T,size),
@@ -413,13 +622,18 @@ memory(Config) when is_list(Config) ->
[Key, ets:info(T,type), Before, ets:info(T,size), Objs])
end,
L),
- ?line XRes3 = adjust_xmem(L, {16836,16048,16024,16030}),
+ XR3 = case mem_mode(T1) of
+ {normal,_} -> {13816,13028,13010,13016}; %{13842,13054,13036,13042};
+ {compressed,4} -> {11021,10233,10215,10216}; %{11047,10259,10241,10242};
+ {compressed,8} -> {10030,9242,9224,9224} %{10056,9268,9250,9250}
+ end,
+ ?line XRes3 = adjust_xmem(L, XR3),
?line Res3 = {?S(T1),?S(T2),?S(T3),?S(T4)},
?line lists:foreach(fun(T) ->
?line ets:delete_all_objects(T)
end,
L),
- ?line XRes4 = adjust_xmem(L, {76,286,286,286}),
+ ?line XRes4 = adjust_xmem(L, {50,260,260,260}), %{76,286,286,286}),
?line Res4 = {?S(T1),?S(T2),?S(T3),?S(T4)},
lists:foreach(fun(T) ->
?line ets:delete(T)
@@ -430,9 +644,9 @@ memory(Config) when is_list(Config) ->
?line ets:select_delete(T,[{'_',[],[true]}])
end,
L2),
- ?line XRes5 = adjust_xmem(L2, {76,286,286,286}),
+ ?line XRes5 = adjust_xmem(L2, {50,260,260,260}), %{76,286,286,286}),
?line Res5 = {?S(T11),?S(T12),?S(T13),?S(T14)},
- ?line ?t:format("XRes1 = ~p~n"
+ ?line io:format("XRes1 = ~p~n"
" Res1 = ~p~n~n"
"XRes2 = ~p~n"
" Res2 = ~p~n~n"
@@ -452,9 +666,15 @@ memory(Config) when is_list(Config) ->
?line XRes3 = Res3,
?line XRes4 = Res4,
?line XRes5 = Res5,
- ?line catch erts_debug:set_internal_state(available_internal_state, false),
?line ok.
+mem_mode(T) ->
+ {case ets:info(T,compressed) of
+ true -> compressed;
+ false -> normal
+ end,
+ erlang:system_info(wordsize)}.
+
chk_normal_tab_struct_size() ->
?line System = {os:type(),
os:version(),
@@ -462,36 +682,58 @@ chk_normal_tab_struct_size() ->
erlang:system_info(smp_support),
erlang:system_info(heap_type)},
?line ?t:format("System = ~p~n", [System]),
- ?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]),
+ %%?line ?t:format("?NORMAL_TAB_STRUCT_SZ=~p~n", [?NORMAL_TAB_STRUCT_SZ]),
?line ?t:format("?TAB_STRUCT_SZ=~p~n", [?TAB_STRUCT_SZ]),
- ?line case System of
- {{unix, sunos}, {5, 8, 0}, 4, false, private} ->
- ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ,
- ?line ok;
- _ ->
- ?line ok
- end.
-
-adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = Mem0) ->
+ ok.
+% ?line case System of
+% {{unix, sunos}, {5, 8, 0}, 4, false, private} ->
+% ?line ?NORMAL_TAB_STRUCT_SZ = ?TAB_STRUCT_SZ,
+% ?line ok;
+% _ ->
+% ?line ok
+% end.
+
+-define(DB_TREE_STACK_NEED,50). % The static stack for a tree, in halfword pointers are two internal words
+ % so the stack gets twice as big
+-define(DB_HASH_SIZEOF_EXTSEG,260). % The segment size in words, in halfword this will be twice as large.
+
+adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = _Mem0) ->
%% Adjust for 64-bit, smp, and os:
%% Table struct size may differ.
- Mem1 = case ?TAB_STRUCT_SZ of
- ?NORMAL_TAB_STRUCT_SZ ->
- Mem0;
- TabStructSz ->
- TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ,
- {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}
- end,
+
+% Mem1 = case ?TAB_STRUCT_SZ of
+% ?NORMAL_TAB_STRUCT_SZ ->
+% Mem0;
+% TabStructSz ->
+% TabDiff = TabStructSz - ?NORMAL_TAB_STRUCT_SZ,
+% {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}
+% end,
+
+ TabDiff = ?TAB_STRUCT_SZ,
+ Mem1 = {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff},
+
+ Mem2 = case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of
+ %% Halfword, corrections for regular pointers occupying two internal words.
+ {4,8} ->
+ {A1,B1,C1,D1} = Mem1,
+ {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED,
+ B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG,
+ C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG,
+ D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG};
+ _ ->
+ Mem1
+ end,
+
%% Adjust for hybrid and shared heaps:
%% Each record is one word smaller.
- Mem2 = case erlang:system_info(heap_type) of
- private ->
- Mem1;
- _ ->
- {A1,B1,C1,D1} = Mem1,
- {A1-ets:info(T1, size),B1-ets:info(T2, size),
- C1-ets:info(T3, size),D1-ets:info(T4, size)}
- end,
+ %%Mem2 = case erlang:system_info(heap_type) of
+ %% private ->
+ %% Mem1;
+ %% _ ->
+ %% {A1,B1,C1,D1} = Mem1,
+ %% {A1-ets:info(T1, size),B1-ets:info(T2, size),
+ %% C1-ets:info(T3, size),D1-ets:info(T4, size)}
+ %% end,
%%{Mem2,{ets:info(T1,stats),ets:info(T2,stats),ets:info(T3,stats),ets:info(T4,stats)}}.
Mem2.
@@ -510,7 +752,7 @@ t_whitebox(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
whitebox_1(Opts) ->
- ?line T=ets:new(x,[bag | Opts]),
+ ?line T=ets_new(x,[bag | Opts]),
?line ets:insert(T,[{du,glade},{ta,en}]),
?line ets:insert(T,[{hej,hopp2},{du,glade2},{ta,en2}]),
?line {_,C}=ets:match(T,{ta,'$1'},1),
@@ -520,8 +762,8 @@ whitebox_1(Opts) ->
ok.
whitebox_2(Opts) ->
- ?line T=ets:new(x,[ordered_set, {keypos,2} | Opts]),
- ?line T2=ets:new(x,[set, {keypos,2}| Opts]),
+ ?line T=ets_new(x,[ordered_set, {keypos,2} | Opts]),
+ ?line T2=ets_new(x,[set, {keypos,2}| Opts]),
?line 0 = ets:select_delete(T,[{{hej},[],[true]}]),
?line 0 = ets:select_delete(T,[{{hej,hopp},[],[true]}]),
?line 0 = ets:select_delete(T2,[{{hej},[],[true]}]),
@@ -543,7 +785,7 @@ t_ets_dets(Config, Opts) ->
?line (catch file:delete(Fname)),
?line {ok,DTab} = dets:open_file(testdets_1,
[{file, Fname}]),
- ?line ETab = ets:new(x,Opts),
+ ?line ETab = ets_new(x,Opts),
?line filltabint(ETab,3000),
?line DTab = ets:to_dets(ETab,DTab),
?line ets:delete_all_objects(ETab),
@@ -555,7 +797,7 @@ t_ets_dets(Config, Opts) ->
(catch ets:to_dets(ETab,DTab)),
?line {'EXIT',{badarg,[{ets,from_dets,[ETab,DTab]}|_]}} =
(catch ets:from_dets(ETab,DTab)),
- ?line ETab2 = ets:new(x,Opts),
+ ?line ETab2 = ets_new(x,Opts),
?line filltabint(ETab2,3000),
?line dets:close(DTab),
?line {'EXIT',{badarg,[{ets,to_dets,[ETab2,DTab]}|_]}} =
@@ -576,7 +818,7 @@ t_delete_all_objects(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
t_delete_all_objects_do(Opts) ->
- ?line T=ets:new(x,Opts),
+ ?line T=ets_new(x,Opts),
?line filltabint(T,4000),
?line O=ets:first(T),
?line ets:next(T,O),
@@ -605,7 +847,7 @@ t_delete_object(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
t_delete_object_do(Opts) ->
- ?line T = ets:new(x,Opts),
+ ?line T = ets_new(x,Opts),
?line filltabint(T,4000),
?line del_one_by_one_set(T,1,4001),
?line filltabint(T,4000),
@@ -622,19 +864,19 @@ t_delete_object_do(Opts) ->
?line 3999 = ets:info(T,size),
?line 0 = ets:info(T,kept_objects),
?line ets:delete(T),
- ?line T1 = ets:new(x,[ordered_set | Opts]),
+ ?line T1 = ets_new(x,[ordered_set | Opts]),
?line filltabint(T1,4000),
?line del_one_by_one_set(T1,1,4001),
?line filltabint(T1,4000),
?line del_one_by_one_set(T1,4000,0),
?line ets:delete(T1),
- ?line T2 = ets:new(x,[bag | Opts]),
+ ?line T2 = ets_new(x,[bag | Opts]),
?line filltabint2(T2,4000),
?line del_one_by_one_bag(T2,1,4001),
?line filltabint2(T2,4000),
?line del_one_by_one_bag(T2,4000,0),
?line ets:delete(T2),
- ?line T3 = ets:new(x,[duplicate_bag | Opts]),
+ ?line T3 = ets_new(x,[duplicate_bag | Opts]),
?line filltabint3(T3,4000),
?line del_one_by_one_dbag_1(T3,1,4001),
?line filltabint3(T3,4000),
@@ -681,7 +923,7 @@ t_init_table(Config) when is_list(Config)->
?line verify_etsmem(EtsMem).
t_init_table_do(Opts) ->
- ?line T = ets:new(x,[duplicate_bag | Opts]),
+ ?line T = ets_new(x,[duplicate_bag | Opts]),
?line filltabint(T,4000),
?line ets:init_table(T, make_init_fun(1)),
?line del_one_by_one_dbag_1(T,4000,0),
@@ -763,7 +1005,7 @@ t_insert_list(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
t_insert_list_do(Opts) ->
- ?line T = ets:new(x,[duplicate_bag | Opts]),
+ ?line T = ets_new(x,[duplicate_bag | Opts]),
?line do_fill_dbag_using_lists(T,4000),
?line del_one_by_one_dbag_2(T,4000,0),
?line ets:delete(T).
@@ -786,6 +1028,67 @@ t_test_ms(Config) when is_list(Config) ->
?line true = (if is_list(String) -> true; true -> false end),
?line verify_etsmem(EtsMem).
+t_select_reverse(doc) ->
+ ["Test the select reverse BIF's"];
+t_select_reverse(suite) ->
+ [];
+t_select_reverse(Config) when is_list(Config) ->
+ ?line Table = ets_new(xxx, [ordered_set]),
+ ?line filltabint(Table,1000),
+ ?line A = lists:reverse(ets:select(Table,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}])),
+ ?line A = ets:select_reverse(Table,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}]),
+ ?line A = reverse_chunked(Table,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}],3),
+ % A set/bag/duplicate_bag should get the same result regardless
+ % of select or select_reverse
+ ?line Table2 = ets_new(xxx, [set]),
+ ?line filltabint(Table2,1000),
+ ?line Table3 = ets_new(xxx, [bag]),
+ ?line filltabint(Table3,1000),
+ ?line Table4 = ets_new(xxx, [duplicate_bag]),
+ ?line filltabint(Table4,1000),
+ ?line lists:map(fun(Tab) ->
+ B = ets:select(Tab,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}]),
+ B = ets:select_reverse(Tab,[{{'$1', '_'},
+ [{'>',
+ {'rem',
+ '$1', 5},
+ 2}],
+ ['$_']}])
+ end,[Table2, Table3, Table4]),
+ ok.
+
+
+
+reverse_chunked(T,MS,N) ->
+ do_reverse_chunked(ets:select_reverse(T,MS,N),[]).
+
+do_reverse_chunked('$end_of_table',Acc) ->
+ lists:reverse(Acc);
+do_reverse_chunked({L,C},Acc) ->
+ NewAcc = lists:reverse(L)++Acc,
+ do_reverse_chunked(ets:select_reverse(C), NewAcc).
+
+
t_select_delete(doc) ->
["Test the ets:select_delete/2 and ets:select_count/2 BIF's"];
t_select_delete(suite) ->
@@ -1064,8 +1367,8 @@ random_test() ->
do_random_test() ->
?line EtsMem = etsmem(),
- ?line OrdSet = ets:new(xxx,[ordered_set]),
- ?line Set = ets:new(xxx,[]),
+ ?line OrdSet = ets_new(xxx,[ordered_set]),
+ ?line Set = ets_new(xxx,[]),
?line do_n_times(fun() ->
?line Key = create_random_string(25),
?line Value = create_random_tuple(25),
@@ -1269,8 +1572,8 @@ update_element_opts(Opts) ->
update_element_opts(Tuple,KeyPos,UpdPos,Opts) ->
- Set = ets:new(set,[{keypos,KeyPos} | Opts]),
- OrdSet = ets:new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]),
+ Set = ets_new(set,[{keypos,KeyPos} | Opts]),
+ OrdSet = ets_new(ordered_set,[ordered_set,{keypos,KeyPos} | Opts]),
update_element(Set,Tuple,KeyPos,UpdPos),
update_element(OrdSet,Tuple,KeyPos,UpdPos),
true = ets:delete(Set),
@@ -1278,7 +1581,7 @@ update_element_opts(Tuple,KeyPos,UpdPos,Opts) ->
ok.
update_element(T,Tuple,KeyPos,UpdPos) ->
- KeyList = [Key || Key <- lists:seq(1,100)],
+ KeyList = [17,"seventeen",<<"seventeen">>,{17},list_to_binary(lists:seq(1,100)),make_ref(), self()],
lists:foreach(fun(Key) ->
TupleWithKey = setelement(KeyPos,Tuple,Key),
update_element_do(T,TupleWithKey,Key,UpdPos)
@@ -1292,6 +1595,8 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
% This will try all combinations of {fromValue,toValue}
%
% IMPORTANT: size(Values) must be a prime number for this to work!!!
+
+ %io:format("update_element_do for key=~p\n",[Key]),
Big32 = 16#12345678,
Big64 = 16#123456789abcdef0,
Values = { 623, -27, 0, Big32, -Big32, Big64, -Big64, Big32*Big32,
@@ -1312,14 +1617,6 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
(ToIx, [], Pos, _Rand, _MeF) ->
{Pos, element(ToIx+1,Values)} % single {pos,value} arg
end,
-
- NewTupleF = fun({Pos,Val}, Tpl, _MeF) ->
- setelement(Pos, Tpl, Val);
- ([{Pos,Val} | Tail], Tpl, MeF) ->
- MeF(Tail,setelement(Pos, Tpl, Val),MeF);
- ([], Tpl, _MeF) ->
- Tpl
- end,
UpdateF = fun(ToIx,Rand) ->
PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF),
@@ -1327,7 +1624,7 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
ArgHash = erlang:phash2({Tab,Key,PosValArg}),
?line true = ets:update_element(Tab, Key, PosValArg),
?line ArgHash = erlang:phash2({Tab,Key,PosValArg}),
- NewTuple = NewTupleF(PosValArg,Tuple,NewTupleF),
+ NewTuple = update_tuple(PosValArg,Tuple),
?line [NewTuple] = ets:lookup(Tab,Key)
end,
@@ -1355,9 +1652,18 @@ update_element_do(Tab,Tuple,Key,UpdPos) ->
?line Checksum = (Length-1)*Length*(Length+1) div 2, % if Length is a prime
ok.
+update_tuple({Pos,Val}, Tpl) ->
+ setelement(Pos, Tpl, Val);
+update_tuple([{Pos,Val} | Tail], Tpl) ->
+ update_tuple(Tail,setelement(Pos, Tpl, Val));
+update_tuple([], Tpl) ->
+ Tpl.
+
+
+
update_element_neg(Opts) ->
- Set = ets:new(set,Opts),
- OrdSet = ets:new(ordered_set,[ordered_set | Opts]),
+ Set = ets_new(set,Opts),
+ OrdSet = ets_new(ordered_set,[ordered_set | Opts]),
update_element_neg_do(Set),
update_element_neg_do(OrdSet),
ets:delete(Set),
@@ -1365,8 +1671,8 @@ update_element_neg(Opts) ->
ets:delete(OrdSet),
?line {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key,{2,1})),
- ?line Bag = ets:new(bag,[bag | Opts]),
- ?line DBag = ets:new(duplicate_bag,[duplicate_bag | Opts]),
+ ?line Bag = ets_new(bag,[bag | Opts]),
+ ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]),
?line {'EXIT',{badarg,_}} = (catch ets:update_element(Bag,key,{2,1})),
?line {'EXIT',{badarg,_}} = (catch ets:update_element(DBag,key,{2,1})),
true = ets:delete(Bag),
@@ -1416,8 +1722,8 @@ update_counter(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
update_counter_do(Opts) ->
- Set = ets:new(set,Opts),
- OrdSet = ets:new(ordered_set,[ordered_set | Opts]),
+ Set = ets_new(set,Opts),
+ OrdSet = ets_new(ordered_set,[ordered_set | Opts]),
update_counter_for(Set),
update_counter_for(OrdSet),
ets:delete(Set),
@@ -1438,6 +1744,7 @@ update_counter_for(T) ->
(Obj, Times, Arg3, Myself) ->
?line {NewObj, Ret} = uc_mimic(Obj,Arg3),
ArgHash = erlang:phash2({T,a,Arg3}),
+ %%io:format("update_counter(~p, ~p, ~p) expecting ~p\n",[T,a,Arg3,Ret]),
?line Ret = ets:update_counter(T,a,Arg3),
?line ArgHash = erlang:phash2({T,a,Arg3}),
%%io:format("NewObj=~p~n ",[NewObj]),
@@ -1563,8 +1870,8 @@ uc_adder(Init, {_Pos, Add, Thres, Warp}) ->
end.
update_counter_neg(Opts) ->
- Set = ets:new(set,Opts),
- OrdSet = ets:new(ordered_set,[ordered_set | Opts]),
+ Set = ets_new(set,Opts),
+ OrdSet = ets_new(ordered_set,[ordered_set | Opts]),
update_counter_neg_for(Set),
update_counter_neg_for(OrdSet),
ets:delete(Set),
@@ -1572,8 +1879,8 @@ update_counter_neg(Opts) ->
ets:delete(OrdSet),
?line {'EXIT',{badarg,_}} = (catch ets:update_counter(OrdSet,key,1)),
- ?line Bag = ets:new(bag,[bag | Opts]),
- ?line DBag = ets:new(duplicate_bag,[duplicate_bag | Opts]),
+ ?line Bag = ets_new(bag,[bag | Opts]),
+ ?line DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]),
?line {'EXIT',{badarg,_}} = (catch ets:update_counter(Bag,key,1)),
?line {'EXIT',{badarg,_}} = (catch ets:update_counter(DBag,key,1)),
true = ets:delete(Bag),
@@ -1646,7 +1953,7 @@ wait_for_all(Pids0) ->
end.
evil_counter(I,Opts) ->
- T = ets:new(a, Opts),
+ T = ets_new(a, Opts),
Start0 = case I rem 3 of
0 -> 16#12345678;
1 -> 16#12345678FFFFFFFF;
@@ -1654,7 +1961,7 @@ evil_counter(I,Opts) ->
end,
Start = Start0 + random:uniform(100000),
ets:insert(T, {dracula,Start}),
- Iter = 90000,
+ Iter = 40000,
End = Start + Iter,
End = evil_counter_1(Iter, T),
ets:delete(T).
@@ -1675,7 +1982,7 @@ fixtable_next(Config) when is_list(Config) ->
fixtable_next_do(Opts) ->
?line EtsMem = etsmem(),
- ?line do_fixtable_next(ets:new(set,[public | Opts])),
+ ?line do_fixtable_next(ets_new(set,[public | Opts])),
?line verify_etsmem(EtsMem).
do_fixtable_next(Tab) ->
@@ -1756,24 +2063,24 @@ write_concurrency(doc) -> ["The 'write_concurrency' option"];
write_concurrency(suite) -> [];
write_concurrency(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- Yes1 = ets:new(foo,[public,{write_concurrency,true}]),
- Yes2 = ets:new(foo,[protected,{write_concurrency,true}]),
- No1 = ets:new(foo,[private,{write_concurrency,true}]),
+ Yes1 = ets_new(foo,[public,{write_concurrency,true}]),
+ Yes2 = ets_new(foo,[protected,{write_concurrency,true}]),
+ No1 = ets_new(foo,[private,{write_concurrency,true}]),
- Yes3 = ets:new(foo,[bag,public,{write_concurrency,true}]),
- Yes4 = ets:new(foo,[bag,protected,{write_concurrency,true}]),
- No2 = ets:new(foo,[bag,private,{write_concurrency,true}]),
+ Yes3 = ets_new(foo,[bag,public,{write_concurrency,true}]),
+ Yes4 = ets_new(foo,[bag,protected,{write_concurrency,true}]),
+ No2 = ets_new(foo,[bag,private,{write_concurrency,true}]),
- Yes5 = ets:new(foo,[duplicate_bag,public,{write_concurrency,true}]),
- Yes6 = ets:new(foo,[duplicate_bag,protected,{write_concurrency,true}]),
- No3 = ets:new(foo,[duplicate_bag,private,{write_concurrency,true}]),
+ Yes5 = ets_new(foo,[duplicate_bag,public,{write_concurrency,true}]),
+ Yes6 = ets_new(foo,[duplicate_bag,protected,{write_concurrency,true}]),
+ No3 = ets_new(foo,[duplicate_bag,private,{write_concurrency,true}]),
- No4 = ets:new(foo,[ordered_set,public,{write_concurrency,true}]),
- No5 = ets:new(foo,[ordered_set,protected,{write_concurrency,true}]),
- No6 = ets:new(foo,[ordered_set,private,{write_concurrency,true}]),
+ No4 = ets_new(foo,[ordered_set,public,{write_concurrency,true}]),
+ No5 = ets_new(foo,[ordered_set,protected,{write_concurrency,true}]),
+ No6 = ets_new(foo,[ordered_set,private,{write_concurrency,true}]),
- No7 = ets:new(foo,[public,{write_concurrency,false}]),
- No8 = ets:new(foo,[protected,{write_concurrency,false}]),
+ No7 = ets_new(foo,[public,{write_concurrency,false}]),
+ No8 = ets_new(foo,[protected,{write_concurrency,false}]),
?line YesMem = ets:info(Yes1,memory),
?line NoHashMem = ets:info(No1,memory),
@@ -1800,10 +2107,10 @@ write_concurrency(Config) when is_list(Config) ->
?line true = YesMem =:= NoHashMem
end,
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency,foo}])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency}])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,{write_concurrency,true,foo}])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(foo,[public,write_concurrency])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,foo}])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency}])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,{write_concurrency,true,foo}])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(foo,[public,write_concurrency])),
lists:foreach(fun(T) -> ets:delete(T) end,
[Yes1,Yes2,Yes3,Yes4,Yes5,Yes6,
@@ -1880,7 +2187,7 @@ heir_founder(Master, HeirData, Opts) ->
none -> {heir,none};
_ -> {heir, Heir, HeirData}
end,
- ?line T = ets:new(foo,[named_table, private, HeirTpl | Opts]),
+ ?line T = ets_new(foo,[named_table, private, HeirTpl | Opts]),
?line true = ets:insert(T,{key,1}),
?line [{key,1}] = ets:lookup(T,key),
Self = self(),
@@ -1952,7 +2259,7 @@ give_away(Config) when is_list(Config) ->
repeat_for_opts(give_away_do).
give_away_do(Opts) ->
- ?line T = ets:new(foo,[named_table, private | Opts]),
+ ?line T = ets_new(foo,[named_table, private | Opts]),
?line true = ets:insert(T,{key,1}),
?line [{key,1}] = ets:lookup(T,key),
Parent = self(),
@@ -1978,7 +2285,7 @@ give_away_do(Opts) ->
?line undefined = ets:info(T),
%% Give and then kill receiver to get back
- ?line T2 = ets:new(foo,[private | Opts]),
+ ?line T2 = ets_new(foo,[private | Opts]),
?line true = ets:insert(T2,{key,1}),
?line ets:setopts(T2,{heir,self(),"Som en gummiboll..."}),
?line {Receiver2,Mref2} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end),
@@ -2000,7 +2307,7 @@ give_away_do(Opts) ->
?line give_me = receive_any(),
?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,ReceiverNeg,"A deleted table")),
- ?line T3 = ets:new(foo,[public | Opts]),
+ ?line T3 = ets_new(foo,[public | Opts]),
spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")),
Parent ! done
end),
@@ -2035,7 +2342,7 @@ setopts(Config) when is_list(Config) ->
setopts_do(Opts) ->
Self = self(),
- ?line T = ets:new(foo,[named_table, private | Opts]),
+ ?line T = ets_new(foo,[named_table, private | Opts]),
?line none = ets:info(T,heir),
Heir = spawn_link(fun()->heir_heir(Self) end),
?line ets:setopts(T,{heir,Heir,"Data"}),
@@ -2088,10 +2395,10 @@ bad_table(Config) when is_list(Config) ->
bad_table_do(Opts, DummyFile) ->
Parent = self(),
- {Pid,Mref} = spawn_opt(fun()-> ets:new(priv,[private,named_table | Opts]),
- Priv = ets:new(priv,[private | Opts]),
- ets:new(prot,[protected,named_table | Opts]),
- Prot = ets:new(prot,[protected | Opts]),
+ {Pid,Mref} = spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]),
+ Priv = ets_new(priv,[private | Opts]),
+ ets_new(prot,[protected,named_table | Opts]),
+ Prot = ets_new(prot,[protected | Opts]),
Parent ! {self(),Priv,Prot},
die_please = receive_any()
end,
@@ -2149,11 +2456,11 @@ bad_table_do(Opts, DummyFile) ->
bad_table_op({Opts,Priv,Prot}, Op) ->
%%io:format("Doing Op=~p on ~p's\n",[Op,Type]),
- T1 = ets:new(noname,Opts),
+ T1 = ets_new(noname,Opts),
bad_table_call(noname,Op),
ets:delete(T1),
bad_table_call(T1,Op),
- T2 = ets:new(named,[named_table | Opts]),
+ T2 = ets_new(named,[named_table | Opts]),
ets:delete(T2),
bad_table_call(named,Op),
bad_table_call(T2,Op),
@@ -2187,7 +2494,7 @@ rename(Config) when is_list(Config) ->
rename_do(Opts) ->
?line EtsMem = etsmem(),
- ets:new(foobazz,[named_table, public | Opts]),
+ ets_new(foobazz,[named_table, public | Opts]),
ets:insert(foobazz,{foo,bazz}),
ungermanbazz = ets:rename(foobazz,ungermanbazz),
{'EXIT',{badarg, _}} = (catch ets:lookup(foobazz,foo)),
@@ -2205,7 +2512,7 @@ rename_unnamed(Config) when is_list(Config) ->
rename_unnamed_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(bonkz,[public | Opts]),
+ ?line Tab = ets_new(bonkz,[public | Opts]),
?line {'EXIT',{badarg, _}} = (catch ets:insert(bonkz,{foo,bazz})),
?line bonkz = ets:info(Tab, name),
?line Tab = ets:rename(Tab, tjabonkz),
@@ -2224,7 +2531,7 @@ evil_rename(Config) when is_list(Config) ->
evil_rename_1(Old, New, Flags) ->
?line process_flag(trap_exit, true),
- ?line Old = ets:new(Old, Flags),
+ ?line Old = ets_new(Old, Flags),
?line Fixer = fun() -> ets:safe_fixtable(Old, true) end,
?line crazy_fixtable(15000, Fixer),
?line erlang:yield(),
@@ -2234,7 +2541,7 @@ evil_rename_1(Old, New, Flags) ->
ok.
crazy_fixtable(N, Fixer) ->
- Dracula = ets:new(count_dracula, [public]),
+ Dracula = ets_new(count_dracula, [public]),
ets:insert(Dracula, {count,0}),
SpawnFun = fun() ->
Fixer(),
@@ -2268,7 +2575,7 @@ evil_creater_destroyer() ->
ets:delete(T1).
evil_create_fixed_tab() ->
- T = ets:new(arne, [public]),
+ T = ets_new(arne, [public]),
ets:safe_fixtable(T, true),
T.
@@ -2282,8 +2589,8 @@ interface_equality(Config) when is_list(Config) ->
interface_equality_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Set = ets:new(set,[set | Opts]),
- ?line OrderedSet = ets:new(ordered_set,[ordered_set | Opts]),
+ ?line Set = ets_new(set,[set | Opts]),
+ ?line OrderedSet = ets_new(ordered_set,[ordered_set | Opts]),
?line F = fun(X,T,FF) -> case X of
0 -> true;
_ ->
@@ -2362,7 +2669,7 @@ ordered_match_do(Opts) ->
FF(X-1,T,FF)
end
end,
- ?line T1 = ets:new(xxx,[ordered_set| Opts]),
+ ?line T1 = ets_new(xxx,[ordered_set| Opts]),
?line F(3000,T1,F),
?line [[3,3],[3,3],[3,3]] = ets:match(T1, {'_','_','$1','$2',3}),
?line F2 = fun(X,Rem,Res,FF) -> case X of
@@ -2400,7 +2707,7 @@ ordered(Config) when is_list(Config) ->
ordered_do(Opts) ->
?line EtsMem = etsmem(),
- ?line T = ets:new(oset, [ordered_set | Opts]),
+ ?line T = ets_new(oset, [ordered_set | Opts]),
?line InsList = [
25,26,27,28,
5,6,7,8,
@@ -2461,8 +2768,8 @@ setbag(doc) -> ["Small test case for both set and bag type ets tables."];
setbag(suite) -> [];
setbag(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line Set = ets:new(set,[set]),
- ?line Bag = ets:new(bag,[bag]),
+ ?line Set = ets_new(set,[set]),
+ ?line Bag = ets_new(bag,[bag]),
?line Key = {foo,bar},
%% insert some value
@@ -2482,15 +2789,15 @@ setbag(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
badnew(doc) ->
- ["Test case to check proper return values for illegal ets:new() calls."];
+ ["Test case to check proper return values for illegal ets_new() calls."];
badnew(suite) -> [];
badnew(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(12,[])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new({a,b},[])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(name,[foo])),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(name,{bag})),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(name,bag)),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(12,[])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new({a,b},[])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(name,[foo])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(name,{bag})),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(name,bag)),
?line verify_etsmem(EtsMem).
verybadnew(doc) ->
@@ -2499,7 +2806,7 @@ verybadnew(doc) ->
verybadnew(suite) -> [];
verybadnew(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line {'EXIT',{badarg,_}} = (catch ets:new(verybad,[set|protected])),
+ ?line {'EXIT',{badarg,_}} = (catch ets_new(verybad,[set|protected])),
?line verify_etsmem(EtsMem).
named(doc) -> ["Small check to see if named tables work."];
@@ -2576,9 +2883,9 @@ privacy_check(Pub,Prot,Priv) ->
?line [] = ets:lookup(Prot,foo).
privacy_owner(Boss, Opts) ->
- ets:new(pub, [public,named_table | Opts]),
- ets:new(prot,[protected,named_table | Opts]),
- ets:new(priv,[private,named_table | Opts]),
+ ets_new(pub, [public,named_table | Opts]),
+ ets_new(prot,[protected,named_table | Opts]),
+ ets_new(priv,[private,named_table | Opts]),
Boss ! ok,
privacy_owner_loop(Boss).
@@ -2605,8 +2912,6 @@ rotate_tuple(Tuple, N) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-insert(doc) -> ["Test proper and improper inserts into a table."];
-insert(suite) -> [empty,badinsert].
empty(doc) ->
["Check lookup in an empty table and lookup of a non-existing key"];
@@ -2616,7 +2921,7 @@ empty(Config) when is_list(Config) ->
empty_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line [] = ets:lookup(Tab,key),
?line true = ets:insert(Tab,{key2,val}),
?line [] = ets:lookup(Tab,key),
@@ -2633,10 +2938,10 @@ badinsert_do(Opts) ->
?line EtsMem = etsmem(),
?line {'EXIT',{badarg,_}} = (catch ets:insert(foo,{key,val})),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,{})),
- ?line Tab3 = ets:new(foo,[{keypos,3}| Opts]),
+ ?line Tab3 = ets_new(foo,[{keypos,3}| Opts]),
?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab3,{a,b})),
?line {'EXIT',{badarg,_}} = (catch ets:insert(Tab,[key,val2])),
@@ -2646,8 +2951,6 @@ badinsert_do(Opts) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-lookup(doc) -> ["Some tests for lookups (timing, bad lookups, etc.)."];
-lookup(suite) -> [time_lookup,badlookup,lookup_order].
time_lookup(doc) -> ["Lookup timing."];
time_lookup(suite) -> [];
@@ -2660,7 +2963,7 @@ time_lookup(Config) when is_list(Config) ->
"~p ets lookups/s",[Values]))}.
time_lookup_do(Opts) ->
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line ets:insert(Tab,{{a,key},foo}),
?line {Time,_} = ?t:timecall(test_server,do_times,
@@ -2675,7 +2978,7 @@ badlookup(suite) -> [];
badlookup(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(foo,key)),
- ?line Tab = ets:new(foo,[]),
+ ?line Tab = ets_new(foo,[]),
?line ets:delete(Tab),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(Tab,key)),
?line verify_etsmem(EtsMem).
@@ -2700,7 +3003,7 @@ lookup_order_2(Opts, Fixed) ->
Pair = [{A,B},{B,A},{A,C},{C,A},{B,C},{C,B}],
Combos = [{D1,D2,D3} || D1<-ABC, D2<-Pair, D3<-Pair],
lists:foreach(fun({D1,{D2a,D2b},{D3a,D3b}}) ->
- T = ets:new(foo,Opts),
+ T = ets_new(foo,Opts),
case Fixed of
true -> ets:safe_fixtable(T,true);
false -> ok
@@ -2774,8 +3077,6 @@ fill_tab(Tab,Val) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-lookup_element(doc) -> ["Some tests for lookup_element."];
-lookup_element(suite) -> [lookup_element_mult].
lookup_element_mult(doc) -> ["Multiple return elements (OTP-2386)"];
lookup_element_mult(suite) -> [];
@@ -2784,10 +3085,12 @@ lookup_element_mult(Config) when is_list(Config) ->
lookup_element_mult_do(Opts) ->
?line EtsMem = etsmem(),
- ?line T = ets:new(service, [bag, {keypos, 2} | Opts]),
+ ?line T = ets_new(service, [bag, {keypos, 2} | Opts]),
?line D = lists:reverse(lem_data()),
?line lists:foreach(fun(X) -> ets:insert(T, X) end, D),
?line ok = lem_crash_3(T),
+ ?line ets:insert(T, {0, "heap_key"}),
+ ?line ets:lookup_element(T, "heap_key", 2),
?line true = ets:delete(T),
?line verify_etsmem(EtsMem).
@@ -2815,11 +3118,6 @@ lem_crash_3(T) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-delete(doc) ->
- ["Check delete functionality (proper/improper deletes)"];
-delete(suite) ->
- [delete_elem,delete_tab,delete_large_tab,delete_large_named_table,evil_delete,
- table_leak,baddelete,match_delete,match_delete3].
delete_elem(doc) ->
["Check delete of an element inserted in a `filled' table."];
@@ -2829,7 +3127,7 @@ delete_elem(Config) when is_list(Config) ->
delete_elem_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line ets:insert(Tab,{{b,key},foo}),
?line ets:insert(Tab,{{c,key},foo}),
@@ -2849,17 +3147,17 @@ delete_tab(Config) when is_list(Config) ->
delete_tab_do(Opts) ->
Name = foo,
?line EtsMem = etsmem(),
- ?line Name = ets:new(Name, [named_table | Opts]),
+ ?line Name = ets_new(Name, [named_table | Opts]),
?line true = ets:delete(foo),
%% The name should be available again.
- ?line Name = ets:new(Name, [named_table | Opts]),
+ ?line Name = ets_new(Name, [named_table | Opts]),
?line true = ets:delete(Name),
?line verify_etsmem(EtsMem).
delete_large_tab(doc) ->
"Check that ets:delete/1 works and that other processes can run.";
delete_large_tab(Config) when is_list(Config) ->
- ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 200000)],
?line EtsMem = etsmem(),
repeat_for_opts(fun(Opts) -> delete_large_tab_do(Opts,Data) end),
?line verify_etsmem(EtsMem).
@@ -2871,7 +3169,7 @@ delete_large_tab_do(Opts,Data) ->
delete_large_tab_1(Name, Flags, Data, Fix) ->
- ?line Tab = ets:new(Name, Flags),
+ ?line Tab = ets_new(Name, Flags),
?line ets:insert(Tab, Data),
case Fix of
@@ -2938,7 +3236,7 @@ delete_large_named_table_do(Opts,Data) ->
?line delete_large_named_table_1(foo_hash, [named_table | Opts], Data, true).
delete_large_named_table_1(Name, Flags, Data, Fix) ->
- ?line Tab = ets:new(Name, Flags),
+ ?line Tab = ets_new(Name, Flags),
?line ets:insert(Tab, Data),
case Fix of
@@ -2951,7 +3249,7 @@ delete_large_named_table_1(Name, Flags, Data, Fix) ->
Pid = spawn_link(fun() ->
receive
{trace,Parent,call,_} ->
- ets:new(Name, [named_table])
+ ets_new(Name, [named_table])
end
end),
?line erlang:trace(self(), true, [call,{tracer,Pid}]),
@@ -2985,7 +3283,7 @@ evil_delete_do(Opts,Data) ->
evil_delete_not_owner(Name, Flags, Data, Fix) ->
io:format("Not owner: ~p, fix = ~p", [Name,Fix]),
- ?line Tab = ets:new(Name, [public|Flags]),
+ ?line Tab = ets_new(Name, [public|Flags]),
?line ets:insert(Tab, Data),
case Fix of
false -> ok;
@@ -3010,7 +3308,7 @@ evil_delete_not_owner(Name, Flags, Data, Fix) ->
evil_delete_owner(Name, Flags, Data, Fix) ->
?line Fun = fun() ->
- ?line Tab = ets:new(Name, [public|Flags]),
+ ?line Tab = ets_new(Name, [public|Flags]),
?line ets:insert(Tab, Data),
case Fix of
false -> ok;
@@ -3037,48 +3335,60 @@ exit_large_table_owner(doc) ->
exit_large_table_owner(suite) ->
[];
exit_large_table_owner(Config) when is_list(Config) ->
- ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok};
+ (I) -> Do({erlang:phash2(I, 16#ffffff),I}),
+ {true, I+1}
+ end, 1)
+ end,
?line EtsMem = etsmem(),
- repeat_for_opts(fun(Opts) -> exit_large_table_owner_do(Opts,Data,Config) end),
+ repeat_for_opts({exit_large_table_owner_do,{FEData,Config}}),
?line verify_etsmem(EtsMem).
-exit_large_table_owner_do(Opts,Data,Config) ->
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], true, 1, 1),
- ?line verify_rescheduling_exit(Config, Data, Opts, false, 1, 1).
+exit_large_table_owner_do(Opts,{FEData,Config}) ->
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 1, 1),
+ ?line verify_rescheduling_exit(Config, FEData, Opts, false, 1, 1).
exit_many_large_table_owner(doc) -> [];
exit_many_large_table_owner(suite) -> [];
exit_many_large_table_owner(Config) when is_list(Config) ->
- ?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ %%?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 500000)],
+ ?line FEData = fun(Do) -> repeat_while(fun(500000) -> {false,ok};
+ (I) -> Do({erlang:phash2(I, 16#ffffff),I}),
+ {true, I+1}
+ end, 1)
+ end,
?line EtsMem = etsmem(),
- repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,Data,Config) end),
+ repeat_for_opts(fun(Opts) -> exit_many_large_table_owner_do(Opts,FEData,Config) end),
?line verify_etsmem(EtsMem).
-exit_many_large_table_owner_do(Opts,Data,Config) ->
- ?line verify_rescheduling_exit(Config, Data, Opts, true, 1, 4),
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], false, 1, 4).
+exit_many_large_table_owner_do(Opts,FEData,Config) ->
+ ?line verify_rescheduling_exit(Config, FEData, Opts, true, 1, 4),
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 1, 4).
exit_many_tables_owner(doc) -> [];
exit_many_tables_owner(suite) -> [];
exit_many_tables_owner(Config) when is_list(Config) ->
+ NoData = fun(_Do) -> ok end,
?line EtsMem = etsmem(),
- ?line verify_rescheduling_exit(Config, [], [named_table], false, 1000, 1),
- ?line verify_rescheduling_exit(Config, [], [named_table,{write_concurrency,true}], false, 1000, 1),
+ ?line verify_rescheduling_exit(Config, NoData, [named_table], false, 1000, 1),
+ ?line verify_rescheduling_exit(Config, NoData, [named_table,{write_concurrency,true}], false, 1000, 1),
?line verify_etsmem(EtsMem).
exit_many_many_tables_owner(doc) -> [];
exit_many_many_tables_owner(suite) -> [];
exit_many_many_tables_owner(Config) when is_list(Config) ->
?line Data = [{erlang:phash2(I, 16#ffffff),I} || I <- lists:seq(1, 50)],
- repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,Data,Config) end).
+ ?line FEData = fun(Do) -> lists:foreach(Do, Data) end,
+ repeat_for_opts(fun(Opts) -> exit_many_many_tables_owner_do(Opts,FEData,Config) end).
-exit_many_many_tables_owner_do(Opts,Data,Config) ->
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], true, 200, 5),
- ?line verify_rescheduling_exit(Config, Data, Opts, false, 200, 5),
+exit_many_many_tables_owner_do(Opts,FEData,Config) ->
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], true, 200, 5),
+ ?line verify_rescheduling_exit(Config, FEData, Opts, false, 200, 5),
?line wait_for_test_procs(),
?line EtsMem = etsmem(),
- ?line verify_rescheduling_exit(Config, Data, Opts, true, 200, 5),
- ?line verify_rescheduling_exit(Config, Data, [named_table | Opts], false, 200, 5),
+ ?line verify_rescheduling_exit(Config, FEData, Opts, true, 200, 5),
+ ?line verify_rescheduling_exit(Config, FEData, [named_table | Opts], false, 200, 5),
?line verify_etsmem(EtsMem).
@@ -3121,7 +3431,7 @@ vre_fix_tables(Tab) ->
receive Go -> ok end,
ok.
-verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) ->
+verify_rescheduling_exit(Config, ForEachData, Flags, Fix, NOTabs, NOProcs) ->
?line NoFix = 5,
?line TestCase = atom_to_list(?config(test_case, Config)),
?line Parent = self(),
@@ -3136,8 +3446,8 @@ verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) ->
++ "-" ++ integer_to_list(A)
++ "-" ++ integer_to_list(B)
++ "-" ++ integer_to_list(C)),
- Tab = ets:new(Name, Flags),
- ets:insert(Tab, Data),
+ Tab = ets_new(Name, Flags),
+ ForEachData(fun(Data) -> ets:insert(Tab, Data) end),
case Fix of
false -> ok;
true ->
@@ -3145,10 +3455,10 @@ verify_rescheduling_exit(Config, Data, Flags, Fix, NOTabs, NOProcs) ->
vre_fix_tables(Tab)
end,
lists:seq(1,NoFix)),
- lists:foreach(fun({K,_}) ->
- ets:delete(Tab, K)
- end,
- Data)
+ KeyPos = ets:info(Tab,keypos),
+ ForEachData(fun(Data) ->
+ ets:delete(Tab, element(KeyPos,Data))
+ end)
end
end,
NOTabs),
@@ -3195,7 +3505,7 @@ table_leak(Config) when is_list(Config) ->
table_leak_1(_,0) -> ok;
table_leak_1(Opts,N) ->
- ?line T = ets:new(fooflarf, Opts),
+ ?line T = ets_new(fooflarf, Opts),
?line true = ets:delete(T),
table_leak_1(Opts,N-1).
@@ -3205,7 +3515,7 @@ baddelete(suite) -> [];
baddelete(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
?line {'EXIT',{badarg,_}} = (catch ets:delete(foo)),
- ?line Tab = ets:new(foo,[]),
+ ?line Tab = ets_new(foo,[]),
?line true = ets:delete(Tab),
?line {'EXIT',{badarg,_}} = (catch ets:delete(Tab)),
?line verify_etsmem(EtsMem).
@@ -3220,7 +3530,7 @@ match_delete(Config) when is_list(Config) ->
match_delete_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(kad,Opts),
+ ?line Tab = ets_new(kad,Opts),
?line fill_tab(Tab,foo),
?line ets:insert(Tab,{{c,key},bar}),
?line _ = ets:match_delete(Tab,{'_',foo}),
@@ -3264,7 +3574,7 @@ firstnext(Config) when is_list(Config) ->
firstnext_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line [] = firstnext_collect(Tab,ets:first(Tab),[]),
?line fill_tab(Tab,foo),
?line Len = length(ets:tab2list(Tab)),
@@ -3287,10 +3597,10 @@ firstnext_concurrent(Config) when is_list(Config) ->
[dynamic_go() || _ <- lists:seq(1, 2)],
receive
after 5000 -> ok
- end.
+ end.
ets_init(Tab, N) ->
- ets:new(Tab, [named_table,public,ordered_set]),
+ ets_new(Tab, [named_table,public,ordered_set]),
cycle(Tab, lists:seq(1,N+1)).
cycle(_Tab, [H|T]) when H > length(T)-> ok;
@@ -3323,7 +3633,7 @@ slot(Config) when is_list(Config) ->
slot_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line Elts = ets:info(Tab,size),
?line Elts = slot_loop(Tab,0,0),
@@ -3342,7 +3652,6 @@ slot_loop(Tab,SlotNo,EltsSoFar) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-match(suite) -> [match1, match2, match_object, match_object2].
match1(suite) -> [];
match1(Config) when is_list(Config) ->
@@ -3350,7 +3659,7 @@ match1(Config) when is_list(Config) ->
match1_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line fill_tab(Tab,foo),
?line [] = ets:match(Tab,{}),
?line ets:insert(Tab,{{one,4},4}),
@@ -3415,7 +3724,7 @@ match_object(Config) when is_list(Config) ->
match_object_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foobar, Opts),
+ ?line Tab = ets_new(foobar, Opts),
?line fill_tab(Tab, foo),
?line ets:insert(Tab, {{one, 4}, 4}),
?line ets:insert(Tab,{{one,5},5}),
@@ -3459,7 +3768,7 @@ match_object2(Config) when is_list(Config) ->
match_object2_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo, [bag, {keypos, 2} | Opts]),
+ ?line Tab = ets_new(foo, [bag, {keypos, 2} | Opts]),
?line fill_tab2(Tab, 0, 13005), % match_db_object does 1000
% elements per pass, might
% change in the future.
@@ -3477,7 +3786,6 @@ match_object2_do(Opts) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-misc(suite) -> [misc1, safe_fixtable, info, dups, tab2list].
tab2list(doc) -> ["Tests tab2list (OTP-3319)"];
tab2list(suite) -> [];
@@ -3498,7 +3806,7 @@ misc1(Config) when is_list(Config) ->
misc1_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo,Opts),
+ ?line Tab = ets_new(foo,Opts),
?line true = lists:member(Tab,ets:all()),
?line ets:delete(Tab),
?line false = lists:member(Tab,ets:all()),
@@ -3517,7 +3825,7 @@ safe_fixtable(Config) when is_list(Config) ->
safe_fixtable_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foo, Opts),
+ ?line Tab = ets_new(foo, Opts),
?line fill_tab(Tab, foobar),
?line true = ets:safe_fixtable(Tab, true),
?line receive after 1 -> ok end,
@@ -3556,7 +3864,7 @@ info_do(Opts) ->
?line EtsMem = etsmem(),
?line MeMyselfI=self(),
?line ThisNode=node(),
- ?line Tab = ets:new(foobar, [{keypos, 2} | Opts]),
+ ?line Tab = ets_new(foobar, [{keypos, 2} | Opts]),
%% Note: ets:info/1 used to return a tuple, but from R11B onwards it
%% returns a list.
@@ -3610,15 +3918,12 @@ dups_do(Opts) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-files(suite) -> [tab2file, tab2file2, tab2file3, tabfile_ext1, tabfile_ext2,
- tabfile_ext3, tabfile_ext4].
-
tab2file(doc) -> ["Check the ets:tab2file function on an empty "
"ets table."];
tab2file(suite) -> [];
tab2file(Config) when is_list(Config) ->
%% Write an empty ets table to a file, read back and check properties.
- ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, set, private,
+ ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, set, private,
{keypos, 2}]),
?line FName = filename:join([?config(priv_dir, Config),"tab2file_case"]),
?line ok = ets:tab2file(Tab, FName),
@@ -3634,51 +3939,36 @@ tab2file(Config) when is_list(Config) ->
?line verify_etsmem(EtsMem).
tab2file2(doc) -> ["Check the ets:tab2file function on a ",
- "filled set type ets table."];
+ "filled set/bag type ets table."];
tab2file2(suite) -> [];
-tab2file2(Config) when is_list(Config) ->
- %% Try the same on a filled set table.
- ?line EtsMem = etsmem(),
- ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, set, private,
- {keypos, 2}]),
- ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]),
- ?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!)
- ?line Len = length(ets:tab2list(Tab)),
- ?line ok = ets:tab2file(Tab, FName),
- ?line true = ets:delete(Tab),
- %
- ?line {ok, Tab2} = ets:file2tab(FName),
- ?line private = ets:info(Tab2, protection),
- ?line true = ets:info(Tab2, named_table),
- ?line 2 = ets:info(Tab2, keypos),
- ?line set = ets:info(Tab2, type),
- ?line Len = length(ets:tab2list(Tab2)),
- ?line true = ets:delete(Tab2),
- ?line verify_etsmem(EtsMem).
+tab2file2(Config) when is_list(Config) ->
+ repeat_for_opts({tab2file2_do,Config}, [[set,bag],compressed]).
-tab2file3(doc) -> ["Check the ets:tab2file function on a ",
- "filled bag type ets table."];
-tab2file3(suite) -> [];
-tab2file3(Config) when is_list(Config) ->
- %% Try the same on a filled bag table.
+tab2file2_do(Opts, Config) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(ets_SUITE_foo_tab, [named_table, bag, private,
- {keypos, 2}]),
- ?line FName = filename:join([?config(priv_dir, Config),"tab2file3_case"]),
+ ?line Tab = ets_new(ets_SUITE_foo_tab, [named_table, private,
+ {keypos, 2} | Opts]),
+ ?line FName = filename:join([?config(priv_dir, Config),"tab2file2_case"]),
?line ok = fill_tab2(Tab, 0, 10000), % Fill up the table (grucho mucho!)
?line Len = length(ets:tab2list(Tab)),
?line Mem = ets:info(Tab, memory),
+ ?line Type = ets:info(Tab, type),
+ %%io:format("org tab: ~p\n",[ets:info(Tab)]),
?line ok = ets:tab2file(Tab, FName),
?line true = ets:delete(Tab),
+ ?line EtsMem4 = etsmem(),
+
?line {ok, Tab2} = ets:file2tab(FName),
+ %%io:format("loaded tab: ~p\n",[ets:info(Tab2)]),
?line private = ets:info(Tab2, protection),
?line true = ets:info(Tab2, named_table),
?line 2 = ets:info(Tab2, keypos),
- ?line bag = ets:info(Tab2, type),
+ ?line Type = ets:info(Tab2, type),
?line Len = length(ets:tab2list(Tab2)),
?line Mem = ets:info(Tab2, memory),
?line true = ets:delete(Tab2),
+ io:format("Between = ~p\n", [EtsMem4]),
?line verify_etsmem(EtsMem).
-define(test_list, [8,5,4,1,58,125,255, 250, 245, 240, 235,
@@ -3722,7 +4012,7 @@ tabfile_ext1_do(Opts,Config) ->
?line FName = filename:join([?config(priv_dir, Config),"nisse.dat"]),
?line FName2 = filename:join([?config(priv_dir, Config),"countflip.dat"]),
L = lists:seq(1,10),
- T = ets:new(x,Opts),
+ T = ets_new(x,Opts),
Name = make_ref(),
[ets:insert(T,{X,integer_to_list(X)}) || X <- L],
ok = ets:tab2file(T,FName,[{extended_info,[object_count]}]),
@@ -3762,7 +4052,7 @@ tabfile_ext2_do(Opts,Config) ->
?line FName = filename:join([?config(priv_dir, Config),"olle.dat"]),
?line FName2 = filename:join([?config(priv_dir, Config),"bitflip.dat"]),
L = lists:seq(1,10),
- T = ets:new(x,Opts),
+ T = ets_new(x,Opts),
Name = make_ref(),
[ets:insert(T,{X,integer_to_list(X)}) || X <- L],
ok = ets:tab2file(T,FName,[{extended_info,[md5sum]}]),
@@ -3800,7 +4090,7 @@ tabfile_ext3(Config) when is_list(Config) ->
?line FName2 = filename:join([?config(priv_dir, Config),"ncountflip.dat"]),
L = lists:seq(1,10),
Name = make_ref(),
- ?MODULE = ets:new(?MODULE,[named_table]),
+ ?MODULE = ets_new(?MODULE,[named_table]),
[ets:insert(?MODULE,{X,integer_to_list(X)}) || X <- L],
ets:tab2file(?MODULE,FName),
{error,cannot_create_table} = ets:file2tab(FName),
@@ -3832,7 +4122,7 @@ tabfile_ext4(doc) ->
tabfile_ext4(Config) when is_list(Config) ->
?line FName = filename:join([?config(priv_dir, Config),"bauta.dat"]),
LL = lists:seq(1,10000),
- TL = ets:new(x,[]),
+ TL = ets_new(x,[]),
Name2 = make_ref(),
[ets:insert(TL,{X,integer_to_list(X)}) || X <- LL],
ok = ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
@@ -3877,7 +4167,6 @@ make_sub_binary(List, Num) when is_list(List) ->
{_,B} = split_binary(Bin, N+1),
B.
-heavy(suite) -> [heavy_lookup, heavy_lookup_element].
%% Lookup stuff like crazy...
heavy_lookup(doc) -> ["Performs multiple lookups for every key ",
@@ -3888,7 +4177,7 @@ heavy_lookup(Config) when is_list(Config) ->
heavy_lookup_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foobar_table, [set, protected, {keypos, 2} | Opts]),
+ ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
?line ok = fill_tab2(Tab, 0, 7000),
?line ?t:do_times(50, ?MODULE, do_lookup, [Tab, 6999]),
?line true = ets:delete(Tab),
@@ -3911,7 +4200,7 @@ heavy_lookup_element(Config) when is_list(Config) ->
heavy_lookup_element_do(Opts) ->
?line EtsMem = etsmem(),
- ?line Tab = ets:new(foobar_table, [set, protected, {keypos, 2} | Opts]),
+ ?line Tab = ets_new(foobar_table, [set, protected, {keypos, 2} | Opts]),
?line ok = fill_tab2(Tab, 0, 7000),
case os:type() of
vxworks ->
@@ -3940,9 +4229,44 @@ do_lookup_element(Tab, N, M) ->
end.
-fold(suite) -> [foldl_ordered, foldr_ordered,
- foldl, foldr,
- fold_empty].
+heavy_concurrent(Config) when is_list(Config) ->
+ repeat_for_opts(do_heavy_concurrent).
+
+do_heavy_concurrent(Opts) ->
+ ?line Size = 10000,
+ ?line EtsMem = etsmem(),
+ ?line Tab = ets_new(blupp, [set, public, {keypos, 2} | Opts]),
+ ?line ok = fill_tab2(Tab, 0, Size),
+ ?line Procs = lists:map(
+ fun (N) ->
+ spawn_link(
+ fun () ->
+ do_heavy_concurrent_proc(Tab, Size, N)
+ end)
+ end,
+ lists:seq(1, 500)),
+ ?line lists:foreach(fun (P) ->
+ M = erlang:monitor(process, P),
+ receive
+ {'DOWN', M, process, P, _} ->
+ ok
+ end
+ end,
+ Procs),
+ ?line true = ets:delete(Tab),
+ ?line verify_etsmem(EtsMem).
+
+do_heavy_concurrent_proc(_Tab, 0, _Offs) ->
+ done;
+do_heavy_concurrent_proc(Tab, N, Offs) when (N+Offs) rem 100 == 0 ->
+ Data = {"here", are, "S O M E ", data, "toooooooooooooooooo", insert,
+ make_ref(), make_ref(), make_ref()},
+ true=ets:insert(Tab, {{self(),Data}, N}),
+ do_heavy_concurrent_proc(Tab, N-1, Offs);
+do_heavy_concurrent_proc(Tab, N, Offs) ->
+ _ = ets:lookup(Tab, N),
+ do_heavy_concurrent_proc(Tab, N-1, Offs).
+
fold_empty(doc) ->
[];
@@ -4012,7 +4336,7 @@ member(Config) when is_list(Config) ->
member_do(Opts) ->
?line EtsMem = etsmem(),
- ?line T = ets:new(xxx, Opts),
+ ?line T = ets_new(xxx, Opts),
?line false = ets:member(T,hej),
?line E = fun(0,_F)->ok;
(N,F) ->
@@ -4037,7 +4361,7 @@ member_do(Opts) ->
build_table(L1,L2,Num) ->
- T = ets:new(xxx, [ordered_set]
+ T = ets_new(xxx, [ordered_set]
),
lists:foreach(
fun(X1) ->
@@ -4059,7 +4383,7 @@ build_table(L1,L2,Num) ->
T.
build_table2(L1,L2,Num) ->
- T = ets:new(xxx, [ordered_set]
+ T = ets_new(xxx, [ordered_set]
),
lists:foreach(
fun(X1) ->
@@ -4190,7 +4514,7 @@ do_n_times(Fun,N) ->
do_n_times(Fun,N-1).
make_table(Name, Options, Elements) ->
- T = ets:new(Name, Options),
+ T = ets_new(Name, Options),
lists:foreach(fun(E) -> ets:insert(T, E) end, Elements),
T.
filltabint(Tab,0) ->
@@ -4254,13 +4578,13 @@ xfilltabstr(Tab,N) ->
fill_sets_int(N) ->
fill_sets_int(N,[]).
fill_sets_int(N,Opts) ->
- Tab1 = ets:new(xxx, [ordered_set|Opts]),
+ Tab1 = ets_new(xxx, [ordered_set|Opts]),
filltabint(Tab1,N),
- Tab2 = ets:new(xxx, [set|Opts]),
+ Tab2 = ets_new(xxx, [set|Opts]),
filltabint(Tab2,N),
- Tab3 = ets:new(xxx, [bag|Opts]),
+ Tab3 = ets_new(xxx, [bag|Opts]),
filltabint2(Tab3,N),
- Tab4 = ets:new(xxx, [duplicate_bag|Opts]),
+ Tab4 = ets_new(xxx, [duplicate_bag|Opts]),
filltabint3(Tab4,N),
[Tab1,Tab2,Tab3,Tab4].
@@ -4412,7 +4736,7 @@ gen_dets_filename(Config,N) ->
"testdets_" ++ integer_to_list(N) ++ ".dets").
otp_6842_select_1000(Config) when is_list(Config) ->
- ?line Tab = ets:new(xxx,[ordered_set]),
+ ?line Tab = ets_new(xxx,[ordered_set]),
?line [ets:insert(Tab,{X,X}) || X <- lists:seq(1,10000)],
?line AllTrue = lists:duplicate(10,true),
?line AllTrue =
@@ -4445,7 +4769,7 @@ check_seq(A,B,C) ->
otp_6338(Config) when is_list(Config) ->
L = binary_to_term(<<131,108,0,0,0,2,104,2,108,0,0,0,2,103,100,0,19,112,112,98,49,95,98,115,49,50,64,98,108,97,100,101,95,48,95,53,0,0,33,50,0,0,0,4,1,98,0,0,23,226,106,100,0,4,101,120,105,116,104,2,108,0,0,0,2,104,2,100,0,3,115,98,109,100,0,19,112,112,98,50,95,98,115,49,50,64,98,108,97,100,101,95,48,95,56,98,0,0,18,231,106,100,0,4,114,101,99,118,106>>),
- T = ets:new(xxx,[ordered_set]),
+ T = ets_new(xxx,[ordered_set]),
lists:foreach(fun(X) -> ets:insert(T,X) end,L),
[[4839,recv]] = ets:match(T,{[{sbm,ppb2_bs12@blade_0_8},'$1'],'$2'}),
ets:delete(T).
@@ -4456,7 +4780,7 @@ otp_5340(Config) when is_list(Config) ->
otp_5340_do(Opts) ->
N = 3000,
- T = ets:new(otp_5340, [bag,public | Opts]),
+ T = ets_new(otp_5340, [bag,public | Opts]),
Ids = [1,2,3,4,5],
[w(T, N, Id) || Id <- Ids],
verify(T, Ids),
@@ -4492,7 +4816,7 @@ otp_7665(Config) when is_list(Config) ->
repeat_for_opts(otp_7665_do).
otp_7665_do(Opts) ->
- Tab = ets:new(otp_7665,[bag | Opts]),
+ Tab = ets_new(otp_7665,[bag | Opts]),
Min = 0,
Max = 10,
lists:foreach(fun(N)-> otp_7665_act(Tab,Min,Max,N) end,
@@ -4555,7 +4879,7 @@ meta_wb_do(Opts) ->
Names).
meta_wb_new(Name, _, Tabs, Opts) ->
- case (catch ets:new(Name,[named_table|Opts])) of
+ case (catch ets_new(Name,[named_table|Opts])) of
Name ->
?line false = lists:member(Name, Tabs),
[Name | Tabs];
@@ -4603,7 +4927,7 @@ grow_shrink_0([N|Ns], EtsMem) ->
grow_shrink_0([], _) -> ok.
grow_shrink_1(N, Flags) ->
- ?line T = ets:new(a, Flags),
+ ?line T = ets_new(a, Flags),
?line grow_shrink_2(N, N, T),
?line ets:delete(T).
@@ -4633,7 +4957,7 @@ grow_pseudo_deleted_do() ->
grow_pseudo_deleted_do(Type) ->
process_flag(scheduler,1),
Self = self(),
- ?line T = ets:new(kalle,[Type,public,{write_concurrency,true}]),
+ ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]),
Mod = 7, Mult = 10000,
filltabint(T,Mod*Mult),
?line true = ets:safe_fixtable(T,true),
@@ -4675,7 +4999,7 @@ shrink_pseudo_deleted_do() ->
shrink_pseudo_deleted_do(Type) ->
process_flag(scheduler,1),
Self = self(),
- ?line T = ets:new(kalle,[Type,public,{write_concurrency,true}]),
+ ?line T = ets_new(kalle,[Type,public,{write_concurrency,true}]),
Half = 10000,
filltabint(T,Half*2),
?line true = ets:safe_fixtable(T,true),
@@ -4704,17 +5028,10 @@ shrink_pseudo_deleted_do(Type) ->
process_flag(scheduler,0).
-meta_smp(suite) ->
- [meta_lookup_unnamed_read,
- meta_lookup_unnamed_write,
- meta_lookup_named_read,
- meta_lookup_named_write,
- meta_newdel_unnamed,
- meta_newdel_named].
meta_lookup_unnamed_read(suite) -> [];
meta_lookup_unnamed_read(Config) when is_list(Config) ->
- InitF = fun(_) -> Tab = ets:new(unnamed,[]),
+ InitF = fun(_) -> Tab = ets_new(unnamed,[]),
true = ets:insert(Tab,{key,data}),
Tab
end,
@@ -4727,7 +5044,7 @@ meta_lookup_unnamed_read(Config) when is_list(Config) ->
meta_lookup_unnamed_write(suite) -> [];
meta_lookup_unnamed_write(Config) when is_list(Config) ->
- InitF = fun(_) -> Tab = ets:new(unnamed,[]),
+ InitF = fun(_) -> Tab = ets_new(unnamed,[]),
{Tab,0}
end,
ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}),
@@ -4740,7 +5057,7 @@ meta_lookup_unnamed_write(Config) when is_list(Config) ->
meta_lookup_named_read(suite) -> [];
meta_lookup_named_read(Config) when is_list(Config) ->
InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)),
- Tab = ets:new(Name,[named_table]),
+ Tab = ets_new(Name,[named_table]),
true = ets:insert(Tab,{key,data}),
Tab
end,
@@ -4754,7 +5071,7 @@ meta_lookup_named_read(Config) when is_list(Config) ->
meta_lookup_named_write(suite) -> [];
meta_lookup_named_write(Config) when is_list(Config) ->
InitF = fun([ProcN|_]) -> Name = list_to_atom(integer_to_list(ProcN)),
- Tab = ets:new(Name,[named_table]),
+ Tab = ets_new(Name,[named_table]),
{Tab,0}
end,
ExecF = fun({Tab,N}) -> true = ets:insert(Tab,{key,N}),
@@ -4767,7 +5084,7 @@ meta_lookup_named_write(Config) when is_list(Config) ->
meta_newdel_unnamed(suite) -> [];
meta_newdel_unnamed(Config) when is_list(Config) ->
InitF = fun(_) -> ok end,
- ExecF = fun(_) -> Tab = ets:new(unnamed,[]),
+ ExecF = fun(_) -> Tab = ets_new(unnamed,[]),
true = ets:delete(Tab)
end,
FiniF = fun(_) -> ok end,
@@ -4777,7 +5094,7 @@ meta_newdel_named(suite) -> [];
meta_newdel_named(Config) when is_list(Config) ->
InitF = fun([ProcN|_]) -> list_to_atom(integer_to_list(ProcN))
end,
- ExecF = fun(Name) -> Name = ets:new(Name,[named_table]),
+ ExecF = fun(Name) -> Name = ets_new(Name,[named_table]),
true = ets:delete(Name),
Name
end,
@@ -4787,7 +5104,7 @@ meta_newdel_named(Config) when is_list(Config) ->
smp_insert(doc) -> ["Concurrent insert's on same table"];
smp_insert(suite) -> [];
smp_insert(Config) when is_list(Config) ->
- ets:new(smp_insert,[named_table,public,{write_concurrency,true}]),
+ ets_new(smp_insert,[named_table,public,{write_concurrency,true}]),
InitF = fun(_) -> ok end,
ExecF = fun(_) -> true = ets:insert(smp_insert,{random:uniform(10000)})
end,
@@ -4802,7 +5119,7 @@ smp_fixed_delete(Config) when is_list(Config) ->
only_if_smp(fun()->smp_fixed_delete_do() end).
smp_fixed_delete_do() ->
- T = ets:new(foo,[public,{write_concurrency,true}]),
+ T = ets_new(foo,[public,{write_concurrency,true}]),
%%Mem = ets:info(T,memory),
NumOfObjs = 100000,
filltabint(T,NumOfObjs),
@@ -4838,7 +5155,7 @@ smp_unfix_fix(Config) when is_list(Config) ->
smp_unfix_fix_do() ->
process_flag(scheduler,1),
Parent = self(),
- T = ets:new(foo,[public,{write_concurrency,true}]),
+ T = ets_new(foo,[public,{write_concurrency,true}]),
%%Mem = ets:info(T,memory),
NumOfObjs = 100000,
Deleted = 50000,
@@ -4898,7 +5215,7 @@ otp_8166_do(WC) ->
%% Bug scenario: One process segv while reading the table because another
%% process is doing unfix without write-lock at the end of a trapping match_object.
process_flag(scheduler,1),
- T = ets:new(foo,[public, {write_concurrency,WC}]),
+ T = ets_new(foo,[public, {write_concurrency,WC}]),
NumOfObjs = 3000, %% Need more than 1000 live objects for match_object to trap one time
Deleted = NumOfObjs div 2,
filltabint(T,NumOfObjs),
@@ -5010,14 +5327,20 @@ verify_table_load(T) ->
end.
-
-
+otp_8732(doc) -> ["ets:select on a tree with NIL key object"];
+otp_8732(Config) when is_list(Config) ->
+ Tab = ets_new(noname,[ordered_set]),
+ filltabstr(Tab,999),
+ ets:insert(Tab,{[],"nasty NIL object"}),
+ ?line [] = ets:match(Tab,{'_',nomatch}), %% Will hang if bug not fixed
+ ok.
+
smp_select_delete(suite) -> [];
smp_select_delete(doc) ->
["Run concurrent select_delete (and inserts) on same table."];
smp_select_delete(Config) when is_list(Config) ->
- T = ets:new(smp_select_delete,[named_table,public,{write_concurrency,true}]),
+ T = ets_new(smp_select_delete,[named_table,public,{write_concurrency,true}]),
Mod = 17,
Zeros = erlang:make_tuple(Mod,0),
InitF = fun(_) -> Zeros end,
@@ -5070,6 +5393,39 @@ smp_select_delete(Config) when is_list(Config) ->
?line false = ets:info(T,fixed),
ets:delete(T).
+types(doc) -> ["Test different types"];
+types(Config) when is_list(Config) ->
+ init_externals(),
+ repeat_for_opts(types_do,[[set,ordered_set],compressed]).
+
+types_do(Opts) ->
+ EtsMem = etsmem(),
+ ?line T = ets_new(xxx,Opts),
+ Fun = fun(Term) ->
+ ets:insert(T,{Term}),
+ ?line [{Term}] = ets:lookup(T,Term),
+ ets:insert(T,{Term,xxx}),
+ ?line [{Term,xxx}] = ets:lookup(T,Term),
+ ets:insert(T,{Term,"xxx"}),
+ ?line [{Term,"xxx"}] = ets:lookup(T,Term),
+ ets:insert(T,{xxx,Term}),
+ ?line [{xxx,Term}] = ets:lookup(T,xxx),
+ ets:insert(T,{"xxx",Term}),
+ ?line [{"xxx",Term}] = ets:lookup(T,"xxx"),
+ ets:delete_all_objects(T),
+ ?line 0 = ets:info(T,size)
+ end,
+ test_terms(Fun, strict),
+ ets:delete(T),
+ ?line verify_etsmem(EtsMem).
+
+
+
+
+%
+% Utility functions:
+%
+
add_lists(L1,L2) ->
add_lists(L1,L2,[]).
add_lists([],[],Acc) ->
@@ -5134,7 +5490,29 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc);
my_tab_to_list(Ts,Key, Acc) ->
my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]).
+wait_for_all_schedulers_online_to_execute() ->
+ PMs = lists:map(fun (Sched) ->
+ spawn_opt(fun () -> ok end,
+ [monitor, {scheduler, Sched}])
+ end,
+ lists:seq(1,erlang:system_info(schedulers_online))),
+ lists:foreach(fun ({P, M}) ->
+ receive
+ {'DOWN', M, process, P, _} -> ok
+ end
+ end,
+ PMs),
+ ok.
+
etsmem() ->
+ %% Wait until it is guaranteed that all already scheduled
+ %% deallocations of DbTable structures have completed.
+ wait_for_all_schedulers_online_to_execute(),
+
+ AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size),
+ ets:info(T,memory),ets:info(T,type)}
+ end, ets:all()),
+ Mem =
{try erlang:memory(ets) catch error:notsup -> notsup end,
case erlang:system_info({allocator,ets_alloc}) of
false -> undefined;
@@ -5153,12 +5531,13 @@ etsmem() ->
{value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L),
{Bl0+Bl,BlSz0+BlSz}
end, {0,0}, MSBCS)
- end}.
+ end},
+ {Mem,AllTabs}.
-verify_etsmem(MemInfo) ->
+verify_etsmem({MemInfo,AllTabs}) ->
wait_for_test_procs(),
case etsmem() of
- MemInfo ->
+ {MemInfo,_} ->
io:format("Ets mem info: ~p", [MemInfo]),
case MemInfo of
{ErlMem,EtsAlloc} when ErlMem == notsup; EtsAlloc == undefined ->
@@ -5167,12 +5546,15 @@ verify_etsmem(MemInfo) ->
_ ->
ok
end;
- Other ->
+ {MemInfo2, AllTabs2} ->
io:format("Expected: ~p", [MemInfo]),
- io:format("Actual: ~p", [Other]),
+ io:format("Actual: ~p", [MemInfo2]),
+ io:format("Changed tables before: ~p\n",[AllTabs -- AllTabs2]),
+ io:format("Changed tables after: ~p\n", [AllTabs2 -- AllTabs]),
?t:fail()
end.
+
start_loopers(N, Prio, Fun, State) ->
lists:map(fun (_) ->
my_spawn_opt(fun () -> looper(Fun, State) end,
@@ -5277,6 +5659,20 @@ repeat_while(Fun, Arg0) ->
{false,Ret} -> Ret
end.
+%% Some (but not all) permutations of List
+repeat_for_permutations(Fun, List) ->
+ repeat_for_permutations(Fun, List, length(List)-1).
+repeat_for_permutations(Fun, List, 0) ->
+ Fun(List);
+repeat_for_permutations(Fun, List, N) ->
+ {A,B} = lists:split(N, List),
+ L1 = B++A,
+ L2 = lists:reverse(L1),
+ L3 = B++lists:reverse(A),
+ L4 = lists:reverse(B)++A,
+ Fun(L1), Fun(L2), Fun(L3), Fun(L4),
+ repeat_for_permutations(Fun, List, N-1).
+
receive_any() ->
receive M ->
io:format("Process ~p got msg ~p\n", [self(),M]),
@@ -5332,22 +5728,232 @@ only_if_smp(Schedulers, Func) ->
{true,_} -> Func()
end.
+%% Copy-paste from emulator/test/binary_SUITE.erl
+-define(heap_binary_size, 64).
+test_terms(Test_Func, Mode) ->
+ garbage_collect(),
+ ?line Pib0 = process_info(self(),binary),
+
+ ?line Test_Func(atom),
+ ?line Test_Func(''),
+ ?line Test_Func('a'),
+ ?line Test_Func('ab'),
+ ?line Test_Func('abc'),
+ ?line Test_Func('abcd'),
+ ?line Test_Func('abcde'),
+ ?line Test_Func('abcdef'),
+ ?line Test_Func('abcdefg'),
+ ?line Test_Func('abcdefgh'),
+
+ ?line Test_Func(fun() -> ok end),
+ X = id([a,{b,c},c]),
+ Y = id({x,y,z}),
+ Z = id(1 bsl 8*257),
+ ?line Test_Func(fun() -> X end),
+ ?line Test_Func(fun() -> {X,Y} end),
+ ?line Test_Func([fun() -> {X,Y,Z} end,
+ fun() -> {Z,X,Y} end,
+ fun() -> {Y,Z,X} end]),
+
+ ?line Test_Func({trace_ts,{even_bigger,{some_data,fun() -> ok end}},{1,2,3}}),
+ ?line Test_Func({trace_ts,{even_bigger,{some_data,<<1,2,3,4,5,6,7,8,9,10>>}},
+ {1,2,3}}),
+
+ ?line Test_Func(1),
+ ?line Test_Func(42),
+ ?line Test_Func(-23),
+ ?line Test_Func(256),
+ ?line Test_Func(25555),
+ ?line Test_Func(-3333),
+
+ ?line Test_Func(1.0),
+
+ ?line Test_Func(183749783987483978498378478393874),
+ ?line Test_Func(-37894183749783987483978498378478393874),
+ Very_Big = very_big_num(),
+ ?line Test_Func(Very_Big),
+ ?line Test_Func(-Very_Big+1),
+
+ ?line Test_Func([]),
+ ?line Test_Func("abcdef"),
+ ?line Test_Func([a, b, 1, 2]),
+ ?line Test_Func([a|b]),
+
+ ?line Test_Func({}),
+ ?line Test_Func({1}),
+ ?line Test_Func({a, b}),
+ ?line Test_Func({a, b, c}),
+ ?line Test_Func(list_to_tuple(lists:seq(0, 255))),
+ ?line Test_Func(list_to_tuple(lists:seq(0, 256))),
+
+ ?line Test_Func(make_ref()),
+ ?line Test_Func([make_ref(), make_ref()]),
+
+ ?line Test_Func(make_port()),
+
+ ?line Test_Func(make_pid()),
+ ?line Test_Func(make_ext_pid()),
+ ?line Test_Func(make_ext_port()),
+ ?line Test_Func(make_ext_ref()),
+
+ Bin0 = list_to_binary(lists:seq(0, 14)),
+ ?line Test_Func(Bin0),
+ Bin1 = list_to_binary(lists:seq(0, ?heap_binary_size)),
+ ?line Test_Func(Bin1),
+ Bin2 = list_to_binary(lists:seq(0, ?heap_binary_size+1)),
+ ?line Test_Func(Bin2),
+ Bin3 = list_to_binary(lists:seq(0, 255)),
+ garbage_collect(),
+ Pib = process_info(self(),binary),
+ ?line Test_Func(Bin3),
+ garbage_collect(),
+ case Mode of
+ strict -> ?line Pib = process_info(self(),binary);
+ skip_refc_check -> ok
+ end,
+
+ ?line Test_Func(make_unaligned_sub_binary(Bin0)),
+ ?line Test_Func(make_unaligned_sub_binary(Bin1)),
+ ?line Test_Func(make_unaligned_sub_binary(Bin2)),
+ ?line Test_Func(make_unaligned_sub_binary(Bin3)),
+
+ ?line Test_Func(make_sub_binary(lists:seq(42, 43))),
+ ?line Test_Func(make_sub_binary([42,43,44])),
+ ?line Test_Func(make_sub_binary([42,43,44,45])),
+ ?line Test_Func(make_sub_binary([42,43,44,45,46])),
+ ?line Test_Func(make_sub_binary([42,43,44,45,46,47])),
+ ?line Test_Func(make_sub_binary([42,43,44,45,46,47,48])),
+ ?line Test_Func(make_sub_binary(lists:seq(42, 49))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, 14))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, ?heap_binary_size+1))),
+ ?line Test_Func(make_sub_binary(lists:seq(0, 255))),
+
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 43))),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47])),
+ ?line Test_Func(make_unaligned_sub_binary([42,43,44,45,46,47,48])),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(42, 49))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 14))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, ?heap_binary_size+1))),
+ ?line Test_Func(make_unaligned_sub_binary(lists:seq(0, 255))),
+
+ %% Bit level binaries.
+ ?line Test_Func(<<1:1>>),
+ ?line Test_Func(<<2:2>>),
+ ?line Test_Func(<<42:10>>),
+ ?line Test_Func(list_to_bitstring([<<5:6>>|lists:seq(0, 255)])),
+
+ ?line Test_Func(F = fun(A) -> 42*A end),
+ ?line Test_Func(lists:duplicate(32, F)),
+
+ ?line Test_Func(FF = fun binary_SUITE:all/1),
+ ?line Test_Func(lists:duplicate(32, FF)),
+
+ garbage_collect(),
+ case Mode of
+ strict -> ?line Pib0 = process_info(self(),binary);
+ skip_refc_check -> ok
+ end,
+ ok.
+
+
+id(I) -> I.
+
+very_big_num() ->
+ very_big_num(33, 1).
+
+very_big_num(Left, Result) when Left > 0 ->
+ ?line very_big_num(Left-1, Result*256);
+very_big_num(0, Result) ->
+ ?line Result.
+
+make_port() ->
+ ?line open_port({spawn, efile}, [eof]).
+
+make_pid() ->
+ ?line spawn_link(?MODULE, sleeper, []).
+
+sleeper() ->
+ ?line receive after infinity -> ok end.
+
+make_ext_pid() ->
+ {Pid, _, _} = get(externals),
+ Pid.
+
+make_ext_port() ->
+ {_, Port, _} = get(externals),
+ Port.
+make_ext_ref() ->
+ {_, _, Ref} = get(externals),
+ Ref.
+
+init_externals() ->
+ case get(externals) of
+ undefined ->
+ SysDistSz = ets:info(sys_dist,size),
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line {ok, Node} = test_server:start_node(plopp, slave, [{args, " -pa " ++ Pa}]),
+ ?line Res = case rpc:call(Node, ?MODULE, rpc_externals, []) of
+ {badrpc, {'EXIT', E}} ->
+ test_server:fail({rpcresult, E});
+ R -> R
+ end,
+ ?line test_server:stop_node(Node),
+
+ %% Wait for table 'sys_dist' to stabilize
+ repeat_while(fun() ->
+ case ets:info(sys_dist,size) of
+ SysDistSz -> false;
+ Sz ->
+ io:format("Waiting for sys_dist to revert size from ~p to size ~p\n",
+ [Sz, SysDistSz]),
+ receive after 1000 -> true end
+ end
+ end),
+ put(externals, Res);
+
+ {_,_,_} -> ok
+ end.
+
+rpc_externals() ->
+ {self(), make_port(), make_ref()}.
+
+make_sub_binary(Bin) when is_binary(Bin) ->
+ {_,B} = split_binary(list_to_binary([0,1,3,Bin]), 3),
+ B;
+make_sub_binary(List) ->
+ make_sub_binary(list_to_binary(List)).
+
+make_unaligned_sub_binary(Bin0) when is_binary(Bin0) ->
+ Bin1 = <<0:3,Bin0/binary,31:5>>,
+ Sz = size(Bin0),
+ <<0:3,Bin:Sz/binary,31:5>> = id(Bin1),
+ Bin;
+make_unaligned_sub_binary(List) ->
+ make_unaligned_sub_binary(list_to_binary(List)).
%% Repeat test function with different combination of table options
%%
repeat_for_opts(F) ->
- repeat_for_opts(F, [write_concurrency]).
+ repeat_for_opts(F, [write_concurrency, read_concurrency, compressed]).
repeat_for_opts(F, OptGenList) when is_atom(F) ->
repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts) end, OptGenList);
+repeat_for_opts({F,Args}, OptGenList) when is_atom(F) ->
+ repeat_for_opts(fun(Opts) -> ?MODULE:F(Opts,Args) end, OptGenList);
repeat_for_opts(F, OptGenList) ->
repeat_for_opts(F, OptGenList, []).
repeat_for_opts(F, [], Acc) ->
- lists:map(fun(Opts) ->
- io:format("Calling with options ~p\n",[Opts]),
- F(Opts)
- end, Acc);
+ lists:map(fun(Opts) ->
+ OptList = lists:filter(fun(E) -> E =/= void end, Opts),
+ io:format("Calling with options ~p\n",[OptList]),
+ F(OptList)
+ end, Acc);
repeat_for_opts(F, [OptList | Tail], []) when is_list(OptList) ->
repeat_for_opts(F, Tail, [[Opt] || Opt <- OptList]);
repeat_for_opts(F, [OptList | Tail], AccList) when is_list(OptList) ->
@@ -5356,6 +5962,10 @@ repeat_for_opts(F, [Atom | Tail], AccList) when is_atom(Atom) ->
repeat_for_opts(F, [repeat_for_opts_atom2list(Atom) | Tail ], AccList).
repeat_for_opts_atom2list(all_types) -> [set,ordered_set,bag,duplicate_bag];
-repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}].
-
+repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}];
+repeat_for_opts_atom2list(read_concurrency) -> [{read_concurrency,false},{read_concurrency,true}];
+repeat_for_opts_atom2list(compressed) -> [compressed,void].
+ets_new(Name, Opts) ->
+ %%ets:new(Name, [compressed | Opts]).
+ ets:new(Name, Opts).
diff --git a/lib/stdlib/test/ets_tough_SUITE.erl b/lib/stdlib/test/ets_tough_SUITE.erl
index 4c8d941f13..d9d0461575 100644
--- a/lib/stdlib/test/ets_tough_SUITE.erl
+++ b/lib/stdlib/test/ets_tough_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,13 +17,33 @@
%% %CopyrightEnd%
%%
-module(ets_tough_SUITE).
--export([all/1,ex1/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,ex1/1]).
-export([init/1,terminate/2,handle_call/3,handle_info/2]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-compile([export_all]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [ex1].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> [ex1].
-define(DEBUG(X),debug_disabled).
@@ -34,7 +54,7 @@ init_per_testcase(_Func, Config) ->
Dog=test_server:timetrap(test_server:seconds(300)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ets:delete(?GLOBAL_PARAMS).
diff --git a/lib/stdlib/test/file_sorter_SUITE.erl b/lib/stdlib/test/file_sorter_SUITE.erl
index c00ed91fe7..80d4ea5fdc 100644
--- a/lib/stdlib/test/file_sorter_SUITE.erl
+++ b/lib/stdlib/test/file_sorter_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,12 +27,13 @@
-define(t,test_server).
-define(privdir(_), "./file_sorter_SUITE_priv").
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(privdir(Conf), ?config(priv_dir, Conf)).
-endif.
--export([all/1, basic/1, badarg/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, basic/1, badarg/1,
term_sort/1, term_keysort/1,
binary_term_sort/1, binary_term_keysort/1,
binary_sort/1,
@@ -44,30 +45,42 @@
binary_check/1,
inout/1, misc/1, many/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
init_per_testcase(_Case, Config) ->
Dog=?t:timetrap(?t:minutes(2)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- {req,[stdlib,kernel],
- [basic, badarg,
- term_sort, term_keysort,
- binary_term_sort, binary_term_keysort,
- binary_sort,
- term_merge, term_keymerge,
- binary_term_merge, binary_term_keymerge,
- binary_merge,
- term_check, binary_term_keycheck,
- binary_term_check, binary_term_keycheck,
- binary_check,
- inout, misc, many]}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [basic, badarg, term_sort, term_keysort,
+ binary_term_sort, binary_term_keysort, binary_sort,
+ term_merge, term_keymerge, binary_term_merge,
+ binary_term_keymerge, binary_merge, term_check,
+ binary_term_keycheck, binary_term_check,
+ binary_term_keycheck, binary_check, inout, misc, many].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
basic(doc) ->
["Basic test case."];
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index d54741051f..a355097fe2 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,27 +19,47 @@
-module(filelib_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
wildcard_one/1,wildcard_two/1,wildcard_errors/1,
fold_files/1,otp_5960/1,ensure_dir_eexist/1]).
-import(lists, [foreach/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [wildcard_one,wildcard_two,wildcard_errors,fold_files,otp_5960,
- ensure_dir_eexist].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [wildcard_one, wildcard_two, wildcard_errors,
+ fold_files, otp_5960, ensure_dir_eexist].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
wildcard_one(Config) when is_list(Config) ->
?line {ok,OldCwd} = file:get_cwd(),
@@ -53,8 +73,11 @@ wildcard_one(Config) when is_list(Config) ->
wildcard_two(Config) when is_list(Config) ->
?line Dir = filename:join(?config(priv_dir, Config), "wildcard_two"),
+ ?line DirB = unicode:characters_to_binary(Dir, file:native_name_encoding()),
?line ok = file:make_dir(Dir),
- ?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir) end),
+ ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,Dir, X = filelib:wildcard(Wc, Dir)}]),X end),
+ ?line do_wildcard_1(Dir, fun(Wc) -> io:format("~p~n",[{Wc,DirB, X = filelib:wildcard(Wc, DirB)}]),
+ [unicode:characters_to_list(Y,file:native_name_encoding()) || Y <- X] end),
?line do_wildcard_1(Dir, fun(Wc) -> filelib:wildcard(Wc, Dir++"/") end),
case os:type() of
{win32,_} ->
@@ -253,5 +276,7 @@ ensure_dir_eexist(Config) when is_list(Config) ->
%% There already is a file with the name of the directory
%% we want to create.
?line NeedFile = filename:join(Name, "file"),
+ ?line NeedFileB = filename:join(Name, <<"file">>),
?line {error, eexist} = filelib:ensure_dir(NeedFile),
+ ?line {error, eexist} = filelib:ensure_dir(NeedFileB),
ok.
diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl
index ab6521f37b..70b0d413dc 100644
--- a/lib/stdlib/test/filename_SUITE.erl
+++ b/lib/stdlib/test/filename_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,17 +17,44 @@
%% %CopyrightEnd%
%%
-module(filename_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([absname/1, absname_2/1,
basename_1/1, basename_2/1,
dirname/1, extension/1, join/1, t_nativename/1]).
-export([pathtype/1,rootname/1,split/1,find_src/1]).
--include("test_server.hrl").
+-export([absname_bin/1, absname_bin_2/1,
+ basename_bin_1/1, basename_bin_2/1,
+ dirname_bin/1, extension_bin/1, join_bin/1]).
+-export([pathtype_bin/1,rootname_bin/1,split_bin/1]).
-all(suite) ->
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[absname, absname_2, basename_1, basename_2, dirname,
extension,
- join, pathtype, rootname, split, t_nativename, find_src].
+ join, pathtype, rootname, split, t_nativename, find_src,
+ absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin,
+ extension_bin,
+ join_bin, pathtype_bin, rootname_bin, split_bin].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -457,3 +484,307 @@ find_src(Config) when is_list(Config) ->
%% Try to find the source for a preloaded module.
?line {error,{preloaded,init}} = filename:find_src(init),
ok.
+
+%%
+%%
+%% With binaries
+%%
+%%
+
+absname_bin(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ ?line [Drive|_] = ?config(priv_dir, Config),
+ ?line Temp = filename:join([Drive|":/"], "temp"),
+ ?line case file:make_dir(Temp) of
+ ok -> ok;
+ {error,eexist} -> ok
+ end,
+ ?line {ok,Cwd} = file:get_cwd(),
+ ?line ok = file:set_cwd(Temp),
+ ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>),
+ ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>),
+ ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>),
+ ?line <<Drive:8,":/temp/erlang/src">> =
+ filename:absname(<<Drive:8,":erlang/src">>),
+ ?line <<Drive:8,":/temp/erlang/src">> =
+ filename:absname(<<Drive:8,":erlang\\src\\">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>),
+
+ ?line file:set_cwd(<<Drive:8,":/">>),
+ ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>),
+ ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>),
+
+ ?line file:set_cwd(Cwd),
+ ok;
+ {unix, _} ->
+ ?line ok = file:set_cwd(<<"/usr">>),
+ ?line <<"/usr/foo">> = filename:absname(<<"foo">>),
+ ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>),
+
+ ?line file:set_cwd(<<"/">>),
+ ?line <<"/foo">> = filename:absname(<<"foo">>),
+ ?line <<"/../ebin">> = filename:absname(<<"../ebin">>),
+ ?line <<"/erlang">> = filename:absname(<<"/erlang">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>),
+ ok
+ end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+absname_bin_2(Config) when is_list(Config) ->
+ case os:type() of
+ {win32, _} ->
+ ?line [Drive|_] = ?config(priv_dir, Config),
+ ?line <<Drive:8,":/temp/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/../ebin">> = filename:absname(<<"../ebin">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\src">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/erlang">> = filename:absname(<<Drive:8,":erlang">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>,
+ <<Drive:8,":/temp">>),
+ ?line <<Drive:8,":/temp/erlang/src">> =
+ filename:absname(<<Drive:8,":erlang\\src\\">>, <<Drive:8,":/temp">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/temp">>),
+
+ ?line file:set_cwd(<<Drive:8,":/">>),
+ ?line <<Drive:8,":/foo">> = filename:absname(foo, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/foo">> = filename:absname(<<"foo">>, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/../ebin">> = filename:absname(<<"../ebin">>, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<"/erlang">>, <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"/erlang/src">>,
+ <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<"\\erlang\\\\src">>,
+ <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang">> = filename:absname(<<Drive:8,":erlang">>,
+ <<Drive:8,":/">>),
+ ?line <<Drive:8,":/erlang/src">> = filename:absname(<<Drive:8,":erlang/src">>,
+ <<Drive:8,":/">>),
+ ?line <<"a:/erlang">> = filename:absname(<<"a:erlang">>, <<Drive:8,":/">>),
+
+ ok;
+ {unix, _} ->
+ ?line <<"/usr/foo">> = filename:absname(<<"foo">>, <<"/usr">>),
+ ?line <<"/usr/../ebin">> = filename:absname(<<"../ebin">>, <<"/usr">>),
+
+ ?line <<"/foo">> = filename:absname(<<"foo">>, <<"/">>),
+ ?line <<"/../ebin">> = filename:absname(<<"../ebin">>, <<"/">>),
+ ?line <<"/erlang">> = filename:absname(<<"/erlang">>, <<"/">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang/src">>, <<"/">>),
+ ?line <<"/erlang/src">> = filename:absname(<<"/erlang///src">>, <<"/">>),
+ ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+basename_bin_1(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line <<".">> = filename:basename(<<".">>),
+ ?line <<"foo">> = filename:basename(<<"foo">>),
+ ?line <<"foo">> = filename:basename(<<"/usr/foo">>),
+ ?line <<"foo.erl">> = filename:basename(<<"A:usr/foo.erl">>),
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>),
+ ?line <<"foo">> = filename:basename(<<"A:foo">>);
+ {unix, _} ->
+ ?line <<"strange\\but\\true">> =
+ filename:basename(<<"strange\\but\\true">>)
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+basename_bin_2(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ ?line <<".">> = filename:basename(<<".">>, <<".erl">>),
+ ?line <<"foo">> = filename:basename(<<"foo.erl">>, <<".erl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"/usr/foo.erl">>, <<".hrl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"/usr.hrl/foo.erl">>, <<".hrl">>),
+ ?line <<"foo">> = filename:basename(<<"/usr.hrl/foo">>, <<".hrl">>),
+ ?line <<"foo">> = filename:basename(<<"usr/foo/">>, <<".erl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"usr/foo.erl/">>, <<".erl">>),
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"foo">> = filename:basename(<<"A:foo">>, <<".erl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"a:\\usr\\foo.erl">>,
+ <<".hrl">>),
+ ?line <<"foo.erl">> = filename:basename(<<"c:\\usr.hrl\\foo.erl">>,
+ <<".hrl">>),
+ ?line <<"foo">> = filename:basename(<<"A:\\usr\\foo">>, <<".hrl">>);
+ {unix, _} ->
+ ?line <<"strange\\but\\true">> =
+ filename:basename(<<"strange\\but\\true.erl">>, <<".erl">>),
+ ?line <<"strange\\but\\true">> =
+ filename:basename(<<"strange\\but\\true">>, <<".erl">>)
+ end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+dirname_bin(Config) when is_list(Config) ->
+ case os:type() of
+ {win32,_} ->
+ ?line <<"A:/usr">> = filename:dirname(<<"A:/usr/foo.erl">>),
+ ?line <<"A:usr">> = filename:dirname(<<"A:usr/foo.erl">>),
+ ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
+ ?line <<"/">> = filename:dirname(<<"\\usr">>),
+ ?line <<"A:">> = filename:dirname(<<"A:">>);
+ vxworks ->
+ ?line <<"net:/usr">> = filename:dirname(<<"net:/usr/foo.erl">>),
+ ?line <<"/disk0:/usr">> = filename:dirname(<<"/disk0:/usr/foo.erl">>),
+ ?line <<"/usr">> = filename:dirname(<<"\\usr\\foo.erl">>),
+ ?line <<"/usr">> = filename:dirname(<<"\\usr">>),
+ ?line <<"net:">> = filename:dirname(<<"net:">>);
+ _ -> true
+ end,
+ ?line <<"usr">> = filename:dirname(<<"usr///foo.erl">>),
+ ?line <<".">> = filename:dirname(<<"foo.erl">>),
+ ?line <<".">> = filename:dirname(<<".">>),
+ case os:type() of
+ vxworks ->
+ ?line <<"/">> = filename:dirname(<<"/">>),
+ ?line <<"/usr">> = filename:dirname(<<"/usr">>);
+ _ ->
+ ?line <<"/">> = filename:dirname(<<"/">>),
+ ?line <<"/">> = filename:dirname(<<"/usr">>)
+ end,
+ ok.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+extension_bin(Config) when is_list(Config) ->
+ ?line <<".erl">> = filename:extension(<<"A:/usr/foo.erl">>),
+ ?line <<".erl">> = filename:extension(<<"A:/usr/foo.nisse.erl">>),
+ ?line <<".erl">> = filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ ?line <<"">> = filename:extension(<<"A:/usr/foo">>),
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"">> = filename:extension(<<"A:\\usr\\foo">>),
+ ?line <<".erl">> =
+ filename:extension(<<"A:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"A:/usr.bar/foo">>),
+ ok;
+ vxworks ->
+ ?line <<"">> = filename:extension(<<"/disk0:\\usr\\foo">>),
+ ?line <<".erl">> =
+ filename:extension(<<"net:/usr.bar/foo.nisse.erl">>),
+ ?line <<"">> = filename:extension(<<"net:/usr.bar/foo">>),
+ ok;
+ _ -> ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+join_bin(Config) when is_list(Config) ->
+ ?line <<"/">> = filename:join([<<"/">>]),
+ ?line <<"/">> = filename:join([<<"//">>]),
+ ?line <<"usr/foo.erl">> = filename:join(<<"usr">>,<<"foo.erl">>),
+ ?line <<"/src/foo.erl">> = filename:join(usr, <<"/src/foo.erl">>),
+ ?line <<"/src/foo.erl">> = filename:join([<<"/src/">>,'foo.erl']),
+ ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, ["/sr", 'c/foo.erl']),
+ ?line <<"/src/foo.erl">> = filename:join(<<"usr">>, <<"/src/foo.erl">>),
+
+ %% Make sure that redundant slashes work too.
+ ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/////d//e/f/g">>]),
+ ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c/">>, <<"d//e/f/g">>]),
+ ?line <<"a/b/c/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"d//e/f/g">>]),
+ ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"/d//e/f/g">>]),
+ ?line <<"/d/e/f/g">> = filename:join([<<"a//b/c">>, <<"//d//e/f/g">>]),
+
+ ?line <<"foo/bar">> = filename:join([$f,$o,$o,$/,[]], <<"bar">>),
+
+ ?line case os:type() of
+ {win32, _} ->
+ ?line <<"d:/">> = filename:join([<<"D:/">>]),
+ ?line <<"d:/">> = filename:join([<<"D:\\">>]),
+ ?line <<"d:/abc">> = filename:join([<<"D:/">>, <<"abc">>]),
+ ?line <<"d:abc">> = filename:join([<<"D:">>, <<"abc">>]),
+ ?line <<"a/b/c/d/e/f/g">> =
+ filename:join([<<"a//b\\c//\\/\\d/\\e/f\\g">>]),
+ ?line <<"a:usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"usr">>,<<"foo.erl">>]),
+ ?line <<"/usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"/usr">>,<<"foo.erl">>]),
+ ?line <<"c:usr">> = filename:join(<<"A:">>,<<"C:usr">>),
+ ?line <<"a:usr">> = filename:join(<<"A:">>,<<"usr">>),
+ ?line <<"c:/usr">> = filename:join(<<"A:">>, <<"C:/usr">>),
+ ?line <<"c:/usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"C:/usr">>,<<"foo.erl">>]),
+ ?line <<"c:usr/foo.erl">> =
+ filename:join([<<"A:">>,<<"C:usr">>,<<"foo.erl">>]),
+ ?line <<"d:/foo">> = filename:join([$D, $:, $/, []], <<"foo">>),
+ ok;
+ {unix, _} ->
+ ok
+ end.
+
+pathtype_bin(Config) when is_list(Config) ->
+ ?line relative = filename:pathtype(<<"..">>),
+ ?line relative = filename:pathtype(<<"foo">>),
+ ?line relative = filename:pathtype(<<"foo/bar">>),
+ ?line relative = filename:pathtype('foo/bar'),
+ case os:type() of
+ {win32, _} ->
+ ?line volumerelative = filename:pathtype(<<"/usr/local/bin">>),
+ ?line volumerelative = filename:pathtype(<<"A:usr/local/bin">>),
+ ok;
+ {unix, _} ->
+ ?line absolute = filename:pathtype(<<"/">>),
+ ?line absolute = filename:pathtype(<<"/usr/local/bin">>),
+ ok
+ end.
+
+rootname_bin(Config) when is_list(Config) ->
+ ?line <<"/jam.src/kalle">> = filename:rootname(<<"/jam.src/kalle">>),
+ ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>),
+ ?line <<"/jam.src/foo">> = filename:rootname(<<"/jam.src/foo.erl">>, <<".erl">>),
+ ?line <<"/jam.src/foo.jam">> = filename:rootname(<<"/jam.src/foo.jam">>, <<".erl">>),
+ ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j',"am"],<<".erl">>),
+ ?line <<"/jam.src/foo.jam">> = filename:rootname(["/jam.sr",'c/foo.j'|am],<<".erl">>),
+ ok.
+
+split_bin(Config) when is_list(Config) ->
+ case os:type() of
+ vxworks ->
+ ?line [<<"/usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>);
+ _ ->
+ ?line [<<"/">>,<<"usr">>,<<"local">>,<<"bin">>] = filename:split(<<"/usr/local/bin">>)
+ end,
+ ?line [<<"foo">>,<<"bar">>]= filename:split(<<"foo/bar">>),
+ ?line [<<"foo">>, <<"bar">>, <<"hello">>]= filename:split(<<"foo////bar//hello">>),
+ case os:type() of
+ {win32,_} ->
+ ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"a:/msdev/include">>),
+ ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"A:/msdev/include">>),
+ ?line [<<"msdev">>,<<"include">>] =
+ filename:split(<<"msdev\\include">>),
+ ?line [<<"a:/">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"a:\\msdev\\include">>),
+ ?line [<<"a:">>,<<"msdev">>,<<"include">>] =
+ filename:split(<<"a:msdev\\include">>),
+ ok;
+ _ ->
+ ok
+ end.
+
diff --git a/lib/stdlib/test/fixtable_SUITE.erl b/lib/stdlib/test/fixtable_SUITE.erl
index 1940ee147e..57fe4c4508 100644
--- a/lib/stdlib/test/fixtable_SUITE.erl
+++ b/lib/stdlib/test/fixtable_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,22 +21,41 @@
%%%----------------------------------------------------------------------
-module(fixtable_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%%% Test cases
-export([multiple_fixes/1, multiple_processes/1,
other_process_deletes/1, owner_dies/1,
other_process_closes/1,insert_same_key/1]).
-export([fixbag/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%%% Internal exports
-export([command_loop/0,start_commander/0]).
-all(suite) -> {req, [stdlib],
- [multiple_fixes, multiple_processes,
- other_process_deletes, owner_dies,
- other_process_closes,insert_same_key,fixbag]}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
--include("test_server.hrl").
+all() ->
+ [multiple_fixes, multiple_processes,
+ other_process_deletes, owner_dies, other_process_closes,
+ insert_same_key, fixbag].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+-include_lib("test_server/include/test_server.hrl").
%%% I wrote this thinking I would use more than one temporary at a time, but
%%% I wasn't... Well, maybe in the future...
@@ -53,7 +72,7 @@ init_per_testcase(_Func, Config) ->
Dog=test_server:timetrap(test_server:seconds(60)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
lists:foreach(fun(X) ->
diff --git a/lib/stdlib/test/format_SUITE.erl b/lib/stdlib/test/format_SUITE.erl
index 1c9e953003..68e17a0459 100644
--- a/lib/stdlib/test/format_SUITE.erl
+++ b/lib/stdlib/test/format_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,13 +17,14 @@
%% %CopyrightEnd%
%%
-module(format_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([hang_1/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -31,16 +32,32 @@
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test cases for io:format/[2,3]."];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[hang_1].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
hang_1(doc) ->
["Bad args can hang (OTP-2400)"];
hang_1(suite) ->
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index 8cbffaca56..9e3e717e7d 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,14 +18,39 @@
%%
-module(gen_event_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
--export([start/1, test_all/1, add_handler/1, add_sup_handler/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([start/1, add_handler/1, add_sup_handler/1,
delete_handler/1, swap_handler/1, swap_sup_handler/1,
- notify/1, sync_notify/1, call/1, info/1, hibernate/1]).
+ notify/1, sync_notify/1, call/1, info/1, hibernate/1,
+ call_format_status/1, error_format_status/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start, {group, test_all}, hibernate,
+ call_format_status, error_format_status].
+
+groups() ->
+ [{test_all, [],
+ [add_handler, add_sup_handler, delete_handler,
+ swap_handler, swap_sup_handler, notify, sync_notify,
+ call, info]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> {req, [stdlib], [start, test_all, hibernate]}.
%% --------------------------------------
%% Start an event manager.
@@ -169,9 +194,6 @@ hibernate(Config) when is_list(Config) ->
ok.
-test_all(suite) -> [add_handler, add_sup_handler, delete_handler,
- swap_handler, swap_sup_handler, notify,
- sync_notify, call, info].
add_handler(doc) -> [];
add_handler(suite) -> [];
@@ -844,3 +866,56 @@ info(Config) when is_list(Config) ->
?line ok = gen_event:stop(my_dummy_handler),
ok.
+
+call_format_status(suite) ->
+ [];
+call_format_status(doc) ->
+ ["Test that sys:get_status/1,2 calls format_status/2"];
+call_format_status(Config) when is_list(Config) ->
+ ?line {ok, Pid} = gen_event:start({local, my_dummy_handler}),
+ %% State here intentionally differs from what we expect from format_status
+ State = self(),
+ FmtState = "dummy1_h handler state",
+ ?line ok = gen_event:add_handler(my_dummy_handler, dummy1_h, [State]),
+ ?line Status1 = sys:get_status(Pid),
+ ?line Status2 = sys:get_status(Pid, 5000),
+ ?line ok = gen_event:stop(Pid),
+ ?line {status, Pid, _, [_, _, Pid, [], Data1]} = Status1,
+ ?line HandlerInfo1 = proplists:get_value(items, Data1),
+ ?line {"Installed handlers", [{_,dummy1_h,_,FmtState,_}]} = HandlerInfo1,
+ ?line {status, Pid, _, [_, _, Pid, [], Data2]} = Status2,
+ ?line HandlerInfo2 = proplists:get_value(items, Data2),
+ ?line {"Installed handlers", [{_,dummy1_h,_,FmtState,_}]} = HandlerInfo2,
+ ok.
+
+error_format_status(suite) ->
+ [];
+error_format_status(doc) ->
+ ["Test that a handler error calls format_status/2"];
+error_format_status(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ State = self(),
+ ?line {ok, Pid} = gen_event:start({local, my_dummy_handler}),
+ ?line ok = gen_event:add_sup_handler(my_dummy_handler, dummy1_h, [State]),
+ ?line ok = gen_event:notify(my_dummy_handler, do_crash),
+ ?line receive
+ {gen_event_EXIT,dummy1_h,{'EXIT',_}} -> ok
+ after 5000 ->
+ ?t:fail(exit_gen_event)
+ end,
+ FmtState = "dummy1_h handler state",
+ receive
+ {error,_GroupLeader, {Pid,
+ "** gen_event handler"++_,
+ [dummy1_h,my_dummy_handler,do_crash,
+ FmtState, _]}} ->
+ ok;
+ Other ->
+ ?line io:format("Unexpected: ~p", [Other]),
+ ?line ?t:fail()
+ end,
+ ?t:messages_get(),
+ ?line ok = gen_event:stop(Pid),
+ process_flag(trap_exit, OldFl),
+ ok.
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index 23c1d9a193..d60629d841 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -1,36 +1,37 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_fsm_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test cases
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([start/1, start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1,
+-export([ start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1,
start7/1, start8/1, start9/1, start10/1, start11/1]).
--export([abnormal/1, abnormal1/1, abnormal2/1]).
+-export([ abnormal1/1, abnormal2/1]).
-export([shutdown/1]).
--export([sys/1, sys1/1, call_format_status/1]).
+-export([ sys1/1, call_format_status/1, error_format_status/1]).
-export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]).
@@ -53,13 +54,31 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) ->
- [start, abnormal, shutdown, sys, hibernate, enter_loop].
-
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, start}, {group, abnormal}, shutdown,
+ {group, sys}, hibernate, enter_loop].
+groups() ->
+ [{start, [],
+ [start1, start2, start3, start4, start5, start6, start7,
+ start8, start9, start10, start11]},
+ {abnormal, [], [abnormal1, abnormal2]},
+ {sys, [],
+ [sys1, call_format_status, error_format_status]}].
-start(suite) -> [start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11].
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%% anonymous
start1(Config) when is_list(Config) ->
@@ -239,7 +258,6 @@ start11(Config) when is_list(Config) ->
test_server:messages_get(),
ok.
-abnormal(suite) -> [abnormal1, abnormal2].
%% Check that time outs in calls work
abnormal1(suite) -> [];
@@ -305,7 +323,6 @@ shutdown(Config) when is_list(Config) ->
ok.
-sys(suite) -> [sys1, call_format_status].
sys1(Config) when is_list(Config) ->
?line {ok, Pid} =
@@ -320,10 +337,53 @@ sys1(Config) when is_list(Config) ->
call_format_status(Config) when is_list(Config) ->
?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, [], []),
?line Status = sys:get_status(Pid),
- ?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data]} = Status,
+ ?line {status, Pid, _Mod, [_PDict, running, _, _, Data]} = Status,
?line [format_status_called | _] = lists:reverse(Data),
- ?line stop_it(Pid).
+ ?line stop_it(Pid),
+ %% check that format_status can handle a name being an atom (pid is
+ %% already checked by the previous test)
+ ?line {ok, Pid2} = gen_fsm:start({local, gfsm}, gen_fsm_SUITE, [], []),
+ ?line Status2 = sys:get_status(gfsm),
+ ?line {status, Pid2, _Mod, [_PDict2, running, _, _, Data2]} = Status2,
+ ?line [format_status_called | _] = lists:reverse(Data2),
+ ?line stop_it(Pid2),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ GlobalName1 = {global, "CallFormatStatus"},
+ ?line {ok, Pid3} = gen_fsm:start(GlobalName1, gen_fsm_SUITE, [], []),
+ ?line Status3 = sys:get_status(GlobalName1),
+ ?line {status, Pid3, _Mod, [_PDict3, running, _, _, Data3]} = Status3,
+ ?line [format_status_called | _] = lists:reverse(Data3),
+ ?line stop_it(Pid3),
+ GlobalName2 = {global, {name, "term"}},
+ ?line {ok, Pid4} = gen_fsm:start(GlobalName2, gen_fsm_SUITE, [], []),
+ ?line Status4 = sys:get_status(GlobalName2),
+ ?line {status, Pid4, _Mod, [_PDict4, running, _, _, Data4]} = Status4,
+ ?line [format_status_called | _] = lists:reverse(Data4),
+ ?line stop_it(Pid4).
+
+error_format_status(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ StateData = "called format_status",
+ ?line {ok, Pid} = gen_fsm:start(gen_fsm_SUITE, {state_data, StateData}, []),
+ %% bad return value in the gen_fsm loop
+ ?line {'EXIT',{{bad_return_value, badreturn},_}} =
+ (catch gen_fsm:sync_send_event(Pid, badreturn)),
+ receive
+ {error,_GroupLeader,{Pid,
+ "** State machine"++_,
+ [Pid,{_,_,badreturn},idle,StateData,_]}} ->
+ ok;
+ Other ->
+ ?line io:format("Unexpected: ~p", [Other]),
+ ?line ?t:fail()
+ end,
+ ?t:messages_get(),
+ process_flag(trap_exit, OldFl),
+ ok.
%% Hibernation
hibernate(suite) -> [];
@@ -704,6 +764,8 @@ init(hiber) ->
{ok, hiber_idle, []};
init(hiber_now) ->
{ok, hiber_idle, [], hibernate};
+init({state_data, StateData}) ->
+ {ok, idle, StateData};
init(_) ->
{ok, idle, state_data}.
@@ -844,5 +906,7 @@ handle_sync_event(stop_shutdown_reason, _From, _State, Data) ->
handle_sync_event({get, _Pid}, _From, State, Data) ->
{reply, {state, State, Data}, State, Data}.
-format_status(_Opt, [_Pdict, _StateData]) ->
+format_status(terminate, [_Pdict, StateData]) ->
+ StateData;
+format_status(normal, [_Pdict, _StateData]) ->
[format_status_called].
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 0f60c2c4ee..a614d6595d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,13 @@
%%
-module(gen_server_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/inet.hrl").
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([start/1, crash/1, call/1, cast/1, cast_fast/1,
info/1, abcast/1, multicall/1, multicall_down/1,
call_remote1/1, call_remote2/1, call_remote3/1,
@@ -31,7 +32,7 @@
spec_init_local_registered_parent/1,
spec_init_global_registered_parent/1,
otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1,
- call_with_huge_message_queue/1
+ error_format_status/1, call_with_huge_message_queue/1
]).
% spawn export
@@ -45,22 +46,55 @@
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, format_status/2]).
-all(suite) ->
- [start, crash, call, cast, cast_fast, info,
- abcast, multicall, multicall_down, call_remote1,
- call_remote2, call_remote3, call_remote_n1,
- call_remote_n2, call_remote_n3, spec_init,
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [start, crash, call, cast, cast_fast, info, abcast,
+ multicall, multicall_down, call_remote1, call_remote2,
+ call_remote3, call_remote_n1, call_remote_n2,
+ call_remote_n3, spec_init,
spec_init_local_registered_parent,
- spec_init_global_registered_parent,
- otp_5854, hibernate, otp_7669, call_format_status,
+ spec_init_global_registered_parent, otp_5854, hibernate,
+ otp_7669, call_format_status, error_format_status,
call_with_huge_message_queue].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
-define(default_timeout, ?t:minutes(1)).
+init_per_testcase(Case, Config) when Case == call_remote1;
+ Case == call_remote2;
+ Case == call_remote3;
+ Case == call_remote_n1;
+ Case == call_remote_n2;
+ Case == call_remote_n3 ->
+ {ok,N} = start_node(hubba),
+ ?line Dog = ?t:timetrap(?default_timeout),
+ [{node,N},{watchdog, Dog} | Config];
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
+ case proplists:get_value(node, Config) of
+ undefined ->
+ ok;
+ N ->
+ test_server:stop_node(N)
+ end,
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -293,8 +327,8 @@ start_node(Name) ->
call_remote1(suite) -> [];
call_remote1(Config) when is_list(Config) ->
- ?line N = hubba,
- ?line {ok, Node} = start_node(N),
+ N = hubba,
+ ?line Node = proplists:get_value(node,Config),
?line {ok, Pid} = rpc:call(Node, gen_server, start,
[{global, N}, ?MODULE, [], []]),
?line ok = (catch gen_server:call({global, N}, started_p, infinity)),
@@ -307,7 +341,7 @@ call_remote1(Config) when is_list(Config) ->
call_remote2(suite) -> [];
call_remote2(Config) when is_list(Config) ->
?line N = hubba,
- ?line {ok, Node} = start_node(N),
+ ?line Node = proplists:get_value(node,Config),
?line {ok, Pid} = rpc:call(Node, gen_server, start,
[{global, N}, ?MODULE, [], []]),
@@ -320,8 +354,7 @@ call_remote2(Config) when is_list(Config) ->
call_remote3(suite) -> [];
call_remote3(Config) when is_list(Config) ->
- ?line N = hubba,
- ?line {ok, Node} = start_node(N),
+ ?line Node = proplists:get_value(node,Config),
?line {ok, Pid} = rpc:call(Node, gen_server, start,
[{local, piller}, ?MODULE, [], []]),
@@ -339,7 +372,7 @@ call_remote3(Config) when is_list(Config) ->
call_remote_n1(suite) -> [];
call_remote_n1(Config) when is_list(Config) ->
?line N = hubba,
- ?line {ok, Node} = start_node(N),
+ ?line Node = proplists:get_value(node,Config),
?line {ok, _Pid} = rpc:call(Node, gen_server, start,
[{global, N}, ?MODULE, [], []]),
?line _ = test_server:stop_node(Node),
@@ -351,7 +384,7 @@ call_remote_n1(Config) when is_list(Config) ->
call_remote_n2(suite) -> [];
call_remote_n2(Config) when is_list(Config) ->
?line N = hubba,
- ?line {ok, Node} = start_node(N),
+ ?line Node = proplists:get_value(node,Config),
?line {ok, Pid} = rpc:call(Node, gen_server, start,
[{global, N}, ?MODULE, [], []]),
@@ -363,8 +396,7 @@ call_remote_n2(Config) when is_list(Config) ->
call_remote_n3(suite) -> [];
call_remote_n3(Config) when is_list(Config) ->
- ?line N = hubba,
- ?line {ok, Node} = start_node(N),
+ ?line Node = proplists:get_value(node,Config),
?line {ok, _Pid} = rpc:call(Node, gen_server, start,
[{local, piller}, ?MODULE, [], []]),
@@ -897,13 +929,64 @@ call_format_status(doc) ->
["Test that sys:get_status/1,2 calls format_status/2"];
call_format_status(Config) when is_list(Config) ->
?line {ok, Pid} = gen_server:start_link({local, call_format_status},
- gen_server_SUITE, [], []),
+ ?MODULE, [], []),
?line Status1 = sys:get_status(call_format_status),
?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data1]} = Status1,
?line [format_status_called | _] = lists:reverse(Data1),
?line Status2 = sys:get_status(call_format_status, 5000),
?line {status, Pid, _Mod, [_PDict, running, _Parent, _, Data2]} = Status2,
?line [format_status_called | _] = lists:reverse(Data2),
+
+ %% check that format_status can handle a name being a pid (atom is
+ %% already checked by the previous test)
+ ?line {ok, Pid3} = gen_server:start_link(gen_server_SUITE, [], []),
+ ?line Status3 = sys:get_status(Pid3),
+ ?line {status, Pid3, _Mod, [_PDict3, running, _Parent, _, Data3]} = Status3,
+ ?line [format_status_called | _] = lists:reverse(Data3),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ GlobalName1 = {global, "CallFormatStatus"},
+ ?line {ok, Pid4} = gen_server:start_link(GlobalName1,
+ gen_server_SUITE, [], []),
+ ?line Status4 = sys:get_status(Pid4),
+ ?line {status, Pid4, _Mod, [_PDict4, running, _Parent, _, Data4]} = Status4,
+ ?line [format_status_called | _] = lists:reverse(Data4),
+ GlobalName2 = {global, {name, "term"}},
+ ?line {ok, Pid5} = gen_server:start_link(GlobalName2,
+ gen_server_SUITE, [], []),
+ ?line Status5 = sys:get_status(GlobalName2),
+ ?line {status, Pid5, _Mod, [_PDict5, running, _Parent, _, Data5]} = Status5,
+ ?line [format_status_called | _] = lists:reverse(Data5),
+ ok.
+
+%% Verify that error termination correctly calls our format_status/2 fun
+%%
+error_format_status(suite) ->
+ [];
+error_format_status(doc) ->
+ ["Test that an error termination calls format_status/2"];
+error_format_status(Config) when is_list(Config) ->
+ ?line error_logger_forwarder:register(),
+ OldFl = process_flag(trap_exit, true),
+ State = "called format_status",
+ ?line {ok, Pid} = gen_server:start_link(?MODULE, {state, State}, []),
+ ?line {'EXIT',{crashed,_}} = (catch gen_server:call(Pid, crash)),
+ receive
+ {'EXIT', Pid, crashed} ->
+ ok
+ end,
+ receive
+ {error,_GroupLeader,{Pid,
+ "** Generic server"++_,
+ [Pid,crash,State,crashed]}} ->
+ ok;
+ Other ->
+ ?line io:format("Unexpected: ~p", [Other]),
+ ?line ?t:fail()
+ end,
+ ?t:messages_get(),
+ process_flag(trap_exit, OldFl),
ok.
%% Test that the time for a huge message queue is not
@@ -1105,5 +1188,7 @@ terminate({From, stopped_info}, _State) ->
terminate(_Reason, _State) ->
ok.
-format_status(_Opt, [_PDict, _State]) ->
- [format_status_called].
+format_status(terminate, [_PDict, State]) ->
+ State;
+format_status(normal, [_PDict, _State]) ->
+ format_status_called.
diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl
index 95ee509833..e1972a100e 100644
--- a/lib/stdlib/test/id_transform_SUITE.erl
+++ b/lib/stdlib/test/id_transform_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,17 +21,37 @@
-include_lib("kernel/include/file.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
id_transform/1]).
-export([check/2,check2/1,g/0,f/1,t/1,t1/1,t2/1,t3/1,t4/1,
t5/1,t6/1,apa/1,new_fun/0]).
-% Serves as test...
+ % Serves as test...
-hej(hopp).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [id_transform].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> [id_transform].
id_transform(doc) -> "Test erl_id_trans.";
id_transform(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl
index 73efeb004a..54a98985cd 100644
--- a/lib/stdlib/test/io_SUITE.erl
+++ b/lib/stdlib/test/io_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,15 +18,16 @@
%%
-module(io_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-export([error_1/1, float_g/1, otp_5403/1, otp_5813/1, otp_6230/1,
otp_6282/1, otp_6354/1, otp_6495/1, otp_6517/1, otp_6502/1,
manpage/1, otp_6708/1, otp_7084/1, otp_7421/1,
io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1,
- io_fread_newlines/1]).
+ io_fread_newlines/1, otp_8989/1]).
%-define(debug, true).
@@ -37,7 +38,7 @@
-define(t, test_server).
-define(privdir(_), "./io_SUITE_priv").
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(privdir(Conf), ?config(priv_dir, Conf)).
-endif.
@@ -49,17 +50,35 @@
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test cases for io."];
-all(suite) ->
- [error_1,float_g,otp_5403,otp_5813,otp_6230,otp_6282,otp_6354,otp_6495,
- otp_6517,otp_6502,manpage,otp_6708,otp_7084,otp_7421,
- io_lib_collect_line_3_wb,cr_whitespace_in_string,io_fread_newlines].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [error_1, float_g, otp_5403, otp_5813, otp_6230,
+ otp_6282, otp_6354, otp_6495, otp_6517, otp_6502,
+ manpage, otp_6708, otp_7084, otp_7421,
+ io_lib_collect_line_3_wb, cr_whitespace_in_string,
+ io_fread_newlines, otp_8989].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
error_1(doc) ->
["Error cases for output"];
@@ -1898,3 +1917,81 @@ read_newlines(Fd, Acc, N0) ->
eof ->
{lists:reverse(Acc),N0}
end.
+
+
+
+otp_8989(doc) ->
+ "OTP-8989 io:format for ~F.Ps ignores P in some cases";
+otp_8989(Suite) when is_list(Suite) ->
+ Hello = "Hello",
+ ?line " Hello" = fmt("~6.6s", [Hello]),
+ ?line " Hello" = fmt("~*.6s", [6,Hello]),
+ ?line " Hello" = fmt("~6.*s", [6,Hello]),
+ ?line " Hello" = fmt("~*.*s", [6,6,Hello]),
+ %%
+ ?line " Hello" = fmt("~6.5s", [Hello]),
+ ?line " Hello" = fmt("~*.5s", [6,Hello]),
+ ?line " Hello" = fmt("~6.*s", [5,Hello]),
+ ?line " Hello" = fmt("~*.*s", [6,5,Hello]),
+ %%
+ ?line " Hell" = fmt("~6.4s", [Hello]),
+ ?line " Hell" = fmt("~*.4s", [6,Hello]),
+ ?line " Hell" = fmt("~6.*s", [4,Hello]),
+ ?line " Hell" = fmt("~*.*s", [6,4,Hello]),
+ %%
+ ?line "Hello" = fmt("~5.5s", [Hello]),
+ ?line "Hello" = fmt("~*.5s", [5,Hello]),
+ ?line "Hello" = fmt("~5.*s", [5,Hello]),
+ ?line "Hello" = fmt("~*.*s", [5,5,Hello]),
+ %%
+ ?line " Hell" = fmt("~5.4s", [Hello]),
+ ?line " Hell" = fmt("~*.4s", [5,Hello]),
+ ?line " Hell" = fmt("~5.*s", [4,Hello]),
+ ?line " Hell" = fmt("~*.*s", [5,4,Hello]),
+ %%
+ ?line "Hell" = fmt("~4.4s", [Hello]),
+ ?line "Hell" = fmt("~*.4s", [4,Hello]),
+ ?line "Hell" = fmt("~4.*s", [4,Hello]),
+ ?line "Hell" = fmt("~*.*s", [4,4,Hello]),
+ %%
+ ?line " Hel" = fmt("~4.3s", [Hello]),
+ ?line " Hel" = fmt("~*.3s", [4,Hello]),
+ ?line " Hel" = fmt("~4.*s", [3,Hello]),
+ ?line " Hel" = fmt("~*.*s", [4,3,Hello]),
+ %%
+ %%
+ ?line "Hello " = fmt("~-6.6s", [Hello]),
+ ?line "Hello " = fmt("~*.6s", [-6,Hello]),
+ ?line "Hello " = fmt("~-6.*s", [6,Hello]),
+ ?line "Hello " = fmt("~*.*s", [-6,6,Hello]),
+ %%
+ ?line "Hello " = fmt("~-6.5s", [Hello]),
+ ?line "Hello " = fmt("~*.5s", [-6,Hello]),
+ ?line "Hello " = fmt("~-6.*s", [5,Hello]),
+ ?line "Hello " = fmt("~*.*s", [-6,5,Hello]),
+ %%
+ ?line "Hell " = fmt("~-6.4s", [Hello]),
+ ?line "Hell " = fmt("~*.4s", [-6,Hello]),
+ ?line "Hell " = fmt("~-6.*s", [4,Hello]),
+ ?line "Hell " = fmt("~*.*s", [-6,4,Hello]),
+ %%
+ ?line "Hello" = fmt("~-5.5s", [Hello]),
+ ?line "Hello" = fmt("~*.5s", [-5,Hello]),
+ ?line "Hello" = fmt("~-5.*s", [5,Hello]),
+ ?line "Hello" = fmt("~*.*s", [-5,5,Hello]),
+ %%
+ ?line "Hell " = fmt("~-5.4s", [Hello]),
+ ?line "Hell " = fmt("~*.4s", [-5,Hello]),
+ ?line "Hell " = fmt("~-5.*s", [4,Hello]),
+ ?line "Hell " = fmt("~*.*s", [-5,4,Hello]),
+ %%
+ ?line "Hell" = fmt("~-4.4s", [Hello]),
+ ?line "Hell" = fmt("~*.4s", [-4,Hello]),
+ ?line "Hell" = fmt("~-4.*s", [4,Hello]),
+ ?line "Hell" = fmt("~*.*s", [-4,4,Hello]),
+ %%
+ ?line "Hel " = fmt("~-4.3s", [Hello]),
+ ?line "Hel " = fmt("~*.3s", [-4,Hello]),
+ ?line "Hel " = fmt("~-4.*s", [3,Hello]),
+ ?line "Hel " = fmt("~*.*s", [-4,3,Hello]),
+ ok.
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index d9672a8c7b..b69cd74edb 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,15 +19,19 @@
-module(io_proto_SUITE).
-compile(r12).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--export([setopts_getopts/1,unicode_options/1,unicode_options_gen/1, binary_options/1, bc_with_r12/1,
- bc_with_r12_gl/1, read_modes_gl/1,bc_with_r12_ogl/1, read_modes_ogl/1, broken_unicode/1,eof_on_pipe/1,unicode_prompt/1]).
+-export([setopts_getopts/1,unicode_options/1,unicode_options_gen/1,
+ binary_options/1, bc_with_r12/1,
+ bc_with_r12_gl/1, read_modes_gl/1,bc_with_r12_ogl/1,
+ read_modes_ogl/1, broken_unicode/1,eof_on_pipe/1,unicode_prompt/1]).
--export([io_server_proxy/1,start_io_server_proxy/0, proxy_getall/1, proxy_setnext/2, proxy_quit/1]).
+-export([io_server_proxy/1,start_io_server_proxy/0, proxy_getall/1,
+ proxy_setnext/2, proxy_quit/1]).
%% For spawn
-export([toerl_server/3,hold_the_line/3,answering_machine1/3,
answering_machine2/3]).
@@ -42,7 +46,7 @@
-define(t, test_server).
-define(privdir(_), "./io_SUITE_priv").
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(privdir(Conf), ?config(priv_dir, Conf)).
-endif.
@@ -73,19 +77,36 @@ init_per_testcase(_Case, Config) ->
end,
os:putenv("TERM","vt100"),
[{watchdog, Dog}, {term, Term} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
Term = ?config(term,Config),
os:putenv("TERM",Term),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test cases for the io_protocol."];
-all(suite) ->
- [setopts_getopts, unicode_options, unicode_options_gen, binary_options, bc_with_r12,
- bc_with_r12_gl,bc_with_r12_ogl, read_modes_gl, read_modes_ogl,
- broken_unicode,eof_on_pipe,unicode_prompt].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [setopts_getopts, unicode_options, unicode_options_gen,
+ binary_options, bc_with_r12, bc_with_r12_gl,
+ bc_with_r12_ogl, read_modes_gl, read_modes_ogl,
+ broken_unicode, eof_on_pipe, unicode_prompt].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-record(state, {
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 0089e874c8..b56f0b39d8 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
%%%-----------------------------------------------------------------
-module(lists_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
@@ -30,36 +30,37 @@
-define(default_timeout, ?t:minutes(4)).
% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
-export([member/1, reverse/1,
keymember/1, keysearch_keyfind/1,
keystore/1, keytake/1,
- append/1, append_1/1, append_2/1,
- seq/1, seq_loop/1, seq_2/1, seq_3/1, seq_2_e/1, seq_3_e/1,
- sublist/1, flatten/1,
+ append_1/1, append_2/1,
+ seq_loop/1, seq_2/1, seq_3/1, seq_2_e/1, seq_3_e/1,
+
sublist_2/1, sublist_3/1, sublist_2_e/1, sublist_3_e/1,
flatten_1/1, flatten_2/1, flatten_1_e/1, flatten_2_e/1,
dropwhile/1,
- sort/1, sort_1/1, sort_stable/1, merge/1, rmerge/1, sort_rand/1,
- usort/1, usort_1/1, usort_stable/1, umerge/1, rumerge/1,usort_rand/1,
+ sort_1/1, sort_stable/1, merge/1, rmerge/1, sort_rand/1,
+ usort_1/1, usort_stable/1, umerge/1, rumerge/1,usort_rand/1,
keymerge/1, rkeymerge/1,
- keysort/1, keysort_1/1, keysort_i/1, keysort_stable/1,
+ keysort_1/1, keysort_i/1, keysort_stable/1,
keysort_rand/1, keysort_error/1,
ukeymerge/1, rukeymerge/1,
- ukeysort/1, ukeysort_1/1, ukeysort_i/1, ukeysort_stable/1,
+ ukeysort_1/1, ukeysort_i/1, ukeysort_stable/1,
ukeysort_rand/1, ukeysort_error/1,
funmerge/1, rfunmerge/1,
- funsort/1, funsort_1/1, funsort_stable/1, funsort_rand/1,
+ funsort_1/1, funsort_stable/1, funsort_rand/1,
funsort_error/1,
ufunmerge/1, rufunmerge/1,
- ufunsort/1, ufunsort_1/1, ufunsort_stable/1, ufunsort_rand/1,
+ ufunsort_1/1, ufunsort_stable/1, ufunsort_rand/1,
ufunsort_error/1,
zip_unzip/1, zip_unzip3/1, zipwith/1, zipwith3/1,
filter_partition/1,
- tickets/1, otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1,
+ otp_5939/1, otp_6023/1, otp_6606/1, otp_7230/1,
suffix/1, subtract/1]).
%% Sort randomized lists until stopped.
@@ -76,21 +77,59 @@
%%
%% all/1
%%
-all(doc) ->
- [];
-all(suite) ->
- [append, reverse, member, keymember, keysearch_keyfind, keystore, keytake,
- dropwhile,
- sort, usort, keysort, ukeysort,
- funsort, ufunsort, sublist, flatten, seq,
- zip_unzip, zip_unzip3, zipwith, zipwith3,
- filter_partition, tickets, suffix, subtract].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, append}, reverse, member, keymember,
+ keysearch_keyfind, keystore, keytake, dropwhile, {group,sort},
+ {group, usort}, {group, keysort}, {group, ukeysort},
+ {group, funsort}, {group, ufunsort}, {group, sublist},
+ {group, flatten}, {group, seq}, zip_unzip, zip_unzip3,
+ zipwith, zipwith3, filter_partition, {group, tickets},
+ suffix, subtract].
+
+groups() ->
+ [{append, [], [append_1, append_2]},
+ {usort, [],
+ [umerge, rumerge, usort_1, usort_rand, usort_stable]},
+ {keysort, [],
+ [keymerge, rkeymerge, keysort_1, keysort_rand,
+ keysort_i, keysort_stable, keysort_error]},
+ {sort,[],[merge, rmerge, sort_1, sort_rand]},
+ {ukeysort, [],
+ [ukeymerge, rukeymerge, ukeysort_1, ukeysort_rand,
+ ukeysort_i, ukeysort_stable, ukeysort_error]},
+ {funsort, [],
+ [funmerge, rfunmerge, funsort_1, funsort_stable,
+ funsort_error, funsort_rand]},
+ {ufunsort, [],
+ [ufunmerge, rufunmerge, ufunsort_1, ufunsort_stable,
+ ufunsort_error, ufunsort_rand]},
+ {seq, [], [seq_loop, seq_2, seq_3, seq_2_e, seq_3_e]},
+ {sublist, [],
+ [sublist_2, sublist_3, sublist_2_e, sublist_3_e]},
+ {flatten, [],
+ [flatten_1, flatten_2, flatten_1_e, flatten_2_e]},
+ {tickets, [], [otp_5939, otp_6023, otp_6606, otp_7230]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -98,10 +137,6 @@ fin_per_testcase(_Case, Config) ->
%
% Test cases starts here.
%
-append(doc) ->
- ["Tests lists:append/1 & lists:append/2"];
-append(suite) ->
- [append_1, append_2].
append_1(doc) -> [];
append_1(suite) -> [];
@@ -346,12 +381,6 @@ keytake(Config) when is_list(Config) ->
?line false = lists:keytake(4, 2, L),
ok.
-sort(doc) ->
- ["Tests merge functions and lists:sort/1"];
-sort(suite) ->
- %% [merge, rmerge, sort_1, sort_rand, sort_stable].
- [merge, rmerge, sort_1, sort_rand].
-
merge(doc) -> ["merge functions"];
merge(suite) -> [];
merge(Config) when is_list(Config) ->
@@ -536,10 +565,6 @@ expl_pid([{I,F} | T], L) when is_function(F) ->
expl_pid([], L) ->
L.
-usort(doc) ->
- ["Tests unique merge functions and lists:usort/1"];
-usort(suite) ->
- [umerge, rumerge, usort_1, usort_rand, usort_stable].
usort_1(suite) -> [];
usort_1(doc) -> [""];
@@ -750,11 +775,6 @@ ucheck_stability(L) ->
U = lists:usort(L),
check_stab(L, U, S, "usort/1", "ukeysort/2").
-keysort(doc) ->
- ["Tests lists:keysort/2"];
-keysort(suite) ->
- [keymerge, rkeymerge,
- keysort_1, keysort_rand, keysort_i, keysort_stable, keysort_error].
keymerge(doc) -> ["Key merge two lists."];
keymerge(suite) -> [];
@@ -946,11 +966,6 @@ keycompare(I, J, A, B) when element(I, A) == element(I, B),
element(J, A) =< element(J, B) ->
ok.
-ukeysort(doc) ->
- ["Tests lists:ukeysort/2"];
-ukeysort(suite) ->
- [ukeymerge, rukeymerge,
- ukeysort_1, ukeysort_rand, ukeysort_i, ukeysort_stable, ukeysort_error].
ukeymerge(suite) -> [];
ukeymerge(doc) -> ["Merge two lists while removing duplicates."];
@@ -1240,11 +1255,6 @@ ukeycompare(I, J, A, B) when A =/= B,
ok.
-funsort(doc) ->
- ["Tests lists:sort/2"];
-funsort(suite) ->
- [funmerge, rfunmerge,
- funsort_1, funsort_stable, funsort_error, funsort_rand].
funmerge(doc) -> ["Merge two lists using a fun."];
funmerge(suite) -> [];
@@ -1377,11 +1387,6 @@ funsort_check(I, Input, Expected) ->
?line Expected = funsort(I, Input),
check_sorted(I, Input, Expected).
-ufunsort(doc) ->
- ["Tests lists:usort/2"];
-ufunsort(suite) ->
- [ufunmerge, rufunmerge,
- ufunsort_1, ufunsort_stable, ufunsort_error, ufunsort_rand].
ufunmerge(suite) -> [];
ufunmerge(doc) -> ["Merge two lists while removing duplicates using a fun."];
@@ -2076,12 +2081,6 @@ rkeymerge2_2(_I, T1, _E1, [], M, H1) ->
%%%------------------------------------------------------------
-seq(doc) ->
- ["Tests lists:seq/3"];
-seq(suite) ->
- [
- seq_loop,
- seq_2, seq_3, seq_2_e, seq_3_e].
seq_loop(doc) ->
["Test for infinite loop (OTP-2404)."];
@@ -2229,10 +2228,6 @@ property(From, To, Step) ->
%%%------------------------------------------------------------
-sublist(doc) ->
- ["Tests lists:sublist/[2,3]"];
-sublist(suite) ->
- [sublist_2, sublist_3, sublist_2_e, sublist_3_e].
-define(sublist_error2(X,Y), ?line {'EXIT', _} = (catch lists:sublist(X,Y))).
-define(sublist_error3(X,Y,Z), ?line {'EXIT', _} = (catch lists:sublist(X,Y,Z))).
@@ -2326,10 +2321,6 @@ sublist_3_e(Config) when is_list(Config) ->
%%%------------------------------------------------------------
-flatten(doc) ->
- ["Tests lists:flatten/[1,2]"];
-flatten(suite) ->
- [flatten_1, flatten_2, flatten_1_e, flatten_2_e].
-define(flatten_error1(X), ?line {'EXIT', _} = (catch lists:flatten(X))).
-define(flatten_error2(X,Y), ?line {'EXIT', _} = (catch lists:flatten(X,Y))).
@@ -2489,10 +2480,6 @@ filpart(F, All, Exp) ->
Other = lists:filter(fun(E) -> not F(E) end, All),
{Exp,Other} = lists:partition(F, All).
-tickets(doc) ->
- ["Ticktes."];
-tickets(suite) ->
- [otp_5939, otp_6023, otp_6606, otp_7230].
otp_5939(doc) -> ["OTP-5939. Guard tests added."];
otp_5939(suite) -> [];
diff --git a/lib/stdlib/test/log_mf_h_SUITE.erl b/lib/stdlib/test/log_mf_h_SUITE.erl
index 640261f665..2fd05afb11 100644
--- a/lib/stdlib/test/log_mf_h_SUITE.erl
+++ b/lib/stdlib/test/log_mf_h_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,32 @@
%%
-module(log_mf_h_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
--export([all/1, test/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, test/1]).
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
-all(suite) -> [test].
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl
index 79a0a9af89..4e5df12798 100644
--- a/lib/stdlib/test/ms_transform_SUITE.erl
+++ b/lib/stdlib/test/ms_transform_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -19,9 +19,10 @@
-module(ms_transform_SUITE).
-author('[email protected]').
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([basic_ets/1]).
-export([basic_dbg/1]).
-export([from_shell/1]).
@@ -37,20 +38,122 @@
-export([andalso_orelse/1]).
-export([float_1_function/1]).
-export([action_function/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([warnings/1]).
+-export([init_per_testcase/2, end_per_testcase/2]).
init_per_testcase(_Func, Config) ->
Dog=test_server:timetrap(test_server:seconds(360)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Func, Config) ->
+end_per_testcase(_Func, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog).
-all(suite) -> [from_shell,basic_ets,basic_dbg,records,record_index,multipass,
- bitsyntax, record_defaults, andalso_orelse,
- float_1_function, action_function,
- top_match, old_guards, autoimported, semicolon].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [from_shell, basic_ets, basic_dbg, records,
+ record_index, multipass, bitsyntax, record_defaults,
+ andalso_orelse, float_1_function, action_function,
+ warnings, top_match, old_guards, autoimported,
+ semicolon].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+%% This may be subject to change
+-define(WARN_NUMBER_SHADOW,50).
+warnings(suite) ->
+ [];
+warnings(doc) ->
+ ["Check that shadowed variables in fun head generate warning"];
+warnings(Config) when is_list(Config) ->
+ ?line setup(Config),
+ Prog = <<"A=5, "
+ "ets:fun2ms(fun({A,B}) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " A andalso B "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}}]}] =
+ compile_ww(Prog),
+ Prog2 = <<"C=5, "
+ "ets:fun2ms(fun({A,B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Prog2),
+ Rec3 = <<"-record(a,{a,b,c,d=foppa}).">>,
+ Prog3 = <<"A=3,C=5, "
+ "ets:fun2ms(fun(#a{a = A, b = B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
+ {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Rec3,Prog3),
+ Rec4 = <<"-record(a,{a,b,c,d=foppa}).">>,
+ Prog4 = <<"A=3,C=5, "
+ "F = fun(B) -> B*3 end,"
+ "erlang:display(F(A)),"
+ "ets:fun2ms(fun(#a{a = A, b = B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
+ {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Rec4,Prog4),
+ Rec5 = <<"-record(a,{a,b,c,d=foppa}).">>,
+ Prog5 = <<"A=3,C=5, "
+ "F = fun(B) -> B*3 end,"
+ "erlang:display(F(A)),"
+ "B = ets:fun2ms(fun(#a{a = A, b = B} = C) "
+ " when is_integer(A) and (A+5 > B) -> "
+ " {A andalso B,C} "
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'A'}},
+ {_,ms_transform,{?WARN_NUMBER_SHADOW,'C'}}]}] =
+ compile_ww(Rec5,Prog5),
+ Prog6 = <<" X=bar, "
+ " A = case X of"
+ " foo ->"
+ " foo;"
+ " Y ->"
+ " ets:fun2ms(fun(Y) ->" % This is a warning
+ " 3*Y"
+ " end)"
+ " end,"
+ " ets:fun2ms(fun(Y) ->" % Y out of "scope" here, so no warning
+ " {3*Y,A}"
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] =
+ compile_ww(Prog6),
+ Prog7 = <<" X=bar, "
+ " A = case X of"
+ " foo ->"
+ " Y = foo;"
+ " Y ->"
+ " bar"
+ " end,"
+ " ets:fun2ms(fun(Y) ->" % Y exported from case and safe, so warn
+ " {3*Y,A}"
+ " end)">>,
+ ?line [{_,[{_,ms_transform,{?WARN_NUMBER_SHADOW,'Y'}}]}] =
+ compile_ww(Prog7),
+ ok.
andalso_orelse(suite) ->
[];
@@ -721,6 +824,24 @@ compile_and_run(Records,Expr) ->
code:load_binary(tmp,FN,Bin),
tmp:tmp().
+compile_ww(Expr) ->
+ compile_ww(<<>>,Expr).
+compile_ww(Records,Expr) ->
+ Prog = <<
+ "-module(tmp).\n",
+ "-include_lib(\"stdlib/include/ms_transform.hrl\").\n",
+ "-export([tmp/0]).\n",
+ Records/binary,"\n",
+ "tmp() ->\n",
+ Expr/binary,".\n">>,
+ FN=temp_name(),
+ file:write_file(FN,Prog),
+ {ok,Forms} = epp:parse_file(FN,"",""),
+ {ok,tmp,_Bin,Wlist} = compile:forms(Forms,[return_warnings,
+ nowarn_unused_vars,
+ nowarn_unused_record]),
+ Wlist.
+
do_eval(String) ->
{done,{ok,T,_},[]} = erl_scan:tokens(
[],
diff --git a/lib/stdlib/test/naughty_child.erl b/lib/stdlib/test/naughty_child.erl
index b56130929c..b939436bfc 100644
--- a/lib/stdlib/test/naughty_child.erl
+++ b/lib/stdlib/test/naughty_child.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
index 2fd7725335..1565aa9bba 100644
--- a/lib/stdlib/test/proc_lib_SUITE.erl
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,10 +23,12 @@
%%
%%-define(STANDALONE,1).
--export([all/1, crash/1, sync_start/1, sync_start_nolink/1, sync_start_link/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ crash/1, sync_start_nolink/1, sync_start_link/1,
spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1,
hibernate/1]).
--export([tickets/1, otp_6345/1]).
+-export([ otp_6345/1]).
-export([hib_loop/1, awaken/1]).
@@ -40,12 +42,32 @@
-ifdef(STANDALONE).
-define(line, noop, ).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-endif.
-all(suite) -> [crash, sync_start, spawn_opt, hibernate, tickets].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [crash, {group, sync_start}, spawn_opt, hibernate,
+ {group, tickets}].
+
+groups() ->
+ [{tickets, [], [otp_6345]},
+ {sync_start, [], [sync_start_nolink, sync_start_link]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-tickets(suite) -> [otp_6345].
%%-----------------------------------------------------------------
%% We don't have to test that spwn and spawn_link actually spawns
@@ -127,7 +149,6 @@ crash(Config) when is_list(Config) ->
ok
end.
-sync_start(suite) -> [sync_start_nolink, sync_start_link].
sync_start_nolink(Config) when is_list(Config) ->
_Pid = spawn_link(?MODULE, sp5, [self()]),
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index aa12ed57da..98eeaee118 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -43,7 +43,7 @@
-define(testcase, current_testcase). % don't know
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(datadir, ?config(data_dir, Config)).
-define(privdir, ?config(priv_dir, Config)).
-define(testcase, ?config(?TESTCASE, Config)).
@@ -51,36 +51,33 @@
-include_lib("stdlib/include/ms_transform.hrl").
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
--export([parse_transform/1,
- badarg/1, nested_qlc/1, unused_var/1, lc/1, fun_clauses/1,
- filter_var/1, single/1, exported_var/1, generator_vars/1,
- nomatch/1, errors/1, pattern/1,
+-export([
+ badarg/1, nested_qlc/1, unused_var/1, lc/1, fun_clauses/1,
+ filter_var/1, single/1, exported_var/1, generator_vars/1,
+ nomatch/1, errors/1, pattern/1,
- evaluation/1,
- eval/1, cursor/1, fold/1, eval_unique/1, eval_cache/1, append/1,
- evaluator/1, string_to_handle/1, table/1, process_dies/1,
- sort/1, keysort/1, filesort/1, cache/1, cache_list/1, filter/1,
- info/1, nested_info/1, lookup1/1, lookup2/1, lookup_rec/1,
- indices/1, pre_fun/1, skip_filters/1,
+ eval/1, cursor/1, fold/1, eval_unique/1, eval_cache/1, append/1,
+ evaluator/1, string_to_handle/1, table/1, process_dies/1,
+ sort/1, keysort/1, filesort/1, cache/1, cache_list/1, filter/1,
+ info/1, nested_info/1, lookup1/1, lookup2/1, lookup_rec/1,
+ indices/1, pre_fun/1, skip_filters/1,
- table_impls/1,
- ets/1, dets/1,
+ ets/1, dets/1,
- join/1,
- join_option/1, join_filter/1, join_lookup/1, join_merge/1,
- join_sort/1, join_complex/1,
+ join_option/1, join_filter/1, join_lookup/1, join_merge/1,
+ join_sort/1, join_complex/1,
- tickets/1,
- otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1,
- otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1,
- otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1,
+ otp_5644/1, otp_5195/1, otp_6038_bug/1, otp_6359/1, otp_6562/1,
+ otp_6590/1, otp_6673/1, otp_6964/1, otp_7114/1, otp_7238/1,
+ otp_7232/1, otp_7552/1, otp_6674/1, otp_7714/1,
- manpage/1,
+ manpage/1,
- compat/1,
- backward/1, forward/1]).
+ backward/1, forward/1]).
%% Internal exports.
-export([bad_table_throw/1, bad_table_exit/1, default_table/1, bad_table/1,
@@ -114,17 +111,50 @@ init_per_testcase(Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{?TESTCASE, Case}, {watchdog, Dog} | Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog = ?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [parse_transform, evaluation, table_impls, join, tickets, manpage, compat].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, parse_transform}, {group, evaluation},
+ {group, table_impls}, {group, join}, {group, tickets},
+ manpage, {group, compat}].
+
+groups() ->
+ [{parse_transform, [],
+ [badarg, nested_qlc, unused_var, lc, fun_clauses,
+ filter_var, single, exported_var, generator_vars,
+ nomatch, errors, pattern]},
+ {evaluation, [],
+ [eval, cursor, fold, eval_unique, eval_cache, append,
+ evaluator, string_to_handle, table, process_dies, sort,
+ keysort, filesort, cache, cache_list, filter, info,
+ nested_info, lookup1, lookup2, lookup_rec, indices,
+ pre_fun, skip_filters]},
+ {table_impls, [], [ets, dets]},
+ {join, [],
+ [join_option, join_filter, join_lookup, join_merge,
+ join_sort, join_complex]},
+ {tickets, [],
+ [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562,
+ otp_6590, otp_6673, otp_6964, otp_7114, otp_7232,
+ otp_7238, otp_7552, otp_6674, otp_7714]},
+ {compat, [], [backward, forward]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
-parse_transform(suite) ->
- [badarg, nested_qlc, unused_var, lc, fun_clauses, filter_var,
- single, exported_var, generator_vars, nomatch, errors, pattern].
+end_per_group(_GroupName, Config) ->
+ Config.
badarg(doc) ->
"Badarg.";
@@ -461,11 +491,6 @@ pattern(Config) when is_list(Config) ->
-record(k, {t,v}).\n">>, Ts),
ok.
-evaluation(suite) ->
- [eval, cursor, fold, eval_unique, eval_cache, append, evaluator,
- string_to_handle, table, process_dies, sort, keysort, filesort, cache,
- cache_list, filter, info, nested_info, lookup1, lookup2, lookup_rec,
- indices, pre_fun, skip_filters].
eval(doc) ->
"eval/2";
@@ -3184,7 +3209,9 @@ lookup2(Config) when is_list(Config) ->
[] = qlc:e(Q),
false = lookup_keys(Q)
end, [{1,b},{2,3}])">>,
- {warnings,[{{3,48},qlc,nomatch_filter}]}},
+ {warnings,[{2,sys_core_fold,nomatch_guard},
+ {3,qlc,nomatch_filter},
+ {3,sys_core_fold,{eval_failure,badarg}}]}},
<<"etsc(fun(E) ->
Q = qlc:q([X || {X} <- ets:table(E), element(1,{X}) =:= 1]),
@@ -4295,8 +4322,6 @@ skip_filters(Config) when is_list(Config) ->
ok.
-table_impls(suite) ->
- [ets, dets].
ets(doc) ->
"ets:table/1,2.";
@@ -4443,9 +4468,6 @@ dets(Config) when is_list(Config) ->
_ = file:delete(Fname),
ok.
-join(suite) ->
- [join_option, join_filter, join_lookup, join_merge,
- join_sort, join_complex].
join_option(doc) ->
"The 'join' option (any, lookup, merge, nested_loop). Also cache/unique.";
@@ -5727,10 +5749,6 @@ join_complex(Config) when is_list(Config) ->
ok.
-tickets(suite) ->
- [otp_5644, otp_5195, otp_6038_bug, otp_6359, otp_6562, otp_6590,
- otp_6673, otp_6964, otp_7114, otp_7232, otp_7238, otp_7552, otp_6674,
- otp_7714].
otp_5644(doc) ->
"OTP-5644. Handle the new language element M:F/A.";
@@ -7376,8 +7394,6 @@ gb_iter(I0, N, EFun) ->
end.
">>.
-compat(suite) ->
- [backward, forward].
backward(doc) ->
"OTP-6674. Join info and extra constants.";
diff --git a/lib/stdlib/test/queue_SUITE.erl b/lib/stdlib/test/queue_SUITE.erl
index 2cd6b52311..3d3152919a 100644
--- a/lib/stdlib/test/queue_SUITE.erl
+++ b/lib/stdlib/test/queue_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,13 +17,14 @@
%% %CopyrightEnd%
%%
-module(queue_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([do/1, to_list/1, io_test/1, op_test/1, error/1, oops/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -31,16 +32,32 @@
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test cases for queue."];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[do, to_list, io_test, op_test, error, oops].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
do(doc) ->
[""];
do(suite) ->
diff --git a/lib/stdlib/test/random_SUITE.erl b/lib/stdlib/test/random_SUITE.erl
index 8f1c304705..ac9d1a6c06 100644
--- a/lib/stdlib/test/random_SUITE.erl
+++ b/lib/stdlib/test/random_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,13 +17,14 @@
%% %CopyrightEnd%
-module(random_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([interval_1/1, seed0/1, seed/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -31,16 +32,32 @@
init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?default_timeout),
[{watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test cases for random."];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[interval_1, seed0, seed].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
seed0(doc) ->
["Test that seed is set implicitly, and always the same."];
seed0(suite) ->
diff --git a/lib/stdlib/test/random_iolist.erl b/lib/stdlib/test/random_iolist.erl
index 4bce347d9a..8f21b5a3b3 100644
--- a/lib/stdlib/test/random_iolist.erl
+++ b/lib/stdlib/test/random_iolist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/random_unicode_list.erl b/lib/stdlib/test/random_unicode_list.erl
index 3e83383b08..b8bd719b89 100644
--- a/lib/stdlib/test/random_unicode_list.erl
+++ b/lib/stdlib/test/random_unicode_list.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl
index 46a84d4e24..c4817c0d38 100644
--- a/lib/stdlib/test/re_SUITE.erl
+++ b/lib/stdlib/test/re_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,41 @@
%%
-module(re_SUITE).
--export([all/1, pcre/1,compile_options/1,run_options/1,combined_options/1,replace_autogen/1,global_capture/1,replace_input_types/1,replace_return/1,split_autogen/1,split_options/1,split_specials/1,error_handling/1,pcre_cve_2008_2371/1,pcre_compile_workspace_overflow/1,re_infinite_loop/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, pcre/1,compile_options/1,
+ run_options/1,combined_options/1,replace_autogen/1,
+ global_capture/1,replace_input_types/1,replace_return/1,
+ split_autogen/1,split_options/1,split_specials/1,
+ error_handling/1,pcre_cve_2008_2371/1,
+ pcre_compile_workspace_overflow/1,re_infinite_loop/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
-all(suite) -> [pcre,compile_options,run_options,combined_options,replace_autogen,global_capture,replace_input_types,replace_return,split_autogen,split_options,split_specials,error_handling,pcre_cve_2008_2371,pcre_compile_workspace_overflow,re_infinite_loop].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [pcre, compile_options, run_options, combined_options,
+ replace_autogen, global_capture, replace_input_types,
+ replace_return, split_autogen, split_options,
+ split_specials, error_handling, pcre_cve_2008_2371,
+ pcre_compile_workspace_overflow, re_infinite_loop].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
pcre(doc) ->
["Run all applicable tests from the PCRE testsuites."];
diff --git a/lib/stdlib/test/select_SUITE.erl b/lib/stdlib/test/select_SUITE.erl
index 6900f1a8f5..546c25f954 100644
--- a/lib/stdlib/test/select_SUITE.erl
+++ b/lib/stdlib/test/select_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -37,7 +37,7 @@
-export([config/2]).
-define(fmt(A,B),io:format(A,B)).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(fmt(A,B),test_server:format(A,B)).
-endif.
@@ -58,23 +58,41 @@ config(priv_dir,_) ->
".".
-else.
%% When run in test server.
--export([all/1,select_test/1,init_per_testcase/2, fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,select_test/1,
+ init_per_testcase/2, end_per_testcase/2,
return_values/1]).
init_per_testcase(_Case, Config) when is_list(Config) ->
?line Dog=test_server:timetrap(test_server:seconds(1200)),
[{watchdog, Dog}|Config].
-
-fin_per_testcase(_Case, Config) ->
+
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test ets:select"];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[return_values, select_test].
-
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
select_test(suite) ->
[];
select_test(doc) ->
diff --git a/lib/stdlib/test/sets_SUITE.erl b/lib/stdlib/test/sets_SUITE.erl
index c9f1a03598..f284276bd7 100644
--- a/lib/stdlib/test/sets_SUITE.erl
+++ b/lib/stdlib/test/sets_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,13 +22,15 @@
-module(sets_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2,
create/1,add_element/1,del_element/1,
subtract/1,intersection/1,union/1,is_subset/1,
is_set/1,fold/1,filter/1,
take_smallest/1,take_largest/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-import(lists, [foldl/3,reverse/1]).
@@ -36,15 +38,33 @@ init_per_testcase(_Case, Config) ->
?line Dog = ?t:timetrap(?t:minutes(5)),
[{watchdog,Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
-all(suite) ->
- [create,add_element,del_element,subtract,
- intersection,union,is_subset,is_set,fold,filter,
- take_smallest,take_largest].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [create, add_element, del_element, subtract,
+ intersection, union, is_subset, is_set, fold, filter,
+ take_smallest, take_largest].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
create(Config) when is_list(Config) ->
test_all(fun create_1/1).
diff --git a/lib/stdlib/test/sets_test_lib.erl b/lib/stdlib/test/sets_test_lib.erl
index 6b6fb00550..bdfb0d59d2 100644
--- a/lib/stdlib/test/sets_test_lib.erl
+++ b/lib/stdlib/test/sets_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index 588342d46a..8273377ba1 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,21 +17,22 @@
%% %CopyrightEnd%
%%
-module(shell_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
-export([forget/1, records/1, known_bugs/1, otp_5226/1, otp_5327/1,
- otp_5435/1, otp_5195/1, otp_5915/1, otp_5916/1,
- bits/1, bs_match_misc_SUITE/1, bs_match_int_SUITE/1,
- bs_match_tail_SUITE/1, bs_match_bin_SUITE/1,
- bs_construct_SUITE/1,
- refman/1, refman_bit_syntax/1,
- progex/1, progex_bit_syntax/1, progex_records/1,
- progex_lc/1, progex_funs/1,
- tickets/1, otp_5990/1, otp_6166/1, otp_6554/1, otp_6785/1,
- otp_7184/1, otp_7232/1, otp_8393/1]).
-
--export([restricted/1, start_restricted_from_shell/1,
- start_restricted_on_command_line/1,restricted_local/1]).
+ otp_5435/1, otp_5195/1, otp_5915/1, otp_5916/1,
+ bs_match_misc_SUITE/1, bs_match_int_SUITE/1,
+ bs_match_tail_SUITE/1, bs_match_bin_SUITE/1,
+ bs_construct_SUITE/1,
+ refman_bit_syntax/1,
+ progex_bit_syntax/1, progex_records/1,
+ progex_lc/1, progex_funs/1,
+ otp_5990/1, otp_6166/1, otp_6554/1, otp_6785/1,
+ otp_7184/1, otp_7232/1, otp_8393/1]).
+
+-export([ start_restricted_from_shell/1,
+ start_restricted_on_command_line/1,restricted_local/1]).
%% Internal export.
-export([otp_5435_2/0, prompt1/1, prompt2/1, prompt3/1, prompt4/1,
@@ -50,8 +51,8 @@
config(priv_dir,_) ->
".".
-else.
--include("test_server.hrl").
--export([init_per_testcase/2, fin_per_testcase/2]).
+-include_lib("test_server/include/test_server.hrl").
+-export([init_per_testcase/2, end_per_testcase/2]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(2)).
init_per_testcase(_Case, Config) ->
@@ -60,7 +61,7 @@ init_per_testcase(_Case, Config) ->
?line code:add_patha(?config(priv_dir,Config)),
[{orig_path,OrigPath}, {watchdog, Dog} | Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
?line Dog = ?config(watchdog, Config),
?line test_server:timetrap_cancel(Dog),
?line OrigPath = ?config(orig_path,Config),
@@ -71,18 +72,44 @@ fin_per_testcase(_Case, Config) ->
ok.
-endif.
-all(doc) ->
- ["Test cases for the 'shell' module."];
-all(suite) ->
- [forget, records, known_bugs, otp_5226, otp_5327, otp_5435, otp_5195,
- otp_5915, otp_5916, bits, refman, progex, tickets, restricted].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [forget, records, known_bugs, otp_5226, otp_5327,
+ otp_5435, otp_5195, otp_5915, otp_5916, {group, bits},
+ {group, refman}, {group, progex}, {group, tickets},
+ {group, restricted}].
+
+groups() ->
+ [{restricted, [],
+ [start_restricted_from_shell,
+ start_restricted_on_command_line, restricted_local]},
+ {bits, [],
+ [bs_match_misc_SUITE, bs_match_tail_SUITE,
+ bs_match_bin_SUITE, bs_construct_SUITE]},
+ {refman, [], [refman_bit_syntax]},
+ {progex, [],
+ [progex_bit_syntax, progex_records, progex_lc,
+ progex_funs]},
+ {tickets, [],
+ [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184,
+ otp_7232, otp_8393]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
-record(state, {bin, reply, leader}).
-restricted(doc) ->
- ["Test restricted_shell"];
-restricted(suite) ->
- [start_restricted_from_shell,start_restricted_on_command_line,restricted_local].
start_restricted_from_shell(doc) ->
["Test that a restricted shell can be started from the normal shell"];
@@ -797,9 +824,6 @@ otp_5916(Config) when is_list(Config) ->
[ok] = scan(C),
ok.
-bits(suite) ->
- [bs_match_misc_SUITE, % bs_match_int_SUITE/,
- bs_match_tail_SUITE, bs_match_bin_SUITE, bs_construct_SUITE].
bs_match_misc_SUITE(doc) ->
["OTP-5327. Adopted from parts of emulator/test/bs_match_misc_SUITE.erl."];
@@ -1520,8 +1544,6 @@ evaluate(Str, Vars) ->
Result
end.
-refman(suite) ->
- [refman_bit_syntax].
refman_bit_syntax(doc) ->
["Bit syntax examples from the Reference Manual. OTP-5237."];
@@ -1564,8 +1586,6 @@ refman_bit_syntax(Config) when is_list(Config) ->
?line <<2,4,6>> = << << (X*2) >> || <<X>> <= << 1,2,3 >> >>,
ok.
-progex(suite) ->
- [progex_bit_syntax, progex_records, progex_lc, progex_funs].
-define(IP_VERSION, 4).
-define(IP_MIN_HDR_LEN, 5).
@@ -2256,8 +2276,6 @@ progex_funs(Config) when is_list(Config) ->
?line [ok] = scan(Test2_shell),
ok.
-tickets(suite) ->
- [otp_5990, otp_6166, otp_6554, otp_6785, otp_7184, otp_7232, otp_8393].
otp_5990(doc) ->
"OTP-5990. {erlang,is_record}.";
diff --git a/lib/stdlib/test/slave_SUITE.erl b/lib/stdlib/test/slave_SUITE.erl
index 5c1282fe9b..37fc694083 100644
--- a/lib/stdlib/test/slave_SUITE.erl
+++ b/lib/stdlib/test/slave_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,18 +18,37 @@
%%
-module(slave_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1, t_start/1, t_start_link/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, t_start/1, t_start_link/1,
start_link_nodedown/1, errors/1]).
%% Internal exports.
-export([fun_init/1, test_errors/1]).
-export([timeout_test/1, auth_test/1, rsh_test/1, start_a_slave/3]).
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[t_start_link, start_link_nodedown, t_start, errors].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
t_start_link(suite) -> [];
t_start_link(Config) when is_list(Config) ->
?line Dog = test_server:timetrap(test_server:seconds(20)),
diff --git a/lib/stdlib/test/sofs_SUITE.erl b/lib/stdlib/test/sofs_SUITE.erl
index d60cfc6895..01de1f0600 100644
--- a/lib/stdlib/test/sofs_SUITE.erl
+++ b/lib/stdlib/test/sofs_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,13 +26,14 @@
-define(config(X,Y), foo).
-define(t, test_server).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-endif.
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
--export([sofs/1, from_term_1/1, set_1/1, from_sets_1/1, relation_1/1,
+-export([ from_term_1/1, set_1/1, from_sets_1/1, relation_1/1,
a_function_1/1, family_1/1, projection/1,
relation_to_family_1/1, domain_1/1, range_1/1, image/1,
inverse_image/1, inverse_1/1, converse_1/1, no_elements_1/1,
@@ -47,7 +48,7 @@
multiple_relative_product/1, digraph/1, constant_function/1,
misc/1]).
--export([sofs_family/1, family_specification/1,
+-export([ family_specification/1,
family_domain_1/1, family_range_1/1,
family_to_relation_1/1,
union_of_family_1/1, intersection_of_family_1/1,
@@ -81,18 +82,56 @@
union/1, union/2, family_to_digraph/1, family_to_digraph/2,
digraph_to_family/1, digraph_to_family/2]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
-compile({inline,[{eval,2}]}).
-all(suite) ->
- [sofs, sofs_family].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, sofs}, {group, sofs_family}].
+
+groups() ->
+ [{sofs, [],
+ [from_term_1, set_1, from_sets_1, relation_1,
+ a_function_1, family_1, relation_to_family_1, domain_1,
+ range_1, image, inverse_image, inverse_1, converse_1,
+ no_elements_1, substitution, restriction, drestriction,
+ projection, strict_relation_1, extension,
+ weak_relation_1, to_sets_1, specification, union_1,
+ intersection_1, difference, symdiff,
+ symmetric_partition, is_sofs_set_1, is_set_1, is_equal,
+ is_subset, is_a_function_1, is_disjoint, join,
+ canonical, composite_1, relative_product_1,
+ relative_product_2, product_1, partition_1, partition_3,
+ multiple_relative_product, digraph, constant_function,
+ misc]},
+ {sofs_family, [],
+ [family_specification, family_domain_1, family_range_1,
+ family_to_relation_1, union_of_family_1,
+ intersection_of_family_1, family_projection,
+ family_difference, family_intersection_1,
+ family_intersection_2, family_union_1, family_union_2,
+ partition_family]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
Dog=?t:timetrap(?t:minutes(2)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -100,18 +139,6 @@ fin_per_testcase(_Case, Config) ->
%% [{2,b},{1,a,b}] == lists:sort([{2,b},{1,a,b}])
%% [{1,a,b},{2,b}] == lists:keysort(1,[{2,b},{1,a,b}])
-sofs(suite) ->
- [from_term_1, set_1, from_sets_1, relation_1, a_function_1,
- family_1, relation_to_family_1, domain_1, range_1, image,
- inverse_image, inverse_1, converse_1, no_elements_1,
- substitution, restriction, drestriction, projection,
- strict_relation_1, extension, weak_relation_1, to_sets_1,
- specification, union_1, intersection_1, difference, symdiff,
- symmetric_partition, is_sofs_set_1, is_set_1, is_equal,
- is_subset, is_a_function_1, is_disjoint, join, canonical,
- composite_1, relative_product_1, relative_product_2, product_1,
- partition_1, partition_3, multiple_relative_product, digraph,
- constant_function, misc].
from_term_1(suite) -> [];
from_term_1(doc) -> [""];
@@ -1934,12 +1961,6 @@ relational_restriction(R) ->
Fun = fun(S) -> no_elements(S) > 1 end,
family_to_relation(family_specification(Fun, relation_to_family(R))).
-sofs_family(suite) ->
- [family_specification, family_domain_1, family_range_1,
- family_to_relation_1, union_of_family_1, intersection_of_family_1,
- family_projection, family_difference,
- family_intersection_1, family_intersection_2,
- family_union_1, family_union_2, partition_family].
family_specification(suite) -> [];
family_specification(doc) -> [""];
diff --git a/lib/stdlib/test/stdlib.cover b/lib/stdlib/test/stdlib.cover
index b98d949889..61f4f064b9 100644
--- a/lib/stdlib/test/stdlib.cover
+++ b/lib/stdlib/test/stdlib.cover
@@ -1,10 +1,17 @@
%% -*- erlang -*-
-{exclude,
- [erl_parse,
- ets,
- filename,
- gen_event,
- gen_server,
- gen,
- lists,
- proc_lib]}.
+{incl_app,stdlib,details}.
+
+{excl_mods,stdlib,
+ [erl_parse,
+ erl_eval,
+ ets,
+ filename,
+ gen_event,
+ gen_server,
+ gen,
+ lists,
+ io,
+ io_lib,
+ io_lib_format,
+ io_lib_pretty,
+ proc_lib]}.
diff --git a/lib/stdlib/test/stdlib.spec b/lib/stdlib/test/stdlib.spec
index bbfb43bd15..3768e494b2 100644
--- a/lib/stdlib/test/stdlib.spec
+++ b/lib/stdlib/test/stdlib.spec
@@ -1,4 +1 @@
-{topcase, {dir, "../stdlib_test"}}.
-%{skip,{dets_SUITE,open_file_1,"Crashes Windows tests"}}.
-%{skip,{dets_SUITE,fold,"Crashes Windows tests"}}.
-%{skip,{dets_SUITE,match,"Crashes Windows tests"}}.
+{suites,"../stdlib_test",all}.
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index d46a2caf90..0cca030b3d 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,7 +20,7 @@
%%% Purpose:Stdlib application test suite.
%%%-----------------------------------------------------------------
-module(stdlib_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
@@ -28,8 +28,9 @@
-define(application, stdlib).
% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
-export([app_test/1]).
@@ -38,15 +39,31 @@
%%
%% all/1
%%
-all(doc) ->
- [];
-all(suite) ->
- [?cases].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [app_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -58,7 +75,7 @@ app_test(suite) ->
[];
app_test(doc) ->
["Application consistency test."];
-app_test(Config) when list(Config) ->
+app_test(Config) when is_list(Config) ->
?t:app_test(stdlib),
ok.
diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl
index 3171b87c44..1dcd4be21e 100644
--- a/lib/stdlib/test/string_SUITE.erl
+++ b/lib/stdlib/test/string_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,15 +20,16 @@
%%% Purpose: string test suite.
%%%-----------------------------------------------------------------
-module(string_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
-export([len/1,equal/1,concat/1,chr_rchr/1,str_rstr/1]).
@@ -40,19 +41,34 @@
%%
%% all/1
%%
-all(doc) ->
- [];
-all(suite) ->
- [len,equal,concat,chr_rchr,str_rstr,
- span_cspan,substr,tokens,chars,
- copies,words,strip,sub_word,left_right,
- sub_string,centre, join,
- to_integer,to_float,to_upper_to_lower].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [len, equal, concat, chr_rchr, str_rstr, span_cspan,
+ substr, tokens, chars, copies, words, strip, sub_word,
+ left_right, sub_string, centre, join, to_integer,
+ to_float, to_upper_to_lower].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
@@ -240,7 +256,8 @@ copies(Config) when is_list(Config) ->
?line "." = string:copies(".", 1),
?line 30 = length(string:copies("123", 10)),
%% invalid arg type
- ?line {'EXIT',_} = (catch string:chars("hej", -1)),
+ ?line {'EXIT',_} = (catch string:copies("hej", -1)),
+ ?line {'EXIT',_} = (catch string:copies("hej", 2.0)),
ok.
words(suite) ->
diff --git a/lib/stdlib/test/supervisor_1.erl b/lib/stdlib/test/supervisor_1.erl
index 297550b230..3198be0fed 100644
--- a/lib/stdlib/test/supervisor_1.erl
+++ b/lib/stdlib/test/supervisor_1.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 039ea298c4..6e927da2ab 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -20,49 +20,104 @@
-module(supervisor_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Testserver specific export
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, init_per_testcase/2,
+ end_per_testcase/2]).
%% Indirect spawn export
-export([init/1]).
%% API tests
--export([sup_start/1, sup_start_normal/1, sup_start_ignore_init/1,
+-export([ sup_start_normal/1, sup_start_ignore_init/1,
sup_start_ignore_child/1, sup_start_error_return/1,
- sup_start_fail/1, sup_stop/1, sup_stop_infinity/1,
+ sup_start_fail/1, sup_stop_infinity/1,
sup_stop_timeout/1, sup_stop_brutal_kill/1, child_adm/1,
child_adm_simple/1, child_specs/1, extra_return/1]).
%% Tests concept permanent, transient and temporary
--export([normal_termination/1, permanent_normal/1, transient_normal/1,
- temporary_normal/1, abnormal_termination/1,
+-export([ permanent_normal/1, transient_normal/1,
+ temporary_normal/1,
permanent_abnormal/1, transient_abnormal/1,
temporary_abnormal/1]).
%% Restart strategy tests
--export([restart_one_for_one/1, one_for_one/1,
- one_for_one_escalation/1, restart_one_for_all/1, one_for_all/1,
- one_for_all_escalation/1, restart_simple_one_for_one/1,
+-export([ one_for_one/1,
+ one_for_one_escalation/1, one_for_all/1,
+ one_for_all_escalation/1,
simple_one_for_one/1, simple_one_for_one_escalation/1,
- restart_rest_for_one/1, rest_for_one/1, rest_for_one_escalation/1,
+ rest_for_one/1, rest_for_one_escalation/1,
simple_one_for_one_extra/1]).
%% Misc tests
--export([child_unlink/1, tree/1, count_children_memory/1]).
+-export([child_unlink/1, tree/1, count_children_memory/1,
+ do_not_save_start_parameters_for_temporary_children/1]).
%-------------------------------------------------------------------------
-all(suite) ->
- {req,[stdlib],
- [sup_start, sup_stop, child_adm,
- child_adm_simple, extra_return, child_specs,
- restart_one_for_one, restart_one_for_all,
- restart_simple_one_for_one, restart_rest_for_one,
- normal_termination, abnormal_termination, child_unlink, tree,
- count_children_memory]}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, sup_start}, {group, sup_stop}, child_adm,
+ child_adm_simple, extra_return, child_specs,
+ {group, restart_one_for_one},
+ {group, restart_one_for_all},
+ {group, restart_simple_one_for_one},
+ {group, restart_rest_for_one},
+ {group, normal_termination},
+ {group, abnormal_termination}, child_unlink, tree,
+ count_children_memory, do_not_save_start_parameters_for_temporary_children].
+
+groups() ->
+ [{sup_start, [],
+ [sup_start_normal, sup_start_ignore_init,
+ sup_start_ignore_child, sup_start_error_return,
+ sup_start_fail]},
+ {sup_stop, [],
+ [sup_stop_infinity, sup_stop_timeout,
+ sup_stop_brutal_kill]},
+ {normal_termination, [],
+ [permanent_normal, transient_normal, temporary_normal]},
+ {abnormal_termination, [],
+ [permanent_abnormal, transient_abnormal,
+ temporary_abnormal]},
+ {restart_one_for_one, [],
+ [one_for_one, one_for_one_escalation]},
+ {restart_one_for_all, [],
+ [one_for_all, one_for_all_escalation]},
+ {restart_simple_one_for_one, [],
+ [simple_one_for_one, simple_one_for_one_extra,
+ simple_one_for_one_escalation]},
+ {restart_rest_for_one, [],
+ [rest_for_one, rest_for_one_escalation]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(count_children_memory, Config) ->
+ MemoryState = erlang:system_info(allocator),
+ case count_children_allocator_test(MemoryState) of
+ true -> Config;
+ false ->
+ {skip, "+Meamin used during test; erlang:memory/1 not available"}
+ end;
+init_per_testcase(_Case, Config) ->
+ Config.
+
+end_per_testcase(_Case, _Config) ->
+ ok.
start(InitResult) ->
supervisor:start_link({local, sup_test}, ?MODULE, InitResult).
@@ -81,19 +136,8 @@ get_child_counts(Supervisor) ->
proplists:get_value(supervisors, Counts),
proplists:get_value(workers, Counts)].
-
%-------------------------------------------------------------------------
-%
% Test cases starts here.
-%
-%-------------------------------------------------------------------------
-
-sup_start(doc) ->
- ["Test start of a supervisor."];
-sup_start(suite) ->
- [sup_start_normal, sup_start_ignore_init, sup_start_ignore_child,
- sup_start_error_return, sup_start_fail].
-
%-------------------------------------------------------------------------
sup_start_normal(doc) ->
["Tests that the supervisor process starts correctly and that it "
@@ -192,12 +236,6 @@ sup_start_fail(Config) when is_list(Config) ->
end,
ok.
%-------------------------------------------------------------------------
-sup_stop(doc) ->
- ["Tests that the supervisor shoutdowns its children if it is "
- "shutdown itself."];
-sup_stop(suite) -> [sup_stop_infinity, sup_stop_timeout, sup_stop_brutal_kill].
-
-%-------------------------------------------------------------------------
sup_stop_infinity(doc) ->
["See sup_stop/1 when Shutdown = infinity, this walue is only allowed "
@@ -549,11 +587,6 @@ child_specs(Config) when is_list(Config) ->
?line ok = supervisor:check_childspecs([C3]),
?line ok = supervisor:check_childspecs([C4]),
ok.
-%-------------------------------------------------------------------------
-normal_termination(doc) ->
- ["Testes the supervisors behaviour if a child dies with reason normal"];
-normal_termination(suite) ->
- [permanent_normal, transient_normal, temporary_normal].
%-------------------------------------------------------------------------
permanent_normal(doc) ->
@@ -615,11 +648,6 @@ temporary_normal(Config) when is_list(Config) ->
?line [1,0,0,1] = get_child_counts(sup_test),
ok.
-%-------------------------------------------------------------------------
-abnormal_termination(doc) ->
- ["Testes the supervisors behaviour if a child dies with reason abnormal"];
-abnormal_termination(suite) ->
- [permanent_abnormal, transient_abnormal, temporary_abnormal].
%-------------------------------------------------------------------------
permanent_abnormal(doc) ->
@@ -688,12 +716,6 @@ temporary_abnormal(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-restart_one_for_one(doc) ->
- ["Test that the one_for_one strategy works."];
-
-restart_one_for_one(suite) -> [one_for_one, one_for_one_escalation].
-
-%-------------------------------------------------------------------------
one_for_one(doc) ->
["Test the one_for_one base case."];
one_for_one(suite) -> [];
@@ -772,13 +794,6 @@ one_for_one_escalation(Config) when is_list(Config) ->
end,
ok.
%-------------------------------------------------------------------------
-restart_one_for_all(doc) ->
- ["Test that the one_for_all strategy works."];
-
-restart_one_for_all(suite) ->
- [one_for_all, one_for_all_escalation].
-
-%-------------------------------------------------------------------------
one_for_all(doc) ->
["Test the one_for_all base case."];
one_for_all(suite) -> [];
@@ -866,14 +881,6 @@ one_for_all_escalation(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-restart_simple_one_for_one(doc) ->
- ["Test that the simple_one_for_one strategy works."];
-
-restart_simple_one_for_one(suite) ->
- [simple_one_for_one, simple_one_for_one_extra,
- simple_one_for_one_escalation].
-
-%-------------------------------------------------------------------------
simple_one_for_one(doc) ->
["Test the simple_one_for_one base case."];
simple_one_for_one(suite) -> [];
@@ -990,11 +997,6 @@ simple_one_for_one_escalation(Config) when is_list(Config) ->
end,
ok.
%-------------------------------------------------------------------------
-restart_rest_for_one(doc) ->
- ["Test that the rest_for_one strategy works."];
-restart_rest_for_one(suite) -> [rest_for_one, rest_for_one_escalation].
-
-%-------------------------------------------------------------------------
rest_for_one(doc) ->
["Test the rest_for_one base case."];
rest_for_one(suite) -> [];
@@ -1267,26 +1269,10 @@ tree(Config) when is_list(Config) ->
ok.
%-------------------------------------------------------------------------
-count_children_allocator_test(MemoryState) ->
- Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc,
- driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc,
- sys_alloc],
- MemoryStateList = element(4, MemoryState),
- AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList)
- || Alloc <- Allocators],
- AllocStates = [lists:keyfind(e, 1, AllocValue)
- || {_Type, AllocValue} <- AllocTypes],
- lists:all(fun(State) -> State == {e, true} end, AllocStates).
-
count_children_memory(doc) ->
- ["Test that which_children eats memory, but count_children does not."];
+ ["Test that count_children does not eat memory."];
count_children_memory(suite) ->
- MemoryState = erlang:system_info(allocator),
- case count_children_allocator_test(MemoryState) of
- true -> [];
- false ->
- {skip, "+Meamin used during test; erlang:memory/1 not available"}
- end;
+ [];
count_children_memory(Config) when is_list(Config) ->
process_flag(trap_exit, true),
Child = {child, {supervisor_1, start_child, []}, temporary, 1000,
@@ -1299,7 +1285,7 @@ count_children_memory(Config) when is_list(Config) ->
Children = supervisor:which_children(sup_test),
_Size2 = erlang:memory(processes_used),
ChildCount = get_child_counts(sup_test),
- Size3 = erlang:memory(processes_used),
+ _Size3 = erlang:memory(processes_used),
[supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)],
@@ -1323,8 +1309,8 @@ count_children_memory(Config) when is_list(Config) ->
?line ChildCount3 = ChildCount2,
%% count_children consumes memory using an accumulator function,
- %% but the space can be reclaimed incrementally, whereas
- %% which_children generates a return list.
+ %% but the space can be reclaimed incrementally,
+ %% which_children may generate garbage that will be reclaimed later.
case (Size5 =< Size4) of
true -> ok;
false ->
@@ -1336,19 +1322,98 @@ count_children_memory(Config) when is_list(Config) ->
?line test_server:fail({count_children, used_more_memory})
end,
- case Size4 > Size3 of
- true -> ok;
- false ->
- ?line test_server:fail({which_children, used_no_memory})
- end,
- case Size6 > Size5 of
- true -> ok;
- false ->
- ?line test_server:fail({which_children, used_no_memory})
- end,
-
[exit(Pid, kill) || {undefined, Pid, worker, _Modules} <- Children3],
test_server:sleep(100),
?line [1,0,0,0] = get_child_counts(sup_test),
-
ok.
+count_children_allocator_test(MemoryState) ->
+ Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc,
+ driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc,
+ sys_alloc],
+ MemoryStateList = element(4, MemoryState),
+ AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList)
+ || Alloc <- Allocators],
+ AllocStates = [lists:keyfind(e, 1, AllocValue)
+ || {_Type, AllocValue} <- AllocTypes],
+ lists:all(fun(State) -> State == {e, true} end, AllocStates).
+%-------------------------------------------------------------------------
+do_not_save_start_parameters_for_temporary_children(doc) ->
+ ["Temporary children shall not be restarted so they should not "
+ "save start parameters, as it potentially can "
+ "take up a huge amount of memory for no purpose."];
+do_not_save_start_parameters_for_temporary_children(suite) ->
+ [];
+do_not_save_start_parameters_for_temporary_children(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ dont_save_start_parameters_for_temporary_children(one_for_all),
+ dont_save_start_parameters_for_temporary_children(one_for_one),
+ dont_save_start_parameters_for_temporary_children(rest_for_one),
+ dont_save_start_parameters_for_temporary_children(simple_one_for_one).
+
+dont_save_start_parameters_for_temporary_children(simple_one_for_one = Type) ->
+ Permanent = {child, {supervisor_1, start_child, []},
+ permanent, 1000, worker, []},
+ Transient = {child, {supervisor_1, start_child, []},
+ transient, 1000, worker, []},
+ Temporary = {child, {supervisor_1, start_child, []},
+ temporary, 1000, worker, []},
+ {ok, Sup1} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Permanent]}}),
+ {ok, Sup2} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Transient]}}),
+ {ok, Sup3} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, [Temporary]}}),
+
+ LargeList = lists:duplicate(10, "Potentially large"),
+
+ start_children(Sup1, [LargeList], 100),
+ start_children(Sup2, [LargeList], 100),
+ start_children(Sup3, [LargeList], 100),
+
+ [{memory,Mem1}] = process_info(Sup1, [memory]),
+ [{memory,Mem2}] = process_info(Sup2, [memory]),
+ [{memory,Mem3}] = process_info(Sup3, [memory]),
+
+ true = (Mem3 < Mem1) and (Mem3 < Mem2),
+
+ exit(Sup1, shutdown),
+ exit(Sup2, shutdown),
+ exit(Sup3, shutdown);
+
+dont_save_start_parameters_for_temporary_children(Type) ->
+ {ok, Sup1} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}),
+ {ok, Sup2} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}),
+ {ok, Sup3} = supervisor:start_link(?MODULE, {ok, {{Type, 2, 3600}, []}}),
+
+ LargeList = lists:duplicate(10, "Potentially large"),
+
+ Permanent = {child1, {supervisor_1, start_child, [LargeList]},
+ permanent, 1000, worker, []},
+ Transient = {child2, {supervisor_1, start_child, [LargeList]},
+ transient, 1000, worker, []},
+ Temporary = {child3, {supervisor_1, start_child, [LargeList]},
+ temporary, 1000, worker, []},
+
+ start_children(Sup1, Permanent, 100),
+ start_children(Sup2, Transient, 100),
+ start_children(Sup3, Temporary, 100),
+
+ [{memory,Mem1}] = process_info(Sup1, [memory]),
+ [{memory,Mem2}] = process_info(Sup2, [memory]),
+ [{memory,Mem3}] = process_info(Sup3, [memory]),
+
+ true = (Mem3 < Mem1) and (Mem3 < Mem2),
+
+ exit(Sup1, shutdown),
+ exit(Sup2, shutdown),
+ exit(Sup3, shutdown).
+
+start_children(_,_, 0) ->
+ ok;
+start_children(Sup, Args, N) ->
+ Spec = child_spec(Args, N),
+ {ok, _, _} = supervisor:start_child(Sup, Spec),
+ start_children(Sup, Args, N-1).
+
+child_spec([_|_] = SimpleOneForOneArgs, _) ->
+ SimpleOneForOneArgs;
+child_spec({Name, MFA, RestartType, Shutdown, Type, Modules}, N) ->
+ NewName = list_to_atom((atom_to_list(Name) ++ integer_to_list(N))),
+ {NewName, MFA, RestartType, Shutdown, Type, Modules}.
diff --git a/lib/stdlib/test/supervisor_bridge_SUITE.erl b/lib/stdlib/test/supervisor_bridge_SUITE.erl
index b23bac2d44..f2dbad0b3b 100644
--- a/lib/stdlib/test/supervisor_bridge_SUITE.erl
+++ b/lib/stdlib/test/supervisor_bridge_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,16 +17,37 @@
%% %CopyrightEnd%
%%
-module(supervisor_bridge_SUITE).
--export([all/1,starting/1,mini_terminate/1,mini_die/1,badstart/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,starting/1,
+ mini_terminate/1,mini_die/1,badstart/1]).
-export([client/1,init/1,internal_loop_init/1,terminate/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(bridge_name,supervisor_bridge_SUITE_server).
-define(work_bridge_name,work_supervisor_bridge_SUITE_server).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all(suite) -> [starting,mini_terminate,mini_die,badstart].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [starting, mini_terminate, mini_die, badstart].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/test/sys_SUITE.erl b/lib/stdlib/test/sys_SUITE.erl
index e44fd56403..72b089aa3f 100644
--- a/lib/stdlib/test/sys_SUITE.erl
+++ b/lib/stdlib/test/sys_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -17,9 +17,11 @@
%% %CopyrightEnd%
%%
-module(sys_SUITE).
--export([all/1,log/1,log_to_file/1,stats/1,trace/1,suspend/1,install/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,log/1,log_to_file/1,
+ stats/1,trace/1,suspend/1,install/1]).
-export([handle_call/3,terminate/2,init/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(server,sys_SUITE_server).
@@ -29,7 +31,26 @@
%% system messages at all.
-all(suite) -> [log,log_to_file,stats,trace,suspend,install].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [log, log_to_file, stats, trace, suspend, install].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/test/tar_SUITE.erl b/lib/stdlib/test/tar_SUITE.erl
index 7646f4c249..e32704ca65 100644
--- a/lib/stdlib/test/tar_SUITE.erl
+++ b/lib/stdlib/test/tar_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,21 +18,39 @@
%%
-module(tar_SUITE).
--export([all/1, borderline/1, atomic/1, long_names/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, borderline/1, atomic/1, long_names/1,
create_long_names/1, bad_tar/1, errors/1, extract_from_binary/1,
extract_from_binary_compressed/1,
extract_from_open_file/1, symlinks/1, open_add_close/1, cooked_compressed/1,
memory/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
-all(suite) -> [borderline, atomic, long_names, create_long_names,
- bad_tar, errors,
- extract_from_binary, extract_from_binary_compressed,
- extract_from_open_file,
- symlinks, open_add_close, cooked_compressed,
- memory].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [borderline, atomic, long_names, create_long_names,
+ bad_tar, errors, extract_from_binary,
+ extract_from_binary_compressed, extract_from_open_file,
+ symlinks, open_add_close, cooked_compressed, memory].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
borderline(doc) ->
["Test creating, listing and extracting one file from an archive",
diff --git a/lib/stdlib/test/timer_SUITE.erl b/lib/stdlib/test/timer_SUITE.erl
index 5f38c91c64..f84c72b0f8 100644
--- a/lib/stdlib/test/timer_SUITE.erl
+++ b/lib/stdlib/test/timer_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,12 @@
%%
-module(timer_SUITE).
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2]).
-export([do_big_test/1]).
-export([big_test/1, collect/3, i_t/3, a_t/2]).
-export([do_nrev/1, internal_watchdog/2]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test suite for timer module. This is a really nasty test it runs a
%% lot of timeouts and then checks in the end if any of them was
@@ -51,7 +51,26 @@
%% amount of load. The test suite should also include tests that test the
%% interface of the timer module.
-all(suite) -> [do_big_test].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [do_big_test].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% ------------------------------------------------------- %%
diff --git a/lib/stdlib/test/timer_simple_SUITE.erl b/lib/stdlib/test/timer_simple_SUITE.erl
index 021a22c61b..852afa1a4d 100644
--- a/lib/stdlib/test/timer_simple_SUITE.erl
+++ b/lib/stdlib/test/timer_simple_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,8 @@
-module(timer_simple_SUITE).
%% external
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
apply_after/1,
send_after1/1,
@@ -49,31 +50,35 @@
timer/4,
timer/5]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(MAXREF, (1 bsl 18)).
-define(REFMARG, 30).
-all(doc) -> "Test of the timer module.";
-all(suite) ->
- [apply_after,
- send_after1,
- send_after2,
- send_after3,
- exit_after1,
- exit_after2,
- kill_after1,
- kill_after2,
- apply_interval,
- send_interval1,
- send_interval2,
- send_interval3,
- send_interval4,
- cancel1,
- cancel2,
- tc,
- unique_refs,
- timer_perf].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [apply_after, send_after1, send_after2, send_after3,
+ exit_after1, exit_after2, kill_after1, kill_after2,
+ apply_interval, send_interval1, send_interval2,
+ send_interval3, send_interval4, cancel1, cancel2, tc,
+ unique_refs, timer_perf].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init_per_testcase(_, Config) when is_list(Config) ->
timer:start(),
@@ -224,11 +229,19 @@ cancel2(Config) when is_list(Config) ->
tc(doc) -> "Test sleep/1 and tc/3.";
tc(suite) -> [];
tc(Config) when is_list(Config) ->
- % This should both sleep and tc
- ?line {Res, ok} = timer:tc(timer, sleep, [500]),
- ?line ok = if
- Res < 500*1000 -> {too_early, Res}; % Too early
- Res > 800*1000 -> {too_late, Res}; % Too much time
+ % This should both sleep and tc/3
+ ?line {Res1, ok} = timer:tc(timer, sleep, [500]),
+ ?line ok = if
+ Res1 < 500*1000 -> {too_early, Res1}; % Too early
+ Res1 > 800*1000 -> {too_late, Res1}; % Too much time
+ true -> ok
+ end,
+
+ % This should both sleep and tc/2
+ ?line {Res2, ok} = timer:tc(fun(T) -> timer:sleep(T) end, [500]),
+ ?line ok = if
+ Res2 < 500*1000 -> {too_early, Res2}; % Too early
+ Res2 > 800*1000 -> {too_late, Res2}; % Too much time
true -> ok
end,
diff --git a/lib/stdlib/test/unicode_SUITE.erl b/lib/stdlib/test/unicode_SUITE.erl
index 141ac64606..9aa800209d 100644
--- a/lib/stdlib/test/unicode_SUITE.erl
+++ b/lib/stdlib/test/unicode_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,11 +18,12 @@
%%
-module(unicode_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
init_per_testcase/2,
- fin_per_testcase/2,
+ end_per_testcase/2,
utf8_illegal_sequences_bif/1,
utf16_illegal_sequences_bif/1,
random_lists/1,
@@ -34,12 +35,32 @@ init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
Dog=?t:timetrap(?t:minutes(20)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog).
-all(suite) ->
- [utf8_illegal_sequences_bif,utf16_illegal_sequences_bif,random_lists,roundtrips,latin1,exceptions].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [utf8_illegal_sequences_bif,
+ utf16_illegal_sequences_bif, random_lists, roundtrips,
+ latin1, exceptions].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
exceptions(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/win32reg_SUITE.erl b/lib/stdlib/test/win32reg_SUITE.erl
index c8cc82f61e..d3984ba67c 100644
--- a/lib/stdlib/test/win32reg_SUITE.erl
+++ b/lib/stdlib/test/win32reg_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,22 +18,34 @@
%%
-module(win32reg_SUITE).
--export([all/1,long/1,evil_write/1]).
--export([ostype/1,fini/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,long/1,evil_write/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-all(suite) ->
- [{conf,ostype,[long,evil_write],fini}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
-ostype(Config) when is_list(Config) ->
+all() ->
+ [long, evil_write].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+init_per_suite(Config) when is_list(Config) ->
case os:type() of
{win32, _} ->
Config;
_ ->
{skip,"Doesn't run on UNIX."}
end.
-fini(Config) when is_list(Config) ->
+end_per_suite(Config) when is_list(Config) ->
Config.
long(doc) -> "Test long keys and entries (OTP-3446).";
diff --git a/lib/stdlib/test/y2k_SUITE.erl b/lib/stdlib/test/y2k_SUITE.erl
index a574d5e36e..d4d0721abf 100644
--- a/lib/stdlib/test/y2k_SUITE.erl
+++ b/lib/stdlib/test/y2k_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,30 +21,38 @@
-module(y2k_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
date_1999_01_01/1, date_1999_02_28/1,
date_1999_09_09/1, date_2000_01_01/1,
date_2000_02_29/1, date_2001_01_01/1,
date_2001_02_29/1, date_2004_02_29/1
]).
-all(doc) ->
- "This is the test suite for year 2000. Eight dates according "
- "to Ericsson Corporate Millennium Test Specification "
- "(LME/DT-98:1097 are tested.";
-
-all(suite) ->
- [date_1999_01_01,
- date_1999_02_28,
- date_1999_09_09,
- date_2000_01_01,
- date_2000_02_29,
- date_2001_01_01,
- date_2001_02_29,
- date_2004_02_29
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [date_1999_01_01, date_1999_02_28, date_1999_09_09,
+ date_2000_01_01, date_2000_02_29, date_2001_01_01,
+ date_2001_02_29, date_2004_02_29].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
date_1999_01_01(doc) ->
"#1 : 1999-01-01: test roll-over from 1998-12-31 to 1999-01-01.";
diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl
index 48b14396c1..d5f2cd52d4 100644
--- a/lib/stdlib/test/zip_SUITE.erl
+++ b/lib/stdlib/test/zip_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,7 +18,8 @@
%%
-module(zip_SUITE).
--export([all/1, borderline/1, atomic/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, borderline/1, atomic/1,
bad_zip/1, unzip_from_binary/1, unzip_to_binary/1,
zip_to_binary/1,
unzip_options/1, zip_options/1, list_dir_options/1, aliases/1,
@@ -26,18 +27,34 @@
compress_control/1,
foldl/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include("test_server_line.hrl").
-include_lib("kernel/include/file.hrl").
-include_lib("stdlib/include/zip.hrl").
-all(suite) -> [borderline, atomic, bad_zip,
- unzip_from_binary, unzip_to_binary,
- zip_to_binary,
- unzip_options, zip_options, list_dir_options, aliases,
- openzip_api, zip_api, unzip_jar,
- compress_control,
- foldl].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [borderline, atomic, bad_zip, unzip_from_binary,
+ unzip_to_binary, zip_to_binary, unzip_options,
+ zip_options, list_dir_options, aliases, openzip_api,
+ zip_api, unzip_jar, compress_control, foldl].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
borderline(doc) ->
["Test creating, listing and extracting one file from an archive "
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index f081bd60e2..c0956030cf 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1,21 +1 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-STDLIB_VSN = 1.17
-
+STDLIB_VSN = 1.17.4
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index a3b842b50b..3f5eb7231e 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -31,6 +31,35 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 1.6.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Miscellaneous updates</p>
+ <p>
+ Own Id: OTP-8976</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Syntax_Tools 1.6.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor changes and clean-ups.</p>
+ <p>
+ Own Id: OTP-8709</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 1.6.5</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl
index 6b0f2034f8..9f6f7d815e 100644
--- a/lib/syntax_tools/src/epp_dodger.erl
+++ b/lib/syntax_tools/src/epp_dodger.erl
@@ -809,6 +809,8 @@ tokens_to_string([{atom,_,A} | Ts]) ->
io_lib:write_atom(A) ++ " " ++ tokens_to_string(Ts);
tokens_to_string([{string, _, S} | Ts]) ->
io_lib:write_string(S) ++ " " ++ tokens_to_string(Ts);
+tokens_to_string([{char, _, C} | Ts]) ->
+ io_lib:write_char(C) ++ " " ++ tokens_to_string(Ts);
tokens_to_string([{float, _, F} | Ts]) ->
float_to_list(F) ++ " " ++ tokens_to_string(Ts);
tokens_to_string([{integer, _, N} | Ts]) ->
diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl
index e2c6976a2b..108ab3bffd 100644
--- a/lib/syntax_tools/src/erl_comment_scan.erl
+++ b/lib/syntax_tools/src/erl_comment_scan.erl
@@ -26,6 +26,7 @@
-export([file/1, join_lines/1, scan_lines/1, string/1]).
+-export_type([comment/0]).
%% =====================================================================
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index c2c72d1ed2..7caf0b3db6 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -50,11 +50,15 @@
-type hook() :: 'none'
| fun((erl_syntax:syntaxTree(), _, _) -> prettypr:document()).
+-type clause_t() :: 'case_expr' | 'cond_expr' | 'fun_expr'
+ | 'if_expr' | 'receive_expr' | 'try_expr'
+ | {'function', prettypr:document()}
+ | {'rule', prettypr:document()}.
-record(ctxt, {prec = 0 :: integer(),
sub_indent = 2 :: non_neg_integer(),
break_indent = 4 :: non_neg_integer(),
- clause = undefined,
+ clause = undefined :: clause_t() | 'undefined',
hook = ?NOHOOK :: hook(),
paper = ?PAPER :: integer(),
ribbon = ?RIBBON :: integer(),
@@ -599,9 +603,8 @@ lay_2(Node, Ctxt) ->
case_expr ->
Ctxt1 = reset_prec(Ctxt),
D1 = lay(erl_syntax:case_expr_argument(Node), Ctxt1),
- D2 = lay_clauses(
- erl_syntax:case_expr_clauses(Node),
- case_expr, Ctxt1),
+ D2 = lay_clauses(erl_syntax:case_expr_clauses(Node),
+ case_expr, Ctxt1),
sep([par([follow(text("case"), D1, Ctxt1#ctxt.sub_indent),
text("of")],
Ctxt1#ctxt.break_indent),
@@ -819,9 +822,8 @@ lay_2(Node, Ctxt) ->
receive_expr ->
Ctxt1 = reset_prec(Ctxt),
- D1 = lay_clauses(
- erl_syntax:receive_expr_clauses(Node),
- receive_expr, Ctxt1),
+ D1 = lay_clauses(erl_syntax:receive_expr_clauses(Node),
+ receive_expr, Ctxt1),
D2 = case erl_syntax:receive_expr_timeout(Node) of
none ->
D1;
diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl
index 145bbc6f37..fc7c515700 100644
--- a/lib/syntax_tools/src/erl_recomment.erl
+++ b/lib/syntax_tools/src/erl_recomment.erl
@@ -163,7 +163,7 @@ recomment_forms_2(C, [N | Ns] = Nodes, Insert) ->
Trailing =
case Ns of
[] -> true;
- [Next | _] -> L < node_min(Next) - 2
+ [Next | _] -> L + Delta < node_min(Next) - 2
end,
if L > Max + 1 ; L =:= Max + 1, not Trailing ->
[N | recomment_forms_2(C, Ns, Insert)];
@@ -215,7 +215,8 @@ comment_delta(Text) ->
%% the source file itself, but have been included by preprocessing. This
%% way, comments will not be inserted into such parts by mistake.
--record(filter, {file = undefined, line = 0 :: integer()}).
+-record(filter, {file = undefined :: file:filename() | 'undefined',
+ line = 0 :: integer()}).
filter_forms(Fs) ->
filter_forms(Fs, false, #filter{}).
@@ -486,7 +487,7 @@ build_tree(Node) ->
%% Include L, while preserving Min =< Max.
tree_node(minpos(L, Min),
- max(L, Max),
+ erlang:max(L, Max),
erl_syntax:type(Node),
erl_syntax:get_attrs(Node),
Subtrees)
@@ -507,7 +508,7 @@ build_list(Ts) ->
build_list([T | Ts], Min, Max, Ack) ->
Node = build_tree(T),
Min1 = minpos(node_min(Node), Min),
- Max1 = max(node_max(Node), Max),
+ Max1 = erlang:max(node_max(Node), Max),
build_list(Ts, Min1, Max1, [Node | Ack]);
build_list([], Min, Max, Ack) ->
list_node(Min, Max, lists:reverse(Ack)).
@@ -518,7 +519,7 @@ build_list_list(Ls) ->
build_list_list([L | Ls], Min, Max, Ack) ->
Node = build_list(L),
Min1 = minpos(node_min(Node), Min),
- Max1 = max(node_max(Node), Max),
+ Max1 = erlang:max(node_max(Node), Max),
build_list_list(Ls, Min1, Max1, [Node | Ack]);
build_list_list([], Min, Max, Ack) ->
{lists:reverse(Ack), Min, Max}.
@@ -721,11 +722,6 @@ tree_node_attrs(#tree{attrs = Attrs}) ->
%% =====================================================================
%% General utility functions
-%% Just the generic "maximum" function
-
-max(X, Y) when X > Y -> X;
-max(_, Y) -> Y.
-
%% Return the least positive integer of X and Y, or zero if none of them
%% are positive. (This is necessary for computing minimum source line
%% numbers, since zero (or negative) numbers may occur, but they
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 9a2967d550..9df5f26454 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -309,6 +309,7 @@
data/1,
is_tree/1]).
+-export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0]).
%% =====================================================================
%% IMPLEMENTATION NOTES:
@@ -1817,7 +1818,7 @@ char_value(Node) ->
%%
%% @see char/1
--spec char_literal(syntaxTree()) -> string().
+-spec char_literal(syntaxTree()) -> nonempty_string().
char_literal(Node) ->
io_lib:write_char(char_value(Node)).
@@ -1907,7 +1908,7 @@ string_value(Node) ->
%%
%% @see string/1
--spec string_literal(syntaxTree()) -> string().
+-spec string_literal(syntaxTree()) -> nonempty_string().
string_literal(Node) ->
io_lib:write_string(string_value(Node)).
diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl
index 5c4e074488..97dfbfd7cd 100644
--- a/lib/syntax_tools/src/erl_syntax_lib.erl
+++ b/lib/syntax_tools/src/erl_syntax_lib.erl
@@ -46,9 +46,7 @@
new_variable_names/2, new_variable_names/3, strip_comments/1,
to_comment/1, to_comment/2, to_comment/3, variables/1]).
-%% =====================================================================
-
--type ordset(X) :: [X]. % XXX: TAKE ME OUT
+-export_type([info_pair/0]).
%% =====================================================================
%% @spec map(Function, Tree::syntaxTree()) -> syntaxTree()
@@ -400,10 +398,7 @@ new_variable_name(N, R, _T, F, S) ->
%% implementation of `sets'.
start_range(S) ->
- max(sets:size(S) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
-
-max(X, Y) when X > Y -> X;
-max(_, Y) -> Y.
+ erlang:max(sets:size(S) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).
%% The previous number might or might not be used to compute the
%% next number to be tried. It is currently not used.
@@ -481,7 +476,7 @@ new_variable_names(0, Names, _, _, _) ->
%% @see annotate_bindings/1
%% @see //stdlib/ordsets
--spec annotate_bindings(erl_syntax:syntaxTree(), ordset(atom())) ->
+-spec annotate_bindings(erl_syntax:syntaxTree(), ordsets:ordset(atom())) ->
erl_syntax:syntaxTree().
annotate_bindings(Tree, Env) ->
@@ -1135,21 +1130,21 @@ collect_attribute(_, {N, V}, Info) ->
%% Abstract datatype for collecting module information.
--record(forms, {module, exports, module_imports, imports, attributes,
- records, errors, warnings, functions, rules}).
+-record(forms, {module = none :: 'none' | {'value', atom()},
+ exports = [] :: [{atom(), arity()}],
+ module_imports = [] :: [atom()],
+ imports = [] :: [{atom(), [{atom(), arity()}]}],
+ attributes = [] :: [{atom(), term()}],
+ records = [] :: [{atom(), [{atom(), field_default()}]}],
+ errors = [] :: [term()],
+ warnings = [] :: [term()],
+ functions = [] :: [{atom(), arity()}],
+ rules = [] :: [{atom(), arity()}]}).
+
+-type field_default() :: 'none' | erl_syntax:syntaxTree().
new_finfo() ->
- #forms{module = none,
- exports = [],
- module_imports = [],
- imports = [],
- attributes = [],
- records = [],
- errors = [],
- warnings = [],
- functions = [],
- rules = []
- }.
+ #forms{}.
finfo_set_module(Name, Info) ->
case Info#forms.module of
diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl
index 021ab18736..1cfdc7234a 100644
--- a/lib/syntax_tools/src/erl_tidy.erl
+++ b/lib/syntax_tools/src/erl_tidy.erl
@@ -935,12 +935,13 @@ hidden_uses_2(Tree, Used) ->
Used
end.
+-type fa() :: {atom(), arity()}.
-type context() :: 'guard_expr' | 'guard_test' | 'normal'.
-record(env, {file :: file:filename(),
- module,
- current,
- imports,
+ module :: atom(),
+ current :: fa(),
+ imports = dict:new() :: dict(),
context = normal :: context(),
verbosity = 1 :: 0 | 1 | 2,
quiet = false :: boolean(),
@@ -951,7 +952,13 @@ hidden_uses_2(Tree, Used) ->
new_guard_tests = true :: boolean(),
old_guard_tests = false :: boolean()}).
--record(st, {varc, used, imported, vars, functions, new_forms, rename}).
+-record(st, {varc :: non_neg_integer(),
+ used = sets:new() :: set(),
+ imported :: set(),
+ vars :: set(),
+ functions :: set(),
+ new_forms = [] :: [erl_syntax:syntaxTree()],
+ rename :: dict()}).
visit_used(Names, Defs, Roots, Imports, Module, Opts) ->
File = proplists:get_value(file, Opts, ""),
diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl
index 702b399615..aa933eb54b 100644
--- a/lib/syntax_tools/src/igor.erl
+++ b/lib/syntax_tools/src/igor.erl
@@ -119,20 +119,16 @@
%% =====================================================================
--type ordset(X) :: [X]. % XXX: TAKE ME OUT
-
-%% =====================================================================
-
%% Data structure for module information
-record(module, {name :: atom(),
vars = none :: [atom()] | 'none',
- functions :: ordset({atom(), arity()}),
- exports :: ordset({atom(), arity()})
- | ordset({{atom(), arity()}, term()}),
- aliases :: ordset({{atom(), arity()},
- {atom(), {atom(), arity()}}}),
- attributes :: ordset({atom(), term()}),
+ functions :: ordsets:ordset({atom(), arity()}),
+ exports :: ordsets:ordset({atom(), arity()})
+ | ordsets:ordset({{atom(), arity()}, term()}),
+ aliases :: ordsets:ordset({{atom(), arity()},
+ {atom(), {atom(), arity()}}}),
+ attributes :: ordsets:ordset({atom(), term()}),
records :: [{atom(), [{atom(), term()}]}]
}).
@@ -149,7 +145,7 @@ default_printer(Tree, Options) ->
-type moduleName() :: atom().
-type functionName() :: {atom(), arity()}.
-type functionPair() :: {functionName(), {moduleName(), functionName()}}.
--type stubDescriptor() :: [{moduleName(), [functionPair()], [attribute()]}].
+-type stubDescriptor() :: {moduleName(), [functionPair()], [attribute()]}.
-type notes() :: 'always' | 'yes' | 'no'.
@@ -209,7 +205,7 @@ parse_transform(Forms, Options) ->
%% @spec merge(Name::atom(), Files::[filename()]) -> [filename()]
%% @equiv merge(Name, Files, [])
--spec merge(atom(), [file:filename()]) -> [file:filename()].
+-spec merge(atom(), [file:filename()]) -> [file:filename(),...].
merge(Name, Files) ->
merge(Name, Files, []).
@@ -343,7 +339,7 @@ merge(Name, Files) ->
{suffix, ?DEFAULT_SUFFIX},
{verbose, false}]).
--spec merge(atom(), [file:filename()], [option()]) -> [file:filename()].
+-spec merge(atom(), [file:filename()], [option()]) -> [file:filename(),...].
merge(Name, Files, Opts) ->
Opts1 = Opts ++ ?DEFAULT_MERGE_OPTS,
@@ -484,7 +480,7 @@ merge_files(Name, Trees, Files, Opts) ->
%%
%% Forms = syntaxTree() | [syntaxTree()]
%%
-%% @type stubDescriptor() = [{ModuleName, Functions, [Attribute]}]
+%% @type stubDescriptor() = {ModuleName, Functions, [Attribute]}
%% ModuleName = atom()
%% Functions = [{FunctionName, {ModuleName, FunctionName}}]
%% FunctionName = {atom(), integer()}
@@ -687,15 +683,15 @@ merge_files(Name, Trees, Files, Opts) ->
%% Data structure for merging environment.
-record(merge, {target :: atom(),
- sources :: ordset(atom()),
- export :: ordset(atom()),
- static :: ordset(atom()),
- safe :: ordset(atom()),
+ sources :: ordsets:ordset(atom()),
+ export :: ordsets:ordset(atom()),
+ static :: ordsets:ordset(atom()),
+ safe :: ordsets:ordset(atom()),
preserved :: boolean(),
no_headers :: boolean(),
notes :: notes(),
redirect :: dict(), % = dict(atom(), atom())
- no_imports :: ordset(atom()),
+ no_imports :: ordsets:ordset(atom()),
options :: [option()]
}).
@@ -1035,7 +1031,12 @@ make_stub(M, Map, Env) ->
%% Removing and/or out-commenting program forms. The returned form
%% sequence tree is not necessarily flat.
--record(filter, {records :: set(), file_attributes, attributes}).
+-type atts() :: 'delete' | 'kill'.
+-type file_atts() :: 'delete' | 'keep' | 'kill'.
+
+-record(filter, {records :: set(),
+ file_attributes :: file_atts(),
+ attributes :: atts()}).
filter_forms(Tree, Env) ->
Forms = erl_syntax:form_list_elements(
@@ -1576,6 +1577,8 @@ alias_expansions_2(Modules, Table) ->
%% ---------------------------------------------------------------------
%% Merging the source code.
+-type map_fun() :: fun(({atom(), integer()}) -> {atom(), integer()}).
+
%% Data structure for code transformation environment.
-record(code, {module :: atom(),
@@ -1586,11 +1589,10 @@ alias_expansions_2(Modules, Table) ->
preserved :: boolean(),
no_headers :: boolean(),
notes :: notes(),
- map, % = ({atom(), int()}) -> {atom(), int()}
- renaming, % = (atom()) -> ({atom(), int()}) ->
- % {atom(), int()}
- expand :: dict(), % = dict({atom(), int()},
- % {atom(), {atom(), int()}})
+ map :: map_fun(),
+ renaming :: fun((atom()) -> map_fun()),
+ expand :: dict(), % = dict({atom(), integer()},
+ % {atom(), {atom(), integer()}})
redirect :: dict() % = dict(atom(), atom())
}).
diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl
index 1868f63e54..c13fa30998 100644
--- a/lib/syntax_tools/src/prettypr.erl
+++ b/lib/syntax_tools/src/prettypr.erl
@@ -48,6 +48,8 @@
nest/2, par/1, par/2, sep/1, text/1, null_text/1, text_par/1,
text_par/2]).
+-export_type([document/0]).
+
%% ---------------------------------------------------------------------
-type deep_string() :: [char() | deep_string()].
diff --git a/lib/syntax_tools/test/Makefile b/lib/syntax_tools/test/Makefile
index 621c76f5a5..3e31bdbd50 100644
--- a/lib/syntax_tools/test/Makefile
+++ b/lib/syntax_tools/test/Makefile
@@ -59,7 +59,7 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
- $(INSTALL_DATA) syntax_tools.dynspec $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) syntax_tools.spec syntax_tools.cover $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
release_docs_spec:
diff --git a/lib/syntax_tools/test/syntax_tools.cover b/lib/syntax_tools/test/syntax_tools.cover
new file mode 100644
index 0000000000..fd30f66cc4
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools.cover
@@ -0,0 +1,2 @@
+{incl_app,syntax_tools,details}.
+
diff --git a/lib/syntax_tools/test/syntax_tools.dynspec b/lib/syntax_tools/test/syntax_tools.dynspec
deleted file mode 100644
index 981cb8175e..0000000000
--- a/lib/syntax_tools/test/syntax_tools.dynspec
+++ /dev/null
@@ -1,5 +0,0 @@
-%% -*- erlang -*-
-%% You can test this file using this command.
-%% file:script("syntax_tools.dynspec", [{'Os',"Unix"}]).
-
-[].
diff --git a/lib/syntax_tools/test/syntax_tools.spec b/lib/syntax_tools/test/syntax_tools.spec
new file mode 100644
index 0000000000..e7ddbf7586
--- /dev/null
+++ b/lib/syntax_tools/test/syntax_tools.spec
@@ -0,0 +1,2 @@
+%% -*- erlang -*-
+{suites,"../syntax_tools_test",all}.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index 16f794683b..fd381f0b25 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -17,17 +17,36 @@
%%
-module(syntax_tools_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
%% Test cases
-export([smoke_test/1]).
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[smoke_test].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%% Read and parse all source in the OTP release.
smoke_test(Config) when is_list(Config) ->
?line Dog = ?t:timetrap(?t:minutes(12)),
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 2ba5eac582..2e23f6aef9 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 1.6.5
+SYNTAX_TOOLS_VSN = 1.6.7
diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml
index b6e0a6cefa..9c62b0fcf6 100644
--- a/lib/test_server/doc/src/notes.xml
+++ b/lib/test_server/doc/src/notes.xml
@@ -32,6 +32,163 @@
<file>notes.xml</file>
</header>
+<section><title>Test_Server 3.4.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Updated the ts*.config files to contain information
+ relevant to testing Erlang/OTP in an open source
+ environment.</p>
+ <p>
+ Own Id: OTP-9017</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Alpha release of Common Test Hooks (CTH). CTHs allow the
+ users of common test to abtract out common behaviours
+ from test suites in a much more elegant and flexible way
+ than was possible before. Note that the addition of this
+ feature may introduce minor changes in the undocumented
+ behaviour of the interface inbetween common_test and
+ test_server.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8851</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Test_Server 3.4.2</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Miscellaneous updates</p>
+ <p>
+ Own Id: OTP-8976</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Test_Server 3.4.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Returning {return_group_result,failed} from end_per_group
+ in a group that is part of a sequence, did not cause the
+ proceeding cases (or groups) to get skipped. This has
+ been fixed.</p>
+ <p>
+ Own Id: OTP-8753 Aux Id: seq11644 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Common Test has been updated to handle start options and
+ test specification terms for test case groups (and test
+ cases in groups). Also, an option named 'label', has been
+ added that associates the test run with a name that
+ Common Test prints in the overview HTML logs.</p>
+ <p>
+ Own Id: OTP-8725 Aux Id: OTP-8727 </p>
+ </item>
+ <item>
+ <p>
+ It is now possible to skip all tests in a suite, or a
+ group, by returning {fail,Reason} from the end_tc/5
+ framework function for init_per_suite, or init_per_group.</p>
+ <p>
+ Own Id: OTP-8805 Aux Id: seq11664 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Test_Server 3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Returning {fail,Reason} from the framework end_tc
+ function was not handled properly by Test Server for all
+ test suite functions.</p>
+ <p>
+ Own Id: OTP-8492 Aux Id: seq11502 </p>
+ </item>
+ <item>
+ <p>
+ If the framework end_tc function would hang and get
+ aborted by Test Server, there was no indication of
+ failure in the logs. This has been fixed.</p>
+ <p>
+ Own Id: OTP-8682 Aux Id: seq11504 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ It is now possible for the Test Server framework end_tc
+ function to change the status of the test case from ok or
+ auto-skipped to failed by returning {fail,Reason}.</p>
+ <p>
+ Own Id: OTP-8495 Aux Id: seq11502 </p>
+ </item>
+ <item>
+ <p>
+ Test Server will now call the end_per_testcase/2 function
+ even if the test case has been terminated explicitly
+ (with abort_current_testcase/1), or after a timetrap
+ timeout. Under these circumstances the return value of
+ end_per_testcase is completely ignored. Therefore the
+ function will not be able to change the reason for test
+ case termination by returning {fail,Reason}, nor will it
+ be able to save data with {save_config,Data}.</p>
+ <p>
+ Own Id: OTP-8500 Aux Id: seq11521 </p>
+ </item>
+ <item>
+ <p>
+ Previously, a repeat property of a test case group
+ specified the number of times the group should be
+ repeated after the main test run. I.e. {repeat,N} would
+ case the group to execute 1+N times. To be consistent
+ with the behaviour of the run_test repeat option, this
+ has been changed. N now specifies the absolute number of
+ executions instead.</p>
+ <p>
+ Own Id: OTP-8689 Aux Id: seq11502 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Test_Server 3.3.6</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/test_server/doc/src/test_server.xml b/lib/test_server/doc/src/test_server.xml
index 6e75425862..78bb922cc5 100644
--- a/lib/test_server/doc/src/test_server.xml
+++ b/lib/test_server/doc/src/test_server.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -167,6 +167,22 @@
</desc>
</func>
<func>
+ <name>adjusted_sleep(MSecs) -> ok</name>
+ <fsummary>Suspens the calling task for a specified time.</fsummary>
+ <type>
+ <v>MSecs = integer() | float() | infinity</v>
+ <d>The default number of milliseconds to sleep</d>
+ </type>
+ <desc>
+ <p>This function suspends the calling process for at least the
+ supplied number of milliseconds. The function behaves the same
+ way as <c>test_server:sleep/1</c>, only <c>MSecs</c>
+ will be multiplied by the 'multiply_timetraps' value, if set,
+ and also automatically scaled up if 'scale_timetraps' is set
+ to true (which it is by default).</p>
+ </desc>
+ </func>
+ <func>
<name>hours(N) -> MSecs</name>
<name>minutes(N) -> MSecs</name>
<name>seconds(N) -> MSecs</name>
diff --git a/lib/test_server/doc/src/test_server_ctrl.xml b/lib/test_server/doc/src/test_server_ctrl.xml
index 8b60849b61..9028a67ecb 100644
--- a/lib/test_server/doc/src/test_server_ctrl.xml
+++ b/lib/test_server/doc/src/test_server_ctrl.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -376,6 +376,31 @@ Optional, if not given the test server controller node
</desc>
</func>
<func>
+ <name>scale_timetraps(Bool) -> ok</name>
+ <fsummary>.</fsummary>
+ <type>
+ <v>Bool = true | false</v>
+ </type>
+ <desc>
+ <p>This function should be called before a test is started.
+ The parameter specifies if test_server should attempt
+ to automatically scale the timetrap value in order to compensate
+ for delays caused by e.g. the cover tool.</p>
+ </desc>
+ </func>
+ <func>
+ <name>get_timetrap_parameters() -> {N,Bool} </name>
+ <fsummary>Read the parameter values that affect timetraps.</fsummary>
+ <type>
+ <v>N = integer() | infinity</v>
+ <v>Bool = true | false</v>
+ </type>
+ <desc>
+ <p>This function may be called to read the values set by
+ <c>multiply_timetraps/1</c> and <c>scale_timetraps/1</c>.</p>
+ </desc>
+ </func>
+ <func>
<name>cover(Application,Analyse) -> ok</name>
<name>cover(CoverFile,Analyse) -> ok</name>
<name>cover(App,CoverFile,Analyse) -> ok</name>
diff --git a/lib/test_server/doc/src/ts.xml b/lib/test_server/doc/src/ts.xml
index f60c79aadd..496ad3667a 100644
--- a/lib/test_server/doc/src/ts.xml
+++ b/lib/test_server/doc/src/ts.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2007</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile
index d55a3a597d..63a585d526 100644
--- a/lib/test_server/src/Makefile
+++ b/lib/test_server/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2010. All Rights Reserved.
+# Copyright Ericsson AB 1996-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -57,7 +57,8 @@ TS_MODULES= \
ts_erl_config \
ts_autoconf_win32 \
ts_autoconf_vxworks \
- ts_install
+ ts_install \
+ ts_install_cth
TARGET_MODULES= $(MODULES:%=$(EBIN)/%)
TS_TARGET_MODULES= $(TS_MODULES:%=$(EBIN)/%)
@@ -71,7 +72,7 @@ C_FILES =
AUTOCONF_FILES = configure.in conf_vars.in
COVER_FILES = cross.cover
PROGRAMS = configure config.sub config.guess install-sh
-CONFIG = ts.config ts.unix.config ts.win32.config ts.vxworks.config
+CONFIG = ts.config ts.unix.config ts.win32.config
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) \
$(APP_TARGET) $(APPUP_TARGET)
@@ -136,10 +137,10 @@ release_tests_spec: opt
$(INSTALL_DIR) $(RELEASE_PATH)/test_server
$(INSTALL_DATA) $(ERL_FILES) $(TS_ERL_FILES) \
$(HRL_FILES) $(INTERNAL_HRL_FILES) $(TS_HRL_FILES) \
- $(TARGET_FILES) $(TS_TARGET_FILES) \
+ $(TS_TARGET_FILES) \
$(AUTOCONF_FILES) $(C_FILES) $(COVER_FILES) $(CONFIG) \
$(RELEASE_PATH)/test_server
- $(INSTALL_PROGRAM) $(PROGRAMS) $(RELEASE_PATH)/test_server
+ $(INSTALL_SCRIPT) $(PROGRAMS) $(RELEASE_PATH)/test_server
release_docs_spec:
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 7db103a4c6..7f0011bd68 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,7 +35,7 @@
-export([fail/0,fail/1,format/1,format/2,format/3]).
-export([capture_start/0,capture_stop/0,capture_get/0]).
-export([messages_get/0]).
--export([hours/1,minutes/1,seconds/1,sleep/1,timecall/3]).
+-export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]).
-export([timetrap_scale_factor/0,timetrap/1,timetrap_cancel/1]).
-export([m_out_of_n/3,do_times/4,do_times/2]).
-export([call_crash/3,call_crash/4,call_crash/5]).
@@ -89,14 +89,14 @@ init(Host,Port,Starter) ->
global:register_name(?MODULE,self()),
process_flag(trap_exit,true),
test_server_sup:cleanup_crash_dumps(),
- case gen_tcp:connect(Host,Port, [binary,
- {reuseaddr,true},
+ case gen_tcp:connect(Host,Port, [binary,
+ {reuseaddr,true},
{packet,2}]) of
- {ok,MainSock} ->
+ {ok,MainSock} ->
Starter ! {self(),started},
request(MainSock,{target_info,init_target_info()}),
loop(#state{controller={Host,MainSock}});
- Error ->
+ Error ->
Starter ! {self(),{error,
{could_not_contact_controller,Error}}}
end.
@@ -127,7 +127,7 @@ loop(#state{controller={_,MainSock}} = State) ->
halt();
{'EXIT',Pid,Reason} ->
case lists:keysearch(Pid,1,State#state.jobs) of
- {value,{Pid,Name}} ->
+ {value,{Pid,Name}} ->
case Reason of
normal -> ignore;
_other -> request(MainSock,{job_proc_killed,Name,Reason})
@@ -157,14 +157,14 @@ init_purify() ->
job(Host,Port,Starter) ->
process_flag(trap_exit,true),
init_purify(),
- case gen_tcp:connect(Host,Port, [binary,
- {reuseaddr,true},
+ case gen_tcp:connect(Host,Port, [binary,
+ {reuseaddr,true},
{packet,4},
{active,false}]) of
{ok,JobSock} ->
Starter ! {self(),started},
job(JobSock);
- Error ->
+ Error ->
Starter ! {self(),{error,
{could_not_contact_controller,Error}}}
end.
@@ -192,7 +192,7 @@ get_jobdir() ->
true ->
{ok,Cwd} = file:get_cwd(),
Cwd ++ "/" ++ Basename;
- false ->
+ false ->
filename:absname(Basename)
end.
@@ -216,7 +216,7 @@ send_privdir(JobDir,JobSock) ->
del_dir(Dir) ->
case file:read_file_info(Dir) of
- {ok,#file_info{type=directory}} ->
+ {ok,#file_info{type=directory}} ->
{ok,Cont} = file:list_dir(Dir),
lists:foreach(fun(F) -> del_dir(filename:join(Dir,F)) end, Cont),
ok = file:del_dir(Dir);
@@ -227,7 +227,7 @@ del_dir(Dir) ->
catch file:delete(Dir),
ok
end.
-
+
%%
%% Receive and decode request on job socket
%%
@@ -237,7 +237,7 @@ job_loop(JobSock) ->
ok -> job_loop(JobSock);
{stop,R} -> R
end.
-
+
decode_job({{beam,Mod,Which},Beam}) ->
% FIXME, shared directory structure on host and target required,
% "Library beams" are not loaded from HOST... /Patrik
@@ -254,7 +254,7 @@ decode_job({{datadir,Tarfile0},Archive}) ->
ok = erl_tar:extract(Tarfile,[compressed,{cwd,JobDir}]),
ok = file:delete(Tarfile),
ok;
-decode_job({test_case,Case}) ->
+decode_job({test_case,Case}) ->
Result = run_test_case_apply(Case),
JobSock = get(test_server_job_sock),
request(JobSock,{test_case_result,Result}),
@@ -266,11 +266,11 @@ decode_job({test_case,Case}) ->
request(JobSock,{{crash_dumps,filename:basename(TarFile)},TarBin})
end,
ok;
-decode_job({sync_apply,{M,F,A}}) ->
+decode_job({sync_apply,{M,F,A}}) ->
R = apply(M,F,A),
request(get(test_server_job_sock),{sync_result,R}),
ok;
-decode_job(job_done) ->
+decode_job(job_done) ->
{stop,stopped}.
%%
@@ -282,9 +282,9 @@ decode_job(job_done) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% cover_compile({App,Include,Exclude,Cross}) ->
+%% cover_compile({App,Include,Exclude,Cross}) ->
%% {ok,AnalyseModules} | {error,Reason}
-%%
+%%
%% App = atom() , name of application to be compiled
%% Exclude = [atom()], list of modules to exclude
%% Include = [atom()], list of modules outside of App that should be included
@@ -293,7 +293,7 @@ decode_job(job_done) ->
%% in the cover compilation, but that shall not be part of
%% the cover analysis for this application.
%%
-%% Cover compile the given application. Return {ok,AnalyseMods} if application
+%% Cover compile the given application. Return {ok,AnalyseMods} if application
%% is found, else {error,application_not_found}.
cover_compile({none,_Exclude,Include,Cross}) ->
@@ -330,7 +330,7 @@ cover_compile({App,all,Include,Cross}) ->
end;
cover_compile({App,Exclude,Include,Cross}) ->
case code:lib_dir(App) of
- {error,bad_name} ->
+ {error,bad_name} ->
case Include++Cross of
[] ->
io:format("\nWARNING: Can't find lib_dir for \'~w\'\n"
@@ -366,7 +366,7 @@ cover_compile({App,Exclude,Include,Cross}) ->
{ok,AnalyseMods}
end
end.
-
+
module_names(Beams) ->
[list_to_atom(filename:basename(filename:rootname(Beam))) || Beam <- Beams].
@@ -380,11 +380,11 @@ do_cover_compile1([Dont|Rest]) when Dont=:=cover;
Dont=:=test_server_ctrl ->
do_cover_compile1(Rest);
do_cover_compile1([M|Rest]) ->
- case {code:is_sticky(M),code:is_loaded(M)} of
+ case {code:is_sticky(M),code:is_loaded(M)} of
{true,_} ->
code:unstick_mod(M),
case cover:compile_beam(M) of
- {ok,_} ->
+ {ok,_} ->
ok;
Error ->
io:fwrite("\nWARNING: Could not cover compile ~w: ~p\n",
@@ -402,7 +402,7 @@ do_cover_compile1([M|Rest]) ->
end;
{false,_} ->
case cover:compile_beam(M) of
- {ok,_} ->
+ {ok,_} ->
ok;
Error ->
io:fwrite("\nWARNING: Could not cover compile ~w: ~p\n",
@@ -415,14 +415,14 @@ do_cover_compile1([]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% cover_analyse(Analyse,Modules) -> [{M,{Cov,NotCov,Details}}]
-%%
+%%
%% Analyse = {details,Dir} | details | {overview,void()} | overview
%% Modules = [atom()], the modules to analyse
%%
%% Cover analysis. If this is a remote target, analyse_to_file can not be used.
%% In that case the analyse level 'line' is used instead if Analyse==details.
%%
-%% If this is a local target, the test directory is given
+%% If this is a local target, the test directory is given
%% (Analyse=={details,Dir}) and analyse_to_file can be used directly.
%%
%% If Analyse==overview | {overview,Dir} analyse_to_file is not used, only
@@ -432,12 +432,12 @@ do_cover_compile1([]) ->
%% all.coverdata in that directory.
cover_analyse(Analyse,Modules) ->
io:fwrite("Cover analysing...\n",[]),
- DetailsFun =
+ DetailsFun =
case Analyse of
{details,Dir} ->
case cover:export(filename:join(Dir,"all.coverdata")) of
ok ->
- fun(M) ->
+ fun(M) ->
OutFile = filename:join(Dir,
atom_to_list(M) ++
".COVER.html"),
@@ -451,7 +451,7 @@ cover_analyse(Analyse,Modules) ->
Error ->
fun(_) -> Error end
end;
- details ->
+ details ->
fun(M) ->
case cover:analyse(M,line) of
{ok,Lines} ->
@@ -470,7 +470,7 @@ cover_analyse(Analyse,Modules) ->
overview ->
fun(_) -> undefined end
end,
- R = lists:map(
+ R = pmap(
fun(M) ->
case cover:analyse(M,module) of
{ok,{M,{Cov,NotCov}}} ->
@@ -486,10 +486,23 @@ cover_analyse(Analyse,Modules) ->
stick_all_sticky(node(),Sticky),
R.
+pmap(Fun,List) ->
+ Collector = self(),
+ Pids = lists:map(fun(E) ->
+ spawn(fun() ->
+ Collector ! {res,self(),Fun(E)}
+ end)
+ end, List),
+ lists:map(fun(Pid) ->
+ receive
+ {res,Pid,Res} ->
+ Res
+ end
+ end, Pids).
unstick_all_sticky(Node) ->
lists:filter(
- fun(M) ->
+ fun(M) ->
case code:is_sticky(M) of
true ->
rpc:call(Node,code,unstick_mod,[M]),
@@ -502,24 +515,24 @@ unstick_all_sticky(Node) ->
stick_all_sticky(Node,Sticky) ->
lists:foreach(
- fun(M) ->
+ fun(M) ->
rpc:call(Node,code,stick_mod,[M])
end,
Sticky).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_case_apply(Mod,Func,Args,Name,RunInit,MultiplyTimetrap) ->
+%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData) ->
%% {Time,Value,Loc,Opts,Comment} | {died,Reason,unknown,Comment}
-%%
+%%
%% Time = float() (seconds)
%% Value = term()
%% Loc = term()
%% Comment = string()
%% Reason = term()
%%
-%% Spawns off a process (case process) that actually runs the test suite.
-%% The case process will have the job process as group leader, which makes
+%% Spawns off a process (case process) that actually runs the test suite.
+%% The case process will have the job process as group leader, which makes
%% it possible to capture all it's output from io:format/2, etc.
%%
%% The job process then sits down and waits for news from the case process.
@@ -535,40 +548,43 @@ stick_all_sticky(Node,Sticky) ->
%% called or the comment given by the return value {comment,Comment} from
%% a test case.
%%
-%% {died,Reason,unknown,Comment} is returned if the test case was killed
+%% {died,Reason,unknown,Comment} is returned if the test case was killed
%% by some other process. Reason is the kill reason provided.
%%
-%% MultiplyTimetrap indicates a possible extension of all timetraps
-%% Timetraps will be multiplied by this integer. If it is infinity, no
-%% timetraps will be started at all.
+%% TimetrapData = {MultiplyTimetrap,ScaleTimetrap}, which indicates a
+%% possible extension of all timetraps. Timetraps will be multiplied by
+%% MultiplyTimetrap. If it is infinity, no timetraps will be started at all.
+%% ScaleTimetrap indicates if test_server should attemp to automatically
+%% compensate timetraps for runtime delays introduced by e.g. tools like
+%% cover.
-run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,MultiplyTimetrap}) ->
+run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) ->
purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]),
case os:getenv("TS_RUN_VALGRIND") of
- false ->
+ false ->
ok;
_ ->
os:putenv("VALGRIND_LOGFILE_INFIX",atom_to_list(Mod)++"."++
atom_to_list(Func)++"-")
end,
test_server_h:testcase({Mod,Func,1}),
- ProcBef = erlang:system_info(process_count),
- Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap),
+ ProcBef = erlang:system_info(process_count),
+ Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData),
ProcAft = erlang:system_info(process_count),
purify_new_leaks(),
DetFail = get(test_server_detected_fail),
{Result,DetFail,ProcBef,ProcAft}.
-
-run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap) ->
+
+run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
case get(test_server_job_dir) of
undefined ->
%% i'm a local target
- do_run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap);
+ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData);
JobDir ->
%% i'm a remote target
case Args of
[Config] when is_list(Config) ->
- {value,{data_dir,HostDataDir}} =
+ {value,{data_dir,HostDataDir}} =
lists:keysearch(data_dir, 1, Config),
DataBase = filename:basename(HostDataDir),
TargetDataDir = filename:join(JobDir, DataBase),
@@ -578,18 +594,18 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap) ->
Config2 = lists:keyreplace(priv_dir, 1, Config1,
{priv_dir,TargetPrivDir}),
do_run_test_case_apply(Mod, Func, [Config2], Name, RunInit,
- MultiplyTimetrap);
+ TimetrapData);
_other ->
do_run_test_case_apply(Mod, Func, Args, Name, RunInit,
- MultiplyTimetrap)
+ TimetrapData)
end
end.
-do_run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap) ->
+do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) ->
{ok,Cwd} = file:get_cwd(),
Args2Print = case Args of
- [Args1] when is_list(Args1) ->
+ [Args1] when is_list(Args1) ->
lists:keydelete(tc_group_result, 1, Args1);
- _ ->
+ _ ->
Args
end,
print(minor, "Test case started with:\n~s:~s(~p)\n", [Mod,Func,Args2Print]),
@@ -600,16 +616,16 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap) ->
OldGLeader = group_leader(),
%% Set ourself to group leader for the spawned process
group_leader(self(),self()),
- Pid =
+ Pid =
spawn_link(
- fun() ->
- run_test_case_eval(Mod, Func, Args, Name, Ref,
- RunInit, MultiplyTimetrap,
+ fun() ->
+ run_test_case_eval(Mod, Func, Args, Name, Ref,
+ RunInit, TimetrapData,
TCCallback)
end),
group_leader(OldGLeader, self()),
put(test_server_detected_fail, []),
- run_test_case_msgloop(Ref, Pid, false, false, "").
+ run_test_case_msgloop(Ref, Pid, false, false, "", undefined).
%% Ugly bug (pre R5A):
%% If this process (group leader of the test case) terminates before
@@ -620,7 +636,7 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, MultiplyTimetrap) ->
%% A test case is known to have failed if it returns {'EXIT', _} tuple,
%% or sends a message {failed, File, Line} to it's group_leader
%%
-run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment) ->
+run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) ->
%% NOTE: Keep job_proxy_msgloop/0 up to date when changes
%% are made in this function!
{Timeout,ReturnValue} =
@@ -641,13 +657,13 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment) ->
receive
{'DOWN', Mon, process, Pid, _} ->
Comment
- after 10000 ->
+ after 10000 ->
%% Pid is probably trapping exits, hit it harder...
exit(Pid, kill),
%% here's the only place we know Reason, so we save
%% it as a comment, potentially replacing user data
Error = lists:flatten(io_lib:format("Aborted: ~p",[Reason])),
- Error1 = lists:flatten([string:strip(S,left) ||
+ Error1 = lists:flatten([string:strip(S,left) ||
S <- string:tokens(Error,[$\n])]),
if length(Error1) > 63 ->
string:substr(Error1,1,60) ++ "...";
@@ -655,149 +671,224 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment) ->
Error1
end
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,NewComment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,NewComment,CurrConf);
{io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}}
when is_list(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}}
when is_atom(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,Bytes}} ->
run_test_case_msgloop_io(
ReplyAs,CaptureStdout,Bytes,From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}}
when is_list(Format) ->
Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)),
run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}}
when is_list(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}}
when is_atom(Format) ->
Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)),
run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}}
when is_atom(Format) ->
Msg = (catch io_lib:Func(Format,Args)),
run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,unicode,Bytes}} ->
run_test_case_msgloop_io(
ReplyAs,CaptureStdout,unicode_to_latin1(Bytes),From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{io_request,From,ReplyAs,{put_chars,latin1,Bytes}} ->
run_test_case_msgloop_io(
ReplyAs,CaptureStdout,Bytes,From,put_chars),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
IoReq when element(1, IoReq) == io_request ->
%% something else, just pass it on
group_leader() ! IoReq,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{structured_io,ClientPid,Msg} ->
output(Msg, ClientPid),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{capture,NewCapture} ->
- run_test_case_msgloop(Ref,Pid,NewCapture,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,NewCapture,Terminate,Comment,CurrConf);
{sync_apply,From,MFA} ->
sync_local_or_remote_apply(false,From,MFA),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{sync_apply_proxy,Proxy,From,MFA} ->
sync_local_or_remote_apply(Proxy,From,MFA),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{printout,Detail,Format,Args} ->
print(Detail,Format,Args),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{comment,NewComment} ->
Terminate1 =
case Terminate of
- {true,{Time,Value,Loc,Opts,_OldComment}} ->
+ {true,{Time,Value,Loc,Opts,_OldComment}} ->
{true,{Time,Value,mod_loc(Loc),Opts,NewComment}};
Other ->
Other
end,
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1,NewComment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1,NewComment,CurrConf);
+ {set_curr_conf,NewCurrConf} ->
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,NewCurrConf);
{'EXIT',Pid,{Ref,Time,Value,Loc,Opts}} ->
RetVal = {Time/1000000,Value,mod_loc(Loc),Opts,Comment},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment,undefined);
{'EXIT',Pid,Reason} ->
case Reason of
{timetrap_timeout,TVal,Loc} ->
%% convert Loc to form that can be formatted
- Loc1 = mod_loc(Loc),
- {Mod,Func} = get_mf(Loc1),
- %% The framework functions mustn't execute on this
- %% group leader process or io will cause deadlock,
- %% so we spawn a dedicated process for the operation
- %% and let the group leader go back to handle io.
- spawn_fw_call(Mod,Func,Pid,{timetrap_timeout,TVal},
- Loc1,self(),Comment),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ case mod_loc(Loc) of
+ {FwMod,FwFunc,framework} ->
+ %% timout during framework call
+ spawn_fw_call(FwMod,FwFunc,Pid,
+ {framework_error,{timetrap,TVal}},
+ unknown,self(),Comment),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ Comment,undefined);
+ Loc1 ->
+ {Mod,Func} = get_mf(Loc1),
+ %% call end_per_testcase on a separate process,
+ %% only so that the user has a chance to clean up
+ %% after init_per_testcase, even after a timetrap timeout
+ NewCurrConf =
+ case CurrConf of
+ {{Mod,Func},Conf} ->
+ EndConfPid =
+ call_end_conf(Mod,Func,Pid,
+ {timetrap_timeout,TVal},
+ Loc1,[{tc_status,
+ {failed,
+ timetrap_timeout}}|Conf],
+ TVal),
+ {EndConfPid,{Mod,Func},Conf};
+ _ ->
+ %% The framework functions mustn't execute on this
+ %% group leader process or io will cause deadlock,
+ %% so we spawn a dedicated process for the operation
+ %% and let the group leader go back to handle io.
+ spawn_fw_call(Mod,Func,Pid,{timetrap_timeout,TVal},
+ Loc1,self(),Comment),
+ undefined
+ end,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ Comment,NewCurrConf)
+ end;
{timetrap_timeout,TVal,Loc,InitOrEnd} ->
- Loc1 = mod_loc(Loc),
- {Mod,_Func} = get_mf(Loc1),
- spawn_fw_call(Mod,InitOrEnd,Pid,{timetrap_timeout,TVal},
- Loc1,self(),Comment),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
- {testcase_aborted,Reason,Loc} ->
- Loc1 = mod_loc(Loc),
- {Mod,Func} = get_mf(Loc1),
- spawn_fw_call(Mod,Func,Pid,{testcase_aborted,Reason},
- Loc1,self(),Comment),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
- killed ->
+ case mod_loc(Loc) of
+ {FwMod,FwFunc,framework} ->
+ %% timout during framework call
+ spawn_fw_call(FwMod,FwFunc,Pid,
+ {framework_error,{timetrap,TVal}},
+ unknown,self(),Comment);
+ Loc1 ->
+ {Mod,_Func} = get_mf(Loc1),
+ spawn_fw_call(Mod,InitOrEnd,Pid,{timetrap_timeout,TVal},
+ Loc1,self(),Comment)
+ end,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
+ {testcase_aborted,AbortReason,AbortLoc} ->
+ ErrorMsg = {testcase_aborted,AbortReason},
+ case mod_loc(AbortLoc) of
+ {FwMod,FwFunc,framework} ->
+ %% abort during framework call
+ spawn_fw_call(FwMod,FwFunc,Pid,
+ {framework_error,ErrorMsg},
+ unknown,self(),Comment),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ Comment,undefined);
+ Loc1 ->
+ {Mod,Func} = get_mf(Loc1),
+ %% call end_per_testcase on a separate process, only so
+ %% that the user has a chance to clean up after init_per_testcase,
+ %% even after abortion
+ NewCurrConf =
+ case CurrConf of
+ {{Mod,Func},Conf} ->
+ TVal = case lists:keysearch(default_timeout,1,Conf) of
+ {value,{default_timeout,Tmo}} -> Tmo;
+ _ -> ?DEFAULT_TIMETRAP_SECS*1000
+ end,
+ EndConfPid =
+ call_end_conf(Mod,Func,Pid,ErrorMsg,
+ Loc1,
+ [{tc_status,{failed,ErrorMsg}}|Conf],
+ TVal),
+ {EndConfPid,{Mod,Func},Conf};
+ _ ->
+ spawn_fw_call(Mod,Func,Pid,ErrorMsg,
+ Loc1,self(),Comment),
+ undefined
+ end,
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,
+ Comment,NewCurrConf)
+ end;
+ killed ->
%% result of an exit(TestCase,kill) call, which is the
- %% only way to abort a testcase process that traps exits
+ %% only way to abort a testcase process that traps exits
%% (see abort_current_testcase)
spawn_fw_call(undefined,undefined,Pid,testcase_aborted_or_killed,
unknown,self(),Comment),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
{fw_error,{FwMod,FwFunc,FwError}} ->
spawn_fw_call(FwMod,FwFunc,Pid,{framework_error,FwError},
unknown,self(),Comment),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
- _ ->
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
+ _Other ->
%% the testcase has terminated because of Reason (e.g. an exit
%% because a linked process failed)
spawn_fw_call(undefined,undefined,Pid,Reason,
unknown,self(),Comment),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment)
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf)
+ end;
+ {EndConfPid,{call_end_conf,Data,_Result}} ->
+ case CurrConf of
+ {EndConfPid,{Mod,Func},_Conf} ->
+ {_Mod,_Func,TCPid,TCExitReason,Loc} = Data,
+ spawn_fw_call(Mod,Func,TCPid,TCExitReason,Loc,self(),Comment),
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,undefined);
+ _ ->
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf)
end;
{_FwCallPid,fw_notify_done,RetVal} ->
%% the framework has been notified, we're finished
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment,undefined);
{'EXIT',_FwCallPid,{fw_notify_done,Func,Error}} ->
%% a framework function failed
CB = os:getenv("TEST_SERVER_FRAMEWORK"),
Loc = case CB of
- false ->
+ FW when FW =:= false; FW =:= "undefined" ->
{test_server,Func};
- _ ->
+ _ ->
{list_to_atom(CB),Func}
end,
RetVal = {died,{framework_error,Loc,Error},Loc,"Framework error"},
- run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment,undefined);
{failed,File,Line} ->
- put(test_server_detected_fail,
+ put(test_server_detected_fail,
[{File, Line}| get(test_server_detected_fail)]),
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
_Other when not is_tuple(_Other) ->
%% ignore anything not generated by test server
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment);
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf);
_Other when element(1, _Other) /= 'EXIT',
element(1, _Other) /= started,
element(1, _Other) /= finished,
element(1, _Other) /= print ->
%% ignore anything not generated by test server
- run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment)
+ run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf)
after Timeout ->
ReturnValue
end.
@@ -819,15 +910,45 @@ run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func) ->
output(Msg,Sender) ->
local_or_remote_apply({test_server_ctrl,output,[Msg,Sender]}).
+call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) ->
+ Starter = self(),
+ Data = {Mod,Func,TCPid,TCExitReason,Loc},
+ EndConfProc =
+ fun() ->
+ Supervisor = self(),
+ EndConfApply =
+ fun() ->
+ case catch apply(Mod,end_per_testcase,[Func,Conf]) of
+ {'EXIT',Why} ->
+ group_leader() ! {printout,12,
+ "ERROR! ~p:end_per_testcase(~p, ~p)"
+ " crashed!\n\tReason: ~p\n",
+ [Mod,Func,Conf,Why]};
+ _ ->
+ ok
+ end,
+ Supervisor ! {self(),end_conf}
+ end,
+ Pid = spawn_link(EndConfApply),
+ receive
+ {Pid,end_conf} ->
+ Starter ! {self(),{call_end_conf,Data,ok}};
+ {'EXIT',Pid,Reason} ->
+ Starter ! {self(),{call_end_conf,Data,{error,Reason}}}
+ after TVal ->
+ Starter ! {self(),{call_end_conf,Data,{error,timeout}}}
+ end
+ end,
+ spawn_link(EndConfProc).
+
spawn_fw_call(Mod,{init_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why,
Loc,SendTo,Comment) ->
FwCall =
fun() ->
Skip = {skip,{failed,{Mod,init_per_testcase,Why}}},
- %% if init_per_testcase fails, the test case
+ %% if init_per_testcase fails, the test case
%% should be skipped
- case catch test_server_sup:framework_call(
- end_tc,[?pl2a(Mod),Func,{Pid,Skip,[[]]}]) of
+ case catch do_end_tc_call(Mod,Func,{Pid,Skip,[[]]},Why) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
_ ->
@@ -838,6 +959,7 @@ spawn_fw_call(Mod,{init_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why,
{TVal/1000,Skip,Loc,[],Comment}}
end,
spawn_link(FwCall);
+
spawn_fw_call(Mod,{end_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why,
Loc,SendTo,_Comment) ->
FwCall =
@@ -845,11 +967,9 @@ spawn_fw_call(Mod,{end_per_testcase,Func},Pid,{timetrap_timeout,TVal}=Why,
Conf = [{tc_status,ok}],
%% if end_per_testcase fails, the test case should be
%% reported successful with a warning printed as comment
- case catch test_server_sup:framework_call(end_tc,
- [?pl2a(Mod),Func,
- {Pid,
- {failed,{Mod,end_per_testcase,Why}},
- [Conf]}]) of
+ case catch do_end_tc_call(Mod,Func,{Pid,
+ {failed,{Mod,end_per_testcase,Why}},
+ [Conf]}, Why) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
_ ->
@@ -869,7 +989,7 @@ spawn_fw_call(FwMod,FwFunc,_Pid,{framework_error,FwError},_,SendTo,_Comment) ->
fun() ->
test_server_sup:framework_call(report, [framework_error,
{{FwMod,FwFunc},FwError}]),
- Comment =
+ Comment =
lists:flatten(
io_lib:format("<font color=\"red\">"
"WARNING! ~w:~w failed!</font>", [FwMod,FwFunc])),
@@ -891,9 +1011,7 @@ spawn_fw_call(Mod,Func,Pid,Error,Loc,SendTo,Comment) ->
ok
end,
Conf = [{tc_status,{failed,timetrap_timeout}}],
- case catch test_server_sup:framework_call(end_tc,
- [?pl2a(Mod),Func,
- {Pid,Error,[Conf]}]) of
+ case catch do_end_tc_call(Mod,Func,{Pid,Error,[Conf]},Error) of
{'EXIT',FwEndTCErr} ->
exit({fw_notify_done,end_tc,FwEndTCErr});
_ ->
@@ -953,32 +1071,33 @@ job_proxy_msgloop() ->
%% A test case is known to have failed if it returns {'EXIT', _} tuple,
%% or sends a message {failed, File, Line} to it's group_leader
-run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit,
- MultiplyTimetrap, TCCallback) ->
- put(test_server_multiply_timetraps,MultiplyTimetrap),
+run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit,
+ TimetrapData, TCCallback) ->
+ put(test_server_multiply_timetraps,TimetrapData),
+
{{Time,Value},Loc,Opts} =
case test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0],
- {ok,Args0}) of
+ {ok, Args0}) of
{ok,Args} ->
run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback);
Error = {error,_Reason} ->
- test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,{Error,Args0}]),
- {{0,{skip,{failed,Error}}},{Mod,Func},[]};
+ NewResult = do_end_tc_call(Mod,Func,{Error,Args0},
+ {skip,{failed,Error}}),
+ {{0,NewResult},{Mod,Func},[]};
{fail,Reason} ->
[Conf] = Args0,
Conf1 = [{tc_status,{failed,Reason}} | Conf],
fw_error_notify(Mod, Func, Conf, Reason),
- test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,
- {{error,Reason},[Conf1]}]),
- {{0,{failed,Reason}},{Mod,Func},[]};
+ NewResult = do_end_tc_call(Mod,Func, {{error,Reason},[Conf1]},
+ {fail, Reason}),
+ {{0,NewResult},{Mod,Func},[]};
Skip = {skip,_Reason} ->
- test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,{Skip,Args0}]),
- {{0,Skip},{Mod,Func},[]};
+ NewResult = do_end_tc_call(Mod,Func,{Skip,Args0},Skip),
+ {{0,NewResult},{Mod,Func},[]};
{auto_skip,Reason} ->
- test_server_sup:framework_call(end_tc,[?pl2a(Mod),
- Func,
- {{skip,Reason},Args0}]),
- {{0,{skip,{fw_auto_skip,Reason}}},{Mod,Func},[]}
+ NewResult = do_end_tc_call(Mod, Func, {{skip,Reason},Args0},
+ {skip, {fw_auto_skip,Reason}}),
+ {{0,NewResult},{Mod,Func},[]}
end,
exit({Ref,Time,Value,Loc,Opts}).
@@ -992,18 +1111,20 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
Skip = {skip,Reason} ->
Line = get_loc(),
Conf = [{tc_status,{skipped,Reason}}],
- test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,{Skip,[Conf]}]),
- {{0,{skip,Reason}},Line,[]};
+ NewRes = do_end_tc_call(Mod,Func,{Skip,[Conf]}, Skip),
+ {{0,NewRes},Line,[]};
{skip_and_save,Reason,SaveCfg} ->
Line = get_loc(),
Conf = [{tc_status,{skipped,Reason}},{save_config,SaveCfg}],
- test_server_sup:framework_call(end_tc,[?pl2a(Mod),Func,
- {{skip,Reason},[Conf]}]),
- {{0,{skip,Reason}},Line,[]};
+ NewRes = do_end_tc_call(Mod, Func, {{skip, Reason}, [Conf]},
+ {skip, Reason}),
+ {{0,NewRes},Line,[]};
{ok,NewConf} ->
put(test_server_init_or_end_conf,undefined),
%% call user callback function if defined
NewConf1 = user_callback(TCCallback, Mod, Func, init, NewConf),
+ %% save current state in controller loop
+ group_leader() ! {set_curr_conf,{{Mod,Func},NewConf1}},
put(test_server_loc, {Mod,Func}),
%% execute the test case
{{T,Return},Loc} = {ts_tc(Mod, Func, [NewConf1]),get_loc()},
@@ -1025,6 +1146,8 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
_ ->
{[{tc_status,ok}|NewConf1],Return,ok}
end,
+ %% clear current state in controller loop
+ group_leader() ! {set_curr_conf,undefined},
%% call user callback function if defined
EndConf1 = user_callback(TCCallback, Mod, Func, 'end', EndConf),
{FWReturn1,TSReturn1,EndConf2} =
@@ -1036,16 +1159,16 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
{{error,ReasonToFail},{failed,ReasonToFail},EndConf1};
{failed,{_,end_per_testcase,_}} = Failure -> % unexpected termination
{Failure,TSReturn,EndConf1};
- _ ->
+ _ ->
{FWReturn,TSReturn,EndConf1}
end,
- case test_server_sup:framework_call(end_tc, [?pl2a(Mod), Func,
- {FWReturn1,[EndConf2]}]) of
- {fail,Reason} ->
- fw_error_notify(Mod, Func, EndConf2, Reason),
- {{T,{failed,Reason}},{Mod,Func},[]};
- _ ->
- {{T,TSReturn1},Loc,[]}
+ put(test_server_init_or_end_conf,undefined),
+ case do_end_tc_call(Mod, Func, {FWReturn1,[EndConf2]}, TSReturn1) of
+ {failed,Reason} = NewReturn ->
+ fw_error_notify(Mod,Func,EndConf2, Reason),
+ {{T,NewReturn},{Mod,Func},[]};
+ NewReturn ->
+ {{T,NewReturn},Loc,[]}
end
end;
skip_init ->
@@ -1063,11 +1186,37 @@ run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback) ->
{{T,Return},Loc} = {ts_tc(Mod, Func, Args2),get_loc()},
%% call user callback function if defined
Return1 = user_callback(TCCallback, Mod, Func, 'end', Return),
- {Return2,Opts} = process_return_val([Return1], Mod,Func,Args1, Loc, Return1),
+ {Return2,Opts} = process_return_val([Return1], Mod, Func,
+ Args1, Loc, Return1),
{{T,Return2},Loc,Opts}
end.
-%% the return value is a list and we have to check if it contains
+do_end_tc_call(M,F,Res,Return) ->
+ Ref = make_ref(),
+ case test_server_sup:framework_call(
+ end_tc, [?pl2a(M),F,Res], Ref) of
+ {fail,FWReason} ->
+ {failed,FWReason};
+ Ref ->
+ case test_server_sup:framework_call(
+ end_tc, [?pl2a(M),F,Res, Return], ok) of
+ {fail,FWReason} ->
+ {failed,FWReason};
+ ok ->
+ case Return of
+ {fail,Reason} ->
+ {failed,Reason};
+ Return ->
+ Return
+ end;
+ NewReturn ->
+ NewReturn
+ end;
+ _ ->
+ Return
+ end.
+
+%% the return value is a list and we have to check if it contains
%% the result of an end conf case or if it's a Config list
process_return_val([Return], M,F,A, Loc, Final) when is_list(Return) ->
ReturnTags = [skip,skip_and_save,save_config,comment,return_group_result],
@@ -1081,25 +1230,34 @@ process_return_val([Return], M,F,A, Loc, Final) when is_list(Return) ->
end, Return) of
true -> % must be return value from end conf case
process_return_val1(Return, M,F,A, Loc, Final, []);
- false -> % must be Config value from init conf case
- test_server_sup:framework_call(end_tc, [?pl2a(M),F,{ok,A}]),
- {Return,[]}
+ false -> % must be Config value from init conf case
+ case do_end_tc_call(M,F,{ok,A}, Return) of
+ {failed, FWReason} = Failed ->
+ fw_error_notify(M,F,A, FWReason),
+ {Failed, []};
+ NewReturn ->
+ {NewReturn, []}
+ end
end;
%% the return value is not a list, so it's the return value from an
%% end conf case or it's a dummy value that can be ignored
process_return_val(Return, M,F,A, Loc, Final) ->
process_return_val1(Return, M,F,A, Loc, Final, []).
-process_return_val1([Failed={E,TCError}|_], M,F,A=[Args], Loc, _, SaveOpts) when E=='EXIT';
- E==failed ->
+process_return_val1([Failed={E,TCError}|_], M,F,A=[Args], Loc, _, SaveOpts)
+ when E=='EXIT';
+ E==failed ->
fw_error_notify(M,F,A, TCError, mod_loc(Loc)),
- test_server_sup:framework_call(end_tc,
- [?pl2a(M),F,{{error,TCError},
- [[{tc_status,{failed,TCError}}|Args]]}]),
- {Failed,SaveOpts};
-process_return_val1([SaveCfg={save_config,_}|Opts], M,F,[Args], Loc, Final, SaveOpts) ->
+ case do_end_tc_call(M,F,{{error,TCError},
+ [[{tc_status,{failed,TCError}}|Args]]}, Failed) of
+ {failed,FWReason} ->
+ {{failed,FWReason},SaveOpts};
+ NewReturn ->
+ {NewReturn,SaveOpts}
+ end;
+process_return_val1([SaveCfg={save_config,_}|Opts], M,F,[Args], Loc, Final, SaveOpts) ->
process_return_val1(Opts, M,F,[[SaveCfg|Args]], Loc, Final, SaveOpts);
-process_return_val1([{skip_and_save,Why,SaveCfg}|Opts], M,F,[Args], Loc, _, SaveOpts) ->
+process_return_val1([{skip_and_save,Why,SaveCfg}|Opts], M,F,[Args], Loc, _, SaveOpts) ->
process_return_val1(Opts, M,F,[[{save_config,SaveCfg}|Args]], Loc, {skip,Why}, SaveOpts);
process_return_val1([GR={return_group_result,_}|Opts], M,F,A, Loc, Final, SaveOpts) ->
process_return_val1(Opts, M,F,A, Loc, Final, [GR|SaveOpts]);
@@ -1109,8 +1267,12 @@ process_return_val1([RetVal={Tag,_}|Opts], M,F,A, Loc, _, SaveOpts) when Tag==sk
process_return_val1([_|Opts], M,F,A, Loc, Final, SaveOpts) ->
process_return_val1(Opts, M,F,A, Loc, Final, SaveOpts);
process_return_val1([], M,F,A, _Loc, Final, SaveOpts) ->
- test_server_sup:framework_call(end_tc, [?pl2a(M),F,{Final,A}]),
- {Final,lists:reverse(SaveOpts)}.
+ case do_end_tc_call(M,F,{Final,A}, Final) of
+ {failed,FWReason} ->
+ {{failed,FWReason},SaveOpts};
+ NewReturn ->
+ {NewReturn,lists:reverse(SaveOpts)}
+ end.
user_callback(undefined, _, _, _, Args) ->
Args;
@@ -1134,11 +1296,11 @@ init_per_testcase(Mod, Func, Args) ->
false -> code:load_file(Mod);
_ -> ok
end,
- %% init_per_testcase defined, returns new configuration
+%% init_per_testcase defined, returns new configuration
case erlang:function_exported(Mod,init_per_testcase,2) of
true ->
case catch my_apply(Mod, init_per_testcase, [Func|Args]) of
- {'$test_server_ok',{Skip,Reason}} when Skip==skip;
+ {'$test_server_ok',{Skip,Reason}} when Skip==skip;
Skip==skipped ->
{skip,Reason};
{'$test_server_ok',Res={skip_and_save,_,_}} ->
@@ -1149,40 +1311,40 @@ init_per_testcase(Mod, Func, Args) ->
[] ->
{ok,NewConf};
Bad ->
- group_leader() ! {printout,12,
+ group_leader() ! {printout,12,
"ERROR! init_per_testcase has returned "
- "bad elements in Config: ~p\n",[Bad]},
+ "bad elements in Config: ~p\n",[Bad]},
{skip,{failed,{Mod,init_per_testcase,bad_return}}}
end;
{'$test_server_ok',_Other} ->
- group_leader() ! {printout,12,
+ group_leader() ! {printout,12,
"ERROR! init_per_testcase did not return "
- "a Config list.\n",[]},
+ "a Config list.\n",[]},
{skip,{failed,{Mod,init_per_testcase,bad_return}}};
{'EXIT',Reason} ->
Line = get_loc(),
FormattedLoc = test_server_sup:format_loc(mod_loc(Line)),
- group_leader() ! {printout,12,
+ group_leader() ! {printout,12,
"ERROR! init_per_testcase crashed!\n"
"\tLocation: ~s\n\tReason: ~p\n",
- [FormattedLoc,Reason]},
+ [FormattedLoc,Reason]},
{skip,{failed,{Mod,init_per_testcase,Reason}}};
Other ->
Line = get_loc(),
FormattedLoc = test_server_sup:format_loc(mod_loc(Line)),
- group_leader() ! {printout,12,
+ group_leader() ! {printout,12,
"ERROR! init_per_testcase thrown!\n"
"\tLocation: ~s\n\tReason: ~p\n",
- [FormattedLoc, Other]},
+ [FormattedLoc, Other]},
{skip,{failed,{Mod,init_per_testcase,Other}}}
end;
false ->
- %% Optional init_per_testcase not defined
- %% keep quiet.
+%% Optional init_per_testcase not defined
+%% keep quiet.
[Config] = Args,
{ok, Config}
end.
-
+
end_per_testcase(Mod, Func, Conf) ->
case erlang:function_exported(Mod,end_per_testcase,2) of
true ->
@@ -1211,11 +1373,11 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
comment(io_lib:format("<font color=\"red\">"
"WARNING: ~w crashed!"
"</font>\n",[EndFunc])),
- group_leader() ! {printout,12,
+ group_leader() ! {printout,12,
"WARNING: ~w crashed!\n"
"Reason: ~p\n"
"Line: ~s\n",
- [EndFunc, Reason,
+ [EndFunc, Reason,
test_server_sup:format_loc(
mod_loc(get_loc()))]},
{failed,{Mod,end_per_testcase,Why}};
@@ -1223,13 +1385,13 @@ do_end_per_testcase(Mod,EndFunc,Func,Conf) ->
comment(io_lib:format("<font color=\"red\">"
"WARNING: ~w thrown!"
"</font>\n",[EndFunc])),
- group_leader() ! {printout,12,
+ group_leader() ! {printout,12,
"WARNING: ~w thrown!\n"
"Reason: ~p\n"
"Line: ~s\n",
- [EndFunc, Other,
+ [EndFunc, Other,
test_server_sup:format_loc(
- mod_loc(get_loc()))]},
+ mod_loc(get_loc()))]},
{failed,{Mod,end_per_testcase,Other}}
end.
@@ -1254,7 +1416,7 @@ get_mf(_) -> {undefined,undefined}.
mod_loc(Loc) ->
%% handle diff line num versions
- case Loc of
+ case Loc of
[{{_M,_F},_L}|_] ->
[{?pl2a(M),F,L} || {{M,F},L} <- Loc];
[{_M,_F}|_] ->
@@ -1286,7 +1448,7 @@ fw_error_notify(Mod, Func, Args, Error, Loc) ->
%% Args = [term()]
%%
%% Just like io:format, except that depending on the Detail value, the output
-%% is directed to console, major and/or minor log files.
+%% is directed to console, major and/or minor log files.
print(Detail,Format,Args) ->
local_or_remote_apply({test_server_ctrl,print,[Detail,Format,Args]}).
@@ -1296,11 +1458,11 @@ print(Detail,Format,Args) ->
%%
%% Prints Leader followed by a time stamp (date and time). Depending on
%% the Detail value, the output is directed to console, major and/or minor
-%% log files.
+%% log files.
print_timestamp(Detail,Leader) ->
local_or_remote_apply({test_server_ctrl,print_timestamp,[Detail,Leader]}).
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% lookup_config(Key,Config) -> {value,{Key,Value}} | undefined
@@ -1326,11 +1488,11 @@ ts_tc(M, F, A) ->
Val = (catch my_apply(M, F, A)),
After = erlang:now(),
Result = case Val of
- {'$test_server_ok', R} ->
+ {'$test_server_ok', R} ->
R; % test case ok
- {'EXIT',_Reason} = R ->
+ {'EXIT',_Reason} = R ->
R; % test case crashed
- Other ->
+ Other ->
{failed, {thrown,Other}} % test case was thrown
end,
Elapsed =
@@ -1352,7 +1514,7 @@ my_apply(M, F, A) ->
%% in an attempt to keep this modules small (yeah, right!) %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
unicode_to_latin1(Chars) when is_list(Chars); is_binary(Chars) ->
- lists:flatten(
+ lists:flatten(
[ case X of
High when High > 255 ->
io_lib:format("\\{~.8B}",[X]);
@@ -1460,6 +1622,44 @@ sleep(MSecs) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% adjusted_sleep(Time) -> ok
+%% Time = integer() | float() | infinity
+%%
+%% Sleeps the specified number of milliseconds, multiplied by the
+%% 'multiply_timetraps' value (if set) and possibly also automatically scaled
+%% up if 'scale_timetraps' is set to true (which is default).
+%% This function also accepts floating point numbers (which are truncated) and
+%% the atom 'infinity'.
+adjusted_sleep(infinity) ->
+ receive
+ after infinity ->
+ ok
+ end;
+adjusted_sleep(MSecs) ->
+ {Multiplier,ScaleFactor} =
+ case test_server_ctrl:get_timetrap_parameters() of
+ {undefined,undefined} ->
+ {1,1};
+ {undefined,false} ->
+ {1,1};
+ {undefined,true} ->
+ {1,timetrap_scale_factor()};
+ {infinity,_} ->
+ {infinity,1};
+ {Mult,undefined} ->
+ {Mult,1};
+ {Mult,false} ->
+ {Mult,1};
+ {Mult,true} ->
+ {Mult,timetrap_scale_factor()}
+ end,
+ receive
+ after trunc(MSecs*Multiplier*ScaleFactor) ->
+ ok
+ end,
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% fail(Reason) -> exit({suite_failed,Reason})
%%
%% Immediately calls exit. Included because test suites are easier
@@ -1509,9 +1709,9 @@ break(Comment) ->
receive continue -> ok end.
spawn_break_process(Pid) ->
- spawn(fun() ->
+ spawn(fun() ->
register(test_server_break_process,self()),
- receive
+ receive
continue -> continue(Pid);
cancel -> ok
end
@@ -1561,20 +1761,21 @@ timetrap_scale_factor() ->
%% timetrap(Timeout) -> Handle
%% Handle = term()
%%
-%% Creates a time trap, that will kill the calling process if the
+%% Creates a time trap, that will kill the calling process if the
%% trap is not cancelled with timetrap_cancel/1, within Timeout milliseconds.
-
timetrap(Timeout0) ->
Timeout = time_ms(Timeout0),
cancel_default_timetrap(),
case get(test_server_multiply_timetraps) of
- undefined -> timetrap1(Timeout);
- infinity -> infinity;
- Int -> timetrap1(Timeout*Int)
+ undefined -> timetrap1(Timeout, true);
+ {undefined,false} -> timetrap1(Timeout, false);
+ {undefined,_} -> timetrap1(Timeout, true);
+ {infinity,_} -> infinity;
+ {Int,Scale} -> timetrap1(Timeout*Int, Scale)
end.
-timetrap1(Timeout) ->
- Ref = spawn_link(test_server_sup,timetrap,[Timeout,self()]),
+timetrap1(Timeout, Scale) ->
+ Ref = spawn_link(test_server_sup,timetrap,[Timeout,Scale,self()]),
case get(test_server_timetraps) of
undefined -> put(test_server_timetraps,[Ref]);
List -> put(test_server_timetraps,[Ref|List])
@@ -1582,7 +1783,6 @@ timetrap1(Timeout) ->
Ref.
ensure_timetrap(Config) ->
- %format("ensure_timetrap:~p~n",[Config]),
case get(test_server_timetraps) of
[_|_] ->
ok;
@@ -1623,7 +1823,7 @@ cancel_default_timetrap() ->
time_ms({hours,N}) -> hours(N);
time_ms({minutes,N}) -> minutes(N);
time_ms({seconds,N}) -> seconds(N);
-time_ms({Other,_N}) ->
+time_ms({Other,_N}) ->
format("=== ERROR: Invalid time specification: ~p. "
"Should be seconds, minutes, or hours.~n", [Other]),
exit({invalid_time_spec,Other});
@@ -1770,14 +1970,14 @@ call_crash(Time,Crash,M,F,A) ->
%% by the test server after completion of the test case
%% Therefore it is IMPORTANT that the USER terminates
%% the node!!
-%% {erl, ReleaseList} - Use an Erlang emulator determined by ReleaseList
-%% when starting nodes, instead of the same emulator
+%% {erl, ReleaseList} - Use an Erlang emulator determined by ReleaseList
+%% when starting nodes, instead of the same emulator
%% as the test server is running. ReleaseList is a list
-%% of specifiers, where a specifier is either
-%% {release, Rel}, {prog, Prog}, or 'this'. Rel is
-%% either the name of a release, e.g., "r7a" or
-%% 'latest'. 'this' means using the same emulator as
-%% the test server. Prog is the name of an emulator
+%% of specifiers, where a specifier is either
+%% {release, Rel}, {prog, Prog}, or 'this'. Rel is
+%% either the name of a release, e.g., "r7a" or
+%% 'latest'. 'this' means using the same emulator as
+%% the test server. Prog is the name of an emulator
%% executable. If the list has more than one element,
%% one of them is picked randomly. (Only
%% works on Solaris and Linux, and the test
@@ -1792,13 +1992,13 @@ call_crash(Time,Crash,M,F,A) ->
%% peer nodes.
%% Note that slave nodes always act as if they had
%% fail_on_error==false.
-%%
+%%
start_node(Name, Type, Options) ->
lists:foreach(
- fun(N) ->
+ fun(N) ->
case firstname(N) of
- Name ->
+ Name ->
format("=== WARNING: Trying to start node \'~w\' when node"
" with same first name exists: ~w", [Name, N]);
_other -> ok
@@ -1817,19 +2017,19 @@ start_node(Name, Type, Options) ->
%% Cannot run cover on shielded node or on a node started
%% by a shielded node.
Cover = case is_cover() of
- true ->
+ true ->
not is_shielded(Name) andalso same_version(Node);
- false ->
+ false ->
false
end,
net_adm:ping(Node),
case Cover of
- true ->
+ true ->
Sticky = unstick_all_sticky(Node),
cover:start(Node),
stick_all_sticky(Node,Sticky);
- _ ->
+ _ ->
ok
end,
{ok,Node};
@@ -1857,7 +2057,7 @@ wait_for_node(Slave) ->
self(),
{test_server_ctrl,wait_for_node,[Slave]}},
receive {sync_result,R} -> R end.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% stop_node(Name) -> true|false
@@ -1867,7 +2067,7 @@ wait_for_node(Slave) ->
stop_node(Slave) ->
Nocover = is_shielded(Slave) orelse not same_version(Slave),
case is_cover() of
- true when not Nocover ->
+ true when not Nocover ->
Sticky = unstick_all_sticky(Slave),
cover:stop(Slave),
stick_all_sticky(Slave,Sticky);
@@ -1895,10 +2095,10 @@ stop_node(Slave) ->
%% with the {cleanup,false} option, or it was started
%% in some other way than test_server:start_node/3
format("=== WARNING: Attempt to stop a nonexisting slavenode (~p)~n"
- "=== Trying to kill it anyway!!!",
+ "=== Trying to kill it anyway!!!",
[Slave]),
case net_adm:ping(Slave)of
- pong ->
+ pong ->
slave:stop(Slave),
true;
pang ->
@@ -1918,7 +2118,7 @@ is_release_available(Release) ->
self(),
{test_server_ctrl,is_release_available,[Release]}},
receive {sync_result,R} -> R end.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% run_on_shielded_node(Fun, CArgs) -> term()
@@ -1937,7 +2137,7 @@ is_release_available(Release) ->
%%
%% Fun - Function to execute
%% CArg - Extra command line arguments to use when starting
-%% the shielded node.
+%% the shielded node.
%%
%% If Fun is successfully executed, the result is returned.
%%
@@ -2037,8 +2237,8 @@ is_native(Mod) ->
%% The given String will occur in the comment field
%% of the table on the test suite result page. If
%% called several times, only the last comment is
-%% printed.
-%% comment/1 is also overwritten by the return value
+%% printed.
+%% comment/1 is also overwritten by the return value
%% {comment,Comment} or fail/1 (which prints Reason
%% as a comment).
comment(String) ->
@@ -2154,7 +2354,7 @@ purify_new_fds_inuse() ->
{'EXIT', _} -> false;
Inuse when is_integer(Inuse) -> Inuse
end.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% purify_format(Format, Args) -> ok
%% Format = string()
@@ -2202,9 +2402,9 @@ local_or_remote_apply({M,F,A} = MFA) ->
request(Sock,Request) ->
gen_tcp:send(Sock,<<1,(term_to_binary(Request))/binary>>).
-%%
+%%
%% Generic receive function for communication with host
-%%
+%%
recv(Sock) ->
case gen_tcp:recv(Sock,0) of
{error,closed} ->
diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl
index 4cb5863955..30d7314058 100644
--- a/lib/test_server/src/test_server_ctrl.erl
+++ b/lib/test_server/src/test_server_ctrl.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,7 +27,7 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% MODULE DEPENDENCIES:
-%% HARD TO REMOVE: erlang, lists, io_lib, gen_server, file, io, string,
+%% HARD TO REMOVE: erlang, lists, io_lib, gen_server, file, io, string,
%% code, ets, rpc, gen_tcp, inet, erl_tar, sets,
%% test_server, test_server_sup, test_server_node
%% EASIER TO REMOVE: filename, filelib, lib, re
@@ -36,7 +36,7 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ARCHITECTURE
-%%
+%%
%% The Erlang Test Server can be run on the target machine (local target)
%% or towards a remote target. The execution flow is mainly the same in
%% both cases, but with a remote target the test cases are (obviously)
@@ -44,11 +44,11 @@
%% socket connections because the host should not be introduced as an
%% additional node in the distributed erlang system in which the test
%% cases are run.
-%%
-%%
+%%
+%%
%% Local Target:
%% =============
-%%
+%%
%% -----
%% | | test_server_ctrl ({global,test_server})
%% ----- (test_server_ctrl.erl)
@@ -62,33 +62,33 @@
%% -----
%% | | CaseProc
%% ----- (test_server.erl)
-%%
-%%
-%%
+%%
+%%
+%%
%% test_server_ctrl is the main process in the system. It is a registered
%% process, and it will always be alive when testing is ongoing.
%% test_server_ctrl initiates testing and monitors JobProc(s).
-%%
-%% When target is local, and Test Server is *not* being used by a framework
-%% application (where it might cause duplicate name problems in a distributed
-%% test environment), the process is globally registered as 'test_server'
+%%
+%% When target is local, and Test Server is *not* being used by a framework
+%% application (where it might cause duplicate name problems in a distributed
+%% test environment), the process is globally registered as 'test_server'
%% to be able to simulate the {global,test_server} process on a remote target.
-%%
-%% JobProc is spawned for each 'job' added to the test_server_ctrl.
+%%
+%% JobProc is spawned for each 'job' added to the test_server_ctrl.
%% A job can mean one test case, one test suite or one spec.
%% JobProc creates and writes logs and presents results from testing.
%% JobProc is the group leader for CaseProc.
-%%
+%%
%% CaseProc is spawned for each test case. It runs the test case and
%% sends results and any other information to its group leader - JobProc.
-%%
-%%
-%%
+%%
+%%
+%%
%% Remote Target:
%% ==============
-%%
+%%
%% HOST TARGET
-%%
+%%
%% ----- MainSock -----
%% test_server_ctrl | |- - - - - - -| | {global,test_server}
%% (test_server_ctrl.erl) ----- ----- (test_server.erl)
@@ -102,36 +102,36 @@
%% -----
%% | | CaseProc
%% ----- (test_server.erl)
-%%
-%%
-%%
-%%
+%%
+%%
+%%
+%%
%% A separate test_server process only exists when target is remote. It
%% is then the main process on target. It is started when test_server_ctrl
%% is started, and a socket connection is established between
%% test_server_ctrl and test_server. The following information can be sent
%% over MainSock:
-%%
+%%
%% HOST TARGET
%% -> {target_info, TargetInfo} (during initiation)
%% <- {job_proc_killed,Name,Reason} (if a JobProcT dies unexpectedly)
%% -> {job,Port,Name} (to start a new JobProcT)
-%%
-%%
+%%
+%%
%% When target is remote, JobProc is split into to processes: JobProcH
%% executing on Host and JobProcT executing on Target. (The two processes
%% execute the same code as JobProc does when target is local.) JobProcH
%% and JobProcT communicates over a socket connection. The following
%% information can be sent over JobSock:
-%%
+%%
%% HOST TARGET
%% -> {test_case, Case} To start a new test case
%% -> {beam,Mod} .beam file as binary to be loaded
%% on target, e.g. a test suite
%% -> {datadir,Tarfile} Content of the datadir for a test suite
%% <- {apply,MFA} MFA to be applied on host, ignore return;
-%% (apply is used for printing information in
-%% log or console)
+%% (apply is used for printing information in
+%% log or console)
%% <- {sync_apply,MFA} MFA to be applied on host, wait for return
%% (used for starting and stopping slave nodes)
%% -> {sync_apply,MFA} MFA to be applied on target, wait for return
@@ -141,7 +141,7 @@
%% <- {crash_dumps,Tarfile} When a test case is finished
%% -> job_done When a job is finished
%% <- {privdir,Privdir} When a job is finished
-%%
+%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -151,21 +151,24 @@
%%% OPERATOR INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([add_spec/1, add_dir/2, add_dir/3]).
--export([add_module/1, add_module/2, add_case/2, add_case/3, add_cases/2,
- add_cases/3]).
+-export([add_module/1, add_module/2,
+ add_conf/3,
+ add_case/2, add_case/3, add_cases/2, add_cases/3]).
-export([add_dir_with_skip/3, add_dir_with_skip/4, add_tests_with_skip/3]).
-export([add_module_with_skip/2, add_module_with_skip/3,
+ add_conf_with_skip/4,
add_case_with_skip/3, add_case_with_skip/4,
add_cases_with_skip/3, add_cases_with_skip/4]).
-export([jobs/0, run_test/1, wait_finish/0, idle_notify/1,
abort_current_testcase/1, abort/0]).
-export([start_get_totals/1, stop_get_totals/0]).
-export([get_levels/0, set_levels/3]).
--export([multiply_timetraps/1]).
+-export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]).
-export([cover/2, cover/3, cover/7,
cross_cover_analyse/1, cross_cover_analyse/2, trc/1, stop_trace/0]).
-export([testcase_callback/1]).
-export([set_random_seed/1]).
+-export([kill_slavenodes/0]).
%%% TEST_SERVER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([output/2, print/2, print/3, print_timestamp/2]).
@@ -207,13 +210,15 @@
-define(pl2a(M), test_server_sup:package_atom(M)).
-define(void_fun, fun() -> ok end).
--define(mod_result(X), if X == skip -> skipped;
- X == auto_skip -> skipped;
+-define(mod_result(X), if X == skip -> skipped;
+ X == auto_skip -> skipped;
true -> X end).
--record(state,{jobs=[],levels={1,19,10},multiply_timetraps=1,finish=false,
+-record(state,{jobs=[],levels={1,19,10},
+ multiply_timetraps=1,scale_timetraps=true,
+ finish=false,
target_info, trc=false, cover=false, wait_for_node=[],
- testcase_callback=undefined, idle_notify=[],
+ testcase_callback=undefined, idle_notify=[],
get_totals=false, random_seed=undefined}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -222,21 +227,28 @@
add_dir(Name, Job=[Dir|_Dirs]) when is_list(Dir) ->
add_job(cast_to_list(Name),
lists:map(fun(D)-> {dir,cast_to_list(D)} end, Job));
-add_dir(Name, Dir) ->
+add_dir(Name, Dir) ->
add_job(cast_to_list(Name), {dir,cast_to_list(Dir)}).
add_dir(Name, Job=[Dir|_Dirs], Pattern) when is_list(Dir) ->
add_job(cast_to_list(Name),
lists:map(fun(D)-> {dir,cast_to_list(D),
cast_to_list(Pattern)} end, Job));
-add_dir(Name, Dir, Pattern) ->
+add_dir(Name, Dir, Pattern) ->
add_job(cast_to_list(Name), {dir,cast_to_list(Dir),cast_to_list(Pattern)}).
add_module(Mod) when is_atom(Mod) ->
add_job(atom_to_list(Mod), {Mod,all}).
+
add_module(Name, Mods) when is_list(Mods) ->
add_job(cast_to_list(Name), lists:map(fun(Mod) -> {Mod,all} end, Mods)).
+add_conf(Name, Mod, Conf) when is_tuple(Conf) ->
+ add_job(cast_to_list(Name), {Mod,[Conf]});
+
+add_conf(Name, Mod, Confs) when is_list(Confs) ->
+ add_job(cast_to_list(Name), {Mod,Confs}).
+
add_case(Mod, Case) when is_atom(Mod), is_atom(Case) ->
add_job(atom_to_list(Mod), {Mod,Case}).
@@ -256,14 +268,14 @@ add_spec(Spec) ->
false -> {error,nofile}
end.
-%% This version of the interface is to be used if there are
+%% This version of the interface is to be used if there are
%% suites or cases that should be skipped.
add_dir_with_skip(Name, Job=[Dir|_Dirs], Skip) when is_list(Dir) ->
add_job(cast_to_list(Name),
lists:map(fun(D)-> {dir,cast_to_list(D)} end, Job),
Skip);
-add_dir_with_skip(Name, Dir, Skip) ->
+add_dir_with_skip(Name, Dir, Skip) ->
add_job(cast_to_list(Name), {dir,cast_to_list(Dir)}, Skip).
add_dir_with_skip(Name, Job=[Dir|_Dirs], Pattern, Skip) when is_list(Dir) ->
@@ -271,7 +283,7 @@ add_dir_with_skip(Name, Job=[Dir|_Dirs], Pattern, Skip) when is_list(Dir) ->
lists:map(fun(D)-> {dir,cast_to_list(D),
cast_to_list(Pattern)} end, Job),
Skip);
-add_dir_with_skip(Name, Dir, Pattern, Skip) ->
+add_dir_with_skip(Name, Dir, Pattern, Skip) ->
add_job(cast_to_list(Name),
{dir,cast_to_list(Dir),cast_to_list(Pattern)}, Skip).
@@ -281,6 +293,12 @@ add_module_with_skip(Mod, Skip) when is_atom(Mod) ->
add_module_with_skip(Name, Mods, Skip) when is_list(Mods) ->
add_job(cast_to_list(Name), lists:map(fun(Mod) -> {Mod,all} end, Mods), Skip).
+add_conf_with_skip(Name, Mod, Conf, Skip) when is_tuple(Conf) ->
+ add_job(cast_to_list(Name), {Mod,[Conf]}, Skip);
+
+add_conf_with_skip(Name, Mod, Confs, Skip) when is_list(Confs) ->
+ add_job(cast_to_list(Name), {Mod,Confs}, Skip).
+
add_case_with_skip(Mod, Case, Skip) when is_atom(Mod), is_atom(Case) ->
add_job(atom_to_list(Mod), {Mod,Case}, Skip).
@@ -295,15 +313,14 @@ add_cases_with_skip(Name, Mod, Cases, Skip) when is_atom(Mod), is_list(Cases) ->
add_tests_with_skip(LogDir, Tests, Skip) ->
add_job(LogDir,
- lists:map(fun({Dir,all,all}) ->
+ lists:map(fun({Dir,all,all}) ->
{Dir,{dir,Dir}};
- ({Dir,Mods,all}) ->
+ ({Dir,Mods,all}) ->
{Dir,lists:map(fun(M) -> {M,all} end, Mods)};
({Dir,Mod,Cases}) ->
{Dir,{Mod,Cases}}
end, Tests),
Skip).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% COMMAND LINE INTERFACE
@@ -315,7 +332,7 @@ parse_cmd_line(['SPEC',Spec|Cmds], SpecList, Names, Param, Trc, Cov, TCCB) ->
case file:consult(Spec) of
{ok, TermList} ->
Name = filename:rootname(Spec),
- parse_cmd_line(Cmds, TermList++SpecList, [Name|Names], Param,
+ parse_cmd_line(Cmds, TermList++SpecList, [Name|Names], Param,
Trc, Cov, TCCB);
{error,Reason} ->
io:format("Can't open ~s: ~p\n",
@@ -406,7 +423,7 @@ run_test(CommandLine) ->
end,
testcase_callback(TCCB),
add_job(Name, {command_line,SpecList}),
-
+
%% adding of jobs involves file i/o which may take long time
%% when running a nfs mounted file system (VxWorks).
case controller_call(get_target_info) of
@@ -479,6 +496,12 @@ set_levels(Show, Major, Minor) ->
multiply_timetraps(N) ->
controller_call({multiply_timetraps,N}).
+scale_timetraps(Bool) ->
+ controller_call({scale_timetraps,Bool}).
+
+get_timetrap_parameters() ->
+ controller_call(get_timetrap_parameters).
+
trc(TraceFile) ->
controller_call({trace,TraceFile}, 2*?ACCEPT_TIMEOUT).
@@ -503,6 +526,9 @@ testcase_callback(ModFunc) ->
set_random_seed(Seed) ->
controller_call({set_random_seed,Seed}).
+kill_slavenodes() ->
+ controller_call(kill_slavenodes).
+
get_hosts() ->
get(test_server_hosts).
@@ -511,6 +537,8 @@ get_target_os_type() ->
undefined ->
%% This is probably called on the target node
os:type();
+ Pid when Pid =:= self() ->
+ os:type();
_pid ->
%% This is called on the controller, e.g. from a
%% specification clause of a test case
@@ -551,7 +579,7 @@ controller_call(Arg, Timeout) ->
Other ->
Other
end.
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -615,9 +643,9 @@ contact_main_target(local) ->
%% When used by a general framework, global registration of
%% test_server should not be required.
case os:getenv("TEST_SERVER_FRAMEWORK") of
- false ->
+ FW when FW =:= false; FW =:= "undefined" ->
%% Local target! The global test_server process implemented by
- %% test_server.erl will not be started, so we simulate it by
+ %% test_server.erl will not be started, so we simulate it by
%% globally registering this process instead.
global:sync(),
case global:whereis_name(test_server) of
@@ -681,9 +709,9 @@ read_parameters([], Par) when Par#par.type==undefined ->
read_parameters([], Par) when Par#par.target==undefined ->
{error, {missing_mandatory_parameter,target}};
read_parameters([], Par0) ->
- Par =
+ Par =
case {Par0#par.type, Par0#par.master} of
- {ose, undefined} ->
+ {ose, undefined} ->
%% Use this node as master and bootserver for target
%% and slave nodes
Par0#par{master = atom_to_list(node()),
@@ -691,10 +719,10 @@ read_parameters([], Par0) ->
{ose, _Master} ->
%% Master for target and slave nodes was defined in parameterfile
Par0;
- _ ->
+ _ ->
%% Use target as master for slave nodes,
%% (No master is used for target)
- Par0#par{master="test_server@" ++ Par0#par.target}
+ Par0#par{master="test_server@" ++ Par0#par.target}
end,
{ok,Par}.
@@ -708,7 +736,7 @@ naming() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call(kill_slavenodes, From, State) -> ok
%%
-%% Kill all slave nodes that remain after a test case
+%% Kill all slave nodes that remain after a test case
%% is completed.
%%
handle_call(kill_slavenodes, _From, State) ->
@@ -736,7 +764,7 @@ handle_call(get_hosts, _From, State) ->
{reply, Hosts, State};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% handle_call({add_job,Dir,Name,TopCase,Skip}, _, State) ->
+%% handle_call({add_job,Dir,Name,TopCase,Skip}, _, State) ->
%% ok | {error,Reason}
%%
%% Dir = string()
@@ -760,7 +788,7 @@ handle_call(get_hosts, _From, State) ->
handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
LogDir = Dir ++ ?logdir_ext,
- ExtraTools =
+ ExtraTools =
case State#state.cover of
false -> [];
{App,Analyse} -> [{cover,App,Analyse}]
@@ -776,19 +804,21 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
{spec,SpecName} ->
Pid = spawn_tester(
?MODULE, do_spec,
- [SpecName,State#state.multiply_timetraps],
- LogDir, Name, State#state.levels,
+ [SpecName,{State#state.multiply_timetraps,
+ State#state.scale_timetraps}],
+ LogDir, Name, State#state.levels,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
- {reply, ok, State#state{jobs=NewJobs}};
+ {reply, ok, State#state{jobs=NewJobs}};
{command_line,SpecList} ->
Pid = spawn_tester(
?MODULE, do_spec_list,
- [SpecList,State#state.multiply_timetraps],
- LogDir, Name, State#state.levels,
+ [SpecList,{State#state.multiply_timetraps,
+ State#state.scale_timetraps}],
+ LogDir, Name, State#state.levels,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
- {reply, ok, State#state{jobs=NewJobs}};
+ {reply, ok, State#state{jobs=NewJobs}};
TopCase ->
case State#state.get_totals of
{CliPid,Fun} ->
@@ -798,10 +828,11 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) ->
_ ->
Cfg = make_config([]),
Pid = spawn_tester(
- ?MODULE, do_test_cases,
+ ?MODULE, do_test_cases,
[TopCase,Skip,Cfg,
- State#state.multiply_timetraps],
- LogDir, Name, State#state.levels,
+ {State#state.multiply_timetraps,
+ State#state.scale_timetraps}],
+ LogDir, Name, State#state.levels,
State#state.testcase_callback, ExtraTools1),
NewJobs = [{Name,Pid}|State#state.jobs],
{reply, ok, State#state{jobs=NewJobs}}
@@ -827,7 +858,7 @@ handle_call(jobs, _From, State) ->
%% handle_call({abort_current_testcase,Reason}, _, State) -> Result
%% Reason = term()
%% Result = ok | {error,no_testcase_running}
-%%
+%%
%% Attempts to abort the test case that's currently running.
handle_call({abort_current_testcase,Reason}, _From, State) ->
@@ -855,7 +886,7 @@ handle_call({abort_current_testcase,Reason}, _From, State) ->
handle_call({finish,Fini}, _From, State) ->
case State#state.jobs of
[] ->
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
State#state.idle_notify),
State2 = State#state{finish=false},
{stop,shutdown,{ok,self()}, State2};
@@ -878,7 +909,7 @@ handle_call({idle_notify,Fun}, {Cli,_Ref}, State) ->
{reply, {ok,self()}, State};
_ ->
Subscribed = State#state.idle_notify,
- {reply, {ok,self()},
+ {reply, {ok,self()},
State#state{idle_notify=[{Cli,Fun}|Subscribed]}}
end;
@@ -891,7 +922,7 @@ handle_call({idle_notify,Fun}, {Cli,_Ref}, State) ->
handle_call({start_get_totals,Fun}, {Cli,_Ref}, State) ->
{reply, {ok,self()}, State#state{get_totals={Cli,Fun}}};
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call(stop_get_totals, From, State) -> ok
%%
@@ -942,11 +973,31 @@ handle_call({multiply_timetraps,N}, _From, State) ->
{reply,ok,State#state{multiply_timetraps=N}};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% handle_call({scale_timetraps,Bool}, _, State) -> ok
+%% Bool = true | false
+%%
+%% Specifies if test_server should scale the timetrap value
+%% automatically if e.g. cover is running.
+
+handle_call({scale_timetraps,Bool}, _From, State) ->
+ {reply,ok,State#state{scale_timetraps=Bool}};
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% handle_call(get_timetrap_parameters, _, State) -> {Multiplier,Scale}
+%% Multiplier = integer() | infinity
+%% Scale = true | false
+%%
+%% Returns the parameter values that affect timetraps.
+
+handle_call(get_timetrap_parameters, _From, State) ->
+ {reply,{State#state.multiply_timetraps,State#state.scale_timetraps},State};
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call({trace,TraceFile}, _, State) -> ok | {error,Reason}
%%
-%% Starts a separate node (trace control node) which
+%% Starts a separate node (trace control node) which
%% starts tracing on target and all slave nodes
-%%
+%%
%% TraceFile is a text file with elements of type
%% {Trace,Mod,TracePattern}.
%% {Trace,Mod,Func,TracePattern}.
@@ -955,10 +1006,10 @@ handle_call({multiply_timetraps,N}, _From, State) ->
%% Trace = tp | tpl; local or global call trace
%% Mod,Func = atom(), Arity=integer(); defines what to trace
%% TracePattern = [] | match_spec()
-%%
+%%
%% The 'call' trace flag is set on all processes, and then
%% the given trace patterns are set.
-
+
handle_call({trace,TraceFile}, _From, State=#state{trc=false}) ->
TI = State#state.target_info,
case test_server_node:start_tracer_node(TraceFile, TI) of
@@ -993,7 +1044,7 @@ handle_call({cover,App,Analyse}, _From, State) ->
%% handle_call({testcase_callback,{Mod,Func}}, _, State) -> ok | {error,Reason}
%%
%% Add a callback function that will be called before and after every
-%% test case (on the test case process):
+%% test case (on the test case process):
%%
%% Mod:Func(Suite,TestCase,InitOrEnd,Config)
%%
@@ -1001,9 +1052,9 @@ handle_call({cover,App,Analyse}, _From, State) ->
handle_call({testcase_callback,ModFunc}, _From, State) ->
case ModFunc of
- {Mod,Func} ->
+ {Mod,Func} ->
case code:is_loaded(Mod) of
- {file,_} ->
+ {file,_} ->
ok;
false ->
code:load_file(Mod)
@@ -1065,15 +1116,15 @@ handle_call({start_node, Name, Type, Options}, From, State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_call({wait_for_node,Node}, _, State) -> ok
%%
-%% Waits for a new node to take contact. Used if
+%% Waits for a new node to take contact. Used if
%% node is started with option {wait,false}
handle_call({wait_for_node, Node}, From, State) ->
- NewWaitList =
+ NewWaitList =
case ets:lookup(slave_tab,Node) of
- [] ->
+ [] ->
[{Node,From}|State#state.wait_for_node];
- _ ->
+ _ ->
gen_server:reply(From,ok),
State#state.wait_for_node
end,
@@ -1086,7 +1137,7 @@ handle_call({wait_for_node, Node}, From, State) ->
%% - the node is really stopped by test_server when this returns.
handle_call({stop_node, Name}, _From, State) ->
- R = test_server_node:stop_node(Name, State#state.target_info),
+ R = test_server_node:stop_node(Name, State#state.target_info),
{reply, R, State};
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1112,7 +1163,7 @@ handle_cast({node_started,Node}, State) ->
false -> ok;
Trc -> test_server_node:trace_nodes(Trc, [Node])
end,
- NewWaitList =
+ NewWaitList =
case lists:keysearch(Node,1,State#state.wait_for_node) of
{value,{Node,From}} ->
gen_server:reply(From, ok),
@@ -1128,10 +1179,10 @@ handle_cast({node_started,Node}, State) ->
%% Reason = term()
%%
%% Handles exit messages from linked processes. Only test suites and
-%% possibly a target client are expected to be linked.
+%% possibly a target client are expected to be linked.
%% When a test suite terminates, it is removed from the job queue.
%% If a target client terminates it means that we lost contact with
-%% target. The test_server_ctrl process is terminated, and teminate/2
+%% target. The test_server_ctrl process is terminated, and teminate/2
%% will do the cleanup
handle_info({'EXIT',Pid,Reason}, State) ->
@@ -1139,7 +1190,7 @@ handle_info({'EXIT',Pid,Reason}, State) ->
false ->
TI = State#state.target_info,
case TI#target_info.target_client of
- Pid ->
+ Pid ->
%% The target client died - lost contact with target
{stop,{lost_contact_with_target,Reason},State};
_other ->
@@ -1160,13 +1211,13 @@ handle_info({'EXIT',Pid,Reason}, State) ->
State2 = State#state{jobs=NewJobs},
case NewJobs of
[] ->
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
State2#state.idle_notify),
case State2#state.finish of
false ->
{noreply,State2#state{idle_notify=[]}};
_ -> % true | abort
- %% test_server:finish() has been called and
+ %% test_server:finish() has been called and
%% there are no jobs in the job queue =>
%% stop the test_server_ctrl
{stop,shutdown,State2#state{finish=false}}
@@ -1174,7 +1225,7 @@ handle_info({'EXIT',Pid,Reason}, State) ->
_ -> % pending jobs
case State2#state.finish of
abort -> % abort test now!
- lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
+ lists:foreach(fun({Cli,Fun}) -> Fun(Cli) end,
State2#state.idle_notify),
{stop,shutdown,State2#state{finish=false}};
_ -> % true | false
@@ -1194,9 +1245,9 @@ handle_info({tcp,_MainSock,<<1,Request/binary>>}, State) ->
case binary_to_term(Request) of
{job_proc_killed,Name,Reason} ->
%% The only purpose of this is to inform the user about what
- %% happened on target.
+ %% happened on target.
%% The local job proc will soon be killed by the closed socket or
- %% because the job is finished. Then the above clause ('EXIT') will
+ %% because the job is finished. Then the above clause ('EXIT') will
%% handle the problem.
io:format("Suite ~s was killed on remote target with reason"
" ~p\n", [Name,Reason]);
@@ -1204,13 +1255,13 @@ handle_info({tcp,_MainSock,<<1,Request/binary>>}, State) ->
ignore
end,
{noreply,State};
-
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% handle_info({tcp_closed,Sock}, State)
%%
%% A Socket was closed. This indicates that a node died.
-%% This can be
+%% This can be
%% *Target node (if remote)
%% *Slave or peer node started by a test suite
%% *Trace controll node
@@ -1221,10 +1272,10 @@ handle_info({tcp_closed,Sock}, State=#state{trc=Sock}) ->
{noreply,State#state{trc=false}};
handle_info({tcp_closed,Sock}, State) ->
case test_server_node:nodedown(Sock,State#state.target_info) of
- target_died ->
+ target_died ->
%% terminate/2 will do the cleanup
{stop,target_died,State};
- _ ->
+ _ ->
{noreply,State}
end;
@@ -1260,7 +1311,7 @@ kill_all_jobs([]) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% spawn_tester(Mod, Func, Args, Dir, Name, Levels,
+%% spawn_tester(Mod, Func, Args, Dir, Name, Levels,
%% TestCaseCallback, ExtraTools) -> Pid
%% Mod = atom()
%% Func = atom()
@@ -1268,23 +1319,23 @@ kill_all_jobs([]) ->
%% Dir = string()
%% Name = string()
%% Levels = {integer(),integer(),integer()}
-%% TestCaseCallback = {CBMod,CBFunc} | undefined
+%% TestCaseCallback = {CBMod,CBFunc} | undefined
%% ExtraTools = [ExtraTool,...]
%% ExtraTool = CoverInfo | TraceInfo | RandomSeed
%%
%% Spawns a test suite execute-process, just an ordinary spawn, except
%% that it will set a lot of dictionary information before starting the
%% named function. Also, the execution is timed and protected by a catch.
-%% When the named function is done executing, a summary of the results
+%% When the named function is done executing, a summary of the results
%% is printed to the log files.
spawn_tester(Mod, Func, Args, Dir, Name, Levels, TCCallback, ExtraTools) ->
spawn_link(
- fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels,
- TCCallback, ExtraTools)
+ fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels,
+ TCCallback, ExtraTools)
end).
-init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
+init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
TCCallback, ExtraTools) ->
process_flag(trap_exit, true),
put(test_server_name, Name),
@@ -1324,7 +1375,7 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev},
{Skipped,_} -> {Skipped,io_lib:format(", ~p Skipped", [Skipped])}
end,
OkN = get(test_server_ok),
- FailedN = get(test_server_failed),
+ FailedN = get(test_server_failed),
print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td>"
"<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n",
[Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]).
@@ -1338,9 +1389,9 @@ ts_tc(M, F, A) ->
{Elapsed,Val}.
elapsed_time(Before, After) ->
- (element(1,After)*1000000000000 +
+ (element(1,After)*1000000000000 +
element(2,After)*1000000 + element(3,After)) -
- (element(1,Before)*1000000000000 +
+ (element(1,Before)*1000000000000 +
element(2,Before)*1000000 + element(3,Before)).
start_extra_tools(ExtraTools) ->
@@ -1378,28 +1429,32 @@ stop_extra_tools([], _) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% do_spec(SpecName, MultiplyTimetrap) -> {error,Reason} | exit(Result)
+%% do_spec(SpecName, TimetrapSpec) -> {error,Reason} | exit(Result)
%% SpecName = string()
+%% TimetrapSpec = MultiplyTimetrap | {MultiplyTimetrap,ScaleTimetrap}
%% MultiplyTimetrap = integer() | infinity
+%% ScaleTimetrap = bool()
%%
%% Reads the named test suite specification file, and executes it.
%%
-%% This function is meant to be called by a process created by
+%% This function is meant to be called by a process created by
%% spawn_tester/7, which sets up some necessary dictionary values.
-do_spec(SpecName, MultiplyTimetrap) when is_list(SpecName) ->
+do_spec(SpecName, TimetrapSpec) when is_list(SpecName) ->
case file:consult(SpecName) of
{ok,TermList} ->
- do_spec_list(TermList,MultiplyTimetrap);
+ do_spec_list(TermList,TimetrapSpec);
{error,Reason} ->
io:format("Can't open ~s: ~p\n", [SpecName,Reason]),
{error,{cant_open_spec,Reason}}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% do_spec_list(TermList) -> exit(Result)
+%% do_spec_list(TermList, TimetrapSpec) -> exit(Result)
%% TermList = [term()|...]
+%% TimetrapSpec = MultiplyTimetrap | {MultiplyTimetrap,ScaleTimetrap}
%% MultiplyTimetrap = integer() | infinity
+%% ScaleTimetrap = bool()
%%
%% Executes a list of test suite specification commands. The following
%% commands are available, and may occur zero or more times (if several,
@@ -1422,21 +1477,21 @@ do_spec(SpecName, MultiplyTimetrap) when is_list(SpecName) ->
%% nodenames will be generated from the local host.
%%
%% {hosts, Hosts} Specifies a list of available hosts on which to start
-%% slave nodes. It is used when the {remote, true} option is given to the
+%% slave nodes. It is used when the {remote, true} option is given to the
%% test_server:start_node/3 function. Also, if {require_nodenames, Num} is
-%% contained in the TermList, the generated nodenames will be spread over
+%% contained in the TermList, the generated nodenames will be spread over
%% all hosts given in this Hosts list. The hostnames are given as atoms or
%% strings.
-%%
+%%
%% {diskless, true}</c></tag> is kept for backwards compatiblilty and
%% should not be used. Use a configuration test case instead.
-%%
-%% This function is meant to be called by a process created by
+%%
+%% This function is meant to be called by a process created by
%% spawn_tester/7, which sets up some necessary dictionary values.
-do_spec_list(TermList0, MultiplyTimetrap) ->
+do_spec_list(TermList0, TimetrapSpec) ->
Nodes = [],
- TermList =
+ TermList =
case lists:keysearch(hosts, 1, TermList0) of
{value, {hosts, Hosts0}} ->
Hosts = lists:map(fun(H) -> cast_to_list(H) end, Hosts0),
@@ -1447,7 +1502,7 @@ do_spec_list(TermList0, MultiplyTimetrap) ->
end,
DefaultConfig = make_config([{nodes,Nodes}]),
{TopCases,SkipList,Config} = do_spec_terms(TermList, [], [], DefaultConfig),
- do_test_cases(TopCases, SkipList, Config, MultiplyTimetrap).
+ do_test_cases(TopCases, SkipList, Config, TimetrapSpec).
do_spec_terms([], TopCases, SkipList, Config) ->
{TopCases,SkipList,Config};
@@ -1470,21 +1525,21 @@ do_spec_terms([{default_timeout,Tmo}|Terms], TopCases, SkipList, Config) ->
do_spec_terms([{require_nodenames,NumNames}|Terms], TopCases, SkipList, Config) ->
NodeNames0=generate_nodenames(NumNames),
NodeNames=lists:delete([], NodeNames0),
- do_spec_terms(Terms, TopCases, SkipList,
+ do_spec_terms(Terms, TopCases, SkipList,
update_config(Config, {nodenames,NodeNames}));
do_spec_terms([Other|Terms], TopCases, SkipList, Config) ->
io:format("** WARNING: Spec file contains unknown directive ~p\n",
[Other]),
do_spec_terms(Terms, TopCases, SkipList, Config).
-
+
generate_nodenames(Num) ->
Hosts = case controller_call(get_hosts) of
- [] ->
+ [] ->
TI = controller_call(get_target_info),
[TI#target_info.host];
- List ->
+ List ->
List
end,
generate_nodenames2(Num, Hosts, []).
@@ -1511,25 +1566,25 @@ temp_nodename([Chr|Base], Acc) ->
%% NoOfCases = integer() | unknown
%%
%% Counts the test cases that are about to run and returns that number.
-%% If there's a conf group in TestSpec with a repeat property, the total number
+%% If there's a conf group in TestSpec with a repeat property, the total number
%% of cases can not be calculated and NoOfCases = unknown.
count_test_cases(TopCases, SkipCases) when is_list(TopCases) ->
case collect_all_cases(TopCases, SkipCases) of
- {error,_} ->
- error;
+ {error,_Why} = Error ->
+ Error;
TestSpec ->
{get_suites(TestSpec, []),
case remove_conf(TestSpec) of
{repeats,_} ->
unknown;
- TestSpec1 ->
+ TestSpec1 ->
length(TestSpec1)
end}
end;
count_test_cases(TopCase, SkipCases) ->
count_test_cases([TopCase], SkipCases).
-
+
remove_conf(Cases) ->
remove_conf(Cases, [], false).
@@ -1538,13 +1593,15 @@ remove_conf([{conf, _Ref, Props, _MF}|Cases], NoConf, Repeats) ->
case get_repeat(Props) of
undefined ->
remove_conf(Cases, NoConf, Repeats);
+ {_RepType,1} ->
+ remove_conf(Cases, NoConf, Repeats);
_ ->
remove_conf(Cases, NoConf, true)
end;
remove_conf([{make,_Ref,_MF}|Cases], NoConf, Repeats) ->
remove_conf(Cases, NoConf, Repeats);
-remove_conf([{skip_case,{Type,_Ref,_MF,_Cmt}}|Cases],
- NoConf, Repeats) when Type==conf;
+remove_conf([{skip_case,{Type,_Ref,_MF,_Cmt}}|Cases],
+ NoConf, Repeats) when Type==conf;
Type==make ->
remove_conf(Cases, NoConf, Repeats);
remove_conf([C|Cases], NoConf, Repeats) ->
@@ -1582,22 +1639,30 @@ add_mod(Mod, Mods) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) ->
+%% do_test_cases(TopCases, SkipCases, Config, TimetrapSpec) ->
%% exit(Result)
%%
%% TopCases = term() (See collect_cases/3)
%% SkipCases = term() (See collect_cases/3)
%% Config = term() (See collect_cases/3)
+%% TimetrapSpec = MultiplyTimetrap | {MultiplyTimetrap,ScaleTimetrap}
%% MultiplyTimetrap = integer() | infinity
+%% ScaleTimetrap = bool()
%%
%% Initializes and starts the test run, for "ordinary" test suites.
%% Creates log directories and log files, inserts initial timestamps and
%% configuration information into the log files.
%%
-%% This function is meant to be called by a process created by
+%% This function is meant to be called by a process created by
%% spawn_tester/7, which sets up some necessary dictionary values.
-
-do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_list(TopCases) ->
+do_test_cases(TopCases, SkipCases,
+ Config, MultiplyTimetrap) when is_integer(MultiplyTimetrap);
+ MultiplyTimetrap == infinity ->
+ do_test_cases(TopCases, SkipCases, Config, {MultiplyTimetrap,true});
+
+do_test_cases(TopCases, SkipCases,
+ Config, TimetrapData) when is_list(TopCases),
+ is_tuple(TimetrapData) ->
start_log_file(),
case collect_all_cases(TopCases, SkipCases) of
{error,Why} ->
@@ -1607,27 +1672,36 @@ do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_list(TopCas
N = case remove_conf(TestSpec0) of
{repeats,_} -> unknown;
TS -> length(TS)
- end,
+ end,
put(test_server_cases, N),
put(test_server_case_num, 0),
- TestSpec =
+ TestSpec =
add_init_and_end_per_suite(TestSpec0, undefined, undefined),
+
TI = get_target_info(),
print(1, "Starting test~s", [print_if_known(N, {", ~w test cases",[N]},
{" (with repeated test cases)",[]})]),
- test_server_sup:framework_call(report, [tests_start,
- {get(test_server_name),N}]),
- print(html,
- "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
- "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n"
- "<html>\n"
- "<head><title>Test ~p results</title>\n"
- "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n"
- "</head>\n"
- "<body bgcolor=\"white\" text=\"black\" "
- "link=\"blue\" vlink=\"purple\" alink=\"red\">"
- "<h2>Results from test ~p</h2>\n",
- [get(test_server_name),get(test_server_name)]),
+ Test = get(test_server_name),
+ test_server_sup:framework_call(report, [tests_start,{Test,N}]),
+
+ Header =
+ case test_server_sup:framework_call(overview_html_header, [Test], "") of
+ "" ->
+ TestName = lists:flatten(io_lib:format("~p", [Test])),
+ ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
+ "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n",
+ "<html>\n",
+ "<head><title>Test ", TestName, " results</title>\n",
+ "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n",
+ "</head>\n",
+ "<body bgcolor=\"white\" text=\"black\" ",
+ "link=\"blue\" vlink=\"purple\" alink=\"red\">",
+ "<h2>Results from test ", TestName, "</h2>\n"];
+ Html ->
+ ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
+ "<!-- autogenerated by '", atom_to_list(?MODULE), "'. -->\n" | Html]
+ end,
+ print(html, Header, []),
print_timestamp(html, "Test started at "),
print(html, "<p>Host:<br>\n"),
@@ -1636,14 +1710,14 @@ do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_list(TopCas
[erlang:system_info(version), code:root_dir()]),
case os:getenv("TEST_SERVER_FRAMEWORK") of
- false ->
+ FW when FW =:= false; FW =:= "undefined" ->
print(html, "<p>Target:<br>\n"),
print_who(TI#target_info.host, TI#target_info.username),
print(html, "<br>Used Erlang ~s in <tt>~s</tt>.\n",
[TI#target_info.version, TI#target_info.root_dir]);
_ ->
case test_server_sup:framework_call(target_info, []) of
- TargetInfo when is_list(TargetInfo),
+ TargetInfo when is_list(TargetInfo),
length(TargetInfo) > 0 ->
print(html, "<p>Target:<br>\n"),
print(html, "~s\n", [TargetInfo]);
@@ -1658,7 +1732,7 @@ do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_list(TopCas
[?suitelog_name,?coverlog_name]),
print(html,"<p>~s"
"<p>\n"
- "<table border=3 cellpadding=5>"
+ "<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">"
"<tr><th>Num</th><th>Module</th><th>Case</th><th>Log</th>"
"<th>Time</th><th>Result</th><th>Comment</th></tr>\n",
[print_if_known(N, {"Suite contains ~p test cases.\n",[N]},
@@ -1681,12 +1755,12 @@ do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_list(TopCas
print(major, "=otp_release ~s", [TI#target_info.otp_release]),
print(major, "=started ~s",
[lists:flatten(timestamp_get(""))]),
- run_test_cases(TestSpec, Config, MultiplyTimetrap)
+ run_test_cases(TestSpec, Config, TimetrapData)
end;
-do_test_cases(TopCase, SkipCases, Config, MultiplyTimetrap) ->
+do_test_cases(TopCase, SkipCases, Config, TimetrapSpec) ->
%% when not list(TopCase)
- do_test_cases([TopCase], SkipCases, Config, MultiplyTimetrap).
+ do_test_cases([TopCase], SkipCases, Config, TimetrapSpec).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -1741,8 +1815,8 @@ start_log_file() ->
ok.
make_html_link(LinkName, Target, Explanation) ->
- %% if possible use a relative reference�to�Target.
- TargetL = filename:split(Target),
+ %% if possible use a relative reference to Target.
+ TargetL = filename:split(Target),
PwdL = filename:split(filename:dirname(LinkName)),
Href = case lists:prefix(PwdL, TargetL) of
true ->
@@ -1782,7 +1856,7 @@ start_minor_log_file(Mod, Func) ->
start_minor_log_file1(Mod, Func, LogDir, AbsName);
{ok,_} -> %% special case, duplicate names
{_,S,Us} = now(),
- Name1_0 =
+ Name1_0 =
lists:flatten(io_lib:format("~s.~s.~w.~w~s", [Mod,Func,S,
trunc(Us/1000),
?html_ext])),
@@ -1853,7 +1927,7 @@ html_convert_modules(TestSpec, _Config) ->
%% Retrieve a list of modules out of the test spec.
html_isolate_modules(List) -> html_isolate_modules(List, sets:new()).
-
+
html_isolate_modules([], Set) -> sets:to_list(Set);
html_isolate_modules([{skip_case,_}|Cases], Set) ->
html_isolate_modules(Cases, Set);
@@ -1919,36 +1993,36 @@ add_init_and_end_per_suite([{make,_,_}=Case|Cases], LastMod, LastRef) ->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef)];
add_init_and_end_per_suite([{skip_case,{{Mod,all},_}}=Case|Cases], LastMod, LastRef)
when Mod =/= LastMod ->
- {PreCases, NextMod, NextRef} =
+ {PreCases, NextMod, NextRef} =
do_add_end_per_suite_and_skip(LastMod, LastRef, Mod),
PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef)];
add_init_and_end_per_suite([{skip_case,{{Mod,_},_}}=Case|Cases], LastMod, LastRef)
when Mod =/= LastMod ->
- {PreCases, NextMod, NextRef} =
+ {PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod),
PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef)];
add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_}}=Case|Cases], LastMod, LastRef)
when Mod =/= LastMod ->
- {PreCases, NextMod, NextRef} =
+ {PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod),
PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef)];
add_init_and_end_per_suite([{skip_case,_}=Case|Cases], LastMod, LastRef) ->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef)];
-add_init_and_end_per_suite([{conf,_,_,{Mod,_}}=Case|Cases], LastMod, LastRef)
+add_init_and_end_per_suite([{conf,_,_,{Mod,_}}=Case|Cases], LastMod, LastRef)
when Mod =/= LastMod ->
- {PreCases, NextMod, NextRef} =
+ {PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod),
PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef)];
add_init_and_end_per_suite([{conf,_,_,_}=Case|Cases], LastMod, LastRef) ->
[Case|add_init_and_end_per_suite(Cases, LastMod, LastRef)];
-add_init_and_end_per_suite([{Mod,_}=Case|Cases], LastMod, LastRef)
+add_init_and_end_per_suite([{Mod,_}=Case|Cases], LastMod, LastRef)
when Mod =/= LastMod ->
- {PreCases, NextMod, NextRef} =
+ {PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod),
PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef)];
add_init_and_end_per_suite([{Mod,_,_}=Case|Cases], LastMod, LastRef)
when Mod =/= LastMod ->
- {PreCases, NextMod, NextRef} =
+ {PreCases, NextMod, NextRef} =
do_add_init_and_end_per_suite(LastMod, LastRef, Mod),
PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef)];
add_init_and_end_per_suite([Case|Cases], LastMod, LastRef)->
@@ -1965,7 +2039,7 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod) ->
false -> code:load_file(Mod);
_ -> ok
end,
- {Init,NextMod,NextRef} =
+ {Init,NextMod,NextRef} =
case erlang:function_exported(Mod, init_per_suite, 1) of
true ->
Ref = make_ref(),
@@ -1973,15 +2047,15 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod) ->
false ->
{[],Mod,undefined}
end,
- Cases =
+ Cases =
if LastRef==undefined ->
Init;
LastRef==skipped_suite ->
Init;
true ->
- %% Adding end_per_suite here without checking if the
+ %% Adding end_per_suite here without checking if the
%% function is actually exported. This is because a
- %% conf case must have an end case - so if it doesn't
+ %% conf case must have an end case - so if it doesn't
%% exist, it will only fail...
[{conf,LastRef,[],{LastMod,end_per_suite}}|Init]
end,
@@ -1997,23 +2071,19 @@ do_add_end_per_suite_and_skip(LastMod, LastRef, Mod) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_cases(TestSpec, Config, MultiplyTimetrap) -> exit(Result)
+%% run_test_cases(TestSpec, Config, TimetrapData) -> exit(Result)
%%
%% If remote target, a socket connection is established.
%% Runs the specified tests, then displays/logs the summary.
-run_test_cases(TestSpec, Config, MultiplyTimetrap) ->
+run_test_cases(TestSpec, Config, TimetrapData) ->
maybe_open_job_sock(),
html_convert_modules(TestSpec, Config),
- %%! For readable tracing...
- %%! Config1 = [{data_dir,""},{priv_dir,""},{nodes,[]}],
- %%! run_test_cases_loop(TestSpec, [[]], MultiplyTimetrap, [], []),
+ run_test_cases_loop(TestSpec, [Config], TimetrapData, [], []),
- run_test_cases_loop(TestSpec, [Config], MultiplyTimetrap, [], []),
-
maybe_get_privdir(),
{AllSkippedN,UserSkipN,AutoSkipN,SkipStr} =
@@ -2060,10 +2130,10 @@ maybe_open_job_sock() ->
%% tar packet containing the privdir created by the test case.
maybe_get_privdir() ->
case get(test_server_ctrl_job_sock) of
- undefined ->
+ undefined ->
%% local target
ok;
- Sock ->
+ Sock ->
%% remote target
request(Sock, job_done),
gen_tcp:close(Sock)
@@ -2071,37 +2141,39 @@ maybe_get_privdir() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_cases_loop(TestCases, Config, MultiplyTimetrap, Mode, Status) -> ok
+%% run_test_cases_loop(TestCases, Config, TimetrapData, Mode, Status) -> ok
%% TestCases = [Test,...]
%% Config = [[{Key,Val},...],...]
+%% TimetrapData = {MultiplyTimetrap,ScaleTimetrap}
%% MultiplyTimetrap = integer() | infinity
+%% ScaleTimetrap = bool()
%% Mode = [{Ref,[Prop,..],StartTime}]
%% Ref = reference()
-%% Prop = {name,Name} | sequence | parallel |
+%% Prop = {name,Name} | sequence | parallel |
%% shuffle | {shuffle,Seed} |
-%% repeat | {repeat,N} |
+%% repeat | {repeat,N} |
%% repeat_until_all_ok | {repeat_until_all_ok,N} |
-%% repeat_until_any_ok | {repeat_until_any_ok,N} |
-%% repeat_until_any_fail | {repeat_until_any_fail,N} |
-%% repeat_until_all_fail | {repeat_until_all_fail,N}
+%% repeat_until_any_ok | {repeat_until_any_ok,N} |
+%% repeat_until_any_fail | {repeat_until_any_fail,N} |
+%% repeat_until_all_fail | {repeat_until_all_fail,N}
%% Status = [{Ref,{{Ok,Skipped,Failed},CopiedCases}}]
%% Ok = Skipped = Failed = [Case,...]
%%
%% Execute the TestCases under configuration Config. Config is a list
%% of lists, where hd(Config) holds the config tuples for the current
-%% conf case and tl(Config) is the data for the higher level conf cases.
-%% Config data is "inherited" from top to nested conf cases, but
+%% conf case and tl(Config) is the data for the higher level conf cases.
+%% Config data is "inherited" from top to nested conf cases, but
%% never the other way around. if length(Config) == 1, Config contains
%% only the initial config data for the suite.
%%
%% Test may be one of the following:
%%
-%% {conf,Ref,Props,{Mod,Func}} Mod:Func is a configuration modification
+%% {conf,Ref,Props,{Mod,Func}} Mod:Func is a configuration modification
%% function, call it with the current configuration as argument. It will
%% return a new configuration.
%%
-%% {make,Ref,{Mod,Func,Args}} Mod:Func is a make function, and it is called
-%% with the given arguments. This function will *always* be called on the host
+%% {make,Ref,{Mod,Func,Args}} Mod:Func is a make function, and it is called
+%% with the given arguments. This function will *always* be called on the host
%% - not on target.
%%
%% {Mod,Case} This is a normal test case. Determine the correct
@@ -2114,16 +2186,16 @@ maybe_get_privdir() ->
%% {skip_case,{conf,Ref,Case,Comment}} An init conf case gets skipped
%% by the user. This will also cause the end conf case to be skipped.
%% Note that it is not possible to skip an end conf case directly (it
-%% can only be skipped indirectly by a skipped init conf case). The
-%% comment (which gets printed in the log files) describes why the case
+%% can only be skipped indirectly by a skipped init conf case). The
+%% comment (which gets printed in the log files) describes why the case
%% was skipped.
%%
-%% {skip_case,{Case,Comment}} A normal test case skipped by the user.
-%% The comment (which gets printed in the log files) describes why the
+%% {skip_case,{Case,Comment}} A normal test case skipped by the user.
+%% The comment (which gets printed in the log files) describes why the
%% case was skipped.
%%
%% {auto_skip_case,{conf,Ref,Case,Comment},Mode} This is the result of
-%% an end conf case being automatically skipped due to a failing init
+%% an end conf case being automatically skipped due to a failing init
%% conf case. It could also be a nested conf case that gets skipped
%% because of a failed or skipped top level conf.
%%
@@ -2151,25 +2223,25 @@ maybe_get_privdir() ->
%% messages to the main process instead of writing the data to file
%% (only true for printouts to common log files).
%%
-%% If a conf group nested under a parallel group in the test
+%% If a conf group nested under a parallel group in the test
%% specification should be started, the 'test_server_common_io_handler'
%% value gets set also on the main process. This causes all printouts
-%% to common files - both from parallel test cases and from cases
+%% to common files - both from parallel test cases and from cases
%% executed by the main process - to all end up as messages in the
-%% inbox of the main process.
+%% inbox of the main process.
%%
%% During execution of a parallel group (or of a group nested under a
-%% parallel group), *any* new test case being started gets registered
+%% parallel group), *any* new test case being started gets registered
%% in a list saved in the dictionary with 'test_server_queued_io' as key.
%% When the top level parallel group is finished (only then can we be
%% sure all parallel test cases have finished and "reported in"), the
-%% list of test cases is traversed in order and printout messages from
-%% each process - including the main process - are handled in turn. See
+%% list of test cases is traversed in order and printout messages from
+%% each process - including the main process - are handled in turn. See
%% handle_test_case_io_and_status/0 for details.
%%
%% To be able to handle nested conf groups with different properties,
%% the Mode argument specifies a list of {Ref,Properties} tuples.
-%% The head of the Mode list at any given time identifies the group
+%% The head of the Mode list at any given time identifies the group
%% currently being processed. The tail of the list identifies groups
%% on higher level.
%%
@@ -2179,13 +2251,13 @@ maybe_get_privdir() ->
%%
%% A group nested under a parallel group will start executing in
%% parallel with previous (parallel) test cases (no matter what
-%% properties the nested group has). Test cases are however never
+%% properties the nested group has). Test cases are however never
%% executed in parallel with the start or end conf case of the same
%% group! Because of this, the test_server_ctrl loop waits at
%% the end conf of a group for all parallel cases to finish
%% before the end conf case actually executes. This has the effect
%% that it's only after a nested group has finished that any
-%% remaining parallel cases in the previous group get spawned (*).
+%% remaining parallel cases in the previous group get spawned (*).
%% Example (all parallel cases):
%%
%% group1_init |---->
@@ -2201,15 +2273,16 @@ maybe_get_privdir() ->
%%
run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
- Config, MultiplyTimetrap, Mode, Status) when Type==conf;
- Type==make ->
-
+ Config, TimetrapData, Mode, Status) when Type==conf;
+ Type==make ->
file:set_cwd(filename:dirname(get(test_server_dir))),
CurrIOHandler = get(test_server_common_io_handler),
+ ParentMode = tl(Mode),
+
%% check and update the mode for test case execution and io msg handling
case {curr_ref(Mode),check_props(parallel, Mode)} of
{Ref,Ref} ->
- case check_props(parallel, tl(Mode)) of
+ case check_props(parallel, ParentMode) of
false ->
%% this is a skipped end conf for a top level parallel group,
%% buffered io can be flushed
@@ -2217,24 +2290,24 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
set_io_buffering(undefined),
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, false, SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, tl(Mode),
+ run_test_cases_loop(Cases, Config, TimetrapData, ParentMode,
delete_status(Ref, Status));
_ ->
- %% this is a skipped end conf for a parallel group nested under a
+ %% this is a skipped end conf for a parallel group nested under a
%% parallel group (io buffering is active)
wait_for_cases(Ref),
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
case CurrIOHandler of
- {Ref,_} ->
+ {Ref,_} ->
%% current_io_handler was set by start conf of this
- %% group, so we can unset it now (no more io from main
+ %% group, so we can unset it now (no more io from main
%% process needs to be buffered)
set_io_buffering(undefined);
- _ ->
+ _ ->
ok
end,
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, tl(Mode),
+ run_test_cases_loop(Cases, Config, TimetrapData, ParentMode,
delete_status(Ref, Status))
end;
{Ref,false} ->
@@ -2242,7 +2315,31 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
%% nested under a parallel group
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, false, SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, tl(Mode),
+
+ %% Check if this group is auto skipped because of error in the init conf.
+ %% If so, check if the parent group is a sequence, and if it is, skip
+ %% all proceeding tests in that group.
+ GrName = get_name(Mode),
+ Cases1 =
+ case get_tc_results(Status) of
+ {_,_,Fails} when length(Fails) > 0 ->
+ case lists:member({group_result,GrName}, Fails) of
+ true ->
+ case check_prop(sequence, ParentMode) of
+ false ->
+ Cases;
+ ParentRef ->
+ Reason = {group_result,GrName,failed},
+ skip_cases_upto(ParentRef, Cases,
+ Reason, tc, Mode)
+ end;
+ false ->
+ Cases
+ end;
+ _ ->
+ Cases
+ end,
+ run_test_cases_loop(Cases1, Config, TimetrapData, ParentMode,
delete_status(Ref, Status));
{Ref,_} ->
%% this is a skipped end conf for a non-parallel group nested under
@@ -2250,22 +2347,22 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
case CurrIOHandler of
- {Ref,_} ->
+ {Ref,_} ->
%% current_io_handler was set by start conf of this
- %% group, so we can unset it now (no more io from main
+ %% group, so we can unset it now (no more io from main
%% process needs to be buffered)
set_io_buffering(undefined);
- _ ->
+ _ ->
ok
end,
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, tl(Mode),
+ run_test_cases_loop(Cases, Config, TimetrapData, tl(Mode),
delete_status(Ref, Status));
{_,false} ->
%% this is a skipped start conf for a group which is not nested
%% under a parallel group
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, false, SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, [conf(Ref,[])|Mode], Status);
+ run_test_cases_loop(Cases, Config, TimetrapData, [conf(Ref,[])|Mode], Status);
{_,Ref0} when is_reference(Ref0) ->
%% this is a skipped start conf for a group nested under a parallel group
%% and if this is the first nested group, io buffering must be activated
@@ -2276,19 +2373,19 @@ run_test_cases_loop([{auto_skip_case,{Type,Ref,Case,Comment},SkipMode}|Cases],
end,
{Mod,Func} = skip_case(auto, Ref, 0, Case, Comment, true, SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, [conf(Ref,[])|Mode], Status)
- end;
+ run_test_cases_loop(Cases, Config, TimetrapData, [conf(Ref,[])|Mode], Status)
+ end;
run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases],
- Config, MultiplyTimetrap, Mode, Status) ->
+ Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(auto, undefined, get(test_server_case_num)+1, Case, Comment,
(undefined /= get(test_server_common_io_handler)), SkipMode),
test_server_sup:framework_call(report, [tc_auto_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, Mode,
+ run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
run_test_cases_loop([{skip_case,{conf,Ref,Case,Comment}}|Cases0],
- Config, MultiplyTimetrap, Mode, Status) ->
+ Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(user, Ref, 0, Case, Comment,
(undefined /= get(test_server_common_io_handler))),
{Cases,Config1} =
@@ -2301,24 +2398,23 @@ run_test_cases_loop([{skip_case,{conf,Ref,Case,Comment}}|Cases0],
{skip_cases_upto(Ref, Cases0, Comment, conf, Mode),Config}
end,
test_server_sup:framework_call(report, [tc_user_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config1, MultiplyTimetrap, Mode,
+ run_test_cases_loop(Cases, Config1, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
run_test_cases_loop([{skip_case,{Case,Comment}}|Cases],
- Config, MultiplyTimetrap, Mode, Status) ->
+ Config, TimetrapData, Mode, Status) ->
{Mod,Func} = skip_case(user, undefined, get(test_server_case_num)+1, Case, Comment,
(undefined /= get(test_server_common_io_handler))),
test_server_sup:framework_call(report, [tc_user_skip,{?pl2a(Mod),Func,Comment}]),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, Mode,
+ run_test_cases_loop(Cases, Config, TimetrapData, Mode,
update_status(skipped, Mod, Func, Status));
%% a start *or* end conf case, wrapping test cases or other conf cases
-run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
- Config, MultiplyTimetrap, Mode0, Status) ->
-
+run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
+ Config, TimetrapData, Mode0, Status) ->
CurrIOHandler = get(test_server_common_io_handler),
%% check and update the mode for test case execution and io msg handling
- {StartConf,Mode,IOHandler,ConfTime,Status1} =
+ {StartConf,Mode,IOHandler,ConfTime,Status1} =
case {curr_ref(Mode0),check_props(parallel, Mode0)} of
{Ref,Ref} ->
case check_props(parallel, tl(Mode0)) of
@@ -2334,19 +2430,19 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{false,tl(Mode0),undefined,Elapsed,
update_status(Ref, OkSkipFail, Status)};
_ ->
- %% this is an end conf for a parallel group nested under a
+ %% this is an end conf for a parallel group nested under a
%% parallel group (io buffering is active)
OkSkipFail = wait_for_cases(Ref),
queue_test_case_io(Ref, self(), 0, Mod, Func),
Elapsed = elapsed_time(conf_start(Ref, Mode0),?now)/1000000,
case CurrIOHandler of
- {Ref,_} ->
+ {Ref,_} ->
%% current_io_handler was set by start conf of this
%% group, so we can unset it after this case (no
%% more io from main process needs to be buffered)
{false,tl(Mode0),undefined,Elapsed,
update_status(Ref, OkSkipFail, Status)};
- _ ->
+ _ ->
{false,tl(Mode0),CurrIOHandler,Elapsed,
update_status(Ref, OkSkipFail, Status)}
end
@@ -2362,16 +2458,16 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
queue_test_case_io(Ref, self(), 0, Mod, Func),
Elapsed = elapsed_time(conf_start(Ref, Mode0),?now)/1000000,
case CurrIOHandler of
- {Ref,_} ->
+ {Ref,_} ->
%% current_io_handler was set by start conf of this
%% group, so we can unset it after this case (no
%% more io from main process needs to be buffered)
{false,tl(Mode0),undefined,Elapsed,Status};
- _ ->
+ _ ->
{false,tl(Mode0),CurrIOHandler,Elapsed,Status}
end;
{_,false} ->
- %% this is a start conf for a group which is not nested under a
+ %% this is a start conf for a group which is not nested under a
%% parallel group, check if this case starts a new parallel group
case lists:member(parallel, Props) of
true ->
@@ -2424,9 +2520,9 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
end;
NumStr ->
%% Ex: "123 456 789" or "123,456,789" -> {123,456,789}
- list_to_tuple([list_to_integer(NS) ||
+ list_to_tuple([list_to_integer(NS) ||
NS <- string:tokens(NumStr, [$ ,$:,$,])])
- end,
+ end,
{shuffle_cases(Ref, Cs0, UseSeed),{shuffle,UseSeed}}
end;
not StartConf ->
@@ -2440,17 +2536,19 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
if StartConf ->
case get_repeat(Props) of
undefined ->
- %% we *must* have a status entry for every conf since we
+ %% we *must* have a status entry for every conf since we
%% will continously update status with test case results
%% without knowing the Ref (but update hd(Status))
{false,new_status(Ref, Status1),Cases1,?void_fun};
- _ ->
+ {_RepType,N} when N =< 1 ->
+ {false,new_status(Ref, Status1),Cases1,?void_fun};
+ _ ->
{Copied,_} = copy_cases(Ref, make_ref(), Cs1),
{true,new_status(Ref, Copied, Status1),Cases1,?void_fun}
end;
not StartConf ->
RepVal = get_repeat(get_props(Mode0)),
- ReportStop =
+ ReportStop =
fun() ->
print(minor, "~n*** Stopping repeat operation ~w", [RepVal]),
print(1, "Stopping repeat operation ~w", [RepVal])
@@ -2461,21 +2559,23 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
case RepVal of
undefined ->
{false,EndStatus,Cases1,?void_fun};
+ {_RepType,N} when N =< 1 ->
+ {false,EndStatus,Cases1,?void_fun};
{repeat,_} ->
{true,EndStatus,CopiedCases++Cases1,?void_fun};
{repeat_until_all_ok,_} ->
{RestCs,Fun} = case get_tc_results(Status1) of
- {_,_,[]} ->
+ {_,_,[]} ->
{Cases1,ReportStop};
- _ ->
+ _ ->
{CopiedCases++Cases1,?void_fun}
end,
{true,EndStatus,RestCs,Fun};
{repeat_until_any_ok,_} ->
{RestCs,Fun} = case get_tc_results(Status1) of
- {Ok,_,_} when length(Ok) > 0 ->
+ {Ok,_,_Fails} when length(Ok) > 0 ->
{Cases1,ReportStop};
- _ ->
+ _ ->
{CopiedCases++Cases1,?void_fun}
end,
{true,EndStatus,RestCs,Fun};
@@ -2483,15 +2583,15 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
{RestCs,Fun} = case get_tc_results(Status1) of
{_,_,Fails} when length(Fails) > 0 ->
{Cases1,ReportStop};
- _ ->
+ _ ->
{CopiedCases++Cases1,?void_fun}
end,
{true,EndStatus,RestCs,Fun};
{repeat_until_all_fail,_} ->
{RestCs,Fun} = case get_tc_results(Status1) of
- {[],_,_} ->
+ {[],_,_} ->
{Cases1,ReportStop};
- _ ->
+ _ ->
{CopiedCases++Cases1,?void_fun}
end,
{true,EndStatus,RestCs,Fun}
@@ -2517,13 +2617,13 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
[{tc_group_properties,get_props(Mode0)},
{tc_group_result,[{ok,TcOk},{skipped,TcSkip},{failed,TcFail}]}]
end,
- ActualCfg =
+ ActualCfg =
update_config(hd(Config), [{priv_dir,get(test_server_priv_dir)},
{data_dir,get_data_dir(Mod)}] ++ CfgProps),
CurrMode = curr_mode(Ref, Mode0, Mode),
- ConfCaseResult = run_test_case(Ref, 0, Mod, Func, [ActualCfg], skip_init, target,
- MultiplyTimetrap, CurrMode),
+ ConfCaseResult = run_test_case(Ref, 0, Mod, Func, [ActualCfg], skip_init, target,
+ TimetrapData, CurrMode),
case ConfCaseResult of
{_,NewCfg,_} when Func == init_per_suite, is_list(NewCfg) ->
@@ -2533,8 +2633,8 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
[] ->
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases, [NewCfg|Config],
- MultiplyTimetrap, Mode, Status2);
+ run_test_cases_loop(Cases, [NewCfg|Config],
+ TimetrapData, Mode, Status2);
Bad ->
print(minor, "~n*** ~p returned bad elements in Config: ~p.~n",
[Func,Bad]),
@@ -2542,53 +2642,55 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
Cases2 = skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases2, Config, MultiplyTimetrap, Mode,
+ run_test_cases_loop(Cases2, Config, TimetrapData, Mode,
delete_status(Ref, Status2))
- end;
+ end;
{_,NewCfg,_} when StartConf, is_list(NewCfg) ->
print_conf_time(ConfTime),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases, [NewCfg|Config], MultiplyTimetrap, Mode, Status2);
+ run_test_cases_loop(Cases, [NewCfg|Config], TimetrapData, Mode, Status2);
{_,{framework_error,{FwMod,FwFunc},Reason},_} ->
print(minor, "~n*** ~p failed in ~p. Reason: ~p~n", [FwMod,FwFunc,Reason]),
print(1, "~p failed in ~p. Reason: ~p~n", [FwMod,FwFunc,Reason]),
exit(framework_error);
- {_,Fail,_} when element(1,Fail) == 'EXIT';
+ {_,Fail,_} when element(1,Fail) == 'EXIT';
element(1,Fail) == timetrap_timeout;
element(1,Fail) == failed ->
- {Cases2,Config1} =
+ {Cases2,Config1,Status3} =
if StartConf ->
ReportAbortRepeat(failed),
print(minor, "~n*** ~p failed.~n"
" Skipping all cases.", [Func]),
Reason = {failed,{Mod,Func,Fail}},
- {skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),Config};
+ {skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
+ Config,
+ update_status(failed, group_result, get_name(Mode),
+ delete_status(Ref, Status2))};
not StartConf ->
ReportRepeatStop(),
print_conf_time(ConfTime),
- {Cases,tl(Config)}
+ {Cases,tl(Config),delete_status(Ref, Status2)}
end,
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases2, Config1, MultiplyTimetrap, Mode,
- delete_status(Ref, Status2));
+ run_test_cases_loop(Cases2, Config1, TimetrapData, Mode, Status3);
{died,Why,_} when Func == init_per_suite ->
print(minor, "~n*** Unexpected exit during init_per_suite.~n", []),
Reason = {failed,{Mod,init_per_suite,Why}},
Cases2 = skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases2, Config, MultiplyTimetrap, Mode,
- delete_status(Ref, Status2));
+ run_test_cases_loop(Cases2, Config, TimetrapData, Mode,
+ delete_status(Ref, Status2));
{_,{Skip,Reason},_} when StartConf and ((Skip==skip) or (Skip==skipped)) ->
ReportAbortRepeat(skipped),
print(minor, "~n*** ~p skipped.~n"
" Skipping all cases.", [Func]),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
- Config, MultiplyTimetrap, Mode,
+ run_test_cases_loop(skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
+ Config, TimetrapData, Mode,
delete_status(Ref, Status2));
{_,{skip_and_save,Reason,_SavedConfig},_} when StartConf ->
ReportAbortRepeat(skipped),
@@ -2596,8 +2698,8 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
" Skipping all cases.", [Func]),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
- Config, MultiplyTimetrap, Mode,
+ run_test_cases_loop(skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
+ Config, TimetrapData, Mode,
delete_status(Ref, Status2));
{_,_Other,_} when Func == init_per_suite ->
print(minor, "~n*** init_per_suite failed to return a Config list.~n", []),
@@ -2605,61 +2707,77 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0,
Cases2 = skip_cases_upto(Ref, Cases, Reason, conf, CurrMode),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases2, Config, MultiplyTimetrap, Mode,
+ run_test_cases_loop(Cases2, Config, TimetrapData, Mode,
delete_status(Ref, Status2));
{_,_Other,_} when StartConf ->
print_conf_time(ConfTime),
set_io_buffering(IOHandler),
ReportRepeatStop(),
stop_minor_log_file(),
- run_test_cases_loop(Cases, [hd(Config)|Config], MultiplyTimetrap,
+ run_test_cases_loop(Cases, [hd(Config)|Config], TimetrapData,
Mode, Status2);
-
+
{_,_EndConfRetVal,Opts} ->
- %% check if return_group_result is set (ok, skipped or failed) and
- %% if so return the value to the group "above" so that result may be
- %% used for evaluating repeat_until_*
- Status3 =
+ %% Check if return_group_result is set (ok, skipped or failed) and
+ %% if so:
+ %% 1) *If* the parent group is a sequence, skip all proceeding tests
+ %% in that group.
+ %% 2) Return the value to the group "above" so that result may be
+ %% used for evaluating a 'repeat_until_*' property.
+ GrName = get_name(Mode0, Func),
+ {Cases2,Status3} =
case lists:keysearch(return_group_result, 1, Opts) of
+ {value,{_,failed}} ->
+ case {curr_ref(Mode),check_prop(sequence, Mode)} of
+ {ParentRef,ParentRef} ->
+ Reason = {group_result,GrName,failed},
+ {skip_cases_upto(ParentRef, Cases, Reason, tc, Mode),
+ update_status(failed, group_result, GrName,
+ delete_status(Ref, Status2))};
+ _ ->
+ {Cases,update_status(failed, group_result, GrName,
+ delete_status(Ref, Status2))}
+ end;
{value,{_,GroupResult}} ->
- update_status(GroupResult, group_result, Func,
- delete_status(Ref, Status2));
+ {Cases,update_status(GroupResult, group_result, GrName,
+ delete_status(Ref, Status2))};
false ->
- delete_status(Ref, Status2)
+ {Cases,update_status(ok, group_result, GrName,
+ delete_status(Ref, Status2))}
end,
print_conf_time(ConfTime),
ReportRepeatStop(),
set_io_buffering(IOHandler),
stop_minor_log_file(),
- run_test_cases_loop(Cases, tl(Config), MultiplyTimetrap, Mode, Status3)
+ run_test_cases_loop(Cases2, tl(Config), TimetrapData, Mode, Status3)
end;
-run_test_cases_loop([{make,Ref,{Mod,Func,Args}}|Cases0], Config, MultiplyTimetrap, Mode, Status) ->
- case run_test_case(Ref, 0, Mod, Func, Args, skip_init, host, MultiplyTimetrap) of
+run_test_cases_loop([{make,Ref,{Mod,Func,Args}}|Cases0], Config, TimetrapData, Mode, Status) ->
+ case run_test_case(Ref, 0, Mod, Func, Args, skip_init, host, TimetrapData) of
{_,Why={'EXIT',_},_} ->
print(minor, "~n*** ~p failed.~n"
" Skipping all cases.", [Func]),
Reason = {failed,{Mod,Func,Why}},
Cases = skip_cases_upto(Ref, Cases0, Reason, conf, Mode),
stop_minor_log_file(),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, Mode, Status);
+ run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status);
{_,_Whatever,_} ->
stop_minor_log_file(),
- run_test_cases_loop(Cases0, Config, MultiplyTimetrap, Mode, Status)
+ run_test_cases_loop(Cases0, Config, TimetrapData, Mode, Status)
end;
-run_test_cases_loop([{conf,_Ref,_Props,_X}=Conf|_Cases0],
- Config, _MultiplyTimetrap, _Mode, _Status) ->
+run_test_cases_loop([{conf,_Ref,_Props,_X}=Conf|_Cases0],
+ Config, _TimetrapData, _Mode, _Status) ->
erlang:error(badarg, [Conf,Config]);
-run_test_cases_loop([{Mod,Case}|Cases], Config, MultiplyTimetrap, Mode, Status) ->
- ActualCfg =
+run_test_cases_loop([{Mod,Case}|Cases], Config, TimetrapData, Mode, Status) ->
+ ActualCfg =
update_config(hd(Config), [{priv_dir,get(test_server_priv_dir)},
{data_dir,get_data_dir(Mod)}]),
run_test_cases_loop([{Mod,Case,[ActualCfg]}|Cases], Config,
- MultiplyTimetrap, Mode, Status);
+ TimetrapData, Mode, Status);
-run_test_cases_loop([{Mod,Func,Args}|Cases], Config, MultiplyTimetrap, Mode, Status) ->
+run_test_cases_loop([{Mod,Func,Args}|Cases], Config, TimetrapData, Mode, Status) ->
Num = put(test_server_case_num, get(test_server_case_num)+1),
%% check the current execution mode and save info about the case if
%% detected that printouts to common log files is handled later
@@ -2669,15 +2787,15 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, MultiplyTimetrap, Mode, Sta
undefined ->
%% io printouts are written to straight to file
ok;
- _ ->
+ _ ->
%% io messages are buffered, put test case in queue
queue_test_case_io(undefined, self(), Num+1, Mod, Func)
end;
_ ->
ok
end,
- case run_test_case(undefined, Num+1, Mod, Func, Args,
- run_init, target, MultiplyTimetrap, Mode) of
+ case run_test_case(undefined, Num+1, Mod, Func, Args,
+ run_init, target, TimetrapData, Mode) of
%% callback to framework module failed, exit immediately
{_,{framework_error,{FwMod,FwFunc},Reason},_} ->
print(minor, "~n*** ~p failed in ~p. Reason: ~p~n", [FwMod,FwFunc,Reason]),
@@ -2688,50 +2806,50 @@ run_test_cases_loop([{Mod,Func,Args}|Cases], Config, MultiplyTimetrap, Mode, Sta
{Time,RetVal,_} ->
{Failed,Status1} =
case Time of
- died ->
+ died ->
{true,update_status(failed, Mod, Func, Status)};
_ when is_tuple(RetVal) ->
case element(1, RetVal) of
- R when R=='EXIT'; R==failed ->
+ R when R=='EXIT'; R==failed ->
{true,update_status(failed, Mod, Func, Status)};
- R when R==skip; R==skipped ->
+ R when R==skip; R==skipped ->
{false,update_status(skipped, Mod, Func, Status)};
- _ ->
+ _ ->
{false,update_status(ok, Mod, Func, Status)}
end;
- _ ->
+ _ ->
{false,update_status(ok, Mod, Func, Status)}
- end,
+ end,
case check_prop(sequence, Mode) of
false ->
stop_minor_log_file(),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, Mode, Status1);
- Ref ->
- %% the case is in a sequence; we must check the result and
+ run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status1);
+ Ref ->
+ %% the case is in a sequence; we must check the result and
%% determine if the following cases should run or be skipped
if not Failed -> % proceed with next case
stop_minor_log_file(),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, Mode, Status1);
+ run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status1);
true -> % skip rest of cases in sequence
print(minor, "~n*** ~p failed.~n"
" Skipping all other cases in sequence.", [Func]),
Reason = {failed,{Mod,Func}},
Cases2 = skip_cases_upto(Ref, Cases, Reason, tc, Mode),
stop_minor_log_file(),
- run_test_cases_loop(Cases2, Config, MultiplyTimetrap, Mode, Status1)
+ run_test_cases_loop(Cases2, Config, TimetrapData, Mode, Status1)
end
end;
%% the test case is being executed in parallel with the main process (and
%% other test cases) and Pid is the dedicated process executing the case
Pid ->
- %% io from Pid will be buffered in the main process inbox and handled
+ %% io from Pid will be buffered in the main process inbox and handled
%% later, so we have to save info about the case
queue_test_case_io(undefined, Pid, Num+1, Mod, Func),
- run_test_cases_loop(Cases, Config, MultiplyTimetrap, Mode, Status)
+ run_test_cases_loop(Cases, Config, TimetrapData, Mode, Status)
end;
%% TestSpec processing finished
-run_test_cases_loop([], _Config, _MultiplyTimetrap, _, _) ->
+run_test_cases_loop([], _Config, _TimetrapData, _, _) ->
ok.
%%--------------------------------------------------------------------
@@ -2765,7 +2883,10 @@ get_copied_cases([{_,{_,Cases}} | _Status]) ->
Cases.
get_tc_results([{_,{OkSkipFail,_}} | _Status]) ->
- OkSkipFail.
+ OkSkipFail;
+get_tc_results([]) -> % in case init_per_suite crashed
+ {[],[],[]}.
+
conf(Ref, Props) ->
{Ref,Props,?now}.
@@ -2798,12 +2919,18 @@ check_props(Attrib, Mode) ->
case [R || {R,Ps,_} <- Mode, lists:member(Attrib, Ps)] of
[] -> false;
[Ref|_] -> Ref
- end.
+ end.
+
+get_name(Mode, Def) ->
+ case get_name(Mode) of
+ undefined -> Def;
+ Name -> Name
+ end.
get_name([{_Ref,Props,_}|_]) ->
proplists:get_value(name, Props);
get_name([]) ->
- undefined.
+ undefined.
conf_start(Ref, Mode) ->
case lists:keysearch(Ref, 1, Mode) of
@@ -2826,10 +2953,10 @@ print_conf_time(0) ->
print_conf_time(ConfTime) ->
print(major, "=group_time ~.3fs", [ConfTime]),
print(minor, "~n=== Total execution time of group: ~.3fs~n", [ConfTime]).
-
-print_props(_, []) ->
+
+print_props(_, []) ->
ok;
-print_props(true, Props) ->
+print_props(true, Props) ->
print(major, "=group_props ~p", [Props]),
print(minor, "Group properties: ~p~n", [Props]);
print_props(_, _) ->
@@ -2853,12 +2980,12 @@ update_repeat(Props) ->
Props1 =
if N == forever ->
[{RepType,N}|lists:keydelete(RepType, 1, Props)];
- N < 2 ->
+ N < 3 ->
lists:keydelete(RepType, 1, Props);
- N >= 2 ->
+ N >= 3 ->
[{RepType,N-1}|lists:keydelete(RepType, 1, Props)]
end,
- %% if shuffle is used in combination with repeat, a new
+ %% if shuffle is used in combination with repeat, a new
%% seed shouldn't be set every new turn
case get_shuffle(Props1) of
undefined ->
@@ -2874,13 +3001,13 @@ get_shuffle(Props) ->
delete_shuffle(Props) ->
delete_prop([shuffle], Props).
-%% Return {Item,Value} if found, else if Item alone
+%% Return {Item,Value} if found, else if Item alone
%% is found, return {Item,Default}
get_prop([Item|Items], Default, Props) ->
case lists:keysearch(Item, 1, Props) of
- {value,R} ->
+ {value,R} ->
R;
- false ->
+ false ->
case lists:member(Item, Props) of
true ->
{Item,Default};
@@ -2940,8 +3067,8 @@ random_order(1, {_Pos,Seed}, [{_Ix,CaseOrGroup}], Shuffled) ->
put(test_server_curr_random_seed, Seed),
Shuffled++CaseOrGroup;
random_order(N, {Pos,NewSeed}, IxCases, Shuffled) ->
- {First,[{_Ix,CaseOrGroup}|Rest]} = lists:split(Pos-1, IxCases),
- random_order(N-1, random:uniform_s(N-1, NewSeed),
+ {First,[{_Ix,CaseOrGroup}|Rest]} = lists:split(Pos-1, IxCases),
+ random_order(N-1, random:uniform_s(N-1, NewSeed),
First++Rest, Shuffled++CaseOrGroup).
@@ -2949,7 +3076,7 @@ random_order(N, {Pos,NewSeed}, IxCases, Shuffled) ->
%% skip_case(Type, Ref, CaseNum, Case, Comment, SendSync) -> {Mod,Func}
%%
%% Prints info about a skipped case in the major and html log files.
-%% SendSync determines if start and finished messages must be sent so
+%% SendSync determines if start and finished messages must be sent so
%% that the printouts can be buffered and handled in order with io from
%% parallel processes.
@@ -2969,13 +3096,13 @@ skip_case(Type, Ref, CaseNum, Case, Comment, SendSync, Mode) ->
not SendSync ->
skip_case1(Type, CaseNum, Mod, Func, Comment, Mode)
end,
- MF.
+ MF.
skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) ->
{{Col0,Col1},_} = get_font_style((CaseNum > 0), Mode),
ResultCol = if Type == auto -> "#ffcc99";
Type == user -> "#ff9933"
- end,
+ end,
Comment1 = reason_to_string(Comment),
@@ -3084,7 +3211,7 @@ modify_cases_upto1(Ref, ModOp, [{skip_case,{_F,_Cmt}}=MF|T], Orig, Alt) ->
%% next is a normal case (possibly in a sequence), mark as skipped, or copy, and proceed
modify_cases_upto1(Ref, {skip,Reason,_,Mode}=Op, [{_M,_F}=MF|T], Orig, Alt) ->
- modify_cases_upto1(Ref, Op, T, Orig, [{auto_skip_case,{MF,Reason},Mode}|Alt]);
+ modify_cases_upto1(Ref, Op, T, Orig, [{auto_skip_case,{MF,Reason},Mode}|Alt]);
modify_cases_upto1(Ref, CopyOp, [{_M,_F}=MF|T], Orig, Alt) ->
modify_cases_upto1(Ref, CopyOp, T, [MF|Orig], [MF|Alt]);
@@ -3110,7 +3237,7 @@ set_io_buffering(IOHandler) ->
%% queue_test_case_io(Pid, Num, Mod, Func) -> ok
%%
%% Save info about test case that gets its io buffered. This can
-%% be a parallel test case or it can be a test case (conf or normal)
+%% be a parallel test case or it can be a test case (conf or normal)
%% that belongs to a group nested under a parallel group. The queue
%% is processed after io buffering is disabled. See run_test_cases_loop/4
%% and handle_test_case_io_and_status/0 for more info.
@@ -3124,10 +3251,10 @@ queue_test_case_io(Ref, Pid, Num, Mod, Func) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% wait_for_cases(Ref) -> {Ok,Skipped,Failed}
%%
-%% At the end of a nested parallel group, we have to wait for the test
+%% At the end of a nested parallel group, we have to wait for the test
%% cases to terminate before we can go on (since test cases never execute
-%% in parallel with the end conf case of the group). When a top level
-%% parallel group is finished, buffered io messages must be handled and
+%% in parallel with the end conf case of the group). When a top level
+%% parallel group is finished, buffered io messages must be handled and
%% this is taken care of by handle_test_case_io_and_status/0.
wait_for_cases(Ref) ->
@@ -3135,15 +3262,15 @@ wait_for_cases(Ref) ->
[] ->
{[],[],[]};
Cases ->
- [_Start|TCs] =
+ [_Start|TCs] =
lists:dropwhile(fun({R,_,_,_,_}) when R == Ref -> false;
(_) -> true
end, Cases),
wait_and_resend(Ref, TCs, [],[],[])
end.
-wait_and_resend(Ref, [{OtherRef,_,0,_,_}|Ps],
- Ok,Skip,Fail) when is_reference(OtherRef),
+wait_and_resend(Ref, [{OtherRef,_,0,_,_}|Ps],
+ Ok,Skip,Fail) when is_reference(OtherRef),
OtherRef /= Ref ->
%% ignore cases that belong to nested group
Ps1 = rm_cases_upto(OtherRef, Ps),
@@ -3152,7 +3279,7 @@ wait_and_resend(Ref, [{OtherRef,_,0,_,_}|Ps],
wait_and_resend(Ref, [{_,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
receive
{finished,_Ref,CurrPid,CaseNum,Mod,Func,Result,_RetVal} = Msg ->
- %% resend message to main process so that it can be used
+ %% resend message to main process so that it can be used
%% to handle buffered io messages later
self() ! Msg,
MF = {Mod,Func},
@@ -3163,7 +3290,7 @@ wait_and_resend(Ref, [{_,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
failed -> {Ok,Skip,[MF|Fail]}
end,
wait_and_resend(Ref, Ps, Ok1,Skip1,Fail1);
- {'EXIT',CurrPid,Reason} when Reason /= normal ->
+ {'EXIT',CurrPid,Reason} when Reason /= normal ->
%% unexpected termination of test case process
{value,{_,_,CaseNum,Mod,Func}} = lists:keysearch(CurrPid, 2, Cases),
print(1, "Error! Process for test case #~p (~p:~p) died! Reason: ~p",
@@ -3186,17 +3313,17 @@ rm_cases_upto(Ref, [_|Ps]) ->
%% execution. The common log files (major, html etc) must however be
%% written to sequentially. The test case processes send print requests
%% to the main (starting) process (the same process executing
-%% run_test_cases_loop/4), which handles these requests in the same
+%% run_test_cases_loop/4), which handles these requests in the same
%% order that the test case processes were started.
%%
%% An io session is always started with a {started,Ref,Pid,Num,Mod,Func}
%% message and terminated with {finished,Ref,Pid,Num,Mod,Func,Result,RetVal}.
%% The result shipped with the finished message from a parallel process
-%% is used to update status data of the current test run. An 'EXIT'
-%% message from each parallel test case process (after finishing and
+%% is used to update status data of the current test run. An 'EXIT'
+%% message from each parallel test case process (after finishing and
%% terminating) is also received and handled here.
%%
-%% During execution of a parallel group, any cases (conf or normal)
+%% During execution of a parallel group, any cases (conf or normal)
%% belonging to a nested group will also get its io printouts buffered.
%% This is necessary to get the major and html log files written in
%% correct sequence. This function handles also the print messages
@@ -3207,7 +3334,7 @@ rm_cases_upto(Ref, [_|Ps]) ->
%% See the header comment for run_test_cases_loop/4 for more
%% info about IO handling.
%%
-%% Note: It is important that the type of messages handled here
+%% Note: It is important that the type of messages handled here
%% do not get consumated by test_server:run_test_case_msgloop/5
%% during the test case execution (e.g. in the catch clause of
%% the receive)!
@@ -3231,7 +3358,7 @@ handle_test_case_io_and_status() ->
ok
end, Cases),
Result
- end.
+ end.
%% Handle cases (without Ref) that belong to the top parallel group (i.e. when Refs = [])
handle_io_and_exit_loop([], [{undefined,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
@@ -3249,7 +3376,7 @@ handle_io_and_exit_loop([], [{undefined,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, O
1000 ->
exit({testcase_failed_to_start,Mod,Func})
end;
-
+
%% Handle cases that belong to groups nested under top parallel group
handle_io_and_exit_loop(Refs, [{Ref,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) ->
receive
@@ -3269,7 +3396,7 @@ handle_io_and_exit_loop(Refs, [{Ref,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Sk
1000 ->
exit({testcase_failed_to_start,Mod,Func})
end;
-
+
handle_io_and_exit_loop(_, [], Ok,Skip,Fail) ->
{lists:reverse(Ok),lists:reverse(Skip),lists:reverse(Fail)}.
@@ -3286,7 +3413,7 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
failed ->
put(test_server_failed, get(test_server_failed)+1);
skipped ->
- SkipCounters =
+ SkipCounters =
update_skip_counters(RetVal, get(test_server_skipped)),
put(test_server_skipped, SkipCounters)
end,
@@ -3298,7 +3425,7 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases);
%% unexpected termination of test case process
- {'EXIT',TCPid,Reason} when Reason /= normal ->
+ {'EXIT',TCPid,Reason} when Reason /= normal ->
{value,{_,_,Num,M,F}} = lists:keysearch(TCPid, 2, Cases),
print(1, "Error! Process for test case #~p (~p:~p) died! Reason: ~p",
[Num, M, F, Reason]),
@@ -3307,65 +3434,65 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_case(Ref, Num, Mod, Func, Args, RunInit,
-%% Where, MultiplyTimetrap, Mode) -> RetVal
+%% run_test_case(Ref, Num, Mod, Func, Args, RunInit,
+%% Where, TimetrapData, Mode) -> RetVal
%%
%% Creates the minor log file and inserts some test case specific headers
-%% and footers into the log files. If a remote target is used, the test
+%% and footers into the log files. If a remote target is used, the test
%% suite (binary) and the content of data_dir is sent. Then the test case
-%% is executed and the result is printed to the log files (also info
+%% is executed and the result is printed to the log files (also info
%% about lingering processes & slave nodes in the system is presented).
-%%
+%%
%% RunInit decides if the per test case init is to be run (true for all
%% but conf cases).
%%
-%% Where specifies if the test case should run on target or on the host.
+%% Where specifies if the test case should run on target or on the host.
%% (Note that 'make' test cases always run on host).
-%%
+%%
%% Mode specifies if the test case should be executed by a dedicated,
%% parallel, process rather than sequentially by the main process. If
%% the former, the new process is spawned and the dictionary of the main
%% process is copied to the test case process.
-%%
-%% RetVal is the result of executing the test case. It contains info
+%%
+%% RetVal is the result of executing the test case. It contains info
%% about the execution time and the return value of the test case function.
-run_test_case(Ref, Num, Mod, Func, Args, RunInit, Where, MultiplyTimetrap) ->
+run_test_case(Ref, Num, Mod, Func, Args, RunInit, Where, TimetrapData) ->
file:set_cwd(filename:dirname(get(test_server_dir))),
- run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- MultiplyTimetrap, [], [], self()).
+ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
+ TimetrapData, [], [], self()).
-run_test_case(Ref, Num, Mod, Func, Args, skip_init, Where, MultiplyTimetrap, Mode) ->
+run_test_case(Ref, Num, Mod, Func, Args, skip_init, Where, TimetrapData, Mode) ->
%% a conf case is always executed by the main process
- run_test_case1(Ref, Num, Mod, Func, Args, skip_init, Where,
- MultiplyTimetrap, [], Mode, self());
+ run_test_case1(Ref, Num, Mod, Func, Args, skip_init, Where,
+ TimetrapData, [], Mode, self());
-run_test_case(Ref, Num, Mod, Func, Args, RunInit, Where, MultiplyTimetrap, Mode) ->
+run_test_case(Ref, Num, Mod, Func, Args, RunInit, Where, TimetrapData, Mode) ->
file:set_cwd(filename:dirname(get(test_server_dir))),
case check_prop(parallel, Mode) of
false ->
%% this is a sequential test case
- run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- MultiplyTimetrap, [], Mode, self());
+ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
+ TimetrapData, [], Mode, self());
_Ref ->
%% this a parallel test case, spawn the new process
Main = self(),
- {dictionary,State} = process_info(self(), dictionary),
+ {dictionary,State} = process_info(self(), dictionary),
spawn_link(fun() ->
- run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- MultiplyTimetrap, State, Mode, Main)
- end)
+ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
+ TimetrapData, State, Mode, Main)
+ end)
end.
-run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
- MultiplyTimetrap, State, Mode, Main) ->
- %% if this runs on a parallel test case process,
+run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
+ TimetrapData, State, Mode, Main) ->
+ %% if this runs on a parallel test case process,
%% copy the dictionary from the main process
do_if_parallel(Main, fun() -> process_flag(trap_exit, true) end, ok),
CopyDict = fun() -> lists:foreach(fun({Key,Val}) -> put(Key, Val) end, State) end,
do_if_parallel(Main, CopyDict, ok),
do_if_parallel(Main, fun() -> put(test_server_common_io_handler, {tc,Main}) end, ok),
- %% if io is being buffered, send start io session message
+ %% if io is being buffered, send start io session message
%% (no matter if case runs on parallel or main process)
case get(test_server_common_io_handler) of
undefined -> ok;
@@ -3373,7 +3500,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
end,
TSDir = get(test_server_dir),
case Where of
- target ->
+ target ->
maybe_send_beam_and_datadir(Mod);
host ->
ok
@@ -3396,8 +3523,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
do_if_parallel(Main, ok, fun erlang:yield/0),
%% run the test case
{Result,DetectedFail,ProcsBefore,ProcsAfter} =
- run_test_case_apply(Num, Mod, Func, Args, get_name(Mode),
- RunInit, Where, MultiplyTimetrap),
+ run_test_case_apply(Num, Mod, Func, Args, get_name(Mode),
+ RunInit, Where, TimetrapData),
{Time,RetVal,Loc,Opts,Comment} =
case Result of
Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal;
@@ -3409,7 +3536,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
print(major, "=ended ~s", [lists:flatten(timestamp_get(""))]),
do_if_parallel(Main, ok, fun() -> file:set_cwd(filename:dirname(TSDir)) end),
-
+
%% call the appropriate progress function clause to print the results to log
Status =
case {Time,RetVal} of
@@ -3423,16 +3550,16 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
progress(skip, Num, Mod, Func, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',_Pid,{Skip,Reason}}} when Skip==skip; Skip==skipped ->
- progress(skip, Num, Mod, Func, Loc, Reason,
+ progress(skip, Num, Mod, Func, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',_Pid,Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, Loc, Reason,
Time, Comment, Style);
{_,{'EXIT',Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ progress(failed, Num, Mod, Func, Loc, Reason,
Time, Comment, Style);
- {_, {failed, Reason}} ->
- progress(failed, Num, Mod, Func, Loc, Reason,
+ {_, {Fail, Reason}} when Fail =:= fail; Fail =:= failed ->
+ progress(failed, Num, Mod, Func, Loc, Reason,
Time, Comment, Style);
{_, {Skip, Reason}} when Skip==skip; Skip==skipped ->
progress(skip, Num, Mod, Func, Loc, Reason,
@@ -3442,7 +3569,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
[] ->
progress(ok, Num, Mod, Func, Loc, RetVal,
Time, Comment, Style);
-
+
Reason ->
progress(failed, Num, Mod, Func, Loc, Reason,
Time, Comment, Style)
@@ -3465,18 +3592,18 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
{US,AS} = get(test_server_skipped),
put(test_server_skipped, {US,AS+1})
end,
- %% only if test case execution is sequential do we care about the
+ %% only if test case execution is sequential do we care about the
%% remaining processes and slave nodes count
case self() of
Main ->
case test_server_sup:framework_call(warn, [processes], true) of
true ->
if ProcsBefore < ProcsAfter ->
- print(minor,
+ print(minor,
"WARNING: ~w more processes in system after test case",
[ProcsAfter-ProcsBefore]);
ProcsBefore > ProcsAfter ->
- print(minor,
+ print(minor,
"WARNING: ~w less processes in system after test case",
[ProcsBefore-ProcsAfter]);
true -> ok
@@ -3487,13 +3614,13 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
case test_server_sup:framework_call(warn, [nodes], true) of
true ->
case catch controller_call(kill_slavenodes) of
- {'EXIT',_}=Exit ->
+ {'EXIT',_} = Exit ->
print(minor,
"WARNING: There might be slavenodes left in the"
" system. I tried to kill them, but I failed: ~p\n",
[Exit]);
[] -> ok;
- List ->
+ List ->
print(minor, "WARNING: ~w slave nodes in system after test"++
"case. Tried to killed them.~n"++
" Names:~p",
@@ -3505,8 +3632,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
_ ->
ok
end,
- %% if the test case was executed sequentially, this updates the execution
- %% time count on the main process (adding execution time of parallel test
+ %% if the test case was executed sequentially, this updates the execution
+ %% time count on the main process (adding execution time of parallel test
%% case groups is done in run_test_cases_loop/4)
if is_number(Time) ->
put(test_server_total_time, get(test_server_total_time)+Time);
@@ -3515,7 +3642,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
end,
check_new_crash_dumps(Where),
- %% if io is being buffered, send finished message
+ %% if io is being buffered, send finished message
%% (no matter if case runs on parallel or main process)
case get(test_server_common_io_handler) of
undefined -> ok;
@@ -3528,7 +3655,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where,
%%--------------------------------------------------------------------
%% various help functions
-%% Call If() if we're on parallel process, or
+%% Call If() if we're on parallel process, or
%% call Else() if we're on main process
do_if_parallel(Pid, If, Else) ->
case self() of
@@ -3536,7 +3663,7 @@ do_if_parallel(Pid, If, Else) ->
if is_function(Else) -> Else();
true -> Else
end;
- _ ->
+ _ ->
if is_function(If) -> If();
true -> If
end
@@ -3549,13 +3676,13 @@ num2str(N) -> integer_to_list(N).
%% and the content of datadir til target.
maybe_send_beam_and_datadir(Mod) ->
case get(test_server_ctrl_job_sock) of
- undefined ->
+ undefined ->
%% local target
ok;
JobSock ->
%% remote target
case get(test_server_downloaded_suites) of
- undefined ->
+ undefined ->
send_beam_and_datadir(Mod, JobSock),
put(test_server_downloaded_suites, [Mod]);
Suites ->
@@ -3571,10 +3698,10 @@ maybe_send_beam_and_datadir(Mod) ->
send_beam_and_datadir(Mod, JobSock) ->
case code:which(Mod) of
- non_existing ->
+ non_existing ->
io:format("** WARNING: Suite ~w could not be found on host\n",
[Mod]);
- BeamFile ->
+ BeamFile ->
send_beam(JobSock, Mod, BeamFile)
end,
DataDir = get_data_dir(Mod),
@@ -3589,7 +3716,7 @@ send_beam_and_datadir(Mod, JobSock) ->
ModsInDatadir = filelib:wildcard(Wc),
SendBeamFun = fun(X) -> send_beam(JobSock, X) end,
lists:foreach(SendBeamFun, ModsInDatadir),
- %% No need to send C code or makefiles since
+ %% No need to send C code or makefiles since
%% no compilation can be done on target anyway.
%% Compiled C code must exist on target.
%% Beam files are already sent as binaries.
@@ -3597,7 +3724,7 @@ send_beam_and_datadir(Mod, JobSock) ->
%% is to compile it.
Filter = fun("Makefile") -> false;
("Makefile.src") -> false;
- (Y) ->
+ (Y) ->
case filename:extension(Y) of
".c" -> false;
ObjExt -> false;
@@ -3611,7 +3738,7 @@ send_beam_and_datadir(Mod, JobSock) ->
Tarfile = "data_dir.tar.gz",
{ok,Tar} = erl_tar:open(Tarfile, [write,compressed]),
ShortDataDir = filename:basename(DataDir),
- AddTarFun =
+ AddTarFun =
fun(File) ->
Long = filename:join(DataDir, File),
Short = filename:join(ShortDataDir, File),
@@ -3628,11 +3755,11 @@ send_beam_and_datadir(Mod, JobSock) ->
send_beam(JobSock, BeamFile) ->
Mod=filename:rootname(filename:basename(BeamFile), code:objfile_extension()),
- send_beam(JobSock, list_to_atom(Mod), BeamFile).
+ send_beam(JobSock, list_to_atom(Mod), BeamFile).
send_beam(JobSock, Mod, BeamFile) ->
{ok,BeamBin} = file:read_file(BeamFile),
request(JobSock, {{beam,Mod,BeamFile}, BeamBin}).
-
+
check_new_crash_dumps(Where) ->
case Where of
target ->
@@ -3649,25 +3776,25 @@ check_new_crash_dumps(Where) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% progress(Result, CaseNum, Mod, Func, Location, Reason, Time,
+%% progress(Result, CaseNum, Mod, Func, Location, Reason, Time,
%% Comment, TimeFormat) -> Result
%%
%% Prints the result of the test case to log file.
%% Note: Strings that are to be written to the minor log must
%% be prefixed with "=== " here, or the indentation will be wrong.
-progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
+progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
Comment, {St0,St1}) ->
- {Reason1,{Color,Ret}} = if_auto_skip(Reason,
+ {Reason1,{Color,Ret}} = if_auto_skip(Reason,
fun() -> {"#ffcc99",auto_skip} end,
fun() -> {"#ff9933",skip} end),
print(major, "=result skipped", []),
- print(1, "*** SKIPPED *** ~s",
+ print(1, "*** SKIPPED *** ~s",
[get_info_str(Func, CaseNum, get(test_server_cases))]),
test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,
{skipped,Reason1}}]),
ReasonStr = reason_to_string(Reason1),
- ReasonStr1 = lists:flatten([string:strip(S,left) ||
+ ReasonStr1 = lists:flatten([string:strip(S,left) ||
S <- string:tokens(ReasonStr,[$\n])]),
ReasonStr2 =
if length(ReasonStr1) > 80 ->
@@ -3686,10 +3813,10 @@ progress(skip, CaseNum, Mod, Func, Loc, Reason, Time,
[Time,Color,ReasonStr2,Comment1]),
FormatLoc = test_server_sup:format_loc(Loc),
print(minor, "=== location ~s", [FormatLoc]),
- print(minor, "=== reason = ~s", [ReasonStr1]),
+ print(minor, "=== reason = ~s", [ReasonStr1]),
Ret;
-
-progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
+
+progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
Comment0, {St0,St1}) ->
print(major, "=result failed: timeout, ~p", [Loc]),
print(1, "*** FAILED *** ~s",
@@ -3699,23 +3826,23 @@ progress(failed, CaseNum, Mod, Func, Loc, timetrap_timeout, T,
{failed,timetrap_timeout}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{timetrap_timeout,~s}", [FormatLastLoc]),
- Comment =
+ Comment =
case Comment0 of
"" -> "<font color=\"red\">" ++ ErrorReason ++ "</font>";
- _ -> "<font color=\"red\">" ++ ErrorReason ++ "</font><br>" ++
+ _ -> "<font color=\"red\">" ++ ErrorReason ++ "</font><br>" ++
to_string(Comment0)
end,
- print(html,
+ print(html,
"<td>" ++ St0 ++ "~.3fs" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td>~s</td></tr>\n",
+ "<td>~s</td></tr>\n",
[T/1000,Comment]),
FormatLoc = test_server_sup:format_loc(Loc),
print(minor, "=== location ~s", [FormatLoc]),
print(minor, "=== reason = timetrap timeout", []),
failed;
-progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
+progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
Comment0, {St0,St1}) ->
print(major, "=result failed: testcase_aborted, ~p", [Loc]),
print(1, "*** FAILED *** ~s",
@@ -3725,23 +3852,23 @@ progress(failed, CaseNum, Mod, Func, Loc, {testcase_aborted,Reason}, _T,
{failed,testcase_aborted}}]),
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
ErrorReason = io_lib:format("{testcase_aborted,~s}", [FormatLastLoc]),
- Comment =
+ Comment =
case Comment0 of
"" -> "<font color=\"red\">" ++ ErrorReason ++ "</font>";
- _ -> "<font color=\"red\">" ++ ErrorReason ++ "</font><br>" ++
+ _ -> "<font color=\"red\">" ++ ErrorReason ++ "</font><br>" ++
to_string(Comment0)
end,
- print(html,
+ print(html,
"<td>" ++ St0 ++ "died" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td>~s</td></tr>\n",
+ "<td>~s</td></tr>\n",
[Comment]),
FormatLoc = test_server_sup:format_loc(Loc),
print(minor, "=== location ~s", [FormatLoc]),
print(minor, "=== reason = {testcase_aborted,~p}", [Reason]),
failed;
-progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
+progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~p", [Reason,unknown]),
print(1, "*** FAILED *** ~s",
@@ -3749,10 +3876,10 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
- true -> "~w"
+ true -> "~w"
end, [Time]),
ErrorReason = lists:flatten(io_lib:format("~p", [Reason])),
- ErrorReason1 = lists:flatten([string:strip(S,left) ||
+ ErrorReason1 = lists:flatten([string:strip(S,left) ||
S <- string:tokens(ErrorReason,[$\n])]),
ErrorReason2 =
if length(ErrorReason1) > 63 ->
@@ -3760,13 +3887,13 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
true ->
ErrorReason1
end,
- Comment =
+ Comment =
case Comment0 of
"" -> "<font color=\"red\">" ++ ErrorReason2 ++ "</font>";
- _ -> "<font color=\"red\">" ++ ErrorReason2 ++ "</font><br>" ++
+ _ -> "<font color=\"red\">" ++ ErrorReason2 ++ "</font><br>" ++
to_string(Comment0)
end,
- print(html,
+ print(html,
"<td>" ++ St0 ++ "~s" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
"<td>~s</td></tr>\n",
@@ -3776,7 +3903,7 @@ progress(failed, CaseNum, Mod, Func, unknown, Reason, Time,
print(minor, "=== reason = "++FStr, [FormattedReason]),
failed;
-progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
+progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
Comment0, {St0,St1}) ->
print(major, "=result failed: ~p, ~p", [Reason,Loc]),
print(1, "*** FAILED *** ~s",
@@ -3784,18 +3911,18 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,
{failed,Reason}}]),
TimeStr = io_lib:format(if is_float(Time) -> "~.3fs";
- true -> "~w"
+ true -> "~w"
end, [Time]),
- Comment =
+ Comment =
case Comment0 of
"" -> "";
_ -> "<br>" ++ to_string(Comment0)
end,
FormatLastLoc = test_server_sup:format_loc(get_last_loc(Loc)),
- print(html,
+ print(html,
"<td>" ++ St0 ++ "~s" ++ St1 ++ "</td>"
"<td><font color=\"red\">FAILED</font></td>"
- "<td><font color=\"red\">~s</font>~s</td></tr>\n",
+ "<td><font color=\"red\">~s</font>~s</td></tr>\n",
[TimeStr,FormatLastLoc,Comment]),
FormatLoc = test_server_sup:format_loc(Loc),
print(minor, "=== location ~s", [FormatLoc]),
@@ -3803,7 +3930,7 @@ progress(failed, CaseNum, Mod, Func, Loc, Reason, Time,
print(minor, "=== reason = "++FStr, [FormattedReason]),
failed;
-progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time,
+progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time,
Comment0, {St0,St1}) ->
print(minor, "successfully completed test case", []),
test_server_sup:framework_call(report, [tc_done,{?pl2a(Mod),Func,ok}]),
@@ -3852,9 +3979,9 @@ get_info_str(Func, 0, _Cases) ->
get_info_str(_Func, CaseNum, unknown) ->
"test case " ++ integer_to_list(CaseNum);
get_info_str(_Func, CaseNum, Cases) ->
- "test case " ++ integer_to_list(CaseNum) ++
+ "test case " ++ integer_to_list(CaseNum) ++
" of " ++ integer_to_list(Cases).
-
+
print_if_known(Known, {SK,AK}, {SU,AU}) ->
{S,A} = if Known == unknown -> {SU,AU};
true -> {SK,AK}
@@ -3880,7 +4007,7 @@ reason_to_string({failed,{_,FailFunc,bad_return}}) ->
atom_to_list(FailFunc) ++ " bad return value";
reason_to_string({failed,{_,FailFunc,{timetrap_timeout,_}}}) ->
atom_to_list(FailFunc) ++ " timed out";
-reason_to_string(FWInitFail = {failed,{_CB,init_tc,_Reason}}) ->
+reason_to_string(FWInitFail = {failed,{_CB,init_tc,_Reason}}) ->
to_string(FWInitFail);
reason_to_string({failed,{_,FailFunc,_}}) ->
atom_to_list(FailFunc) ++ " failed";
@@ -3889,29 +4016,29 @@ reason_to_string(Other) ->
%get_font_style(Prop) ->
% {Col,St0,St1} = get_font_style1(Prop),
-% {{"<font color="++Col++">","</font>"},
+% {{"<font color="++Col++">","</font>"},
% {"<font color="++Col++">"++St0,St1++"</font>"}}.
-
+
get_font_style(NormalCase, Mode) ->
- Prop = if not NormalCase ->
+ Prop = if not NormalCase ->
default;
true ->
case check_prop(parallel, Mode) of
- false ->
+ false ->
case check_prop(sequence, Mode) of
- false ->
+ false ->
default;
- _ ->
+ _ ->
sequence
end;
- _ ->
+ _ ->
parallel
end
end,
{Col,St0,St1} = get_font_style1(Prop),
- {{"<font color="++Col++">","</font>"},
+ {{"<font color="++Col++">","</font>"},
{"<font color="++Col++">"++St0,St1++"</font>"}}.
-
+
get_font_style1(parallel) ->
{"\"darkslategray\"","<i>","</i>"};
get_font_style1(sequence) ->
@@ -3931,12 +4058,12 @@ get_font_style1(default) ->
%% The framework application can switch this feature off by setting
%% *its* application environment variable 'format_exception' to false.
%% It is also possible to switch formatting off by starting the
-%% test_server node with init argument 'test_server_format_exception'
+%% test_server node with init argument 'test_server_format_exception'
%% set to false.
format_exception(Reason={_Error,Stack}) when is_list(Stack) ->
case os:getenv("TEST_SERVER_FRAMEWORK") of
- false ->
+ FW when FW =:= false; FW =:= "undefined" ->
case application:get_env(test_server, format_exception) of
{ok,false} ->
{"~p",Reason};
@@ -3950,17 +4077,17 @@ format_exception(Reason={_Error,Stack}) when is_list(Stack) ->
_ ->
do_format_exception(Reason)
end
- end;
+ end;
format_exception(Error) ->
format_exception({Error,[]}).
do_format_exception(Reason={Error,Stack}) ->
StackFun = fun(_, _, _) -> false end,
- PF = fun(Term, I) ->
- io_lib:format("~." ++ integer_to_list(I) ++ "p", [Term])
+ PF = fun(Term, I) ->
+ io_lib:format("~." ++ integer_to_list(I) ++ "p", [Term])
end,
case catch lib:format_exception(1, error, Error, Stack, StackFun, PF) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
{"~p",Reason};
Formatted ->
Formatted1 = re:replace(Formatted, "exception error: ", "", [{return,list}]),
@@ -3969,8 +4096,8 @@ do_format_exception(Reason={Error,Stack}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
-%% Where, MultiplyTimetrap) ->
+%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit,
+%% Where, TimetrapData) ->
%% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} |
%% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter}
%% Name = atom()
@@ -3984,24 +4111,24 @@ do_format_exception(Reason={Error,Stack}) ->
%% ProcessesBefore = ProcessesAfter = integer()
%%
%% Where indicates if the test should run on target or always on the host.
-%%
-%% If test is to be run on target, and target is remote the request is
+%%
+%% If test is to be run on target, and target is remote the request is
%% sent over socket to target, and test_server runs the case and sends the
%% result back over the socket. Else test_server runs the case directly on host.
-run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, MultiplyTimetrap) ->
+run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, TimetrapData) ->
test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- MultiplyTimetrap});
-run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, MultiplyTimetrap) ->
+ TimetrapData});
+run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, TimetrapData) ->
case get(test_server_ctrl_job_sock) of
undefined ->
%% local target
test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,
- MultiplyTimetrap});
+ TimetrapData});
JobSock ->
%% remote target
request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit,
- MultiplyTimetrap}}),
+ TimetrapData}}),
read_job_sock_loop(JobSock)
end.
@@ -4012,15 +4139,15 @@ run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, MultiplyTim
%% Args = [term()]
%%
%% Just like io:format, except that depending on the Detail value, the output
-%% is directed to console, major and/or minor log files.
+%% is directed to console, major and/or minor log files.
%%
%% To handle printouts to common (not minor) log files from parallel test
%% case processes, the test_server_common_io_handler value is checked. If
%% set, the data is sent to the main controlling process. Note that test
%% cases that belong to a conf group nested under a parallel group will also
%% get its io data sent to main rather than immediately printed out, even
-%% if the test cases are executed by the same, main, process (ie the main
-%% process sends messages to itself then).
+%% if the test cases are executed by the same, main, process (ie the main
+%% process sends messages to itself then).
%%
%% Buffered io is handled by the handle_test_case_io_and_status/0 function.
@@ -4040,21 +4167,21 @@ print_or_buffer(Detail, Msg, Printer) ->
output({Detail,Msg}, Printer);
MinLevel when is_number(Detail), Detail >= MinLevel ->
output({Detail,Msg}, Printer);
- _ -> % Detail < Minor | major | html
+ _ -> % Detail < Minor | major | html
case get(test_server_common_io_handler) of
- undefined ->
+ undefined ->
output({Detail,Msg}, Printer);
{_,MainPid} ->
MainPid ! {print,self(),Detail,Msg}
end
- end.
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% print_timestamp(Detail, Leader) -> ok
%%
%% Prints Leader followed by a time stamp (date and time). Depending on
%% the Detail value, the output is directed to console, major and/or minor
-%% log files.
+%% log files.
print_timestamp(Detail, Leader) ->
print(Detail, timestamp_get(Leader), []).
@@ -4288,7 +4415,7 @@ update_config(Config, []) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% collect_cases(CurMod, TopCase, SkipList) ->
+%% collect_cases(CurMod, TopCase, SkipList) ->
%% BasicCaseList | {error,Reason}
%%
%% CurMod = atom()
@@ -4319,18 +4446,18 @@ update_config(Config, []) ->
%% are listed, and each Module:all(suite) is called
%% {dir,Dir,Pattern} All modules <Pattern>_SUITE in the named dir
%% are listed, and each Module:all(suite) is called
-%% {conf,InitMF,Cases,FinMF}
-%% {conf,Props,InitMF,Cases,FinMF}
+%% {conf,InitMF,Cases,FinMF}
+%% {conf,Props,InitMF,Cases,FinMF}
%% InitMF is placed in the BasicCaseList, then
%% Cases is treated according to this table, then
%% FinMF is placed in the BasicCaseList. InitMF
%% and FinMF are configuration manipulation
%% functions. See below.
-%% {make,InitMFA,Cases,FinMFA}
+%% {make,InitMFA,Cases,FinMFA}
%% InitMFA is placed in the BasicCaseList, then
%% Cases is treated according to this table, then
%% FinMFA is placed in the BasicCaseList. InitMFA
-%% and FinMFA are make/unmake functions. If InitMFA
+%% and FinMFA are make/unmake functions. If InitMFA
%% fails, Cases are not run. InitMFA and FinMFA are
%% always run on the host - not on target.
%%
@@ -4339,7 +4466,7 @@ update_config(Config, []) ->
%%
%% [] Leaf case
%% {req,ReqList} Kept for backwards compatibility - same as []
-%% {req,ReqList,Cases} Kept for backwards compatibility -
+%% {req,ReqList,Cases} Kept for backwards compatibility -
%% Cases parsed recursively with collect_cases/3
%% Cases (list) Recursively parsed with collect_cases/3
%%
@@ -4351,7 +4478,7 @@ update_config(Config, []) ->
%% Configuration manipulation functions are called with the current
%% configuration list as only argument, and are expected to return a new
%% configuration list. Such a pair of function may, for example, start a
-%% server and stop it after a serie of test cases.
+%% server and stop it after a serie of test cases.
%%
%% SkipCases is expected to be in the format:
%%
@@ -4364,10 +4491,10 @@ update_config(Config, []) ->
skip}). % skip list
collect_all_cases(Top, Skip) when is_list(Skip) ->
- Result =
+ Result =
case collect_cases(Top, #cc{mod=[],skip=Skip}) of
{ok,Cases,_St} -> Cases;
- Other -> Other
+ Other -> Other
end,
Result.
@@ -4379,12 +4506,12 @@ collect_cases([Case|Cs0], St0) ->
case collect_cases(Cs0, St1) of
{ok,FlatCases2,St} ->
{ok,FlatCases1 ++ FlatCases2,St};
- {error,_Reason}=Error -> Error
+ {error,_Reason} = Error -> Error
end;
- {error,_Reason}=Error -> Error
+ {error,_Reason} = Error -> Error
end;
-
+
collect_cases({module,Case}, St) when is_atom(Case), is_atom(St#cc.mod) ->
collect_case({St#cc.mod,Case}, St);
collect_cases({module,Mod,Case}, St) ->
@@ -4404,38 +4531,71 @@ collect_cases({conf,InitMF,CaseList,FinF}, St) when is_atom(FinF) ->
collect_cases({conf,InitMF,CaseList,FinMF}, St0) ->
collect_cases({conf,[],InitMF,CaseList,FinMF}, St0);
collect_cases({conf,Props,InitF,CaseList,FinMF}, St) when is_atom(InitF) ->
- collect_cases({conf,Props,{St#cc.mod,InitF},CaseList,FinMF}, St);
+ case init_props(Props) of
+ {error,_} ->
+ {ok,[],St};
+ Props1 ->
+ collect_cases({conf,Props1,{St#cc.mod,InitF},CaseList,FinMF}, St)
+ end;
collect_cases({conf,Props,InitMF,CaseList,FinF}, St) when is_atom(FinF) ->
- collect_cases({conf,Props,InitMF,CaseList,{St#cc.mod,FinF}}, St);
-collect_cases({conf,Props,InitMF,CaseList,FinMF}, St0) ->
- case collect_cases(CaseList, St0) of
- {ok,[],_St}=Empty ->
- Empty;
- {ok,FlatCases,St} ->
+ case init_props(Props) of
+ {error,_} ->
+ {ok,[],St};
+ Props1 ->
+ collect_cases({conf,Props1,InitMF,CaseList,{St#cc.mod,FinF}}, St)
+ end;
+collect_cases({conf,Props,InitMF,CaseList,FinMF} = Conf, St) ->
+ case init_props(Props) of
+ {error,_} ->
+ {ok,[],St};
+ Props1 ->
Ref = make_ref(),
- case in_skip_list(InitMF, St#cc.skip) of
- {true,Comment} ->
- {ok,[{skip_case,{conf,Ref,InitMF,Comment}} |
- FlatCases ++ [{conf,Ref,[],FinMF}]],St};
+ Skips = St#cc.skip,
+ case in_skip_list({St#cc.mod,Conf}, Skips) of
+ {true,Comment} -> % conf init skipped
+ {ok,[{skip_case,{conf,Ref,InitMF,Comment}} |
+ [] ++ [{conf,Ref,[],FinMF}]],St};
+ {true,Name,Comment} when is_atom(Name) -> % all cases skipped
+ {ok,[{skip_case,{{St#cc.mod,{group,Name}},Comment}}],St};
+ {true,ToSkip,_} when is_list(ToSkip) -> % some cases skipped
+ case collect_cases(CaseList,
+ St#cc{skip=ToSkip++Skips}) of
+ {ok,[],_St} = Empty ->
+ Empty;
+ {ok,FlatCases,St1} ->
+ {ok,[{conf,Ref,Props1,InitMF} |
+ FlatCases ++ [{conf,Ref,
+ keep_name(Props1),
+ FinMF}]],St1#cc{skip=Skips}};
+ {error,_Reason} = Error ->
+ Error
+ end;
false ->
- {ok,[{conf,Ref,Props,InitMF} |
- FlatCases ++ [{conf,Ref,keep_name(Props),FinMF}]],St}
- end;
- {error,_Reason}=Error ->
- Error
+ case collect_cases(CaseList, St) of
+ {ok,[],_St} = Empty ->
+ Empty;
+ {ok,FlatCases,St1} ->
+ {ok,[{conf,Ref,Props1,InitMF} |
+ FlatCases ++ [{conf,Ref,
+ keep_name(Props1),
+ FinMF}]],St1};
+ {error,_Reason} = Error ->
+ Error
+ end
+ end
end;
collect_cases({make,InitMFA,CaseList,FinMFA}, St0) ->
case collect_cases(CaseList, St0) of
- {ok,[],_St}=Empty -> Empty;
+ {ok,[],_St} = Empty -> Empty;
{ok,FlatCases,St} ->
Ref = make_ref(),
- {ok,[{make,Ref,InitMFA}|FlatCases ++
+ {ok,[{make,Ref,InitMFA}|FlatCases ++
[{make,Ref,FinMFA}]],St};
- {error,_Reason}=Error -> Error
+ {error,_Reason} = Error -> Error
end;
-collect_cases({Module, Cases}, St) when is_list(Cases) ->
+collect_cases({Module, Cases}, St) when is_list(Cases) ->
case (catch collect_case(Cases, St#cc{mod=Module}, [])) of
{ok, NewCases, NewSt} ->
{ok, NewCases, NewSt};
@@ -4450,8 +4610,11 @@ collect_cases({_Mod,_Case,_Args}=Spec, St) ->
collect_case(Spec, St);
collect_cases(Case, St) when is_atom(Case), is_atom(St#cc.mod) ->
collect_case({St#cc.mod,Case}, St);
-collect_cases(Other, _St) ->
- {error,{bad_subtest_spec,Other}}.
+collect_cases(Other, St) ->
+ {error,{bad_subtest_spec,St#cc.mod,Other}}.
+
+collect_case({Mod,{conf,_,_,_,_}=Conf}, St) ->
+ collect_case_invoke(Mod, Conf, [], St);
collect_case(MFA, St) ->
case in_skip_list(MFA, St#cc.skip) of
@@ -4473,11 +4636,11 @@ collect_case([Case | Cases], St, Acc) ->
collect_case_invoke(Mod, Case, MFA, St) ->
case os:getenv("TEST_SERVER_FRAMEWORK") of
- false ->
+ FW when FW =:= false; FW =:= "undefined" ->
case catch apply(Mod, Case, [suite]) of
- {'EXIT',_} ->
+ {'EXIT',_} ->
{ok,[MFA],St};
- Suite ->
+ Suite ->
collect_subcases(Mod, Case, MFA, St, Suite)
end;
_ ->
@@ -4485,9 +4648,10 @@ collect_case_invoke(Mod, Case, MFA, St) ->
collect_subcases(Mod, Case, MFA, St, Suite)
end.
-collect_subcases(Mod, Case, MFA, St, Suite) ->
+collect_subcases(Mod, Case, MFA, St, Suite) ->
case Suite of
[] when Case == all -> {ok,[],St};
+ [] when element(1, Case) == conf -> {ok,[],St};
[] -> {ok,[MFA],St};
%%%! --- START Kept for backwards compatibilty ---
%%%! Requirements are not used
@@ -4498,6 +4662,8 @@ collect_subcases(Mod, Case, MFA, St, Suite) ->
%%%! --- END Kept for backwards compatibilty ---
{Skip,Reason} when Skip==skip; Skip==skipped ->
{ok,[{skip_case,{MFA,Reason}}],St};
+ {error,Reason} ->
+ throw(Reason);
SubCases ->
collect_case_subcases(Mod, Case, SubCases, St)
end.
@@ -4536,7 +4702,7 @@ collect_case_deny(Mod, Case, MFA, ReqList, SubCases, St) ->
{granted,SubCases} ->
collect_case_subcases(Mod, Case, SubCases, St)
end.
-
+
check_deny([Req|Reqs], DenyList) ->
case check_deny_req(Req, DenyList) of
{denied,_Comment}=Denied -> Denied;
@@ -4559,8 +4725,49 @@ check_deny_req(Req, DenyList) ->
false -> granted
end.
+in_skip_list({Mod,{conf,Props,InitMF,_CaseList,_FinMF}}, SkipList) ->
+ case in_skip_list(InitMF, SkipList) of
+ {true,_} = Yes ->
+ Yes;
+ _ ->
+ case proplists:get_value(name, Props) of
+ undefined ->
+ false;
+ Name ->
+ ToSkip =
+ lists:flatmap(
+ fun({M,{conf,SProps,_,SCaseList,_},Cmt}) when
+ M == Mod ->
+ case proplists:get_value(name, SProps) of
+ all ->
+ [{M,all,Cmt}];
+ Name ->
+ case SCaseList of
+ all ->
+ [{M,all,Cmt}];
+ _ ->
+ [{M,F,Cmt} || F <- SCaseList]
+ end;
+ _ ->
+ []
+ end;
+ (_) ->
+ []
+ end, SkipList),
+ case ToSkip of
+ [] ->
+ false;
+ _ ->
+ case lists:keysearch(all, 2, ToSkip) of
+ {value,{_,_,Cmt}} -> {true,Name,Cmt};
+ _ -> {true,ToSkip,""}
+ end
+ end
+ end
+ end;
+
in_skip_list({Mod,Func,_Args}, SkipList) ->
- in_skip_list({Mod,Func}, SkipList);
+ in_skip_list({Mod,Func}, SkipList);
in_skip_list({Mod,Func}, [{Mod,Funcs,Comment}|SkipList]) when is_list(Funcs) ->
case lists:member(Func, Funcs) of
true ->
@@ -4577,9 +4784,21 @@ in_skip_list({Mod,Func}, [_|SkipList]) ->
in_skip_list(_, []) ->
false.
+%% remove unnecessary properties
+init_props(Props) ->
+ case get_repeat(Props) of
+ Repeat = {_RepType,N} when N < 2 ->
+ if N == 0 ->
+ {error,{invalid_property,Repeat}};
+ true ->
+ lists:delete(Repeat, Props)
+ end;
+ _ ->
+ Props
+ end.
+
keep_name(Props) ->
lists:filter(fun({name,_}) -> true; (_) -> false end, Props).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Target node handling functions %%
@@ -4615,13 +4834,13 @@ start_node(Name, Type, Options) ->
end,
case Warning of
[] -> ok;
- _ ->
+ _ ->
format(1, Warning),
format(minor, Warning)
end,
{ok, Nodename};
{fail,{Ret, Host, Cmd}} ->
- format(minor,
+ format(minor,
"Failed to start node ~p on ~p with command: ~p~n"
"Reason: ~p",
[Name, Host, Cmd, Ret]),
@@ -4630,7 +4849,7 @@ start_node(Name, Type, Options) ->
format(minor, "Failed to start node ~p: ~p", [Name,Ret]),
Ret;
{Ret, Host, Cmd} ->
- format(minor,
+ format(minor,
"Failed to start node ~p on ~p with command: ~p~n"
"Reason: ~p",
[Name, Host, Cmd, Ret]),
@@ -4685,7 +4904,7 @@ read_job_sock_loop(Sock) ->
exit({controller,connection_lost,Reason});
{ok,<<1,Request/binary>>} ->
case decode(binary_to_term(Request)) of
- ok ->
+ ok ->
read_job_sock_loop(Sock);
{stop,Result} ->
Result
@@ -4695,14 +4914,14 @@ read_job_sock_loop(Sock) ->
decode({apply,{M,F,A}}) ->
apply(M,F,A),
ok;
-decode({sync_apply,{M,F,A}}) ->
+decode({sync_apply,{M,F,A}}) ->
R = apply(M,F,A),
request(get(test_server_ctrl_job_sock),{sync_result,R}),
ok;
decode({sync_result,Result}) ->
{stop,Result};
decode({test_case_result,Result}) ->
- {stop,Result};
+ {stop,Result};
decode({privdir,empty_priv_dir}) ->
{stop,ok};
decode({{privdir,PrivDirTar},TarBin}) ->
@@ -4742,7 +4961,7 @@ p({A,B,C}) ->
p(X) ->
pinfo(X).
-t() ->
+t() ->
t(wall_clock).
t(X) ->
element(1, statistics(X)).
@@ -4781,7 +5000,7 @@ display_info([Pid|T], R, M) ->
Other ->
Other
end,
- Reds = fetch(reductions, Info),
+ Reds = fetch(reductions, Info),
LM = length(fetch(messages, Info)),
pformat(io_lib:format("~w", [Pid]),
io_lib:format("~w", [Call]),
@@ -4822,12 +5041,12 @@ pinfo(P) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% A module is included in the cover analysis if
-%% - it belongs to the tested application and is not listed in the
+%% - it belongs to the tested application and is not listed in the
%% {exclude,List} part of the App.cover file
%% - it does not belong to the application, but is listed in the
%% {include,List} part of the App.cover file
-%% - it does not belong to the application, but is listed in the
-%% cross.cover file (in the test_server application) under 'all'
+%% - it does not belong to the application, but is listed in the
+%% cross.cover file (in the test_server application) under 'all'
%% or under the tested application.
%%
%% The modules listed in the cross.cover file are modules that are
@@ -4893,7 +5112,7 @@ read_cover_file(CoverFile) ->
io:fwrite("Faulty format of CoverFile ~p\n", [CoverFile]),
{[],[]}
end;
- {error,Reason} ->
+ {error,Reason} ->
io:fwrite("Can't read CoverFile ~p\nReason: ~p\n",
[CoverFile,Reason]),
{[],[]}
@@ -4958,7 +5177,7 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
end,
io:fwrite(CoverLog, "<p>Excluded module(s): <code>~p</code>\n", [Excluded]),
-
+
Coverage = cover_analyse(Analyse, AnalyseMods),
case lists:filter(fun({_M,{_,_,_}}) -> false;
@@ -4968,7 +5187,7 @@ cover_analyse({App,CoverInfo}, Analyse, AnalyseMods, TestDir) ->
ok;
Bad ->
io:fwrite(CoverLog, "<p>Analysis failed for ~w module(s): "
- "<code>~w</code>\n",
+ "<code>~w</code>\n",
[length(Bad),[BadM || {BadM,{_,_Why}} <- Bad]])
end,
@@ -5002,10 +5221,10 @@ cross_cover_analyse(Analyse, CrossModules) ->
CoverdataFiles = get_coverdata_files(),
lists:foreach(fun(CDF) -> cover:import(CDF) end, CoverdataFiles),
io:fwrite("Cover analysing... ", []),
- DetailsFun =
+ DetailsFun =
case Analyse of
details ->
- fun(Dir,M) ->
+ fun(Dir,M) ->
OutFile = filename:join(Dir,
atom_to_list(M) ++
".CROSS_COVER.html"),
@@ -5018,7 +5237,7 @@ cross_cover_analyse(Analyse, CrossModules) ->
SortedModules =
case CrossModules of
undefined ->
- sort_modules([Mod || Mod <- get_all_cross_modules(),
+ sort_modules([Mod || Mod <- get_all_cross_modules(),
lists:member(Mod, cover:imported_modules())], []);
_ ->
sort_modules(CrossModules, [])
@@ -5031,7 +5250,7 @@ cross_cover_analyse(Analyse, CrossModules) ->
%% cross.cover, write a cross cover log (cross_cover.html).
write_cross_cover_logs([{App,Coverage}|T]) ->
case last_test_for_app(App) of
- false ->
+ false ->
ok;
Dir ->
CoverLogName = filename:join(Dir,?cross_coverlog_name),
@@ -5045,13 +5264,13 @@ write_cross_cover_logs([{App,Coverage}|T]) ->
end,
write_cross_cover_logs(T);
write_cross_cover_logs([]) ->
- io:fwrite("done\n", []).
+ io:fwrite("done\n", []).
%% Find all exported coverdata files. First find all the latest
%% run.<timestamp> directories, and the check if there is a file named
%% all.coverdata.
get_coverdata_files() ->
- PossibleFiles = [last_coverdata_file(Dir) ||
+ PossibleFiles = [last_coverdata_file(Dir) ||
Dir <- filelib:wildcard([$*|?logdir_ext]),
filelib:is_dir(Dir)],
[File || File <- PossibleFiles, filelib:is_file(File)].
@@ -5074,12 +5293,12 @@ last_test([_|Rest], Latest) ->
last_test(Rest, Latest);
last_test([], Latest) ->
Latest.
-
+
%% Sort modules according to the application they belong to.
%% Return [{App,LastTestDir,ModuleList}]
sort_modules([M|Modules], Acc) ->
App = get_app(M),
- Acc1 =
+ Acc1 =
case lists:keysearch(App, 1, Acc) of
{value,{App,LastTest,List}} ->
lists:keyreplace(App, 1, Acc, {App,LastTest,[M|List]});
@@ -5120,9 +5339,9 @@ get_all_cross_modules() ->
get_cross_modules(all).
get_cross_modules(App) ->
case file:consult(?cross_cover_file) of
- {ok,List} ->
+ {ok,List} ->
get_cross_modules(App, List, []);
- _X ->
+ _X ->
[]
end.
@@ -5134,11 +5353,11 @@ get_cross_modules(App, [_H|T], Acc) ->
get_cross_modules(App, T, Acc);
get_cross_modules(_App, [], Acc) ->
Acc.
-
+
%% Support functions for writing the cover logs (both cross and normal)
write_coverlog_header(CoverLog) ->
- case catch
+ case catch
io:fwrite(CoverLog,
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n"
"<!-- autogenerated by '~w'. -->\n"
@@ -5162,13 +5381,13 @@ format_analyse(M,Cov,NotCov,undefined) ->
io_lib:fwrite("<tr><td>~w</td>"
"<td align=right>~w %</td>"
"<td align=right>~w</td>"
- "<td align=right>~w</td></tr>\n",
+ "<td align=right>~w</td></tr>\n",
[M,pc(Cov,NotCov),Cov,NotCov]);
format_analyse(M,Cov,NotCov,{file,File}) ->
io_lib:fwrite("<tr><td><a href=\"~s\">~w</a></td>"
"<td align=right>~w %</td>"
"<td align=right>~w</td>"
- "<td align=right>~w</td></tr>\n",
+ "<td align=right>~w</td></tr>\n",
[filename:basename(File),M,pc(Cov,NotCov),Cov,NotCov]);
format_analyse(M,Cov,NotCov,{lines,Lines}) ->
CoverOutName = atom_to_list(M)++".COVER.html",
@@ -5177,15 +5396,15 @@ format_analyse(M,Cov,NotCov,{lines,Lines}) ->
io_lib:fwrite("<tr><td><a href=\"~s\">~w</a></td>"
"<td align=right>~w %</td>"
"<td align=right>~w</td>"
- "<td align=right>~w</td></tr>\n",
+ "<td align=right>~w</td></tr>\n",
[CoverOutName,M,pc(Cov,NotCov),Cov,NotCov]);
format_analyse(M,Cov,NotCov,{error,_}) ->
io_lib:fwrite("<tr><td>~w</td>"
"<td align=right>~w %</td>"
"<td align=right>~w</td>"
- "<td align=right>~w</td></tr>\n",
+ "<td align=right>~w</td></tr>\n",
[M,pc(Cov,NotCov),Cov,NotCov]).
-
+
pc(0,0) ->
0;
@@ -5200,9 +5419,9 @@ write_not_covered(CoverOut,M,Lines) ->
"<table border=3 cellpadding=5>\n"
"<th>Line Number</th>\n",
[M]),
- lists:foreach(fun({{_M,Line},{0,1}}) ->
+ lists:foreach(fun({{_M,Line},{0,1}}) ->
io:fwrite(CoverOut,"<tr><td>~w</td></tr>\n", [Line]);
- (_) ->
+ (_) ->
ok
end,
Lines),
@@ -5216,7 +5435,7 @@ write_default_coverlog(TestDir) ->
file:close(CoverLog).
write_default_cross_coverlog(TestDir) ->
- {ok,CrossCoverLog} =
+ {ok,CrossCoverLog} =
file:open(filename:join(TestDir,?cross_coverlog_name), [write]),
write_coverlog_header(CrossCoverLog),
io:fwrite(CrossCoverLog,
@@ -5232,7 +5451,7 @@ write_cover_result_table(CoverLog,Coverage) ->
"<th>Not covered (Lines)</th>\n",
[]),
{TotCov,TotNotCov} =
- lists:foldl(fun({M,{Cov,NotCov,Details}},{AccCov,AccNotCov}) ->
+ lists:foldl(fun({M,{Cov,NotCov,Details}},{AccCov,AccNotCov}) ->
Str = format_analyse(M,Cov,NotCov,Details),
io:fwrite(CoverLog,"~s", [Str]),
{AccCov+Cov,AccNotCov+NotCov};
diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl
index 49025b1a3d..1fd40d1dd9 100644
--- a/lib/test_server/src/test_server_node.erl
+++ b/lib/test_server/src/test_server_node.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -84,7 +84,7 @@ start_remote_main_target(Parameters) ->
MasterNode,MasterCookie),
Cmd =
case os:getenv("TEST_SERVER_FRAMEWORK") of
- false -> Cmd0;
+ FW when FW =:= false; FW =:= "undefined" -> Cmd0;
FW -> Cmd0 ++ " -env TEST_SERVER_FRAMEWORK " ++ FW
end,
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index 89edb0f881..1a614d74d5 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -21,7 +21,7 @@
%%% Purpose: Test server support functions.
%%%-------------------------------------------------------------------
-module(test_server_sup).
--export([timetrap/2, timetrap_cancel/1, capture_get/1, messages_get/1,
+-export([timetrap/2, timetrap/3, timetrap_cancel/1, capture_get/1, messages_get/1,
timecall/3, call_crash/5, app_test/2, check_new_crash_dumps/0,
cleanup_crash_dumps/0, crash_dump_dir/0, tar_crash_dumps/0,
get_username/0, get_os_family/0,
@@ -34,16 +34,23 @@
-define(src_listing_ext, ".src.html").
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% timetrap(Timeout,Pid) -> Handle
+%% timetrap(Timeout,Scale,Pid) -> Handle
%% Handle = term()
%%
%% Creates a time trap, that will kill the given process if the
%% trap is not cancelled with timetrap_cancel/1, within Timeout
%% milliseconds.
+%% Scale says if the time should be scaled up to compensate for
+%% delays during the test (e.g. if cover is running).
timetrap(Timeout0, Pid) ->
+ timetrap(Timeout0, true, Pid).
+
+timetrap(Timeout0, Scale, Pid) ->
process_flag(priority, max),
- Timeout = test_server:timetrap_scale_factor() * Timeout0,
+ Timeout = if not Scale -> Timeout0;
+ true -> test_server:timetrap_scale_factor() * Timeout0
+ end,
receive
after trunc(Timeout) ->
Line = test_server:get_loc(Pid),
@@ -487,7 +494,8 @@ framework_call(Func,Args) ->
framework_call(Func,Args,DefaultReturn) ->
CB = os:getenv("TEST_SERVER_FRAMEWORK"),
framework_call(CB,Func,Args,DefaultReturn).
-framework_call(false,_Func,_Args,DefaultReturn) ->
+framework_call(FW,_Func,_Args,DefaultReturn)
+ when FW =:= false; FW =:= "undefined" ->
DefaultReturn;
framework_call(Callback,Func,Args,DefaultReturn) ->
Mod = list_to_atom(Callback),
@@ -497,6 +505,7 @@ framework_call(Callback,Func,Args,DefaultReturn) ->
end,
case erlang:function_exported(Mod,Func,length(Args)) of
true ->
+ put(test_server_loc, {Mod,Func,framework}),
EH = fun(Reason) -> exit({fw_error,{Mod,Func,Reason}}) end,
try apply(Mod,Func,Args) of
Result ->
diff --git a/lib/test_server/src/ts.config b/lib/test_server/src/ts.config
index 30ef25a0b8..f021f5958b 100644
--- a/lib/test_server/src/ts.config
+++ b/lib/test_server/src/ts.config
@@ -1,45 +1,46 @@
%% -*- erlang -*-
-{ipv6_hosts,[otptest06,otptest08,sauron,iluvatar]}.
-%%% Change these to suite the environment.
-%%% test_hosts are looked up using "ypmatch xx yy zz hosts"
-{test_hosts,
- [bingo, hurin, turin, gandalf, super,
- merry, nenya, sam, elrond, isildur]}.
+%%% Change these to suite the environment. See the inet_SUITE for info about
+%%% what they are used for.
+%%% test_hosts are looked up using "ypmatch xx yy zz hosts.byname"
+%{test_hosts,[my_ip4_host]}.
%% IPv4 host only - no ipv6 entry must exist!
-{test_host_ipv4_only,
- {"isildur", %Short hostname
- "isildur.du.uab.ericsson.se", %Long hostname
- "134.138.177.24", %IP string
- {134,138,177,24}, %IP tuple
- ["isildur"], %Any aliases
- "::ffff:134.138.177.24", %IPv6 string (compatibilty addr)
- {0,0,0,0,0,65535,34442,45336} %IPv6 tuple
- }}.
-
-{test_host_ipv6_only,
- {"otptest06", %Short hostname
- "otptest06.du.uab.ericsson.se", %Long hostname
- "fec0::a00:20ff:feb2:b4a9", %IPv6 string
- {65216,0,0,0,2560,8447,65202,46249}, %IPv6 tuple
- ["otptest06-ip6"] %Aliases.
- }}.
-
-
-
-{test_dummy_host, {"dummy",
- "dummy.du.uab.ericsson.se",
- "192.138.177.1",
- {192,138,177,1},
- ["dummy"],
- "::ffff:192.138.177.1",
- {0,0,0,0,0,65535,49290,45313}
- }}.
-
-{test_dummy_ipv6_host, {"dummy6",
- "dummy6.du.uab.ericsson.se",
- "fec0::a00:20ff:feb2:6666",
- {65216,0,0,0,2560,8447,65202,26214},
- ["dummy6-ip6"]
- }}.
+%{test_host_ipv4_only,
+% {"my_ip4_host", %Short hostname
+% "my_ip4_host.mydomain.com", %Long hostname
+% "10.10.0.1", %IP string
+% {10,10,0,1}, %IP tuple
+% ["my_ip4_host"], %Any aliases
+% "::ffff:10.10.0.1", %IPv6 string (compatibilty addr)
+% {0,0,0,0,0,65535,2570,1} %IPv6 tuple
+% }}.
+
+%{test_dummy_host, {"dummy",
+% "dummy.mydomain.com",
+% "192.168.0.1",
+% {192,168,0,1},
+% ["dummy"],
+% "::ffff:192.168.0.1",
+% {0,0,0,0,0,65535,49320,1}
+% }}.
+
+
+%%% test_hosts are looked up using "ypmatch xx yy zz ipnodes.byname"
+%{ipv6_hosts,[my_ip6_host]}.
+
+
+%{test_host_ipv6_only,
+% {"my_ip6_host", %Short hostname
+% "my_ip6_host.mydomain.com", %Long hostname
+% "::2eff:f2b0:1ea0", %IPv6 string
+% {0,0,0,0,0,12031,62128,7840}, %IPv6 tuple
+% ["my_ip6_host"] %Aliases.
+% }}.
+
+%{test_dummy_ipv6_host, {"dummy6",
+% "dummy6.mydomain.com",
+% "127::1",
+% {295,0,0,0,0,0,0,1},
+% ["dummy6-ip6"]
+% }}.
diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl
index e23b891392..729a2b11fc 100644
--- a/lib/test_server/src/ts.erl
+++ b/lib/test_server/src/ts.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -71,7 +71,7 @@
%%% ts_erl_config Finds out information about the Erlang system,
%%% for instance the location of erl_interface.
%%% This works for either an installed OTP or an Erlang
-%%% system running from Clearcase.
+%%% system running in a git repository/source tree.
%%% ts_make Interface to run the `make' program on Unix
%%% and other platforms.
%%% ts_make_erl A corrected version of the standar Erlang module
@@ -150,6 +150,14 @@ help(installed) ->
" TraceSpec is the name of a file containing\n",
" trace specifications or a list of trace\n",
" specification elements.\n",
+ " {config, Path} - Specify which directory ts should get it's \n"
+ " config files from. The files should follow\n"
+ " the convention lib/test_server/src/ts*.config.\n"
+ " These config files can also be specified by\n"
+ " setting the TEST_CONFIG_PATH environment\n"
+ " variable to the directory where the config\n"
+ " files are. The default location is\n"
+ " tests/test_server/.\n"
"\n",
"Supported trace information elements\n",
" {tp | tpl, Mod, [] | match_spec()}\n",
@@ -249,7 +257,7 @@ run_some([Spec|Specs], Opts) ->
run(Testspec) when is_atom(Testspec) ->
Options=check_test_get_opts(Testspec, []),
File = atom_to_list(Testspec),
- run_test(File, ["SPEC current.spec NAME ",File], Options);
+ run_test(File, [{spec,[File++".spec"]}], Options);
%% This can be used from command line, e.g.
%% erl -s ts run all_tests <config>
@@ -293,11 +301,11 @@ run(List, Opts) when is_list(List), is_list(Opts) ->
run(Testspec, Config) when is_atom(Testspec), is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
File=atom_to_list(Testspec),
- run_test(File, ["SPEC current.spec NAME ", File], Options);
+ run_test(File, [{spec,[File++".spec"]}], Options);
%% Runs one module in a spec (interactive)
run(Testspec, Mod) when is_atom(Testspec), is_atom(Mod) ->
run_test({atom_to_list(Testspec), Mod},
- ["SPEC current.spec NAME ", atom_to_list(Mod)],
+ [{suite,Mod}],
[interactive]).
%% run/3
@@ -305,20 +313,23 @@ run(Testspec, Mod) when is_atom(Testspec), is_atom(Mod) ->
run(Testspec,Mod,Config) when is_atom(Testspec), is_atom(Mod), is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
run_test({atom_to_list(Testspec), Mod},
- ["SPEC current.spec NAME ", atom_to_list(Mod)],
+ [{suite,Mod}],
Options);
%% Runs one testcase in a module.
run(Testspec, Mod, Case) when is_atom(Testspec), is_atom(Mod), is_atom(Case) ->
Options=check_test_get_opts(Testspec, []),
- Args = ["CASE ",atom_to_list(Mod)," ",atom_to_list(Case)],
+ Args = [{suite,atom_to_list(Mod)},{testcase,atom_to_list(Case)}],
run_test(atom_to_list(Testspec), Args, Options).
%% run/4
%% Run one testcase in a module with Options.
-run(Testspec, Mod, Case, Config) when is_atom(Testspec), is_atom(Mod), is_atom(Case), is_list(Config) ->
+run(Testspec, Mod, Case, Config) when is_atom(Testspec),
+ is_atom(Mod),
+ is_atom(Case),
+ is_list(Config) ->
Options=check_test_get_opts(Testspec, Config),
- Args = ["CASE ",atom_to_list(Mod), " ",atom_to_list(Case)],
+ Args = [{suite,atom_to_list(Mod)}, {testcase,atom_to_list(Case)}],
run_test(atom_to_list(Testspec), Args, Options).
%% Check testspec to be valid and get possible Options
@@ -327,10 +338,11 @@ check_test_get_opts(Testspec, Config) ->
validate_test(Testspec),
Mode = configmember(batch, {batch, interactive}, Config),
Vars = configvars(Config),
- Trace = configtrace(Config),
+ Trace = get_config(trace,Config),
+ ConfigPath = get_config(config,Config),
KeepTopcase = configmember(keep_topcase, {keep_topcase,[]}, Config),
Cover = configcover(Testspec,Config),
- lists:flatten([Vars,Mode,Trace,KeepTopcase,Cover]).
+ lists:flatten([Vars,Mode,Trace,KeepTopcase,Cover,ConfigPath]).
to_erlang_term(Atom) ->
String = atom_to_list(Atom),
@@ -398,8 +410,8 @@ special_vars(Config) ->
SpecVars1
end.
-configtrace(Config) ->
- case lists:keysearch(trace,1,Config) of
+get_config(Key,Config) ->
+ case lists:keysearch(Key,1,Config) of
{value,Value} -> Value;
false -> []
end.
diff --git a/lib/test_server/src/ts.unix.config b/lib/test_server/src/ts.unix.config
index b4325f065f..5a2580f464 100644
--- a/lib/test_server/src/ts.unix.config
+++ b/lib/test_server/src/ts.unix.config
@@ -1,4 +1,4 @@
%% -*- erlang -*-
%% Always run a (VNC) X server on host
-{xserver, "frumgar.du.uab.ericsson.se:66"}.
+%% {xserver, "xserver.example.com:66"}.
diff --git a/lib/test_server/src/ts.vxworks.config b/lib/test_server/src/ts.vxworks.config
deleted file mode 100644
index b0b66e07ad..0000000000
--- a/lib/test_server/src/ts.vxworks.config
+++ /dev/null
@@ -1,19 +0,0 @@
-%% -*- erlang -*-
-
-%%% There is no equivalent command to ypmatch on Win32... :-(
-{hardcoded_hosts,
- [{"134.138.177.74","strider"},
- {"134.138.177.72", "elrond"},
- {"134.138.177.67", "sam"},
- {"134.138.176.215", "nenya"},
- {"134.138.176.192", "merry"},
- {"134.138.177.35", "lw4"},
- {"134.138.177.35", "lw5"},
- {"134.138.176.16", "super"},
- {"134.138.177.16", "gandalf"},
- {"134.138.177.92", "turin"},
- {"134.138.177.86", "mallor"}]}.
-
-{hardcoded_ipv6_hosts,
- [{"fe80::a00:20ff:feb2:b4a9","otptest06"},
- {"fe80::a00:20ff:feb2:a621","otptest08"}]}.
diff --git a/lib/test_server/src/ts.win32.config b/lib/test_server/src/ts.win32.config
index 2802c4a75a..cae587bea8 100644
--- a/lib/test_server/src/ts.win32.config
+++ b/lib/test_server/src/ts.win32.config
@@ -1,15 +1,8 @@
%% -*- erlang -*-
%%% There is no equivalent command to ypmatch on Win32... :-(
-{hardcoded_hosts,
- [{"134.138.177.24","isildur"},
- {"134.138.177.72", "elrond"},
- {"134.138.176.215", "nenya"},
- {"134.138.176.192", "merry"},
- {"134.138.176.16", "super"},
- {"134.138.177.16", "gandalf"},
- {"134.138.177.92", "turin"}]}.
+%{hardcoded_hosts,
+% [{"127.0.0.1","localhost"}]}.
-{hardcoded_ipv6_hosts,
- [{"fe80::a00:20ff:feb2:b4a9","otptest06"},
- {"fe80::a00:20ff:feb2:a621","otptest08"}]}.
+%{hardcoded_ipv6_hosts,
+% [{"::1","localhost"}]}.
diff --git a/lib/test_server/src/ts_erl_config.erl b/lib/test_server/src/ts_erl_config.erl
index 5cdbf0fbb8..640c8ddc9f 100644
--- a/lib/test_server/src/ts_erl_config.erl
+++ b/lib/test_server/src/ts_erl_config.erl
@@ -70,18 +70,18 @@ dl_vars(Vars, _) ->
ShlibRules = ts_lib:subst(ShlibRules0, Vars),
[{'SHLIB_RULES', ShlibRules}|Vars].
-erts_lib_name(multi_threaded, win32) ->
+erts_lib_name(multi_threaded, {win32, V}) ->
link_library("erts_MD" ++ case is_debug_build() of
true -> "d";
false -> ""
end,
- win32);
-erts_lib_name(single_threaded, win32) ->
+ {win32, V});
+erts_lib_name(single_threaded, {win32, V}) ->
link_library("erts_ML" ++ case is_debug_build() of
true -> "d";
false -> ""
end,
- win32);
+ {win32, V});
erts_lib_name(multi_threaded, OsType) ->
link_library("erts_r", OsType);
erts_lib_name(single_threaded, OsType) ->
@@ -107,7 +107,7 @@ erts_lib(Vars,OsType) ->
ErtsIncludeInternal,
ErtsLib,
ErtsLibInternal};
- {Type, Root, Target} when Type == clearcase; Type == srctree ->
+ {srctree, Root, Target} ->
Erts = filename:join([Root, "erts"]),
ErtsInclude = filename:join([Erts, "include"]),
ErtsIncludeTarget = filename:join([ErtsInclude, Target]),
@@ -146,7 +146,7 @@ erl_include(Vars) ->
case erl_root(Vars) of
{installed, Root} ->
filename:join([Root, "usr", "include"]);
- {Type, Root, Target} when Type == clearcase; Type == srctree ->
+ {srctree, Root, Target} ->
filename:join([Root, "erts", "emulator", "beam"])
++ " -I" ++ filename:join([Root, "erts", "emulator"])
++ system_include(Root, Vars)
@@ -179,7 +179,7 @@ erl_interface(Vars,OsType) ->
{srctree, _Root, _Target} when OsType =:= vxworks ->
{filename:join(Dir, "lib"),
filename:join([Dir, "src"])};
- {Type, _Root, Target} when Type == clearcase; Type == srctree ->
+ {srctree, _Root, Target} ->
{filename:join([Dir, "obj", Target]),
filename:join([Dir, "src", Target])}
end}
@@ -246,7 +246,7 @@ ic(Vars, OsType) ->
case erl_root(Vars) of
{installed, _Root} ->
filename:join([Dir, "priv", "lib"]);
- {Type, _Root, Target} when Type == clearcase; Type == srctree ->
+ {srctree, _Root, Target} ->
filename:join([Dir, "priv", "lib", Target])
end,
filename:join(Dir, "include")}
@@ -266,21 +266,6 @@ jinterface(Vars, _OsType) ->
end,
[{jinterface_classpath, filename:nativename(ClassPath)}|Vars].
-%% Unused!
-% ig_vars(Vars) ->
-% {Lib0, Incl} =
-% case erl_root(Vars) of
-% {installed, Root} ->
-% Base = filename:join([Root, "usr"]),
-% {filename:join([Base, "lib"]),
-% filename:join([Base, "include"])};
-% {Type, Root, Target} when Type == clearcase; Type == srctree ->
-% {filename:join([Root, "lib", "ig", "obj", Target]),
-% filename:join([Root, "lib", "ig", "include"])}
-% end,
-% [{ig_libdir, filename:nativename(Lib0)},
-% {ig_include, filename:nativename(Incl)}|Vars].
-
lib_dir(Vars, Lib) ->
LibLibDir = case Lib of
erts ->
@@ -317,9 +302,6 @@ lib_dir(Vars, Lib) ->
erl_root(Vars) ->
Root = code:root_dir(),
case ts_lib:erlang_type() of
- {clearcase, _Version} ->
- Target = get_var(target, Vars),
- {clearcase, Root, Target};
{srctree, _Version} ->
Target = get_var(target, Vars),
{srctree, Root, Target};
@@ -349,10 +331,7 @@ sock_libraries({unix, _}) ->
sock_libraries(vxworks) ->
"";
sock_libraries(ose) ->
- "";
-sock_libraries(_Other) ->
- exit({sock_libraries, not_supported}).
-
+ "".
link_library(LibName,{win32, _}) ->
LibName ++ ".lib";
diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl
index 94926eba80..2ddffccf5b 100644
--- a/lib/test_server/src/ts_install.erl
+++ b/lib/test_server/src/ts_install.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(ts_install).
@@ -175,15 +175,8 @@ get_testcase_callback() ->
get_rsh_name() ->
case os:getenv("ERL_RSH") of
- false ->
- case ts_lib:erlang_type() of
- {clearcase, _} ->
- "ctrsh";
- {_, _} ->
- "rsh"
- end;
- Str ->
- Str
+ false -> "rsh";
+ Str -> Str
end.
platform_id(Vars) ->
@@ -233,9 +226,11 @@ to_upper(String) ->
String).
word_size() ->
- case erlang:system_info(wordsize) of
- 4 -> "";
- 8 -> "/64"
+ case {erlang:system_info({wordsize,external}),
+ erlang:system_info({wordsize,internal})} of
+ {4,4} -> "";
+ {8,8} -> "/64";
+ {8,4} -> "/Halfword"
end.
linux_dist() ->
diff --git a/lib/test_server/src/ts_install_cth.erl b/lib/test_server/src/ts_install_cth.erl
new file mode 100644
index 0000000000..c5444a342f
--- /dev/null
+++ b/lib/test_server/src/ts_install_cth.erl
@@ -0,0 +1,286 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%% @doc TS Installed SCB
+%%%
+%%% This module does what the make parts of the ts:run/x command did,
+%%% but not the Makefile.first parts! So they have to be done by ts or
+%%% manually!!
+
+-module(ts_install_cth).
+
+%% Suite Callbacks
+-export([id/1]).
+-export([init/2]).
+
+-export([pre_init_per_suite/3]).
+-export([post_init_per_suite/4]).
+-export([pre_end_per_suite/3]).
+-export([post_end_per_suite/4]).
+
+-export([pre_init_per_group/3]).
+-export([post_init_per_group/4]).
+-export([pre_end_per_group/3]).
+-export([post_end_per_group/4]).
+
+-export([pre_init_per_testcase/3]).
+-export([post_end_per_testcase/4]).
+
+-export([on_tc_fail/3]).
+-export([on_tc_skip/3]).
+
+-export([terminate/1]).
+
+-include_lib("kernel/include/file.hrl").
+
+-type proplist() :: list({atom(),term()}).
+-type config() :: proplist().
+-type reason() :: term().
+-type skip_or_fail() :: {skip, reason()} |
+ {auto_skip, reason()} |
+ {fail, reason()}.
+
+-record(state, { ts_conf_dir, target_system, install_opts, nodenames, nodes }).
+
+%% @doc The id of this SCB
+-spec id(Opts :: term()) ->
+ Id :: term().
+id(_Opts) ->
+ ?MODULE.
+
+%% @doc Always called before any other callback function.
+-spec init(Id :: term(), Opts :: proplist()) ->
+ State :: #state{}.
+init(_Id, Opts) ->
+ Nodenames = proplists:get_value(nodenames, Opts, 0),
+ Nodes = proplists:get_value(nodes, Opts, 0),
+ TSConfDir = proplists:get_value(ts_conf_dir, Opts),
+ TargetSystem = proplists:get_value(target_system, Opts, install_local),
+ InstallOpts = proplists:get_value(install_opts, Opts, []),
+ #state{ nodenames = Nodenames,
+ nodes = Nodes,
+ ts_conf_dir = TSConfDir,
+ target_system = TargetSystem,
+ install_opts = InstallOpts }.
+
+%% @doc Called before init_per_suite is called.
+-spec pre_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_suite(Suite,Config,#state{ ts_conf_dir = undefined} = State) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ ParentDir = filename:join(
+ lists:reverse(
+ tl(lists:reverse(filename:split(DataDir))))),
+ TSConfDir = filename:join([ParentDir, "..","test_server"]),
+ pre_init_per_suite(Suite, Config, State#state{ ts_conf_dir = TSConfDir });
+pre_init_per_suite(_Suite,Config,State) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ try
+ {ok,Variables} =
+ file:consult(filename:join(State#state.ts_conf_dir,"variables")),
+
+ %% Make the stuff in all_SUITE_data if it exists
+ AllDir = filename:join(DataDir,"../all_SUITE_data"),
+ case filelib:is_dir(AllDir) of
+ true ->
+ make_non_erlang(AllDir,Variables);
+ false ->
+ ok
+ end,
+
+ make_non_erlang(DataDir, Variables),
+
+ {add_node_name(Config, State), State}
+ catch Error:Reason ->
+ Stack = erlang:get_stacktrace(),
+ ct:pal("~p failed! ~p:{~p,~p}",[?MODULE,Error,Reason,Stack]),
+ {{fail,{?MODULE,{Error,Reason, Stack}}},State}
+ end.
+
+%% @doc Called after init_per_suite.
+-spec post_init_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_suite(_Suite,_Config,Return,State) ->
+ test_server_ctrl:kill_slavenodes(),
+ {Return, State}.
+
+%% @doc Called before end_per_suite.
+-spec pre_end_per_suite(Suite :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_suite(_Suite,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after end_per_suite.
+-spec post_end_per_suite(Suite :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_suite(_Suite,_Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each init_per_group.
+-spec pre_init_per_group(Group :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_group(_Group,Config,State) ->
+ {add_node_name(Config, State), State}.
+
+%% @doc Called after each init_per_group.
+-spec post_init_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+post_init_per_group(_Group,_Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called after each end_per_group.
+-spec pre_end_per_group(Group :: atom(),
+ Config :: config() | skip_or_fail(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+pre_end_per_group(_Group,Config,State) ->
+ {Config, State}.
+
+%% @doc Called after each end_per_group.
+-spec post_end_per_group(Group :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_group(_Group,_Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called before each test case.
+-spec pre_init_per_testcase(TC :: atom(),
+ Config :: config(),
+ State :: #state{}) ->
+ {config() | skip_or_fail(), NewState :: #state{}}.
+pre_init_per_testcase(_TC,Config,State) ->
+ {add_node_name(Config, State), State}.
+
+%% @doc Called after each test case.
+-spec post_end_per_testcase(TC :: atom(),
+ Config :: config(),
+ Return :: term(),
+ State :: #state{}) ->
+ {ok | skip_or_fail(), NewState :: #state{}}.
+post_end_per_testcase(_TC,_Config,Return,State) ->
+ {Return, State}.
+
+%% @doc Called after a test case failed.
+-spec on_tc_fail(TC :: init_per_suite | end_per_suite |
+ init_per_group | end_per_group | atom(),
+ Reason :: term(), State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_fail(_TC, _Reason, State) ->
+ State.
+
+%% @doc Called when a test case is skipped.
+-spec on_tc_skip(TC :: end_per_suite | init_per_group | end_per_group | atom(),
+ {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(),
+ Reason :: term()}}} |
+ {tc_user_skip, {skipped, Reason :: term()}},
+ State :: #state{}) ->
+ NewState :: #state{}.
+on_tc_skip(_TC, _Reason, State) ->
+ State.
+
+%% @doc Called when the scope of the SCB is done.
+-spec terminate(State :: #state{}) ->
+ term().
+terminate(_State) ->
+ ok.
+
+%%% ============================================================================
+%%% Local functions
+%%% ============================================================================
+%% Configure and run all the Makefiles in the data dirs of the suite
+%% in question
+make_non_erlang(DataDir, Variables) ->
+ {ok,CurrWD} = file:get_cwd(),
+ try
+ file:set_cwd(DataDir),
+ MakeCommand = proplists:get_value(make_command,Variables),
+
+ FirstMakefile = filename:join(DataDir,"Makefile.first"),
+ case filelib:is_regular(FirstMakefile) of
+ true ->
+ ct:log("Making ~p",[FirstMakefile]),
+ ok = ts_make:make(
+ MakeCommand, DataDir, filename:basename(FirstMakefile));
+ false ->
+ ok
+ end,
+
+ MakefileSrc = filename:join(DataDir,"Makefile.src"),
+ MakefileDest = filename:join(DataDir,"Makefile"),
+ case filelib:is_regular(MakefileSrc) of
+ true ->
+ ok = ts_lib:subst_file(MakefileSrc,MakefileDest,Variables),
+ ct:log("Making ~p",[MakefileDest]),
+ ok = ts_make:make([{makefile,"Makefile"},{data_dir,DataDir}
+ | Variables]);
+ false ->
+ ok
+ end
+ after
+ file:set_cwd(CurrWD),
+ timer:sleep(100)
+ end.
+
+%% Add a nodename to config if it does not exist
+add_node_name(Config, State) ->
+ case proplists:get_value(nodenames, Config) of
+ undefined ->
+ lists:keystore(
+ nodenames, 1, Config,
+ {nodenames,generate_nodenames(State#state.nodenames)});
+ _Else ->
+ Config
+ end.
+
+
+%% Copied from test_server_ctrl.erl
+generate_nodenames(Num) ->
+ {ok,Name} = inet:gethostname(),
+ generate_nodenames2(Num, [Name], []).
+
+generate_nodenames2(0, _Hosts, Acc) ->
+ Acc;
+generate_nodenames2(N, Hosts, Acc) ->
+ Host=lists:nth((N rem (length(Hosts)))+1, Hosts),
+ Name=list_to_atom(temp_nodename("nod", []) ++ "@" ++ Host),
+ generate_nodenames2(N-1, Hosts, [Name|Acc]).
+
+temp_nodename([], Acc) ->
+ lists:flatten(Acc);
+temp_nodename([Chr|Base], Acc) ->
+ {A,B,C} = erlang:now(),
+ New = [Chr | integer_to_list(Chr bxor A bxor B+A bxor C+B)],
+ temp_nodename(Base, [New|Acc]).
diff --git a/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl
index 082c9e0519..2f0a4ea8c0 100644
--- a/lib/test_server/src/ts_lib.erl
+++ b/lib/test_server/src/ts_lib.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(ts_lib).
@@ -21,6 +21,8 @@
-include_lib("kernel/include/file.hrl").
-include("ts.hrl").
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([error/1, var/2, erlang_type/0,
initial_capital/1, interesting_logs/1,
specs/1, suites/2, last_test/1,
@@ -72,12 +74,10 @@ progress(Vars, Level, Format, Args) ->
erlang_type() ->
{_, Version} = init:script_id(),
- RelDir = filename:join([code:root_dir(), "releases"]), % Only in installed
- SysDir = filename:join([code:root_dir(), "system"]), % Nonexisting link/dir outside ClearCase
- case {filelib:is_file(RelDir),filelib:is_file(SysDir)} of
- {true,_} -> {otp, Version}; % installed OTP
- {_,true} -> {clearcase, Version};
- _ -> {srctree, Version}
+ RelDir = filename:join(code:root_dir(), "releases"), % Only in installed
+ case filelib:is_file(RelDir) of
+ true -> {otp,Version}; % installed OTP
+ false -> {srctree,Version} % source code tree
end.
%% Upcases the first letter in a string.
diff --git a/lib/test_server/src/ts_reports.erl b/lib/test_server/src/ts_reports.erl
index b41291d342..f981a77ae4 100644
--- a/lib/test_server/src/ts_reports.erl
+++ b/lib/test_server/src/ts_reports.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -27,6 +27,8 @@
-include_lib("kernel/include/file.hrl").
-include("ts.hrl").
+-compile({no_auto_import,[error/1]}).
+
-import(filename, [basename/1, rootname/1]).
-import(ts_lib, [error/1]).
diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl
index 888ac98973..067961a216 100644
--- a/lib/test_server/src/ts_run.erl
+++ b/lib/test_server/src/ts_run.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-include("ts.hrl").
--import(lists, [map/2,member/2,filter/2,reverse/1]).
+-import(lists, [member/2,filter/2]).
-record(state,
{file, % File given.
@@ -63,50 +63,18 @@ run(File, Args0, Options, Vars0) ->
_ ->
{false, fun run_interactive/3}
end,
- HandleTopcase = case member(keep_topcase, Options) of
- true -> [fun copy_topcase/3];
- false -> [fun remove_original_topcase/3,
- fun init_topcase/3]
- end,
- MakefileHooks = [fun make_make/3,
- fun add_make_testcase/3],
- MakeLoop = fun(V, Sp, St) -> make_loop(MakefileHooks, V, Sp, St) end,
Hooks = [fun init_state/3,
- fun read_spec_file/3] ++
- HandleTopcase ++
- [fun run_preinits/3,
- fun find_makefiles/3,
- MakeLoop,
- fun make_test_suite/3,
- fun add_topcase_to_spec/3,
- fun write_spec_file/3,
+ fun run_preinits/3,
fun make_command/3,
Runner],
- Args = make_test_server_args(Args0,Options,Vars),
+ Args = make_common_test_args(Args0,Options,Vars),
St = #state{file=File,test_server_args=Args,batch=Batch},
R = execute(Hooks, Vars, [], St),
- case Batch of
- true -> ts_reports:make_index();
- false -> ok % ts_reports:make_index() is run on the test_server node
- end,
case R of
{ok,_,_,_} -> ok;
Error -> Error
end.
-make_loop(Hooks, Vars0, Spec0, St0) ->
- case St0#state.makefiles of
- [Makefile|Rest] ->
- case execute(Hooks, Vars0, Spec0, St0#state{makefile=Makefile}) of
- {error, Reason} ->
- {error, Reason};
- {ok, Vars, Spec, St} ->
- make_loop(Hooks, Vars, Spec, St#state{makefiles=Rest})
- end;
- [] ->
- {ok, Vars0, Spec0, St0}
- end.
-
execute([Hook|Rest], Vars0, Spec0, St0) ->
case Hook(Vars0, Spec0, St0) of
ok ->
@@ -156,101 +124,6 @@ init_state(Vars, [], St0) ->
false ->
{error,{no_test_directory,TestDir}}
end.
-
-%% Read the spec file for the test suite.
-
-read_spec_file(Vars, _, St) ->
- TestDir = St#state.test_dir,
- File = St#state.file,
- {SpecFile,Res} = get_spec_filename(Vars, TestDir, File),
- case Res of
- {ok,Spec} ->
- {ok,Vars,Spec,St};
- {error,Atom} when is_atom(Atom) ->
- {error,{no_spec,SpecFile}};
- {error,Reason} ->
- {error,{bad_spec,lists:flatten(file:format_error(Reason))}}
- end.
-
-get_spec_filename(Vars, TestDir, File) ->
- DynSpec = filename:join(TestDir, File ++ ".dynspec"),
- case filelib:is_file(DynSpec) of
- true ->
- Bs0 = erl_eval:new_bindings(),
- Bs1 = erl_eval:add_binding('Target', ts_lib:var(target, Vars), Bs0),
- Bs2 = erl_eval:add_binding('Os', ts_lib:var(os, Vars), Bs1),
- TCCStr = ts_lib:var(test_c_compiler, Vars),
- TCC = try
- {ok, Toks, _} = erl_scan:string(TCCStr ++ "."),
- {ok, Tcc} = erl_parse:parse_term(Toks),
- Tcc
- catch
- _:_ -> undefined
- end,
- Bs = erl_eval:add_binding('TestCCompiler', TCC, Bs2),
- {DynSpec,file:script(DynSpec, Bs)};
- false ->
- SpecFile = get_spec_filename_1(Vars, TestDir, File),
- {SpecFile,file:consult(SpecFile)}
- end.
-
-get_spec_filename_1(Vars, TestDir, File) ->
- case ts_lib:var(os, Vars) of
- "VxWorks" ->
- check_spec_filename(TestDir, File, ".spec.vxworks");
- "Windows"++_ ->
- check_spec_filename(TestDir, File, ".spec.win");
- _Other ->
- filename:join(TestDir, File ++ ".spec")
- end.
-
-check_spec_filename(TestDir, File, Ext) ->
- Spec = filename:join(TestDir, File ++ Ext),
- case filelib:is_file(Spec) of
- true -> Spec;
- false -> filename:join(TestDir, File ++ ".spec")
- end.
-
-%% Remove the top case from the spec file. We will add our own
-%% top case later.
-
-remove_original_topcase(Vars, Spec, St) ->
- {ok,Vars,filter(fun ({topcase,_}) -> false;
- (_Other) -> true end, Spec),St}.
-
-%% Initialize our new top case. We'll keep in it the state to be
-%% able to add more to it.
-
-init_topcase(Vars, Spec, St) ->
- TestDir = St#state.test_dir,
- TopCase =
- case St#state.mod of
- Mod when is_atom(Mod) ->
- ModStr = atom_to_list(Mod),
- case filelib:is_file(filename:join(TestDir,ModStr++".erl")) of
- true -> [{Mod,all}];
- false ->
- Wc = filename:join(TestDir, ModStr ++ "*_SUITE.erl"),
- [{list_to_atom(filename:basename(M, ".erl")),all} ||
- M <- filelib:wildcard(Wc)]
- end;
- _Other ->
- %% Here we used to return {dir,TestDir}. Now we instead
- %% list all suites in TestDir, so we can add make testcases
- %% around it later (see add_make_testcase) without getting
- %% duplicates of the suite. (test_server_ctrl does no longer
- %% check for duplicates of testcases)
- Wc = filename:join(TestDir, "*_SUITE.erl"),
- [{list_to_atom(filename:basename(M, ".erl")),all} ||
- M <- filelib:wildcard(Wc)]
- end,
- {ok,Vars,Spec,St#state{topcase=TopCase}}.
-
-%% Or if option keep_topcase was given, eh... keep the topcase
-copy_topcase(Vars, Spec, St) ->
- {value,{topcase,Tc}} = lists:keysearch(topcase,1,Spec),
- {ok, Vars, lists:keydelete(topcase,1,Spec),St#state{topcase=Tc}}.
-
%% Run any "Makefile.first" files first.
%% XXX We should fake a failing test case if the make fails.
@@ -279,171 +152,14 @@ run_pre_makefile(Vars, Spec, St) ->
{error,_Reason}=Error -> Error
end.
-%% Search for `Makefile.src' in each *_SUITE_data directory.
-
-find_makefiles(Vars, Spec, St) ->
- Wc = filename:join(St#state.data_wc, "Makefile.src"),
- Makefiles = reverse(del_skipped_suite_data_dir(filelib:wildcard(Wc), Spec)),
- {ok,Vars,Spec,St#state{makefiles=Makefiles}}.
-
-%% Create "Makefile" from "Makefile.src".
-
-make_make(Vars, Spec, State) ->
- Src = State#state.makefile,
- Dest = filename:rootname(Src),
- ts_lib:progress(Vars, 1, "Making ~s...\n", [Dest]),
- case ts_lib:subst_file(Src, Dest, Vars) of
- ok ->
- {ok, Vars, Spec, State#state{makefile=Dest}};
- {error, Reason} ->
- {error, {Src, Reason}}
- end.
-
-%% Add a testcase which will do the making of the stuff in the data directory.
-
-add_make_testcase(Vars, Spec, St) ->
- Makefile = St#state.makefile,
- Dir = filename:dirname(Makefile),
- Shortname = filename:basename(Makefile),
- Suite = filename:basename(Dir, "_data"),
- Config = [{data_dir,Dir},{makefile,Shortname}],
- MakeModule = Suite ++ "_make",
- MakeModuleSrc = filename:join(filename:dirname(Dir),
- MakeModule ++ ".erl"),
- MakeMod = list_to_atom(MakeModule),
- case filelib:is_file(MakeModuleSrc) of
- true -> ok;
- false -> generate_make_module(ts_lib:var(make_command, Vars),
- MakeModuleSrc,
- MakeModule)
- end,
- case Suite of
- "all_SUITE" ->
- {ok,Vars,Spec,St#state{all={MakeMod,Config}}};
- _ ->
- %% Avoid duplicates of testcases. There is no longer
- %% a check for this in test_server_ctrl.
- TestCase = {list_to_atom(Suite),all},
- TopCase0 = case St#state.topcase of
- List when is_list(List) ->
- List -- [TestCase];
- Top ->
- [Top] -- [TestCase]
- end,
- TopCase = [{make,{MakeMod,make,[Config]},
- TestCase,
- {MakeMod,unmake,[Config]}}|TopCase0],
- {ok,Vars,Spec,St#state{topcase=TopCase}}
- end.
-
-generate_make_module(MakeCmd, Name, ModuleString) ->
- {ok,Host} = inet:gethostname(),
- file:write_file(Name,
- ["-module(",ModuleString,").\n",
- "\n",
- "-export([make/1,unmake/1]).\n",
- "\n",
- "make(Config) when is_list(Config) ->\n",
- " Mins = " ++ integer_to_list(?DEFAULT_MAKE_TIMETRAP_MINUTES) ++ ",\n"
- " test_server:format(\"=== Setting timetrap to ~p minutes ===~n\", [Mins]),\n"
- " TimeTrap = test_server:timetrap(test_server:minutes(Mins)),\n"
- " Res = ts_make:make([{make_command, \""++MakeCmd++"\"},{cross_node,\'ts@" ++ Host ++ "\'}|Config]),\n",
- " test_server:timetrap_cancel(TimeTrap),\n"
- " Res.\n"
- "\n",
- "unmake(Config) when is_list(Config) ->\n",
- " Mins = " ++ integer_to_list(?DEFAULT_UNMAKE_TIMETRAP_MINUTES) ++ ",\n"
- " test_server:format(\"=== Setting timetrap to ~p minutes ===~n\", [Mins]),\n"
- " TimeTrap = test_server:timetrap(test_server:minutes(Mins)),\n"
- " Res = ts_make:unmake([{make_command, \""++MakeCmd++"\"}|Config]),\n"
- " test_server:timetrap_cancel(TimeTrap),\n"
- " Res.\n"
- "\n"]).
-
-
-make_test_suite(Vars, _Spec, State) ->
- TestDir = State#state.test_dir,
-
- Erl_flags=[{i, "../test_server"}|ts_lib:var(erl_flags,Vars)],
-
- case code:is_loaded(test_server_line) of
- false -> code:load_file(test_server_line);
- _ -> ok
- end,
-
- {ok, Cwd} = file:get_cwd(),
- ok = file:set_cwd(TestDir),
- Result = (catch make_all(Erl_flags)),
- ok = file:set_cwd(Cwd),
- case Result of
- up_to_date ->
- ok;
- {'EXIT', Reason} ->
- %% If I return an error here, the test will be stopped
- %% and it will not show up in the top index page. Instead
- %% I return ok - the test will run for all existing suites.
- %% It might be that there are old suites that are run, but
- %% at least one suite is missing, and that is reported on the
- %% top index page.
- io:format("~s: {error,{make_crashed,~p}\n",
- [State#state.file,Reason]),
- ok;
- error ->
- %% See comment above
- io:format("~s: {error,make_of_test_suite_failed}\n",
- [State#state.file]),
- ok
- end.
-
-%% Add topcase to spec.
-
-add_topcase_to_spec(Vars, Spec, St) ->
- Tc = case St#state.all of
- {MakeMod,Config} ->
- [{make,{MakeMod,make,[Config]},
- St#state.topcase,
- {MakeMod,unmake,[Config]}}];
- undefined -> St#state.topcase
- end,
- {ok,Vars,Spec++[{topcase,Tc}],St}.
-
-%% Writes the (possibly transformed) spec file.
-
-write_spec_file(Vars, Spec, _State) ->
- F = fun(Term) -> io_lib:format("~p.~n", [Term]) end,
- SpecFile = map(F, Spec),
- Hosts =
- case lists:keysearch(hosts, 1, Vars) of
- false ->
- [];
- {value, {hosts, HostList}} ->
- io_lib:format("{hosts,~p}.~n",[HostList])
- end,
- DiskLess =
- case lists:keysearch(diskless, 1, Vars) of
- false ->
- [];
- {value, {diskless, How}} ->
- io_lib:format("{diskless, ~p}.~n",[How])
- end,
- Conf = consult_config(),
- MoreConfig = io_lib:format("~p.\n", [{config,Conf}]),
- file:write_file("current.spec", [DiskLess,Hosts,MoreConfig,SpecFile]).
-
-consult_config() ->
- {ok,Conf} = file:consult("ts.config"),
- case os:type() of
- {unix,_} -> consult_config("ts.unix.config", Conf);
- {win32,_} -> consult_config("ts.win32.config", Conf);
- vxworks -> consult_config("ts.vxworks.config", Conf);
- _ -> Conf
- end.
-
-consult_config(File, Conf0) ->
- case file:consult(File) of
- {ok,Conf} -> Conf++Conf0;
- {error,enoent} -> Conf0
- end.
+get_config_files() ->
+ TSConfig = "ts.config",
+ [TSConfig | case os:type() of
+ {unix,_} -> ["ts.unix.config"];
+ {win32,_} -> ["ts.win32.config"];
+ vxworks -> ["ts.vxworks.config"];
+ _ -> []
+ end].
%% Makes the command to start up the Erlang node to run the tests.
@@ -457,6 +173,7 @@ backslashify([]) ->
[].
make_command(Vars, Spec, State) ->
+ {ok,Cwd} = file:get_cwd(),
TestDir = State#state.test_dir,
TestPath = filename:nativename(TestDir),
Erl = case os:getenv("TS_RUN_VALGRIND") of
@@ -487,7 +204,7 @@ make_command(Vars, Spec, State) ->
{value,{erl_start_args,Args}} -> Args;
false -> ""
end,
- CrashFile = State#state.file ++ "_erl_crash.dump",
+ CrashFile = filename:join(Cwd,State#state.file ++ "_erl_crash.dump"),
case filelib:is_file(CrashFile) of
true ->
io:format("ts_run: Deleting dump: ~s\n",[CrashFile]),
@@ -495,7 +212,8 @@ make_command(Vars, Spec, State) ->
false ->
ok
end,
- Cmd = [Erl, Naming, "test_server -pa ", $", TestPath, $",
+ %% NOTE: Do not use ' in these commands as it wont work on windows
+ Cmd = [Erl, Naming, "test_server"
" -rsh ", ts_lib:var(rsh_name, Vars),
" -env PATH \"",
backslashify(lists:flatten([TestPath, path_separator(),
@@ -505,15 +223,20 @@ make_command(Vars, Spec, State) ->
%% uncomment the line below to disable exception formatting
%% " -test_server_format_exception false",
" -boot start_sasl -sasl errlog_type error",
- " -s test_server_ctrl run_test ", State#state.test_server_args,
+ " -pz ",Cwd,
+ " -eval \"file:set_cwd(\\\"",TestDir,"\\\")\" "
+ " -eval \"ct:run_test(",
+ backslashify(lists:flatten(State#state.test_server_args)),")\""
" ",
ExtraArgs],
{ok, Vars, Spec, State#state{command=lists:flatten(Cmd)}}.
+
run_batch(Vars, _Spec, State) ->
process_flag(trap_exit, true),
Command = State#state.command ++ " -noinput -s erlang halt",
ts_lib:progress(Vars, 1, "Command: ~s~n", [Command]),
+ io:format(user, "Command: ~s~n",[Command]),
Port = open_port({spawn, Command}, [stream, in, eof]),
tricky_print_data(Port).
@@ -554,7 +277,7 @@ is_testnode_dead([{"test_server",_}|_]) -> false;
is_testnode_dead([_|T]) -> is_testnode_dead(T).
run_interactive(Vars, _Spec, State) ->
- Command = State#state.command ++ " -s ts_reports make_index",
+ Command = State#state.command,
ts_lib:progress(Vars, 1, "Command: ~s~n", [Command]),
case ts_lib:var(os, Vars) of
"Windows 95" ->
@@ -604,77 +327,57 @@ path_separator() ->
end.
-make_test_server_args(Args0,Options,Vars) ->
- Parameters =
- case ts_lib:var(os, Vars) of
- "VxWorks" ->
- F = write_parameterfile(vxworks,Vars),
- " PARAMETERS " ++ F;
- _ ->
- ""
- end,
+make_common_test_args(Args0, Options, _Vars) ->
Trace =
case lists:keysearch(trace,1,Options) of
{value,{trace,TI}} when is_tuple(TI); is_tuple(hd(TI)) ->
ok = file:write_file(?tracefile,io_lib:format("~p.~n",[TI])),
- " TRACE " ++ ?tracefile;
+ [{ct_trace,?tracefile}];
{value,{trace,TIFile}} when is_atom(TIFile) ->
- " TRACE " ++ atom_to_list(TIFile);
+ [{ct_trace,atom_to_list(TIFile)}];
{value,{trace,TIFile}} ->
- " TRACE " ++ TIFile;
+ [{ct_trace,TIFile}];
false ->
- ""
+ []
end,
Cover =
case lists:keysearch(cover,1,Options) of
- {value,{cover,App,File,Analyse}} ->
- " COVER " ++ to_list(App) ++ " " ++ to_list(File) ++ " " ++
- to_list(Analyse);
+ {value,{cover, App, none, _Analyse}} ->
+ io:format("No cover file found for ~p~n",[App]),
+ [];
+ {value,{cover,_App,File,_Analyse}} ->
+ [{cover,to_list(File)}];
false ->
- ""
- end,
- TCCallback =
- case ts_lib:var(ts_testcase_callback, Vars) of
- "" ->
- "";
- {Mod,Func} ->
- io:format("Function ~w:~w/4 will be called before and "
- "after each test case.\n", [Mod,Func]),
- " TESTCASE_CALLBACK " ++ to_list(Mod) ++ " " ++ to_list(Func);
- ModFunc when is_list(ModFunc) ->
- [Mod,Func]=string:tokens(ModFunc," "),
- io:format("Function ~s:~s/4 will be called before and "
- "after each test case.\n", [Mod,Func]),
- " TESTCASE_CALLBACK " ++ ModFunc;
- _ ->
- ""
+ []
end,
- Args0 ++ Parameters ++ Trace ++ Cover ++ TCCallback.
+
+ Logdir = case lists:keysearch(logdir, 1, Options) of
+ {value,{logdir, _}} ->
+ [];
+ false ->
+ [{logdir,"../test_server"}]
+ end,
+
+ ConfigPath = case {os:getenv("TEST_CONFIG_PATH"),
+ lists:keysearch(config, 1, Options)} of
+ {false,{value, {config, Path}}} ->
+ Path;
+ {false,false} ->
+ "../test_server";
+ {Path,_} ->
+ Path
+ end,
+ ConfigFiles = [{config,[filename:join(ConfigPath,File)
+ || File <- get_config_files()]}],
+
+ io_lib:format("~100000p",[Args0++Trace++Cover++Logdir++
+ ConfigFiles++Options]).
to_list(X) when is_atom(X) ->
atom_to_list(X);
to_list(X) when is_list(X) ->
X.
-write_parameterfile(Type,Vars) ->
- Cross_host = ts_lib:var(target_host, Vars),
- SlaveTargets = case lists:keysearch(slavetargets,1,Vars) of
- {value, ST} ->
- [ST];
- _ ->
- []
- end,
- Master = case lists:keysearch(master,1,Vars) of
- {value,M} -> [M];
- false -> []
- end,
- ToWrite = [{type,Type},
- {target, list_to_atom(Cross_host)}] ++ SlaveTargets ++ Master,
-
- Crossfile = atom_to_list(Type) ++ "parameters" ++ os:getpid(),
- ok = file:write_file(Crossfile,io_lib:format("~p.~n", [ToWrite])),
- Crossfile.
-
%%
%% Paths and spaces handling for w2k and XP
%%
@@ -720,53 +423,3 @@ split_one(Path) ->
split_path(Path) ->
string:tokens(Path,";").
-
-%%
-%% Run make:all/1 if the test suite seems to be designed
-%% to be built/re-built by ts.
-%%
-make_all(Flags) ->
- case filelib:is_regular("Emakefile") of
- false ->
- make_all_no_emakefile(Flags);
- true ->
- make:all(Flags)
- end.
-
-make_all_no_emakefile(Flags) ->
- case filelib:wildcard("*.beam") of
- [] ->
- %% Since there are no *.beam files, we will assume
- %% that this test suite was designed to be built and
- %% re-built by ts. Create an Emakefile so that
- %% make:all/1 will be run the next time too
- %% (in case a test suite is being interactively
- %% developed).
- create_emakefile(Flags, "*.erl");
- [_|_] ->
- %% There is no Emakefile and there already are
- %% some *.beam files here. Assume that this test
- %% suite was not designed to be re-built by ts.
- %% Only create a Emakefile that will compile
- %% generated *_SUITE_make files (if any).
- create_emakefile(Flags, "*_SUITE_make.erl")
- end.
-
-create_emakefile(Flags, Wc) ->
- case filelib:wildcard(Wc) of
- [] ->
- %% There are no files to be built (i.e. not even any
- %% generated *_SUITE_make.erl files). We must handle
- %% this case specially, because make:all/1 will crash
- %% on Emakefile with an empty list of modules.
- io:put_chars("No Emakefile found - not running make:all/1\n"),
- up_to_date;
- [_|_]=Ms0 ->
- io:format("Creating an Emakefile for compiling files matching ~s\n",
- [Wc]),
- Ms = [list_to_atom(filename:rootname(M, ".erl")) || M <- Ms0],
- Make0 = {Ms,Flags},
- Make = io_lib:format("~p. \n", [Make0]),
- ok = file:write_file("Emakefile", Make),
- make:all(Flags)
- end.
diff --git a/lib/test_server/test/Makefile b/lib/test_server/test/Makefile
index 702d73f5af..ab72a9d579 100644
--- a/lib/test_server/test/Makefile
+++ b/lib/test_server/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -27,11 +27,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES= \
test_server_SUITE \
test_server_line_SUITE \
- test_server_skip_SUITE \
- test_server_conf01_SUITE \
- test_server_conf02_SUITE \
- test_server_parallel01_SUITE \
- test_server_shuffle01_SUITE
+ test_server_test_lib
ERL_FILES= $(MODULES:%=%.erl)
@@ -52,6 +48,7 @@ RELSYSDIR = $(RELEASE_PATH)/test_server_test
ERL_MAKE_FLAGS += -pa $(ERL_TOP)/lib/test_server/ebin
ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include
+ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/test
EBIN = .
@@ -88,8 +85,8 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
$(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR)
- $(INSTALL_PROGRAM) test_server.spec $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) test_server.spec test_server.cover $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/test_server/test/test_server.cover b/lib/test_server/test/test_server.cover
index c2366db166..5c59bab494 100644
--- a/lib/test_server/test/test_server.cover
+++ b/lib/test_server/test/test_server.cover
@@ -1,20 +1,22 @@
-{exclude,[test_server,
- test_server_ctrl,
- ts_selftest]}.
+{incl_app,test_server,details}.
-%% Using include list here because the test_server might not find
+{excl_mods, test_server, [test_server,
+ test_server_ctrl,
+ ts_selftest]}.
+
+%% Using incl_mods list here because the test_server might not find
%% lib_dir for test_server - and so it will not find which modules to
%% compile.
-{include,[erl2html2,
- test_server_node,
- test_server_sup,
- ts,
- ts_autoconf_vxworks,
- ts_autoconf_win32,
- ts_erl_config,
- ts_install,
- ts_lib,
- ts_make,
- ts_run,
- vxworks_client]}.
+{incl_mods, test_server, [erl2html2,
+ test_server_node,
+ test_server_sup,
+ ts,
+ ts_autoconf_vxworks,
+ ts_autoconf_win32,
+ ts_erl_config,
+ ts_install,
+ ts_lib,
+ ts_make,
+ ts_run,
+ vxworks_client]}.
diff --git a/lib/test_server/test/test_server.spec b/lib/test_server/test/test_server.spec
index 23b0b71963..a3b4d01d08 100644
--- a/lib/test_server/test/test_server.spec
+++ b/lib/test_server/test/test_server.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../test_server_test"}}.
-{skip,{test_server_SUITE,skip_case7,"This case should be noted as `Skipped'"}}.
+{suites, "../test_server_test", all}.
diff --git a/lib/test_server/test/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE.erl
index dfe1028d3a..4c344717f0 100644
--- a/lib/test_server/test/test_server_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -16,539 +16,149 @@
%%
%% %CopyrightEnd%
%%
-
-%%%------------------------------------------------------------------
-%%% Test Server self test.
-%%%------------------------------------------------------------------
+%%%-------------------------------------------------------------------
+%%% @author Lukas Larsson <[email protected]>
+%%% @copyright (C) 2011, Erlang Solutions Ltd.
+%%% @doc
+%%%
+%%% @end
+%%% Created : 15 Feb 2011 by Lukas Larsson <[email protected]>
+%%%-------------------------------------------------------------------
-module(test_server_SUITE).
--include_lib("test_server/include/test_server.hrl").
--include_lib("test_server/include/test_server_line.hrl").
--include_lib("kernel/include/file.hrl").
--export([all/1]).
--export([init_per_suite/1, end_per_suite/1]).
--export([init_per_testcase/2, end_per_testcase/2, fin_per_testcase/2]).
--export([config/1, comment/1, timetrap/1, timetrap_cancel/1, multiply_timetrap/1,
- init_per_s/1, init_per_tc/1, end_per_tc/1,
- timeconv/1, msgs/1, capture/1, timecall/1,
- do_times/1, do_times_mfa/1, do_times_fun/1,
- skip_cases/1, skip_case1/1, skip_case2/1, skip_case3/1,
- skip_case4/1, skip_case5/1, skip_case6/1, skip_case7/1,
- skip_case8/1, skip_case9/1, undefined_functions/1,
- conf_init/1, check_new_conf/1, conf_cleanup/1,
- check_old_conf/1, conf_init_fail/1, start_stop_node/1,
- cleanup_nodes_init/1, check_survive_nodes/1, cleanup_nodes_fin/1,
- commercial/1]).
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include("test_server_test_lib.hrl").
--export([dummy_function/0,dummy_function/1,doer/1]).
+%%--------------------------------------------------------------------
+%% COMMON TEST CALLBACK FUNCTIONS
+%%--------------------------------------------------------------------
-all(doc) -> ["Test Server self test"];
-all(suite) ->
- [config, comment, timetrap, timetrap_cancel, multiply_timetrap,
- init_per_s, init_per_tc, end_per_tc,
- timeconv, msgs, capture, timecall, do_times, skip_cases,
- undefined_functions, commercial,
- {conf, conf_init, [check_new_conf], conf_cleanup},
- check_old_conf,
- {conf, conf_init_fail,[conf_member_skip],conf_cleanup_skip},
- start_stop_node,
- {conf, cleanup_nodes_init,[check_survive_nodes],cleanup_nodes_fin},
- config
- ].
+%% @spec suite() -> Info
+suite() ->
+ [{ct_hooks,[ts_install_cth,test_server_test_lib]}].
+%% @spec init_per_suite(Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
init_per_suite(Config) ->
- [{init_per_suite_var,ok}|Config].
+ [{path_dirs,[proplists:get_value(data_dir,Config)]} | Config].
+%% @spec end_per_suite(Config) -> _
end_per_suite(_Config) ->
- ok.
-
-init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog = ?t:timetrap(?t:minutes(2)),
- Config1 = [{watchdog, Dog}|Config],
- case Func of
- init_per_tc ->
- [{strange_var, 1}|Config1];
- skip_case8 ->
- {skipped, "This case should be noted as `Skipped'"};
- skip_case9 ->
- {skip, "This case should be noted as `Skipped'"};
- _ ->
- Config1
- end;
-init_per_testcase(Func, Config) ->
- io:format("Func:~p",[Func]),
- io:format("Config:~p",[Config]),
- ?t:fail("Arguments to init_per_testcase not correct").
-
-end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
- Dog=?config(watchdog, Config),
- ?t:timetrap_cancel(Dog),
- case Func of
- end_per_tc -> io:format("CLEANUP => this test case is ok\n");
- _Other -> ok
- end;
-end_per_testcase(Func, Config) ->
- io:format("Func:~p",[Func]),
- io:format("Config:~p",[Config]),
- ?t:fail("Arguments to end_per_testcase not correct").
-
-fin_per_testcase(Func, Config) ->
- io:format("Func:~p",[Func]),
- io:format("Config:~p",[Config]),
- ?t:fail("fin_per_testcase/2 called, should have called end_per_testcase/2").
+ io:format("TEST_SERVER_FRAMEWORK: ~p",[os:getenv("TEST_SERVER_FRAMEWORK")]),
+ ok.
+
+%% @spec init_per_group(GroupName, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+init_per_group(_GroupName, Config) ->
+ Config.
+
+%% @spec end_per_group(GroupName, Config0) ->
+%% void() | {save_config,Config1}
+end_per_group(_GroupName, _Config) ->
+ ok.
+
+%% @spec init_per_testcase(TestCase, Config0) ->
+%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1}
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+%% @spec end_per_testcase(TestCase, Config0) ->
+%% void() | {save_config,Config1} | {fail,Reason}
+end_per_testcase(_TestCase, _Config) ->
+ ok.
+
+%% @spec: groups() -> [Group]
+groups() ->
+ [].
+
+%% @spec all() -> GroupsAndTestCases | {skip,Reason}
+all() ->
+ [test_server_SUITE, test_server_parallel01_SUITE,
+ test_server_conf02_SUITE, test_server_conf01_SUITE,
+ test_server_skip_SUITE, test_server_shuffle01_SUITE].
+
+
+%%--------------------------------------------------------------------
+%% TEST CASES
+%%--------------------------------------------------------------------
+%% @spec TestCase(Config0) ->
+%% ok | exit() | {skip,Reason} | {comment,Comment} |
+%% {save_config,Config1} | {skip_and_save,Reason,Config1}
+test_server_SUITE(Config) ->
+% rpc:call(Node,dbg, tracer,[]),
+% rpc:call(Node,dbg, p,[all,c]),
+% rpc:call(Node,dbg, tpl,[test_server_ctrl,x]),
+ run_test_server_tests("test_server_SUITE", 39, 1, 31,
+ 20, 9, 1, 11, 2, 26, Config).
+
+test_server_parallel01_SUITE(Config) ->
+ run_test_server_tests("test_server_parallel01_SUITE", 37, 0, 19,
+ 19, 0, 0, 0, 0, 37, Config).
+
+test_server_shuffle01_SUITE(Config) ->
+ run_test_server_tests("test_server_shuffle01_SUITE", 130, 0, 0,
+ 76, 0, 0, 0, 0, 130, Config).
+
+test_server_skip_SUITE(Config) ->
+ run_test_server_tests("test_server_skip_SUITE", 3, 0, 1,
+ 0, 0, 1, 3, 0, 0, Config).
+
+test_server_conf01_SUITE(Config) ->
+ run_test_server_tests("test_server_conf01_SUITE", 24, 0, 12,
+ 12, 0, 0, 0, 0, 24, Config).
+
+test_server_conf02_SUITE(Config) ->
+ run_test_server_tests("test_server_conf02_SUITE", 26, 0, 12,
+ 12, 0, 0, 0, 0, 26, Config).
+
+
+run_test_server_tests(SuiteName, NCases, NFail, NExpected, NSucc,
+ NUsrSkip, NAutoSkip,
+ NActualSkip, NActualFail, NActualSucc, Config) ->
+ Node = proplists:get_value(node, Config),
+ {ok,_Pid} = rpc:call(Node,test_server_ctrl, start, []),
+ rpc:call(Node,
+ test_server_ctrl,add_dir_with_skip,
+ [SuiteName,
+ [proplists:get_value(data_dir,Config)],SuiteName,
+ [{test_server_SUITE,skip_case7,"SKIPPED!"}]]),
+
+ until(fun() ->
+ rpc:call(Node,test_server_ctrl,jobs,[]) =:= []
+ end),
-
-config(suite) -> [];
-config(doc) -> ["Test that the Config variable is decent, ",
- "and that the std config variables are correct ",
- "(check that data/priv dir exists)."
- "Also check that ?config macro works."];
-config(Config) when is_list(Config) ->
- is_tuplelist(Config),
- {value,{data_dir,Dd}}=lists:keysearch(data_dir,1,Config),
- {value,{priv_dir,Dp}}=lists:keysearch(priv_dir,1,Config),
- true=is_dir(Dd),
- {ok, _Bin}=file:read_file(filename:join(Dd, "dummy_file")),
- true=is_dir(Dp),
-
- Dd = ?config(data_dir,Config),
- Dp = ?config(priv_dir,Config),
- ok;
-config(_Config) ->
- ?t:fail("Config variable is not a list.").
-
-is_tuplelist([]) ->
- true;
-is_tuplelist([{_A,_B}|Rest]) ->
- is_tuplelist(Rest);
-is_tuplelist(_) ->
- false.
-
-is_dir(Dir) ->
- case file:read_file_info(Dir) of
- {ok, #file_info{type=directory}} ->
- true;
- _ ->
- false
- end.
-
-comment(suite) -> [];
-comment(doc) -> ["Print a comment in the HTML log"];
-comment(Config) when is_list(Config) ->
- ?t:comment("This comment should not occur in the HTML log because a later"
- " comment shall overwrite it"),
- ?t:comment("This comment is printed with the comment/1 function."
- " It should occur in the HTML log").
-
-
-
-timetrap(suite) -> [];
-timetrap(doc) -> ["Test that timetrap works."];
-timetrap(Config) when is_list(Config) ->
- TrapAfter = 3000,
- Dog=?t:timetrap(TrapAfter),
- process_flag(trap_exit, true),
- TimeOut = TrapAfter * test_server:timetrap_scale_factor() + 1000,
- receive
- {'EXIT', Dog, {timetrap_timeout, _, _}} ->
- ok;
- {'EXIT', _OtherPid, {timetrap_timeout, _, _}} ->
- ?t:fail("EXIT signal from wrong process")
- after
- TimeOut ->
- ?t:fail("Timetrap is not working.")
- end,
- ?t:timetrap_cancel(Dog),
- ok.
-
-
-timetrap_cancel(suite) -> [];
-timetrap_cancel(doc) -> ["Test that timetrap_cancel works."];
-timetrap_cancel(Config) when is_list(Config) ->
- Dog=?t:timetrap(1000),
- receive
- after
- 500 ->
- ok
- end,
- ?t:timetrap_cancel(Dog),
- receive
- after 1000 ->
- ok
- end,
- ok.
-
-multiply_timetrap(suite) -> [];
-multiply_timetrap(doc) -> ["Test multiply timetrap"];
-multiply_timetrap(Config) when is_list(Config) ->
- %% This simulates the call to test_server_ctrl:multiply_timetraps/1:
- put(test_server_multiply_timetraps,2),
-
- Dog = ?t:timetrap(500),
- timer:sleep(800),
- ?t:timetrap_cancel(Dog),
-
- %% Reset
- put(test_server_multiply_timetraps,1),
- ok.
-
-
-init_per_s(suite) -> [];
-init_per_s(doc) -> ["Test that a Config that is altered in ",
- "init_per_suite gets through to the testcases."];
-init_per_s(Config) ->
- %% Check that the config var sent from init_per_suite
- %% really exists.
- {value, {init_per_suite_var, ok}} =
- lists:keysearch(init_per_suite_var,1,Config),
-
- %% Check that the other variables still exist.
- {value,{data_dir,_Dd}}=lists:keysearch(data_dir,1,Config),
- {value,{priv_dir,_Dp}}=lists:keysearch(priv_dir,1,Config),
- ok.
-
-init_per_tc(suite) -> [];
-init_per_tc(doc) -> ["Test that a Config that is altered in ",
- "init_per_testcase gets through to the ",
- "actual testcase."];
-init_per_tc(Config) ->
- %% Check that the config var sent from init_per_testcase
- %% really exists.
- {value, {strange_var, 1}} = lists:keysearch(strange_var,1,Config),
-
- %% Check that the other variables still exist.
- {value,{data_dir,_Dd}}=lists:keysearch(data_dir,1,Config),
- {value,{priv_dir,_Dp}}=lists:keysearch(priv_dir,1,Config),
- ok.
-
-end_per_tc(suite) -> [];
-end_per_tc(doc) -> ["Test that end_per_testcase/2 is called even if"
- " test case fails"];
-end_per_tc(Config) when is_list(Config) ->
- ?t:fail("This case should fail! Check that \"CLEANUP\" is"
- " printed in the minor log file.").
-
-
-timeconv(suite) -> [];
-timeconv(doc) -> ["Test that the time unit conversion functions ",
- "works."];
-timeconv(Config) when is_list(Config) ->
- Val=2,
- Secs=Val*1000,
- Mins=Secs*60,
- Hrs=Mins*60,
- Secs=?t:seconds(2),
- Mins=?t:minutes(2),
- Hrs=?t:hours(2),
- ok.
-
-
-msgs(suite) -> [];
-msgs(doc) -> ["Tests the messages_get function."];
-msgs(Config) when is_list(Config) ->
- self() ! {hej, du},
- self() ! {lite, "data"},
- self() ! en_atom,
- [{hej, du}, {lite, "data"}, en_atom] = ?t:messages_get(),
- ok.
-
-capture(suite) -> [];
-capture(doc) -> ["Test that the capture functions work properly."];
-capture(Config) when is_list(Config) ->
- String1="abcedfghjiklmnopqrstuvwxyz",
- String2="0123456789",
- ?t:capture_start(),
- io:format(String1),
- [String1]=?t:capture_get(),
- io:format(String2),
- [String2]=?t:capture_get(),
- ?t:capture_stop(),
- []=?t:capture_get(),
- io:format(String2),
- []=?t:capture_get(),
- ok.
-
-timecall(suite) -> [];
-timecall(doc) -> ["Tests that timed calls work."];
-timecall(Config) when is_list(Config) ->
- {_Time1, liten_apa_e_oxo_farlig} = ?t:timecall(?MODULE, dummy_function, []),
- {Time2, jag_ar_en_gorilla} = ?t:timecall(?MODULE, dummy_function, [gorilla]),
- DTime=round(Time2),
- if
- DTime<1 ->
- ?t:fail("Timecall reported a too low time.");
- DTime==1 ->
+ rpc:call(Node,test_server_ctrl, stop, []),
+ {ok,#suite{ n_cases = NCases,
+ n_cases_failed = NFail,
+ n_cases_expected = NExpected,
+ n_cases_succ = NSucc,
+ n_cases_user_skip = NUsrSkip,
+ n_cases_auto_skip = NAutoSkip,
+ cases = Cases }} = Data =
+ test_server_test_lib:parse_suite(
+ hd(filelib:wildcard(
+ filename:join([proplists:get_value(priv_dir, Config),
+ SuiteName++".logs","run*","suite.log"])))),
+ {NActualSkip,NActualFail,NActualSucc} =
+ lists:foldl(fun(#tc{ result = skip },{S,F,Su}) ->
+ {S+1,F,Su};
+ (#tc{ result = ok },{S,F,Su}) ->
+ {S,F,Su+1};
+ (#tc{ result = failed },{S,F,Su}) ->
+ {S,F+1,Su}
+ end,{0,0,0},Cases),
+ Data.
+
+until(Fun) ->
+ case Fun() of
+ true ->
ok;
- DTime>1 ->
- ?t:fail("Timecall reported a too high time.")
- end,
- ok.
-
-dummy_function() ->
- liten_apa_e_oxo_farlig.
-dummy_function(gorilla) ->
- receive after 1000 -> ok end,
- jag_ar_en_gorilla.
-
-
-do_times(suite) -> [do_times_mfa, do_times_fun];
-do_times(doc) -> ["Test the do_times function."].
-
-do_times_mfa(suite) -> [];
-do_times_mfa(doc) -> ["Test the do_times function with M,F,A given."];
-do_times_mfa(Config) when is_list(Config) ->
- ?t:do_times(100, ?MODULE, doer, [self()]),
- 100=length(?t:messages_get()),
- ok.
-
-do_times_fun(suite) -> [];
-do_times_fun(doc) -> ["Test the do_times function with fun given."];
-do_times_fun(Config) when is_list(Config) ->
- Self = self(),
- ?t:do_times(100, fun() -> doer(Self) end),
- 100=length(?t:messages_get()),
- ok.
-
-doer(From) ->
- From ! a,
- ok.
-
-skip_cases(doc) -> ["Test all possible ways to skip a test case."];
-skip_cases(suite) -> [skip_case1, skip_case2, skip_case3, skip_case4,
- skip_case5, skip_case6, skip_case7, skip_case8,
- skip_case9].
-
-skip_case1(suite) -> [];
-skip_case1(doc) -> ["Test that you can return {skipped, Reason},"
- " and that Reason is in the comment field in the HTML log"];
-skip_case1(Config) when is_list(Config) ->
- %% If this comment shows, the case failed!!
- ?t:comment("ERROR: This case should have been noted as `Skipped'"),
- %% The Reason in {skipped, Reason} should overwrite a 'comment'
- {skipped, "This case should be noted as `Skipped'"}.
-
-skip_case2(suite) -> [];
-skip_case2(doc) -> ["Test that you can return {skipped, Reason},"
- " and that Reason is in the comment field in the HTML log"];
-skip_case2(Config) when is_list(Config) ->
- %% If this comment shows, the case failed!!
- ?t:comment("ERROR: This case should have been noted as `Skipped'"),
- %% The Reason in {skipped, Reason} should overwrite a 'comment'
- exit({skipped, "This case should be noted as `Skipped'"}).
-
-skip_case3(suite) -> [];
-skip_case3(doc) -> ["Test that you can return {skip, Reason},"
- " and that Reason is in the comment field in the HTML log"];
-skip_case3(Config) when is_list(Config) ->
- %% If this comment shows, the case failed!!
- ?t:comment("ERROR: This case should have been noted as `Skipped'"),
- %% The Reason in {skip, Reason} should overwrite a 'comment'
- {skip, "This case should be noted as `Skipped'"}.
-
-skip_case4(suite) -> [];
-skip_case4(doc) -> ["Test that you can return {skip, Reason},"
- " and that Reason is in the comment field in the HTML log"];
-skip_case4(Config) when is_list(Config) ->
- %% If this comment shows, the case failed!!
- ?t:comment("ERROR: This case should have been noted as `Skipped'"),
- %% The Reason in {skip, Reason} should overwrite a 'comment'
- exit({skip, "This case should be noted as `Skipped'"}).
-
-skip_case5(suite) -> {skipped, "This case should be noted as `Skipped'"};
-skip_case5(doc) -> ["Test that you can return {skipped, Reason}"
- " from the specification clause"].
-
-skip_case6(suite) -> {skip, "This case should be noted as `Skipped'"};
-skip_case6(doc) -> ["Test that you can return {skip, Reason}"
- " from the specification clause"].
-
-skip_case7(suite) -> [];
-skip_case7(doc) -> ["Test that skip works from a test specification file"];
-skip_case7(Config) when is_list(Config) ->
- %% This case shall be skipped by adding
- %% {skip, {test_server_SUITE, skip_case7, Reason}}.
- %% to the test specification file.
- ?t:fail("This case should have been Skipped by the .spec file").
-
-skip_case8(suite) -> [];
-skip_case8(doc) -> ["Test that {skipped, Reason} works from"
- " init_per_testcase/2"];
-skip_case8(Config) when is_list(Config) ->
- %% This case shall be skipped by adding a specific clause to
- %% returning {skipped, Reason} from init_per_testcase/2 for this case.
- ?t:fail("This case should have been Skipped by init_per_testcase/2").
-
-skip_case9(suite) -> [];
-skip_case9(doc) -> ["Test that {skip, Reason} works from a init_per_testcase/2"];
-skip_case9(Config) when is_list(Config) ->
- %% This case shall be skipped by adding a specific clause to
- %% returning {skip, Reason} from init_per_testcase/2 for this case.
- ?t:fail("This case should have been Skipped by init_per_testcase/2").
-
-undefined_functions(suite) -> [];
-undefined_functions(doc) -> ["Check for calls to undefined functions in"
- " test_server."
- "Skip if cover is running"];
-undefined_functions(Config) when is_list(Config) ->
- case whereis(cover_server) of
- Pid when is_pid(Pid) ->
- {skip,"Cover is running"};
- undefined ->
- undefined_functions()
- end.
-
-undefined_functions() ->
- TestServerDir = filename:dirname(code:which(test_server)),
- Res = xref:d(TestServerDir),
-
- {value,{unused,Unused}} = lists:keysearch(unused, 1, Res),
- case Unused of
- [] -> ok;
- _ ->
- lists:foreach(fun (MFA) ->
- io:format("~s unused", [format_mfa(MFA)])
- end, Unused)
- end,
-
- {value,{undefined,Undef0}} = lists:keysearch(undefined, 1, Res),
- Undef = [U || U <- Undef0, not unresolved(U)],
- case Undef of
- [] -> ok;
- _ ->
- lists:foreach(fun ({MFA1,MFA2}) ->
- io:format("~s calls undefined ~s",
- [format_mfa(MFA1),format_mfa(MFA2)])
- end, Undef),
- ?t:fail({length(Undef),undefined_functions_in_otp})
- end,
- ok.
-
-unresolved({_,{_,'$F_EXPR',_}}) -> true;
-unresolved(_) -> false.
-
-format_mfa({M,F,A}) ->
- lists:flatten(io_lib:format("~s:~s/~p", [M,F,A])).
-
-conf_init(doc) -> ["Test successful conf case: Change Config parameter"];
-conf_init(Config) when is_list(Config) ->
- [{conf_init_var,1389}|Config].
-
-check_new_conf(suite) -> [];
-check_new_conf(doc) -> ["Check that Config parameter changed by"
- " conf_init is used"];
-check_new_conf(Config) when is_list(Config) ->
- 1389 = ?config(conf_init_var,Config),
- ok.
-
-conf_cleanup(doc) -> ["Test successful conf case: Restore Config parameter"];
-conf_cleanup(Config) when is_list(Config) ->
- lists:keydelete(conf_init_var,1,Config).
-
-check_old_conf(suite) -> [];
-check_old_conf(doc) -> ["Test that the restored Config is used after a"
- " conf cleanup"];
-check_old_conf(Config) when is_list(Config) ->
- undefined = ?config(conf_init_var,Config),
- ok.
-
-conf_init_fail(doc) -> ["Test that config members are skipped if"
- " conf init function fails."];
-conf_init_fail(Config) when is_list(Config) ->
- ?t:fail("This case should fail! Check that conf_member_skip and"
- " conf_cleanup_skip are skipped.").
-
-
-
-start_stop_node(suite) -> [];
-start_stop_node(doc) -> ["Test start and stop of slave and peer nodes"];
-start_stop_node(Config) when is_list(Config) ->
- {ok,Node2} = ?t:start_node(node2,peer,[]),
- {error, _} = ?t:start_node(node2,peer,[{fail_on_error,false}]),
- true = lists:member(Node2,nodes()),
-
- {ok,Node3} = ?t:start_node(node3,slave,[]),
- {error, _} = ?t:start_node(node3,slave,[]),
- true = lists:member(Node3,nodes()),
-
- {ok,Node4} = ?t:start_node(node4,peer,[{wait,false}]),
- case lists:member(Node4,nodes()) of
- true ->
- ?t:comment("WARNING: Node started with {wait,false}"
- " is up faster than expected...");
false ->
- wait_for_node(Node4,0),
- true = lists:member(Node4,nodes())
- end,
-
- true = ?t:stop_node(Node2),
- false = lists:member(Node2,nodes()),
-
- true = ?t:stop_node(Node3),
- false = lists:member(Node3,nodes()),
-
- true = ?t:stop_node(Node4),
- false = lists:member(Node4,nodes()),
- timer:sleep(2000),
- false = ?t:stop_node(Node4),
-
- ok.
-
-
-wait_for_node(Node,Acc) ->
- case net_adm:ping(Node) of
- pang ->
timer:sleep(100),
- wait_for_node(Node,Acc+100);
- pong ->
- Acc
+ until(Fun)
end.
-
-cleanup_nodes_init(doc) -> ["Test that nodes are terminated when test case"
- " is finished unless {cleanup,false} is given."];
-cleanup_nodes_init(Config) when is_list(Config) ->
- {ok,DieSlave} = ?t:start_node(die_slave, slave, []),
- {ok,SurviveSlave} = ?t:start_node(survive_slave, slave, [{cleanup,false}]),
- {ok,DiePeer} = ?t:start_node(die_peer, peer, []),
- {ok,SurvivePeer} = ?t:start_node(survive_peer, peer, [{cleanup,false}]),
- [{die_slave,DieSlave},
- {survive_slave,SurviveSlave},
- {die_peer,DiePeer},
- {survive_peer,SurvivePeer} | Config].
-
-
-
-check_survive_nodes(suite) -> [];
-check_survive_nodes(doc) -> ["Test that nodes with {cleanup,false} survived"];
-check_survive_nodes(Config) when is_list(Config) ->
- timer:sleep(1000),
- false = lists:member(?config(die_slave,Config),nodes()),
- true = lists:member(?config(survive_slave,Config),nodes()),
- false = lists:member(?config(die_peer,Config),nodes()),
- true = lists:member(?config(survive_peer,Config),nodes()),
- ok.
-
-
-cleanup_nodes_fin(doc) -> ["Test that nodes started with {cleanup,false}"
- " can be stopped"];
-cleanup_nodes_fin(Config) when is_list(Config) ->
- Slave = ?config(survive_slave,Config),
- Peer = ?config(survive_peer,Config),
-
- true = ?t:stop_node(Slave),
- false = lists:member(Slave,nodes()),
- true = ?t:stop_node(Peer),
- false = lists:member(Peer,nodes()),
-
- C1 = lists:keydelete(die_slave,1,Config),
- C2 = lists:keydelete(survive_slave,1,C1),
- C3 = lists:keydelete(die_peer,1,C2),
- lists:keydelete(survive_peer,1,C3).
-
-commercial(Config) when is_list(Config) ->
- case ?t:is_commercial() of
- false -> {comment,"Open-source build"};
- true -> {comment,"Commercial build"}
- end.
-
-
+
diff --git a/lib/test_server/test/test_server_SUITE_data/Makefile.src b/lib/test_server/test/test_server_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..d5af919eec
--- /dev/null
+++ b/lib/test_server/test/test_server_SUITE_data/Makefile.src
@@ -0,0 +1,2 @@
+all:
+ erlc *.erl \ No newline at end of file
diff --git a/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl
new file mode 100644
index 0000000000..dfcdff0c3e
--- /dev/null
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_SUITE.erl
@@ -0,0 +1,554 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%------------------------------------------------------------------
+%%% Test Server self test.
+%%%------------------------------------------------------------------
+-module(test_server_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+-include_lib("test_server/include/test_server_line.hrl").
+-include_lib("kernel/include/file.hrl").
+-export([all/1]).
+
+-export([init_per_suite/1, end_per_suite/1]).
+-export([init_per_testcase/2, end_per_testcase/2, fin_per_testcase/2]).
+-export([config/1, comment/1, timetrap/1, timetrap_cancel/1, multiply_timetrap/1,
+ init_per_s/1, init_per_tc/1, end_per_tc/1,
+ timeconv/1, msgs/1, capture/1, timecall/1,
+ do_times/1, do_times_mfa/1, do_times_fun/1,
+ skip_cases/1, skip_case1/1, skip_case2/1, skip_case3/1,
+ skip_case4/1, skip_case5/1, skip_case6/1, skip_case7/1,
+ skip_case8/1, skip_case9/1, undefined_functions/1,
+ conf_init/1, check_new_conf/1, conf_cleanup/1,
+ check_old_conf/1, conf_init_fail/1, start_stop_node/1,
+ cleanup_nodes_init/1, check_survive_nodes/1, cleanup_nodes_fin/1,
+ commercial/1]).
+
+-export([dummy_function/0,dummy_function/1,doer/1]).
+
+all(doc) -> ["Test Server self test"];
+all(suite) ->
+ [config, comment, timetrap, timetrap_cancel, multiply_timetrap,
+ init_per_s, init_per_tc, end_per_tc,
+ timeconv, msgs, capture, timecall, do_times, skip_cases,
+ undefined_functions, commercial,
+ {conf, conf_init, [check_new_conf], conf_cleanup},
+ check_old_conf,
+ {conf, conf_init_fail,[conf_member_skip],conf_cleanup_skip},
+ start_stop_node,
+ {conf, cleanup_nodes_init,[check_survive_nodes],cleanup_nodes_fin},
+ config
+ ].
+
+
+init_per_suite(Config) ->
+ [{init_per_suite_var,ok}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ Dog = ?t:timetrap(?t:minutes(2)),
+ Config1 = [{watchdog, Dog}|Config],
+ case Func of
+ init_per_tc ->
+ [{strange_var, 1}|Config1];
+ skip_case8 ->
+ {skipped, "This case should be noted as `Skipped'"};
+ skip_case9 ->
+ {skip, "This case should be noted as `Skipped'"};
+ _ ->
+ Config1
+ end;
+init_per_testcase(Func, Config) ->
+ io:format("Func:~p",[Func]),
+ io:format("Config:~p",[Config]),
+ ?t:fail("Arguments to init_per_testcase not correct").
+
+end_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
+ Dog=?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ case Func of
+ end_per_tc -> io:format("CLEANUP => this test case is ok\n");
+ _Other -> ok
+ end;
+end_per_testcase(Func, Config) ->
+ io:format("Func:~p",[Func]),
+ io:format("Config:~p",[Config]),
+ ?t:fail("Arguments to end_per_testcase not correct").
+
+fin_per_testcase(Func, Config) ->
+ io:format("Func:~p",[Func]),
+ io:format("Config:~p",[Config]),
+ ?t:fail("fin_per_testcase/2 called, should have called end_per_testcase/2").
+
+
+config(suite) -> [];
+config(doc) -> ["Test that the Config variable is decent, ",
+ "and that the std config variables are correct ",
+ "(check that data/priv dir exists)."
+ "Also check that ?config macro works."];
+config(Config) when is_list(Config) ->
+ is_tuplelist(Config),
+ {value,{data_dir,Dd}}=lists:keysearch(data_dir,1,Config),
+ {value,{priv_dir,Dp}}=lists:keysearch(priv_dir,1,Config),
+ true=is_dir(Dd),
+ {ok, _Bin}=file:read_file(filename:join(Dd, "dummy_file")),
+ true=is_dir(Dp),
+
+ Dd = ?config(data_dir,Config),
+ Dp = ?config(priv_dir,Config),
+ ok;
+config(_Config) ->
+ ?t:fail("Config variable is not a list.").
+
+is_tuplelist([]) ->
+ true;
+is_tuplelist([{_A,_B}|Rest]) ->
+ is_tuplelist(Rest);
+is_tuplelist(_) ->
+ false.
+
+is_dir(Dir) ->
+ case file:read_file_info(Dir) of
+ {ok, #file_info{type=directory}} ->
+ true;
+ _ ->
+ false
+ end.
+
+comment(suite) -> [];
+comment(doc) -> ["Print a comment in the HTML log"];
+comment(Config) when is_list(Config) ->
+ ?t:comment("This comment should not occur in the HTML log because a later"
+ " comment shall overwrite it"),
+ ?t:comment("This comment is printed with the comment/1 function."
+ " It should occur in the HTML log").
+
+
+
+timetrap(suite) -> [];
+timetrap(doc) -> ["Test that timetrap works."];
+timetrap(Config) when is_list(Config) ->
+ TrapAfter = 3000,
+ Dog=?t:timetrap(TrapAfter),
+ process_flag(trap_exit, true),
+ TimeOut = TrapAfter * test_server:timetrap_scale_factor() + 1000,
+ receive
+ {'EXIT', Dog, {timetrap_timeout, _, _}} ->
+ ok;
+ {'EXIT', _OtherPid, {timetrap_timeout, _, _}} ->
+ ?t:fail("EXIT signal from wrong process")
+ after
+ TimeOut ->
+ ?t:fail("Timetrap is not working.")
+ end,
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+
+timetrap_cancel(suite) -> [];
+timetrap_cancel(doc) -> ["Test that timetrap_cancel works."];
+timetrap_cancel(Config) when is_list(Config) ->
+ Dog=?t:timetrap(1000),
+ receive
+ after
+ 500 ->
+ ok
+ end,
+ ?t:timetrap_cancel(Dog),
+ receive
+ after 1000 ->
+ ok
+ end,
+ ok.
+
+multiply_timetrap(suite) -> [];
+multiply_timetrap(doc) -> ["Test multiply timetrap"];
+multiply_timetrap(Config) when is_list(Config) ->
+ %% This simulates the call to test_server_ctrl:multiply_timetraps/1:
+ put(test_server_multiply_timetraps,{2,true}),
+
+ Dog = ?t:timetrap(500),
+ timer:sleep(800),
+ ?t:timetrap_cancel(Dog),
+
+ %% Reset
+ put(test_server_multiply_timetraps,1),
+ ok.
+
+
+init_per_s(suite) -> [];
+init_per_s(doc) -> ["Test that a Config that is altered in ",
+ "init_per_suite gets through to the testcases."];
+init_per_s(Config) ->
+ %% Check that the config var sent from init_per_suite
+ %% really exists.
+ {value, {init_per_suite_var, ok}} =
+ lists:keysearch(init_per_suite_var,1,Config),
+
+ %% Check that the other variables still exist.
+ {value,{data_dir,_Dd}}=lists:keysearch(data_dir,1,Config),
+ {value,{priv_dir,_Dp}}=lists:keysearch(priv_dir,1,Config),
+ ok.
+
+init_per_tc(suite) -> [];
+init_per_tc(doc) -> ["Test that a Config that is altered in ",
+ "init_per_testcase gets through to the ",
+ "actual testcase."];
+init_per_tc(Config) ->
+ %% Check that the config var sent from init_per_testcase
+ %% really exists.
+ {value, {strange_var, 1}} = lists:keysearch(strange_var,1,Config),
+
+ %% Check that the other variables still exist.
+ {value,{data_dir,_Dd}}=lists:keysearch(data_dir,1,Config),
+ {value,{priv_dir,_Dp}}=lists:keysearch(priv_dir,1,Config),
+ ok.
+
+end_per_tc(suite) -> [];
+end_per_tc(doc) -> ["Test that end_per_testcase/2 is called even if"
+ " test case fails"];
+end_per_tc(Config) when is_list(Config) ->
+ ?t:fail("This case should fail! Check that \"CLEANUP\" is"
+ " printed in the minor log file.").
+
+
+timeconv(suite) -> [];
+timeconv(doc) -> ["Test that the time unit conversion functions ",
+ "works."];
+timeconv(Config) when is_list(Config) ->
+ Val=2,
+ Secs=Val*1000,
+ Mins=Secs*60,
+ Hrs=Mins*60,
+ Secs=?t:seconds(2),
+ Mins=?t:minutes(2),
+ Hrs=?t:hours(2),
+ ok.
+
+
+msgs(suite) -> [];
+msgs(doc) -> ["Tests the messages_get function."];
+msgs(Config) when is_list(Config) ->
+ self() ! {hej, du},
+ self() ! {lite, "data"},
+ self() ! en_atom,
+ [{hej, du}, {lite, "data"}, en_atom] = ?t:messages_get(),
+ ok.
+
+capture(suite) -> [];
+capture(doc) -> ["Test that the capture functions work properly."];
+capture(Config) when is_list(Config) ->
+ String1="abcedfghjiklmnopqrstuvwxyz",
+ String2="0123456789",
+ ?t:capture_start(),
+ io:format(String1),
+ [String1]=?t:capture_get(),
+ io:format(String2),
+ [String2]=?t:capture_get(),
+ ?t:capture_stop(),
+ []=?t:capture_get(),
+ io:format(String2),
+ []=?t:capture_get(),
+ ok.
+
+timecall(suite) -> [];
+timecall(doc) -> ["Tests that timed calls work."];
+timecall(Config) when is_list(Config) ->
+ {_Time1, liten_apa_e_oxo_farlig} = ?t:timecall(?MODULE, dummy_function, []),
+ {Time2, jag_ar_en_gorilla} = ?t:timecall(?MODULE, dummy_function, [gorilla]),
+ DTime=round(Time2),
+ if
+ DTime<1 ->
+ ?t:fail("Timecall reported a too low time.");
+ DTime==1 ->
+ ok;
+ DTime>1 ->
+ ?t:fail("Timecall reported a too high time.")
+ end,
+ ok.
+
+dummy_function() ->
+ liten_apa_e_oxo_farlig.
+dummy_function(gorilla) ->
+ receive after 1000 -> ok end,
+ jag_ar_en_gorilla.
+
+
+do_times(suite) -> [do_times_mfa, do_times_fun];
+do_times(doc) -> ["Test the do_times function."].
+
+do_times_mfa(suite) -> [];
+do_times_mfa(doc) -> ["Test the do_times function with M,F,A given."];
+do_times_mfa(Config) when is_list(Config) ->
+ ?t:do_times(100, ?MODULE, doer, [self()]),
+ 100=length(?t:messages_get()),
+ ok.
+
+do_times_fun(suite) -> [];
+do_times_fun(doc) -> ["Test the do_times function with fun given."];
+do_times_fun(Config) when is_list(Config) ->
+ Self = self(),
+ ?t:do_times(100, fun() -> doer(Self) end),
+ 100=length(?t:messages_get()),
+ ok.
+
+doer(From) ->
+ From ! a,
+ ok.
+
+skip_cases(doc) -> ["Test all possible ways to skip a test case."];
+skip_cases(suite) -> [skip_case1, skip_case2, skip_case3, skip_case4,
+ skip_case5, skip_case6, skip_case7, skip_case8,
+ skip_case9].
+
+skip_case1(suite) -> [];
+skip_case1(doc) -> ["Test that you can return {skipped, Reason},"
+ " and that Reason is in the comment field in the HTML log"];
+skip_case1(Config) when is_list(Config) ->
+ %% If this comment shows, the case failed!!
+ ?t:comment("ERROR: This case should have been noted as `Skipped'"),
+ %% The Reason in {skipped, Reason} should overwrite a 'comment'
+ {skipped, "This case should be noted as `Skipped'"}.
+
+skip_case2(suite) -> [];
+skip_case2(doc) -> ["Test that you can return {skipped, Reason},"
+ " and that Reason is in the comment field in the HTML log"];
+skip_case2(Config) when is_list(Config) ->
+ %% If this comment shows, the case failed!!
+ ?t:comment("ERROR: This case should have been noted as `Skipped'"),
+ %% The Reason in {skipped, Reason} should overwrite a 'comment'
+ exit({skipped, "This case should be noted as `Skipped'"}).
+
+skip_case3(suite) -> [];
+skip_case3(doc) -> ["Test that you can return {skip, Reason},"
+ " and that Reason is in the comment field in the HTML log"];
+skip_case3(Config) when is_list(Config) ->
+ %% If this comment shows, the case failed!!
+ ?t:comment("ERROR: This case should have been noted as `Skipped'"),
+ %% The Reason in {skip, Reason} should overwrite a 'comment'
+ {skip, "This case should be noted as `Skipped'"}.
+
+skip_case4(suite) -> [];
+skip_case4(doc) -> ["Test that you can return {skip, Reason},"
+ " and that Reason is in the comment field in the HTML log"];
+skip_case4(Config) when is_list(Config) ->
+ %% If this comment shows, the case failed!!
+ ?t:comment("ERROR: This case should have been noted as `Skipped'"),
+ %% The Reason in {skip, Reason} should overwrite a 'comment'
+ exit({skip, "This case should be noted as `Skipped'"}).
+
+skip_case5(suite) -> {skipped, "This case should be noted as `Skipped'"};
+skip_case5(doc) -> ["Test that you can return {skipped, Reason}"
+ " from the specification clause"].
+
+skip_case6(suite) -> {skip, "This case should be noted as `Skipped'"};
+skip_case6(doc) -> ["Test that you can return {skip, Reason}"
+ " from the specification clause"].
+
+skip_case7(suite) -> [];
+skip_case7(doc) -> ["Test that skip works from a test specification file"];
+skip_case7(Config) when is_list(Config) ->
+ %% This case shall be skipped by adding
+ %% {skip, {test_server_SUITE, skip_case7, Reason}}.
+ %% to the test specification file.
+ ?t:fail("This case should have been Skipped by the .spec file").
+
+skip_case8(suite) -> [];
+skip_case8(doc) -> ["Test that {skipped, Reason} works from"
+ " init_per_testcase/2"];
+skip_case8(Config) when is_list(Config) ->
+ %% This case shall be skipped by adding a specific clause to
+ %% returning {skipped, Reason} from init_per_testcase/2 for this case.
+ ?t:fail("This case should have been Skipped by init_per_testcase/2").
+
+skip_case9(suite) -> [];
+skip_case9(doc) -> ["Test that {skip, Reason} works from a init_per_testcase/2"];
+skip_case9(Config) when is_list(Config) ->
+ %% This case shall be skipped by adding a specific clause to
+ %% returning {skip, Reason} from init_per_testcase/2 for this case.
+ ?t:fail("This case should have been Skipped by init_per_testcase/2").
+
+undefined_functions(suite) -> [];
+undefined_functions(doc) -> ["Check for calls to undefined functions in"
+ " test_server."
+ "Skip if cover is running"];
+undefined_functions(Config) when is_list(Config) ->
+ case whereis(cover_server) of
+ Pid when is_pid(Pid) ->
+ {skip,"Cover is running"};
+ undefined ->
+ undefined_functions()
+ end.
+
+undefined_functions() ->
+ TestServerDir = filename:dirname(code:which(test_server)),
+ Res = xref:d(TestServerDir),
+
+ {value,{unused,Unused}} = lists:keysearch(unused, 1, Res),
+ case Unused of
+ [] -> ok;
+ _ ->
+ lists:foreach(fun (MFA) ->
+ io:format("~s unused", [format_mfa(MFA)])
+ end, Unused)
+ end,
+
+ {value,{undefined,Undef0}} = lists:keysearch(undefined, 1, Res),
+ Undef = [U || U <- Undef0, not unresolved(U)],
+ case Undef of
+ [] -> ok;
+ _ ->
+ lists:foreach(fun ({MFA1,MFA2}) ->
+ io:format("~s calls undefined ~s",
+ [format_mfa(MFA1),format_mfa(MFA2)])
+ end, Undef),
+ ?t:fail({length(Undef),undefined_functions_in_otp})
+ end,
+ ok.
+
+unresolved({_,{_,'$F_EXPR',_}}) -> true;
+unresolved(_) -> false.
+
+format_mfa({M,F,A}) ->
+ lists:flatten(io_lib:format("~s:~s/~p", [M,F,A])).
+
+conf_init(doc) -> ["Test successful conf case: Change Config parameter"];
+conf_init(Config) when is_list(Config) ->
+ [{conf_init_var,1389}|Config].
+
+check_new_conf(suite) -> [];
+check_new_conf(doc) -> ["Check that Config parameter changed by"
+ " conf_init is used"];
+check_new_conf(Config) when is_list(Config) ->
+ 1389 = ?config(conf_init_var,Config),
+ ok.
+
+conf_cleanup(doc) -> ["Test successful conf case: Restore Config parameter"];
+conf_cleanup(Config) when is_list(Config) ->
+ lists:keydelete(conf_init_var,1,Config).
+
+check_old_conf(suite) -> [];
+check_old_conf(doc) -> ["Test that the restored Config is used after a"
+ " conf cleanup"];
+check_old_conf(Config) when is_list(Config) ->
+ undefined = ?config(conf_init_var,Config),
+ ok.
+
+conf_init_fail(doc) -> ["Test that config members are skipped if"
+ " conf init function fails."];
+conf_init_fail(Config) when is_list(Config) ->
+ ?t:fail("This case should fail! Check that conf_member_skip and"
+ " conf_cleanup_skip are skipped.").
+
+
+
+start_stop_node(suite) -> [];
+start_stop_node(doc) -> ["Test start and stop of slave and peer nodes"];
+start_stop_node(Config) when is_list(Config) ->
+ {ok,Node2} = ?t:start_node(node2,peer,[]),
+ {error, _} = ?t:start_node(node2,peer,[{fail_on_error,false}]),
+ true = lists:member(Node2,nodes()),
+
+ {ok,Node3} = ?t:start_node(node3,slave,[]),
+ {error, _} = ?t:start_node(node3,slave,[]),
+ true = lists:member(Node3,nodes()),
+
+ {ok,Node4} = ?t:start_node(node4,peer,[{wait,false}]),
+ case lists:member(Node4,nodes()) of
+ true ->
+ ?t:comment("WARNING: Node started with {wait,false}"
+ " is up faster than expected...");
+ false ->
+ wait_for_node(Node4,0),
+ true = lists:member(Node4,nodes())
+ end,
+
+ true = ?t:stop_node(Node2),
+ false = lists:member(Node2,nodes()),
+
+ true = ?t:stop_node(Node3),
+ false = lists:member(Node3,nodes()),
+
+ true = ?t:stop_node(Node4),
+ false = lists:member(Node4,nodes()),
+ timer:sleep(2000),
+ false = ?t:stop_node(Node4),
+
+ ok.
+
+
+wait_for_node(Node,Acc) ->
+ case net_adm:ping(Node) of
+ pang ->
+ timer:sleep(100),
+ wait_for_node(Node,Acc+100);
+ pong ->
+ Acc
+ end.
+
+cleanup_nodes_init(doc) -> ["Test that nodes are terminated when test case"
+ " is finished unless {cleanup,false} is given."];
+cleanup_nodes_init(Config) when is_list(Config) ->
+ {ok,DieSlave} = ?t:start_node(die_slave, slave, []),
+ {ok,SurviveSlave} = ?t:start_node(survive_slave, slave, [{cleanup,false}]),
+ {ok,DiePeer} = ?t:start_node(die_peer, peer, []),
+ {ok,SurvivePeer} = ?t:start_node(survive_peer, peer, [{cleanup,false}]),
+ [{die_slave,DieSlave},
+ {survive_slave,SurviveSlave},
+ {die_peer,DiePeer},
+ {survive_peer,SurvivePeer} | Config].
+
+
+
+check_survive_nodes(suite) -> [];
+check_survive_nodes(doc) -> ["Test that nodes with {cleanup,false} survived"];
+check_survive_nodes(Config) when is_list(Config) ->
+ timer:sleep(1000),
+ false = lists:member(?config(die_slave,Config),nodes()),
+ true = lists:member(?config(survive_slave,Config),nodes()),
+ false = lists:member(?config(die_peer,Config),nodes()),
+ true = lists:member(?config(survive_peer,Config),nodes()),
+ ok.
+
+
+cleanup_nodes_fin(doc) -> ["Test that nodes started with {cleanup,false}"
+ " can be stopped"];
+cleanup_nodes_fin(Config) when is_list(Config) ->
+ Slave = ?config(survive_slave,Config),
+ Peer = ?config(survive_peer,Config),
+
+ true = ?t:stop_node(Slave),
+ false = lists:member(Slave,nodes()),
+ true = ?t:stop_node(Peer),
+ false = lists:member(Peer,nodes()),
+
+ C1 = lists:keydelete(die_slave,1,Config),
+ C2 = lists:keydelete(survive_slave,1,C1),
+ C3 = lists:keydelete(die_peer,1,C2),
+ lists:keydelete(survive_peer,1,C3).
+
+commercial(Config) when is_list(Config) ->
+ case ?t:is_commercial() of
+ false -> {comment,"Open-source build"};
+ true -> {comment,"Commercial build"}
+ end.
+
+
diff --git a/lib/test_server/test/test_server_SUITE_data/dummy_file b/lib/test_server/test/test_server_SUITE_data/test_server_SUITE_data/dummy_file
index 65c88fbd75..65c88fbd75 100644
--- a/lib/test_server/test/test_server_SUITE_data/dummy_file
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_SUITE_data/dummy_file
diff --git a/lib/test_server/test/test_server_conf01_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_conf01_SUITE.erl
index a6d7dfe851..06e0ea80c4 100644
--- a/lib/test_server/test/test_server_conf01_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_conf01_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/test/test_server_conf02_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_conf02_SUITE.erl
index deba4660c6..ccc0f12bf5 100644
--- a/lib/test_server/test/test_server_conf02_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_conf02_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/test/test_server_parallel01_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
index 0e7f329f89..f38f768f3b 100644
--- a/lib/test_server/test/test_server_parallel01_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_parallel01_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/test/test_server_shuffle01_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_shuffle01_SUITE.erl
index 7ad269501d..0faf50a345 100644
--- a/lib/test_server/test/test_server_shuffle01_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_shuffle01_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/test/test_server_skip_SUITE.erl b/lib/test_server/test/test_server_SUITE_data/test_server_skip_SUITE.erl
index 4037e1cc0e..9607d0d689 100644
--- a/lib/test_server/test/test_server_skip_SUITE.erl
+++ b/lib/test_server/test/test_server_SUITE_data/test_server_skip_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/test/test_server_line_SUITE.erl b/lib/test_server/test/test_server_line_SUITE.erl
index 02897f164f..0aba54f6b5 100644
--- a/lib/test_server/test/test_server_line_SUITE.erl
+++ b/lib/test_server/test/test_server_line_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,20 +23,29 @@
-module(test_server_line_SUITE).
-include_lib("test_server/include/test_server.hrl").
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0,suite/0]).
+-export([init_per_suite/1,end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-export([parse_transform/1, lines/1]).
-all(doc) -> ["Test of parse transform for collection line numbers"];
-all(suite) -> [parse_transform,lines].
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {doc,["Test of parse transform for collection line numbers"]}].
+all() -> [parse_transform,lines].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
init_per_testcase(_Case, Config) ->
?line test_server_line:clear(),
Dog = ?t:timetrap(?t:minutes(2)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
?line test_server_line:clear(),
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
diff --git a/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl b/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl
index c3ee1b68cd..8f3477d3ac 100644
--- a/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl
+++ b/lib/test_server/test/test_server_line_SUITE_data/parse_transform_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/test_server/test/test_server_test_lib.erl b/lib/test_server/test/test_server_test_lib.erl
new file mode 100644
index 0000000000..5ca24f3df7
--- /dev/null
+++ b/lib/test_server/test/test_server_test_lib.erl
@@ -0,0 +1,191 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(test_server_test_lib).
+-export([parse_suite/1]).
+-export([init/2, pre_init_per_testcase/3, post_end_per_testcase/4]).
+
+-include("test_server_test_lib.hrl").
+
+%% The CTH hooks all tests
+init(_Id, _Opts) ->
+ [].
+
+pre_init_per_testcase(_TC,Config,State) ->
+ case os:type() of
+ {win32, _} ->
+ %% Extend timeout for windows as starting node
+ %% can take a long time there
+ test_server:timetrap( 120000 * test_server:timetrap_scale_factor());
+ _ ->
+ ok
+ end,
+ {start_slave(Config, 50),State}.
+
+start_slave(Config,_Level) ->
+ [_,Host] = string:tokens(atom_to_list(node()), "@"),
+
+ ct:log("Trying to start ~s~n",
+ ["test_server_tester@"++Host]),
+ case slave:start(Host, test_server_tester, []) of
+ {error,Reason} ->
+ test_server:fail(Reason);
+ {ok,Node} ->
+ ct:log("Node ~p started~n", [Node]),
+ IsCover = test_server:is_cover(),
+ if IsCover ->
+ cover:start(Node);
+ true->
+ ok
+ end,
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+
+ %% PrivDir as well as directory of Test Server suites
+ %% have to be in code path on Test Server node.
+ [_ | Parts] = lists:reverse(filename:split(DataDir)),
+ TSDir = filename:join(lists:reverse(Parts)),
+ AddPathDirs = case proplists:get_value(path_dirs, Config) of
+ undefined -> [];
+ Ds -> Ds
+ end,
+ PathDirs = [PrivDir,TSDir | AddPathDirs],
+ [true = rpc:call(Node, code, add_patha, [D]) || D <- PathDirs],
+ io:format("Dirs added to code path (on ~w):~n",
+ [Node]),
+ [io:format("~s~n", [D]) || D <- PathDirs],
+
+ true = rpc:call(Node, os, putenv,
+ ["TEST_SERVER_FRAMEWORK", "undefined"]),
+
+ ok = rpc:call(Node, file, set_cwd, [PrivDir]),
+ [{node,Node} | Config]
+ end.
+
+post_end_per_testcase(_TC, Config, Return, State) ->
+ Node = proplists:get_value(node, Config),
+ cover:stop(Node),
+ slave:stop(Node),
+
+ {Return, State}.
+
+%% Parse an .suite log file
+parse_suite(FileName) ->
+
+ case file:open(FileName, [read, raw, read_ahead]) of
+ {ok, Fd} ->
+ Data = parse_suite(Fd, #suite{ }),
+ file:close(Fd),
+ {ok, Data};
+ _ ->
+ error
+ end.
+
+fline(Fd) ->
+ case prim_file:read_line(Fd) of
+ eof -> eof;
+ {ok, Line} -> Line
+ end.
+
+parse_suite(Fd, S) ->
+ _Started = fline(Fd),
+ _Starting = fline(Fd),
+ "=cases" ++ NCases = fline(Fd),
+ "=user" ++ _User = fline(Fd),
+ "=host" ++ Host = fline(Fd),
+ "=hosts" ++ _Hosts = fline(Fd),
+ "=emulator_vsn" ++ Evsn = fline(Fd),
+ "=emulator" ++ Emu = fline(Fd),
+ "=otp_release" ++ OtpRel = fline(Fd),
+ "=started" ++ Start = fline(Fd),
+ NewS = parse_cases(Fd, S#suite{
+ n_cases_expected = list_to_int(clean(NCases)),
+ host = list_to_binary(clean(Host)),
+ emulator_vsn = list_to_binary(clean(Evsn)),
+ emulator = list_to_binary(clean(Emu)),
+ otp_release = list_to_binary(clean(OtpRel)),
+ started = list_to_binary(clean(Start))
+ }),
+ "=failed" ++ Failed = fline(Fd),
+ "=successful" ++ Succ = fline(Fd),
+ "=user_skipped" ++ UsrSkip = fline(Fd),
+ "=auto_skipped" ++ AutSkip = fline(Fd),
+ NewS#suite{ n_cases_failed = list_to_int(clean(Failed)),
+ n_cases_succ = list_to_int(clean(Succ)),
+ n_cases_user_skip = list_to_int(clean(UsrSkip)),
+ n_cases_auto_skip = list_to_int(clean(AutSkip)) }.
+
+
+parse_cases(Fd, #suite{ n_cases = N,
+ cases = Cases } = S) ->
+ case parse_case(Fd) of
+ finished -> S#suite{ log_ok = true };
+ {eof, Tc} ->
+ S#suite{ n_cases = N + 1,
+ cases = [Tc#tc{ result = crashed }|Cases]};
+ {ok, Case} ->
+ parse_cases(Fd, S#suite{ n_cases = N + 1,
+ cases = [Case|Cases]})
+ end.
+
+parse_case(Fd) -> parse_case(Fd, #tc{}).
+parse_case(Fd, Tc) -> parse_case(fline(Fd), Fd, Tc).
+
+parse_case(eof, _, Tc) -> {eof, Tc};
+parse_case("=case" ++ Case, Fd, Tc) ->
+ Name = list_to_binary(clean(Case)),
+ parse_case(fline(Fd), Fd, Tc#tc{ name = Name });
+parse_case("=logfile" ++ File, Fd, Tc) ->
+ Log = list_to_binary(clean(File)),
+ parse_case(fline(Fd), Fd, Tc#tc{ logfile = Log });
+parse_case("=elapsed" ++ Elapsed, Fd, Tc) ->
+ {ok, [Time], _} = io_lib:fread("~f", clean(Elapsed)),
+ parse_case(fline(Fd), Fd, Tc#tc{ elapsed = Time });
+parse_case("=result" ++ Result, _, Tc) ->
+ case clean(Result) of
+ "ok" ++ _ ->
+ {ok, Tc#tc{ result = ok } };
+ "failed" ++ _ ->
+ {ok, Tc#tc{ result = failed } };
+ "skipped" ++ _ ->
+ {ok, Tc#tc{ result = skip } }
+ end;
+parse_case("=finished" ++ _ , _Fd, #tc{ name = undefined }) ->
+ finished;
+parse_case(_, Fd, Tc) ->
+ parse_case(fline(Fd), Fd, Tc).
+
+skip([]) -> [];
+skip([$ |Ts]) -> skip(Ts);
+skip(Ts) -> Ts.
+
+%rmnl(L) -> L.
+rmnl([]) -> [];
+rmnl([$\n | Ts]) -> rmnl(Ts);
+rmnl([T|Ts]) -> [T | rmnl(Ts)].
+
+clean(L) ->
+ rmnl(skip(L)).
+
+list_to_int(L) ->
+ try
+ list_to_integer(L)
+ catch
+ _:_ ->
+ 0
+ end.
diff --git a/lib/test_server/test/test_server_test_lib.hrl b/lib/test_server/test/test_server_test_lib.hrl
new file mode 100644
index 0000000000..27b7be9618
--- /dev/null
+++ b/lib/test_server/test/test_server_test_lib.hrl
@@ -0,0 +1,23 @@
+-record(tc, {
+ name,
+ result,
+ elapsed,
+ logfile
+ }).
+
+-record(suite, {
+ application,
+ n_cases = 0,
+ n_cases_failed = 0,
+ n_cases_expected = 0,
+ n_cases_succ,
+ n_cases_user_skip,
+ n_cases_auto_skip,
+ cases = [],
+ host,
+ emulator_vsn,
+ emulator,
+ otp_release,
+ started,
+ log_ok = false
+ }).
diff --git a/lib/test_server/vsn.mk b/lib/test_server/vsn.mk
index 3c6efeffde..b7c0987845 100644
--- a/lib/test_server/vsn.mk
+++ b/lib/test_server/vsn.mk
@@ -1,2 +1,2 @@
-TEST_SERVER_VSN = 3.3.7
+TEST_SERVER_VSN = 3.4.3
diff --git a/lib/toolbar/doc/src/toolbar.xml b/lib/toolbar/doc/src/toolbar.xml
index 4e9798e5ae..ad379438fe 100644
--- a/lib/toolbar/doc/src/toolbar.xml
+++ b/lib/toolbar/doc/src/toolbar.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in
index e6b76e2238..65a7f5f424 100644
--- a/lib/tools/c_src/Makefile.in
+++ b/lib/tools/c_src/Makefile.in
@@ -128,12 +128,13 @@ EMEM_ERTS_LIB=erts_r$(TYPEMARKER)
endif
+EMEM_ETHR_LIBS=$(subst -l$(ETHR_LIB_NAME),-l$(ETHR_LIB_NAME)$(TYPEMARKER),$(subst -lerts_internal_r,-lerts_internal_r$(TYPEMARKER),$(ETHR_LIBS)))
+
EMEM_LIBS = $(LIBS) \
-L$(ERL_TOP)/erts/lib/$(TARGET) \
-L$(ERL_TOP)/erts/lib/internal/$(TARGET) \
-l$(EMEM_ERTS_LIB) \
- -l$(ETHR_LIB_NAME)$(TYPEMARKER) \
- $(ETHR_X_LIBS)
+ $(EMEM_ETHR_LIBS)
EMEM_OBJS = $(addprefix $(EMEM_OBJ_DIR)/,$(notdir $(EMEM_SRCS:.c=.o)))
diff --git a/lib/tools/c_src/erl_memory.c b/lib/tools/c_src/erl_memory.c
index a0e139f059..872d55e789 100644
--- a/lib/tools/c_src/erl_memory.c
+++ b/lib/tools/c_src/erl_memory.c
@@ -1,22 +1,22 @@
-/* ``The contents of this file are subject to the Erlang Public License,
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
* compliance with the License. You should have received a copy of the
* Erlang Public License along with this software. If not, it can be
- * retrieved via the world wide web at http://www.erlang.org/.
- *
+ * retrieved online at http://www.erlang.org/.
+ *
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
- *
- * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
- * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
- * AB. All Rights Reserved.''
- *
- * $Id$
+ *
+ * %CopyrightEnd%
*/
-
/*
* Description:
*
@@ -281,17 +281,13 @@ mutex_destroy(ethr_mutex *mtx)
static INLINE void
mutex_lock(ethr_mutex *mtx)
{
- int res = ethr_mutex_lock(mtx);
- if (res)
- error_msg(res, "Mutex lock");
+ ethr_mutex_lock(mtx);
}
static INLINE void
mutex_unlock(ethr_mutex *mtx)
{
- int res = ethr_mutex_unlock(mtx);
- if (res)
- error_msg(res, "Mutex unlock");
+ ethr_mutex_unlock(mtx);
}
static INLINE void
@@ -314,16 +310,14 @@ static INLINE void
cond_wait(ethr_cond *cnd, ethr_mutex *mtx)
{
int res = ethr_cond_wait(cnd, mtx);
- if (res)
+ if (res != 0 && res != EINTR)
error_msg(res, "Cond wait");
}
static INLINE void
cond_signal(ethr_cond *cnd)
{
- int res = ethr_cond_signal(cnd);
- if (res)
- error_msg(res, "Cond signal");
+ ethr_cond_signal(cnd);
}
@@ -2774,7 +2768,7 @@ main(int argc, char *argv[])
exit(1);
}
- if (ethr_init(NULL) != 0) {
+ if (ethr_init(NULL) != 0 || ethr_late_init(NULL) != 0) {
fprintf(stderr, "emem: failed to initialize thread package\n");
exit(1);
}
diff --git a/lib/tools/doc/src/cover.xml b/lib/tools/doc/src/cover.xml
index 323bd0dda8..683acc025d 100644
--- a/lib/tools/doc/src/cover.xml
+++ b/lib/tools/doc/src/cover.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2001</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
@@ -270,6 +270,8 @@
defaults to <c>function</c>.</p>
<p>If <c>Module</c> is not Cover compiled, the function returns
<c>{error,{not_cover_compiled,Module}}</c>.</p>
+ <p>HINT: It is possible to issue multiple analyse_to_file commands at
+ the same time. </p>
</desc>
</func>
<func>
@@ -307,6 +309,33 @@
<c>.beam</c> file, or in <c>../src</c> relative to that
directory. If no source code is found,
<c>,{error,no_source_code_found}</c> is returned.</p>
+ <p>HINT: It is possible to issue multiple analyse_to_file commands at
+ the same time. </p>
+ </desc>
+ </func>
+ <func>
+ <name>async_analyse_to_file(Module) -> </name>
+ <name>async_analyse_to_file(Module,Options) -> </name>
+ <name>async_analyse_to_file(Module, OutFile) -> </name>
+ <name>async_analyse_to_file(Module, OutFile, Options) -> pid()</name>
+ <fsummary>Asynchronous call to analyse_to_file.</fsummary>
+ <type>
+ <v>Module = atom()</v>
+ <v>OutFile = string()</v>
+ <v>Options = [Option]</v>
+ <v>Option = html</v>
+ <v>Error = {not_cover_compiled,Module} | {file,File,Reason} | no_source_code_found | not_main_node</v>
+ <v>&nbsp;File = string()</v>
+ <v>&nbsp;Reason = term()</v>
+ </type>
+ <desc>
+ <p>This function works exactly the same way as
+ <seealso marker="#analyse_to_file-1">analyse_to_file</seealso> except
+ that it is asynchronous instead of synchronous. The spawned process
+ will link with the caller when created. If an <c>Error</c> occurs
+ while doing the cover analysis the process will crash with the same
+ error reason as <seealso marker="#analyse_to_file-1">analyse_to_file</seealso>
+ would return.</p>
</desc>
</func>
<func>
diff --git a/lib/tools/doc/src/cover_chapter.xml b/lib/tools/doc/src/cover_chapter.xml
index b4f7919183..5083b01f1d 100644
--- a/lib/tools/doc/src/cover_chapter.xml
+++ b/lib/tools/doc/src/cover_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2001</year><year>2009</year>
+ <year>2001</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -403,6 +403,13 @@ ok
database contains information about each executable line in each
Cover compiled module, performance decreases proportionally to
the size and number of the Cover compiled modules.</p>
+ <p>To improve performance when analysing cover results it is possible
+ to do multiple calls to <seealso marker="cover#analyse-1">analyse</seealso>
+ and <seealso marker="cover#analyse_to_file-1">analyse_to_file</seealso>
+ at once. You can also use the
+ <seealso marker="cover#async_analyse_to_file-1">async_analyse_to_file</seealso>
+ convenience function.
+ </p>
</section>
<section>
diff --git a/lib/tools/doc/src/cprof.xml b/lib/tools/doc/src/cprof.xml
index 421ed7875a..2dc419d29c 100644
--- a/lib/tools/doc/src/cprof.xml
+++ b/lib/tools/doc/src/cprof.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2002</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml
index ae1033f2d0..6d68c90768 100644
--- a/lib/tools/doc/src/eprof.xml
+++ b/lib/tools/doc/src/eprof.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>eprof</title>
@@ -35,8 +35,7 @@
used. The profiling is done using the Erlang trace BIFs. Tracing of
local function calls for a specified set of processes is enabled when
profiling is begun, and disabled when profiling is stopped.</p>
- <p>When using Eprof, expect a significant slowdown in program execution,
- in most cases at least 100 percent.</p>
+ <p>When using Eprof expect a slowdown in program execution.</p>
</description>
<funcs>
<func>
@@ -47,15 +46,19 @@
<v>Reason = {already_started,Pid}</v>
</type>
<desc>
- <p>Starts the Eprof server which owns the Eprof internal database.</p>
+ <p>Starts the Eprof server which holds the internal state of the collected data.</p>
</desc>
</func>
<func>
- <name>start_profiling(Rootset) -> profiling | error</name>
- <name>profile(Rootset) -> profiling | error</name>
+ <name>start_profiling(Rootset) -> profiling | {error, Reason}</name>
+ <name>start_profiling(Rootset,Pattern) -> profiling | {error, Reason}</name>
<fsummary>Start profiling.</fsummary>
<type>
<v>Rootset = [atom() | pid()]</v>
+ <v>Pattern = {Module, Function, Arity}</v>
+ <v>Module = Function = atom()</v>
+ <v>Arity = integer()</v>
+ <v>Reason = term()</v>
</type>
<desc>
<p>Starts profiling for the processes in <c>Rootset</c> (and any new
@@ -64,6 +67,9 @@
<p><c>Rootset</c> is a list of pids and registered names.</p>
<p>The function returns <c>profiling</c> if tracing could be enabled
for all processes in <c>Rootset</c>, or <c>error</c> otherwise.</p>
+ <p>A pattern can be selected to narrow the profiling. For instance ca a specific
+ module be selected and only the code processes executes in that module will be
+ profiled.</p>
</desc>
</func>
<func>
@@ -75,14 +81,20 @@
</desc>
</func>
<func>
- <name>profile(Rootset,Fun) -> {ok,Value} | {error,Reason} | error</name>
- <name>profile(Rootset,Module,Function,Args) -> {ok,Value} | {error,Reason} | error</name>
+ <name>profile(Fun) -> profiling | {error, Reason}</name>
+ <name>profile(Rootset) -> profiling | {error, Reason}</name>
+ <name>profile(Rootset,Fun) -> {ok, Value} | {error,Reason}</name>
+ <name>profile(Rootset,Fun,Pattern) -> {ok, Value} | {error, Reason}</name>
+ <name>profile(Rootset,Module,Function,Args) -> {ok, Value} | {error, Reason}</name>
+ <name>profile(Rootset,Module,Function,Args,Pattern) -> {ok, Value} | {error, Reason}</name>
<fsummary>Start profiling.</fsummary>
<type>
<v>Rootset = [atom() | pid()]</v>
<v>Fun = fun() -> term()</v>
+ <v>Pattern = {Module, Function, Arity}</v>
<v>Module = Function = atom()</v>
<v>Args = [term()]</v>
+ <v>Arity = integer()</v>
<v>Value = Reason = term()</v>
</type>
<desc>
@@ -96,7 +108,7 @@
<c>Rootset</c>, the function returns <c>{ok,Value}</c> when
<c>Fun()</c>/<c>apply</c> returns with the value <c>Value</c>, or
<c>{error,Reason}</c> if <c>Fun()</c>/<c>apply</c> fails with
- exit reason <c>Reason</c>. Otherwise it returns <c>error</c>
+ exit reason <c>Reason</c>. Otherwise it returns <c>{error, Reason}</c>
immediately.</p>
<p>The programmer must ensure that the function given as argument
is truly synchronous and that no work continues after
@@ -104,7 +116,15 @@
</desc>
</func>
<func>
- <name>analyse()</name>
+ <name>analyze() -> ok</name>
+ <name>analyze(Type) -> ok</name>
+ <name>analyze(Type,Options) -> ok</name>
+ <type>
+ <v>Type = procs | total</v>
+ <v>Options = [{filter, Filter} | {sort, Sort}</v>
+ <v>Filter = [{calls, integer()} | {time, float()}]</v>
+ <v>Sort = time | calls | mfa</v>
+ </type>
<fsummary>Display profiling results per process.</fsummary>
<desc>
<p>Call this function when profiling has been stopped to display
@@ -113,17 +133,10 @@
<item>how much time has been used by each process, and</item>
<item>in which function calls this time has been spent.</item>
</list>
- <p>Time is shown as percentage of total time, not as absolute time.</p>
- </desc>
- </func>
- <func>
- <name>total_analyse()</name>
- <fsummary>Display profiling results per function call.</fsummary>
- <desc>
- <p>Call this function when profiling has been stopped to display
+ <p>Call <c>analyze</c> with <c>total</c> option when profiling has been stopped to display
the results per function call, that is in which function calls
the time has been spent.</p>
- <p>Time is shown as percentage of total time, not as absolute time.</p>
+ <p>Time is shown as percentage of total time and as absolute time.</p>
</desc>
</func>
<func>
diff --git a/lib/tools/doc/src/erlang_mode.xml b/lib/tools/doc/src/erlang_mode.xml
index 912c442153..794224d601 100644
--- a/lib/tools/doc/src/erlang_mode.xml
+++ b/lib/tools/doc/src/erlang_mode.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -173,7 +173,7 @@
sum(L) -> sum(L, 0).
sum([H|T], Sum) -> sum(T, Sum + H);
- sum([], Sum) -> Sum."</code>
+ sum([], Sum) -> Sum.</code>
</item>
</list>
</section>
diff --git a/lib/tools/doc/src/erlang_mode_chapter.xml b/lib/tools/doc/src/erlang_mode_chapter.xml
index b22c6b1809..4ffa224ea5 100644
--- a/lib/tools/doc/src/erlang_mode_chapter.xml
+++ b/lib/tools/doc/src/erlang_mode_chapter.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -45,7 +45,7 @@
<section>
<title>Elisp</title>
- <p>There are two Elsip modules include in this tool package
+ <p>There are two Elisp modules included in this tool package
for Emacs. There is erlang.el that defines the actual erlang mode
and there is erlang-start.el that makes some nice initializations.</p>
</section>
diff --git a/lib/tools/doc/src/make.xml b/lib/tools/doc/src/make.xml
index f13514d99f..1c8df67abf 100644
--- a/lib/tools/doc/src/make.xml
+++ b/lib/tools/doc/src/make.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1996</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index e7d1ae150c..118800e44a 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -30,6 +30,161 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 2.6.6.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Declare indentation options as "safe" in erlang-mode for
+ Emacs</p>
+ <p>
+ Emacs has a facility for setting options on a per-file
+ basis based on comments in the source file. By default,
+ all options are considered "unsafe", and the user is
+ queried before the variable is set. This patch declares
+ the variables erlang-indent-level, erlang-indent-guard
+ and erlang-argument-indent to be safe, if the value
+ specified in the source file is valid.</p>
+ <p>
+ Such declarations usually look like this:</p>
+ <p>
+ %% -*- erlang-indent-level: 2 -*-</p>
+ <p>
+ and appear on the first line of the file. (thanks to
+ Magnus Henoch)</p>
+ <p>
+ Own Id: OTP-9122</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Cover has been improved to take less memory and allow
+ parallel analysis of cover data. Data collection from
+ nodes is now done in parallel and it is now possible to
+ issue multiple analyse and analyse_to_file requests at
+ the same time. A new function call async_analyse_to_file
+ has also been introduced, see the documentation for more
+ details.</p>
+ <p>
+ Own Id: OTP-9043 Aux Id: seq11771 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.6.6.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>eprof: API sort mismatch has now been fixed. </p>
+ <p>
+ Own Id: OTP-8853</p>
+ </item>
+ <item>
+ <p>
+ eprof: fix division by zero in statistics</p>
+ <p>
+ Own Id: OTP-8963</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.6.6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>cover</c> will now show ampersand characters in the
+ source code correctly. (Thanks to Tom Moertel.)</p>
+ <p>
+ Own Id: OTP-8776</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Tools 2.6.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>A race condition affecting Cover has been removed.</p>
+ <p>
+ Own Id: OTP-8469</p>
+ </item>
+ <item>
+ <p>
+ Emacs improvements:</p>
+ <p>
+ Fixed emacs-mode installation problems.</p>
+ <p>
+ Fixed a couple of -spec and -type indentation and
+ font-lock problems.</p>
+ <p>
+ Fixed error messages on emacs-21.</p>
+ <p>
+ Magnus Henoch fixed several issues.</p>
+ <p>
+ Ralf Doering, Klas Johansson and Chris Bernard
+ contributed various emacs-eunit improvements.</p>
+ <p>
+ Klas Johansson and Dave Peticolas added emacs-flymake
+ support.</p>
+ <p>
+ Own Id: OTP-8530</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Xref has been updated to use the <c>re</c> module
+ instead of the deprecated <c>regexp</c> module.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8472</p>
+ </item>
+ <item>
+ <p>When given the option <c>{builtins,true}</c> Xref now
+ adds calls to operators.</p>
+ <p>
+ Own Id: OTP-8647</p>
+ </item>
+ <item>
+ <p><c>eprof</c> has been reimplemented with support in
+ the Erlang virtual machine and is now both faster (i.e.
+ slows down the code being measured less) and scales much
+ better. In measurements we saw speed-ups compared to the
+ old eprof ranging from 6 times (for sequential code that
+ only uses one scheduler/core) up to 84 times (for
+ parallel code that uses 8 cores).</p>
+ <p>Note: The API for the <c>eprof</c> has been cleaned up
+ and extended. See the documentation.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-8706</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 2.6.5.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/doc/src/part_notes_history.xml b/lib/tools/doc/src/part_notes_history.xml
index b40b530c02..da637f380a 100644
--- a/lib/tools/doc/src/part_notes_history.xml
+++ b/lib/tools/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/doc/src/tags.xml b/lib/tools/doc/src/tags.xml
index 5e1da25acf..54b5a4914c 100644
--- a/lib/tools/doc/src/tags.xml
+++ b/lib/tools/doc/src/tags.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1998</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile
index 0028df247c..8533488463 100644
--- a/lib/tools/emacs/Makefile
+++ b/lib/tools/emacs/Makefile
@@ -42,6 +42,7 @@ EMACS_FILES= \
erlang_appwiz \
erlang-start \
erlang-eunit \
+ erlang-flymake \
erlang
README_FILES= README
diff --git a/lib/tools/emacs/README b/lib/tools/emacs/README
index ca068d04c4..cc107dcd41 100644
--- a/lib/tools/emacs/README
+++ b/lib/tools/emacs/README
@@ -42,7 +42,14 @@ Files\erl-<Ver>:
(setq erlang-root-dir "C:/Program Files/erl<Ver>")
(setq exec-path (cons "C:/Program Files/erl<Ver>/bin" exec-path))
(require 'erlang-start)
-
+Miscellaneous addons
+--------------------
+
+In order to check erlang source code on the fly, add the following
+line to your .emacs file (after erlang-start, see above). See
+erlang-flymake.el for more information on how to customize the syntax
+check.
+ (require 'erlang-flymake)
diff --git a/lib/tools/emacs/erlang-eunit.el b/lib/tools/emacs/erlang-eunit.el
index b2598f93e6..f2c0db67dd 100644
--- a/lib/tools/emacs/erlang-eunit.el
+++ b/lib/tools/emacs/erlang-eunit.el
@@ -23,8 +23,22 @@
(eval-when-compile
(require 'cl))
-(defvar erlang-eunit-separate-src-and-test-directories t
- "*Whether or not to keep source and EUnit test files in separate directories")
+(defvar erlang-eunit-src-candidate-dirs '("../src" ".")
+ "*Name of directories which to search for source files matching
+an EUnit test file. The first directory in the list will be used,
+if there is no match.")
+
+(defvar erlang-eunit-test-candidate-dirs '("../test" ".")
+ "*Name of directories which to search for EUnit test files matching
+a source file. The first directory in the list will be used,
+if there is no match.")
+
+(defvar erlang-eunit-autosave nil
+ "*Set to non-nil to automtically save unsaved buffers before running tests.
+This is useful, reducing the save-compile-load-test cycle to one keychord.")
+
+(defvar erlang-eunit-recent-info '((mode . nil) (module . nil) (test . nil) (cover . nil))
+ "Info about the most recent running of an EUnit test representation.")
;;;
;;; Switch between src/EUnit test buffers
@@ -44,7 +58,6 @@ buffer and vice versa"
"Open the EUnit test file which corresponds to a src file"
(find-file-other-window (erlang-eunit-test-filename src-file-path)))
-
;;;
;;; Open the src file which corresponds to the an EUnit test file
;;;
@@ -55,37 +68,55 @@ buffer and vice versa"
;;; Return the name and path of the EUnit test file
;;, (input may be either the source filename itself or the EUnit test filename)
(defun erlang-eunit-test-filename (file-path)
- (erlang-eunit-rewrite-filename file-path "test" "_tests"))
+ (if (erlang-eunit-test-file-p file-path)
+ file-path
+ (erlang-eunit-rewrite-filename file-path erlang-eunit-test-candidate-dirs)))
;;; Return the name and path of the source file
;;, (input may be either the source filename itself or the EUnit test filename)
(defun erlang-eunit-src-filename (file-path)
- (erlang-eunit-rewrite-filename file-path "src" ""))
+ (if (erlang-eunit-src-file-p file-path)
+ file-path
+ (erlang-eunit-rewrite-filename file-path erlang-eunit-src-candidate-dirs)))
;;; Rewrite a filename from the src or test filename to the other
-(defun erlang-eunit-rewrite-filename (orig-file-path dest-dirname dest-suffix)
- (let* ((root-dir-name (erlang-eunit-file-root-dir-name orig-file-path))
- (src-module-name (erlang-eunit-source-module-name orig-file-path))
- (dest-base-name (concat src-module-name dest-suffix ".erl"))
- (dest-dir-name-1 (file-name-directory orig-file-path))
- (dest-dir-name-2 (filename-join root-dir-name dest-dirname))
- (dest-file-name-1 (filename-join dest-dir-name-1 dest-base-name))
- (dest-file-name-2 (filename-join dest-dir-name-2 dest-base-name)))
- ;; This function tries to be a bit intelligent:
- ;; * if there already is a test (or source) file in the same
- ;; directory as a source (or test) file, it'll be picked
- ;; * if there already is a test (or source) file in a separate
- ;; test (or src) directory, it'll be picked
- ;; * otherwise it'll resort to whatever alternative (same or
- ;; separate directories) that the user has chosen
- (cond ((file-readable-p dest-file-name-1)
- dest-file-name-1)
- ((file-readable-p dest-file-name-2)
- dest-file-name-2)
- (erlang-eunit-separate-src-and-test-directories
- dest-file-name-2)
- (t
- dest-file-name-1))))
+(defun erlang-eunit-rewrite-filename (orig-file-path candidate-dirs)
+ (or (erlang-eunit-locate-buddy orig-file-path candidate-dirs)
+ (erlang-eunit-buddy-file-path orig-file-path (car candidate-dirs))))
+
+;;; Search for a file's buddy file (a source file's EUnit test file,
+;;; or an EUnit test file's source file) in a list of candidate
+;;; directories.
+(defun erlang-eunit-locate-buddy (orig-file-path candidate-dirs)
+ (when candidate-dirs
+ (let ((buddy-file-path (erlang-eunit-buddy-file-path
+ orig-file-path
+ (car candidate-dirs))))
+ (if (file-readable-p buddy-file-path)
+ buddy-file-path
+ (erlang-eunit-locate-buddy orig-file-path (cdr candidate-dirs))))))
+
+(defun erlang-eunit-buddy-file-path (orig-file-path buddy-dir-name)
+ (let* ((orig-dir-name (file-name-directory orig-file-path))
+ (buddy-dir-name (file-truename
+ (filename-join orig-dir-name buddy-dir-name)))
+ (buddy-base-name (erlang-eunit-buddy-basename orig-file-path)))
+ (filename-join buddy-dir-name buddy-base-name)))
+
+;;; Return the basename of the buddy file:
+;;; /tmp/foo/src/x.erl --> x_tests.erl
+;;; /tmp/foo/test/x_tests.erl --> x.erl
+(defun erlang-eunit-buddy-basename (file-path)
+ (let ((src-module-name (erlang-eunit-source-module-name file-path)))
+ (cond
+ ((erlang-eunit-src-file-p file-path)
+ (concat src-module-name "_tests.erl"))
+ ((erlang-eunit-test-file-p file-path)
+ (concat src-module-name ".erl")))))
+
+;;; Checks whether a file is a source file or not
+(defun erlang-eunit-src-file-p (file-path)
+ (not (erlang-eunit-test-file-p file-path)))
;;; Checks whether a file is a EUnit test file or not
(defun erlang-eunit-test-file-p (file-path)
@@ -96,23 +127,17 @@ buffer and vice versa"
;;; /tmp/foo/test/x_tests.erl --> x
(defun erlang-eunit-source-module-name (file-path)
(interactive)
- (let* ((file-name (file-name-nondirectory file-path))
- (base-name (file-name-sans-extension file-name)))
- (if (string-match "^\\(.+\\)_tests$" base-name)
- (substring base-name (match-beginning 1) (match-end 1))
- base-name)))
-
-;;; Return the directory name which is common to both src and test
-;;; /tmp/foo/src/x.erl --> /tmp/foo
-;;; /tmp/foo/test/x_tests.erl --> /tmp/foo
-(defun erlang-eunit-file-root-dir-name (file-path)
- (erlang-eunit-dir-parent-dirname (file-name-directory file-path)))
-
-;;; Return the parent directory name of a directory
-;;; /tmp/foo/ --> /tmp
-;;; /tmp/foo --> /tmp
-(defun erlang-eunit-dir-parent-dirname (dir-name)
- (file-name-directory (directory-file-name dir-name)))
+ (let ((module-name (erlang-eunit-module-name file-path)))
+ (if (string-match "^\\(.+\\)_tests$" module-name)
+ (substring module-name (match-beginning 1) (match-end 1))
+ module-name)))
+
+;;; Return the module name of the file
+;;; /tmp/foo/src/x.erl --> x
+;;; /tmp/foo/test/x_tests.erl --> x_tests
+(defun erlang-eunit-module-name (file-path)
+ (interactive)
+ (file-name-sans-extension (file-name-nondirectory file-path)))
;;; Older emacsen don't have string-match-p.
(defun erlang-eunit-string-match-p (regexp string &optional start)
@@ -128,25 +153,158 @@ buffer and vice versa"
(concat dir file)
(concat dir "/" file)))
+;;; Get info about the most recent running of EUnit
+(defun erlang-eunit-recent (key)
+ (cdr (assq key erlang-eunit-recent-info)))
+
+;;; Record info about the most recent running of EUnit
+;;; Known modes are 'module-mode and 'test-mode
+(defun erlang-eunit-record-recent (mode module test)
+ (setcdr (assq 'mode erlang-eunit-recent-info) mode)
+ (setcdr (assq 'module erlang-eunit-recent-info) module)
+ (setcdr (assq 'test erlang-eunit-recent-info) test))
+
+;;; Record whether the most recent running of EUnit included cover
+;;; compilation
+(defun erlang-eunit-record-recent-compile (under-cover)
+ (setcdr (assq 'cover erlang-eunit-recent-info) under-cover))
+
+;;; Determine options for EUnit.
+(defun erlang-eunit-opts ()
+ (if current-prefix-arg ", [verbose]" ""))
+
+;;; Determine current test function
+(defun erlang-eunit-current-test ()
+ (save-excursion
+ (erlang-end-of-function 1)
+ (erlang-beginning-of-function 1)
+ (erlang-name-of-function)))
+
+(defun erlang-eunit-simple-test-p (test-name)
+ (if (erlang-eunit-string-match-p "^\\(.+\\)_test$" test-name) t nil))
+
+(defun erlang-eunit-test-generator-p (test-name)
+ (if (erlang-eunit-string-match-p "^\\(.+\\)_test_$" test-name) t nil))
+
+;;; Run one EUnit test
+(defun erlang-eunit-run-test (module-name test-name)
+ (let ((command
+ (cond ((erlang-eunit-simple-test-p test-name)
+ (format "eunit:test({%s, %s}%s)."
+ module-name test-name (erlang-eunit-opts)))
+ ((erlang-eunit-test-generator-p test-name)
+ (format "eunit:test({generator, %s, %s}%s)."
+ module-name test-name (erlang-eunit-opts)))
+ (t (format "%% WARNING: '%s' is not a test function" test-name)))))
+ (erlang-eunit-record-recent 'test-mode module-name test-name)
+ (erlang-eunit-inferior-erlang-send-command command)))
+
;;; Run EUnit tests for the current module
-(defun erlang-eunit-run-tests ()
- "Run the EUnit test suite for the current module.
+(defun erlang-eunit-run-module-tests (module-name)
+ (let ((command (format "eunit:test(%s%s)." module-name (erlang-eunit-opts))))
+ (erlang-eunit-record-recent 'module-mode module-name nil)
+ (erlang-eunit-inferior-erlang-send-command command)))
+
+(defun erlang-eunit-compile-and-run-recent ()
+ "Compile the source and test files and repeat the most recent EUnit test run.
-With prefix arg, runs tests with the verbose flag set."
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
(interactive)
- (let* ((module-name (erlang-add-quotes-if-needed
- (erlang-eunit-source-module-name buffer-file-name)))
- (opts (if current-prefix-arg ", [verbose]" ""))
- (command (format "eunit:test(%s%s)." module-name opts)))
- (erlang-eunit-inferior-erlang-send-command command)))
+ (case (erlang-eunit-recent 'mode)
+ ('test-mode
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-test (list (erlang-eunit-recent 'module)
+ (erlang-eunit-recent 'test))))
+ ('module-mode
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-module-tests (list (erlang-eunit-recent 'module))
+ (erlang-eunit-recent 'cover)))
+ (t (error "EUnit has not yet been run. Please run a test first."))))
+
+(defun erlang-eunit-cover-compile ()
+ "Cover compile current module."
+ (interactive)
+ (let* ((erlang-compile-extra-opts
+ (append (list 'debug_info) erlang-compile-extra-opts))
+ (module-name
+ (erlang-add-quotes-if-needed
+ (erlang-eunit-module-name buffer-file-name)))
+ (compile-command
+ (format "cover:compile_beam(%s)." module-name)))
+ (erlang-compile)
+ (if (erlang-eunit-last-compilation-successful-p)
+ (erlang-eunit-inferior-erlang-send-command compile-command))))
+
+(defun erlang-eunit-analyze-coverage ()
+ "Analyze the data collected by cover tool for the module in the
+current buffer.
+
+Assumes that the module has been cover compiled prior to this
+call. This function will do two things: print the number of
+covered and uncovered functions in the erlang shell and display a
+new buffer called *<module name> coverage* which shows the source
+code along with the coverage analysis results."
+ (interactive)
+ (let* ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-module-name buffer-file-name)))
+ (tmp-filename (make-temp-file "cover"))
+ (analyze-command (format "cover:analyze_to_file(%s, \"%s\"). "
+ module-name tmp-filename))
+ (buf-name (format "*%s coverage*" module-name)))
+ (erlang-eunit-inferior-erlang-send-command analyze-command)
+ ;; The purpose of the following snippet is to get the result of the
+ ;; analysis from a file into a new buffer (or an old, if one with
+ ;; the specified name already exists). Also we want the erlang-mode
+ ;; *and* view-mode to be enabled.
+ (save-excursion
+ (let ((buf (get-buffer-create (format "*%s coverage*" module-name))))
+ (set-buffer buf)
+ (setq buffer-read-only nil)
+ (insert-file-contents tmp-filename nil nil nil t)
+ (if (= (buffer-size) 0)
+ (kill-buffer buf)
+ ;; FIXME: this would be a good place to enable (emacs-mode)
+ ;; to get some nice syntax highlighting in the
+ ;; coverage report, but it doesn't play well with
+ ;; flymake. Leave it off for now.
+ (view-buffer buf))))
+ (delete-file tmp-filename)))
+
+(defun erlang-eunit-compile-and-run-current-test ()
+ "Compile the source and test files and run the current EUnit test.
-;;; Compile source and EUnit test file and finally run EUnit tests for
-;;; the current module
-(defun erlang-eunit-compile-and-run-tests ()
- "Compile the source and test files and run the EUnit test suite.
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (interactive)
+ (let ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-module-name buffer-file-name)))
+ (test-name (erlang-eunit-current-test)))
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-test (list module-name test-name))))
+
+(defun erlang-eunit-compile-and-run-module-tests ()
+ "Compile the source and test files and run all EUnit tests in the module.
With prefix arg, compiles for debug and runs tests with the verbose flag set."
(interactive)
+ (let ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-source-module-name buffer-file-name))))
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-module-tests (list module-name))))
+
+;;; Compile source and EUnit test file and finally run EUnit tests for
+;;; the current module
+(defun erlang-eunit-compile-and-test (test-fun test-args &optional under-cover)
+ "Compile the source and test files and run the EUnit test suite.
+
+If under-cover is set to t, the module under test is compile for
+code coverage analysis. If under-cover is left out or not set,
+coverage analysis is disabled. The result of the code coverage
+is both printed to the erlang shell (the number of covered vs
+uncovered functions in a module) and written to a buffer called
+*<module> coverage* (which shows the source code for the module
+and the number of times each line is covered).
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (erlang-eunit-record-recent-compile under-cover)
(let ((src-filename (erlang-eunit-src-filename buffer-file-name))
(test-filename (erlang-eunit-test-filename buffer-file-name)))
@@ -154,7 +312,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;; below, is to ask the question about saving buffers only once,
;; instead of possibly several: one for each file to compile,
;; for instance for both x.erl and x_tests.erl.
- (save-some-buffers)
+ (save-some-buffers erlang-eunit-autosave)
(flet ((save-some-buffers (&optional any) nil))
;; Compilation of the source file is mandatory (the file must
@@ -162,23 +320,56 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;; test file on the other hand, is optional, since eunit tests may
;; be placed in the source file instead. Any compilation error
;; will prevent the subsequent steps to be run (hence the `and')
- (and (erlang-eunit-compile-file src-filename)
+ (and (erlang-eunit-compile-file src-filename under-cover)
(if (file-readable-p test-filename)
(erlang-eunit-compile-file test-filename)
t)
- (erlang-eunit-run-tests)))))
+ (apply test-fun test-args)
+ (if under-cover
+ (save-excursion
+ (set-buffer (find-file-noselect src-filename))
+ (erlang-eunit-analyze-coverage)))))))
+
+(defun erlang-eunit-compile-and-run-module-tests-under-cover ()
+ "Compile the source and test files and run the EUnit test suite and measure
+code coverage.
+
+With prefix arg, compiles for debug and runs tests with the verbose flag set."
+ (interactive)
+ (let ((module-name (erlang-add-quotes-if-needed
+ (erlang-eunit-source-module-name buffer-file-name))))
+ (erlang-eunit-compile-and-test
+ 'erlang-eunit-run-module-tests (list module-name) t)))
-(defun erlang-eunit-compile-file (file-path)
+(defun erlang-eunit-compile-file (file-path &optional under-cover)
(if (file-readable-p file-path)
(save-excursion
- (set-buffer (find-file-noselect file-path))
- (erlang-compile)
- (erlang-eunit-last-compilation-successful-p))
+ (set-buffer (find-file-noselect file-path))
+ ;; In order to run a code coverage analysis on a
+ ;; module, we have two options:
+ ;;
+ ;; * either compile the module with cover:compile instead of the
+ ;; regular compiler
+ ;;
+ ;; * or first compile the module with the regular compiler (but
+ ;; *with* debug_info) and then compile it for coverage
+ ;; analysis using cover:compile_beam.
+ ;;
+ ;; We could accomplish the first by changing the
+ ;; erlang-compile-erlang-function to cover:compile, but there's
+ ;; a risk that that's used for other purposes. Therefore, a
+ ;; safer alternative (although with more steps) is to add
+ ;; debug_info to the list of compiler options and go for the
+ ;; second alternative.
+ (if under-cover
+ (erlang-eunit-cover-compile)
+ (erlang-compile))
+ (erlang-eunit-last-compilation-successful-p))
(let ((msg (format "Could not read %s" file-path)))
- (erlang-eunit-inferior-erlang-send-command
+ (erlang-eunit-inferior-erlang-send-command
(format "%% WARNING: %s" msg))
(error msg))))
-
+
(defun erlang-eunit-last-compilation-successful-p ()
(save-excursion
(set-buffer inferior-erlang-buffer)
@@ -187,7 +378,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(lambda (re) (let ((continue t)
(result t))
(while continue ; ignore warnings, stop at errors
- (if (re-search-forward re (point-max) t)
+ (if (re-search-forward re (point-max) t)
(if (erlang-eunit-is-compilation-warning)
t
(setq result nil)
@@ -198,7 +389,7 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
(mapcar (lambda (e) (car e)) erlang-error-regexp-alist))))
(defun erlang-eunit-is-compilation-warning ()
- (erlang-eunit-string-match-p
+ (erlang-eunit-string-match-p
"[0-9]+: Warning:"
(buffer-substring (line-beginning-position) (line-end-position))))
@@ -224,22 +415,22 @@ With prefix arg, compiles for debug and runs tests with the verbose flag set."
;;; Key bindings
;;;====================================================================
-(defvar erlang-eunit-toggle-src-and-test-file-other-window-key "\C-c\C-et"
- "*Key to which the `erlang-eunit-toggle-src-and-test-file-other-window'
-function will be bound.")
-(defvar erlang-eunit-compile-and-run-tests-key "\C-c\C-ek"
- "*Key to which the `erlang-eunit-compile-and-run-tests'
-function will be bound.")
+(defconst erlang-eunit-key-bindings
+ '(("\C-c\C-et" erlang-eunit-toggle-src-and-test-file-other-window)
+ ("\C-c\C-ek" erlang-eunit-compile-and-run-module-tests)
+ ("\C-c\C-ej" erlang-eunit-compile-and-run-current-test)
+ ("\C-c\C-el" erlang-eunit-compile-and-run-recent)
+ ("\C-c\C-ec" erlang-eunit-compile-and-run-module-tests-under-cover)
+ ("\C-c\C-ev" erlang-eunit-cover-compile)
+ ("\C-c\C-ea" erlang-eunit-analyze-coverage)))
(defun erlang-eunit-add-key-bindings ()
- (erlang-eunit-ensure-keymap-for-key
- erlang-eunit-toggle-src-and-test-file-other-window-key)
- (local-set-key erlang-eunit-toggle-src-and-test-file-other-window-key
- 'erlang-eunit-toggle-src-and-test-file-other-window)
- (erlang-eunit-ensure-keymap-for-key
- erlang-eunit-compile-and-run-tests-key)
- (local-set-key erlang-eunit-compile-and-run-tests-key
- 'erlang-eunit-compile-and-run-tests))
+ (dolist (binding erlang-eunit-key-bindings)
+ (erlang-eunit-bind-key (car binding) (cadr binding))))
+
+(defun erlang-eunit-bind-key (key function)
+ (erlang-eunit-ensure-keymap-for-key key)
+ (local-set-key key function))
(defun erlang-eunit-ensure-keymap-for-key (key-seq)
(let ((prefix-keys (butlast (append key-seq nil)))
diff --git a/lib/tools/emacs/erlang-flymake.el b/lib/tools/emacs/erlang-flymake.el
new file mode 100644
index 0000000000..bc368e9454
--- /dev/null
+++ b/lib/tools/emacs/erlang-flymake.el
@@ -0,0 +1,102 @@
+;; erlang-flymake.el
+;;
+;; Syntax check erlang source code on the fly (integrates with flymake).
+;;
+;; Start using flymake with erlang by putting the following somewhere
+;; in your .emacs file:
+;;
+;; (require 'erlang-flymake)
+;;
+;; Flymake is rather eager and does its syntax checks frequently by
+;; default and if you are bothered by this, you might want to put the
+;; following in your .emacs as well:
+;;
+;; (erlang-flymake-only-on-save)
+;;
+;; There are a couple of variables which control the compilation options:
+;; * erlang-flymake-get-code-path-dirs-function
+;; * erlang-flymake-get-include-dirs-function
+;; * erlang-flymake-extra-opts
+;;
+;; This code is inspired by http://www.emacswiki.org/emacs/FlymakeErlang.
+
+(require 'flymake)
+(eval-when-compile
+ (require 'cl))
+
+(defvar erlang-flymake-command
+ "erlc"
+ "The command that will be used to perform the syntax check")
+
+(defvar erlang-flymake-get-code-path-dirs-function
+ 'erlang-flymake-get-code-path-dirs
+ "Return a list of ebin directories to add to the code path.")
+
+(defvar erlang-flymake-get-include-dirs-function
+ 'erlang-flymake-get-include-dirs
+ "Return a list of include directories to add to the compiler options.")
+
+(defvar erlang-flymake-extra-opts
+ (list "+warn_obsolete_guard"
+ "+warn_unused_import"
+ "+warn_shadow_vars"
+ "+warn_export_vars"
+ "+strong_validation"
+ "+report")
+ "A list of options that will be passed to the compiler")
+
+(defun erlang-flymake-only-on-save ()
+ "Trigger flymake only when the buffer is saved (disables syntax
+check on newline and when there are no changes)."
+ (interactive)
+ ;; There doesn't seem to be a way of disabling this; set to the
+ ;; largest int available as a workaround (most-positive-fixnum
+ ;; equates to 8.5 years on my machine, so it ought to be enough ;-) )
+ (setq flymake-no-changes-timeout most-positive-fixnum)
+ (setq flymake-start-syntax-check-on-newline nil))
+
+
+(defun erlang-flymake-get-code-path-dirs ()
+ (list (concat (erlang-flymake-get-app-dir) "ebin")))
+
+(defun erlang-flymake-get-include-dirs ()
+ (list (concat (erlang-flymake-get-app-dir) "include")))
+
+(defun erlang-flymake-get-app-dir ()
+ (let ((src-path (file-name-directory (buffer-file-name))))
+ (file-name-directory (directory-file-name src-path))))
+
+(defun erlang-flymake-init ()
+ (let* ((temp-file
+ (flet ((flymake-get-temp-dir () (erlang-flymake-temp-dir)))
+ (flymake-init-create-temp-buffer-copy
+ 'flymake-create-temp-with-folder-structure)))
+ (code-dir-opts
+ (erlang-flymake-flatten
+ (mapcar (lambda (dir) (list "-pa" dir))
+ (funcall erlang-flymake-get-code-path-dirs-function))))
+ (inc-dir-opts
+ (erlang-flymake-flatten
+ (mapcar (lambda (dir) (list "-I" dir))
+ (funcall erlang-flymake-get-include-dirs-function))))
+ (compile-opts
+ (append inc-dir-opts
+ code-dir-opts
+ erlang-flymake-extra-opts)))
+ (list erlang-flymake-command (append compile-opts (list temp-file)))))
+
+(defun erlang-flymake-temp-dir ()
+ ;; Squeeze the user's name in there in order to make sure that files
+ ;; for two users who are working on the same computer (like a linux
+ ;; box) don't collide
+ (format "%s/flymake-%s" temporary-file-directory user-login-name))
+
+(defun erlang-flymake-flatten (list)
+ (apply #'append list))
+
+(add-to-list 'flymake-allowed-file-name-masks
+ '("\\.erl\\'" erlang-flymake-init))
+(add-hook 'erlang-mode-hook 'flymake-mode)
+
+(provide 'erlang-flymake)
+;; erlang-flymake ends here
diff --git a/lib/tools/emacs/erlang-start.el b/lib/tools/emacs/erlang-start.el
index 542e81f24c..bbcea3e46a 100644
--- a/lib/tools/emacs/erlang-start.el
+++ b/lib/tools/emacs/erlang-start.el
@@ -90,6 +90,11 @@
(or (assoc (car b) auto-mode-alist)
(setq auto-mode-alist (cons b auto-mode-alist))))
+;;
+;; Associate files using interpreter "escript" with Erlang mode.
+;;
+
+(add-to-list 'interpreter-mode-alist (cons "escript" 'erlang-mode))
;;
;; Ignore files ending in ".jam", ".vee", and ".beam" when performing
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 0132587d28..e1c0d31371 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -1,7 +1,7 @@
;; erlang.el --- Major modes for editing and running Erlang
;; %CopyrightBegin%
;;
-;; Copyright Ericsson AB 1996-2010. All Rights Reserved.
+;; Copyright Ericsson AB 1996-2011. All Rights Reserved.
;;
;; The contents of this file are subject to the Erlang Public License,
;; Version 1.1, (the "License"); you may not use this file except in
@@ -466,14 +466,17 @@ To activate the workaround, place the following in your `~/.emacs' file:
(defvar erlang-indent-level 4
"*Indentation of Erlang calls/clauses within blocks.")
+(put 'erlang-indent-level 'safe-local-variable 'integerp)
(defvar erlang-indent-guard 2
"*Indentation of Erlang guards.")
+(put 'erlang-indent-guard 'safe-local-variable 'integerp)
(defvar erlang-argument-indent 2
"*Indentation of the first argument in a function call.
When nil, indent to the column after the `(' of the
function.")
+(put 'erlang-argument-indent 'safe-local-variable '(lambda (val) (or (null val) (integerp val))))
(defvar erlang-tab-always-indent t
"*Non-nil means TAB in Erlang mode should always re-indent the current line,
@@ -659,24 +662,30 @@ resulting regexp is surrounded by \\_< and \\_>."
(eval-and-compile
(defconst erlang-guards-regexp (erlang-regexp-opt erlang-guards 'symbols)))
-
(eval-and-compile
(defvar erlang-predefined-types
'("any"
"arity"
+ "boolean"
"byte"
"char"
"cons"
"deep_string"
+ "iolist"
"maybe_improper_list"
+ "module"
"mfa"
"nil"
+ "neg_integer"
"none"
"non_neg_integer"
"nonempty_list"
"nonempty_improper_list"
"nonempty_maybe_improper_list"
+ "no_return"
+ "pos_integer"
"string"
+ "term"
"timeout")
"Erlang type specs types"))
@@ -885,15 +894,54 @@ files written in other languages than Erlang.")
If nil, the inferior shell replaces the window. This is the traditional
behaviour.")
-(defvar erlang-mode-map nil
+(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist)
+ "Non-nil means use `compilation-minor-mode' in Erlang shell.")
+
+(defvar erlang-mode-map
+ (let ((map (make-sparse-keymap)))
+ (unless (boundp 'indent-line-function)
+ (define-key map "\t" 'erlang-indent-command))
+ (define-key map ";" 'erlang-electric-semicolon)
+ (define-key map "," 'erlang-electric-comma)
+ (define-key map "<" 'erlang-electric-lt)
+ (define-key map ">" 'erlang-electric-gt)
+ (define-key map "\C-m" 'erlang-electric-newline)
+ (if (not (boundp 'delete-key-deletes-forward))
+ (define-key map "\177" 'backward-delete-char-untabify)
+ (define-key map [(backspace)] 'backward-delete-char-untabify))
+ ;;(unless (boundp 'fill-paragraph-function)
+ (define-key map "\M-q" 'erlang-fill-paragraph)
+ (unless (boundp 'beginning-of-defun-function)
+ (define-key map "\M-\C-a" 'erlang-beginning-of-function)
+ (define-key map "\M-\C-e" 'erlang-end-of-function)
+ (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
+ (define-key map "\M-\t" 'erlang-complete-tag)
+ (define-key map "\C-c\M-\t" 'tempo-complete-tag)
+ (define-key map "\M-+" 'erlang-find-next-tag)
+ (define-key map "\C-c\M-a" 'erlang-beginning-of-clause)
+ (define-key map "\C-c\M-b" 'tempo-backward-mark)
+ (define-key map "\C-c\M-e" 'erlang-end-of-clause)
+ (define-key map "\C-c\M-f" 'tempo-forward-mark)
+ (define-key map "\C-c\M-h" 'erlang-mark-clause)
+ (define-key map "\C-c\C-c" 'comment-region)
+ (define-key map "\C-c\C-j" 'erlang-generate-new-clause)
+ (define-key map "\C-c\C-k" 'erlang-compile)
+ (define-key map "\C-c\C-l" 'erlang-compile-display)
+ (define-key map "\C-c\C-s" 'erlang-show-syntactic-information)
+ (define-key map "\C-c\C-q" 'erlang-indent-function)
+ (define-key map "\C-c\C-u" 'erlang-uncomment-region)
+ (define-key map "\C-c\C-y" 'erlang-clone-arguments)
+ (define-key map "\C-c\C-a" 'erlang-align-arrows)
+ (define-key map "\C-c\C-z" 'erlang-shell-display)
+ (unless inferior-erlang-use-cmm
+ (define-key map "\C-x`" 'erlang-next-error))
+ map)
"*Keymap used in Erlang mode.")
(defvar erlang-mode-abbrev-table nil
"Abbrev table in use in Erlang-mode buffers.")
(defvar erlang-mode-syntax-table nil
"Syntax table in use in Erlang-mode buffers.")
-(defconst inferior-erlang-use-cmm (boundp 'minor-mode-overriding-map-alist)
- "Non-nil means use `compilation-minor-mode' in Erlang shell.")
(defvar erlang-skel-file "erlang-skels"
@@ -1247,7 +1295,7 @@ Other commands:
(setq major-mode 'erlang-mode)
(setq mode-name "Erlang")
(erlang-syntax-table-init)
- (erlang-keymap-init)
+ (use-local-map erlang-mode-map)
(erlang-electric-init)
(erlang-menu-init)
(erlang-mode-variables)
@@ -1302,53 +1350,6 @@ Other commands:
(set-syntax-table erlang-mode-syntax-table))
-(defun erlang-keymap-init ()
- (if erlang-mode-map
- nil
- (setq erlang-mode-map (make-sparse-keymap))
- (erlang-mode-commands erlang-mode-map))
- (use-local-map erlang-mode-map))
-
-
-(defun erlang-mode-commands (map)
- (unless (boundp 'indent-line-function)
- (define-key map "\t" 'erlang-indent-command))
- (define-key map ";" 'erlang-electric-semicolon)
- (define-key map "," 'erlang-electric-comma)
- (define-key map "<" 'erlang-electric-lt)
- (define-key map ">" 'erlang-electric-gt)
- (define-key map "\C-m" 'erlang-electric-newline)
- (if (not (boundp 'delete-key-deletes-forward))
- (define-key map "\177" 'backward-delete-char-untabify)
- (define-key map [(backspace)] 'backward-delete-char-untabify))
- ;;(unless (boundp 'fill-paragraph-function)
- (define-key map "\M-q" 'erlang-fill-paragraph)
- (unless (boundp 'beginning-of-defun-function)
- (define-key map "\M-\C-a" 'erlang-beginning-of-function)
- (define-key map "\M-\C-e" 'erlang-end-of-function)
- (define-key map '(meta control h) 'erlang-mark-function)) ; Xemacs
- (define-key map "\M-\t" 'erlang-complete-tag)
- (define-key map "\C-c\M-\t" 'tempo-complete-tag)
- (define-key map "\M-+" 'erlang-find-next-tag)
- (define-key map "\C-c\M-a" 'erlang-beginning-of-clause)
- (define-key map "\C-c\M-b" 'tempo-backward-mark)
- (define-key map "\C-c\M-e" 'erlang-end-of-clause)
- (define-key map "\C-c\M-f" 'tempo-forward-mark)
- (define-key map "\C-c\M-h" 'erlang-mark-clause)
- (define-key map "\C-c\C-c" 'comment-region)
- (define-key map "\C-c\C-j" 'erlang-generate-new-clause)
- (define-key map "\C-c\C-k" 'erlang-compile)
- (define-key map "\C-c\C-l" 'erlang-compile-display)
- (define-key map "\C-c\C-s" 'erlang-show-syntactic-information)
- (define-key map "\C-c\C-q" 'erlang-indent-function)
- (define-key map "\C-c\C-u" 'erlang-uncomment-region)
- (define-key map "\C-c\C-y" 'erlang-clone-arguments)
- (define-key map "\C-c\C-a" 'erlang-align-arrows)
- (define-key map "\C-c\C-z" 'erlang-shell-display)
- (unless inferior-erlang-use-cmm
- (define-key map "\C-x`" 'erlang-next-error)))
-
-
(defun erlang-electric-init ()
;; Set up electric character functions to work with
;; delsel/pending-del mode. Also, set up text properties for bit
@@ -1402,7 +1403,7 @@ Other commands:
(set (make-local-variable 'imenu-prev-index-position-function)
'erlang-beginning-of-function)
(set (make-local-variable 'imenu-extract-index-name-function)
- 'erlang-get-function-name)
+ 'erlang-get-function-name-and-arity)
(set (make-local-variable 'tempo-match-finder)
"[^-a-zA-Z0-9_]\\([-a-zA-Z0-9_]*\\)\\=")
(set (make-local-variable 'beginning-of-defun-function)
@@ -1483,7 +1484,23 @@ Other commands:
erlang-font-lock-keywords-3
erlang-font-lock-keywords-4)
nil nil ((?_ . "w")) erlang-beginning-of-clause
- (font-lock-mark-block-function . erlang-mark-clause))))
+ (font-lock-mark-block-function . erlang-mark-clause)
+ (font-lock-syntactic-keywords
+ ;; A dollar sign right before the double quote that ends a
+ ;; string is not a character escape.
+ ;;
+ ;; And a "string" has with a double quote not escaped by a
+ ;; dollar sign, any number of non-backslash non-newline
+ ;; characters or escaped backslashes, a dollar sign
+ ;; (otherwise we wouldn't care) and a double quote. This
+ ;; doesn't match multi-line strings, but this is probably
+ ;; the best we can get, since while font-locking we don't
+ ;; know whether matching started inside a string: limiting
+ ;; search to a single line keeps things sane.
+ . (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w")
+ ;; And the dollar sign in $\" escapes two characters, not
+ ;; just one.
+ ("\\(\\$\\)\\\\\\\"" 1 "'"))))))
@@ -2492,9 +2509,10 @@ Value is list (stack token-start token-type in-what)."
((looking-at "\\(of\\)[^_a-zA-Z0-9]")
;; Must handle separately, try X of -> catch
(if (and stack (eq (car (car stack)) 'try))
- (let ((try-column (nth 2 (car stack))))
+ (let ((try-column (nth 2 (car stack)))
+ (try-pos (nth 1 (car stack))))
(erlang-pop stack)
- (erlang-push (list 'icr token try-column) stack))))
+ (erlang-push (list 'icr try-pos try-column) stack))))
((looking-at "\\(fun\\)[^_a-zA-Z0-9]")
;; Push a new layer if we are defining a `fun'
@@ -2755,7 +2773,7 @@ Return nil if inside string, t if in a comment."
;;
;; `after' should be indented to the same level as the
;; corresponding receive.
- (cond ((looking-at "\\(after\\|catch\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)")
+ (cond ((looking-at "\\(after\\|of\\)\\($\\|[^_a-zA-Z0-9]\\)")
(nth 2 stack-top))
((looking-at "when[^_a-zA-Z0-9]")
;; Handling one when part
@@ -2774,7 +2792,7 @@ Return nil if inside string, t if in a comment."
((and (eq (car stack-top) '||) (looking-at "\\(]\\|>>\\)[^_a-zA-Z0-9]"))
(nth 2 (car (cdr stack))))
;; Real indentation, where operators create extra indentation etc.
- ((memq (car stack-top) '(-> || begin try))
+ ((memq (car stack-top) '(-> || try begin))
(if (looking-at "\\(of\\)[^_a-zA-Z0-9]")
(nth 2 stack-top)
(goto-char (nth 1 stack-top))
@@ -2803,19 +2821,24 @@ Return nil if inside string, t if in a comment."
(erlang-caddr (car stack))
0))
((looking-at "catch\\($\\|[^_a-zA-Z0-9]\\)")
- (if (or (eq (car stack-top) 'try)
- (eq (car (car (cdr stack))) 'icr))
- (progn
- (if (eq (car stack-top) '->)
- (erlang-pop stack))
- (if stack
- (erlang-caddr (car stack))
- 0))
- base)) ;; old catch
+ ;; Are we in a try
+ (let ((start (if (eq (car stack-top) '->)
+ (car (cdr stack))
+ stack-top)))
+ (if (null start) nil
+ (goto-char (nth 1 start)))
+ (cond ((looking-at "try\\($\\|[^_a-zA-Z0-9]\\)")
+ (progn
+ (if (eq (car stack-top) '->)
+ (erlang-pop stack))
+ (if stack
+ (erlang-caddr (car stack))
+ 0)))
+ (t (erlang-indent-standard indent-point token base 'nil))))) ;; old catch
(t
(erlang-indent-standard indent-point token base 'nil)
))))
- ))
+ ))
((eq (car stack-top) 'when)
(goto-char (nth 1 stack-top))
(if (looking-at "when\\s *\\($\\|%\\)")
@@ -2841,27 +2864,32 @@ Return nil if inside string, t if in a comment."
(current-column)))
;; Type and Spec indentation
((eq (car stack-top) '::)
- (cond ((null erlang-argument-indent)
- ;; indent to next column.
- (+ 2 (nth 2 stack-top)))
- ((looking-at "::[^_a-zA-Z0-9]")
- (nth 2 stack-top))
- (t
- (let ((start-alternativ (if (looking-at "|") 2 0)))
- (goto-char (nth 1 stack-top))
- (- (cond ((looking-at "::\\s *\\($\\|%\\)")
- ;; Line ends with ::
- (if (eq (car (car (last stack))) 'spec)
+ (if (looking-at "}")
+ ;; Closing record definition with types
+ ;; pop stack and recurse
+ (erlang-calculate-stack-indent indent-point
+ (cons (erlang-pop stack) (cdr state)))
+ (cond ((null erlang-argument-indent)
+ ;; indent to next column.
+ (+ 2 (nth 2 stack-top)))
+ ((looking-at "::[^_a-zA-Z0-9]")
+ (nth 2 stack-top))
+ (t
+ (let ((start-alternativ (if (looking-at "|") 2 0)))
+ (goto-char (nth 1 stack-top))
+ (- (cond ((looking-at "::\\s *\\($\\|%\\)")
+ ;; Line ends with ::
+ (if (eq (car (car (last stack))) 'spec)
(+ (erlang-indent-find-preceding-expr 1)
erlang-argument-indent)
- (+ (erlang-indent-find-preceding-expr 2)
- erlang-argument-indent)))
- (t
- ;; Indent to the same column as the first
- ;; argument.
- (goto-char (+ 2 (nth 1 stack-top)))
- (skip-chars-forward " \t")
- (current-column))) start-alternativ)))))
+ (+ (erlang-indent-find-preceding-expr 2)
+ erlang-argument-indent)))
+ (t
+ ;; Indent to the same column as the first
+ ;; argument.
+ (goto-char (+ 2 (nth 1 stack-top)))
+ (skip-chars-forward " \t")
+ (current-column))) start-alternativ))))))
)))
(defun erlang-indent-standard (indent-point token base inside-parenthesis)
@@ -2933,10 +2961,16 @@ This assumes that the preceding expression is either simple
(skip-chars-backward " \t")
;; Needed to match the colon in "'foo':'bar'".
(if (not (memq (preceding-char) '(?# ?:)))
- col
- (backward-char 1)
- (forward-sexp -1)
- (current-column)))))
+ col
+ ;; Special hack to handle: (note line break)
+ ;; [#myrecord{
+ ;; foo = foo}]
+ (or
+ (ignore-errors
+ (backward-char 1)
+ (forward-sexp -1)
+ (current-column))
+ col)))))
(defun erlang-indent-parenthesis (stack-position)
(let ((previous (erlang-indent-find-preceding-expr)))
@@ -3505,6 +3539,13 @@ Normally used in conjunction with `erlang-beginning-of-clause', e.g.:
res)
(error nil)))))
+(defun erlang-get-function-name-and-arity ()
+ "Return the name and arity of the function at point, or nil.
+The return value is a string of the form \"foo/1\"."
+ (let ((name (erlang-get-function-name))
+ (arity (erlang-get-function-arity)))
+ (and name arity (format "%s/%d" name arity))))
+
(defun erlang-get-function-arguments ()
"Return arguments of current function, or nil."
(if (not (looking-at (eval-when-compile
@@ -4901,9 +4942,14 @@ a prompt. When nil, we will wait forever, or until \\[keyboard-quit].")
(defvar inferior-erlang-buffer nil
"Buffer of last invoked inferior Erlang, or nil.")
+;; Enable uniquifying Erlang shell buffers based on directory name.
+(eval-after-load "uniquify"
+ '(add-to-list 'uniquify-list-buffers-directory-modes 'erlang-shell-mode))
+
;;;###autoload
-(defun inferior-erlang ()
+(defun inferior-erlang (&optional command)
"Run an inferior Erlang.
+With prefix command, prompt for command to start Erlang with.
This is just like running Erlang in a normal shell, except that
an Emacs buffer is used for input and output.
@@ -4917,17 +4963,37 @@ Entry to this mode calls the functions in the variables
The following commands imitate the usual Unix interrupt and
editing control characters:
\\{erlang-shell-mode-map}"
- (interactive)
+ (interactive
+ (when current-prefix-arg
+ (list (if (fboundp 'read-shell-command)
+ ;; `read-shell-command' is a new function in Emacs 23.
+ (read-shell-command "Erlang command: ")
+ (read-string "Erlang command: ")))))
(require 'comint)
- (let ((opts inferior-erlang-machine-options))
- (cond ((eq inferior-erlang-shell-type 'oldshell)
- (setq opts (cons "-oldshell" opts)))
- ((eq inferior-erlang-shell-type 'newshell)
- (setq opts (append '("-newshell" "-env" "TERM" "vt100") opts))))
- (setq inferior-erlang-buffer
- (apply 'make-comint
- inferior-erlang-process-name inferior-erlang-machine
- nil opts)))
+ (let (cmd opts)
+ (if command
+ (setq cmd "sh"
+ opts (list "-c" command))
+ (setq cmd inferior-erlang-machine
+ opts inferior-erlang-machine-options)
+ (cond ((eq inferior-erlang-shell-type 'oldshell)
+ (setq opts (cons "-oldshell" opts)))
+ ((eq inferior-erlang-shell-type 'newshell)
+ (setq opts (append '("-newshell" "-env" "TERM" "vt100") opts)))))
+
+ ;; Using create-file-buffer and list-buffers-directory in this way
+ ;; makes uniquify give each buffer a unique name based on the
+ ;; directory.
+ (let ((fake-file-name (expand-file-name inferior-erlang-buffer-name default-directory)))
+ (setq inferior-erlang-buffer (create-file-buffer fake-file-name))
+ (apply 'make-comint-in-buffer
+ inferior-erlang-process-name
+ inferior-erlang-buffer
+ cmd
+ nil opts)
+ (with-current-buffer inferior-erlang-buffer
+ (setq list-buffers-directory fake-file-name))))
+
(setq inferior-erlang-process
(get-buffer-process inferior-erlang-buffer))
(if (> 21 erlang-emacs-major-version) ; funcalls to avoid compiler warnings
@@ -4940,10 +5006,6 @@ editing control characters:
(if (and (not (eq system-type 'windows-nt))
(eq inferior-erlang-shell-type 'newshell))
(setq comint-process-echoes t))
- ;; `rename-buffer' takes only one argument in Emacs 18.
- (condition-case nil
- (rename-buffer inferior-erlang-buffer-name t)
- (error (rename-buffer inferior-erlang-buffer-name)))
(erlang-shell-mode))
diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented
index d0ea4c29cf..2948ccf1b5 100644
--- a/lib/tools/emacs/test.erl.indented
+++ b/lib/tools/emacs/test.erl.indented
@@ -93,11 +93,27 @@
-type t13() :: maybe_improper_list(integer(), t11()).
-type t14() :: [erl_scan:foo() |
%% Should be highlighted
- non_neg_integer() | nonempty_list() |
+ term() |
+ bool() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
%% Should not be highlighted
nonempty_() | nonlist() |
- erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+
-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
@@ -172,6 +188,9 @@
f19 = 3 :: integer()|undefined,
f5 = 3 :: undefined|integer()}).
+-record(state, {
+ sequence_number = 1 :: integer()
+ }).
highlighting(X) % Function definitions should be highlighted
@@ -493,7 +512,9 @@ indent_try_catch() ->
file:close(Xfile)
end;
indent_try_catch() ->
- try foo(bar) of
+ try
+ foo(bar)
+ of
X when true andalso
kalle ->
io:format(stdout, "Parsing file ~s, ",
@@ -551,14 +572,57 @@ indent_catch() ->
C = catch B +
float(43.1),
- case catch (X) of
+ case catch foo(X) of
+ A ->
+ B
+ end,
+
+ case
+ catch foo(X)
+ of
A ->
B
end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
try sune of
_ -> foo
catch _:_ -> baf
- end.
+ end,
+
+ try
+ sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+ (catch exit())
+ catch
+ _ ->
+ catch baf()
+ end,
+ ok.
indent_binary() ->
X = lists:foldr(fun(M) ->
@@ -588,3 +652,8 @@ indent_comprehensions() ->
true = (X rem 2)
>>,
ok.
+
+%% This causes an error in earlier erlang-mode versions.
+foo() ->
+ [#foo{
+ foo = foo}].
diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig
index 70e97a2e91..1221c5655e 100644
--- a/lib/tools/emacs/test.erl.orig
+++ b/lib/tools/emacs/test.erl.orig
@@ -93,11 +93,27 @@
-type t13() :: maybe_improper_list(integer(), t11()).
-type t14() :: [erl_scan:foo() |
%% Should be highlighted
- non_neg_integer() | nonempty_list() |
+ term() |
+ bool() |
+ byte() |
+ char() |
+ non_neg_integer() | nonempty_list() |
+ pos_integer() |
+ neg_integer() |
+ number() |
+ list() |
nonempty_improper_list() | nonempty_maybe_improper_list() |
+ maybe_improper_list() | string() | iolist() | byte() |
+ module() |
+ mfa() |
+ node() |
+ timeout() |
+ no_return() |
%% Should not be highlighted
nonempty_() | nonlist() |
-erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+ erl_scan:bar(34, 92) | t13() | m:f(integer() | <<_:_*16>>)].
+
+
-type t15() :: {binary(),<<>>,<<_:34>>,<<_:_*42>>,
<<_:3,_:_*14>>,<<>>} | [<<>>|<<_:34>>|<<_:16>>|
<<_:3,_:_*1472>>|<<_:19,_:_*14>>| <<_:34>>|
@@ -172,6 +188,9 @@ f18 :: 1 | 2 | 'undefined',
f19 = 3 :: integer()|undefined,
f5 = 3 :: undefined|integer()}).
+-record(state, {
+ sequence_number = 1 :: integer()
+ }).
highlighting(X) % Function definitions should be highlighted
@@ -493,7 +512,9 @@ indent_try_catch() ->
file:close(Xfile)
end;
indent_try_catch() ->
- try foo(bar) of
+ try
+ foo(bar)
+ of
X when true andalso
kalle ->
io:format(stdout, "Parsing file ~s, ",
@@ -551,14 +572,57 @@ indent_catch() ->
C = catch B +
float(43.1),
- case catch (X) of
+ case catch foo(X) of
A ->
B
end,
+
+ case
+ catch foo(X)
+ of
+ A ->
+ B
+ end,
+
+ case
+ foo(X)
+ of
+ A ->
+ catch B,
+ X
+ end,
+
try sune of
- _ -> foo
- catch _:_ -> baf
- end.
+ _ -> foo
+ catch _:_ -> baf
+ end,
+
+ try
+sune
+ of
+ _ ->
+ X = 5,
+ (catch foo(X)),
+ X + 10
+ catch _:_ -> baf
+ end,
+
+ try
+ (catch sune)
+ of
+ _ ->
+ catch foo() %% BUGBUG can't handle catch inside try without parentheses
+ catch _:_ ->
+ baf
+ end,
+
+ try
+(catch exit())
+ catch
+_ ->
+ catch baf()
+ end,
+ ok.
indent_binary() ->
X = lists:foldr(fun(M) ->
@@ -588,3 +652,8 @@ Binary2 = << <<X:8>> || <<X:32,_:32>> <= <<0:512>>,
true = (X rem 2)
>>,
ok.
+
+%% This causes an error in earlier erlang-mode versions.
+foo() ->
+[#foo{
+foo = foo}].
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index 6501e05a6e..230f0e9428 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,23 +35,37 @@
%% remote_process_loop/1.
%%
%% TABLES
-%% Each nodes has an ets table named 'cover_internal_data_table'
-%% (?COVER_TABLE). This table contains the coverage data and is
-%% continously updated when cover compiled code is executed.
+%% Each nodes has two tables: cover_internal_data_table (?COVER_TABLE) and.
+%% cover_internal_clause_table (?COVER_CLAUSE_TABLE).
+%% ?COVER_TABLE contains the bump data i.e. the data about which lines
+%% have been executed how many times.
+%% ?COVER_CLAUSE_TABLE contains information about which clauses in which modules
+%% cover is currently collecting statistics.
%%
-%% The main node owns a table named
-%% 'cover_collected_remote_data_table' (?COLLECTION_TABLE). This table
-%% contains data which is collected from remote nodes (either when a
-%% remote node is stopped with cover:stop/1 or when analysing. When
-%% analysing, data is even moved from the ?COVER_TABLE on the main
-%% node to the ?COLLECTION_TABLE.
+%% The main node owns tables named
+%% 'cover_collected_remote_data_table' (?COLLECTION_TABLE) and
+%% 'cover_collected_remote_clause_table' (?COLLECTION_CLAUSE_TABLE).
+%% These tables contain data which is collected from remote nodes (either when a
+%% remote node is stopped with cover:stop/1 or when analysing). When
+%% analysing, data is even moved from the COVER tables on the main
+%% node to the COLLECTION tables.
%%
%% The main node also has a table named 'cover_binary_code_table'
%% (?BINARY_TABLE). This table contains the binary code for each cover
%% compiled module. This is necessary so that the code can be loaded
%% on remote nodes that are started after the compilation.
%%
-
+%% PARELLALISM
+%% To take advantage of SMP when doing the cover analysis both the data
+%% collection and analysis has been parallelized. One process is spawned for
+%% each node when collecting data, and on the remote node when collecting data
+%% one process is spawned per module.
+%%
+%% When analyzing data it is possible to issue multiple analyse(_to_file)/X
+%% calls at once. They are however all calls (for backwardscompatability
+%% reasons) so the user of cover will have to spawn several processes to to the
+%% calls ( or use async_analyse_to_file ).
+%%
%% External exports
-export([start/0, start/1,
@@ -61,6 +75,9 @@
analyse/1, analyse/2, analyse/3, analyze/1, analyze/2, analyze/3,
analyse_to_file/1, analyse_to_file/2, analyse_to_file/3,
analyze_to_file/1, analyze_to_file/2, analyze_to_file/3,
+ async_analyse_to_file/1,async_analyse_to_file/2,
+ async_analyse_to_file/3, async_analyze_to_file/1,
+ async_analyze_to_file/2, async_analyze_to_file/3,
export/1, export/2, import/1,
modules/0, imported/0, imported_modules/0, which_nodes/0, is_compiled/1,
reset/1, reset/0,
@@ -100,8 +117,10 @@
}).
-define(COVER_TABLE, 'cover_internal_data_table').
+-define(COVER_CLAUSE_TABLE, 'cover_internal_clause_table').
-define(BINARY_TABLE, 'cover_binary_code_table').
-define(COLLECTION_TABLE, 'cover_collected_remote_data_table').
+-define(COLLECTION_CLAUSE_TABLE, 'cover_collected_remote_clause_table').
-define(TAG, cover_compiled).
-define(SERVER, cover_server).
@@ -114,6 +133,8 @@
true -> ?BLOCK(Expr)
end).
+-define(SPAWN_DBG(Tag,Value),put(Tag,Value)).
+
-include_lib("stdlib/include/ms_transform.hrl").
%%%----------------------------------------------------------------------
@@ -127,7 +148,10 @@ start() ->
case whereis(?SERVER) of
undefined ->
Starter = self(),
- Pid = spawn(fun() -> init_main(Starter) end),
+ Pid = spawn(fun() ->
+ ?SPAWN_DBG(start,[]),
+ init_main(Starter)
+ end),
Ref = erlang:monitor(process,Pid),
Return =
receive
@@ -382,6 +406,30 @@ analyze_to_file(Module, OptOrOut) -> analyse_to_file(Module, OptOrOut).
analyze_to_file(Module, OutFile, Options) ->
analyse_to_file(Module, OutFile, Options).
+async_analyse_to_file(Module) ->
+ do_spawn(?MODULE, analyse_to_file, [Module]).
+async_analyse_to_file(Module, OutFileOrOpts) ->
+ do_spawn(?MODULE, analyse_to_file, [Module, OutFileOrOpts]).
+async_analyse_to_file(Module, OutFile, Options) ->
+ do_spawn(?MODULE, analyse_to_file, [Module, OutFile, Options]).
+
+do_spawn(M,F,A) ->
+ spawn_link(fun() ->
+ case apply(M,F,A) of
+ {ok, _} ->
+ ok;
+ {error, Reason} ->
+ exit(Reason)
+ end
+ end).
+
+async_analyze_to_file(Module) ->
+ async_analyse_to_file(Module).
+async_analyze_to_file(Module, OutFileOrOpts) ->
+ async_analyse_to_file(Module, OutFileOrOpts).
+async_analyze_to_file(Module, OutFile, Options) ->
+ async_analyse_to_file(Module, OutFile, Options).
+
outfilename(Module,Opts) ->
case lists:member(html,Opts) of
true ->
@@ -500,6 +548,8 @@ remote_call(Node,Request) ->
Return
end.
+remote_reply(Proc,Reply) when is_pid(Proc) ->
+ Proc ! {?SERVER,Reply};
remote_reply(MainNode,Reply) ->
{?SERVER,MainNode} ! {?SERVER,Reply}.
@@ -509,9 +559,15 @@ remote_reply(MainNode,Reply) ->
init_main(Starter) ->
register(?SERVER,self()),
- ets:new(?COVER_TABLE, [set, public, named_table]),
+ %% Having write concurrancy here gives a 40% performance boost
+ %% when collect/1 is called.
+ ets:new(?COVER_TABLE, [set, public, named_table
+ ,{write_concurrency, true}
+ ]),
+ ets:new(?COVER_CLAUSE_TABLE, [set, public, named_table]),
ets:new(?BINARY_TABLE, [set, named_table]),
ets:new(?COLLECTION_TABLE, [set, public, named_table]),
+ ets:new(?COLLECTION_CLAUSE_TABLE, [set, public, named_table]),
process_flag(trap_exit,true),
Starter ! {?SERVER,started},
main_process_loop(#main_state{}).
@@ -593,40 +649,10 @@ main_process_loop(State) ->
end;
{From, {export,OutFile,Module}} ->
- case file:open(OutFile,[write,binary,raw]) of
- {ok,Fd} ->
- Reply =
- case Module of
- '_' ->
- export_info(State#main_state.imported),
- collect(State#main_state.nodes),
- do_export_table(State#main_state.compiled,
- State#main_state.imported,
- Fd);
- _ ->
- export_info(Module,State#main_state.imported),
- case is_loaded(Module, State) of
- {loaded, File} ->
- [{Module,Clauses}] =
- ets:lookup(?COVER_TABLE,Module),
- collect(Module, Clauses,
- State#main_state.nodes),
- do_export_table([{Module,File}],[],Fd);
- {imported, File, ImportFiles} ->
- %% don't know if I should allow this -
- %% export a module which is only imported
- Imported = [{Module,File,ImportFiles}],
- do_export_table([],Imported,Fd);
- _NotLoaded ->
- {error,{not_cover_compiled,Module}}
- end
- end,
- file:close(Fd),
- reply(From, Reply);
- {error,Reason} ->
- reply(From, {error, {cant_open_file,OutFile,Reason}})
-
- end,
+ spawn(fun() ->
+ ?SPAWN_DBG(export,{OutFile, Module}),
+ do_export(Module, OutFile, From, State)
+ end),
main_process_loop(State);
{From, {import,File}} ->
@@ -692,107 +718,73 @@ main_process_loop(State) ->
unregister(?SERVER),
reply(From, ok);
- {From, {Request, Module}} ->
- case is_loaded(Module, State) of
- {loaded, File} ->
- {Reply,State1} =
- case Request of
- {analyse, Analysis, Level} ->
- analyse_info(Module,State#main_state.imported),
- [{Module,Clauses}] =
- ets:lookup(?COVER_TABLE,Module),
- collect(Module,Clauses,State#main_state.nodes),
- R = do_analyse(Module, Analysis, Level, Clauses),
- {R,State};
-
- {analyse_to_file, OutFile, Opts} ->
- R = case find_source(File) of
- {beam,_BeamFile} ->
- {error,no_source_code_found};
- ErlFile ->
- Imported = State#main_state.imported,
- analyse_info(Module,Imported),
- [{Module,Clauses}] =
- ets:lookup(?COVER_TABLE,Module),
- collect(Module, Clauses,
- State#main_state.nodes),
- HTML = lists:member(html,Opts),
- do_analyse_to_file(Module,OutFile,
- ErlFile,HTML)
- end,
- {R,State};
-
- is_compiled ->
- {{file, File},State};
-
- reset ->
- R = do_reset_main_node(Module,
- State#main_state.nodes),
- Imported =
- remove_imported(Module,
- State#main_state.imported),
- {R,State#main_state{imported=Imported}}
- end,
- reply(From, Reply),
- main_process_loop(State1);
-
- {imported,File,_ImportFiles} ->
- {Reply,State1} =
- case Request of
- {analyse, Analysis, Level} ->
- analyse_info(Module,State#main_state.imported),
- [{Module,Clauses}] =
- ets:lookup(?COLLECTION_TABLE,Module),
- R = do_analyse(Module, Analysis, Level, Clauses),
- {R,State};
-
- {analyse_to_file, OutFile, Opts} ->
- R = case find_source(File) of
- {beam,_BeamFile} ->
- {error,no_source_code_found};
- ErlFile ->
- Imported = State#main_state.imported,
- analyse_info(Module,Imported),
- HTML = lists:member(html,Opts),
- do_analyse_to_file(Module,OutFile,
- ErlFile,HTML)
- end,
- {R,State};
-
- is_compiled ->
- {false,State};
-
- reset ->
- R = do_reset_collection_table(Module),
- Imported =
- remove_imported(Module,
- State#main_state.imported),
- {R,State#main_state{imported=Imported}}
- end,
- reply(From, Reply),
- main_process_loop(State1);
-
- NotLoaded ->
- Reply =
- case Request of
- is_compiled ->
- false;
- _ ->
- {error, {not_cover_compiled,Module}}
- end,
- Compiled =
- case NotLoaded of
- unloaded ->
- do_clear(Module),
- remote_unload(State#main_state.nodes,[Module]),
- update_compiled([Module],
- State#main_state.compiled);
- false ->
- State#main_state.compiled
+ {From, {{analyse, Analysis, Level}, Module}} ->
+ S = try
+ Loaded = is_loaded(Module, State),
+ spawn(fun() ->
+ ?SPAWN_DBG(analyse,{Module,Analysis, Level}),
+ do_parallel_analysis(
+ Module, Analysis, Level,
+ Loaded, From, State)
+ end),
+ State
+ catch throw:Reason ->
+ reply(From,{error, {not_cover_compiled,Module}}),
+ not_loaded(Module, Reason, State)
+ end,
+ main_process_loop(S);
+
+ {From, {{analyse_to_file, OutFile, Opts},Module}} ->
+ S = try
+ Loaded = is_loaded(Module, State),
+ spawn(fun() ->
+ ?SPAWN_DBG(analyse_to_file,
+ {Module,OutFile, Opts}),
+ do_parallel_analysis_to_file(
+ Module, OutFile, Opts,
+ Loaded, From, State)
+ end),
+ State
+ catch throw:Reason ->
+ reply(From,{error, {not_cover_compiled,Module}}),
+ not_loaded(Module, Reason, State)
+ end,
+ main_process_loop(S);
+
+ {From, {is_compiled, Module}} ->
+ S = try is_loaded(Module, State) of
+ {loaded, File} ->
+ reply(From,{file, File}),
+ State;
+ {imported,_File,_ImportFiles} ->
+ reply(From,false),
+ State
+ catch throw:Reason ->
+ reply(From,false),
+ not_loaded(Module, Reason, State)
+ end,
+ main_process_loop(S);
+
+ {From, {reset, Module}} ->
+ S = try
+ Loaded = is_loaded(Module,State),
+ R = case Loaded of
+ {loaded, _File} ->
+ do_reset_main_node(
+ Module, State#main_state.nodes);
+ {imported, _File, _} ->
+ do_reset_collection_table(Module)
end,
- reply(From, Reply),
- main_process_loop(State#main_state{compiled=Compiled})
- end;
+ Imported =
+ remove_imported(Module,
+ State#main_state.imported),
+ reply(From, R),
+ State#main_state{imported=Imported}
+ catch throw:Reason ->
+ reply(From,{error, {not_cover_compiled,Module}}),
+ not_loaded(Module, Reason, State)
+ end,
+ main_process_loop(S);
{'EXIT',Pid,_Reason} ->
%% Exit is trapped on the main node only, so this will only happen
@@ -807,17 +799,17 @@ main_process_loop(State) ->
main_process_loop(State)
end.
-
-
-
-
%%%----------------------------------------------------------------------
%%% cover_server on remote node
%%%----------------------------------------------------------------------
init_remote(Starter,MainNode) ->
register(?SERVER,self()),
- ets:new(?COVER_TABLE, [set, public, named_table]),
+ ets:new(?COVER_TABLE, [set, public, named_table
+ %% write_concurrency here makes otp_8270 break :(
+ %,{write_concurrency, true}
+ ]),
+ ets:new(?COVER_CLAUSE_TABLE, [set, public, named_table]),
Starter ! {self(),started},
remote_process_loop(#remote_state{main_node=MainNode}).
@@ -843,29 +835,14 @@ remote_process_loop(State) ->
remote_process_loop(State);
{remote,collect,Module,CollectorPid} ->
- MS =
- case Module of
- '_' -> ets:fun2ms(fun({M,C}) when is_atom(M) -> C end);
- _ -> ets:fun2ms(fun({M,C}) when M=:=Module -> C end)
- end,
- AllClauses = lists:flatten(ets:select(?COVER_TABLE,MS)),
-
- %% Sending clause by clause in order to avoid large lists
- lists:foreach(
- fun({M,F,A,C,_L}) ->
- Pattern =
- {#bump{module=M, function=F, arity=A, clause=C}, '_'},
- Bumps = ets:match_object(?COVER_TABLE, Pattern),
- %% Reset
- lists:foreach(fun({Bump,_N}) ->
- ets:insert(?COVER_TABLE, {Bump,0})
- end,
- Bumps),
- CollectorPid ! {chunk,Bumps}
- end,
- AllClauses),
- CollectorPid ! done,
- remote_reply(State#remote_state.main_node, ok),
+ self() ! {remote,collect,Module,CollectorPid, ?SERVER};
+
+ {remote,collect,Module,CollectorPid,From} ->
+ spawn(fun() ->
+ ?SPAWN_DBG(remote_collect,
+ {Module, CollectorPid, From}),
+ do_collect(Module, CollectorPid, From)
+ end),
remote_process_loop(State);
{remote,stop} ->
@@ -894,6 +871,33 @@ remote_process_loop(State) ->
end.
+do_collect(Module, CollectorPid, From) ->
+ AllMods =
+ case Module of
+ '_' -> ets:tab2list(?COVER_CLAUSE_TABLE);
+ _ -> ets:lookup(?COVER_CLAUSE_TABLE, Module)
+ end,
+
+ %% Sending clause by clause in order to avoid large lists
+ pmap(
+ fun({_Mod,Clauses}) ->
+ lists:map(fun(Clause) ->
+ send_collected_data(Clause, CollectorPid)
+ end,Clauses)
+ end,AllMods),
+ CollectorPid ! done,
+ remote_reply(From, ok).
+
+send_collected_data({M,F,A,C,_L}, CollectorPid) ->
+ Pattern =
+ {#bump{module=M, function=F, arity=A, clause=C}, '_'},
+ Bumps = ets:match_object(?COVER_TABLE, Pattern),
+ %% Reset
+ lists:foreach(fun({Bump,_N}) ->
+ ets:insert(?COVER_TABLE, {Bump,0})
+ end,
+ Bumps),
+ CollectorPid ! {chunk,Bumps}.
reload_originals([{Module,_File}|Compiled]) ->
do_reload_original(Module),
@@ -932,6 +936,9 @@ load_compiled([{Module,File,Binary,InitialTable}|Compiled],Acc) ->
load_compiled([],Acc) ->
Acc.
+insert_initial_data([Item|Items]) when is_atom(element(1,Item)) ->
+ ets:insert(?COVER_CLAUSE_TABLE, Item),
+ insert_initial_data(Items);
insert_initial_data([Item|Items]) ->
ets:insert(?COVER_TABLE, Item),
insert_initial_data(Items);
@@ -957,7 +964,10 @@ remote_start(MainNode) ->
case whereis(?SERVER) of
undefined ->
Starter = self(),
- Pid = spawn(fun() -> init_remote(Starter,MainNode) end),
+ Pid = spawn(fun() ->
+ ?SPAWN_DBG(remote_start,{MainNode}),
+ init_remote(Starter,MainNode)
+ end),
Ref = erlang:monitor(process,Pid),
Return =
receive
@@ -972,14 +982,25 @@ remote_start(MainNode) ->
{error,{already_started,Pid}}
end.
-%% Load a set of cover compiled modules on remote nodes
-remote_load_compiled(Nodes,Compiled0) ->
- Compiled = lists:map(fun get_data_for_remote_loading/1,Compiled0),
+%% Load a set of cover compiled modules on remote nodes,
+%% We do it ?MAX_MODS modules at a time so that we don't
+%% run out of memory on the cover_server node.
+-define(MAX_MODS, 10).
+remote_load_compiled(Nodes,Compiled) ->
+ remote_load_compiled(Nodes, Compiled, [], 0).
+remote_load_compiled(_Nodes, [], [], _ModNum) ->
+ ok;
+remote_load_compiled(Nodes, Compiled, Acc, ModNum)
+ when Compiled == []; ModNum == ?MAX_MODS ->
lists:foreach(
fun(Node) ->
- remote_call(Node,{remote,load_compiled,Compiled})
+ remote_call(Node,{remote,load_compiled,Acc})
end,
- Nodes).
+ Nodes),
+ remote_load_compiled(Nodes, Compiled, [], 0);
+remote_load_compiled(Nodes, [MF | Rest], Acc, ModNum) ->
+ remote_load_compiled(
+ Nodes, Rest, [get_data_for_remote_loading(MF) | Acc], ModNum + 1).
%% Read all data needed for loading a cover compiled module on a remote node
%% Binary is the beam code for the module and InitialTable is the initial
@@ -987,15 +1008,15 @@ remote_load_compiled(Nodes,Compiled0) ->
get_data_for_remote_loading({Module,File}) ->
[{Module,Binary}] = ets:lookup(?BINARY_TABLE,Module),
%%! The InitialTable list will be long if the module is big - what to do??
- InitialTable = ets:select(?COVER_TABLE,ms(Module)),
- {Module,File,Binary,InitialTable}.
+ InitialBumps = ets:select(?COVER_TABLE,ms(Module)),
+ InitialClauses = ets:lookup(?COVER_CLAUSE_TABLE,Module),
+
+ {Module,File,Binary,InitialBumps ++ InitialClauses}.
%% Create a match spec which returns the clause info {Module,InitInfo} and
%% all #bump keys for the given module with 0 number of calls.
ms(Module) ->
- ets:fun2ms(fun({Module,InitInfo}) ->
- {Module,InitInfo};
- ({Key,_}) when is_record(Key,bump),Key#bump.module=:=Module ->
+ ets:fun2ms(fun({Key,_}) when Key#bump.module=:=Module ->
{Key,0}
end).
@@ -1017,27 +1038,30 @@ remote_reset(Module,Nodes) ->
%% Collect data from remote nodes - used for analyse or stop(Node)
remote_collect(Module,Nodes,Stop) ->
- CollectorPid = spawn(fun() -> collector_proc(length(Nodes)) end),
- lists:foreach(
- fun(Node) ->
- remote_call(Node,{remote,collect,Module,CollectorPid}),
- if Stop -> remote_call(Node,{remote,stop});
- true -> ok
- end
- end,
- Nodes).
+ pmap(fun(Node) ->
+ ?SPAWN_DBG(remote_collect,
+ {Module, Nodes, Stop}),
+ do_collection(Node, Module, Stop)
+ end,
+ Nodes).
+
+do_collection(Node, Module, Stop) ->
+ CollectorPid = spawn(fun collector_proc/0),
+ remote_call(Node,{remote,collect,Module,CollectorPid, self()}),
+ if Stop -> remote_call(Node,{remote,stop});
+ true -> ok
+ end.
%% Process which receives chunks of data from remote nodes - either when
%% analysing or when stopping cover on the remote nodes.
-collector_proc(0) ->
- ok;
-collector_proc(N) ->
+collector_proc() ->
+ ?SPAWN_DBG(collector_proc, []),
receive
{chunk,Chunk} ->
insert_in_collection_table(Chunk),
- collector_proc(N);
+ collector_proc();
done ->
- collector_proc(N-1)
+ ok
end.
insert_in_collection_table([{Key,Val}|Chunk]) ->
@@ -1052,7 +1076,13 @@ insert_in_collection_table(Key,Val) ->
ets:update_counter(?COLLECTION_TABLE,
Key,Val);
false ->
- ets:insert(?COLLECTION_TABLE,{Key,Val})
+ %% Make sure that there are no race conditions from ets:member
+ case ets:insert_new(?COLLECTION_TABLE,{Key,Val}) of
+ false ->
+ insert_in_collection_table(Key,Val);
+ _ ->
+ ok
+ end
end.
@@ -1073,14 +1103,15 @@ analyse_info(Module,Imported) ->
export_info(_Module,[]) ->
ok;
-export_info(Module,Imported) ->
- imported_info("Export",Module,Imported).
+export_info(_Module,_Imported) ->
+ %% Do not print that the export includes imported modules
+ ok.
export_info([]) ->
ok;
-export_info(Imported) ->
- AllImportFiles = get_all_importfiles(Imported,[]),
- io:format("Export includes data from imported files\n~p\n",[AllImportFiles]).
+export_info(_Imported) ->
+ %% Do not print that the export includes imported modules
+ ok.
get_all_importfiles([{_M,_F,ImportFiles}|Imported],Acc) ->
NewAcc = do_get_all_importfiles(ImportFiles,Acc),
@@ -1153,14 +1184,14 @@ is_loaded(Module, State) ->
{ok, File} ->
case code:which(Module) of
?TAG -> {loaded, File};
- _ -> unloaded
+ _ -> throw(unloaded)
end;
false ->
case get_file(Module,State#main_state.imported) of
{ok,File,ImportFiles} ->
{imported, File, ImportFiles};
false ->
- false
+ throw(not_loaded)
end
end.
@@ -1259,7 +1290,7 @@ do_compile_beam(Module,Beam) ->
%% Store info about all function clauses in database
InitInfo = reverse(Vars#vars.init_info),
- ets:insert(?COVER_TABLE, {Module, InitInfo}),
+ ets:insert(?COVER_CLAUSE_TABLE, {Module, InitInfo}),
%% Store binary code so it can be loaded on remote nodes
ets:insert(?BINARY_TABLE, {Module, Binary}),
@@ -1793,9 +1824,8 @@ common_elems(L1, L2) ->
%% Collect data for all modules
collect(Nodes) ->
%% local node
- MS = ets:fun2ms(fun({M,C}) when is_atom(M) -> {M,C} end),
- AllClauses = ets:select(?COVER_TABLE,MS),
- move_modules(AllClauses),
+ AllClauses = ets:tab2list(?COVER_CLAUSE_TABLE),
+ pmap(fun move_modules/1,AllClauses),
%% remote nodes
remote_collect('_',Nodes,false).
@@ -1803,7 +1833,7 @@ collect(Nodes) ->
%% Collect data for one module
collect(Module,Clauses,Nodes) ->
%% local node
- move_modules([{Module,Clauses}]),
+ move_modules({Module,Clauses}),
%% remote nodes
remote_collect(Module,Nodes,false).
@@ -1811,12 +1841,9 @@ collect(Module,Clauses,Nodes) ->
%% When analysing, the data from the local ?COVER_TABLE is moved to the
%% ?COLLECTION_TABLE. Resetting data in ?COVER_TABLE
-move_modules([{Module,Clauses}|AllClauses]) ->
- ets:insert(?COLLECTION_TABLE,{Module,Clauses}),
- move_clauses(Clauses),
- move_modules(AllClauses);
-move_modules([]) ->
- ok.
+move_modules({Module,Clauses}) ->
+ ets:insert(?COLLECTION_CLAUSE_TABLE,{Module,Clauses}),
+ move_clauses(Clauses).
move_clauses([{M,F,A,C,_L}|Clauses]) ->
Pattern = {#bump{module=M, function=F, arity=A, clause=C}, '_'},
@@ -1855,6 +1882,22 @@ find_source(File0) ->
end
end.
+do_parallel_analysis(Module, Analysis, Level, Loaded, From, State) ->
+ analyse_info(Module,State#main_state.imported),
+ C = case Loaded of
+ {loaded, _File} ->
+ [{Module,Clauses}] =
+ ets:lookup(?COVER_CLAUSE_TABLE,Module),
+ collect(Module,Clauses,State#main_state.nodes),
+ Clauses;
+ _ ->
+ [{Module,Clauses}] =
+ ets:lookup(?COLLECTION_CLAUSE_TABLE,Module),
+ Clauses
+ end,
+ R = do_analyse(Module, Analysis, Level, C),
+ reply(From, R).
+
%% do_analyse(Module, Analysis, Level, Clauses)-> {ok,Answer} | {error,Error}
%% Clauses = [{Module,Function,Arity,Clause,Lines}]
do_analyse(Module, Analysis, line, _Clauses) ->
@@ -1931,6 +1974,28 @@ merge_functions([{_MFA,R}|Functions], MFun, Result) ->
merge_functions([], _MFun, Result) ->
Result.
+do_parallel_analysis_to_file(Module, OutFile, Opts, Loaded, From, State) ->
+ File = case Loaded of
+ {loaded, File0} ->
+ [{Module,Clauses}] =
+ ets:lookup(?COVER_CLAUSE_TABLE,Module),
+ collect(Module, Clauses,
+ State#main_state.nodes),
+ File0;
+ {imported, File0, _} ->
+ File0
+ end,
+ case find_source(File) of
+ {beam,_BeamFile} ->
+ reply(From, {error,no_source_code_found});
+ ErlFile ->
+ analyse_info(Module,State#main_state.imported),
+ HTML = lists:member(html,Opts),
+ R = do_analyse_to_file(Module,OutFile,
+ ErlFile,HTML),
+ reply(From, R)
+ end.
+
%% do_analyse_to_file(Module,OutFile,ErlFile) -> {ok,OutFile} | {error,Error}
%% Module = atom()
%% OutFile = ErlFile = string()
@@ -2027,6 +2092,42 @@ fill2() -> ".| ".
fill3() -> "| ".
%%%--Export--------------------------------------------------------------
+do_export(Module, OutFile, From, State) ->
+ case file:open(OutFile,[write,binary,raw]) of
+ {ok,Fd} ->
+ Reply =
+ case Module of
+ '_' ->
+ export_info(State#main_state.imported),
+ collect(State#main_state.nodes),
+ do_export_table(State#main_state.compiled,
+ State#main_state.imported,
+ Fd);
+ _ ->
+ export_info(Module,State#main_state.imported),
+ try is_loaded(Module, State) of
+ {loaded, File} ->
+ [{Module,Clauses}] =
+ ets:lookup(?COVER_CLAUSE_TABLE,Module),
+ collect(Module, Clauses,
+ State#main_state.nodes),
+ do_export_table([{Module,File}],[],Fd);
+ {imported, File, ImportFiles} ->
+ %% don't know if I should allow this -
+ %% export a module which is only imported
+ Imported = [{Module,File,ImportFiles}],
+ do_export_table([],Imported,Fd)
+ catch throw:_ ->
+ {error,{not_cover_compiled,Module}}
+ end
+ end,
+ file:close(Fd),
+ reply(From, Reply);
+ {error,Reason} ->
+ reply(From, {error, {cant_open_file,OutFile,Reason}})
+
+ end.
+
do_export_table(Compiled, Imported, Fd) ->
ModList = merge(Imported,Compiled),
write_module_data(ModList,Fd).
@@ -2043,7 +2144,7 @@ merge([],ModuleList) ->
write_module_data([{Module,File}|ModList],Fd) ->
write({file,Module,File},Fd),
- [Clauses] = ets:lookup(?COLLECTION_TABLE,Module),
+ [Clauses] = ets:lookup(?COLLECTION_CLAUSE_TABLE,Module),
write(Clauses,Fd),
ModuleData = ets:match_object(?COLLECTION_TABLE,{#bump{module=Module},'_'}),
do_write_module_data(ModuleData,Fd),
@@ -2093,7 +2194,7 @@ do_import_to_table(Fd,ImportFile,Imported,DontImport) ->
{Module,Clauses} ->
case lists:member(Module,DontImport) of
false ->
- ets:insert(?COLLECTION_TABLE,{Module,Clauses});
+ ets:insert(?COLLECTION_CLAUSE_TABLE,{Module,Clauses});
true ->
ok
end,
@@ -2127,14 +2228,14 @@ do_reset_main_node(Module,Nodes) ->
remote_reset(Module,Nodes).
do_reset_collection_table(Module) ->
- ets:delete(?COLLECTION_TABLE,Module),
+ ets:delete(?COLLECTION_CLAUSE_TABLE,Module),
ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}).
%% do_reset(Module) -> ok
%% The reset is done on a per-clause basis to avoid building
%% long lists in the case of very large modules
do_reset(Module) ->
- [{Module,Clauses}] = ets:lookup(?COVER_TABLE, Module),
+ [{Module,Clauses}] = ets:lookup(?COVER_CLAUSE_TABLE, Module),
do_reset2(Clauses).
do_reset2([{M,F,A,C,_L}|Clauses]) ->
@@ -2149,10 +2250,19 @@ do_reset2([]) ->
ok.
do_clear(Module) ->
- ets:match_delete(?COVER_TABLE, {Module,'_'}),
+ ets:match_delete(?COVER_CLAUSE_TABLE, {Module,'_'}),
ets:match_delete(?COVER_TABLE, {#bump{module=Module},'_'}),
ets:match_delete(?COLLECTION_TABLE, {#bump{module=Module},'_'}).
+not_loaded(Module, unloaded, State) ->
+ do_clear(Module),
+ remote_unload(State#main_state.nodes,[Module]),
+ Compiled = update_compiled([Module],
+ State#main_state.compiled),
+ State#main_state{ compiled = Compiled };
+not_loaded(_Module,_Else, State) ->
+ State.
+
%%%--Div-----------------------------------------------------------------
@@ -2174,7 +2284,36 @@ escape_lt_and_gt1([$<|T],Acc) ->
escape_lt_and_gt1(T,[$;,$t,$l,$&|Acc]);
escape_lt_and_gt1([$>|T],Acc) ->
escape_lt_and_gt1(T,[$;,$t,$g,$&|Acc]);
+escape_lt_and_gt1([$&|T],Acc) ->
+ escape_lt_and_gt1(T,[$;,$p,$m,$a,$&|Acc]);
escape_lt_and_gt1([],Acc) ->
lists:reverse(Acc);
escape_lt_and_gt1([H|T],Acc) ->
escape_lt_and_gt1(T,[H|Acc]).
+
+pmap(Fun, List) ->
+ pmap(Fun, List, 20).
+pmap(Fun, List, Limit) ->
+ pmap(Fun, List, [], Limit, 0, []).
+pmap(Fun, [E | Rest], Pids, Limit, Cnt, Acc) when Cnt < Limit ->
+ Collector = self(),
+ Pid = spawn_link(fun() ->
+ ?SPAWN_DBG(pmap,E),
+ Collector ! {res,self(),Fun(E)}
+ end),
+ erlang:monitor(process, Pid),
+ pmap(Fun, Rest, Pids ++ [Pid], Limit, Cnt + 1, Acc);
+pmap(Fun, List, [Pid | Pids], Limit, Cnt, Acc) ->
+ receive
+ {'DOWN', _Ref, process, _, _} ->
+ pmap(Fun, List, [Pid | Pids], Limit, Cnt - 1, Acc);
+ {res, Pid, Res} ->
+ pmap(Fun, List, Pids, Limit, Cnt, [Res | Acc])
+ end;
+pmap(_Fun, [], [], _Limit, 0, Acc) ->
+ lists:reverse(Acc);
+pmap(Fun, [], [], Limit, Cnt, Acc) ->
+ receive
+ {'DOWN', _Ref, process, _, _} ->
+ pmap(Fun, [], [], Limit, Cnt - 1, Acc)
+ end.
diff --git a/lib/tools/src/eprof.erl b/lib/tools/src/eprof.erl
index b4313d6888..87fdc1fa34 100644
--- a/lib/tools/src/eprof.erl
+++ b/lib/tools/src/eprof.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
%% Purpose: Profile a system in order to figure out where the
@@ -23,456 +23,467 @@
-module(eprof).
-behaviour(gen_server).
--export([start/0, stop/0, dump/0, total_analyse/0,
- start_profiling/1, profile/2, profile/4, profile/1,
- stop_profiling/0, analyse/0, log/1]).
+-export([start/0,
+ stop/0,
+ dump/0,
+ start_profiling/1, start_profiling/2,
+ profile/1, profile/2, profile/3, profile/4, profile/5,
+ stop_profiling/0,
+ analyze/0, analyze/1, analyze/2,
+ log/1]).
%% Internal exports
-export([init/1,
- call/4,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
+-record(bpd, {
+ n = 0, % number of total calls
+ us = 0, % sum of uS for all calls
+ p = gb_trees:empty(), % tree of {Pid, {Mfa, {Count, Us}}}
+ mfa = [] % list of {Mfa, {Count, Us}}
+ }).
+
+-record(state, {
+ profiling = false,
+ pattern = {'_','_','_'},
+ rootset = [],
+ fd = undefined,
+ start_ts = undefined,
+ reply = undefined,
+ bpd = #bpd{}
+ }).
+
+
+
+%% -------------------------------------------------------------------- %%
+%%
+%% API
+%%
+%% -------------------------------------------------------------------- %%
--include_lib("stdlib/include/qlc.hrl").
-
--import(lists, [flatten/1,reverse/1,keysort/2]).
-
-
--record(state, {table = notable,
- proc = noproc,
- profiling = false,
- pfunc = undefined,
- pop = running,
- ptime = 0,
- overhead = 0,
- rootset = []}).
-
-%%%%%%%%%%%%%%
-
-start() -> gen_server:start({local, eprof}, eprof, [], []).
-stop() -> gen_server:call(eprof, stop, infinity).
+start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []).
+stop() -> gen_server:call(?MODULE, stop, infinity).
+profile(Fun) when is_function(Fun) ->
+ profile([], Fun);
+profile(Rs) when is_list(Rs) ->
+ start_profiling(Rs).
profile(Pids, Fun) ->
- start(),
- gen_server:call(eprof, {profile,Pids,erlang,apply,[Fun,[]]},infinity).
+ profile(Pids, Fun, {'_','_','_'}).
+
+profile(Pids, Fun, Pattern) ->
+ profile(Pids, erlang, apply, [Fun,[]], Pattern).
profile(Pids, M, F, A) ->
+ profile(Pids, M, F, A, {'_','_','_'}).
+
+profile(Pids, M, F, A, Pattern) ->
start(),
- gen_server:call(eprof, {profile,Pids,M,F,A},infinity).
+ gen_server:call(?MODULE, {profile,Pids,Pattern,M,F,A},infinity).
dump() ->
- gen_server:call(eprof, dump, infinity).
+ gen_server:call(?MODULE, dump, infinity).
-analyse() ->
- gen_server:call(eprof, analyse, infinity).
+analyze() ->
+ analyze(procs).
-log(File) ->
- gen_server:call(eprof, {logfile, File}, infinity).
+analyze(Type) when is_atom(Type) ->
+ analyze(Type, []);
+analyze(Opts) when is_list(Opts) ->
+ analyze(procs, Opts).
+analyze(Type, Opts) when is_list(Opts) ->
+ gen_server:call(?MODULE, {analyze, Type, Opts}, infinity).
-total_analyse() ->
- gen_server:call(eprof, total_analyse, infinity).
+log(File) ->
+ gen_server:call(?MODULE, {logfile, File}, infinity).
start_profiling(Rootset) ->
+ start_profiling(Rootset, {'_','_','_'}).
+start_profiling(Rootset, Pattern) ->
start(),
- gen_server:call(eprof, {profile, Rootset}, infinity).
+ gen_server:call(?MODULE, {profile, Rootset, Pattern}, infinity).
stop_profiling() ->
- gen_server:call(eprof, stop_profiling, infinity).
+ gen_server:call(?MODULE, stop_profiling, infinity).
-profile(Rs) ->
- start_profiling(Rs).
-%%%%%%%%%%%%%%%%
+%% -------------------------------------------------------------------- %%
+%%
+%% init
+%%
+%% -------------------------------------------------------------------- %%
-init(_) ->
+init([]) ->
process_flag(trap_exit, true),
- process_flag(priority, max),
- put(three_one, {3,1}), %To avoid building garbage.
{ok, #state{}}.
-subtr({X1,Y1,Z1}, {X1,Y1,Z2}) ->
- Z1 - Z2;
-subtr({X1,Y1,Z1}, {X2,Y2,Z2}) ->
- (((X1-X2) * 1000000) + Y1 - Y2) * 1000000 + Z1 - Z2.
+%% -------------------------------------------------------------------- %%
+%%
+%% handle_call
+%%
+%% -------------------------------------------------------------------- %%
-update_call_statistics(Tab, Key, Time) ->
- try ets:update_counter(Tab, Key, Time) of
- NewTime when is_integer(NewTime) ->
- ets:update_counter(Tab, Key, get(three_one))
- catch
- error:badarg ->
- ets:insert(Tab, {Key,Time,1})
- end.
+%% analyze
-update_other_statistics(Tab, Key, Time) ->
- try
- ets:update_counter(Tab, Key, Time)
- catch
- error:badarg ->
- ets:insert(Tab, {Key,Time,0})
- end.
+handle_call({analyze, _, _}, _, #state{ bpd = #bpd{ p = {0,nil}, us = 0, n = 0} = Bpd } = S) when is_record(Bpd, bpd) ->
+ {reply, nothing_to_analyze, S};
-do_messages({trace_ts,From,Op,Mfa,Time}, Tab, undefined,_PrevOp0,_PrevTime0) ->
- PrevFunc = [From|Mfa],
- receive
- {trace_ts,_,_,_,_}=Ts -> do_messages(Ts, Tab, PrevFunc, Op, Time)
- after 0 ->
- {PrevFunc,Op,Time}
- end;
-do_messages({trace_ts,From,Op,Mfa,Time}, Tab, PrevFunc0, call, PrevTime0) ->
- update_call_statistics(Tab, PrevFunc0, subtr(Time, PrevTime0)),
- PrevFunc = case Op of
- exit -> undefined;
- out -> undefined;
- _ -> [From|Mfa]
- end,
- receive
- {trace_ts,_,_,_,_}=Ts -> do_messages(Ts, Tab, PrevFunc, Op, Time)
- after 0 ->
- {PrevFunc,Op,Time}
- end;
-do_messages({trace_ts,From,Op,Mfa,Time}, Tab, PrevFunc0, _PrevOp0, PrevTime0) ->
- update_other_statistics(Tab, PrevFunc0, subtr(Time, PrevTime0)),
- PrevFunc = case Op of
- exit -> undefined;
- out -> undefined;
- _ -> [From|Mfa]
- end,
- receive
- {trace_ts,_,_,_,_}=Ts -> do_messages(Ts, Tab, PrevFunc, Op, Time)
- after 0 ->
- {PrevFunc,Op,Time}
- end.
+handle_call({analyze, procs, Opts}, _, #state{ bpd = #bpd{ p = Ps, us = Tus} = Bpd, fd = Fd} = S) when is_record(Bpd, bpd) ->
+ lists:foreach(fun
+ ({Pid, Mfas}) ->
+ {Pn, Pus} = sum_bp_total_n_us(Mfas),
+ format(Fd, "~n****** Process ~w -- ~s % of profiled time *** ~n", [Pid, s("~.2f", [100.0*divide(Pus,Tus)])]),
+ print_bp_mfa(Mfas, {Pn,Pus}, Fd, Opts),
+ ok
+ end, gb_trees:to_list(Ps)),
+ {reply, ok, S};
-%%%%%%%%%%%%%%%%%%
+handle_call({analyze, total, Opts}, _, #state{ bpd = #bpd{ mfa = Mfas, n = Tn, us = Tus} = Bpd, fd = Fd} = S) when is_record(Bpd, bpd) ->
+ print_bp_mfa(Mfas, {Tn, Tus}, Fd, Opts),
+ {reply, ok, S};
-handle_cast(_Req, S) -> {noreply, S}.
+handle_call({analyze, Type, _Opts}, _, S) ->
+ {reply, {error, {undefined, Type}}, S};
-terminate(_Reason,_S) ->
- call_trace_for_all(false),
- normal.
+%% profile
-%%%%%%%%%%%%%%%%%%
+handle_call({profile, _Rootset, _Pattern, _M,_F,_A}, _From, #state{ profiling = true } = S) ->
+ {reply, {error, already_profiling}, S};
-handle_call({logfile, F}, _FromTag, Status) ->
- case file:open(F, [write]) of
- {ok, Fd} ->
- case get(fd) of
- undefined -> ok;
- FdOld -> file:close(FdOld)
- end,
- put(fd, Fd),
- {reply, ok, Status};
- {error, _} ->
- {reply, error, Status}
- end;
+handle_call({profile, Rootset, Pattern, M,F,A}, From, #state{fd = Fd } = S) ->
-handle_call({profile, Rootset}, {From, _Tag}, S) ->
- link(From),
- maybe_delete(S#state.table),
- io:format("eprof: Starting profiling ..... ~n",[]),
- ptrac(S#state.rootset, false, all()),
- flush_receive(),
- Tab = ets:new(eprof, [set, public]),
- case ptrac(Rootset, true, all()) of
- false ->
- {reply, error, #state{}};
+ set_pattern_trace(false, S#state.pattern),
+ set_process_trace(false, S#state.rootset),
+
+ Pid = setup_profiling(M,F,A),
+ case set_process_trace(true, [Pid|Rootset]) of
true ->
- uni_schedule(),
- call_trace_for_all(true),
- erase(replyto),
- {reply, profiling, #state{table = Tab,
- proc = From,
- profiling = true,
- rootset = Rootset}}
+ set_pattern_trace(true, Pattern),
+ T0 = now(),
+ execute_profiling(Pid),
+ {noreply, #state{
+ profiling = true,
+ rootset = [Pid|Rootset],
+ start_ts = T0,
+ reply = From,
+ fd = Fd,
+ pattern = Pattern
+ }};
+ false ->
+ exit(Pid, eprof_kill),
+ {reply, error, #state{ fd = Fd}}
end;
-handle_call(stop_profiling, _FromTag, S) when S#state.profiling ->
- ptrac(S#state.rootset, false, all()),
- call_trace_for_all(false),
- multi_schedule(),
- io:format("eprof: Stop profiling~n",[]),
- ets:delete(S#state.table, nofunc),
- {reply, profiling_stopped, S#state{profiling = false}};
+handle_call({profile, _Rootset, _Pattern}, _From, #state{ profiling = true } = S) ->
+ {reply, {error, already_profiling}, S};
-handle_call(stop_profiling, _FromTag, S) ->
- {reply, profiling_already_stopped, S};
+handle_call({profile, Rootset, Pattern}, From, #state{ fd = Fd } = S) ->
+
+ set_pattern_trace(false, S#state.pattern),
+ set_process_trace(false, S#state.rootset),
-handle_call({profile, Rootset, M, F, A}, FromTag, S) ->
- io:format("eprof: Starting profiling..... ~n", []),
- maybe_delete(S#state.table),
- ptrac(S#state.rootset, false, all()),
- flush_receive(),
- put(replyto, FromTag),
- Tab = ets:new(eprof, [set, public]),
- P = spawn_link(eprof, call, [self(), M, F, A]),
- case ptrac([P|Rootset], true, all()) of
+ case set_process_trace(true, Rootset) of
true ->
- uni_schedule(),
- call_trace_for_all(true),
- P ! {self(),go},
- {noreply, #state{table = Tab,
- profiling = true,
- rootset = [P|Rootset]}};
+ T0 = now(),
+ set_pattern_trace(true, Pattern),
+ {reply, profiling, #state{
+ profiling = true,
+ rootset = Rootset,
+ start_ts = T0,
+ reply = From,
+ fd = Fd,
+ pattern = Pattern
+ }};
false ->
- exit(P, kill),
- erase(replyto),
- {reply, error, #state{}}
+ {reply, error, #state{ fd = Fd }}
end;
-handle_call(dump, _FromTag, S) ->
- {reply, dump(S#state.table), S};
-
-handle_call(analyse, _FromTag, S) ->
- {reply, analyse(S), S};
+handle_call(stop_profiling, _From, #state{ profiling = false } = S) ->
+ {reply, profiling_already_stopped, S};
-handle_call(total_analyse, _FromTag, S) ->
- {reply, total_analyse(S), S};
+handle_call(stop_profiling, _From, #state{ profiling = true } = S) ->
-handle_call(stop, _FromTag, S) ->
- multi_schedule(),
- {stop, normal, stopped, S}.
+ set_pattern_trace(pause, S#state.pattern),
-%%%%%%%%%%%%%%%%%%%
+ Bpd = collect_bpd(),
-handle_info({trace_ts,_From,_Op,_Func,_Time}=M, S0) when S0#state.profiling ->
- Start = erlang:now(),
- #state{table=Tab,pop=PrevOp0,ptime=PrevTime0,pfunc=PrevFunc0,
- overhead=Overhead0} = S0,
- {PrevFunc,PrevOp,PrevTime} = do_messages(M, Tab, PrevFunc0, PrevOp0, PrevTime0),
- Overhead = Overhead0 + subtr(erlang:now(), Start),
- S = S0#state{overhead=Overhead,pfunc=PrevFunc,pop=PrevOp,ptime=PrevTime},
- {noreply,S};
+ set_process_trace(false, S#state.rootset),
+ set_pattern_trace(false, S#state.pattern),
-handle_info({trace_ts, From, _, _, _}, S) when not S#state.profiling ->
- ptrac([From], false, all()),
- {noreply, S};
+ {reply, profiling_stopped, S#state{
+ profiling = false,
+ rootset = [],
+ pattern = {'_','_','_'},
+ bpd = Bpd
+ }};
-handle_info({_P, {answer, A}}, S) ->
- ptrac(S#state.rootset, false, all()),
- io:format("eprof: Stop profiling~n",[]),
- {From,_Tag} = get(replyto),
- catch unlink(From),
- ets:delete(S#state.table, nofunc),
- gen_server:reply(erase(replyto), {ok, A}),
- multi_schedule(),
- {noreply, S#state{profiling = false,
- rootset = []}};
-
-handle_info({'EXIT', P, Reason},
- #state{profiling=true,proc=P,table=T,rootset=RootSet}) ->
- maybe_delete(T),
- ptrac(RootSet, false, all()),
- multi_schedule(),
- io:format("eprof: Profiling failed\n",[]),
- case erase(replyto) of
- undefined ->
- {noreply, #state{}};
- FromTag ->
- gen_server:reply(FromTag, {error, Reason}),
- {noreply, #state{}}
+%% logfile
+handle_call({logfile, File}, _From, #state{ fd = OldFd } = S) ->
+ case file:open(File, [write]) of
+ {ok, Fd} ->
+ case OldFd of
+ undefined -> ok;
+ OldFd -> file:close(OldFd)
+ end,
+ {reply, ok, S#state{ fd = Fd}};
+ Error ->
+ {reply, Error, S}
end;
-handle_info({'EXIT',_P,_Reason}, S) ->
- {noreply, S}.
+handle_call(dump, _From, #state{ bpd = Bpd } = S) when is_record(Bpd, bpd) ->
+ {reply, gb_trees:to_list(Bpd#bpd.p), S};
-uni_schedule() ->
- erlang:system_flag(multi_scheduling, block).
+handle_call(stop, _FromTag, S) ->
+ {stop, normal, stopped, S}.
-multi_schedule() ->
- erlang:system_flag(multi_scheduling, unblock).
+%% -------------------------------------------------------------------- %%
+%%
+%% handle_cast
+%%
+%% -------------------------------------------------------------------- %%
-%%%%%%%%%%%%%%%%%%
+handle_cast(_Msg, State) ->
+ {noreply, State}.
-call(Top, M, F, A) ->
- receive
- {Top,go} ->
- Top ! {self(), {answer, apply(M,F,A)}}
- end.
+%% -------------------------------------------------------------------- %%
+%%
+%% handle_info
+%%
+%% -------------------------------------------------------------------- %%
-call_trace_for_all(Flag) ->
- erlang:trace_pattern(on_load, Flag, [local]),
- erlang:trace_pattern({'_','_','_'}, Flag, [local]).
+handle_info({'EXIT', _, normal}, S) ->
+ {noreply, S};
+handle_info({'EXIT', _, eprof_kill}, S) ->
+ {noreply, S};
+handle_info({'EXIT', _, Reason}, #state{ reply = FromTag } = S) ->
-ptrac([P|T], How, Flags) when is_pid(P) ->
- case dotrace(P, How, Flags) of
- true ->
- ptrac(T, How, Flags);
- false when How ->
- false;
- false ->
- ptrac(T, How, Flags)
- end;
+ set_process_trace(false, S#state.rootset),
+ set_pattern_trace(false, S#state.pattern),
-ptrac([P|T], How, Flags) when is_atom(P) ->
- case whereis(P) of
- undefined when How ->
- false;
- undefined when not How ->
- ptrac(T, How, Flags);
- Pid ->
- ptrac([Pid|T], How, Flags)
- end;
+ gen_server:reply(FromTag, {error, Reason}),
+ {noreply, S#state{
+ profiling = false,
+ rootset = [],
+ pattern = {'_','_','_'}
+ }};
-ptrac([H|_],_How,_Flags) ->
- io:format("** eprof bad process ~w~n",[H]),
- false;
+% check if Pid is spawned process?
+handle_info({_Pid, {answer, Result}}, #state{ reply = {From,_} = FromTag} = S) ->
-ptrac([],_,_) -> true.
+ set_pattern_trace(pause, S#state.pattern),
-dotrace(P, How, What) ->
- case (catch erlang:trace(P, How, What)) of
- 1 ->
- true;
- _Other when not How ->
- true;
- _Other ->
- io:format("** eprof: bad process: ~p,~p,~p~n", [P,How,What]),
- false
- end.
+ Bpd = collect_bpd(),
-all() -> [call,arity,return_to,running,timestamp,set_on_spawn].
-
-total_analyse(#state{table=notable}) ->
- nothing_to_analyse;
-total_analyse(S) ->
- #state{table = T, overhead = Overhead} = S,
- QH = qlc:q([{{From,Mfa},Time,Count} ||
- {[From|Mfa],Time,Count} <- ets:table(T)]),
- Pcalls = reverse(keysort(2, replicas(qlc:eval(QH)))),
- Time = collect_times(Pcalls),
- format("FUNCTION~44s TIME ~n", ["CALLS"]),
- printit(Pcalls, Time),
- format("\nTotal time: ~.2f\n", [Time / 1000000]),
- format("Measurement overhead: ~.2f\n", [Overhead / 1000000]).
-
-analyse(#state{table=notable}) ->
- nothing_to_analyse;
-analyse(S) ->
- #state{table = T, overhead = Overhead} = S,
- Pids = ordsets:from_list(flatten(ets:match(T, {['$1'|'_'],'_', '_'}))),
- Times = sum(ets:match(T, {'_','$1', '_'})),
- format("FUNCTION~44s TIME ~n", ["CALLS"]),
- do_pids(Pids, T, 0, Times),
- format("\nTotal time: ~.2f\n", [Times / 1000000]),
- format("Measurement overhead: ~.2f\n", [Overhead / 1000000]).
-
-do_pids([Pid|Tail], T, AckTime, Total) ->
- Pcalls =
- reverse(keysort(2, to_tups(ets:match(T, {[Pid|'$1'], '$2','$3'})))),
- Time = collect_times(Pcalls),
- PercentTotal = 100 * (divide(Time, Total)),
- format("~n****** Process ~w -- ~s % of profiled time *** ~n",
- [Pid, fpf(PercentTotal)]),
- printit(Pcalls, Time),
- do_pids(Tail, T, AckTime + Time, Total);
-do_pids([], _, _, _) ->
- ok.
+ set_process_trace(false, S#state.rootset),
+ set_pattern_trace(false, S#state.pattern),
-printit([],_) -> ok;
-printit([{{Mod,Fun,Arity}, Time, Calls} |Tail], ProcTime) ->
- format("~s ~s ~s % ~n", [ff(Mod,Fun,Arity), fint(Calls),
- fpf(100*(divide(Time,ProcTime)))]),
- printit(Tail, ProcTime);
-printit([{{_,{Mod,Fun,Arity}}, Time, Calls} |Tail], ProcTime) ->
- format("~s ~s ~s % ~n", [ff(Mod,Fun,Arity), fint(Calls),
- fpf(100*(divide(Time,ProcTime)))]),
- printit(Tail, ProcTime);
-printit([_|T], Time) ->
- printit(T, Time).
-
-ff(Mod,Fun,Arity) ->
- pad(flatten(io_lib:format("~w:~w/~w", [Mod,Fun, Arity])),45).
-
-pad(Str, Len) ->
- Strlen = length(Str),
- if
- Strlen > Len -> strip_tail(Str, 45);
- true -> lists:append(Str, mklist(Len-Strlen))
- end.
+ catch unlink(From),
+ gen_server:reply(FromTag, {ok, Result}),
+ {noreply, S#state{
+ profiling = false,
+ rootset = [],
+ pattern = {'_','_','_'},
+ bpd = Bpd
+ }}.
+
+%% -------------------------------------------------------------------- %%
+%%
+%% termination
+%%
+%% -------------------------------------------------------------------- %%
+
+terminate(_Reason, #state{ fd = undefined }) ->
+ set_pattern_trace(false, {'_','_','_'}),
+ ok;
+terminate(_Reason, #state{ fd = Fd }) ->
+ file:close(Fd),
+ set_pattern_trace(false, {'_','_','_'}),
+ ok.
-strip_tail([_|_], 0) ->[];
-strip_tail([H|T], I) -> [H|strip_tail(T, I-1)];
-strip_tail([],_I) -> [].
+%% -------------------------------------------------------------------- %%
+%%
+%% code_change
+%%
+%% -------------------------------------------------------------------- %%
-fpf(F) -> strip_tail(flatten(io_lib:format("~w", [round(F)])), 5).
-fint(Int) -> pad(flatten(io_lib:format("~w",[Int])), 10).
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
-mklist(0) -> [];
-mklist(I) -> [$ |mklist(I-1)].
-to_tups(L) -> lists:map(fun(List) -> erlang:list_to_tuple(List) end, L).
+%% -------------------------------------------------------------------- %%
+%%
+%% AUX Functions
+%%
+%% -------------------------------------------------------------------- %%
-divide(X,Y) -> X / Y.
+setup_profiling(M,F,A) ->
+ spawn_link(fun() -> spin_profile(M,F,A) end).
-collect_times([]) -> 0;
-collect_times([Tup|Tail]) -> element(2, Tup) + collect_times(Tail).
+spin_profile(M, F, A) ->
+ receive
+ {Pid, execute} ->
+ Pid ! {self(), {answer, erlang:apply(M,F,A)}}
+ end.
-dump(T) ->
- L = ets:tab2list(T),
- format(L).
+execute_profiling(Pid) ->
+ Pid ! {self(), execute}.
-format([H|T]) ->
- format("~p~n", [H]), format(T);
-format([]) -> ok.
+set_pattern_trace(Flag, Pattern) ->
+ erlang:system_flag(multi_scheduling, block),
+ erlang:trace_pattern(on_load, Flag, [call_time]),
+ erlang:trace_pattern(Pattern, Flag, [call_time]),
+ erlang:system_flag(multi_scheduling, unblock),
+ ok.
-format(F, A) ->
- io:format(F,A),
- case get(fd) of
- undefined -> ok;
- Fd -> io:format(Fd, F,A)
+set_process_trace(Flag, Pids) ->
+ % do we need procs for meta info?
+ % could be useful
+ set_process_trace(Flag, Pids, [call, set_on_spawn]).
+set_process_trace(_, [], _) -> true;
+set_process_trace(Flag, [Pid|Pids], Options) when is_pid(Pid) ->
+ try
+ erlang:trace(Pid, Flag, Options),
+ set_process_trace(Flag, Pids, Options)
+ catch
+ _:_ ->
+ false
+ end;
+set_process_trace(Flag, [Name|Pids], Options) when is_atom(Name) ->
+ case whereis(Name) of
+ undefined ->
+ set_process_trace(Flag, Pids, Options);
+ Pid ->
+ set_process_trace(Flag, [Pid|Pids], Options)
end.
-maybe_delete(T) ->
- catch ets:delete(T).
+collect_bpd() ->
+ collect_bpd([M || M <- [element(1, Mi) || Mi <- code:all_loaded()], M =/= ?MODULE]).
+
+collect_bpd(Ms) when is_list(Ms) ->
+ collect_bpdf(collect_mfas(Ms) ++ erlang:system_info(snifs)).
+
+collect_mfas(Ms) ->
+ lists:foldl(fun
+ (M, Mfas) ->
+ Mfas ++ [{M, F, A} || {F, A} <- M:module_info(functions)]
+ end, [], Ms).
+
+collect_bpdf(Mfas) ->
+ collect_bpdf(Mfas, #bpd{}).
+collect_bpdf([], Bpd) ->
+ Bpd;
+collect_bpdf([Mfa|Mfas], #bpd{n = N, us = Us, p = Tree, mfa = Code } = Bpd) ->
+ case erlang:trace_info(Mfa, call_time) of
+ {call_time, []} ->
+ collect_bpdf(Mfas, Bpd);
+ {call_time, Data} when is_list(Data) ->
+ {CTn, CTus, CTree} = collect_bpdfp(Mfa, Tree, Data),
+ collect_bpdf(Mfas, Bpd#bpd{
+ n = CTn + N,
+ us = CTus + Us,
+ p = CTree,
+ mfa = [{Mfa, {CTn, CTus}}|Code]
+ });
+ {call_time, false} ->
+ collect_bpdf(Mfas, Bpd);
+ {call_time, _Other} ->
+ collect_bpdf(Mfas, Bpd)
+ end.
-sum([[H]|T]) -> H + sum(T);
-sum([]) -> 0.
+collect_bpdfp(Mfa, Tree, Data) ->
+ lists:foldl(fun
+ ({Pid, Ni, Si, Usi}, {PTno, PTuso, To}) ->
+ Time = Si * 1000000 + Usi,
+ Ti1 = case gb_trees:lookup(Pid, To) of
+ none ->
+ gb_trees:enter(Pid, [{Mfa, {Ni, Time}}], To);
+ {value, Pmfas} ->
+ gb_trees:enter(Pid, [{Mfa, {Ni, Time}}|Pmfas], To)
+ end,
+ {PTno + Ni, PTuso + Time, Ti1}
+ end, {0,0, Tree}, Data).
+
+%% manipulators
+sort_mfa(Bpfs, mfa) when is_list(Bpfs) ->
+ lists:sort(fun
+ ({A,_}, {B,_}) when A < B -> true;
+ (_, _) -> false
+ end, Bpfs);
+sort_mfa(Bpfs, time) when is_list(Bpfs) ->
+ lists:sort(fun
+ ({_,{_,A}}, {_,{_,B}}) when A < B -> true;
+ (_, _) -> false
+ end, Bpfs);
+sort_mfa(Bpfs, calls) when is_list(Bpfs) ->
+ lists:sort(fun
+ ({_,{A,_}}, {_,{B,_}}) when A < B -> true;
+ (_, _) -> false
+ end, Bpfs);
+sort_mfa(Bpfs, _) when is_list(Bpfs) -> sort_mfa(Bpfs, time).
+
+filter_mfa(Bpfs, Ts) when is_list(Ts) ->
+ filter_mfa(Bpfs, [], proplists:get_value(calls, Ts, 0), proplists:get_value(time, Ts, 0));
+filter_mfa(Bpfs, _) -> Bpfs.
+filter_mfa([], Out, _, _) -> lists:reverse(Out);
+filter_mfa([{_, {C, T}}=Bpf|Bpfs], Out, Ct, Tt) when C >= Ct, T >= Tt -> filter_mfa(Bpfs, [Bpf|Out], Ct, Tt);
+filter_mfa([_|Bpfs], Out, Ct, Tt) -> filter_mfa(Bpfs, Out, Ct, Tt).
+
+sum_bp_total_n_us(Mfas) ->
+ lists:foldl(fun ({_, {Ci,Usi}}, {Co, Uso}) -> {Co + Ci, Uso + Usi} end, {0,0}, Mfas).
+
+%% strings and format
+
+string_bp_mfa(Mfas, Tus) -> string_bp_mfa(Mfas, Tus, {0,0,0,0,0}, []).
+string_bp_mfa([], _, Ws, Strings) -> {Ws, lists:reverse(Strings)};
+string_bp_mfa([{Mfa, {Count, Time}}|Mfas], Tus, {MfaW, CountW, PercW, TimeW, TpCW}, Strings) ->
+ Smfa = s(Mfa),
+ Scount = s(Count),
+ Stime = s(Time),
+ Sperc = s("~.2f", [100*divide(Time,Tus)]),
+ Stpc = s("~.2f", [divide(Time,Count)]),
+
+ string_bp_mfa(Mfas, Tus, {
+ erlang:max(MfaW, length(Smfa)),
+ erlang:max(CountW,length(Scount)),
+ erlang:max(PercW, length(Sperc)),
+ erlang:max(TimeW, length(Stime)),
+ erlang:max(TpCW, length(Stpc))
+ }, [[Smfa, Scount, Sperc, Stime, Stpc] | Strings]).
+
+print_bp_mfa(Mfas, {_Tn, Tus}, Fd, Opts) ->
+ Fmfas = filter_mfa(sort_mfa(Mfas, proplists:get_value(sort, Opts)), proplists:get_value(filter, Opts)),
+ {{MfaW, CountW, PercW, TimeW, TpCW}, Strs} = string_bp_mfa(Fmfas, Tus),
+ Ws = {
+ erlang:max(length("FUNCTION"), MfaW),
+ erlang:max(length("CALLS"), CountW),
+ erlang:max(length(" %"), PercW),
+ erlang:max(length("TIME"), TimeW),
+ erlang:max(length("uS / CALLS"), TpCW)
+ },
+ format(Fd, Ws, ["FUNCTION", "CALLS", " %", "TIME", "uS / CALLS"]),
+ format(Fd, Ws, ["--------", "-----", "---", "----", "----------"]),
+
+ lists:foreach(fun (String) -> format(Fd, Ws, String) end, Strs),
+ ok.
-replicas(L) ->
- replicas(L, []).
+s({M,F,A}) -> s("~w:~w/~w",[M,F,A]);
+s(Term) -> s("~p", [Term]).
+s(Format, Terms) -> lists:flatten(io_lib:format(Format, Terms)).
-replicas([{{Pid, {Mod,Fun,Arity}}, Ack,Calls} |Tail], Result) ->
- case search({Mod,Fun,Arity},Result) of
- false ->
- replicas(Tail, [{{Pid, {Mod,Fun,Arity}}, Ack,Calls} |Result]);
- {Ack2, Calls2} ->
- Result2 = del({Mod,Fun,Arity}, Result),
- replicas(Tail, [{{Pid, {Mod,Fun,Arity}},
- Ack+Ack2,Calls+Calls2} |Result2])
- end;
-replicas([_|T], Ack) -> %% Whimpy
- replicas(T, Ack);
-
-replicas([], Res) -> Res.
-
-search(Key, [{{_,Key}, Ack, Calls}|_]) ->
- {Ack, Calls};
-search(Key, [_|T]) ->
- search(Key, T);
-search(_Key,[]) -> false.
-
-del(Key, [{{_,Key},_Ack,_Calls}|T]) ->
- T;
-del(Key, [H | Tail]) ->
- [H|del(Key, Tail)];
-del(_Key,[]) -> [].
-
-flush_receive() ->
- receive
- {trace_ts, From, _, _, _} when is_pid(From) ->
- ptrac([From], false, all()),
- flush_receive();
- _ ->
- flush_receive()
- after 0 ->
- ok
- end.
+format(Fd, {MfaW, CountW, PercW, TimeW, TpCW}, Strings) ->
+ format(Fd, s("~~.~ps ~~~ps ~~~ps ~~~ps [~~~ps]~~n", [MfaW, CountW, PercW, TimeW, TpCW]), Strings);
+format(undefined, Format, Strings) ->
+ io:format(Format, Strings),
+ ok;
+format(Fd, Format, Strings) ->
+ io:format(Fd, Format, Strings),
+ io:format(Format, Strings),
+ ok.
-code_change(_OldVsn, State, _Extra) ->
- {ok,State}.
+divide(_,0) -> 0.0;
+divide(T,N) -> T/N.
diff --git a/lib/tools/src/xref_base.erl b/lib/tools/src/xref_base.erl
index d0dbf4a2b4..93f0e9c0c8 100644
--- a/lib/tools/src/xref_base.erl
+++ b/lib/tools/src/xref_base.erl
@@ -1,24 +1,26 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(xref_base).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([new/0, new/1, delete/1,
add_directory/2, add_directory/3,
add_module/2, add_module/3,
@@ -29,7 +31,7 @@
add_release/2, add_release/3,
get_library_path/1, set_library_path/2, set_library_path/3,
set_up/1, set_up/2,
- q/2, q/3, info/1, info/2, info/3, update/1, update/2,
+ q/2, q/3, info/1, info/2, info/3, update/1, update/2,
forget/1, forget/2, variables/1, variables/2,
analyze/2, analyze/3, analysis/1,
get_default/2, set_default/3,
@@ -38,14 +40,14 @@
-export([format_error/1]).
%% The following functions are exported for testing purposes only:
--export([do_add_module/4, do_add_application/2, do_add_release/2,
+-export([do_add_module/4, do_add_application/2, do_add_release/2,
do_remove_module/2]).
--import(lists,
- [filter/2, flatten/1, foldl/3, keysearch/3, map/2, mapfoldl/3,
- member/2, reverse/1, sort/1, usort/1]).
+-import(lists,
+ [filter/2, flatten/1, foldl/3, foreach/2, keysearch/3, map/2,
+ mapfoldl/3, member/2, reverse/1, sort/1, usort/1]).
--import(sofs,
+-import(sofs,
[constant_function/2, converse/1, difference/2, domain/1,
empty_set/0, family/1, family_difference/2, intersection/2,
family_projection/2, family_to_relation/1, family_union/1,
@@ -103,12 +105,12 @@ delete(State) ->
ok
end
end,
- map(Fun, dict:to_list(State#xref.variables)),
+ foreach(Fun, dict:to_list(State#xref.variables)),
ok.
add_directory(State, Dir) ->
add_directory(State, Dir, []).
-
+
%% -> {ok, Modules, NewState} | Error
add_directory(State, Dir, Options) ->
ValOptions = option_values([builtins, recurse, verbose, warnings], State),
@@ -277,7 +279,7 @@ q(S, Q, Options) when is_atom(Q) ->
q(S, atom_to_list(Q), Options);
q(S, Q, Options) ->
case xref_utils:is_string(Q, 1) of
- true ->
+ true ->
case set_up(S, Options) of
{ok, S1} ->
case xref_compiler:compile(Q, S1#xref.variables) of
@@ -336,7 +338,7 @@ forget(State, Variable) when is_atom(Variable) ->
forget(State, Variables) ->
Vars = State#xref.variables,
do_forget(Variables, Vars, Variables, State).
-
+
variables(State) ->
variables(State, [user]).
@@ -350,9 +352,9 @@ variables(State, Options) ->
{ok, NewState} ->
{U, P} = do_variables(NewState),
R1 = if User -> [{user, U}]; true -> [] end,
- R = if
- Predef -> [{predefined,P} | R1];
- true -> R1
+ R = if
+ Predef -> [{predefined,P} | R1];
+ true -> R1
end,
{{ok, R}, NewState};
Error ->
@@ -368,7 +370,7 @@ analyze(State, Analysis) ->
%% -> {{ok, Answer}, NewState} | {Error, NewState}
analyze(State, Analysis, Options) ->
case analysis(Analysis, State#xref.mode) of
- P when is_list(P) ->
+ P when is_list(P) ->
q(State, P, Options);
error ->
R = case analysis(Analysis, functions) of
@@ -461,7 +463,7 @@ get_default(State, Option) ->
%% -> [{Option, Value}]
get_default(State) ->
- Fun = fun(O) -> V = current_default(State, O), {O, V} end,
+ Fun = fun(O) -> V = current_default(State, O), {O, V} end,
map(Fun, [builtins, recurse, verbose, warnings]).
%% -> {ok, NewState} -> Error
@@ -478,7 +480,7 @@ set_default(State, Options) ->
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({invalid_options, Options}) ->
- io_lib:format("Unknown option(s) or invalid option value(s): ~p~n",
+ io_lib:format("Unknown option(s) or invalid option value(s): ~p~n",
[Options]);
format_error({invalid_filename, Term}) ->
io_lib:format("A file name (a string) was expected: ~p~n", [Term]);
@@ -540,7 +542,7 @@ updated_modules(State) ->
case xref_utils:file_info(File) of
{ok, {_, file, readable, MTime}} when MTime =/= RTime ->
[{M,File} | L];
- _Else ->
+ _Else ->
L
end
end,
@@ -591,7 +593,7 @@ do_add_release(Dir, RelName, OB, OV, OW, State) ->
case xref_utils:release_directory(Dir, true, "ebin") of
{ok, ReleaseDirName, ApplDir, Dirs} ->
ApplDirs = xref_utils:select_last_application_version(Dirs),
- Release = case RelName of
+ Release = case RelName of
[[]] -> ReleaseDirName;
[Name] -> Name
end,
@@ -615,7 +617,7 @@ do_add_release(S, XRel) ->
end.
add_rel_appls([ApplDir | ApplDirs], Release, OB, OV, OW, State) ->
- {ok, _AppName, NewState} =
+ {ok, _AppName, NewState} =
add_appldir(ApplDir, Release, [[]], OB, OV, OW, State),
add_rel_appls(ApplDirs, Release, OB, OV, OW, NewState);
add_rel_appls([], [Release], _OB, _OV, _OW, NewState) ->
@@ -637,10 +639,10 @@ add_appldir(ApplDir, Release, Name, OB, OV, OW, OldState) ->
[[]] -> AppName0;
[N] -> N
end,
- AppInfo = #xref_app{name = AppName, rel_name = Release,
+ AppInfo = #xref_app{name = AppName, rel_name = Release,
vsn = Vsn, dir = Dir},
State1 = do_add_application(OldState, AppInfo),
- {ok, _Modules, NewState} =
+ {ok, _Modules, NewState} =
do_add_directory(Dir, [AppName], OB, false, OV, OW, State1),
{ok, AppName, NewState}.
@@ -662,7 +664,7 @@ do_add_directory(Dir, AppName, Bui, Rec, Ver, War, State) ->
ok = is_filename(Dir),
{FileNames, Errors, Jams, Unreadable} =
xref_utils:scan_directory(Dir, Rec, [?Suffix], [".jam"]),
- warnings(War, jam, Jams),
+ warnings(War, jam, Jams),
warnings(War, unreadable, Unreadable),
case Errors of
[] ->
@@ -683,7 +685,7 @@ do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) ->
false ->
throw_error({invalid_filename, File});
Splitname ->
- do_add_module(Splitname, AppName, Builtins, Verbose,
+ do_add_module(Splitname, AppName, Builtins, Verbose,
Warnings, State)
end.
@@ -691,7 +693,7 @@ do_add_a_module(File, AppName, Builtins, Verbose, Warnings, State) ->
%% Options: verbose, warnings, builtins
do_add_module({Dir, Basename}, AppName, Builtins, Verbose, Warnings, State) ->
File = filename:join(Dir, Basename),
- {ok, M, Bad, NewState} =
+ {ok, M, Bad, NewState} =
do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State),
filter(fun({Tag,B}) -> warnings(Warnings, Tag, [[File,B]]) end, Bad),
{ok, M, NewState}.
@@ -723,7 +725,7 @@ do_add_module1(Dir, File, AppName, Builtins, Verbose, Warnings, State) ->
{ok, {_, _, _, Time}} -> Time;
Error -> throw(Error)
end,
- XMod = #xref_mod{name = M, app_name = AppName, dir = Dir,
+ XMod = #xref_mod{name = M, app_name = AppName, dir = Dir,
mtime = T, builtins = Builtins,
no_unresolved = NoUnresCalls},
do_add_module(State, XMod, UnresCalls, Data);
@@ -736,13 +738,13 @@ abst(File, Builtins, Mode) when Mode =:= functions ->
case beam_lib:chunks(File, [abstract_code, exports, attributes]) of
{ok, {M,[{abstract_code,NoA},_X,_A]}} when NoA =:= no_abstract_code ->
{ok, M, NoA};
- {ok, {M, [{abstract_code, {abstract_v1, Forms}},
+ {ok, {M, [{abstract_code, {abstract_v1, Forms}},
{exports,X0}, {attributes,A}]}} ->
%% R7.
X = xref_utils:fa_to_mfa(X0, M),
D = deprecated(A, X, M),
xref_reader:module(M, Forms, Builtins, X, D);
- {ok, {M, [{abstract_code, {abstract_v2, Forms}},
+ {ok, {M, [{abstract_code, {abstract_v2, Forms}},
{exports,X0}, {attributes,A}]}} ->
%% R8-R9B.
X = xref_utils:fa_to_mfa(X0, M),
@@ -769,8 +771,8 @@ abst(File, Builtins, Mode) when Mode =:= modules ->
true ->
I0;
false ->
- Fun = fun({M,F,A}) ->
- not xref_utils:is_builtin(M, F, A)
+ Fun = fun({M,F,A}) ->
+ not xref_utils:is_builtin(M, F, A)
end,
filter(Fun, I0)
end,
@@ -790,7 +792,7 @@ mfa_exports(X0, Attributes, M) ->
xref_utils:fa_to_mfa(X1, M).
adjust_arity(F, A) ->
- case xref_utils:is_static_function(F, A) of
+ case xref_utils:is_static_function(F, A) of
true -> A;
false -> A - 1
end.
@@ -885,7 +887,7 @@ do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions ->
Unres = domain(UnresCalls),
DefinedFuns = domain(DefAt),
- {AXC, ALC, Bad1, LPreCAt2, XPreCAt2} =
+ {AXC, ALC, Bad1, LPreCAt2, XPreCAt2} =
extra_edges(AXC1, ALC1, Bad0, DefinedFuns),
Bad = map(fun(B) -> {xref_attr, B} end, Bad1),
LPreCAt = union(LPreCAt1, LPreCAt2),
@@ -904,8 +906,8 @@ do_add_module(S, M, XMod, Unres0, Data) when S#xref.mode =:= functions ->
%% {EE, ECallAt} = inter_graph(X, L, LC, XC, LCallAt, XCallAt),
Self = self(),
- Fun = fun() -> inter_graph(Self, X, L, LC, XC, CallAt) end,
- {EE, ECallAt} =
+ Fun = fun() -> inter_graph(Self, X, L, LC, XC, CallAt) end,
+ {EE, ECallAt} =
xref_utils:subprocess(Fun, [link, {min_heap_size,100000}]),
[DefAt2,L2,X2,LCallAt2,XCallAt2,CallAt2,LC2,XC2,EE2,ECallAt2,
@@ -977,13 +979,13 @@ extra_edges(CAX, CAL, Bad0, F) ->
ALC = restriction(2, restriction(ALC0, F), F),
LPreCAt2 = restriction(CAL, ALC),
XPreCAt2 = restriction(CAX, AXC),
- Bad = Bad0 ++ to_external(difference(AXC0, AXC))
+ Bad = Bad0 ++ to_external(difference(AXC0, AXC))
++ to_external(difference(ALC0, ALC)),
{AXC, ALC, Bad, LPreCAt2, XPreCAt2}.
no_info(X, L, LC, XC, EE, Unres, NoCalls, NoUnresCalls) ->
NoUnres = no_elements(Unres),
- [{no_calls, {NoCalls-NoUnresCalls, NoUnresCalls}},
+ [{no_calls, {NoCalls-NoUnresCalls, NoUnresCalls}},
{no_function_calls, {no_elements(LC), no_elements(XC)-NoUnres, NoUnres}},
{no_functions, {no_elements(L), no_elements(X)}},
%% Note: this is overwritten in do_set_up():
@@ -1011,10 +1013,10 @@ inter_graph(X, L, LC, XC, CallAt) ->
Es = union(LEs, XEs),
E1 = to_external(restriction(difference(LC, LEs), XL)),
- R0 = xref_utils:xset(reachable(E1, G, []),
+ R0 = xref_utils:xset(reachable(E1, G, []),
[{tspec(func), tspec(fun_edge)}]),
true = digraph:delete(G),
-
+
% RL is a set of indirect local calls to exports.
RL = restriction(R0, XL),
% RX is a set of indirect external calls to exports.
@@ -1033,7 +1035,7 @@ inter_graph(X, L, LC, XC, CallAt) ->
?FORMAT("XL=~p~nXEs=~p~nLEs=~p~nE1=~p~nR0=~p~nRL=~p~nRX=~p~nR=~p~n"
"EE=~p~nECallAt1=~p~nECallAt2=~p~nECallAt=~p~n~n",
- [XL, XEs, LEs, E1, R0, RL, RX, R, EE,
+ [XL, XEs, LEs, E1, R0, RL, RX, R, EE,
ECallAt1, ECallAt2, ECallAt]),
{EE, ECallAt}.
@@ -1121,7 +1123,7 @@ remove_erase([], D) ->
do_add_libraries(Path, Verbose, State) ->
message(Verbose, lib_search, []),
- {C, E} = xref_utils:list_path(Path, [?Suffix]),
+ {C, E} = xref_utils:list_path(Path, [?Suffix]),
message(Verbose, done, []),
MDs = to_external(relation_to_family(relation(C))),
%% message(Verbose, lib_check, []),
@@ -1160,23 +1162,23 @@ do_set_up(S, VerboseOpt) ->
Reply.
%% If data has been supplied using add_module/9 (and that is the only
-%% sanctioned way), then DefAt, L, X, LCallAt, XCallAt, CallAt, XC, LC,
-%% and LU are guaranteed to be functions (with all supplied
-%% modules as domain (disregarding unknown modules, that is, modules
+%% sanctioned way), then DefAt, L, X, LCallAt, XCallAt, CallAt, XC, LC,
+%% and LU are guaranteed to be functions (with all supplied
+%% modules as domain (disregarding unknown modules, that is, modules
%% not supplied but hosting unknown functions)).
%% As a consequence, V and E are also functions. V is defined for unknown
%% modules also.
%% UU is also a function (thanks to sofs:family_difference/2...).
-%% XU on the other hand can be a partial function (that is, not defined
+%% XU on the other hand can be a partial function (that is, not defined
%% for all modules). U is derived from XU, so U is also partial.
%% The inverse variables - LC_1, XC_1, E_1 and EE_1 - are all partial.
%% B is also partial.
do_set_up(S) when S#xref.mode =:= functions ->
ModDictList = dict:to_list(S#xref.modules),
- [DefAt0, L, X0, LCallAt, XCallAt, CallAt, LC, XC, LU,
+ [DefAt0, L, X0, LCallAt, XCallAt, CallAt, LC, XC, LU,
EE0, ECallAt, UC, LPredefined,
Mod_DF,Mod_DF_1,Mod_DF_2,Mod_DF_3] = make_families(ModDictList, 18),
-
+
{XC_1, XU, XPredefined} = do_set_up_1(XC),
LC_1 = user_family(union_of_family(LC)),
E_1 = family_union(XC_1, LC_1),
@@ -1206,7 +1208,7 @@ do_set_up(S) when S#xref.mode =:= functions ->
AM = domain(F1),
%% Undef is the union of U0 and Lib:
- {Undef, U0, Lib, Lib_DF, Lib_DF_1, Lib_DF_2, Lib_DF_3} =
+ {Undef, U0, Lib, Lib_DF, Lib_DF_1, Lib_DF_2, Lib_DF_3} =
make_libs(XU, F1, AM, S#xref.library_path, S#xref.libraries),
{B, U} = make_builtins(U0),
X1_B = family_union(X1, B),
@@ -1228,22 +1230,22 @@ do_set_up(S) when S#xref.mode =:= functions ->
%% way to discard calls to local functions in other modules.
EE_conv = converse(union_of_family(EE0)),
EE_exported = restriction(EE_conv, union_of_family(X)),
- EE_local =
+ EE_local =
specification({external, fun({{M1,_,_},{M2,_,_}}) -> M1 =:= M2 end},
EE_conv),
EE_0 = converse(union(EE_local, EE_exported)),
EE_1 = user_family(EE_0),
- EE1 = partition_family({external, fun({{M1,_,_}, _MFA2}) -> M1 end},
+ EE1 = partition_family({external, fun({{M1,_,_}, _MFA2}) -> M1 end},
EE_0),
%% Make sure EE is defined for all modules:
EE = family_union(family_difference(EE0, EE0), EE1),
- IFun =
- fun({Mod,EE_M}, XMods) ->
- IMFun =
+ IFun =
+ fun({Mod,EE_M}, XMods) ->
+ IMFun =
fun(XrefMod) ->
- [NoCalls, NoFunctionCalls,
+ [NoCalls, NoFunctionCalls,
NoFunctions, _NoInter] = XrefMod#xref_mod.info,
- NewInfo = [NoCalls, NoFunctionCalls, NoFunctions,
+ NewInfo = [NoCalls, NoFunctionCalls, NoFunctions,
{no_inter_function_calls,length(EE_M)}],
XrefMod#xref_mod{info = NewInfo}
end,
@@ -1274,11 +1276,11 @@ do_set_up(S) when S#xref.mode =:= functions ->
finish_set_up(S1, Vs);
do_set_up(S) when S#xref.mode =:= modules ->
ModDictList = dict:to_list(S#xref.modules),
- [X0, I0, Mod_DF, Mod_DF_1, Mod_DF_2, Mod_DF_3] =
+ [X0, I0, Mod_DF, Mod_DF_1, Mod_DF_2, Mod_DF_3] =
make_families(ModDictList, 7),
I = union_of_family(I0),
AM = domain(X0),
-
+
{XU, Predefined} = make_predefined(I, AM),
%% Add "hidden" functions to the exports.
X1 = family_union(X0, Predefined),
@@ -1288,8 +1290,8 @@ do_set_up(S) when S#xref.mode =:= modules ->
M2A = make_M2A(ModDictList),
{A2R,A} = make_A2R(S#xref.applications),
R = set(dict:fetch_keys(S#xref.releases)),
-
- ME = projection({external, fun({M1,{M2,_F2,_A2}}) -> {M1,M2} end},
+
+ ME = projection({external, fun({M1,{M2,_F2,_A2}}) -> {M1,M2} end},
family_to_relation(I0)),
ME2AE = multiple_relative_product({M2A, M2A}, ME),
@@ -1298,7 +1300,7 @@ do_set_up(S) when S#xref.mode =:= modules ->
RE = range(AE2RE),
%% Undef is the union of U0 and Lib:
- {_Undef, U0, Lib, Lib_DF, Lib_DF_1, Lib_DF_2, Lib_DF_3} =
+ {_Undef, U0, Lib, Lib_DF, Lib_DF_1, Lib_DF_2, Lib_DF_3} =
make_libs(XU, X1, AM, S#xref.library_path, S#xref.libraries),
{B, U} = make_builtins(U0),
X1_B = family_union(X1, B),
@@ -1312,7 +1314,7 @@ do_set_up(S) when S#xref.mode =:= modules ->
X = family_union(X1, Lib),
Empty = empty_set(),
- Vs = [{'X',X},{'U',U},{'B',B},{'XU',XU},{v,V},
+ Vs = [{'X',X},{'U',U},{'B',B},{'XU',XU},{v,V},
{e,{Empty,Empty}},
{'M',M},{'A',A},{'R',R},
{'AM',AM},{'UM',UM},{'LM',LM},
@@ -1328,10 +1330,10 @@ finish_set_up(S, Vs) ->
S1 = S#xref{variables = T},
%% io:format("~p <= state <= ~p~n", [pack:lsize(S), pack:usize(S)]),
{ok, S1}.
-
+
do_finish_set_up([{Key, Value} | Vs], T) ->
{Type, OType} = var_type(Key),
- Val = #xref_var{name = Key, value = Value, vtype = predef,
+ Val = #xref_var{name = Key, value = Value, vtype = predef,
otype = OType, type = Type},
T1 = dict:store(Key, Val, T),
do_finish_set_up(Vs, T1);
@@ -1362,15 +1364,15 @@ var_type('EE') -> {function, edge};
var_type('LC') -> {function, edge};
var_type('UC') -> {function, edge};
var_type('XC') -> {function, edge};
-var_type('AE') -> {application, edge};
-var_type('ME') -> {module, edge};
+var_type('AE') -> {application, edge};
+var_type('ME') -> {module, edge};
var_type('RE') -> {release, edge};
var_type(_) -> {foo, bar}.
make_families(ModDictList, N) ->
Fun1 = fun({_,XMod}) -> XMod#xref_mod.data end,
Ss = from_sets(map(Fun1, ModDictList)),
- %% io:format("~n~p <= module data <= ~p~n",
+ %% io:format("~n~p <= module data <= ~p~n",
%% [pack:lsize(Ss), pack:usize(Ss)]),
make_fams(N, Ss, []).
@@ -1389,7 +1391,7 @@ make_M2A(ModDictList) ->
make_A2R(ApplDict) ->
AppDict = dict:to_list(ApplDict),
Fun = fun({A,XApp}) -> {A, XApp#xref_app.rel_name} end,
- Appl0 = family(map(Fun, AppDict)),
+ Appl0 = family(map(Fun, AppDict)),
AllApps = domain(Appl0),
Appl = family_to_relation(Appl0),
{Appl, AllApps}.
@@ -1445,13 +1447,13 @@ make_libs(XU, F, AM, LibPath, LibDict) ->
false ->
Libraries = dict:to_list(LibDict),
Lb = restriction(a_function(Libraries), UM),
- MFun = fun({M,XLib}) ->
+ MFun = fun({M,XLib}) ->
#xref_lib{dir = Dir} = XLib,
xref_utils:module_filename(Dir, M)
end,
map(MFun, to_external(Lb))
end,
- Fun = fun(FileName, Deprs) ->
+ Fun = fun(FileName, Deprs) ->
case beam_lib:chunks(FileName, [exports, attributes]) of
{ok, {M, [{exports,X}, {attributes,A}]}} ->
Exports = mfa_exports(X, A, M),
@@ -1496,14 +1498,14 @@ user_family(R) ->
partition_family({external, fun({_MFA1, {M2,_,_}}) -> M2 end}, R).
do_variables(State) ->
- Fun = fun({Name, #xref_var{vtype = user}}, {P,U}) ->
+ Fun = fun({Name, #xref_var{vtype = user}}, {P,U}) ->
{P,[Name | U]};
- ({Name, #xref_var{vtype = predef}}, A={P,U}) ->
+ ({Name, #xref_var{vtype = predef}}, A={P,U}) ->
case atom_to_list(Name) of
[H|_] when H>= $a, H=<$z -> A;
_Else -> {[Name | P], U}
end;
- ({{tmp, V}, _}, A) ->
+ ({{tmp, V}, _}, A) ->
io:format("Bug in ~p: temporary ~p~n", [?MODULE, V]), A;
(_V, A) -> A
end,
@@ -1565,7 +1567,7 @@ do_info(S, libraries) ->
map(fun({_L,XLib}) -> lib_info(XLib) end, D);
do_info(_S, I) ->
error({no_such_info, I}).
-
+
do_info(S, Type, E) when is_atom(E) ->
do_info(S, Type, [E]);
do_info(S, modules, Modules0) when is_list(Modules0) ->
@@ -1598,7 +1600,7 @@ find_info([E | Es], Dict, Error) ->
{ok, X} ->
[X | find_info(Es, Dict, Error)]
end;
-find_info([], _Dict, _Error) ->
+find_info([], _Dict, _Error) ->
[].
%% -> {[{AppName, RelName}], [{RelName, XApp}]}
@@ -1618,7 +1620,7 @@ rel_apps(S) ->
rel_apps_sums(AR, RRA0, S) ->
AppMods = app_mods(S), % [{AppName, XMod}]
RRA1 = relation_to_family(relation(RRA0)),
- RRA = inverse(substitution(1, RRA1)),
+ RRA = inverse(substitution(1, RRA1)),
%% RRA is [{RelName,{RelName,[XApp]}}]
RelMods = relative_product1(relation(AR), relation(AppMods)),
RelAppsMods = relative_product1(RRA, RelMods),
@@ -1630,7 +1632,7 @@ rel_apps_sums(AR, RRA0, S) ->
%% -> [{AppName, XMod}]
app_mods(S) ->
D = sort(dict:to_list(S#xref.modules)),
- Fun = fun({_M,XMod}, Acc) ->
+ Fun = fun({_M,XMod}, Acc) ->
case XMod#xref_mod.app_name of
[] -> Acc;
[AppName] -> [{AppName, XMod} | Acc]
@@ -1639,7 +1641,7 @@ app_mods(S) ->
foldl(Fun, [], D).
mod_info(XMod) ->
- #xref_mod{name = M, app_name = AppName, builtins = BuiltIns,
+ #xref_mod{name = M, app_name = AppName, builtins = BuiltIns,
dir = Dir, info = Info} = XMod,
App = sup_info(AppName),
{M, [{application, App}, {builtins, BuiltIns}, {directory, Dir} | Info]}.
@@ -1649,7 +1651,7 @@ app_info({AppName, ModSums}, S) ->
#xref_app{rel_name = RelName, vsn = Vsn, dir = Dir} = XApp,
Release = sup_info(RelName),
{AppName, [{directory,Dir}, {release, Release}, {version,Vsn} | ModSums]}.
-
+
rel_info({{RelName, XApps}, ModSums}, S) ->
NoApps = length(XApps),
XRel = dict:fetch(RelName, S#xref.releases),
@@ -1678,16 +1680,16 @@ no_sum(S, L) when S#xref.mode =:= modules ->
[{no_analyzed_modules, length(L)}].
no_sum([XMod | D], C0, UC0, LC0, XC0, UFC0, L0, X0, EV0, NoM) ->
- [{no_calls, {C,UC}},
+ [{no_calls, {C,UC}},
{no_function_calls, {LC,XC,UFC}},
{no_functions, {L,X}},
{no_inter_function_calls, EV}] = XMod#xref_mod.info,
no_sum(D, C0+C, UC0+UC, LC0+LC, XC0+XC, UFC0+UFC, L0+L, X0+X, EV0+EV, NoM);
no_sum([], C, UC, LC, XC, UFC, L, X, EV, NoM) ->
[{no_analyzed_modules, NoM},
- {no_calls, {C,UC}},
+ {no_calls, {C,UC}},
{no_function_calls, {LC,XC,UFC}},
- {no_functions, {L,X}},
+ {no_functions, {L,X}},
{no_inter_function_calls, EV}].
%% -> ok | throw(Error)
@@ -1712,20 +1714,20 @@ warnings(Flag, Message, [F | Fs]) ->
%% pack(term()) -> term()
%%
%% The identify function. The returned term does not use more heap
-%% than the given term. Tuples that are equal (=:=/2) are made
+%% than the given term. Tuples that are equal (=:=/2) are made
%% "the same".
%%
%% The process dictionary is used because it seems to be faster than
%% anything else right now...
%%
%pack(T) -> T;
-pack(T) ->
+pack(T) ->
PD = erase(),
NT = pack1(T),
%% true = T =:= NT,
%% io:format("erasing ~p elements...~n", [length(erase())]),
erase(), % wasting heap (and time)...
- map(fun({K,V}) -> put(K, V) end, PD),
+ foreach(fun({K,V}) -> put(K, V) end, PD),
NT.
pack1(C) when not is_tuple(C), not is_list(C) ->
diff --git a/lib/tools/src/xref_compiler.erl b/lib/tools/src/xref_compiler.erl
index 67ac8c617d..1445e135be 100644
--- a/lib/tools/src/xref_compiler.erl
+++ b/lib/tools/src/xref_compiler.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -31,21 +31,23 @@
-define(CALL(F), ok).
-endif.
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([compile/2]).
-export([update_graph_counter/3]).
-export([format_error/1]).
--import(lists,
+-import(lists,
[concat/1, foldl/3, nthtail/2, reverse/1, sort/1, sublist/2]).
-import(sofs,
[composite/2, difference/2, empty_set/0, from_term/1,
intersection/2, is_empty_set/1, multiple_relative_product/2,
projection/2, relation/1, relation_to_family/1,
- restriction/2, substitution/2, to_external/1, union/2,
- union_of_family/1]).
+ restriction/2, specification/2, substitution/2,
+ to_external/1, union/2, union_of_family/1]).
%%
%% Exported functions
@@ -75,7 +77,7 @@ compile(Chars, Table) ->
{error, Info, Line} ->
error({parse_error, Line, Info})
end.
-
+
format_error({error, Module, Error}) ->
Module:format_error(Error);
format_error({parse_error, Line, Error}) ->
@@ -115,7 +117,7 @@ statements([Stmt={assign, VarType, Name, E} | Stmts0], Table, L, UV) ->
throw_error({variable_reassigned, xref_parser:t2s(Stmt)});
error ->
{Type, OType, NewE} = t_expr(E, Table),
- Val = #xref_var{name = Name, vtype = VarType,
+ Val = #xref_var{name = Name, vtype = VarType,
otype = OType, type = Type},
NewTable = dict:store(Name, Val, Table),
Stmts = if Stmts0 =:= [] -> [{variable, Name}]; true -> Stmts0 end,
@@ -128,9 +130,9 @@ statements([Expr], Table, L, UV) ->
E1 = un_familiarize(Type, OType, NewE),
NE = case {Type, OType} of
%% Edges with empty sets of line numbers are removed.
- {{line, _}, edge} ->
+ {{line, _}, edge} ->
{relation_to_family, E1};
- {_Type, edge_closure} ->
+ {_Type, edge_closure} ->
%% Fake a closure usage, just to make sure it is destroyed.
E2 = {fun graph_access/2, E1, E1},
{fun(_E) -> 'closure()' end, E2};
@@ -163,7 +165,7 @@ t_expr(E, Table) ->
%%% Constant = atom() | {atom(), atom()} | MFA | {MFA, MFA}
%%% Call = atom() % function in the sofs module
%%% | fun()
-%%% Type = {line, LineType} | function | module | application | release
+%%% Type = {line, LineType} | function | module | application | release
%%% | number
%%% LineType = line | local_call | external_call | export_call | all_line_call
%%% VarType = predef | user | tmp
@@ -182,7 +184,7 @@ check_expr({variable, Name}, Table) ->
case dict:find(Name, Table) of
{ok, #xref_var{vtype = VarType, otype = OType, type = Type}} ->
V0 = {variable, {VarType, Name}},
- V = case {VarType, Type, OType} of
+ V = case {VarType, Type, OType} of
{predef, release, _} -> V0;
{predef, application, _} -> V0;
{predef, module, _} -> V0;
@@ -212,7 +214,7 @@ check_expr(Expr={set, SOp, E}, Table) ->
{edge_set, domain} -> vertex_set;
{edge_set, weak} -> edge_set;
{edge_set, strict} -> edge_set;
- _ ->
+ _ ->
throw_error({type_error, xref_parser:t2s(Expr)})
end,
Op = set_op(SOp),
@@ -223,10 +225,10 @@ check_expr(Expr={graph, Op, E}, Table) ->
case Type of
{line, _LineType} ->
throw_error({type_error, xref_parser:t2s(Expr)});
- _Else ->
+ _Else ->
ok
end,
- OType =
+ OType =
case {NOType, Op} of
{edge, components} -> vertex_set;
{edge, condensation} -> edge_set;
@@ -237,7 +239,7 @@ check_expr(Expr={graph, Op, E}, Table) ->
%% Neither need nor want these ones:
%% {edge_set, closure} -> edge_set_closure;
%% {edge_set, components} -> vertex_set_set;
- _ ->
+ _ ->
throw_error({type_error, xref_parser:t2s(Expr)})
end,
E2 = {convert, NOType, edge_closure, E1},
@@ -271,10 +273,10 @@ check_expr(Expr={set, SOp, E1, E2}, Table) ->
number ->
{expr, number, number, {call, ari_op(SOp), NE1, NE2}};
_Else -> % set
- {Type, NewE1, NewE2} =
+ {Type, NewE1, NewE2} =
case {type_ord(Type1), type_ord(Type2)} of
{T1, T2} when T1 =:= T2 ->
- %% Example: if Type1 = {line, line} and
+ %% Example: if Type1 = {line, line} and
%% Type2 = {line, export_line}, then this is not
%% correct, but works:
{Type1, NE1, NE2};
@@ -296,7 +298,7 @@ check_expr(Expr={restr, ROp, E1, E2}, Table) ->
throw_error({type_error, xref_parser:t2s(Expr)});
{_Type1, {line, _LineType2}} ->
throw_error({type_error, xref_parser:t2s(Expr)});
- _ ->
+ _ ->
ok
end,
case {OType1, OType2} of
@@ -307,14 +309,14 @@ check_expr(Expr={restr, ROp, E1, E2}, Table) ->
{edge, vertex} ->
restriction(ROp, E1, Type1, NE1, Type2, NE2);
{edge_closure, vertex} when ROp =:= '|||' ->
- {expr, _, _, R1} =
+ {expr, _, _, R1} =
closure_restriction('|', Type1, Type2, OType2, NE1, NE2),
- {expr, _, _, R2} =
+ {expr, _, _, R2} =
closure_restriction('||', Type1, Type2, OType2, NE1, NE2),
{expr, Type1, edge, {call, intersection, R1, R2}};
- {edge_closure, vertex} ->
+ {edge_closure, vertex} ->
closure_restriction(ROp, Type1, Type2, OType2, NE1, NE2);
- _ ->
+ _ ->
throw_error({type_error, xref_parser:t2s(Expr)})
end;
check_expr(Expr={path, E1, E2}, Table) ->
@@ -330,7 +332,7 @@ check_expr(Expr={path, E1, E2}, Table) ->
end,
E2b = {convert, OType2, Type2, Type1, E2a},
{OType1, NE1} = path_arg(OType1a, E1a),
- NE2 = case {OType1, OType2} of
+ NE2 = case {OType1, OType2} of
{path, edge} -> {convert, OType2, edge_closure, E2b};
{path, edge_closure} when Type1 =:= Type2 -> E2b;
_ -> throw_error({type_error, xref_parser:t2s(Expr)})
@@ -347,7 +349,7 @@ check_expr({regexpr, RExpr, Type0}, _Table) ->
release -> 'R'
end,
Var = {variable, {predef, V}},
- Call = {call, fun(E, V2) -> xref_utils:regexpr(E, V2) end,
+ Call = {call, fun(E, V2) -> xref_utils:regexpr(E, V2) end,
{constants, RExpr}, Var},
{expr, Type, vertex, Call};
check_expr(C={constant, _Type, _OType, _C}, Table) ->
@@ -368,15 +370,15 @@ check_conversion(OType, Type1, Type2, Expr) ->
end.
%% Allowed conversions.
-conversions(_OType, {line, LineType}, {line, LineType}) -> ok;
+conversions(_OType, {line, LineType}, {line, LineType}) -> ok;
conversions(edge, {line, _}, {line, all_line_call}) -> ok;
-conversions(edge, From, {line, Line})
+conversions(edge, From, {line, Line})
when is_atom(From), Line =/= all_line_call -> ok;
conversions(vertex, From, {line, line}) when is_atom(From) -> ok;
conversions(vertex, From, To) when is_atom(From), is_atom(To) -> ok;
conversions(edge, From, To) when is_atom(From), is_atom(To) -> ok;
%% "Extra":
-conversions(edge, {line, Line}, To)
+conversions(edge, {line, Line}, To)
when is_atom(To), Line =/= all_line_call -> ok;
conversions(vertex, {line, line}, To) when is_atom(To) -> ok;
conversions(_OType, _From, _To) -> not_ok.
@@ -399,7 +401,7 @@ ari_op(difference) -> fun(X, Y) -> X - Y end.
restriction(ROp, E1, Type1, NE1, Type2, NE2) ->
{Column, _} = restr_op(ROp),
- case NE1 of
+ case NE1 of
{call, union_of_family, _E} when ROp =:= '|' ->
restriction(Column, Type1, E1, Type2, NE2);
{call, union_of_family, _E} when ROp =:= '||' ->
@@ -455,8 +457,8 @@ check_constants(Cs=[C={constant, Type0, OType, _Con} | Cs1], Table) ->
E = function_vertices_to_family(Type, OType, {constants, S}),
{expr, Type, OType, E};
[{Type1, [C1|_]}, {Type2, [C2|_]} | _] ->
- throw_error({type_mismatch,
- make_vertex(Type1, C1),
+ throw_error({type_mismatch,
+ make_vertex(Type1, C1),
make_vertex(Type2, C2)})
end.
@@ -467,7 +469,7 @@ check_mix([C={constant, Type, OType, _Con} | Cs], Type0, OType, _C0)
check_mix(Cs, Type, OType, C);
check_mix([C | _], _Type0, _OType0, C0) ->
throw_error({type_mismatch, xref_parser:t2s(C0), xref_parser:t2s(C)});
-check_mix([], _Type0, _OType0, _C0) ->
+check_mix([], _Type0, _OType0, _C0) ->
ok.
split(Types, Cs, Table) ->
@@ -478,11 +480,11 @@ split([Type | Types], Vs, AllSoFar, _Type, Table, L) ->
S0 = known_vertices(Type, Vs, Table),
S = difference(S0, AllSoFar),
case is_empty_set(S) of
- true ->
+ true ->
split(Types, Vs, AllSoFar, Type, Table, L);
- false ->
+ false ->
All = union(AllSoFar, S0),
- split(Types, Vs, All, Type, Table,
+ split(Types, Vs, All, Type, Table,
[{Type, to_external(S)} | L])
end;
split([], Vs, All, Type, _Table, L) ->
@@ -491,7 +493,7 @@ split([], Vs, All, Type, _Table, L) ->
[C|_] -> throw_error({unknown_constant, make_vertex(Type, C)})
end.
-make_vertex(Type, C) ->
+make_vertex(Type, C) ->
xref_parser:t2s({constant, Type, vertex, C}).
constant_vertices([{constant, _Type, edge, {A,B}} | Cs], L) ->
@@ -504,7 +506,7 @@ constant_vertices([], L) ->
known_vertices('Fun', Cs, T) ->
M = projection(1, Cs),
F = union_of_family(restriction(fetch_value(v, T), M)),
- intersection(Cs, F);
+ union(bifs(Cs), intersection(Cs, F));
known_vertices('Mod', Cs, T) ->
intersection(Cs, fetch_value('M', T));
known_vertices('App', Cs, T) ->
@@ -512,6 +514,11 @@ known_vertices('App', Cs, T) ->
known_vertices('Rel', Cs, T) ->
intersection(Cs, fetch_value('R', T)).
+bifs(Cs) ->
+ specification({external,
+ fun({M,F,A}) -> xref_utils:is_builtin(M, F, A) end},
+ Cs).
+
function_vertices_to_family(function, vertex, E) ->
{call, partition_family, 1, E};
function_vertices_to_family(_Type, _OType, E) ->
@@ -567,11 +574,11 @@ convert(E, OType, FromType, ToType) ->
general(_ObjectType, FromType, ToType, X) when FromType =:= ToType ->
X;
-general(edge, {line, _LineType}, ToType, LEs) ->
+general(edge, {line, _LineType}, ToType, LEs) ->
VEs = {projection, ?Q({external, fun({V1V2,_Ls}) -> V1V2 end}), LEs},
general(edge, function, ToType, VEs);
general(edge, function, ToType, VEs) ->
- MEs = {projection,
+ MEs = {projection,
?Q({external, fun({{M1,_,_},{M2,_,_}}) -> {M1,M2} end}),
VEs},
general(edge, module, ToType, MEs);
@@ -580,7 +587,7 @@ general(edge, module, ToType, MEs) ->
general(edge, application, ToType, AEs);
general(edge, application, release, AEs) ->
{image, {get, ae}, AEs};
-general(vertex, {line, _LineType}, ToType, L) ->
+general(vertex, {line, _LineType}, ToType, L) ->
V = {partition_family, ?Q(1), {domain, L}},
general(vertex, function, ToType, V);
general(vertex, function, ToType, V) ->
@@ -595,18 +602,18 @@ general(vertex, application, release, A) ->
special(_ObjectType, FromType, ToType, X) when FromType =:= ToType ->
X;
special(edge, {line, _LineType}, {line, all_line_call}, Calls) ->
- {put, ?T(mods),
- {projection,
- ?Q({external, fun({{{M1,_,_},{M2,_,_}},_}) -> {M1,M2} end}),
+ {put, ?T(mods),
+ {projection,
+ ?Q({external, fun({{{M1,_,_},{M2,_,_}},_}) -> {M1,M2} end}),
Calls},
- {put, ?T(def_at),
+ {put, ?T(def_at),
{union, {image, {get, def_at},
- {union, {domain, {get, ?T(mods)}},
+ {union, {domain, {get, ?T(mods)}},
{range, {get, ?T(mods)}}}}},
{fun funs_to_lines/2,
{get, ?T(def_at)}, Calls}}};
special(edge, function, {line, LineType}, VEs) ->
- Var = if
+ Var = if
LineType =:= line -> call_at;
LineType =:= export_call -> e_call_at;
LineType =:= local_call -> l_call_at;
@@ -615,9 +622,9 @@ special(edge, function, {line, LineType}, VEs) ->
line_edges(VEs, Var);
special(edge, module, ToType, MEs) ->
VEs = {image,
- {projection,
+ {projection,
?Q({external, fun(FE={{M1,_,_},{M2,_,_}}) -> {{M1,M2},FE} end}),
- {union,
+ {union,
{image, {get, e},
{projection, ?Q({external, fun({M1,_M2}) -> M1 end}), MEs}}}},
MEs},
@@ -629,7 +636,7 @@ special(edge, release, ToType, REs) ->
AEs = {inverse_image, {get, ae}, REs},
special(edge, application, ToType, AEs);
special(vertex, function, {line, _LineType}, V) ->
- {restriction,
+ {restriction,
{union_of_family, {restriction, {get, def_at}, {domain, V}}},
{union_of_family, V}};
special(vertex, module, ToType, M) ->
@@ -643,15 +650,15 @@ special(vertex, release, ToType, R) ->
special(vertex, application, ToType, A).
line_edges(VEs, CallAt) ->
- {put, ?T(ves), VEs,
- {put, ?T(m1),
- {projection, ?Q({external, fun({{M1,_,_},_}) -> M1 end}),
+ {put, ?T(ves), VEs,
+ {put, ?T(m1),
+ {projection, ?Q({external, fun({{M1,_,_},_}) -> M1 end}),
{get, ?T(ves)}},
{image, {projection, ?Q({external, fun(C={VV,_L}) -> {VV,C} end}),
{union, {image, {get, CallAt}, {get, ?T(m1)}}}},
{get, ?T(ves)}}}}.
-%% {(((v1,l1),(v2,l2)),l) :
+%% {(((v1,l1),(v2,l2)),l) :
%% (v1,l1) in DefAt and (v2,l2) in DefAt and ((v1,v2),L) in CallAt}
funs_to_lines(DefAt, CallAt) ->
T1 = multiple_relative_product({DefAt, DefAt}, projection(1, CallAt)),
@@ -765,7 +772,7 @@ save_vars([], _D, Vs, UVs, L) ->
%% Traverses the expression again, this time using more or less the
%% inverse of the table created by find_nodes. The first time a node
-%% is visited, its children are traversed, the following times a
+%% is visited, its children are traversed, the following times a
%% get instructions are inserted (using the saved value).
make_instructions(N, UserVars, D) ->
{D1, Is0} = make_instrs(N, D, []),
@@ -777,9 +784,9 @@ make_instructions(N, UserVars, D) ->
make_more_instrs([UV | UVs], D, Is) ->
case dict:find(UV, D) of
- error ->
+ error ->
make_more_instrs(UVs, D, Is);
- _Else ->
+ _Else ->
{ND, NIs} = make_instrs(UV, D, Is),
make_more_instrs(UVs, ND, [pop | NIs])
end;
@@ -844,17 +851,17 @@ evaluate([{quote, Val} | P], T, S) ->
evaluate(P, T, [Val | S]);
evaluate([{get, Var} | P], T, S) when is_atom(Var) -> % predefined
Value = fetch_value(Var, T),
- Val = case Value of
+ Val = case Value of
{R, _} -> R; % relation
_ -> Value % simple set
end,
- evaluate(P, T, [Val | S]);
+ evaluate(P, T, [Val | S]);
evaluate([{get, {inverse, Var}} | P], T, S) -> % predefined, inverse
{_, R} = fetch_value(Var, T),
- evaluate(P, T, [R | S]);
+ evaluate(P, T, [R | S]);
evaluate([{get, {user, Var}} | P], T, S) ->
Val = fetch_value(Var, T),
- evaluate(P, T, [Val | S]);
+ evaluate(P, T, [Val | S]);
evaluate([{get, Var} | P], T, S) -> % tmp
evaluate(P, T, [dict:fetch(Var, T) | S]);
evaluate([{save, Var={tmp, _}} | P], T, S=[Val | _]) ->
@@ -862,7 +869,7 @@ evaluate([{save, Var={tmp, _}} | P], T, S=[Val | _]) ->
evaluate(P, dict:store(Var, Val, T1), S);
evaluate([{save, {user, Name}} | P], T, S=[Val | _]) ->
#xref_var{vtype = user, otype = OType, type = Type} = dict:fetch(Name, T),
- NewVar = #xref_var{name = Name, value = Val,
+ NewVar = #xref_var{name = Name, value = Val,
vtype = user, otype = OType, type = Type},
T1 = update_graph_counter(Val, +1, T),
NT = dict:store(Name, NewVar, T1),
@@ -889,7 +896,7 @@ update_graph_counter(Value, Inc, T) ->
error when Inc =:= 1 ->
dict:store(Value, 1, T)
end;
- _EXIT ->
+ _EXIT ->
T
end.
diff --git a/lib/tools/src/xref_reader.erl b/lib/tools/src/xref_reader.erl
index db755c31d8..d22f0df164 100644
--- a/lib/tools/src/xref_reader.erl
+++ b/lib/tools/src/xref_reader.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(xref_reader).
@@ -22,7 +22,7 @@
-import(lists, [keysearch/3, member/2, reverse/1]).
--record(xrefr,
+-record(xrefr,
{module=[],
function=[],
def_at=[],
@@ -59,15 +59,15 @@
module(Module, Forms, CollectBuiltins, X, DF) ->
Attrs = [{Attr,V} || {attribute,_Line,Attr,V} <- Forms],
IsAbstract = xref_utils:is_abstract_module(Attrs),
- S = #xrefr{module = Module, builtins_too = CollectBuiltins,
+ S = #xrefr{module = Module, builtins_too = CollectBuiltins,
is_abstr = IsAbstract, x = X, df = DF},
forms(Forms, S).
forms([F | Fs], S) ->
S1 = form(F, S),
forms(Fs, S1);
-forms([], S) ->
- #xrefr{module = M, def_at = DefAt,
+forms([], S) ->
+ #xrefr{module = M, def_at = DefAt,
l_call_at = LCallAt, x_call_at = XCallAt,
el = LC, ex = XC, x = X, df = Depr,
lattrs = AL, xattrs = AX, battrs = B, unresolved = U} = S,
@@ -75,7 +75,7 @@ forms([], S) ->
{ok, M, {DefAt, LCallAt, XCallAt, LC, XC, X, Attrs, Depr}, U}.
form({attribute, Line, xref, Calls}, S) -> % experimental
- #xrefr{module = M, function = Fun,
+ #xrefr{module = M, function = Fun,
lattrs = L, xattrs = X, battrs = B} = S,
attr(Calls, Line, M, Fun, L, X, B, S);
form({attribute, _Line, _Attr, _Val}, S) ->
@@ -110,12 +110,12 @@ clauses([{clause, _Line, _H, G, B} | Cs], FunVars, Matches, S) ->
S2 = expr(B, S1),
S3 = S2#xrefr{funvars = FunVars, matches = Matches},
clauses(Cs, S3);
-clauses([], _FunVars, _Matches, S) ->
+clauses([], _FunVars, _Matches, S) ->
S.
attr([E={From, To} | As], Ln, M, Fun, AL, AX, B, S) ->
case mfa(From, M) of
- {_, _, MFA} when MFA =:= Fun; [] =:= Fun ->
+ {_, _, MFA} when MFA =:= Fun; [] =:= Fun ->
attr(From, To, Ln, M, Fun, AL, AX, B, S, As, E);
{_, _, _} ->
attr(As, Ln, M, Fun, AL, AX, [E | B], S);
@@ -164,7 +164,7 @@ expr({call, Line,
%% Added in R10B-6. M:F/A.
expr({'fun', Line, {function, Mod, Fun, Arity}}, S);
expr({'fun', Line, {function, Mod, Name, Arity}}, S) ->
- %% Added in R10B-6. M:F/A.
+ %% Added in R10B-6. M:F/A.
As = lists:duplicate(Arity, {atom, Line, foo}),
external_call(Mod, Name, As, Line, false, S);
expr({'fun', Line, {function, Name, Arity}, _Extra}, S) ->
@@ -183,7 +183,7 @@ expr({call, Line, {remote, _Line, Mod, Name}, As}, S) ->
expr({call, Line, F, As}, S) ->
external_call(erlang, apply, [F, list2term(As)], Line, true, S);
expr({match, _Line, {var,_,Var}, {'fun', _, {clauses, Cs}, _Extra}}, S) ->
- %% This is what is needed in R7 to avoid warnings for the functions
+ %% This is what is needed in R7 to avoid warnings for the functions
%% that are passed around by the "expansion" of list comprehension.
S1 = S#xrefr{funvars = [Var | S#xrefr.funvars]},
clauses(Cs, S1);
@@ -192,6 +192,14 @@ expr({match, _Line, {var,_,Var}, E}, S) ->
%% Args = [A,B], apply(m, f, Args)
S1 = S#xrefr{matches = [{Var, E} | S#xrefr.matches]},
expr(E, S1);
+expr({op, _Line, 'orelse', Op1, Op2}, S) ->
+ expr([Op1, Op2], S);
+expr({op, _Line, 'andalso', Op1, Op2}, S) ->
+ expr([Op1, Op2], S);
+expr({op, Line, Op, Operand1, Operand2}, S) ->
+ external_call(erlang, Op, [Operand1, Operand2], Line, false, S);
+expr({op, Line, Op, Operand}, S) ->
+ external_call(erlang, Op, [Operand], Line, false, S);
expr(T, S) when is_tuple(T) ->
expr(tuple_to_list(T), S);
expr([E | Es], S) ->
@@ -241,13 +249,13 @@ external_call(Mod, Fun, ArgsList, Line, X, S) ->
_Else -> % apply2, 1 or 2
check_funarg(W, ArgsList, Line, S1)
end.
-
+
eval_args(Mod, Fun, ArgsTerm, Line, S, ArgsList, Extra) ->
{IsSimpleCall, M, F} = mod_fun(Mod, Fun),
case term2list(ArgsTerm, [], S) of
undefined ->
S1 = unresolved(M, F, -1, Line, S),
- expr(ArgsList, S1);
+ expr(ArgsList, S1);
ArgsList2 when not IsSimpleCall ->
S1 = unresolved(M, F, length(ArgsList2), Line, S),
expr(ArgsList, S1);
@@ -288,14 +296,14 @@ fun_args(apply2, [FunArg, Args]) -> {FunArg, Args};
fun_args(1, [FunArg | Args]) -> {FunArg, Args};
fun_args(2, [_Node, FunArg | Args]) -> {FunArg, Args}.
-list2term([A | As]) ->
+list2term([A | As]) ->
{cons, 0, A, list2term(As)};
-list2term([]) ->
+list2term([]) ->
{nil, 0}.
term2list({cons, _Line, H, T}, L, S) ->
term2list(T, [H | L], S);
-term2list({nil, _Line}, L, _S) ->
+term2list({nil, _Line}, L, _S) ->
reverse(L);
term2list({var, _, Var}, L, S) ->
case keysearch(Var, 1, S#xrefr.matches) of
@@ -332,11 +340,11 @@ handle_call(Locality, To0, Line, S, IsUnres) ->
true ->
S
end,
- case Locality of
- local ->
+ case Locality of
+ local ->
S1#xrefr{el = [Call | S1#xrefr.el],
l_call_at = [CallAt | S1#xrefr.l_call_at]};
- external ->
+ external ->
S1#xrefr{ex = [Call | S1#xrefr.ex],
x_call_at = [CallAt | S1#xrefr.x_call_at]}
end.
diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl
index 0ef199cec7..9d4a175d88 100644
--- a/lib/tools/src/xref_utils.erl
+++ b/lib/tools/src/xref_utils.erl
@@ -18,6 +18,8 @@
%%
-module(xref_utils).
+%% Avoid warning for local function error/1 clashing with autoimported BIF.
+-compile({no_auto_import,[error/1]}).
-export([xset/2]).
-export([is_directory/1, file_info/1, fa_to_mfa/2]).
diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile
index 3a59be758a..8019b7269f 100644
--- a/lib/tools/test/Makefile
+++ b/lib/tools/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1997-2010. All Rights Reserved.
+# Copyright Ericsson AB 1997-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -39,7 +39,8 @@ INSTALL_PROGS= $(TARGET_FILES)
EMAKEFILE=Emakefile
-SPEC_FILES= tools.spec tools.spec.win
+SPEC_FILES= tools.spec
+COVER_FILE = tools.cover
# ----------------------------------------------------
# Release directory specification
@@ -84,8 +85,9 @@ release_spec: opt
release_tests_spec: make_emakefile
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) $(SPEC_FILES) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR)
- chmod -f -R u+w $(RELSYSDIR)
+ $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(EMAKEFILE) \
+ $(ERL_FILES) $(RELSYSDIR)
+ chmod -R u+w $(RELSYSDIR)
@tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
release_docs_spec:
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index b9ccd62d0b..b5c8e8a1b7 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,13 +18,16 @@
%%
-module(cover_SUITE).
--export([all/1]).
+-export([all/0, init_per_testcase/2, end_per_testcase/2,
+ suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+
-export([start/1, compile/1, analyse/1, misc/1, stop/1,
distribution/1, export_import/1,
otp_5031/1, eif/1, otp_5305/1, otp_5418/1, otp_6115/1, otp_7095/1,
otp_8188/1, otp_8270/1, otp_8273/1, otp_8340/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%%----------------------------------------------------------------------
%% The following directory structure is assumed:
@@ -37,18 +40,55 @@
%% y
%%----------------------------------------------------------------------
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
case whereis(cover_server) of
undefined ->
[start, compile, analyse, misc, stop, distribution,
- export_import,
- otp_5031, eif, otp_5305, otp_5418, otp_6115, otp_7095,
- otp_8188, otp_8270, otp_8273, otp_8340];
+ export_import, otp_5031, eif, otp_5305, otp_5418,
+ otp_6115, otp_7095, otp_8188, otp_8270, otp_8273,
+ otp_8340];
_pid ->
- {skip,"It looks like the test server is running cover. "
- "Can't run cover test."}
+ {skip,
+ "It looks like the test server is running "
+ "cover. Can't run cover test."}
end.
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(TC, Config) when TC =:= misc;
+ TC =:= compile;
+ TC =:= analyse;
+ TC =:= distribution;
+ TC =:= otp_5031;
+ TC =:= stop ->
+ case code:which(crypto) of
+ Path when is_list(Path) ->
+ init_per_testcase(dummy_tc, Config);
+ _Else ->
+ {skip, "No crypto file to test with"}
+ end;
+init_per_testcase(_TestCase, Config) ->
+ Config.
+
+end_per_testcase(_TestCase, _Config) ->
+ %cover:stop(),
+ ok.
+
start(suite) -> [];
start(Config) when is_list(Config) ->
?line ok = file:set_cwd(?config(data_dir, Config)),
@@ -331,6 +371,7 @@ distribution(Config) when is_list(Config) ->
%% Check that stop() unloads on all nodes
?line ok = cover:stop(),
+ ?line timer:sleep(100), %% Give nodes time to unload on slow machines.
?line LocalBeam = code:which(f),
?line N2Beam = rpc:call(N2,code,which,[f]),
?line true = is_unloaded(LocalBeam),
@@ -381,8 +422,8 @@ export_import(Config) when is_list(Config) ->
?line {ok,a} = cover:compile(a),
?line ?t:capture_start(),
?line ok = cover:export("all_exported"),
- ?line [Text2] = ?t:capture_get(),
- ?line "Export includes data from imported files"++_ = lists:flatten(Text2),
+ ?line [] = ?t:capture_get(),
+% ?line "Export includes data from imported files"++_ = lists:flatten(Text2),
?line ?t:capture_stop(),
?line ok = cover:stop(),
?line ok = cover:import("all_exported"),
diff --git a/lib/tools/test/cprof_SUITE.erl b/lib/tools/test/cprof_SUITE.erl
index e697cc1571..ce5cf66a14 100644
--- a/lib/tools/test/cprof_SUITE.erl
+++ b/lib/tools/test/cprof_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -41,7 +41,7 @@
-define(config(A,B),config(A,B)).
-export([config/2]).
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-endif.
-ifdef(debug).
@@ -63,14 +63,17 @@ config(data_dir, _) ->
"cprof_SUITE_data".
-else.
%% When run in test server.
--export([all/1, init_per_testcase/2, fin_per_testcase/2, not_run/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2,
+ not_run/1]).
-export([basic/1, on_load/1, modules/1]).
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(test_server:seconds(30)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
erlang:trace_pattern({'_','_','_'}, false, [local,meta,call_count]),
erlang:trace_pattern(on_load, false, [local,meta,call_count]),
erlang:trace(all, false, [all]),
@@ -78,16 +81,30 @@ fin_per_testcase(_Case, Config) ->
test_server:timetrap_cancel(Dog),
ok.
-all(doc) ->
- ["Test the cprof profiling tool."];
-all(suite) ->
- case test_server:is_native(?MODULE) of
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case test_server:is_native(cprof_SUITE) of
true -> [not_run];
false -> [basic, on_load, modules]
-%, on_and_off, info,
-% pause_and_restart, combo]
end.
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
not_run(Config) when is_list(Config) ->
{skipped,"Native code"}.
diff --git a/lib/tools/test/emem_SUITE.erl b/lib/tools/test/emem_SUITE.erl
index 430fa86c6c..11fb8bec68 100644
--- a/lib/tools/test/emem_SUITE.erl
+++ b/lib/tools/test/emem_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,7 +24,8 @@
receive_and_save_trace/2, send_trace/2]).
--export([all/1, init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_testcase/2, end_per_testcase/2]).
-export([live_node/1,
'sparc_sunos5.8_32b_emt2.0'/1,
@@ -41,7 +42,7 @@
-include_lib("kernel/include/file.hrl").
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(DEFAULT_TIMEOUT, ?t:minutes(5)).
@@ -65,23 +66,32 @@
%%
%%
-all(doc) -> [];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
case is_debug_compiled() of
- true -> {skipped, "Not run when debug compiled"};
+ true -> {skip, "Not run when debug compiled"};
false -> test_cases()
end.
-
-test_cases() ->
- [live_node,
- 'sparc_sunos5.8_32b_emt2.0',
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+test_cases() ->
+ [live_node, 'sparc_sunos5.8_32b_emt2.0',
'pc_win2000_32b_emt2.0',
'pc.smp_linux2.2.19pre17_32b_emt2.0',
'powerpc_darwin7.7.0_32b_emt2.0',
'alpha_osf1v5.1_64b_emt2.0',
'sparc_sunos5.8_64b_emt2.0',
- 'sparc_sunos5.8_32b_emt1.0',
- 'pc_win2000_32b_emt1.0',
+ 'sparc_sunos5.8_32b_emt1.0', 'pc_win2000_32b_emt1.0',
'powerpc_darwin7.7.0_32b_emt1.0',
'alpha_osf1v5.1_64b_emt1.0',
'sparc_sunos5.8_64b_emt1.0'].
@@ -100,7 +110,7 @@ init_per_testcase(Case, Config) when is_list(Config) ->
[{watchdog, Dog}, {testcase, Case} | Config])
end.
-fin_per_testcase(_Case, Config) when is_list(Config) ->
+end_per_testcase(_Case, Config) when is_list(Config) ->
ignore_cores:restore(Config),
Dog = ?config(watchdog, Config),
?t:timetrap_cancel(Dog),
@@ -700,8 +710,8 @@ start_node(Name, Args) ->
% stop_node(Node) ->
% ?t:stop_node(Node).
-is_debug_compiled() ->
- is_debug_compiled(erlang:system_info(system_version)).
+is_debug_compiled() ->
+is_debug_compiled(erlang:system_info(system_version)).
is_debug_compiled([$d,$e,$b,$u,$g | _]) ->
true;
diff --git a/lib/tools/test/eprof_SUITE.erl b/lib/tools/test/eprof_SUITE.erl
index 028fea8fe1..ecdbc5ce57 100644
--- a/lib/tools/test/eprof_SUITE.erl
+++ b/lib/tools/test/eprof_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,111 @@
%%
-module(eprof_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
--export([all/1,tiny/1,eed/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,tiny/1,eed/1,basic/1]).
-all(suite) -> [tiny,eed].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+all() ->
+ [basic, tiny, eed].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
+basic(suite) -> [];
+basic(Config) when is_list(Config) ->
+
+ %% load eprof_test and change directory
+
+ ?line {ok, OldCurDir} = file:get_cwd(),
+ Datadir = ?config(data_dir, Config),
+ Privdir = ?config(priv_dir, Config),
+ ?line {ok,eprof_test} = compile:file(filename:join(Datadir, "eprof_test"),
+ [trace,{outdir, Privdir}]),
+ ?line ok = file:set_cwd(Privdir),
+ ?line code:purge(eprof_test),
+ ?line {module,eprof_test} = code:load_file(eprof_test),
+
+ %% rootset profiling
+
+ ?line ensure_eprof_stopped(),
+ ?line profiling = eprof:profile([self()]),
+ ?line {error, already_profiling} = eprof:profile([self()]),
+ ?line profiling_stopped = eprof:stop_profiling(),
+ ?line profiling_already_stopped = eprof:stop_profiling(),
+ ?line profiling = eprof:start_profiling([self(),self(),self()]),
+ ?line profiling_stopped = eprof:stop_profiling(),
+
+ %% with patterns
+
+ ?line profiling = eprof:start_profiling([self()], {?MODULE, '_', '_'}),
+ ?line {error, already_profiling} = eprof:start_profiling([self()], {?MODULE, '_', '_'}),
+ ?line profiling_stopped = eprof:stop_profiling(),
+ ?line profiling = eprof:start_profiling([self()], {?MODULE, start_stop, '_'}),
+ ?line profiling_stopped = eprof:stop_profiling(),
+ ?line profiling = eprof:start_profiling([self()], {?MODULE, start_stop, 1}),
+ ?line profiling_stopped = eprof:stop_profiling(),
+
+ %% with fun
+
+ ?line {ok, _} = eprof:profile(fun() -> eprof_test:go(10) end),
+ ?line profiling = eprof:profile([self()]),
+ ?line {error, already_profiling} = eprof:profile(fun() -> eprof_test:go(10) end),
+ ?line profiling_stopped = eprof:stop_profiling(),
+ ?line {ok, _} = eprof:profile(fun() -> eprof_test:go(10) end),
+ ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end),
+ ?line Pid = whereis(eprof),
+ ?line {ok, _} = eprof:profile(erlang:processes() -- [Pid], fun() -> eprof_test:go(10) end),
+ ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, '_', '_'}),
+ ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, go, '_'}),
+ ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, go, 1}),
+ ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, dec, 1}),
+
+ %% error case
+
+ ?line error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
+ ?line Pid = whereis(eprof),
+ ?line error = eprof:profile([Pid], fun() -> eprof_test:go(10) end),
+ ?line A = spawn(fun() -> receive _ -> ok end end),
+ ?line profiling = eprof:profile([A]),
+ ?line true = exit(A, kill_it),
+ ?line profiling_stopped = eprof:stop_profiling(),
+ ?line {error,_} = eprof:profile(fun() -> a = b end),
+
+ %% with mfa
+
+ ?line {ok, _} = eprof:profile([], eprof_test, go, [10]),
+ ?line {ok, _} = eprof:profile([], eprof_test, go, [10], {eprof_test, dec, 1}),
+
+ %% dump
+
+ ?line {ok, _} = eprof:profile([], fun() -> eprof_test:go(10) end, {eprof_test, '_', '_'}),
+ ?line [{_, Mfas}] = eprof:dump(),
+ ?line Dec_mfa = {eprof_test, dec, 1},
+ ?line Go_mfa = {eprof_test, go, 1},
+ ?line {value, {Go_mfa, { 1, _Time1}}} = lists:keysearch(Go_mfa, 1, Mfas),
+ ?line {value, {Dec_mfa, {11, _Time2}}} = lists:keysearch(Dec_mfa, 1, Mfas),
+
+ %% change current working directory
+
+ ?line ok = file:set_cwd(OldCurDir),
+ ?line stopped = eprof:stop(),
+ ok.
tiny(suite) -> [];
tiny(Config) when is_list(Config) ->
@@ -40,11 +139,11 @@ tiny(Config) when is_list(Config) ->
?line code:purge(eprof_suite_test),
?line {module,eprof_suite_test} = code:load_file(eprof_suite_test),
?line {ok,_Pid} = eprof:start(),
- ?line nothing_to_analyse = eprof:analyse(),
- ?line nothing_to_analyse = eprof:total_analyse(),
+ ?line nothing_to_analyze = eprof:analyze(),
+ ?line nothing_to_analyze = eprof:analyze(total),
?line eprof:profile([], eprof_suite_test, test, [Config]),
- ?line ok = eprof:analyse(),
- ?line ok = eprof:total_analyse(),
+ ?line ok = eprof:analyze(),
+ ?line ok = eprof:analyze(total),
?line ok = eprof:log("eprof_SUITE_logfile"),
?line stopped = eprof:stop(),
?line ?t:timetrap_cancel(TTrap),
@@ -79,8 +178,8 @@ eed(Config) when is_list(Config) ->
?line {ok,ok} = eprof:profile([], eed, file, [Script]),
?line {T3,_} = statistics(runtime),
?line profiling_already_stopped = eprof:stop_profiling(),
- ?line ok = eprof:analyse(),
- ?line ok = eprof:total_analyse(),
+ ?line ok = eprof:analyze(),
+ ?line ok = eprof:analyze(total),
?line ok = eprof:log("eprof_SUITE_logfile"),
?line stopped = eprof:stop(),
?line ?t:timetrap_cancel(TTrap),
diff --git a/lib/tools/test/eprof_SUITE_data/eprof_test.erl b/lib/tools/test/eprof_SUITE_data/eprof_test.erl
new file mode 100644
index 0000000000..33c428e893
--- /dev/null
+++ b/lib/tools/test/eprof_SUITE_data/eprof_test.erl
@@ -0,0 +1,9 @@
+-module(eprof_test).
+-export([go/1]).
+
+go(N) ->
+ 0 = dec(N),
+ ok.
+
+dec(0) -> 0;
+dec(N) -> dec(N - 1).
diff --git a/lib/tools/test/fprof_SUITE.erl b/lib/tools/test/fprof_SUITE.erl
index e437007e76..0da6d4a9ea 100644
--- a/lib/tools/test/fprof_SUITE.erl
+++ b/lib/tools/test/fprof_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2001-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,10 +18,11 @@
%%
-module(fprof_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server framework exports
--export([all/1, not_run/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, not_run/1]).
%% Test suites
-export([stack_seq/1, tail_seq/1, create_file_slow/1, spawn_simple/1,
@@ -54,18 +55,33 @@
-all(doc) ->
- ["Test the 'fprof' profiling tool."];
-all(suite) ->
- case test_server:is_native(?MODULE) of
- true ->
- [not_run];
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case test_server:is_native(fprof_SUITE) of
+ true -> [not_run];
false ->
[stack_seq, tail_seq, create_file_slow, spawn_simple,
imm_tail_seq, imm_create_file_slow, imm_compile,
cpu_create_file_slow]
end.
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
not_run(Config) when is_list(Config) ->
{skipped, "Native code"}.
@@ -356,7 +372,7 @@ imm_tail_seq(Config) when is_list(Config) ->
?line profiling_stopped = eprof:stop_profiling(),
?line R2 = R0,
%%
- ?line eprof:analyse(),
+ ?line eprof:analyze(),
?line stopped = eprof:stop(),
%%
?line {ok, Tracer} = fprof:profile(start),
@@ -471,7 +487,7 @@ imm_compile(Config) when is_list(Config) ->
?line TS3 = erlang:now(),
?line profiling_stopped = eprof:stop_profiling(),
%%
- ?line eprof:analyse(),
+ ?line eprof:analyze(),
?line stopped = eprof:stop(),
%%
?line {ok, Tracer} = fprof:profile(start),
diff --git a/lib/tools/test/ignore_cores.erl b/lib/tools/test/ignore_cores.erl
index 8902a469ef..8b1ac0fe6c 120000..100644
--- a/lib/tools/test/ignore_cores.erl
+++ b/lib/tools/test/ignore_cores.erl
@@ -1 +1,158 @@
-../../../erts/test/ignore_cores.erl \ No newline at end of file
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%-------------------------------------------------------------------
+%%% File : ignore_cores.erl
+%%% Author : Rickard Green <[email protected]>
+%%% Description :
+%%%
+%%% Created : 11 Feb 2008 by Rickard Green <[email protected]>
+%%%-------------------------------------------------------------------
+
+-module(ignore_cores).
+
+-include_lib("test_server/include/test_server.hrl").
+
+-export([init/1, fini/1, setup/3, setup/4, restore/1, dir/1]).
+
+-record(ignore_cores, {org_cwd,
+ org_path,
+ org_pwd_env,
+ ign_dir = false,
+ cores_dir = false}).
+
+%%
+%% Takes a testcase config
+%%
+
+init(Config) ->
+ {ok, OrgCWD} = file:get_cwd(),
+ [{ignore_cores,
+ #ignore_cores{org_cwd = OrgCWD,
+ org_path = code:get_path(),
+ org_pwd_env = os:getenv("PWD")}}
+ | lists:keydelete(ignore_cores, 1, Config)].
+
+fini(Config) ->
+ #ignore_cores{org_cwd = OrgCWD,
+ org_path = OrgPath,
+ org_pwd_env = OrgPWD} = ?config(ignore_cores, Config),
+ ok = file:set_cwd(OrgCWD),
+ true = code:set_path(OrgPath),
+ case OrgPWD of
+ false -> ok;
+ _ -> true = os:putenv("PWD", OrgPWD)
+ end,
+ lists:keydelete(ignore_cores, 1, Config).
+
+setup(Suite, Testcase, Config) ->
+ setup(Suite, Testcase, Config, false).
+
+setup(Suite, Testcase, Config, SetCwd) when is_atom(Suite),
+ is_atom(Testcase),
+ is_list(Config) ->
+ #ignore_cores{org_cwd = OrgCWD,
+ org_path = OrgPath,
+ org_pwd_env = OrgPWD} = ?config(ignore_cores, Config),
+ Path = lists:map(fun (".") -> OrgCWD; (Dir) -> Dir end, OrgPath),
+ true = code:set_path(Path),
+ PrivDir = ?config(priv_dir, Config),
+ IgnDir = filename:join([PrivDir,
+ atom_to_list(Suite)
+ ++ "_"
+ ++ atom_to_list(Testcase)
+ ++ "_wd"]),
+ ok = file:make_dir(IgnDir),
+ case SetCwd of
+ false ->
+ ok;
+ _ ->
+ ok = file:set_cwd(IgnDir),
+ OrgPWD = case os:getenv("PWD") of
+ false -> false;
+ PWD ->
+ os:putenv("PWD", IgnDir),
+ PWD
+ end
+ end,
+ ok = file:write_file(filename:join([IgnDir, "ignore_core_files"]), <<>>),
+ %% cores are dumped in /cores on MacOS X
+ CoresDir = case {?t:os_type(), filelib:is_dir("/cores")} of
+ {{unix,darwin}, true} ->
+ filelib:fold_files("/cores",
+ "^core.*$",
+ false,
+ fun (C,Cs) -> [C|Cs] end,
+ []);
+ _ ->
+ false
+ end,
+ lists:keyreplace(ignore_cores,
+ 1,
+ Config,
+ {ignore_cores,
+ #ignore_cores{org_cwd = OrgCWD,
+ org_path = OrgPath,
+ org_pwd_env = OrgPWD,
+ ign_dir = IgnDir,
+ cores_dir = CoresDir}}).
+
+restore(Config) ->
+ #ignore_cores{org_cwd = OrgCWD,
+ org_path = OrgPath,
+ org_pwd_env = OrgPWD,
+ ign_dir = IgnDir,
+ cores_dir = CoresDir} = ?config(ignore_cores, Config),
+ try
+ case CoresDir of
+ false ->
+ ok;
+ _ ->
+ %% Move cores dumped by these testcases in /cores
+ %% to cwd.
+ lists:foreach(fun (C) ->
+ case lists:member(C, CoresDir) of
+ true -> ok;
+ _ ->
+ Dst = filename:join(
+ [IgnDir,
+ filename:basename(C)]),
+ {ok, _} = file:copy(C, Dst),
+ file:delete(C)
+ end
+ end,
+ filelib:fold_files("/cores",
+ "^core.*$",
+ false,
+ fun (C,Cs) -> [C|Cs] end,
+ []))
+ end
+ after
+ catch file:set_cwd(OrgCWD),
+ catch code:set_path(OrgPath),
+ case OrgPWD of
+ false -> ok;
+ _ -> catch os:putenv("PWD", OrgPWD)
+ end
+ end.
+
+
+dir(Config) ->
+ #ignore_cores{ign_dir = Dir} = ?config(ignore_cores, Config),
+ Dir.
diff --git a/lib/tools/test/instrument_SUITE.erl b/lib/tools/test/instrument_SUITE.erl
index da5930e015..bc886d47c3 100644
--- a/lib/tools/test/instrument_SUITE.erl
+++ b/lib/tools/test/instrument_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,22 +18,43 @@
%%
-module(instrument_SUITE).
--export([all/1,init_per_testcase/2,fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ init_per_testcase/2,end_per_testcase/2]).
-export(['+Mim true'/1, '+Mis true'/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
init_per_testcase(_Case, Config) ->
?line Dog=?t:timetrap(10000),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
?t:timetrap_cancel(Dog),
ok.
-all(suite) -> ['+Mim true', '+Mis true'].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ ['+Mim true', '+Mis true'].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
'+Mim true'(doc) -> ["Check that memory data can be read and processed"];
'+Mim true'(suite) -> [];
diff --git a/lib/tools/test/lcnt_SUITE.erl b/lib/tools/test/lcnt_SUITE.erl
index e6866f721d..f2afa60e33 100644
--- a/lib/tools/test/lcnt_SUITE.erl
+++ b/lib/tools/test/lcnt_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,10 +18,10 @@
%%
-module(lcnt_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Test server specific exports
--export([all/1]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]).
-export([init_per_suite/1, end_per_suite/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -51,10 +51,21 @@ end_per_testcase(_Case, Config) ->
?t:timetrap_cancel(Dog),
ok.
-all(suite) ->
- % Test cases
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[load_v1, conflicts, locations, swap_keys].
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%%----------------------------------------------------------------------
%% Tests
%%----------------------------------------------------------------------
diff --git a/lib/tools/test/make_SUITE.erl b/lib/tools/test/make_SUITE.erl
index 72dccdb465..b1a65226de 100644
--- a/lib/tools/test/make_SUITE.erl
+++ b/lib/tools/test/make_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,12 +18,13 @@
%%
-module(make_SUITE).
--export([all/1, make_all/1, make_files/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, make_all/1, make_files/1]).
-export([otp_6057_init/1,
otp_6057_a/1, otp_6057_b/1, otp_6057_c/1,
otp_6057_end/1]).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-include_lib("kernel/include/file.hrl").
@@ -35,9 +36,27 @@
%% that the file :"test5.erl" shall be compiled with the 'S' option,
%% i.e. produce "test5.S" instead of "test5.<objext>"
-all(suite) -> [make_all, make_files,
- {conf, otp_6057_init,
- [otp_6057_a,otp_6057_b,otp_6057_c], otp_6057_end}].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [make_all, make_files, {group, otp_6057}].
+
+groups() ->
+ [{otp_6057,[],[otp_6057_a, otp_6057_b,
+ otp_6057_c]}].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ otp_6057_init(Config).
+
+end_per_group(_GroupName, Config) ->
+ otp_6057_end(Config).
+
test_files() -> ["test1", "test2", "test3", "test4"].
@@ -146,7 +165,7 @@ otp_6057_init(Config) when is_list(Config) ->
otp_6057_a(suite) ->
[];
otp_6057_a(doc) ->
- ["Test that make:all/0 looks for object file in correct place"];
+ ["Test that make:all/0, suite/0 looks for object file in correct place"];
otp_6057_a(Config) when is_list(Config) ->
?line PrivDir = ?config(priv_dir, Config),
diff --git a/lib/tools/test/tools.cover b/lib/tools/test/tools.cover
new file mode 100644
index 0000000000..1053be4f0f
--- /dev/null
+++ b/lib/tools/test/tools.cover
@@ -0,0 +1,2 @@
+{incl_app,tools,details}.
+
diff --git a/lib/tools/test/tools.spec b/lib/tools/test/tools.spec
index 93d5930472..1b07cf1cb6 100644
--- a/lib/tools/test/tools.spec
+++ b/lib/tools/test/tools.spec
@@ -1 +1 @@
-{topcase, {dir, "../tools_test"}}.
+{suites,"../tools_test",all}.
diff --git a/lib/tools/test/tools_SUITE.erl b/lib/tools/test/tools_SUITE.erl
index 6b952f10ab..ea3f59dbe1 100644
--- a/lib/tools/test/tools_SUITE.erl
+++ b/lib/tools/test/tools_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -18,28 +18,45 @@
%%
-module(tools_SUITE).
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
%% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
-define(application, tools).
%% Test server specific exports
--export([all/1]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%% Test cases must be exported.
-export([app_test/1]).
-all(doc) ->
- [];
-all(suite) ->
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
[app_test].
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
init_per_testcase(_Case, Config) ->
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, Config) ->
Dog=?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl
index a7855b0bb9..2f83ab4995 100644
--- a/lib/tools/test/xref_SUITE.erl
+++ b/lib/tools/test/xref_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,28 +29,29 @@
-define(privdir, "xref_SUITE_priv").
-define(copydir, "xref_SUITE_priv/datacopy").
-else.
--include("test_server.hrl").
+-include_lib("test_server/include/test_server.hrl").
-define(format(S, A), ok).
-define(datadir, ?config(data_dir, Conf)).
-define(privdir, ?config(priv_dir, Conf)).
-define(copydir, ?config(copy_dir, Conf)).
-endif.
--export([all/1, init/1, fini/1]).
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2, init/1, fini/1]).
--export([xref/1,
- addrem/1, convert/1, intergraph/1, lines/1, loops/1,
+-export([
+ addrem/1, convert/1, intergraph/1, lines/1, loops/1,
no_data/1, modules/1]).
--export([files/1,
- add/1, default/1, info/1, lib/1, read/1, read2/1, remove/1,
+-export([
+ add/1, default/1, info/1, lib/1, read/1, read2/1, remove/1,
replace/1, update/1, deprecated/1, trycatch/1,
abstract_modules/1, fun_mfa/1, qlc/1]).
--export([analyses/1,
+-export([
analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]).
--export([misc/1,
+-export([
format_error/1, otp_7423/1, otp_7831/1]).
-import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]).
@@ -59,7 +60,7 @@
range/1, relation_to_family/1, set/1, to_external/1,
union/2]).
--export([init_per_testcase/2, fin_per_testcase/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
%% Checks some info counters of a server and some relations that should hold.
-export([check_count/1, check_state/1]).
@@ -68,8 +69,36 @@
-include_lib("tools/src/xref.hrl").
-all(suite) ->
- {conf, init, [xref, files, analyses, misc], fini}.
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, xref}, {group, files}, {group, analyses},
+ {group, misc}].
+
+groups() ->
+ [{xref, [],
+ [addrem, convert, intergraph, lines, loops, no_data,
+ modules]},
+ {files, [],
+ [add, default, info, lib, read, read2, remove, replace,
+ update, deprecated, trycatch, abstract_modules, fun_mfa,
+ qlc]},
+ {analyses, [],
+ [analyze, basic, md, q, variables, unused_locals]},
+ {misc, [], [format_error, otp_7423, otp_7831]}].
+
+init_per_suite(Config) ->
+ init(Config).
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
init(Conf) when is_list(Conf) ->
DataDir = ?datadir,
@@ -82,7 +111,7 @@ init(Conf) when is_list(Conf) ->
?line ok = erl_tar:extract(TarFile, [compressed]),
?line ok = file:delete(TarFile),
[{copy_dir, CopyDir} | Conf].
-
+
fini(Conf) when is_list(Conf) ->
%% Nothing.
Conf.
@@ -91,13 +120,11 @@ init_per_testcase(_Case, Config) ->
Dog=?t:timetrap(?t:minutes(2)),
[{watchdog, Dog}|Config].
-fin_per_testcase(_Case, _Config) ->
+end_per_testcase(_Case, _Config) ->
Dog=?config(watchdog, _Config),
test_server:timetrap_cancel(Dog),
ok.
-xref(suite) ->
- [addrem, convert, intergraph, lines, loops, no_data, modules].
%% Seems a bit short...
addrem(suite) -> [];
@@ -120,7 +147,7 @@ addrem(Conf) when is_list(Conf) ->
LCallAt_m1 = [],
XCallAt_m1 = [{E1,13}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
D2 = {F2,7},
@@ -132,7 +159,7 @@ addrem(Conf) when is_list(Conf) ->
LCallAt_m2 = [],
XCallAt_m2 = [{E2,96}],
Info2 = #xref_mod{name = m2, app_name = [a2]},
- ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
+ ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
XC_m2, LC_m2),
?line S5 = set_up(S2),
@@ -142,7 +169,7 @@ addrem(Conf) when is_list(Conf) ->
?line {ok, XMod2, S6a} = remove_module(S6, m2),
?line [a2] = XMod2#xref_mod.app_name,
?line S7 = set_up(S6a),
-
+
?line AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
?line S9 = add_application(S7, AppInfo1),
?line S10 = set_up(S9),
@@ -186,7 +213,7 @@ convert(Conf) when is_list(Conf) ->
LCallAt_m1 = [],
XCallAt_m1 = [{E1,13},{E2,17},{E4,7}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
D2 = {F2,7},
@@ -200,7 +227,7 @@ convert(Conf) when is_list(Conf) ->
LCallAt_m2 = [],
XCallAt_m2 = [{E3,96},{E6,12},{UE1,77}],
Info2 = #xref_mod{name = m2, app_name = [a2]},
- ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
+ ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
XC_m2, LC_m2),
D4 = {F4,6},
@@ -213,7 +240,7 @@ convert(Conf) when is_list(Conf) ->
LCallAt_m3 = [{E5,19}],
XCallAt_m3 = [{UE2,22}],
Info3 = #xref_mod{name = m3, app_name = [a3]},
- ?line S3 = add_module(S2, Info3, DefAt_m3, X_m3, LCallAt_m3, XCallAt_m3,
+ ?line S3 = add_module(S2, Info3, DefAt_m3, X_m3, LCallAt_m3, XCallAt_m3,
XC_m3, LC_m3),
Info4 = #xref_mod{name = m4, app_name = [a2]},
@@ -303,7 +330,7 @@ convert(Conf) when is_list(Conf) ->
?line {ok, _} = eval("(XXL) (Lin) (Fun) E", AllCallAt, S),
?line {ok, _} = eval("(XXL) (XXL) (Lin) (Fun) E", AllCallAt, S),
- ?line {ok, _} = eval(f("(XXL) (Lin) ~p", [[E1, E6]]),
+ ?line {ok, _} = eval(f("(XXL) (Lin) ~p", [[E1, E6]]),
[{{D1,D3},[13]}, {{D7,D4},[12]}], S),
?line {ok, _} = eval(f("(Fun) ~p", [AllMs]), AllE, S),
?line {ok, _} = eval("(Fun) [m1->m2,m2->m3]", [E1,E2,E6], S),
@@ -323,7 +350,7 @@ intergraph(Conf) when is_list(Conf) ->
F3 = {m1,f3,3},
F4 = {m1,f4,4},
F5 = {m1,f5,5},
-
+
F6 = {m2,f1,6}, % X
F7 = {m2,f1,7},
F8 = {m2,f1,8},
@@ -339,7 +366,7 @@ intergraph(Conf) when is_list(Conf) ->
E5 = {F4,F2},
E6 = {F5,F4},
E7 = {F4,F5},
-
+
E8 = {F6,F7},
E9 = {F7,F8},
E10 = {F8,F1}, % X
@@ -363,9 +390,9 @@ intergraph(Conf) when is_list(Conf) ->
LCallAt_m1 = [{E1,1},{E2,2},{E3,3},{E5,5},{E6,6},{E7,7}],
XCallAt_m1 = [{E1,4}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
-
+
D6 = {F6,6},
D7 = {F7,7},
D8 = {F8,8},
@@ -380,7 +407,7 @@ intergraph(Conf) when is_list(Conf) ->
LCallAt_m2 = [{E8,8},{E9,9},{E11,11},{E12,12},{E13,13},{E14,14}],
XCallAt_m2 = [{E10,10},{E15,15}],
Info2 = #xref_mod{name = m2, app_name = [a2]},
- ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
+ ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
XC_m2, LC_m2),
AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
@@ -397,13 +424,13 @@ intergraph(Conf) when is_list(Conf) ->
?line {ok, _} = eval("EE | m2", [{F6,F1}], S),
?line {ok, _} = eval("EE | m2 + EE | m2", [{F6,F1}], S),
- ?line {ok, _} = eval("(Fun)(Lin)(E | m1)",
+ ?line {ok, _} = eval("(Fun)(Lin)(E | m1)",
to_external(union(set(XC_m1), set(LC_m1))), S),
- ?line {ok, _} = eval("(XXL)(ELin) (EE | m1)",
- [{{D2,D1},[1,2,4]},{{D4,D2},[5]},{{D5,D4},[6]},{{D4,D5},[7]}],
+ ?line {ok, _} = eval("(XXL)(ELin) (EE | m1)",
+ [{{D2,D1},[1,2,4]},{{D4,D2},[5]},{{D5,D4},[6]},{{D4,D5},[7]}],
S),
?line {ok, _} = eval("(XXL)(ELin)(EE | m2)", [{{D6,D1},[8,11,12]}], S),
- ?line {ok, _} = eval("(XXL)(ELin)(ELin)(EE | m2)",
+ ?line {ok, _} = eval("(XXL)(ELin)(ELin)(EE | m2)",
[{{D6,D1},[8,11,12]}], S),
%% Combining graphs (equal or different):
@@ -420,15 +447,15 @@ intergraph(Conf) when is_list(Conf) ->
?line {ok, _} = eval("EE | m1 + E | m1", LC_m1, S),
?line {ok, _} = eval(f("EE | ~p + E | ~p", [F2, F2]), [E1,E2], S),
%% [1,4] from 'calls' is a subset of [1,2,4] from Inter Call Graph:
- ?line {ok, _} = eval(f("(XXL)(Lin) (E | ~p)", [F2]),
+ ?line {ok, _} = eval(f("(XXL)(Lin) (E | ~p)", [F2]),
[{{D2,D1},[1,4]},{{D2,D3},[2]}], S),
- ?line {ok, _} = eval(f("(XXL)(ELin) (EE | ~p)", [F2]),
+ ?line {ok, _} = eval(f("(XXL)(ELin) (EE | ~p)", [F2]),
[{{D2,D1},[1,2,4]}], S),
?line {ok, _} = eval(f("(XXL)((ELin)(EE | ~p) + (Lin)(E | ~p))", [F2, F2]),
[{{D2,D1},[1,2,4]},{{D2,D3},[2]}], S),
- ?line {ok, _} =
- eval(f("(XXL)((ELin) ~p + (Lin) ~p)", [{F2, F1}, {F2, F1}]),
+ ?line {ok, _} =
+ eval(f("(XXL)((ELin) ~p + (Lin) ~p)", [{F2, F1}, {F2, F1}]),
[{{D2,D1},[1,2,4]}], S),
?line {ok, _} = eval(f("(Fun)(Lin) ~p", [{F2, F1}]), [E1], S),
%% The external call E4 is included in the reply:
@@ -438,7 +465,7 @@ intergraph(Conf) when is_list(Conf) ->
%% The local call E1 is included in the reply:
?line {ok, _} = eval("(XXL)(Lin)(XC | m1)", [{{D2,D1},[1,4]}], S),
- ?line {ok, _} = eval(f("(LLin) (E | ~p || ~p) + (XLin) (E | ~p || ~p)",
+ ?line {ok, _} = eval(f("(LLin) (E | ~p || ~p) + (XLin) (E | ~p || ~p)",
[F2, F1, F2, F1]), [{E4,[1,4]}], S),
?line {ok, _} = eval("# (ELin) E", 6, S),
@@ -449,7 +476,7 @@ lines(suite) -> [];
lines(doc) -> ["More test of Inter Call Graph, and regular expressions"];
lines(Conf) when is_list(Conf) ->
S0 = new(),
-
+
F1 = {m1,f1,1}, % X
F2 = {m1,f2,2},
F3 = {m1,f3,3},
@@ -464,14 +491,14 @@ lines(Conf) when is_list(Conf) ->
E5 = {F2,F4}, % X
E6 = {F5,F6},
E7 = {F6,F4}, % X
-
+
D1 = {F1,1},
D2 = {F2,2},
D3 = {F3,3},
D4 = {F4,4},
D5 = {F5,5},
D6 = {F6,6},
-
+
DefAt_m1 = [D1,D2,D3,D5,D6],
X_m1 = [F1,F5],
% L_m1 = [F2,F3,F6],
@@ -480,7 +507,7 @@ lines(Conf) when is_list(Conf) ->
LCallAt_m1 = [{E1,1},{E3,3},{E6,6}],
XCallAt_m1 = [{E2,2},{E4,4},{E5,5},{E7,7}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
DefAt_m2 = [D4],
@@ -491,9 +518,9 @@ lines(Conf) when is_list(Conf) ->
LCallAt_m2 = [],
XCallAt_m2 = [],
Info2 = #xref_mod{name = m2, app_name = [a2]},
- ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
+ ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
XC_m2, LC_m2),
-
+
AppInfo1 = #xref_app{name = a1, rel_name = [r1]},
?line S5 = add_application(S2, AppInfo1),
AppInfo2 = #xref_app{name = a2, rel_name = [r1]},
@@ -509,10 +536,10 @@ lines(Conf) when is_list(Conf) ->
{{D5,D4},[6]}], S),
?line {ok, _} = eval("(XXL)(Lin) (E | m1)",
[{{D1,D2},[1]},{{D1,D4},[4]},{{D2,D1},[2]},
- {{D2,D4},[5]},{{D3,D2},[3]},{{D5,D6},[6]},{{D6,D4},[7]}],
+ {{D2,D4},[5]},{{D3,D2},[3]},{{D5,D6},[6]},{{D6,D4},[7]}],
S),
?line {ok, _} = eval("(E | m1) + (EE | m1)",
- [E1,E2,E3,E4,E5,E6,E7,{F1,F1},{F3,F1},{F3,F4},{F5,F4}],
+ [E1,E2,E3,E4,E5,E6,E7,{F1,F1},{F3,F1},{F3,F4},{F5,F4}],
S),
?line {ok, _} = eval("(Lin)(E | m1)",
[{E4,[4]},{E1,[1]},{E2,[2]},{E5,[5]},
@@ -567,7 +594,7 @@ lines(Conf) when is_list(Conf) ->
loops(suite) -> [];
loops(doc) -> ["More Inter Call Graph, loops and \"unusual\" cases"];
loops(Conf) when is_list(Conf) ->
- S0 = new(),
+ S0 = new(),
F1 = {m1,f1,1}, % X
F2 = {m1,f2,2},
@@ -582,7 +609,7 @@ loops(Conf) when is_list(Conf) ->
E3 = {F3,F4},
E4 = {F4,F5},
E5 = {F5,F3}, % X
-
+
D1 = {F1,1},
D2 = {F2,2},
D3 = {F3,3},
@@ -598,7 +625,7 @@ loops(Conf) when is_list(Conf) ->
LCallAt_m1 = [{E2,2},{E3,3},{E4,4}],
XCallAt_m1 = [{E1,1},{E5,5}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
?line S = set_up(S1),
@@ -659,16 +686,16 @@ modules(Conf) when is_list(Conf) ->
?line {ok, y} = compile:file(Y, [debug_info, {outdir,EB1_1}]),
?line {ok, S0} = xref_base:new([{xref_mode, modules}]),
- ?line {ok, release2, S1} =
+ ?line {ok, release2, S1} =
xref_base:add_release(S0, Dir, [{name,release2}]),
?line S = set_up(S1),
?line {{error, _, {unavailable_analysis, undefined_function_calls}}, _} =
xref_base:analyze(S, undefined_function_calls),
- ?line {{error, _, {unavailable_analysis, locals_not_used}}, _} =
+ ?line {{error, _, {unavailable_analysis, locals_not_used}}, _} =
xref_base:analyze(S, locals_not_used),
- ?line {{error, _, {unavailable_analysis, {call, foo}}}, _} =
+ ?line {{error, _, {unavailable_analysis, {call, foo}}}, _} =
xref_base:analyze(S, {call, foo}),
- ?line {{error, _, {unavailable_analysis, {use, foo}}}, _} =
+ ?line {{error, _, {unavailable_analysis, {use, foo}}}, _} =
xref_base:analyze(S, {use, foo}),
?line analyze(undefined_functions, [{x,undef,0}], S),
?line 5 = length(xref_base:info(S)),
@@ -680,9 +707,6 @@ modules(Conf) when is_list(Conf) ->
?line ok = xref_base:delete(S),
ok.
-files(suite) ->
- [add, default, info, lib, read, read2, remove, replace, update,
- deprecated, trycatch, abstract_modules, fun_mfa, qlc].
add(suite) -> [];
add(doc) -> ["Add modules, applications, releases, directories"];
@@ -708,7 +732,7 @@ add(Conf) when is_list(Conf) ->
{unix, _} ->
?line make_udir(UDir),
?line make_ufile(UFile);
- _ ->
+ _ ->
true
end,
@@ -743,20 +767,20 @@ add(Conf) when is_list(Conf) ->
xref_base:add_release(S, foo, [{builtins,not_a_value}]),
?line {error, _, {invalid_filename,{foo,bar}}} =
xref_base:add_release(S, {foo,bar}, []),
- ?line {ok, S1} =
+ ?line {ok, S1} =
xref_base:set_default(S, [{verbose,false}, {warnings, false}]),
?line case os:type() of
{unix, _} ->
- ?line {error, _, {file_error, _, _}} =
+ ?line {error, _, {file_error, _, _}} =
xref_base:add_release(S, UDir);
_ ->
true
end,
- ?line {error, _, {file_error, _, _}} =
+ ?line {error, _, {file_error, _, _}} =
xref_base:add_release(S, fname(["/a/b/c/d/e/f","__foo"])),
- ?line {ok, release2, S2} =
+ ?line {ok, release2, S2} =
xref_base:add_release(S1, Dir, [{name,release2}]),
- ?line {error, _, {module_clash, {x, _, _}}} =
+ ?line {error, _, {module_clash, {x, _, _}}} =
xref_base:add_module(S2, Xbeam),
?line {ok, S3} = xref_base:remove_release(S2, release2),
?line {ok, rel2, S4} = xref_base:add_release(S3, Dir),
@@ -764,11 +788,11 @@ add(Conf) when is_list(Conf) ->
xref_base:add_release(S4, Dir),
?line {ok, S5} = xref_base:remove_release(S4, rel2),
%% One unreadable file and one JAM file found (no verification here):
- ?line {ok, [], S6} = xref_base:add_directory(S5, fname(CopyDir,"dir"),
+ ?line {ok, [], S6} = xref_base:add_directory(S5, fname(CopyDir,"dir"),
[{recurse,true}, {warnings,true}]),
?line case os:type() of
{unix, _} ->
- ?line {error, _, {file_error, _, _}} =
+ ?line {error, _, {file_error, _, _}} =
xref_base:add_directory(S6, UDir);
_ ->
true
@@ -803,7 +827,7 @@ default(Conf) when is_list(Conf) ->
xref_base:set_default(S, [not_an_option]),
?line D = xref_base:get_default(S),
- ?line [{builtins,false},{recurse,false},{verbose,false},{warnings,true}] =
+ ?line [{builtins,false},{recurse,false},{verbose,false},{warnings,true}] =
D,
?line ok = xref_base:delete(S),
@@ -831,7 +855,7 @@ info(Conf) when is_list(Conf) ->
?line {error, _, {no_such_info, release}} = xref:info(s, release),
?line {error, _, {no_such_info, release}} = xref:info(s, release, rel),
?line {error, _, {no_such_module, mod}} = xref:info(s, modules, mod),
- ?line {error, _, {no_such_application, app}} =
+ ?line {error, _, {no_such_application, app}} =
xref:info(s, applications, app),
?line {error, _, {no_such_release, rel}} = xref:info(s, releases, rel),
?line ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
@@ -845,9 +869,9 @@ info(Conf) when is_list(Conf) ->
?line [{rel2,_}] = xref:info(s, releases, rel2),
?line {error, _, {no_such_library, foo}} = xref:info(s, libraries, [foo]),
- ?line {ok, lib1} =
+ ?line {ok, lib1} =
compile:file(fname(LDir,lib1),[debug_info,{outdir,LDir}]),
- ?line {ok, lib2} =
+ ?line {ok, lib2} =
compile:file(fname(LDir,lib2),[debug_info,{outdir,LDir}]),
?line ok = xref:set_library_path(s, [LDir], [{verbose,false}]),
?line [{lib1,_}, {lib2, _}] = xref:info(s, libraries),
@@ -883,13 +907,13 @@ lib(Conf) when is_list(Conf) ->
xref:set_library_path(s, ["foo"], [not_an_option]),
?line {error, _, {invalid_path,otp}} = xref:set_library_path(s,otp),
?line {error, _, {invalid_path,[""]}} = xref:set_library_path(s,[""]),
- ?line {error, _, {invalid_path,[[$a | $b]]}} =
+ ?line {error, _, {invalid_path,[[$a | $b]]}} =
xref:set_library_path(s,[[$a | $b]]),
?line {error, _, {invalid_path,[otp]}} = xref:set_library_path(s,[otp]),
?line {ok, []} = xref:get_library_path(s),
?line ok = xref:set_library_path(s, [Dir], [{verbose,false}]),
?line {ok, UnknownFunctions} = xref:q(s, "U"),
- ?line [{lib1,unknown,0}, {lib2,local,0},
+ ?line [{lib1,unknown,0}, {lib2,local,0},
{lib2,unknown,0}, {unknown,unknown,0}]
= UnknownFunctions,
?line {ok, [{lib2,f,0},{lib3,f,0}]} = xref:q(s, "DF"),
@@ -934,7 +958,7 @@ lib(Conf) when is_list(Conf) ->
?line ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
?line {ok, cp} = xref:add_module(s, fname(Dir,"cp.beam")),
?line {ok, [{lists, sort, 1}]} = xref:q(s, "U"),
- ?line ok = xref:set_library_path(s, code_path),
+ ?line ok = xref:set_library_path(s, code_path),
?line {ok, []} = xref:q(s, "U"),
?line check_state(s),
?line xref:stop(s),
@@ -1010,18 +1034,18 @@ do_read(File, Version) ->
?line {ok, CallsB} = xref:q(s, "(Lin) (E - UC) "),
?line ok = check_state(s),
?line {ok, XU} = xref:q(s, "XU"),
- ?line Erl = set([{erlang,length,1},{erlang,integer,1},
+ ?line Erl = set([{erlang,length,1},{erlang,integer,1},
{erlang,binary_to_term,1}]),
- ?line [{erlang,binary_to_term,1},{erlang,length,1}] =
+ ?line [{erlang,binary_to_term,1},{erlang,length,1}] =
to_external(intersection(set(XU), Erl)),
- ?line xref:stop(s).
+ ?line xref:stop(s).
%% What is expected when xref_SUITE_data/read/read.erl is added:
read_expected(Version) ->
%% Line positions in xref_SUITE_data/read/read.erl:
- POS1 = 28, POS2 = POS1+10, POS3 = POS2+6, POS4 = POS3+6, POS5 = POS4+10,
- POS6 = POS5+5, POS7 = POS6+6, POS8 = POS7+6, POS9 = POS8+8,
- POS10 = POS9+10, POS11 = POS10+7, POS12 = POS11+8, POS13 = POS12+10,
+ POS1 = 28, POS2 = POS1+10, POS3 = POS2+6, POS4 = POS3+6, POS5 = POS4+10,
+ POS6 = POS5+5, POS7 = POS6+6, POS8 = POS7+6, POS9 = POS8+8,
+ POS10 = POS9+10, POS11 = POS10+7, POS12 = POS11+8, POS13 = POS12+10,
POS14 = POS13+18, % POS15 = POS14+23,
FF = {read,funfuns,0},
@@ -1162,7 +1186,7 @@ read_expected(Version) ->
{POS14+17,{{read,bi,0},{read,bi,0}}}],
OK = case Version of
- abstract_v1 ->
+ abstract_v1 ->
[{POS8+3, {FF,{erlang,apply,3}}},
{POS10+1, {FF,{erlang,apply,3}}},
{POS10+6, {FF,{erlang,apply,3}}}]
@@ -1170,7 +1194,7 @@ read_expected(Version) ->
[{0,{FF,{read,'$F_EXPR',178}}},
{0,{FF,{modul,'$F_EXPR',179}}}]
++ O1;
- _ ->
+ _ ->
% [{POS15+2,{{read,bi,0},{foo,t,0}}},
% {POS15+3,{{read,bi,0},{bar,t,0}}},
% {POS15+6,{{read,bi,0},{read,local,0}}},
@@ -1183,18 +1207,34 @@ read_expected(Version) ->
end,
%% When builtins =:= true:
- OKB = [{POS13+1,{FF,{erts_debug,apply,4}}},
- {POS13+2,{FF,{erts_debug,apply,4}}},
- {POS13+3,{FF,{erts_debug,apply,4}}},
- {POS1+3, {FF,{erlang,binary_to_term,1}}},
- {POS3+1, {FF,{erlang,spawn,3}}},
- {POS3+2, {FF,{erlang,spawn,3}}},
- {POS3+3, {FF,{erlang,spawn_link,3}}},
- {POS3+4, {FF,{erlang,spawn_link,3}}},
- {POS6+4, {FF,{erlang,spawn,3}}},
- {POS13+5, {{read,bi,0},{erlang,length,1}}},
- {POS14+3, {{read,bi,0},{erlang,length,1}}}]
- ++ OK,
+ OKB1 = [{POS13+1,{FF,{erts_debug,apply,4}}},
+ {POS13+2,{FF,{erts_debug,apply,4}}},
+ {POS13+3,{FF,{erts_debug,apply,4}}},
+ {POS1+3, {FF,{erlang,binary_to_term,1}}},
+ {POS3+1, {FF,{erlang,spawn,3}}},
+ {POS3+2, {FF,{erlang,spawn,3}}},
+ {POS3+3, {FF,{erlang,spawn_link,3}}},
+ {POS3+4, {FF,{erlang,spawn_link,3}}},
+ {POS6+4, {FF,{erlang,spawn,3}}},
+ {POS13+5, {{read,bi,0},{erlang,length,1}}},
+ {POS14+3, {{read,bi,0},{erlang,length,1}}}],
+
+ %% Operators (OTP-8647):
+ OKB = case Version of
+ abstract_v1 ->
+ [];
+ _ ->
+ [{POS13+16, {{read,bi,0},{erlang,'!',2}}},
+ {POS13+16, {{read,bi,0},{erlang,'-',1}}},
+ {POS13+16, {{read,bi,0},{erlang,self,0}}}]
+ end
+ ++ [{POS14+19, {{read,bi,0},{erlang,'+',2}}},
+ {POS14+21, {{read,bi,0},{erlang,'+',2}}},
+ {POS13+16, {{read,bi,0},{erlang,'==',2}}},
+ {POS14+15, {{read,bi,0},{erlang,'==',2}}},
+ {POS13+5, {{read,bi,0},{erlang,'>',2}}},
+ {POS14+3, {{read,bi,0},{erlang,'>',2}}}]
+ ++ OKB1 ++ OK,
{U, OK, OKB}.
@@ -1217,9 +1257,9 @@ read2(Conf) when is_list(Conf) ->
spawn_opt(fun() -> foo end, [link]),
spawn_opt(f(),
{read2,f}, [{min_heap_size,1000}]),
- spawn_opt(f(),
+ spawn_opt(f(),
fun() -> f() end, [flopp]),
- spawn_opt(f(),
+ spawn_opt(f(),
read2, f, [], []);
f() ->
%% Duplicated unresolved calls are ignored:
@@ -1237,7 +1277,7 @@ read2(Conf) when is_list(Conf) ->
?line {ok, U2} = xref:q(s, "(Lin) UC"),
?line {ok, OK2} = xref:q(s, "(Lin) (E - UC)"),
?line true = U =:= U2,
- ?line true = OK =:= OK2,
+ ?line true = OK =:= OK2,
?line ok = check_state(s),
?line xref:stop(s),
@@ -1304,7 +1344,7 @@ replace(Conf) when is_list(Conf) ->
?line {ok, true} = xref:set_default(s, warnings, false),
?line {ok, rel2} = xref:add_release(s, Dir, []),
?line {error, _, _} = xref:replace_application(s, app1, "no_data"),
- ?line {error, _, {no_such_application, app12}} =
+ ?line {error, _, {no_such_application, app12}} =
xref:replace_application(s, app12, A1_0, []),
?line {error, _, {invalid_filename,{foo,bar}}} =
xref:replace_application(s, app1, {foo,bar}, []),
@@ -1312,7 +1352,7 @@ replace(Conf) when is_list(Conf) ->
xref:replace_application(s, foo, bar, [not_an_option]),
?line {error, _, {invalid_options,[{builtins,not_a_value}]}} =
xref:replace_application(s, foo, bar, [{builtins,not_a_value}]),
- ?line {ok, app1} =
+ ?line {ok, app1} =
xref:replace_application(s, app1, A1_0),
?line [{_, AppInfo}] = xref:info(s, applications, app1),
?line {value, {release, [rel2]}} = keysearch(release, 1, AppInfo),
@@ -1332,14 +1372,14 @@ replace(Conf) when is_list(Conf) ->
?line {ok, x} = compile:file(X, [no_debug_info, {outdir,EB1_1}]),
?line {error, _, {no_debug_info, _}} = xref:replace_module(s, x, Xbeam),
- ?line {error, _, {module_mismatch, x,y}} =
+ ?line {error, _, {module_mismatch, x,y}} =
xref:replace_module(s, x, Ybeam),
?line case os:type() of
{unix, _} ->
?line hide_file(Ybeam),
- ?line {error, _, {file_error, _, _}} =
+ ?line {error, _, {file_error, _, _}} =
xref:replace_module(s, x, Ybeam);
- _ ->
+ _ ->
true
end,
?line ok = xref:remove_module(s, x),
@@ -1362,16 +1402,16 @@ update(Conf) when is_list(Conf) ->
Source = fname(Dir, "x.erl"),
Beam = fname(Dir, "x.beam"),
?line copy_file(fname(Dir, "x.erl.1"), Source),
- ?line {ok, x} = compile:file(Source, [debug_info, {outdir,Dir}]),
-
+ ?line {ok, x} = compile:file(Source, [debug_info, {outdir,Dir}]),
+
?line {ok, _} = start(s),
- ?line ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
+ ?line ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
?line {ok, [x]} = xref:add_directory(s, Dir, [{builtins,true}]),
?line {error, _, {invalid_options,[not_an_option]}} =
xref:update(s, [not_an_option]),
?line {ok, []} = xref:update(s),
?line {ok, [{erlang,atom_to_list,1}]} = xref:q(s, "XU"),
-
+
?line [{x, ModInfo}] = xref:info(s, modules, x),
?line case keysearch(directory, 1, ModInfo) of
{value, {directory, Dir}} -> ok
@@ -1379,7 +1419,7 @@ update(Conf) when is_list(Conf) ->
timer:sleep(2000), % make sure modification time has changed
?line copy_file(fname(Dir, "x.erl.2"), Source),
- ?line {ok, x} = compile:file(Source, [debug_info, {outdir,Dir}]),
+ ?line {ok, x} = compile:file(Source, [debug_info, {outdir,Dir}]),
?line {ok, [x]} = xref:update(s, []),
?line {ok, [{erlang,list_to_atom,1}]} = xref:q(s, "XU"),
@@ -1454,11 +1494,11 @@ deprecated(Conf) when is_list(Conf) ->
DF = usort(DF_3++[{{M9,t,0},{M9,f,1}}]),
?line {ok,DF} = xref:analyze(s, deprecated_function_calls),
- ?line {ok,DF_1} =
+ ?line {ok,DF_1} =
xref:analyze(s, {deprecated_function_calls,next_version}),
- ?line {ok,DF_2} =
+ ?line {ok,DF_2} =
xref:analyze(s, {deprecated_function_calls,next_major_release}),
- ?line {ok,DF_3} =
+ ?line {ok,DF_3} =
xref:analyze(s, {deprecated_function_calls,eventually}),
D = to_external(range(from_term(DF))),
@@ -1467,11 +1507,11 @@ deprecated(Conf) when is_list(Conf) ->
D_3 = to_external(range(from_term(DF_3))),
?line {ok,D} = xref:analyze(s, deprecated_functions),
- ?line {ok,D_1} =
+ ?line {ok,D_1} =
xref:analyze(s, {deprecated_functions,next_version}),
- ?line {ok,D_2} =
+ ?line {ok,D_2} =
xref:analyze(s, {deprecated_functions,next_major_release}),
- ?line {ok,D_3} =
+ ?line {ok,D_3} =
xref:analyze(s, {deprecated_functions,eventually}),
?line ok = check_state(s),
@@ -1516,11 +1556,11 @@ deprecated(Conf) when is_list(Conf) ->
DFa = DFa_3,
?line {ok,DFa} = xref:analyze(s, deprecated_function_calls),
- ?line {ok,DFa_1} =
+ ?line {ok,DFa_1} =
xref:analyze(s, {deprecated_function_calls,next_version}),
- ?line {ok,DFa_2} =
+ ?line {ok,DFa_2} =
xref:analyze(s, {deprecated_function_calls,next_major_release}),
- ?line {ok,DFa_3} =
+ ?line {ok,DFa_3} =
xref:analyze(s, {deprecated_function_calls,eventually}),
?line ok = check_state(s),
@@ -1564,11 +1604,11 @@ deprecated(Conf) when is_list(Conf) ->
DFb = usort(DFb_2++[{{M,bar,2},{M,t,0}},{{M,g,3},{M,bar,2}}]),
?line {ok,DFb} = xref:analyze(s, deprecated_function_calls),
- ?line {ok,DFb_1} =
+ ?line {ok,DFb_1} =
xref:analyze(s, {deprecated_function_calls,next_version}),
- ?line {ok,DFb_2} =
+ ?line {ok,DFb_2} =
xref:analyze(s, {deprecated_function_calls,next_major_release}),
- ?line {ok,DFb_3} =
+ ?line {ok,DFb_3} =
xref:analyze(s, {deprecated_function_calls,eventually}),
?line ok = check_state(s),
@@ -1599,7 +1639,7 @@ trycatch(Conf) when is_list(Conf) ->
catch
error:a -> err:e1();
error:b -> err:e2()
- after
+ after
fini:shed()
end.
">>,
@@ -1616,7 +1656,7 @@ trycatch(Conf) when is_list(Conf) ->
{{{A,A,0},{err,e2,0}},[13]},
{{{A,A,0},{fini,shed,0}},[15]},
{{{A,A,0},{foo,bar,0}},[7]},
- {{{A,A,0},{foo,foo,0}},[9]}]} =
+ {{{A,A,0},{foo,foo,0}},[9]}]} =
xref:q(s, "(Lin) (E | trycatch:trycatch/0)"),
?line ok = check_state(s),
@@ -1662,7 +1702,7 @@ abstract_modules(Conf) when is_list(Conf) ->
{{{A,args,1},{A,local,1}},[6]},
{{{A,args,1},{A,new,2}},[8]},
{{{A,local,1},{A,module_info,1}},[12]},
- {{{param,new,2},{param,instance,2}},[0]}]} =
+ {{{param,new,2},{param,instance,2}},[0]}]} =
xref:q(s, "(Lin) E"),
?line {ok,[{param,args,1},
{param,instance,2},
@@ -1747,10 +1787,10 @@ qlc(Conf) when is_list(Conf) ->
t() ->
dets:open_file(t, []),
dets:insert(t, [{1,a},{2,b},{3,c},{4,d}]),
- MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y}
+ MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y}
end),
QH1 = dets:table(t, [{traverse, {select, MS}}]),
- QH2 = qlc:q([{Y} || {X,Y} <- dets:table(t),
+ QH2 = qlc:q([{Y} || {X,Y} <- dets:table(t),
(X > 1) or (X < 5)]),
true = qlc:info(QH1) =:= qlc:info(QH2),
dets:close(t),
@@ -1772,8 +1812,6 @@ qlc(Conf) when is_list(Conf) ->
ok.
-analyses(suite) ->
- [analyze, basic, md, q, variables, unused_locals].
analyze(suite) -> [];
analyze(doc) -> ["Simple analyses"];
@@ -1783,7 +1821,7 @@ analyze(Conf) when is_list(Conf) ->
xref_base:analyze(S0, undefined_function_calls, [not_an_option]),
?line {{error, _, {invalid_query,{q}}}, _} = xref_base:q(S0,{q}),
?line {{error, _, {unknown_analysis,foo}}, _} = xref_base:analyze(S0, foo),
- ?line {{error, _, {unknown_constant,"foo:bar/-1"}}, _} =
+ ?line {{error, _, {unknown_constant,"foo:bar/-1"}}, _} =
xref_base:analyze(S0, {use,{foo,bar,-1}}),
CopyDir = ?copydir,
@@ -1803,30 +1841,30 @@ analyze(Conf) when is_list(Conf) ->
?line {ok, rel2, S1} = xref_base:add_release(S0, Dir, [{verbose,false}]),
?line S = set_up(S1),
- ?line {ok, _} =
+ ?line {ok, _} =
analyze(undefined_function_calls, [{{x,xx,0},{x,undef,0}}], S),
?line {ok, _} = analyze(undefined_functions, [{x,undef,0}], S),
?line {ok, _} = analyze(locals_not_used, [{x,l,0},{x,l1,0}], S),
?line {ok, _} = analyze(exports_not_used, [{x,xx,0},{y,t,0}], S),
- ?line {ok, _} =
+ ?line {ok, _} =
analyze(deprecated_function_calls, [{{y,t,0},{x,t,0}}], S),
?line {ok, _} = analyze({deprecated_function_calls,next_version}, [], S),
- ?line {ok, _} =
+ ?line {ok, _} =
analyze({deprecated_function_calls,next_major_release}, [], S),
- ?line {ok, _} = analyze({deprecated_function_calls,eventually},
+ ?line {ok, _} = analyze({deprecated_function_calls,eventually},
[{{y,t,0},{x,t,0}}], S),
?line {ok, _} = analyze(deprecated_functions, [{x,t,0}], S),
?line {ok, _} = analyze({deprecated_functions,next_version}, [], S),
- ?line {ok, _} =
+ ?line {ok, _} =
analyze({deprecated_functions,next_major_release}, [], S),
?line {ok, _} = analyze({deprecated_functions,eventually}, [{x,t,0}], S),
?line {ok, _} = analyze({call, {x,xx,0}}, [{x,undef,0}], S),
- ?line {ok, _} =
+ ?line {ok, _} =
analyze({call, [{x,xx,0},{x,l,0}]}, [{x,l1,0},{x,undef,0}], S),
?line {ok, _} = analyze({use, {x,l,0}}, [{x,l1,0}], S),
- ?line {ok, _} =
+ ?line {ok, _} =
analyze({use, [{x,l,0},{x,l1,0}]}, [{x,l,0},{x,l1,0}], S),
?line {ok, _} = analyze({module_call, x}, [x], S),
@@ -1881,7 +1919,7 @@ basic(Conf) when is_list(Conf) ->
LCallAt_m1 = [{E7,12}],
XCallAt_m1 = [{E1,13},{E2,17},{E4,7}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
D2 = {F2,7},
@@ -1895,7 +1933,7 @@ basic(Conf) when is_list(Conf) ->
LCallAt_m2 = [],
XCallAt_m2 = [{E3,96},{E6,12},{UE1,77}],
Info2 = #xref_mod{name = m2, app_name = [a2]},
- ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
+ ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
XC_m2, LC_m2),
D4 = {F4,6},
@@ -1908,7 +1946,7 @@ basic(Conf) when is_list(Conf) ->
LCallAt_m3 = [{E5,19}],
XCallAt_m3 = [{UE2,22}],
Info3 = #xref_mod{name = m3, app_name = [a3]},
- ?line S3 = add_module(S2, Info3, DefAt_m3, X_m3, LCallAt_m3, XCallAt_m3,
+ ?line S3 = add_module(S2, Info3, DefAt_m3, X_m3, LCallAt_m3, XCallAt_m3,
XC_m3, LC_m3),
Info4 = #xref_mod{name = m4, app_name = [a2]},
@@ -1955,7 +1993,7 @@ basic(Conf) when is_list(Conf) ->
?line {ok, _} = eval(f("(Mod) ~p", [[F1,F6,F5]]), [m1,m3], S),
?line {ok, _} = eval("(Lin) M - (Lin) m1",
[{F2,7},{F3,9},{F7,19},{F4,6},{F5,97},{UF2,0}], S),
- ?line {ok, _} = eval(f("(Lin) M * (Lin) ~p", [[F1,F6]]),
+ ?line {ok, _} = eval(f("(Lin) M * (Lin) ~p", [[F1,F6]]),
[{F1,12},{F6,3}], S),
?line {ok, _} = eval(f("X * ~p", [[F1, F2, F3, F4, F5]]), [F3, F4], S),
@@ -1976,7 +2014,7 @@ basic(Conf) when is_list(Conf) ->
?line {ok, _} = eval(f("(XXL) (Lin) (XC | ~p)", [F1]),
[{{D1,D3},[13]},{{D1,D4},[7]}],S),
?line {ok, _} = eval(f("XC | (~p + ~p)", [F1, F2]), [E1,E4,E3,UE1], S),
- ?line {ok, _} = eval(f("(XXL) (Lin) (XC | ~p)", [F1]),
+ ?line {ok, _} = eval(f("(XXL) (Lin) (XC | ~p)", [F1]),
[{{D1,D3},[13]},{{D1,D4},[7]}], S),
?line {ok, _} = eval("LC | m3", [E5], S),
?line {ok, _} = eval(f("LC | ~p", [F1]), [E7], S),
@@ -1984,7 +2022,7 @@ basic(Conf) when is_list(Conf) ->
?line {ok, _} = eval("E | m1", [E1,E2,E4,E7], S),
?line {ok, _} = eval(f("E | ~p", [F1]), [E1,E7,E4], S),
?line {ok, _} = eval(f("E | (~p + ~p)", [F1, F2]), [E1,E7,E4,E3,UE1], S),
-
+
?line {ok, _} = eval("XC || m1", [E3,UE2], S),
?line {ok, _} = eval(f("XC || ~p", [F6]), [E3], S),
?line {ok, _} = eval(f("XC || (~p + ~p)", [F4, UF2]), [UE1,E4,E6], S),
@@ -2012,18 +2050,18 @@ basic(Conf) when is_list(Conf) ->
?line {ok, _} = eval("components V", type_error, S),
?line {ok, _} = eval("components E + components E", type_error, S),
- ?line {ok, _} = eval(f("range (closure E | ~p)", [[F1,F2]]),
+ ?line {ok, _} = eval(f("range (closure E | ~p)", [[F1,F2]]),
[F6,F3,F7,F4,F5,UF1,UF2], S),
- ?line {ok, _} =
+ ?line {ok, _} =
eval(f("domain (closure E || ~p)", [[UF2,F7]]), [F1,F2,F6], S),
?line {ok, _} = eval("components E", [], S),
?line {ok, _} = eval("components (Mod) E", [[m1,m2,m3]], S),
?line {ok, _} = eval("components closure (Mod) E", [[m1,m2,m3]], S),
- ?line {ok, _} = eval("condensation (Mod) E",
+ ?line {ok, _} = eval("condensation (Mod) E",
[{[m1,m2,m3],[m1,m2,m3]},{[m1,m2,m3],[m17]}], S),
- ?line {ok, _} = eval("condensation closure (Mod) E",
+ ?line {ok, _} = eval("condensation closure (Mod) E",
[{[m1,m2,m3],[m1,m2,m3]},{[m1,m2,m3],[m17]}], S),
- ?line {ok, _} = eval("condensation closure closure closure (Mod) E",
+ ?line {ok, _} = eval("condensation closure closure closure (Mod) E",
[{[m1,m2,m3],[m1,m2,m3]},{[m1,m2,m3],[m17]}], S),
?line {ok, _} = eval("weak condensation (Mod) E",
[{[m1,m2,m3],[m1,m2,m3]},{[m1,m2,m3],[m17]},{[m17],[m17]}], S),
@@ -2035,11 +2073,11 @@ basic(Conf) when is_list(Conf) ->
[[m1,m2,m3]], S),
%% |, ||, |||
- ?line {ok, _} = eval("(Lin) E || V", type_error, S),
- ?line {ok, _} = eval("E ||| (Lin) V", type_error, S),
+ ?line {ok, _} = eval("(Lin) E || V", type_error, S),
+ ?line {ok, _} = eval("E ||| (Lin) V", type_error, S),
?line {ok, _} = eval("E ||| m1", [E7], S),
?line {ok, _} = eval("closure E ||| m1", [E7,{F1,UF1},{F6,UF1}], S),
- ?line {ok, _} = eval("closure E ||| [m1,m2]",
+ ?line {ok, _} = eval("closure E ||| [m1,m2]",
[{F1,UF1},{F2,F7},{F1,F7},{F6,UF1},{F2,UF1},{F7,UF1},E7,E1,E2,E3], S),
?line {ok, _} = eval("AE | a1", [{a1,a1},{a1,a2},{a1,a3}], S),
@@ -2095,7 +2133,7 @@ md(Conf) when is_list(Conf) ->
Y = fname(Dir, "y__y.erl"),
Xbeam = fname(Dir, "x__x.beam"),
Ybeam = fname(Dir, "y__y.beam"),
-
+
?line {error, _, {invalid_filename,{foo,bar}}} = xref:m({foo,bar}),
?line {error, _, {invalid_filename,{foo,bar}}} = xref:d({foo,bar}),
@@ -2171,7 +2209,7 @@ variables(Conf) when is_list(Conf) ->
LCallAt_m1 = [],
XCallAt_m1 = [{E1,13},{E3,17}],
Info1 = #xref_mod{name = m1, app_name = [a1]},
- ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
+ ?line S1 = add_module(S0, Info1, DefAt_m1, X_m1, LCallAt_m1, XCallAt_m1,
XC_m1, LC_m1),
D2 = {F2,7},
@@ -2183,11 +2221,11 @@ variables(Conf) when is_list(Conf) ->
LCallAt_m2 = [],
XCallAt_m2 = [{E2,96}],
Info2 = #xref_mod{name = m2, app_name = [a2]},
- ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
+ ?line S2 = add_module(S1, Info2, DefAt_m2, X_m2, LCallAt_m2, XCallAt_m2,
XC_m2, LC_m2),
?line S = set_up(S2),
-
+
?line eval("T1=E, T2=E*T1, T3 = T2*T2, T4=range T3, T5=T3|T4, T5",
[E1,E2,E3], S),
?line eval("((E*E)*(E*E)) | (range ((E*E)*(E*E)))",
@@ -2202,16 +2240,16 @@ variables(Conf) when is_list(Conf) ->
?line {ok, S102} = eval("T2 := E | m2", [E2], S101),
?line {{ok, [{user, ['T0', 'T1', 'T2']}]}, _} = xref_base:variables(S102),
?line {ok, S103} = xref_base:forget(S102, 'T0'),
- ?line {{ok, [{user, ['T1', 'T2']}]}, S104} =
+ ?line {{ok, [{user, ['T1', 'T2']}]}, S104} =
xref_base:variables(S103, [user]),
?line {ok, S105} = xref_base:forget(S104),
?line {{ok, [{user, []}]}, S106} = xref_base:variables(S105),
- ?line {{ok, [{predefined,_}]}, S107_0} =
+ ?line {{ok, [{predefined,_}]}, S107_0} =
xref_base:variables(S106, [predefined]),
- ?line {ok, S107_1} =
+ ?line {ok, S107_1} =
eval("TT := E, TT2 := V, TT1 := TT * TT", [E1,E2,E3], S107_0),
- ?line {{ok, [{user, ['TT', 'TT1', 'TT2']}]}, _} =
+ ?line {{ok, [{user, ['TT', 'TT1', 'TT2']}]}, _} =
xref_base:variables(S107_1),
?line {ok, S107} = xref_base:forget(S107_1),
@@ -2220,14 +2258,14 @@ variables(Conf) when is_list(Conf) ->
Beam = fname(Dir, "lib1.beam"),
?line copy_file(fname(Dir, "lib1.erl"), Beam),
- ?line {ok, S108} =
+ ?line {ok, S108} =
xref_base:set_library_path(S107, [Dir], [{verbose,false}]),
?line {{error, _, _}, _} = xref_base:variables(S108, [{verbose,false}]),
?line {ok, S109} = xref_base:set_library_path(S108, [], [{verbose,false}]),
?line Tabs = length(ets:all()),
- ?line {ok, S110} = eval("Eplus := closure E, TT := Eplus",
+ ?line {ok, S110} = eval("Eplus := closure E, TT := Eplus",
'closure()', S109),
?line {{ok, [{user, ['Eplus','TT']}]}, S111} = xref_base:variables(S110),
?line {ok, S112} = xref_base:forget(S111, ['TT','Eplus']),
@@ -2289,21 +2327,19 @@ unused_locals(Conf) when is_list(Conf) ->
?line {ok, []} = xref:analyse(s, locals_not_used),
?line ok = check_state(s),
?line xref:stop(s),
-
+
?line ok = file:delete(File1),
?line ok = file:delete(Beam1),
?line ok = file:delete(File2),
?line ok = file:delete(Beam2),
ok.
-misc(suite) ->
- [format_error, otp_7423, otp_7831].
format_error(suite) -> [];
format_error(doc) -> ["Format error messages"];
format_error(Conf) when is_list(Conf) ->
?line {ok, _Pid} = start(s),
- ?line ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
+ ?line ok = xref:set_default(s, [{verbose,false}, {warnings, false}]),
%% Parse error messages.
?line "Invalid regular expression \"add(\"" ++ _ =
@@ -2332,7 +2368,7 @@ format_error(Conf) when is_list(Conf) ->
%% Other messages
?line 'Variable \'QQ\' used before set\n' =
fatom(xref:q(s,"QQ")),
- ?line 'Unknown constant a\n' =
+ ?line 'Unknown constant a\n' =
fatom(xref:q(s,"{a} of E")),
%% Testing xref_parser:t2s/1.
@@ -2341,12 +2377,12 @@ format_error(Conf) when is_list(Conf) ->
?line 'Variable assigned more than once: E = E + E\n' =
fatom(xref:q(s,"E=E + E")),
?line "Operator applied to argument(s) of different or invalid type(s): "
- "E + V * V\n" =
+ "E + V * V\n" =
flatten(xref:format_error(xref:q(s,"E + (V * V)"))),
?line {error,xref_compiler,{type_error,"(V + V) * E"}} =
xref:q(s,"(V + V) * E"),
?line "Type does not match structure of constant: [m:f/3 -> g:h/17] : "
- "App\n" =
+ "App\n" =
flatten(xref:format_error(xref:q(s,"[{{m,f,3},{g,h,17}}] : App"))),
?line 'Type does not match structure of constant: [m -> f, g -> h] : Fun\n'
= fatom(xref:q(s,"[{m,f},g->h] : Fun")),
@@ -2360,11 +2396,11 @@ format_error(Conf) when is_list(Conf) ->
xref:q(s,"condensation (# E + # V)"),
?line {error,xref_compiler,{type_error,"range (# E + # E)"}} =
xref:q(s,"range (#E + #E)"),
- ?line {error,xref_compiler,{type_error,"range (# E)"}} =
+ ?line {error,xref_compiler,{type_error,"range (# E)"}} =
xref:q(s,"range #E"), % Hm...
?line {error,xref_compiler,{type_error,"E + # E"}} =
xref:q(s,"E + #E + #E"), % Hm...
- ?line {error,xref_compiler,{type_error,"V * E || V | V"}} =
+ ?line {error,xref_compiler,{type_error,"V * E || V | V"}} =
xref:q(s,"V * (E || V) | V"),
?line {error,xref_compiler,{type_error,"E || (E | V)"}} =
xref:q(s,"V * E || (E | V)"),
@@ -2421,7 +2457,7 @@ eval(Query, E, S) ->
?format("------------------------------~n", []),
?format("Evaluating ~p~n", [Query]),
?line {Answer, NewState} = xref_base:q(S, Query, [{verbose, false}]),
- {Reply, Expected} =
+ {Reply, Expected} =
case Answer of
{ok, R} when is_list(E) ->
{unsetify(R), sort(E)};
@@ -2430,7 +2466,7 @@ eval(Query, E, S) ->
{error, _Module, Reason} ->
{element(1, Reason), E}
end,
- if
+ if
Reply =:= Expected ->
?format("As expected, got ~n~p~n", [Expected]),
{ok, NewState};
@@ -2442,7 +2478,7 @@ eval(Query, E, S) ->
analyze(Query, E, S) ->
?format("------------------------------~n", []),
?format("Evaluating ~p~n", [Query]),
- ?line {{ok, L}, NewState} =
+ ?line {{ok, L}, NewState} =
xref_base:analyze(S, Query, [{verbose, false}]),
case {unsetify(L), sort(E)} of
{X,X} ->
@@ -2461,7 +2497,7 @@ unsetify(S) ->
%% Note: assumes S has been set up; the new state is not returned
eval(Query, S) ->
- ?line {{ok, Answer}, _NewState} =
+ ?line {{ok, Answer}, _NewState} =
xref_base:q(S, Query, [{verbose, false}]),
unsetify(Answer).
@@ -2514,7 +2550,7 @@ check_state(S) ->
functions_mode_check(S, Info)
end.
-%% The manual mentions some facts that should always hold.
+%% The manual mentions some facts that should always hold.
%% Here they are again.
functions_mode_check(S, Info) ->
%% F = L + X,
@@ -2526,7 +2562,7 @@ functions_mode_check(S, Info) ->
?line {ok, V} = xref:q(S, "X + L + B + U"),
%% X, L, B and U are disjoint.
- ?line {ok, []} =
+ ?line {ok, []} =
xref:q(S, "X * L + X * B + X * U + L * B + L * U + B * U"),
%% V = UU + XU + LU,
@@ -2577,11 +2613,11 @@ functions_mode_check(S, Info) ->
?line {Local, Exported} = info(Info, no_functions),
?line LX = Local+Exported,
- ?line {ok, LXs} = xref:q(S, 'Extra = _:module_info/"(0|1)" + LM,
+ ?line {ok, LXs} = xref:q(S, 'Extra = _:module_info/"(0|1)" + LM,
# (F - Extra)'),
?line true = LX =:= LXs,
- ?line {LocalCalls, ExternalCalls, UnresCalls} =
+ ?line {LocalCalls, ExternalCalls, UnresCalls} =
info(Info, no_function_calls),
?line LEU = LocalCalls + ExternalCalls + UnresCalls,
?line {ok, LEU} = xref:q(S, "# LC + # XC"),
@@ -2635,7 +2671,7 @@ check_count(S) ->
%% {ok, A} = xref:q(S, 'A'),
{ok, M} = xref:q(S, 'AM'),
- {ok, _} = xref:q(S,
+ {ok, _} = xref:q(S,
"Extra := _:module_info/\"(0|1)\" + LM"),
%% info/1:
@@ -2670,7 +2706,7 @@ check_count(S) ->
ok.
info_module([M | Ms], S) ->
- {ok, NoCalls} = per_module("T = (E | ~p : Mod), # (XLin) T + # (LLin) T",
+ {ok, NoCalls} = per_module("T = (E | ~p : Mod), # (XLin) T + # (LLin) T",
M, S),
{ok, NoFunCalls} = per_module("# (E | ~p : Mod)", M, S),
{ok, NoXCalls} = per_module("# (XC | ~p : Mod)", M, S),
@@ -2719,14 +2755,14 @@ start(Server) ->
end.
add_erts_code_path(KernelPath) ->
- VersionDirs =
+ VersionDirs =
filelib:is_dir(
filename:join(
[code:lib_dir(),
lists:flatten(
["kernel-",
- [X ||
- {kernel,_,X} <-
+ [X ||
+ {kernel,_,X} <-
application_controller:which_applications()]])])),
case VersionDirs of
true ->
@@ -2746,5 +2782,5 @@ add_erts_code_path(KernelPath) ->
[KernelPath]
end
end.
-
-
+
+
diff --git a/lib/tools/test/xref_SUITE_data/read/read.erl b/lib/tools/test/xref_SUITE_data/read/read.erl
index 4a0cc280c3..19694c9e25 100644
--- a/lib/tools/test/xref_SUITE_data/read/read.erl
+++ b/lib/tools/test/xref_SUITE_data/read/read.erl
@@ -106,13 +106,13 @@ funfuns() ->
apply(m,f,a), % {m,f,-1}
3(a), % {'$M_EXPR','$F_EXPR',1}
apply(3,[a]), % {'$M_EXPR','$F_EXPR',1}
-
+
%% POS12=POS11+8
apply(A, A), % number of arguments is not known, {'$M_EXPR','$F_EXPR',-1}
Args0 = [list],
Args = [a | Args0], % number of arguments is known
apply(A, Args), % {'$M_EXPR','$F_EXPR',2}
- apply(m3, f3, Args), %
+ apply(m3, f3, Args), %
NotArgs = [is_not, a | list], % number of arguments is not known
apply(A, NotArgs), % {'$M_EXPR','$F_EXPR',-1}
apply(m4, f4, NotArgs), % {m4,f4,-1}
@@ -125,7 +125,7 @@ funfuns() ->
bi() when length([]) > 17 ->
foo:module_info(),
module_info(),
- A = tjo,
+ A = true andalso tjo ,
t:foo(A),
case true of
true when integer(1) ->
@@ -133,7 +133,7 @@ bi() when length([]) > 17 ->
false ->
X = flopp
end,
- X == A;
+ self() ! X == -A orelse false;
bi() ->
%% POS14=POS13+18
Z = fun(Y) -> Y end,
@@ -159,7 +159,7 @@ bi() ->
D + E + F.
%bi() ->
% %% POS15=POS14+13
-% try
+% try
% foo:t(),
% bar:t()
% of
@@ -169,7 +169,7 @@ bi() ->
% foo:t()
% catch
% {'EXIT',_} -> bar:t()
-% end.
+% end.
local() ->
true.
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 13cf5af9f5..83027cfaa6 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1,19 +1 @@
-# This is an -*-makefile-*- file.
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-
-TOOLS_VSN = 2.6.5.1
+TOOLS_VSN = 2.6.6.3
diff --git a/lib/tv/doc/src/notes.xml b/lib/tv/doc/src/notes.xml
index 62fab2f0f1..388bb82c91 100644
--- a/lib/tv/doc/src/notes.xml
+++ b/lib/tv/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2004</year><year>2009</year>
+ <year>2004</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>TV Release Notes</title>
@@ -30,6 +30,38 @@
</header>
<p>This document describes the changes made to the TV application.</p>
+<section><title>TV 2.1.4.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ With some window managers (e.g. stumpwm), <c>tv</c> would
+ constantly restart while trying to open a table. (Thanks
+ to Dmitriy Budashny.)</p>
+ <p>
+ Own Id: OTP-8751</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>TV 2.1.4.5</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Warnings due to new autoimported BIFs removed</p>
+ <p>
+ Own Id: OTP-8674 Aux Id: OTP-8579 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>TV 2.1.4.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/tv/doc/src/tv.xml b/lib/tv/doc/src/tv.xml
index 76edcac71b..84b9f8c33d 100644
--- a/lib/tv/doc/src/tv.xml
+++ b/lib/tv/doc/src/tv.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1997</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/tv/src/tv_io_lib.erl b/lib/tv/src/tv_io_lib.erl
index f693ff796d..5457575b7d 100644
--- a/lib/tv/src/tv_io_lib.erl
+++ b/lib/tv/src/tv_io_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -52,10 +52,11 @@ write(_Term, 0) -> "...";
write(Term, _D) when is_integer(Term) -> integer_to_list(Term);
write(Term, _D) when is_float(Term) -> tv_io_lib_format:fwrite_g(Term);
write(Atom, _D) when is_atom(Atom) -> write_atom(Atom);
-write(Term, _D) when is_port(Term) -> "#Port";
+write(Term, _D) when is_port(Term) -> lists:flatten(io_lib:write(Term));
write(Term, _D) when is_pid(Term) -> pid_to_list(Term);
-write(Term, _D) when is_reference(Term) -> "#Ref";
-write(Term, _D) when is_binary(Term) -> "#Bin";
+write(Term, _D) when is_reference(Term) -> io_lib:write(Term);
+write(Term, _D) when is_binary(Term), byte_size(Term) > 100 -> "#Bin";
+write(Term, _D) when is_binary(Term) -> "<<\"" ++ binary_to_list(Term) ++ "\">>";
write(Term, _D) when is_bitstring(Term) -> "#Bitstr";
write([], _D) -> "[]";
write({}, _D) -> "{}";
diff --git a/lib/tv/src/tv_io_lib_format.erl b/lib/tv/src/tv_io_lib_format.erl
index 5042fd3f9d..e043d9296e 100644
--- a/lib/tv/src/tv_io_lib_format.erl
+++ b/lib/tv/src/tv_io_lib_format.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1998-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
-module(tv_io_lib_format).
@@ -188,7 +188,7 @@ indentation([], I) -> I.
term(T, none, _Adj, none, _Pad) -> T;
term(T, none, Adj, P, Pad) -> term(T, P, Adj, P, Pad);
-term(T, F, Adj, none, Pad) -> term(T, F, Adj, min(flat_length(T), F), Pad);
+term(T, F, Adj, none, Pad) -> term(T, F, Adj, erlang:min(flat_length(T), F), Pad);
term(T, F, Adj, P, Pad) when F >= P ->
adjust_error(T, F, Adj, P, Pad).
@@ -316,7 +316,7 @@ fwrite_g(Fl, F, Adj, P, Pad) ->
string(S, none, _Adj, none, _Pad) -> S;
string(S, F, Adj, none, Pad) ->
- string(S, F, Adj, min(flat_length(S), F), Pad);
+ string(S, F, Adj, erlang:min(flat_length(S), F), Pad);
string(S, none, _Adj, P, Pad) ->
string:left(flatten(S), P, Pad);
string(S, F, Adj, P, Pad) when F >= P ->
@@ -362,9 +362,6 @@ reverse([H|T], Stack) ->
reverse(T, [H|Stack]);
reverse([], Stack) -> Stack.
-min(L, R) when L < R -> L;
-min(_, R) -> R.
-
%% flatten(List)
%% Flatten a list.
diff --git a/lib/tv/src/tv_pb.erl b/lib/tv/src/tv_pb.erl
index 34db8d0772..78a27185dc 100644
--- a/lib/tv/src/tv_pb.erl
+++ b/lib/tv/src/tv_pb.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
-module(tv_pb).
@@ -522,7 +522,7 @@ handle_col_resizing(RbtnId, RealCol, VirtualCol, Xpos, ProcVars) ->
get_xdiff(Id, Btn, LastXdiff, LineId, LineXpos, MinAllowedXdiff) ->
receive
{gs, Id, motion, {resbtn, _RealCol, _VirtCol, _OldXpos}, [NewXdiff | _T]} ->
- UsedXdiff = max(MinAllowedXdiff, NewXdiff),
+ UsedXdiff = erlang:max(MinAllowedXdiff, NewXdiff),
gs:config(LineId, [{x, LineXpos + UsedXdiff}]),
get_xdiff(Id, Btn, UsedXdiff, LineId, LineXpos, MinAllowedXdiff);
{gs, Id, buttonrelease, _Data, [Btn | _T]} ->
@@ -658,28 +658,3 @@ update_vbtns(Msg, ProcVars) ->
update_keys(Msg, ProcVars) ->
#pb_key_info{list_of_keys = KeyList} = Msg,
tv_pb_funcs:update_keys(KeyList, ProcVars).
-
-
-
-
-
-
-
-
-%%======================================================================
-%% Function:
-%%
-%% Return Value:
-%%
-%% Description:
-%%
-%% Parameters:
-%%======================================================================
-
-
-max(A, B) when A >= B ->
- A;
-max(_, B) ->
- B.
-
-
diff --git a/lib/tv/src/tv_pg_gridfcns.erl b/lib/tv/src/tv_pg_gridfcns.erl
index 809403fd96..3d23c8a69f 100644
--- a/lib/tv/src/tv_pg_gridfcns.erl
+++ b/lib/tv/src/tv_pg_gridfcns.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
-module(tv_pg_gridfcns).
@@ -98,7 +98,7 @@ init_grid(GridParentId, GridWidth,
nof_rows_shown = NofRowsShown
},
- NewNofCols = max(length(ColsShown), NofCols),
+ NewNofCols = erlang:max(length(ColsShown), NofCols),
% The GridColWidths list shall contain the current width of each frame.
NewColWidths = update_col_widths(ColsShown, ColWidths, FirstColShown,
@@ -205,8 +205,8 @@ resize_grid(NewWidth, NewHeight, ProcVars) ->
check_nof_cols(ColsShown, (NofColsShown - NofCols), ColFrameIds, ColIds,
RowIds, NofRows, RowHeight, FgColor, BgColor ),
- clear_fields(lists:nthtail(NofColsShown, NewColIds),
- lists:nthtail(NofRowsShown, NewRowIds)),
+ clear_fields(safe_nthtail(NofColsShown, NewColIds),
+ safe_nthtail(NofRowsShown, NewRowIds)),
RowsToUpdate = lists:sublist(NewRowIds, NofRowsShown),
@@ -270,7 +270,7 @@ resize_grid_column(RealCol, VirtualCol, Xdiff, ProcVars) ->
lists_as_strings = ListAsStr} = GridP,
% Get new width!
- Width = min(MaxColWidth, max((lists:nth(VirtualCol, ColWidths) + Xdiff),
+ Width = erlang:min(MaxColWidth, erlang:max((lists:nth(VirtualCol, ColWidths) + Xdiff),
MinColWidth)),
% Resize the column.
@@ -279,7 +279,7 @@ resize_grid_column(RealCol, VirtualCol, Xdiff, ProcVars) ->
% Update the ColWidths list.
TempColWidths = lists:sublist(ColWidths, VirtualCol - 1) ++
- [NewWidthOfCol | lists:nthtail(VirtualCol, ColWidths)],
+ [NewWidthOfCol | safe_nthtail(VirtualCol, ColWidths)],
% Check the other columns, whether a new column has to be created.
ColsShown = compute_cols_shown(FirstColShown, TempColWidths, GridWidth,
@@ -455,8 +455,8 @@ scroll_grid_horizontally(NewFirstColShown, ProcVars) ->
refresh_visible_rows(RowsToUpdate, NewFirstColShown, NofColsShown, RowDataList, ListAsStr),
% Clear fields currently not visible.
- clear_fields(lists:nthtail(NofColsShown, NewColIds),
- lists:nthtail(NofRowsShown, NewRowIds)),
+ clear_fields(safe_nthtail(NofColsShown, NewColIds),
+ safe_nthtail(NofRowsShown, NewRowIds)),
NewGridP = GridP#grid_params{nof_cols = NewNofCols,
@@ -1336,7 +1336,7 @@ resize_all_grid_columns(RealCol, [ColWidth | Tail], ColFrameIds, MaxColWidth, Mi
resize_one_column(RealCol, Width, ColFrameIds, MaxW, MinW) ->
- NewWidthOfCol = min(MaxW, max(Width, MinW)),
+ NewWidthOfCol = erlang:min(MaxW, erlang:max(Width, MinW)),
case length(ColFrameIds) of
RealCol ->
done;
@@ -1565,7 +1565,7 @@ update_col_widths(ColsShown, ColWidths, FirstColShown, DefaultColWidth) ->
if
NecessaryNofVirtualCols > NofVirtualCols ->
TailNo = NofVirtualCols - FirstColShown + 1, % Always >= 0 !!!
- NewColWidths ++ lists:nthtail(TailNo, ColsShown);
+ NewColWidths ++ safe_nthtail(TailNo, ColsShown);
true ->
NewColWidths
end.
@@ -1653,7 +1653,7 @@ compute_cols_shown(FirstColShown, ColWidths, GridWidth, _NofCols, DefaultColWidt
ColWidthsLength < FirstColShown ->
[];
true ->
- lists:nthtail(FirstColShown - 1, ColWidths)
+ safe_nthtail(FirstColShown - 1, ColWidths)
end,
compute_cols_shown(UsedColWidths, GridWidth, DefaultColWidth).
@@ -1896,44 +1896,19 @@ extract_ids_for_one_row(N, [ColIds | Tail]) ->
%%%---------------------------------------------------------------------
-
-
-
-%%======================================================================
-%% Function:
-%%
-%% Return Value:
-%%
-%% Description:
-%%
-%% Parameters:
-%%======================================================================
-
-
-max(A, B) when A > B ->
- A;
-max(_, B) ->
- B.
-
-
-
-
-
-
-
%%======================================================================
-%% Function:
+%% Function:
%%
-%% Return Value:
+%% Return Value:
%%
-%% Description:
+%% Description:
%%
-%% Parameters:
+%% Parameters:
%%======================================================================
-min(A, B) when A < B ->
- A;
-min(_, B) ->
- B.
-
+safe_nthtail(_, []) -> [];
+safe_nthtail(1, [_|T]) -> T;
+safe_nthtail(N, [_|T]) when N > 1 ->
+ safe_nthtail(N - 1, T);
+safe_nthtail(0, L) when is_list(L) -> L.
diff --git a/lib/tv/vsn.mk b/lib/tv/vsn.mk
index be1981b755..958aa0ea42 100644
--- a/lib/tv/vsn.mk
+++ b/lib/tv/vsn.mk
@@ -1,20 +1 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1997-2009. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-TV_VSN = 2.1.4.4
+TV_VSN = 2.1.4.6
diff --git a/lib/typer/RELEASE_NOTES b/lib/typer/RELEASE_NOTES
new file mode 100644
index 0000000000..d91a815ee9
--- /dev/null
+++ b/lib/typer/RELEASE_NOTES
@@ -0,0 +1,22 @@
+==============================================================================
+ Major features, additions and changes between Typer versions
+ (in reversed chronological order)
+==============================================================================
+
+Version 0.9 (in Erlang/OTP R14B02)
+----------------------------------
+ - Major rewrite; all code has been cleaned up and placed in one file.
+ The only reason why this is not version 1.0 yet is that there is no proper
+ documentation for typer which can be displayed in the www.erlang.org site.
+ - Added ability to receive the set of exported types and report unknown ones.
+ - Better handling of overloaded contracts; especially erroneous ones on which
+ typer does not crash anymore.
+ - Fixed problem that caused typer to hang when given a file whose module name
+ did not correspond to the file name.
+ - Added two undocumented options that may come very handy when trying to
+ understand why typer reports some particular set of types for the functions
+ in a module. These options are mainly for typer developers at this point,
+ but may become documented in some future version.
+
+Older versions
+--------------
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
index 9c9ef6156f..620b3ebb69 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+# Copyright Ericsson AB 2006-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -45,15 +45,9 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES = \
- typer \
- typer_annotator \
- typer_info \
- typer_map \
- typer_options \
- typer_preprocess
-
-HRL_FILES= typer.hrl
+MODULES = typer
+
+HRL_FILES=
ERL_FILES= $(MODULES:%=%.erl)
INSTALL_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
TARGET_FILES= $(INSTALL_FILES)
@@ -87,8 +81,8 @@ clean:
# Special Build Targets
# ----------------------------------------------------
-$(EBIN)/typer_options.$(EMULATOR): typer_options.erl ../vsn.mk Makefile
- erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer_options.erl
+$(EBIN)/typer.$(EMULATOR): typer.erl ../vsn.mk Makefile
+ erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer.erl
$(APP_TARGET): $(APP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
@@ -97,14 +91,9 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
sed -e 's;%VSN%;$(VSN);' $< > $@
# ---------------------------------------------------------------------
-# dependencies -- I wish they were somehow automatically generated
+# dependencies
# ---------------------------------------------------------------------
-$(EBIN)/typer.beam: typer.hrl
-$(EBIN)/typer_annotator.beam: typer.hrl
-$(EBIN)/typer_info.beam: typer.hrl
-$(EBIN)/typer_options.beam: typer.hrl
-$(EBIN)/typer_preprocess.beam: typer.hrl
# ----------------------------------------------------
# Release Target
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
index 3eb0cbf816..850829e1dc 100644
--- a/lib/typer/src/typer.app.src
+++ b/lib/typer/src/typer.app.src
@@ -3,12 +3,7 @@
{application, typer,
[{description, "TYPe annotator for ERlang programs, version %VSN%"},
{vsn, "%VSN%"},
- {modules, [typer,
- typer_annotator,
- typer_info,
- typer_map,
- typer_options,
- typer_preprocess]},
+ {modules, [typer]},
{registered, []},
{applications, [compiler, dialyzer, hipe, kernel, stdlib]},
{env, []}]}.
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index cebe6ea488..e40c4f39cd 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -1,63 +1,107 @@
%% -*- erlang-indent-level: 2 -*-
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-%%--------------------------------------------------------------------
+%%-----------------------------------------------------------------------
%% File : typer.erl
-%% Author : Bingwen He <[email protected]>
-%% Description : The main driver of the TypEr application
-%%--------------------------------------------------------------------
+%% Author(s) : The first version of typer was written by Bingwen He
+%% with guidance from Kostis Sagonas and Tobias Lindahl.
+%% Since June 2008 typer is maintained by Kostis Sagonas.
+%% Description : An Erlang/OTP application that shows type information
+%% for Erlang modules to the user. Additionally, it can
+%% annotate the code of files with such type information.
+%%-----------------------------------------------------------------------
-module(typer).
-export([start/0]).
--export([error/1, compile_error/1]). % for error reporting
--include("typer.hrl").
+%%-----------------------------------------------------------------------
+
+-define(SHOW, show).
+-define(SHOW_EXPORTED, show_exported).
+-define(ANNOTATE, annotate).
+-define(ANNOTATE_INC_FILES, annotate_inc_files).
+
+-type mode() :: ?SHOW | ?SHOW_EXPORTED | ?ANNOTATE | ?ANNOTATE_INC_FILES.
+
+%%-----------------------------------------------------------------------
+
+-type files() :: [file:filename()].
+-type callgraph() :: dialyzer_callgraph:callgraph().
+-type codeserver() :: dialyzer_codeserver:codeserver().
+-type plt() :: dialyzer_plt:plt().
+
+-record(analysis,
+ {mode :: mode() | 'undefined',
+ macros = [] :: [{atom(), term()}],
+ includes = [] :: files(),
+ codeserver = dialyzer_codeserver:new():: codeserver(),
+ callgraph = dialyzer_callgraph:new() :: callgraph(),
+ files = [] :: files(), % absolute names
+ plt = none :: 'none' | file:filename(),
+ no_spec = false :: boolean(),
+ show_succ = false :: boolean(),
+ %% For choosing between specs or edoc @spec comments
+ edoc = false :: boolean(),
+ %% Files in 'fms' are compilable with option 'to_pp'; we keep them
+ %% as {FileName, ModuleName} in case the ModuleName is different
+ fms = [] :: [{file:filename(), module()}],
+ ex_func = map__new() :: map(),
+ record = map__new() :: map(),
+ func = map__new() :: map(),
+ inc_func = map__new() :: map(),
+ trust_plt = dialyzer_plt:new() :: plt()}).
+-type analysis() :: #analysis{}.
+
+-record(args, {files = [] :: files(),
+ files_r = [] :: files(),
+ trusted = [] :: files()}).
+-type args() :: #args{}.
%%--------------------------------------------------------------------
-spec start() -> no_return().
start() ->
- {Args, Analysis} = typer_options:process(),
+ {Args, Analysis} = process_cl_args(),
%% io:format("Args: ~p\n", [Args]),
%% io:format("Analysis: ~p\n", [Analysis]),
- TrustedFiles = typer_preprocess:get_all_files(Args, trust),
- Analysis1 = Analysis#typer_analysis{t_files = TrustedFiles},
- Analysis2 = extract(Analysis1),
- All_Files = typer_preprocess:get_all_files(Args, analysis),
+ TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1),
+ Analysis2 = extract(Analysis, TrustedFiles),
+ All_Files = get_all_files(Args),
%% io:format("All_Files: ~p\n", [All_Files]),
- Analysis3 = Analysis2#typer_analysis{ana_files = All_Files},
- Analysis4 = typer_info:collect(Analysis3),
- %% io:format("Final: ~p\n", [Analysis4#typer_analysis.final_files]),
+ Analysis3 = Analysis2#analysis{files = All_Files},
+ Analysis4 = collect_info(Analysis3),
+ %% io:format("Final: ~p\n", [Analysis4#analysis.fms]),
TypeInfo = get_type_info(Analysis4),
- typer_annotator:annotate(TypeInfo),
+ show_or_annotate(TypeInfo),
%% io:format("\nTyper analysis finished\n"),
erlang:halt(0).
%%--------------------------------------------------------------------
--spec extract(#typer_analysis{}) -> #typer_analysis{}.
+-spec extract(analysis(), files()) -> analysis().
-extract(#typer_analysis{macros = Macros, includes = Includes,
- t_files = TFiles, trust_plt = TrustPLT} = Analysis) ->
+extract(#analysis{macros = Macros,
+ includes = Includes,
+ trust_plt = TrustPLT} = Analysis, TrustedFiles) ->
%% io:format("--- Extracting trusted typer_info... "),
Ds = [{d, Name, Value} || {Name, Value} <- Macros],
CodeServer = dialyzer_codeserver:new(),
@@ -85,7 +129,7 @@ extract(#typer_analysis{macros = Macros, includes = Includes,
{error, Reason} -> compile_error(Reason)
end
end,
- CodeServer1 = lists:foldl(Fun, CodeServer, TFiles),
+ CodeServer1 = lists:foldl(Fun, CodeServer, TrustedFiles),
%% Process remote types
NewCodeServer =
try
@@ -110,30 +154,30 @@ extract(#typer_analysis{macros = Macros, includes = Includes,
dialyzer_plt:insert_contract_list(TmpPlt, SpecList)
end,
NewTrustPLT = lists:foldl(FoldFun, TrustPLT, Modules),
- Analysis#typer_analysis{trust_plt = NewTrustPLT}.
+ Analysis#analysis{trust_plt = NewTrustPLT}.
%%--------------------------------------------------------------------
--spec get_type_info(#typer_analysis{}) -> #typer_analysis{}.
+-spec get_type_info(analysis()) -> analysis().
-get_type_info(#typer_analysis{callgraph = CallGraph,
- trust_plt = TrustPLT,
- code_server = CodeServer} = Analysis) ->
+get_type_info(#analysis{callgraph = CallGraph,
+ trust_plt = TrustPLT,
+ codeserver = CodeServer} = Analysis) ->
StrippedCallGraph = remove_external(CallGraph, TrustPLT),
%% io:format("--- Analyzing callgraph... "),
try
NewPlt = dialyzer_succ_typings:analyze_callgraph(StrippedCallGraph,
TrustPLT, CodeServer),
- Analysis#typer_analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt}
+ Analysis#analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt}
catch
error:What ->
- error(io_lib:format("Analysis failed with message: ~p",
- [{What, erlang:get_stacktrace()}]));
+ fatal_error(io_lib:format("Analysis failed with message: ~p",
+ [{What, erlang:get_stacktrace()}]));
throw:{dialyzer_succ_typing_error, Msg} ->
- error(io_lib:format("Analysis failed with message: ~s", [Msg]))
+ fatal_error(io_lib:format("Analysis failed with message: ~s", [Msg]))
end.
--spec remove_external(dialyzer_callgraph:callgraph(), dialyzer_plt:plt()) -> dialyzer_callgraph:callgraph().
+-spec remove_external(callgraph(), plt()) -> callgraph().
remove_external(CallGraph, PLT) ->
{StrippedCG0, Ext} = dialyzer_callgraph:remove_external(CallGraph),
@@ -141,11 +185,16 @@ remove_external(CallGraph, PLT) ->
case get_external(Ext, PLT) of
[] -> ok;
Externals ->
- msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)]))
+ msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)])),
+ ExtTypes = rcv_ext_types(),
+ case ExtTypes of
+ [] -> ok;
+ _ -> msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes]))
+ end
end,
StrippedCG.
--spec get_external([{mfa(), mfa()}], dialyzer_plt:plt()) -> [mfa()].
+-spec get_external([{mfa(), mfa()}], plt()) -> [mfa()].
get_external(Exts, Plt) ->
Fun = fun ({_From, To = {M, F, A}}, Acc) ->
@@ -161,31 +210,790 @@ get_external(Exts, Plt) ->
lists:foldl(Fun, [], Exts).
%%--------------------------------------------------------------------
+%% Showing type information or annotating files with such information.
+%%--------------------------------------------------------------------
+
+-define(TYPER_ANN_DIR, "typer_ann").
+
+-type line() :: non_neg_integer().
+-type fa() :: {atom(), arity()}.
+-type func_info() :: {line(), atom(), arity()}.
+
+-record(info, {records = map__new() :: map(),
+ functions = [] :: [func_info()],
+ types = map__new() :: map(),
+ edoc = false :: boolean()}).
+-record(inc, {map = map__new() :: map(), filter = [] :: files()}).
+-type inc() :: #inc{}.
+
+-spec show_or_annotate(analysis()) -> 'ok'.
+
+show_or_annotate(#analysis{mode = Mode, fms = Files} = Analysis) ->
+ case Mode of
+ ?SHOW -> show(Analysis);
+ ?SHOW_EXPORTED -> show(Analysis);
+ ?ANNOTATE ->
+ Fun = fun ({File, Module}) ->
+ Info = get_final_info(File, Module, Analysis),
+ write_typed_file(File, Info)
+ end,
+ lists:foreach(Fun, Files);
+ ?ANNOTATE_INC_FILES ->
+ IncInfo = write_and_collect_inc_info(Analysis),
+ write_inc_files(IncInfo)
+ end.
+
+write_and_collect_inc_info(Analysis) ->
+ Fun = fun ({File, Module}, Inc) ->
+ Info = get_final_info(File, Module, Analysis),
+ write_typed_file(File, Info),
+ IncFuns = get_functions(File, Analysis),
+ collect_imported_functions(IncFuns, Info#info.types, Inc)
+ end,
+ NewInc = lists:foldl(Fun, #inc{}, Analysis#analysis.fms),
+ clean_inc(NewInc).
+
+write_inc_files(Inc) ->
+ Fun =
+ fun (File) ->
+ Val = map__lookup(File, Inc#inc.map),
+ %% Val is function with its type info
+ %% in form [{{Line,F,A},Type}]
+ Functions = [Key || {Key, _} <- Val],
+ Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val],
+ Info = #info{types = map__from_list(Val1),
+ records = map__new(),
+ %% Note we need to sort functions here!
+ functions = lists:keysort(1, Functions)},
+ %% io:format("Types ~p\n", [Info#info.types]),
+ %% io:format("Functions ~p\n", [Info#info.functions]),
+ %% io:format("Records ~p\n", [Info#info.records]),
+ write_typed_file(File, Info)
+ end,
+ lists:foreach(Fun, dict:fetch_keys(Inc#inc.map)).
+
+show(Analysis) ->
+ Fun = fun ({File, Module}) ->
+ Info = get_final_info(File, Module, Analysis),
+ show_type_info(File, Info)
+ end,
+ lists:foreach(Fun, Analysis#analysis.fms).
+
+get_final_info(File, Module, Analysis) ->
+ Records = get_records(File, Analysis),
+ Types = get_types(Module, Analysis, Records),
+ Functions = get_functions(File, Analysis),
+ Edoc = Analysis#analysis.edoc,
+ #info{records = Records, functions = Functions, types = Types, edoc = Edoc}.
+
+collect_imported_functions(Functions, Types, Inc) ->
+ %% Coming from other sourses, including:
+ %% FIXME: How to deal with yecc-generated file????
+ %% --.yrl (yecc-generated file)???
+ %% -- yeccpre.hrl (yecc-generated file)???
+ %% -- other cases
+ Fun = fun ({File, _} = Obj, I) ->
+ case is_yecc_gen(File, I) of
+ {true, NewI} -> NewI;
+ {false, NewI} ->
+ check_imported_functions(Obj, NewI, Types)
+ end
+ end,
+ lists:foldl(Fun, Inc, Functions).
+
+-spec is_yecc_gen(file:filename(), inc()) -> {boolean(), inc()}.
+
+is_yecc_gen(File, #inc{filter = Fs} = Inc) ->
+ case lists:member(File, Fs) of
+ true -> {true, Inc};
+ false ->
+ case filename:extension(File) of
+ ".yrl" ->
+ Rootname = filename:rootname(File, ".yrl"),
+ Obj = Rootname ++ ".erl",
+ case lists:member(Obj, Fs) of
+ true -> {true, Inc};
+ false ->
+ NewInc = Inc#inc{filter = [Obj|Fs]},
+ {true, NewInc}
+ end;
+ _ ->
+ case filename:basename(File) of
+ "yeccpre.hrl" -> {true, Inc};
+ _ -> {false, Inc}
+ end
+ end
+ end.
+
+check_imported_functions({File, {Line, F, A}}, Inc, Types) ->
+ IncMap = Inc#inc.map,
+ FA = {F, A},
+ Type = get_type_info(FA, Types),
+ case map__lookup(File, IncMap) of
+ none -> %% File is not added. Add it
+ Obj = {File,[{FA, {Line, Type}}]},
+ NewMap = map__insert(Obj, IncMap),
+ Inc#inc{map = NewMap};
+ Val -> %% File is already in. Check.
+ case lists:keyfind(FA, 1, Val) of
+ false ->
+ %% Function is not in; add it
+ Obj = {File, Val ++ [{FA, {Line, Type}}]},
+ NewMap = map__insert(Obj, IncMap),
+ Inc#inc{map = NewMap};
+ Type ->
+ %% Function is in and with same type
+ Inc;
+ _ ->
+ %% Function is in but with diff type
+ inc_warning(FA, File),
+ Elem = lists:keydelete(FA, 1, Val),
+ NewMap = case Elem of
+ [] -> map__remove(File, IncMap);
+ _ -> map__insert({File, Elem}, IncMap)
+ end,
+ Inc#inc{map = NewMap}
+ end
+ end.
+
+inc_warning({F, A}, File) ->
+ io:format(" ***Warning: Skip function ~p/~p ", [F, A]),
+ io:format("in file ~p because of inconsistent type\n", [File]).
+
+clean_inc(Inc) ->
+ Inc1 = remove_yecc_generated_file(Inc),
+ normalize_obj(Inc1).
+
+remove_yecc_generated_file(#inc{filter = Filter} = Inc) ->
+ Fun = fun (Key, #inc{map = Map} = I) ->
+ I#inc{map = map__remove(Key, Map)}
+ end,
+ lists:foldl(Fun, Inc, Filter).
+
+normalize_obj(TmpInc) ->
+ Fun = fun (Key, Val, Inc) ->
+ NewVal = [{{Line,F,A},Type} || {{F,A},{Line,Type}} <- Val],
+ map__insert({Key, NewVal}, Inc)
+ end,
+ TmpInc#inc{map = map__fold(Fun, map__new(), TmpInc#inc.map)}.
+
+get_records(File, Analysis) ->
+ map__lookup(File, Analysis#analysis.record).
+
+get_types(Module, Analysis, Records) ->
+ TypeInfoPlt = Analysis#analysis.trust_plt,
+ TypeInfo =
+ case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of
+ none -> [];
+ {value, List} -> List
+ end,
+ CodeServer = Analysis#analysis.codeserver,
+ TypeInfoList =
+ case Analysis#analysis.show_succ of
+ true ->
+ [convert_type_info(I) || I <- TypeInfo];
+ false ->
+ [get_type(I, CodeServer, Records) || I <- TypeInfo]
+ end,
+ map__from_list(TypeInfoList).
+
+convert_type_info({{_M, F, A}, Range, Arg}) ->
+ {{F, A}, {Range, Arg}}.
+
+get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) ->
+ case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of
+ error ->
+ {{F, A}, {Range, Arg}};
+ {ok, {_FileLine, Contract}} ->
+ Sig = erl_types:t_fun(Arg, Range),
+ case dialyzer_contracts:check_contract(Contract, Sig) of
+ ok -> {{F, A}, {contract, Contract}};
+ {error, {extra_range, _, _}} ->
+ {{F, A}, {contract, Contract}};
+ {error, {overlapping_contract, []}} ->
+ {{F, A}, {contract, Contract}};
+ {error, invalid_contract} ->
+ CString = dialyzer_contracts:contract_to_string(Contract),
+ SigString = dialyzer_utils:format_sig(Sig, Records),
+ Msg = io_lib:format("Error in contract of function ~w:~w/~w\n"
+ "\t The contract is: " ++ CString ++ "\n" ++
+ "\t but the inferred signature is: ~s",
+ [M, F, A, SigString]),
+ fatal_error(Msg);
+ {error, ErrorStr} when is_list(ErrorStr) -> % ErrorStr is a string()
+ Msg = io_lib:format("Error in contract of function ~w:~w/~w: ~s",
+ [M, F, A, ErrorStr]),
+ fatal_error(Msg)
+ end
+ end.
+
+get_functions(File, Analysis) ->
+ case Analysis#analysis.mode of
+ ?SHOW ->
+ Funcs = map__lookup(File, Analysis#analysis.func),
+ Inc_Funcs = map__lookup(File, Analysis#analysis.inc_func),
+ remove_module_info(Funcs) ++ normalize_incFuncs(Inc_Funcs);
+ ?SHOW_EXPORTED ->
+ Ex_Funcs = map__lookup(File, Analysis#analysis.ex_func),
+ remove_module_info(Ex_Funcs);
+ ?ANNOTATE ->
+ Funcs = map__lookup(File, Analysis#analysis.func),
+ remove_module_info(Funcs);
+ ?ANNOTATE_INC_FILES ->
+ map__lookup(File, Analysis#analysis.inc_func)
+ end.
+
+normalize_incFuncs(Functions) ->
+ [FunInfo || {_FileName, FunInfo} <- Functions].
+
+-spec remove_module_info([func_info()]) -> [func_info()].
+
+remove_module_info(FunInfoList) ->
+ F = fun ({_,module_info,0}) -> false;
+ ({_,module_info,1}) -> false;
+ ({Line,F,A}) when is_integer(Line), is_atom(F), is_integer(A) -> true
+ end,
+ lists:filter(F, FunInfoList).
+
+write_typed_file(File, Info) ->
+ io:format(" Processing file: ~p\n", [File]),
+ Dir = filename:dirname(File),
+ RootName = filename:basename(filename:rootname(File)),
+ Ext = filename:extension(File),
+ TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR),
+ TmpNewFilename = lists:concat([RootName, ".ann", Ext]),
+ NewFileName = filename:join(TyperAnnDir, TmpNewFilename),
+ case file:make_dir(TyperAnnDir) of
+ {error, Reason} ->
+ case Reason of
+ eexist -> %% TypEr dir exists; remove old typer files
+ ok = file:delete(NewFileName),
+ write_typed_file(File, Info, NewFileName);
+ enospc ->
+ Msg = io_lib:format("Not enough space in ~p\n", [Dir]),
+ fatal_error(Msg);
+ eacces ->
+ Msg = io:format("No write permission in ~p\n", [Dir]),
+ fatal_error(Msg);
+ _ ->
+ Msg = io_lib:format("Unhandled error ~s when writing ~p\n",
+ [Reason, Dir]),
+ fatal_error(Msg)
+ end;
+ ok -> %% Typer dir does NOT exist
+ write_typed_file(File, Info, NewFileName)
+ end.
+
+write_typed_file(File, Info, NewFileName) ->
+ {ok, Binary} = file:read_file(File),
+ Chars = binary_to_list(Binary),
+ write_typed_file(Chars, NewFileName, Info, 1, []),
+ io:format(" Saved as: ~p\n", [NewFileName]).
+
+write_typed_file(Chars, File, #info{functions = []}, _LNo, _Acc) ->
+ ok = file:write_file(File, list_to_binary(Chars), [append]);
+write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) ->
+ [{Line,F,A}|RestFuncs] = Info#info.functions,
+ case Line of
+ 1 -> %% This will happen only for inc files
+ ok = raw_write(F, A, Info, File, []),
+ NewInfo = Info#info{functions = RestFuncs},
+ NewAcc = [],
+ write_typed_file(Chars, File, NewInfo, Line, NewAcc);
+ _ ->
+ case Ch of
+ 10 ->
+ NewLineNo = LineNo + 1,
+ {NewInfo, NewAcc} =
+ case NewLineNo of
+ Line ->
+ ok = raw_write(F, A, Info, File, [Ch|Acc]),
+ {Info#info{functions = RestFuncs}, []};
+ _ ->
+ {Info, [Ch|Acc]}
+ end,
+ write_typed_file(Chs, File, NewInfo, NewLineNo, NewAcc);
+ _ ->
+ write_typed_file(Chs, File, Info, LineNo, [Ch|Acc])
+ end
+ end.
+
+raw_write(F, A, Info, File, Content) ->
+ TypeInfo = get_type_string(F, A, Info, file),
+ ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n",
+ ContentBin = list_to_binary(ContentList),
+ file:write_file(File, ContentBin, [append]).
+
+get_type_string(F, A, Info, Mode) ->
+ Type = get_type_info({F,A}, Info#info.types),
+ TypeStr =
+ case Type of
+ {contract, C} ->
+ dialyzer_contracts:contract_to_string(C);
+ {RetType, ArgType} ->
+ Sig = erl_types:t_fun(ArgType, RetType),
+ dialyzer_utils:format_sig(Sig, Info#info.records)
+ end,
+ case Info#info.edoc of
+ false ->
+ case {Mode, Type} of
+ {file, {contract, _}} -> "";
+ _ ->
+ Prefix = lists:concat(["-spec ", F]),
+ lists:concat([Prefix, TypeStr, "."])
+ end;
+ true ->
+ Prefix = lists:concat(["%% @spec ", F]),
+ lists:concat([Prefix, TypeStr, "."])
+ end.
+
+show_type_info(File, Info) ->
+ io:format("\n%% File: ~p\n%% ", [File]),
+ OutputString = lists:concat(["~.", length(File)+8, "c~n"]),
+ io:fwrite(OutputString, [$-]),
+ Fun = fun ({_LineNo, F, A}) ->
+ TypeInfo = get_type_string(F, A, Info, show),
+ io:format("~s\n", [TypeInfo])
+ end,
+ lists:foreach(Fun, Info#info.functions).
+
+get_type_info(Func, Types) ->
+ case map__lookup(Func, Types) of
+ none ->
+ %% Note: Typeinfo of any function should exist in
+ %% the result offered by dialyzer, otherwise there
+ %% *must* be something wrong with the analysis
+ Msg = io_lib:format("No type info for function: ~p\n", [Func]),
+ fatal_error(Msg);
+ {contract, _Fun} = C -> C;
+ {_RetType, _ArgType} = RA -> RA
+ end.
+
+%%--------------------------------------------------------------------
+%% Processing of command-line options and arguments.
+%%--------------------------------------------------------------------
+
+-spec process_cl_args() -> {args(), analysis()}.
+
+process_cl_args() ->
+ ArgList = init:get_plain_arguments(),
+ %% io:format("Args is ~p\n", [ArgList]),
+ {Args, Analysis} = analyze_args(ArgList, #args{}, #analysis{}),
+ %% if the mode has not been set, set it to the default mode (show)
+ {Args, case Analysis#analysis.mode of
+ undefined -> Analysis#analysis{mode = ?SHOW};
+ Mode when is_atom(Mode) -> Analysis
+ end}.
+
+analyze_args([], Args, Analysis) ->
+ {Args, Analysis};
+analyze_args(ArgList, Args, Analysis) ->
+ {Result, Rest} = cl(ArgList),
+ {NewArgs, NewAnalysis} = analyze_result(Result, Args, Analysis),
+ analyze_args(Rest, NewArgs, NewAnalysis).
+
+cl(["-h"|_]) -> help_message();
+cl(["--help"|_]) -> help_message();
+cl(["-v"|_]) -> version_message();
+cl(["--version"|_]) -> version_message();
+cl(["--edoc"|Opts]) -> {edoc, Opts};
+cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts};
+cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
+cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
+cl(["--show_success_typings"|Opts]) -> {show_succ, Opts};
+cl(["--show-success-typings"|Opts]) -> {show_succ, Opts};
+cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts};
+cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts};
+cl(["--no_spec"|Opts]) -> {no_spec, Opts};
+cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts};
+cl(["-D"++Def|Opts]) ->
+ case Def of
+ "" -> fatal_error("no variable name specified after -D");
+ _ ->
+ DefPair = process_def_list(re:split(Def, "=", [{return, list}])),
+ {{def, DefPair}, Opts}
+ end;
+cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts};
+cl(["-I"++Dir|Opts]) ->
+ case Dir of
+ "" -> fatal_error("no include directory specified after -I");
+ _ -> {{inc, Dir}, Opts}
+ end;
+cl(["-T"|Opts]) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ case Files of
+ [] -> fatal_error("no file or directory specified after -T");
+ [_|_] -> {{trusted, Files}, RestOpts}
+ end;
+cl(["-r"|Opts]) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ {{files_r, Files}, RestOpts};
+cl(["-pa",Dir|Opts]) -> {{pa,Dir}, Opts};
+cl(["-pz",Dir|Opts]) -> {{pz,Dir}, Opts};
+cl(["-"++H|_]) -> fatal_error("unknown option -"++H);
+cl(Opts) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ {{files, Files}, RestOpts}.
+
+process_def_list(L) ->
+ case L of
+ [Name, Value] ->
+ {ok, Tokens, _} = erl_scan:string(Value ++ "."),
+ {ok, ErlValue} = erl_parse:parse_term(Tokens),
+ {list_to_atom(Name), ErlValue};
+ [Name] ->
+ {list_to_atom(Name), true}
+ end.
+
+%% Get information about files that the user trusts and wants to analyze
+analyze_result({files, Val}, Args, Analysis) ->
+ NewVal = Args#args.files ++ Val,
+ {Args#args{files = NewVal}, Analysis};
+analyze_result({files_r, Val}, Args, Analysis) ->
+ NewVal = Args#args.files_r ++ Val,
+ {Args#args{files_r = NewVal}, Analysis};
+analyze_result({trusted, Val}, Args, Analysis) ->
+ NewVal = Args#args.trusted ++ Val,
+ {Args#args{trusted = NewVal}, Analysis};
+analyze_result(edoc, Args, Analysis) ->
+ {Args, Analysis#analysis{edoc = true}};
+%% Get useful information for actual analysis
+analyze_result({mode, Mode}, Args, Analysis) ->
+ case Analysis#analysis.mode of
+ undefined -> {Args, Analysis#analysis{mode = Mode}};
+ OldMode -> mode_error(OldMode, Mode)
+ end;
+analyze_result({def, Val}, Args, Analysis) ->
+ NewVal = Analysis#analysis.macros ++ [Val],
+ {Args, Analysis#analysis{macros = NewVal}};
+analyze_result({inc, Val}, Args, Analysis) ->
+ NewVal = Analysis#analysis.includes ++ [Val],
+ {Args, Analysis#analysis{includes = NewVal}};
+analyze_result({plt, Plt}, Args, Analysis) ->
+ {Args, Analysis#analysis{plt = Plt}};
+analyze_result(show_succ, Args, Analysis) ->
+ {Args, Analysis#analysis{show_succ = true}};
+analyze_result(no_spec, Args, Analysis) ->
+ {Args, Analysis#analysis{no_spec = true}};
+analyze_result({pa, Dir}, Args, Analysis) ->
+ code:add_patha(Dir),
+ {Args, Analysis};
+analyze_result({pz, Dir}, Args, Analysis) ->
+ code:add_pathz(Dir),
+ {Args, Analysis}.
+
+%%--------------------------------------------------------------------
+%% File processing.
+%%--------------------------------------------------------------------
+
+-spec get_all_files(args()) -> [file:filename(),...].
+
+get_all_files(#args{files = Fs, files_r = Ds}) ->
+ case filter_fd(Fs, Ds, fun test_erl_file_exclude_ann/1) of
+ [] -> fatal_error("no file(s) to analyze");
+ AllFiles -> AllFiles
+ end.
--spec error(string()) -> no_return().
+-spec test_erl_file_exclude_ann(file:filename()) -> boolean().
-error(Slogan) ->
+test_erl_file_exclude_ann(File) ->
+ case is_erl_file(File) of
+ true -> %% Exclude files ending with ".ann.erl"
+ case re:run(File, "[\.]ann[\.]erl$") of
+ {match, _} -> false;
+ nomatch -> true
+ end;
+ false -> false
+ end.
+
+-spec is_erl_file(file:filename()) -> boolean().
+
+is_erl_file(File) ->
+ filename:extension(File) =:= ".erl".
+
+-type test_file_fun() :: fun((file:filename()) -> boolean()).
+
+-spec filter_fd(files(), files(), test_file_fun()) -> files().
+
+filter_fd(File_Dir, Dir_R, Fun) ->
+ All_File_1 = process_file_and_dir(File_Dir, Fun),
+ All_File_2 = process_dir_rec(Dir_R, Fun),
+ remove_dup(All_File_1 ++ All_File_2).
+
+-spec process_file_and_dir(files(), test_file_fun()) -> files().
+
+process_file_and_dir(File_Dir, TestFun) ->
+ Fun =
+ fun (Elem, Acc) ->
+ case filelib:is_regular(Elem) of
+ true -> process_file(Elem, TestFun, Acc);
+ false -> check_dir(Elem, false, Acc, TestFun)
+ end
+ end,
+ lists:foldl(Fun, [], File_Dir).
+
+-spec process_dir_rec(files(), test_file_fun()) -> files().
+
+process_dir_rec(Dirs, TestFun) ->
+ Fun = fun (Dir, Acc) -> check_dir(Dir, true, Acc, TestFun) end,
+ lists:foldl(Fun, [], Dirs).
+
+-spec check_dir(file:filename(), boolean(), files(), test_file_fun()) -> files().
+
+check_dir(Dir, Recursive, Acc, Fun) ->
+ case file:list_dir(Dir) of
+ {ok, Files} ->
+ {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir),
+ case Recursive of
+ false ->
+ FinalFiles = process_file_and_dir(TmpFiles, Fun),
+ Acc ++ FinalFiles;
+ true ->
+ TmpAcc1 = process_file_and_dir(TmpFiles, Fun),
+ TmpAcc2 = process_dir_rec(TmpDirs, Fun),
+ Acc ++ TmpAcc1 ++ TmpAcc2
+ end;
+ {error, eacces} ->
+ fatal_error("no access permission to dir \""++Dir++"\"");
+ {error, enoent} ->
+ fatal_error("cannot access "++Dir++": No such file or directory");
+ {error, _Reason} ->
+ fatal_error("error involving a use of file:list_dir/1")
+ end.
+
+%% Same order as the input list
+-spec process_file(file:filename(), test_file_fun(), files()) -> files().
+
+process_file(File, TestFun, Acc) ->
+ case TestFun(File) of
+ true -> Acc ++ [File];
+ false -> Acc
+ end.
+
+%% Same order as the input list
+-spec split_dirs_and_files(files(), file:filename()) -> {files(), files()}.
+
+split_dirs_and_files(Elems, Dir) ->
+ Test_Fun =
+ fun (Elem, {DirAcc, FileAcc}) ->
+ File = filename:join(Dir, Elem),
+ case filelib:is_regular(File) of
+ false -> {[File|DirAcc], FileAcc};
+ true -> {DirAcc, [File|FileAcc]}
+ end
+ end,
+ {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems),
+ {lists:reverse(Dirs), lists:reverse(Files)}.
+
+%% Removes duplicate filenames but keeps the order of the input list
+-spec remove_dup(files()) -> files().
+
+remove_dup(Files) ->
+ Test_Dup = fun (File, Acc) ->
+ case lists:member(File, Acc) of
+ true -> Acc;
+ false -> [File|Acc]
+ end
+ end,
+ Reversed_Elems = lists:foldl(Test_Dup, [], Files),
+ lists:reverse(Reversed_Elems).
+
+%%--------------------------------------------------------------------
+%% Collect information.
+%%--------------------------------------------------------------------
+
+-type inc_file_info() :: {file:filename(), func_info()}.
+
+-record(tmpAcc, {file :: file:filename(),
+ module :: atom(),
+ funcAcc = [] :: [func_info()],
+ incFuncAcc = [] :: [inc_file_info()],
+ dialyzerObj = [] :: [{mfa(), {_, _}}]}).
+
+-spec collect_info(analysis()) -> analysis().
+
+collect_info(Analysis) ->
+ NewPlt =
+ try get_dialyzer_plt(Analysis) of
+ DialyzerPlt ->
+ dialyzer_plt:merge_plts([Analysis#analysis.trust_plt, DialyzerPlt])
+ catch
+ throw:{dialyzer_error,_Reason} ->
+ fatal_error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
+ end,
+ NewAnalysis = lists:foldl(fun collect_one_file_info/2,
+ Analysis#analysis{trust_plt = NewPlt},
+ Analysis#analysis.files),
+ %% Process Remote Types
+ TmpCServer = NewAnalysis#analysis.codeserver,
+ NewCServer =
+ try
+ NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
+ NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer),
+ OldRecords = dialyzer_plt:get_types(NewPlt),
+ OldExpTypes = dialyzer_plt:get_exported_types(NewPlt),
+ MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ MergedExpTypes = sets:union(NewExpTypes, OldExpTypes),
+ %% io:format("Merged Records ~p",[MergedRecords]),
+ TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
+ TmpCServer2 =
+ dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
+ TmpCServer1),
+ TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3)
+ catch
+ throw:{error, ErrorMsg} ->
+ fatal_error(ErrorMsg)
+ end,
+ NewAnalysis#analysis{codeserver = NewCServer}.
+
+collect_one_file_info(File, Analysis) ->
+ Ds = [{d,Name,Val} || {Name,Val} <- Analysis#analysis.macros],
+ %% Current directory should also be included in "Includes".
+ Includes = [filename:dirname(File)|Analysis#analysis.includes],
+ Is = [{i,Dir} || Dir <- Includes],
+ Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
+ case dialyzer_utils:get_abstract_code_from_src(File, Options) of
+ {error, Reason} ->
+ %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
+ compile_error(Reason);
+ {ok, AbstractCode} ->
+ case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
+ error -> compile_error(["Could not get core erlang for "++File]);
+ {ok, Core} ->
+ case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, Records} ->
+ Mod = cerl:concrete(cerl:module_name(Core)),
+ case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, SpecInfo} ->
+ ExpTypes = get_exported_types_from_core(Core),
+ analyze_core_tree(Core, Records, SpecInfo, ExpTypes,
+ Analysis, File)
+ end
+ end
+ end
+ end.
+
+analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) ->
+ Module = cerl:concrete(cerl:module_name(Core)),
+ TmpTree = cerl:from_records(Core),
+ CS1 = Analysis#analysis.codeserver,
+ NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
+ {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
+ CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
+ CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
+ CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
+ CS5 =
+ case Analysis#analysis.no_spec of
+ true -> CS4;
+ false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4)
+ end,
+ OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CS5),
+ MergedExpTypes = sets:union(ExpTypes, OldExpTypes),
+ CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5),
+ Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
+ TmpCG = Analysis#analysis.callgraph,
+ CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
+ Fun = fun analyze_one_function/2,
+ All_Defs = cerl:module_defs(Tree),
+ Acc = lists:foldl(Fun, #tmpAcc{file = File, module = Module}, All_Defs),
+ Exported_FuncMap = map__insert({File, Ex_Funcs}, Analysis#analysis.ex_func),
+ %% we must sort all functions in the file which
+ %% originate from this file by *numerical order* of lineNo
+ Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
+ FuncMap = map__insert({File, Sorted_Functions}, Analysis#analysis.func),
+ %% we do not need to sort functions which are imported from included files
+ IncFuncMap = map__insert({File, Acc#tmpAcc.incFuncAcc},
+ Analysis#analysis.inc_func),
+ FMs = Analysis#analysis.fms ++ [{File, Module}],
+ RecordMap = map__insert({File, Records}, Analysis#analysis.record),
+ Analysis#analysis{fms = FMs,
+ callgraph = CG,
+ codeserver = CS6,
+ ex_func = Exported_FuncMap,
+ inc_func = IncFuncMap,
+ record = RecordMap,
+ func = FuncMap}.
+
+analyze_one_function({Var, FunBody} = Function, Acc) ->
+ F = cerl:fname_id(Var),
+ A = cerl:fname_arity(Var),
+ TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
+ NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
+ [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
+ BaseName = filename:basename(FileName),
+ FuncInfo = {LineNo, F, A},
+ OriginalName = Acc#tmpAcc.file,
+ {FuncAcc, IncFuncAcc} =
+ case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
+ true -> %% Coming from original file
+ %% io:format("Added function ~p\n", [{LineNo, F, A}]),
+ {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
+ false ->
+ %% Coming from other sourses, including:
+ %% -- .yrl (yecc-generated file)
+ %% -- yeccpre.hrl (yecc-generated file)
+ %% -- other cases
+ {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
+ end,
+ Acc#tmpAcc{funcAcc = FuncAcc,
+ incFuncAcc = IncFuncAcc,
+ dialyzerObj = NewDialyzerObj}.
+
+-spec get_dialyzer_plt(analysis()) -> plt().
+
+get_dialyzer_plt(#analysis{plt = PltFile0}) ->
+ PltFile =
+ case PltFile0 =:= none of
+ true -> dialyzer_plt:get_default_plt();
+ false -> PltFile0
+ end,
+ dialyzer_plt:from_file(PltFile).
+
+%% Exported Types
+
+get_exported_types_from_core(Core) ->
+ Attrs = cerl:module_attrs(Core),
+ ExpTypes1 = [cerl:concrete(L2) || {L1, L2} <- Attrs,
+ cerl:is_literal(L1),
+ cerl:is_literal(L2),
+ cerl:concrete(L1) =:= 'export_type'],
+ ExpTypes2 = lists:flatten(ExpTypes1),
+ M = cerl:atom_val(cerl:module_name(Core)),
+ sets:from_list([{M, F, A} || {F, A} <- ExpTypes2]).
+
+%%--------------------------------------------------------------------
+%% Utilities for error reporting.
+%%--------------------------------------------------------------------
+
+-spec fatal_error(string()) -> no_return().
+
+fatal_error(Slogan) ->
msg(io_lib:format("typer: ~s\n", [Slogan])),
erlang:halt(1).
-%%--------------------------------------------------------------------
+-spec mode_error(mode(), mode()) -> no_return().
+
+mode_error(OldMode, NewMode) ->
+ Msg = io_lib:format("Mode was previously set to '~s'; "
+ "can not set it to '~s' now",
+ [OldMode, NewMode]),
+ fatal_error(Msg).
-spec compile_error([string()]) -> no_return().
compile_error(Reason) ->
JoinedString = lists:flatten([X ++ "\n" || X <- Reason]),
Msg = "Analysis failed with error report:\n" ++ JoinedString,
- error(Msg).
-
-%%--------------------------------------------------------------------
-%% Outputs a message on 'stderr', if possible.
-%%--------------------------------------------------------------------
+ fatal_error(Msg).
-spec msg(string()) -> 'ok'.
msg(Msg) ->
case os:type() of
- {unix, _} ->
+ {unix, _} -> % Output a message on 'stderr', if possible
P = open_port({fd, 0, 2}, [out]),
port_command(P, Msg),
true = port_close(P),
@@ -195,3 +1003,111 @@ msg(Msg) ->
end.
%%--------------------------------------------------------------------
+%% Version and help messages.
+%%--------------------------------------------------------------------
+
+-spec version_message() -> no_return().
+
+version_message() ->
+ io:format("TypEr version "++?VSN++"\n"),
+ erlang:halt(0).
+
+-spec help_message() -> no_return().
+
+help_message() ->
+ S = <<" Usage: typer [--help] [--version] [--plt PLT] [--edoc]
+ [--show | --show-exported | --annotate | --annotate-inc-files]
+ [-Ddefine]* [-I include_dir]* [-pa dir]* [-pz dir]*
+ [-T application]* [-r] file*
+
+ Options:
+ -r dir*
+ search directories recursively for .erl files below them
+ --show
+ Prints type specifications for all functions on stdout.
+ (this is the default behaviour; this option is not really needed)
+ --show-exported (or --show_exported)
+ Same as --show, but prints specifications for exported functions only
+ Specs are displayed sorted alphabetically on the function's name
+ --annotate
+ Annotates the specified files with type specifications
+ --annotate-inc-files
+ Same as --annotate but annotates all -include() files as well as
+ all .erl files (use this option with caution - has not been tested much)
+ --edoc
+ Prints type information as Edoc @spec comments, not as type specs
+ --plt PLT
+ Use the specified dialyzer PLT file rather than the default one
+ -T file*
+ The specified file(s) already contain type specifications and these
+ are to be trusted in order to print specs for the rest of the files
+ (Multiple files or dirs, separated by spaces, can be specified.)
+ -Dname (or -Dname=value)
+ pass the defined name(s) to TypEr
+ (The syntax of defines is the same as that used by \"erlc\".)
+ -I include_dir
+ pass the include_dir to TypEr
+ (The syntax of includes is the same as that used by \"erlc\".)
+ -pa dir
+ -pz dir
+ Set code path options to TypEr
+ (This is useful for files that use parse tranforms.)
+ --version (or -v)
+ prints the Typer version and exits
+ --help (or -h)
+ prints this message and exits
+
+ Note:
+ * denotes that multiple occurrences of these options are possible.
+">>,
+ io:put_chars(S),
+ erlang:halt(0).
+
+%%--------------------------------------------------------------------
+%% Handle messages.
+%%--------------------------------------------------------------------
+
+rcv_ext_types() ->
+ Self = self(),
+ Self ! {Self, done},
+ rcv_ext_types(Self, []).
+
+rcv_ext_types(Self, ExtTypes) ->
+ receive
+ {Self, ext_types, ExtType} ->
+ rcv_ext_types(Self, [ExtType|ExtTypes]);
+ {Self, done} ->
+ lists:usort(ExtTypes)
+ end.
+
+%%--------------------------------------------------------------------
+%% A convenient abstraction of a Key-Value mapping data structure
+%% specialized for the uses in this module
+%%--------------------------------------------------------------------
+
+-type map() :: dict().
+
+-spec map__new() -> map().
+map__new() ->
+ dict:new().
+
+-spec map__insert({term(), term()}, map()) -> map().
+map__insert(Object, Map) ->
+ {Key, Value} = Object,
+ dict:store(Key, Value, Map).
+
+-spec map__lookup(term(), map()) -> term().
+map__lookup(Key, Map) ->
+ try dict:fetch(Key, Map) catch error:_ -> none end.
+
+-spec map__from_list([{fa(), term()}]) -> map().
+map__from_list(List) ->
+ dict:from_list(List).
+
+-spec map__remove(term(), map()) -> map().
+map__remove(Key, Dict) ->
+ dict:erase(Key, Dict).
+
+-spec map__fold(fun((term(), term(), term()) -> map()), map(), map()) -> map().
+map__fold(Fun, Acc0, Dict) ->
+ dict:fold(Fun, Acc0, Dict).
diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl
deleted file mode 100644
index c331dd82db..0000000000
--- a/lib/typer/src/typer.hrl
+++ /dev/null
@@ -1,64 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--define(SHOW, show).
--define(SHOW_EXPORTED, show_exported).
--define(ANNOTATE, annotate).
--define(ANNOTATE_INC_FILES, annotate_inc_files).
-
--type mode() :: ?SHOW | ?SHOW_EXPORTED | ?ANNOTATE | ?ANNOTATE_INC_FILES.
-
--record(typer_analysis,
- {mode :: mode(),
- macros = [] :: [{atom(), _}], % {macro_name, value}
- includes = [] :: [string()],
-
- %% Esp for Dialyzer
- %% ----------------------
- code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(),
- callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(),
- ana_files = [] :: [string()], % absolute filenames
- plt = none :: 'none' | string(),
-
- %% Esp for TypEr
- %% ----------------------
- t_files = [] :: [string()],
-
- %% For choosing between contracts or comments
- contracts = true :: boolean(),
-
- %% Any file in 'final_files' is compilable.
- %% And we need to keep it as {FileName,ModuleName}
- %% in case filename does NOT match with moduleName
- final_files = [] :: [{string(), atom()}],
-
- ex_func = typer_map:new() :: dict(),
- record = typer_map:new() :: dict(),
-
- %% Functions: the line number of the function
- %% should be kept as well
- func = typer_map:new() :: dict(),
- inc_func = typer_map:new() :: dict(),
- trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}).
-
--record(args,
- {analyze = [] :: [string()],
- analyzed_dir_r = [] :: [string()],
- trust = [] :: [string()]}).
diff --git a/lib/typer/src/typer_annotator.erl b/lib/typer/src/typer_annotator.erl
deleted file mode 100644
index 68a8f03a5c..0000000000
--- a/lib/typer/src/typer_annotator.erl
+++ /dev/null
@@ -1,384 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%============================================================================
-%% File : typer_annotator.erl
-%% Author : Bingwen He <[email protected]>
-%% Description :
-%% If file 'FILENAME' has been analyzed, then the output of
-%% command "diff -B FILENAME.erl typer_ann/FILENAME.ann.erl"
-%% should be exactly what TypEr has added, namely type info.
-%%============================================================================
-
--module(typer_annotator).
-
--export([annotate/1]).
-
-%%----------------------------------------------------------------------------
-
--include("typer.hrl").
-
-%%----------------------------------------------------------------------------
-
--define(TYPER_ANN_DIR, "typer_ann").
-
--type func_info() :: {non_neg_integer(), atom(), arity()}.
-
--record(info, {recMap = typer_map:new() :: dict(),
- funcs = [] :: [func_info()],
- typeMap :: dict(),
- contracts :: boolean()}).
--record(inc, {map = typer_map:new() :: dict(),
- filter = [] :: [string()]}).
-
-%%----------------------------------------------------------------------------
-
--spec annotate(#typer_analysis{}) -> 'ok'.
-
-annotate(Analysis) ->
- case Analysis#typer_analysis.mode of
- ?SHOW -> show(Analysis);
- ?SHOW_EXPORTED -> show(Analysis);
- ?ANNOTATE ->
- Fun = fun({File, Module}) ->
- Info = get_final_info(File, Module, Analysis),
- write_typed_file(File, Info)
- end,
- lists:foreach(Fun, Analysis#typer_analysis.final_files);
- ?ANNOTATE_INC_FILES ->
- IncInfo = write_and_collect_inc_info(Analysis),
- write_inc_files(IncInfo)
- end.
-
-write_and_collect_inc_info(Analysis) ->
- Fun = fun({File, Module}, Inc) ->
- Info = get_final_info(File, Module, Analysis),
- write_typed_file(File, Info),
- IncFuns = get_functions(File, Analysis),
- collect_imported_funcs(IncFuns, Info#info.typeMap, Inc)
- end,
- NewInc = lists:foldl(Fun,#inc{}, Analysis#typer_analysis.final_files),
- clean_inc(NewInc).
-
-write_inc_files(Inc) ->
- Fun =
- fun (File) ->
- Val = typer_map:lookup(File,Inc#inc.map),
- %% Val is function with its type info
- %% in form [{{Line,F,A},Type}]
- Functions = [Key || {Key,_} <- Val],
- Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val],
- Info = #info{typeMap = typer_map:from_list(Val1),
- recMap = typer_map:new(),
- %% Note we need to sort functions here!
- funcs = lists:keysort(1, Functions)},
- %% io:format("TypeMap ~p\n", [Info#info.typeMap]),
- %% io:format("Funcs ~p\n", [Info#info.funcs]),
- %% io:format("RecMap ~p\n", [Info#info.recMap]),
- write_typed_file(File, Info)
- end,
- lists:foreach(Fun, dict:fetch_keys(Inc#inc.map)).
-
-show(Analysis) ->
- Fun = fun({File, Module}) ->
- Info = get_final_info(File, Module, Analysis),
- show_type_info_only(File, Info)
- end,
- lists:foreach(Fun, Analysis#typer_analysis.final_files).
-
-get_final_info(File, Module, Analysis) ->
- RecMap = get_recMap(File, Analysis),
- TypeMap = get_typeMap(Module, Analysis,RecMap),
- Functions = get_functions(File, Analysis),
- Contracts = Analysis#typer_analysis.contracts,
- #info{recMap=RecMap, funcs=Functions, typeMap=TypeMap, contracts=Contracts}.
-
-collect_imported_funcs(Funcs, TypeMap, TmpInc) ->
- %% Coming from other sourses, including:
- %% FIXME: How to deal with yecc-generated file????
- %% --.yrl (yecc-generated file)???
- %% -- yeccpre.hrl (yecc-generated file)???
- %% -- other cases
- Fun = fun({File,_} = Obj, Inc) ->
- case is_yecc_file(File, Inc) of
- {yecc_generated, NewInc} -> NewInc;
- {not_yecc, NewInc} ->
- check_imported_funcs(Obj, NewInc, TypeMap)
- end
- end,
- lists:foldl(Fun, TmpInc, Funcs).
-
--spec is_yecc_file(string(), #inc{}) -> {'not_yecc', #inc{}}
- | {'yecc_generated', #inc{}}.
-is_yecc_file(File, Inc) ->
- case lists:member(File, Inc#inc.filter) of
- true -> {yecc_generated, Inc};
- false ->
- case filename:extension(File) of
- ".yrl" ->
- Rootname = filename:rootname(File, ".yrl"),
- Obj = Rootname ++ ".erl",
- case lists:member(Obj, Inc#inc.filter) of
- true -> {yecc_generated, Inc};
- false ->
- NewFilter = [Obj|Inc#inc.filter],
- NewInc = Inc#inc{filter = NewFilter},
- {yecc_generated, NewInc}
- end;
- _ ->
- case filename:basename(File) of
- "yeccpre.hrl" -> {yecc_generated, Inc};
- _ -> {not_yecc, Inc}
- end
- end
- end.
-
-check_imported_funcs({File, {Line, F, A}}, Inc, TypeMap) ->
- IncMap = Inc#inc.map,
- FA = {F, A},
- Type = get_type_info(FA, TypeMap),
- case typer_map:lookup(File, IncMap) of
- none -> %% File is not added. Add it
- Obj = {File,[{FA, {Line, Type}}]},
- NewMap = typer_map:insert(Obj, IncMap),
- Inc#inc{map = NewMap};
- Val -> %% File is already in. Check.
- case lists:keyfind(FA, 1, Val) of
- false ->
- %% Function is not in; add it
- Obj = {File, Val ++ [{FA, {Line, Type}}]},
- NewMap = typer_map:insert(Obj, IncMap),
- Inc#inc{map = NewMap};
- Type ->
- %% Function is in and with same type
- Inc;
- _ ->
- %% Function is in but with diff type
- inc_warning(FA, File),
- Elem = lists:keydelete(FA, 1, Val),
- NewMap = case Elem of
- [] ->
- typer_map:remove(File, IncMap);
- _ ->
- typer_map:insert({File, Elem}, IncMap)
- end,
- Inc#inc{map = NewMap}
- end
- end.
-
-inc_warning({F, A}, File) ->
- io:format(" ***Warning: Skip function ~p/~p ", [F, A]),
- io:format("in file ~p because of inconsistent type\n", [File]).
-
-clean_inc(Inc) ->
- Inc1 = remove_yecc_generated_file(Inc),
- normalize_obj(Inc1).
-
-remove_yecc_generated_file(TmpInc) ->
- Fun = fun(Key, Inc) ->
- NewMap = typer_map:remove(Key, Inc#inc.map),
- Inc#inc{map = NewMap}
- end,
- lists:foldl(Fun, TmpInc, TmpInc#inc.filter).
-
-normalize_obj(TmpInc) ->
- Fun = fun(Key, Val, Inc) ->
- NewVal = [{{Line,F,A},Type} || {{F,A},{Line,Type}} <- Val],
- typer_map:insert({Key,NewVal}, Inc)
- end,
- NewMap = typer_map:fold(Fun, typer_map:new(), TmpInc#inc.map),
- TmpInc#inc{map = NewMap}.
-
-get_recMap(File, Analysis) ->
- typer_map:lookup(File, Analysis#typer_analysis.record).
-
-get_typeMap(Module, Analysis, RecMap) ->
- TypeInfoPlt = Analysis#typer_analysis.trust_plt,
- TypeInfo =
- case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of
- none -> [];
- {value, List} -> List
- end,
- CodeServer = Analysis#typer_analysis.code_server,
- TypeInfoList = [get_type(I, CodeServer, RecMap) || I <- TypeInfo],
- typer_map:from_list(TypeInfoList).
-
-get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, RecMap) ->
- case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of
- error ->
- {{F, A}, {Range, Arg}};
- {ok, {_FileLine, Contract}} ->
- Sig = erl_types:t_fun(Arg, Range),
- case dialyzer_contracts:check_contract(Contract, Sig) of
- ok -> {{F, A}, {contract, Contract}};
- {error, {extra_range, _, _}} ->
- {{F, A}, {contract, Contract}};
- {error, invalid_contract} ->
- CString = dialyzer_contracts:contract_to_string(Contract),
- SigString = dialyzer_utils:format_sig(Sig, RecMap),
- typer:error(
- io_lib:format("Error in contract of function ~w:~w/~w\n"
- "\t The contract is: " ++ CString ++ "\n" ++
- "\t but the inferred signature is: ~s",
- [M, F, A, SigString]));
- {error, Msg} when is_list(Msg) -> % Msg is a string()
- typer:error(
- io_lib:format("Error in contract of function ~w:~w/~w: ~s",
- [M, F, A, Msg]))
- end
- end.
-
-get_functions(File, Analysis) ->
- case Analysis#typer_analysis.mode of
- ?SHOW ->
- Funcs = typer_map:lookup(File, Analysis#typer_analysis.func),
- Inc_Funcs = typer_map:lookup(File, Analysis#typer_analysis.inc_func),
- remove_module_info(Funcs) ++ normalize_incFuncs(Inc_Funcs);
- ?SHOW_EXPORTED ->
- Ex_Funcs = typer_map:lookup(File, Analysis#typer_analysis.ex_func),
- remove_module_info(Ex_Funcs);
- ?ANNOTATE ->
- Funcs = typer_map:lookup(File, Analysis#typer_analysis.func),
- remove_module_info(Funcs);
- ?ANNOTATE_INC_FILES ->
- typer_map:lookup(File, Analysis#typer_analysis.inc_func)
- end.
-
-normalize_incFuncs(Funcs) ->
- [FuncInfo || {_FileName, FuncInfo} <- Funcs].
-
--spec remove_module_info([func_info()]) -> [func_info()].
-
-remove_module_info(FuncInfoList) ->
- F = fun ({_,module_info,0}) -> false;
- ({_,module_info,1}) -> false;
- ({Line,F,A}) when is_integer(Line), is_atom(F), is_integer(A) -> true
- end,
- lists:filter(F, FuncInfoList).
-
-write_typed_file(File, Info) ->
- io:format(" Processing file: ~p\n", [File]),
- Dir = filename:dirname(File),
- RootName = filename:basename(filename:rootname(File)),
- Ext = filename:extension(File),
- TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR),
- TmpNewFilename = lists:concat([RootName,".ann",Ext]),
- NewFileName = filename:join(TyperAnnDir, TmpNewFilename),
- case file:make_dir(TyperAnnDir) of
- {error, Reason} ->
- case Reason of
- eexist -> %% TypEr dir exists; remove old typer files
- ok = file:delete(NewFileName),
- write_typed_file(File, Info, NewFileName);
- enospc ->
- io:format(" Not enough space in ~p\n", [Dir]);
- eacces ->
- io:format(" No write permission in ~p\n", [Dir]);
- _ ->
- io:format("Unknown error when writing ~p\n", [Dir]),
- halt()
- end;
- ok -> %% Typer dir does NOT exist
- write_typed_file(File, Info, NewFileName)
- end.
-
-write_typed_file(File, Info, NewFileName) ->
- {ok, Binary} = file:read_file(File),
- Chars = binary_to_list(Binary),
- write_typed_file(Chars, NewFileName, Info, 1, []),
- io:format(" Saved as: ~p\n", [NewFileName]).
-
-write_typed_file(Chars, File, #info{funcs = []}, _LNo, _Acc) ->
- ok = file:write_file(File, list_to_binary(Chars), [append]);
-write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) ->
- [{Line,F,A}|RestFuncs] = Info#info.funcs,
- case Line of
- 1 -> %% This will happen only for inc files
- ok = raw_write(F, A, Info, File, []),
- NewInfo = Info#info{funcs = RestFuncs},
- NewAcc = [],
- write_typed_file(Chars, File, NewInfo, Line, NewAcc);
- _ ->
- case Ch of
- 10 ->
- NewLineNo = LineNo + 1,
- {NewInfo, NewAcc} =
- case NewLineNo of
- Line ->
- ok = raw_write(F, A, Info, File, [Ch|Acc]),
- {Info#info{funcs = RestFuncs}, []};
- _ ->
- {Info, [Ch|Acc]}
- end,
- write_typed_file(Chs, File, NewInfo, NewLineNo, NewAcc);
- _ ->
- write_typed_file(Chs, File, Info, LineNo, [Ch|Acc])
- end
- end.
-
-raw_write(F, A, Info, File, Content) ->
- TypeInfo = get_type_string(F, A, Info, file),
- ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n",
- ContentBin = list_to_binary(ContentList),
- file:write_file(File, ContentBin, [append]).
-
-get_type_string(F, A, Info, Mode) ->
- Type = get_type_info({F,A}, Info#info.typeMap),
- TypeStr =
- case Type of
- {contract, C} ->
- dialyzer_contracts:contract_to_string(C);
- {RetType, ArgType} ->
- dialyzer_utils:format_sig(erl_types:t_fun(ArgType, RetType),
- Info#info.recMap)
- end,
- case Info#info.contracts of
- true ->
- case {Mode, Type} of
- {file, {contract, _}} -> "";
- _ ->
- Prefix = lists:concat(["-spec ", F]),
- lists:concat([Prefix, TypeStr, "."])
- end;
- false ->
- Prefix = lists:concat(["%% @spec ", F]),
- lists:concat([Prefix, TypeStr, "."])
- end.
-
-show_type_info_only(File, Info) ->
- io:format("\n%% File: ~p\n%% ", [File]),
- OutputString = lists:concat(["~.", length(File)+8, "c~n"]),
- io:fwrite(OutputString, [$-]),
- Fun = fun ({_LineNo, F, A}) ->
- TypeInfo = get_type_string(F, A, Info, show),
- io:format("~s\n", [TypeInfo])
- end,
- lists:foreach(Fun, Info#info.funcs).
-
-get_type_info(Func, TypeMap) ->
- case typer_map:lookup(Func, TypeMap) of
- none ->
- %% Note: Typeinfo of any function should exist in
- %% the result offered by dialyzer, otherwise there
- %% *must* be something wrong with the analysis
- io:format("No type info for function: ~p\n", [Func]),
- halt();
- {contract, _Fun} = C -> C;
- {_RetType, _ArgType} = RA -> RA
- end.
diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl
deleted file mode 100644
index ea25fa6f68..0000000000
--- a/lib/typer/src/typer_info.erl
+++ /dev/null
@@ -1,162 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(typer_info).
-
--export([collect/1]).
-
--type func_info() :: {non_neg_integer(), atom(), arity()}.
--type inc_file_info() :: {string(), func_info()}.
-
--record(tmpAcc, {file :: string(),
- module :: atom(),
- funcAcc=[] :: [func_info()],
- incFuncAcc=[] :: [inc_file_info()],
- dialyzerObj=[] :: [{mfa(), {_, _}}]}).
-
--include("typer.hrl").
-
--spec collect(#typer_analysis{}) -> #typer_analysis{}.
-
-collect(Analysis) ->
- NewPlt =
- try get_dialyzer_plt(Analysis) of
- DialyzerPlt ->
- dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt])
- catch
- throw:{dialyzer_error,_Reason} ->
- typer:error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
- end,
- NewAnalysis = lists:foldl(fun collect_one_file_info/2,
- Analysis#typer_analysis{trust_plt = NewPlt},
- Analysis#typer_analysis.ana_files),
- %% Process Remote Types
- TmpCServer = NewAnalysis#typer_analysis.code_server,
- NewCServer =
- try
- NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
- OldRecords = dialyzer_plt:get_types(NewPlt),
- MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
- %% io:format("Merged Records ~p",[MergedRecords]),
- TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
- TmpCServer2 = dialyzer_utils:process_record_remote_types(TmpCServer1),
- dialyzer_contracts:process_contract_remote_types(TmpCServer2)
- catch
- throw:{error, ErrorMsg} ->
- typer:error(ErrorMsg)
- end,
- NewAnalysis#typer_analysis{code_server = NewCServer}.
-
-collect_one_file_info(File, Analysis) ->
- Ds = [{d,Name,Val} || {Name,Val} <- Analysis#typer_analysis.macros],
- %% Current directory should also be included in "Includes".
- Includes = [filename:dirname(File)|Analysis#typer_analysis.includes],
- Is = [{i,Dir} || Dir <- Includes],
- Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
- case dialyzer_utils:get_abstract_code_from_src(File, Options) of
- {error, Reason} ->
- %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
- typer:compile_error(Reason);
- {ok, AbstractCode} ->
- case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
- error -> typer:compile_error(["Could not get core erlang for "++File]);
- {ok, Core} ->
- case dialyzer_utils:get_record_and_type_info(AbstractCode) of
- {error, Reason} -> typer:compile_error([Reason]);
- {ok, Records} ->
- Mod = list_to_atom(filename:basename(File, ".erl")),
- case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
- {error, Reason} -> typer:compile_error([Reason]);
- {ok, SpecInfo} ->
- analyze_core_tree(Core, Records, SpecInfo, Analysis, File)
- end
- end
- end
- end.
-
-analyze_core_tree(Core, Records, SpecInfo, Analysis, File) ->
- Module = list_to_atom(filename:basename(File, ".erl")),
- TmpTree = cerl:from_records(Core),
- CS1 = Analysis#typer_analysis.code_server,
- NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
- {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
- CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
- CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
- CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
- CS5 = dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4),
- Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
- TmpCG = Analysis#typer_analysis.callgraph,
- CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
- Fun = fun analyze_one_function/2,
- All_Defs = cerl:module_defs(Tree),
- Acc = lists:foldl(Fun, #tmpAcc{file=File, module=Module}, All_Defs),
- Exported_FuncMap = typer_map:insert({File, Ex_Funcs},
- Analysis#typer_analysis.ex_func),
- %% NOTE: we must sort all functions in the file which
- %% originate from this file by *numerical order* of lineNo
- Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
- FuncMap = typer_map:insert({File, Sorted_Functions},
- Analysis#typer_analysis.func),
- %% NOTE: However we do not need to sort functions
- %% which are imported from included files.
- IncFuncMap = typer_map:insert({File, Acc#tmpAcc.incFuncAcc},
- Analysis#typer_analysis.inc_func),
- Final_Files = Analysis#typer_analysis.final_files ++ [{File, Module}],
- RecordMap = typer_map:insert({File, Records}, Analysis#typer_analysis.record),
- Analysis#typer_analysis{final_files=Final_Files,
- callgraph=CG,
- code_server=CS5,
- ex_func=Exported_FuncMap,
- inc_func=IncFuncMap,
- record=RecordMap,
- func=FuncMap}.
-
-analyze_one_function({Var, FunBody} = Function, Acc) ->
- F = cerl:fname_id(Var),
- A = cerl:fname_arity(Var),
- TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
- NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
- [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
- BaseName = filename:basename(FileName),
- FuncInfo = {LineNo, F, A},
- OriginalName = Acc#tmpAcc.file,
- {FuncAcc, IncFuncAcc} =
- case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
- true -> %% Coming from original file
- %% io:format("Added function ~p\n", [{LineNo, F, A}]),
- {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
- false ->
- %% Coming from other sourses, including:
- %% -- .yrl (yecc-generated file)
- %% -- yeccpre.hrl (yecc-generated file)
- %% -- other cases
- {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
- end,
- Acc#tmpAcc{funcAcc = FuncAcc,
- incFuncAcc = IncFuncAcc,
- dialyzerObj = NewDialyzerObj}.
-
-get_dialyzer_plt(#typer_analysis{plt = PltFile0}) ->
- PltFile =
- case PltFile0 =:= none of
- true -> dialyzer_plt:get_default_plt();
- false -> PltFile0
- end,
- dialyzer_plt:from_file(PltFile).
diff --git a/lib/typer/src/typer_map.erl b/lib/typer/src/typer_map.erl
deleted file mode 100644
index bf62dea651..0000000000
--- a/lib/typer/src/typer_map.erl
+++ /dev/null
@@ -1,47 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(typer_map).
-
--export([new/0, insert/2, lookup/2, from_list/1, remove/2, fold/3]).
-
--spec new() -> dict().
-new() ->
- dict:new().
-
--spec insert({term(), term()}, dict()) -> dict().
-insert(Object, Dict) ->
- {Key, Value} = Object,
- dict:store(Key, Value, Dict).
-
--spec lookup(term(), dict()) -> any().
-lookup(Key, Dict) ->
- try dict:fetch(Key, Dict) catch error:_ -> none end.
-
--spec from_list([{term(), term()}]) -> dict().
-from_list(List) ->
- dict:from_list(List).
-
--spec remove(term(), dict()) -> dict().
-remove(Key, Dict) ->
- dict:erase(Key, Dict).
-
--spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term().
-fold(Fun, Acc0, Dict) ->
- dict:fold(Fun, Acc0, Dict).
diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl
deleted file mode 100644
index 1e53b1b305..0000000000
--- a/lib/typer/src/typer_options.erl
+++ /dev/null
@@ -1,191 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%===========================================================================
-%% File : typer_options.erl
-%% Author : Bingwen He <[email protected]>
-%% Description : Handles all command-line options given to TypEr
-%%===========================================================================
-
--module(typer_options).
-
--export([process/0]).
-
-%%---------------------------------------------------------------------------
-
--include("typer.hrl").
-
-%%---------------------------------------------------------------------------
-%% Exported functions
-%%---------------------------------------------------------------------------
-
--spec process() -> {#args{}, #typer_analysis{}}.
-
-process() ->
- ArgList = init:get_plain_arguments(),
- %% io:format("Args is ~p\n",[Args]),
- {Args, Analysis} = analyze_args(ArgList, #args{}, #typer_analysis{}),
- %% if the mode has not been set, set it to the default mode (show)
- {Args, case Analysis#typer_analysis.mode of
- undefined -> Analysis#typer_analysis{mode = ?SHOW};
- Mode when is_atom(Mode) -> Analysis
- end}.
-
-%%---------------------------------------------------------------------------
-%% Internal functions
-%%---------------------------------------------------------------------------
-
-analyze_args([], Args, Analysis) ->
- {Args, Analysis};
-analyze_args(ArgList, Args, Analysis) ->
- {Result, Rest} = cl(ArgList),
- {NewArgs, NewAnalysis} = analyze_result(Result, Args, Analysis),
- analyze_args(Rest, NewArgs, NewAnalysis).
-
-cl(["-h"|_]) -> help_message();
-cl(["--help"|_]) -> help_message();
-cl(["-v"|_]) -> version_message();
-cl(["--version"|_]) -> version_message();
-cl(["--comments"|Opts]) -> {comments, Opts};
-cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts};
-cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
-cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
-cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts};
-cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts};
-cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts};
-cl(["-D"++Def|Opts]) ->
- case Def of
- "" -> typer:error("no variable name specified after -D");
- _ ->
- L = re:split(Def, "=", [{return, list}]),
- DefPair = process_def_list(L),
- {{def, DefPair}, Opts}
- end;
-cl(["-I",Dir|Opts]) -> {{inc,Dir}, Opts};
-cl(["-I"++Dir|Opts]) ->
- case Dir of
- "" -> typer:error("no include directory specified after -I");
- _ -> {{inc, Dir}, Opts}
- end;
-cl(["-T"|Opts]) ->
- {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
- case Files of
- [] -> typer:error("no file or directory specified after -T");
- [_|_] -> {{trust, Files}, RestOpts}
- end;
-cl(["-r"|Opts]) ->
- {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
- {{a_dir_r, Files}, RestOpts};
-cl(["-"++H|_]) -> typer:error("unknown option -"++H);
-cl(Opts) ->
- {Args, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
- {{analyze, Args}, RestOpts}.
-
-process_def_list(L) ->
- case L of
- [Name, Value] ->
- {ok, Tokens, _} = erl_scan:string(Value ++ "."),
- {ok, ErlValue} = erl_parse:parse_term(Tokens),
- {list_to_atom(Name), ErlValue};
- [Name] ->
- {list_to_atom(Name), true}
- end.
-
-%% Get information about files that the user trusts and wants to analyze
-analyze_result({analyze, Val}, Args, Analysis) ->
- NewVal = Args#args.analyze ++ Val,
- {Args#args{analyze = NewVal}, Analysis};
-analyze_result({a_dir_r, Val}, Args, Analysis) ->
- NewVal = Args#args.analyzed_dir_r ++ Val,
- {Args#args{analyzed_dir_r = NewVal}, Analysis};
-analyze_result({trust, Val}, Args, Analysis) ->
- NewVal = Args#args.trust ++ Val,
- {Args#args{trust = NewVal}, Analysis};
-analyze_result(comments, Args, Analysis) ->
- {Args, Analysis#typer_analysis{contracts = false}};
-%% Get useful information for actual analysis
-analyze_result({mode, Val}, Args, Analysis) ->
- case Analysis#typer_analysis.mode of
- undefined -> {Args, Analysis#typer_analysis{mode = Val}};
- _ -> mode_error()
- end;
-analyze_result({def, Val}, Args, Analysis) ->
- NewVal = Analysis#typer_analysis.macros ++ [Val],
- {Args, Analysis#typer_analysis{macros = NewVal}};
-analyze_result({inc, Val}, Args, Analysis) ->
- NewVal = Analysis#typer_analysis.includes ++ [Val],
- {Args, Analysis#typer_analysis{includes = NewVal}};
-analyze_result({plt, Plt}, Args, Analysis) ->
- {Args, Analysis#typer_analysis{plt = Plt}}.
-
-%%--------------------------------------------------------------------
-
--spec mode_error() -> no_return().
-mode_error() ->
- typer:error("can not do \"show\", \"show-exported\", \"annotate\", and \"annotate-inc-files\" at the same time").
-
--spec version_message() -> no_return().
-version_message() ->
- io:format("TypEr version "++?VSN++"\n"),
- erlang:halt(0).
-
--spec help_message() -> no_return().
-help_message() ->
- S = " Usage: typer [--help] [--version] [--comments] [--plt PLT]
- [--show | --show-exported | --annotate | --annotate-inc-files]
- [-Ddefine]* [-I include_dir]* [-T application]* [-r] file*
-
- Options:
- -r dir*
- search directories recursively for .erl files below them
- --show
- Prints type specifications for all functions on stdout.
- (this is the default behaviour; this option is not really needed)
- --show-exported (or --show_exported)
- Same as --show, but prints specifications for exported functions only
- Specs are displayed sorted alphabetically on the function's name
- --annotate
- Annotates the specified files with type specifications
- --annotate-inc-files
- Same as --annotate but annotates all -include() files as well as
- all .erl files (use this option with caution - has not been tested much)
- --comments
- Prints type information using Edoc comments, not type specs
- --plt PLT
- Use the specified dialyzer PLT file rather than the default one
- -T file*
- The specified file(s) already contain type specifications and these
- are to be trusted in order to print specs for the rest of the files
- (Multiple files or dirs, separated by spaces, can be specified.)
- -Dname (or -Dname=value)
- pass the defined name(s) to TypEr
- (The syntax of defines is the same as that used by \"erlc\".)
- -I include_dir
- pass the include_dir to TypEr
- (The syntax of includes is the same as that used by \"erlc\".)
- --version (or -v)
- prints the Typer version and exits
- --help (or -h)
- prints this message and exits
-
- Note:
- * denotes that multiple occurrences of these options are possible.
-",
- io:put_chars(S),
- erlang:halt(0).
diff --git a/lib/typer/src/typer_preprocess.erl b/lib/typer/src/typer_preprocess.erl
deleted file mode 100644
index 7cb0b9932b..0000000000
--- a/lib/typer/src/typer_preprocess.erl
+++ /dev/null
@@ -1,154 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(typer_preprocess).
-
--export([get_all_files/2]).
-
--include("typer.hrl").
-
-%%----------------------------------------------------------------------------
-
--spec get_all_files(#args{}, 'analysis' | 'trust') -> [string()].
-
-get_all_files(Args, analysis) ->
- case internal_get_all_files(Args#args.analyze,
- Args#args.analyzed_dir_r,
- fun test_erl_file_exclude_ann/1) of
- [] -> typer:error("no file(s) to analyze");
- AllFiles -> AllFiles
- end;
-get_all_files(Args, trust) ->
- internal_get_all_files(Args#args.trust, [], fun test_erl_file/1).
-
--spec test_erl_file_exclude_ann(string()) -> boolean().
-
-test_erl_file_exclude_ann(File) ->
- case filename:extension(File) of
- ".erl" -> %% Exclude files ending with ".ann.erl"
- case re:run(File, "[\.]ann[\.]erl$") of
- {match, _} -> false;
- nomatch -> true
- end;
- _ -> false
- end.
-
--spec test_erl_file(string()) -> boolean().
-
-test_erl_file(File) ->
- filename:extension(File) =:= ".erl".
-
--spec internal_get_all_files([string()], [string()],
- fun((string()) -> boolean())) -> [string()].
-
-internal_get_all_files(File_Dir, Dir_R, Fun) ->
- All_File_1 = process_file_and_dir(File_Dir, Fun),
- All_File_2 = process_dir_recursively(Dir_R, Fun),
- remove_dup(All_File_1 ++ All_File_2).
-
--spec process_file_and_dir([string()],
- fun((string()) -> boolean())) -> [string()].
-
-process_file_and_dir(File_Dir, TestFun) ->
- Fun =
- fun (Elem, Acc) ->
- case filelib:is_regular(Elem) of
- true -> process_file(Elem, TestFun, Acc);
- false -> check_dir(Elem, non_recursive, Acc, TestFun)
- end
- end,
- lists:foldl(Fun, [], File_Dir).
-
--spec process_dir_recursively([string()],
- fun((string()) -> boolean())) -> [string()].
-
-process_dir_recursively(Dirs, TestFun) ->
- Fun = fun (Dir, Acc) ->
- check_dir(Dir, recursive, Acc, TestFun)
- end,
- lists:foldl(Fun, [], Dirs).
-
--spec check_dir(string(),
- 'non_recursive' | 'recursive',
- [string()],
- fun((string()) -> boolean())) -> [string()].
-
-check_dir(Dir, Mode, Acc, Fun) ->
- case file:list_dir(Dir) of
- {ok, Files} ->
- {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir),
- case Mode of
- non_recursive ->
- FinalFiles = process_file_and_dir(TmpFiles, Fun),
- Acc ++ FinalFiles;
- recursive ->
- TmpAcc1 = process_file_and_dir(TmpFiles, Fun),
- TmpAcc2 = process_dir_recursively(TmpDirs, Fun),
- Acc ++ TmpAcc1 ++ TmpAcc2
- end;
- {error, eacces} ->
- typer:error("no access permission to dir \""++Dir++"\"");
- {error, enoent} ->
- typer:error("cannot access "++Dir++": No such file or directory");
- {error, _Reason} ->
- typer:error("error involving a use of file:list_dir/1")
- end.
-
-%% Same order as the input list
--spec process_file(string(), fun((string()) -> boolean()), string()) -> [string()].
-
-process_file(File, TestFun, Acc) ->
- case TestFun(File) of
- true -> Acc ++ [File];
- false -> Acc
- end.
-
-%% Same order as the input list
--spec split_dirs_and_files([string()], string()) -> {[string()], [string()]}.
-
-split_dirs_and_files(Elems, Dir) ->
- Test_Fun =
- fun (Elem, {DirAcc, FileAcc}) ->
- File = filename:join(Dir, Elem),
- case filelib:is_regular(File) of
- false -> {[File|DirAcc], FileAcc};
- true -> {DirAcc, [File|FileAcc]}
- end
- end,
- {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems),
- {lists:reverse(Dirs), lists:reverse(Files)}.
-
-%%-----------------------------------------------------------------------
-%% Utilities
-%%-----------------------------------------------------------------------
-
-%% Removes duplicate filenames but it keeps the order of the input list
-
--spec remove_dup([string()]) -> [string()].
-
-remove_dup(Files) ->
- Test_Dup = fun (File, Acc) ->
- case lists:member(File, Acc) of
- true -> Acc;
- false -> [File|Acc]
- end
- end,
- Reversed_Elems = lists:foldl(Test_Dup, [], Files),
- lists:reverse(Reversed_Elems).
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
index 285fa62da3..51561939ac 100644
--- a/lib/typer/vsn.mk
+++ b/lib/typer/vsn.mk
@@ -1 +1 @@
-TYPER_VSN = 0.1.7.4
+TYPER_VSN = 0.9
diff --git a/lib/webtool/doc/src/notes.xml b/lib/webtool/doc/src/notes.xml
index dc325b5b61..5179f37db2 100644
--- a/lib/webtool/doc/src/notes.xml
+++ b/lib/webtool/doc/src/notes.xml
@@ -31,6 +31,23 @@
<p>This document describes the changes made to the Webtool
application.</p>
+<section><title>WebTool 0.8.7</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Up until now Netscape has been the default web browser on
+ Unix/Linux. Webtool has now been updated to start Firefox
+ as default browser instead.</p>
+ <p>
+ Own Id: OTP-8651 Aux Id: OTP-8650 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>WebTool 0.8.6</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/webtool/doc/src/notes_history.xml b/lib/webtool/doc/src/notes_history.xml
index edab54d61f..a72a85412d 100644
--- a/lib/webtool/doc/src/notes_history.xml
+++ b/lib/webtool/doc/src/notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/webtool/doc/src/part_notes_history.xml b/lib/webtool/doc/src/part_notes_history.xml
index c1f6f846f5..76db9b7d9a 100644
--- a/lib/webtool/doc/src/part_notes_history.xml
+++ b/lib/webtool/doc/src/part_notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/webtool/doc/src/start_webtool.xml b/lib/webtool/doc/src/start_webtool.xml
index 184285c631..b525b38845 100644
--- a/lib/webtool/doc/src/start_webtool.xml
+++ b/lib/webtool/doc/src/start_webtool.xml
@@ -4,7 +4,7 @@
<comref>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
-
+
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
-
+
</legalnotice>
<title>start_webtool</title>
@@ -56,7 +56,7 @@ Or http://127.0.0.1:8888/
Usage: start_webtool application [ browser ]
Available applications are: [orber,appmon,crashdump_viewer,webcover]
-Default browser is 'iexplore' (Internet Explorer) on Windows or else 'netscape' </pre>
+Default browser is 'iexplore' (Internet Explorer) on Windows or else 'firefox' </pre>
<p>To start any of the listed applications, give the
application name as the first argument, e.g.</p>
<pre>
@@ -68,7 +68,7 @@ Starting webcover...
Sending URL to netscape...done </pre>
<p>The WebTool application WebCover is then started and the
default browser is used. The default browser is Internet
- Explorer on Windows or else Netscape.
+ Explorer on Windows or else Firefox.
</p>
<p>To use another browser, give the browser's start command
as the second argument, e.g.</p>
diff --git a/lib/webtool/doc/src/webtool.xml b/lib/webtool/doc/src/webtool.xml
index 55bac8bd34..bbb25d29bd 100644
--- a/lib/webtool/doc/src/webtool.xml
+++ b/lib/webtool/doc/src/webtool.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2001</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/webtool/src/webtool.erl b/lib/webtool/src/webtool.erl
index 51d821751c..1be903b827 100644
--- a/lib/webtool/src/webtool.erl
+++ b/lib/webtool/src/webtool.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2001-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(webtool).
@@ -139,7 +139,7 @@ script_start([App]) ->
DefaultBrowser =
case os:type() of
{win32,_} -> iexplore;
- _ -> netscape
+ _ -> firefox
end,
script_start([App,DefaultBrowser]);
script_start([App,Browser]) ->
@@ -159,6 +159,8 @@ script_start([App,Browser]) ->
"http://localhost:" ++ PortStr ++ "/" ++ StartPage
end,
case Browser of
+ none ->
+ ok;
iexplore when OSType == win32->
io:format("Starting internet explorer...\n"),
{ok,R} = win32reg:open(""),
@@ -170,7 +172,7 @@ script_start([App,Browser]) ->
_ when OSType == win32 ->
io:format("Starting ~w...\n",[Browser]),
os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url);
- B when B==netscape; B==mozilla ->
+ B when B==firefox; B==mozilla ->
io:format("Sending URL to ~w...",[Browser]),
BStr = atom_to_list(Browser),
SendCmd = BStr ++ " -raise -remote \'openUrl(" ++
@@ -209,7 +211,7 @@ usage() ->
"\nUsage: start_webtool application [ browser ]\n"
"\nAvailable applications are: ~p\n"
"Default browser is \'iexplore\' (Internet Explorer) on Windows "
- "or else \'netscape\'\n",
+ "or else \'firefox\'\n",
[Apps]),
stop().
diff --git a/lib/webtool/vsn.mk b/lib/webtool/vsn.mk
index 712e3abbaf..6b76883330 100644
--- a/lib/webtool/vsn.mk
+++ b/lib/webtool/vsn.mk
@@ -1 +1 @@
-WEBTOOL_VSN=0.8.6
+WEBTOOL_VSN=0.8.7
diff --git a/lib/wx/.gitignore b/lib/wx/.gitignore
new file mode 100644
index 0000000000..fd76f078d7
--- /dev/null
+++ b/lib/wx/.gitignore
@@ -0,0 +1,2 @@
+test_log_*
+wx_test_case_info
diff --git a/lib/wx/Makefile b/lib/wx/Makefile
index 83f545b662..0bc89e08ad 100644
--- a/lib/wx/Makefile
+++ b/lib/wx/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2010. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -23,7 +23,7 @@ SUBDIRS = src
ifeq ($(CAN_BUILD_DRIVER), true)
SUBDIRS += c_src
endif
-SUBDIRS += examples demos doc/src
+SUBDIRS += examples doc/src
CLEANDIRS = $(SUBDIRS) api_gen
ifeq ($(INSIDE_ERLSRC),true)
diff --git a/lib/wx/api_gen/Makefile b/lib/wx/api_gen/Makefile
index c6b65b60bc..756ec598ce 100644
--- a/lib/wx/api_gen/Makefile
+++ b/lib/wx/api_gen/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2009. All Rights Reserved.
+# Copyright Ericsson AB 2008-2010. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -39,14 +39,14 @@ TARGET_CDIR = ../c_src/gen
C_TARGETS = wxe_funcs.cpp
GL_C_TARGETS = gl_funcs.cpp
-WX = $(TARGET_CDIR)/$(C_TARGETS)
+WX = wx_code_generated
-GL = $(TARGET_CDIR)/$(GL_C_TARGETS)
+GL = gl_code_generated
opt: $(WX) $(GL)
$(WX): wxxml_generated $(COMPILER_T) wxapi.conf $(wildcard wx_extra/wx*.c_src) $(wildcard wx_extra/wx*.erl)
- erl -noshell -run wx_gen code
+ erl -noshell -run wx_gen code && touch wx_code_generated
wxxml_generated: wx_doxygen.conf wx_extra/bugs.h wx_extra/wxe_evth.h
sed -e 's|@WXGTK_DIR@|$(WXGTK_DIR)|g' wx_doxygen.conf > wx_doxygen
@@ -56,9 +56,8 @@ glxml_generated: gl_doxygen.conf
sed -e 's|@GL_DIR@|$(GL_DIR)|g' gl_doxygen.conf > gl_doxygen
doxygen gl_doxygen && touch glxml_generated
-
$(GL): glxml_generated $(GL_COMP_T) glapi.conf
- erl -noshell -run gl_gen code
+ erl -noshell -run gl_gen code && touch gl_code_generated
%.beam: %.erl wx_gen.hrl gl_gen.hrl
$(ERLC) -W $(ERL_FLAGS) $(ERL_COMPILE_FLAGS) $< -o$(EBIN)
@@ -66,7 +65,7 @@ $(GL): glxml_generated $(GL_COMP_T) glapi.conf
# TODO split cleans into separate targets?
complete_clean:
rm -f gl_doxygen wx_doxygen wx_xml/*.x* gl_xml/*.x*
- rm -f glxml_generated wxxml_generated
+ rm -f *_generated
$(MAKE) clean
clean:
rm -f *~
diff --git a/lib/wx/api_gen/gen_util.erl b/lib/wx/api_gen/gen_util.erl
index 859317bdef..b53f817ce0 100644
--- a/lib/wx/api_gen/gen_util.erl
+++ b/lib/wx/api_gen/gen_util.erl
@@ -40,6 +40,10 @@ strip_name([H|R1],[H|R2]) ->
strip_name(R1,R2);
strip_name(String,[]) -> String.
+
+get_hook(_Type, undefined) -> ignore;
+get_hook(Type, List) -> proplists:get_value(Type, List, ignore).
+
open_write(File) ->
%% io:format("Generating ~s~n",[File]),
{ok, Fd} = file:open(File++".temp", [write]),
@@ -58,10 +62,10 @@ close() ->
[] ->
ok = file:delete(File ++ ".temp"),
%% So that make understands that we have made this
- case os:getenv("CLEARCASE_ROOT") of
- false -> os:cmd("touch " ++ File);
- _ -> ignore
- end,
+ %% case os:getenv("CLEARCASE_ROOT") of
+ %% false -> os:cmd("touch " ++ File);
+ %% _ -> ignore
+ %% end,
ok;
Diff ->
case check_diff(Diff) of
diff --git a/lib/wx/api_gen/gl_gen.erl b/lib/wx/api_gen/gl_gen.erl
index 42802c6de7..374e0bd12b 100644
--- a/lib/wx/api_gen/gl_gen.erl
+++ b/lib/wx/api_gen/gl_gen.erl
@@ -67,7 +67,7 @@ gen_code() ->
gl_gen_erl:gl_defines(GLDefines),
gl_gen_erl:gl_api(GLFuncs),
- gl_gen_erl:gen_debug(GLFuncs,GLUFuncs),
+ %%gl_gen_erl:gen_debug(GLFuncs,GLUFuncs),
gl_gen_c:gen(GLFuncs,GLUFuncs),
ok.
@@ -206,10 +206,10 @@ parse_define([], D, _Opts) ->
parse_func(Xml, Opts) ->
{Func,_} = foldl(fun(X,Acc) -> parse_func(X,Acc,Opts) end, {#func{},1}, Xml),
+ put(current_func, Func#func.name),
#func{params=Args0,type=Type0} = Func,
Args = filter(fun(#arg{type=void}) -> false; (_) -> true end, Args0),
- #arg{type=Type} =
- patch_param(Func#func.name,#arg{name="result",type=Type0},Opts),
+ #arg{type=Type} = patch_param(Func#func.name,#arg{name="result",type=Type0},Opts),
Func#func{params=reverse(Args), type=Type}.
parse_func(#xmlElement{name=type, content=C}, {F,AC}, Os) ->
@@ -220,6 +220,7 @@ parse_func(#xmlElement{name=name, content=[#xmlText{value=C}]},{F,AC},Os) ->
put(current_func, Func),
{F#func{name=name(Func,Os)},AC};
parse_func(#xmlElement{name=param, content=C},{F,AC},Os) ->
+ put(current_func, F#func.name),
Parse = fun(Con, Ac) -> parse_param(Con, Ac, Os) end,
Param0 = foldl(Parse, #arg{}, drop_empty(C)),
Param = fix_param_name(Param0, F, AC),
@@ -314,11 +315,17 @@ handle_arg_opt(both, P) -> P#arg{in=both};
handle_arg_opt(binary, P=#arg{type=T}) ->
P#arg{type=T#type{size=undefined,base=binary}};
handle_arg_opt({binary,Sz}, P=#arg{type=T}) ->
- P#arg{type=T#type{size=Sz,base=binary}};
+ P#arg{type=T#type{size={Sz, Sz},base=binary}};
+handle_arg_opt({binary,Max, Sz}, P=#arg{type=T}) ->
+ P#arg{type=T#type{size={Max, Sz},base=binary}};
handle_arg_opt({type,Type}, P=#arg{type=T}) -> P#arg{type=T#type{name=Type}};
handle_arg_opt({single,Opt},P=#arg{type=T}) -> P#arg{type=T#type{single=Opt}};
+handle_arg_opt({base,{Opt, Sz}}, P=#arg{type=T}) -> P#arg{type=T#type{base=Opt, size=Sz}};
handle_arg_opt({base,Opt}, P=#arg{type=T}) -> P#arg{type=T#type{base=Opt}};
-handle_arg_opt({c_only,Opt},P) -> P#arg{where=c, alt=Opt}.
+handle_arg_opt({c_only,Opt},P) -> P#arg{where=c, alt=Opt};
+handle_arg_opt(string, P=#arg{type=T}) -> P#arg{type=T#type{base=string}};
+handle_arg_opt({string,Max,Sz}, P=#arg{type=T}) ->
+ P#arg{type=T#type{base=string, size={Max,Sz}}}.
parse_type([], _Os) -> void;
parse_type(C, Os) ->
@@ -367,6 +374,8 @@ parse_type2([N="GLbitfield"|R],T,Opts) ->
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
parse_type2([N="GLvoid"|R],T,Opts) ->
parse_type2(R,T#type{name=N, base=idx_binary},Opts);
+parse_type2([N="GLsync"|R],T,Opts) ->
+ parse_type2(R,T#type{name=N, base=int, size=8},Opts);
parse_type2([N="GLbyte"|R],T,Opts) ->
parse_type2(R,T#type{name=N, size=1, base=int},Opts);
@@ -378,6 +387,11 @@ parse_type2([N="GLushort"|R],T,Opts) ->
parse_type2(R,T#type{name=N, size=2, base=int},Opts);
parse_type2([N="GLint"|R],T,Opts) ->
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
+parse_type2([N="GLint64"|R],T,Opts) ->
+ parse_type2(R,T#type{name=N, size=8, base=int},Opts);
+parse_type2([N="GLuint64"|R],T,Opts) ->
+ parse_type2(R,T#type{name=N, size=8, base=int},Opts);
+
parse_type2([N="GLuint"|R],T,Opts) ->
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
parse_type2([N="GLsizei"|R],T,Opts) ->
@@ -548,8 +562,10 @@ setup_idx_binary(Name,Ext,_Opts) ->
%% Ok warn if single is undefined
lists:foreach(fun(#arg{type=#type{base=memory}}) -> ok;
+ (#arg{type=#type{base=string}}) -> ok;
(#arg{type=#type{base=idx_binary}}) -> ok;
(#arg{type=#type{name="GLUquadric"}}) -> ok;
+ (#arg{type=#type{base=binary, size=Sz}}) when Sz =/= undefined -> ok;
(A=#arg{type=#type{single=undefined}}) ->
?warning("~p Unknown size of~n ~p~n",
[get(current_func),A]),
diff --git a/lib/wx/api_gen/gl_gen_c.erl b/lib/wx/api_gen/gl_gen_c.erl
index 3293050ab9..0f5cb0e1f4 100644
--- a/lib/wx/api_gen/gl_gen_c.erl
+++ b/lib/wx/api_gen/gl_gen_c.erl
@@ -47,34 +47,29 @@ gen(GLFuncs, GLUFuncs) ->
w("/***** This file is generated do not edit ****/~n~n", []),
w("#include <stdio.h>~n", []),
w("#include <string.h>~n", []),
- w("#include \"../wxe_impl.h\"~n", []),
- w("#include \"../wxe_gl.h\"~n", []),
- w("#include \"gl_fdefs.h\"~n", []),
+ w("#include \"../egl_impl.h\"~n", []),
+ w("#include \"gl_fdefs.h\"~n~n", []),
+ w("extern gl_fns_t gl_fns[];~n~n", []),
- w("~nint gl_error_op;~n", []),
- w("void gl_dispatch(int op, char *bp,ErlDrvTermData caller,WXEBinRef *bins[]){~n",
+ w("void egl_dispatch(int op, char *bp, ErlDrvPort port, "
+ "ErlDrvTermData caller, char *bins[], int bins_sz[]){~n",
[]),
- w(" gl_error_op = op;~n", []),
- w(" if(caller != gl_active) {~n", []),
- w(" wxGLCanvas * current = glc[caller];~n", []),
- w(" if(current) { gl_active = caller; current->SetCurrent();}~n", []),
- w(" else {~n "
- " ErlDrvTermData rt[] = // Error msg~n"
- " {ERL_DRV_ATOM, driver_mk_atom((char *) \"_wxe_error_\"),~n"
- " ERL_DRV_INT, op,~n"
- " ERL_DRV_ATOM, driver_mk_atom((char *) \"no_gl_context\"),~n"
- " ERL_DRV_TUPLE,3};~n"
- " driver_send_term(WXE_DRV_PORT,caller,rt,8);~n"
- " return ;~n }~n };~n~n", []),
-
+ w(" try {~n",[]),
w(" switch(op)~n{~n",[]),
- w(" case 5000:~n wxe_tess_impl(bp, caller);~n break;~n", []),
- w(" case WXE_BIN_INCR:~n driver_binary_inc_refc(bins[0]->bin);~n break;~n",[]),
- w(" case WXE_BIN_DECR:~n driver_binary_dec_refc(bins[0]->bin);~n break;~n",[]),
+ w(" case 5000:~n erl_tess_impl(bp, port, caller);~n break;~n", []),
[funcs(F) || F <- GLUFuncs],
[funcs(F) || F <- GLFuncs],
+ w("}} catch (char *err_msg) {\n"
+ "int AP = 0; ErlDrvTermData rt[12];\n"
+ "rt[AP++] = ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) \"_egl_error_\");\n"
+ "rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) op;\n"
+ "rt[AP++] = ERL_DRV_ATOM; rt[AP++] = driver_mk_atom((char *) err_msg);\n"
+ "// rt[AP++] = ERL_DRV_ATOM; rt[AP++] = driver_mk_atom((char *) gl_fns[op-GLE_GL_FUNC_START].name);\n"
+ "// rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;\n"
+ "rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;\n"
+ "driver_send_term(port,caller,rt,AP);\n", []),
w("}} /* The End */~n~n",[]),
close().
@@ -123,25 +118,53 @@ declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=B,single={tuple,Sz}}})
true = is_number(Sz), %% Assert
w(" ~s ~s[~p] = {~s};~n", [T,N,Sz,args(fun zero/1,",",lists:duplicate(Sz,B))]),
A;
-declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=B,single={list,Sz}}}) when is_number(Sz) ->
+declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=B,single={list,Sz}}})
+ when is_number(Sz) ->
w(" ~s ~s[~p] = {~s};~n", [T,N,Sz,args(fun zero/1,",",lists:duplicate(Sz,B))]),
A;
+declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=string,size={Max,_}, single=Single}}) ->
+ case is_integer(Max) of
+ true ->
+ w(" ~s ~s[~p];~n", [T,N,Max]);
+ false ->
+ %% w(" ~s ~s[*~s];~n", [T,N,Max]),
+ w(" ~s *~s;~n", [T,N]),
+ w(" ~s = (~s *) driver_alloc(sizeof(~s) * *~s);~n", [N,T,T,Max]),
+ store_free(N)
+ %% case Single of
+ %% {list, _, _} ->
+ %% w(" ~s *~s_p = ~s;~n", [T,N,N]);
+ %% _ -> ok
+ %% end
+ end,
+ A;
+declare_var(A=#arg{name=N,in=false,type=#type{base=binary,size={MaxSz, _}}}) ->
+ MaxSz == undefined andalso error({assert, A}),
+ case is_integer(MaxSz) of
+ true ->
+ w(" ErlDrvBinary *~s = driver_alloc_binary(~p);~n", [N,MaxSz]);
+ false ->
+ w(" ErlDrvBinary *~s = driver_alloc_binary(*~s);~n", [N,MaxSz])
+ end,
+ A;
declare_var(A=#arg{name=N,in=false,type=#type{name=T,single={list,ASz,_USz},mod=[]}}) ->
true = is_list(ASz), %% Assert
w(" ~s *~s;~n", [T,N]),
w(" ~s = (~s *) driver_alloc(sizeof(~s) * *~s);~n", [N,T,T,ASz]),
store_free(N),
- A;
-declare_var(A=#arg{name=N,in=false,type=#type{name=T,base=binary,size=Sz}}) ->
- true = is_number(Sz), %% Assert
- w(" ~s ~s[~p];~n", [T,N,Sz]),
+ %% w(" ~s ~s[*~s];~n", [T,N,ASz]),
A;
declare_var(A=#arg{in=false, type=#type{name="GLUquadric",by_val=false,single=true}}) ->
A;
+declare_var(A=#arg{in=false, type=#type{base=string,by_val=false,single=true}}) ->
+ A;
declare_var(A=#arg{name=N,in=false,
type=#type{name=T,base=B,by_val=false,single=true}}) ->
w(" ~s ~s[1] = {~s};~n", [T,N,zero(B)]),
A;
+declare_var(A=#arg{where=c, type=#type{name=T}, alt={size,Var}}) ->
+ w(" ~s ~s_size = bins_sz[~p];~n", [T, Var, get(bin_count)]),
+ A;
declare_var(A=#arg{where=_}) ->
A.
@@ -171,10 +194,10 @@ decode_arg(P=#arg{where=c},A) -> {P,A};
decode_arg(P=#arg{in=false},A) -> {P,A};
decode_arg(P=#arg{name=Name,type=#type{name=Type,base=binary}},A0) ->
- w(" ~s *~s = (~s *) bins[~p]->base;~n", [Type,Name,Type,next_id(bin_count)]),
+ w(" ~s *~s = (~s *) bins[~p];~n", [Type,Name,Type,next_id(bin_count)]),
{P, A0};
decode_arg(P=#arg{name=Name,type=#type{name=Type,base=memory}},A0) ->
- w(" ~s *~s = (~s *) bins[~p]->base;~n", [Type,Name,Type,next_id(bin_count)]),
+ w(" ~s *~s = (~s *) bins[~p];~n", [Type,Name,Type,next_id(bin_count)]),
{P, A0};
decode_arg(P=#arg{name=Name,type=#type{name=T,base=string,single=list}},A0) ->
A = align(4,A0),
@@ -219,7 +242,7 @@ decode_arg(P=#arg{name=Name,type=#type{name=Type,base=guard_int}},A0) ->
{P, A};
decode_arg(P=#arg{name=Name,type=#type{name=Type,base=string,single=true}},A0) ->
w(" ~s *~s = (~s *) bp;~n", [Type,Name,Type]),
- w(" int ~sLen = strlen((char *)~s); bp += ~sLen+1+((8-((1+~sLen+~p)%8))%8);~n",
+ w(" int ~sLen[1] = {strlen((char *)~s)}; bp += ~sLen[0]+1+((8-((1+~sLen[0]+~p)%8))%8);~n",
[Name,Name,Name,Name,A0]),
{P, 0};
decode_arg(P=#arg{name=Name,
@@ -288,6 +311,8 @@ result_type(#type{name=T, ref=undefined}) -> T;
result_type(#type{name=T, ref={pointer,1}, mod=Mods}) ->
mod(Mods) ++ T ++ " * ".
+call_arg(#arg{alt={size,Alt},type=#type{}}) ->
+ Alt ++ "_size";
call_arg(#arg{alt={length,Alt},type=#type{}}) ->
"*" ++ Alt ++ "Len";
call_arg(#arg{alt={constant,Alt},type=#type{}}) ->
@@ -298,6 +323,8 @@ call_arg(#arg{name=Name,type=#type{single={list, _}}}) ->
Name;
call_arg(#arg{name=Name,type=#type{size=8,base=int,ref=undefined}}) ->
Name;
+call_arg(#arg{name=Name,in=false,type=#type{name=T, base=binary}}) ->
+ "(" ++ T ++ "*) " ++ Name ++ "->orig_bytes";
call_arg(#arg{name=Name,type=#type{ref=undefined}}) ->
"*" ++ Name;
call_arg(#arg{name=Name,type=#type{base=guard_int}}) ->
@@ -318,27 +345,27 @@ build_return_vals(Type,As) ->
true ->
w(" int AP = 0; ErlDrvTermData rt[6];~n",[]),
w(" rt[AP++]=ERL_DRV_ATOM;"
- " rt[AP++]=driver_mk_atom((char *) \"_wxe_result_\");~n",[]),
+ " rt[AP++]=driver_mk_atom((char *) \"_egl_result_\");~n",[]),
w(" rt[AP++]=ERL_DRV_ATOM;"
" rt[AP++]=driver_mk_atom((char *) \"ok\");~n",[]),
w(" rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;~n",[]),
- w(" driver_send_term(WXE_DRV_PORT,caller,rt,AP);~n",[]),
+ w(" driver_send_term(port,caller,rt,AP);~n",[]),
ok
end;
{Val,Vars,Cnt} ->
ExtraTuple = if Cnt > 1 -> 2; true -> 0 end,
- CSize = if Vars =:= none ->
- Sz = integer_to_list(Val+4+ExtraTuple),
- w(" int AP = 0; ErlDrvTermData rt[~s];~n",[Sz]),
- Sz;
- true ->
- Sz = integer_to_list(Val+4+ExtraTuple) ++ " + " ++ Vars,
- w(" int AP = 0; ErlDrvTermData *rt;~n",[]),
- w(" rt = (ErlDrvTermData *) "
- "driver_alloc(sizeof(ErlDrvTermData)*(~s));~n", [Sz]),
- Sz
- end,
- w(" rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) \"_wxe_result_\");~n",[]),
+ if Vars =:= none ->
+ Sz = integer_to_list(Val+4+ExtraTuple),
+ w(" int AP = 0; ErlDrvTermData rt[~s];~n",[Sz]),
+ Sz;
+ true ->
+ Sz = integer_to_list(Val+4+ExtraTuple) ++ " + " ++ Vars,
+ w(" int AP = 0; ErlDrvTermData *rt;~n",[]),
+ w(" rt = (ErlDrvTermData *) "
+ "driver_alloc(sizeof(ErlDrvTermData)*(~s));~n", [Sz]),
+ Sz
+ end,
+ w(" rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) \"_egl_result_\");~n",[]),
FreeList = build_ret_types(Type,As),
case Cnt of
1 -> ok;
@@ -346,9 +373,9 @@ build_return_vals(Type,As) ->
w(" rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = ~p;~n",[Cnt])
end,
w(" rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;~n",[]),
- w(" if (AP != ~s ) fprintf(stderr, \"%d: ERROR AP mismatch %d %d\\r\\n\",__LINE__,AP,~s);~n",
- [CSize,CSize]),
- w(" driver_send_term(WXE_DRV_PORT,caller,rt,AP);~n",[]),
+ %%w(" if (AP != ~s ) fprintf(stderr, \"%d: ERROR AP mismatch %d %d\\r\\n\",__LINE__,AP,~s);~n",
+ %% [CSize,CSize]),
+ w(" driver_send_term(port,caller,rt,AP);~n",[]),
case Vars of
none -> ignore;
_ ->
@@ -371,7 +398,7 @@ calc_sizes(Type,As) ->
{Val, none} -> {Sz+Val, Vars, Cnt+1};
{Val, Var} when Vars =:= none ->
{Sz+Val, Var,Cnt+1};
- {Val, Var} when Vars =:= none ->
+ {Val, Var} ->
{Sz+Val, Var ++ " + " ++ Vars,Cnt+1}
end;
(_,Acc) -> Acc
@@ -379,13 +406,16 @@ calc_sizes(Type,As) ->
foldl(Calc, TSz, As).
return_size(_N,void) -> {0, none};
-return_size(_N,#type{base=binary}) -> {4, none};
-return_size(_N,#type{single=true}) -> {2,none};
return_size(_N,#type{single={tuple,Sz}}) -> {Sz*2+2, none};
-return_size(_N,#type{name="GLubyte",single={list,null}}) ->{3, none};
return_size(_N,#type{single={list,Sz}}) -> {Sz*2+3, none};
-return_size(_N,#type{base=string,single={list,_,_}}) -> {3, none};
-return_size(_N,#type{single={list,_,Sz}}) -> {3, "(*" ++Sz++")*2"}.
+return_size(_N,#type{base=string,single=true}) -> {3, none};
+return_size(_N,#type{base=string,single=undefined}) -> {3, none};
+return_size(_N,#type{base=string,single={list,_,"result"}}) -> {3, "result*3"};
+return_size(_N,#type{base=string,single={list,_,Sz}}) -> {3, "(*" ++Sz++")*3"};
+return_size(_N,#type{single={list,_,"result"}}) -> {3, "result*2"};
+return_size(_N,#type{single={list,_,Sz}}) -> {3, "(*" ++Sz++")*2"};
+return_size(_N,#type{base=binary}) -> {4, none};
+return_size(_N,#type{single=true}) -> {2, none}.
build_ret_types(void,Ps) ->
@@ -444,17 +474,27 @@ build_ret(Name,_Q,#type{name=T,base=_,single={tuple,Sz}}) ->
[w(" rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *~s++;~n", [Temp])
|| _ <- lists:seq(1,Sz)],
w(" rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = ~p;~n",[Sz]);
-build_ret(Name,_Q,#type{name="GLubyte",single={list,null}}) ->
+build_ret(Name,_Q,#type{base=string,size=1,single=true}) ->
w(" rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) ~s;"
" rt[AP++] = strlen((char *) ~s);\n", [Name, Name]);
-build_ret(Name,_Q,#type{base=string,single={list,_,Sz}}) ->
+build_ret(Name,_Q,#type{base=string, size={_Max,Sz}, single=S})
+ when S == true; S == undefined ->
w(" rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) ~s;"
" rt[AP++] = *~s;\n", [Name, Sz]);
+build_ret(Name,_Q,#type{name=_T,base=string,size={_, SSz}, single={list,_,Sz}}) ->
+ P = if Sz == "result" -> ["(int) "]; true -> "*" end,
+ w(" for(int i=0; i < ~s~s; i++) {\n", [P,Sz]),
+ w(" rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) ~s;"
+ " rt[AP++] = ~s[i]-1;\n", [Name, SSz]),
+ w(" ~s += ~s[i]; }~n", [Name, SSz]),
+ w(" rt[AP++] = ERL_DRV_NIL;", []),
+ w(" rt[AP++] = ERL_DRV_LIST; rt[AP++] = (~s~s)+1;~n",[P,Sz]);
build_ret(Name,_Q,#type{name=_T,base=B,single={list,_,Sz}}) when B =/= float ->
- w(" for(int i=0; i < *~s; i++) {\n", [Sz]),
+ P = if Sz == "result" -> ["(int) "]; true -> "*" end,
+ w(" for(int i=0; i < ~s~s; i++) {\n", [P,Sz]),
w(" rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) ~s[i];}~n", [Name]),
w(" rt[AP++] = ERL_DRV_NIL;", []),
- w(" rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*~s)+1;~n",[Sz]);
+ w(" rt[AP++] = ERL_DRV_LIST; rt[AP++] = (~s~s)+1;~n",[P,Sz]);
build_ret(Name,_Q,#type{name=_T,size=FSz,base=float,single={list,Sz}}) ->
Temp = Name ++ "Tmp",
case FSz of
@@ -475,12 +515,14 @@ build_ret(Name,_Q,#type{name=T,base=_,single={list,Sz}}) ->
|| _ <- lists:seq(1,Sz)],
w(" rt[AP++] = ERL_DRV_NIL;", []),
w(" rt[AP++] = ERL_DRV_LIST; rt[AP++] = ~p+1;~n",[Sz]);
-build_ret(Name,_Q,#type{name="GLubyte",base=binary,size=Sz}) ->
- w(" ErlDrvBinary * BinCopy = driver_alloc_binary(~p);~n", [Sz]),
- w(" memcpy(BinCopy->orig_bytes, ~s, ~p);~n", [Name,Sz]),
- w(" rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) BinCopy;", []),
- w(" rt[AP++] = ~p; rt[AP++] = 0;~n", [Sz]),
- "driver_free_binary(BinCopy);";
+build_ret(Name,_Q,#type{base=binary,size={_,Sz}}) ->
+ w(" rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) ~s;", [Name]),
+ if is_integer(Sz) ->
+ w(" rt[AP++] = ~p; rt[AP++] = 0;~n", [Sz]);
+ is_list(Sz) ->
+ w(" rt[AP++] = *~s; rt[AP++] = 0;~n", [Sz])
+ end,
+ "driver_free_binary(" ++ Name ++ ");";
build_ret(Name,_Q,T=#type{}) ->
io:format("{~p, {~p, {single,{tuple,X}}}}.~n", [get(current_func),Name]),
io:format(" ~p~n",[T]).
@@ -496,6 +538,19 @@ gen_defines(GLFuncs,GLUFuncs) ->
w("# define WXE_EXTERN~n", []),
w("#else~n# define WXE_EXTERN extern~n", []),
w("#endif~n~n", []),
+
+ w("typedef struct {\n"
+ " const char * name;\n"
+ " const char * alt;\n"
+ " void * func;\n"
+ "} gl_fns_t;\n\n", []),
+
+ GLFirst = case hd(GLFuncs) of
+ [First|_] when is_list(First) -> get(First);
+ First -> get(First)
+ end,
+ w("#define GLE_GL_FUNC_START ~p~n", [GLFirst#func.id]),
+
[fdefs(F) || F <- GLFuncs],
[fdefs(F) || F <- GLUFuncs],
close().
@@ -543,11 +598,7 @@ gl_gen_init(Funcs) ->
open_write("../c_src/gen/gl_finit.h"),
c_copyright(),
w("/***** This file is generated do not edit ****/~n~n", []),
- w("static struct {\n"
- " const char * name;\n"
- " const char * alt;\n"
- " void * func;\n"
- "} gl_fns[] = \n"
+ w("gl_fns_t gl_fns[] = \n"
"{\n", []),
[finits(F) || F <- Funcs],
w(" { NULL, NULL, NULL}};\n",[]),
diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl
index ce35484561..f292c8723e 100644
--- a/lib/wx/api_gen/gl_gen_erl.erl
+++ b/lib/wx/api_gen/gl_gen_erl.erl
@@ -49,7 +49,7 @@ glu_defines(Defs) ->
w("~n%% GLU DEFINITIONS~n~n", []),
w("%% This file is generated DO NOT EDIT~n~n", []),
[gen_define(Def) || Def=#def{} <- Defs],
- close(),
+ close(),
ok.
gen_define(#def{name=N, val=Val, type=int}) ->
@@ -78,7 +78,11 @@ types() ->
{"GLsizeiptr","64/native-unsigned"}, % 64 bits int, convert on c-side
{"GLintptr", "64/native-unsigned"}, % 64 bits int, convert on c-sidew
{"GLUquadric", "64/native-unsigned"},% Handle 32bits aargh 64bits on mac64
- {"GLhandleARB","64/native-unsigned"} % Handle 32bits aargh 64bits on mac64
+ {"GLhandleARB","64/native-unsigned"},% Handle 32bits aargh 64bits on mac64
+
+ {"GLsync", "64/native-unsigned"}, % Pointer to record
+ {"GLuint64", "64/native-unsigned"},
+ {"GLint64", "64/native-signed"}
].
gl_api(Fs) ->
@@ -90,22 +94,53 @@ gl_api(Fs) ->
w("%% See <a href=\"http://www.opengl.org/sdk/docs/man/\">www.opengl.org</a>~n",[]),
w("%%~n", []),
w("%% Booleans are represented by integers 0 and 1.~n~n", []),
- w("%% @type wx_mem(). see wx.erl on memory allocation functions~n", []),
+ w("%% @type mem(). memory block~n", []),
w("%% @type enum(). An integer defined in gl.hrl~n", []),
w("%% @type offset(). An integer which is an offset in an array~n", []),
w("%% @type clamp(). A float clamped between 0.0 - 1.0~n", []),
w("-module(gl).~n~n",[]),
w("-compile(inline).~n", []),
- %% w("-compile(export_all).~n~n", []),
- %% w("-compile(binary_comprehension).~n~n", []),
- w("-include(\"wxe.hrl\").~n", []),
+%% w("-include(\"wxe.hrl\").~n", []),
[w("-define(~s,~s).~n", [GL,Erl]) || {GL,Erl} <- types()],
-
+
+ gen_types(gl),
+
Exp = fun(F) -> gen_export(F) end,
ExportList = lists:map(Exp,Fs),
w("~n-export([~s]).~n~n", [args(fun(EF) -> EF end, ",", ExportList, 60)]),
-
+ w("-export([call/2, cast/2, send_bin/1]).~n",[]),
+ w("%% @hidden~n", []),
+ w("call(Op, Args) ->~n", []),
+ w(" Port = get(opengl_port), ~n", []),
+ w(" _ = erlang:port_control(Port,Op,Args),~n", []),
+ w(" rec().~n", []),
+ w(" ~n", []),
+ w("%% @hidden~n", []),
+ w("cast(Op, Args) ->~n", []),
+ w(" Port = get(opengl_port), ~n", []),
+ w(" _ = erlang:port_control(Port,Op,Args),~n", []),
+ w(" ok.~n", []),
+ w(" ~n", []),
+ w("%% @hidden~n", []),
+ w("rec() ->~n", []),
+ w(" receive ~n", []),
+ w(" {'_egl_result_', Res} -> Res;~n", []),
+ w(" {'_egl_error_', Op, Res} -> error({error,Res,Op})~n", []),
+ w(" end. ~n", []),
+ w("~n", []),
+ w("%% @hidden~n", []),
+ w("send_bin(Bin) when is_binary(Bin) ->~n", []),
+ w(" Port = get(opengl_port), ~n", []),
+ w(" erlang:port_command(Port,Bin);~n", []),
+ w("send_bin(Tuple) when is_tuple(Tuple) ->~n", []),
+ w(" Port = get(opengl_port), ~n", []),
+ w(" case element(2, Tuple) of~n", []),
+ w(" Bin when is_binary(Bin) ->~n", []),
+ w(" erlang:port_command(Port,Bin)~n", []),
+ w(" end.~n", []),
+ w("~n", []),
+
w("~n%% API~n~n", []),
[gen_funcs(F) || F <- Fs],
close(),
@@ -120,20 +155,22 @@ glu_api(Fs) ->
w("%% See <a href=\"http://www.opengl.org/sdk/docs/man/\">www.opengl.org</a>~n",[]),
w("%%~n", []),
w("%% Booleans are represented by integers 0 and 1.~n~n", []),
- w("%% @type wx_mem(). see wx.erl on memory allocation functions~n", []),
+ w("%% @type mem(). memory block~n", []),
w("%% @type enum(). An integer defined in gl.hrl~n", []),
w("%% @type offset(). An integer which is an offset in an array~n", []),
w("%% @type clamp(). A float clamped between 0.0 - 1.0~n~n", []),
w("-module(glu).~n",[]),
w("-compile(inline).~n", []),
- w("-include(\"wxe.hrl\").~n", []),
+ %%w("-include(\"wxe.hrl\").~n", []),
[w("-define(~s,~s).~n", [GL,Erl]) || {GL,Erl} <- types()],
+ gen_types(glu),
+
Exp = fun(F) -> gen_export(F) end,
ExportList = ["tesselate/2" | lists:map(Exp,Fs)],
w("~n-export([~s]).~n~n", [args(fun(EF) -> EF end, ",", ExportList, 60)]),
-
+ w("-import(gl, [call/2,cast/2,send_bin/1]).", []),
w("~n%% API~n~n", []),
w("%% @spec (Vec3, [Vec3]) -> {Triangles, VertexPos}~n",[]),
@@ -148,13 +185,13 @@ glu_api(Fs) ->
"%% may contain newly created vertices in the end.~n", []),
w("tesselate({Nx,Ny,Nz}, Vs) ->~n",[]),
- w(" wxe_util:call(5000, <<(length(Vs)):32/native,0:32,~n"
+ w(" call(5000, <<(length(Vs)):32/native,0:32,~n"
" Nx:?GLdouble,Ny:?GLdouble,Nz:?GLdouble,~n"
" (<< <<Vx:?GLdouble,Vy:?GLdouble,Vz:?GLdouble >>~n"
" || {Vx,Vy,Vz} <- Vs>>)/binary >>).~n~n", []),
[gen_funcs(F) || F <- Fs],
- close(),
+ close(),
ok.
gen_funcs([F]) when is_list(F) ->
@@ -178,6 +215,17 @@ gen_funcs(F) ->
erase(current_func),
w(".~n~n",[]).
+gen_types(Where) ->
+ case Where of
+ glu -> ignore;
+ gl ->
+ w("-type clamp() :: float().~n", []),
+ w("-type offset() :: non_neg_integer().~n", [])
+ end,
+ w("-type enum() :: non_neg_integer().~n", []),
+ w("-type mem() :: binary() | tuple().~n", []),
+ ok.
+
gen_export(F) ->
try gen_export_1(F)
catch E:R ->
@@ -199,19 +247,27 @@ gen_export2(#func{name=Name,params=As0}) ->
Args = lists:filter(fun(Arg) -> func_arg(Arg) =/= skip end, As0),
erl_func_name(Name) ++ "/" ++ integer_to_list(length(Args)).
-
-gen_doc([#func{alt={vector,VecPos,Vec}}]) ->
+gen_doc([#func{name=Name, alt={vector,VecPos,Vec}}]) ->
#func{type=T,params=As} = get(Vec),
{As1,As2} = lists:split(VecPos, As),
Args1 = case args(fun func_arg/1, ",", As1) of [] -> []; Else -> Else++"," end,
Args2 = args(fun func_arg/1, ",", As2),
- w("%% @spec (~s{~s}) -> ~s~n",[Args1,Args2,doc_return_types(T,As)]),
- w("%% @equiv ~s(~s)~n",[erl_func_name(Vec), Args1++Args2]);
+ w("%% @spec (~s{~s}) -> ~s~n",[Args1,Args2,doc_return_types(T,As, doc)]),
+ w("%% @equiv ~s(~s)~n",[erl_func_name(Vec), Args1++Args2]),
+ SA1 = case doc_arg_types(As1, spec) of [] -> []; E -> E++"," end,
+ SA2 = doc_arg_types(As2, spec),
+ w("-spec ~s(~s{~s}) -> ~s.~n",
+ [erl_func_name(Name), SA1, SA2,
+ doc_return_types(T,As, spec)]);
+
gen_doc([#func{name=Name,type=T,params=As,alt=Alt}|_]) ->
- w("%% @spec (~s) -> ~s~n", [doc_arg_types(As),doc_return_types(T,As)]),
+ w("%% @spec (~s) -> ~s~n", [doc_arg_types(As, doc),doc_return_types(T,As, doc)]),
GLDoc = "http://www.opengl.org/sdk/docs/man/xhtml/",
w("%% @doc See <a href=\"~s~s.xml\">external</a> documentation.~n",
- [GLDoc, doc_name(Name,Alt)]).
+ [GLDoc, doc_name(Name,Alt)]),
+ w("-spec ~s(~s) -> ~s.~n",
+ [erl_func_name(Name), doc_arg_types(As, spec), doc_return_types(T,As, spec)]).
+
gen_func(#func{name=Name,alt={vector,VecPos,Vec}}) ->
#func{params=As} = get(Vec),
@@ -229,9 +285,9 @@ gen_func(_F=#func{name=Name,type=T,params=As,id=MId}) ->
{StrArgs,_} = marshal_args(PreAs),
case have_return_vals(T,As) of
true ->
- w(" wxe_util:call(~p, <<~s>>)", [MId, StrArgs]);
+ w(" call(~p, <<~s>>)", [MId, StrArgs]);
false ->
- w(" wxe_util:cast(~p, <<~s>>)", [MId, StrArgs])
+ w(" cast(~p, <<~s>>)", [MId, StrArgs])
end.
func_arg(#arg{in=In,where=W,name=Name,type=Type})
@@ -249,60 +305,65 @@ func_arg(#arg{in=In,where=W,name=Name,type=Type})
end;
func_arg(_) -> skip.
-doc_arg_types(Ps0) ->
+doc_arg_types(Ps0, Type) ->
Ps = [P || P=#arg{in=In, where=Where} <- Ps0,In =/= false, Where =/= c],
- args(fun doc_arg_type/1, ",", Ps).
+ args(fun(Arg) -> doc_arg_type(Arg, Type) end, ",", Ps).
-doc_return_types(T, Ps0) ->
+doc_return_types(T, Ps0, Type) ->
Ps = [P || P=#arg{in=In, where=Where} <- Ps0,In =/= true, Where =/= c],
- doc_return_types2(T, Ps).
-
-doc_return_types2(void, []) -> "ok";
-doc_return_types2(void, [#arg{type=T}]) -> doc_arg_type2(T);
-doc_return_types2(T, []) -> doc_arg_type2(T);
-doc_return_types2(void, Ps) ->
- "{" ++ args(fun doc_arg_type/1,",",Ps) ++ "}";
-doc_return_types2(T, Ps) ->
- "{" ++ doc_arg_type2(T) ++ "," ++ args(fun doc_arg_type/1,",",Ps) ++ "}".
-
-doc_arg_type(#arg{name=Name,type=T}) ->
+ doc_return_types2(T, Ps, Type).
+
+doc_return_types2(void, [], _) -> "ok";
+doc_return_types2(void, [#arg{type=T}], _) -> doc_arg_type2(T);
+doc_return_types2(T, [], _) -> doc_arg_type2(T);
+doc_return_types2(void, Ps, Type) ->
+ "{" ++ args(fun(Arg) -> doc_arg_type(Arg, Type) end,",",Ps) ++ "}";
+doc_return_types2(T, Ps, Type) ->
+ "{" ++ doc_arg_type2(T) ++ "," ++
+ args(fun(Arg) -> doc_arg_type(Arg, Type) end,",",Ps) ++ "}".
+
+doc_arg_type(#arg{name=Name,type=T}, doc) ->
try
erl_arg_name(Name) ++ "::" ++ doc_arg_type2(T)
catch _:Error ->
io:format("Error: ~p ~p~n~p~n",[Name, Error, erlang:get_stacktrace()]),
exit(error)
+ end;
+doc_arg_type(#arg{name=Name,type=T}, spec) ->
+ try
+ doc_arg_type2(T)
+ catch _:Error ->
+ io:format("Error spec: ~p ~p~n~p~n",[Name, Error, erlang:get_stacktrace()]),
+ exit(error)
end.
+
doc_arg_type2(T=#type{single=true}) ->
doc_arg_type3(T);
doc_arg_type2(T=#type{single=undefined}) ->
doc_arg_type3(T);
doc_arg_type2(T=#type{single={tuple,undefined}}) ->
"{" ++ doc_arg_type3(T) ++ "}";
-doc_arg_type2(T=#type{single={tuple,_Sz}}) ->
- "{" ++ doc_arg_type3(T) ++ "}";
+doc_arg_type2(T=#type{single={tuple,Sz}}) ->
+ "{" ++ args(fun doc_arg_type3/1, ",", lists:duplicate(Sz,T)) ++ "}";
doc_arg_type2(T=#type{single=list}) ->
"[" ++ doc_arg_type3(T) ++ "]";
-doc_arg_type2(T=#type{single={list, Max}}) when is_integer(Max) ->
+doc_arg_type2(T=#type{single={list, _Max}}) ->
"[" ++ doc_arg_type3(T) ++ "]";
-doc_arg_type2(_T=#type{single={list,null}}) ->
- "string()";
-doc_arg_type2(T=#type{base=string}) ->
- doc_arg_type3(T);
doc_arg_type2(T=#type{single={list,_,_}}) ->
"[" ++ doc_arg_type3(T) ++ "]";
-doc_arg_type2(T=#type{single={tuple_list,_TSz}}) ->
- "[{" ++ doc_arg_type3(T) ++ "}]".
+doc_arg_type2(T=#type{single={tuple_list,Sz}}) ->
+ "[{" ++ args(fun doc_arg_type3/1, ",", lists:duplicate(Sz,T)) ++ "}]".
doc_arg_type3(#type{name="GLenum"}) -> "enum()";
doc_arg_type3(#type{name="GLclamp"++_}) -> "clamp()";
doc_arg_type3(#type{base=int}) -> "integer()";
doc_arg_type3(#type{base=float}) -> "float()";
-doc_arg_type3(#type{base=guard_int}) -> "offset()|binary()";
+doc_arg_type3(#type{base=guard_int}) -> "offset()|mem()";
doc_arg_type3(#type{base=string}) -> "string()";
doc_arg_type3(#type{base=bool}) -> "0|1";
doc_arg_type3(#type{base=binary}) -> "binary()";
-doc_arg_type3(#type{base=memory}) -> "wx:wx_mem()".
+doc_arg_type3(#type{base=memory}) -> "mem()".
guard_test(As) ->
Str = args(fun(#arg{name=N,type=#type{base=guard_int}}) ->
@@ -316,10 +377,10 @@ guard_test(As) ->
end.
pre_marshal([#arg{name=N,in=true,type=#type{base=binary}}|R]) ->
- w(" wxe_util:send_bin(~s),~n", [erl_arg_name(N)]),
+ w(" send_bin(~s),~n", [erl_arg_name(N)]),
pre_marshal(R);
pre_marshal([#arg{name=N,type=#type{base=memory}}|R]) ->
- w(" wxe_util:send_bin(~s#wx_mem.bin),~n", [erl_arg_name(N)]),
+ w(" send_bin(~s),~n", [erl_arg_name(N)]),
pre_marshal(R);
pre_marshal([A=#arg{name=N,type=#type{base=string,single=list}}|R]) ->
%% With null terminations
@@ -595,6 +656,7 @@ gen_debug(GL, GLU) ->
w("].~n~n", []),
close().
+
printd([F|R],Mod) when is_list(F) ->
printd(F,Mod),
printd(R,Mod);
diff --git a/lib/wx/api_gen/glapi.conf b/lib/wx/api_gen/glapi.conf
index f9ed7a1065..525ccf8b68 100644
--- a/lib/wx/api_gen/glapi.conf
+++ b/lib/wx/api_gen/glapi.conf
@@ -29,8 +29,35 @@
"glMatrixIndexPointerARB",
"glPixelTransformParameter",
%% OpengGL 3.0
- %"glGetTransformFeedbackVarying", %% Jobbiga
- %"glTransformFeedbackVaryings",
+
+ %% ARB
+ "glCreateSyncFromCLeventARB", % _cl_context _cl_event ??
+ "glDebugMessageCallbackARB",
+
+ "glGetn", %*
+ "glReadnPixels", %*
+
+ "glVertexP2", %*
+ "glVertexP3", %*
+ "glVertexP4", %*
+ "glTexCoordP1", %*
+ "glTexCoordP2", %*
+ "glTexCoordP3", %*
+ "glTexCoordP4", %*
+ "glMultiTexCoordP1", %*
+ "glMultiTexCoordP2", %*
+ "glMultiTexCoordP3", %*
+ "glMultiTexCoordP4", %*
+ "glNormalP3", %*
+ "glColorP3", %*
+ "glColorP4", %*
+ "glSecondaryColorP3", %*
+ "glVertexAttribP1", %*
+ "glVertexAttribP2", %*
+ "glVertexAttribP3", %*
+ "glVertexAttribP4", %*
+
+ "glGetActiveSubroutineUniformiv", %% Bad API don't know what to allocate needs to ask
%% EXT
%% By default skip these extensions
@@ -136,11 +163,12 @@
{"glRect", [{"v1", {single,{tuple,2}}},{"v2", {single,{tuple,2}}}]}.
-{"glGetString", {"result", {single,{list,null}}}}.
+{"glGetString", {"result", string}}.
{"glGetBooleanv", {"params", {single,{list,16}}}}.
{"glGetDoublev", {"params", {single,{list,16}}}}.
{"glGetFloatv", {"params", {single,{list,16}}}}.
{"glGetIntegerv", {"params", {single,{list,16}}}}.
+{"glGetInteger64v", {"params", {single,{list,16}}}}.
{"glFeedbackBuffer", {"buffer", [{base,memory}, in]}}.
{"glSelectBuffer", {"buffer", [{base,memory}, in]}}.
@@ -174,24 +202,24 @@
{"glGetActiveAttribARB", [{"length",[skip,{single, true}]},
{"size", {single, true}},
{"type", {single, true}},
- {"name", {single, {list,"maxLength","length"}}}
+ {"name", {string,"maxLength","length"}}
]}.
{"glGetActiveAttrib", [{"length",[skip,{single, true}]},
{"size", {single, true}},
{"type", {single, true}},
- {"name", {single, {list,"bufSize","length"}}}
+ {"name", {string,"bufSize","length"}}
]}.
{"glGetActiveUniformARB", [{"length",[skip,{single, true}]},
{"size", {single, true}},
{"type", {single, true}},
- {"name", {single, {list,"maxLength","length"}}}
+ {"name", {string,"maxLength","length"}}
]}.
{"glGetActiveUniform", [{"length",[skip,{single, true}]},
{"size", {single, true}},
{"type", {single, true}},
- {"name", {single, {list,"bufSize","length"}}}
+ {"name", {string,"bufSize","length"}}
]}.
{"glGetAttachedShaders", [{"count", [skip,{single,true}]},
@@ -201,18 +229,18 @@
{"glGetProgramiv", {"params", {single,true}}}.
{"glGetProgramInfoLog", [{"length", [skip,{single,true}]},
- {"infoLog", {single, {list,"bufSize","length"}}}
+ {"infoLog", {string,"bufSize","length"}}
]}.
{"glGetShaderiv", {"params", {single,true}}}.
{"glGetShaderInfoLog", [{"length", [skip,{single,true}]},
- {"infoLog", {single, {list,"bufSize","length"}}}
+ {"infoLog", {string,"bufSize","length"}}
]}.
{"glGetShaderSourceARB", [{"length", [skip,{single,true}]},
- {"source", {single, {list,"maxLength","length"}}}
+ {"source", {string,"maxLength","length"}}
]}.
{"glGetShaderSource", [{"length", [skip,{single,true}]},
- {"source", {single, {list,"bufSize","length"}}}
+ {"source", {string,"bufSize","length"}}
]}.
@@ -239,7 +267,7 @@
{"glMatrixIndex", [{"size",{c_only,{length,"indices"}}}, {"indices", {single,list}}]}.
-{"glProgramStringARB", [{"len",{c_only,{constant,"stringLen"}}},
+{"glProgramStringARB", [{"len",{c_only,{length,"string"}}},
{"string",[{base,string},{single,true}]}]}.
{"glGetProgramStringARB", {"string", [in,{base,memory}]}}.
{"glGenProgramsARB", {"programs", {single,{list,"n","n"}}}}.
@@ -250,7 +278,7 @@
{"glGetProgramLocalParameter", {"params", {single,{tuple,4}}}}.
{"glGetObjectParameter", {"params", {single,true}}}.
{"glGetInfoLogARB", [{"length", [skip,{single,true}]},
- {"infoLog", {single, {list,"maxLength","length"}}}
+ {"infoLog", {string,"maxLength","length"}}
]}.
{"glGetAttachedObjectsARB", [{"count", [skip,{single,true}]},
{"obj", {single, {list,"maxCount","count"}}}
@@ -280,9 +308,10 @@
{"objW",[{single,true},out]}]}.
{"gluBuild", {"data", [binary]}}.
{"gluScaleImage", [{"dataIn", [in, binary]}, {"dataOut", [in, {base, memory}]}]}.
-{"gluCheckExtension", [{"extName", {single, list}}, {"extString", {single, list}}]}.
-{"gluErrorString", {"result", {single, {list,null}}}}.
-{"gluGetString", {"result", {single, {list,null}}}}.
+{"gluCheckExtension", [{"extName", string},
+ {"extString", string}]}.
+{"gluErrorString", {"result", string}}.
+{"gluGetString", {"result", string}}.
{"gluDeleteQuadric", {"quad", in}}.
{"gluQuadric", {"quad", in}}.
@@ -291,15 +320,21 @@
{"gluDisk", {"quad", in}}.
{"gluCylinder", {"quad", in}}.
-%% OpenGL 3.0
+%% OpenGL 3.0 and later
{"glGetBooleani_v", {"data", {single,{list,16}}}}.
{"glGetIntegeri_v", {"data", {single,{list,16}}}}.
+{"glGetFloati_v", {"data", {single,{list,16}}}}.
+{"glGetDoublei_v", {"data", {single,{list,16}}}}.
+{"glGetInteger64i_v", {"data", {single,{list,16}}}}.
+
+{"glGetBufferParameteriv", {"params", {single,{list,16}}}}.
+{"glGetBufferParameteri64v", {"params", {single,{list,16}}}}.
{"glTransformFeedbackVaryings", [{"count", {c_only,{length,"varyings"}}},
{"varyings", [{base,string}, {single,list}]}]}.
-{"glGetTransformFeedbackVarying", [{"size", {single, true}},{"type", {single, true}},
+{"glGetTransformFeedbackVarying", [{"size", {single, true}},{"type", {single, true}},
{"length", [skip, {single, true}]},
- {"name", {single, {list,"bufSize","length"}}}]}.
+ {"name", {string,"bufSize","length"}}]}.
{"glGenRenderbuffers", {"renderbuffers", {single,{list,"n","n"}}}}.
@@ -327,7 +362,133 @@
{"params", [out, {single, {list, "uniformIndicesLen", "uniformIndicesLen"}}]}]}.
{"glGetActiveUniformName", [{"length",[skip,{single, true}]},
- {"uniformName", {single, {list,"bufSize","length"}}}]}.
+ {"uniformName", {string,"bufSize","length"}}]}.
{"glGetActiveUniformBlockName", [{"length",[skip,{single, true}]},
- {"uniformBlockName", {single, {list,"bufSize","length"}}}]}.
+ {"uniformBlockName", {string,"bufSize","length"}}]}.
{"glGetActiveUniformBlockiv", {"params", [in, {base,memory}]}}.
+
+
+{"glGetSynciv", [{"values", {single, {list, "bufSize","length"}}},
+ {"length", [skip,{single, true}]}]}.
+
+{"glGetMultisamplefv", {"val", [out, {single, {tuple,2}}]}}.
+
+
+{"glNamedStringARB", [{"stringlen", {c_only, {length, "string"}}},
+ {"namelen", {c_only, {length, "name"}}}]}.
+{"glDeleteNamedStringARB", [{"namelen", {c_only, {length, "name"}}}]}.
+{"glIsNamedStringARB", [{"namelen", {c_only, {length, "name"}}}]}.
+{"glGetNamedStringARB",[{"namelen", {c_only, {length, "name"}}},
+ {"stringlen",[skip,{single, true}]},
+ {"string", {string,"bufSize","stringlen"}}]}.
+{"glGetNamedStringivARB",[{"namelen", {c_only, {length, "name"}}},
+ {"params", [out, {single, true}]}]}.
+{"glCompileShaderIncludeARB", [{"length", {c_only,{constant,"NULL"}}},
+ {"count", {c_only,{length,"path"}}},
+ {"path", {single,list}}]}.
+
+
+{"glGenSamplers", {"samplers", {single, {list,"count","count"}}}}.
+{"glDeleteSamplers", [{"count", {c_only, {length, "samplers"}}},
+ {"samplers", {single, list}}]}.
+{"glGetSamplerParameter", {"params", {single, {list, 4}}}}.
+{"glSamplerParameterI", {"param", {single, list}}}.
+{"glSamplerParameterfv", {"param", {single, list}}}.
+{"glSamplerParameteriv", {"param", {single, list}}}.
+
+%{"glGetActiveSubroutineUniformiv", {"values", }}.
+{"glGetActiveSubroutineUniformName", [{"length",[skip,{single, true}]},
+ {"name", {string,"bufsize","length"}}]}.
+{"glGetActiveSubroutineName", [{"length",[skip,{single, true}]},
+ {"name", {string,"bufsize","length"}}]}.
+{"glGetProgramStageiv", {"values", {single, true}}}.
+{"glUniformSubroutinesuiv", [{"count",{c_only,{length,"indices"}}},{"indices", {single, list}}]}.
+
+{"glGenTransformFeedbacks", {"ids", {single, {list,"n","n"}}}}.
+{"glDeleteTransformFeedbacks", [{"n", {c_only, {length, "ids"}}},
+ {"ids", {single, list}}]}.
+
+{"glPatchParameterfv", {"values", {single, list}}}.
+
+
+{"glGetQueryIndexediv", {"params", {single, true} }}.
+{"glShaderBinary", [{"count", {c_only, {length, "shaders"}}},
+ {"length", {c_only, {size, "binary"}}},
+ {"shaders", {single, list}},
+ {"binary", binary}
+ ]}.
+{"glGetShaderPrecisionFormat", [{"range", {single, {tuple, 2}}},
+ {"precision", {single, true}}]}.
+
+{"glGetProgramBinary", [{"length",[skip,{single, true}]},
+ {"binary", [out, {binary, "bufSize", "length"}]},
+ {"binaryFormat", {single, true}}]}.
+{"glProgramBinary", [{"binary", binary}, {"length", {c_only, {size, "binary"}}}]}.
+
+{"glGenProgramPipelines", {"pipelines", {single, {list,"n","n"}}}}.
+{"glDeleteProgramPipelines", [{"n", {c_only, {length, "pipelines"}}},
+ {"pipelines", {single, list}}]}.
+
+{"glCreateShaderProgramv", [{"count", {c_only, {length, "strings"}}},
+ {"strings", {single, list}}]}.
+{"glGetProgramPipelineInfoLog", [{"length", [skip,{single, true}]},
+ {"infoLog", {string,"bufSize","length"}}]}.
+{"glGetProgramPipelineiv", {"params", {single, true}}}.
+
+
+%% {"glCreateSyncFromCLeventARB", {"context", }}.
+
+{"glDebugMessageControlARB", [{"count", {c_only, {length, "ids"}}},
+ {"ids", {single, list}}]}.
+{"glDebugMessageInsertARB", {"length", {c_only, {length, "buf"}}}}.
+{"glGetDebugMessageLogARB", [{"sources", {single, {list, "count", "result"}}},
+ {"types", {single, {list, "count", "result"}}},
+ {"ids", {single, {list, "count", "result"}}},
+ {"severities", {single, {list, "count", "result"}}},
+ {"lengths", [{c_only, undefined}, {single, {list, "count", "result"}}]},
+ {"messageLog", [{string, "bufsize", "lengths"},
+ {single, {list, "bufsize", "result"}}]}]}.
+
+
+{"glUniformMatrix2dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,4}}]}]}.
+{"glUniformMatrix3dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,9}}]}]}.
+{"glUniformMatrix4dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,16}}]}]}.
+{"glUniformMatrix2x3dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,6}}]}]}.
+{"glUniformMatrix3x2dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,6}}]}]}.
+{"glUniformMatrix2x4dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,8}}]}]}.
+{"glUniformMatrix4x2dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,8}}]}]}.
+{"glUniformMatrix3x4dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,12}}]}]}.
+{"glUniformMatrix4x3dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,12}}]}]}.
+
+{"glProgramUniform1", [{"count",{c_only,{length,"value"}}}, {"value", [{single,list}]}]}.
+{"glProgramUniform2", [{"count",{c_only,{length,"value"}}}, {"value", [{single,{tuple_list,2}}]}]}.
+{"glProgramUniform3", [{"count",{c_only,{length,"value"}}}, {"value", [{single,{tuple_list,3}}]}]}.
+{"glProgramUniform4", [{"count",{c_only,{length,"value"}}}, {"value", [{single,{tuple_list,4}}]}]}.
+
+{"glProgramUniformMatrix2fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,4}}]}]}.
+{"glProgramUniformMatrix2dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,4}}]}]}.
+{"glProgramUniformMatrix3dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,9}}]}]}.
+{"glProgramUniformMatrix3fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,9}}]}]}.
+{"glProgramUniformMatrix4dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,16}}]}]}.
+{"glProgramUniformMatrix4fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,16}}]}]}.
+{"glProgramUniformMatrix2x3fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,6}}]}]}.
+{"glProgramUniformMatrix3x2fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,6}}]}]}.
+{"glProgramUniformMatrix2x4fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,8}}]}]}.
+{"glProgramUniformMatrix4x2fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,8}}]}]}.
+{"glProgramUniformMatrix3x4fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,12}}]}]}.
+{"glProgramUniformMatrix4x3fv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,12}}]}]}.
+
+{"glProgramUniformMatrix2x3dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,6}}]}]}.
+{"glProgramUniformMatrix3x2dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,6}}]}]}.
+{"glProgramUniformMatrix2x4dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,8}}]}]}.
+{"glProgramUniformMatrix4x2dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,8}}]}]}.
+{"glProgramUniformMatrix3x4dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,12}}]}]}.
+{"glProgramUniformMatrix4x3dv", [{"count",{c_only,{length,"value"}}},{"value", [{single,{tuple_list,12}}]}]}.
+
+{"glViewportArrayv", [{"count",{c_only,{length,"v"}}}, {"v", [{single,{tuple_list,4}}]}]}.
+{"glViewportIndexedfv", {"v", {single,{tuple,4}}}}.
+{"glScissorArrayv", [{"count",{c_only,{length,"v"}}}, {"v", [{single,{tuple_list,4}}]}]}.
+{"glScissorIndexedv", {"v", {single,{tuple,4}}}}.
+{"glDepthRangeArrayv", [{"count",{c_only,{length,"v"}}}, {"v", [{single,{tuple_list,2}}]}]}.
+
+
diff --git a/lib/wx/api_gen/wx_gen.erl b/lib/wx/api_gen/wx_gen.erl
index c075324c1f..2f20c42a5d 100644
--- a/lib/wx/api_gen/wx_gen.erl
+++ b/lib/wx/api_gen/wx_gen.erl
@@ -887,7 +887,7 @@ add_method2(M0=#method{name=Name,params=Ps0,type=T0},#class{name=CName,parent=Pa
id = next_id(func_id),
pre_hook = get_opt(pre_hook, Name, length(Ps), Opts),
post_hook = get_opt(post_hook, Name, length(Ps), Opts),
- doc = get_opt(doc, Name, length(Ps), Opts)
+ doc = get_opt(doc, Name, length(Ps), Opts)
},
M = case Name of
CName ->
diff --git a/lib/wx/api_gen/wx_gen.hrl b/lib/wx/api_gen/wx_gen.hrl
index 17265a2842..426e3adfae 100644
--- a/lib/wx/api_gen/wx_gen.hrl
+++ b/lib/wx/api_gen/wx_gen.hrl
@@ -43,9 +43,9 @@
id = undefined, % Id (integer)
doc, % Extra documentation
virtual, % Is virtual?
- pre_hook, % Pre hook before call in c-code
- post_hook % Post hook after call in c-code
- }
+ pre_hook = [], % Pre hook before call in c-code
+ post_hook = [] % Post hook after call in c-code
+ }
).
-record(param,
diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl
index 846cec46c4..4b33068d8f 100644
--- a/lib/wx/api_gen/wx_gen_cpp.erl
+++ b/lib/wx/api_gen/wx_gen_cpp.erl
@@ -152,6 +152,7 @@ gen_funcs(Defs) ->
w("#include \"../wxe_impl.h\"~n"),
w("#include \"../wxe_events.h\"~n"),
w("#include \"../wxe_return.h\"~n"),
+ w("#include \"../wxe_gl.h\"~n"),
w("#include \"wxe_macros.h\"~n"),
w("#include \"wxe_derived_dest.h\"~n~n"),
@@ -176,6 +177,9 @@ gen_funcs(Defs) ->
" rt.addAtom(\"ok\");~n"
" break;~n"
" }~n"),
+ w(" case WXE_BIN_INCR:~n driver_binary_inc_refc(Ecmd.bin[0]->bin);~n break;~n",[]),
+ w(" case WXE_BIN_DECR:~n driver_binary_dec_refc(Ecmd.bin[0]->bin);~n break;~n",[]),
+ w(" case WXE_INIT_OPENGL:~n wxe_initOpenGL(rt, bp);~n break;~n",[]),
Res = [gen_class(Class) || Class <- Defs],
@@ -265,13 +269,13 @@ gen_method(CName, M=#method{name=N,params=Ps0,type=T,method_type=MT,id=MethodId
Opts = [Opt || Opt = #param{def=Def,in=In,where=Where} <- Ps2,
Def =/= none, In =/= false, Where =/= c],
decode_options(Opts, Align),
- case M#method.pre_hook of
- undefined -> skip;
+ case gen_util:get_hook(c, M#method.pre_hook) of
+ ignore -> skip;
Pre -> w(" ~s;~n", [Pre])
end,
Ps3 = call_wx(N,{MT,CName},T,Ps2),
- case M#method.post_hook of
- undefined -> skip;
+ case gen_util:get_hook(c, M#method.post_hook) of
+ ignore -> skip;
Post -> w(" ~s;~n", [Post])
end,
free_args(),
diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl
index 7962dd9fbf..e1201ab0d4 100644
--- a/lib/wx/api_gen/wx_gen_erl.erl
+++ b/lib/wx/api_gen/wx_gen_erl.erl
@@ -270,6 +270,16 @@ gen_method2(M=#method{name=N,alias=A,params=Ps,type=T,method_type=MT,id=MethodId
MId = arg_type_tests(Args, "?" ++ get_unique_name(MethodId)),
{MArgs,Align} = marshal_args(Args),
MOpts = marshal_opts(Optional, Align, Args),
+ case gen_util:get_hook(erl, M#method.pre_hook) of
+ ignore -> skip;
+ Pre -> w(" ~s~n", [Pre])
+ end,
+
+ case gen_util:get_hook(erl, M#method.post_hook) of
+ ignore -> skip;
+ _ -> w(" _Result =", [])
+ end,
+
case have_return_vals(T, Ps) of
_ when MT =:= constructor ->
w(" wxe_util:construct(~s,~n <<~s~s>>)", [MId, MArgs,MOpts]);
@@ -278,6 +288,13 @@ gen_method2(M=#method{name=N,alias=A,params=Ps,type=T,method_type=MT,id=MethodId
false ->
w(" wxe_util:cast(~s,~n <<~s~s>>)", [MId, MArgs,MOpts])
end,
+ case gen_util:get_hook(erl, M#method.post_hook) of
+ ignore -> skip;
+ Post ->
+ w(",~n ~s~n", [Post]),
+ w(" _Result", [])
+ end,
+
erase(current_func),
M.
diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf
index c91a9d7fff..aec8a4944a 100644
--- a/lib/wx/api_gen/wxapi.conf
+++ b/lib/wx/api_gen/wxapi.conf
@@ -505,15 +505,15 @@
{"data",[in,{base,binary}]},
{"alpha",[in,{base,binary}]},
{{4,pre_hook},
- "if(!static_data) {"
- "data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
- "memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"},
+ [{c, "if(!static_data) {"
+ "data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
+ "memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"}]},
{{5,pre_hook},
- "if(!static_data) {"
- " data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
- " alpha = (unsigned char *) malloc(Ecmd.bin[1]->size);"
- " memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);"
- " memcpy(alpha,Ecmd.bin[1]->base,Ecmd.bin[1]->size);}"}
+ [{c, "if(!static_data) {"
+ " data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
+ " alpha = (unsigned char *) malloc(Ecmd.bin[1]->size);"
+ " memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);"
+ " memcpy(alpha,Ecmd.bin[1]->base,Ecmd.bin[1]->size);}"}]}
]},
'~wxImage',%'AddHandler',
'Blur','BlurHorizontal','BlurVertical',
@@ -524,15 +524,15 @@
{"data",[in,{base,binary}]},
{"alpha",[in,{base,binary}]},
{{4,pre_hook},
- "if(!static_data) {"
- "data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
- "memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"},
+ [{c, "if(!static_data) {"
+ "data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
+ "memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"}]},
{{5,pre_hook},
- "if(!static_data) {"
- " data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
- " alpha = (unsigned char *) malloc(Ecmd.bin[1]->size);"
- " memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);"
- " memcpy(alpha,Ecmd.bin[1]->base,Ecmd.bin[1]->size);}"}
+ [{c, "if(!static_data) {"
+ " data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
+ " alpha = (unsigned char *) malloc(Ecmd.bin[1]->size);"
+ " memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);"
+ " memcpy(alpha,Ecmd.bin[1]->base,Ecmd.bin[1]->size);}"}]}
]},
'Destroy','FindFirstUnusedColour', % 'FindHandler',
'GetImageExtWildcard',
@@ -551,15 +551,15 @@
'Rotate90','SaveFile','Scale','Size',
{'SetAlpha', [{{2,"alpha"},[in,{base,binary}, {def, none}]},
{{2,pre_hook},
- "if(!static_data) {"
- "alpha = (unsigned char *) malloc(Ecmd.bin[0]->size);"
- "memcpy(alpha,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"}
+ [{c, "if(!static_data) {"
+ "alpha = (unsigned char *) malloc(Ecmd.bin[0]->size);"
+ "memcpy(alpha,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"}]}
]},
{'SetData', [{"data",[in,{base,binary}]},
{pre_hook,
- "if(!static_data) {"
- "data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
- "memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"}
+ [{c, "if(!static_data) {"
+ "data = (unsigned char *) malloc(Ecmd.bin[0]->size);"
+ "memcpy(data,Ecmd.bin[0]->base,Ecmd.bin[0]->size);}"}]}
]},
'SetMask','SetMaskColour','SetMaskFromImage','SetOption',
'SetPalette',
@@ -902,8 +902,8 @@
['wxTreeCtrl','~wxTreeCtrl','AddRoot','AppendItem',
%% Not on Windows 'AssignButtonsImageList','GetButtonsImageList','SetButtonsImageList'
'AssignImageList','AssignStateImageList','Collapse','CollapseAndReset',
- 'Create','Delete','DeleteAllItems','DeleteChildren',{'EditLabel',1},
- %'EndEditLabel',
+ 'Create','Delete','DeleteAllItems','DeleteChildren',
+ {'EditLabel', [{"textCtrlClass", [nowhere]}]}, %'EndEditLabel',
'EnsureVisible','Expand','GetBoundingRect',
'GetChildrenCount','GetCount','GetEditControl',
{'GetFirstChild',[{"cookie", out}]}, {'GetNextChild',[{"cookie", [both]}]},
@@ -1148,7 +1148,8 @@
[{skip, [{'SetCurrent', 2}]}], %% NA MAC
[{'wxGLCanvas', [{"attribList", [in, {single,array}]}]},
'GetContext',
- {'SetCurrent', [{post_hook,"if(This->GetContext()) setActiveGL(Ecmd.caller,This)"}]},
+ {'SetCurrent', [{post_hook,[{c, "if(This->GetContext()) setActiveGL(Ecmd.caller,This)"},
+ {erl, "{ok, _} = wxe_master:init_opengl(),"}]}]},
%%{'SetColour', [{"colour", [in, {single,array}]}]},
'SwapBuffers']}.
@@ -1740,6 +1741,11 @@
%% 'GetItemRect', 'SetItemRect', 'GetToolId', 'SetToolId'
%% ]}.
+{class, wxSystemSettings, object, [],
+ [
+ 'GetColour','GetFont','GetMetric','GetScreenType'
+ ]}.
+
{class, wxAuiNotebookEvent, wxNotifyEvent,
[{acc, [{old_selection, "GetOldSelection()"},
{selection, "GetSelection()"},
diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in
index 8710641b57..69418f62ef 100644
--- a/lib/wx/c_src/Makefile.in
+++ b/lib/wx/c_src/Makefile.in
@@ -32,14 +32,16 @@ endif
SO_EXT = @SO_EXT@
-GENERAL = wxe_driver wxe_ps_init wxe_impl wxePrintout wxe_return
+GENERAL = wxe_driver wxe_ps_init wxe_impl wxePrintout wxe_return wxe_gl
GENERAL_H = wxe_driver.h wxe_impl.h wxe_return.h
GENERATED_F = wxe_funcs wxe_events wxe_init
-GENERATED_H = gen/wxe_macros.h gen/glu_finit.h gen/gl_finit.h gen/gl_fdefs.h
+GENERATED_H = gen/wxe_macros.h
+
+GL_H = egl_impl.h gen/glu_finit.h gen/gl_finit.h gen/gl_fdefs.h
HAVE_OPENGL = true
-OPENGL_F = gl_funcs wxe_gl
+OPENGL_F = gl_funcs egl_impl
ifneq ($(INSIDE_ERLSRC),true)
@@ -60,9 +62,9 @@ SYS_TYPE = @WXERL_SYS_TYPE@
GENERAL_O = $(GENERAL:%=$(SYS_TYPE)/%.o)
GENERATED_O = $(GENERATED_F:%=$(SYS_TYPE)/%.o)
ifeq ($(HAVE_OPENGL), true)
- OPENGL_O = $(OPENGL_F:%=$(SYS_TYPE)/%.o)
+ GL_OBJECTS = $(OPENGL_F:%=$(SYS_TYPE)/%.o)
else
- OPENGL_O =
+ GL_OBJECTS =
endif
RC_FILE_EXT = @RC_FILE_TYPE@
@@ -72,10 +74,12 @@ else
RC_FILE =
endif
-OBJECTS = $(GENERAL_O) $(GENERATED_O) $(OPENGL_O) $(RC_FILE)
+WX_OBJECTS = $(GENERAL_O) $(GENERATED_O) $(RC_FILE)
+
+OBJECTS = $(WX_OBJECTS) $(GL_OBJECTS)
-TARGET_API = wxe_driver
-TARGET_DIR = ../priv/$(SYS_TYPE)
+TARGET_APIS = wxe_driver erl_gl
+TARGET_DIR = ../priv
# -O2 -funroll-loops -ffast-math -fomit-frame-pointer
@@ -87,31 +91,36 @@ LD = $(CPP)
LDFLAGS = @LDFLAGS@
RESCOMP = @WX_RESCOMP@
-
ifeq (@WX_HAVE_STATIC_LIBS@,true)
-WX_LIBS = @WX_LIBS_STATIC@
+OPT_WX_LIBS = @WX_LIBS_STATIC@
DEBUG_WX_LIBS = @DEBUG_WX_LIBS_STATIC@
else
-WX_LIBS = @WX_LIBS@
+OPT_WX_LIBS = @WX_LIBS@
DEBUG_WX_LIBS = @DEBUG_WX_LIBS@
endif
ifeq ($(TYPE),debug)
-CFLAGS = @DEBUG_WX_CFLAGS@ @DEBUG_CFLAGS@
-CPP_FLAGS = @DEBUG_WX_CXXFLAGS@ @DEBUG_CXXFLAGS@
-LIBS = $(DEBUG_WX_LIBS)
+WX_CFLAGS = @DEBUG_WX_CFLAGS@
+CFLAGS = @DEBUG_CFLAGS@
+WX_CXX_FLAGS = @DEBUG_WX_CXXFLAGS@
+CXX_FLAGS = @DEBUG_CXXFLAGS@
+WX_LIBS = $(DEBUG_WX_LIBS)
else
-CFLAGS = @WX_CFLAGS@ @CFLAGS@
-CPP_FLAGS = @WX_CXXFLAGS@ @CXXFLAGS@
-LIBS = $(WX_LIBS)
+WX_CFLAGS = @WX_CFLAGS@
+CFLAGS = @CFLAGS@
+WX_CXX_FLAGS = @WX_CXXFLAGS@
+CXX_FLAGS = @CXXFLAGS@
+WX_LIBS = $(OPT_WX_LIBS)
endif
-CC_O = $(CC) -c $(CFLAGS) $(COMMON_CFLAGS)
-CPP_O = $(CPP) -c $(CPP_FLAGS) $(COMMON_CFLAGS)
+GL_LIBS = @GL_LIBS@
+
+CC_O = $(CC) -c $(CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS)
+CPP_O = $(CPP) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS)
# Targets
-opt: $(TARGET_DIR)/$(TARGET_API)$(SO_EXT)
+opt: $(TARGET_DIR)/wxe_driver$(SO_EXT) $(TARGET_DIR)/erl_gl$(SO_EXT)
debug:
@${MAKE} TYPE=debug
@@ -132,20 +141,22 @@ complete_clean:
docs:
+$(GL_OBJECTS): $(GL_H)
+$(WX_OBJECTS): $(GENERATED_H) $(GENERAL_H)
-$(SYS_TYPE)/%.o: %.cpp $(GENERATED_H) $(GENERAL_H)
+$(SYS_TYPE)/%.o: %.cpp
mkdir -p $(SYS_TYPE)
$(CPP_O) $< -o $@
-$(SYS_TYPE)/%.o: %.c $(GENERATED_H) $(GENERAL_H)
+$(SYS_TYPE)/%.o: %.c
mkdir -p $(SYS_TYPE)
$(CC_O) $< -o $@
-$(SYS_TYPE)/%.o: gen/%.cpp $(GENERATED_H) $(GENERAL_H)
+$(SYS_TYPE)/%.o: gen/%.cpp
mkdir -p $(SYS_TYPE)
$(CPP_O) $< -o $@
-$(SYS_TYPE)/%.o: gen/%.c $(GENERATED_H) $(GENERAL_H)
+$(SYS_TYPE)/%.o: gen/%.c
mkdir -p $(SYS_TYPE)
$(CC_O) $< -o $@
@@ -153,9 +164,13 @@ $(SYS_TYPE)/wxe_win32.$(RC_FILE_EXT): wxe_win32.rc
mkdir -p $(SYS_TYPE)
$(RESCOMP) -o $@ $<
-$(TARGET_DIR)/$(TARGET_API)$(SO_EXT): $(OBJECTS)
+$(TARGET_DIR)/wxe_driver$(SO_EXT): $(WX_OBJECTS)
+ mkdir -p $(TARGET_DIR)
+ $(LD) $(LDFLAGS) $(WX_OBJECTS) $(WX_LIBS) -o $@
+
+$(TARGET_DIR)/erl_gl$(SO_EXT): $(GL_OBJECTS)
mkdir -p $(TARGET_DIR)
- $(LD) $(LDFLAGS) $(OBJECTS) $(LIBS) -o $@
+ $(CC) $(LDFLAGS) $(GL_OBJECTS) $(GL_LIBS) -o $@
# ----------------------------------------------------
@@ -164,10 +179,11 @@ $(TARGET_DIR)/$(TARGET_API)$(SO_EXT): $(OBJECTS)
ifeq ($(INSIDE_ERLSRC),true)
include $(ERL_TOP)/make/otp_release_targets.mk
release_spec: opt
- $(INSTALL_DIR) $(RELSYSDIR)/priv/$(SYS_TYPE)
+ $(INSTALL_DIR) $(RELSYSDIR)/priv
$(INSTALL_DATA) ../priv/erlang-logo32.png $(RELSYSDIR)/priv/
$(INSTALL_DATA) ../priv/erlang-logo64.png $(RELSYSDIR)/priv/
- $(INSTALL_PROGRAM) $(TARGET_DIR)/$(TARGET_API)$(SO_EXT) $(RELSYSDIR)/priv/$(SYS_TYPE)
+ $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) $(RELSYSDIR)/priv/
+ $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) $(RELSYSDIR)/priv/
release_docs_spec:
diff --git a/lib/wx/c_src/egl_impl.cpp b/lib/wx/c_src/egl_impl.cpp
new file mode 100644
index 0000000000..e2dbbb73c4
--- /dev/null
+++ b/lib/wx/c_src/egl_impl.cpp
@@ -0,0 +1,306 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include "egl_impl.h"
+
+#define WX_DEF_EXTS
+#include "gen/gl_fdefs.h"
+#include "gen/gl_finit.h"
+#include "gen/glu_finit.h"
+
+void init_tess();
+void exit_tess();
+int load_gl_functions();
+
+/* ****************************************************************************
+ * OPENGL INITIALIZATION
+ *****************************************************************************/
+
+int egl_initiated = 0;
+
+#ifdef _WIN32
+#define RTLD_LAZY 0
+#define OPENGL_LIB L"opengl32.dll"
+#define OPENGLU_LIB L"glu32.dll"
+typedef HMODULE DL_LIB_P;
+typedef WCHAR DL_CHAR;
+void * dlsym(HMODULE Lib, const char *func) {
+ void * funcp;
+ if((funcp = (void *) GetProcAddress(Lib, func)))
+ return funcp;
+ else
+ return (void *) wglGetProcAddress(func);
+}
+
+HMODULE dlopen(const WCHAR *DLL, int unused) {
+ return LoadLibrary(DLL);
+}
+
+void dlclose(HMODULE Lib) {
+ FreeLibrary(Lib);
+}
+
+#else
+typedef void * DL_LIB_P;
+typedef char DL_CHAR;
+# ifdef _MACOSX
+# define OPENGL_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"
+# define OPENGLU_LIB "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib"
+# else
+# define OPENGL_LIB "libGL.so"
+# define OPENGLU_LIB "libGLU.so"
+# endif
+#endif
+extern "C" {
+DRIVER_INIT(EGL_DRIVER) {
+ return NULL;
+}
+}
+
+int egl_init_opengl(void *erlCallbacks)
+{
+#ifdef _WIN32
+ driver_init((TWinDynDriverCallbacks *) erlCallbacks);
+#endif
+ if(egl_initiated == 0) {
+ if(load_gl_functions()) {
+ init_tess();
+ egl_initiated = 1;
+ }
+ }
+ return 1;
+}
+
+int load_gl_functions() {
+ DL_CHAR * DLName = OPENGL_LIB;
+ DL_LIB_P LIBhandle = dlopen(DLName, RTLD_LAZY);
+ //fprintf(stderr, "Loading GL: %s\r\n", (const char*)DLName);
+ void * func = NULL;
+ int i;
+
+ if(LIBhandle) {
+ for(i=0; gl_fns[i].name != NULL; i++) {
+ if((func = dlsym(LIBhandle, gl_fns[i].name))) {
+ * (void **) (gl_fns[i].func) = func;
+ // fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].name);
+ } else {
+ if(gl_fns[i].alt != NULL) {
+ if((func = dlsym(LIBhandle, gl_fns[i].alt))) {
+ * (void **) (gl_fns[i].func) = func;
+ // fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].alt);
+ } else {
+ * (void **) (gl_fns[i].func) = (void *) &gl_error;
+ // fprintf(stderr, "GL Skipped %s and %s \r\n", gl_fns[i].name, gl_fns[i].alt);
+ };
+ } else {
+ * (void **) (gl_fns[i].func) = (void *) &gl_error;
+ // fprintf(stderr, "GL Skipped %s \r\n", gl_fns[i].name);
+ }
+ }
+ }
+ dlclose(LIBhandle);
+ // fprintf(stderr, "OPENGL library is loaded\r\n");
+ } else {
+ fprintf(stderr, "Could NOT load OpenGL library: %s\r\n", DLName);
+ };
+
+ DLName = OPENGLU_LIB;
+ LIBhandle = dlopen(DLName, RTLD_LAZY);
+ // fprintf(stderr, "Loading GLU: %s\r\n", (const char*)DLName);
+ func = NULL;
+
+ if(LIBhandle) {
+ for(i=0; glu_fns[i].name != NULL; i++) {
+ if((func = dlsym(LIBhandle, glu_fns[i].name))) {
+ * (void **) (glu_fns[i].func) = func;
+ } else {
+ if(glu_fns[i].alt != NULL) {
+ if((func = dlsym(LIBhandle, glu_fns[i].alt))) {
+ * (void **) (glu_fns[i].func) = func;
+ } else {
+ * (void **) (glu_fns[i].func) = (void *) &gl_error;
+ // fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].alt);
+ };
+ } else {
+ * (void **) (glu_fns[i].func) = (void *) &gl_error;
+ // fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].name);
+ }
+ }
+ }
+ dlclose(LIBhandle);
+ // fprintf(stderr, "GLU library is loaded\r\n");
+ } else {
+ fprintf(stderr, "Could NOT load OpenGL GLU library: %s\r\n", DLName);
+ };
+
+ return 1;
+}
+
+void gl_error() {
+ // fprintf(stderr, "OpenGL Extension not available \r\n");
+ throw "undef_extension";
+}
+
+/* *******************************************************************************
+ * GLU Tesselation special
+ * ******************************************************************************/
+
+static GLUtesselator* tess;
+static GLdouble* tess_coords;
+static GLdouble* tess_alloc_vertex;
+static int* tess_vertices;
+
+void CALLBACK
+egl_ogla_vertex(GLdouble* coords)
+{
+ /* fprintf(stderr, "%d\r\n", (int) (coords - tess_coords) / 3); */
+
+ *tess_vertices++ = (int) (coords - tess_coords) / 3;
+}
+
+void CALLBACK
+egl_ogla_edge_flag(GLboolean flag)
+{
+}
+
+void CALLBACK
+egl_ogla_error(GLenum errorCode)
+{
+ const GLubyte *err;
+ err = gluErrorString(errorCode);
+ // wxString msg;
+ // msg.Printf(wxT("Tesselation error: %d: "), (int)errorCode);
+ // msg += wxString::FromAscii((char *) err);
+ // send_msg("error", &msg);
+ fprintf(stderr, "Tesselation error: %d\r\n", (int) errorCode);
+}
+
+void CALLBACK
+egl_ogla_combine(GLdouble coords[3],
+ void* vertex_data[4],
+ GLfloat w[4],
+ void **dataOut)
+{
+ GLdouble* vertex = tess_alloc_vertex;
+
+ tess_alloc_vertex += 3;
+
+#if 0
+ fprintf(stderr, "combine: ");
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (w[i] > 0.0) {
+ fprintf(stderr, "%d(%g) ", (int) vertex_data[i], w[i]);
+ }
+ }
+ fprintf(stderr, "\r\n");
+ fprintf(stderr, "%g %g %g\r\n", vertex[0], vertex[1], vertex[2]);
+#endif
+
+ vertex[0] = coords[0];
+ vertex[1] = coords[1];
+ vertex[2] = coords[2];
+ *dataOut = vertex;
+}
+
+void init_tess()
+{
+ tess = gluNewTess();
+
+ gluTessCallback(tess, GLU_TESS_VERTEX, (GLUfuncptr) egl_ogla_vertex);
+ gluTessCallback(tess, GLU_TESS_EDGE_FLAG, (GLUfuncptr) egl_ogla_edge_flag);
+ gluTessCallback(tess, GLU_TESS_COMBINE, (GLUfuncptr) egl_ogla_combine);
+ gluTessCallback(tess, GLU_TESS_ERROR, (GLUfuncptr) egl_ogla_error);
+
+}
+
+void exit_tess()
+{
+ gluDeleteTess(tess);
+}
+
+int erl_tess_impl(char* buff, ErlDrvPort port, ErlDrvTermData caller)
+{
+ ErlDrvBinary* bin;
+ int i;
+ GLdouble* new_vertices;
+ int *vertices;
+ int num_vertices;
+ GLdouble *n;
+ int n_pos, AP, res;
+
+ num_vertices = * (int *) buff; buff += 8; /* Align */
+ n = (double *) buff; buff += 8*3;
+
+ bin = driver_alloc_binary(num_vertices*6*sizeof(GLdouble));
+ new_vertices = tess_coords = (double *) bin->orig_bytes;
+ memcpy(tess_coords,buff,num_vertices*3*sizeof(GLdouble));
+ tess_alloc_vertex = tess_coords + num_vertices*3;
+
+#if 0
+ fprintf(stderr, "n=%d\r\n", num_vertices);
+#endif
+ vertices = (int *) driver_alloc(sizeof(int) * 16*num_vertices);
+
+ tess_vertices = vertices;
+
+ gluTessNormal(tess, n[0], n[1], n[2]);
+ gluTessBeginPolygon(tess, 0);
+ gluTessBeginContour(tess);
+ for (i = 0; i < num_vertices; i++) {
+ gluTessVertex(tess, tess_coords+3*i, tess_coords+3*i);
+ }
+ gluTessEndContour(tess);
+ gluTessEndPolygon(tess);
+
+ n_pos = (tess_vertices - vertices);
+
+ AP = 0; ErlDrvTermData *rt;
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData) * (13+n_pos*2));
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+
+ for(i=0; i < n_pos; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) vertices[i];
+ };
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = n_pos+1;
+
+ rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) bin;
+ rt[AP++] = (tess_alloc_vertex-new_vertices)*sizeof(GLdouble); rt[AP++] = 0;
+
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Return tuple {list, Bin}
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Result tuple
+
+ res = driver_send_term(port,caller,rt,AP);
+ /* fprintf(stderr, "List %d: %d %d %d \r\n", */
+ /* res, */
+ /* n_pos, */
+ /* (tess_alloc_vertex-new_vertices)*sizeof(GLdouble), */
+ /* num_vertices*6*sizeof(GLdouble)); */
+ driver_free_binary(bin);
+ driver_free(vertices);
+ driver_free(rt);
+ return 0;
+}
diff --git a/lib/wx/c_src/egl_impl.h b/lib/wx/c_src/egl_impl.h
new file mode 100644
index 0000000000..e93e4caefd
--- /dev/null
+++ b/lib/wx/c_src/egl_impl.h
@@ -0,0 +1,149 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2010. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_driver.h"
+
+/* Wrap everything from glext.h so we are not dependent on the user version of it */
+
+#ifndef _WIN32
+# include <dlfcn.h>
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <gl/gl.h>
+#include <gl/glu.h>
+#elif defined(HAVE_GL_GL_H)
+#include <GL/gl.h>
+# include <GL/glu.h>
+#elif defined(HAVE_OPENGL_GL_H)
+#include <OpenGL/gl.h>
+#include <OpenGL/glu.h>
+#endif
+
+#ifndef APIENTRY
+#define APIENTRY
+#endif
+
+#ifndef CALLBACK
+# define CALLBACK
+#endif
+
+#ifdef _WIN32
+# ifndef _GLUfuncptr
+// Visual studio CPP ++ compiler
+# define _GLUfuncptr void (_stdcall *)()
+# endif
+#endif
+
+#ifdef _GLUfuncptr
+# define GLUfuncptr _GLUfuncptr
+#elif defined(TESS_CB_TIGER_STYLE)
+# define GLUfuncptr GLvoid (*)(...)
+#else
+# define GLUfuncptr GLvoid (*)()
+#endif
+
+/* Some new GL types (eliminates the need for glext.h) */
+
+#ifndef HAVE_GLINTPTR
+#ifndef HAVE_GLINTPTRARB
+# include <stddef.h>
+/* GL types for handling large vertex buffer objects */
+typedef ptrdiff_t GLintptrARB;
+typedef ptrdiff_t GLsizeiptrARB;
+#endif /* HAVE_GLINTPTRARB */
+typedef GLintptrARB GLintptr;
+typedef GLsizeiptrARB GLsizeiptr;
+#endif /* HAVE_GLINTPTR */
+
+#ifndef HAVE_GLCHAR
+# ifndef HAVE_GLCHARARB
+/* GL types for handling shader object handles and characters */
+typedef char GLcharARB; /* native character */
+typedef unsigned int GLhandleARB; /* shader object handle */
+#endif /* HAVE_GLCHARARB */
+typedef GLcharARB GLchar;
+#endif
+
+#ifndef HAVE_GLHALFARB
+/* GL types for "half" precision (s10e5) float data in host memory */
+typedef unsigned short GLhalfARB;
+#endif
+
+/* Define int32_t, int64_t, and uint64_t types for UST/MSC */
+/* (as used in the GLX_OML_sync_control extension). */
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+#elif defined(__sun__)
+#include <inttypes.h>
+#if defined(__STDC__)
+#if defined(__arch64__)
+typedef long int int64_t;
+typedef unsigned long int uint64_t;
+#else
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#endif /* __arch64__ */
+#endif /* __STDC__ */
+#elif defined( __VMS )
+#include <inttypes.h>
+#elif defined(__SCO__) || defined(__USLC__)
+#include <stdint.h>
+#elif defined(__UNIXOS2__) || defined(__SOL64__)
+typedef long int int32_t;
+typedef long long int int64_t;
+typedef unsigned long long int uint64_t;
+#elif defined(WIN32) && defined(_MSC_VER)
+typedef long int int32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#elif defined(WIN32) && defined(__GNUC__)
+#include <stdint.h>
+#else
+#include <inttypes.h> /* Fallback option */
+#endif
+
+#ifndef HAVE_GLINT64EXT
+typedef int64_t GLint64EXT;
+typedef uint64_t GLuint64EXT;
+#endif
+
+#ifndef GL_ARB_sync
+typedef int64_t GLint64;
+typedef uint64_t GLuint64;
+typedef struct __GLsync *GLsync;
+#endif
+
+/* External Api */
+
+#ifdef _WIN32
+extern "C" __declspec(dllexport) int egl_init_opengl(void *);
+extern "C" __declspec(dllexport) void egl_dispatch(int, char *, ErlDrvPort, ErlDrvTermData, char **, int *);
+#else
+extern "C" int egl_init_opengl(void *);
+extern "C" void egl_dispatch(int, char *, ErlDrvPort, ErlDrvTermData, char **, int *);
+#endif
+
+/* internal */
+int erl_tess_impl(char* buff, ErlDrvPort port, ErlDrvTermData caller);
+void gl_error();
+extern int gl_error_op;
+extern ErlDrvTermData gl_active;
+
diff --git a/lib/wx/c_src/gen/gl_fdefs.h b/lib/wx/c_src/gen/gl_fdefs.h
index 2096f7a413..a45896d30d 100644
--- a/lib/wx/c_src/gen/gl_fdefs.h
+++ b/lib/wx/c_src/gen/gl_fdefs.h
@@ -24,6 +24,13 @@
# define WXE_EXTERN extern
#endif
+typedef struct {
+ const char * name;
+ const char * alt;
+ void * func;
+} gl_fns_t;
+
+#define GLE_GL_FUNC_START 5037
typedef void (APIENTRY * WXEGLACCUM)(GLenum,GLfloat);
WXE_EXTERN WXEGLACCUM weglAccum;
typedef void (APIENTRY * WXEGLALPHAFUNC)(GLenum,GLclampf);
@@ -684,7 +691,7 @@ typedef void (APIENTRY * WXEGLMULTTRANSPOSEMATRIXD)(const GLdouble *);
WXE_EXTERN WXEGLMULTTRANSPOSEMATRIXD weglMultTransposeMatrixd;
typedef void (APIENTRY * WXEGLBLENDFUNCSEPARATE)(GLenum,GLenum,GLenum,GLenum);
WXE_EXTERN WXEGLBLENDFUNCSEPARATE weglBlendFuncSeparate;
-typedef void (APIENTRY * WXEGLMULTIDRAWARRAYS)(GLenum,GLint *,GLsizei *,GLsizei);
+typedef void (APIENTRY * WXEGLMULTIDRAWARRAYS)(GLenum,const GLint *,const GLsizei *,GLsizei);
WXE_EXTERN WXEGLMULTIDRAWARRAYS weglMultiDrawArrays;
typedef void (APIENTRY * WXEGLPOINTPARAMETERF)(GLenum,GLfloat);
WXE_EXTERN WXEGLPOINTPARAMETERF weglPointParameterf;
@@ -972,6 +979,30 @@ typedef void (APIENTRY * WXEGLGETVERTEXATTRIBIIV)(GLuint,GLenum,GLint *);
WXE_EXTERN WXEGLGETVERTEXATTRIBIIV weglGetVertexAttribIiv;
typedef void (APIENTRY * WXEGLGETVERTEXATTRIBIUIV)(GLuint,GLenum,GLuint *);
WXE_EXTERN WXEGLGETVERTEXATTRIBIUIV weglGetVertexAttribIuiv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI1IV)(GLuint,const GLint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI1IV weglVertexAttribI1iv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI2IV)(GLuint,const GLint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI2IV weglVertexAttribI2iv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI3IV)(GLuint,const GLint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI3IV weglVertexAttribI3iv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI4IV)(GLuint,const GLint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI4IV weglVertexAttribI4iv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI1UIV)(GLuint,const GLuint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI1UIV weglVertexAttribI1uiv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI2UIV)(GLuint,const GLuint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI2UIV weglVertexAttribI2uiv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI3UIV)(GLuint,const GLuint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI3UIV weglVertexAttribI3uiv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI4UIV)(GLuint,const GLuint *);
+WXE_EXTERN WXEGLVERTEXATTRIBI4UIV weglVertexAttribI4uiv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI4BV)(GLuint,const GLbyte *);
+WXE_EXTERN WXEGLVERTEXATTRIBI4BV weglVertexAttribI4bv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI4SV)(GLuint,const GLshort *);
+WXE_EXTERN WXEGLVERTEXATTRIBI4SV weglVertexAttribI4sv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI4UBV)(GLuint,const GLubyte *);
+WXE_EXTERN WXEGLVERTEXATTRIBI4UBV weglVertexAttribI4ubv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBI4USV)(GLuint,const GLushort *);
+WXE_EXTERN WXEGLVERTEXATTRIBI4USV weglVertexAttribI4usv;
typedef void (APIENTRY * WXEGLGETUNIFORMUIV)(GLuint,GLint,GLuint *);
WXE_EXTERN WXEGLGETUNIFORMUIV weglGetUniformuiv;
typedef void (APIENTRY * WXEGLBINDFRAGDATALOCATION)(GLuint,GLuint,const GLchar *);
@@ -1012,30 +1043,6 @@ typedef void (APIENTRY * WXEGLCLEARBUFFERFI)(GLenum,GLint,GLfloat,GLint);
WXE_EXTERN WXEGLCLEARBUFFERFI weglClearBufferfi;
typedef const GLubyte * (APIENTRY * WXEGLGETSTRINGI)(GLenum,GLuint);
WXE_EXTERN WXEGLGETSTRINGI weglGetStringi;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI1IV)(GLuint,const GLint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI1IV weglVertexAttribI1iv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI2IV)(GLuint,const GLint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI2IV weglVertexAttribI2iv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI3IV)(GLuint,const GLint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI3IV weglVertexAttribI3iv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI4IV)(GLuint,const GLint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI4IV weglVertexAttribI4iv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI1UIV)(GLuint,const GLuint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI1UIV weglVertexAttribI1uiv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI2UIV)(GLuint,const GLuint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI2UIV weglVertexAttribI2uiv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI3UIV)(GLuint,const GLuint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI3UIV weglVertexAttribI3uiv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI4UIV)(GLuint,const GLuint *);
-WXE_EXTERN WXEGLVERTEXATTRIBI4UIV weglVertexAttribI4uiv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI4BV)(GLuint,const GLbyte *);
-WXE_EXTERN WXEGLVERTEXATTRIBI4BV weglVertexAttribI4bv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI4SV)(GLuint,const GLshort *);
-WXE_EXTERN WXEGLVERTEXATTRIBI4SV weglVertexAttribI4sv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI4UBV)(GLuint,const GLubyte *);
-WXE_EXTERN WXEGLVERTEXATTRIBI4UBV weglVertexAttribI4ubv;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBI4USV)(GLuint,const GLushort *);
-WXE_EXTERN WXEGLVERTEXATTRIBI4USV weglVertexAttribI4usv;
typedef void (APIENTRY * WXEGLDRAWARRAYSINSTANCED)(GLenum,GLint,GLsizei,GLsizei);
WXE_EXTERN WXEGLDRAWARRAYSINSTANCED weglDrawArraysInstanced;
typedef void (APIENTRY * WXEGLDRAWELEMENTSINSTANCED)(GLenum,GLsizei,GLenum,const GLvoid *,GLsizei);
@@ -1044,6 +1051,24 @@ typedef void (APIENTRY * WXEGLTEXBUFFER)(GLenum,GLenum,GLuint);
WXE_EXTERN WXEGLTEXBUFFER weglTexBuffer;
typedef void (APIENTRY * WXEGLPRIMITIVERESTARTINDEX)(GLuint);
WXE_EXTERN WXEGLPRIMITIVERESTARTINDEX weglPrimitiveRestartIndex;
+typedef void (APIENTRY * WXEGLGETINTEGER64I_V)(GLenum,GLuint,GLint64 *);
+WXE_EXTERN WXEGLGETINTEGER64I_V weglGetInteger64i_v;
+typedef void (APIENTRY * WXEGLGETBUFFERPARAMETERI64V)(GLenum,GLenum,GLint64 *);
+WXE_EXTERN WXEGLGETBUFFERPARAMETERI64V weglGetBufferParameteri64v;
+typedef void (APIENTRY * WXEGLFRAMEBUFFERTEXTURE)(GLenum,GLenum,GLuint,GLint);
+WXE_EXTERN WXEGLFRAMEBUFFERTEXTURE weglFramebufferTexture;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBDIVISOR)(GLuint,GLuint);
+WXE_EXTERN WXEGLVERTEXATTRIBDIVISOR weglVertexAttribDivisor;
+typedef void (APIENTRY * WXEGLMINSAMPLESHADING)(GLclampf);
+WXE_EXTERN WXEGLMINSAMPLESHADING weglMinSampleShading;
+typedef void (APIENTRY * WXEGLBLENDEQUATIONI)(GLuint,GLenum);
+WXE_EXTERN WXEGLBLENDEQUATIONI weglBlendEquationi;
+typedef void (APIENTRY * WXEGLBLENDEQUATIONSEPARATEI)(GLuint,GLenum,GLenum);
+WXE_EXTERN WXEGLBLENDEQUATIONSEPARATEI weglBlendEquationSeparatei;
+typedef void (APIENTRY * WXEGLBLENDFUNCI)(GLuint,GLenum,GLenum);
+WXE_EXTERN WXEGLBLENDFUNCI weglBlendFunci;
+typedef void (APIENTRY * WXEGLBLENDFUNCSEPARATEI)(GLuint,GLenum,GLenum,GLenum,GLenum);
+WXE_EXTERN WXEGLBLENDFUNCSEPARATEI weglBlendFuncSeparatei;
typedef void (APIENTRY * WXEGLLOADTRANSPOSEMATRIXFARB)(const GLfloat *);
WXE_EXTERN WXEGLLOADTRANSPOSEMATRIXFARB weglLoadTransposeMatrixfARB;
typedef void (APIENTRY * WXEGLLOADTRANSPOSEMATRIXDARB)(const GLdouble *);
@@ -1112,6 +1137,8 @@ typedef void (APIENTRY * WXEGLGETPROGRAMLOCALPARAMETERFVARB)(GLenum,GLuint,GLflo
WXE_EXTERN WXEGLGETPROGRAMLOCALPARAMETERFVARB weglGetProgramLocalParameterfvARB;
typedef void (APIENTRY * WXEGLGETPROGRAMSTRINGARB)(GLenum,GLenum,GLvoid *);
WXE_EXTERN WXEGLGETPROGRAMSTRINGARB weglGetProgramStringARB;
+typedef void (APIENTRY * WXEGLGETBUFFERPARAMETERIVARB)(GLenum,GLenum,GLint *);
+WXE_EXTERN WXEGLGETBUFFERPARAMETERIVARB weglGetBufferParameterivARB;
typedef void (APIENTRY * WXEGLDELETEOBJECTARB)(GLhandleARB);
WXE_EXTERN WXEGLDELETEOBJECTARB weglDeleteObjectARB;
typedef GLhandleARB (APIENTRY * WXEGLGETHANDLEARB)(GLenum);
@@ -1198,14 +1225,8 @@ typedef void (APIENTRY * WXEGLRENDERBUFFERSTORAGEMULTISAMPLE)(GLenum,GLsizei,GLe
WXE_EXTERN WXEGLRENDERBUFFERSTORAGEMULTISAMPLE weglRenderbufferStorageMultisample;
typedef void (APIENTRY * WXEGLFRAMEBUFFERTEXTURELAYER)(GLenum,GLenum,GLuint,GLint,GLint);
WXE_EXTERN WXEGLFRAMEBUFFERTEXTURELAYER weglFramebufferTextureLayer;
-typedef void (APIENTRY * WXEGLPROGRAMPARAMETERIARB)(GLuint,GLenum,GLint);
-WXE_EXTERN WXEGLPROGRAMPARAMETERIARB weglProgramParameteriARB;
-typedef void (APIENTRY * WXEGLFRAMEBUFFERTEXTUREARB)(GLenum,GLenum,GLuint,GLint);
-WXE_EXTERN WXEGLFRAMEBUFFERTEXTUREARB weglFramebufferTextureARB;
typedef void (APIENTRY * WXEGLFRAMEBUFFERTEXTUREFACEARB)(GLenum,GLenum,GLuint,GLint,GLenum);
WXE_EXTERN WXEGLFRAMEBUFFERTEXTUREFACEARB weglFramebufferTextureFaceARB;
-typedef void (APIENTRY * WXEGLVERTEXATTRIBDIVISORARB)(GLuint,GLuint);
-WXE_EXTERN WXEGLVERTEXATTRIBDIVISORARB weglVertexAttribDivisorARB;
typedef void (APIENTRY * WXEGLFLUSHMAPPEDBUFFERRANGE)(GLenum,GLintptr,GLsizeiptr);
WXE_EXTERN WXEGLFLUSHMAPPEDBUFFERRANGE weglFlushMappedBufferRange;
typedef void (APIENTRY * WXEGLBINDVERTEXARRAY)(GLuint);
@@ -1232,6 +1253,342 @@ typedef void (APIENTRY * WXEGLUNIFORMBLOCKBINDING)(GLuint,GLuint,GLuint);
WXE_EXTERN WXEGLUNIFORMBLOCKBINDING weglUniformBlockBinding;
typedef void (APIENTRY * WXEGLCOPYBUFFERSUBDATA)(GLenum,GLenum,GLintptr,GLintptr,GLsizeiptr);
WXE_EXTERN WXEGLCOPYBUFFERSUBDATA weglCopyBufferSubData;
+typedef void (APIENTRY * WXEGLDRAWELEMENTSBASEVERTEX)(GLenum,GLsizei,GLenum,const GLvoid *,GLint);
+WXE_EXTERN WXEGLDRAWELEMENTSBASEVERTEX weglDrawElementsBaseVertex;
+typedef void (APIENTRY * WXEGLDRAWRANGEELEMENTSBASEVERTEX)(GLenum,GLuint,GLuint,GLsizei,GLenum,const GLvoid *,GLint);
+WXE_EXTERN WXEGLDRAWRANGEELEMENTSBASEVERTEX weglDrawRangeElementsBaseVertex;
+typedef void (APIENTRY * WXEGLDRAWELEMENTSINSTANCEDBASEVERTEX)(GLenum,GLsizei,GLenum,const GLvoid *,GLsizei,GLint);
+WXE_EXTERN WXEGLDRAWELEMENTSINSTANCEDBASEVERTEX weglDrawElementsInstancedBaseVertex;
+typedef void (APIENTRY * WXEGLPROVOKINGVERTEX)(GLenum);
+WXE_EXTERN WXEGLPROVOKINGVERTEX weglProvokingVertex;
+typedef GLsync (APIENTRY * WXEGLFENCESYNC)(GLenum,GLbitfield);
+WXE_EXTERN WXEGLFENCESYNC weglFenceSync;
+typedef GLboolean (APIENTRY * WXEGLISSYNC)(GLsync);
+WXE_EXTERN WXEGLISSYNC weglIsSync;
+typedef void (APIENTRY * WXEGLDELETESYNC)(GLsync);
+WXE_EXTERN WXEGLDELETESYNC weglDeleteSync;
+typedef GLenum (APIENTRY * WXEGLCLIENTWAITSYNC)(GLsync,GLbitfield,GLuint64);
+WXE_EXTERN WXEGLCLIENTWAITSYNC weglClientWaitSync;
+typedef void (APIENTRY * WXEGLWAITSYNC)(GLsync,GLbitfield,GLuint64);
+WXE_EXTERN WXEGLWAITSYNC weglWaitSync;
+typedef void (APIENTRY * WXEGLGETINTEGER64V)(GLenum,GLint64 *);
+WXE_EXTERN WXEGLGETINTEGER64V weglGetInteger64v;
+typedef void (APIENTRY * WXEGLGETSYNCIV)(GLsync,GLenum,GLsizei,GLsizei *,GLint *);
+WXE_EXTERN WXEGLGETSYNCIV weglGetSynciv;
+typedef void (APIENTRY * WXEGLTEXIMAGE2DMULTISAMPLE)(GLenum,GLsizei,GLint,GLsizei,GLsizei,GLboolean);
+WXE_EXTERN WXEGLTEXIMAGE2DMULTISAMPLE weglTexImage2DMultisample;
+typedef void (APIENTRY * WXEGLTEXIMAGE3DMULTISAMPLE)(GLenum,GLsizei,GLint,GLsizei,GLsizei,GLsizei,GLboolean);
+WXE_EXTERN WXEGLTEXIMAGE3DMULTISAMPLE weglTexImage3DMultisample;
+typedef void (APIENTRY * WXEGLGETMULTISAMPLEFV)(GLenum,GLuint,GLfloat *);
+WXE_EXTERN WXEGLGETMULTISAMPLEFV weglGetMultisamplefv;
+typedef void (APIENTRY * WXEGLSAMPLEMASKI)(GLuint,GLbitfield);
+WXE_EXTERN WXEGLSAMPLEMASKI weglSampleMaski;
+typedef void (APIENTRY * WXEGLNAMEDSTRINGARB)(GLenum,GLint,const GLchar *,GLint,const GLchar *);
+WXE_EXTERN WXEGLNAMEDSTRINGARB weglNamedStringARB;
+typedef void (APIENTRY * WXEGLDELETENAMEDSTRINGARB)(GLint,const GLchar *);
+WXE_EXTERN WXEGLDELETENAMEDSTRINGARB weglDeleteNamedStringARB;
+typedef void (APIENTRY * WXEGLCOMPILESHADERINCLUDEARB)(GLuint,GLsizei,const GLchar **,const GLint *);
+WXE_EXTERN WXEGLCOMPILESHADERINCLUDEARB weglCompileShaderIncludeARB;
+typedef GLboolean (APIENTRY * WXEGLISNAMEDSTRINGARB)(GLint,const GLchar *);
+WXE_EXTERN WXEGLISNAMEDSTRINGARB weglIsNamedStringARB;
+typedef void (APIENTRY * WXEGLGETNAMEDSTRINGARB)(GLint,const GLchar *,GLsizei,GLint *,GLchar *);
+WXE_EXTERN WXEGLGETNAMEDSTRINGARB weglGetNamedStringARB;
+typedef void (APIENTRY * WXEGLGETNAMEDSTRINGIVARB)(GLint,const GLchar *,GLenum,GLint *);
+WXE_EXTERN WXEGLGETNAMEDSTRINGIVARB weglGetNamedStringivARB;
+typedef void (APIENTRY * WXEGLBINDFRAGDATALOCATIONINDEXED)(GLuint,GLuint,GLuint,const GLchar *);
+WXE_EXTERN WXEGLBINDFRAGDATALOCATIONINDEXED weglBindFragDataLocationIndexed;
+typedef GLint (APIENTRY * WXEGLGETFRAGDATAINDEX)(GLuint,const GLchar *);
+WXE_EXTERN WXEGLGETFRAGDATAINDEX weglGetFragDataIndex;
+typedef void (APIENTRY * WXEGLGENSAMPLERS)(GLsizei,GLuint *);
+WXE_EXTERN WXEGLGENSAMPLERS weglGenSamplers;
+typedef void (APIENTRY * WXEGLDELETESAMPLERS)(GLsizei,const GLuint *);
+WXE_EXTERN WXEGLDELETESAMPLERS weglDeleteSamplers;
+typedef GLboolean (APIENTRY * WXEGLISSAMPLER)(GLuint);
+WXE_EXTERN WXEGLISSAMPLER weglIsSampler;
+typedef void (APIENTRY * WXEGLBINDSAMPLER)(GLuint,GLuint);
+WXE_EXTERN WXEGLBINDSAMPLER weglBindSampler;
+typedef void (APIENTRY * WXEGLSAMPLERPARAMETERI)(GLuint,GLenum,GLint);
+WXE_EXTERN WXEGLSAMPLERPARAMETERI weglSamplerParameteri;
+typedef void (APIENTRY * WXEGLSAMPLERPARAMETERIV)(GLuint,GLenum,const GLint *);
+WXE_EXTERN WXEGLSAMPLERPARAMETERIV weglSamplerParameteriv;
+typedef void (APIENTRY * WXEGLSAMPLERPARAMETERF)(GLuint,GLenum,GLfloat);
+WXE_EXTERN WXEGLSAMPLERPARAMETERF weglSamplerParameterf;
+typedef void (APIENTRY * WXEGLSAMPLERPARAMETERFV)(GLuint,GLenum,const GLfloat *);
+WXE_EXTERN WXEGLSAMPLERPARAMETERFV weglSamplerParameterfv;
+typedef void (APIENTRY * WXEGLSAMPLERPARAMETERIIV)(GLuint,GLenum,const GLint *);
+WXE_EXTERN WXEGLSAMPLERPARAMETERIIV weglSamplerParameterIiv;
+typedef void (APIENTRY * WXEGLSAMPLERPARAMETERIUIV)(GLuint,GLenum,const GLuint *);
+WXE_EXTERN WXEGLSAMPLERPARAMETERIUIV weglSamplerParameterIuiv;
+typedef void (APIENTRY * WXEGLGETSAMPLERPARAMETERIV)(GLuint,GLenum,GLint *);
+WXE_EXTERN WXEGLGETSAMPLERPARAMETERIV weglGetSamplerParameteriv;
+typedef void (APIENTRY * WXEGLGETSAMPLERPARAMETERIIV)(GLuint,GLenum,GLint *);
+WXE_EXTERN WXEGLGETSAMPLERPARAMETERIIV weglGetSamplerParameterIiv;
+typedef void (APIENTRY * WXEGLGETSAMPLERPARAMETERFV)(GLuint,GLenum,GLfloat *);
+WXE_EXTERN WXEGLGETSAMPLERPARAMETERFV weglGetSamplerParameterfv;
+typedef void (APIENTRY * WXEGLGETSAMPLERPARAMETERIUIV)(GLuint,GLenum,GLuint *);
+WXE_EXTERN WXEGLGETSAMPLERPARAMETERIUIV weglGetSamplerParameterIuiv;
+typedef void (APIENTRY * WXEGLQUERYCOUNTER)(GLuint,GLenum);
+WXE_EXTERN WXEGLQUERYCOUNTER weglQueryCounter;
+typedef void (APIENTRY * WXEGLGETQUERYOBJECTI64V)(GLuint,GLenum,GLint64 *);
+WXE_EXTERN WXEGLGETQUERYOBJECTI64V weglGetQueryObjecti64v;
+typedef void (APIENTRY * WXEGLGETQUERYOBJECTUI64V)(GLuint,GLenum,GLuint64 *);
+WXE_EXTERN WXEGLGETQUERYOBJECTUI64V weglGetQueryObjectui64v;
+typedef void (APIENTRY * WXEGLDRAWARRAYSINDIRECT)(GLenum,const GLvoid *);
+WXE_EXTERN WXEGLDRAWARRAYSINDIRECT weglDrawArraysIndirect;
+typedef void (APIENTRY * WXEGLDRAWELEMENTSINDIRECT)(GLenum,GLenum,const GLvoid *);
+WXE_EXTERN WXEGLDRAWELEMENTSINDIRECT weglDrawElementsIndirect;
+typedef void (APIENTRY * WXEGLUNIFORM1D)(GLint,GLdouble);
+WXE_EXTERN WXEGLUNIFORM1D weglUniform1d;
+typedef void (APIENTRY * WXEGLUNIFORM2D)(GLint,GLdouble,GLdouble);
+WXE_EXTERN WXEGLUNIFORM2D weglUniform2d;
+typedef void (APIENTRY * WXEGLUNIFORM3D)(GLint,GLdouble,GLdouble,GLdouble);
+WXE_EXTERN WXEGLUNIFORM3D weglUniform3d;
+typedef void (APIENTRY * WXEGLUNIFORM4D)(GLint,GLdouble,GLdouble,GLdouble,GLdouble);
+WXE_EXTERN WXEGLUNIFORM4D weglUniform4d;
+typedef void (APIENTRY * WXEGLUNIFORM1DV)(GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORM1DV weglUniform1dv;
+typedef void (APIENTRY * WXEGLUNIFORM2DV)(GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORM2DV weglUniform2dv;
+typedef void (APIENTRY * WXEGLUNIFORM3DV)(GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORM3DV weglUniform3dv;
+typedef void (APIENTRY * WXEGLUNIFORM4DV)(GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORM4DV weglUniform4dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX2DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX2DV weglUniformMatrix2dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX3DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX3DV weglUniformMatrix3dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX4DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX4DV weglUniformMatrix4dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX2X3DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX2X3DV weglUniformMatrix2x3dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX2X4DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX2X4DV weglUniformMatrix2x4dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX3X2DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX3X2DV weglUniformMatrix3x2dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX3X4DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX3X4DV weglUniformMatrix3x4dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX4X2DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX4X2DV weglUniformMatrix4x2dv;
+typedef void (APIENTRY * WXEGLUNIFORMMATRIX4X3DV)(GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLUNIFORMMATRIX4X3DV weglUniformMatrix4x3dv;
+typedef void (APIENTRY * WXEGLGETUNIFORMDV)(GLuint,GLint,GLdouble *);
+WXE_EXTERN WXEGLGETUNIFORMDV weglGetUniformdv;
+typedef GLint (APIENTRY * WXEGLGETSUBROUTINEUNIFORMLOCATION)(GLuint,GLenum,const GLchar *);
+WXE_EXTERN WXEGLGETSUBROUTINEUNIFORMLOCATION weglGetSubroutineUniformLocation;
+typedef GLuint (APIENTRY * WXEGLGETSUBROUTINEINDEX)(GLuint,GLenum,const GLchar *);
+WXE_EXTERN WXEGLGETSUBROUTINEINDEX weglGetSubroutineIndex;
+typedef void (APIENTRY * WXEGLGETACTIVESUBROUTINEUNIFORMNAME)(GLuint,GLenum,GLuint,GLsizei,GLsizei *,GLchar *);
+WXE_EXTERN WXEGLGETACTIVESUBROUTINEUNIFORMNAME weglGetActiveSubroutineUniformName;
+typedef void (APIENTRY * WXEGLGETACTIVESUBROUTINENAME)(GLuint,GLenum,GLuint,GLsizei,GLsizei *,GLchar *);
+WXE_EXTERN WXEGLGETACTIVESUBROUTINENAME weglGetActiveSubroutineName;
+typedef void (APIENTRY * WXEGLUNIFORMSUBROUTINESUIV)(GLenum,GLsizei,const GLuint *);
+WXE_EXTERN WXEGLUNIFORMSUBROUTINESUIV weglUniformSubroutinesuiv;
+typedef void (APIENTRY * WXEGLGETUNIFORMSUBROUTINEUIV)(GLenum,GLint,GLuint *);
+WXE_EXTERN WXEGLGETUNIFORMSUBROUTINEUIV weglGetUniformSubroutineuiv;
+typedef void (APIENTRY * WXEGLGETPROGRAMSTAGEIV)(GLuint,GLenum,GLenum,GLint *);
+WXE_EXTERN WXEGLGETPROGRAMSTAGEIV weglGetProgramStageiv;
+typedef void (APIENTRY * WXEGLPATCHPARAMETERI)(GLenum,GLint);
+WXE_EXTERN WXEGLPATCHPARAMETERI weglPatchParameteri;
+typedef void (APIENTRY * WXEGLPATCHPARAMETERFV)(GLenum,const GLfloat *);
+WXE_EXTERN WXEGLPATCHPARAMETERFV weglPatchParameterfv;
+typedef void (APIENTRY * WXEGLBINDTRANSFORMFEEDBACK)(GLenum,GLuint);
+WXE_EXTERN WXEGLBINDTRANSFORMFEEDBACK weglBindTransformFeedback;
+typedef void (APIENTRY * WXEGLDELETETRANSFORMFEEDBACKS)(GLsizei,const GLuint *);
+WXE_EXTERN WXEGLDELETETRANSFORMFEEDBACKS weglDeleteTransformFeedbacks;
+typedef void (APIENTRY * WXEGLGENTRANSFORMFEEDBACKS)(GLsizei,GLuint *);
+WXE_EXTERN WXEGLGENTRANSFORMFEEDBACKS weglGenTransformFeedbacks;
+typedef GLboolean (APIENTRY * WXEGLISTRANSFORMFEEDBACK)(GLuint);
+WXE_EXTERN WXEGLISTRANSFORMFEEDBACK weglIsTransformFeedback;
+typedef void (APIENTRY * WXEGLPAUSETRANSFORMFEEDBACK)();
+WXE_EXTERN WXEGLPAUSETRANSFORMFEEDBACK weglPauseTransformFeedback;
+typedef void (APIENTRY * WXEGLRESUMETRANSFORMFEEDBACK)();
+WXE_EXTERN WXEGLRESUMETRANSFORMFEEDBACK weglResumeTransformFeedback;
+typedef void (APIENTRY * WXEGLDRAWTRANSFORMFEEDBACK)(GLenum,GLuint);
+WXE_EXTERN WXEGLDRAWTRANSFORMFEEDBACK weglDrawTransformFeedback;
+typedef void (APIENTRY * WXEGLDRAWTRANSFORMFEEDBACKSTREAM)(GLenum,GLuint,GLuint);
+WXE_EXTERN WXEGLDRAWTRANSFORMFEEDBACKSTREAM weglDrawTransformFeedbackStream;
+typedef void (APIENTRY * WXEGLBEGINQUERYINDEXED)(GLenum,GLuint,GLuint);
+WXE_EXTERN WXEGLBEGINQUERYINDEXED weglBeginQueryIndexed;
+typedef void (APIENTRY * WXEGLENDQUERYINDEXED)(GLenum,GLuint);
+WXE_EXTERN WXEGLENDQUERYINDEXED weglEndQueryIndexed;
+typedef void (APIENTRY * WXEGLGETQUERYINDEXEDIV)(GLenum,GLuint,GLenum,GLint *);
+WXE_EXTERN WXEGLGETQUERYINDEXEDIV weglGetQueryIndexediv;
+typedef void (APIENTRY * WXEGLRELEASESHADERCOMPILER)();
+WXE_EXTERN WXEGLRELEASESHADERCOMPILER weglReleaseShaderCompiler;
+typedef void (APIENTRY * WXEGLSHADERBINARY)(GLsizei,const GLuint *,GLenum,const GLvoid *,GLsizei);
+WXE_EXTERN WXEGLSHADERBINARY weglShaderBinary;
+typedef void (APIENTRY * WXEGLGETSHADERPRECISIONFORMAT)(GLenum,GLenum,GLint *,GLint *);
+WXE_EXTERN WXEGLGETSHADERPRECISIONFORMAT weglGetShaderPrecisionFormat;
+typedef void (APIENTRY * WXEGLDEPTHRANGEF)(GLclampf,GLclampf);
+WXE_EXTERN WXEGLDEPTHRANGEF weglDepthRangef;
+typedef void (APIENTRY * WXEGLCLEARDEPTHF)(GLclampf);
+WXE_EXTERN WXEGLCLEARDEPTHF weglClearDepthf;
+typedef void (APIENTRY * WXEGLGETPROGRAMBINARY)(GLuint,GLsizei,GLsizei *,GLenum *,GLvoid *);
+WXE_EXTERN WXEGLGETPROGRAMBINARY weglGetProgramBinary;
+typedef void (APIENTRY * WXEGLPROGRAMBINARY)(GLuint,GLenum,const GLvoid *,GLsizei);
+WXE_EXTERN WXEGLPROGRAMBINARY weglProgramBinary;
+typedef void (APIENTRY * WXEGLPROGRAMPARAMETERI)(GLuint,GLenum,GLint);
+WXE_EXTERN WXEGLPROGRAMPARAMETERI weglProgramParameteri;
+typedef void (APIENTRY * WXEGLUSEPROGRAMSTAGES)(GLuint,GLbitfield,GLuint);
+WXE_EXTERN WXEGLUSEPROGRAMSTAGES weglUseProgramStages;
+typedef void (APIENTRY * WXEGLACTIVESHADERPROGRAM)(GLuint,GLuint);
+WXE_EXTERN WXEGLACTIVESHADERPROGRAM weglActiveShaderProgram;
+typedef GLuint (APIENTRY * WXEGLCREATESHADERPROGRAMV)(GLenum,GLsizei,const GLchar **);
+WXE_EXTERN WXEGLCREATESHADERPROGRAMV weglCreateShaderProgramv;
+typedef void (APIENTRY * WXEGLBINDPROGRAMPIPELINE)(GLuint);
+WXE_EXTERN WXEGLBINDPROGRAMPIPELINE weglBindProgramPipeline;
+typedef void (APIENTRY * WXEGLDELETEPROGRAMPIPELINES)(GLsizei,const GLuint *);
+WXE_EXTERN WXEGLDELETEPROGRAMPIPELINES weglDeleteProgramPipelines;
+typedef void (APIENTRY * WXEGLGENPROGRAMPIPELINES)(GLsizei,GLuint *);
+WXE_EXTERN WXEGLGENPROGRAMPIPELINES weglGenProgramPipelines;
+typedef GLboolean (APIENTRY * WXEGLISPROGRAMPIPELINE)(GLuint);
+WXE_EXTERN WXEGLISPROGRAMPIPELINE weglIsProgramPipeline;
+typedef void (APIENTRY * WXEGLGETPROGRAMPIPELINEIV)(GLuint,GLenum,GLint *);
+WXE_EXTERN WXEGLGETPROGRAMPIPELINEIV weglGetProgramPipelineiv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1I)(GLuint,GLint,GLint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1I weglProgramUniform1i;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1IV)(GLuint,GLint,GLsizei,const GLint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1IV weglProgramUniform1iv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1F)(GLuint,GLint,GLfloat);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1F weglProgramUniform1f;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1FV)(GLuint,GLint,GLsizei,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1FV weglProgramUniform1fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1D)(GLuint,GLint,GLdouble);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1D weglProgramUniform1d;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1DV)(GLuint,GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1DV weglProgramUniform1dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1UI)(GLuint,GLint,GLuint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1UI weglProgramUniform1ui;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM1UIV)(GLuint,GLint,GLsizei,const GLuint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM1UIV weglProgramUniform1uiv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2I)(GLuint,GLint,GLint,GLint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2I weglProgramUniform2i;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2IV)(GLuint,GLint,GLsizei,const GLint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2IV weglProgramUniform2iv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2F)(GLuint,GLint,GLfloat,GLfloat);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2F weglProgramUniform2f;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2FV)(GLuint,GLint,GLsizei,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2FV weglProgramUniform2fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2D)(GLuint,GLint,GLdouble,GLdouble);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2D weglProgramUniform2d;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2DV)(GLuint,GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2DV weglProgramUniform2dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2UI)(GLuint,GLint,GLuint,GLuint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2UI weglProgramUniform2ui;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM2UIV)(GLuint,GLint,GLsizei,const GLuint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM2UIV weglProgramUniform2uiv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3I)(GLuint,GLint,GLint,GLint,GLint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3I weglProgramUniform3i;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3IV)(GLuint,GLint,GLsizei,const GLint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3IV weglProgramUniform3iv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3F)(GLuint,GLint,GLfloat,GLfloat,GLfloat);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3F weglProgramUniform3f;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3FV)(GLuint,GLint,GLsizei,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3FV weglProgramUniform3fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3D)(GLuint,GLint,GLdouble,GLdouble,GLdouble);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3D weglProgramUniform3d;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3DV)(GLuint,GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3DV weglProgramUniform3dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3UI)(GLuint,GLint,GLuint,GLuint,GLuint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3UI weglProgramUniform3ui;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM3UIV)(GLuint,GLint,GLsizei,const GLuint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM3UIV weglProgramUniform3uiv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4I)(GLuint,GLint,GLint,GLint,GLint,GLint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4I weglProgramUniform4i;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4IV)(GLuint,GLint,GLsizei,const GLint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4IV weglProgramUniform4iv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4F)(GLuint,GLint,GLfloat,GLfloat,GLfloat,GLfloat);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4F weglProgramUniform4f;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4FV)(GLuint,GLint,GLsizei,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4FV weglProgramUniform4fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4D)(GLuint,GLint,GLdouble,GLdouble,GLdouble,GLdouble);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4D weglProgramUniform4d;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4DV)(GLuint,GLint,GLsizei,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4DV weglProgramUniform4dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4UI)(GLuint,GLint,GLuint,GLuint,GLuint,GLuint);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4UI weglProgramUniform4ui;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORM4UIV)(GLuint,GLint,GLsizei,const GLuint *);
+WXE_EXTERN WXEGLPROGRAMUNIFORM4UIV weglProgramUniform4uiv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX2FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX2FV weglProgramUniformMatrix2fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX3FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX3FV weglProgramUniformMatrix3fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX4FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX4FV weglProgramUniformMatrix4fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX2DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX2DV weglProgramUniformMatrix2dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX3DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX3DV weglProgramUniformMatrix3dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX4DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX4DV weglProgramUniformMatrix4dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX2X3FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX2X3FV weglProgramUniformMatrix2x3fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX3X2FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX3X2FV weglProgramUniformMatrix3x2fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX2X4FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX2X4FV weglProgramUniformMatrix2x4fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX4X2FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX4X2FV weglProgramUniformMatrix4x2fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX3X4FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX3X4FV weglProgramUniformMatrix3x4fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX4X3FV)(GLuint,GLint,GLsizei,GLboolean,const GLfloat *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX4X3FV weglProgramUniformMatrix4x3fv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX2X3DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX2X3DV weglProgramUniformMatrix2x3dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX3X2DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX3X2DV weglProgramUniformMatrix3x2dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX2X4DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX2X4DV weglProgramUniformMatrix2x4dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX4X2DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX4X2DV weglProgramUniformMatrix4x2dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX3X4DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX3X4DV weglProgramUniformMatrix3x4dv;
+typedef void (APIENTRY * WXEGLPROGRAMUNIFORMMATRIX4X3DV)(GLuint,GLint,GLsizei,GLboolean,const GLdouble *);
+WXE_EXTERN WXEGLPROGRAMUNIFORMMATRIX4X3DV weglProgramUniformMatrix4x3dv;
+typedef void (APIENTRY * WXEGLVALIDATEPROGRAMPIPELINE)(GLuint);
+WXE_EXTERN WXEGLVALIDATEPROGRAMPIPELINE weglValidateProgramPipeline;
+typedef void (APIENTRY * WXEGLGETPROGRAMPIPELINEINFOLOG)(GLuint,GLsizei,GLsizei *,GLchar *);
+WXE_EXTERN WXEGLGETPROGRAMPIPELINEINFOLOG weglGetProgramPipelineInfoLog;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBL1DV)(GLuint,const GLdouble *);
+WXE_EXTERN WXEGLVERTEXATTRIBL1DV weglVertexAttribL1dv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBL2DV)(GLuint,const GLdouble *);
+WXE_EXTERN WXEGLVERTEXATTRIBL2DV weglVertexAttribL2dv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBL3DV)(GLuint,const GLdouble *);
+WXE_EXTERN WXEGLVERTEXATTRIBL3DV weglVertexAttribL3dv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBL4DV)(GLuint,const GLdouble *);
+WXE_EXTERN WXEGLVERTEXATTRIBL4DV weglVertexAttribL4dv;
+typedef void (APIENTRY * WXEGLVERTEXATTRIBLPOINTER)(GLuint,GLint,GLenum,GLsizei,const GLvoid *);
+WXE_EXTERN WXEGLVERTEXATTRIBLPOINTER weglVertexAttribLPointer;
+typedef void (APIENTRY * WXEGLGETVERTEXATTRIBLDV)(GLuint,GLenum,GLdouble *);
+WXE_EXTERN WXEGLGETVERTEXATTRIBLDV weglGetVertexAttribLdv;
+typedef void (APIENTRY * WXEGLVIEWPORTARRAYV)(GLuint,GLsizei,const GLfloat *);
+WXE_EXTERN WXEGLVIEWPORTARRAYV weglViewportArrayv;
+typedef void (APIENTRY * WXEGLVIEWPORTINDEXEDF)(GLuint,GLfloat,GLfloat,GLfloat,GLfloat);
+WXE_EXTERN WXEGLVIEWPORTINDEXEDF weglViewportIndexedf;
+typedef void (APIENTRY * WXEGLVIEWPORTINDEXEDFV)(GLuint,const GLfloat *);
+WXE_EXTERN WXEGLVIEWPORTINDEXEDFV weglViewportIndexedfv;
+typedef void (APIENTRY * WXEGLSCISSORARRAYV)(GLuint,GLsizei,const GLint *);
+WXE_EXTERN WXEGLSCISSORARRAYV weglScissorArrayv;
+typedef void (APIENTRY * WXEGLSCISSORINDEXED)(GLuint,GLint,GLint,GLsizei,GLsizei);
+WXE_EXTERN WXEGLSCISSORINDEXED weglScissorIndexed;
+typedef void (APIENTRY * WXEGLSCISSORINDEXEDV)(GLuint,const GLint *);
+WXE_EXTERN WXEGLSCISSORINDEXEDV weglScissorIndexedv;
+typedef void (APIENTRY * WXEGLDEPTHRANGEARRAYV)(GLuint,GLsizei,const GLclampd *);
+WXE_EXTERN WXEGLDEPTHRANGEARRAYV weglDepthRangeArrayv;
+typedef void (APIENTRY * WXEGLDEPTHRANGEINDEXED)(GLuint,GLclampd,GLclampd);
+WXE_EXTERN WXEGLDEPTHRANGEINDEXED weglDepthRangeIndexed;
+typedef void (APIENTRY * WXEGLGETFLOATI_V)(GLenum,GLuint,GLfloat *);
+WXE_EXTERN WXEGLGETFLOATI_V weglGetFloati_v;
+typedef void (APIENTRY * WXEGLGETDOUBLEI_V)(GLenum,GLuint,GLdouble *);
+WXE_EXTERN WXEGLGETDOUBLEI_V weglGetDoublei_v;
+typedef void (APIENTRY * WXEGLDEBUGMESSAGECONTROLARB)(GLenum,GLenum,GLenum,GLsizei,const GLuint *,GLboolean);
+WXE_EXTERN WXEGLDEBUGMESSAGECONTROLARB weglDebugMessageControlARB;
+typedef void (APIENTRY * WXEGLDEBUGMESSAGEINSERTARB)(GLenum,GLenum,GLuint,GLenum,GLsizei,const GLchar *);
+WXE_EXTERN WXEGLDEBUGMESSAGEINSERTARB weglDebugMessageInsertARB;
+typedef GLuint (APIENTRY * WXEGLGETDEBUGMESSAGELOGARB)(GLuint,GLsizei,GLenum *,GLenum *,GLuint *,GLenum *,GLsizei *,GLchar *);
+WXE_EXTERN WXEGLGETDEBUGMESSAGELOGARB weglGetDebugMessageLogARB;
+typedef GLenum (APIENTRY * WXEGLGETGRAPHICSRESETSTATUSARB)();
+WXE_EXTERN WXEGLGETGRAPHICSRESETSTATUSARB weglGetGraphicsResetStatusARB;
typedef void (APIENTRY * WXEGLRESIZEBUFFERSMESA)();
WXE_EXTERN WXEGLRESIZEBUFFERSMESA weglResizeBuffersMESA;
typedef void (APIENTRY * WXEGLWINDOWPOS4DVMESA)(const GLdouble *);
diff --git a/lib/wx/c_src/gen/gl_finit.h b/lib/wx/c_src/gen/gl_finit.h
index ef29f05c4d..583e36faf7 100644
--- a/lib/wx/c_src/gen/gl_finit.h
+++ b/lib/wx/c_src/gen/gl_finit.h
@@ -18,11 +18,7 @@
*/
/***** This file is generated do not edit ****/
-static struct {
- const char * name;
- const char * alt;
- void * func;
-} gl_fns[] =
+gl_fns_t gl_fns[] =
{
{"glAccum", NULL, &weglAccum},
{"glAlphaFunc", NULL, &weglAlphaFunc},
@@ -394,7 +390,7 @@ static struct {
{"glBufferData", "glBufferDataARB", &weglBufferData},
{"glBufferSubData", "glBufferSubDataARB", &weglBufferSubData},
{"glGetBufferSubData", "glGetBufferSubDataARB", &weglGetBufferSubData},
- {"glGetBufferParameteriv", "glGetBufferParameterivARB", &weglGetBufferParameteriv},
+ {"glGetBufferParameteriv", NULL, &weglGetBufferParameteriv},
{"glBlendEquationSeparate", "glBlendEquationSeparateEXT", &weglBlendEquationSeparate},
{"glDrawBuffers", "glDrawBuffersARB", &weglDrawBuffers},
{"glStencilOpSeparate", "glStencilOpSeparateATI", &weglStencilOpSeparate},
@@ -498,6 +494,18 @@ static struct {
{"glVertexAttribIPointer", NULL, &weglVertexAttribIPointer},
{"glGetVertexAttribIiv", NULL, &weglGetVertexAttribIiv},
{"glGetVertexAttribIuiv", NULL, &weglGetVertexAttribIuiv},
+ {"glVertexAttribI1iv", NULL, &weglVertexAttribI1iv},
+ {"glVertexAttribI2iv", NULL, &weglVertexAttribI2iv},
+ {"glVertexAttribI3iv", NULL, &weglVertexAttribI3iv},
+ {"glVertexAttribI4iv", NULL, &weglVertexAttribI4iv},
+ {"glVertexAttribI1uiv", NULL, &weglVertexAttribI1uiv},
+ {"glVertexAttribI2uiv", NULL, &weglVertexAttribI2uiv},
+ {"glVertexAttribI3uiv", NULL, &weglVertexAttribI3uiv},
+ {"glVertexAttribI4uiv", NULL, &weglVertexAttribI4uiv},
+ {"glVertexAttribI4bv", NULL, &weglVertexAttribI4bv},
+ {"glVertexAttribI4sv", NULL, &weglVertexAttribI4sv},
+ {"glVertexAttribI4ubv", NULL, &weglVertexAttribI4ubv},
+ {"glVertexAttribI4usv", NULL, &weglVertexAttribI4usv},
{"glGetUniformuiv", NULL, &weglGetUniformuiv},
{"glBindFragDataLocation", NULL, &weglBindFragDataLocation},
{"glGetFragDataLocation", NULL, &weglGetFragDataLocation},
@@ -518,22 +526,19 @@ static struct {
{"glClearBufferfv", NULL, &weglClearBufferfv},
{"glClearBufferfi", NULL, &weglClearBufferfi},
{"glGetStringi", NULL, &weglGetStringi},
- {"glVertexAttribI1iv", NULL, &weglVertexAttribI1iv},
- {"glVertexAttribI2iv", NULL, &weglVertexAttribI2iv},
- {"glVertexAttribI3iv", NULL, &weglVertexAttribI3iv},
- {"glVertexAttribI4iv", NULL, &weglVertexAttribI4iv},
- {"glVertexAttribI1uiv", NULL, &weglVertexAttribI1uiv},
- {"glVertexAttribI2uiv", NULL, &weglVertexAttribI2uiv},
- {"glVertexAttribI3uiv", NULL, &weglVertexAttribI3uiv},
- {"glVertexAttribI4uiv", NULL, &weglVertexAttribI4uiv},
- {"glVertexAttribI4bv", NULL, &weglVertexAttribI4bv},
- {"glVertexAttribI4sv", NULL, &weglVertexAttribI4sv},
- {"glVertexAttribI4ubv", NULL, &weglVertexAttribI4ubv},
- {"glVertexAttribI4usv", NULL, &weglVertexAttribI4usv},
{"glDrawArraysInstanced", "glDrawArraysInstancedARB", &weglDrawArraysInstanced},
{"glDrawElementsInstanced", "glDrawElementsInstancedARB", &weglDrawElementsInstanced},
{"glTexBuffer", "glTexBufferARB", &weglTexBuffer},
{"glPrimitiveRestartIndex", NULL, &weglPrimitiveRestartIndex},
+ {"glGetInteger64i_v", NULL, &weglGetInteger64i_v},
+ {"glGetBufferParameteri64v", NULL, &weglGetBufferParameteri64v},
+ {"glFramebufferTexture", "glFramebufferTextureARB", &weglFramebufferTexture},
+ {"glVertexAttribDivisor", "glVertexAttribDivisorARB", &weglVertexAttribDivisor},
+ {"glMinSampleShading", "glMinSampleShadingARB", &weglMinSampleShading},
+ {"glBlendEquationi", "glBlendEquationiARB", &weglBlendEquationi},
+ {"glBlendEquationSeparatei", "glBlendEquationSeparateiARB", &weglBlendEquationSeparatei},
+ {"glBlendFunci", "glBlendFunciARB", &weglBlendFunci},
+ {"glBlendFuncSeparatei", "glBlendFuncSeparateiARB", &weglBlendFuncSeparatei},
{"glLoadTransposeMatrixfARB", NULL, &weglLoadTransposeMatrixfARB},
{"glLoadTransposeMatrixdARB", NULL, &weglLoadTransposeMatrixdARB},
{"glMultTransposeMatrixfARB", NULL, &weglMultTransposeMatrixfARB},
@@ -568,6 +573,7 @@ static struct {
{"glGetProgramLocalParameterdvARB", NULL, &weglGetProgramLocalParameterdvARB},
{"glGetProgramLocalParameterfvARB", NULL, &weglGetProgramLocalParameterfvARB},
{"glGetProgramStringARB", NULL, &weglGetProgramStringARB},
+ {"glGetBufferParameterivARB", NULL, &weglGetBufferParameterivARB},
{"glDeleteObjectARB", NULL, &weglDeleteObjectARB},
{"glGetHandleARB", NULL, &weglGetHandleARB},
{"glDetachObjectARB", NULL, &weglDetachObjectARB},
@@ -611,10 +617,7 @@ static struct {
{"glBlitFramebuffer", "glBlitFramebufferEXT", &weglBlitFramebuffer},
{"glRenderbufferStorageMultisample", "glRenderbufferStorageMultisampleEXT", &weglRenderbufferStorageMultisample},
{"glFramebufferTextureLayer", "glFramebufferTextureLayerARB", &weglFramebufferTextureLayer},
- {"glProgramParameteriARB", NULL, &weglProgramParameteriARB},
- {"glFramebufferTextureARB", NULL, &weglFramebufferTextureARB},
{"glFramebufferTextureFaceARB", NULL, &weglFramebufferTextureFaceARB},
- {"glVertexAttribDivisorARB", NULL, &weglVertexAttribDivisorARB},
{"glFlushMappedBufferRange", NULL, &weglFlushMappedBufferRange},
{"glBindVertexArray", NULL, &weglBindVertexArray},
{"glDeleteVertexArrays", NULL, &weglDeleteVertexArrays},
@@ -628,6 +631,174 @@ static struct {
{"glGetActiveUniformBlockName", NULL, &weglGetActiveUniformBlockName},
{"glUniformBlockBinding", NULL, &weglUniformBlockBinding},
{"glCopyBufferSubData", NULL, &weglCopyBufferSubData},
+ {"glDrawElementsBaseVertex", NULL, &weglDrawElementsBaseVertex},
+ {"glDrawRangeElementsBaseVertex", NULL, &weglDrawRangeElementsBaseVertex},
+ {"glDrawElementsInstancedBaseVertex", NULL, &weglDrawElementsInstancedBaseVertex},
+ {"glProvokingVertex", NULL, &weglProvokingVertex},
+ {"glFenceSync", NULL, &weglFenceSync},
+ {"glIsSync", NULL, &weglIsSync},
+ {"glDeleteSync", NULL, &weglDeleteSync},
+ {"glClientWaitSync", NULL, &weglClientWaitSync},
+ {"glWaitSync", NULL, &weglWaitSync},
+ {"glGetInteger64v", NULL, &weglGetInteger64v},
+ {"glGetSynciv", NULL, &weglGetSynciv},
+ {"glTexImage2DMultisample", NULL, &weglTexImage2DMultisample},
+ {"glTexImage3DMultisample", NULL, &weglTexImage3DMultisample},
+ {"glGetMultisamplefv", NULL, &weglGetMultisamplefv},
+ {"glSampleMaski", NULL, &weglSampleMaski},
+ {"glNamedStringARB", NULL, &weglNamedStringARB},
+ {"glDeleteNamedStringARB", NULL, &weglDeleteNamedStringARB},
+ {"glCompileShaderIncludeARB", NULL, &weglCompileShaderIncludeARB},
+ {"glIsNamedStringARB", NULL, &weglIsNamedStringARB},
+ {"glGetNamedStringARB", NULL, &weglGetNamedStringARB},
+ {"glGetNamedStringivARB", NULL, &weglGetNamedStringivARB},
+ {"glBindFragDataLocationIndexed", NULL, &weglBindFragDataLocationIndexed},
+ {"glGetFragDataIndex", NULL, &weglGetFragDataIndex},
+ {"glGenSamplers", NULL, &weglGenSamplers},
+ {"glDeleteSamplers", NULL, &weglDeleteSamplers},
+ {"glIsSampler", NULL, &weglIsSampler},
+ {"glBindSampler", NULL, &weglBindSampler},
+ {"glSamplerParameteri", NULL, &weglSamplerParameteri},
+ {"glSamplerParameteriv", NULL, &weglSamplerParameteriv},
+ {"glSamplerParameterf", NULL, &weglSamplerParameterf},
+ {"glSamplerParameterfv", NULL, &weglSamplerParameterfv},
+ {"glSamplerParameterIiv", NULL, &weglSamplerParameterIiv},
+ {"glSamplerParameterIuiv", NULL, &weglSamplerParameterIuiv},
+ {"glGetSamplerParameteriv", NULL, &weglGetSamplerParameteriv},
+ {"glGetSamplerParameterIiv", NULL, &weglGetSamplerParameterIiv},
+ {"glGetSamplerParameterfv", NULL, &weglGetSamplerParameterfv},
+ {"glGetSamplerParameterIuiv", NULL, &weglGetSamplerParameterIuiv},
+ {"glQueryCounter", NULL, &weglQueryCounter},
+ {"glGetQueryObjecti64v", NULL, &weglGetQueryObjecti64v},
+ {"glGetQueryObjectui64v", NULL, &weglGetQueryObjectui64v},
+ {"glDrawArraysIndirect", NULL, &weglDrawArraysIndirect},
+ {"glDrawElementsIndirect", NULL, &weglDrawElementsIndirect},
+ {"glUniform1d", NULL, &weglUniform1d},
+ {"glUniform2d", NULL, &weglUniform2d},
+ {"glUniform3d", NULL, &weglUniform3d},
+ {"glUniform4d", NULL, &weglUniform4d},
+ {"glUniform1dv", NULL, &weglUniform1dv},
+ {"glUniform2dv", NULL, &weglUniform2dv},
+ {"glUniform3dv", NULL, &weglUniform3dv},
+ {"glUniform4dv", NULL, &weglUniform4dv},
+ {"glUniformMatrix2dv", NULL, &weglUniformMatrix2dv},
+ {"glUniformMatrix3dv", NULL, &weglUniformMatrix3dv},
+ {"glUniformMatrix4dv", NULL, &weglUniformMatrix4dv},
+ {"glUniformMatrix2x3dv", NULL, &weglUniformMatrix2x3dv},
+ {"glUniformMatrix2x4dv", NULL, &weglUniformMatrix2x4dv},
+ {"glUniformMatrix3x2dv", NULL, &weglUniformMatrix3x2dv},
+ {"glUniformMatrix3x4dv", NULL, &weglUniformMatrix3x4dv},
+ {"glUniformMatrix4x2dv", NULL, &weglUniformMatrix4x2dv},
+ {"glUniformMatrix4x3dv", NULL, &weglUniformMatrix4x3dv},
+ {"glGetUniformdv", NULL, &weglGetUniformdv},
+ {"glGetSubroutineUniformLocation", NULL, &weglGetSubroutineUniformLocation},
+ {"glGetSubroutineIndex", NULL, &weglGetSubroutineIndex},
+ {"glGetActiveSubroutineUniformName", NULL, &weglGetActiveSubroutineUniformName},
+ {"glGetActiveSubroutineName", NULL, &weglGetActiveSubroutineName},
+ {"glUniformSubroutinesuiv", NULL, &weglUniformSubroutinesuiv},
+ {"glGetUniformSubroutineuiv", NULL, &weglGetUniformSubroutineuiv},
+ {"glGetProgramStageiv", NULL, &weglGetProgramStageiv},
+ {"glPatchParameteri", NULL, &weglPatchParameteri},
+ {"glPatchParameterfv", NULL, &weglPatchParameterfv},
+ {"glBindTransformFeedback", NULL, &weglBindTransformFeedback},
+ {"glDeleteTransformFeedbacks", NULL, &weglDeleteTransformFeedbacks},
+ {"glGenTransformFeedbacks", NULL, &weglGenTransformFeedbacks},
+ {"glIsTransformFeedback", NULL, &weglIsTransformFeedback},
+ {"glPauseTransformFeedback", NULL, &weglPauseTransformFeedback},
+ {"glResumeTransformFeedback", NULL, &weglResumeTransformFeedback},
+ {"glDrawTransformFeedback", NULL, &weglDrawTransformFeedback},
+ {"glDrawTransformFeedbackStream", NULL, &weglDrawTransformFeedbackStream},
+ {"glBeginQueryIndexed", NULL, &weglBeginQueryIndexed},
+ {"glEndQueryIndexed", NULL, &weglEndQueryIndexed},
+ {"glGetQueryIndexediv", NULL, &weglGetQueryIndexediv},
+ {"glReleaseShaderCompiler", NULL, &weglReleaseShaderCompiler},
+ {"glShaderBinary", NULL, &weglShaderBinary},
+ {"glGetShaderPrecisionFormat", NULL, &weglGetShaderPrecisionFormat},
+ {"glDepthRangef", NULL, &weglDepthRangef},
+ {"glClearDepthf", NULL, &weglClearDepthf},
+ {"glGetProgramBinary", NULL, &weglGetProgramBinary},
+ {"glProgramBinary", NULL, &weglProgramBinary},
+ {"glProgramParameteri", "glProgramParameteriARB", &weglProgramParameteri},
+ {"glUseProgramStages", NULL, &weglUseProgramStages},
+ {"glActiveShaderProgram", NULL, &weglActiveShaderProgram},
+ {"glCreateShaderProgramv", NULL, &weglCreateShaderProgramv},
+ {"glBindProgramPipeline", NULL, &weglBindProgramPipeline},
+ {"glDeleteProgramPipelines", NULL, &weglDeleteProgramPipelines},
+ {"glGenProgramPipelines", NULL, &weglGenProgramPipelines},
+ {"glIsProgramPipeline", NULL, &weglIsProgramPipeline},
+ {"glGetProgramPipelineiv", NULL, &weglGetProgramPipelineiv},
+ {"glProgramUniform1i", NULL, &weglProgramUniform1i},
+ {"glProgramUniform1iv", NULL, &weglProgramUniform1iv},
+ {"glProgramUniform1f", NULL, &weglProgramUniform1f},
+ {"glProgramUniform1fv", NULL, &weglProgramUniform1fv},
+ {"glProgramUniform1d", NULL, &weglProgramUniform1d},
+ {"glProgramUniform1dv", NULL, &weglProgramUniform1dv},
+ {"glProgramUniform1ui", NULL, &weglProgramUniform1ui},
+ {"glProgramUniform1uiv", NULL, &weglProgramUniform1uiv},
+ {"glProgramUniform2i", NULL, &weglProgramUniform2i},
+ {"glProgramUniform2iv", NULL, &weglProgramUniform2iv},
+ {"glProgramUniform2f", NULL, &weglProgramUniform2f},
+ {"glProgramUniform2fv", NULL, &weglProgramUniform2fv},
+ {"glProgramUniform2d", NULL, &weglProgramUniform2d},
+ {"glProgramUniform2dv", NULL, &weglProgramUniform2dv},
+ {"glProgramUniform2ui", NULL, &weglProgramUniform2ui},
+ {"glProgramUniform2uiv", NULL, &weglProgramUniform2uiv},
+ {"glProgramUniform3i", NULL, &weglProgramUniform3i},
+ {"glProgramUniform3iv", NULL, &weglProgramUniform3iv},
+ {"glProgramUniform3f", NULL, &weglProgramUniform3f},
+ {"glProgramUniform3fv", NULL, &weglProgramUniform3fv},
+ {"glProgramUniform3d", NULL, &weglProgramUniform3d},
+ {"glProgramUniform3dv", NULL, &weglProgramUniform3dv},
+ {"glProgramUniform3ui", NULL, &weglProgramUniform3ui},
+ {"glProgramUniform3uiv", NULL, &weglProgramUniform3uiv},
+ {"glProgramUniform4i", NULL, &weglProgramUniform4i},
+ {"glProgramUniform4iv", NULL, &weglProgramUniform4iv},
+ {"glProgramUniform4f", NULL, &weglProgramUniform4f},
+ {"glProgramUniform4fv", NULL, &weglProgramUniform4fv},
+ {"glProgramUniform4d", NULL, &weglProgramUniform4d},
+ {"glProgramUniform4dv", NULL, &weglProgramUniform4dv},
+ {"glProgramUniform4ui", NULL, &weglProgramUniform4ui},
+ {"glProgramUniform4uiv", NULL, &weglProgramUniform4uiv},
+ {"glProgramUniformMatrix2fv", NULL, &weglProgramUniformMatrix2fv},
+ {"glProgramUniformMatrix3fv", NULL, &weglProgramUniformMatrix3fv},
+ {"glProgramUniformMatrix4fv", NULL, &weglProgramUniformMatrix4fv},
+ {"glProgramUniformMatrix2dv", NULL, &weglProgramUniformMatrix2dv},
+ {"glProgramUniformMatrix3dv", NULL, &weglProgramUniformMatrix3dv},
+ {"glProgramUniformMatrix4dv", NULL, &weglProgramUniformMatrix4dv},
+ {"glProgramUniformMatrix2x3fv", NULL, &weglProgramUniformMatrix2x3fv},
+ {"glProgramUniformMatrix3x2fv", NULL, &weglProgramUniformMatrix3x2fv},
+ {"glProgramUniformMatrix2x4fv", NULL, &weglProgramUniformMatrix2x4fv},
+ {"glProgramUniformMatrix4x2fv", NULL, &weglProgramUniformMatrix4x2fv},
+ {"glProgramUniformMatrix3x4fv", NULL, &weglProgramUniformMatrix3x4fv},
+ {"glProgramUniformMatrix4x3fv", NULL, &weglProgramUniformMatrix4x3fv},
+ {"glProgramUniformMatrix2x3dv", NULL, &weglProgramUniformMatrix2x3dv},
+ {"glProgramUniformMatrix3x2dv", NULL, &weglProgramUniformMatrix3x2dv},
+ {"glProgramUniformMatrix2x4dv", NULL, &weglProgramUniformMatrix2x4dv},
+ {"glProgramUniformMatrix4x2dv", NULL, &weglProgramUniformMatrix4x2dv},
+ {"glProgramUniformMatrix3x4dv", NULL, &weglProgramUniformMatrix3x4dv},
+ {"glProgramUniformMatrix4x3dv", NULL, &weglProgramUniformMatrix4x3dv},
+ {"glValidateProgramPipeline", NULL, &weglValidateProgramPipeline},
+ {"glGetProgramPipelineInfoLog", NULL, &weglGetProgramPipelineInfoLog},
+ {"glVertexAttribL1dv", NULL, &weglVertexAttribL1dv},
+ {"glVertexAttribL2dv", NULL, &weglVertexAttribL2dv},
+ {"glVertexAttribL3dv", NULL, &weglVertexAttribL3dv},
+ {"glVertexAttribL4dv", NULL, &weglVertexAttribL4dv},
+ {"glVertexAttribLPointer", NULL, &weglVertexAttribLPointer},
+ {"glGetVertexAttribLdv", NULL, &weglGetVertexAttribLdv},
+ {"glViewportArrayv", NULL, &weglViewportArrayv},
+ {"glViewportIndexedf", NULL, &weglViewportIndexedf},
+ {"glViewportIndexedfv", NULL, &weglViewportIndexedfv},
+ {"glScissorArrayv", NULL, &weglScissorArrayv},
+ {"glScissorIndexed", NULL, &weglScissorIndexed},
+ {"glScissorIndexedv", NULL, &weglScissorIndexedv},
+ {"glDepthRangeArrayv", NULL, &weglDepthRangeArrayv},
+ {"glDepthRangeIndexed", NULL, &weglDepthRangeIndexed},
+ {"glGetFloati_v", NULL, &weglGetFloati_v},
+ {"glGetDoublei_v", NULL, &weglGetDoublei_v},
+ {"glDebugMessageControlARB", NULL, &weglDebugMessageControlARB},
+ {"glDebugMessageInsertARB", NULL, &weglDebugMessageInsertARB},
+ {"glGetDebugMessageLogARB", NULL, &weglGetDebugMessageLogARB},
+ {"glGetGraphicsResetStatusARB", NULL, &weglGetGraphicsResetStatusARB},
{"glResizeBuffersMESA", NULL, &weglResizeBuffersMESA},
{"glWindowPos4dvMESA", NULL, &weglWindowPos4dvMESA},
{"glWindowPos4fvMESA", NULL, &weglWindowPos4fvMESA},
diff --git a/lib/wx/c_src/gen/gl_funcs.cpp b/lib/wx/c_src/gen/gl_funcs.cpp
index 95d3c23b23..30542a0f02 100644
--- a/lib/wx/c_src/gen/gl_funcs.cpp
+++ b/lib/wx/c_src/gen/gl_funcs.cpp
@@ -20,37 +20,17 @@
#include <stdio.h>
#include <string.h>
-#include "../wxe_impl.h"
-#include "../wxe_gl.h"
+#include "../egl_impl.h"
#include "gl_fdefs.h"
-int gl_error_op;
-void gl_dispatch(int op, char *bp,ErlDrvTermData caller,WXEBinRef *bins[]){
- gl_error_op = op;
- if(caller != gl_active) {
- wxGLCanvas * current = glc[caller];
- if(current) { gl_active = caller; current->SetCurrent();}
- else {
- ErlDrvTermData rt[] = // Error msg
- {ERL_DRV_ATOM, driver_mk_atom((char *) "_wxe_error_"),
- ERL_DRV_INT, op,
- ERL_DRV_ATOM, driver_mk_atom((char *) "no_gl_context"),
- ERL_DRV_TUPLE,3};
- driver_send_term(WXE_DRV_PORT,caller,rt,8);
- return ;
- }
- };
+extern gl_fns_t gl_fns[];
+void egl_dispatch(int op, char *bp, ErlDrvPort port, ErlDrvTermData caller, char *bins[], int bins_sz[]){
+ try {
switch(op)
{
case 5000:
- wxe_tess_impl(bp, caller);
- break;
- case WXE_BIN_INCR:
- driver_binary_inc_refc(bins[0]->bin);
- break;
- case WXE_BIN_DECR:
- driver_binary_dec_refc(bins[0]->bin);
+ erl_tess_impl(bp, port, caller);
break;
case 5010: { // gluBuild1DMipmapLevels
GLenum *target = (GLenum *) bp; bp += 4;
@@ -61,14 +41,13 @@ case 5010: { // gluBuild1DMipmapLevels
GLint *level = (GLint *) bp; bp += 4;
GLint *base = (GLint *) bp; bp += 4;
GLint *max = (GLint *) bp; bp += 4;
- void *data = (void *) bins[0]->base;
+ void *data = (void *) bins[0];
GLint result = wegluBuild1DMipmapLevels(*target,*internalFormat,*width,*format,*type,*level,*base,*max,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5011: { // gluBuild1DMipmaps
GLenum *target = (GLenum *) bp; bp += 4;
@@ -76,14 +55,13 @@ case 5011: { // gluBuild1DMipmaps
GLsizei *width = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- void *data = (void *) bins[0]->base;
+ void *data = (void *) bins[0];
GLint result = wegluBuild1DMipmaps(*target,*internalFormat,*width,*format,*type,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5012: { // gluBuild2DMipmapLevels
GLenum *target = (GLenum *) bp; bp += 4;
@@ -95,14 +73,13 @@ case 5012: { // gluBuild2DMipmapLevels
GLint *level = (GLint *) bp; bp += 4;
GLint *base = (GLint *) bp; bp += 4;
GLint *max = (GLint *) bp; bp += 4;
- void *data = (void *) bins[0]->base;
+ void *data = (void *) bins[0];
GLint result = wegluBuild2DMipmapLevels(*target,*internalFormat,*width,*height,*format,*type,*level,*base,*max,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5013: { // gluBuild2DMipmaps
GLenum *target = (GLenum *) bp; bp += 4;
@@ -111,14 +88,13 @@ case 5013: { // gluBuild2DMipmaps
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- void *data = (void *) bins[0]->base;
+ void *data = (void *) bins[0];
GLint result = wegluBuild2DMipmaps(*target,*internalFormat,*width,*height,*format,*type,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5014: { // gluBuild3DMipmapLevels
GLenum *target = (GLenum *) bp; bp += 4;
@@ -131,14 +107,13 @@ case 5014: { // gluBuild3DMipmapLevels
GLint *level = (GLint *) bp; bp += 4;
GLint *base = (GLint *) bp; bp += 4;
GLint *max = (GLint *) bp; bp += 4;
- void *data = (void *) bins[0]->base;
+ void *data = (void *) bins[0];
GLint result = wegluBuild3DMipmapLevels(*target,*internalFormat,*width,*height,*depth,*format,*type,*level,*base,*max,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5015: { // gluBuild3DMipmaps
GLenum *target = (GLenum *) bp; bp += 4;
@@ -148,27 +123,25 @@ case 5015: { // gluBuild3DMipmaps
GLsizei *depth = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- void *data = (void *) bins[0]->base;
+ void *data = (void *) bins[0];
GLint result = wegluBuild3DMipmaps(*target,*internalFormat,*width,*height,*depth,*format,*type,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5016: { // gluCheckExtension
- int * extNameLen = (int *) bp; bp += 4;
- GLubyte * extName = (GLubyte *) bp; bp += (8-((*extNameLen*1+4)%8))%8;
- int * extStringLen = (int *) bp; bp += 4;
- GLubyte * extString = (GLubyte *) bp; bp += (8-((*extStringLen*1+4)%8))%8;
+ GLubyte *extName = (GLubyte *) bp;
+ int extNameLen[1] = {strlen((char *)extName)}; bp += extNameLen[0]+1+((8-((1+extNameLen[0]+0)%8))%8);
+ GLubyte *extString = (GLubyte *) bp;
+ int extStringLen[1] = {strlen((char *)extString)}; bp += extStringLen[0]+1+((8-((1+extStringLen[0]+0)%8))%8);
GLboolean result = wegluCheckExtension(extName,extString);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5017: { // gluCylinder
GLUquadric * quad = (GLUquadric *) * (GLuint64EXT *) bp; bp += 8;
@@ -195,21 +168,19 @@ case 5020: { // gluErrorString
GLenum *error = (GLenum *) bp; bp += 4;
const GLubyte * result = wegluErrorString(*error);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) result; rt[AP++] = strlen((char *) result);
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5021: { // gluGetString
GLenum *name = (GLenum *) bp; bp += 4;
const GLubyte * result = wegluGetString(*name);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) result; rt[AP++] = strlen((char *) result);
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5022: { // gluLookAt
GLdouble *eyeX = (GLdouble *) bp; bp += 8;
@@ -226,11 +197,10 @@ case 5022: { // gluLookAt
case 5023: { // gluNewQuadric
GLUquadric * result = wegluNewQuadric();
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5024: { // gluOrtho2D
GLdouble *left = (GLdouble *) bp; bp += 8;
@@ -276,15 +246,14 @@ case 5028: { // gluProject
GLdouble winZ[1] = {0.0};
GLint result = wegluProject(*objX,*objY,*objZ,model,proj,view,winX,winY,winZ);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) winX;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) winY;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) winZ;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5029: { // gluQuadricDrawStyle
GLUquadric * quad = (GLUquadric *) * (GLuint64EXT *) bp; bp += 8;
@@ -311,18 +280,17 @@ case 5033: { // gluScaleImage
GLsizei *wIn = (GLsizei *) bp; bp += 4;
GLsizei *hIn = (GLsizei *) bp; bp += 4;
GLenum *typeIn = (GLenum *) bp; bp += 4;
- void *dataIn = (void *) bins[0]->base;
+ void *dataIn = (void *) bins[0];
GLsizei *wOut = (GLsizei *) bp; bp += 4;
GLsizei *hOut = (GLsizei *) bp; bp += 4;
GLenum *typeOut = (GLenum *) bp; bp += 4;
- GLvoid *dataOut = (GLvoid *) bins[1]->base;
+ GLvoid *dataOut = (GLvoid *) bins[1];
GLint result = wegluScaleImage(*format,*wIn,*hIn,*typeIn,dataIn,*wOut,*hOut,*typeOut,dataOut);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5034: { // gluSphere
GLUquadric * quad = (GLUquadric *) * (GLuint64EXT *) bp; bp += 8;
@@ -343,15 +311,14 @@ case 5035: { // gluUnProject
GLdouble objZ[1] = {0.0};
GLint result = wegluUnProject(*winX,*winY,*winZ,model,proj,view,objX,objY,objZ);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) objX;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) objY;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) objZ;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5036: { // gluUnProject4
GLdouble *winX = (GLdouble *) bp; bp += 8;
@@ -369,7 +336,7 @@ case 5036: { // gluUnProject4
GLdouble objW[1] = {0.0};
GLint result = wegluUnProject4(*winX,*winY,*winZ,*clipW,model,proj,view,*nearVal,*farVal,objX,objY,objZ,objW);
int AP = 0; ErlDrvTermData rt[16];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) objX;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) objY;
@@ -377,8 +344,7 @@ case 5036: { // gluUnProject4
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) objW;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 5;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 16 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,16);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5037: { // glAccum
GLenum *op = (GLenum *) bp; bp += 4;
@@ -398,15 +364,14 @@ case 5039: { // glAreTexturesResident
GLboolean result = weglAreTexturesResident(*texturesLen,textures,residences);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(11 + (*texturesLen)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
for(int i=0; i < *texturesLen; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) residences[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*texturesLen)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 11 + (*texturesLen)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,11 + (*texturesLen)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(residences);
}; break;
@@ -440,7 +405,7 @@ case 5044: { // glBitmap
GLfloat *yorig = (GLfloat *) bp; bp += 4;
GLfloat *xmove = (GLfloat *) bp; bp += 4;
GLfloat *ymove = (GLfloat *) bp; bp += 4;
- GLubyte *bitmap = (GLubyte *) bins[0]->base;
+ GLubyte *bitmap = (GLubyte *) bins[0];
weglBitmap(*width,*height,*xorig,*yorig,*xmove,*ymove,bitmap);
}; break;
case 5045: { // glBlendFunc
@@ -580,7 +545,7 @@ case 5074: { // glColorPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglColorPointer(*size,*type,*stride,pointer);
}; break;
case 5075: { // glCopyPixels
@@ -688,7 +653,7 @@ case 5091: { // glDrawElements
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *indices = (GLvoid *) bins[0]->base;
+ GLvoid *indices = (GLvoid *) bins[0];
weglDrawElements(*mode,*count,*type,indices);
}; break;
case 5092: { // glDrawPixels
@@ -704,7 +669,7 @@ case 5093: { // glDrawPixels
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglDrawPixels(*width,*height,*format,*type,pixels);
}; break;
case 5094: { // glEdgeFlagv
@@ -718,7 +683,7 @@ case 5095: { // glEdgeFlagPointer
}; break;
case 5096: { // glEdgeFlagPointer
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglEdgeFlagPointer(*stride,pointer);
}; break;
case 5097: { // glEnable
@@ -777,13 +742,13 @@ case 5108: { // glEvalPoint2
case 5109: { // glFeedbackBuffer
GLsizei *size = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLfloat *buffer = (GLfloat *) bins[0]->base;
+ GLfloat *buffer = (GLfloat *) bins[0];
weglFeedbackBuffer(*size,*type,buffer);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5110: { // glFinish
weglFinish();
@@ -830,11 +795,10 @@ case 5118: { // glGenLists
GLsizei *range = (GLsizei *) bp; bp += 4;
GLuint result = weglGenLists(*range);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5119: { // glGenTextures
GLsizei *n = (GLsizei *) bp; bp += 4;
@@ -843,13 +807,12 @@ case 5119: { // glGenTextures
weglGenTextures(*n,textures);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) textures[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(textures);
}; break;
@@ -858,7 +821,7 @@ case 5120: { // glGetBooleanv
GLboolean params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetBooleanv(*pname,params);
int AP = 0; ErlDrvTermData rt[39];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLboolean *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -878,15 +841,14 @@ case 5120: { // glGetBooleanv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 39 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,39);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5121: { // glGetClipPlane
GLenum *plane = (GLenum *) bp; bp += 4;
GLdouble equation[4] = {0.0,0.0,0.0,0.0};
weglGetClipPlane(*plane,equation);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble *equationTmp = equation;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) equationTmp++;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) equationTmp++;
@@ -894,15 +856,14 @@ case 5121: { // glGetClipPlane
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) equationTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5122: { // glGetDoublev
GLenum *pname = (GLenum *) bp; bp += 4;
GLdouble params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
weglGetDoublev(*pname,params);
int AP = 0; ErlDrvTermData rt[39];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble *paramsTmp = params;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -922,24 +883,22 @@ case 5122: { // glGetDoublev
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 39 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,39);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5123: { // glGetError
GLenum result = weglGetError();
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5124: { // glGetFloatv
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
weglGetFloatv(*pname,params);
int AP = 0; ErlDrvTermData rt[39];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[16], *paramsTmp = paramsConv;
for(int i=0; i < 16; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -960,15 +919,14 @@ case 5124: { // glGetFloatv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 39 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,39);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5125: { // glGetIntegerv
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetIntegerv(*pname,params);
int AP = 0; ErlDrvTermData rt[39];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -988,8 +946,7 @@ case 5125: { // glGetIntegerv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 39 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,39);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5126: { // glGetLightfv
GLenum *light = (GLenum *) bp; bp += 4;
@@ -997,7 +954,7 @@ case 5126: { // glGetLightfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetLightfv(*light,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -1006,8 +963,7 @@ case 5126: { // glGetLightfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5127: { // glGetLightiv
GLenum *light = (GLenum *) bp; bp += 4;
@@ -1015,7 +971,7 @@ case 5127: { // glGetLightiv
GLint params[4] = {0,0,0,0};
weglGetLightiv(*light,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -1023,41 +979,40 @@ case 5127: { // glGetLightiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5128: { // glGetMapdv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *query = (GLenum *) bp; bp += 4;
- GLdouble *v = (GLdouble *) bins[0]->base;
+ GLdouble *v = (GLdouble *) bins[0];
weglGetMapdv(*target,*query,v);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5129: { // glGetMapfv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *query = (GLenum *) bp; bp += 4;
- GLfloat *v = (GLfloat *) bins[0]->base;
+ GLfloat *v = (GLfloat *) bins[0];
weglGetMapfv(*target,*query,v);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5130: { // glGetMapiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *query = (GLenum *) bp; bp += 4;
- GLint *v = (GLint *) bins[0]->base;
+ GLint *v = (GLint *) bins[0];
weglGetMapiv(*target,*query,v);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5131: { // glGetMaterialfv
GLenum *face = (GLenum *) bp; bp += 4;
@@ -1065,7 +1020,7 @@ case 5131: { // glGetMaterialfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetMaterialfv(*face,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -1074,8 +1029,7 @@ case 5131: { // glGetMaterialfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5132: { // glGetMaterialiv
GLenum *face = (GLenum *) bp; bp += 4;
@@ -1083,7 +1037,7 @@ case 5132: { // glGetMaterialiv
GLint params[4] = {0,0,0,0};
weglGetMaterialiv(*face,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -1091,61 +1045,56 @@ case 5132: { // glGetMaterialiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5133: { // glGetPixelMapfv
GLenum *map = (GLenum *) bp; bp += 4;
- GLfloat *values = (GLfloat *) bins[0]->base;
+ GLfloat *values = (GLfloat *) bins[0];
weglGetPixelMapfv(*map,values);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5134: { // glGetPixelMapuiv
GLenum *map = (GLenum *) bp; bp += 4;
- GLuint *values = (GLuint *) bins[0]->base;
+ GLuint *values = (GLuint *) bins[0];
weglGetPixelMapuiv(*map,values);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5135: { // glGetPixelMapusv
GLenum *map = (GLenum *) bp; bp += 4;
- GLushort *values = (GLushort *) bins[0]->base;
+ GLushort *values = (GLushort *) bins[0];
weglGetPixelMapusv(*map,values);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5136: { // glGetPolygonStipple
- GLubyte mask[128];
- weglGetPolygonStipple(mask);
+ ErlDrvBinary *mask = driver_alloc_binary(128);
+ weglGetPolygonStipple((GLubyte*) mask->orig_bytes);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
- ErlDrvBinary * BinCopy = driver_alloc_binary(128);
- memcpy(BinCopy->orig_bytes, mask, 128);
- rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) BinCopy; rt[AP++] = 128; rt[AP++] = 0;
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) mask; rt[AP++] = 128; rt[AP++] = 0;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
- driver_free_binary(BinCopy);
+ driver_send_term(port,caller,rt,AP);
+ driver_free_binary(mask);
}; break;
case 5137: { // glGetString
GLenum *name = (GLenum *) bp; bp += 4;
const GLubyte * result = weglGetString(*name);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) result; rt[AP++] = strlen((char *) result);
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5138: { // glGetTexEnvfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1153,7 +1102,7 @@ case 5138: { // glGetTexEnvfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetTexEnvfv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -1162,8 +1111,7 @@ case 5138: { // glGetTexEnvfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5139: { // glGetTexEnviv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1171,7 +1119,7 @@ case 5139: { // glGetTexEnviv
GLint params[4] = {0,0,0,0};
weglGetTexEnviv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -1179,8 +1127,7 @@ case 5139: { // glGetTexEnviv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5140: { // glGetTexGendv
GLenum *coord = (GLenum *) bp; bp += 4;
@@ -1188,7 +1135,7 @@ case 5140: { // glGetTexGendv
GLdouble params[4] = {0.0,0.0,0.0,0.0};
weglGetTexGendv(*coord,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble *paramsTmp = params;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -1196,8 +1143,7 @@ case 5140: { // glGetTexGendv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5141: { // glGetTexGenfv
GLenum *coord = (GLenum *) bp; bp += 4;
@@ -1205,7 +1151,7 @@ case 5141: { // glGetTexGenfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetTexGenfv(*coord,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -1214,8 +1160,7 @@ case 5141: { // glGetTexGenfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5142: { // glGetTexGeniv
GLenum *coord = (GLenum *) bp; bp += 4;
@@ -1223,7 +1168,7 @@ case 5142: { // glGetTexGeniv
GLint params[4] = {0,0,0,0};
weglGetTexGeniv(*coord,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -1231,21 +1176,20 @@ case 5142: { // glGetTexGeniv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5143: { // glGetTexImage
GLenum *target = (GLenum *) bp; bp += 4;
GLint *level = (GLint *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglGetTexImage(*target,*level,*format,*type,pixels);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5144: { // glGetTexLevelParameterfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1254,14 +1198,13 @@ case 5144: { // glGetTexLevelParameterfv
GLfloat params[1] = {0.0};
weglGetTexLevelParameterfv(*target,*level,*pname,params);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[1], *paramsTmp = paramsConv;
for(int i=0; i < 1; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5145: { // glGetTexLevelParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1270,13 +1213,12 @@ case 5145: { // glGetTexLevelParameteriv
GLint params[1] = {0};
weglGetTexLevelParameteriv(*target,*level,*pname,params);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5146: { // glGetTexParameterfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1284,7 +1226,7 @@ case 5146: { // glGetTexParameterfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetTexParameterfv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -1293,8 +1235,7 @@ case 5146: { // glGetTexParameterfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5147: { // glGetTexParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1302,7 +1243,7 @@ case 5147: { // glGetTexParameteriv
GLint params[4] = {0,0,0,0};
weglGetTexParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -1310,8 +1251,7 @@ case 5147: { // glGetTexParameteriv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5148: { // glHint
GLenum *target = (GLenum *) bp; bp += 4;
@@ -1331,7 +1271,7 @@ case 5150: { // glIndexPointer
case 5151: { // glIndexPointer
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglIndexPointer(*type,*stride,pointer);
}; break;
case 5152: { // glIndexdv
@@ -1366,38 +1306,35 @@ case 5158: { // glInterleavedArrays
case 5159: { // glInterleavedArrays
GLenum *format = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglInterleavedArrays(*format,*stride,pointer);
}; break;
case 5160: { // glIsEnabled
GLenum *cap = (GLenum *) bp; bp += 4;
GLboolean result = weglIsEnabled(*cap);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5161: { // glIsList
GLuint *list = (GLuint *) bp; bp += 4;
GLboolean result = weglIsList(*list);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5162: { // glIsTexture
GLuint *texture = (GLuint *) bp; bp += 4;
GLboolean result = weglIsTexture(*texture);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5163: { // glLightModelf
GLenum *pname = (GLenum *) bp; bp += 4;
@@ -1486,7 +1423,7 @@ case 5179: { // glMap1d
GLdouble *u2 = (GLdouble *) bp; bp += 8;
GLint *stride = (GLint *) bp; bp += 4;
GLint *order = (GLint *) bp; bp += 4;
- GLdouble *points = (GLdouble *) bins[0]->base;
+ GLdouble *points = (GLdouble *) bins[0];
weglMap1d(*target,*u1,*u2,*stride,*order,points);
}; break;
case 5180: { // glMap1f
@@ -1495,7 +1432,7 @@ case 5180: { // glMap1f
GLfloat *u2 = (GLfloat *) bp; bp += 4;
GLint *stride = (GLint *) bp; bp += 4;
GLint *order = (GLint *) bp; bp += 4;
- GLfloat *points = (GLfloat *) bins[0]->base;
+ GLfloat *points = (GLfloat *) bins[0];
weglMap1f(*target,*u1,*u2,*stride,*order,points);
}; break;
case 5181: { // glMap2d
@@ -1509,7 +1446,7 @@ case 5181: { // glMap2d
GLdouble *v2 = (GLdouble *) bp; bp += 8;
GLint *vstride = (GLint *) bp; bp += 4;
GLint *vorder = (GLint *) bp; bp += 4;
- GLdouble *points = (GLdouble *) bins[0]->base;
+ GLdouble *points = (GLdouble *) bins[0];
weglMap2d(*target,*u1,*u2,*ustride,*uorder,*v1,*v2,*vstride,*vorder,points);
}; break;
case 5182: { // glMap2f
@@ -1522,7 +1459,7 @@ case 5182: { // glMap2f
GLfloat *v2 = (GLfloat *) bp; bp += 4;
GLint *vstride = (GLint *) bp; bp += 4;
GLint *vorder = (GLint *) bp; bp += 4;
- GLfloat *points = (GLfloat *) bins[0]->base;
+ GLfloat *points = (GLfloat *) bins[0];
weglMap2f(*target,*u1,*u2,*ustride,*uorder,*v1,*v2,*vstride,*vorder,points);
}; break;
case 5183: { // glMapGrid1d
@@ -1630,7 +1567,7 @@ case 5200: { // glNormalPointer
case 5201: { // glNormalPointer
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglNormalPointer(*type,*stride,pointer);
}; break;
case 5202: { // glOrtho
@@ -1649,19 +1586,19 @@ case 5203: { // glPassThrough
case 5204: { // glPixelMapfv
GLenum *map = (GLenum *) bp; bp += 4;
GLsizei *mapsize = (GLsizei *) bp; bp += 4;
- GLfloat *values = (GLfloat *) bins[0]->base;
+ GLfloat *values = (GLfloat *) bins[0];
weglPixelMapfv(*map,*mapsize,values);
}; break;
case 5205: { // glPixelMapuiv
GLenum *map = (GLenum *) bp; bp += 4;
GLsizei *mapsize = (GLsizei *) bp; bp += 4;
- GLuint *values = (GLuint *) bins[0]->base;
+ GLuint *values = (GLuint *) bins[0];
weglPixelMapuiv(*map,*mapsize,values);
}; break;
case 5206: { // glPixelMapusv
GLenum *map = (GLenum *) bp; bp += 4;
GLsizei *mapsize = (GLsizei *) bp; bp += 4;
- GLushort *values = (GLushort *) bins[0]->base;
+ GLushort *values = (GLushort *) bins[0];
weglPixelMapusv(*map,*mapsize,values);
}; break;
case 5207: { // glPixelStoref
@@ -1704,7 +1641,7 @@ case 5214: { // glPolygonOffset
weglPolygonOffset(*factor,*units);
}; break;
case 5215: { // glPolygonStipple
- GLubyte *mask = (GLubyte *) bins[0]->base;
+ GLubyte *mask = (GLubyte *) bins[0];
weglPolygonStipple(mask);
}; break;
case 5216: { // glPopAttrib
@@ -1800,13 +1737,13 @@ case 5238: { // glReadPixels
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglReadPixels(*x,*y,*width,*height,*format,*type,pixels);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5239: { // glRectd
GLdouble *x1 = (GLdouble *) bp; bp += 8;
@@ -1860,11 +1797,10 @@ case 5247: { // glRenderMode
GLenum *mode = (GLenum *) bp; bp += 4;
GLint result = weglRenderMode(*mode);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5248: { // glRotated
GLdouble *angle = (GLdouble *) bp; bp += 8;
@@ -1901,13 +1837,13 @@ case 5252: { // glScissor
}; break;
case 5253: { // glSelectBuffer
GLsizei *size = (GLsizei *) bp; bp += 4;
- GLuint *buffer = (GLuint *) bins[0]->base;
+ GLuint *buffer = (GLuint *) bins[0];
weglSelectBuffer(*size,buffer);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5254: { // glShadeModel
GLenum *mode = (GLenum *) bp; bp += 4;
@@ -2004,7 +1940,7 @@ case 5275: { // glTexCoordPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglTexCoordPointer(*size,*type,*stride,pointer);
}; break;
case 5276: { // glTexEnvf
@@ -2091,7 +2027,7 @@ case 5287: { // glTexImage1D
GLint *border = (GLint *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglTexImage1D(*target,*level,*internalformat,*width,*border,*format,*type,pixels);
}; break;
case 5288: { // glTexImage2D
@@ -2115,7 +2051,7 @@ case 5289: { // glTexImage2D
GLint *border = (GLint *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglTexImage2D(*target,*level,*internalformat,*width,*height,*border,*format,*type,pixels);
}; break;
case 5290: { // glTexParameterf
@@ -2161,7 +2097,7 @@ case 5295: { // glTexSubImage1D
GLsizei *width = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglTexSubImage1D(*target,*level,*xoffset,*width,*format,*type,pixels);
}; break;
case 5296: { // glTexSubImage2D
@@ -2185,7 +2121,7 @@ case 5297: { // glTexSubImage2D
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglTexSubImage2D(*target,*level,*xoffset,*yoffset,*width,*height,*format,*type,pixels);
}; break;
case 5298: { // glTranslated
@@ -2259,7 +2195,7 @@ case 5313: { // glVertexPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglVertexPointer(*size,*type,*stride,pointer);
}; break;
case 5314: { // glViewport
@@ -2295,7 +2231,7 @@ case 5318: { // glDrawRangeElements
GLuint *end = (GLuint *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *indices = (GLvoid *) bins[0]->base;
+ GLvoid *indices = (GLvoid *) bins[0];
weglDrawRangeElements(*mode,*start,*end,*count,*type,indices);
}; break;
case 5319: { // glTexImage3D
@@ -2321,7 +2257,7 @@ case 5320: { // glTexImage3D
GLint *border = (GLint *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglTexImage3D(*target,*level,*internalformat,*width,*height,*depth,*border,*format,*type,pixels);
}; break;
case 5321: { // glTexSubImage3D
@@ -2349,7 +2285,7 @@ case 5322: { // glTexSubImage3D
GLsizei *depth = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *pixels = (GLvoid *) bins[0]->base;
+ GLvoid *pixels = (GLvoid *) bins[0];
weglTexSubImage3D(*target,*level,*xoffset,*yoffset,*zoffset,*width,*height,*depth,*format,*type,pixels);
}; break;
case 5323: { // glCopyTexSubImage3D
@@ -2379,7 +2315,7 @@ case 5325: { // glColorTable
GLsizei *width = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *table = (GLvoid *) bins[0]->base;
+ GLvoid *table = (GLvoid *) bins[0];
weglColorTable(*target,*internalformat,*width,*format,*type,table);
}; break;
case 5326: { // glColorTableParameterfv
@@ -2406,13 +2342,13 @@ case 5329: { // glGetColorTable
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *table = (GLvoid *) bins[0]->base;
+ GLvoid *table = (GLvoid *) bins[0];
weglGetColorTable(*target,*format,*type,table);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5330: { // glGetColorTableParameterfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2420,7 +2356,7 @@ case 5330: { // glGetColorTableParameterfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetColorTableParameterfv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -2429,8 +2365,7 @@ case 5330: { // glGetColorTableParameterfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5331: { // glGetColorTableParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2438,7 +2373,7 @@ case 5331: { // glGetColorTableParameteriv
GLint params[4] = {0,0,0,0};
weglGetColorTableParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -2446,8 +2381,7 @@ case 5331: { // glGetColorTableParameteriv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5332: { // glColorSubTable
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2464,7 +2398,7 @@ case 5333: { // glColorSubTable
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglColorSubTable(*target,*start,*count,*format,*type,data);
}; break;
case 5334: { // glCopyColorSubTable
@@ -2490,7 +2424,7 @@ case 5336: { // glConvolutionFilter1D
GLsizei *width = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *image = (GLvoid *) bins[0]->base;
+ GLvoid *image = (GLvoid *) bins[0];
weglConvolutionFilter1D(*target,*internalformat,*width,*format,*type,image);
}; break;
case 5337: { // glConvolutionFilter2D
@@ -2510,7 +2444,7 @@ case 5338: { // glConvolutionFilter2D
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *image = (GLvoid *) bins[0]->base;
+ GLvoid *image = (GLvoid *) bins[0];
weglConvolutionFilter2D(*target,*internalformat,*width,*height,*format,*type,image);
}; break;
case 5339: { // glConvolutionParameterfv
@@ -2548,13 +2482,13 @@ case 5343: { // glGetConvolutionFilter
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *image = (GLvoid *) bins[0]->base;
+ GLvoid *image = (GLvoid *) bins[0];
weglGetConvolutionFilter(*target,*format,*type,image);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5344: { // glGetConvolutionParameterfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2562,7 +2496,7 @@ case 5344: { // glGetConvolutionParameterfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetConvolutionParameterfv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -2571,8 +2505,7 @@ case 5344: { // glGetConvolutionParameterfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5345: { // glGetConvolutionParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2580,7 +2513,7 @@ case 5345: { // glGetConvolutionParameteriv
GLint params[4] = {0,0,0,0};
weglGetConvolutionParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -2588,8 +2521,7 @@ case 5345: { // glGetConvolutionParameteriv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5346: { // glSeparableFilter2D
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2609,8 +2541,8 @@ case 5347: { // glSeparableFilter2D
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *row = (GLvoid *) bins[0]->base;
- GLvoid *column = (GLvoid *) bins[1]->base;
+ GLvoid *row = (GLvoid *) bins[0];
+ GLvoid *column = (GLvoid *) bins[1];
weglSeparableFilter2D(*target,*internalformat,*width,*height,*format,*type,row,column);
}; break;
case 5348: { // glGetHistogram
@@ -2619,13 +2551,13 @@ case 5348: { // glGetHistogram
bp += 3;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *values = (GLvoid *) bins[0]->base;
+ GLvoid *values = (GLvoid *) bins[0];
weglGetHistogram(*target,*reset,*format,*type,values);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5349: { // glGetHistogramParameterfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2633,14 +2565,13 @@ case 5349: { // glGetHistogramParameterfv
GLfloat params[1] = {0.0};
weglGetHistogramParameterfv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[1], *paramsTmp = paramsConv;
for(int i=0; i < 1; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5350: { // glGetHistogramParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2648,13 +2579,12 @@ case 5350: { // glGetHistogramParameteriv
GLint params[1] = {0};
weglGetHistogramParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5351: { // glGetMinmax
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2662,13 +2592,13 @@ case 5351: { // glGetMinmax
bp += 3;
GLenum *format = (GLenum *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *values = (GLvoid *) bins[0]->base;
+ GLvoid *values = (GLvoid *) bins[0];
weglGetMinmax(*target,*reset,*format,*type,values);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5352: { // glGetMinmaxParameterfv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2676,14 +2606,13 @@ case 5352: { // glGetMinmaxParameterfv
GLfloat params[1] = {0.0};
weglGetMinmaxParameterfv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[1], *paramsTmp = paramsConv;
for(int i=0; i < 1; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5353: { // glGetMinmaxParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2691,13 +2620,12 @@ case 5353: { // glGetMinmaxParameteriv
GLint params[1] = {0};
weglGetMinmaxParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[8];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 8 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,8);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5354: { // glHistogram
GLenum *target = (GLenum *) bp; bp += 4;
@@ -2750,7 +2678,7 @@ case 5361: { // glCompressedTexImage3D
GLsizei *depth = (GLsizei *) bp; bp += 4;
GLint *border = (GLint *) bp; bp += 4;
GLsizei *imageSize = (GLsizei *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglCompressedTexImage3D(*target,*level,*internalformat,*width,*height,*depth,*border,*imageSize,data);
}; break;
case 5362: { // glCompressedTexImage2D
@@ -2772,7 +2700,7 @@ case 5363: { // glCompressedTexImage2D
GLsizei *height = (GLsizei *) bp; bp += 4;
GLint *border = (GLint *) bp; bp += 4;
GLsizei *imageSize = (GLsizei *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglCompressedTexImage2D(*target,*level,*internalformat,*width,*height,*border,*imageSize,data);
}; break;
case 5364: { // glCompressedTexImage1D
@@ -2792,7 +2720,7 @@ case 5365: { // glCompressedTexImage1D
GLsizei *width = (GLsizei *) bp; bp += 4;
GLint *border = (GLint *) bp; bp += 4;
GLsizei *imageSize = (GLsizei *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglCompressedTexImage1D(*target,*level,*internalformat,*width,*border,*imageSize,data);
}; break;
case 5366: { // glCompressedTexSubImage3D
@@ -2820,7 +2748,7 @@ case 5367: { // glCompressedTexSubImage3D
GLsizei *depth = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLsizei *imageSize = (GLsizei *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglCompressedTexSubImage3D(*target,*level,*xoffset,*yoffset,*zoffset,*width,*height,*depth,*format,*imageSize,data);
}; break;
case 5368: { // glCompressedTexSubImage2D
@@ -2844,7 +2772,7 @@ case 5369: { // glCompressedTexSubImage2D
GLsizei *height = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLsizei *imageSize = (GLsizei *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglCompressedTexSubImage2D(*target,*level,*xoffset,*yoffset,*width,*height,*format,*imageSize,data);
}; break;
case 5370: { // glCompressedTexSubImage1D
@@ -2864,19 +2792,19 @@ case 5371: { // glCompressedTexSubImage1D
GLsizei *width = (GLsizei *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLsizei *imageSize = (GLsizei *) bp; bp += 4;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglCompressedTexSubImage1D(*target,*level,*xoffset,*width,*format,*imageSize,data);
}; break;
case 5372: { // glGetCompressedTexImage
GLenum *target = (GLenum *) bp; bp += 4;
GLint *level = (GLint *) bp; bp += 4;
- GLvoid *img = (GLvoid *) bins[0]->base;
+ GLvoid *img = (GLvoid *) bins[0];
weglGetCompressedTexImage(*target,*level,img);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5373: { // glClientActiveTexture
GLenum *texture = (GLenum *) bp; bp += 4;
@@ -3036,7 +2964,7 @@ case 5402: { // glFogCoordPointer
case 5403: { // glFogCoordPointer
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglFogCoordPointer(*type,*stride,pointer);
}; break;
case 5404: { // glSecondaryColor3bv
@@ -3082,7 +3010,7 @@ case 5413: { // glSecondaryColorPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglSecondaryColorPointer(*size,*type,*stride,pointer);
}; break;
case 5414: { // glWindowPos2dv
@@ -3124,13 +3052,12 @@ case 5422: { // glGenQueries
weglGenQueries(*n,ids);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) ids[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(ids);
}; break;
@@ -3143,11 +3070,10 @@ case 5424: { // glIsQuery
GLuint *id = (GLuint *) bp; bp += 4;
GLboolean result = weglIsQuery(*id);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5425: { // glBeginQuery
GLenum *target = (GLenum *) bp; bp += 4;
@@ -3164,11 +3090,10 @@ case 5427: { // glGetQueryiv
GLint params[1] = {0};
weglGetQueryiv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5428: { // glGetQueryObjectiv
GLuint *id = (GLuint *) bp; bp += 4;
@@ -3176,11 +3101,10 @@ case 5428: { // glGetQueryObjectiv
GLint params[1] = {0};
weglGetQueryObjectiv(*id,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5429: { // glGetQueryObjectuiv
GLuint *id = (GLuint *) bp; bp += 4;
@@ -3188,11 +3112,10 @@ case 5429: { // glGetQueryObjectuiv
GLuint params[1] = {0};
weglGetQueryObjectuiv(*id,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5430: { // glBindBuffer
GLenum *target = (GLenum *) bp; bp += 4;
@@ -3211,13 +3134,12 @@ case 5432: { // glGenBuffers
weglGenBuffers(*n,buffers);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) buffers[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(buffers);
}; break;
@@ -3225,11 +3147,10 @@ case 5433: { // glIsBuffer
GLuint *buffer = (GLuint *) bp; bp += 4;
GLboolean result = weglIsBuffer(*buffer);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5434: { // glBufferData
GLenum *target = (GLenum *) bp; bp += 4;
@@ -3243,7 +3164,7 @@ case 5435: { // glBufferData
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
GLenum *usage = (GLenum *) bp; bp += 4;
weglBufferData(*target,size,data,*usage);
}; break;
@@ -3260,7 +3181,7 @@ case 5437: { // glBufferSubData
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglBufferSubData(*target,offset,size,data);
}; break;
case 5438: { // glGetBufferSubData
@@ -3268,13 +3189,13 @@ case 5438: { // glGetBufferSubData
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
- GLvoid *data = (GLvoid *) bins[0]->base;
+ GLvoid *data = (GLvoid *) bins[0];
weglGetBufferSubData(*target,offset,size,data);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5439: { // glGetBufferParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
@@ -3282,11 +3203,10 @@ case 5439: { // glGetBufferParameteriv
GLint params[1] = {0};
weglGetBufferParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5440: { // glBlendEquationSeparate
GLenum *modeRGB = (GLenum *) bp; bp += 4;
@@ -3306,11 +3226,11 @@ case 5442: { // glStencilOpSeparate
weglStencilOpSeparate(*face,*sfail,*dpfail,*dppass);
}; break;
case 5443: { // glStencilFuncSeparate
- GLenum *frontfunc = (GLenum *) bp; bp += 4;
- GLenum *backfunc = (GLenum *) bp; bp += 4;
+ GLenum *face = (GLenum *) bp; bp += 4;
+ GLenum *func = (GLenum *) bp; bp += 4;
GLint *ref = (GLint *) bp; bp += 4;
GLuint *mask = (GLuint *) bp; bp += 4;
- weglStencilFuncSeparate(*frontfunc,*backfunc,*ref,*mask);
+ weglStencilFuncSeparate(*face,*func,*ref,*mask);
}; break;
case 5444: { // glStencilMaskSeparate
GLenum *face = (GLenum *) bp; bp += 4;
@@ -3326,7 +3246,7 @@ case 5446: { // glBindAttribLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+0)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
weglBindAttribLocation(*program,*index,name);
}; break;
case 5447: { // glCompileShader
@@ -3336,21 +3256,19 @@ case 5447: { // glCompileShader
case 5448: { // glCreateProgram
GLuint result = weglCreateProgram();
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5449: { // glCreateShader
GLenum *type = (GLenum *) bp; bp += 4;
GLuint result = weglCreateShader(*type);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5450: { // glDeleteProgram
GLuint *program = (GLuint *) bp; bp += 4;
@@ -3384,14 +3302,13 @@ case 5455: { // glGetActiveAttrib
name = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetActiveAttrib(*program,*index,*bufSize,length,size,type,name);
int AP = 0; ErlDrvTermData rt[13];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *size;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *type;
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 13 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,13);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
case 5456: { // glGetActiveUniform
@@ -3405,14 +3322,13 @@ case 5456: { // glGetActiveUniform
name = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetActiveUniform(*program,*index,*bufSize,length,size,type,name);
int AP = 0; ErlDrvTermData rt[13];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *size;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *type;
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 13 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,13);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
case 5457: { // glGetAttachedShaders
@@ -3424,27 +3340,25 @@ case 5457: { // glGetAttachedShaders
weglGetAttachedShaders(*program,*maxCount,count,obj);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*count)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *count; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) obj[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*count)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*count)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*count)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(obj);
}; break;
case 5458: { // glGetAttribLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+4)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
GLint result = weglGetAttribLocation(*program,name);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5459: { // glGetProgramiv
GLuint *program = (GLuint *) bp; bp += 4;
@@ -3452,11 +3366,10 @@ case 5459: { // glGetProgramiv
GLint params[1] = {0};
weglGetProgramiv(*program,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5460: { // glGetProgramInfoLog
GLuint *program = (GLuint *) bp; bp += 4;
@@ -3466,11 +3379,10 @@ case 5460: { // glGetProgramInfoLog
infoLog = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetProgramInfoLog(*program,*bufSize,length,infoLog);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) infoLog; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
case 5461: { // glGetShaderiv
@@ -3479,11 +3391,10 @@ case 5461: { // glGetShaderiv
GLint params[1] = {0};
weglGetShaderiv(*shader,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5462: { // glGetShaderInfoLog
GLuint *shader = (GLuint *) bp; bp += 4;
@@ -3493,11 +3404,10 @@ case 5462: { // glGetShaderInfoLog
infoLog = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetShaderInfoLog(*shader,*bufSize,length,infoLog);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) infoLog; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
case 5463: { // glGetShaderSource
@@ -3508,24 +3418,22 @@ case 5463: { // glGetShaderSource
source = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetShaderSource(*shader,*bufSize,length,source);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) source; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(source);
}; break;
case 5464: { // glGetUniformLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+4)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
GLint result = weglGetUniformLocation(*program,name);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5465: { // glGetUniformfv
GLuint *program = (GLuint *) bp; bp += 4;
@@ -3533,7 +3441,7 @@ case 5465: { // glGetUniformfv
GLfloat params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
weglGetUniformfv(*program,*location,params);
int AP = 0; ErlDrvTermData rt[38];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[16], *paramsTmp = paramsConv;
for(int i=0; i < 16; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -3554,8 +3462,7 @@ case 5465: { // glGetUniformfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 38 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,38);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5466: { // glGetUniformiv
GLuint *program = (GLuint *) bp; bp += 4;
@@ -3563,7 +3470,7 @@ case 5466: { // glGetUniformiv
GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetUniformiv(*program,*location,params);
int AP = 0; ErlDrvTermData rt[38];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -3583,8 +3490,7 @@ case 5466: { // glGetUniformiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 38 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,38);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5467: { // glGetVertexAttribdv
GLuint *index = (GLuint *) bp; bp += 4;
@@ -3592,7 +3498,7 @@ case 5467: { // glGetVertexAttribdv
GLdouble params[4] = {0.0,0.0,0.0,0.0};
weglGetVertexAttribdv(*index,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble *paramsTmp = params;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -3600,8 +3506,7 @@ case 5467: { // glGetVertexAttribdv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5468: { // glGetVertexAttribfv
GLuint *index = (GLuint *) bp; bp += 4;
@@ -3609,7 +3514,7 @@ case 5468: { // glGetVertexAttribfv
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetVertexAttribfv(*index,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -3618,8 +3523,7 @@ case 5468: { // glGetVertexAttribfv
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5469: { // glGetVertexAttribiv
GLuint *index = (GLuint *) bp; bp += 4;
@@ -3627,7 +3531,7 @@ case 5469: { // glGetVertexAttribiv
GLint params[4] = {0,0,0,0};
weglGetVertexAttribiv(*index,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -3635,28 +3539,25 @@ case 5469: { // glGetVertexAttribiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5470: { // glIsProgram
GLuint *program = (GLuint *) bp; bp += 4;
GLboolean result = weglIsProgram(*program);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5471: { // glIsShader
GLuint *shader = (GLuint *) bp; bp += 4;
GLboolean result = weglIsShader(*shader);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5472: { // glLinkProgram
GLuint *program = (GLuint *) bp; bp += 4;
@@ -3942,7 +3843,7 @@ case 5519: { // glVertexAttribPointer
GLboolean *normalized = (GLboolean *) bp; bp += 1;
bp += 3;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglVertexAttribPointer(*index,*size,*type,*normalized,*stride,pointer);
}; break;
case 5520: { // glUniformMatrix2x3fv
@@ -4007,7 +3908,7 @@ case 5527: { // glGetBooleani_v
GLboolean data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetBooleani_v(*target,*index,data);
int AP = 0; ErlDrvTermData rt[39];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLboolean *dataTmp = data;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
@@ -4027,8 +3928,7 @@ case 5527: { // glGetBooleani_v
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 39 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,39);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5528: { // glGetIntegeri_v
GLenum *target = (GLenum *) bp; bp += 4;
@@ -4036,7 +3936,7 @@ case 5528: { // glGetIntegeri_v
GLint data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetIntegeri_v(*target,*index,data);
int AP = 0; ErlDrvTermData rt[39];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *dataTmp = data;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
@@ -4056,8 +3956,7 @@ case 5528: { // glGetIntegeri_v
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 39 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,39);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5529: { // glEnablei
GLenum *target = (GLenum *) bp; bp += 4;
@@ -4074,11 +3973,10 @@ case 5531: { // glIsEnabledi
GLuint *index = (GLuint *) bp; bp += 4;
GLboolean result = weglIsEnabledi(*target,*index);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5532: { // glBeginTransformFeedback
GLenum *primitiveMode = (GLenum *) bp; bp += 4;
@@ -4126,14 +4024,13 @@ case 5537: { // glGetTransformFeedbackVarying
name = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetTransformFeedbackVarying(*program,*index,*bufSize,length,size,type,name);
int AP = 0; ErlDrvTermData rt[13];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *size;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *type;
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 13 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,13);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
case 5538: { // glClampColor
@@ -4162,7 +4059,7 @@ case 5542: { // glVertexAttribIPointer
GLint *size = (GLint *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
GLsizei *stride = (GLsizei *) bp; bp += 4;
- GLvoid *pointer = (GLvoid *) bins[0]->base;
+ GLvoid *pointer = (GLvoid *) bins[0];
weglVertexAttribIPointer(*index,*size,*type,*stride,pointer);
}; break;
case 5543: { // glGetVertexAttribIiv
@@ -4171,7 +4068,7 @@ case 5543: { // glGetVertexAttribIiv
GLint params[4] = {0,0,0,0};
weglGetVertexAttribIiv(*index,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -4179,8 +4076,7 @@ case 5543: { // glGetVertexAttribIiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5544: { // glGetVertexAttribIuiv
GLuint *index = (GLuint *) bp; bp += 4;
@@ -4188,7 +4084,7 @@ case 5544: { // glGetVertexAttribIuiv
GLuint params[4] = {0,0,0,0};
weglGetVertexAttribIuiv(*index,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLuint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -4196,16 +4092,75 @@ case 5544: { // glGetVertexAttribIuiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5545: { // glVertexAttribI1iv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint *v = (GLint *) bp; bp += 4;
+ weglVertexAttribI1iv(*index,v);
+}; break;
+case 5546: { // glVertexAttribI2iv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint *v = (GLint *) bp; bp += 4;
+ weglVertexAttribI2iv(*index,v);
+}; break;
+case 5547: { // glVertexAttribI3iv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint *v = (GLint *) bp; bp += 4;
+ weglVertexAttribI3iv(*index,v);
+}; break;
+case 5548: { // glVertexAttribI4iv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint * v = (GLint *) bp; bp += 16;
+ weglVertexAttribI4iv(*index,v);
+}; break;
+case 5549: { // glVertexAttribI1uiv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLuint *v = (GLuint *) bp; bp += 4;
+ weglVertexAttribI1uiv(*index,v);
+}; break;
+case 5550: { // glVertexAttribI2uiv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLuint *v = (GLuint *) bp; bp += 4;
+ weglVertexAttribI2uiv(*index,v);
+}; break;
+case 5551: { // glVertexAttribI3uiv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLuint *v = (GLuint *) bp; bp += 4;
+ weglVertexAttribI3uiv(*index,v);
}; break;
-case 5545: { // glGetUniformuiv
+case 5552: { // glVertexAttribI4uiv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLuint * v = (GLuint *) bp; bp += 16;
+ weglVertexAttribI4uiv(*index,v);
+}; break;
+case 5553: { // glVertexAttribI4bv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLbyte * v = (GLbyte *) bp; bp += 4;
+ weglVertexAttribI4bv(*index,v);
+}; break;
+case 5554: { // glVertexAttribI4sv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLshort * v = (GLshort *) bp; bp += 8;
+ weglVertexAttribI4sv(*index,v);
+}; break;
+case 5555: { // glVertexAttribI4ubv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLubyte * v = (GLubyte *) bp; bp += 4;
+ weglVertexAttribI4ubv(*index,v);
+}; break;
+case 5556: { // glVertexAttribI4usv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLushort * v = (GLushort *) bp; bp += 8;
+ weglVertexAttribI4usv(*index,v);
+}; break;
+case 5557: { // glGetUniformuiv
GLuint *program = (GLuint *) bp; bp += 4;
GLint *location = (GLint *) bp; bp += 4;
GLuint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetUniformuiv(*program,*location,params);
int AP = 0; ErlDrvTermData rt[38];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLuint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -4225,47 +4180,45 @@ case 5545: { // glGetUniformuiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 38 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,38);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5546: { // glBindFragDataLocation
+case 5558: { // glBindFragDataLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *color = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+0)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
weglBindFragDataLocation(*program,*color,name);
}; break;
-case 5547: { // glGetFragDataLocation
+case 5559: { // glGetFragDataLocation
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+4)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
GLint result = weglGetFragDataLocation(*program,name);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5548: { // glUniform1ui
+case 5560: { // glUniform1ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
weglUniform1ui(*location,*v0);
}; break;
-case 5549: { // glUniform2ui
+case 5561: { // glUniform2ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
weglUniform2ui(*location,*v0,*v1);
}; break;
-case 5550: { // glUniform3ui
+case 5562: { // glUniform3ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
GLuint *v2 = (GLuint *) bp; bp += 4;
weglUniform3ui(*location,*v0,*v1,*v2);
}; break;
-case 5551: { // glUniform4ui
+case 5563: { // glUniform4ui
GLint *location = (GLint *) bp; bp += 4;
GLuint *v0 = (GLuint *) bp; bp += 4;
GLuint *v1 = (GLuint *) bp; bp += 4;
@@ -4273,51 +4226,51 @@ case 5551: { // glUniform4ui
GLuint *v3 = (GLuint *) bp; bp += 4;
weglUniform4ui(*location,*v0,*v1,*v2,*v3);
}; break;
-case 5552: { // glUniform1uiv
+case 5564: { // glUniform1uiv
GLint *location = (GLint *) bp; bp += 4;
int * valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += (8-((*valueLen*4+0)%8))%8;
weglUniform1uiv(*location,*valueLen,value);
}; break;
-case 5553: { // glUniform2uiv
+case 5565: { // glUniform2uiv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*8;
weglUniform2uiv(*location,*valueLen,value);
}; break;
-case 5554: { // glUniform3uiv
+case 5566: { // glUniform3uiv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*12;
weglUniform3uiv(*location,*valueLen,value);
}; break;
-case 5555: { // glUniform4uiv
+case 5567: { // glUniform4uiv
GLint *location = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint * value = (GLuint *) bp; bp += *valueLen*16;
weglUniform4uiv(*location,*valueLen,value);
}; break;
-case 5556: { // glTexParameterIiv
+case 5568: { // glTexParameterIiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int *paramsLen = (int *) bp; bp += 4;
GLint *params = (GLint *) bp; bp += *paramsLen*4+((*paramsLen)+1)%2*4;
weglTexParameterIiv(*target,*pname,params);
}; break;
-case 5557: { // glTexParameterIuiv
+case 5569: { // glTexParameterIuiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
int *paramsLen = (int *) bp; bp += 4;
GLuint *params = (GLuint *) bp; bp += *paramsLen*4+((*paramsLen)+1)%2*4;
weglTexParameterIuiv(*target,*pname,params);
}; break;
-case 5558: { // glGetTexParameterIiv
+case 5570: { // glGetTexParameterIiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[4] = {0,0,0,0};
weglGetTexParameterIiv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -4325,16 +4278,15 @@ case 5558: { // glGetTexParameterIiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5559: { // glGetTexParameterIuiv
+case 5571: { // glGetTexParameterIuiv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLuint params[4] = {0,0,0,0};
weglGetTexParameterIuiv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLuint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -4342,107 +4294,45 @@ case 5559: { // glGetTexParameterIuiv
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5560: { // glClearBufferiv
+case 5572: { // glClearBufferiv
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLint *value = (GLint *) bp; bp += *valueLen*4+((*valueLen)+1)%2*4;
weglClearBufferiv(*buffer,*drawbuffer,value);
}; break;
-case 5561: { // glClearBufferuiv
+case 5573: { // glClearBufferuiv
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLuint *value = (GLuint *) bp; bp += *valueLen*4+((*valueLen)+1)%2*4;
weglClearBufferuiv(*buffer,*drawbuffer,value);
}; break;
-case 5562: { // glClearBufferfv
+case 5574: { // glClearBufferfv
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
int *valueLen = (int *) bp; bp += 4;
GLfloat *value = (GLfloat *) bp; bp += *valueLen*4+((*valueLen)+1)%2*4;
weglClearBufferfv(*buffer,*drawbuffer,value);
}; break;
-case 5563: { // glClearBufferfi
+case 5575: { // glClearBufferfi
GLenum *buffer = (GLenum *) bp; bp += 4;
GLint *drawbuffer = (GLint *) bp; bp += 4;
GLfloat *depth = (GLfloat *) bp; bp += 4;
GLint *stencil = (GLint *) bp; bp += 4;
weglClearBufferfi(*buffer,*drawbuffer,*depth,*stencil);
}; break;
-case 5564: { // glGetStringi
+case 5576: { // glGetStringi
GLenum *name = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
const GLubyte * result = weglGetStringi(*name,*index);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) result; rt[AP++] = strlen((char *) result);
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
-}; break;
-case 5565: { // glVertexAttribI1iv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLint *v = (GLint *) bp; bp += 4;
- weglVertexAttribI1iv(*index,v);
-}; break;
-case 5566: { // glVertexAttribI2iv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLint *v = (GLint *) bp; bp += 4;
- weglVertexAttribI2iv(*index,v);
-}; break;
-case 5567: { // glVertexAttribI3iv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLint *v = (GLint *) bp; bp += 4;
- weglVertexAttribI3iv(*index,v);
-}; break;
-case 5568: { // glVertexAttribI4iv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLint * v = (GLint *) bp; bp += 16;
- weglVertexAttribI4iv(*index,v);
-}; break;
-case 5569: { // glVertexAttribI1uiv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLuint *v = (GLuint *) bp; bp += 4;
- weglVertexAttribI1uiv(*index,v);
-}; break;
-case 5570: { // glVertexAttribI2uiv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLuint *v = (GLuint *) bp; bp += 4;
- weglVertexAttribI2uiv(*index,v);
-}; break;
-case 5571: { // glVertexAttribI3uiv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLuint *v = (GLuint *) bp; bp += 4;
- weglVertexAttribI3uiv(*index,v);
-}; break;
-case 5572: { // glVertexAttribI4uiv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLuint * v = (GLuint *) bp; bp += 16;
- weglVertexAttribI4uiv(*index,v);
-}; break;
-case 5573: { // glVertexAttribI4bv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLbyte * v = (GLbyte *) bp; bp += 4;
- weglVertexAttribI4bv(*index,v);
-}; break;
-case 5574: { // glVertexAttribI4sv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLshort * v = (GLshort *) bp; bp += 8;
- weglVertexAttribI4sv(*index,v);
-}; break;
-case 5575: { // glVertexAttribI4ubv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLubyte * v = (GLubyte *) bp; bp += 4;
- weglVertexAttribI4ubv(*index,v);
-}; break;
-case 5576: { // glVertexAttribI4usv
- GLuint *index = (GLuint *) bp; bp += 4;
- GLushort * v = (GLushort *) bp; bp += 8;
- weglVertexAttribI4usv(*index,v);
+ driver_send_term(port,caller,rt,AP);
}; break;
case 5577: { // glDrawArraysInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
@@ -4463,7 +4353,7 @@ case 5579: { // glDrawElementsInstanced
GLenum *mode = (GLenum *) bp; bp += 4;
GLsizei *count = (GLsizei *) bp; bp += 4;
GLenum *type = (GLenum *) bp; bp += 4;
- GLvoid *indices = (GLvoid *) bins[0]->base;
+ GLvoid *indices = (GLvoid *) bins[0];
GLsizei *primcount = (GLsizei *) bp; bp += 4;
weglDrawElementsInstanced(*mode,*count,*type,indices,*primcount);
}; break;
@@ -4477,120 +4367,216 @@ case 5581: { // glPrimitiveRestartIndex
GLuint *index = (GLuint *) bp; bp += 4;
weglPrimitiveRestartIndex(*index);
}; break;
-case 5582: { // glLoadTransposeMatrixfARB
+case 5582: { // glGetInteger64i_v
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint64 data[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ weglGetInteger64i_v(*target,*index,data);
+ int AP = 0; ErlDrvTermData rt[39];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint64 *dataTmp = data;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *dataTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5583: { // glGetBufferParameteri64v
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint64 params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ weglGetBufferParameteri64v(*target,*pname,params);
+ int AP = 0; ErlDrvTermData rt[39];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint64 *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5584: { // glFramebufferTexture
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLenum *attachment = (GLenum *) bp; bp += 4;
+ GLuint *texture = (GLuint *) bp; bp += 4;
+ GLint *level = (GLint *) bp; bp += 4;
+ weglFramebufferTexture(*target,*attachment,*texture,*level);
+}; break;
+case 5585: { // glVertexAttribDivisor
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLuint *divisor = (GLuint *) bp; bp += 4;
+ weglVertexAttribDivisor(*index,*divisor);
+}; break;
+case 5586: { // glMinSampleShading
+ GLclampf *value = (GLclampf *) bp; bp += 4;
+ weglMinSampleShading(*value);
+}; break;
+case 5587: { // glBlendEquationi
+ GLuint *buf = (GLuint *) bp; bp += 4;
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ weglBlendEquationi(*buf,*mode);
+}; break;
+case 5588: { // glBlendEquationSeparatei
+ GLuint *buf = (GLuint *) bp; bp += 4;
+ GLenum *modeRGB = (GLenum *) bp; bp += 4;
+ GLenum *modeAlpha = (GLenum *) bp; bp += 4;
+ weglBlendEquationSeparatei(*buf,*modeRGB,*modeAlpha);
+}; break;
+case 5589: { // glBlendFunci
+ GLuint *buf = (GLuint *) bp; bp += 4;
+ GLenum *src = (GLenum *) bp; bp += 4;
+ GLenum *dst = (GLenum *) bp; bp += 4;
+ weglBlendFunci(*buf,*src,*dst);
+}; break;
+case 5590: { // glBlendFuncSeparatei
+ GLuint *buf = (GLuint *) bp; bp += 4;
+ GLenum *srcRGB = (GLenum *) bp; bp += 4;
+ GLenum *dstRGB = (GLenum *) bp; bp += 4;
+ GLenum *srcAlpha = (GLenum *) bp; bp += 4;
+ GLenum *dstAlpha = (GLenum *) bp; bp += 4;
+ weglBlendFuncSeparatei(*buf,*srcRGB,*dstRGB,*srcAlpha,*dstAlpha);
+}; break;
+case 5591: { // glLoadTransposeMatrixfARB
GLfloat * m = (GLfloat *) bp; bp += 64;
weglLoadTransposeMatrixfARB(m);
}; break;
-case 5583: { // glLoadTransposeMatrixdARB
+case 5592: { // glLoadTransposeMatrixdARB
GLdouble * m = (GLdouble *) bp; bp += 128;
weglLoadTransposeMatrixdARB(m);
}; break;
-case 5584: { // glMultTransposeMatrixfARB
+case 5593: { // glMultTransposeMatrixfARB
GLfloat * m = (GLfloat *) bp; bp += 64;
weglMultTransposeMatrixfARB(m);
}; break;
-case 5585: { // glMultTransposeMatrixdARB
+case 5594: { // glMultTransposeMatrixdARB
GLdouble * m = (GLdouble *) bp; bp += 128;
weglMultTransposeMatrixdARB(m);
}; break;
-case 5586: { // glWeightbvARB
+case 5595: { // glWeightbvARB
int * weightsLen = (int *) bp; bp += 4;
GLbyte * weights = (GLbyte *) bp; bp += (8-((*weightsLen*1+4)%8))%8;
weglWeightbvARB(*weightsLen,weights);
}; break;
-case 5587: { // glWeightsvARB
+case 5596: { // glWeightsvARB
int * weightsLen = (int *) bp; bp += 4;
GLshort * weights = (GLshort *) bp; bp += (8-((*weightsLen*2+4)%8))%8;
weglWeightsvARB(*weightsLen,weights);
}; break;
-case 5588: { // glWeightivARB
+case 5597: { // glWeightivARB
int * weightsLen = (int *) bp; bp += 4;
GLint * weights = (GLint *) bp; bp += (8-((*weightsLen*4+4)%8))%8;
weglWeightivARB(*weightsLen,weights);
}; break;
-case 5589: { // glWeightfvARB
+case 5598: { // glWeightfvARB
int * weightsLen = (int *) bp; bp += 4;
GLfloat * weights = (GLfloat *) bp; bp += (8-((*weightsLen*4+4)%8))%8;
weglWeightfvARB(*weightsLen,weights);
}; break;
-case 5590: { // glWeightdvARB
+case 5599: { // glWeightdvARB
int * weightsLen = (int *) bp; bp += 8;
GLdouble * weights = (GLdouble *) bp; bp += (8-((*weightsLen*8+0)%8))%8;
weglWeightdvARB(*weightsLen,weights);
}; break;
-case 5591: { // glWeightubvARB
+case 5600: { // glWeightubvARB
int * weightsLen = (int *) bp; bp += 4;
GLubyte * weights = (GLubyte *) bp; bp += (8-((*weightsLen*1+4)%8))%8;
weglWeightubvARB(*weightsLen,weights);
}; break;
-case 5592: { // glWeightusvARB
+case 5601: { // glWeightusvARB
int * weightsLen = (int *) bp; bp += 4;
GLushort * weights = (GLushort *) bp; bp += (8-((*weightsLen*2+4)%8))%8;
weglWeightusvARB(*weightsLen,weights);
}; break;
-case 5593: { // glWeightuivARB
+case 5602: { // glWeightuivARB
int * weightsLen = (int *) bp; bp += 4;
GLuint * weights = (GLuint *) bp; bp += (8-((*weightsLen*4+4)%8))%8;
weglWeightuivARB(*weightsLen,weights);
}; break;
-case 5594: { // glVertexBlendARB
+case 5603: { // glVertexBlendARB
GLint *count = (GLint *) bp; bp += 4;
weglVertexBlendARB(*count);
}; break;
-case 5595: { // glCurrentPaletteMatrixARB
+case 5604: { // glCurrentPaletteMatrixARB
GLint *index = (GLint *) bp; bp += 4;
weglCurrentPaletteMatrixARB(*index);
}; break;
-case 5596: { // glMatrixIndexubvARB
+case 5605: { // glMatrixIndexubvARB
int * indicesLen = (int *) bp; bp += 4;
GLubyte * indices = (GLubyte *) bp; bp += (8-((*indicesLen*1+4)%8))%8;
weglMatrixIndexubvARB(*indicesLen,indices);
}; break;
-case 5597: { // glMatrixIndexusvARB
+case 5606: { // glMatrixIndexusvARB
int * indicesLen = (int *) bp; bp += 4;
GLushort * indices = (GLushort *) bp; bp += (8-((*indicesLen*2+4)%8))%8;
weglMatrixIndexusvARB(*indicesLen,indices);
}; break;
-case 5598: { // glMatrixIndexuivARB
+case 5607: { // glMatrixIndexuivARB
int * indicesLen = (int *) bp; bp += 4;
GLuint * indices = (GLuint *) bp; bp += (8-((*indicesLen*4+4)%8))%8;
weglMatrixIndexuivARB(*indicesLen,indices);
}; break;
-case 5599: { // glProgramStringARB
+case 5608: { // glProgramStringARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *format = (GLenum *) bp; bp += 4;
GLvoid *string = (GLvoid *) bp;
- int stringLen = strlen((char *)string); bp += stringLen+1+((8-((1+stringLen+0)%8))%8);
- weglProgramStringARB(*target,*format,stringLen,string);
+ int stringLen[1] = {strlen((char *)string)}; bp += stringLen[0]+1+((8-((1+stringLen[0]+0)%8))%8);
+ weglProgramStringARB(*target,*format,*stringLen,string);
}; break;
-case 5600: { // glBindProgramARB
+case 5609: { // glBindProgramARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *program = (GLuint *) bp; bp += 4;
weglBindProgramARB(*target,*program);
}; break;
-case 5601: { // glDeleteProgramsARB
+case 5610: { // glDeleteProgramsARB
int * programsLen = (int *) bp; bp += 4;
GLuint * programs = (GLuint *) bp; bp += (8-((*programsLen*4+4)%8))%8;
weglDeleteProgramsARB(*programsLen,programs);
}; break;
-case 5602: { // glGenProgramsARB
+case 5611: { // glGenProgramsARB
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *programs;
programs = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
weglGenProgramsARB(*n,programs);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) programs[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(programs);
}; break;
-case 5603: { // glProgramEnvParameter4dARB
+case 5612: { // glProgramEnvParameter4dARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
@@ -4599,13 +4585,13 @@ case 5603: { // glProgramEnvParameter4dARB
GLdouble *w = (GLdouble *) bp; bp += 8;
weglProgramEnvParameter4dARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5604: { // glProgramEnvParameter4dvARB
+case 5613: { // glProgramEnvParameter4dvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble * params = (GLdouble *) bp; bp += 32;
weglProgramEnvParameter4dvARB(*target,*index,params);
}; break;
-case 5605: { // glProgramEnvParameter4fARB
+case 5614: { // glProgramEnvParameter4fARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *x = (GLfloat *) bp; bp += 4;
@@ -4614,13 +4600,13 @@ case 5605: { // glProgramEnvParameter4fARB
GLfloat *w = (GLfloat *) bp; bp += 4;
weglProgramEnvParameter4fARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5606: { // glProgramEnvParameter4fvARB
+case 5615: { // glProgramEnvParameter4fvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat * params = (GLfloat *) bp; bp += 16;
weglProgramEnvParameter4fvARB(*target,*index,params);
}; break;
-case 5607: { // glProgramLocalParameter4dARB
+case 5616: { // glProgramLocalParameter4dARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble *x = (GLdouble *) bp; bp += 8;
@@ -4629,13 +4615,13 @@ case 5607: { // glProgramLocalParameter4dARB
GLdouble *w = (GLdouble *) bp; bp += 8;
weglProgramLocalParameter4dARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5608: { // glProgramLocalParameter4dvARB
+case 5617: { // glProgramLocalParameter4dvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble * params = (GLdouble *) bp; bp += 32;
weglProgramLocalParameter4dvARB(*target,*index,params);
}; break;
-case 5609: { // glProgramLocalParameter4fARB
+case 5618: { // glProgramLocalParameter4fARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat *x = (GLfloat *) bp; bp += 4;
@@ -4644,19 +4630,19 @@ case 5609: { // glProgramLocalParameter4fARB
GLfloat *w = (GLfloat *) bp; bp += 4;
weglProgramLocalParameter4fARB(*target,*index,*x,*y,*z,*w);
}; break;
-case 5610: { // glProgramLocalParameter4fvARB
+case 5619: { // glProgramLocalParameter4fvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat * params = (GLfloat *) bp; bp += 16;
weglProgramLocalParameter4fvARB(*target,*index,params);
}; break;
-case 5611: { // glGetProgramEnvParameterdvARB
+case 5620: { // glGetProgramEnvParameterdvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble params[4] = {0.0,0.0,0.0,0.0};
weglGetProgramEnvParameterdvARB(*target,*index,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble *paramsTmp = params;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -4664,16 +4650,15 @@ case 5611: { // glGetProgramEnvParameterdvARB
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5612: { // glGetProgramEnvParameterfvARB
+case 5621: { // glGetProgramEnvParameterfvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetProgramEnvParameterfvARB(*target,*index,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -4682,16 +4667,15 @@ case 5612: { // glGetProgramEnvParameterfvARB
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5613: { // glGetProgramLocalParameterdvARB
+case 5622: { // glGetProgramLocalParameterdvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLdouble params[4] = {0.0,0.0,0.0,0.0};
weglGetProgramLocalParameterdvARB(*target,*index,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble *paramsTmp = params;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -4699,16 +4683,15 @@ case 5613: { // glGetProgramLocalParameterdvARB
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5614: { // glGetProgramLocalParameterfvARB
+case 5623: { // glGetProgramLocalParameterfvARB
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *index = (GLuint *) bp; bp += 4;
GLfloat params[4] = {0.0,0.0,0.0,0.0};
weglGetProgramLocalParameterfvARB(*target,*index,params);
int AP = 0; ErlDrvTermData rt[14];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[4], *paramsTmp = paramsConv;
for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -4717,50 +4700,75 @@ case 5614: { // glGetProgramLocalParameterfvARB
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 14 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,14);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5615: { // glGetProgramStringARB
+case 5624: { // glGetProgramStringARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
- GLvoid *string = (GLvoid *) bins[0]->base;
+ GLvoid *string = (GLvoid *) bins[0];
weglGetProgramStringARB(*target,*pname,string);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5625: { // glGetBufferParameterivARB
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ weglGetBufferParameterivARB(*target,*pname,params);
+ int AP = 0; ErlDrvTermData rt[39];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5616: { // glDeleteObjectARB
+case 5626: { // glDeleteObjectARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglDeleteObjectARB(obj);
}; break;
-case 5617: { // glGetHandleARB
+case 5627: { // glGetHandleARB
GLenum *pname = (GLenum *) bp; bp += 4;
GLhandleARB result = weglGetHandleARB(*pname);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5618: { // glDetachObjectARB
+case 5628: { // glDetachObjectARB
GLhandleARB containerObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLhandleARB attachedObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglDetachObjectARB(containerObj,attachedObj);
}; break;
-case 5619: { // glCreateShaderObjectARB
+case 5629: { // glCreateShaderObjectARB
GLenum *shaderType = (GLenum *) bp; bp += 4;
GLhandleARB result = weglCreateShaderObjectARB(*shaderType);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5620: { // glShaderSourceARB
+case 5630: { // glShaderSourceARB
GLhandleARB shaderObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
int * stringLen = (int *) bp; bp += 4;
int * stringTotSize = (int *) bp; bp += 4;
@@ -4772,62 +4780,59 @@ case 5620: { // glShaderSourceARB
weglShaderSourceARB(shaderObj,*stringLen,(const GLchar **) string,NULL);
driver_free(string);
}; break;
-case 5621: { // glCompileShaderARB
+case 5631: { // glCompileShaderARB
GLhandleARB shaderObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglCompileShaderARB(shaderObj);
}; break;
-case 5622: { // glCreateProgramObjectARB
+case 5632: { // glCreateProgramObjectARB
GLhandleARB result = weglCreateProgramObjectARB();
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5623: { // glAttachObjectARB
+case 5633: { // glAttachObjectARB
GLhandleARB containerObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglAttachObjectARB(containerObj,obj);
}; break;
-case 5624: { // glLinkProgramARB
+case 5634: { // glLinkProgramARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglLinkProgramARB(programObj);
}; break;
-case 5625: { // glUseProgramObjectARB
+case 5635: { // glUseProgramObjectARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglUseProgramObjectARB(programObj);
}; break;
-case 5626: { // glValidateProgramARB
+case 5636: { // glValidateProgramARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
weglValidateProgramARB(programObj);
}; break;
-case 5627: { // glGetObjectParameterfvARB
+case 5637: { // glGetObjectParameterfvARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLenum *pname = (GLenum *) bp; bp += 4;
GLfloat params[1] = {0.0};
weglGetObjectParameterfvARB(obj,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv = (double) *params;
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) &paramsConv;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5628: { // glGetObjectParameterivARB
+case 5638: { // glGetObjectParameterivARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
weglGetObjectParameterivARB(obj,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5629: { // glGetInfoLogARB
+case 5639: { // glGetInfoLogARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -4835,14 +4840,13 @@ case 5629: { // glGetInfoLogARB
infoLog = (GLchar *) driver_alloc(sizeof(GLchar) * *maxLength);
weglGetInfoLogARB(obj,*maxLength,length,infoLog);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) infoLog; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(infoLog);
}; break;
-case 5630: { // glGetAttachedObjectsARB
+case 5640: { // glGetAttachedObjectsARB
GLhandleARB containerObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLsizei *maxCount = (GLsizei *) bp; bp += 4;
GLsizei count[1] = {0};
@@ -4851,29 +4855,27 @@ case 5630: { // glGetAttachedObjectsARB
weglGetAttachedObjectsARB(containerObj,*maxCount,count,obj);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*count)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *count; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) obj[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*count)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*count)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*count)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(obj);
}; break;
-case 5631: { // glGetUniformLocationARB
+case 5641: { // glGetUniformLocationARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+0)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
GLint result = weglGetUniformLocationARB(programObj,name);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5632: { // glGetActiveUniformARB
+case 5642: { // glGetActiveUniformARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
@@ -4884,23 +4886,22 @@ case 5632: { // glGetActiveUniformARB
name = (GLchar *) driver_alloc(sizeof(GLchar) * *maxLength);
weglGetActiveUniformARB(programObj,*index,*maxLength,length,size,type,name);
int AP = 0; ErlDrvTermData rt[13];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *size;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *type;
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 13 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,13);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5633: { // glGetUniformfvARB
+case 5643: { // glGetUniformfvARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLint *location = (GLint *) bp; bp += 4;
GLfloat params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
weglGetUniformfvARB(programObj,*location,params);
int AP = 0; ErlDrvTermData rt[38];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLdouble paramsConv[16], *paramsTmp = paramsConv;
for(int i=0; i < 16; i++) paramsConv[i] = (GLdouble) params[i];
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
@@ -4921,16 +4922,15 @@ case 5633: { // glGetUniformfvARB
rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 38 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,38);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5634: { // glGetUniformivARB
+case 5644: { // glGetUniformivARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLint *location = (GLint *) bp; bp += 4;
GLint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
weglGetUniformivARB(programObj,*location,params);
int AP = 0; ErlDrvTermData rt[38];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
GLint *paramsTmp = params;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
@@ -4950,10 +4950,9 @@ case 5634: { // glGetUniformivARB
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 38 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,38);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5635: { // glGetShaderSourceARB
+case 5645: { // glGetShaderSourceARB
GLhandleARB obj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
GLsizei length[1] = {0};
@@ -4961,21 +4960,20 @@ case 5635: { // glGetShaderSourceARB
source = (GLchar *) driver_alloc(sizeof(GLchar) * *maxLength);
weglGetShaderSourceARB(obj,*maxLength,length,source);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) source; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(source);
}; break;
-case 5636: { // glBindAttribLocationARB
+case 5646: { // glBindAttribLocationARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLuint *index = (GLuint *) bp; bp += 4;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+4)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
weglBindAttribLocationARB(programObj,*index,name);
}; break;
-case 5637: { // glGetActiveAttribARB
+case 5647: { // glGetActiveAttribARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLuint *index = (GLuint *) bp; bp += 4;
GLsizei *maxLength = (GLsizei *) bp; bp += 4;
@@ -4986,132 +4984,124 @@ case 5637: { // glGetActiveAttribARB
name = (GLchar *) driver_alloc(sizeof(GLchar) * *maxLength);
weglGetActiveAttribARB(programObj,*index,*maxLength,length,size,type,name);
int AP = 0; ErlDrvTermData rt[13];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *size;
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *type;
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 13 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,13);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(name);
}; break;
-case 5638: { // glGetAttribLocationARB
+case 5648: { // glGetAttribLocationARB
GLhandleARB programObj = (GLhandleARB) * (GLuint64EXT *) bp; bp += 8;
GLchar *name = (GLchar *) bp;
- int nameLen = strlen((char *)name); bp += nameLen+1+((8-((1+nameLen+0)%8))%8);
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
GLint result = weglGetAttribLocationARB(programObj,name);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5639: { // glIsRenderbuffer
+case 5649: { // glIsRenderbuffer
GLuint *renderbuffer = (GLuint *) bp; bp += 4;
GLboolean result = weglIsRenderbuffer(*renderbuffer);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5640: { // glBindRenderbuffer
+case 5650: { // glBindRenderbuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *renderbuffer = (GLuint *) bp; bp += 4;
weglBindRenderbuffer(*target,*renderbuffer);
}; break;
-case 5641: { // glDeleteRenderbuffers
+case 5651: { // glDeleteRenderbuffers
int * renderbuffersLen = (int *) bp; bp += 4;
GLuint * renderbuffers = (GLuint *) bp; bp += (8-((*renderbuffersLen*4+4)%8))%8;
weglDeleteRenderbuffers(*renderbuffersLen,renderbuffers);
}; break;
-case 5642: { // glGenRenderbuffers
+case 5652: { // glGenRenderbuffers
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *renderbuffers;
renderbuffers = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
weglGenRenderbuffers(*n,renderbuffers);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) renderbuffers[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(renderbuffers);
}; break;
-case 5643: { // glRenderbufferStorage
+case 5653: { // glRenderbufferStorage
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
GLsizei *width = (GLsizei *) bp; bp += 4;
GLsizei *height = (GLsizei *) bp; bp += 4;
weglRenderbufferStorage(*target,*internalformat,*width,*height);
}; break;
-case 5644: { // glGetRenderbufferParameteriv
+case 5654: { // glGetRenderbufferParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
weglGetRenderbufferParameteriv(*target,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5645: { // glIsFramebuffer
+case 5655: { // glIsFramebuffer
GLuint *framebuffer = (GLuint *) bp; bp += 4;
GLboolean result = weglIsFramebuffer(*framebuffer);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5646: { // glBindFramebuffer
+case 5656: { // glBindFramebuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLuint *framebuffer = (GLuint *) bp; bp += 4;
weglBindFramebuffer(*target,*framebuffer);
}; break;
-case 5647: { // glDeleteFramebuffers
+case 5657: { // glDeleteFramebuffers
int * framebuffersLen = (int *) bp; bp += 4;
GLuint * framebuffers = (GLuint *) bp; bp += (8-((*framebuffersLen*4+4)%8))%8;
weglDeleteFramebuffers(*framebuffersLen,framebuffers);
}; break;
-case 5648: { // glGenFramebuffers
+case 5658: { // glGenFramebuffers
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *framebuffers;
framebuffers = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
weglGenFramebuffers(*n,framebuffers);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) framebuffers[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(framebuffers);
}; break;
-case 5649: { // glCheckFramebufferStatus
+case 5659: { // glCheckFramebufferStatus
GLenum *target = (GLenum *) bp; bp += 4;
GLenum result = weglCheckFramebufferStatus(*target);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5650: { // glFramebufferTexture1D
+case 5660: { // glFramebufferTexture1D
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *textarget = (GLenum *) bp; bp += 4;
@@ -5119,7 +5109,7 @@ case 5650: { // glFramebufferTexture1D
GLint *level = (GLint *) bp; bp += 4;
weglFramebufferTexture1D(*target,*attachment,*textarget,*texture,*level);
}; break;
-case 5651: { // glFramebufferTexture2D
+case 5661: { // glFramebufferTexture2D
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *textarget = (GLenum *) bp; bp += 4;
@@ -5127,7 +5117,7 @@ case 5651: { // glFramebufferTexture2D
GLint *level = (GLint *) bp; bp += 4;
weglFramebufferTexture2D(*target,*attachment,*textarget,*texture,*level);
}; break;
-case 5652: { // glFramebufferTexture3D
+case 5662: { // glFramebufferTexture3D
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *textarget = (GLenum *) bp; bp += 4;
@@ -5136,31 +5126,30 @@ case 5652: { // glFramebufferTexture3D
GLint *zoffset = (GLint *) bp; bp += 4;
weglFramebufferTexture3D(*target,*attachment,*textarget,*texture,*level,*zoffset);
}; break;
-case 5653: { // glFramebufferRenderbuffer
+case 5663: { // glFramebufferRenderbuffer
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *renderbuffertarget = (GLenum *) bp; bp += 4;
GLuint *renderbuffer = (GLuint *) bp; bp += 4;
weglFramebufferRenderbuffer(*target,*attachment,*renderbuffertarget,*renderbuffer);
}; break;
-case 5654: { // glGetFramebufferAttachmentParameteriv
+case 5664: { // glGetFramebufferAttachmentParameteriv
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
GLint params[1] = {0};
weglGetFramebufferAttachmentParameteriv(*target,*attachment,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5655: { // glGenerateMipmap
+case 5665: { // glGenerateMipmap
GLenum *target = (GLenum *) bp; bp += 4;
weglGenerateMipmap(*target);
}; break;
-case 5656: { // glBlitFramebuffer
+case 5666: { // glBlitFramebuffer
GLint *srcX0 = (GLint *) bp; bp += 4;
GLint *srcY0 = (GLint *) bp; bp += 4;
GLint *srcX1 = (GLint *) bp; bp += 4;
@@ -5173,7 +5162,7 @@ case 5656: { // glBlitFramebuffer
GLenum *filter = (GLenum *) bp; bp += 4;
weglBlitFramebuffer(*srcX0,*srcY0,*srcX1,*srcY1,*dstX0,*dstY0,*dstX1,*dstY1,*mask,*filter);
}; break;
-case 5657: { // glRenderbufferStorageMultisample
+case 5667: { // glRenderbufferStorageMultisample
GLenum *target = (GLenum *) bp; bp += 4;
GLsizei *samples = (GLsizei *) bp; bp += 4;
GLenum *internalformat = (GLenum *) bp; bp += 4;
@@ -5181,7 +5170,7 @@ case 5657: { // glRenderbufferStorageMultisample
GLsizei *height = (GLsizei *) bp; bp += 4;
weglRenderbufferStorageMultisample(*target,*samples,*internalformat,*width,*height);
}; break;
-case 5658: { // glFramebufferTextureLayer
+case 5668: { // glFramebufferTextureLayer
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLuint *texture = (GLuint *) bp; bp += 4;
@@ -5189,20 +5178,7 @@ case 5658: { // glFramebufferTextureLayer
GLint *layer = (GLint *) bp; bp += 4;
weglFramebufferTextureLayer(*target,*attachment,*texture,*level,*layer);
}; break;
-case 5659: { // glProgramParameteriARB
- GLuint *program = (GLuint *) bp; bp += 4;
- GLenum *pname = (GLenum *) bp; bp += 4;
- GLint *value = (GLint *) bp; bp += 4;
- weglProgramParameteriARB(*program,*pname,*value);
-}; break;
-case 5660: { // glFramebufferTextureARB
- GLenum *target = (GLenum *) bp; bp += 4;
- GLenum *attachment = (GLenum *) bp; bp += 4;
- GLuint *texture = (GLuint *) bp; bp += 4;
- GLint *level = (GLint *) bp; bp += 4;
- weglFramebufferTextureARB(*target,*attachment,*texture,*level);
-}; break;
-case 5661: { // glFramebufferTextureFaceARB
+case 5669: { // glFramebufferTextureFaceARB
GLenum *target = (GLenum *) bp; bp += 4;
GLenum *attachment = (GLenum *) bp; bp += 4;
GLuint *texture = (GLuint *) bp; bp += 4;
@@ -5210,55 +5186,48 @@ case 5661: { // glFramebufferTextureFaceARB
GLenum *face = (GLenum *) bp; bp += 4;
weglFramebufferTextureFaceARB(*target,*attachment,*texture,*level,*face);
}; break;
-case 5662: { // glVertexAttribDivisorARB
- GLuint *index = (GLuint *) bp; bp += 4;
- GLuint *divisor = (GLuint *) bp; bp += 4;
- weglVertexAttribDivisorARB(*index,*divisor);
-}; break;
-case 5663: { // glFlushMappedBufferRange
+case 5670: { // glFlushMappedBufferRange
GLenum *target = (GLenum *) bp; bp += 4;
bp += 4;
GLintptr offset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
GLsizeiptr length = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
weglFlushMappedBufferRange(*target,offset,length);
}; break;
-case 5664: { // glBindVertexArray
+case 5671: { // glBindVertexArray
GLuint *array = (GLuint *) bp; bp += 4;
weglBindVertexArray(*array);
}; break;
-case 5665: { // glDeleteVertexArrays
+case 5672: { // glDeleteVertexArrays
int * arraysLen = (int *) bp; bp += 4;
GLuint * arrays = (GLuint *) bp; bp += (8-((*arraysLen*4+4)%8))%8;
weglDeleteVertexArrays(*arraysLen,arrays);
}; break;
-case 5666: { // glGenVertexArrays
+case 5673: { // glGenVertexArrays
GLsizei *n = (GLsizei *) bp; bp += 4;
GLuint *arrays;
arrays = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
weglGenVertexArrays(*n,arrays);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *n; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) arrays[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*n)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*n)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(arrays);
}; break;
-case 5667: { // glIsVertexArray
+case 5674: { // glIsVertexArray
GLuint *array = (GLuint *) bp; bp += 4;
GLboolean result = weglIsVertexArray(*array);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5668: { // glGetUniformIndices
+case 5675: { // glGetUniformIndices
GLuint *program = (GLuint *) bp; bp += 4;
int * uniformNamesLen = (int *) bp; bp += 4;
int * uniformNamesTotSize = (int *) bp; bp += 4;
@@ -5272,18 +5241,17 @@ case 5668: { // glGetUniformIndices
weglGetUniformIndices(*program,*uniformNamesLen,(const GLchar **) uniformNames,uniformIndices);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*uniformNamesLen)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *uniformNamesLen; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) uniformIndices[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*uniformNamesLen)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*uniformNamesLen)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*uniformNamesLen)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(uniformIndices);
driver_free(uniformNames);
}; break;
-case 5669: { // glGetActiveUniformsiv
+case 5676: { // glGetActiveUniformsiv
GLuint *program = (GLuint *) bp; bp += 4;
int * uniformIndicesLen = (int *) bp; bp += 4;
GLuint * uniformIndices = (GLuint *) bp; bp += (8-((*uniformIndicesLen*4+0)%8))%8;
@@ -5293,17 +5261,16 @@ case 5669: { // glGetActiveUniformsiv
weglGetActiveUniformsiv(*program,*uniformIndicesLen,uniformIndices,*pname,params);
int AP = 0; ErlDrvTermData *rt;
rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*uniformIndicesLen)*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
for(int i=0; i < *uniformIndicesLen; i++) {
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) params[i];}
rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*uniformIndicesLen)+1;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 + (*uniformIndicesLen)*2 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7 + (*uniformIndicesLen)*2);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(rt);
driver_free(params);
}; break;
-case 5670: { // glGetActiveUniformName
+case 5677: { // glGetActiveUniformName
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformIndex = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -5312,38 +5279,36 @@ case 5670: { // glGetActiveUniformName
uniformName = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetActiveUniformName(*program,*uniformIndex,*bufSize,length,uniformName);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) uniformName; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(uniformName);
}; break;
-case 5671: { // glGetUniformBlockIndex
+case 5678: { // glGetUniformBlockIndex
GLuint *program = (GLuint *) bp; bp += 4;
GLchar *uniformBlockName = (GLchar *) bp;
- int uniformBlockNameLen = strlen((char *)uniformBlockName); bp += uniformBlockNameLen+1+((8-((1+uniformBlockNameLen+4)%8))%8);
+ int uniformBlockNameLen[1] = {strlen((char *)uniformBlockName)}; bp += uniformBlockNameLen[0]+1+((8-((1+uniformBlockNameLen[0]+4)%8))%8);
GLuint result = weglGetUniformBlockIndex(*program,uniformBlockName);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 6 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,6);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5672: { // glGetActiveUniformBlockiv
+case 5679: { // glGetActiveUniformBlockiv
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformBlockIndex = (GLuint *) bp; bp += 4;
GLenum *pname = (GLenum *) bp; bp += 4;
- GLint *params = (GLint *) bins[0]->base;
+ GLint *params = (GLint *) bins[0];
weglGetActiveUniformBlockiv(*program,*uniformBlockIndex,*pname,params);
int AP = 0; ErlDrvTermData rt[6];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "ok");
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
}; break;
-case 5673: { // glGetActiveUniformBlockName
+case 5680: { // glGetActiveUniformBlockName
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformBlockIndex = (GLuint *) bp; bp += 4;
GLsizei *bufSize = (GLsizei *) bp; bp += 4;
@@ -5352,20 +5317,19 @@ case 5673: { // glGetActiveUniformBlockName
uniformBlockName = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
weglGetActiveUniformBlockName(*program,*uniformBlockIndex,*bufSize,length,uniformBlockName);
int AP = 0; ErlDrvTermData rt[7];
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) uniformBlockName; rt[AP++] = *length;
rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
- if (AP != 7 ) fprintf(stderr, "%d: ERROR AP mismatch %d %d\r\n",__LINE__,AP,7);
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
+ driver_send_term(port,caller,rt,AP);
driver_free(uniformBlockName);
}; break;
-case 5674: { // glUniformBlockBinding
+case 5681: { // glUniformBlockBinding
GLuint *program = (GLuint *) bp; bp += 4;
GLuint *uniformBlockIndex = (GLuint *) bp; bp += 4;
GLuint *uniformBlockBinding = (GLuint *) bp; bp += 4;
weglUniformBlockBinding(*program,*uniformBlockIndex,*uniformBlockBinding);
}; break;
-case 5675: { // glCopyBufferSubData
+case 5682: { // glCopyBufferSubData
GLenum *readTarget = (GLenum *) bp; bp += 4;
GLenum *writeTarget = (GLenum *) bp; bp += 4;
GLintptr readOffset = (GLintptr) * (GLuint64EXT *) bp; bp += 8;
@@ -5373,34 +5337,1633 @@ case 5675: { // glCopyBufferSubData
GLsizeiptr size = (GLsizeiptr) * (GLuint64EXT *) bp; bp += 8;
weglCopyBufferSubData(*readTarget,*writeTarget,readOffset,writeOffset,size);
}; break;
-case 5676: { // glResizeBuffersMESA
+case 5683: { // glDrawElementsBaseVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indices = (GLvoid *) * (int *) bp; bp += 4;
+ GLint *basevertex = (GLint *) bp; bp += 4;
+ weglDrawElementsBaseVertex(*mode,*count,*type,indices,*basevertex);
+}; break;
+case 5684: { // glDrawElementsBaseVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indices = (GLvoid *) bins[0];
+ GLint *basevertex = (GLint *) bp; bp += 4;
+ weglDrawElementsBaseVertex(*mode,*count,*type,indices,*basevertex);
+}; break;
+case 5685: { // glDrawRangeElementsBaseVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLuint *start = (GLuint *) bp; bp += 4;
+ GLuint *end = (GLuint *) bp; bp += 4;
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indices = (GLvoid *) * (int *) bp; bp += 4;
+ GLint *basevertex = (GLint *) bp; bp += 4;
+ weglDrawRangeElementsBaseVertex(*mode,*start,*end,*count,*type,indices,*basevertex);
+}; break;
+case 5686: { // glDrawRangeElementsBaseVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLuint *start = (GLuint *) bp; bp += 4;
+ GLuint *end = (GLuint *) bp; bp += 4;
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indices = (GLvoid *) bins[0];
+ GLint *basevertex = (GLint *) bp; bp += 4;
+ weglDrawRangeElementsBaseVertex(*mode,*start,*end,*count,*type,indices,*basevertex);
+}; break;
+case 5687: { // glDrawElementsInstancedBaseVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indices = (GLvoid *) * (int *) bp; bp += 4;
+ GLsizei *primcount = (GLsizei *) bp; bp += 4;
+ GLint *basevertex = (GLint *) bp; bp += 4;
+ weglDrawElementsInstancedBaseVertex(*mode,*count,*type,indices,*primcount,*basevertex);
+}; break;
+case 5688: { // glDrawElementsInstancedBaseVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indices = (GLvoid *) bins[0];
+ GLsizei *primcount = (GLsizei *) bp; bp += 4;
+ GLint *basevertex = (GLint *) bp; bp += 4;
+ weglDrawElementsInstancedBaseVertex(*mode,*count,*type,indices,*primcount,*basevertex);
+}; break;
+case 5689: { // glProvokingVertex
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ weglProvokingVertex(*mode);
+}; break;
+case 5690: { // glFenceSync
+ GLenum *condition = (GLenum *) bp; bp += 4;
+ GLbitfield *flags = (GLbitfield *) bp; bp += 4;
+ GLsync result = weglFenceSync(*condition,*flags);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5691: { // glIsSync
+ GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
+ GLboolean result = weglIsSync(sync);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5692: { // glDeleteSync
+ GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
+ weglDeleteSync(sync);
+}; break;
+case 5693: { // glClientWaitSync
+ GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
+ GLbitfield *flags = (GLbitfield *) bp; bp += 4;
+ bp += 4;
+ GLuint64 timeout = (GLuint64) * (GLuint64EXT *) bp; bp += 8;
+ GLenum result = weglClientWaitSync(sync,*flags,timeout);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5694: { // glWaitSync
+ GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
+ GLbitfield *flags = (GLbitfield *) bp; bp += 4;
+ bp += 4;
+ GLuint64 timeout = (GLuint64) * (GLuint64EXT *) bp; bp += 8;
+ weglWaitSync(sync,*flags,timeout);
+}; break;
+case 5695: { // glGetInteger64v
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint64 params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ weglGetInteger64v(*pname,params);
+ int AP = 0; ErlDrvTermData rt[39];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint64 *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5696: { // glGetSynciv
+ GLsync sync = (GLsync) * (GLuint64EXT *) bp; bp += 8;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLsizei *bufSize = (GLsizei *) bp; bp += 4;
+ GLsizei length[1] = {0};
+ GLint *values;
+ values = (GLint *) driver_alloc(sizeof(GLint) * *bufSize);
+ weglGetSynciv(sync,*pname,*bufSize,length,values);
+ int AP = 0; ErlDrvTermData *rt;
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*length)*2));
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ for(int i=0; i < *length; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) values[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*length)+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(rt);
+ driver_free(values);
+}; break;
+case 5697: { // glTexImage2DMultisample
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLsizei *samples = (GLsizei *) bp; bp += 4;
+ GLint *internalformat = (GLint *) bp; bp += 4;
+ GLsizei *width = (GLsizei *) bp; bp += 4;
+ GLsizei *height = (GLsizei *) bp; bp += 4;
+ GLboolean *fixedsamplelocations = (GLboolean *) bp; bp += 1;
+ weglTexImage2DMultisample(*target,*samples,*internalformat,*width,*height,*fixedsamplelocations);
+}; break;
+case 5698: { // glTexImage3DMultisample
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLsizei *samples = (GLsizei *) bp; bp += 4;
+ GLint *internalformat = (GLint *) bp; bp += 4;
+ GLsizei *width = (GLsizei *) bp; bp += 4;
+ GLsizei *height = (GLsizei *) bp; bp += 4;
+ GLsizei *depth = (GLsizei *) bp; bp += 4;
+ GLboolean *fixedsamplelocations = (GLboolean *) bp; bp += 1;
+ weglTexImage3DMultisample(*target,*samples,*internalformat,*width,*height,*depth,*fixedsamplelocations);
+}; break;
+case 5699: { // glGetMultisamplefv
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLfloat val[2] = {0.0,0.0};
+ weglGetMultisamplefv(*pname,*index,val);
+ int AP = 0; ErlDrvTermData rt[10];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLdouble valConv[2], *valTmp = valConv;
+ for(int i=0; i < 2; i++) valConv[i] = (GLdouble) val[i];
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) valTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) valTmp++;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5700: { // glSampleMaski
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLbitfield *mask = (GLbitfield *) bp; bp += 4;
+ weglSampleMaski(*index,*mask);
+}; break;
+case 5701: { // glNamedStringARB
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
+ GLchar *string = (GLchar *) bp;
+ int stringLen[1] = {strlen((char *)string)}; bp += stringLen[0]+1+((8-((1+stringLen[0]+0)%8))%8);
+ weglNamedStringARB(*type,*nameLen,name,*stringLen,string);
+}; break;
+case 5702: { // glDeleteNamedStringARB
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
+ weglDeleteNamedStringARB(*nameLen,name);
+}; break;
+case 5703: { // glCompileShaderIncludeARB
+ GLuint *shader = (GLuint *) bp; bp += 4;
+ int * pathLen = (int *) bp; bp += 4;
+ int * pathTotSize = (int *) bp; bp += 4;
+ GLchar **path;
+ path = (GLchar **) driver_alloc(sizeof(GLchar *) * *pathLen);
+ for(int i=0;i<*pathLen;i++) {
+ path[i] = (GLchar *) bp; bp += 1+strlen(bp);};
+ bp += (8 - ((0 + *pathTotSize) % 8)) % 8;
+ weglCompileShaderIncludeARB(*shader,*pathLen,(const GLchar **) path,NULL);
+ driver_free(path);
+}; break;
+case 5704: { // glIsNamedStringARB
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
+ GLboolean result = weglIsNamedStringARB(*nameLen,name);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5705: { // glGetNamedStringARB
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
+ GLsizei *bufSize = (GLsizei *) bp; bp += 4;
+ GLint stringlen[1] = {0};
+ GLchar *string;
+ string = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
+ weglGetNamedStringARB(*nameLen,name,*bufSize,stringlen,string);
+ int AP = 0; ErlDrvTermData rt[7];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) string; rt[AP++] = *stringlen;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(string);
+}; break;
+case 5706: { // glGetNamedStringivARB
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint params[1] = {0};
+ weglGetNamedStringivARB(*nameLen,name,*pname,params);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5707: { // glBindFragDataLocationIndexed
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLuint *colorNumber = (GLuint *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
+ weglBindFragDataLocationIndexed(*program,*colorNumber,*index,name);
+}; break;
+case 5708: { // glGetFragDataIndex
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+4)%8))%8);
+ GLint result = weglGetFragDataIndex(*program,name);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5709: { // glGenSamplers
+ GLsizei *count = (GLsizei *) bp; bp += 4;
+ GLuint *samplers;
+ samplers = (GLuint *) driver_alloc(sizeof(GLuint) * *count);
+ weglGenSamplers(*count,samplers);
+ int AP = 0; ErlDrvTermData *rt;
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*count)*2));
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ for(int i=0; i < *count; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) samplers[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*count)+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(rt);
+ driver_free(samplers);
+}; break;
+case 5710: { // glDeleteSamplers
+ int * samplersLen = (int *) bp; bp += 4;
+ GLuint * samplers = (GLuint *) bp; bp += (8-((*samplersLen*4+4)%8))%8;
+ weglDeleteSamplers(*samplersLen,samplers);
+}; break;
+case 5711: { // glIsSampler
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLboolean result = weglIsSampler(*sampler);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5712: { // glBindSampler
+ GLuint *unit = (GLuint *) bp; bp += 4;
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ weglBindSampler(*unit,*sampler);
+}; break;
+case 5713: { // glSamplerParameteri
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint *param = (GLint *) bp; bp += 4;
+ weglSamplerParameteri(*sampler,*pname,*param);
+}; break;
+case 5714: { // glSamplerParameteriv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ int * paramLen = (int *) bp; bp += 4;
+ GLint * param = (GLint *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ weglSamplerParameteriv(*sampler,*pname,param);
+}; break;
+case 5715: { // glSamplerParameterf
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLfloat *param = (GLfloat *) bp; bp += 4;
+ weglSamplerParameterf(*sampler,*pname,*param);
+}; break;
+case 5716: { // glSamplerParameterfv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ int * paramLen = (int *) bp; bp += 4;
+ GLfloat * param = (GLfloat *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ weglSamplerParameterfv(*sampler,*pname,param);
+}; break;
+case 5717: { // glSamplerParameterIiv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ int * paramLen = (int *) bp; bp += 4;
+ GLint * param = (GLint *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ weglSamplerParameterIiv(*sampler,*pname,param);
+}; break;
+case 5718: { // glSamplerParameterIuiv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ int * paramLen = (int *) bp; bp += 4;
+ GLuint * param = (GLuint *) bp; bp += (8-((*paramLen*4+4)%8))%8;
+ weglSamplerParameterIuiv(*sampler,*pname,param);
+}; break;
+case 5719: { // glGetSamplerParameteriv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint params[4] = {0,0,0,0};
+ weglGetSamplerParameteriv(*sampler,*pname,params);
+ int AP = 0; ErlDrvTermData rt[15];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 4+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5720: { // glGetSamplerParameterIiv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint params[4] = {0,0,0,0};
+ weglGetSamplerParameterIiv(*sampler,*pname,params);
+ int AP = 0; ErlDrvTermData rt[15];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 4+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5721: { // glGetSamplerParameterfv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLfloat params[4] = {0.0,0.0,0.0,0.0};
+ weglGetSamplerParameterfv(*sampler,*pname,params);
+ int AP = 0; ErlDrvTermData rt[15];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLdouble paramsConv[4], *paramsTmp = paramsConv;
+ for(int i=0; i < 4; i++) paramsConv[i] = (GLdouble) params[i];
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 4+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5722: { // glGetSamplerParameterIuiv
+ GLuint *sampler = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLuint params[4] = {0,0,0,0};
+ weglGetSamplerParameterIuiv(*sampler,*pname,params);
+ int AP = 0; ErlDrvTermData rt[15];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLuint *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 4+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5723: { // glQueryCounter
+ GLuint *id = (GLuint *) bp; bp += 4;
+ GLenum *target = (GLenum *) bp; bp += 4;
+ weglQueryCounter(*id,*target);
+}; break;
+case 5724: { // glGetQueryObjecti64v
+ GLuint *id = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint64 params[1] = {0};
+ weglGetQueryObjecti64v(*id,*pname,params);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5725: { // glGetQueryObjectui64v
+ GLuint *id = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLuint64 params[1] = {0};
+ weglGetQueryObjectui64v(*id,*pname,params);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5726: { // glDrawArraysIndirect
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLvoid *indirect = (GLvoid *) * (int *) bp; bp += 4;
+ weglDrawArraysIndirect(*mode,indirect);
+}; break;
+case 5727: { // glDrawArraysIndirect
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLvoid *indirect = (GLvoid *) bins[0];
+ weglDrawArraysIndirect(*mode,indirect);
+}; break;
+case 5728: { // glDrawElementsIndirect
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indirect = (GLvoid *) * (int *) bp; bp += 4;
+ weglDrawElementsIndirect(*mode,*type,indirect);
+}; break;
+case 5729: { // glDrawElementsIndirect
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLvoid *indirect = (GLvoid *) bins[0];
+ weglDrawElementsIndirect(*mode,*type,indirect);
+}; break;
+case 5730: { // glUniform1d
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *x = (GLdouble *) bp; bp += 8;
+ weglUniform1d(*location,*x);
+}; break;
+case 5731: { // glUniform2d
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *x = (GLdouble *) bp; bp += 8;
+ GLdouble *y = (GLdouble *) bp; bp += 8;
+ weglUniform2d(*location,*x,*y);
+}; break;
+case 5732: { // glUniform3d
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *x = (GLdouble *) bp; bp += 8;
+ GLdouble *y = (GLdouble *) bp; bp += 8;
+ GLdouble *z = (GLdouble *) bp; bp += 8;
+ weglUniform3d(*location,*x,*y,*z);
+}; break;
+case 5733: { // glUniform4d
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *x = (GLdouble *) bp; bp += 8;
+ GLdouble *y = (GLdouble *) bp; bp += 8;
+ GLdouble *z = (GLdouble *) bp; bp += 8;
+ GLdouble *w = (GLdouble *) bp; bp += 8;
+ weglUniform4d(*location,*x,*y,*z,*w);
+}; break;
+case 5734: { // glUniform1dv
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ int * valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += (8-((*valueLen*8+0)%8))%8;
+ weglUniform1dv(*location,*valueLen,value);
+}; break;
+case 5735: { // glUniform2dv
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*16;
+ weglUniform2dv(*location,*valueLen,value);
+}; break;
+case 5736: { // glUniform3dv
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*24;
+ weglUniform3dv(*location,*valueLen,value);
+}; break;
+case 5737: { // glUniform4dv
+ GLint *location = (GLint *) bp; bp += 4;
+ bp += 4;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
+ weglUniform4dv(*location,*valueLen,value);
+}; break;
+case 5738: { // glUniformMatrix2dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
+ weglUniformMatrix2dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5739: { // glUniformMatrix3dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*72;
+ weglUniformMatrix3dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5740: { // glUniformMatrix4dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*128;
+ weglUniformMatrix4dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5741: { // glUniformMatrix2x3dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
+ weglUniformMatrix2x3dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5742: { // glUniformMatrix2x4dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
+ weglUniformMatrix2x4dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5743: { // glUniformMatrix3x2dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
+ weglUniformMatrix3x2dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5744: { // glUniformMatrix3x4dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
+ weglUniformMatrix3x4dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5745: { // glUniformMatrix4x2dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
+ weglUniformMatrix4x2dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5746: { // glUniformMatrix4x3dv
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
+ weglUniformMatrix4x3dv(*location,*valueLen,*transpose,value);
+}; break;
+case 5747: { // glGetUniformdv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLdouble params[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+ weglGetUniformdv(*program,*location,params);
+ int AP = 0; ErlDrvTermData rt[38];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLdouble *paramsTmp = params;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5748: { // glGetSubroutineUniformLocation
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
+ GLint result = weglGetSubroutineUniformLocation(*program,*shadertype,name);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5749: { // glGetSubroutineIndex
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLchar *name = (GLchar *) bp;
+ int nameLen[1] = {strlen((char *)name)}; bp += nameLen[0]+1+((8-((1+nameLen[0]+0)%8))%8);
+ GLuint result = weglGetSubroutineIndex(*program,*shadertype,name);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5750: { // glGetActiveSubroutineUniformName
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLsizei *bufsize = (GLsizei *) bp; bp += 4;
+ GLsizei length[1] = {0};
+ GLchar *name;
+ name = (GLchar *) driver_alloc(sizeof(GLchar) * *bufsize);
+ weglGetActiveSubroutineUniformName(*program,*shadertype,*index,*bufsize,length,name);
+ int AP = 0; ErlDrvTermData rt[7];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(name);
+}; break;
+case 5751: { // glGetActiveSubroutineName
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLsizei *bufsize = (GLsizei *) bp; bp += 4;
+ GLsizei length[1] = {0};
+ GLchar *name;
+ name = (GLchar *) driver_alloc(sizeof(GLchar) * *bufsize);
+ weglGetActiveSubroutineName(*program,*shadertype,*index,*bufsize,length,name);
+ int AP = 0; ErlDrvTermData rt[7];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) name; rt[AP++] = *length;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(name);
+}; break;
+case 5752: { // glUniformSubroutinesuiv
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ int * indicesLen = (int *) bp; bp += 4;
+ GLuint * indices = (GLuint *) bp; bp += (8-((*indicesLen*4+0)%8))%8;
+ weglUniformSubroutinesuiv(*shadertype,*indicesLen,indices);
+}; break;
+case 5753: { // glGetUniformSubroutineuiv
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLuint params[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ weglGetUniformSubroutineuiv(*shadertype,*location,params);
+ int AP = 0; ErlDrvTermData rt[38];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLuint *paramsTmp = params;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *paramsTmp++;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 16;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5754: { // glGetProgramStageiv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint values[1] = {0};
+ weglGetProgramStageiv(*program,*shadertype,*pname,values);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *values;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5755: { // glPatchParameteri
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint *value = (GLint *) bp; bp += 4;
+ weglPatchParameteri(*pname,*value);
+}; break;
+case 5756: { // glPatchParameterfv
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ int * valuesLen = (int *) bp; bp += 4;
+ GLfloat * values = (GLfloat *) bp; bp += (8-((*valuesLen*4+0)%8))%8;
+ weglPatchParameterfv(*pname,values);
+}; break;
+case 5757: { // glBindTransformFeedback
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *id = (GLuint *) bp; bp += 4;
+ weglBindTransformFeedback(*target,*id);
+}; break;
+case 5758: { // glDeleteTransformFeedbacks
+ int * idsLen = (int *) bp; bp += 4;
+ GLuint * ids = (GLuint *) bp; bp += (8-((*idsLen*4+4)%8))%8;
+ weglDeleteTransformFeedbacks(*idsLen,ids);
+}; break;
+case 5759: { // glGenTransformFeedbacks
+ GLsizei *n = (GLsizei *) bp; bp += 4;
+ GLuint *ids;
+ ids = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
+ weglGenTransformFeedbacks(*n,ids);
+ int AP = 0; ErlDrvTermData *rt;
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ for(int i=0; i < *n; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) ids[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(rt);
+ driver_free(ids);
+}; break;
+case 5760: { // glIsTransformFeedback
+ GLuint *id = (GLuint *) bp; bp += 4;
+ GLboolean result = weglIsTransformFeedback(*id);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5761: { // glPauseTransformFeedback
+ weglPauseTransformFeedback();
+}; break;
+case 5762: { // glResumeTransformFeedback
+ weglResumeTransformFeedback();
+}; break;
+case 5763: { // glDrawTransformFeedback
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLuint *id = (GLuint *) bp; bp += 4;
+ weglDrawTransformFeedback(*mode,*id);
+}; break;
+case 5764: { // glDrawTransformFeedbackStream
+ GLenum *mode = (GLenum *) bp; bp += 4;
+ GLuint *id = (GLuint *) bp; bp += 4;
+ GLuint *stream = (GLuint *) bp; bp += 4;
+ weglDrawTransformFeedbackStream(*mode,*id,*stream);
+}; break;
+case 5765: { // glBeginQueryIndexed
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLuint *id = (GLuint *) bp; bp += 4;
+ weglBeginQueryIndexed(*target,*index,*id);
+}; break;
+case 5766: { // glEndQueryIndexed
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ weglEndQueryIndexed(*target,*index);
+}; break;
+case 5767: { // glGetQueryIndexediv
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint params[1] = {0};
+ weglGetQueryIndexediv(*target,*index,*pname,params);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5768: { // glReleaseShaderCompiler
+ weglReleaseShaderCompiler();
+}; break;
+case 5769: { // glShaderBinary
+ int * shadersLen = (int *) bp; bp += 4;
+ GLuint * shaders = (GLuint *) bp; bp += (8-((*shadersLen*4+4)%8))%8;
+ GLenum *binaryformat = (GLenum *) bp; bp += 4;
+ GLvoid *binary = (GLvoid *) bins[0];
+ GLsizei binary_size = bins_sz[0];
+ weglShaderBinary(*shadersLen,shaders,*binaryformat,binary,binary_size);
+}; break;
+case 5770: { // glGetShaderPrecisionFormat
+ GLenum *shadertype = (GLenum *) bp; bp += 4;
+ GLenum *precisiontype = (GLenum *) bp; bp += 4;
+ GLint range[2] = {0,0};
+ GLint precision[1] = {0};
+ weglGetShaderPrecisionFormat(*shadertype,*precisiontype,range,precision);
+ int AP = 0; ErlDrvTermData rt[14];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLint *rangeTmp = range;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *rangeTmp++;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *rangeTmp++;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *precision;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5771: { // glDepthRangef
+ GLclampf *n = (GLclampf *) bp; bp += 4;
+ GLclampf *f = (GLclampf *) bp; bp += 4;
+ weglDepthRangef(*n,*f);
+}; break;
+case 5772: { // glClearDepthf
+ GLclampf *d = (GLclampf *) bp; bp += 4;
+ weglClearDepthf(*d);
+}; break;
+case 5773: { // glGetProgramBinary
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLsizei *bufSize = (GLsizei *) bp; bp += 4;
+ GLsizei length[1] = {0};
+ GLenum binaryFormat[1] = {0};
+ ErlDrvBinary *binary = driver_alloc_binary(*bufSize);
+ weglGetProgramBinary(*program,*bufSize,length,binaryFormat,(GLvoid*) binary->orig_bytes);
+ int AP = 0; ErlDrvTermData rt[12];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *binaryFormat;
+ rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) binary; rt[AP++] = *length; rt[AP++] = 0;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free_binary(binary);
+}; break;
+case 5774: { // glProgramBinary
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *binaryFormat = (GLenum *) bp; bp += 4;
+ GLvoid *binary = (GLvoid *) bins[0];
+ GLsizei binary_size = bins_sz[0];
+ weglProgramBinary(*program,*binaryFormat,binary,binary_size);
+}; break;
+case 5775: { // glProgramParameteri
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint *value = (GLint *) bp; bp += 4;
+ weglProgramParameteri(*program,*pname,*value);
+}; break;
+case 5776: { // glUseProgramStages
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ GLbitfield *stages = (GLbitfield *) bp; bp += 4;
+ GLuint *program = (GLuint *) bp; bp += 4;
+ weglUseProgramStages(*pipeline,*stages,*program);
+}; break;
+case 5777: { // glActiveShaderProgram
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ GLuint *program = (GLuint *) bp; bp += 4;
+ weglActiveShaderProgram(*pipeline,*program);
+}; break;
+case 5778: { // glCreateShaderProgramv
+ GLenum *type = (GLenum *) bp; bp += 4;
+ int * stringsLen = (int *) bp; bp += 4;
+ int * stringsTotSize = (int *) bp; bp += 4;
+ GLchar **strings;
+ strings = (GLchar **) driver_alloc(sizeof(GLchar *) * *stringsLen);
+ for(int i=0;i<*stringsLen;i++) {
+ strings[i] = (GLchar *) bp; bp += 1+strlen(bp);};
+ bp += (8 - ((0 + *stringsTotSize) % 8)) % 8;
+ GLuint result = weglCreateShaderProgramv(*type,*stringsLen,(const GLchar **) strings);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(strings);
+}; break;
+case 5779: { // glBindProgramPipeline
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ weglBindProgramPipeline(*pipeline);
+}; break;
+case 5780: { // glDeleteProgramPipelines
+ int * pipelinesLen = (int *) bp; bp += 4;
+ GLuint * pipelines = (GLuint *) bp; bp += (8-((*pipelinesLen*4+4)%8))%8;
+ weglDeleteProgramPipelines(*pipelinesLen,pipelines);
+}; break;
+case 5781: { // glGenProgramPipelines
+ GLsizei *n = (GLsizei *) bp; bp += 4;
+ GLuint *pipelines;
+ pipelines = (GLuint *) driver_alloc(sizeof(GLuint) * *n);
+ weglGenProgramPipelines(*n,pipelines);
+ int AP = 0; ErlDrvTermData *rt;
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(7 + (*n)*2));
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ for(int i=0; i < *n; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) pipelines[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = (*n)+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(rt);
+ driver_free(pipelines);
+}; break;
+case 5782: { // glIsProgramPipeline
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ GLboolean result = weglIsProgramPipeline(*pipeline);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5783: { // glGetProgramPipelineiv
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLint params[1] = {0};
+ weglGetProgramPipelineiv(*pipeline,*pname,params);
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) *params;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5784: { // glProgramUniform1i
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLint *v0 = (GLint *) bp; bp += 4;
+ weglProgramUniform1i(*program,*location,*v0);
+}; break;
+case 5785: { // glProgramUniform1iv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int * valueLen = (int *) bp; bp += 4;
+ GLint * value = (GLint *) bp; bp += (8-((*valueLen*4+4)%8))%8;
+ weglProgramUniform1iv(*program,*location,*valueLen,value);
+}; break;
+case 5786: { // glProgramUniform1f
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLfloat *v0 = (GLfloat *) bp; bp += 4;
+ weglProgramUniform1f(*program,*location,*v0);
+}; break;
+case 5787: { // glProgramUniform1fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int * valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += (8-((*valueLen*4+4)%8))%8;
+ weglProgramUniform1fv(*program,*location,*valueLen,value);
+}; break;
+case 5788: { // glProgramUniform1d
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLdouble *v0 = (GLdouble *) bp; bp += 8;
+ weglProgramUniform1d(*program,*location,*v0);
+}; break;
+case 5789: { // glProgramUniform1dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int * valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += (8-((*valueLen*8+0)%8))%8;
+ weglProgramUniform1dv(*program,*location,*valueLen,value);
+}; break;
+case 5790: { // glProgramUniform1ui
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLuint *v0 = (GLuint *) bp; bp += 4;
+ weglProgramUniform1ui(*program,*location,*v0);
+}; break;
+case 5791: { // glProgramUniform1uiv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int * valueLen = (int *) bp; bp += 4;
+ GLuint * value = (GLuint *) bp; bp += (8-((*valueLen*4+4)%8))%8;
+ weglProgramUniform1uiv(*program,*location,*valueLen,value);
+}; break;
+case 5792: { // glProgramUniform2i
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLint *v0 = (GLint *) bp; bp += 4;
+ GLint *v1 = (GLint *) bp; bp += 4;
+ weglProgramUniform2i(*program,*location,*v0,*v1);
+}; break;
+case 5793: { // glProgramUniform2iv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLint * value = (GLint *) bp; bp += *valueLen*8;
+ weglProgramUniform2iv(*program,*location,*valueLen,value);
+}; break;
+case 5794: { // glProgramUniform2f
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLfloat *v0 = (GLfloat *) bp; bp += 4;
+ GLfloat *v1 = (GLfloat *) bp; bp += 4;
+ weglProgramUniform2f(*program,*location,*v0,*v1);
+}; break;
+case 5795: { // glProgramUniform2fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*8;
+ weglProgramUniform2fv(*program,*location,*valueLen,value);
+}; break;
+case 5796: { // glProgramUniform2d
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLdouble *v0 = (GLdouble *) bp; bp += 8;
+ GLdouble *v1 = (GLdouble *) bp; bp += 8;
+ weglProgramUniform2d(*program,*location,*v0,*v1);
+}; break;
+case 5797: { // glProgramUniform2dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*16;
+ weglProgramUniform2dv(*program,*location,*valueLen,value);
+}; break;
+case 5798: { // glProgramUniform2ui
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLuint *v0 = (GLuint *) bp; bp += 4;
+ GLuint *v1 = (GLuint *) bp; bp += 4;
+ weglProgramUniform2ui(*program,*location,*v0,*v1);
+}; break;
+case 5799: { // glProgramUniform2uiv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLuint * value = (GLuint *) bp; bp += *valueLen*8;
+ weglProgramUniform2uiv(*program,*location,*valueLen,value);
+}; break;
+case 5800: { // glProgramUniform3i
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLint *v0 = (GLint *) bp; bp += 4;
+ GLint *v1 = (GLint *) bp; bp += 4;
+ GLint *v2 = (GLint *) bp; bp += 4;
+ weglProgramUniform3i(*program,*location,*v0,*v1,*v2);
+}; break;
+case 5801: { // glProgramUniform3iv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLint * value = (GLint *) bp; bp += *valueLen*12;
+ weglProgramUniform3iv(*program,*location,*valueLen,value);
+}; break;
+case 5802: { // glProgramUniform3f
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLfloat *v0 = (GLfloat *) bp; bp += 4;
+ GLfloat *v1 = (GLfloat *) bp; bp += 4;
+ GLfloat *v2 = (GLfloat *) bp; bp += 4;
+ weglProgramUniform3f(*program,*location,*v0,*v1,*v2);
+}; break;
+case 5803: { // glProgramUniform3fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*12;
+ weglProgramUniform3fv(*program,*location,*valueLen,value);
+}; break;
+case 5804: { // glProgramUniform3d
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLdouble *v0 = (GLdouble *) bp; bp += 8;
+ GLdouble *v1 = (GLdouble *) bp; bp += 8;
+ GLdouble *v2 = (GLdouble *) bp; bp += 8;
+ weglProgramUniform3d(*program,*location,*v0,*v1,*v2);
+}; break;
+case 5805: { // glProgramUniform3dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*24;
+ weglProgramUniform3dv(*program,*location,*valueLen,value);
+}; break;
+case 5806: { // glProgramUniform3ui
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLuint *v0 = (GLuint *) bp; bp += 4;
+ GLuint *v1 = (GLuint *) bp; bp += 4;
+ GLuint *v2 = (GLuint *) bp; bp += 4;
+ weglProgramUniform3ui(*program,*location,*v0,*v1,*v2);
+}; break;
+case 5807: { // glProgramUniform3uiv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLuint * value = (GLuint *) bp; bp += *valueLen*12;
+ weglProgramUniform3uiv(*program,*location,*valueLen,value);
+}; break;
+case 5808: { // glProgramUniform4i
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLint *v0 = (GLint *) bp; bp += 4;
+ GLint *v1 = (GLint *) bp; bp += 4;
+ GLint *v2 = (GLint *) bp; bp += 4;
+ GLint *v3 = (GLint *) bp; bp += 4;
+ weglProgramUniform4i(*program,*location,*v0,*v1,*v2,*v3);
+}; break;
+case 5809: { // glProgramUniform4iv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLint * value = (GLint *) bp; bp += *valueLen*16;
+ weglProgramUniform4iv(*program,*location,*valueLen,value);
+}; break;
+case 5810: { // glProgramUniform4f
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLfloat *v0 = (GLfloat *) bp; bp += 4;
+ GLfloat *v1 = (GLfloat *) bp; bp += 4;
+ GLfloat *v2 = (GLfloat *) bp; bp += 4;
+ GLfloat *v3 = (GLfloat *) bp; bp += 4;
+ weglProgramUniform4f(*program,*location,*v0,*v1,*v2,*v3);
+}; break;
+case 5811: { // glProgramUniform4fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*16;
+ weglProgramUniform4fv(*program,*location,*valueLen,value);
+}; break;
+case 5812: { // glProgramUniform4d
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLdouble *v0 = (GLdouble *) bp; bp += 8;
+ GLdouble *v1 = (GLdouble *) bp; bp += 8;
+ GLdouble *v2 = (GLdouble *) bp; bp += 8;
+ GLdouble *v3 = (GLdouble *) bp; bp += 8;
+ weglProgramUniform4d(*program,*location,*v0,*v1,*v2,*v3);
+}; break;
+case 5813: { // glProgramUniform4dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
+ weglProgramUniform4dv(*program,*location,*valueLen,value);
+}; break;
+case 5814: { // glProgramUniform4ui
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLuint *v0 = (GLuint *) bp; bp += 4;
+ GLuint *v1 = (GLuint *) bp; bp += 4;
+ GLuint *v2 = (GLuint *) bp; bp += 4;
+ GLuint *v3 = (GLuint *) bp; bp += 4;
+ weglProgramUniform4ui(*program,*location,*v0,*v1,*v2,*v3);
+}; break;
+case 5815: { // glProgramUniform4uiv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ int *valueLen = (int *) bp; bp += 4;
+ GLuint * value = (GLuint *) bp; bp += *valueLen*16;
+ weglProgramUniform4uiv(*program,*location,*valueLen,value);
+}; break;
+case 5816: { // glProgramUniformMatrix2fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*16;
+ weglProgramUniformMatrix2fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5817: { // glProgramUniformMatrix3fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*36;
+ weglProgramUniformMatrix3fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5818: { // glProgramUniformMatrix4fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*64;
+ weglProgramUniformMatrix4fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5819: { // glProgramUniformMatrix2dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*32;
+ weglProgramUniformMatrix2dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5820: { // glProgramUniformMatrix3dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*72;
+ weglProgramUniformMatrix3dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5821: { // glProgramUniformMatrix4dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*128;
+ weglProgramUniformMatrix4dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5822: { // glProgramUniformMatrix2x3fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*24;
+ weglProgramUniformMatrix2x3fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5823: { // glProgramUniformMatrix3x2fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*24;
+ weglProgramUniformMatrix3x2fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5824: { // glProgramUniformMatrix2x4fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*32;
+ weglProgramUniformMatrix2x4fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5825: { // glProgramUniformMatrix4x2fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*32;
+ weglProgramUniformMatrix4x2fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5826: { // glProgramUniformMatrix3x4fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*48;
+ weglProgramUniformMatrix3x4fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5827: { // glProgramUniformMatrix4x3fv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 3;
+ int *valueLen = (int *) bp; bp += 4;
+ GLfloat * value = (GLfloat *) bp; bp += *valueLen*48;
+ weglProgramUniformMatrix4x3fv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5828: { // glProgramUniformMatrix2x3dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
+ weglProgramUniformMatrix2x3dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5829: { // glProgramUniformMatrix3x2dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*48;
+ weglProgramUniformMatrix3x2dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5830: { // glProgramUniformMatrix2x4dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
+ weglProgramUniformMatrix2x4dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5831: { // glProgramUniformMatrix4x2dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*64;
+ weglProgramUniformMatrix4x2dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5832: { // glProgramUniformMatrix3x4dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
+ weglProgramUniformMatrix3x4dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5833: { // glProgramUniformMatrix4x3dv
+ GLuint *program = (GLuint *) bp; bp += 4;
+ GLint *location = (GLint *) bp; bp += 4;
+ GLboolean *transpose = (GLboolean *) bp; bp += 1;
+ bp += 7;
+ int *valueLen = (int *) bp; bp += 8;
+ GLdouble * value = (GLdouble *) bp; bp += *valueLen*96;
+ weglProgramUniformMatrix4x3dv(*program,*location,*valueLen,*transpose,value);
+}; break;
+case 5834: { // glValidateProgramPipeline
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ weglValidateProgramPipeline(*pipeline);
+}; break;
+case 5835: { // glGetProgramPipelineInfoLog
+ GLuint *pipeline = (GLuint *) bp; bp += 4;
+ GLsizei *bufSize = (GLsizei *) bp; bp += 4;
+ GLsizei length[1] = {0};
+ GLchar *infoLog;
+ infoLog = (GLchar *) driver_alloc(sizeof(GLchar) * *bufSize);
+ weglGetProgramPipelineInfoLog(*pipeline,*bufSize,length,infoLog);
+ int AP = 0; ErlDrvTermData rt[7];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) infoLog; rt[AP++] = *length;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(infoLog);
+}; break;
+case 5836: { // glVertexAttribL1dv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *v = (GLdouble *) bp; bp += 8;
+ weglVertexAttribL1dv(*index,v);
+}; break;
+case 5837: { // glVertexAttribL2dv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *v = (GLdouble *) bp; bp += 8;
+ weglVertexAttribL2dv(*index,v);
+}; break;
+case 5838: { // glVertexAttribL3dv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *v = (GLdouble *) bp; bp += 8;
+ weglVertexAttribL3dv(*index,v);
+}; break;
+case 5839: { // glVertexAttribL4dv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ bp += 4;
+ GLdouble *v = (GLdouble *) bp; bp += 8;
+ weglVertexAttribL4dv(*index,v);
+}; break;
+case 5840: { // glVertexAttribLPointer
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint *size = (GLint *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLsizei *stride = (GLsizei *) bp; bp += 4;
+ GLvoid *pointer = (GLvoid *) * (int *) bp; bp += 4;
+ weglVertexAttribLPointer(*index,*size,*type,*stride,pointer);
+}; break;
+case 5841: { // glVertexAttribLPointer
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint *size = (GLint *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLsizei *stride = (GLsizei *) bp; bp += 4;
+ GLvoid *pointer = (GLvoid *) bins[0];
+ weglVertexAttribLPointer(*index,*size,*type,*stride,pointer);
+}; break;
+case 5842: { // glGetVertexAttribLdv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLenum *pname = (GLenum *) bp; bp += 4;
+ GLdouble params[4] = {0.0,0.0,0.0,0.0};
+ weglGetVertexAttribLdv(*index,*pname,params);
+ int AP = 0; ErlDrvTermData rt[14];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLdouble *paramsTmp = params;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) paramsTmp++;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 4;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5843: { // glViewportArrayv
+ GLuint *first = (GLuint *) bp; bp += 4;
+ int *vLen = (int *) bp; bp += 4;
+ GLfloat * v = (GLfloat *) bp; bp += *vLen*16;
+ weglViewportArrayv(*first,*vLen,v);
+}; break;
+case 5844: { // glViewportIndexedf
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLfloat *x = (GLfloat *) bp; bp += 4;
+ GLfloat *y = (GLfloat *) bp; bp += 4;
+ GLfloat *w = (GLfloat *) bp; bp += 4;
+ GLfloat *h = (GLfloat *) bp; bp += 4;
+ weglViewportIndexedf(*index,*x,*y,*w,*h);
+}; break;
+case 5845: { // glViewportIndexedfv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLfloat * v = (GLfloat *) bp; bp += 16;
+ weglViewportIndexedfv(*index,v);
+}; break;
+case 5846: { // glScissorArrayv
+ GLuint *first = (GLuint *) bp; bp += 4;
+ int *vLen = (int *) bp; bp += 4;
+ GLint * v = (GLint *) bp; bp += *vLen*16;
+ weglScissorArrayv(*first,*vLen,v);
+}; break;
+case 5847: { // glScissorIndexed
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint *left = (GLint *) bp; bp += 4;
+ GLint *bottom = (GLint *) bp; bp += 4;
+ GLsizei *width = (GLsizei *) bp; bp += 4;
+ GLsizei *height = (GLsizei *) bp; bp += 4;
+ weglScissorIndexed(*index,*left,*bottom,*width,*height);
+}; break;
+case 5848: { // glScissorIndexedv
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLint * v = (GLint *) bp; bp += 16;
+ weglScissorIndexedv(*index,v);
+}; break;
+case 5849: { // glDepthRangeArrayv
+ GLuint *first = (GLuint *) bp; bp += 4;
+ bp += 4;
+ int *vLen = (int *) bp; bp += 8;
+ GLclampd * v = (GLclampd *) bp; bp += *vLen*16;
+ weglDepthRangeArrayv(*first,*vLen,v);
+}; break;
+case 5850: { // glDepthRangeIndexed
+ GLuint *index = (GLuint *) bp; bp += 4;
+ bp += 4;
+ GLclampd *n = (GLclampd *) bp; bp += 8;
+ GLclampd *f = (GLclampd *) bp; bp += 8;
+ weglDepthRangeIndexed(*index,*n,*f);
+}; break;
+case 5851: { // glGetFloati_v
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLfloat data[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+ weglGetFloati_v(*target,*index,data);
+ int AP = 0; ErlDrvTermData rt[39];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLdouble dataConv[16], *dataTmp = dataConv;
+ for(int i=0; i < 16; i++) dataConv[i] = (GLdouble) data[i];
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5852: { // glGetDoublei_v
+ GLenum *target = (GLenum *) bp; bp += 4;
+ GLuint *index = (GLuint *) bp; bp += 4;
+ GLdouble data[16] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
+ weglGetDoublei_v(*target,*index,data);
+ int AP = 0; ErlDrvTermData rt[39];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ GLdouble *dataTmp = data;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_FLOAT; rt[AP++] = (ErlDrvTermData) dataTmp++;
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = 16+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5853: { // glDebugMessageControlARB
+ GLenum *source = (GLenum *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLenum *severity = (GLenum *) bp; bp += 4;
+ int * idsLen = (int *) bp; bp += 4;
+ GLuint * ids = (GLuint *) bp; bp += (8-((*idsLen*4+0)%8))%8;
+ GLboolean *enabled = (GLboolean *) bp; bp += 1;
+ weglDebugMessageControlARB(*source,*type,*severity,*idsLen,ids,*enabled);
+}; break;
+case 5854: { // glDebugMessageInsertARB
+ GLenum *source = (GLenum *) bp; bp += 4;
+ GLenum *type = (GLenum *) bp; bp += 4;
+ GLuint *id = (GLuint *) bp; bp += 4;
+ GLenum *severity = (GLenum *) bp; bp += 4;
+ GLchar *buf = (GLchar *) bp;
+ int bufLen[1] = {strlen((char *)buf)}; bp += bufLen[0]+1+((8-((1+bufLen[0]+0)%8))%8);
+ weglDebugMessageInsertARB(*source,*type,*id,*severity,*bufLen,buf);
+}; break;
+case 5855: { // glGetDebugMessageLogARB
+ GLuint *count = (GLuint *) bp; bp += 4;
+ GLsizei *bufsize = (GLsizei *) bp; bp += 4;
+ GLenum *sources;
+ sources = (GLenum *) driver_alloc(sizeof(GLenum) * *count);
+ GLenum *types;
+ types = (GLenum *) driver_alloc(sizeof(GLenum) * *count);
+ GLuint *ids;
+ ids = (GLuint *) driver_alloc(sizeof(GLuint) * *count);
+ GLenum *severities;
+ severities = (GLenum *) driver_alloc(sizeof(GLenum) * *count);
+ GLsizei *lengths;
+ lengths = (GLsizei *) driver_alloc(sizeof(GLsizei) * *count);
+ GLchar *messageLog;
+ messageLog = (GLchar *) driver_alloc(sizeof(GLchar) * *bufsize);
+ GLuint result = weglGetDebugMessageLogARB(*count,*bufsize,sources,types,ids,severities,lengths,messageLog);
+ int AP = 0; ErlDrvTermData *rt;
+ rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData)*(23 + result*3 + result*2 + result*2 + result*2 + result*2));
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ for(int i=0; i < (int) result; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) sources[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = ((int) result)+1;
+ for(int i=0; i < (int) result; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) types[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = ((int) result)+1;
+ for(int i=0; i < (int) result; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) ids[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = ((int) result)+1;
+ for(int i=0; i < (int) result; i++) {
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) severities[i];}
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = ((int) result)+1;
+ for(int i=0; i < (int) result; i++) {
+ rt[AP++] = ERL_DRV_STRING; rt[AP++] = (ErlDrvTermData) messageLog; rt[AP++] = lengths[i]-1;
+ messageLog += lengths[i]; }
+ rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = ((int) result)+1;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 6;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+ driver_free(rt);
+ driver_free(messageLog);
+ driver_free(lengths);
+ driver_free(severities);
+ driver_free(ids);
+ driver_free(types);
+ driver_free(sources);
+}; break;
+case 5856: { // glGetGraphicsResetStatusARB
+ GLenum result = weglGetGraphicsResetStatusARB();
+ int AP = 0; ErlDrvTermData rt[6];
+ rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_result_");
+ rt[AP++] = ERL_DRV_INT; rt[AP++] = (ErlDrvSInt) result;
+ rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+ driver_send_term(port,caller,rt,AP);
+}; break;
+case 5857: { // glResizeBuffersMESA
weglResizeBuffersMESA();
}; break;
-case 5677: { // glWindowPos4dvMESA
+case 5858: { // glWindowPos4dvMESA
GLdouble *v = (GLdouble *) bp; bp += 8;
weglWindowPos4dvMESA(v);
}; break;
-case 5678: { // glWindowPos4fvMESA
+case 5859: { // glWindowPos4fvMESA
GLfloat *v = (GLfloat *) bp; bp += 4;
weglWindowPos4fvMESA(v);
}; break;
-case 5679: { // glWindowPos4ivMESA
+case 5860: { // glWindowPos4ivMESA
GLint *v = (GLint *) bp; bp += 4;
weglWindowPos4ivMESA(v);
}; break;
-case 5680: { // glWindowPos4svMESA
+case 5861: { // glWindowPos4svMESA
GLshort *v = (GLshort *) bp; bp += 2;
weglWindowPos4svMESA(v);
}; break;
-case 5681: { // glDepthBoundsEXT
+case 5862: { // glDepthBoundsEXT
GLclampd *zmin = (GLclampd *) bp; bp += 8;
GLclampd *zmax = (GLclampd *) bp; bp += 8;
weglDepthBoundsEXT(*zmin,*zmax);
}; break;
-case 5682: { // glStencilClearTagEXT
+case 5863: { // glStencilClearTagEXT
GLsizei *stencilTagBits = (GLsizei *) bp; bp += 4;
GLuint *stencilClearTag = (GLuint *) bp; bp += 4;
weglStencilClearTagEXT(*stencilTagBits,*stencilClearTag);
}; break;
+}} catch (char *err_msg) {
+int AP = 0; ErlDrvTermData rt[12];
+rt[AP++] = ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_egl_error_");
+rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) op;
+rt[AP++] = ERL_DRV_ATOM; rt[AP++] = driver_mk_atom((char *) err_msg);
+// rt[AP++] = ERL_DRV_ATOM; rt[AP++] = driver_mk_atom((char *) gl_fns[op-GLE_GL_FUNC_START].name);
+// rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2;
+rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
+driver_send_term(port,caller,rt,AP);
}} /* The End */
diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h
index 57b0faa2cb..ad46a98c90 100644
--- a/lib/wx/c_src/gen/wxe_derived_dest.h
+++ b/lib/wx/c_src/gen/wxe_derived_dest.h
@@ -736,7 +736,7 @@ void WxeApp::delete_object(void *ptr, wxeRefData *refd) {
case 211: /* delete (wxFileDataObject *) ptr;These objects must be deleted by owner object */ break;
case 212: /* delete (wxTextDataObject *) ptr;These objects must be deleted by owner object */ break;
case 213: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break;
- case 222: delete (wxLogNull *) ptr; break;
+ case 223: delete (wxLogNull *) ptr; break;
default: delete (wxObject *) ptr;
}}
diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp
index a6857442c9..692eef858c 100644
--- a/lib/wx/c_src/gen/wxe_events.cpp
+++ b/lib/wx/c_src/gen/wxe_events.cpp
@@ -266,41 +266,41 @@ void initEventTable()
{wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, 217, "command_splitter_doubleclicked"},
{wxEVT_COMMAND_SPLITTER_UNSPLIT, 217, "command_splitter_unsplit"},
{wxEVT_COMMAND_HTML_LINK_CLICKED, 219, "command_html_link_clicked"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 220, "command_auinotebook_page_close"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 220, "command_auinotebook_page_changed"},
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 220, "command_auinotebook_page_changing"},
- {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 220, "command_auinotebook_button"},
- {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 220, "command_auinotebook_begin_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 220, "command_auinotebook_end_drag"},
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 220, "command_auinotebook_drag_motion"},
- {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 220, "command_auinotebook_allow_dnd"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSE, 221, "command_auinotebook_page_close"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGED, 221, "command_auinotebook_page_changed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CHANGING, 221, "command_auinotebook_page_changing"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BUTTON, 221, "command_auinotebook_button"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BEGIN_DRAG, 221, "command_auinotebook_begin_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_END_DRAG, 221, "command_auinotebook_end_drag"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_MOTION, 221, "command_auinotebook_drag_motion"},
+ {wxEVT_COMMAND_AUINOTEBOOK_ALLOW_DND, 221, "command_auinotebook_allow_dnd"},
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 220, "command_auinotebook_tab_middle_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_DOWN, 221, "command_auinotebook_tab_middle_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 220, "command_auinotebook_tab_middle_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_MIDDLE_UP, 221, "command_auinotebook_tab_middle_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 220, "command_auinotebook_tab_right_down"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_DOWN, 221, "command_auinotebook_tab_right_down"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 220, "command_auinotebook_tab_right_up"},
+ {wxEVT_COMMAND_AUINOTEBOOK_TAB_RIGHT_UP, 221, "command_auinotebook_tab_right_up"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 220, "command_auinotebook_page_closed"},
+ {wxEVT_COMMAND_AUINOTEBOOK_PAGE_CLOSED, 221, "command_auinotebook_page_closed"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 220, "command_auinotebook_drag_done"},
+ {wxEVT_COMMAND_AUINOTEBOOK_DRAG_DONE, 221, "command_auinotebook_drag_done"},
#endif
#if wxCHECK_VERSION(2,8,5)
- {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 220, "command_auinotebook_bg_dclick"},
+ {wxEVT_COMMAND_AUINOTEBOOK_BG_DCLICK, 221, "command_auinotebook_bg_dclick"},
#endif
- {wxEVT_AUI_PANE_BUTTON, 221, "aui_pane_button"},
- {wxEVT_AUI_PANE_CLOSE, 221, "aui_pane_close"},
- {wxEVT_AUI_PANE_MAXIMIZE, 221, "aui_pane_maximize"},
- {wxEVT_AUI_PANE_RESTORE, 221, "aui_pane_restore"},
- {wxEVT_AUI_RENDER, 221, "aui_render"},
- {wxEVT_AUI_FIND_MANAGER, 221, "aui_find_manager"},
+ {wxEVT_AUI_PANE_BUTTON, 222, "aui_pane_button"},
+ {wxEVT_AUI_PANE_CLOSE, 222, "aui_pane_close"},
+ {wxEVT_AUI_PANE_MAXIMIZE, 222, "aui_pane_maximize"},
+ {wxEVT_AUI_PANE_RESTORE, 222, "aui_pane_restore"},
+ {wxEVT_AUI_RENDER, 222, "aui_render"},
+ {wxEVT_AUI_FIND_MANAGER, 222, "aui_find_manager"},
{-1, 0, }
};
for(int i=0; event_types[i].ev_type != -1; i++) {
@@ -778,7 +778,7 @@ case 219: {// wxHtmlLinkEvent
rt.addTupleCount(3);
break;
}
-case 220: {// wxAuiNotebookEvent
+case 221: {// wxAuiNotebookEvent
wxAuiNotebookEvent * ev = (wxAuiNotebookEvent *) event;
wxAuiNotebook * GetDragSource = ev->GetDragSource();
evClass = (char*)"wxAuiNotebookEvent";
@@ -790,7 +790,7 @@ case 220: {// wxAuiNotebookEvent
rt.addTupleCount(5);
break;
}
-case 221: {// wxAuiManagerEvent
+case 222: {// wxAuiManagerEvent
wxAuiManagerEvent * ev = (wxAuiManagerEvent *) event;
wxAuiManager * GetManager = ev->GetManager();
wxAuiPaneInfo * GetPane = ev->GetPane();
diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp
index fd496c2ccc..479d7679a4 100644
--- a/lib/wx/c_src/gen/wxe_funcs.cpp
+++ b/lib/wx/c_src/gen/wxe_funcs.cpp
@@ -23,6 +23,7 @@
#include "../wxe_impl.h"
#include "../wxe_events.h"
#include "../wxe_return.h"
+#include "../wxe_gl.h"
#include "wxe_macros.h"
#include "wxe_derived_dest.h"
@@ -43,6 +44,15 @@ void WxeApp::wxe_dispatch(wxeCommand& Ecmd)
rt.addAtom("ok");
break;
}
+ case WXE_BIN_INCR:
+ driver_binary_inc_refc(Ecmd.bin[0]->bin);
+ break;
+ case WXE_BIN_DECR:
+ driver_binary_dec_refc(Ecmd.bin[0]->bin);
+ break;
+ case WXE_INIT_OPENGL:
+ wxe_initOpenGL(rt, bp);
+ break;
case 98: { // wxeEvtListener::wxeEvtListener
wxeEvtListener *Result = new wxeEvtListener(Ecmd.port);
rt.addRef(getRef((void *)Result,memenv), "wxeEvtListener");
@@ -18501,6 +18511,15 @@ case wxTreeCtrl_DeleteChildren: { // wxTreeCtrl::DeleteChildren
This->DeleteChildren(item);
break;
}
+case wxTreeCtrl_EditLabel: { // wxTreeCtrl::EditLabel
+ wxTreeCtrl *This = (wxTreeCtrl *) getPtr(bp,memenv); bp += 4;
+ bp += 4; /* Align */
+ wxTreeItemId item = wxTreeItemId((void *) *(wxUint64 *) bp); bp += 8;
+ if(!This) throw wxe_badarg(0);
+ wxTextCtrl * Result = (wxTextCtrl*)This->EditLabel(item);
+ rt.addRef(getRef((void *)Result,memenv), "wxTextCtrl");
+ break;
+}
case wxTreeCtrl_EnsureVisible: { // wxTreeCtrl::EnsureVisible
wxTreeCtrl *This = (wxTreeCtrl *) getPtr(bp,memenv); bp += 4;
bp += 4; /* Align */
@@ -30976,6 +30995,36 @@ case wxHtmlLinkEvent_GetLinkInfo: { // wxHtmlLinkEvent::GetLinkInfo
rt.add(Result);
break;
}
+case wxSystemSettings_GetColour: { // wxSystemSettings::GetColour
+ wxSystemColour index = *(wxSystemColour *) bp; bp += 4;;
+ wxColour Result = wxSystemSettings::GetColour((wxSystemColour) index);
+ rt.add(Result);
+ break;
+}
+case wxSystemSettings_GetFont: { // wxSystemSettings::GetFont
+ wxSystemFont index = *(wxSystemFont *) bp; bp += 4;;
+ wxFont * Result = new wxFont(wxSystemSettings::GetFont((wxSystemFont) index)); newPtr((void *) Result,3, memenv);;
+ rt.addRef(getRef((void *)Result,memenv), "wxFont");
+ break;
+}
+case wxSystemSettings_GetMetric: { // wxSystemSettings::GetMetric
+ wxWindow * win=NULL;
+ wxSystemMetric index = *(wxSystemMetric *) bp; bp += 4;;
+ bp += 4; /* Align */
+ while( * (int*) bp) { switch (* (int*) bp) {
+ case 1: {bp += 4;
+win = (wxWindow *) getPtr(bp,memenv); bp += 4;
+ } break;
+ }};
+ int Result = wxSystemSettings::GetMetric((wxSystemMetric) index,win);
+ rt.addInt(Result);
+ break;
+}
+case wxSystemSettings_GetScreenType: { // wxSystemSettings::GetScreenType
+ int Result = wxSystemSettings::GetScreenType();
+ rt.addInt(Result);
+ break;
+}
case wxAuiNotebookEvent_SetSelection: { // wxAuiNotebookEvent::SetSelection
wxAuiNotebookEvent *This = (wxAuiNotebookEvent *) getPtr(bp,memenv); bp += 4;
int * s = (int *) bp; bp += 4;
@@ -31110,7 +31159,7 @@ case wxAuiManagerEvent_CanVeto: { // wxAuiManagerEvent::CanVeto
}
case wxLogNull_new: { // wxLogNull::wxLogNull
wxLogNull * Result = new wxLogNull();
- newPtr((void *) Result, 222, memenv);
+ newPtr((void *) Result, 223, memenv);
rt.addRef(getRef((void *)Result,memenv), "wxLogNull");
break;
}
diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h
index 415b7f0b29..4fb76f960b 100644
--- a/lib/wx/c_src/gen/wxe_macros.h
+++ b/lib/wx/c_src/gen/wxe_macros.h
@@ -1882,1444 +1882,1449 @@
#define wxTreeCtrl_Delete 2007
#define wxTreeCtrl_DeleteAllItems 2008
#define wxTreeCtrl_DeleteChildren 2009
-#define wxTreeCtrl_EnsureVisible 2010
-#define wxTreeCtrl_Expand 2011
-#define wxTreeCtrl_GetBoundingRect 2012
-#define wxTreeCtrl_GetChildrenCount 2014
-#define wxTreeCtrl_GetCount 2015
-#define wxTreeCtrl_GetEditControl 2016
-#define wxTreeCtrl_GetFirstChild 2017
-#define wxTreeCtrl_GetNextChild 2018
-#define wxTreeCtrl_GetFirstVisibleItem 2019
-#define wxTreeCtrl_GetImageList 2020
-#define wxTreeCtrl_GetIndent 2021
-#define wxTreeCtrl_GetItemBackgroundColour 2022
-#define wxTreeCtrl_GetItemData 2023
-#define wxTreeCtrl_GetItemFont 2024
-#define wxTreeCtrl_GetItemImage_1 2025
-#define wxTreeCtrl_GetItemImage_2 2026
-#define wxTreeCtrl_GetItemText 2027
-#define wxTreeCtrl_GetItemTextColour 2028
-#define wxTreeCtrl_GetLastChild 2029
-#define wxTreeCtrl_GetNextSibling 2030
-#define wxTreeCtrl_GetNextVisible 2031
-#define wxTreeCtrl_GetItemParent 2032
-#define wxTreeCtrl_GetPrevSibling 2033
-#define wxTreeCtrl_GetPrevVisible 2034
-#define wxTreeCtrl_GetRootItem 2035
-#define wxTreeCtrl_GetSelection 2036
-#define wxTreeCtrl_GetSelections 2037
-#define wxTreeCtrl_GetStateImageList 2038
-#define wxTreeCtrl_HitTest 2039
-#define wxTreeCtrl_InsertItem 2041
-#define wxTreeCtrl_IsBold 2042
-#define wxTreeCtrl_IsExpanded 2043
-#define wxTreeCtrl_IsSelected 2044
-#define wxTreeCtrl_IsVisible 2045
-#define wxTreeCtrl_ItemHasChildren 2046
-#define wxTreeCtrl_PrependItem 2047
-#define wxTreeCtrl_ScrollTo 2048
-#define wxTreeCtrl_SelectItem_1 2049
-#define wxTreeCtrl_SelectItem_2 2050
-#define wxTreeCtrl_SetIndent 2051
-#define wxTreeCtrl_SetImageList 2052
-#define wxTreeCtrl_SetItemBackgroundColour 2053
-#define wxTreeCtrl_SetItemBold 2054
-#define wxTreeCtrl_SetItemData 2055
-#define wxTreeCtrl_SetItemDropHighlight 2056
-#define wxTreeCtrl_SetItemFont 2057
-#define wxTreeCtrl_SetItemHasChildren 2058
-#define wxTreeCtrl_SetItemImage_2 2059
-#define wxTreeCtrl_SetItemImage_3 2060
-#define wxTreeCtrl_SetItemText 2061
-#define wxTreeCtrl_SetItemTextColour 2062
-#define wxTreeCtrl_SetStateImageList 2063
-#define wxTreeCtrl_SetWindowStyle 2064
-#define wxTreeCtrl_SortChildren 2065
-#define wxTreeCtrl_Toggle 2066
-#define wxTreeCtrl_ToggleItemSelection 2067
-#define wxTreeCtrl_Unselect 2068
-#define wxTreeCtrl_UnselectAll 2069
-#define wxTreeCtrl_UnselectItem 2070
-#define wxScrollBar_new_0 2071
-#define wxScrollBar_new_3 2072
-#define wxScrollBar_destruct 2073
-#define wxScrollBar_Create 2074
-#define wxScrollBar_GetRange 2075
-#define wxScrollBar_GetPageSize 2076
-#define wxScrollBar_GetThumbPosition 2077
-#define wxScrollBar_GetThumbSize 2078
-#define wxScrollBar_SetThumbPosition 2079
-#define wxScrollBar_SetScrollbar 2080
-#define wxSpinButton_new_2 2082
-#define wxSpinButton_new_0 2083
-#define wxSpinButton_Create 2084
-#define wxSpinButton_GetMax 2085
-#define wxSpinButton_GetMin 2086
-#define wxSpinButton_GetValue 2087
-#define wxSpinButton_SetRange 2088
-#define wxSpinButton_SetValue 2089
-#define wxSpinButton_destroy 2090
-#define wxSpinCtrl_new_0 2091
-#define wxSpinCtrl_new_2 2092
-#define wxSpinCtrl_Create 2094
-#define wxSpinCtrl_SetValue_1_1 2097
-#define wxSpinCtrl_SetValue_1_0 2098
-#define wxSpinCtrl_GetValue 2100
-#define wxSpinCtrl_SetRange 2102
-#define wxSpinCtrl_SetSelection 2103
-#define wxSpinCtrl_GetMin 2105
-#define wxSpinCtrl_GetMax 2107
-#define wxSpinCtrl_destroy 2108
-#define wxStaticText_new_0 2109
-#define wxStaticText_new_4 2110
-#define wxStaticText_Create 2111
-#define wxStaticText_GetLabel 2112
-#define wxStaticText_SetLabel 2113
-#define wxStaticText_Wrap 2114
-#define wxStaticText_destroy 2115
-#define wxStaticBitmap_new_0 2116
-#define wxStaticBitmap_new_4 2117
-#define wxStaticBitmap_Create 2118
-#define wxStaticBitmap_GetBitmap 2119
-#define wxStaticBitmap_SetBitmap 2120
-#define wxStaticBitmap_destroy 2121
-#define wxRadioBox_new 2122
-#define wxRadioBox_destruct 2124
-#define wxRadioBox_Create 2125
-#define wxRadioBox_Enable_2 2126
-#define wxRadioBox_Enable_1 2127
-#define wxRadioBox_GetSelection 2128
-#define wxRadioBox_GetString 2129
-#define wxRadioBox_SetSelection 2130
-#define wxRadioBox_Show_2 2131
-#define wxRadioBox_Show_1 2132
-#define wxRadioBox_GetColumnCount 2133
-#define wxRadioBox_GetItemHelpText 2134
-#define wxRadioBox_GetItemToolTip 2135
-#define wxRadioBox_GetItemFromPoint 2137
-#define wxRadioBox_GetRowCount 2138
-#define wxRadioBox_IsItemEnabled 2139
-#define wxRadioBox_IsItemShown 2140
-#define wxRadioBox_SetItemHelpText 2141
-#define wxRadioBox_SetItemToolTip 2142
-#define wxRadioButton_new_0 2143
-#define wxRadioButton_new_4 2144
-#define wxRadioButton_Create 2145
-#define wxRadioButton_GetValue 2146
-#define wxRadioButton_SetValue 2147
-#define wxRadioButton_destroy 2148
-#define wxSlider_new_6 2150
-#define wxSlider_new_0 2151
-#define wxSlider_Create 2152
-#define wxSlider_GetLineSize 2153
-#define wxSlider_GetMax 2154
-#define wxSlider_GetMin 2155
-#define wxSlider_GetPageSize 2156
-#define wxSlider_GetThumbLength 2157
-#define wxSlider_GetValue 2158
-#define wxSlider_SetLineSize 2159
-#define wxSlider_SetPageSize 2160
-#define wxSlider_SetRange 2161
-#define wxSlider_SetThumbLength 2162
-#define wxSlider_SetValue 2163
-#define wxSlider_destroy 2164
-#define wxDialog_new_4 2166
-#define wxDialog_new_0 2167
-#define wxDialog_destruct 2169
-#define wxDialog_Create 2170
-#define wxDialog_CreateButtonSizer 2171
-#define wxDialog_CreateStdDialogButtonSizer 2172
-#define wxDialog_EndModal 2173
-#define wxDialog_GetAffirmativeId 2174
-#define wxDialog_GetReturnCode 2175
-#define wxDialog_IsModal 2176
-#define wxDialog_SetAffirmativeId 2177
-#define wxDialog_SetReturnCode 2178
-#define wxDialog_Show 2179
-#define wxDialog_ShowModal 2180
-#define wxColourDialog_new_0 2181
-#define wxColourDialog_new_2 2182
-#define wxColourDialog_destruct 2183
-#define wxColourDialog_Create 2184
-#define wxColourDialog_GetColourData 2185
-#define wxColourData_new_0 2186
-#define wxColourData_new_1 2187
-#define wxColourData_destruct 2188
-#define wxColourData_GetChooseFull 2189
-#define wxColourData_GetColour 2190
-#define wxColourData_GetCustomColour 2192
-#define wxColourData_SetChooseFull 2193
-#define wxColourData_SetColour 2194
-#define wxColourData_SetCustomColour 2195
-#define wxPalette_new_0 2196
-#define wxPalette_new_4 2197
-#define wxPalette_destruct 2199
-#define wxPalette_Create 2200
-#define wxPalette_GetColoursCount 2201
-#define wxPalette_GetPixel 2202
-#define wxPalette_GetRGB 2203
-#define wxPalette_IsOk 2204
-#define wxDirDialog_new 2208
-#define wxDirDialog_destruct 2209
-#define wxDirDialog_GetPath 2210
-#define wxDirDialog_GetMessage 2211
-#define wxDirDialog_SetMessage 2212
-#define wxDirDialog_SetPath 2213
-#define wxFileDialog_new 2217
-#define wxFileDialog_destruct 2218
-#define wxFileDialog_GetDirectory 2219
-#define wxFileDialog_GetFilename 2220
-#define wxFileDialog_GetFilenames 2221
-#define wxFileDialog_GetFilterIndex 2222
-#define wxFileDialog_GetMessage 2223
-#define wxFileDialog_GetPath 2224
-#define wxFileDialog_GetPaths 2225
-#define wxFileDialog_GetWildcard 2226
-#define wxFileDialog_SetDirectory 2227
-#define wxFileDialog_SetFilename 2228
-#define wxFileDialog_SetFilterIndex 2229
-#define wxFileDialog_SetMessage 2230
-#define wxFileDialog_SetPath 2231
-#define wxFileDialog_SetWildcard 2232
-#define wxPickerBase_SetInternalMargin 2233
-#define wxPickerBase_GetInternalMargin 2234
-#define wxPickerBase_SetTextCtrlProportion 2235
-#define wxPickerBase_SetPickerCtrlProportion 2236
-#define wxPickerBase_GetTextCtrlProportion 2237
-#define wxPickerBase_GetPickerCtrlProportion 2238
-#define wxPickerBase_HasTextCtrl 2239
-#define wxPickerBase_GetTextCtrl 2240
-#define wxPickerBase_IsTextCtrlGrowable 2241
-#define wxPickerBase_SetPickerCtrlGrowable 2242
-#define wxPickerBase_SetTextCtrlGrowable 2243
-#define wxPickerBase_IsPickerCtrlGrowable 2244
-#define wxFilePickerCtrl_new_0 2245
-#define wxFilePickerCtrl_new_3 2246
-#define wxFilePickerCtrl_Create 2247
-#define wxFilePickerCtrl_GetPath 2248
-#define wxFilePickerCtrl_SetPath 2249
-#define wxFilePickerCtrl_destroy 2250
-#define wxDirPickerCtrl_new_0 2251
-#define wxDirPickerCtrl_new_3 2252
-#define wxDirPickerCtrl_Create 2253
-#define wxDirPickerCtrl_GetPath 2254
-#define wxDirPickerCtrl_SetPath 2255
-#define wxDirPickerCtrl_destroy 2256
-#define wxColourPickerCtrl_new_0 2257
-#define wxColourPickerCtrl_new_3 2258
-#define wxColourPickerCtrl_Create 2259
-#define wxColourPickerCtrl_GetColour 2260
-#define wxColourPickerCtrl_SetColour_1_1 2261
-#define wxColourPickerCtrl_SetColour_1_0 2262
-#define wxColourPickerCtrl_destroy 2263
-#define wxDatePickerCtrl_new_0 2264
-#define wxDatePickerCtrl_new_3 2265
-#define wxDatePickerCtrl_GetRange 2266
-#define wxDatePickerCtrl_GetValue 2267
-#define wxDatePickerCtrl_SetRange 2268
-#define wxDatePickerCtrl_SetValue 2269
-#define wxDatePickerCtrl_destroy 2270
-#define wxFontPickerCtrl_new_0 2271
-#define wxFontPickerCtrl_new_3 2272
-#define wxFontPickerCtrl_Create 2273
-#define wxFontPickerCtrl_GetSelectedFont 2274
-#define wxFontPickerCtrl_SetSelectedFont 2275
-#define wxFontPickerCtrl_GetMaxPointSize 2276
-#define wxFontPickerCtrl_SetMaxPointSize 2277
-#define wxFontPickerCtrl_destroy 2278
-#define wxFindReplaceDialog_new_0 2281
-#define wxFindReplaceDialog_new_4 2282
-#define wxFindReplaceDialog_destruct 2283
-#define wxFindReplaceDialog_Create 2284
-#define wxFindReplaceDialog_GetData 2285
-#define wxFindReplaceData_new_0 2286
-#define wxFindReplaceData_new_1 2287
-#define wxFindReplaceData_GetFindString 2288
-#define wxFindReplaceData_GetReplaceString 2289
-#define wxFindReplaceData_GetFlags 2290
-#define wxFindReplaceData_SetFlags 2291
-#define wxFindReplaceData_SetFindString 2292
-#define wxFindReplaceData_SetReplaceString 2293
-#define wxFindReplaceData_destroy 2294
-#define wxMultiChoiceDialog_new_0 2295
-#define wxMultiChoiceDialog_new_5 2297
-#define wxMultiChoiceDialog_GetSelections 2298
-#define wxMultiChoiceDialog_SetSelections 2299
-#define wxMultiChoiceDialog_destroy 2300
-#define wxSingleChoiceDialog_new_0 2301
-#define wxSingleChoiceDialog_new_5 2303
-#define wxSingleChoiceDialog_GetSelection 2304
-#define wxSingleChoiceDialog_GetStringSelection 2305
-#define wxSingleChoiceDialog_SetSelection 2306
-#define wxSingleChoiceDialog_destroy 2307
-#define wxTextEntryDialog_new 2308
-#define wxTextEntryDialog_GetValue 2309
-#define wxTextEntryDialog_SetValue 2310
-#define wxTextEntryDialog_destroy 2311
-#define wxPasswordEntryDialog_new 2312
-#define wxPasswordEntryDialog_destroy 2313
-#define wxFontData_new_0 2314
-#define wxFontData_new_1 2315
-#define wxFontData_destruct 2316
-#define wxFontData_EnableEffects 2317
-#define wxFontData_GetAllowSymbols 2318
-#define wxFontData_GetColour 2319
-#define wxFontData_GetChosenFont 2320
-#define wxFontData_GetEnableEffects 2321
-#define wxFontData_GetInitialFont 2322
-#define wxFontData_GetShowHelp 2323
-#define wxFontData_SetAllowSymbols 2324
-#define wxFontData_SetChosenFont 2325
-#define wxFontData_SetColour 2326
-#define wxFontData_SetInitialFont 2327
-#define wxFontData_SetRange 2328
-#define wxFontData_SetShowHelp 2329
-#define wxFontDialog_new_0 2333
-#define wxFontDialog_new_2 2335
-#define wxFontDialog_Create 2337
-#define wxFontDialog_GetFontData 2338
-#define wxFontDialog_destroy 2340
-#define wxProgressDialog_new 2341
-#define wxProgressDialog_destruct 2342
-#define wxProgressDialog_Resume 2343
-#define wxProgressDialog_Update_2 2344
-#define wxProgressDialog_Update_0 2345
-#define wxMessageDialog_new 2346
-#define wxMessageDialog_destruct 2347
-#define wxPageSetupDialog_new 2348
-#define wxPageSetupDialog_destruct 2349
-#define wxPageSetupDialog_GetPageSetupData 2350
-#define wxPageSetupDialog_ShowModal 2351
-#define wxPageSetupDialogData_new_0 2352
-#define wxPageSetupDialogData_new_1_0 2353
-#define wxPageSetupDialogData_new_1_1 2354
-#define wxPageSetupDialogData_destruct 2355
-#define wxPageSetupDialogData_EnableHelp 2356
-#define wxPageSetupDialogData_EnableMargins 2357
-#define wxPageSetupDialogData_EnableOrientation 2358
-#define wxPageSetupDialogData_EnablePaper 2359
-#define wxPageSetupDialogData_EnablePrinter 2360
-#define wxPageSetupDialogData_GetDefaultMinMargins 2361
-#define wxPageSetupDialogData_GetEnableMargins 2362
-#define wxPageSetupDialogData_GetEnableOrientation 2363
-#define wxPageSetupDialogData_GetEnablePaper 2364
-#define wxPageSetupDialogData_GetEnablePrinter 2365
-#define wxPageSetupDialogData_GetEnableHelp 2366
-#define wxPageSetupDialogData_GetDefaultInfo 2367
-#define wxPageSetupDialogData_GetMarginTopLeft 2368
-#define wxPageSetupDialogData_GetMarginBottomRight 2369
-#define wxPageSetupDialogData_GetMinMarginTopLeft 2370
-#define wxPageSetupDialogData_GetMinMarginBottomRight 2371
-#define wxPageSetupDialogData_GetPaperId 2372
-#define wxPageSetupDialogData_GetPaperSize 2373
-#define wxPageSetupDialogData_GetPrintData 2375
-#define wxPageSetupDialogData_IsOk 2376
-#define wxPageSetupDialogData_SetDefaultInfo 2377
-#define wxPageSetupDialogData_SetDefaultMinMargins 2378
-#define wxPageSetupDialogData_SetMarginTopLeft 2379
-#define wxPageSetupDialogData_SetMarginBottomRight 2380
-#define wxPageSetupDialogData_SetMinMarginTopLeft 2381
-#define wxPageSetupDialogData_SetMinMarginBottomRight 2382
-#define wxPageSetupDialogData_SetPaperId 2383
-#define wxPageSetupDialogData_SetPaperSize_1_1 2384
-#define wxPageSetupDialogData_SetPaperSize_1_0 2385
-#define wxPageSetupDialogData_SetPrintData 2386
-#define wxPrintDialog_new_2_0 2387
-#define wxPrintDialog_new_2_1 2388
-#define wxPrintDialog_destruct 2389
-#define wxPrintDialog_GetPrintDialogData 2390
-#define wxPrintDialog_GetPrintDC 2391
-#define wxPrintDialogData_new_0 2392
-#define wxPrintDialogData_new_1_1 2393
-#define wxPrintDialogData_new_1_0 2394
-#define wxPrintDialogData_destruct 2395
-#define wxPrintDialogData_EnableHelp 2396
-#define wxPrintDialogData_EnablePageNumbers 2397
-#define wxPrintDialogData_EnablePrintToFile 2398
-#define wxPrintDialogData_EnableSelection 2399
-#define wxPrintDialogData_GetAllPages 2400
-#define wxPrintDialogData_GetCollate 2401
-#define wxPrintDialogData_GetFromPage 2402
-#define wxPrintDialogData_GetMaxPage 2403
-#define wxPrintDialogData_GetMinPage 2404
-#define wxPrintDialogData_GetNoCopies 2405
-#define wxPrintDialogData_GetPrintData 2406
-#define wxPrintDialogData_GetPrintToFile 2407
-#define wxPrintDialogData_GetSelection 2408
-#define wxPrintDialogData_GetToPage 2409
-#define wxPrintDialogData_IsOk 2410
-#define wxPrintDialogData_SetCollate 2411
-#define wxPrintDialogData_SetFromPage 2412
-#define wxPrintDialogData_SetMaxPage 2413
-#define wxPrintDialogData_SetMinPage 2414
-#define wxPrintDialogData_SetNoCopies 2415
-#define wxPrintDialogData_SetPrintData 2416
-#define wxPrintDialogData_SetPrintToFile 2417
-#define wxPrintDialogData_SetSelection 2418
-#define wxPrintDialogData_SetToPage 2419
-#define wxPrintData_new_0 2420
-#define wxPrintData_new_1 2421
-#define wxPrintData_destruct 2422
-#define wxPrintData_GetCollate 2423
-#define wxPrintData_GetBin 2424
-#define wxPrintData_GetColour 2425
-#define wxPrintData_GetDuplex 2426
-#define wxPrintData_GetNoCopies 2427
-#define wxPrintData_GetOrientation 2428
-#define wxPrintData_GetPaperId 2429
-#define wxPrintData_GetPrinterName 2430
-#define wxPrintData_GetQuality 2431
-#define wxPrintData_IsOk 2432
-#define wxPrintData_SetBin 2433
-#define wxPrintData_SetCollate 2434
-#define wxPrintData_SetColour 2435
-#define wxPrintData_SetDuplex 2436
-#define wxPrintData_SetNoCopies 2437
-#define wxPrintData_SetOrientation 2438
-#define wxPrintData_SetPaperId 2439
-#define wxPrintData_SetPrinterName 2440
-#define wxPrintData_SetQuality 2441
-#define wxPrintPreview_new_2 2444
-#define wxPrintPreview_new_3 2445
-#define wxPrintPreview_destruct 2447
-#define wxPrintPreview_GetCanvas 2448
-#define wxPrintPreview_GetCurrentPage 2449
-#define wxPrintPreview_GetFrame 2450
-#define wxPrintPreview_GetMaxPage 2451
-#define wxPrintPreview_GetMinPage 2452
-#define wxPrintPreview_GetPrintout 2453
-#define wxPrintPreview_GetPrintoutForPrinting 2454
-#define wxPrintPreview_IsOk 2455
-#define wxPrintPreview_PaintPage 2456
-#define wxPrintPreview_Print 2457
-#define wxPrintPreview_RenderPage 2458
-#define wxPrintPreview_SetCanvas 2459
-#define wxPrintPreview_SetCurrentPage 2460
-#define wxPrintPreview_SetFrame 2461
-#define wxPrintPreview_SetPrintout 2462
-#define wxPrintPreview_SetZoom 2463
-#define wxPreviewFrame_new 2464
-#define wxPreviewFrame_destruct 2465
-#define wxPreviewFrame_CreateControlBar 2466
-#define wxPreviewFrame_CreateCanvas 2467
-#define wxPreviewFrame_Initialize 2468
-#define wxPreviewFrame_OnCloseWindow 2469
-#define wxPreviewControlBar_new 2470
-#define wxPreviewControlBar_destruct 2471
-#define wxPreviewControlBar_CreateButtons 2472
-#define wxPreviewControlBar_GetPrintPreview 2473
-#define wxPreviewControlBar_GetZoomControl 2474
-#define wxPreviewControlBar_SetZoomControl 2475
-#define wxPrinter_new 2477
-#define wxPrinter_CreateAbortWindow 2478
-#define wxPrinter_GetAbort 2479
-#define wxPrinter_GetLastError 2480
-#define wxPrinter_GetPrintDialogData 2481
-#define wxPrinter_Print 2482
-#define wxPrinter_PrintDialog 2483
-#define wxPrinter_ReportError 2484
-#define wxPrinter_Setup 2485
-#define wxPrinter_destroy 2486
-#define wxXmlResource_new_1 2487
-#define wxXmlResource_new_2 2488
-#define wxXmlResource_destruct 2489
-#define wxXmlResource_AttachUnknownControl 2490
-#define wxXmlResource_ClearHandlers 2491
-#define wxXmlResource_CompareVersion 2492
-#define wxXmlResource_Get 2493
-#define wxXmlResource_GetFlags 2494
-#define wxXmlResource_GetVersion 2495
-#define wxXmlResource_GetXRCID 2496
-#define wxXmlResource_InitAllHandlers 2497
-#define wxXmlResource_Load 2498
-#define wxXmlResource_LoadBitmap 2499
-#define wxXmlResource_LoadDialog_2 2500
-#define wxXmlResource_LoadDialog_3 2501
-#define wxXmlResource_LoadFrame_2 2502
-#define wxXmlResource_LoadFrame_3 2503
-#define wxXmlResource_LoadIcon 2504
-#define wxXmlResource_LoadMenu 2505
-#define wxXmlResource_LoadMenuBar_2 2506
-#define wxXmlResource_LoadMenuBar_1 2507
-#define wxXmlResource_LoadPanel_2 2508
-#define wxXmlResource_LoadPanel_3 2509
-#define wxXmlResource_LoadToolBar 2510
-#define wxXmlResource_Set 2511
-#define wxXmlResource_SetFlags 2512
-#define wxXmlResource_Unload 2513
-#define wxXmlResource_xrcctrl 2514
-#define wxHtmlEasyPrinting_new 2515
-#define wxHtmlEasyPrinting_destruct 2516
-#define wxHtmlEasyPrinting_GetPrintData 2517
-#define wxHtmlEasyPrinting_GetPageSetupData 2518
-#define wxHtmlEasyPrinting_PreviewFile 2519
-#define wxHtmlEasyPrinting_PreviewText 2520
-#define wxHtmlEasyPrinting_PrintFile 2521
-#define wxHtmlEasyPrinting_PrintText 2522
-#define wxHtmlEasyPrinting_PageSetup 2523
-#define wxHtmlEasyPrinting_SetFonts 2524
-#define wxHtmlEasyPrinting_SetHeader 2525
-#define wxHtmlEasyPrinting_SetFooter 2526
-#define wxGLCanvas_new_2 2528
-#define wxGLCanvas_new_3_1 2529
-#define wxGLCanvas_new_3_0 2530
-#define wxGLCanvas_GetContext 2531
-#define wxGLCanvas_SetCurrent 2533
-#define wxGLCanvas_SwapBuffers 2534
-#define wxGLCanvas_destroy 2535
-#define wxAuiManager_new 2536
-#define wxAuiManager_destruct 2537
-#define wxAuiManager_AddPane_2_1 2538
-#define wxAuiManager_AddPane_3 2539
-#define wxAuiManager_AddPane_2_0 2540
-#define wxAuiManager_DetachPane 2541
-#define wxAuiManager_GetAllPanes 2542
-#define wxAuiManager_GetArtProvider 2543
-#define wxAuiManager_GetDockSizeConstraint 2544
-#define wxAuiManager_GetFlags 2545
-#define wxAuiManager_GetManagedWindow 2546
-#define wxAuiManager_GetManager 2547
-#define wxAuiManager_GetPane_1_1 2548
-#define wxAuiManager_GetPane_1_0 2549
-#define wxAuiManager_HideHint 2550
-#define wxAuiManager_InsertPane 2551
-#define wxAuiManager_LoadPaneInfo 2552
-#define wxAuiManager_LoadPerspective 2553
-#define wxAuiManager_SavePaneInfo 2554
-#define wxAuiManager_SavePerspective 2555
-#define wxAuiManager_SetArtProvider 2556
-#define wxAuiManager_SetDockSizeConstraint 2557
-#define wxAuiManager_SetFlags 2558
-#define wxAuiManager_SetManagedWindow 2559
-#define wxAuiManager_ShowHint 2560
-#define wxAuiManager_UnInit 2561
-#define wxAuiManager_Update 2562
-#define wxAuiPaneInfo_new_0 2563
-#define wxAuiPaneInfo_new_1 2564
-#define wxAuiPaneInfo_destruct 2565
-#define wxAuiPaneInfo_BestSize_1 2566
-#define wxAuiPaneInfo_BestSize_2 2567
-#define wxAuiPaneInfo_Bottom 2568
-#define wxAuiPaneInfo_BottomDockable 2569
-#define wxAuiPaneInfo_Caption 2570
-#define wxAuiPaneInfo_CaptionVisible 2571
-#define wxAuiPaneInfo_Centre 2572
-#define wxAuiPaneInfo_CentrePane 2573
-#define wxAuiPaneInfo_CloseButton 2574
-#define wxAuiPaneInfo_DefaultPane 2575
-#define wxAuiPaneInfo_DestroyOnClose 2576
-#define wxAuiPaneInfo_Direction 2577
-#define wxAuiPaneInfo_Dock 2578
-#define wxAuiPaneInfo_Dockable 2579
-#define wxAuiPaneInfo_Fixed 2580
-#define wxAuiPaneInfo_Float 2581
-#define wxAuiPaneInfo_Floatable 2582
-#define wxAuiPaneInfo_FloatingPosition_1 2583
-#define wxAuiPaneInfo_FloatingPosition_2 2584
-#define wxAuiPaneInfo_FloatingSize_1 2585
-#define wxAuiPaneInfo_FloatingSize_2 2586
-#define wxAuiPaneInfo_Gripper 2587
-#define wxAuiPaneInfo_GripperTop 2588
-#define wxAuiPaneInfo_HasBorder 2589
-#define wxAuiPaneInfo_HasCaption 2590
-#define wxAuiPaneInfo_HasCloseButton 2591
-#define wxAuiPaneInfo_HasFlag 2592
-#define wxAuiPaneInfo_HasGripper 2593
-#define wxAuiPaneInfo_HasGripperTop 2594
-#define wxAuiPaneInfo_HasMaximizeButton 2595
-#define wxAuiPaneInfo_HasMinimizeButton 2596
-#define wxAuiPaneInfo_HasPinButton 2597
-#define wxAuiPaneInfo_Hide 2598
-#define wxAuiPaneInfo_IsBottomDockable 2599
-#define wxAuiPaneInfo_IsDocked 2600
-#define wxAuiPaneInfo_IsFixed 2601
-#define wxAuiPaneInfo_IsFloatable 2602
-#define wxAuiPaneInfo_IsFloating 2603
-#define wxAuiPaneInfo_IsLeftDockable 2604
-#define wxAuiPaneInfo_IsMovable 2605
-#define wxAuiPaneInfo_IsOk 2606
-#define wxAuiPaneInfo_IsResizable 2607
-#define wxAuiPaneInfo_IsRightDockable 2608
-#define wxAuiPaneInfo_IsShown 2609
-#define wxAuiPaneInfo_IsToolbar 2610
-#define wxAuiPaneInfo_IsTopDockable 2611
-#define wxAuiPaneInfo_Layer 2612
-#define wxAuiPaneInfo_Left 2613
-#define wxAuiPaneInfo_LeftDockable 2614
-#define wxAuiPaneInfo_MaxSize_1 2615
-#define wxAuiPaneInfo_MaxSize_2 2616
-#define wxAuiPaneInfo_MaximizeButton 2617
-#define wxAuiPaneInfo_MinSize_1 2618
-#define wxAuiPaneInfo_MinSize_2 2619
-#define wxAuiPaneInfo_MinimizeButton 2620
-#define wxAuiPaneInfo_Movable 2621
-#define wxAuiPaneInfo_Name 2622
-#define wxAuiPaneInfo_PaneBorder 2623
-#define wxAuiPaneInfo_PinButton 2624
-#define wxAuiPaneInfo_Position 2625
-#define wxAuiPaneInfo_Resizable 2626
-#define wxAuiPaneInfo_Right 2627
-#define wxAuiPaneInfo_RightDockable 2628
-#define wxAuiPaneInfo_Row 2629
-#define wxAuiPaneInfo_SafeSet 2630
-#define wxAuiPaneInfo_SetFlag 2631
-#define wxAuiPaneInfo_Show 2632
-#define wxAuiPaneInfo_ToolbarPane 2633
-#define wxAuiPaneInfo_Top 2634
-#define wxAuiPaneInfo_TopDockable 2635
-#define wxAuiPaneInfo_Window 2636
-#define wxAuiNotebook_new_0 2637
-#define wxAuiNotebook_new_2 2638
-#define wxAuiNotebook_AddPage 2639
-#define wxAuiNotebook_Create 2640
-#define wxAuiNotebook_DeletePage 2641
-#define wxAuiNotebook_GetArtProvider 2642
-#define wxAuiNotebook_GetPage 2643
-#define wxAuiNotebook_GetPageBitmap 2644
-#define wxAuiNotebook_GetPageCount 2645
-#define wxAuiNotebook_GetPageIndex 2646
-#define wxAuiNotebook_GetPageText 2647
-#define wxAuiNotebook_GetSelection 2648
-#define wxAuiNotebook_InsertPage 2649
-#define wxAuiNotebook_RemovePage 2650
-#define wxAuiNotebook_SetArtProvider 2651
-#define wxAuiNotebook_SetFont 2652
-#define wxAuiNotebook_SetPageBitmap 2653
-#define wxAuiNotebook_SetPageText 2654
-#define wxAuiNotebook_SetSelection 2655
-#define wxAuiNotebook_SetTabCtrlHeight 2656
-#define wxAuiNotebook_SetUniformBitmapSize 2657
-#define wxAuiNotebook_destroy 2658
-#define wxMDIParentFrame_new_0 2659
-#define wxMDIParentFrame_new_4 2660
-#define wxMDIParentFrame_destruct 2661
-#define wxMDIParentFrame_ActivateNext 2662
-#define wxMDIParentFrame_ActivatePrevious 2663
-#define wxMDIParentFrame_ArrangeIcons 2664
-#define wxMDIParentFrame_Cascade 2665
-#define wxMDIParentFrame_Create 2666
-#define wxMDIParentFrame_GetActiveChild 2667
-#define wxMDIParentFrame_GetClientWindow 2668
-#define wxMDIParentFrame_Tile 2669
-#define wxMDIChildFrame_new_0 2670
-#define wxMDIChildFrame_new_4 2671
-#define wxMDIChildFrame_destruct 2672
-#define wxMDIChildFrame_Activate 2673
-#define wxMDIChildFrame_Create 2674
-#define wxMDIChildFrame_Maximize 2675
-#define wxMDIChildFrame_Restore 2676
-#define wxMDIClientWindow_new_0 2677
-#define wxMDIClientWindow_new_2 2678
-#define wxMDIClientWindow_destruct 2679
-#define wxMDIClientWindow_CreateClient 2680
-#define wxLayoutAlgorithm_new 2681
-#define wxLayoutAlgorithm_LayoutFrame 2682
-#define wxLayoutAlgorithm_LayoutMDIFrame 2683
-#define wxLayoutAlgorithm_LayoutWindow 2684
-#define wxLayoutAlgorithm_destroy 2685
-#define wxEvent_GetId 2686
-#define wxEvent_GetSkipped 2687
-#define wxEvent_GetTimestamp 2688
-#define wxEvent_IsCommandEvent 2689
-#define wxEvent_ResumePropagation 2690
-#define wxEvent_ShouldPropagate 2691
-#define wxEvent_Skip 2692
-#define wxEvent_StopPropagation 2693
-#define wxCommandEvent_getClientData 2694
-#define wxCommandEvent_GetExtraLong 2695
-#define wxCommandEvent_GetInt 2696
-#define wxCommandEvent_GetSelection 2697
-#define wxCommandEvent_GetString 2698
-#define wxCommandEvent_IsChecked 2699
-#define wxCommandEvent_IsSelection 2700
-#define wxCommandEvent_SetInt 2701
-#define wxCommandEvent_SetString 2702
-#define wxScrollEvent_GetOrientation 2703
-#define wxScrollEvent_GetPosition 2704
-#define wxScrollWinEvent_GetOrientation 2705
-#define wxScrollWinEvent_GetPosition 2706
-#define wxMouseEvent_AltDown 2707
-#define wxMouseEvent_Button 2708
-#define wxMouseEvent_ButtonDClick 2709
-#define wxMouseEvent_ButtonDown 2710
-#define wxMouseEvent_ButtonUp 2711
-#define wxMouseEvent_CmdDown 2712
-#define wxMouseEvent_ControlDown 2713
-#define wxMouseEvent_Dragging 2714
-#define wxMouseEvent_Entering 2715
-#define wxMouseEvent_GetButton 2716
-#define wxMouseEvent_GetPosition 2719
-#define wxMouseEvent_GetLogicalPosition 2720
-#define wxMouseEvent_GetLinesPerAction 2721
-#define wxMouseEvent_GetWheelRotation 2722
-#define wxMouseEvent_GetWheelDelta 2723
-#define wxMouseEvent_GetX 2724
-#define wxMouseEvent_GetY 2725
-#define wxMouseEvent_IsButton 2726
-#define wxMouseEvent_IsPageScroll 2727
-#define wxMouseEvent_Leaving 2728
-#define wxMouseEvent_LeftDClick 2729
-#define wxMouseEvent_LeftDown 2730
-#define wxMouseEvent_LeftIsDown 2731
-#define wxMouseEvent_LeftUp 2732
-#define wxMouseEvent_MetaDown 2733
-#define wxMouseEvent_MiddleDClick 2734
-#define wxMouseEvent_MiddleDown 2735
-#define wxMouseEvent_MiddleIsDown 2736
-#define wxMouseEvent_MiddleUp 2737
-#define wxMouseEvent_Moving 2738
-#define wxMouseEvent_RightDClick 2739
-#define wxMouseEvent_RightDown 2740
-#define wxMouseEvent_RightIsDown 2741
-#define wxMouseEvent_RightUp 2742
-#define wxMouseEvent_ShiftDown 2743
-#define wxSetCursorEvent_GetCursor 2744
-#define wxSetCursorEvent_GetX 2745
-#define wxSetCursorEvent_GetY 2746
-#define wxSetCursorEvent_HasCursor 2747
-#define wxSetCursorEvent_SetCursor 2748
-#define wxKeyEvent_AltDown 2749
-#define wxKeyEvent_CmdDown 2750
-#define wxKeyEvent_ControlDown 2751
-#define wxKeyEvent_GetKeyCode 2752
-#define wxKeyEvent_GetModifiers 2753
-#define wxKeyEvent_GetPosition 2756
-#define wxKeyEvent_GetRawKeyCode 2757
-#define wxKeyEvent_GetRawKeyFlags 2758
-#define wxKeyEvent_GetUnicodeKey 2759
-#define wxKeyEvent_GetX 2760
-#define wxKeyEvent_GetY 2761
-#define wxKeyEvent_HasModifiers 2762
-#define wxKeyEvent_MetaDown 2763
-#define wxKeyEvent_ShiftDown 2764
-#define wxSizeEvent_GetSize 2765
-#define wxMoveEvent_GetPosition 2766
-#define wxEraseEvent_GetDC 2767
-#define wxFocusEvent_GetWindow 2768
-#define wxChildFocusEvent_GetWindow 2769
-#define wxMenuEvent_GetMenu 2770
-#define wxMenuEvent_GetMenuId 2771
-#define wxMenuEvent_IsPopup 2772
-#define wxCloseEvent_CanVeto 2773
-#define wxCloseEvent_GetLoggingOff 2774
-#define wxCloseEvent_SetCanVeto 2775
-#define wxCloseEvent_SetLoggingOff 2776
-#define wxCloseEvent_Veto 2777
-#define wxShowEvent_SetShow 2778
-#define wxShowEvent_GetShow 2779
-#define wxIconizeEvent_Iconized 2780
-#define wxJoystickEvent_ButtonDown 2781
-#define wxJoystickEvent_ButtonIsDown 2782
-#define wxJoystickEvent_ButtonUp 2783
-#define wxJoystickEvent_GetButtonChange 2784
-#define wxJoystickEvent_GetButtonState 2785
-#define wxJoystickEvent_GetJoystick 2786
-#define wxJoystickEvent_GetPosition 2787
-#define wxJoystickEvent_GetZPosition 2788
-#define wxJoystickEvent_IsButton 2789
-#define wxJoystickEvent_IsMove 2790
-#define wxJoystickEvent_IsZMove 2791
-#define wxUpdateUIEvent_CanUpdate 2792
-#define wxUpdateUIEvent_Check 2793
-#define wxUpdateUIEvent_Enable 2794
-#define wxUpdateUIEvent_Show 2795
-#define wxUpdateUIEvent_GetChecked 2796
-#define wxUpdateUIEvent_GetEnabled 2797
-#define wxUpdateUIEvent_GetShown 2798
-#define wxUpdateUIEvent_GetSetChecked 2799
-#define wxUpdateUIEvent_GetSetEnabled 2800
-#define wxUpdateUIEvent_GetSetShown 2801
-#define wxUpdateUIEvent_GetSetText 2802
-#define wxUpdateUIEvent_GetText 2803
-#define wxUpdateUIEvent_GetMode 2804
-#define wxUpdateUIEvent_GetUpdateInterval 2805
-#define wxUpdateUIEvent_ResetUpdateTime 2806
-#define wxUpdateUIEvent_SetMode 2807
-#define wxUpdateUIEvent_SetText 2808
-#define wxUpdateUIEvent_SetUpdateInterval 2809
-#define wxMouseCaptureChangedEvent_GetCapturedWindow 2810
-#define wxPaletteChangedEvent_SetChangedWindow 2811
-#define wxPaletteChangedEvent_GetChangedWindow 2812
-#define wxQueryNewPaletteEvent_SetPaletteRealized 2813
-#define wxQueryNewPaletteEvent_GetPaletteRealized 2814
-#define wxNavigationKeyEvent_GetDirection 2815
-#define wxNavigationKeyEvent_SetDirection 2816
-#define wxNavigationKeyEvent_IsWindowChange 2817
-#define wxNavigationKeyEvent_SetWindowChange 2818
-#define wxNavigationKeyEvent_IsFromTab 2819
-#define wxNavigationKeyEvent_SetFromTab 2820
-#define wxNavigationKeyEvent_GetCurrentFocus 2821
-#define wxNavigationKeyEvent_SetCurrentFocus 2822
-#define wxHelpEvent_GetOrigin 2823
-#define wxHelpEvent_GetPosition 2824
-#define wxHelpEvent_SetOrigin 2825
-#define wxHelpEvent_SetPosition 2826
-#define wxContextMenuEvent_GetPosition 2827
-#define wxContextMenuEvent_SetPosition 2828
-#define wxIdleEvent_CanSend 2829
-#define wxIdleEvent_GetMode 2830
-#define wxIdleEvent_RequestMore 2831
-#define wxIdleEvent_MoreRequested 2832
-#define wxIdleEvent_SetMode 2833
-#define wxGridEvent_AltDown 2834
-#define wxGridEvent_ControlDown 2835
-#define wxGridEvent_GetCol 2836
-#define wxGridEvent_GetPosition 2837
-#define wxGridEvent_GetRow 2838
-#define wxGridEvent_MetaDown 2839
-#define wxGridEvent_Selecting 2840
-#define wxGridEvent_ShiftDown 2841
-#define wxNotifyEvent_Allow 2842
-#define wxNotifyEvent_IsAllowed 2843
-#define wxNotifyEvent_Veto 2844
-#define wxSashEvent_GetEdge 2845
-#define wxSashEvent_GetDragRect 2846
-#define wxSashEvent_GetDragStatus 2847
-#define wxListEvent_GetCacheFrom 2848
-#define wxListEvent_GetCacheTo 2849
-#define wxListEvent_GetKeyCode 2850
-#define wxListEvent_GetIndex 2851
-#define wxListEvent_GetColumn 2852
-#define wxListEvent_GetPoint 2853
-#define wxListEvent_GetLabel 2854
-#define wxListEvent_GetText 2855
-#define wxListEvent_GetImage 2856
-#define wxListEvent_GetData 2857
-#define wxListEvent_GetMask 2858
-#define wxListEvent_GetItem 2859
-#define wxListEvent_IsEditCancelled 2860
-#define wxDateEvent_GetDate 2861
-#define wxCalendarEvent_GetWeekDay 2862
-#define wxFileDirPickerEvent_GetPath 2863
-#define wxColourPickerEvent_GetColour 2864
-#define wxFontPickerEvent_GetFont 2865
-#define wxStyledTextEvent_GetPosition 2866
-#define wxStyledTextEvent_GetKey 2867
-#define wxStyledTextEvent_GetModifiers 2868
-#define wxStyledTextEvent_GetModificationType 2869
-#define wxStyledTextEvent_GetText 2870
-#define wxStyledTextEvent_GetLength 2871
-#define wxStyledTextEvent_GetLinesAdded 2872
-#define wxStyledTextEvent_GetLine 2873
-#define wxStyledTextEvent_GetFoldLevelNow 2874
-#define wxStyledTextEvent_GetFoldLevelPrev 2875
-#define wxStyledTextEvent_GetMargin 2876
-#define wxStyledTextEvent_GetMessage 2877
-#define wxStyledTextEvent_GetWParam 2878
-#define wxStyledTextEvent_GetLParam 2879
-#define wxStyledTextEvent_GetListType 2880
-#define wxStyledTextEvent_GetX 2881
-#define wxStyledTextEvent_GetY 2882
-#define wxStyledTextEvent_GetDragText 2883
-#define wxStyledTextEvent_GetDragAllowMove 2884
-#define wxStyledTextEvent_GetDragResult 2885
-#define wxStyledTextEvent_GetShift 2886
-#define wxStyledTextEvent_GetControl 2887
-#define wxStyledTextEvent_GetAlt 2888
-#define utils_wxGetKeyState 2889
-#define utils_wxGetMousePosition 2890
-#define utils_wxGetMouseState 2891
-#define utils_wxSetDetectableAutoRepeat 2892
-#define utils_wxBell 2893
-#define utils_wxFindMenuItemId 2894
-#define utils_wxGenericFindWindowAtPoint 2895
-#define utils_wxFindWindowAtPoint 2896
-#define utils_wxBeginBusyCursor 2897
-#define utils_wxEndBusyCursor 2898
-#define utils_wxIsBusy 2899
-#define utils_wxShutdown 2900
-#define utils_wxShell 2901
-#define utils_wxLaunchDefaultBrowser 2902
-#define utils_wxGetEmailAddress 2903
-#define utils_wxGetUserId 2904
-#define utils_wxGetHomeDir 2905
-#define utils_wxNewId 2906
-#define utils_wxRegisterId 2907
-#define utils_wxGetCurrentId 2908
-#define utils_wxGetOsDescription 2909
-#define utils_wxIsPlatformLittleEndian 2910
-#define utils_wxIsPlatform64Bit 2911
-#define wxPrintout_new 2912
-#define wxPrintout_destruct 2913
-#define wxPrintout_GetDC 2914
-#define wxPrintout_GetPageSizeMM 2915
-#define wxPrintout_GetPageSizePixels 2916
-#define wxPrintout_GetPaperRectPixels 2917
-#define wxPrintout_GetPPIPrinter 2918
-#define wxPrintout_GetPPIScreen 2919
-#define wxPrintout_GetTitle 2920
-#define wxPrintout_IsPreview 2921
-#define wxPrintout_FitThisSizeToPaper 2922
-#define wxPrintout_FitThisSizeToPage 2923
-#define wxPrintout_FitThisSizeToPageMargins 2924
-#define wxPrintout_MapScreenSizeToPaper 2925
-#define wxPrintout_MapScreenSizeToPage 2926
-#define wxPrintout_MapScreenSizeToPageMargins 2927
-#define wxPrintout_MapScreenSizeToDevice 2928
-#define wxPrintout_GetLogicalPaperRect 2929
-#define wxPrintout_GetLogicalPageRect 2930
-#define wxPrintout_GetLogicalPageMarginsRect 2931
-#define wxPrintout_SetLogicalOrigin 2932
-#define wxPrintout_OffsetLogicalOrigin 2933
-#define wxStyledTextCtrl_new_2 2934
-#define wxStyledTextCtrl_new_0 2935
-#define wxStyledTextCtrl_destruct 2936
-#define wxStyledTextCtrl_Create 2937
-#define wxStyledTextCtrl_AddText 2938
-#define wxStyledTextCtrl_AddStyledText 2939
-#define wxStyledTextCtrl_InsertText 2940
-#define wxStyledTextCtrl_ClearAll 2941
-#define wxStyledTextCtrl_ClearDocumentStyle 2942
-#define wxStyledTextCtrl_GetLength 2943
-#define wxStyledTextCtrl_GetCharAt 2944
-#define wxStyledTextCtrl_GetCurrentPos 2945
-#define wxStyledTextCtrl_GetAnchor 2946
-#define wxStyledTextCtrl_GetStyleAt 2947
-#define wxStyledTextCtrl_Redo 2948
-#define wxStyledTextCtrl_SetUndoCollection 2949
-#define wxStyledTextCtrl_SelectAll 2950
-#define wxStyledTextCtrl_SetSavePoint 2951
-#define wxStyledTextCtrl_GetStyledText 2952
-#define wxStyledTextCtrl_CanRedo 2953
-#define wxStyledTextCtrl_MarkerLineFromHandle 2954
-#define wxStyledTextCtrl_MarkerDeleteHandle 2955
-#define wxStyledTextCtrl_GetUndoCollection 2956
-#define wxStyledTextCtrl_GetViewWhiteSpace 2957
-#define wxStyledTextCtrl_SetViewWhiteSpace 2958
-#define wxStyledTextCtrl_PositionFromPoint 2959
-#define wxStyledTextCtrl_PositionFromPointClose 2960
-#define wxStyledTextCtrl_GotoLine 2961
-#define wxStyledTextCtrl_GotoPos 2962
-#define wxStyledTextCtrl_SetAnchor 2963
-#define wxStyledTextCtrl_GetCurLine 2964
-#define wxStyledTextCtrl_GetEndStyled 2965
-#define wxStyledTextCtrl_ConvertEOLs 2966
-#define wxStyledTextCtrl_GetEOLMode 2967
-#define wxStyledTextCtrl_SetEOLMode 2968
-#define wxStyledTextCtrl_StartStyling 2969
-#define wxStyledTextCtrl_SetStyling 2970
-#define wxStyledTextCtrl_GetBufferedDraw 2971
-#define wxStyledTextCtrl_SetBufferedDraw 2972
-#define wxStyledTextCtrl_SetTabWidth 2973
-#define wxStyledTextCtrl_GetTabWidth 2974
-#define wxStyledTextCtrl_SetCodePage 2975
-#define wxStyledTextCtrl_MarkerDefine 2976
-#define wxStyledTextCtrl_MarkerSetForeground 2977
-#define wxStyledTextCtrl_MarkerSetBackground 2978
-#define wxStyledTextCtrl_MarkerAdd 2979
-#define wxStyledTextCtrl_MarkerDelete 2980
-#define wxStyledTextCtrl_MarkerDeleteAll 2981
-#define wxStyledTextCtrl_MarkerGet 2982
-#define wxStyledTextCtrl_MarkerNext 2983
-#define wxStyledTextCtrl_MarkerPrevious 2984
-#define wxStyledTextCtrl_MarkerDefineBitmap 2985
-#define wxStyledTextCtrl_MarkerAddSet 2986
-#define wxStyledTextCtrl_MarkerSetAlpha 2987
-#define wxStyledTextCtrl_SetMarginType 2988
-#define wxStyledTextCtrl_GetMarginType 2989
-#define wxStyledTextCtrl_SetMarginWidth 2990
-#define wxStyledTextCtrl_GetMarginWidth 2991
-#define wxStyledTextCtrl_SetMarginMask 2992
-#define wxStyledTextCtrl_GetMarginMask 2993
-#define wxStyledTextCtrl_SetMarginSensitive 2994
-#define wxStyledTextCtrl_GetMarginSensitive 2995
-#define wxStyledTextCtrl_StyleClearAll 2996
-#define wxStyledTextCtrl_StyleSetForeground 2997
-#define wxStyledTextCtrl_StyleSetBackground 2998
-#define wxStyledTextCtrl_StyleSetBold 2999
-#define wxStyledTextCtrl_StyleSetItalic 3000
-#define wxStyledTextCtrl_StyleSetSize 3001
-#define wxStyledTextCtrl_StyleSetFaceName 3002
-#define wxStyledTextCtrl_StyleSetEOLFilled 3003
-#define wxStyledTextCtrl_StyleResetDefault 3004
-#define wxStyledTextCtrl_StyleSetUnderline 3005
-#define wxStyledTextCtrl_StyleSetCase 3006
-#define wxStyledTextCtrl_StyleSetHotSpot 3007
-#define wxStyledTextCtrl_SetSelForeground 3008
-#define wxStyledTextCtrl_SetSelBackground 3009
-#define wxStyledTextCtrl_GetSelAlpha 3010
-#define wxStyledTextCtrl_SetSelAlpha 3011
-#define wxStyledTextCtrl_SetCaretForeground 3012
-#define wxStyledTextCtrl_CmdKeyAssign 3013
-#define wxStyledTextCtrl_CmdKeyClear 3014
-#define wxStyledTextCtrl_CmdKeyClearAll 3015
-#define wxStyledTextCtrl_SetStyleBytes 3016
-#define wxStyledTextCtrl_StyleSetVisible 3017
-#define wxStyledTextCtrl_GetCaretPeriod 3018
-#define wxStyledTextCtrl_SetCaretPeriod 3019
-#define wxStyledTextCtrl_SetWordChars 3020
-#define wxStyledTextCtrl_BeginUndoAction 3021
-#define wxStyledTextCtrl_EndUndoAction 3022
-#define wxStyledTextCtrl_IndicatorSetStyle 3023
-#define wxStyledTextCtrl_IndicatorGetStyle 3024
-#define wxStyledTextCtrl_IndicatorSetForeground 3025
-#define wxStyledTextCtrl_IndicatorGetForeground 3026
-#define wxStyledTextCtrl_SetWhitespaceForeground 3027
-#define wxStyledTextCtrl_SetWhitespaceBackground 3028
-#define wxStyledTextCtrl_GetStyleBits 3029
-#define wxStyledTextCtrl_SetLineState 3030
-#define wxStyledTextCtrl_GetLineState 3031
-#define wxStyledTextCtrl_GetMaxLineState 3032
-#define wxStyledTextCtrl_GetCaretLineVisible 3033
-#define wxStyledTextCtrl_SetCaretLineVisible 3034
-#define wxStyledTextCtrl_GetCaretLineBackground 3035
-#define wxStyledTextCtrl_SetCaretLineBackground 3036
-#define wxStyledTextCtrl_AutoCompShow 3037
-#define wxStyledTextCtrl_AutoCompCancel 3038
-#define wxStyledTextCtrl_AutoCompActive 3039
-#define wxStyledTextCtrl_AutoCompPosStart 3040
-#define wxStyledTextCtrl_AutoCompComplete 3041
-#define wxStyledTextCtrl_AutoCompStops 3042
-#define wxStyledTextCtrl_AutoCompSetSeparator 3043
-#define wxStyledTextCtrl_AutoCompGetSeparator 3044
-#define wxStyledTextCtrl_AutoCompSelect 3045
-#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3046
-#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3047
-#define wxStyledTextCtrl_AutoCompSetFillUps 3048
-#define wxStyledTextCtrl_AutoCompSetChooseSingle 3049
-#define wxStyledTextCtrl_AutoCompGetChooseSingle 3050
-#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3051
-#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3052
-#define wxStyledTextCtrl_UserListShow 3053
-#define wxStyledTextCtrl_AutoCompSetAutoHide 3054
-#define wxStyledTextCtrl_AutoCompGetAutoHide 3055
-#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3056
-#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3057
-#define wxStyledTextCtrl_RegisterImage 3058
-#define wxStyledTextCtrl_ClearRegisteredImages 3059
-#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3060
-#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3061
-#define wxStyledTextCtrl_AutoCompSetMaxWidth 3062
-#define wxStyledTextCtrl_AutoCompGetMaxWidth 3063
-#define wxStyledTextCtrl_AutoCompSetMaxHeight 3064
-#define wxStyledTextCtrl_AutoCompGetMaxHeight 3065
-#define wxStyledTextCtrl_SetIndent 3066
-#define wxStyledTextCtrl_GetIndent 3067
-#define wxStyledTextCtrl_SetUseTabs 3068
-#define wxStyledTextCtrl_GetUseTabs 3069
-#define wxStyledTextCtrl_SetLineIndentation 3070
-#define wxStyledTextCtrl_GetLineIndentation 3071
-#define wxStyledTextCtrl_GetLineIndentPosition 3072
-#define wxStyledTextCtrl_GetColumn 3073
-#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3074
-#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3075
-#define wxStyledTextCtrl_SetIndentationGuides 3076
-#define wxStyledTextCtrl_GetIndentationGuides 3077
-#define wxStyledTextCtrl_SetHighlightGuide 3078
-#define wxStyledTextCtrl_GetHighlightGuide 3079
-#define wxStyledTextCtrl_GetLineEndPosition 3080
-#define wxStyledTextCtrl_GetCodePage 3081
-#define wxStyledTextCtrl_GetCaretForeground 3082
-#define wxStyledTextCtrl_GetReadOnly 3083
-#define wxStyledTextCtrl_SetCurrentPos 3084
-#define wxStyledTextCtrl_SetSelectionStart 3085
-#define wxStyledTextCtrl_GetSelectionStart 3086
-#define wxStyledTextCtrl_SetSelectionEnd 3087
-#define wxStyledTextCtrl_GetSelectionEnd 3088
-#define wxStyledTextCtrl_SetPrintMagnification 3089
-#define wxStyledTextCtrl_GetPrintMagnification 3090
-#define wxStyledTextCtrl_SetPrintColourMode 3091
-#define wxStyledTextCtrl_GetPrintColourMode 3092
-#define wxStyledTextCtrl_FindText 3093
-#define wxStyledTextCtrl_FormatRange 3094
-#define wxStyledTextCtrl_GetFirstVisibleLine 3095
-#define wxStyledTextCtrl_GetLine 3096
-#define wxStyledTextCtrl_GetLineCount 3097
-#define wxStyledTextCtrl_SetMarginLeft 3098
-#define wxStyledTextCtrl_GetMarginLeft 3099
-#define wxStyledTextCtrl_SetMarginRight 3100
-#define wxStyledTextCtrl_GetMarginRight 3101
-#define wxStyledTextCtrl_GetModify 3102
-#define wxStyledTextCtrl_SetSelection 3103
-#define wxStyledTextCtrl_GetSelectedText 3104
-#define wxStyledTextCtrl_GetTextRange 3105
-#define wxStyledTextCtrl_HideSelection 3106
-#define wxStyledTextCtrl_LineFromPosition 3107
-#define wxStyledTextCtrl_PositionFromLine 3108
-#define wxStyledTextCtrl_LineScroll 3109
-#define wxStyledTextCtrl_EnsureCaretVisible 3110
-#define wxStyledTextCtrl_ReplaceSelection 3111
-#define wxStyledTextCtrl_SetReadOnly 3112
-#define wxStyledTextCtrl_CanPaste 3113
-#define wxStyledTextCtrl_CanUndo 3114
-#define wxStyledTextCtrl_EmptyUndoBuffer 3115
-#define wxStyledTextCtrl_Undo 3116
-#define wxStyledTextCtrl_Cut 3117
-#define wxStyledTextCtrl_Copy 3118
-#define wxStyledTextCtrl_Paste 3119
-#define wxStyledTextCtrl_Clear 3120
-#define wxStyledTextCtrl_SetText 3121
-#define wxStyledTextCtrl_GetText 3122
-#define wxStyledTextCtrl_GetTextLength 3123
-#define wxStyledTextCtrl_GetOvertype 3124
-#define wxStyledTextCtrl_SetCaretWidth 3125
-#define wxStyledTextCtrl_GetCaretWidth 3126
-#define wxStyledTextCtrl_SetTargetStart 3127
-#define wxStyledTextCtrl_GetTargetStart 3128
-#define wxStyledTextCtrl_SetTargetEnd 3129
-#define wxStyledTextCtrl_GetTargetEnd 3130
-#define wxStyledTextCtrl_ReplaceTarget 3131
-#define wxStyledTextCtrl_SearchInTarget 3132
-#define wxStyledTextCtrl_SetSearchFlags 3133
-#define wxStyledTextCtrl_GetSearchFlags 3134
-#define wxStyledTextCtrl_CallTipShow 3135
-#define wxStyledTextCtrl_CallTipCancel 3136
-#define wxStyledTextCtrl_CallTipActive 3137
-#define wxStyledTextCtrl_CallTipPosAtStart 3138
-#define wxStyledTextCtrl_CallTipSetHighlight 3139
-#define wxStyledTextCtrl_CallTipSetBackground 3140
-#define wxStyledTextCtrl_CallTipSetForeground 3141
-#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3142
-#define wxStyledTextCtrl_CallTipUseStyle 3143
-#define wxStyledTextCtrl_VisibleFromDocLine 3144
-#define wxStyledTextCtrl_DocLineFromVisible 3145
-#define wxStyledTextCtrl_WrapCount 3146
-#define wxStyledTextCtrl_SetFoldLevel 3147
-#define wxStyledTextCtrl_GetFoldLevel 3148
-#define wxStyledTextCtrl_GetLastChild 3149
-#define wxStyledTextCtrl_GetFoldParent 3150
-#define wxStyledTextCtrl_ShowLines 3151
-#define wxStyledTextCtrl_HideLines 3152
-#define wxStyledTextCtrl_GetLineVisible 3153
-#define wxStyledTextCtrl_SetFoldExpanded 3154
-#define wxStyledTextCtrl_GetFoldExpanded 3155
-#define wxStyledTextCtrl_ToggleFold 3156
-#define wxStyledTextCtrl_EnsureVisible 3157
-#define wxStyledTextCtrl_SetFoldFlags 3158
-#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3159
-#define wxStyledTextCtrl_SetTabIndents 3160
-#define wxStyledTextCtrl_GetTabIndents 3161
-#define wxStyledTextCtrl_SetBackSpaceUnIndents 3162
-#define wxStyledTextCtrl_GetBackSpaceUnIndents 3163
-#define wxStyledTextCtrl_SetMouseDwellTime 3164
-#define wxStyledTextCtrl_GetMouseDwellTime 3165
-#define wxStyledTextCtrl_WordStartPosition 3166
-#define wxStyledTextCtrl_WordEndPosition 3167
-#define wxStyledTextCtrl_SetWrapMode 3168
-#define wxStyledTextCtrl_GetWrapMode 3169
-#define wxStyledTextCtrl_SetWrapVisualFlags 3170
-#define wxStyledTextCtrl_GetWrapVisualFlags 3171
-#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3172
-#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3173
-#define wxStyledTextCtrl_SetWrapStartIndent 3174
-#define wxStyledTextCtrl_GetWrapStartIndent 3175
-#define wxStyledTextCtrl_SetLayoutCache 3176
-#define wxStyledTextCtrl_GetLayoutCache 3177
-#define wxStyledTextCtrl_SetScrollWidth 3178
-#define wxStyledTextCtrl_GetScrollWidth 3179
-#define wxStyledTextCtrl_TextWidth 3180
-#define wxStyledTextCtrl_GetEndAtLastLine 3181
-#define wxStyledTextCtrl_TextHeight 3182
-#define wxStyledTextCtrl_SetUseVerticalScrollBar 3183
-#define wxStyledTextCtrl_GetUseVerticalScrollBar 3184
-#define wxStyledTextCtrl_AppendText 3185
-#define wxStyledTextCtrl_GetTwoPhaseDraw 3186
-#define wxStyledTextCtrl_SetTwoPhaseDraw 3187
-#define wxStyledTextCtrl_TargetFromSelection 3188
-#define wxStyledTextCtrl_LinesJoin 3189
-#define wxStyledTextCtrl_LinesSplit 3190
-#define wxStyledTextCtrl_SetFoldMarginColour 3191
-#define wxStyledTextCtrl_SetFoldMarginHiColour 3192
-#define wxStyledTextCtrl_LineDown 3193
-#define wxStyledTextCtrl_LineDownExtend 3194
-#define wxStyledTextCtrl_LineUp 3195
-#define wxStyledTextCtrl_LineUpExtend 3196
-#define wxStyledTextCtrl_CharLeft 3197
-#define wxStyledTextCtrl_CharLeftExtend 3198
-#define wxStyledTextCtrl_CharRight 3199
-#define wxStyledTextCtrl_CharRightExtend 3200
-#define wxStyledTextCtrl_WordLeft 3201
-#define wxStyledTextCtrl_WordLeftExtend 3202
-#define wxStyledTextCtrl_WordRight 3203
-#define wxStyledTextCtrl_WordRightExtend 3204
-#define wxStyledTextCtrl_Home 3205
-#define wxStyledTextCtrl_HomeExtend 3206
-#define wxStyledTextCtrl_LineEnd 3207
-#define wxStyledTextCtrl_LineEndExtend 3208
-#define wxStyledTextCtrl_DocumentStart 3209
-#define wxStyledTextCtrl_DocumentStartExtend 3210
-#define wxStyledTextCtrl_DocumentEnd 3211
-#define wxStyledTextCtrl_DocumentEndExtend 3212
-#define wxStyledTextCtrl_PageUp 3213
-#define wxStyledTextCtrl_PageUpExtend 3214
-#define wxStyledTextCtrl_PageDown 3215
-#define wxStyledTextCtrl_PageDownExtend 3216
-#define wxStyledTextCtrl_EditToggleOvertype 3217
-#define wxStyledTextCtrl_Cancel 3218
-#define wxStyledTextCtrl_DeleteBack 3219
-#define wxStyledTextCtrl_Tab 3220
-#define wxStyledTextCtrl_BackTab 3221
-#define wxStyledTextCtrl_NewLine 3222
-#define wxStyledTextCtrl_FormFeed 3223
-#define wxStyledTextCtrl_VCHome 3224
-#define wxStyledTextCtrl_VCHomeExtend 3225
-#define wxStyledTextCtrl_ZoomIn 3226
-#define wxStyledTextCtrl_ZoomOut 3227
-#define wxStyledTextCtrl_DelWordLeft 3228
-#define wxStyledTextCtrl_DelWordRight 3229
-#define wxStyledTextCtrl_LineCut 3230
-#define wxStyledTextCtrl_LineDelete 3231
-#define wxStyledTextCtrl_LineTranspose 3232
-#define wxStyledTextCtrl_LineDuplicate 3233
-#define wxStyledTextCtrl_LowerCase 3234
-#define wxStyledTextCtrl_UpperCase 3235
-#define wxStyledTextCtrl_LineScrollDown 3236
-#define wxStyledTextCtrl_LineScrollUp 3237
-#define wxStyledTextCtrl_DeleteBackNotLine 3238
-#define wxStyledTextCtrl_HomeDisplay 3239
-#define wxStyledTextCtrl_HomeDisplayExtend 3240
-#define wxStyledTextCtrl_LineEndDisplay 3241
-#define wxStyledTextCtrl_LineEndDisplayExtend 3242
-#define wxStyledTextCtrl_HomeWrapExtend 3243
-#define wxStyledTextCtrl_LineEndWrap 3244
-#define wxStyledTextCtrl_LineEndWrapExtend 3245
-#define wxStyledTextCtrl_VCHomeWrap 3246
-#define wxStyledTextCtrl_VCHomeWrapExtend 3247
-#define wxStyledTextCtrl_LineCopy 3248
-#define wxStyledTextCtrl_MoveCaretInsideView 3249
-#define wxStyledTextCtrl_LineLength 3250
-#define wxStyledTextCtrl_BraceHighlight 3251
-#define wxStyledTextCtrl_BraceBadLight 3252
-#define wxStyledTextCtrl_BraceMatch 3253
-#define wxStyledTextCtrl_GetViewEOL 3254
-#define wxStyledTextCtrl_SetViewEOL 3255
-#define wxStyledTextCtrl_SetModEventMask 3256
-#define wxStyledTextCtrl_GetEdgeColumn 3257
-#define wxStyledTextCtrl_SetEdgeColumn 3258
-#define wxStyledTextCtrl_GetEdgeMode 3259
-#define wxStyledTextCtrl_GetEdgeColour 3260
-#define wxStyledTextCtrl_SetEdgeColour 3261
-#define wxStyledTextCtrl_SearchAnchor 3262
-#define wxStyledTextCtrl_SearchNext 3263
-#define wxStyledTextCtrl_SearchPrev 3264
-#define wxStyledTextCtrl_LinesOnScreen 3265
-#define wxStyledTextCtrl_UsePopUp 3266
-#define wxStyledTextCtrl_SelectionIsRectangle 3267
-#define wxStyledTextCtrl_SetZoom 3268
-#define wxStyledTextCtrl_GetZoom 3269
-#define wxStyledTextCtrl_GetModEventMask 3270
-#define wxStyledTextCtrl_SetSTCFocus 3271
-#define wxStyledTextCtrl_GetSTCFocus 3272
-#define wxStyledTextCtrl_SetStatus 3273
-#define wxStyledTextCtrl_GetStatus 3274
-#define wxStyledTextCtrl_SetMouseDownCaptures 3275
-#define wxStyledTextCtrl_GetMouseDownCaptures 3276
-#define wxStyledTextCtrl_SetSTCCursor 3277
-#define wxStyledTextCtrl_GetSTCCursor 3278
-#define wxStyledTextCtrl_SetControlCharSymbol 3279
-#define wxStyledTextCtrl_GetControlCharSymbol 3280
-#define wxStyledTextCtrl_WordPartLeft 3281
-#define wxStyledTextCtrl_WordPartLeftExtend 3282
-#define wxStyledTextCtrl_WordPartRight 3283
-#define wxStyledTextCtrl_WordPartRightExtend 3284
-#define wxStyledTextCtrl_SetVisiblePolicy 3285
-#define wxStyledTextCtrl_DelLineLeft 3286
-#define wxStyledTextCtrl_DelLineRight 3287
-#define wxStyledTextCtrl_GetXOffset 3288
-#define wxStyledTextCtrl_ChooseCaretX 3289
-#define wxStyledTextCtrl_SetXCaretPolicy 3290
-#define wxStyledTextCtrl_SetYCaretPolicy 3291
-#define wxStyledTextCtrl_GetPrintWrapMode 3292
-#define wxStyledTextCtrl_SetHotspotActiveForeground 3293
-#define wxStyledTextCtrl_SetHotspotActiveBackground 3294
-#define wxStyledTextCtrl_SetHotspotActiveUnderline 3295
-#define wxStyledTextCtrl_SetHotspotSingleLine 3296
-#define wxStyledTextCtrl_ParaDownExtend 3297
-#define wxStyledTextCtrl_ParaUp 3298
-#define wxStyledTextCtrl_ParaUpExtend 3299
-#define wxStyledTextCtrl_PositionBefore 3300
-#define wxStyledTextCtrl_PositionAfter 3301
-#define wxStyledTextCtrl_CopyRange 3302
-#define wxStyledTextCtrl_CopyText 3303
-#define wxStyledTextCtrl_SetSelectionMode 3304
-#define wxStyledTextCtrl_GetSelectionMode 3305
-#define wxStyledTextCtrl_LineDownRectExtend 3306
-#define wxStyledTextCtrl_LineUpRectExtend 3307
-#define wxStyledTextCtrl_CharLeftRectExtend 3308
-#define wxStyledTextCtrl_CharRightRectExtend 3309
-#define wxStyledTextCtrl_HomeRectExtend 3310
-#define wxStyledTextCtrl_VCHomeRectExtend 3311
-#define wxStyledTextCtrl_LineEndRectExtend 3312
-#define wxStyledTextCtrl_PageUpRectExtend 3313
-#define wxStyledTextCtrl_PageDownRectExtend 3314
-#define wxStyledTextCtrl_StutteredPageUp 3315
-#define wxStyledTextCtrl_StutteredPageUpExtend 3316
-#define wxStyledTextCtrl_StutteredPageDown 3317
-#define wxStyledTextCtrl_StutteredPageDownExtend 3318
-#define wxStyledTextCtrl_WordLeftEnd 3319
-#define wxStyledTextCtrl_WordLeftEndExtend 3320
-#define wxStyledTextCtrl_WordRightEnd 3321
-#define wxStyledTextCtrl_WordRightEndExtend 3322
-#define wxStyledTextCtrl_SetWhitespaceChars 3323
-#define wxStyledTextCtrl_SetCharsDefault 3324
-#define wxStyledTextCtrl_AutoCompGetCurrent 3325
-#define wxStyledTextCtrl_Allocate 3326
-#define wxStyledTextCtrl_FindColumn 3327
-#define wxStyledTextCtrl_GetCaretSticky 3328
-#define wxStyledTextCtrl_SetCaretSticky 3329
-#define wxStyledTextCtrl_ToggleCaretSticky 3330
-#define wxStyledTextCtrl_SetPasteConvertEndings 3331
-#define wxStyledTextCtrl_GetPasteConvertEndings 3332
-#define wxStyledTextCtrl_SelectionDuplicate 3333
-#define wxStyledTextCtrl_SetCaretLineBackAlpha 3334
-#define wxStyledTextCtrl_GetCaretLineBackAlpha 3335
-#define wxStyledTextCtrl_StartRecord 3336
-#define wxStyledTextCtrl_StopRecord 3337
-#define wxStyledTextCtrl_SetLexer 3338
-#define wxStyledTextCtrl_GetLexer 3339
-#define wxStyledTextCtrl_Colourise 3340
-#define wxStyledTextCtrl_SetProperty 3341
-#define wxStyledTextCtrl_SetKeyWords 3342
-#define wxStyledTextCtrl_SetLexerLanguage 3343
-#define wxStyledTextCtrl_GetProperty 3344
-#define wxStyledTextCtrl_GetStyleBitsNeeded 3345
-#define wxStyledTextCtrl_GetCurrentLine 3346
-#define wxStyledTextCtrl_StyleSetSpec 3347
-#define wxStyledTextCtrl_StyleSetFont 3348
-#define wxStyledTextCtrl_StyleSetFontAttr 3349
-#define wxStyledTextCtrl_StyleSetCharacterSet 3350
-#define wxStyledTextCtrl_StyleSetFontEncoding 3351
-#define wxStyledTextCtrl_CmdKeyExecute 3352
-#define wxStyledTextCtrl_SetMargins 3353
-#define wxStyledTextCtrl_GetSelection 3354
-#define wxStyledTextCtrl_PointFromPosition 3355
-#define wxStyledTextCtrl_ScrollToLine 3356
-#define wxStyledTextCtrl_ScrollToColumn 3357
-#define wxStyledTextCtrl_SendMsg 3358
-#define wxStyledTextCtrl_SetVScrollBar 3359
-#define wxStyledTextCtrl_SetHScrollBar 3360
-#define wxStyledTextCtrl_GetLastKeydownProcessed 3361
-#define wxStyledTextCtrl_SetLastKeydownProcessed 3362
-#define wxStyledTextCtrl_SaveFile 3363
-#define wxStyledTextCtrl_LoadFile 3364
-#define wxStyledTextCtrl_DoDragOver 3365
-#define wxStyledTextCtrl_DoDropText 3366
-#define wxStyledTextCtrl_GetUseAntiAliasing 3367
-#define wxStyledTextCtrl_AddTextRaw 3368
-#define wxStyledTextCtrl_InsertTextRaw 3369
-#define wxStyledTextCtrl_GetCurLineRaw 3370
-#define wxStyledTextCtrl_GetLineRaw 3371
-#define wxStyledTextCtrl_GetSelectedTextRaw 3372
-#define wxStyledTextCtrl_GetTextRangeRaw 3373
-#define wxStyledTextCtrl_SetTextRaw 3374
-#define wxStyledTextCtrl_GetTextRaw 3375
-#define wxStyledTextCtrl_AppendTextRaw 3376
-#define wxArtProvider_GetBitmap 3377
-#define wxArtProvider_GetIcon 3378
-#define wxTreeEvent_GetKeyCode 3379
-#define wxTreeEvent_GetItem 3380
-#define wxTreeEvent_GetKeyEvent 3381
-#define wxTreeEvent_GetLabel 3382
-#define wxTreeEvent_GetOldItem 3383
-#define wxTreeEvent_GetPoint 3384
-#define wxTreeEvent_IsEditCancelled 3385
-#define wxTreeEvent_SetToolTip 3386
-#define wxNotebookEvent_GetOldSelection 3387
-#define wxNotebookEvent_GetSelection 3388
-#define wxNotebookEvent_SetOldSelection 3389
-#define wxNotebookEvent_SetSelection 3390
-#define wxFileDataObject_new 3391
-#define wxFileDataObject_AddFile 3392
-#define wxFileDataObject_GetFilenames 3393
-#define wxFileDataObject_destroy 3394
-#define wxTextDataObject_new 3395
-#define wxTextDataObject_GetTextLength 3396
-#define wxTextDataObject_GetText 3397
-#define wxTextDataObject_SetText 3398
-#define wxTextDataObject_destroy 3399
-#define wxBitmapDataObject_new_1_1 3400
-#define wxBitmapDataObject_new_1_0 3401
-#define wxBitmapDataObject_GetBitmap 3402
-#define wxBitmapDataObject_SetBitmap 3403
-#define wxBitmapDataObject_destroy 3404
-#define wxClipboard_new 3406
-#define wxClipboard_destruct 3407
-#define wxClipboard_AddData 3408
-#define wxClipboard_Clear 3409
-#define wxClipboard_Close 3410
-#define wxClipboard_Flush 3411
-#define wxClipboard_GetData 3412
-#define wxClipboard_IsOpened 3413
-#define wxClipboard_Open 3414
-#define wxClipboard_SetData 3415
-#define wxClipboard_UsePrimarySelection 3417
-#define wxClipboard_IsSupported 3418
-#define wxClipboard_Get 3419
-#define wxSpinEvent_GetPosition 3420
-#define wxSpinEvent_SetPosition 3421
-#define wxSplitterWindow_new_0 3422
-#define wxSplitterWindow_new_2 3423
-#define wxSplitterWindow_destruct 3424
-#define wxSplitterWindow_Create 3425
-#define wxSplitterWindow_GetMinimumPaneSize 3426
-#define wxSplitterWindow_GetSashGravity 3427
-#define wxSplitterWindow_GetSashPosition 3428
-#define wxSplitterWindow_GetSplitMode 3429
-#define wxSplitterWindow_GetWindow1 3430
-#define wxSplitterWindow_GetWindow2 3431
-#define wxSplitterWindow_Initialize 3432
-#define wxSplitterWindow_IsSplit 3433
-#define wxSplitterWindow_ReplaceWindow 3434
-#define wxSplitterWindow_SetSashGravity 3435
-#define wxSplitterWindow_SetSashPosition 3436
-#define wxSplitterWindow_SetSashSize 3437
-#define wxSplitterWindow_SetMinimumPaneSize 3438
-#define wxSplitterWindow_SetSplitMode 3439
-#define wxSplitterWindow_SplitHorizontally 3440
-#define wxSplitterWindow_SplitVertically 3441
-#define wxSplitterWindow_Unsplit 3442
-#define wxSplitterWindow_UpdateSize 3443
-#define wxSplitterEvent_GetSashPosition 3444
-#define wxSplitterEvent_GetX 3445
-#define wxSplitterEvent_GetY 3446
-#define wxSplitterEvent_GetWindowBeingRemoved 3447
-#define wxSplitterEvent_SetSashPosition 3448
-#define wxHtmlWindow_new_0 3449
-#define wxHtmlWindow_new_2 3450
-#define wxHtmlWindow_AppendToPage 3451
-#define wxHtmlWindow_GetOpenedAnchor 3452
-#define wxHtmlWindow_GetOpenedPage 3453
-#define wxHtmlWindow_GetOpenedPageTitle 3454
-#define wxHtmlWindow_GetRelatedFrame 3455
-#define wxHtmlWindow_HistoryBack 3456
-#define wxHtmlWindow_HistoryCanBack 3457
-#define wxHtmlWindow_HistoryCanForward 3458
-#define wxHtmlWindow_HistoryClear 3459
-#define wxHtmlWindow_HistoryForward 3460
-#define wxHtmlWindow_LoadFile 3461
-#define wxHtmlWindow_LoadPage 3462
-#define wxHtmlWindow_SelectAll 3463
-#define wxHtmlWindow_SelectionToText 3464
-#define wxHtmlWindow_SelectLine 3465
-#define wxHtmlWindow_SelectWord 3466
-#define wxHtmlWindow_SetBorders 3467
-#define wxHtmlWindow_SetFonts 3468
-#define wxHtmlWindow_SetPage 3469
-#define wxHtmlWindow_SetRelatedFrame 3470
-#define wxHtmlWindow_SetRelatedStatusBar 3471
-#define wxHtmlWindow_ToText 3472
-#define wxHtmlWindow_destroy 3473
-#define wxHtmlLinkEvent_GetLinkInfo 3474
-#define wxAuiNotebookEvent_SetSelection 3475
-#define wxAuiNotebookEvent_GetSelection 3476
-#define wxAuiNotebookEvent_SetOldSelection 3477
-#define wxAuiNotebookEvent_GetOldSelection 3478
-#define wxAuiNotebookEvent_SetDragSource 3479
-#define wxAuiNotebookEvent_GetDragSource 3480
-#define wxAuiManagerEvent_SetManager 3481
-#define wxAuiManagerEvent_GetManager 3482
-#define wxAuiManagerEvent_SetPane 3483
-#define wxAuiManagerEvent_GetPane 3484
-#define wxAuiManagerEvent_SetButton 3485
-#define wxAuiManagerEvent_GetButton 3486
-#define wxAuiManagerEvent_SetDC 3487
-#define wxAuiManagerEvent_GetDC 3488
-#define wxAuiManagerEvent_Veto 3489
-#define wxAuiManagerEvent_GetVeto 3490
-#define wxAuiManagerEvent_SetCanVeto 3491
-#define wxAuiManagerEvent_CanVeto 3492
-#define wxLogNull_new 3493
-#define wxLogNull_destroy 3494
+#define wxTreeCtrl_EditLabel 2010
+#define wxTreeCtrl_EnsureVisible 2011
+#define wxTreeCtrl_Expand 2012
+#define wxTreeCtrl_GetBoundingRect 2013
+#define wxTreeCtrl_GetChildrenCount 2015
+#define wxTreeCtrl_GetCount 2016
+#define wxTreeCtrl_GetEditControl 2017
+#define wxTreeCtrl_GetFirstChild 2018
+#define wxTreeCtrl_GetNextChild 2019
+#define wxTreeCtrl_GetFirstVisibleItem 2020
+#define wxTreeCtrl_GetImageList 2021
+#define wxTreeCtrl_GetIndent 2022
+#define wxTreeCtrl_GetItemBackgroundColour 2023
+#define wxTreeCtrl_GetItemData 2024
+#define wxTreeCtrl_GetItemFont 2025
+#define wxTreeCtrl_GetItemImage_1 2026
+#define wxTreeCtrl_GetItemImage_2 2027
+#define wxTreeCtrl_GetItemText 2028
+#define wxTreeCtrl_GetItemTextColour 2029
+#define wxTreeCtrl_GetLastChild 2030
+#define wxTreeCtrl_GetNextSibling 2031
+#define wxTreeCtrl_GetNextVisible 2032
+#define wxTreeCtrl_GetItemParent 2033
+#define wxTreeCtrl_GetPrevSibling 2034
+#define wxTreeCtrl_GetPrevVisible 2035
+#define wxTreeCtrl_GetRootItem 2036
+#define wxTreeCtrl_GetSelection 2037
+#define wxTreeCtrl_GetSelections 2038
+#define wxTreeCtrl_GetStateImageList 2039
+#define wxTreeCtrl_HitTest 2040
+#define wxTreeCtrl_InsertItem 2042
+#define wxTreeCtrl_IsBold 2043
+#define wxTreeCtrl_IsExpanded 2044
+#define wxTreeCtrl_IsSelected 2045
+#define wxTreeCtrl_IsVisible 2046
+#define wxTreeCtrl_ItemHasChildren 2047
+#define wxTreeCtrl_PrependItem 2048
+#define wxTreeCtrl_ScrollTo 2049
+#define wxTreeCtrl_SelectItem_1 2050
+#define wxTreeCtrl_SelectItem_2 2051
+#define wxTreeCtrl_SetIndent 2052
+#define wxTreeCtrl_SetImageList 2053
+#define wxTreeCtrl_SetItemBackgroundColour 2054
+#define wxTreeCtrl_SetItemBold 2055
+#define wxTreeCtrl_SetItemData 2056
+#define wxTreeCtrl_SetItemDropHighlight 2057
+#define wxTreeCtrl_SetItemFont 2058
+#define wxTreeCtrl_SetItemHasChildren 2059
+#define wxTreeCtrl_SetItemImage_2 2060
+#define wxTreeCtrl_SetItemImage_3 2061
+#define wxTreeCtrl_SetItemText 2062
+#define wxTreeCtrl_SetItemTextColour 2063
+#define wxTreeCtrl_SetStateImageList 2064
+#define wxTreeCtrl_SetWindowStyle 2065
+#define wxTreeCtrl_SortChildren 2066
+#define wxTreeCtrl_Toggle 2067
+#define wxTreeCtrl_ToggleItemSelection 2068
+#define wxTreeCtrl_Unselect 2069
+#define wxTreeCtrl_UnselectAll 2070
+#define wxTreeCtrl_UnselectItem 2071
+#define wxScrollBar_new_0 2072
+#define wxScrollBar_new_3 2073
+#define wxScrollBar_destruct 2074
+#define wxScrollBar_Create 2075
+#define wxScrollBar_GetRange 2076
+#define wxScrollBar_GetPageSize 2077
+#define wxScrollBar_GetThumbPosition 2078
+#define wxScrollBar_GetThumbSize 2079
+#define wxScrollBar_SetThumbPosition 2080
+#define wxScrollBar_SetScrollbar 2081
+#define wxSpinButton_new_2 2083
+#define wxSpinButton_new_0 2084
+#define wxSpinButton_Create 2085
+#define wxSpinButton_GetMax 2086
+#define wxSpinButton_GetMin 2087
+#define wxSpinButton_GetValue 2088
+#define wxSpinButton_SetRange 2089
+#define wxSpinButton_SetValue 2090
+#define wxSpinButton_destroy 2091
+#define wxSpinCtrl_new_0 2092
+#define wxSpinCtrl_new_2 2093
+#define wxSpinCtrl_Create 2095
+#define wxSpinCtrl_SetValue_1_1 2098
+#define wxSpinCtrl_SetValue_1_0 2099
+#define wxSpinCtrl_GetValue 2101
+#define wxSpinCtrl_SetRange 2103
+#define wxSpinCtrl_SetSelection 2104
+#define wxSpinCtrl_GetMin 2106
+#define wxSpinCtrl_GetMax 2108
+#define wxSpinCtrl_destroy 2109
+#define wxStaticText_new_0 2110
+#define wxStaticText_new_4 2111
+#define wxStaticText_Create 2112
+#define wxStaticText_GetLabel 2113
+#define wxStaticText_SetLabel 2114
+#define wxStaticText_Wrap 2115
+#define wxStaticText_destroy 2116
+#define wxStaticBitmap_new_0 2117
+#define wxStaticBitmap_new_4 2118
+#define wxStaticBitmap_Create 2119
+#define wxStaticBitmap_GetBitmap 2120
+#define wxStaticBitmap_SetBitmap 2121
+#define wxStaticBitmap_destroy 2122
+#define wxRadioBox_new 2123
+#define wxRadioBox_destruct 2125
+#define wxRadioBox_Create 2126
+#define wxRadioBox_Enable_2 2127
+#define wxRadioBox_Enable_1 2128
+#define wxRadioBox_GetSelection 2129
+#define wxRadioBox_GetString 2130
+#define wxRadioBox_SetSelection 2131
+#define wxRadioBox_Show_2 2132
+#define wxRadioBox_Show_1 2133
+#define wxRadioBox_GetColumnCount 2134
+#define wxRadioBox_GetItemHelpText 2135
+#define wxRadioBox_GetItemToolTip 2136
+#define wxRadioBox_GetItemFromPoint 2138
+#define wxRadioBox_GetRowCount 2139
+#define wxRadioBox_IsItemEnabled 2140
+#define wxRadioBox_IsItemShown 2141
+#define wxRadioBox_SetItemHelpText 2142
+#define wxRadioBox_SetItemToolTip 2143
+#define wxRadioButton_new_0 2144
+#define wxRadioButton_new_4 2145
+#define wxRadioButton_Create 2146
+#define wxRadioButton_GetValue 2147
+#define wxRadioButton_SetValue 2148
+#define wxRadioButton_destroy 2149
+#define wxSlider_new_6 2151
+#define wxSlider_new_0 2152
+#define wxSlider_Create 2153
+#define wxSlider_GetLineSize 2154
+#define wxSlider_GetMax 2155
+#define wxSlider_GetMin 2156
+#define wxSlider_GetPageSize 2157
+#define wxSlider_GetThumbLength 2158
+#define wxSlider_GetValue 2159
+#define wxSlider_SetLineSize 2160
+#define wxSlider_SetPageSize 2161
+#define wxSlider_SetRange 2162
+#define wxSlider_SetThumbLength 2163
+#define wxSlider_SetValue 2164
+#define wxSlider_destroy 2165
+#define wxDialog_new_4 2167
+#define wxDialog_new_0 2168
+#define wxDialog_destruct 2170
+#define wxDialog_Create 2171
+#define wxDialog_CreateButtonSizer 2172
+#define wxDialog_CreateStdDialogButtonSizer 2173
+#define wxDialog_EndModal 2174
+#define wxDialog_GetAffirmativeId 2175
+#define wxDialog_GetReturnCode 2176
+#define wxDialog_IsModal 2177
+#define wxDialog_SetAffirmativeId 2178
+#define wxDialog_SetReturnCode 2179
+#define wxDialog_Show 2180
+#define wxDialog_ShowModal 2181
+#define wxColourDialog_new_0 2182
+#define wxColourDialog_new_2 2183
+#define wxColourDialog_destruct 2184
+#define wxColourDialog_Create 2185
+#define wxColourDialog_GetColourData 2186
+#define wxColourData_new_0 2187
+#define wxColourData_new_1 2188
+#define wxColourData_destruct 2189
+#define wxColourData_GetChooseFull 2190
+#define wxColourData_GetColour 2191
+#define wxColourData_GetCustomColour 2193
+#define wxColourData_SetChooseFull 2194
+#define wxColourData_SetColour 2195
+#define wxColourData_SetCustomColour 2196
+#define wxPalette_new_0 2197
+#define wxPalette_new_4 2198
+#define wxPalette_destruct 2200
+#define wxPalette_Create 2201
+#define wxPalette_GetColoursCount 2202
+#define wxPalette_GetPixel 2203
+#define wxPalette_GetRGB 2204
+#define wxPalette_IsOk 2205
+#define wxDirDialog_new 2209
+#define wxDirDialog_destruct 2210
+#define wxDirDialog_GetPath 2211
+#define wxDirDialog_GetMessage 2212
+#define wxDirDialog_SetMessage 2213
+#define wxDirDialog_SetPath 2214
+#define wxFileDialog_new 2218
+#define wxFileDialog_destruct 2219
+#define wxFileDialog_GetDirectory 2220
+#define wxFileDialog_GetFilename 2221
+#define wxFileDialog_GetFilenames 2222
+#define wxFileDialog_GetFilterIndex 2223
+#define wxFileDialog_GetMessage 2224
+#define wxFileDialog_GetPath 2225
+#define wxFileDialog_GetPaths 2226
+#define wxFileDialog_GetWildcard 2227
+#define wxFileDialog_SetDirectory 2228
+#define wxFileDialog_SetFilename 2229
+#define wxFileDialog_SetFilterIndex 2230
+#define wxFileDialog_SetMessage 2231
+#define wxFileDialog_SetPath 2232
+#define wxFileDialog_SetWildcard 2233
+#define wxPickerBase_SetInternalMargin 2234
+#define wxPickerBase_GetInternalMargin 2235
+#define wxPickerBase_SetTextCtrlProportion 2236
+#define wxPickerBase_SetPickerCtrlProportion 2237
+#define wxPickerBase_GetTextCtrlProportion 2238
+#define wxPickerBase_GetPickerCtrlProportion 2239
+#define wxPickerBase_HasTextCtrl 2240
+#define wxPickerBase_GetTextCtrl 2241
+#define wxPickerBase_IsTextCtrlGrowable 2242
+#define wxPickerBase_SetPickerCtrlGrowable 2243
+#define wxPickerBase_SetTextCtrlGrowable 2244
+#define wxPickerBase_IsPickerCtrlGrowable 2245
+#define wxFilePickerCtrl_new_0 2246
+#define wxFilePickerCtrl_new_3 2247
+#define wxFilePickerCtrl_Create 2248
+#define wxFilePickerCtrl_GetPath 2249
+#define wxFilePickerCtrl_SetPath 2250
+#define wxFilePickerCtrl_destroy 2251
+#define wxDirPickerCtrl_new_0 2252
+#define wxDirPickerCtrl_new_3 2253
+#define wxDirPickerCtrl_Create 2254
+#define wxDirPickerCtrl_GetPath 2255
+#define wxDirPickerCtrl_SetPath 2256
+#define wxDirPickerCtrl_destroy 2257
+#define wxColourPickerCtrl_new_0 2258
+#define wxColourPickerCtrl_new_3 2259
+#define wxColourPickerCtrl_Create 2260
+#define wxColourPickerCtrl_GetColour 2261
+#define wxColourPickerCtrl_SetColour_1_1 2262
+#define wxColourPickerCtrl_SetColour_1_0 2263
+#define wxColourPickerCtrl_destroy 2264
+#define wxDatePickerCtrl_new_0 2265
+#define wxDatePickerCtrl_new_3 2266
+#define wxDatePickerCtrl_GetRange 2267
+#define wxDatePickerCtrl_GetValue 2268
+#define wxDatePickerCtrl_SetRange 2269
+#define wxDatePickerCtrl_SetValue 2270
+#define wxDatePickerCtrl_destroy 2271
+#define wxFontPickerCtrl_new_0 2272
+#define wxFontPickerCtrl_new_3 2273
+#define wxFontPickerCtrl_Create 2274
+#define wxFontPickerCtrl_GetSelectedFont 2275
+#define wxFontPickerCtrl_SetSelectedFont 2276
+#define wxFontPickerCtrl_GetMaxPointSize 2277
+#define wxFontPickerCtrl_SetMaxPointSize 2278
+#define wxFontPickerCtrl_destroy 2279
+#define wxFindReplaceDialog_new_0 2282
+#define wxFindReplaceDialog_new_4 2283
+#define wxFindReplaceDialog_destruct 2284
+#define wxFindReplaceDialog_Create 2285
+#define wxFindReplaceDialog_GetData 2286
+#define wxFindReplaceData_new_0 2287
+#define wxFindReplaceData_new_1 2288
+#define wxFindReplaceData_GetFindString 2289
+#define wxFindReplaceData_GetReplaceString 2290
+#define wxFindReplaceData_GetFlags 2291
+#define wxFindReplaceData_SetFlags 2292
+#define wxFindReplaceData_SetFindString 2293
+#define wxFindReplaceData_SetReplaceString 2294
+#define wxFindReplaceData_destroy 2295
+#define wxMultiChoiceDialog_new_0 2296
+#define wxMultiChoiceDialog_new_5 2298
+#define wxMultiChoiceDialog_GetSelections 2299
+#define wxMultiChoiceDialog_SetSelections 2300
+#define wxMultiChoiceDialog_destroy 2301
+#define wxSingleChoiceDialog_new_0 2302
+#define wxSingleChoiceDialog_new_5 2304
+#define wxSingleChoiceDialog_GetSelection 2305
+#define wxSingleChoiceDialog_GetStringSelection 2306
+#define wxSingleChoiceDialog_SetSelection 2307
+#define wxSingleChoiceDialog_destroy 2308
+#define wxTextEntryDialog_new 2309
+#define wxTextEntryDialog_GetValue 2310
+#define wxTextEntryDialog_SetValue 2311
+#define wxTextEntryDialog_destroy 2312
+#define wxPasswordEntryDialog_new 2313
+#define wxPasswordEntryDialog_destroy 2314
+#define wxFontData_new_0 2315
+#define wxFontData_new_1 2316
+#define wxFontData_destruct 2317
+#define wxFontData_EnableEffects 2318
+#define wxFontData_GetAllowSymbols 2319
+#define wxFontData_GetColour 2320
+#define wxFontData_GetChosenFont 2321
+#define wxFontData_GetEnableEffects 2322
+#define wxFontData_GetInitialFont 2323
+#define wxFontData_GetShowHelp 2324
+#define wxFontData_SetAllowSymbols 2325
+#define wxFontData_SetChosenFont 2326
+#define wxFontData_SetColour 2327
+#define wxFontData_SetInitialFont 2328
+#define wxFontData_SetRange 2329
+#define wxFontData_SetShowHelp 2330
+#define wxFontDialog_new_0 2334
+#define wxFontDialog_new_2 2336
+#define wxFontDialog_Create 2338
+#define wxFontDialog_GetFontData 2339
+#define wxFontDialog_destroy 2341
+#define wxProgressDialog_new 2342
+#define wxProgressDialog_destruct 2343
+#define wxProgressDialog_Resume 2344
+#define wxProgressDialog_Update_2 2345
+#define wxProgressDialog_Update_0 2346
+#define wxMessageDialog_new 2347
+#define wxMessageDialog_destruct 2348
+#define wxPageSetupDialog_new 2349
+#define wxPageSetupDialog_destruct 2350
+#define wxPageSetupDialog_GetPageSetupData 2351
+#define wxPageSetupDialog_ShowModal 2352
+#define wxPageSetupDialogData_new_0 2353
+#define wxPageSetupDialogData_new_1_0 2354
+#define wxPageSetupDialogData_new_1_1 2355
+#define wxPageSetupDialogData_destruct 2356
+#define wxPageSetupDialogData_EnableHelp 2357
+#define wxPageSetupDialogData_EnableMargins 2358
+#define wxPageSetupDialogData_EnableOrientation 2359
+#define wxPageSetupDialogData_EnablePaper 2360
+#define wxPageSetupDialogData_EnablePrinter 2361
+#define wxPageSetupDialogData_GetDefaultMinMargins 2362
+#define wxPageSetupDialogData_GetEnableMargins 2363
+#define wxPageSetupDialogData_GetEnableOrientation 2364
+#define wxPageSetupDialogData_GetEnablePaper 2365
+#define wxPageSetupDialogData_GetEnablePrinter 2366
+#define wxPageSetupDialogData_GetEnableHelp 2367
+#define wxPageSetupDialogData_GetDefaultInfo 2368
+#define wxPageSetupDialogData_GetMarginTopLeft 2369
+#define wxPageSetupDialogData_GetMarginBottomRight 2370
+#define wxPageSetupDialogData_GetMinMarginTopLeft 2371
+#define wxPageSetupDialogData_GetMinMarginBottomRight 2372
+#define wxPageSetupDialogData_GetPaperId 2373
+#define wxPageSetupDialogData_GetPaperSize 2374
+#define wxPageSetupDialogData_GetPrintData 2376
+#define wxPageSetupDialogData_IsOk 2377
+#define wxPageSetupDialogData_SetDefaultInfo 2378
+#define wxPageSetupDialogData_SetDefaultMinMargins 2379
+#define wxPageSetupDialogData_SetMarginTopLeft 2380
+#define wxPageSetupDialogData_SetMarginBottomRight 2381
+#define wxPageSetupDialogData_SetMinMarginTopLeft 2382
+#define wxPageSetupDialogData_SetMinMarginBottomRight 2383
+#define wxPageSetupDialogData_SetPaperId 2384
+#define wxPageSetupDialogData_SetPaperSize_1_1 2385
+#define wxPageSetupDialogData_SetPaperSize_1_0 2386
+#define wxPageSetupDialogData_SetPrintData 2387
+#define wxPrintDialog_new_2_0 2388
+#define wxPrintDialog_new_2_1 2389
+#define wxPrintDialog_destruct 2390
+#define wxPrintDialog_GetPrintDialogData 2391
+#define wxPrintDialog_GetPrintDC 2392
+#define wxPrintDialogData_new_0 2393
+#define wxPrintDialogData_new_1_1 2394
+#define wxPrintDialogData_new_1_0 2395
+#define wxPrintDialogData_destruct 2396
+#define wxPrintDialogData_EnableHelp 2397
+#define wxPrintDialogData_EnablePageNumbers 2398
+#define wxPrintDialogData_EnablePrintToFile 2399
+#define wxPrintDialogData_EnableSelection 2400
+#define wxPrintDialogData_GetAllPages 2401
+#define wxPrintDialogData_GetCollate 2402
+#define wxPrintDialogData_GetFromPage 2403
+#define wxPrintDialogData_GetMaxPage 2404
+#define wxPrintDialogData_GetMinPage 2405
+#define wxPrintDialogData_GetNoCopies 2406
+#define wxPrintDialogData_GetPrintData 2407
+#define wxPrintDialogData_GetPrintToFile 2408
+#define wxPrintDialogData_GetSelection 2409
+#define wxPrintDialogData_GetToPage 2410
+#define wxPrintDialogData_IsOk 2411
+#define wxPrintDialogData_SetCollate 2412
+#define wxPrintDialogData_SetFromPage 2413
+#define wxPrintDialogData_SetMaxPage 2414
+#define wxPrintDialogData_SetMinPage 2415
+#define wxPrintDialogData_SetNoCopies 2416
+#define wxPrintDialogData_SetPrintData 2417
+#define wxPrintDialogData_SetPrintToFile 2418
+#define wxPrintDialogData_SetSelection 2419
+#define wxPrintDialogData_SetToPage 2420
+#define wxPrintData_new_0 2421
+#define wxPrintData_new_1 2422
+#define wxPrintData_destruct 2423
+#define wxPrintData_GetCollate 2424
+#define wxPrintData_GetBin 2425
+#define wxPrintData_GetColour 2426
+#define wxPrintData_GetDuplex 2427
+#define wxPrintData_GetNoCopies 2428
+#define wxPrintData_GetOrientation 2429
+#define wxPrintData_GetPaperId 2430
+#define wxPrintData_GetPrinterName 2431
+#define wxPrintData_GetQuality 2432
+#define wxPrintData_IsOk 2433
+#define wxPrintData_SetBin 2434
+#define wxPrintData_SetCollate 2435
+#define wxPrintData_SetColour 2436
+#define wxPrintData_SetDuplex 2437
+#define wxPrintData_SetNoCopies 2438
+#define wxPrintData_SetOrientation 2439
+#define wxPrintData_SetPaperId 2440
+#define wxPrintData_SetPrinterName 2441
+#define wxPrintData_SetQuality 2442
+#define wxPrintPreview_new_2 2445
+#define wxPrintPreview_new_3 2446
+#define wxPrintPreview_destruct 2448
+#define wxPrintPreview_GetCanvas 2449
+#define wxPrintPreview_GetCurrentPage 2450
+#define wxPrintPreview_GetFrame 2451
+#define wxPrintPreview_GetMaxPage 2452
+#define wxPrintPreview_GetMinPage 2453
+#define wxPrintPreview_GetPrintout 2454
+#define wxPrintPreview_GetPrintoutForPrinting 2455
+#define wxPrintPreview_IsOk 2456
+#define wxPrintPreview_PaintPage 2457
+#define wxPrintPreview_Print 2458
+#define wxPrintPreview_RenderPage 2459
+#define wxPrintPreview_SetCanvas 2460
+#define wxPrintPreview_SetCurrentPage 2461
+#define wxPrintPreview_SetFrame 2462
+#define wxPrintPreview_SetPrintout 2463
+#define wxPrintPreview_SetZoom 2464
+#define wxPreviewFrame_new 2465
+#define wxPreviewFrame_destruct 2466
+#define wxPreviewFrame_CreateControlBar 2467
+#define wxPreviewFrame_CreateCanvas 2468
+#define wxPreviewFrame_Initialize 2469
+#define wxPreviewFrame_OnCloseWindow 2470
+#define wxPreviewControlBar_new 2471
+#define wxPreviewControlBar_destruct 2472
+#define wxPreviewControlBar_CreateButtons 2473
+#define wxPreviewControlBar_GetPrintPreview 2474
+#define wxPreviewControlBar_GetZoomControl 2475
+#define wxPreviewControlBar_SetZoomControl 2476
+#define wxPrinter_new 2478
+#define wxPrinter_CreateAbortWindow 2479
+#define wxPrinter_GetAbort 2480
+#define wxPrinter_GetLastError 2481
+#define wxPrinter_GetPrintDialogData 2482
+#define wxPrinter_Print 2483
+#define wxPrinter_PrintDialog 2484
+#define wxPrinter_ReportError 2485
+#define wxPrinter_Setup 2486
+#define wxPrinter_destroy 2487
+#define wxXmlResource_new_1 2488
+#define wxXmlResource_new_2 2489
+#define wxXmlResource_destruct 2490
+#define wxXmlResource_AttachUnknownControl 2491
+#define wxXmlResource_ClearHandlers 2492
+#define wxXmlResource_CompareVersion 2493
+#define wxXmlResource_Get 2494
+#define wxXmlResource_GetFlags 2495
+#define wxXmlResource_GetVersion 2496
+#define wxXmlResource_GetXRCID 2497
+#define wxXmlResource_InitAllHandlers 2498
+#define wxXmlResource_Load 2499
+#define wxXmlResource_LoadBitmap 2500
+#define wxXmlResource_LoadDialog_2 2501
+#define wxXmlResource_LoadDialog_3 2502
+#define wxXmlResource_LoadFrame_2 2503
+#define wxXmlResource_LoadFrame_3 2504
+#define wxXmlResource_LoadIcon 2505
+#define wxXmlResource_LoadMenu 2506
+#define wxXmlResource_LoadMenuBar_2 2507
+#define wxXmlResource_LoadMenuBar_1 2508
+#define wxXmlResource_LoadPanel_2 2509
+#define wxXmlResource_LoadPanel_3 2510
+#define wxXmlResource_LoadToolBar 2511
+#define wxXmlResource_Set 2512
+#define wxXmlResource_SetFlags 2513
+#define wxXmlResource_Unload 2514
+#define wxXmlResource_xrcctrl 2515
+#define wxHtmlEasyPrinting_new 2516
+#define wxHtmlEasyPrinting_destruct 2517
+#define wxHtmlEasyPrinting_GetPrintData 2518
+#define wxHtmlEasyPrinting_GetPageSetupData 2519
+#define wxHtmlEasyPrinting_PreviewFile 2520
+#define wxHtmlEasyPrinting_PreviewText 2521
+#define wxHtmlEasyPrinting_PrintFile 2522
+#define wxHtmlEasyPrinting_PrintText 2523
+#define wxHtmlEasyPrinting_PageSetup 2524
+#define wxHtmlEasyPrinting_SetFonts 2525
+#define wxHtmlEasyPrinting_SetHeader 2526
+#define wxHtmlEasyPrinting_SetFooter 2527
+#define wxGLCanvas_new_2 2529
+#define wxGLCanvas_new_3_1 2530
+#define wxGLCanvas_new_3_0 2531
+#define wxGLCanvas_GetContext 2532
+#define wxGLCanvas_SetCurrent 2534
+#define wxGLCanvas_SwapBuffers 2535
+#define wxGLCanvas_destroy 2536
+#define wxAuiManager_new 2537
+#define wxAuiManager_destruct 2538
+#define wxAuiManager_AddPane_2_1 2539
+#define wxAuiManager_AddPane_3 2540
+#define wxAuiManager_AddPane_2_0 2541
+#define wxAuiManager_DetachPane 2542
+#define wxAuiManager_GetAllPanes 2543
+#define wxAuiManager_GetArtProvider 2544
+#define wxAuiManager_GetDockSizeConstraint 2545
+#define wxAuiManager_GetFlags 2546
+#define wxAuiManager_GetManagedWindow 2547
+#define wxAuiManager_GetManager 2548
+#define wxAuiManager_GetPane_1_1 2549
+#define wxAuiManager_GetPane_1_0 2550
+#define wxAuiManager_HideHint 2551
+#define wxAuiManager_InsertPane 2552
+#define wxAuiManager_LoadPaneInfo 2553
+#define wxAuiManager_LoadPerspective 2554
+#define wxAuiManager_SavePaneInfo 2555
+#define wxAuiManager_SavePerspective 2556
+#define wxAuiManager_SetArtProvider 2557
+#define wxAuiManager_SetDockSizeConstraint 2558
+#define wxAuiManager_SetFlags 2559
+#define wxAuiManager_SetManagedWindow 2560
+#define wxAuiManager_ShowHint 2561
+#define wxAuiManager_UnInit 2562
+#define wxAuiManager_Update 2563
+#define wxAuiPaneInfo_new_0 2564
+#define wxAuiPaneInfo_new_1 2565
+#define wxAuiPaneInfo_destruct 2566
+#define wxAuiPaneInfo_BestSize_1 2567
+#define wxAuiPaneInfo_BestSize_2 2568
+#define wxAuiPaneInfo_Bottom 2569
+#define wxAuiPaneInfo_BottomDockable 2570
+#define wxAuiPaneInfo_Caption 2571
+#define wxAuiPaneInfo_CaptionVisible 2572
+#define wxAuiPaneInfo_Centre 2573
+#define wxAuiPaneInfo_CentrePane 2574
+#define wxAuiPaneInfo_CloseButton 2575
+#define wxAuiPaneInfo_DefaultPane 2576
+#define wxAuiPaneInfo_DestroyOnClose 2577
+#define wxAuiPaneInfo_Direction 2578
+#define wxAuiPaneInfo_Dock 2579
+#define wxAuiPaneInfo_Dockable 2580
+#define wxAuiPaneInfo_Fixed 2581
+#define wxAuiPaneInfo_Float 2582
+#define wxAuiPaneInfo_Floatable 2583
+#define wxAuiPaneInfo_FloatingPosition_1 2584
+#define wxAuiPaneInfo_FloatingPosition_2 2585
+#define wxAuiPaneInfo_FloatingSize_1 2586
+#define wxAuiPaneInfo_FloatingSize_2 2587
+#define wxAuiPaneInfo_Gripper 2588
+#define wxAuiPaneInfo_GripperTop 2589
+#define wxAuiPaneInfo_HasBorder 2590
+#define wxAuiPaneInfo_HasCaption 2591
+#define wxAuiPaneInfo_HasCloseButton 2592
+#define wxAuiPaneInfo_HasFlag 2593
+#define wxAuiPaneInfo_HasGripper 2594
+#define wxAuiPaneInfo_HasGripperTop 2595
+#define wxAuiPaneInfo_HasMaximizeButton 2596
+#define wxAuiPaneInfo_HasMinimizeButton 2597
+#define wxAuiPaneInfo_HasPinButton 2598
+#define wxAuiPaneInfo_Hide 2599
+#define wxAuiPaneInfo_IsBottomDockable 2600
+#define wxAuiPaneInfo_IsDocked 2601
+#define wxAuiPaneInfo_IsFixed 2602
+#define wxAuiPaneInfo_IsFloatable 2603
+#define wxAuiPaneInfo_IsFloating 2604
+#define wxAuiPaneInfo_IsLeftDockable 2605
+#define wxAuiPaneInfo_IsMovable 2606
+#define wxAuiPaneInfo_IsOk 2607
+#define wxAuiPaneInfo_IsResizable 2608
+#define wxAuiPaneInfo_IsRightDockable 2609
+#define wxAuiPaneInfo_IsShown 2610
+#define wxAuiPaneInfo_IsToolbar 2611
+#define wxAuiPaneInfo_IsTopDockable 2612
+#define wxAuiPaneInfo_Layer 2613
+#define wxAuiPaneInfo_Left 2614
+#define wxAuiPaneInfo_LeftDockable 2615
+#define wxAuiPaneInfo_MaxSize_1 2616
+#define wxAuiPaneInfo_MaxSize_2 2617
+#define wxAuiPaneInfo_MaximizeButton 2618
+#define wxAuiPaneInfo_MinSize_1 2619
+#define wxAuiPaneInfo_MinSize_2 2620
+#define wxAuiPaneInfo_MinimizeButton 2621
+#define wxAuiPaneInfo_Movable 2622
+#define wxAuiPaneInfo_Name 2623
+#define wxAuiPaneInfo_PaneBorder 2624
+#define wxAuiPaneInfo_PinButton 2625
+#define wxAuiPaneInfo_Position 2626
+#define wxAuiPaneInfo_Resizable 2627
+#define wxAuiPaneInfo_Right 2628
+#define wxAuiPaneInfo_RightDockable 2629
+#define wxAuiPaneInfo_Row 2630
+#define wxAuiPaneInfo_SafeSet 2631
+#define wxAuiPaneInfo_SetFlag 2632
+#define wxAuiPaneInfo_Show 2633
+#define wxAuiPaneInfo_ToolbarPane 2634
+#define wxAuiPaneInfo_Top 2635
+#define wxAuiPaneInfo_TopDockable 2636
+#define wxAuiPaneInfo_Window 2637
+#define wxAuiNotebook_new_0 2638
+#define wxAuiNotebook_new_2 2639
+#define wxAuiNotebook_AddPage 2640
+#define wxAuiNotebook_Create 2641
+#define wxAuiNotebook_DeletePage 2642
+#define wxAuiNotebook_GetArtProvider 2643
+#define wxAuiNotebook_GetPage 2644
+#define wxAuiNotebook_GetPageBitmap 2645
+#define wxAuiNotebook_GetPageCount 2646
+#define wxAuiNotebook_GetPageIndex 2647
+#define wxAuiNotebook_GetPageText 2648
+#define wxAuiNotebook_GetSelection 2649
+#define wxAuiNotebook_InsertPage 2650
+#define wxAuiNotebook_RemovePage 2651
+#define wxAuiNotebook_SetArtProvider 2652
+#define wxAuiNotebook_SetFont 2653
+#define wxAuiNotebook_SetPageBitmap 2654
+#define wxAuiNotebook_SetPageText 2655
+#define wxAuiNotebook_SetSelection 2656
+#define wxAuiNotebook_SetTabCtrlHeight 2657
+#define wxAuiNotebook_SetUniformBitmapSize 2658
+#define wxAuiNotebook_destroy 2659
+#define wxMDIParentFrame_new_0 2660
+#define wxMDIParentFrame_new_4 2661
+#define wxMDIParentFrame_destruct 2662
+#define wxMDIParentFrame_ActivateNext 2663
+#define wxMDIParentFrame_ActivatePrevious 2664
+#define wxMDIParentFrame_ArrangeIcons 2665
+#define wxMDIParentFrame_Cascade 2666
+#define wxMDIParentFrame_Create 2667
+#define wxMDIParentFrame_GetActiveChild 2668
+#define wxMDIParentFrame_GetClientWindow 2669
+#define wxMDIParentFrame_Tile 2670
+#define wxMDIChildFrame_new_0 2671
+#define wxMDIChildFrame_new_4 2672
+#define wxMDIChildFrame_destruct 2673
+#define wxMDIChildFrame_Activate 2674
+#define wxMDIChildFrame_Create 2675
+#define wxMDIChildFrame_Maximize 2676
+#define wxMDIChildFrame_Restore 2677
+#define wxMDIClientWindow_new_0 2678
+#define wxMDIClientWindow_new_2 2679
+#define wxMDIClientWindow_destruct 2680
+#define wxMDIClientWindow_CreateClient 2681
+#define wxLayoutAlgorithm_new 2682
+#define wxLayoutAlgorithm_LayoutFrame 2683
+#define wxLayoutAlgorithm_LayoutMDIFrame 2684
+#define wxLayoutAlgorithm_LayoutWindow 2685
+#define wxLayoutAlgorithm_destroy 2686
+#define wxEvent_GetId 2687
+#define wxEvent_GetSkipped 2688
+#define wxEvent_GetTimestamp 2689
+#define wxEvent_IsCommandEvent 2690
+#define wxEvent_ResumePropagation 2691
+#define wxEvent_ShouldPropagate 2692
+#define wxEvent_Skip 2693
+#define wxEvent_StopPropagation 2694
+#define wxCommandEvent_getClientData 2695
+#define wxCommandEvent_GetExtraLong 2696
+#define wxCommandEvent_GetInt 2697
+#define wxCommandEvent_GetSelection 2698
+#define wxCommandEvent_GetString 2699
+#define wxCommandEvent_IsChecked 2700
+#define wxCommandEvent_IsSelection 2701
+#define wxCommandEvent_SetInt 2702
+#define wxCommandEvent_SetString 2703
+#define wxScrollEvent_GetOrientation 2704
+#define wxScrollEvent_GetPosition 2705
+#define wxScrollWinEvent_GetOrientation 2706
+#define wxScrollWinEvent_GetPosition 2707
+#define wxMouseEvent_AltDown 2708
+#define wxMouseEvent_Button 2709
+#define wxMouseEvent_ButtonDClick 2710
+#define wxMouseEvent_ButtonDown 2711
+#define wxMouseEvent_ButtonUp 2712
+#define wxMouseEvent_CmdDown 2713
+#define wxMouseEvent_ControlDown 2714
+#define wxMouseEvent_Dragging 2715
+#define wxMouseEvent_Entering 2716
+#define wxMouseEvent_GetButton 2717
+#define wxMouseEvent_GetPosition 2720
+#define wxMouseEvent_GetLogicalPosition 2721
+#define wxMouseEvent_GetLinesPerAction 2722
+#define wxMouseEvent_GetWheelRotation 2723
+#define wxMouseEvent_GetWheelDelta 2724
+#define wxMouseEvent_GetX 2725
+#define wxMouseEvent_GetY 2726
+#define wxMouseEvent_IsButton 2727
+#define wxMouseEvent_IsPageScroll 2728
+#define wxMouseEvent_Leaving 2729
+#define wxMouseEvent_LeftDClick 2730
+#define wxMouseEvent_LeftDown 2731
+#define wxMouseEvent_LeftIsDown 2732
+#define wxMouseEvent_LeftUp 2733
+#define wxMouseEvent_MetaDown 2734
+#define wxMouseEvent_MiddleDClick 2735
+#define wxMouseEvent_MiddleDown 2736
+#define wxMouseEvent_MiddleIsDown 2737
+#define wxMouseEvent_MiddleUp 2738
+#define wxMouseEvent_Moving 2739
+#define wxMouseEvent_RightDClick 2740
+#define wxMouseEvent_RightDown 2741
+#define wxMouseEvent_RightIsDown 2742
+#define wxMouseEvent_RightUp 2743
+#define wxMouseEvent_ShiftDown 2744
+#define wxSetCursorEvent_GetCursor 2745
+#define wxSetCursorEvent_GetX 2746
+#define wxSetCursorEvent_GetY 2747
+#define wxSetCursorEvent_HasCursor 2748
+#define wxSetCursorEvent_SetCursor 2749
+#define wxKeyEvent_AltDown 2750
+#define wxKeyEvent_CmdDown 2751
+#define wxKeyEvent_ControlDown 2752
+#define wxKeyEvent_GetKeyCode 2753
+#define wxKeyEvent_GetModifiers 2754
+#define wxKeyEvent_GetPosition 2757
+#define wxKeyEvent_GetRawKeyCode 2758
+#define wxKeyEvent_GetRawKeyFlags 2759
+#define wxKeyEvent_GetUnicodeKey 2760
+#define wxKeyEvent_GetX 2761
+#define wxKeyEvent_GetY 2762
+#define wxKeyEvent_HasModifiers 2763
+#define wxKeyEvent_MetaDown 2764
+#define wxKeyEvent_ShiftDown 2765
+#define wxSizeEvent_GetSize 2766
+#define wxMoveEvent_GetPosition 2767
+#define wxEraseEvent_GetDC 2768
+#define wxFocusEvent_GetWindow 2769
+#define wxChildFocusEvent_GetWindow 2770
+#define wxMenuEvent_GetMenu 2771
+#define wxMenuEvent_GetMenuId 2772
+#define wxMenuEvent_IsPopup 2773
+#define wxCloseEvent_CanVeto 2774
+#define wxCloseEvent_GetLoggingOff 2775
+#define wxCloseEvent_SetCanVeto 2776
+#define wxCloseEvent_SetLoggingOff 2777
+#define wxCloseEvent_Veto 2778
+#define wxShowEvent_SetShow 2779
+#define wxShowEvent_GetShow 2780
+#define wxIconizeEvent_Iconized 2781
+#define wxJoystickEvent_ButtonDown 2782
+#define wxJoystickEvent_ButtonIsDown 2783
+#define wxJoystickEvent_ButtonUp 2784
+#define wxJoystickEvent_GetButtonChange 2785
+#define wxJoystickEvent_GetButtonState 2786
+#define wxJoystickEvent_GetJoystick 2787
+#define wxJoystickEvent_GetPosition 2788
+#define wxJoystickEvent_GetZPosition 2789
+#define wxJoystickEvent_IsButton 2790
+#define wxJoystickEvent_IsMove 2791
+#define wxJoystickEvent_IsZMove 2792
+#define wxUpdateUIEvent_CanUpdate 2793
+#define wxUpdateUIEvent_Check 2794
+#define wxUpdateUIEvent_Enable 2795
+#define wxUpdateUIEvent_Show 2796
+#define wxUpdateUIEvent_GetChecked 2797
+#define wxUpdateUIEvent_GetEnabled 2798
+#define wxUpdateUIEvent_GetShown 2799
+#define wxUpdateUIEvent_GetSetChecked 2800
+#define wxUpdateUIEvent_GetSetEnabled 2801
+#define wxUpdateUIEvent_GetSetShown 2802
+#define wxUpdateUIEvent_GetSetText 2803
+#define wxUpdateUIEvent_GetText 2804
+#define wxUpdateUIEvent_GetMode 2805
+#define wxUpdateUIEvent_GetUpdateInterval 2806
+#define wxUpdateUIEvent_ResetUpdateTime 2807
+#define wxUpdateUIEvent_SetMode 2808
+#define wxUpdateUIEvent_SetText 2809
+#define wxUpdateUIEvent_SetUpdateInterval 2810
+#define wxMouseCaptureChangedEvent_GetCapturedWindow 2811
+#define wxPaletteChangedEvent_SetChangedWindow 2812
+#define wxPaletteChangedEvent_GetChangedWindow 2813
+#define wxQueryNewPaletteEvent_SetPaletteRealized 2814
+#define wxQueryNewPaletteEvent_GetPaletteRealized 2815
+#define wxNavigationKeyEvent_GetDirection 2816
+#define wxNavigationKeyEvent_SetDirection 2817
+#define wxNavigationKeyEvent_IsWindowChange 2818
+#define wxNavigationKeyEvent_SetWindowChange 2819
+#define wxNavigationKeyEvent_IsFromTab 2820
+#define wxNavigationKeyEvent_SetFromTab 2821
+#define wxNavigationKeyEvent_GetCurrentFocus 2822
+#define wxNavigationKeyEvent_SetCurrentFocus 2823
+#define wxHelpEvent_GetOrigin 2824
+#define wxHelpEvent_GetPosition 2825
+#define wxHelpEvent_SetOrigin 2826
+#define wxHelpEvent_SetPosition 2827
+#define wxContextMenuEvent_GetPosition 2828
+#define wxContextMenuEvent_SetPosition 2829
+#define wxIdleEvent_CanSend 2830
+#define wxIdleEvent_GetMode 2831
+#define wxIdleEvent_RequestMore 2832
+#define wxIdleEvent_MoreRequested 2833
+#define wxIdleEvent_SetMode 2834
+#define wxGridEvent_AltDown 2835
+#define wxGridEvent_ControlDown 2836
+#define wxGridEvent_GetCol 2837
+#define wxGridEvent_GetPosition 2838
+#define wxGridEvent_GetRow 2839
+#define wxGridEvent_MetaDown 2840
+#define wxGridEvent_Selecting 2841
+#define wxGridEvent_ShiftDown 2842
+#define wxNotifyEvent_Allow 2843
+#define wxNotifyEvent_IsAllowed 2844
+#define wxNotifyEvent_Veto 2845
+#define wxSashEvent_GetEdge 2846
+#define wxSashEvent_GetDragRect 2847
+#define wxSashEvent_GetDragStatus 2848
+#define wxListEvent_GetCacheFrom 2849
+#define wxListEvent_GetCacheTo 2850
+#define wxListEvent_GetKeyCode 2851
+#define wxListEvent_GetIndex 2852
+#define wxListEvent_GetColumn 2853
+#define wxListEvent_GetPoint 2854
+#define wxListEvent_GetLabel 2855
+#define wxListEvent_GetText 2856
+#define wxListEvent_GetImage 2857
+#define wxListEvent_GetData 2858
+#define wxListEvent_GetMask 2859
+#define wxListEvent_GetItem 2860
+#define wxListEvent_IsEditCancelled 2861
+#define wxDateEvent_GetDate 2862
+#define wxCalendarEvent_GetWeekDay 2863
+#define wxFileDirPickerEvent_GetPath 2864
+#define wxColourPickerEvent_GetColour 2865
+#define wxFontPickerEvent_GetFont 2866
+#define wxStyledTextEvent_GetPosition 2867
+#define wxStyledTextEvent_GetKey 2868
+#define wxStyledTextEvent_GetModifiers 2869
+#define wxStyledTextEvent_GetModificationType 2870
+#define wxStyledTextEvent_GetText 2871
+#define wxStyledTextEvent_GetLength 2872
+#define wxStyledTextEvent_GetLinesAdded 2873
+#define wxStyledTextEvent_GetLine 2874
+#define wxStyledTextEvent_GetFoldLevelNow 2875
+#define wxStyledTextEvent_GetFoldLevelPrev 2876
+#define wxStyledTextEvent_GetMargin 2877
+#define wxStyledTextEvent_GetMessage 2878
+#define wxStyledTextEvent_GetWParam 2879
+#define wxStyledTextEvent_GetLParam 2880
+#define wxStyledTextEvent_GetListType 2881
+#define wxStyledTextEvent_GetX 2882
+#define wxStyledTextEvent_GetY 2883
+#define wxStyledTextEvent_GetDragText 2884
+#define wxStyledTextEvent_GetDragAllowMove 2885
+#define wxStyledTextEvent_GetDragResult 2886
+#define wxStyledTextEvent_GetShift 2887
+#define wxStyledTextEvent_GetControl 2888
+#define wxStyledTextEvent_GetAlt 2889
+#define utils_wxGetKeyState 2890
+#define utils_wxGetMousePosition 2891
+#define utils_wxGetMouseState 2892
+#define utils_wxSetDetectableAutoRepeat 2893
+#define utils_wxBell 2894
+#define utils_wxFindMenuItemId 2895
+#define utils_wxGenericFindWindowAtPoint 2896
+#define utils_wxFindWindowAtPoint 2897
+#define utils_wxBeginBusyCursor 2898
+#define utils_wxEndBusyCursor 2899
+#define utils_wxIsBusy 2900
+#define utils_wxShutdown 2901
+#define utils_wxShell 2902
+#define utils_wxLaunchDefaultBrowser 2903
+#define utils_wxGetEmailAddress 2904
+#define utils_wxGetUserId 2905
+#define utils_wxGetHomeDir 2906
+#define utils_wxNewId 2907
+#define utils_wxRegisterId 2908
+#define utils_wxGetCurrentId 2909
+#define utils_wxGetOsDescription 2910
+#define utils_wxIsPlatformLittleEndian 2911
+#define utils_wxIsPlatform64Bit 2912
+#define wxPrintout_new 2913
+#define wxPrintout_destruct 2914
+#define wxPrintout_GetDC 2915
+#define wxPrintout_GetPageSizeMM 2916
+#define wxPrintout_GetPageSizePixels 2917
+#define wxPrintout_GetPaperRectPixels 2918
+#define wxPrintout_GetPPIPrinter 2919
+#define wxPrintout_GetPPIScreen 2920
+#define wxPrintout_GetTitle 2921
+#define wxPrintout_IsPreview 2922
+#define wxPrintout_FitThisSizeToPaper 2923
+#define wxPrintout_FitThisSizeToPage 2924
+#define wxPrintout_FitThisSizeToPageMargins 2925
+#define wxPrintout_MapScreenSizeToPaper 2926
+#define wxPrintout_MapScreenSizeToPage 2927
+#define wxPrintout_MapScreenSizeToPageMargins 2928
+#define wxPrintout_MapScreenSizeToDevice 2929
+#define wxPrintout_GetLogicalPaperRect 2930
+#define wxPrintout_GetLogicalPageRect 2931
+#define wxPrintout_GetLogicalPageMarginsRect 2932
+#define wxPrintout_SetLogicalOrigin 2933
+#define wxPrintout_OffsetLogicalOrigin 2934
+#define wxStyledTextCtrl_new_2 2935
+#define wxStyledTextCtrl_new_0 2936
+#define wxStyledTextCtrl_destruct 2937
+#define wxStyledTextCtrl_Create 2938
+#define wxStyledTextCtrl_AddText 2939
+#define wxStyledTextCtrl_AddStyledText 2940
+#define wxStyledTextCtrl_InsertText 2941
+#define wxStyledTextCtrl_ClearAll 2942
+#define wxStyledTextCtrl_ClearDocumentStyle 2943
+#define wxStyledTextCtrl_GetLength 2944
+#define wxStyledTextCtrl_GetCharAt 2945
+#define wxStyledTextCtrl_GetCurrentPos 2946
+#define wxStyledTextCtrl_GetAnchor 2947
+#define wxStyledTextCtrl_GetStyleAt 2948
+#define wxStyledTextCtrl_Redo 2949
+#define wxStyledTextCtrl_SetUndoCollection 2950
+#define wxStyledTextCtrl_SelectAll 2951
+#define wxStyledTextCtrl_SetSavePoint 2952
+#define wxStyledTextCtrl_GetStyledText 2953
+#define wxStyledTextCtrl_CanRedo 2954
+#define wxStyledTextCtrl_MarkerLineFromHandle 2955
+#define wxStyledTextCtrl_MarkerDeleteHandle 2956
+#define wxStyledTextCtrl_GetUndoCollection 2957
+#define wxStyledTextCtrl_GetViewWhiteSpace 2958
+#define wxStyledTextCtrl_SetViewWhiteSpace 2959
+#define wxStyledTextCtrl_PositionFromPoint 2960
+#define wxStyledTextCtrl_PositionFromPointClose 2961
+#define wxStyledTextCtrl_GotoLine 2962
+#define wxStyledTextCtrl_GotoPos 2963
+#define wxStyledTextCtrl_SetAnchor 2964
+#define wxStyledTextCtrl_GetCurLine 2965
+#define wxStyledTextCtrl_GetEndStyled 2966
+#define wxStyledTextCtrl_ConvertEOLs 2967
+#define wxStyledTextCtrl_GetEOLMode 2968
+#define wxStyledTextCtrl_SetEOLMode 2969
+#define wxStyledTextCtrl_StartStyling 2970
+#define wxStyledTextCtrl_SetStyling 2971
+#define wxStyledTextCtrl_GetBufferedDraw 2972
+#define wxStyledTextCtrl_SetBufferedDraw 2973
+#define wxStyledTextCtrl_SetTabWidth 2974
+#define wxStyledTextCtrl_GetTabWidth 2975
+#define wxStyledTextCtrl_SetCodePage 2976
+#define wxStyledTextCtrl_MarkerDefine 2977
+#define wxStyledTextCtrl_MarkerSetForeground 2978
+#define wxStyledTextCtrl_MarkerSetBackground 2979
+#define wxStyledTextCtrl_MarkerAdd 2980
+#define wxStyledTextCtrl_MarkerDelete 2981
+#define wxStyledTextCtrl_MarkerDeleteAll 2982
+#define wxStyledTextCtrl_MarkerGet 2983
+#define wxStyledTextCtrl_MarkerNext 2984
+#define wxStyledTextCtrl_MarkerPrevious 2985
+#define wxStyledTextCtrl_MarkerDefineBitmap 2986
+#define wxStyledTextCtrl_MarkerAddSet 2987
+#define wxStyledTextCtrl_MarkerSetAlpha 2988
+#define wxStyledTextCtrl_SetMarginType 2989
+#define wxStyledTextCtrl_GetMarginType 2990
+#define wxStyledTextCtrl_SetMarginWidth 2991
+#define wxStyledTextCtrl_GetMarginWidth 2992
+#define wxStyledTextCtrl_SetMarginMask 2993
+#define wxStyledTextCtrl_GetMarginMask 2994
+#define wxStyledTextCtrl_SetMarginSensitive 2995
+#define wxStyledTextCtrl_GetMarginSensitive 2996
+#define wxStyledTextCtrl_StyleClearAll 2997
+#define wxStyledTextCtrl_StyleSetForeground 2998
+#define wxStyledTextCtrl_StyleSetBackground 2999
+#define wxStyledTextCtrl_StyleSetBold 3000
+#define wxStyledTextCtrl_StyleSetItalic 3001
+#define wxStyledTextCtrl_StyleSetSize 3002
+#define wxStyledTextCtrl_StyleSetFaceName 3003
+#define wxStyledTextCtrl_StyleSetEOLFilled 3004
+#define wxStyledTextCtrl_StyleResetDefault 3005
+#define wxStyledTextCtrl_StyleSetUnderline 3006
+#define wxStyledTextCtrl_StyleSetCase 3007
+#define wxStyledTextCtrl_StyleSetHotSpot 3008
+#define wxStyledTextCtrl_SetSelForeground 3009
+#define wxStyledTextCtrl_SetSelBackground 3010
+#define wxStyledTextCtrl_GetSelAlpha 3011
+#define wxStyledTextCtrl_SetSelAlpha 3012
+#define wxStyledTextCtrl_SetCaretForeground 3013
+#define wxStyledTextCtrl_CmdKeyAssign 3014
+#define wxStyledTextCtrl_CmdKeyClear 3015
+#define wxStyledTextCtrl_CmdKeyClearAll 3016
+#define wxStyledTextCtrl_SetStyleBytes 3017
+#define wxStyledTextCtrl_StyleSetVisible 3018
+#define wxStyledTextCtrl_GetCaretPeriod 3019
+#define wxStyledTextCtrl_SetCaretPeriod 3020
+#define wxStyledTextCtrl_SetWordChars 3021
+#define wxStyledTextCtrl_BeginUndoAction 3022
+#define wxStyledTextCtrl_EndUndoAction 3023
+#define wxStyledTextCtrl_IndicatorSetStyle 3024
+#define wxStyledTextCtrl_IndicatorGetStyle 3025
+#define wxStyledTextCtrl_IndicatorSetForeground 3026
+#define wxStyledTextCtrl_IndicatorGetForeground 3027
+#define wxStyledTextCtrl_SetWhitespaceForeground 3028
+#define wxStyledTextCtrl_SetWhitespaceBackground 3029
+#define wxStyledTextCtrl_GetStyleBits 3030
+#define wxStyledTextCtrl_SetLineState 3031
+#define wxStyledTextCtrl_GetLineState 3032
+#define wxStyledTextCtrl_GetMaxLineState 3033
+#define wxStyledTextCtrl_GetCaretLineVisible 3034
+#define wxStyledTextCtrl_SetCaretLineVisible 3035
+#define wxStyledTextCtrl_GetCaretLineBackground 3036
+#define wxStyledTextCtrl_SetCaretLineBackground 3037
+#define wxStyledTextCtrl_AutoCompShow 3038
+#define wxStyledTextCtrl_AutoCompCancel 3039
+#define wxStyledTextCtrl_AutoCompActive 3040
+#define wxStyledTextCtrl_AutoCompPosStart 3041
+#define wxStyledTextCtrl_AutoCompComplete 3042
+#define wxStyledTextCtrl_AutoCompStops 3043
+#define wxStyledTextCtrl_AutoCompSetSeparator 3044
+#define wxStyledTextCtrl_AutoCompGetSeparator 3045
+#define wxStyledTextCtrl_AutoCompSelect 3046
+#define wxStyledTextCtrl_AutoCompSetCancelAtStart 3047
+#define wxStyledTextCtrl_AutoCompGetCancelAtStart 3048
+#define wxStyledTextCtrl_AutoCompSetFillUps 3049
+#define wxStyledTextCtrl_AutoCompSetChooseSingle 3050
+#define wxStyledTextCtrl_AutoCompGetChooseSingle 3051
+#define wxStyledTextCtrl_AutoCompSetIgnoreCase 3052
+#define wxStyledTextCtrl_AutoCompGetIgnoreCase 3053
+#define wxStyledTextCtrl_UserListShow 3054
+#define wxStyledTextCtrl_AutoCompSetAutoHide 3055
+#define wxStyledTextCtrl_AutoCompGetAutoHide 3056
+#define wxStyledTextCtrl_AutoCompSetDropRestOfWord 3057
+#define wxStyledTextCtrl_AutoCompGetDropRestOfWord 3058
+#define wxStyledTextCtrl_RegisterImage 3059
+#define wxStyledTextCtrl_ClearRegisteredImages 3060
+#define wxStyledTextCtrl_AutoCompGetTypeSeparator 3061
+#define wxStyledTextCtrl_AutoCompSetTypeSeparator 3062
+#define wxStyledTextCtrl_AutoCompSetMaxWidth 3063
+#define wxStyledTextCtrl_AutoCompGetMaxWidth 3064
+#define wxStyledTextCtrl_AutoCompSetMaxHeight 3065
+#define wxStyledTextCtrl_AutoCompGetMaxHeight 3066
+#define wxStyledTextCtrl_SetIndent 3067
+#define wxStyledTextCtrl_GetIndent 3068
+#define wxStyledTextCtrl_SetUseTabs 3069
+#define wxStyledTextCtrl_GetUseTabs 3070
+#define wxStyledTextCtrl_SetLineIndentation 3071
+#define wxStyledTextCtrl_GetLineIndentation 3072
+#define wxStyledTextCtrl_GetLineIndentPosition 3073
+#define wxStyledTextCtrl_GetColumn 3074
+#define wxStyledTextCtrl_SetUseHorizontalScrollBar 3075
+#define wxStyledTextCtrl_GetUseHorizontalScrollBar 3076
+#define wxStyledTextCtrl_SetIndentationGuides 3077
+#define wxStyledTextCtrl_GetIndentationGuides 3078
+#define wxStyledTextCtrl_SetHighlightGuide 3079
+#define wxStyledTextCtrl_GetHighlightGuide 3080
+#define wxStyledTextCtrl_GetLineEndPosition 3081
+#define wxStyledTextCtrl_GetCodePage 3082
+#define wxStyledTextCtrl_GetCaretForeground 3083
+#define wxStyledTextCtrl_GetReadOnly 3084
+#define wxStyledTextCtrl_SetCurrentPos 3085
+#define wxStyledTextCtrl_SetSelectionStart 3086
+#define wxStyledTextCtrl_GetSelectionStart 3087
+#define wxStyledTextCtrl_SetSelectionEnd 3088
+#define wxStyledTextCtrl_GetSelectionEnd 3089
+#define wxStyledTextCtrl_SetPrintMagnification 3090
+#define wxStyledTextCtrl_GetPrintMagnification 3091
+#define wxStyledTextCtrl_SetPrintColourMode 3092
+#define wxStyledTextCtrl_GetPrintColourMode 3093
+#define wxStyledTextCtrl_FindText 3094
+#define wxStyledTextCtrl_FormatRange 3095
+#define wxStyledTextCtrl_GetFirstVisibleLine 3096
+#define wxStyledTextCtrl_GetLine 3097
+#define wxStyledTextCtrl_GetLineCount 3098
+#define wxStyledTextCtrl_SetMarginLeft 3099
+#define wxStyledTextCtrl_GetMarginLeft 3100
+#define wxStyledTextCtrl_SetMarginRight 3101
+#define wxStyledTextCtrl_GetMarginRight 3102
+#define wxStyledTextCtrl_GetModify 3103
+#define wxStyledTextCtrl_SetSelection 3104
+#define wxStyledTextCtrl_GetSelectedText 3105
+#define wxStyledTextCtrl_GetTextRange 3106
+#define wxStyledTextCtrl_HideSelection 3107
+#define wxStyledTextCtrl_LineFromPosition 3108
+#define wxStyledTextCtrl_PositionFromLine 3109
+#define wxStyledTextCtrl_LineScroll 3110
+#define wxStyledTextCtrl_EnsureCaretVisible 3111
+#define wxStyledTextCtrl_ReplaceSelection 3112
+#define wxStyledTextCtrl_SetReadOnly 3113
+#define wxStyledTextCtrl_CanPaste 3114
+#define wxStyledTextCtrl_CanUndo 3115
+#define wxStyledTextCtrl_EmptyUndoBuffer 3116
+#define wxStyledTextCtrl_Undo 3117
+#define wxStyledTextCtrl_Cut 3118
+#define wxStyledTextCtrl_Copy 3119
+#define wxStyledTextCtrl_Paste 3120
+#define wxStyledTextCtrl_Clear 3121
+#define wxStyledTextCtrl_SetText 3122
+#define wxStyledTextCtrl_GetText 3123
+#define wxStyledTextCtrl_GetTextLength 3124
+#define wxStyledTextCtrl_GetOvertype 3125
+#define wxStyledTextCtrl_SetCaretWidth 3126
+#define wxStyledTextCtrl_GetCaretWidth 3127
+#define wxStyledTextCtrl_SetTargetStart 3128
+#define wxStyledTextCtrl_GetTargetStart 3129
+#define wxStyledTextCtrl_SetTargetEnd 3130
+#define wxStyledTextCtrl_GetTargetEnd 3131
+#define wxStyledTextCtrl_ReplaceTarget 3132
+#define wxStyledTextCtrl_SearchInTarget 3133
+#define wxStyledTextCtrl_SetSearchFlags 3134
+#define wxStyledTextCtrl_GetSearchFlags 3135
+#define wxStyledTextCtrl_CallTipShow 3136
+#define wxStyledTextCtrl_CallTipCancel 3137
+#define wxStyledTextCtrl_CallTipActive 3138
+#define wxStyledTextCtrl_CallTipPosAtStart 3139
+#define wxStyledTextCtrl_CallTipSetHighlight 3140
+#define wxStyledTextCtrl_CallTipSetBackground 3141
+#define wxStyledTextCtrl_CallTipSetForeground 3142
+#define wxStyledTextCtrl_CallTipSetForegroundHighlight 3143
+#define wxStyledTextCtrl_CallTipUseStyle 3144
+#define wxStyledTextCtrl_VisibleFromDocLine 3145
+#define wxStyledTextCtrl_DocLineFromVisible 3146
+#define wxStyledTextCtrl_WrapCount 3147
+#define wxStyledTextCtrl_SetFoldLevel 3148
+#define wxStyledTextCtrl_GetFoldLevel 3149
+#define wxStyledTextCtrl_GetLastChild 3150
+#define wxStyledTextCtrl_GetFoldParent 3151
+#define wxStyledTextCtrl_ShowLines 3152
+#define wxStyledTextCtrl_HideLines 3153
+#define wxStyledTextCtrl_GetLineVisible 3154
+#define wxStyledTextCtrl_SetFoldExpanded 3155
+#define wxStyledTextCtrl_GetFoldExpanded 3156
+#define wxStyledTextCtrl_ToggleFold 3157
+#define wxStyledTextCtrl_EnsureVisible 3158
+#define wxStyledTextCtrl_SetFoldFlags 3159
+#define wxStyledTextCtrl_EnsureVisibleEnforcePolicy 3160
+#define wxStyledTextCtrl_SetTabIndents 3161
+#define wxStyledTextCtrl_GetTabIndents 3162
+#define wxStyledTextCtrl_SetBackSpaceUnIndents 3163
+#define wxStyledTextCtrl_GetBackSpaceUnIndents 3164
+#define wxStyledTextCtrl_SetMouseDwellTime 3165
+#define wxStyledTextCtrl_GetMouseDwellTime 3166
+#define wxStyledTextCtrl_WordStartPosition 3167
+#define wxStyledTextCtrl_WordEndPosition 3168
+#define wxStyledTextCtrl_SetWrapMode 3169
+#define wxStyledTextCtrl_GetWrapMode 3170
+#define wxStyledTextCtrl_SetWrapVisualFlags 3171
+#define wxStyledTextCtrl_GetWrapVisualFlags 3172
+#define wxStyledTextCtrl_SetWrapVisualFlagsLocation 3173
+#define wxStyledTextCtrl_GetWrapVisualFlagsLocation 3174
+#define wxStyledTextCtrl_SetWrapStartIndent 3175
+#define wxStyledTextCtrl_GetWrapStartIndent 3176
+#define wxStyledTextCtrl_SetLayoutCache 3177
+#define wxStyledTextCtrl_GetLayoutCache 3178
+#define wxStyledTextCtrl_SetScrollWidth 3179
+#define wxStyledTextCtrl_GetScrollWidth 3180
+#define wxStyledTextCtrl_TextWidth 3181
+#define wxStyledTextCtrl_GetEndAtLastLine 3182
+#define wxStyledTextCtrl_TextHeight 3183
+#define wxStyledTextCtrl_SetUseVerticalScrollBar 3184
+#define wxStyledTextCtrl_GetUseVerticalScrollBar 3185
+#define wxStyledTextCtrl_AppendText 3186
+#define wxStyledTextCtrl_GetTwoPhaseDraw 3187
+#define wxStyledTextCtrl_SetTwoPhaseDraw 3188
+#define wxStyledTextCtrl_TargetFromSelection 3189
+#define wxStyledTextCtrl_LinesJoin 3190
+#define wxStyledTextCtrl_LinesSplit 3191
+#define wxStyledTextCtrl_SetFoldMarginColour 3192
+#define wxStyledTextCtrl_SetFoldMarginHiColour 3193
+#define wxStyledTextCtrl_LineDown 3194
+#define wxStyledTextCtrl_LineDownExtend 3195
+#define wxStyledTextCtrl_LineUp 3196
+#define wxStyledTextCtrl_LineUpExtend 3197
+#define wxStyledTextCtrl_CharLeft 3198
+#define wxStyledTextCtrl_CharLeftExtend 3199
+#define wxStyledTextCtrl_CharRight 3200
+#define wxStyledTextCtrl_CharRightExtend 3201
+#define wxStyledTextCtrl_WordLeft 3202
+#define wxStyledTextCtrl_WordLeftExtend 3203
+#define wxStyledTextCtrl_WordRight 3204
+#define wxStyledTextCtrl_WordRightExtend 3205
+#define wxStyledTextCtrl_Home 3206
+#define wxStyledTextCtrl_HomeExtend 3207
+#define wxStyledTextCtrl_LineEnd 3208
+#define wxStyledTextCtrl_LineEndExtend 3209
+#define wxStyledTextCtrl_DocumentStart 3210
+#define wxStyledTextCtrl_DocumentStartExtend 3211
+#define wxStyledTextCtrl_DocumentEnd 3212
+#define wxStyledTextCtrl_DocumentEndExtend 3213
+#define wxStyledTextCtrl_PageUp 3214
+#define wxStyledTextCtrl_PageUpExtend 3215
+#define wxStyledTextCtrl_PageDown 3216
+#define wxStyledTextCtrl_PageDownExtend 3217
+#define wxStyledTextCtrl_EditToggleOvertype 3218
+#define wxStyledTextCtrl_Cancel 3219
+#define wxStyledTextCtrl_DeleteBack 3220
+#define wxStyledTextCtrl_Tab 3221
+#define wxStyledTextCtrl_BackTab 3222
+#define wxStyledTextCtrl_NewLine 3223
+#define wxStyledTextCtrl_FormFeed 3224
+#define wxStyledTextCtrl_VCHome 3225
+#define wxStyledTextCtrl_VCHomeExtend 3226
+#define wxStyledTextCtrl_ZoomIn 3227
+#define wxStyledTextCtrl_ZoomOut 3228
+#define wxStyledTextCtrl_DelWordLeft 3229
+#define wxStyledTextCtrl_DelWordRight 3230
+#define wxStyledTextCtrl_LineCut 3231
+#define wxStyledTextCtrl_LineDelete 3232
+#define wxStyledTextCtrl_LineTranspose 3233
+#define wxStyledTextCtrl_LineDuplicate 3234
+#define wxStyledTextCtrl_LowerCase 3235
+#define wxStyledTextCtrl_UpperCase 3236
+#define wxStyledTextCtrl_LineScrollDown 3237
+#define wxStyledTextCtrl_LineScrollUp 3238
+#define wxStyledTextCtrl_DeleteBackNotLine 3239
+#define wxStyledTextCtrl_HomeDisplay 3240
+#define wxStyledTextCtrl_HomeDisplayExtend 3241
+#define wxStyledTextCtrl_LineEndDisplay 3242
+#define wxStyledTextCtrl_LineEndDisplayExtend 3243
+#define wxStyledTextCtrl_HomeWrapExtend 3244
+#define wxStyledTextCtrl_LineEndWrap 3245
+#define wxStyledTextCtrl_LineEndWrapExtend 3246
+#define wxStyledTextCtrl_VCHomeWrap 3247
+#define wxStyledTextCtrl_VCHomeWrapExtend 3248
+#define wxStyledTextCtrl_LineCopy 3249
+#define wxStyledTextCtrl_MoveCaretInsideView 3250
+#define wxStyledTextCtrl_LineLength 3251
+#define wxStyledTextCtrl_BraceHighlight 3252
+#define wxStyledTextCtrl_BraceBadLight 3253
+#define wxStyledTextCtrl_BraceMatch 3254
+#define wxStyledTextCtrl_GetViewEOL 3255
+#define wxStyledTextCtrl_SetViewEOL 3256
+#define wxStyledTextCtrl_SetModEventMask 3257
+#define wxStyledTextCtrl_GetEdgeColumn 3258
+#define wxStyledTextCtrl_SetEdgeColumn 3259
+#define wxStyledTextCtrl_GetEdgeMode 3260
+#define wxStyledTextCtrl_GetEdgeColour 3261
+#define wxStyledTextCtrl_SetEdgeColour 3262
+#define wxStyledTextCtrl_SearchAnchor 3263
+#define wxStyledTextCtrl_SearchNext 3264
+#define wxStyledTextCtrl_SearchPrev 3265
+#define wxStyledTextCtrl_LinesOnScreen 3266
+#define wxStyledTextCtrl_UsePopUp 3267
+#define wxStyledTextCtrl_SelectionIsRectangle 3268
+#define wxStyledTextCtrl_SetZoom 3269
+#define wxStyledTextCtrl_GetZoom 3270
+#define wxStyledTextCtrl_GetModEventMask 3271
+#define wxStyledTextCtrl_SetSTCFocus 3272
+#define wxStyledTextCtrl_GetSTCFocus 3273
+#define wxStyledTextCtrl_SetStatus 3274
+#define wxStyledTextCtrl_GetStatus 3275
+#define wxStyledTextCtrl_SetMouseDownCaptures 3276
+#define wxStyledTextCtrl_GetMouseDownCaptures 3277
+#define wxStyledTextCtrl_SetSTCCursor 3278
+#define wxStyledTextCtrl_GetSTCCursor 3279
+#define wxStyledTextCtrl_SetControlCharSymbol 3280
+#define wxStyledTextCtrl_GetControlCharSymbol 3281
+#define wxStyledTextCtrl_WordPartLeft 3282
+#define wxStyledTextCtrl_WordPartLeftExtend 3283
+#define wxStyledTextCtrl_WordPartRight 3284
+#define wxStyledTextCtrl_WordPartRightExtend 3285
+#define wxStyledTextCtrl_SetVisiblePolicy 3286
+#define wxStyledTextCtrl_DelLineLeft 3287
+#define wxStyledTextCtrl_DelLineRight 3288
+#define wxStyledTextCtrl_GetXOffset 3289
+#define wxStyledTextCtrl_ChooseCaretX 3290
+#define wxStyledTextCtrl_SetXCaretPolicy 3291
+#define wxStyledTextCtrl_SetYCaretPolicy 3292
+#define wxStyledTextCtrl_GetPrintWrapMode 3293
+#define wxStyledTextCtrl_SetHotspotActiveForeground 3294
+#define wxStyledTextCtrl_SetHotspotActiveBackground 3295
+#define wxStyledTextCtrl_SetHotspotActiveUnderline 3296
+#define wxStyledTextCtrl_SetHotspotSingleLine 3297
+#define wxStyledTextCtrl_ParaDownExtend 3298
+#define wxStyledTextCtrl_ParaUp 3299
+#define wxStyledTextCtrl_ParaUpExtend 3300
+#define wxStyledTextCtrl_PositionBefore 3301
+#define wxStyledTextCtrl_PositionAfter 3302
+#define wxStyledTextCtrl_CopyRange 3303
+#define wxStyledTextCtrl_CopyText 3304
+#define wxStyledTextCtrl_SetSelectionMode 3305
+#define wxStyledTextCtrl_GetSelectionMode 3306
+#define wxStyledTextCtrl_LineDownRectExtend 3307
+#define wxStyledTextCtrl_LineUpRectExtend 3308
+#define wxStyledTextCtrl_CharLeftRectExtend 3309
+#define wxStyledTextCtrl_CharRightRectExtend 3310
+#define wxStyledTextCtrl_HomeRectExtend 3311
+#define wxStyledTextCtrl_VCHomeRectExtend 3312
+#define wxStyledTextCtrl_LineEndRectExtend 3313
+#define wxStyledTextCtrl_PageUpRectExtend 3314
+#define wxStyledTextCtrl_PageDownRectExtend 3315
+#define wxStyledTextCtrl_StutteredPageUp 3316
+#define wxStyledTextCtrl_StutteredPageUpExtend 3317
+#define wxStyledTextCtrl_StutteredPageDown 3318
+#define wxStyledTextCtrl_StutteredPageDownExtend 3319
+#define wxStyledTextCtrl_WordLeftEnd 3320
+#define wxStyledTextCtrl_WordLeftEndExtend 3321
+#define wxStyledTextCtrl_WordRightEnd 3322
+#define wxStyledTextCtrl_WordRightEndExtend 3323
+#define wxStyledTextCtrl_SetWhitespaceChars 3324
+#define wxStyledTextCtrl_SetCharsDefault 3325
+#define wxStyledTextCtrl_AutoCompGetCurrent 3326
+#define wxStyledTextCtrl_Allocate 3327
+#define wxStyledTextCtrl_FindColumn 3328
+#define wxStyledTextCtrl_GetCaretSticky 3329
+#define wxStyledTextCtrl_SetCaretSticky 3330
+#define wxStyledTextCtrl_ToggleCaretSticky 3331
+#define wxStyledTextCtrl_SetPasteConvertEndings 3332
+#define wxStyledTextCtrl_GetPasteConvertEndings 3333
+#define wxStyledTextCtrl_SelectionDuplicate 3334
+#define wxStyledTextCtrl_SetCaretLineBackAlpha 3335
+#define wxStyledTextCtrl_GetCaretLineBackAlpha 3336
+#define wxStyledTextCtrl_StartRecord 3337
+#define wxStyledTextCtrl_StopRecord 3338
+#define wxStyledTextCtrl_SetLexer 3339
+#define wxStyledTextCtrl_GetLexer 3340
+#define wxStyledTextCtrl_Colourise 3341
+#define wxStyledTextCtrl_SetProperty 3342
+#define wxStyledTextCtrl_SetKeyWords 3343
+#define wxStyledTextCtrl_SetLexerLanguage 3344
+#define wxStyledTextCtrl_GetProperty 3345
+#define wxStyledTextCtrl_GetStyleBitsNeeded 3346
+#define wxStyledTextCtrl_GetCurrentLine 3347
+#define wxStyledTextCtrl_StyleSetSpec 3348
+#define wxStyledTextCtrl_StyleSetFont 3349
+#define wxStyledTextCtrl_StyleSetFontAttr 3350
+#define wxStyledTextCtrl_StyleSetCharacterSet 3351
+#define wxStyledTextCtrl_StyleSetFontEncoding 3352
+#define wxStyledTextCtrl_CmdKeyExecute 3353
+#define wxStyledTextCtrl_SetMargins 3354
+#define wxStyledTextCtrl_GetSelection 3355
+#define wxStyledTextCtrl_PointFromPosition 3356
+#define wxStyledTextCtrl_ScrollToLine 3357
+#define wxStyledTextCtrl_ScrollToColumn 3358
+#define wxStyledTextCtrl_SendMsg 3359
+#define wxStyledTextCtrl_SetVScrollBar 3360
+#define wxStyledTextCtrl_SetHScrollBar 3361
+#define wxStyledTextCtrl_GetLastKeydownProcessed 3362
+#define wxStyledTextCtrl_SetLastKeydownProcessed 3363
+#define wxStyledTextCtrl_SaveFile 3364
+#define wxStyledTextCtrl_LoadFile 3365
+#define wxStyledTextCtrl_DoDragOver 3366
+#define wxStyledTextCtrl_DoDropText 3367
+#define wxStyledTextCtrl_GetUseAntiAliasing 3368
+#define wxStyledTextCtrl_AddTextRaw 3369
+#define wxStyledTextCtrl_InsertTextRaw 3370
+#define wxStyledTextCtrl_GetCurLineRaw 3371
+#define wxStyledTextCtrl_GetLineRaw 3372
+#define wxStyledTextCtrl_GetSelectedTextRaw 3373
+#define wxStyledTextCtrl_GetTextRangeRaw 3374
+#define wxStyledTextCtrl_SetTextRaw 3375
+#define wxStyledTextCtrl_GetTextRaw 3376
+#define wxStyledTextCtrl_AppendTextRaw 3377
+#define wxArtProvider_GetBitmap 3378
+#define wxArtProvider_GetIcon 3379
+#define wxTreeEvent_GetKeyCode 3380
+#define wxTreeEvent_GetItem 3381
+#define wxTreeEvent_GetKeyEvent 3382
+#define wxTreeEvent_GetLabel 3383
+#define wxTreeEvent_GetOldItem 3384
+#define wxTreeEvent_GetPoint 3385
+#define wxTreeEvent_IsEditCancelled 3386
+#define wxTreeEvent_SetToolTip 3387
+#define wxNotebookEvent_GetOldSelection 3388
+#define wxNotebookEvent_GetSelection 3389
+#define wxNotebookEvent_SetOldSelection 3390
+#define wxNotebookEvent_SetSelection 3391
+#define wxFileDataObject_new 3392
+#define wxFileDataObject_AddFile 3393
+#define wxFileDataObject_GetFilenames 3394
+#define wxFileDataObject_destroy 3395
+#define wxTextDataObject_new 3396
+#define wxTextDataObject_GetTextLength 3397
+#define wxTextDataObject_GetText 3398
+#define wxTextDataObject_SetText 3399
+#define wxTextDataObject_destroy 3400
+#define wxBitmapDataObject_new_1_1 3401
+#define wxBitmapDataObject_new_1_0 3402
+#define wxBitmapDataObject_GetBitmap 3403
+#define wxBitmapDataObject_SetBitmap 3404
+#define wxBitmapDataObject_destroy 3405
+#define wxClipboard_new 3407
+#define wxClipboard_destruct 3408
+#define wxClipboard_AddData 3409
+#define wxClipboard_Clear 3410
+#define wxClipboard_Close 3411
+#define wxClipboard_Flush 3412
+#define wxClipboard_GetData 3413
+#define wxClipboard_IsOpened 3414
+#define wxClipboard_Open 3415
+#define wxClipboard_SetData 3416
+#define wxClipboard_UsePrimarySelection 3418
+#define wxClipboard_IsSupported 3419
+#define wxClipboard_Get 3420
+#define wxSpinEvent_GetPosition 3421
+#define wxSpinEvent_SetPosition 3422
+#define wxSplitterWindow_new_0 3423
+#define wxSplitterWindow_new_2 3424
+#define wxSplitterWindow_destruct 3425
+#define wxSplitterWindow_Create 3426
+#define wxSplitterWindow_GetMinimumPaneSize 3427
+#define wxSplitterWindow_GetSashGravity 3428
+#define wxSplitterWindow_GetSashPosition 3429
+#define wxSplitterWindow_GetSplitMode 3430
+#define wxSplitterWindow_GetWindow1 3431
+#define wxSplitterWindow_GetWindow2 3432
+#define wxSplitterWindow_Initialize 3433
+#define wxSplitterWindow_IsSplit 3434
+#define wxSplitterWindow_ReplaceWindow 3435
+#define wxSplitterWindow_SetSashGravity 3436
+#define wxSplitterWindow_SetSashPosition 3437
+#define wxSplitterWindow_SetSashSize 3438
+#define wxSplitterWindow_SetMinimumPaneSize 3439
+#define wxSplitterWindow_SetSplitMode 3440
+#define wxSplitterWindow_SplitHorizontally 3441
+#define wxSplitterWindow_SplitVertically 3442
+#define wxSplitterWindow_Unsplit 3443
+#define wxSplitterWindow_UpdateSize 3444
+#define wxSplitterEvent_GetSashPosition 3445
+#define wxSplitterEvent_GetX 3446
+#define wxSplitterEvent_GetY 3447
+#define wxSplitterEvent_GetWindowBeingRemoved 3448
+#define wxSplitterEvent_SetSashPosition 3449
+#define wxHtmlWindow_new_0 3450
+#define wxHtmlWindow_new_2 3451
+#define wxHtmlWindow_AppendToPage 3452
+#define wxHtmlWindow_GetOpenedAnchor 3453
+#define wxHtmlWindow_GetOpenedPage 3454
+#define wxHtmlWindow_GetOpenedPageTitle 3455
+#define wxHtmlWindow_GetRelatedFrame 3456
+#define wxHtmlWindow_HistoryBack 3457
+#define wxHtmlWindow_HistoryCanBack 3458
+#define wxHtmlWindow_HistoryCanForward 3459
+#define wxHtmlWindow_HistoryClear 3460
+#define wxHtmlWindow_HistoryForward 3461
+#define wxHtmlWindow_LoadFile 3462
+#define wxHtmlWindow_LoadPage 3463
+#define wxHtmlWindow_SelectAll 3464
+#define wxHtmlWindow_SelectionToText 3465
+#define wxHtmlWindow_SelectLine 3466
+#define wxHtmlWindow_SelectWord 3467
+#define wxHtmlWindow_SetBorders 3468
+#define wxHtmlWindow_SetFonts 3469
+#define wxHtmlWindow_SetPage 3470
+#define wxHtmlWindow_SetRelatedFrame 3471
+#define wxHtmlWindow_SetRelatedStatusBar 3472
+#define wxHtmlWindow_ToText 3473
+#define wxHtmlWindow_destroy 3474
+#define wxHtmlLinkEvent_GetLinkInfo 3475
+#define wxSystemSettings_GetColour 3476
+#define wxSystemSettings_GetFont 3477
+#define wxSystemSettings_GetMetric 3478
+#define wxSystemSettings_GetScreenType 3479
+#define wxAuiNotebookEvent_SetSelection 3480
+#define wxAuiNotebookEvent_GetSelection 3481
+#define wxAuiNotebookEvent_SetOldSelection 3482
+#define wxAuiNotebookEvent_GetOldSelection 3483
+#define wxAuiNotebookEvent_SetDragSource 3484
+#define wxAuiNotebookEvent_GetDragSource 3485
+#define wxAuiManagerEvent_SetManager 3486
+#define wxAuiManagerEvent_GetManager 3487
+#define wxAuiManagerEvent_SetPane 3488
+#define wxAuiManagerEvent_GetPane 3489
+#define wxAuiManagerEvent_SetButton 3490
+#define wxAuiManagerEvent_GetButton 3491
+#define wxAuiManagerEvent_SetDC 3492
+#define wxAuiManagerEvent_GetDC 3493
+#define wxAuiManagerEvent_Veto 3494
+#define wxAuiManagerEvent_GetVeto 3495
+#define wxAuiManagerEvent_SetCanVeto 3496
+#define wxAuiManagerEvent_CanVeto 3497
+#define wxLogNull_new 3498
+#define wxLogNull_destroy 3499
diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c
index 310325ea26..2404b13cc3 100644
--- a/lib/wx/c_src/wxe_driver.c
+++ b/lib/wx/c_src/wxe_driver.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -117,8 +117,7 @@ wxe_driver_start(ErlDrvPort port, char *buff)
if(WXE_DRV_PORT == 0) {
for(; *buff != 32; buff++);
buff++;
- erl_wx_privdir = malloc(strlen(buff));
- strcpy(erl_wx_privdir, buff);
+ erl_wx_privdir = strdup(buff);
WXE_DRV_PORT = port;
wxe_master = data;
@@ -146,7 +145,6 @@ static void
wxe_driver_unload(void)
{
// fprintf(stderr, "%s:%d: UNLOAD \r\n", __FILE__,__LINE__);
- meta_command(WXE_SHUTDOWN, wxe_master);
stop_native_gui(wxe_master);
unload_native_gui();
free(wxe_master);
diff --git a/lib/wx/c_src/wxe_driver.h b/lib/wx/c_src/wxe_driver.h
index 13a17e356f..5c5b8614ed 100644
--- a/lib/wx/c_src/wxe_driver.h
+++ b/lib/wx/c_src/wxe_driver.h
@@ -83,8 +83,9 @@ extern char * erl_wx_privdir;
#define WXE_CB_START 8
#define WXE_DEBUG_DRIVER 9
#define WXE_DEBUG_PING 10
-#define WXE_BIN_INCR 5001
-#define WXE_BIN_DECR 5002
+#define WXE_BIN_INCR 11
+#define WXE_BIN_DECR 12
+#define WXE_INIT_OPENGL 13
#define OPENGL_START 5000
diff --git a/lib/wx/c_src/wxe_gl.cpp b/lib/wx/c_src/wxe_gl.cpp
index 63dd68fa5e..e947a1bc6e 100644
--- a/lib/wx/c_src/wxe_gl.cpp
+++ b/lib/wx/c_src/wxe_gl.cpp
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -19,303 +19,142 @@
#include <stdio.h>
#include <string.h>
+#ifndef _WIN32
+#include <dlfcn.h>
+#else
+#include <windows.h>
+#endif
#include "wxe_impl.h"
-
-#include "wxe_gl.h"
-
-#define WX_DEF_EXTS
-#include "gen/gl_fdefs.h"
-#include "gen/gl_finit.h"
-#include "gen/glu_finit.h"
+#include "wxe_return.h"
/* ****************************************************************************
* Opengl context management *
* ****************************************************************************/
-int gl_initiated = FALSE;
+int erl_gl_initiated = FALSE;
ErlDrvTermData gl_active = 0;
wxeGLC glc;
-void setActiveGL(ErlDrvTermData caller, wxGLCanvas *canvas)
-{
- if(gl_initiated == FALSE) {
- initOpenGL();
- init_tess();
- gl_initiated = TRUE;
- }
- gl_active = caller;
- glc[caller] = canvas;
-}
-
-void deleteActiveGL(wxGLCanvas *canvas)
-{
- gl_active = 0;
- wxeGLC::iterator it;
- for(it = glc.begin(); it != glc.end(); ++it) {
- if(it->second == canvas) {
- it->second = (wxGLCanvas *) 0;
- }
- }
-}
-
-/* ****************************************************************************
- * OPENGL INITIALIZATION
- *****************************************************************************/
+typedef void (*WXE_GL_DISPATCH) (int, char *, ErlDrvPort, ErlDrvTermData, char **, int *);
+WXE_GL_DISPATCH wxe_gl_dispatch;
#ifdef _WIN32
+#define RTLD_LAZY 0
+typedef HMODULE DL_LIB_P;
void * dlsym(HMODULE Lib, const char *func) {
void * funcp;
- if((funcp = (void *) GetProcAddress(Lib, func)))
+ if((funcp = (void *) GetProcAddress(Lib, func)))
return funcp;
- else
+ else
return (void *) wglGetProcAddress(func);
}
-#endif
-int initOpenGL()
-{
-#ifdef _MACOSX
- char * DLName = "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib";
- void * LIBhandle = dlopen(DLName, RTLD_LAZY);
-#elif defined(_WIN32)
- WCHAR * DLName = wxT("opengl32.dll");
- HMODULE LIBhandle = LoadLibrary(DLName);
-#else
- char * DLName = (char *) "libGL.so";
- void * LIBhandle = dlopen(DLName, RTLD_LAZY);
-#endif
- // fprintf(stderr, "Loading GL: %s\r\n", (const char*)DLName);
- void * func = NULL;
- int i;
+HMODULE dlopen(const char *path, int unused) {
+ WCHAR * DLL;
+ int len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
+ DLL = (WCHAR *) malloc(len * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, 0, path, -1, DLL, len);
+ HMODULE lib = LoadLibrary(DLL);
+ free(DLL);
+ return lib;
+}
- if(LIBhandle) {
- for(i=0; gl_fns[i].name != NULL; i++) {
- if((func = dlsym(LIBhandle, gl_fns[i].name))) {
- * (void **) (gl_fns[i].func) = func;
- // fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].name);
- } else {
- if(gl_fns[i].alt != NULL) {
- if((func = dlsym(LIBhandle, gl_fns[i].alt))) {
- * (void **) (gl_fns[i].func) = func;
- // fprintf(stderr, "GL LOADED %s \r\n", gl_fns[i].alt);
- } else {
- * (void **) (gl_fns[i].func) = (void *) &gl_error;
- // fprintf(stderr, "GL Skipped %s and %s \r\n", gl_fns[i].name, gl_fns[i].alt);
- };
- } else {
- * (void **) (gl_fns[i].func) = (void *) &gl_error;
- // fprintf(stderr, "GL Skipped %s \r\n", gl_fns[i].name);
- }
- }
- }
-#ifdef _WIN32
- FreeLibrary(LIBhandle);
-#else
- dlclose(LIBhandle);
-#endif
- // fprintf(stderr, "OPENGL library is loaded\r\n");
- } else {
- wxString msg;
- msg.Printf(wxT("Could NOT load OpenGL library: "));
-#ifdef _WIN32
- msg += DLName;
+void dlclose(HMODULE Lib) {
+ FreeLibrary(Lib);
+}
#else
- msg += wxString::FromAscii((char *)DLName);
+typedef void * DL_LIB_P;
#endif
- send_msg("error", &msg);
- };
-#ifdef _MACOSX
- DLName = "/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGLU.dylib";
- LIBhandle = dlopen(DLName, RTLD_LAZY);
-#elif defined(_WIN32)
- DLName = wxT("glu32.dll");
- LIBhandle = LoadLibrary(DLName);
+void wxe_initOpenGL(wxeReturn rt, char *bp) {
+ DL_LIB_P LIBhandle;
+ int (*init_opengl)(void *);
+#ifdef _WIN32
+ void * erlCallbacks = &WinDynDriverCallbacks;
#else
- DLName = (char *) "libGLU.so";
- LIBhandle = dlopen(DLName, RTLD_LAZY);
+ void * erlCallbacks = NULL;
#endif
- // fprintf(stderr, "Loading GL: %s\r\n", (const char*)DLName);
- func = NULL;
-
- if(LIBhandle) {
- for(i=0; glu_fns[i].name != NULL; i++) {
- if((func = dlsym(LIBhandle, glu_fns[i].name))) {
- * (void **) (glu_fns[i].func) = func;
+
+ if(erl_gl_initiated == FALSE) {
+ if((LIBhandle = dlopen(bp, RTLD_LAZY))) {
+ *(void **) (&init_opengl) = dlsym(LIBhandle, "egl_init_opengl");
+ wxe_gl_dispatch = (WXE_GL_DISPATCH) dlsym(LIBhandle, "egl_dispatch");
+ if(init_opengl && wxe_gl_dispatch) {
+ (*init_opengl)(erlCallbacks);
+ rt.addAtom((char *) "ok");
+ rt.add(wxString::FromAscii("initiated"));
+ rt.addTupleCount(2);
+ erl_gl_initiated = TRUE;
} else {
- if(glu_fns[i].alt != NULL) {
- if((func = dlsym(LIBhandle, glu_fns[i].alt))) {
- * (void **) (glu_fns[i].func) = func;
- } else {
- * (void **) (glu_fns[i].func) = (void *) &gl_error;
- // fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].alt);
- };
- } else {
- * (void **) (glu_fns[i].func) = (void *) &gl_error;
- // fprintf(stderr, "GLU Skipped %s\r\n", glu_fns[i].name);
- }
+ wxString msg;
+ msg.Printf(wxT("In library: "));
+ msg += wxString::FromAscii(bp);
+ msg += wxT(" functions: ");
+ if(!init_opengl)
+ msg += wxT("egl_init_opengl ");
+ if(!wxe_gl_dispatch)
+ msg += wxT("egl_dispatch ");
+ rt.addAtom((char *) "error");
+ rt.add(msg);
+ rt.addTupleCount(2);
}
+ } else {
+ wxString msg;
+ msg.Printf(wxT("Could not load dll: "));
+ msg += wxString::FromAscii(bp);
+ rt.addAtom((char *) "error");
+ rt.add(msg);
+ rt.addTupleCount(2);
}
-#ifdef _WIN32
- FreeLibrary(LIBhandle);
-#else
- dlclose(LIBhandle);
-#endif
- // fprintf(stderr, "GLU library is loaded\r\n");
} else {
- wxString msg;
- msg.Printf(wxT("Could NOT load OpenGL GLU library: "));
-#ifdef _WIN32
- msg += DLName;
-#else
- msg += wxString::FromAscii((char *)DLName);
-#endif
- send_msg("error", &msg);
- };
- return 0;
-}
-
-void gl_error() {
- int AP = 0; ErlDrvTermData rt[8];
- rt[AP++] = ERL_DRV_ATOM; rt[AP++] = driver_mk_atom((char *)"_wxe_error_");
- rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) gl_error_op;
- rt[AP++] = ERL_DRV_ATOM; rt[AP++] = driver_mk_atom((char *)"undef");
- rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 3;
- driver_send_term(WXE_DRV_PORT,gl_active,rt,AP);
-}
-
-/* *******************************************************************************
- * GLU Tesselation special
- * ******************************************************************************/
-
-static GLUtesselator* tess;
-static GLdouble* tess_coords;
-static GLdouble* tess_alloc_vertex;
-static int* tess_vertices;
-
-void CALLBACK
-wxe_ogla_vertex(GLdouble* coords)
-{
- /* fprintf(stderr, "%d\r\n", (int) (coords - tess_coords) / 3); */
-
- *tess_vertices++ = (int) (coords - tess_coords) / 3;
-}
-
-void CALLBACK
-wxe_ogla_edge_flag(GLboolean flag)
-{
+ rt.addAtom((char *) "ok");
+ rt.add(wxString::FromAscii("already initilized"));
+ rt.addTupleCount(2);
+ }
+ rt.send();
}
-void CALLBACK
-wxe_ogla_error(GLenum errorCode)
+void setActiveGL(ErlDrvTermData caller, wxGLCanvas *canvas)
{
- const GLubyte *err;
- err = gluErrorString(errorCode);
- wxString msg;
- msg.Printf(wxT("Tesselation error: %d: "), (int)errorCode);
- msg += wxString::FromAscii((char *) err);
- send_msg("error", &msg);
+ gl_active = caller;
+ glc[caller] = canvas;
}
-void CALLBACK
-wxe_ogla_combine(GLdouble coords[3],
- void* vertex_data[4],
- GLfloat w[4],
- void **dataOut)
+void deleteActiveGL(wxGLCanvas *canvas)
{
- GLdouble* vertex = tess_alloc_vertex;
-
- tess_alloc_vertex += 3;
-
-#if 0
- fprintf(stderr, "combine: ");
- int i;
- for (i = 0; i < 4; i++) {
- if (w[i] > 0.0) {
- fprintf(stderr, "%d(%g) ", (int) vertex_data[i], w[i]);
+ gl_active = 0;
+ wxeGLC::iterator it;
+ for(it = glc.begin(); it != glc.end(); ++it) {
+ if(it->second == canvas) {
+ it->second = (wxGLCanvas *) 0;
}
}
- fprintf(stderr, "\r\n");
- fprintf(stderr, "%g %g %g\r\n", vertex[0], vertex[1], vertex[2]);
-#endif
-
- vertex[0] = coords[0];
- vertex[1] = coords[1];
- vertex[2] = coords[2];
- *dataOut = vertex;
-}
-
-void init_tess()
-{
- tess = gluNewTess();
-
- gluTessCallback(tess, GLU_TESS_VERTEX, (GLUfuncptr) wxe_ogla_vertex);
- gluTessCallback(tess, GLU_TESS_EDGE_FLAG, (GLUfuncptr) wxe_ogla_edge_flag);
- gluTessCallback(tess, GLU_TESS_COMBINE, (GLUfuncptr) wxe_ogla_combine);
- gluTessCallback(tess, GLU_TESS_ERROR, (GLUfuncptr) wxe_ogla_error);
-
}
-void exit_tess()
-{
- gluDeleteTess(tess);
-}
-
-int wxe_tess_impl(char* buff, ErlDrvTermData caller)
-{
- ErlDrvBinary* bin;
- int i;
- int num_vertices = * (int *) buff; buff += 8; // Align
- GLdouble *n = (double *) buff; buff += 8*3;
-
- GLdouble* new_vertices;
- bin = driver_alloc_binary(num_vertices*6*sizeof(GLdouble));
- new_vertices = tess_coords = (double *) bin->orig_bytes;
- memcpy(tess_coords,buff,num_vertices*3*sizeof(GLdouble));
- tess_alloc_vertex = tess_coords + num_vertices*3;
-
-#if 0
- fprintf(stderr, "n=%d\r\n", num_vertices);
-#endif
- int *vertices;
- vertices = (int *) driver_alloc(sizeof(int) * 16*num_vertices);
-
- tess_vertices = vertices;
-
- gluTessNormal(tess, n[0], n[1], n[2]);
- gluTessBeginPolygon(tess, 0);
- gluTessBeginContour(tess);
- for (i = 0; i < num_vertices; i++) {
- gluTessVertex(tess, tess_coords+3*i, tess_coords+3*i);
- }
- gluTessEndContour(tess);
- gluTessEndPolygon(tess);
-
- int n_pos = (tess_vertices - vertices);
-
- int AP = 0; ErlDrvTermData *rt;
- rt = (ErlDrvTermData *) driver_alloc(sizeof(ErlDrvTermData) * (13+n_pos*2));
- rt[AP++]=ERL_DRV_ATOM; rt[AP++]=driver_mk_atom((char *) "_wxe_result_");
-
- for(i=0; i < n_pos; i++) {
- rt[AP++] = ERL_DRV_INT; rt[AP++] = (int) vertices[i];
+void gl_dispatch(int op, char *bp,ErlDrvTermData caller,WXEBinRef *bins[]){
+ if(caller != gl_active) {
+ wxGLCanvas * current = glc[caller];
+ if(current) { gl_active = caller; current->SetCurrent();}
+ else {
+ ErlDrvTermData rt[] = // Error msg
+ {ERL_DRV_ATOM, driver_mk_atom((char *) "_egl_error_"),
+ ERL_DRV_INT, op,
+ ERL_DRV_ATOM, driver_mk_atom((char *) "no_gl_context"),
+ ERL_DRV_TUPLE,3};
+ driver_send_term(WXE_DRV_PORT,caller,rt,8);
+ return ;
+ }
};
- rt[AP++] = ERL_DRV_NIL; rt[AP++] = ERL_DRV_LIST; rt[AP++] = n_pos+1;
-
- rt[AP++] = ERL_DRV_BINARY; rt[AP++] = (ErlDrvTermData) bin;
- rt[AP++] = (tess_alloc_vertex-new_vertices)*sizeof(GLdouble); rt[AP++] = 0;
-
- rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Return tuple {list, Bin}
- rt[AP++] = ERL_DRV_TUPLE; rt[AP++] = 2; // Result tuple
-
- driver_send_term(WXE_DRV_PORT,caller,rt,AP);
-// fprintf(stderr, "List %d %d %d \r\n",
-// n_pos,
-// (tess_alloc_vertex-new_vertices)*sizeof(GLdouble),
-// num_vertices*6*sizeof(GLdouble));
- driver_free_binary(bin);
- driver_free(vertices);
- driver_free(rt);
- return 0;
+ char * bs[3];
+ int bs_sz[3];
+ for(int i=0; i<3; i++) {
+ if(bins[i]) {
+ bs[i] = bins[i]->base;
+ bs_sz[i] = bins[i]->size;
+ }
+ else
+ bs[i] = NULL;
+ }
+ wxe_gl_dispatch(op, bp, WXE_DRV_PORT, caller, bs, bs_sz);
}
diff --git a/lib/wx/c_src/wxe_gl.h b/lib/wx/c_src/wxe_gl.h
index 3a47b3c1bd..1b556ff4ec 100644
--- a/lib/wx/c_src/wxe_gl.h
+++ b/lib/wx/c_src/wxe_gl.h
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2010. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -17,119 +17,6 @@
* %CopyrightEnd%
*/
+#include "egl_impl.h"
-#ifndef _WIN32
-# include <dlfcn.h>
-#endif
-
-#ifndef __WXMAC__
-# include <GL/gl.h>
-# include <GL/glu.h> /* Header File For The OpenGL Library */
-#else
-# include <OpenGL/glu.h> /* Header File For The OpenGL Library */
-#endif
-
-#ifndef CALLBACK
-# define CALLBACK
-#endif
-
-#ifdef _WIN32
-# ifndef _GLUfuncptr
-// Visual studio CPP ++ compiler
-# define _GLUfuncptr void (_stdcall *)()
-# endif
-#endif
-
-#ifdef _GLUfuncptr
-# define GLUfuncptr _GLUfuncptr
-#elif defined(TESS_CB_TIGER_STYLE)
-# define GLUfuncptr GLvoid (*)(...)
-#else
-# define GLUfuncptr GLvoid (*)()
-#endif
-
-#ifdef WIN32
-#include <windows.h>
-#include <gl/gl.h>
-#elif defined(HAVE_GL_GL_H)
-#include <GL/gl.h>
-#elif defined(HAVE_OPENGL_GL_H)
-#endif
-
-#ifndef APIENTRY
-#define APIENTRY
-#endif
-
-int initOpenGL();
-void gl_error();
-extern int gl_error_op;
-extern ErlDrvTermData gl_active;
-
-/* Some new GL types (eliminates the need for glext.h) */
-
-#ifndef HAVE_GLINTPTR
-#ifndef HAVE_GLINTPTRARB
-# include <stddef.h>
-/* GL types for handling large vertex buffer objects */
-typedef ptrdiff_t GLintptrARB;
-typedef ptrdiff_t GLsizeiptrARB;
-#endif /* HAVE_GLINTPTRARB */
-typedef GLintptrARB GLintptr;
-typedef GLsizeiptrARB GLsizeiptr;
-#endif /* HAVE_GLINTPTR */
-
-#ifndef HAVE_GLCHAR
-# ifndef HAVE_GLCHARARB
-/* GL types for handling shader object handles and characters */
-typedef char GLcharARB; /* native character */
-typedef unsigned int GLhandleARB; /* shader object handle */
-#endif /* HAVE_GLCHARARB */
-typedef GLcharARB GLchar;
-#endif
-
-#ifndef HAVE_GLHALFARB
-/* GL types for "half" precision (s10e5) float data in host memory */
-typedef unsigned short GLhalfARB;
-#endif
-
-/* Define int32_t, int64_t, and uint64_t types for UST/MSC */
-/* (as used in the GLX_OML_sync_control extension). */
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
-#include <inttypes.h>
-#elif defined(__sun__)
-#include <inttypes.h>
-#if defined(__STDC__)
-#if defined(__arch64__)
-typedef long int int64_t;
-typedef unsigned long int uint64_t;
-#else
-typedef long long int int64_t;
-typedef unsigned long long int uint64_t;
-#endif /* __arch64__ */
-#endif /* __STDC__ */
-#elif defined( __VMS )
-#include <inttypes.h>
-#elif defined(__SCO__) || defined(__USLC__)
-#include <stdint.h>
-#elif defined(__UNIXOS2__) || defined(__SOL64__)
-typedef long int int32_t;
-typedef long long int int64_t;
-typedef unsigned long long int uint64_t;
-#elif defined(WIN32) && defined(_MSC_VER)
-typedef long int int32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-#elif defined(WIN32) && defined(__GNUC__)
-#include <stdint.h>
-#else
-#include <inttypes.h> /* Fallback option */
-#endif
-
-#ifndef HAVE_GLINT64EXT
-typedef int64_t GLint64EXT;
-typedef uint64_t GLuint64EXT;
-#endif
-
-void init_tess();
-void exit_tess();
-int wxe_tess_impl(char* buff, ErlDrvTermData caller);
+void wxe_initOpenGL(wxeReturn, char*);
diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp
index 528a08e654..365fb691a1 100644
--- a/lib/wx/c_src/wxe_impl.cpp
+++ b/lib/wx/c_src/wxe_impl.cpp
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2010. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
@@ -68,7 +68,6 @@ ErlDrvTermData wxe_batch_caller = 0;
ErlDrvTermData init_caller = 0;
// extern opengl
-extern int gl_initiated;
void gl_dispatch(int op, char *bp, ErlDrvTermData caller, WXEBinRef *bins[]);
@@ -79,6 +78,21 @@ extern void erts_thread_disable_fpe(void);
}
#endif
+#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+#define __DARWIN__ 1
+#endif
+
+#ifdef __DARWIN__
+extern "C" {
+ int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp);
+ int erl_drv_steal_main_thread(char *name,
+ ErlDrvTid *dtid,
+ void* (*func)(void*),
+ void* arg,
+ ErlDrvThreadOpts *opts);
+}
+#endif
+
void *wxe_main_loop(void * );
/* ************************************************************
@@ -100,8 +114,14 @@ int start_native_gui(wxe_data *sd)
wxe_batch_locker_c = erl_drv_cond_create((char *)"wxe_batch_locker_c");
init_caller = driver_connected(sd->port);
- if((res = erl_drv_thread_create((char *)"wxwidgets",
- &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL)) == 0) {
+#ifdef __DARWIN__
+ res = erl_drv_steal_main_thread((char *)"wxwidgets",
+ &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
+#else
+ res = erl_drv_thread_create((char *)"wxwidgets",
+ &wxe_thread,wxe_main_loop,(void *) sd->pdl,NULL);
+#endif
+ if(res == 0) {
erl_drv_mutex_lock(wxe_status_m);
for(;wxe_status == WXE_NOT_INITIATED;) {
erl_drv_cond_wait(wxe_status_c, wxe_status_m);
@@ -118,7 +138,14 @@ int start_native_gui(wxe_data *sd)
void stop_native_gui(wxe_data *sd)
{
+ if(wxe_status == WXE_INITIATED) {
+ meta_command(WXE_SHUTDOWN, sd);
+ }
+#ifdef __DARWIN__
+ erl_drv_stolen_main_thread_join(wxe_thread, NULL);
+#else
erl_drv_thread_join(wxe_thread, NULL);
+#endif
erl_drv_mutex_destroy(wxe_status_m);
erl_drv_cond_destroy(wxe_status_c);
erl_drv_mutex_destroy(wxe_batch_locker_m);
@@ -183,8 +210,8 @@ void *wxe_main_loop(void *vpdl)
{
int result;
int argc = 1;
- char * temp = (char *) "Erlang\0";
- char ** argv = &temp;
+ char * temp = (char *) "Erlang";
+ char * argv[] = {temp,NULL};
ErlDrvPDL pdl = (ErlDrvPDL) vpdl;
driver_pdl_inc_refc(pdl);
@@ -203,7 +230,9 @@ void *wxe_main_loop(void *vpdl)
/* We are done try to make a clean exit */
wxe_status = WXE_EXITED;
driver_pdl_dec_refc(pdl);
+#ifndef __DARWIN__
erl_drv_thread_exit(NULL);
+#endif
return NULL;
} else {
erl_drv_mutex_lock(wxe_status_m);
@@ -265,7 +294,6 @@ bool WxeApp::OnInit()
init_nonconsts(global_me, init_caller);
erl_drv_mutex_lock(wxe_status_m);
wxe_status = WXE_INITIATED;
- gl_initiated = FALSE;
erl_drv_cond_signal(wxe_status_c);
erl_drv_mutex_unlock(wxe_status_m);
return TRUE;
@@ -411,7 +439,7 @@ void WxeApp::dispatch_cb(wxList * batch, wxList * temp, ErlDrvTermData process)
if(event->caller == process || // Callbacks from CB process only
event->op == WXE_CB_START || // Recursive event callback allow
// Allow connect_cb during CB i.e. msg from wxe_server.
- event->caller == memenv->owner)
+ (memenv && event->caller == memenv->owner))
{
switch(event->op) {
case WXE_BATCH_END:
@@ -669,7 +697,7 @@ void WxeApp::clearPtr(void * ptr) {
send_msg("debug", &msg);
}
- if(refd->pid != -1) {
+ if(((int) refd->pid) != -1) {
// Send terminate pid to owner
wxeReturn rt = wxeReturn(WXE_DRV_PORT,refd->memenv->owner, false);
rt.addAtom("_wxe_destroy_");
diff --git a/lib/wx/c_src/wxe_ps_init.c b/lib/wx/c_src/wxe_ps_init.c
index e787c214bd..a85f751024 100644
--- a/lib/wx/c_src/wxe_ps_init.c
+++ b/lib/wx/c_src/wxe_ps_init.c
@@ -1,7 +1,7 @@
/*
* %CopyrightBegin%
*
- * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ * Copyright Ericsson AB 2008-2011. All Rights Reserved.
*
* The contents of this file are subject to the Erlang Public License,
* Version 1.1, (the "License"); you may not use this file except in
diff --git a/lib/wx/c_src/wxe_return.cpp b/lib/wx/c_src/wxe_return.cpp
index 2c4f7541e7..9fd627829e 100644
--- a/lib/wx/c_src/wxe_return.cpp
+++ b/lib/wx/c_src/wxe_return.cpp
@@ -64,11 +64,14 @@ int wxeReturn::send() {
int res = driver_send_term(port, caller, rtData, rtLength);
driver_free(rtData);
+#ifdef DEBUG
if(res == -1) {
wxString msg;
msg.Printf(wxT("Failed to send return or event msg"));
send_msg("internal_error", &msg);
}
+#endif
+
reset();
return res;
}
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 855c0c975e..f7128db23a 100755
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -162,16 +162,20 @@ esac
case $host_os in
darwin*)
CFLAGS="-no-cpp-precomp $CFLAGS"
- LDFLAGS="-bundle -flat_namespace -undefined warning -fPIC -framework OpenGL $LDFLAGS"
+ LDFLAGS="-bundle -flat_namespace -undefined warning -fPIC $LDFLAGS"
+ GL_LIBS="-framework OpenGL"
;;
win32)
LDFLAGS="-dll $LDFLAGS"
+ GL_LIBS="-lglu32 -lOpengl32"
;;
mingw32)
LDFLAGS="-shared -fPIC $LDFLAGS"
+ GL_LIBS="-lglu32 -lOpengl32"
;;
*)
LDFLAGS="-shared -fPIC $LDFLAGS"
+ GL_LIBS="-lGL -lGLU"
;;
esac
@@ -194,6 +198,42 @@ case $host_os in
;;
esac
+dnl
+dnl Opengl tests
+dnl
+
+if test X"$host_os" != X"win32" ; then
+ AC_CHECK_HEADERS([GL/gl.h], [],
+ [AC_CHECK_HEADERS([OpenGL/gl.h])])
+ if test X"$ac_cv_header_GL_gl_h" != Xyes &&
+ test X"$ac_cv_header_OpenGL_gl_h" != Xyes
+ then
+ saved_CPPFLAGS="$CPPFLAGS"
+ AC_MSG_NOTICE(Checking for OpenGL headers in /usr/X11R6)
+ CPPFLAGS="-isystem /usr/X11R6/include $CPPFLAGS"
+ $as_unset ac_cv_header_GL_gl_h
+ AC_CHECK_HEADERS([GL/gl.h])
+ if test X"$ac_cv_header_GL_gl_h" != Xyes ; then
+ AC_MSG_NOTICE(Checking for OpenGL headers in /usr/local)
+ CPPFLAGS="-isystem /usr/local/include $saved_CPPFLAGS"
+ $as_unset ac_cv_header_GL_gl_h
+ AC_CHECK_HEADERS([GL/gl.h])
+ if test X"$ac_cv_header_GL_gl_h" != Xyes ; then
+ AC_MSG_WARN([No OpenGL headers found, wx will NOT be usable])
+ CPPFLAGS="$saved_CPPFLAGS"
+ else
+ GL_LIBS="-L/usr/local/lib $GL_LIBS"
+ fi
+ else
+ GL_LIBS="-L/usr/X11R6/lib $GL_LIBS"
+ fi
+ fi
+else
+ AC_CHECK_HEADERS([gl/gl.h],[],[],[#include <windows.h>])
+fi
+
+AC_SUBST(GL_LIBS)
+
CXXFLAGS="$CFLAGS $CPPFLAGS"
CFLAGS="$CFLAGS $CPPFLAGS $C_ONLY_FLAGS"
@@ -386,17 +426,6 @@ if test "$WXERL_CAN_BUILD_DRIVER" != "false"; then
AC_SUBST(WX_HAVE_STATIC_LIBS)
AC_SUBST(RC_FILE_TYPE)
-dnl
-dnl Opengl tests
-dnl
-
-if test X"$host_os" != X"win32" ; then
- AC_CHECK_HEADERS([GL/gl.h])
- AC_CHECK_HEADERS([OpenGL/gl.h])
-else
- AC_CHECK_HEADERS([gl/gl.h],[],[],[#include <windows.h>])
-fi
-
AC_MSG_CHECKING(if wxwidgets have opengl support)
AC_LANG_PUSH(C++)
saved_CXXFLAGS=$CXXFLAGS
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 92933c348b..3d27cf671b 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -31,6 +31,96 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 0.98.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Wx crashed if graphics could not be initiated, for
+ instance if DISPLAY was not available.</p> <p>Wx could
+ crash during startup, thanks Boris Muhmer for extra
+ ordinary testing.</p>
+ <p>
+ Own Id: OTP-9080</p>
+ </item>
+ <item>
+ <p>
+ Wx on MacOS X generated complains on stderr about certain
+ cocoa functions not beeing called from the "Main thread".
+ This is now corrected.</p>
+ <p>
+ Own Id: OTP-9081</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Wx 0.98.8</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Add wxSystemSettings which was missing in the previous
+ release, despite previous comments.</p> <p>Fix an
+ external loop when stopping erlang nicely.</p>
+ <p>Separate OpenGL to it's own dynamic loaded library, so
+ other graphic libraries can reuse the gl module and it
+ will not waste memory if not used.</p>
+ <p>
+ Own Id: OTP-8951</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Wx 0.98.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix crash (segmentation fault) in callback handling.</p>
+ <p>
+ Own Id: OTP-8766</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add wxSystemSettings module.</p>
+ <p>
+ Add wxTreeCtrl:editLabel/2.</p>
+ <p>
+ Own Id: OTP-8767</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Wx 0.98.6</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Calling <c>sys:get_status()</c> for processes that have
+ globally registered names that were not atoms would cause
+ a crash. Corrected. (Thanks to Steve Vinoski.)</p>
+ <p>
+ Own Id: OTP-8656</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 0.98.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/include/gl.hrl b/lib/wx/include/gl.hrl
index 2fa0d72a59..52f2635af9 100644
--- a/lib/wx/include/gl.hrl
+++ b/lib/wx/include/gl.hrl
@@ -782,7 +782,7 @@
-define(GL_TEXTURE_COMPARE_MODE, 16#884C).
-define(GL_TEXTURE_COMPARE_FUNC, 16#884D).
-define(GL_COMPARE_R_TO_TEXTURE, 16#884E).
--define(GL_GLEXT_VERSION, 52).
+-define(GL_GLEXT_VERSION, 65).
-define(GL_CONSTANT_COLOR, 16#8001).
-define(GL_ONE_MINUS_CONSTANT_COLOR, 16#8002).
-define(GL_CONSTANT_ALPHA, 16#8003).
@@ -1021,6 +1021,8 @@
-define(GL_CLIP_DISTANCE3, 16#3003).
-define(GL_CLIP_DISTANCE4, 16#3004).
-define(GL_CLIP_DISTANCE5, 16#3005).
+-define(GL_CLIP_DISTANCE6, 16#3006).
+-define(GL_CLIP_DISTANCE7, 16#3007).
-define(GL_MAX_CLIP_DISTANCES, 16#D32).
-define(GL_MAJOR_VERSION, 16#821B).
-define(GL_MINOR_VERSION, 16#821C).
@@ -1111,6 +1113,9 @@
-define(GL_QUERY_NO_WAIT, 16#8E14).
-define(GL_QUERY_BY_REGION_WAIT, 16#8E15).
-define(GL_QUERY_BY_REGION_NO_WAIT, 16#8E16).
+-define(GL_BUFFER_ACCESS_FLAGS, 16#911F).
+-define(GL_BUFFER_MAP_LENGTH, 16#9120).
+-define(GL_BUFFER_MAP_OFFSET, 16#9121).
-define(GL_CLAMP_VERTEX_COLOR, 16#891A).
-define(GL_CLAMP_FRAGMENT_COLOR, 16#891B).
-define(GL_ALPHA_INTEGER, 16#8D97).
@@ -1145,6 +1150,40 @@
-define(GL_SIGNED_NORMALIZED, 16#8F9C).
-define(GL_PRIMITIVE_RESTART, 16#8F9D).
-define(GL_PRIMITIVE_RESTART_INDEX, 16#8F9E).
+-define(GL_CONTEXT_CORE_PROFILE_BIT, 16#1).
+-define(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, 16#2).
+-define(GL_LINES_ADJACENCY, 16#A).
+-define(GL_LINE_STRIP_ADJACENCY, 16#B).
+-define(GL_TRIANGLES_ADJACENCY, 16#C).
+-define(GL_TRIANGLE_STRIP_ADJACENCY, 16#D).
+-define(GL_PROGRAM_POINT_SIZE, 16#8642).
+-define(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, 16#8C29).
+-define(GL_FRAMEBUFFER_ATTACHMENT_LAYERED, 16#8DA7).
+-define(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS, 16#8DA8).
+-define(GL_GEOMETRY_SHADER, 16#8DD9).
+-define(GL_GEOMETRY_VERTICES_OUT, 16#8916).
+-define(GL_GEOMETRY_INPUT_TYPE, 16#8917).
+-define(GL_GEOMETRY_OUTPUT_TYPE, 16#8918).
+-define(GL_MAX_GEOMETRY_UNIFORM_COMPONENTS, 16#8DDF).
+-define(GL_MAX_GEOMETRY_OUTPUT_VERTICES, 16#8DE0).
+-define(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, 16#8DE1).
+-define(GL_MAX_VERTEX_OUTPUT_COMPONENTS, 16#9122).
+-define(GL_MAX_GEOMETRY_INPUT_COMPONENTS, 16#9123).
+-define(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, 16#9124).
+-define(GL_MAX_FRAGMENT_INPUT_COMPONENTS, 16#9125).
+-define(GL_CONTEXT_PROFILE_MASK, 16#9126).
+-define(GL_VERTEX_ATTRIB_ARRAY_DIVISOR, 16#88FE).
+-define(GL_SAMPLE_SHADING, 16#8C36).
+-define(GL_MIN_SAMPLE_SHADING_VALUE, 16#8C37).
+-define(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET, 16#8E5E).
+-define(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET, 16#8E5F).
+-define(GL_TEXTURE_CUBE_MAP_ARRAY, 16#9009).
+-define(GL_TEXTURE_BINDING_CUBE_MAP_ARRAY, 16#900A).
+-define(GL_PROXY_TEXTURE_CUBE_MAP_ARRAY, 16#900B).
+-define(GL_SAMPLER_CUBE_MAP_ARRAY, 16#900C).
+-define(GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW, 16#900D).
+-define(GL_INT_SAMPLER_CUBE_MAP_ARRAY, 16#900E).
+-define(GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY, 16#900F).
-define(GL_TEXTURE0_ARB, 16#84C0).
-define(GL_TEXTURE1_ARB, 16#84C1).
-define(GL_TEXTURE2_ARB, 16#84C2).
@@ -1712,6 +1751,211 @@
-define(GL_INVALID_INDEX, 16#FFFFFFFF).
-define(GL_COPY_READ_BUFFER, 16#8F36).
-define(GL_COPY_WRITE_BUFFER, 16#8F37).
+-define(GL_DEPTH_CLAMP, 16#864F).
+-define(GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION, 16#8E4C).
+-define(GL_FIRST_VERTEX_CONVENTION, 16#8E4D).
+-define(GL_LAST_VERTEX_CONVENTION, 16#8E4E).
+-define(GL_PROVOKING_VERTEX, 16#8E4F).
+-define(GL_TEXTURE_CUBE_MAP_SEAMLESS, 16#884F).
+-define(GL_MAX_SERVER_WAIT_TIMEOUT, 16#9111).
+-define(GL_OBJECT_TYPE, 16#9112).
+-define(GL_SYNC_CONDITION, 16#9113).
+-define(GL_SYNC_STATUS, 16#9114).
+-define(GL_SYNC_FLAGS, 16#9115).
+-define(GL_SYNC_FENCE, 16#9116).
+-define(GL_SYNC_GPU_COMMANDS_COMPLETE, 16#9117).
+-define(GL_UNSIGNALED, 16#9118).
+-define(GL_SIGNALED, 16#9119).
+-define(GL_ALREADY_SIGNALED, 16#911A).
+-define(GL_TIMEOUT_EXPIRED, 16#911B).
+-define(GL_CONDITION_SATISFIED, 16#911C).
+-define(GL_WAIT_FAILED, 16#911D).
+-define(GL_SYNC_FLUSH_COMMANDS_BIT, 16#1).
+-define(GL_TIMEOUT_IGNORED, 16#FFFFFFFFFFFFFFFF).
+-define(GL_SAMPLE_POSITION, 16#8E50).
+-define(GL_SAMPLE_MASK, 16#8E51).
+-define(GL_SAMPLE_MASK_VALUE, 16#8E52).
+-define(GL_MAX_SAMPLE_MASK_WORDS, 16#8E59).
+-define(GL_TEXTURE_2D_MULTISAMPLE, 16#9100).
+-define(GL_PROXY_TEXTURE_2D_MULTISAMPLE, 16#9101).
+-define(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, 16#9102).
+-define(GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY, 16#9103).
+-define(GL_TEXTURE_BINDING_2D_MULTISAMPLE, 16#9104).
+-define(GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY, 16#9105).
+-define(GL_TEXTURE_SAMPLES, 16#9106).
+-define(GL_TEXTURE_FIXED_SAMPLE_LOCATIONS, 16#9107).
+-define(GL_SAMPLER_2D_MULTISAMPLE, 16#9108).
+-define(GL_INT_SAMPLER_2D_MULTISAMPLE, 16#9109).
+-define(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE, 16#910A).
+-define(GL_SAMPLER_2D_MULTISAMPLE_ARRAY, 16#910B).
+-define(GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, 16#910C).
+-define(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY, 16#910D).
+-define(GL_MAX_COLOR_TEXTURE_SAMPLES, 16#910E).
+-define(GL_MAX_DEPTH_TEXTURE_SAMPLES, 16#910F).
+-define(GL_MAX_INTEGER_SAMPLES, 16#9110).
+-define(GL_SAMPLE_SHADING_ARB, 16#8C36).
+-define(GL_MIN_SAMPLE_SHADING_VALUE_ARB, 16#8C37).
+-define(GL_TEXTURE_CUBE_MAP_ARRAY_ARB, 16#9009).
+-define(GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB, 16#900A).
+-define(GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB, 16#900B).
+-define(GL_SAMPLER_CUBE_MAP_ARRAY_ARB, 16#900C).
+-define(GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB, 16#900D).
+-define(GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB, 16#900E).
+-define(GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB, 16#900F).
+-define(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB, 16#8E5E).
+-define(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB, 16#8E5F).
+-define(GL_SHADER_INCLUDE_ARB, 16#8DAE).
+-define(GL_NAMED_STRING_LENGTH_ARB, 16#8DE9).
+-define(GL_NAMED_STRING_TYPE_ARB, 16#8DEA).
+-define(GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, 16#8E8C).
+-define(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB, 16#8E8D).
+-define(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, 16#8E8E).
+-define(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, 16#8E8F).
+-define(GL_SRC1_COLOR, 16#88F9).
+-define(GL_ONE_MINUS_SRC1_COLOR, 16#88FA).
+-define(GL_ONE_MINUS_SRC1_ALPHA, 16#88FB).
+-define(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, 16#88FC).
+-define(GL_ANY_SAMPLES_PASSED, 16#8C2F).
+-define(GL_SAMPLER_BINDING, 16#8919).
+-define(GL_RGB10_A2UI, 16#906F).
+-define(GL_TEXTURE_SWIZZLE_R, 16#8E42).
+-define(GL_TEXTURE_SWIZZLE_G, 16#8E43).
+-define(GL_TEXTURE_SWIZZLE_B, 16#8E44).
+-define(GL_TEXTURE_SWIZZLE_A, 16#8E45).
+-define(GL_TEXTURE_SWIZZLE_RGBA, 16#8E46).
+-define(GL_TIME_ELAPSED, 16#88BF).
+-define(GL_TIMESTAMP, 16#8E28).
+-define(GL_INT_2_10_10_10_REV, 16#8D9F).
+-define(GL_DRAW_INDIRECT_BUFFER, 16#8F3F).
+-define(GL_DRAW_INDIRECT_BUFFER_BINDING, 16#8F43).
+-define(GL_GEOMETRY_SHADER_INVOCATIONS, 16#887F).
+-define(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, 16#8E5A).
+-define(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET, 16#8E5B).
+-define(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET, 16#8E5C).
+-define(GL_FRAGMENT_INTERPOLATION_OFFSET_BITS, 16#8E5D).
+-define(GL_DOUBLE_VEC2, 16#8FFC).
+-define(GL_DOUBLE_VEC3, 16#8FFD).
+-define(GL_DOUBLE_VEC4, 16#8FFE).
+-define(GL_DOUBLE_MAT2, 16#8F46).
+-define(GL_DOUBLE_MAT3, 16#8F47).
+-define(GL_DOUBLE_MAT4, 16#8F48).
+-define(GL_DOUBLE_MAT2x3, 16#8F49).
+-define(GL_DOUBLE_MAT2x4, 16#8F4A).
+-define(GL_DOUBLE_MAT3x2, 16#8F4B).
+-define(GL_DOUBLE_MAT3x4, 16#8F4C).
+-define(GL_DOUBLE_MAT4x2, 16#8F4D).
+-define(GL_DOUBLE_MAT4x3, 16#8F4E).
+-define(GL_ACTIVE_SUBROUTINES, 16#8DE5).
+-define(GL_ACTIVE_SUBROUTINE_UNIFORMS, 16#8DE6).
+-define(GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, 16#8E47).
+-define(GL_ACTIVE_SUBROUTINE_MAX_LENGTH, 16#8E48).
+-define(GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH, 16#8E49).
+-define(GL_MAX_SUBROUTINES, 16#8DE7).
+-define(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, 16#8DE8).
+-define(GL_NUM_COMPATIBLE_SUBROUTINES, 16#8E4A).
+-define(GL_COMPATIBLE_SUBROUTINES, 16#8E4B).
+-define(GL_PATCHES, 16#E).
+-define(GL_PATCH_VERTICES, 16#8E72).
+-define(GL_PATCH_DEFAULT_INNER_LEVEL, 16#8E73).
+-define(GL_PATCH_DEFAULT_OUTER_LEVEL, 16#8E74).
+-define(GL_TESS_CONTROL_OUTPUT_VERTICES, 16#8E75).
+-define(GL_TESS_GEN_MODE, 16#8E76).
+-define(GL_TESS_GEN_SPACING, 16#8E77).
+-define(GL_TESS_GEN_VERTEX_ORDER, 16#8E78).
+-define(GL_TESS_GEN_POINT_MODE, 16#8E79).
+-define(GL_ISOLINES, 16#8E7A).
+-define(GL_FRACTIONAL_ODD, 16#8E7B).
+-define(GL_FRACTIONAL_EVEN, 16#8E7C).
+-define(GL_MAX_PATCH_VERTICES, 16#8E7D).
+-define(GL_MAX_TESS_GEN_LEVEL, 16#8E7E).
+-define(GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS, 16#8E7F).
+-define(GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS, 16#8E80).
+-define(GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS, 16#8E81).
+-define(GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS, 16#8E82).
+-define(GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS, 16#8E83).
+-define(GL_MAX_TESS_PATCH_COMPONENTS, 16#8E84).
+-define(GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS, 16#8E85).
+-define(GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS, 16#8E86).
+-define(GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS, 16#8E89).
+-define(GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS, 16#8E8A).
+-define(GL_MAX_TESS_CONTROL_INPUT_COMPONENTS, 16#886C).
+-define(GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS, 16#886D).
+-define(GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS, 16#8E1E).
+-define(GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS, 16#8E1F).
+-define(GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER, 16#84F0).
+-define(GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER, 16#84F1).
+-define(GL_TESS_EVALUATION_SHADER, 16#8E87).
+-define(GL_TESS_CONTROL_SHADER, 16#8E88).
+-define(GL_TRANSFORM_FEEDBACK, 16#8E22).
+-define(GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED, 16#8E23).
+-define(GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE, 16#8E24).
+-define(GL_TRANSFORM_FEEDBACK_BINDING, 16#8E25).
+-define(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, 16#8E70).
+-define(GL_MAX_VERTEX_STREAMS, 16#8E71).
+-define(GL_FIXED, 16#140C).
+-define(GL_IMPLEMENTATION_COLOR_READ_TYPE, 16#8B9A).
+-define(GL_IMPLEMENTATION_COLOR_READ_FORMAT, 16#8B9B).
+-define(GL_LOW_FLOAT, 16#8DF0).
+-define(GL_MEDIUM_FLOAT, 16#8DF1).
+-define(GL_HIGH_FLOAT, 16#8DF2).
+-define(GL_LOW_INT, 16#8DF3).
+-define(GL_MEDIUM_INT, 16#8DF4).
+-define(GL_HIGH_INT, 16#8DF5).
+-define(GL_SHADER_COMPILER, 16#8DFA).
+-define(GL_NUM_SHADER_BINARY_FORMATS, 16#8DF9).
+-define(GL_MAX_VERTEX_UNIFORM_VECTORS, 16#8DFB).
+-define(GL_MAX_VARYING_VECTORS, 16#8DFC).
+-define(GL_MAX_FRAGMENT_UNIFORM_VECTORS, 16#8DFD).
+-define(GL_PROGRAM_BINARY_RETRIEVABLE_HINT, 16#8257).
+-define(GL_PROGRAM_BINARY_LENGTH, 16#8741).
+-define(GL_NUM_PROGRAM_BINARY_FORMATS, 16#87FE).
+-define(GL_PROGRAM_BINARY_FORMATS, 16#87FF).
+-define(GL_VERTEX_SHADER_BIT, 16#1).
+-define(GL_FRAGMENT_SHADER_BIT, 16#2).
+-define(GL_GEOMETRY_SHADER_BIT, 16#4).
+-define(GL_TESS_CONTROL_SHADER_BIT, 16#8).
+-define(GL_TESS_EVALUATION_SHADER_BIT, 16#10).
+-define(GL_ALL_SHADER_BITS, 16#FFFFFFFF).
+-define(GL_PROGRAM_SEPARABLE, 16#8258).
+-define(GL_ACTIVE_PROGRAM, 16#8259).
+-define(GL_PROGRAM_PIPELINE_BINDING, 16#825A).
+-define(GL_MAX_VIEWPORTS, 16#825B).
+-define(GL_VIEWPORT_SUBPIXEL_BITS, 16#825C).
+-define(GL_VIEWPORT_BOUNDS_RANGE, 16#825D).
+-define(GL_LAYER_PROVOKING_VERTEX, 16#825E).
+-define(GL_VIEWPORT_INDEX_PROVOKING_VERTEX, 16#825F).
+-define(GL_UNDEFINED_VERTEX, 16#8260).
+-define(GL_SYNC_CL_EVENT_ARB, 16#8240).
+-define(GL_SYNC_CL_EVENT_COMPLETE_ARB, 16#8241).
+-define(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB, 16#8242).
+-define(GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB, 16#8243).
+-define(GL_DEBUG_CALLBACK_FUNCTION_ARB, 16#8244).
+-define(GL_DEBUG_CALLBACK_USER_PARAM_ARB, 16#8245).
+-define(GL_DEBUG_SOURCE_API_ARB, 16#8246).
+-define(GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB, 16#8247).
+-define(GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, 16#8248).
+-define(GL_DEBUG_SOURCE_THIRD_PARTY_ARB, 16#8249).
+-define(GL_DEBUG_SOURCE_APPLICATION_ARB, 16#824A).
+-define(GL_DEBUG_SOURCE_OTHER_ARB, 16#824B).
+-define(GL_DEBUG_TYPE_ERROR_ARB, 16#824C).
+-define(GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB, 16#824D).
+-define(GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB, 16#824E).
+-define(GL_DEBUG_TYPE_PORTABILITY_ARB, 16#824F).
+-define(GL_DEBUG_TYPE_PERFORMANCE_ARB, 16#8250).
+-define(GL_DEBUG_TYPE_OTHER_ARB, 16#8251).
+-define(GL_MAX_DEBUG_MESSAGE_LENGTH_ARB, 16#9143).
+-define(GL_MAX_DEBUG_LOGGED_MESSAGES_ARB, 16#9144).
+-define(GL_DEBUG_LOGGED_MESSAGES_ARB, 16#9145).
+-define(GL_DEBUG_SEVERITY_HIGH_ARB, 16#9146).
+-define(GL_DEBUG_SEVERITY_MEDIUM_ARB, 16#9147).
+-define(GL_DEBUG_SEVERITY_LOW_ARB, 16#9148).
+-define(GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, 16#4).
+-define(GL_LOSE_CONTEXT_ON_RESET_ARB, 16#8252).
+-define(GL_GUILTY_CONTEXT_RESET_ARB, 16#8253).
+-define(GL_INNOCENT_CONTEXT_RESET_ARB, 16#8254).
+-define(GL_UNKNOWN_CONTEXT_RESET_ARB, 16#8255).
+-define(GL_RESET_NOTIFICATION_STRATEGY_ARB, 16#8256).
+-define(GL_NO_RESET_NOTIFICATION_ARB, 16#8261).
-define(GL_CONSTANT_COLOR_EXT, 16#8001).
-define(GL_ONE_MINUS_CONSTANT_COLOR_EXT, 16#8002).
-define(GL_CONSTANT_ALPHA_EXT, 16#8003).
@@ -2921,9 +3165,9 @@
-define(GL_ACTIVE_STENCIL_FACE_EXT, 16#8911).
-define(GL_TEXT_FRAGMENT_SHADER_ATI, 16#8200).
-define(GL_UNPACK_CLIENT_STORAGE_APPLE, 16#85B2).
--define(GL_ELEMENT_ARRAY_APPLE, 16#8768).
--define(GL_ELEMENT_ARRAY_TYPE_APPLE, 16#8769).
--define(GL_ELEMENT_ARRAY_POINTER_APPLE, 16#876A).
+-define(GL_ELEMENT_ARRAY_APPLE, 16#8A0C).
+-define(GL_ELEMENT_ARRAY_TYPE_APPLE, 16#8A0D).
+-define(GL_ELEMENT_ARRAY_POINTER_APPLE, 16#8A0E).
-define(GL_DRAW_PIXELS_APPLE, 16#8A0A).
-define(GL_FENCE_APPLE, 16#8A0B).
-define(GL_VERTEX_ARRAY_BINDING_APPLE, 16#85B5).
@@ -2931,6 +3175,7 @@
-define(GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE, 16#851E).
-define(GL_VERTEX_ARRAY_STORAGE_HINT_APPLE, 16#851F).
-define(GL_VERTEX_ARRAY_RANGE_POINTER_APPLE, 16#8521).
+-define(GL_STORAGE_CLIENT_APPLE, 16#85B4).
-define(GL_STORAGE_CACHED_APPLE, 16#85BE).
-define(GL_STORAGE_SHARED_APPLE, 16#85BF).
-define(GL_YCBCR_422_APPLE, 16#85B9).
@@ -3244,6 +3489,12 @@
-define(GL_SEPARATE_ATTRIBS_NV, 16#8C8D).
-define(GL_TRANSFORM_FEEDBACK_BUFFER_NV, 16#8C8E).
-define(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV, 16#8C8F).
+-define(GL_LAYER_NV, 16#8DAA).
+-define(GL_NEXT_BUFFER_NV, -2).
+-define(GL_SKIP_COMPONENTS4_NV, -3).
+-define(GL_SKIP_COMPONENTS3_NV, -4).
+-define(GL_SKIP_COMPONENTS2_NV, -5).
+-define(GL_SKIP_COMPONENTS1_NV, -6).
-define(GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT, 16#8DE2).
-define(GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT, 16#8DE3).
-define(GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT, 16#8DE4).
@@ -3335,11 +3586,11 @@
-define(GL_SAMPLE_MASK_VALUE_NV, 16#8E52).
-define(GL_TEXTURE_BINDING_RENDERBUFFER_NV, 16#8E53).
-define(GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV, 16#8E54).
--define(GL_MAX_SAMPLE_MASK_WORDS_NV, 16#8E59).
-define(GL_TEXTURE_RENDERBUFFER_NV, 16#8E55).
-define(GL_SAMPLER_RENDERBUFFER_NV, 16#8E56).
-define(GL_INT_SAMPLER_RENDERBUFFER_NV, 16#8E57).
-define(GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV, 16#8E58).
+-define(GL_MAX_SAMPLE_MASK_WORDS_NV, 16#8E59).
-define(GL_TRANSFORM_FEEDBACK_NV, 16#8E22).
-define(GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV, 16#8E23).
-define(GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV, 16#8E24).
@@ -3365,6 +3616,247 @@
-define(GL_FIRST_VERTEX_CONVENTION_EXT, 16#8E4D).
-define(GL_LAST_VERTEX_CONVENTION_EXT, 16#8E4E).
-define(GL_PROVOKING_VERTEX_EXT, 16#8E4F).
+-define(GL_ALPHA_SNORM, 16#9010).
+-define(GL_LUMINANCE_SNORM, 16#9011).
+-define(GL_LUMINANCE_ALPHA_SNORM, 16#9012).
+-define(GL_INTENSITY_SNORM, 16#9013).
+-define(GL_ALPHA8_SNORM, 16#9014).
+-define(GL_LUMINANCE8_SNORM, 16#9015).
+-define(GL_LUMINANCE8_ALPHA8_SNORM, 16#9016).
+-define(GL_INTENSITY8_SNORM, 16#9017).
+-define(GL_ALPHA16_SNORM, 16#9018).
+-define(GL_LUMINANCE16_SNORM, 16#9019).
+-define(GL_LUMINANCE16_ALPHA16_SNORM, 16#901A).
+-define(GL_INTENSITY16_SNORM, 16#901B).
+-define(GL_TEXTURE_RANGE_LENGTH_APPLE, 16#85B7).
+-define(GL_TEXTURE_RANGE_POINTER_APPLE, 16#85B8).
+-define(GL_TEXTURE_STORAGE_HINT_APPLE, 16#85BC).
+-define(GL_STORAGE_PRIVATE_APPLE, 16#85BD).
+-define(GL_HALF_APPLE, 16#140B).
+-define(GL_RGBA_FLOAT32_APPLE, 16#8814).
+-define(GL_RGB_FLOAT32_APPLE, 16#8815).
+-define(GL_ALPHA_FLOAT32_APPLE, 16#8816).
+-define(GL_INTENSITY_FLOAT32_APPLE, 16#8817).
+-define(GL_LUMINANCE_FLOAT32_APPLE, 16#8818).
+-define(GL_LUMINANCE_ALPHA_FLOAT32_APPLE, 16#8819).
+-define(GL_RGBA_FLOAT16_APPLE, 16#881A).
+-define(GL_RGB_FLOAT16_APPLE, 16#881B).
+-define(GL_ALPHA_FLOAT16_APPLE, 16#881C).
+-define(GL_INTENSITY_FLOAT16_APPLE, 16#881D).
+-define(GL_LUMINANCE_FLOAT16_APPLE, 16#881E).
+-define(GL_LUMINANCE_ALPHA_FLOAT16_APPLE, 16#881F).
+-define(GL_COLOR_FLOAT_APPLE, 16#8A0F).
+-define(GL_VERTEX_ATTRIB_MAP1_APPLE, 16#8A00).
+-define(GL_VERTEX_ATTRIB_MAP2_APPLE, 16#8A01).
+-define(GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE, 16#8A02).
+-define(GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE, 16#8A03).
+-define(GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE, 16#8A04).
+-define(GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE, 16#8A05).
+-define(GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE, 16#8A06).
+-define(GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE, 16#8A07).
+-define(GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE, 16#8A08).
+-define(GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE, 16#8A09).
+-define(GL_AUX_DEPTH_STENCIL_APPLE, 16#8A14).
+-define(GL_BUFFER_OBJECT_APPLE, 16#85B3).
+-define(GL_RELEASED_APPLE, 16#8A19).
+-define(GL_VOLATILE_APPLE, 16#8A1A).
+-define(GL_RETAINED_APPLE, 16#8A1B).
+-define(GL_UNDEFINED_APPLE, 16#8A1C).
+-define(GL_PURGEABLE_APPLE, 16#8A1D).
+-define(GL_PACK_ROW_BYTES_APPLE, 16#8A15).
+-define(GL_UNPACK_ROW_BYTES_APPLE, 16#8A16).
+-define(GL_RGB_422_APPLE, 16#8A1F).
+-define(GL_VIDEO_BUFFER_NV, 16#9020).
+-define(GL_VIDEO_BUFFER_BINDING_NV, 16#9021).
+-define(GL_FIELD_UPPER_NV, 16#9022).
+-define(GL_FIELD_LOWER_NV, 16#9023).
+-define(GL_NUM_VIDEO_CAPTURE_STREAMS_NV, 16#9024).
+-define(GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV, 16#9025).
+-define(GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV, 16#9026).
+-define(GL_LAST_VIDEO_CAPTURE_STATUS_NV, 16#9027).
+-define(GL_VIDEO_BUFFER_PITCH_NV, 16#9028).
+-define(GL_VIDEO_COLOR_CONVERSION_MATRIX_NV, 16#9029).
+-define(GL_VIDEO_COLOR_CONVERSION_MAX_NV, 16#902A).
+-define(GL_VIDEO_COLOR_CONVERSION_MIN_NV, 16#902B).
+-define(GL_VIDEO_COLOR_CONVERSION_OFFSET_NV, 16#902C).
+-define(GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV, 16#902D).
+-define(GL_PARTIAL_SUCCESS_NV, 16#902E).
+-define(GL_SUCCESS_NV, 16#902F).
+-define(GL_FAILURE_NV, 16#9030).
+-define(GL_YCBYCR8_422_NV, 16#9031).
+-define(GL_YCBAYCR8A_4224_NV, 16#9032).
+-define(GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV, 16#9033).
+-define(GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV, 16#9034).
+-define(GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV, 16#9035).
+-define(GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV, 16#9036).
+-define(GL_Z4Y12Z4CB12Z4CR12_444_NV, 16#9037).
+-define(GL_VIDEO_CAPTURE_FRAME_WIDTH_NV, 16#9038).
+-define(GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV, 16#9039).
+-define(GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV, 16#903A).
+-define(GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV, 16#903B).
+-define(GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV, 16#903C).
+-define(GL_ACTIVE_PROGRAM_EXT, 16#8B8D).
+-define(GL_BUFFER_GPU_ADDRESS_NV, 16#8F1D).
+-define(GL_GPU_ADDRESS_NV, 16#8F34).
+-define(GL_MAX_SHADER_BUFFER_ADDRESS_NV, 16#8F35).
+-define(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV, 16#8F1E).
+-define(GL_ELEMENT_ARRAY_UNIFIED_NV, 16#8F1F).
+-define(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 16#8F20).
+-define(GL_VERTEX_ARRAY_ADDRESS_NV, 16#8F21).
+-define(GL_NORMAL_ARRAY_ADDRESS_NV, 16#8F22).
+-define(GL_COLOR_ARRAY_ADDRESS_NV, 16#8F23).
+-define(GL_INDEX_ARRAY_ADDRESS_NV, 16#8F24).
+-define(GL_TEXTURE_COORD_ARRAY_ADDRESS_NV, 16#8F25).
+-define(GL_EDGE_FLAG_ARRAY_ADDRESS_NV, 16#8F26).
+-define(GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV, 16#8F27).
+-define(GL_FOG_COORD_ARRAY_ADDRESS_NV, 16#8F28).
+-define(GL_ELEMENT_ARRAY_ADDRESS_NV, 16#8F29).
+-define(GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV, 16#8F2A).
+-define(GL_VERTEX_ARRAY_LENGTH_NV, 16#8F2B).
+-define(GL_NORMAL_ARRAY_LENGTH_NV, 16#8F2C).
+-define(GL_COLOR_ARRAY_LENGTH_NV, 16#8F2D).
+-define(GL_INDEX_ARRAY_LENGTH_NV, 16#8F2E).
+-define(GL_TEXTURE_COORD_ARRAY_LENGTH_NV, 16#8F2F).
+-define(GL_EDGE_FLAG_ARRAY_LENGTH_NV, 16#8F30).
+-define(GL_SECONDARY_COLOR_ARRAY_LENGTH_NV, 16#8F31).
+-define(GL_FOG_COORD_ARRAY_LENGTH_NV, 16#8F32).
+-define(GL_ELEMENT_ARRAY_LENGTH_NV, 16#8F33).
+-define(GL_DRAW_INDIRECT_UNIFIED_NV, 16#8F40).
+-define(GL_DRAW_INDIRECT_ADDRESS_NV, 16#8F41).
+-define(GL_DRAW_INDIRECT_LENGTH_NV, 16#8F42).
+-define(GL_MAX_IMAGE_UNITS_EXT, 16#8F38).
+-define(GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT, 16#8F39).
+-define(GL_IMAGE_BINDING_NAME_EXT, 16#8F3A).
+-define(GL_IMAGE_BINDING_LEVEL_EXT, 16#8F3B).
+-define(GL_IMAGE_BINDING_LAYERED_EXT, 16#8F3C).
+-define(GL_IMAGE_BINDING_LAYER_EXT, 16#8F3D).
+-define(GL_IMAGE_BINDING_ACCESS_EXT, 16#8F3E).
+-define(GL_IMAGE_1D_EXT, 16#904C).
+-define(GL_IMAGE_2D_EXT, 16#904D).
+-define(GL_IMAGE_3D_EXT, 16#904E).
+-define(GL_IMAGE_2D_RECT_EXT, 16#904F).
+-define(GL_IMAGE_CUBE_EXT, 16#9050).
+-define(GL_IMAGE_BUFFER_EXT, 16#9051).
+-define(GL_IMAGE_1D_ARRAY_EXT, 16#9052).
+-define(GL_IMAGE_2D_ARRAY_EXT, 16#9053).
+-define(GL_IMAGE_CUBE_MAP_ARRAY_EXT, 16#9054).
+-define(GL_IMAGE_2D_MULTISAMPLE_EXT, 16#9055).
+-define(GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT, 16#9056).
+-define(GL_INT_IMAGE_1D_EXT, 16#9057).
+-define(GL_INT_IMAGE_2D_EXT, 16#9058).
+-define(GL_INT_IMAGE_3D_EXT, 16#9059).
+-define(GL_INT_IMAGE_2D_RECT_EXT, 16#905A).
+-define(GL_INT_IMAGE_CUBE_EXT, 16#905B).
+-define(GL_INT_IMAGE_BUFFER_EXT, 16#905C).
+-define(GL_INT_IMAGE_1D_ARRAY_EXT, 16#905D).
+-define(GL_INT_IMAGE_2D_ARRAY_EXT, 16#905E).
+-define(GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT, 16#905F).
+-define(GL_INT_IMAGE_2D_MULTISAMPLE_EXT, 16#9060).
+-define(GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT, 16#9061).
+-define(GL_UNSIGNED_INT_IMAGE_1D_EXT, 16#9062).
+-define(GL_UNSIGNED_INT_IMAGE_2D_EXT, 16#9063).
+-define(GL_UNSIGNED_INT_IMAGE_3D_EXT, 16#9064).
+-define(GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT, 16#9065).
+-define(GL_UNSIGNED_INT_IMAGE_CUBE_EXT, 16#9066).
+-define(GL_UNSIGNED_INT_IMAGE_BUFFER_EXT, 16#9067).
+-define(GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT, 16#9068).
+-define(GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT, 16#9069).
+-define(GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT, 16#906A).
+-define(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT, 16#906B).
+-define(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT, 16#906C).
+-define(GL_MAX_IMAGE_SAMPLES_EXT, 16#906D).
+-define(GL_IMAGE_BINDING_FORMAT_EXT, 16#906E).
+-define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT, 16#1).
+-define(GL_ELEMENT_ARRAY_BARRIER_BIT_EXT, 16#2).
+-define(GL_UNIFORM_BARRIER_BIT_EXT, 16#4).
+-define(GL_TEXTURE_FETCH_BARRIER_BIT_EXT, 16#8).
+-define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT, 16#20).
+-define(GL_COMMAND_BARRIER_BIT_EXT, 16#40).
+-define(GL_PIXEL_BUFFER_BARRIER_BIT_EXT, 16#80).
+-define(GL_TEXTURE_UPDATE_BARRIER_BIT_EXT, 16#100).
+-define(GL_BUFFER_UPDATE_BARRIER_BIT_EXT, 16#200).
+-define(GL_FRAMEBUFFER_BARRIER_BIT_EXT, 16#400).
+-define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT, 16#800).
+-define(GL_ATOMIC_COUNTER_BARRIER_BIT_EXT, 16#1000).
+-define(GL_ALL_BARRIER_BITS_EXT, 16#FFFFFFFF).
+-define(GL_DOUBLE_VEC2_EXT, 16#8FFC).
+-define(GL_DOUBLE_VEC3_EXT, 16#8FFD).
+-define(GL_DOUBLE_VEC4_EXT, 16#8FFE).
+-define(GL_DOUBLE_MAT2_EXT, 16#8F46).
+-define(GL_DOUBLE_MAT3_EXT, 16#8F47).
+-define(GL_DOUBLE_MAT4_EXT, 16#8F48).
+-define(GL_DOUBLE_MAT2x3_EXT, 16#8F49).
+-define(GL_DOUBLE_MAT2x4_EXT, 16#8F4A).
+-define(GL_DOUBLE_MAT3x2_EXT, 16#8F4B).
+-define(GL_DOUBLE_MAT3x4_EXT, 16#8F4C).
+-define(GL_DOUBLE_MAT4x2_EXT, 16#8F4D).
+-define(GL_DOUBLE_MAT4x3_EXT, 16#8F4E).
+-define(GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV, 16#8E5A).
+-define(GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV, 16#8E5B).
+-define(GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV, 16#8E5C).
+-define(GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV, 16#8E5D).
+-define(GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV, 16#8E5E).
+-define(GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV, 16#8E5F).
+-define(GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV, 16#8F44).
+-define(GL_MAX_PROGRAM_SUBROUTINE_NUM_NV, 16#8F45).
+-define(GL_INT64_NV, 16#140E).
+-define(GL_UNSIGNED_INT64_NV, 16#140F).
+-define(GL_INT8_NV, 16#8FE0).
+-define(GL_INT8_VEC2_NV, 16#8FE1).
+-define(GL_INT8_VEC3_NV, 16#8FE2).
+-define(GL_INT8_VEC4_NV, 16#8FE3).
+-define(GL_INT16_NV, 16#8FE4).
+-define(GL_INT16_VEC2_NV, 16#8FE5).
+-define(GL_INT16_VEC3_NV, 16#8FE6).
+-define(GL_INT16_VEC4_NV, 16#8FE7).
+-define(GL_INT64_VEC2_NV, 16#8FE9).
+-define(GL_INT64_VEC3_NV, 16#8FEA).
+-define(GL_INT64_VEC4_NV, 16#8FEB).
+-define(GL_UNSIGNED_INT8_NV, 16#8FEC).
+-define(GL_UNSIGNED_INT8_VEC2_NV, 16#8FED).
+-define(GL_UNSIGNED_INT8_VEC3_NV, 16#8FEE).
+-define(GL_UNSIGNED_INT8_VEC4_NV, 16#8FEF).
+-define(GL_UNSIGNED_INT16_NV, 16#8FF0).
+-define(GL_UNSIGNED_INT16_VEC2_NV, 16#8FF1).
+-define(GL_UNSIGNED_INT16_VEC3_NV, 16#8FF2).
+-define(GL_UNSIGNED_INT16_VEC4_NV, 16#8FF3).
+-define(GL_UNSIGNED_INT64_VEC2_NV, 16#8FF5).
+-define(GL_UNSIGNED_INT64_VEC3_NV, 16#8FF6).
+-define(GL_UNSIGNED_INT64_VEC4_NV, 16#8FF7).
+-define(GL_FLOAT16_NV, 16#8FF8).
+-define(GL_FLOAT16_VEC2_NV, 16#8FF9).
+-define(GL_FLOAT16_VEC3_NV, 16#8FFA).
+-define(GL_FLOAT16_VEC4_NV, 16#8FFB).
+-define(GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV, 16#10).
+-define(GL_MAX_PROGRAM_PATCH_ATTRIBS_NV, 16#86D8).
+-define(GL_TESS_CONTROL_PROGRAM_NV, 16#891E).
+-define(GL_TESS_EVALUATION_PROGRAM_NV, 16#891F).
+-define(GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV, 16#8C74).
+-define(GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, 16#8C75).
+-define(GL_COVERAGE_SAMPLES_NV, 16#80A9).
+-define(GL_COLOR_SAMPLES_NV, 16#8E20).
+-define(GL_DATA_BUFFER_AMD, 16#9151).
+-define(GL_PERFORMANCE_MONITOR_AMD, 16#9152).
+-define(GL_QUERY_OBJECT_AMD, 16#9153).
+-define(GL_VERTEX_ARRAY_OBJECT_AMD, 16#9154).
+-define(GL_SAMPLER_OBJECT_AMD, 16#9155).
+-define(GL_MAX_DEBUG_LOGGED_MESSAGES_AMD, 16#9144).
+-define(GL_DEBUG_LOGGED_MESSAGES_AMD, 16#9145).
+-define(GL_DEBUG_SEVERITY_HIGH_AMD, 16#9146).
+-define(GL_DEBUG_SEVERITY_MEDIUM_AMD, 16#9147).
+-define(GL_DEBUG_SEVERITY_LOW_AMD, 16#9148).
+-define(GL_DEBUG_CATEGORY_API_ERROR_AMD, 16#9149).
+-define(GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD, 16#914A).
+-define(GL_DEBUG_CATEGORY_DEPRECATION_AMD, 16#914B).
+-define(GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD, 16#914C).
+-define(GL_DEBUG_CATEGORY_PERFORMANCE_AMD, 16#914D).
+-define(GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD, 16#914E).
+-define(GL_DEBUG_CATEGORY_APPLICATION_AMD, 16#914F).
+-define(GL_DEBUG_CATEGORY_OTHER_AMD, 16#9150).
+-define(GL_SURFACE_STATE_NV, 16#86EB).
+-define(GL_SURFACE_REGISTERED_NV, 16#86FD).
+-define(GL_SURFACE_MAPPED_NV, 16#8700).
+-define(GL_WRITE_DISCARD_NV, 16#88BE).
-define(GL_VERSION_1_2, 1).
-define(GL_VERSION_1_2_DEPRECATED, 1).
-define(GL_VERSION_1_3, 1).
@@ -3375,8 +3867,11 @@
-define(GL_VERSION_2_0, 1).
-define(GL_VERSION_2_1, 1).
-define(GL_VERSION_3_0, 1).
--define(GL_VERSION_3_0_DEPRECATED, 1).
-define(GL_VERSION_3_1, 1).
+-define(GL_VERSION_3_2, 1).
+-define(GL_VERSION_3_3, 1).
+-define(GL_VERSION_4_0, 1).
+-define(GL_VERSION_4_1, 1).
-define(GL_ARB_multitexture, 1).
-define(GL_ARB_transpose_matrix, 1).
-define(GL_ARB_multisample, 1).
@@ -3428,6 +3923,46 @@
-define(GL_ARB_compatibility, 1).
-define(GL_ARB_copy_buffer, 1).
-define(GL_ARB_shader_texture_lod, 1).
+-define(GL_ARB_depth_clamp, 1).
+-define(GL_ARB_draw_elements_base_vertex, 1).
+-define(GL_ARB_fragment_coord_conventions, 1).
+-define(GL_ARB_provoking_vertex, 1).
+-define(GL_ARB_seamless_cube_map, 1).
+-define(GL_ARB_sync, 1).
+-define(GL_ARB_texture_multisample, 1).
+-define(GL_ARB_vertex_array_bgra, 1).
+-define(GL_ARB_draw_buffers_blend, 1).
+-define(GL_ARB_sample_shading, 1).
+-define(GL_ARB_texture_cube_map_array, 1).
+-define(GL_ARB_texture_gather, 1).
+-define(GL_ARB_texture_query_lod, 1).
+-define(GL_ARB_shading_language_include, 1).
+-define(GL_ARB_texture_compression_bptc, 1).
+-define(GL_ARB_blend_func_extended, 1).
+-define(GL_ARB_explicit_attrib_location, 1).
+-define(GL_ARB_occlusion_query2, 1).
+-define(GL_ARB_sampler_objects, 1).
+-define(GL_ARB_texture_rgb10_a2ui, 1).
+-define(GL_ARB_texture_swizzle, 1).
+-define(GL_ARB_timer_query, 1).
+-define(GL_ARB_vertex_type_2_10_10_10_rev, 1).
+-define(GL_ARB_draw_indirect, 1).
+-define(GL_ARB_gpu_shader5, 1).
+-define(GL_ARB_gpu_shader_fp64, 1).
+-define(GL_ARB_shader_subroutine, 1).
+-define(GL_ARB_tessellation_shader, 1).
+-define(GL_ARB_texture_buffer_object_rgb32, 1).
+-define(GL_ARB_transform_feedback2, 1).
+-define(GL_ARB_transform_feedback3, 1).
+-define(GL_ARB_ES2_compatibility, 1).
+-define(GL_ARB_get_program_binary, 1).
+-define(GL_ARB_separate_shader_objects, 1).
+-define(GL_ARB_vertex_attrib_64bit, 1).
+-define(GL_ARB_viewport_array, 1).
+-define(GL_ARB_cl_event, 1).
+-define(GL_ARB_debug_output, 1).
+-define(GL_ARB_robustness, 1).
+-define(GL_ARB_shader_stencil_export, 1).
-define(GL_EXT_abgr, 1).
-define(GL_EXT_blend_color, 1).
-define(GL_EXT_polygon_offset, 1).
@@ -3684,3 +4219,34 @@
-define(GL_AMD_texture_texture4, 1).
-define(GL_AMD_vertex_shader_tesselator, 1).
-define(GL_EXT_provoking_vertex, 1).
+-define(GL_EXT_texture_snorm, 1).
+-define(GL_AMD_draw_buffers_blend, 1).
+-define(GL_APPLE_texture_range, 1).
+-define(GL_APPLE_float_pixels, 1).
+-define(GL_APPLE_vertex_program_evaluators, 1).
+-define(GL_APPLE_aux_depth_stencil, 1).
+-define(GL_APPLE_object_purgeable, 1).
+-define(GL_APPLE_row_bytes, 1).
+-define(GL_APPLE_rgb_422, 1).
+-define(GL_NV_video_capture, 1).
+-define(GL_NV_copy_image, 1).
+-define(GL_EXT_separate_shader_objects, 1).
+-define(GL_NV_parameter_buffer_object2, 1).
+-define(GL_NV_shader_buffer_load, 1).
+-define(GL_NV_vertex_buffer_unified_memory, 1).
+-define(GL_NV_texture_barrier, 1).
+-define(GL_AMD_shader_stencil_export, 1).
+-define(GL_AMD_seamless_cubemap_per_texture, 1).
+-define(GL_AMD_conservative_depth, 1).
+-define(GL_EXT_shader_image_load_store, 1).
+-define(GL_EXT_vertex_attrib_64bit, 1).
+-define(GL_NV_gpu_program5, 1).
+-define(GL_NV_gpu_shader5, 1).
+-define(GL_NV_shader_buffer_store, 1).
+-define(GL_NV_tessellation_program5, 1).
+-define(GL_NV_vertex_attrib_integer_64bit, 1).
+-define(GL_NV_multisample_coverage, 1).
+-define(GL_AMD_name_gen_delete, 1).
+-define(GL_AMD_debug_output, 1).
+-define(GL_NV_vdpau_interop, 1).
+-define(GL_AMD_transform_feedback3_lines_triangles, 1).
diff --git a/lib/wx/src/Makefile b/lib/wx/src/Makefile
index a9fd468959..3cc668375f 100644
--- a/lib/wx/src/Makefile
+++ b/lib/wx/src/Makefile
@@ -47,7 +47,6 @@ GEN_FILES = $(wildcard gen/wx*.erl) \
GEN_MODS = $(GEN_FILES:gen/%.erl= %,\n )
GEN_HRL = \
- $(EGEN)/gl_debug.hrl \
$(EGEN)/wxe_debug.hrl \
$(EGEN)/wxe_funcs.hrl
diff --git a/lib/wx/src/gen/gl.erl b/lib/wx/src/gen/gl.erl
index 62d0ff6aed..0ebf51d28a 100644
--- a/lib/wx/src/gen/gl.erl
+++ b/lib/wx/src/gen/gl.erl
@@ -25,14 +25,13 @@
%%
%% Booleans are represented by integers 0 and 1.
-%% @type wx_mem(). see wx.erl on memory allocation functions
+%% @type mem(). memory block
%% @type enum(). An integer defined in gl.hrl
%% @type offset(). An integer which is an offset in an array
%% @type clamp(). A float clamped between 0.0 - 1.0
-module(gl).
-compile(inline).
--include("wxe.hrl").
-define(GLenum,32/native-unsigned).
-define(GLboolean,8/native-unsigned).
-define(GLbitfield,32/native-unsigned).
@@ -51,6 +50,13 @@
-define(GLintptr,64/native-unsigned).
-define(GLUquadric,64/native-unsigned).
-define(GLhandleARB,64/native-unsigned).
+-define(GLsync,64/native-unsigned).
+-define(GLuint64,64/native-unsigned).
+-define(GLint64,64/native-signed).
+-type clamp() :: float().
+-type offset() :: non_neg_integer().
+-type enum() :: non_neg_integer().
+-type mem() :: binary() | tuple().
-export([accum/2,alphaFunc/2,areTexturesResident/1,arrayElement/1,'begin'/1,
bindTexture/2,bitmap/7,blendFunc/2,callList/1,callLists/1,clear/1,clearAccum/4,
@@ -173,3862 +179,5840 @@
enablei/2,disablei/2,isEnabledi/2,beginTransformFeedback/1,endTransformFeedback/0,
bindBufferRange/5,bindBufferBase/3,transformFeedbackVaryings/3,getTransformFeedbackVarying/3,
clampColor/2,beginConditionalRender/2,endConditionalRender/0,vertexAttribIPointer/5,
- getVertexAttribIiv/2,getVertexAttribIuiv/2,getUniformuiv/2,bindFragDataLocation/3,
+ getVertexAttribIiv/2,getVertexAttribIuiv/2,vertexAttribI1i/2,vertexAttribI2i/3,
+ vertexAttribI3i/4,vertexAttribI4i/5,vertexAttribI1ui/2,vertexAttribI2ui/3,
+ vertexAttribI3ui/4,vertexAttribI4ui/5,vertexAttribI1iv/2,vertexAttribI2iv/2,
+ vertexAttribI3iv/2,vertexAttribI4iv/2,vertexAttribI1uiv/2,vertexAttribI2uiv/2,
+ vertexAttribI3uiv/2,vertexAttribI4uiv/2,vertexAttribI4bv/2,vertexAttribI4sv/2,
+ vertexAttribI4ubv/2,vertexAttribI4usv/2,getUniformuiv/2,bindFragDataLocation/3,
getFragDataLocation/2,uniform1ui/2,uniform2ui/3,uniform3ui/4,uniform4ui/5,
uniform1uiv/2,uniform2uiv/2,uniform3uiv/2,uniform4uiv/2,texParameterIiv/3,
texParameterIuiv/3,getTexParameterIiv/2,getTexParameterIuiv/2,clearBufferiv/3,
- clearBufferuiv/3,clearBufferfv/3,clearBufferfi/4,getStringi/2,vertexAttribI1i/2,
- vertexAttribI2i/3,vertexAttribI3i/4,vertexAttribI4i/5,vertexAttribI1ui/2,
- vertexAttribI2ui/3,vertexAttribI3ui/4,vertexAttribI4ui/5,vertexAttribI1iv/2,
- vertexAttribI2iv/2,vertexAttribI3iv/2,vertexAttribI4iv/2,vertexAttribI1uiv/2,
- vertexAttribI2uiv/2,vertexAttribI3uiv/2,vertexAttribI4uiv/2,vertexAttribI4bv/2,
- vertexAttribI4sv/2,vertexAttribI4ubv/2,vertexAttribI4usv/2,drawArraysInstanced/4,
- drawElementsInstanced/5,texBuffer/3,primitiveRestartIndex/1,loadTransposeMatrixfARB/1,
- loadTransposeMatrixdARB/1,multTransposeMatrixfARB/1,multTransposeMatrixdARB/1,
- weightbvARB/1,weightsvARB/1,weightivARB/1,weightfvARB/1,weightdvARB/1,
- weightubvARB/1,weightusvARB/1,weightuivARB/1,vertexBlendARB/1,currentPaletteMatrixARB/1,
+ clearBufferuiv/3,clearBufferfv/3,clearBufferfi/4,getStringi/2,drawArraysInstanced/4,
+ drawElementsInstanced/5,texBuffer/3,primitiveRestartIndex/1,getInteger64i_v/2,
+ getBufferParameteri64v/2,framebufferTexture/4,vertexAttribDivisor/2,
+ minSampleShading/1,blendEquationi/2,blendEquationSeparatei/3,blendFunci/3,
+ blendFuncSeparatei/5,loadTransposeMatrixfARB/1,loadTransposeMatrixdARB/1,
+ multTransposeMatrixfARB/1,multTransposeMatrixdARB/1,weightbvARB/1,
+ weightsvARB/1,weightivARB/1,weightfvARB/1,weightdvARB/1,weightubvARB/1,
+ weightusvARB/1,weightuivARB/1,vertexBlendARB/1,currentPaletteMatrixARB/1,
matrixIndexubvARB/1,matrixIndexusvARB/1,matrixIndexuivARB/1,programStringARB/3,
bindProgramARB/2,deleteProgramsARB/1,genProgramsARB/1,programEnvParameter4dARB/6,
programEnvParameter4dvARB/3,programEnvParameter4fARB/6,programEnvParameter4fvARB/3,
programLocalParameter4dARB/6,programLocalParameter4dvARB/3,programLocalParameter4fARB/6,
programLocalParameter4fvARB/3,getProgramEnvParameterdvARB/2,getProgramEnvParameterfvARB/2,
getProgramLocalParameterdvARB/2,getProgramLocalParameterfvARB/2,
- getProgramStringARB/3,deleteObjectARB/1,getHandleARB/1,detachObjectARB/2,
- createShaderObjectARB/1,shaderSourceARB/2,compileShaderARB/1,createProgramObjectARB/0,
- attachObjectARB/2,linkProgramARB/1,useProgramObjectARB/1,validateProgramARB/1,
- getObjectParameterfvARB/2,getObjectParameterivARB/2,getInfoLogARB/2,
- getAttachedObjectsARB/2,getUniformLocationARB/2,getActiveUniformARB/3,
- getUniformfvARB/2,getUniformivARB/2,getShaderSourceARB/2,bindAttribLocationARB/3,
- getActiveAttribARB/3,getAttribLocationARB/2,isRenderbuffer/1,bindRenderbuffer/2,
- deleteRenderbuffers/1,genRenderbuffers/1,renderbufferStorage/4,getRenderbufferParameteriv/2,
+ getProgramStringARB/3,getBufferParameterivARB/2,deleteObjectARB/1,
+ getHandleARB/1,detachObjectARB/2,createShaderObjectARB/1,shaderSourceARB/2,
+ compileShaderARB/1,createProgramObjectARB/0,attachObjectARB/2,linkProgramARB/1,
+ useProgramObjectARB/1,validateProgramARB/1,getObjectParameterfvARB/2,
+ getObjectParameterivARB/2,getInfoLogARB/2,getAttachedObjectsARB/2,
+ getUniformLocationARB/2,getActiveUniformARB/3,getUniformfvARB/2,
+ getUniformivARB/2,getShaderSourceARB/2,bindAttribLocationARB/3,getActiveAttribARB/3,
+ getAttribLocationARB/2,isRenderbuffer/1,bindRenderbuffer/2,deleteRenderbuffers/1,
+ genRenderbuffers/1,renderbufferStorage/4,getRenderbufferParameteriv/2,
isFramebuffer/1,bindFramebuffer/2,deleteFramebuffers/1,genFramebuffers/1,
checkFramebufferStatus/1,framebufferTexture1D/5,framebufferTexture2D/5,
framebufferTexture3D/6,framebufferRenderbuffer/4,getFramebufferAttachmentParameteriv/3,
generateMipmap/1,blitFramebuffer/10,renderbufferStorageMultisample/5,
- framebufferTextureLayer/5,programParameteriARB/3,framebufferTextureARB/4,
- framebufferTextureFaceARB/5,vertexAttribDivisorARB/2,flushMappedBufferRange/3,
+ framebufferTextureLayer/5,framebufferTextureFaceARB/5,flushMappedBufferRange/3,
bindVertexArray/1,deleteVertexArrays/1,genVertexArrays/1,isVertexArray/1,
getUniformIndices/2,getActiveUniformsiv/3,getActiveUniformName/3,
getUniformBlockIndex/2,getActiveUniformBlockiv/4,getActiveUniformBlockName/3,
- uniformBlockBinding/3,copyBufferSubData/5,resizeBuffersMESA/0,windowPos4dMESA/4,
+ uniformBlockBinding/3,copyBufferSubData/5,drawElementsBaseVertex/5,
+ drawRangeElementsBaseVertex/7,drawElementsInstancedBaseVertex/6,
+ provokingVertex/1,fenceSync/2,isSync/1,deleteSync/1,clientWaitSync/3,
+ waitSync/3,getInteger64v/1,getSynciv/3,texImage2DMultisample/6,texImage3DMultisample/7,
+ getMultisamplefv/2,sampleMaski/2,namedStringARB/3,deleteNamedStringARB/1,
+ compileShaderIncludeARB/2,isNamedStringARB/1,getNamedStringARB/2,
+ getNamedStringivARB/2,bindFragDataLocationIndexed/4,getFragDataIndex/2,
+ genSamplers/1,deleteSamplers/1,isSampler/1,bindSampler/2,samplerParameteri/3,
+ samplerParameteriv/3,samplerParameterf/3,samplerParameterfv/3,samplerParameterIiv/3,
+ samplerParameterIuiv/3,getSamplerParameteriv/2,getSamplerParameterIiv/2,
+ getSamplerParameterfv/2,getSamplerParameterIuiv/2,queryCounter/2,
+ getQueryObjecti64v/2,getQueryObjectui64v/2,drawArraysIndirect/2,
+ drawElementsIndirect/3,uniform1d/2,uniform2d/3,uniform3d/4,uniform4d/5,
+ uniform1dv/2,uniform2dv/2,uniform3dv/2,uniform4dv/2,uniformMatrix2dv/3,
+ uniformMatrix3dv/3,uniformMatrix4dv/3,uniformMatrix2x3dv/3,uniformMatrix2x4dv/3,
+ uniformMatrix3x2dv/3,uniformMatrix3x4dv/3,uniformMatrix4x2dv/3,uniformMatrix4x3dv/3,
+ getUniformdv/2,getSubroutineUniformLocation/3,getSubroutineIndex/3,
+ getActiveSubroutineUniformName/4,getActiveSubroutineName/4,uniformSubroutinesuiv/2,
+ getUniformSubroutineuiv/2,getProgramStageiv/3,patchParameteri/2,
+ patchParameterfv/2,bindTransformFeedback/2,deleteTransformFeedbacks/1,
+ genTransformFeedbacks/1,isTransformFeedback/1,pauseTransformFeedback/0,
+ resumeTransformFeedback/0,drawTransformFeedback/2,drawTransformFeedbackStream/3,
+ beginQueryIndexed/3,endQueryIndexed/2,getQueryIndexediv/3,releaseShaderCompiler/0,
+ shaderBinary/3,getShaderPrecisionFormat/2,depthRangef/2,clearDepthf/1,
+ getProgramBinary/2,programBinary/3,programParameteri/3,useProgramStages/3,
+ activeShaderProgram/2,createShaderProgramv/2,bindProgramPipeline/1,
+ deleteProgramPipelines/1,genProgramPipelines/1,isProgramPipeline/1,
+ getProgramPipelineiv/2,programUniform1i/3,programUniform1iv/3,programUniform1f/3,
+ programUniform1fv/3,programUniform1d/3,programUniform1dv/3,programUniform1ui/3,
+ programUniform1uiv/3,programUniform2i/4,programUniform2iv/3,programUniform2f/4,
+ programUniform2fv/3,programUniform2d/4,programUniform2dv/3,programUniform2ui/4,
+ programUniform2uiv/3,programUniform3i/5,programUniform3iv/3,programUniform3f/5,
+ programUniform3fv/3,programUniform3d/5,programUniform3dv/3,programUniform3ui/5,
+ programUniform3uiv/3,programUniform4i/6,programUniform4iv/3,programUniform4f/6,
+ programUniform4fv/3,programUniform4d/6,programUniform4dv/3,programUniform4ui/6,
+ programUniform4uiv/3,programUniformMatrix2fv/4,programUniformMatrix3fv/4,
+ programUniformMatrix4fv/4,programUniformMatrix2dv/4,programUniformMatrix3dv/4,
+ programUniformMatrix4dv/4,programUniformMatrix2x3fv/4,programUniformMatrix3x2fv/4,
+ programUniformMatrix2x4fv/4,programUniformMatrix4x2fv/4,programUniformMatrix3x4fv/4,
+ programUniformMatrix4x3fv/4,programUniformMatrix2x3dv/4,programUniformMatrix3x2dv/4,
+ programUniformMatrix2x4dv/4,programUniformMatrix4x2dv/4,programUniformMatrix3x4dv/4,
+ programUniformMatrix4x3dv/4,validateProgramPipeline/1,getProgramPipelineInfoLog/2,
+ vertexAttribL1d/2,vertexAttribL2d/3,vertexAttribL3d/4,vertexAttribL4d/5,
+ vertexAttribL1dv/2,vertexAttribL2dv/2,vertexAttribL3dv/2,vertexAttribL4dv/2,
+ vertexAttribLPointer/5,getVertexAttribLdv/2,viewportArrayv/2,viewportIndexedf/5,
+ viewportIndexedfv/2,scissorArrayv/2,scissorIndexed/5,scissorIndexedv/2,
+ depthRangeArrayv/2,depthRangeIndexed/3,getFloati_v/2,getDoublei_v/2,
+ debugMessageControlARB/5,debugMessageInsertARB/5,getDebugMessageLogARB/2,
+ getGraphicsResetStatusARB/0,resizeBuffersMESA/0,windowPos4dMESA/4,
windowPos4dvMESA/1,windowPos4fMESA/4,windowPos4fvMESA/1,windowPos4iMESA/4,
windowPos4ivMESA/1,windowPos4sMESA/4,windowPos4svMESA/1,depthBoundsEXT/2,
stencilClearTagEXT/2]).
+-export([call/2, cast/2, send_bin/1]).
+%% @hidden
+call(Op, Args) ->
+ Port = get(opengl_port),
+ _ = erlang:port_control(Port,Op,Args),
+ rec().
+
+%% @hidden
+cast(Op, Args) ->
+ Port = get(opengl_port),
+ _ = erlang:port_control(Port,Op,Args),
+ ok.
+
+%% @hidden
+rec() ->
+ receive
+ {'_egl_result_', Res} -> Res;
+ {'_egl_error_', Op, Res} -> error({error,Res,Op})
+ end.
+
+%% @hidden
+send_bin(Bin) when is_binary(Bin) ->
+ Port = get(opengl_port),
+ erlang:port_command(Port,Bin);
+send_bin(Tuple) when is_tuple(Tuple) ->
+ Port = get(opengl_port),
+ case element(2, Tuple) of
+ Bin when is_binary(Bin) ->
+ erlang:port_command(Port,Bin)
+ end.
+
%% API
%% @spec (Op::enum(),Value::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAccum.xml">external</a> documentation.
+-spec accum(enum(),float()) -> ok.
accum(Op,Value) ->
- wxe_util:cast(5037, <<Op:?GLenum,Value:?GLfloat>>).
+ cast(5037, <<Op:?GLenum,Value:?GLfloat>>).
%% @spec (Func::enum(),Ref::clamp()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAlphaFunc.xml">external</a> documentation.
+-spec alphaFunc(enum(),clamp()) -> ok.
alphaFunc(Func,Ref) ->
- wxe_util:cast(5038, <<Func:?GLenum,Ref:?GLclampf>>).
+ cast(5038, <<Func:?GLenum,Ref:?GLclampf>>).
%% @spec (Textures::[integer()]) -> {0|1,Residences::[0|1]}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAreTexturesResident.xml">external</a> documentation.
+-spec areTexturesResident([integer()]) -> {0|1,[0|1]}.
areTexturesResident(Textures) ->
- wxe_util:call(5039, <<(length(Textures)):?GLuint,
+ call(5039, <<(length(Textures)):?GLuint,
(<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+length(Textures)) rem 2)*32)>>).
%% @spec (I::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glArrayElement.xml">external</a> documentation.
+-spec arrayElement(integer()) -> ok.
arrayElement(I) ->
- wxe_util:cast(5040, <<I:?GLint>>).
+ cast(5040, <<I:?GLint>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBegin.xml">external</a> documentation.
+-spec 'begin'(enum()) -> ok.
'begin'(Mode) ->
- wxe_util:cast(5041, <<Mode:?GLenum>>).
+ cast(5041, <<Mode:?GLenum>>).
%% @spec (Target::enum(),Texture::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindTexture.xml">external</a> documentation.
+-spec bindTexture(enum(),integer()) -> ok.
bindTexture(Target,Texture) ->
- wxe_util:cast(5042, <<Target:?GLenum,Texture:?GLuint>>).
+ cast(5042, <<Target:?GLenum,Texture:?GLuint>>).
-%% @spec (Width::integer(),Height::integer(),Xorig::float(),Yorig::float(),Xmove::float(),Ymove::float(),Bitmap::offset()|binary()) -> ok
+%% @spec (Width::integer(),Height::integer(),Xorig::float(),Yorig::float(),Xmove::float(),Ymove::float(),Bitmap::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBitmap.xml">external</a> documentation.
+-spec bitmap(integer(),integer(),float(),float(),float(),float(),offset()|mem()) -> ok.
bitmap(Width,Height,Xorig,Yorig,Xmove,Ymove,Bitmap) when is_integer(Bitmap) ->
- wxe_util:cast(5043, <<Width:?GLsizei,Height:?GLsizei,Xorig:?GLfloat,Yorig:?GLfloat,Xmove:?GLfloat,Ymove:?GLfloat,Bitmap:?GLuint>>);
+ cast(5043, <<Width:?GLsizei,Height:?GLsizei,Xorig:?GLfloat,Yorig:?GLfloat,Xmove:?GLfloat,Ymove:?GLfloat,Bitmap:?GLuint>>);
bitmap(Width,Height,Xorig,Yorig,Xmove,Ymove,Bitmap) ->
- wxe_util:send_bin(Bitmap),
- wxe_util:cast(5044, <<Width:?GLsizei,Height:?GLsizei,Xorig:?GLfloat,Yorig:?GLfloat,Xmove:?GLfloat,Ymove:?GLfloat>>).
+ send_bin(Bitmap),
+ cast(5044, <<Width:?GLsizei,Height:?GLsizei,Xorig:?GLfloat,Yorig:?GLfloat,Xmove:?GLfloat,Ymove:?GLfloat>>).
%% @spec (Sfactor::enum(),Dfactor::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunc.xml">external</a> documentation.
+-spec blendFunc(enum(),enum()) -> ok.
blendFunc(Sfactor,Dfactor) ->
- wxe_util:cast(5045, <<Sfactor:?GLenum,Dfactor:?GLenum>>).
+ cast(5045, <<Sfactor:?GLenum,Dfactor:?GLenum>>).
%% @spec (List::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCallList.xml">external</a> documentation.
+-spec callList(integer()) -> ok.
callList(List) ->
- wxe_util:cast(5046, <<List:?GLuint>>).
+ cast(5046, <<List:?GLuint>>).
%% @spec (Lists::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCallLists.xml">external</a> documentation.
+-spec callLists([integer()]) -> ok.
callLists(Lists) ->
- wxe_util:cast(5047, <<(length(Lists)):?GLuint,
+ cast(5047, <<(length(Lists)):?GLuint,
(<< <<C:?GLuint>> || C <- Lists>>)/binary,0:(((1+length(Lists)) rem 2)*32)>>).
%% @spec (Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClear.xml">external</a> documentation.
+-spec clear(integer()) -> ok.
clear(Mask) ->
- wxe_util:cast(5048, <<Mask:?GLbitfield>>).
+ cast(5048, <<Mask:?GLbitfield>>).
%% @spec (Red::float(),Green::float(),Blue::float(),Alpha::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearAccum.xml">external</a> documentation.
+-spec clearAccum(float(),float(),float(),float()) -> ok.
clearAccum(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5049, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat,Alpha:?GLfloat>>).
+ cast(5049, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat,Alpha:?GLfloat>>).
%% @spec (Red::clamp(),Green::clamp(),Blue::clamp(),Alpha::clamp()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearColor.xml">external</a> documentation.
+-spec clearColor(clamp(),clamp(),clamp(),clamp()) -> ok.
clearColor(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5050, <<Red:?GLclampf,Green:?GLclampf,Blue:?GLclampf,Alpha:?GLclampf>>).
+ cast(5050, <<Red:?GLclampf,Green:?GLclampf,Blue:?GLclampf,Alpha:?GLclampf>>).
%% @spec (Depth::clamp()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearDepth.xml">external</a> documentation.
+-spec clearDepth(clamp()) -> ok.
clearDepth(Depth) ->
- wxe_util:cast(5051, <<Depth:?GLclampd>>).
+ cast(5051, <<Depth:?GLclampd>>).
%% @spec (C::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearIndex.xml">external</a> documentation.
+-spec clearIndex(float()) -> ok.
clearIndex(C) ->
- wxe_util:cast(5052, <<C:?GLfloat>>).
+ cast(5052, <<C:?GLfloat>>).
%% @spec (S::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearStencil.xml">external</a> documentation.
+-spec clearStencil(integer()) -> ok.
clearStencil(S) ->
- wxe_util:cast(5053, <<S:?GLint>>).
+ cast(5053, <<S:?GLint>>).
-%% @spec (Plane::enum(),Equation::{float()}) -> ok
+%% @spec (Plane::enum(),Equation::{float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClipPlane.xml">external</a> documentation.
+-spec clipPlane(enum(),{float(),float(),float(),float()}) -> ok.
clipPlane(Plane,{E1,E2,E3,E4}) ->
- wxe_util:cast(5054, <<Plane:?GLenum,0:32,E1:?GLdouble,E2:?GLdouble,E3:?GLdouble,E4:?GLdouble>>).
+ cast(5054, <<Plane:?GLenum,0:32,E1:?GLdouble,E2:?GLdouble,E3:?GLdouble,E4:?GLdouble>>).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3b(integer(),integer(),integer()) -> ok.
color3b(Red,Green,Blue) ->
- wxe_util:cast(5055, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte>>).
+ cast(5055, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3b(Red,Green,Blue)
+-spec color3bv({integer(),integer(),integer()}) -> ok.
color3bv({Red,Green,Blue}) -> color3b(Red,Green,Blue).
%% @spec (Red::float(),Green::float(),Blue::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3d(float(),float(),float()) -> ok.
color3d(Red,Green,Blue) ->
- wxe_util:cast(5056, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble>>).
+ cast(5056, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3d(Red,Green,Blue)
+-spec color3dv({float(),float(),float()}) -> ok.
color3dv({Red,Green,Blue}) -> color3d(Red,Green,Blue).
%% @spec (Red::float(),Green::float(),Blue::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3f(float(),float(),float()) -> ok.
color3f(Red,Green,Blue) ->
- wxe_util:cast(5057, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat>>).
+ cast(5057, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3f(Red,Green,Blue)
+-spec color3fv({float(),float(),float()}) -> ok.
color3fv({Red,Green,Blue}) -> color3f(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3i(integer(),integer(),integer()) -> ok.
color3i(Red,Green,Blue) ->
- wxe_util:cast(5058, <<Red:?GLint,Green:?GLint,Blue:?GLint>>).
+ cast(5058, <<Red:?GLint,Green:?GLint,Blue:?GLint>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3i(Red,Green,Blue)
+-spec color3iv({integer(),integer(),integer()}) -> ok.
color3iv({Red,Green,Blue}) -> color3i(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3s(integer(),integer(),integer()) -> ok.
color3s(Red,Green,Blue) ->
- wxe_util:cast(5059, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort>>).
+ cast(5059, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3s(Red,Green,Blue)
+-spec color3sv({integer(),integer(),integer()}) -> ok.
color3sv({Red,Green,Blue}) -> color3s(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3ub(integer(),integer(),integer()) -> ok.
color3ub(Red,Green,Blue) ->
- wxe_util:cast(5060, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte>>).
+ cast(5060, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3ub(Red,Green,Blue)
+-spec color3ubv({integer(),integer(),integer()}) -> ok.
color3ubv({Red,Green,Blue}) -> color3ub(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3ui(integer(),integer(),integer()) -> ok.
color3ui(Red,Green,Blue) ->
- wxe_util:cast(5061, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint>>).
+ cast(5061, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3ui(Red,Green,Blue)
+-spec color3uiv({integer(),integer(),integer()}) -> ok.
color3uiv({Red,Green,Blue}) -> color3ui(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color3us(integer(),integer(),integer()) -> ok.
color3us(Red,Green,Blue) ->
- wxe_util:cast(5062, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort>>).
+ cast(5062, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv color3us(Red,Green,Blue)
+-spec color3usv({integer(),integer(),integer()}) -> ok.
color3usv({Red,Green,Blue}) -> color3us(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer(),Alpha::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4b(integer(),integer(),integer(),integer()) -> ok.
color4b(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5063, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte,Alpha:?GLbyte>>).
+ cast(5063, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte,Alpha:?GLbyte>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4b(Red,Green,Blue,Alpha)
+-spec color4bv({integer(),integer(),integer(),integer()}) -> ok.
color4bv({Red,Green,Blue,Alpha}) -> color4b(Red,Green,Blue,Alpha).
%% @spec (Red::float(),Green::float(),Blue::float(),Alpha::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4d(float(),float(),float(),float()) -> ok.
color4d(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5064, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble,Alpha:?GLdouble>>).
+ cast(5064, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble,Alpha:?GLdouble>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4d(Red,Green,Blue,Alpha)
+-spec color4dv({float(),float(),float(),float()}) -> ok.
color4dv({Red,Green,Blue,Alpha}) -> color4d(Red,Green,Blue,Alpha).
%% @spec (Red::float(),Green::float(),Blue::float(),Alpha::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4f(float(),float(),float(),float()) -> ok.
color4f(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5065, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat,Alpha:?GLfloat>>).
+ cast(5065, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat,Alpha:?GLfloat>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4f(Red,Green,Blue,Alpha)
+-spec color4fv({float(),float(),float(),float()}) -> ok.
color4fv({Red,Green,Blue,Alpha}) -> color4f(Red,Green,Blue,Alpha).
%% @spec (Red::integer(),Green::integer(),Blue::integer(),Alpha::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4i(integer(),integer(),integer(),integer()) -> ok.
color4i(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5066, <<Red:?GLint,Green:?GLint,Blue:?GLint,Alpha:?GLint>>).
+ cast(5066, <<Red:?GLint,Green:?GLint,Blue:?GLint,Alpha:?GLint>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4i(Red,Green,Blue,Alpha)
+-spec color4iv({integer(),integer(),integer(),integer()}) -> ok.
color4iv({Red,Green,Blue,Alpha}) -> color4i(Red,Green,Blue,Alpha).
%% @spec (Red::integer(),Green::integer(),Blue::integer(),Alpha::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4s(integer(),integer(),integer(),integer()) -> ok.
color4s(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5067, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort,Alpha:?GLshort>>).
+ cast(5067, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort,Alpha:?GLshort>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4s(Red,Green,Blue,Alpha)
+-spec color4sv({integer(),integer(),integer(),integer()}) -> ok.
color4sv({Red,Green,Blue,Alpha}) -> color4s(Red,Green,Blue,Alpha).
%% @spec (Red::integer(),Green::integer(),Blue::integer(),Alpha::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4ub(integer(),integer(),integer(),integer()) -> ok.
color4ub(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5068, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte,Alpha:?GLubyte>>).
+ cast(5068, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte,Alpha:?GLubyte>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4ub(Red,Green,Blue,Alpha)
+-spec color4ubv({integer(),integer(),integer(),integer()}) -> ok.
color4ubv({Red,Green,Blue,Alpha}) -> color4ub(Red,Green,Blue,Alpha).
%% @spec (Red::integer(),Green::integer(),Blue::integer(),Alpha::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4ui(integer(),integer(),integer(),integer()) -> ok.
color4ui(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5069, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint,Alpha:?GLuint>>).
+ cast(5069, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint,Alpha:?GLuint>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4ui(Red,Green,Blue,Alpha)
+-spec color4uiv({integer(),integer(),integer(),integer()}) -> ok.
color4uiv({Red,Green,Blue,Alpha}) -> color4ui(Red,Green,Blue,Alpha).
%% @spec (Red::integer(),Green::integer(),Blue::integer(),Alpha::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColor.xml">external</a> documentation.
+-spec color4us(integer(),integer(),integer(),integer()) -> ok.
color4us(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5070, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort,Alpha:?GLushort>>).
+ cast(5070, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort,Alpha:?GLushort>>).
%% @spec ({Red,Green,Blue,Alpha}) -> ok
%% @equiv color4us(Red,Green,Blue,Alpha)
+-spec color4usv({integer(),integer(),integer(),integer()}) -> ok.
color4usv({Red,Green,Blue,Alpha}) -> color4us(Red,Green,Blue,Alpha).
%% @spec (Red::0|1,Green::0|1,Blue::0|1,Alpha::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorMask.xml">external</a> documentation.
+-spec colorMask(0|1,0|1,0|1,0|1) -> ok.
colorMask(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5071, <<Red:?GLboolean,Green:?GLboolean,Blue:?GLboolean,Alpha:?GLboolean>>).
+ cast(5071, <<Red:?GLboolean,Green:?GLboolean,Blue:?GLboolean,Alpha:?GLboolean>>).
%% @spec (Face::enum(),Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorMaterial.xml">external</a> documentation.
+-spec colorMaterial(enum(),enum()) -> ok.
colorMaterial(Face,Mode) ->
- wxe_util:cast(5072, <<Face:?GLenum,Mode:?GLenum>>).
+ cast(5072, <<Face:?GLenum,Mode:?GLenum>>).
-%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorPointer.xml">external</a> documentation.
+-spec colorPointer(integer(),enum(),integer(),offset()|mem()) -> ok.
colorPointer(Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5073, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5073, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
colorPointer(Size,Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5074, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5074, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @spec (X::integer(),Y::integer(),Width::integer(),Height::integer(),Type::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyPixels.xml">external</a> documentation.
+-spec copyPixels(integer(),integer(),integer(),integer(),enum()) -> ok.
copyPixels(X,Y,Width,Height,Type) ->
- wxe_util:cast(5075, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei,Type:?GLenum>>).
+ cast(5075, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei,Type:?GLenum>>).
%% @spec (Target::enum(),Level::integer(),InternalFormat::enum(),X::integer(),Y::integer(),Width::integer(),Border::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyTexImage1D.xml">external</a> documentation.
+-spec copyTexImage1D(enum(),integer(),enum(),integer(),integer(),integer(),integer()) -> ok.
copyTexImage1D(Target,Level,InternalFormat,X,Y,Width,Border) ->
- wxe_util:cast(5076, <<Target:?GLenum,Level:?GLint,InternalFormat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei,Border:?GLint>>).
+ cast(5076, <<Target:?GLenum,Level:?GLint,InternalFormat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei,Border:?GLint>>).
%% @spec (Target::enum(),Level::integer(),InternalFormat::enum(),X::integer(),Y::integer(),Width::integer(),Height::integer(),Border::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyTexImage2D.xml">external</a> documentation.
+-spec copyTexImage2D(enum(),integer(),enum(),integer(),integer(),integer(),integer(),integer()) -> ok.
copyTexImage2D(Target,Level,InternalFormat,X,Y,Width,Height,Border) ->
- wxe_util:cast(5077, <<Target:?GLenum,Level:?GLint,InternalFormat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei,Border:?GLint>>).
+ cast(5077, <<Target:?GLenum,Level:?GLint,InternalFormat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei,Border:?GLint>>).
%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),X::integer(),Y::integer(),Width::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyTexSubImage1D.xml">external</a> documentation.
+-spec copyTexSubImage1D(enum(),integer(),integer(),integer(),integer(),integer()) -> ok.
copyTexSubImage1D(Target,Level,Xoffset,X,Y,Width) ->
- wxe_util:cast(5078, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,X:?GLint,Y:?GLint,Width:?GLsizei>>).
+ cast(5078, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,X:?GLint,Y:?GLint,Width:?GLsizei>>).
%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),X::integer(),Y::integer(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyTexSubImage2D.xml">external</a> documentation.
+-spec copyTexSubImage2D(enum(),integer(),integer(),integer(),integer(),integer(),integer(),integer()) -> ok.
copyTexSubImage2D(Target,Level,Xoffset,Yoffset,X,Y,Width,Height) ->
- wxe_util:cast(5079, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5079, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCullFace.xml">external</a> documentation.
+-spec cullFace(enum()) -> ok.
cullFace(Mode) ->
- wxe_util:cast(5080, <<Mode:?GLenum>>).
+ cast(5080, <<Mode:?GLenum>>).
%% @spec (List::integer(),Range::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteLists.xml">external</a> documentation.
+-spec deleteLists(integer(),integer()) -> ok.
deleteLists(List,Range) ->
- wxe_util:cast(5081, <<List:?GLuint,Range:?GLsizei>>).
+ cast(5081, <<List:?GLuint,Range:?GLsizei>>).
%% @spec (Textures::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteTextures.xml">external</a> documentation.
+-spec deleteTextures([integer()]) -> ok.
deleteTextures(Textures) ->
- wxe_util:cast(5082, <<(length(Textures)):?GLuint,
+ cast(5082, <<(length(Textures)):?GLuint,
(<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+length(Textures)) rem 2)*32)>>).
%% @spec (Func::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthFunc.xml">external</a> documentation.
+-spec depthFunc(enum()) -> ok.
depthFunc(Func) ->
- wxe_util:cast(5083, <<Func:?GLenum>>).
+ cast(5083, <<Func:?GLenum>>).
%% @spec (Flag::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthMask.xml">external</a> documentation.
+-spec depthMask(0|1) -> ok.
depthMask(Flag) ->
- wxe_util:cast(5084, <<Flag:?GLboolean>>).
+ cast(5084, <<Flag:?GLboolean>>).
%% @spec (ZNear::clamp(),ZFar::clamp()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthRange.xml">external</a> documentation.
+-spec depthRange(clamp(),clamp()) -> ok.
depthRange(ZNear,ZFar) ->
- wxe_util:cast(5085, <<ZNear:?GLclampd,ZFar:?GLclampd>>).
+ cast(5085, <<ZNear:?GLclampd,ZFar:?GLclampd>>).
%% @spec (Cap::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDisable.xml">external</a> documentation.
+-spec disable(enum()) -> ok.
disable(Cap) ->
- wxe_util:cast(5086, <<Cap:?GLenum>>).
+ cast(5086, <<Cap:?GLenum>>).
%% @spec (Array::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDisableClientState.xml">external</a> documentation.
+-spec disableClientState(enum()) -> ok.
disableClientState(Array) ->
- wxe_util:cast(5087, <<Array:?GLenum>>).
+ cast(5087, <<Array:?GLenum>>).
%% @spec (Mode::enum(),First::integer(),Count::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawArrays.xml">external</a> documentation.
+-spec drawArrays(enum(),integer(),integer()) -> ok.
drawArrays(Mode,First,Count) ->
- wxe_util:cast(5088, <<Mode:?GLenum,First:?GLint,Count:?GLsizei>>).
+ cast(5088, <<Mode:?GLenum,First:?GLint,Count:?GLsizei>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawBuffer.xml">external</a> documentation.
+-spec drawBuffer(enum()) -> ok.
drawBuffer(Mode) ->
- wxe_util:cast(5089, <<Mode:?GLenum>>).
+ cast(5089, <<Mode:?GLenum>>).
-%% @spec (Mode::enum(),Count::integer(),Type::enum(),Indices::offset()|binary()) -> ok
+%% @spec (Mode::enum(),Count::integer(),Type::enum(),Indices::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElements.xml">external</a> documentation.
+-spec drawElements(enum(),integer(),enum(),offset()|mem()) -> ok.
drawElements(Mode,Count,Type,Indices) when is_integer(Indices) ->
- wxe_util:cast(5090, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint>>);
+ cast(5090, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint>>);
drawElements(Mode,Count,Type,Indices) ->
- wxe_util:send_bin(Indices),
- wxe_util:cast(5091, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum>>).
+ send_bin(Indices),
+ cast(5091, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum>>).
-%% @spec (Width::integer(),Height::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Width::integer(),Height::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawPixels.xml">external</a> documentation.
+-spec drawPixels(integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
drawPixels(Width,Height,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5092, <<Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5092, <<Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
drawPixels(Width,Height,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5093, <<Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5093, <<Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (Flag::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEdgeFlag.xml">external</a> documentation.
+-spec edgeFlag(0|1) -> ok.
edgeFlag(Flag) ->
- wxe_util:cast(5094, <<Flag:?GLboolean>>).
+ cast(5094, <<Flag:?GLboolean>>).
-%% @spec (Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEdgeFlagPointer.xml">external</a> documentation.
+-spec edgeFlagPointer(integer(),offset()|mem()) -> ok.
edgeFlagPointer(Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5095, <<Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5095, <<Stride:?GLsizei,Pointer:?GLuint>>);
edgeFlagPointer(Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5096, <<Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5096, <<Stride:?GLsizei>>).
%% @spec ({Flag}) -> ok
%% @equiv edgeFlag(Flag)
+-spec edgeFlagv({0|1}) -> ok.
edgeFlagv({Flag}) -> edgeFlag(Flag).
%% @spec (Cap::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnable.xml">external</a> documentation.
+-spec enable(enum()) -> ok.
enable(Cap) ->
- wxe_util:cast(5097, <<Cap:?GLenum>>).
+ cast(5097, <<Cap:?GLenum>>).
%% @spec (Array::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnableClientState.xml">external</a> documentation.
+-spec enableClientState(enum()) -> ok.
enableClientState(Array) ->
- wxe_util:cast(5098, <<Array:?GLenum>>).
+ cast(5098, <<Array:?GLenum>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnd.xml">external</a> documentation.
+-spec 'end'() -> ok.
'end'() ->
- wxe_util:cast(5099, <<>>).
+ cast(5099, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEndList.xml">external</a> documentation.
+-spec endList() -> ok.
endList() ->
- wxe_util:cast(5100, <<>>).
+ cast(5100, <<>>).
%% @spec (U::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalCoord.xml">external</a> documentation.
+-spec evalCoord1d(float()) -> ok.
evalCoord1d(U) ->
- wxe_util:cast(5101, <<U:?GLdouble>>).
+ cast(5101, <<U:?GLdouble>>).
%% @spec ({U}) -> ok
%% @equiv evalCoord1d(U)
+-spec evalCoord1dv({float()}) -> ok.
evalCoord1dv({U}) -> evalCoord1d(U).
%% @spec (U::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalCoord.xml">external</a> documentation.
+-spec evalCoord1f(float()) -> ok.
evalCoord1f(U) ->
- wxe_util:cast(5102, <<U:?GLfloat>>).
+ cast(5102, <<U:?GLfloat>>).
%% @spec ({U}) -> ok
%% @equiv evalCoord1f(U)
+-spec evalCoord1fv({float()}) -> ok.
evalCoord1fv({U}) -> evalCoord1f(U).
%% @spec (U::float(),V::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalCoord.xml">external</a> documentation.
+-spec evalCoord2d(float(),float()) -> ok.
evalCoord2d(U,V) ->
- wxe_util:cast(5103, <<U:?GLdouble,V:?GLdouble>>).
+ cast(5103, <<U:?GLdouble,V:?GLdouble>>).
%% @spec ({U,V}) -> ok
%% @equiv evalCoord2d(U,V)
+-spec evalCoord2dv({float(),float()}) -> ok.
evalCoord2dv({U,V}) -> evalCoord2d(U,V).
%% @spec (U::float(),V::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalCoord.xml">external</a> documentation.
+-spec evalCoord2f(float(),float()) -> ok.
evalCoord2f(U,V) ->
- wxe_util:cast(5104, <<U:?GLfloat,V:?GLfloat>>).
+ cast(5104, <<U:?GLfloat,V:?GLfloat>>).
%% @spec ({U,V}) -> ok
%% @equiv evalCoord2f(U,V)
+-spec evalCoord2fv({float(),float()}) -> ok.
evalCoord2fv({U,V}) -> evalCoord2f(U,V).
%% @spec (Mode::enum(),I1::integer(),I2::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalMesh.xml">external</a> documentation.
+-spec evalMesh1(enum(),integer(),integer()) -> ok.
evalMesh1(Mode,I1,I2) ->
- wxe_util:cast(5105, <<Mode:?GLenum,I1:?GLint,I2:?GLint>>).
+ cast(5105, <<Mode:?GLenum,I1:?GLint,I2:?GLint>>).
%% @spec (Mode::enum(),I1::integer(),I2::integer(),J1::integer(),J2::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalMesh.xml">external</a> documentation.
+-spec evalMesh2(enum(),integer(),integer(),integer(),integer()) -> ok.
evalMesh2(Mode,I1,I2,J1,J2) ->
- wxe_util:cast(5106, <<Mode:?GLenum,I1:?GLint,I2:?GLint,J1:?GLint,J2:?GLint>>).
+ cast(5106, <<Mode:?GLenum,I1:?GLint,I2:?GLint,J1:?GLint,J2:?GLint>>).
%% @spec (I::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalPoint.xml">external</a> documentation.
+-spec evalPoint1(integer()) -> ok.
evalPoint1(I) ->
- wxe_util:cast(5107, <<I:?GLint>>).
+ cast(5107, <<I:?GLint>>).
%% @spec (I::integer(),J::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEvalPoint.xml">external</a> documentation.
+-spec evalPoint2(integer(),integer()) -> ok.
evalPoint2(I,J) ->
- wxe_util:cast(5108, <<I:?GLint,J:?GLint>>).
+ cast(5108, <<I:?GLint,J:?GLint>>).
-%% @spec (Size::integer(),Type::enum(),Buffer::wx:wx_mem()) -> ok
+%% @spec (Size::integer(),Type::enum(),Buffer::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFeedbackBuffer.xml">external</a> documentation.
+-spec feedbackBuffer(integer(),enum(),mem()) -> ok.
feedbackBuffer(Size,Type,Buffer) ->
- wxe_util:send_bin(Buffer#wx_mem.bin),
- wxe_util:call(5109, <<Size:?GLsizei,Type:?GLenum>>).
+ send_bin(Buffer),
+ call(5109, <<Size:?GLsizei,Type:?GLenum>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFinish.xml">external</a> documentation.
+-spec finish() -> ok.
finish() ->
- wxe_util:cast(5110, <<>>).
+ cast(5110, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFlush.xml">external</a> documentation.
+-spec flush() -> ok.
flush() ->
- wxe_util:cast(5111, <<>>).
+ cast(5111, <<>>).
%% @spec (Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFog.xml">external</a> documentation.
+-spec fogf(enum(),float()) -> ok.
fogf(Pname,Param) ->
- wxe_util:cast(5112, <<Pname:?GLenum,Param:?GLfloat>>).
+ cast(5112, <<Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFog.xml">external</a> documentation.
+-spec fogfv(enum(),{float()}) -> ok.
fogfv(Pname,Params) ->
- wxe_util:cast(5113, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5113, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @spec (Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFog.xml">external</a> documentation.
+-spec fogi(enum(),integer()) -> ok.
fogi(Pname,Param) ->
- wxe_util:cast(5114, <<Pname:?GLenum,Param:?GLint>>).
+ cast(5114, <<Pname:?GLenum,Param:?GLint>>).
%% @spec (Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFog.xml">external</a> documentation.
+-spec fogiv(enum(),{integer()}) -> ok.
fogiv(Pname,Params) ->
- wxe_util:cast(5115, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5115, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFrontFace.xml">external</a> documentation.
+-spec frontFace(enum()) -> ok.
frontFace(Mode) ->
- wxe_util:cast(5116, <<Mode:?GLenum>>).
+ cast(5116, <<Mode:?GLenum>>).
%% @spec (Left::float(),Right::float(),Bottom::float(),Top::float(),ZNear::float(),ZFar::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFrustum.xml">external</a> documentation.
+-spec frustum(float(),float(),float(),float(),float(),float()) -> ok.
frustum(Left,Right,Bottom,Top,ZNear,ZFar) ->
- wxe_util:cast(5117, <<Left:?GLdouble,Right:?GLdouble,Bottom:?GLdouble,Top:?GLdouble,ZNear:?GLdouble,ZFar:?GLdouble>>).
+ cast(5117, <<Left:?GLdouble,Right:?GLdouble,Bottom:?GLdouble,Top:?GLdouble,ZNear:?GLdouble,ZFar:?GLdouble>>).
%% @spec (Range::integer()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenLists.xml">external</a> documentation.
+-spec genLists(integer()) -> integer().
genLists(Range) ->
- wxe_util:call(5118, <<Range:?GLsizei>>).
+ call(5118, <<Range:?GLsizei>>).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenTextures.xml">external</a> documentation.
+-spec genTextures(integer()) -> [integer()].
genTextures(N) ->
- wxe_util:call(5119, <<N:?GLsizei>>).
+ call(5119, <<N:?GLsizei>>).
%% @spec (Pname::enum()) -> [0|1]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBooleanv.xml">external</a> documentation.
+-spec getBooleanv(enum()) -> [0|1].
getBooleanv(Pname) ->
- wxe_util:call(5120, <<Pname:?GLenum>>).
+ call(5120, <<Pname:?GLenum>>).
-%% @spec (Plane::enum()) -> {float()}
+%% @spec (Plane::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetClipPlane.xml">external</a> documentation.
+-spec getClipPlane(enum()) -> {float(),float(),float(),float()}.
getClipPlane(Plane) ->
- wxe_util:call(5121, <<Plane:?GLenum>>).
+ call(5121, <<Plane:?GLenum>>).
%% @spec (Pname::enum()) -> [float()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetDoublev.xml">external</a> documentation.
+-spec getDoublev(enum()) -> [float()].
getDoublev(Pname) ->
- wxe_util:call(5122, <<Pname:?GLenum>>).
+ call(5122, <<Pname:?GLenum>>).
%% @spec () -> enum()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetError.xml">external</a> documentation.
+-spec getError() -> enum().
getError() ->
- wxe_util:call(5123, <<>>).
+ call(5123, <<>>).
%% @spec (Pname::enum()) -> [float()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFloatv.xml">external</a> documentation.
+-spec getFloatv(enum()) -> [float()].
getFloatv(Pname) ->
- wxe_util:call(5124, <<Pname:?GLenum>>).
+ call(5124, <<Pname:?GLenum>>).
%% @spec (Pname::enum()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetIntegerv.xml">external</a> documentation.
+-spec getIntegerv(enum()) -> [integer()].
getIntegerv(Pname) ->
- wxe_util:call(5125, <<Pname:?GLenum>>).
+ call(5125, <<Pname:?GLenum>>).
-%% @spec (Light::enum(),Pname::enum()) -> {float()}
+%% @spec (Light::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetLight.xml">external</a> documentation.
+-spec getLightfv(enum(),enum()) -> {float(),float(),float(),float()}.
getLightfv(Light,Pname) ->
- wxe_util:call(5126, <<Light:?GLenum,Pname:?GLenum>>).
+ call(5126, <<Light:?GLenum,Pname:?GLenum>>).
-%% @spec (Light::enum(),Pname::enum()) -> {integer()}
+%% @spec (Light::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetLight.xml">external</a> documentation.
+-spec getLightiv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getLightiv(Light,Pname) ->
- wxe_util:call(5127, <<Light:?GLenum,Pname:?GLenum>>).
+ call(5127, <<Light:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Query::enum(),V::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Query::enum(),V::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMap.xml">external</a> documentation.
+-spec getMapdv(enum(),enum(),mem()) -> ok.
getMapdv(Target,Query,V) ->
- wxe_util:send_bin(V#wx_mem.bin),
- wxe_util:call(5128, <<Target:?GLenum,Query:?GLenum>>).
+ send_bin(V),
+ call(5128, <<Target:?GLenum,Query:?GLenum>>).
-%% @spec (Target::enum(),Query::enum(),V::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Query::enum(),V::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMap.xml">external</a> documentation.
+-spec getMapfv(enum(),enum(),mem()) -> ok.
getMapfv(Target,Query,V) ->
- wxe_util:send_bin(V#wx_mem.bin),
- wxe_util:call(5129, <<Target:?GLenum,Query:?GLenum>>).
+ send_bin(V),
+ call(5129, <<Target:?GLenum,Query:?GLenum>>).
-%% @spec (Target::enum(),Query::enum(),V::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Query::enum(),V::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMap.xml">external</a> documentation.
+-spec getMapiv(enum(),enum(),mem()) -> ok.
getMapiv(Target,Query,V) ->
- wxe_util:send_bin(V#wx_mem.bin),
- wxe_util:call(5130, <<Target:?GLenum,Query:?GLenum>>).
+ send_bin(V),
+ call(5130, <<Target:?GLenum,Query:?GLenum>>).
-%% @spec (Face::enum(),Pname::enum()) -> {float()}
+%% @spec (Face::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMaterial.xml">external</a> documentation.
+-spec getMaterialfv(enum(),enum()) -> {float(),float(),float(),float()}.
getMaterialfv(Face,Pname) ->
- wxe_util:call(5131, <<Face:?GLenum,Pname:?GLenum>>).
+ call(5131, <<Face:?GLenum,Pname:?GLenum>>).
-%% @spec (Face::enum(),Pname::enum()) -> {integer()}
+%% @spec (Face::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMaterial.xml">external</a> documentation.
+-spec getMaterialiv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getMaterialiv(Face,Pname) ->
- wxe_util:call(5132, <<Face:?GLenum,Pname:?GLenum>>).
+ call(5132, <<Face:?GLenum,Pname:?GLenum>>).
-%% @spec (Map::enum(),Values::wx:wx_mem()) -> ok
+%% @spec (Map::enum(),Values::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetPixelMap.xml">external</a> documentation.
+-spec getPixelMapfv(enum(),mem()) -> ok.
getPixelMapfv(Map,Values) ->
- wxe_util:send_bin(Values#wx_mem.bin),
- wxe_util:call(5133, <<Map:?GLenum>>).
+ send_bin(Values),
+ call(5133, <<Map:?GLenum>>).
-%% @spec (Map::enum(),Values::wx:wx_mem()) -> ok
+%% @spec (Map::enum(),Values::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetPixelMap.xml">external</a> documentation.
+-spec getPixelMapuiv(enum(),mem()) -> ok.
getPixelMapuiv(Map,Values) ->
- wxe_util:send_bin(Values#wx_mem.bin),
- wxe_util:call(5134, <<Map:?GLenum>>).
+ send_bin(Values),
+ call(5134, <<Map:?GLenum>>).
-%% @spec (Map::enum(),Values::wx:wx_mem()) -> ok
+%% @spec (Map::enum(),Values::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetPixelMap.xml">external</a> documentation.
+-spec getPixelMapusv(enum(),mem()) -> ok.
getPixelMapusv(Map,Values) ->
- wxe_util:send_bin(Values#wx_mem.bin),
- wxe_util:call(5135, <<Map:?GLenum>>).
+ send_bin(Values),
+ call(5135, <<Map:?GLenum>>).
%% @spec () -> binary()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetPolygonStipple.xml">external</a> documentation.
+-spec getPolygonStipple() -> binary().
getPolygonStipple() ->
- wxe_util:call(5136, <<>>).
+ call(5136, <<>>).
%% @spec (Name::enum()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetString.xml">external</a> documentation.
+-spec getString(enum()) -> string().
getString(Name) ->
- wxe_util:call(5137, <<Name:?GLenum>>).
+ call(5137, <<Name:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {float()}
+%% @spec (Target::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexEnv.xml">external</a> documentation.
+-spec getTexEnvfv(enum(),enum()) -> {float(),float(),float(),float()}.
getTexEnvfv(Target,Pname) ->
- wxe_util:call(5138, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5138, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {integer()}
+%% @spec (Target::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexEnv.xml">external</a> documentation.
+-spec getTexEnviv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getTexEnviv(Target,Pname) ->
- wxe_util:call(5139, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5139, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Coord::enum(),Pname::enum()) -> {float()}
+%% @spec (Coord::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexGen.xml">external</a> documentation.
+-spec getTexGendv(enum(),enum()) -> {float(),float(),float(),float()}.
getTexGendv(Coord,Pname) ->
- wxe_util:call(5140, <<Coord:?GLenum,Pname:?GLenum>>).
+ call(5140, <<Coord:?GLenum,Pname:?GLenum>>).
-%% @spec (Coord::enum(),Pname::enum()) -> {float()}
+%% @spec (Coord::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexGen.xml">external</a> documentation.
+-spec getTexGenfv(enum(),enum()) -> {float(),float(),float(),float()}.
getTexGenfv(Coord,Pname) ->
- wxe_util:call(5141, <<Coord:?GLenum,Pname:?GLenum>>).
+ call(5141, <<Coord:?GLenum,Pname:?GLenum>>).
-%% @spec (Coord::enum(),Pname::enum()) -> {integer()}
+%% @spec (Coord::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexGen.xml">external</a> documentation.
+-spec getTexGeniv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getTexGeniv(Coord,Pname) ->
- wxe_util:call(5142, <<Coord:?GLenum,Pname:?GLenum>>).
+ call(5142, <<Coord:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Level::integer(),Format::enum(),Type::enum(),Pixels::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Level::integer(),Format::enum(),Type::enum(),Pixels::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexImage.xml">external</a> documentation.
+-spec getTexImage(enum(),integer(),enum(),enum(),mem()) -> ok.
getTexImage(Target,Level,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels#wx_mem.bin),
- wxe_util:call(5143, <<Target:?GLenum,Level:?GLint,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ call(5143, <<Target:?GLenum,Level:?GLint,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Level::integer(),Pname::enum()) -> {float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexLevelParameter.xml">external</a> documentation.
+-spec getTexLevelParameterfv(enum(),integer(),enum()) -> {float()}.
getTexLevelParameterfv(Target,Level,Pname) ->
- wxe_util:call(5144, <<Target:?GLenum,Level:?GLint,Pname:?GLenum>>).
+ call(5144, <<Target:?GLenum,Level:?GLint,Pname:?GLenum>>).
%% @spec (Target::enum(),Level::integer(),Pname::enum()) -> {integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexLevelParameter.xml">external</a> documentation.
+-spec getTexLevelParameteriv(enum(),integer(),enum()) -> {integer()}.
getTexLevelParameteriv(Target,Level,Pname) ->
- wxe_util:call(5145, <<Target:?GLenum,Level:?GLint,Pname:?GLenum>>).
+ call(5145, <<Target:?GLenum,Level:?GLint,Pname:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {float()}
+%% @spec (Target::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexParameter.xml">external</a> documentation.
+-spec getTexParameterfv(enum(),enum()) -> {float(),float(),float(),float()}.
getTexParameterfv(Target,Pname) ->
- wxe_util:call(5146, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5146, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {integer()}
+%% @spec (Target::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexParameter.xml">external</a> documentation.
+-spec getTexParameteriv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getTexParameteriv(Target,Pname) ->
- wxe_util:call(5147, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5147, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Target::enum(),Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glHint.xml">external</a> documentation.
+-spec hint(enum(),enum()) -> ok.
hint(Target,Mode) ->
- wxe_util:cast(5148, <<Target:?GLenum,Mode:?GLenum>>).
+ cast(5148, <<Target:?GLenum,Mode:?GLenum>>).
%% @spec (Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndexMask.xml">external</a> documentation.
+-spec indexMask(integer()) -> ok.
indexMask(Mask) ->
- wxe_util:cast(5149, <<Mask:?GLuint>>).
+ cast(5149, <<Mask:?GLuint>>).
-%% @spec (Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndexPointer.xml">external</a> documentation.
+-spec indexPointer(enum(),integer(),offset()|mem()) -> ok.
indexPointer(Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5150, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5150, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
indexPointer(Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5151, <<Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5151, <<Type:?GLenum,Stride:?GLsizei>>).
%% @spec (C::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndex.xml">external</a> documentation.
+-spec indexd(float()) -> ok.
indexd(C) ->
- wxe_util:cast(5152, <<C:?GLdouble>>).
+ cast(5152, <<C:?GLdouble>>).
%% @spec ({C}) -> ok
%% @equiv indexd(C)
+-spec indexdv({float()}) -> ok.
indexdv({C}) -> indexd(C).
%% @spec (C::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndex.xml">external</a> documentation.
+-spec indexf(float()) -> ok.
indexf(C) ->
- wxe_util:cast(5153, <<C:?GLfloat>>).
+ cast(5153, <<C:?GLfloat>>).
%% @spec ({C}) -> ok
%% @equiv indexf(C)
+-spec indexfv({float()}) -> ok.
indexfv({C}) -> indexf(C).
%% @spec (C::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndex.xml">external</a> documentation.
+-spec indexi(integer()) -> ok.
indexi(C) ->
- wxe_util:cast(5154, <<C:?GLint>>).
+ cast(5154, <<C:?GLint>>).
%% @spec ({C}) -> ok
%% @equiv indexi(C)
+-spec indexiv({integer()}) -> ok.
indexiv({C}) -> indexi(C).
%% @spec (C::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndex.xml">external</a> documentation.
+-spec indexs(integer()) -> ok.
indexs(C) ->
- wxe_util:cast(5155, <<C:?GLshort>>).
+ cast(5155, <<C:?GLshort>>).
%% @spec ({C}) -> ok
%% @equiv indexs(C)
+-spec indexsv({integer()}) -> ok.
indexsv({C}) -> indexs(C).
%% @spec (C::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIndex.xml">external</a> documentation.
+-spec indexub(integer()) -> ok.
indexub(C) ->
- wxe_util:cast(5156, <<C:?GLubyte>>).
+ cast(5156, <<C:?GLubyte>>).
%% @spec ({C}) -> ok
%% @equiv indexub(C)
+-spec indexubv({integer()}) -> ok.
indexubv({C}) -> indexub(C).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glInitNames.xml">external</a> documentation.
+-spec initNames() -> ok.
initNames() ->
- wxe_util:cast(5157, <<>>).
+ cast(5157, <<>>).
-%% @spec (Format::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Format::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glInterleavedArrays.xml">external</a> documentation.
+-spec interleavedArrays(enum(),integer(),offset()|mem()) -> ok.
interleavedArrays(Format,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5158, <<Format:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5158, <<Format:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
interleavedArrays(Format,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5159, <<Format:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5159, <<Format:?GLenum,Stride:?GLsizei>>).
%% @spec (Cap::enum()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsEnabled.xml">external</a> documentation.
+-spec isEnabled(enum()) -> 0|1.
isEnabled(Cap) ->
- wxe_util:call(5160, <<Cap:?GLenum>>).
+ call(5160, <<Cap:?GLenum>>).
%% @spec (List::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsList.xml">external</a> documentation.
+-spec isList(integer()) -> 0|1.
isList(List) ->
- wxe_util:call(5161, <<List:?GLuint>>).
+ call(5161, <<List:?GLuint>>).
%% @spec (Texture::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsTexture.xml">external</a> documentation.
+-spec isTexture(integer()) -> 0|1.
isTexture(Texture) ->
- wxe_util:call(5162, <<Texture:?GLuint>>).
+ call(5162, <<Texture:?GLuint>>).
%% @spec (Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLightModel.xml">external</a> documentation.
+-spec lightModelf(enum(),float()) -> ok.
lightModelf(Pname,Param) ->
- wxe_util:cast(5163, <<Pname:?GLenum,Param:?GLfloat>>).
+ cast(5163, <<Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLightModel.xml">external</a> documentation.
+-spec lightModelfv(enum(),{float()}) -> ok.
lightModelfv(Pname,Params) ->
- wxe_util:cast(5164, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5164, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @spec (Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLightModel.xml">external</a> documentation.
+-spec lightModeli(enum(),integer()) -> ok.
lightModeli(Pname,Param) ->
- wxe_util:cast(5165, <<Pname:?GLenum,Param:?GLint>>).
+ cast(5165, <<Pname:?GLenum,Param:?GLint>>).
%% @spec (Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLightModel.xml">external</a> documentation.
+-spec lightModeliv(enum(),{integer()}) -> ok.
lightModeliv(Pname,Params) ->
- wxe_util:cast(5166, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5166, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @spec (Light::enum(),Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLight.xml">external</a> documentation.
+-spec lightf(enum(),enum(),float()) -> ok.
lightf(Light,Pname,Param) ->
- wxe_util:cast(5167, <<Light:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
+ cast(5167, <<Light:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Light::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLight.xml">external</a> documentation.
+-spec lightfv(enum(),enum(),{float()}) -> ok.
lightfv(Light,Pname,Params) ->
- wxe_util:cast(5168, <<Light:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5168, <<Light:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Light::enum(),Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLight.xml">external</a> documentation.
+-spec lighti(enum(),enum(),integer()) -> ok.
lighti(Light,Pname,Param) ->
- wxe_util:cast(5169, <<Light:?GLenum,Pname:?GLenum,Param:?GLint>>).
+ cast(5169, <<Light:?GLenum,Pname:?GLenum,Param:?GLint>>).
%% @spec (Light::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLight.xml">external</a> documentation.
+-spec lightiv(enum(),enum(),{integer()}) -> ok.
lightiv(Light,Pname,Params) ->
- wxe_util:cast(5170, <<Light:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5170, <<Light:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Factor::integer(),Pattern::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLineStipple.xml">external</a> documentation.
+-spec lineStipple(integer(),integer()) -> ok.
lineStipple(Factor,Pattern) ->
- wxe_util:cast(5171, <<Factor:?GLint,Pattern:?GLushort>>).
+ cast(5171, <<Factor:?GLint,Pattern:?GLushort>>).
%% @spec (Width::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLineWidth.xml">external</a> documentation.
+-spec lineWidth(float()) -> ok.
lineWidth(Width) ->
- wxe_util:cast(5172, <<Width:?GLfloat>>).
+ cast(5172, <<Width:?GLfloat>>).
%% @spec (Base::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glListBase.xml">external</a> documentation.
+-spec listBase(integer()) -> ok.
listBase(Base) ->
- wxe_util:cast(5173, <<Base:?GLuint>>).
+ cast(5173, <<Base:?GLuint>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadIdentity.xml">external</a> documentation.
+-spec loadIdentity() -> ok.
loadIdentity() ->
- wxe_util:cast(5174, <<>>).
+ cast(5174, <<>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadMatrix.xml">external</a> documentation.
+-spec loadMatrixd({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
loadMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5175, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5175, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
loadMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5175, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5175, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadMatrix.xml">external</a> documentation.
+-spec loadMatrixf({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
loadMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5176, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5176, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
loadMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5176, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5176, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
%% @spec (Name::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadName.xml">external</a> documentation.
+-spec loadName(integer()) -> ok.
loadName(Name) ->
- wxe_util:cast(5177, <<Name:?GLuint>>).
+ cast(5177, <<Name:?GLuint>>).
%% @spec (Opcode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLogicOp.xml">external</a> documentation.
+-spec logicOp(enum()) -> ok.
logicOp(Opcode) ->
- wxe_util:cast(5178, <<Opcode:?GLenum>>).
+ cast(5178, <<Opcode:?GLenum>>).
%% @spec (Target::enum(),U1::float(),U2::float(),Stride::integer(),Order::integer(),Points::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMap.xml">external</a> documentation.
+-spec map1d(enum(),float(),float(),integer(),integer(),binary()) -> ok.
map1d(Target,U1,U2,Stride,Order,Points) ->
- wxe_util:send_bin(Points),
- wxe_util:cast(5179, <<Target:?GLenum,0:32,U1:?GLdouble,U2:?GLdouble,Stride:?GLint,Order:?GLint>>).
+ send_bin(Points),
+ cast(5179, <<Target:?GLenum,0:32,U1:?GLdouble,U2:?GLdouble,Stride:?GLint,Order:?GLint>>).
%% @spec (Target::enum(),U1::float(),U2::float(),Stride::integer(),Order::integer(),Points::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMap.xml">external</a> documentation.
+-spec map1f(enum(),float(),float(),integer(),integer(),binary()) -> ok.
map1f(Target,U1,U2,Stride,Order,Points) ->
- wxe_util:send_bin(Points),
- wxe_util:cast(5180, <<Target:?GLenum,U1:?GLfloat,U2:?GLfloat,Stride:?GLint,Order:?GLint>>).
+ send_bin(Points),
+ cast(5180, <<Target:?GLenum,U1:?GLfloat,U2:?GLfloat,Stride:?GLint,Order:?GLint>>).
%% @spec (Target::enum(),U1::float(),U2::float(),Ustride::integer(),Uorder::integer(),V1::float(),V2::float(),Vstride::integer(),Vorder::integer(),Points::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMap.xml">external</a> documentation.
+-spec map2d(enum(),float(),float(),integer(),integer(),float(),float(),integer(),integer(),binary()) -> ok.
map2d(Target,U1,U2,Ustride,Uorder,V1,V2,Vstride,Vorder,Points) ->
- wxe_util:send_bin(Points),
- wxe_util:cast(5181, <<Target:?GLenum,0:32,U1:?GLdouble,U2:?GLdouble,Ustride:?GLint,Uorder:?GLint,V1:?GLdouble,V2:?GLdouble,Vstride:?GLint,Vorder:?GLint>>).
+ send_bin(Points),
+ cast(5181, <<Target:?GLenum,0:32,U1:?GLdouble,U2:?GLdouble,Ustride:?GLint,Uorder:?GLint,V1:?GLdouble,V2:?GLdouble,Vstride:?GLint,Vorder:?GLint>>).
%% @spec (Target::enum(),U1::float(),U2::float(),Ustride::integer(),Uorder::integer(),V1::float(),V2::float(),Vstride::integer(),Vorder::integer(),Points::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMap.xml">external</a> documentation.
+-spec map2f(enum(),float(),float(),integer(),integer(),float(),float(),integer(),integer(),binary()) -> ok.
map2f(Target,U1,U2,Ustride,Uorder,V1,V2,Vstride,Vorder,Points) ->
- wxe_util:send_bin(Points),
- wxe_util:cast(5182, <<Target:?GLenum,U1:?GLfloat,U2:?GLfloat,Ustride:?GLint,Uorder:?GLint,V1:?GLfloat,V2:?GLfloat,Vstride:?GLint,Vorder:?GLint>>).
+ send_bin(Points),
+ cast(5182, <<Target:?GLenum,U1:?GLfloat,U2:?GLfloat,Ustride:?GLint,Uorder:?GLint,V1:?GLfloat,V2:?GLfloat,Vstride:?GLint,Vorder:?GLint>>).
%% @spec (Un::integer(),U1::float(),U2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMapGrid.xml">external</a> documentation.
+-spec mapGrid1d(integer(),float(),float()) -> ok.
mapGrid1d(Un,U1,U2) ->
- wxe_util:cast(5183, <<Un:?GLint,0:32,U1:?GLdouble,U2:?GLdouble>>).
+ cast(5183, <<Un:?GLint,0:32,U1:?GLdouble,U2:?GLdouble>>).
%% @spec (Un::integer(),U1::float(),U2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMapGrid.xml">external</a> documentation.
+-spec mapGrid1f(integer(),float(),float()) -> ok.
mapGrid1f(Un,U1,U2) ->
- wxe_util:cast(5184, <<Un:?GLint,U1:?GLfloat,U2:?GLfloat>>).
+ cast(5184, <<Un:?GLint,U1:?GLfloat,U2:?GLfloat>>).
%% @spec (Un::integer(),U1::float(),U2::float(),Vn::integer(),V1::float(),V2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMapGrid.xml">external</a> documentation.
+-spec mapGrid2d(integer(),float(),float(),integer(),float(),float()) -> ok.
mapGrid2d(Un,U1,U2,Vn,V1,V2) ->
- wxe_util:cast(5185, <<Un:?GLint,0:32,U1:?GLdouble,U2:?GLdouble,Vn:?GLint,0:32,V1:?GLdouble,V2:?GLdouble>>).
+ cast(5185, <<Un:?GLint,0:32,U1:?GLdouble,U2:?GLdouble,Vn:?GLint,0:32,V1:?GLdouble,V2:?GLdouble>>).
%% @spec (Un::integer(),U1::float(),U2::float(),Vn::integer(),V1::float(),V2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMapGrid.xml">external</a> documentation.
+-spec mapGrid2f(integer(),float(),float(),integer(),float(),float()) -> ok.
mapGrid2f(Un,U1,U2,Vn,V1,V2) ->
- wxe_util:cast(5186, <<Un:?GLint,U1:?GLfloat,U2:?GLfloat,Vn:?GLint,V1:?GLfloat,V2:?GLfloat>>).
+ cast(5186, <<Un:?GLint,U1:?GLfloat,U2:?GLfloat,Vn:?GLint,V1:?GLfloat,V2:?GLfloat>>).
%% @spec (Face::enum(),Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMaterial.xml">external</a> documentation.
+-spec materialf(enum(),enum(),float()) -> ok.
materialf(Face,Pname,Param) ->
- wxe_util:cast(5187, <<Face:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
+ cast(5187, <<Face:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Face::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMaterial.xml">external</a> documentation.
+-spec materialfv(enum(),enum(),{float()}) -> ok.
materialfv(Face,Pname,Params) ->
- wxe_util:cast(5188, <<Face:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5188, <<Face:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Face::enum(),Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMaterial.xml">external</a> documentation.
+-spec materiali(enum(),enum(),integer()) -> ok.
materiali(Face,Pname,Param) ->
- wxe_util:cast(5189, <<Face:?GLenum,Pname:?GLenum,Param:?GLint>>).
+ cast(5189, <<Face:?GLenum,Pname:?GLenum,Param:?GLint>>).
%% @spec (Face::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMaterial.xml">external</a> documentation.
+-spec materialiv(enum(),enum(),{integer()}) -> ok.
materialiv(Face,Pname,Params) ->
- wxe_util:cast(5190, <<Face:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5190, <<Face:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixMode.xml">external</a> documentation.
+-spec matrixMode(enum()) -> ok.
matrixMode(Mode) ->
- wxe_util:cast(5191, <<Mode:?GLenum>>).
+ cast(5191, <<Mode:?GLenum>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultMatrix.xml">external</a> documentation.
+-spec multMatrixd({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
multMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5192, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5192, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
multMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5192, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5192, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultMatrix.xml">external</a> documentation.
+-spec multMatrixf({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
multMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5193, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5193, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
multMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5193, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5193, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
%% @spec (List::integer(),Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNewList.xml">external</a> documentation.
+-spec newList(integer(),enum()) -> ok.
newList(List,Mode) ->
- wxe_util:cast(5194, <<List:?GLuint,Mode:?GLenum>>).
+ cast(5194, <<List:?GLuint,Mode:?GLenum>>).
%% @spec (Nx::integer(),Ny::integer(),Nz::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNormal.xml">external</a> documentation.
+-spec normal3b(integer(),integer(),integer()) -> ok.
normal3b(Nx,Ny,Nz) ->
- wxe_util:cast(5195, <<Nx:?GLbyte,Ny:?GLbyte,Nz:?GLbyte>>).
+ cast(5195, <<Nx:?GLbyte,Ny:?GLbyte,Nz:?GLbyte>>).
%% @spec ({Nx,Ny,Nz}) -> ok
%% @equiv normal3b(Nx,Ny,Nz)
+-spec normal3bv({integer(),integer(),integer()}) -> ok.
normal3bv({Nx,Ny,Nz}) -> normal3b(Nx,Ny,Nz).
%% @spec (Nx::float(),Ny::float(),Nz::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNormal.xml">external</a> documentation.
+-spec normal3d(float(),float(),float()) -> ok.
normal3d(Nx,Ny,Nz) ->
- wxe_util:cast(5196, <<Nx:?GLdouble,Ny:?GLdouble,Nz:?GLdouble>>).
+ cast(5196, <<Nx:?GLdouble,Ny:?GLdouble,Nz:?GLdouble>>).
%% @spec ({Nx,Ny,Nz}) -> ok
%% @equiv normal3d(Nx,Ny,Nz)
+-spec normal3dv({float(),float(),float()}) -> ok.
normal3dv({Nx,Ny,Nz}) -> normal3d(Nx,Ny,Nz).
%% @spec (Nx::float(),Ny::float(),Nz::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNormal.xml">external</a> documentation.
+-spec normal3f(float(),float(),float()) -> ok.
normal3f(Nx,Ny,Nz) ->
- wxe_util:cast(5197, <<Nx:?GLfloat,Ny:?GLfloat,Nz:?GLfloat>>).
+ cast(5197, <<Nx:?GLfloat,Ny:?GLfloat,Nz:?GLfloat>>).
%% @spec ({Nx,Ny,Nz}) -> ok
%% @equiv normal3f(Nx,Ny,Nz)
+-spec normal3fv({float(),float(),float()}) -> ok.
normal3fv({Nx,Ny,Nz}) -> normal3f(Nx,Ny,Nz).
%% @spec (Nx::integer(),Ny::integer(),Nz::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNormal.xml">external</a> documentation.
+-spec normal3i(integer(),integer(),integer()) -> ok.
normal3i(Nx,Ny,Nz) ->
- wxe_util:cast(5198, <<Nx:?GLint,Ny:?GLint,Nz:?GLint>>).
+ cast(5198, <<Nx:?GLint,Ny:?GLint,Nz:?GLint>>).
%% @spec ({Nx,Ny,Nz}) -> ok
%% @equiv normal3i(Nx,Ny,Nz)
+-spec normal3iv({integer(),integer(),integer()}) -> ok.
normal3iv({Nx,Ny,Nz}) -> normal3i(Nx,Ny,Nz).
%% @spec (Nx::integer(),Ny::integer(),Nz::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNormal.xml">external</a> documentation.
+-spec normal3s(integer(),integer(),integer()) -> ok.
normal3s(Nx,Ny,Nz) ->
- wxe_util:cast(5199, <<Nx:?GLshort,Ny:?GLshort,Nz:?GLshort>>).
+ cast(5199, <<Nx:?GLshort,Ny:?GLshort,Nz:?GLshort>>).
%% @spec ({Nx,Ny,Nz}) -> ok
%% @equiv normal3s(Nx,Ny,Nz)
+-spec normal3sv({integer(),integer(),integer()}) -> ok.
normal3sv({Nx,Ny,Nz}) -> normal3s(Nx,Ny,Nz).
-%% @spec (Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNormalPointer.xml">external</a> documentation.
+-spec normalPointer(enum(),integer(),offset()|mem()) -> ok.
normalPointer(Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5200, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5200, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
normalPointer(Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5201, <<Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5201, <<Type:?GLenum,Stride:?GLsizei>>).
%% @spec (Left::float(),Right::float(),Bottom::float(),Top::float(),ZNear::float(),ZFar::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glOrtho.xml">external</a> documentation.
+-spec ortho(float(),float(),float(),float(),float(),float()) -> ok.
ortho(Left,Right,Bottom,Top,ZNear,ZFar) ->
- wxe_util:cast(5202, <<Left:?GLdouble,Right:?GLdouble,Bottom:?GLdouble,Top:?GLdouble,ZNear:?GLdouble,ZFar:?GLdouble>>).
+ cast(5202, <<Left:?GLdouble,Right:?GLdouble,Bottom:?GLdouble,Top:?GLdouble,ZNear:?GLdouble,ZFar:?GLdouble>>).
%% @spec (Token::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPassThrough.xml">external</a> documentation.
+-spec passThrough(float()) -> ok.
passThrough(Token) ->
- wxe_util:cast(5203, <<Token:?GLfloat>>).
+ cast(5203, <<Token:?GLfloat>>).
%% @spec (Map::enum(),Mapsize::integer(),Values::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelMap.xml">external</a> documentation.
+-spec pixelMapfv(enum(),integer(),binary()) -> ok.
pixelMapfv(Map,Mapsize,Values) ->
- wxe_util:send_bin(Values),
- wxe_util:cast(5204, <<Map:?GLenum,Mapsize:?GLsizei>>).
+ send_bin(Values),
+ cast(5204, <<Map:?GLenum,Mapsize:?GLsizei>>).
%% @spec (Map::enum(),Mapsize::integer(),Values::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelMap.xml">external</a> documentation.
+-spec pixelMapuiv(enum(),integer(),binary()) -> ok.
pixelMapuiv(Map,Mapsize,Values) ->
- wxe_util:send_bin(Values),
- wxe_util:cast(5205, <<Map:?GLenum,Mapsize:?GLsizei>>).
+ send_bin(Values),
+ cast(5205, <<Map:?GLenum,Mapsize:?GLsizei>>).
%% @spec (Map::enum(),Mapsize::integer(),Values::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelMap.xml">external</a> documentation.
+-spec pixelMapusv(enum(),integer(),binary()) -> ok.
pixelMapusv(Map,Mapsize,Values) ->
- wxe_util:send_bin(Values),
- wxe_util:cast(5206, <<Map:?GLenum,Mapsize:?GLsizei>>).
+ send_bin(Values),
+ cast(5206, <<Map:?GLenum,Mapsize:?GLsizei>>).
%% @spec (Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelStore.xml">external</a> documentation.
+-spec pixelStoref(enum(),float()) -> ok.
pixelStoref(Pname,Param) ->
- wxe_util:cast(5207, <<Pname:?GLenum,Param:?GLfloat>>).
+ cast(5207, <<Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelStore.xml">external</a> documentation.
+-spec pixelStorei(enum(),integer()) -> ok.
pixelStorei(Pname,Param) ->
- wxe_util:cast(5208, <<Pname:?GLenum,Param:?GLint>>).
+ cast(5208, <<Pname:?GLenum,Param:?GLint>>).
%% @spec (Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelTransfer.xml">external</a> documentation.
+-spec pixelTransferf(enum(),float()) -> ok.
pixelTransferf(Pname,Param) ->
- wxe_util:cast(5209, <<Pname:?GLenum,Param:?GLfloat>>).
+ cast(5209, <<Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelTransfer.xml">external</a> documentation.
+-spec pixelTransferi(enum(),integer()) -> ok.
pixelTransferi(Pname,Param) ->
- wxe_util:cast(5210, <<Pname:?GLenum,Param:?GLint>>).
+ cast(5210, <<Pname:?GLenum,Param:?GLint>>).
%% @spec (Xfactor::float(),Yfactor::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPixelZoom.xml">external</a> documentation.
+-spec pixelZoom(float(),float()) -> ok.
pixelZoom(Xfactor,Yfactor) ->
- wxe_util:cast(5211, <<Xfactor:?GLfloat,Yfactor:?GLfloat>>).
+ cast(5211, <<Xfactor:?GLfloat,Yfactor:?GLfloat>>).
%% @spec (Size::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPointSize.xml">external</a> documentation.
+-spec pointSize(float()) -> ok.
pointSize(Size) ->
- wxe_util:cast(5212, <<Size:?GLfloat>>).
+ cast(5212, <<Size:?GLfloat>>).
%% @spec (Face::enum(),Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPolygonMode.xml">external</a> documentation.
+-spec polygonMode(enum(),enum()) -> ok.
polygonMode(Face,Mode) ->
- wxe_util:cast(5213, <<Face:?GLenum,Mode:?GLenum>>).
+ cast(5213, <<Face:?GLenum,Mode:?GLenum>>).
%% @spec (Factor::float(),Units::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPolygonOffset.xml">external</a> documentation.
+-spec polygonOffset(float(),float()) -> ok.
polygonOffset(Factor,Units) ->
- wxe_util:cast(5214, <<Factor:?GLfloat,Units:?GLfloat>>).
+ cast(5214, <<Factor:?GLfloat,Units:?GLfloat>>).
%% @spec (Mask::binary()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPolygonStipple.xml">external</a> documentation.
+-spec polygonStipple(binary()) -> ok.
polygonStipple(Mask) ->
- wxe_util:send_bin(Mask),
- wxe_util:cast(5215, <<>>).
+ send_bin(Mask),
+ cast(5215, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPopAttrib.xml">external</a> documentation.
+-spec popAttrib() -> ok.
popAttrib() ->
- wxe_util:cast(5216, <<>>).
+ cast(5216, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPopClientAttrib.xml">external</a> documentation.
+-spec popClientAttrib() -> ok.
popClientAttrib() ->
- wxe_util:cast(5217, <<>>).
+ cast(5217, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPopMatrix.xml">external</a> documentation.
+-spec popMatrix() -> ok.
popMatrix() ->
- wxe_util:cast(5218, <<>>).
+ cast(5218, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPopName.xml">external</a> documentation.
+-spec popName() -> ok.
popName() ->
- wxe_util:cast(5219, <<>>).
+ cast(5219, <<>>).
%% @spec (Textures::[integer()],Priorities::[clamp()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPrioritizeTextures.xml">external</a> documentation.
+-spec prioritizeTextures([integer()],[clamp()]) -> ok.
prioritizeTextures(Textures,Priorities) ->
- wxe_util:cast(5220, <<(length(Textures)):?GLuint,
+ cast(5220, <<(length(Textures)):?GLuint,
(<< <<C:?GLuint>> || C <- Textures>>)/binary,0:(((1+length(Textures)) rem 2)*32),(length(Priorities)):?GLuint,
(<< <<C:?GLclampf>> || C <- Priorities>>)/binary,0:(((1+length(Priorities)) rem 2)*32)>>).
%% @spec (Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPushAttrib.xml">external</a> documentation.
+-spec pushAttrib(integer()) -> ok.
pushAttrib(Mask) ->
- wxe_util:cast(5221, <<Mask:?GLbitfield>>).
+ cast(5221, <<Mask:?GLbitfield>>).
%% @spec (Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPushClientAttrib.xml">external</a> documentation.
+-spec pushClientAttrib(integer()) -> ok.
pushClientAttrib(Mask) ->
- wxe_util:cast(5222, <<Mask:?GLbitfield>>).
+ cast(5222, <<Mask:?GLbitfield>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPushMatrix.xml">external</a> documentation.
+-spec pushMatrix() -> ok.
pushMatrix() ->
- wxe_util:cast(5223, <<>>).
+ cast(5223, <<>>).
%% @spec (Name::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPushName.xml">external</a> documentation.
+-spec pushName(integer()) -> ok.
pushName(Name) ->
- wxe_util:cast(5224, <<Name:?GLuint>>).
+ cast(5224, <<Name:?GLuint>>).
%% @spec (X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos2d(float(),float()) -> ok.
rasterPos2d(X,Y) ->
- wxe_util:cast(5225, <<X:?GLdouble,Y:?GLdouble>>).
+ cast(5225, <<X:?GLdouble,Y:?GLdouble>>).
%% @spec ({X,Y}) -> ok
%% @equiv rasterPos2d(X,Y)
+-spec rasterPos2dv({float(),float()}) -> ok.
rasterPos2dv({X,Y}) -> rasterPos2d(X,Y).
%% @spec (X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos2f(float(),float()) -> ok.
rasterPos2f(X,Y) ->
- wxe_util:cast(5226, <<X:?GLfloat,Y:?GLfloat>>).
+ cast(5226, <<X:?GLfloat,Y:?GLfloat>>).
%% @spec ({X,Y}) -> ok
%% @equiv rasterPos2f(X,Y)
+-spec rasterPos2fv({float(),float()}) -> ok.
rasterPos2fv({X,Y}) -> rasterPos2f(X,Y).
%% @spec (X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos2i(integer(),integer()) -> ok.
rasterPos2i(X,Y) ->
- wxe_util:cast(5227, <<X:?GLint,Y:?GLint>>).
+ cast(5227, <<X:?GLint,Y:?GLint>>).
%% @spec ({X,Y}) -> ok
%% @equiv rasterPos2i(X,Y)
+-spec rasterPos2iv({integer(),integer()}) -> ok.
rasterPos2iv({X,Y}) -> rasterPos2i(X,Y).
%% @spec (X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos2s(integer(),integer()) -> ok.
rasterPos2s(X,Y) ->
- wxe_util:cast(5228, <<X:?GLshort,Y:?GLshort>>).
+ cast(5228, <<X:?GLshort,Y:?GLshort>>).
%% @spec ({X,Y}) -> ok
%% @equiv rasterPos2s(X,Y)
+-spec rasterPos2sv({integer(),integer()}) -> ok.
rasterPos2sv({X,Y}) -> rasterPos2s(X,Y).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos3d(float(),float(),float()) -> ok.
rasterPos3d(X,Y,Z) ->
- wxe_util:cast(5229, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5229, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv rasterPos3d(X,Y,Z)
+-spec rasterPos3dv({float(),float(),float()}) -> ok.
rasterPos3dv({X,Y,Z}) -> rasterPos3d(X,Y,Z).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos3f(float(),float(),float()) -> ok.
rasterPos3f(X,Y,Z) ->
- wxe_util:cast(5230, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5230, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv rasterPos3f(X,Y,Z)
+-spec rasterPos3fv({float(),float(),float()}) -> ok.
rasterPos3fv({X,Y,Z}) -> rasterPos3f(X,Y,Z).
%% @spec (X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos3i(integer(),integer(),integer()) -> ok.
rasterPos3i(X,Y,Z) ->
- wxe_util:cast(5231, <<X:?GLint,Y:?GLint,Z:?GLint>>).
+ cast(5231, <<X:?GLint,Y:?GLint,Z:?GLint>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv rasterPos3i(X,Y,Z)
+-spec rasterPos3iv({integer(),integer(),integer()}) -> ok.
rasterPos3iv({X,Y,Z}) -> rasterPos3i(X,Y,Z).
%% @spec (X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos3s(integer(),integer(),integer()) -> ok.
rasterPos3s(X,Y,Z) ->
- wxe_util:cast(5232, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
+ cast(5232, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv rasterPos3s(X,Y,Z)
+-spec rasterPos3sv({integer(),integer(),integer()}) -> ok.
rasterPos3sv({X,Y,Z}) -> rasterPos3s(X,Y,Z).
%% @spec (X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos4d(float(),float(),float(),float()) -> ok.
rasterPos4d(X,Y,Z,W) ->
- wxe_util:cast(5233, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5233, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv rasterPos4d(X,Y,Z,W)
+-spec rasterPos4dv({float(),float(),float(),float()}) -> ok.
rasterPos4dv({X,Y,Z,W}) -> rasterPos4d(X,Y,Z,W).
%% @spec (X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos4f(float(),float(),float(),float()) -> ok.
rasterPos4f(X,Y,Z,W) ->
- wxe_util:cast(5234, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5234, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv rasterPos4f(X,Y,Z,W)
+-spec rasterPos4fv({float(),float(),float(),float()}) -> ok.
rasterPos4fv({X,Y,Z,W}) -> rasterPos4f(X,Y,Z,W).
%% @spec (X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos4i(integer(),integer(),integer(),integer()) -> ok.
rasterPos4i(X,Y,Z,W) ->
- wxe_util:cast(5235, <<X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
+ cast(5235, <<X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv rasterPos4i(X,Y,Z,W)
+-spec rasterPos4iv({integer(),integer(),integer(),integer()}) -> ok.
rasterPos4iv({X,Y,Z,W}) -> rasterPos4i(X,Y,Z,W).
%% @spec (X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRasterPos.xml">external</a> documentation.
+-spec rasterPos4s(integer(),integer(),integer(),integer()) -> ok.
rasterPos4s(X,Y,Z,W) ->
- wxe_util:cast(5236, <<X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
+ cast(5236, <<X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv rasterPos4s(X,Y,Z,W)
+-spec rasterPos4sv({integer(),integer(),integer(),integer()}) -> ok.
rasterPos4sv({X,Y,Z,W}) -> rasterPos4s(X,Y,Z,W).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glReadBuffer.xml">external</a> documentation.
+-spec readBuffer(enum()) -> ok.
readBuffer(Mode) ->
- wxe_util:cast(5237, <<Mode:?GLenum>>).
+ cast(5237, <<Mode:?GLenum>>).
-%% @spec (X::integer(),Y::integer(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Pixels::wx:wx_mem()) -> ok
+%% @spec (X::integer(),Y::integer(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Pixels::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glReadPixels.xml">external</a> documentation.
+-spec readPixels(integer(),integer(),integer(),integer(),enum(),enum(),mem()) -> ok.
readPixels(X,Y,Width,Height,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels#wx_mem.bin),
- wxe_util:call(5238, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ call(5238, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (X1::float(),Y1::float(),X2::float(),Y2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rectd(float(),float(),float(),float()) -> ok.
rectd(X1,Y1,X2,Y2) ->
- wxe_util:cast(5239, <<X1:?GLdouble,Y1:?GLdouble,X2:?GLdouble,Y2:?GLdouble>>).
+ cast(5239, <<X1:?GLdouble,Y1:?GLdouble,X2:?GLdouble,Y2:?GLdouble>>).
-%% @spec (V1::{float()},V2::{float()}) -> ok
+%% @spec (V1::{float(),float()},V2::{float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rectdv({float(),float()},{float(),float()}) -> ok.
rectdv({V1,V2},{V1,V2}) ->
- wxe_util:cast(5240, <<V1:?GLdouble,V2:?GLdouble,V1:?GLdouble,V2:?GLdouble>>).
+ cast(5240, <<V1:?GLdouble,V2:?GLdouble,V1:?GLdouble,V2:?GLdouble>>).
%% @spec (X1::float(),Y1::float(),X2::float(),Y2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rectf(float(),float(),float(),float()) -> ok.
rectf(X1,Y1,X2,Y2) ->
- wxe_util:cast(5241, <<X1:?GLfloat,Y1:?GLfloat,X2:?GLfloat,Y2:?GLfloat>>).
+ cast(5241, <<X1:?GLfloat,Y1:?GLfloat,X2:?GLfloat,Y2:?GLfloat>>).
-%% @spec (V1::{float()},V2::{float()}) -> ok
+%% @spec (V1::{float(),float()},V2::{float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rectfv({float(),float()},{float(),float()}) -> ok.
rectfv({V1,V2},{V1,V2}) ->
- wxe_util:cast(5242, <<V1:?GLfloat,V2:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
+ cast(5242, <<V1:?GLfloat,V2:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
%% @spec (X1::integer(),Y1::integer(),X2::integer(),Y2::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec recti(integer(),integer(),integer(),integer()) -> ok.
recti(X1,Y1,X2,Y2) ->
- wxe_util:cast(5243, <<X1:?GLint,Y1:?GLint,X2:?GLint,Y2:?GLint>>).
+ cast(5243, <<X1:?GLint,Y1:?GLint,X2:?GLint,Y2:?GLint>>).
-%% @spec (V1::{integer()},V2::{integer()}) -> ok
+%% @spec (V1::{integer(),integer()},V2::{integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rectiv({integer(),integer()},{integer(),integer()}) -> ok.
rectiv({V1,V2},{V1,V2}) ->
- wxe_util:cast(5244, <<V1:?GLint,V2:?GLint,V1:?GLint,V2:?GLint>>).
+ cast(5244, <<V1:?GLint,V2:?GLint,V1:?GLint,V2:?GLint>>).
%% @spec (X1::integer(),Y1::integer(),X2::integer(),Y2::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rects(integer(),integer(),integer(),integer()) -> ok.
rects(X1,Y1,X2,Y2) ->
- wxe_util:cast(5245, <<X1:?GLshort,Y1:?GLshort,X2:?GLshort,Y2:?GLshort>>).
+ cast(5245, <<X1:?GLshort,Y1:?GLshort,X2:?GLshort,Y2:?GLshort>>).
-%% @spec (V1::{integer()},V2::{integer()}) -> ok
+%% @spec (V1::{integer(),integer()},V2::{integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRect.xml">external</a> documentation.
+-spec rectsv({integer(),integer()},{integer(),integer()}) -> ok.
rectsv({V1,V2},{V1,V2}) ->
- wxe_util:cast(5246, <<V1:?GLshort,V2:?GLshort,V1:?GLshort,V2:?GLshort>>).
+ cast(5246, <<V1:?GLshort,V2:?GLshort,V1:?GLshort,V2:?GLshort>>).
%% @spec (Mode::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRenderMode.xml">external</a> documentation.
+-spec renderMode(enum()) -> integer().
renderMode(Mode) ->
- wxe_util:call(5247, <<Mode:?GLenum>>).
+ call(5247, <<Mode:?GLenum>>).
%% @spec (Angle::float(),X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRotate.xml">external</a> documentation.
+-spec rotated(float(),float(),float(),float()) -> ok.
rotated(Angle,X,Y,Z) ->
- wxe_util:cast(5248, <<Angle:?GLdouble,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5248, <<Angle:?GLdouble,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec (Angle::float(),X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRotate.xml">external</a> documentation.
+-spec rotatef(float(),float(),float(),float()) -> ok.
rotatef(Angle,X,Y,Z) ->
- wxe_util:cast(5249, <<Angle:?GLfloat,X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5249, <<Angle:?GLfloat,X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScale.xml">external</a> documentation.
+-spec scaled(float(),float(),float()) -> ok.
scaled(X,Y,Z) ->
- wxe_util:cast(5250, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5250, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScale.xml">external</a> documentation.
+-spec scalef(float(),float(),float()) -> ok.
scalef(X,Y,Z) ->
- wxe_util:cast(5251, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5251, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec (X::integer(),Y::integer(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissor.xml">external</a> documentation.
+-spec scissor(integer(),integer(),integer(),integer()) -> ok.
scissor(X,Y,Width,Height) ->
- wxe_util:cast(5252, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5252, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
-%% @spec (Size::integer(),Buffer::wx:wx_mem()) -> ok
+%% @spec (Size::integer(),Buffer::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSelectBuffer.xml">external</a> documentation.
+-spec selectBuffer(integer(),mem()) -> ok.
selectBuffer(Size,Buffer) ->
- wxe_util:send_bin(Buffer#wx_mem.bin),
- wxe_util:call(5253, <<Size:?GLsizei>>).
+ send_bin(Buffer),
+ call(5253, <<Size:?GLsizei>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShadeModel.xml">external</a> documentation.
+-spec shadeModel(enum()) -> ok.
shadeModel(Mode) ->
- wxe_util:cast(5254, <<Mode:?GLenum>>).
+ cast(5254, <<Mode:?GLenum>>).
%% @spec (Func::enum(),Ref::integer(),Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilFunc.xml">external</a> documentation.
+-spec stencilFunc(enum(),integer(),integer()) -> ok.
stencilFunc(Func,Ref,Mask) ->
- wxe_util:cast(5255, <<Func:?GLenum,Ref:?GLint,Mask:?GLuint>>).
+ cast(5255, <<Func:?GLenum,Ref:?GLint,Mask:?GLuint>>).
%% @spec (Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilMask.xml">external</a> documentation.
+-spec stencilMask(integer()) -> ok.
stencilMask(Mask) ->
- wxe_util:cast(5256, <<Mask:?GLuint>>).
+ cast(5256, <<Mask:?GLuint>>).
%% @spec (Fail::enum(),Zfail::enum(),Zpass::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilOp.xml">external</a> documentation.
+-spec stencilOp(enum(),enum(),enum()) -> ok.
stencilOp(Fail,Zfail,Zpass) ->
- wxe_util:cast(5257, <<Fail:?GLenum,Zfail:?GLenum,Zpass:?GLenum>>).
+ cast(5257, <<Fail:?GLenum,Zfail:?GLenum,Zpass:?GLenum>>).
%% @spec (S::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord1d(float()) -> ok.
texCoord1d(S) ->
- wxe_util:cast(5258, <<S:?GLdouble>>).
+ cast(5258, <<S:?GLdouble>>).
%% @spec ({S}) -> ok
%% @equiv texCoord1d(S)
+-spec texCoord1dv({float()}) -> ok.
texCoord1dv({S}) -> texCoord1d(S).
%% @spec (S::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord1f(float()) -> ok.
texCoord1f(S) ->
- wxe_util:cast(5259, <<S:?GLfloat>>).
+ cast(5259, <<S:?GLfloat>>).
%% @spec ({S}) -> ok
%% @equiv texCoord1f(S)
+-spec texCoord1fv({float()}) -> ok.
texCoord1fv({S}) -> texCoord1f(S).
%% @spec (S::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord1i(integer()) -> ok.
texCoord1i(S) ->
- wxe_util:cast(5260, <<S:?GLint>>).
+ cast(5260, <<S:?GLint>>).
%% @spec ({S}) -> ok
%% @equiv texCoord1i(S)
+-spec texCoord1iv({integer()}) -> ok.
texCoord1iv({S}) -> texCoord1i(S).
%% @spec (S::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord1s(integer()) -> ok.
texCoord1s(S) ->
- wxe_util:cast(5261, <<S:?GLshort>>).
+ cast(5261, <<S:?GLshort>>).
%% @spec ({S}) -> ok
%% @equiv texCoord1s(S)
+-spec texCoord1sv({integer()}) -> ok.
texCoord1sv({S}) -> texCoord1s(S).
%% @spec (S::float(),T::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord2d(float(),float()) -> ok.
texCoord2d(S,T) ->
- wxe_util:cast(5262, <<S:?GLdouble,T:?GLdouble>>).
+ cast(5262, <<S:?GLdouble,T:?GLdouble>>).
%% @spec ({S,T}) -> ok
%% @equiv texCoord2d(S,T)
+-spec texCoord2dv({float(),float()}) -> ok.
texCoord2dv({S,T}) -> texCoord2d(S,T).
%% @spec (S::float(),T::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord2f(float(),float()) -> ok.
texCoord2f(S,T) ->
- wxe_util:cast(5263, <<S:?GLfloat,T:?GLfloat>>).
+ cast(5263, <<S:?GLfloat,T:?GLfloat>>).
%% @spec ({S,T}) -> ok
%% @equiv texCoord2f(S,T)
+-spec texCoord2fv({float(),float()}) -> ok.
texCoord2fv({S,T}) -> texCoord2f(S,T).
%% @spec (S::integer(),T::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord2i(integer(),integer()) -> ok.
texCoord2i(S,T) ->
- wxe_util:cast(5264, <<S:?GLint,T:?GLint>>).
+ cast(5264, <<S:?GLint,T:?GLint>>).
%% @spec ({S,T}) -> ok
%% @equiv texCoord2i(S,T)
+-spec texCoord2iv({integer(),integer()}) -> ok.
texCoord2iv({S,T}) -> texCoord2i(S,T).
%% @spec (S::integer(),T::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord2s(integer(),integer()) -> ok.
texCoord2s(S,T) ->
- wxe_util:cast(5265, <<S:?GLshort,T:?GLshort>>).
+ cast(5265, <<S:?GLshort,T:?GLshort>>).
%% @spec ({S,T}) -> ok
%% @equiv texCoord2s(S,T)
+-spec texCoord2sv({integer(),integer()}) -> ok.
texCoord2sv({S,T}) -> texCoord2s(S,T).
%% @spec (S::float(),T::float(),R::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord3d(float(),float(),float()) -> ok.
texCoord3d(S,T,R) ->
- wxe_util:cast(5266, <<S:?GLdouble,T:?GLdouble,R:?GLdouble>>).
+ cast(5266, <<S:?GLdouble,T:?GLdouble,R:?GLdouble>>).
%% @spec ({S,T,R}) -> ok
%% @equiv texCoord3d(S,T,R)
+-spec texCoord3dv({float(),float(),float()}) -> ok.
texCoord3dv({S,T,R}) -> texCoord3d(S,T,R).
%% @spec (S::float(),T::float(),R::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord3f(float(),float(),float()) -> ok.
texCoord3f(S,T,R) ->
- wxe_util:cast(5267, <<S:?GLfloat,T:?GLfloat,R:?GLfloat>>).
+ cast(5267, <<S:?GLfloat,T:?GLfloat,R:?GLfloat>>).
%% @spec ({S,T,R}) -> ok
%% @equiv texCoord3f(S,T,R)
+-spec texCoord3fv({float(),float(),float()}) -> ok.
texCoord3fv({S,T,R}) -> texCoord3f(S,T,R).
%% @spec (S::integer(),T::integer(),R::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord3i(integer(),integer(),integer()) -> ok.
texCoord3i(S,T,R) ->
- wxe_util:cast(5268, <<S:?GLint,T:?GLint,R:?GLint>>).
+ cast(5268, <<S:?GLint,T:?GLint,R:?GLint>>).
%% @spec ({S,T,R}) -> ok
%% @equiv texCoord3i(S,T,R)
+-spec texCoord3iv({integer(),integer(),integer()}) -> ok.
texCoord3iv({S,T,R}) -> texCoord3i(S,T,R).
%% @spec (S::integer(),T::integer(),R::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord3s(integer(),integer(),integer()) -> ok.
texCoord3s(S,T,R) ->
- wxe_util:cast(5269, <<S:?GLshort,T:?GLshort,R:?GLshort>>).
+ cast(5269, <<S:?GLshort,T:?GLshort,R:?GLshort>>).
%% @spec ({S,T,R}) -> ok
%% @equiv texCoord3s(S,T,R)
+-spec texCoord3sv({integer(),integer(),integer()}) -> ok.
texCoord3sv({S,T,R}) -> texCoord3s(S,T,R).
%% @spec (S::float(),T::float(),R::float(),Q::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord4d(float(),float(),float(),float()) -> ok.
texCoord4d(S,T,R,Q) ->
- wxe_util:cast(5270, <<S:?GLdouble,T:?GLdouble,R:?GLdouble,Q:?GLdouble>>).
+ cast(5270, <<S:?GLdouble,T:?GLdouble,R:?GLdouble,Q:?GLdouble>>).
%% @spec ({S,T,R,Q}) -> ok
%% @equiv texCoord4d(S,T,R,Q)
+-spec texCoord4dv({float(),float(),float(),float()}) -> ok.
texCoord4dv({S,T,R,Q}) -> texCoord4d(S,T,R,Q).
%% @spec (S::float(),T::float(),R::float(),Q::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord4f(float(),float(),float(),float()) -> ok.
texCoord4f(S,T,R,Q) ->
- wxe_util:cast(5271, <<S:?GLfloat,T:?GLfloat,R:?GLfloat,Q:?GLfloat>>).
+ cast(5271, <<S:?GLfloat,T:?GLfloat,R:?GLfloat,Q:?GLfloat>>).
%% @spec ({S,T,R,Q}) -> ok
%% @equiv texCoord4f(S,T,R,Q)
+-spec texCoord4fv({float(),float(),float(),float()}) -> ok.
texCoord4fv({S,T,R,Q}) -> texCoord4f(S,T,R,Q).
%% @spec (S::integer(),T::integer(),R::integer(),Q::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord4i(integer(),integer(),integer(),integer()) -> ok.
texCoord4i(S,T,R,Q) ->
- wxe_util:cast(5272, <<S:?GLint,T:?GLint,R:?GLint,Q:?GLint>>).
+ cast(5272, <<S:?GLint,T:?GLint,R:?GLint,Q:?GLint>>).
%% @spec ({S,T,R,Q}) -> ok
%% @equiv texCoord4i(S,T,R,Q)
+-spec texCoord4iv({integer(),integer(),integer(),integer()}) -> ok.
texCoord4iv({S,T,R,Q}) -> texCoord4i(S,T,R,Q).
%% @spec (S::integer(),T::integer(),R::integer(),Q::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoord.xml">external</a> documentation.
+-spec texCoord4s(integer(),integer(),integer(),integer()) -> ok.
texCoord4s(S,T,R,Q) ->
- wxe_util:cast(5273, <<S:?GLshort,T:?GLshort,R:?GLshort,Q:?GLshort>>).
+ cast(5273, <<S:?GLshort,T:?GLshort,R:?GLshort,Q:?GLshort>>).
%% @spec ({S,T,R,Q}) -> ok
%% @equiv texCoord4s(S,T,R,Q)
+-spec texCoord4sv({integer(),integer(),integer(),integer()}) -> ok.
texCoord4sv({S,T,R,Q}) -> texCoord4s(S,T,R,Q).
-%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexCoordPointer.xml">external</a> documentation.
+-spec texCoordPointer(integer(),enum(),integer(),offset()|mem()) -> ok.
texCoordPointer(Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5274, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5274, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
texCoordPointer(Size,Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5275, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5275, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @spec (Target::enum(),Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexEnvf.xml">external</a> documentation.
+-spec texEnvf(enum(),enum(),float()) -> ok.
texEnvf(Target,Pname,Param) ->
- wxe_util:cast(5276, <<Target:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
+ cast(5276, <<Target:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Target::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml">external</a> documentation.
+-spec texEnvfv(enum(),enum(),{float()}) -> ok.
texEnvfv(Target,Pname,Params) ->
- wxe_util:cast(5277, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5277, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Target::enum(),Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexEnvi.xml">external</a> documentation.
+-spec texEnvi(enum(),enum(),integer()) -> ok.
texEnvi(Target,Pname,Param) ->
- wxe_util:cast(5278, <<Target:?GLenum,Pname:?GLenum,Param:?GLint>>).
+ cast(5278, <<Target:?GLenum,Pname:?GLenum,Param:?GLint>>).
%% @spec (Target::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml">external</a> documentation.
+-spec texEnviv(enum(),enum(),{integer()}) -> ok.
texEnviv(Target,Pname,Params) ->
- wxe_util:cast(5279, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5279, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Coord::enum(),Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexGen.xml">external</a> documentation.
+-spec texGend(enum(),enum(),float()) -> ok.
texGend(Coord,Pname,Param) ->
- wxe_util:cast(5280, <<Coord:?GLenum,Pname:?GLenum,Param:?GLdouble>>).
+ cast(5280, <<Coord:?GLenum,Pname:?GLenum,Param:?GLdouble>>).
%% @spec (Coord::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexGen.xml">external</a> documentation.
+-spec texGendv(enum(),enum(),{float()}) -> ok.
texGendv(Coord,Pname,Params) ->
- wxe_util:cast(5281, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,0:32,
+ cast(5281, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,0:32,
(<< <<C:?GLdouble>> ||C <- tuple_to_list(Params)>>)/binary>>).
%% @spec (Coord::enum(),Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexGen.xml">external</a> documentation.
+-spec texGenf(enum(),enum(),float()) -> ok.
texGenf(Coord,Pname,Param) ->
- wxe_util:cast(5282, <<Coord:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
+ cast(5282, <<Coord:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Coord::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexGen.xml">external</a> documentation.
+-spec texGenfv(enum(),enum(),{float()}) -> ok.
texGenfv(Coord,Pname,Params) ->
- wxe_util:cast(5283, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5283, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Coord::enum(),Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexGen.xml">external</a> documentation.
+-spec texGeni(enum(),enum(),integer()) -> ok.
texGeni(Coord,Pname,Param) ->
- wxe_util:cast(5284, <<Coord:?GLenum,Pname:?GLenum,Param:?GLint>>).
+ cast(5284, <<Coord:?GLenum,Pname:?GLenum,Param:?GLint>>).
%% @spec (Coord::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexGen.xml">external</a> documentation.
+-spec texGeniv(enum(),enum(),{integer()}) -> ok.
texGeniv(Coord,Pname,Params) ->
- wxe_util:cast(5285, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5285, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
-%% @spec (Target::enum(),Level::integer(),Internalformat::integer(),Width::integer(),Border::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Internalformat::integer(),Width::integer(),Border::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage1D.xml">external</a> documentation.
+-spec texImage1D(enum(),integer(),integer(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
texImage1D(Target,Level,Internalformat,Width,Border,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5286, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5286, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
texImage1D(Target,Level,Internalformat,Width,Border,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5287, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5287, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Level::integer(),Internalformat::integer(),Width::integer(),Height::integer(),Border::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Internalformat::integer(),Width::integer(),Height::integer(),Border::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2D.xml">external</a> documentation.
+-spec texImage2D(enum(),integer(),integer(),integer(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
texImage2D(Target,Level,Internalformat,Width,Height,Border,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5288, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5288, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
texImage2D(Target,Level,Internalformat,Width,Height,Border,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5289, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5289, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml">external</a> documentation.
+-spec texParameterf(enum(),enum(),float()) -> ok.
texParameterf(Target,Pname,Param) ->
- wxe_util:cast(5290, <<Target:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
+ cast(5290, <<Target:?GLenum,Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Target::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml">external</a> documentation.
+-spec texParameterfv(enum(),enum(),{float()}) -> ok.
texParameterfv(Target,Pname,Params) ->
- wxe_util:cast(5291, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5291, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Target::enum(),Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml">external</a> documentation.
+-spec texParameteri(enum(),enum(),integer()) -> ok.
texParameteri(Target,Pname,Param) ->
- wxe_util:cast(5292, <<Target:?GLenum,Pname:?GLenum,Param:?GLint>>).
+ cast(5292, <<Target:?GLenum,Pname:?GLenum,Param:?GLint>>).
%% @spec (Target::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameter.xml">external</a> documentation.
+-spec texParameteriv(enum(),enum(),{integer()}) -> ok.
texParameteriv(Target,Pname,Params) ->
- wxe_util:cast(5293, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5293, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
-%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Width::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Width::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage1D.xml">external</a> documentation.
+-spec texSubImage1D(enum(),integer(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
texSubImage1D(Target,Level,Xoffset,Width,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5294, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5294, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
texSubImage1D(Target,Level,Xoffset,Width,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5295, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5295, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage2D.xml">external</a> documentation.
+-spec texSubImage2D(enum(),integer(),integer(),integer(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
texSubImage2D(Target,Level,Xoffset,Yoffset,Width,Height,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5296, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5296, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
texSubImage2D(Target,Level,Xoffset,Yoffset,Width,Height,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5297, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5297, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTranslate.xml">external</a> documentation.
+-spec translated(float(),float(),float()) -> ok.
translated(X,Y,Z) ->
- wxe_util:cast(5298, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5298, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTranslate.xml">external</a> documentation.
+-spec translatef(float(),float(),float()) -> ok.
translatef(X,Y,Z) ->
- wxe_util:cast(5299, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5299, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec (X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex2d(float(),float()) -> ok.
vertex2d(X,Y) ->
- wxe_util:cast(5300, <<X:?GLdouble,Y:?GLdouble>>).
+ cast(5300, <<X:?GLdouble,Y:?GLdouble>>).
%% @spec ({X,Y}) -> ok
%% @equiv vertex2d(X,Y)
+-spec vertex2dv({float(),float()}) -> ok.
vertex2dv({X,Y}) -> vertex2d(X,Y).
%% @spec (X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex2f(float(),float()) -> ok.
vertex2f(X,Y) ->
- wxe_util:cast(5301, <<X:?GLfloat,Y:?GLfloat>>).
+ cast(5301, <<X:?GLfloat,Y:?GLfloat>>).
%% @spec ({X,Y}) -> ok
%% @equiv vertex2f(X,Y)
+-spec vertex2fv({float(),float()}) -> ok.
vertex2fv({X,Y}) -> vertex2f(X,Y).
%% @spec (X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex2i(integer(),integer()) -> ok.
vertex2i(X,Y) ->
- wxe_util:cast(5302, <<X:?GLint,Y:?GLint>>).
+ cast(5302, <<X:?GLint,Y:?GLint>>).
%% @spec ({X,Y}) -> ok
%% @equiv vertex2i(X,Y)
+-spec vertex2iv({integer(),integer()}) -> ok.
vertex2iv({X,Y}) -> vertex2i(X,Y).
%% @spec (X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex2s(integer(),integer()) -> ok.
vertex2s(X,Y) ->
- wxe_util:cast(5303, <<X:?GLshort,Y:?GLshort>>).
+ cast(5303, <<X:?GLshort,Y:?GLshort>>).
%% @spec ({X,Y}) -> ok
%% @equiv vertex2s(X,Y)
+-spec vertex2sv({integer(),integer()}) -> ok.
vertex2sv({X,Y}) -> vertex2s(X,Y).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex3d(float(),float(),float()) -> ok.
vertex3d(X,Y,Z) ->
- wxe_util:cast(5304, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5304, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv vertex3d(X,Y,Z)
+-spec vertex3dv({float(),float(),float()}) -> ok.
vertex3dv({X,Y,Z}) -> vertex3d(X,Y,Z).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex3f(float(),float(),float()) -> ok.
vertex3f(X,Y,Z) ->
- wxe_util:cast(5305, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5305, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv vertex3f(X,Y,Z)
+-spec vertex3fv({float(),float(),float()}) -> ok.
vertex3fv({X,Y,Z}) -> vertex3f(X,Y,Z).
%% @spec (X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex3i(integer(),integer(),integer()) -> ok.
vertex3i(X,Y,Z) ->
- wxe_util:cast(5306, <<X:?GLint,Y:?GLint,Z:?GLint>>).
+ cast(5306, <<X:?GLint,Y:?GLint,Z:?GLint>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv vertex3i(X,Y,Z)
+-spec vertex3iv({integer(),integer(),integer()}) -> ok.
vertex3iv({X,Y,Z}) -> vertex3i(X,Y,Z).
%% @spec (X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex3s(integer(),integer(),integer()) -> ok.
vertex3s(X,Y,Z) ->
- wxe_util:cast(5307, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
+ cast(5307, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv vertex3s(X,Y,Z)
+-spec vertex3sv({integer(),integer(),integer()}) -> ok.
vertex3sv({X,Y,Z}) -> vertex3s(X,Y,Z).
%% @spec (X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex4d(float(),float(),float(),float()) -> ok.
vertex4d(X,Y,Z,W) ->
- wxe_util:cast(5308, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5308, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv vertex4d(X,Y,Z,W)
+-spec vertex4dv({float(),float(),float(),float()}) -> ok.
vertex4dv({X,Y,Z,W}) -> vertex4d(X,Y,Z,W).
%% @spec (X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex4f(float(),float(),float(),float()) -> ok.
vertex4f(X,Y,Z,W) ->
- wxe_util:cast(5309, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5309, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv vertex4f(X,Y,Z,W)
+-spec vertex4fv({float(),float(),float(),float()}) -> ok.
vertex4fv({X,Y,Z,W}) -> vertex4f(X,Y,Z,W).
%% @spec (X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex4i(integer(),integer(),integer(),integer()) -> ok.
vertex4i(X,Y,Z,W) ->
- wxe_util:cast(5310, <<X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
+ cast(5310, <<X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv vertex4i(X,Y,Z,W)
+-spec vertex4iv({integer(),integer(),integer(),integer()}) -> ok.
vertex4iv({X,Y,Z,W}) -> vertex4i(X,Y,Z,W).
%% @spec (X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertex.xml">external</a> documentation.
+-spec vertex4s(integer(),integer(),integer(),integer()) -> ok.
vertex4s(X,Y,Z,W) ->
- wxe_util:cast(5311, <<X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
+ cast(5311, <<X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv vertex4s(X,Y,Z,W)
+-spec vertex4sv({integer(),integer(),integer(),integer()}) -> ok.
vertex4sv({X,Y,Z,W}) -> vertex4s(X,Y,Z,W).
-%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexPointer.xml">external</a> documentation.
+-spec vertexPointer(integer(),enum(),integer(),offset()|mem()) -> ok.
vertexPointer(Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5312, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5312, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
vertexPointer(Size,Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5313, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5313, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @spec (X::integer(),Y::integer(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glViewport.xml">external</a> documentation.
+-spec viewport(integer(),integer(),integer(),integer()) -> ok.
viewport(X,Y,Width,Height) ->
- wxe_util:cast(5314, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5314, <<X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
%% @spec (Red::clamp(),Green::clamp(),Blue::clamp(),Alpha::clamp()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendColor.xml">external</a> documentation.
+-spec blendColor(clamp(),clamp(),clamp(),clamp()) -> ok.
blendColor(Red,Green,Blue,Alpha) ->
- wxe_util:cast(5315, <<Red:?GLclampf,Green:?GLclampf,Blue:?GLclampf,Alpha:?GLclampf>>).
+ cast(5315, <<Red:?GLclampf,Green:?GLclampf,Blue:?GLclampf,Alpha:?GLclampf>>).
%% @spec (Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquation.xml">external</a> documentation.
+-spec blendEquation(enum()) -> ok.
blendEquation(Mode) ->
- wxe_util:cast(5316, <<Mode:?GLenum>>).
+ cast(5316, <<Mode:?GLenum>>).
-%% @spec (Mode::enum(),Start::integer(),End::integer(),Count::integer(),Type::enum(),Indices::offset()|binary()) -> ok
+%% @spec (Mode::enum(),Start::integer(),End::integer(),Count::integer(),Type::enum(),Indices::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawRangeElements.xml">external</a> documentation.
+-spec drawRangeElements(enum(),integer(),integer(),integer(),enum(),offset()|mem()) -> ok.
drawRangeElements(Mode,Start,End,Count,Type,Indices) when is_integer(Indices) ->
- wxe_util:cast(5317, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Indices:?GLuint>>);
+ cast(5317, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Indices:?GLuint>>);
drawRangeElements(Mode,Start,End,Count,Type,Indices) ->
- wxe_util:send_bin(Indices),
- wxe_util:cast(5318, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum>>).
+ send_bin(Indices),
+ cast(5318, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum>>).
-%% @spec (Target::enum(),Level::integer(),Internalformat::integer(),Width::integer(),Height::integer(),Depth::integer(),Border::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Internalformat::integer(),Width::integer(),Height::integer(),Depth::integer(),Border::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage3D.xml">external</a> documentation.
+-spec texImage3D(enum(),integer(),integer(),integer(),integer(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
texImage3D(Target,Level,Internalformat,Width,Height,Depth,Border,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5319, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5319, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
texImage3D(Target,Level,Internalformat,Width,Height,Depth,Border,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5320, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5320, <<Target:?GLenum,Level:?GLint,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Zoffset::integer(),Width::integer(),Height::integer(),Depth::integer(),Format::enum(),Type::enum(),Pixels::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Zoffset::integer(),Width::integer(),Height::integer(),Depth::integer(),Format::enum(),Type::enum(),Pixels::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexSubImage3D.xml">external</a> documentation.
+-spec texSubImage3D(enum(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
texSubImage3D(Target,Level,Xoffset,Yoffset,Zoffset,Width,Height,Depth,Format,Type,Pixels) when is_integer(Pixels) ->
- wxe_util:cast(5321, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
+ cast(5321, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum,Pixels:?GLuint>>);
texSubImage3D(Target,Level,Xoffset,Yoffset,Zoffset,Width,Height,Depth,Format,Type,Pixels) ->
- wxe_util:send_bin(Pixels),
- wxe_util:cast(5322, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Pixels),
+ cast(5322, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Zoffset::integer(),X::integer(),Y::integer(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyTexSubImage3D.xml">external</a> documentation.
+-spec copyTexSubImage3D(enum(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()) -> ok.
copyTexSubImage3D(Target,Level,Xoffset,Yoffset,Zoffset,X,Y,Width,Height) ->
- wxe_util:cast(5323, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5323, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
-%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Format::enum(),Type::enum(),Table::offset()|binary()) -> ok
+%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Format::enum(),Type::enum(),Table::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorTable.xml">external</a> documentation.
+-spec colorTable(enum(),enum(),integer(),enum(),enum(),offset()|mem()) -> ok.
colorTable(Target,Internalformat,Width,Format,Type,Table) when is_integer(Table) ->
- wxe_util:cast(5324, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Table:?GLuint>>);
+ cast(5324, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Table:?GLuint>>);
colorTable(Target,Internalformat,Width,Format,Type,Table) ->
- wxe_util:send_bin(Table),
- wxe_util:cast(5325, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Table),
+ cast(5325, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum(),Params::{float()}) -> ok
+%% @spec (Target::enum(),Pname::enum(),Params::{float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorTableParameter.xml">external</a> documentation.
+-spec colorTableParameterfv(enum(),enum(),{float(),float(),float(),float()}) -> ok.
colorTableParameterfv(Target,Pname,{P1,P2,P3,P4}) ->
- wxe_util:cast(5326, <<Target:?GLenum,Pname:?GLenum,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
+ cast(5326, <<Target:?GLenum,Pname:?GLenum,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
-%% @spec (Target::enum(),Pname::enum(),Params::{integer()}) -> ok
+%% @spec (Target::enum(),Pname::enum(),Params::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorTableParameter.xml">external</a> documentation.
+-spec colorTableParameteriv(enum(),enum(),{integer(),integer(),integer(),integer()}) -> ok.
colorTableParameteriv(Target,Pname,{P1,P2,P3,P4}) ->
- wxe_util:cast(5327, <<Target:?GLenum,Pname:?GLenum,P1:?GLint,P2:?GLint,P3:?GLint,P4:?GLint>>).
+ cast(5327, <<Target:?GLenum,Pname:?GLenum,P1:?GLint,P2:?GLint,P3:?GLint,P4:?GLint>>).
%% @spec (Target::enum(),Internalformat::enum(),X::integer(),Y::integer(),Width::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyColorTable.xml">external</a> documentation.
+-spec copyColorTable(enum(),enum(),integer(),integer(),integer()) -> ok.
copyColorTable(Target,Internalformat,X,Y,Width) ->
- wxe_util:cast(5328, <<Target:?GLenum,Internalformat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei>>).
+ cast(5328, <<Target:?GLenum,Internalformat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei>>).
-%% @spec (Target::enum(),Format::enum(),Type::enum(),Table::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Format::enum(),Type::enum(),Table::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetColorTable.xml">external</a> documentation.
+-spec getColorTable(enum(),enum(),enum(),mem()) -> ok.
getColorTable(Target,Format,Type,Table) ->
- wxe_util:send_bin(Table#wx_mem.bin),
- wxe_util:call(5329, <<Target:?GLenum,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Table),
+ call(5329, <<Target:?GLenum,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {float()}
+%% @spec (Target::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetColorTableParameter.xml">external</a> documentation.
+-spec getColorTableParameterfv(enum(),enum()) -> {float(),float(),float(),float()}.
getColorTableParameterfv(Target,Pname) ->
- wxe_util:call(5330, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5330, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {integer()}
+%% @spec (Target::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetColorTableParameter.xml">external</a> documentation.
+-spec getColorTableParameteriv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getColorTableParameteriv(Target,Pname) ->
- wxe_util:call(5331, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5331, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Start::integer(),Count::integer(),Format::enum(),Type::enum(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Start::integer(),Count::integer(),Format::enum(),Type::enum(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorSubTable.xml">external</a> documentation.
+-spec colorSubTable(enum(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
colorSubTable(Target,Start,Count,Format,Type,Data) when is_integer(Data) ->
- wxe_util:cast(5332, <<Target:?GLenum,Start:?GLsizei,Count:?GLsizei,Format:?GLenum,Type:?GLenum,Data:?GLuint>>);
+ cast(5332, <<Target:?GLenum,Start:?GLsizei,Count:?GLsizei,Format:?GLenum,Type:?GLenum,Data:?GLuint>>);
colorSubTable(Target,Start,Count,Format,Type,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5333, <<Target:?GLenum,Start:?GLsizei,Count:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Data),
+ cast(5333, <<Target:?GLenum,Start:?GLsizei,Count:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Start::integer(),X::integer(),Y::integer(),Width::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyColorSubTable.xml">external</a> documentation.
+-spec copyColorSubTable(enum(),integer(),integer(),integer(),integer()) -> ok.
copyColorSubTable(Target,Start,X,Y,Width) ->
- wxe_util:cast(5334, <<Target:?GLenum,Start:?GLsizei,X:?GLint,Y:?GLint,Width:?GLsizei>>).
+ cast(5334, <<Target:?GLenum,Start:?GLsizei,X:?GLint,Y:?GLint,Width:?GLsizei>>).
-%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Format::enum(),Type::enum(),Image::offset()|binary()) -> ok
+%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Format::enum(),Type::enum(),Image::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glConvolutionFilter1D.xml">external</a> documentation.
+-spec convolutionFilter1D(enum(),enum(),integer(),enum(),enum(),offset()|mem()) -> ok.
convolutionFilter1D(Target,Internalformat,Width,Format,Type,Image) when is_integer(Image) ->
- wxe_util:cast(5335, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Image:?GLuint>>);
+ cast(5335, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Image:?GLuint>>);
convolutionFilter1D(Target,Internalformat,Width,Format,Type,Image) ->
- wxe_util:send_bin(Image),
- wxe_util:cast(5336, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Image),
+ cast(5336, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Image::offset()|binary()) -> ok
+%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Image::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glConvolutionFilter2D.xml">external</a> documentation.
+-spec convolutionFilter2D(enum(),enum(),integer(),integer(),enum(),enum(),offset()|mem()) -> ok.
convolutionFilter2D(Target,Internalformat,Width,Height,Format,Type,Image) when is_integer(Image) ->
- wxe_util:cast(5337, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Image:?GLuint>>);
+ cast(5337, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Image:?GLuint>>);
convolutionFilter2D(Target,Internalformat,Width,Height,Format,Type,Image) ->
- wxe_util:send_bin(Image),
- wxe_util:cast(5338, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Image),
+ cast(5338, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glConvolutionParameter.xml">external</a> documentation.
+-spec convolutionParameterf(enum(),enum(),{float()}) -> ok.
convolutionParameterf(Target,Pname,Params) ->
- wxe_util:cast(5339, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5339, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Target,Pname,{Params}) -> ok
%% @equiv convolutionParameterf(Target,Pname,Params)
+-spec convolutionParameterfv(enum(),enum(),{{float()}}) -> ok.
convolutionParameterfv(Target,Pname,{Params}) -> convolutionParameterf(Target,Pname,Params).
%% @spec (Target::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glConvolutionParameter.xml">external</a> documentation.
+-spec convolutionParameteri(enum(),enum(),{integer()}) -> ok.
convolutionParameteri(Target,Pname,Params) ->
- wxe_util:cast(5340, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5340, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Target,Pname,{Params}) -> ok
%% @equiv convolutionParameteri(Target,Pname,Params)
+-spec convolutionParameteriv(enum(),enum(),{{integer()}}) -> ok.
convolutionParameteriv(Target,Pname,{Params}) -> convolutionParameteri(Target,Pname,Params).
%% @spec (Target::enum(),Internalformat::enum(),X::integer(),Y::integer(),Width::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyConvolutionFilter1D.xml">external</a> documentation.
+-spec copyConvolutionFilter1D(enum(),enum(),integer(),integer(),integer()) -> ok.
copyConvolutionFilter1D(Target,Internalformat,X,Y,Width) ->
- wxe_util:cast(5341, <<Target:?GLenum,Internalformat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei>>).
+ cast(5341, <<Target:?GLenum,Internalformat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei>>).
%% @spec (Target::enum(),Internalformat::enum(),X::integer(),Y::integer(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyConvolutionFilter2D.xml">external</a> documentation.
+-spec copyConvolutionFilter2D(enum(),enum(),integer(),integer(),integer(),integer()) -> ok.
copyConvolutionFilter2D(Target,Internalformat,X,Y,Width,Height) ->
- wxe_util:cast(5342, <<Target:?GLenum,Internalformat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5342, <<Target:?GLenum,Internalformat:?GLenum,X:?GLint,Y:?GLint,Width:?GLsizei,Height:?GLsizei>>).
-%% @spec (Target::enum(),Format::enum(),Type::enum(),Image::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Format::enum(),Type::enum(),Image::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetConvolutionFilter.xml">external</a> documentation.
+-spec getConvolutionFilter(enum(),enum(),enum(),mem()) -> ok.
getConvolutionFilter(Target,Format,Type,Image) ->
- wxe_util:send_bin(Image#wx_mem.bin),
- wxe_util:call(5343, <<Target:?GLenum,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Image),
+ call(5343, <<Target:?GLenum,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {float()}
+%% @spec (Target::enum(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetConvolutionParameter.xml">external</a> documentation.
+-spec getConvolutionParameterfv(enum(),enum()) -> {float(),float(),float(),float()}.
getConvolutionParameterfv(Target,Pname) ->
- wxe_util:call(5344, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5344, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {integer()}
+%% @spec (Target::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetConvolutionParameter.xml">external</a> documentation.
+-spec getConvolutionParameteriv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getConvolutionParameteriv(Target,Pname) ->
- wxe_util:call(5345, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5345, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Row::offset()|binary(),Column::offset()|binary()) -> ok
+%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Row::offset()|mem(),Column::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSeparableFilter2D.xml">external</a> documentation.
+-spec separableFilter2D(enum(),enum(),integer(),integer(),enum(),enum(),offset()|mem(),offset()|mem()) -> ok.
separableFilter2D(Target,Internalformat,Width,Height,Format,Type,Row,Column) when is_integer(Row), is_integer(Column) ->
- wxe_util:cast(5346, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Row:?GLuint,Column:?GLuint>>);
+ cast(5346, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Row:?GLuint,Column:?GLuint>>);
separableFilter2D(Target,Internalformat,Width,Height,Format,Type,Row,Column) ->
- wxe_util:send_bin(Row),
- wxe_util:send_bin(Column),
- wxe_util:cast(5347, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Row),
+ send_bin(Column),
+ cast(5347, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
-%% @spec (Target::enum(),Reset::0|1,Format::enum(),Type::enum(),Values::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Reset::0|1,Format::enum(),Type::enum(),Values::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetHistogram.xml">external</a> documentation.
+-spec getHistogram(enum(),0|1,enum(),enum(),mem()) -> ok.
getHistogram(Target,Reset,Format,Type,Values) ->
- wxe_util:send_bin(Values#wx_mem.bin),
- wxe_util:call(5348, <<Target:?GLenum,Reset:?GLboolean,0:24,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Values),
+ call(5348, <<Target:?GLenum,Reset:?GLboolean,0:24,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Pname::enum()) -> {float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetHistogramParameter.xml">external</a> documentation.
+-spec getHistogramParameterfv(enum(),enum()) -> {float()}.
getHistogramParameterfv(Target,Pname) ->
- wxe_util:call(5349, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5349, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Target::enum(),Pname::enum()) -> {integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetHistogramParameter.xml">external</a> documentation.
+-spec getHistogramParameteriv(enum(),enum()) -> {integer()}.
getHistogramParameteriv(Target,Pname) ->
- wxe_util:call(5350, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5350, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Reset::0|1,Format::enum(),Type::enum(),Values::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Reset::0|1,Format::enum(),Type::enum(),Values::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMinmax.xml">external</a> documentation.
+-spec getMinmax(enum(),0|1,enum(),enum(),mem()) -> ok.
getMinmax(Target,Reset,Format,Type,Values) ->
- wxe_util:send_bin(Values#wx_mem.bin),
- wxe_util:call(5351, <<Target:?GLenum,Reset:?GLboolean,0:24,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Values),
+ call(5351, <<Target:?GLenum,Reset:?GLboolean,0:24,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),Pname::enum()) -> {float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMinmaxParameter.xml">external</a> documentation.
+-spec getMinmaxParameterfv(enum(),enum()) -> {float()}.
getMinmaxParameterfv(Target,Pname) ->
- wxe_util:call(5352, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5352, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Target::enum(),Pname::enum()) -> {integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMinmaxParameter.xml">external</a> documentation.
+-spec getMinmaxParameteriv(enum(),enum()) -> {integer()}.
getMinmaxParameteriv(Target,Pname) ->
- wxe_util:call(5353, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5353, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Target::enum(),Width::integer(),Internalformat::enum(),Sink::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glHistogram.xml">external</a> documentation.
+-spec histogram(enum(),integer(),enum(),0|1) -> ok.
histogram(Target,Width,Internalformat,Sink) ->
- wxe_util:cast(5354, <<Target:?GLenum,Width:?GLsizei,Internalformat:?GLenum,Sink:?GLboolean>>).
+ cast(5354, <<Target:?GLenum,Width:?GLsizei,Internalformat:?GLenum,Sink:?GLboolean>>).
%% @spec (Target::enum(),Internalformat::enum(),Sink::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMinmax.xml">external</a> documentation.
+-spec minmax(enum(),enum(),0|1) -> ok.
minmax(Target,Internalformat,Sink) ->
- wxe_util:cast(5355, <<Target:?GLenum,Internalformat:?GLenum,Sink:?GLboolean>>).
+ cast(5355, <<Target:?GLenum,Internalformat:?GLenum,Sink:?GLboolean>>).
%% @spec (Target::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glResetHistogram.xml">external</a> documentation.
+-spec resetHistogram(enum()) -> ok.
resetHistogram(Target) ->
- wxe_util:cast(5356, <<Target:?GLenum>>).
+ cast(5356, <<Target:?GLenum>>).
%% @spec (Target::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glResetMinmax.xml">external</a> documentation.
+-spec resetMinmax(enum()) -> ok.
resetMinmax(Target) ->
- wxe_util:cast(5357, <<Target:?GLenum>>).
+ cast(5357, <<Target:?GLenum>>).
%% @spec (Texture::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glActiveTexture.xml">external</a> documentation.
+-spec activeTexture(enum()) -> ok.
activeTexture(Texture) ->
- wxe_util:cast(5358, <<Texture:?GLenum>>).
+ cast(5358, <<Texture:?GLenum>>).
%% @spec (Value::clamp(),Invert::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSampleCoverage.xml">external</a> documentation.
+-spec sampleCoverage(clamp(),0|1) -> ok.
sampleCoverage(Value,Invert) ->
- wxe_util:cast(5359, <<Value:?GLclampf,Invert:?GLboolean>>).
+ cast(5359, <<Value:?GLclampf,Invert:?GLboolean>>).
-%% @spec (Target::enum(),Level::integer(),Internalformat::enum(),Width::integer(),Height::integer(),Depth::integer(),Border::integer(),ImageSize::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Internalformat::enum(),Width::integer(),Height::integer(),Depth::integer(),Border::integer(),ImageSize::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage3D.xml">external</a> documentation.
+-spec compressedTexImage3D(enum(),integer(),enum(),integer(),integer(),integer(),integer(),integer(),offset()|mem()) -> ok.
compressedTexImage3D(Target,Level,Internalformat,Width,Height,Depth,Border,ImageSize,Data) when is_integer(Data) ->
- wxe_util:cast(5360, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,ImageSize:?GLsizei,Data:?GLuint>>);
+ cast(5360, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,ImageSize:?GLsizei,Data:?GLuint>>);
compressedTexImage3D(Target,Level,Internalformat,Width,Height,Depth,Border,ImageSize,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5361, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,ImageSize:?GLsizei>>).
+ send_bin(Data),
+ cast(5361, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Border:?GLint,ImageSize:?GLsizei>>).
-%% @spec (Target::enum(),Level::integer(),Internalformat::enum(),Width::integer(),Height::integer(),Border::integer(),ImageSize::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Internalformat::enum(),Width::integer(),Height::integer(),Border::integer(),ImageSize::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage2D.xml">external</a> documentation.
+-spec compressedTexImage2D(enum(),integer(),enum(),integer(),integer(),integer(),integer(),offset()|mem()) -> ok.
compressedTexImage2D(Target,Level,Internalformat,Width,Height,Border,ImageSize,Data) when is_integer(Data) ->
- wxe_util:cast(5362, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Border:?GLint,ImageSize:?GLsizei,Data:?GLuint>>);
+ cast(5362, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Border:?GLint,ImageSize:?GLsizei,Data:?GLuint>>);
compressedTexImage2D(Target,Level,Internalformat,Width,Height,Border,ImageSize,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5363, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Border:?GLint,ImageSize:?GLsizei>>).
+ send_bin(Data),
+ cast(5363, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei,Border:?GLint,ImageSize:?GLsizei>>).
-%% @spec (Target::enum(),Level::integer(),Internalformat::enum(),Width::integer(),Border::integer(),ImageSize::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Internalformat::enum(),Width::integer(),Border::integer(),ImageSize::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexImage1D.xml">external</a> documentation.
+-spec compressedTexImage1D(enum(),integer(),enum(),integer(),integer(),integer(),offset()|mem()) -> ok.
compressedTexImage1D(Target,Level,Internalformat,Width,Border,ImageSize,Data) when is_integer(Data) ->
- wxe_util:cast(5364, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Border:?GLint,ImageSize:?GLsizei,Data:?GLuint>>);
+ cast(5364, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Border:?GLint,ImageSize:?GLsizei,Data:?GLuint>>);
compressedTexImage1D(Target,Level,Internalformat,Width,Border,ImageSize,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5365, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Border:?GLint,ImageSize:?GLsizei>>).
+ send_bin(Data),
+ cast(5365, <<Target:?GLenum,Level:?GLint,Internalformat:?GLenum,Width:?GLsizei,Border:?GLint,ImageSize:?GLsizei>>).
-%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Zoffset::integer(),Width::integer(),Height::integer(),Depth::integer(),Format::enum(),ImageSize::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Zoffset::integer(),Width::integer(),Height::integer(),Depth::integer(),Format::enum(),ImageSize::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexSubImage3D.xml">external</a> documentation.
+-spec compressedTexSubImage3D(enum(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),enum(),integer(),offset()|mem()) -> ok.
compressedTexSubImage3D(Target,Level,Xoffset,Yoffset,Zoffset,Width,Height,Depth,Format,ImageSize,Data) when is_integer(Data) ->
- wxe_util:cast(5366, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,ImageSize:?GLsizei,Data:?GLuint>>);
+ cast(5366, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,ImageSize:?GLsizei,Data:?GLuint>>);
compressedTexSubImage3D(Target,Level,Xoffset,Yoffset,Zoffset,Width,Height,Depth,Format,ImageSize,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5367, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,ImageSize:?GLsizei>>).
+ send_bin(Data),
+ cast(5367, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Zoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,ImageSize:?GLsizei>>).
-%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Width::integer(),Height::integer(),Format::enum(),ImageSize::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Yoffset::integer(),Width::integer(),Height::integer(),Format::enum(),ImageSize::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexSubImage2D.xml">external</a> documentation.
+-spec compressedTexSubImage2D(enum(),integer(),integer(),integer(),integer(),integer(),enum(),integer(),offset()|mem()) -> ok.
compressedTexSubImage2D(Target,Level,Xoffset,Yoffset,Width,Height,Format,ImageSize,Data) when is_integer(Data) ->
- wxe_util:cast(5368, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,ImageSize:?GLsizei,Data:?GLuint>>);
+ cast(5368, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,ImageSize:?GLsizei,Data:?GLuint>>);
compressedTexSubImage2D(Target,Level,Xoffset,Yoffset,Width,Height,Format,ImageSize,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5369, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,ImageSize:?GLsizei>>).
+ send_bin(Data),
+ cast(5369, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Yoffset:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,ImageSize:?GLsizei>>).
-%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Width::integer(),Format::enum(),ImageSize::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Level::integer(),Xoffset::integer(),Width::integer(),Format::enum(),ImageSize::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompressedTexSubImage1D.xml">external</a> documentation.
+-spec compressedTexSubImage1D(enum(),integer(),integer(),integer(),enum(),integer(),offset()|mem()) -> ok.
compressedTexSubImage1D(Target,Level,Xoffset,Width,Format,ImageSize,Data) when is_integer(Data) ->
- wxe_util:cast(5370, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,ImageSize:?GLsizei,Data:?GLuint>>);
+ cast(5370, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,ImageSize:?GLsizei,Data:?GLuint>>);
compressedTexSubImage1D(Target,Level,Xoffset,Width,Format,ImageSize,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5371, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,ImageSize:?GLsizei>>).
+ send_bin(Data),
+ cast(5371, <<Target:?GLenum,Level:?GLint,Xoffset:?GLint,Width:?GLsizei,Format:?GLenum,ImageSize:?GLsizei>>).
-%% @spec (Target::enum(),Level::integer(),Img::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Level::integer(),Img::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetCompressedTexImage.xml">external</a> documentation.
+-spec getCompressedTexImage(enum(),integer(),mem()) -> ok.
getCompressedTexImage(Target,Level,Img) ->
- wxe_util:send_bin(Img#wx_mem.bin),
- wxe_util:call(5372, <<Target:?GLenum,Level:?GLint>>).
+ send_bin(Img),
+ call(5372, <<Target:?GLenum,Level:?GLint>>).
%% @spec (Texture::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClientActiveTexture.xml">external</a> documentation.
+-spec clientActiveTexture(enum()) -> ok.
clientActiveTexture(Texture) ->
- wxe_util:cast(5373, <<Texture:?GLenum>>).
+ cast(5373, <<Texture:?GLenum>>).
%% @spec (Target::enum(),S::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord1d(enum(),float()) -> ok.
multiTexCoord1d(Target,S) ->
- wxe_util:cast(5374, <<Target:?GLenum,0:32,S:?GLdouble>>).
+ cast(5374, <<Target:?GLenum,0:32,S:?GLdouble>>).
%% @spec (Target,{S}) -> ok
%% @equiv multiTexCoord1d(Target,S)
+-spec multiTexCoord1dv(enum(),{float()}) -> ok.
multiTexCoord1dv(Target,{S}) -> multiTexCoord1d(Target,S).
%% @spec (Target::enum(),S::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord1f(enum(),float()) -> ok.
multiTexCoord1f(Target,S) ->
- wxe_util:cast(5375, <<Target:?GLenum,S:?GLfloat>>).
+ cast(5375, <<Target:?GLenum,S:?GLfloat>>).
%% @spec (Target,{S}) -> ok
%% @equiv multiTexCoord1f(Target,S)
+-spec multiTexCoord1fv(enum(),{float()}) -> ok.
multiTexCoord1fv(Target,{S}) -> multiTexCoord1f(Target,S).
%% @spec (Target::enum(),S::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord1i(enum(),integer()) -> ok.
multiTexCoord1i(Target,S) ->
- wxe_util:cast(5376, <<Target:?GLenum,S:?GLint>>).
+ cast(5376, <<Target:?GLenum,S:?GLint>>).
%% @spec (Target,{S}) -> ok
%% @equiv multiTexCoord1i(Target,S)
+-spec multiTexCoord1iv(enum(),{integer()}) -> ok.
multiTexCoord1iv(Target,{S}) -> multiTexCoord1i(Target,S).
%% @spec (Target::enum(),S::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord1s(enum(),integer()) -> ok.
multiTexCoord1s(Target,S) ->
- wxe_util:cast(5377, <<Target:?GLenum,S:?GLshort>>).
+ cast(5377, <<Target:?GLenum,S:?GLshort>>).
%% @spec (Target,{S}) -> ok
%% @equiv multiTexCoord1s(Target,S)
+-spec multiTexCoord1sv(enum(),{integer()}) -> ok.
multiTexCoord1sv(Target,{S}) -> multiTexCoord1s(Target,S).
%% @spec (Target::enum(),S::float(),T::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord2d(enum(),float(),float()) -> ok.
multiTexCoord2d(Target,S,T) ->
- wxe_util:cast(5378, <<Target:?GLenum,0:32,S:?GLdouble,T:?GLdouble>>).
+ cast(5378, <<Target:?GLenum,0:32,S:?GLdouble,T:?GLdouble>>).
%% @spec (Target,{S,T}) -> ok
%% @equiv multiTexCoord2d(Target,S,T)
+-spec multiTexCoord2dv(enum(),{float(),float()}) -> ok.
multiTexCoord2dv(Target,{S,T}) -> multiTexCoord2d(Target,S,T).
%% @spec (Target::enum(),S::float(),T::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord2f(enum(),float(),float()) -> ok.
multiTexCoord2f(Target,S,T) ->
- wxe_util:cast(5379, <<Target:?GLenum,S:?GLfloat,T:?GLfloat>>).
+ cast(5379, <<Target:?GLenum,S:?GLfloat,T:?GLfloat>>).
%% @spec (Target,{S,T}) -> ok
%% @equiv multiTexCoord2f(Target,S,T)
+-spec multiTexCoord2fv(enum(),{float(),float()}) -> ok.
multiTexCoord2fv(Target,{S,T}) -> multiTexCoord2f(Target,S,T).
%% @spec (Target::enum(),S::integer(),T::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord2i(enum(),integer(),integer()) -> ok.
multiTexCoord2i(Target,S,T) ->
- wxe_util:cast(5380, <<Target:?GLenum,S:?GLint,T:?GLint>>).
+ cast(5380, <<Target:?GLenum,S:?GLint,T:?GLint>>).
%% @spec (Target,{S,T}) -> ok
%% @equiv multiTexCoord2i(Target,S,T)
+-spec multiTexCoord2iv(enum(),{integer(),integer()}) -> ok.
multiTexCoord2iv(Target,{S,T}) -> multiTexCoord2i(Target,S,T).
%% @spec (Target::enum(),S::integer(),T::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord2s(enum(),integer(),integer()) -> ok.
multiTexCoord2s(Target,S,T) ->
- wxe_util:cast(5381, <<Target:?GLenum,S:?GLshort,T:?GLshort>>).
+ cast(5381, <<Target:?GLenum,S:?GLshort,T:?GLshort>>).
%% @spec (Target,{S,T}) -> ok
%% @equiv multiTexCoord2s(Target,S,T)
+-spec multiTexCoord2sv(enum(),{integer(),integer()}) -> ok.
multiTexCoord2sv(Target,{S,T}) -> multiTexCoord2s(Target,S,T).
%% @spec (Target::enum(),S::float(),T::float(),R::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord3d(enum(),float(),float(),float()) -> ok.
multiTexCoord3d(Target,S,T,R) ->
- wxe_util:cast(5382, <<Target:?GLenum,0:32,S:?GLdouble,T:?GLdouble,R:?GLdouble>>).
+ cast(5382, <<Target:?GLenum,0:32,S:?GLdouble,T:?GLdouble,R:?GLdouble>>).
%% @spec (Target,{S,T,R}) -> ok
%% @equiv multiTexCoord3d(Target,S,T,R)
+-spec multiTexCoord3dv(enum(),{float(),float(),float()}) -> ok.
multiTexCoord3dv(Target,{S,T,R}) -> multiTexCoord3d(Target,S,T,R).
%% @spec (Target::enum(),S::float(),T::float(),R::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord3f(enum(),float(),float(),float()) -> ok.
multiTexCoord3f(Target,S,T,R) ->
- wxe_util:cast(5383, <<Target:?GLenum,S:?GLfloat,T:?GLfloat,R:?GLfloat>>).
+ cast(5383, <<Target:?GLenum,S:?GLfloat,T:?GLfloat,R:?GLfloat>>).
%% @spec (Target,{S,T,R}) -> ok
%% @equiv multiTexCoord3f(Target,S,T,R)
+-spec multiTexCoord3fv(enum(),{float(),float(),float()}) -> ok.
multiTexCoord3fv(Target,{S,T,R}) -> multiTexCoord3f(Target,S,T,R).
%% @spec (Target::enum(),S::integer(),T::integer(),R::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord3i(enum(),integer(),integer(),integer()) -> ok.
multiTexCoord3i(Target,S,T,R) ->
- wxe_util:cast(5384, <<Target:?GLenum,S:?GLint,T:?GLint,R:?GLint>>).
+ cast(5384, <<Target:?GLenum,S:?GLint,T:?GLint,R:?GLint>>).
%% @spec (Target,{S,T,R}) -> ok
%% @equiv multiTexCoord3i(Target,S,T,R)
+-spec multiTexCoord3iv(enum(),{integer(),integer(),integer()}) -> ok.
multiTexCoord3iv(Target,{S,T,R}) -> multiTexCoord3i(Target,S,T,R).
%% @spec (Target::enum(),S::integer(),T::integer(),R::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord3s(enum(),integer(),integer(),integer()) -> ok.
multiTexCoord3s(Target,S,T,R) ->
- wxe_util:cast(5385, <<Target:?GLenum,S:?GLshort,T:?GLshort,R:?GLshort>>).
+ cast(5385, <<Target:?GLenum,S:?GLshort,T:?GLshort,R:?GLshort>>).
%% @spec (Target,{S,T,R}) -> ok
%% @equiv multiTexCoord3s(Target,S,T,R)
+-spec multiTexCoord3sv(enum(),{integer(),integer(),integer()}) -> ok.
multiTexCoord3sv(Target,{S,T,R}) -> multiTexCoord3s(Target,S,T,R).
%% @spec (Target::enum(),S::float(),T::float(),R::float(),Q::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord4d(enum(),float(),float(),float(),float()) -> ok.
multiTexCoord4d(Target,S,T,R,Q) ->
- wxe_util:cast(5386, <<Target:?GLenum,0:32,S:?GLdouble,T:?GLdouble,R:?GLdouble,Q:?GLdouble>>).
+ cast(5386, <<Target:?GLenum,0:32,S:?GLdouble,T:?GLdouble,R:?GLdouble,Q:?GLdouble>>).
%% @spec (Target,{S,T,R,Q}) -> ok
%% @equiv multiTexCoord4d(Target,S,T,R,Q)
+-spec multiTexCoord4dv(enum(),{float(),float(),float(),float()}) -> ok.
multiTexCoord4dv(Target,{S,T,R,Q}) -> multiTexCoord4d(Target,S,T,R,Q).
%% @spec (Target::enum(),S::float(),T::float(),R::float(),Q::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord4f(enum(),float(),float(),float(),float()) -> ok.
multiTexCoord4f(Target,S,T,R,Q) ->
- wxe_util:cast(5387, <<Target:?GLenum,S:?GLfloat,T:?GLfloat,R:?GLfloat,Q:?GLfloat>>).
+ cast(5387, <<Target:?GLenum,S:?GLfloat,T:?GLfloat,R:?GLfloat,Q:?GLfloat>>).
%% @spec (Target,{S,T,R,Q}) -> ok
%% @equiv multiTexCoord4f(Target,S,T,R,Q)
+-spec multiTexCoord4fv(enum(),{float(),float(),float(),float()}) -> ok.
multiTexCoord4fv(Target,{S,T,R,Q}) -> multiTexCoord4f(Target,S,T,R,Q).
%% @spec (Target::enum(),S::integer(),T::integer(),R::integer(),Q::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord4i(enum(),integer(),integer(),integer(),integer()) -> ok.
multiTexCoord4i(Target,S,T,R,Q) ->
- wxe_util:cast(5388, <<Target:?GLenum,S:?GLint,T:?GLint,R:?GLint,Q:?GLint>>).
+ cast(5388, <<Target:?GLenum,S:?GLint,T:?GLint,R:?GLint,Q:?GLint>>).
%% @spec (Target,{S,T,R,Q}) -> ok
%% @equiv multiTexCoord4i(Target,S,T,R,Q)
+-spec multiTexCoord4iv(enum(),{integer(),integer(),integer(),integer()}) -> ok.
multiTexCoord4iv(Target,{S,T,R,Q}) -> multiTexCoord4i(Target,S,T,R,Q).
%% @spec (Target::enum(),S::integer(),T::integer(),R::integer(),Q::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml">external</a> documentation.
+-spec multiTexCoord4s(enum(),integer(),integer(),integer(),integer()) -> ok.
multiTexCoord4s(Target,S,T,R,Q) ->
- wxe_util:cast(5389, <<Target:?GLenum,S:?GLshort,T:?GLshort,R:?GLshort,Q:?GLshort>>).
+ cast(5389, <<Target:?GLenum,S:?GLshort,T:?GLshort,R:?GLshort,Q:?GLshort>>).
%% @spec (Target,{S,T,R,Q}) -> ok
%% @equiv multiTexCoord4s(Target,S,T,R,Q)
+-spec multiTexCoord4sv(enum(),{integer(),integer(),integer(),integer()}) -> ok.
multiTexCoord4sv(Target,{S,T,R,Q}) -> multiTexCoord4s(Target,S,T,R,Q).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadTransposeMatrix.xml">external</a> documentation.
+-spec loadTransposeMatrixf({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
loadTransposeMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5390, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5390, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
loadTransposeMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5390, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5390, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadTransposeMatrix.xml">external</a> documentation.
+-spec loadTransposeMatrixd({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
loadTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5391, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5391, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
loadTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5391, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5391, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultTransposeMatrix.xml">external</a> documentation.
+-spec multTransposeMatrixf({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
multTransposeMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5392, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5392, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
multTransposeMatrixf({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5392, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5392, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultTransposeMatrix.xml">external</a> documentation.
+-spec multTransposeMatrixd({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
multTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5393, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5393, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
multTransposeMatrixd({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5393, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5393, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
%% @spec (SfactorRGB::enum(),DfactorRGB::enum(),SfactorAlpha::enum(),DfactorAlpha::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendFuncSeparate.xml">external</a> documentation.
+-spec blendFuncSeparate(enum(),enum(),enum(),enum()) -> ok.
blendFuncSeparate(SfactorRGB,DfactorRGB,SfactorAlpha,DfactorAlpha) ->
- wxe_util:cast(5394, <<SfactorRGB:?GLenum,DfactorRGB:?GLenum,SfactorAlpha:?GLenum,DfactorAlpha:?GLenum>>).
+ cast(5394, <<SfactorRGB:?GLenum,DfactorRGB:?GLenum,SfactorAlpha:?GLenum,DfactorAlpha:?GLenum>>).
%% @spec (Mode::enum(),First::[integer()],Count::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultiDrawArrays.xml">external</a> documentation.
+-spec multiDrawArrays(enum(),[integer()],[integer()]) -> ok.
multiDrawArrays(Mode,First,Count) ->
- wxe_util:cast(5395, <<Mode:?GLenum,(length(First)):?GLuint,
+ cast(5395, <<Mode:?GLenum,(length(First)):?GLuint,
(<< <<C:?GLint>> || C <- First>>)/binary,0:(((length(First)) rem 2)*32),(length(Count)):?GLuint,
(<< <<C:?GLsizei>> || C <- Count>>)/binary,0:(((1+length(Count)) rem 2)*32)>>).
%% @spec (Pname::enum(),Param::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPointParameter.xml">external</a> documentation.
+-spec pointParameterf(enum(),float()) -> ok.
pointParameterf(Pname,Param) ->
- wxe_util:cast(5396, <<Pname:?GLenum,Param:?GLfloat>>).
+ cast(5396, <<Pname:?GLenum,Param:?GLfloat>>).
%% @spec (Pname::enum(),Params::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPointParameter.xml">external</a> documentation.
+-spec pointParameterfv(enum(),{float()}) -> ok.
pointParameterfv(Pname,Params) ->
- wxe_util:cast(5397, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5397, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @spec (Pname::enum(),Param::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPointParameter.xml">external</a> documentation.
+-spec pointParameteri(enum(),integer()) -> ok.
pointParameteri(Pname,Param) ->
- wxe_util:cast(5398, <<Pname:?GLenum,Param:?GLint>>).
+ cast(5398, <<Pname:?GLenum,Param:?GLint>>).
%% @spec (Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPointParameter.xml">external</a> documentation.
+-spec pointParameteriv(enum(),{integer()}) -> ok.
pointParameteriv(Pname,Params) ->
- wxe_util:cast(5399, <<Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5399, <<Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>).
%% @spec (Coord::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFogCoord.xml">external</a> documentation.
+-spec fogCoordf(float()) -> ok.
fogCoordf(Coord) ->
- wxe_util:cast(5400, <<Coord:?GLfloat>>).
+ cast(5400, <<Coord:?GLfloat>>).
%% @spec ({Coord}) -> ok
%% @equiv fogCoordf(Coord)
+-spec fogCoordfv({float()}) -> ok.
fogCoordfv({Coord}) -> fogCoordf(Coord).
%% @spec (Coord::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFogCoord.xml">external</a> documentation.
+-spec fogCoordd(float()) -> ok.
fogCoordd(Coord) ->
- wxe_util:cast(5401, <<Coord:?GLdouble>>).
+ cast(5401, <<Coord:?GLdouble>>).
%% @spec ({Coord}) -> ok
%% @equiv fogCoordd(Coord)
+-spec fogCoorddv({float()}) -> ok.
fogCoorddv({Coord}) -> fogCoordd(Coord).
-%% @spec (Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFogCoordPointer.xml">external</a> documentation.
+-spec fogCoordPointer(enum(),integer(),offset()|mem()) -> ok.
fogCoordPointer(Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5402, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5402, <<Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
fogCoordPointer(Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5403, <<Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5403, <<Type:?GLenum,Stride:?GLsizei>>).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3b(integer(),integer(),integer()) -> ok.
secondaryColor3b(Red,Green,Blue) ->
- wxe_util:cast(5404, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte>>).
+ cast(5404, <<Red:?GLbyte,Green:?GLbyte,Blue:?GLbyte>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3b(Red,Green,Blue)
+-spec secondaryColor3bv({integer(),integer(),integer()}) -> ok.
secondaryColor3bv({Red,Green,Blue}) -> secondaryColor3b(Red,Green,Blue).
%% @spec (Red::float(),Green::float(),Blue::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3d(float(),float(),float()) -> ok.
secondaryColor3d(Red,Green,Blue) ->
- wxe_util:cast(5405, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble>>).
+ cast(5405, <<Red:?GLdouble,Green:?GLdouble,Blue:?GLdouble>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3d(Red,Green,Blue)
+-spec secondaryColor3dv({float(),float(),float()}) -> ok.
secondaryColor3dv({Red,Green,Blue}) -> secondaryColor3d(Red,Green,Blue).
%% @spec (Red::float(),Green::float(),Blue::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3f(float(),float(),float()) -> ok.
secondaryColor3f(Red,Green,Blue) ->
- wxe_util:cast(5406, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat>>).
+ cast(5406, <<Red:?GLfloat,Green:?GLfloat,Blue:?GLfloat>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3f(Red,Green,Blue)
+-spec secondaryColor3fv({float(),float(),float()}) -> ok.
secondaryColor3fv({Red,Green,Blue}) -> secondaryColor3f(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3i(integer(),integer(),integer()) -> ok.
secondaryColor3i(Red,Green,Blue) ->
- wxe_util:cast(5407, <<Red:?GLint,Green:?GLint,Blue:?GLint>>).
+ cast(5407, <<Red:?GLint,Green:?GLint,Blue:?GLint>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3i(Red,Green,Blue)
+-spec secondaryColor3iv({integer(),integer(),integer()}) -> ok.
secondaryColor3iv({Red,Green,Blue}) -> secondaryColor3i(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3s(integer(),integer(),integer()) -> ok.
secondaryColor3s(Red,Green,Blue) ->
- wxe_util:cast(5408, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort>>).
+ cast(5408, <<Red:?GLshort,Green:?GLshort,Blue:?GLshort>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3s(Red,Green,Blue)
+-spec secondaryColor3sv({integer(),integer(),integer()}) -> ok.
secondaryColor3sv({Red,Green,Blue}) -> secondaryColor3s(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3ub(integer(),integer(),integer()) -> ok.
secondaryColor3ub(Red,Green,Blue) ->
- wxe_util:cast(5409, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte>>).
+ cast(5409, <<Red:?GLubyte,Green:?GLubyte,Blue:?GLubyte>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3ub(Red,Green,Blue)
+-spec secondaryColor3ubv({integer(),integer(),integer()}) -> ok.
secondaryColor3ubv({Red,Green,Blue}) -> secondaryColor3ub(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3ui(integer(),integer(),integer()) -> ok.
secondaryColor3ui(Red,Green,Blue) ->
- wxe_util:cast(5410, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint>>).
+ cast(5410, <<Red:?GLuint,Green:?GLuint,Blue:?GLuint>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3ui(Red,Green,Blue)
+-spec secondaryColor3uiv({integer(),integer(),integer()}) -> ok.
secondaryColor3uiv({Red,Green,Blue}) -> secondaryColor3ui(Red,Green,Blue).
%% @spec (Red::integer(),Green::integer(),Blue::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColor.xml">external</a> documentation.
+-spec secondaryColor3us(integer(),integer(),integer()) -> ok.
secondaryColor3us(Red,Green,Blue) ->
- wxe_util:cast(5411, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort>>).
+ cast(5411, <<Red:?GLushort,Green:?GLushort,Blue:?GLushort>>).
%% @spec ({Red,Green,Blue}) -> ok
%% @equiv secondaryColor3us(Red,Green,Blue)
+-spec secondaryColor3usv({integer(),integer(),integer()}) -> ok.
secondaryColor3usv({Red,Green,Blue}) -> secondaryColor3us(Red,Green,Blue).
-%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSecondaryColorPointer.xml">external</a> documentation.
+-spec secondaryColorPointer(integer(),enum(),integer(),offset()|mem()) -> ok.
secondaryColorPointer(Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5412, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5412, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
secondaryColorPointer(Size,Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5413, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5413, <<Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
%% @spec (X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos2d(float(),float()) -> ok.
windowPos2d(X,Y) ->
- wxe_util:cast(5414, <<X:?GLdouble,Y:?GLdouble>>).
+ cast(5414, <<X:?GLdouble,Y:?GLdouble>>).
%% @spec ({X,Y}) -> ok
%% @equiv windowPos2d(X,Y)
+-spec windowPos2dv({float(),float()}) -> ok.
windowPos2dv({X,Y}) -> windowPos2d(X,Y).
%% @spec (X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos2f(float(),float()) -> ok.
windowPos2f(X,Y) ->
- wxe_util:cast(5415, <<X:?GLfloat,Y:?GLfloat>>).
+ cast(5415, <<X:?GLfloat,Y:?GLfloat>>).
%% @spec ({X,Y}) -> ok
%% @equiv windowPos2f(X,Y)
+-spec windowPos2fv({float(),float()}) -> ok.
windowPos2fv({X,Y}) -> windowPos2f(X,Y).
%% @spec (X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos2i(integer(),integer()) -> ok.
windowPos2i(X,Y) ->
- wxe_util:cast(5416, <<X:?GLint,Y:?GLint>>).
+ cast(5416, <<X:?GLint,Y:?GLint>>).
%% @spec ({X,Y}) -> ok
%% @equiv windowPos2i(X,Y)
+-spec windowPos2iv({integer(),integer()}) -> ok.
windowPos2iv({X,Y}) -> windowPos2i(X,Y).
%% @spec (X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos2s(integer(),integer()) -> ok.
windowPos2s(X,Y) ->
- wxe_util:cast(5417, <<X:?GLshort,Y:?GLshort>>).
+ cast(5417, <<X:?GLshort,Y:?GLshort>>).
%% @spec ({X,Y}) -> ok
%% @equiv windowPos2s(X,Y)
+-spec windowPos2sv({integer(),integer()}) -> ok.
windowPos2sv({X,Y}) -> windowPos2s(X,Y).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos3d(float(),float(),float()) -> ok.
windowPos3d(X,Y,Z) ->
- wxe_util:cast(5418, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5418, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv windowPos3d(X,Y,Z)
+-spec windowPos3dv({float(),float(),float()}) -> ok.
windowPos3dv({X,Y,Z}) -> windowPos3d(X,Y,Z).
%% @spec (X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos3f(float(),float(),float()) -> ok.
windowPos3f(X,Y,Z) ->
- wxe_util:cast(5419, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5419, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv windowPos3f(X,Y,Z)
+-spec windowPos3fv({float(),float(),float()}) -> ok.
windowPos3fv({X,Y,Z}) -> windowPos3f(X,Y,Z).
%% @spec (X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos3i(integer(),integer(),integer()) -> ok.
windowPos3i(X,Y,Z) ->
- wxe_util:cast(5420, <<X:?GLint,Y:?GLint,Z:?GLint>>).
+ cast(5420, <<X:?GLint,Y:?GLint,Z:?GLint>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv windowPos3i(X,Y,Z)
+-spec windowPos3iv({integer(),integer(),integer()}) -> ok.
windowPos3iv({X,Y,Z}) -> windowPos3i(X,Y,Z).
%% @spec (X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos.xml">external</a> documentation.
+-spec windowPos3s(integer(),integer(),integer()) -> ok.
windowPos3s(X,Y,Z) ->
- wxe_util:cast(5421, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
+ cast(5421, <<X:?GLshort,Y:?GLshort,Z:?GLshort>>).
%% @spec ({X,Y,Z}) -> ok
%% @equiv windowPos3s(X,Y,Z)
+-spec windowPos3sv({integer(),integer(),integer()}) -> ok.
windowPos3sv({X,Y,Z}) -> windowPos3s(X,Y,Z).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenQueries.xml">external</a> documentation.
+-spec genQueries(integer()) -> [integer()].
genQueries(N) ->
- wxe_util:call(5422, <<N:?GLsizei>>).
+ call(5422, <<N:?GLsizei>>).
%% @spec (Ids::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteQueries.xml">external</a> documentation.
+-spec deleteQueries([integer()]) -> ok.
deleteQueries(Ids) ->
- wxe_util:cast(5423, <<(length(Ids)):?GLuint,
+ cast(5423, <<(length(Ids)):?GLuint,
(<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((1+length(Ids)) rem 2)*32)>>).
%% @spec (Id::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsQuery.xml">external</a> documentation.
+-spec isQuery(integer()) -> 0|1.
isQuery(Id) ->
- wxe_util:call(5424, <<Id:?GLuint>>).
+ call(5424, <<Id:?GLuint>>).
%% @spec (Target::enum(),Id::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginQuery.xml">external</a> documentation.
+-spec beginQuery(enum(),integer()) -> ok.
beginQuery(Target,Id) ->
- wxe_util:cast(5425, <<Target:?GLenum,Id:?GLuint>>).
+ cast(5425, <<Target:?GLenum,Id:?GLuint>>).
%% @spec (Target::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEndQuery.xml">external</a> documentation.
+-spec endQuery(enum()) -> ok.
endQuery(Target) ->
- wxe_util:cast(5426, <<Target:?GLenum>>).
+ cast(5426, <<Target:?GLenum>>).
%% @spec (Target::enum(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQuery.xml">external</a> documentation.
+-spec getQueryiv(enum(),enum()) -> integer().
getQueryiv(Target,Pname) ->
- wxe_util:call(5427, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5427, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Id::integer(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObject.xml">external</a> documentation.
+-spec getQueryObjectiv(integer(),enum()) -> integer().
getQueryObjectiv(Id,Pname) ->
- wxe_util:call(5428, <<Id:?GLuint,Pname:?GLenum>>).
+ call(5428, <<Id:?GLuint,Pname:?GLenum>>).
%% @spec (Id::integer(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObject.xml">external</a> documentation.
+-spec getQueryObjectuiv(integer(),enum()) -> integer().
getQueryObjectuiv(Id,Pname) ->
- wxe_util:call(5429, <<Id:?GLuint,Pname:?GLenum>>).
+ call(5429, <<Id:?GLuint,Pname:?GLenum>>).
%% @spec (Target::enum(),Buffer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindBuffer.xml">external</a> documentation.
+-spec bindBuffer(enum(),integer()) -> ok.
bindBuffer(Target,Buffer) ->
- wxe_util:cast(5430, <<Target:?GLenum,Buffer:?GLuint>>).
+ cast(5430, <<Target:?GLenum,Buffer:?GLuint>>).
%% @spec (Buffers::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteBuffers.xml">external</a> documentation.
+-spec deleteBuffers([integer()]) -> ok.
deleteBuffers(Buffers) ->
- wxe_util:cast(5431, <<(length(Buffers)):?GLuint,
+ cast(5431, <<(length(Buffers)):?GLuint,
(<< <<C:?GLuint>> || C <- Buffers>>)/binary,0:(((1+length(Buffers)) rem 2)*32)>>).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenBuffers.xml">external</a> documentation.
+-spec genBuffers(integer()) -> [integer()].
genBuffers(N) ->
- wxe_util:call(5432, <<N:?GLsizei>>).
+ call(5432, <<N:?GLsizei>>).
%% @spec (Buffer::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsBuffer.xml">external</a> documentation.
+-spec isBuffer(integer()) -> 0|1.
isBuffer(Buffer) ->
- wxe_util:call(5433, <<Buffer:?GLuint>>).
+ call(5433, <<Buffer:?GLuint>>).
-%% @spec (Target::enum(),Size::integer(),Data::offset()|binary(),Usage::enum()) -> ok
+%% @spec (Target::enum(),Size::integer(),Data::offset()|mem(),Usage::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBufferData.xml">external</a> documentation.
+-spec bufferData(enum(),integer(),offset()|mem(),enum()) -> ok.
bufferData(Target,Size,Data,Usage) when is_integer(Data) ->
- wxe_util:cast(5434, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Data:?GLuint,Usage:?GLenum>>);
+ cast(5434, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Data:?GLuint,Usage:?GLenum>>);
bufferData(Target,Size,Data,Usage) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5435, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Usage:?GLenum>>).
+ send_bin(Data),
+ cast(5435, <<Target:?GLenum,0:32,Size:?GLsizeiptr,Usage:?GLenum>>).
-%% @spec (Target::enum(),Offset::integer(),Size::integer(),Data::offset()|binary()) -> ok
+%% @spec (Target::enum(),Offset::integer(),Size::integer(),Data::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBufferSubData.xml">external</a> documentation.
+-spec bufferSubData(enum(),integer(),integer(),offset()|mem()) -> ok.
bufferSubData(Target,Offset,Size,Data) when is_integer(Data) ->
- wxe_util:cast(5436, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr,Data:?GLuint>>);
+ cast(5436, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr,Data:?GLuint>>);
bufferSubData(Target,Offset,Size,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:cast(5437, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
+ send_bin(Data),
+ cast(5437, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
-%% @spec (Target::enum(),Offset::integer(),Size::integer(),Data::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Offset::integer(),Size::integer(),Data::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferSubData.xml">external</a> documentation.
+-spec getBufferSubData(enum(),integer(),integer(),mem()) -> ok.
getBufferSubData(Target,Offset,Size,Data) ->
- wxe_util:send_bin(Data#wx_mem.bin),
- wxe_util:call(5438, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
+ send_bin(Data),
+ call(5438, <<Target:?GLenum,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
%% @spec (Target::enum(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameteriv.xml">external</a> documentation.
+-spec getBufferParameteriv(enum(),enum()) -> integer().
getBufferParameteriv(Target,Pname) ->
- wxe_util:call(5439, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5439, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (ModeRGB::enum(),ModeAlpha::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquationSeparate.xml">external</a> documentation.
+-spec blendEquationSeparate(enum(),enum()) -> ok.
blendEquationSeparate(ModeRGB,ModeAlpha) ->
- wxe_util:cast(5440, <<ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
+ cast(5440, <<ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
%% @spec (Bufs::[enum()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawBuffers.xml">external</a> documentation.
+-spec drawBuffers([enum()]) -> ok.
drawBuffers(Bufs) ->
- wxe_util:cast(5441, <<(length(Bufs)):?GLuint,
+ cast(5441, <<(length(Bufs)):?GLuint,
(<< <<C:?GLenum>> || C <- Bufs>>)/binary,0:(((1+length(Bufs)) rem 2)*32)>>).
%% @spec (Face::enum(),Sfail::enum(),Dpfail::enum(),Dppass::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilOpSeparate.xml">external</a> documentation.
+-spec stencilOpSeparate(enum(),enum(),enum(),enum()) -> ok.
stencilOpSeparate(Face,Sfail,Dpfail,Dppass) ->
- wxe_util:cast(5442, <<Face:?GLenum,Sfail:?GLenum,Dpfail:?GLenum,Dppass:?GLenum>>).
+ cast(5442, <<Face:?GLenum,Sfail:?GLenum,Dpfail:?GLenum,Dppass:?GLenum>>).
-%% @spec (Frontfunc::enum(),Backfunc::enum(),Ref::integer(),Mask::integer()) -> ok
+%% @spec (Face::enum(),Func::enum(),Ref::integer(),Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilFuncSeparate.xml">external</a> documentation.
-stencilFuncSeparate(Frontfunc,Backfunc,Ref,Mask) ->
- wxe_util:cast(5443, <<Frontfunc:?GLenum,Backfunc:?GLenum,Ref:?GLint,Mask:?GLuint>>).
+-spec stencilFuncSeparate(enum(),enum(),integer(),integer()) -> ok.
+stencilFuncSeparate(Face,Func,Ref,Mask) ->
+ cast(5443, <<Face:?GLenum,Func:?GLenum,Ref:?GLint,Mask:?GLuint>>).
%% @spec (Face::enum(),Mask::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilMaskSeparate.xml">external</a> documentation.
+-spec stencilMaskSeparate(enum(),integer()) -> ok.
stencilMaskSeparate(Face,Mask) ->
- wxe_util:cast(5444, <<Face:?GLenum,Mask:?GLuint>>).
+ cast(5444, <<Face:?GLenum,Mask:?GLuint>>).
%% @spec (Program::integer(),Shader::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAttachShader.xml">external</a> documentation.
+-spec attachShader(integer(),integer()) -> ok.
attachShader(Program,Shader) ->
- wxe_util:cast(5445, <<Program:?GLuint,Shader:?GLuint>>).
+ cast(5445, <<Program:?GLuint,Shader:?GLuint>>).
%% @spec (Program::integer(),Index::integer(),Name::string()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindAttribLocation.xml">external</a> documentation.
+-spec bindAttribLocation(integer(),integer(),string()) -> ok.
bindAttribLocation(Program,Index,Name) ->
- wxe_util:cast(5446, <<Program:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ cast(5446, <<Program:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
%% @spec (Shader::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShader.xml">external</a> documentation.
+-spec compileShader(integer()) -> ok.
compileShader(Shader) ->
- wxe_util:cast(5447, <<Shader:?GLuint>>).
+ cast(5447, <<Shader:?GLuint>>).
%% @spec () -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateProgram.xml">external</a> documentation.
+-spec createProgram() -> integer().
createProgram() ->
- wxe_util:call(5448, <<>>).
+ call(5448, <<>>).
%% @spec (Type::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShader.xml">external</a> documentation.
+-spec createShader(enum()) -> integer().
createShader(Type) ->
- wxe_util:call(5449, <<Type:?GLenum>>).
+ call(5449, <<Type:?GLenum>>).
%% @spec (Program::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteProgram.xml">external</a> documentation.
+-spec deleteProgram(integer()) -> ok.
deleteProgram(Program) ->
- wxe_util:cast(5450, <<Program:?GLuint>>).
+ cast(5450, <<Program:?GLuint>>).
%% @spec (Shader::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteShader.xml">external</a> documentation.
+-spec deleteShader(integer()) -> ok.
deleteShader(Shader) ->
- wxe_util:cast(5451, <<Shader:?GLuint>>).
+ cast(5451, <<Shader:?GLuint>>).
%% @spec (Program::integer(),Shader::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDetachShader.xml">external</a> documentation.
+-spec detachShader(integer(),integer()) -> ok.
detachShader(Program,Shader) ->
- wxe_util:cast(5452, <<Program:?GLuint,Shader:?GLuint>>).
+ cast(5452, <<Program:?GLuint,Shader:?GLuint>>).
%% @spec (Index::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDisableVertexAttribArray.xml">external</a> documentation.
+-spec disableVertexAttribArray(integer()) -> ok.
disableVertexAttribArray(Index) ->
- wxe_util:cast(5453, <<Index:?GLuint>>).
+ cast(5453, <<Index:?GLuint>>).
%% @spec (Index::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnableVertexAttribArray.xml">external</a> documentation.
+-spec enableVertexAttribArray(integer()) -> ok.
enableVertexAttribArray(Index) ->
- wxe_util:cast(5454, <<Index:?GLuint>>).
+ cast(5454, <<Index:?GLuint>>).
%% @spec (Program::integer(),Index::integer(),BufSize::integer()) -> {Size::integer(),Type::enum(),Name::string()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveAttrib.xml">external</a> documentation.
+-spec getActiveAttrib(integer(),integer(),integer()) -> {integer(),enum(),string()}.
getActiveAttrib(Program,Index,BufSize) ->
- wxe_util:call(5455, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
+ call(5455, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
%% @spec (Program::integer(),Index::integer(),BufSize::integer()) -> {Size::integer(),Type::enum(),Name::string()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniform.xml">external</a> documentation.
+-spec getActiveUniform(integer(),integer(),integer()) -> {integer(),enum(),string()}.
getActiveUniform(Program,Index,BufSize) ->
- wxe_util:call(5456, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
+ call(5456, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
%% @spec (Program::integer(),MaxCount::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttachedShaders.xml">external</a> documentation.
+-spec getAttachedShaders(integer(),integer()) -> [integer()].
getAttachedShaders(Program,MaxCount) ->
- wxe_util:call(5457, <<Program:?GLuint,MaxCount:?GLsizei>>).
+ call(5457, <<Program:?GLuint,MaxCount:?GLsizei>>).
%% @spec (Program::integer(),Name::string()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttribLocation.xml">external</a> documentation.
+-spec getAttribLocation(integer(),string()) -> integer().
getAttribLocation(Program,Name) ->
- wxe_util:call(5458, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ call(5458, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
%% @spec (Program::integer(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgram.xml">external</a> documentation.
+-spec getProgramiv(integer(),enum()) -> integer().
getProgramiv(Program,Pname) ->
- wxe_util:call(5459, <<Program:?GLuint,Pname:?GLenum>>).
+ call(5459, <<Program:?GLuint,Pname:?GLenum>>).
%% @spec (Program::integer(),BufSize::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramInfoLog.xml">external</a> documentation.
+-spec getProgramInfoLog(integer(),integer()) -> string().
getProgramInfoLog(Program,BufSize) ->
- wxe_util:call(5460, <<Program:?GLuint,BufSize:?GLsizei>>).
+ call(5460, <<Program:?GLuint,BufSize:?GLsizei>>).
%% @spec (Shader::integer(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShader.xml">external</a> documentation.
+-spec getShaderiv(integer(),enum()) -> integer().
getShaderiv(Shader,Pname) ->
- wxe_util:call(5461, <<Shader:?GLuint,Pname:?GLenum>>).
+ call(5461, <<Shader:?GLuint,Pname:?GLenum>>).
%% @spec (Shader::integer(),BufSize::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderInfoLog.xml">external</a> documentation.
+-spec getShaderInfoLog(integer(),integer()) -> string().
getShaderInfoLog(Shader,BufSize) ->
- wxe_util:call(5462, <<Shader:?GLuint,BufSize:?GLsizei>>).
+ call(5462, <<Shader:?GLuint,BufSize:?GLsizei>>).
%% @spec (Shader::integer(),BufSize::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderSource.xml">external</a> documentation.
+-spec getShaderSource(integer(),integer()) -> string().
getShaderSource(Shader,BufSize) ->
- wxe_util:call(5463, <<Shader:?GLuint,BufSize:?GLsizei>>).
+ call(5463, <<Shader:?GLuint,BufSize:?GLsizei>>).
%% @spec (Program::integer(),Name::string()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformLocation.xml">external</a> documentation.
+-spec getUniformLocation(integer(),string()) -> integer().
getUniformLocation(Program,Name) ->
- wxe_util:call(5464, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ call(5464, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
-%% @spec (Program::integer(),Location::integer()) -> {float()}
+%% @spec (Program::integer(),Location::integer()) -> {float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniform.xml">external</a> documentation.
+-spec getUniformfv(integer(),integer()) -> {float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}.
getUniformfv(Program,Location) ->
- wxe_util:call(5465, <<Program:?GLuint,Location:?GLint>>).
+ call(5465, <<Program:?GLuint,Location:?GLint>>).
-%% @spec (Program::integer(),Location::integer()) -> {integer()}
+%% @spec (Program::integer(),Location::integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniform.xml">external</a> documentation.
+-spec getUniformiv(integer(),integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}.
getUniformiv(Program,Location) ->
- wxe_util:call(5466, <<Program:?GLuint,Location:?GLint>>).
+ call(5466, <<Program:?GLuint,Location:?GLint>>).
-%% @spec (Index::integer(),Pname::enum()) -> {float()}
+%% @spec (Index::integer(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttrib.xml">external</a> documentation.
+-spec getVertexAttribdv(integer(),enum()) -> {float(),float(),float(),float()}.
getVertexAttribdv(Index,Pname) ->
- wxe_util:call(5467, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5467, <<Index:?GLuint,Pname:?GLenum>>).
-%% @spec (Index::integer(),Pname::enum()) -> {float()}
+%% @spec (Index::integer(),Pname::enum()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttrib.xml">external</a> documentation.
+-spec getVertexAttribfv(integer(),enum()) -> {float(),float(),float(),float()}.
getVertexAttribfv(Index,Pname) ->
- wxe_util:call(5468, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5468, <<Index:?GLuint,Pname:?GLenum>>).
-%% @spec (Index::integer(),Pname::enum()) -> {integer()}
+%% @spec (Index::integer(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttrib.xml">external</a> documentation.
+-spec getVertexAttribiv(integer(),enum()) -> {integer(),integer(),integer(),integer()}.
getVertexAttribiv(Index,Pname) ->
- wxe_util:call(5469, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5469, <<Index:?GLuint,Pname:?GLenum>>).
%% @spec (Program::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsProgram.xml">external</a> documentation.
+-spec isProgram(integer()) -> 0|1.
isProgram(Program) ->
- wxe_util:call(5470, <<Program:?GLuint>>).
+ call(5470, <<Program:?GLuint>>).
%% @spec (Shader::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsShader.xml">external</a> documentation.
+-spec isShader(integer()) -> 0|1.
isShader(Shader) ->
- wxe_util:call(5471, <<Shader:?GLuint>>).
+ call(5471, <<Shader:?GLuint>>).
%% @spec (Program::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLinkProgram.xml">external</a> documentation.
+-spec linkProgram(integer()) -> ok.
linkProgram(Program) ->
- wxe_util:cast(5472, <<Program:?GLuint>>).
+ cast(5472, <<Program:?GLuint>>).
%% @spec (Shader::integer(),String::[string()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderSource.xml">external</a> documentation.
+-spec shaderSource(integer(),[string()]) -> ok.
shaderSource(Shader,String) ->
StringTemp = list_to_binary([[Str|[0]] || Str <- String ]),
- wxe_util:cast(5473, <<Shader:?GLuint,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+0) rem 8)) rem 8)>>).
+ cast(5473, <<Shader:?GLuint,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+0) rem 8)) rem 8)>>).
%% @spec (Program::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUseProgram.xml">external</a> documentation.
+-spec useProgram(integer()) -> ok.
useProgram(Program) ->
- wxe_util:cast(5474, <<Program:?GLuint>>).
+ cast(5474, <<Program:?GLuint>>).
%% @spec (Location::integer(),V0::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1f(integer(),float()) -> ok.
uniform1f(Location,V0) ->
- wxe_util:cast(5475, <<Location:?GLint,V0:?GLfloat>>).
+ cast(5475, <<Location:?GLint,V0:?GLfloat>>).
%% @spec (Location::integer(),V0::float(),V1::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2f(integer(),float(),float()) -> ok.
uniform2f(Location,V0,V1) ->
- wxe_util:cast(5476, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
+ cast(5476, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
%% @spec (Location::integer(),V0::float(),V1::float(),V2::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3f(integer(),float(),float(),float()) -> ok.
uniform3f(Location,V0,V1,V2) ->
- wxe_util:cast(5477, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
+ cast(5477, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
%% @spec (Location::integer(),V0::float(),V1::float(),V2::float(),V3::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4f(integer(),float(),float(),float(),float()) -> ok.
uniform4f(Location,V0,V1,V2,V3) ->
- wxe_util:cast(5478, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
+ cast(5478, <<Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
%% @spec (Location::integer(),V0::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1i(integer(),integer()) -> ok.
uniform1i(Location,V0) ->
- wxe_util:cast(5479, <<Location:?GLint,V0:?GLint>>).
+ cast(5479, <<Location:?GLint,V0:?GLint>>).
%% @spec (Location::integer(),V0::integer(),V1::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2i(integer(),integer(),integer()) -> ok.
uniform2i(Location,V0,V1) ->
- wxe_util:cast(5480, <<Location:?GLint,V0:?GLint,V1:?GLint>>).
+ cast(5480, <<Location:?GLint,V0:?GLint,V1:?GLint>>).
%% @spec (Location::integer(),V0::integer(),V1::integer(),V2::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3i(integer(),integer(),integer(),integer()) -> ok.
uniform3i(Location,V0,V1,V2) ->
- wxe_util:cast(5481, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
+ cast(5481, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
%% @spec (Location::integer(),V0::integer(),V1::integer(),V2::integer(),V3::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4i(integer(),integer(),integer(),integer(),integer()) -> ok.
uniform4i(Location,V0,V1,V2,V3) ->
- wxe_util:cast(5482, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
+ cast(5482, <<Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
%% @spec (Location::integer(),Value::[float()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1fv(integer(),[float()]) -> ok.
uniform1fv(Location,Value) ->
- wxe_util:cast(5483, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5483, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<C:?GLfloat>> || C <- Value>>)/binary,0:(((length(Value)) rem 2)*32)>>).
-%% @spec (Location::integer(),Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Value::[{float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2fv(integer(),[{float(),float()}]) -> ok.
uniform2fv(Location,Value) ->
- wxe_util:cast(5484, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5484, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat>> || {V1,V2} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Value::[{float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3fv(integer(),[{float(),float(),float()}]) -> ok.
uniform3fv(Location,Value) ->
- wxe_util:cast(5485, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5485, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>> || {V1,V2,V3} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Value::[{float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4fv(integer(),[{float(),float(),float(),float()}]) -> ok.
uniform4fv(Location,Value) ->
- wxe_util:cast(5486, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5486, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @spec (Location::integer(),Value::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1iv(integer(),[integer()]) -> ok.
uniform1iv(Location,Value) ->
- wxe_util:cast(5487, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5487, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<C:?GLint>> || C <- Value>>)/binary,0:(((length(Value)) rem 2)*32)>>).
-%% @spec (Location::integer(),Value::[{integer()}]) -> ok
+%% @spec (Location::integer(),Value::[{integer(),integer()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2iv(integer(),[{integer(),integer()}]) -> ok.
uniform2iv(Location,Value) ->
- wxe_util:cast(5488, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5488, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLint,V2:?GLint>> || {V1,V2} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Value::[{integer()}]) -> ok
+%% @spec (Location::integer(),Value::[{integer(),integer(),integer()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3iv(integer(),[{integer(),integer(),integer()}]) -> ok.
uniform3iv(Location,Value) ->
- wxe_util:cast(5489, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5489, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint>> || {V1,V2,V3} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Value::[{integer()}]) -> ok
+%% @spec (Location::integer(),Value::[{integer(),integer(),integer(),integer()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4iv(integer(),[{integer(),integer(),integer(),integer()}]) -> ok.
uniform4iv(Location,Value) ->
- wxe_util:cast(5490, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5490, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix.xml">external</a> documentation.
+-spec uniformMatrix2fv(integer(),0|1,[{float(),float(),float(),float()}]) -> ok.
uniformMatrix2fv(Location,Transpose,Value) ->
- wxe_util:cast(5491, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5491, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix.xml">external</a> documentation.
+-spec uniformMatrix3fv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix3fv(Location,Transpose,Value) ->
- wxe_util:cast(5492, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5492, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix.xml">external</a> documentation.
+-spec uniformMatrix4fv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix4fv(Location,Transpose,Value) ->
- wxe_util:cast(5493, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5493, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat,V13:?GLfloat,V14:?GLfloat,V15:?GLfloat,V16:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
%% @spec (Program::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgram.xml">external</a> documentation.
+-spec validateProgram(integer()) -> ok.
validateProgram(Program) ->
- wxe_util:cast(5494, <<Program:?GLuint>>).
+ cast(5494, <<Program:?GLuint>>).
%% @spec (Index::integer(),X::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib1d(integer(),float()) -> ok.
vertexAttrib1d(Index,X) ->
- wxe_util:cast(5495, <<Index:?GLuint,0:32,X:?GLdouble>>).
+ cast(5495, <<Index:?GLuint,0:32,X:?GLdouble>>).
%% @spec (Index,{X}) -> ok
%% @equiv vertexAttrib1d(Index,X)
+-spec vertexAttrib1dv(integer(),{float()}) -> ok.
vertexAttrib1dv(Index,{X}) -> vertexAttrib1d(Index,X).
%% @spec (Index::integer(),X::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib1f(integer(),float()) -> ok.
vertexAttrib1f(Index,X) ->
- wxe_util:cast(5496, <<Index:?GLuint,X:?GLfloat>>).
+ cast(5496, <<Index:?GLuint,X:?GLfloat>>).
%% @spec (Index,{X}) -> ok
%% @equiv vertexAttrib1f(Index,X)
+-spec vertexAttrib1fv(integer(),{float()}) -> ok.
vertexAttrib1fv(Index,{X}) -> vertexAttrib1f(Index,X).
%% @spec (Index::integer(),X::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib1s(integer(),integer()) -> ok.
vertexAttrib1s(Index,X) ->
- wxe_util:cast(5497, <<Index:?GLuint,X:?GLshort>>).
+ cast(5497, <<Index:?GLuint,X:?GLshort>>).
%% @spec (Index,{X}) -> ok
%% @equiv vertexAttrib1s(Index,X)
+-spec vertexAttrib1sv(integer(),{integer()}) -> ok.
vertexAttrib1sv(Index,{X}) -> vertexAttrib1s(Index,X).
%% @spec (Index::integer(),X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib2d(integer(),float(),float()) -> ok.
vertexAttrib2d(Index,X,Y) ->
- wxe_util:cast(5498, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
+ cast(5498, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
%% @spec (Index,{X,Y}) -> ok
%% @equiv vertexAttrib2d(Index,X,Y)
+-spec vertexAttrib2dv(integer(),{float(),float()}) -> ok.
vertexAttrib2dv(Index,{X,Y}) -> vertexAttrib2d(Index,X,Y).
%% @spec (Index::integer(),X::float(),Y::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib2f(integer(),float(),float()) -> ok.
vertexAttrib2f(Index,X,Y) ->
- wxe_util:cast(5499, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat>>).
+ cast(5499, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat>>).
%% @spec (Index,{X,Y}) -> ok
%% @equiv vertexAttrib2f(Index,X,Y)
+-spec vertexAttrib2fv(integer(),{float(),float()}) -> ok.
vertexAttrib2fv(Index,{X,Y}) -> vertexAttrib2f(Index,X,Y).
%% @spec (Index::integer(),X::integer(),Y::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib2s(integer(),integer(),integer()) -> ok.
vertexAttrib2s(Index,X,Y) ->
- wxe_util:cast(5500, <<Index:?GLuint,X:?GLshort,Y:?GLshort>>).
+ cast(5500, <<Index:?GLuint,X:?GLshort,Y:?GLshort>>).
%% @spec (Index,{X,Y}) -> ok
%% @equiv vertexAttrib2s(Index,X,Y)
+-spec vertexAttrib2sv(integer(),{integer(),integer()}) -> ok.
vertexAttrib2sv(Index,{X,Y}) -> vertexAttrib2s(Index,X,Y).
%% @spec (Index::integer(),X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib3d(integer(),float(),float(),float()) -> ok.
vertexAttrib3d(Index,X,Y,Z) ->
- wxe_util:cast(5501, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+ cast(5501, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
%% @spec (Index,{X,Y,Z}) -> ok
%% @equiv vertexAttrib3d(Index,X,Y,Z)
+-spec vertexAttrib3dv(integer(),{float(),float(),float()}) -> ok.
vertexAttrib3dv(Index,{X,Y,Z}) -> vertexAttrib3d(Index,X,Y,Z).
%% @spec (Index::integer(),X::float(),Y::float(),Z::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib3f(integer(),float(),float(),float()) -> ok.
vertexAttrib3f(Index,X,Y,Z) ->
- wxe_util:cast(5502, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
+ cast(5502, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat>>).
%% @spec (Index,{X,Y,Z}) -> ok
%% @equiv vertexAttrib3f(Index,X,Y,Z)
+-spec vertexAttrib3fv(integer(),{float(),float(),float()}) -> ok.
vertexAttrib3fv(Index,{X,Y,Z}) -> vertexAttrib3f(Index,X,Y,Z).
%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib3s(integer(),integer(),integer(),integer()) -> ok.
vertexAttrib3s(Index,X,Y,Z) ->
- wxe_util:cast(5503, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort>>).
+ cast(5503, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort>>).
%% @spec (Index,{X,Y,Z}) -> ok
%% @equiv vertexAttrib3s(Index,X,Y,Z)
+-spec vertexAttrib3sv(integer(),{integer(),integer(),integer()}) -> ok.
vertexAttrib3sv(Index,{X,Y,Z}) -> vertexAttrib3s(Index,X,Y,Z).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4Nbv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4Nbv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5504, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
+ cast(5504, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4Niv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4Niv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5505, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ cast(5505, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4Nsv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4Nsv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5506, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
+ cast(5506, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4Nub(integer(),integer(),integer(),integer(),integer()) -> ok.
vertexAttrib4Nub(Index,X,Y,Z,W) ->
- wxe_util:cast(5507, <<Index:?GLuint,X:?GLubyte,Y:?GLubyte,Z:?GLubyte,W:?GLubyte>>).
+ cast(5507, <<Index:?GLuint,X:?GLubyte,Y:?GLubyte,Z:?GLubyte,W:?GLubyte>>).
%% @spec (Index,{X,Y,Z,W}) -> ok
%% @equiv vertexAttrib4Nub(Index,X,Y,Z,W)
+-spec vertexAttrib4Nubv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4Nubv(Index,{X,Y,Z,W}) -> vertexAttrib4Nub(Index,X,Y,Z,W).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4Nuiv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4Nuiv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5508, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
+ cast(5508, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4Nusv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4Nusv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5509, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
+ cast(5509, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4bv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4bv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5510, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
+ cast(5510, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
%% @spec (Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4d(integer(),float(),float(),float(),float()) -> ok.
vertexAttrib4d(Index,X,Y,Z,W) ->
- wxe_util:cast(5511, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5511, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @spec (Index,{X,Y,Z,W}) -> ok
%% @equiv vertexAttrib4d(Index,X,Y,Z,W)
+-spec vertexAttrib4dv(integer(),{float(),float(),float(),float()}) -> ok.
vertexAttrib4dv(Index,{X,Y,Z,W}) -> vertexAttrib4d(Index,X,Y,Z,W).
%% @spec (Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4f(integer(),float(),float(),float(),float()) -> ok.
vertexAttrib4f(Index,X,Y,Z,W) ->
- wxe_util:cast(5512, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5512, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @spec (Index,{X,Y,Z,W}) -> ok
%% @equiv vertexAttrib4f(Index,X,Y,Z,W)
+-spec vertexAttrib4fv(integer(),{float(),float(),float(),float()}) -> ok.
vertexAttrib4fv(Index,{X,Y,Z,W}) -> vertexAttrib4f(Index,X,Y,Z,W).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4iv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4iv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5513, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ cast(5513, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4s(integer(),integer(),integer(),integer(),integer()) -> ok.
vertexAttrib4s(Index,X,Y,Z,W) ->
- wxe_util:cast(5514, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
+ cast(5514, <<Index:?GLuint,X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
%% @spec (Index,{X,Y,Z,W}) -> ok
%% @equiv vertexAttrib4s(Index,X,Y,Z,W)
+-spec vertexAttrib4sv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4sv(Index,{X,Y,Z,W}) -> vertexAttrib4s(Index,X,Y,Z,W).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4ubv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4ubv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5515, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
+ cast(5515, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4uiv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4uiv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5516, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
+ cast(5516, <<Index:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>>).
-%% @spec (Index::integer(),V::{integer()}) -> ok
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttrib.xml">external</a> documentation.
+-spec vertexAttrib4usv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
vertexAttrib4usv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5517, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
+ cast(5517, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
-%% @spec (Index::integer(),Size::integer(),Type::enum(),Normalized::0|1,Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Index::integer(),Size::integer(),Type::enum(),Normalized::0|1,Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribPointer.xml">external</a> documentation.
+-spec vertexAttribPointer(integer(),integer(),enum(),0|1,integer(),offset()|mem()) -> ok.
vertexAttribPointer(Index,Size,Type,Normalized,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5518, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5518, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei,Pointer:?GLuint>>);
vertexAttribPointer(Index,Size,Type,Normalized,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5519, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5519, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Normalized:?GLboolean,0:24,Stride:?GLsizei>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix2x.xml">external</a> documentation.
+-spec uniformMatrix2x3fv(integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix2x3fv(Location,Transpose,Value) ->
- wxe_util:cast(5520, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5520, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix3x.xml">external</a> documentation.
+-spec uniformMatrix3x2fv(integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix3x2fv(Location,Transpose,Value) ->
- wxe_util:cast(5521, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5521, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix2x.xml">external</a> documentation.
+-spec uniformMatrix2x4fv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix2x4fv(Location,Transpose,Value) ->
- wxe_util:cast(5522, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5522, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix4x.xml">external</a> documentation.
+-spec uniformMatrix4x2fv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix4x2fv(Location,Transpose,Value) ->
- wxe_util:cast(5523, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5523, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix3x.xml">external</a> documentation.
+-spec uniformMatrix3x4fv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix3x4fv(Location,Transpose,Value) ->
- wxe_util:cast(5524, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5524, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Transpose::0|1,Value::[{float()}]) -> ok
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix4x.xml">external</a> documentation.
+-spec uniformMatrix4x3fv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
uniformMatrix4x3fv(Location,Transpose,Value) ->
- wxe_util:cast(5525, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ cast(5525, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
(<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
%% @spec (Index::integer(),R::0|1,G::0|1,B::0|1,A::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glColorMaski.xml">external</a> documentation.
+-spec colorMaski(integer(),0|1,0|1,0|1,0|1) -> ok.
colorMaski(Index,R,G,B,A) ->
- wxe_util:cast(5526, <<Index:?GLuint,R:?GLboolean,G:?GLboolean,B:?GLboolean,A:?GLboolean>>).
+ cast(5526, <<Index:?GLuint,R:?GLboolean,G:?GLboolean,B:?GLboolean,A:?GLboolean>>).
%% @spec (Target::enum(),Index::integer()) -> [0|1]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBooleani_v.xml">external</a> documentation.
+-spec getBooleani_v(enum(),integer()) -> [0|1].
getBooleani_v(Target,Index) ->
- wxe_util:call(5527, <<Target:?GLenum,Index:?GLuint>>).
+ call(5527, <<Target:?GLenum,Index:?GLuint>>).
%% @spec (Target::enum(),Index::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetIntegeri_v.xml">external</a> documentation.
+-spec getIntegeri_v(enum(),integer()) -> [integer()].
getIntegeri_v(Target,Index) ->
- wxe_util:call(5528, <<Target:?GLenum,Index:?GLuint>>).
+ call(5528, <<Target:?GLenum,Index:?GLuint>>).
%% @spec (Target::enum(),Index::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEnable.xml">external</a> documentation.
+-spec enablei(enum(),integer()) -> ok.
enablei(Target,Index) ->
- wxe_util:cast(5529, <<Target:?GLenum,Index:?GLuint>>).
+ cast(5529, <<Target:?GLenum,Index:?GLuint>>).
%% @spec (Target::enum(),Index::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDisable.xml">external</a> documentation.
+-spec disablei(enum(),integer()) -> ok.
disablei(Target,Index) ->
- wxe_util:cast(5530, <<Target:?GLenum,Index:?GLuint>>).
+ cast(5530, <<Target:?GLenum,Index:?GLuint>>).
%% @spec (Target::enum(),Index::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsEnabledi.xml">external</a> documentation.
+-spec isEnabledi(enum(),integer()) -> 0|1.
isEnabledi(Target,Index) ->
- wxe_util:call(5531, <<Target:?GLenum,Index:?GLuint>>).
+ call(5531, <<Target:?GLenum,Index:?GLuint>>).
%% @spec (PrimitiveMode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginTransformFeedback.xml">external</a> documentation.
+-spec beginTransformFeedback(enum()) -> ok.
beginTransformFeedback(PrimitiveMode) ->
- wxe_util:cast(5532, <<PrimitiveMode:?GLenum>>).
+ cast(5532, <<PrimitiveMode:?GLenum>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEndTransformFeedback.xml">external</a> documentation.
+-spec endTransformFeedback() -> ok.
endTransformFeedback() ->
- wxe_util:cast(5533, <<>>).
+ cast(5533, <<>>).
%% @spec (Target::enum(),Index::integer(),Buffer::integer(),Offset::integer(),Size::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindBufferRange.xml">external</a> documentation.
+-spec bindBufferRange(enum(),integer(),integer(),integer(),integer()) -> ok.
bindBufferRange(Target,Index,Buffer,Offset,Size) ->
- wxe_util:cast(5534, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
+ cast(5534, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint,0:32,Offset:?GLintptr,Size:?GLsizeiptr>>).
%% @spec (Target::enum(),Index::integer(),Buffer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindBufferBase.xml">external</a> documentation.
+-spec bindBufferBase(enum(),integer(),integer()) -> ok.
bindBufferBase(Target,Index,Buffer) ->
- wxe_util:cast(5535, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint>>).
+ cast(5535, <<Target:?GLenum,Index:?GLuint,Buffer:?GLuint>>).
%% @spec (Program::integer(),Varyings::[string()],BufferMode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTransformFeedbackVaryings.xml">external</a> documentation.
+-spec transformFeedbackVaryings(integer(),[string()],enum()) -> ok.
transformFeedbackVaryings(Program,Varyings,BufferMode) ->
VaryingsTemp = list_to_binary([[Str|[0]] || Str <- Varyings ]),
- wxe_util:cast(5536, <<Program:?GLuint,(length(Varyings)):?GLuint,(size(VaryingsTemp)):?GLuint,(VaryingsTemp)/binary,0:((8-((size(VaryingsTemp)+0) rem 8)) rem 8),BufferMode:?GLenum>>).
+ cast(5536, <<Program:?GLuint,(length(Varyings)):?GLuint,(size(VaryingsTemp)):?GLuint,(VaryingsTemp)/binary,0:((8-((size(VaryingsTemp)+0) rem 8)) rem 8),BufferMode:?GLenum>>).
%% @spec (Program::integer(),Index::integer(),BufSize::integer()) -> {Size::integer(),Type::enum(),Name::string()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTransformFeedbackVarying.xml">external</a> documentation.
+-spec getTransformFeedbackVarying(integer(),integer(),integer()) -> {integer(),enum(),string()}.
getTransformFeedbackVarying(Program,Index,BufSize) ->
- wxe_util:call(5537, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
+ call(5537, <<Program:?GLuint,Index:?GLuint,BufSize:?GLsizei>>).
%% @spec (Target::enum(),Clamp::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClampColor.xml">external</a> documentation.
+-spec clampColor(enum(),enum()) -> ok.
clampColor(Target,Clamp) ->
- wxe_util:cast(5538, <<Target:?GLenum,Clamp:?GLenum>>).
+ cast(5538, <<Target:?GLenum,Clamp:?GLenum>>).
%% @spec (Id::integer(),Mode::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginConditionalRender.xml">external</a> documentation.
+-spec beginConditionalRender(integer(),enum()) -> ok.
beginConditionalRender(Id,Mode) ->
- wxe_util:cast(5539, <<Id:?GLuint,Mode:?GLenum>>).
+ cast(5539, <<Id:?GLuint,Mode:?GLenum>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEndConditionalRender.xml">external</a> documentation.
+-spec endConditionalRender() -> ok.
endConditionalRender() ->
- wxe_util:cast(5540, <<>>).
+ cast(5540, <<>>).
-%% @spec (Index::integer(),Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|binary()) -> ok
+%% @spec (Index::integer(),Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribIPointer.xml">external</a> documentation.
+-spec vertexAttribIPointer(integer(),integer(),enum(),integer(),offset()|mem()) -> ok.
vertexAttribIPointer(Index,Size,Type,Stride,Pointer) when is_integer(Pointer) ->
- wxe_util:cast(5541, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+ cast(5541, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
vertexAttribIPointer(Index,Size,Type,Stride,Pointer) ->
- wxe_util:send_bin(Pointer),
- wxe_util:cast(5542, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+ send_bin(Pointer),
+ cast(5542, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
-%% @spec (Index::integer(),Pname::enum()) -> {integer()}
+%% @spec (Index::integer(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttribI.xml">external</a> documentation.
+-spec getVertexAttribIiv(integer(),enum()) -> {integer(),integer(),integer(),integer()}.
getVertexAttribIiv(Index,Pname) ->
- wxe_util:call(5543, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5543, <<Index:?GLuint,Pname:?GLenum>>).
-%% @spec (Index::integer(),Pname::enum()) -> {integer()}
+%% @spec (Index::integer(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttribI.xml">external</a> documentation.
+-spec getVertexAttribIuiv(integer(),enum()) -> {integer(),integer(),integer(),integer()}.
getVertexAttribIuiv(Index,Pname) ->
- wxe_util:call(5544, <<Index:?GLuint,Pname:?GLenum>>).
+ call(5544, <<Index:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Index::integer(),X::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI1i(integer(),integer()) -> ok.
+vertexAttribI1i(Index,X) ->
+ cast(5545, <<Index:?GLuint,X:?GLint>>).
+
+%% @spec (Index::integer(),X::integer(),Y::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI2i(integer(),integer(),integer()) -> ok.
+vertexAttribI2i(Index,X,Y) ->
+ cast(5546, <<Index:?GLuint,X:?GLint,Y:?GLint>>).
+
+%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI3i(integer(),integer(),integer(),integer()) -> ok.
+vertexAttribI3i(Index,X,Y,Z) ->
+ cast(5547, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint>>).
+
+%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI4i(integer(),integer(),integer(),integer(),integer()) -> ok.
+vertexAttribI4i(Index,X,Y,Z,W) ->
+ cast(5548, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
+
+%% @spec (Index::integer(),X::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI1ui(integer(),integer()) -> ok.
+vertexAttribI1ui(Index,X) ->
+ cast(5549, <<Index:?GLuint,X:?GLuint>>).
+
+%% @spec (Index::integer(),X::integer(),Y::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI2ui(integer(),integer(),integer()) -> ok.
+vertexAttribI2ui(Index,X,Y) ->
+ cast(5550, <<Index:?GLuint,X:?GLuint,Y:?GLuint>>).
+
+%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI3ui(integer(),integer(),integer(),integer()) -> ok.
+vertexAttribI3ui(Index,X,Y,Z) ->
+ cast(5551, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint>>).
+
+%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI4ui(integer(),integer(),integer(),integer(),integer()) -> ok.
+vertexAttribI4ui(Index,X,Y,Z,W) ->
+ cast(5552, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint,W:?GLuint>>).
+
+%% @spec (Index,{X}) -> ok
+%% @equiv vertexAttribI1i(Index,X)
+-spec vertexAttribI1iv(integer(),{integer()}) -> ok.
+vertexAttribI1iv(Index,{X}) -> vertexAttribI1i(Index,X).
+
+%% @spec (Index,{X,Y}) -> ok
+%% @equiv vertexAttribI2i(Index,X,Y)
+-spec vertexAttribI2iv(integer(),{integer(),integer()}) -> ok.
+vertexAttribI2iv(Index,{X,Y}) -> vertexAttribI2i(Index,X,Y).
+
+%% @spec (Index,{X,Y,Z}) -> ok
+%% @equiv vertexAttribI3i(Index,X,Y,Z)
+-spec vertexAttribI3iv(integer(),{integer(),integer(),integer()}) -> ok.
+vertexAttribI3iv(Index,{X,Y,Z}) -> vertexAttribI3i(Index,X,Y,Z).
+
+%% @spec (Index,{X,Y,Z,W}) -> ok
+%% @equiv vertexAttribI4i(Index,X,Y,Z,W)
+-spec vertexAttribI4iv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+vertexAttribI4iv(Index,{X,Y,Z,W}) -> vertexAttribI4i(Index,X,Y,Z,W).
+
+%% @spec (Index,{X}) -> ok
+%% @equiv vertexAttribI1ui(Index,X)
+-spec vertexAttribI1uiv(integer(),{integer()}) -> ok.
+vertexAttribI1uiv(Index,{X}) -> vertexAttribI1ui(Index,X).
+
+%% @spec (Index,{X,Y}) -> ok
+%% @equiv vertexAttribI2ui(Index,X,Y)
+-spec vertexAttribI2uiv(integer(),{integer(),integer()}) -> ok.
+vertexAttribI2uiv(Index,{X,Y}) -> vertexAttribI2ui(Index,X,Y).
+
+%% @spec (Index,{X,Y,Z}) -> ok
+%% @equiv vertexAttribI3ui(Index,X,Y,Z)
+-spec vertexAttribI3uiv(integer(),{integer(),integer(),integer()}) -> ok.
+vertexAttribI3uiv(Index,{X,Y,Z}) -> vertexAttribI3ui(Index,X,Y,Z).
+
+%% @spec (Index,{X,Y,Z,W}) -> ok
+%% @equiv vertexAttribI4ui(Index,X,Y,Z,W)
+-spec vertexAttribI4uiv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+vertexAttribI4uiv(Index,{X,Y,Z,W}) -> vertexAttribI4ui(Index,X,Y,Z,W).
+
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI4bv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+vertexAttribI4bv(Index,{V1,V2,V3,V4}) ->
+ cast(5553, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
+
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI4sv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+vertexAttribI4sv(Index,{V1,V2,V3,V4}) ->
+ cast(5554, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
+
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI4ubv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+vertexAttribI4ubv(Index,{V1,V2,V3,V4}) ->
+ cast(5555, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
+
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
+-spec vertexAttribI4usv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+vertexAttribI4usv(Index,{V1,V2,V3,V4}) ->
+ cast(5556, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
-%% @spec (Program::integer(),Location::integer()) -> {integer()}
+%% @spec (Program::integer(),Location::integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniform.xml">external</a> documentation.
+-spec getUniformuiv(integer(),integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}.
getUniformuiv(Program,Location) ->
- wxe_util:call(5545, <<Program:?GLuint,Location:?GLint>>).
+ call(5557, <<Program:?GLuint,Location:?GLint>>).
%% @spec (Program::integer(),Color::integer(),Name::string()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindFragDataLocation.xml">external</a> documentation.
+-spec bindFragDataLocation(integer(),integer(),string()) -> ok.
bindFragDataLocation(Program,Color,Name) ->
- wxe_util:cast(5546, <<Program:?GLuint,Color:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ cast(5558, <<Program:?GLuint,Color:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
%% @spec (Program::integer(),Name::string()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFragDataLocation.xml">external</a> documentation.
+-spec getFragDataLocation(integer(),string()) -> integer().
getFragDataLocation(Program,Name) ->
- wxe_util:call(5547, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ call(5559, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
%% @spec (Location::integer(),V0::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1ui(integer(),integer()) -> ok.
uniform1ui(Location,V0) ->
- wxe_util:cast(5548, <<Location:?GLint,V0:?GLuint>>).
+ cast(5560, <<Location:?GLint,V0:?GLuint>>).
%% @spec (Location::integer(),V0::integer(),V1::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2ui(integer(),integer(),integer()) -> ok.
uniform2ui(Location,V0,V1) ->
- wxe_util:cast(5549, <<Location:?GLint,V0:?GLuint,V1:?GLuint>>).
+ cast(5561, <<Location:?GLint,V0:?GLuint,V1:?GLuint>>).
%% @spec (Location::integer(),V0::integer(),V1::integer(),V2::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3ui(integer(),integer(),integer(),integer()) -> ok.
uniform3ui(Location,V0,V1,V2) ->
- wxe_util:cast(5550, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
+ cast(5562, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
%% @spec (Location::integer(),V0::integer(),V1::integer(),V2::integer(),V3::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4ui(integer(),integer(),integer(),integer(),integer()) -> ok.
uniform4ui(Location,V0,V1,V2,V3) ->
- wxe_util:cast(5551, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
+ cast(5563, <<Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
%% @spec (Location::integer(),Value::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1uiv(integer(),[integer()]) -> ok.
uniform1uiv(Location,Value) ->
- wxe_util:cast(5552, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5564, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<C:?GLuint>> || C <- Value>>)/binary,0:(((length(Value)) rem 2)*32)>>).
-%% @spec (Location::integer(),Value::[{integer()}]) -> ok
+%% @spec (Location::integer(),Value::[{integer(),integer()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2uiv(integer(),[{integer(),integer()}]) -> ok.
uniform2uiv(Location,Value) ->
- wxe_util:cast(5553, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5565, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLuint,V2:?GLuint>> || {V1,V2} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Value::[{integer()}]) -> ok
+%% @spec (Location::integer(),Value::[{integer(),integer(),integer()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3uiv(integer(),[{integer(),integer(),integer()}]) -> ok.
uniform3uiv(Location,Value) ->
- wxe_util:cast(5554, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5566, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint>> || {V1,V2,V3} <- Value>>)/binary>>).
-%% @spec (Location::integer(),Value::[{integer()}]) -> ok
+%% @spec (Location::integer(),Value::[{integer(),integer(),integer(),integer()}]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4uiv(integer(),[{integer(),integer(),integer(),integer()}]) -> ok.
uniform4uiv(Location,Value) ->
- wxe_util:cast(5555, <<Location:?GLint,(length(Value)):?GLuint,
+ cast(5567, <<Location:?GLint,(length(Value)):?GLuint,
(<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
%% @spec (Target::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameterI.xml">external</a> documentation.
+-spec texParameterIiv(enum(),enum(),{integer()}) -> ok.
texParameterIiv(Target,Pname,Params) ->
- wxe_util:cast(5556, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5568, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
%% @spec (Target::enum(),Pname::enum(),Params::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameterI.xml">external</a> documentation.
+-spec texParameterIuiv(enum(),enum(),{integer()}) -> ok.
texParameterIuiv(Target,Pname,Params) ->
- wxe_util:cast(5557, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
+ cast(5569, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,
(<< <<C:?GLuint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>).
-%% @spec (Target::enum(),Pname::enum()) -> {integer()}
+%% @spec (Target::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexParameterI.xml">external</a> documentation.
+-spec getTexParameterIiv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getTexParameterIiv(Target,Pname) ->
- wxe_util:call(5558, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5570, <<Target:?GLenum,Pname:?GLenum>>).
-%% @spec (Target::enum(),Pname::enum()) -> {integer()}
+%% @spec (Target::enum(),Pname::enum()) -> {integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetTexParameterI.xml">external</a> documentation.
+-spec getTexParameterIuiv(enum(),enum()) -> {integer(),integer(),integer(),integer()}.
getTexParameterIuiv(Target,Pname) ->
- wxe_util:call(5559, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5571, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Buffer::enum(),Drawbuffer::integer(),Value::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBuffer.xml">external</a> documentation.
+-spec clearBufferiv(enum(),integer(),{integer()}) -> ok.
clearBufferiv(Buffer,Drawbuffer,Value) ->
- wxe_util:cast(5560, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
+ cast(5572, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
(<< <<C:?GLint>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>).
%% @spec (Buffer::enum(),Drawbuffer::integer(),Value::{integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBuffer.xml">external</a> documentation.
+-spec clearBufferuiv(enum(),integer(),{integer()}) -> ok.
clearBufferuiv(Buffer,Drawbuffer,Value) ->
- wxe_util:cast(5561, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
+ cast(5573, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
(<< <<C:?GLuint>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>).
%% @spec (Buffer::enum(),Drawbuffer::integer(),Value::{float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBuffer.xml">external</a> documentation.
+-spec clearBufferfv(enum(),integer(),{float()}) -> ok.
clearBufferfv(Buffer,Drawbuffer,Value) ->
- wxe_util:cast(5562, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
+ cast(5574, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint,
(<< <<C:?GLfloat>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>).
%% @spec (Buffer::enum(),Drawbuffer::integer(),Depth::float(),Stencil::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBufferfi.xml">external</a> documentation.
+-spec clearBufferfi(enum(),integer(),float(),integer()) -> ok.
clearBufferfi(Buffer,Drawbuffer,Depth,Stencil) ->
- wxe_util:cast(5563, <<Buffer:?GLenum,Drawbuffer:?GLint,Depth:?GLfloat,Stencil:?GLint>>).
+ cast(5575, <<Buffer:?GLenum,Drawbuffer:?GLint,Depth:?GLfloat,Stencil:?GLint>>).
%% @spec (Name::enum(),Index::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetString.xml">external</a> documentation.
+-spec getStringi(enum(),integer()) -> string().
getStringi(Name,Index) ->
- wxe_util:call(5564, <<Name:?GLenum,Index:?GLuint>>).
-
-%% @spec (Index::integer(),X::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI1i(Index,X) ->
- wxe_util:cast(5565, <<Index:?GLuint,X:?GLint>>).
-
-%% @spec (Index::integer(),X::integer(),Y::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI2i(Index,X,Y) ->
- wxe_util:cast(5566, <<Index:?GLuint,X:?GLint,Y:?GLint>>).
-
-%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI3i(Index,X,Y,Z) ->
- wxe_util:cast(5567, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint>>).
-
-%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI4i(Index,X,Y,Z,W) ->
- wxe_util:cast(5568, <<Index:?GLuint,X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
-
-%% @spec (Index::integer(),X::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI1ui(Index,X) ->
- wxe_util:cast(5569, <<Index:?GLuint,X:?GLuint>>).
-
-%% @spec (Index::integer(),X::integer(),Y::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI2ui(Index,X,Y) ->
- wxe_util:cast(5570, <<Index:?GLuint,X:?GLuint,Y:?GLuint>>).
-
-%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI3ui(Index,X,Y,Z) ->
- wxe_util:cast(5571, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint>>).
-
-%% @spec (Index::integer(),X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI4ui(Index,X,Y,Z,W) ->
- wxe_util:cast(5572, <<Index:?GLuint,X:?GLuint,Y:?GLuint,Z:?GLuint,W:?GLuint>>).
-
-%% @spec (Index,{X}) -> ok
-%% @equiv vertexAttribI1i(Index,X)
-vertexAttribI1iv(Index,{X}) -> vertexAttribI1i(Index,X).
-
-%% @spec (Index,{X,Y}) -> ok
-%% @equiv vertexAttribI2i(Index,X,Y)
-vertexAttribI2iv(Index,{X,Y}) -> vertexAttribI2i(Index,X,Y).
-
-%% @spec (Index,{X,Y,Z}) -> ok
-%% @equiv vertexAttribI3i(Index,X,Y,Z)
-vertexAttribI3iv(Index,{X,Y,Z}) -> vertexAttribI3i(Index,X,Y,Z).
-
-%% @spec (Index,{X,Y,Z,W}) -> ok
-%% @equiv vertexAttribI4i(Index,X,Y,Z,W)
-vertexAttribI4iv(Index,{X,Y,Z,W}) -> vertexAttribI4i(Index,X,Y,Z,W).
-
-%% @spec (Index,{X}) -> ok
-%% @equiv vertexAttribI1ui(Index,X)
-vertexAttribI1uiv(Index,{X}) -> vertexAttribI1ui(Index,X).
-
-%% @spec (Index,{X,Y}) -> ok
-%% @equiv vertexAttribI2ui(Index,X,Y)
-vertexAttribI2uiv(Index,{X,Y}) -> vertexAttribI2ui(Index,X,Y).
-
-%% @spec (Index,{X,Y,Z}) -> ok
-%% @equiv vertexAttribI3ui(Index,X,Y,Z)
-vertexAttribI3uiv(Index,{X,Y,Z}) -> vertexAttribI3ui(Index,X,Y,Z).
-
-%% @spec (Index,{X,Y,Z,W}) -> ok
-%% @equiv vertexAttribI4ui(Index,X,Y,Z,W)
-vertexAttribI4uiv(Index,{X,Y,Z,W}) -> vertexAttribI4ui(Index,X,Y,Z,W).
-
-%% @spec (Index::integer(),V::{integer()}) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI4bv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5573, <<Index:?GLuint,V1:?GLbyte,V2:?GLbyte,V3:?GLbyte,V4:?GLbyte>>).
-
-%% @spec (Index::integer(),V::{integer()}) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI4sv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5574, <<Index:?GLuint,V1:?GLshort,V2:?GLshort,V3:?GLshort,V4:?GLshort>>).
-
-%% @spec (Index::integer(),V::{integer()}) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI4ubv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5575, <<Index:?GLuint,V1:?GLubyte,V2:?GLubyte,V3:?GLubyte,V4:?GLubyte>>).
-
-%% @spec (Index::integer(),V::{integer()}) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribI.xml">external</a> documentation.
-vertexAttribI4usv(Index,{V1,V2,V3,V4}) ->
- wxe_util:cast(5576, <<Index:?GLuint,V1:?GLushort,V2:?GLushort,V3:?GLushort,V4:?GLushort>>).
+ call(5576, <<Name:?GLenum,Index:?GLuint>>).
%% @spec (Mode::enum(),First::integer(),Count::integer(),Primcount::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawArraysInstance.xml">external</a> documentation.
+-spec drawArraysInstanced(enum(),integer(),integer(),integer()) -> ok.
drawArraysInstanced(Mode,First,Count,Primcount) ->
- wxe_util:cast(5577, <<Mode:?GLenum,First:?GLint,Count:?GLsizei,Primcount:?GLsizei>>).
+ cast(5577, <<Mode:?GLenum,First:?GLint,Count:?GLsizei,Primcount:?GLsizei>>).
-%% @spec (Mode::enum(),Count::integer(),Type::enum(),Indices::offset()|binary(),Primcount::integer()) -> ok
+%% @spec (Mode::enum(),Count::integer(),Type::enum(),Indices::offset()|mem(),Primcount::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsInstance.xml">external</a> documentation.
+-spec drawElementsInstanced(enum(),integer(),enum(),offset()|mem(),integer()) -> ok.
drawElementsInstanced(Mode,Count,Type,Indices,Primcount) when is_integer(Indices) ->
- wxe_util:cast(5578, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei>>);
+ cast(5578, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei>>);
drawElementsInstanced(Mode,Count,Type,Indices,Primcount) ->
- wxe_util:send_bin(Indices),
- wxe_util:cast(5579, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei>>).
+ send_bin(Indices),
+ cast(5579, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei>>).
%% @spec (Target::enum(),Internalformat::enum(),Buffer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexBuffer.xml">external</a> documentation.
+-spec texBuffer(enum(),enum(),integer()) -> ok.
texBuffer(Target,Internalformat,Buffer) ->
- wxe_util:cast(5580, <<Target:?GLenum,Internalformat:?GLenum,Buffer:?GLuint>>).
+ cast(5580, <<Target:?GLenum,Internalformat:?GLenum,Buffer:?GLuint>>).
%% @spec (Index::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPrimitiveRestartIndex.xml">external</a> documentation.
+-spec primitiveRestartIndex(integer()) -> ok.
primitiveRestartIndex(Index) ->
- wxe_util:cast(5581, <<Index:?GLuint>>).
+ cast(5581, <<Index:?GLuint>>).
+
+%% @spec (Target::enum(),Index::integer()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetInteger64i_v.xml">external</a> documentation.
+-spec getInteger64i_v(enum(),integer()) -> [integer()].
+getInteger64i_v(Target,Index) ->
+ call(5582, <<Target:?GLenum,Index:?GLuint>>).
+
+%% @spec (Target::enum(),Pname::enum()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameteri64v.xml">external</a> documentation.
+-spec getBufferParameteri64v(enum(),enum()) -> [integer()].
+getBufferParameteri64v(Target,Pname) ->
+ call(5583, <<Target:?GLenum,Pname:?GLenum>>).
+
+%% @spec (Target::enum(),Attachment::enum(),Texture::integer(),Level::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTexture.xml">external</a> documentation.
+-spec framebufferTexture(enum(),enum(),integer(),integer()) -> ok.
+framebufferTexture(Target,Attachment,Texture,Level) ->
+ cast(5584, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (Index::integer(),Divisor::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml">external</a> documentation.
+-spec vertexAttribDivisor(integer(),integer()) -> ok.
+vertexAttribDivisor(Index,Divisor) ->
+ cast(5585, <<Index:?GLuint,Divisor:?GLuint>>).
+
+%% @spec (Value::clamp()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMinSampleShading.xml">external</a> documentation.
+-spec minSampleShading(clamp()) -> ok.
+minSampleShading(Value) ->
+ cast(5586, <<Value:?GLclampf>>).
+
+%% @spec (Buf::integer(),Mode::enum()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquation.xml">external</a> documentation.
+-spec blendEquationi(integer(),enum()) -> ok.
+blendEquationi(Buf,Mode) ->
+ cast(5587, <<Buf:?GLuint,Mode:?GLenum>>).
+
+%% @spec (Buf::integer(),ModeRGB::enum(),ModeAlpha::enum()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendEquationSeparate.xml">external</a> documentation.
+-spec blendEquationSeparatei(integer(),enum(),enum()) -> ok.
+blendEquationSeparatei(Buf,ModeRGB,ModeAlpha) ->
+ cast(5588, <<Buf:?GLuint,ModeRGB:?GLenum,ModeAlpha:?GLenum>>).
+
+%% @spec (Buf::integer(),Src::enum(),Dst::enum()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendFunci.xml">external</a> documentation.
+-spec blendFunci(integer(),enum(),enum()) -> ok.
+blendFunci(Buf,Src,Dst) ->
+ cast(5589, <<Buf:?GLuint,Src:?GLenum,Dst:?GLenum>>).
+
+%% @spec (Buf::integer(),SrcRGB::enum(),DstRGB::enum(),SrcAlpha::enum(),DstAlpha::enum()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlendFuncSeparate.xml">external</a> documentation.
+-spec blendFuncSeparatei(integer(),enum(),enum(),enum(),enum()) -> ok.
+blendFuncSeparatei(Buf,SrcRGB,DstRGB,SrcAlpha,DstAlpha) ->
+ cast(5590, <<Buf:?GLuint,SrcRGB:?GLenum,DstRGB:?GLenum,SrcAlpha:?GLenum,DstAlpha:?GLenum>>).
+
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadTransposeMatrixARB.xml">external</a> documentation.
+-spec loadTransposeMatrixfARB({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
loadTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5582, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5591, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
loadTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5582, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5591, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLoadTransposeMatrixARB.xml">external</a> documentation.
+-spec loadTransposeMatrixdARB({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
loadTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5583, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5592, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
loadTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5583, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5592, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultTransposeMatrixARB.xml">external</a> documentation.
+-spec multTransposeMatrixfARB({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
multTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5584, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
+ cast(5593, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,M13:?GLfloat,M14:?GLfloat,M15:?GLfloat,M16:?GLfloat>>);
multTransposeMatrixfARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5584, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
+ cast(5593, <<M1:?GLfloat,M2:?GLfloat,M3:?GLfloat,0:?GLfloat,M4:?GLfloat,M5:?GLfloat,M6:?GLfloat,0:?GLfloat,M7:?GLfloat,M8:?GLfloat,M9:?GLfloat,0:?GLfloat,M10:?GLfloat,M11:?GLfloat,M12:?GLfloat,1:?GLfloat>>).
-%% @spec (M::{float()}) -> ok
+%% @spec (M::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMultTransposeMatrixARB.xml">external</a> documentation.
+-spec multTransposeMatrixdARB({float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}) -> ok.
multTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16}) ->
- wxe_util:cast(5585, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
+ cast(5594, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble>>);
multTransposeMatrixdARB({M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12}) ->
- wxe_util:cast(5585, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
+ cast(5594, <<M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble>>).
%% @spec (Weights::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightbvARB([integer()]) -> ok.
weightbvARB(Weights) ->
- wxe_util:cast(5586, <<(length(Weights)):?GLuint,
+ cast(5595, <<(length(Weights)):?GLuint,
(<< <<C:?GLbyte>> || C <- Weights>>)/binary,0:((8-((length(Weights)+ 4) rem 8)) rem 8)>>).
%% @spec (Weights::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightsvARB([integer()]) -> ok.
weightsvARB(Weights) ->
- wxe_util:cast(5587, <<(length(Weights)):?GLuint,
+ cast(5596, <<(length(Weights)):?GLuint,
(<< <<C:?GLshort>> || C <- Weights>>)/binary,0:((8-((length(Weights)*2+ 4) rem 8)) rem 8)>>).
%% @spec (Weights::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightivARB([integer()]) -> ok.
weightivARB(Weights) ->
- wxe_util:cast(5588, <<(length(Weights)):?GLuint,
+ cast(5597, <<(length(Weights)):?GLuint,
(<< <<C:?GLint>> || C <- Weights>>)/binary,0:(((1+length(Weights)) rem 2)*32)>>).
%% @spec (Weights::[float()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightfvARB([float()]) -> ok.
weightfvARB(Weights) ->
- wxe_util:cast(5589, <<(length(Weights)):?GLuint,
+ cast(5598, <<(length(Weights)):?GLuint,
(<< <<C:?GLfloat>> || C <- Weights>>)/binary,0:(((1+length(Weights)) rem 2)*32)>>).
%% @spec (Weights::[float()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightdvARB([float()]) -> ok.
weightdvARB(Weights) ->
- wxe_util:cast(5590, <<(length(Weights)):?GLuint,0:32,
+ cast(5599, <<(length(Weights)):?GLuint,0:32,
(<< <<C:?GLdouble>> || C <- Weights>>)/binary>>).
%% @spec (Weights::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightubvARB([integer()]) -> ok.
weightubvARB(Weights) ->
- wxe_util:cast(5591, <<(length(Weights)):?GLuint,
+ cast(5600, <<(length(Weights)):?GLuint,
(<< <<C:?GLubyte>> || C <- Weights>>)/binary,0:((8-((length(Weights)+ 4) rem 8)) rem 8)>>).
%% @spec (Weights::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightusvARB([integer()]) -> ok.
weightusvARB(Weights) ->
- wxe_util:cast(5592, <<(length(Weights)):?GLuint,
+ cast(5601, <<(length(Weights)):?GLuint,
(<< <<C:?GLushort>> || C <- Weights>>)/binary,0:((8-((length(Weights)*2+ 4) rem 8)) rem 8)>>).
%% @spec (Weights::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWeightARB.xml">external</a> documentation.
+-spec weightuivARB([integer()]) -> ok.
weightuivARB(Weights) ->
- wxe_util:cast(5593, <<(length(Weights)):?GLuint,
+ cast(5602, <<(length(Weights)):?GLuint,
(<< <<C:?GLuint>> || C <- Weights>>)/binary,0:(((1+length(Weights)) rem 2)*32)>>).
%% @spec (Count::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexBlenARB.xml">external</a> documentation.
+-spec vertexBlendARB(integer()) -> ok.
vertexBlendARB(Count) ->
- wxe_util:cast(5594, <<Count:?GLint>>).
+ cast(5603, <<Count:?GLint>>).
%% @spec (Index::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCurrentPaletteMatrixARB.xml">external</a> documentation.
+-spec currentPaletteMatrixARB(integer()) -> ok.
currentPaletteMatrixARB(Index) ->
- wxe_util:cast(5595, <<Index:?GLint>>).
+ cast(5604, <<Index:?GLint>>).
%% @spec (Indices::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixIndexARB.xml">external</a> documentation.
+-spec matrixIndexubvARB([integer()]) -> ok.
matrixIndexubvARB(Indices) ->
- wxe_util:cast(5596, <<(length(Indices)):?GLuint,
+ cast(5605, <<(length(Indices)):?GLuint,
(<< <<C:?GLubyte>> || C <- Indices>>)/binary,0:((8-((length(Indices)+ 4) rem 8)) rem 8)>>).
%% @spec (Indices::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixIndexARB.xml">external</a> documentation.
+-spec matrixIndexusvARB([integer()]) -> ok.
matrixIndexusvARB(Indices) ->
- wxe_util:cast(5597, <<(length(Indices)):?GLuint,
+ cast(5606, <<(length(Indices)):?GLuint,
(<< <<C:?GLushort>> || C <- Indices>>)/binary,0:((8-((length(Indices)*2+ 4) rem 8)) rem 8)>>).
%% @spec (Indices::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glMatrixIndexARB.xml">external</a> documentation.
+-spec matrixIndexuivARB([integer()]) -> ok.
matrixIndexuivARB(Indices) ->
- wxe_util:cast(5598, <<(length(Indices)):?GLuint,
+ cast(5607, <<(length(Indices)):?GLuint,
(<< <<C:?GLuint>> || C <- Indices>>)/binary,0:(((1+length(Indices)) rem 2)*32)>>).
%% @spec (Target::enum(),Format::enum(),String::string()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramStringARB.xml">external</a> documentation.
+-spec programStringARB(enum(),enum(),string()) -> ok.
programStringARB(Target,Format,String) ->
- wxe_util:cast(5599, <<Target:?GLenum,Format:?GLenum,(list_to_binary([String|[0]]))/binary,0:((8-((length(String)+ 1) rem 8)) rem 8)>>).
+ cast(5608, <<Target:?GLenum,Format:?GLenum,(list_to_binary([String|[0]]))/binary,0:((8-((length(String)+ 1) rem 8)) rem 8)>>).
%% @spec (Target::enum(),Program::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindProgramARB.xml">external</a> documentation.
+-spec bindProgramARB(enum(),integer()) -> ok.
bindProgramARB(Target,Program) ->
- wxe_util:cast(5600, <<Target:?GLenum,Program:?GLuint>>).
+ cast(5609, <<Target:?GLenum,Program:?GLuint>>).
%% @spec (Programs::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteProgramsARB.xml">external</a> documentation.
+-spec deleteProgramsARB([integer()]) -> ok.
deleteProgramsARB(Programs) ->
- wxe_util:cast(5601, <<(length(Programs)):?GLuint,
+ cast(5610, <<(length(Programs)):?GLuint,
(<< <<C:?GLuint>> || C <- Programs>>)/binary,0:(((1+length(Programs)) rem 2)*32)>>).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenProgramsARB.xml">external</a> documentation.
+-spec genProgramsARB(integer()) -> [integer()].
genProgramsARB(N) ->
- wxe_util:call(5602, <<N:?GLsizei>>).
+ call(5611, <<N:?GLsizei>>).
%% @spec (Target::enum(),Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
+-spec programEnvParameter4dARB(enum(),integer(),float(),float(),float(),float()) -> ok.
programEnvParameter4dARB(Target,Index,X,Y,Z,W) ->
- wxe_util:cast(5603, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5612, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
-%% @spec (Target::enum(),Index::integer(),Params::{float()}) -> ok
+%% @spec (Target::enum(),Index::integer(),Params::{float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
+-spec programEnvParameter4dvARB(enum(),integer(),{float(),float(),float(),float()}) -> ok.
programEnvParameter4dvARB(Target,Index,{P1,P2,P3,P4}) ->
- wxe_util:cast(5604, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
+ cast(5613, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
%% @spec (Target::enum(),Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
+-spec programEnvParameter4fARB(enum(),integer(),float(),float(),float(),float()) -> ok.
programEnvParameter4fARB(Target,Index,X,Y,Z,W) ->
- wxe_util:cast(5605, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5614, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
-%% @spec (Target::enum(),Index::integer(),Params::{float()}) -> ok
+%% @spec (Target::enum(),Index::integer(),Params::{float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramEnvParameterARB.xml">external</a> documentation.
+-spec programEnvParameter4fvARB(enum(),integer(),{float(),float(),float(),float()}) -> ok.
programEnvParameter4fvARB(Target,Index,{P1,P2,P3,P4}) ->
- wxe_util:cast(5606, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
+ cast(5615, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
%% @spec (Target::enum(),Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
+-spec programLocalParameter4dARB(enum(),integer(),float(),float(),float(),float()) -> ok.
programLocalParameter4dARB(Target,Index,X,Y,Z,W) ->
- wxe_util:cast(5607, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5616, <<Target:?GLenum,Index:?GLuint,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
-%% @spec (Target::enum(),Index::integer(),Params::{float()}) -> ok
+%% @spec (Target::enum(),Index::integer(),Params::{float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
+-spec programLocalParameter4dvARB(enum(),integer(),{float(),float(),float(),float()}) -> ok.
programLocalParameter4dvARB(Target,Index,{P1,P2,P3,P4}) ->
- wxe_util:cast(5608, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
+ cast(5617, <<Target:?GLenum,Index:?GLuint,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble>>).
%% @spec (Target::enum(),Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
+-spec programLocalParameter4fARB(enum(),integer(),float(),float(),float(),float()) -> ok.
programLocalParameter4fARB(Target,Index,X,Y,Z,W) ->
- wxe_util:cast(5609, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5618, <<Target:?GLenum,Index:?GLuint,X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
-%% @spec (Target::enum(),Index::integer(),Params::{float()}) -> ok
+%% @spec (Target::enum(),Index::integer(),Params::{float(),float(),float(),float()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramLocalParameterARB.xml">external</a> documentation.
+-spec programLocalParameter4fvARB(enum(),integer(),{float(),float(),float(),float()}) -> ok.
programLocalParameter4fvARB(Target,Index,{P1,P2,P3,P4}) ->
- wxe_util:cast(5610, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
+ cast(5619, <<Target:?GLenum,Index:?GLuint,P1:?GLfloat,P2:?GLfloat,P3:?GLfloat,P4:?GLfloat>>).
-%% @spec (Target::enum(),Index::integer()) -> {float()}
+%% @spec (Target::enum(),Index::integer()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramEnvParameterARB.xml">external</a> documentation.
+-spec getProgramEnvParameterdvARB(enum(),integer()) -> {float(),float(),float(),float()}.
getProgramEnvParameterdvARB(Target,Index) ->
- wxe_util:call(5611, <<Target:?GLenum,Index:?GLuint>>).
+ call(5620, <<Target:?GLenum,Index:?GLuint>>).
-%% @spec (Target::enum(),Index::integer()) -> {float()}
+%% @spec (Target::enum(),Index::integer()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramEnvParameterARB.xml">external</a> documentation.
+-spec getProgramEnvParameterfvARB(enum(),integer()) -> {float(),float(),float(),float()}.
getProgramEnvParameterfvARB(Target,Index) ->
- wxe_util:call(5612, <<Target:?GLenum,Index:?GLuint>>).
+ call(5621, <<Target:?GLenum,Index:?GLuint>>).
-%% @spec (Target::enum(),Index::integer()) -> {float()}
+%% @spec (Target::enum(),Index::integer()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramLocalParameterARB.xml">external</a> documentation.
+-spec getProgramLocalParameterdvARB(enum(),integer()) -> {float(),float(),float(),float()}.
getProgramLocalParameterdvARB(Target,Index) ->
- wxe_util:call(5613, <<Target:?GLenum,Index:?GLuint>>).
+ call(5622, <<Target:?GLenum,Index:?GLuint>>).
-%% @spec (Target::enum(),Index::integer()) -> {float()}
+%% @spec (Target::enum(),Index::integer()) -> {float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramLocalParameterARB.xml">external</a> documentation.
+-spec getProgramLocalParameterfvARB(enum(),integer()) -> {float(),float(),float(),float()}.
getProgramLocalParameterfvARB(Target,Index) ->
- wxe_util:call(5614, <<Target:?GLenum,Index:?GLuint>>).
+ call(5623, <<Target:?GLenum,Index:?GLuint>>).
-%% @spec (Target::enum(),Pname::enum(),String::wx:wx_mem()) -> ok
+%% @spec (Target::enum(),Pname::enum(),String::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramStringARB.xml">external</a> documentation.
+-spec getProgramStringARB(enum(),enum(),mem()) -> ok.
getProgramStringARB(Target,Pname,String) ->
- wxe_util:send_bin(String#wx_mem.bin),
- wxe_util:call(5615, <<Target:?GLenum,Pname:?GLenum>>).
+ send_bin(String),
+ call(5624, <<Target:?GLenum,Pname:?GLenum>>).
+
+%% @spec (Target::enum(),Pname::enum()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetBufferParameterARB.xml">external</a> documentation.
+-spec getBufferParameterivARB(enum(),enum()) -> [integer()].
+getBufferParameterivARB(Target,Pname) ->
+ call(5625, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Obj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteObjectARB.xml">external</a> documentation.
+-spec deleteObjectARB(integer()) -> ok.
deleteObjectARB(Obj) ->
- wxe_util:cast(5616, <<Obj:?GLhandleARB>>).
+ cast(5626, <<Obj:?GLhandleARB>>).
%% @spec (Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetHandleARB.xml">external</a> documentation.
+-spec getHandleARB(enum()) -> integer().
getHandleARB(Pname) ->
- wxe_util:call(5617, <<Pname:?GLenum>>).
+ call(5627, <<Pname:?GLenum>>).
%% @spec (ContainerObj::integer(),AttachedObj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDetachObjectARB.xml">external</a> documentation.
+-spec detachObjectARB(integer(),integer()) -> ok.
detachObjectARB(ContainerObj,AttachedObj) ->
- wxe_util:cast(5618, <<ContainerObj:?GLhandleARB,AttachedObj:?GLhandleARB>>).
+ cast(5628, <<ContainerObj:?GLhandleARB,AttachedObj:?GLhandleARB>>).
%% @spec (ShaderType::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShaderObjectARB.xml">external</a> documentation.
+-spec createShaderObjectARB(enum()) -> integer().
createShaderObjectARB(ShaderType) ->
- wxe_util:call(5619, <<ShaderType:?GLenum>>).
+ call(5629, <<ShaderType:?GLenum>>).
%% @spec (ShaderObj::integer(),String::[string()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderSourceARB.xml">external</a> documentation.
+-spec shaderSourceARB(integer(),[string()]) -> ok.
shaderSourceARB(ShaderObj,String) ->
StringTemp = list_to_binary([[Str|[0]] || Str <- String ]),
- wxe_util:cast(5620, <<ShaderObj:?GLhandleARB,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+4) rem 8)) rem 8)>>).
+ cast(5630, <<ShaderObj:?GLhandleARB,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+4) rem 8)) rem 8)>>).
%% @spec (ShaderObj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShaderARB.xml">external</a> documentation.
+-spec compileShaderARB(integer()) -> ok.
compileShaderARB(ShaderObj) ->
- wxe_util:cast(5621, <<ShaderObj:?GLhandleARB>>).
+ cast(5631, <<ShaderObj:?GLhandleARB>>).
%% @spec () -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateProgramObjectARB.xml">external</a> documentation.
+-spec createProgramObjectARB() -> integer().
createProgramObjectARB() ->
- wxe_util:call(5622, <<>>).
+ call(5632, <<>>).
%% @spec (ContainerObj::integer(),Obj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glAttachObjectARB.xml">external</a> documentation.
+-spec attachObjectARB(integer(),integer()) -> ok.
attachObjectARB(ContainerObj,Obj) ->
- wxe_util:cast(5623, <<ContainerObj:?GLhandleARB,Obj:?GLhandleARB>>).
+ cast(5633, <<ContainerObj:?GLhandleARB,Obj:?GLhandleARB>>).
%% @spec (ProgramObj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glLinkProgramARB.xml">external</a> documentation.
+-spec linkProgramARB(integer()) -> ok.
linkProgramARB(ProgramObj) ->
- wxe_util:cast(5624, <<ProgramObj:?GLhandleARB>>).
+ cast(5634, <<ProgramObj:?GLhandleARB>>).
%% @spec (ProgramObj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUseProgramObjectARB.xml">external</a> documentation.
+-spec useProgramObjectARB(integer()) -> ok.
useProgramObjectARB(ProgramObj) ->
- wxe_util:cast(5625, <<ProgramObj:?GLhandleARB>>).
+ cast(5635, <<ProgramObj:?GLhandleARB>>).
%% @spec (ProgramObj::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgramARB.xml">external</a> documentation.
+-spec validateProgramARB(integer()) -> ok.
validateProgramARB(ProgramObj) ->
- wxe_util:cast(5626, <<ProgramObj:?GLhandleARB>>).
+ cast(5636, <<ProgramObj:?GLhandleARB>>).
%% @spec (Obj::integer(),Pname::enum()) -> float()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetObjectParameterARB.xml">external</a> documentation.
+-spec getObjectParameterfvARB(integer(),enum()) -> float().
getObjectParameterfvARB(Obj,Pname) ->
- wxe_util:call(5627, <<Obj:?GLhandleARB,Pname:?GLenum>>).
+ call(5637, <<Obj:?GLhandleARB,Pname:?GLenum>>).
%% @spec (Obj::integer(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetObjectParameterARB.xml">external</a> documentation.
+-spec getObjectParameterivARB(integer(),enum()) -> integer().
getObjectParameterivARB(Obj,Pname) ->
- wxe_util:call(5628, <<Obj:?GLhandleARB,Pname:?GLenum>>).
+ call(5638, <<Obj:?GLhandleARB,Pname:?GLenum>>).
%% @spec (Obj::integer(),MaxLength::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetInfoLogARB.xml">external</a> documentation.
+-spec getInfoLogARB(integer(),integer()) -> string().
getInfoLogARB(Obj,MaxLength) ->
- wxe_util:call(5629, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
+ call(5639, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
%% @spec (ContainerObj::integer(),MaxCount::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttachedObjectsARB.xml">external</a> documentation.
+-spec getAttachedObjectsARB(integer(),integer()) -> [integer()].
getAttachedObjectsARB(ContainerObj,MaxCount) ->
- wxe_util:call(5630, <<ContainerObj:?GLhandleARB,MaxCount:?GLsizei>>).
+ call(5640, <<ContainerObj:?GLhandleARB,MaxCount:?GLsizei>>).
%% @spec (ProgramObj::integer(),Name::string()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformLocationARB.xml">external</a> documentation.
+-spec getUniformLocationARB(integer(),string()) -> integer().
getUniformLocationARB(ProgramObj,Name) ->
- wxe_util:call(5631, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ call(5641, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
%% @spec (ProgramObj::integer(),Index::integer(),MaxLength::integer()) -> {Size::integer(),Type::enum(),Name::string()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformARB.xml">external</a> documentation.
+-spec getActiveUniformARB(integer(),integer(),integer()) -> {integer(),enum(),string()}.
getActiveUniformARB(ProgramObj,Index,MaxLength) ->
- wxe_util:call(5632, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
+ call(5642, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
-%% @spec (ProgramObj::integer(),Location::integer()) -> {float()}
+%% @spec (ProgramObj::integer(),Location::integer()) -> {float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformARB.xml">external</a> documentation.
+-spec getUniformfvARB(integer(),integer()) -> {float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}.
getUniformfvARB(ProgramObj,Location) ->
- wxe_util:call(5633, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
+ call(5643, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
-%% @spec (ProgramObj::integer(),Location::integer()) -> {integer()}
+%% @spec (ProgramObj::integer(),Location::integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformARB.xml">external</a> documentation.
+-spec getUniformivARB(integer(),integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}.
getUniformivARB(ProgramObj,Location) ->
- wxe_util:call(5634, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
+ call(5644, <<ProgramObj:?GLhandleARB,Location:?GLint>>).
%% @spec (Obj::integer(),MaxLength::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderSourceARB.xml">external</a> documentation.
+-spec getShaderSourceARB(integer(),integer()) -> string().
getShaderSourceARB(Obj,MaxLength) ->
- wxe_util:call(5635, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
+ call(5645, <<Obj:?GLhandleARB,MaxLength:?GLsizei>>).
%% @spec (ProgramObj::integer(),Index::integer(),Name::string()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindAttribLocationARB.xml">external</a> documentation.
+-spec bindAttribLocationARB(integer(),integer(),string()) -> ok.
bindAttribLocationARB(ProgramObj,Index,Name) ->
- wxe_util:cast(5636, <<ProgramObj:?GLhandleARB,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+ cast(5646, <<ProgramObj:?GLhandleARB,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
%% @spec (ProgramObj::integer(),Index::integer(),MaxLength::integer()) -> {Size::integer(),Type::enum(),Name::string()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveAttribARB.xml">external</a> documentation.
+-spec getActiveAttribARB(integer(),integer(),integer()) -> {integer(),enum(),string()}.
getActiveAttribARB(ProgramObj,Index,MaxLength) ->
- wxe_util:call(5637, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
+ call(5647, <<ProgramObj:?GLhandleARB,Index:?GLuint,MaxLength:?GLsizei>>).
%% @spec (ProgramObj::integer(),Name::string()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetAttribLocationARB.xml">external</a> documentation.
+-spec getAttribLocationARB(integer(),string()) -> integer().
getAttribLocationARB(ProgramObj,Name) ->
- wxe_util:call(5638, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+ call(5648, <<ProgramObj:?GLhandleARB,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
%% @spec (Renderbuffer::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsRenderbuffer.xml">external</a> documentation.
+-spec isRenderbuffer(integer()) -> 0|1.
isRenderbuffer(Renderbuffer) ->
- wxe_util:call(5639, <<Renderbuffer:?GLuint>>).
+ call(5649, <<Renderbuffer:?GLuint>>).
%% @spec (Target::enum(),Renderbuffer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindRenderbuffer.xml">external</a> documentation.
+-spec bindRenderbuffer(enum(),integer()) -> ok.
bindRenderbuffer(Target,Renderbuffer) ->
- wxe_util:cast(5640, <<Target:?GLenum,Renderbuffer:?GLuint>>).
+ cast(5650, <<Target:?GLenum,Renderbuffer:?GLuint>>).
%% @spec (Renderbuffers::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteRenderbuffers.xml">external</a> documentation.
+-spec deleteRenderbuffers([integer()]) -> ok.
deleteRenderbuffers(Renderbuffers) ->
- wxe_util:cast(5641, <<(length(Renderbuffers)):?GLuint,
+ cast(5651, <<(length(Renderbuffers)):?GLuint,
(<< <<C:?GLuint>> || C <- Renderbuffers>>)/binary,0:(((1+length(Renderbuffers)) rem 2)*32)>>).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenRenderbuffers.xml">external</a> documentation.
+-spec genRenderbuffers(integer()) -> [integer()].
genRenderbuffers(N) ->
- wxe_util:call(5642, <<N:?GLsizei>>).
+ call(5652, <<N:?GLsizei>>).
%% @spec (Target::enum(),Internalformat::enum(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRenderbufferStorage.xml">external</a> documentation.
+-spec renderbufferStorage(enum(),enum(),integer(),integer()) -> ok.
renderbufferStorage(Target,Internalformat,Width,Height) ->
- wxe_util:cast(5643, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5653, <<Target:?GLenum,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
%% @spec (Target::enum(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetRenderbufferParameter.xml">external</a> documentation.
+-spec getRenderbufferParameteriv(enum(),enum()) -> integer().
getRenderbufferParameteriv(Target,Pname) ->
- wxe_util:call(5644, <<Target:?GLenum,Pname:?GLenum>>).
+ call(5654, <<Target:?GLenum,Pname:?GLenum>>).
%% @spec (Framebuffer::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsFramebuffer.xml">external</a> documentation.
+-spec isFramebuffer(integer()) -> 0|1.
isFramebuffer(Framebuffer) ->
- wxe_util:call(5645, <<Framebuffer:?GLuint>>).
+ call(5655, <<Framebuffer:?GLuint>>).
%% @spec (Target::enum(),Framebuffer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindFramebuffer.xml">external</a> documentation.
+-spec bindFramebuffer(enum(),integer()) -> ok.
bindFramebuffer(Target,Framebuffer) ->
- wxe_util:cast(5646, <<Target:?GLenum,Framebuffer:?GLuint>>).
+ cast(5656, <<Target:?GLenum,Framebuffer:?GLuint>>).
%% @spec (Framebuffers::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteFramebuffers.xml">external</a> documentation.
+-spec deleteFramebuffers([integer()]) -> ok.
deleteFramebuffers(Framebuffers) ->
- wxe_util:cast(5647, <<(length(Framebuffers)):?GLuint,
+ cast(5657, <<(length(Framebuffers)):?GLuint,
(<< <<C:?GLuint>> || C <- Framebuffers>>)/binary,0:(((1+length(Framebuffers)) rem 2)*32)>>).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenFramebuffers.xml">external</a> documentation.
+-spec genFramebuffers(integer()) -> [integer()].
genFramebuffers(N) ->
- wxe_util:call(5648, <<N:?GLsizei>>).
+ call(5658, <<N:?GLsizei>>).
%% @spec (Target::enum()) -> enum()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCheckFramebufferStatus.xml">external</a> documentation.
+-spec checkFramebufferStatus(enum()) -> enum().
checkFramebufferStatus(Target) ->
- wxe_util:call(5649, <<Target:?GLenum>>).
+ call(5659, <<Target:?GLenum>>).
%% @spec (Target::enum(),Attachment::enum(),Textarget::enum(),Texture::integer(),Level::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTexture1D.xml">external</a> documentation.
+-spec framebufferTexture1D(enum(),enum(),enum(),integer(),integer()) -> ok.
framebufferTexture1D(Target,Attachment,Textarget,Texture,Level) ->
- wxe_util:cast(5650, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
+ cast(5660, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
%% @spec (Target::enum(),Attachment::enum(),Textarget::enum(),Texture::integer(),Level::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTexture2D.xml">external</a> documentation.
+-spec framebufferTexture2D(enum(),enum(),enum(),integer(),integer()) -> ok.
framebufferTexture2D(Target,Attachment,Textarget,Texture,Level) ->
- wxe_util:cast(5651, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
+ cast(5661, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint>>).
%% @spec (Target::enum(),Attachment::enum(),Textarget::enum(),Texture::integer(),Level::integer(),Zoffset::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTexture3D.xml">external</a> documentation.
+-spec framebufferTexture3D(enum(),enum(),enum(),integer(),integer(),integer()) -> ok.
framebufferTexture3D(Target,Attachment,Textarget,Texture,Level,Zoffset) ->
- wxe_util:cast(5652, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint,Zoffset:?GLint>>).
+ cast(5662, <<Target:?GLenum,Attachment:?GLenum,Textarget:?GLenum,Texture:?GLuint,Level:?GLint,Zoffset:?GLint>>).
%% @spec (Target::enum(),Attachment::enum(),Renderbuffertarget::enum(),Renderbuffer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferRenderbuffer.xml">external</a> documentation.
+-spec framebufferRenderbuffer(enum(),enum(),enum(),integer()) -> ok.
framebufferRenderbuffer(Target,Attachment,Renderbuffertarget,Renderbuffer) ->
- wxe_util:cast(5653, <<Target:?GLenum,Attachment:?GLenum,Renderbuffertarget:?GLenum,Renderbuffer:?GLuint>>).
+ cast(5663, <<Target:?GLenum,Attachment:?GLenum,Renderbuffertarget:?GLenum,Renderbuffer:?GLuint>>).
%% @spec (Target::enum(),Attachment::enum(),Pname::enum()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFramebufferAttachmentParameter.xml">external</a> documentation.
+-spec getFramebufferAttachmentParameteriv(enum(),enum(),enum()) -> integer().
getFramebufferAttachmentParameteriv(Target,Attachment,Pname) ->
- wxe_util:call(5654, <<Target:?GLenum,Attachment:?GLenum,Pname:?GLenum>>).
+ call(5664, <<Target:?GLenum,Attachment:?GLenum,Pname:?GLenum>>).
%% @spec (Target::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenerateMipmap.xml">external</a> documentation.
+-spec generateMipmap(enum()) -> ok.
generateMipmap(Target) ->
- wxe_util:cast(5655, <<Target:?GLenum>>).
+ cast(5665, <<Target:?GLenum>>).
%% @spec (SrcX0::integer(),SrcY0::integer(),SrcX1::integer(),SrcY1::integer(),DstX0::integer(),DstY0::integer(),DstX1::integer(),DstY1::integer(),Mask::integer(),Filter::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBlitFramebuffer.xml">external</a> documentation.
+-spec blitFramebuffer(integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),enum()) -> ok.
blitFramebuffer(SrcX0,SrcY0,SrcX1,SrcY1,DstX0,DstY0,DstX1,DstY1,Mask,Filter) ->
- wxe_util:cast(5656, <<SrcX0:?GLint,SrcY0:?GLint,SrcX1:?GLint,SrcY1:?GLint,DstX0:?GLint,DstY0:?GLint,DstX1:?GLint,DstY1:?GLint,Mask:?GLbitfield,Filter:?GLenum>>).
+ cast(5666, <<SrcX0:?GLint,SrcY0:?GLint,SrcX1:?GLint,SrcY1:?GLint,DstX0:?GLint,DstY0:?GLint,DstX1:?GLint,DstY1:?GLint,Mask:?GLbitfield,Filter:?GLenum>>).
%% @spec (Target::enum(),Samples::integer(),Internalformat::enum(),Width::integer(),Height::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glRenderbufferStorageMultisample.xml">external</a> documentation.
+-spec renderbufferStorageMultisample(enum(),integer(),enum(),integer(),integer()) -> ok.
renderbufferStorageMultisample(Target,Samples,Internalformat,Width,Height) ->
- wxe_util:cast(5657, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
+ cast(5667, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLenum,Width:?GLsizei,Height:?GLsizei>>).
%% @spec (Target::enum(),Attachment::enum(),Texture::integer(),Level::integer(),Layer::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTextureLayer.xml">external</a> documentation.
+-spec framebufferTextureLayer(enum(),enum(),integer(),integer(),integer()) -> ok.
framebufferTextureLayer(Target,Attachment,Texture,Level,Layer) ->
- wxe_util:cast(5658, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Layer:?GLint>>).
-
-%% @spec (Program::integer(),Pname::enum(),Value::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramParameterARB.xml">external</a> documentation.
-programParameteriARB(Program,Pname,Value) ->
- wxe_util:cast(5659, <<Program:?GLuint,Pname:?GLenum,Value:?GLint>>).
-
-%% @spec (Target::enum(),Attachment::enum(),Texture::integer(),Level::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTextureARB.xml">external</a> documentation.
-framebufferTextureARB(Target,Attachment,Texture,Level) ->
- wxe_util:cast(5660, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint>>).
+ cast(5668, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Layer:?GLint>>).
%% @spec (Target::enum(),Attachment::enum(),Texture::integer(),Level::integer(),Face::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFramebufferTextureFaceARB.xml">external</a> documentation.
+-spec framebufferTextureFaceARB(enum(),enum(),integer(),integer(),enum()) -> ok.
framebufferTextureFaceARB(Target,Attachment,Texture,Level,Face) ->
- wxe_util:cast(5661, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Face:?GLenum>>).
-
-%% @spec (Index::integer(),Divisor::integer()) -> ok
-%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisorARB.xml">external</a> documentation.
-vertexAttribDivisorARB(Index,Divisor) ->
- wxe_util:cast(5662, <<Index:?GLuint,Divisor:?GLuint>>).
+ cast(5669, <<Target:?GLenum,Attachment:?GLenum,Texture:?GLuint,Level:?GLint,Face:?GLenum>>).
%% @spec (Target::enum(),Offset::integer(),Length::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFlushMappedBufferRange.xml">external</a> documentation.
+-spec flushMappedBufferRange(enum(),integer(),integer()) -> ok.
flushMappedBufferRange(Target,Offset,Length) ->
- wxe_util:cast(5663, <<Target:?GLenum,0:32,Offset:?GLintptr,Length:?GLsizeiptr>>).
+ cast(5670, <<Target:?GLenum,0:32,Offset:?GLintptr,Length:?GLsizeiptr>>).
%% @spec (Array::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindVertexArray.xml">external</a> documentation.
+-spec bindVertexArray(integer()) -> ok.
bindVertexArray(Array) ->
- wxe_util:cast(5664, <<Array:?GLuint>>).
+ cast(5671, <<Array:?GLuint>>).
%% @spec (Arrays::[integer()]) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteVertexArrays.xml">external</a> documentation.
+-spec deleteVertexArrays([integer()]) -> ok.
deleteVertexArrays(Arrays) ->
- wxe_util:cast(5665, <<(length(Arrays)):?GLuint,
+ cast(5672, <<(length(Arrays)):?GLuint,
(<< <<C:?GLuint>> || C <- Arrays>>)/binary,0:(((1+length(Arrays)) rem 2)*32)>>).
%% @spec (N::integer()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenVertexArrays.xml">external</a> documentation.
+-spec genVertexArrays(integer()) -> [integer()].
genVertexArrays(N) ->
- wxe_util:call(5666, <<N:?GLsizei>>).
+ call(5673, <<N:?GLsizei>>).
%% @spec (Array::integer()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsVertexArray.xml">external</a> documentation.
+-spec isVertexArray(integer()) -> 0|1.
isVertexArray(Array) ->
- wxe_util:call(5667, <<Array:?GLuint>>).
+ call(5674, <<Array:?GLuint>>).
%% @spec (Program::integer(),UniformNames::[string()]) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformIndices.xml">external</a> documentation.
+-spec getUniformIndices(integer(),[string()]) -> [integer()].
getUniformIndices(Program,UniformNames) ->
UniformNamesTemp = list_to_binary([[Str|[0]] || Str <- UniformNames ]),
- wxe_util:call(5668, <<Program:?GLuint,(length(UniformNames)):?GLuint,(size(UniformNamesTemp)):?GLuint,(UniformNamesTemp)/binary,0:((8-((size(UniformNamesTemp)+0) rem 8)) rem 8)>>).
+ call(5675, <<Program:?GLuint,(length(UniformNames)):?GLuint,(size(UniformNamesTemp)):?GLuint,(UniformNamesTemp)/binary,0:((8-((size(UniformNamesTemp)+0) rem 8)) rem 8)>>).
%% @spec (Program::integer(),UniformIndices::[integer()],Pname::enum()) -> [integer()]
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniforms.xml">external</a> documentation.
+-spec getActiveUniformsiv(integer(),[integer()],enum()) -> [integer()].
getActiveUniformsiv(Program,UniformIndices,Pname) ->
- wxe_util:call(5669, <<Program:?GLuint,(length(UniformIndices)):?GLuint,
+ call(5676, <<Program:?GLuint,(length(UniformIndices)):?GLuint,
(<< <<C:?GLuint>> || C <- UniformIndices>>)/binary,0:(((length(UniformIndices)) rem 2)*32),Pname:?GLenum>>).
%% @spec (Program::integer(),UniformIndex::integer(),BufSize::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformName.xml">external</a> documentation.
+-spec getActiveUniformName(integer(),integer(),integer()) -> string().
getActiveUniformName(Program,UniformIndex,BufSize) ->
- wxe_util:call(5670, <<Program:?GLuint,UniformIndex:?GLuint,BufSize:?GLsizei>>).
+ call(5677, <<Program:?GLuint,UniformIndex:?GLuint,BufSize:?GLsizei>>).
%% @spec (Program::integer(),UniformBlockName::string()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformBlockIndex.xml">external</a> documentation.
+-spec getUniformBlockIndex(integer(),string()) -> integer().
getUniformBlockIndex(Program,UniformBlockName) ->
- wxe_util:call(5671, <<Program:?GLuint,(list_to_binary([UniformBlockName|[0]]))/binary,0:((8-((length(UniformBlockName)+ 5) rem 8)) rem 8)>>).
+ call(5678, <<Program:?GLuint,(list_to_binary([UniformBlockName|[0]]))/binary,0:((8-((length(UniformBlockName)+ 5) rem 8)) rem 8)>>).
-%% @spec (Program::integer(),UniformBlockIndex::integer(),Pname::enum(),Params::wx:wx_mem()) -> ok
+%% @spec (Program::integer(),UniformBlockIndex::integer(),Pname::enum(),Params::mem()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformBlock.xml">external</a> documentation.
+-spec getActiveUniformBlockiv(integer(),integer(),enum(),mem()) -> ok.
getActiveUniformBlockiv(Program,UniformBlockIndex,Pname,Params) ->
- wxe_util:send_bin(Params#wx_mem.bin),
- wxe_util:call(5672, <<Program:?GLuint,UniformBlockIndex:?GLuint,Pname:?GLenum>>).
+ send_bin(Params),
+ call(5679, <<Program:?GLuint,UniformBlockIndex:?GLuint,Pname:?GLenum>>).
%% @spec (Program::integer(),UniformBlockIndex::integer(),BufSize::integer()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveUniformBlockName.xml">external</a> documentation.
+-spec getActiveUniformBlockName(integer(),integer(),integer()) -> string().
getActiveUniformBlockName(Program,UniformBlockIndex,BufSize) ->
- wxe_util:call(5673, <<Program:?GLuint,UniformBlockIndex:?GLuint,BufSize:?GLsizei>>).
+ call(5680, <<Program:?GLuint,UniformBlockIndex:?GLuint,BufSize:?GLsizei>>).
%% @spec (Program::integer(),UniformBlockIndex::integer(),UniformBlockBinding::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformBlockBinding.xml">external</a> documentation.
+-spec uniformBlockBinding(integer(),integer(),integer()) -> ok.
uniformBlockBinding(Program,UniformBlockIndex,UniformBlockBinding) ->
- wxe_util:cast(5674, <<Program:?GLuint,UniformBlockIndex:?GLuint,UniformBlockBinding:?GLuint>>).
+ cast(5681, <<Program:?GLuint,UniformBlockIndex:?GLuint,UniformBlockBinding:?GLuint>>).
%% @spec (ReadTarget::enum(),WriteTarget::enum(),ReadOffset::integer(),WriteOffset::integer(),Size::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCopyBufferSubData.xml">external</a> documentation.
+-spec copyBufferSubData(enum(),enum(),integer(),integer(),integer()) -> ok.
copyBufferSubData(ReadTarget,WriteTarget,ReadOffset,WriteOffset,Size) ->
- wxe_util:cast(5675, <<ReadTarget:?GLenum,WriteTarget:?GLenum,ReadOffset:?GLintptr,WriteOffset:?GLintptr,Size:?GLsizeiptr>>).
+ cast(5682, <<ReadTarget:?GLenum,WriteTarget:?GLenum,ReadOffset:?GLintptr,WriteOffset:?GLintptr,Size:?GLsizeiptr>>).
+
+%% @spec (Mode::enum(),Count::integer(),Type::enum(),Indices::offset()|mem(),Basevertex::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsBaseVertex.xml">external</a> documentation.
+-spec drawElementsBaseVertex(enum(),integer(),enum(),offset()|mem(),integer()) -> ok.
+drawElementsBaseVertex(Mode,Count,Type,Indices,Basevertex) when is_integer(Indices) ->
+ cast(5683, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Basevertex:?GLint>>);
+drawElementsBaseVertex(Mode,Count,Type,Indices,Basevertex) ->
+ send_bin(Indices),
+ cast(5684, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Basevertex:?GLint>>).
+
+%% @spec (Mode::enum(),Start::integer(),End::integer(),Count::integer(),Type::enum(),Indices::offset()|mem(),Basevertex::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawRangeElementsBaseVertex.xml">external</a> documentation.
+-spec drawRangeElementsBaseVertex(enum(),integer(),integer(),integer(),enum(),offset()|mem(),integer()) -> ok.
+drawRangeElementsBaseVertex(Mode,Start,End,Count,Type,Indices,Basevertex) when is_integer(Indices) ->
+ cast(5685, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Basevertex:?GLint>>);
+drawRangeElementsBaseVertex(Mode,Start,End,Count,Type,Indices,Basevertex) ->
+ send_bin(Indices),
+ cast(5686, <<Mode:?GLenum,Start:?GLuint,End:?GLuint,Count:?GLsizei,Type:?GLenum,Basevertex:?GLint>>).
+
+%% @spec (Mode::enum(),Count::integer(),Type::enum(),Indices::offset()|mem(),Primcount::integer(),Basevertex::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsInstancedBaseVertex.xml">external</a> documentation.
+-spec drawElementsInstancedBaseVertex(enum(),integer(),enum(),offset()|mem(),integer(),integer()) -> ok.
+drawElementsInstancedBaseVertex(Mode,Count,Type,Indices,Primcount,Basevertex) when is_integer(Indices) ->
+ cast(5687, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Indices:?GLuint,Primcount:?GLsizei,Basevertex:?GLint>>);
+drawElementsInstancedBaseVertex(Mode,Count,Type,Indices,Primcount,Basevertex) ->
+ send_bin(Indices),
+ cast(5688, <<Mode:?GLenum,Count:?GLsizei,Type:?GLenum,Primcount:?GLsizei,Basevertex:?GLint>>).
+
+%% @spec (Mode::enum()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProvokingVertex.xml">external</a> documentation.
+-spec provokingVertex(enum()) -> ok.
+provokingVertex(Mode) ->
+ cast(5689, <<Mode:?GLenum>>).
+
+%% @spec (Condition::enum(),Flags::integer()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glFenceSync.xml">external</a> documentation.
+-spec fenceSync(enum(),integer()) -> integer().
+fenceSync(Condition,Flags) ->
+ call(5690, <<Condition:?GLenum,Flags:?GLbitfield>>).
+
+%% @spec (Sync::integer()) -> 0|1
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsSync.xml">external</a> documentation.
+-spec isSync(integer()) -> 0|1.
+isSync(Sync) ->
+ call(5691, <<Sync:?GLsync>>).
+
+%% @spec (Sync::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteSync.xml">external</a> documentation.
+-spec deleteSync(integer()) -> ok.
+deleteSync(Sync) ->
+ cast(5692, <<Sync:?GLsync>>).
+
+%% @spec (Sync::integer(),Flags::integer(),Timeout::integer()) -> enum()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClientWaitSync.xml">external</a> documentation.
+-spec clientWaitSync(integer(),integer(),integer()) -> enum().
+clientWaitSync(Sync,Flags,Timeout) ->
+ call(5693, <<Sync:?GLsync,Flags:?GLbitfield,0:32,Timeout:?GLuint64>>).
+
+%% @spec (Sync::integer(),Flags::integer(),Timeout::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWaitSync.xml">external</a> documentation.
+-spec waitSync(integer(),integer(),integer()) -> ok.
+waitSync(Sync,Flags,Timeout) ->
+ cast(5694, <<Sync:?GLsync,Flags:?GLbitfield,0:32,Timeout:?GLuint64>>).
+
+%% @spec (Pname::enum()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetInteger64v.xml">external</a> documentation.
+-spec getInteger64v(enum()) -> [integer()].
+getInteger64v(Pname) ->
+ call(5695, <<Pname:?GLenum>>).
+
+%% @spec (Sync::integer(),Pname::enum(),BufSize::integer()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSync.xml">external</a> documentation.
+-spec getSynciv(integer(),enum(),integer()) -> [integer()].
+getSynciv(Sync,Pname,BufSize) ->
+ call(5696, <<Sync:?GLsync,Pname:?GLenum,BufSize:?GLsizei>>).
+
+%% @spec (Target::enum(),Samples::integer(),Internalformat::integer(),Width::integer(),Height::integer(),Fixedsamplelocations::0|1) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage2DMultisample.xml">external</a> documentation.
+-spec texImage2DMultisample(enum(),integer(),integer(),integer(),integer(),0|1) -> ok.
+texImage2DMultisample(Target,Samples,Internalformat,Width,Height,Fixedsamplelocations) ->
+ cast(5697, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Fixedsamplelocations:?GLboolean>>).
+
+%% @spec (Target::enum(),Samples::integer(),Internalformat::integer(),Width::integer(),Height::integer(),Depth::integer(),Fixedsamplelocations::0|1) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexImage3DMultisample.xml">external</a> documentation.
+-spec texImage3DMultisample(enum(),integer(),integer(),integer(),integer(),integer(),0|1) -> ok.
+texImage3DMultisample(Target,Samples,Internalformat,Width,Height,Depth,Fixedsamplelocations) ->
+ cast(5698, <<Target:?GLenum,Samples:?GLsizei,Internalformat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Fixedsamplelocations:?GLboolean>>).
+
+%% @spec (Pname::enum(),Index::integer()) -> {float(),float()}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetMultisample.xml">external</a> documentation.
+-spec getMultisamplefv(enum(),integer()) -> {float(),float()}.
+getMultisamplefv(Pname,Index) ->
+ call(5699, <<Pname:?GLenum,Index:?GLuint>>).
+
+%% @spec (Index::integer(),Mask::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSampleMaski.xml">external</a> documentation.
+-spec sampleMaski(integer(),integer()) -> ok.
+sampleMaski(Index,Mask) ->
+ cast(5700, <<Index:?GLuint,Mask:?GLbitfield>>).
+
+%% @spec (Type::enum(),Name::string(),String::string()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glNamedStringARB.xml">external</a> documentation.
+-spec namedStringARB(enum(),string(),string()) -> ok.
+namedStringARB(Type,Name,String) ->
+ cast(5701, <<Type:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8),(list_to_binary([String|[0]]))/binary,0:((8-((length(String)+ 1) rem 8)) rem 8)>>).
+
+%% @spec (Name::string()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteNamedStringARB.xml">external</a> documentation.
+-spec deleteNamedStringARB(string()) -> ok.
+deleteNamedStringARB(Name) ->
+ cast(5702, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+
+%% @spec (Shader::integer(),Path::[string()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShaderIncludeARB.xml">external</a> documentation.
+-spec compileShaderIncludeARB(integer(),[string()]) -> ok.
+compileShaderIncludeARB(Shader,Path) ->
+ PathTemp = list_to_binary([[Str|[0]] || Str <- Path ]),
+ cast(5703, <<Shader:?GLuint,(length(Path)):?GLuint,(size(PathTemp)):?GLuint,(PathTemp)/binary,0:((8-((size(PathTemp)+0) rem 8)) rem 8)>>).
+
+%% @spec (Name::string()) -> 0|1
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsNamedStringARB.xml">external</a> documentation.
+-spec isNamedStringARB(string()) -> 0|1.
+isNamedStringARB(Name) ->
+ call(5704, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+
+%% @spec (Name::string(),BufSize::integer()) -> string()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetNamedStringARB.xml">external</a> documentation.
+-spec getNamedStringARB(string(),integer()) -> string().
+getNamedStringARB(Name,BufSize) ->
+ call(5705, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8),BufSize:?GLsizei>>).
+
+%% @spec (Name::string(),Pname::enum()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetNamedStringARB.xml">external</a> documentation.
+-spec getNamedStringivARB(string(),enum()) -> integer().
+getNamedStringivARB(Name,Pname) ->
+ call(5706, <<(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8),Pname:?GLenum>>).
+
+%% @spec (Program::integer(),ColorNumber::integer(),Index::integer(),Name::string()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindFragDataLocationIndexe.xml">external</a> documentation.
+-spec bindFragDataLocationIndexed(integer(),integer(),integer(),string()) -> ok.
+bindFragDataLocationIndexed(Program,ColorNumber,Index,Name) ->
+ cast(5707, <<Program:?GLuint,ColorNumber:?GLuint,Index:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+
+%% @spec (Program::integer(),Name::string()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFragDataIndex.xml">external</a> documentation.
+-spec getFragDataIndex(integer(),string()) -> integer().
+getFragDataIndex(Program,Name) ->
+ call(5708, <<Program:?GLuint,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 5) rem 8)) rem 8)>>).
+
+%% @spec (Count::integer()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenSamplers.xml">external</a> documentation.
+-spec genSamplers(integer()) -> [integer()].
+genSamplers(Count) ->
+ call(5709, <<Count:?GLsizei>>).
+
+%% @spec (Samplers::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteSamplers.xml">external</a> documentation.
+-spec deleteSamplers([integer()]) -> ok.
+deleteSamplers(Samplers) ->
+ cast(5710, <<(length(Samplers)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Samplers>>)/binary,0:(((1+length(Samplers)) rem 2)*32)>>).
+
+%% @spec (Sampler::integer()) -> 0|1
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsSampler.xml">external</a> documentation.
+-spec isSampler(integer()) -> 0|1.
+isSampler(Sampler) ->
+ call(5711, <<Sampler:?GLuint>>).
+
+%% @spec (Unit::integer(),Sampler::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindSampler.xml">external</a> documentation.
+-spec bindSampler(integer(),integer()) -> ok.
+bindSampler(Unit,Sampler) ->
+ cast(5712, <<Unit:?GLuint,Sampler:?GLuint>>).
+
+%% @spec (Sampler::integer(),Pname::enum(),Param::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameter.xml">external</a> documentation.
+-spec samplerParameteri(integer(),enum(),integer()) -> ok.
+samplerParameteri(Sampler,Pname,Param) ->
+ cast(5713, <<Sampler:?GLuint,Pname:?GLenum,Param:?GLint>>).
+
+%% @spec (Sampler::integer(),Pname::enum(),Param::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameter.xml">external</a> documentation.
+-spec samplerParameteriv(integer(),enum(),[integer()]) -> ok.
+samplerParameteriv(Sampler,Pname,Param) ->
+ cast(5714, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
+ (<< <<C:?GLint>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+
+%% @spec (Sampler::integer(),Pname::enum(),Param::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameter.xml">external</a> documentation.
+-spec samplerParameterf(integer(),enum(),float()) -> ok.
+samplerParameterf(Sampler,Pname,Param) ->
+ cast(5715, <<Sampler:?GLuint,Pname:?GLenum,Param:?GLfloat>>).
+
+%% @spec (Sampler::integer(),Pname::enum(),Param::[float()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameter.xml">external</a> documentation.
+-spec samplerParameterfv(integer(),enum(),[float()]) -> ok.
+samplerParameterfv(Sampler,Pname,Param) ->
+ cast(5716, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
+ (<< <<C:?GLfloat>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+
+%% @spec (Sampler::integer(),Pname::enum(),Param::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameterI.xml">external</a> documentation.
+-spec samplerParameterIiv(integer(),enum(),[integer()]) -> ok.
+samplerParameterIiv(Sampler,Pname,Param) ->
+ cast(5717, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
+ (<< <<C:?GLint>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+
+%% @spec (Sampler::integer(),Pname::enum(),Param::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glSamplerParameterI.xml">external</a> documentation.
+-spec samplerParameterIuiv(integer(),enum(),[integer()]) -> ok.
+samplerParameterIuiv(Sampler,Pname,Param) ->
+ cast(5718, <<Sampler:?GLuint,Pname:?GLenum,(length(Param)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Param>>)/binary,0:(((1+length(Param)) rem 2)*32)>>).
+
+%% @spec (Sampler::integer(),Pname::enum()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSamplerParameter.xml">external</a> documentation.
+-spec getSamplerParameteriv(integer(),enum()) -> [integer()].
+getSamplerParameteriv(Sampler,Pname) ->
+ call(5719, <<Sampler:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Sampler::integer(),Pname::enum()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSamplerParameterI.xml">external</a> documentation.
+-spec getSamplerParameterIiv(integer(),enum()) -> [integer()].
+getSamplerParameterIiv(Sampler,Pname) ->
+ call(5720, <<Sampler:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Sampler::integer(),Pname::enum()) -> [float()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSamplerParameter.xml">external</a> documentation.
+-spec getSamplerParameterfv(integer(),enum()) -> [float()].
+getSamplerParameterfv(Sampler,Pname) ->
+ call(5721, <<Sampler:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Sampler::integer(),Pname::enum()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSamplerParameterI.xml">external</a> documentation.
+-spec getSamplerParameterIuiv(integer(),enum()) -> [integer()].
+getSamplerParameterIuiv(Sampler,Pname) ->
+ call(5722, <<Sampler:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Id::integer(),Target::enum()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glQueryCounter.xml">external</a> documentation.
+-spec queryCounter(integer(),enum()) -> ok.
+queryCounter(Id,Target) ->
+ cast(5723, <<Id:?GLuint,Target:?GLenum>>).
+
+%% @spec (Id::integer(),Pname::enum()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObjecti64v.xml">external</a> documentation.
+-spec getQueryObjecti64v(integer(),enum()) -> integer().
+getQueryObjecti64v(Id,Pname) ->
+ call(5724, <<Id:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Id::integer(),Pname::enum()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryObjectui64v.xml">external</a> documentation.
+-spec getQueryObjectui64v(integer(),enum()) -> integer().
+getQueryObjectui64v(Id,Pname) ->
+ call(5725, <<Id:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Mode::enum(),Indirect::offset()|mem()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawArraysIndirect.xml">external</a> documentation.
+-spec drawArraysIndirect(enum(),offset()|mem()) -> ok.
+drawArraysIndirect(Mode,Indirect) when is_integer(Indirect) ->
+ cast(5726, <<Mode:?GLenum,Indirect:?GLuint>>);
+drawArraysIndirect(Mode,Indirect) ->
+ send_bin(Indirect),
+ cast(5727, <<Mode:?GLenum>>).
+
+%% @spec (Mode::enum(),Type::enum(),Indirect::offset()|mem()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawElementsIndirect.xml">external</a> documentation.
+-spec drawElementsIndirect(enum(),enum(),offset()|mem()) -> ok.
+drawElementsIndirect(Mode,Type,Indirect) when is_integer(Indirect) ->
+ cast(5728, <<Mode:?GLenum,Type:?GLenum,Indirect:?GLuint>>);
+drawElementsIndirect(Mode,Type,Indirect) ->
+ send_bin(Indirect),
+ cast(5729, <<Mode:?GLenum,Type:?GLenum>>).
+
+%% @spec (Location::integer(),X::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1d(integer(),float()) -> ok.
+uniform1d(Location,X) ->
+ cast(5730, <<Location:?GLint,0:32,X:?GLdouble>>).
+
+%% @spec (Location::integer(),X::float(),Y::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2d(integer(),float(),float()) -> ok.
+uniform2d(Location,X,Y) ->
+ cast(5731, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble>>).
+
+%% @spec (Location::integer(),X::float(),Y::float(),Z::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3d(integer(),float(),float(),float()) -> ok.
+uniform3d(Location,X,Y,Z) ->
+ cast(5732, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+
+%% @spec (Location::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4d(integer(),float(),float(),float(),float()) -> ok.
+uniform4d(Location,X,Y,Z,W) ->
+ cast(5733, <<Location:?GLint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+
+%% @spec (Location::integer(),Value::[float()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform1dv(integer(),[float()]) -> ok.
+uniform1dv(Location,Value) ->
+ cast(5734, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ (<< <<C:?GLdouble>> || C <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Value::[{float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform2dv(integer(),[{float(),float()}]) -> ok.
+uniform2dv(Location,Value) ->
+ cast(5735, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble>> || {V1,V2} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Value::[{float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform3dv(integer(),[{float(),float(),float()}]) -> ok.
+uniform3dv(Location,Value) ->
+ cast(5736, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>> || {V1,V2,V3} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Value::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniform.xml">external</a> documentation.
+-spec uniform4dv(integer(),[{float(),float(),float(),float()}]) -> ok.
+uniform4dv(Location,Value) ->
+ cast(5737, <<Location:?GLint,0:32,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix.xml">external</a> documentation.
+-spec uniformMatrix2dv(integer(),0|1,[{float(),float(),float(),float()}]) -> ok.
+uniformMatrix2dv(Location,Transpose,Value) ->
+ cast(5738, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix.xml">external</a> documentation.
+-spec uniformMatrix3dv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix3dv(Location,Transpose,Value) ->
+ cast(5739, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix.xml">external</a> documentation.
+-spec uniformMatrix4dv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix4dv(Location,Transpose,Value) ->
+ cast(5740, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble,V13:?GLdouble,V14:?GLdouble,V15:?GLdouble,V16:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix2x.xml">external</a> documentation.
+-spec uniformMatrix2x3dv(integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix2x3dv(Location,Transpose,Value) ->
+ cast(5741, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix2x.xml">external</a> documentation.
+-spec uniformMatrix2x4dv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix2x4dv(Location,Transpose,Value) ->
+ cast(5742, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix3x.xml">external</a> documentation.
+-spec uniformMatrix3x2dv(integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix3x2dv(Location,Transpose,Value) ->
+ cast(5743, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix3x.xml">external</a> documentation.
+-spec uniformMatrix3x4dv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix3x4dv(Location,Transpose,Value) ->
+ cast(5744, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix4x.xml">external</a> documentation.
+-spec uniformMatrix4x2dv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix4x2dv(Location,Transpose,Value) ->
+ cast(5745, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
+
+%% @spec (Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformMatrix4x.xml">external</a> documentation.
+-spec uniformMatrix4x3dv(integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+uniformMatrix4x3dv(Location,Transpose,Value) ->
+ cast(5746, <<Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer()) -> {float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniform.xml">external</a> documentation.
+-spec getUniformdv(integer(),integer()) -> {float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}.
+getUniformdv(Program,Location) ->
+ call(5747, <<Program:?GLuint,Location:?GLint>>).
+
+%% @spec (Program::integer(),Shadertype::enum(),Name::string()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSubroutineUniformLocation.xml">external</a> documentation.
+-spec getSubroutineUniformLocation(integer(),enum(),string()) -> integer().
+getSubroutineUniformLocation(Program,Shadertype,Name) ->
+ call(5748, <<Program:?GLuint,Shadertype:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+
+%% @spec (Program::integer(),Shadertype::enum(),Name::string()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetSubroutineIndex.xml">external</a> documentation.
+-spec getSubroutineIndex(integer(),enum(),string()) -> integer().
+getSubroutineIndex(Program,Shadertype,Name) ->
+ call(5749, <<Program:?GLuint,Shadertype:?GLenum,(list_to_binary([Name|[0]]))/binary,0:((8-((length(Name)+ 1) rem 8)) rem 8)>>).
+
+%% @spec (Program::integer(),Shadertype::enum(),Index::integer(),Bufsize::integer()) -> string()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveSubroutineUniformName.xml">external</a> documentation.
+-spec getActiveSubroutineUniformName(integer(),enum(),integer(),integer()) -> string().
+getActiveSubroutineUniformName(Program,Shadertype,Index,Bufsize) ->
+ call(5750, <<Program:?GLuint,Shadertype:?GLenum,Index:?GLuint,Bufsize:?GLsizei>>).
+
+%% @spec (Program::integer(),Shadertype::enum(),Index::integer(),Bufsize::integer()) -> string()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetActiveSubroutineName.xml">external</a> documentation.
+-spec getActiveSubroutineName(integer(),enum(),integer(),integer()) -> string().
+getActiveSubroutineName(Program,Shadertype,Index,Bufsize) ->
+ call(5751, <<Program:?GLuint,Shadertype:?GLenum,Index:?GLuint,Bufsize:?GLsizei>>).
+
+%% @spec (Shadertype::enum(),Indices::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUniformSubroutines.xml">external</a> documentation.
+-spec uniformSubroutinesuiv(enum(),[integer()]) -> ok.
+uniformSubroutinesuiv(Shadertype,Indices) ->
+ cast(5752, <<Shadertype:?GLenum,(length(Indices)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Indices>>)/binary,0:(((length(Indices)) rem 2)*32)>>).
+
+%% @spec (Shadertype::enum(),Location::integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformSubroutine.xml">external</a> documentation.
+-spec getUniformSubroutineuiv(enum(),integer()) -> {integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer(),integer()}.
+getUniformSubroutineuiv(Shadertype,Location) ->
+ call(5753, <<Shadertype:?GLenum,Location:?GLint>>).
+
+%% @spec (Program::integer(),Shadertype::enum(),Pname::enum()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramStage.xml">external</a> documentation.
+-spec getProgramStageiv(integer(),enum(),enum()) -> integer().
+getProgramStageiv(Program,Shadertype,Pname) ->
+ call(5754, <<Program:?GLuint,Shadertype:?GLenum,Pname:?GLenum>>).
+
+%% @spec (Pname::enum(),Value::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPatchParameter.xml">external</a> documentation.
+-spec patchParameteri(enum(),integer()) -> ok.
+patchParameteri(Pname,Value) ->
+ cast(5755, <<Pname:?GLenum,Value:?GLint>>).
+
+%% @spec (Pname::enum(),Values::[float()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPatchParameter.xml">external</a> documentation.
+-spec patchParameterfv(enum(),[float()]) -> ok.
+patchParameterfv(Pname,Values) ->
+ cast(5756, <<Pname:?GLenum,(length(Values)):?GLuint,
+ (<< <<C:?GLfloat>> || C <- Values>>)/binary,0:(((length(Values)) rem 2)*32)>>).
+
+%% @spec (Target::enum(),Id::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindTransformFeedback.xml">external</a> documentation.
+-spec bindTransformFeedback(enum(),integer()) -> ok.
+bindTransformFeedback(Target,Id) ->
+ cast(5757, <<Target:?GLenum,Id:?GLuint>>).
+
+%% @spec (Ids::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteTransformFeedbacks.xml">external</a> documentation.
+-spec deleteTransformFeedbacks([integer()]) -> ok.
+deleteTransformFeedbacks(Ids) ->
+ cast(5758, <<(length(Ids)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((1+length(Ids)) rem 2)*32)>>).
+
+%% @spec (N::integer()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenTransformFeedbacks.xml">external</a> documentation.
+-spec genTransformFeedbacks(integer()) -> [integer()].
+genTransformFeedbacks(N) ->
+ call(5759, <<N:?GLsizei>>).
+
+%% @spec (Id::integer()) -> 0|1
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsTransformFeedback.xml">external</a> documentation.
+-spec isTransformFeedback(integer()) -> 0|1.
+isTransformFeedback(Id) ->
+ call(5760, <<Id:?GLuint>>).
+
+%% @spec () -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glPauseTransformFeedback.xml">external</a> documentation.
+-spec pauseTransformFeedback() -> ok.
+pauseTransformFeedback() ->
+ cast(5761, <<>>).
+
+%% @spec () -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glResumeTransformFeedback.xml">external</a> documentation.
+-spec resumeTransformFeedback() -> ok.
+resumeTransformFeedback() ->
+ cast(5762, <<>>).
+
+%% @spec (Mode::enum(),Id::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawTransformFeedback.xml">external</a> documentation.
+-spec drawTransformFeedback(enum(),integer()) -> ok.
+drawTransformFeedback(Mode,Id) ->
+ cast(5763, <<Mode:?GLenum,Id:?GLuint>>).
+
+%% @spec (Mode::enum(),Id::integer(),Stream::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDrawTransformFeedbackStream.xml">external</a> documentation.
+-spec drawTransformFeedbackStream(enum(),integer(),integer()) -> ok.
+drawTransformFeedbackStream(Mode,Id,Stream) ->
+ cast(5764, <<Mode:?GLenum,Id:?GLuint,Stream:?GLuint>>).
+
+%% @spec (Target::enum(),Index::integer(),Id::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBeginQueryIndexe.xml">external</a> documentation.
+-spec beginQueryIndexed(enum(),integer(),integer()) -> ok.
+beginQueryIndexed(Target,Index,Id) ->
+ cast(5765, <<Target:?GLenum,Index:?GLuint,Id:?GLuint>>).
+
+%% @spec (Target::enum(),Index::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glEndQueryIndexe.xml">external</a> documentation.
+-spec endQueryIndexed(enum(),integer()) -> ok.
+endQueryIndexed(Target,Index) ->
+ cast(5766, <<Target:?GLenum,Index:?GLuint>>).
+
+%% @spec (Target::enum(),Index::integer(),Pname::enum()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetQueryIndexed.xml">external</a> documentation.
+-spec getQueryIndexediv(enum(),integer(),enum()) -> integer().
+getQueryIndexediv(Target,Index,Pname) ->
+ call(5767, <<Target:?GLenum,Index:?GLuint,Pname:?GLenum>>).
+
+%% @spec () -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glReleaseShaderCompiler.xml">external</a> documentation.
+-spec releaseShaderCompiler() -> ok.
+releaseShaderCompiler() ->
+ cast(5768, <<>>).
+
+%% @spec (Shaders::[integer()],Binaryformat::enum(),Binary::binary()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderBinary.xml">external</a> documentation.
+-spec shaderBinary([integer()],enum(),binary()) -> ok.
+shaderBinary(Shaders,Binaryformat,Binary) ->
+ send_bin(Binary),
+ cast(5769, <<(length(Shaders)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Shaders>>)/binary,0:(((1+length(Shaders)) rem 2)*32),Binaryformat:?GLenum>>).
+
+%% @spec (Shadertype::enum(),Precisiontype::enum()) -> {Range::{integer(),integer()},Precision::integer()}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetShaderPrecisionFormat.xml">external</a> documentation.
+-spec getShaderPrecisionFormat(enum(),enum()) -> {{integer(),integer()},integer()}.
+getShaderPrecisionFormat(Shadertype,Precisiontype) ->
+ call(5770, <<Shadertype:?GLenum,Precisiontype:?GLenum>>).
+
+%% @spec (N::clamp(),F::clamp()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthRange.xml">external</a> documentation.
+-spec depthRangef(clamp(),clamp()) -> ok.
+depthRangef(N,F) ->
+ cast(5771, <<N:?GLclampf,F:?GLclampf>>).
+
+%% @spec (D::clamp()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearDepthf.xml">external</a> documentation.
+-spec clearDepthf(clamp()) -> ok.
+clearDepthf(D) ->
+ cast(5772, <<D:?GLclampf>>).
+
+%% @spec (Program::integer(),BufSize::integer()) -> {BinaryFormat::enum(),Binary::binary()}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramBinary.xml">external</a> documentation.
+-spec getProgramBinary(integer(),integer()) -> {enum(),binary()}.
+getProgramBinary(Program,BufSize) ->
+ call(5773, <<Program:?GLuint,BufSize:?GLsizei>>).
+
+%% @spec (Program::integer(),BinaryFormat::enum(),Binary::binary()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramBinary.xml">external</a> documentation.
+-spec programBinary(integer(),enum(),binary()) -> ok.
+programBinary(Program,BinaryFormat,Binary) ->
+ send_bin(Binary),
+ cast(5774, <<Program:?GLuint,BinaryFormat:?GLenum>>).
+
+%% @spec (Program::integer(),Pname::enum(),Value::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramParameter.xml">external</a> documentation.
+-spec programParameteri(integer(),enum(),integer()) -> ok.
+programParameteri(Program,Pname,Value) ->
+ cast(5775, <<Program:?GLuint,Pname:?GLenum,Value:?GLint>>).
+
+%% @spec (Pipeline::integer(),Stages::integer(),Program::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glUseProgramStages.xml">external</a> documentation.
+-spec useProgramStages(integer(),integer(),integer()) -> ok.
+useProgramStages(Pipeline,Stages,Program) ->
+ cast(5776, <<Pipeline:?GLuint,Stages:?GLbitfield,Program:?GLuint>>).
+
+%% @spec (Pipeline::integer(),Program::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glActiveShaderProgram.xml">external</a> documentation.
+-spec activeShaderProgram(integer(),integer()) -> ok.
+activeShaderProgram(Pipeline,Program) ->
+ cast(5777, <<Pipeline:?GLuint,Program:?GLuint>>).
+
+%% @spec (Type::enum(),Strings::[string()]) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShaderProgramv.xml">external</a> documentation.
+-spec createShaderProgramv(enum(),[string()]) -> integer().
+createShaderProgramv(Type,Strings) ->
+ StringsTemp = list_to_binary([[Str|[0]] || Str <- Strings ]),
+ call(5778, <<Type:?GLenum,(length(Strings)):?GLuint,(size(StringsTemp)):?GLuint,(StringsTemp)/binary,0:((8-((size(StringsTemp)+0) rem 8)) rem 8)>>).
+
+%% @spec (Pipeline::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glBindProgramPipeline.xml">external</a> documentation.
+-spec bindProgramPipeline(integer()) -> ok.
+bindProgramPipeline(Pipeline) ->
+ cast(5779, <<Pipeline:?GLuint>>).
+
+%% @spec (Pipelines::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDeleteProgramPipelines.xml">external</a> documentation.
+-spec deleteProgramPipelines([integer()]) -> ok.
+deleteProgramPipelines(Pipelines) ->
+ cast(5780, <<(length(Pipelines)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Pipelines>>)/binary,0:(((1+length(Pipelines)) rem 2)*32)>>).
+
+%% @spec (N::integer()) -> [integer()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGenProgramPipelines.xml">external</a> documentation.
+-spec genProgramPipelines(integer()) -> [integer()].
+genProgramPipelines(N) ->
+ call(5781, <<N:?GLsizei>>).
+
+%% @spec (Pipeline::integer()) -> 0|1
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glIsProgramPipeline.xml">external</a> documentation.
+-spec isProgramPipeline(integer()) -> 0|1.
+isProgramPipeline(Pipeline) ->
+ call(5782, <<Pipeline:?GLuint>>).
+
+%% @spec (Pipeline::integer(),Pname::enum()) -> integer()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramPipeline.xml">external</a> documentation.
+-spec getProgramPipelineiv(integer(),enum()) -> integer().
+getProgramPipelineiv(Pipeline,Pname) ->
+ call(5783, <<Pipeline:?GLuint,Pname:?GLenum>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1i(integer(),integer(),integer()) -> ok.
+programUniform1i(Program,Location,V0) ->
+ cast(5784, <<Program:?GLuint,Location:?GLint,V0:?GLint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1iv(integer(),integer(),[integer()]) -> ok.
+programUniform1iv(Program,Location,Value) ->
+ cast(5785, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<C:?GLint>> || C <- Value>>)/binary,0:(((1+length(Value)) rem 2)*32)>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1f(integer(),integer(),float()) -> ok.
+programUniform1f(Program,Location,V0) ->
+ cast(5786, <<Program:?GLuint,Location:?GLint,V0:?GLfloat>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[float()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1fv(integer(),integer(),[float()]) -> ok.
+programUniform1fv(Program,Location,Value) ->
+ cast(5787, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<C:?GLfloat>> || C <- Value>>)/binary,0:(((1+length(Value)) rem 2)*32)>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1d(integer(),integer(),float()) -> ok.
+programUniform1d(Program,Location,V0) ->
+ cast(5788, <<Program:?GLuint,Location:?GLint,V0:?GLdouble>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[float()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1dv(integer(),integer(),[float()]) -> ok.
+programUniform1dv(Program,Location,Value) ->
+ cast(5789, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ (<< <<C:?GLdouble>> || C <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1ui(integer(),integer(),integer()) -> ok.
+programUniform1ui(Program,Location,V0) ->
+ cast(5790, <<Program:?GLuint,Location:?GLint,V0:?GLuint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[integer()]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform1uiv(integer(),integer(),[integer()]) -> ok.
+programUniform1uiv(Program,Location,Value) ->
+ cast(5791, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Value>>)/binary,0:(((1+length(Value)) rem 2)*32)>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer(),V1::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2i(integer(),integer(),integer(),integer()) -> ok.
+programUniform2i(Program,Location,V0,V1) ->
+ cast(5792, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2iv(integer(),integer(),[{integer(),integer()}]) -> ok.
+programUniform2iv(Program,Location,Value) ->
+ cast(5793, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLint,V2:?GLint>> || {V1,V2} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float(),V1::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2f(integer(),integer(),float(),float()) -> ok.
+programUniform2f(Program,Location,V0,V1) ->
+ cast(5794, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2fv(integer(),integer(),[{float(),float()}]) -> ok.
+programUniform2fv(Program,Location,Value) ->
+ cast(5795, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat>> || {V1,V2} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float(),V1::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2d(integer(),integer(),float(),float()) -> ok.
+programUniform2d(Program,Location,V0,V1) ->
+ cast(5796, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2dv(integer(),integer(),[{float(),float()}]) -> ok.
+programUniform2dv(Program,Location,Value) ->
+ cast(5797, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble>> || {V1,V2} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer(),V1::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2ui(integer(),integer(),integer(),integer()) -> ok.
+programUniform2ui(Program,Location,V0,V1) ->
+ cast(5798, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform2uiv(integer(),integer(),[{integer(),integer()}]) -> ok.
+programUniform2uiv(Program,Location,Value) ->
+ cast(5799, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLuint,V2:?GLuint>> || {V1,V2} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer(),V1::integer(),V2::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3i(integer(),integer(),integer(),integer(),integer()) -> ok.
+programUniform3i(Program,Location,V0,V1,V2) ->
+ cast(5800, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{integer(),integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3iv(integer(),integer(),[{integer(),integer(),integer()}]) -> ok.
+programUniform3iv(Program,Location,Value) ->
+ cast(5801, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLint,V2:?GLint,V3:?GLint>> || {V1,V2,V3} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float(),V1::float(),V2::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3f(integer(),integer(),float(),float(),float()) -> ok.
+programUniform3f(Program,Location,V0,V1,V2) ->
+ cast(5802, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3fv(integer(),integer(),[{float(),float(),float()}]) -> ok.
+programUniform3fv(Program,Location,Value) ->
+ cast(5803, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>> || {V1,V2,V3} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float(),V1::float(),V2::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3d(integer(),integer(),float(),float(),float()) -> ok.
+programUniform3d(Program,Location,V0,V1,V2) ->
+ cast(5804, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble,V2:?GLdouble>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3dv(integer(),integer(),[{float(),float(),float()}]) -> ok.
+programUniform3dv(Program,Location,Value) ->
+ cast(5805, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>> || {V1,V2,V3} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer(),V1::integer(),V2::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3ui(integer(),integer(),integer(),integer(),integer()) -> ok.
+programUniform3ui(Program,Location,V0,V1,V2) ->
+ cast(5806, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{integer(),integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform3uiv(integer(),integer(),[{integer(),integer(),integer()}]) -> ok.
+programUniform3uiv(Program,Location,Value) ->
+ cast(5807, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint>> || {V1,V2,V3} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer(),V1::integer(),V2::integer(),V3::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4i(integer(),integer(),integer(),integer(),integer(),integer()) -> ok.
+programUniform4i(Program,Location,V0,V1,V2,V3) ->
+ cast(5808, <<Program:?GLuint,Location:?GLint,V0:?GLint,V1:?GLint,V2:?GLint,V3:?GLint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{integer(),integer(),integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4iv(integer(),integer(),[{integer(),integer(),integer(),integer()}]) -> ok.
+programUniform4iv(Program,Location,Value) ->
+ cast(5809, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float(),V1::float(),V2::float(),V3::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4f(integer(),integer(),float(),float(),float(),float()) -> ok.
+programUniform4f(Program,Location,V0,V1,V2,V3) ->
+ cast(5810, <<Program:?GLuint,Location:?GLint,V0:?GLfloat,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4fv(integer(),integer(),[{float(),float(),float(),float()}]) -> ok.
+programUniform4fv(Program,Location,Value) ->
+ cast(5811, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::float(),V1::float(),V2::float(),V3::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4d(integer(),integer(),float(),float(),float(),float()) -> ok.
+programUniform4d(Program,Location,V0,V1,V2,V3) ->
+ cast(5812, <<Program:?GLuint,Location:?GLint,V0:?GLdouble,V1:?GLdouble,V2:?GLdouble,V3:?GLdouble>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4dv(integer(),integer(),[{float(),float(),float(),float()}]) -> ok.
+programUniform4dv(Program,Location,Value) ->
+ cast(5813, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),V0::integer(),V1::integer(),V2::integer(),V3::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4ui(integer(),integer(),integer(),integer(),integer(),integer()) -> ok.
+programUniform4ui(Program,Location,V0,V1,V2,V3) ->
+ cast(5814, <<Program:?GLuint,Location:?GLint,V0:?GLuint,V1:?GLuint,V2:?GLuint,V3:?GLuint>>).
+
+%% @spec (Program::integer(),Location::integer(),Value::[{integer(),integer(),integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniform.xml">external</a> documentation.
+-spec programUniform4uiv(integer(),integer(),[{integer(),integer(),integer(),integer()}]) -> ok.
+programUniform4uiv(Program,Location,Value) ->
+ cast(5815, <<Program:?GLuint,Location:?GLint,(length(Value)):?GLuint,
+ (<< <<V1:?GLuint,V2:?GLuint,V3:?GLuint,V4:?GLuint>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix.xml">external</a> documentation.
+-spec programUniformMatrix2fv(integer(),integer(),0|1,[{float(),float(),float(),float()}]) -> ok.
+programUniformMatrix2fv(Program,Location,Transpose,Value) ->
+ cast(5816, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix.xml">external</a> documentation.
+-spec programUniformMatrix3fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix3fv(Program,Location,Transpose,Value) ->
+ cast(5817, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix.xml">external</a> documentation.
+-spec programUniformMatrix4fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix4fv(Program,Location,Transpose,Value) ->
+ cast(5818, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat,V13:?GLfloat,V14:?GLfloat,V15:?GLfloat,V16:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix.xml">external</a> documentation.
+-spec programUniformMatrix2dv(integer(),integer(),0|1,[{float(),float(),float(),float()}]) -> ok.
+programUniformMatrix2dv(Program,Location,Transpose,Value) ->
+ cast(5819, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble>> || {V1,V2,V3,V4} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix.xml">external</a> documentation.
+-spec programUniformMatrix3dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix3dv(Program,Location,Transpose,Value) ->
+ cast(5820, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix.xml">external</a> documentation.
+-spec programUniformMatrix4dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix4dv(Program,Location,Transpose,Value) ->
+ cast(5821, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble,V13:?GLdouble,V14:?GLdouble,V15:?GLdouble,V16:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix2x.xml">external</a> documentation.
+-spec programUniformMatrix2x3fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix2x3fv(Program,Location,Transpose,Value) ->
+ cast(5822, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix3x.xml">external</a> documentation.
+-spec programUniformMatrix3x2fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix3x2fv(Program,Location,Transpose,Value) ->
+ cast(5823, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix2x.xml">external</a> documentation.
+-spec programUniformMatrix2x4fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix2x4fv(Program,Location,Transpose,Value) ->
+ cast(5824, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix4x.xml">external</a> documentation.
+-spec programUniformMatrix4x2fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix4x2fv(Program,Location,Transpose,Value) ->
+ cast(5825, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix3x.xml">external</a> documentation.
+-spec programUniformMatrix3x4fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix3x4fv(Program,Location,Transpose,Value) ->
+ cast(5826, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix4x.xml">external</a> documentation.
+-spec programUniformMatrix4x3fv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix4x3fv(Program,Location,Transpose,Value) ->
+ cast(5827, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:24,(length(Value)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat,V5:?GLfloat,V6:?GLfloat,V7:?GLfloat,V8:?GLfloat,V9:?GLfloat,V10:?GLfloat,V11:?GLfloat,V12:?GLfloat>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix2x.xml">external</a> documentation.
+-spec programUniformMatrix2x3dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix2x3dv(Program,Location,Transpose,Value) ->
+ cast(5828, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix3x.xml">external</a> documentation.
+-spec programUniformMatrix3x2dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix3x2dv(Program,Location,Transpose,Value) ->
+ cast(5829, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble>> || {V1,V2,V3,V4,V5,V6} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix2x.xml">external</a> documentation.
+-spec programUniformMatrix2x4dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix2x4dv(Program,Location,Transpose,Value) ->
+ cast(5830, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix4x.xml">external</a> documentation.
+-spec programUniformMatrix4x2dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix4x2dv(Program,Location,Transpose,Value) ->
+ cast(5831, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix3x.xml">external</a> documentation.
+-spec programUniformMatrix3x4dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix3x4dv(Program,Location,Transpose,Value) ->
+ cast(5832, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
+
+%% @spec (Program::integer(),Location::integer(),Transpose::0|1,Value::[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glProgramUniformMatrix4x.xml">external</a> documentation.
+-spec programUniformMatrix4x3dv(integer(),integer(),0|1,[{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()}]) -> ok.
+programUniformMatrix4x3dv(Program,Location,Transpose,Value) ->
+ cast(5833, <<Program:?GLuint,Location:?GLint,Transpose:?GLboolean,0:56,(length(Value)):?GLuint,0:32,
+ (<< <<V1:?GLdouble,V2:?GLdouble,V3:?GLdouble,V4:?GLdouble,V5:?GLdouble,V6:?GLdouble,V7:?GLdouble,V8:?GLdouble,V9:?GLdouble,V10:?GLdouble,V11:?GLdouble,V12:?GLdouble>> || {V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12} <- Value>>)/binary>>).
+
+%% @spec (Pipeline::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glValidateProgramPipeline.xml">external</a> documentation.
+-spec validateProgramPipeline(integer()) -> ok.
+validateProgramPipeline(Pipeline) ->
+ cast(5834, <<Pipeline:?GLuint>>).
+
+%% @spec (Pipeline::integer(),BufSize::integer()) -> string()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetProgramPipelineInfoLog.xml">external</a> documentation.
+-spec getProgramPipelineInfoLog(integer(),integer()) -> string().
+getProgramPipelineInfoLog(Pipeline,BufSize) ->
+ call(5835, <<Pipeline:?GLuint,BufSize:?GLsizei>>).
+
+%% @spec (Index::integer(),X::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
+-spec vertexAttribL1d(integer(),float()) -> ok.
+vertexAttribL1d(Index,X) ->
+ cast(5836, <<Index:?GLuint,0:32,X:?GLdouble>>).
+
+%% @spec (Index::integer(),X::float(),Y::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
+-spec vertexAttribL2d(integer(),float(),float()) -> ok.
+vertexAttribL2d(Index,X,Y) ->
+ cast(5837, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble>>).
+
+%% @spec (Index::integer(),X::float(),Y::float(),Z::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
+-spec vertexAttribL3d(integer(),float(),float(),float()) -> ok.
+vertexAttribL3d(Index,X,Y,Z) ->
+ cast(5838, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble>>).
+
+%% @spec (Index::integer(),X::float(),Y::float(),Z::float(),W::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribL.xml">external</a> documentation.
+-spec vertexAttribL4d(integer(),float(),float(),float(),float()) -> ok.
+vertexAttribL4d(Index,X,Y,Z,W) ->
+ cast(5839, <<Index:?GLuint,0:32,X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+
+%% @spec (Index,{X}) -> ok
+%% @equiv vertexAttribL1d(Index,X)
+-spec vertexAttribL1dv(integer(),{float()}) -> ok.
+vertexAttribL1dv(Index,{X}) -> vertexAttribL1d(Index,X).
+
+%% @spec (Index,{X,Y}) -> ok
+%% @equiv vertexAttribL2d(Index,X,Y)
+-spec vertexAttribL2dv(integer(),{float(),float()}) -> ok.
+vertexAttribL2dv(Index,{X,Y}) -> vertexAttribL2d(Index,X,Y).
+
+%% @spec (Index,{X,Y,Z}) -> ok
+%% @equiv vertexAttribL3d(Index,X,Y,Z)
+-spec vertexAttribL3dv(integer(),{float(),float(),float()}) -> ok.
+vertexAttribL3dv(Index,{X,Y,Z}) -> vertexAttribL3d(Index,X,Y,Z).
+
+%% @spec (Index,{X,Y,Z,W}) -> ok
+%% @equiv vertexAttribL4d(Index,X,Y,Z,W)
+-spec vertexAttribL4dv(integer(),{float(),float(),float(),float()}) -> ok.
+vertexAttribL4dv(Index,{X,Y,Z,W}) -> vertexAttribL4d(Index,X,Y,Z,W).
+
+%% @spec (Index::integer(),Size::integer(),Type::enum(),Stride::integer(),Pointer::offset()|mem()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribLPointer.xml">external</a> documentation.
+-spec vertexAttribLPointer(integer(),integer(),enum(),integer(),offset()|mem()) -> ok.
+vertexAttribLPointer(Index,Size,Type,Stride,Pointer) when is_integer(Pointer) ->
+ cast(5840, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei,Pointer:?GLuint>>);
+vertexAttribLPointer(Index,Size,Type,Stride,Pointer) ->
+ send_bin(Pointer),
+ cast(5841, <<Index:?GLuint,Size:?GLint,Type:?GLenum,Stride:?GLsizei>>).
+
+%% @spec (Index::integer(),Pname::enum()) -> {float(),float(),float(),float()}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetVertexAttribL.xml">external</a> documentation.
+-spec getVertexAttribLdv(integer(),enum()) -> {float(),float(),float(),float()}.
+getVertexAttribLdv(Index,Pname) ->
+ call(5842, <<Index:?GLuint,Pname:?GLenum>>).
+
+%% @spec (First::integer(),V::[{float(),float(),float(),float()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glViewportArrayv.xml">external</a> documentation.
+-spec viewportArrayv(integer(),[{float(),float(),float(),float()}]) -> ok.
+viewportArrayv(First,V) ->
+ cast(5843, <<First:?GLuint,(length(V)):?GLuint,
+ (<< <<V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>> || {V1,V2,V3,V4} <- V>>)/binary>>).
+
+%% @spec (Index::integer(),X::float(),Y::float(),W::float(),H::float()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glViewportIndexed.xml">external</a> documentation.
+-spec viewportIndexedf(integer(),float(),float(),float(),float()) -> ok.
+viewportIndexedf(Index,X,Y,W,H) ->
+ cast(5844, <<Index:?GLuint,X:?GLfloat,Y:?GLfloat,W:?GLfloat,H:?GLfloat>>).
+
+%% @spec (Index::integer(),V::{float(),float(),float(),float()}) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glViewportIndexed.xml">external</a> documentation.
+-spec viewportIndexedfv(integer(),{float(),float(),float(),float()}) -> ok.
+viewportIndexedfv(Index,{V1,V2,V3,V4}) ->
+ cast(5845, <<Index:?GLuint,V1:?GLfloat,V2:?GLfloat,V3:?GLfloat,V4:?GLfloat>>).
+
+%% @spec (First::integer(),V::[{integer(),integer(),integer(),integer()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissorArrayv.xml">external</a> documentation.
+-spec scissorArrayv(integer(),[{integer(),integer(),integer(),integer()}]) -> ok.
+scissorArrayv(First,V) ->
+ cast(5846, <<First:?GLuint,(length(V)):?GLuint,
+ (<< <<V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>> || {V1,V2,V3,V4} <- V>>)/binary>>).
+
+%% @spec (Index::integer(),Left::integer(),Bottom::integer(),Width::integer(),Height::integer()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissorIndexe.xml">external</a> documentation.
+-spec scissorIndexed(integer(),integer(),integer(),integer(),integer()) -> ok.
+scissorIndexed(Index,Left,Bottom,Width,Height) ->
+ cast(5847, <<Index:?GLuint,Left:?GLint,Bottom:?GLint,Width:?GLsizei,Height:?GLsizei>>).
+
+%% @spec (Index::integer(),V::{integer(),integer(),integer(),integer()}) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glScissorIndexe.xml">external</a> documentation.
+-spec scissorIndexedv(integer(),{integer(),integer(),integer(),integer()}) -> ok.
+scissorIndexedv(Index,{V1,V2,V3,V4}) ->
+ cast(5848, <<Index:?GLuint,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+
+%% @spec (First::integer(),V::[{clamp(),clamp()}]) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthRangeArrayv.xml">external</a> documentation.
+-spec depthRangeArrayv(integer(),[{clamp(),clamp()}]) -> ok.
+depthRangeArrayv(First,V) ->
+ cast(5849, <<First:?GLuint,0:32,(length(V)):?GLuint,0:32,
+ (<< <<V1:?GLclampd,V2:?GLclampd>> || {V1,V2} <- V>>)/binary>>).
+
+%% @spec (Index::integer(),N::clamp(),F::clamp()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthRangeIndexe.xml">external</a> documentation.
+-spec depthRangeIndexed(integer(),clamp(),clamp()) -> ok.
+depthRangeIndexed(Index,N,F) ->
+ cast(5850, <<Index:?GLuint,0:32,N:?GLclampd,F:?GLclampd>>).
+
+%% @spec (Target::enum(),Index::integer()) -> [float()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetFloati_v.xml">external</a> documentation.
+-spec getFloati_v(enum(),integer()) -> [float()].
+getFloati_v(Target,Index) ->
+ call(5851, <<Target:?GLenum,Index:?GLuint>>).
+
+%% @spec (Target::enum(),Index::integer()) -> [float()]
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetDoublei_v.xml">external</a> documentation.
+-spec getDoublei_v(enum(),integer()) -> [float()].
+getDoublei_v(Target,Index) ->
+ call(5852, <<Target:?GLenum,Index:?GLuint>>).
+
+%% @spec (Source::enum(),Type::enum(),Severity::enum(),Ids::[integer()],Enabled::0|1) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDebugMessageControlARB.xml">external</a> documentation.
+-spec debugMessageControlARB(enum(),enum(),enum(),[integer()],0|1) -> ok.
+debugMessageControlARB(Source,Type,Severity,Ids,Enabled) ->
+ cast(5853, <<Source:?GLenum,Type:?GLenum,Severity:?GLenum,(length(Ids)):?GLuint,
+ (<< <<C:?GLuint>> || C <- Ids>>)/binary,0:(((length(Ids)) rem 2)*32),Enabled:?GLboolean>>).
+
+%% @spec (Source::enum(),Type::enum(),Id::integer(),Severity::enum(),Buf::string()) -> ok
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDebugMessageInsertARB.xml">external</a> documentation.
+-spec debugMessageInsertARB(enum(),enum(),integer(),enum(),string()) -> ok.
+debugMessageInsertARB(Source,Type,Id,Severity,Buf) ->
+ cast(5854, <<Source:?GLenum,Type:?GLenum,Id:?GLuint,Severity:?GLenum,(list_to_binary([Buf|[0]]))/binary,0:((8-((length(Buf)+ 1) rem 8)) rem 8)>>).
+
+%% @spec (Count::integer(),Bufsize::integer()) -> {integer(),Sources::[enum()],Types::[enum()],Ids::[integer()],Severities::[enum()],MessageLog::[string()]}
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetDebugMessageLogARB.xml">external</a> documentation.
+-spec getDebugMessageLogARB(integer(),integer()) -> {integer(),[enum()],[enum()],[integer()],[enum()],[string()]}.
+getDebugMessageLogARB(Count,Bufsize) ->
+ call(5855, <<Count:?GLuint,Bufsize:?GLsizei>>).
+
+%% @spec () -> enum()
+%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetGraphicsResetStatusARB.xml">external</a> documentation.
+-spec getGraphicsResetStatusARB() -> enum().
+getGraphicsResetStatusARB() ->
+ call(5856, <<>>).
%% @spec () -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glResizeBuffersMESA.xml">external</a> documentation.
+-spec resizeBuffersMESA() -> ok.
resizeBuffersMESA() ->
- wxe_util:cast(5676, <<>>).
+ cast(5857, <<>>).
%% @spec (X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos4dMESA.xml">external</a> documentation.
+-spec windowPos4dMESA(float(),float(),float(),float()) -> ok.
windowPos4dMESA(X,Y,Z,W) ->
- wxe_util:cast(5677, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
+ cast(5858, <<X:?GLdouble,Y:?GLdouble,Z:?GLdouble,W:?GLdouble>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv windowPos4dMESA(X,Y,Z,W)
+-spec windowPos4dvMESA({float(),float(),float(),float()}) -> ok.
windowPos4dvMESA({X,Y,Z,W}) -> windowPos4dMESA(X,Y,Z,W).
%% @spec (X::float(),Y::float(),Z::float(),W::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos4fMESA.xml">external</a> documentation.
+-spec windowPos4fMESA(float(),float(),float(),float()) -> ok.
windowPos4fMESA(X,Y,Z,W) ->
- wxe_util:cast(5678, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
+ cast(5859, <<X:?GLfloat,Y:?GLfloat,Z:?GLfloat,W:?GLfloat>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv windowPos4fMESA(X,Y,Z,W)
+-spec windowPos4fvMESA({float(),float(),float(),float()}) -> ok.
windowPos4fvMESA({X,Y,Z,W}) -> windowPos4fMESA(X,Y,Z,W).
%% @spec (X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos4iMESA.xml">external</a> documentation.
+-spec windowPos4iMESA(integer(),integer(),integer(),integer()) -> ok.
windowPos4iMESA(X,Y,Z,W) ->
- wxe_util:cast(5679, <<X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
+ cast(5860, <<X:?GLint,Y:?GLint,Z:?GLint,W:?GLint>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv windowPos4iMESA(X,Y,Z,W)
+-spec windowPos4ivMESA({integer(),integer(),integer(),integer()}) -> ok.
windowPos4ivMESA({X,Y,Z,W}) -> windowPos4iMESA(X,Y,Z,W).
%% @spec (X::integer(),Y::integer(),Z::integer(),W::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glWindowPos4sMESA.xml">external</a> documentation.
+-spec windowPos4sMESA(integer(),integer(),integer(),integer()) -> ok.
windowPos4sMESA(X,Y,Z,W) ->
- wxe_util:cast(5680, <<X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
+ cast(5861, <<X:?GLshort,Y:?GLshort,Z:?GLshort,W:?GLshort>>).
%% @spec ({X,Y,Z,W}) -> ok
%% @equiv windowPos4sMESA(X,Y,Z,W)
+-spec windowPos4svMESA({integer(),integer(),integer(),integer()}) -> ok.
windowPos4svMESA({X,Y,Z,W}) -> windowPos4sMESA(X,Y,Z,W).
%% @spec (Zmin::clamp(),Zmax::clamp()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glDepthBoundsEXT.xml">external</a> documentation.
+-spec depthBoundsEXT(clamp(),clamp()) -> ok.
depthBoundsEXT(Zmin,Zmax) ->
- wxe_util:cast(5681, <<Zmin:?GLclampd,Zmax:?GLclampd>>).
+ cast(5862, <<Zmin:?GLclampd,Zmax:?GLclampd>>).
%% @spec (StencilTagBits::integer(),StencilClearTag::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glStencilClearTagEXT.xml">external</a> documentation.
+-spec stencilClearTagEXT(integer(),integer()) -> ok.
stencilClearTagEXT(StencilTagBits,StencilClearTag) ->
- wxe_util:cast(5682, <<StencilTagBits:?GLsizei,StencilClearTag:?GLuint>>).
+ cast(5863, <<StencilTagBits:?GLsizei,StencilClearTag:?GLuint>>).
diff --git a/lib/wx/src/gen/gl_debug.hrl b/lib/wx/src/gen/gl_debug.hrl
deleted file mode 100644
index 0b8086f24e..0000000000
--- a/lib/wx/src/gen/gl_debug.hrl
+++ /dev/null
@@ -1,697 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%% This file is generated DO NOT EDIT
-
-gldebug_table() ->
-[
- {5037, {gl, accum, 0}},
- {5038, {gl, alphaFunc, 0}},
- {5039, {gl, areTexturesResident, 0}},
- {5040, {gl, arrayElement, 0}},
- {5041, {gl, 'begin', 0}},
- {5042, {gl, bindTexture, 0}},
- {5043, {gl, bitmap, 0}},
- {5044, {gl, bitmap, 0}},
- {5045, {gl, blendFunc, 0}},
- {5046, {gl, callList, 0}},
- {5047, {gl, callLists, 0}},
- {5048, {gl, clear, 0}},
- {5049, {gl, clearAccum, 0}},
- {5050, {gl, clearColor, 0}},
- {5051, {gl, clearDepth, 0}},
- {5052, {gl, clearIndex, 0}},
- {5053, {gl, clearStencil, 0}},
- {5054, {gl, clipPlane, 0}},
- {5055, {gl, color3b, 0}},
- {5056, {gl, color3d, 0}},
- {5057, {gl, color3f, 0}},
- {5058, {gl, color3i, 0}},
- {5059, {gl, color3s, 0}},
- {5060, {gl, color3ub, 0}},
- {5061, {gl, color3ui, 0}},
- {5062, {gl, color3us, 0}},
- {5063, {gl, color4b, 0}},
- {5064, {gl, color4d, 0}},
- {5065, {gl, color4f, 0}},
- {5066, {gl, color4i, 0}},
- {5067, {gl, color4s, 0}},
- {5068, {gl, color4ub, 0}},
- {5069, {gl, color4ui, 0}},
- {5070, {gl, color4us, 0}},
- {5071, {gl, colorMask, 0}},
- {5072, {gl, colorMaterial, 0}},
- {5073, {gl, colorPointer, 0}},
- {5074, {gl, colorPointer, 0}},
- {5075, {gl, copyPixels, 0}},
- {5076, {gl, copyTexImage1D, 0}},
- {5077, {gl, copyTexImage2D, 0}},
- {5078, {gl, copyTexSubImage1D, 0}},
- {5079, {gl, copyTexSubImage2D, 0}},
- {5080, {gl, cullFace, 0}},
- {5081, {gl, deleteLists, 0}},
- {5082, {gl, deleteTextures, 0}},
- {5083, {gl, depthFunc, 0}},
- {5084, {gl, depthMask, 0}},
- {5085, {gl, depthRange, 0}},
- {5086, {gl, disable, 0}},
- {5087, {gl, disableClientState, 0}},
- {5088, {gl, drawArrays, 0}},
- {5089, {gl, drawBuffer, 0}},
- {5090, {gl, drawElements, 0}},
- {5091, {gl, drawElements, 0}},
- {5092, {gl, drawPixels, 0}},
- {5093, {gl, drawPixels, 0}},
- {5094, {gl, edgeFlag, 0}},
- {5095, {gl, edgeFlagPointer, 0}},
- {5096, {gl, edgeFlagPointer, 0}},
- {5097, {gl, enable, 0}},
- {5098, {gl, enableClientState, 0}},
- {5099, {gl, 'end', 0}},
- {5100, {gl, endList, 0}},
- {5101, {gl, evalCoord1d, 0}},
- {5102, {gl, evalCoord1f, 0}},
- {5103, {gl, evalCoord2d, 0}},
- {5104, {gl, evalCoord2f, 0}},
- {5105, {gl, evalMesh1, 0}},
- {5106, {gl, evalMesh2, 0}},
- {5107, {gl, evalPoint1, 0}},
- {5108, {gl, evalPoint2, 0}},
- {5109, {gl, feedbackBuffer, 0}},
- {5110, {gl, finish, 0}},
- {5111, {gl, flush, 0}},
- {5112, {gl, fogf, 0}},
- {5113, {gl, fogfv, 0}},
- {5114, {gl, fogi, 0}},
- {5115, {gl, fogiv, 0}},
- {5116, {gl, frontFace, 0}},
- {5117, {gl, frustum, 0}},
- {5118, {gl, genLists, 0}},
- {5119, {gl, genTextures, 0}},
- {5120, {gl, getBooleanv, 0}},
- {5121, {gl, getClipPlane, 0}},
- {5122, {gl, getDoublev, 0}},
- {5123, {gl, getError, 0}},
- {5124, {gl, getFloatv, 0}},
- {5125, {gl, getIntegerv, 0}},
- {5126, {gl, getLightfv, 0}},
- {5127, {gl, getLightiv, 0}},
- {5128, {gl, getMapdv, 0}},
- {5129, {gl, getMapfv, 0}},
- {5130, {gl, getMapiv, 0}},
- {5131, {gl, getMaterialfv, 0}},
- {5132, {gl, getMaterialiv, 0}},
- {5133, {gl, getPixelMapfv, 0}},
- {5134, {gl, getPixelMapuiv, 0}},
- {5135, {gl, getPixelMapusv, 0}},
- {5136, {gl, getPolygonStipple, 0}},
- {5137, {gl, getString, 0}},
- {5138, {gl, getTexEnvfv, 0}},
- {5139, {gl, getTexEnviv, 0}},
- {5140, {gl, getTexGendv, 0}},
- {5141, {gl, getTexGenfv, 0}},
- {5142, {gl, getTexGeniv, 0}},
- {5143, {gl, getTexImage, 0}},
- {5144, {gl, getTexLevelParameterfv, 0}},
- {5145, {gl, getTexLevelParameteriv, 0}},
- {5146, {gl, getTexParameterfv, 0}},
- {5147, {gl, getTexParameteriv, 0}},
- {5148, {gl, hint, 0}},
- {5149, {gl, indexMask, 0}},
- {5150, {gl, indexPointer, 0}},
- {5151, {gl, indexPointer, 0}},
- {5152, {gl, indexd, 0}},
- {5153, {gl, indexf, 0}},
- {5154, {gl, indexi, 0}},
- {5155, {gl, indexs, 0}},
- {5156, {gl, indexub, 0}},
- {5157, {gl, initNames, 0}},
- {5158, {gl, interleavedArrays, 0}},
- {5159, {gl, interleavedArrays, 0}},
- {5160, {gl, isEnabled, 0}},
- {5161, {gl, isList, 0}},
- {5162, {gl, isTexture, 0}},
- {5163, {gl, lightModelf, 0}},
- {5164, {gl, lightModelfv, 0}},
- {5165, {gl, lightModeli, 0}},
- {5166, {gl, lightModeliv, 0}},
- {5167, {gl, lightf, 0}},
- {5168, {gl, lightfv, 0}},
- {5169, {gl, lighti, 0}},
- {5170, {gl, lightiv, 0}},
- {5171, {gl, lineStipple, 0}},
- {5172, {gl, lineWidth, 0}},
- {5173, {gl, listBase, 0}},
- {5174, {gl, loadIdentity, 0}},
- {5175, {gl, loadMatrixd, 0}},
- {5176, {gl, loadMatrixf, 0}},
- {5177, {gl, loadName, 0}},
- {5178, {gl, logicOp, 0}},
- {5179, {gl, map1d, 0}},
- {5180, {gl, map1f, 0}},
- {5181, {gl, map2d, 0}},
- {5182, {gl, map2f, 0}},
- {5183, {gl, mapGrid1d, 0}},
- {5184, {gl, mapGrid1f, 0}},
- {5185, {gl, mapGrid2d, 0}},
- {5186, {gl, mapGrid2f, 0}},
- {5187, {gl, materialf, 0}},
- {5188, {gl, materialfv, 0}},
- {5189, {gl, materiali, 0}},
- {5190, {gl, materialiv, 0}},
- {5191, {gl, matrixMode, 0}},
- {5192, {gl, multMatrixd, 0}},
- {5193, {gl, multMatrixf, 0}},
- {5194, {gl, newList, 0}},
- {5195, {gl, normal3b, 0}},
- {5196, {gl, normal3d, 0}},
- {5197, {gl, normal3f, 0}},
- {5198, {gl, normal3i, 0}},
- {5199, {gl, normal3s, 0}},
- {5200, {gl, normalPointer, 0}},
- {5201, {gl, normalPointer, 0}},
- {5202, {gl, ortho, 0}},
- {5203, {gl, passThrough, 0}},
- {5204, {gl, pixelMapfv, 0}},
- {5205, {gl, pixelMapuiv, 0}},
- {5206, {gl, pixelMapusv, 0}},
- {5207, {gl, pixelStoref, 0}},
- {5208, {gl, pixelStorei, 0}},
- {5209, {gl, pixelTransferf, 0}},
- {5210, {gl, pixelTransferi, 0}},
- {5211, {gl, pixelZoom, 0}},
- {5212, {gl, pointSize, 0}},
- {5213, {gl, polygonMode, 0}},
- {5214, {gl, polygonOffset, 0}},
- {5215, {gl, polygonStipple, 0}},
- {5216, {gl, popAttrib, 0}},
- {5217, {gl, popClientAttrib, 0}},
- {5218, {gl, popMatrix, 0}},
- {5219, {gl, popName, 0}},
- {5220, {gl, prioritizeTextures, 0}},
- {5221, {gl, pushAttrib, 0}},
- {5222, {gl, pushClientAttrib, 0}},
- {5223, {gl, pushMatrix, 0}},
- {5224, {gl, pushName, 0}},
- {5225, {gl, rasterPos2d, 0}},
- {5226, {gl, rasterPos2f, 0}},
- {5227, {gl, rasterPos2i, 0}},
- {5228, {gl, rasterPos2s, 0}},
- {5229, {gl, rasterPos3d, 0}},
- {5230, {gl, rasterPos3f, 0}},
- {5231, {gl, rasterPos3i, 0}},
- {5232, {gl, rasterPos3s, 0}},
- {5233, {gl, rasterPos4d, 0}},
- {5234, {gl, rasterPos4f, 0}},
- {5235, {gl, rasterPos4i, 0}},
- {5236, {gl, rasterPos4s, 0}},
- {5237, {gl, readBuffer, 0}},
- {5238, {gl, readPixels, 0}},
- {5239, {gl, rectd, 0}},
- {5240, {gl, rectdv, 0}},
- {5241, {gl, rectf, 0}},
- {5242, {gl, rectfv, 0}},
- {5243, {gl, recti, 0}},
- {5244, {gl, rectiv, 0}},
- {5245, {gl, rects, 0}},
- {5246, {gl, rectsv, 0}},
- {5247, {gl, renderMode, 0}},
- {5248, {gl, rotated, 0}},
- {5249, {gl, rotatef, 0}},
- {5250, {gl, scaled, 0}},
- {5251, {gl, scalef, 0}},
- {5252, {gl, scissor, 0}},
- {5253, {gl, selectBuffer, 0}},
- {5254, {gl, shadeModel, 0}},
- {5255, {gl, stencilFunc, 0}},
- {5256, {gl, stencilMask, 0}},
- {5257, {gl, stencilOp, 0}},
- {5258, {gl, texCoord1d, 0}},
- {5259, {gl, texCoord1f, 0}},
- {5260, {gl, texCoord1i, 0}},
- {5261, {gl, texCoord1s, 0}},
- {5262, {gl, texCoord2d, 0}},
- {5263, {gl, texCoord2f, 0}},
- {5264, {gl, texCoord2i, 0}},
- {5265, {gl, texCoord2s, 0}},
- {5266, {gl, texCoord3d, 0}},
- {5267, {gl, texCoord3f, 0}},
- {5268, {gl, texCoord3i, 0}},
- {5269, {gl, texCoord3s, 0}},
- {5270, {gl, texCoord4d, 0}},
- {5271, {gl, texCoord4f, 0}},
- {5272, {gl, texCoord4i, 0}},
- {5273, {gl, texCoord4s, 0}},
- {5274, {gl, texCoordPointer, 0}},
- {5275, {gl, texCoordPointer, 0}},
- {5276, {gl, texEnvf, 0}},
- {5277, {gl, texEnvfv, 0}},
- {5278, {gl, texEnvi, 0}},
- {5279, {gl, texEnviv, 0}},
- {5280, {gl, texGend, 0}},
- {5281, {gl, texGendv, 0}},
- {5282, {gl, texGenf, 0}},
- {5283, {gl, texGenfv, 0}},
- {5284, {gl, texGeni, 0}},
- {5285, {gl, texGeniv, 0}},
- {5286, {gl, texImage1D, 0}},
- {5287, {gl, texImage1D, 0}},
- {5288, {gl, texImage2D, 0}},
- {5289, {gl, texImage2D, 0}},
- {5290, {gl, texParameterf, 0}},
- {5291, {gl, texParameterfv, 0}},
- {5292, {gl, texParameteri, 0}},
- {5293, {gl, texParameteriv, 0}},
- {5294, {gl, texSubImage1D, 0}},
- {5295, {gl, texSubImage1D, 0}},
- {5296, {gl, texSubImage2D, 0}},
- {5297, {gl, texSubImage2D, 0}},
- {5298, {gl, translated, 0}},
- {5299, {gl, translatef, 0}},
- {5300, {gl, vertex2d, 0}},
- {5301, {gl, vertex2f, 0}},
- {5302, {gl, vertex2i, 0}},
- {5303, {gl, vertex2s, 0}},
- {5304, {gl, vertex3d, 0}},
- {5305, {gl, vertex3f, 0}},
- {5306, {gl, vertex3i, 0}},
- {5307, {gl, vertex3s, 0}},
- {5308, {gl, vertex4d, 0}},
- {5309, {gl, vertex4f, 0}},
- {5310, {gl, vertex4i, 0}},
- {5311, {gl, vertex4s, 0}},
- {5312, {gl, vertexPointer, 0}},
- {5313, {gl, vertexPointer, 0}},
- {5314, {gl, viewport, 0}},
- {5315, {gl, blendColor, 0}},
- {5316, {gl, blendEquation, 0}},
- {5317, {gl, drawRangeElements, 0}},
- {5318, {gl, drawRangeElements, 0}},
- {5319, {gl, texImage3D, 0}},
- {5320, {gl, texImage3D, 0}},
- {5321, {gl, texSubImage3D, 0}},
- {5322, {gl, texSubImage3D, 0}},
- {5323, {gl, copyTexSubImage3D, 0}},
- {5324, {gl, colorTable, 0}},
- {5325, {gl, colorTable, 0}},
- {5326, {gl, colorTableParameterfv, 0}},
- {5327, {gl, colorTableParameteriv, 0}},
- {5328, {gl, copyColorTable, 0}},
- {5329, {gl, getColorTable, 0}},
- {5330, {gl, getColorTableParameterfv, 0}},
- {5331, {gl, getColorTableParameteriv, 0}},
- {5332, {gl, colorSubTable, 0}},
- {5333, {gl, colorSubTable, 0}},
- {5334, {gl, copyColorSubTable, 0}},
- {5335, {gl, convolutionFilter1D, 0}},
- {5336, {gl, convolutionFilter1D, 0}},
- {5337, {gl, convolutionFilter2D, 0}},
- {5338, {gl, convolutionFilter2D, 0}},
- {5339, {gl, convolutionParameterf, 0}},
- {5340, {gl, convolutionParameteri, 0}},
- {5341, {gl, copyConvolutionFilter1D, 0}},
- {5342, {gl, copyConvolutionFilter2D, 0}},
- {5343, {gl, getConvolutionFilter, 0}},
- {5344, {gl, getConvolutionParameterfv, 0}},
- {5345, {gl, getConvolutionParameteriv, 0}},
- {5346, {gl, separableFilter2D, 0}},
- {5347, {gl, separableFilter2D, 0}},
- {5348, {gl, getHistogram, 0}},
- {5349, {gl, getHistogramParameterfv, 0}},
- {5350, {gl, getHistogramParameteriv, 0}},
- {5351, {gl, getMinmax, 0}},
- {5352, {gl, getMinmaxParameterfv, 0}},
- {5353, {gl, getMinmaxParameteriv, 0}},
- {5354, {gl, histogram, 0}},
- {5355, {gl, minmax, 0}},
- {5356, {gl, resetHistogram, 0}},
- {5357, {gl, resetMinmax, 0}},
- {5358, {gl, activeTexture, 0}},
- {5359, {gl, sampleCoverage, 0}},
- {5360, {gl, compressedTexImage3D, 0}},
- {5361, {gl, compressedTexImage3D, 0}},
- {5362, {gl, compressedTexImage2D, 0}},
- {5363, {gl, compressedTexImage2D, 0}},
- {5364, {gl, compressedTexImage1D, 0}},
- {5365, {gl, compressedTexImage1D, 0}},
- {5366, {gl, compressedTexSubImage3D, 0}},
- {5367, {gl, compressedTexSubImage3D, 0}},
- {5368, {gl, compressedTexSubImage2D, 0}},
- {5369, {gl, compressedTexSubImage2D, 0}},
- {5370, {gl, compressedTexSubImage1D, 0}},
- {5371, {gl, compressedTexSubImage1D, 0}},
- {5372, {gl, getCompressedTexImage, 0}},
- {5373, {gl, clientActiveTexture, 0}},
- {5374, {gl, multiTexCoord1d, 0}},
- {5375, {gl, multiTexCoord1f, 0}},
- {5376, {gl, multiTexCoord1i, 0}},
- {5377, {gl, multiTexCoord1s, 0}},
- {5378, {gl, multiTexCoord2d, 0}},
- {5379, {gl, multiTexCoord2f, 0}},
- {5380, {gl, multiTexCoord2i, 0}},
- {5381, {gl, multiTexCoord2s, 0}},
- {5382, {gl, multiTexCoord3d, 0}},
- {5383, {gl, multiTexCoord3f, 0}},
- {5384, {gl, multiTexCoord3i, 0}},
- {5385, {gl, multiTexCoord3s, 0}},
- {5386, {gl, multiTexCoord4d, 0}},
- {5387, {gl, multiTexCoord4f, 0}},
- {5388, {gl, multiTexCoord4i, 0}},
- {5389, {gl, multiTexCoord4s, 0}},
- {5390, {gl, loadTransposeMatrixf, 0}},
- {5391, {gl, loadTransposeMatrixd, 0}},
- {5392, {gl, multTransposeMatrixf, 0}},
- {5393, {gl, multTransposeMatrixd, 0}},
- {5394, {gl, blendFuncSeparate, 0}},
- {5395, {gl, multiDrawArrays, 0}},
- {5396, {gl, pointParameterf, 0}},
- {5397, {gl, pointParameterfv, 0}},
- {5398, {gl, pointParameteri, 0}},
- {5399, {gl, pointParameteriv, 0}},
- {5400, {gl, fogCoordf, 0}},
- {5401, {gl, fogCoordd, 0}},
- {5402, {gl, fogCoordPointer, 0}},
- {5403, {gl, fogCoordPointer, 0}},
- {5404, {gl, secondaryColor3b, 0}},
- {5405, {gl, secondaryColor3d, 0}},
- {5406, {gl, secondaryColor3f, 0}},
- {5407, {gl, secondaryColor3i, 0}},
- {5408, {gl, secondaryColor3s, 0}},
- {5409, {gl, secondaryColor3ub, 0}},
- {5410, {gl, secondaryColor3ui, 0}},
- {5411, {gl, secondaryColor3us, 0}},
- {5412, {gl, secondaryColorPointer, 0}},
- {5413, {gl, secondaryColorPointer, 0}},
- {5414, {gl, windowPos2d, 0}},
- {5415, {gl, windowPos2f, 0}},
- {5416, {gl, windowPos2i, 0}},
- {5417, {gl, windowPos2s, 0}},
- {5418, {gl, windowPos3d, 0}},
- {5419, {gl, windowPos3f, 0}},
- {5420, {gl, windowPos3i, 0}},
- {5421, {gl, windowPos3s, 0}},
- {5422, {gl, genQueries, 0}},
- {5423, {gl, deleteQueries, 0}},
- {5424, {gl, isQuery, 0}},
- {5425, {gl, beginQuery, 0}},
- {5426, {gl, endQuery, 0}},
- {5427, {gl, getQueryiv, 0}},
- {5428, {gl, getQueryObjectiv, 0}},
- {5429, {gl, getQueryObjectuiv, 0}},
- {5430, {gl, bindBuffer, 0}},
- {5431, {gl, deleteBuffers, 0}},
- {5432, {gl, genBuffers, 0}},
- {5433, {gl, isBuffer, 0}},
- {5434, {gl, bufferData, 0}},
- {5435, {gl, bufferData, 0}},
- {5436, {gl, bufferSubData, 0}},
- {5437, {gl, bufferSubData, 0}},
- {5438, {gl, getBufferSubData, 0}},
- {5439, {gl, getBufferParameteriv, 0}},
- {5440, {gl, blendEquationSeparate, 0}},
- {5441, {gl, drawBuffers, 0}},
- {5442, {gl, stencilOpSeparate, 0}},
- {5443, {gl, stencilFuncSeparate, 0}},
- {5444, {gl, stencilMaskSeparate, 0}},
- {5445, {gl, attachShader, 0}},
- {5446, {gl, bindAttribLocation, 0}},
- {5447, {gl, compileShader, 0}},
- {5448, {gl, createProgram, 0}},
- {5449, {gl, createShader, 0}},
- {5450, {gl, deleteProgram, 0}},
- {5451, {gl, deleteShader, 0}},
- {5452, {gl, detachShader, 0}},
- {5453, {gl, disableVertexAttribArray, 0}},
- {5454, {gl, enableVertexAttribArray, 0}},
- {5455, {gl, getActiveAttrib, 0}},
- {5456, {gl, getActiveUniform, 0}},
- {5457, {gl, getAttachedShaders, 0}},
- {5458, {gl, getAttribLocation, 0}},
- {5459, {gl, getProgramiv, 0}},
- {5460, {gl, getProgramInfoLog, 0}},
- {5461, {gl, getShaderiv, 0}},
- {5462, {gl, getShaderInfoLog, 0}},
- {5463, {gl, getShaderSource, 0}},
- {5464, {gl, getUniformLocation, 0}},
- {5465, {gl, getUniformfv, 0}},
- {5466, {gl, getUniformiv, 0}},
- {5467, {gl, getVertexAttribdv, 0}},
- {5468, {gl, getVertexAttribfv, 0}},
- {5469, {gl, getVertexAttribiv, 0}},
- {5470, {gl, isProgram, 0}},
- {5471, {gl, isShader, 0}},
- {5472, {gl, linkProgram, 0}},
- {5473, {gl, shaderSource, 0}},
- {5474, {gl, useProgram, 0}},
- {5475, {gl, uniform1f, 0}},
- {5476, {gl, uniform2f, 0}},
- {5477, {gl, uniform3f, 0}},
- {5478, {gl, uniform4f, 0}},
- {5479, {gl, uniform1i, 0}},
- {5480, {gl, uniform2i, 0}},
- {5481, {gl, uniform3i, 0}},
- {5482, {gl, uniform4i, 0}},
- {5483, {gl, uniform1fv, 0}},
- {5484, {gl, uniform2fv, 0}},
- {5485, {gl, uniform3fv, 0}},
- {5486, {gl, uniform4fv, 0}},
- {5487, {gl, uniform1iv, 0}},
- {5488, {gl, uniform2iv, 0}},
- {5489, {gl, uniform3iv, 0}},
- {5490, {gl, uniform4iv, 0}},
- {5491, {gl, uniformMatrix2fv, 0}},
- {5492, {gl, uniformMatrix3fv, 0}},
- {5493, {gl, uniformMatrix4fv, 0}},
- {5494, {gl, validateProgram, 0}},
- {5495, {gl, vertexAttrib1d, 0}},
- {5496, {gl, vertexAttrib1f, 0}},
- {5497, {gl, vertexAttrib1s, 0}},
- {5498, {gl, vertexAttrib2d, 0}},
- {5499, {gl, vertexAttrib2f, 0}},
- {5500, {gl, vertexAttrib2s, 0}},
- {5501, {gl, vertexAttrib3d, 0}},
- {5502, {gl, vertexAttrib3f, 0}},
- {5503, {gl, vertexAttrib3s, 0}},
- {5504, {gl, vertexAttrib4Nbv, 0}},
- {5505, {gl, vertexAttrib4Niv, 0}},
- {5506, {gl, vertexAttrib4Nsv, 0}},
- {5507, {gl, vertexAttrib4Nub, 0}},
- {5508, {gl, vertexAttrib4Nuiv, 0}},
- {5509, {gl, vertexAttrib4Nusv, 0}},
- {5510, {gl, vertexAttrib4bv, 0}},
- {5511, {gl, vertexAttrib4d, 0}},
- {5512, {gl, vertexAttrib4f, 0}},
- {5513, {gl, vertexAttrib4iv, 0}},
- {5514, {gl, vertexAttrib4s, 0}},
- {5515, {gl, vertexAttrib4ubv, 0}},
- {5516, {gl, vertexAttrib4uiv, 0}},
- {5517, {gl, vertexAttrib4usv, 0}},
- {5518, {gl, vertexAttribPointer, 0}},
- {5519, {gl, vertexAttribPointer, 0}},
- {5520, {gl, uniformMatrix2x3fv, 0}},
- {5521, {gl, uniformMatrix3x2fv, 0}},
- {5522, {gl, uniformMatrix2x4fv, 0}},
- {5523, {gl, uniformMatrix4x2fv, 0}},
- {5524, {gl, uniformMatrix3x4fv, 0}},
- {5525, {gl, uniformMatrix4x3fv, 0}},
- {5526, {gl, colorMaski, 0}},
- {5527, {gl, getBooleani_v, 0}},
- {5528, {gl, getIntegeri_v, 0}},
- {5529, {gl, enablei, 0}},
- {5530, {gl, disablei, 0}},
- {5531, {gl, isEnabledi, 0}},
- {5532, {gl, beginTransformFeedback, 0}},
- {5533, {gl, endTransformFeedback, 0}},
- {5534, {gl, bindBufferRange, 0}},
- {5535, {gl, bindBufferBase, 0}},
- {5536, {gl, transformFeedbackVaryings, 0}},
- {5537, {gl, getTransformFeedbackVarying, 0}},
- {5538, {gl, clampColor, 0}},
- {5539, {gl, beginConditionalRender, 0}},
- {5540, {gl, endConditionalRender, 0}},
- {5541, {gl, vertexAttribIPointer, 0}},
- {5542, {gl, vertexAttribIPointer, 0}},
- {5543, {gl, getVertexAttribIiv, 0}},
- {5544, {gl, getVertexAttribIuiv, 0}},
- {5545, {gl, getUniformuiv, 0}},
- {5546, {gl, bindFragDataLocation, 0}},
- {5547, {gl, getFragDataLocation, 0}},
- {5548, {gl, uniform1ui, 0}},
- {5549, {gl, uniform2ui, 0}},
- {5550, {gl, uniform3ui, 0}},
- {5551, {gl, uniform4ui, 0}},
- {5552, {gl, uniform1uiv, 0}},
- {5553, {gl, uniform2uiv, 0}},
- {5554, {gl, uniform3uiv, 0}},
- {5555, {gl, uniform4uiv, 0}},
- {5556, {gl, texParameterIiv, 0}},
- {5557, {gl, texParameterIuiv, 0}},
- {5558, {gl, getTexParameterIiv, 0}},
- {5559, {gl, getTexParameterIuiv, 0}},
- {5560, {gl, clearBufferiv, 0}},
- {5561, {gl, clearBufferuiv, 0}},
- {5562, {gl, clearBufferfv, 0}},
- {5563, {gl, clearBufferfi, 0}},
- {5564, {gl, getStringi, 0}},
- {5565, {gl, vertexAttribI1i, 0}},
- {5566, {gl, vertexAttribI2i, 0}},
- {5567, {gl, vertexAttribI3i, 0}},
- {5568, {gl, vertexAttribI4i, 0}},
- {5569, {gl, vertexAttribI1ui, 0}},
- {5570, {gl, vertexAttribI2ui, 0}},
- {5571, {gl, vertexAttribI3ui, 0}},
- {5572, {gl, vertexAttribI4ui, 0}},
- {5573, {gl, vertexAttribI4bv, 0}},
- {5574, {gl, vertexAttribI4sv, 0}},
- {5575, {gl, vertexAttribI4ubv, 0}},
- {5576, {gl, vertexAttribI4usv, 0}},
- {5577, {gl, drawArraysInstanced, 0}},
- {5578, {gl, drawElementsInstanced, 0}},
- {5579, {gl, drawElementsInstanced, 0}},
- {5580, {gl, texBuffer, 0}},
- {5581, {gl, primitiveRestartIndex, 0}},
- {5582, {gl, loadTransposeMatrixfARB, 0}},
- {5583, {gl, loadTransposeMatrixdARB, 0}},
- {5584, {gl, multTransposeMatrixfARB, 0}},
- {5585, {gl, multTransposeMatrixdARB, 0}},
- {5586, {gl, weightbvARB, 0}},
- {5587, {gl, weightsvARB, 0}},
- {5588, {gl, weightivARB, 0}},
- {5589, {gl, weightfvARB, 0}},
- {5590, {gl, weightdvARB, 0}},
- {5591, {gl, weightubvARB, 0}},
- {5592, {gl, weightusvARB, 0}},
- {5593, {gl, weightuivARB, 0}},
- {5594, {gl, vertexBlendARB, 0}},
- {5595, {gl, currentPaletteMatrixARB, 0}},
- {5596, {gl, matrixIndexubvARB, 0}},
- {5597, {gl, matrixIndexusvARB, 0}},
- {5598, {gl, matrixIndexuivARB, 0}},
- {5599, {gl, programStringARB, 0}},
- {5600, {gl, bindProgramARB, 0}},
- {5601, {gl, deleteProgramsARB, 0}},
- {5602, {gl, genProgramsARB, 0}},
- {5603, {gl, programEnvParameter4dARB, 0}},
- {5604, {gl, programEnvParameter4dvARB, 0}},
- {5605, {gl, programEnvParameter4fARB, 0}},
- {5606, {gl, programEnvParameter4fvARB, 0}},
- {5607, {gl, programLocalParameter4dARB, 0}},
- {5608, {gl, programLocalParameter4dvARB, 0}},
- {5609, {gl, programLocalParameter4fARB, 0}},
- {5610, {gl, programLocalParameter4fvARB, 0}},
- {5611, {gl, getProgramEnvParameterdvARB, 0}},
- {5612, {gl, getProgramEnvParameterfvARB, 0}},
- {5613, {gl, getProgramLocalParameterdvARB, 0}},
- {5614, {gl, getProgramLocalParameterfvARB, 0}},
- {5615, {gl, getProgramStringARB, 0}},
- {5616, {gl, deleteObjectARB, 0}},
- {5617, {gl, getHandleARB, 0}},
- {5618, {gl, detachObjectARB, 0}},
- {5619, {gl, createShaderObjectARB, 0}},
- {5620, {gl, shaderSourceARB, 0}},
- {5621, {gl, compileShaderARB, 0}},
- {5622, {gl, createProgramObjectARB, 0}},
- {5623, {gl, attachObjectARB, 0}},
- {5624, {gl, linkProgramARB, 0}},
- {5625, {gl, useProgramObjectARB, 0}},
- {5626, {gl, validateProgramARB, 0}},
- {5627, {gl, getObjectParameterfvARB, 0}},
- {5628, {gl, getObjectParameterivARB, 0}},
- {5629, {gl, getInfoLogARB, 0}},
- {5630, {gl, getAttachedObjectsARB, 0}},
- {5631, {gl, getUniformLocationARB, 0}},
- {5632, {gl, getActiveUniformARB, 0}},
- {5633, {gl, getUniformfvARB, 0}},
- {5634, {gl, getUniformivARB, 0}},
- {5635, {gl, getShaderSourceARB, 0}},
- {5636, {gl, bindAttribLocationARB, 0}},
- {5637, {gl, getActiveAttribARB, 0}},
- {5638, {gl, getAttribLocationARB, 0}},
- {5639, {gl, isRenderbuffer, 0}},
- {5640, {gl, bindRenderbuffer, 0}},
- {5641, {gl, deleteRenderbuffers, 0}},
- {5642, {gl, genRenderbuffers, 0}},
- {5643, {gl, renderbufferStorage, 0}},
- {5644, {gl, getRenderbufferParameteriv, 0}},
- {5645, {gl, isFramebuffer, 0}},
- {5646, {gl, bindFramebuffer, 0}},
- {5647, {gl, deleteFramebuffers, 0}},
- {5648, {gl, genFramebuffers, 0}},
- {5649, {gl, checkFramebufferStatus, 0}},
- {5650, {gl, framebufferTexture1D, 0}},
- {5651, {gl, framebufferTexture2D, 0}},
- {5652, {gl, framebufferTexture3D, 0}},
- {5653, {gl, framebufferRenderbuffer, 0}},
- {5654, {gl, getFramebufferAttachmentParameteriv, 0}},
- {5655, {gl, generateMipmap, 0}},
- {5656, {gl, blitFramebuffer, 0}},
- {5657, {gl, renderbufferStorageMultisample, 0}},
- {5658, {gl, framebufferTextureLayer, 0}},
- {5659, {gl, programParameteriARB, 0}},
- {5660, {gl, framebufferTextureARB, 0}},
- {5661, {gl, framebufferTextureFaceARB, 0}},
- {5662, {gl, vertexAttribDivisorARB, 0}},
- {5663, {gl, flushMappedBufferRange, 0}},
- {5664, {gl, bindVertexArray, 0}},
- {5665, {gl, deleteVertexArrays, 0}},
- {5666, {gl, genVertexArrays, 0}},
- {5667, {gl, isVertexArray, 0}},
- {5668, {gl, getUniformIndices, 0}},
- {5669, {gl, getActiveUniformsiv, 0}},
- {5670, {gl, getActiveUniformName, 0}},
- {5671, {gl, getUniformBlockIndex, 0}},
- {5672, {gl, getActiveUniformBlockiv, 0}},
- {5673, {gl, getActiveUniformBlockName, 0}},
- {5674, {gl, uniformBlockBinding, 0}},
- {5675, {gl, copyBufferSubData, 0}},
- {5676, {gl, resizeBuffersMESA, 0}},
- {5677, {gl, windowPos4dMESA, 0}},
- {5678, {gl, windowPos4fMESA, 0}},
- {5679, {gl, windowPos4iMESA, 0}},
- {5680, {gl, windowPos4sMESA, 0}},
- {5681, {gl, depthBoundsEXT, 0}},
- {5682, {gl, stencilClearTagEXT, 0}},
- {5010, {glu, build1DMipmapLevels, 0}},
- {5011, {glu, build1DMipmaps, 0}},
- {5012, {glu, build2DMipmapLevels, 0}},
- {5013, {glu, build2DMipmaps, 0}},
- {5014, {glu, build3DMipmapLevels, 0}},
- {5015, {glu, build3DMipmaps, 0}},
- {5016, {glu, checkExtension, 0}},
- {5017, {glu, cylinder, 0}},
- {5018, {glu, deleteQuadric, 0}},
- {5019, {glu, disk, 0}},
- {5020, {glu, errorString, 0}},
- {5021, {glu, getString, 0}},
- {5022, {glu, lookAt, 0}},
- {5023, {glu, newQuadric, 0}},
- {5024, {glu, ortho2D, 0}},
- {5025, {glu, partialDisk, 0}},
- {5026, {glu, perspective, 0}},
- {5027, {glu, pickMatrix, 0}},
- {5028, {glu, project, 0}},
- {5029, {glu, quadricDrawStyle, 0}},
- {5030, {glu, quadricNormals, 0}},
- {5031, {glu, quadricOrientation, 0}},
- {5032, {glu, quadricTexture, 0}},
- {5033, {glu, scaleImage, 0}},
- {5034, {glu, sphere, 0}},
- {5035, {glu, unProject, 0}},
- {5036, {glu, unProject4, 0}},
- {-1, {mod, func, -1}}
-].
-
diff --git a/lib/wx/src/gen/glu.erl b/lib/wx/src/gen/glu.erl
index d410c4663d..c16f0cf125 100644
--- a/lib/wx/src/gen/glu.erl
+++ b/lib/wx/src/gen/glu.erl
@@ -25,14 +25,13 @@
%%
%% Booleans are represented by integers 0 and 1.
-%% @type wx_mem(). see wx.erl on memory allocation functions
+%% @type mem(). memory block
%% @type enum(). An integer defined in gl.hrl
%% @type offset(). An integer which is an offset in an array
%% @type clamp(). A float clamped between 0.0 - 1.0
-module(glu).
-compile(inline).
--include("wxe.hrl").
-define(GLenum,32/native-unsigned).
-define(GLboolean,8/native-unsigned).
-define(GLbitfield,32/native-unsigned).
@@ -51,6 +50,11 @@
-define(GLintptr,64/native-unsigned).
-define(GLUquadric,64/native-unsigned).
-define(GLhandleARB,64/native-unsigned).
+-define(GLsync,64/native-unsigned).
+-define(GLuint64,64/native-unsigned).
+-define(GLint64,64/native-signed).
+-type enum() :: non_neg_integer().
+-type mem() :: binary() | tuple().
-export([tesselate/2,build1DMipmapLevels/9,build1DMipmaps/6,build2DMipmapLevels/10,
build2DMipmaps/7,build3DMipmapLevels/11,build3DMipmaps/8,checkExtension/2,
@@ -59,7 +63,7 @@
quadricDrawStyle/2,quadricNormals/2,quadricOrientation/2,quadricTexture/2,
scaleImage/9,sphere/4,unProject/6,unProject4/9]).
-
+-import(gl, [call/2,cast/2,send_bin/1]).
%% API
%% @spec (Vec3, [Vec3]) -> {Triangles, VertexPos}
@@ -73,159 +77,184 @@
%% vertex positions, it starts with the vertices in Vs and
%% may contain newly created vertices in the end.
tesselate({Nx,Ny,Nz}, Vs) ->
- wxe_util:call(5000, <<(length(Vs)):32/native,0:32,
+ call(5000, <<(length(Vs)):32/native,0:32,
Nx:?GLdouble,Ny:?GLdouble,Nz:?GLdouble,
(<< <<Vx:?GLdouble,Vy:?GLdouble,Vz:?GLdouble >>
|| {Vx,Vy,Vz} <- Vs>>)/binary >>).
%% @spec (Target::enum(),InternalFormat::integer(),Width::integer(),Format::enum(),Type::enum(),Level::integer(),Base::integer(),Max::integer(),Data::binary()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluBuild1DMipmapLevels.xml">external</a> documentation.
+-spec build1DMipmapLevels(enum(),integer(),integer(),enum(),enum(),integer(),integer(),integer(),binary()) -> integer().
build1DMipmapLevels(Target,InternalFormat,Width,Format,Type,Level,Base,Max,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:call(5010, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Level:?GLint,Base:?GLint,Max:?GLint>>).
+ send_bin(Data),
+ call(5010, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum,Level:?GLint,Base:?GLint,Max:?GLint>>).
%% @spec (Target::enum(),InternalFormat::integer(),Width::integer(),Format::enum(),Type::enum(),Data::binary()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluBuild1DMipmaps.xml">external</a> documentation.
+-spec build1DMipmaps(enum(),integer(),integer(),enum(),enum(),binary()) -> integer().
build1DMipmaps(Target,InternalFormat,Width,Format,Type,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:call(5011, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Data),
+ call(5011, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),InternalFormat::integer(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Level::integer(),Base::integer(),Max::integer(),Data::binary()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluBuild2DMipmapLevels.xml">external</a> documentation.
+-spec build2DMipmapLevels(enum(),integer(),integer(),integer(),enum(),enum(),integer(),integer(),integer(),binary()) -> integer().
build2DMipmapLevels(Target,InternalFormat,Width,Height,Format,Type,Level,Base,Max,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:call(5012, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Level:?GLint,Base:?GLint,Max:?GLint>>).
+ send_bin(Data),
+ call(5012, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum,Level:?GLint,Base:?GLint,Max:?GLint>>).
%% @spec (Target::enum(),InternalFormat::integer(),Width::integer(),Height::integer(),Format::enum(),Type::enum(),Data::binary()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluBuild2DMipmaps.xml">external</a> documentation.
+-spec build2DMipmaps(enum(),integer(),integer(),integer(),enum(),enum(),binary()) -> integer().
build2DMipmaps(Target,InternalFormat,Width,Height,Format,Type,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:call(5013, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Data),
+ call(5013, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Format:?GLenum,Type:?GLenum>>).
%% @spec (Target::enum(),InternalFormat::integer(),Width::integer(),Height::integer(),Depth::integer(),Format::enum(),Type::enum(),Level::integer(),Base::integer(),Max::integer(),Data::binary()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluBuild3DMipmapLevels.xml">external</a> documentation.
+-spec build3DMipmapLevels(enum(),integer(),integer(),integer(),integer(),enum(),enum(),integer(),integer(),integer(),binary()) -> integer().
build3DMipmapLevels(Target,InternalFormat,Width,Height,Depth,Format,Type,Level,Base,Max,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:call(5014, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum,Level:?GLint,Base:?GLint,Max:?GLint>>).
+ send_bin(Data),
+ call(5014, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum,Level:?GLint,Base:?GLint,Max:?GLint>>).
%% @spec (Target::enum(),InternalFormat::integer(),Width::integer(),Height::integer(),Depth::integer(),Format::enum(),Type::enum(),Data::binary()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluBuild3DMipmaps.xml">external</a> documentation.
+-spec build3DMipmaps(enum(),integer(),integer(),integer(),integer(),enum(),enum(),binary()) -> integer().
build3DMipmaps(Target,InternalFormat,Width,Height,Depth,Format,Type,Data) ->
- wxe_util:send_bin(Data),
- wxe_util:call(5015, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum>>).
+ send_bin(Data),
+ call(5015, <<Target:?GLenum,InternalFormat:?GLint,Width:?GLsizei,Height:?GLsizei,Depth:?GLsizei,Format:?GLenum,Type:?GLenum>>).
-%% @spec (ExtName::[integer()],ExtString::[integer()]) -> 0|1
+%% @spec (ExtName::string(),ExtString::string()) -> 0|1
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluCheckExtension.xml">external</a> documentation.
+-spec checkExtension(string(),string()) -> 0|1.
checkExtension(ExtName,ExtString) ->
- wxe_util:call(5016, <<(length(ExtName)):?GLuint,
- (<< <<C:?GLubyte>> || C <- ExtName>>)/binary,0:((8-((length(ExtName)+ 4) rem 8)) rem 8),(length(ExtString)):?GLuint,
- (<< <<C:?GLubyte>> || C <- ExtString>>)/binary,0:((8-((length(ExtString)+ 4) rem 8)) rem 8)>>).
+ call(5016, <<(list_to_binary([ExtName|[0]]))/binary,0:((8-((length(ExtName)+ 1) rem 8)) rem 8),(list_to_binary([ExtString|[0]]))/binary,0:((8-((length(ExtString)+ 1) rem 8)) rem 8)>>).
%% @spec (Quad::integer(),Base::float(),Top::float(),Height::float(),Slices::integer(),Stacks::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluCylinder.xml">external</a> documentation.
+-spec cylinder(integer(),float(),float(),float(),integer(),integer()) -> ok.
cylinder(Quad,Base,Top,Height,Slices,Stacks) ->
- wxe_util:cast(5017, <<Quad:?GLUquadric,Base:?GLdouble,Top:?GLdouble,Height:?GLdouble,Slices:?GLint,Stacks:?GLint>>).
+ cast(5017, <<Quad:?GLUquadric,Base:?GLdouble,Top:?GLdouble,Height:?GLdouble,Slices:?GLint,Stacks:?GLint>>).
%% @spec (Quad::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluDeleteQuadric.xml">external</a> documentation.
+-spec deleteQuadric(integer()) -> ok.
deleteQuadric(Quad) ->
- wxe_util:cast(5018, <<Quad:?GLUquadric>>).
+ cast(5018, <<Quad:?GLUquadric>>).
%% @spec (Quad::integer(),Inner::float(),Outer::float(),Slices::integer(),Loops::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluDisk.xml">external</a> documentation.
+-spec disk(integer(),float(),float(),integer(),integer()) -> ok.
disk(Quad,Inner,Outer,Slices,Loops) ->
- wxe_util:cast(5019, <<Quad:?GLUquadric,Inner:?GLdouble,Outer:?GLdouble,Slices:?GLint,Loops:?GLint>>).
+ cast(5019, <<Quad:?GLUquadric,Inner:?GLdouble,Outer:?GLdouble,Slices:?GLint,Loops:?GLint>>).
%% @spec (Error::enum()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluErrorString.xml">external</a> documentation.
+-spec errorString(enum()) -> string().
errorString(Error) ->
- wxe_util:call(5020, <<Error:?GLenum>>).
+ call(5020, <<Error:?GLenum>>).
%% @spec (Name::enum()) -> string()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluGetString.xml">external</a> documentation.
+-spec getString(enum()) -> string().
getString(Name) ->
- wxe_util:call(5021, <<Name:?GLenum>>).
+ call(5021, <<Name:?GLenum>>).
%% @spec (EyeX::float(),EyeY::float(),EyeZ::float(),CenterX::float(),CenterY::float(),CenterZ::float(),UpX::float(),UpY::float(),UpZ::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluLookAt.xml">external</a> documentation.
+-spec lookAt(float(),float(),float(),float(),float(),float(),float(),float(),float()) -> ok.
lookAt(EyeX,EyeY,EyeZ,CenterX,CenterY,CenterZ,UpX,UpY,UpZ) ->
- wxe_util:cast(5022, <<EyeX:?GLdouble,EyeY:?GLdouble,EyeZ:?GLdouble,CenterX:?GLdouble,CenterY:?GLdouble,CenterZ:?GLdouble,UpX:?GLdouble,UpY:?GLdouble,UpZ:?GLdouble>>).
+ cast(5022, <<EyeX:?GLdouble,EyeY:?GLdouble,EyeZ:?GLdouble,CenterX:?GLdouble,CenterY:?GLdouble,CenterZ:?GLdouble,UpX:?GLdouble,UpY:?GLdouble,UpZ:?GLdouble>>).
%% @spec () -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluNewQuadric.xml">external</a> documentation.
+-spec newQuadric() -> integer().
newQuadric() ->
- wxe_util:call(5023, <<>>).
+ call(5023, <<>>).
%% @spec (Left::float(),Right::float(),Bottom::float(),Top::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluOrtho2D.xml">external</a> documentation.
+-spec ortho2D(float(),float(),float(),float()) -> ok.
ortho2D(Left,Right,Bottom,Top) ->
- wxe_util:cast(5024, <<Left:?GLdouble,Right:?GLdouble,Bottom:?GLdouble,Top:?GLdouble>>).
+ cast(5024, <<Left:?GLdouble,Right:?GLdouble,Bottom:?GLdouble,Top:?GLdouble>>).
%% @spec (Quad::integer(),Inner::float(),Outer::float(),Slices::integer(),Loops::integer(),Start::float(),Sweep::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluPartialDisk.xml">external</a> documentation.
+-spec partialDisk(integer(),float(),float(),integer(),integer(),float(),float()) -> ok.
partialDisk(Quad,Inner,Outer,Slices,Loops,Start,Sweep) ->
- wxe_util:cast(5025, <<Quad:?GLUquadric,Inner:?GLdouble,Outer:?GLdouble,Slices:?GLint,Loops:?GLint,Start:?GLdouble,Sweep:?GLdouble>>).
+ cast(5025, <<Quad:?GLUquadric,Inner:?GLdouble,Outer:?GLdouble,Slices:?GLint,Loops:?GLint,Start:?GLdouble,Sweep:?GLdouble>>).
%% @spec (Fovy::float(),Aspect::float(),ZNear::float(),ZFar::float()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluPerspective.xml">external</a> documentation.
+-spec perspective(float(),float(),float(),float()) -> ok.
perspective(Fovy,Aspect,ZNear,ZFar) ->
- wxe_util:cast(5026, <<Fovy:?GLdouble,Aspect:?GLdouble,ZNear:?GLdouble,ZFar:?GLdouble>>).
+ cast(5026, <<Fovy:?GLdouble,Aspect:?GLdouble,ZNear:?GLdouble,ZFar:?GLdouble>>).
-%% @spec (X::float(),Y::float(),DelX::float(),DelY::float(),Viewport::{integer()}) -> ok
+%% @spec (X::float(),Y::float(),DelX::float(),DelY::float(),Viewport::{integer(),integer(),integer(),integer()}) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluPickMatrix.xml">external</a> documentation.
+-spec pickMatrix(float(),float(),float(),float(),{integer(),integer(),integer(),integer()}) -> ok.
pickMatrix(X,Y,DelX,DelY,{V1,V2,V3,V4}) ->
- wxe_util:cast(5027, <<X:?GLdouble,Y:?GLdouble,DelX:?GLdouble,DelY:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ cast(5027, <<X:?GLdouble,Y:?GLdouble,DelX:?GLdouble,DelY:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
-%% @spec (ObjX::float(),ObjY::float(),ObjZ::float(),Model::{float()},Proj::{float()},View::{integer()}) -> {integer(),WinX::float(),WinY::float(),WinZ::float()}
+%% @spec (ObjX::float(),ObjY::float(),ObjZ::float(),Model::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},Proj::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},View::{integer(),integer(),integer(),integer()}) -> {integer(),WinX::float(),WinY::float(),WinZ::float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluProject.xml">external</a> documentation.
+-spec project(float(),float(),float(),{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},{integer(),integer(),integer(),integer()}) -> {integer(),float(),float(),float()}.
project(ObjX,ObjY,ObjZ,{M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16},{P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15,P16},{V1,V2,V3,V4}) ->
- wxe_util:call(5028, <<ObjX:?GLdouble,ObjY:?GLdouble,ObjZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,P13:?GLdouble,P14:?GLdouble,P15:?GLdouble,P16:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>);
+ call(5028, <<ObjX:?GLdouble,ObjY:?GLdouble,ObjZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,P13:?GLdouble,P14:?GLdouble,P15:?GLdouble,P16:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>);
project(ObjX,ObjY,ObjZ,{M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12},{P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12},{V1,V2,V3,V4}) ->
- wxe_util:call(5028, <<ObjX:?GLdouble,ObjY:?GLdouble,ObjZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,0:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,0:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,0:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,1:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ call(5028, <<ObjX:?GLdouble,ObjY:?GLdouble,ObjZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,0:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,0:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,0:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,1:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
%% @spec (Quad::integer(),Draw::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluQuadricDrawStyle.xml">external</a> documentation.
+-spec quadricDrawStyle(integer(),enum()) -> ok.
quadricDrawStyle(Quad,Draw) ->
- wxe_util:cast(5029, <<Quad:?GLUquadric,Draw:?GLenum>>).
+ cast(5029, <<Quad:?GLUquadric,Draw:?GLenum>>).
%% @spec (Quad::integer(),Normal::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluQuadricNormals.xml">external</a> documentation.
+-spec quadricNormals(integer(),enum()) -> ok.
quadricNormals(Quad,Normal) ->
- wxe_util:cast(5030, <<Quad:?GLUquadric,Normal:?GLenum>>).
+ cast(5030, <<Quad:?GLUquadric,Normal:?GLenum>>).
%% @spec (Quad::integer(),Orientation::enum()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluQuadricOrientation.xml">external</a> documentation.
+-spec quadricOrientation(integer(),enum()) -> ok.
quadricOrientation(Quad,Orientation) ->
- wxe_util:cast(5031, <<Quad:?GLUquadric,Orientation:?GLenum>>).
+ cast(5031, <<Quad:?GLUquadric,Orientation:?GLenum>>).
%% @spec (Quad::integer(),Texture::0|1) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluQuadricTexture.xml">external</a> documentation.
+-spec quadricTexture(integer(),0|1) -> ok.
quadricTexture(Quad,Texture) ->
- wxe_util:cast(5032, <<Quad:?GLUquadric,Texture:?GLboolean>>).
+ cast(5032, <<Quad:?GLUquadric,Texture:?GLboolean>>).
-%% @spec (Format::enum(),WIn::integer(),HIn::integer(),TypeIn::enum(),DataIn::binary(),WOut::integer(),HOut::integer(),TypeOut::enum(),DataOut::wx:wx_mem()) -> integer()
+%% @spec (Format::enum(),WIn::integer(),HIn::integer(),TypeIn::enum(),DataIn::binary(),WOut::integer(),HOut::integer(),TypeOut::enum(),DataOut::mem()) -> integer()
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluScaleImage.xml">external</a> documentation.
+-spec scaleImage(enum(),integer(),integer(),enum(),binary(),integer(),integer(),enum(),mem()) -> integer().
scaleImage(Format,WIn,HIn,TypeIn,DataIn,WOut,HOut,TypeOut,DataOut) ->
- wxe_util:send_bin(DataIn),
- wxe_util:send_bin(DataOut#wx_mem.bin),
- wxe_util:call(5033, <<Format:?GLenum,WIn:?GLsizei,HIn:?GLsizei,TypeIn:?GLenum,WOut:?GLsizei,HOut:?GLsizei,TypeOut:?GLenum>>).
+ send_bin(DataIn),
+ send_bin(DataOut),
+ call(5033, <<Format:?GLenum,WIn:?GLsizei,HIn:?GLsizei,TypeIn:?GLenum,WOut:?GLsizei,HOut:?GLsizei,TypeOut:?GLenum>>).
%% @spec (Quad::integer(),Radius::float(),Slices::integer(),Stacks::integer()) -> ok
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluSphere.xml">external</a> documentation.
+-spec sphere(integer(),float(),integer(),integer()) -> ok.
sphere(Quad,Radius,Slices,Stacks) ->
- wxe_util:cast(5034, <<Quad:?GLUquadric,Radius:?GLdouble,Slices:?GLint,Stacks:?GLint>>).
+ cast(5034, <<Quad:?GLUquadric,Radius:?GLdouble,Slices:?GLint,Stacks:?GLint>>).
-%% @spec (WinX::float(),WinY::float(),WinZ::float(),Model::{float()},Proj::{float()},View::{integer()}) -> {integer(),ObjX::float(),ObjY::float(),ObjZ::float()}
+%% @spec (WinX::float(),WinY::float(),WinZ::float(),Model::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},Proj::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},View::{integer(),integer(),integer(),integer()}) -> {integer(),ObjX::float(),ObjY::float(),ObjZ::float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluUnProject.xml">external</a> documentation.
+-spec unProject(float(),float(),float(),{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},{integer(),integer(),integer(),integer()}) -> {integer(),float(),float(),float()}.
unProject(WinX,WinY,WinZ,{M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16},{P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15,P16},{V1,V2,V3,V4}) ->
- wxe_util:call(5035, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,P13:?GLdouble,P14:?GLdouble,P15:?GLdouble,P16:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>);
+ call(5035, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,P13:?GLdouble,P14:?GLdouble,P15:?GLdouble,P16:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>);
unProject(WinX,WinY,WinZ,{M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12},{P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12},{V1,V2,V3,V4}) ->
- wxe_util:call(5035, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,0:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,0:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,0:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,1:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
+ call(5035, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,0:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,0:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,0:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,1:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint>>).
-%% @spec (WinX::float(),WinY::float(),WinZ::float(),ClipW::float(),Model::{float()},Proj::{float()},View::{integer()},NearVal::float(),FarVal::float()) -> {integer(),ObjX::float(),ObjY::float(),ObjZ::float(),ObjW::float()}
+%% @spec (WinX::float(),WinY::float(),WinZ::float(),ClipW::float(),Model::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},Proj::{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},View::{integer(),integer(),integer(),integer()},NearVal::float(),FarVal::float()) -> {integer(),ObjX::float(),ObjY::float(),ObjZ::float(),ObjW::float()}
%% @doc See <a href="http://www.opengl.org/sdk/docs/man/xhtml/gluUnProject.xml">external</a> documentation.
+-spec unProject4(float(),float(),float(),float(),{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},{float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float(),float()},{integer(),integer(),integer(),integer()},float(),float()) -> {integer(),float(),float(),float(),float()}.
unProject4(WinX,WinY,WinZ,ClipW,{M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15,M16},{P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12,P13,P14,P15,P16},{V1,V2,V3,V4},NearVal,FarVal) ->
- wxe_util:call(5036, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,ClipW:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,P13:?GLdouble,P14:?GLdouble,P15:?GLdouble,P16:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint,NearVal:?GLdouble,FarVal:?GLdouble>>);
+ call(5036, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,ClipW:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,M13:?GLdouble,M14:?GLdouble,M15:?GLdouble,M16:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,P13:?GLdouble,P14:?GLdouble,P15:?GLdouble,P16:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint,NearVal:?GLdouble,FarVal:?GLdouble>>);
unProject4(WinX,WinY,WinZ,ClipW,{M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12},{P1,P2,P3,P4,P5,P6,P7,P8,P9,P10,P11,P12},{V1,V2,V3,V4},NearVal,FarVal) ->
- wxe_util:call(5036, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,ClipW:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,0:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,0:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,0:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,1:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint,NearVal:?GLdouble,FarVal:?GLdouble>>).
+ call(5036, <<WinX:?GLdouble,WinY:?GLdouble,WinZ:?GLdouble,ClipW:?GLdouble,M1:?GLdouble,M2:?GLdouble,M3:?GLdouble,0:?GLdouble,M4:?GLdouble,M5:?GLdouble,M6:?GLdouble,0:?GLdouble,M7:?GLdouble,M8:?GLdouble,M9:?GLdouble,0:?GLdouble,M10:?GLdouble,M11:?GLdouble,M12:?GLdouble,1:?GLdouble,P1:?GLdouble,P2:?GLdouble,P3:?GLdouble,0:?GLdouble,P4:?GLdouble,P5:?GLdouble,P6:?GLdouble,0:?GLdouble,P7:?GLdouble,P8:?GLdouble,P9:?GLdouble,0:?GLdouble,P10:?GLdouble,P11:?GLdouble,P12:?GLdouble,1:?GLdouble,V1:?GLint,V2:?GLint,V3:?GLint,V4:?GLint,NearVal:?GLdouble,FarVal:?GLdouble>>).
diff --git a/lib/wx/src/gen/wxGLCanvas.erl b/lib/wx/src/gen/wxGLCanvas.erl
index 3e0d1bd9ae..032d42535d 100644
--- a/lib/wx/src/gen/wxGLCanvas.erl
+++ b/lib/wx/src/gen/wxGLCanvas.erl
@@ -144,8 +144,10 @@ getContext(#wx_ref{type=ThisT,ref=ThisRef}) ->
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxglcanvas.html#wxglcanvassetcurrent">external documentation</a>.
setCurrent(#wx_ref{type=ThisT,ref=ThisRef}) ->
?CLASS(ThisT,wxGLCanvas),
- wxe_util:cast(?wxGLCanvas_SetCurrent,
- <<ThisRef:32/?UI>>).
+ _Result = wxe_util:cast(?wxGLCanvas_SetCurrent,
+ <<ThisRef:32/?UI>>),
+ {ok, _} = wxe_master:init_opengl(),
+ _Result.
%% @spec (This::wxGLCanvas()) -> ok
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxglcanvas.html#wxglcanvasswapbuffers">external documentation</a>.
diff --git a/lib/wx/src/gen/wxSystemSettings.erl b/lib/wx/src/gen/wxSystemSettings.erl
new file mode 100644
index 0000000000..3f7e0a1ad6
--- /dev/null
+++ b/lib/wx/src/gen/wxSystemSettings.erl
@@ -0,0 +1,79 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%% This file is generated DO NOT EDIT
+
+%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsystemsettings.html">wxSystemSettings</a>.
+%% @type wxSystemSettings(). An object reference, The representation is internal
+%% and can be changed without notice. It can't be used for comparsion
+%% stored on disc or distributed for use on other nodes.
+
+-module(wxSystemSettings).
+-include("wxe.hrl").
+-export([getColour/1,getFont/1,getMetric/1,getMetric/2,getScreenType/0]).
+
+%% inherited exports
+-export([parent_class/1]).
+
+%% @hidden
+parent_class(_Class) -> erlang:error({badtype, ?MODULE}).
+
+%% @spec (Index::WxSystemColour) -> wx:colour()
+%% WxSystemColour = integer()
+%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsystemsettings.html#wxsystemsettingsgetcolour">external documentation</a>.
+%%<br /> WxSystemColour is one of ?wxSYS_COLOUR_SCROLLBAR | ?wxSYS_COLOUR_BACKGROUND | ?wxSYS_COLOUR_DESKTOP | ?wxSYS_COLOUR_ACTIVECAPTION | ?wxSYS_COLOUR_INACTIVECAPTION | ?wxSYS_COLOUR_MENU | ?wxSYS_COLOUR_WINDOW | ?wxSYS_COLOUR_WINDOWFRAME | ?wxSYS_COLOUR_MENUTEXT | ?wxSYS_COLOUR_WINDOWTEXT | ?wxSYS_COLOUR_CAPTIONTEXT | ?wxSYS_COLOUR_ACTIVEBORDER | ?wxSYS_COLOUR_INACTIVEBORDER | ?wxSYS_COLOUR_APPWORKSPACE | ?wxSYS_COLOUR_HIGHLIGHT | ?wxSYS_COLOUR_HIGHLIGHTTEXT | ?wxSYS_COLOUR_BTNFACE | ?wxSYS_COLOUR_3DFACE | ?wxSYS_COLOUR_BTNSHADOW | ?wxSYS_COLOUR_3DSHADOW | ?wxSYS_COLOUR_GRAYTEXT | ?wxSYS_COLOUR_BTNTEXT | ?wxSYS_COLOUR_INACTIVECAPTIONTEXT | ?wxSYS_COLOUR_BTNHIGHLIGHT | ?wxSYS_COLOUR_BTNHILIGHT | ?wxSYS_COLOUR_3DHIGHLIGHT | ?wxSYS_COLOUR_3DHILIGHT | ?wxSYS_COLOUR_3DDKSHADOW | ?wxSYS_COLOUR_3DLIGHT | ?wxSYS_COLOUR_INFOTEXT | ?wxSYS_COLOUR_INFOBK | ?wxSYS_COLOUR_LISTBOX | ?wxSYS_COLOUR_HOTLIGHT | ?wxSYS_COLOUR_GRADIENTACTIVECAPTION | ?wxSYS_COLOUR_GRADIENTINACTIVECAPTION | ?wxSYS_COLOUR_MENUHILIGHT | ?wxSYS_COLOUR_MENUBAR | ?wxSYS_COLOUR_LISTBOXTEXT | ?wxSYS_COLOUR_MAX
+getColour(Index)
+ when is_integer(Index) ->
+ wxe_util:call(?wxSystemSettings_GetColour,
+ <<Index:32/?UI>>).
+
+%% @spec (Index::WxSystemFont) -> wxFont:wxFont()
+%% WxSystemFont = integer()
+%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsystemsettings.html#wxsystemsettingsgetfont">external documentation</a>.
+%%<br /> WxSystemFont is one of ?wxSYS_OEM_FIXED_FONT | ?wxSYS_ANSI_FIXED_FONT | ?wxSYS_ANSI_VAR_FONT | ?wxSYS_SYSTEM_FONT | ?wxSYS_DEVICE_DEFAULT_FONT | ?wxSYS_DEFAULT_PALETTE | ?wxSYS_SYSTEM_FIXED_FONT | ?wxSYS_DEFAULT_GUI_FONT | ?wxSYS_ICONTITLE_FONT
+getFont(Index)
+ when is_integer(Index) ->
+ wxe_util:call(?wxSystemSettings_GetFont,
+ <<Index:32/?UI>>).
+
+%% @spec (Index::WxSystemMetric) -> integer()
+%% @equiv getMetric(Index, [])
+getMetric(Index)
+ when is_integer(Index) ->
+ getMetric(Index, []).
+
+%% @spec (Index::WxSystemMetric, [Option]) -> integer()
+%% Option = {win, wxWindow:wxWindow()}
+%% WxSystemMetric = integer()
+%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsystemsettings.html#wxsystemsettingsgetmetric">external documentation</a>.
+%%<br /> WxSystemMetric is one of ?wxSYS_MOUSE_BUTTONS | ?wxSYS_BORDER_X | ?wxSYS_BORDER_Y | ?wxSYS_CURSOR_X | ?wxSYS_CURSOR_Y | ?wxSYS_DCLICK_X | ?wxSYS_DCLICK_Y | ?wxSYS_DRAG_X | ?wxSYS_DRAG_Y | ?wxSYS_EDGE_X | ?wxSYS_EDGE_Y | ?wxSYS_HSCROLL_ARROW_X | ?wxSYS_HSCROLL_ARROW_Y | ?wxSYS_HTHUMB_X | ?wxSYS_ICON_X | ?wxSYS_ICON_Y | ?wxSYS_ICONSPACING_X | ?wxSYS_ICONSPACING_Y | ?wxSYS_WINDOWMIN_X | ?wxSYS_WINDOWMIN_Y | ?wxSYS_SCREEN_X | ?wxSYS_SCREEN_Y | ?wxSYS_FRAMESIZE_X | ?wxSYS_FRAMESIZE_Y | ?wxSYS_SMALLICON_X | ?wxSYS_SMALLICON_Y | ?wxSYS_HSCROLL_Y | ?wxSYS_VSCROLL_X | ?wxSYS_VSCROLL_ARROW_X | ?wxSYS_VSCROLL_ARROW_Y | ?wxSYS_VTHUMB_Y | ?wxSYS_CAPTION_Y | ?wxSYS_MENU_Y | ?wxSYS_NETWORK_PRESENT | ?wxSYS_PENWINDOWS_PRESENT | ?wxSYS_SHOW_SOUNDS | ?wxSYS_SWAP_BUTTONS
+getMetric(Index, Options)
+ when is_integer(Index),is_list(Options) ->
+ MOpts = fun({win, #wx_ref{type=WinT,ref=WinRef}}, Acc) -> ?CLASS(WinT,wxWindow),[<<1:32/?UI,WinRef:32/?UI>>|Acc];
+ (BadOpt, _) -> erlang:error({badoption, BadOpt}) end,
+ BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)),
+ wxe_util:call(?wxSystemSettings_GetMetric,
+ <<Index:32/?UI, 0:32,BinOpt/binary>>).
+
+%% @spec () -> WxSystemScreenType
+%% WxSystemScreenType = integer()
+%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsystemsettings.html#wxsystemsettingsgetscreentype">external documentation</a>.
+%%<br /> WxSystemScreenType is one of ?wxSYS_SCREEN_NONE | ?wxSYS_SCREEN_TINY | ?wxSYS_SCREEN_PDA | ?wxSYS_SCREEN_SMALL | ?wxSYS_SCREEN_DESKTOP
+getScreenType() ->
+ wxe_util:call(?wxSystemSettings_GetScreenType,
+ <<>>).
+
diff --git a/lib/wx/src/gen/wxTreeCtrl.erl b/lib/wx/src/gen/wxTreeCtrl.erl
index 4fcbb9366e..e3fe4c9612 100644
--- a/lib/wx/src/gen/wxTreeCtrl.erl
+++ b/lib/wx/src/gen/wxTreeCtrl.erl
@@ -35,7 +35,7 @@
-include("wxe.hrl").
-export([addRoot/2,addRoot/3,appendItem/3,appendItem/4,assignImageList/2,assignStateImageList/2,
collapse/2,collapseAndReset/2,create/2,create/3,delete/2,deleteAllItems/1,
- deleteChildren/2,destroy/1,ensureVisible/2,expand/2,getBoundingRect/3,
+ deleteChildren/2,destroy/1,editLabel/2,ensureVisible/2,expand/2,getBoundingRect/3,
getBoundingRect/4,getChildrenCount/2,getChildrenCount/3,getCount/1,
getEditControl/1,getFirstChild/2,getFirstVisibleItem/1,getImageList/1,
getIndent/1,getItemBackgroundColour/2,getItemData/2,getItemFont/2,
@@ -243,6 +243,14 @@ deleteChildren(#wx_ref{type=ThisT,ref=ThisRef},Item)
wxe_util:cast(?wxTreeCtrl_DeleteChildren,
<<ThisRef:32/?UI,0:32,Item:64/?UI>>).
+%% @spec (This::wxTreeCtrl(), Item::integer()) -> wxTextCtrl:wxTextCtrl()
+%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtreectrl.html#wxtreectrleditlabel">external documentation</a>.
+editLabel(#wx_ref{type=ThisT,ref=ThisRef},Item)
+ when is_integer(Item) ->
+ ?CLASS(ThisT,wxTreeCtrl),
+ wxe_util:call(?wxTreeCtrl_EditLabel,
+ <<ThisRef:32/?UI,0:32,Item:64/?UI>>).
+
%% @spec (This::wxTreeCtrl(), Item::integer()) -> ok
%% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtreectrl.html#wxtreectrlensurevisible">external documentation</a>.
ensureVisible(#wx_ref{type=ThisT,ref=ThisRef},Item)
diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl
index a3416b75c8..3edfa73599 100644
--- a/lib/wx/src/gen/wxe_debug.hrl
+++ b/lib/wx/src/gen/wxe_debug.hrl
@@ -1837,1445 +1837,1450 @@ wxdebug_table() ->
{2007, {wxTreeCtrl, delete, 1}},
{2008, {wxTreeCtrl, deleteAllItems, 0}},
{2009, {wxTreeCtrl, deleteChildren, 1}},
- {2010, {wxTreeCtrl, ensureVisible, 1}},
- {2011, {wxTreeCtrl, expand, 1}},
- {2012, {wxTreeCtrl, getBoundingRect, 3}},
- {2014, {wxTreeCtrl, getChildrenCount, 2}},
- {2015, {wxTreeCtrl, getCount, 0}},
- {2016, {wxTreeCtrl, getEditControl, 0}},
- {2017, {wxTreeCtrl, getFirstChild, 2}},
- {2018, {wxTreeCtrl, getNextChild, 2}},
- {2019, {wxTreeCtrl, getFirstVisibleItem, 0}},
- {2020, {wxTreeCtrl, getImageList, 0}},
- {2021, {wxTreeCtrl, getIndent, 0}},
- {2022, {wxTreeCtrl, getItemBackgroundColour, 1}},
- {2023, {wxTreeCtrl, getItemData, 1}},
- {2024, {wxTreeCtrl, getItemFont, 1}},
- {2025, {wxTreeCtrl, getItemImage_1, 1}},
- {2026, {wxTreeCtrl, getItemImage_2, 2}},
- {2027, {wxTreeCtrl, getItemText, 1}},
- {2028, {wxTreeCtrl, getItemTextColour, 1}},
- {2029, {wxTreeCtrl, getLastChild, 1}},
- {2030, {wxTreeCtrl, getNextSibling, 1}},
- {2031, {wxTreeCtrl, getNextVisible, 1}},
- {2032, {wxTreeCtrl, getItemParent, 1}},
- {2033, {wxTreeCtrl, getPrevSibling, 1}},
- {2034, {wxTreeCtrl, getPrevVisible, 1}},
- {2035, {wxTreeCtrl, getRootItem, 0}},
- {2036, {wxTreeCtrl, getSelection, 0}},
- {2037, {wxTreeCtrl, getSelections, 1}},
- {2038, {wxTreeCtrl, getStateImageList, 0}},
- {2039, {wxTreeCtrl, hitTest, 1}},
- {2041, {wxTreeCtrl, insertItem, 4}},
- {2042, {wxTreeCtrl, isBold, 1}},
- {2043, {wxTreeCtrl, isExpanded, 1}},
- {2044, {wxTreeCtrl, isSelected, 1}},
- {2045, {wxTreeCtrl, isVisible, 1}},
- {2046, {wxTreeCtrl, itemHasChildren, 1}},
- {2047, {wxTreeCtrl, prependItem, 3}},
- {2048, {wxTreeCtrl, scrollTo, 1}},
- {2049, {wxTreeCtrl, selectItem_1, 1}},
- {2050, {wxTreeCtrl, selectItem_2, 2}},
- {2051, {wxTreeCtrl, setIndent, 1}},
- {2052, {wxTreeCtrl, setImageList, 1}},
- {2053, {wxTreeCtrl, setItemBackgroundColour, 2}},
- {2054, {wxTreeCtrl, setItemBold, 2}},
- {2055, {wxTreeCtrl, setItemData, 2}},
- {2056, {wxTreeCtrl, setItemDropHighlight, 2}},
- {2057, {wxTreeCtrl, setItemFont, 2}},
- {2058, {wxTreeCtrl, setItemHasChildren, 2}},
- {2059, {wxTreeCtrl, setItemImage_2, 2}},
- {2060, {wxTreeCtrl, setItemImage_3, 3}},
- {2061, {wxTreeCtrl, setItemText, 2}},
- {2062, {wxTreeCtrl, setItemTextColour, 2}},
- {2063, {wxTreeCtrl, setStateImageList, 1}},
- {2064, {wxTreeCtrl, setWindowStyle, 1}},
- {2065, {wxTreeCtrl, sortChildren, 1}},
- {2066, {wxTreeCtrl, toggle, 1}},
- {2067, {wxTreeCtrl, toggleItemSelection, 1}},
- {2068, {wxTreeCtrl, unselect, 0}},
- {2069, {wxTreeCtrl, unselectAll, 0}},
- {2070, {wxTreeCtrl, unselectItem, 1}},
- {2071, {wxScrollBar, new_0, 0}},
- {2072, {wxScrollBar, new_3, 3}},
- {2073, {wxScrollBar, destruct, 0}},
- {2074, {wxScrollBar, create, 3}},
- {2075, {wxScrollBar, getRange, 0}},
- {2076, {wxScrollBar, getPageSize, 0}},
- {2077, {wxScrollBar, getThumbPosition, 0}},
- {2078, {wxScrollBar, getThumbSize, 0}},
- {2079, {wxScrollBar, setThumbPosition, 1}},
- {2080, {wxScrollBar, setScrollbar, 5}},
- {2082, {wxSpinButton, new_2, 2}},
- {2083, {wxSpinButton, new_0, 0}},
- {2084, {wxSpinButton, create, 2}},
- {2085, {wxSpinButton, getMax, 0}},
- {2086, {wxSpinButton, getMin, 0}},
- {2087, {wxSpinButton, getValue, 0}},
- {2088, {wxSpinButton, setRange, 2}},
- {2089, {wxSpinButton, setValue, 1}},
- {2090, {wxSpinButton, 'Destroy', undefined}},
- {2091, {wxSpinCtrl, new_0, 0}},
- {2092, {wxSpinCtrl, new_2, 2}},
- {2094, {wxSpinCtrl, create, 2}},
- {2097, {wxSpinCtrl, setValue_1_1, 1}},
- {2098, {wxSpinCtrl, setValue_1_0, 1}},
- {2100, {wxSpinCtrl, getValue, 0}},
- {2102, {wxSpinCtrl, setRange, 2}},
- {2103, {wxSpinCtrl, setSelection, 2}},
- {2105, {wxSpinCtrl, getMin, 0}},
- {2107, {wxSpinCtrl, getMax, 0}},
- {2108, {wxSpinCtrl, 'Destroy', undefined}},
- {2109, {wxStaticText, new_0, 0}},
- {2110, {wxStaticText, new_4, 4}},
- {2111, {wxStaticText, create, 4}},
- {2112, {wxStaticText, getLabel, 0}},
- {2113, {wxStaticText, setLabel, 1}},
- {2114, {wxStaticText, wrap, 1}},
- {2115, {wxStaticText, 'Destroy', undefined}},
- {2116, {wxStaticBitmap, new_0, 0}},
- {2117, {wxStaticBitmap, new_4, 4}},
- {2118, {wxStaticBitmap, create, 4}},
- {2119, {wxStaticBitmap, getBitmap, 0}},
- {2120, {wxStaticBitmap, setBitmap, 1}},
- {2121, {wxStaticBitmap, 'Destroy', undefined}},
- {2122, {wxRadioBox, new, 7}},
- {2124, {wxRadioBox, destruct, 0}},
- {2125, {wxRadioBox, create, 7}},
- {2126, {wxRadioBox, enable_2, 2}},
- {2127, {wxRadioBox, enable_1, 1}},
- {2128, {wxRadioBox, getSelection, 0}},
- {2129, {wxRadioBox, getString, 1}},
- {2130, {wxRadioBox, setSelection, 1}},
- {2131, {wxRadioBox, show_2, 2}},
- {2132, {wxRadioBox, show_1, 1}},
- {2133, {wxRadioBox, getColumnCount, 0}},
- {2134, {wxRadioBox, getItemHelpText, 1}},
- {2135, {wxRadioBox, getItemToolTip, 1}},
- {2137, {wxRadioBox, getItemFromPoint, 1}},
- {2138, {wxRadioBox, getRowCount, 0}},
- {2139, {wxRadioBox, isItemEnabled, 1}},
- {2140, {wxRadioBox, isItemShown, 1}},
- {2141, {wxRadioBox, setItemHelpText, 2}},
- {2142, {wxRadioBox, setItemToolTip, 2}},
- {2143, {wxRadioButton, new_0, 0}},
- {2144, {wxRadioButton, new_4, 4}},
- {2145, {wxRadioButton, create, 4}},
- {2146, {wxRadioButton, getValue, 0}},
- {2147, {wxRadioButton, setValue, 1}},
- {2148, {wxRadioButton, 'Destroy', undefined}},
- {2150, {wxSlider, new_6, 6}},
- {2151, {wxSlider, new_0, 0}},
- {2152, {wxSlider, create, 6}},
- {2153, {wxSlider, getLineSize, 0}},
- {2154, {wxSlider, getMax, 0}},
- {2155, {wxSlider, getMin, 0}},
- {2156, {wxSlider, getPageSize, 0}},
- {2157, {wxSlider, getThumbLength, 0}},
- {2158, {wxSlider, getValue, 0}},
- {2159, {wxSlider, setLineSize, 1}},
- {2160, {wxSlider, setPageSize, 1}},
- {2161, {wxSlider, setRange, 2}},
- {2162, {wxSlider, setThumbLength, 1}},
- {2163, {wxSlider, setValue, 1}},
- {2164, {wxSlider, 'Destroy', undefined}},
- {2166, {wxDialog, new_4, 4}},
- {2167, {wxDialog, new_0, 0}},
- {2169, {wxDialog, destruct, 0}},
- {2170, {wxDialog, create, 4}},
- {2171, {wxDialog, createButtonSizer, 1}},
- {2172, {wxDialog, createStdDialogButtonSizer, 1}},
- {2173, {wxDialog, endModal, 1}},
- {2174, {wxDialog, getAffirmativeId, 0}},
- {2175, {wxDialog, getReturnCode, 0}},
- {2176, {wxDialog, isModal, 0}},
- {2177, {wxDialog, setAffirmativeId, 1}},
- {2178, {wxDialog, setReturnCode, 1}},
- {2179, {wxDialog, show, 1}},
- {2180, {wxDialog, showModal, 0}},
- {2181, {wxColourDialog, new_0, 0}},
- {2182, {wxColourDialog, new_2, 2}},
- {2183, {wxColourDialog, destruct, 0}},
- {2184, {wxColourDialog, create, 2}},
- {2185, {wxColourDialog, getColourData, 0}},
- {2186, {wxColourData, new_0, 0}},
- {2187, {wxColourData, new_1, 1}},
- {2188, {wxColourData, destruct, 0}},
- {2189, {wxColourData, getChooseFull, 0}},
- {2190, {wxColourData, getColour, 0}},
- {2192, {wxColourData, getCustomColour, 1}},
- {2193, {wxColourData, setChooseFull, 1}},
- {2194, {wxColourData, setColour, 1}},
- {2195, {wxColourData, setCustomColour, 2}},
- {2196, {wxPalette, new_0, 0}},
- {2197, {wxPalette, new_4, 4}},
- {2199, {wxPalette, destruct, 0}},
- {2200, {wxPalette, create, 4}},
- {2201, {wxPalette, getColoursCount, 0}},
- {2202, {wxPalette, getPixel, 3}},
- {2203, {wxPalette, getRGB, 4}},
- {2204, {wxPalette, isOk, 0}},
- {2208, {wxDirDialog, new, 2}},
- {2209, {wxDirDialog, destruct, 0}},
- {2210, {wxDirDialog, getPath, 0}},
- {2211, {wxDirDialog, getMessage, 0}},
- {2212, {wxDirDialog, setMessage, 1}},
- {2213, {wxDirDialog, setPath, 1}},
- {2217, {wxFileDialog, new, 2}},
- {2218, {wxFileDialog, destruct, 0}},
- {2219, {wxFileDialog, getDirectory, 0}},
- {2220, {wxFileDialog, getFilename, 0}},
- {2221, {wxFileDialog, getFilenames, 1}},
- {2222, {wxFileDialog, getFilterIndex, 0}},
- {2223, {wxFileDialog, getMessage, 0}},
- {2224, {wxFileDialog, getPath, 0}},
- {2225, {wxFileDialog, getPaths, 1}},
- {2226, {wxFileDialog, getWildcard, 0}},
- {2227, {wxFileDialog, setDirectory, 1}},
- {2228, {wxFileDialog, setFilename, 1}},
- {2229, {wxFileDialog, setFilterIndex, 1}},
- {2230, {wxFileDialog, setMessage, 1}},
- {2231, {wxFileDialog, setPath, 1}},
- {2232, {wxFileDialog, setWildcard, 1}},
- {2233, {wxPickerBase, setInternalMargin, 1}},
- {2234, {wxPickerBase, getInternalMargin, 0}},
- {2235, {wxPickerBase, setTextCtrlProportion, 1}},
- {2236, {wxPickerBase, setPickerCtrlProportion, 1}},
- {2237, {wxPickerBase, getTextCtrlProportion, 0}},
- {2238, {wxPickerBase, getPickerCtrlProportion, 0}},
- {2239, {wxPickerBase, hasTextCtrl, 0}},
- {2240, {wxPickerBase, getTextCtrl, 0}},
- {2241, {wxPickerBase, isTextCtrlGrowable, 0}},
- {2242, {wxPickerBase, setPickerCtrlGrowable, 1}},
- {2243, {wxPickerBase, setTextCtrlGrowable, 1}},
- {2244, {wxPickerBase, isPickerCtrlGrowable, 0}},
- {2245, {wxFilePickerCtrl, new_0, 0}},
- {2246, {wxFilePickerCtrl, new_3, 3}},
- {2247, {wxFilePickerCtrl, create, 3}},
- {2248, {wxFilePickerCtrl, getPath, 0}},
- {2249, {wxFilePickerCtrl, setPath, 1}},
- {2250, {wxFilePickerCtrl, 'Destroy', undefined}},
- {2251, {wxDirPickerCtrl, new_0, 0}},
- {2252, {wxDirPickerCtrl, new_3, 3}},
- {2253, {wxDirPickerCtrl, create, 3}},
- {2254, {wxDirPickerCtrl, getPath, 0}},
- {2255, {wxDirPickerCtrl, setPath, 1}},
- {2256, {wxDirPickerCtrl, 'Destroy', undefined}},
- {2257, {wxColourPickerCtrl, new_0, 0}},
- {2258, {wxColourPickerCtrl, new_3, 3}},
- {2259, {wxColourPickerCtrl, create, 3}},
- {2260, {wxColourPickerCtrl, getColour, 0}},
- {2261, {wxColourPickerCtrl, setColour_1_1, 1}},
- {2262, {wxColourPickerCtrl, setColour_1_0, 1}},
- {2263, {wxColourPickerCtrl, 'Destroy', undefined}},
- {2264, {wxDatePickerCtrl, new_0, 0}},
- {2265, {wxDatePickerCtrl, new_3, 3}},
- {2266, {wxDatePickerCtrl, getRange, 2}},
- {2267, {wxDatePickerCtrl, getValue, 0}},
- {2268, {wxDatePickerCtrl, setRange, 2}},
- {2269, {wxDatePickerCtrl, setValue, 1}},
- {2270, {wxDatePickerCtrl, 'Destroy', undefined}},
- {2271, {wxFontPickerCtrl, new_0, 0}},
- {2272, {wxFontPickerCtrl, new_3, 3}},
- {2273, {wxFontPickerCtrl, create, 3}},
- {2274, {wxFontPickerCtrl, getSelectedFont, 0}},
- {2275, {wxFontPickerCtrl, setSelectedFont, 1}},
- {2276, {wxFontPickerCtrl, getMaxPointSize, 0}},
- {2277, {wxFontPickerCtrl, setMaxPointSize, 1}},
- {2278, {wxFontPickerCtrl, 'Destroy', undefined}},
- {2281, {wxFindReplaceDialog, new_0, 0}},
- {2282, {wxFindReplaceDialog, new_4, 4}},
- {2283, {wxFindReplaceDialog, destruct, 0}},
- {2284, {wxFindReplaceDialog, create, 4}},
- {2285, {wxFindReplaceDialog, getData, 0}},
- {2286, {wxFindReplaceData, new_0, 0}},
- {2287, {wxFindReplaceData, new_1, 1}},
- {2288, {wxFindReplaceData, getFindString, 0}},
- {2289, {wxFindReplaceData, getReplaceString, 0}},
- {2290, {wxFindReplaceData, getFlags, 0}},
- {2291, {wxFindReplaceData, setFlags, 1}},
- {2292, {wxFindReplaceData, setFindString, 1}},
- {2293, {wxFindReplaceData, setReplaceString, 1}},
- {2294, {wxFindReplaceData, 'Destroy', undefined}},
- {2295, {wxMultiChoiceDialog, new_0, 0}},
- {2297, {wxMultiChoiceDialog, new_5, 5}},
- {2298, {wxMultiChoiceDialog, getSelections, 0}},
- {2299, {wxMultiChoiceDialog, setSelections, 1}},
- {2300, {wxMultiChoiceDialog, 'Destroy', undefined}},
- {2301, {wxSingleChoiceDialog, new_0, 0}},
- {2303, {wxSingleChoiceDialog, new_5, 5}},
- {2304, {wxSingleChoiceDialog, getSelection, 0}},
- {2305, {wxSingleChoiceDialog, getStringSelection, 0}},
- {2306, {wxSingleChoiceDialog, setSelection, 1}},
- {2307, {wxSingleChoiceDialog, 'Destroy', undefined}},
- {2308, {wxTextEntryDialog, new, 3}},
- {2309, {wxTextEntryDialog, getValue, 0}},
- {2310, {wxTextEntryDialog, setValue, 1}},
- {2311, {wxTextEntryDialog, 'Destroy', undefined}},
- {2312, {wxPasswordEntryDialog, new, 3}},
- {2313, {wxPasswordEntryDialog, 'Destroy', undefined}},
- {2314, {wxFontData, new_0, 0}},
- {2315, {wxFontData, new_1, 1}},
- {2316, {wxFontData, destruct, 0}},
- {2317, {wxFontData, enableEffects, 1}},
- {2318, {wxFontData, getAllowSymbols, 0}},
- {2319, {wxFontData, getColour, 0}},
- {2320, {wxFontData, getChosenFont, 0}},
- {2321, {wxFontData, getEnableEffects, 0}},
- {2322, {wxFontData, getInitialFont, 0}},
- {2323, {wxFontData, getShowHelp, 0}},
- {2324, {wxFontData, setAllowSymbols, 1}},
- {2325, {wxFontData, setChosenFont, 1}},
- {2326, {wxFontData, setColour, 1}},
- {2327, {wxFontData, setInitialFont, 1}},
- {2328, {wxFontData, setRange, 2}},
- {2329, {wxFontData, setShowHelp, 1}},
- {2333, {wxFontDialog, new_0, 0}},
- {2335, {wxFontDialog, new_2, 2}},
- {2337, {wxFontDialog, create, 2}},
- {2338, {wxFontDialog, getFontData, 0}},
- {2340, {wxFontDialog, 'Destroy', undefined}},
- {2341, {wxProgressDialog, new, 3}},
- {2342, {wxProgressDialog, destruct, 0}},
- {2343, {wxProgressDialog, resume, 0}},
- {2344, {wxProgressDialog, update_2, 2}},
- {2345, {wxProgressDialog, update_0, 0}},
- {2346, {wxMessageDialog, new, 3}},
- {2347, {wxMessageDialog, destruct, 0}},
- {2348, {wxPageSetupDialog, new, 2}},
- {2349, {wxPageSetupDialog, destruct, 0}},
- {2350, {wxPageSetupDialog, getPageSetupData, 0}},
- {2351, {wxPageSetupDialog, showModal, 0}},
- {2352, {wxPageSetupDialogData, new_0, 0}},
- {2353, {wxPageSetupDialogData, new_1_0, 1}},
- {2354, {wxPageSetupDialogData, new_1_1, 1}},
- {2355, {wxPageSetupDialogData, destruct, 0}},
- {2356, {wxPageSetupDialogData, enableHelp, 1}},
- {2357, {wxPageSetupDialogData, enableMargins, 1}},
- {2358, {wxPageSetupDialogData, enableOrientation, 1}},
- {2359, {wxPageSetupDialogData, enablePaper, 1}},
- {2360, {wxPageSetupDialogData, enablePrinter, 1}},
- {2361, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
- {2362, {wxPageSetupDialogData, getEnableMargins, 0}},
- {2363, {wxPageSetupDialogData, getEnableOrientation, 0}},
- {2364, {wxPageSetupDialogData, getEnablePaper, 0}},
- {2365, {wxPageSetupDialogData, getEnablePrinter, 0}},
- {2366, {wxPageSetupDialogData, getEnableHelp, 0}},
- {2367, {wxPageSetupDialogData, getDefaultInfo, 0}},
- {2368, {wxPageSetupDialogData, getMarginTopLeft, 0}},
- {2369, {wxPageSetupDialogData, getMarginBottomRight, 0}},
- {2370, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
- {2371, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
- {2372, {wxPageSetupDialogData, getPaperId, 0}},
- {2373, {wxPageSetupDialogData, getPaperSize, 0}},
- {2375, {wxPageSetupDialogData, getPrintData, 0}},
- {2376, {wxPageSetupDialogData, isOk, 0}},
- {2377, {wxPageSetupDialogData, setDefaultInfo, 1}},
- {2378, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
- {2379, {wxPageSetupDialogData, setMarginTopLeft, 1}},
- {2380, {wxPageSetupDialogData, setMarginBottomRight, 1}},
- {2381, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
- {2382, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
- {2383, {wxPageSetupDialogData, setPaperId, 1}},
- {2384, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
- {2385, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
- {2386, {wxPageSetupDialogData, setPrintData, 1}},
- {2387, {wxPrintDialog, new_2_0, 2}},
- {2388, {wxPrintDialog, new_2_1, 2}},
- {2389, {wxPrintDialog, destruct, 0}},
- {2390, {wxPrintDialog, getPrintDialogData, 0}},
- {2391, {wxPrintDialog, getPrintDC, 0}},
- {2392, {wxPrintDialogData, new_0, 0}},
- {2393, {wxPrintDialogData, new_1_1, 1}},
- {2394, {wxPrintDialogData, new_1_0, 1}},
- {2395, {wxPrintDialogData, destruct, 0}},
- {2396, {wxPrintDialogData, enableHelp, 1}},
- {2397, {wxPrintDialogData, enablePageNumbers, 1}},
- {2398, {wxPrintDialogData, enablePrintToFile, 1}},
- {2399, {wxPrintDialogData, enableSelection, 1}},
- {2400, {wxPrintDialogData, getAllPages, 0}},
- {2401, {wxPrintDialogData, getCollate, 0}},
- {2402, {wxPrintDialogData, getFromPage, 0}},
- {2403, {wxPrintDialogData, getMaxPage, 0}},
- {2404, {wxPrintDialogData, getMinPage, 0}},
- {2405, {wxPrintDialogData, getNoCopies, 0}},
- {2406, {wxPrintDialogData, getPrintData, 0}},
- {2407, {wxPrintDialogData, getPrintToFile, 0}},
- {2408, {wxPrintDialogData, getSelection, 0}},
- {2409, {wxPrintDialogData, getToPage, 0}},
- {2410, {wxPrintDialogData, isOk, 0}},
- {2411, {wxPrintDialogData, setCollate, 1}},
- {2412, {wxPrintDialogData, setFromPage, 1}},
- {2413, {wxPrintDialogData, setMaxPage, 1}},
- {2414, {wxPrintDialogData, setMinPage, 1}},
- {2415, {wxPrintDialogData, setNoCopies, 1}},
- {2416, {wxPrintDialogData, setPrintData, 1}},
- {2417, {wxPrintDialogData, setPrintToFile, 1}},
- {2418, {wxPrintDialogData, setSelection, 1}},
- {2419, {wxPrintDialogData, setToPage, 1}},
- {2420, {wxPrintData, new_0, 0}},
- {2421, {wxPrintData, new_1, 1}},
- {2422, {wxPrintData, destruct, 0}},
- {2423, {wxPrintData, getCollate, 0}},
- {2424, {wxPrintData, getBin, 0}},
- {2425, {wxPrintData, getColour, 0}},
- {2426, {wxPrintData, getDuplex, 0}},
- {2427, {wxPrintData, getNoCopies, 0}},
- {2428, {wxPrintData, getOrientation, 0}},
- {2429, {wxPrintData, getPaperId, 0}},
- {2430, {wxPrintData, getPrinterName, 0}},
- {2431, {wxPrintData, getQuality, 0}},
- {2432, {wxPrintData, isOk, 0}},
- {2433, {wxPrintData, setBin, 1}},
- {2434, {wxPrintData, setCollate, 1}},
- {2435, {wxPrintData, setColour, 1}},
- {2436, {wxPrintData, setDuplex, 1}},
- {2437, {wxPrintData, setNoCopies, 1}},
- {2438, {wxPrintData, setOrientation, 1}},
- {2439, {wxPrintData, setPaperId, 1}},
- {2440, {wxPrintData, setPrinterName, 1}},
- {2441, {wxPrintData, setQuality, 1}},
- {2444, {wxPrintPreview, new_2, 2}},
- {2445, {wxPrintPreview, new_3, 3}},
- {2447, {wxPrintPreview, destruct, 0}},
- {2448, {wxPrintPreview, getCanvas, 0}},
- {2449, {wxPrintPreview, getCurrentPage, 0}},
- {2450, {wxPrintPreview, getFrame, 0}},
- {2451, {wxPrintPreview, getMaxPage, 0}},
- {2452, {wxPrintPreview, getMinPage, 0}},
- {2453, {wxPrintPreview, getPrintout, 0}},
- {2454, {wxPrintPreview, getPrintoutForPrinting, 0}},
- {2455, {wxPrintPreview, isOk, 0}},
- {2456, {wxPrintPreview, paintPage, 2}},
- {2457, {wxPrintPreview, print, 1}},
- {2458, {wxPrintPreview, renderPage, 1}},
- {2459, {wxPrintPreview, setCanvas, 1}},
- {2460, {wxPrintPreview, setCurrentPage, 1}},
- {2461, {wxPrintPreview, setFrame, 1}},
- {2462, {wxPrintPreview, setPrintout, 1}},
- {2463, {wxPrintPreview, setZoom, 1}},
- {2464, {wxPreviewFrame, new, 3}},
- {2465, {wxPreviewFrame, destruct, 0}},
- {2466, {wxPreviewFrame, createControlBar, 0}},
- {2467, {wxPreviewFrame, createCanvas, 0}},
- {2468, {wxPreviewFrame, initialize, 0}},
- {2469, {wxPreviewFrame, onCloseWindow, 1}},
- {2470, {wxPreviewControlBar, new, 4}},
- {2471, {wxPreviewControlBar, destruct, 0}},
- {2472, {wxPreviewControlBar, createButtons, 0}},
- {2473, {wxPreviewControlBar, getPrintPreview, 0}},
- {2474, {wxPreviewControlBar, getZoomControl, 0}},
- {2475, {wxPreviewControlBar, setZoomControl, 1}},
- {2477, {wxPrinter, new, 1}},
- {2478, {wxPrinter, createAbortWindow, 2}},
- {2479, {wxPrinter, getAbort, 0}},
- {2480, {wxPrinter, getLastError, 0}},
- {2481, {wxPrinter, getPrintDialogData, 0}},
- {2482, {wxPrinter, print, 3}},
- {2483, {wxPrinter, printDialog, 1}},
- {2484, {wxPrinter, reportError, 3}},
- {2485, {wxPrinter, setup, 1}},
- {2486, {wxPrinter, 'Destroy', undefined}},
- {2487, {wxXmlResource, new_1, 1}},
- {2488, {wxXmlResource, new_2, 2}},
- {2489, {wxXmlResource, destruct, 0}},
- {2490, {wxXmlResource, attachUnknownControl, 3}},
- {2491, {wxXmlResource, clearHandlers, 0}},
- {2492, {wxXmlResource, compareVersion, 4}},
- {2493, {wxXmlResource, get, 0}},
- {2494, {wxXmlResource, getFlags, 0}},
- {2495, {wxXmlResource, getVersion, 0}},
- {2496, {wxXmlResource, getXRCID, 2}},
- {2497, {wxXmlResource, initAllHandlers, 0}},
- {2498, {wxXmlResource, load, 1}},
- {2499, {wxXmlResource, loadBitmap, 1}},
- {2500, {wxXmlResource, loadDialog_2, 2}},
- {2501, {wxXmlResource, loadDialog_3, 3}},
- {2502, {wxXmlResource, loadFrame_2, 2}},
- {2503, {wxXmlResource, loadFrame_3, 3}},
- {2504, {wxXmlResource, loadIcon, 1}},
- {2505, {wxXmlResource, loadMenu, 1}},
- {2506, {wxXmlResource, loadMenuBar_2, 2}},
- {2507, {wxXmlResource, loadMenuBar_1, 1}},
- {2508, {wxXmlResource, loadPanel_2, 2}},
- {2509, {wxXmlResource, loadPanel_3, 3}},
- {2510, {wxXmlResource, loadToolBar, 2}},
- {2511, {wxXmlResource, set, 1}},
- {2512, {wxXmlResource, setFlags, 1}},
- {2513, {wxXmlResource, unload, 1}},
- {2514, {wxXmlResource, xrcctrl, 3}},
- {2515, {wxHtmlEasyPrinting, new, 1}},
- {2516, {wxHtmlEasyPrinting, destruct, 0}},
- {2517, {wxHtmlEasyPrinting, getPrintData, 0}},
- {2518, {wxHtmlEasyPrinting, getPageSetupData, 0}},
- {2519, {wxHtmlEasyPrinting, previewFile, 1}},
- {2520, {wxHtmlEasyPrinting, previewText, 2}},
- {2521, {wxHtmlEasyPrinting, printFile, 1}},
- {2522, {wxHtmlEasyPrinting, printText, 2}},
- {2523, {wxHtmlEasyPrinting, pageSetup, 0}},
- {2524, {wxHtmlEasyPrinting, setFonts, 3}},
- {2525, {wxHtmlEasyPrinting, setHeader, 2}},
- {2526, {wxHtmlEasyPrinting, setFooter, 2}},
- {2528, {wxGLCanvas, new_2, 2}},
- {2529, {wxGLCanvas, new_3_1, 3}},
- {2530, {wxGLCanvas, new_3_0, 3}},
- {2531, {wxGLCanvas, getContext, 0}},
- {2533, {wxGLCanvas, setCurrent, 0}},
- {2534, {wxGLCanvas, swapBuffers, 0}},
- {2535, {wxGLCanvas, 'Destroy', undefined}},
- {2536, {wxAuiManager, new, 1}},
- {2537, {wxAuiManager, destruct, 0}},
- {2538, {wxAuiManager, addPane_2_1, 2}},
- {2539, {wxAuiManager, addPane_3, 3}},
- {2540, {wxAuiManager, addPane_2_0, 2}},
- {2541, {wxAuiManager, detachPane, 1}},
- {2542, {wxAuiManager, getAllPanes, 0}},
- {2543, {wxAuiManager, getArtProvider, 0}},
- {2544, {wxAuiManager, getDockSizeConstraint, 2}},
- {2545, {wxAuiManager, getFlags, 0}},
- {2546, {wxAuiManager, getManagedWindow, 0}},
- {2547, {wxAuiManager, getManager, 1}},
- {2548, {wxAuiManager, getPane_1_1, 1}},
- {2549, {wxAuiManager, getPane_1_0, 1}},
- {2550, {wxAuiManager, hideHint, 0}},
- {2551, {wxAuiManager, insertPane, 3}},
- {2552, {wxAuiManager, loadPaneInfo, 2}},
- {2553, {wxAuiManager, loadPerspective, 2}},
- {2554, {wxAuiManager, savePaneInfo, 1}},
- {2555, {wxAuiManager, savePerspective, 0}},
- {2556, {wxAuiManager, setArtProvider, 1}},
- {2557, {wxAuiManager, setDockSizeConstraint, 2}},
- {2558, {wxAuiManager, setFlags, 1}},
- {2559, {wxAuiManager, setManagedWindow, 1}},
- {2560, {wxAuiManager, showHint, 1}},
- {2561, {wxAuiManager, unInit, 0}},
- {2562, {wxAuiManager, update, 0}},
- {2563, {wxAuiPaneInfo, new_0, 0}},
- {2564, {wxAuiPaneInfo, new_1, 1}},
- {2565, {wxAuiPaneInfo, destruct, 0}},
- {2566, {wxAuiPaneInfo, bestSize_1, 1}},
- {2567, {wxAuiPaneInfo, bestSize_2, 2}},
- {2568, {wxAuiPaneInfo, bottom, 0}},
- {2569, {wxAuiPaneInfo, bottomDockable, 1}},
- {2570, {wxAuiPaneInfo, caption, 1}},
- {2571, {wxAuiPaneInfo, captionVisible, 1}},
- {2572, {wxAuiPaneInfo, centre, 0}},
- {2573, {wxAuiPaneInfo, centrePane, 0}},
- {2574, {wxAuiPaneInfo, closeButton, 1}},
- {2575, {wxAuiPaneInfo, defaultPane, 0}},
- {2576, {wxAuiPaneInfo, destroyOnClose, 1}},
- {2577, {wxAuiPaneInfo, direction, 1}},
- {2578, {wxAuiPaneInfo, dock, 0}},
- {2579, {wxAuiPaneInfo, dockable, 1}},
- {2580, {wxAuiPaneInfo, fixed, 0}},
- {2581, {wxAuiPaneInfo, float, 0}},
- {2582, {wxAuiPaneInfo, floatable, 1}},
- {2583, {wxAuiPaneInfo, floatingPosition_1, 1}},
- {2584, {wxAuiPaneInfo, floatingPosition_2, 2}},
- {2585, {wxAuiPaneInfo, floatingSize_1, 1}},
- {2586, {wxAuiPaneInfo, floatingSize_2, 2}},
- {2587, {wxAuiPaneInfo, gripper, 1}},
- {2588, {wxAuiPaneInfo, gripperTop, 1}},
- {2589, {wxAuiPaneInfo, hasBorder, 0}},
- {2590, {wxAuiPaneInfo, hasCaption, 0}},
- {2591, {wxAuiPaneInfo, hasCloseButton, 0}},
- {2592, {wxAuiPaneInfo, hasFlag, 1}},
- {2593, {wxAuiPaneInfo, hasGripper, 0}},
- {2594, {wxAuiPaneInfo, hasGripperTop, 0}},
- {2595, {wxAuiPaneInfo, hasMaximizeButton, 0}},
- {2596, {wxAuiPaneInfo, hasMinimizeButton, 0}},
- {2597, {wxAuiPaneInfo, hasPinButton, 0}},
- {2598, {wxAuiPaneInfo, hide, 0}},
- {2599, {wxAuiPaneInfo, isBottomDockable, 0}},
- {2600, {wxAuiPaneInfo, isDocked, 0}},
- {2601, {wxAuiPaneInfo, isFixed, 0}},
- {2602, {wxAuiPaneInfo, isFloatable, 0}},
- {2603, {wxAuiPaneInfo, isFloating, 0}},
- {2604, {wxAuiPaneInfo, isLeftDockable, 0}},
- {2605, {wxAuiPaneInfo, isMovable, 0}},
- {2606, {wxAuiPaneInfo, isOk, 0}},
- {2607, {wxAuiPaneInfo, isResizable, 0}},
- {2608, {wxAuiPaneInfo, isRightDockable, 0}},
- {2609, {wxAuiPaneInfo, isShown, 0}},
- {2610, {wxAuiPaneInfo, isToolbar, 0}},
- {2611, {wxAuiPaneInfo, isTopDockable, 0}},
- {2612, {wxAuiPaneInfo, layer, 1}},
- {2613, {wxAuiPaneInfo, left, 0}},
- {2614, {wxAuiPaneInfo, leftDockable, 1}},
- {2615, {wxAuiPaneInfo, maxSize_1, 1}},
- {2616, {wxAuiPaneInfo, maxSize_2, 2}},
- {2617, {wxAuiPaneInfo, maximizeButton, 1}},
- {2618, {wxAuiPaneInfo, minSize_1, 1}},
- {2619, {wxAuiPaneInfo, minSize_2, 2}},
- {2620, {wxAuiPaneInfo, minimizeButton, 1}},
- {2621, {wxAuiPaneInfo, movable, 1}},
- {2622, {wxAuiPaneInfo, name, 1}},
- {2623, {wxAuiPaneInfo, paneBorder, 1}},
- {2624, {wxAuiPaneInfo, pinButton, 1}},
- {2625, {wxAuiPaneInfo, position, 1}},
- {2626, {wxAuiPaneInfo, resizable, 1}},
- {2627, {wxAuiPaneInfo, right, 0}},
- {2628, {wxAuiPaneInfo, rightDockable, 1}},
- {2629, {wxAuiPaneInfo, row, 1}},
- {2630, {wxAuiPaneInfo, safeSet, 1}},
- {2631, {wxAuiPaneInfo, setFlag, 2}},
- {2632, {wxAuiPaneInfo, show, 1}},
- {2633, {wxAuiPaneInfo, toolbarPane, 0}},
- {2634, {wxAuiPaneInfo, top, 0}},
- {2635, {wxAuiPaneInfo, topDockable, 1}},
- {2636, {wxAuiPaneInfo, window, 1}},
- {2637, {wxAuiNotebook, new_0, 0}},
- {2638, {wxAuiNotebook, new_2, 2}},
- {2639, {wxAuiNotebook, addPage, 3}},
- {2640, {wxAuiNotebook, create, 2}},
- {2641, {wxAuiNotebook, deletePage, 1}},
- {2642, {wxAuiNotebook, getArtProvider, 0}},
- {2643, {wxAuiNotebook, getPage, 1}},
- {2644, {wxAuiNotebook, getPageBitmap, 1}},
- {2645, {wxAuiNotebook, getPageCount, 0}},
- {2646, {wxAuiNotebook, getPageIndex, 1}},
- {2647, {wxAuiNotebook, getPageText, 1}},
- {2648, {wxAuiNotebook, getSelection, 0}},
- {2649, {wxAuiNotebook, insertPage, 4}},
- {2650, {wxAuiNotebook, removePage, 1}},
- {2651, {wxAuiNotebook, setArtProvider, 1}},
- {2652, {wxAuiNotebook, setFont, 1}},
- {2653, {wxAuiNotebook, setPageBitmap, 2}},
- {2654, {wxAuiNotebook, setPageText, 2}},
- {2655, {wxAuiNotebook, setSelection, 1}},
- {2656, {wxAuiNotebook, setTabCtrlHeight, 1}},
- {2657, {wxAuiNotebook, setUniformBitmapSize, 1}},
- {2658, {wxAuiNotebook, 'Destroy', undefined}},
- {2659, {wxMDIParentFrame, new_0, 0}},
- {2660, {wxMDIParentFrame, new_4, 4}},
- {2661, {wxMDIParentFrame, destruct, 0}},
- {2662, {wxMDIParentFrame, activateNext, 0}},
- {2663, {wxMDIParentFrame, activatePrevious, 0}},
- {2664, {wxMDIParentFrame, arrangeIcons, 0}},
- {2665, {wxMDIParentFrame, cascade, 0}},
- {2666, {wxMDIParentFrame, create, 4}},
- {2667, {wxMDIParentFrame, getActiveChild, 0}},
- {2668, {wxMDIParentFrame, getClientWindow, 0}},
- {2669, {wxMDIParentFrame, tile, 1}},
- {2670, {wxMDIChildFrame, new_0, 0}},
- {2671, {wxMDIChildFrame, new_4, 4}},
- {2672, {wxMDIChildFrame, destruct, 0}},
- {2673, {wxMDIChildFrame, activate, 0}},
- {2674, {wxMDIChildFrame, create, 4}},
- {2675, {wxMDIChildFrame, maximize, 1}},
- {2676, {wxMDIChildFrame, restore, 0}},
- {2677, {wxMDIClientWindow, new_0, 0}},
- {2678, {wxMDIClientWindow, new_2, 2}},
- {2679, {wxMDIClientWindow, destruct, 0}},
- {2680, {wxMDIClientWindow, createClient, 2}},
- {2681, {wxLayoutAlgorithm, new, 0}},
- {2682, {wxLayoutAlgorithm, layoutFrame, 2}},
- {2683, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
- {2684, {wxLayoutAlgorithm, layoutWindow, 2}},
- {2685, {wxLayoutAlgorithm, 'Destroy', undefined}},
- {2686, {wxEvent, getId, 0}},
- {2687, {wxEvent, getSkipped, 0}},
- {2688, {wxEvent, getTimestamp, 0}},
- {2689, {wxEvent, isCommandEvent, 0}},
- {2690, {wxEvent, resumePropagation, 1}},
- {2691, {wxEvent, shouldPropagate, 0}},
- {2692, {wxEvent, skip, 1}},
- {2693, {wxEvent, stopPropagation, 0}},
- {2694, {wxCommandEvent, getClientData, 0}},
- {2695, {wxCommandEvent, getExtraLong, 0}},
- {2696, {wxCommandEvent, getInt, 0}},
- {2697, {wxCommandEvent, getSelection, 0}},
- {2698, {wxCommandEvent, getString, 0}},
- {2699, {wxCommandEvent, isChecked, 0}},
- {2700, {wxCommandEvent, isSelection, 0}},
- {2701, {wxCommandEvent, setInt, 1}},
- {2702, {wxCommandEvent, setString, 1}},
- {2703, {wxScrollEvent, getOrientation, 0}},
- {2704, {wxScrollEvent, getPosition, 0}},
- {2705, {wxScrollWinEvent, getOrientation, 0}},
- {2706, {wxScrollWinEvent, getPosition, 0}},
- {2707, {wxMouseEvent, altDown, 0}},
- {2708, {wxMouseEvent, button, 1}},
- {2709, {wxMouseEvent, buttonDClick, 1}},
- {2710, {wxMouseEvent, buttonDown, 1}},
- {2711, {wxMouseEvent, buttonUp, 1}},
- {2712, {wxMouseEvent, cmdDown, 0}},
- {2713, {wxMouseEvent, controlDown, 0}},
- {2714, {wxMouseEvent, dragging, 0}},
- {2715, {wxMouseEvent, entering, 0}},
- {2716, {wxMouseEvent, getButton, 0}},
- {2719, {wxMouseEvent, getPosition, 0}},
- {2720, {wxMouseEvent, getLogicalPosition, 1}},
- {2721, {wxMouseEvent, getLinesPerAction, 0}},
- {2722, {wxMouseEvent, getWheelRotation, 0}},
- {2723, {wxMouseEvent, getWheelDelta, 0}},
- {2724, {wxMouseEvent, getX, 0}},
- {2725, {wxMouseEvent, getY, 0}},
- {2726, {wxMouseEvent, isButton, 0}},
- {2727, {wxMouseEvent, isPageScroll, 0}},
- {2728, {wxMouseEvent, leaving, 0}},
- {2729, {wxMouseEvent, leftDClick, 0}},
- {2730, {wxMouseEvent, leftDown, 0}},
- {2731, {wxMouseEvent, leftIsDown, 0}},
- {2732, {wxMouseEvent, leftUp, 0}},
- {2733, {wxMouseEvent, metaDown, 0}},
- {2734, {wxMouseEvent, middleDClick, 0}},
- {2735, {wxMouseEvent, middleDown, 0}},
- {2736, {wxMouseEvent, middleIsDown, 0}},
- {2737, {wxMouseEvent, middleUp, 0}},
- {2738, {wxMouseEvent, moving, 0}},
- {2739, {wxMouseEvent, rightDClick, 0}},
- {2740, {wxMouseEvent, rightDown, 0}},
- {2741, {wxMouseEvent, rightIsDown, 0}},
- {2742, {wxMouseEvent, rightUp, 0}},
- {2743, {wxMouseEvent, shiftDown, 0}},
- {2744, {wxSetCursorEvent, getCursor, 0}},
- {2745, {wxSetCursorEvent, getX, 0}},
- {2746, {wxSetCursorEvent, getY, 0}},
- {2747, {wxSetCursorEvent, hasCursor, 0}},
- {2748, {wxSetCursorEvent, setCursor, 1}},
- {2749, {wxKeyEvent, altDown, 0}},
- {2750, {wxKeyEvent, cmdDown, 0}},
- {2751, {wxKeyEvent, controlDown, 0}},
- {2752, {wxKeyEvent, getKeyCode, 0}},
- {2753, {wxKeyEvent, getModifiers, 0}},
- {2756, {wxKeyEvent, getPosition, 0}},
- {2757, {wxKeyEvent, getRawKeyCode, 0}},
- {2758, {wxKeyEvent, getRawKeyFlags, 0}},
- {2759, {wxKeyEvent, getUnicodeKey, 0}},
- {2760, {wxKeyEvent, getX, 0}},
- {2761, {wxKeyEvent, getY, 0}},
- {2762, {wxKeyEvent, hasModifiers, 0}},
- {2763, {wxKeyEvent, metaDown, 0}},
- {2764, {wxKeyEvent, shiftDown, 0}},
- {2765, {wxSizeEvent, getSize, 0}},
- {2766, {wxMoveEvent, getPosition, 0}},
- {2767, {wxEraseEvent, getDC, 0}},
- {2768, {wxFocusEvent, getWindow, 0}},
- {2769, {wxChildFocusEvent, getWindow, 0}},
- {2770, {wxMenuEvent, getMenu, 0}},
- {2771, {wxMenuEvent, getMenuId, 0}},
- {2772, {wxMenuEvent, isPopup, 0}},
- {2773, {wxCloseEvent, canVeto, 0}},
- {2774, {wxCloseEvent, getLoggingOff, 0}},
- {2775, {wxCloseEvent, setCanVeto, 1}},
- {2776, {wxCloseEvent, setLoggingOff, 1}},
- {2777, {wxCloseEvent, veto, 1}},
- {2778, {wxShowEvent, setShow, 1}},
- {2779, {wxShowEvent, getShow, 0}},
- {2780, {wxIconizeEvent, iconized, 0}},
- {2781, {wxJoystickEvent, buttonDown, 1}},
- {2782, {wxJoystickEvent, buttonIsDown, 1}},
- {2783, {wxJoystickEvent, buttonUp, 1}},
- {2784, {wxJoystickEvent, getButtonChange, 0}},
- {2785, {wxJoystickEvent, getButtonState, 0}},
- {2786, {wxJoystickEvent, getJoystick, 0}},
- {2787, {wxJoystickEvent, getPosition, 0}},
- {2788, {wxJoystickEvent, getZPosition, 0}},
- {2789, {wxJoystickEvent, isButton, 0}},
- {2790, {wxJoystickEvent, isMove, 0}},
- {2791, {wxJoystickEvent, isZMove, 0}},
- {2792, {wxUpdateUIEvent, canUpdate, 1}},
- {2793, {wxUpdateUIEvent, check, 1}},
- {2794, {wxUpdateUIEvent, enable, 1}},
- {2795, {wxUpdateUIEvent, show, 1}},
- {2796, {wxUpdateUIEvent, getChecked, 0}},
- {2797, {wxUpdateUIEvent, getEnabled, 0}},
- {2798, {wxUpdateUIEvent, getShown, 0}},
- {2799, {wxUpdateUIEvent, getSetChecked, 0}},
- {2800, {wxUpdateUIEvent, getSetEnabled, 0}},
- {2801, {wxUpdateUIEvent, getSetShown, 0}},
- {2802, {wxUpdateUIEvent, getSetText, 0}},
- {2803, {wxUpdateUIEvent, getText, 0}},
- {2804, {wxUpdateUIEvent, getMode, 0}},
- {2805, {wxUpdateUIEvent, getUpdateInterval, 0}},
- {2806, {wxUpdateUIEvent, resetUpdateTime, 0}},
- {2807, {wxUpdateUIEvent, setMode, 1}},
- {2808, {wxUpdateUIEvent, setText, 1}},
- {2809, {wxUpdateUIEvent, setUpdateInterval, 1}},
- {2810, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
- {2811, {wxPaletteChangedEvent, setChangedWindow, 1}},
- {2812, {wxPaletteChangedEvent, getChangedWindow, 0}},
- {2813, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
- {2814, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
- {2815, {wxNavigationKeyEvent, getDirection, 0}},
- {2816, {wxNavigationKeyEvent, setDirection, 1}},
- {2817, {wxNavigationKeyEvent, isWindowChange, 0}},
- {2818, {wxNavigationKeyEvent, setWindowChange, 1}},
- {2819, {wxNavigationKeyEvent, isFromTab, 0}},
- {2820, {wxNavigationKeyEvent, setFromTab, 1}},
- {2821, {wxNavigationKeyEvent, getCurrentFocus, 0}},
- {2822, {wxNavigationKeyEvent, setCurrentFocus, 1}},
- {2823, {wxHelpEvent, getOrigin, 0}},
- {2824, {wxHelpEvent, getPosition, 0}},
- {2825, {wxHelpEvent, setOrigin, 1}},
- {2826, {wxHelpEvent, setPosition, 1}},
- {2827, {wxContextMenuEvent, getPosition, 0}},
- {2828, {wxContextMenuEvent, setPosition, 1}},
- {2829, {wxIdleEvent, canSend, 1}},
- {2830, {wxIdleEvent, getMode, 0}},
- {2831, {wxIdleEvent, requestMore, 1}},
- {2832, {wxIdleEvent, moreRequested, 0}},
- {2833, {wxIdleEvent, setMode, 1}},
- {2834, {wxGridEvent, altDown, 0}},
- {2835, {wxGridEvent, controlDown, 0}},
- {2836, {wxGridEvent, getCol, 0}},
- {2837, {wxGridEvent, getPosition, 0}},
- {2838, {wxGridEvent, getRow, 0}},
- {2839, {wxGridEvent, metaDown, 0}},
- {2840, {wxGridEvent, selecting, 0}},
- {2841, {wxGridEvent, shiftDown, 0}},
- {2842, {wxNotifyEvent, allow, 0}},
- {2843, {wxNotifyEvent, isAllowed, 0}},
- {2844, {wxNotifyEvent, veto, 0}},
- {2845, {wxSashEvent, getEdge, 0}},
- {2846, {wxSashEvent, getDragRect, 0}},
- {2847, {wxSashEvent, getDragStatus, 0}},
- {2848, {wxListEvent, getCacheFrom, 0}},
- {2849, {wxListEvent, getCacheTo, 0}},
- {2850, {wxListEvent, getKeyCode, 0}},
- {2851, {wxListEvent, getIndex, 0}},
- {2852, {wxListEvent, getColumn, 0}},
- {2853, {wxListEvent, getPoint, 0}},
- {2854, {wxListEvent, getLabel, 0}},
- {2855, {wxListEvent, getText, 0}},
- {2856, {wxListEvent, getImage, 0}},
- {2857, {wxListEvent, getData, 0}},
- {2858, {wxListEvent, getMask, 0}},
- {2859, {wxListEvent, getItem, 0}},
- {2860, {wxListEvent, isEditCancelled, 0}},
- {2861, {wxDateEvent, getDate, 0}},
- {2862, {wxCalendarEvent, getWeekDay, 0}},
- {2863, {wxFileDirPickerEvent, getPath, 0}},
- {2864, {wxColourPickerEvent, getColour, 0}},
- {2865, {wxFontPickerEvent, getFont, 0}},
- {2866, {wxStyledTextEvent, getPosition, 0}},
- {2867, {wxStyledTextEvent, getKey, 0}},
- {2868, {wxStyledTextEvent, getModifiers, 0}},
- {2869, {wxStyledTextEvent, getModificationType, 0}},
- {2870, {wxStyledTextEvent, getText, 0}},
- {2871, {wxStyledTextEvent, getLength, 0}},
- {2872, {wxStyledTextEvent, getLinesAdded, 0}},
- {2873, {wxStyledTextEvent, getLine, 0}},
- {2874, {wxStyledTextEvent, getFoldLevelNow, 0}},
- {2875, {wxStyledTextEvent, getFoldLevelPrev, 0}},
- {2876, {wxStyledTextEvent, getMargin, 0}},
- {2877, {wxStyledTextEvent, getMessage, 0}},
- {2878, {wxStyledTextEvent, getWParam, 0}},
- {2879, {wxStyledTextEvent, getLParam, 0}},
- {2880, {wxStyledTextEvent, getListType, 0}},
- {2881, {wxStyledTextEvent, getX, 0}},
- {2882, {wxStyledTextEvent, getY, 0}},
- {2883, {wxStyledTextEvent, getDragText, 0}},
- {2884, {wxStyledTextEvent, getDragAllowMove, 0}},
- {2885, {wxStyledTextEvent, getDragResult, 0}},
- {2886, {wxStyledTextEvent, getShift, 0}},
- {2887, {wxStyledTextEvent, getControl, 0}},
- {2888, {wxStyledTextEvent, getAlt, 0}},
- {2889, {utils, getKeyState, 1}},
- {2890, {utils, getMousePosition, 2}},
- {2891, {utils, getMouseState, 0}},
- {2892, {utils, setDetectableAutoRepeat, 1}},
- {2893, {utils, bell, 0}},
- {2894, {utils, findMenuItemId, 3}},
- {2895, {utils, genericFindWindowAtPoint, 1}},
- {2896, {utils, findWindowAtPoint, 1}},
- {2897, {utils, beginBusyCursor, 1}},
- {2898, {utils, endBusyCursor, 0}},
- {2899, {utils, isBusy, 0}},
- {2900, {utils, shutdown, 1}},
- {2901, {utils, shell, 1}},
- {2902, {utils, launchDefaultBrowser, 2}},
- {2903, {utils, getEmailAddress, 0}},
- {2904, {utils, getUserId, 0}},
- {2905, {utils, getHomeDir, 0}},
- {2906, {utils, newId, 0}},
- {2907, {utils, registerId, 1}},
- {2908, {utils, getCurrentId, 0}},
- {2909, {utils, getOsDescription, 0}},
- {2910, {utils, isPlatformLittleEndian, 0}},
- {2911, {utils, isPlatform64Bit, 0}},
- {2912, {wxPrintout, new, 1}},
- {2913, {wxPrintout, destruct, 0}},
- {2914, {wxPrintout, getDC, 0}},
- {2915, {wxPrintout, getPageSizeMM, 2}},
- {2916, {wxPrintout, getPageSizePixels, 2}},
- {2917, {wxPrintout, getPaperRectPixels, 0}},
- {2918, {wxPrintout, getPPIPrinter, 2}},
- {2919, {wxPrintout, getPPIScreen, 2}},
- {2920, {wxPrintout, getTitle, 0}},
- {2921, {wxPrintout, isPreview, 0}},
- {2922, {wxPrintout, fitThisSizeToPaper, 1}},
- {2923, {wxPrintout, fitThisSizeToPage, 1}},
- {2924, {wxPrintout, fitThisSizeToPageMargins, 2}},
- {2925, {wxPrintout, mapScreenSizeToPaper, 0}},
- {2926, {wxPrintout, mapScreenSizeToPage, 0}},
- {2927, {wxPrintout, mapScreenSizeToPageMargins, 1}},
- {2928, {wxPrintout, mapScreenSizeToDevice, 0}},
- {2929, {wxPrintout, getLogicalPaperRect, 0}},
- {2930, {wxPrintout, getLogicalPageRect, 0}},
- {2931, {wxPrintout, getLogicalPageMarginsRect, 1}},
- {2932, {wxPrintout, setLogicalOrigin, 2}},
- {2933, {wxPrintout, offsetLogicalOrigin, 2}},
- {2934, {wxStyledTextCtrl, new_2, 2}},
- {2935, {wxStyledTextCtrl, new_0, 0}},
- {2936, {wxStyledTextCtrl, destruct, 0}},
- {2937, {wxStyledTextCtrl, create, 2}},
- {2938, {wxStyledTextCtrl, addText, 1}},
- {2939, {wxStyledTextCtrl, addStyledText, 1}},
- {2940, {wxStyledTextCtrl, insertText, 2}},
- {2941, {wxStyledTextCtrl, clearAll, 0}},
- {2942, {wxStyledTextCtrl, clearDocumentStyle, 0}},
- {2943, {wxStyledTextCtrl, getLength, 0}},
- {2944, {wxStyledTextCtrl, getCharAt, 1}},
- {2945, {wxStyledTextCtrl, getCurrentPos, 0}},
- {2946, {wxStyledTextCtrl, getAnchor, 0}},
- {2947, {wxStyledTextCtrl, getStyleAt, 1}},
- {2948, {wxStyledTextCtrl, redo, 0}},
- {2949, {wxStyledTextCtrl, setUndoCollection, 1}},
- {2950, {wxStyledTextCtrl, selectAll, 0}},
- {2951, {wxStyledTextCtrl, setSavePoint, 0}},
- {2952, {wxStyledTextCtrl, getStyledText, 2}},
- {2953, {wxStyledTextCtrl, canRedo, 0}},
- {2954, {wxStyledTextCtrl, markerLineFromHandle, 1}},
- {2955, {wxStyledTextCtrl, markerDeleteHandle, 1}},
- {2956, {wxStyledTextCtrl, getUndoCollection, 0}},
- {2957, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
- {2958, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
- {2959, {wxStyledTextCtrl, positionFromPoint, 1}},
- {2960, {wxStyledTextCtrl, positionFromPointClose, 2}},
- {2961, {wxStyledTextCtrl, gotoLine, 1}},
- {2962, {wxStyledTextCtrl, gotoPos, 1}},
- {2963, {wxStyledTextCtrl, setAnchor, 1}},
- {2964, {wxStyledTextCtrl, getCurLine, 1}},
- {2965, {wxStyledTextCtrl, getEndStyled, 0}},
- {2966, {wxStyledTextCtrl, convertEOLs, 1}},
- {2967, {wxStyledTextCtrl, getEOLMode, 0}},
- {2968, {wxStyledTextCtrl, setEOLMode, 1}},
- {2969, {wxStyledTextCtrl, startStyling, 2}},
- {2970, {wxStyledTextCtrl, setStyling, 2}},
- {2971, {wxStyledTextCtrl, getBufferedDraw, 0}},
- {2972, {wxStyledTextCtrl, setBufferedDraw, 1}},
- {2973, {wxStyledTextCtrl, setTabWidth, 1}},
- {2974, {wxStyledTextCtrl, getTabWidth, 0}},
- {2975, {wxStyledTextCtrl, setCodePage, 1}},
- {2976, {wxStyledTextCtrl, markerDefine, 3}},
- {2977, {wxStyledTextCtrl, markerSetForeground, 2}},
- {2978, {wxStyledTextCtrl, markerSetBackground, 2}},
- {2979, {wxStyledTextCtrl, markerAdd, 2}},
- {2980, {wxStyledTextCtrl, markerDelete, 2}},
- {2981, {wxStyledTextCtrl, markerDeleteAll, 1}},
- {2982, {wxStyledTextCtrl, markerGet, 1}},
- {2983, {wxStyledTextCtrl, markerNext, 2}},
- {2984, {wxStyledTextCtrl, markerPrevious, 2}},
- {2985, {wxStyledTextCtrl, markerDefineBitmap, 2}},
- {2986, {wxStyledTextCtrl, markerAddSet, 2}},
- {2987, {wxStyledTextCtrl, markerSetAlpha, 2}},
- {2988, {wxStyledTextCtrl, setMarginType, 2}},
- {2989, {wxStyledTextCtrl, getMarginType, 1}},
- {2990, {wxStyledTextCtrl, setMarginWidth, 2}},
- {2991, {wxStyledTextCtrl, getMarginWidth, 1}},
- {2992, {wxStyledTextCtrl, setMarginMask, 2}},
- {2993, {wxStyledTextCtrl, getMarginMask, 1}},
- {2994, {wxStyledTextCtrl, setMarginSensitive, 2}},
- {2995, {wxStyledTextCtrl, getMarginSensitive, 1}},
- {2996, {wxStyledTextCtrl, styleClearAll, 0}},
- {2997, {wxStyledTextCtrl, styleSetForeground, 2}},
- {2998, {wxStyledTextCtrl, styleSetBackground, 2}},
- {2999, {wxStyledTextCtrl, styleSetBold, 2}},
- {3000, {wxStyledTextCtrl, styleSetItalic, 2}},
- {3001, {wxStyledTextCtrl, styleSetSize, 2}},
- {3002, {wxStyledTextCtrl, styleSetFaceName, 2}},
- {3003, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
- {3004, {wxStyledTextCtrl, styleResetDefault, 0}},
- {3005, {wxStyledTextCtrl, styleSetUnderline, 2}},
- {3006, {wxStyledTextCtrl, styleSetCase, 2}},
- {3007, {wxStyledTextCtrl, styleSetHotSpot, 2}},
- {3008, {wxStyledTextCtrl, setSelForeground, 2}},
- {3009, {wxStyledTextCtrl, setSelBackground, 2}},
- {3010, {wxStyledTextCtrl, getSelAlpha, 0}},
- {3011, {wxStyledTextCtrl, setSelAlpha, 1}},
- {3012, {wxStyledTextCtrl, setCaretForeground, 1}},
- {3013, {wxStyledTextCtrl, cmdKeyAssign, 3}},
- {3014, {wxStyledTextCtrl, cmdKeyClear, 2}},
- {3015, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
- {3016, {wxStyledTextCtrl, setStyleBytes, 2}},
- {3017, {wxStyledTextCtrl, styleSetVisible, 2}},
- {3018, {wxStyledTextCtrl, getCaretPeriod, 0}},
- {3019, {wxStyledTextCtrl, setCaretPeriod, 1}},
- {3020, {wxStyledTextCtrl, setWordChars, 1}},
- {3021, {wxStyledTextCtrl, beginUndoAction, 0}},
- {3022, {wxStyledTextCtrl, endUndoAction, 0}},
- {3023, {wxStyledTextCtrl, indicatorSetStyle, 2}},
- {3024, {wxStyledTextCtrl, indicatorGetStyle, 1}},
- {3025, {wxStyledTextCtrl, indicatorSetForeground, 2}},
- {3026, {wxStyledTextCtrl, indicatorGetForeground, 1}},
- {3027, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
- {3028, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
- {3029, {wxStyledTextCtrl, getStyleBits, 0}},
- {3030, {wxStyledTextCtrl, setLineState, 2}},
- {3031, {wxStyledTextCtrl, getLineState, 1}},
- {3032, {wxStyledTextCtrl, getMaxLineState, 0}},
- {3033, {wxStyledTextCtrl, getCaretLineVisible, 0}},
- {3034, {wxStyledTextCtrl, setCaretLineVisible, 1}},
- {3035, {wxStyledTextCtrl, getCaretLineBackground, 0}},
- {3036, {wxStyledTextCtrl, setCaretLineBackground, 1}},
- {3037, {wxStyledTextCtrl, autoCompShow, 2}},
- {3038, {wxStyledTextCtrl, autoCompCancel, 0}},
- {3039, {wxStyledTextCtrl, autoCompActive, 0}},
- {3040, {wxStyledTextCtrl, autoCompPosStart, 0}},
- {3041, {wxStyledTextCtrl, autoCompComplete, 0}},
- {3042, {wxStyledTextCtrl, autoCompStops, 1}},
- {3043, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
- {3044, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
- {3045, {wxStyledTextCtrl, autoCompSelect, 1}},
- {3046, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
- {3047, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
- {3048, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
- {3049, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
- {3050, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
- {3051, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
- {3052, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
- {3053, {wxStyledTextCtrl, userListShow, 2}},
- {3054, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
- {3055, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
- {3056, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
- {3057, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
- {3058, {wxStyledTextCtrl, registerImage, 2}},
- {3059, {wxStyledTextCtrl, clearRegisteredImages, 0}},
- {3060, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
- {3061, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
- {3062, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
- {3063, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
- {3064, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
- {3065, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
- {3066, {wxStyledTextCtrl, setIndent, 1}},
- {3067, {wxStyledTextCtrl, getIndent, 0}},
- {3068, {wxStyledTextCtrl, setUseTabs, 1}},
- {3069, {wxStyledTextCtrl, getUseTabs, 0}},
- {3070, {wxStyledTextCtrl, setLineIndentation, 2}},
- {3071, {wxStyledTextCtrl, getLineIndentation, 1}},
- {3072, {wxStyledTextCtrl, getLineIndentPosition, 1}},
- {3073, {wxStyledTextCtrl, getColumn, 1}},
- {3074, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
- {3075, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
- {3076, {wxStyledTextCtrl, setIndentationGuides, 1}},
- {3077, {wxStyledTextCtrl, getIndentationGuides, 0}},
- {3078, {wxStyledTextCtrl, setHighlightGuide, 1}},
- {3079, {wxStyledTextCtrl, getHighlightGuide, 0}},
- {3080, {wxStyledTextCtrl, getLineEndPosition, 1}},
- {3081, {wxStyledTextCtrl, getCodePage, 0}},
- {3082, {wxStyledTextCtrl, getCaretForeground, 0}},
- {3083, {wxStyledTextCtrl, getReadOnly, 0}},
- {3084, {wxStyledTextCtrl, setCurrentPos, 1}},
- {3085, {wxStyledTextCtrl, setSelectionStart, 1}},
- {3086, {wxStyledTextCtrl, getSelectionStart, 0}},
- {3087, {wxStyledTextCtrl, setSelectionEnd, 1}},
- {3088, {wxStyledTextCtrl, getSelectionEnd, 0}},
- {3089, {wxStyledTextCtrl, setPrintMagnification, 1}},
- {3090, {wxStyledTextCtrl, getPrintMagnification, 0}},
- {3091, {wxStyledTextCtrl, setPrintColourMode, 1}},
- {3092, {wxStyledTextCtrl, getPrintColourMode, 0}},
- {3093, {wxStyledTextCtrl, findText, 4}},
- {3094, {wxStyledTextCtrl, formatRange, 7}},
- {3095, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
- {3096, {wxStyledTextCtrl, getLine, 1}},
- {3097, {wxStyledTextCtrl, getLineCount, 0}},
- {3098, {wxStyledTextCtrl, setMarginLeft, 1}},
- {3099, {wxStyledTextCtrl, getMarginLeft, 0}},
- {3100, {wxStyledTextCtrl, setMarginRight, 1}},
- {3101, {wxStyledTextCtrl, getMarginRight, 0}},
- {3102, {wxStyledTextCtrl, getModify, 0}},
- {3103, {wxStyledTextCtrl, setSelection, 2}},
- {3104, {wxStyledTextCtrl, getSelectedText, 0}},
- {3105, {wxStyledTextCtrl, getTextRange, 2}},
- {3106, {wxStyledTextCtrl, hideSelection, 1}},
- {3107, {wxStyledTextCtrl, lineFromPosition, 1}},
- {3108, {wxStyledTextCtrl, positionFromLine, 1}},
- {3109, {wxStyledTextCtrl, lineScroll, 2}},
- {3110, {wxStyledTextCtrl, ensureCaretVisible, 0}},
- {3111, {wxStyledTextCtrl, replaceSelection, 1}},
- {3112, {wxStyledTextCtrl, setReadOnly, 1}},
- {3113, {wxStyledTextCtrl, canPaste, 0}},
- {3114, {wxStyledTextCtrl, canUndo, 0}},
- {3115, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
- {3116, {wxStyledTextCtrl, undo, 0}},
- {3117, {wxStyledTextCtrl, cut, 0}},
- {3118, {wxStyledTextCtrl, copy, 0}},
- {3119, {wxStyledTextCtrl, paste, 0}},
- {3120, {wxStyledTextCtrl, clear, 0}},
- {3121, {wxStyledTextCtrl, setText, 1}},
- {3122, {wxStyledTextCtrl, getText, 0}},
- {3123, {wxStyledTextCtrl, getTextLength, 0}},
- {3124, {wxStyledTextCtrl, getOvertype, 0}},
- {3125, {wxStyledTextCtrl, setCaretWidth, 1}},
- {3126, {wxStyledTextCtrl, getCaretWidth, 0}},
- {3127, {wxStyledTextCtrl, setTargetStart, 1}},
- {3128, {wxStyledTextCtrl, getTargetStart, 0}},
- {3129, {wxStyledTextCtrl, setTargetEnd, 1}},
- {3130, {wxStyledTextCtrl, getTargetEnd, 0}},
- {3131, {wxStyledTextCtrl, replaceTarget, 1}},
- {3132, {wxStyledTextCtrl, searchInTarget, 1}},
- {3133, {wxStyledTextCtrl, setSearchFlags, 1}},
- {3134, {wxStyledTextCtrl, getSearchFlags, 0}},
- {3135, {wxStyledTextCtrl, callTipShow, 2}},
- {3136, {wxStyledTextCtrl, callTipCancel, 0}},
- {3137, {wxStyledTextCtrl, callTipActive, 0}},
- {3138, {wxStyledTextCtrl, callTipPosAtStart, 0}},
- {3139, {wxStyledTextCtrl, callTipSetHighlight, 2}},
- {3140, {wxStyledTextCtrl, callTipSetBackground, 1}},
- {3141, {wxStyledTextCtrl, callTipSetForeground, 1}},
- {3142, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
- {3143, {wxStyledTextCtrl, callTipUseStyle, 1}},
- {3144, {wxStyledTextCtrl, visibleFromDocLine, 1}},
- {3145, {wxStyledTextCtrl, docLineFromVisible, 1}},
- {3146, {wxStyledTextCtrl, wrapCount, 1}},
- {3147, {wxStyledTextCtrl, setFoldLevel, 2}},
- {3148, {wxStyledTextCtrl, getFoldLevel, 1}},
- {3149, {wxStyledTextCtrl, getLastChild, 2}},
- {3150, {wxStyledTextCtrl, getFoldParent, 1}},
- {3151, {wxStyledTextCtrl, showLines, 2}},
- {3152, {wxStyledTextCtrl, hideLines, 2}},
- {3153, {wxStyledTextCtrl, getLineVisible, 1}},
- {3154, {wxStyledTextCtrl, setFoldExpanded, 2}},
- {3155, {wxStyledTextCtrl, getFoldExpanded, 1}},
- {3156, {wxStyledTextCtrl, toggleFold, 1}},
- {3157, {wxStyledTextCtrl, ensureVisible, 1}},
- {3158, {wxStyledTextCtrl, setFoldFlags, 1}},
- {3159, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
- {3160, {wxStyledTextCtrl, setTabIndents, 1}},
- {3161, {wxStyledTextCtrl, getTabIndents, 0}},
- {3162, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
- {3163, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
- {3164, {wxStyledTextCtrl, setMouseDwellTime, 1}},
- {3165, {wxStyledTextCtrl, getMouseDwellTime, 0}},
- {3166, {wxStyledTextCtrl, wordStartPosition, 2}},
- {3167, {wxStyledTextCtrl, wordEndPosition, 2}},
- {3168, {wxStyledTextCtrl, setWrapMode, 1}},
- {3169, {wxStyledTextCtrl, getWrapMode, 0}},
- {3170, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
- {3171, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
- {3172, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
- {3173, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
- {3174, {wxStyledTextCtrl, setWrapStartIndent, 1}},
- {3175, {wxStyledTextCtrl, getWrapStartIndent, 0}},
- {3176, {wxStyledTextCtrl, setLayoutCache, 1}},
- {3177, {wxStyledTextCtrl, getLayoutCache, 0}},
- {3178, {wxStyledTextCtrl, setScrollWidth, 1}},
- {3179, {wxStyledTextCtrl, getScrollWidth, 0}},
- {3180, {wxStyledTextCtrl, textWidth, 2}},
- {3181, {wxStyledTextCtrl, getEndAtLastLine, 0}},
- {3182, {wxStyledTextCtrl, textHeight, 1}},
- {3183, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
- {3184, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
- {3185, {wxStyledTextCtrl, appendText, 1}},
- {3186, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
- {3187, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
- {3188, {wxStyledTextCtrl, targetFromSelection, 0}},
- {3189, {wxStyledTextCtrl, linesJoin, 0}},
- {3190, {wxStyledTextCtrl, linesSplit, 1}},
- {3191, {wxStyledTextCtrl, setFoldMarginColour, 2}},
- {3192, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
- {3193, {wxStyledTextCtrl, lineDown, 0}},
- {3194, {wxStyledTextCtrl, lineDownExtend, 0}},
- {3195, {wxStyledTextCtrl, lineUp, 0}},
- {3196, {wxStyledTextCtrl, lineUpExtend, 0}},
- {3197, {wxStyledTextCtrl, charLeft, 0}},
- {3198, {wxStyledTextCtrl, charLeftExtend, 0}},
- {3199, {wxStyledTextCtrl, charRight, 0}},
- {3200, {wxStyledTextCtrl, charRightExtend, 0}},
- {3201, {wxStyledTextCtrl, wordLeft, 0}},
- {3202, {wxStyledTextCtrl, wordLeftExtend, 0}},
- {3203, {wxStyledTextCtrl, wordRight, 0}},
- {3204, {wxStyledTextCtrl, wordRightExtend, 0}},
- {3205, {wxStyledTextCtrl, home, 0}},
- {3206, {wxStyledTextCtrl, homeExtend, 0}},
- {3207, {wxStyledTextCtrl, lineEnd, 0}},
- {3208, {wxStyledTextCtrl, lineEndExtend, 0}},
- {3209, {wxStyledTextCtrl, documentStart, 0}},
- {3210, {wxStyledTextCtrl, documentStartExtend, 0}},
- {3211, {wxStyledTextCtrl, documentEnd, 0}},
- {3212, {wxStyledTextCtrl, documentEndExtend, 0}},
- {3213, {wxStyledTextCtrl, pageUp, 0}},
- {3214, {wxStyledTextCtrl, pageUpExtend, 0}},
- {3215, {wxStyledTextCtrl, pageDown, 0}},
- {3216, {wxStyledTextCtrl, pageDownExtend, 0}},
- {3217, {wxStyledTextCtrl, editToggleOvertype, 0}},
- {3218, {wxStyledTextCtrl, cancel, 0}},
- {3219, {wxStyledTextCtrl, deleteBack, 0}},
- {3220, {wxStyledTextCtrl, tab, 0}},
- {3221, {wxStyledTextCtrl, backTab, 0}},
- {3222, {wxStyledTextCtrl, newLine, 0}},
- {3223, {wxStyledTextCtrl, formFeed, 0}},
- {3224, {wxStyledTextCtrl, vCHome, 0}},
- {3225, {wxStyledTextCtrl, vCHomeExtend, 0}},
- {3226, {wxStyledTextCtrl, zoomIn, 0}},
- {3227, {wxStyledTextCtrl, zoomOut, 0}},
- {3228, {wxStyledTextCtrl, delWordLeft, 0}},
- {3229, {wxStyledTextCtrl, delWordRight, 0}},
- {3230, {wxStyledTextCtrl, lineCut, 0}},
- {3231, {wxStyledTextCtrl, lineDelete, 0}},
- {3232, {wxStyledTextCtrl, lineTranspose, 0}},
- {3233, {wxStyledTextCtrl, lineDuplicate, 0}},
- {3234, {wxStyledTextCtrl, lowerCase, 0}},
- {3235, {wxStyledTextCtrl, upperCase, 0}},
- {3236, {wxStyledTextCtrl, lineScrollDown, 0}},
- {3237, {wxStyledTextCtrl, lineScrollUp, 0}},
- {3238, {wxStyledTextCtrl, deleteBackNotLine, 0}},
- {3239, {wxStyledTextCtrl, homeDisplay, 0}},
- {3240, {wxStyledTextCtrl, homeDisplayExtend, 0}},
- {3241, {wxStyledTextCtrl, lineEndDisplay, 0}},
- {3242, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
- {3243, {wxStyledTextCtrl, homeWrapExtend, 0}},
- {3244, {wxStyledTextCtrl, lineEndWrap, 0}},
- {3245, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
- {3246, {wxStyledTextCtrl, vCHomeWrap, 0}},
- {3247, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
- {3248, {wxStyledTextCtrl, lineCopy, 0}},
- {3249, {wxStyledTextCtrl, moveCaretInsideView, 0}},
- {3250, {wxStyledTextCtrl, lineLength, 1}},
- {3251, {wxStyledTextCtrl, braceHighlight, 2}},
- {3252, {wxStyledTextCtrl, braceBadLight, 1}},
- {3253, {wxStyledTextCtrl, braceMatch, 1}},
- {3254, {wxStyledTextCtrl, getViewEOL, 0}},
- {3255, {wxStyledTextCtrl, setViewEOL, 1}},
- {3256, {wxStyledTextCtrl, setModEventMask, 1}},
- {3257, {wxStyledTextCtrl, getEdgeColumn, 0}},
- {3258, {wxStyledTextCtrl, setEdgeColumn, 1}},
- {3259, {wxStyledTextCtrl, getEdgeMode, 0}},
- {3260, {wxStyledTextCtrl, getEdgeColour, 0}},
- {3261, {wxStyledTextCtrl, setEdgeColour, 1}},
- {3262, {wxStyledTextCtrl, searchAnchor, 0}},
- {3263, {wxStyledTextCtrl, searchNext, 2}},
- {3264, {wxStyledTextCtrl, searchPrev, 2}},
- {3265, {wxStyledTextCtrl, linesOnScreen, 0}},
- {3266, {wxStyledTextCtrl, usePopUp, 1}},
- {3267, {wxStyledTextCtrl, selectionIsRectangle, 0}},
- {3268, {wxStyledTextCtrl, setZoom, 1}},
- {3269, {wxStyledTextCtrl, getZoom, 0}},
- {3270, {wxStyledTextCtrl, getModEventMask, 0}},
- {3271, {wxStyledTextCtrl, setSTCFocus, 1}},
- {3272, {wxStyledTextCtrl, getSTCFocus, 0}},
- {3273, {wxStyledTextCtrl, setStatus, 1}},
- {3274, {wxStyledTextCtrl, getStatus, 0}},
- {3275, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
- {3276, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
- {3277, {wxStyledTextCtrl, setSTCCursor, 1}},
- {3278, {wxStyledTextCtrl, getSTCCursor, 0}},
- {3279, {wxStyledTextCtrl, setControlCharSymbol, 1}},
- {3280, {wxStyledTextCtrl, getControlCharSymbol, 0}},
- {3281, {wxStyledTextCtrl, wordPartLeft, 0}},
- {3282, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
- {3283, {wxStyledTextCtrl, wordPartRight, 0}},
- {3284, {wxStyledTextCtrl, wordPartRightExtend, 0}},
- {3285, {wxStyledTextCtrl, setVisiblePolicy, 2}},
- {3286, {wxStyledTextCtrl, delLineLeft, 0}},
- {3287, {wxStyledTextCtrl, delLineRight, 0}},
- {3288, {wxStyledTextCtrl, getXOffset, 0}},
- {3289, {wxStyledTextCtrl, chooseCaretX, 0}},
- {3290, {wxStyledTextCtrl, setXCaretPolicy, 2}},
- {3291, {wxStyledTextCtrl, setYCaretPolicy, 2}},
- {3292, {wxStyledTextCtrl, getPrintWrapMode, 0}},
- {3293, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
- {3294, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
- {3295, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
- {3296, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
- {3297, {wxStyledTextCtrl, paraDownExtend, 0}},
- {3298, {wxStyledTextCtrl, paraUp, 0}},
- {3299, {wxStyledTextCtrl, paraUpExtend, 0}},
- {3300, {wxStyledTextCtrl, positionBefore, 1}},
- {3301, {wxStyledTextCtrl, positionAfter, 1}},
- {3302, {wxStyledTextCtrl, copyRange, 2}},
- {3303, {wxStyledTextCtrl, copyText, 2}},
- {3304, {wxStyledTextCtrl, setSelectionMode, 1}},
- {3305, {wxStyledTextCtrl, getSelectionMode, 0}},
- {3306, {wxStyledTextCtrl, lineDownRectExtend, 0}},
- {3307, {wxStyledTextCtrl, lineUpRectExtend, 0}},
- {3308, {wxStyledTextCtrl, charLeftRectExtend, 0}},
- {3309, {wxStyledTextCtrl, charRightRectExtend, 0}},
- {3310, {wxStyledTextCtrl, homeRectExtend, 0}},
- {3311, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
- {3312, {wxStyledTextCtrl, lineEndRectExtend, 0}},
- {3313, {wxStyledTextCtrl, pageUpRectExtend, 0}},
- {3314, {wxStyledTextCtrl, pageDownRectExtend, 0}},
- {3315, {wxStyledTextCtrl, stutteredPageUp, 0}},
- {3316, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
- {3317, {wxStyledTextCtrl, stutteredPageDown, 0}},
- {3318, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
- {3319, {wxStyledTextCtrl, wordLeftEnd, 0}},
- {3320, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
- {3321, {wxStyledTextCtrl, wordRightEnd, 0}},
- {3322, {wxStyledTextCtrl, wordRightEndExtend, 0}},
- {3323, {wxStyledTextCtrl, setWhitespaceChars, 1}},
- {3324, {wxStyledTextCtrl, setCharsDefault, 0}},
- {3325, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
- {3326, {wxStyledTextCtrl, allocate, 1}},
- {3327, {wxStyledTextCtrl, findColumn, 2}},
- {3328, {wxStyledTextCtrl, getCaretSticky, 0}},
- {3329, {wxStyledTextCtrl, setCaretSticky, 1}},
- {3330, {wxStyledTextCtrl, toggleCaretSticky, 0}},
- {3331, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
- {3332, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
- {3333, {wxStyledTextCtrl, selectionDuplicate, 0}},
- {3334, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
- {3335, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
- {3336, {wxStyledTextCtrl, startRecord, 0}},
- {3337, {wxStyledTextCtrl, stopRecord, 0}},
- {3338, {wxStyledTextCtrl, setLexer, 1}},
- {3339, {wxStyledTextCtrl, getLexer, 0}},
- {3340, {wxStyledTextCtrl, colourise, 2}},
- {3341, {wxStyledTextCtrl, setProperty, 2}},
- {3342, {wxStyledTextCtrl, setKeyWords, 2}},
- {3343, {wxStyledTextCtrl, setLexerLanguage, 1}},
- {3344, {wxStyledTextCtrl, getProperty, 1}},
- {3345, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
- {3346, {wxStyledTextCtrl, getCurrentLine, 0}},
- {3347, {wxStyledTextCtrl, styleSetSpec, 2}},
- {3348, {wxStyledTextCtrl, styleSetFont, 2}},
- {3349, {wxStyledTextCtrl, styleSetFontAttr, 7}},
- {3350, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
- {3351, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
- {3352, {wxStyledTextCtrl, cmdKeyExecute, 1}},
- {3353, {wxStyledTextCtrl, setMargins, 2}},
- {3354, {wxStyledTextCtrl, getSelection, 2}},
- {3355, {wxStyledTextCtrl, pointFromPosition, 1}},
- {3356, {wxStyledTextCtrl, scrollToLine, 1}},
- {3357, {wxStyledTextCtrl, scrollToColumn, 1}},
- {3358, {wxStyledTextCtrl, sendMsg, 2}},
- {3359, {wxStyledTextCtrl, setVScrollBar, 1}},
- {3360, {wxStyledTextCtrl, setHScrollBar, 1}},
- {3361, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
- {3362, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
- {3363, {wxStyledTextCtrl, saveFile, 1}},
- {3364, {wxStyledTextCtrl, loadFile, 1}},
- {3365, {wxStyledTextCtrl, doDragOver, 3}},
- {3366, {wxStyledTextCtrl, doDropText, 3}},
- {3367, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
- {3368, {wxStyledTextCtrl, addTextRaw, 1}},
- {3369, {wxStyledTextCtrl, insertTextRaw, 2}},
- {3370, {wxStyledTextCtrl, getCurLineRaw, 1}},
- {3371, {wxStyledTextCtrl, getLineRaw, 1}},
- {3372, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
- {3373, {wxStyledTextCtrl, getTextRangeRaw, 2}},
- {3374, {wxStyledTextCtrl, setTextRaw, 1}},
- {3375, {wxStyledTextCtrl, getTextRaw, 0}},
- {3376, {wxStyledTextCtrl, appendTextRaw, 1}},
- {3377, {wxArtProvider, getBitmap, 2}},
- {3378, {wxArtProvider, getIcon, 2}},
- {3379, {wxTreeEvent, getKeyCode, 0}},
- {3380, {wxTreeEvent, getItem, 0}},
- {3381, {wxTreeEvent, getKeyEvent, 0}},
- {3382, {wxTreeEvent, getLabel, 0}},
- {3383, {wxTreeEvent, getOldItem, 0}},
- {3384, {wxTreeEvent, getPoint, 0}},
- {3385, {wxTreeEvent, isEditCancelled, 0}},
- {3386, {wxTreeEvent, setToolTip, 1}},
- {3387, {wxNotebookEvent, getOldSelection, 0}},
- {3388, {wxNotebookEvent, getSelection, 0}},
- {3389, {wxNotebookEvent, setOldSelection, 1}},
- {3390, {wxNotebookEvent, setSelection, 1}},
- {3391, {wxFileDataObject, new, 0}},
- {3392, {wxFileDataObject, addFile, 1}},
- {3393, {wxFileDataObject, getFilenames, 0}},
- {3394, {wxFileDataObject, 'Destroy', undefined}},
- {3395, {wxTextDataObject, new, 1}},
- {3396, {wxTextDataObject, getTextLength, 0}},
- {3397, {wxTextDataObject, getText, 0}},
- {3398, {wxTextDataObject, setText, 1}},
- {3399, {wxTextDataObject, 'Destroy', undefined}},
- {3400, {wxBitmapDataObject, new_1_1, 1}},
- {3401, {wxBitmapDataObject, new_1_0, 1}},
- {3402, {wxBitmapDataObject, getBitmap, 0}},
- {3403, {wxBitmapDataObject, setBitmap, 1}},
- {3404, {wxBitmapDataObject, 'Destroy', undefined}},
- {3406, {wxClipboard, new, 0}},
- {3407, {wxClipboard, destruct, 0}},
- {3408, {wxClipboard, addData, 1}},
- {3409, {wxClipboard, clear, 0}},
- {3410, {wxClipboard, close, 0}},
- {3411, {wxClipboard, flush, 0}},
- {3412, {wxClipboard, getData, 1}},
- {3413, {wxClipboard, isOpened, 0}},
- {3414, {wxClipboard, open, 0}},
- {3415, {wxClipboard, setData, 1}},
- {3417, {wxClipboard, usePrimarySelection, 1}},
- {3418, {wxClipboard, isSupported, 1}},
- {3419, {wxClipboard, get, 0}},
- {3420, {wxSpinEvent, getPosition, 0}},
- {3421, {wxSpinEvent, setPosition, 1}},
- {3422, {wxSplitterWindow, new_0, 0}},
- {3423, {wxSplitterWindow, new_2, 2}},
- {3424, {wxSplitterWindow, destruct, 0}},
- {3425, {wxSplitterWindow, create, 2}},
- {3426, {wxSplitterWindow, getMinimumPaneSize, 0}},
- {3427, {wxSplitterWindow, getSashGravity, 0}},
- {3428, {wxSplitterWindow, getSashPosition, 0}},
- {3429, {wxSplitterWindow, getSplitMode, 0}},
- {3430, {wxSplitterWindow, getWindow1, 0}},
- {3431, {wxSplitterWindow, getWindow2, 0}},
- {3432, {wxSplitterWindow, initialize, 1}},
- {3433, {wxSplitterWindow, isSplit, 0}},
- {3434, {wxSplitterWindow, replaceWindow, 2}},
- {3435, {wxSplitterWindow, setSashGravity, 1}},
- {3436, {wxSplitterWindow, setSashPosition, 2}},
- {3437, {wxSplitterWindow, setSashSize, 1}},
- {3438, {wxSplitterWindow, setMinimumPaneSize, 1}},
- {3439, {wxSplitterWindow, setSplitMode, 1}},
- {3440, {wxSplitterWindow, splitHorizontally, 3}},
- {3441, {wxSplitterWindow, splitVertically, 3}},
- {3442, {wxSplitterWindow, unsplit, 1}},
- {3443, {wxSplitterWindow, updateSize, 0}},
- {3444, {wxSplitterEvent, getSashPosition, 0}},
- {3445, {wxSplitterEvent, getX, 0}},
- {3446, {wxSplitterEvent, getY, 0}},
- {3447, {wxSplitterEvent, getWindowBeingRemoved, 0}},
- {3448, {wxSplitterEvent, setSashPosition, 1}},
- {3449, {wxHtmlWindow, new_0, 0}},
- {3450, {wxHtmlWindow, new_2, 2}},
- {3451, {wxHtmlWindow, appendToPage, 1}},
- {3452, {wxHtmlWindow, getOpenedAnchor, 0}},
- {3453, {wxHtmlWindow, getOpenedPage, 0}},
- {3454, {wxHtmlWindow, getOpenedPageTitle, 0}},
- {3455, {wxHtmlWindow, getRelatedFrame, 0}},
- {3456, {wxHtmlWindow, historyBack, 0}},
- {3457, {wxHtmlWindow, historyCanBack, 0}},
- {3458, {wxHtmlWindow, historyCanForward, 0}},
- {3459, {wxHtmlWindow, historyClear, 0}},
- {3460, {wxHtmlWindow, historyForward, 0}},
- {3461, {wxHtmlWindow, loadFile, 1}},
- {3462, {wxHtmlWindow, loadPage, 1}},
- {3463, {wxHtmlWindow, selectAll, 0}},
- {3464, {wxHtmlWindow, selectionToText, 0}},
- {3465, {wxHtmlWindow, selectLine, 1}},
- {3466, {wxHtmlWindow, selectWord, 1}},
- {3467, {wxHtmlWindow, setBorders, 1}},
- {3468, {wxHtmlWindow, setFonts, 3}},
- {3469, {wxHtmlWindow, setPage, 1}},
- {3470, {wxHtmlWindow, setRelatedFrame, 2}},
- {3471, {wxHtmlWindow, setRelatedStatusBar, 1}},
- {3472, {wxHtmlWindow, toText, 0}},
- {3473, {wxHtmlWindow, 'Destroy', undefined}},
- {3474, {wxHtmlLinkEvent, getLinkInfo, 0}},
- {3475, {wxAuiNotebookEvent, setSelection, 1}},
- {3476, {wxAuiNotebookEvent, getSelection, 0}},
- {3477, {wxAuiNotebookEvent, setOldSelection, 1}},
- {3478, {wxAuiNotebookEvent, getOldSelection, 0}},
- {3479, {wxAuiNotebookEvent, setDragSource, 1}},
- {3480, {wxAuiNotebookEvent, getDragSource, 0}},
- {3481, {wxAuiManagerEvent, setManager, 1}},
- {3482, {wxAuiManagerEvent, getManager, 0}},
- {3483, {wxAuiManagerEvent, setPane, 1}},
- {3484, {wxAuiManagerEvent, getPane, 0}},
- {3485, {wxAuiManagerEvent, setButton, 1}},
- {3486, {wxAuiManagerEvent, getButton, 0}},
- {3487, {wxAuiManagerEvent, setDC, 1}},
- {3488, {wxAuiManagerEvent, getDC, 0}},
- {3489, {wxAuiManagerEvent, veto, 1}},
- {3490, {wxAuiManagerEvent, getVeto, 0}},
- {3491, {wxAuiManagerEvent, setCanVeto, 1}},
- {3492, {wxAuiManagerEvent, canVeto, 0}},
- {3493, {wxLogNull, new, 0}},
- {3494, {wxLogNull, 'Destroy', undefined}},
+ {2010, {wxTreeCtrl, editLabel, 1}},
+ {2011, {wxTreeCtrl, ensureVisible, 1}},
+ {2012, {wxTreeCtrl, expand, 1}},
+ {2013, {wxTreeCtrl, getBoundingRect, 3}},
+ {2015, {wxTreeCtrl, getChildrenCount, 2}},
+ {2016, {wxTreeCtrl, getCount, 0}},
+ {2017, {wxTreeCtrl, getEditControl, 0}},
+ {2018, {wxTreeCtrl, getFirstChild, 2}},
+ {2019, {wxTreeCtrl, getNextChild, 2}},
+ {2020, {wxTreeCtrl, getFirstVisibleItem, 0}},
+ {2021, {wxTreeCtrl, getImageList, 0}},
+ {2022, {wxTreeCtrl, getIndent, 0}},
+ {2023, {wxTreeCtrl, getItemBackgroundColour, 1}},
+ {2024, {wxTreeCtrl, getItemData, 1}},
+ {2025, {wxTreeCtrl, getItemFont, 1}},
+ {2026, {wxTreeCtrl, getItemImage_1, 1}},
+ {2027, {wxTreeCtrl, getItemImage_2, 2}},
+ {2028, {wxTreeCtrl, getItemText, 1}},
+ {2029, {wxTreeCtrl, getItemTextColour, 1}},
+ {2030, {wxTreeCtrl, getLastChild, 1}},
+ {2031, {wxTreeCtrl, getNextSibling, 1}},
+ {2032, {wxTreeCtrl, getNextVisible, 1}},
+ {2033, {wxTreeCtrl, getItemParent, 1}},
+ {2034, {wxTreeCtrl, getPrevSibling, 1}},
+ {2035, {wxTreeCtrl, getPrevVisible, 1}},
+ {2036, {wxTreeCtrl, getRootItem, 0}},
+ {2037, {wxTreeCtrl, getSelection, 0}},
+ {2038, {wxTreeCtrl, getSelections, 1}},
+ {2039, {wxTreeCtrl, getStateImageList, 0}},
+ {2040, {wxTreeCtrl, hitTest, 1}},
+ {2042, {wxTreeCtrl, insertItem, 4}},
+ {2043, {wxTreeCtrl, isBold, 1}},
+ {2044, {wxTreeCtrl, isExpanded, 1}},
+ {2045, {wxTreeCtrl, isSelected, 1}},
+ {2046, {wxTreeCtrl, isVisible, 1}},
+ {2047, {wxTreeCtrl, itemHasChildren, 1}},
+ {2048, {wxTreeCtrl, prependItem, 3}},
+ {2049, {wxTreeCtrl, scrollTo, 1}},
+ {2050, {wxTreeCtrl, selectItem_1, 1}},
+ {2051, {wxTreeCtrl, selectItem_2, 2}},
+ {2052, {wxTreeCtrl, setIndent, 1}},
+ {2053, {wxTreeCtrl, setImageList, 1}},
+ {2054, {wxTreeCtrl, setItemBackgroundColour, 2}},
+ {2055, {wxTreeCtrl, setItemBold, 2}},
+ {2056, {wxTreeCtrl, setItemData, 2}},
+ {2057, {wxTreeCtrl, setItemDropHighlight, 2}},
+ {2058, {wxTreeCtrl, setItemFont, 2}},
+ {2059, {wxTreeCtrl, setItemHasChildren, 2}},
+ {2060, {wxTreeCtrl, setItemImage_2, 2}},
+ {2061, {wxTreeCtrl, setItemImage_3, 3}},
+ {2062, {wxTreeCtrl, setItemText, 2}},
+ {2063, {wxTreeCtrl, setItemTextColour, 2}},
+ {2064, {wxTreeCtrl, setStateImageList, 1}},
+ {2065, {wxTreeCtrl, setWindowStyle, 1}},
+ {2066, {wxTreeCtrl, sortChildren, 1}},
+ {2067, {wxTreeCtrl, toggle, 1}},
+ {2068, {wxTreeCtrl, toggleItemSelection, 1}},
+ {2069, {wxTreeCtrl, unselect, 0}},
+ {2070, {wxTreeCtrl, unselectAll, 0}},
+ {2071, {wxTreeCtrl, unselectItem, 1}},
+ {2072, {wxScrollBar, new_0, 0}},
+ {2073, {wxScrollBar, new_3, 3}},
+ {2074, {wxScrollBar, destruct, 0}},
+ {2075, {wxScrollBar, create, 3}},
+ {2076, {wxScrollBar, getRange, 0}},
+ {2077, {wxScrollBar, getPageSize, 0}},
+ {2078, {wxScrollBar, getThumbPosition, 0}},
+ {2079, {wxScrollBar, getThumbSize, 0}},
+ {2080, {wxScrollBar, setThumbPosition, 1}},
+ {2081, {wxScrollBar, setScrollbar, 5}},
+ {2083, {wxSpinButton, new_2, 2}},
+ {2084, {wxSpinButton, new_0, 0}},
+ {2085, {wxSpinButton, create, 2}},
+ {2086, {wxSpinButton, getMax, 0}},
+ {2087, {wxSpinButton, getMin, 0}},
+ {2088, {wxSpinButton, getValue, 0}},
+ {2089, {wxSpinButton, setRange, 2}},
+ {2090, {wxSpinButton, setValue, 1}},
+ {2091, {wxSpinButton, 'Destroy', undefined}},
+ {2092, {wxSpinCtrl, new_0, 0}},
+ {2093, {wxSpinCtrl, new_2, 2}},
+ {2095, {wxSpinCtrl, create, 2}},
+ {2098, {wxSpinCtrl, setValue_1_1, 1}},
+ {2099, {wxSpinCtrl, setValue_1_0, 1}},
+ {2101, {wxSpinCtrl, getValue, 0}},
+ {2103, {wxSpinCtrl, setRange, 2}},
+ {2104, {wxSpinCtrl, setSelection, 2}},
+ {2106, {wxSpinCtrl, getMin, 0}},
+ {2108, {wxSpinCtrl, getMax, 0}},
+ {2109, {wxSpinCtrl, 'Destroy', undefined}},
+ {2110, {wxStaticText, new_0, 0}},
+ {2111, {wxStaticText, new_4, 4}},
+ {2112, {wxStaticText, create, 4}},
+ {2113, {wxStaticText, getLabel, 0}},
+ {2114, {wxStaticText, setLabel, 1}},
+ {2115, {wxStaticText, wrap, 1}},
+ {2116, {wxStaticText, 'Destroy', undefined}},
+ {2117, {wxStaticBitmap, new_0, 0}},
+ {2118, {wxStaticBitmap, new_4, 4}},
+ {2119, {wxStaticBitmap, create, 4}},
+ {2120, {wxStaticBitmap, getBitmap, 0}},
+ {2121, {wxStaticBitmap, setBitmap, 1}},
+ {2122, {wxStaticBitmap, 'Destroy', undefined}},
+ {2123, {wxRadioBox, new, 7}},
+ {2125, {wxRadioBox, destruct, 0}},
+ {2126, {wxRadioBox, create, 7}},
+ {2127, {wxRadioBox, enable_2, 2}},
+ {2128, {wxRadioBox, enable_1, 1}},
+ {2129, {wxRadioBox, getSelection, 0}},
+ {2130, {wxRadioBox, getString, 1}},
+ {2131, {wxRadioBox, setSelection, 1}},
+ {2132, {wxRadioBox, show_2, 2}},
+ {2133, {wxRadioBox, show_1, 1}},
+ {2134, {wxRadioBox, getColumnCount, 0}},
+ {2135, {wxRadioBox, getItemHelpText, 1}},
+ {2136, {wxRadioBox, getItemToolTip, 1}},
+ {2138, {wxRadioBox, getItemFromPoint, 1}},
+ {2139, {wxRadioBox, getRowCount, 0}},
+ {2140, {wxRadioBox, isItemEnabled, 1}},
+ {2141, {wxRadioBox, isItemShown, 1}},
+ {2142, {wxRadioBox, setItemHelpText, 2}},
+ {2143, {wxRadioBox, setItemToolTip, 2}},
+ {2144, {wxRadioButton, new_0, 0}},
+ {2145, {wxRadioButton, new_4, 4}},
+ {2146, {wxRadioButton, create, 4}},
+ {2147, {wxRadioButton, getValue, 0}},
+ {2148, {wxRadioButton, setValue, 1}},
+ {2149, {wxRadioButton, 'Destroy', undefined}},
+ {2151, {wxSlider, new_6, 6}},
+ {2152, {wxSlider, new_0, 0}},
+ {2153, {wxSlider, create, 6}},
+ {2154, {wxSlider, getLineSize, 0}},
+ {2155, {wxSlider, getMax, 0}},
+ {2156, {wxSlider, getMin, 0}},
+ {2157, {wxSlider, getPageSize, 0}},
+ {2158, {wxSlider, getThumbLength, 0}},
+ {2159, {wxSlider, getValue, 0}},
+ {2160, {wxSlider, setLineSize, 1}},
+ {2161, {wxSlider, setPageSize, 1}},
+ {2162, {wxSlider, setRange, 2}},
+ {2163, {wxSlider, setThumbLength, 1}},
+ {2164, {wxSlider, setValue, 1}},
+ {2165, {wxSlider, 'Destroy', undefined}},
+ {2167, {wxDialog, new_4, 4}},
+ {2168, {wxDialog, new_0, 0}},
+ {2170, {wxDialog, destruct, 0}},
+ {2171, {wxDialog, create, 4}},
+ {2172, {wxDialog, createButtonSizer, 1}},
+ {2173, {wxDialog, createStdDialogButtonSizer, 1}},
+ {2174, {wxDialog, endModal, 1}},
+ {2175, {wxDialog, getAffirmativeId, 0}},
+ {2176, {wxDialog, getReturnCode, 0}},
+ {2177, {wxDialog, isModal, 0}},
+ {2178, {wxDialog, setAffirmativeId, 1}},
+ {2179, {wxDialog, setReturnCode, 1}},
+ {2180, {wxDialog, show, 1}},
+ {2181, {wxDialog, showModal, 0}},
+ {2182, {wxColourDialog, new_0, 0}},
+ {2183, {wxColourDialog, new_2, 2}},
+ {2184, {wxColourDialog, destruct, 0}},
+ {2185, {wxColourDialog, create, 2}},
+ {2186, {wxColourDialog, getColourData, 0}},
+ {2187, {wxColourData, new_0, 0}},
+ {2188, {wxColourData, new_1, 1}},
+ {2189, {wxColourData, destruct, 0}},
+ {2190, {wxColourData, getChooseFull, 0}},
+ {2191, {wxColourData, getColour, 0}},
+ {2193, {wxColourData, getCustomColour, 1}},
+ {2194, {wxColourData, setChooseFull, 1}},
+ {2195, {wxColourData, setColour, 1}},
+ {2196, {wxColourData, setCustomColour, 2}},
+ {2197, {wxPalette, new_0, 0}},
+ {2198, {wxPalette, new_4, 4}},
+ {2200, {wxPalette, destruct, 0}},
+ {2201, {wxPalette, create, 4}},
+ {2202, {wxPalette, getColoursCount, 0}},
+ {2203, {wxPalette, getPixel, 3}},
+ {2204, {wxPalette, getRGB, 4}},
+ {2205, {wxPalette, isOk, 0}},
+ {2209, {wxDirDialog, new, 2}},
+ {2210, {wxDirDialog, destruct, 0}},
+ {2211, {wxDirDialog, getPath, 0}},
+ {2212, {wxDirDialog, getMessage, 0}},
+ {2213, {wxDirDialog, setMessage, 1}},
+ {2214, {wxDirDialog, setPath, 1}},
+ {2218, {wxFileDialog, new, 2}},
+ {2219, {wxFileDialog, destruct, 0}},
+ {2220, {wxFileDialog, getDirectory, 0}},
+ {2221, {wxFileDialog, getFilename, 0}},
+ {2222, {wxFileDialog, getFilenames, 1}},
+ {2223, {wxFileDialog, getFilterIndex, 0}},
+ {2224, {wxFileDialog, getMessage, 0}},
+ {2225, {wxFileDialog, getPath, 0}},
+ {2226, {wxFileDialog, getPaths, 1}},
+ {2227, {wxFileDialog, getWildcard, 0}},
+ {2228, {wxFileDialog, setDirectory, 1}},
+ {2229, {wxFileDialog, setFilename, 1}},
+ {2230, {wxFileDialog, setFilterIndex, 1}},
+ {2231, {wxFileDialog, setMessage, 1}},
+ {2232, {wxFileDialog, setPath, 1}},
+ {2233, {wxFileDialog, setWildcard, 1}},
+ {2234, {wxPickerBase, setInternalMargin, 1}},
+ {2235, {wxPickerBase, getInternalMargin, 0}},
+ {2236, {wxPickerBase, setTextCtrlProportion, 1}},
+ {2237, {wxPickerBase, setPickerCtrlProportion, 1}},
+ {2238, {wxPickerBase, getTextCtrlProportion, 0}},
+ {2239, {wxPickerBase, getPickerCtrlProportion, 0}},
+ {2240, {wxPickerBase, hasTextCtrl, 0}},
+ {2241, {wxPickerBase, getTextCtrl, 0}},
+ {2242, {wxPickerBase, isTextCtrlGrowable, 0}},
+ {2243, {wxPickerBase, setPickerCtrlGrowable, 1}},
+ {2244, {wxPickerBase, setTextCtrlGrowable, 1}},
+ {2245, {wxPickerBase, isPickerCtrlGrowable, 0}},
+ {2246, {wxFilePickerCtrl, new_0, 0}},
+ {2247, {wxFilePickerCtrl, new_3, 3}},
+ {2248, {wxFilePickerCtrl, create, 3}},
+ {2249, {wxFilePickerCtrl, getPath, 0}},
+ {2250, {wxFilePickerCtrl, setPath, 1}},
+ {2251, {wxFilePickerCtrl, 'Destroy', undefined}},
+ {2252, {wxDirPickerCtrl, new_0, 0}},
+ {2253, {wxDirPickerCtrl, new_3, 3}},
+ {2254, {wxDirPickerCtrl, create, 3}},
+ {2255, {wxDirPickerCtrl, getPath, 0}},
+ {2256, {wxDirPickerCtrl, setPath, 1}},
+ {2257, {wxDirPickerCtrl, 'Destroy', undefined}},
+ {2258, {wxColourPickerCtrl, new_0, 0}},
+ {2259, {wxColourPickerCtrl, new_3, 3}},
+ {2260, {wxColourPickerCtrl, create, 3}},
+ {2261, {wxColourPickerCtrl, getColour, 0}},
+ {2262, {wxColourPickerCtrl, setColour_1_1, 1}},
+ {2263, {wxColourPickerCtrl, setColour_1_0, 1}},
+ {2264, {wxColourPickerCtrl, 'Destroy', undefined}},
+ {2265, {wxDatePickerCtrl, new_0, 0}},
+ {2266, {wxDatePickerCtrl, new_3, 3}},
+ {2267, {wxDatePickerCtrl, getRange, 2}},
+ {2268, {wxDatePickerCtrl, getValue, 0}},
+ {2269, {wxDatePickerCtrl, setRange, 2}},
+ {2270, {wxDatePickerCtrl, setValue, 1}},
+ {2271, {wxDatePickerCtrl, 'Destroy', undefined}},
+ {2272, {wxFontPickerCtrl, new_0, 0}},
+ {2273, {wxFontPickerCtrl, new_3, 3}},
+ {2274, {wxFontPickerCtrl, create, 3}},
+ {2275, {wxFontPickerCtrl, getSelectedFont, 0}},
+ {2276, {wxFontPickerCtrl, setSelectedFont, 1}},
+ {2277, {wxFontPickerCtrl, getMaxPointSize, 0}},
+ {2278, {wxFontPickerCtrl, setMaxPointSize, 1}},
+ {2279, {wxFontPickerCtrl, 'Destroy', undefined}},
+ {2282, {wxFindReplaceDialog, new_0, 0}},
+ {2283, {wxFindReplaceDialog, new_4, 4}},
+ {2284, {wxFindReplaceDialog, destruct, 0}},
+ {2285, {wxFindReplaceDialog, create, 4}},
+ {2286, {wxFindReplaceDialog, getData, 0}},
+ {2287, {wxFindReplaceData, new_0, 0}},
+ {2288, {wxFindReplaceData, new_1, 1}},
+ {2289, {wxFindReplaceData, getFindString, 0}},
+ {2290, {wxFindReplaceData, getReplaceString, 0}},
+ {2291, {wxFindReplaceData, getFlags, 0}},
+ {2292, {wxFindReplaceData, setFlags, 1}},
+ {2293, {wxFindReplaceData, setFindString, 1}},
+ {2294, {wxFindReplaceData, setReplaceString, 1}},
+ {2295, {wxFindReplaceData, 'Destroy', undefined}},
+ {2296, {wxMultiChoiceDialog, new_0, 0}},
+ {2298, {wxMultiChoiceDialog, new_5, 5}},
+ {2299, {wxMultiChoiceDialog, getSelections, 0}},
+ {2300, {wxMultiChoiceDialog, setSelections, 1}},
+ {2301, {wxMultiChoiceDialog, 'Destroy', undefined}},
+ {2302, {wxSingleChoiceDialog, new_0, 0}},
+ {2304, {wxSingleChoiceDialog, new_5, 5}},
+ {2305, {wxSingleChoiceDialog, getSelection, 0}},
+ {2306, {wxSingleChoiceDialog, getStringSelection, 0}},
+ {2307, {wxSingleChoiceDialog, setSelection, 1}},
+ {2308, {wxSingleChoiceDialog, 'Destroy', undefined}},
+ {2309, {wxTextEntryDialog, new, 3}},
+ {2310, {wxTextEntryDialog, getValue, 0}},
+ {2311, {wxTextEntryDialog, setValue, 1}},
+ {2312, {wxTextEntryDialog, 'Destroy', undefined}},
+ {2313, {wxPasswordEntryDialog, new, 3}},
+ {2314, {wxPasswordEntryDialog, 'Destroy', undefined}},
+ {2315, {wxFontData, new_0, 0}},
+ {2316, {wxFontData, new_1, 1}},
+ {2317, {wxFontData, destruct, 0}},
+ {2318, {wxFontData, enableEffects, 1}},
+ {2319, {wxFontData, getAllowSymbols, 0}},
+ {2320, {wxFontData, getColour, 0}},
+ {2321, {wxFontData, getChosenFont, 0}},
+ {2322, {wxFontData, getEnableEffects, 0}},
+ {2323, {wxFontData, getInitialFont, 0}},
+ {2324, {wxFontData, getShowHelp, 0}},
+ {2325, {wxFontData, setAllowSymbols, 1}},
+ {2326, {wxFontData, setChosenFont, 1}},
+ {2327, {wxFontData, setColour, 1}},
+ {2328, {wxFontData, setInitialFont, 1}},
+ {2329, {wxFontData, setRange, 2}},
+ {2330, {wxFontData, setShowHelp, 1}},
+ {2334, {wxFontDialog, new_0, 0}},
+ {2336, {wxFontDialog, new_2, 2}},
+ {2338, {wxFontDialog, create, 2}},
+ {2339, {wxFontDialog, getFontData, 0}},
+ {2341, {wxFontDialog, 'Destroy', undefined}},
+ {2342, {wxProgressDialog, new, 3}},
+ {2343, {wxProgressDialog, destruct, 0}},
+ {2344, {wxProgressDialog, resume, 0}},
+ {2345, {wxProgressDialog, update_2, 2}},
+ {2346, {wxProgressDialog, update_0, 0}},
+ {2347, {wxMessageDialog, new, 3}},
+ {2348, {wxMessageDialog, destruct, 0}},
+ {2349, {wxPageSetupDialog, new, 2}},
+ {2350, {wxPageSetupDialog, destruct, 0}},
+ {2351, {wxPageSetupDialog, getPageSetupData, 0}},
+ {2352, {wxPageSetupDialog, showModal, 0}},
+ {2353, {wxPageSetupDialogData, new_0, 0}},
+ {2354, {wxPageSetupDialogData, new_1_0, 1}},
+ {2355, {wxPageSetupDialogData, new_1_1, 1}},
+ {2356, {wxPageSetupDialogData, destruct, 0}},
+ {2357, {wxPageSetupDialogData, enableHelp, 1}},
+ {2358, {wxPageSetupDialogData, enableMargins, 1}},
+ {2359, {wxPageSetupDialogData, enableOrientation, 1}},
+ {2360, {wxPageSetupDialogData, enablePaper, 1}},
+ {2361, {wxPageSetupDialogData, enablePrinter, 1}},
+ {2362, {wxPageSetupDialogData, getDefaultMinMargins, 0}},
+ {2363, {wxPageSetupDialogData, getEnableMargins, 0}},
+ {2364, {wxPageSetupDialogData, getEnableOrientation, 0}},
+ {2365, {wxPageSetupDialogData, getEnablePaper, 0}},
+ {2366, {wxPageSetupDialogData, getEnablePrinter, 0}},
+ {2367, {wxPageSetupDialogData, getEnableHelp, 0}},
+ {2368, {wxPageSetupDialogData, getDefaultInfo, 0}},
+ {2369, {wxPageSetupDialogData, getMarginTopLeft, 0}},
+ {2370, {wxPageSetupDialogData, getMarginBottomRight, 0}},
+ {2371, {wxPageSetupDialogData, getMinMarginTopLeft, 0}},
+ {2372, {wxPageSetupDialogData, getMinMarginBottomRight, 0}},
+ {2373, {wxPageSetupDialogData, getPaperId, 0}},
+ {2374, {wxPageSetupDialogData, getPaperSize, 0}},
+ {2376, {wxPageSetupDialogData, getPrintData, 0}},
+ {2377, {wxPageSetupDialogData, isOk, 0}},
+ {2378, {wxPageSetupDialogData, setDefaultInfo, 1}},
+ {2379, {wxPageSetupDialogData, setDefaultMinMargins, 1}},
+ {2380, {wxPageSetupDialogData, setMarginTopLeft, 1}},
+ {2381, {wxPageSetupDialogData, setMarginBottomRight, 1}},
+ {2382, {wxPageSetupDialogData, setMinMarginTopLeft, 1}},
+ {2383, {wxPageSetupDialogData, setMinMarginBottomRight, 1}},
+ {2384, {wxPageSetupDialogData, setPaperId, 1}},
+ {2385, {wxPageSetupDialogData, setPaperSize_1_1, 1}},
+ {2386, {wxPageSetupDialogData, setPaperSize_1_0, 1}},
+ {2387, {wxPageSetupDialogData, setPrintData, 1}},
+ {2388, {wxPrintDialog, new_2_0, 2}},
+ {2389, {wxPrintDialog, new_2_1, 2}},
+ {2390, {wxPrintDialog, destruct, 0}},
+ {2391, {wxPrintDialog, getPrintDialogData, 0}},
+ {2392, {wxPrintDialog, getPrintDC, 0}},
+ {2393, {wxPrintDialogData, new_0, 0}},
+ {2394, {wxPrintDialogData, new_1_1, 1}},
+ {2395, {wxPrintDialogData, new_1_0, 1}},
+ {2396, {wxPrintDialogData, destruct, 0}},
+ {2397, {wxPrintDialogData, enableHelp, 1}},
+ {2398, {wxPrintDialogData, enablePageNumbers, 1}},
+ {2399, {wxPrintDialogData, enablePrintToFile, 1}},
+ {2400, {wxPrintDialogData, enableSelection, 1}},
+ {2401, {wxPrintDialogData, getAllPages, 0}},
+ {2402, {wxPrintDialogData, getCollate, 0}},
+ {2403, {wxPrintDialogData, getFromPage, 0}},
+ {2404, {wxPrintDialogData, getMaxPage, 0}},
+ {2405, {wxPrintDialogData, getMinPage, 0}},
+ {2406, {wxPrintDialogData, getNoCopies, 0}},
+ {2407, {wxPrintDialogData, getPrintData, 0}},
+ {2408, {wxPrintDialogData, getPrintToFile, 0}},
+ {2409, {wxPrintDialogData, getSelection, 0}},
+ {2410, {wxPrintDialogData, getToPage, 0}},
+ {2411, {wxPrintDialogData, isOk, 0}},
+ {2412, {wxPrintDialogData, setCollate, 1}},
+ {2413, {wxPrintDialogData, setFromPage, 1}},
+ {2414, {wxPrintDialogData, setMaxPage, 1}},
+ {2415, {wxPrintDialogData, setMinPage, 1}},
+ {2416, {wxPrintDialogData, setNoCopies, 1}},
+ {2417, {wxPrintDialogData, setPrintData, 1}},
+ {2418, {wxPrintDialogData, setPrintToFile, 1}},
+ {2419, {wxPrintDialogData, setSelection, 1}},
+ {2420, {wxPrintDialogData, setToPage, 1}},
+ {2421, {wxPrintData, new_0, 0}},
+ {2422, {wxPrintData, new_1, 1}},
+ {2423, {wxPrintData, destruct, 0}},
+ {2424, {wxPrintData, getCollate, 0}},
+ {2425, {wxPrintData, getBin, 0}},
+ {2426, {wxPrintData, getColour, 0}},
+ {2427, {wxPrintData, getDuplex, 0}},
+ {2428, {wxPrintData, getNoCopies, 0}},
+ {2429, {wxPrintData, getOrientation, 0}},
+ {2430, {wxPrintData, getPaperId, 0}},
+ {2431, {wxPrintData, getPrinterName, 0}},
+ {2432, {wxPrintData, getQuality, 0}},
+ {2433, {wxPrintData, isOk, 0}},
+ {2434, {wxPrintData, setBin, 1}},
+ {2435, {wxPrintData, setCollate, 1}},
+ {2436, {wxPrintData, setColour, 1}},
+ {2437, {wxPrintData, setDuplex, 1}},
+ {2438, {wxPrintData, setNoCopies, 1}},
+ {2439, {wxPrintData, setOrientation, 1}},
+ {2440, {wxPrintData, setPaperId, 1}},
+ {2441, {wxPrintData, setPrinterName, 1}},
+ {2442, {wxPrintData, setQuality, 1}},
+ {2445, {wxPrintPreview, new_2, 2}},
+ {2446, {wxPrintPreview, new_3, 3}},
+ {2448, {wxPrintPreview, destruct, 0}},
+ {2449, {wxPrintPreview, getCanvas, 0}},
+ {2450, {wxPrintPreview, getCurrentPage, 0}},
+ {2451, {wxPrintPreview, getFrame, 0}},
+ {2452, {wxPrintPreview, getMaxPage, 0}},
+ {2453, {wxPrintPreview, getMinPage, 0}},
+ {2454, {wxPrintPreview, getPrintout, 0}},
+ {2455, {wxPrintPreview, getPrintoutForPrinting, 0}},
+ {2456, {wxPrintPreview, isOk, 0}},
+ {2457, {wxPrintPreview, paintPage, 2}},
+ {2458, {wxPrintPreview, print, 1}},
+ {2459, {wxPrintPreview, renderPage, 1}},
+ {2460, {wxPrintPreview, setCanvas, 1}},
+ {2461, {wxPrintPreview, setCurrentPage, 1}},
+ {2462, {wxPrintPreview, setFrame, 1}},
+ {2463, {wxPrintPreview, setPrintout, 1}},
+ {2464, {wxPrintPreview, setZoom, 1}},
+ {2465, {wxPreviewFrame, new, 3}},
+ {2466, {wxPreviewFrame, destruct, 0}},
+ {2467, {wxPreviewFrame, createControlBar, 0}},
+ {2468, {wxPreviewFrame, createCanvas, 0}},
+ {2469, {wxPreviewFrame, initialize, 0}},
+ {2470, {wxPreviewFrame, onCloseWindow, 1}},
+ {2471, {wxPreviewControlBar, new, 4}},
+ {2472, {wxPreviewControlBar, destruct, 0}},
+ {2473, {wxPreviewControlBar, createButtons, 0}},
+ {2474, {wxPreviewControlBar, getPrintPreview, 0}},
+ {2475, {wxPreviewControlBar, getZoomControl, 0}},
+ {2476, {wxPreviewControlBar, setZoomControl, 1}},
+ {2478, {wxPrinter, new, 1}},
+ {2479, {wxPrinter, createAbortWindow, 2}},
+ {2480, {wxPrinter, getAbort, 0}},
+ {2481, {wxPrinter, getLastError, 0}},
+ {2482, {wxPrinter, getPrintDialogData, 0}},
+ {2483, {wxPrinter, print, 3}},
+ {2484, {wxPrinter, printDialog, 1}},
+ {2485, {wxPrinter, reportError, 3}},
+ {2486, {wxPrinter, setup, 1}},
+ {2487, {wxPrinter, 'Destroy', undefined}},
+ {2488, {wxXmlResource, new_1, 1}},
+ {2489, {wxXmlResource, new_2, 2}},
+ {2490, {wxXmlResource, destruct, 0}},
+ {2491, {wxXmlResource, attachUnknownControl, 3}},
+ {2492, {wxXmlResource, clearHandlers, 0}},
+ {2493, {wxXmlResource, compareVersion, 4}},
+ {2494, {wxXmlResource, get, 0}},
+ {2495, {wxXmlResource, getFlags, 0}},
+ {2496, {wxXmlResource, getVersion, 0}},
+ {2497, {wxXmlResource, getXRCID, 2}},
+ {2498, {wxXmlResource, initAllHandlers, 0}},
+ {2499, {wxXmlResource, load, 1}},
+ {2500, {wxXmlResource, loadBitmap, 1}},
+ {2501, {wxXmlResource, loadDialog_2, 2}},
+ {2502, {wxXmlResource, loadDialog_3, 3}},
+ {2503, {wxXmlResource, loadFrame_2, 2}},
+ {2504, {wxXmlResource, loadFrame_3, 3}},
+ {2505, {wxXmlResource, loadIcon, 1}},
+ {2506, {wxXmlResource, loadMenu, 1}},
+ {2507, {wxXmlResource, loadMenuBar_2, 2}},
+ {2508, {wxXmlResource, loadMenuBar_1, 1}},
+ {2509, {wxXmlResource, loadPanel_2, 2}},
+ {2510, {wxXmlResource, loadPanel_3, 3}},
+ {2511, {wxXmlResource, loadToolBar, 2}},
+ {2512, {wxXmlResource, set, 1}},
+ {2513, {wxXmlResource, setFlags, 1}},
+ {2514, {wxXmlResource, unload, 1}},
+ {2515, {wxXmlResource, xrcctrl, 3}},
+ {2516, {wxHtmlEasyPrinting, new, 1}},
+ {2517, {wxHtmlEasyPrinting, destruct, 0}},
+ {2518, {wxHtmlEasyPrinting, getPrintData, 0}},
+ {2519, {wxHtmlEasyPrinting, getPageSetupData, 0}},
+ {2520, {wxHtmlEasyPrinting, previewFile, 1}},
+ {2521, {wxHtmlEasyPrinting, previewText, 2}},
+ {2522, {wxHtmlEasyPrinting, printFile, 1}},
+ {2523, {wxHtmlEasyPrinting, printText, 2}},
+ {2524, {wxHtmlEasyPrinting, pageSetup, 0}},
+ {2525, {wxHtmlEasyPrinting, setFonts, 3}},
+ {2526, {wxHtmlEasyPrinting, setHeader, 2}},
+ {2527, {wxHtmlEasyPrinting, setFooter, 2}},
+ {2529, {wxGLCanvas, new_2, 2}},
+ {2530, {wxGLCanvas, new_3_1, 3}},
+ {2531, {wxGLCanvas, new_3_0, 3}},
+ {2532, {wxGLCanvas, getContext, 0}},
+ {2534, {wxGLCanvas, setCurrent, 0}},
+ {2535, {wxGLCanvas, swapBuffers, 0}},
+ {2536, {wxGLCanvas, 'Destroy', undefined}},
+ {2537, {wxAuiManager, new, 1}},
+ {2538, {wxAuiManager, destruct, 0}},
+ {2539, {wxAuiManager, addPane_2_1, 2}},
+ {2540, {wxAuiManager, addPane_3, 3}},
+ {2541, {wxAuiManager, addPane_2_0, 2}},
+ {2542, {wxAuiManager, detachPane, 1}},
+ {2543, {wxAuiManager, getAllPanes, 0}},
+ {2544, {wxAuiManager, getArtProvider, 0}},
+ {2545, {wxAuiManager, getDockSizeConstraint, 2}},
+ {2546, {wxAuiManager, getFlags, 0}},
+ {2547, {wxAuiManager, getManagedWindow, 0}},
+ {2548, {wxAuiManager, getManager, 1}},
+ {2549, {wxAuiManager, getPane_1_1, 1}},
+ {2550, {wxAuiManager, getPane_1_0, 1}},
+ {2551, {wxAuiManager, hideHint, 0}},
+ {2552, {wxAuiManager, insertPane, 3}},
+ {2553, {wxAuiManager, loadPaneInfo, 2}},
+ {2554, {wxAuiManager, loadPerspective, 2}},
+ {2555, {wxAuiManager, savePaneInfo, 1}},
+ {2556, {wxAuiManager, savePerspective, 0}},
+ {2557, {wxAuiManager, setArtProvider, 1}},
+ {2558, {wxAuiManager, setDockSizeConstraint, 2}},
+ {2559, {wxAuiManager, setFlags, 1}},
+ {2560, {wxAuiManager, setManagedWindow, 1}},
+ {2561, {wxAuiManager, showHint, 1}},
+ {2562, {wxAuiManager, unInit, 0}},
+ {2563, {wxAuiManager, update, 0}},
+ {2564, {wxAuiPaneInfo, new_0, 0}},
+ {2565, {wxAuiPaneInfo, new_1, 1}},
+ {2566, {wxAuiPaneInfo, destruct, 0}},
+ {2567, {wxAuiPaneInfo, bestSize_1, 1}},
+ {2568, {wxAuiPaneInfo, bestSize_2, 2}},
+ {2569, {wxAuiPaneInfo, bottom, 0}},
+ {2570, {wxAuiPaneInfo, bottomDockable, 1}},
+ {2571, {wxAuiPaneInfo, caption, 1}},
+ {2572, {wxAuiPaneInfo, captionVisible, 1}},
+ {2573, {wxAuiPaneInfo, centre, 0}},
+ {2574, {wxAuiPaneInfo, centrePane, 0}},
+ {2575, {wxAuiPaneInfo, closeButton, 1}},
+ {2576, {wxAuiPaneInfo, defaultPane, 0}},
+ {2577, {wxAuiPaneInfo, destroyOnClose, 1}},
+ {2578, {wxAuiPaneInfo, direction, 1}},
+ {2579, {wxAuiPaneInfo, dock, 0}},
+ {2580, {wxAuiPaneInfo, dockable, 1}},
+ {2581, {wxAuiPaneInfo, fixed, 0}},
+ {2582, {wxAuiPaneInfo, float, 0}},
+ {2583, {wxAuiPaneInfo, floatable, 1}},
+ {2584, {wxAuiPaneInfo, floatingPosition_1, 1}},
+ {2585, {wxAuiPaneInfo, floatingPosition_2, 2}},
+ {2586, {wxAuiPaneInfo, floatingSize_1, 1}},
+ {2587, {wxAuiPaneInfo, floatingSize_2, 2}},
+ {2588, {wxAuiPaneInfo, gripper, 1}},
+ {2589, {wxAuiPaneInfo, gripperTop, 1}},
+ {2590, {wxAuiPaneInfo, hasBorder, 0}},
+ {2591, {wxAuiPaneInfo, hasCaption, 0}},
+ {2592, {wxAuiPaneInfo, hasCloseButton, 0}},
+ {2593, {wxAuiPaneInfo, hasFlag, 1}},
+ {2594, {wxAuiPaneInfo, hasGripper, 0}},
+ {2595, {wxAuiPaneInfo, hasGripperTop, 0}},
+ {2596, {wxAuiPaneInfo, hasMaximizeButton, 0}},
+ {2597, {wxAuiPaneInfo, hasMinimizeButton, 0}},
+ {2598, {wxAuiPaneInfo, hasPinButton, 0}},
+ {2599, {wxAuiPaneInfo, hide, 0}},
+ {2600, {wxAuiPaneInfo, isBottomDockable, 0}},
+ {2601, {wxAuiPaneInfo, isDocked, 0}},
+ {2602, {wxAuiPaneInfo, isFixed, 0}},
+ {2603, {wxAuiPaneInfo, isFloatable, 0}},
+ {2604, {wxAuiPaneInfo, isFloating, 0}},
+ {2605, {wxAuiPaneInfo, isLeftDockable, 0}},
+ {2606, {wxAuiPaneInfo, isMovable, 0}},
+ {2607, {wxAuiPaneInfo, isOk, 0}},
+ {2608, {wxAuiPaneInfo, isResizable, 0}},
+ {2609, {wxAuiPaneInfo, isRightDockable, 0}},
+ {2610, {wxAuiPaneInfo, isShown, 0}},
+ {2611, {wxAuiPaneInfo, isToolbar, 0}},
+ {2612, {wxAuiPaneInfo, isTopDockable, 0}},
+ {2613, {wxAuiPaneInfo, layer, 1}},
+ {2614, {wxAuiPaneInfo, left, 0}},
+ {2615, {wxAuiPaneInfo, leftDockable, 1}},
+ {2616, {wxAuiPaneInfo, maxSize_1, 1}},
+ {2617, {wxAuiPaneInfo, maxSize_2, 2}},
+ {2618, {wxAuiPaneInfo, maximizeButton, 1}},
+ {2619, {wxAuiPaneInfo, minSize_1, 1}},
+ {2620, {wxAuiPaneInfo, minSize_2, 2}},
+ {2621, {wxAuiPaneInfo, minimizeButton, 1}},
+ {2622, {wxAuiPaneInfo, movable, 1}},
+ {2623, {wxAuiPaneInfo, name, 1}},
+ {2624, {wxAuiPaneInfo, paneBorder, 1}},
+ {2625, {wxAuiPaneInfo, pinButton, 1}},
+ {2626, {wxAuiPaneInfo, position, 1}},
+ {2627, {wxAuiPaneInfo, resizable, 1}},
+ {2628, {wxAuiPaneInfo, right, 0}},
+ {2629, {wxAuiPaneInfo, rightDockable, 1}},
+ {2630, {wxAuiPaneInfo, row, 1}},
+ {2631, {wxAuiPaneInfo, safeSet, 1}},
+ {2632, {wxAuiPaneInfo, setFlag, 2}},
+ {2633, {wxAuiPaneInfo, show, 1}},
+ {2634, {wxAuiPaneInfo, toolbarPane, 0}},
+ {2635, {wxAuiPaneInfo, top, 0}},
+ {2636, {wxAuiPaneInfo, topDockable, 1}},
+ {2637, {wxAuiPaneInfo, window, 1}},
+ {2638, {wxAuiNotebook, new_0, 0}},
+ {2639, {wxAuiNotebook, new_2, 2}},
+ {2640, {wxAuiNotebook, addPage, 3}},
+ {2641, {wxAuiNotebook, create, 2}},
+ {2642, {wxAuiNotebook, deletePage, 1}},
+ {2643, {wxAuiNotebook, getArtProvider, 0}},
+ {2644, {wxAuiNotebook, getPage, 1}},
+ {2645, {wxAuiNotebook, getPageBitmap, 1}},
+ {2646, {wxAuiNotebook, getPageCount, 0}},
+ {2647, {wxAuiNotebook, getPageIndex, 1}},
+ {2648, {wxAuiNotebook, getPageText, 1}},
+ {2649, {wxAuiNotebook, getSelection, 0}},
+ {2650, {wxAuiNotebook, insertPage, 4}},
+ {2651, {wxAuiNotebook, removePage, 1}},
+ {2652, {wxAuiNotebook, setArtProvider, 1}},
+ {2653, {wxAuiNotebook, setFont, 1}},
+ {2654, {wxAuiNotebook, setPageBitmap, 2}},
+ {2655, {wxAuiNotebook, setPageText, 2}},
+ {2656, {wxAuiNotebook, setSelection, 1}},
+ {2657, {wxAuiNotebook, setTabCtrlHeight, 1}},
+ {2658, {wxAuiNotebook, setUniformBitmapSize, 1}},
+ {2659, {wxAuiNotebook, 'Destroy', undefined}},
+ {2660, {wxMDIParentFrame, new_0, 0}},
+ {2661, {wxMDIParentFrame, new_4, 4}},
+ {2662, {wxMDIParentFrame, destruct, 0}},
+ {2663, {wxMDIParentFrame, activateNext, 0}},
+ {2664, {wxMDIParentFrame, activatePrevious, 0}},
+ {2665, {wxMDIParentFrame, arrangeIcons, 0}},
+ {2666, {wxMDIParentFrame, cascade, 0}},
+ {2667, {wxMDIParentFrame, create, 4}},
+ {2668, {wxMDIParentFrame, getActiveChild, 0}},
+ {2669, {wxMDIParentFrame, getClientWindow, 0}},
+ {2670, {wxMDIParentFrame, tile, 1}},
+ {2671, {wxMDIChildFrame, new_0, 0}},
+ {2672, {wxMDIChildFrame, new_4, 4}},
+ {2673, {wxMDIChildFrame, destruct, 0}},
+ {2674, {wxMDIChildFrame, activate, 0}},
+ {2675, {wxMDIChildFrame, create, 4}},
+ {2676, {wxMDIChildFrame, maximize, 1}},
+ {2677, {wxMDIChildFrame, restore, 0}},
+ {2678, {wxMDIClientWindow, new_0, 0}},
+ {2679, {wxMDIClientWindow, new_2, 2}},
+ {2680, {wxMDIClientWindow, destruct, 0}},
+ {2681, {wxMDIClientWindow, createClient, 2}},
+ {2682, {wxLayoutAlgorithm, new, 0}},
+ {2683, {wxLayoutAlgorithm, layoutFrame, 2}},
+ {2684, {wxLayoutAlgorithm, layoutMDIFrame, 2}},
+ {2685, {wxLayoutAlgorithm, layoutWindow, 2}},
+ {2686, {wxLayoutAlgorithm, 'Destroy', undefined}},
+ {2687, {wxEvent, getId, 0}},
+ {2688, {wxEvent, getSkipped, 0}},
+ {2689, {wxEvent, getTimestamp, 0}},
+ {2690, {wxEvent, isCommandEvent, 0}},
+ {2691, {wxEvent, resumePropagation, 1}},
+ {2692, {wxEvent, shouldPropagate, 0}},
+ {2693, {wxEvent, skip, 1}},
+ {2694, {wxEvent, stopPropagation, 0}},
+ {2695, {wxCommandEvent, getClientData, 0}},
+ {2696, {wxCommandEvent, getExtraLong, 0}},
+ {2697, {wxCommandEvent, getInt, 0}},
+ {2698, {wxCommandEvent, getSelection, 0}},
+ {2699, {wxCommandEvent, getString, 0}},
+ {2700, {wxCommandEvent, isChecked, 0}},
+ {2701, {wxCommandEvent, isSelection, 0}},
+ {2702, {wxCommandEvent, setInt, 1}},
+ {2703, {wxCommandEvent, setString, 1}},
+ {2704, {wxScrollEvent, getOrientation, 0}},
+ {2705, {wxScrollEvent, getPosition, 0}},
+ {2706, {wxScrollWinEvent, getOrientation, 0}},
+ {2707, {wxScrollWinEvent, getPosition, 0}},
+ {2708, {wxMouseEvent, altDown, 0}},
+ {2709, {wxMouseEvent, button, 1}},
+ {2710, {wxMouseEvent, buttonDClick, 1}},
+ {2711, {wxMouseEvent, buttonDown, 1}},
+ {2712, {wxMouseEvent, buttonUp, 1}},
+ {2713, {wxMouseEvent, cmdDown, 0}},
+ {2714, {wxMouseEvent, controlDown, 0}},
+ {2715, {wxMouseEvent, dragging, 0}},
+ {2716, {wxMouseEvent, entering, 0}},
+ {2717, {wxMouseEvent, getButton, 0}},
+ {2720, {wxMouseEvent, getPosition, 0}},
+ {2721, {wxMouseEvent, getLogicalPosition, 1}},
+ {2722, {wxMouseEvent, getLinesPerAction, 0}},
+ {2723, {wxMouseEvent, getWheelRotation, 0}},
+ {2724, {wxMouseEvent, getWheelDelta, 0}},
+ {2725, {wxMouseEvent, getX, 0}},
+ {2726, {wxMouseEvent, getY, 0}},
+ {2727, {wxMouseEvent, isButton, 0}},
+ {2728, {wxMouseEvent, isPageScroll, 0}},
+ {2729, {wxMouseEvent, leaving, 0}},
+ {2730, {wxMouseEvent, leftDClick, 0}},
+ {2731, {wxMouseEvent, leftDown, 0}},
+ {2732, {wxMouseEvent, leftIsDown, 0}},
+ {2733, {wxMouseEvent, leftUp, 0}},
+ {2734, {wxMouseEvent, metaDown, 0}},
+ {2735, {wxMouseEvent, middleDClick, 0}},
+ {2736, {wxMouseEvent, middleDown, 0}},
+ {2737, {wxMouseEvent, middleIsDown, 0}},
+ {2738, {wxMouseEvent, middleUp, 0}},
+ {2739, {wxMouseEvent, moving, 0}},
+ {2740, {wxMouseEvent, rightDClick, 0}},
+ {2741, {wxMouseEvent, rightDown, 0}},
+ {2742, {wxMouseEvent, rightIsDown, 0}},
+ {2743, {wxMouseEvent, rightUp, 0}},
+ {2744, {wxMouseEvent, shiftDown, 0}},
+ {2745, {wxSetCursorEvent, getCursor, 0}},
+ {2746, {wxSetCursorEvent, getX, 0}},
+ {2747, {wxSetCursorEvent, getY, 0}},
+ {2748, {wxSetCursorEvent, hasCursor, 0}},
+ {2749, {wxSetCursorEvent, setCursor, 1}},
+ {2750, {wxKeyEvent, altDown, 0}},
+ {2751, {wxKeyEvent, cmdDown, 0}},
+ {2752, {wxKeyEvent, controlDown, 0}},
+ {2753, {wxKeyEvent, getKeyCode, 0}},
+ {2754, {wxKeyEvent, getModifiers, 0}},
+ {2757, {wxKeyEvent, getPosition, 0}},
+ {2758, {wxKeyEvent, getRawKeyCode, 0}},
+ {2759, {wxKeyEvent, getRawKeyFlags, 0}},
+ {2760, {wxKeyEvent, getUnicodeKey, 0}},
+ {2761, {wxKeyEvent, getX, 0}},
+ {2762, {wxKeyEvent, getY, 0}},
+ {2763, {wxKeyEvent, hasModifiers, 0}},
+ {2764, {wxKeyEvent, metaDown, 0}},
+ {2765, {wxKeyEvent, shiftDown, 0}},
+ {2766, {wxSizeEvent, getSize, 0}},
+ {2767, {wxMoveEvent, getPosition, 0}},
+ {2768, {wxEraseEvent, getDC, 0}},
+ {2769, {wxFocusEvent, getWindow, 0}},
+ {2770, {wxChildFocusEvent, getWindow, 0}},
+ {2771, {wxMenuEvent, getMenu, 0}},
+ {2772, {wxMenuEvent, getMenuId, 0}},
+ {2773, {wxMenuEvent, isPopup, 0}},
+ {2774, {wxCloseEvent, canVeto, 0}},
+ {2775, {wxCloseEvent, getLoggingOff, 0}},
+ {2776, {wxCloseEvent, setCanVeto, 1}},
+ {2777, {wxCloseEvent, setLoggingOff, 1}},
+ {2778, {wxCloseEvent, veto, 1}},
+ {2779, {wxShowEvent, setShow, 1}},
+ {2780, {wxShowEvent, getShow, 0}},
+ {2781, {wxIconizeEvent, iconized, 0}},
+ {2782, {wxJoystickEvent, buttonDown, 1}},
+ {2783, {wxJoystickEvent, buttonIsDown, 1}},
+ {2784, {wxJoystickEvent, buttonUp, 1}},
+ {2785, {wxJoystickEvent, getButtonChange, 0}},
+ {2786, {wxJoystickEvent, getButtonState, 0}},
+ {2787, {wxJoystickEvent, getJoystick, 0}},
+ {2788, {wxJoystickEvent, getPosition, 0}},
+ {2789, {wxJoystickEvent, getZPosition, 0}},
+ {2790, {wxJoystickEvent, isButton, 0}},
+ {2791, {wxJoystickEvent, isMove, 0}},
+ {2792, {wxJoystickEvent, isZMove, 0}},
+ {2793, {wxUpdateUIEvent, canUpdate, 1}},
+ {2794, {wxUpdateUIEvent, check, 1}},
+ {2795, {wxUpdateUIEvent, enable, 1}},
+ {2796, {wxUpdateUIEvent, show, 1}},
+ {2797, {wxUpdateUIEvent, getChecked, 0}},
+ {2798, {wxUpdateUIEvent, getEnabled, 0}},
+ {2799, {wxUpdateUIEvent, getShown, 0}},
+ {2800, {wxUpdateUIEvent, getSetChecked, 0}},
+ {2801, {wxUpdateUIEvent, getSetEnabled, 0}},
+ {2802, {wxUpdateUIEvent, getSetShown, 0}},
+ {2803, {wxUpdateUIEvent, getSetText, 0}},
+ {2804, {wxUpdateUIEvent, getText, 0}},
+ {2805, {wxUpdateUIEvent, getMode, 0}},
+ {2806, {wxUpdateUIEvent, getUpdateInterval, 0}},
+ {2807, {wxUpdateUIEvent, resetUpdateTime, 0}},
+ {2808, {wxUpdateUIEvent, setMode, 1}},
+ {2809, {wxUpdateUIEvent, setText, 1}},
+ {2810, {wxUpdateUIEvent, setUpdateInterval, 1}},
+ {2811, {wxMouseCaptureChangedEvent, getCapturedWindow, 0}},
+ {2812, {wxPaletteChangedEvent, setChangedWindow, 1}},
+ {2813, {wxPaletteChangedEvent, getChangedWindow, 0}},
+ {2814, {wxQueryNewPaletteEvent, setPaletteRealized, 1}},
+ {2815, {wxQueryNewPaletteEvent, getPaletteRealized, 0}},
+ {2816, {wxNavigationKeyEvent, getDirection, 0}},
+ {2817, {wxNavigationKeyEvent, setDirection, 1}},
+ {2818, {wxNavigationKeyEvent, isWindowChange, 0}},
+ {2819, {wxNavigationKeyEvent, setWindowChange, 1}},
+ {2820, {wxNavigationKeyEvent, isFromTab, 0}},
+ {2821, {wxNavigationKeyEvent, setFromTab, 1}},
+ {2822, {wxNavigationKeyEvent, getCurrentFocus, 0}},
+ {2823, {wxNavigationKeyEvent, setCurrentFocus, 1}},
+ {2824, {wxHelpEvent, getOrigin, 0}},
+ {2825, {wxHelpEvent, getPosition, 0}},
+ {2826, {wxHelpEvent, setOrigin, 1}},
+ {2827, {wxHelpEvent, setPosition, 1}},
+ {2828, {wxContextMenuEvent, getPosition, 0}},
+ {2829, {wxContextMenuEvent, setPosition, 1}},
+ {2830, {wxIdleEvent, canSend, 1}},
+ {2831, {wxIdleEvent, getMode, 0}},
+ {2832, {wxIdleEvent, requestMore, 1}},
+ {2833, {wxIdleEvent, moreRequested, 0}},
+ {2834, {wxIdleEvent, setMode, 1}},
+ {2835, {wxGridEvent, altDown, 0}},
+ {2836, {wxGridEvent, controlDown, 0}},
+ {2837, {wxGridEvent, getCol, 0}},
+ {2838, {wxGridEvent, getPosition, 0}},
+ {2839, {wxGridEvent, getRow, 0}},
+ {2840, {wxGridEvent, metaDown, 0}},
+ {2841, {wxGridEvent, selecting, 0}},
+ {2842, {wxGridEvent, shiftDown, 0}},
+ {2843, {wxNotifyEvent, allow, 0}},
+ {2844, {wxNotifyEvent, isAllowed, 0}},
+ {2845, {wxNotifyEvent, veto, 0}},
+ {2846, {wxSashEvent, getEdge, 0}},
+ {2847, {wxSashEvent, getDragRect, 0}},
+ {2848, {wxSashEvent, getDragStatus, 0}},
+ {2849, {wxListEvent, getCacheFrom, 0}},
+ {2850, {wxListEvent, getCacheTo, 0}},
+ {2851, {wxListEvent, getKeyCode, 0}},
+ {2852, {wxListEvent, getIndex, 0}},
+ {2853, {wxListEvent, getColumn, 0}},
+ {2854, {wxListEvent, getPoint, 0}},
+ {2855, {wxListEvent, getLabel, 0}},
+ {2856, {wxListEvent, getText, 0}},
+ {2857, {wxListEvent, getImage, 0}},
+ {2858, {wxListEvent, getData, 0}},
+ {2859, {wxListEvent, getMask, 0}},
+ {2860, {wxListEvent, getItem, 0}},
+ {2861, {wxListEvent, isEditCancelled, 0}},
+ {2862, {wxDateEvent, getDate, 0}},
+ {2863, {wxCalendarEvent, getWeekDay, 0}},
+ {2864, {wxFileDirPickerEvent, getPath, 0}},
+ {2865, {wxColourPickerEvent, getColour, 0}},
+ {2866, {wxFontPickerEvent, getFont, 0}},
+ {2867, {wxStyledTextEvent, getPosition, 0}},
+ {2868, {wxStyledTextEvent, getKey, 0}},
+ {2869, {wxStyledTextEvent, getModifiers, 0}},
+ {2870, {wxStyledTextEvent, getModificationType, 0}},
+ {2871, {wxStyledTextEvent, getText, 0}},
+ {2872, {wxStyledTextEvent, getLength, 0}},
+ {2873, {wxStyledTextEvent, getLinesAdded, 0}},
+ {2874, {wxStyledTextEvent, getLine, 0}},
+ {2875, {wxStyledTextEvent, getFoldLevelNow, 0}},
+ {2876, {wxStyledTextEvent, getFoldLevelPrev, 0}},
+ {2877, {wxStyledTextEvent, getMargin, 0}},
+ {2878, {wxStyledTextEvent, getMessage, 0}},
+ {2879, {wxStyledTextEvent, getWParam, 0}},
+ {2880, {wxStyledTextEvent, getLParam, 0}},
+ {2881, {wxStyledTextEvent, getListType, 0}},
+ {2882, {wxStyledTextEvent, getX, 0}},
+ {2883, {wxStyledTextEvent, getY, 0}},
+ {2884, {wxStyledTextEvent, getDragText, 0}},
+ {2885, {wxStyledTextEvent, getDragAllowMove, 0}},
+ {2886, {wxStyledTextEvent, getDragResult, 0}},
+ {2887, {wxStyledTextEvent, getShift, 0}},
+ {2888, {wxStyledTextEvent, getControl, 0}},
+ {2889, {wxStyledTextEvent, getAlt, 0}},
+ {2890, {utils, getKeyState, 1}},
+ {2891, {utils, getMousePosition, 2}},
+ {2892, {utils, getMouseState, 0}},
+ {2893, {utils, setDetectableAutoRepeat, 1}},
+ {2894, {utils, bell, 0}},
+ {2895, {utils, findMenuItemId, 3}},
+ {2896, {utils, genericFindWindowAtPoint, 1}},
+ {2897, {utils, findWindowAtPoint, 1}},
+ {2898, {utils, beginBusyCursor, 1}},
+ {2899, {utils, endBusyCursor, 0}},
+ {2900, {utils, isBusy, 0}},
+ {2901, {utils, shutdown, 1}},
+ {2902, {utils, shell, 1}},
+ {2903, {utils, launchDefaultBrowser, 2}},
+ {2904, {utils, getEmailAddress, 0}},
+ {2905, {utils, getUserId, 0}},
+ {2906, {utils, getHomeDir, 0}},
+ {2907, {utils, newId, 0}},
+ {2908, {utils, registerId, 1}},
+ {2909, {utils, getCurrentId, 0}},
+ {2910, {utils, getOsDescription, 0}},
+ {2911, {utils, isPlatformLittleEndian, 0}},
+ {2912, {utils, isPlatform64Bit, 0}},
+ {2913, {wxPrintout, new, 1}},
+ {2914, {wxPrintout, destruct, 0}},
+ {2915, {wxPrintout, getDC, 0}},
+ {2916, {wxPrintout, getPageSizeMM, 2}},
+ {2917, {wxPrintout, getPageSizePixels, 2}},
+ {2918, {wxPrintout, getPaperRectPixels, 0}},
+ {2919, {wxPrintout, getPPIPrinter, 2}},
+ {2920, {wxPrintout, getPPIScreen, 2}},
+ {2921, {wxPrintout, getTitle, 0}},
+ {2922, {wxPrintout, isPreview, 0}},
+ {2923, {wxPrintout, fitThisSizeToPaper, 1}},
+ {2924, {wxPrintout, fitThisSizeToPage, 1}},
+ {2925, {wxPrintout, fitThisSizeToPageMargins, 2}},
+ {2926, {wxPrintout, mapScreenSizeToPaper, 0}},
+ {2927, {wxPrintout, mapScreenSizeToPage, 0}},
+ {2928, {wxPrintout, mapScreenSizeToPageMargins, 1}},
+ {2929, {wxPrintout, mapScreenSizeToDevice, 0}},
+ {2930, {wxPrintout, getLogicalPaperRect, 0}},
+ {2931, {wxPrintout, getLogicalPageRect, 0}},
+ {2932, {wxPrintout, getLogicalPageMarginsRect, 1}},
+ {2933, {wxPrintout, setLogicalOrigin, 2}},
+ {2934, {wxPrintout, offsetLogicalOrigin, 2}},
+ {2935, {wxStyledTextCtrl, new_2, 2}},
+ {2936, {wxStyledTextCtrl, new_0, 0}},
+ {2937, {wxStyledTextCtrl, destruct, 0}},
+ {2938, {wxStyledTextCtrl, create, 2}},
+ {2939, {wxStyledTextCtrl, addText, 1}},
+ {2940, {wxStyledTextCtrl, addStyledText, 1}},
+ {2941, {wxStyledTextCtrl, insertText, 2}},
+ {2942, {wxStyledTextCtrl, clearAll, 0}},
+ {2943, {wxStyledTextCtrl, clearDocumentStyle, 0}},
+ {2944, {wxStyledTextCtrl, getLength, 0}},
+ {2945, {wxStyledTextCtrl, getCharAt, 1}},
+ {2946, {wxStyledTextCtrl, getCurrentPos, 0}},
+ {2947, {wxStyledTextCtrl, getAnchor, 0}},
+ {2948, {wxStyledTextCtrl, getStyleAt, 1}},
+ {2949, {wxStyledTextCtrl, redo, 0}},
+ {2950, {wxStyledTextCtrl, setUndoCollection, 1}},
+ {2951, {wxStyledTextCtrl, selectAll, 0}},
+ {2952, {wxStyledTextCtrl, setSavePoint, 0}},
+ {2953, {wxStyledTextCtrl, getStyledText, 2}},
+ {2954, {wxStyledTextCtrl, canRedo, 0}},
+ {2955, {wxStyledTextCtrl, markerLineFromHandle, 1}},
+ {2956, {wxStyledTextCtrl, markerDeleteHandle, 1}},
+ {2957, {wxStyledTextCtrl, getUndoCollection, 0}},
+ {2958, {wxStyledTextCtrl, getViewWhiteSpace, 0}},
+ {2959, {wxStyledTextCtrl, setViewWhiteSpace, 1}},
+ {2960, {wxStyledTextCtrl, positionFromPoint, 1}},
+ {2961, {wxStyledTextCtrl, positionFromPointClose, 2}},
+ {2962, {wxStyledTextCtrl, gotoLine, 1}},
+ {2963, {wxStyledTextCtrl, gotoPos, 1}},
+ {2964, {wxStyledTextCtrl, setAnchor, 1}},
+ {2965, {wxStyledTextCtrl, getCurLine, 1}},
+ {2966, {wxStyledTextCtrl, getEndStyled, 0}},
+ {2967, {wxStyledTextCtrl, convertEOLs, 1}},
+ {2968, {wxStyledTextCtrl, getEOLMode, 0}},
+ {2969, {wxStyledTextCtrl, setEOLMode, 1}},
+ {2970, {wxStyledTextCtrl, startStyling, 2}},
+ {2971, {wxStyledTextCtrl, setStyling, 2}},
+ {2972, {wxStyledTextCtrl, getBufferedDraw, 0}},
+ {2973, {wxStyledTextCtrl, setBufferedDraw, 1}},
+ {2974, {wxStyledTextCtrl, setTabWidth, 1}},
+ {2975, {wxStyledTextCtrl, getTabWidth, 0}},
+ {2976, {wxStyledTextCtrl, setCodePage, 1}},
+ {2977, {wxStyledTextCtrl, markerDefine, 3}},
+ {2978, {wxStyledTextCtrl, markerSetForeground, 2}},
+ {2979, {wxStyledTextCtrl, markerSetBackground, 2}},
+ {2980, {wxStyledTextCtrl, markerAdd, 2}},
+ {2981, {wxStyledTextCtrl, markerDelete, 2}},
+ {2982, {wxStyledTextCtrl, markerDeleteAll, 1}},
+ {2983, {wxStyledTextCtrl, markerGet, 1}},
+ {2984, {wxStyledTextCtrl, markerNext, 2}},
+ {2985, {wxStyledTextCtrl, markerPrevious, 2}},
+ {2986, {wxStyledTextCtrl, markerDefineBitmap, 2}},
+ {2987, {wxStyledTextCtrl, markerAddSet, 2}},
+ {2988, {wxStyledTextCtrl, markerSetAlpha, 2}},
+ {2989, {wxStyledTextCtrl, setMarginType, 2}},
+ {2990, {wxStyledTextCtrl, getMarginType, 1}},
+ {2991, {wxStyledTextCtrl, setMarginWidth, 2}},
+ {2992, {wxStyledTextCtrl, getMarginWidth, 1}},
+ {2993, {wxStyledTextCtrl, setMarginMask, 2}},
+ {2994, {wxStyledTextCtrl, getMarginMask, 1}},
+ {2995, {wxStyledTextCtrl, setMarginSensitive, 2}},
+ {2996, {wxStyledTextCtrl, getMarginSensitive, 1}},
+ {2997, {wxStyledTextCtrl, styleClearAll, 0}},
+ {2998, {wxStyledTextCtrl, styleSetForeground, 2}},
+ {2999, {wxStyledTextCtrl, styleSetBackground, 2}},
+ {3000, {wxStyledTextCtrl, styleSetBold, 2}},
+ {3001, {wxStyledTextCtrl, styleSetItalic, 2}},
+ {3002, {wxStyledTextCtrl, styleSetSize, 2}},
+ {3003, {wxStyledTextCtrl, styleSetFaceName, 2}},
+ {3004, {wxStyledTextCtrl, styleSetEOLFilled, 2}},
+ {3005, {wxStyledTextCtrl, styleResetDefault, 0}},
+ {3006, {wxStyledTextCtrl, styleSetUnderline, 2}},
+ {3007, {wxStyledTextCtrl, styleSetCase, 2}},
+ {3008, {wxStyledTextCtrl, styleSetHotSpot, 2}},
+ {3009, {wxStyledTextCtrl, setSelForeground, 2}},
+ {3010, {wxStyledTextCtrl, setSelBackground, 2}},
+ {3011, {wxStyledTextCtrl, getSelAlpha, 0}},
+ {3012, {wxStyledTextCtrl, setSelAlpha, 1}},
+ {3013, {wxStyledTextCtrl, setCaretForeground, 1}},
+ {3014, {wxStyledTextCtrl, cmdKeyAssign, 3}},
+ {3015, {wxStyledTextCtrl, cmdKeyClear, 2}},
+ {3016, {wxStyledTextCtrl, cmdKeyClearAll, 0}},
+ {3017, {wxStyledTextCtrl, setStyleBytes, 2}},
+ {3018, {wxStyledTextCtrl, styleSetVisible, 2}},
+ {3019, {wxStyledTextCtrl, getCaretPeriod, 0}},
+ {3020, {wxStyledTextCtrl, setCaretPeriod, 1}},
+ {3021, {wxStyledTextCtrl, setWordChars, 1}},
+ {3022, {wxStyledTextCtrl, beginUndoAction, 0}},
+ {3023, {wxStyledTextCtrl, endUndoAction, 0}},
+ {3024, {wxStyledTextCtrl, indicatorSetStyle, 2}},
+ {3025, {wxStyledTextCtrl, indicatorGetStyle, 1}},
+ {3026, {wxStyledTextCtrl, indicatorSetForeground, 2}},
+ {3027, {wxStyledTextCtrl, indicatorGetForeground, 1}},
+ {3028, {wxStyledTextCtrl, setWhitespaceForeground, 2}},
+ {3029, {wxStyledTextCtrl, setWhitespaceBackground, 2}},
+ {3030, {wxStyledTextCtrl, getStyleBits, 0}},
+ {3031, {wxStyledTextCtrl, setLineState, 2}},
+ {3032, {wxStyledTextCtrl, getLineState, 1}},
+ {3033, {wxStyledTextCtrl, getMaxLineState, 0}},
+ {3034, {wxStyledTextCtrl, getCaretLineVisible, 0}},
+ {3035, {wxStyledTextCtrl, setCaretLineVisible, 1}},
+ {3036, {wxStyledTextCtrl, getCaretLineBackground, 0}},
+ {3037, {wxStyledTextCtrl, setCaretLineBackground, 1}},
+ {3038, {wxStyledTextCtrl, autoCompShow, 2}},
+ {3039, {wxStyledTextCtrl, autoCompCancel, 0}},
+ {3040, {wxStyledTextCtrl, autoCompActive, 0}},
+ {3041, {wxStyledTextCtrl, autoCompPosStart, 0}},
+ {3042, {wxStyledTextCtrl, autoCompComplete, 0}},
+ {3043, {wxStyledTextCtrl, autoCompStops, 1}},
+ {3044, {wxStyledTextCtrl, autoCompSetSeparator, 1}},
+ {3045, {wxStyledTextCtrl, autoCompGetSeparator, 0}},
+ {3046, {wxStyledTextCtrl, autoCompSelect, 1}},
+ {3047, {wxStyledTextCtrl, autoCompSetCancelAtStart, 1}},
+ {3048, {wxStyledTextCtrl, autoCompGetCancelAtStart, 0}},
+ {3049, {wxStyledTextCtrl, autoCompSetFillUps, 1}},
+ {3050, {wxStyledTextCtrl, autoCompSetChooseSingle, 1}},
+ {3051, {wxStyledTextCtrl, autoCompGetChooseSingle, 0}},
+ {3052, {wxStyledTextCtrl, autoCompSetIgnoreCase, 1}},
+ {3053, {wxStyledTextCtrl, autoCompGetIgnoreCase, 0}},
+ {3054, {wxStyledTextCtrl, userListShow, 2}},
+ {3055, {wxStyledTextCtrl, autoCompSetAutoHide, 1}},
+ {3056, {wxStyledTextCtrl, autoCompGetAutoHide, 0}},
+ {3057, {wxStyledTextCtrl, autoCompSetDropRestOfWord, 1}},
+ {3058, {wxStyledTextCtrl, autoCompGetDropRestOfWord, 0}},
+ {3059, {wxStyledTextCtrl, registerImage, 2}},
+ {3060, {wxStyledTextCtrl, clearRegisteredImages, 0}},
+ {3061, {wxStyledTextCtrl, autoCompGetTypeSeparator, 0}},
+ {3062, {wxStyledTextCtrl, autoCompSetTypeSeparator, 1}},
+ {3063, {wxStyledTextCtrl, autoCompSetMaxWidth, 1}},
+ {3064, {wxStyledTextCtrl, autoCompGetMaxWidth, 0}},
+ {3065, {wxStyledTextCtrl, autoCompSetMaxHeight, 1}},
+ {3066, {wxStyledTextCtrl, autoCompGetMaxHeight, 0}},
+ {3067, {wxStyledTextCtrl, setIndent, 1}},
+ {3068, {wxStyledTextCtrl, getIndent, 0}},
+ {3069, {wxStyledTextCtrl, setUseTabs, 1}},
+ {3070, {wxStyledTextCtrl, getUseTabs, 0}},
+ {3071, {wxStyledTextCtrl, setLineIndentation, 2}},
+ {3072, {wxStyledTextCtrl, getLineIndentation, 1}},
+ {3073, {wxStyledTextCtrl, getLineIndentPosition, 1}},
+ {3074, {wxStyledTextCtrl, getColumn, 1}},
+ {3075, {wxStyledTextCtrl, setUseHorizontalScrollBar, 1}},
+ {3076, {wxStyledTextCtrl, getUseHorizontalScrollBar, 0}},
+ {3077, {wxStyledTextCtrl, setIndentationGuides, 1}},
+ {3078, {wxStyledTextCtrl, getIndentationGuides, 0}},
+ {3079, {wxStyledTextCtrl, setHighlightGuide, 1}},
+ {3080, {wxStyledTextCtrl, getHighlightGuide, 0}},
+ {3081, {wxStyledTextCtrl, getLineEndPosition, 1}},
+ {3082, {wxStyledTextCtrl, getCodePage, 0}},
+ {3083, {wxStyledTextCtrl, getCaretForeground, 0}},
+ {3084, {wxStyledTextCtrl, getReadOnly, 0}},
+ {3085, {wxStyledTextCtrl, setCurrentPos, 1}},
+ {3086, {wxStyledTextCtrl, setSelectionStart, 1}},
+ {3087, {wxStyledTextCtrl, getSelectionStart, 0}},
+ {3088, {wxStyledTextCtrl, setSelectionEnd, 1}},
+ {3089, {wxStyledTextCtrl, getSelectionEnd, 0}},
+ {3090, {wxStyledTextCtrl, setPrintMagnification, 1}},
+ {3091, {wxStyledTextCtrl, getPrintMagnification, 0}},
+ {3092, {wxStyledTextCtrl, setPrintColourMode, 1}},
+ {3093, {wxStyledTextCtrl, getPrintColourMode, 0}},
+ {3094, {wxStyledTextCtrl, findText, 4}},
+ {3095, {wxStyledTextCtrl, formatRange, 7}},
+ {3096, {wxStyledTextCtrl, getFirstVisibleLine, 0}},
+ {3097, {wxStyledTextCtrl, getLine, 1}},
+ {3098, {wxStyledTextCtrl, getLineCount, 0}},
+ {3099, {wxStyledTextCtrl, setMarginLeft, 1}},
+ {3100, {wxStyledTextCtrl, getMarginLeft, 0}},
+ {3101, {wxStyledTextCtrl, setMarginRight, 1}},
+ {3102, {wxStyledTextCtrl, getMarginRight, 0}},
+ {3103, {wxStyledTextCtrl, getModify, 0}},
+ {3104, {wxStyledTextCtrl, setSelection, 2}},
+ {3105, {wxStyledTextCtrl, getSelectedText, 0}},
+ {3106, {wxStyledTextCtrl, getTextRange, 2}},
+ {3107, {wxStyledTextCtrl, hideSelection, 1}},
+ {3108, {wxStyledTextCtrl, lineFromPosition, 1}},
+ {3109, {wxStyledTextCtrl, positionFromLine, 1}},
+ {3110, {wxStyledTextCtrl, lineScroll, 2}},
+ {3111, {wxStyledTextCtrl, ensureCaretVisible, 0}},
+ {3112, {wxStyledTextCtrl, replaceSelection, 1}},
+ {3113, {wxStyledTextCtrl, setReadOnly, 1}},
+ {3114, {wxStyledTextCtrl, canPaste, 0}},
+ {3115, {wxStyledTextCtrl, canUndo, 0}},
+ {3116, {wxStyledTextCtrl, emptyUndoBuffer, 0}},
+ {3117, {wxStyledTextCtrl, undo, 0}},
+ {3118, {wxStyledTextCtrl, cut, 0}},
+ {3119, {wxStyledTextCtrl, copy, 0}},
+ {3120, {wxStyledTextCtrl, paste, 0}},
+ {3121, {wxStyledTextCtrl, clear, 0}},
+ {3122, {wxStyledTextCtrl, setText, 1}},
+ {3123, {wxStyledTextCtrl, getText, 0}},
+ {3124, {wxStyledTextCtrl, getTextLength, 0}},
+ {3125, {wxStyledTextCtrl, getOvertype, 0}},
+ {3126, {wxStyledTextCtrl, setCaretWidth, 1}},
+ {3127, {wxStyledTextCtrl, getCaretWidth, 0}},
+ {3128, {wxStyledTextCtrl, setTargetStart, 1}},
+ {3129, {wxStyledTextCtrl, getTargetStart, 0}},
+ {3130, {wxStyledTextCtrl, setTargetEnd, 1}},
+ {3131, {wxStyledTextCtrl, getTargetEnd, 0}},
+ {3132, {wxStyledTextCtrl, replaceTarget, 1}},
+ {3133, {wxStyledTextCtrl, searchInTarget, 1}},
+ {3134, {wxStyledTextCtrl, setSearchFlags, 1}},
+ {3135, {wxStyledTextCtrl, getSearchFlags, 0}},
+ {3136, {wxStyledTextCtrl, callTipShow, 2}},
+ {3137, {wxStyledTextCtrl, callTipCancel, 0}},
+ {3138, {wxStyledTextCtrl, callTipActive, 0}},
+ {3139, {wxStyledTextCtrl, callTipPosAtStart, 0}},
+ {3140, {wxStyledTextCtrl, callTipSetHighlight, 2}},
+ {3141, {wxStyledTextCtrl, callTipSetBackground, 1}},
+ {3142, {wxStyledTextCtrl, callTipSetForeground, 1}},
+ {3143, {wxStyledTextCtrl, callTipSetForegroundHighlight, 1}},
+ {3144, {wxStyledTextCtrl, callTipUseStyle, 1}},
+ {3145, {wxStyledTextCtrl, visibleFromDocLine, 1}},
+ {3146, {wxStyledTextCtrl, docLineFromVisible, 1}},
+ {3147, {wxStyledTextCtrl, wrapCount, 1}},
+ {3148, {wxStyledTextCtrl, setFoldLevel, 2}},
+ {3149, {wxStyledTextCtrl, getFoldLevel, 1}},
+ {3150, {wxStyledTextCtrl, getLastChild, 2}},
+ {3151, {wxStyledTextCtrl, getFoldParent, 1}},
+ {3152, {wxStyledTextCtrl, showLines, 2}},
+ {3153, {wxStyledTextCtrl, hideLines, 2}},
+ {3154, {wxStyledTextCtrl, getLineVisible, 1}},
+ {3155, {wxStyledTextCtrl, setFoldExpanded, 2}},
+ {3156, {wxStyledTextCtrl, getFoldExpanded, 1}},
+ {3157, {wxStyledTextCtrl, toggleFold, 1}},
+ {3158, {wxStyledTextCtrl, ensureVisible, 1}},
+ {3159, {wxStyledTextCtrl, setFoldFlags, 1}},
+ {3160, {wxStyledTextCtrl, ensureVisibleEnforcePolicy, 1}},
+ {3161, {wxStyledTextCtrl, setTabIndents, 1}},
+ {3162, {wxStyledTextCtrl, getTabIndents, 0}},
+ {3163, {wxStyledTextCtrl, setBackSpaceUnIndents, 1}},
+ {3164, {wxStyledTextCtrl, getBackSpaceUnIndents, 0}},
+ {3165, {wxStyledTextCtrl, setMouseDwellTime, 1}},
+ {3166, {wxStyledTextCtrl, getMouseDwellTime, 0}},
+ {3167, {wxStyledTextCtrl, wordStartPosition, 2}},
+ {3168, {wxStyledTextCtrl, wordEndPosition, 2}},
+ {3169, {wxStyledTextCtrl, setWrapMode, 1}},
+ {3170, {wxStyledTextCtrl, getWrapMode, 0}},
+ {3171, {wxStyledTextCtrl, setWrapVisualFlags, 1}},
+ {3172, {wxStyledTextCtrl, getWrapVisualFlags, 0}},
+ {3173, {wxStyledTextCtrl, setWrapVisualFlagsLocation, 1}},
+ {3174, {wxStyledTextCtrl, getWrapVisualFlagsLocation, 0}},
+ {3175, {wxStyledTextCtrl, setWrapStartIndent, 1}},
+ {3176, {wxStyledTextCtrl, getWrapStartIndent, 0}},
+ {3177, {wxStyledTextCtrl, setLayoutCache, 1}},
+ {3178, {wxStyledTextCtrl, getLayoutCache, 0}},
+ {3179, {wxStyledTextCtrl, setScrollWidth, 1}},
+ {3180, {wxStyledTextCtrl, getScrollWidth, 0}},
+ {3181, {wxStyledTextCtrl, textWidth, 2}},
+ {3182, {wxStyledTextCtrl, getEndAtLastLine, 0}},
+ {3183, {wxStyledTextCtrl, textHeight, 1}},
+ {3184, {wxStyledTextCtrl, setUseVerticalScrollBar, 1}},
+ {3185, {wxStyledTextCtrl, getUseVerticalScrollBar, 0}},
+ {3186, {wxStyledTextCtrl, appendText, 1}},
+ {3187, {wxStyledTextCtrl, getTwoPhaseDraw, 0}},
+ {3188, {wxStyledTextCtrl, setTwoPhaseDraw, 1}},
+ {3189, {wxStyledTextCtrl, targetFromSelection, 0}},
+ {3190, {wxStyledTextCtrl, linesJoin, 0}},
+ {3191, {wxStyledTextCtrl, linesSplit, 1}},
+ {3192, {wxStyledTextCtrl, setFoldMarginColour, 2}},
+ {3193, {wxStyledTextCtrl, setFoldMarginHiColour, 2}},
+ {3194, {wxStyledTextCtrl, lineDown, 0}},
+ {3195, {wxStyledTextCtrl, lineDownExtend, 0}},
+ {3196, {wxStyledTextCtrl, lineUp, 0}},
+ {3197, {wxStyledTextCtrl, lineUpExtend, 0}},
+ {3198, {wxStyledTextCtrl, charLeft, 0}},
+ {3199, {wxStyledTextCtrl, charLeftExtend, 0}},
+ {3200, {wxStyledTextCtrl, charRight, 0}},
+ {3201, {wxStyledTextCtrl, charRightExtend, 0}},
+ {3202, {wxStyledTextCtrl, wordLeft, 0}},
+ {3203, {wxStyledTextCtrl, wordLeftExtend, 0}},
+ {3204, {wxStyledTextCtrl, wordRight, 0}},
+ {3205, {wxStyledTextCtrl, wordRightExtend, 0}},
+ {3206, {wxStyledTextCtrl, home, 0}},
+ {3207, {wxStyledTextCtrl, homeExtend, 0}},
+ {3208, {wxStyledTextCtrl, lineEnd, 0}},
+ {3209, {wxStyledTextCtrl, lineEndExtend, 0}},
+ {3210, {wxStyledTextCtrl, documentStart, 0}},
+ {3211, {wxStyledTextCtrl, documentStartExtend, 0}},
+ {3212, {wxStyledTextCtrl, documentEnd, 0}},
+ {3213, {wxStyledTextCtrl, documentEndExtend, 0}},
+ {3214, {wxStyledTextCtrl, pageUp, 0}},
+ {3215, {wxStyledTextCtrl, pageUpExtend, 0}},
+ {3216, {wxStyledTextCtrl, pageDown, 0}},
+ {3217, {wxStyledTextCtrl, pageDownExtend, 0}},
+ {3218, {wxStyledTextCtrl, editToggleOvertype, 0}},
+ {3219, {wxStyledTextCtrl, cancel, 0}},
+ {3220, {wxStyledTextCtrl, deleteBack, 0}},
+ {3221, {wxStyledTextCtrl, tab, 0}},
+ {3222, {wxStyledTextCtrl, backTab, 0}},
+ {3223, {wxStyledTextCtrl, newLine, 0}},
+ {3224, {wxStyledTextCtrl, formFeed, 0}},
+ {3225, {wxStyledTextCtrl, vCHome, 0}},
+ {3226, {wxStyledTextCtrl, vCHomeExtend, 0}},
+ {3227, {wxStyledTextCtrl, zoomIn, 0}},
+ {3228, {wxStyledTextCtrl, zoomOut, 0}},
+ {3229, {wxStyledTextCtrl, delWordLeft, 0}},
+ {3230, {wxStyledTextCtrl, delWordRight, 0}},
+ {3231, {wxStyledTextCtrl, lineCut, 0}},
+ {3232, {wxStyledTextCtrl, lineDelete, 0}},
+ {3233, {wxStyledTextCtrl, lineTranspose, 0}},
+ {3234, {wxStyledTextCtrl, lineDuplicate, 0}},
+ {3235, {wxStyledTextCtrl, lowerCase, 0}},
+ {3236, {wxStyledTextCtrl, upperCase, 0}},
+ {3237, {wxStyledTextCtrl, lineScrollDown, 0}},
+ {3238, {wxStyledTextCtrl, lineScrollUp, 0}},
+ {3239, {wxStyledTextCtrl, deleteBackNotLine, 0}},
+ {3240, {wxStyledTextCtrl, homeDisplay, 0}},
+ {3241, {wxStyledTextCtrl, homeDisplayExtend, 0}},
+ {3242, {wxStyledTextCtrl, lineEndDisplay, 0}},
+ {3243, {wxStyledTextCtrl, lineEndDisplayExtend, 0}},
+ {3244, {wxStyledTextCtrl, homeWrapExtend, 0}},
+ {3245, {wxStyledTextCtrl, lineEndWrap, 0}},
+ {3246, {wxStyledTextCtrl, lineEndWrapExtend, 0}},
+ {3247, {wxStyledTextCtrl, vCHomeWrap, 0}},
+ {3248, {wxStyledTextCtrl, vCHomeWrapExtend, 0}},
+ {3249, {wxStyledTextCtrl, lineCopy, 0}},
+ {3250, {wxStyledTextCtrl, moveCaretInsideView, 0}},
+ {3251, {wxStyledTextCtrl, lineLength, 1}},
+ {3252, {wxStyledTextCtrl, braceHighlight, 2}},
+ {3253, {wxStyledTextCtrl, braceBadLight, 1}},
+ {3254, {wxStyledTextCtrl, braceMatch, 1}},
+ {3255, {wxStyledTextCtrl, getViewEOL, 0}},
+ {3256, {wxStyledTextCtrl, setViewEOL, 1}},
+ {3257, {wxStyledTextCtrl, setModEventMask, 1}},
+ {3258, {wxStyledTextCtrl, getEdgeColumn, 0}},
+ {3259, {wxStyledTextCtrl, setEdgeColumn, 1}},
+ {3260, {wxStyledTextCtrl, getEdgeMode, 0}},
+ {3261, {wxStyledTextCtrl, getEdgeColour, 0}},
+ {3262, {wxStyledTextCtrl, setEdgeColour, 1}},
+ {3263, {wxStyledTextCtrl, searchAnchor, 0}},
+ {3264, {wxStyledTextCtrl, searchNext, 2}},
+ {3265, {wxStyledTextCtrl, searchPrev, 2}},
+ {3266, {wxStyledTextCtrl, linesOnScreen, 0}},
+ {3267, {wxStyledTextCtrl, usePopUp, 1}},
+ {3268, {wxStyledTextCtrl, selectionIsRectangle, 0}},
+ {3269, {wxStyledTextCtrl, setZoom, 1}},
+ {3270, {wxStyledTextCtrl, getZoom, 0}},
+ {3271, {wxStyledTextCtrl, getModEventMask, 0}},
+ {3272, {wxStyledTextCtrl, setSTCFocus, 1}},
+ {3273, {wxStyledTextCtrl, getSTCFocus, 0}},
+ {3274, {wxStyledTextCtrl, setStatus, 1}},
+ {3275, {wxStyledTextCtrl, getStatus, 0}},
+ {3276, {wxStyledTextCtrl, setMouseDownCaptures, 1}},
+ {3277, {wxStyledTextCtrl, getMouseDownCaptures, 0}},
+ {3278, {wxStyledTextCtrl, setSTCCursor, 1}},
+ {3279, {wxStyledTextCtrl, getSTCCursor, 0}},
+ {3280, {wxStyledTextCtrl, setControlCharSymbol, 1}},
+ {3281, {wxStyledTextCtrl, getControlCharSymbol, 0}},
+ {3282, {wxStyledTextCtrl, wordPartLeft, 0}},
+ {3283, {wxStyledTextCtrl, wordPartLeftExtend, 0}},
+ {3284, {wxStyledTextCtrl, wordPartRight, 0}},
+ {3285, {wxStyledTextCtrl, wordPartRightExtend, 0}},
+ {3286, {wxStyledTextCtrl, setVisiblePolicy, 2}},
+ {3287, {wxStyledTextCtrl, delLineLeft, 0}},
+ {3288, {wxStyledTextCtrl, delLineRight, 0}},
+ {3289, {wxStyledTextCtrl, getXOffset, 0}},
+ {3290, {wxStyledTextCtrl, chooseCaretX, 0}},
+ {3291, {wxStyledTextCtrl, setXCaretPolicy, 2}},
+ {3292, {wxStyledTextCtrl, setYCaretPolicy, 2}},
+ {3293, {wxStyledTextCtrl, getPrintWrapMode, 0}},
+ {3294, {wxStyledTextCtrl, setHotspotActiveForeground, 2}},
+ {3295, {wxStyledTextCtrl, setHotspotActiveBackground, 2}},
+ {3296, {wxStyledTextCtrl, setHotspotActiveUnderline, 1}},
+ {3297, {wxStyledTextCtrl, setHotspotSingleLine, 1}},
+ {3298, {wxStyledTextCtrl, paraDownExtend, 0}},
+ {3299, {wxStyledTextCtrl, paraUp, 0}},
+ {3300, {wxStyledTextCtrl, paraUpExtend, 0}},
+ {3301, {wxStyledTextCtrl, positionBefore, 1}},
+ {3302, {wxStyledTextCtrl, positionAfter, 1}},
+ {3303, {wxStyledTextCtrl, copyRange, 2}},
+ {3304, {wxStyledTextCtrl, copyText, 2}},
+ {3305, {wxStyledTextCtrl, setSelectionMode, 1}},
+ {3306, {wxStyledTextCtrl, getSelectionMode, 0}},
+ {3307, {wxStyledTextCtrl, lineDownRectExtend, 0}},
+ {3308, {wxStyledTextCtrl, lineUpRectExtend, 0}},
+ {3309, {wxStyledTextCtrl, charLeftRectExtend, 0}},
+ {3310, {wxStyledTextCtrl, charRightRectExtend, 0}},
+ {3311, {wxStyledTextCtrl, homeRectExtend, 0}},
+ {3312, {wxStyledTextCtrl, vCHomeRectExtend, 0}},
+ {3313, {wxStyledTextCtrl, lineEndRectExtend, 0}},
+ {3314, {wxStyledTextCtrl, pageUpRectExtend, 0}},
+ {3315, {wxStyledTextCtrl, pageDownRectExtend, 0}},
+ {3316, {wxStyledTextCtrl, stutteredPageUp, 0}},
+ {3317, {wxStyledTextCtrl, stutteredPageUpExtend, 0}},
+ {3318, {wxStyledTextCtrl, stutteredPageDown, 0}},
+ {3319, {wxStyledTextCtrl, stutteredPageDownExtend, 0}},
+ {3320, {wxStyledTextCtrl, wordLeftEnd, 0}},
+ {3321, {wxStyledTextCtrl, wordLeftEndExtend, 0}},
+ {3322, {wxStyledTextCtrl, wordRightEnd, 0}},
+ {3323, {wxStyledTextCtrl, wordRightEndExtend, 0}},
+ {3324, {wxStyledTextCtrl, setWhitespaceChars, 1}},
+ {3325, {wxStyledTextCtrl, setCharsDefault, 0}},
+ {3326, {wxStyledTextCtrl, autoCompGetCurrent, 0}},
+ {3327, {wxStyledTextCtrl, allocate, 1}},
+ {3328, {wxStyledTextCtrl, findColumn, 2}},
+ {3329, {wxStyledTextCtrl, getCaretSticky, 0}},
+ {3330, {wxStyledTextCtrl, setCaretSticky, 1}},
+ {3331, {wxStyledTextCtrl, toggleCaretSticky, 0}},
+ {3332, {wxStyledTextCtrl, setPasteConvertEndings, 1}},
+ {3333, {wxStyledTextCtrl, getPasteConvertEndings, 0}},
+ {3334, {wxStyledTextCtrl, selectionDuplicate, 0}},
+ {3335, {wxStyledTextCtrl, setCaretLineBackAlpha, 1}},
+ {3336, {wxStyledTextCtrl, getCaretLineBackAlpha, 0}},
+ {3337, {wxStyledTextCtrl, startRecord, 0}},
+ {3338, {wxStyledTextCtrl, stopRecord, 0}},
+ {3339, {wxStyledTextCtrl, setLexer, 1}},
+ {3340, {wxStyledTextCtrl, getLexer, 0}},
+ {3341, {wxStyledTextCtrl, colourise, 2}},
+ {3342, {wxStyledTextCtrl, setProperty, 2}},
+ {3343, {wxStyledTextCtrl, setKeyWords, 2}},
+ {3344, {wxStyledTextCtrl, setLexerLanguage, 1}},
+ {3345, {wxStyledTextCtrl, getProperty, 1}},
+ {3346, {wxStyledTextCtrl, getStyleBitsNeeded, 0}},
+ {3347, {wxStyledTextCtrl, getCurrentLine, 0}},
+ {3348, {wxStyledTextCtrl, styleSetSpec, 2}},
+ {3349, {wxStyledTextCtrl, styleSetFont, 2}},
+ {3350, {wxStyledTextCtrl, styleSetFontAttr, 7}},
+ {3351, {wxStyledTextCtrl, styleSetCharacterSet, 2}},
+ {3352, {wxStyledTextCtrl, styleSetFontEncoding, 2}},
+ {3353, {wxStyledTextCtrl, cmdKeyExecute, 1}},
+ {3354, {wxStyledTextCtrl, setMargins, 2}},
+ {3355, {wxStyledTextCtrl, getSelection, 2}},
+ {3356, {wxStyledTextCtrl, pointFromPosition, 1}},
+ {3357, {wxStyledTextCtrl, scrollToLine, 1}},
+ {3358, {wxStyledTextCtrl, scrollToColumn, 1}},
+ {3359, {wxStyledTextCtrl, sendMsg, 2}},
+ {3360, {wxStyledTextCtrl, setVScrollBar, 1}},
+ {3361, {wxStyledTextCtrl, setHScrollBar, 1}},
+ {3362, {wxStyledTextCtrl, getLastKeydownProcessed, 0}},
+ {3363, {wxStyledTextCtrl, setLastKeydownProcessed, 1}},
+ {3364, {wxStyledTextCtrl, saveFile, 1}},
+ {3365, {wxStyledTextCtrl, loadFile, 1}},
+ {3366, {wxStyledTextCtrl, doDragOver, 3}},
+ {3367, {wxStyledTextCtrl, doDropText, 3}},
+ {3368, {wxStyledTextCtrl, getUseAntiAliasing, 0}},
+ {3369, {wxStyledTextCtrl, addTextRaw, 1}},
+ {3370, {wxStyledTextCtrl, insertTextRaw, 2}},
+ {3371, {wxStyledTextCtrl, getCurLineRaw, 1}},
+ {3372, {wxStyledTextCtrl, getLineRaw, 1}},
+ {3373, {wxStyledTextCtrl, getSelectedTextRaw, 0}},
+ {3374, {wxStyledTextCtrl, getTextRangeRaw, 2}},
+ {3375, {wxStyledTextCtrl, setTextRaw, 1}},
+ {3376, {wxStyledTextCtrl, getTextRaw, 0}},
+ {3377, {wxStyledTextCtrl, appendTextRaw, 1}},
+ {3378, {wxArtProvider, getBitmap, 2}},
+ {3379, {wxArtProvider, getIcon, 2}},
+ {3380, {wxTreeEvent, getKeyCode, 0}},
+ {3381, {wxTreeEvent, getItem, 0}},
+ {3382, {wxTreeEvent, getKeyEvent, 0}},
+ {3383, {wxTreeEvent, getLabel, 0}},
+ {3384, {wxTreeEvent, getOldItem, 0}},
+ {3385, {wxTreeEvent, getPoint, 0}},
+ {3386, {wxTreeEvent, isEditCancelled, 0}},
+ {3387, {wxTreeEvent, setToolTip, 1}},
+ {3388, {wxNotebookEvent, getOldSelection, 0}},
+ {3389, {wxNotebookEvent, getSelection, 0}},
+ {3390, {wxNotebookEvent, setOldSelection, 1}},
+ {3391, {wxNotebookEvent, setSelection, 1}},
+ {3392, {wxFileDataObject, new, 0}},
+ {3393, {wxFileDataObject, addFile, 1}},
+ {3394, {wxFileDataObject, getFilenames, 0}},
+ {3395, {wxFileDataObject, 'Destroy', undefined}},
+ {3396, {wxTextDataObject, new, 1}},
+ {3397, {wxTextDataObject, getTextLength, 0}},
+ {3398, {wxTextDataObject, getText, 0}},
+ {3399, {wxTextDataObject, setText, 1}},
+ {3400, {wxTextDataObject, 'Destroy', undefined}},
+ {3401, {wxBitmapDataObject, new_1_1, 1}},
+ {3402, {wxBitmapDataObject, new_1_0, 1}},
+ {3403, {wxBitmapDataObject, getBitmap, 0}},
+ {3404, {wxBitmapDataObject, setBitmap, 1}},
+ {3405, {wxBitmapDataObject, 'Destroy', undefined}},
+ {3407, {wxClipboard, new, 0}},
+ {3408, {wxClipboard, destruct, 0}},
+ {3409, {wxClipboard, addData, 1}},
+ {3410, {wxClipboard, clear, 0}},
+ {3411, {wxClipboard, close, 0}},
+ {3412, {wxClipboard, flush, 0}},
+ {3413, {wxClipboard, getData, 1}},
+ {3414, {wxClipboard, isOpened, 0}},
+ {3415, {wxClipboard, open, 0}},
+ {3416, {wxClipboard, setData, 1}},
+ {3418, {wxClipboard, usePrimarySelection, 1}},
+ {3419, {wxClipboard, isSupported, 1}},
+ {3420, {wxClipboard, get, 0}},
+ {3421, {wxSpinEvent, getPosition, 0}},
+ {3422, {wxSpinEvent, setPosition, 1}},
+ {3423, {wxSplitterWindow, new_0, 0}},
+ {3424, {wxSplitterWindow, new_2, 2}},
+ {3425, {wxSplitterWindow, destruct, 0}},
+ {3426, {wxSplitterWindow, create, 2}},
+ {3427, {wxSplitterWindow, getMinimumPaneSize, 0}},
+ {3428, {wxSplitterWindow, getSashGravity, 0}},
+ {3429, {wxSplitterWindow, getSashPosition, 0}},
+ {3430, {wxSplitterWindow, getSplitMode, 0}},
+ {3431, {wxSplitterWindow, getWindow1, 0}},
+ {3432, {wxSplitterWindow, getWindow2, 0}},
+ {3433, {wxSplitterWindow, initialize, 1}},
+ {3434, {wxSplitterWindow, isSplit, 0}},
+ {3435, {wxSplitterWindow, replaceWindow, 2}},
+ {3436, {wxSplitterWindow, setSashGravity, 1}},
+ {3437, {wxSplitterWindow, setSashPosition, 2}},
+ {3438, {wxSplitterWindow, setSashSize, 1}},
+ {3439, {wxSplitterWindow, setMinimumPaneSize, 1}},
+ {3440, {wxSplitterWindow, setSplitMode, 1}},
+ {3441, {wxSplitterWindow, splitHorizontally, 3}},
+ {3442, {wxSplitterWindow, splitVertically, 3}},
+ {3443, {wxSplitterWindow, unsplit, 1}},
+ {3444, {wxSplitterWindow, updateSize, 0}},
+ {3445, {wxSplitterEvent, getSashPosition, 0}},
+ {3446, {wxSplitterEvent, getX, 0}},
+ {3447, {wxSplitterEvent, getY, 0}},
+ {3448, {wxSplitterEvent, getWindowBeingRemoved, 0}},
+ {3449, {wxSplitterEvent, setSashPosition, 1}},
+ {3450, {wxHtmlWindow, new_0, 0}},
+ {3451, {wxHtmlWindow, new_2, 2}},
+ {3452, {wxHtmlWindow, appendToPage, 1}},
+ {3453, {wxHtmlWindow, getOpenedAnchor, 0}},
+ {3454, {wxHtmlWindow, getOpenedPage, 0}},
+ {3455, {wxHtmlWindow, getOpenedPageTitle, 0}},
+ {3456, {wxHtmlWindow, getRelatedFrame, 0}},
+ {3457, {wxHtmlWindow, historyBack, 0}},
+ {3458, {wxHtmlWindow, historyCanBack, 0}},
+ {3459, {wxHtmlWindow, historyCanForward, 0}},
+ {3460, {wxHtmlWindow, historyClear, 0}},
+ {3461, {wxHtmlWindow, historyForward, 0}},
+ {3462, {wxHtmlWindow, loadFile, 1}},
+ {3463, {wxHtmlWindow, loadPage, 1}},
+ {3464, {wxHtmlWindow, selectAll, 0}},
+ {3465, {wxHtmlWindow, selectionToText, 0}},
+ {3466, {wxHtmlWindow, selectLine, 1}},
+ {3467, {wxHtmlWindow, selectWord, 1}},
+ {3468, {wxHtmlWindow, setBorders, 1}},
+ {3469, {wxHtmlWindow, setFonts, 3}},
+ {3470, {wxHtmlWindow, setPage, 1}},
+ {3471, {wxHtmlWindow, setRelatedFrame, 2}},
+ {3472, {wxHtmlWindow, setRelatedStatusBar, 1}},
+ {3473, {wxHtmlWindow, toText, 0}},
+ {3474, {wxHtmlWindow, 'Destroy', undefined}},
+ {3475, {wxHtmlLinkEvent, getLinkInfo, 0}},
+ {3476, {wxSystemSettings, getColour, 1}},
+ {3477, {wxSystemSettings, getFont, 1}},
+ {3478, {wxSystemSettings, getMetric, 2}},
+ {3479, {wxSystemSettings, getScreenType, 0}},
+ {3480, {wxAuiNotebookEvent, setSelection, 1}},
+ {3481, {wxAuiNotebookEvent, getSelection, 0}},
+ {3482, {wxAuiNotebookEvent, setOldSelection, 1}},
+ {3483, {wxAuiNotebookEvent, getOldSelection, 0}},
+ {3484, {wxAuiNotebookEvent, setDragSource, 1}},
+ {3485, {wxAuiNotebookEvent, getDragSource, 0}},
+ {3486, {wxAuiManagerEvent, setManager, 1}},
+ {3487, {wxAuiManagerEvent, getManager, 0}},
+ {3488, {wxAuiManagerEvent, setPane, 1}},
+ {3489, {wxAuiManagerEvent, getPane, 0}},
+ {3490, {wxAuiManagerEvent, setButton, 1}},
+ {3491, {wxAuiManagerEvent, getButton, 0}},
+ {3492, {wxAuiManagerEvent, setDC, 1}},
+ {3493, {wxAuiManagerEvent, getDC, 0}},
+ {3494, {wxAuiManagerEvent, veto, 1}},
+ {3495, {wxAuiManagerEvent, getVeto, 0}},
+ {3496, {wxAuiManagerEvent, setCanVeto, 1}},
+ {3497, {wxAuiManagerEvent, canVeto, 0}},
+ {3498, {wxLogNull, new, 0}},
+ {3499, {wxLogNull, 'Destroy', undefined}},
{-1, {mod, func, -1}}
].
diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl
index 7b5ca229c8..cf672644c6 100644
--- a/lib/wx/src/gen/wxe_funcs.hrl
+++ b/lib/wx/src/gen/wxe_funcs.hrl
@@ -1834,1442 +1834,1447 @@
-define(wxTreeCtrl_Delete, 2007).
-define(wxTreeCtrl_DeleteAllItems, 2008).
-define(wxTreeCtrl_DeleteChildren, 2009).
--define(wxTreeCtrl_EnsureVisible, 2010).
--define(wxTreeCtrl_Expand, 2011).
--define(wxTreeCtrl_GetBoundingRect, 2012).
--define(wxTreeCtrl_GetChildrenCount, 2014).
--define(wxTreeCtrl_GetCount, 2015).
--define(wxTreeCtrl_GetEditControl, 2016).
--define(wxTreeCtrl_GetFirstChild, 2017).
--define(wxTreeCtrl_GetNextChild, 2018).
--define(wxTreeCtrl_GetFirstVisibleItem, 2019).
--define(wxTreeCtrl_GetImageList, 2020).
--define(wxTreeCtrl_GetIndent, 2021).
--define(wxTreeCtrl_GetItemBackgroundColour, 2022).
--define(wxTreeCtrl_GetItemData, 2023).
--define(wxTreeCtrl_GetItemFont, 2024).
--define(wxTreeCtrl_GetItemImage_1, 2025).
--define(wxTreeCtrl_GetItemImage_2, 2026).
--define(wxTreeCtrl_GetItemText, 2027).
--define(wxTreeCtrl_GetItemTextColour, 2028).
--define(wxTreeCtrl_GetLastChild, 2029).
--define(wxTreeCtrl_GetNextSibling, 2030).
--define(wxTreeCtrl_GetNextVisible, 2031).
--define(wxTreeCtrl_GetItemParent, 2032).
--define(wxTreeCtrl_GetPrevSibling, 2033).
--define(wxTreeCtrl_GetPrevVisible, 2034).
--define(wxTreeCtrl_GetRootItem, 2035).
--define(wxTreeCtrl_GetSelection, 2036).
--define(wxTreeCtrl_GetSelections, 2037).
--define(wxTreeCtrl_GetStateImageList, 2038).
--define(wxTreeCtrl_HitTest, 2039).
--define(wxTreeCtrl_InsertItem, 2041).
--define(wxTreeCtrl_IsBold, 2042).
--define(wxTreeCtrl_IsExpanded, 2043).
--define(wxTreeCtrl_IsSelected, 2044).
--define(wxTreeCtrl_IsVisible, 2045).
--define(wxTreeCtrl_ItemHasChildren, 2046).
--define(wxTreeCtrl_PrependItem, 2047).
--define(wxTreeCtrl_ScrollTo, 2048).
--define(wxTreeCtrl_SelectItem_1, 2049).
--define(wxTreeCtrl_SelectItem_2, 2050).
--define(wxTreeCtrl_SetIndent, 2051).
--define(wxTreeCtrl_SetImageList, 2052).
--define(wxTreeCtrl_SetItemBackgroundColour, 2053).
--define(wxTreeCtrl_SetItemBold, 2054).
--define(wxTreeCtrl_SetItemData, 2055).
--define(wxTreeCtrl_SetItemDropHighlight, 2056).
--define(wxTreeCtrl_SetItemFont, 2057).
--define(wxTreeCtrl_SetItemHasChildren, 2058).
--define(wxTreeCtrl_SetItemImage_2, 2059).
--define(wxTreeCtrl_SetItemImage_3, 2060).
--define(wxTreeCtrl_SetItemText, 2061).
--define(wxTreeCtrl_SetItemTextColour, 2062).
--define(wxTreeCtrl_SetStateImageList, 2063).
--define(wxTreeCtrl_SetWindowStyle, 2064).
--define(wxTreeCtrl_SortChildren, 2065).
--define(wxTreeCtrl_Toggle, 2066).
--define(wxTreeCtrl_ToggleItemSelection, 2067).
--define(wxTreeCtrl_Unselect, 2068).
--define(wxTreeCtrl_UnselectAll, 2069).
--define(wxTreeCtrl_UnselectItem, 2070).
--define(wxScrollBar_new_0, 2071).
--define(wxScrollBar_new_3, 2072).
--define(wxScrollBar_destruct, 2073).
--define(wxScrollBar_Create, 2074).
--define(wxScrollBar_GetRange, 2075).
--define(wxScrollBar_GetPageSize, 2076).
--define(wxScrollBar_GetThumbPosition, 2077).
--define(wxScrollBar_GetThumbSize, 2078).
--define(wxScrollBar_SetThumbPosition, 2079).
--define(wxScrollBar_SetScrollbar, 2080).
--define(wxSpinButton_new_2, 2082).
--define(wxSpinButton_new_0, 2083).
--define(wxSpinButton_Create, 2084).
--define(wxSpinButton_GetMax, 2085).
--define(wxSpinButton_GetMin, 2086).
--define(wxSpinButton_GetValue, 2087).
--define(wxSpinButton_SetRange, 2088).
--define(wxSpinButton_SetValue, 2089).
--define(wxSpinButton_destroy, 2090).
--define(wxSpinCtrl_new_0, 2091).
--define(wxSpinCtrl_new_2, 2092).
--define(wxSpinCtrl_Create, 2094).
--define(wxSpinCtrl_SetValue_1_1, 2097).
--define(wxSpinCtrl_SetValue_1_0, 2098).
--define(wxSpinCtrl_GetValue, 2100).
--define(wxSpinCtrl_SetRange, 2102).
--define(wxSpinCtrl_SetSelection, 2103).
--define(wxSpinCtrl_GetMin, 2105).
--define(wxSpinCtrl_GetMax, 2107).
--define(wxSpinCtrl_destroy, 2108).
--define(wxStaticText_new_0, 2109).
--define(wxStaticText_new_4, 2110).
--define(wxStaticText_Create, 2111).
--define(wxStaticText_GetLabel, 2112).
--define(wxStaticText_SetLabel, 2113).
--define(wxStaticText_Wrap, 2114).
--define(wxStaticText_destroy, 2115).
--define(wxStaticBitmap_new_0, 2116).
--define(wxStaticBitmap_new_4, 2117).
--define(wxStaticBitmap_Create, 2118).
--define(wxStaticBitmap_GetBitmap, 2119).
--define(wxStaticBitmap_SetBitmap, 2120).
--define(wxStaticBitmap_destroy, 2121).
--define(wxRadioBox_new, 2122).
--define(wxRadioBox_destruct, 2124).
--define(wxRadioBox_Create, 2125).
--define(wxRadioBox_Enable_2, 2126).
--define(wxRadioBox_Enable_1, 2127).
--define(wxRadioBox_GetSelection, 2128).
--define(wxRadioBox_GetString, 2129).
--define(wxRadioBox_SetSelection, 2130).
--define(wxRadioBox_Show_2, 2131).
--define(wxRadioBox_Show_1, 2132).
--define(wxRadioBox_GetColumnCount, 2133).
--define(wxRadioBox_GetItemHelpText, 2134).
--define(wxRadioBox_GetItemToolTip, 2135).
--define(wxRadioBox_GetItemFromPoint, 2137).
--define(wxRadioBox_GetRowCount, 2138).
--define(wxRadioBox_IsItemEnabled, 2139).
--define(wxRadioBox_IsItemShown, 2140).
--define(wxRadioBox_SetItemHelpText, 2141).
--define(wxRadioBox_SetItemToolTip, 2142).
--define(wxRadioButton_new_0, 2143).
--define(wxRadioButton_new_4, 2144).
--define(wxRadioButton_Create, 2145).
--define(wxRadioButton_GetValue, 2146).
--define(wxRadioButton_SetValue, 2147).
--define(wxRadioButton_destroy, 2148).
--define(wxSlider_new_6, 2150).
--define(wxSlider_new_0, 2151).
--define(wxSlider_Create, 2152).
--define(wxSlider_GetLineSize, 2153).
--define(wxSlider_GetMax, 2154).
--define(wxSlider_GetMin, 2155).
--define(wxSlider_GetPageSize, 2156).
--define(wxSlider_GetThumbLength, 2157).
--define(wxSlider_GetValue, 2158).
--define(wxSlider_SetLineSize, 2159).
--define(wxSlider_SetPageSize, 2160).
--define(wxSlider_SetRange, 2161).
--define(wxSlider_SetThumbLength, 2162).
--define(wxSlider_SetValue, 2163).
--define(wxSlider_destroy, 2164).
--define(wxDialog_new_4, 2166).
--define(wxDialog_new_0, 2167).
--define(wxDialog_destruct, 2169).
--define(wxDialog_Create, 2170).
--define(wxDialog_CreateButtonSizer, 2171).
--define(wxDialog_CreateStdDialogButtonSizer, 2172).
--define(wxDialog_EndModal, 2173).
--define(wxDialog_GetAffirmativeId, 2174).
--define(wxDialog_GetReturnCode, 2175).
--define(wxDialog_IsModal, 2176).
--define(wxDialog_SetAffirmativeId, 2177).
--define(wxDialog_SetReturnCode, 2178).
--define(wxDialog_Show, 2179).
--define(wxDialog_ShowModal, 2180).
--define(wxColourDialog_new_0, 2181).
--define(wxColourDialog_new_2, 2182).
--define(wxColourDialog_destruct, 2183).
--define(wxColourDialog_Create, 2184).
--define(wxColourDialog_GetColourData, 2185).
--define(wxColourData_new_0, 2186).
--define(wxColourData_new_1, 2187).
--define(wxColourData_destruct, 2188).
--define(wxColourData_GetChooseFull, 2189).
--define(wxColourData_GetColour, 2190).
--define(wxColourData_GetCustomColour, 2192).
--define(wxColourData_SetChooseFull, 2193).
--define(wxColourData_SetColour, 2194).
--define(wxColourData_SetCustomColour, 2195).
--define(wxPalette_new_0, 2196).
--define(wxPalette_new_4, 2197).
--define(wxPalette_destruct, 2199).
--define(wxPalette_Create, 2200).
--define(wxPalette_GetColoursCount, 2201).
--define(wxPalette_GetPixel, 2202).
--define(wxPalette_GetRGB, 2203).
--define(wxPalette_IsOk, 2204).
--define(wxDirDialog_new, 2208).
--define(wxDirDialog_destruct, 2209).
--define(wxDirDialog_GetPath, 2210).
--define(wxDirDialog_GetMessage, 2211).
--define(wxDirDialog_SetMessage, 2212).
--define(wxDirDialog_SetPath, 2213).
--define(wxFileDialog_new, 2217).
--define(wxFileDialog_destruct, 2218).
--define(wxFileDialog_GetDirectory, 2219).
--define(wxFileDialog_GetFilename, 2220).
--define(wxFileDialog_GetFilenames, 2221).
--define(wxFileDialog_GetFilterIndex, 2222).
--define(wxFileDialog_GetMessage, 2223).
--define(wxFileDialog_GetPath, 2224).
--define(wxFileDialog_GetPaths, 2225).
--define(wxFileDialog_GetWildcard, 2226).
--define(wxFileDialog_SetDirectory, 2227).
--define(wxFileDialog_SetFilename, 2228).
--define(wxFileDialog_SetFilterIndex, 2229).
--define(wxFileDialog_SetMessage, 2230).
--define(wxFileDialog_SetPath, 2231).
--define(wxFileDialog_SetWildcard, 2232).
--define(wxPickerBase_SetInternalMargin, 2233).
--define(wxPickerBase_GetInternalMargin, 2234).
--define(wxPickerBase_SetTextCtrlProportion, 2235).
--define(wxPickerBase_SetPickerCtrlProportion, 2236).
--define(wxPickerBase_GetTextCtrlProportion, 2237).
--define(wxPickerBase_GetPickerCtrlProportion, 2238).
--define(wxPickerBase_HasTextCtrl, 2239).
--define(wxPickerBase_GetTextCtrl, 2240).
--define(wxPickerBase_IsTextCtrlGrowable, 2241).
--define(wxPickerBase_SetPickerCtrlGrowable, 2242).
--define(wxPickerBase_SetTextCtrlGrowable, 2243).
--define(wxPickerBase_IsPickerCtrlGrowable, 2244).
--define(wxFilePickerCtrl_new_0, 2245).
--define(wxFilePickerCtrl_new_3, 2246).
--define(wxFilePickerCtrl_Create, 2247).
--define(wxFilePickerCtrl_GetPath, 2248).
--define(wxFilePickerCtrl_SetPath, 2249).
--define(wxFilePickerCtrl_destroy, 2250).
--define(wxDirPickerCtrl_new_0, 2251).
--define(wxDirPickerCtrl_new_3, 2252).
--define(wxDirPickerCtrl_Create, 2253).
--define(wxDirPickerCtrl_GetPath, 2254).
--define(wxDirPickerCtrl_SetPath, 2255).
--define(wxDirPickerCtrl_destroy, 2256).
--define(wxColourPickerCtrl_new_0, 2257).
--define(wxColourPickerCtrl_new_3, 2258).
--define(wxColourPickerCtrl_Create, 2259).
--define(wxColourPickerCtrl_GetColour, 2260).
--define(wxColourPickerCtrl_SetColour_1_1, 2261).
--define(wxColourPickerCtrl_SetColour_1_0, 2262).
--define(wxColourPickerCtrl_destroy, 2263).
--define(wxDatePickerCtrl_new_0, 2264).
--define(wxDatePickerCtrl_new_3, 2265).
--define(wxDatePickerCtrl_GetRange, 2266).
--define(wxDatePickerCtrl_GetValue, 2267).
--define(wxDatePickerCtrl_SetRange, 2268).
--define(wxDatePickerCtrl_SetValue, 2269).
--define(wxDatePickerCtrl_destroy, 2270).
--define(wxFontPickerCtrl_new_0, 2271).
--define(wxFontPickerCtrl_new_3, 2272).
--define(wxFontPickerCtrl_Create, 2273).
--define(wxFontPickerCtrl_GetSelectedFont, 2274).
--define(wxFontPickerCtrl_SetSelectedFont, 2275).
--define(wxFontPickerCtrl_GetMaxPointSize, 2276).
--define(wxFontPickerCtrl_SetMaxPointSize, 2277).
--define(wxFontPickerCtrl_destroy, 2278).
--define(wxFindReplaceDialog_new_0, 2281).
--define(wxFindReplaceDialog_new_4, 2282).
--define(wxFindReplaceDialog_destruct, 2283).
--define(wxFindReplaceDialog_Create, 2284).
--define(wxFindReplaceDialog_GetData, 2285).
--define(wxFindReplaceData_new_0, 2286).
--define(wxFindReplaceData_new_1, 2287).
--define(wxFindReplaceData_GetFindString, 2288).
--define(wxFindReplaceData_GetReplaceString, 2289).
--define(wxFindReplaceData_GetFlags, 2290).
--define(wxFindReplaceData_SetFlags, 2291).
--define(wxFindReplaceData_SetFindString, 2292).
--define(wxFindReplaceData_SetReplaceString, 2293).
--define(wxFindReplaceData_destroy, 2294).
--define(wxMultiChoiceDialog_new_0, 2295).
--define(wxMultiChoiceDialog_new_5, 2297).
--define(wxMultiChoiceDialog_GetSelections, 2298).
--define(wxMultiChoiceDialog_SetSelections, 2299).
--define(wxMultiChoiceDialog_destroy, 2300).
--define(wxSingleChoiceDialog_new_0, 2301).
--define(wxSingleChoiceDialog_new_5, 2303).
--define(wxSingleChoiceDialog_GetSelection, 2304).
--define(wxSingleChoiceDialog_GetStringSelection, 2305).
--define(wxSingleChoiceDialog_SetSelection, 2306).
--define(wxSingleChoiceDialog_destroy, 2307).
--define(wxTextEntryDialog_new, 2308).
--define(wxTextEntryDialog_GetValue, 2309).
--define(wxTextEntryDialog_SetValue, 2310).
--define(wxTextEntryDialog_destroy, 2311).
--define(wxPasswordEntryDialog_new, 2312).
--define(wxPasswordEntryDialog_destroy, 2313).
--define(wxFontData_new_0, 2314).
--define(wxFontData_new_1, 2315).
--define(wxFontData_destruct, 2316).
--define(wxFontData_EnableEffects, 2317).
--define(wxFontData_GetAllowSymbols, 2318).
--define(wxFontData_GetColour, 2319).
--define(wxFontData_GetChosenFont, 2320).
--define(wxFontData_GetEnableEffects, 2321).
--define(wxFontData_GetInitialFont, 2322).
--define(wxFontData_GetShowHelp, 2323).
--define(wxFontData_SetAllowSymbols, 2324).
--define(wxFontData_SetChosenFont, 2325).
--define(wxFontData_SetColour, 2326).
--define(wxFontData_SetInitialFont, 2327).
--define(wxFontData_SetRange, 2328).
--define(wxFontData_SetShowHelp, 2329).
--define(wxFontDialog_new_0, 2333).
--define(wxFontDialog_new_2, 2335).
--define(wxFontDialog_Create, 2337).
--define(wxFontDialog_GetFontData, 2338).
--define(wxFontDialog_destroy, 2340).
--define(wxProgressDialog_new, 2341).
--define(wxProgressDialog_destruct, 2342).
--define(wxProgressDialog_Resume, 2343).
--define(wxProgressDialog_Update_2, 2344).
--define(wxProgressDialog_Update_0, 2345).
--define(wxMessageDialog_new, 2346).
--define(wxMessageDialog_destruct, 2347).
--define(wxPageSetupDialog_new, 2348).
--define(wxPageSetupDialog_destruct, 2349).
--define(wxPageSetupDialog_GetPageSetupData, 2350).
--define(wxPageSetupDialog_ShowModal, 2351).
--define(wxPageSetupDialogData_new_0, 2352).
--define(wxPageSetupDialogData_new_1_0, 2353).
--define(wxPageSetupDialogData_new_1_1, 2354).
--define(wxPageSetupDialogData_destruct, 2355).
--define(wxPageSetupDialogData_EnableHelp, 2356).
--define(wxPageSetupDialogData_EnableMargins, 2357).
--define(wxPageSetupDialogData_EnableOrientation, 2358).
--define(wxPageSetupDialogData_EnablePaper, 2359).
--define(wxPageSetupDialogData_EnablePrinter, 2360).
--define(wxPageSetupDialogData_GetDefaultMinMargins, 2361).
--define(wxPageSetupDialogData_GetEnableMargins, 2362).
--define(wxPageSetupDialogData_GetEnableOrientation, 2363).
--define(wxPageSetupDialogData_GetEnablePaper, 2364).
--define(wxPageSetupDialogData_GetEnablePrinter, 2365).
--define(wxPageSetupDialogData_GetEnableHelp, 2366).
--define(wxPageSetupDialogData_GetDefaultInfo, 2367).
--define(wxPageSetupDialogData_GetMarginTopLeft, 2368).
--define(wxPageSetupDialogData_GetMarginBottomRight, 2369).
--define(wxPageSetupDialogData_GetMinMarginTopLeft, 2370).
--define(wxPageSetupDialogData_GetMinMarginBottomRight, 2371).
--define(wxPageSetupDialogData_GetPaperId, 2372).
--define(wxPageSetupDialogData_GetPaperSize, 2373).
--define(wxPageSetupDialogData_GetPrintData, 2375).
--define(wxPageSetupDialogData_IsOk, 2376).
--define(wxPageSetupDialogData_SetDefaultInfo, 2377).
--define(wxPageSetupDialogData_SetDefaultMinMargins, 2378).
--define(wxPageSetupDialogData_SetMarginTopLeft, 2379).
--define(wxPageSetupDialogData_SetMarginBottomRight, 2380).
--define(wxPageSetupDialogData_SetMinMarginTopLeft, 2381).
--define(wxPageSetupDialogData_SetMinMarginBottomRight, 2382).
--define(wxPageSetupDialogData_SetPaperId, 2383).
--define(wxPageSetupDialogData_SetPaperSize_1_1, 2384).
--define(wxPageSetupDialogData_SetPaperSize_1_0, 2385).
--define(wxPageSetupDialogData_SetPrintData, 2386).
--define(wxPrintDialog_new_2_0, 2387).
--define(wxPrintDialog_new_2_1, 2388).
--define(wxPrintDialog_destruct, 2389).
--define(wxPrintDialog_GetPrintDialogData, 2390).
--define(wxPrintDialog_GetPrintDC, 2391).
--define(wxPrintDialogData_new_0, 2392).
--define(wxPrintDialogData_new_1_1, 2393).
--define(wxPrintDialogData_new_1_0, 2394).
--define(wxPrintDialogData_destruct, 2395).
--define(wxPrintDialogData_EnableHelp, 2396).
--define(wxPrintDialogData_EnablePageNumbers, 2397).
--define(wxPrintDialogData_EnablePrintToFile, 2398).
--define(wxPrintDialogData_EnableSelection, 2399).
--define(wxPrintDialogData_GetAllPages, 2400).
--define(wxPrintDialogData_GetCollate, 2401).
--define(wxPrintDialogData_GetFromPage, 2402).
--define(wxPrintDialogData_GetMaxPage, 2403).
--define(wxPrintDialogData_GetMinPage, 2404).
--define(wxPrintDialogData_GetNoCopies, 2405).
--define(wxPrintDialogData_GetPrintData, 2406).
--define(wxPrintDialogData_GetPrintToFile, 2407).
--define(wxPrintDialogData_GetSelection, 2408).
--define(wxPrintDialogData_GetToPage, 2409).
--define(wxPrintDialogData_IsOk, 2410).
--define(wxPrintDialogData_SetCollate, 2411).
--define(wxPrintDialogData_SetFromPage, 2412).
--define(wxPrintDialogData_SetMaxPage, 2413).
--define(wxPrintDialogData_SetMinPage, 2414).
--define(wxPrintDialogData_SetNoCopies, 2415).
--define(wxPrintDialogData_SetPrintData, 2416).
--define(wxPrintDialogData_SetPrintToFile, 2417).
--define(wxPrintDialogData_SetSelection, 2418).
--define(wxPrintDialogData_SetToPage, 2419).
--define(wxPrintData_new_0, 2420).
--define(wxPrintData_new_1, 2421).
--define(wxPrintData_destruct, 2422).
--define(wxPrintData_GetCollate, 2423).
--define(wxPrintData_GetBin, 2424).
--define(wxPrintData_GetColour, 2425).
--define(wxPrintData_GetDuplex, 2426).
--define(wxPrintData_GetNoCopies, 2427).
--define(wxPrintData_GetOrientation, 2428).
--define(wxPrintData_GetPaperId, 2429).
--define(wxPrintData_GetPrinterName, 2430).
--define(wxPrintData_GetQuality, 2431).
--define(wxPrintData_IsOk, 2432).
--define(wxPrintData_SetBin, 2433).
--define(wxPrintData_SetCollate, 2434).
--define(wxPrintData_SetColour, 2435).
--define(wxPrintData_SetDuplex, 2436).
--define(wxPrintData_SetNoCopies, 2437).
--define(wxPrintData_SetOrientation, 2438).
--define(wxPrintData_SetPaperId, 2439).
--define(wxPrintData_SetPrinterName, 2440).
--define(wxPrintData_SetQuality, 2441).
--define(wxPrintPreview_new_2, 2444).
--define(wxPrintPreview_new_3, 2445).
--define(wxPrintPreview_destruct, 2447).
--define(wxPrintPreview_GetCanvas, 2448).
--define(wxPrintPreview_GetCurrentPage, 2449).
--define(wxPrintPreview_GetFrame, 2450).
--define(wxPrintPreview_GetMaxPage, 2451).
--define(wxPrintPreview_GetMinPage, 2452).
--define(wxPrintPreview_GetPrintout, 2453).
--define(wxPrintPreview_GetPrintoutForPrinting, 2454).
--define(wxPrintPreview_IsOk, 2455).
--define(wxPrintPreview_PaintPage, 2456).
--define(wxPrintPreview_Print, 2457).
--define(wxPrintPreview_RenderPage, 2458).
--define(wxPrintPreview_SetCanvas, 2459).
--define(wxPrintPreview_SetCurrentPage, 2460).
--define(wxPrintPreview_SetFrame, 2461).
--define(wxPrintPreview_SetPrintout, 2462).
--define(wxPrintPreview_SetZoom, 2463).
--define(wxPreviewFrame_new, 2464).
--define(wxPreviewFrame_destruct, 2465).
--define(wxPreviewFrame_CreateControlBar, 2466).
--define(wxPreviewFrame_CreateCanvas, 2467).
--define(wxPreviewFrame_Initialize, 2468).
--define(wxPreviewFrame_OnCloseWindow, 2469).
--define(wxPreviewControlBar_new, 2470).
--define(wxPreviewControlBar_destruct, 2471).
--define(wxPreviewControlBar_CreateButtons, 2472).
--define(wxPreviewControlBar_GetPrintPreview, 2473).
--define(wxPreviewControlBar_GetZoomControl, 2474).
--define(wxPreviewControlBar_SetZoomControl, 2475).
--define(wxPrinter_new, 2477).
--define(wxPrinter_CreateAbortWindow, 2478).
--define(wxPrinter_GetAbort, 2479).
--define(wxPrinter_GetLastError, 2480).
--define(wxPrinter_GetPrintDialogData, 2481).
--define(wxPrinter_Print, 2482).
--define(wxPrinter_PrintDialog, 2483).
--define(wxPrinter_ReportError, 2484).
--define(wxPrinter_Setup, 2485).
--define(wxPrinter_destroy, 2486).
--define(wxXmlResource_new_1, 2487).
--define(wxXmlResource_new_2, 2488).
--define(wxXmlResource_destruct, 2489).
--define(wxXmlResource_AttachUnknownControl, 2490).
--define(wxXmlResource_ClearHandlers, 2491).
--define(wxXmlResource_CompareVersion, 2492).
--define(wxXmlResource_Get, 2493).
--define(wxXmlResource_GetFlags, 2494).
--define(wxXmlResource_GetVersion, 2495).
--define(wxXmlResource_GetXRCID, 2496).
--define(wxXmlResource_InitAllHandlers, 2497).
--define(wxXmlResource_Load, 2498).
--define(wxXmlResource_LoadBitmap, 2499).
--define(wxXmlResource_LoadDialog_2, 2500).
--define(wxXmlResource_LoadDialog_3, 2501).
--define(wxXmlResource_LoadFrame_2, 2502).
--define(wxXmlResource_LoadFrame_3, 2503).
--define(wxXmlResource_LoadIcon, 2504).
--define(wxXmlResource_LoadMenu, 2505).
--define(wxXmlResource_LoadMenuBar_2, 2506).
--define(wxXmlResource_LoadMenuBar_1, 2507).
--define(wxXmlResource_LoadPanel_2, 2508).
--define(wxXmlResource_LoadPanel_3, 2509).
--define(wxXmlResource_LoadToolBar, 2510).
--define(wxXmlResource_Set, 2511).
--define(wxXmlResource_SetFlags, 2512).
--define(wxXmlResource_Unload, 2513).
--define(wxXmlResource_xrcctrl, 2514).
--define(wxHtmlEasyPrinting_new, 2515).
--define(wxHtmlEasyPrinting_destruct, 2516).
--define(wxHtmlEasyPrinting_GetPrintData, 2517).
--define(wxHtmlEasyPrinting_GetPageSetupData, 2518).
--define(wxHtmlEasyPrinting_PreviewFile, 2519).
--define(wxHtmlEasyPrinting_PreviewText, 2520).
--define(wxHtmlEasyPrinting_PrintFile, 2521).
--define(wxHtmlEasyPrinting_PrintText, 2522).
--define(wxHtmlEasyPrinting_PageSetup, 2523).
--define(wxHtmlEasyPrinting_SetFonts, 2524).
--define(wxHtmlEasyPrinting_SetHeader, 2525).
--define(wxHtmlEasyPrinting_SetFooter, 2526).
--define(wxGLCanvas_new_2, 2528).
--define(wxGLCanvas_new_3_1, 2529).
--define(wxGLCanvas_new_3_0, 2530).
--define(wxGLCanvas_GetContext, 2531).
--define(wxGLCanvas_SetCurrent, 2533).
--define(wxGLCanvas_SwapBuffers, 2534).
--define(wxGLCanvas_destroy, 2535).
--define(wxAuiManager_new, 2536).
--define(wxAuiManager_destruct, 2537).
--define(wxAuiManager_AddPane_2_1, 2538).
--define(wxAuiManager_AddPane_3, 2539).
--define(wxAuiManager_AddPane_2_0, 2540).
--define(wxAuiManager_DetachPane, 2541).
--define(wxAuiManager_GetAllPanes, 2542).
--define(wxAuiManager_GetArtProvider, 2543).
--define(wxAuiManager_GetDockSizeConstraint, 2544).
--define(wxAuiManager_GetFlags, 2545).
--define(wxAuiManager_GetManagedWindow, 2546).
--define(wxAuiManager_GetManager, 2547).
--define(wxAuiManager_GetPane_1_1, 2548).
--define(wxAuiManager_GetPane_1_0, 2549).
--define(wxAuiManager_HideHint, 2550).
--define(wxAuiManager_InsertPane, 2551).
--define(wxAuiManager_LoadPaneInfo, 2552).
--define(wxAuiManager_LoadPerspective, 2553).
--define(wxAuiManager_SavePaneInfo, 2554).
--define(wxAuiManager_SavePerspective, 2555).
--define(wxAuiManager_SetArtProvider, 2556).
--define(wxAuiManager_SetDockSizeConstraint, 2557).
--define(wxAuiManager_SetFlags, 2558).
--define(wxAuiManager_SetManagedWindow, 2559).
--define(wxAuiManager_ShowHint, 2560).
--define(wxAuiManager_UnInit, 2561).
--define(wxAuiManager_Update, 2562).
--define(wxAuiPaneInfo_new_0, 2563).
--define(wxAuiPaneInfo_new_1, 2564).
--define(wxAuiPaneInfo_destruct, 2565).
--define(wxAuiPaneInfo_BestSize_1, 2566).
--define(wxAuiPaneInfo_BestSize_2, 2567).
--define(wxAuiPaneInfo_Bottom, 2568).
--define(wxAuiPaneInfo_BottomDockable, 2569).
--define(wxAuiPaneInfo_Caption, 2570).
--define(wxAuiPaneInfo_CaptionVisible, 2571).
--define(wxAuiPaneInfo_Centre, 2572).
--define(wxAuiPaneInfo_CentrePane, 2573).
--define(wxAuiPaneInfo_CloseButton, 2574).
--define(wxAuiPaneInfo_DefaultPane, 2575).
--define(wxAuiPaneInfo_DestroyOnClose, 2576).
--define(wxAuiPaneInfo_Direction, 2577).
--define(wxAuiPaneInfo_Dock, 2578).
--define(wxAuiPaneInfo_Dockable, 2579).
--define(wxAuiPaneInfo_Fixed, 2580).
--define(wxAuiPaneInfo_Float, 2581).
--define(wxAuiPaneInfo_Floatable, 2582).
--define(wxAuiPaneInfo_FloatingPosition_1, 2583).
--define(wxAuiPaneInfo_FloatingPosition_2, 2584).
--define(wxAuiPaneInfo_FloatingSize_1, 2585).
--define(wxAuiPaneInfo_FloatingSize_2, 2586).
--define(wxAuiPaneInfo_Gripper, 2587).
--define(wxAuiPaneInfo_GripperTop, 2588).
--define(wxAuiPaneInfo_HasBorder, 2589).
--define(wxAuiPaneInfo_HasCaption, 2590).
--define(wxAuiPaneInfo_HasCloseButton, 2591).
--define(wxAuiPaneInfo_HasFlag, 2592).
--define(wxAuiPaneInfo_HasGripper, 2593).
--define(wxAuiPaneInfo_HasGripperTop, 2594).
--define(wxAuiPaneInfo_HasMaximizeButton, 2595).
--define(wxAuiPaneInfo_HasMinimizeButton, 2596).
--define(wxAuiPaneInfo_HasPinButton, 2597).
--define(wxAuiPaneInfo_Hide, 2598).
--define(wxAuiPaneInfo_IsBottomDockable, 2599).
--define(wxAuiPaneInfo_IsDocked, 2600).
--define(wxAuiPaneInfo_IsFixed, 2601).
--define(wxAuiPaneInfo_IsFloatable, 2602).
--define(wxAuiPaneInfo_IsFloating, 2603).
--define(wxAuiPaneInfo_IsLeftDockable, 2604).
--define(wxAuiPaneInfo_IsMovable, 2605).
--define(wxAuiPaneInfo_IsOk, 2606).
--define(wxAuiPaneInfo_IsResizable, 2607).
--define(wxAuiPaneInfo_IsRightDockable, 2608).
--define(wxAuiPaneInfo_IsShown, 2609).
--define(wxAuiPaneInfo_IsToolbar, 2610).
--define(wxAuiPaneInfo_IsTopDockable, 2611).
--define(wxAuiPaneInfo_Layer, 2612).
--define(wxAuiPaneInfo_Left, 2613).
--define(wxAuiPaneInfo_LeftDockable, 2614).
--define(wxAuiPaneInfo_MaxSize_1, 2615).
--define(wxAuiPaneInfo_MaxSize_2, 2616).
--define(wxAuiPaneInfo_MaximizeButton, 2617).
--define(wxAuiPaneInfo_MinSize_1, 2618).
--define(wxAuiPaneInfo_MinSize_2, 2619).
--define(wxAuiPaneInfo_MinimizeButton, 2620).
--define(wxAuiPaneInfo_Movable, 2621).
--define(wxAuiPaneInfo_Name, 2622).
--define(wxAuiPaneInfo_PaneBorder, 2623).
--define(wxAuiPaneInfo_PinButton, 2624).
--define(wxAuiPaneInfo_Position, 2625).
--define(wxAuiPaneInfo_Resizable, 2626).
--define(wxAuiPaneInfo_Right, 2627).
--define(wxAuiPaneInfo_RightDockable, 2628).
--define(wxAuiPaneInfo_Row, 2629).
--define(wxAuiPaneInfo_SafeSet, 2630).
--define(wxAuiPaneInfo_SetFlag, 2631).
--define(wxAuiPaneInfo_Show, 2632).
--define(wxAuiPaneInfo_ToolbarPane, 2633).
--define(wxAuiPaneInfo_Top, 2634).
--define(wxAuiPaneInfo_TopDockable, 2635).
--define(wxAuiPaneInfo_Window, 2636).
--define(wxAuiNotebook_new_0, 2637).
--define(wxAuiNotebook_new_2, 2638).
--define(wxAuiNotebook_AddPage, 2639).
--define(wxAuiNotebook_Create, 2640).
--define(wxAuiNotebook_DeletePage, 2641).
--define(wxAuiNotebook_GetArtProvider, 2642).
--define(wxAuiNotebook_GetPage, 2643).
--define(wxAuiNotebook_GetPageBitmap, 2644).
--define(wxAuiNotebook_GetPageCount, 2645).
--define(wxAuiNotebook_GetPageIndex, 2646).
--define(wxAuiNotebook_GetPageText, 2647).
--define(wxAuiNotebook_GetSelection, 2648).
--define(wxAuiNotebook_InsertPage, 2649).
--define(wxAuiNotebook_RemovePage, 2650).
--define(wxAuiNotebook_SetArtProvider, 2651).
--define(wxAuiNotebook_SetFont, 2652).
--define(wxAuiNotebook_SetPageBitmap, 2653).
--define(wxAuiNotebook_SetPageText, 2654).
--define(wxAuiNotebook_SetSelection, 2655).
--define(wxAuiNotebook_SetTabCtrlHeight, 2656).
--define(wxAuiNotebook_SetUniformBitmapSize, 2657).
--define(wxAuiNotebook_destroy, 2658).
--define(wxMDIParentFrame_new_0, 2659).
--define(wxMDIParentFrame_new_4, 2660).
--define(wxMDIParentFrame_destruct, 2661).
--define(wxMDIParentFrame_ActivateNext, 2662).
--define(wxMDIParentFrame_ActivatePrevious, 2663).
--define(wxMDIParentFrame_ArrangeIcons, 2664).
--define(wxMDIParentFrame_Cascade, 2665).
--define(wxMDIParentFrame_Create, 2666).
--define(wxMDIParentFrame_GetActiveChild, 2667).
--define(wxMDIParentFrame_GetClientWindow, 2668).
--define(wxMDIParentFrame_Tile, 2669).
--define(wxMDIChildFrame_new_0, 2670).
--define(wxMDIChildFrame_new_4, 2671).
--define(wxMDIChildFrame_destruct, 2672).
--define(wxMDIChildFrame_Activate, 2673).
--define(wxMDIChildFrame_Create, 2674).
--define(wxMDIChildFrame_Maximize, 2675).
--define(wxMDIChildFrame_Restore, 2676).
--define(wxMDIClientWindow_new_0, 2677).
--define(wxMDIClientWindow_new_2, 2678).
--define(wxMDIClientWindow_destruct, 2679).
--define(wxMDIClientWindow_CreateClient, 2680).
--define(wxLayoutAlgorithm_new, 2681).
--define(wxLayoutAlgorithm_LayoutFrame, 2682).
--define(wxLayoutAlgorithm_LayoutMDIFrame, 2683).
--define(wxLayoutAlgorithm_LayoutWindow, 2684).
--define(wxLayoutAlgorithm_destroy, 2685).
--define(wxEvent_GetId, 2686).
--define(wxEvent_GetSkipped, 2687).
--define(wxEvent_GetTimestamp, 2688).
--define(wxEvent_IsCommandEvent, 2689).
--define(wxEvent_ResumePropagation, 2690).
--define(wxEvent_ShouldPropagate, 2691).
--define(wxEvent_Skip, 2692).
--define(wxEvent_StopPropagation, 2693).
--define(wxCommandEvent_getClientData, 2694).
--define(wxCommandEvent_GetExtraLong, 2695).
--define(wxCommandEvent_GetInt, 2696).
--define(wxCommandEvent_GetSelection, 2697).
--define(wxCommandEvent_GetString, 2698).
--define(wxCommandEvent_IsChecked, 2699).
--define(wxCommandEvent_IsSelection, 2700).
--define(wxCommandEvent_SetInt, 2701).
--define(wxCommandEvent_SetString, 2702).
--define(wxScrollEvent_GetOrientation, 2703).
--define(wxScrollEvent_GetPosition, 2704).
--define(wxScrollWinEvent_GetOrientation, 2705).
--define(wxScrollWinEvent_GetPosition, 2706).
--define(wxMouseEvent_AltDown, 2707).
--define(wxMouseEvent_Button, 2708).
--define(wxMouseEvent_ButtonDClick, 2709).
--define(wxMouseEvent_ButtonDown, 2710).
--define(wxMouseEvent_ButtonUp, 2711).
--define(wxMouseEvent_CmdDown, 2712).
--define(wxMouseEvent_ControlDown, 2713).
--define(wxMouseEvent_Dragging, 2714).
--define(wxMouseEvent_Entering, 2715).
--define(wxMouseEvent_GetButton, 2716).
--define(wxMouseEvent_GetPosition, 2719).
--define(wxMouseEvent_GetLogicalPosition, 2720).
--define(wxMouseEvent_GetLinesPerAction, 2721).
--define(wxMouseEvent_GetWheelRotation, 2722).
--define(wxMouseEvent_GetWheelDelta, 2723).
--define(wxMouseEvent_GetX, 2724).
--define(wxMouseEvent_GetY, 2725).
--define(wxMouseEvent_IsButton, 2726).
--define(wxMouseEvent_IsPageScroll, 2727).
--define(wxMouseEvent_Leaving, 2728).
--define(wxMouseEvent_LeftDClick, 2729).
--define(wxMouseEvent_LeftDown, 2730).
--define(wxMouseEvent_LeftIsDown, 2731).
--define(wxMouseEvent_LeftUp, 2732).
--define(wxMouseEvent_MetaDown, 2733).
--define(wxMouseEvent_MiddleDClick, 2734).
--define(wxMouseEvent_MiddleDown, 2735).
--define(wxMouseEvent_MiddleIsDown, 2736).
--define(wxMouseEvent_MiddleUp, 2737).
--define(wxMouseEvent_Moving, 2738).
--define(wxMouseEvent_RightDClick, 2739).
--define(wxMouseEvent_RightDown, 2740).
--define(wxMouseEvent_RightIsDown, 2741).
--define(wxMouseEvent_RightUp, 2742).
--define(wxMouseEvent_ShiftDown, 2743).
--define(wxSetCursorEvent_GetCursor, 2744).
--define(wxSetCursorEvent_GetX, 2745).
--define(wxSetCursorEvent_GetY, 2746).
--define(wxSetCursorEvent_HasCursor, 2747).
--define(wxSetCursorEvent_SetCursor, 2748).
--define(wxKeyEvent_AltDown, 2749).
--define(wxKeyEvent_CmdDown, 2750).
--define(wxKeyEvent_ControlDown, 2751).
--define(wxKeyEvent_GetKeyCode, 2752).
--define(wxKeyEvent_GetModifiers, 2753).
--define(wxKeyEvent_GetPosition, 2756).
--define(wxKeyEvent_GetRawKeyCode, 2757).
--define(wxKeyEvent_GetRawKeyFlags, 2758).
--define(wxKeyEvent_GetUnicodeKey, 2759).
--define(wxKeyEvent_GetX, 2760).
--define(wxKeyEvent_GetY, 2761).
--define(wxKeyEvent_HasModifiers, 2762).
--define(wxKeyEvent_MetaDown, 2763).
--define(wxKeyEvent_ShiftDown, 2764).
--define(wxSizeEvent_GetSize, 2765).
--define(wxMoveEvent_GetPosition, 2766).
--define(wxEraseEvent_GetDC, 2767).
--define(wxFocusEvent_GetWindow, 2768).
--define(wxChildFocusEvent_GetWindow, 2769).
--define(wxMenuEvent_GetMenu, 2770).
--define(wxMenuEvent_GetMenuId, 2771).
--define(wxMenuEvent_IsPopup, 2772).
--define(wxCloseEvent_CanVeto, 2773).
--define(wxCloseEvent_GetLoggingOff, 2774).
--define(wxCloseEvent_SetCanVeto, 2775).
--define(wxCloseEvent_SetLoggingOff, 2776).
--define(wxCloseEvent_Veto, 2777).
--define(wxShowEvent_SetShow, 2778).
--define(wxShowEvent_GetShow, 2779).
--define(wxIconizeEvent_Iconized, 2780).
--define(wxJoystickEvent_ButtonDown, 2781).
--define(wxJoystickEvent_ButtonIsDown, 2782).
--define(wxJoystickEvent_ButtonUp, 2783).
--define(wxJoystickEvent_GetButtonChange, 2784).
--define(wxJoystickEvent_GetButtonState, 2785).
--define(wxJoystickEvent_GetJoystick, 2786).
--define(wxJoystickEvent_GetPosition, 2787).
--define(wxJoystickEvent_GetZPosition, 2788).
--define(wxJoystickEvent_IsButton, 2789).
--define(wxJoystickEvent_IsMove, 2790).
--define(wxJoystickEvent_IsZMove, 2791).
--define(wxUpdateUIEvent_CanUpdate, 2792).
--define(wxUpdateUIEvent_Check, 2793).
--define(wxUpdateUIEvent_Enable, 2794).
--define(wxUpdateUIEvent_Show, 2795).
--define(wxUpdateUIEvent_GetChecked, 2796).
--define(wxUpdateUIEvent_GetEnabled, 2797).
--define(wxUpdateUIEvent_GetShown, 2798).
--define(wxUpdateUIEvent_GetSetChecked, 2799).
--define(wxUpdateUIEvent_GetSetEnabled, 2800).
--define(wxUpdateUIEvent_GetSetShown, 2801).
--define(wxUpdateUIEvent_GetSetText, 2802).
--define(wxUpdateUIEvent_GetText, 2803).
--define(wxUpdateUIEvent_GetMode, 2804).
--define(wxUpdateUIEvent_GetUpdateInterval, 2805).
--define(wxUpdateUIEvent_ResetUpdateTime, 2806).
--define(wxUpdateUIEvent_SetMode, 2807).
--define(wxUpdateUIEvent_SetText, 2808).
--define(wxUpdateUIEvent_SetUpdateInterval, 2809).
--define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2810).
--define(wxPaletteChangedEvent_SetChangedWindow, 2811).
--define(wxPaletteChangedEvent_GetChangedWindow, 2812).
--define(wxQueryNewPaletteEvent_SetPaletteRealized, 2813).
--define(wxQueryNewPaletteEvent_GetPaletteRealized, 2814).
--define(wxNavigationKeyEvent_GetDirection, 2815).
--define(wxNavigationKeyEvent_SetDirection, 2816).
--define(wxNavigationKeyEvent_IsWindowChange, 2817).
--define(wxNavigationKeyEvent_SetWindowChange, 2818).
--define(wxNavigationKeyEvent_IsFromTab, 2819).
--define(wxNavigationKeyEvent_SetFromTab, 2820).
--define(wxNavigationKeyEvent_GetCurrentFocus, 2821).
--define(wxNavigationKeyEvent_SetCurrentFocus, 2822).
--define(wxHelpEvent_GetOrigin, 2823).
--define(wxHelpEvent_GetPosition, 2824).
--define(wxHelpEvent_SetOrigin, 2825).
--define(wxHelpEvent_SetPosition, 2826).
--define(wxContextMenuEvent_GetPosition, 2827).
--define(wxContextMenuEvent_SetPosition, 2828).
--define(wxIdleEvent_CanSend, 2829).
--define(wxIdleEvent_GetMode, 2830).
--define(wxIdleEvent_RequestMore, 2831).
--define(wxIdleEvent_MoreRequested, 2832).
--define(wxIdleEvent_SetMode, 2833).
--define(wxGridEvent_AltDown, 2834).
--define(wxGridEvent_ControlDown, 2835).
--define(wxGridEvent_GetCol, 2836).
--define(wxGridEvent_GetPosition, 2837).
--define(wxGridEvent_GetRow, 2838).
--define(wxGridEvent_MetaDown, 2839).
--define(wxGridEvent_Selecting, 2840).
--define(wxGridEvent_ShiftDown, 2841).
--define(wxNotifyEvent_Allow, 2842).
--define(wxNotifyEvent_IsAllowed, 2843).
--define(wxNotifyEvent_Veto, 2844).
--define(wxSashEvent_GetEdge, 2845).
--define(wxSashEvent_GetDragRect, 2846).
--define(wxSashEvent_GetDragStatus, 2847).
--define(wxListEvent_GetCacheFrom, 2848).
--define(wxListEvent_GetCacheTo, 2849).
--define(wxListEvent_GetKeyCode, 2850).
--define(wxListEvent_GetIndex, 2851).
--define(wxListEvent_GetColumn, 2852).
--define(wxListEvent_GetPoint, 2853).
--define(wxListEvent_GetLabel, 2854).
--define(wxListEvent_GetText, 2855).
--define(wxListEvent_GetImage, 2856).
--define(wxListEvent_GetData, 2857).
--define(wxListEvent_GetMask, 2858).
--define(wxListEvent_GetItem, 2859).
--define(wxListEvent_IsEditCancelled, 2860).
--define(wxDateEvent_GetDate, 2861).
--define(wxCalendarEvent_GetWeekDay, 2862).
--define(wxFileDirPickerEvent_GetPath, 2863).
--define(wxColourPickerEvent_GetColour, 2864).
--define(wxFontPickerEvent_GetFont, 2865).
--define(wxStyledTextEvent_GetPosition, 2866).
--define(wxStyledTextEvent_GetKey, 2867).
--define(wxStyledTextEvent_GetModifiers, 2868).
--define(wxStyledTextEvent_GetModificationType, 2869).
--define(wxStyledTextEvent_GetText, 2870).
--define(wxStyledTextEvent_GetLength, 2871).
--define(wxStyledTextEvent_GetLinesAdded, 2872).
--define(wxStyledTextEvent_GetLine, 2873).
--define(wxStyledTextEvent_GetFoldLevelNow, 2874).
--define(wxStyledTextEvent_GetFoldLevelPrev, 2875).
--define(wxStyledTextEvent_GetMargin, 2876).
--define(wxStyledTextEvent_GetMessage, 2877).
--define(wxStyledTextEvent_GetWParam, 2878).
--define(wxStyledTextEvent_GetLParam, 2879).
--define(wxStyledTextEvent_GetListType, 2880).
--define(wxStyledTextEvent_GetX, 2881).
--define(wxStyledTextEvent_GetY, 2882).
--define(wxStyledTextEvent_GetDragText, 2883).
--define(wxStyledTextEvent_GetDragAllowMove, 2884).
--define(wxStyledTextEvent_GetDragResult, 2885).
--define(wxStyledTextEvent_GetShift, 2886).
--define(wxStyledTextEvent_GetControl, 2887).
--define(wxStyledTextEvent_GetAlt, 2888).
--define(utils_wxGetKeyState, 2889).
--define(utils_wxGetMousePosition, 2890).
--define(utils_wxGetMouseState, 2891).
--define(utils_wxSetDetectableAutoRepeat, 2892).
--define(utils_wxBell, 2893).
--define(utils_wxFindMenuItemId, 2894).
--define(utils_wxGenericFindWindowAtPoint, 2895).
--define(utils_wxFindWindowAtPoint, 2896).
--define(utils_wxBeginBusyCursor, 2897).
--define(utils_wxEndBusyCursor, 2898).
--define(utils_wxIsBusy, 2899).
--define(utils_wxShutdown, 2900).
--define(utils_wxShell, 2901).
--define(utils_wxLaunchDefaultBrowser, 2902).
--define(utils_wxGetEmailAddress, 2903).
--define(utils_wxGetUserId, 2904).
--define(utils_wxGetHomeDir, 2905).
--define(utils_wxNewId, 2906).
--define(utils_wxRegisterId, 2907).
--define(utils_wxGetCurrentId, 2908).
--define(utils_wxGetOsDescription, 2909).
--define(utils_wxIsPlatformLittleEndian, 2910).
--define(utils_wxIsPlatform64Bit, 2911).
--define(wxPrintout_new, 2912).
--define(wxPrintout_destruct, 2913).
--define(wxPrintout_GetDC, 2914).
--define(wxPrintout_GetPageSizeMM, 2915).
--define(wxPrintout_GetPageSizePixels, 2916).
--define(wxPrintout_GetPaperRectPixels, 2917).
--define(wxPrintout_GetPPIPrinter, 2918).
--define(wxPrintout_GetPPIScreen, 2919).
--define(wxPrintout_GetTitle, 2920).
--define(wxPrintout_IsPreview, 2921).
--define(wxPrintout_FitThisSizeToPaper, 2922).
--define(wxPrintout_FitThisSizeToPage, 2923).
--define(wxPrintout_FitThisSizeToPageMargins, 2924).
--define(wxPrintout_MapScreenSizeToPaper, 2925).
--define(wxPrintout_MapScreenSizeToPage, 2926).
--define(wxPrintout_MapScreenSizeToPageMargins, 2927).
--define(wxPrintout_MapScreenSizeToDevice, 2928).
--define(wxPrintout_GetLogicalPaperRect, 2929).
--define(wxPrintout_GetLogicalPageRect, 2930).
--define(wxPrintout_GetLogicalPageMarginsRect, 2931).
--define(wxPrintout_SetLogicalOrigin, 2932).
--define(wxPrintout_OffsetLogicalOrigin, 2933).
--define(wxStyledTextCtrl_new_2, 2934).
--define(wxStyledTextCtrl_new_0, 2935).
--define(wxStyledTextCtrl_destruct, 2936).
--define(wxStyledTextCtrl_Create, 2937).
--define(wxStyledTextCtrl_AddText, 2938).
--define(wxStyledTextCtrl_AddStyledText, 2939).
--define(wxStyledTextCtrl_InsertText, 2940).
--define(wxStyledTextCtrl_ClearAll, 2941).
--define(wxStyledTextCtrl_ClearDocumentStyle, 2942).
--define(wxStyledTextCtrl_GetLength, 2943).
--define(wxStyledTextCtrl_GetCharAt, 2944).
--define(wxStyledTextCtrl_GetCurrentPos, 2945).
--define(wxStyledTextCtrl_GetAnchor, 2946).
--define(wxStyledTextCtrl_GetStyleAt, 2947).
--define(wxStyledTextCtrl_Redo, 2948).
--define(wxStyledTextCtrl_SetUndoCollection, 2949).
--define(wxStyledTextCtrl_SelectAll, 2950).
--define(wxStyledTextCtrl_SetSavePoint, 2951).
--define(wxStyledTextCtrl_GetStyledText, 2952).
--define(wxStyledTextCtrl_CanRedo, 2953).
--define(wxStyledTextCtrl_MarkerLineFromHandle, 2954).
--define(wxStyledTextCtrl_MarkerDeleteHandle, 2955).
--define(wxStyledTextCtrl_GetUndoCollection, 2956).
--define(wxStyledTextCtrl_GetViewWhiteSpace, 2957).
--define(wxStyledTextCtrl_SetViewWhiteSpace, 2958).
--define(wxStyledTextCtrl_PositionFromPoint, 2959).
--define(wxStyledTextCtrl_PositionFromPointClose, 2960).
--define(wxStyledTextCtrl_GotoLine, 2961).
--define(wxStyledTextCtrl_GotoPos, 2962).
--define(wxStyledTextCtrl_SetAnchor, 2963).
--define(wxStyledTextCtrl_GetCurLine, 2964).
--define(wxStyledTextCtrl_GetEndStyled, 2965).
--define(wxStyledTextCtrl_ConvertEOLs, 2966).
--define(wxStyledTextCtrl_GetEOLMode, 2967).
--define(wxStyledTextCtrl_SetEOLMode, 2968).
--define(wxStyledTextCtrl_StartStyling, 2969).
--define(wxStyledTextCtrl_SetStyling, 2970).
--define(wxStyledTextCtrl_GetBufferedDraw, 2971).
--define(wxStyledTextCtrl_SetBufferedDraw, 2972).
--define(wxStyledTextCtrl_SetTabWidth, 2973).
--define(wxStyledTextCtrl_GetTabWidth, 2974).
--define(wxStyledTextCtrl_SetCodePage, 2975).
--define(wxStyledTextCtrl_MarkerDefine, 2976).
--define(wxStyledTextCtrl_MarkerSetForeground, 2977).
--define(wxStyledTextCtrl_MarkerSetBackground, 2978).
--define(wxStyledTextCtrl_MarkerAdd, 2979).
--define(wxStyledTextCtrl_MarkerDelete, 2980).
--define(wxStyledTextCtrl_MarkerDeleteAll, 2981).
--define(wxStyledTextCtrl_MarkerGet, 2982).
--define(wxStyledTextCtrl_MarkerNext, 2983).
--define(wxStyledTextCtrl_MarkerPrevious, 2984).
--define(wxStyledTextCtrl_MarkerDefineBitmap, 2985).
--define(wxStyledTextCtrl_MarkerAddSet, 2986).
--define(wxStyledTextCtrl_MarkerSetAlpha, 2987).
--define(wxStyledTextCtrl_SetMarginType, 2988).
--define(wxStyledTextCtrl_GetMarginType, 2989).
--define(wxStyledTextCtrl_SetMarginWidth, 2990).
--define(wxStyledTextCtrl_GetMarginWidth, 2991).
--define(wxStyledTextCtrl_SetMarginMask, 2992).
--define(wxStyledTextCtrl_GetMarginMask, 2993).
--define(wxStyledTextCtrl_SetMarginSensitive, 2994).
--define(wxStyledTextCtrl_GetMarginSensitive, 2995).
--define(wxStyledTextCtrl_StyleClearAll, 2996).
--define(wxStyledTextCtrl_StyleSetForeground, 2997).
--define(wxStyledTextCtrl_StyleSetBackground, 2998).
--define(wxStyledTextCtrl_StyleSetBold, 2999).
--define(wxStyledTextCtrl_StyleSetItalic, 3000).
--define(wxStyledTextCtrl_StyleSetSize, 3001).
--define(wxStyledTextCtrl_StyleSetFaceName, 3002).
--define(wxStyledTextCtrl_StyleSetEOLFilled, 3003).
--define(wxStyledTextCtrl_StyleResetDefault, 3004).
--define(wxStyledTextCtrl_StyleSetUnderline, 3005).
--define(wxStyledTextCtrl_StyleSetCase, 3006).
--define(wxStyledTextCtrl_StyleSetHotSpot, 3007).
--define(wxStyledTextCtrl_SetSelForeground, 3008).
--define(wxStyledTextCtrl_SetSelBackground, 3009).
--define(wxStyledTextCtrl_GetSelAlpha, 3010).
--define(wxStyledTextCtrl_SetSelAlpha, 3011).
--define(wxStyledTextCtrl_SetCaretForeground, 3012).
--define(wxStyledTextCtrl_CmdKeyAssign, 3013).
--define(wxStyledTextCtrl_CmdKeyClear, 3014).
--define(wxStyledTextCtrl_CmdKeyClearAll, 3015).
--define(wxStyledTextCtrl_SetStyleBytes, 3016).
--define(wxStyledTextCtrl_StyleSetVisible, 3017).
--define(wxStyledTextCtrl_GetCaretPeriod, 3018).
--define(wxStyledTextCtrl_SetCaretPeriod, 3019).
--define(wxStyledTextCtrl_SetWordChars, 3020).
--define(wxStyledTextCtrl_BeginUndoAction, 3021).
--define(wxStyledTextCtrl_EndUndoAction, 3022).
--define(wxStyledTextCtrl_IndicatorSetStyle, 3023).
--define(wxStyledTextCtrl_IndicatorGetStyle, 3024).
--define(wxStyledTextCtrl_IndicatorSetForeground, 3025).
--define(wxStyledTextCtrl_IndicatorGetForeground, 3026).
--define(wxStyledTextCtrl_SetWhitespaceForeground, 3027).
--define(wxStyledTextCtrl_SetWhitespaceBackground, 3028).
--define(wxStyledTextCtrl_GetStyleBits, 3029).
--define(wxStyledTextCtrl_SetLineState, 3030).
--define(wxStyledTextCtrl_GetLineState, 3031).
--define(wxStyledTextCtrl_GetMaxLineState, 3032).
--define(wxStyledTextCtrl_GetCaretLineVisible, 3033).
--define(wxStyledTextCtrl_SetCaretLineVisible, 3034).
--define(wxStyledTextCtrl_GetCaretLineBackground, 3035).
--define(wxStyledTextCtrl_SetCaretLineBackground, 3036).
--define(wxStyledTextCtrl_AutoCompShow, 3037).
--define(wxStyledTextCtrl_AutoCompCancel, 3038).
--define(wxStyledTextCtrl_AutoCompActive, 3039).
--define(wxStyledTextCtrl_AutoCompPosStart, 3040).
--define(wxStyledTextCtrl_AutoCompComplete, 3041).
--define(wxStyledTextCtrl_AutoCompStops, 3042).
--define(wxStyledTextCtrl_AutoCompSetSeparator, 3043).
--define(wxStyledTextCtrl_AutoCompGetSeparator, 3044).
--define(wxStyledTextCtrl_AutoCompSelect, 3045).
--define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3046).
--define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3047).
--define(wxStyledTextCtrl_AutoCompSetFillUps, 3048).
--define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3049).
--define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3050).
--define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3051).
--define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3052).
--define(wxStyledTextCtrl_UserListShow, 3053).
--define(wxStyledTextCtrl_AutoCompSetAutoHide, 3054).
--define(wxStyledTextCtrl_AutoCompGetAutoHide, 3055).
--define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3056).
--define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3057).
--define(wxStyledTextCtrl_RegisterImage, 3058).
--define(wxStyledTextCtrl_ClearRegisteredImages, 3059).
--define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3060).
--define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3061).
--define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3062).
--define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3063).
--define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3064).
--define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3065).
--define(wxStyledTextCtrl_SetIndent, 3066).
--define(wxStyledTextCtrl_GetIndent, 3067).
--define(wxStyledTextCtrl_SetUseTabs, 3068).
--define(wxStyledTextCtrl_GetUseTabs, 3069).
--define(wxStyledTextCtrl_SetLineIndentation, 3070).
--define(wxStyledTextCtrl_GetLineIndentation, 3071).
--define(wxStyledTextCtrl_GetLineIndentPosition, 3072).
--define(wxStyledTextCtrl_GetColumn, 3073).
--define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3074).
--define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3075).
--define(wxStyledTextCtrl_SetIndentationGuides, 3076).
--define(wxStyledTextCtrl_GetIndentationGuides, 3077).
--define(wxStyledTextCtrl_SetHighlightGuide, 3078).
--define(wxStyledTextCtrl_GetHighlightGuide, 3079).
--define(wxStyledTextCtrl_GetLineEndPosition, 3080).
--define(wxStyledTextCtrl_GetCodePage, 3081).
--define(wxStyledTextCtrl_GetCaretForeground, 3082).
--define(wxStyledTextCtrl_GetReadOnly, 3083).
--define(wxStyledTextCtrl_SetCurrentPos, 3084).
--define(wxStyledTextCtrl_SetSelectionStart, 3085).
--define(wxStyledTextCtrl_GetSelectionStart, 3086).
--define(wxStyledTextCtrl_SetSelectionEnd, 3087).
--define(wxStyledTextCtrl_GetSelectionEnd, 3088).
--define(wxStyledTextCtrl_SetPrintMagnification, 3089).
--define(wxStyledTextCtrl_GetPrintMagnification, 3090).
--define(wxStyledTextCtrl_SetPrintColourMode, 3091).
--define(wxStyledTextCtrl_GetPrintColourMode, 3092).
--define(wxStyledTextCtrl_FindText, 3093).
--define(wxStyledTextCtrl_FormatRange, 3094).
--define(wxStyledTextCtrl_GetFirstVisibleLine, 3095).
--define(wxStyledTextCtrl_GetLine, 3096).
--define(wxStyledTextCtrl_GetLineCount, 3097).
--define(wxStyledTextCtrl_SetMarginLeft, 3098).
--define(wxStyledTextCtrl_GetMarginLeft, 3099).
--define(wxStyledTextCtrl_SetMarginRight, 3100).
--define(wxStyledTextCtrl_GetMarginRight, 3101).
--define(wxStyledTextCtrl_GetModify, 3102).
--define(wxStyledTextCtrl_SetSelection, 3103).
--define(wxStyledTextCtrl_GetSelectedText, 3104).
--define(wxStyledTextCtrl_GetTextRange, 3105).
--define(wxStyledTextCtrl_HideSelection, 3106).
--define(wxStyledTextCtrl_LineFromPosition, 3107).
--define(wxStyledTextCtrl_PositionFromLine, 3108).
--define(wxStyledTextCtrl_LineScroll, 3109).
--define(wxStyledTextCtrl_EnsureCaretVisible, 3110).
--define(wxStyledTextCtrl_ReplaceSelection, 3111).
--define(wxStyledTextCtrl_SetReadOnly, 3112).
--define(wxStyledTextCtrl_CanPaste, 3113).
--define(wxStyledTextCtrl_CanUndo, 3114).
--define(wxStyledTextCtrl_EmptyUndoBuffer, 3115).
--define(wxStyledTextCtrl_Undo, 3116).
--define(wxStyledTextCtrl_Cut, 3117).
--define(wxStyledTextCtrl_Copy, 3118).
--define(wxStyledTextCtrl_Paste, 3119).
--define(wxStyledTextCtrl_Clear, 3120).
--define(wxStyledTextCtrl_SetText, 3121).
--define(wxStyledTextCtrl_GetText, 3122).
--define(wxStyledTextCtrl_GetTextLength, 3123).
--define(wxStyledTextCtrl_GetOvertype, 3124).
--define(wxStyledTextCtrl_SetCaretWidth, 3125).
--define(wxStyledTextCtrl_GetCaretWidth, 3126).
--define(wxStyledTextCtrl_SetTargetStart, 3127).
--define(wxStyledTextCtrl_GetTargetStart, 3128).
--define(wxStyledTextCtrl_SetTargetEnd, 3129).
--define(wxStyledTextCtrl_GetTargetEnd, 3130).
--define(wxStyledTextCtrl_ReplaceTarget, 3131).
--define(wxStyledTextCtrl_SearchInTarget, 3132).
--define(wxStyledTextCtrl_SetSearchFlags, 3133).
--define(wxStyledTextCtrl_GetSearchFlags, 3134).
--define(wxStyledTextCtrl_CallTipShow, 3135).
--define(wxStyledTextCtrl_CallTipCancel, 3136).
--define(wxStyledTextCtrl_CallTipActive, 3137).
--define(wxStyledTextCtrl_CallTipPosAtStart, 3138).
--define(wxStyledTextCtrl_CallTipSetHighlight, 3139).
--define(wxStyledTextCtrl_CallTipSetBackground, 3140).
--define(wxStyledTextCtrl_CallTipSetForeground, 3141).
--define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3142).
--define(wxStyledTextCtrl_CallTipUseStyle, 3143).
--define(wxStyledTextCtrl_VisibleFromDocLine, 3144).
--define(wxStyledTextCtrl_DocLineFromVisible, 3145).
--define(wxStyledTextCtrl_WrapCount, 3146).
--define(wxStyledTextCtrl_SetFoldLevel, 3147).
--define(wxStyledTextCtrl_GetFoldLevel, 3148).
--define(wxStyledTextCtrl_GetLastChild, 3149).
--define(wxStyledTextCtrl_GetFoldParent, 3150).
--define(wxStyledTextCtrl_ShowLines, 3151).
--define(wxStyledTextCtrl_HideLines, 3152).
--define(wxStyledTextCtrl_GetLineVisible, 3153).
--define(wxStyledTextCtrl_SetFoldExpanded, 3154).
--define(wxStyledTextCtrl_GetFoldExpanded, 3155).
--define(wxStyledTextCtrl_ToggleFold, 3156).
--define(wxStyledTextCtrl_EnsureVisible, 3157).
--define(wxStyledTextCtrl_SetFoldFlags, 3158).
--define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3159).
--define(wxStyledTextCtrl_SetTabIndents, 3160).
--define(wxStyledTextCtrl_GetTabIndents, 3161).
--define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3162).
--define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3163).
--define(wxStyledTextCtrl_SetMouseDwellTime, 3164).
--define(wxStyledTextCtrl_GetMouseDwellTime, 3165).
--define(wxStyledTextCtrl_WordStartPosition, 3166).
--define(wxStyledTextCtrl_WordEndPosition, 3167).
--define(wxStyledTextCtrl_SetWrapMode, 3168).
--define(wxStyledTextCtrl_GetWrapMode, 3169).
--define(wxStyledTextCtrl_SetWrapVisualFlags, 3170).
--define(wxStyledTextCtrl_GetWrapVisualFlags, 3171).
--define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3172).
--define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3173).
--define(wxStyledTextCtrl_SetWrapStartIndent, 3174).
--define(wxStyledTextCtrl_GetWrapStartIndent, 3175).
--define(wxStyledTextCtrl_SetLayoutCache, 3176).
--define(wxStyledTextCtrl_GetLayoutCache, 3177).
--define(wxStyledTextCtrl_SetScrollWidth, 3178).
--define(wxStyledTextCtrl_GetScrollWidth, 3179).
--define(wxStyledTextCtrl_TextWidth, 3180).
--define(wxStyledTextCtrl_GetEndAtLastLine, 3181).
--define(wxStyledTextCtrl_TextHeight, 3182).
--define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3183).
--define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3184).
--define(wxStyledTextCtrl_AppendText, 3185).
--define(wxStyledTextCtrl_GetTwoPhaseDraw, 3186).
--define(wxStyledTextCtrl_SetTwoPhaseDraw, 3187).
--define(wxStyledTextCtrl_TargetFromSelection, 3188).
--define(wxStyledTextCtrl_LinesJoin, 3189).
--define(wxStyledTextCtrl_LinesSplit, 3190).
--define(wxStyledTextCtrl_SetFoldMarginColour, 3191).
--define(wxStyledTextCtrl_SetFoldMarginHiColour, 3192).
--define(wxStyledTextCtrl_LineDown, 3193).
--define(wxStyledTextCtrl_LineDownExtend, 3194).
--define(wxStyledTextCtrl_LineUp, 3195).
--define(wxStyledTextCtrl_LineUpExtend, 3196).
--define(wxStyledTextCtrl_CharLeft, 3197).
--define(wxStyledTextCtrl_CharLeftExtend, 3198).
--define(wxStyledTextCtrl_CharRight, 3199).
--define(wxStyledTextCtrl_CharRightExtend, 3200).
--define(wxStyledTextCtrl_WordLeft, 3201).
--define(wxStyledTextCtrl_WordLeftExtend, 3202).
--define(wxStyledTextCtrl_WordRight, 3203).
--define(wxStyledTextCtrl_WordRightExtend, 3204).
--define(wxStyledTextCtrl_Home, 3205).
--define(wxStyledTextCtrl_HomeExtend, 3206).
--define(wxStyledTextCtrl_LineEnd, 3207).
--define(wxStyledTextCtrl_LineEndExtend, 3208).
--define(wxStyledTextCtrl_DocumentStart, 3209).
--define(wxStyledTextCtrl_DocumentStartExtend, 3210).
--define(wxStyledTextCtrl_DocumentEnd, 3211).
--define(wxStyledTextCtrl_DocumentEndExtend, 3212).
--define(wxStyledTextCtrl_PageUp, 3213).
--define(wxStyledTextCtrl_PageUpExtend, 3214).
--define(wxStyledTextCtrl_PageDown, 3215).
--define(wxStyledTextCtrl_PageDownExtend, 3216).
--define(wxStyledTextCtrl_EditToggleOvertype, 3217).
--define(wxStyledTextCtrl_Cancel, 3218).
--define(wxStyledTextCtrl_DeleteBack, 3219).
--define(wxStyledTextCtrl_Tab, 3220).
--define(wxStyledTextCtrl_BackTab, 3221).
--define(wxStyledTextCtrl_NewLine, 3222).
--define(wxStyledTextCtrl_FormFeed, 3223).
--define(wxStyledTextCtrl_VCHome, 3224).
--define(wxStyledTextCtrl_VCHomeExtend, 3225).
--define(wxStyledTextCtrl_ZoomIn, 3226).
--define(wxStyledTextCtrl_ZoomOut, 3227).
--define(wxStyledTextCtrl_DelWordLeft, 3228).
--define(wxStyledTextCtrl_DelWordRight, 3229).
--define(wxStyledTextCtrl_LineCut, 3230).
--define(wxStyledTextCtrl_LineDelete, 3231).
--define(wxStyledTextCtrl_LineTranspose, 3232).
--define(wxStyledTextCtrl_LineDuplicate, 3233).
--define(wxStyledTextCtrl_LowerCase, 3234).
--define(wxStyledTextCtrl_UpperCase, 3235).
--define(wxStyledTextCtrl_LineScrollDown, 3236).
--define(wxStyledTextCtrl_LineScrollUp, 3237).
--define(wxStyledTextCtrl_DeleteBackNotLine, 3238).
--define(wxStyledTextCtrl_HomeDisplay, 3239).
--define(wxStyledTextCtrl_HomeDisplayExtend, 3240).
--define(wxStyledTextCtrl_LineEndDisplay, 3241).
--define(wxStyledTextCtrl_LineEndDisplayExtend, 3242).
--define(wxStyledTextCtrl_HomeWrapExtend, 3243).
--define(wxStyledTextCtrl_LineEndWrap, 3244).
--define(wxStyledTextCtrl_LineEndWrapExtend, 3245).
--define(wxStyledTextCtrl_VCHomeWrap, 3246).
--define(wxStyledTextCtrl_VCHomeWrapExtend, 3247).
--define(wxStyledTextCtrl_LineCopy, 3248).
--define(wxStyledTextCtrl_MoveCaretInsideView, 3249).
--define(wxStyledTextCtrl_LineLength, 3250).
--define(wxStyledTextCtrl_BraceHighlight, 3251).
--define(wxStyledTextCtrl_BraceBadLight, 3252).
--define(wxStyledTextCtrl_BraceMatch, 3253).
--define(wxStyledTextCtrl_GetViewEOL, 3254).
--define(wxStyledTextCtrl_SetViewEOL, 3255).
--define(wxStyledTextCtrl_SetModEventMask, 3256).
--define(wxStyledTextCtrl_GetEdgeColumn, 3257).
--define(wxStyledTextCtrl_SetEdgeColumn, 3258).
--define(wxStyledTextCtrl_GetEdgeMode, 3259).
--define(wxStyledTextCtrl_GetEdgeColour, 3260).
--define(wxStyledTextCtrl_SetEdgeColour, 3261).
--define(wxStyledTextCtrl_SearchAnchor, 3262).
--define(wxStyledTextCtrl_SearchNext, 3263).
--define(wxStyledTextCtrl_SearchPrev, 3264).
--define(wxStyledTextCtrl_LinesOnScreen, 3265).
--define(wxStyledTextCtrl_UsePopUp, 3266).
--define(wxStyledTextCtrl_SelectionIsRectangle, 3267).
--define(wxStyledTextCtrl_SetZoom, 3268).
--define(wxStyledTextCtrl_GetZoom, 3269).
--define(wxStyledTextCtrl_GetModEventMask, 3270).
--define(wxStyledTextCtrl_SetSTCFocus, 3271).
--define(wxStyledTextCtrl_GetSTCFocus, 3272).
--define(wxStyledTextCtrl_SetStatus, 3273).
--define(wxStyledTextCtrl_GetStatus, 3274).
--define(wxStyledTextCtrl_SetMouseDownCaptures, 3275).
--define(wxStyledTextCtrl_GetMouseDownCaptures, 3276).
--define(wxStyledTextCtrl_SetSTCCursor, 3277).
--define(wxStyledTextCtrl_GetSTCCursor, 3278).
--define(wxStyledTextCtrl_SetControlCharSymbol, 3279).
--define(wxStyledTextCtrl_GetControlCharSymbol, 3280).
--define(wxStyledTextCtrl_WordPartLeft, 3281).
--define(wxStyledTextCtrl_WordPartLeftExtend, 3282).
--define(wxStyledTextCtrl_WordPartRight, 3283).
--define(wxStyledTextCtrl_WordPartRightExtend, 3284).
--define(wxStyledTextCtrl_SetVisiblePolicy, 3285).
--define(wxStyledTextCtrl_DelLineLeft, 3286).
--define(wxStyledTextCtrl_DelLineRight, 3287).
--define(wxStyledTextCtrl_GetXOffset, 3288).
--define(wxStyledTextCtrl_ChooseCaretX, 3289).
--define(wxStyledTextCtrl_SetXCaretPolicy, 3290).
--define(wxStyledTextCtrl_SetYCaretPolicy, 3291).
--define(wxStyledTextCtrl_GetPrintWrapMode, 3292).
--define(wxStyledTextCtrl_SetHotspotActiveForeground, 3293).
--define(wxStyledTextCtrl_SetHotspotActiveBackground, 3294).
--define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3295).
--define(wxStyledTextCtrl_SetHotspotSingleLine, 3296).
--define(wxStyledTextCtrl_ParaDownExtend, 3297).
--define(wxStyledTextCtrl_ParaUp, 3298).
--define(wxStyledTextCtrl_ParaUpExtend, 3299).
--define(wxStyledTextCtrl_PositionBefore, 3300).
--define(wxStyledTextCtrl_PositionAfter, 3301).
--define(wxStyledTextCtrl_CopyRange, 3302).
--define(wxStyledTextCtrl_CopyText, 3303).
--define(wxStyledTextCtrl_SetSelectionMode, 3304).
--define(wxStyledTextCtrl_GetSelectionMode, 3305).
--define(wxStyledTextCtrl_LineDownRectExtend, 3306).
--define(wxStyledTextCtrl_LineUpRectExtend, 3307).
--define(wxStyledTextCtrl_CharLeftRectExtend, 3308).
--define(wxStyledTextCtrl_CharRightRectExtend, 3309).
--define(wxStyledTextCtrl_HomeRectExtend, 3310).
--define(wxStyledTextCtrl_VCHomeRectExtend, 3311).
--define(wxStyledTextCtrl_LineEndRectExtend, 3312).
--define(wxStyledTextCtrl_PageUpRectExtend, 3313).
--define(wxStyledTextCtrl_PageDownRectExtend, 3314).
--define(wxStyledTextCtrl_StutteredPageUp, 3315).
--define(wxStyledTextCtrl_StutteredPageUpExtend, 3316).
--define(wxStyledTextCtrl_StutteredPageDown, 3317).
--define(wxStyledTextCtrl_StutteredPageDownExtend, 3318).
--define(wxStyledTextCtrl_WordLeftEnd, 3319).
--define(wxStyledTextCtrl_WordLeftEndExtend, 3320).
--define(wxStyledTextCtrl_WordRightEnd, 3321).
--define(wxStyledTextCtrl_WordRightEndExtend, 3322).
--define(wxStyledTextCtrl_SetWhitespaceChars, 3323).
--define(wxStyledTextCtrl_SetCharsDefault, 3324).
--define(wxStyledTextCtrl_AutoCompGetCurrent, 3325).
--define(wxStyledTextCtrl_Allocate, 3326).
--define(wxStyledTextCtrl_FindColumn, 3327).
--define(wxStyledTextCtrl_GetCaretSticky, 3328).
--define(wxStyledTextCtrl_SetCaretSticky, 3329).
--define(wxStyledTextCtrl_ToggleCaretSticky, 3330).
--define(wxStyledTextCtrl_SetPasteConvertEndings, 3331).
--define(wxStyledTextCtrl_GetPasteConvertEndings, 3332).
--define(wxStyledTextCtrl_SelectionDuplicate, 3333).
--define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3334).
--define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3335).
--define(wxStyledTextCtrl_StartRecord, 3336).
--define(wxStyledTextCtrl_StopRecord, 3337).
--define(wxStyledTextCtrl_SetLexer, 3338).
--define(wxStyledTextCtrl_GetLexer, 3339).
--define(wxStyledTextCtrl_Colourise, 3340).
--define(wxStyledTextCtrl_SetProperty, 3341).
--define(wxStyledTextCtrl_SetKeyWords, 3342).
--define(wxStyledTextCtrl_SetLexerLanguage, 3343).
--define(wxStyledTextCtrl_GetProperty, 3344).
--define(wxStyledTextCtrl_GetStyleBitsNeeded, 3345).
--define(wxStyledTextCtrl_GetCurrentLine, 3346).
--define(wxStyledTextCtrl_StyleSetSpec, 3347).
--define(wxStyledTextCtrl_StyleSetFont, 3348).
--define(wxStyledTextCtrl_StyleSetFontAttr, 3349).
--define(wxStyledTextCtrl_StyleSetCharacterSet, 3350).
--define(wxStyledTextCtrl_StyleSetFontEncoding, 3351).
--define(wxStyledTextCtrl_CmdKeyExecute, 3352).
--define(wxStyledTextCtrl_SetMargins, 3353).
--define(wxStyledTextCtrl_GetSelection, 3354).
--define(wxStyledTextCtrl_PointFromPosition, 3355).
--define(wxStyledTextCtrl_ScrollToLine, 3356).
--define(wxStyledTextCtrl_ScrollToColumn, 3357).
--define(wxStyledTextCtrl_SendMsg, 3358).
--define(wxStyledTextCtrl_SetVScrollBar, 3359).
--define(wxStyledTextCtrl_SetHScrollBar, 3360).
--define(wxStyledTextCtrl_GetLastKeydownProcessed, 3361).
--define(wxStyledTextCtrl_SetLastKeydownProcessed, 3362).
--define(wxStyledTextCtrl_SaveFile, 3363).
--define(wxStyledTextCtrl_LoadFile, 3364).
--define(wxStyledTextCtrl_DoDragOver, 3365).
--define(wxStyledTextCtrl_DoDropText, 3366).
--define(wxStyledTextCtrl_GetUseAntiAliasing, 3367).
--define(wxStyledTextCtrl_AddTextRaw, 3368).
--define(wxStyledTextCtrl_InsertTextRaw, 3369).
--define(wxStyledTextCtrl_GetCurLineRaw, 3370).
--define(wxStyledTextCtrl_GetLineRaw, 3371).
--define(wxStyledTextCtrl_GetSelectedTextRaw, 3372).
--define(wxStyledTextCtrl_GetTextRangeRaw, 3373).
--define(wxStyledTextCtrl_SetTextRaw, 3374).
--define(wxStyledTextCtrl_GetTextRaw, 3375).
--define(wxStyledTextCtrl_AppendTextRaw, 3376).
--define(wxArtProvider_GetBitmap, 3377).
--define(wxArtProvider_GetIcon, 3378).
--define(wxTreeEvent_GetKeyCode, 3379).
--define(wxTreeEvent_GetItem, 3380).
--define(wxTreeEvent_GetKeyEvent, 3381).
--define(wxTreeEvent_GetLabel, 3382).
--define(wxTreeEvent_GetOldItem, 3383).
--define(wxTreeEvent_GetPoint, 3384).
--define(wxTreeEvent_IsEditCancelled, 3385).
--define(wxTreeEvent_SetToolTip, 3386).
--define(wxNotebookEvent_GetOldSelection, 3387).
--define(wxNotebookEvent_GetSelection, 3388).
--define(wxNotebookEvent_SetOldSelection, 3389).
--define(wxNotebookEvent_SetSelection, 3390).
--define(wxFileDataObject_new, 3391).
--define(wxFileDataObject_AddFile, 3392).
--define(wxFileDataObject_GetFilenames, 3393).
--define(wxFileDataObject_destroy, 3394).
--define(wxTextDataObject_new, 3395).
--define(wxTextDataObject_GetTextLength, 3396).
--define(wxTextDataObject_GetText, 3397).
--define(wxTextDataObject_SetText, 3398).
--define(wxTextDataObject_destroy, 3399).
--define(wxBitmapDataObject_new_1_1, 3400).
--define(wxBitmapDataObject_new_1_0, 3401).
--define(wxBitmapDataObject_GetBitmap, 3402).
--define(wxBitmapDataObject_SetBitmap, 3403).
--define(wxBitmapDataObject_destroy, 3404).
--define(wxClipboard_new, 3406).
--define(wxClipboard_destruct, 3407).
--define(wxClipboard_AddData, 3408).
--define(wxClipboard_Clear, 3409).
--define(wxClipboard_Close, 3410).
--define(wxClipboard_Flush, 3411).
--define(wxClipboard_GetData, 3412).
--define(wxClipboard_IsOpened, 3413).
--define(wxClipboard_Open, 3414).
--define(wxClipboard_SetData, 3415).
--define(wxClipboard_UsePrimarySelection, 3417).
--define(wxClipboard_IsSupported, 3418).
--define(wxClipboard_Get, 3419).
--define(wxSpinEvent_GetPosition, 3420).
--define(wxSpinEvent_SetPosition, 3421).
--define(wxSplitterWindow_new_0, 3422).
--define(wxSplitterWindow_new_2, 3423).
--define(wxSplitterWindow_destruct, 3424).
--define(wxSplitterWindow_Create, 3425).
--define(wxSplitterWindow_GetMinimumPaneSize, 3426).
--define(wxSplitterWindow_GetSashGravity, 3427).
--define(wxSplitterWindow_GetSashPosition, 3428).
--define(wxSplitterWindow_GetSplitMode, 3429).
--define(wxSplitterWindow_GetWindow1, 3430).
--define(wxSplitterWindow_GetWindow2, 3431).
--define(wxSplitterWindow_Initialize, 3432).
--define(wxSplitterWindow_IsSplit, 3433).
--define(wxSplitterWindow_ReplaceWindow, 3434).
--define(wxSplitterWindow_SetSashGravity, 3435).
--define(wxSplitterWindow_SetSashPosition, 3436).
--define(wxSplitterWindow_SetSashSize, 3437).
--define(wxSplitterWindow_SetMinimumPaneSize, 3438).
--define(wxSplitterWindow_SetSplitMode, 3439).
--define(wxSplitterWindow_SplitHorizontally, 3440).
--define(wxSplitterWindow_SplitVertically, 3441).
--define(wxSplitterWindow_Unsplit, 3442).
--define(wxSplitterWindow_UpdateSize, 3443).
--define(wxSplitterEvent_GetSashPosition, 3444).
--define(wxSplitterEvent_GetX, 3445).
--define(wxSplitterEvent_GetY, 3446).
--define(wxSplitterEvent_GetWindowBeingRemoved, 3447).
--define(wxSplitterEvent_SetSashPosition, 3448).
--define(wxHtmlWindow_new_0, 3449).
--define(wxHtmlWindow_new_2, 3450).
--define(wxHtmlWindow_AppendToPage, 3451).
--define(wxHtmlWindow_GetOpenedAnchor, 3452).
--define(wxHtmlWindow_GetOpenedPage, 3453).
--define(wxHtmlWindow_GetOpenedPageTitle, 3454).
--define(wxHtmlWindow_GetRelatedFrame, 3455).
--define(wxHtmlWindow_HistoryBack, 3456).
--define(wxHtmlWindow_HistoryCanBack, 3457).
--define(wxHtmlWindow_HistoryCanForward, 3458).
--define(wxHtmlWindow_HistoryClear, 3459).
--define(wxHtmlWindow_HistoryForward, 3460).
--define(wxHtmlWindow_LoadFile, 3461).
--define(wxHtmlWindow_LoadPage, 3462).
--define(wxHtmlWindow_SelectAll, 3463).
--define(wxHtmlWindow_SelectionToText, 3464).
--define(wxHtmlWindow_SelectLine, 3465).
--define(wxHtmlWindow_SelectWord, 3466).
--define(wxHtmlWindow_SetBorders, 3467).
--define(wxHtmlWindow_SetFonts, 3468).
--define(wxHtmlWindow_SetPage, 3469).
--define(wxHtmlWindow_SetRelatedFrame, 3470).
--define(wxHtmlWindow_SetRelatedStatusBar, 3471).
--define(wxHtmlWindow_ToText, 3472).
--define(wxHtmlWindow_destroy, 3473).
--define(wxHtmlLinkEvent_GetLinkInfo, 3474).
--define(wxAuiNotebookEvent_SetSelection, 3475).
--define(wxAuiNotebookEvent_GetSelection, 3476).
--define(wxAuiNotebookEvent_SetOldSelection, 3477).
--define(wxAuiNotebookEvent_GetOldSelection, 3478).
--define(wxAuiNotebookEvent_SetDragSource, 3479).
--define(wxAuiNotebookEvent_GetDragSource, 3480).
--define(wxAuiManagerEvent_SetManager, 3481).
--define(wxAuiManagerEvent_GetManager, 3482).
--define(wxAuiManagerEvent_SetPane, 3483).
--define(wxAuiManagerEvent_GetPane, 3484).
--define(wxAuiManagerEvent_SetButton, 3485).
--define(wxAuiManagerEvent_GetButton, 3486).
--define(wxAuiManagerEvent_SetDC, 3487).
--define(wxAuiManagerEvent_GetDC, 3488).
--define(wxAuiManagerEvent_Veto, 3489).
--define(wxAuiManagerEvent_GetVeto, 3490).
--define(wxAuiManagerEvent_SetCanVeto, 3491).
--define(wxAuiManagerEvent_CanVeto, 3492).
--define(wxLogNull_new, 3493).
--define(wxLogNull_destroy, 3494).
+-define(wxTreeCtrl_EditLabel, 2010).
+-define(wxTreeCtrl_EnsureVisible, 2011).
+-define(wxTreeCtrl_Expand, 2012).
+-define(wxTreeCtrl_GetBoundingRect, 2013).
+-define(wxTreeCtrl_GetChildrenCount, 2015).
+-define(wxTreeCtrl_GetCount, 2016).
+-define(wxTreeCtrl_GetEditControl, 2017).
+-define(wxTreeCtrl_GetFirstChild, 2018).
+-define(wxTreeCtrl_GetNextChild, 2019).
+-define(wxTreeCtrl_GetFirstVisibleItem, 2020).
+-define(wxTreeCtrl_GetImageList, 2021).
+-define(wxTreeCtrl_GetIndent, 2022).
+-define(wxTreeCtrl_GetItemBackgroundColour, 2023).
+-define(wxTreeCtrl_GetItemData, 2024).
+-define(wxTreeCtrl_GetItemFont, 2025).
+-define(wxTreeCtrl_GetItemImage_1, 2026).
+-define(wxTreeCtrl_GetItemImage_2, 2027).
+-define(wxTreeCtrl_GetItemText, 2028).
+-define(wxTreeCtrl_GetItemTextColour, 2029).
+-define(wxTreeCtrl_GetLastChild, 2030).
+-define(wxTreeCtrl_GetNextSibling, 2031).
+-define(wxTreeCtrl_GetNextVisible, 2032).
+-define(wxTreeCtrl_GetItemParent, 2033).
+-define(wxTreeCtrl_GetPrevSibling, 2034).
+-define(wxTreeCtrl_GetPrevVisible, 2035).
+-define(wxTreeCtrl_GetRootItem, 2036).
+-define(wxTreeCtrl_GetSelection, 2037).
+-define(wxTreeCtrl_GetSelections, 2038).
+-define(wxTreeCtrl_GetStateImageList, 2039).
+-define(wxTreeCtrl_HitTest, 2040).
+-define(wxTreeCtrl_InsertItem, 2042).
+-define(wxTreeCtrl_IsBold, 2043).
+-define(wxTreeCtrl_IsExpanded, 2044).
+-define(wxTreeCtrl_IsSelected, 2045).
+-define(wxTreeCtrl_IsVisible, 2046).
+-define(wxTreeCtrl_ItemHasChildren, 2047).
+-define(wxTreeCtrl_PrependItem, 2048).
+-define(wxTreeCtrl_ScrollTo, 2049).
+-define(wxTreeCtrl_SelectItem_1, 2050).
+-define(wxTreeCtrl_SelectItem_2, 2051).
+-define(wxTreeCtrl_SetIndent, 2052).
+-define(wxTreeCtrl_SetImageList, 2053).
+-define(wxTreeCtrl_SetItemBackgroundColour, 2054).
+-define(wxTreeCtrl_SetItemBold, 2055).
+-define(wxTreeCtrl_SetItemData, 2056).
+-define(wxTreeCtrl_SetItemDropHighlight, 2057).
+-define(wxTreeCtrl_SetItemFont, 2058).
+-define(wxTreeCtrl_SetItemHasChildren, 2059).
+-define(wxTreeCtrl_SetItemImage_2, 2060).
+-define(wxTreeCtrl_SetItemImage_3, 2061).
+-define(wxTreeCtrl_SetItemText, 2062).
+-define(wxTreeCtrl_SetItemTextColour, 2063).
+-define(wxTreeCtrl_SetStateImageList, 2064).
+-define(wxTreeCtrl_SetWindowStyle, 2065).
+-define(wxTreeCtrl_SortChildren, 2066).
+-define(wxTreeCtrl_Toggle, 2067).
+-define(wxTreeCtrl_ToggleItemSelection, 2068).
+-define(wxTreeCtrl_Unselect, 2069).
+-define(wxTreeCtrl_UnselectAll, 2070).
+-define(wxTreeCtrl_UnselectItem, 2071).
+-define(wxScrollBar_new_0, 2072).
+-define(wxScrollBar_new_3, 2073).
+-define(wxScrollBar_destruct, 2074).
+-define(wxScrollBar_Create, 2075).
+-define(wxScrollBar_GetRange, 2076).
+-define(wxScrollBar_GetPageSize, 2077).
+-define(wxScrollBar_GetThumbPosition, 2078).
+-define(wxScrollBar_GetThumbSize, 2079).
+-define(wxScrollBar_SetThumbPosition, 2080).
+-define(wxScrollBar_SetScrollbar, 2081).
+-define(wxSpinButton_new_2, 2083).
+-define(wxSpinButton_new_0, 2084).
+-define(wxSpinButton_Create, 2085).
+-define(wxSpinButton_GetMax, 2086).
+-define(wxSpinButton_GetMin, 2087).
+-define(wxSpinButton_GetValue, 2088).
+-define(wxSpinButton_SetRange, 2089).
+-define(wxSpinButton_SetValue, 2090).
+-define(wxSpinButton_destroy, 2091).
+-define(wxSpinCtrl_new_0, 2092).
+-define(wxSpinCtrl_new_2, 2093).
+-define(wxSpinCtrl_Create, 2095).
+-define(wxSpinCtrl_SetValue_1_1, 2098).
+-define(wxSpinCtrl_SetValue_1_0, 2099).
+-define(wxSpinCtrl_GetValue, 2101).
+-define(wxSpinCtrl_SetRange, 2103).
+-define(wxSpinCtrl_SetSelection, 2104).
+-define(wxSpinCtrl_GetMin, 2106).
+-define(wxSpinCtrl_GetMax, 2108).
+-define(wxSpinCtrl_destroy, 2109).
+-define(wxStaticText_new_0, 2110).
+-define(wxStaticText_new_4, 2111).
+-define(wxStaticText_Create, 2112).
+-define(wxStaticText_GetLabel, 2113).
+-define(wxStaticText_SetLabel, 2114).
+-define(wxStaticText_Wrap, 2115).
+-define(wxStaticText_destroy, 2116).
+-define(wxStaticBitmap_new_0, 2117).
+-define(wxStaticBitmap_new_4, 2118).
+-define(wxStaticBitmap_Create, 2119).
+-define(wxStaticBitmap_GetBitmap, 2120).
+-define(wxStaticBitmap_SetBitmap, 2121).
+-define(wxStaticBitmap_destroy, 2122).
+-define(wxRadioBox_new, 2123).
+-define(wxRadioBox_destruct, 2125).
+-define(wxRadioBox_Create, 2126).
+-define(wxRadioBox_Enable_2, 2127).
+-define(wxRadioBox_Enable_1, 2128).
+-define(wxRadioBox_GetSelection, 2129).
+-define(wxRadioBox_GetString, 2130).
+-define(wxRadioBox_SetSelection, 2131).
+-define(wxRadioBox_Show_2, 2132).
+-define(wxRadioBox_Show_1, 2133).
+-define(wxRadioBox_GetColumnCount, 2134).
+-define(wxRadioBox_GetItemHelpText, 2135).
+-define(wxRadioBox_GetItemToolTip, 2136).
+-define(wxRadioBox_GetItemFromPoint, 2138).
+-define(wxRadioBox_GetRowCount, 2139).
+-define(wxRadioBox_IsItemEnabled, 2140).
+-define(wxRadioBox_IsItemShown, 2141).
+-define(wxRadioBox_SetItemHelpText, 2142).
+-define(wxRadioBox_SetItemToolTip, 2143).
+-define(wxRadioButton_new_0, 2144).
+-define(wxRadioButton_new_4, 2145).
+-define(wxRadioButton_Create, 2146).
+-define(wxRadioButton_GetValue, 2147).
+-define(wxRadioButton_SetValue, 2148).
+-define(wxRadioButton_destroy, 2149).
+-define(wxSlider_new_6, 2151).
+-define(wxSlider_new_0, 2152).
+-define(wxSlider_Create, 2153).
+-define(wxSlider_GetLineSize, 2154).
+-define(wxSlider_GetMax, 2155).
+-define(wxSlider_GetMin, 2156).
+-define(wxSlider_GetPageSize, 2157).
+-define(wxSlider_GetThumbLength, 2158).
+-define(wxSlider_GetValue, 2159).
+-define(wxSlider_SetLineSize, 2160).
+-define(wxSlider_SetPageSize, 2161).
+-define(wxSlider_SetRange, 2162).
+-define(wxSlider_SetThumbLength, 2163).
+-define(wxSlider_SetValue, 2164).
+-define(wxSlider_destroy, 2165).
+-define(wxDialog_new_4, 2167).
+-define(wxDialog_new_0, 2168).
+-define(wxDialog_destruct, 2170).
+-define(wxDialog_Create, 2171).
+-define(wxDialog_CreateButtonSizer, 2172).
+-define(wxDialog_CreateStdDialogButtonSizer, 2173).
+-define(wxDialog_EndModal, 2174).
+-define(wxDialog_GetAffirmativeId, 2175).
+-define(wxDialog_GetReturnCode, 2176).
+-define(wxDialog_IsModal, 2177).
+-define(wxDialog_SetAffirmativeId, 2178).
+-define(wxDialog_SetReturnCode, 2179).
+-define(wxDialog_Show, 2180).
+-define(wxDialog_ShowModal, 2181).
+-define(wxColourDialog_new_0, 2182).
+-define(wxColourDialog_new_2, 2183).
+-define(wxColourDialog_destruct, 2184).
+-define(wxColourDialog_Create, 2185).
+-define(wxColourDialog_GetColourData, 2186).
+-define(wxColourData_new_0, 2187).
+-define(wxColourData_new_1, 2188).
+-define(wxColourData_destruct, 2189).
+-define(wxColourData_GetChooseFull, 2190).
+-define(wxColourData_GetColour, 2191).
+-define(wxColourData_GetCustomColour, 2193).
+-define(wxColourData_SetChooseFull, 2194).
+-define(wxColourData_SetColour, 2195).
+-define(wxColourData_SetCustomColour, 2196).
+-define(wxPalette_new_0, 2197).
+-define(wxPalette_new_4, 2198).
+-define(wxPalette_destruct, 2200).
+-define(wxPalette_Create, 2201).
+-define(wxPalette_GetColoursCount, 2202).
+-define(wxPalette_GetPixel, 2203).
+-define(wxPalette_GetRGB, 2204).
+-define(wxPalette_IsOk, 2205).
+-define(wxDirDialog_new, 2209).
+-define(wxDirDialog_destruct, 2210).
+-define(wxDirDialog_GetPath, 2211).
+-define(wxDirDialog_GetMessage, 2212).
+-define(wxDirDialog_SetMessage, 2213).
+-define(wxDirDialog_SetPath, 2214).
+-define(wxFileDialog_new, 2218).
+-define(wxFileDialog_destruct, 2219).
+-define(wxFileDialog_GetDirectory, 2220).
+-define(wxFileDialog_GetFilename, 2221).
+-define(wxFileDialog_GetFilenames, 2222).
+-define(wxFileDialog_GetFilterIndex, 2223).
+-define(wxFileDialog_GetMessage, 2224).
+-define(wxFileDialog_GetPath, 2225).
+-define(wxFileDialog_GetPaths, 2226).
+-define(wxFileDialog_GetWildcard, 2227).
+-define(wxFileDialog_SetDirectory, 2228).
+-define(wxFileDialog_SetFilename, 2229).
+-define(wxFileDialog_SetFilterIndex, 2230).
+-define(wxFileDialog_SetMessage, 2231).
+-define(wxFileDialog_SetPath, 2232).
+-define(wxFileDialog_SetWildcard, 2233).
+-define(wxPickerBase_SetInternalMargin, 2234).
+-define(wxPickerBase_GetInternalMargin, 2235).
+-define(wxPickerBase_SetTextCtrlProportion, 2236).
+-define(wxPickerBase_SetPickerCtrlProportion, 2237).
+-define(wxPickerBase_GetTextCtrlProportion, 2238).
+-define(wxPickerBase_GetPickerCtrlProportion, 2239).
+-define(wxPickerBase_HasTextCtrl, 2240).
+-define(wxPickerBase_GetTextCtrl, 2241).
+-define(wxPickerBase_IsTextCtrlGrowable, 2242).
+-define(wxPickerBase_SetPickerCtrlGrowable, 2243).
+-define(wxPickerBase_SetTextCtrlGrowable, 2244).
+-define(wxPickerBase_IsPickerCtrlGrowable, 2245).
+-define(wxFilePickerCtrl_new_0, 2246).
+-define(wxFilePickerCtrl_new_3, 2247).
+-define(wxFilePickerCtrl_Create, 2248).
+-define(wxFilePickerCtrl_GetPath, 2249).
+-define(wxFilePickerCtrl_SetPath, 2250).
+-define(wxFilePickerCtrl_destroy, 2251).
+-define(wxDirPickerCtrl_new_0, 2252).
+-define(wxDirPickerCtrl_new_3, 2253).
+-define(wxDirPickerCtrl_Create, 2254).
+-define(wxDirPickerCtrl_GetPath, 2255).
+-define(wxDirPickerCtrl_SetPath, 2256).
+-define(wxDirPickerCtrl_destroy, 2257).
+-define(wxColourPickerCtrl_new_0, 2258).
+-define(wxColourPickerCtrl_new_3, 2259).
+-define(wxColourPickerCtrl_Create, 2260).
+-define(wxColourPickerCtrl_GetColour, 2261).
+-define(wxColourPickerCtrl_SetColour_1_1, 2262).
+-define(wxColourPickerCtrl_SetColour_1_0, 2263).
+-define(wxColourPickerCtrl_destroy, 2264).
+-define(wxDatePickerCtrl_new_0, 2265).
+-define(wxDatePickerCtrl_new_3, 2266).
+-define(wxDatePickerCtrl_GetRange, 2267).
+-define(wxDatePickerCtrl_GetValue, 2268).
+-define(wxDatePickerCtrl_SetRange, 2269).
+-define(wxDatePickerCtrl_SetValue, 2270).
+-define(wxDatePickerCtrl_destroy, 2271).
+-define(wxFontPickerCtrl_new_0, 2272).
+-define(wxFontPickerCtrl_new_3, 2273).
+-define(wxFontPickerCtrl_Create, 2274).
+-define(wxFontPickerCtrl_GetSelectedFont, 2275).
+-define(wxFontPickerCtrl_SetSelectedFont, 2276).
+-define(wxFontPickerCtrl_GetMaxPointSize, 2277).
+-define(wxFontPickerCtrl_SetMaxPointSize, 2278).
+-define(wxFontPickerCtrl_destroy, 2279).
+-define(wxFindReplaceDialog_new_0, 2282).
+-define(wxFindReplaceDialog_new_4, 2283).
+-define(wxFindReplaceDialog_destruct, 2284).
+-define(wxFindReplaceDialog_Create, 2285).
+-define(wxFindReplaceDialog_GetData, 2286).
+-define(wxFindReplaceData_new_0, 2287).
+-define(wxFindReplaceData_new_1, 2288).
+-define(wxFindReplaceData_GetFindString, 2289).
+-define(wxFindReplaceData_GetReplaceString, 2290).
+-define(wxFindReplaceData_GetFlags, 2291).
+-define(wxFindReplaceData_SetFlags, 2292).
+-define(wxFindReplaceData_SetFindString, 2293).
+-define(wxFindReplaceData_SetReplaceString, 2294).
+-define(wxFindReplaceData_destroy, 2295).
+-define(wxMultiChoiceDialog_new_0, 2296).
+-define(wxMultiChoiceDialog_new_5, 2298).
+-define(wxMultiChoiceDialog_GetSelections, 2299).
+-define(wxMultiChoiceDialog_SetSelections, 2300).
+-define(wxMultiChoiceDialog_destroy, 2301).
+-define(wxSingleChoiceDialog_new_0, 2302).
+-define(wxSingleChoiceDialog_new_5, 2304).
+-define(wxSingleChoiceDialog_GetSelection, 2305).
+-define(wxSingleChoiceDialog_GetStringSelection, 2306).
+-define(wxSingleChoiceDialog_SetSelection, 2307).
+-define(wxSingleChoiceDialog_destroy, 2308).
+-define(wxTextEntryDialog_new, 2309).
+-define(wxTextEntryDialog_GetValue, 2310).
+-define(wxTextEntryDialog_SetValue, 2311).
+-define(wxTextEntryDialog_destroy, 2312).
+-define(wxPasswordEntryDialog_new, 2313).
+-define(wxPasswordEntryDialog_destroy, 2314).
+-define(wxFontData_new_0, 2315).
+-define(wxFontData_new_1, 2316).
+-define(wxFontData_destruct, 2317).
+-define(wxFontData_EnableEffects, 2318).
+-define(wxFontData_GetAllowSymbols, 2319).
+-define(wxFontData_GetColour, 2320).
+-define(wxFontData_GetChosenFont, 2321).
+-define(wxFontData_GetEnableEffects, 2322).
+-define(wxFontData_GetInitialFont, 2323).
+-define(wxFontData_GetShowHelp, 2324).
+-define(wxFontData_SetAllowSymbols, 2325).
+-define(wxFontData_SetChosenFont, 2326).
+-define(wxFontData_SetColour, 2327).
+-define(wxFontData_SetInitialFont, 2328).
+-define(wxFontData_SetRange, 2329).
+-define(wxFontData_SetShowHelp, 2330).
+-define(wxFontDialog_new_0, 2334).
+-define(wxFontDialog_new_2, 2336).
+-define(wxFontDialog_Create, 2338).
+-define(wxFontDialog_GetFontData, 2339).
+-define(wxFontDialog_destroy, 2341).
+-define(wxProgressDialog_new, 2342).
+-define(wxProgressDialog_destruct, 2343).
+-define(wxProgressDialog_Resume, 2344).
+-define(wxProgressDialog_Update_2, 2345).
+-define(wxProgressDialog_Update_0, 2346).
+-define(wxMessageDialog_new, 2347).
+-define(wxMessageDialog_destruct, 2348).
+-define(wxPageSetupDialog_new, 2349).
+-define(wxPageSetupDialog_destruct, 2350).
+-define(wxPageSetupDialog_GetPageSetupData, 2351).
+-define(wxPageSetupDialog_ShowModal, 2352).
+-define(wxPageSetupDialogData_new_0, 2353).
+-define(wxPageSetupDialogData_new_1_0, 2354).
+-define(wxPageSetupDialogData_new_1_1, 2355).
+-define(wxPageSetupDialogData_destruct, 2356).
+-define(wxPageSetupDialogData_EnableHelp, 2357).
+-define(wxPageSetupDialogData_EnableMargins, 2358).
+-define(wxPageSetupDialogData_EnableOrientation, 2359).
+-define(wxPageSetupDialogData_EnablePaper, 2360).
+-define(wxPageSetupDialogData_EnablePrinter, 2361).
+-define(wxPageSetupDialogData_GetDefaultMinMargins, 2362).
+-define(wxPageSetupDialogData_GetEnableMargins, 2363).
+-define(wxPageSetupDialogData_GetEnableOrientation, 2364).
+-define(wxPageSetupDialogData_GetEnablePaper, 2365).
+-define(wxPageSetupDialogData_GetEnablePrinter, 2366).
+-define(wxPageSetupDialogData_GetEnableHelp, 2367).
+-define(wxPageSetupDialogData_GetDefaultInfo, 2368).
+-define(wxPageSetupDialogData_GetMarginTopLeft, 2369).
+-define(wxPageSetupDialogData_GetMarginBottomRight, 2370).
+-define(wxPageSetupDialogData_GetMinMarginTopLeft, 2371).
+-define(wxPageSetupDialogData_GetMinMarginBottomRight, 2372).
+-define(wxPageSetupDialogData_GetPaperId, 2373).
+-define(wxPageSetupDialogData_GetPaperSize, 2374).
+-define(wxPageSetupDialogData_GetPrintData, 2376).
+-define(wxPageSetupDialogData_IsOk, 2377).
+-define(wxPageSetupDialogData_SetDefaultInfo, 2378).
+-define(wxPageSetupDialogData_SetDefaultMinMargins, 2379).
+-define(wxPageSetupDialogData_SetMarginTopLeft, 2380).
+-define(wxPageSetupDialogData_SetMarginBottomRight, 2381).
+-define(wxPageSetupDialogData_SetMinMarginTopLeft, 2382).
+-define(wxPageSetupDialogData_SetMinMarginBottomRight, 2383).
+-define(wxPageSetupDialogData_SetPaperId, 2384).
+-define(wxPageSetupDialogData_SetPaperSize_1_1, 2385).
+-define(wxPageSetupDialogData_SetPaperSize_1_0, 2386).
+-define(wxPageSetupDialogData_SetPrintData, 2387).
+-define(wxPrintDialog_new_2_0, 2388).
+-define(wxPrintDialog_new_2_1, 2389).
+-define(wxPrintDialog_destruct, 2390).
+-define(wxPrintDialog_GetPrintDialogData, 2391).
+-define(wxPrintDialog_GetPrintDC, 2392).
+-define(wxPrintDialogData_new_0, 2393).
+-define(wxPrintDialogData_new_1_1, 2394).
+-define(wxPrintDialogData_new_1_0, 2395).
+-define(wxPrintDialogData_destruct, 2396).
+-define(wxPrintDialogData_EnableHelp, 2397).
+-define(wxPrintDialogData_EnablePageNumbers, 2398).
+-define(wxPrintDialogData_EnablePrintToFile, 2399).
+-define(wxPrintDialogData_EnableSelection, 2400).
+-define(wxPrintDialogData_GetAllPages, 2401).
+-define(wxPrintDialogData_GetCollate, 2402).
+-define(wxPrintDialogData_GetFromPage, 2403).
+-define(wxPrintDialogData_GetMaxPage, 2404).
+-define(wxPrintDialogData_GetMinPage, 2405).
+-define(wxPrintDialogData_GetNoCopies, 2406).
+-define(wxPrintDialogData_GetPrintData, 2407).
+-define(wxPrintDialogData_GetPrintToFile, 2408).
+-define(wxPrintDialogData_GetSelection, 2409).
+-define(wxPrintDialogData_GetToPage, 2410).
+-define(wxPrintDialogData_IsOk, 2411).
+-define(wxPrintDialogData_SetCollate, 2412).
+-define(wxPrintDialogData_SetFromPage, 2413).
+-define(wxPrintDialogData_SetMaxPage, 2414).
+-define(wxPrintDialogData_SetMinPage, 2415).
+-define(wxPrintDialogData_SetNoCopies, 2416).
+-define(wxPrintDialogData_SetPrintData, 2417).
+-define(wxPrintDialogData_SetPrintToFile, 2418).
+-define(wxPrintDialogData_SetSelection, 2419).
+-define(wxPrintDialogData_SetToPage, 2420).
+-define(wxPrintData_new_0, 2421).
+-define(wxPrintData_new_1, 2422).
+-define(wxPrintData_destruct, 2423).
+-define(wxPrintData_GetCollate, 2424).
+-define(wxPrintData_GetBin, 2425).
+-define(wxPrintData_GetColour, 2426).
+-define(wxPrintData_GetDuplex, 2427).
+-define(wxPrintData_GetNoCopies, 2428).
+-define(wxPrintData_GetOrientation, 2429).
+-define(wxPrintData_GetPaperId, 2430).
+-define(wxPrintData_GetPrinterName, 2431).
+-define(wxPrintData_GetQuality, 2432).
+-define(wxPrintData_IsOk, 2433).
+-define(wxPrintData_SetBin, 2434).
+-define(wxPrintData_SetCollate, 2435).
+-define(wxPrintData_SetColour, 2436).
+-define(wxPrintData_SetDuplex, 2437).
+-define(wxPrintData_SetNoCopies, 2438).
+-define(wxPrintData_SetOrientation, 2439).
+-define(wxPrintData_SetPaperId, 2440).
+-define(wxPrintData_SetPrinterName, 2441).
+-define(wxPrintData_SetQuality, 2442).
+-define(wxPrintPreview_new_2, 2445).
+-define(wxPrintPreview_new_3, 2446).
+-define(wxPrintPreview_destruct, 2448).
+-define(wxPrintPreview_GetCanvas, 2449).
+-define(wxPrintPreview_GetCurrentPage, 2450).
+-define(wxPrintPreview_GetFrame, 2451).
+-define(wxPrintPreview_GetMaxPage, 2452).
+-define(wxPrintPreview_GetMinPage, 2453).
+-define(wxPrintPreview_GetPrintout, 2454).
+-define(wxPrintPreview_GetPrintoutForPrinting, 2455).
+-define(wxPrintPreview_IsOk, 2456).
+-define(wxPrintPreview_PaintPage, 2457).
+-define(wxPrintPreview_Print, 2458).
+-define(wxPrintPreview_RenderPage, 2459).
+-define(wxPrintPreview_SetCanvas, 2460).
+-define(wxPrintPreview_SetCurrentPage, 2461).
+-define(wxPrintPreview_SetFrame, 2462).
+-define(wxPrintPreview_SetPrintout, 2463).
+-define(wxPrintPreview_SetZoom, 2464).
+-define(wxPreviewFrame_new, 2465).
+-define(wxPreviewFrame_destruct, 2466).
+-define(wxPreviewFrame_CreateControlBar, 2467).
+-define(wxPreviewFrame_CreateCanvas, 2468).
+-define(wxPreviewFrame_Initialize, 2469).
+-define(wxPreviewFrame_OnCloseWindow, 2470).
+-define(wxPreviewControlBar_new, 2471).
+-define(wxPreviewControlBar_destruct, 2472).
+-define(wxPreviewControlBar_CreateButtons, 2473).
+-define(wxPreviewControlBar_GetPrintPreview, 2474).
+-define(wxPreviewControlBar_GetZoomControl, 2475).
+-define(wxPreviewControlBar_SetZoomControl, 2476).
+-define(wxPrinter_new, 2478).
+-define(wxPrinter_CreateAbortWindow, 2479).
+-define(wxPrinter_GetAbort, 2480).
+-define(wxPrinter_GetLastError, 2481).
+-define(wxPrinter_GetPrintDialogData, 2482).
+-define(wxPrinter_Print, 2483).
+-define(wxPrinter_PrintDialog, 2484).
+-define(wxPrinter_ReportError, 2485).
+-define(wxPrinter_Setup, 2486).
+-define(wxPrinter_destroy, 2487).
+-define(wxXmlResource_new_1, 2488).
+-define(wxXmlResource_new_2, 2489).
+-define(wxXmlResource_destruct, 2490).
+-define(wxXmlResource_AttachUnknownControl, 2491).
+-define(wxXmlResource_ClearHandlers, 2492).
+-define(wxXmlResource_CompareVersion, 2493).
+-define(wxXmlResource_Get, 2494).
+-define(wxXmlResource_GetFlags, 2495).
+-define(wxXmlResource_GetVersion, 2496).
+-define(wxXmlResource_GetXRCID, 2497).
+-define(wxXmlResource_InitAllHandlers, 2498).
+-define(wxXmlResource_Load, 2499).
+-define(wxXmlResource_LoadBitmap, 2500).
+-define(wxXmlResource_LoadDialog_2, 2501).
+-define(wxXmlResource_LoadDialog_3, 2502).
+-define(wxXmlResource_LoadFrame_2, 2503).
+-define(wxXmlResource_LoadFrame_3, 2504).
+-define(wxXmlResource_LoadIcon, 2505).
+-define(wxXmlResource_LoadMenu, 2506).
+-define(wxXmlResource_LoadMenuBar_2, 2507).
+-define(wxXmlResource_LoadMenuBar_1, 2508).
+-define(wxXmlResource_LoadPanel_2, 2509).
+-define(wxXmlResource_LoadPanel_3, 2510).
+-define(wxXmlResource_LoadToolBar, 2511).
+-define(wxXmlResource_Set, 2512).
+-define(wxXmlResource_SetFlags, 2513).
+-define(wxXmlResource_Unload, 2514).
+-define(wxXmlResource_xrcctrl, 2515).
+-define(wxHtmlEasyPrinting_new, 2516).
+-define(wxHtmlEasyPrinting_destruct, 2517).
+-define(wxHtmlEasyPrinting_GetPrintData, 2518).
+-define(wxHtmlEasyPrinting_GetPageSetupData, 2519).
+-define(wxHtmlEasyPrinting_PreviewFile, 2520).
+-define(wxHtmlEasyPrinting_PreviewText, 2521).
+-define(wxHtmlEasyPrinting_PrintFile, 2522).
+-define(wxHtmlEasyPrinting_PrintText, 2523).
+-define(wxHtmlEasyPrinting_PageSetup, 2524).
+-define(wxHtmlEasyPrinting_SetFonts, 2525).
+-define(wxHtmlEasyPrinting_SetHeader, 2526).
+-define(wxHtmlEasyPrinting_SetFooter, 2527).
+-define(wxGLCanvas_new_2, 2529).
+-define(wxGLCanvas_new_3_1, 2530).
+-define(wxGLCanvas_new_3_0, 2531).
+-define(wxGLCanvas_GetContext, 2532).
+-define(wxGLCanvas_SetCurrent, 2534).
+-define(wxGLCanvas_SwapBuffers, 2535).
+-define(wxGLCanvas_destroy, 2536).
+-define(wxAuiManager_new, 2537).
+-define(wxAuiManager_destruct, 2538).
+-define(wxAuiManager_AddPane_2_1, 2539).
+-define(wxAuiManager_AddPane_3, 2540).
+-define(wxAuiManager_AddPane_2_0, 2541).
+-define(wxAuiManager_DetachPane, 2542).
+-define(wxAuiManager_GetAllPanes, 2543).
+-define(wxAuiManager_GetArtProvider, 2544).
+-define(wxAuiManager_GetDockSizeConstraint, 2545).
+-define(wxAuiManager_GetFlags, 2546).
+-define(wxAuiManager_GetManagedWindow, 2547).
+-define(wxAuiManager_GetManager, 2548).
+-define(wxAuiManager_GetPane_1_1, 2549).
+-define(wxAuiManager_GetPane_1_0, 2550).
+-define(wxAuiManager_HideHint, 2551).
+-define(wxAuiManager_InsertPane, 2552).
+-define(wxAuiManager_LoadPaneInfo, 2553).
+-define(wxAuiManager_LoadPerspective, 2554).
+-define(wxAuiManager_SavePaneInfo, 2555).
+-define(wxAuiManager_SavePerspective, 2556).
+-define(wxAuiManager_SetArtProvider, 2557).
+-define(wxAuiManager_SetDockSizeConstraint, 2558).
+-define(wxAuiManager_SetFlags, 2559).
+-define(wxAuiManager_SetManagedWindow, 2560).
+-define(wxAuiManager_ShowHint, 2561).
+-define(wxAuiManager_UnInit, 2562).
+-define(wxAuiManager_Update, 2563).
+-define(wxAuiPaneInfo_new_0, 2564).
+-define(wxAuiPaneInfo_new_1, 2565).
+-define(wxAuiPaneInfo_destruct, 2566).
+-define(wxAuiPaneInfo_BestSize_1, 2567).
+-define(wxAuiPaneInfo_BestSize_2, 2568).
+-define(wxAuiPaneInfo_Bottom, 2569).
+-define(wxAuiPaneInfo_BottomDockable, 2570).
+-define(wxAuiPaneInfo_Caption, 2571).
+-define(wxAuiPaneInfo_CaptionVisible, 2572).
+-define(wxAuiPaneInfo_Centre, 2573).
+-define(wxAuiPaneInfo_CentrePane, 2574).
+-define(wxAuiPaneInfo_CloseButton, 2575).
+-define(wxAuiPaneInfo_DefaultPane, 2576).
+-define(wxAuiPaneInfo_DestroyOnClose, 2577).
+-define(wxAuiPaneInfo_Direction, 2578).
+-define(wxAuiPaneInfo_Dock, 2579).
+-define(wxAuiPaneInfo_Dockable, 2580).
+-define(wxAuiPaneInfo_Fixed, 2581).
+-define(wxAuiPaneInfo_Float, 2582).
+-define(wxAuiPaneInfo_Floatable, 2583).
+-define(wxAuiPaneInfo_FloatingPosition_1, 2584).
+-define(wxAuiPaneInfo_FloatingPosition_2, 2585).
+-define(wxAuiPaneInfo_FloatingSize_1, 2586).
+-define(wxAuiPaneInfo_FloatingSize_2, 2587).
+-define(wxAuiPaneInfo_Gripper, 2588).
+-define(wxAuiPaneInfo_GripperTop, 2589).
+-define(wxAuiPaneInfo_HasBorder, 2590).
+-define(wxAuiPaneInfo_HasCaption, 2591).
+-define(wxAuiPaneInfo_HasCloseButton, 2592).
+-define(wxAuiPaneInfo_HasFlag, 2593).
+-define(wxAuiPaneInfo_HasGripper, 2594).
+-define(wxAuiPaneInfo_HasGripperTop, 2595).
+-define(wxAuiPaneInfo_HasMaximizeButton, 2596).
+-define(wxAuiPaneInfo_HasMinimizeButton, 2597).
+-define(wxAuiPaneInfo_HasPinButton, 2598).
+-define(wxAuiPaneInfo_Hide, 2599).
+-define(wxAuiPaneInfo_IsBottomDockable, 2600).
+-define(wxAuiPaneInfo_IsDocked, 2601).
+-define(wxAuiPaneInfo_IsFixed, 2602).
+-define(wxAuiPaneInfo_IsFloatable, 2603).
+-define(wxAuiPaneInfo_IsFloating, 2604).
+-define(wxAuiPaneInfo_IsLeftDockable, 2605).
+-define(wxAuiPaneInfo_IsMovable, 2606).
+-define(wxAuiPaneInfo_IsOk, 2607).
+-define(wxAuiPaneInfo_IsResizable, 2608).
+-define(wxAuiPaneInfo_IsRightDockable, 2609).
+-define(wxAuiPaneInfo_IsShown, 2610).
+-define(wxAuiPaneInfo_IsToolbar, 2611).
+-define(wxAuiPaneInfo_IsTopDockable, 2612).
+-define(wxAuiPaneInfo_Layer, 2613).
+-define(wxAuiPaneInfo_Left, 2614).
+-define(wxAuiPaneInfo_LeftDockable, 2615).
+-define(wxAuiPaneInfo_MaxSize_1, 2616).
+-define(wxAuiPaneInfo_MaxSize_2, 2617).
+-define(wxAuiPaneInfo_MaximizeButton, 2618).
+-define(wxAuiPaneInfo_MinSize_1, 2619).
+-define(wxAuiPaneInfo_MinSize_2, 2620).
+-define(wxAuiPaneInfo_MinimizeButton, 2621).
+-define(wxAuiPaneInfo_Movable, 2622).
+-define(wxAuiPaneInfo_Name, 2623).
+-define(wxAuiPaneInfo_PaneBorder, 2624).
+-define(wxAuiPaneInfo_PinButton, 2625).
+-define(wxAuiPaneInfo_Position, 2626).
+-define(wxAuiPaneInfo_Resizable, 2627).
+-define(wxAuiPaneInfo_Right, 2628).
+-define(wxAuiPaneInfo_RightDockable, 2629).
+-define(wxAuiPaneInfo_Row, 2630).
+-define(wxAuiPaneInfo_SafeSet, 2631).
+-define(wxAuiPaneInfo_SetFlag, 2632).
+-define(wxAuiPaneInfo_Show, 2633).
+-define(wxAuiPaneInfo_ToolbarPane, 2634).
+-define(wxAuiPaneInfo_Top, 2635).
+-define(wxAuiPaneInfo_TopDockable, 2636).
+-define(wxAuiPaneInfo_Window, 2637).
+-define(wxAuiNotebook_new_0, 2638).
+-define(wxAuiNotebook_new_2, 2639).
+-define(wxAuiNotebook_AddPage, 2640).
+-define(wxAuiNotebook_Create, 2641).
+-define(wxAuiNotebook_DeletePage, 2642).
+-define(wxAuiNotebook_GetArtProvider, 2643).
+-define(wxAuiNotebook_GetPage, 2644).
+-define(wxAuiNotebook_GetPageBitmap, 2645).
+-define(wxAuiNotebook_GetPageCount, 2646).
+-define(wxAuiNotebook_GetPageIndex, 2647).
+-define(wxAuiNotebook_GetPageText, 2648).
+-define(wxAuiNotebook_GetSelection, 2649).
+-define(wxAuiNotebook_InsertPage, 2650).
+-define(wxAuiNotebook_RemovePage, 2651).
+-define(wxAuiNotebook_SetArtProvider, 2652).
+-define(wxAuiNotebook_SetFont, 2653).
+-define(wxAuiNotebook_SetPageBitmap, 2654).
+-define(wxAuiNotebook_SetPageText, 2655).
+-define(wxAuiNotebook_SetSelection, 2656).
+-define(wxAuiNotebook_SetTabCtrlHeight, 2657).
+-define(wxAuiNotebook_SetUniformBitmapSize, 2658).
+-define(wxAuiNotebook_destroy, 2659).
+-define(wxMDIParentFrame_new_0, 2660).
+-define(wxMDIParentFrame_new_4, 2661).
+-define(wxMDIParentFrame_destruct, 2662).
+-define(wxMDIParentFrame_ActivateNext, 2663).
+-define(wxMDIParentFrame_ActivatePrevious, 2664).
+-define(wxMDIParentFrame_ArrangeIcons, 2665).
+-define(wxMDIParentFrame_Cascade, 2666).
+-define(wxMDIParentFrame_Create, 2667).
+-define(wxMDIParentFrame_GetActiveChild, 2668).
+-define(wxMDIParentFrame_GetClientWindow, 2669).
+-define(wxMDIParentFrame_Tile, 2670).
+-define(wxMDIChildFrame_new_0, 2671).
+-define(wxMDIChildFrame_new_4, 2672).
+-define(wxMDIChildFrame_destruct, 2673).
+-define(wxMDIChildFrame_Activate, 2674).
+-define(wxMDIChildFrame_Create, 2675).
+-define(wxMDIChildFrame_Maximize, 2676).
+-define(wxMDIChildFrame_Restore, 2677).
+-define(wxMDIClientWindow_new_0, 2678).
+-define(wxMDIClientWindow_new_2, 2679).
+-define(wxMDIClientWindow_destruct, 2680).
+-define(wxMDIClientWindow_CreateClient, 2681).
+-define(wxLayoutAlgorithm_new, 2682).
+-define(wxLayoutAlgorithm_LayoutFrame, 2683).
+-define(wxLayoutAlgorithm_LayoutMDIFrame, 2684).
+-define(wxLayoutAlgorithm_LayoutWindow, 2685).
+-define(wxLayoutAlgorithm_destroy, 2686).
+-define(wxEvent_GetId, 2687).
+-define(wxEvent_GetSkipped, 2688).
+-define(wxEvent_GetTimestamp, 2689).
+-define(wxEvent_IsCommandEvent, 2690).
+-define(wxEvent_ResumePropagation, 2691).
+-define(wxEvent_ShouldPropagate, 2692).
+-define(wxEvent_Skip, 2693).
+-define(wxEvent_StopPropagation, 2694).
+-define(wxCommandEvent_getClientData, 2695).
+-define(wxCommandEvent_GetExtraLong, 2696).
+-define(wxCommandEvent_GetInt, 2697).
+-define(wxCommandEvent_GetSelection, 2698).
+-define(wxCommandEvent_GetString, 2699).
+-define(wxCommandEvent_IsChecked, 2700).
+-define(wxCommandEvent_IsSelection, 2701).
+-define(wxCommandEvent_SetInt, 2702).
+-define(wxCommandEvent_SetString, 2703).
+-define(wxScrollEvent_GetOrientation, 2704).
+-define(wxScrollEvent_GetPosition, 2705).
+-define(wxScrollWinEvent_GetOrientation, 2706).
+-define(wxScrollWinEvent_GetPosition, 2707).
+-define(wxMouseEvent_AltDown, 2708).
+-define(wxMouseEvent_Button, 2709).
+-define(wxMouseEvent_ButtonDClick, 2710).
+-define(wxMouseEvent_ButtonDown, 2711).
+-define(wxMouseEvent_ButtonUp, 2712).
+-define(wxMouseEvent_CmdDown, 2713).
+-define(wxMouseEvent_ControlDown, 2714).
+-define(wxMouseEvent_Dragging, 2715).
+-define(wxMouseEvent_Entering, 2716).
+-define(wxMouseEvent_GetButton, 2717).
+-define(wxMouseEvent_GetPosition, 2720).
+-define(wxMouseEvent_GetLogicalPosition, 2721).
+-define(wxMouseEvent_GetLinesPerAction, 2722).
+-define(wxMouseEvent_GetWheelRotation, 2723).
+-define(wxMouseEvent_GetWheelDelta, 2724).
+-define(wxMouseEvent_GetX, 2725).
+-define(wxMouseEvent_GetY, 2726).
+-define(wxMouseEvent_IsButton, 2727).
+-define(wxMouseEvent_IsPageScroll, 2728).
+-define(wxMouseEvent_Leaving, 2729).
+-define(wxMouseEvent_LeftDClick, 2730).
+-define(wxMouseEvent_LeftDown, 2731).
+-define(wxMouseEvent_LeftIsDown, 2732).
+-define(wxMouseEvent_LeftUp, 2733).
+-define(wxMouseEvent_MetaDown, 2734).
+-define(wxMouseEvent_MiddleDClick, 2735).
+-define(wxMouseEvent_MiddleDown, 2736).
+-define(wxMouseEvent_MiddleIsDown, 2737).
+-define(wxMouseEvent_MiddleUp, 2738).
+-define(wxMouseEvent_Moving, 2739).
+-define(wxMouseEvent_RightDClick, 2740).
+-define(wxMouseEvent_RightDown, 2741).
+-define(wxMouseEvent_RightIsDown, 2742).
+-define(wxMouseEvent_RightUp, 2743).
+-define(wxMouseEvent_ShiftDown, 2744).
+-define(wxSetCursorEvent_GetCursor, 2745).
+-define(wxSetCursorEvent_GetX, 2746).
+-define(wxSetCursorEvent_GetY, 2747).
+-define(wxSetCursorEvent_HasCursor, 2748).
+-define(wxSetCursorEvent_SetCursor, 2749).
+-define(wxKeyEvent_AltDown, 2750).
+-define(wxKeyEvent_CmdDown, 2751).
+-define(wxKeyEvent_ControlDown, 2752).
+-define(wxKeyEvent_GetKeyCode, 2753).
+-define(wxKeyEvent_GetModifiers, 2754).
+-define(wxKeyEvent_GetPosition, 2757).
+-define(wxKeyEvent_GetRawKeyCode, 2758).
+-define(wxKeyEvent_GetRawKeyFlags, 2759).
+-define(wxKeyEvent_GetUnicodeKey, 2760).
+-define(wxKeyEvent_GetX, 2761).
+-define(wxKeyEvent_GetY, 2762).
+-define(wxKeyEvent_HasModifiers, 2763).
+-define(wxKeyEvent_MetaDown, 2764).
+-define(wxKeyEvent_ShiftDown, 2765).
+-define(wxSizeEvent_GetSize, 2766).
+-define(wxMoveEvent_GetPosition, 2767).
+-define(wxEraseEvent_GetDC, 2768).
+-define(wxFocusEvent_GetWindow, 2769).
+-define(wxChildFocusEvent_GetWindow, 2770).
+-define(wxMenuEvent_GetMenu, 2771).
+-define(wxMenuEvent_GetMenuId, 2772).
+-define(wxMenuEvent_IsPopup, 2773).
+-define(wxCloseEvent_CanVeto, 2774).
+-define(wxCloseEvent_GetLoggingOff, 2775).
+-define(wxCloseEvent_SetCanVeto, 2776).
+-define(wxCloseEvent_SetLoggingOff, 2777).
+-define(wxCloseEvent_Veto, 2778).
+-define(wxShowEvent_SetShow, 2779).
+-define(wxShowEvent_GetShow, 2780).
+-define(wxIconizeEvent_Iconized, 2781).
+-define(wxJoystickEvent_ButtonDown, 2782).
+-define(wxJoystickEvent_ButtonIsDown, 2783).
+-define(wxJoystickEvent_ButtonUp, 2784).
+-define(wxJoystickEvent_GetButtonChange, 2785).
+-define(wxJoystickEvent_GetButtonState, 2786).
+-define(wxJoystickEvent_GetJoystick, 2787).
+-define(wxJoystickEvent_GetPosition, 2788).
+-define(wxJoystickEvent_GetZPosition, 2789).
+-define(wxJoystickEvent_IsButton, 2790).
+-define(wxJoystickEvent_IsMove, 2791).
+-define(wxJoystickEvent_IsZMove, 2792).
+-define(wxUpdateUIEvent_CanUpdate, 2793).
+-define(wxUpdateUIEvent_Check, 2794).
+-define(wxUpdateUIEvent_Enable, 2795).
+-define(wxUpdateUIEvent_Show, 2796).
+-define(wxUpdateUIEvent_GetChecked, 2797).
+-define(wxUpdateUIEvent_GetEnabled, 2798).
+-define(wxUpdateUIEvent_GetShown, 2799).
+-define(wxUpdateUIEvent_GetSetChecked, 2800).
+-define(wxUpdateUIEvent_GetSetEnabled, 2801).
+-define(wxUpdateUIEvent_GetSetShown, 2802).
+-define(wxUpdateUIEvent_GetSetText, 2803).
+-define(wxUpdateUIEvent_GetText, 2804).
+-define(wxUpdateUIEvent_GetMode, 2805).
+-define(wxUpdateUIEvent_GetUpdateInterval, 2806).
+-define(wxUpdateUIEvent_ResetUpdateTime, 2807).
+-define(wxUpdateUIEvent_SetMode, 2808).
+-define(wxUpdateUIEvent_SetText, 2809).
+-define(wxUpdateUIEvent_SetUpdateInterval, 2810).
+-define(wxMouseCaptureChangedEvent_GetCapturedWindow, 2811).
+-define(wxPaletteChangedEvent_SetChangedWindow, 2812).
+-define(wxPaletteChangedEvent_GetChangedWindow, 2813).
+-define(wxQueryNewPaletteEvent_SetPaletteRealized, 2814).
+-define(wxQueryNewPaletteEvent_GetPaletteRealized, 2815).
+-define(wxNavigationKeyEvent_GetDirection, 2816).
+-define(wxNavigationKeyEvent_SetDirection, 2817).
+-define(wxNavigationKeyEvent_IsWindowChange, 2818).
+-define(wxNavigationKeyEvent_SetWindowChange, 2819).
+-define(wxNavigationKeyEvent_IsFromTab, 2820).
+-define(wxNavigationKeyEvent_SetFromTab, 2821).
+-define(wxNavigationKeyEvent_GetCurrentFocus, 2822).
+-define(wxNavigationKeyEvent_SetCurrentFocus, 2823).
+-define(wxHelpEvent_GetOrigin, 2824).
+-define(wxHelpEvent_GetPosition, 2825).
+-define(wxHelpEvent_SetOrigin, 2826).
+-define(wxHelpEvent_SetPosition, 2827).
+-define(wxContextMenuEvent_GetPosition, 2828).
+-define(wxContextMenuEvent_SetPosition, 2829).
+-define(wxIdleEvent_CanSend, 2830).
+-define(wxIdleEvent_GetMode, 2831).
+-define(wxIdleEvent_RequestMore, 2832).
+-define(wxIdleEvent_MoreRequested, 2833).
+-define(wxIdleEvent_SetMode, 2834).
+-define(wxGridEvent_AltDown, 2835).
+-define(wxGridEvent_ControlDown, 2836).
+-define(wxGridEvent_GetCol, 2837).
+-define(wxGridEvent_GetPosition, 2838).
+-define(wxGridEvent_GetRow, 2839).
+-define(wxGridEvent_MetaDown, 2840).
+-define(wxGridEvent_Selecting, 2841).
+-define(wxGridEvent_ShiftDown, 2842).
+-define(wxNotifyEvent_Allow, 2843).
+-define(wxNotifyEvent_IsAllowed, 2844).
+-define(wxNotifyEvent_Veto, 2845).
+-define(wxSashEvent_GetEdge, 2846).
+-define(wxSashEvent_GetDragRect, 2847).
+-define(wxSashEvent_GetDragStatus, 2848).
+-define(wxListEvent_GetCacheFrom, 2849).
+-define(wxListEvent_GetCacheTo, 2850).
+-define(wxListEvent_GetKeyCode, 2851).
+-define(wxListEvent_GetIndex, 2852).
+-define(wxListEvent_GetColumn, 2853).
+-define(wxListEvent_GetPoint, 2854).
+-define(wxListEvent_GetLabel, 2855).
+-define(wxListEvent_GetText, 2856).
+-define(wxListEvent_GetImage, 2857).
+-define(wxListEvent_GetData, 2858).
+-define(wxListEvent_GetMask, 2859).
+-define(wxListEvent_GetItem, 2860).
+-define(wxListEvent_IsEditCancelled, 2861).
+-define(wxDateEvent_GetDate, 2862).
+-define(wxCalendarEvent_GetWeekDay, 2863).
+-define(wxFileDirPickerEvent_GetPath, 2864).
+-define(wxColourPickerEvent_GetColour, 2865).
+-define(wxFontPickerEvent_GetFont, 2866).
+-define(wxStyledTextEvent_GetPosition, 2867).
+-define(wxStyledTextEvent_GetKey, 2868).
+-define(wxStyledTextEvent_GetModifiers, 2869).
+-define(wxStyledTextEvent_GetModificationType, 2870).
+-define(wxStyledTextEvent_GetText, 2871).
+-define(wxStyledTextEvent_GetLength, 2872).
+-define(wxStyledTextEvent_GetLinesAdded, 2873).
+-define(wxStyledTextEvent_GetLine, 2874).
+-define(wxStyledTextEvent_GetFoldLevelNow, 2875).
+-define(wxStyledTextEvent_GetFoldLevelPrev, 2876).
+-define(wxStyledTextEvent_GetMargin, 2877).
+-define(wxStyledTextEvent_GetMessage, 2878).
+-define(wxStyledTextEvent_GetWParam, 2879).
+-define(wxStyledTextEvent_GetLParam, 2880).
+-define(wxStyledTextEvent_GetListType, 2881).
+-define(wxStyledTextEvent_GetX, 2882).
+-define(wxStyledTextEvent_GetY, 2883).
+-define(wxStyledTextEvent_GetDragText, 2884).
+-define(wxStyledTextEvent_GetDragAllowMove, 2885).
+-define(wxStyledTextEvent_GetDragResult, 2886).
+-define(wxStyledTextEvent_GetShift, 2887).
+-define(wxStyledTextEvent_GetControl, 2888).
+-define(wxStyledTextEvent_GetAlt, 2889).
+-define(utils_wxGetKeyState, 2890).
+-define(utils_wxGetMousePosition, 2891).
+-define(utils_wxGetMouseState, 2892).
+-define(utils_wxSetDetectableAutoRepeat, 2893).
+-define(utils_wxBell, 2894).
+-define(utils_wxFindMenuItemId, 2895).
+-define(utils_wxGenericFindWindowAtPoint, 2896).
+-define(utils_wxFindWindowAtPoint, 2897).
+-define(utils_wxBeginBusyCursor, 2898).
+-define(utils_wxEndBusyCursor, 2899).
+-define(utils_wxIsBusy, 2900).
+-define(utils_wxShutdown, 2901).
+-define(utils_wxShell, 2902).
+-define(utils_wxLaunchDefaultBrowser, 2903).
+-define(utils_wxGetEmailAddress, 2904).
+-define(utils_wxGetUserId, 2905).
+-define(utils_wxGetHomeDir, 2906).
+-define(utils_wxNewId, 2907).
+-define(utils_wxRegisterId, 2908).
+-define(utils_wxGetCurrentId, 2909).
+-define(utils_wxGetOsDescription, 2910).
+-define(utils_wxIsPlatformLittleEndian, 2911).
+-define(utils_wxIsPlatform64Bit, 2912).
+-define(wxPrintout_new, 2913).
+-define(wxPrintout_destruct, 2914).
+-define(wxPrintout_GetDC, 2915).
+-define(wxPrintout_GetPageSizeMM, 2916).
+-define(wxPrintout_GetPageSizePixels, 2917).
+-define(wxPrintout_GetPaperRectPixels, 2918).
+-define(wxPrintout_GetPPIPrinter, 2919).
+-define(wxPrintout_GetPPIScreen, 2920).
+-define(wxPrintout_GetTitle, 2921).
+-define(wxPrintout_IsPreview, 2922).
+-define(wxPrintout_FitThisSizeToPaper, 2923).
+-define(wxPrintout_FitThisSizeToPage, 2924).
+-define(wxPrintout_FitThisSizeToPageMargins, 2925).
+-define(wxPrintout_MapScreenSizeToPaper, 2926).
+-define(wxPrintout_MapScreenSizeToPage, 2927).
+-define(wxPrintout_MapScreenSizeToPageMargins, 2928).
+-define(wxPrintout_MapScreenSizeToDevice, 2929).
+-define(wxPrintout_GetLogicalPaperRect, 2930).
+-define(wxPrintout_GetLogicalPageRect, 2931).
+-define(wxPrintout_GetLogicalPageMarginsRect, 2932).
+-define(wxPrintout_SetLogicalOrigin, 2933).
+-define(wxPrintout_OffsetLogicalOrigin, 2934).
+-define(wxStyledTextCtrl_new_2, 2935).
+-define(wxStyledTextCtrl_new_0, 2936).
+-define(wxStyledTextCtrl_destruct, 2937).
+-define(wxStyledTextCtrl_Create, 2938).
+-define(wxStyledTextCtrl_AddText, 2939).
+-define(wxStyledTextCtrl_AddStyledText, 2940).
+-define(wxStyledTextCtrl_InsertText, 2941).
+-define(wxStyledTextCtrl_ClearAll, 2942).
+-define(wxStyledTextCtrl_ClearDocumentStyle, 2943).
+-define(wxStyledTextCtrl_GetLength, 2944).
+-define(wxStyledTextCtrl_GetCharAt, 2945).
+-define(wxStyledTextCtrl_GetCurrentPos, 2946).
+-define(wxStyledTextCtrl_GetAnchor, 2947).
+-define(wxStyledTextCtrl_GetStyleAt, 2948).
+-define(wxStyledTextCtrl_Redo, 2949).
+-define(wxStyledTextCtrl_SetUndoCollection, 2950).
+-define(wxStyledTextCtrl_SelectAll, 2951).
+-define(wxStyledTextCtrl_SetSavePoint, 2952).
+-define(wxStyledTextCtrl_GetStyledText, 2953).
+-define(wxStyledTextCtrl_CanRedo, 2954).
+-define(wxStyledTextCtrl_MarkerLineFromHandle, 2955).
+-define(wxStyledTextCtrl_MarkerDeleteHandle, 2956).
+-define(wxStyledTextCtrl_GetUndoCollection, 2957).
+-define(wxStyledTextCtrl_GetViewWhiteSpace, 2958).
+-define(wxStyledTextCtrl_SetViewWhiteSpace, 2959).
+-define(wxStyledTextCtrl_PositionFromPoint, 2960).
+-define(wxStyledTextCtrl_PositionFromPointClose, 2961).
+-define(wxStyledTextCtrl_GotoLine, 2962).
+-define(wxStyledTextCtrl_GotoPos, 2963).
+-define(wxStyledTextCtrl_SetAnchor, 2964).
+-define(wxStyledTextCtrl_GetCurLine, 2965).
+-define(wxStyledTextCtrl_GetEndStyled, 2966).
+-define(wxStyledTextCtrl_ConvertEOLs, 2967).
+-define(wxStyledTextCtrl_GetEOLMode, 2968).
+-define(wxStyledTextCtrl_SetEOLMode, 2969).
+-define(wxStyledTextCtrl_StartStyling, 2970).
+-define(wxStyledTextCtrl_SetStyling, 2971).
+-define(wxStyledTextCtrl_GetBufferedDraw, 2972).
+-define(wxStyledTextCtrl_SetBufferedDraw, 2973).
+-define(wxStyledTextCtrl_SetTabWidth, 2974).
+-define(wxStyledTextCtrl_GetTabWidth, 2975).
+-define(wxStyledTextCtrl_SetCodePage, 2976).
+-define(wxStyledTextCtrl_MarkerDefine, 2977).
+-define(wxStyledTextCtrl_MarkerSetForeground, 2978).
+-define(wxStyledTextCtrl_MarkerSetBackground, 2979).
+-define(wxStyledTextCtrl_MarkerAdd, 2980).
+-define(wxStyledTextCtrl_MarkerDelete, 2981).
+-define(wxStyledTextCtrl_MarkerDeleteAll, 2982).
+-define(wxStyledTextCtrl_MarkerGet, 2983).
+-define(wxStyledTextCtrl_MarkerNext, 2984).
+-define(wxStyledTextCtrl_MarkerPrevious, 2985).
+-define(wxStyledTextCtrl_MarkerDefineBitmap, 2986).
+-define(wxStyledTextCtrl_MarkerAddSet, 2987).
+-define(wxStyledTextCtrl_MarkerSetAlpha, 2988).
+-define(wxStyledTextCtrl_SetMarginType, 2989).
+-define(wxStyledTextCtrl_GetMarginType, 2990).
+-define(wxStyledTextCtrl_SetMarginWidth, 2991).
+-define(wxStyledTextCtrl_GetMarginWidth, 2992).
+-define(wxStyledTextCtrl_SetMarginMask, 2993).
+-define(wxStyledTextCtrl_GetMarginMask, 2994).
+-define(wxStyledTextCtrl_SetMarginSensitive, 2995).
+-define(wxStyledTextCtrl_GetMarginSensitive, 2996).
+-define(wxStyledTextCtrl_StyleClearAll, 2997).
+-define(wxStyledTextCtrl_StyleSetForeground, 2998).
+-define(wxStyledTextCtrl_StyleSetBackground, 2999).
+-define(wxStyledTextCtrl_StyleSetBold, 3000).
+-define(wxStyledTextCtrl_StyleSetItalic, 3001).
+-define(wxStyledTextCtrl_StyleSetSize, 3002).
+-define(wxStyledTextCtrl_StyleSetFaceName, 3003).
+-define(wxStyledTextCtrl_StyleSetEOLFilled, 3004).
+-define(wxStyledTextCtrl_StyleResetDefault, 3005).
+-define(wxStyledTextCtrl_StyleSetUnderline, 3006).
+-define(wxStyledTextCtrl_StyleSetCase, 3007).
+-define(wxStyledTextCtrl_StyleSetHotSpot, 3008).
+-define(wxStyledTextCtrl_SetSelForeground, 3009).
+-define(wxStyledTextCtrl_SetSelBackground, 3010).
+-define(wxStyledTextCtrl_GetSelAlpha, 3011).
+-define(wxStyledTextCtrl_SetSelAlpha, 3012).
+-define(wxStyledTextCtrl_SetCaretForeground, 3013).
+-define(wxStyledTextCtrl_CmdKeyAssign, 3014).
+-define(wxStyledTextCtrl_CmdKeyClear, 3015).
+-define(wxStyledTextCtrl_CmdKeyClearAll, 3016).
+-define(wxStyledTextCtrl_SetStyleBytes, 3017).
+-define(wxStyledTextCtrl_StyleSetVisible, 3018).
+-define(wxStyledTextCtrl_GetCaretPeriod, 3019).
+-define(wxStyledTextCtrl_SetCaretPeriod, 3020).
+-define(wxStyledTextCtrl_SetWordChars, 3021).
+-define(wxStyledTextCtrl_BeginUndoAction, 3022).
+-define(wxStyledTextCtrl_EndUndoAction, 3023).
+-define(wxStyledTextCtrl_IndicatorSetStyle, 3024).
+-define(wxStyledTextCtrl_IndicatorGetStyle, 3025).
+-define(wxStyledTextCtrl_IndicatorSetForeground, 3026).
+-define(wxStyledTextCtrl_IndicatorGetForeground, 3027).
+-define(wxStyledTextCtrl_SetWhitespaceForeground, 3028).
+-define(wxStyledTextCtrl_SetWhitespaceBackground, 3029).
+-define(wxStyledTextCtrl_GetStyleBits, 3030).
+-define(wxStyledTextCtrl_SetLineState, 3031).
+-define(wxStyledTextCtrl_GetLineState, 3032).
+-define(wxStyledTextCtrl_GetMaxLineState, 3033).
+-define(wxStyledTextCtrl_GetCaretLineVisible, 3034).
+-define(wxStyledTextCtrl_SetCaretLineVisible, 3035).
+-define(wxStyledTextCtrl_GetCaretLineBackground, 3036).
+-define(wxStyledTextCtrl_SetCaretLineBackground, 3037).
+-define(wxStyledTextCtrl_AutoCompShow, 3038).
+-define(wxStyledTextCtrl_AutoCompCancel, 3039).
+-define(wxStyledTextCtrl_AutoCompActive, 3040).
+-define(wxStyledTextCtrl_AutoCompPosStart, 3041).
+-define(wxStyledTextCtrl_AutoCompComplete, 3042).
+-define(wxStyledTextCtrl_AutoCompStops, 3043).
+-define(wxStyledTextCtrl_AutoCompSetSeparator, 3044).
+-define(wxStyledTextCtrl_AutoCompGetSeparator, 3045).
+-define(wxStyledTextCtrl_AutoCompSelect, 3046).
+-define(wxStyledTextCtrl_AutoCompSetCancelAtStart, 3047).
+-define(wxStyledTextCtrl_AutoCompGetCancelAtStart, 3048).
+-define(wxStyledTextCtrl_AutoCompSetFillUps, 3049).
+-define(wxStyledTextCtrl_AutoCompSetChooseSingle, 3050).
+-define(wxStyledTextCtrl_AutoCompGetChooseSingle, 3051).
+-define(wxStyledTextCtrl_AutoCompSetIgnoreCase, 3052).
+-define(wxStyledTextCtrl_AutoCompGetIgnoreCase, 3053).
+-define(wxStyledTextCtrl_UserListShow, 3054).
+-define(wxStyledTextCtrl_AutoCompSetAutoHide, 3055).
+-define(wxStyledTextCtrl_AutoCompGetAutoHide, 3056).
+-define(wxStyledTextCtrl_AutoCompSetDropRestOfWord, 3057).
+-define(wxStyledTextCtrl_AutoCompGetDropRestOfWord, 3058).
+-define(wxStyledTextCtrl_RegisterImage, 3059).
+-define(wxStyledTextCtrl_ClearRegisteredImages, 3060).
+-define(wxStyledTextCtrl_AutoCompGetTypeSeparator, 3061).
+-define(wxStyledTextCtrl_AutoCompSetTypeSeparator, 3062).
+-define(wxStyledTextCtrl_AutoCompSetMaxWidth, 3063).
+-define(wxStyledTextCtrl_AutoCompGetMaxWidth, 3064).
+-define(wxStyledTextCtrl_AutoCompSetMaxHeight, 3065).
+-define(wxStyledTextCtrl_AutoCompGetMaxHeight, 3066).
+-define(wxStyledTextCtrl_SetIndent, 3067).
+-define(wxStyledTextCtrl_GetIndent, 3068).
+-define(wxStyledTextCtrl_SetUseTabs, 3069).
+-define(wxStyledTextCtrl_GetUseTabs, 3070).
+-define(wxStyledTextCtrl_SetLineIndentation, 3071).
+-define(wxStyledTextCtrl_GetLineIndentation, 3072).
+-define(wxStyledTextCtrl_GetLineIndentPosition, 3073).
+-define(wxStyledTextCtrl_GetColumn, 3074).
+-define(wxStyledTextCtrl_SetUseHorizontalScrollBar, 3075).
+-define(wxStyledTextCtrl_GetUseHorizontalScrollBar, 3076).
+-define(wxStyledTextCtrl_SetIndentationGuides, 3077).
+-define(wxStyledTextCtrl_GetIndentationGuides, 3078).
+-define(wxStyledTextCtrl_SetHighlightGuide, 3079).
+-define(wxStyledTextCtrl_GetHighlightGuide, 3080).
+-define(wxStyledTextCtrl_GetLineEndPosition, 3081).
+-define(wxStyledTextCtrl_GetCodePage, 3082).
+-define(wxStyledTextCtrl_GetCaretForeground, 3083).
+-define(wxStyledTextCtrl_GetReadOnly, 3084).
+-define(wxStyledTextCtrl_SetCurrentPos, 3085).
+-define(wxStyledTextCtrl_SetSelectionStart, 3086).
+-define(wxStyledTextCtrl_GetSelectionStart, 3087).
+-define(wxStyledTextCtrl_SetSelectionEnd, 3088).
+-define(wxStyledTextCtrl_GetSelectionEnd, 3089).
+-define(wxStyledTextCtrl_SetPrintMagnification, 3090).
+-define(wxStyledTextCtrl_GetPrintMagnification, 3091).
+-define(wxStyledTextCtrl_SetPrintColourMode, 3092).
+-define(wxStyledTextCtrl_GetPrintColourMode, 3093).
+-define(wxStyledTextCtrl_FindText, 3094).
+-define(wxStyledTextCtrl_FormatRange, 3095).
+-define(wxStyledTextCtrl_GetFirstVisibleLine, 3096).
+-define(wxStyledTextCtrl_GetLine, 3097).
+-define(wxStyledTextCtrl_GetLineCount, 3098).
+-define(wxStyledTextCtrl_SetMarginLeft, 3099).
+-define(wxStyledTextCtrl_GetMarginLeft, 3100).
+-define(wxStyledTextCtrl_SetMarginRight, 3101).
+-define(wxStyledTextCtrl_GetMarginRight, 3102).
+-define(wxStyledTextCtrl_GetModify, 3103).
+-define(wxStyledTextCtrl_SetSelection, 3104).
+-define(wxStyledTextCtrl_GetSelectedText, 3105).
+-define(wxStyledTextCtrl_GetTextRange, 3106).
+-define(wxStyledTextCtrl_HideSelection, 3107).
+-define(wxStyledTextCtrl_LineFromPosition, 3108).
+-define(wxStyledTextCtrl_PositionFromLine, 3109).
+-define(wxStyledTextCtrl_LineScroll, 3110).
+-define(wxStyledTextCtrl_EnsureCaretVisible, 3111).
+-define(wxStyledTextCtrl_ReplaceSelection, 3112).
+-define(wxStyledTextCtrl_SetReadOnly, 3113).
+-define(wxStyledTextCtrl_CanPaste, 3114).
+-define(wxStyledTextCtrl_CanUndo, 3115).
+-define(wxStyledTextCtrl_EmptyUndoBuffer, 3116).
+-define(wxStyledTextCtrl_Undo, 3117).
+-define(wxStyledTextCtrl_Cut, 3118).
+-define(wxStyledTextCtrl_Copy, 3119).
+-define(wxStyledTextCtrl_Paste, 3120).
+-define(wxStyledTextCtrl_Clear, 3121).
+-define(wxStyledTextCtrl_SetText, 3122).
+-define(wxStyledTextCtrl_GetText, 3123).
+-define(wxStyledTextCtrl_GetTextLength, 3124).
+-define(wxStyledTextCtrl_GetOvertype, 3125).
+-define(wxStyledTextCtrl_SetCaretWidth, 3126).
+-define(wxStyledTextCtrl_GetCaretWidth, 3127).
+-define(wxStyledTextCtrl_SetTargetStart, 3128).
+-define(wxStyledTextCtrl_GetTargetStart, 3129).
+-define(wxStyledTextCtrl_SetTargetEnd, 3130).
+-define(wxStyledTextCtrl_GetTargetEnd, 3131).
+-define(wxStyledTextCtrl_ReplaceTarget, 3132).
+-define(wxStyledTextCtrl_SearchInTarget, 3133).
+-define(wxStyledTextCtrl_SetSearchFlags, 3134).
+-define(wxStyledTextCtrl_GetSearchFlags, 3135).
+-define(wxStyledTextCtrl_CallTipShow, 3136).
+-define(wxStyledTextCtrl_CallTipCancel, 3137).
+-define(wxStyledTextCtrl_CallTipActive, 3138).
+-define(wxStyledTextCtrl_CallTipPosAtStart, 3139).
+-define(wxStyledTextCtrl_CallTipSetHighlight, 3140).
+-define(wxStyledTextCtrl_CallTipSetBackground, 3141).
+-define(wxStyledTextCtrl_CallTipSetForeground, 3142).
+-define(wxStyledTextCtrl_CallTipSetForegroundHighlight, 3143).
+-define(wxStyledTextCtrl_CallTipUseStyle, 3144).
+-define(wxStyledTextCtrl_VisibleFromDocLine, 3145).
+-define(wxStyledTextCtrl_DocLineFromVisible, 3146).
+-define(wxStyledTextCtrl_WrapCount, 3147).
+-define(wxStyledTextCtrl_SetFoldLevel, 3148).
+-define(wxStyledTextCtrl_GetFoldLevel, 3149).
+-define(wxStyledTextCtrl_GetLastChild, 3150).
+-define(wxStyledTextCtrl_GetFoldParent, 3151).
+-define(wxStyledTextCtrl_ShowLines, 3152).
+-define(wxStyledTextCtrl_HideLines, 3153).
+-define(wxStyledTextCtrl_GetLineVisible, 3154).
+-define(wxStyledTextCtrl_SetFoldExpanded, 3155).
+-define(wxStyledTextCtrl_GetFoldExpanded, 3156).
+-define(wxStyledTextCtrl_ToggleFold, 3157).
+-define(wxStyledTextCtrl_EnsureVisible, 3158).
+-define(wxStyledTextCtrl_SetFoldFlags, 3159).
+-define(wxStyledTextCtrl_EnsureVisibleEnforcePolicy, 3160).
+-define(wxStyledTextCtrl_SetTabIndents, 3161).
+-define(wxStyledTextCtrl_GetTabIndents, 3162).
+-define(wxStyledTextCtrl_SetBackSpaceUnIndents, 3163).
+-define(wxStyledTextCtrl_GetBackSpaceUnIndents, 3164).
+-define(wxStyledTextCtrl_SetMouseDwellTime, 3165).
+-define(wxStyledTextCtrl_GetMouseDwellTime, 3166).
+-define(wxStyledTextCtrl_WordStartPosition, 3167).
+-define(wxStyledTextCtrl_WordEndPosition, 3168).
+-define(wxStyledTextCtrl_SetWrapMode, 3169).
+-define(wxStyledTextCtrl_GetWrapMode, 3170).
+-define(wxStyledTextCtrl_SetWrapVisualFlags, 3171).
+-define(wxStyledTextCtrl_GetWrapVisualFlags, 3172).
+-define(wxStyledTextCtrl_SetWrapVisualFlagsLocation, 3173).
+-define(wxStyledTextCtrl_GetWrapVisualFlagsLocation, 3174).
+-define(wxStyledTextCtrl_SetWrapStartIndent, 3175).
+-define(wxStyledTextCtrl_GetWrapStartIndent, 3176).
+-define(wxStyledTextCtrl_SetLayoutCache, 3177).
+-define(wxStyledTextCtrl_GetLayoutCache, 3178).
+-define(wxStyledTextCtrl_SetScrollWidth, 3179).
+-define(wxStyledTextCtrl_GetScrollWidth, 3180).
+-define(wxStyledTextCtrl_TextWidth, 3181).
+-define(wxStyledTextCtrl_GetEndAtLastLine, 3182).
+-define(wxStyledTextCtrl_TextHeight, 3183).
+-define(wxStyledTextCtrl_SetUseVerticalScrollBar, 3184).
+-define(wxStyledTextCtrl_GetUseVerticalScrollBar, 3185).
+-define(wxStyledTextCtrl_AppendText, 3186).
+-define(wxStyledTextCtrl_GetTwoPhaseDraw, 3187).
+-define(wxStyledTextCtrl_SetTwoPhaseDraw, 3188).
+-define(wxStyledTextCtrl_TargetFromSelection, 3189).
+-define(wxStyledTextCtrl_LinesJoin, 3190).
+-define(wxStyledTextCtrl_LinesSplit, 3191).
+-define(wxStyledTextCtrl_SetFoldMarginColour, 3192).
+-define(wxStyledTextCtrl_SetFoldMarginHiColour, 3193).
+-define(wxStyledTextCtrl_LineDown, 3194).
+-define(wxStyledTextCtrl_LineDownExtend, 3195).
+-define(wxStyledTextCtrl_LineUp, 3196).
+-define(wxStyledTextCtrl_LineUpExtend, 3197).
+-define(wxStyledTextCtrl_CharLeft, 3198).
+-define(wxStyledTextCtrl_CharLeftExtend, 3199).
+-define(wxStyledTextCtrl_CharRight, 3200).
+-define(wxStyledTextCtrl_CharRightExtend, 3201).
+-define(wxStyledTextCtrl_WordLeft, 3202).
+-define(wxStyledTextCtrl_WordLeftExtend, 3203).
+-define(wxStyledTextCtrl_WordRight, 3204).
+-define(wxStyledTextCtrl_WordRightExtend, 3205).
+-define(wxStyledTextCtrl_Home, 3206).
+-define(wxStyledTextCtrl_HomeExtend, 3207).
+-define(wxStyledTextCtrl_LineEnd, 3208).
+-define(wxStyledTextCtrl_LineEndExtend, 3209).
+-define(wxStyledTextCtrl_DocumentStart, 3210).
+-define(wxStyledTextCtrl_DocumentStartExtend, 3211).
+-define(wxStyledTextCtrl_DocumentEnd, 3212).
+-define(wxStyledTextCtrl_DocumentEndExtend, 3213).
+-define(wxStyledTextCtrl_PageUp, 3214).
+-define(wxStyledTextCtrl_PageUpExtend, 3215).
+-define(wxStyledTextCtrl_PageDown, 3216).
+-define(wxStyledTextCtrl_PageDownExtend, 3217).
+-define(wxStyledTextCtrl_EditToggleOvertype, 3218).
+-define(wxStyledTextCtrl_Cancel, 3219).
+-define(wxStyledTextCtrl_DeleteBack, 3220).
+-define(wxStyledTextCtrl_Tab, 3221).
+-define(wxStyledTextCtrl_BackTab, 3222).
+-define(wxStyledTextCtrl_NewLine, 3223).
+-define(wxStyledTextCtrl_FormFeed, 3224).
+-define(wxStyledTextCtrl_VCHome, 3225).
+-define(wxStyledTextCtrl_VCHomeExtend, 3226).
+-define(wxStyledTextCtrl_ZoomIn, 3227).
+-define(wxStyledTextCtrl_ZoomOut, 3228).
+-define(wxStyledTextCtrl_DelWordLeft, 3229).
+-define(wxStyledTextCtrl_DelWordRight, 3230).
+-define(wxStyledTextCtrl_LineCut, 3231).
+-define(wxStyledTextCtrl_LineDelete, 3232).
+-define(wxStyledTextCtrl_LineTranspose, 3233).
+-define(wxStyledTextCtrl_LineDuplicate, 3234).
+-define(wxStyledTextCtrl_LowerCase, 3235).
+-define(wxStyledTextCtrl_UpperCase, 3236).
+-define(wxStyledTextCtrl_LineScrollDown, 3237).
+-define(wxStyledTextCtrl_LineScrollUp, 3238).
+-define(wxStyledTextCtrl_DeleteBackNotLine, 3239).
+-define(wxStyledTextCtrl_HomeDisplay, 3240).
+-define(wxStyledTextCtrl_HomeDisplayExtend, 3241).
+-define(wxStyledTextCtrl_LineEndDisplay, 3242).
+-define(wxStyledTextCtrl_LineEndDisplayExtend, 3243).
+-define(wxStyledTextCtrl_HomeWrapExtend, 3244).
+-define(wxStyledTextCtrl_LineEndWrap, 3245).
+-define(wxStyledTextCtrl_LineEndWrapExtend, 3246).
+-define(wxStyledTextCtrl_VCHomeWrap, 3247).
+-define(wxStyledTextCtrl_VCHomeWrapExtend, 3248).
+-define(wxStyledTextCtrl_LineCopy, 3249).
+-define(wxStyledTextCtrl_MoveCaretInsideView, 3250).
+-define(wxStyledTextCtrl_LineLength, 3251).
+-define(wxStyledTextCtrl_BraceHighlight, 3252).
+-define(wxStyledTextCtrl_BraceBadLight, 3253).
+-define(wxStyledTextCtrl_BraceMatch, 3254).
+-define(wxStyledTextCtrl_GetViewEOL, 3255).
+-define(wxStyledTextCtrl_SetViewEOL, 3256).
+-define(wxStyledTextCtrl_SetModEventMask, 3257).
+-define(wxStyledTextCtrl_GetEdgeColumn, 3258).
+-define(wxStyledTextCtrl_SetEdgeColumn, 3259).
+-define(wxStyledTextCtrl_GetEdgeMode, 3260).
+-define(wxStyledTextCtrl_GetEdgeColour, 3261).
+-define(wxStyledTextCtrl_SetEdgeColour, 3262).
+-define(wxStyledTextCtrl_SearchAnchor, 3263).
+-define(wxStyledTextCtrl_SearchNext, 3264).
+-define(wxStyledTextCtrl_SearchPrev, 3265).
+-define(wxStyledTextCtrl_LinesOnScreen, 3266).
+-define(wxStyledTextCtrl_UsePopUp, 3267).
+-define(wxStyledTextCtrl_SelectionIsRectangle, 3268).
+-define(wxStyledTextCtrl_SetZoom, 3269).
+-define(wxStyledTextCtrl_GetZoom, 3270).
+-define(wxStyledTextCtrl_GetModEventMask, 3271).
+-define(wxStyledTextCtrl_SetSTCFocus, 3272).
+-define(wxStyledTextCtrl_GetSTCFocus, 3273).
+-define(wxStyledTextCtrl_SetStatus, 3274).
+-define(wxStyledTextCtrl_GetStatus, 3275).
+-define(wxStyledTextCtrl_SetMouseDownCaptures, 3276).
+-define(wxStyledTextCtrl_GetMouseDownCaptures, 3277).
+-define(wxStyledTextCtrl_SetSTCCursor, 3278).
+-define(wxStyledTextCtrl_GetSTCCursor, 3279).
+-define(wxStyledTextCtrl_SetControlCharSymbol, 3280).
+-define(wxStyledTextCtrl_GetControlCharSymbol, 3281).
+-define(wxStyledTextCtrl_WordPartLeft, 3282).
+-define(wxStyledTextCtrl_WordPartLeftExtend, 3283).
+-define(wxStyledTextCtrl_WordPartRight, 3284).
+-define(wxStyledTextCtrl_WordPartRightExtend, 3285).
+-define(wxStyledTextCtrl_SetVisiblePolicy, 3286).
+-define(wxStyledTextCtrl_DelLineLeft, 3287).
+-define(wxStyledTextCtrl_DelLineRight, 3288).
+-define(wxStyledTextCtrl_GetXOffset, 3289).
+-define(wxStyledTextCtrl_ChooseCaretX, 3290).
+-define(wxStyledTextCtrl_SetXCaretPolicy, 3291).
+-define(wxStyledTextCtrl_SetYCaretPolicy, 3292).
+-define(wxStyledTextCtrl_GetPrintWrapMode, 3293).
+-define(wxStyledTextCtrl_SetHotspotActiveForeground, 3294).
+-define(wxStyledTextCtrl_SetHotspotActiveBackground, 3295).
+-define(wxStyledTextCtrl_SetHotspotActiveUnderline, 3296).
+-define(wxStyledTextCtrl_SetHotspotSingleLine, 3297).
+-define(wxStyledTextCtrl_ParaDownExtend, 3298).
+-define(wxStyledTextCtrl_ParaUp, 3299).
+-define(wxStyledTextCtrl_ParaUpExtend, 3300).
+-define(wxStyledTextCtrl_PositionBefore, 3301).
+-define(wxStyledTextCtrl_PositionAfter, 3302).
+-define(wxStyledTextCtrl_CopyRange, 3303).
+-define(wxStyledTextCtrl_CopyText, 3304).
+-define(wxStyledTextCtrl_SetSelectionMode, 3305).
+-define(wxStyledTextCtrl_GetSelectionMode, 3306).
+-define(wxStyledTextCtrl_LineDownRectExtend, 3307).
+-define(wxStyledTextCtrl_LineUpRectExtend, 3308).
+-define(wxStyledTextCtrl_CharLeftRectExtend, 3309).
+-define(wxStyledTextCtrl_CharRightRectExtend, 3310).
+-define(wxStyledTextCtrl_HomeRectExtend, 3311).
+-define(wxStyledTextCtrl_VCHomeRectExtend, 3312).
+-define(wxStyledTextCtrl_LineEndRectExtend, 3313).
+-define(wxStyledTextCtrl_PageUpRectExtend, 3314).
+-define(wxStyledTextCtrl_PageDownRectExtend, 3315).
+-define(wxStyledTextCtrl_StutteredPageUp, 3316).
+-define(wxStyledTextCtrl_StutteredPageUpExtend, 3317).
+-define(wxStyledTextCtrl_StutteredPageDown, 3318).
+-define(wxStyledTextCtrl_StutteredPageDownExtend, 3319).
+-define(wxStyledTextCtrl_WordLeftEnd, 3320).
+-define(wxStyledTextCtrl_WordLeftEndExtend, 3321).
+-define(wxStyledTextCtrl_WordRightEnd, 3322).
+-define(wxStyledTextCtrl_WordRightEndExtend, 3323).
+-define(wxStyledTextCtrl_SetWhitespaceChars, 3324).
+-define(wxStyledTextCtrl_SetCharsDefault, 3325).
+-define(wxStyledTextCtrl_AutoCompGetCurrent, 3326).
+-define(wxStyledTextCtrl_Allocate, 3327).
+-define(wxStyledTextCtrl_FindColumn, 3328).
+-define(wxStyledTextCtrl_GetCaretSticky, 3329).
+-define(wxStyledTextCtrl_SetCaretSticky, 3330).
+-define(wxStyledTextCtrl_ToggleCaretSticky, 3331).
+-define(wxStyledTextCtrl_SetPasteConvertEndings, 3332).
+-define(wxStyledTextCtrl_GetPasteConvertEndings, 3333).
+-define(wxStyledTextCtrl_SelectionDuplicate, 3334).
+-define(wxStyledTextCtrl_SetCaretLineBackAlpha, 3335).
+-define(wxStyledTextCtrl_GetCaretLineBackAlpha, 3336).
+-define(wxStyledTextCtrl_StartRecord, 3337).
+-define(wxStyledTextCtrl_StopRecord, 3338).
+-define(wxStyledTextCtrl_SetLexer, 3339).
+-define(wxStyledTextCtrl_GetLexer, 3340).
+-define(wxStyledTextCtrl_Colourise, 3341).
+-define(wxStyledTextCtrl_SetProperty, 3342).
+-define(wxStyledTextCtrl_SetKeyWords, 3343).
+-define(wxStyledTextCtrl_SetLexerLanguage, 3344).
+-define(wxStyledTextCtrl_GetProperty, 3345).
+-define(wxStyledTextCtrl_GetStyleBitsNeeded, 3346).
+-define(wxStyledTextCtrl_GetCurrentLine, 3347).
+-define(wxStyledTextCtrl_StyleSetSpec, 3348).
+-define(wxStyledTextCtrl_StyleSetFont, 3349).
+-define(wxStyledTextCtrl_StyleSetFontAttr, 3350).
+-define(wxStyledTextCtrl_StyleSetCharacterSet, 3351).
+-define(wxStyledTextCtrl_StyleSetFontEncoding, 3352).
+-define(wxStyledTextCtrl_CmdKeyExecute, 3353).
+-define(wxStyledTextCtrl_SetMargins, 3354).
+-define(wxStyledTextCtrl_GetSelection, 3355).
+-define(wxStyledTextCtrl_PointFromPosition, 3356).
+-define(wxStyledTextCtrl_ScrollToLine, 3357).
+-define(wxStyledTextCtrl_ScrollToColumn, 3358).
+-define(wxStyledTextCtrl_SendMsg, 3359).
+-define(wxStyledTextCtrl_SetVScrollBar, 3360).
+-define(wxStyledTextCtrl_SetHScrollBar, 3361).
+-define(wxStyledTextCtrl_GetLastKeydownProcessed, 3362).
+-define(wxStyledTextCtrl_SetLastKeydownProcessed, 3363).
+-define(wxStyledTextCtrl_SaveFile, 3364).
+-define(wxStyledTextCtrl_LoadFile, 3365).
+-define(wxStyledTextCtrl_DoDragOver, 3366).
+-define(wxStyledTextCtrl_DoDropText, 3367).
+-define(wxStyledTextCtrl_GetUseAntiAliasing, 3368).
+-define(wxStyledTextCtrl_AddTextRaw, 3369).
+-define(wxStyledTextCtrl_InsertTextRaw, 3370).
+-define(wxStyledTextCtrl_GetCurLineRaw, 3371).
+-define(wxStyledTextCtrl_GetLineRaw, 3372).
+-define(wxStyledTextCtrl_GetSelectedTextRaw, 3373).
+-define(wxStyledTextCtrl_GetTextRangeRaw, 3374).
+-define(wxStyledTextCtrl_SetTextRaw, 3375).
+-define(wxStyledTextCtrl_GetTextRaw, 3376).
+-define(wxStyledTextCtrl_AppendTextRaw, 3377).
+-define(wxArtProvider_GetBitmap, 3378).
+-define(wxArtProvider_GetIcon, 3379).
+-define(wxTreeEvent_GetKeyCode, 3380).
+-define(wxTreeEvent_GetItem, 3381).
+-define(wxTreeEvent_GetKeyEvent, 3382).
+-define(wxTreeEvent_GetLabel, 3383).
+-define(wxTreeEvent_GetOldItem, 3384).
+-define(wxTreeEvent_GetPoint, 3385).
+-define(wxTreeEvent_IsEditCancelled, 3386).
+-define(wxTreeEvent_SetToolTip, 3387).
+-define(wxNotebookEvent_GetOldSelection, 3388).
+-define(wxNotebookEvent_GetSelection, 3389).
+-define(wxNotebookEvent_SetOldSelection, 3390).
+-define(wxNotebookEvent_SetSelection, 3391).
+-define(wxFileDataObject_new, 3392).
+-define(wxFileDataObject_AddFile, 3393).
+-define(wxFileDataObject_GetFilenames, 3394).
+-define(wxFileDataObject_destroy, 3395).
+-define(wxTextDataObject_new, 3396).
+-define(wxTextDataObject_GetTextLength, 3397).
+-define(wxTextDataObject_GetText, 3398).
+-define(wxTextDataObject_SetText, 3399).
+-define(wxTextDataObject_destroy, 3400).
+-define(wxBitmapDataObject_new_1_1, 3401).
+-define(wxBitmapDataObject_new_1_0, 3402).
+-define(wxBitmapDataObject_GetBitmap, 3403).
+-define(wxBitmapDataObject_SetBitmap, 3404).
+-define(wxBitmapDataObject_destroy, 3405).
+-define(wxClipboard_new, 3407).
+-define(wxClipboard_destruct, 3408).
+-define(wxClipboard_AddData, 3409).
+-define(wxClipboard_Clear, 3410).
+-define(wxClipboard_Close, 3411).
+-define(wxClipboard_Flush, 3412).
+-define(wxClipboard_GetData, 3413).
+-define(wxClipboard_IsOpened, 3414).
+-define(wxClipboard_Open, 3415).
+-define(wxClipboard_SetData, 3416).
+-define(wxClipboard_UsePrimarySelection, 3418).
+-define(wxClipboard_IsSupported, 3419).
+-define(wxClipboard_Get, 3420).
+-define(wxSpinEvent_GetPosition, 3421).
+-define(wxSpinEvent_SetPosition, 3422).
+-define(wxSplitterWindow_new_0, 3423).
+-define(wxSplitterWindow_new_2, 3424).
+-define(wxSplitterWindow_destruct, 3425).
+-define(wxSplitterWindow_Create, 3426).
+-define(wxSplitterWindow_GetMinimumPaneSize, 3427).
+-define(wxSplitterWindow_GetSashGravity, 3428).
+-define(wxSplitterWindow_GetSashPosition, 3429).
+-define(wxSplitterWindow_GetSplitMode, 3430).
+-define(wxSplitterWindow_GetWindow1, 3431).
+-define(wxSplitterWindow_GetWindow2, 3432).
+-define(wxSplitterWindow_Initialize, 3433).
+-define(wxSplitterWindow_IsSplit, 3434).
+-define(wxSplitterWindow_ReplaceWindow, 3435).
+-define(wxSplitterWindow_SetSashGravity, 3436).
+-define(wxSplitterWindow_SetSashPosition, 3437).
+-define(wxSplitterWindow_SetSashSize, 3438).
+-define(wxSplitterWindow_SetMinimumPaneSize, 3439).
+-define(wxSplitterWindow_SetSplitMode, 3440).
+-define(wxSplitterWindow_SplitHorizontally, 3441).
+-define(wxSplitterWindow_SplitVertically, 3442).
+-define(wxSplitterWindow_Unsplit, 3443).
+-define(wxSplitterWindow_UpdateSize, 3444).
+-define(wxSplitterEvent_GetSashPosition, 3445).
+-define(wxSplitterEvent_GetX, 3446).
+-define(wxSplitterEvent_GetY, 3447).
+-define(wxSplitterEvent_GetWindowBeingRemoved, 3448).
+-define(wxSplitterEvent_SetSashPosition, 3449).
+-define(wxHtmlWindow_new_0, 3450).
+-define(wxHtmlWindow_new_2, 3451).
+-define(wxHtmlWindow_AppendToPage, 3452).
+-define(wxHtmlWindow_GetOpenedAnchor, 3453).
+-define(wxHtmlWindow_GetOpenedPage, 3454).
+-define(wxHtmlWindow_GetOpenedPageTitle, 3455).
+-define(wxHtmlWindow_GetRelatedFrame, 3456).
+-define(wxHtmlWindow_HistoryBack, 3457).
+-define(wxHtmlWindow_HistoryCanBack, 3458).
+-define(wxHtmlWindow_HistoryCanForward, 3459).
+-define(wxHtmlWindow_HistoryClear, 3460).
+-define(wxHtmlWindow_HistoryForward, 3461).
+-define(wxHtmlWindow_LoadFile, 3462).
+-define(wxHtmlWindow_LoadPage, 3463).
+-define(wxHtmlWindow_SelectAll, 3464).
+-define(wxHtmlWindow_SelectionToText, 3465).
+-define(wxHtmlWindow_SelectLine, 3466).
+-define(wxHtmlWindow_SelectWord, 3467).
+-define(wxHtmlWindow_SetBorders, 3468).
+-define(wxHtmlWindow_SetFonts, 3469).
+-define(wxHtmlWindow_SetPage, 3470).
+-define(wxHtmlWindow_SetRelatedFrame, 3471).
+-define(wxHtmlWindow_SetRelatedStatusBar, 3472).
+-define(wxHtmlWindow_ToText, 3473).
+-define(wxHtmlWindow_destroy, 3474).
+-define(wxHtmlLinkEvent_GetLinkInfo, 3475).
+-define(wxSystemSettings_GetColour, 3476).
+-define(wxSystemSettings_GetFont, 3477).
+-define(wxSystemSettings_GetMetric, 3478).
+-define(wxSystemSettings_GetScreenType, 3479).
+-define(wxAuiNotebookEvent_SetSelection, 3480).
+-define(wxAuiNotebookEvent_GetSelection, 3481).
+-define(wxAuiNotebookEvent_SetOldSelection, 3482).
+-define(wxAuiNotebookEvent_GetOldSelection, 3483).
+-define(wxAuiNotebookEvent_SetDragSource, 3484).
+-define(wxAuiNotebookEvent_GetDragSource, 3485).
+-define(wxAuiManagerEvent_SetManager, 3486).
+-define(wxAuiManagerEvent_GetManager, 3487).
+-define(wxAuiManagerEvent_SetPane, 3488).
+-define(wxAuiManagerEvent_GetPane, 3489).
+-define(wxAuiManagerEvent_SetButton, 3490).
+-define(wxAuiManagerEvent_GetButton, 3491).
+-define(wxAuiManagerEvent_SetDC, 3492).
+-define(wxAuiManagerEvent_GetDC, 3493).
+-define(wxAuiManagerEvent_Veto, 3494).
+-define(wxAuiManagerEvent_GetVeto, 3495).
+-define(wxAuiManagerEvent_SetCanVeto, 3496).
+-define(wxAuiManagerEvent_CanVeto, 3497).
+-define(wxLogNull_new, 3498).
+-define(wxLogNull_destroy, 3499).
diff --git a/lib/wx/src/wx.erl b/lib/wx/src/wx.erl
index 14abd0d817..9d76f3bc42 100644
--- a/lib/wx/src/wx.erl
+++ b/lib/wx/src/wx.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -96,7 +96,8 @@ new() ->
%% @doc Starts a wx server.
%% Option may be {debug, Level}, see debug/1.
new(Options) when is_list(Options) ->
- #wx_env{} = wxe_server:start(),
+ #wx_env{port=Port} = wxe_server:start(),
+ put(opengl_port, Port),
Debug = proplists:get_value(debug, Options, 0),
debug(Debug),
null().
@@ -121,8 +122,9 @@ get_env() ->
%% @spec (wx_env()) -> ok
%% @doc Sets the process wx environment, allows this process to use
%% another process wx environment.
-set_env(#wx_env{sv=Pid} = Env) ->
- put(?WXE_IDENTIFIER, Env),
+set_env(#wx_env{sv=Pid, port=Port} = Env) ->
+ put(?WXE_IDENTIFIER, Env),
+ put(opengl_port, Port),
%% wxe_util:cast(?REGISTER_PID, <<>>),
wxe_server:register_me(Pid),
ok.
diff --git a/lib/wx/src/wx_object.erl b/lib/wx/src/wx_object.erl
index 1f0b7922a0..bfd38960dd 100644
--- a/lib/wx/src/wx_object.erl
+++ b/lib/wx/src/wx_object.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%%-------------------------------------------------------------------
%%% File : wx_object.erl
@@ -321,7 +321,8 @@ loop(Parent, Name, State, Mod, Time, Debug) ->
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
- Debug1 = sys:handle_debug(Debug, {gen_server, print_event}, Name, {in, Msg}),
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
+ Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.
@@ -410,12 +411,12 @@ handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) ->
Debug1 = reply(Name, From, Reply, NState, Debug),
loop(Parent, Name, NState, Mod, Time1, Debug1);
{noreply, NState} ->
- Debug1 = sys:handle_debug(Debug, {gen_server, print_event}, Name,
- {noreply, NState}),
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
+ Name, {noreply, NState}),
loop(Parent, Name, NState, Mod, infinity, Debug1);
{noreply, NState, Time1} ->
- Debug1 = sys:handle_debug(Debug, {gen_server, print_event}, Name,
- {noreply, NState}),
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
+ Name, {noreply, NState}),
loop(Parent, Name, NState, Mod, Time1, Debug1);
{stop, Reason, Reply, NState} ->
{'EXIT', R} =
@@ -437,12 +438,12 @@ handle_no_reply({noreply, NState}, Parent, Name, _Msg, Mod, _State, []) ->
handle_no_reply({noreply, NState, Time1}, Parent, Name, _Msg, Mod, _State, []) ->
loop(Parent, Name, NState, Mod, Time1, []);
handle_no_reply({noreply, NState}, Parent, Name, _Msg, Mod, _State, Debug) ->
- Debug1 = sys:handle_debug(Debug, {gen_server, print_event}, Name,
- {noreply, NState}),
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
+ Name, {noreply, NState}),
loop(Parent, Name, NState, Mod, infinity, Debug1);
handle_no_reply({noreply, NState, Time1}, Parent, Name, _Msg, Mod, _State, Debug) ->
- Debug1 = sys:handle_debug(Debug, {gen_server, print_event}, Name,
- {noreply, NState}),
+ Debug1 = sys:handle_debug(Debug, fun print_event/3,
+ Name, {noreply, NState}),
loop(Parent, Name, NState, Mod, Time1, Debug1);
handle_no_reply(Reply, _Parent, Name, Msg, Mod, State, Debug) ->
handle_common_reply(Reply, Name, Msg, Mod, State,Debug).
@@ -462,8 +463,8 @@ handle_common_reply(Reply, Name, Msg, Mod, State, Debug) ->
%% @hidden
reply(Name, {To, Tag}, Reply, State, Debug) ->
reply({To, Tag}, Reply),
- sys:handle_debug(Debug, {gen_server, print_event}, Name,
- {out, Reply, To, State} ).
+ sys:handle_debug(Debug, fun print_event/3,
+ Name, {out, Reply, To, State}).
%%-----------------------------------------------------------------
@@ -485,6 +486,29 @@ system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) ->
Else -> Else
end.
+%%-----------------------------------------------------------------
+%% Format debug messages. Print them as the call-back module sees
+%% them, not as the real erlang messages. Use trace for that.
+%%-----------------------------------------------------------------
+print_event(Dev, {in, Msg}, Name) ->
+ case Msg of
+ {'$gen_call', {From, _Tag}, Call} ->
+ io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
+ [Name, Call, From]);
+ {'$gen_cast', Cast} ->
+ io:format(Dev, "*DBG* ~p got cast ~p~n",
+ [Name, Cast]);
+ _ ->
+ io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
+ end;
+print_event(Dev, {out, Msg, To, State}, Name) ->
+ io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
+ [Name, Msg, To, State]);
+print_event(Dev, {noreply, State}, Name) ->
+ io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
+print_event(Dev, Event, Name) ->
+ io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
+
%%% ---------------------------------------------------
%%% Terminate the server.
%%% ---------------------------------------------------
@@ -581,12 +605,15 @@ dbg_opts(Name, Opts) ->
%%-----------------------------------------------------------------
format_status(Opt, StatusData) ->
[PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData,
- NameTag = if is_pid(Name) ->
- pid_to_list(Name);
- is_atom(Name) ->
- Name
- end,
- Header = lists:concat(["Status for generic server ", NameTag]),
+ StatusHdr = "Status for wx object ",
+ Header = if
+ is_pid(Name) ->
+ lists:concat([StatusHdr, pid_to_list(Name)]);
+ is_atom(Name); is_list(Name) ->
+ lists:concat([StatusHdr, Name]);
+ true ->
+ {StatusHdr, Name}
+ end,
Log = sys:get_debug(log, Debug, []),
Specfic =
case erlang:function_exported(Mod, format_status, 2) of
diff --git a/lib/wx/src/wxe.hrl b/lib/wx/src/wxe.hrl
index bb70a03bfe..bd34b13385 100644
--- a/lib/wx/src/wxe.hrl
+++ b/lib/wx/src/wxe.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -50,6 +50,8 @@
-define(WXE_CB_START, 8). %% Used for event-callback start
-define(WXE_DEBUG_DRIVER, 9). %% Set debug
%%-define(WXE_DEBUG_PING, 10). %% debug ping (when using debugger it's needed)
--define(WXE_BIN_INCR, 5001). %% Binary refc incr
--define(WXE_BIN_DECR, 5002). %% Binary refc decr
+-define(WXE_BIN_INCR, 11). %% Binary refc incr
+-define(WXE_BIN_DECR, 12). %% Binary refc decr
+-define(WXE_INIT_OPENGL, 13). %% Binary refc decr
+
-include("gen/wxe_funcs.hrl").
diff --git a/lib/wx/src/wxe_master.erl b/lib/wx/src/wxe_master.erl
index 5ab76a77cf..9efe59054c 100644
--- a/lib/wx/src/wxe_master.erl
+++ b/lib/wx/src/wxe_master.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -28,7 +28,7 @@
-behaviour(gen_server).
%% API
--export([start/0, init_port/0]).
+-export([start/0, init_port/0, init_opengl/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -38,8 +38,8 @@
users, %% List of wx servers, needed ??
driver}). %% Driver name so wx_server can create it's own port
+-include("wxe.hrl").
-include("gen/wxe_debug.hrl").
--include("gen/gl_debug.hrl").
-define(DRIVER, "wxe_driver").
@@ -74,6 +74,14 @@ init_port() ->
receive wx_port_initiated -> ok end,
{Port, CBport}.
+
+%%--------------------------------------------------------------------
+%% Initlizes the opengl library
+%%--------------------------------------------------------------------
+init_opengl() ->
+ GLLib = wxe_util:wxgl_dl(),
+ wxe_util:call(?WXE_INIT_OPENGL, <<(list_to_binary(GLLib))/binary, 0:8>>).
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -87,7 +95,7 @@ init_port() ->
%%--------------------------------------------------------------------
init([]) ->
DriverName = ?DRIVER,
- PrivDir = priv_dir(),
+ PrivDir = wxe_util:priv_dir(?DRIVER),
erlang:group_leader(whereis(init), self()),
case catch erlang:system_info(smp_support) of
true -> ok;
@@ -120,20 +128,19 @@ init([]) ->
process_flag(trap_exit, true),
DriverWithArgs = DriverName ++ " " ++ code:priv_dir(wx) ++ [0],
- case catch open_port({spawn, DriverWithArgs},[binary]) of
- {'EXIT', Err} ->
- erlang:error({open_port,Err});
- Port ->
- wx_debug_info = ets:new(wx_debug_info, [named_table]),
- wx_non_consts = ets:new(wx_non_consts, [named_table]),
- true = ets:insert(wx_debug_info, wxdebug_table()),
- true = ets:insert(wx_debug_info, gldebug_table()),
- spawn_link(fun() -> debug_ping(Port) end),
- receive
- {wx_consts, List} ->
- true = ets:insert(wx_non_consts, List)
- end,
- {ok, #state{cb_port=Port, driver=DriverName, users=gb_sets:empty()}}
+ try
+ Port = open_port({spawn, DriverWithArgs},[binary]),
+ wx_debug_info = ets:new(wx_debug_info, [named_table]),
+ wx_non_consts = ets:new(wx_non_consts, [named_table]),
+ true = ets:insert(wx_debug_info, wxdebug_table()),
+ spawn_link(fun() -> debug_ping(Port) end),
+ receive
+ {wx_consts, List} ->
+ true = ets:insert(wx_non_consts, List)
+ end,
+ {ok, #state{cb_port=Port, driver=DriverName, users=gb_sets:empty()}}
+ catch _:Err ->
+ error({Err, "Could not initiate graphics"})
end.
%%--------------------------------------------------------------------
@@ -205,108 +212,9 @@ code_change(_OldVsn, State, _Extra) ->
%%%%%%%%%%%% INTERNAL %%%%%%%%%%%%%%%%%%%%%%%%
-%% If you want anything done, do it yourself.
-
-priv_dir() ->
- Type = erlang:system_info(system_architecture),
- {file, Path} = code:is_loaded(?MODULE),
- Priv = case filelib:is_regular(Path) of
- true ->
- Beam = filename:join(["ebin/",atom_to_list(?MODULE) ++ ".beam"]),
- filename:join(strip(Path, Beam), "priv");
- false ->
- code:priv_dir(wx)
- end,
- try
- {ok, Dirs0} = file:list_dir(Priv),
- Dirs1 = split_dirs(Dirs0),
- Dirs = lists:reverse(lists:sort(Dirs1)),
-
- Best = best_dir(hd(split_dirs([Type])),Dirs, Priv),
- filename:join(Priv, Best)
- catch _:_ ->
- error_logger:format("WX ERROR: Could not find suitable \'~s\' for ~s in: ~s~n",
- [?DRIVER, Type, Priv]),
- erlang:error({load_driver, "No driver found"})
- end.
-
-best_dir(Dir, Dirs0, Priv) ->
- Dirs = [{D,D} || D <- Dirs0],
- best_dir(Dir, Dirs, [], Priv).
-
-best_dir(Pre, [{[],_}|R], Acc, Priv) -> %% Empty skip'em
- best_dir(Pre, R, Acc, Priv);
-best_dir(Pre, [{Pre,Dir}|R], Acc, Priv) ->
- Real = dir_app(lists:reverse(Dir)),
- case file:list_dir(filename:join(Priv,Real)) of
- {ok, Fs} ->
- case lists:any(fun(File) -> filename:rootname(File) =:= ?DRIVER end, Fs) of
- true -> Real; %% Found dir and it contains a driver
- false -> best_dir(Pre, R, Acc, Priv)
- end;
- _ ->
- best_dir(Pre, R, Acc, Priv)
- end;
-best_dir(Pre, [{[_|F],Dir}|R], Acc, Priv) ->
- best_dir(Pre, R, [{F,Dir}|Acc], Priv);
-best_dir(_Pre, [], [],_) -> throw(no_dir); %% Nothing found
-best_dir([_|Pre], [], Acc, Priv) ->
- best_dir(Pre, lists:reverse(Acc), [], Priv);
-best_dir([], _, _,_) -> throw(no_dir). %% Nothing found
-
-split_dirs(Dirs0) ->
- ToInt = fun(Str) ->
- try
- list_to_integer(Str)
- catch _:_ -> Str
- end
- end,
- Split = fun(Dir) ->
- Toks = tokens(Dir,".-"),
- lists:reverse([ToInt(Str) || Str <- Toks])
- end,
- lists:map(Split,Dirs0).
-
-dir_app([]) -> [];
-dir_app([Dir]) -> Dir;
-dir_app(Dir) ->
- dir_app2(Dir).
-dir_app2([Int]) when is_integer(Int) ->
- integer_to_list(Int);
-dir_app2([Str]) when is_list(Str) ->
- Str;
-dir_app2([Head|Rest]) when is_integer(Head) ->
- integer_to_list(Head) ++ dir_app2(Rest);
-dir_app2([Head|Rest]) when is_list(Head) ->
- Head ++ dir_app2(Rest).
-
-strip(Src, Src) ->
- [];
-strip([H|R], Src) ->
- [H| strip(R, Src)].
-
-
debug_ping(Port) ->
timer:sleep(1*333),
_R = (catch erlang:port_call(Port, 0, [])),
%% io:format("Erlang ping ~p ~n", [_R]),
debug_ping(Port).
-tokens(S,Seps) ->
- tokens1(S, Seps, []).
-
-tokens1([C|S], Seps, Toks) ->
- case lists:member(C, Seps) of
- true -> tokens1(S, Seps, [[C]|Toks]);
- false -> tokens2(S, Seps, Toks, [C])
- end;
-tokens1([], _Seps, Toks) ->
- lists:reverse(Toks).
-
-tokens2([C|S], Seps, Toks, Cs) ->
- case lists:member(C, Seps) of
- true -> tokens1(S, Seps, [[C], lists:reverse(Cs) |Toks]);
- false -> tokens2(S, Seps, Toks, [C|Cs])
- end;
-tokens2([], _Seps, Toks, Cs) ->
- lists:reverse([lists:reverse(Cs)|Toks]).
diff --git a/lib/wx/src/wxe_util.erl b/lib/wx/src/wxe_util.erl
index a2fb4641c9..02bca62486 100644
--- a/lib/wx/src/wxe_util.erl
+++ b/lib/wx/src/wxe_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,8 +32,9 @@
get_const/1,colour_bin/1,datetime_bin/1,
to_bool/1,from_bool/1]).
--include("wxe.hrl").
+-export([wxgl_dl/0, priv_dir/1]).
+-include("wxe.hrl").
to_bool(0) -> false;
to_bool(_) -> true.
@@ -199,3 +200,47 @@ check_previous() ->
erlang:error({Error, MF})
after 0 -> ok
end.
+
+%% Get gl dynamic library
+
+wxgl_dl() ->
+ DynLib0 = "erl_gl",
+ PrivDir = priv_dir(DynLib0),
+ DynLib = case os:type() of
+ {win32,_} ->
+ DynLib0 ++ ".dll";
+ _ ->
+ DynLib0 ++ ".so"
+ end,
+ filename:join(PrivDir, DynLib).
+
+priv_dir(Driver0) ->
+ {file, Path} = code:is_loaded(?MODULE),
+ Priv = case filelib:is_regular(Path) of
+ true ->
+ Beam = filename:join(["ebin/",atom_to_list(?MODULE) ++ ".beam"]),
+ filename:join(strip(Path, Beam), "priv");
+ false ->
+ code:priv_dir(wx)
+ end,
+ Driver = case os:type() of
+ {win32,_} ->
+ Driver0 ++ ".dll";
+ _ ->
+ Driver0 ++ ".so"
+ end,
+
+ case file:read_file_info(filename:join(Priv, Driver)) of
+ {ok, _} ->
+ Priv;
+ {error, _} ->
+ error_logger:format("ERROR: Could not find \'~s\' in: ~s~n",
+ [Driver, Priv]),
+ erlang:error({load_driver, "No driver found"})
+ end.
+
+strip(Src, Src) ->
+ [];
+strip([H|R], Src) ->
+ [H| strip(R, Src)].
+
diff --git a/lib/wx/test/Makefile b/lib/wx/test/Makefile
index 71b79aa272..cf51d7918f 100644
--- a/lib/wx/test/Makefile
+++ b/lib/wx/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2010. All Rights Reserved.
+# Copyright Ericsson AB 2008-2011. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
@@ -62,8 +62,8 @@ release_spec:
release_tests_spec: opt
$(INSTALL_DIR) $(RELSYSDIR)
- $(INSTALL_DATA) wx.spec wx_test_lib.hrl $(ErlSrc) $(ErlTargets) $(RELSYSDIR)
- $(INSTALL_PROGRAM) wxt $(RELSYSDIR)
+ $(INSTALL_DATA) wx.spec wx.cover wx_test_lib.hrl $(ErlSrc) $(ErlTargets) $(RELSYSDIR)
+ $(INSTALL_SCRIPT) wxt $(RELSYSDIR)
release_docs_spec:
diff --git a/lib/wx/test/wx.cover b/lib/wx/test/wx.cover
new file mode 100644
index 0000000000..47e162ba7d
--- /dev/null
+++ b/lib/wx/test/wx.cover
@@ -0,0 +1,2 @@
+{incl_app,wx,details}.
+
diff --git a/lib/wx/test/wx.spec b/lib/wx/test/wx.spec
index a9201e5737..21e4a8c064 100644
--- a/lib/wx/test/wx.spec
+++ b/lib/wx/test/wx.spec
@@ -1,2 +1 @@
-{topcase, {dir, "../wx_test"}}.
-
+{suites,"../wx_test",all}.
diff --git a/lib/wx/test/wx_app_SUITE.erl b/lib/wx/test/wx_app_SUITE.erl
index 8fff324913..162923eaa3 100644
--- a/lib/wx/test/wx_app_SUITE.erl
+++ b/lib/wx/test/wx_app_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,22 +45,28 @@ init_per_testcase(Case, Config0) ->
end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func, Config).
-fin_per_testcase(Case, Config) ->
- wx_test_lib:end_per_testcase(Case, Config).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-all() ->
- all(suite).
-
-all(suite) ->
- [
- fields,
- modules,
- exportall,
- app_depend,
- undef_funcs
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [fields, modules, exportall, app_depend, undef_funcs].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl
index 599aa371ba..9ad34248a9 100644
--- a/lib/wx/test/wx_basic_SUITE.erl
+++ b/lib/wx/test/wx_basic_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,8 +23,9 @@
%%% Created : 3 Nov 2008 by Dan Gudmundsson <[email protected]>
%%%-------------------------------------------------------------------
-module(wx_basic_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -41,20 +42,23 @@ init_per_testcase(Func,Config) ->
wx_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- create_window,
- several_apps,
- wx_api,
- wx_misc,
- data_types
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [create_window, several_apps, wx_api, wx_misc,
+ data_types].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% The test cases
diff --git a/lib/wx/test/wx_class_SUITE.erl b/lib/wx/test/wx_class_SUITE.erl
index 76df6e4a23..79e6833e9b 100644
--- a/lib/wx/test/wx_class_SUITE.erl
+++ b/lib/wx/test/wx_class_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,8 +24,9 @@
%%%-------------------------------------------------------------------
-module(wx_class_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -42,24 +43,23 @@ init_per_testcase(Func,Config) ->
wx_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- calendarCtrl,
- treeCtrl,
- notebook,
- staticBoxSizer,
- clipboard,
- helpFrame,
- htmlWindow,
- listCtrlSort,
- radioBox
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [calendarCtrl, treeCtrl, notebook, staticBoxSizer,
+ clipboard, helpFrame, htmlWindow, listCtrlSort,
+ radioBox, systemSettings].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
%% The test cases
@@ -137,6 +137,9 @@ treeCtrl(Config) ->
wxFrame:connect(Tree, command_tree_item_expanded),
wxFrame:connect(Tree, command_tree_item_collapsed),
wxFrame:connect(Frame, close_window),
+
+ wxTreeCtrl:editLabel(Tree, Root),
+
wx_test_lib:wx_destroy(Frame,Config).
@@ -389,3 +392,17 @@ radioBox(Config) ->
wxWindow:show(Frame),
wx_test_lib:wx_destroy(Frame,Config).
+
+
+systemSettings(TestInfo) when is_atom(TestInfo) -> wx_test_lib:tc_info(TestInfo);
+systemSettings(Config) ->
+ Wx = wx:new(),
+ Frame = wxFrame:new(Wx, ?wxID_ANY, "Frame"),
+
+ ?m({_,_,_,_}, wxSystemSettings:getColour(?wxSYS_COLOUR_DESKTOP)),
+ ?mt(wxFont, wxSystemSettings:getFont(?wxSYS_SYSTEM_FONT)),
+ ?m(true, is_integer(wxSystemSettings:getMetric(?wxSYS_MOUSE_BUTTONS))),
+ ?m(true, is_integer(wxSystemSettings:getScreenType())),
+
+ wxWindow:show(Frame),
+ wx_test_lib:wx_destroy(Frame,Config).
diff --git a/lib/wx/test/wx_event_SUITE.erl b/lib/wx/test/wx_event_SUITE.erl
index dea10d892e..0d8dd4852e 100644
--- a/lib/wx/test/wx_event_SUITE.erl
+++ b/lib/wx/test/wx_event_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,8 +22,9 @@
%%% Created : 3 Nov 2008 by Dan Gudmundsson <[email protected]>
%%%-------------------------------------------------------------------
-module(wx_event_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -40,22 +41,23 @@ init_per_testcase(Func,Config) ->
wx_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- connect,
- disconnect,
- connect_msg_20,
- connect_cb_20,
- mouse_on_grid,
- spin_event,
- connect_in_callback
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [connect, disconnect, connect_msg_20, connect_cb_20,
+ mouse_on_grid, spin_event, connect_in_callback].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% The test cases
diff --git a/lib/wx/test/wx_opengl_SUITE.erl b/lib/wx/test/wx_opengl_SUITE.erl
index ce4651bcb1..e8fdf603d6 100644
--- a/lib/wx/test/wx_opengl_SUITE.erl
+++ b/lib/wx/test/wx_opengl_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -22,8 +22,9 @@
%%% Created : 3 Nov 2008 by Dan Gudmundsson <[email protected]>
%%%-------------------------------------------------------------------
-module(wx_opengl_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -48,18 +49,23 @@ init_per_testcase(Func,Config) ->
wx_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- canvas,
- glu_tesselation
- ].
-
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [canvas, glu_tesselation].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+
%% The test cases
-define(VS, {{ 0.5, 0.5, -0.5}, %1
@@ -91,7 +97,7 @@ canvas(Config) ->
?m(true, wxWindow:show(Frame)),
?m(false, wx:is_null(wxGLCanvas:getContext(Canvas))),
- ?m({'EXIT', {{no_gl_context,_},_}}, gl:getString(?GL_VENDOR)),
+ ?m({'EXIT', {{error, no_gl_context,_},_}}, gl:getString(?GL_VENDOR)),
?m(ok, wxGLCanvas:setCurrent(Canvas)),
io:format("Vendor: ~s~n", [gl:getString(?GL_VENDOR)]),
@@ -113,7 +119,7 @@ canvas(Config) ->
Data = {?FACES,?VS},
drawBox(0, Data),
?m(ok, wxGLCanvas:swapBuffers(Canvas)),
-
+ ?m([], flush()),
Env = wx:get_env(),
Tester = self(),
spawn_link(fun() ->
@@ -125,10 +131,23 @@ canvas(Config) ->
%% This may fail when window is deleted
catch draw_loop(2,Data,Canvas)
end),
-
?m_receive(works),
+ ?m([], flush()),
+ io:format("Undef func ~p ~n", [catch gl:uniform1d(2, 0.75)]),
+ timer:sleep(500),
+ ?m([], flush()),
wx_test_lib:wx_destroy(Frame, Config).
-
+
+flush() ->
+ flush([]).
+
+flush(Collected) ->
+ receive Msg ->
+ flush([Msg|Collected])
+ after 1 ->
+ lists:reverse(Collected)
+ end.
+
draw_loop(Deg,Data,Canvas) ->
timer:sleep(15),
drawBox(Deg,Data),
@@ -136,6 +155,7 @@ draw_loop(Deg,Data,Canvas) ->
draw_loop(Deg+1, Data,Canvas).
+
drawBox(Deg,{Fs,Vs}) ->
gl:matrixMode(?GL_MODELVIEW),
gl:loadIdentity(),
diff --git a/lib/wx/test/wx_test_lib.erl b/lib/wx/test/wx_test_lib.erl
index 9368aa4bdc..8509d6be6f 100644
--- a/lib/wx/test/wx_test_lib.erl
+++ b/lib/wx/test/wx_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,7 +35,7 @@ init_per_suite(Config) ->
exit("Can not test on MacOSX");
{unix, _} ->
io:format("DISPLAY ~s~n", [os:getenv("DISPLAY")]),
- case proplists:get_value(xserver, Config, none) of
+ case ct:get_config(xserver, none) of
none -> ignore;
Server ->
os:putenv("DISPLAY", Server)
@@ -200,7 +200,7 @@ eval_test_case(Mod, Fun, Config) ->
test_case_evaluator(Mod, Fun, [Config]) ->
NewConfig = Mod:init_per_testcase(Fun, Config),
R = apply(Mod, Fun, [NewConfig]),
- Mod:fin_per_testcase(Fun, NewConfig),
+ Mod:end_per_testcase(Fun, NewConfig),
exit({test_case_ok, R}).
wait_for_evaluator(Pid, Mod, Fun, Config) ->
@@ -216,12 +216,12 @@ wait_for_evaluator(Pid, Mod, Fun, Config) ->
{'EXIT', Pid, {skipped, Reason}} ->
log("<WARNING> Test case ~w skipped, because ~p~n",
[{Mod, Fun}, Reason]),
- Mod:fin_per_testcase(Fun, Config),
+ Mod:end_per_testcase(Fun, Config),
{skip, {Mod, Fun}, Reason};
{'EXIT', Pid, Reason} ->
log("<ERROR> Eval process ~w exited, because ~p~n",
[{Mod, Fun}, Reason]),
- Mod:fin_per_testcase(Fun, Config),
+ Mod:end_per_testcase(Fun, Config),
{crash, {Mod, Fun}, Reason}
end.
diff --git a/lib/wx/test/wx_xtra_SUITE.erl b/lib/wx/test/wx_xtra_SUITE.erl
index d5888bbf94..02a0672594 100644
--- a/lib/wx/test/wx_xtra_SUITE.erl
+++ b/lib/wx/test/wx_xtra_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -23,8 +23,9 @@
%%% Created : 3 Nov 2008 by Dan Gudmundsson <[email protected]>
%%%-------------------------------------------------------------------
-module(wx_xtra_SUITE).
--export([all/0, init_per_suite/1, end_per_suite/1,
- init_per_testcase/2, fin_per_testcase/2, end_per_testcase/2]).
+-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2,
+ init_per_suite/1, end_per_suite/1,
+ init_per_testcase/2, end_per_testcase/2]).
-compile(export_all).
@@ -41,19 +42,23 @@ init_per_testcase(Func,Config) ->
wx_test_lib:init_per_testcase(Func,Config).
end_per_testcase(Func,Config) ->
wx_test_lib:end_per_testcase(Func,Config).
-fin_per_testcase(Func,Config) -> %% For test_server
- wx_test_lib:end_per_testcase(Func,Config).
%% SUITE specification
-all() ->
- all(suite).
-all(suite) ->
- [
- destroy_app,
- multiple_add_in_sizer,
- app_dies,
- menu_item_debug
- ].
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [destroy_app, multiple_add_in_sizer, app_dies,
+ menu_item_debug].
+
+groups() ->
+ [].
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
%% The test cases
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index 54ab92cad2..7c440a7f5b 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1,7 +1 @@
-WX_VSN = 0.98.5
-
-TICKETS = OTP-8330 OTP-8461 OTP-8408 OTP-8455 OTP-8462
-TICKETS_0.98.4 = OTP-8243 OTP-8250 OTP-8292
-TICKETS_0.98.3 = OTP-8138 OTP-8126 OTP-8083
-TICKETS_0.98.2 = OTP-7943
-TICKETS_0.98.1 = OTP-7875 \ No newline at end of file
+WX_VSN = 0.98.9
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 207f6fdf16..8542435456 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -31,6 +31,88 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.2.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> The function xmerl_lib:expand_content/1 is mainly for
+ expanding Simple XML, but can also handle xmerl records.
+ This patch fixes an omission that caused expand_content/1
+ to not maintain the parents list when expanding
+ #xmlElement{} records. (Thanks to Ulf Wiger) </p>
+ <p>
+ Own Id: OTP-9034</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Removed some dialyzer warnings. </p>
+ <p>
+ Own Id: OTP-9074</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Xmerl 1.2.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> An empty element declared as simpleContent was not
+ properly validated. </p>
+ <p>
+ Own Id: OTP-8599</p>
+ </item>
+ <item>
+ <p> Fix format_man_pages so it handles all man sections
+ and remove warnings/errors in various man pages. </p>
+ <p>
+ Own Id: OTP-8600</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Fix entity checking so there are no fatal errors for
+ undefined entities when option skip_external_dtd is used.
+ </p>
+ <p>
+ Own Id: OTP-8947</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Xmerl 1.2.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Fixed problem with hex entities in UTF-8 documents:
+ When a document was in UTF-8 encoding, xmerl_scan
+ improperly replaced hex entities by the UTF-8 bytes
+ instead of returning the character, as it does with
+ inline UTF-8 text and decimal entities. (Thanks to Paul
+ Guyot.) </p>
+ <p>
+ Own Id: OTP-8697</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.2.5</title>
<section><title>Improvements and New Features</title>
@@ -50,6 +132,14 @@
Own Id: OTP-8537
</p>
</item>
+ <item>
+ <p>
+ An empty element declared as a simpleContent was not properly validated.
+ </p>
+ <p>
+ Own Id: OTP-8599
+ </p>
+ </item>
</list>
</section>
diff --git a/lib/xmerl/doc/src/notes_history.xml b/lib/xmerl/doc/src/notes_history.xml
index 06d0cb3b40..a8f7d8b3a6 100644
--- a/lib/xmerl/doc/src/notes_history.xml
+++ b/lib/xmerl/doc/src/notes_history.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2006</year>
- <year>2007</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/xmerl/doc/src/xmerl_sax_parser.xml b/lib/xmerl/doc/src/xmerl_sax_parser.xml
index ea63ba22a1..972023622e 100644
--- a/lib/xmerl/doc/src/xmerl_sax_parser.xml
+++ b/lib/xmerl/doc/src/xmerl_sax_parser.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>2008</year>
- <year>2008</year>
+ <year>2011</year>
<holder>Ericsson AB, All Rights Reserved</holder>
</copyright>
<legalnotice>
diff --git a/lib/xmerl/src/xmerl_lib.erl b/lib/xmerl/src/xmerl_lib.erl
index 7b76a76a33..6402f1cbeb 100644
--- a/lib/xmerl/src/xmerl_lib.erl
+++ b/lib/xmerl/src/xmerl_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -148,9 +148,10 @@ expand_element(Element) ->
expand_element(Element, Pos, Parents) ->
expand_element(Element, Pos, Parents, false).
-expand_element(E = #xmlElement{}, Pos, Parents, Norm) ->
- Content = expand_content(E#xmlElement.content, 1, Parents, Norm),
- Attrs = expand_attributes(E#xmlElement.attributes, 1, []),
+expand_element(E = #xmlElement{name = N}, Pos, Parents, Norm) ->
+ NewParents = [{N,Pos}|Parents],
+ Content = expand_content(E#xmlElement.content, 1, NewParents, Norm),
+ Attrs = expand_attributes(E#xmlElement.attributes, 1, NewParents),
E#xmlElement{pos = Pos,
parents = Parents,
attributes = Attrs,
diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
index 9d184152d1..3b9eaa309c 100644
--- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc
@@ -1,7 +1,7 @@
%%-*-erlang-*-
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -934,6 +934,13 @@ parse_att_value(?STRING_REST("&", Rest), State, Stop, Acc) ->
parse_att_value(Rest1, State2, Stop, ParsedValue ++ Acc);
{external_general, Name, _} ->
?fatal_error(State1, "External parsed entity reference in attribute value: " ++ Name);
+ {not_found, Name} ->
+ case State#xmerl_sax_parser_state.skip_external_dtd of
+ false ->
+ ?fatal_error(State1, "Entity not declared: " ++ Name); %%VC: Entity Declared
+ true ->
+ parse_att_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc)
+ end;
{unparsed, Name, _} ->
?fatal_error(State1, "Unparsed entity reference in attribute value: " ++ Name)
end;
@@ -1098,6 +1105,13 @@ parse_content(?STRING_REST("&", Rest), State, Acc, _IgnorableWS) ->
{external_general, _, {PubId, SysId}} ->
State2 = parse_external_entity(State1, PubId, SysId),
parse_content(Rest1, State2, Acc, false);
+ {not_found, Name} ->
+ case State#xmerl_sax_parser_state.skip_external_dtd of
+ false ->
+ ?fatal_error(State1, "Entity not declared: " ++ Name); %%VC: Entity Declared
+ true ->
+ parse_content(Rest1, State1, ";" ++ lists:reverse(Name) ++ "&" ++ Acc, false)
+ end;
{unparsed, Name, _} ->
?fatal_error(State1, "Unparsed entity reference in content: " ++ Name)
end;
@@ -1357,7 +1371,7 @@ look_up_reference(Name, HaveToExist, State) ->
yes ->
?fatal_error(State, "Entity not declared: " ++ Name); %%WFC: Entity Declared
no ->
- ?fatal_error(State, "Entity not declared: " ++ Name) %%VC: Entity Declared
+ {not_found, Name} %%VC: Entity Declared
end;
false ->
{not_found, Name}
@@ -1869,7 +1883,14 @@ parse_doctype_decl(?STRING_REST("%", Rest), State) ->
parse_doctype_decl(?APPEND_STRING(IValue, Rest1), State1);
{external_parameter, _, {PubId, SysId}} ->
State2 = parse_external_entity(State1#xmerl_sax_parser_state{file_type = entity}, PubId, SysId),
- parse_doctype_decl(Rest1, State2)
+ parse_doctype_decl(Rest1, State2);
+ {not_found, Name} ->
+ case State#xmerl_sax_parser_state.skip_external_dtd of
+ false ->
+ ?fatal_error(State1, "Entity not declared: " ++ Name); %%WFC: Entity Declared
+ true ->
+ parse_doctype_decl(Rest1, State1)
+ end
end;
parse_doctype_decl(?STRING_REST("<!", Rest1), State) ->
parse_doctype_decl_1(Rest1, State);
@@ -2443,7 +2464,7 @@ parse_ndata(Bytes, State) ->
%% Acc = string()
%% Result : {Value, Rest, State}
%% Value = string()
-%% Description: Parse an attribute value
+%% Description: Parse an entity value
%%----------------------------------------------------------------------
parse_entity_value(?STRING_EMPTY, State, undefined, Acc) ->
{Acc, [], State}; %% stop clause when parsing references
@@ -2473,7 +2494,7 @@ parse_entity_value(?STRING_REST("&", Rest), State, Stop, Acc) ->
{external_general, Name, _} ->
parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc);
{not_found, Name} ->
- parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc);
+ parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc);
{unparsed, Name, _} ->
?fatal_error(State1, "Unparsed entity reference in entity value: " ++ Name)
end;
@@ -2490,7 +2511,15 @@ parse_entity_value(?STRING_REST("%", Rest), #xmerl_sax_parser_state{file_type=Ty
IValue = ?TO_INPUT_FORMAT(" " ++ RefValue ++ " "),
parse_entity_value(?APPEND_STRING(IValue, Rest1), State1, Stop, Acc);
{external_parameter, _, {_PubId, _SysId}} ->
- ?fatal_error(State1, "Parameter references in entity value not supported yet.")
+ ?fatal_error(State1, "Parameter references in entity value not supported yet.");
+ {not_found, Name} ->
+ case State#xmerl_sax_parser_state.skip_external_dtd of
+ false ->
+ ?fatal_error(State1, "Entity not declared: " ++ Name); %%VC: Entity Declared
+ true ->
+ parse_entity_value(Rest1, State1, Stop, ";" ++ lists:reverse(Name) ++ "&" ++ Acc)
+ end
+
end
end;
parse_entity_value(?STRING_UNBOUND_REST(Stop, Rest), State, Stop, Acc) ->
diff --git a/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc b/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc
index fae5346e6a..5c995a5a9c 100644
--- a/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_utf16be.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,6 @@
%% STRING_REST and STRING_UNBOUND_REST is only different in the list case
-define(STRING_UNBOUND_REST(MatchChar, Rest), <<MatchChar/big-utf16, Rest/binary>>).
--define(BYTE_ORDER_MARK_1, undefined).
+-define(BYTE_ORDER_MARK_1, undefined_bom1).
-define(BYTE_ORDER_MARK_2, <<16#FE>>).
-define(BYTE_ORDER_MARK_REST(Rest), <<16#FE, 16#FF, Rest/binary>>).
diff --git a/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc b/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc
index 5e1f0a217c..5c6ca0caba 100644
--- a/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc
+++ b/lib/xmerl/src/xmerl_sax_parser_utf16le.erlsrc
@@ -2,7 +2,7 @@
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -35,6 +35,6 @@
%% STRING_REST and STRING_UNBOUND_REST is only different in the list case
-define(STRING_UNBOUND_REST(MatchChar, Rest), <<MatchChar/little-utf16, Rest/binary>>).
--define(BYTE_ORDER_MARK_1, undefined).
+-define(BYTE_ORDER_MARK_1, undefined_bom1).
-define(BYTE_ORDER_MARK_2, <<16#FF>>).
-define(BYTE_ORDER_MARK_REST(Rest), <<16#FF, 16#FE, Rest/binary>>).
diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl
index 4e5cc59d8f..e07d495fc7 100644
--- a/lib/xmerl/src/xmerl_scan.erl
+++ b/lib/xmerl/src/xmerl_scan.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -34,7 +34,9 @@
%% See also <a href="xmerl_examples.html">tutorial</a> on customization
%% functions.
%% </p>
+%% <p>
%% Possible options are:
+%% </p>
%% <dl>
%% <dt><code>{acc_fun, Fun}</code></dt>
%% <dd>Call back function to accumulate contents of entity.</dd>
@@ -2602,8 +2604,7 @@ scan_reference("#x" ++ T, S0) ->
%% [66] CharRef
?bump_col(1),
if hd(T) /= $; ->
- {[Ch], T2, S2} = scan_char_ref_hex(T, S, 0),
- {to_char_set(S2#xmerl_scanner.encoding,Ch),T2,S2};
+ scan_char_ref_hex(T, S, 0);
true ->
?fatal(invalid_char_ref, S)
end;
@@ -3452,14 +3453,14 @@ scan_entity_value("%" ++ T, S0, Delim, Acc, PEName,Namespace,PENesting) ->
%% {system,URI} or {public,URI}
%% Included in literal.
{ExpRef,Sx}=fetch_not_parse(Tuple,S1),
- {EntV,_,_S2} =
- scan_entity_value(ExpRef, Sx, no_delim,[],
+ {EntV, _, S5} =
+ scan_entity_value(ExpRef, Sx, no_delim,[],
PERefName,parameter,[]),
%% should do an update Write(parameter_entity)
%% so next expand_pe_reference is faster
- {EntV,_S2};
+ {string_to_char_set(S5#xmerl_scanner.encoding, EntV), S5};
ExpRef ->
- {ExpRef,S1}
+ {string_to_char_set(S1#xmerl_scanner.encoding, ExpRef) ,S1}
end,
%% single or duoble qoutes are not treated as delimeters
%% in passages "included in literal"
@@ -4020,12 +4021,12 @@ utf8_2_ucs([A|Rest]) when A < 16#80 ->
utf8_2_ucs([A|Rest]) ->
{{error,{bad_character,A}},Rest}.
-to_char_set("iso-10646-utf-1",Ch) ->
- [Ch];
-to_char_set(UTF8,Ch) when UTF8 =:= "utf-8"; UTF8 =:= undefined ->
- ucs_2_utf8(Ch);
-to_char_set(_,Ch) ->
- [Ch].
+%% to_char_set("iso-10646-utf-1",Ch) ->
+%% [Ch];
+%% to_char_set(UTF8,Ch) when UTF8 =:= "utf-8"; UTF8 =:= undefined ->
+%% ucs_2_utf8(Ch);
+%% to_char_set(_,Ch) ->
+%% [Ch].
ucs_2_utf8(Ch) when Ch < 128 ->
%% 0vvvvvvv
diff --git a/lib/xmerl/src/xmerl_uri.erl b/lib/xmerl/src/xmerl_uri.erl
index d8edb2e6e1..a0c6f1c2a7 100644
--- a/lib/xmerl/src/xmerl_uri.erl
+++ b/lib/xmerl/src/xmerl_uri.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -359,9 +359,9 @@ scan_host(C0) ->
%% Hex4=<?HEX ->
%% {C1,lists:reverse(lists:append(IPv6address))};
{C1,Hostname,[A|_HostF]} ->
- {C1,lists:reverse(lists:append(Hostname))};
- _ ->
- {error,no_host}
+ {C1,lists:reverse(lists:append(Hostname))}
+%% _ ->
+%% {error,no_host}
end.
scan_host2([H|C0],Acc,CurF,Host,HostF) when $0=<H,H=<$9 ->
diff --git a/lib/xmerl/src/xmerl_xpath.erl b/lib/xmerl/src/xmerl_xpath.erl
index 182a186d2c..e654a8ef1d 100644
--- a/lib/xmerl/src/xmerl_xpath.erl
+++ b/lib/xmerl/src/xmerl_xpath.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -57,7 +57,9 @@
%% @type option_list(). <p>Options allows to customize the behaviour of the
%% XPath scanner.
%% </p>
+%% <p>
%% Possible options are:
+%% </p>
%% <dl>
%% <dt><code>{namespace, #xmlNamespace}</code></dt>
%% <dd>Set namespace nodes, from XmlNamspace, in xmlContext</dd>
diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl
index c7bca86205..c0923520c2 100644
--- a/lib/xmerl/src/xmerl_xsd.erl
+++ b/lib/xmerl/src/xmerl_xsd.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2006-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
-%%
+%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -29,7 +29,9 @@
%% @type option_list(). <p>Options allow to customize the behaviour of the
%% validation.
%% </p>
+%% <p>
%% Possible options are :
+%% </p>
%% <dl>
%% <dt><code>{tab2file,boolean()}</code></dt>
%% <dd>Enables saving of abstract structure on file for debugging
@@ -46,6 +48,7 @@
%% <dd>It is possible by this option to provide a state with process
%% information from an earlier validation.</dd>
%% </dl>
+%% @end
%%%-------------------------------------------------------------------
-module(xmerl_xsd).
@@ -2687,13 +2690,16 @@ check_element_type(XML=[E=#xmlElement{name=Name}|Rest],
_ ->
{error,{error_path(E,Name),?MODULE,{element_bad_match,E,Any,Env}}}
end;
-check_element_type([],CM,_Env,_Block,_S,Checked) ->
+check_element_type([],CM,_Env,_Block,S,Checked) ->
%% #schema_complex_type, any, #schema_group, anyType and lists are
%% catched above.
case CM of
+ #schema_simple_type{} ->
+ {NewVal,S2} = check_type(CM,[],unapplied,S),
+ {NewVal,[],S2};
{simpleType,_} ->
- {error,{error_path(Checked,undefined),?MODULE,
- {empty_content_not_allowed,CM}}};
+ {NewVal,S2} = check_type(CM,[],unapplied,S),
+ {NewVal,[],S2};
_ ->
{error,{error_path(Checked,undefined),?MODULE,
{empty_content_not_allowed,CM}}}
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 83b9d4826f..280ff10efa 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1,129 +1 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 2003-2010. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# compliance with the License. You should have received a copy of the
-# Erlang Public License along with this software. If not, it can be
-# retrieved online at http://www.erlang.org/.
-#
-# Software distributed under the License is distributed on an "AS IS"
-# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-# the License for the specific language governing rights and limitations
-# under the License.
-#
-# %CopyrightEnd%
-#
-
-XMERL_VSN = 1.2.5
-
-
-TICKETS = \
- OTP-8537
-
-TICKETS_1.2.5 = \
- OTP-8537
-
-TICKETS_1.2.4 = \
- OTP-8343
-
-TICKETS_1.2.3 = \
- OTP-8251 \
- OTP-8252 \
- OTP-8253
-
-TICKETS_1.2.2 = \
- OTP-8213 \
- OTP-8214
-
-TICKETS_1.2.1 = \
- OTP-8084 \
- OTP-8153 \
- OTP-8156
-
-TICKETS_1.2 = \
- OTP-6635
-
-TICKETS_1.1.12 = \
- OTP-7847
-
-TICKETS_1.1.11 = \
- OTP-7736
-
-TICKETS_1.1.10 = \
- OTP-6053 \
- OTP-6873 \
- OTP-7430 \
- OTP-7473 \
- OTP-7496
-
-TICKETS_1.1.9 = \
- OTP-5998 \
- OTP-6947 \
- OTP-7288
-
-TICKETS_1.1.8 = \
- OTP-7211 \
- OTP-7214
-
-TICKETS_1.1.7 = \
- OTP-7190
-
-TICKETS_1.1.6 = \
- OTP-6773 \
- OTP-6777 \
- OTP-6877 \
- OTP-6910
-
-TICKETS_1.1.5 = \
- OTP-6720 \
- OTP-6739 \
- OTP-6752
-
-TICKETS_1.1.4 = \
- OTP-6679
-
-TICKETS_1.1.3 = \
- OTP-6599
-
-TICKETS_1.1.2 = \
- OTP-6507 \
- OTP-6460
-
-TICKETS_1.1.1 = \
- OTP-6402
-
-TICKETS_1.1 = \
- OTP-6043 \
- OTP-6099 \
- OTP-6401
-
-TICKETS_1.0.5 = \
-
-
-TICKETS_1.0.4 = \
- OTP-5599 \
- OTP-5718 \
- OTP-5734 \
- OTP-5895 \
- OTP-5902 \
- OTP-5905
-
-TICKETS_1.0.3 = \
- OTP-5587
-
-TICKETS_1.0.2 = \
- OTP-5498 \
- OTP-5500 \
- OTP-5531
-
-TICKETS_1.0.1 = \
- OTP-5268 \
- OTP-5301 \
- OTP-5407
-
-TICKETS_1.0 = \
- OTP-5174 \
-
+XMERL_VSN = 1.2.8